1 /*{# Copyright (c) 2010-2013 Turbulenz Limited #}*/ 2 /* 3 * @title: 3D Physics 4 * @description: 5 * This sample shows how to create and use the Turbulenz physics device. 6 * The sample creates a floor plane and multiple cube shaped rigid bodies attached to nodes in the rendering scene. 7 * Click on the rendering window to move and rotate the camera around, 8 * click again to pick up and move the rigid bodies using the mouse forces object. 9 */ 10 /*{{ javascript("jslib/camera.js") }}*/ 11 /*{{ javascript("jslib/floor.js") }}*/ 12 /*{{ javascript("jslib/mouseforces.js") }}*/ 13 /*{{ javascript("jslib/observer.js") }}*/ 14 /*{{ javascript("jslib/utilities.js") }}*/ 15 /*{{ javascript("jslib/requesthandler.js") }}*/ 16 /*{{ javascript("jslib/services/turbulenzservices.js") }}*/ 17 /*{{ javascript("jslib/services/turbulenzbridge.js") }}*/ 18 /*{{ javascript("jslib/services/gamesession.js") }}*/ 19 /*{{ javascript("jslib/services/mappingtable.js") }}*/ 20 /*{{ javascript("scripts/htmlcontrols.js") }}*/ 21 /*global TurbulenzEngine: true */ 22 /*global RequestHandler: false */ 23 /*global window: false */ 24 /*global Camera: false */ 25 /*global CameraController: false */ 26 /*global Floor: false */ 27 /*global RequestHandler: false */ 28 /*global HTMLControls: false */ 29 /*global MouseForces: false */ 30 /*global TurbulenzServices: false */ 31 TurbulenzEngine.onload = function onloadFn() { 32 var errorCallback = function errorCallback(msg) { 33 window.alert(msg); 34 }; 35 36 var graphicsDeviceParameters = {}; 37 var graphicsDevice = TurbulenzEngine.createGraphicsDevice(graphicsDeviceParameters); 38 39 if (!graphicsDevice.shadingLanguageVersion) { 40 errorCallback("No shading language support detected.\nPlease check your graphics drivers are up to date."); 41 graphicsDevice = null; 42 return; 43 } 44 45 var mathDeviceParameters = {}; 46 var mathDevice = TurbulenzEngine.createMathDevice(mathDeviceParameters); 47 48 var inputDeviceParameters = {}; 49 var inputDevice = TurbulenzEngine.createInputDevice(inputDeviceParameters); 50 51 var assetsToLoad = 3; 52 53 // Setup camera & controller 54 var camera = Camera.create(mathDevice); 55 var cameraInitialize = function cameraInitializeFn() { 56 var worldUp = mathDevice.v3BuildYAxis(); 57 camera.lookAt(worldUp, worldUp, mathDevice.v3Build(0.0, 3.0, -15.0)); 58 camera.updateViewMatrix(); 59 }; 60 cameraInitialize(); 61 62 var cameraController = CameraController.create(graphicsDevice, inputDevice, camera); 63 64 var floor = Floor.create(graphicsDevice, mathDevice); 65 66 var requestHandlerParameters = {}; 67 var requestHandler = RequestHandler.create(requestHandlerParameters); 68 69 var shader3d = null; 70 var technique3d = null; 71 72 var shader3dLoaded = function shader3dLoadedFn(shaderText) { 73 if (shaderText) { 74 var shaderParameters = JSON.parse(shaderText); 75 shader3d = graphicsDevice.createShader(shaderParameters); 76 if (shader3d) { 77 technique3d = shader3d.getTechnique("textured3D"); 78 assetsToLoad -= 1; 79 } 80 } 81 }; 82 83 var sharedTechniqueParameters = graphicsDevice.createTechniqueParameters({ 84 diffuse: null 85 }); 86 87 /*jshint white: false*/ 88 // Vertex buffer parameters for crate 89 var vertexbufferParameters = { 90 numVertices: 24, 91 attributes: ['FLOAT3', 'SHORT2'], 92 dynamic: false, 93 data: [ 94 -0.5, 95 -0.5, 96 0.5, 97 0, 98 0, 99 0.5, 100 -0.5, 101 0.5, 102 1, 103 0, 104 0.5, 105 0.5, 106 0.5, 107 1, 108 1, 109 -0.5, 110 0.5, 111 0.5, 112 0, 113 1, 114 -0.5, 115 0.5, 116 0.5, 117 0, 118 0, 119 0.5, 120 0.5, 121 0.5, 122 1, 123 0, 124 0.5, 125 0.5, 126 -0.5, 127 1, 128 1, 129 -0.5, 130 0.5, 131 -0.5, 132 0, 133 1, 134 -0.5, 135 0.5, 136 -0.5, 137 1, 138 1, 139 0.5, 140 0.5, 141 -0.5, 142 0, 143 1, 144 0.5, 145 -0.5, 146 -0.5, 147 0, 148 0, 149 -0.5, 150 -0.5, 151 -0.5, 152 1, 153 0, 154 -0.5, 155 -0.5, 156 -0.5, 157 0, 158 0, 159 0.5, 160 -0.5, 161 -0.5, 162 1, 163 0, 164 0.5, 165 -0.5, 166 0.5, 167 1, 168 1, 169 -0.5, 170 -0.5, 171 0.5, 172 0, 173 1, 174 0.5, 175 -0.5, 176 0.5, 177 0, 178 0, 179 0.5, 180 -0.5, 181 -0.5, 182 1, 183 0, 184 0.5, 185 0.5, 186 -0.5, 187 1, 188 1, 189 0.5, 190 0.5, 191 0.5, 192 0, 193 1, 194 -0.5, 195 -0.5, 196 -0.5, 197 0, 198 0, 199 -0.5, 200 -0.5, 201 0.5, 202 1, 203 0, 204 -0.5, 205 0.5, 206 0.5, 207 1, 208 1, 209 -0.5, 210 0.5, 211 -0.5, 212 0, 213 1 214 ] 215 }; 216 217 /*jshint white: true*/ 218 var vertexbuffer = graphicsDevice.createVertexBuffer(vertexbufferParameters); 219 220 var semantics = graphicsDevice.createSemantics([graphicsDevice.SEMANTIC_POSITION, graphicsDevice.SEMANTIC_TEXCOORD]); 221 222 /*jshint white: false*/ 223 var indexbufferParameters = { 224 numIndices: 36, 225 format: 'USHORT', 226 dynamic: false, 227 data: [ 228 2, 229 0, 230 1, 231 3, 232 0, 233 2, 234 6, 235 4, 236 5, 237 7, 238 4, 239 6, 240 10, 241 8, 242 9, 243 11, 244 8, 245 10, 246 14, 247 12, 248 13, 249 15, 250 12, 251 14, 252 18, 253 16, 254 17, 255 19, 256 16, 257 18, 258 22, 259 20, 260 21, 261 23, 262 20, 263 22 264 ] 265 }; 266 267 /*jshint white: true*/ 268 var indexbuffer = graphicsDevice.createIndexBuffer(indexbufferParameters); 269 270 var primitive = graphicsDevice.PRIMITIVE_TRIANGLES; 271 var numIndices = 36; 272 273 // Cache mathDevice functions 274 var m43MulM44 = mathDevice.m43MulM44; 275 var isVisibleBoxOrigin = mathDevice.isVisibleBoxOrigin; 276 var v4Build = mathDevice.v4Build; 277 var m43BuildTranslation = mathDevice.m43BuildTranslation; 278 279 var shader2d = null; 280 var technique2d = null; 281 282 var shader2dLoaded = function shader2dLoadedFn(shaderText) { 283 if (shaderText) { 284 var shaderParameters = JSON.parse(shaderText); 285 shader2d = graphicsDevice.createShader(shaderParameters); 286 if (shader2d) { 287 technique2d = shader2d.getTechnique("constantColor2D"); 288 assetsToLoad -= 1; 289 } 290 } 291 }; 292 293 var techniqueParameters2d = graphicsDevice.createTechniqueParameters({ 294 clipSpace: null, 295 constantColor: mathDevice.v4Build(0, 0, 0, 1) 296 }); 297 298 var linePrim = graphicsDevice.PRIMITIVE_LINES; 299 var cursorFormat = [graphicsDevice.VERTEXFORMAT_FLOAT3]; 300 var cursorSemantic = graphicsDevice.createSemantics([graphicsDevice.SEMANTIC_POSITION]); 301 302 var clearColor = mathDevice.v4Build(0.95, 0.95, 1.0, 1.0); 303 304 // 305 // Physics 306 // 307 var physicsDeviceParameters = {}; 308 var physicsDevice = TurbulenzEngine.createPhysicsDevice(physicsDeviceParameters); 309 310 var dynamicsWorldParameters = {}; 311 var dynamicsWorld = physicsDevice.createDynamicsWorld(dynamicsWorldParameters); 312 313 // Specify the generic settings for the collision objects 314 var collisionMargin = 0.005; 315 var mass = 20.0; 316 var numCubes = 200; 317 var cubeExtents = mathDevice.v3Build(0.5, 0.5, 0.5); 318 319 // Floor is represented by a plane 320 var floorShape = physicsDevice.createPlaneShape({ 321 normal: mathDevice.v3Build(0, 1, 0), 322 distance: 0, 323 margin: collisionMargin 324 }); 325 326 var floorObject = physicsDevice.createCollisionObject({ 327 shape: floorShape, 328 transform: mathDevice.m43BuildIdentity(), 329 friction: 0.5, 330 restitution: 0.3, 331 group: physicsDevice.FILTER_STATIC, 332 mask: physicsDevice.FILTER_ALL 333 }); 334 335 // Adds the floor collision object to the physicsDevice 336 dynamicsWorld.addCollisionObject(floorObject); 337 338 var boxShape = physicsDevice.createBoxShape({ 339 halfExtents: cubeExtents, 340 margin: collisionMargin 341 }); 342 343 var inertia = boxShape.inertia; 344 inertia = mathDevice.v3ScalarMul(inertia, mass); 345 346 var boxBodies = []; 347 348 // Initial box is created as a rigid body 349 boxBodies[0] = physicsDevice.createRigidBody({ 350 shape: boxShape, 351 mass: mass, 352 inertia: inertia, 353 transform: mathDevice.m43BuildTranslation(0.0, 1.0, 0.0), 354 friction: 0.5, 355 restitution: 0.3, 356 angularDamping: 0.9, 357 frozen: false 358 }); 359 dynamicsWorld.addRigidBody(boxBodies[0]); 360 361 for (var n = 1; n < numCubes; n += 1) { 362 // Each additional box is cloned from the original 363 boxBodies[n] = boxBodies[0].clone(); 364 boxBodies[n].transform = m43BuildTranslation.call(mathDevice, 0.0, 1.0 + 1.5 * n, 0.0); 365 366 dynamicsWorld.addRigidBody(boxBodies[n]); 367 } 368 369 var doReset = false; 370 var reset = function resetFn() { 371 var transform = mathDevice.m43BuildIdentity(); 372 var v3Zero = mathDevice.v3BuildZero(); 373 374 for (n = 0; n < numCubes; n += 1) { 375 var body = boxBodies[n]; 376 body.transform = mathDevice.m43BuildTranslation(0.0, 1.0 + 1.5 * n, 0.0, transform); 377 body.linearVelocity = v3Zero; 378 body.angularVelocity = v3Zero; 379 body.active = true; 380 } 381 382 cameraInitialize(); 383 }; 384 385 // Controls 386 var htmlControls = HTMLControls.create(); 387 388 htmlControls.addButtonControl({ 389 id: "button01", 390 value: "Reset", 391 fn: function schedulResetFn() { 392 doReset = true; 393 } 394 }); 395 htmlControls.register(); 396 397 var keyCodes = inputDevice.keyCodes; 398 var mouseCodes = inputDevice.mouseCodes; 399 400 var onKeyUp = function physicsOnkeyupFn(keynum) { 401 if (keynum === keyCodes.R) { 402 reset(); 403 } 404 }; 405 406 // Mouse forces 407 var dragMin = mathDevice.v3Build(-1000, 0, -1000); 408 var dragMax = mathDevice.v3Build(1000, 1000, 1000); 409 var mouseForces = MouseForces.create(graphicsDevice, inputDevice, mathDevice, physicsDevice, dragMin, dragMax); 410 411 var onMouseDown = function (button) { 412 if (mouseCodes.BUTTON_0 === button || mouseCodes.BUTTON_1 === button) { 413 mouseForces.onmousedown(); 414 } 415 }; 416 417 var onMouseUp = function (button) { 418 if (mouseCodes.BUTTON_0 === button || mouseCodes.BUTTON_1 === button) { 419 mouseForces.onmouseup(); 420 } 421 }; 422 423 // Add event listeners 424 inputDevice.addEventListener("keyup", onKeyUp); 425 inputDevice.addEventListener("mousedown", onMouseDown); 426 inputDevice.addEventListener("mouseup", onMouseUp); 427 428 // 429 // Update 430 // 431 var update = function updateFn() { 432 inputDevice.update(); 433 434 if (doReset) { 435 reset(); 436 doReset = false; 437 } 438 439 if (mouseForces.pickedBody) { 440 // If we're dragging a body don't apply the movement to the camera 441 cameraController.pitch = 0; 442 cameraController.turn = 0; 443 cameraController.step = 0; 444 } 445 446 cameraController.update(); 447 448 var aspectRatio = (graphicsDevice.width / graphicsDevice.height); 449 if (aspectRatio !== camera.aspectRatio) { 450 camera.aspectRatio = aspectRatio; 451 camera.updateProjectionMatrix(); 452 } 453 camera.updateViewProjectionMatrix(); 454 455 if (0 >= assetsToLoad) { 456 mouseForces.update(dynamicsWorld, camera, 0.1); 457 458 dynamicsWorld.update(); 459 } 460 461 var vp = camera.viewProjectionMatrix; 462 var transform = mathDevice.m43BuildIdentity(); 463 var wvp; 464 465 if (graphicsDevice.beginFrame()) { 466 graphicsDevice.clear(clearColor, 1.0, 0); 467 468 floor.render(graphicsDevice, camera); 469 470 if (0 >= assetsToLoad) { 471 graphicsDevice.setStream(vertexbuffer, semantics); 472 473 graphicsDevice.setIndexBuffer(indexbuffer); 474 475 graphicsDevice.setTechnique(technique3d); 476 477 graphicsDevice.setTechniqueParameters(sharedTechniqueParameters); 478 479 for (n = 0; n < numCubes; n += 1) { 480 boxBodies[n].calculateTransform(transform); 481 wvp = m43MulM44.call(mathDevice, transform, vp, wvp); 482 if (isVisibleBoxOrigin.call(mathDevice, cubeExtents, wvp)) { 483 // Use directly the active technique when just a single property changes 484 technique3d.worldViewProjection = wvp; 485 graphicsDevice.drawIndexed(primitive, numIndices); 486 } 487 } 488 489 if (!mouseForces.pickedBody) { 490 graphicsDevice.setTechnique(technique2d); 491 492 var screenWidth = graphicsDevice.width; 493 var screenHeight = graphicsDevice.height; 494 techniqueParameters2d['clipSpace'] = v4Build.call(mathDevice, 2.0 / screenWidth, -2.0 / screenHeight, -1.0, 1.0); 495 graphicsDevice.setTechniqueParameters(techniqueParameters2d); 496 497 var writer = graphicsDevice.beginDraw(linePrim, 4, cursorFormat, cursorSemantic); 498 499 if (writer) { 500 var halfWidth = screenWidth * 0.5; 501 var halfHeight = screenHeight * 0.5; 502 writer([halfWidth - 10, halfHeight]); 503 writer([halfWidth + 10, halfHeight]); 504 writer([halfWidth, halfHeight - 10]); 505 writer([halfWidth, halfHeight + 10]); 506 507 graphicsDevice.endDraw(writer); 508 } 509 } 510 } 511 512 graphicsDevice.endFrame(); 513 } 514 }; 515 var intervalID = TurbulenzEngine.setInterval(update, 1000 / 60); 516 517 var mappingTableReceived = function mappingTableReceivedFn(mappingTable) { 518 var textureParameters = { 519 src: mappingTable.getURL("textures/crate.jpg"), 520 mipmaps: true, 521 onload: function (texture) { 522 sharedTechniqueParameters['diffuse'] = texture; 523 assetsToLoad -= 1; 524 } 525 }; 526 527 graphicsDevice.createTexture(textureParameters); 528 529 requestHandler.request({ 530 src: mappingTable.getURL("shaders/generic3D.cgfx"), 531 onload: shader3dLoaded 532 }); 533 requestHandler.request({ 534 src: mappingTable.getURL("shaders/generic2D.cgfx"), 535 onload: shader2dLoaded 536 }); 537 }; 538 539 var gameSessionCreated = function gameSessionCreatedFn(gameSession) { 540 TurbulenzServices.createMappingTable(requestHandler, gameSession, mappingTableReceived); 541 }; 542 var gameSession = TurbulenzServices.createGameSession(requestHandler, gameSessionCreated); 543 544 TurbulenzEngine.onunload = function destroyScene() { 545 TurbulenzEngine.clearInterval(intervalID); 546 547 if (gameSession) { 548 gameSession.destroy(); 549 gameSession = null; 550 } 551 552 requestHandler = null; 553 indexbuffer = null; 554 vertexbuffer = null; 555 semantics = null; 556 cursorSemantic = null; 557 primitive = null; 558 linePrim = null; 559 technique3d = null; 560 shader3d = null; 561 technique2d = null; 562 shader2d = null; 563 techniqueParameters2d = null; 564 sharedTechniqueParameters = null; 565 mouseForces = null; 566 floor = null; 567 cameraController = null; 568 camera = null; 569 boxBodies = null; 570 inertia = null; 571 clearColor = null; 572 m43MulM44 = null; 573 m43BuildTranslation = null; 574 isVisibleBoxOrigin = null; 575 v4Build = null; 576 dragMax = null; 577 dragMin = null; 578 mouseCodes = null; 579 keyCodes = null; 580 581 TurbulenzEngine.flush(); 582 physicsDevice = null; 583 inputDevice = null; 584 graphicsDevice = null; 585 mathDevice = null; 586 }; 587 };