1 /*{# Copyright (c) 2012 Turbulenz Limited #}*/ 2 /* 3 * @title: 3D Physics benchmark 4 * @description: 5 * This sample is a benchmark for rigid body physics simulation with randomly generated boxes, spheres, cones, 6 * cylinders, capsules and convex hulls. 7 * The rigid bodies fall into a procedurally generated triangle mesh bowl that can be animated. 8 * The sample also shows the time spent on the different physics simulation phases. 9 * Disabling the debug rendering will show its impact on the framerate, the physics simulation will continue but 10 * without any graphics update. 11 */ 12 /*{{ javascript("jslib/aabbtree.js") }}*/ 13 /*{{ javascript("jslib/camera.js") }}*/ 14 /*{{ javascript("jslib/floor.js") }}*/ 15 /*{{ javascript("jslib/geometry.js") }}*/ 16 /*{{ javascript("jslib/material.js") }}*/ 17 /*{{ javascript("jslib/light.js") }}*/ 18 /*{{ javascript("jslib/scenenode.js") }}*/ 19 /*{{ javascript("jslib/scene.js") }}*/ 20 /*{{ javascript("jslib/vmath.js") }}*/ 21 /*{{ javascript("jslib/shadermanager.js") }}*/ 22 /*{{ javascript("jslib/renderingcommon.js") }}*/ 23 /*{{ javascript("jslib/resourceloader.js") }}*/ 24 /*{{ javascript("jslib/scenedebugging.js") }}*/ 25 /*{{ javascript("jslib/observer.js") }}*/ 26 /*{{ javascript("jslib/physicsmanager.js") }}*/ 27 /*{{ javascript("jslib/utilities.js") }}*/ 28 /*{{ javascript("jslib/vertexbuffermanager.js") }}*/ 29 /*{{ javascript("jslib/indexbuffermanager.js") }}*/ 30 /*{{ javascript("jslib/mouseforces.js") }}*/ 31 /*{{ javascript("jslib/utilities.js") }}*/ 32 /*{{ javascript("jslib/requesthandler.js") }}*/ 33 /*{{ javascript("jslib/services/turbulenzservices.js") }}*/ 34 /*{{ javascript("jslib/services/turbulenzbridge.js") }}*/ 35 /*{{ javascript("jslib/services/gamesession.js") }}*/ 36 /*{{ javascript("jslib/services/mappingtable.js") }}*/ 37 /*{{ javascript("scripts/htmlcontrols.js") }}*/ 38 /*{{ javascript("scripts/sceneloader.js") }}*/ 39 /*global TurbulenzEngine: true */ 40 /*global RequestHandler: false */ 41 /*global SceneLoader: false */ 42 /*global SceneNode: false */ 43 /*global TurbulenzServices: false */ 44 /*global ShaderManager: false */ 45 /*global Scene: false */ 46 /*global Camera: false */ 47 /*global CameraController: false */ 48 /*global Floor: false */ 49 /*global MouseForces: false */ 50 /*global PhysicsManager: false */ 51 /*global HTMLControls: false */ 52 TurbulenzEngine.onload = function onloadFn() { 53 var errorCallback = function errorCallback(msg) { 54 window.alert(msg); 55 }; 56 TurbulenzEngine.onerror = errorCallback; 57 58 var warningCallback = function warningCallback(msg) { 59 window.alert(msg); 60 }; 61 TurbulenzEngine.onwarning = warningCallback; 62 63 var mathDeviceParameters = {}; 64 var mathDevice = TurbulenzEngine.createMathDevice(mathDeviceParameters); 65 66 var graphicsDeviceParameters = {}; 67 var graphicsDevice = TurbulenzEngine.createGraphicsDevice(graphicsDeviceParameters); 68 69 var physicsDeviceParameters = {}; 70 var physicsDevice = TurbulenzEngine.createPhysicsDevice(physicsDeviceParameters); 71 72 var dynamicsWorldParameters = { 73 variableTimeSteps: true, 74 maxSubSteps: 2 75 }; 76 var dynamicsWorld = physicsDevice.createDynamicsWorld(dynamicsWorldParameters); 77 78 var inputDeviceParameters = {}; 79 var inputDevice = TurbulenzEngine.createInputDevice(inputDeviceParameters); 80 81 var requestHandlerParameters = {}; 82 var requestHandler = RequestHandler.create(requestHandlerParameters); 83 84 var shaderManager = ShaderManager.create(graphicsDevice, requestHandler, null, errorCallback); 85 var physicsManager = PhysicsManager.create(mathDevice, physicsDevice, dynamicsWorld); 86 87 var debugMode = true; 88 89 // Renderer and assets for the scene. 90 var scene = Scene.create(mathDevice); 91 var sceneLoader = SceneLoader.create(); 92 93 // Setup world space 94 var clearColor = mathDevice.v4Build(0.95, 0.95, 1.0, 1.0); 95 var loadingClearColor = mathDevice.v4Build(0.8, 0.8, 0.8, 1.0); 96 var worldUp = mathDevice.v3BuildYAxis(); 97 98 // Setup a camera to view a close-up object 99 var camera = Camera.create(mathDevice); 100 camera.nearPlane = 0.05; 101 var cameraDefaultPos = mathDevice.v3Build(0, 8.0, 18.1); 102 var cameraDefaultLook = mathDevice.v3Build(0, -(camera.farPlane / 4), -camera.farPlane); 103 104 // The objects needed to draw the crosshair 105 var technique2d; 106 var shader2d; 107 var techniqueParameters2d; 108 var chSemantics = graphicsDevice.createSemantics(['POSITION']); 109 var chFormats = [graphicsDevice.VERTEXFORMAT_FLOAT3]; 110 111 // Setup world floor 112 var floor = Floor.create(graphicsDevice, mathDevice); 113 var cameraController = CameraController.create(graphicsDevice, inputDevice, camera); 114 115 // Mouse forces 116 var dragMin = mathDevice.v3Build(-50, -50, -50); 117 var dragMax = mathDevice.v3Build(50, 50, 50); 118 var mouseForces = MouseForces.create(graphicsDevice, inputDevice, mathDevice, physicsDevice, dragMin, dragMax); 119 mouseForces.clamp = 400; 120 121 // Control codes 122 var keyCodes = inputDevice.keyCodes; 123 var mouseCodes = inputDevice.mouseCodes; 124 125 // Dynamic physics objects 126 var physicsObjects = []; 127 var bowlObject; 128 129 // Configuration of demo. 130 // Bowl radius and height 131 var bowlRadius = 9; 132 var bowlHeight = 5; 133 134 // Number of radial points, and planes in bowl. 135 var radialN = 30; 136 var depthN = 10; 137 138 // Control approximate size of objects 139 var objectSize = 0.5; 140 141 // Radius to place objects at in spiral 142 // y-displacement between each object. 143 // And start y position. 144 var genRadius = bowlRadius - 4; 145 var genDeltaY = 1; 146 var genStartY = 50; 147 var genStartSpeed = 60; 148 149 // Number of objects 150 var genCount = 100; 151 152 // Determine a suitable angular displacement between each object. 153 var genTheta = Math.asin(0.5 * Math.sqrt(100 * objectSize * objectSize + genDeltaY * genDeltaY) / genRadius); 154 155 // Whether bowl is animated. 156 var animateBowl = false; 157 var animateBowlTime = 0; 158 var prevAnimationTime = 0; 159 var animatedBowlAxis = mathDevice.v3Build(0, 0, 1); 160 var animatedBowlTransform = mathDevice.m43BuildIdentity(); 161 162 function reset() { 163 // Reset camera 164 camera.lookAt(cameraDefaultLook, worldUp, cameraDefaultPos); 165 camera.updateViewMatrix(); 166 167 // Reset physics object positions to new random values. 168 // We keep the physics objects that already exist to simplify things. 169 var n; 170 var maxN = physicsObjects.length; 171 for (n = 0; n < maxN; n += 1) { 172 var body = physicsObjects[n]; 173 dynamicsWorld.removeRigidBody(body); 174 var position = mathDevice.m43BuildTranslation(genRadius * Math.cos(n * genTheta), genStartY + (genDeltaY * n) * 3, genRadius * Math.sin(n * genTheta)); 175 body.transform = position; 176 body.linearVelocity = mathDevice.v3Build(0, -genStartSpeed, 0); 177 body.angularVelocity = mathDevice.v3BuildZero(); 178 body.active = true; 179 dynamicsWorld.addRigidBody(body); 180 } 181 } 182 183 var onMouseDown = function (button) { 184 if (mouseCodes.BUTTON_0 === button || mouseCodes.BUTTON_1 === button) { 185 mouseForces.onmousedown(); 186 } 187 }; 188 189 var onMouseUp = function (button) { 190 if (mouseCodes.BUTTON_0 === button || mouseCodes.BUTTON_1 === button) { 191 mouseForces.onmouseup(); 192 } 193 }; 194 195 var onKeyUp = function physicsOnkeyupFn(keynum) { 196 if (keynum === keyCodes.R) { 197 reset(); 198 } else { 199 cameraController.onkeyup(keynum); 200 } 201 }; 202 203 // Add event listeners 204 inputDevice.addEventListener("keyup", onKeyUp); 205 inputDevice.addEventListener("mousedown", onMouseDown); 206 inputDevice.addEventListener("mouseup", onMouseUp); 207 208 // Controls 209 var htmlControls = HTMLControls.create(); 210 htmlControls.addCheckboxControl({ 211 id: "checkbox01", 212 value: "debugMode", 213 isSelected: debugMode, 214 fn: function () { 215 debugMode = !debugMode; 216 return debugMode; 217 } 218 }); 219 htmlControls.addCheckboxControl({ 220 id: "checkbox02", 221 value: "animate", 222 isSelected: animateBowl, 223 fn: function () { 224 animateBowl = !animateBowl; 225 prevAnimationTime = TurbulenzEngine.time; 226 return animateBowl; 227 } 228 }); 229 htmlControls.register(); 230 231 function drawCrosshair() { 232 if (!mouseForces.pickedBody) { 233 graphicsDevice.setTechnique(technique2d); 234 235 var screenWidth = graphicsDevice.width; 236 var screenHeight = graphicsDevice.height; 237 techniqueParameters2d.clipSpace = mathDevice.v4Build(2.0 / screenWidth, -2.0 / screenHeight, -1.0, 1.0, techniqueParameters2d.clipSpace); 238 graphicsDevice.setTechniqueParameters(techniqueParameters2d); 239 240 var writer = graphicsDevice.beginDraw(graphicsDevice.PRIMITIVE_LINES, 4, chFormats, chSemantics); 241 242 if (writer) { 243 var halfWidth = screenWidth * 0.5; 244 var halfHeight = screenHeight * 0.5; 245 writer(halfWidth - 10, halfHeight); 246 writer(halfWidth + 10, halfHeight); 247 writer(halfWidth, halfHeight - 10); 248 writer(halfWidth, halfHeight + 10); 249 250 graphicsDevice.endDraw(writer); 251 } 252 } 253 } 254 255 var nextUpdate = 0; 256 257 var fpsElement = document.getElementById("fpscounter"); 258 var lastFPS = ""; 259 260 var discreteElement = document.getElementById("discrete"); 261 var preComputationsElement = document.getElementById("precomputations"); 262 var physicsIterationsElement = document.getElementById("physicsiterations"); 263 var continuousElement = document.getElementById("continuous"); 264 265 var lastDiscreteText = ""; 266 var lastPreComputationsText = ""; 267 var lastPhysicsIterationsText = ""; 268 var lastContinuousText = ""; 269 270 var discreteVal = -1; 271 var preComputationsVal = -1; 272 var physicsIterationsVal = -1; 273 var continuousVal = -1; 274 275 var displayPerformance = function displayPerformanceFn() { 276 if (TurbulenzEngine.canvas) { 277 var data = dynamicsWorld.performanceData; 278 var preval = data.sleepComputation + data.prestepContacts + data.prestepConstraints + data.integrateVelocities + data.warmstartContacts + data.warmstartConstraints; 279 var contval = data.integratePositions + data.continuous; 280 281 if (discreteVal === -1) { 282 discreteVal = data.discrete; 283 preComputationsVal = preval; 284 physicsIterationsVal = data.physicsIterations; 285 continuousVal = contval; 286 } else { 287 discreteVal = (0.95 * discreteVal) + (0.05 * data.discrete); 288 preComputationsVal = (0.95 * preComputationsVal) + (0.05 * preval); 289 physicsIterationsVal = (0.95 * physicsIterationsVal) + (0.05 * data.physicsIterations); 290 continuousVal = (0.95 * continuousVal) + (0.05 * contval); 291 } 292 } 293 294 var currentTime = TurbulenzEngine.time; 295 if (nextUpdate < currentTime) { 296 nextUpdate = (currentTime + 0.1); 297 298 if (fpsElement) { 299 var fpsText = (graphicsDevice.fps).toFixed(2); 300 if (lastFPS !== fpsText) { 301 lastFPS = fpsText; 302 303 fpsElement.innerHTML = fpsText + " fps"; 304 } 305 } 306 307 if (TurbulenzEngine.canvas) { 308 var discreteText = (1e3 * discreteVal).toFixed(2); 309 var preComputationsText = (1e3 * preComputationsVal).toFixed(2); 310 var physicsIterationsText = (1e3 * physicsIterationsVal).toFixed(2); 311 var continuousText = (1e3 * continuousVal).toFixed(2); 312 313 if (discreteElement && lastDiscreteText !== discreteText) { 314 lastDiscreteText = discreteText; 315 discreteElement.innerHTML = discreteText + " ms"; 316 } 317 318 if (preComputationsElement && lastPreComputationsText !== preComputationsText) { 319 lastPreComputationsText = preComputationsText; 320 preComputationsElement.innerHTML = preComputationsText + " ms"; 321 } 322 323 if (physicsIterationsElement && lastPhysicsIterationsText !== physicsIterationsText) { 324 lastPhysicsIterationsText = physicsIterationsText; 325 physicsIterationsElement.innerHTML = physicsIterationsText + " ms"; 326 } 327 328 if (continuousElement && lastContinuousText !== continuousText) { 329 lastContinuousText = continuousText; 330 continuousElement.innerHTML = continuousText + " ms"; 331 } 332 333 discreteVal = -1; 334 preComputationsVal = -1; 335 physicsIterationsVal = -1; 336 continuousVal = -1; 337 } 338 } 339 }; 340 341 // Functions to generate a physics object of a particular type. 342 var factories = [ 343 // Create a random box primitive 344 function boxFactoryFn() { 345 var width = objectSize + (Math.random() * objectSize); 346 var height = objectSize + (Math.random() * objectSize); 347 var depth = objectSize + (Math.random() * objectSize); 348 349 return physicsDevice.createBoxShape({ 350 halfExtents: mathDevice.v3Build(width, height, depth), 351 margin: 0.001 352 }); 353 }, 354 // Create a random convex hull primitive 355 function convexHullFactoryFn() { 356 var radius0 = (objectSize + (Math.random() * objectSize)) * 2.0; 357 var radius1 = (objectSize + (Math.random() * objectSize)) * 2.0; 358 var radius2 = (objectSize + (Math.random() * objectSize)) * 2.0; 359 var numPoints = Math.floor(5 + Math.random() * 30); 360 361 var positionsData = []; 362 var i; 363 for (i = 0; i < (numPoints * 3); i += 3) { 364 var azimuth = Math.random() * Math.PI * 2; 365 var elevation = (Math.random() - 0.5) * Math.PI; 366 positionsData[i] = Math.sin(azimuth) * Math.cos(elevation) * radius0; 367 positionsData[i + 1] = Math.cos(azimuth) * radius2; 368 positionsData[i + 2] = Math.sin(azimuth) * Math.sin(elevation) * radius1; 369 } 370 371 return physicsDevice.createConvexHullShape({ 372 points: positionsData, 373 margin: 0.001 374 }); 375 }, 376 // Create a random sphere primitive 377 function sphereFactoryFn() { 378 return physicsDevice.createSphereShape({ 379 radius: (objectSize + (Math.random() * objectSize)) * 1.5, 380 margin: 0.0 381 }); 382 }, 383 // Create a random capsule primitive 384 function capsuleFactoryFn() { 385 return physicsDevice.createCapsuleShape({ 386 radius: (objectSize + (Math.random() * objectSize)), 387 height: (objectSize + (Math.random() * objectSize)) * 2, 388 margin: 0.001 389 }); 390 }, 391 // Create a random cylinder primitive 392 function cylinderFactoryFn() { 393 var radius = (objectSize + (Math.random() * objectSize)); 394 var height = (objectSize + (Math.random() * objectSize)); 395 396 return physicsDevice.createCylinderShape({ 397 halfExtents: mathDevice.v3Build(radius, height, radius), 398 margin: 0.001 399 }); 400 }, 401 // Create a random cone primitive 402 function coneFactoryFn() { 403 return physicsDevice.createConeShape({ 404 radius: (objectSize + (Math.random() * objectSize)) * 1.5, 405 height: (objectSize + (Math.random() * objectSize)) * 3, 406 margin: 0.001 407 }); 408 } 409 ]; 410 411 var skipFrame = true; 412 413 var deferredObjectCreation = function deferredObjectCreationFn() { 414 var i = physicsObjects.length; 415 var shape = factories[Math.floor(factories.length * Math.random())](); 416 var position = mathDevice.m43BuildTranslation(genRadius * Math.cos(i * genTheta), genStartY + (genDeltaY * i), genRadius * Math.sin(i * genTheta)); 417 418 var sceneNode = SceneNode.create({ 419 name: "Phys" + i, 420 local: position, 421 dynamic: true, 422 disabled: false 423 }); 424 425 var rigidBody = physicsDevice.createRigidBody({ 426 shape: shape, 427 mass: 10.0, 428 inertia: mathDevice.v3ScalarMul(shape.inertia, 10.0), 429 transform: position, 430 friction: 0.8, 431 restitution: 0.2, 432 angularDamping: 0.4, 433 linearVelocity: mathDevice.v3Build(0, -genStartSpeed, 0) 434 }); 435 436 scene.addRootNode(sceneNode); 437 438 physicsManager.addNode(sceneNode, rigidBody); 439 440 physicsObjects.push(rigidBody); 441 }; 442 443 var renderFrame = function renderFrameFn() { 444 // Update input and camera 445 inputDevice.update(); 446 447 if (mouseForces.pickedBody) { 448 // If we're dragging a body don't apply the movement to the camera 449 cameraController.pitch = 0; 450 cameraController.turn = 0; 451 cameraController.step = 0; 452 } 453 454 cameraController.update(); 455 456 var deviceWidth = graphicsDevice.width; 457 var deviceHeight = graphicsDevice.height; 458 var aspectRatio = (deviceWidth / deviceHeight); 459 if (aspectRatio !== camera.aspectRatio) { 460 camera.aspectRatio = aspectRatio; 461 camera.updateProjectionMatrix(); 462 } 463 camera.updateViewProjectionMatrix(); 464 465 if (physicsObjects.length !== genCount && !skipFrame) { 466 deferredObjectCreation(); 467 } 468 skipFrame = !skipFrame; 469 470 if (animateBowl) { 471 animateBowlTime += (TurbulenzEngine.time - prevAnimationTime); 472 prevAnimationTime = TurbulenzEngine.time; 473 474 mathDevice.m43FromAxisRotation(animatedBowlAxis, 0.5 * Math.sin(animateBowlTime), animatedBowlTransform); 475 animatedBowlTransform[10] = Math.abs(7 * 0.5 * Math.sin(Math.sin(animateBowlTime))); 476 bowlObject.transform = animatedBowlTransform; 477 } 478 479 // Update the physics 480 mouseForces.update(dynamicsWorld, camera, 0.1); 481 dynamicsWorld.update(); 482 483 physicsManager.update(); 484 scene.update(); 485 486 scene.updateVisibleNodes(camera); 487 488 if (graphicsDevice.beginFrame()) { 489 graphicsDevice.clear(clearColor, 1.0, 0); 490 491 floor.render(graphicsDevice, camera); 492 493 if (debugMode) { 494 scene.drawPhysicsGeometry(graphicsDevice, shaderManager, camera, physicsManager); 495 } 496 497 drawCrosshair(); 498 499 graphicsDevice.endFrame(); 500 } 501 502 displayPerformance(); 503 }; 504 505 var intervalID; 506 var loadingCompleted = false; 507 var loadingLoop = function loadingLoopFn() { 508 if (graphicsDevice.beginFrame()) { 509 graphicsDevice.clear(loadingClearColor); 510 graphicsDevice.endFrame(); 511 } 512 513 if (loadingCompleted) { 514 TurbulenzEngine.clearInterval(intervalID); 515 516 camera.lookAt(cameraDefaultLook, worldUp, cameraDefaultPos); 517 camera.updateViewMatrix(); 518 519 shader2d = shaderManager.get("shaders/generic2D.cgfx"); 520 technique2d = shader2d.getTechnique("constantColor2D"); 521 techniqueParameters2d = graphicsDevice.createTechniqueParameters({ 522 clipSpace: mathDevice.v4BuildOne(), 523 constantColor: mathDevice.v4Build(0, 0, 0, 1) 524 }); 525 526 intervalID = TurbulenzEngine.setInterval(renderFrame, 1000 / 60); 527 } 528 }; 529 intervalID = TurbulenzEngine.setInterval(loadingLoop, 1000 / 10); 530 531 // Change the clear color before we start loading assets 532 loadingLoop(); 533 534 var postLoad = function postLoadFn() { 535 // Floor is represented by a plane shape 536 var floorShape = physicsDevice.createPlaneShape({ 537 normal: mathDevice.v3Build(0, 1, 0), 538 distance: 0, 539 margin: 0.001 540 }); 541 542 var floorObject = physicsDevice.createCollisionObject({ 543 shape: floorShape, 544 transform: mathDevice.m43BuildIdentity(), 545 friction: 0.8, 546 restitution: 0.1, 547 group: physicsDevice.FILTER_STATIC, 548 mask: physicsDevice.FILTER_ALL 549 }); 550 551 // Adds the floor collision object to the world 552 dynamicsWorld.addCollisionObject(floorObject); 553 554 // Bowl is represented by a triangle mesh shape. 555 // We create the triangle mesh simply and manually. 556 var positionsData = []; 557 var indicesData = []; 558 559 // Compute bowl vertices. 560 var i, j, offset; 561 for (i = 0; i < depthN; i += 1) { 562 var elevation = (Math.PI * 0.75) * ((i + 1) / (depthN + 2)); 563 for (j = 0; j < radialN; j += 1) { 564 var azimuth = (Math.PI * 2) * (j / radialN); 565 566 offset = ((i * radialN) + j) * 3; 567 positionsData[offset] = Math.sin(elevation) * Math.cos(azimuth) * bowlRadius; 568 positionsData[offset + 1] = (1 - Math.cos(elevation)) * bowlHeight; 569 positionsData[offset + 2] = Math.sin(elevation) * Math.sin(azimuth) * bowlRadius; 570 } 571 } 572 573 offset = (depthN * radialN) * 3; 574 positionsData[offset] = 0; 575 positionsData[offset + 1] = 0; 576 positionsData[offset + 2] = 0; 577 578 for (i = 0; i < (depthN - 1); i += 1) { 579 for (j = 0; j < radialN; j += 1) { 580 offset = ((i * radialN) + j) * 3 * 2; 581 indicesData[offset] = (i * radialN) + j; 582 indicesData[offset + 1] = (i * radialN) + ((j + 1) % radialN); 583 indicesData[offset + 2] = ((i + 1) * radialN) + j; 584 585 indicesData[offset + 3] = ((i + 1) * radialN) + j; 586 indicesData[offset + 4] = (i * radialN) + ((j + 1) % radialN); 587 indicesData[offset + 5] = ((i + 1) * radialN) + ((j + 1) % radialN); 588 } 589 } 590 591 for (i = 0; i < radialN; i += 1) { 592 offset = (((depthN - 1) * radialN) * 3 * 2) + (i * 3); 593 indicesData[offset] = i; 594 indicesData[offset + 1] = (depthN * radialN); 595 indicesData[offset + 2] = ((i + 1) % radialN); 596 } 597 598 // Create triangle array for bowl 599 var bowlTriangleArray = physicsDevice.createTriangleArray({ 600 vertices: positionsData, 601 indices: indicesData 602 }); 603 604 // Create bowl physics shape and object. 605 var bowlShape = physicsDevice.createTriangleMeshShape({ 606 triangleArray: bowlTriangleArray, 607 margin: 0.001 608 }); 609 610 bowlObject = physicsDevice.createCollisionObject({ 611 shape: bowlShape, 612 transform: mathDevice.m43BuildIdentity(), 613 friction: 0.8, 614 restitution: 0.1, 615 group: physicsDevice.FILTER_STATIC, 616 mask: physicsDevice.FILTER_ALL, 617 kinematic: true 618 }); 619 620 // Create SceneNode for bowl, and add to scene. 621 var bowlSceneNode = SceneNode.create({ 622 name: "Bowl", 623 local: bowlObject.transform, 624 dynamic: true, 625 disabled: false 626 }); 627 628 scene.addRootNode(bowlSceneNode); 629 630 physicsManager.addNode(bowlSceneNode, bowlObject, null, bowlTriangleArray); 631 }; 632 633 var numShadersToLoad = 2; 634 635 var shadersLoaded = function shadersLoadedFn(/* shader */ ) { 636 numShadersToLoad -= 1; 637 if (0 === numShadersToLoad) { 638 postLoad(); 639 loadingCompleted = true; 640 } 641 }; 642 643 var loadAssets = function loadAssetsFn() { 644 shaderManager.load("shaders/debug.cgfx", shadersLoaded); 645 shaderManager.load("shaders/generic2D.cgfx", shadersLoaded); 646 }; 647 648 var mappingTableReceived = function mappingTableReceivedFn(mappingTable) { 649 shaderManager.setPathRemapping(mappingTable.urlMapping, mappingTable.assetPrefix); 650 sceneLoader.setPathRemapping(mappingTable.urlMapping, mappingTable.assetPrefix); 651 652 loadAssets(); 653 }; 654 655 var gameSessionCreated = function gameSessionCreatedFn(gameSession) { 656 TurbulenzServices.createMappingTable(requestHandler, gameSession, mappingTableReceived); 657 }; 658 659 var gameSessionFailed = function gameSessionFailedFn(reason) { 660 gameSessionCreated(null); 661 }; 662 663 var gameSession = TurbulenzServices.createGameSession(requestHandler, gameSessionCreated); 664 665 // Create a scene destroy callback to run when the window is closed 666 function destroyScene() { 667 gameSession.destroy(); 668 669 TurbulenzEngine.clearInterval(intervalID); 670 clearColor = null; 671 672 if (physicsManager) { 673 physicsManager.clear(); 674 physicsManager = null; 675 } 676 677 if (scene) { 678 scene.destroy(); 679 scene = null; 680 } 681 682 requestHandler = null; 683 684 camera = null; 685 686 techniqueParameters2d = null; 687 technique2d = null; 688 shader2d = null; 689 chSemantics = null; 690 chFormats = null; 691 692 if (shaderManager) { 693 shaderManager.destroy(); 694 shaderManager = null; 695 } 696 697 TurbulenzEngine.flush(); 698 graphicsDevice = null; 699 mathDevice = null; 700 physicsDevice = null; 701 dynamicsWorld = null; 702 mouseCodes = null; 703 keyCodes = null; 704 inputDevice = null; 705 cameraController = null; 706 floor = null; 707 } 708 709 TurbulenzEngine.onunload = destroyScene; 710 };