1 /*{# Copyright (c) 2010-2012 Turbulenz Limited #}*/ 2 /* 3 * @title: Forward rendering 4 * @description: 5 * This sample shows a scene with dynamic shadows cast by point or spot lights rendered using the Turbulenz forward renderer. 6 * You can enable multiple colored lights to see the impact on performance. 7 * Click on the rendering window to move and rotate the camera around. 8 */ 9 /*{{ javascript("jslib/camera.js") }}*/ 10 /*{{ javascript("jslib/aabbtree.js") }}*/ 11 /*{{ javascript("jslib/effectmanager.js") }}*/ 12 /*{{ javascript("jslib/shadermanager.js") }}*/ 13 /*{{ javascript("jslib/texturemanager.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/renderingcommon.js") }}*/ 20 /*{{ javascript("jslib/forwardrendering.js") }}*/ 21 /*{{ javascript("jslib/shadowmapping.js") }}*/ 22 /*{{ javascript("jslib/posteffects.js") }}*/ 23 /*{{ javascript("jslib/resourceloader.js") }}*/ 24 /*{{ javascript("jslib/observer.js") }}*/ 25 /*{{ javascript("jslib/utilities.js") }}*/ 26 /*{{ javascript("jslib/requesthandler.js") }}*/ 27 /*{{ javascript("jslib/vertexbuffermanager.js") }}*/ 28 /*{{ javascript("jslib/indexbuffermanager.js") }}*/ 29 /*{{ javascript("jslib/services/turbulenzservices.js") }}*/ 30 /*{{ javascript("jslib/services/turbulenzbridge.js") }}*/ 31 /*{{ javascript("jslib/services/gamesession.js") }}*/ 32 /*{{ javascript("jslib/services/mappingtable.js") }}*/ 33 /*{{ javascript("scripts/sceneloader.js") }}*/ 34 /*{{ javascript("scripts/htmlcontrols.js") }}*/ 35 /*global TurbulenzEngine: true */ 36 /*global PostEffects: false */ 37 /*global ForwardRendering: false */ 38 /*global TurbulenzServices: false */ 39 /*global Camera: false */ 40 /*global CameraController: false */ 41 /*global TextureManager: false */ 42 /*global ShaderManager: false */ 43 /*global EffectManager: false */ 44 /*global Scene: false */ 45 /*global SceneNode: false */ 46 /*global SceneLoader: false */ 47 /*global LightInstance: false */ 48 /*global Light: false */ 49 /*global HTMLControls: false */ 50 /*global RequestHandler: false*/ 51 /*global window: false */ 52 TurbulenzEngine.onload = function onloadFn() { 53 var errorCallback = function errorCallback(msg) { 54 window.alert(msg); 55 }; 56 57 // Create the engine devices objects 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 // Create a camera with a 60 degree FOV 81 var camera = Camera.create(mathDevice); 82 var halfFov = Math.tan(30 * Math.PI / 180); 83 camera.recipViewWindowX = 1.0 / halfFov; 84 camera.recipViewWindowY = 1.0 / halfFov; 85 camera.updateProjectionMatrix(); 86 var worldUp = mathDevice.v3BuildYAxis(); 87 camera.lookAt(worldUp, worldUp, mathDevice.v3Build(0.0, 50.0, 200.0)); 88 camera.updateViewMatrix(); 89 90 var cameraController = CameraController.create(graphicsDevice, inputDevice, camera); 91 92 var requestHandlerParameters = {}; 93 var requestHandler = RequestHandler.create(requestHandlerParameters); 94 95 var textureManager = TextureManager.create(graphicsDevice, requestHandler, null, errorCallback); 96 var shaderManager = ShaderManager.create(graphicsDevice, requestHandler, null, errorCallback); 97 var effectManager = EffectManager.create(); 98 99 var scene = Scene.create(mathDevice); 100 var sceneLoader = SceneLoader.create(); 101 102 var maxSpeed = cameraController.maxSpeed; 103 104 var postEffects; 105 var renderer; 106 107 // Create some rendering callbacks to pass to the forward rendering renderer 108 function drawExtraDecalsFn() { 109 } 110 111 function drawExtraTransparentFn() { 112 } 113 114 function drawDebugFn() { 115 } 116 117 var coloredLights = false; 118 var pointLights = true; 119 var spotLights = false; 120 var directionalLights = false; 121 var enablePostEffects = false; 122 var pointLightCenter = [-3.5, 15, -0.5]; 123 var spotLightCenter = [-3.5, 25, -0.5]; 124 var yAxis = mathDevice.v3BuildYAxis(); 125 126 var setDisabled = function setDisabledFn(colored, spot, directional) { 127 var disabled = !colored || spot || directional; 128 scene.findNode("redLight-node").setDisabled(disabled); 129 scene.findNode("greenLight-node").setDisabled(disabled); 130 scene.findNode("blueLight-node").setDisabled(disabled); 131 scene.findNode("whiteLight-node").setDisabled(colored || spot || directional); 132 disabled = !colored || !spot; 133 scene.findNode("redSpotLight-node").setDisabled(disabled); 134 scene.findNode("greenSpotLight-node").setDisabled(disabled); 135 scene.findNode("blueSpotLight-node").setDisabled(disabled); 136 scene.findNode("whiteSpotLight-node").setDisabled(colored || !spot); 137 disabled = !colored || !directional; 138 scene.findNode("redDirectionalLight-node").setDisabled(disabled); 139 scene.findNode("greenDirectionalLight-node").setDisabled(disabled); 140 scene.findNode("blueDirectionalLight-node").setDisabled(disabled); 141 scene.findNode("whiteDirectionalLight-node").setDisabled(colored || !directional); 142 }; 143 144 // Controls 145 var htmlControls = HTMLControls.create(); 146 147 htmlControls.addRadioControl({ 148 id: "radio-point", 149 groupName: "lightType", 150 radioIndex: 0, 151 value: "pointLights", 152 fn: function () { 153 pointLights = true; 154 spotLights = false; 155 directionalLights = false; 156 if (sceneLoader.complete()) { 157 setDisabled(coloredLights, spotLights, directionalLights); 158 } 159 }, 160 isDefault: true 161 }); 162 htmlControls.addRadioControl({ 163 id: "radio-spot", 164 groupName: "lightType", 165 radioIndex: 1, 166 value: "spotLights", 167 fn: function () { 168 pointLights = false; 169 spotLights = true; 170 directionalLights = false; 171 if (sceneLoader.complete()) { 172 setDisabled(coloredLights, spotLights, directionalLights); 173 } 174 }, 175 isDefault: false 176 }); 177 htmlControls.addRadioControl({ 178 id: "radio-directional", 179 groupName: "lightType", 180 radioIndex: 2, 181 value: "directionalLights", 182 fn: function () { 183 pointLights = false; 184 spotLights = false; 185 directionalLights = true; 186 if (sceneLoader.complete()) { 187 setDisabled(coloredLights, spotLights, directionalLights); 188 } 189 }, 190 isDefault: false 191 }); 192 193 htmlControls.addCheckboxControl({ 194 id: "checkbox-colored", 195 value: "coloredLights", 196 isSelected: coloredLights, 197 fn: function () { 198 if (sceneLoader.complete()) { 199 coloredLights = !coloredLights; 200 setDisabled(coloredLights, spotLights, directionalLights); 201 } 202 return coloredLights; 203 } 204 }); 205 206 htmlControls.addCheckboxControl({ 207 id: "checkbox-post", 208 value: "enablePostEffects", 209 isSelected: enablePostEffects, 210 fn: function () { 211 enablePostEffects = !enablePostEffects; 212 return enablePostEffects; 213 } 214 }); 215 216 htmlControls.register(); 217 218 var v3Build = mathDevice.v3Build; 219 var m43BuildTranslation = mathDevice.m43BuildTranslation; 220 221 // Create a callback for post scene load to add some lights and update the camera position 222 var loadSceneFinished = function loadSceneFinishedFn(scene) { 223 var addLight = function addLightFn(light, enabled, lightCenter) { 224 scene.addLight(light); 225 226 var lightMatrix = m43BuildTranslation.call(mathDevice, v3Build.apply(mathDevice, lightCenter)); 227 228 var lightNode = SceneNode.create({ 229 name: light.name + "-node", 230 local: lightMatrix, 231 dynamic: true, 232 disabled: !enabled 233 }); 234 235 lightNode.addLightInstance(LightInstance.create(light)); 236 237 scene.addRootNode(lightNode); 238 }; 239 240 var createPointLight = function createPointLightFn(lightName, lightMaterial, color) { 241 return Light.create({ 242 name: lightName, 243 color: color, 244 point: true, 245 shadows: true, 246 halfExtents: mathDevice.v3Build(40, 40, 40), 247 origin: mathDevice.v3Build(0, 10, 0), 248 material: lightMaterial 249 }); 250 }; 251 252 var createSpotLight = function createSpotLightFn(lightName, lightMaterial, color) { 253 return Light.create({ 254 name: lightName, 255 color: mathDevice.v3Build(color[0], color[1], color[2]), 256 spot: true, 257 shadows: true, 258 material: lightMaterial, 259 target: mathDevice.v3Build(0, -40, 0), 260 right: mathDevice.v3Build(0, 0, 40), 261 up: mathDevice.v3Build(40, 0, 0) 262 }); 263 }; 264 265 var createDirectionalLight = function createDirectionalLightFn(lightName, lightMaterial, color, direction) { 266 return Light.create({ 267 name: lightName, 268 color: mathDevice.v3Build(color[0], color[1], color[2]), 269 directional: true, 270 shadows: true, 271 material: lightMaterial, 272 halfExtents: mathDevice.v3Build(60, 60, 60), 273 direction: mathDevice.v3Build(direction[0], direction[1], direction[2]) 274 }); 275 }; 276 277 var lightMaterialData = { 278 effect: "lambert", 279 parameters: { 280 lightfalloff: "textures/default_light.png", 281 lightprojection: "textures/default_light.png" 282 } 283 }; 284 scene.loadMaterial(graphicsDevice, textureManager, effectManager, "defaultLightMaterial", lightMaterialData); 285 var lightMaterial = scene.getMaterial("defaultLightMaterial"); 286 287 // Add some lights into the scene 288 addLight(createPointLight("whiteLight", lightMaterial, [1, 1, 1]), true, pointLightCenter); 289 addLight(createPointLight("redLight", lightMaterial, [0.9, 0.1, 0.1]), false, pointLightCenter); 290 addLight(createPointLight("greenLight", lightMaterial, [0.1, 0.9, 0.1]), false, pointLightCenter); 291 addLight(createPointLight("blueLight", lightMaterial, [0.1, 0.1, 0.9]), false, pointLightCenter); 292 293 addLight(createSpotLight("whiteSpotLight", lightMaterial, [1, 1, 1]), false, spotLightCenter); 294 addLight(createSpotLight("redSpotLight", lightMaterial, [0.9, 0.1, 0.1]), false, spotLightCenter); 295 addLight(createSpotLight("greenSpotLight", lightMaterial, [0.1, 0.9, 0.1]), false, spotLightCenter); 296 addLight(createSpotLight("blueSpotLight", lightMaterial, [0.1, 0.1, 0.9]), false, spotLightCenter); 297 298 addLight(createDirectionalLight("whiteDirectionalLight", lightMaterial, [1, 1, 1], [-0.707107, -0.707107, 0]), false, pointLightCenter); 299 addLight(createDirectionalLight("redDirectionalLight", lightMaterial, [0.9, 0.1, 0.1], [0.707107, -0.707107, 0]), false, pointLightCenter); 300 addLight(createDirectionalLight("greenDirectionalLight", lightMaterial, [0.1, 0.9, 0.1], [0, -0.707107, 0.707107]), false, pointLightCenter); 301 addLight(createDirectionalLight("blueDirectionalLight", lightMaterial, [0.1, 0.1, 0.9], [0, -0.707107, -0.707107]), false, pointLightCenter); 302 303 scene.addLight(Light.create({ 304 name: "ambient", 305 ambient: true, 306 color: [0.1, 0.1, 0.1] 307 })); 308 309 var sceneExtents = scene.getExtents(); 310 var sceneMinExtent = mathDevice.v3Build(sceneExtents[0], sceneExtents[1], sceneExtents[2]); 311 var sceneMaxExtent = mathDevice.v3Build(sceneExtents[3], sceneExtents[4], sceneExtents[5]); 312 var c = mathDevice.v3ScalarMul(mathDevice.v3Add(sceneMaxExtent, sceneMinExtent), 0.5); 313 var e = mathDevice.v3Sub(c, sceneMinExtent); 314 315 camera.lookAt(mathDevice.v3Build(-3.5, 5, -0.5), mathDevice.v3Build(0.0, 1.0, 0.0), mathDevice.v3Build(-51, 16, 8)); 316 camera.updateViewMatrix(); 317 318 var len = mathDevice.v3Length(e); 319 if (len < 4.0) { 320 camera.nearPlane = len * 0.25; 321 } else { 322 camera.nearPlane = 1.0; 323 } 324 camera.farPlane = Math.ceil(len) * 100.0; 325 camera.updateProjectionMatrix(); 326 327 maxSpeed = len; 328 }; 329 330 var previousFrameTime = TurbulenzEngine.time; 331 var doUpdate = true; 332 var intervalID; 333 var fpsElement = document.getElementById("fpscounter"); 334 var lastFPS = ''; 335 336 var renderFrame = function renderFrameFn() { 337 var currentTime = TurbulenzEngine.time; 338 var deltaTime = (currentTime - previousFrameTime); 339 if (deltaTime > 0.1) { 340 deltaTime = 0.1; 341 } 342 cameraController.maxSpeed = (deltaTime * maxSpeed); 343 344 // Update the input device 345 inputDevice.update(); 346 347 cameraController.update(); 348 349 // Update the aspect ratio of the camera in case of window resizes 350 var aspectRatio = (graphicsDevice.width / graphicsDevice.height); 351 if (aspectRatio !== camera.aspectRatio) { 352 camera.aspectRatio = aspectRatio; 353 camera.updateProjectionMatrix(); 354 } 355 camera.updateViewProjectionMatrix(); 356 357 if (doUpdate) { 358 // Orbit the lights above the scene 359 var updateLight = function updateLightFn(lightName, orbitCycle, orbitOffset, orbitRadius, forward, lightCenter) { 360 var orbit = (currentTime - orbitOffset) % orbitCycle; 361 if (!forward) { 362 orbit *= -1; 363 } 364 var omega = (orbit / orbitCycle) * 2 * Math.PI; 365 var x = orbitRadius * Math.cos(omega); 366 var y = orbitRadius * Math.sin(omega); 367 var lightNode = scene.findNode(lightName + "-node"); 368 var matrix = lightNode.getLocalTransform(); 369 matrix[9] = (lightCenter[0] + x); 370 matrix[11] = (lightCenter[2] + y); 371 lightNode.setLocalTransform(matrix); 372 }; 373 374 var updateDirectionalLight = function updateDirectionalLightFn(lightName, orbitCycle, orbitOffset, forward, axis) { 375 var orbit = (currentTime - orbitOffset) % orbitCycle; 376 if (!forward) { 377 orbit *= -1; 378 } 379 var omega = (orbit / orbitCycle) * 2 * Math.PI; 380 var lightNode = scene.findNode(lightName + "-node"); 381 var matrix = lightNode.getLocalTransform(); 382 mathDevice.m43FromAxisRotation(axis, omega, matrix); 383 lightNode.setLocalTransform(matrix); 384 }; 385 386 updateLight("whiteLight", 10, 0, 10, true, pointLightCenter); 387 updateLight("redLight", 5, 2, 10, true, pointLightCenter); 388 updateLight("greenLight", 7, 4.5, 10, true, pointLightCenter); 389 updateLight("blueLight", 8, 6, 10, false, pointLightCenter); 390 391 updateLight("whiteSpotLight", 10, 0, 10, true, spotLightCenter); 392 updateLight("redSpotLight", 5, 2, 10, true, spotLightCenter); 393 updateLight("greenSpotLight", 7, 4.5, 10, true, spotLightCenter); 394 updateLight("blueSpotLight", 8, 6, 10, false, spotLightCenter); 395 396 updateDirectionalLight("whiteDirectionalLight", 10, 0, true, yAxis); 397 updateDirectionalLight("redDirectionalLight", 5, 2, true, yAxis); 398 updateDirectionalLight("greenDirectionalLight", 7, 4.5, true, yAxis); 399 updateDirectionalLight("blueDirectionalLight", 8, 6, false, yAxis); 400 401 scene.update(); 402 403 renderer.update(graphicsDevice, camera, scene, currentTime); 404 } 405 406 if (graphicsDevice.beginFrame()) { 407 var clearColor = [0.0, 0.0, 0.0, 1.0]; 408 409 // Render the scene with the forward renderer without a posteffect 410 renderer.draw(graphicsDevice, clearColor, drawExtraDecalsFn, drawExtraTransparentFn, drawDebugFn, (postEffects && enablePostEffects ? postEffects.getEffectSetupCB("bicolor") : null)); 411 412 graphicsDevice.endFrame(); 413 } 414 415 previousFrameTime = currentTime; 416 417 if (fpsElement) { 418 var fps = (graphicsDevice.fps).toFixed(2); 419 if (lastFPS !== fps) { 420 lastFPS = fps; 421 422 // Execute any code that interacts with the DOM in a separate callback 423 TurbulenzEngine.setTimeout(function () { 424 fpsElement.innerHTML = fps + ' fps'; 425 }, 1); 426 } 427 } 428 }; 429 430 // Create a loop to run on an interval whilst loading 431 var loadingLoop = function loadingLoopFn() { 432 if (graphicsDevice.beginFrame()) { 433 graphicsDevice.clear(clearColor); 434 graphicsDevice.endFrame(); 435 } 436 437 if (sceneLoader.complete()) { 438 // Loading has completed, update the shader and effects systems 439 TurbulenzEngine.clearInterval(intervalID); 440 441 renderer.updateShader(shaderManager); 442 443 if (postEffects) { 444 postEffects.updateShader(shaderManager); 445 } 446 447 // Register the rendering callback as the new interval 448 intervalID = TurbulenzEngine.setInterval(renderFrame, 1000 / 60); 449 } 450 }; 451 452 intervalID = TurbulenzEngine.setInterval(loadingLoop, 1000 / 10); 453 454 var loadAssets = function loadAssets() { 455 renderer = ForwardRendering.create(graphicsDevice, mathDevice, shaderManager, effectManager, { shadowRendering: true }); 456 457 if (renderer.updateBuffers(graphicsDevice, 1024, 1024)) { 458 postEffects = PostEffects.create(graphicsDevice, shaderManager); 459 } else { 460 var buttonPostEffects = document.getElementById("checkbox-post"); 461 if (buttonPostEffects) { 462 buttonPostEffects.disabled = true; 463 } 464 } 465 466 sceneLoader.load({ 467 scene: scene, 468 append: true, 469 assetPath: "models/diningroom.dae", 470 keepLights: true, 471 graphicsDevice: graphicsDevice, 472 mathDevice: mathDevice, 473 textureManager: textureManager, 474 shaderManager: shaderManager, 475 effectManager: effectManager, 476 requestHandler: requestHandler, 477 postSceneLoadFn: loadSceneFinished 478 }); 479 }; 480 481 var mappingTableReceived = function mappingTableReceivedFn(mappingTable) { 482 textureManager.setPathRemapping(mappingTable.urlMapping, mappingTable.assetPrefix); 483 shaderManager.setPathRemapping(mappingTable.urlMapping, mappingTable.assetPrefix); 484 sceneLoader.setPathRemapping(mappingTable.urlMapping, mappingTable.assetPrefix); 485 486 loadAssets(); 487 }; 488 489 var gameSessionCreated = function gameSessionCreatedFn(gameSession) { 490 TurbulenzServices.createMappingTable(requestHandler, gameSession, mappingTableReceived); 491 }; 492 var gameSession = TurbulenzServices.createGameSession(requestHandler, gameSessionCreated); 493 494 // Create a scene destroy callback to run when the window is closed 495 TurbulenzEngine.onunload = function destroyScene() { 496 TurbulenzEngine.clearInterval(intervalID); 497 498 if (gameSession) { 499 gameSession.destroy(); 500 gameSession = null; 501 } 502 503 requestHandler = null; 504 sceneLoader = null; 505 506 if (scene) { 507 scene.destroy(); 508 scene = null; 509 } 510 511 effectManager = null; 512 513 if (shaderManager) { 514 shaderManager.destroy(); 515 shaderManager = null; 516 } 517 518 if (textureManager) { 519 textureManager.destroy(); 520 textureManager = null; 521 } 522 523 cameraController = null; 524 camera = null; 525 526 if (postEffects) { 527 postEffects.destroy(); 528 postEffects = null; 529 } 530 531 if (renderer) { 532 renderer.destroy(); 533 renderer = null; 534 } 535 536 TurbulenzEngine.flush(); 537 538 inputDevice = null; 539 graphicsDevice = null; 540 mathDevice = null; 541 542 fpsElement = null; 543 }; 544 };