Repository

js/Mobilizing/renderer/3D/three/scene/Material.js

import * as THREE from "three";

import Color from "../types/Color";

/**
* A material will give color and some effect to the surface of the 3D object.
* Mobilizing.js 3D renderer is based on the one of Three.js https://threejs.org/docs/index.html#api/en/materials/Material and its variations
*/
export default class Material {
    /**
    * @example
    *     //this is how to use a parameters object in order to instanciate a Mobilizing.js object
    *     var mobilizingObject = new Mobilizing.Class({paramName1: value, paramName2: value});
    *
    * @param {Object} params The parameters object
    * @param {String | THREE.Material} [params.type="basic"] the type of the matrial, one of "basic", "projectionmapping", "phong", "line", "sprite", or directly a Three.js Material.
    * @param {Color} [params.color=Color.white.clone()]
    * @param {String} [params.shading="flat"]
    * @param {Object} [params.customParams={}] the parameters to use for custom type.
    */
    constructor({
        type = "default",
        color = Color.white.clone(),
        shading = "flat", //|| "smooth"
        customParams = {}
    } = {}) {
        this.type = type;
        this.color = color;
        this.shading = shading;
        this.customParams = customParams;

        this.texture = undefined;

        //console.log(this.type);

        if (this.type instanceof THREE.Material) {
            //console.log("THREE.Material input");
            this._material = this.type;
        }
        else {
            switch (this.type) {
                case "basic":

                    this._material = new THREE.MeshBasicMaterial({
                        color: this.color,
                        wireframe: false
                        /*,overdraw: true*/
                    });
                    break;

                case "points":

                    this._material = new THREE.PointsMaterial({ color: this.color });
                    break;

                case "custom":
                    this._material = new THREE.ShaderMaterial(this.customParams);

                    break;

                case "projectionmapping":

                    // eslint-disable-next-line no-case-declarations
                    const shader = THREE.ShaderLib.cube;
                    // eslint-disable-next-line no-case-declarations
                    const uniforms = THREE.UniformsUtils.clone(shader.uniforms);

                    shader.vertexShader = shader.vertexShader.replace("vWorldPosition = transformDirection( position, modelMatrix );",
                        "vWorldPosition = (modelMatrix * vec4( position, 0.0 )).xyz;");

                    shader.fragmentShader = shader.fragmentShader.replace("gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );",
                        "vec3 viewDir = vWorldPosition-viewPosition;  gl_FragColor = textureCube( tCube, normalize(viewDir));");

                    shader.fragmentShader = shader.fragmentShader.replace("uniform samplerCube tCube;",
                        "uniform samplerCube tCube; uniform vec3 viewPosition;");

                    uniforms.viewPosition = { type: 'v3', value: new THREE.Vector3(0, 0, 0) };

                    this._material = new THREE.ShaderMaterial({
                        fragmentShader: shader.fragmentShader,
                        vertexShader: shader.vertexShader,
                        uniforms
                    });
                    break;

                case "lambert": //vertex lit

                    this._material = new THREE.MeshLambertMaterial({
                        color: 0xffffff,
                        wireframe: false
                    });
                    break;

                case "default":
                case "phong":
                    this._material = new THREE.MeshPhongMaterial({ color: this.color });
                    break;

                case "line":

                    this._material = new THREE.LineBasicMaterial({ color: this.color });
                    break;

                case "sprite":

                    this._material = new THREE.SpriteMaterial({ color: this.color });
                    break;

                default:

                    this._material = new THREE.MeshBasicMaterial({ color: this.color, wireframe: false });
            }
        }

        this.setShading(this.shading);
        this._material.side = THREE.DoubleSide;
    }

    /**
    * @returns the Three.js native object used in this class
    */
    getNativeObject() {
        return this._material;
    }

    /**
    * set this material texture
    * @param {Texture} texture
    */
    setTexture(texture) {
        this.texture = texture;

        const nativeTexture = this.texture.getNativeObject();

        if (nativeTexture) {
            this._material.map = nativeTexture;
            //this._material.needsUpdate = true;
        }
        else if (texture.cube) {
            if (this.type === "projectionmapping") {
                this._material.uniforms.tCube.value = texture.cube;   // textureCube has been init before
            }
            else {
                this._material.envMap = texture.cube;
            }
        }
    }

    /**
    * Gets the current texture
    * @return {Texture}
    */
    getTexture() {
        return this.texture;
    }

    /**
    * @param {String} name the name of the uniform to set
    * @param {Object} value the value of the uniform to set
    */
    setUniform(name, value) {
        this._material.uniforms[name].value = value;
    }

    /**
    * @param {Boolean} trans
    */
    setTransparent(trans) {
        this._material.transparent = trans;
    }

    /**
    * @return {Boolean}
    */
    getTransparent() {
        return this._material.transparent;
    }

    /**
    * @param {Color} color
    */
    setColor(color) {
        this.color = color;
        this._material.color = this.color;
    }

    /**
    * @return {Color}
    */
    getColor() {
        return this.color;
    }

    /**
    * @param {Boolean} wireframe
    */
    setWireframe(wireframe) {
        this._material.wireframe = wireframe;
    }

    /**
    * @return {Boolean}
    */
    getWireframe() {
        return this._material.wireframe;
    }

    /**
    * @param {float} op between 0 and 1
    */
    setOpacity(op) {
        this._material.opacity = op;
    }

    /**
    * @return {float}
    */
    getOpacity() {
        return this._material.opacity;
    }

    /**
    * @param {Boolean} sided
    */
    setDoubleSided(sided) {
        this._material.side = sided ? THREE.DoubleSide : THREE.FrontSide;
    }

    /**
    * @return {Boolean}
    */
    getDoubleSided() {
        return (this._material.side === THREE.DoubleSide);
    }

    /**
    * @param {Number} val between 0 and 1
    */
    setAlphaTest(val) {
        this._material.alphaTest = val;
    }

    /**
    * @return {Number}
    */
    getAlphaTest() {
        return this._material.alphaTest;
    }

    /**
    * Sets the depth write.
    * @param {boolean} bool - The depthWrite value
    */
    setDepthWrite(bool) {
        this._material.depthWrite = bool;
    }

    /**
    * Sets the depth test.
    * @param {boolean} bool - The depthTest value
    */
    setDepthTest(bool) {
        this._material.depthTest = bool;
    }

    /**
    * @param {Number} val
    */
    setLineWidth(val) {
        this._material.linewidth = val;
    }

    /**
    * @return {Number}
    */
    getLineWidth() {
        return this._material.linewidth;
    }

    /**
    * @param {Number} val
    */
    setPointSize(val) {
        if (this._material.isPointsMaterial) {
            this._material.size = val;
        }
    }

    /**
    * @return {Number}
    */
    getPointSize() {
        if (this._material.isPointsMaterial) {
            return this._material.size;
        }

        return undefined;
    }

    /**
    * @param {Number} color
    */
    setShininess(val) {
        this._material.shininess = val;
    }

    /**
    * @return {Number}
    */
    getShininess() {
        return this._material.shininess;
    }

    /**
    * @param {Color} color
    */
    setEmissiveColor(color) {
        this._material.emissive = color;
    }

    /**
    * @return {Color}
    */
    getEmissiveColor() {
        return this._material.emissive;
    }

    /**
    * @param {String} shading one of "smooth", "flat"
    */
    setShading(shading) {
        this.shading = shading;
        this._material.flatShading = ((this.shading === "flat") ? true : false);
        this._material.needsUpdate = true;
    }

    /**
    * @param {Color} color
    */
    setSpecularColor(color) {
        this._material.specular = color;
    }

    /**
    * @return {Color}
    */
    getSpecularColor() {
        return this._material.specular;
    }

    //cf http://blog.cjgammon.com/threejs-custom-shader-material
    //cf https://threejs.org/docs/index.html#api/en/materials/ShaderMaterial

    /**
    * @param {String} name
    * @param {Object} value (depending on the property)
    */
    setProperty(name, val) {
        this._material.uniforms[name].value = val;
    }

    /**
    * @param {String} name
    * @return {Object} value (depending on the property)
    */
    getProperty(name) {
        return this._material.uniforms[name].value;
    }

    /**
    * @param {String} name
    */
    setBlending(blending) {
        this._material.blending = blending;
    }

    /**
    * erase this material (calling dispose and clearing texture map)
    */
    erase() {
        if (this._material) {
            if (this._material.map) {
                this._material.map.dispose();
            }
            this._material.dispose();
        }
    }

}