@polygonjs/polygonjs
Version:
node-based WebGL 3D engine https://polygonjs.com
155 lines (154 loc) • 5.72 kB
JavaScript
"use strict";
import { BaseSopOperation } from "./_Base";
import { TypedNodePathParamValue } from "../../../core/Walker";
import { NodeContext } from "../../../engine/poly/NodeContext";
import { applyRenderHook, applyCustomMaterials, cloneMaterial } from "../../../core/geometry/Material";
import { ShaderMaterial } from "three";
import { GlobalsGeometryHandler } from "../../../engine/nodes/gl/code/globals/Geometry";
import { InputCloneMode } from "../../../engine/poly/InputCloneMode";
import { isBooleanTrue } from "../../../core/BooleanValue";
import { CoreMask } from "../../../core/geometry/Mask";
let _nextId = 0;
export class MaterialSopOperation extends BaseSopOperation {
constructor() {
super(...arguments);
this._materialSopOperationId = _nextId++;
this._globalsHandler = new GlobalsGeometryHandler();
this._onMaterialUpdateBound = this._onMaterialUpdate.bind(this);
this._oldMatByOldNewId = /* @__PURE__ */ new Map();
this._materialByUuid = /* @__PURE__ */ new Map();
}
static type() {
return "material";
}
async cook(inputCoreGroups, params) {
const coreGroup = inputCoreGroups[0];
this._oldMatByOldNewId.clear();
await this._applyMaterials(coreGroup, params);
this._swapTextures(coreGroup, params);
return coreGroup;
}
async _getMaterial(params) {
var _a, _b, _c, _d;
const materialNode = params.material.nodeWithContext(NodeContext.MAT, (_a = this.states) == null ? void 0 : _a.error);
if (materialNode) {
this._watchMaterialNode(materialNode);
const material = await materialNode.material();
const baseBuilderMatNode = materialNode;
if (baseBuilderMatNode.assemblerController) {
(_b = baseBuilderMatNode.assemblerController()) == null ? void 0 : _b.setAssemblerGlobalsHandler(this._globalsHandler);
}
if (!material) {
(_c = this.states) == null ? void 0 : _c.error.set(`material invalid. (error: '${materialNode.states.error.message()}')`);
}
return material;
} else {
(_d = this.states) == null ? void 0 : _d.error.set(`no material node found`);
}
}
_watchMaterialNode(materialNode) {
if (this._watchedMaterialNode == materialNode) {
return;
}
const hookName = this._watchHookName();
materialNode.addPostDirtyHook(hookName, this._onMaterialUpdateBound);
materialNode.cookController.registerOnCookEnd(hookName, this._onMaterialUpdateBound);
if (this._watchedMaterialNode) {
this._watchedMaterialNode.removePostDirtyHook(hookName);
this._watchedMaterialNode.cookController.deregisterOnCookEnd(hookName);
}
this._watchedMaterialNode = materialNode;
}
async _onMaterialUpdate() {
if (!this._watchedMaterialNode) {
return;
}
const container = await this._watchedMaterialNode.compute();
const material = container.material();
if (material != this._watchedMaterialNodeMaterial) {
this._watchedMaterialNodeMaterial = material;
if (this._node) {
this._node.p.material.setDirty();
}
}
}
_watchHookName() {
return `MaterialSopOperationId-${this._materialSopOperationId}`;
}
async _applyMaterials(coreGroup, params) {
if (!isBooleanTrue(params.assignMat)) {
return;
}
const material = await this._getMaterial(params);
if (!material) {
return;
}
const selectedObjects = CoreMask.filterThreejsObjects(coreGroup, params);
for (let selectedObject of selectedObjects) {
this._applyMaterial(selectedObject, material, params);
}
return coreGroup;
}
_swapTextures(coreGroup, params) {
if (!isBooleanTrue(params.swapCurrentTex)) {
return;
}
this._materialByUuid.clear();
const objects = CoreMask.filterObjects(coreGroup, params, coreGroup.allCoreObjects());
for (let object of objects) {
const mat = object.material;
this._materialByUuid.set(mat.uuid, mat);
}
this._materialByUuid.forEach((mat, mat_uuid) => {
this._swapTexture(mat, params);
});
}
_applyMaterial(object, srcMaterial, params) {
const usedMaterial = isBooleanTrue(params.cloneMat) ? cloneMaterial(this.scene(), srcMaterial, {
shareCustomUniforms: params.shareCustomUniforms,
addCustomMaterials: true
}) : srcMaterial;
if (srcMaterial instanceof ShaderMaterial && usedMaterial instanceof ShaderMaterial) {
for (let uniform_name in srcMaterial.uniforms) {
usedMaterial.uniforms[uniform_name] = srcMaterial.uniforms[uniform_name];
}
}
if (object.isGroup) {
return;
}
const object_with_material = object;
this._oldMatByOldNewId.set(usedMaterial.uuid, object_with_material.material);
object_with_material.material = usedMaterial;
applyRenderHook(object, usedMaterial);
applyCustomMaterials(object, usedMaterial);
}
_swapTexture(target_mat, params) {
if (params.texSrc0 == "" || params.texDest0 == "") {
return;
}
let src_mat = this._oldMatByOldNewId.get(target_mat.uuid);
src_mat = src_mat || target_mat;
const src_tex = src_mat[params.texSrc0];
if (src_tex) {
target_mat[params.texDest0] = src_tex;
const uniforms = target_mat.uniforms;
if (uniforms) {
const uniforms_map = uniforms[params.texDest0];
if (uniforms_map) {
uniforms[params.texDest0] = { value: src_tex };
}
}
}
}
}
MaterialSopOperation.DEFAULT_PARAMS = {
group: "",
assignMat: true,
material: new TypedNodePathParamValue(""),
cloneMat: false,
shareCustomUniforms: true,
swapCurrentTex: false,
texSrc0: "emissiveMap",
texDest0: "map"
};
MaterialSopOperation.INPUT_CLONED_STATE = InputCloneMode.FROM_NODE;