Quellcode durchsuchen

Merge https://github.com/BabylonJS/Babylon.js into oculusGoSupport

Trevor Baron vor 7 Jahren
Ursprung
Commit
780865993d
100 geänderte Dateien mit 15490 neuen und 13655 gelöschten Zeilen
  1. 1 0
      CNAME
  2. 12436 12181
      Playground/babylon.d.txt
  3. 5 0
      Playground/frame.css
  4. 4 0
      Playground/frame.html
  5. 1 0
      Playground/full.html
  6. 3 3
      Playground/index.html
  7. 2 1
      Playground/indexStable.html
  8. 1 1
      Playground/js/index.js
  9. 2 0
      Playground/js/libs/dat.gui.min.js
  10. 5 0
      Playground/js/libs/jquery.min.js
  11. 206 0
      Playground/js/libs/pep.min.js
  12. BIN
      Playground/scenes/CompileMaterialsTest/BaseColor.png
  13. BIN
      Playground/scenes/CompileMaterialsTest/Test.bin
  14. 0 127
      Playground/scenes/CompileMaterialsTest/Test.gltf
  15. BIN
      Playground/scenes/MultiPrimitive/MultiPrimitive.bin
  16. 0 134
      Playground/scenes/MultiPrimitive/MultiPrimitive.gltf
  17. BIN
      Playground/scenes/TwoQuads/LOD0.png
  18. BIN
      Playground/scenes/TwoQuads/LOD1.png
  19. BIN
      Playground/scenes/TwoQuads/LOD2.png
  20. BIN
      Playground/scenes/TwoQuads/TwoQuads.bin
  21. 0 162
      Playground/scenes/TwoQuads/TwoQuads.gltf
  22. BIN
      Playground/textures/Flare2.png
  23. BIN
      Playground/textures/Space/space_back.jpg
  24. BIN
      Playground/textures/Space/space_down.jpg
  25. BIN
      Playground/textures/Space/space_front.jpg
  26. BIN
      Playground/textures/Space/space_left.jpg
  27. BIN
      Playground/textures/Space/space_right.jpg
  28. BIN
      Playground/textures/Space/space_up.jpg
  29. BIN
      Playground/textures/flare3.png
  30. BIN
      Playground/textures/wood.jpg
  31. 5 3
      Tools/Gulp/config.json
  32. 1 0
      Tools/Gulp/package.json
  33. BIN
      Viewer/assets/babylon.woff
  34. BIN
      Viewer/assets/img/BabylonJS_Logo_Small.png
  35. 27 3
      Viewer/assets/templates/default/defaultTemplate.html
  36. 1 2
      Viewer/assets/templates/default/defaultViewer.html
  37. 41 0
      Viewer/assets/templates/default/fillContainer.html
  38. 3 3
      Viewer/assets/templates/default/loadingScreen.html
  39. 442 67
      Viewer/assets/templates/default/navbar.html
  40. 2 0
      Viewer/dist/_headers
  41. BIN
      Viewer/dist/assets/environment/Skybox_2.0-256.dds
  42. BIN
      Viewer/dist/external/msft/EnvMap_2.0-256.env
  43. BIN
      Viewer/dist/external/msft/Ground_2.0-1024.png
  44. BIN
      Viewer/dist/external/msft/Skybox_3.0-256.dds
  45. 42 0
      Viewer/dist/external/msft/codeExample.html
  46. 25 0
      Viewer/dist/external/msft/config.json
  47. 19 0
      Viewer/dist/external/msft/htmlExample.html
  48. 4 11
      Viewer/dist/ufoExample.html
  49. 1 0
      Viewer/package.json
  50. 77 19
      Viewer/src/configuration/configuration.ts
  51. 1 0
      Viewer/src/configuration/index.ts
  52. 43 52
      Viewer/src/configuration/types/default.ts
  53. 4 1
      Viewer/src/configuration/types/environmentMap.ts
  54. 265 40
      Viewer/src/configuration/types/extended.ts
  55. 8 0
      Viewer/src/configuration/types/index.ts
  56. 5 1
      Viewer/src/configuration/types/minimal.ts
  57. 9 3
      Viewer/src/configuration/types/shadowLight.ts
  58. 3 2
      Viewer/src/eventManager.ts
  59. 4 0
      Viewer/src/index.ts
  60. 3 3
      Viewer/src/labs/environmentSerializer.ts
  61. 11 6
      Viewer/src/labs/viewerLabs.ts
  62. 33 4
      Viewer/src/loader/modelLoader.ts
  63. 21 0
      Viewer/src/loader/plugins/applyMaterialConfig.ts
  64. 4 2
      Viewer/src/loader/plugins/extendedMaterialLoaderPlugin.ts
  65. 8 2
      Viewer/src/loader/plugins/index.ts
  66. 5 0
      Viewer/src/loader/plugins/loaderPlugin.ts
  67. 0 39
      Viewer/src/loader/plugins/minecraftLoaderPlugin.ts
  68. 3 1
      Viewer/src/loader/plugins/msftLodLoaderPlugin.ts
  69. 1 1
      Viewer/src/loader/plugins/telemetryLoaderPlugin.ts
  70. 58 26
      Viewer/src/model/modelAnimation.ts
  71. 741 466
      Viewer/src/model/viewerModel.ts
  72. 104 0
      Viewer/src/optimizer/custom/extended.ts
  73. 25 0
      Viewer/src/optimizer/custom/index.ts
  74. 3 0
      Viewer/src/telemetryManager.ts
  75. 60 3
      Viewer/src/templateManager.ts
  76. 211 69
      Viewer/src/viewer/defaultViewer.ts
  77. 409 151
      Viewer/src/viewer/sceneManager.ts
  78. 37 30
      Viewer/src/viewer/viewer.ts
  79. 50 35
      Viewer/tests/unit/src/viewer/viewer.ts
  80. 5 1
      Viewer/tests/unit/webpack.config.js
  81. BIN
      Viewer/tests/validation/ReferenceImages/BrainStem.png
  82. BIN
      Viewer/tests/validation/ReferenceImages/BrainStemEnv.png
  83. BIN
      Viewer/tests/validation/ReferenceImages/BrainStemTransformation.png
  84. BIN
      Viewer/tests/validation/ReferenceImages/CameraContrast0.png
  85. BIN
      Viewer/tests/validation/ReferenceImages/CameraContrast1.png
  86. BIN
      Viewer/tests/validation/ReferenceImages/CameraExposure0.png
  87. BIN
      Viewer/tests/validation/ReferenceImages/CameraExposure1.png
  88. BIN
      Viewer/tests/validation/ReferenceImages/Control.png
  89. BIN
      Viewer/tests/validation/ReferenceImages/ControlDefault.png
  90. BIN
      Viewer/tests/validation/ReferenceImages/Diffuse.png
  91. BIN
      Viewer/tests/validation/ReferenceImages/Emissive.png
  92. BIN
      Viewer/tests/validation/ReferenceImages/MainColorEnv0-0.png
  93. BIN
      Viewer/tests/validation/ReferenceImages/MainColorEnv0-100.png
  94. BIN
      Viewer/tests/validation/ReferenceImages/MainColorEnv0-50.png
  95. BIN
      Viewer/tests/validation/ReferenceImages/MainColorEnv100-0.png
  96. BIN
      Viewer/tests/validation/ReferenceImages/MainColorEnv100-100.png
  97. BIN
      Viewer/tests/validation/ReferenceImages/MainColorEnv100-50.png
  98. BIN
      Viewer/tests/validation/ReferenceImages/MainColorEnv50-0.png
  99. BIN
      Viewer/tests/validation/ReferenceImages/MainColorEnv50-100.png
  100. 0 0
      Viewer/tests/validation/ReferenceImages/MainColorEnv50-50.png

+ 1 - 0
CNAME

@@ -0,0 +1 @@
+babylonjs.com

Datei-Diff unterdrückt, da er zu groß ist
+ 12436 - 12181
Playground/babylon.d.txt


+ 5 - 0
Playground/frame.css

@@ -8,6 +8,11 @@
     touch-action: none;
 }
 
+canvas {
+    border:none !important;
+    outline:none !important;
+}
+
 #fpsLabel {
     position: absolute;
     right: 10px;

+ 4 - 0
Playground/frame.html

@@ -22,11 +22,15 @@
     <meta name="msapplication-TileImage" content="https://www.babylonjs.com/img/favicon/ms-icon-144x144.png">
     <meta name="msapplication-config" content="https://www.babylonjs.com/img/favicon/browserconfig.xml">
     <meta name="theme-color" content="#ffffff">
+    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
 
     <script src="https://code.jquery.com/pep/0.4.2/pep.min.js"></script>
+    
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.2/dat.gui.min.js"></script>
     <!-- Babylon.js -->
     <script src="https://preview.babylonjs.com/cannon.js"></script>
     <script src="https://preview.babylonjs.com/Oimo.js"></script>
+    <script src="https://preview.babylonjs.com/earcut.min.js"></script>
     <script src="https://preview.babylonjs.com/babylon.js"></script>
     <script src="https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>
 

+ 1 - 0
Playground/full.html

@@ -22,6 +22,7 @@
         <meta name="msapplication-TileImage" content="https://www.babylonjs.com/img/favicon/ms-icon-144x144.png">
         <meta name="msapplication-config" content="https://www.babylonjs.com/img/favicon/browserconfig.xml">
         <meta name="theme-color" content="#ffffff">
+        <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
 
         <script src="https://code.jquery.com/pep/0.4.2/pep.min.js"></script>
         <!-- Babylon.js -->

+ 3 - 3
Playground/index.html

@@ -25,11 +25,11 @@
         <meta name="msapplication-config" content="https://www.babylonjs.com/img/favicon/browserconfig.xml">
         <meta name="theme-color" content="#ffffff">
 
-        <script src="https://code.jquery.com/pep/0.4.2/pep.min.js"></script>
+        <script src="js/libs/pep.min.js"></script>
         <!--For canvas/code separator-->
         <script src="js/libs/split.js"></script>
 
-        <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.2/dat.gui.min.js"></script>
+        <script src="js/libs/dat.gui.min.js"></script>
         <!-- jszip -->
         <script src="js/libs/jszip.min.js"></script>
         <script src="js/libs/fileSaver.js"></script>
@@ -484,7 +484,7 @@
             <img src="waitlogo.png" id="waitLogo" />
         </div>
 
-        <script src="https://code.jquery.com/jquery.js"></script>
+        <script src="js/libs/jquery.min.js"></script>
 
         <script src="js/actions.js"></script>
         <script src="js/pbt.js"></script>

+ 2 - 1
Playground/indexStable.html

@@ -35,7 +35,8 @@
         <script src="js/libs/fileSaver.js"></script>
         <!-- Physics -->
         <script src="https://cdn.babylonjs.com/cannon.js"></script>
-        <script src="https://cdn.babylonjs.com/Oimo.js"></script>
+        <script src="https://cdn.babylonjs.com/Oimo.js"></script>        
+        <script src="https://preview.babylonjs.com/earcut.min.js"></script>
         <!-- Monaco -->
         <script src="node_modules/monaco-editor/min/vs/loader.js"></script>
         <!-- Babylon.js -->

Datei-Diff unterdrückt, da er zu groß ist
+ 1 - 1
Playground/js/index.js


Datei-Diff unterdrückt, da er zu groß ist
+ 2 - 0
Playground/js/libs/dat.gui.min.js


Datei-Diff unterdrückt, da er zu groß ist
+ 5 - 0
Playground/js/libs/jquery.min.js


Datei-Diff unterdrückt, da er zu groß ist
+ 206 - 0
Playground/js/libs/pep.min.js


BIN
Playground/scenes/CompileMaterialsTest/BaseColor.png


BIN
Playground/scenes/CompileMaterialsTest/Test.bin


+ 0 - 127
Playground/scenes/CompileMaterialsTest/Test.gltf

@@ -1,127 +0,0 @@
-{
-  "accessors": [
-    {
-      "bufferView": 0,
-      "componentType": 5126,
-      "count": 4,
-      "type": "VEC3",
-      "max": [
-        0.5,
-        0.5,
-        0.0
-      ],
-      "min": [
-        -0.5,
-        -0.5,
-        0.0
-      ]
-    },
-    {
-      "bufferView": 1,
-      "componentType": 5126,
-      "count": 4,
-      "type": "VEC4"
-    },
-    {
-      "bufferView": 2,
-      "componentType": 5126,
-      "count": 4,
-      "type": "VEC2"
-    },
-    {
-      "bufferView": 3,
-      "componentType": 5125,
-      "count": 6,
-      "type": "SCALAR"
-    }
-  ],
-  "asset": {
-    "version": "2.0"
-  },
-  "buffers": [
-    {
-      "uri": "Test.bin",
-      "byteLength": 168
-    }
-  ],
-  "bufferViews": [
-    {
-      "buffer": 0,
-      "byteLength": 48
-    },
-    {
-      "buffer": 0,
-      "byteOffset": 48,
-      "byteLength": 64
-    },
-    {
-      "buffer": 0,
-      "byteOffset": 112,
-      "byteLength": 32
-    },
-    {
-      "buffer": 0,
-      "byteOffset": 144,
-      "byteLength": 24
-    }
-  ],
-  "images": [
-    {
-      "uri": "BaseColor.png"
-    }
-  ],
-  "materials": [
-    {
-      "pbrMetallicRoughness": {
-        "baseColorFactor": [
-          1.0,
-          1.0,
-          1.0,
-          0.7
-        ],
-        "baseColorTexture": {
-          "index": 0
-        }
-      },
-      "alphaMode": "BLEND"
-    }
-  ],
-  "meshes": [
-    {
-      "primitives": [
-        {
-          "attributes": {
-            "POSITION": 0,
-            "COLOR_0": 1,
-            "TEXCOORD_0": 2
-          },
-          "indices": 3,
-          "material": 0
-        }
-      ]
-    }
-  ],
-  "nodes": [
-    {
-      "mesh": 0,
-      "scale": [
-        0.5,
-        1,
-        1
-      ]
-    }
-  ],
-  "scene": 0,
-  "scenes": [
-    {
-      "nodes": [
-        0
-      ]
-    }
-  ],
-  "textures": [
-    {
-      "source": 0
-    }
-  ]
-}

BIN
Playground/scenes/MultiPrimitive/MultiPrimitive.bin


+ 0 - 134
Playground/scenes/MultiPrimitive/MultiPrimitive.gltf

@@ -1,134 +0,0 @@
-{
-  "accessors": [
-    {
-      "bufferView": 0,
-      "componentType": 5126,
-      "count": 3,
-      "type": "VEC3",
-      "max": [
-        0.5,
-        0.5,
-        0.0
-      ],
-      "min": [
-        -0.5,
-        -0.5,
-        0.0
-      ]
-    },
-    {
-      "bufferView": 1,
-      "componentType": 5126,
-      "count": 3,
-      "type": "VEC2"
-    },
-    {
-      "bufferView": 2,
-      "componentType": 5125,
-      "count": 3,
-      "type": "SCALAR"
-    },
-    {
-      "bufferView": 3,
-      "componentType": 5126,
-      "count": 3,
-      "type": "VEC3",
-      "max": [
-        0.5,
-        0.5,
-        0.0
-      ],
-      "min": [
-        -0.5,
-        -0.5,
-        0.0
-      ]
-    },
-    {
-      "bufferView": 4,
-      "componentType": 5126,
-      "count": 3,
-      "type": "VEC2"
-    },
-    {
-      "bufferView": 5,
-      "componentType": 5125,
-      "count": 3,
-      "type": "SCALAR"
-    }
-  ],
-  "asset": {
-    "version": "2.0"
-  },
-  "buffers": [
-    {
-      "uri": "MultiPrimitive.bin",
-      "byteLength": 144
-    }
-  ],
-  "bufferViews": [
-    {
-      "buffer": 0,
-      "byteLength": 36
-    },
-    {
-      "buffer": 0,
-      "byteOffset": 36,
-      "byteLength": 24
-    },
-    {
-      "buffer": 0,
-      "byteOffset": 60,
-      "byteLength": 12
-    },
-    {
-      "buffer": 0,
-      "byteOffset": 72,
-      "byteLength": 36
-    },
-    {
-      "buffer": 0,
-      "byteOffset": 108,
-      "byteLength": 24
-    },
-    {
-      "buffer": 0,
-      "byteOffset": 132,
-      "byteLength": 12
-    }
-  ],
-  "meshes": [
-    {
-      "primitives": [
-        {
-          "attributes": {
-            "POSITION": 0,
-            "TEXCOORD_0": 1
-          },
-          "indices": 2
-        },
-        {
-          "attributes": {
-            "POSITION": 3,
-            "TEXCOORD_0": 4
-          },
-          "indices": 5
-        }
-      ]
-    }
-  ],
-  "nodes": [
-    {
-      "name": "node",
-      "mesh": 0
-    }
-  ],
-  "scene": 0,
-  "scenes": [
-    {
-      "nodes": [
-        0
-      ]
-    }
-  ]
-}

BIN
Playground/scenes/TwoQuads/LOD0.png


BIN
Playground/scenes/TwoQuads/LOD1.png


BIN
Playground/scenes/TwoQuads/LOD2.png


BIN
Playground/scenes/TwoQuads/TwoQuads.bin


+ 0 - 162
Playground/scenes/TwoQuads/TwoQuads.gltf

@@ -1,162 +0,0 @@
-{
-  "accessors": [
-    {
-      "bufferView": 0,
-      "componentType": 5126,
-      "count": 4,
-      "type": "VEC3",
-      "max": [
-        0.5,
-        0.5,
-        0.0
-      ],
-      "min": [
-        -0.5,
-        -0.5,
-        0.0
-      ]
-    },
-    {
-      "bufferView": 1,
-      "componentType": 5126,
-      "count": 4,
-      "type": "VEC2"
-    },
-    {
-      "bufferView": 2,
-      "componentType": 5125,
-      "count": 6,
-      "type": "SCALAR"
-    }
-  ],
-  "asset": {
-    "version": "2.0"
-  },
-  "buffers": [
-    {
-      "uri": "TwoQuads.bin",
-      "byteLength": 104
-    }
-  ],
-  "bufferViews": [
-    {
-      "buffer": 0,
-      "byteLength": 48
-    },
-    {
-      "buffer": 0,
-      "byteOffset": 48,
-      "byteLength": 32
-    },
-    {
-      "buffer": 0,
-      "byteOffset": 80,
-      "byteLength": 24
-    }
-  ],
-  "extensionsUsed": [
-    "MSFT_lod"
-  ],
-  "images": [
-    {
-      "uri": "LOD0.png"
-    },
-    {
-      "uri": "LOD1.png"
-    },
-    {
-      "uri": "LOD2.png"
-    }
-  ],
-  "materials": [
-    {
-      "name": "LOD0",
-      "pbrMetallicRoughness": {
-        "baseColorTexture": {
-          "index": 0
-        },
-        "metallicFactor": 0
-      },
-      "extensions": {
-        "MSFT_lod": {
-          "ids": [
-            1,
-            2
-          ]
-        }
-      }
-    },
-    {
-      "name": "LOD1",
-      "pbrMetallicRoughness": {
-        "baseColorTexture": {
-          "index": 1
-        },
-        "metallicFactor": 0
-      }
-    },
-    {
-      "name": "LOD2",
-      "pbrMetallicRoughness": {
-        "baseColorTexture": {
-          "index": 2
-        },
-        "metallicFactor": 0
-      }
-    }
-  ],
-  "meshes": [
-    {
-      "primitives": [
-        {
-          "attributes": {
-            "POSITION": 0,
-            "TEXCOORD_0": 1
-          },
-          "indices": 2,
-          "material": 0
-        }
-      ]
-    }
-  ],
-  "nodes": [
-    {
-      "name": "node0",
-      "mesh": 0,
-      "translation": [
-        -0.55,
-        0,
-        0
-      ]
-    },
-    {
-      "name": "node1",
-      "mesh": 0,
-      "translation": [
-        0.55,
-        0,
-        0
-      ]
-    }
-  ],
-  "scene": 0,
-  "scenes": [
-    {
-      "nodes": [
-        0,
-        1
-      ]
-    }
-  ],
-  "textures": [
-    {
-      "source": 0
-    },
-    {
-      "source": 1
-    },
-    {
-      "source": 2
-    }
-  ]
-}

BIN
Playground/textures/Flare2.png


BIN
Playground/textures/Space/space_back.jpg


BIN
Playground/textures/Space/space_down.jpg


BIN
Playground/textures/Space/space_front.jpg


BIN
Playground/textures/Space/space_left.jpg


BIN
Playground/textures/Space/space_right.jpg


BIN
Playground/textures/Space/space_up.jpg


BIN
Playground/textures/flare3.png


BIN
Playground/textures/wood.jpg


+ 5 - 3
Tools/Gulp/config.json

@@ -1538,7 +1538,8 @@
                     "../../loaders/src/glTF/2.0/Extensions/KHR_draco_mesh_compression.ts",
                     "../../loaders/src/glTF/2.0/Extensions/KHR_materials_pbrSpecularGlossiness.ts",
                     "../../loaders/src/glTF/2.0/Extensions/KHR_materials_unlit.ts",
-                    "../../loaders/src/glTF/2.0/Extensions/KHR_lights.ts"
+                    "../../loaders/src/glTF/2.0/Extensions/KHR_lights.ts",
+                    "../../loaders/src/glTF/2.0/Extensions/KHR_texture_transform.ts"
                 ],
                 "doNotIncludeInBundle": true,
                 "output": "babylon.glTF2FileLoader.js"
@@ -1562,7 +1563,8 @@
                     "../../loaders/src/glTF/2.0/Extensions/KHR_draco_mesh_compression.ts",
                     "../../loaders/src/glTF/2.0/Extensions/KHR_materials_pbrSpecularGlossiness.ts",
                     "../../loaders/src/glTF/2.0/Extensions/KHR_materials_unlit.ts",
-                    "../../loaders/src/glTF/2.0/Extensions/KHR_lights.ts"
+                    "../../loaders/src/glTF/2.0/Extensions/KHR_lights.ts",
+                    "../../loaders/src/glTF/2.0/Extensions/KHR_texture_transform.ts"
                 ],
                 "output": "babylon.glTFFileLoader.js"
             }
@@ -1764,7 +1766,7 @@
                 "name": "babylonjs-viewer",
                 "main": "../../Viewer/src/index.d.ts",
                 "out": "../../dist/preview release/viewer/babylon.viewer.module.d.ts",
-                "prependText": "/// <reference path=\"./babylon.d.ts\"/>\n/// <reference path=\"./babylon.glTF2Interface.d.ts\"/>\n/// <reference path=\"./babylonjs.loaders.d.ts\"/>\n"
+                "prependText": "/// <reference path=\"./babylon.d.ts\"/>\n/// <reference path=\"./babylon.glTF2Interface.d.ts\"/>\n/// <reference path=\"./babylonjs.loaders.d.ts\"/>\ndeclare module \"babylonjs-loaders\"{ export=BABYLON;}\n"
             },
             "outputs": [
                 {

+ 1 - 0
Tools/Gulp/package.json

@@ -10,6 +10,7 @@
     "license": "(Apache-2.0)",
     "devDependencies": {
         "@types/node": "^8.9.4",
+        "base64-font-loader": "0.0.4",
         "base64-image-loader": "^1.2.1",
         "chai": "^4.1.2",
         "color-support": "^1.1.3",

BIN
Viewer/assets/babylon.woff


BIN
Viewer/assets/img/BabylonJS_Logo_Small.png


+ 27 - 3
Viewer/assets/templates/default/defaultTemplate.html

@@ -1,3 +1,27 @@
-<viewer></viewer>
-<loading-screen></loading-screen>
-<overlay></overlay>
+<style>
+    @font-face {
+        font-family: 'babylon';
+        src: url('{{babylonFont}}') format('woff');
+        font-weight: normal;
+        font-style: normal;
+    }
+
+    .icon {
+        font-family: "babylon";
+    }
+</style>
+
+{{#if fillScreen}}
+<style>
+    html,
+    body {
+        width: 100%;
+        height: 100%;
+
+        margin: 0;
+        padding: 0;
+    }
+</style>
+{{/if}}
+
+<fill-container></fill-container>

+ 1 - 2
Viewer/assets/templates/default/defaultViewer.html

@@ -3,7 +3,6 @@
         position: relative;
         overflow: hidden;
         /* Start stage */
-        flex: 1;
         z-index: 1;
         justify-content: center;
         align-items: center;
@@ -20,7 +19,7 @@
     }
 </style>
 
-<canvas class="babylonjs-canvas" id="{{canvasId}}">
+<canvas class="babylonjs-canvas" touch-action="none">
 </canvas>
 
 <nav-bar></nav-bar>

+ 41 - 0
Viewer/assets/templates/default/fillContainer.html

@@ -0,0 +1,41 @@
+<style>
+    fill-container {
+        display: flex;
+    }
+
+    fill-container>* {
+        touch-action: none;
+    }
+</style>
+
+{{#unless disable}}
+<style>
+    fill-container {
+        width: 100%;
+        height: 100%;
+
+        justify-content: flex-start;
+        /* align items in Main Axis */
+        align-items: stretch;
+        /* align items in Cross Axis */
+        align-content: stretch;
+        /* Extra space in Cross Axis */
+    }
+
+    fill-container>* {
+        flex: 1 1 auto;
+        flex-direction: row;
+
+        justify-content: flex-start;
+        /* align items in Main Axis */
+        align-items: stretch;
+        /* align items in Cross Axis */
+        align-content: stretch;
+        /* Extra space in Cross Axis */
+    }
+</style>
+{{/unless}}
+
+<viewer></viewer>
+<loading-screen></loading-screen>
+<overlay></overlay>

+ 3 - 3
Viewer/assets/templates/default/loadingScreen.html

@@ -10,9 +10,9 @@
         display: flex;
         justify-content: center;
         align-items: center;
-        -webkit-transition: opacity 2s ease;
-        -moz-transition: opacity 2s ease;
-        transition: opacity 2s ease;
+        -webkit-transition: opacity 1s ease;
+        -moz-transition: opacity 1s ease;
+        transition: opacity 1s ease;
     }
 
     img.loading-image {

+ 442 - 67
Viewer/assets/templates/default/navbar.html

@@ -1,79 +1,412 @@
 <style>
     nav-bar {
         position: absolute;
-        height: 160px;
+        height: 48px;
         width: 100%;
-        bottom: 0;
-        background-color: rgba(0, 0, 0, 0.3);
+        bottom: 10px;
+        display: flex;
+        justify-content: center;
+    }
+
+    nav-bar .nav-container {
+        display: flex;
+        flex-direction: row;
+        margin: 0 10px;
+        height: 100%;
+        width: 100%;
+        justify-content: center;
+    }
+
+    nav-bar .animation-control {
+        background-color: rgba(91, 93, 107, .75);
+        display: flex;
+        flex-direction: row;
+        height: 100%;
+        width: 100%;
+        max-width: 1280px;
+        justify-content: center;
+    }
+
+    nav-bar .flex-container {
+        display: flex;
+        flex-direction: row;
+        justify-content: center;
+        height: 100%;
+        width: 100%;
+    }
+
+    nav-bar button {
+        background: none;
+        border: none;
         color: white;
-        transition: 1s;
-        align-items: flex-start;
-        justify-content: space-around;
+        margin: 0;
+        padding: 0;
+
+        height: 100%;
+        min-width: 48px;
+        cursor: pointer;
+    }
+
+    nav-bar button:hover,
+    nav-bar button:active,
+    nav-bar button:focus {
+        background: none;
+        border: none;
+        outline: none;
+    }
+
+    nav-bar button:hover {
+        background-color: rgba(22, 24, 26, .20);
+    }
+
+    nav-bar .control-text {
+        font-family: "Segoe UI";
+        font-size: 12px;
+        font-weight: 400;
+        pointer-events: none;
+    }
+
+    nav-bar img,
+    nav-bar .icon {
+        pointer-events: none;
+    }
+
+    .logo-button {
         display: flex;
+        align-items: center;
+        flex-direction: row;
+        justify-content: center;
+        background-color: rgba(91, 93, 107, .75);
+        height: 48px;
+        min-width: 48px;
+        margin-right: 4px;
+    }
 
+    .logo-button img {
+        height: 20px;
+        width: 20px;
+    }
+
+    .types {
+        display: flex;
         flex-direction: column;
+        width: 48px;
     }
 
-    /* Big screens have room for the entire navbar */
+    .types .flex-container,
+    .speed .flex-container {
+        align-items: center;
+    }
 
-    @media screen and (min-width: 768px) {
-        nav-bar {
-            align-items: center;
-            flex-direction: row;
-            justify-content: space-between;
-            height: 80px;
-        }
+    .menu-options {
+        position: absolute;
+        bottom: 48px;
+        width: 48px;
+        background-color: rgba(22, 24, 26, .90);
     }
 
-    div.flex-container {
-        display: flex;
-        width: 100%;
+    .logo-button,
+    .animation-label,
+    .types-icon,
+    .help,
+    .speed {
+        display: none;
+    }
+
+    .types-icon:after {
+        font-size: 16px;
+        content: "\F6BE";
+    }
+
+    .up-icon:after {
+        line-height: 16px;
+        font-size: 12px;
+        content: "\E70E";
     }
 
-    div.thumbnail {
+    .play-icon:after {
+        font-size: 16px;
+        content: "\E768";
+    }
+
+    .pause-icon:after {
+        font-size: 16px;
+        content: "\E769";
+    }
+
+    .fullscreen-icon:after {
+        font-size: 16px;
+        content: "\E740";
+    }
+
+    .help-icon:after {
+        font-size: 16px;
+        content: "\EF4E";
+    }
+
+    .progress-control {
+        display: flex;
+        flex: 1;
         position: relative;
         overflow: hidden;
-        display: block;
-        width: 40px;
-        height: 40px;
-        background-size: cover;
-        background-position: center;
-        border-radius: 20px;
-        margin: 0 10px;
+        cursor: pointer;
+        align-items: center;
     }
 
-    div.title-container {
-        flex-direction: column;
+    .animation-number {
+        margin: 0 6px;
+    }
+
+    .speed-text {
+        margin-right: 6px;
+    }
+
+    .progress-circle {
+        width: 12px;
+        height: 12px;
+        border: 2px solid rgb(255, 255, 255);
+        border-radius: 50%;
+        background-color: rgb(91, 93, 107);
+        cursor: pointer;
+        position: relative;
+        bottom: 10px;
+        pointer-events: none;
+    }
+
+    .default-control {
         display: flex;
-        justify-content: space-between;
+        flex-direction: row;
+        height: 100%;
+        background-color: rgba(91, 93, 107, .75);
+    }
+
+    .menu-options button {
+        width: 100%;
+        height: 48px;
+        color: rgb(142, 147, 155);
     }
 
-    span.model-title {
-        font-size: 125%;
+    .menu-options button {
+        width: 100%;
+        height: 48px;
+        color: rgb(142, 147, 155);
     }
 
-    span.model-subtitle {
-        font-size: 90%;
+    .menu-options button:hover {
+        background-color: transparent;
+        color: rgb(255, 255, 255);
     }
 
-    div.button-container,
-    div.animation-container {
-        align-items: center;
-        justify-content: flex-end;
+    .menu-options .animation-number {
+        margin: 0 18px 0 6px;
+    }
+
+    .menu-options .speed-text {
+        margin-right: 18px;
+    }
+
+    .menu-options {
+        visibility: hidden;
+    }
+
+    .open .menu-options {
+        visibility: visible;
     }
 
-    div.button {
+    .types .menu-options {
+        width: 144px;
+    }
+
+    .types .menu-options button {
+        padding: 0 8px;
+        justify-content: left;
+    }
+
+    .types .menu-options button>* {
+        display: flex;
+        margin: 8px;
+    }
+
+    .types .menu-options button span.animation-number {
+        display: none;
+    }
+
+    /* Disable fullscreen button for small screens */
+
+    .fullscreen {
+        display: none;
+    }
+
+    @media screen and (min-width: 540px) {
+        .help,
+        .types-icon,
+        .speed {
+            display: inline-block;
+        }
+
+        .logo-button {
+            display: flex;
+        }
+
+        .types {
+            width: 84px;
+        }
+
+        .progress-bar-container {
+            margin: 0 12px;
+        }
+
+        .speed {
+            width: 64px;
+        }
+
+        .speed .menu-options {
+            width: 64px;
+        }
+
+        .fullscreen {
+            display: block;
+        }
+    }
+
+    @media screen and (min-width: 1024px) {
+        .animation-label {
+            display: block;
+            margin-left: 6px;
+            text-overflow: ellipsis;
+            overflow: hidden;
+        }
+
+        nav-bar button.animation-buttons {
+            padding: 0 8px;
+            justify-content: left;
+        }
+
+        .icon.up-icon {
+            margin-left: 8px;
+        }
+
+        nav-bar button.animation-buttons>div {
+            display: flex;
+            pointer-events: none;
+        }
+
+        .animation-number {
+            display: none;
+        }
+
+        .progress-bar-container {
+            margin: 0 12px;
+        }
+
+        .types {
+            width: 144px;
+        }
+    }
+
+
+    .progress-wrapper {
+        -webkit-appearance: none;
         cursor: pointer;
+        width: 100%;
+        outline: none;
+        margin: 0 12px;
         height: 30px;
-        margin: 0 10px;
+        background-color: transparent;
     }
 
-    div.button img {
-        height: 100%;
+    /*Chrome -webkit */
+
+    .progress-wrapper::-webkit-slider-thumb {
+        -webkit-appearance: none;
+        width: 20px;
+        height: 20px;
+        border: 2px solid white;
+        border-radius: 50%;
+        background: rgba(91, 93, 107, 1);
+        margin-top: -10px;
+    }
+
+    .progress-wrapper::-webkit-slider-runnable-track {
+        height: 2px;
+        -webkit-appearance: none;
+        background-color: white;
+    }
+
+
+    /** FireFox -moz */
+
+    .progress-wrapper::-moz-range-progress {
+        background-color: white;
+        height: 2px;
+    }
+
+    .progress-wrapper::-moz-range-thumb {
+        width: 20px;
+        height: 20px;
+        border: 2px solid white;
+        border-radius: 50%;
+        background: rgba(91, 93, 107, 1);
+    }
+
+    .progress-wrapper::-moz-range-track {
+        background: white;
+        height: 2px;
+    }
+
+    /** IE -ms */
+
+    .progress-wrapper::-ms-track {
+        height: 2px;
+
+        /*remove bg colour from the track, we'll use ms-fill-lower and ms-fill-upper instead */
+        background: transparent;
+
+        /*leave room for the larger thumb to overflow with a transparent border */
+        border-color: transparent;
+        border-width: 10px 0;
+
+        /*remove default tick marks*/
+        color: transparent;
+    }
+
+    .progress-wrapper::-ms-fill-lower {
+        background: white;
+        border-radius: 5px;
+    }
+
+    .progress-wrapper::-ms-fill-upper {
+        background: white;
+        border-radius: 5px;
+    }
+
+    .progress-wrapper::-ms-thumb {
+        width: 16px;
+        height: 16px;
+        border: 2px solid white;
+        border-radius: 50%;
+        background: rgba(91, 93, 107, 1);
+        margin-top: 0px;
+    }
+
+    span {
+        display: inline-block;
     }
 </style>
 
-{{#if disableOnFullscreen}}
+{{#if (or (not animations) hideAnimations)}} {{#if hideLogo}}
+<style>
+    nav-bar .nav-container {
+        justify-content: flex-end;
+    }
+</style>
+{{else}}
+<style>
+    nav-bar .nav-container {
+        justify-content: space-between;
+    }
+</style>
+{{/if}} {{/if}} {{#if disableOnFullscreen}}
 <style>
     viewer:fullscreen nav-bar {
         display: none;
@@ -89,33 +422,75 @@
 </style>
 {{/if}}
 
-<div class="flex-container" id="model-metadata">
-    <!-- holding the description -->
-    <div class="thumbnail">
-        <!-- holding the thumbnail 
-        <img src="{{thumbnail}}" alt="{{title}}">-->
+<div class="nav-container" id="navbar-control">
+    {{#unless hideLogo}}
+    <div class="logo-button" title="{{logoText}}">
+        {{#if logoLink}}
+        <a href="{{logoLink}}" target="_blank">
+            <img src="{{logoImage}}">
+        </a>
+        {{else}}
+        <img src="{{logoImage}}"> {{/if}}
     </div>
-    <div class="title-container">
-
-        <span class="model-title">{{#if title}}{{title}}{{/if}}</span>
-        <span class="model-subtitle"> {{#if subtitle}}{{subtitle}} {{/if}}</span>
+    {{/unless}}{{#unless (or (not animations) hideAnimations)}}
+    <div class="animation-control">
+        <div class="types">
+            <button class="flex-container animation-buttons" id="types-button">
+                <!-- <div> -->
+                <span class="icon types-icon"></span>
+                <span class="control-text animation-label">{{selectedAnimationName}}</span>
+                <span class="control-text animation-number">{{selectedAnimation}}</span>
+                <!-- </div> -->
+                {{#if (gt (count animations) 1)}}
+                <span class="icon up-icon"></span>
+                {{/if}}
+            </button>
+            <div class="menu-options">
+                {{#each animations}} {{#unless (eq ../selectedAnimation (add @index 1))}}
+                <button class="flex-container animation-buttons" id="label-option-button" data-value="{{this}}">
+                    <!-- <div> -->
+                    <span class="icon types-icon"></span>
+                    <span class="control-text animation-label">{{this}}</span>
+                    <span class="control-text animation-number">{{add @index 1}}</span>
+                    <!-- </div> -->
+                </button>
+                {{/unless}} {{/each}}
+            </div>
+        </div>
+        <div class="progress-control" id="progress-control">
+            <button class="play-pause" id="play-pause-button">
+                {{#if paused}}
+                <span class="icon play-icon"></span>
+                {{else}}
+                <span class="icon pause-icon"></span>
+                {{/if}}
+            </button>
+            <input class="progress-wrapper" id="progress-wrapper" type="range" min="0" max="100" step="0.01">
+        </div>
+        <div class="speed">
+            <button class="flex-container" id="speed-button">
+                <span class="control-text speed-text">{{selectedSpeed}}</span>
+                <span class="icon up-icon"></span>
+            </button>
+            <div class="menu-options">
+                {{#eachInMap speedList}} {{#unless (eq ../selectedSpeed id)}}
+                <button class="flex-container" id="speed-option-button" data-value="{{value}}">
+                    <span class="control-text speed-text">{{id}}</span>
+                </button>
+                {{/unless}} {{/eachInMap}}
+            </div>
+        </div>
     </div>
-</div>
-{{#if animations}} {{#if hideAnimations}}{{else}}
-<div class="animation-container flex-container">
-    <select id="animation-selector" name="animations">
-        {{#each animations}}
-        <option value="{{this}}">{{this}}</option>> {{/each}}
-    </select>
-</div>
-{{/if}} {{/if}}
-<div class="button-container flex-container">
-    <!-- holding the buttons -->
-    {{#eachInMap buttons}}
-    <div id="{{id}}" class="button">
-        {{#if text}}
-        <span>{{text}}</span>> {{/if}} {{#if image}}
-        <img src="{{image}}" alt="{{altText}}"> {{/if}}
+    {{/unless}}
+    <div class="default-control">
+        {{#unless hideHelp}}
+        <button class="help" id="help-button" title="Help">
+            <span class="icon help-icon"></span>
+        </button>
+        {{/unless}} {{#unless hideFullScreen}}
+        <button class="fullscreen" id="fullscreen-button" title="Fullscreen">
+            <span class="icon fullscreen-icon"></span>
+        </button>
+        {{/unless}}
     </div>
-    {{/eachInMap}}
 </div>

+ 2 - 0
Viewer/dist/_headers

@@ -0,0 +1,2 @@
+/*
+	Access-Control-Allow-Origin: *

BIN
Viewer/dist/assets/environment/Skybox_2.0-256.dds


BIN
Viewer/dist/external/msft/EnvMap_2.0-256.env


BIN
Viewer/dist/external/msft/Ground_2.0-1024.png


BIN
Viewer/dist/external/msft/Skybox_3.0-256.dds


+ 42 - 0
Viewer/dist/external/msft/codeExample.html

@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html lang="en">
+
+    <head>
+        <meta charset="UTF-8">
+        <meta name="viewport" content="width=device-width, initial-scale=1.0">
+        <meta http-equiv="X-UA-Compatible" content="ie=edge">
+        <title>BabylonJS Viewer - Configuration usage with code</title>
+        <style>
+        </style>
+    </head>
+
+    <body>
+        <div id="viewport"></div>
+        <script src="../../viewer.js"></script>
+        <script>
+            var config = {
+                extends: 'extended',
+                configuration: "./config.json",
+                model: {
+                    url: "https://babylonjs.com/Assets/FlightHelmet/glTF/FlightHelmet_Materials.gltf",
+                },
+                templates: {
+                    main: {
+                        params: {
+                            fillScreen: true
+                        }
+                    }
+                },
+                lab: {
+                    assetsRootURL: '/external/msft/'
+                }
+            }
+
+            // create viewer
+            var viewerElement = document.getElementById("viewport");
+            new BabylonViewer.DefaultViewer(viewerElement, config);
+
+        </script>
+    </body>
+
+</html>

+ 25 - 0
Viewer/dist/external/msft/config.json

@@ -0,0 +1,25 @@
+{
+    "ground": {
+        "material": {
+            "primaryColorHighlightLevel": 0.05
+        }
+    },
+    "skybox": {
+        "cubeTexture": {
+            "url": "Skybox_3.0-256.dds"
+        },
+        "material": {
+            "primaryColorShadowLevel": 0.1
+        }
+    },
+    "scene": {
+        "imageProcessingConfiguration": {
+            "vignetteWeight": 0
+        },
+        "mainColor": {
+            "r": 0.9607843137254902,
+            "g": 0.9607843137254902,
+            "b": 0.9607843137254902
+        }
+    }
+}

+ 19 - 0
Viewer/dist/external/msft/htmlExample.html

@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html lang="en">
+
+    <head>
+        <meta charset="UTF-8">
+        <meta name="viewport" content="width=device-width, initial-scale=1.0">
+        <meta http-equiv="X-UA-Compatible" content="ie=edge">
+        <title>BabylonJS Viewer - Configuration usage</title>
+        <style>
+        </style>
+    </head>
+
+    <body>
+        <babylon id="viewer" configuration="./config.json" lab.assets-root-u-r-l="/external/msft/" templates.main.params.fill-screen="true"
+            extends="extended" model.url="https://babylonjs.com/Assets/FlightHelmet/glTF/FlightHelmet_Materials.gltf"></babylon>
+        <script src="../../viewer.js"></script>
+    </body>
+
+</html>

+ 4 - 11
Viewer/dist/ufoExample.html

@@ -6,22 +6,15 @@
         <meta name="viewport" content="width=device-width, initial-scale=1.0">
         <meta http-equiv="X-UA-Compatible" content="ie=edge">
         <title>BabylonJS Viewer - UFO</title>
-        <style>
-            babylon,
-            #viewport {
-                max-width: 900px;
-                max-height: 600px;
-                width: 100%;
-                height: 600px;
-            }
-        </style>
     </head>
 
     <body>
-        <babylon extends="default, shadowDirectionalLight, environmentMap" templates.nav-bar.params.hide-animations="true" templates.nav-bar.params.disable-on-fullscreen="true">
+        <babylon extends="default, shadowDirectionalLight, environmentMap" templates.nav-bar.params.hide-logo="true" templates.main.params.fill-screen="true">
             <scene glow="true">
-                <main-color r="0.5" g="0.2" b="0.2"></main-color>
             </scene>
+            <lab>
+                <environment-main-color r="0.5" g="0.2" b="0.2"></environment-main-color>
+            </lab>
             <model url="https://models.babylonjs.com/ufo.glb">
                 <animation auto-start="true"></animation>
             </model>

+ 1 - 0
Viewer/package.json

@@ -24,6 +24,7 @@
     "homepage": "https://github.com/BabylonJS/Babylon.js#readme",
     "devDependencies": {
         "@types/node": "^8.9.4",
+        "base64-font-loader": "0.0.4",
         "base64-image-loader": "^1.2.1",
         "html-loader": "^0.5.5",
         "json-loader": "^0.5.7",

+ 77 - 19
Viewer/src/configuration/configuration.ts

@@ -1,5 +1,19 @@
 import { ITemplateConfiguration } from './../templateManager';
-import { EngineOptions, IGlowLayerOptions } from 'babylonjs';
+import { EngineOptions, IGlowLayerOptions, DepthOfFieldEffectBlurLevel } from 'babylonjs';
+
+export function getConfigurationKey(key: string, configObject: any) {
+    let splits = key.split('.');
+
+    if (splits.length === 0 || !configObject) return false;
+    else if (splits.length === 1) {
+        if (configObject[key] !== undefined) {
+            return configObject[key];
+        }
+    } else {
+        let firstKey = splits.shift();
+        return getConfigurationKey(splits.join("."), configObject[firstKey!])
+    }
+}
 
 export interface ViewerConfiguration {
 
@@ -32,6 +46,7 @@ export interface ViewerConfiguration {
     lights?: { [name: string]: boolean | ILightConfiguration },
     // engine configuration. optional!
     engine?: {
+        renderInBackground?: boolean;
         antialiasing?: boolean;
         disableResize?: boolean;
         engineOptions?: EngineOptions;
@@ -73,7 +88,8 @@ export interface ViewerConfiguration {
             specular?: { r: number, g: number, b: number };
         }
         hideLoadingDelay?: number;
-        environmentAssetsRootURL?: string;
+        assetsRootURL?: string;
+        environmentMainColor?: { r: number, g: number, b: number };
         environmentMap?: {
             /**
              * Environment map texture path in relative to the asset folder.
@@ -90,30 +106,67 @@ export interface ViewerConfiguration {
              */
             tintLevel: number;
         }
-        renderingPipelines?: {
-            default?: boolean | {
-                [propName: string]: any;
-            };
-            standard?: boolean | {
-                [propName: string]: any;
-            };
-            /*lens?: boolean | {
-                [propName: string]: boolean | string | number | undefined;
-            };*/
-            ssao?: boolean | {
-                [propName: string]: any;
-            };
-            ssao2?: boolean | {
-                [propName: string]: any;
-            };
-        }
+        defaultRenderingPipelines?: boolean | IDefaultRenderingPipelineConfiguration;
     }
 }
 
+/**
+ * Defines an animation to be applied to a model (translation, scale or rotation).
+ */
+export interface IModelAnimationConfiguration {
+    /**
+     * Time of animation, in seconds
+     */
+    time?: number;
+
+    /**
+     * Scale to apply
+     */
+    scaling?: {
+        x: number;
+        y: number;
+        z: number;
+    };
+
+    /**
+     * Easing function to apply
+     * See SPECTRE.EasingFunction
+     */
+    easingFunction?: number;
+
+    /**
+     * An Easing mode to apply to the easing function
+     * See BABYLON.EasingFunction
+     */
+    easingMode?: number;
+}
+
+
+export interface IDefaultRenderingPipelineConfiguration {
+    sharpenEnabled?: boolean;
+    bloomEnabled?: boolean;
+    bloomThreshold?: number;
+    depthOfFieldEnabled?: boolean;
+    depthOfFieldBlurLevel?: DepthOfFieldEffectBlurLevel;
+    fxaaEnabled?: boolean;
+    imageProcessingEnabled?: boolean;
+    defaultPipelineTextureType?: number;
+    bloomScale?: number;
+    chromaticAberrationEnabled?: boolean;
+    grainEnabled?: boolean;
+    bloomKernel?: number;
+    hardwareScaleLevel?: number;
+    bloomWeight?: number;
+    bllomThreshold?: number;
+    hdr?: boolean;
+    samples?: number;
+}
+
 export interface IModelConfiguration {
     id?: string;
     url?: string;
     root?: string; //optional
+    file?: string; // 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 };
@@ -135,8 +188,12 @@ export interface IModelConfiguration {
     animation?: {
         autoStart?: boolean | string;
         playOnce?: boolean;
+        autoStartIndex?: number;
     }
 
+    entryAnimation?: IModelAnimationConfiguration;
+    exitAnimation?: IModelAnimationConfiguration;
+
     material?: {
         directEnabled?: boolean;
         directIntensity?: number;
@@ -358,6 +415,7 @@ export interface ISceneOptimizerConfiguration {
         renderTarget?: ISceneOptimizerParameters;
         mergeMeshes?: ISceneOptimizerParameters;
     }
+    custom?: string;
 }
 
 export interface IObserversConfiguration {

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

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

+ 43 - 52
Viewer/src/configuration/types/default.ts

@@ -1,10 +1,24 @@
 import { ViewerConfiguration } from './../configuration';
 
+/**
+ * The default configuration of the viewer, including templates (canvas, overly, loading screen)
+ * This configuration doesn't hold specific parameters, and only defines objects that are needed for the viewer to fully work correctly.
+ */
 export let defaultConfiguration: ViewerConfiguration = {
     version: "3.2.0-alpha4",
     templates: {
         main: {
-            html: require("../../../assets/templates/default/defaultTemplate.html")
+            html: require("../../../assets/templates/default/defaultTemplate.html"),
+            params: {
+                babylonFont: require('../../../assets/babylon.woff'),
+                noEscape: true
+            }
+        },
+        fillContainer: {
+            html: require("../../../assets/templates/default/fillContainer.html"),
+            params: {
+                disable: false
+            }
         },
         loadingScreen: {
             html: require("../../../assets/templates/default/loadingScreen.html"),
@@ -14,32 +28,34 @@ export let defaultConfiguration: ViewerConfiguration = {
             }
         },
         viewer: {
-            html: require("../../../assets/templates/default/defaultViewer.html"),
-            events: {
-                pointerout: true,
-                pointerdown: true,
-                pointerup: true
-            }
+            html: require("../../../assets/templates/default/defaultViewer.html")
         },
         navBar: {
             html: require("../../../assets/templates/default/navbar.html"),
             params: {
-                buttons: {
-                    /*"help-button": {
-                        altText: "Help",
-                        image: require('../../../assets/img/help-circle.png')
-                    },*/
-                    "fullscreen-button": {
-                        altText: "Fullscreen",
-                        image: require('../../../assets/img/fullscreen.png')
-                    }
+                speedList: {
+                    "0.5x": "0.5",
+                    "1.0x": "1.0",
+                    "1.5x": "1.5",
+                    "2.0x": "2.0",
                 },
-                visibilityTimeout: 2000
+                logoImage: require('../../../assets/img/BabylonJS_Logo_Small.png'),
+                logoText: 'BabylonJS',
+                logoLink: 'https://babylonjs.com',
+                hideHelp: true,
+                disableOnFullscreen: false,
             },
             events: {
-                pointerdown: { 'fullscreen-button': true/*, '#help-button': true*/ },
-                pointerover: true,
-                change: { 'animation-selector': true }
+                pointerdown: {
+                    'navbar-control': true,
+                    'help-button': true
+                },
+                input: {
+                    'progress-wrapper': true
+                },
+                pointerup: {
+                    'progress-wrapper': true
+                }
             }
         },
         overlay: {
@@ -62,41 +78,21 @@ export let defaultConfiguration: ViewerConfiguration = {
     },
     camera: {
         behaviors: {
-            autoRotate: 0,
+            autoRotate: {
+                type: 0
+            },
             framing: {
                 type: 2,
                 zoomOnBoundingInfo: true,
                 zoomStopsAnimation: false
             },
-            bouncing: 1
+            bouncing: {
+                type: 1
+            }
         },
         wheelPrecision: 200,
     },
     skybox: {
-        /*cubeTexture: {
-            url: 'https://playground.babylonjs.com/textures/environment.dds',
-            gammaSpace: false
-        },*/
-        pbr: true,
-        blur: 0.7,
-        infiniteDistance: false,
-        /*material: {
-            imageProcessingConfiguration: {
-                colorCurves: {
-                    globalDensity: 89,
-                    globalHue: 58.88,
-                    globalSaturation: 94
-                },
-                colorCurvesEnabled: true,
-                exposure: 1.5,
-                contrast: 1.66,
-                toneMappingEnabled: true,
-                vignetteEnabled: true,
-                vignetteWeight: 5,
-                vignetteColor: { r: 0.8, g: 0.6, b: 0.4 },
-                vignetteM: true
-            }
-        }*/
     },
     ground: {
         receiveShadows: true
@@ -105,10 +101,5 @@ export let defaultConfiguration: ViewerConfiguration = {
         antialiasing: true
     },
     scene: {
-        imageProcessingConfiguration: {
-            exposure: 1.4,
-            contrast: 1.66,
-            toneMappingEnabled: true
-        }
     }
-}
+}

+ 4 - 1
Viewer/src/configuration/types/environmentMap.ts

@@ -1,8 +1,11 @@
 import { ViewerConfiguration } from './../configuration';
 
+/**
+ * Lab-oriented default .env support
+ */
 export const environmentMapConfiguration: ViewerConfiguration = {
     lab: {
-        environmentAssetsRootURL: '/assets/environment/',
+        assetsRootURL: '/assets/environment/',
         environmentMap: {
             texture: 'EnvMap_2.0-256.env',
             rotationY: 0,

+ 265 - 40
Viewer/src/configuration/types/extended.ts

@@ -1,93 +1,318 @@
 import { ViewerConfiguration } from './../configuration';
+import { Tools } from 'babylonjs';
 
+/**
+ * The viewer's "extended" configuration.
+ * This configuration defines specific obejcts and parameters that we think make any model look good.
+ */
 export let extendedConfiguration: ViewerConfiguration = {
     version: "3.2.0",
     extends: "default",
     camera: {
+        exposure: 3.034578,
+        fov: 0.7853981633974483,
+        contrast: 1.6,
+        toneMappingEnabled: true,
+        upperBetaLimit: 1.3962634015954636 + Math.PI / 2,
+        lowerBetaLimit: -1.4835298641951802 + Math.PI / 2,
+        behaviors: {
+            framing: {
+                type: 2,
+                mode: 0,
+                positionScale: 0.5,
+                defaultElevation: 0.2617993877991494,
+                elevationReturnWaitTime: 3000,
+                elevationReturnTime: 2000,
+                framingTime: 500,
+                zoomStopsAnimation: false,
+                radiusScale: 0.866
+            },
+            autoRotate: {
+                type: 0,
+                idleRotationWaitTime: 4000,
+                idleRotationSpeed: 0.17453292519943295,
+                idleRotationSpinupTime: 2500,
+                zoomStopsAnimation: false
+            },
+            bouncing: {
+                type: 1,
+                lowerRadiusTransitionRange: 0.05,
+                upperRadiusTransitionRange: -0.2
+            }
+        },
+        upperRadiusLimit: 5,
+        lowerRadiusLimit: 0.5,
+        frameOnModelLoad: true,
+        framingElevation: 0.2617993877991494,
+        framingRotation: 1.5707963267948966,
         radius: 2,
-        alpha: -1.5708,
+        alpha: 1.5708,
         beta: Math.PI * 0.5 - 0.2618,
         wheelPrecision: 300,
         minZ: 0.1,
         maxZ: 50,
+        fovMode: 0,
+        pinchPrecision: 1500,
+        panningSensibility: 3000
     },
     lights: {
-        "light1": {
+        light0: {
             type: 0,
-            shadowEnabled: false,
-            position: { x: -1.78, y: 2.298, z: 2.62 },
-            diffuse: { r: 0.8, g: 0.8, b: 0.8 },
-            intensity: 3,
+            frustumEdgeFalloff: 0,
+            intensity: 7,
             intensityMode: 0,
-            radius: 3.135,
+            radius: 0.6,
+            range: 0.6,
+            spotAngle: 60,
+            diffuse: {
+                r: 1,
+                g: 1,
+                b: 1
+            },
+            position: {
+                x: -2,
+                y: 2.5,
+                z: 2
+            },
+            target: {
+                x: 0,
+                y: 0,
+                z: 0
+            },
+            enabled: true,
+            shadowEnabled: true,
+            shadowBufferSize: 512,
+            shadowMinZ: 1,
+            shadowMaxZ: 10,
+            shadowFieldOfView: 60,
+            shadowFrustumSize: 2,
+            shadowConfig: {
+                useBlurCloseExponentialShadowMap: true,
+                useKernelBlur: true,
+                blurScale: 1.0,
+                bias: 0.001,
+                depthScale: 50 * (10 - 1),
+                frustumEdgeFalloff: 0
+            }
         },
-        "light3": {
-            type: 2,
+        light1: {
+            type: 0,
+            frustumEdgeFalloff: 0,
+            intensity: 7,
+            intensityMode: 0,
+            radius: 0.4,
+            range: 0.4,
+            spotAngle: 57,
+            diffuse: {
+                r: 1,
+                g: 1,
+                b: 1
+            },
+            position: {
+                x: 4,
+                y: 3,
+                z: -0.5
+            },
+            target: {
+                x: 0,
+                y: 0,
+                z: 0
+            },
+            enabled: true,
             shadowEnabled: false,
-            position: { x: -4, y: 2, z: -2.23 },
-            diffuse: { r: 0.718, g: 0.772, b: 0.749 },
-            intensity: 2.052,
+            shadowBufferSize: 512,
+            shadowMinZ: 0.2,
+            shadowMaxZ: 10,
+            shadowFieldOfView: 28,
+            shadowFrustumSize: 2
+        },
+        light2: {
+            type: 0,
+            frustumEdgeFalloff: 0,
+            intensity: 1,
             intensityMode: 0,
             radius: 0.5,
-            spotAngle: 42.85
+            range: 0.5,
+            spotAngle: 42.85,
+            diffuse: {
+                r: 0.8,
+                g: 0.8,
+                b: 0.8
+            },
+            position: {
+                x: -1,
+                y: 3,
+                z: -3
+            },
+            target: {
+                x: 0,
+                y: 0,
+                z: 0
+            },
+            enabled: true,
+            shadowEnabled: false,
+            shadowBufferSize: 512,
+            shadowMinZ: 0.2,
+            shadowMaxZ: 10,
+            shadowFieldOfView: 45,
+            shadowFrustumSize: 2
         }
     },
     ground: {
-        receiveShadows: true
+        shadowLevel: 0.9,
+        texture: "Ground_2.0-1024.png",
+        material: {
+            primaryColorHighlightLevel: 0.035,
+            primaryColorShadowLevel: 0,
+            enableNoise: true,
+            useRGBColor: false,
+            maxSimultaneousLights: 1,
+            diffuseTexture: {
+                gammaSpace: true
+            }
+        },
+        opacity: 1,
+        mirror: false,
+        receiveShadows: true,
+        size: 5
+    },
+    skybox: {
+        scale: 11,
+        cubeTexture: {
+            url: "Skybox_2.0-256.dds"
+        },
+        material: {
+            primaryColorHighlightLevel: 0.03,
+            primaryColorShadowLevel: 0.03,
+            enableNoise: true,
+            useRGBColor: false,
+            reflectionTexture: {
+                gammaSpace: true
+            }
+        }
+    },
+    engine: {
+        renderInBackground: true
     },
     scene: {
+        flags: {
+            shadowsEnabled: true,
+            particlesEnabled: false,
+            collisionsEnabled: false,
+            lightsEnabled: true,
+            texturesEnabled: true,
+            lensFlaresEnabled: false,
+            proceduralTexturesEnabled: false,
+            renderTargetsEnabled: true,
+            spritesEnabled: false,
+            skeletonsEnabled: true,
+            audioEnabled: false,
+        },
+        defaultMaterial: {
+            materialType: 'pbr',
+            reflectivityColor: {
+                r: 0.1,
+                g: 0.1,
+                b: 0.1
+            },
+            microSurface: 0.6
+        },
+        clearColor: {
+            r: 0.9,
+            g: 0.9,
+            b: 0.9,
+            a: 1.0
+        },
         imageProcessingConfiguration: {
+            vignetteCentreX: 0,
+            vignetteCentreY: 0,
+            vignetteColor: {
+                r: 0.086,
+                g: 0.184,
+                b: 0.259,
+                a: 1
+            },
+            vignetteWeight: 0.855,
+            vignetteStretch: 0.5,
+            vignetteBlendMode: 0,
+            vignetteCameraFov: 0.7853981633974483,
+            isEnabled: true,
             colorCurves: {
-                shadowsHue: 43.359,
-                shadowsDensity: 1,
-                shadowsSaturation: -25,
-                shadowsExposure: -3.0,
-                midtonesHue: 93.65,
-                midtonesDensity: -15.24,
-                midtonesExposure: 7.37,
-                midtonesSaturation: -15,
-                highlightsHue: 37.2,
-                highlightsDensity: -22.43,
-                highlightsExposure: 45.0,
-                highlightsSaturation: -15
+                shadowsHue: 0,
+                shadowsDensity: 0,
+                shadowsSaturation: 0,
+                shadowsExposure: 0,
+                midtonesHue: 0,
+                midtonesDensity: 0,
+                midtonesExposure: 0,
+                midtonesSaturation: 0,
+                highlightsHue: 0,
+                highlightsDensity: 0,
+                highlightsExposure: 0,
+                highlightsSaturation: 0
             }
         },
         mainColor: {
-            r: 0.7,
-            g: 0.7,
-            b: 0.7
+            r: 0.8823529411764706,
+            g: 0.8823529411764706,
+            b: 0.8823529411764706
         }
     },
     loaderPlugins: {
         extendedMaterial: true,
-        minecraft: true,
+        applyMaterialConfig: true,
         msftLod: true,
         telemetry: true
     },
     model: {
         rotationOffsetAxis: {
             x: 0,
-            y: 1,
+            y: -1,
             z: 0
         },
-        rotationOffsetAngle: 3.66519,
+        rotationOffsetAngle: Tools.ToRadians(210),
         material: {
             directEnabled: true,
             directIntensity: 0.884,
             emissiveIntensity: 1.04,
-            environmentIntensity: 0.868
+            environmentIntensity: 0.6
+        },
+        entryAnimation: {
+            scaling: {
+                x: 0,
+                y: 0,
+                z: 0
+            },
+            time: 0.5,
+            easingFunction: 4,
+            easingMode: 1
+        },
+        exitAnimation: {
+            scaling: {
+                x: 0,
+                y: 0,
+                z: 0
+            },
+            time: 0.5,
+            easingFunction: 4,
+            easingMode: 1
         },
         normalize: true,
         castShadow: true,
         receiveShadows: true
     },
     lab: {
-        renderingPipelines: {
-            default: {
-                bloomEnabled: true,
-                bloomThreshold: 1.0,
-                fxaaEnabled: true
-            }
+        assetsRootURL: 'https://viewer.babylonjs.com/assets/environment/',
+        environmentMap: {
+            texture: "EnvMap_2.0-256.env",
+            rotationY: 3,
+            tintLevel: 0.4
+        },
+        defaultRenderingPipelines: {
+            bloomEnabled: true,
+            bloomThreshold: 1.0,
+            fxaaEnabled: true,
+            bloomWeight: 0.05
         }
     }
-}
+}

+ 8 - 0
Viewer/src/configuration/types/index.ts

@@ -6,6 +6,14 @@ import { shadowDirectionalLightConfiguration, shadowSpotlLightConfiguration } fr
 import { environmentMapConfiguration } from './environmentMap';
 import * as deepmerge from '../../../assets/deepmerge.min.js';
 
+/**
+ * Get the configuration type you need to use as the base for your viewer.
+ * The types can either be a single string, or comma separated types that will extend each other. for example:
+ * 
+ * "default, environmentMap" will first load the default configuration and will extend it using the environmentMap configuration.
+ * 
+ * @param types a comma-separated string of the type(s) or configuration to load. 
+ */
 let getConfigurationType = function (types: string): ViewerConfiguration {
     let config: ViewerConfiguration = {};
     let typesSeparated = types.split(",");

Datei-Diff unterdrückt, da er zu groß ist
+ 5 - 1
Viewer/src/configuration/types/minimal.ts


+ 9 - 3
Viewer/src/configuration/types/shadowLight.ts

@@ -1,5 +1,8 @@
 import { ViewerConfiguration } from './../configuration';
 
+/**
+ * Defines a default directional shadow light for normalized objects (!)
+ */
 export const shadowDirectionalLightConfiguration: ViewerConfiguration = {
     model: {
         receiveShadows: true,
@@ -18,18 +21,21 @@ export const shadowDirectionalLightConfiguration: ViewerConfiguration = {
             intensity: 4.887,
             intensityMode: 0,
             shadowBufferSize: 1024,
-            shadowFrustumSize: 8.0,
+            shadowFrustumSize: 6.0,
             shadowFieldOfView: 50.977,
             shadowMinZ: 0.1,
-            shadowMaxZ: 12.0,
+            shadowMaxZ: 10.0,
             shadowConfig: {
                 blurKernel: 32,
-                useBlurExponentialShadowMap: true
+                useBlurCloseExponentialShadowMap: true
             }
         }
     }
 }
 
+/**
+ * Defines a default shadow-enabled spot light for normalized objects.
+ */
 export const shadowSpotlLightConfiguration: ViewerConfiguration = {
     model: {
         receiveShadows: true,

+ 3 - 2
Viewer/src/eventManager.ts

@@ -30,8 +30,9 @@ export class EventManager {
             this._callbacksContainer[templateName] = [];
         }
         this._callbacksContainer[templateName].push({
-            eventType: eventType,
-            callback: callback
+            eventType,
+            callback,
+            selector
         });
     }
 

+ 4 - 0
Viewer/src/index.ts

@@ -42,5 +42,9 @@ function disposeAll() {
 
 const Version = BABYLON.Engine.Version;
 
+console.log("Babylon.js viewer (v" + Version + ")");
+
 // public API for initialization
 export { BABYLON, Version, InitTags, DefaultViewer, AbstractViewer, viewerGlobals, telemetryManager, disableInit, viewerManager, mapperManager, disposeAll, ModelLoader, ViewerModel, AnimationPlayMode, AnimationState, ModelState, ILoaderPlugin };
+// export publicliy all configuration interfaces
+export * from './configuration';

+ 3 - 3
Viewer/src/labs/environmentSerializer.ts

@@ -2,9 +2,9 @@ import { Vector3, Tools } from "babylonjs";
 import { TextureCube, PixelFormat, PixelType } from './texture';
 
 /**
-	 * Spherical polynomial coefficients (counter part to spherical harmonic coefficients used in shader irradiance calculation)
-	 * @ignoreChildren
-	 */
+ * Spherical polynomial coefficients (counter part to spherical harmonic coefficients used in shader irradiance calculation)
+ * @ignoreChildren
+ */
 export interface SphericalPolynomalCoefficients {
     x: Vector3;
     y: Vector3;

+ 11 - 6
Viewer/src/labs/viewerLabs.ts

@@ -5,11 +5,16 @@ import { Tools, Quaternion } from 'babylonjs';
 import { ViewerConfiguration } from "../configuration/configuration";
 import { TextureUtils } from "./texture";
 
+/**
+ * The ViewerLabs class will hold functions that are not (!) backwards compatible.
+ * The APIs in all labs-related classes and configuration  might change.
+ * Once stable, lab features will be moved to the publis API and configuration object.
+ */
 export class ViewerLabs {
 
     constructor(private _sceneManager: SceneManager) { }
 
-    public environmentAssetsRootURL: string;
+    public assetsRootURL: string;
     public environment: PBREnvironment = {
         //irradiance
         irradiancePolynomialCoefficients: {
@@ -57,7 +62,7 @@ export class ViewerLabs {
             this.environment = EnvironmentDeserializer.Parse(data);
             if (onSuccess) onSuccess(this.environment);
         } else if (typeof data === 'string') {
-            let url = this.getEnvironmentAssetUrl(data);
+            let url = this.getAssetUrl(data);
             this._sceneManager.scene._loadFile(
                 url,
                 (arrayBuffer: ArrayBuffer) => {
@@ -124,15 +129,15 @@ export class ViewerLabs {
      * @param url Asset url
      * @returns The Asset url using the `environmentAssetsRootURL` if the url is not an absolute path.
      */
-    public getEnvironmentAssetUrl(url: string): string {
+    public getAssetUrl(url: string): string {
         let returnUrl = url;
         if (url && url.toLowerCase().indexOf("//") === -1) {
-            if (!this.environmentAssetsRootURL) {
-                Tools.Warn("Please, specify the root url of your assets before loading the configuration (labs.environmentAssetsRootURL) or disable the background through the viewer options.");
+            if (!this.assetsRootURL) {
+                // Tools.Warn("Please, specify the root url of your assets before loading the configuration (labs.environmentAssetsRootURL) or disable the background through the viewer options.");
                 return url;
             }
 
-            returnUrl = this.environmentAssetsRootURL + returnUrl;
+            returnUrl = this.assetsRootURL + returnUrl;
         }
 
         return returnUrl;

+ 33 - 4
Viewer/src/loader/modelLoader.ts

@@ -32,6 +32,11 @@ export class ModelLoader {
         this._plugins = [];
     }
 
+    /**
+     * Adds a new plugin to the loader process.
+     * 
+     * @param plugin the plugin name or the plugin itself
+     */
     public addPlugin(plugin: ILoaderPlugin | string) {
         let actualPlugin: ILoaderPlugin = {};
         if (typeof plugin === 'string') {
@@ -63,8 +68,18 @@ export class ModelLoader {
             return model;
         }
 
-        let filename = Tools.GetFilename(modelConfiguration.url) || modelConfiguration.url;
-        let base = modelConfiguration.root || Tools.GetFolderPath(modelConfiguration.url);
+        let base: string;
+        let filename: any;
+        if (modelConfiguration.file) {
+            base = "file:";
+            filename = modelConfiguration.file;
+        }
+        else {
+            filename = Tools.GetFilename(modelConfiguration.url) || modelConfiguration.url;
+            base = modelConfiguration.root || Tools.GetFolderPath(modelConfiguration.url);
+        }
+
+
         let plugin = modelConfiguration.loader;
 
         model.loader = SceneLoader.ImportMesh(undefined, base, filename, this._viewer.sceneManager.scene, (meshes, particleSystems, skeletons, animationGroups) => {
@@ -80,7 +95,9 @@ export class ModelLoader {
             }
 
             this._checkAndRun("onLoaded", model);
-            model.onLoadedObservable.notifyObserversWithPromise(model);
+            this._viewer.sceneManager.scene.executeWhenReady(() => {
+                model.onLoadedObservable.notifyObservers(model);
+            });
         }, (progressEvent) => {
             this._checkAndRun("onProgress", progressEvent);
             model.onLoadProgressObservable.notifyObserversWithPromise(progressEvent);
@@ -95,6 +112,12 @@ export class ModelLoader {
             let gltfLoader = (<GLTFFileLoader>model.loader);
             gltfLoader.animationStartMode = GLTFLoaderAnimationStartMode.NONE;
             gltfLoader.compileMaterials = true;
+
+            if (!modelConfiguration.file) {
+                gltfLoader.rewriteRootURL = (rootURL, responseURL) => {
+                    return modelConfiguration.root || Tools.GetFolderPath(responseURL || modelConfiguration.url || '');
+                };
+            }
             // if ground is set to "mirror":
             if (this._viewer.configuration.ground && typeof this._viewer.configuration.ground === 'object' && this._viewer.configuration.ground.mirror) {
                 gltfLoader.useClipPlane = true;
@@ -109,7 +132,13 @@ export class ModelLoader {
                 if (data && data.json && data.json['asset']) {
                     model.loadInfo = data.json['asset'];
                 }
-            })
+            });
+
+            gltfLoader.onCompleteObservable.add(() => {
+                model.loaderDone = true;
+            });
+        } else {
+            model.loaderDone = true;
         }
 
         this._checkAndRun("onInit", model.loader, model);

+ 21 - 0
Viewer/src/loader/plugins/applyMaterialConfig.ts

@@ -0,0 +1,21 @@
+import { ILoaderPlugin } from "./loaderPlugin";
+import { telemetryManager } from "../../telemetryManager";
+import { ViewerModel } from "../..";
+import { Tools, ISceneLoaderPlugin, ISceneLoaderPluginAsync, Material } from "babylonjs";
+import { IGLTFLoaderData, GLTF2 } from "babylonjs-loaders";
+
+/**
+ * Force-apply material configuration right after a material was loaded.
+ */
+export class ApplyMaterialConfigPlugin implements ILoaderPlugin {
+
+    private _model: ViewerModel;
+
+    public onInit(loader: ISceneLoaderPlugin | ISceneLoaderPluginAsync, model: ViewerModel) {
+        this._model = model;
+    }
+
+    public onMaterialLoaded(material: Material) {
+        this._model && this._model._applyModelMaterialConfiguration(material);
+    }
+}

+ 4 - 2
Viewer/src/loader/plugins/extendedMaterialLoaderPlugin.ts

@@ -3,10 +3,12 @@ import { telemetryManager } from "../../telemetryManager";
 import { ViewerModel } from "../..";
 import { Color3, Texture, BaseTexture, Tools, ISceneLoaderPlugin, ISceneLoaderPluginAsync, Material, PBRMaterial, Engine } from "babylonjs";
 
+/**
+ * A (PBR) material will be extended using this function.
+ * This function will hold extra default configuration for the viewer, if not implemented in Babylon itself.
+ */
 export class ExtendedMaterialLoaderPlugin implements ILoaderPlugin {
 
-    private _model: ViewerModel;
-
     public onMaterialLoaded(baseMaterial: Material) {
         var material = baseMaterial as PBRMaterial;
         material.alphaMode = Engine.ALPHA_PREMULTIPLIED_PORTERDUFF;

+ 8 - 2
Viewer/src/loader/plugins/index.ts

@@ -1,11 +1,17 @@
 import { TelemetryLoaderPlugin } from "./telemetryLoaderPlugin";
 import { ILoaderPlugin } from "./loaderPlugin";
 import { MSFTLodLoaderPlugin } from './msftLodLoaderPlugin';
-import { MinecraftLoaderPlugin } from './minecraftLoaderPlugin';
+import { ApplyMaterialConfigPlugin } from './applyMaterialConfig';
 import { ExtendedMaterialLoaderPlugin } from './extendedMaterialLoaderPlugin';
 
 const pluginCache: { [key: string]: ILoaderPlugin } = {};
 
+/**
+ * Get a loader plugin according to its name.
+ * The plugin will be cached and will be reused if called for again.
+ * 
+ * @param name the name of the plugin
+ */
 export function getLoaderPluginByName(name: string) {
     if (!pluginCache[name]) {
         switch (name) {
@@ -15,7 +21,7 @@ export function getLoaderPluginByName(name: string) {
             case 'msftLod':
                 pluginCache[name] = new MSFTLodLoaderPlugin();
                 break;
-            case 'minecraft':
+            case 'applyMaterialConfig':
                 pluginCache[name] = new MSFTLodLoaderPlugin();
                 break;
             case 'extendedMaterial':

+ 5 - 0
Viewer/src/loader/plugins/loaderPlugin.ts

@@ -2,6 +2,11 @@ import { ViewerModel } from "../../model/viewerModel";
 import { IGLTFLoaderExtension, IGLTFLoaderData } from "babylonjs-loaders";
 import { AbstractMesh, ISceneLoaderPlugin, ISceneLoaderPluginAsync, SceneLoaderProgressEvent, BaseTexture, Material } from "babylonjs";
 
+/**
+ * This interface defines the structure of a loader plugin.
+ * Any of those functions will be called if (!) the loader supports those callbacks.
+ * Any loader supports onInit, onLoaded, onError and onProgress.
+ */
 export interface ILoaderPlugin {
     onInit?: (loader: ISceneLoaderPlugin | ISceneLoaderPluginAsync, model: ViewerModel) => void;
     onLoaded?: (model: ViewerModel) => void;

+ 0 - 39
Viewer/src/loader/plugins/minecraftLoaderPlugin.ts

@@ -1,39 +0,0 @@
-import { ILoaderPlugin } from "./loaderPlugin";
-import { telemetryManager } from "../../telemetryManager";
-import { ViewerModel } from "../..";
-import { Tools, ISceneLoaderPlugin, ISceneLoaderPluginAsync, Material } from "babylonjs";
-import { IGLTFLoaderData, GLTF2 } from "babylonjs-loaders";
-
-
-export class MinecraftLoaderPlugin implements ILoaderPlugin {
-
-    private _model: ViewerModel;
-
-    private _minecraftEnabled: boolean;
-
-    public onInit(loader: ISceneLoaderPlugin | ISceneLoaderPluginAsync, model: ViewerModel) {
-        this._model = model;
-        this._minecraftEnabled = false;
-    }
-
-    public inParsed(data: IGLTFLoaderData) {
-        if (data && data.json && data.json['meshes'] && data.json['meshes'].length) {
-            var meshes = data.json['meshes'] as GLTF2.IMesh[];
-            for (var i = 0; i < meshes.length; i++) {
-                var mesh = meshes[i];
-                if (mesh && mesh.extras && mesh.extras.MSFT_minecraftMesh) {
-                    this._minecraftEnabled = true;
-                    break;
-                }
-            }
-        }
-    }
-
-    public onMaterialLoaded(material: Material) {
-        if (this._minecraftEnabled && material.needAlphaBlending()) {
-            material.forceDepthWrite = true;
-            material.backFaceCulling = true;
-            material.separateCullingPass = true;
-        }
-    }
-}

+ 3 - 1
Viewer/src/loader/plugins/msftLodLoaderPlugin.ts

@@ -4,7 +4,9 @@ import { ViewerModel } from "../..";
 import { Tools, ISceneLoaderPlugin, ISceneLoaderPluginAsync } from "babylonjs";
 import { IGLTFLoaderExtension, GLTF2 } from "babylonjs-loaders";
 
-
+/**
+ * A loder plugin to use MSFT_lod extension correctly (glTF)
+ */
 export class MSFTLodLoaderPlugin implements ILoaderPlugin {
 
     private _model: ViewerModel;

+ 1 - 1
Viewer/src/loader/plugins/telemetryLoaderPlugin.ts

@@ -17,7 +17,7 @@ export class TelemetryLoaderPlugin implements ILoaderPlugin {
     }
 
     public onLoaded(model: ViewerModel) {
-        telemetryManager.broadcast("Load First LOD Complete", model.getViewer(), {
+        telemetryManager.broadcast("Model Loaded", model.getViewer(), {
             model: model,
             loadTime: Tools.Now - this._loadStart
         });

+ 58 - 26
Viewer/src/model/modelAnimation.ts

@@ -1,9 +1,9 @@
-import { AnimationGroup, Animatable, Skeleton } from "babylonjs";
+import { AnimationGroup, Animatable, Skeleton, Vector3 } from "babylonjs";
 
 /**
  * Animation play mode enum - is the animation looping or playing once
  */
-export enum AnimationPlayMode {
+export const enum AnimationPlayMode {
     ONCE,
     LOOP
 }
@@ -11,7 +11,7 @@ export enum AnimationPlayMode {
 /**
  * An enum representing the current state of an animation object
  */
-export enum AnimationState {
+export const enum AnimationState {
     INIT,
     PLAYING,
     PAUSED,
@@ -20,6 +20,51 @@ export enum AnimationState {
 }
 
 /**
+ * The different type of easing functions available 
+ */
+export const enum EasingFunction {
+    Linear = 0,
+    CircleEase = 1,
+    BackEase = 2,
+    BounceEase = 3,
+    CubicEase = 4,
+    ElasticEase = 5,
+    ExponentialEase = 6,
+    PowerEase = 7,
+    QuadraticEase = 8,
+    QuarticEase = 9,
+    QuinticEase = 10,
+    SineEase = 11
+}
+
+/**
+ * Defines a simple animation to be applied to a model (scale).
+ */
+export interface ModelAnimationConfiguration {
+    /**
+     * Time of animation, in seconds
+     */
+    time: number;
+
+    /**
+     * Scale to apply
+     */
+    scaling?: Vector3;
+
+    /**
+     * Easing function to apply
+     * See SPECTRE.EasingFunction
+     */
+    easingFunction?: number;
+
+    /**
+     * An Easing mode to apply to the easing function
+     * See BABYLON.EasingFunction
+     */
+    easingMode?: number;
+}
+
+/**
  * This interface can be implemented to define new types of ModelAnimation objects.
  */
 export interface IModelAnimation {
@@ -146,11 +191,7 @@ export class GroupModelAnimation implements IModelAnimation {
      * In correlation to an arry, this would be ".length"
      */
     public get frames(): number {
-        let animationFrames = this._animationGroup.targetedAnimations.map(ta => {
-            let keys = ta.animation.getKeys();
-            return keys[keys.length - 1].frame;
-        });
-        return Math.max.apply(null, animationFrames);
+        return this._animationGroup.to - this._animationGroup.from;
     }
 
     /**
@@ -160,20 +201,11 @@ export class GroupModelAnimation implements IModelAnimation {
      * In correlation to an array, this would be the current index
      */
     public get currentFrame(): number {
-        // get the first currentFrame found
-        for (let i = 0; i < this._animationGroup.animatables.length; ++i) {
-            let animatable: Animatable = this._animationGroup.animatables[i];
-            let animations = animatable.getAnimations();
-            if (!animations || !animations.length) {
-                continue;
-            }
-            for (let idx = 0; idx < animations.length; ++idx) {
-                if (animations[idx].currentFrame) {
-                    return animations[idx].currentFrame;
-                }
-            }
+        if (this._animationGroup.targetedAnimations[0] && this._animationGroup.targetedAnimations[0].animation.runtimeAnimations[0]) {
+            return this._animationGroup.targetedAnimations[0].animation.runtimeAnimations[0].currentFrame - this._animationGroup.from;
+        } else {
+            return 0;
         }
-        return 0;
     }
 
     /**
@@ -234,7 +266,10 @@ export class GroupModelAnimation implements IModelAnimation {
      * Restart the animation group
      */
     restart() {
-        this._animationGroup.restart();
+        if (this.state === AnimationState.PAUSED)
+            this._animationGroup.restart();
+        else
+            this.start();
     }
 
     /**
@@ -242,10 +277,7 @@ export class GroupModelAnimation implements IModelAnimation {
      * @param frameNumber Go to a specific frame in the animation
      */
     goToFrame(frameNumber: number) {
-        // this._animationGroup.goToFrame(frameNumber);
-        this._animationGroup['_animatables'].forEach(a => {
-            a.goToFrame(frameNumber);
-        })
+        this._animationGroup.goToFrame(frameNumber + this._animationGroup.from);
     }
 
     /**

Datei-Diff unterdrückt, da er zu groß ist
+ 741 - 466
Viewer/src/model/viewerModel.ts


+ 104 - 0
Viewer/src/optimizer/custom/extended.ts

@@ -0,0 +1,104 @@
+import { AbstractViewer } from '../../viewer/viewer';
+import { Scalar, DefaultRenderingPipeline } from 'babylonjs';
+
+/**
+ * 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;
+    // 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);
+        return false;
+    }
+    if (!viewer.sceneManager.scene.postProcessesEnabled) {
+        viewer.sceneManager.scene.postProcessesEnabled = true;
+        return false;
+    }
+    if (!viewer.sceneManager.groundEnabled) {
+        viewer.sceneManager.groundEnabled = true;
+        return false;
+    }
+    if (defaultPipeline && !viewer.sceneManager.fxaaEnabled) {
+        viewer.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);
+        return false;
+    }
+    if (!viewer.sceneManager.processShadows) {
+        viewer.sceneManager.processShadows = true;
+        return false;
+    }
+    if (defaultPipeline && !viewer.sceneManager.bloomEnabled) {
+        viewer.sceneManager.bloomEnabled = true
+        return false;
+    }
+    if (!viewer.sceneManager.groundMirrorEnabled) {
+        viewer.sceneManager.groundMirrorEnabled = true;
+        return false;
+    }
+    return true;
+}
+
+/**
+ * A custom degrade-oriented function configuration for the scene optimizer.
+ * 
+ * @param viewer the viewer to optimize
+ */
+export function extendedDegrade(viewer: AbstractViewer): boolean {
+    let defaultPipeline = <DefaultRenderingPipeline>viewer.sceneManager.defaultRenderingPipeline;
+
+    if (viewer.sceneManager.groundMirrorEnabled) {
+        viewer.sceneManager.groundMirrorEnabled = false;
+        return false;
+    }
+    if (defaultPipeline && viewer.sceneManager.bloomEnabled) {
+        viewer.sceneManager.bloomEnabled = false;
+        return false;
+    }
+    if (viewer.sceneManager.processShadows) {
+        viewer.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);
+        return false;
+    }
+    if (defaultPipeline && viewer.sceneManager.fxaaEnabled) {
+        viewer.sceneManager.fxaaEnabled = false;
+        return false;
+    }
+    if (viewer.sceneManager.groundEnabled) {
+        viewer.sceneManager.groundEnabled = false;
+        return false;
+    }
+    if (viewer.sceneManager.scene.postProcessesEnabled) {
+        viewer.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);
+        return false;
+    }
+    // if (this.Scene.BackgroundHelper) {
+    // 	this.Scene.EngineScene.autoClear = true;
+    // this.Scene.BackgroundHelper = false;
+    // Would require a dedicated clear color;
+    // return false;
+    // }
+    return true;
+}

+ 25 - 0
Viewer/src/optimizer/custom/index.ts

@@ -0,0 +1,25 @@
+import { AbstractViewer } from "../../viewer/viewer";
+import { extendedUpgrade, extendedDegrade } from "./extended";
+
+const cache: { [key: string]: (viewer: AbstractViewer) => boolean } = {};
+
+/**
+ * 
+ * @param name the name of the custom optimizer configuration
+ * @param upgrade set to true if you want to upgrade optimizer and false if you want to degrade
+ */
+export function getCustomOptimizerByName(name: string, upgrade?: boolean) {
+    if (!cache[name]) {
+        switch (name) {
+            case 'extended':
+                if (upgrade) {
+                    return extendedUpgrade;
+                }
+                else {
+                    return extendedDegrade;
+                }
+        }
+    }
+
+    return cache[name];
+}

+ 3 - 0
Viewer/src/telemetryManager.ts

@@ -118,6 +118,9 @@ export class TelemetryManager {
         return this._currentSessionId;
     }
 
+    /**
+     * Disposes the telemetry manager
+     */
     public dispose() {
         this.onEventBroadcastedObservable.clear();
         delete this.onEventBroadcastedObservable;

+ 60 - 3
Viewer/src/templateManager.ts

@@ -7,10 +7,28 @@ import * as deepmerge from '../assets/deepmerge.min.js';
  * A single template configuration object
  */
 export interface ITemplateConfiguration {
+    /**
+     * can be either the id of the template's html element or a URL.
+     * See - http://doc.babylonjs.com/extensions/the_templating_system#location-vs-html
+     */
     location?: string; // #template-id OR http://example.com/loading.html
+    /**
+     * If no location is provided you can provide here the raw html of this template.
+     * See http://doc.babylonjs.com/extensions/the_templating_system#location-vs-html
+     */
     html?: string; // raw html string
     id?: string;
+    /**
+     * Parameters that will be delivered to the template and will render it accordingly.
+     */
     params?: { [key: string]: string | number | boolean | object };
+    /**
+     * Events to attach to this template.
+     * event name is the key. the value can either be a boolean (attach to the parent element)
+     * or a map of html id elements.
+     * 
+     * See - http://doc.babylonjs.com/extensions/the_templating_system#event-binding
+     */
     events?: {
         // pointer events
         pointerdown?: boolean | { [id: string]: boolean; };
@@ -27,6 +45,7 @@ export interface ITemplateConfiguration {
         dragstart?: boolean | { [id: string]: boolean; };
         drop?: boolean | { [id: string]: boolean; };
 
+
         [key: string]: boolean | { [id: string]: boolean; } | undefined;
     }
 }
@@ -241,6 +260,36 @@ Handlebars.registerHelper('eachInMap', function (map, block) {
     return out;
 });
 
+Handlebars.registerHelper('add', function (a, b) {
+    var out = a + b;
+    return out;
+});
+
+Handlebars.registerHelper('eq', function (a, b) {
+    var out = (a == b);
+    return out;
+});
+
+
+Handlebars.registerHelper('or', function (a, b) {
+    var out = a || b;
+    return out;
+});
+
+Handlebars.registerHelper('not', function (a) {
+    var out = !a;
+    return out;
+});
+
+Handlebars.registerHelper('count', function (map) {
+    return map.length;
+});
+
+Handlebars.registerHelper('gt', function (a, b) {
+    var out = a > b;
+    return out;
+});
+
 /**
  * This class represents a single template in the viewer's template tree.
  * An example for a template is a single canvas, an overlay (containing sub-templates) or the navigation bar.
@@ -318,7 +367,7 @@ export class Template {
         this.initPromise = htmlContentPromise.then(htmlTemplate => {
             if (htmlTemplate) {
                 this._htmlTemplate = htmlTemplate;
-                let compiledTemplate = Handlebars.compile(htmlTemplate);
+                let compiledTemplate = Handlebars.compile(htmlTemplate, { noEscape: (this._configuration.params && this._configuration.params.noEscape) });
                 let config = this._configuration.params || {};
                 this._rawHtml = compiledTemplate(config);
                 try {
@@ -341,6 +390,8 @@ export class Template {
      * The parameters are provided to Handlebars which in turn generates the template.
      * This function will update the template with the new parameters
      * 
+     * Note that when updating parameters the events will be registered again (after being cleared).
+     * 
      * @param params the new template parameters
      */
     public updateParams(params: { [key: string]: string | number | boolean | object }, append: boolean = true) {
@@ -569,8 +620,14 @@ export class Template {
 
                     // if boolean, set the parent as the event listener
                     if (typeof this._configuration.events[eventName] === 'boolean') {
-                        let binding = functionToFire.bind(this, '#' + this.parent.id);
-                        this.parent.addEventListener(eventName, functionToFire.bind(this, '#' + this.parent.id), false);
+                        let selector = this.parent.id
+                        if (selector) {
+                            selector = '#' + selector
+                        } else {
+                            selector = this.parent.tagName
+                        }
+                        let binding = functionToFire.bind(this, selector);
+                        this.parent.addEventListener(eventName, functionToFire.bind(this, selector), false);
                         this._registeredEvents.push({
                             htmlElement: this.parent,
                             eventName: eventName,

+ 211 - 69
Viewer/src/viewer/defaultViewer.ts

@@ -7,6 +7,7 @@ import { SpotLight, MirrorTexture, Plane, ShadowGenerator, Texture, BackgroundMa
 import { CameraBehavior } from '../interfaces';
 import { ViewerModel } from '../model/viewerModel';
 import { extendClassWithConfig } from '../helper';
+import { IModelAnimation, AnimationState } from '../model/modelAnimation';
 
 /**
  * The Default viewer is the default implementation of the AbstractViewer.
@@ -22,10 +23,6 @@ export class DefaultViewer extends AbstractViewer {
     constructor(public containerElement: HTMLElement, initialConfiguration: ViewerConfiguration = { extends: 'default' }) {
         super(containerElement, initialConfiguration);
         this.onModelLoadedObservable.add(this._onModelLoaded);
-        this.sceneManager.onSceneInitObservable.add(() => {
-            // extendClassWithConfig(this.sceneManager.scene, this._configuration.scene);
-            return this.sceneManager.scene;
-        });
 
         this.sceneManager.onLightsConfiguredObservable.add((data) => {
             this._configureLights(data.newConfiguration, data.model!);
@@ -55,48 +52,187 @@ export class DefaultViewer extends AbstractViewer {
     private _initNavbar() {
         let navbar = this.templateManager.getTemplate('navBar');
         if (navbar) {
-            let navbarHeight = navbar.parent.clientHeight + 'px';
-
-            let navbarShown: boolean = true;
-            let timeoutCancel /*: number*/;
-
-            let triggerNavbar = function (show: boolean = false, evt: PointerEvent) {
-                // only left-click on no-button.
-                if (!navbar || evt.button > 0) return;
-                // clear timeout
-                timeoutCancel && clearTimeout(timeoutCancel);
-                // if state is the same, do nothing
-                if (show === navbarShown) return;
-                //showing? simply show it!
-                if (show) {
-                    navbar.parent.style.bottom = show ? '0px' : '-' + navbarHeight;
-                    navbarShown = show;
+            this.onFrameRenderedObservable.add(this._updateProgressBar);
+            this.templateManager.eventManager.registerCallback('navBar', this._handlePointerDown, 'pointerdown');
+            // an example how to trigger the help button. publiclly available
+            this.templateManager.eventManager.registerCallback("navBar", () => {
+                // do your thing
+            }, "pointerdown", "#help-button");
+
+            this.templateManager.eventManager.registerCallback("navBar", (event: EventCallback) => {
+                const evt = event.event;
+                const element = <HTMLInputElement>(evt.target);
+                if (!this._currentAnimation) return;
+                const gotoFrame = +element.value / 100 * this._currentAnimation.frames;
+                if (isNaN(gotoFrame)) return;
+                this._currentAnimation.goToFrame(gotoFrame);
+            }, "input");
+
+            this.templateManager.eventManager.registerCallback("navBar", (e) => {
+                if (this._resumePlay) {
+                    this._togglePlayPause(true);
+                }
+                this._resumePlay = false;
+            }, "pointerup", "#progress-wrapper");
+        }
+    }
+
+    private _animationList: string[];
+    private _currentAnimation: IModelAnimation;
+    private _isAnimationPaused: boolean;
+    private _resumePlay: boolean;
+
+    private _handlePointerDown = (event: EventCallback) => {
+
+        let pointerDown = <PointerEvent>event.event;
+        if (pointerDown.button !== 0) return;
+        var element = (<HTMLElement>event.event.target);
+
+        if (!element) {
+            return;
+        }
+
+        let parentClasses = element.parentElement!.classList;
+
+        switch (element.id) {
+            case "speed-button":
+            case "types-button":
+                if (parentClasses.contains("open")) {
+                    parentClasses.remove("open");
                 } else {
-                    let visibilityTimeout = 2000;
-                    if (navbar.configuration.params && navbar.configuration.params.visibilityTimeout !== undefined) {
-                        visibilityTimeout = <number>navbar.configuration.params.visibilityTimeout;
-                    }
-                    // not showing? set timeout until it is removed.
-                    timeoutCancel = setTimeout(function () {
-                        if (navbar) {
-                            navbar.parent.style.bottom = '-' + navbarHeight;
-                        }
-                        navbarShown = show;
-                    }, visibilityTimeout);
+                    parentClasses.add("open");
+                }
+                break;
+            case "play-pause-button":
+                this._togglePlayPause();
+                break;
+            case "label-option-button":
+                var label = element.dataset["value"];
+                if (label) {
+                    this._updateAnimationType(label);
+                }
+                break;
+            case "speed-option-button":
+                if (!this._currentAnimation) {
+                    return;
+                }
+                var speed = element.dataset["value"];
+                if (speed)
+                    this._updateAnimationSpeed(speed);
+                break;
+            case "progress-wrapper":
+                this._resumePlay = !this._isAnimationPaused;
+                if (this._resumePlay) {
+                    this._togglePlayPause(true);
                 }
+                break;
+            case "fullscreen-button":
+                this.toggleFullscreen();
+            default:
+                return;
+        }
+    }
+
+    /**
+     * Plays or Pauses animation
+     */
+    private _togglePlayPause = (noUiUpdate?: boolean) => {
+        if (!this._currentAnimation) {
+            return;
+        }
+        if (this._isAnimationPaused) {
+            this._currentAnimation.restart();
+        } else {
+            this._currentAnimation.pause();
+        }
+
+        this._isAnimationPaused = !this._isAnimationPaused;
+
+        if (noUiUpdate) return;
+
+        let navbar = this.templateManager.getTemplate('navBar');
+        if (!navbar) return;
+
+        navbar.updateParams({
+            paused: this._isAnimationPaused,
+        });
+    }
+
+    private _oldIdleRotationValue: number;
+
+    /**
+     * Control progress bar position based on animation current frame
+     */
+    private _updateProgressBar = () => {
+        let navbar = this.templateManager.getTemplate('navBar');
+        if (!navbar) return;
+        var progressSlider = <HTMLInputElement>navbar.parent.querySelector("input#progress-wrapper");
+        if (progressSlider && this._currentAnimation) {
+            const progress = this._currentAnimation.currentFrame / this._currentAnimation.frames * 100;
+            var currentValue = progressSlider.valueAsNumber;
+            if (Math.abs(currentValue - progress) > 0.5) { // Only move if greater than a 1% change
+                progressSlider.value = '' + progress;
             }
 
-            this.templateManager.eventManager.registerCallback('viewer', triggerNavbar.bind(this, false), 'pointerout');
-            this.templateManager.eventManager.registerCallback('viewer', triggerNavbar.bind(this, true), 'pointerdown');
-            this.templateManager.eventManager.registerCallback('viewer', triggerNavbar.bind(this, false), 'pointerup');
-            this.templateManager.eventManager.registerCallback('navBar', triggerNavbar.bind(this, true), 'pointerover');
+            if (this._currentAnimation.state === AnimationState.PLAYING) {
+                if (this.sceneManager.camera.autoRotationBehavior && !this._oldIdleRotationValue) {
+                    this._oldIdleRotationValue = this.sceneManager.camera.autoRotationBehavior.idleRotationSpeed;
+                    this.sceneManager.camera.autoRotationBehavior.idleRotationSpeed = 0;
+                }
+            } else {
+                if (this.sceneManager.camera.autoRotationBehavior && this._oldIdleRotationValue) {
+                    this.sceneManager.camera.autoRotationBehavior.idleRotationSpeed = this._oldIdleRotationValue;
+                    this._oldIdleRotationValue = 0;
+                }
+            }
+        }
+    }
+
+    /** 
+     * Update Current Animation Speed
+     */
+    private _updateAnimationSpeed = (speed: string, paramsObject?: any) => {
+        let navbar = this.templateManager.getTemplate('navBar');
+        if (!navbar) return;
+
+        if (speed && this._currentAnimation) {
+            this._currentAnimation.speedRatio = parseFloat(speed);
+            if (!this._isAnimationPaused) {
+                this._currentAnimation.restart();
+            }
+
+            if (paramsObject) {
+                paramsObject.selectedSpeed = speed + "x"
+            } else {
+                navbar.updateParams({
+                    selectedSpeed: speed + "x",
+                });
+            }
+        }
+    }
+
+    /** 
+     * Update Current Animation Type
+     */
+    private _updateAnimationType = (label: string, paramsObject?: any) => {
+        let navbar = this.templateManager.getTemplate('navBar');
+        if (!navbar) return;
 
-            this.templateManager.eventManager.registerCallback('navBar', this.toggleFullscreen, 'pointerdown', '#fullscreen-button');
-            this.templateManager.eventManager.registerCallback('navBar', (data) => {
-                if (data && data.event && data.event.target)
-                    this.sceneManager.models[0].playAnimation(data.event.target['value']);
-            }, 'change', '#animation-selector');
+        if (label) {
+            this._currentAnimation = this.sceneManager.models[0].setCurrentAnimationByName(label);
+        }
+
+        if (paramsObject) {
+            paramsObject.selectedAnimation = (this._animationList.indexOf(label) + 1);
+            paramsObject.selectedAnimationName = label;
+        } else {
+            navbar.updateParams({
+                selectedAnimation: (this._animationList.indexOf(label) + 1),
+                selectedAnimationName: label
+            });
         }
+
+        this._updateAnimationSpeed("1.0", paramsObject);
     }
 
     /**
@@ -123,6 +259,7 @@ export class DefaultViewer extends AbstractViewer {
      */
     protected _prepareContainerElement() {
         this.containerElement.style.position = 'relative';
+        this.containerElement.style.height = '100%';
         this.containerElement.style.display = 'flex';
     }
 
@@ -135,32 +272,28 @@ export class DefaultViewer extends AbstractViewer {
         let navbar = this.templateManager.getTemplate('navBar');
         if (!navbar) return;
 
-        if (model.getAnimationNames().length > 1) {
-            navbar.updateParams({ animations: model.getAnimationNames() });
-        }
-
-        let modelConfiguration = model.configuration;
-
-        let metadataContainer = navbar.parent.querySelector('#model-metadata');
-        if (metadataContainer) {
-            if (modelConfiguration.title !== undefined) {
-                let element = metadataContainer.querySelector('span.model-title');
-                if (element) {
-                    element.innerHTML = modelConfiguration.title;
-                }
-            }
-
-            if (modelConfiguration.subtitle !== undefined) {
-                let element = metadataContainer.querySelector('span.model-subtitle');
-                if (element) {
-                    element.innerHTML = modelConfiguration.subtitle;
+        let newParams: any = {};
+
+        let animationNames = model.getAnimationNames();
+        if (animationNames.length >= 1) {
+            this._isAnimationPaused = (model.configuration.animation && !model.configuration.animation.autoStart) || !model.configuration.animation;
+            this._animationList = animationNames;
+            newParams.animations = this._animationList;
+            newParams.paused = this._isAnimationPaused;
+            let animationIndex = 0;
+            if (model.configuration.animation && typeof model.configuration.animation.autoStart === 'string') {
+                animationIndex = animationNames.indexOf(model.configuration.animation.autoStart);
+                if (animationIndex === -1) {
+                    animationIndex = 0;
                 }
             }
+            this._updateAnimationType(animationNames[animationIndex], newParams);
+        }
 
-            if (modelConfiguration.thumbnail !== undefined) {
-                (<HTMLDivElement>metadataContainer.querySelector('.thumbnail')).style.backgroundImage = `url('${modelConfiguration.thumbnail}')`;
-            }
+        if (model.configuration.thumbnail) {
+            newParams.logoImage = model.configuration.thumbnail
         }
+        navbar.updateParams(newParams);
     }
 
     /**
@@ -169,9 +302,12 @@ export class DefaultViewer extends AbstractViewer {
      * The scene will automatically be cleared of the old models, if exist.
      * @param model the configuration object (or URL) to load.
      */
-    public loadModel(model: any = this._configuration.model): Promise<ViewerModel> {
+    public loadModel(model?: string | IModelConfiguration): Promise<ViewerModel> {
+        if (!model) {
+            model = this.configuration.model;
+        }
         this.showLoadingScreen();
-        return super.loadModel(model, true).catch((error) => {
+        return super.loadModel(model!, true).catch((error) => {
             console.log(error);
             this.hideLoadingScreen();
             this.showOverlayScreen('error');
@@ -182,12 +318,14 @@ export class DefaultViewer extends AbstractViewer {
     private _onModelLoaded = (model: ViewerModel) => {
         this._configureTemplate(model);
         // with a short timeout, making sure everything is there already.
-        let hideLoadingDelay = 500;
+        let hideLoadingDelay = 20;
         if (this._configuration.lab && this._configuration.lab.hideLoadingDelay !== undefined) {
             hideLoadingDelay = this._configuration.lab.hideLoadingDelay;
         }
         setTimeout(() => {
-            this.hideLoadingScreen();
+            this.sceneManager.scene.executeWhenReady(() => {
+                this.hideLoadingScreen();
+            });
         }, hideLoadingDelay);
 
         return;
@@ -205,7 +343,6 @@ export class DefaultViewer extends AbstractViewer {
         return template.show((template => {
 
             var canvasRect = this.containerElement.getBoundingClientRect();
-            var canvasPositioning = window.getComputedStyle(this.containerElement).position;
 
             template.parent.style.display = 'flex';
             template.parent.style.width = canvasRect.width + "px";
@@ -284,14 +421,19 @@ export class DefaultViewer extends AbstractViewer {
         return template.show((template => {
 
             var canvasRect = this.containerElement.getBoundingClientRect();
-            var canvasPositioning = window.getComputedStyle(this.containerElement).position;
+            // var canvasPositioning = window.getComputedStyle(this.containerElement).position;
 
             template.parent.style.display = 'flex';
             template.parent.style.width = canvasRect.width + "px";
             template.parent.style.height = canvasRect.height + "px";
             template.parent.style.opacity = "1";
             // from the configuration!!!
-            template.parent.style.backgroundColor = "black";
+            let color = "black";
+            if (this.configuration.templates && this.configuration.templates.loadingScreen) {
+                color = (this.configuration.templates.loadingScreen.params &&
+                    <string>this.configuration.templates.loadingScreen.params.backgroundColor) || color;
+            }
+            template.parent.style.backgroundColor = color;
             return Promise.resolve(template);
         }));
     }

Datei-Diff unterdrückt, da er zu groß ist
+ 409 - 151
Viewer/src/viewer/sceneManager.ts


Datei-Diff unterdrückt, da er zu groß ist
+ 37 - 30
Viewer/src/viewer/viewer.ts


+ 50 - 35
Viewer/tests/unit/src/viewer/viewer.ts

@@ -76,7 +76,7 @@ describe('Viewer', function () {
         let viewer: DefaultViewer = <DefaultViewer>Helper.getNewViewerInstance();
         let renderCount = 0;
         let sceneRenderCount = 0;
-        viewer.onSceneInitObservable.add(() => {
+        viewer.onSceneInitObservable.add((scene) => {
             viewer.sceneManager.scene.registerBeforeRender(() => {
                 sceneRenderCount++;
             });
@@ -174,13 +174,15 @@ describe('Viewer', function () {
     it('should render in background if set to true', (done) => {
         let viewer = Helper.getNewViewerInstance();
         viewer.onInitDoneObservable.add(() => {
-            assert.isFalse(viewer.engine.renderEvenInBackground, "Engine is rendering in background");
+            assert.isTrue(viewer.engine.renderEvenInBackground, "Engine is rendering in background");
+            assert.equal(viewer.engine.renderEvenInBackground, viewer.renderInBackground, "engine render in background should be equal to the viewer's");
             viewer.updateConfiguration({
                 scene: {
-                    renderInBackground: true
+                    renderInBackground: false
                 }
             });
-            assert.isTrue(viewer.engine.renderEvenInBackground, "Engine is not rendering in background");
+            assert.isFalse(viewer.engine.renderEvenInBackground, "Engine is not rendering in background");
+            assert.equal(viewer.engine.renderEvenInBackground, viewer.renderInBackground, "engine render in background should be equal to the viewer's");
             viewer.dispose();
             done();
         });
@@ -260,6 +262,50 @@ describe('Viewer', function () {
             viewer.forceRender();
         });
     });
+
+    it('should have the correct base ID', (done) => {
+        let element = document.createElement("div");
+        let randomString = "" + Math.random();
+        element.id = randomString;
+        let viewer = Helper.getNewViewerInstance(element);
+        assert.equal(viewer.baseId, viewer.containerElement.id);
+        assert.equal(randomString, viewer.baseId);
+        viewer.dispose();
+        done();
+    });
+
+    it('should update the configuration object when updateConfiguration is called', (done) => {
+        let randomVersion = "" + Math.random();
+        let viewer = Helper.getNewViewerInstance(undefined, {
+            version: randomVersion
+        });
+        viewer.onInitDoneObservable.add(() => {
+            assert.equal(viewer.configuration.version, randomVersion);
+            let newRandom = "" + Math.random();
+            viewer.updateConfiguration({
+                version: newRandom
+            });
+            assert.equal(viewer.configuration.version, newRandom);
+            viewer.dispose();
+            done();
+        });
+    });
+
+    it('should not init engine if viewer is disposed right after created', (done) => {
+        let viewer = Helper.getNewViewerInstance();
+        viewer.dispose();
+        // wait a bit for the engine to initialize, if failed
+        let timeout = setTimeout(() => {
+            assert.isUndefined(viewer.engine);
+            done();
+        }, 1000);
+
+        viewer.onEngineInitObservable.add(() => {
+            assert.fail();
+            clearTimeout(timeout);
+            done();
+        });
+    });
 });
 
 //}
@@ -399,35 +445,4 @@ QUnit.test('Test getEnvironmentAssetUrl absolute root', function (assert) {
     assert.ok(viewer.getEnvironmentAssetUrl("http://foo.png") === "http://foo.png", "Absolute url should not be undefined with configuration.");
 });
 
-QUnit.test('unlockBabylonFeatures', function () {
-    let viewer = Helper.createViewer();
-
-    QUnit.assert.ok(viewer.Scene.EngineScene.shadowsEnabled, "shadowsEnabled");
-    QUnit.assert.ok(!viewer.Scene.EngineScene.particlesEnabled, "particlesEnabled");
-    QUnit.assert.ok(!viewer.Scene.EngineScene.collisionsEnabled, "collisionsEnabled");
-    QUnit.assert.ok(viewer.Scene.EngineScene.lightsEnabled, "lightsEnabled");
-    QUnit.assert.ok(viewer.Scene.EngineScene.texturesEnabled, "texturesEnabled");
-    QUnit.assert.ok(!viewer.Scene.EngineScene.lensFlaresEnabled, "lensFlaresEnabled");
-    QUnit.assert.ok(!viewer.Scene.EngineScene.proceduralTexturesEnabled, "proceduralTexturesEnabled");
-    QUnit.assert.ok(viewer.Scene.EngineScene.renderTargetsEnabled, "renderTargetsEnabled");
-    QUnit.assert.ok(!viewer.Scene.EngineScene.spritesEnabled, "spritesEnabled");
-    QUnit.assert.ok(viewer.Scene.EngineScene.skeletonsEnabled, "skeletonsEnabled");
-    QUnit.assert.ok(!viewer.Scene.EngineScene.audioEnabled, "audioEnabled");
-
-    viewer.unlockBabylonFeatures();
-
-    QUnit.assert.ok(viewer.Scene.EngineScene.shadowsEnabled, "shadowsEnabled");
-    QUnit.assert.ok(viewer.Scene.EngineScene.particlesEnabled, "particlesEnabled");
-    QUnit.assert.ok(viewer.Scene.EngineScene.postProcessesEnabled, "postProcessesEnabled");
-    QUnit.assert.ok(viewer.Scene.EngineScene.collisionsEnabled, "collisionsEnabled");
-    QUnit.assert.ok(viewer.Scene.EngineScene.lightsEnabled, "lightsEnabled");
-    QUnit.assert.ok(viewer.Scene.EngineScene.texturesEnabled, "texturesEnabled");
-    QUnit.assert.ok(viewer.Scene.EngineScene.lensFlaresEnabled, "lensFlaresEnabled");
-    QUnit.assert.ok(viewer.Scene.EngineScene.proceduralTexturesEnabled, "proceduralTexturesEnabled");
-    QUnit.assert.ok(viewer.Scene.EngineScene.renderTargetsEnabled, "renderTargetsEnabled");
-    QUnit.assert.ok(viewer.Scene.EngineScene.spritesEnabled, "spritesEnabled");
-    QUnit.assert.ok(viewer.Scene.EngineScene.skeletonsEnabled, "skeletonsEnabled");
-    QUnit.assert.ok(viewer.Scene.EngineScene.audioEnabled, "audioEnabled");
-});
-
 */

+ 5 - 1
Viewer/tests/unit/webpack.config.js

@@ -42,8 +42,12 @@ module.exports = {
             }
         },
         {
-            test: /\.(jpe?g|png|ttf|eot|svg|woff(2)?)(\?[a-z0-9=&.]+)?$/,
+            test: /\.(jpe?g|png|ttf|eot|svg?)(\?[a-z0-9=&.]+)?$/,
             use: 'base64-image-loader?limit=1000&name=[name].[ext]'
+        },
+        {
+            test: /\.(woff|ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
+            loader: 'base64-font-loader'
         }]
     },
     devServer: {

BIN
Viewer/tests/validation/ReferenceImages/BrainStem.png


BIN
Viewer/tests/validation/ReferenceImages/BrainStemEnv.png


BIN
Viewer/tests/validation/ReferenceImages/BrainStemTransformation.png


BIN
Viewer/tests/validation/ReferenceImages/CameraContrast0.png


BIN
Viewer/tests/validation/ReferenceImages/CameraContrast1.png


BIN
Viewer/tests/validation/ReferenceImages/CameraExposure0.png


BIN
Viewer/tests/validation/ReferenceImages/CameraExposure1.png


BIN
Viewer/tests/validation/ReferenceImages/Control.png


BIN
Viewer/tests/validation/ReferenceImages/ControlDefault.png


BIN
Viewer/tests/validation/ReferenceImages/Diffuse.png


BIN
Viewer/tests/validation/ReferenceImages/Emissive.png


BIN
Viewer/tests/validation/ReferenceImages/MainColorEnv0-0.png


BIN
Viewer/tests/validation/ReferenceImages/MainColorEnv0-100.png


BIN
Viewer/tests/validation/ReferenceImages/MainColorEnv0-50.png


BIN
Viewer/tests/validation/ReferenceImages/MainColorEnv100-0.png


BIN
Viewer/tests/validation/ReferenceImages/MainColorEnv100-100.png


BIN
Viewer/tests/validation/ReferenceImages/MainColorEnv100-50.png


BIN
Viewer/tests/validation/ReferenceImages/MainColorEnv50-0.png


BIN
Viewer/tests/validation/ReferenceImages/MainColorEnv50-100.png


+ 0 - 0
Viewer/tests/validation/ReferenceImages/MainColorEnv50-50.png


Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.