Module simple_3dviz.io.material

Load a material file into ambient, diffuse and specular colors and a set of texture maps.

Expand source code
"""Load a material file into ambient, diffuse and specular colors and a set of
texture maps."""

import numpy as np
import os

from .utils import _get_file, _close_file
from ..utils import read_image


class MaterialReader(object):
    """MaterialReader defines the common interface for reading material files.
    """
    def __init__(self, filename=None):
        self._name = None
        self._ambient = None
        self._diffuse = None
        self._specular = None
        self._Ns = None
        self._texture = None
        self._bump_map = None
        self._mode = None

        if filename is not None:
            self.read(filename)

    def read(self, filename):
        raise NotImplementedError()

    @property
    def ambient(self):
        if self._ambient is None:
            raise NotImplementedError()
        return self._ambient

    @property
    def diffuse(self):
        if self._diffuse is None:
            raise NotImplementedError()
        return self._diffuse

    @property
    def specular(self):
        if self._specular is None:
            raise NotImplementedError()
        return self._specular

    @property
    def Ns(self):
        if self._Ns is None:
            raise NotImplementedError()
        return self._Ns

    @property
    def texture(self):
        if self._texture is None:
            raise NotImplementedError()
        return self._texture

    @property
    def bump_map(self):
        if self._bump_map is None:
            raise NotImplementedError()
        return self._bump_map

    @property
    def mode(self):
        if self._mode is None:
            raise NotImplementedError()
        return self._mode

    def __getattribute__(self, key):
        if key.startswith("optional_"):
            key = key[9:]
            try:
                return getattr(self, key)
            except NotImplementedError:
                return None
        else:
            return super().__getattribute__(key)


class MtlMaterialReader(MaterialReader):
    """Read MTL material files."""
    def read(self, filename):
        try:
            f = None
            f = _get_file(filename)

            lines = f.readlines()

            # Collect the material color information, namely lines starting
            # with "Ka" followed by 3 floats indicating the r,g,b components
            # for the ambient reflectivity, "Kd" followed by 3 floats
            # indicating the r,g,b components for the diffuse reflectivity and
            # "Ks" followed by 3 floats indicating the r,g,b components for the
            # specular reflectivity.
            self._ambient = np.array([
                list(map(float, l.strip().split()[1:]))
                for l in lines if l.strip().startswith("Ka")
            ], dtype=np.float32)
            self._diffuse = np.array([
                list(map(float, l.strip().split()[1:]))
                for l in lines if l.strip().startswith("Kd")
            ], dtype=np.float32)
            self._specular = np.array([
                list(map(float, l.strip().split()[1:]))
                for l in lines if l.strip().startswith("Ks")
            ], dtype=np.float32)

            # Collect the specular exponent, namely a line starting with "Ns"
            # followed by a float.
            self._Ns = float([
                float(l.strip().split()[1:][0])
                for l in lines if l.strip().startswith("Ns")
            ][0])

            # Collect the information regarding texture maps and the bump map.
            try:
                texture_file = [
                    l.strip().split()[1:][0]
                    for l in lines if l.strip().startswith("map_Kd")
                ][0]

                if isinstance(filename, str):
                    self._texture = read_image(os.path.join(
                        os.path.dirname(filename),
                        texture_file
                    ))
                else:
                    if hasattr(filename, "name"):
                        self._texture = read_image(os.path.join(
                            os.path.dirname(filename.name),
                            texture_file
                        ))
                    else:
                        pass
            except IndexError:
                pass

            try:
                bump_map_file = [
                    l.strip().split()[1:][0]
                    for l in lines if l.strip().startswith("bump")
                ][0]
                if isinstance(filename, str):
                    self._bump_map = read_image(os.path.join(
                        os.path.dirname(filename),
                        bump_map_file
                    ))
                else:
                    if hasattr(filename, "name"):
                        self._bump_map = read_image(os.path.join(
                            os.path.dirname(filename.name),
                            bump_map_file
                        ))
                    else:
                        pass
            except IndexError:
                pass

            # Collect the illumination model used for the material, namely a
            # line starting with "illum" and followed by an integer, specifying
            # the id of the illumination model.
            self._mode = "specular"
            try:
                mode_id = [
                    l.strip().split()[1:][0]
                    for l in lines if l.strip().startswith("illum")
                ][0]
                self._mode = {
                    "0" : "constant",
                    "1" : "diffuse",
                    "2" : "specular",
                }[mode_id]
            except IndexError:
                pass

            # # Collect some additional informatation

            # # Transmission filter of the current material, namely a line
            # # starting with "Tf" followed by 3 floats indicating the r,g,b
            # # components of the atmosphere.
            # Tf = np.array([
            #     list(map(float, l.strip().split()[1:]))
            #     for l in lines if l.strip().startswith("Tf")
            # ], dtype=np.float32)

            # # Collect the dissolve for the current material, namely a line
            # # starting with "d" followed by a float, indicating the amount that
            # # this material dissolves into the background.
            # d = float([
            #     float(l.strip().split()[1:][0])
            #     for l in lines if l.strip().startswith("d")
            # ][0])

            # # Collect the optical density (index of refraction), namely a line
            # # starting with "Ni" followed by a float.
            # Ni = float([
            #     float(l.strip().split()[1:][0])
            #     for l in lines if l.strip().startswith("Ni")
            # ][0])

        finally:
            if f is not None:
                _close_file(filename, f)

Classes

class MaterialReader (filename=None)

MaterialReader defines the common interface for reading material files.

Expand source code
class MaterialReader(object):
    """MaterialReader defines the common interface for reading material files.
    """
    def __init__(self, filename=None):
        self._name = None
        self._ambient = None
        self._diffuse = None
        self._specular = None
        self._Ns = None
        self._texture = None
        self._bump_map = None
        self._mode = None

        if filename is not None:
            self.read(filename)

    def read(self, filename):
        raise NotImplementedError()

    @property
    def ambient(self):
        if self._ambient is None:
            raise NotImplementedError()
        return self._ambient

    @property
    def diffuse(self):
        if self._diffuse is None:
            raise NotImplementedError()
        return self._diffuse

    @property
    def specular(self):
        if self._specular is None:
            raise NotImplementedError()
        return self._specular

    @property
    def Ns(self):
        if self._Ns is None:
            raise NotImplementedError()
        return self._Ns

    @property
    def texture(self):
        if self._texture is None:
            raise NotImplementedError()
        return self._texture

    @property
    def bump_map(self):
        if self._bump_map is None:
            raise NotImplementedError()
        return self._bump_map

    @property
    def mode(self):
        if self._mode is None:
            raise NotImplementedError()
        return self._mode

    def __getattribute__(self, key):
        if key.startswith("optional_"):
            key = key[9:]
            try:
                return getattr(self, key)
            except NotImplementedError:
                return None
        else:
            return super().__getattribute__(key)

Subclasses

Instance variables

var Ns
Expand source code
@property
def Ns(self):
    if self._Ns is None:
        raise NotImplementedError()
    return self._Ns
var ambient
Expand source code
@property
def ambient(self):
    if self._ambient is None:
        raise NotImplementedError()
    return self._ambient
var bump_map
Expand source code
@property
def bump_map(self):
    if self._bump_map is None:
        raise NotImplementedError()
    return self._bump_map
var diffuse
Expand source code
@property
def diffuse(self):
    if self._diffuse is None:
        raise NotImplementedError()
    return self._diffuse
var mode
Expand source code
@property
def mode(self):
    if self._mode is None:
        raise NotImplementedError()
    return self._mode
var specular
Expand source code
@property
def specular(self):
    if self._specular is None:
        raise NotImplementedError()
    return self._specular
var texture
Expand source code
@property
def texture(self):
    if self._texture is None:
        raise NotImplementedError()
    return self._texture

Methods

def read(self, filename)
Expand source code
def read(self, filename):
    raise NotImplementedError()
class MtlMaterialReader (filename=None)

Read MTL material files.

Expand source code
class MtlMaterialReader(MaterialReader):
    """Read MTL material files."""
    def read(self, filename):
        try:
            f = None
            f = _get_file(filename)

            lines = f.readlines()

            # Collect the material color information, namely lines starting
            # with "Ka" followed by 3 floats indicating the r,g,b components
            # for the ambient reflectivity, "Kd" followed by 3 floats
            # indicating the r,g,b components for the diffuse reflectivity and
            # "Ks" followed by 3 floats indicating the r,g,b components for the
            # specular reflectivity.
            self._ambient = np.array([
                list(map(float, l.strip().split()[1:]))
                for l in lines if l.strip().startswith("Ka")
            ], dtype=np.float32)
            self._diffuse = np.array([
                list(map(float, l.strip().split()[1:]))
                for l in lines if l.strip().startswith("Kd")
            ], dtype=np.float32)
            self._specular = np.array([
                list(map(float, l.strip().split()[1:]))
                for l in lines if l.strip().startswith("Ks")
            ], dtype=np.float32)

            # Collect the specular exponent, namely a line starting with "Ns"
            # followed by a float.
            self._Ns = float([
                float(l.strip().split()[1:][0])
                for l in lines if l.strip().startswith("Ns")
            ][0])

            # Collect the information regarding texture maps and the bump map.
            try:
                texture_file = [
                    l.strip().split()[1:][0]
                    for l in lines if l.strip().startswith("map_Kd")
                ][0]

                if isinstance(filename, str):
                    self._texture = read_image(os.path.join(
                        os.path.dirname(filename),
                        texture_file
                    ))
                else:
                    if hasattr(filename, "name"):
                        self._texture = read_image(os.path.join(
                            os.path.dirname(filename.name),
                            texture_file
                        ))
                    else:
                        pass
            except IndexError:
                pass

            try:
                bump_map_file = [
                    l.strip().split()[1:][0]
                    for l in lines if l.strip().startswith("bump")
                ][0]
                if isinstance(filename, str):
                    self._bump_map = read_image(os.path.join(
                        os.path.dirname(filename),
                        bump_map_file
                    ))
                else:
                    if hasattr(filename, "name"):
                        self._bump_map = read_image(os.path.join(
                            os.path.dirname(filename.name),
                            bump_map_file
                        ))
                    else:
                        pass
            except IndexError:
                pass

            # Collect the illumination model used for the material, namely a
            # line starting with "illum" and followed by an integer, specifying
            # the id of the illumination model.
            self._mode = "specular"
            try:
                mode_id = [
                    l.strip().split()[1:][0]
                    for l in lines if l.strip().startswith("illum")
                ][0]
                self._mode = {
                    "0" : "constant",
                    "1" : "diffuse",
                    "2" : "specular",
                }[mode_id]
            except IndexError:
                pass

            # # Collect some additional informatation

            # # Transmission filter of the current material, namely a line
            # # starting with "Tf" followed by 3 floats indicating the r,g,b
            # # components of the atmosphere.
            # Tf = np.array([
            #     list(map(float, l.strip().split()[1:]))
            #     for l in lines if l.strip().startswith("Tf")
            # ], dtype=np.float32)

            # # Collect the dissolve for the current material, namely a line
            # # starting with "d" followed by a float, indicating the amount that
            # # this material dissolves into the background.
            # d = float([
            #     float(l.strip().split()[1:][0])
            #     for l in lines if l.strip().startswith("d")
            # ][0])

            # # Collect the optical density (index of refraction), namely a line
            # # starting with "Ni" followed by a float.
            # Ni = float([
            #     float(l.strip().split()[1:][0])
            #     for l in lines if l.strip().startswith("Ni")
            # ][0])

        finally:
            if f is not None:
                _close_file(filename, f)

Ancestors

Methods

def read(self, filename)
Expand source code
def read(self, filename):
    try:
        f = None
        f = _get_file(filename)

        lines = f.readlines()

        # Collect the material color information, namely lines starting
        # with "Ka" followed by 3 floats indicating the r,g,b components
        # for the ambient reflectivity, "Kd" followed by 3 floats
        # indicating the r,g,b components for the diffuse reflectivity and
        # "Ks" followed by 3 floats indicating the r,g,b components for the
        # specular reflectivity.
        self._ambient = np.array([
            list(map(float, l.strip().split()[1:]))
            for l in lines if l.strip().startswith("Ka")
        ], dtype=np.float32)
        self._diffuse = np.array([
            list(map(float, l.strip().split()[1:]))
            for l in lines if l.strip().startswith("Kd")
        ], dtype=np.float32)
        self._specular = np.array([
            list(map(float, l.strip().split()[1:]))
            for l in lines if l.strip().startswith("Ks")
        ], dtype=np.float32)

        # Collect the specular exponent, namely a line starting with "Ns"
        # followed by a float.
        self._Ns = float([
            float(l.strip().split()[1:][0])
            for l in lines if l.strip().startswith("Ns")
        ][0])

        # Collect the information regarding texture maps and the bump map.
        try:
            texture_file = [
                l.strip().split()[1:][0]
                for l in lines if l.strip().startswith("map_Kd")
            ][0]

            if isinstance(filename, str):
                self._texture = read_image(os.path.join(
                    os.path.dirname(filename),
                    texture_file
                ))
            else:
                if hasattr(filename, "name"):
                    self._texture = read_image(os.path.join(
                        os.path.dirname(filename.name),
                        texture_file
                    ))
                else:
                    pass
        except IndexError:
            pass

        try:
            bump_map_file = [
                l.strip().split()[1:][0]
                for l in lines if l.strip().startswith("bump")
            ][0]
            if isinstance(filename, str):
                self._bump_map = read_image(os.path.join(
                    os.path.dirname(filename),
                    bump_map_file
                ))
            else:
                if hasattr(filename, "name"):
                    self._bump_map = read_image(os.path.join(
                        os.path.dirname(filename.name),
                        bump_map_file
                    ))
                else:
                    pass
        except IndexError:
            pass

        # Collect the illumination model used for the material, namely a
        # line starting with "illum" and followed by an integer, specifying
        # the id of the illumination model.
        self._mode = "specular"
        try:
            mode_id = [
                l.strip().split()[1:][0]
                for l in lines if l.strip().startswith("illum")
            ][0]
            self._mode = {
                "0" : "constant",
                "1" : "diffuse",
                "2" : "specular",
            }[mode_id]
        except IndexError:
            pass

        # # Collect some additional informatation

        # # Transmission filter of the current material, namely a line
        # # starting with "Tf" followed by 3 floats indicating the r,g,b
        # # components of the atmosphere.
        # Tf = np.array([
        #     list(map(float, l.strip().split()[1:]))
        #     for l in lines if l.strip().startswith("Tf")
        # ], dtype=np.float32)

        # # Collect the dissolve for the current material, namely a line
        # # starting with "d" followed by a float, indicating the amount that
        # # this material dissolves into the background.
        # d = float([
        #     float(l.strip().split()[1:][0])
        #     for l in lines if l.strip().startswith("d")
        # ][0])

        # # Collect the optical density (index of refraction), namely a line
        # # starting with "Ni" followed by a float.
        # Ni = float([
        #     float(l.strip().split()[1:][0])
        #     for l in lines if l.strip().startswith("Ni")
        # ][0])

    finally:
        if f is not None:
            _close_file(filename, f)