Selaa lähdekoodia

reorganization

Raanan Weber 7 vuotta sitten
vanhempi
commit
1443fbdb14
29 muutettua tiedostoa jossa 688 lisäystä ja 561 poistoa
  1. 1 379
      Viewer/src/configuration/configuration.ts
  2. 2 1
      Viewer/src/configuration/index.ts
  3. 24 0
      Viewer/src/configuration/interfaces/cameraConfiguration.ts
  4. 101 0
      Viewer/src/configuration/interfaces/colorGradingConfiguration.ts
  5. 20 0
      Viewer/src/configuration/interfaces/groundConfiguration.ts
  6. 38 0
      Viewer/src/configuration/interfaces/imageProcessingConfiguration.ts
  7. 13 0
      Viewer/src/configuration/interfaces/index.ts
  8. 42 0
      Viewer/src/configuration/interfaces/lightConfiguration.ts
  9. 63 0
      Viewer/src/configuration/interfaces/modelConfiguration.ts
  10. 5 0
      Viewer/src/configuration/interfaces/observersConfiguration.ts
  11. 39 0
      Viewer/src/configuration/interfaces/sceneConfiguration.ts
  12. 24 0
      Viewer/src/configuration/interfaces/sceneOptimizerConfiguration.ts
  13. 19 0
      Viewer/src/configuration/interfaces/skyboxConfiguration.ts
  14. 1 1
      Viewer/src/configuration/loader.ts
  15. 1 1
      Viewer/src/configuration/mappers.ts
  16. 1 1
      Viewer/src/configuration/types/index.ts
  17. 0 0
      Viewer/src/helper/deepmerge.ts
  18. 4 0
      Viewer/src/helper/index.ts
  19. 12 14
      Viewer/src/labs/viewerLabs.ts
  20. 1 1
      Viewer/src/loader/modelLoader.ts
  21. 11 0
      Viewer/src/loader/plugins/index.ts
  22. 77 0
      Viewer/src/managers/observablesManager.ts
  23. 3 3
      Viewer/src/model/viewerModel.ts
  24. 42 41
      Viewer/src/optimizer/custom/extended.ts
  25. 2 2
      Viewer/src/optimizer/custom/index.ts
  26. 3 2
      Viewer/src/templating/templateManager.ts
  27. 35 5
      Viewer/src/viewer/defaultViewer.ts
  28. 42 34
      Viewer/src/viewer/sceneManager.ts
  29. 62 76
      Viewer/src/viewer/viewer.ts

+ 1 - 379
Viewer/src/configuration/configuration.ts

@@ -1,5 +1,5 @@
-import { ITemplateConfiguration } from './../templateManager';
 import { EngineOptions, IGlowLayerOptions, DepthOfFieldEffectBlurLevel } from 'babylonjs';
+import { IObserversConfiguration, IModelConfiguration, ISceneConfiguration, ISceneOptimizerConfiguration, ICameraConfiguration, ISkyboxConfiguration, IGroundConfiguration, ILightConfiguration, IDefaultRenderingPipelineConfiguration, ITemplateConfiguration } from './interfaces';
 
 export function getConfigurationKey(key: string, configObject: any) {
     let splits = key.split('.');
@@ -112,382 +112,4 @@ export interface ViewerConfiguration {
         defaultRenderingPipelines?: boolean | IDefaultRenderingPipelineConfiguration;
         globalLightRotation?: number;
     }
-}
-
-export interface IModelConfiguration {
-    id?: string;
-    url?: string;
-    root?: string; //optional
-    file?: string | File; // is a file being loaded? root and url ignored
-    loader?: string; // obj, gltf?
-    position?: { x: number, y: number, z: number };
-    rotation?: { x: number, y: number, z: number, w?: number };
-    scaling?: { x: number, y: number, z: number };
-    parentObjectIndex?: number; // the index of the parent object of the model in the loaded meshes array.
-
-    castShadow?: boolean;
-    receiveShadows?: boolean;
-    normalize?: boolean | {
-        center?: boolean;
-        unitSize?: boolean;
-        parentIndex?: number;
-    }; // should the model be scaled to unit-size
-
-    title?: string;
-    subtitle?: string;
-    thumbnail?: string; // URL or data-url
-
-    animation?: {
-        autoStart?: boolean | string;
-        playOnce?: boolean;
-        autoStartIndex?: number;
-    }
-
-    entryAnimation?: IModelAnimationConfiguration;
-    exitAnimation?: IModelAnimationConfiguration;
-
-    material?: {
-        directEnabled?: boolean;
-        directIntensity?: number;
-        emissiveIntensity?: number;
-        environmentIntensity?: number;
-        [propName: string]: any;
-    }
-
-    /** 
-     * Rotation offset axis definition
-     */
-    rotationOffsetAxis?: {
-        x: number;
-        y: number;
-        z: number;
-    };
-
-    /**
-     * the offset angle
-     */
-    rotationOffsetAngle?: number;
-
-    loaderConfiguration?: {
-        maxLODsToLoad?: number;
-        progressiveLoading?: boolean;
-    }
-
-    // [propName: string]: any; // further configuration, like title and creator
-}
-
-export interface ISkyboxConfiguration {
-    cubeTexture?: {
-        noMipMap?: boolean;
-        gammaSpace?: boolean;
-        url?: string | Array<string>;
-    };
-    color?: { r: number, g: number, b: number };
-    pbr?: boolean; // deprecated
-    scale?: number;
-    blur?: number; // deprecated
-    material?: {
-        imageProcessingConfiguration?: IImageProcessingConfiguration;
-        [propName: string]: any;
-    };
-    infiniteDistance?: boolean;
-
-}
-
-export interface IGroundConfiguration {
-    size?: number;
-    receiveShadows?: boolean;
-    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?: {
-        [propName: string]: any;
-    };
-}
-
-export interface ISceneConfiguration {
-    debug?: boolean;
-    clearColor?: { r: number, g: number, b: number, a: number };
-    mainColor?: { r?: number, g?: number, b?: number };
-    imageProcessingConfiguration?: IImageProcessingConfiguration;
-    environmentTexture?: string;
-    colorGrading?: IColorGradingConfiguration;
-    environmentRotationY?: number;
-    /**
-     * Deprecated, please use default rendering pipeline
-     */
-    glow?: boolean | IGlowLayerOptions;
-    disableHdr?: boolean;
-    renderInBackground?: boolean;
-    disableCameraControl?: boolean;
-    animationPropertiesOverride?: {
-        [propName: string]: any;
-    };
-    defaultMaterial?: {
-        materialType: "standard" | "pbr";
-        [propName: string]: any;
-    };
-    flags?: {
-        shadowsEnabled?: boolean;
-        particlesEnabled?: boolean;
-        collisionsEnabled?: boolean;
-        lightsEnabled?: boolean;
-        texturesEnabled?: boolean;
-        lensFlaresEnabled?: boolean;
-        proceduralTexturesEnabled?: boolean;
-        renderTargetsEnabled?: boolean;
-        spritesEnabled?: boolean;
-        skeletonsEnabled?: boolean;
-        audioEnabled?: boolean;
-    }
-}
-
-/**
- * The Color Grading Configuration groups the different settings used to define the color grading used in the viewer.
- */
-export interface IColorGradingConfiguration {
-
-    /**
-     * Transform data string, encoded as determined by transformDataFormat.
-     */
-    transformData: string;
-
-    /**
-     * The encoding format of TransformData (currently only raw-base16 is supported).
-     */
-    transformDataFormat: string;
-
-    /**
-     * The weight of the transform
-     */
-    transformWeight: number;
-
-    /**
-     * Color curve colorFilterHueGlobal value
-     */
-    colorFilterHueGlobal: number;
-
-    /**
-     * Color curve colorFilterHueShadows value
-     */
-    colorFilterHueShadows: number;
-
-    /**
-     * Color curve colorFilterHueMidtones value
-     */
-    colorFilterHueMidtones: number;
-
-    /**
-     * Color curve colorFilterHueHighlights value
-     */
-    colorFilterHueHighlights: number;
-
-    /**
-     * Color curve colorFilterDensityGlobal value
-     */
-    colorFilterDensityGlobal: number;
-
-    /**
-     * Color curve colorFilterDensityShadows value
-     */
-    colorFilterDensityShadows: number;
-
-    /**
-     * Color curve colorFilterDensityMidtones value
-     */
-    colorFilterDensityMidtones: number;
-
-    /**
-     * Color curve colorFilterDensityHighlights value
-     */
-    colorFilterDensityHighlights: number;
-
-    /**
-     * Color curve saturationGlobal value
-     */
-    saturationGlobal: number;
-
-    /**
-     * Color curve saturationShadows value
-     */
-    saturationShadows: number;
-
-    /**
-     * Color curve saturationMidtones value
-     */
-    saturationMidtones: number;
-
-    /**
-     * Color curve saturationHighlights value
-     */
-    saturationHighlights: number;
-
-    /**
-     * Color curve exposureGlobal value
-     */
-    exposureGlobal: number;
-
-    /**
-     * Color curve exposureShadows value
-     */
-    exposureShadows: number;
-
-    /**
-     * Color curve exposureMidtones value
-     */
-    exposureMidtones: number;
-
-    /**
-     * Color curve exposureHighlights value
-     */
-    exposureHighlights: number;
-
-}
-
-export interface ISceneOptimizerConfiguration {
-    targetFrameRate?: number;
-    trackerDuration?: number;
-    autoGeneratePriorities?: boolean;
-    improvementMode?: boolean;
-    degradation?: string; // low, moderate, high
-    types?: {
-        texture?: ISceneOptimizerParameters;
-        hardwareScaling?: ISceneOptimizerParameters;
-        shadow?: ISceneOptimizerParameters;
-        postProcess?: ISceneOptimizerParameters;
-        lensFlare?: ISceneOptimizerParameters;
-        particles?: ISceneOptimizerParameters;
-        renderTarget?: ISceneOptimizerParameters;
-        mergeMeshes?: ISceneOptimizerParameters;
-    }
-    custom?: string;
-}
-
-export interface IObserversConfiguration {
-    onEngineInit?: string;
-    onSceneInit?: string;
-    onModelLoaded?: string;
-}
-
-export interface ICameraConfiguration {
-    position?: { x: number, y: number, z: number };
-    rotation?: { x: number, y: number, z: number, w: number };
-    fov?: number;
-    fovMode?: number;
-    minZ?: number;
-    maxZ?: number;
-    inertia?: number;
-    exposure?: number;
-    pinchPrecision?: number;
-    behaviors?: {
-        [name: string]: boolean | number | ICameraBehaviorConfiguration;
-    };
-    disableCameraControl?: boolean;
-    disableCtrlForPanning?: boolean;
-    disableAutoFocus?: boolean;
-
-    [propName: string]: any;
-}
-
-export interface ICameraBehaviorConfiguration {
-    type: number;
-    [propName: string]: any;
-}
-
-export interface ILightConfiguration {
-    type: number;
-    name?: string;
-    disabled?: boolean;
-    position?: { x: number, y: number, z: number };
-    target?: { x: number, y: number, z: number };
-    direction?: { x: number, y: number, z: number };
-    diffuse?: { r: number, g: number, b: number };
-    specular?: { r: number, g: number, b: number };
-    intensity?: number;
-    intensityMode?: number;
-    radius?: number;
-    shadownEnabled?: boolean; // only on specific lights!
-    shadowConfig?: {
-        useBlurExponentialShadowMap?: boolean;
-        useBlurCloseExponentialShadowMap?: boolean;
-        useKernelBlur?: boolean;
-        blurKernel?: number;
-        blurScale?: number;
-        minZ?: number;
-        maxZ?: number;
-        frustumSize?: number;
-        angleScale?: number;
-        frustumEdgeFalloff?: number;
-        [propName: string]: any;
-    };
-    spotAngle?: number;
-    shadowFieldOfView?: number;
-    shadowBufferSize?: number;
-    shadowFrustumSize?: number;
-    shadowMinZ?: number;
-    shadowMaxZ?: number;
-    [propName: string]: any;
-
-    // no behaviors for light at the moment, but allowing configuration for future reference.
-    behaviors?: {
-        [name: string]: number | {
-            type: number;
-            [propName: string]: any;
-        };
-    };
-}
-
-export interface ISceneOptimizerParameters {
-    priority?: number;
-    maximumSize?: number;
-    step?: number;
-}
-
-export interface IImageProcessingConfiguration {
-    colorGradingEnabled?: boolean;
-    colorCurvesEnabled?: boolean;
-    colorCurves?: {
-        globalHue?: number;
-        globalDensity?: number;
-        globalSaturation?: number;
-        globalExposure?: number;
-        highlightsHue?: number;
-        highlightsDensity?: number;
-        highlightsSaturation?: number;
-        highlightsExposure?: number;
-        midtonesHue?: number;
-        midtonesDensity?: number;
-        midtonesSaturation?: number;
-        midtonesExposure?: number;
-        shadowsHue?: number;
-        shadowsDensity?: number;
-        shadowsSaturation?: number;
-        shadowsExposure?: number;
-    };
-    colorGradingWithGreenDepth?: boolean;
-    colorGradingBGR?: boolean;
-    exposure?: number;
-    toneMappingEnabled?: boolean;
-    contrast?: number;
-    vignetteEnabled?: boolean;
-    vignetteStretch?: number;
-    vignetteCentreX?: number;
-    vignetteCentreY?: number;
-    vignetteWeight?: number;
-    vignetteColor?: { r: number, g: number, b: number, a?: number };
-    vignetteCameraFov?: number;
-    vignetteBlendMode?: number;
-    vignetteM?: boolean;
-    applyByPostProcess?: boolean;
-    isEnabled?: boolean;
 }

+ 2 - 1
Viewer/src/configuration/index.ts

@@ -1 +1,2 @@
-export * from './configuration';
+export * from './configuration';
+export * from './interfaces';

+ 24 - 0
Viewer/src/configuration/interfaces/cameraConfiguration.ts

@@ -0,0 +1,24 @@
+export interface ICameraConfiguration {
+    position?: { x: number, y: number, z: number };
+    rotation?: { x: number, y: number, z: number, w: number };
+    fov?: number;
+    fovMode?: number;
+    minZ?: number;
+    maxZ?: number;
+    inertia?: number;
+    exposure?: number;
+    pinchPrecision?: number;
+    behaviors?: {
+        [name: string]: boolean | number | ICameraBehaviorConfiguration;
+    };
+    disableCameraControl?: boolean;
+    disableCtrlForPanning?: boolean;
+    disableAutoFocus?: boolean;
+
+    [propName: string]: any;
+}
+
+export interface ICameraBehaviorConfiguration {
+    type: number;
+    [propName: string]: any;
+}

+ 101 - 0
Viewer/src/configuration/interfaces/colorGradingConfiguration.ts

@@ -0,0 +1,101 @@
+/**
+ * The Color Grading Configuration groups the different settings used to define the color grading used in the viewer.
+ */
+export interface IColorGradingConfiguration {
+
+    /**
+     * Transform data string, encoded as determined by transformDataFormat.
+     */
+    transformData: string;
+
+    /**
+     * The encoding format of TransformData (currently only raw-base16 is supported).
+     */
+    transformDataFormat: string;
+
+    /**
+     * The weight of the transform
+     */
+    transformWeight: number;
+
+    /**
+     * Color curve colorFilterHueGlobal value
+     */
+    colorFilterHueGlobal: number;
+
+    /**
+     * Color curve colorFilterHueShadows value
+     */
+    colorFilterHueShadows: number;
+
+    /**
+     * Color curve colorFilterHueMidtones value
+     */
+    colorFilterHueMidtones: number;
+
+    /**
+     * Color curve colorFilterHueHighlights value
+     */
+    colorFilterHueHighlights: number;
+
+    /**
+     * Color curve colorFilterDensityGlobal value
+     */
+    colorFilterDensityGlobal: number;
+
+    /**
+     * Color curve colorFilterDensityShadows value
+     */
+    colorFilterDensityShadows: number;
+
+    /**
+     * Color curve colorFilterDensityMidtones value
+     */
+    colorFilterDensityMidtones: number;
+
+    /**
+     * Color curve colorFilterDensityHighlights value
+     */
+    colorFilterDensityHighlights: number;
+
+    /**
+     * Color curve saturationGlobal value
+     */
+    saturationGlobal: number;
+
+    /**
+     * Color curve saturationShadows value
+     */
+    saturationShadows: number;
+
+    /**
+     * Color curve saturationMidtones value
+     */
+    saturationMidtones: number;
+
+    /**
+     * Color curve saturationHighlights value
+     */
+    saturationHighlights: number;
+
+    /**
+     * Color curve exposureGlobal value
+     */
+    exposureGlobal: number;
+
+    /**
+     * Color curve exposureShadows value
+     */
+    exposureShadows: number;
+
+    /**
+     * Color curve exposureMidtones value
+     */
+    exposureMidtones: number;
+
+    /**
+     * Color curve exposureHighlights value
+     */
+    exposureHighlights: number;
+
+}

+ 20 - 0
Viewer/src/configuration/interfaces/groundConfiguration.ts

@@ -0,0 +1,20 @@
+export interface IGroundConfiguration {
+    size?: number;
+    receiveShadows?: boolean;
+    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?: {
+        [propName: string]: any;
+    };
+}

+ 38 - 0
Viewer/src/configuration/interfaces/imageProcessingConfiguration.ts

@@ -0,0 +1,38 @@
+export interface IImageProcessingConfiguration {
+    colorGradingEnabled?: boolean;
+    colorCurvesEnabled?: boolean;
+    colorCurves?: {
+        globalHue?: number;
+        globalDensity?: number;
+        globalSaturation?: number;
+        globalExposure?: number;
+        highlightsHue?: number;
+        highlightsDensity?: number;
+        highlightsSaturation?: number;
+        highlightsExposure?: number;
+        midtonesHue?: number;
+        midtonesDensity?: number;
+        midtonesSaturation?: number;
+        midtonesExposure?: number;
+        shadowsHue?: number;
+        shadowsDensity?: number;
+        shadowsSaturation?: number;
+        shadowsExposure?: number;
+    };
+    colorGradingWithGreenDepth?: boolean;
+    colorGradingBGR?: boolean;
+    exposure?: number;
+    toneMappingEnabled?: boolean;
+    contrast?: number;
+    vignetteEnabled?: boolean;
+    vignetteStretch?: number;
+    vignetteCentreX?: number;
+    vignetteCentreY?: number;
+    vignetteWeight?: number;
+    vignetteColor?: { r: number, g: number, b: number, a?: number };
+    vignetteCameraFov?: number;
+    vignetteBlendMode?: number;
+    vignetteM?: boolean;
+    applyByPostProcess?: boolean;
+    isEnabled?: boolean;
+}

+ 13 - 0
Viewer/src/configuration/interfaces/index.ts

@@ -0,0 +1,13 @@
+export * from './cameraConfiguration';
+export * from './colorGradingConfiguration';
+export * from './defaultRenderingPipelineConfiguration';
+export * from './groundConfiguration';
+export * from './imageProcessingConfiguration';
+export * from './lightConfiguration';
+export * from './modelAnimationConfiguration';
+export * from './modelConfiguration';
+export * from './observersConfiguration';
+export * from './sceneConfiguration';
+export * from './sceneOptimizerConfiguration';
+export * from './skyboxConfiguration';
+export * from './templateConfiguration';

+ 42 - 0
Viewer/src/configuration/interfaces/lightConfiguration.ts

@@ -0,0 +1,42 @@
+export interface ILightConfiguration {
+    type: number;
+    name?: string;
+    disabled?: boolean;
+    position?: { x: number, y: number, z: number };
+    target?: { x: number, y: number, z: number };
+    direction?: { x: number, y: number, z: number };
+    diffuse?: { r: number, g: number, b: number };
+    specular?: { r: number, g: number, b: number };
+    intensity?: number;
+    intensityMode?: number;
+    radius?: number;
+    shadownEnabled?: boolean; // only on specific lights!
+    shadowConfig?: {
+        useBlurExponentialShadowMap?: boolean;
+        useBlurCloseExponentialShadowMap?: boolean;
+        useKernelBlur?: boolean;
+        blurKernel?: number;
+        blurScale?: number;
+        minZ?: number;
+        maxZ?: number;
+        frustumSize?: number;
+        angleScale?: number;
+        frustumEdgeFalloff?: number;
+        [propName: string]: any;
+    };
+    spotAngle?: number;
+    shadowFieldOfView?: number;
+    shadowBufferSize?: number;
+    shadowFrustumSize?: number;
+    shadowMinZ?: number;
+    shadowMaxZ?: number;
+    [propName: string]: any;
+
+    // no behaviors for light at the moment, but allowing configuration for future reference.
+    behaviors?: {
+        [name: string]: number | {
+            type: number;
+            [propName: string]: any;
+        };
+    };
+}

+ 63 - 0
Viewer/src/configuration/interfaces/modelConfiguration.ts

@@ -0,0 +1,63 @@
+import { IModelAnimationConfiguration } from ".";
+
+export interface IModelConfiguration {
+    id?: string;
+    url?: string;
+    root?: string; //optional
+    file?: string | File; // is a file being loaded? root and url ignored
+    loader?: string; // obj, gltf?
+    position?: { x: number, y: number, z: number };
+    rotation?: { x: number, y: number, z: number, w?: number };
+    scaling?: { x: number, y: number, z: number };
+    parentObjectIndex?: number; // the index of the parent object of the model in the loaded meshes array.
+
+    castShadow?: boolean;
+    receiveShadows?: boolean;
+    normalize?: boolean | {
+        center?: boolean;
+        unitSize?: boolean;
+        parentIndex?: number;
+    }; // should the model be scaled to unit-size
+
+    title?: string;
+    subtitle?: string;
+    thumbnail?: string; // URL or data-url
+
+    animation?: {
+        autoStart?: boolean | string;
+        playOnce?: boolean;
+        autoStartIndex?: number;
+    }
+
+    entryAnimation?: IModelAnimationConfiguration;
+    exitAnimation?: IModelAnimationConfiguration;
+
+    material?: {
+        directEnabled?: boolean;
+        directIntensity?: number;
+        emissiveIntensity?: number;
+        environmentIntensity?: number;
+        [propName: string]: any;
+    }
+
+    /** 
+     * Rotation offset axis definition
+     */
+    rotationOffsetAxis?: {
+        x: number;
+        y: number;
+        z: number;
+    };
+
+    /**
+     * the offset angle
+     */
+    rotationOffsetAngle?: number;
+
+    loaderConfiguration?: {
+        maxLODsToLoad?: number;
+        progressiveLoading?: boolean;
+    }
+
+    // [propName: string]: any; // further configuration, like title and creator
+}

+ 5 - 0
Viewer/src/configuration/interfaces/observersConfiguration.ts

@@ -0,0 +1,5 @@
+export interface IObserversConfiguration {
+    onEngineInit?: string;
+    onSceneInit?: string;
+    onModelLoaded?: string;
+}

+ 39 - 0
Viewer/src/configuration/interfaces/sceneConfiguration.ts

@@ -0,0 +1,39 @@
+import { IImageProcessingConfiguration, IColorGradingConfiguration } from ".";
+import { IGlowLayerOptions } from "babylonjs";
+
+export interface ISceneConfiguration {
+    debug?: boolean;
+    clearColor?: { r: number, g: number, b: number, a: number };
+    mainColor?: { r?: number, g?: number, b?: number };
+    imageProcessingConfiguration?: IImageProcessingConfiguration;
+    environmentTexture?: string;
+    colorGrading?: IColorGradingConfiguration;
+    environmentRotationY?: number;
+    /**
+     * Deprecated, please use default rendering pipeline
+     */
+    glow?: boolean | IGlowLayerOptions;
+    disableHdr?: boolean;
+    renderInBackground?: boolean;
+    disableCameraControl?: boolean;
+    animationPropertiesOverride?: {
+        [propName: string]: any;
+    };
+    defaultMaterial?: {
+        materialType: "standard" | "pbr";
+        [propName: string]: any;
+    };
+    flags?: {
+        shadowsEnabled?: boolean;
+        particlesEnabled?: boolean;
+        collisionsEnabled?: boolean;
+        lightsEnabled?: boolean;
+        texturesEnabled?: boolean;
+        lensFlaresEnabled?: boolean;
+        proceduralTexturesEnabled?: boolean;
+        renderTargetsEnabled?: boolean;
+        spritesEnabled?: boolean;
+        skeletonsEnabled?: boolean;
+        audioEnabled?: boolean;
+    }
+}

+ 24 - 0
Viewer/src/configuration/interfaces/sceneOptimizerConfiguration.ts

@@ -0,0 +1,24 @@
+export interface ISceneOptimizerConfiguration {
+    targetFrameRate?: number;
+    trackerDuration?: number;
+    autoGeneratePriorities?: boolean;
+    improvementMode?: boolean;
+    degradation?: string; // low, moderate, high
+    types?: {
+        texture?: ISceneOptimizerParameters;
+        hardwareScaling?: ISceneOptimizerParameters;
+        shadow?: ISceneOptimizerParameters;
+        postProcess?: ISceneOptimizerParameters;
+        lensFlare?: ISceneOptimizerParameters;
+        particles?: ISceneOptimizerParameters;
+        renderTarget?: ISceneOptimizerParameters;
+        mergeMeshes?: ISceneOptimizerParameters;
+    }
+    custom?: string;
+}
+
+export interface ISceneOptimizerParameters {
+    priority?: number;
+    maximumSize?: number;
+    step?: number;
+}

+ 19 - 0
Viewer/src/configuration/interfaces/skyboxConfiguration.ts

@@ -0,0 +1,19 @@
+import { IImageProcessingConfiguration } from ".";
+
+export interface ISkyboxConfiguration {
+    cubeTexture?: {
+        noMipMap?: boolean;
+        gammaSpace?: boolean;
+        url?: string | Array<string>;
+    };
+    color?: { r: number, g: number, b: number };
+    pbr?: boolean; // deprecated
+    scale?: number;
+    blur?: number; // deprecated
+    material?: {
+        imageProcessingConfiguration?: IImageProcessingConfiguration;
+        [propName: string]: any;
+    };
+    infiniteDistance?: boolean;
+
+}

+ 1 - 1
Viewer/src/configuration/loader.ts

@@ -3,7 +3,7 @@ import { ViewerConfiguration } from './configuration';
 import { getConfigurationType } from './types';
 import { processConfigurationCompatibility } from './configurationCompatibility';
 
-import * as deepmerge from '../../assets/deepmerge.min.js';
+import { deepmerge } from '../helper/';
 import { Tools, IFileRequest } from 'babylonjs';
 
 /**

+ 1 - 1
Viewer/src/configuration/mappers.ts

@@ -1,7 +1,7 @@
 import { Tools } from 'babylonjs';
 import { ViewerConfiguration } from './configuration';
 
-import { kebabToCamel } from '../helper';
+import { kebabToCamel } from '../helper/';
 
 /**
  * This is the mapper's interface. Implement this function to create your own mapper and register it at the mapper manager

+ 1 - 1
Viewer/src/configuration/types/index.ts

@@ -4,7 +4,7 @@ import { extendedConfiguration } from './extended';
 import { ViewerConfiguration } from '../configuration';
 import { shadowDirectionalLightConfiguration, shadowSpotlLightConfiguration } from './shadowLight';
 import { environmentMapConfiguration } from './environmentMap';
-import * as deepmerge from '../../../assets/deepmerge.min.js';
+import { deepmerge } from '../../helper/';
 
 /**
  * Get the configuration type you need to use as the base for your viewer.

+ 0 - 0
Viewer/src/helper/deepmerge.ts


+ 4 - 0
Viewer/src/helper/index.ts

@@ -1,3 +1,7 @@
+import * as deepmerge from '../../assets/deepmerge.min.js';
+
+export { deepmerge };
+
 /**
  * Is the provided string a URL?
  * 

+ 12 - 14
Viewer/src/labs/viewerLabs.ts

@@ -1,8 +1,6 @@
 import { PBREnvironment, EnvironmentDeserializer } from "./environmentSerializer";
-import { SceneManager } from '../viewer/sceneManager';
 
-import { Tools, Quaternion, ShadowLight, Vector3, Axis, Matrix, SphericalPolynomial, Tmp } from 'babylonjs';
-import { ViewerConfiguration } from "../configuration/configuration";
+import { Tools, Quaternion, ShadowLight, Vector3, Axis, Matrix, SphericalPolynomial, Tmp, Scene } from 'babylonjs';
 import { TextureUtils } from "./texture";
 
 /**
@@ -12,7 +10,7 @@ import { TextureUtils } from "./texture";
  */
 export class ViewerLabs {
 
-    constructor(private _sceneManager: SceneManager) { }
+    constructor(private _scene: Scene) { }
 
     public assetsRootURL: string;
     public environment: PBREnvironment = {
@@ -63,7 +61,7 @@ export class ViewerLabs {
             if (onSuccess) onSuccess(this.environment);
         } else if (typeof data === 'string') {
             let url = this.getAssetUrl(data);
-            this._sceneManager.scene._loadFile(
+            this._scene._loadFile(
                 url,
                 (arrayBuffer: ArrayBuffer) => {
                     this.environment = EnvironmentDeserializer.Parse(arrayBuffer);
@@ -98,15 +96,15 @@ export class ViewerLabs {
         // Add env texture to the scene.
         if (this.environment.specularTexture) {
             // IE crashes when disposing the old texture and setting a new one
-            if (!this._sceneManager.scene.environmentTexture) {
-                this._sceneManager.scene.environmentTexture = TextureUtils.GetBabylonCubeTexture(this._sceneManager.scene, this.environment.specularTexture, false, true);
+            if (!this._scene.environmentTexture) {
+                this._scene.environmentTexture = TextureUtils.GetBabylonCubeTexture(this._scene, this.environment.specularTexture, false, true);
             }
-            if (this._sceneManager.scene.environmentTexture) {
-                this._sceneManager.scene.environmentTexture.level = this.environment.textureIntensityScale;
-                this._sceneManager.scene.environmentTexture.invertZ = true;
-                this._sceneManager.scene.environmentTexture.lodLevelInAlpha = true;
+            if (this._scene.environmentTexture) {
+                this._scene.environmentTexture.level = this.environment.textureIntensityScale;
+                this._scene.environmentTexture.invertZ = true;
+                this._scene.environmentTexture.lodLevelInAlpha = true;
 
-                var poly = this._sceneManager.scene.environmentTexture.sphericalPolynomial || new SphericalPolynomial();
+                var poly = this._scene.environmentTexture.sphericalPolynomial || new SphericalPolynomial();
                 poly.x = this.environment.irradiancePolynomialCoefficients.x;
                 poly.y = this.environment.irradiancePolynomialCoefficients.y;
                 poly.z = this.environment.irradiancePolynomialCoefficients.z;
@@ -116,10 +114,10 @@ export class ViewerLabs {
                 poly.yz = this.environment.irradiancePolynomialCoefficients.yz;
                 poly.zx = this.environment.irradiancePolynomialCoefficients.zx;
                 poly.zz = this.environment.irradiancePolynomialCoefficients.zz;
-                this._sceneManager.scene.environmentTexture.sphericalPolynomial = poly;
+                this._scene.environmentTexture.sphericalPolynomial = poly;
 
                 //set orientation
-                Matrix.FromQuaternionToRef(rotatquatRotationionY, this._sceneManager.scene.environmentTexture.getReflectionTextureMatrix());
+                Matrix.FromQuaternionToRef(rotatquatRotationionY, this._scene.environmentTexture.getReflectionTextureMatrix());
             }
         }
     }

+ 1 - 1
Viewer/src/loader/modelLoader.ts

@@ -1,7 +1,7 @@
 import { AbstractViewer } from "../viewer/viewer";
 import { ISceneLoaderPlugin, ISceneLoaderPluginAsync, Tools, SceneLoader, Tags } from "babylonjs";
 import { GLTFFileLoader, GLTFLoaderAnimationStartMode } from "babylonjs-loaders";
-import { IModelConfiguration } from "../configuration/configuration";
+import { IModelConfiguration } from "../configuration/interfaces";
 import { ViewerModel, ModelState } from "../model/viewerModel";
 import { ILoaderPlugin } from './plugins/loaderPlugin';
 import { TelemetryLoaderPlugin } from './plugins/telemetryLoaderPlugin';

+ 11 - 0
Viewer/src/loader/plugins/index.ts

@@ -3,6 +3,7 @@ import { ILoaderPlugin } from "./loaderPlugin";
 import { MSFTLodLoaderPlugin } from './msftLodLoaderPlugin';
 import { ApplyMaterialConfigPlugin } from './applyMaterialConfig';
 import { ExtendedMaterialLoaderPlugin } from './extendedMaterialLoaderPlugin';
+import { Tools } from "babylonjs";
 
 const pluginCache: { [key: string]: ILoaderPlugin } = {};
 
@@ -31,4 +32,14 @@ export function getLoaderPluginByName(name: string) {
     }
 
     return pluginCache[name];
+}
+
+/**
+ * 
+ */
+export function addLoaderPlugin(name: string, plugin: ILoaderPlugin) {
+    if (pluginCache[name]) {
+        Tools.Warn("Overwriting plugin with the same name - " + name);
+    }
+    pluginCache[name] = plugin;
 }

+ 77 - 0
Viewer/src/managers/observablesManager.ts

@@ -0,0 +1,77 @@
+import { Observable, Scene, Engine, SceneLoaderProgressEvent, ISceneLoaderPlugin, ISceneLoaderPluginAsync } from "babylonjs";
+import { ViewerModel } from "../model/viewerModel";
+
+export class ObservablesManager {
+
+    /**
+     * Will notify when the scene was initialized
+     */
+    public onSceneInitObservable: Observable<Scene>;
+    /**
+     * will notify when the engine was initialized
+     */
+    public onEngineInitObservable: Observable<Engine>;
+
+    /**
+     * Will notify when a new model was added to the scene.
+     * Note that added does not neccessarily mean loaded!
+     */
+    public onModelAddedObservable: Observable<ViewerModel>;
+    /**
+     * will notify after every model load
+     */
+    public onModelLoadedObservable: Observable<ViewerModel>;
+    /**
+     * will notify when any model notify of progress
+     */
+    public onModelLoadProgressObservable: Observable<SceneLoaderProgressEvent>;
+    /**
+     * will notify when any model load failed.
+     */
+    public onModelLoadErrorObservable: Observable<{ message: string; exception: any }>;
+    /**
+     * Will notify when a model was removed from the scene;
+     */
+    public onModelRemovedObservable: Observable<ViewerModel>;
+    /**
+     * will notify when a new loader was initialized.
+     * Used mainly to know when a model starts loading.
+     */
+    public onLoaderInitObservable: Observable<ISceneLoaderPlugin | ISceneLoaderPluginAsync>;
+    /**
+     * Observers registered here will be executed when the entire load process has finished.
+     */
+    public onViewerInitDoneObservable: Observable<any>;
+
+    /**
+     * Functions added to this observable will be executed on each frame rendered.
+     */
+    public onFrameRenderedObservable: Observable<any>;
+
+    constructor() {
+        this.onSceneInitObservable = new Observable();
+        this.onEngineInitObservable = new Observable();
+        this.onModelLoadedObservable = new Observable();
+        this.onModelLoadProgressObservable = new Observable();
+        this.onModelLoadErrorObservable = new Observable();
+        this.onModelAddedObservable = new Observable();
+        this.onModelRemovedObservable = new Observable();
+        this.onViewerInitDoneObservable = new Observable();
+        this.onLoaderInitObservable = new Observable();
+        this.onFrameRenderedObservable = new Observable();
+    }
+
+    dispose() {
+        this.onSceneInitObservable.clear();
+        this.onEngineInitObservable.clear();
+        this.onModelLoadedObservable.clear();
+        this.onModelLoadProgressObservable.clear();
+        this.onModelLoadErrorObservable.clear();
+        this.onModelAddedObservable.clear();
+        this.onModelRemovedObservable.clear();
+        this.onViewerInitDoneObservable.clear();
+        this.onLoaderInitObservable.clear();
+        this.onFrameRenderedObservable.clear();
+    }
+
+}

+ 3 - 3
Viewer/src/model/viewerModel.ts

@@ -1,11 +1,11 @@
 import { ISceneLoaderPlugin, ISceneLoaderPluginAsync, AnimationGroup, Animatable, AbstractMesh, Tools, Scene, SceneLoader, Observable, SceneLoaderProgressEvent, Tags, ParticleSystem, Skeleton, IDisposable, Nullable, Animation, Quaternion, Material, Vector3, AnimationPropertiesOverride, QuinticEase, SineEase, CircleEase, BackEase, BounceEase, CubicEase, ElasticEase, ExponentialEase, PowerEase, QuadraticEase, QuarticEase, PBRMaterial, MultiMaterial } from "babylonjs";
 import { GLTFFileLoader, GLTF2 } from "babylonjs-loaders";
-import { IModelConfiguration, IModelAnimationConfiguration } from "../configuration/configuration";
+import { IModelConfiguration, IModelAnimationConfiguration } from "../configuration/interfaces";
 import { IModelAnimation, GroupModelAnimation, AnimationPlayMode, ModelAnimationConfiguration, EasingFunction, AnimationState } from "./modelAnimation";
 
-import * as deepmerge from '../../assets/deepmerge.min.js';
+import { deepmerge } from '../helper/';
 import { AbstractViewer } from "..";
-import { extendClassWithConfig } from "../helper";
+import { extendClassWithConfig } from "../helper/";
 
 
 /**

+ 42 - 41
Viewer/src/optimizer/custom/extended.ts

@@ -1,52 +1,53 @@
 import { AbstractViewer } from '../../viewer/viewer';
-import { Scalar, DefaultRenderingPipeline } from 'babylonjs';
+import { Scalar, DefaultRenderingPipeline, Scene } from 'babylonjs';
+import { SceneManager } from 'viewer/sceneManager';
 
 /**
  * A custom upgrade-oriented function configuration for the scene optimizer.
  * 
  * @param viewer the viewer to optimize
  */
-export function extendedUpgrade(viewer: AbstractViewer): boolean {
-    let defaultPipeline = <DefaultRenderingPipeline>viewer.sceneManager.defaultRenderingPipeline;
+export function extendedUpgrade(sceneManager: SceneManager): boolean {
+    let defaultPipeline = <DefaultRenderingPipeline>sceneManager.defaultRenderingPipeline;
     // if (!this.Scene.BackgroundHelper) {
     // 	this.Scene.EngineScene.autoClear = false;
     // this.Scene.BackgroundHelper = true;
     // Would require a dedicated clear color;
     // return false;
     // }
-    if (viewer.engine.getHardwareScalingLevel() > 1) {
-        let scaling = Scalar.Clamp(viewer.engine.getHardwareScalingLevel() - 0.25, 0, 1);
-        viewer.engine.setHardwareScalingLevel(scaling);
+    if (sceneManager.scene.getEngine().getHardwareScalingLevel() > 1) {
+        let scaling = Scalar.Clamp(sceneManager.scene.getEngine().getHardwareScalingLevel() - 0.25, 0, 1);
+        sceneManager.scene.getEngine().setHardwareScalingLevel(scaling);
         return false;
     }
-    if (!viewer.sceneManager.scene.postProcessesEnabled) {
-        viewer.sceneManager.scene.postProcessesEnabled = true;
+    if (!sceneManager.scene.postProcessesEnabled) {
+        sceneManager.scene.postProcessesEnabled = true;
         return false;
     }
-    if (!viewer.sceneManager.groundEnabled) {
-        viewer.sceneManager.groundEnabled = true;
+    if (!sceneManager.groundEnabled) {
+        sceneManager.groundEnabled = true;
         return false;
     }
-    if (defaultPipeline && !viewer.sceneManager.fxaaEnabled) {
-        viewer.sceneManager.fxaaEnabled = true
+    if (defaultPipeline && !sceneManager.fxaaEnabled) {
+        sceneManager.fxaaEnabled = true
         return false;
     }
     var hardwareScalingLevel = Math.max(1 / 2, 1 / (window.devicePixelRatio || 2));
-    if (viewer.engine.getHardwareScalingLevel() > hardwareScalingLevel) {
-        let scaling = Scalar.Clamp(viewer.engine.getHardwareScalingLevel() - 0.25, 0, hardwareScalingLevel);
-        viewer.engine.setHardwareScalingLevel(scaling);
+    if (sceneManager.scene.getEngine().getHardwareScalingLevel() > hardwareScalingLevel) {
+        let scaling = Scalar.Clamp(sceneManager.scene.getEngine().getHardwareScalingLevel() - 0.25, 0, hardwareScalingLevel);
+        sceneManager.scene.getEngine().setHardwareScalingLevel(scaling);
         return false;
     }
-    if (!viewer.sceneManager.processShadows) {
-        viewer.sceneManager.processShadows = true;
+    if (!sceneManager.processShadows) {
+        sceneManager.processShadows = true;
         return false;
     }
-    if (defaultPipeline && !viewer.sceneManager.bloomEnabled) {
-        viewer.sceneManager.bloomEnabled = true
+    if (defaultPipeline && !sceneManager.bloomEnabled) {
+        sceneManager.bloomEnabled = true
         return false;
     }
-    if (!viewer.sceneManager.groundMirrorEnabled) {
-        viewer.sceneManager.groundMirrorEnabled = true;
+    if (!sceneManager.groundMirrorEnabled) {
+        sceneManager.groundMirrorEnabled = true;
         return false;
     }
     return true;
@@ -57,41 +58,41 @@ export function extendedUpgrade(viewer: AbstractViewer): boolean {
  * 
  * @param viewer the viewer to optimize
  */
-export function extendedDegrade(viewer: AbstractViewer): boolean {
-    let defaultPipeline = <DefaultRenderingPipeline>viewer.sceneManager.defaultRenderingPipeline;
+export function extendedDegrade(sceneManager: SceneManager): boolean {
+    let defaultPipeline = <DefaultRenderingPipeline>sceneManager.defaultRenderingPipeline;
 
-    if (viewer.sceneManager.groundMirrorEnabled) {
-        viewer.sceneManager.groundMirrorEnabled = false;
+    if (sceneManager.groundMirrorEnabled) {
+        sceneManager.groundMirrorEnabled = false;
         return false;
     }
-    if (defaultPipeline && viewer.sceneManager.bloomEnabled) {
-        viewer.sceneManager.bloomEnabled = false;
+    if (defaultPipeline && sceneManager.bloomEnabled) {
+        sceneManager.bloomEnabled = false;
         return false;
     }
-    if (viewer.sceneManager.processShadows) {
-        viewer.sceneManager.processShadows = false;
+    if (sceneManager.processShadows) {
+        sceneManager.processShadows = false;
         return false;
     }
-    if (viewer.engine.getHardwareScalingLevel() < 1) {
-        let scaling = Scalar.Clamp(viewer.engine.getHardwareScalingLevel() + 0.25, 0, 1);
-        viewer.engine.setHardwareScalingLevel(scaling);
+    if (sceneManager.scene.getEngine().getHardwareScalingLevel() < 1) {
+        let scaling = Scalar.Clamp(sceneManager.scene.getEngine().getHardwareScalingLevel() + 0.25, 0, 1);
+        sceneManager.scene.getEngine().setHardwareScalingLevel(scaling);
         return false;
     }
-    if (defaultPipeline && viewer.sceneManager.fxaaEnabled) {
-        viewer.sceneManager.fxaaEnabled = false;
+    if (defaultPipeline && sceneManager.fxaaEnabled) {
+        sceneManager.fxaaEnabled = false;
         return false;
     }
-    if (viewer.sceneManager.groundEnabled) {
-        viewer.sceneManager.groundEnabled = false;
+    if (sceneManager.groundEnabled) {
+        sceneManager.groundEnabled = false;
         return false;
     }
-    if (viewer.sceneManager.scene.postProcessesEnabled) {
-        viewer.sceneManager.scene.postProcessesEnabled = false;
+    if (sceneManager.scene.postProcessesEnabled) {
+        sceneManager.scene.postProcessesEnabled = false;
         return false;
     }
-    if (viewer.engine.getHardwareScalingLevel() < 1.25) {
-        let scaling = Scalar.Clamp(viewer.engine.getHardwareScalingLevel() + 0.25, 0, 1.25);
-        viewer.engine.setHardwareScalingLevel(scaling);
+    if (sceneManager.scene.getEngine().getHardwareScalingLevel() < 1.25) {
+        let scaling = Scalar.Clamp(sceneManager.scene.getEngine().getHardwareScalingLevel() + 0.25, 0, 1.25);
+        sceneManager.scene.getEngine().setHardwareScalingLevel(scaling);
         return false;
     }
     // if (this.Scene.BackgroundHelper) {

+ 2 - 2
Viewer/src/optimizer/custom/index.ts

@@ -1,7 +1,7 @@
-import { AbstractViewer } from "../../viewer/viewer";
 import { extendedUpgrade, extendedDegrade } from "./extended";
+import { SceneManager } from "viewer/sceneManager";
 
-const cache: { [key: string]: (viewer: AbstractViewer) => boolean } = {};
+const cache: { [key: string]: (sceneManager: SceneManager) => boolean } = {};
 
 /**
  * 

+ 3 - 2
Viewer/src/templating/templateManager.ts

@@ -1,10 +1,11 @@
 
 import { Observable, IFileRequest, Tools } from 'babylonjs';
 import { isUrl, camelToKebab, kebabToCamel } from '../helper';
-import * as deepmerge from 'deepmerge';
 
-import * as Handlebars from 'handlebars';
+import * as Handlebars from '../../assets/handlebars.min.js';
 import { EventManager } from './eventManager';
+import { ITemplateConfiguration } from '../configuration/interfaces';
+import { deepmerge } from '../helper/';
 
 /**
  * The object sent when an event is triggered

+ 35 - 5
Viewer/src/viewer/defaultViewer.ts

@@ -1,7 +1,7 @@
 
 
-import { ViewerConfiguration, IModelConfiguration, ILightConfiguration } from './../configuration/configuration';
-import { Template, EventCallback } from './../templateManager';
+import { ViewerConfiguration, IModelConfiguration, ILightConfiguration } from './../configuration';
+import { Template, EventCallback, TemplateManager } from '../templating/templateManager';
 import { AbstractViewer } from './viewer';
 import { SpotLight, MirrorTexture, Plane, ShadowGenerator, Texture, BackgroundMaterial, Observable, ShadowLight, CubeTexture, BouncingBehavior, FramingBehavior, Behavior, Light, Engine, Scene, AutoRotationBehavior, AbstractMesh, Quaternion, StandardMaterial, ArcRotateCamera, ImageProcessingConfiguration, Color3, Vector3, SceneLoader, Mesh, HemisphericLight, FilesInput } from 'babylonjs';
 import { CameraBehavior } from '../interfaces';
@@ -16,17 +16,25 @@ import { IModelAnimation, AnimationState } from '../model/modelAnimation';
 export class DefaultViewer extends AbstractViewer {
 
     /**
+     * The corresponsing template manager of this viewer.
+     */
+    public templateManager: TemplateManager;
+
+    /**
      * Create a new default viewer
      * @param containerElement the element in which the templates will be rendered
      * @param initialConfiguration the initial configuration. Defaults to extending the default configuration
      */
     constructor(public containerElement: HTMLElement, initialConfiguration: ViewerConfiguration = { extends: 'default' }) {
         super(containerElement, initialConfiguration);
+
         this.onModelLoadedObservable.add(this._onModelLoaded);
 
-        this.sceneManager.onLightsConfiguredObservable.add((data) => {
-            this._configureLights(data.newConfiguration, data.model!);
-        })
+        this.onEngineInitObservable.add(() => {
+            this.sceneManager.onLightsConfiguredObservable.add((data) => {
+                this._configureLights(data.newConfiguration, data.model!);
+            })
+        });
     }
 
     /**
@@ -476,6 +484,28 @@ export class DefaultViewer extends AbstractViewer {
         }));
     }
 
+    public dispose() {
+        this.templateManager.dispose();
+        super.dispose();
+    }
+
+    protected _onConfigurationLoaded(configuration: ViewerConfiguration) {
+        super._onConfigurationLoaded(configuration);
+
+        // initialize the templates
+        let templateConfiguration = this._configuration.templates || {};
+        this.templateManager = new TemplateManager(this.containerElement);
+        this.templateManager.initTemplate(templateConfiguration);
+        // when done, execute onTemplatesLoaded()
+        this.templateManager.onAllLoaded.add(() => {
+            let canvas = this.templateManager.getCanvas();
+            if (canvas) {
+                this._canvas = canvas;
+            }
+            this._onTemplateLoaded();
+        });
+    }
+
     /**
      * An extension of the light configuration of the abstract viewer.
      * @param lightsConfiguration the light configuration to use

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 42 - 34
Viewer/src/viewer/sceneManager.ts


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 62 - 76
Viewer/src/viewer/viewer.ts