import * as THREE from 'three' ; import { traverseAtlasItems } from './atlas' ; import { Workbench } from './workbench' ; export const SCENE_OPT_OUT_FLAG = Symbol ( 'lightmap scene bake opt out flag' ) ; type SupportedMaterial = THREE . MeshLambertMaterial | THREE . MeshPhongMaterial | THREE . MeshStandardMaterial | THREE . MeshPhysicalMaterial ; function materialIsSupported ( material ) { return ( material instanceof THREE . MeshLambertMaterial || material instanceof THREE . MeshPhongMaterial || material instanceof THREE . MeshStandardMaterial || material instanceof THREE . MeshPhysicalMaterial ) ; } const ORIGINAL_MATERIAL_KEY = Symbol ( 'lightmap baker: stashed original material' ) ; type UserDataStore < T extends symbol , V > = Record < T , V | undefined > ; export async function withLightScene ( workbench , taskCallback ) { const { aoMode , emissiveMultiplier , bounceMultiplier , lightScene , irradiance } = workbench ; const meshCleanupList = [ ] ; const suppressedCleanupList = [ ] ; traverseAtlasItems ( lightScene , ( object ) => { if ( Object . prototype . hasOwnProperty . call ( object . userData , SCENE_OPT_OUT_FLAG ) ) { object . visible = false ; suppressedCleanupList . push ( object ) ; return ; } if ( aoMode && object instanceof THREE . Light && object . visible ) { object . visible = false ; suppressedCleanupList . push ( object ) ; return ; } if ( ! ( object instanceof THREE . Mesh ) ) { return ; } const mesh = object ; const materialList = Array . isArray ( mesh . material ) ? mesh . material : [ mesh . material ] ; const stagingMaterialList = materialList . map ( ( material ) => { if ( ! material || ! materialIsSupported ( material ) ) { return material ; } if ( aoMode ) { if ( material . aoMap && material . aoMap !== irradiance ) { throw new Error ( 'do not set your own AO map manually on baked scene meshes' ) ; } } else { if ( material . lightMap && material . lightMap !== irradiance ) { throw new Error ( 'do not set your own light map manually on baked scene meshes' ) ; } } const stagingMaterial = new THREE . MeshPhongMaterial ( ) ; stagingMaterial . alphaMap = material . alphaMap ; stagingMaterial . alphaTest = material . alphaTest ; if ( ! ( material instanceof THREE . MeshLambertMaterial ) ) { stagingMaterial . displacementBias = material . displacementBias ; stagingMaterial . displacementMap = material . displacementMap ; stagingMaterial . displacementScale = material . displacementScale ; stagingMaterial . flatShading = material . flatShading ; } stagingMaterial . morphNormals = material . morphNormals ; stagingMaterial . morphTargets = material . morphTargets ; stagingMaterial . opacity = material . opacity ; stagingMaterial . premultipliedAlpha = material . premultipliedAlpha ; stagingMaterial . side = material . side ; stagingMaterial . skinning = material . skinning ; stagingMaterial . transparent = material . transparent ; stagingMaterial . visible = material . visible ; if ( ! aoMode ) { stagingMaterial . aoMap = material . aoMap ; stagingMaterial . aoMapIntensity = material . aoMapIntensity ; stagingMaterial . color = material . color ; stagingMaterial . emissive = material . emissive ; stagingMaterial . emissiveIntensity = material . emissiveIntensity * emissiveMultiplier ; stagingMaterial . emissiveMap = material . emissiveMap ; stagingMaterial . map = material . map ; stagingMaterial . shadowSide = material . shadowSide ; stagingMaterial . vertexColors = material . vertexColors ; } stagingMaterial . shininess = 0 ; stagingMaterial . toneMapped = false ; if ( aoMode ) { stagingMaterial . aoMap = irradiance ; material . aoMap = irradiance ; } else { stagingMaterial . lightMapIntensity = bounceMultiplier ; stagingMaterial . lightMap = irradiance ; material . lightMap = irradiance ; } return stagingMaterial ; } ) ; ( mesh . userData as UserDataStore < typeof ORIGINAL_MATERIAL_KEY , THREE . Material [ ] | THREE . Material > ) [ ORIGINAL_MATERIAL_KEY ] = mesh . material ; mesh . material = Array . isArray ( mesh . material ) ? stagingMaterialList : stagingMaterialList [ 0 ] ; meshCleanupList . push ( mesh ) ; } ) ; let aoSceneLight = null ; if ( aoMode ) { aoSceneLight = new THREE . AmbientLight ( '#ffffff' ) ; lightScene . add ( aoSceneLight ) ; } try { await taskCallback ( ) ; } finally { if ( aoSceneLight ) { lightScene . remove ( aoSceneLight ) ; } suppressedCleanupList . forEach ( ( object ) => { object . visible = true ; } ) ; meshCleanupList . forEach ( ( mesh ) => { const userData = mesh . userData as UserDataStore < typeof ORIGINAL_MATERIAL_KEY , THREE . Material [ ] | THREE . Material | null > ; const origMaterialValue = userData [ ORIGINAL_MATERIAL_KEY ] ; delete userData [ ORIGINAL_MATERIAL_KEY ] ; if ( ! origMaterialValue ) { console . error ( 'lightmap baker: missing original material' , mesh ) ; return ; } mesh . material = origMaterialValue ; } ) ; } }