import { vec3 , quat , mat4 } from "gl-matrix" ; import { AABB , IAABBBounding } from "../AABB" ; import { Renderer } from "../Renderer" ; import { GetUniqueId } from "../utils" ; export interface IContainerOptions { name ? : string ; translation ? : vec3 ; rotation ? : quat ; scale ? : vec3 ; pivot ? : vec3 ; parent ? : Container ; visible ? : boolean ; transformAutoUpdate ? : boolean ; } export const CONTAINER_DEFAULT_OPTIONS = { name : null , translation : null , rotation : null , scale : null , pivot : null , parent : null , visible : true , transformAutoUpdate : true } ; export class Container { private _id : number = 0 ; private _name : string = null ; private _parent : Container = null ; private _pivot : vec3 = null ; private _translation : vec3 = null ; private _rotation : quat = null ; private _scale : vec3 = null ; private _modelMatrix : mat4 = null ; private _modelInverseMatrix : mat4 = null ; private _rotationMatrix : mat4 = null ; private _children : Container [ ] = [ ] ; private _boundings : AABB = null ; private _destroyedState : boolean = false ; private _visibilityState : boolean = false ; private _transformAutoUpdateState : boolean = false ; param options public constructor ( options ? ) { options = Object . assign ( { } , CONTAINER_DEFAULT_OPTIONS , options ) ; this . _id = GetUniqueId ( ) ; this . setName ( options . name ) ; this . _parent = options . parent ; this . _translation = options . translation ? options . translation : vec3 . create ( ) ; this . _rotation = options . rotation ? options . rotation : quat . create ( ) ; this . _scale = options . scale ? options . scale : vec3 . fromValues ( 1 , 1 , 1 ) ; this . _pivot = options . pivot ; this . _visibilityState = options . visible ; this . _transformAutoUpdateState = options . transformAutoUpdate ; this . _modelMatrix = mat4 . create ( ) ; this . _modelInverseMatrix = mat4 . create ( ) ; this . _rotationMatrix = mat4 . create ( ) ; this . _boundings = new AABB ( ) ; if ( this . doTransformAutoUpdate ( ) ) { this . updateTransform ( ) ; } } /**
     * The container name
     */ public getName ( ) { return this . _name ; } param value public setName ( value ) { this . _name = value ; } /**
     * The container's unique id
     */ public getId ( ) { return this . _id ; } /**
     * Returns the parent container
     */ public getParent ( ) { return this . _parent ; } /**
     * Returns the top-level parent container
     */ public getTopLevelParent ( ) { let current = this . getParent ( ) ; while ( current !== null ) { current = current . getParent ( ) ; } return current ; } param parent public setParent ( value ) { this . _parent = value ; } /**
     * Returns the children of this container
     */ public getChildren ( ) { return this . _children ; } /**
     * The model matrix of this container
     */ public getModelMatrix ( ) { return this . _modelMatrix ; } /**
     * The model inverse matrix of this container
     */ public getModelInverseMatrix ( ) { return this . _modelInverseMatrix ; } /**
     * The rotation matrix of this container
     */ public getRotationMatrix ( ) { return this . _rotationMatrix ; } /**
     * The boundings of this container
     */ public getBoundings ( ) { return this . _boundings ; } /**
     * Indicates if the container gets rendered
     */ public isVisible ( ) { return this . _visibilityState ; } /**
     * Enables that the container should be rendered
     */ public show ( ) { this . _visibilityState = true ; } /**
     * Disables that the container should be rendered
     */ public hide ( ) { this . _visibilityState = false ; } /**
     * Indicates if the container is destroyed
     */ public isDestroyed ( ) { return this . _destroyedState ; } /**
     * Indicates if the transforms of the container should be updated automatically
     */ public doTransformAutoUpdate ( ) { return this . _transformAutoUpdateState ; } param vec public localToWorld ( vec ) { return vec3 . transformMat4 ( vec , vec , this . getModelMatrix ( ) ) ; } param vec public worldToLocal ( vec ) { return vec3 . transformMat4 ( vec , vec , this . getModelInverseMatrix ( ) ) ; } /**
     * The container's rotation pivot
     */ public getPivot ( ) { return this . _pivot ; } param value public setPivot ( value ) { if ( this . _pivot === null ) { this . _pivot = vec3 . create ( ) ; } if ( value !== null ) { vec3 . copy ( this . _pivot , value ) ; } else { this . _pivot = null ; } } /**
     * The container's translation
     */ public getTranslation ( ) { return this . _translation ; } param value public setTranslation ( value ) { vec3 . copy ( this . _translation , value ) ; } /**
     * The container's rotation
     */ public getRotation ( ) { return this . _rotation ; } param value public setRotation ( value ) { quat . copy ( this . _rotation , value ) ; } /**
     * The container's scale
     */ public getScale ( ) { return this . _scale ; } param value public setScale ( value ) { vec3 . copy ( this . _scale , value ) ; } param value public translate ( value ) { const translation = this . _translation ; translation [ 0 ] += value [ 0 ] ; translation [ 1 ] += value [ 1 ] ; translation [ 2 ] += value [ 2 ] ; } param value public translateX ( value ) { this . _translation [ 0 ] += value ; } param value public translateY ( value ) { this . _translation [ 1 ] += value ; } param value public translateZ ( value ) { this . _translation [ 2 ] += value ; } param value public rotate ( value ) { const rotation = this . _rotation ; quat . rotateX ( rotation , rotation , value [ 0 ] ) ; quat . rotateY ( rotation , rotation , value [ 1 ] ) ; quat . rotateZ ( rotation , rotation , value [ 2 ] ) ; } param value public rotateX ( value ) { quat . rotateX ( this . _rotation , this . _rotation , value ) ; } param value public rotateY ( value ) { quat . rotateY ( this . _rotation , this . _rotation , value ) ; } param value public rotateZ ( value ) { quat . rotateZ ( this . _rotation , this . _rotation , value ) ; } param axis param angle public rotateByAxisAngle ( axis , angle ) { const rotation = this . _rotation ; const half = angle / 2 ; const s = Math . sin ( half ) ; rotation [ 0 ] = axis [ 0 ] * s ; rotation [ 1 ] = axis [ 1 ] * s ; rotation [ 2 ] = axis [ 2 ] * s ; rotation [ 3 ] = Math . cos ( half ) ; } param value public scale ( value ) { const scale = this . _scale ; scale [ 0 ] *= value [ 0 ] ; scale [ 1 ] *= value [ 1 ] ; scale [ 2 ] *= value [ 2 ] ; } param value public scaleX ( value ) { this . _scale [ 0 ] *= value ; } param value public scaleY ( value ) { this . _scale [ 1 ] *= value ; } param value public scaleZ ( value ) { this . _scale [ 2 ] *= value ; } param id public getChildById ( id ) { const children = this . _children ; if ( this . getId ( ) === id ) return this ; for ( let ii = 0 ; ii < children . length ; ++ ii ) { const child = children [ ii ] ; const result = child . getChildById ( id ) ; if ( result !== null ) return child ; } return null ; } param name public getChildByName ( name ) { const children = this . _children ; if ( this . getName ( ) === name ) return this ; for ( let ii = 0 ; ii < children . length ; ++ ii ) { const child = children [ ii ] ; const result = child . getChildByName ( name ) ; if ( result !== null ) return child ; } return null ; } param child public addChild ( child ) { const childIndex = this . _children . indexOf ( child ) ; if ( childIndex === - 1 ) { child . setParent ( this ) ; this . _children . push ( child ) ; return this . _children . length - 1 ; } return - 1 ; } param children public addChildren ( children ) { for ( let ii = 0 ; ii < children . length ; ++ ii ) { const child = children [ ii ] ; const childIndex = this . _children . indexOf ( child ) ; if ( childIndex === - 1 ) { child . setParent ( this ) ; this . _children . push ( child ) ; } } } param child public hasChild ( child ) { const childIndex = this . _children . indexOf ( child ) ; return childIndex !== - 1 ; } param child public removeChild ( child ) { const childIndex = this . _children . indexOf ( child ) ; if ( childIndex !== - 1 ) { const child = this . _children [ childIndex ] ; child . setParent ( null ) ; this . _children . splice ( childIndex , 1 ) ; return childIndex ; } return - 1 ; } param renderer public update ( renderer ) { if ( this . isDestroyed ( ) ) return ; if ( this . doTransformAutoUpdate ( ) ) { this . updateTransform ( ) ; } const children = this . getChildren ( ) ; for ( let ii = 0 ; ii < children . length ; ++ ii ) { const child = children [ ii ] ; child . update ( renderer ) ; } if ( this . doTransformAutoUpdate ( ) ) { this . updateBoundings ( ) ; } } param encoder public render ( encoder ) { if ( this . isDestroyed ( ) || ! this . isVisible ( ) ) return ; const children = this . getChildren ( ) ; for ( let ii = 0 ; ii < children . length ; ++ ii ) { const child = children [ ii ] ; child . render ( encoder ) ; } } /**
     * Destroy this Object
     */ public destroy ( ) { this . _destroyedState = true ; this . _name = null ; this . _parent = null ; this . _translation = null ; this . _rotation = null ; this . _scale = null ; this . _modelMatrix = null ; this . _modelInverseMatrix = null ; this . _rotationMatrix = null ; const children = this . getChildren ( ) ; for ( let ii = 0 ; ii < children . length ; ++ ii ) { const child = children [ ii ] ; child . destroy ( ) ; } this . _children = null ; this . _boundings = null ; } /**
     * Update the transforms of this container
     */ public updateTransform ( ) { const parent = this . getParent ( ) ; const mModel = this . getModelMatrix ( ) ; const mModelInverse = this . getModelInverseMatrix ( ) ; const mRotation = this . getRotationMatrix ( ) ; const translation = this . _translation ; const rotation = this . _rotation ; const scale = this . _scale ; const pivot = this . _pivot ; mat4 . identity ( mModel ) ; mat4 . identity ( mRotation ) ; mat4 . translate ( mModel , mModel , translation ) ; quat . normalize ( rotation , rotation ) ; mat4 . fromQuat ( mRotation , rotation ) ; mat4 . multiply ( mModel , mModel , mRotation ) ; mat4 . scale ( mModel , mModel , scale ) ; if ( parent !== null ) { mat4 . multiply ( mModel , mModel , parent . getModelMatrix ( ) ) ; } if ( pivot != null ) { const px = pivot [ 0 ] ; const py = pivot [ 1 ] ; const pz = pivot [ 2 ] ; mModel [ 12 ] += px - ( mModel [ 0 ] * px ) - ( mModel [ 4 ] * py ) - ( mModel [ 8 ] * pz ) ; mModel [ 13 ] += py - ( mModel [ 1 ] * px ) - ( mModel [ 5 ] * py ) - ( mModel [ 9 ] * pz ) ; mModel [ 14 ] += pz - ( mModel [ 2 ] * px ) - ( mModel [ 6 ] * py ) - ( mModel [ 10 ] * pz ) ; } mat4 . invert ( mModelInverse , mModel ) ; } /**
     * Compute the boundings of this container
     */ public computeBoundings ( ) { const children = this . getChildren ( ) ; const min = vec3 . fromValues ( Infinity , Infinity , Infinity ) ; const max = vec3 . fromValues ( - Infinity , - Infinity , - Infinity ) ; for ( let ii = 0 ; ii < children . length ; ++ ii ) { const child = children [ ii ] ; const childBoundings = child . getBoundings ( ) ; { const [ x , y , z ] = childBoundings . getMin ( ) ; if ( x < min [ 0 ] ) min [ 0 ] = x ; if ( y < min [ 1 ] ) min [ 1 ] = y ; if ( z < min [ 2 ] ) min [ 2 ] = z ; } { const [ x , y , z ] = childBoundings . getMax ( ) ; if ( x > max [ 0 ] ) max [ 0 ] = x ; if ( y > max [ 1 ] ) max [ 1 ] = y ; if ( z > max [ 2 ] ) max [ 2 ] = z ; } } const mModel = this . getModelMatrix ( ) ; vec3 . transformMat4 ( min , min , mModel ) ; vec3 . transformMat4 ( max , max , mModel ) ; return { min , max } ; } /**
     * Update the boundings of this container
     */ public updateBoundings ( ) { const boundings = this . getBoundings ( ) ; const { min , max } = this . computeBoundings ( ) ; if ( ! vec3 . exactEquals ( boundings . getMin ( ) , min ) || ! vec3 . exactEquals ( boundings . getMax ( ) , max ) ) { vec3 . copy ( boundings . getMin ( ) , min ) ; vec3 . copy ( boundings . getMax ( ) , max ) ; let current = this . getParent ( ) ; while ( current !== null ) { current . updateBoundings ( ) ; current = current . getParent ( ) ; } } } }