import { BuildableComponent } from "../package/component/buildable_component.js";
import { WrappedMaterial } from "../package/material/wrapped_material.js";
import { Reporter } from "../reporter/reporter.js";
import { BlockInstance } from "./block_instance.js";
import { MaterialSet } from "../package/material/material_set.js";
import { Package } from "../package/package.js";
import { PositionedMesh } from "../package/mesh/positioned_mesh.js";
import { checkPropTypes, getAssigningComponent } from '../lib.js';
import { PositionedComponent } from '../package/component/positioned_component.js';
import { PositionedMeshGroup } from '../package/mesh/positioned_mesh_group.js';
import { Component } from '../component/component.js';
import { Project } from '../project.js';
class MaterialAssignment extends BuildableComponent {
/**
* @param {Reporter} reporter
* @param {Object} settings
* @param {UUID} [settings.id]
* @param {string} [settings.name]
* @param {BlockInstance} settings.blockInstance
* @param {PositionedMeshGroup} [settings.positionedMeshGroup]
* @param {PositionedMesh} [settings.positionedMesh]
* @param {WrappedMaterial} settings.material
*/
constructor(reporter, settings) {
if ( settings.blockInstances ) {
delete settings.blockInstances;
}
super(
reporter,
settings
);
checkPropTypes(
settings,
{
blockInstance: BlockInstance,
material: [
WrappedMaterial
]
},
{
positionedMesh: [
PositionedComponent,
val => {
if (settings.blockInstance.dependsOn(val) !== true) {
return `${val.label} is not a dependency of ${settings.blockInstance.label}`;
}
return true;
}
]
}
);
// check assignment
this.assigningComponent = getAssigningComponent(settings);
if (this.assigningComponent.assignable !== true) {
throw new Error(`${this.assigningComponent.label} is not assignable`);
}
else if (!this.assigningComponent.applicableMaterialVariantGroups.find(mvg => mvg.has(settings.material))) {
// throw new Error(`${assigningComponent.label} applicable material variant groups are incompatible with ${settings.material.label}`);
console.warn(`${this.label} will affect no components.`);
}
// check dependencies
if (settings.positionedMesh && !settings.blockInstance.dependsOn(settings.positionedMesh)) {
throw new Error(`${settings.positionedMesh.label} is not a dependency of ${settings.blockInstance.label}`);
}
else if (settings.positionedMeshGroup) {
if ( !settings.blockInstance.dependsOn(settings.positionedMeshGroup)) {
throw new Error(`${settings.positionedMeshGroup.label} is not a dependency of ${settings.blockInstance.label}`);
}
else if (settings.positionedMesh && !settings.positionedMeshGroup.dependsOn(settings.positionedMesh)) {
throw new Error(`${settings.positionedMesh.label} is not a dependency of ${settings.positionedMeshGroup.label}`);
}
}
this._relevantMaterialSets = [];
this.materializableComponents = this.assigningComponent.materializable
? [ this.assigningComponent, ...this.assigningComponent.materializableDependencies ]
: this.assigningComponent.materializableDependencies;
this.affectedComponents = this.materializableComponents
.filter( dep =>
dep instanceof PositionedComponent && dep.component.materialVariantGroup.has( settings.material )
);
// set specificity
// this.specificity = settings.positionedMesh && settings.positionedMeshGroup
// ? 4
// : settings.positionedMesh
// ? 3
// : settings.positionedMeshGroup
// ? 2
// : 1
// ;
// fucking typescript
if (!this.blockInstance) {
/** @type {BlockInstance} */
this.blockInstance = settings.blockInstance;
}
if (!this.material) {
/** @type {WrappedMaterial} */
this.material = settings.material;
}
// this.positionedMeshes = settings.blockInstance.block.allDependencies.filter(dep => dep.constructor.name === 'PositionedMesh');
// const applicablePositionedMeshes = this.positionedMeshes.filter(pm => pm.component.materialVariantGroup.has(settings.material));
// if (applicablePositionedMeshes.length === 0) {
// console.warn(`${this.label} found no applicable meshes in ${settings.blockInstance.label} for ${settings.material.label} - wrong material variant groups`);
// }
// if (settings.mesh) {
// if (!applicablePositionedMeshes.includes(settings.mesh)) {
// throw new Error(`${settings.mesh.label} does not have ${settings.material.label} in its material variant group`);
// }
// this.applicablePositionedMeshes = [settings.mesh];
// }
// else {
// this.applicablePositionedMeshes = applicablePositionedMeshes;
// }
}
static _exportName = {
singular: 'materialAssignment',
plural: 'materialAssignments',
};
/**
* @static
* @type {ExportLevel}
*/
static _exportLevel = 'inline';
// static getassigningComponent = obj => obj.positionedMesh || obj.positionedMeshGroup || obj.blockInstance;
static treeLock = false;
/** @type {Array<Component>} */
affectedComponents;
/** @type {Array<MaterialSet>} */
_relevantMaterialSets;
_updateRelevantMaterialSets() {
if (this._tree) {
this._relevantMaterialSets = this._tree.materialSetsByMaterial(this.material);
}
}
/** @type {Package} */
_tree;
_onTreeSet() {
if (!this._tree.findComponentById(this.blockInstance.block.id)) {
throw new Error(`BlockInstance.block (the BlockInstance's "template") not in tree`);
}
if (!this._tree.findComponentById(this.material.id)) {
throw new Error('Material not in tree');
}
this._tree.on('material-sets-by-material-update', this._updateRelevantMaterialSets.bind(this), this.id);
// const relevantMaterialSets = this._tree.materialSetsByMaterial(this.material);
// // which quality??
// const applicableMeshes = this.blockInstance.content.medium.origin
// this.material
// mat => {
// // find all meshes with material sets with this material
// const block = blockInstance.block;
// // meshes need to be found by their userData.origin property
// return true;
// }
}
_onTreeUnset() {
this._tree.removeListener('material-sets-by-material-update', this.id);
}
_build(part, quality, dependencies) {
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':
// const applicablePositionedMeshIds = this.applicablePositionedMeshes.map(apm => apm.id);
// /** @type {Group} */
// const actualBlockGroup = this.blockInstance.content.main[quality];
// console.assert(actualBlockGroup instanceof Group);
// if (this.applicablePositionedMeshes.length > 0) {
// /** @type {Array<Mesh>} */
// const applicableActualMeshes = [];
// actualBlockGroup.traverse(child => {
// if (applicablePositionedMeshIds.includes(child.userData?.origin)) {
// applicableActualMeshes.push(child)
// }
// });
// // modifying the actual meshes (content) in the BlockInstance
// this.report({ msg: `Applying material to ${applicableActualMeshes.length} meshes in block` });
// applicableActualMeshes.forEach(aaMesh => {
// aaMesh.material = this.material.content.main[quality];
// aaMesh.needsUpdate = true;
// });
// }
this._setContent('main', quality, this._settings.material);
break;
default:
this._setContent(part, quality, null);
break;
}
return this;
}
}
export { MaterialAssignment };