camera.js
  1 /*{# Copyright (c) 2010-2012 Turbulenz Limited #}*/
  2 /*
  3 * @title: Camera
  4 * @description:
  5 * This sample shows how to set up and then animate a Turbulenz Camera object.
  6 * You can switch between tracking, orbiting and chasing motions.
  7 */
  8 /*{{ javascript("jslib/aabbtree.js") }}*/
  9 /*{{ javascript("jslib/camera.js") }}*/
 10 /*{{ javascript("jslib/floor.js") }}*/
 11 /*{{ javascript("jslib/geometry.js") }}*/
 12 /*{{ javascript("jslib/material.js") }}*/
 13 /*{{ javascript("jslib/light.js") }}*/
 14 /*{{ javascript("jslib/scenenode.js") }}*/
 15 /*{{ javascript("jslib/scene.js") }}*/
 16 /*{{ javascript("jslib/vmath.js") }}*/
 17 /*{{ javascript("jslib/effectmanager.js") }}*/
 18 /*{{ javascript("jslib/shadermanager.js") }}*/
 19 /*{{ javascript("jslib/texturemanager.js") }}*/
 20 /*{{ javascript("jslib/renderingcommon.js") }}*/
 21 /*{{ javascript("jslib/defaultrendering.js") }}*/
 22 /*{{ javascript("jslib/observer.js") }}*/
 23 /*{{ javascript("jslib/resourceloader.js") }}*/
 24 /*{{ javascript("jslib/utilities.js") }}*/
 25 /*{{ javascript("jslib/vertexbuffermanager.js") }}*/
 26 /*{{ javascript("jslib/indexbuffermanager.js") }}*/
 27 /*{{ javascript("jslib/requesthandler.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/dynamiccameracontroller.js") }}*/
 33 /*{{ javascript("scripts/htmlcontrols.js") }}*/
 34 /*{{ javascript("scripts/motion.js") }}*/
 35 /*{{ javascript("scripts/sceneloader.js") }}*/
 36 /*global TurbulenzEngine: true */
 37 /*global DefaultRendering: false */
 38 /*global DynamicCameraController: false */
 39 /*global Motion: false */
 40 /*global RequestHandler: false */
 41 /*global SceneLoader: false */
 42 /*global TurbulenzServices: false */
 43 /*global TextureManager: false */
 44 /*global ShaderManager: false */
 45 /*global EffectManager: false */
 46 /*global Camera: false */
 47 /*global Scene: false */
 48 /*global Floor: false */
 49 /*global HTMLControls: false */
 50 TurbulenzEngine.onload = function onloadFn() {
 51     var errorCallback = function errorCallback(msg) {
 52         window.alert(msg);
 53     };
 54 
 55     // Create the engine devices objects
 56     var graphicsDeviceParameters = {};
 57     var graphicsDevice = TurbulenzEngine.createGraphicsDevice(graphicsDeviceParameters);
 58 
 59     if (!graphicsDevice.shadingLanguageVersion) {
 60         errorCallback("No shading language support detected.\nPlease check your graphics drivers are up to date.");
 61         graphicsDevice = null;
 62         return;
 63     }
 64 
 65     // Clear the background color of the engine window
 66     var clearColor = [0.95, 0.95, 1.0, 1.0];
 67     if (graphicsDevice.beginFrame()) {
 68         graphicsDevice.clear(clearColor);
 69         graphicsDevice.endFrame();
 70     }
 71 
 72     var mathDeviceParameters = {};
 73     var mathDevice = TurbulenzEngine.createMathDevice(mathDeviceParameters);
 74 
 75     var inputDeviceParameters = {};
 76     var inputDevice = TurbulenzEngine.createInputDevice(inputDeviceParameters);
 77 
 78     var requestHandlerParameters = {};
 79     var requestHandler = RequestHandler.create(requestHandlerParameters);
 80 
 81     var textureManager = TextureManager.create(graphicsDevice, requestHandler, null, errorCallback);
 82     var shaderManager = ShaderManager.create(graphicsDevice, requestHandler, null, errorCallback);
 83     var effectManager = EffectManager.create();
 84 
 85     var renderer;
 86 
 87     var scene = Scene.create(mathDevice);
 88     var sceneLoader = SceneLoader.create();
 89 
 90     var loadAssets = function loadAssetsFn() {
 91         // Renderer for the scene (requires shader assets).
 92         renderer = DefaultRendering.create(graphicsDevice, mathDevice, shaderManager, effectManager);
 93 
 94         renderer.setGlobalLightPosition(mathDevice.v3Build(0.5, 100.0, 0.5));
 95         renderer.setAmbientColor(mathDevice.v3Build(0.3, 0.3, 0.4));
 96         renderer.setDefaultTexture(textureManager.get("default"));
 97 
 98         sceneLoader.load({
 99             scene: scene,
100             append: false,
101             assetPath: "models/duck.dae",
102             keepCameras: true,
103             graphicsDevice: graphicsDevice,
104             mathDevice: mathDevice,
105             textureManager: textureManager,
106             effectManager: effectManager,
107             shaderManager: shaderManager,
108             requestHandler: requestHandler,
109             dynamic: true
110         });
111     };
112 
113     // Read mapping table
114     var mappingTableReceived = function mappingTableReceivedFn(mappingTable) {
115         textureManager.setPathRemapping(mappingTable.urlMapping, mappingTable.assetPrefix);
116         shaderManager.setPathRemapping(mappingTable.urlMapping, mappingTable.assetPrefix);
117         sceneLoader.setPathRemapping(mappingTable.urlMapping, mappingTable.assetPrefix);
118 
119         loadAssets();
120     };
121 
122     var gameSessionCreated = function gameSessionCreatedFn(gameSession) {
123         TurbulenzServices.createMappingTable(requestHandler, gameSession, mappingTableReceived);
124     };
125 
126     var gameSession = TurbulenzServices.createGameSession(requestHandler, gameSessionCreated);
127 
128     // Create world
129     var worldUp = mathDevice.v3Build(0.0, 1.0, 0.0);
130     var floor = Floor.create(graphicsDevice, mathDevice);
131 
132     // Create duck animation
133     var duck = Motion.create(mathDevice, "duck");
134     duck.setCircularMovement(5.0, [0, 0, 0]);
135     duck.setDuckMotion(0.1, 0.5, 0.3);
136     duck.baseRotation = mathDevice.m43FromAxisRotation(duck.up, -90, duck.baseRotation);
137 
138     // Create fixed positions for tracking camera
139     var cameraFixedPositions = [
140         mathDevice.v3Build(0.0, 4.0, -10.0),
141         mathDevice.v3Build(9.0, 1.5, 9.0),
142         mathDevice.v3Build(-9.0, 8.0, 9.0)
143     ];
144     var cameraIndex = 1;
145 
146     // Create the orbit motion for the orbit camera position
147     var orbit = Motion.create(mathDevice, "orbitCam");
148     orbit.setCircularMovement(7.0, [0, 0, 0]);
149     orbit.setConstantMotion(0.3);
150     var updateOrbit = false;
151 
152     // Create a basic camera with initial target of the duck position
153     var camera = Camera.create(mathDevice);
154     camera.lookAt(duck.position, worldUp, cameraFixedPositions[cameraIndex]);
155     camera.updateViewMatrix();
156 
157     // Generate the initial matrix of the chase camera
158     var updateChase = false;
159     var chaseCamOffset = mathDevice.v3Build(-8.0, 4.5, 0.0);
160     var chaseCamPosition = chaseCamOffset.slice();
161     var chaseCamRate = 5.0;
162 
163     // Create the dynamic camera used for tracking, orbiting and chasing
164     var dynamicCameraController = DynamicCameraController.create(camera, graphicsDevice);
165     dynamicCameraController.setChaseRate(0.3);
166     dynamicCameraController.setTracking(true);
167     dynamicCameraController.setRate(1.4);
168 
169     var htmlControls = HTMLControls.create();
170 
171     // Switch camera
172     var switchToTargetCam = function switchToTargetCamFn(index) {
173         cameraIndex = index;
174         updateOrbit = false;
175         updateChase = false;
176 
177         // Rail camera mode is used to move to the target position over time
178         dynamicCameraController.setCameraMode(dynamicCameraController.cameraType.rail);
179         dynamicCameraController.setCameraTargetPos(cameraFixedPositions[index], TurbulenzEngine.time, 100);
180         htmlControls.setSelectedRadio("camPos", cameraIndex);
181     };
182 
183     var switchToOrbitCam = function switchToOrbitCamFn() {
184         cameraIndex = 3;
185         updateChase = false;
186 
187         // Fixed camera mode is used for orbit to ensure the orbit movement is constant
188         dynamicCameraController.setCameraMode(dynamicCameraController.cameraType.fixed);
189         updateOrbit = true;
190         htmlControls.setSelectedRadio("camPos", cameraIndex);
191     };
192 
193     var switchToChaseCam = function switchToChaseCamFn() {
194         cameraIndex = 4;
195         updateOrbit = false;
196 
197         // Chase camera mode is used, to smooth the varying motion of the
198         // target position
199         dynamicCameraController.setCameraMode(dynamicCameraController.cameraType.chase);
200         updateChase = true;
201         htmlControls.setSelectedRadio("camPos", cameraIndex);
202     };
203 
204     // Controls
205     htmlControls.addRadioControl({
206         id: "radio01",
207         groupName: "camPos",
208         radioIndex: 0,
209         value: "pos01",
210         fn: function () {
211             switchToTargetCam(0);
212         },
213         isDefault: true
214     });
215     htmlControls.addRadioControl({
216         id: "radio02",
217         groupName: "camPos",
218         radioIndex: 1,
219         value: "pos02",
220         fn: function () {
221             switchToTargetCam(1);
222         },
223         isDefault: false
224     });
225     htmlControls.addRadioControl({
226         id: "radio03",
227         groupName: "camPos",
228         radioIndex: 2,
229         value: "pos03",
230         fn: function () {
231             switchToTargetCam(2);
232         },
233         isDefault: false
234     });
235     htmlControls.addRadioControl({
236         id: "radio04",
237         groupName: "camPos",
238         radioIndex: 3,
239         value: "pos04",
240         fn: switchToOrbitCam,
241         isDefault: false
242     });
243     htmlControls.addRadioControl({
244         id: "radio05",
245         groupName: "camPos",
246         radioIndex: 4,
247         value: "pos05",
248         fn: switchToChaseCam,
249         isDefault: false
250     });
251     htmlControls.register();
252 
253     // Input functions
254     var keyCodes = inputDevice.keyCodes;
255 
256     var keyUp = function keyUpFn(key) {
257         if (key === keyCodes.T) {
258             cameraIndex = (cameraIndex + 1) % cameraFixedPositions.length;
259             switchToTargetCam(cameraIndex);
260         }
261 
262         if (key === keyCodes.O) {
263             switchToOrbitCam();
264         }
265 
266         if (key === keyCodes.C) {
267             switchToChaseCam();
268         }
269 
270         if (key === keyCodes.RETURN) {
271             graphicsDevice.fullscreen = !graphicsDevice.fullscreen;
272         }
273     };
274     inputDevice.addEventListener("keyup", keyUp);
275 
276     // Initialize the previous frame time
277     var previousFrameTime = TurbulenzEngine.time;
278     var intervalID;
279 
280     // Callback to draw extra debug information
281     function drawDebugCB() {
282         floor.render(graphicsDevice, camera);
283     }
284 
285     //
286     // Update
287     //
288     var update = function updateFn() {
289         var currentTime = TurbulenzEngine.time;
290         var deltaTime = (currentTime - previousFrameTime);
291         if (deltaTime > 0.1) {
292             deltaTime = 0.1;
293         }
294 
295         // Update input
296         inputDevice.update();
297 
298         // Update duck position and rotation
299         duck.update(deltaTime);
300 
301         if (updateOrbit) {
302             orbit.circularCenter[0] = duck.position[0];
303             orbit.circularCenter[2] = duck.position[2];
304             orbit.position[1] = 4.0;
305             orbit.update(deltaTime);
306             dynamicCameraController.setCameraTargetPos(orbit.position, currentTime, 0);
307         } else if (updateChase) {
308             dynamicCameraController.setCameraTargetPos(chaseCamPosition, currentTime, deltaTime * chaseCamRate);
309         }
310 
311         // Update dynamic camera controller
312         dynamicCameraController.setTrackTarget(duck.position);
313         dynamicCameraController.update(deltaTime);
314 
315         // Update the aspect ratio of the camera in case of window resizes
316         var deviceWidth = graphicsDevice.width;
317         var deviceHeight = graphicsDevice.height;
318         var aspectRatio = (deviceWidth / deviceHeight);
319         if (aspectRatio !== camera.aspectRatio) {
320             camera.aspectRatio = aspectRatio;
321             camera.updateProjectionMatrix();
322         }
323         camera.updateViewProjectionMatrix();
324 
325         //Move the duck model.
326         var duckNode = scene.findNode("LOD3sp");
327         duckNode.setLocalTransform(duck.matrix);
328 
329         if (updateChase) {
330             // Only update the chase camera position when we are using it
331             chaseCamPosition = mathDevice.m43TransformPoint(duckNode.getWorldTransform(), chaseCamOffset);
332         }
333 
334         // Update scene
335         scene.update();
336 
337         // Update renderer with new camera and updated scene
338         renderer.update(graphicsDevice, camera, scene, currentTime);
339 
340         if (graphicsDevice.beginFrame()) {
341             if (renderer.updateBuffers(graphicsDevice, deviceWidth, deviceHeight)) {
342                 renderer.draw(graphicsDevice, clearColor, null, null, drawDebugCB);
343             }
344 
345             graphicsDevice.endFrame();
346         }
347 
348         previousFrameTime = currentTime;
349     };
350 
351     var loadingLoop = function loadingLoopFn() {
352         if (sceneLoader.complete()) {
353             TurbulenzEngine.clearInterval(intervalID);
354             intervalID = TurbulenzEngine.setInterval(update, 1000 / 60);
355 
356             renderer.updateShader(shaderManager);
357 
358             switchToTargetCam(0);
359         }
360     };
361 
362     intervalID = TurbulenzEngine.setInterval(loadingLoop, 1000 / 10);
363 
364     // Create a scene destroy callback to run when the window is closed
365     TurbulenzEngine.onunload = function destroyScene() {
366         TurbulenzEngine.clearInterval(intervalID);
367 
368         if (gameSession) {
369             gameSession.destroy();
370             gameSession = null;
371         }
372 
373         clearColor = null;
374         worldUp = null;
375         floor = null;
376 
377         duck = null;
378         cameraFixedPositions = null;
379 
380         if (renderer) {
381             renderer.destroy();
382             renderer = null;
383         }
384 
385         orbit = null;
386         chaseCamPosition = null;
387 
388         requestHandler = null;
389         sceneLoader = null;
390 
391         if (scene) {
392             scene.destroy();
393             scene = null;
394         }
395 
396         camera = null;
397         dynamicCameraController = null;
398         effectManager = null;
399 
400         if (textureManager) {
401             textureManager.destroy();
402             textureManager = null;
403         }
404 
405         if (shaderManager) {
406             shaderManager.destroy();
407             shaderManager = null;
408         }
409 
410         TurbulenzEngine.flush();
411 
412         keyCodes = null;
413         inputDevice = null;
414         graphicsDevice = null;
415         mathDevice = null;
416     };
417 };