sceneManager.ts 64 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512
  1. import { Scene, ArcRotateCamera, Engine, Light, ShadowLight, Vector3, ShadowGenerator, Tags, CubeTexture, Quaternion, SceneOptimizer, EnvironmentHelper, SceneOptimizerOptions, Color3, IEnvironmentHelperOptions, AbstractMesh, FramingBehavior, Behavior, Observable, Color4, IGlowLayerOptions, PostProcessRenderPipeline, DefaultRenderingPipeline, StandardRenderingPipeline, SSAORenderingPipeline, SSAO2RenderingPipeline, LensRenderingPipeline, RenderTargetTexture, AnimationPropertiesOverride, Animation, Scalar, StandardMaterial, PBRMaterial, Nullable, Mesh, VRExperienceHelperOptions, VRExperienceHelper } from 'babylonjs';
  2. import { ILightConfiguration, ISceneConfiguration, ISceneOptimizerConfiguration, ICameraConfiguration, ISkyboxConfiguration, ViewerConfiguration, IGroundConfiguration, IModelConfiguration, getConfigurationKey, IDefaultRenderingPipelineConfiguration, IVRConfiguration } from '../configuration';
  3. import { ViewerModel, ModelState } from '../model/viewerModel';
  4. import { extendClassWithConfig } from '../helper';
  5. import { CameraBehavior } from '../interfaces';
  6. import { ViewerLabs } from '../labs/viewerLabs';
  7. import { getCustomOptimizerByName } from '../optimizer/custom/';
  8. import { ObservablesManager } from '../managers/observablesManager';
  9. import { ConfigurationContainer } from '../configuration/configurationContainer';
  10. /**
  11. * This interface describes the structure of the variable sent with the configuration observables of the scene manager.
  12. * O - the type of object we are dealing with (Light, ArcRotateCamera, Scene, etc')
  13. * T - the configuration type
  14. */
  15. export interface IPostConfigurationCallback<OBJ, CONF> {
  16. newConfiguration: CONF;
  17. sceneManager: SceneManager;
  18. object: OBJ;
  19. model?: ViewerModel;
  20. }
  21. export class SceneManager {
  22. //Observers
  23. /**
  24. * Will notify when the scene was initialized
  25. */
  26. onSceneInitObservable: Observable<Scene>;
  27. /**
  28. * Will notify after the scene was configured. Can be used to further configure the scene
  29. */
  30. onSceneConfiguredObservable: Observable<IPostConfigurationCallback<Scene, ISceneConfiguration>>;
  31. /**
  32. * Will notify after the scene optimized was configured. Can be used to further configure the scene optimizer
  33. */
  34. onSceneOptimizerConfiguredObservable: Observable<IPostConfigurationCallback<SceneOptimizer, ISceneOptimizerConfiguration | boolean>>;
  35. /**
  36. * Will notify after the camera was configured. Can be used to further configure the camera
  37. */
  38. onCameraConfiguredObservable: Observable<IPostConfigurationCallback<ArcRotateCamera, ICameraConfiguration>>;
  39. /**
  40. * Will notify after the lights were configured. Can be used to further configure lights
  41. */
  42. onLightsConfiguredObservable: Observable<IPostConfigurationCallback<Array<Light>, { [name: string]: ILightConfiguration | boolean | number }>>;
  43. /**
  44. * Will notify after the model(s) were configured. Can be used to further configure models
  45. */
  46. onModelsConfiguredObservable: Observable<IPostConfigurationCallback<Array<ViewerModel>, IModelConfiguration>>;
  47. /**
  48. * Will notify after the envirnoment was configured. Can be used to further configure the environment
  49. */
  50. onEnvironmentConfiguredObservable: Observable<IPostConfigurationCallback<EnvironmentHelper, { skybox?: ISkyboxConfiguration | boolean, ground?: IGroundConfiguration | boolean }>>;
  51. /**
  52. * Will notify after the model(s) were configured. Can be used to further configure models
  53. */
  54. onVRConfiguredObservable: Observable<IPostConfigurationCallback<VRExperienceHelper, IVRConfiguration>>;
  55. /**
  56. * The Babylon Scene of this viewer
  57. */
  58. public scene: Scene;
  59. /**
  60. * The camera used in this viewer
  61. */
  62. public camera: ArcRotateCamera;
  63. /**
  64. * Babylon's scene optimizer
  65. */
  66. public sceneOptimizer: SceneOptimizer;
  67. /**
  68. * Models displayed in this viewer.
  69. */
  70. public models: Array<ViewerModel>;
  71. /**
  72. * Babylon's environment helper of this viewer
  73. */
  74. public environmentHelper?: EnvironmentHelper;
  75. private _animationBlendingEnabled: boolean = true;
  76. //The following are configuration objects, default values.
  77. protected _defaultHighpTextureType: number;
  78. protected _shadowGeneratorBias: number;
  79. protected _defaultPipelineTextureType: number;
  80. /**
  81. * The maximum number of shadows supported by the curent viewer
  82. */
  83. protected _maxShadows: number;
  84. /**
  85. * is HDR supported?
  86. */
  87. private _hdrSupport: boolean;
  88. private readonly _white = Color3.White();
  89. private _forceShadowUpdate: boolean = false;
  90. /**
  91. * The labs variable consists of objects that will have their API change.
  92. * Please be careful when using labs in production.
  93. */
  94. public labs: ViewerLabs;
  95. private _defaultRenderingPipeline: Nullable<DefaultRenderingPipeline>;
  96. public get defaultRenderingPipeline() {
  97. return this._defaultRenderingPipeline;
  98. }
  99. protected _vrHelper?: VRExperienceHelper;
  100. public get vrHelper() {
  101. return this._vrHelper;
  102. }
  103. constructor(private _engine: Engine, private _configurationContainer: ConfigurationContainer, private _observablesManager?: ObservablesManager) {
  104. this.models = [];
  105. this.onCameraConfiguredObservable = new Observable();
  106. this.onLightsConfiguredObservable = new Observable();
  107. this.onModelsConfiguredObservable = new Observable();
  108. this.onSceneConfiguredObservable = new Observable();
  109. this.onSceneInitObservable = new Observable();
  110. this.onSceneOptimizerConfiguredObservable = new Observable();
  111. this.onEnvironmentConfiguredObservable = new Observable();
  112. this.onVRConfiguredObservable = new Observable();
  113. //this._viewer.onEngineInitObservable.add(() => {
  114. this._handleHardwareLimitations();
  115. //});
  116. this.onSceneInitObservable.add((scene) => {
  117. this.scene.animationPropertiesOverride = this.scene.animationPropertiesOverride || new AnimationPropertiesOverride();
  118. this.labs = new ViewerLabs(scene);
  119. let updateShadows = () => {
  120. for (let light of this.scene.lights) {
  121. let generator = light.getShadowGenerator();
  122. if (generator) {
  123. // Processing shadows if animates
  124. let shadowMap = generator.getShadowMap();
  125. if (shadowMap) {
  126. shadowMap.refreshRate = RenderTargetTexture.REFRESHRATE_RENDER_ONCE;
  127. }
  128. }
  129. }
  130. }
  131. scene.registerBeforeRender(() => {
  132. if (this._forceShadowUpdate || (scene.animatables && scene.animatables.length > 0)) {
  133. // make sure all models are loaded
  134. updateShadows();
  135. this._forceShadowUpdate = false;
  136. } else if (!(this.models.every((model) => {
  137. if (!model.shadowsRenderedAfterLoad) {
  138. model.shadowsRenderedAfterLoad = true;
  139. return false;
  140. }
  141. return model.state === ModelState.COMPLETE && !model.currentAnimation
  142. }))) {
  143. updateShadows();
  144. }
  145. });
  146. return this._observablesManager && this._observablesManager.onSceneInitObservable.notifyObserversWithPromise(this.scene);
  147. });
  148. if (this._observablesManager) {
  149. this._observablesManager.onModelLoadedObservable.add((model) => {
  150. for (let light of this.scene.lights) {
  151. let generator = light.getShadowGenerator();
  152. if (generator) {
  153. // Processing shadows if animates
  154. let shadowMap = generator.getShadowMap();
  155. if (shadowMap) {
  156. shadowMap.refreshRate = RenderTargetTexture.REFRESHRATE_RENDER_ONCE;
  157. }
  158. }
  159. }
  160. this._focusOnModel(model);
  161. });
  162. this._observablesManager.onModelAddedObservable.add(model => {
  163. this.models.push(model);
  164. });
  165. this._observablesManager.onModelRemovedObservable.add(model => {
  166. this.models.splice(this.models.indexOf(model), 1);
  167. })
  168. }
  169. }
  170. /**
  171. * Returns a boolean representing HDR support
  172. */
  173. public get isHdrSupported() {
  174. return this._hdrSupport;
  175. }
  176. /**
  177. * Return the main color defined in the configuration.
  178. */
  179. public get mainColor(): Color3 {
  180. return this._configurationContainer.mainColor;
  181. }
  182. public get reflectionColor(): Color3 {
  183. return this._configurationContainer.reflectionColor;
  184. }
  185. public get animationBlendingEnabled() {
  186. return this.scene && this.scene.animationPropertiesOverride!.enableBlending;
  187. }
  188. public set animationBlendingEnabled(value: boolean) {
  189. this.scene.animationPropertiesOverride!.enableBlending = value;
  190. }
  191. public get observablesManager() {
  192. return this._observablesManager;
  193. }
  194. private _processShadows: boolean = true;
  195. /**
  196. * The flag defining whether shadows are rendered constantly or once.
  197. */
  198. public get processShadows() {
  199. return this._processShadows;
  200. }
  201. /**
  202. * Should shadows be rendered every frame, or only once and stop.
  203. * This can be used to optimize a scene.
  204. *
  205. * Not that the shadows will NOT disapear but will remain in place.
  206. * @param process if true shadows will be updated once every frame. if false they will stop being updated.
  207. */
  208. public set processShadows(process: boolean) {
  209. let refreshType = process ? RenderTargetTexture.REFRESHRATE_RENDER_ONEVERYFRAME : RenderTargetTexture.REFRESHRATE_RENDER_ONCE;
  210. for (let light of this.scene.lights) {
  211. let generator = light.getShadowGenerator();
  212. if (generator) {
  213. let shadowMap = generator.getShadowMap();
  214. if (shadowMap) {
  215. shadowMap.refreshRate = refreshType;
  216. }
  217. }
  218. }
  219. this._processShadows = process;
  220. }
  221. private _groundEnabled: boolean = true;
  222. public get groundEnabled() {
  223. return this._groundEnabled;
  224. }
  225. public set groundEnabled(newValue: boolean) {
  226. if (newValue === this._groundEnabled) return;
  227. this._groundEnabled = newValue;
  228. if (this.environmentHelper && this.environmentHelper.ground) {
  229. this.environmentHelper.ground.setEnabled(this._groundEnabled);
  230. }
  231. }
  232. private _groundMirrorEnabled = true;
  233. /**
  234. * gets wether the reflection is disabled.
  235. */
  236. public get groundMirrorEnabled(): boolean {
  237. return this._groundMirrorEnabled;
  238. }
  239. /**
  240. * sets wether the reflection is disabled.
  241. */
  242. public set groundMirrorEnabled(value: boolean) {
  243. if (this._groundMirrorEnabled === value) {
  244. return;
  245. }
  246. this._groundMirrorEnabled = value;
  247. if (this.environmentHelper && this.environmentHelper.groundMaterial && this.environmentHelper.groundMirror) {
  248. if (!value) {
  249. this.environmentHelper.groundMaterial.reflectionTexture = null;
  250. } else {
  251. this.environmentHelper.groundMaterial.reflectionTexture = this.environmentHelper.groundMirror;
  252. }
  253. }
  254. }
  255. private _defaultRenderingPipelineEnabled: boolean = false;
  256. public get defaultRenderingPipelineEnabled() {
  257. return this._defaultRenderingPipelineEnabled;
  258. }
  259. public set defaultRenderingPipelineEnabled(value: boolean) {
  260. if (value === this._defaultRenderingPipelineEnabled) {
  261. return;
  262. }
  263. this._defaultRenderingPipelineEnabled = value;
  264. this._rebuildPostprocesses();
  265. if (this._defaultRenderingPipeline) {
  266. this._defaultRenderingPipelineShouldBuild = false;
  267. this._defaultRenderingPipeline.prepare();
  268. this.scene.imageProcessingConfiguration.applyByPostProcess = true;
  269. }
  270. }
  271. /**
  272. * Sets the engine flags to unlock all babylon features.
  273. * Can also be configured using the scene.flags configuration object
  274. */
  275. public unlockBabylonFeatures() {
  276. this.scene.shadowsEnabled = true;
  277. this.scene.particlesEnabled = true;
  278. this.scene.postProcessesEnabled = true;
  279. this.scene.collisionsEnabled = true;
  280. this.scene.lightsEnabled = true;
  281. this.scene.texturesEnabled = true;
  282. this.scene.lensFlaresEnabled = true;
  283. this.scene.proceduralTexturesEnabled = true;
  284. this.scene.renderTargetsEnabled = true;
  285. this.scene.spritesEnabled = true;
  286. this.scene.skeletonsEnabled = true;
  287. this.scene.audioEnabled = true;
  288. }
  289. /**
  290. * initialize the scene. Calling this function again will dispose the old scene, if exists.
  291. */
  292. public initScene(sceneConfiguration: ISceneConfiguration = {}, optimizerConfiguration?: boolean | ISceneOptimizerConfiguration): Promise<Scene> {
  293. // if the scen exists, dispose it.
  294. if (this.scene) {
  295. this.scene.dispose();
  296. }
  297. // create a new scene
  298. this.scene = new Scene(this._engine);
  299. this._configurationContainer.scene = this.scene;
  300. // set a default PBR material
  301. if (!sceneConfiguration.defaultMaterial) {
  302. var defaultMaterial = new BABYLON.PBRMaterial('defaultMaterial', this.scene);
  303. defaultMaterial.reflectivityColor = new BABYLON.Color3(0.1, 0.1, 0.1);
  304. defaultMaterial.microSurface = 0.6;
  305. if (this.scene.defaultMaterial) {
  306. this.scene.defaultMaterial.dispose();
  307. }
  308. this.scene.defaultMaterial = defaultMaterial;
  309. }
  310. this.scene.animationPropertiesOverride = new AnimationPropertiesOverride();
  311. Animation.AllowMatricesInterpolation = true;
  312. /*if (sceneConfiguration.glow) {
  313. let options: Partial<IGlowLayerOptions> = {
  314. mainTextureFixedSize: 512
  315. };
  316. if (typeof sceneConfiguration.glow === 'object') {
  317. options = sceneConfiguration.glow
  318. }
  319. var gl = new BABYLON.GlowLayer("glow", this.scene, options);
  320. }*/
  321. return this.onSceneInitObservable.notifyObserversWithPromise(this.scene);
  322. }
  323. public clearScene(clearModels: boolean = true, clearLights: boolean = false) {
  324. if (clearModels) {
  325. this.models.forEach(m => m.dispose());
  326. this.models.length = 0;
  327. }
  328. if (clearLights) {
  329. this.scene.lights.forEach(l => l.dispose());
  330. }
  331. }
  332. private _globalConfiguration: ViewerConfiguration = {};
  333. /**
  334. * This will update the scene's configuration, including camera, lights, environment.
  335. * @param newConfiguration the delta that should be configured. This includes only the changes
  336. * @param globalConfiguration The global configuration object, after the new configuration was merged into it
  337. */
  338. public updateConfiguration(newConfiguration: Partial<ViewerConfiguration>) {
  339. if (this._configurationContainer) {
  340. this._globalConfiguration = this._configurationContainer.configuration;
  341. } else {
  342. this._globalConfiguration = newConfiguration;
  343. }
  344. if (newConfiguration.lab) {
  345. if (newConfiguration.lab.assetsRootURL) {
  346. this.labs.assetsRootURL = newConfiguration.lab.assetsRootURL;
  347. }
  348. }
  349. // update scene configuration
  350. if (newConfiguration.scene) {
  351. this._configureScene(newConfiguration.scene);
  352. }
  353. // optimizer
  354. if (newConfiguration.optimizer !== undefined) {
  355. this._configureOptimizer(newConfiguration.optimizer);
  356. }
  357. // configure model
  358. /*if (newConfiguration.model && typeof newConfiguration.model === 'object') {
  359. this._configureModel(newConfiguration.model);
  360. }*/
  361. // lights
  362. this._configureLights(newConfiguration.lights);
  363. // environment
  364. if (newConfiguration.skybox !== undefined || newConfiguration.ground !== undefined) {
  365. this._configureEnvironment(newConfiguration.skybox, newConfiguration.ground);
  366. }
  367. // camera
  368. this._configureCamera(newConfiguration.camera);
  369. if (newConfiguration.vr) {
  370. this._configureVR(newConfiguration.vr);
  371. }
  372. if (newConfiguration.lab) {
  373. if (newConfiguration.lab.environmentMap) {
  374. let rot = newConfiguration.lab.environmentMap.rotationY;
  375. this.labs.loadEnvironment(newConfiguration.lab.environmentMap.texture, () => {
  376. this.labs.applyEnvironmentMapConfiguration(rot);
  377. });
  378. if (!newConfiguration.lab.environmentMap.texture && newConfiguration.lab.environmentMap.rotationY) {
  379. this.labs.applyEnvironmentMapConfiguration(newConfiguration.lab.environmentMap.rotationY);
  380. }
  381. }
  382. // rendering piplines
  383. if (newConfiguration.lab.defaultRenderingPipelines) {
  384. let pipelineConfig = newConfiguration.lab.defaultRenderingPipelines;
  385. if (typeof pipelineConfig === 'boolean') {
  386. this.defaultRenderingPipelineEnabled = pipelineConfig;
  387. } else {
  388. this.defaultRenderingPipelineEnabled = true;
  389. }
  390. }
  391. if (this.environmentHelper && newConfiguration.lab.environmentMainColor) {
  392. let mainColor = new Color3().copyFrom(newConfiguration.lab.environmentMainColor as Color3);
  393. this.environmentHelper.setMainColor(mainColor);
  394. }
  395. if (newConfiguration.lab.globalLightRotation !== undefined) {
  396. // rotate all lights that are shadow lights
  397. this.scene.lights.filter(light => light instanceof ShadowLight).forEach(light => {
  398. // casting and '!' are safe, due to the constraints tested before
  399. this.labs.rotateShadowLight(<ShadowLight>light, newConfiguration.lab!.globalLightRotation!);
  400. });
  401. this._forceShadowUpdate = true;
  402. }
  403. }
  404. if (this._defaultRenderingPipeline && this._defaultRenderingPipeline.imageProcessing) {
  405. this._defaultRenderingPipeline.imageProcessing.fromLinearSpace = true;
  406. }
  407. if (this._defaultRenderingPipelineShouldBuild && this._defaultRenderingPipeline) {
  408. this._defaultRenderingPipelineShouldBuild = false;
  409. this._defaultRenderingPipeline.prepare();
  410. }
  411. }
  412. private _defaultRenderingPipelineShouldBuild: boolean = true;
  413. private _rebuildPostprocesses(configuration?: IDefaultRenderingPipelineConfiguration): void {
  414. if (!this._defaultRenderingPipelineEnabled || !getConfigurationKey("scene.imageProcessingConfiguration.isEnabled", this._globalConfiguration)) {
  415. if (this._defaultRenderingPipeline) {
  416. this._defaultRenderingPipeline.dispose();
  417. this._defaultRenderingPipeline = null;
  418. this.scene.autoClearDepthAndStencil = true;
  419. this.scene.autoClear = true;
  420. this.scene.imageProcessingConfiguration.applyByPostProcess = false;
  421. }
  422. return;
  423. }
  424. let pipelineConfig = configuration || (this._globalConfiguration.lab && this._globalConfiguration.lab.defaultRenderingPipelines);
  425. if (pipelineConfig) {
  426. if (!this._defaultRenderingPipeline) {
  427. // Create pipeline in manual mode to avoid triggering multiple shader compilations
  428. this._defaultRenderingPipeline = new DefaultRenderingPipeline("default rendering pipeline", this._hdrSupport, this.scene, [this.camera], false);
  429. }
  430. this.scene.autoClear = false;
  431. this.scene.autoClearDepthAndStencil = false;
  432. this._defaultRenderingPipelineShouldBuild = true;
  433. let bloomEnabled = this._bloomEnabled;
  434. if (typeof pipelineConfig !== 'boolean') {
  435. extendClassWithConfig(this._defaultRenderingPipeline, pipelineConfig);
  436. this._bloomEnabled = !!pipelineConfig.bloomEnabled;
  437. this._fxaaEnabled = !!pipelineConfig.fxaaEnabled;
  438. bloomEnabled = this._bloomEnabled && pipelineConfig.bloomWeight !== undefined && pipelineConfig.bloomWeight > 0;
  439. this._defaultRenderingPipeline.bloomWeight = (pipelineConfig.bloomWeight !== undefined && pipelineConfig.bloomWeight) || (this._defaultRenderingPipeline.bloomWeight);
  440. }
  441. this._defaultRenderingPipeline.bloomEnabled = bloomEnabled;
  442. this._defaultRenderingPipeline.fxaaEnabled = this.fxaaEnabled;
  443. }
  444. }
  445. // default from rendering pipeline
  446. private _bloomEnabled: boolean = false;
  447. public get bloomEnabled() {
  448. return this._bloomEnabled;
  449. }
  450. public set bloomEnabled(value: boolean) {
  451. if (this._bloomEnabled === value) {
  452. return;
  453. }
  454. this._bloomEnabled = value;
  455. this._rebuildPostprocesses();
  456. if (this._defaultRenderingPipeline) {
  457. this._defaultRenderingPipelineShouldBuild = false;
  458. this._defaultRenderingPipeline.prepare();
  459. this.scene.imageProcessingConfiguration.applyByPostProcess = true;
  460. }
  461. }
  462. // default from rendering pipeline
  463. private _fxaaEnabled: boolean = false;
  464. public get fxaaEnabled() {
  465. return this._fxaaEnabled;
  466. }
  467. public set fxaaEnabled(value: boolean) {
  468. if (this._fxaaEnabled === value) {
  469. return;
  470. }
  471. this._fxaaEnabled = value;
  472. this._rebuildPostprocesses();
  473. if (this._defaultRenderingPipeline) {
  474. this._defaultRenderingPipelineShouldBuild = false;
  475. this._defaultRenderingPipeline.prepare();
  476. this.scene.imageProcessingConfiguration.applyByPostProcess = true;
  477. }
  478. }
  479. /**
  480. * internally configure the scene using the provided configuration.
  481. * The scene will not be recreated, but just updated.
  482. * @param sceneConfig the (new) scene configuration
  483. */
  484. protected _configureScene(sceneConfig: ISceneConfiguration) {
  485. // sanity check!
  486. if (!this.scene) {
  487. return;
  488. }
  489. let cc = sceneConfig.clearColor;
  490. let oldcc = this.scene.clearColor;
  491. if (cc) {
  492. if (cc.r !== undefined) {
  493. oldcc.r = cc.r;
  494. }
  495. if (cc.g !== undefined) {
  496. oldcc.g = cc.g
  497. }
  498. if (cc.b !== undefined) {
  499. oldcc.b = cc.b
  500. }
  501. if (cc.a !== undefined) {
  502. oldcc.a = cc.a
  503. }
  504. }
  505. // image processing configuration - optional.
  506. if (sceneConfig.imageProcessingConfiguration) {
  507. extendClassWithConfig(this.scene.imageProcessingConfiguration, sceneConfig.imageProcessingConfiguration);
  508. }
  509. //animation properties override
  510. if (sceneConfig.animationPropertiesOverride) {
  511. extendClassWithConfig(this.scene.animationPropertiesOverride, sceneConfig.animationPropertiesOverride);
  512. }
  513. if (sceneConfig.environmentTexture) {
  514. if (!(this.scene.environmentTexture && (<CubeTexture>this.scene.environmentTexture).url === sceneConfig.environmentTexture)) {
  515. if (this.scene.environmentTexture && this.scene.environmentTexture.dispose) {
  516. this.scene.environmentTexture.dispose();
  517. }
  518. const environmentTexture = CubeTexture.CreateFromPrefilteredData(sceneConfig.environmentTexture, this.scene);
  519. this.scene.environmentTexture = environmentTexture;
  520. }
  521. }
  522. if (sceneConfig.debug === true) {
  523. this.scene.debugLayer.show();
  524. } else if (sceneConfig.debug === false) {
  525. if (this.scene.debugLayer.isVisible()) {
  526. this.scene.debugLayer.hide();
  527. }
  528. }
  529. if (sceneConfig.disableHdr) {
  530. this._handleHardwareLimitations(false);
  531. } else {
  532. this._handleHardwareLimitations(true);
  533. }
  534. if (sceneConfig.renderInBackground !== undefined) {
  535. this._engine.renderEvenInBackground = !!sceneConfig.renderInBackground;
  536. }
  537. let canvas = this._engine.getRenderingCanvas();
  538. if (canvas) {
  539. if (this.camera && sceneConfig.disableCameraControl) {
  540. this.camera.detachControl(canvas);
  541. } else if (this.camera && sceneConfig.disableCameraControl === false) {
  542. this.camera.attachControl(canvas);
  543. }
  544. }
  545. // process mainColor changes:
  546. if (sceneConfig.mainColor) {
  547. this._configurationContainer.mainColor = this.mainColor || Color3.White();
  548. let mc = sceneConfig.mainColor;
  549. if (mc.r !== undefined) {
  550. this.mainColor.r = mc.r;
  551. }
  552. if (mc.g !== undefined) {
  553. this.mainColor.g = mc.g
  554. }
  555. if (mc.b !== undefined) {
  556. this.mainColor.b = mc.b
  557. }
  558. this.reflectionColor.copyFrom(this.mainColor);
  559. let environmentTint = getConfigurationKey("lab.environmentMap.tintLevel", this._globalConfiguration) || 0;
  560. // reflection color
  561. this.reflectionColor.toLinearSpaceToRef(this.reflectionColor);
  562. this.reflectionColor.scaleToRef(1 / this.scene.imageProcessingConfiguration.exposure, this.reflectionColor);
  563. let tmpColor3 = Color3.Lerp(this._white, this.reflectionColor, environmentTint);
  564. this.reflectionColor.copyFrom(tmpColor3);
  565. //update the environment, if exists
  566. if (this.environmentHelper) {
  567. if (this.environmentHelper.groundMaterial) {
  568. this.environmentHelper.groundMaterial._perceptualColor = this.mainColor;
  569. }
  570. if (this.environmentHelper.skyboxMaterial) {
  571. this.environmentHelper.skyboxMaterial._perceptualColor = this.mainColor;
  572. }
  573. }
  574. }
  575. if (sceneConfig.defaultMaterial) {
  576. let conf = sceneConfig.defaultMaterial;
  577. if ((conf.materialType === 'standard' && this.scene.defaultMaterial.getClassName() !== 'StandardMaterial') ||
  578. (conf.materialType === 'pbr' && this.scene.defaultMaterial.getClassName() !== 'PBRMaterial')) {
  579. this.scene.defaultMaterial.dispose();
  580. if (conf.materialType === 'standard') {
  581. this.scene.defaultMaterial = new StandardMaterial("defaultMaterial", this.scene);
  582. } else {
  583. this.scene.defaultMaterial = new PBRMaterial("defaultMaterial", this.scene);
  584. }
  585. }
  586. extendClassWithConfig(this.scene.defaultMaterial, conf);
  587. }
  588. if (sceneConfig.flags) {
  589. extendClassWithConfig(this.scene, sceneConfig.flags);
  590. }
  591. this.onSceneConfiguredObservable.notifyObservers({
  592. sceneManager: this,
  593. object: this.scene,
  594. newConfiguration: sceneConfig
  595. });
  596. }
  597. /**
  598. * Configure the scene optimizer.
  599. * The existing scene optimizer will be disposed and a new one will be created.
  600. * @param optimizerConfig the (new) optimizer configuration
  601. */
  602. protected _configureOptimizer(optimizerConfig: ISceneOptimizerConfiguration | boolean) {
  603. if (typeof optimizerConfig === 'boolean') {
  604. if (this.sceneOptimizer) {
  605. this.sceneOptimizer.stop();
  606. this.sceneOptimizer.dispose();
  607. delete this.sceneOptimizer;
  608. }
  609. if (optimizerConfig) {
  610. this.sceneOptimizer = new SceneOptimizer(this.scene);
  611. this.sceneOptimizer.start();
  612. }
  613. } else {
  614. let optimizerOptions: SceneOptimizerOptions = new SceneOptimizerOptions(optimizerConfig.targetFrameRate, optimizerConfig.trackerDuration);
  615. // check for degradation
  616. if (optimizerConfig.degradation) {
  617. switch (optimizerConfig.degradation) {
  618. case "low":
  619. optimizerOptions = SceneOptimizerOptions.LowDegradationAllowed(optimizerConfig.targetFrameRate);
  620. break;
  621. case "moderate":
  622. optimizerOptions = SceneOptimizerOptions.ModerateDegradationAllowed(optimizerConfig.targetFrameRate);
  623. break;
  624. case "hight":
  625. optimizerOptions = SceneOptimizerOptions.HighDegradationAllowed(optimizerConfig.targetFrameRate);
  626. break;
  627. }
  628. }
  629. if (this.sceneOptimizer) {
  630. this.sceneOptimizer.stop();
  631. this.sceneOptimizer.dispose()
  632. }
  633. if (optimizerConfig.custom) {
  634. let customOptimizer = getCustomOptimizerByName(optimizerConfig.custom, optimizerConfig.improvementMode);
  635. if (customOptimizer) {
  636. optimizerOptions.addCustomOptimization(() => {
  637. return customOptimizer(this);
  638. }, () => {
  639. return `Babylon Viewer ${optimizerConfig.custom} custom optimization`;
  640. });
  641. }
  642. }
  643. this.sceneOptimizer = new SceneOptimizer(this.scene, optimizerOptions, optimizerConfig.autoGeneratePriorities, optimizerConfig.improvementMode);
  644. this.sceneOptimizer.start();
  645. }
  646. this.onSceneOptimizerConfiguredObservable.notifyObservers({
  647. sceneManager: this,
  648. object: this.sceneOptimizer,
  649. newConfiguration: optimizerConfig
  650. });
  651. }
  652. /**
  653. * configure all models using the configuration.
  654. * @param modelConfiguration the configuration to use to reconfigure the models
  655. */
  656. /*protected _configureModel(modelConfiguration: Partial<IModelConfiguration>) {
  657. this.models.forEach(model => {
  658. model.updateConfiguration(modelConfiguration);
  659. });
  660. this.onModelsConfiguredObservable.notifyObservers({
  661. sceneManager: this,
  662. object: this.models,
  663. newConfiguration: modelConfiguration
  664. });
  665. }*/
  666. protected _configureVR(vrConfig: IVRConfiguration) {
  667. if (vrConfig.disabled) {
  668. if (this._vrHelper) {
  669. if (this._vrHelper.isInVRMode) {
  670. this._vrHelper.exitVR();
  671. }
  672. this._vrHelper.dispose();
  673. this._vrHelper = undefined;
  674. }
  675. return;
  676. }
  677. let vrOptions: VRExperienceHelperOptions = vrConfig.vrOptions || {
  678. useCustomVRButton: true,
  679. createDeviceOrientationCamera: false,
  680. trackPosition: true
  681. }
  682. this._vrHelper = this.scene.createDefaultVRExperience(vrOptions);
  683. if (!vrConfig.disableInteractions) {
  684. this._vrHelper.enableInteractions();
  685. }
  686. if (!vrConfig.disableTeleportation) {
  687. let floorMeshName = vrConfig.overrideFloorMeshName || "BackgroundPlane";
  688. this._vrHelper.enableTeleportation({
  689. floorMeshName
  690. });
  691. }
  692. let rotationOffset: Quaternion | null;
  693. this._vrHelper.onControllerMeshLoadedObservable.add((controller) => {
  694. controller.onTriggerStateChangedObservable.add((data) => {
  695. if (controller.mesh && controller.mesh.rotationQuaternion) {
  696. if (data.pressed) {
  697. if (!rotationOffset) {
  698. rotationOffset = controller.mesh.rotationQuaternion.conjugate();
  699. }
  700. } else {
  701. rotationOffset = null;
  702. }
  703. if (this.models[0]) {
  704. if (rotationOffset) {
  705. this.models[0].rootMesh.rotationQuaternion = controller.mesh.rotationQuaternion.multiply(rotationOffset);
  706. } else {
  707. this.models[0].rootMesh.rotationQuaternion = null;
  708. }
  709. }
  710. }
  711. });
  712. this.scene.registerBeforeRender(() => {
  713. if (this.models[0]) {
  714. if (rotationOffset) {
  715. this.models[0].rootMesh.rotationQuaternion = controller.mesh && controller.mesh.rotationQuaternion && controller.mesh.rotationQuaternion.multiply(rotationOffset);
  716. } else {
  717. this.models[0].rootMesh.rotationQuaternion = null;
  718. }
  719. }
  720. })
  721. })
  722. this.onVRConfiguredObservable.notifyObservers({
  723. sceneManager: this,
  724. object: this._vrHelper,
  725. newConfiguration: vrConfig
  726. });
  727. }
  728. /**
  729. * (Re) configure the camera. The camera will only be created once and from this point will only be reconfigured.
  730. * @param cameraConfig the new camera configuration
  731. * @param model optionally use the model to configure the camera.
  732. */
  733. protected _configureCamera(cameraConfig: ICameraConfiguration = {}) {
  734. if (!this.scene.activeCamera) {
  735. let attachControl = true;
  736. if (this._globalConfiguration.scene && this._globalConfiguration.scene.disableCameraControl) {
  737. attachControl = false;
  738. }
  739. this.scene.createDefaultCamera(true, true, attachControl);
  740. this.camera = <ArcRotateCamera>this.scene.activeCamera!;
  741. this.camera.setTarget(Vector3.Zero());
  742. }
  743. if (!this.camera) {
  744. this.camera = <ArcRotateCamera>this.scene.activeCamera!;
  745. }
  746. if (cameraConfig.position) {
  747. let newPosition = this.camera.position.clone();
  748. extendClassWithConfig(newPosition, cameraConfig.position);
  749. this.camera.setPosition(newPosition);
  750. }
  751. if (cameraConfig.target) {
  752. let newTarget = this.camera.target.clone();
  753. extendClassWithConfig(newTarget, cameraConfig.target);
  754. this.camera.setTarget(newTarget);
  755. } /*else if (this.models.length && !cameraConfig.disableAutoFocus) {
  756. this._focusOnModel(this.models[0]);
  757. }*/
  758. if (cameraConfig.rotation) {
  759. this.camera.rotationQuaternion = new Quaternion(cameraConfig.rotation.x || 0, cameraConfig.rotation.y || 0, cameraConfig.rotation.z || 0, cameraConfig.rotation.w || 0)
  760. }
  761. if (cameraConfig.behaviors) {
  762. for (let name in cameraConfig.behaviors) {
  763. if (cameraConfig.behaviors[name] !== undefined) {
  764. this._setCameraBehavior(name, cameraConfig.behaviors[name]);
  765. }
  766. }
  767. };
  768. const sceneExtends = this.scene.getWorldExtends((mesh) => {
  769. return !this.environmentHelper || (mesh !== this.environmentHelper.ground && mesh !== this.environmentHelper.rootMesh && mesh !== this.environmentHelper.skybox);
  770. });
  771. const sceneDiagonal = sceneExtends.max.subtract(sceneExtends.min);
  772. const sceneDiagonalLength = sceneDiagonal.length();
  773. if (isFinite(sceneDiagonalLength)) {
  774. this.camera.upperRadiusLimit = sceneDiagonalLength * 4;
  775. }
  776. // sanity check!
  777. if (this.scene.imageProcessingConfiguration) {
  778. this.scene.imageProcessingConfiguration.colorCurvesEnabled = true;
  779. this.scene.imageProcessingConfiguration.vignetteEnabled = true;
  780. this.scene.imageProcessingConfiguration.toneMappingEnabled = !!getConfigurationKey("camera.toneMappingEnabled", this._globalConfiguration);
  781. }
  782. extendClassWithConfig(this.camera, cameraConfig);
  783. this.onCameraConfiguredObservable.notifyObservers({
  784. sceneManager: this,
  785. object: this.camera,
  786. newConfiguration: cameraConfig
  787. });
  788. }
  789. private _focusOnModel = (model: ViewerModel) => {
  790. const boundingInfo = model.rootMesh.getHierarchyBoundingVectors(true);
  791. const sizeVec = boundingInfo.max.subtract(boundingInfo.min);
  792. const halfSizeVec = sizeVec.scale(0.5);
  793. const center = boundingInfo.min.add(halfSizeVec);
  794. this.camera.setTarget(center);
  795. this.camera.alpha = (this._globalConfiguration.camera && this._globalConfiguration.camera.alpha) || this.camera.alpha;
  796. this.camera.beta = (this._globalConfiguration.camera && this._globalConfiguration.camera.beta) || this.camera.beta;
  797. this.camera.radius = (this._globalConfiguration.camera && this._globalConfiguration.camera.radius) || this.camera.radius;
  798. const sceneDiagonalLenght = sizeVec.length();
  799. if (isFinite(sceneDiagonalLenght))
  800. this.camera.upperRadiusLimit = sceneDiagonalLenght * 4;
  801. if (this._configurationContainer.configuration)
  802. this._configureEnvironment(this._configurationContainer.configuration.skybox, this._configurationContainer.configuration.ground);
  803. /*this.scene.lights.filter(light => light instanceof ShadowLight).forEach(light => {
  804. // casting ais safe, due to the constraints tested before
  805. (<ShadowLight>light).setDirectionToTarget(center);
  806. });*/
  807. }
  808. protected _configureEnvironment(skyboxConifguration?: ISkyboxConfiguration | boolean, groundConfiguration?: IGroundConfiguration | boolean) {
  809. if (!skyboxConifguration && !groundConfiguration) {
  810. if (this.environmentHelper) {
  811. this.environmentHelper.dispose();
  812. this.environmentHelper = undefined;
  813. };
  814. } else {
  815. const options: Partial<IEnvironmentHelperOptions> = {
  816. createGround: !!groundConfiguration && this._groundEnabled,
  817. createSkybox: !!skyboxConifguration,
  818. setupImageProcessing: false, // will be done at the scene level!,
  819. };
  820. // will that cause problems with model ground configuration?
  821. /*if (model) {
  822. const boundingInfo = model.rootMesh.getHierarchyBoundingVectors(true);
  823. const sizeVec = boundingInfo.max.subtract(boundingInfo.min);
  824. const halfSizeVec = sizeVec.scale(0.5);
  825. const center = boundingInfo.min.add(halfSizeVec);
  826. options.groundYBias = -center.y;
  827. }*/
  828. if (groundConfiguration) {
  829. let groundConfig = (typeof groundConfiguration === 'boolean') ? {} : groundConfiguration;
  830. let groundSize = groundConfig.size || (typeof skyboxConifguration === 'object' && skyboxConifguration.scale);
  831. if (groundSize) {
  832. options.groundSize = groundSize;
  833. }
  834. options.enableGroundShadow = groundConfig === true || groundConfig.receiveShadows;
  835. if (groundConfig.shadowLevel !== undefined) {
  836. options.groundShadowLevel = groundConfig.shadowLevel;
  837. }
  838. options.enableGroundMirror = !!groundConfig.mirror && this.groundMirrorEnabled;
  839. if (groundConfig.texture) {
  840. options.groundTexture = this.labs.getAssetUrl(groundConfig.texture);
  841. }
  842. if (groundConfig.color) {
  843. options.groundColor = new Color3(groundConfig.color.r, groundConfig.color.g, groundConfig.color.b)
  844. }
  845. if (groundConfig.opacity !== undefined) {
  846. options.groundOpacity = groundConfig.opacity;
  847. }
  848. if (groundConfig.mirror) {
  849. options.enableGroundMirror = true;
  850. // to prevent undefines
  851. if (typeof groundConfig.mirror === "object") {
  852. if (groundConfig.mirror.amount !== undefined)
  853. options.groundMirrorAmount = groundConfig.mirror.amount;
  854. if (groundConfig.mirror.sizeRatio !== undefined)
  855. options.groundMirrorSizeRatio = groundConfig.mirror.sizeRatio;
  856. if (groundConfig.mirror.blurKernel !== undefined)
  857. options.groundMirrorBlurKernel = groundConfig.mirror.blurKernel;
  858. if (groundConfig.mirror.fresnelWeight !== undefined)
  859. options.groundMirrorFresnelWeight = groundConfig.mirror.fresnelWeight;
  860. if (groundConfig.mirror.fallOffDistance !== undefined)
  861. options.groundMirrorFallOffDistance = groundConfig.mirror.fallOffDistance;
  862. if (this._defaultPipelineTextureType !== undefined)
  863. options.groundMirrorTextureType = this._defaultPipelineTextureType;
  864. }
  865. }
  866. }
  867. let postInitSkyboxMaterial = false;
  868. if (skyboxConifguration) {
  869. let conf = skyboxConifguration === true ? {} : skyboxConifguration;
  870. if (conf.material && conf.material.imageProcessingConfiguration) {
  871. options.setupImageProcessing = false; // will be configured later manually.
  872. }
  873. let skyboxSize = conf.scale;
  874. if (skyboxSize) {
  875. options.skyboxSize = skyboxSize;
  876. }
  877. options.sizeAuto = !options.skyboxSize;
  878. if (conf.color) {
  879. options.skyboxColor = new Color3(conf.color.r, conf.color.g, conf.color.b)
  880. }
  881. if (conf.cubeTexture && conf.cubeTexture.url) {
  882. if (typeof conf.cubeTexture.url === "string") {
  883. options.skyboxTexture = this.labs.getAssetUrl(conf.cubeTexture.url);
  884. } else {
  885. // init later!
  886. postInitSkyboxMaterial = true;
  887. }
  888. }
  889. if (conf.material) {
  890. postInitSkyboxMaterial = true;
  891. }
  892. }
  893. options.setupImageProcessing = false; // TMP
  894. if (!this.environmentHelper) {
  895. this.environmentHelper = this.scene.createDefaultEnvironment(options)!;
  896. } else {
  897. // unlikely, but there might be a new scene! we need to dispose.
  898. // get the scene used by the envHelper
  899. let scene: Scene = this.environmentHelper.rootMesh.getScene();
  900. // is it a different scene? Oh no!
  901. if (scene !== this.scene) {
  902. this.environmentHelper.dispose();
  903. this.environmentHelper = this.scene.createDefaultEnvironment(options)!;
  904. } else {
  905. // recreate the ground
  906. if (this.environmentHelper.ground) {
  907. this.environmentHelper.ground.dispose();
  908. }
  909. // recreate the skybox
  910. if (this.environmentHelper.skybox) {
  911. this.environmentHelper.skybox.dispose();
  912. }
  913. this.environmentHelper.updateOptions(options)!;
  914. // update doesn't change the size of the skybox and ground, so we have to recreate!
  915. //this.environmentHelper.dispose();
  916. //this.environmentHelper = this.scene.createDefaultEnvironment(options)!;
  917. }
  918. }
  919. if (this.environmentHelper.rootMesh && this._globalConfiguration.scene && this._globalConfiguration.scene.environmentRotationY !== undefined) {
  920. this.environmentHelper.rootMesh.rotation.y = this._globalConfiguration.scene.environmentRotationY;
  921. }
  922. let groundConfig = (typeof groundConfiguration === 'boolean') ? {} : groundConfiguration;
  923. if (this.environmentHelper.groundMaterial && groundConfig) {
  924. this.environmentHelper.groundMaterial._perceptualColor = this.mainColor;
  925. if (groundConfig.material) {
  926. extendClassWithConfig(this.environmentHelper.groundMaterial, groundConfig.material);
  927. }
  928. if (this.environmentHelper.groundMirror) {
  929. const mirrorClearColor = this.environmentHelper.groundMaterial._perceptualColor.toLinearSpace();
  930. // TODO user camera exposure value to set the mirror clear color
  931. let exposure = Math.pow(2.0, -this.scene.imageProcessingConfiguration.exposure) * Math.PI;
  932. mirrorClearColor.scaleToRef(1 / exposure, mirrorClearColor);
  933. this.environmentHelper.groundMirror.clearColor.r = Scalar.Clamp(mirrorClearColor.r);
  934. this.environmentHelper.groundMirror.clearColor.g = Scalar.Clamp(mirrorClearColor.g);
  935. this.environmentHelper.groundMirror.clearColor.b = Scalar.Clamp(mirrorClearColor.b);
  936. this.environmentHelper.groundMirror.clearColor.a = 1;
  937. if (!this.groundMirrorEnabled) {
  938. this.environmentHelper.groundMaterial.reflectionTexture = null;
  939. }
  940. }
  941. }
  942. let skyboxMaterial = this.environmentHelper.skyboxMaterial;
  943. if (skyboxMaterial) {
  944. skyboxMaterial._perceptualColor = this.mainColor;
  945. if (postInitSkyboxMaterial) {
  946. if (typeof skyboxConifguration === 'object' && skyboxConifguration.material) {
  947. extendClassWithConfig(skyboxMaterial, skyboxConifguration.material);
  948. }
  949. }
  950. }
  951. }
  952. this._observablesManager && this._observablesManager.onModelLoadedObservable.add((model) => {
  953. this._updateGroundMirrorRenderList(model);
  954. });
  955. this.onEnvironmentConfiguredObservable.notifyObservers({
  956. sceneManager: this,
  957. object: this.environmentHelper!,
  958. newConfiguration: {
  959. skybox: skyboxConifguration,
  960. ground: groundConfiguration
  961. }
  962. });
  963. }
  964. /**
  965. * configure the lights.
  966. *
  967. * @param lightsConfiguration the (new) light(s) configuration
  968. * @param model optionally use the model to configure the camera.
  969. */
  970. protected _configureLights(lightsConfiguration: { [name: string]: ILightConfiguration | boolean | number } = {}) {
  971. // sanity check!
  972. let lightKeys = Object.keys(lightsConfiguration).filter(name => name !== 'globalRotation');
  973. if (!lightKeys.length) {
  974. if (!this.scene.lights.length)
  975. this.scene.createDefaultLight(true);
  976. } else {
  977. let lightsAvailable: Array<string> = this.scene.lights.map(light => light.name);
  978. // compare to the global (!) configuration object and dispose unneeded:
  979. let lightsToConfigure = Object.keys(this._globalConfiguration.lights || []);
  980. if (Object.keys(lightsToConfigure).length !== lightsAvailable.length) {
  981. lightsAvailable.forEach(lName => {
  982. if (lightsToConfigure.indexOf(lName) === -1) {
  983. this.scene.getLightByName(lName)!.dispose();
  984. }
  985. });
  986. }
  987. lightKeys.forEach((name, idx) => {
  988. let lightConfig: ILightConfiguration = { type: 0 };
  989. if (typeof lightsConfiguration[name] === 'object') {
  990. lightConfig = <ILightConfiguration>lightsConfiguration[name];
  991. }
  992. if (typeof lightsConfiguration[name] === 'number') {
  993. lightConfig.type = <number>lightsConfiguration[name];
  994. }
  995. lightConfig.name = name;
  996. let light: Light;
  997. // light is not already available
  998. if (lightsAvailable.indexOf(name) === -1) {
  999. let constructor = Light.GetConstructorFromName(lightConfig.type, lightConfig.name, this.scene);
  1000. if (!constructor) return;
  1001. light = constructor();
  1002. } else {
  1003. // available? get it from the scene
  1004. light = <Light>this.scene.getLightByName(name);
  1005. if (typeof lightsConfiguration[name] === 'boolean') {
  1006. lightConfig.type = light.getTypeID();
  1007. }
  1008. lightsAvailable = lightsAvailable.filter(ln => ln !== name);
  1009. if (lightConfig.type !== undefined && light.getTypeID() !== lightConfig.type) {
  1010. light.dispose();
  1011. let constructor = Light.GetConstructorFromName(lightConfig.type, lightConfig.name, this.scene);
  1012. if (!constructor) return;
  1013. light = constructor();
  1014. }
  1015. }
  1016. // if config set the light to false, dispose it.
  1017. if (lightsConfiguration[name] === false) {
  1018. light.dispose();
  1019. return;
  1020. }
  1021. //enabled
  1022. var enabled = lightConfig.enabled !== undefined ? lightConfig.enabled : !lightConfig.disabled;
  1023. light.setEnabled(enabled);
  1024. extendClassWithConfig(light, lightConfig);
  1025. //position. Some lights don't support shadows
  1026. if (light instanceof ShadowLight) {
  1027. // set default values
  1028. light.shadowMinZ = light.shadowMinZ || 0.2;
  1029. light.shadowMaxZ = Math.min(10, light.shadowMaxZ || 10); //large far clips reduce shadow depth precision
  1030. if (lightConfig.target) {
  1031. if (light.setDirectionToTarget) {
  1032. let target = Vector3.Zero().copyFrom(lightConfig.target as Vector3);
  1033. light.setDirectionToTarget(target);
  1034. }
  1035. } else if (lightConfig.direction) {
  1036. let direction = Vector3.Zero().copyFrom(lightConfig.direction as Vector3);
  1037. light.direction = direction;
  1038. }
  1039. let isShadowEnabled = false;
  1040. if (light.getTypeID() === BABYLON.Light.LIGHTTYPEID_DIRECTIONALLIGHT) {
  1041. (<BABYLON.DirectionalLight>light).shadowFrustumSize = lightConfig.shadowFrustumSize || 2;
  1042. isShadowEnabled = true;
  1043. }
  1044. else if (light.getTypeID() === BABYLON.Light.LIGHTTYPEID_SPOTLIGHT) {
  1045. let spotLight: BABYLON.SpotLight = <BABYLON.SpotLight>light;
  1046. if (lightConfig.spotAngle !== undefined) {
  1047. spotLight.angle = lightConfig.spotAngle * Math.PI / 180;
  1048. }
  1049. if (spotLight.angle && lightConfig.shadowFieldOfView) {
  1050. spotLight.shadowAngleScale = lightConfig.shadowFieldOfView / spotLight.angle;
  1051. }
  1052. isShadowEnabled = true;
  1053. }
  1054. else if (light.getTypeID() === BABYLON.Light.LIGHTTYPEID_POINTLIGHT) {
  1055. if (lightConfig.shadowFieldOfView) {
  1056. (<BABYLON.PointLight>light).shadowAngle = lightConfig.shadowFieldOfView * Math.PI / 180;
  1057. }
  1058. isShadowEnabled = true;
  1059. }
  1060. let shadowGenerator = <BABYLON.ShadowGenerator>light.getShadowGenerator();
  1061. if (isShadowEnabled && lightConfig.shadowEnabled && this._maxShadows) {
  1062. let bufferSize = lightConfig.shadowBufferSize || 256;
  1063. if (!shadowGenerator) {
  1064. shadowGenerator = new ShadowGenerator(bufferSize, light);
  1065. }
  1066. var blurKernel = this.getBlurKernel(light, bufferSize);
  1067. shadowGenerator.bias = this._shadowGeneratorBias;
  1068. shadowGenerator.blurKernel = blurKernel;
  1069. //override defaults
  1070. extendClassWithConfig(shadowGenerator, lightConfig.shadowConfig || {});
  1071. // add the focues meshes to the shadow list
  1072. this._observablesManager && this._observablesManager.onModelLoadedObservable.add((model) => {
  1073. this._updateShadowRenderList(shadowGenerator, model);
  1074. });
  1075. //if (model) {
  1076. this._updateShadowRenderList(shadowGenerator);
  1077. //}
  1078. } else if (shadowGenerator) {
  1079. shadowGenerator.dispose();
  1080. }
  1081. }
  1082. });
  1083. // render priority
  1084. let globalLightsConfiguration = this._globalConfiguration.lights || {};
  1085. Object.keys(globalLightsConfiguration).sort().forEach((name, idx) => {
  1086. let configuration = globalLightsConfiguration[name];
  1087. let light = this.scene.getLightByName(name);
  1088. // sanity check
  1089. if (!light) return;
  1090. light.renderPriority = -idx;
  1091. });
  1092. }
  1093. this.onLightsConfiguredObservable.notifyObservers({
  1094. sceneManager: this,
  1095. object: this.scene.lights,
  1096. newConfiguration: lightsConfiguration
  1097. });
  1098. }
  1099. private _shadowGroundPlane: Nullable<AbstractMesh>;
  1100. private _updateShadowRenderList(shadowGenerator: ShadowGenerator, model?: ViewerModel, resetList?: boolean) {
  1101. let focusMeshes = model ? model.meshes : this.scene.meshes;
  1102. // add the focues meshes to the shadow list
  1103. let shadownMap = shadowGenerator.getShadowMap();
  1104. if (!shadownMap) return;
  1105. if (resetList && shadownMap.renderList) {
  1106. shadownMap.renderList.length = 0;
  1107. } else {
  1108. shadownMap.renderList = shadownMap.renderList || []
  1109. }
  1110. for (var index = 0; index < focusMeshes.length; index++) {
  1111. let mesh = focusMeshes[index];
  1112. if (Tags.MatchesQuery(mesh, 'castShadow') && shadownMap.renderList.indexOf(mesh) === -1) {
  1113. shadownMap.renderList.push(mesh);
  1114. }
  1115. }
  1116. if (!this._shadowGroundPlane) {
  1117. if (shadowGenerator.useBlurCloseExponentialShadowMap) {
  1118. let shadowGroundPlane = Mesh.CreatePlane("shadowGroundPlane", 100, this.scene, false);
  1119. shadowGroundPlane.useVertexColors = false;
  1120. //material isn't ever used in rendering, just used to set back face culling
  1121. shadowGroundPlane.material = new StandardMaterial('shadowGroundPlaneMaterial', this.scene);
  1122. shadowGroundPlane.material.backFaceCulling = false;
  1123. shadowGroundPlane.rotation.x = Math.PI * 0.5;
  1124. shadowGroundPlane.freezeWorldMatrix();
  1125. this._shadowGroundPlane = shadowGroundPlane;
  1126. this.scene.removeMesh(shadowGroundPlane);
  1127. }
  1128. } else {
  1129. if (!shadowGenerator.useBlurCloseExponentialShadowMap) {
  1130. this._shadowGroundPlane.dispose();
  1131. this._shadowGroundPlane = null;
  1132. }
  1133. }
  1134. if (this._shadowGroundPlane && shadownMap.renderList.indexOf(this._shadowGroundPlane) === -1) {
  1135. shadownMap.renderList.push(this._shadowGroundPlane);
  1136. }
  1137. }
  1138. private _updateGroundMirrorRenderList(model?: ViewerModel, resetList?: boolean) {
  1139. if (this.environmentHelper && this.environmentHelper.groundMirror && this.environmentHelper.groundMirror.renderList) {
  1140. let focusMeshes = model ? model.meshes : this.scene.meshes;
  1141. let renderList = this.environmentHelper.groundMirror.renderList;
  1142. if (resetList) {
  1143. renderList.length = 0;
  1144. }
  1145. for (var index = 0; index < focusMeshes.length; index++) {
  1146. let mesh = focusMeshes[index];
  1147. if (renderList.indexOf(mesh) === -1) {
  1148. renderList.push(mesh);
  1149. }
  1150. }
  1151. }
  1152. }
  1153. /**
  1154. * Gets the shadow map blur kernel according to the light configuration.
  1155. * @param light The light used to generate the shadows
  1156. * @param bufferSize The size of the shadow map
  1157. * @return the kernel blur size
  1158. */
  1159. public getBlurKernel(light: BABYLON.IShadowLight, bufferSize: number): number {
  1160. var normalizedBlurKernel = 0.05; // TODO Should come from the config.
  1161. if (light.getTypeID() === BABYLON.Light.LIGHTTYPEID_DIRECTIONALLIGHT) {
  1162. normalizedBlurKernel = normalizedBlurKernel / (<BABYLON.DirectionalLight>light).shadowFrustumSize;
  1163. }
  1164. else if (light.getTypeID() === BABYLON.Light.LIGHTTYPEID_POINTLIGHT) {
  1165. normalizedBlurKernel = normalizedBlurKernel / (<BABYLON.PointLight>light).shadowAngle;
  1166. }
  1167. else if (light.getTypeID() === BABYLON.Light.LIGHTTYPEID_SPOTLIGHT) {
  1168. normalizedBlurKernel = normalizedBlurKernel / ((<BABYLON.SpotLight>light).angle * (<BABYLON.SpotLight>light).shadowAngleScale);
  1169. }
  1170. let minimumBlurKernel = 5 / (bufferSize / 256); //magic number that aims to keep away sawtooth shadows
  1171. var blurKernel = Math.max(bufferSize * normalizedBlurKernel, minimumBlurKernel);
  1172. return blurKernel;
  1173. }
  1174. /**
  1175. * Alters render settings to reduce features based on hardware feature limitations
  1176. * @param enableHDR Allows the viewer to run in HDR mode.
  1177. */
  1178. protected _handleHardwareLimitations(enableHDR = true) {
  1179. //flip rendering settings switches based on hardware support
  1180. let maxVaryingRows = this._engine.getCaps().maxVaryingVectors;
  1181. let maxFragmentSamplers = this._engine.getCaps().maxTexturesImageUnits;
  1182. //shadows are disabled if there's not enough varyings for a single shadow
  1183. if ((maxVaryingRows < 8) || (maxFragmentSamplers < 8)) {
  1184. this._maxShadows = 0;
  1185. } else {
  1186. this._maxShadows = 3;
  1187. }
  1188. //can we render to any >= 16-bit targets (required for HDR)
  1189. let caps = this._engine.getCaps();
  1190. let linearHalfFloatTargets = caps.textureHalfFloatRender && caps.textureHalfFloatLinearFiltering;
  1191. let linearFloatTargets = caps.textureFloatRender && caps.textureFloatLinearFiltering;
  1192. this._hdrSupport = enableHDR && !!(linearFloatTargets || linearHalfFloatTargets);
  1193. if (linearHalfFloatTargets) {
  1194. this._defaultHighpTextureType = Engine.TEXTURETYPE_HALF_FLOAT;
  1195. this._shadowGeneratorBias = 0.002;
  1196. } else if (linearFloatTargets) {
  1197. this._defaultHighpTextureType = Engine.TEXTURETYPE_FLOAT;
  1198. this._shadowGeneratorBias = 0.001;
  1199. } else {
  1200. this._defaultHighpTextureType = Engine.TEXTURETYPE_UNSIGNED_INT;
  1201. this._shadowGeneratorBias = 0.001;
  1202. }
  1203. this._defaultPipelineTextureType = this._hdrSupport ? this._defaultHighpTextureType : Engine.TEXTURETYPE_UNSIGNED_INT;
  1204. }
  1205. /**
  1206. * Dispoe the entire viewer including the scene and the engine
  1207. */
  1208. public dispose() {
  1209. // this.onCameraConfiguredObservable.clear();
  1210. this.onEnvironmentConfiguredObservable.clear();
  1211. this.onLightsConfiguredObservable.clear();
  1212. this.onModelsConfiguredObservable.clear();
  1213. this.onSceneConfiguredObservable.clear();
  1214. this.onSceneInitObservable.clear();
  1215. this.onSceneOptimizerConfiguredObservable.clear();
  1216. this.onVRConfiguredObservable.clear();
  1217. if (this.sceneOptimizer) {
  1218. this.sceneOptimizer.stop();
  1219. this.sceneOptimizer.dispose();
  1220. }
  1221. if (this.environmentHelper) {
  1222. this.environmentHelper.dispose();
  1223. }
  1224. this.models.forEach(model => {
  1225. model.dispose();
  1226. });
  1227. if (this._defaultRenderingPipeline) {
  1228. this._defaultRenderingPipeline.dispose();
  1229. }
  1230. this.models.length = 0;
  1231. if (this.scene) {
  1232. this.scene.dispose();
  1233. }
  1234. }
  1235. private _cameraBehaviorMapping: { [name: string]: number } = {};
  1236. private _setCameraBehavior(name: string, behaviorConfig: boolean | number | {
  1237. type: number;
  1238. [propName: string]: any;
  1239. }, payload?: any) {
  1240. let behavior: Behavior<ArcRotateCamera> | null;
  1241. let type: number;
  1242. if (typeof behaviorConfig === 'object') {
  1243. type = behaviorConfig.type
  1244. } else if (typeof behaviorConfig === 'number') {
  1245. type = behaviorConfig;
  1246. } else {
  1247. type = this._cameraBehaviorMapping[name];
  1248. }
  1249. if (type === undefined) return;
  1250. let config: { [propName: string]: any } = (typeof behaviorConfig === "object") ? behaviorConfig : {};
  1251. let enabled = true;
  1252. if (typeof behaviorConfig === 'boolean') {
  1253. enabled = behaviorConfig;
  1254. }
  1255. // constructing behavior
  1256. switch (type) {
  1257. case CameraBehavior.AUTOROTATION:
  1258. this.camera.useAutoRotationBehavior = enabled;
  1259. behavior = this.camera.autoRotationBehavior;
  1260. break;
  1261. case CameraBehavior.BOUNCING:
  1262. this.camera.useBouncingBehavior = enabled;
  1263. behavior = this.camera.bouncingBehavior;
  1264. break;
  1265. case CameraBehavior.FRAMING:
  1266. this.camera.useFramingBehavior = enabled;
  1267. behavior = this.camera.framingBehavior;
  1268. break;
  1269. default:
  1270. behavior = null;
  1271. break;
  1272. }
  1273. if (behavior) {
  1274. this._cameraBehaviorMapping[name] = type;
  1275. if (typeof behaviorConfig === "object") {
  1276. extendClassWithConfig(behavior, behaviorConfig);
  1277. }
  1278. }
  1279. // post attach configuration. Some functionalities require the attached camera.
  1280. switch (type) {
  1281. case CameraBehavior.AUTOROTATION:
  1282. break;
  1283. case CameraBehavior.BOUNCING:
  1284. break;
  1285. case CameraBehavior.FRAMING:
  1286. this._observablesManager && this._observablesManager.onModelLoadedObservable.add((model) => {
  1287. if (config.zoomOnBoundingInfo) {
  1288. (<FramingBehavior>behavior).zoomOnMeshHierarchy(model.rootMesh);
  1289. }
  1290. });
  1291. break;
  1292. }
  1293. }
  1294. }