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