import { Block } from '../package/block/block.js';
import { Reporter } from '../reporter/reporter.js';
import { checkPropTypes } from '../lib.js';
import { Connectable } from './connectable.js';
import { Component } from '../component/component.js';
import { Group } from '../../node_modules/three/build/three.module.js';
import { Project } from '../project.js';
/**
* Block instance
*/
class BlockInstance extends Connectable {
/**
* @param {Reporter} reporter
* @param {Object} settings
* @param {UUID} [settings.id]
* @param {string} [settings.name]
* @param {Block} settings.block
*/
constructor(reporter, settings) {
checkPropTypes(
settings,
{
block: Block
}
);
super(
reporter,
settings
);
this.bestMainContent = new Group();
this.bestMainContent.castShadow = true;
this.bestMainContent.receiveShadow = true;
if (!this.block) {
/** @type {Block} */
this.block = settings.block;
}
this.assignableByDefinerId = {};
this.assignables = this.block.subAssignables.map(sa => ({
blockInstance: this,
...sa
}));
if (this.assignable === true) {
this.assignables.push({
blockInstance: this,
assigningComponent: this,
materialVariantGroups: this.applicableMaterialVariantGroups,
name: this.block.name || this.block.slug
});
}
for (let assignable of this.assignables) {
this.assignableByDefinerId[assignable.assigningComponent.id] = assignable;
const biAssId = `BI:${this.slug}`;
assignable.id = assignable.id ? `${biAssId}-${assignable.id}` : biAssId;
}
this.boundingBox = settings.block.boundingBox;
}
static _exportName = {
singular: 'blockInstance',
plural: 'blockInstances',
}
/** @type {ExportLevel} */
static _exportLevel = 'inline';
static connectorTemplateSetting = 'block';
static treeLock = false;
/**
* @type {Object<UUID,Component>}
*/
assignableByDefinerId;
/**
* Whether a block instance is assignable depends on the block settings
* @type {Boolean}
*/
get assignable() {
return this.block.assignable;
}
/**
* A block instance, like a block, does not have a material
* @type {Boolean}
*/
get materializable() {
return false;
}
// get assignedMaterials() {
// return this.assignables
// }
_onTreeSet() {
if (!this._tree.findComponentById(this.block.id)) {
throw new Error('Block (template) not in tree');
}
super._onTreeSet();
}
_onTreeUnset() {
super._onTreeUnset();
}
/**
* Set content and update status to "ready"
* @protected
* @method
* @param {ComponentPart} part
* @param {LoadingQuality} quality
* @param {any} content
* @returns {Promise<Array<any>>}
*/
_setContent(part, quality, content) {
// super will do this again
this._content[part][quality] = content;
if (part === 'main') {
while (this.bestMainContent.children.length > 0) {
this.bestMainContent.remove(this.bestMainContent.children[0]);
}
const bestContent = this._content.main.medium;
// const bestContent = this._content.main.high !== null
// ? this._content.main.high
// : this._content.main.medium !== null
// ? this._content.main.medium
// : this._content.main.low !== null
// ? this._content.main.low
// : this.mainPlaceholder !== undefined
// ? this.mainPlaceholder
// : null
// ;
if (bestContent !== null) {
// console.log( bestContent )
this.bestMainContent.add(bestContent)
}
}
return super._setContent(part, quality, content);
}
bestMainContent;
/** Array<Transform> */
// transforms;
// addTransform(transform) {
// }
get assignedMaterials() {
if (!this._tree) {
return [];
}
else {
return this._tree.materialAssignments.filter(ma => ma.blockInstance === this);
}
}
async _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 block = this._settings.block.content.main[quality].block;
const blockClone = block.clone();
blockClone.castShadow = true;
blockClone.receiveShadow = true;
// so we can use Object3D.getObjectByName in the scene
// https://threejs.org/docs/#api/en/core/Object3D.getObjectByName
blockClone.name = this.id;
// blockClone.userData.origin = this.id;
blockClone.userData.PB = {
type: 'blockInstance',
origin: this.id,
blockInstance: this,
isAssignable: this.assignable,
assignableComponent: this.assignable === true
? {
blockInstance: this
}
: null
};
// export the cloned block and its connectors
const content = blockClone;
blockClone.traverse(obj3D => {
if (!obj3D.userData) {
obj3D.userData = {};
}
if (!obj3D.userData.PB) {
obj3D.userData.PB = {};
}
if (obj3D.userData.PB.type === 'PositionedMeshGroup') {
obj3D.userData.PB.positionedMeshGroup = obj3D.userData.PB.origin;
}
else {
// could be undefined or a value, but copy it
// the parent here could be the configuration wrapper, which has no PB userdata
obj3D.userData.PB.positionedMeshGroup = obj3D.parent?.userData?.PB?.positionedMeshGroup;
}
if (obj3D.userData.PB.type === 'PositionedMesh') {
obj3D.userData.PB.positionedMesh = obj3D.userData.PB.origin;
obj3D.userData.PB.processed = true;
}
else {
// could be undefined or a value, but copy it
obj3D.userData.PB.positionedMesh = obj3D.parent?.userData?.PB?.positionedMesh;
}
if (obj3D.userData.PB.isAssignable === true) {
// obj3D.userData.PB.assignableComponent = this.assignableByDefinerId[obj3D.userData.PB.origin];
obj3D.userData.PB.assignable = this.assignables.find( assignable =>
assignable.blockInstance === obj3D.userData.PB.blockInstance
&&
assignable.positionedMeshGroup === obj3D.userData.PB.positionedMeshGroup
&&
assignable.positionedMesh === obj3D.userData.PB.positionedMesh
);
}
else {
// obj3D.userData.PB.assignableComponent = obj3D.parent.userData?.PB?.assignableComponent;
obj3D.userData.PB.assignable = obj3D.parent?.userData?.PB?.assignable;
}
})
// after a time out traverse rthe 3d structure
// for every object3D check the assignable of the parent and copy + adapt
this._setContent('main', quality, content);
break;
default:
this._setContent(part, quality, null);
break;
}
return this;
}
}
export { BlockInstance }