animation.js
  1 /*{# Copyright (c) 2010-2012 Turbulenz Limited #}*/
  2 var __extends = this.__extends || function (d, b) {
  3     for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
  4     function __() { this.constructor = d; }
  5     __.prototype = b.prototype;
  6     d.prototype = new __();
  7 };
  8 /*
  9 * @title: Animation
 10 * @description:
 11 * This sample shows how to load multiple animations and build an animation controller hierarchy.
 12 * An animation transition controller is used to blend between two animations.
 13 * You can enable drawing of debug information to see the animation bounding box, the skeleton,
 14 * and the mesh wireframe.
 15 * You can also click on the rendering windows to move and rotate the camera around.
 16 */
 17 /*{{ javascript("jslib/aabbtree.js") }}*/
 18 /*{{ javascript("jslib/camera.js") }}*/
 19 /*{{ javascript("jslib/geometry.js") }}*/
 20 /*{{ javascript("jslib/material.js") }}*/
 21 /*{{ javascript("jslib/light.js") }}*/
 22 /*{{ javascript("jslib/scenenode.js") }}*/
 23 /*{{ javascript("jslib/scene.js") }}*/
 24 /*{{ javascript("jslib/vmath.js") }}*/
 25 /*{{ javascript("jslib/effectmanager.js") }}*/
 26 /*{{ javascript("jslib/shadermanager.js") }}*/
 27 /*{{ javascript("jslib/texturemanager.js") }}*/
 28 /*{{ javascript("jslib/animationmanager.js") }}*/
 29 /*{{ javascript("jslib/renderingcommon.js") }}*/
 30 /*{{ javascript("jslib/defaultrendering.js") }}*/
 31 /*{{ javascript("jslib/observer.js") }}*/
 32 /*{{ javascript("jslib/requesthandler.js") }}*/
 33 /*{{ javascript("jslib/resourceloader.js") }}*/
 34 /*{{ javascript("jslib/animation.js") }}*/
 35 /*{{ javascript("jslib/scenedebugging.js") }}*/
 36 /*{{ javascript("jslib/utilities.js") }}*/
 37 /*{{ javascript("jslib/vertexbuffermanager.js") }}*/
 38 /*{{ javascript("jslib/indexbuffermanager.js") }}*/
 39 /*{{ javascript("jslib/services/turbulenzservices.js") }}*/
 40 /*{{ javascript("jslib/services/turbulenzbridge.js") }}*/
 41 /*{{ javascript("jslib/services/gamesession.js") }}*/
 42 /*{{ javascript("jslib/services/mappingtable.js") }}*/
 43 /*{{ javascript("scripts/sceneloader.js") }}*/
 44 /*{{ javascript("scripts/motion.js") }}*/
 45 /*{{ javascript("scripts/htmlcontrols.js") }}*/
 46 /*global TurbulenzEngine: true */
 47 /*global TurbulenzServices: false */
 48 /*global RequestHandler: false */
 49 /*global TextureManager: false */
 50 /*global ShaderManager: false */
 51 /*global EffectManager: false */
 52 /*global Scene: false */
 53 /*global SceneLoader: false */
 54 /*global ResourceLoader: false */
 55 /*global Camera: false */
 56 /*global HTMLControls: false */
 57 /*global AnimationManager: false */
 58 /*global CameraController: false */
 59 /*global DefaultRendering: false */
 60 /*global SkinnedNode: false */
 61 /*global VMath: false */
 62 /*global GPUSkinController: false */
 63 /*global InterpolatorController: false */
 64 /*global ReferenceController: false */
 65 /*global NodeTransformController: false */
 66 /*global TransitionController: false */
 67 // We put some custom data onto Scene
 68 var CustomScene = (function (_super) {
 69     __extends(CustomScene, _super);
 70     function CustomScene() {
 71         _super.apply(this, arguments);
 72     }
 73     return CustomScene;
 74 })(Scene);
 75 ;
 76 
 77 TurbulenzEngine.onload = function onloadFn() {
 78     var errorCallback = function errorCallback(msg) {
 79         window.alert(msg);
 80     };
 81     TurbulenzEngine.onerror = errorCallback;
 82 
 83     var graphicsDeviceParameters = {};
 84     var graphicsDevice = TurbulenzEngine.createGraphicsDevice(graphicsDeviceParameters);
 85 
 86     if (!graphicsDevice.shadingLanguageVersion) {
 87         errorCallback("No shading language support detected.\nPlease check your graphics drivers are up to date.");
 88         graphicsDevice = null;
 89         return;
 90     }
 91 
 92     // Clear the background color of the engine window
 93     var clearColor = [0.5, 0.5, 0.5, 1.0];
 94     if (graphicsDevice.beginFrame()) {
 95         graphicsDevice.clear(clearColor);
 96         graphicsDevice.endFrame();
 97     }
 98 
 99     var mathDeviceParameters = {};
100     var mathDevice = TurbulenzEngine.createMathDevice(mathDeviceParameters);
101 
102     var inputDeviceParameters = {};
103     var inputDevice = TurbulenzEngine.createInputDevice(inputDeviceParameters);
104 
105     var requestHandlerParameters = {};
106     var requestHandler = RequestHandler.create(requestHandlerParameters);
107 
108     var textureManager = TextureManager.create(graphicsDevice, requestHandler, null, errorCallback);
109     var shaderManager = ShaderManager.create(graphicsDevice, requestHandler, null, errorCallback);
110     var effectManager = EffectManager.create();
111     var animationManager = AnimationManager.create(errorCallback);
112     var mappingTable;
113 
114     var resourceLoader = ResourceLoader.create();
115 
116     // Setup world space
117     var worldUp = mathDevice.v3Build(0.0, 1.0, 0.0);
118 
119     // Bounds are red, interpolatorColor is updated per animation
120     var boundsColor = [1, 0, 0];
121     var interpolatorColor = [0, 0, 0];
122 
123     // Setup a camera to view a close-up object
124     var camera = Camera.create(mathDevice);
125     camera.nearPlane = 0.05;
126     camera.updateViewMatrix();
127 
128     var animMinExtent, animMaxExtent;
129 
130     var cameraController = CameraController.create(graphicsDevice, inputDevice, camera);
131     var maxSpeed = cameraController.maxSpeed;
132     var cameraDistanceFactor = 1.0;
133     var cameraDir = [1, 1, 1];
134 
135     // Settings for the animation
136     var settings = {
137         animScale: 1,
138         defaultRate: 1,
139         drawDebug: false,
140         drawInterpolators: false,
141         drawWireframe: false,
142         loopAnimation: true,
143         blendAnimation: false,
144         transitionLength: 1
145     };
146 
147     // The default animation to start with
148     var defaultAnimIndex = 0;
149 
150     // The current playing animation
151     var curAnimIndex = 0;
152 
153     // The controller references by index
154     var controllerMap = [];
155 
156     // This is our base asset that includes a character and animations
157     var assetToLoad = "models/Seymour.dae";
158 
159     // The list of animations to load pre-scene load (by reference)
160     // This is only for animations that are not included in the default scene
161     // e.g. "animations/default_walking.anim"
162     var addAnimations = ["models/Seymour_anim2_rot90_anim_only.dae"];
163 
164     // The list of animations to be remove from the scene data pre-load
165     // This is for undesired animations that are packed in the scene
166     // All these animations are not required for this sample
167     var removeAnimations = [
168         "default_astroboy_w_skel02_gog_polySurface5",
169         "default_astroboy_w_skel02_polySurface5",
170         "default_astroboy_w_skel02c_gog_polySurface5",
171         "default_astroboy_w_skel02c_polySurface5",
172         "default_gog_polySurface5",
173         "default_polySurface5"
174     ];
175 
176     // The controller to blend the transisitions between animations, that don't have a matching key frame
177     var transitionController = null;
178     var transitionStartColor = [0, 0, 0];
179     var transitionEndColor = [0, 0, 0];
180 
181     // Reference controller for the whole animation
182     var currentReferenceController = null;
183     var currentNodeController = null;
184 
185     var animationsLoaded;
186 
187     // When the JSON is loaded, add a prefix to uniquely identify that set of animation data
188     var animationsLoadedCallback = function animationsLoadedCallbackFn(jsonData) {
189         var addAnimNum = (addAnimations.length - animationsLoaded);
190         animationManager.loadData(jsonData, "AnimExtra" + addAnimNum + "-");
191         animationsLoaded -= 1;
192     };
193 
194     var addAnimationsToScene = function addAnimationsToSceneFn() {
195         // Attach additional animations to the scene (specify by path)
196         // The animations are added by reference and the resourceLoader will attempt to load them using request
197         animationsLoaded = addAnimations.length;
198         for (var i = 0; i < addAnimations.length; i += 1) {
199             var path = addAnimations[i];
200             resourceLoader.load(mappingTable.getURL(path), {
201                 append: true,
202                 onload: animationsLoadedCallback,
203                 requestHandler: requestHandler
204             });
205         }
206     };
207 
208     var removeAnimationsFromScene = function removeAnimationsFromSceneFn(sceneData) {
209         // Remove unrequired animations from scene, if they exist before load
210         var anims = sceneData.animations;
211         var animationRef;
212 
213         if (anims) {
214             for (var i = 0; i < removeAnimations.length; i += 1) {
215                 animationRef = removeAnimations[i];
216                 if (anims[animationRef]) {
217                     delete anims[animationRef];
218                 }
219             }
220         }
221     };
222 
223     // Sets the next animation to play
224     var nextAnimation = function nextAnimationFn(nextAnimIndex) {
225         var controllers, interpStart, interpEnd, startColor, endColor, startSkinnedNode, endSkinnedNode, startNodes, endNodes;
226 
227         controllers = controllerMap[curAnimIndex];
228         if (controllers) {
229             interpStart = controllers.interpController;
230             startColor = controllers.color;
231             startSkinnedNode = controllers.skinnedNode;
232             startNodes = controllers.nodeCount;
233         }
234 
235         controllers = controllerMap[nextAnimIndex];
236         if (controllers) {
237             interpEnd = controllers.interpController;
238             endColor = controllers.color;
239             endSkinnedNode = controllers.skinnedNode;
240             endNodes = controllers.nodeCount;
241         }
242 
243         if (interpEnd) {
244             interpEnd.setTime(0);
245 
246             if (startSkinnedNode && (startSkinnedNode !== endSkinnedNode)) {
247                 startSkinnedNode.active = false;
248                 startSkinnedNode.setInputController(interpStart);
249             }
250 
251             if ((settings.blendAnimation || !interpStart) && (interpStart !== interpEnd) && (startNodes === endNodes)) {
252                 if (!transitionController) {
253                     transitionController = TransitionController.create(interpStart, interpEnd, settings.transitionLength);
254                     transitionController.onFinishedTransitionCallback = function onFinishedTransitionCallbackFn(/* transition */ ) {
255                         var controllers = controllerMap[curAnimIndex];
256                         if (controllers) {
257                             var skinnedNode = controllers.skinnedNode;
258                             currentReferenceController.setReferenceController(controllers.interpController);
259                             currentReferenceController.setTime(0);
260                             interpolatorColor = controllers.color;
261                             if (skinnedNode) {
262                                 skinnedNode.setInputController(currentReferenceController);
263                                 skinnedNode.active = true;
264                             }
265                         }
266 
267                         // Callback return value of 'false' tells the controller not to continue operating
268                         // This is because we have decided to set a different controller to be used instead
269                         return false;
270                     };
271                     transitionController.onUpdateCallback = function onUpdateCallbackFn(transition) {
272                         var delta = (transition.transitionTime / transition.transitionLength);
273                         interpolatorColor = VMath.v3Add(VMath.v3ScalarMul(transitionStartColor, 1 - delta), VMath.v3ScalarMul(transitionEndColor, delta));
274                     };
275                 } else {
276                     transitionController.setEndController(interpEnd);
277                     transitionController.setStartController(interpStart);
278                     transitionController.setTransitionLength(settings.transitionLength);
279                 }
280 
281                 currentReferenceController.setReferenceController(transitionController);
282                 transitionStartColor = startColor;
283                 transitionEndColor = endColor;
284             } else {
285                 // Set the node controller to be the reference controller
286                 currentReferenceController.setReferenceController(interpEnd);
287                 interpolatorColor = endColor;
288             }
289 
290             // Reset the animation about to play
291             currentReferenceController.setTime(0);
292 
293             if (endSkinnedNode) {
294                 endSkinnedNode.setInputController(currentReferenceController);
295                 endSkinnedNode.active = true;
296             }
297             curAnimIndex = nextAnimIndex;
298         }
299     };
300 
301     // Update the settings for an existing animation controller
302     var updateAnimations = function updateAnimationsFn(/* scene */ ) {
303         var i, controllers, interp;
304         var length = controllerMap.length;
305 
306         for (i = 0; i < length; i += 1) {
307             controllers = controllerMap[i];
308             if (controllers) {
309                 interp = controllers.interpController;
310                 if (interp) {
311                     interp.setAnimation(interp.currentAnim, settings.loopAnimation);
312                     interp.setRate(settings.defaultRate);
313                 }
314             }
315         }
316     };
317 
318     function calcAnimationExtents(scene, animationIndex) {
319         // Calculate the extents from the animation
320         var controllers = controllerMap[animationIndex];
321         if (controllers) {
322             var maxValue = Number.MAX_VALUE;
323             var animExtentMin0 = maxValue;
324             var animExtentMin1 = maxValue;
325             var animExtentMin2 = maxValue;
326             var animExtentMax0 = -maxValue;
327             var animExtentMax1 = -maxValue;
328             var animExtentMax2 = -maxValue;
329 
330             var bounds = controllers.animation.bounds;
331             var numFrames = bounds.length;
332             for (var i = 0; i < numFrames; i = i + 1) {
333                 var bound = bounds[i];
334                 var center = bound.center;
335                 var halfExtent = bound.halfExtent;
336                 var c0 = center[0];
337                 var c1 = center[1];
338                 var c2 = center[2];
339                 var h0 = halfExtent[0];
340                 var h1 = halfExtent[1];
341                 var h2 = halfExtent[2];
342                 var min0 = (c0 - h0);
343                 var min1 = (c1 - h1);
344                 var min2 = (c2 - h2);
345                 var max0 = (c0 + h0);
346                 var max1 = (c1 + h1);
347                 var max2 = (c2 + h2);
348                 animExtentMin0 = (animExtentMin0 < min0 ? animExtentMin0 : min0);
349                 animExtentMin1 = (animExtentMin1 < min1 ? animExtentMin1 : min1);
350                 animExtentMin2 = (animExtentMin2 < min2 ? animExtentMin2 : min2);
351                 animExtentMax0 = (animExtentMax0 > max0 ? animExtentMax0 : max0);
352                 animExtentMax1 = (animExtentMax1 > max1 ? animExtentMax1 : max1);
353                 animExtentMax2 = (animExtentMax2 > max2 ? animExtentMax2 : max2);
354             }
355 
356             animMinExtent = mathDevice.v3Build(animExtentMin0, animExtentMin1, animExtentMin2);
357             animMaxExtent = mathDevice.v3Build(animExtentMax0, animExtentMax1, animExtentMax2);
358         } else {
359             // Use the scene extents
360             var sceneExtents = scene.getExtents();
361             animMinExtent = mathDevice.v3Build(sceneExtents[0], sceneExtents[1], sceneExtents[2]);
362             animMaxExtent = mathDevice.v3Build(sceneExtents[3], sceneExtents[4], sceneExtents[5]);
363         }
364     }
365 
366     // Calculates a position for the camera to lookAt
367     var resetCamera = function resetCameraFn(camera, scene) {
368         calcAnimationExtents(scene, curAnimIndex);
369 
370         // Update the camera to scale to the size of the scene
371         var center = mathDevice.v3ScalarMul(mathDevice.v3Add(animMaxExtent, animMinExtent), 0.5);
372         var extent = mathDevice.v3Sub(center, animMinExtent);
373 
374         camera.lookAt(center, worldUp, mathDevice.v3Build(center[0] + extent[0] * cameraDistanceFactor * cameraDir[0] * 2, center[1] + extent[1] * cameraDistanceFactor * cameraDir[1], center[2] + extent[2] * cameraDistanceFactor * cameraDir[2] * 2));
375         camera.updateViewMatrix();
376 
377         // Calculates the appropriate nearPlane for the animation extents
378         var len = VMath.v3Length(extent);
379         if (len < 4.0) {
380             camera.nearPlane = len * 0.1;
381         } else {
382             camera.nearPlane = 1.0;
383         }
384         camera.farPlane = Math.ceil(len) * 100.0;
385         camera.updateProjectionMatrix();
386 
387         // Calculates the speed to move around the animation
388         maxSpeed = (len < 100 ? (len * 2) : (len * 0.5));
389     };
390 
391     // Create object using scene loader
392     var scene = (Scene.create(mathDevice));
393     var sceneLoader = SceneLoader.create();
394 
395     var renderer;
396     var loadAssets = function loadAssets() {
397         // Renderer for the scene (requires shader assets).
398         renderer = DefaultRendering.create(graphicsDevice, mathDevice, shaderManager, effectManager);
399 
400         renderer.setGlobalLightPosition(mathDevice.v3Build(0.5, 100.0, 0.5));
401         renderer.setAmbientColor(mathDevice.v3Build(0.3, 0.3, 0.4));
402         renderer.setDefaultTexture(textureManager.get("default"));
403 
404         sceneLoader.setPathRemapping(mappingTable.urlMapping, mappingTable.assetPrefix);
405         sceneLoader.load({
406             scene: scene,
407             assetPath: assetToLoad,
408             graphicsDevice: graphicsDevice,
409             mathDevice: mathDevice,
410             textureManager: textureManager,
411             effectManager: effectManager,
412             shaderManager: shaderManager,
413             animationManager: animationManager,
414             requestHandler: requestHandler,
415             keepVertexData: true,
416             preSceneLoadFn: function (sceneData) {
417                 // Apply the modifications to the data from assetPath
418                 addAnimationsToScene();
419                 removeAnimationsFromScene(sceneData);
420             },
421             keepLights: true,
422             append: true
423         });
424     };
425 
426     var mappingTableReceived = function mappingTableReceivedFn(mappingTable) {
427         textureManager.setPathRemapping(mappingTable.urlMapping, mappingTable.assetPrefix);
428         shaderManager.setPathRemapping(mappingTable.urlMapping, mappingTable.assetPrefix);
429         sceneLoader.setPathRemapping(mappingTable.urlMapping, mappingTable.assetPrefix);
430 
431         loadAssets();
432     };
433 
434     var gameSessionCreated = function gameSessionCreatedFn(gameSession) {
435         var defaultMappingSettings = {
436             mappingTablePrefix: "staticmax/",
437             assetPrefix: "missing/",
438             mappingTableURL: "mapping_table.json"
439         };
440         mappingTable = TurbulenzServices.createMappingTable(requestHandler, gameSession, mappingTableReceived, defaultMappingSettings);
441     };
442     var gameSession = TurbulenzServices.createGameSession(requestHandler, gameSessionCreated);
443 
444     // Controls
445     var htmlControls = HTMLControls.create();
446 
447     htmlControls.addButtonControl({
448         id: "button01",
449         value: "Next",
450         fn: function () {
451             nextAnimation((curAnimIndex + 1) % controllerMap.length);
452         }
453     });
454 
455     htmlControls.addButtonControl({
456         id: "button02",
457         value: "Previous",
458         fn: function () {
459             var index = (curAnimIndex - 1);
460             index = (index === -1) ? (controllerMap.length - 1) : index;
461             nextAnimation(index);
462         }
463     });
464 
465     htmlControls.addButtonControl({
466         id: "button03",
467         value: "Reset Camera",
468         fn: function () {
469             resetCamera(camera, scene);
470         }
471     });
472 
473     htmlControls.addCheckboxControl({
474         id: "checkbox01",
475         value: "loopAnimation",
476         isSelected: settings.loopAnimation,
477         fn: function () {
478             settings.loopAnimation = !settings.loopAnimation;
479             updateAnimations();
480             return settings.loopAnimation;
481         }
482     });
483 
484     htmlControls.addCheckboxControl({
485         id: "checkbox02",
486         value: "blendAnimation",
487         isSelected: settings.blendAnimation,
488         fn: function () {
489             settings.blendAnimation = !settings.blendAnimation;
490             return settings.blendAnimation;
491         }
492     });
493 
494     htmlControls.addCheckboxControl({
495         id: "checkbox03",
496         value: "drawDebug",
497         isSelected: settings.drawDebug,
498         fn: function () {
499             settings.drawDebug = !settings.drawDebug;
500             settings.drawWireframe = settings.drawDebug;
501             settings.drawInterpolators = settings.drawDebug;
502             return settings.drawDebug;
503         }
504     });
505 
506     htmlControls.register();
507 
508     // Provides a distinct array of colors to use for each controller
509     // Does not use red, black or white as they are reserved
510     var colorArray = [
511         [0.5, 1, 0.5],
512         [0.5, 1, 1],
513         [1, 1, 0.5],
514         [1, 0.5, 1],
515         [0.5, 0.5, 1]
516     ];
517     var lastColor = (colorArray.length - 1);
518     var getControllerColor = function getControllerColorFn() {
519         lastColor = (lastColor + 1) % colorArray.length;
520         return colorArray[lastColor];
521     };
522 
523     // Initialise all animations with InterpolatorControllers set to start time
524     var initAnimations = function initAnimationsFn(scene) {
525         var a, n, anim, interp, tempNode, skinnedNode, node, hierarchy;
526         var nodeHasSkeleton = animationManager.nodeHasSkeleton;
527         var sceneNodes = scene.rootNodes;
528         var numNodes = sceneNodes.length;
529         scene.skinnedNodes = [];
530 
531         for (n = 0; n < numNodes; n += 1) {
532             node = sceneNodes[n];
533             var skeleton = nodeHasSkeleton(node);
534             if (skeleton && skeleton.numNodes) {
535                 skinnedNode = SkinnedNode.create(graphicsDevice, mathDevice, node, skeleton);
536                 scene.skinnedNodes.push(skinnedNode);
537             }
538         }
539 
540         numNodes = scene.skinnedNodes.length;
541 
542         // For each animation, create an interpolation controller
543         var animations = animationManager.getAll();
544         for (a in animations) {
545             if (animations.hasOwnProperty(a)) {
546                 anim = animations[a];
547                 hierarchy = anim.hierarchy;
548 
549                 // Create an interpolator controller for each animation
550                 interp = InterpolatorController.create(hierarchy);
551                 interp.setAnimation(anim, settings.loopAnimation);
552                 interp.setTime(0);
553                 interp.setRate(settings.defaultRate);
554 
555                 skinnedNode = null;
556                 if (numNodes !== 0) {
557                     for (n = 0; n < numNodes; n += 1) {
558                         tempNode = scene.skinnedNodes[n];
559                         if (tempNode && (anim.numNodes === tempNode.skinController.skeleton.numNodes)) {
560                             skinnedNode = tempNode;
561                         }
562                     }
563                 }
564 
565                 // Controller map used for every interp controller
566                 controllerMap.push({
567                     interpController: interp,
568                     animation: anim,
569                     nodeCount: hierarchy.numNodes,
570                     skinnedNode: skinnedNode,
571                     color: getControllerColor()
572                 });
573             }
574         }
575 
576         var index = (controllerMap.length - 1);
577         if (index === -1) {
578             // Couldn't find any animations on the scene
579             return false;
580         }
581 
582         var controllers = controllerMap[defaultAnimIndex];
583         if (!controllers) {
584             // Pick the last added as a default
585             controllers = controllerMap[index];
586         } else {
587             index = defaultAnimIndex;
588         }
589 
590         var defaultInterp = controllers.interpController;
591         currentReferenceController = ReferenceController.create(defaultInterp);
592         hierarchy = currentReferenceController.getHierarchy();
593 
594         // Assumes hierarchy is the same for all anims
595         // If different hierarchies are used, create a new controller
596         currentNodeController = NodeTransformController.create(hierarchy, scene);
597         currentNodeController.setInputController(currentReferenceController);
598         currentNodeController.active = true;
599 
600         // Set the initial animation
601         nextAnimation(index);
602 
603         return true;
604     };
605 
606     // Callback to draw extra debug information
607     function drawDebugCB() {
608         if (currentNodeController.active) {
609             if (settings.drawInterpolators) {
610                 var interp = currentNodeController.inputController;
611                 var hierarchy = interp.getHierarchy();
612                 scene.drawAnimationHierarchy(graphicsDevice, shaderManager, camera, hierarchy, hierarchy.numNodes, interp, null, interpolatorColor, boundsColor);
613             }
614         }
615     }
616 
617     // Initialize the previous frame time
618     var previousFrameTime = TurbulenzEngine.time;
619 
620     var renderFrame = function renderFrameFn() {
621         var skinnedNodes, numSkins, skinnedNode, skin;
622 
623         var currentTime = TurbulenzEngine.time;
624         var deltaTime = (currentTime - previousFrameTime);
625         if (deltaTime > 0.1) {
626             deltaTime = 0.1;
627         }
628         cameraController.maxSpeed = (maxSpeed * deltaTime);
629 
630         // Update input
631         inputDevice.update();
632 
633         cameraController.update();
634 
635         var deviceWidth = graphicsDevice.width;
636         var deviceHeight = graphicsDevice.height;
637         var aspectRatio = (deviceWidth / deviceHeight);
638         if (aspectRatio !== camera.aspectRatio) {
639             camera.aspectRatio = aspectRatio;
640             camera.updateProjectionMatrix();
641         }
642         camera.updateViewProjectionMatrix();
643 
644         // Update the current animation by using the node controller
645         // The node controller input is the referenceNodeController
646         currentNodeController.addTime(deltaTime * settings.animScale);
647 
648         skinnedNodes = scene.skinnedNodes;
649         numSkins = skinnedNodes.length;
650 
651         for (skin = 0; skin < numSkins; skin += 1) {
652             skinnedNode = skinnedNodes[skin];
653             if (skinnedNode.active) {
654                 // The skinned node will peform the update
655                 skinnedNode.update();
656             }
657         }
658 
659         scene.update();
660 
661         renderer.update(graphicsDevice, camera, scene, currentTime);
662 
663         if (graphicsDevice.beginFrame()) {
664             if (skinnedNode.active) {
665                 renderer.setWireframe(settings.drawWireframe);
666 
667                 if (renderer.updateBuffers(graphicsDevice, deviceWidth, deviceHeight)) {
668                     renderer.draw(graphicsDevice, clearColor, null, null, drawDebugCB);
669                 }
670             }
671 
672             graphicsDevice.endFrame();
673         }
674 
675         previousFrameTime = currentTime;
676     };
677 
678     var intervalID;
679     var loadingLoop = function loadingLoopFn() {
680         if (sceneLoader.complete() && animationsLoaded === 0) {
681             TurbulenzEngine.clearInterval(intervalID);
682 
683             // Init the animations from the scene
684             initAnimations(scene);
685 
686             // Intial reset of the camera
687             resetCamera(camera, scene);
688 
689             // Scene loading is complete, now update the loaded shaders
690             renderer.updateShader(shaderManager);
691 
692             intervalID = TurbulenzEngine.setInterval(renderFrame, 1000 / 60);
693         }
694     };
695     intervalID = TurbulenzEngine.setInterval(loadingLoop, 1000 / 10);
696 
697     // Create a scene destroy callback to run when the window is closed
698     TurbulenzEngine.onunload = function destroyScene() {
699         TurbulenzEngine.clearInterval(intervalID);
700 
701         if (gameSession) {
702             gameSession.destroy();
703             gameSession = null;
704         }
705 
706         if (scene) {
707             scene.destroy();
708             scene = null;
709         }
710 
711         requestHandler = null;
712         sceneLoader = null;
713 
714         htmlControls = null;
715 
716         transitionController = null;
717         transitionStartColor = [];
718         transitionEndColor = [];
719 
720         currentReferenceController = null;
721         currentNodeController = null;
722 
723         if (renderer) {
724             renderer.destroy();
725             renderer = null;
726         }
727 
728         addAnimations = [];
729         removeAnimations = [];
730 
731         settings = null;
732 
733         cameraController = null;
734         camera = null;
735         cameraDir = [];
736 
737         mappingTable = null;
738 
739         animMinExtent = [];
740         animMaxExtent = [];
741 
742         boundsColor = [];
743         interpolatorColor = [];
744 
745         clearColor = [];
746         worldUp = null;
747 
748         if (textureManager) {
749             textureManager.destroy();
750             textureManager = null;
751         }
752 
753         if (shaderManager) {
754             shaderManager.destroy();
755             shaderManager = null;
756         }
757 
758         effectManager = null;
759 
760         TurbulenzEngine.flush();
761         graphicsDevice = null;
762         mathDevice = null;
763         inputDevice = null;
764     };
765 };