Source: package/material/wrapped_material.js

import {
    Material,
    LineBasicMaterial,
    LineDashedMaterial,
    MeshBasicMaterial,
    MeshDepthMaterial,
    MeshDistanceMaterial,
    MeshLambertMaterial,
    MeshMatcapMaterial,
    MeshNormalMaterial,
    MeshPhongMaterial,
    MeshPhysicalMaterial,
    MeshStandardMaterial,
    MeshToonMaterial,
    PointsMaterial,
    RawShaderMaterial,
    ShaderMaterial,
    ShadowMaterial,
    SpriteMaterial,
    DoubleSide
} from '../../../node_modules/three/build/three.module.js';

//NodeMaterials
import { NodeMaterialLoader, NodeMaterialLoaderUtils } from '../../../node_modules/three/examples/jsm/loaders/NodeMaterialLoader.js';
import * as Nodes from '../../../node_modules/three/examples/jsm/nodes/Nodes.js';

import { buildNodeMaterial } from '../../utilities/nodeMaterialBuilder.js';


import { Reporter } from '../../reporter/reporter.js';
import { Package } from '../../package/package.js';
import { WrappedTexture } from '../wrapped_texture.js';
import { BuildableComponent } from '../component/buildable_component.js';
import { WrappedImage } from '../image/wrapped_image.js';
import { checkPropTypes, copyProps, omit, UUIDRegex } from '../../lib.js';
import { MaterialCategory } from './material_category.js';
import { EnvironmentMap } from '../environment_map.js';
import { Project } from '../../project.js';
import { MaterialSet } from '../material/material_set.js';


/** ProductBuilder Material */
class WrappedMaterial extends BuildableComponent {

    // typescript understands this, but jsdoc crashes
    // /**
    //  * @param {Reporter} reporter
    //  * @param {{[k: string]: any, id?: UUID, type: MaterialType, thumbnail?: WrappedImage } } settings
    //  */

    /**
     * @param {Reporter} reporter
     * @param {Object} settings
     * @param {UUID} [settings.id]
     * @param {string} [settings.name]
     * @param {MaterialType} settings.type
     * @param {Array<MaterialCategory>} [settings.categories]
     * @param {WrappedImage} [settings.thumbnail]
     * @param {WrappedTexture} [settings.alphaMap]
     * @param {WrappedTexture} [settings.aoMap]
     * @param {EnvironmentMap} [settings.envMap]
     * @param {WrappedTexture} [settings.lightMap]
     * @param {WrappedTexture} [settings.map]
     * @param {WrappedTexture} [settings.specularMap]
     * @param {WrappedTexture} [settings.displacementMap]
     * @param {WrappedTexture} [settings.emissiveMap]
     * @param {WrappedTexture} [settings.bumpMap]
     * @param {WrappedTexture} [settings.normalMap]
     * @param {WrappedTexture} [settings.clearcoatMap]
     * @param {WrappedTexture} [settings.clearcoatNormalMap]
     * @param {WrappedTexture} [settings.gradientMap]
     * @param {WrappedTexture} [settings.parallaxMap]
     * @param {any} [settings.color]
    //  * @param {any} [settings.x: string]
     */

    constructor(reporter, settings, instructions = {}) {

        if (instructions.tree && instructions.tree.environmentMaps) {
            if (!settings.envMap) {
                settings.envMap = instructions.tree.environmentMaps[0];
            }
        }

        super(
            reporter,
            settings,
            {
                parse: {
                    categories: 'integral'
                }
            }
        );

        // would be nice to check against "instanceof Material"
        // but THREE classes' prototypes point to themselves
        // for some reason

        checkPropTypes(
            settings,
            {
                type: val => WrappedMaterial.materialTypes[val] !== undefined
            },
            {
                alphaMap: WrappedTexture,
                aoMap: WrappedTexture,
                envMap: EnvironmentMap,
                lightMap: WrappedTexture,
                map: WrappedTexture,
                specularMap: WrappedTexture,
                displacementMap: WrappedTexture,
                emissiveMap: WrappedTexture,
                bumpMap: WrappedTexture,
                normalMap: WrappedTexture,
                clearcoatMap: WrappedTexture,
                clearcoatNormalMap: WrappedTexture,
                gradientMap: WrappedTexture,
                parallaxMap: WrappedTexture,
            }
        );



        // this.exportName = 'materials';


        // material presentability depends only on the thumb's usability

        if (this.settings.thumbnail) {
            this.settings.thumbnail.on('usability-changed', thumbUsability => {
                this._UIStatus = thumbUsability;
            });
        }
    }


    static _exportName = {
        singular: 'material',
        plural: 'materials'
    };


    /** @type {Object<string,Object>} */

    static materialTypes = {
        Nodes,
        LineBasicMaterial,
        LineDashedMaterial,
        Material,
        MeshBasicMaterial,
        MeshDepthMaterial,
        MeshDistanceMaterial,
        MeshLambertMaterial,
        MeshMatcapMaterial,
        MeshNormalMaterial,
        MeshPhongMaterial,
        MeshPhysicalMaterial,
        MeshStandardMaterial,
        MeshToonMaterial,
        PointsMaterial,
        RawShaderMaterial,
        ShaderMaterial,
        ShadowMaterial,
        SpriteMaterial
    };



    /** @returns {Array<MaterialSet>} */

    getMaterialSets() {
        if ( ! this._tree ) {
            return [];
        }
        else {
            return (this._tree.materialSets || []).filter( set => set.has( this ));
        }
    }

    get materialSets() {
        return this.getMaterialSets()
    }


    /**
     * Build material
     * @param {ComponentPart} part
     * @param {LoadingQuality} quality
     * @returns {Promise<WrappedMaterial>}
     */

    async _build(part, quality) {

        switch (part) {

            case 'UI':
                if (this._settings.thumbnail) {
                    this._setContent('UI', quality, this._settings.thumbnail.content.main[quality]);
                }
                else {
                    this._setContent('UI', quality, Project.defaultImages.missing.cloneNode(true));
                }
                break;

            case 'main':

                if( this._settings.type === 'Nodes'){

                  
                    console.log( "node material 123")

                    // //get the product normal texture
                    // const normalTexture = this.moobel.assets.textures[ `tex__prod_norm__${blockInstance.instanceOf}` ]
					
                    // //create texture node with the product normal map
                    // const normalNode 	= normalTexture
                    //                     ?	new Nodes.TextureNode( this.moobel.assets.textures[ `tex__prod_norm__${blockInstance.instanceOf}` ] )
                    //                     :	undefined

                    // // console.log( normalTexture, normalNode )

                    // //create the node material
                    // const nodeMaterial = buildNodeMaterial(
                    //     material,   //material to apply the product map to	
                    //     this.moobel.assets.textures, //texture -> can be removed	
                    //     normalNode,	
                    //     { x: clone.userData.normalMap.tiling[ 0 ], y: clone.userData.normalMap.tiling[ 1 ] } //welke tiling is dit?
                    // )

                    // // console.log( nodeMaterial )

                    //this._setContent('main', quality, nodeMaterial);

                }
                else
                {

                   
                    const material = new WrappedMaterial.materialTypes[this._settings.type]();

                    material.userData = {
                        PB: {
                            origin: this.id
                        }
                    }

                    const textureSettings = Object.keys(this._settings).filter(key => this._settings[key] instanceof WrappedTexture);

                    //console.log( textureSettings )

                    let propsToCopy = Object.keys(material).filter(prop => !textureSettings.includes(prop));

                    //console.log( this.settings )

                    const settingSources = [ this._settings, ...(this._settings.categories || []) ];

                    for ( let settingSource of settingSources ) {

                        const copiedProps = copyProps({
                            from: settingSource,
                            into: material,
                            required: [],
                            optional: propsToCopy
                        });

                        propsToCopy = propsToCopy.filter( entry => ! copiedProps.includes(entry));

                        for (let textureSetting of textureSettings) {
        
                            if(settingSource[textureSetting] && ! material[textureSetting]) {
                                material[textureSetting] = settingSource[textureSetting].content.main[quality];
                            }
                        }
                    }
                    

                    // If this material was loaded as part of a package (not as standalone object)
                    // and an environment map is defined in the package, this material will have added
                    // it to its settings and depencies in the constructor. If, however, the material
                    // was added to a package later, it will not have the envMap as dependency
                    // therefore, check again here if one is available now

                    if (!textureSettings.environmentMap && this._tree && this._tree.environmentMaps && this._tree.environmentMaps[ 0 ] instanceof EnvironmentMap) {
                        const envMap = this._tree.environmentMaps[0];
                        if (envMap.status.main[quality] !== 'ready') {
                            console.warn(`Can not apply ${envMap.label} to ${this.label}, status should be "ready" but is "${envMap.status.main[quality]}".`);
                        }
                        else {
                            material.envMap = this._tree.environmentMaps[0].content.main[quality];
                        }
                    }

                    //material.side = DoubleSide
                    material.needsUpdate = true;

                    //console.log( material )

                    this._setContent('main', quality, material);

                }

                break;


            default:
                this._setContent(part, quality, null);
                break;
        }


        return this;
    }
}

export { WrappedMaterial };