import { Asset } from "./Asset" ; import { Entity , SerializedEntity , EntityInternal } from "../core/Entity" ; import { PrefabInstances , SerializedObjectType , ObjectOverride , PrefabInstance } from "../serialization/SerializedTypes" ; import { SerializerUtils } from "../serialization/SerializerUtils" ; import { AsyncUtils } from "../core/AsyncUtils" ; import { Prefab } from "./Prefab" ; import * as Attributes from "../core/Attributes" ; import { Reference , SerializedReference } from "../serialization/Reference" ; import { Environment , ColorEnvironment } from "../graphics/Environment" ; import { Fog } from "../graphics/Fog" ; import { SerializedObject } from "../core/SerializableObject" ; import { Component } from "../core/Component" ; import { IObjectManagerInternal } from "../core/IObjectManager" ; import { GraphicUtils } from "../graphics/GraphicUtils" ; hidden export interface PrefabToLoadInfo { prefabId : string ; instanceInScene : Entity ; } hidden export interface SerializedScene { typeName : string ; version : number ; id : string ; name : string ; root : SerializedEntity ; prefabInstances ? : PrefabInstances ; environment : SerializedReference ; fog : SerializedReference ; } export class Scene extends Asset { get version ( ) { return 5 ; } @ Attributes . hidden ( ) root : Entity ; hidden @ Attributes . hidden ( ) prefabInstances ? : PrefabInstances ; set fog ( fog ) { if ( fog === this . _fog . instance ) { return ; } GraphicUtils . invalidateShaders ( ) ; this . _fog . instance = fog ; } get fog ( ) { return this . _fog . instance ; } set environment ( environment ) { if ( environment === this . _environment . instance ) { return ; } GraphicUtils . invalidateShaders ( ) ; this . _environment . instance = environment ; } get environment ( ) { return this . _environment . instance ; } private _environment : Reference < Environment > ; private _fog : Reference < Fog > ; @ Attributes . unserializable ( ) private _isLoaded = false ; constructor ( ) { super ( ) ; this . root = new Entity ( ) ; this . root . name = "root" ; this . _environment = new Reference ( Environment , new ColorEnvironment ( ) ) ; this . _fog = new Reference ( Fog ) ; } isLoaded ( ) { if ( this . _isLoaded ) { return true ; } if ( ! this . root . isLoaded ( ) ) { return false ; } this . _isLoaded = true ; return true ; } serialize ( ) { const serializedScene = { typeName : this . constructor . name , version : this . version , id : this . id , name : this . name , root : SerializerUtils . serializeEntityWithPrefabRefs ( this . root ) , environment : SerializerUtils . serializeProperty ( this . _environment ) , fog : SerializerUtils . serializeProperty ( this . _fog ) } ; if ( process . env . NODE_ENV === "development" ) { if ( this . prefabInstances ) { const keys = Object . keys ( this . prefabInstances ) ; for ( const prefabInstance of keys ) { if ( this . root . findChildById ( prefabInstance ) === undefined ) { console . assert ( false , "Unused prefab instance" ) ; delete this . prefabInstances [ prefabInstance ] ; } } } } if ( this . prefabInstances && Object . keys ( this . prefabInstances ) . length > 0 ) { serializedScene . prefabInstances = this . prefabInstances ; } return serializedScene ; } deserialize ( _json ) { const json = _json as SerializedScene ; if ( json . version === 1 ) { let oldJson = ( _json as SerializedObject ) ; json . root = oldJson . properties . root as any ; json . id = oldJson . properties . id as any ; json . name = oldJson . properties . name as any ; } else if ( json . version === 2 ) { } else if ( json . version === 3 ) { SerializerUtils . deserializeProperty ( this , "_environment" , "Reference" , json . environment ) ; } else { SerializerUtils . deserializeProperty ( this , "_environment" , "Reference" , json . environment ) ; SerializerUtils . deserializeProperty ( this , "_fog" , "Reference" , json . fog ) ; } this . id = json . id ; this . name = json . name ; this . prefabInstances = json . prefabInstances ; SerializerUtils . deserializeEntity ( this . root , json . root ) ; const prefabsToLoad = [ ] ; this . root . traverse ( e => { if ( e . prefabId ) { prefabsToLoad . push ( { prefabId : e . prefabId , instanceInScene : e } ) ; return false ; } return true ; } ) ; return new Promise < Scene > ( resolve => { AsyncUtils . processBatch ( prefabsToLoad , ( info , success , error ) => { this . setEntityFromPrefab ( info . instanceInScene , info . prefabId ) . then ( success ) . catch ( error ) ; } , hasErrors => resolve ( this ) ) ; } ) ; } destroy ( ) { this . root . destroy ( ) ; super . destroy ( ) ; } setEntityFromPrefab ( entity , prefabId ) { return Promise . resolve ( ) . then ( ( ) => { if ( process . env . CONFIG === "editor" ) { return IObjectManagerInternal . instance . loadObjectById ( prefabId ) ; } else { return IObjectManagerInternal . instance . loadObject ( prefabId ) ; } } ) . then ( ( [ prefab ] ) => { this . setEntityFromPrefabInstance ( entity , prefab as Prefab ) ; return prefab as Prefab ; } ) ; } setEntityFromPrefabInstance ( entity , prefab ) { entity . prefabId = prefab . id ; const prefabInstanceInfo = this . prefabInstances ? this . prefabInstances [ entity . id ] : undefined ; let prefabIdToInstanceId = prefabInstanceInfo ? prefabInstanceInfo . prefabIdToInstanceId : undefined ; let existingIdMap = true ; if ( ! prefabIdToInstanceId ) { existingIdMap = false ; prefabIdToInstanceId = { } ; } let uniquePrefabRoot ; if ( existingIdMap ) { uniquePrefabRoot = prefab . root . copyWithIdMap ( prefabIdToInstanceId ) ; } else { uniquePrefabRoot = prefab . root . copy ( prefabIdToInstanceId , true ) as Entity ; } console . assert ( entity . children . length === 0 ) ; for ( const child of uniquePrefabRoot . children ) { child . parent = entity ; entity . children . push ( child ) ; } uniquePrefabRoot . iterateComponents ( c => EntityInternal . setComponentFromInstance ( entity , c ) ) ; if ( existingIdMap ) { const _prefabInstanceInfo = prefabInstanceInfo as PrefabInstance ; entity . traverse ( e => { if ( _prefabInstanceInfo . overrides && e . id in _prefabInstanceInfo . overrides ) { this . overrideEntity ( e , _prefabInstanceInfo . overrides [ e . id ] ) ; } return true ; } , true ) ; } else { if ( ! this . prefabInstances ) { this . prefabInstances = { } ; } entity . id = prefabIdToInstanceId [ prefab . root . id ] ; console . assert ( entity . id !== undefined ) ; this . prefabInstances [ entity . id ] = { prefabIdToInstanceId : prefabIdToInstanceId } ; } } copy ( ) { const copy = new Scene ( ) ; copy . templatePath = this . templatePath ; copy . root = this . root . copy ( ) ; copy . fog = this . fog ? this . fog . copy ( ) as Fog : undefined ; copy . environment = this . environment ? this . environment . copy ( ) as Environment : undefined ; return copy ; } private overrideEntity ( instance , override ) { for ( const overridenObjectTypeName of Object . keys ( override ) ) { for ( const overridenPropertyName of Object . keys ( override [ overridenObjectTypeName ] ) ) { const overridenPropertyData = override [ overridenObjectTypeName ] [ overridenPropertyName ] ; let target = null ; if ( overridenObjectTypeName === "Entity" ) { target = instance ; } else if ( instance . hasComponentByName ( overridenObjectTypeName ) ) { target = EntityInternal . getComponentByName ( instance , overridenObjectTypeName ) as Component ; } else { target = EntityInternal . setComponentByName ( instance , overridenObjectTypeName ) ; } if ( overridenPropertyName in target ) { const typeName = SerializerUtils . getPropertyTypeName ( target [ overridenPropertyName ] ) ; SerializerUtils . deserializeProperty ( target , overridenPropertyName , typeName , overridenPropertyData ) ; } } } } }