UNPKG

polygonjs-engine

Version:

node-based webgl 3D engine https://polygonjs.com

173 lines (151 loc) 5.77 kB
import {BaseSopOperation} from './_Base'; import {DefaultOperationParams} from '../_Base'; import {CoreGroup} from '../../geometry/Group'; import {TypedNodePathParamValue} from '../../Walker'; import {NodeContext} from '../../../engine/poly/NodeContext'; import {BaseBuilderMatNodeType} from '../../../engine/nodes/mat/_BaseBuilder'; import {CoreMaterial} from '../../geometry/Material'; import {Object3D} from 'three/src/core/Object3D'; import {Material} from 'three/src/materials/Material'; import {Mesh} from 'three/src/objects/Mesh'; import {Texture} from 'three/src/textures/Texture'; import {GlobalsGeometryHandler} from '../../../engine/nodes/gl/code/globals/Geometry'; import {InputCloneMode} from '../../../engine/poly/InputCloneMode'; import {ShaderMaterial} from 'three/src/materials/ShaderMaterial'; import {CoreObject} from '../../geometry/Object'; interface MaterialSopParams extends DefaultOperationParams { group: string; assignMat: boolean; material: TypedNodePathParamValue; applyToChildren: boolean; cloneMat: boolean; shareUniforms: boolean; swapCurrentTex: boolean; texSrc0: string; texDest0: string; } export class MaterialSopOperation extends BaseSopOperation { static readonly DEFAULT_PARAMS: MaterialSopParams = { group: '', assignMat: true, material: new TypedNodePathParamValue('/MAT/mesh_standard1'), applyToChildren: true, cloneMat: false, shareUniforms: true, swapCurrentTex: false, texSrc0: 'emissiveMap', texDest0: 'map', }; static readonly INPUT_CLONED_STATE = InputCloneMode.FROM_NODE; static type(): Readonly<'material'> { return 'material'; } private _globals_handler: GlobalsGeometryHandler = new GlobalsGeometryHandler(); async cook(input_contents: CoreGroup[], params: MaterialSopParams) { const core_group = input_contents[0]; this._old_mat_by_old_new_id.clear(); await this._apply_materials(core_group, params); this._swap_textures(core_group, params); return core_group; } private async _apply_materials(core_group: CoreGroup, params: MaterialSopParams) { if (!params.assignMat) { return; } const material_node = params.material.ensure_node_context(NodeContext.MAT, this.states?.error); if (material_node) { const material = material_node.material; const assembler_controller = (material_node as BaseBuilderMatNodeType).assemblerController; if (assembler_controller) { assembler_controller.set_assembler_globals_handler(this._globals_handler); } await material_node.requestContainer(); if (material) { if (params.applyToChildren) { // if we apply to children, the group will be tested inside _apply_material for (let object of core_group.objects()) { object.traverse((grand_child) => { this._apply_material(grand_child, material, params); }); } } else { // if we apply to children, the group is tested here for (let object of core_group.objectsFromGroup(params.group)) { this._apply_material(object, material, params); } } return core_group; } else { this.states?.error.set(`material invalid. (error: '${material_node.states.error.message()}')`); } } else { this.states?.error.set(`no material node found`); } } private _old_mat_by_old_new_id: Map<string, Material> = new Map(); private _materials_by_uuid: Map<string, Material> = new Map(); private _swap_textures(core_group: CoreGroup, params: MaterialSopParams) { if (!params.swapCurrentTex) { return; } this._materials_by_uuid.clear(); for (let object of core_group.objectsFromGroup(params.group)) { if (params.applyToChildren) { object.traverse((child) => { const mat = (object as Mesh).material as Material; this._materials_by_uuid.set(mat.uuid, mat); }); } else { const mat = (object as Mesh).material as Material; this._materials_by_uuid.set(mat.uuid, mat); } } this._materials_by_uuid.forEach((mat, mat_uuid) => { this._swap_texture(mat, params); }); } private _apply_material(object: Object3D, src_material: Material, params: MaterialSopParams) { if (params.group) { if (!CoreObject.isInGroup(params.group, object)) { return; } } const used_material = params.cloneMat ? CoreMaterial.clone(src_material) : src_material; if (src_material instanceof ShaderMaterial && used_material instanceof ShaderMaterial) { for (let uniform_name in src_material.uniforms) { used_material.uniforms[uniform_name] = src_material.uniforms[uniform_name]; } } const object_with_material = object as Mesh; // const current_mat = object_with_material.material as Material | undefined; // if (current_mat && params.swapCurrentTex) { // this._swap_texture(used_material, current_mat, params); // } this._old_mat_by_old_new_id.set(used_material.uuid, object_with_material.material as Material); object_with_material.material = used_material; CoreMaterial.apply_render_hook(object, used_material); CoreMaterial.apply_custom_materials(object, used_material); } private _swap_texture(target_mat: Material, params: MaterialSopParams) { if (params.texSrc0 == '' || params.texDest0 == '') { return; } let src_mat = this._old_mat_by_old_new_id.get(target_mat.uuid); src_mat = src_mat || target_mat; const src_tex: Texture | null = (src_mat as any)[params.texSrc0]; if (src_tex) { // swap mat param (target_mat as any)[params.texDest0] = src_tex; // (src_mat as any)[params.texSrc0] = null; // swap uniforms const uniforms = (target_mat as any).uniforms; if (uniforms) { const uniforms_map = uniforms[params.texDest0]; if (uniforms_map) { uniforms[params.texDest0] = {value: src_tex}; // uniforms[params.texSrc0] = {value: null}; } } } } }