浏览代码

Merge pull request #3628 from RaananW/viewer-changes-new

Viewer changes new
Raanan Weber 7 年之前
父节点
当前提交
f1224846ed

+ 54 - 9
Viewer/src/configuration/configuration.ts

@@ -46,6 +46,24 @@ export interface ViewerConfiguration {
         defaultLight?: boolean;
         clearColor?: { r: number, g: number, b: number, a: number };
         imageProcessingConfiguration?: IImageProcessingConfiguration;
+        environmentTexture?: string;
+    },
+    optimizer?: {
+        targetFrameRate?: number;
+        trackerDuration?: number;
+        autoGeneratePriorities?: boolean;
+        improvementMode?: boolean;
+        degradation?: string; // low, moderate, high
+        types?: {
+            texture?: SceneOptimizerParameters;
+            hardwareScaling?: SceneOptimizerParameters;
+            shadow?: SceneOptimizerParameters;
+            postProcess?: SceneOptimizerParameters;
+            lensFlare?: SceneOptimizerParameters;
+            particles?: SceneOptimizerParameters;
+            renderTarget?: SceneOptimizerParameters;
+            mergeMeshes?: SceneOptimizerParameters;
+        }
     },
     // at the moment, support only a single camera.
     camera?: {
@@ -66,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;
         };
@@ -84,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]: {
@@ -133,7 +164,21 @@ export interface ViewerConfiguration {
         main: ITemplateConfiguration,
         [key: string]: ITemplateConfiguration
     };
-    // nodes?
+
+    customShaders?: {
+        shaders?: {
+            [key: string]: string;
+        };
+        includes?: {
+            [key: string]: string;
+        }
+    }
+}
+
+export interface SceneOptimizerParameters {
+    priority?: number;
+    maximumSize?: number;
+    step?: number;
 }
 
 export interface IImageProcessingConfiguration {

+ 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
     }
 }

+ 13 - 0
Viewer/src/templateManager.ts

@@ -154,6 +154,19 @@ export class TemplateManager {
         }
     }
 
+    public dispose() {
+        // dispose all templates
+        Object.keys(this.templates).forEach(template => {
+            this.templates[template].dispose();
+        });
+
+        this.onInit.clear();
+        this.onAllLoaded.clear();
+        this.onEventTriggered.clear();
+        this.onLoaded.clear();
+        this.onStateChange.clear();
+    }
+
 }
 
 

+ 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];
-                }
-            }
-        });
-    }
 }

+ 257 - 5
Viewer/src/viewer/viewer.ts

@@ -1,7 +1,7 @@
 import { viewerManager } from './viewerManager';
 import { TemplateManager } from './../templateManager';
 import configurationLoader from './../configuration/loader';
-import { 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';
 
@@ -11,6 +11,7 @@ export abstract class AbstractViewer {
 
     public engine: Engine;
     public scene: Scene;
+    public sceneOptimizer: SceneOptimizer;
     public baseId: string;
 
     /**
@@ -22,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>;
@@ -84,6 +92,8 @@ export abstract class AbstractViewer {
             });
         });
 
+        //this.onModelLoadedObservable.add(this.initEnvironment.bind(this));
+
     }
 
     public getBaseId(): string {
@@ -113,6 +123,18 @@ export abstract class AbstractViewer {
 
     public dispose() {
         window.removeEventListener('resize', this.resize);
+
+        this.sceneOptimizer.stop();
+        this.sceneOptimizer.dispose();
+
+        if (this.scene.activeCamera) {
+            this.scene.activeCamera.detachControl(this.canvas);
+        }
+
+        this.scene.dispose();
+        this.engine.dispose();
+
+        this.templateManager.dispose();
     }
 
     protected abstract prepareContainerElement();
@@ -163,6 +185,10 @@ export abstract class AbstractViewer {
      * @memberof Viewer
      */
     protected initEngine(): Promise<Engine> {
+
+        // init custom shaders
+        this.injectCustomShaders();
+
         let canvasElement = this.templateManager.getCanvas();
         if (!canvasElement) {
             return Promise.reject('Canvas element not found!');
@@ -186,6 +212,9 @@ export abstract class AbstractViewer {
             this.engine.setHardwareScalingLevel(scale);
         }
 
+        // set hardware limitations for scene initialization
+        this.handleHardwareLimitations();
+
         return Promise.resolve(this.engine);
     }
 
@@ -200,9 +229,47 @@ export abstract class AbstractViewer {
         this.scene = new Scene(this.engine);
         // make sure there is a default camera and light.
         this.scene.createDefaultCameraOrLight(true, true, true);
-        if (this.configuration.scene && this.configuration.scene.debug) {
-            this.scene.debugLayer.show();
+        if (this.configuration.scene) {
+            if (this.configuration.scene.debug) {
+                this.scene.debugLayer.show();
+            }
+
+            // Scene optimizer
+            if (this.configuration.optimizer) {
+
+                let optimizerConfig = this.configuration.optimizer;
+                let optimizerOptions: SceneOptimizerOptions = new SceneOptimizerOptions(optimizerConfig.targetFrameRate, optimizerConfig.trackerDuration);
+                // check for degradation
+                if (optimizerConfig.degradation) {
+                    switch (optimizerConfig.degradation) {
+                        case "low":
+                            optimizerOptions = SceneOptimizerOptions.LowDegradationAllowed(optimizerConfig.targetFrameRate);
+                            break;
+                        case "moderate":
+                            optimizerOptions = SceneOptimizerOptions.ModerateDegradationAllowed(optimizerConfig.targetFrameRate);
+                            break;
+                        case "hight":
+                            optimizerOptions = SceneOptimizerOptions.HighDegradationAllowed(optimizerConfig.targetFrameRate);
+                            break;
+                    }
+                }
+
+                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;
+            }
         }
+
+
+
         return Promise.resolve(this.scene);
     }
 
@@ -229,9 +296,194 @@ 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() {
+        //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.maxShadows = 0;
+        } else {
+            this.maxShadows = 3;
+        }
+
+        //can we render to any >= 16-bit targets (required for HDR)
+        let caps = this.engine.getCaps();
+        let linearHalfFloatTargets = caps.textureHalfFloatRender && caps.textureHalfFloatLinearFiltering;
+        let linearFloatTargets = caps.textureFloatRender && caps.textureFloatLinearFiltering;
+
+        let supportsHDR: boolean = !!(linearFloatTargets || linearHalfFloatTargets);
+
+        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;
+    }
+
+    /**
+     * Injects all the spectre shader in the babylon shader store
+     */
+    protected injectCustomShaders(): void {
+        let customShaders = this.configuration.customShaders;
+        // Inject all the spectre shader in the babylon shader store.
+        if (!customShaders) {
+            return;
+        }
+        if (customShaders.shaders) {
+            Object.keys(customShaders.shaders).forEach(key => {
+                // typescript considers a callback "unsafe", so... '!'
+                Effect.ShadersStore[key] = customShaders!.shaders![key];
             });
+        }
+        if (customShaders.includes) {
+            Object.keys(customShaders.includes).forEach(key => {
+                // typescript considers a callback "unsafe", so... '!'
+                Effect.IncludesShadersStore[key] = customShaders!.includes![key];
+            });
+        }
+    }
+
+    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];
+                }
+            }
         });
     }
 }

+ 2 - 0
dist/preview release/what's new.md

@@ -38,6 +38,8 @@
 - Added base64 helper functions to `Tools` ([bghgary](https://github.com/bghgary))
 - Added `createDefaultCamera` and `createDefaultLight` functions to `Scene` ([bghgary](https://github.com/bghgary))
 - Gulp process now supports multiple outputs when using webpack. ([RaananW](https://github.com/RaananW))
+- (Viewer) Scene Optimizer intergrated in viewer. ([RaananW](https://github.com/RaananW))
+- (Viewer) The viewer supports custom shaders in the configuration. ([RaananW](https://github.com/RaananW))
 
 ## Bug fixes
 - Texture extension detection in `Engine.CreateTexture` ([sebavan](https://github.com/sebavan))