import { findActorByName , isSpline } from '@lib/graphics/entityHelper' ; import { Actor } from 'satisfactory-json' ; import { BufferGeometry , Material , Mesh , MeshMatcapMaterial , Object3D , Scene } from 'three' ; import ColorFactory from './ColorFactory' ; import GeometryFactory from './GeometryFactory' ; import InstancedMeshGroup from './InstancedMeshGroup' ; import { MeshResult } from './MeshFactory' ; import { InstancedModelMesh , ModelMesh , ThreeModelMesh } from './ModelMesh' ; /**
 * manages the meshes displayed on the playground
 */ export default class MeshManager { visibleMeshes : ModelMesh [ ] = [ ] ; invisibleMeshes : ModelMesh [ ] = [ ] ; raycastActiveMeshes : Mesh [ ] = [ ] ; raycastInactiveMeshes : Mesh [ ] = [ ] ; meshByName : { [ id ] : { index : number ; visibility : boolean ; } ; } = { } ; meshDictionaryDirty : boolean = false ; instancedMeshGroups : { [ id ] : InstancedMeshGroup ; } = { } ; scene : Scene ; material : Material ; constructor ( scene , material ) { this . scene = scene ; this . material = material ; } private refreshMeshDictionary ( ) { this . meshDictionaryDirty = false ; this . meshByName = { } ; for ( let i = 0 ; i < this . visibleMeshes . length ; i ++ ) { const mesh = this . visibleMeshes [ i ] ; this . meshByName [ mesh . getPathName ( ) ] = { index : i , visibility : true } ; } for ( let i = 0 ; i < this . invisibleMeshes . length ; i ++ ) { const mesh = this . invisibleMeshes [ i ] ; this . meshByName [ mesh . getPathName ( ) ] = { index : i , visibility : false } ; } } param result param visible add ( result , visible ) { let modelMesh ; if ( result . instance === undefined ) { modelMesh = new ThreeModelMesh ( result . mesh ) ; } else { if ( this . instancedMeshGroups [ result . instance ] === undefined ) { this . instancedMeshGroups [ result . instance ] = new InstancedMeshGroup ( this . material , result . mesh . geometry as BufferGeometry ) ; } const index = this . instancedMeshGroups [ result . instance ] . add ( { pathName : result . mesh . userData . pathName , visible : visible , position : result . mesh . position , quat : result . mesh . quaternion , scale : result . mesh . scale , color : ( result . mesh . material as MeshMatcapMaterial ) . color } ) ; result . mesh . userData . instance = result . instance ; result . mesh . userData . index = index ; modelMesh = new InstancedModelMesh ( this . instancedMeshGroups [ result . instance ] , index , result . mesh ) ; } result . mesh . updateMatrixWorld ( true ) ; if ( visible ) { modelMesh . addToScene ( this . scene ) ; this . visibleMeshes . push ( modelMesh ) ; this . raycastActiveMeshes . push ( modelMesh . getRaycastMesh ( ) ) ; } else { this . invisibleMeshes . push ( modelMesh ) ; this . raycastInactiveMeshes . push ( modelMesh . getRaycastMesh ( ) ) ; } this . meshDictionaryDirty = true ; } /**
     * Builds the InstancedMeshGroups
     *
     * Should be called after all actors are added to the scene
     */ buildInstancedMeshGroups ( ) { for ( const key in this . instancedMeshGroups ) { this . scene . add ( this . instancedMeshGroups [ key ] . buildInstancedMesh ( ) as Object3D ) ; } } findMeshByName ( pathName ) { if ( this . meshDictionaryDirty ) { this . refreshMeshDictionary ( ) ; } const mesh = this . meshByName [ pathName ] ; if ( mesh === undefined ) { return null ; } if ( mesh . visibility ) { return this . visibleMeshes [ mesh . index ] ; } else { return this . invisibleMeshes [ mesh . index ] ; } } /**
     * to search in both lists, but also returns if the mesh was in the visible list
     */ findMeshAndVisibilityByName ( pathName ) { if ( this . meshDictionaryDirty ) { this . refreshMeshDictionary ( ) ; } const mesh = this . meshByName [ pathName ] ; if ( mesh === undefined ) { return null ; } if ( mesh . visibility ) { return { mesh : this . visibleMeshes [ mesh . index ] , visible : true } ; } else { return { mesh : this . invisibleMeshes [ mesh . index ] , visible : true } ; } } findVisibleMeshByName ( pathName ) { if ( this . meshDictionaryDirty ) { this . refreshMeshDictionary ( ) ; } const mesh = this . meshByName [ pathName ] ; if ( mesh === undefined ) { return null ; } if ( mesh . visibility ) { return this . visibleMeshes [ mesh . index ] ; } else { return null ; } } findInvisibleMeshByName ( pathName ) { if ( this . meshDictionaryDirty ) { this . refreshMeshDictionary ( ) ; } const mesh = this . meshByName [ pathName ] ; if ( mesh === undefined ) { return null ; } if ( mesh . visibility ) { return null ; } else { return this . invisibleMeshes [ mesh . index ] ; } } updateClassVisibility ( val ) { for ( let i = 0 ; i < val . length ; i ++ ) { const item = val [ i ] ; if ( item . visible ) { for ( let j = this . invisibleMeshes . length - 1 ; j >= 0 ; j -- ) { const mesh = this . invisibleMeshes [ j ] ; if ( findActorByName ( mesh . getPathName ( ) ) ! . className === item . name ) { mesh . addToScene ( this . scene ) ; this . invisibleMeshes . splice ( j , 1 ) ; this . visibleMeshes . push ( mesh ) ; this . raycastActiveMeshes . push ( this . raycastInactiveMeshes [ j ] ) ; this . raycastInactiveMeshes . splice ( j , 1 ) ; } } } else { for ( let k = this . visibleMeshes . length - 1 ; k >= 0 ; k -- ) { const mesh = this . visibleMeshes [ k ] ; if ( findActorByName ( mesh . getPathName ( ) ) ! . className === item . name ) { mesh . removeFromScene ( this . scene ) ; this . visibleMeshes . splice ( k , 1 ) ; this . invisibleMeshes . push ( mesh ) ; this . raycastInactiveMeshes . push ( this . raycastActiveMeshes [ k ] ) ; this . raycastActiveMeshes . splice ( k , 1 ) ; } } } } this . meshDictionaryDirty = true ; } rebuildConveyorBelts ( geometryFactory ) { for ( const mesh of this . visibleMeshes . concat ( this . invisibleMeshes ) ) { const actor = findActorByName ( mesh . getPathName ( ) ) ; if ( actor === undefined ) continue ; if ( isSpline ( actor ) ) { mesh . rebuildGeometry ( actor , geometryFactory ) ; } } } deleteMeshesForActors ( actors ) { this . meshDictionaryDirty = true ; for ( const actor of actors ) { let found = false ; for ( let i = this . visibleMeshes . length - 1 ; i >= 0 ; i -- ) { if ( this . visibleMeshes [ i ] . getPathName ( ) === actor . pathName ) { this . visibleMeshes [ i ] . removeFromScene ( this . scene ) ; this . visibleMeshes . splice ( i , 1 ) ; this . raycastActiveMeshes . splice ( i , 1 ) ; found = true ; break ; } } if ( found ) { continue ; } for ( let j = this . invisibleMeshes . length - 1 ; j >= 0 ; j -- ) { if ( this . invisibleMeshes [ j ] . getPathName ( ) === actor . pathName ) { this . invisibleMeshes [ j ] . removeFromScene ( this . scene ) ; this . invisibleMeshes . splice ( j , 1 ) ; this . raycastInactiveMeshes . splice ( j , 1 ) ; break ; } } } } updateAllMaterials ( colorFactory ) { console . log ( 'updateAllMaterials' ) ; for ( const mesh of this . visibleMeshes . concat ( this . invisibleMeshes ) ) { const actor = findActorByName ( mesh . getPathName ( ) ) ; if ( actor === undefined ) continue ; mesh . rebuildColor ( actor , colorFactory ) ; } } dispose ( scene ) { for ( const mesh of this . visibleMeshes ) { mesh . removeFromScene ( scene ) ; mesh . dispose ( ) ; } for ( const mesh of this . invisibleMeshes ) { mesh . dispose ( ) ; } for ( const key in this . instancedMeshGroups ) { this . instancedMeshGroups [ key ] . dispose ( ) ; } } }