Source: actor/actor.js

import { Object3D, Group, Vector3, Quaternion } from '../../node_modules/three/build/three.module.js';
import { Reporter } from '../reporter/reporter.js';
import { InformationSource } from '../reporter/information_source.js';
import { BuildableComponent } from '../package/component/buildable_component.js';
import { StateTracker } from '../state_tracker.js';
import { Project } from '../project.js';

/**
 * Actor
 * @event Actor#bodychange emitted whenever the body changes, i.e. when a configuration is changed (but not when an element is dragged or rotated)
 */

class Actor extends StateTracker {

    /**
     * @param {Reporter} reporter 
     * @param {Object} settings 
     */

    constructor(reporter, settings) {
        
        super(reporter, settings);

        this.addEvent('bodychange');

        this.hiddenBody = new Group();
        this.body = new Group();
        this.body.name = "Actor"
        this.body.userData.origin = this;
        this.body.castShadow = true;
        this.body.receiveShadow = true;

        this.visible = true;
    }

    /** @type {Boolean} */
    visible;

    show() {
        console.log('show', this.label)
        // while (this.body.children.length > 0) {
        //     this.body.add(this.hiddenBody.children[0]);
        // }
        this.body.traverse( function( child ) { child.layers.set( Project.layerMap.visibleActors ) } )
        this.visible = true;
    }

    hide() {
        console.log('hide', this.label)
        // while (this.body.children.length > 0) {
        //     this.hiddenBody.add(this.body.children[0]);
        // }
        this.body.traverse( function( child ) { child.layers.set( Project.layerMap.hiddenActors ) } )
        this.visible = false;
    }

    /**
     * Removes all children from the body Group and disposes their
     * Three.js resources (geometries, materials, textures) to prevent
     * GPU memory leaks.
     */

    clearBody() {
        while (this.body.children.length > 0) {
            const child = this.body.children[0];
            this.body.remove(child);
            
            // Dispose Three.js resources recursively
            if (child.traverse) {
                child.traverse(node => {
                    if (node.geometry && node.geometry.dispose) {
                        node.geometry.dispose();
                    }
                    if (node.material) {
                        if (Array.isArray(node.material)) {
                            node.material.forEach(m => { if (m.dispose) m.dispose(); });
                        } else if (node.material.dispose) {
                            node.material.dispose();
                        }
                    }
                    if (node.dispose && node !== child) {
                        node.dispose();
                    }
                });
            }
            
            // Dispose the child itself
            if (child.dispose) {
                child.dispose();
            }
        }
        
        // Also clean up hiddenBody
        while (this.hiddenBody.children.length > 0) {
            const child = this.hiddenBody.children[0];
            this.hiddenBody.remove(child);
            if (child.traverse) {
                child.traverse(node => {
                    if (node.geometry && node.geometry.dispose) node.geometry.dispose();
                    if (node.material) {
                        if (Array.isArray(node.material)) {
                            node.material.forEach(m => { if (m.dispose) m.dispose(); });
                        } else if (node.material.dispose) {
                            node.material.dispose();
                        }
                    }
                });
            }
            if (child.dispose) child.dispose();
        }
    }


    /**
     * @param {Object3D} newContent 
     */

    async updateBody(newContent, quaternion, position, modTransform) {
        
        this.clearBody()

        this.body.add(newContent);

        if ( quaternion ) {
            this.body.quaternion.copy( quaternion );
        }

        if ( position) {
            if (modTransform && modTransform.translation) {
                this.body.position.copy( new Vector3().addVectors( position, modTransform.translation ));
            }
            else {
                this.body.position.copy( position );
            }
        }

        await this.emit('bodychange', this.body);
    }

    destroy() {
        this.clearBody();
        if ( typeof super.destroy === 'function' ) {
            super.destroy()
        }
    }


    /** @type {Group} */

    body = new Group();
}

export { Actor };