polygonjs-engine
Version:
node-based webgl 3D engine https://polygonjs.com
173 lines (151 loc) • 5.77 kB
text/typescript
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};
}
}
}
}
}