morphing.js
  1 /*{# Copyright (c) 2010-2012 Turbulenz Limited #}*/
  2 /*
  3 * @title: Morphing
  4 * @description:
  5 * This sample loads a model with four morph targets and blends between them.
  6 * This kind of geometry manipulation can make the model appear to stretch, squash or twist.
  7 * You can drag the sliders around to modify how much to apply each morph target to the original geometry.
  8 */
  9 /*{{ javascript("jslib/camera.js") }}*/
 10 /*{{ javascript("jslib/aabbtree.js") }}*/
 11 /*{{ javascript("jslib/shadermanager.js") }}*/
 12 /*{{ javascript("jslib/texturemanager.js") }}*/
 13 /*{{ javascript("jslib/effectmanager.js") }}*/
 14 /*{{ javascript("jslib/geometry.js") }}*/
 15 /*{{ javascript("jslib/material.js") }}*/
 16 /*{{ javascript("jslib/light.js") }}*/
 17 /*{{ javascript("jslib/scenenode.js") }}*/
 18 /*{{ javascript("jslib/scene.js") }}*/
 19 /*{{ javascript("jslib/scenedebugging.js") }}*/
 20 /*{{ javascript("jslib/renderingcommon.js") }}*/
 21 /*{{ javascript("jslib/defaultrendering.js") }}*/
 22 /*{{ javascript("jslib/resourceloader.js") }}*/
 23 /*{{ javascript("jslib/observer.js") }}*/
 24 /*{{ javascript("jslib/utilities.js") }}*/
 25 /*{{ javascript("jslib/requesthandler.js") }}*/
 26 /*{{ javascript("jslib/vertexbuffermanager.js") }}*/
 27 /*{{ javascript("jslib/indexbuffermanager.js") }}*/
 28 /*{{ javascript("jslib/services/turbulenzservices.js") }}*/
 29 /*{{ javascript("jslib/services/turbulenzbridge.js") }}*/
 30 /*{{ javascript("jslib/services/gamesession.js") }}*/
 31 /*{{ javascript("jslib/services/mappingtable.js") }}*/
 32 /*{{ javascript("scripts/sceneloader.js") }}*/
 33 /*{{ javascript("scripts/htmlcontrols.js") }}*/
 34 /*{{ javascript("scripts/motion.js") }}*/
 35 /*{{ javascript("scripts/morph.js") }}*/
 36 /*global TurbulenzEngine: true */
 37 /*global Morph: false */
 38 /*global MorphInstance: false */
 39 /*global MorphShape: false */
 40 /*global loadCustomFileShapeFn: false */
 41 /*global RequestHandler: false */
 42 /*global TextureManager: false */
 43 /*global ShaderManager: false */
 44 /*global EffectManager: false */
 45 /*global Camera: false */
 46 /*global CameraController: false */
 47 /*global Scene: false */
 48 /*global SceneLoader: false */
 49 /*global HTMLControls: false */
 50 /*global SceneNode: false */
 51 /*global DefaultRendering: false */
 52 /*global TurbulenzServices: false */
 53 TurbulenzEngine.onload = function onloadFn() {
 54     var errorCallback = function errorCallback(msg) {
 55         window.alert(msg);
 56     };
 57 
 58     var graphicsDeviceParameters = {};
 59     var graphicsDevice = TurbulenzEngine.createGraphicsDevice(graphicsDeviceParameters);
 60 
 61     if (!graphicsDevice.shadingLanguageVersion) {
 62         errorCallback("No shading language support detected.\nPlease check your graphics drivers are up to date.");
 63         graphicsDevice = null;
 64         return;
 65     }
 66 
 67     // Clear the background color of the engine window
 68     var clearColor = [0.5, 0.5, 0.5, 1.0];
 69     if (graphicsDevice.beginFrame()) {
 70         graphicsDevice.clear(clearColor);
 71         graphicsDevice.endFrame();
 72     }
 73 
 74     var mathDeviceParameters = {};
 75     var mathDevice = TurbulenzEngine.createMathDevice(mathDeviceParameters);
 76 
 77     var inputDeviceParameters = {};
 78     var inputDevice = TurbulenzEngine.createInputDevice(inputDeviceParameters);
 79 
 80     var requestHandlerParameters = {};
 81     var requestHandler = RequestHandler.create(requestHandlerParameters);
 82 
 83     var textureManager = TextureManager.create(graphicsDevice, requestHandler, null, errorCallback);
 84     var shaderManager = ShaderManager.create(graphicsDevice, requestHandler, null, errorCallback);
 85     var effectManager = EffectManager.create();
 86 
 87     var assetName = "models/duck_morph.dae";
 88 
 89     var shapes = {};
 90     var fileShapeCount = 0;
 91 
 92     var morphs = [];
 93     var morphNodes = [];
 94     var morphInstances = [];
 95 
 96     var currentMaterial = "blinnMorphMaterial";
 97     var materials = {
 98         debugNormalsMorphMaterial: {
 99             effect: "debug_normals_morph",
100             parameters: {
101                 morphWeights: [0.0, 0.0, 0.0]
102             }
103         },
104         blinnMorphMaterial: {
105             effect: "blinn_morph",
106             parameters: {
107                 diffuse: "textures/duck.png",
108                 morphWeights: [0.0, 0.0, 0.0]
109             }
110         }
111     };
112 
113     var setMaterial = function setMaterialFn(materialName) {
114         var morphInstancesLen = morphInstances.length;
115         for (var i = 0; i < morphInstancesLen; i += 1) {
116             morphInstances[i].setMaterial(materials[materialName]);
117         }
118     };
119 
120     var renderer;
121 
122     // The maximum number of morph shapes the effects/shader supports (includes base shape)
123     var maxMorphShapes = 4;
124 
125     // The weights that are modified at runtime, to calculate a relative morph
126     var weights = [0.0, 0.0, 0.0];
127 
128     // Slider settings for weights
129     var weightScaleMax = 1.0;
130     var weightScaleMin = 0.0;
131     var weightStep = 0.01;
132 
133     // Slider settings for node rotation
134     var rotation = 3.5;
135     var lastRotation = 0;
136     var rotationMax = Math.PI * 2;
137     var rotationMin = 0;
138     var rotationStep = 0.1;
139 
140     // Initial camera view
141     var lookAtScaleFactor = 0.4;
142     var cameraPos = mathDevice.v3Build(-5 * lookAtScaleFactor, 4 * lookAtScaleFactor, 5 * lookAtScaleFactor);
143     var lightPos = mathDevice.v3Build(-1 * lookAtScaleFactor, 50 * lookAtScaleFactor, -5 * lookAtScaleFactor);
144     var targetPos = mathDevice.v3Build(-0.3, 1, 0);
145     var up = mathDevice.v3Build(0, 1, 0);
146 
147     // Setup camera & controller
148     var camera = Camera.create(mathDevice);
149     camera.nearPlane = 0.05;
150     camera.lookAt(targetPos, up, cameraPos);
151     camera.updateViewMatrix();
152     camera.updateProjectionMatrix();
153 
154     var cameraController = CameraController.create(graphicsDevice, inputDevice, camera);
155     var maxSpeed = cameraController.maxSpeed;
156 
157     var scene = Scene.create(mathDevice);
158     var sceneLoader = SceneLoader.create();
159 
160     // Controls
161     var htmlControls = HTMLControls.create();
162 
163     htmlControls.addRadioControl({
164         id: "radio04",
165         groupName: "materialType",
166         radioIndex: 0,
167         value: "Debug Normals",
168         fn: function () {
169             currentMaterial = "debugNormalsMorphMaterial";
170             setMaterial(currentMaterial);
171         },
172         isDefault: false
173     });
174 
175     htmlControls.addRadioControl({
176         id: "radio03",
177         groupName: "materialType",
178         radioIndex: 1,
179         value: "Textured Blinn",
180         fn: function () {
181             currentMaterial = "blinnMorphMaterial";
182             setMaterial(currentMaterial);
183         },
184         isDefault: true
185     });
186 
187     var sliderTarget1ID = "slider1";
188     var sliderTarget2ID = "slider2";
189     var sliderTarget3ID = "slider3";
190     var sliderRotateID = "slider4";
191 
192     var registerSliders = function registerSlidersFn() {
193         htmlControls.addSliderControl({
194             id: sliderTarget1ID,
195             value: weights[0],
196             max: weightScaleMax,
197             min: weightScaleMin,
198             step: weightStep,
199             fn: function () {
200                 var val = this.value;
201                 weights[0] = val;
202                 htmlControls.updateSlider(sliderTarget1ID, val);
203             }
204         });
205 
206         htmlControls.addSliderControl({
207             id: sliderTarget2ID,
208             value: weights[1],
209             max: weightScaleMax,
210             min: weightScaleMin,
211             step: weightStep,
212             fn: function () {
213                 var val = this.value;
214                 weights[1] = val;
215                 htmlControls.updateSlider(sliderTarget2ID, val);
216             }
217         });
218 
219         htmlControls.addSliderControl({
220             id: sliderTarget3ID,
221             value: weights[2],
222             max: weightScaleMax,
223             min: weightScaleMin,
224             step: weightStep,
225             fn: function () {
226                 var val = this.value;
227                 weights[2] = val;
228                 htmlControls.updateSlider(sliderTarget3ID, val);
229             }
230         });
231 
232         htmlControls.addSliderControl({
233             id: sliderRotateID,
234             value: rotation,
235             max: rotationMax,
236             min: rotationMin,
237             step: rotationStep,
238             fn: function () {
239                 var val = this.value;
240                 rotation = val;
241                 htmlControls.updateSlider(sliderRotateID, val);
242             }
243         });
244 
245         htmlControls.register();
246     };
247 
248     // Initialize the previous frame time
249     var previousFrameTime;
250     var intervalID;
251 
252     // Callback for when the scene data is available, pre load
253     var preLoadScene = function preLoadSceneFn(data) {
254         var shapesArray;
255         var geometries = data.geometries;
256         if (geometries) {
257             for (var g in geometries) {
258                 if (geometries.hasOwnProperty(g)) {
259                     shapesArray = shapes[assetName];
260                     if (!shapesArray) {
261                         shapesArray = [];
262                         shapes[assetName] = shapesArray;
263                     }
264 
265                     shapesArray[shapesArray.length] = {
266                         weight: 0.0,
267                         shape: geometries[g]
268                     };
269                     fileShapeCount += 1;
270                 }
271             }
272         }
273     };
274 
275     var loadMorphShape = function loadMorphShapeFn(name, index) {
276         var shapeArray = shapes[name];
277         if (!shapeArray) {
278             return null;
279         }
280 
281         var shape = shapeArray[index];
282         if (!shape) {
283             return null;
284         }
285 
286         var morphShape = MorphShape.create(shape.weight);
287         var loadShapeParams = {
288             graphicsDevice: graphicsDevice,
289             useVertexBufferManager: false,
290             dynamicVertexBuffers: false
291         };
292 
293         // loadShapeParams also takes parameter "onGeometryDestroyed : function (data) {}"
294         // Can be used as a callback for when the geometry is destroyed
295         // Use a custom shape loader to process the shape data from file
296         loadCustomFileShape(name, morphShape, shape.shape, loadShapeParams);
297         return morphShape;
298     };
299 
300     // Callback for when the scene is loaded to assign nodes
301     function loadSceneFinished(scene) {
302         var morphShape = loadMorphShape(assetName, 0);
303         var morph = Morph.create(morphShape);
304         var morphShapes = [];
305 
306         var i;
307         for (i = 1; i < maxMorphShapes; i += 1) {
308             morphShape = loadMorphShape(assetName, i);
309             if (morphShape) {
310                 morphShapes[morphShapes.length] = morphShape;
311                 weights[i - 1] = morphShape.initialWeight;
312             }
313         }
314         if ((morphShapes.length !== 0) && morph.addShapes(morphShapes)) {
315             morphs.push(morph);
316         } else {
317             window.alert("Could not add shapes to morph. Please check the morph shapes are compatible");
318             return;
319         }
320 
321         var morphsLen = morphs.length;
322         for (i = 0; i < morphsLen; i += 1) {
323             morphNodes[i] = SceneNode.create({ name: "MorphNode" + i, dynamic: true });
324             scene.addRootNode(morphNodes[i]);
325         }
326 
327         for (var m in materials) {
328             if (materials.hasOwnProperty(m)) {
329                 scene.loadMaterial(graphicsDevice, textureManager, effectManager, m, materials[m]);
330             }
331         }
332 
333         // registerSliders having loaded the initial weights
334         registerSliders();
335     }
336 
337     //
338     // Update
339     //
340     var update = function updateFn() {
341         var transform = null;
342         var currentTime = TurbulenzEngine.time;
343         var deltaTime = (currentTime - previousFrameTime);
344         if (deltaTime > 0.1) {
345             deltaTime = 0.1;
346         }
347         cameraController.maxSpeed = (deltaTime * maxSpeed);
348 
349         inputDevice.update();
350 
351         cameraController.update();
352 
353         var deviceWidth = graphicsDevice.width;
354         var deviceHeight = graphicsDevice.height;
355         var aspectRatio = (deviceWidth / deviceHeight);
356         if (aspectRatio !== camera.aspectRatio) {
357             camera.aspectRatio = aspectRatio;
358             camera.updateProjectionMatrix();
359         }
360         camera.updateViewProjectionMatrix();
361 
362         if (rotation !== lastRotation) {
363             transform = mathDevice.m43FromAxisRotation(up, rotation);
364             lastRotation = rotation;
365         }
366 
367         var morphNodesLen = morphNodes.length;
368         for (var i = 0; i < morphNodesLen; i += 1) {
369             if (transform) {
370                 morphNodes[i].setLocalTransform(transform);
371             }
372             morphInstances[i].setWeights(mathDevice.v3Build(weights[0], weights[1], weights[2]));
373         }
374 
375         scene.update();
376 
377         renderer.update(graphicsDevice, camera, scene, currentTime);
378 
379         if (graphicsDevice.beginFrame()) {
380             if (renderer.updateBuffers(graphicsDevice, deviceWidth, deviceHeight)) {
381                 renderer.draw(graphicsDevice, clearColor);
382             }
383             graphicsDevice.endFrame();
384         }
385     };
386 
387     var loadingPostSceneEffects = function loadingPostSceneEffectsFn() {
388         var material;
389 
390         if (graphicsDevice.beginFrame()) {
391             graphicsDevice.clear(clearColor);
392             graphicsDevice.endFrame();
393         }
394 
395         if ((!textureManager || 0 === textureManager.getNumPendingTextures()) && (!shaderManager || 0 === shaderManager.getNumPendingShaders())) {
396             TurbulenzEngine.clearInterval(intervalID);
397 
398             for (var m in materials) {
399                 if (materials.hasOwnProperty(m)) {
400                     material = scene.getMaterial(m);
401                     if (material) {
402                         // Add additional references to materials, to avoid them being removed when not in use
403                         material.reference.add();
404                     }
405                     materials[m] = material;
406                 }
407             }
408 
409             var morphNodesLen = morphNodes.length;
410             var morphInstance;
411             for (var i = 0; i < morphNodesLen; i += 1) {
412                 morphInstance = MorphInstance.create(morphs[i], materials[currentMaterial]);
413                 morphNodes[i].addRenderable(morphInstance);
414                 morphInstances[i] = morphInstance;
415             }
416 
417             // All resources loaded, start the render update
418             intervalID = TurbulenzEngine.setInterval(update, 1000 / 60);
419         }
420     };
421 
422     var loadingScene = function loadingSceneFn() {
423         if (graphicsDevice.beginFrame()) {
424             graphicsDevice.clear(clearColor);
425             graphicsDevice.endFrame();
426         }
427 
428         if (sceneLoader.complete()) {
429             TurbulenzEngine.clearInterval(intervalID);
430 
431             // Starts the loop that will wait for resources required, post scene load
432             intervalID = TurbulenzEngine.setInterval(loadingPostSceneEffects, 1000 / 10);
433         }
434     };
435 
436     var loadingDefaultEffects = function loadingDefaultEffectsFn() {
437         if ((!textureManager || 0 === textureManager.getNumPendingTextures()) && (!shaderManager || 0 === shaderManager.getNumPendingShaders())) {
438             TurbulenzEngine.clearInterval(intervalID);
439 
440             // Start the loading of the scene
441             sceneLoader.load({
442                 scene: scene,
443                 append: false,
444                 assetPath: assetName,
445                 graphicsDevice: graphicsDevice,
446                 mathDevice: mathDevice,
447                 textureManager: textureManager,
448                 shaderManager: shaderManager,
449                 effectManager: effectManager,
450                 requestHandler: requestHandler,
451                 preSceneLoadFn: preLoadScene,
452                 postSceneLoadFn: loadSceneFinished
453             });
454 
455             // Starts the loop that will wait for the scene to load
456             intervalID = TurbulenzEngine.setInterval(loadingScene, 1000 / 10);
457         }
458     };
459 
460     var loadAssets = function loadAssetsFn() {
461         // Renderer for the scene (requires shader assets).
462         renderer = DefaultRendering.create(graphicsDevice, mathDevice, shaderManager, effectManager);
463         renderer.setAmbientColor(mathDevice.v3Build(0.3, 0.3, 0.4));
464         renderer.setDefaultTexture(textureManager.get("default"));
465 
466         // Setup light based on lookAtScaleFactor
467         renderer.setGlobalLightPosition(lightPos);
468 
469         // Register the effects that are required for morphing
470         MorphInstance.registerEffects(mathDevice, renderer, shaderManager, effectManager);
471 
472         // Starts the loop that will wait for the shaders and textures required by the default effects registered with the renderer
473         intervalID = TurbulenzEngine.setInterval(loadingDefaultEffects, 1000 / 10);
474     };
475 
476     var mappingTableReceived = function mappingTableReceivedFn(mappingTable) {
477         textureManager.setPathRemapping(mappingTable.urlMapping, mappingTable.assetPrefix);
478         shaderManager.setPathRemapping(mappingTable.urlMapping, mappingTable.assetPrefix);
479         sceneLoader.setPathRemapping(mappingTable.urlMapping, mappingTable.assetPrefix);
480 
481         loadAssets();
482     };
483 
484     var gameSessionCreated = function gameSessionCreatedFn(gameSession) {
485         TurbulenzServices.createMappingTable(requestHandler, gameSession, mappingTableReceived);
486     };
487     var gameSession = TurbulenzServices.createGameSession(requestHandler, gameSessionCreated);
488 
489     TurbulenzEngine.onunload = function destroyScene() {
490         TurbulenzEngine.clearInterval(intervalID);
491 
492         if (gameSession) {
493             gameSession.destroy();
494             gameSession = null;
495         }
496 
497         var material = null;
498         for (var m in materials) {
499             if (materials.hasOwnProperty(m)) {
500                 material = scene.getMaterial(m);
501                 if (material) {
502                     // Remove additional references to materials, we no longer need them
503                     material.reference.remove();
504                 }
505             }
506         }
507         currentMaterial = null;
508 
509         sliderTarget1ID = null;
510         sliderTarget2ID = null;
511         sliderTarget3ID = null;
512         sliderRotateID = null;
513 
514         if (scene) {
515             scene.destroy();
516             scene = null;
517         }
518         requestHandler = null;
519         sceneLoader = null;
520 
521         cameraPos = null;
522         lightPos = null;
523         targetPos = null;
524         up = null;
525 
526         assetName = null;
527 
528         if (renderer) {
529             renderer.destroy();
530             renderer = null;
531         }
532 
533         htmlControls = null;
534         cameraController = null;
535         camera = null;
536 
537         weights = null;
538 
539         morphInstances = null;
540         morphNodes = null;
541         morphs = null;
542         shapes = null;
543 
544         materials = null;
545         clearColor = null;
546 
547         if (textureManager) {
548             textureManager.destroy();
549             textureManager = null;
550         }
551 
552         if (shaderManager) {
553             shaderManager.destroy();
554             shaderManager = null;
555         }
556 
557         effectManager = null;
558 
559         TurbulenzEngine.flush();
560         inputDevice = null;
561         graphicsDevice = null;
562         mathDevice = null;
563     };
564 
565     previousFrameTime = TurbulenzEngine.time;
566 };