瀏覽代碼

Full environment implementation
The viewer is now using Babylon's environment helper instead of self-developed env.
Configuration was simplified
Some environment variable have moced to the abstract viewer, and the initEnvironment function has also moved there.
The default viewer differs in camera, lights and the templates.

Raanan Weber 7 年之前
父節點
當前提交
de1267b916

+ 22 - 10
Viewer/src/configuration/configuration.ts

@@ -46,8 +46,7 @@ export interface ViewerConfiguration {
         defaultLight?: boolean;
         clearColor?: { r: number, g: number, b: number, a: number };
         imageProcessingConfiguration?: IImageProcessingConfiguration;
-        enableHdr?: boolean;
-        maxShadows?: number;
+        environmentTexture?: string;
     },
     optimizer?: {
         targetFrameRate?: number;
@@ -85,14 +84,15 @@ export interface ViewerConfiguration {
         [propName: string]: any;
     },
     skybox?: {
-        cubeTexture: {
+        cubeTexture?: {
             noMipMap?: boolean;
             gammaSpace?: boolean;
-            url: string | Array<string>;
+            url?: string | Array<string>;
         };
-        pbr?: boolean;
+        color?: { r: number, g: number, b: number };
+        pbr?: boolean; // deprecated
         scale?: number;
-        blur?: number;
+        blur?: number; // deprecated
         material?: {
             imageProcessingConfiguration?: IImageProcessingConfiguration;
         };
@@ -103,11 +103,23 @@ export interface ViewerConfiguration {
     ground?: boolean | {
         size?: number;
         receiveShadows?: boolean;
-        shadowOnly?: boolean;
-        mirror?: boolean;
-        material?: {
+        shadowLevel?: number;
+        shadowOnly?: boolean; // deprecated
+        mirror?: boolean | {
+            sizeRatio?: number;
+            blurKernel?: number;
+            amount?: number;
+            fresnelWeight?: number;
+            fallOffDistance?: number;
+            textureType?: number;
+        };
+        texture?: string;
+        color?: { r: number, g: number, b: number };
+        opacity?: number;
+        material?: { // deprecated!
             [propName: string]: any;
-        }
+        };
+
     };
     lights?: {
         [name: string]: {

+ 10 - 14
Viewer/src/configuration/types/default.ts

@@ -1,9 +1,7 @@
 import { ViewerConfiguration } from './../configuration';
 
 export let defaultConfiguration: ViewerConfiguration = {
-    version: "0.1",
-    //eventPrefix: 'babylonviewer-',
-    //events: true,
+    version: "3.2.0-alpha4",
     templates: {
         main: {
             html: require("../../../assets/templates/default/defaultTemplate.html")
@@ -71,12 +69,12 @@ export let defaultConfiguration: ViewerConfiguration = {
             }
         }
     },
-    /*lights: [
-        {
+    /*lights: {
+        "default": {
             type: 1,
             shadowEnabled: true,
-            direction: { x: -0.2, y: -1, z: 0 },
-            position: { x: 0.017, y: 50, z: 0 },
+            direction: { x: -0.2, y: -0.8, z: 0 },
+            position: { x: 10, y: 10, z: 0 },
             intensity: 4.5,
             shadowConfig: {
                 useBlurExponentialShadowMap: true,
@@ -85,16 +83,16 @@ export let defaultConfiguration: ViewerConfiguration = {
                 blurScale: 4
             }
         }
-    ],*/
+    },*/
     skybox: {
-        cubeTexture: {
+        /*cubeTexture: {
             url: 'https://playground.babylonjs.com/textures/environment.dds',
             gammaSpace: false
-        },
+        },*/
         pbr: true,
         blur: 0.7,
         infiniteDIstance: false,
-        material: {
+        /*material: {
             imageProcessingConfiguration: {
                 colorCurves: {
                     globalDensity: 89,
@@ -110,7 +108,7 @@ export let defaultConfiguration: ViewerConfiguration = {
                 vignetteColor: { r: 0.8, g: 0.6, b: 0.4 },
                 vignetteM: true
             }
-        }
+        }*/
     },
     ground: true,
     engine: {
@@ -122,7 +120,5 @@ export let defaultConfiguration: ViewerConfiguration = {
             contrast: 1.66,
             toneMappingEnabled: true
         }
-        //autoRotate: true,
-        //rotationSpeed: 0.1
     }
 }

+ 25 - 19
Viewer/src/viewer/defaultViewer.ts

@@ -135,7 +135,7 @@ export class DefaultViewer extends AbstractViewer {
         this.setupCamera(meshes);
         this.setupLights(meshes);
 
-        return this.initEnvironment(meshes);
+        return; //this.initEnvironment(meshes);
     }
 
     private setModelMetaData() {
@@ -167,7 +167,7 @@ export class DefaultViewer extends AbstractViewer {
 
     }
 
-    public initEnvironment(focusMeshes: Array<AbstractMesh> = []): Promise<Scene> {
+    /*protected initEnvironment(focusMeshes: Array<AbstractMesh> = []): Promise<Scene> {
         if (this.configuration.skybox) {
             // Define a general environment textue
             let texture;
@@ -267,7 +267,7 @@ export class DefaultViewer extends AbstractViewer {
         }
 
         return Promise.resolve(this.scene);
-    }
+    }*/
 
     public showOverlayScreen(subScreen: string) {
         let template = this.templateManager.getTemplate('overlay');
@@ -382,7 +382,7 @@ export class DefaultViewer extends AbstractViewer {
 
                 //position. Some lights don't support shadows
                 if (light instanceof ShadowLight) {
-                    if (lightConfig.shadowEnabled) {
+                    if (lightConfig.shadowEnabled && this.maxShadows) {
                         var shadowGenerator = new ShadowGenerator(512, light);
                         this.extendClassWithConfig(shadowGenerator, lightConfig.shadowConfig || {});
                         // add the focues meshes to the shadow list
@@ -395,6 +395,22 @@ export class DefaultViewer extends AbstractViewer {
                     }
                 }
             });
+        } else {
+            if (!this.configuration.lights && this.configuration.ground) {
+                let light = this.scene.lights[0];
+                if (light instanceof ShadowLight) {
+                    if (this.maxShadows) {
+                        var shadowGenerator = new ShadowGenerator(512, light);
+                        // add the focues meshes to the shadow list
+                        let shadownMap = shadowGenerator.getShadowMap();
+                        if (!shadownMap) return;
+                        let renderList = shadownMap.renderList;
+                        for (var index = 0; index < focusMeshes.length; index++) {
+                            renderList && renderList.push(focusMeshes[index]);
+                        }
+                    }
+                }
+            }
         }
     }
 
@@ -430,6 +446,11 @@ export class DefaultViewer extends AbstractViewer {
         if (sceneConfig.autoRotate) {
             this.camera.useAutoRotationBehavior = true;
         }
+
+        const sceneExtends = this.scene.getWorldExtends();
+        const sceneDiagonal = sceneExtends.max.subtract(sceneExtends.min);
+        const sceneDiagonalLenght = sceneDiagonal.length();
+        this.camera.upperRadiusLimit = sceneDiagonalLenght * 3;
     }
 
     private setCameraBehavior(behaviorConfig: number | {
@@ -481,19 +502,4 @@ export class DefaultViewer extends AbstractViewer {
                 break;
         }
     }
-
-    private extendClassWithConfig(object: any, config: any) {
-        if (!config) return;
-        Object.keys(config).forEach(key => {
-            if (key in object && typeof object[key] !== 'function') {
-                if (typeof object[key] === 'function') return;
-                // if it is an object, iterate internally until reaching basic types
-                if (typeof object[key] === 'object') {
-                    this.extendClassWithConfig(object[key], config[key]);
-                } else {
-                    object[key] = config[key];
-                }
-            }
-        });
-    }
 }

+ 164 - 9
Viewer/src/viewer/viewer.ts

@@ -1,7 +1,7 @@
 import { viewerManager } from './viewerManager';
 import { TemplateManager } from './../templateManager';
 import configurationLoader from './../configuration/loader';
-import { Effect, SceneOptimizer, SceneOptimizerOptions, Observable, Engine, Scene, ArcRotateCamera, Vector3, SceneLoader, AbstractMesh, Mesh, HemisphericLight, Database, SceneLoaderProgressEvent, ISceneLoaderPlugin, ISceneLoaderPluginAsync } from 'babylonjs';
+import { CubeTexture, Color3, IEnvironmentHelperOptions, EnvironmentHelper, Effect, SceneOptimizer, SceneOptimizerOptions, Observable, Engine, Scene, ArcRotateCamera, Vector3, SceneLoader, AbstractMesh, Mesh, HemisphericLight, Database, SceneLoaderProgressEvent, ISceneLoaderPlugin, ISceneLoaderPluginAsync } from 'babylonjs';
 import { ViewerConfiguration } from '../configuration/configuration';
 import { PromiseObservable } from '../util/promiseObservable';
 
@@ -23,6 +23,13 @@ export abstract class AbstractViewer {
     public lastUsedLoader: ISceneLoaderPlugin | ISceneLoaderPluginAsync;
 
     protected configuration: ViewerConfiguration;
+    protected environmentHelper: EnvironmentHelper;
+
+    protected defaultHighpTextureType: number;
+    protected shadowGeneratorBias: number;
+    protected defaultPipelineTextureType: number;
+    protected maxShadows: number;
+
 
     // observables
     public onSceneInitObservable: PromiseObservable<Scene>;
@@ -85,6 +92,8 @@ export abstract class AbstractViewer {
             });
         });
 
+        //this.onModelLoadedObservable.add(this.initEnvironment.bind(this));
+
     }
 
     public getBaseId(): string {
@@ -203,6 +212,7 @@ export abstract class AbstractViewer {
             this.engine.setHardwareScalingLevel(scale);
         }
 
+        // set hardware limitations for scene initialization
         this.handleHardwareLimitations();
 
         return Promise.resolve(this.engine);
@@ -247,6 +257,15 @@ export abstract class AbstractViewer {
                 this.sceneOptimizer = new SceneOptimizer(this.scene, optimizerOptions, optimizerConfig.autoGeneratePriorities, optimizerConfig.improvementMode);
                 this.sceneOptimizer.start();
             }
+
+            // image processing configuration - optional.
+            if (this.configuration.scene.imageProcessingConfiguration) {
+                this.extendClassWithConfig(this.scene.imageProcessingConfiguration, this.configuration.scene.imageProcessingConfiguration);
+            }
+            if (this.configuration.scene.environmentTexture) {
+                const environmentTexture = CubeTexture.CreateFromPrefilteredData(this.configuration.scene.environmentTexture, this.scene);
+                this.scene.environmentTexture = environmentTexture;
+            }
         }
 
 
@@ -277,27 +296,136 @@ export abstract class AbstractViewer {
                 }, plugin)!;
             });
         }).then((meshes: Array<AbstractMesh>) => {
-            return this.onModelLoadedObservable.notifyWithPromise(meshes).then(() => {
-                return this.scene;
-            });
+            return this.onModelLoadedObservable.notifyWithPromise(meshes)
+                .then(() => {
+                    this.initEnvironment();
+                }).then(() => {
+                    return this.scene;
+                });
         });
     }
 
+    protected initEnvironment(focusMeshes: Array<AbstractMesh> = []): Promise<Scene> {
+        if (!this.configuration.skybox && !this.configuration.ground) {
+            if (this.environmentHelper) {
+                this.environmentHelper.dispose();
+            };
+            return Promise.resolve(this.scene);
+        }
+
+        const options: Partial<IEnvironmentHelperOptions> = {
+            createGround: !!this.configuration.ground,
+            createSkybox: !!this.configuration.skybox,
+            setupImageProcessing: false // will be done at the scene level!
+        };
+
+        if (this.configuration.ground) {
+            let groundConfig = (typeof this.configuration.ground === 'boolean') ? {} : this.configuration.ground;
+
+            let groundSize = groundConfig.size || (this.configuration.skybox && this.configuration.skybox.scale);
+            if (groundSize) {
+                options.groundSize = groundSize;
+            }
+
+            options.enableGroundShadow = this.configuration.ground === true || groundConfig.receiveShadows;
+            if (groundConfig.shadowLevel) {
+                options.groundShadowLevel = groundConfig.shadowLevel;
+            }
+            options.enableGroundMirror = !!groundConfig.mirror;
+            if (groundConfig.texture) {
+                options.groundTexture = groundConfig.texture;
+            }
+            if (groundConfig.color) {
+                options.groundColor = new Color3(groundConfig.color.r, groundConfig.color.g, groundConfig.color.b)
+            }
+
+            if (groundConfig.mirror) {
+                options.enableGroundMirror = true;
+                // to prevent undefines
+                if (typeof groundConfig.mirror === "object") {
+                    if (groundConfig.mirror.amount)
+                        options.groundMirrorAmount = groundConfig.mirror.amount;
+                    if (groundConfig.mirror.sizeRatio)
+                        options.groundMirrorSizeRatio = groundConfig.mirror.sizeRatio;
+                    if (groundConfig.mirror.blurKernel)
+                        options.groundMirrorBlurKernel = groundConfig.mirror.blurKernel;
+                    if (groundConfig.mirror.fresnelWeight)
+                        options.groundMirrorFresnelWeight = groundConfig.mirror.fresnelWeight;
+                    if (groundConfig.mirror.fallOffDistance)
+                        options.groundMirrorFallOffDistance = groundConfig.mirror.fallOffDistance;
+                    if (this.defaultHighpTextureType !== undefined)
+                        options.groundMirrorTextureType = this.defaultHighpTextureType;
+                }
+            }
+
+        }
+
+        let postInitSkyboxMaterial = false;
+        if (this.configuration.skybox) {
+            let conf = this.configuration.skybox;
+            if (conf.material && conf.material.imageProcessingConfiguration) {
+                options.setupImageProcessing = false; // will be configured later manually.
+            }
+            let skyboxSize = this.configuration.skybox.scale;
+            if (skyboxSize) {
+                options.skyboxSize = skyboxSize;
+            }
+            options.sizeAuto = !options.skyboxSize;
+            if (conf.color) {
+                options.skyboxColor = new Color3(conf.color.r, conf.color.g, conf.color.b)
+            }
+            if (conf.cubeTexture && conf.cubeTexture.url) {
+                if (typeof conf.cubeTexture.url === "string") {
+                    options.skyboxTexture = conf.cubeTexture.url;
+                } else {
+                    // init later!
+                    postInitSkyboxMaterial = true;
+                }
+            }
+
+            if (conf.material && conf.material.imageProcessingConfiguration) {
+                postInitSkyboxMaterial = true;
+            }
+        }
+
+        if (!this.environmentHelper) {
+            this.environmentHelper = this.scene.createDefaultEnvironment(options)!;
+        }
+        else {
+            // there might be a new scene! we need to dispose.
+            // Need to decide if a scene should stay or be disposed.
+            this.environmentHelper.dispose();
+            //this.environmentHelper.updateOptions(options);
+            this.environmentHelper = this.scene.createDefaultEnvironment(options)!;
+        }
+        console.log(options);
+
+        if (postInitSkyboxMaterial) {
+            let skyboxMaterial = this.environmentHelper.skyboxMaterial;
+            if (skyboxMaterial) {
+                if (this.configuration.skybox && this.configuration.skybox.material && this.configuration.skybox.material.imageProcessingConfiguration) {
+                    this.extendClassWithConfig(skyboxMaterial.imageProcessingConfiguration, this.configuration.skybox.material.imageProcessingConfiguration);
+                }
+            }
+        }
+
+        return Promise.resolve(this.scene);
+    }
+
     /**
 		 * Alters render settings to reduce features based on hardware feature limitations
 		 * @param options Viewer options to modify
 		 */
     protected handleHardwareLimitations() {
-        if (!this.configuration.scene) {
-            this.configuration.scene = {};
-        }
         //flip rendering settings switches based on hardware support
         let maxVaryingRows = this.engine.getCaps().maxVaryingVectors;
         let maxFragmentSamplers = this.engine.getCaps().maxTexturesImageUnits;
 
         //shadows are disabled if there's not enough varyings for a single shadow
         if ((maxVaryingRows < 8) || (maxFragmentSamplers < 8)) {
-            this.configuration.scene.maxShadows = 0;
+            this.maxShadows = 0;
+        } else {
+            this.maxShadows = 3;
         }
 
         //can we render to any >= 16-bit targets (required for HDR)
@@ -306,7 +434,19 @@ export abstract class AbstractViewer {
         let linearFloatTargets = caps.textureFloatRender && caps.textureFloatLinearFiltering;
 
         let supportsHDR: boolean = !!(linearFloatTargets || linearHalfFloatTargets);
-        this.configuration.scene.enableHdr = this.configuration.scene.enableHdr && supportsHDR;
+
+        if (linearHalfFloatTargets) {
+            this.defaultHighpTextureType = Engine.TEXTURETYPE_HALF_FLOAT;
+            this.shadowGeneratorBias = 0.002;
+        } else if (linearFloatTargets) {
+            this.defaultHighpTextureType = Engine.TEXTURETYPE_FLOAT;
+            this.shadowGeneratorBias = 0.001;
+        } else {
+            this.defaultHighpTextureType = Engine.TEXTURETYPE_UNSIGNED_INT;
+            this.shadowGeneratorBias = 0.001;
+        }
+
+        this.defaultPipelineTextureType = supportsHDR ? this.defaultHighpTextureType : Engine.TEXTURETYPE_UNSIGNED_INT;
     }
 
     /**
@@ -331,4 +471,19 @@ export abstract class AbstractViewer {
             });
         }
     }
+
+    protected extendClassWithConfig(object: any, config: any) {
+        if (!config) return;
+        Object.keys(config).forEach(key => {
+            if (key in object && typeof object[key] !== 'function') {
+                if (typeof object[key] === 'function') return;
+                // if it is an object, iterate internally until reaching basic types
+                if (typeof object[key] === 'object') {
+                    this.extendClassWithConfig(object[key], config[key]);
+                } else {
+                    object[key] = config[key];
+                }
+            }
+        });
+    }
 }