summaryrefslogtreecommitdiffstats
path: root/o3d/samples/beachdemo/beachdemo-glsl.js
diff options
context:
space:
mode:
Diffstat (limited to 'o3d/samples/beachdemo/beachdemo-glsl.js')
-rw-r--r--o3d/samples/beachdemo/beachdemo-glsl.js2779
1 files changed, 2779 insertions, 0 deletions
diff --git a/o3d/samples/beachdemo/beachdemo-glsl.js b/o3d/samples/beachdemo/beachdemo-glsl.js
new file mode 100644
index 0000000..2506690
--- /dev/null
+++ b/o3d/samples/beachdemo/beachdemo-glsl.js
@@ -0,0 +1,2779 @@
+/*
+ * Copyright 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/**
+ * @fileoverview The beachdemo javascript.
+ */
+
+
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.pack');
+o3djs.require('o3djs.math');
+o3djs.require('o3djs.quaternions');
+o3djs.require('o3djs.dump');
+o3djs.require('o3djs.camera');
+o3djs.require('o3djs.primitives');
+o3djs.require('o3djs.loader');
+o3djs.require('o3djs.picking');
+o3djs.require('o3djs.canvas');
+o3djs.require('o3djs.fps');
+o3djs.require('o3djs.debug');
+o3djs.require('o3djs.particles');
+o3djs.require('o3djs.performance');
+o3djs.require('o3djs.io');
+
+var PROXY_HEIGHT = 5150;
+
+// client.root
+// |
+// g_mainRoot
+// |
+// +-----+--------+----------------+
+// | | |
+// g_baseRoot g_waterTransform g_skyDomeTransform
+// |
+// g_sceneRoot
+
+var g_re;
+var g_animateCamera = false;
+var g_cameraDuration = 1;
+var g_cameraPoint;
+var g_cameraPointIndex = 0;
+var g_cameraTimer = 0;
+var g_demoTimer = 0;
+var g_runDemo = true;
+var g_oldCameraClock = 0;
+var g_speedTransforms = [[], [], [], []];
+var g_sceneRoot;
+var g_baseRoot;
+var g_reflectionClipHeight = 100;
+var g_mainClipHeight = 100000000;
+var g_o3d;
+var g_hudFadeTime;
+var g_helpVisible = false;
+var g_math;
+var g_key;
+var g_paint;
+var g_sceneUrl;
+var g_quaternions;
+var g_waterMode = 0;
+var g_updateRenderTargets = true;
+var g_compileEffect;
+var g_reflectRefract = false;
+var g_environmentSampler;
+var g_materialPanelElement;
+var g_propPanelElement;
+var g_effectPanelElement;
+var g_upperPanelElement;
+var g_effectTabsElement;
+var g_effectTextAreaElement;
+var g_editableEffects = [];
+var g_editableEffectsSource = [];
+var g_currentEditEffect;
+var g_faderColorParam;
+var g_faderTransform;
+var g_renderTargetDisplayRoot;
+var g_sceneElement;
+var g_client;
+var g_scenePack;
+var g_proxyPack;
+var g_mainPack;
+var g_fadeParams = [];
+var g_mainViewInfo; // main view
+var g_hudRoot; // root transform for hud.
+var g_mainRoot;
+var g_proxyRoot;
+var g_waterLevel = 500;
+var g_reflectionViewInfo;
+var g_refractionViewInfo;
+var g_hudViewInfo;
+var g_loader;
+var g_loadInfo;
+var g_reflectionClipState;
+var g_refractionClipState;
+var g_mainRenderGraphRoot;
+var g_reflectionSurfaceSet;
+var g_refractionSurfaceSet;
+var g_skyDomeTransform;
+var g_waterTransform;
+var g_reflectionTexture;
+var g_refractionTexture;
+var g_reflectionImage;
+var g_refrectionImage;
+var g_depthSurface;
+var g_globalParams;
+var g_globalClockParam;
+var g_clipHeightParam;
+var g_lightPositionParam;
+var g_lightDirectionParam;
+var g_lightColorParam;
+var g_proxyOffsetParam;
+var g_originalLightColor;
+var g_viewPositionParam;
+var g_underwaterMaterials;
+var g_whiteTexture;
+var g_whiteSampler;
+var g_waterMaterial;
+var g_waterEffect;
+var g_waterColorAndSkyEffect;
+var g_waterStyle2Effect;
+var g_torchMaterial;
+var g_torchEmitter;
+var g_torchTexture;
+var g_mistTexture;
+var g_topMistEmitter;
+var g_bottomMistEmitter;
+var g_rippleEmitter;
+var g_skyDomeMaterial;
+var g_o3dWidth = -1;
+var g_o3dHeight = -1;
+var g_o3dElement;
+var g_cameraInfos = [];
+var g_cameraMoveSpeedMultiplier = 50;
+var g_keyCurrentlyDown = 0; // If any key is currently down this is true.
+var g_keyDown = []; // which keys are down by key code.
+var g_keyDownKeyCodeFunctions = {};
+var g_keyUpKeyCodeFunctions = {};
+var g_materialSwapTable = [];
+var g_showingSimpleMaterialsMode = 0;
+var g_simpleEffects = [];
+var g_originalSampler = { };
+var g_dragStartContext;
+var g_dragging = false;
+var g_imageShape;
+var g_imageMaterial;
+var g_imageEffect;
+var g_waterColor = [0.13, 0.19, 0.22, 1];
+var g_hudQuad;
+var g_fpsManager;
+var g_fpsVisible = false;
+var g_particleSystem;
+var g_particleLoader;
+var g_downloadPercent = -1;
+var g_showError = false;
+var g_sceneEffects = [];
+var g_sceneTexturesByURI;
+var g_renderTargetWidth = 256;
+var g_renderTargetHeight = 256;
+var g_perfMon;
+var g_shaders = {};
+
+var g_camera = {
+ farPlane: 80000,
+ nearPlane: 10,
+ up: [0, 0, 1],
+ fieldOfView: Math.PI / 4, // 45 degrees
+ eye: [-9662, -10927, 1920],
+ targetVector: [0.43, 0.90, 0.02],
+ xAxis: [0.8335, -0.5522, -0.0157],
+ minFieldOfView: 5 * Math.PI / 180,
+ maxFieldOfView: 70 * Math.PI / 180
+};
+
+var g_cameraPoints = [
+ { duration: 15,
+ start: {eye: [-7952.043, -3027.629, 1782.494],
+ targetVector: [0.054, 0.997, -0.059],
+ fieldOfView: 45},
+ end: {eye: [4579.533, -3707.403, 1782.494],
+ targetVector: [0.221, 0.963, 0.156],
+ fieldOfView: 45}},
+ { duration: 5,
+ start: {eye: [-9862.542, -11676.196, 1888.062],
+ targetVector: [0.552, 0.834, -0.007],
+ fieldOfView: 45},
+ end: {eye: [-4809.674, -4048.170, 1822.536],
+ targetVector: [0.552, 0.834, -0.007],
+ fieldOfView: 45}},
+ { duration: 5,
+ start: {eye: [2728.344, -6393.682, 2241.729],
+ targetVector: [-0.312, 0.949, 0.039],
+ fieldOfView: 45},
+ end: {eye: [-1683.553, 3379.889, 3616.049],
+ targetVector: [-0.118, 0.796, 0.594],
+ fieldOfView: 45}},
+ { duration: 5,
+ start: {eye: [1499.756, -2208.060, 380.914],
+ targetVector: [-0.537, 0.788, 0.303],
+ fieldOfView: 45},
+ end: {eye: [7333.003, -6937.257, 4163.998],
+ targetVector: [-0.811, 0.509, -0.290],
+ fieldOfView: 45}},
+ { duration: 5,
+ start: {eye: [4746.377, 1086.006, 3433.715],
+ targetVector: [-0.982, 0.188, -0.036],
+ fieldOfView: 45},
+ end: {eye: [4746.377, 1086.006, 3433.715],
+ targetVector: [-0.996, 0.072, 0.044],
+ fieldOfView: 6.49667876045379}},
+ { duration: 5,
+ start: {eye: [-4173.890, -4212.830, 398.267],
+ targetVector: [-0.339, 0.900, 0.272],
+ fieldOfView: 45},
+ end: {eye: [-4149.606, -4391.048, 2110.549],
+ targetVector: [0.007, 0.998, 0.065],
+ fieldOfView: 45}},
+ { duration: 5,
+ start: {eye: [-4809.674, -4048.170, 1822.536],
+ targetVector: [0.294, 0.956, -0.022],
+ fieldOfView: 45},
+ end: {eye: [-4535.282, -187.079, 2537.158],
+ targetVector: [0.146, 0.971, 0.190],
+ fieldOfView: 45}}];
+
+// The artists followed no rules. The assumption by the o3djs libraries
+// is that textures with non-one alpha should be drawn with alpha
+// blending on in the zOrderedDrawPass, otherwise they should be drawn
+// with alpha blending off in the performanceDrawPass but the artists gave
+// us textures that have non-one alpha even though they are meant to be
+// drawn opaque.
+//
+// The next most common way to decide whether to use opaque or
+// transparent blending is a naming convention but the arists
+// didn't do that either.
+//
+// For some cases it doesn't really matter but, (1) drawing with alpha
+// blending on is much slower than off and (2) drawing in the
+// zOrderedDrawPass has to sort which is slow and sometimes things
+// can get sorted wrong if they are too large relative to each other.
+//
+// So, here's a hard coded list to set the materials to the correct
+// drawList :-(
+function makeInfo(list, reflect, refract, main, type, effect) {
+ return {
+ list: list,
+ reflect: reflect,
+ refract: refract,
+ main: main,
+ type: type,
+ effect: effect};
+}
+var g_materialLists = {
+ // ---------------------------- list reflect refract main adj
+ '_6_-_Default': makeInfo(0, true, false, true, 1,
+ 'just_color'),
+ 'default': makeInfo(1, false, false, true, 1, // palmTreeB
+ 'diffuse_bump'),
+ 'Folg_BushA_mat': makeInfo(1, true, false, true, 1,
+ 'diffuse_bump'),
+ 'Folg_BushB_mat': makeInfo(1, true, false, true, 1,
+ 'diffuse_bump'),
+ 'Folg_BushC_mat': makeInfo(1, true, false, true, 1,
+ 'diffuse_bump'),
+ 'Folg_coralD_mat': makeInfo(1, false, true, false, 1,
+ 'diffuse'),
+ 'Folg_coralG_mat': makeInfo(1, false, true, false, 1,
+ 'diffuse'),
+ 'Folg_coralRockA_mat': makeInfo(0, false, true, false, 2,
+ 'diffuse_bump_2textures'),
+ 'Folg_coralRockB_mat': makeInfo(0, false, true, false, 2,
+ 'diffuse_bump_2textures'),
+ 'Folg_FernA_mat': makeInfo(1, true, false, true, 1,
+ 'diffuse'),
+ 'Folg_hangingFerns_mat': makeInfo(1, true, false, true, 1,
+ 'diffuse_bump'),
+ 'Folg_largeFernA_mat': makeInfo(1, true, false, true, 1,
+ 'diffuse'),
+ 'Folg_LeafyPlantA_mat': makeInfo(1, true, false, true, 1,
+ 'diffuse_bump'),
+ 'Folg_palmTreeA': makeInfo(1, false, false, true, 1,
+ 'diffuse'),
+ 'Prop_brokenShip_mat': makeInfo(0, true, true, true, 0,
+ 'diffuse'),
+ 'Prop_pillarA_mat': makeInfo(0, false, false, true, 0,
+ 'diffuse_bump_specular'),
+ 'prop_tikiMaskA': makeInfo(0, false, false, true, 0,
+ 'diffuse_bump_specular'),
+ 'Prop_TorchA_mat': makeInfo(0, false, false, true, 0,
+ 'diffuse'),
+ 'Prop_wallA_mat': makeInfo(0, false, false, true, 0,
+ 'diffuse_bump'),
+ 'Props_Bridge_mat': makeInfo(0, true, false, true, 0,
+ 'diffuse'),
+ 'Rock_Dark': makeInfo(0, true, true, true, 2,
+ 'diffuse_bump'),
+ 'Sand_Dark': makeInfo(0, false, true, false, 0,
+ 'diffuse_bump_2textures'),
+ 'Standard_2': makeInfo(0, true, true, false, 0, // palmfrawns
+ 'diffuse'),
+ 'Standard_3': makeInfo(1, false, true, true, 0, // waterfall
+ ''),
+ 'Rock_Dark_Island': makeInfo(0, true, true, true, 2, // Island
+ 'diffuse_bump_blend')};
+
+var g_randSeed = 0;
+var g_randRange = Math.pow(2, 32);
+
+/**
+ * Dumps a vector with a name label in a format useful for javascript.
+ * @param {string} name The name.
+ * @param {!o3d.math.Vector3} v The vector.
+ */
+function dumpVector(name, v) {
+ o3djs.dump.dump(
+ ' ' + name + ': [' +
+ v[0].toFixed(3) + ', ' +
+ v[1].toFixed(3) + ', ' +
+ v[2].toFixed(3) + '],\n');
+}
+
+/**
+ * Dump the camera info in a format useful for javascript.
+ */
+function dumpCameraInfo() {
+ o3djs.dump.dump('{');
+ dumpVector('eye', g_camera.eye);
+ dumpVector('targetVector', g_camera.targetVector);
+ o3djs.dump.dump(' fieldOfView: ' +
+ g_math.radToDeg(g_camera.fieldOfView) + '};\n');
+}
+
+// ***************************** Mouse functions *******************************
+
+/**
+ * Handler for onmousedown.
+ * @param {event} e A mouse event.
+ */
+function onMouseDown(e) {
+ if (!g_keyCurrentlyDown) {
+ g_dragging = true;
+ g_dragStartContext = {
+ view: o3djs.math.copyMatrix(g_mainViewInfo.drawContext.view),
+ projection: o3djs.math.copyMatrix(g_mainViewInfo.drawContext.projection),
+ offsetX: g_client.width * 0.5 - e.x,
+ offsetY: g_client.height * 0.5 - e.y
+ };
+ stopAnimatedCamera();
+ }
+}
+
+/**
+ * Handler for onmousemove.
+ * @param {event} e A mouse event.
+ */
+function onMouseMove(e) {
+ if (g_dragging) {
+ // Compute the world ray based on the view we had when we started dragging.
+ var worldRay = o3djs.picking.clientPositionToWorldRayEx(
+ g_o3dWidth - (e.x + g_dragStartContext.offsetX),
+ g_o3dHeight - (e.y + g_dragStartContext.offsetY),
+ g_dragStartContext.view,
+ g_dragStartContext.projection,
+ g_o3dWidth,
+ g_o3dHeight);
+
+ g_camera.targetVector = g_math.normalize(g_math.subVector(worldRay.near,
+ g_camera.eye));
+ updateCamera();
+ stopAnimatedCamera();
+ }
+}
+
+/**
+ * Handler for onmouseup.
+ * @param {event} e A mouse event.
+ */
+function onMouseUp(e) {
+ g_dragging = false;
+}
+
+/**
+ * Hander for the scroll wheel.
+ * @param {Event} e Mouse event.
+ */
+function onWheel(e) {
+ if (e.deltaY) {
+ var target = g_camera.minFieldOfView;
+ if (e.deltaY < 0) {
+ target = g_camera.maxFieldOfView;
+ }
+
+ g_camera.fieldOfView = g_math.lerpScalar(target, g_camera.fieldOfView, 0.9);
+
+ updateProjection();
+ stopAnimatedCamera();
+ }
+}
+
+// *************************** Keyboard functions ******************************
+
+/**
+ * Tracks key down events.
+ * @param {Event} e keyboard event.
+ */
+function onKeyDown(e) {
+ if (!g_dragging && !g_keyDown[e.keyCode]) {
+ ++g_keyCurrentlyDown;
+ g_keyDown[e.keyCode] = true;
+
+ var keyFunction = g_keyDownKeyCodeFunctions[e.keyCode];
+ if (keyFunction) {
+ keyFunction(e);
+ }
+ }
+}
+
+/**
+ * Tracks key up events.
+ * @param {Event} e keyboard event.
+ */
+function onKeyUp(e) {
+ if (g_keyDown[e.keyCode]) {
+ --g_keyCurrentlyDown;
+ g_keyDown[e.keyCode] = false;
+
+ var keyFunction = g_keyUpKeyCodeFunctions[e.keyCode];
+ if (keyFunction) {
+ keyFunction(e);
+ }
+ }
+}
+
+/**
+ * Converts a keyCode or charCode to a keyCode.
+ * @param {number|string} code The key code or char code.
+ * @return {number} the key code.
+ */
+function convertToKeyCode(code) {
+ if (typeof(code) == 'string') {
+ code = code.charCodeAt(0);
+ if (code >= 'a'.charCodeAt(0)) {
+ code += 65 - 'a'.charCodeAt(0);
+ }
+ }
+ return code;
+}
+
+/**
+ * Registers a key code with a key up function.
+ * @param {number|string} keyCode The key code to register a function with.
+ * @param {!function(!event): void} keyFunction A function that will be passed
+ * the event for the key.
+ */
+function registerKeyDownFunction(keyCode, keyFunction) {
+ g_keyDownKeyCodeFunctions[convertToKeyCode(keyCode)] = keyFunction;
+}
+
+/**
+ * Registers a key code with a key down function.
+ * @param {number|string} keyCode The key code to register a function with.
+ * @param {!function(!event): void} keyFunction A function that will be passed
+ * the event for the key.
+ */
+function registerKeyUpFunction(keyCode, keyFunction) {
+ g_keyUpKeyCodeFunctions[convertToKeyCode(keyCode)] = keyFunction;
+}
+
+/**
+ * Registers a key code with a both a key down and key up function.
+ * @param {number|string} keyCode The key code to register a function with.
+ * @param {!function(!event): void} keyUpFunction A function that will be passed
+ * the event for the key being released.
+ * @param {!function(!event): void} keyDownFunction A function that will be
+ * passed the event for the key being down..
+ */
+function registerKeyUpDownFunction(keyCode, keyUpFunction, keyDownFunction) {
+ registerKeyUpFunction(keyCode, keyUpFunction);
+ registerKeyUpFunction(keyCode, keyDownFunction);
+}
+
+/**
+ * Registers key handlers.
+ */
+function registerKeyHandlers() {
+ registerKeyDownFunction('0', keySetCamera);
+ registerKeyDownFunction('1', keySetCamera);
+ registerKeyDownFunction('2', keySetCamera);
+ registerKeyDownFunction('3', keySetCamera);
+ registerKeyDownFunction('4', keySetCamera);
+ registerKeyDownFunction('5', keySetCamera);
+ registerKeyDownFunction('6', keySetCamera);
+ registerKeyDownFunction('7', keySetCamera);
+ registerKeyDownFunction('8', keySetCamera);
+ registerKeyDownFunction('9', keySetCamera);
+
+ registerKeyDownFunction('h', toggleHelp);
+ registerKeyDownFunction('p', togglePropsPanel);
+ registerKeyDownFunction('m', toggleMaterialPanel);
+ registerKeyDownFunction('e', toggleEffectPanel);
+ registerKeyDownFunction('r', toggleRenderTargets);
+ registerKeyDownFunction('f', toggleFps);
+ registerKeyDownFunction('c', toggleSimpleMaterials);
+ registerKeyDownFunction('o', toggleWaterEffect);
+ registerKeyDownFunction('q', toggleDemoCamera);
+
+ // Comment the line below in to enable dumping camera info.
+ // This can be used to generate camera points for the animated
+ // camera but is only compatible with Firefox.
+ //registerKeyDownFunction('z', dumpCameraInfo);
+}
+
+// **************************** Camera Functions *******************************
+
+/**
+ * Updates the camera (the view matrix of the drawContext) with the current
+ * camera settings.
+ */
+function updateCamera() {
+ var target = g_math.addVector(g_camera.eye, g_camera.targetVector);
+ var view = g_math.matrix4.lookAt(g_camera.eye,
+ target,
+ g_camera.up);
+ g_viewPositionParam.value = g_camera.eye;
+ g_mainViewInfo.drawContext.view = view;
+ g_reflectionViewInfo.drawContext.view = view;
+ g_refractionViewInfo.drawContext.view = view;
+ var cameraMatrix = g_math.inverse4(view);
+ g_camera.xAxis = cameraMatrix[0].splice(0, 3);
+ g_updateRenderTargets = true;
+}
+
+/**
+ * Updates the projection matrix of the drawContext with the current camera
+ * settings.
+ */
+function updateProjection() {
+ // Create a perspective projection matrix.
+ g_mainViewInfo.drawContext.projection = g_math.matrix4.perspective(
+ g_camera.fieldOfView, g_o3dWidth / g_o3dHeight, g_camera.nearPlane,
+ g_camera.farPlane);
+
+ g_reflectionViewInfo.drawContext.projection = g_math.matrix4.perspective(
+ g_camera.fieldOfView, g_o3dWidth / g_o3dHeight,
+ g_camera.nearPlane, g_camera.farPlane);
+
+ g_refractionViewInfo.drawContext.projection = g_math.matrix4.perspective(
+ g_camera.fieldOfView, g_o3dWidth / g_o3dHeight,
+ g_camera.nearPlane, g_camera.farPlane);
+
+ g_hudViewInfo.drawContext.projection = g_math.matrix4.orthographic(
+ 0 + 0.5,
+ g_o3dWidth + 0.5,
+ g_o3dHeight + 0.5,
+ 0 + 0.5,
+ 0.001,
+ 1000);
+ g_updateRenderTargets = true;
+}
+
+/**
+ * Update the fader plane for the current client area size.
+ */
+function updateFaderPlane() {
+ // Scale fader plane to cover screen.
+ // If we made a custom shader for this this wouldn't be needed.
+ g_faderTransform.identity();
+ g_faderTransform.translate(0, 0, -10);
+ g_faderTransform.scale(g_client.width, g_client.height, 1);
+}
+
+/**
+ * Sets the camera to a preset.
+ * @param {number} cameraIndex Index of camera preset.
+ */
+function setCamera(cameraIndex) {
+ var cameraInfo = g_cameraInfos[cameraIndex];
+ // pull out camera info from view matrix.
+ var cameraMatrix = g_math.inverse4(cameraInfo.view);
+ g_camera.eye = cameraMatrix[3].splice(0, 3);
+ g_camera.targetVector = g_math.negativeVector(cameraMatrix[2].splice(0, 3));
+ //g_camera.fieldOfView = cameraInfo.fieldOfViewRadians;
+ g_camera.fieldOfView = o3djs.math.degToRad(45);
+
+ updateCamera();
+ updateProjection();
+ stopAnimatedCamera();
+}
+
+/**
+ * Moves the camera in its local X axis.
+ * @param {number} direction Position or negative amount to move.
+ */
+function moveCameraLeftRight(direction) {
+ direction *= g_cameraMoveSpeedMultiplier;
+ g_camera.eye = g_math.addVector(
+ g_camera.eye,
+ g_math.mulVectorScalar(g_camera.xAxis, direction));
+ updateCamera();
+ stopAnimatedCamera();
+}
+
+/**
+ * Moves the camera in its local Z axis.
+ * @param {number} direction Position or negative amount to move.
+ */
+function moveCameraForwardBack(direction) {
+ direction *= g_cameraMoveSpeedMultiplier;
+ g_camera.eye = g_math.addVector(
+ g_camera.eye,
+ g_math.mulVectorScalar(g_camera.targetVector, direction));
+ updateCamera();
+ stopAnimatedCamera();
+}
+
+// ************************ Effect Editor Functions ****************************
+
+/**
+ * Starts editing an effect.
+ * @param {number} effectId The clientId of the effect.
+ */
+function editEffect(effectId) {
+ if (g_currentEditEffect) {
+ // Save the current edit.
+ // TODO: would it be better to have a textarea per effect and
+ // hide / unhide them?
+ g_editableEffectsSource[g_currentEditEffect.clientId] =
+ g_effectTextAreaElement.value;
+ }
+
+ var effect = g_client.getObjectById(effectId);
+ g_effectTextAreaElement.value = g_editableEffectsSource[effectId];
+
+ g_currentEditEffect = effect;
+}
+
+/**
+ * Edits an effect from the value on an option element
+ */
+function editEffectFromElement() {
+ var element = o3djs.util.getElementById('effectselect');
+ editEffect(parseInt(element.value));
+}
+
+/**
+ * Compiles the current effect.
+ */
+function compileEffect() {
+ if (g_currentEditEffect) {
+ var source = g_effectTextAreaElement.value;
+
+ // Turn off the default error callback so we can get the error ourselves.
+ g_client.clearErrorCallback();
+ g_client.clearLastError();
+ g_compileEffect.loadFromFXString(source);
+ var error = g_client.lastError;
+ o3djs.base.setErrorHandler(g_client);
+ if (error) {
+ alert(error);
+ } else {
+ g_currentEditEffect.loadFromFXString(source);
+ // TODO: call createUniformParameters for all materials
+ // using this effect then call setupMaterialEditor so it will
+ // display new parameters.
+
+ // Tell the render targets to update.
+ g_updateRenderTargets = true;
+ }
+ }
+}
+
+/**
+ * Setup effect editor.
+ */
+function setupEffectEditor() {
+ // create an effect for testing.
+ g_compileEffect = g_mainPack.createObject('Effect');
+
+ var compileButton = o3djs.util.getElementById('compileButton');
+ compileButton.onclick = compileEffect;
+
+ // create pseudo tabs.
+ // TODO: Make it look prettier.
+ var html = '<select id="effectselect">';
+ for (var ii = 0; ii < g_editableEffects.length; ++ii) {
+ var effect = g_editableEffects[ii];
+ g_editableEffectsSource[effect.clientId] = effect.source;
+ html += '' +
+ '<option value="' + effect.clientId + '">' + effect.name + '</option>';
+ }
+ g_effectTabsElement.innerHTML = html + '</select>';
+ var element = o3djs.util.getElementById('effectselect');
+ element.onchange = editEffectFromElement;
+ element.onblur = editEffectFromElement;
+
+ // Setup the first effect.
+ editEffect(g_editableEffects[0].clientId);
+}
+
+// ************************* Prop Editor Functions *****************************
+
+/**
+ * Setups the prop editor.
+ */
+function setupPropEditor() {
+ var propPrefixes = {watersurface: true};
+ var transforms = g_scenePack.getObjectsByClassName('o3d.Transform');
+ for (var tt = 0; tt < transforms.length; ++tt) {
+ var transform = transforms[tt];
+ if (transform.shapes.length > 0) {
+ var name = transform.name;
+ //if (!isNaN(name.substring(name.length -1))) {
+ // var prefix = name.replace(/\d*$/, '');
+ // if (prefix.length > 0) {
+ // propPrefixes[prefix] = true;
+ // }
+ //}
+ propPrefixes[name] = true;
+ }
+ }
+
+ var html = '<table>';
+ var count = 0;
+ for (var prefix in propPrefixes) {
+ html += '' +
+ '<tr class="' + ((count % 2 == 0) ? 'even' : 'odd') + '"><td>' +
+ '<input id="prop_' + prefix + '" ' +
+ 'type="checkbox" CHECKED />' +
+ prefix +
+ '</td></tr>';
+ ++count;
+ }
+ g_propPanelElement.innerHTML = html + '</table>';
+ for (var prefix in propPrefixes) {
+ var input = o3djs.util.getElementById('prop_' + prefix);
+ input.onclick = o3djs.util.curry(toggleProp, prefix);
+ }
+}
+
+/**
+ * Toggles props.
+ * Goes through all transforms in the client and if their name starts with
+ * prefix sets their visibility to true or false.
+ * @param {string} prefix Prefix of props to toggle.
+ */
+function toggleProp(prefix) {
+ var element = o3djs.util.getElementById('prop_' + prefix);
+ var visible = element.checked;
+ // We should probably cache all the transforms since this is an expensive
+ // operation.
+ var transforms = g_client.getObjectsByClassName('o3d.Transform');
+ for (var tt = 0; tt < transforms.length; ++tt) {
+ var transform = transforms[tt];
+ if (transform.name.substring(0, prefix.length) === prefix) {
+ transform.visible = visible;
+ }
+ }
+ // Tell the render targets to update.
+ g_updateRenderTargets = true;
+}
+
+// *********************** Material Editor Functions ***************************
+
+/**
+ * Escapes a string, changing < to &lt;
+ * @param {string} str to escape.
+ * @return {string} escaped string.
+ */
+function escapeHTML(str) {
+ return str.replace(/</g, '&lt;');
+}
+
+/**
+ * Gets a param value as a string
+ * @param {!o3d.Param} param Param to get value from.
+ * @return {string} value of param as a string.
+ */
+function getParamAsString(param) {
+ if (param.isAClassName('o3d.ParamFloat')) {
+ return param.value.toFixed(5);
+ } else if (param.isAClassName('o3d.ParamFloat4')) {
+ var value = param.value;
+ for (var ii = 0; ii < value.length; ++ii) {
+ value[ii] = value[ii].toFixed(2);
+ }
+ return value.toString();
+ } else {
+ return '--na--';
+ }
+}
+
+/**
+ * Reads the current value of the input and sets the matching param to that
+ * value.
+ * @param {number} paramId Id of param and input.
+ */
+function updateParam(paramId) {
+ var param = g_client.getObjectById(paramId);
+ var element = o3djs.util.getElementById('param_' + paramId);
+ var value = element.value;
+ var error = false;
+ var v;
+ if (param.isAClassName('o3d.ParamFloat')) {
+ if (isNaN(value)) {
+ error = true;
+ }
+ v = parseFloat(value);
+ } else if (param.isAClassName('o3d.ParamFloat4')) {
+ var values = value.split(/ *, *| +/);
+ if (values.length != 4) {
+ error = true;
+ } else {
+ v = [];
+ for (var ii = 0; ii < values.length; ++ii) {
+ if (isNaN(values[ii])) {
+ error = true;
+ break;
+ }
+ v[ii] = parseFloat(values[ii]);
+ }
+ }
+ }
+
+ if (!error) {
+ param.value = v;
+ // Tell the render targets to update.
+ g_updateRenderTargets = true;
+ }
+
+ element.style.backgroundColor = error ? '#fcc' : '';
+}
+
+/**
+ * Creates the html to edit the given param object.
+ * @param {!o3d.ParamObject} paramObject The param object to create html for.
+ * @param {string} rowClass name of class for row.
+ * @return {string} the generated HTML.
+ */
+function createHTMLForParamObject(paramObject, rowClass) {
+ var html = '' +
+ '<tr class="' + rowClass + '">' +
+ '<td class="name" colspan="2">' + escapeHTML(paramObject.name) + '</td>' +
+ '</tr>';
+ var params = paramObject.params;
+ for (var pp = 0; pp < params.length; ++pp) {
+ var param = params[pp];
+ // Skip builtins and ones with an input connection.
+ if (param.name.substring(0, 4) !== 'o3d.' &&
+ param.inputConnection == null &&
+ (param.isAClassName('o3d.ParamFloat') ||
+ param.isAClassName('o3d.ParamFloat4'))) {
+ html += '' +
+ '<tr>' +
+ '<td class="field">' +
+ '<label>' + escapeHTML(param.name) + '</label>' +
+ '</td>' +
+ '<td class="value">' +
+ '<input type="text" id="param_' + param.clientId + '" ' +
+ 'value="' + getParamAsString(param) + '"></input>' +
+ '</td>' +
+ '</tr>';
+ }
+ }
+ return html;
+}
+
+/**
+ * Sets the onblur and onchange handlers in the html for a given param object.
+ * @param {!o3d.ParamObject} paramObject The param object to create html for.
+ */
+function setHTMLHandlersForParamObject(paramObject) {
+ var params = paramObject.params;
+ for (var pp = 0; pp < params.length; ++pp) {
+ var param = params[pp];
+ // Skip builtins and ones with an input connection.
+ if (param.name.substring(0, 4) !== 'o3d.' &&
+ param.inputConnection == null &&
+ (param.isAClassName('o3d.ParamFloat') ||
+ param.isAClassName('o3d.ParamFloat4'))) {
+ var input = o3djs.util.getElementById('param_' + param.clientId);
+ input.onblur = o3djs.util.curry(updateParam, param.clientId);
+ input.onchange = o3djs.util.curry(updateParam, param.clientId);
+ }
+ }
+}
+
+/**
+ * Sets up html with event handers to edit the material parameters.
+ */
+function setupMaterialEditor() {
+ var html = '<table>';
+ var materials = g_scenePack.getObjectsByClassName('o3d.Material');
+ var count = 0;
+ materials.unshift(g_globalParams);
+ materials.unshift(g_waterMaterial);
+ materials.unshift(g_underwaterMaterials[0]);
+ materials.unshift(g_underwaterMaterials[1]);
+ for (var mm = 0; mm < materials.length; ++mm) {
+ var material = materials[mm];
+ html += createHTMLForParamObject(material, count % 2 == 0 ? 'even' : 'odd');
+ ++count;
+ }
+ g_materialPanelElement.innerHTML = html + '</table>';
+
+ for (var mm = 0; mm < materials.length; ++mm) {
+ var material = materials[mm];
+ setHTMLHandlersForParamObject(material)
+ }
+}
+
+// ************************* Specific Key Handlers *****************************
+
+function setupWaterHeavyUpdateOnlyOnViewChange() {
+ g_waterMaterial.effect = g_waterEffect;
+}
+
+function setupWaterHeavyUpdateAlways() {
+ g_waterMaterial.effect = g_waterEffect;
+}
+
+function setupWaterJustSkyAndColor() {
+ g_waterMaterial.effect = g_waterColorAndSkyEffect;
+}
+
+function setupWaterStyle2() {
+ g_waterMaterial.effect = g_waterStyle2Effect;
+}
+
+/**
+ * Toggles the water effect.
+ * @param {Event} e Event for key that was pressed.
+ */
+function toggleWaterEffect(e) {
+ ++g_waterMode;
+ if (g_waterMode == 4) {
+ g_waterMode = 0;
+ }
+
+ switch (g_waterMode) {
+ case 0:
+ setupWaterHeavyUpdateOnlyOnViewChange();
+ break;
+ case 1:
+ setupWaterHeavyUpdateAlways();
+ break;
+ case 2:
+ setupWaterJustSkyAndColor();
+ break;
+ case 3:
+ setupWaterStyle2();
+ break;
+ }
+}
+
+/**
+ * Toggles the animted camera.
+ * @param {Event} e Event for key that was pressed.
+ */
+function toggleDemoCamera(e) {
+ g_runDemo = !g_runDemo;
+ if (g_runDemo) {
+ g_animateCamera = true;
+ } else {
+ stopAnimatedCamera();
+ }
+}
+
+/**
+ * Restores and original sampler on params of a certain name.
+ * @param {!o3d.ParamObject} paramObject Object to restore samplers on.
+ * @param {string} samplerName Name of sampler parameter to restore.
+ */
+function restoreOriginalSampler(paramObject, samplerName) {
+ var param = paramObject.getParam(samplerName);
+ if (param) {
+ param.value = g_originalSampler[param.clientId];
+ }
+}
+
+/**
+ * Replaces samplers on params of a certain name with a white sampler.
+ * @param {!o3d.ParamObject} paramObject Object to replace samplers on.
+ * @param {string} samplerName Name of sampler parameter to replace samplers on.
+ */
+function replaceSamplerWithWhiteSampler(paramObject, samplerName) {
+ var param = paramObject.getParam(samplerName);
+ if (param) {
+ g_originalSampler[param.clientId] = param.value;
+ param.value = g_whiteSampler;
+ }
+}
+
+/**
+ * Toggles the materials to simple effects.
+ * @param {Event} e Event for key that was pressed.
+ */
+function toggleSimpleMaterials(e) {
+ g_updateRenderTargets = true;
+
+ var materials = g_scenePack.getObjectsByClassName('o3d.Material');
+ materials.unshift(g_waterMaterial);
+ materials.unshift(g_underwaterMaterials[0]);
+ materials.unshift(g_underwaterMaterials[1]);
+
+ ++g_showingSimpleMaterialsMode;
+ g_showingSimpleMaterialsMode = g_showingSimpleMaterialsMode % 4;
+
+ switch (g_showingSimpleMaterialsMode) {
+ case 1: {
+ g_originalLightColor = g_lightColorParam.value;
+ g_lightColorParam.value = [1, 1, 1, 1];
+ var drawElements = g_scenePack.getObjectsByClassName('o3d.DrawElement');
+ for (var ii = 0; ii < drawElements.length; ++ii) {
+ replaceSamplerWithWhiteSampler(drawElements[ii], 'diffuseSampler');
+ }
+ break;
+ }
+ case 2: {
+ g_lightColorParam.value = g_originalLightColor;
+ var drawElements = g_scenePack.getObjectsByClassName('o3d.DrawElement');
+ for (var ii = 0; ii < drawElements.length; ++ii) {
+ restoreOriginalSampler(drawElements[ii], 'diffuseSampler');
+ }
+ break;
+ }
+ }
+
+ for (var mm = 0; mm < materials.length; ++mm) {
+ var material = materials[mm];
+ switch (g_showingSimpleMaterialsMode) {
+ case 0: {
+ material.effect = g_materialSwapTable[material.clientId];
+ break;
+ }
+ case 1: {
+ replaceSamplerWithWhiteSampler(material, 'diffuseSampler');
+ replaceSamplerWithWhiteSampler(material, 'diffuse2Sampler');
+ break;
+ }
+ case 2: {
+ restoreOriginalSampler(material, 'diffuseSampler');
+ restoreOriginalSampler(material, 'diffuse2Sampler');
+
+ var effect = material.effect;
+ g_materialSwapTable[material.clientId] = effect;
+ if (!g_simpleEffects[effect.clientId]) {
+ // eat some random number to get pleasing colors.
+ g_math.pseudoRandom();
+ g_math.pseudoRandom();
+ var newEffect = g_mainPack.createObject('Effect');
+ newEffect.loadFromFXString(g_shaders.simpleshader);
+ newEffect.createUniformParameters(newEffect);
+ newEffect.getParam('simpleColor').value = [
+ g_math.pseudoRandom(),
+ g_math.pseudoRandom(),
+ g_math.pseudoRandom(),
+ 1];
+ g_simpleEffects[effect.clientId] = newEffect;
+ }
+ material.effect = g_simpleEffects[effect.clientId];
+ break;
+ }
+ case 3: {
+ material.effect = g_imageEffect;
+ break;
+ }
+ }
+ }
+}
+
+/**
+ * Toggles the render target display.
+ * @param {Event} e Event for key that was pressed.
+ */
+function toggleRenderTargets(e) {
+ g_renderTargetDisplayRoot.visible = !g_renderTargetDisplayRoot.visible;
+}
+
+/**
+ * Toggles the fps display.
+ * @param {Event} e Event for key that was pressed.
+ */
+function toggleFps(e) {
+ g_fpsVisible = !g_fpsVisible;
+ g_fpsManager.setVisible(g_fpsVisible);
+}
+
+function togglePropsPanel(e) {
+ if (g_propPanelElement.style.display === '') {
+ g_propPanelElement.style.display = 'none';
+ g_sceneElement.style.width = '100%';
+ } else {
+ g_materialPanelElement.style.display = 'none';
+ g_propPanelElement.style.display = '';
+ g_sceneElement.style.width = '80%';
+ }
+}
+
+/**
+ * Toggles the material panel.
+ * @param {Event} e Event for key that was pressed.
+ */
+function toggleMaterialPanel(e) {
+ if (g_materialPanelElement.style.display === '') {
+ g_materialPanelElement.style.display = 'none';
+ g_sceneElement.style.width = '100%';
+ } else {
+ g_propPanelElement.style.display = 'none';
+ g_materialPanelElement.style.display = '';
+ g_sceneElement.style.width = '80%';
+ }
+}
+
+/**
+ * Toggles the effect panel.
+ * @param {Event} e Event for key that was pressed.
+ */
+function toggleEffectPanel(e) {
+ if (g_effectPanelElement.style.display === '') {
+ g_effectPanelElement.style.display = 'none';
+ g_upperPanelElement.style.height = '100%';
+ } else {
+ g_effectPanelElement.style.display = '';
+ g_upperPanelElement.style.height = '70%';
+ }
+}
+
+/**
+ * Sets the camera to a camera preset from a key press.
+ * @param {Event} e Event for key that was pressed. Expects 0-9.
+ */
+function keySetCamera(e) {
+ var index = e.keyCode - 49;
+ if (index < 0) {
+ index = 9;
+ }
+ var cameraInfo = g_cameraInfos[index];
+ if (cameraInfo) {
+ setCamera(index);
+ }
+}
+
+// ***************************** Scene Functions *******************************
+
+/**
+ * Sets the position of the sun, updating shader parameters.
+ * @param {!o3djs.math.Vector3} position The position of the sun.
+ */
+function setSunPosition(position) {
+ g_lightPositionParam.value = position;
+ g_lightDirectionParam.value = g_math.negativeVector(
+ g_math.normalize(position));
+ g_lightDirectionParam.value = g_math.normalize(position);
+}
+
+// ********************************** Misc *************************************
+
+/**
+ * Sets a param if it exists.
+ * @param {!o3d.ParamObject} paramObject The object that has the param.
+ * @param {string} paramName name of param.
+ * @param {*} value the value for the param.
+ */
+function setParam(paramObject, paramName, value) {
+ var param = paramObject.getParam(paramName);
+ if (param) {
+ param.value = value;
+ }
+}
+
+/**
+ * Binds a param if it exists.
+ * @param {!o3d.ParamObject} paramObject The object that has the param.
+ * @param {string} paramName name of param.
+ * @param {!o3d.Param} sourceParam The param to bind to.
+ */
+function bindParam(paramObject, paramName, sourceParam) {
+ var param = paramObject.getParam(paramName);
+ if (param) {
+ param.bind(sourceParam);
+ }
+}
+
+/**
+ * Prints out a transform tree.
+ * @param {!o3d.Transform} transform transform to print.
+ * @param {string} prefix Prefix to print.
+ */
+function dumpTransforms(transform, prefix) {
+ var materialName = '';
+ var shapes = transform.shapes;
+ if (shapes.length > 0) {
+ materialName = ' (' + shapes[0].elements[0].material.name + ')';
+ }
+ o3djs.dump.dump(prefix + transform.name + materialName + '\n');
+ var children = transform.children;
+ for (var cc = 0; cc < children.length; ++cc) {
+ dumpTransforms(children[cc], prefix + ' ');
+ }
+}
+
+/**
+ * Adds transforms at each level of the scene to group things by where they
+ * need to be rendered, refraction, main, both.
+ * @param {!o3d.Transform} transform Transform to scan.
+ */
+function getSpeedTransforms(transform) {
+ // 0 : neither, 1 : main, 2 : reflect, 3 : both
+ var speedTransforms = [];
+ var children = transform.children;
+ for (var cc = 0; cc < children.length; ++cc) {
+ var child = children[cc];
+ var check = child;
+
+ // If a child has a single child of the same but with the suffix
+ // '_PIVOT' use that as the check node.
+ var checkChildren = child.children;
+ if (checkChildren.length == 1 &&
+ checkChildren[0].name == child.name + '_PIVOT') {
+ check = checkChildren[0];
+ }
+ // If check has a shape that has a primitive that uses one of the
+ // materials on the list then attach it to a speed transform.
+ var grouped = false;
+ var shapes = check.shapes;
+ if (shapes.length > 0) {
+ // gets assume 1 shape, 1 element
+ var material = shapes[0].elements[0].material;
+ var materialInfo = g_materialLists[material.name];
+ if (materialInfo) {
+ grouped = true;
+ var index = (materialInfo.main ? 1 : 0) +
+ (materialInfo.reflect ? 2 : 0);
+ var speedTransform = speedTransforms[index];
+ if (!speedTransform) {
+ speedTransform = g_mainPack.createObject('Transform');
+ speedTransform.name = 'speed_' + index;
+ speedTransform.parent = transform;
+ speedTransforms[index] = speedTransform;
+ }
+ child.parent = speedTransform;
+ }
+ }
+
+ if (!grouped) {
+ getSpeedTransforms(child);
+ }
+ }
+
+ // Now add speed transforms to global list.
+ for (var ii = 0; ii < 4; ++ii) {
+ if (speedTransforms[ii]) {
+ g_speedTransforms[ii].push(speedTransforms[ii]);
+ }
+ }
+}
+
+/**
+ * Gets a texture from g_scenePack.
+ * @param {string} textureName Name of texture.
+ * @return {!o3d.Texture} The requested texture.
+ */
+function getTexture(textureName) {
+ // I'm searching by URI because the old conditioner sadly renamed all the
+ // textures making it next to impossible to find things :-(
+ if (!g_sceneTexturesByURI) {
+ g_sceneTexturesByURI = { };
+ var textures = g_scenePack.getObjectsByClassName('o3d.Texture');
+ for (var tt = 0; tt < textures.length; ++tt) {
+ var texture = textures[tt];
+ var uri = texture.getParam('uri').value;
+ g_sceneTexturesByURI[uri] = texture;
+ }
+ }
+
+ return g_sceneTexturesByURI['images/' + textureName];
+}
+
+/**
+ * Adds a texture to a material.
+ * @param {!o3d.Material} material Material to add texture to.
+ * @param {string} samplerName Name of sampler parameter to attach texture to.
+ * @param {string} textureName Name of texture.
+ */
+function addTexture(material, samplerName, textureName) {
+ var param = material.createParam(samplerName, 'ParamSampler');
+ var sampler = g_scenePack.createObject('Sampler');
+ param.value = sampler;
+ sampler.texture = getTexture(textureName);
+}
+
+/**
+ * Sets up the materials in the scene.
+ */
+function setupSceneMaterials() {
+ var drawLists = [g_mainViewInfo.performanceDrawList,
+ g_mainViewInfo.zOrderedDrawList];
+
+ var adjust = [
+ {shininess: 50, specular: [0.5, 0.5, 0.5, 1]},
+ {shininess: 100, specular: [0.3, 0.5, 0.3, 1]},
+ {shininess: 80, specular: [0.3, 0.3, 0.3, 1]}];
+
+ // Setup the materials. Because Collada can't really handle
+ // the materials needed we pretty much have to do this manaually. It would
+ // have been good to make a rule for it but I have no time.
+ for (var name in g_materialLists) {
+ var info = g_materialLists[name];
+ var materials = g_scenePack.getObjects(name, 'o3d.Material');
+ for (var mm = 0; mm < materials.length; ++mm) {
+ var material = materials[mm];
+ if (info.effect) {
+ var effect = g_sceneEffects[info.effect];
+ if (!effect) {
+ effect = g_scenePack.createObject('Effect');
+ effect.name = info.effect;
+ var fxString = g_shaders[info.effect];
+ effect.loadFromFXString(fxString);
+ g_sceneEffects[info.effect] = effect;
+ g_editableEffects.push(effect);
+ }
+ material.effect = effect;
+ material.createParam('lightWorldPos', 'ParamFloat3');
+ material.createParam('lightColor', 'ParamFloat4');
+ material.createParam('clipHeight', 'ParamFloat');
+
+ // special handling for island and seafloor materials.
+ if (info.effect == 'diffuse_bump_blend') {
+ addTexture(material, 'diffuse2Sampler', 'image1.dds');
+ }
+ }
+ material.drawList = drawLists[info.list];
+
+ // Manually connect all the materials' lightWorldPos params or a global
+ // light param.
+ bindParam(material, 'lightWorldPos', g_lightPositionParam);
+ bindParam(material, 'lightColor', g_lightColorParam);
+ bindParam(material, 'clipHeight', g_clipHeightParam);
+ setParam(material, 'ambient', [0.2, 0.2, 0.2, 1]);
+
+ var type = info.type;
+ setParam(material, 'shininess', adjust[type].shininess);
+ setParam(material, 'specular', adjust[type].specular);
+ }
+ }
+}
+
+/**
+ * Loads the proxy.
+ */
+function loadProxy() {
+ function callback(pack, parent, exception) {
+ g_loadInfo = null;
+ if (exception) {
+ showError(exception);
+ } else {
+ loadMainScene();
+
+ o3djs.pack.preparePack(pack, g_mainViewInfo);
+
+ var material = pack.getObjectsByClassName('o3d.Material')[0];
+ var effect = g_mainPack.createObject('Effect');
+ effect.loadFromFXString(g_shaders.proxy);
+ effect.createUniformParameters(material);
+ setParam(material, 'lightWorldPos', [0, -100000, 200000]);
+ setParam(material, 'ambient', [0, 0, 0, 0]);
+ setParam(material, 'diffuse', [0.7, 0.7, 0.7, 0.5]);
+ setParam(material, 'specular', [0, 0, 0, 0]);
+ bindParam(material, 'offset', g_proxyOffsetParam);
+ material.effect = effect;
+ material.drawList = g_mainViewInfo.zOrderedDrawList;
+ var state = pack.createObject('State');
+ material.state = state;
+ state.getStateParam('AlphaReference').value = 0.0;
+ state.getStateParam('CullMode').value = g_o3d.State.CULL_CCW;
+ var material2 = pack.createObject('Material');
+ effect.createUniformParameters(material2);
+ material2.copyParams(material);
+ bindParam(material2, 'offset', g_proxyOffsetParam);
+ material2.effect = effect;
+ material2.drawList = g_mainViewInfo.zOrderedDrawList;
+
+ state = pack.createObject('State');
+ material2.state = state;
+ state.getStateParam('AlphaReference').value = 0.0;
+ state.getStateParam('CullMode').value = g_o3d.State.CULL_CW;
+
+ parent.createDrawElements(pack, material2);
+ }
+ }
+
+ g_proxyPack = g_client.createPack();
+ g_proxyRoot = g_proxyPack.createObject('Transform');
+ g_proxyRoot.parent = g_baseRoot;
+
+ try {
+ var url = o3djs.util.getAbsoluteURI('assets/beach-low-poly.o3dtgz');
+ g_loadInfo = o3djs.scene.loadScene(g_client, g_proxyPack, g_proxyRoot,
+ url, callback, {opt_async: false});
+ } catch (e) {
+ showError(e);
+ }
+}
+
+/**
+ * Loads the main scene.
+ */
+function loadMainScene() {
+ function callback(pack, parent, exception) {
+ g_loadInfo = null;
+ if (exception) {
+ showError(exception);
+ } else {
+ g_proxyRoot.visible = false;
+
+ setupWaterfall();
+
+ // Generate draw elements and setup material draw lists.
+ parent.createDrawElements(pack, null);
+
+ setupSceneMaterials();
+
+ // Turn off culling since we can see the entire world checking culling
+ // is a waste of CPU time.
+ var elements = g_scenePack.getObjectsByClassName('o3d.Element');
+ for (var ee = 0; ee < elements.length; ++ee) {
+ elements[ee].cull = false;
+ o3djs.element.setBoundingBoxAndZSortPoint(elements[ee]);
+ }
+
+ // Add missing streams to terrain.
+ var terrainNames = [
+ 'terrainSpireA_002',
+ 'terrainSpireA_003',
+ 'terrainLargeRock',
+ 'terrainSpireA_001'];
+ var semantics = [
+ g_o3d.Stream.TEXCOORD,
+ g_o3d.Stream.BINORMAL,
+ g_o3d.Stream.TANGENT];
+ for (var tt = 0; tt < terrainNames.length; ++tt) {
+ var streamBank = g_scenePack.getObjects(terrainNames[tt],
+ 'o3d.StreamBank')[0];
+ for (var ii = 0; ii < semantics.length; ++ii) {
+ var stream = streamBank.getVertexStream(semantics[ii], 0);
+ streamBank.setVertexStream(semantics[ii], 1, stream.field, 0)
+ }
+ }
+
+ g_cameraInfos = o3djs.camera.getCameraInfos(parent,
+ g_o3dWidth,
+ g_o3dHeight);
+ setCamera(1);
+ setupUnderwater();
+
+ getSpeedTransforms(g_sceneRoot);
+
+ //o3djs.dump.dump("--------\n");
+ //dumpTransforms(g_sceneRoot, '');
+
+ setupMaterialEditor();
+ setupEffectEditor();
+ setupPropEditor();
+
+ registerKeyHandlers();
+
+ if (false) {
+ o3djs.dump.dump('---dump g_scenePack shapes---\n');
+ var shapes = g_scenePack.getObjectsByClassName('o3d.Shape');
+ for (var t = 0; t < shapes.length; t++) {
+ var shape = shapes[t];
+ o3djs.dump.dump('shape ' + t + ': ' + shape.name + '\n');
+ //o3djs.dump.dumpShape(shape);
+ }
+ }
+
+ if (false) {
+ o3djs.dump.dump('---dump g_scenePack materials---\n');
+ var materials = g_scenePack.getObjectsByClassName('o3d.Material');
+ for (var t = 0; t < materials.length; t++) {
+ var material = materials[t];
+ o3djs.dump.dump (
+ ' ' + t + ' : ' + material.className +
+ ' : "' + material.name + '"\n');
+ var params = material.params;
+ for (var p = 0; p < params.length; ++p) {
+ var param = params[p];
+ if (param.className == 'o3d.ParamSampler') {
+ o3djs.dump.dump(' ' + p + ': ' +
+ param.value.texture.name + '\n');
+ }
+ }
+ //o3djs.dump.dumpParams(materials[t], ' ');
+ }
+ }
+
+ if (false) {
+ o3djs.dump.dump('---dump g_scenePack textures---\n');
+ var textures = g_scenePack.getObjectsByClassName('o3d.Texture');
+ for (var t = 0; t < textures.length; t++) {
+ o3djs.dump.dump(t + ': ');
+ o3djs.dump.dumpTexture(textures[t]);
+ }
+
+ o3djs.dump.dump('---dump g_scenePack effects---\n');
+ var effects = g_scenePack.getObjectsByClassName('o3d.Effect');
+ for (var t = 0; t < effects.length; t++) {
+ o3djs.dump.dump (' ' + t + ' : ' + effects[t].className +
+ ' : "' + effects[t].name + '"\n');
+ o3djs.dump.dumpParams(effects[t], ' ');
+ }
+ }
+ }
+ g_perfMon = o3djs.performance.createPerformanceMonitor(
+ 25, 35, increaseRenderTargetResolution, decreaseRenderTargetResolution);
+ }
+
+ try {
+ // We need to make a subloader because we can't make the particles
+ // until both the scene and the particle textures are loaded.
+ g_loadInfo = g_loader.loadInfo;
+ g_particleLoader = g_loader.createLoader(setupParticles);
+ g_particleLoader.loadTexture(
+ g_mainPack,
+ o3djs.util.getAbsoluteURI('assets/pe_fire.jpg'),
+ function(texture, success) {
+ g_torchTexture = texture;
+ });
+ g_particleLoader.loadTexture(
+ g_mainPack,
+ o3djs.util.getAbsoluteURI('assets/pe_mist.png'),
+ function(texture, success) {
+ g_mistTexture = texture;
+ });
+
+ var url = o3djs.util.getAbsoluteURI('assets/beachdemo.o3dtgz');
+ g_particleLoader.loadScene(
+ g_client, g_scenePack, g_sceneRoot, url, callback, {opt_async: false});
+ g_particleLoader.finish()
+ g_loader.finish();
+ } catch (e) {
+ showError(e);
+ }
+}
+
+/**
+ * Records the client's size if it's changed.
+ */
+function setClientSize() {
+ var newWidth = parseInt(g_client.width);
+ var newHeight = parseInt(g_client.height);
+
+ if (newWidth != g_o3dWidth || newHeight != g_o3dHeight) {
+ g_o3dWidth = newWidth;
+ g_o3dHeight = newHeight;
+
+ updateProjection();
+ g_fpsManager.resize(g_o3dWidth, g_o3dHeight);
+ g_fpsManager.setPosition(g_o3dWidth - 80, 10);
+ updateFaderPlane();
+ }
+}
+
+/**
+ * Moves the camera based on key state.
+ */
+function handleCameraKeys() {
+ var moveX = 0;
+ var moveY = 0;
+
+ if (g_keyDown[37] || g_keyDown[65]) {
+ moveX = -1;
+ }
+ if (g_keyDown[39] || g_keyDown[68]) {
+ moveX = 1;
+ }
+ if (g_keyDown[38] || g_keyDown[87]) {
+ moveY = 1;
+ }
+ if (g_keyDown[40] || g_keyDown[83]) {
+ moveY = -1;
+ }
+
+ if (moveX) {
+ moveCameraLeftRight(moveX);
+ stopAnimatedCamera();
+ }
+
+ if (moveY) {
+ moveCameraForwardBack(moveY);
+ stopAnimatedCamera();
+ }
+}
+
+/**
+ * Sets the speed transforms visible or invisible to turn on/off whole groups of
+ * shapes not needed for certain rendering.
+ * @param {boolean} main Turn on stuff marked for main.
+ * @param {boolean} reflect Turn on stuff marked for reflect.
+ * @param {boolean} force Force visible to true.
+ */
+function setSpeedTransforms(main, reflect, force) {
+ var mask = (main ? 1 : 0) + (reflect ? 2 : 0);
+ for (var ii = 0; ii < 4; ++ii) {
+ var visible = ((ii & mask) != 0) || force;
+ var speedTransforms = g_speedTransforms[ii];
+ for (var jj = 0; jj < speedTransforms.length; ++jj) {
+ speedTransforms[jj].visible = visible;
+ }
+ }
+}
+
+/**
+ * Eases in a number.
+ * @param {number} value Value to ease in. Must be 0 to 1.
+ * @return {number} Ease in version of value.
+ */
+function easeIn(value) {
+ return 1 - Math.cos(value * Math.PI * 0.5);
+}
+
+/**
+ * Eases out a number.
+ * @param {number} value Value to ease out. Must be 0 to 1.
+ * @return {number} Ease out version of value.
+ */
+function easeOut(value) {
+ return Math.sin(value * Math.PI * 0.5);
+}
+
+/**
+ * Ease in and out a number.
+ * @param {number} value Value to ease in out. Must be 0 to 1.
+ * @return {number} Ease in out version of value.
+ */
+function easeInOut(value) {
+ if (value < 0.5) {
+ return easeIn(value * 2) * 0.5;
+ } else {
+ return easeOut(value * 2 - 1) * 0.5 + 0.5;
+ }
+}
+
+/**
+ * Stops the animated camera.
+ */
+function stopAnimatedCamera() {
+ g_animateCamera = false;
+ g_demoTimer = 30;
+ g_cameraTimer = 0;
+ g_faderTransform.visible = false;
+}
+
+/**
+ * Animates the camera.
+ * @param {number} elapsedTime Elapsed time in seconds.
+ */
+function animateCamera(elapsedTime) {
+ if (g_animateCamera && window.g_finished) {
+ g_cameraTimer -= elapsedTime;
+
+ if (g_cameraTimer <= 0) {
+ ++g_cameraPointIndex;
+ if (g_cameraPointIndex >= g_cameraPoints.length) {
+ g_cameraPointIndex = 0;
+ }
+ g_cameraPoint = g_cameraPoints[g_cameraPointIndex];
+ g_cameraDuration = g_cameraPoint.duration * 3;
+ g_cameraTimer = g_cameraDuration;
+ }
+
+ var lerp = 1;
+ if (g_cameraTimer > 1) {
+ var moveDuration = g_cameraDuration - 1;
+ var timer = g_cameraTimer - 1;
+ lerp = easeInOut(1 - timer / moveDuration);
+
+ if (g_cameraTimer > g_cameraDuration - 1) {
+ var fade = g_cameraTimer - (g_cameraDuration - 1);
+ g_faderTransform.visible = true;
+ g_faderColorParam.value = [0, 0, 0, fade];
+ } else {
+ g_faderTransform.visible = false;
+ }
+ } else {
+ g_faderTransform.visible = true;
+ g_faderColorParam.value = [0, 0, 0, 1 - g_cameraTimer];
+ }
+
+ g_camera.eye = g_math.lerpVector(g_cameraPoint.start.eye,
+ g_cameraPoint.end.eye,
+ lerp);
+ g_camera.targetVector = g_math.lerpVector(g_cameraPoint.start.targetVector,
+ g_cameraPoint.end.targetVector,
+ lerp);
+ g_camera.fieldOfView = g_math.degToRad(
+ g_math.lerpScalar(g_cameraPoint.start.fieldOfView,
+ g_cameraPoint.end.fieldOfView,
+ lerp));
+
+ updateCamera();
+ updateProjection();
+ } else {
+ if (g_runDemo) {
+ g_demoTimer -= elapsedTime;
+ if (g_demoTimer <= 0) {
+ g_animateCamera = true;
+ }
+ }
+ }
+}
+
+/**
+ * Called every frame.
+ * @param {!o3d.RenderEvent} renderEvent Info about this frame.
+ */
+function onRender(renderEvent) {
+ // save off the render event so look at it from the debugger.
+ g_re = renderEvent;
+
+ var elapsedTime = renderEvent.elapsedTime * window.g_timeMult;
+ if (g_hudFadeTime > 0) {
+ g_hudFadeTime -= elapsedTime;
+ if (g_hudFadeTime <= 0) {
+ g_hudQuad.transform.visible = false;
+ }
+ }
+
+ // This is for selenium so that the hud is predictable.
+ if (g_hudFadeTime > 0 && window.g_timeMult == 0) {
+ g_hudFadeTime = 0;
+ g_hudQuad.transform.visible = false;
+ }
+
+ // Normally I'd have used a SecondCounter but so we can run this in
+ // selenium I set it up this way to be easy.
+ window.g_clock += elapsedTime;
+ g_globalClockParam.value = window.g_clock;
+
+ if (g_loadInfo) {
+ var progressInfo = g_loadInfo.getKnownProgressInfoSoFar();
+ g_proxyOffsetParam.value = progressInfo.percent / 100 * PROXY_HEIGHT;
+ if (progressInfo.percent != g_downloadPercent) {
+ g_downloadPercent = progressInfo.percent;
+ setHudText('Loading... ' + progressInfo.percent + '%' +
+ ' (' + progressInfo.downloaded +
+ ' of ' + progressInfo.totalBytes + progressInfo.suffix + ')');
+ }
+ }
+
+ // This if is for selenium to make the camera predictable.
+ if (window.g_timeMult) {
+ animateCamera(elapsedTime);
+ } else {
+ setCamera(1);
+ }
+
+ handleCameraKeys();
+ setClientSize();
+ g_fpsManager.update(renderEvent);
+
+ if (g_updateRenderTargets || g_waterMode == 1) {
+ g_updateRenderTargets = false;
+
+ // Render the reflection texture.
+ setSpeedTransforms(false, true, false);
+ g_clipHeightParam.value = g_reflectionClipHeight;
+ g_client.root.identity();
+ g_client.root.scale(1, 1, -1); // flip the scene
+ g_client.renderTree(g_reflectionSurfaceSet);
+
+ // Render the refraction texture.
+ setSpeedTransforms(true, true, true);
+ g_client.root.identity();
+ g_client.root.scale(1, 1, 1 /* 0.75 */); // squish the scene.
+ g_client.renderTree(g_refractionSurfaceSet);
+ }
+
+ // Render the main scene.
+ setSpeedTransforms(true, false, false);
+ g_clipHeightParam.value = g_mainClipHeight;
+ g_client.root.identity();
+ g_client.renderTree(g_mainViewInfo.root);
+
+ // Render the HUD.
+ g_client.renderTree(g_hudViewInfo.root);
+
+ // Render the FPS display.
+ g_client.renderTree(g_fpsManager.viewInfo.root);
+
+ if (g_perfMon) {
+ g_perfMon.onRender(renderEvent.elapsedTime);
+ }
+}
+
+function onAllLoadingFinished() {
+ g_loader = null;
+ g_animateCamera = true;
+
+ showHint();
+
+ window.o3d_prepForSelenium = prepForSelenium;
+ window.g_finished = true; // for selenium testing.
+}
+
+// Put the demo in a consistent state.
+function prepForSelenium() {
+ // Turn off the perf monitor.
+ g_perfMon = null;
+
+ // Set the render targets to a fixed size.
+ g_renderTargetWidth = 256;
+ g_renderTargetHeight = 256;
+ setupRenderTargets();
+}
+
+/**
+ * Creates the client area.
+ */
+function init() {
+ // These are here so they are shared by both V8 and the browser.
+ window.g_finished = false; // for selenium
+ window.g_timeMult = 1;
+ window.g_clock = 0;
+
+ // Comment out the line below to run the sample in the browser JavaScript
+ // engine. This may be helpful for debugging.
+ o3djs.util.setMainEngine(o3djs.util.Engine.V8);
+
+ o3djs.util.addScriptUri('');
+ o3djs.util.makeClients(initStep2);
+}
+
+function setupRenderTargets() {
+ var oldReflectionTexture;
+ var oldRefractionTexture;
+ var oldDepthSurface;
+
+ if (g_reflectionTexture) {
+ g_mainPack.removeObject(g_reflectionSurfaceSet.renderSurface);
+ g_mainPack.removeObject(g_refractionSurfaceSet.renderSurface);
+ g_mainPack.removeObject(g_reflectionTexture);
+ g_mainPack.removeObject(g_refractionTexture);
+ g_mainPack.removeObject(g_depthSurface);
+ } else {
+ // First time only.
+ g_reflectionSurfaceSet = g_mainPack.createObject('RenderSurfaceSet');
+ g_refractionSurfaceSet = g_mainPack.createObject('RenderSurfaceSet');
+ }
+
+ // Create Render Targets for the reflection and refraction.
+ g_reflectionTexture = g_mainPack.createTexture2D(g_renderTargetWidth,
+ g_renderTargetHeight,
+ g_o3d.Texture.ARGB8, 1,
+ true);
+ var reflectionSurface = g_reflectionTexture.getRenderSurface(0);
+ g_refractionTexture = g_mainPack.createTexture2D(g_renderTargetWidth,
+ g_renderTargetHeight,
+ g_o3d.Texture.XRGB8, 1,
+ true);
+ var refractionSurface = g_refractionTexture.getRenderSurface(0);
+ g_depthSurface = g_mainPack.createDepthStencilSurface(g_renderTargetWidth,
+ g_renderTargetHeight);
+
+ // Set up the render graph to generate them.
+ g_reflectionSurfaceSet.renderSurface = reflectionSurface;
+ g_reflectionSurfaceSet.renderDepthStencilSurface = g_depthSurface;
+
+ g_refractionSurfaceSet.renderSurface = refractionSurface;
+ g_refractionSurfaceSet.renderDepthStencilSurface = g_depthSurface;
+
+ g_updateRenderTargets = true;
+
+ if (g_waterMaterial) { // Every time after the first.
+ var sampler = g_waterMaterial.getParam('reflectionSampler').value;
+ sampler.texture = g_reflectionTexture;
+ sampler = g_waterMaterial.getParam('refractionSampler').value;
+ sampler.texture = g_refractionTexture;
+ g_reflectionImage.sampler.texture = g_reflectionTexture;
+ g_refractionImage.sampler.texture = g_refractionTexture;
+ }
+}
+
+function increaseRenderTargetResolution() {
+ var changed;
+ if (g_renderTargetWidth < 2048) {
+ g_renderTargetWidth <<= 1;
+ changed = true;
+ }
+ if (g_renderTargetHeight < 2048) {
+ g_renderTargetHeight <<= 1;
+ changed = true;
+ }
+ setupRenderTargets();
+}
+
+function decreaseRenderTargetResolution() {
+ var changed;
+ if (g_renderTargetWidth > 256) {
+ g_renderTargetWidth >>= 1;
+ changed = true;
+ }
+ if (g_renderTargetHeight > 256) {
+ g_renderTargetHeight >>= 1;
+ changed = true;
+ }
+ setupRenderTargets();
+}
+
+/**
+ * Loads shader files into g_shaders object.
+ */
+function loadShaders() {
+ var ii;
+ var n;
+ var names = [
+ 'diffuse',
+ 'diffuse_bump',
+ 'diffuse_bump_2textures',
+ 'diffuse_bump_blend',
+ 'diffuse_bump_blend_underwater',
+ 'diffuse_bump_specular',
+ 'imageshader',
+ 'just_color',
+ 'proxy',
+ 'simpleshader',
+ 'skydomeshader',
+ 'underwatershader',
+ 'watercolorandskyshader',
+ 'waterfallshader',
+ 'watershader',
+ 'waterstyle2',
+ ];
+
+ o3djs.effect.setLanguage('glsl');
+ o3djs.particles.setLanguage('glsl');
+ o3djs.canvas.setLanguage('glsl');
+ o3djs.fps.setLanguage('glsl');
+
+ for (ii = 0; ii < names.length; ++ii) {
+ n = names[ii];
+ g_shaders[n] =
+ o3djs.io.loadTextFileSynchronous('shaders_glsl/' + n + '.glsl');
+ }
+}
+
+/**
+ * Initializes O3D and loads the scene into the transform graph.
+ * @param {Array} clientElements Array of o3d object elements.
+ */
+function initStep2(clientElements) {
+ loadShaders();
+
+ g_materialPanelElement = o3djs.util.getElementById('materialpanel');
+ g_propPanelElement = o3djs.util.getElementById('proppanel');
+ g_effectPanelElement = o3djs.util.getElementById('effectpanel');
+ g_upperPanelElement = o3djs.util.getElementById('upperpanel');
+ g_effectTabsElement = o3djs.util.getElementById('effecttabs');
+ g_effectTextAreaElement = o3djs.util.getElementById('effecttextarea');
+ g_sceneElement = o3djs.util.getElementById('o3d');
+
+ g_o3dElement = clientElements[0];
+ g_o3d = g_o3dElement.o3d;
+ g_math = o3djs.math;
+ g_quaternions = o3djs.quaternions;
+ window.g_client = g_client = g_o3dElement.client;
+
+ g_mainPack = g_client.createPack();
+ g_scenePack = g_client.createPack();
+
+ g_mainRoot = g_mainPack.createObject('Transform');
+ g_baseRoot = g_scenePack.createObject('Transform');
+ g_baseRoot.parent = g_mainRoot;
+ g_sceneRoot = g_scenePack.createObject('Transform');
+ g_sceneRoot.parent = g_baseRoot;
+ g_mainRoot.parent = g_client.root;
+ g_sceneRoot.translate(0, 0, -g_waterLevel);
+
+ setupRenderTargets();
+
+ // Create states to set clipping.
+ g_reflectionClipState = g_mainPack.createObject('State');
+ g_reflectionClipState.getStateParam('AlphaTestEnable').value = true;
+ g_reflectionClipState.getStateParam('AlphaComparisonFunction').value =
+ g_o3d.State.CMP_GREATER;
+ var reflectionStateSet = g_mainPack.createObject('StateSet');
+ reflectionStateSet.state = g_reflectionClipState;
+ reflectionStateSet.parent = g_reflectionSurfaceSet;
+
+ var fStrength = 4.0;
+ g_refractionClipState = g_mainPack.createObject('State');
+ g_refractionClipState.getStateParam('AlphaTestEnable').value = true;
+ g_refractionClipState.getStateParam('AlphaComparisonFunction').value =
+ g_o3d.State.CMP_GREATER;
+
+ var refractionStateSet = g_mainPack.createObject('StateSet');
+ refractionStateSet.state = g_refractionClipState;
+ refractionStateSet.parent = g_refractionSurfaceSet;
+
+ // Create the render graph for the main view.
+ g_mainViewInfo = o3djs.rendergraph.createBasicView(
+ g_mainPack,
+ g_mainRoot);
+
+ // Create a render graph for the reflection map
+ g_reflectionViewInfo = o3djs.rendergraph.createExtraView(g_mainViewInfo);
+ g_reflectionViewInfo.root.parent = reflectionStateSet;
+ g_reflectionViewInfo.treeTraversal.transform = g_baseRoot;
+ g_reflectionViewInfo.performanceState.getStateParam('CullMode').value =
+ g_o3d.State.CULL_CCW;
+ g_reflectionViewInfo.performanceState.getStateParam(
+ 'ColorWriteEnable').value = 15;
+ g_reflectionViewInfo.zOrderedState.getStateParam('CullMode').value =
+ g_o3d.State.CULL_CCW;
+ g_reflectionViewInfo.zOrderedState.getStateParam(
+ 'ColorWriteEnable').value = 15;
+
+ // Create a render graph for the refraction map
+ g_refractionViewInfo = o3djs.rendergraph.createBasicView(
+ g_mainPack,
+ g_baseRoot,
+ refractionStateSet);
+
+ // Create a render graph for the HUD
+ g_hudRoot = g_mainPack.createObject('Transform');
+ g_hudViewInfo = o3djs.rendergraph.createBasicView(
+ g_mainPack,
+ g_hudRoot);
+ g_hudViewInfo.clearBuffer.clearColorFlag = false;
+
+ g_hudViewInfo.zOrderedState.getStateParam('CullMode').value =
+ g_o3d.State.CULL_NONE;
+
+ g_hudViewInfo.drawContext.view = g_math.matrix4.lookAt(
+ [0, 0, 1], // eye
+ [0, 0, 0], // target
+ [0, 1, 0]); // up
+
+ //g_reflectionViewInfo.clearBuffer.clearColor = [0.5, 1, 0.5, 1];
+ //g_refractionViewInfo.clearBuffer.clearColor = [0.5, 0.5, 1, 1];
+ g_reflectionViewInfo.clearBuffer.clearColor = [0, 0, 0, 0];
+ g_refractionViewInfo.clearBuffer.clearColor = g_waterColor;
+
+ // Set some names so it's easier to debug.
+ g_mainViewInfo.performanceDrawList.name = 'performanceDrawList';
+ g_mainViewInfo.zOrderedDrawList.name = 'zOrderedDrawList';
+
+ // Turn off culling for transparent stuff so we can see the backs of leaves.
+ g_mainViewInfo.zOrderedState.getStateParam('CullMode').value =
+ g_o3d.State.CULL_NONE;
+ g_mainViewInfo.zOrderedState.getStateParam('AlphaReference').value = 0.7;
+
+ // Turn on alpha test in the performance list for our clipping plane.
+ g_mainViewInfo.performanceState.getStateParam('AlphaTestEnable').value = true;
+ g_mainViewInfo.performanceState.getStateParam(
+ 'AlphaComparisonFunction').value = g_o3d.State.CMP_GREATER;
+
+ g_fpsManager = o3djs.fps.createFPSManager(g_mainPack,
+ g_client.width,
+ g_client.height);
+ g_fpsManager.setVisible(false);
+
+ // Create a param object to hold a few params to drive things globally.
+ g_globalParams = g_mainPack.createObject('ParamObject');
+ g_globalParams.name = 'global params';
+ g_globalClockParam = g_globalParams.createParam('clock', 'ParamFloat');
+ g_lightPositionParam = g_globalParams.createParam('lightWorldPos',
+ 'ParamFloat3');
+ g_lightDirectionParam = g_globalParams.createParam('lightDirection',
+ 'ParamFloat3');
+ g_lightColorParam = g_globalParams.createParam('lightColor',
+ 'ParamFloat4');
+ g_lightColorParam.value = [2.0, 1.8, 1.4, 1];
+ setSunPosition([0, -100000, 200000]);
+
+ g_clipHeightParam = g_globalParams.createParam('clipHeight', 'ParamFloat');
+ g_proxyOffsetParam = g_globalParams.createParam('offset', 'ParamFloat');
+
+ g_particleSystem = o3djs.particles.createParticleSystem(
+ g_mainPack,
+ g_mainViewInfo,
+ g_globalClockParam,
+ g_math.pseudoRandom);
+
+ // Since we set the state for the draw pass to 'AlphaReference' = 0.7
+ // We need to set it back to 0.0 for the particles.
+ for (var ii = 0; ii < g_particleSystem.particleStates.length; ++ii) {
+ g_particleSystem.particleStates[ii].getStateParam(
+ 'AlphaReference').value = 0.0;
+ }
+
+ g_editableEffects.push(g_particleSystem.effects[0]);
+ g_editableEffects.push(g_particleSystem.effects[1]);
+
+ g_loader = o3djs.loader.createLoader(onAllLoadingFinished);
+
+ setupWater();
+ setupHud();
+
+ loadProxy();
+
+ // It's important to create stuff in g_mainPack and not g_scenePack because
+ // g_scenePack will be scanned and modified after loading.
+
+ setClientSize();
+ updateCamera();
+ updateProjection();
+
+ o3djs.event.addEventListener(g_o3dElement, 'mousedown', onMouseDown);
+ o3djs.event.addEventListener(g_o3dElement, 'mousemove', onMouseMove);
+ o3djs.event.addEventListener(g_o3dElement, 'mouseup', onMouseUp);
+ o3djs.event.addEventListener(g_o3dElement, 'wheel', onWheel);
+ o3djs.event.addEventListener(g_o3dElement, 'keydown', onKeyDown);
+ o3djs.event.addEventListener(g_o3dElement, 'keyup', onKeyUp);
+
+ // If we don't check the size of the client area every frame we don't get a
+ // chance to adjust the perspective matrix fast enough to keep up with the
+ // browser resizing us.
+ g_client.setRenderCallback(onRender);
+
+ // Because we don't render the render targets every frame of the OS has
+ // to reset them their contents will get lost. In that case O3D will notify
+ // us through this callback so we can re-render our render targets.
+ g_client.setLostResourcesCallback(function() {
+ g_updateRenderTargets = true;
+ });
+}
+
+/**
+ * Loads a texture.
+ *
+ * @param {!o3djs.loader.Loader} loader Loader to use to load texture.
+ * @param {!o3d.Pack} pack Pack to load texture in.
+ * @param {!o3d.Material} material Material to attach sampler to.
+ * @param {string} samplerName Name of sampler.
+ * @param {string} textureName filename of texture.
+ * @return {!o3d.Sampler} Sampler attached to material.
+ */
+function loadTexture(loader, pack, material, samplerName, textureName) {
+ var sampler = pack.createObject('Sampler');
+ setParam(material, samplerName, sampler);
+
+ var url = o3djs.util.getAbsoluteURI('assets/' + textureName);
+ loader.loadTexture(pack, url, function(texture, success) {
+ sampler.texture = texture;
+ });
+
+ return sampler;
+}
+
+/**
+ * Create the waterfall effect.
+ */
+function setupWaterfall() {
+ // A prefix for waterfall materials would have been better.
+ var material = g_scenePack.getObjects('Standard_3', 'o3d.Material')[0];
+
+ // Create an effect with a v offset parameter so we can scroll the
+ // UVs.
+ var effect = g_mainPack.createObject('Effect');
+ effect.name = 'waterfall';
+ effect.loadFromFXString(g_shaders.waterfallshader);
+ effect.createUniformParameters(material);
+
+ g_editableEffects.push(effect);
+
+ // Set the waterfall to use additive blending.
+ var state = g_mainPack.createObject('State');
+ state.getStateParam('SourceBlendFunction').value =
+ g_o3d.State.BLENDFUNC_SOURCE_ALPHA;
+ state.getStateParam('DestinationBlendFunction').value =
+ g_o3d.State.BLENDFUNC_ONE;
+ state.getStateParam('AlphaReference').value = 0.0;
+ //state.getStateParam('ZWriteEnable').value = false;
+
+ material.state = state;
+ material.drawList = g_mainViewInfo.zOrderedDrawList;
+ material.effect = effect;
+
+ // Create a counter to scroll the Vs.
+ // var counter = g_mainPack.createObject('SecondCounter');
+ // material.getParam('vOffset').bind(counter.getParam('count'));
+ //
+ // For selenium testing we need a global clock.
+ material.getParam('vOffset').bind(g_globalClockParam);
+
+}
+
+/**
+ * Setup underwater.
+ * Must be called after the scene has loaded.
+ * NOTE: The coral needs a new shader that supports normal maps
+ * but it's a low priority to fix right now.
+ */
+function setupUnderwater() {
+ var effect = g_mainPack.createObject('Effect');
+ effect.name = 'underwater';
+ effect.loadFromFXString(g_shaders.underwatershader);
+ g_editableEffects.push(effect);
+
+ // make 2 materials, one for zOrdered, one for performance.
+ var materials = [];
+ for (var ii = 0; ii < 2; ++ii) {
+ var material = g_mainPack.createObject('Material');
+ // Copy the water params so this material gets access to the noise samplers.
+ // and the clock regardless of whether or not it uses them. That way you
+ // can access them as you edit the shader live.
+ material.copyParams(g_waterMaterial);
+ material.effect = effect;
+ effect.createUniformParameters(material);
+
+ bindParam(material, 'sunVector', g_lightDirectionParam);
+ setParam(material, 'waterColor', g_waterColor);
+ setParam(material, 'fadeFudge', -1 / 1800);
+ bindParam(material, 'clock', g_globalClockParam);
+
+ g_fadeParams[ii] = material.getParam('fadeFudge');
+ materials[ii] = material;
+ }
+
+ materials[0].drawList = g_refractionViewInfo.performanceDrawList;
+ materials[0].name = 'underwaterOpaque';
+ materials[1].drawList = g_refractionViewInfo.zOrderedDrawList;
+ materials[1].name = 'underwaterTransparent';
+
+ g_underwaterMaterials = materials;
+
+ // put a draw element on each element in the scene to draw it with the
+ // underwater shader.
+ var elements = g_scenePack.getObjectsByClassName('o3d.Element');
+ for (var ee = 0; ee < elements.length; ++ee) {
+ var element = elements[ee];
+ var originalMaterial = element.material;
+ var materialInfo = g_materialLists[originalMaterial.name];
+ if ((!materialInfo || materialInfo.refract) &&
+ element.name != 'Seafloor|Sand_Dark' &&
+ (!materialInfo || materialInfo.effect != 'diffuse_bump_2textures')) {
+ // Use the sampler from the original material.
+ var originalSamplerParam = originalMaterial.getParam('diffuseSampler');
+ if (originalSamplerParam) {
+ var drawElement = element.createDrawElement(
+ g_scenePack,
+ originalMaterial.drawList == g_mainViewInfo.performanceDrawList ?
+ materials[0] : materials[1]);
+ // create a Sampler Param on this draw element to use instead of the
+ // material's.
+ var samplerParam = drawElement.createParam('diffuseSampler',
+ 'ParamSampler');
+ samplerParam.value = originalSamplerParam.value;
+ }
+ }
+ }
+
+ // Special case the sand and coral rocks.
+ var materialNames = {
+ 'Sand_Dark': {texture: 'image3.dds'},
+ 'Folg_coralRockA_mat': {texture: 'image30.dds'},
+ 'Folg_coralRockB_mat': {texture: 'image30.dds'}};
+ for (var name in materialNames) {
+ var info = materialNames[name];
+ var material = g_scenePack.getObjects(name, 'o3d.Material')[0];
+ material.drawList = g_refractionViewInfo.performanceDrawList;
+ addTexture(material, 'diffuse2Sampler', info.texture);
+ }
+}
+
+/**
+ * Create the water effect.
+ */
+function setupWater() {
+ var waterEffects = ['watershader', 'watercolorandskyshader', 'waterstyle2'];
+ var effects = [];
+ for (var ee = 0; ee < waterEffects.length; ++ee) {
+ var name = waterEffects[ee]
+ var effect = g_mainPack.createObject('Effect');
+ effect.name = name;
+ effect.loadFromFXString(g_shaders[name]);
+ effects[ee] = effect;
+ g_editableEffects.push(effect);
+ }
+ g_waterEffect = effects[0];
+ g_waterColorAndSkyEffect = effects[1];
+ g_waterStyle2Effect = effects[2];
+
+ var effect = g_waterEffect;
+
+ var material = g_mainPack.createObject('Material');
+ g_waterMaterial = material;
+ material.name = 'water';
+ material.drawList = g_mainViewInfo.performanceDrawList;
+ material.effect = effect;
+ effect.createUniformParameters(material);
+
+ // We could reuse the one from the waterfall but let's make 2 anyway.
+ // var counter = g_mainPack.createObject('SecondCounter');
+ // For selenium testing we need a global clock.
+
+ setParam(material, 'waterColor', g_waterColor);
+ setParam(material, 'reflectionRefractionOffset', 0.1);
+ //material.getParam('clock').bind(counter.getParam('count'));
+ material.getParam('clock').bind(g_globalClockParam);
+ g_viewPositionParam = material.getParam('viewPosition');
+
+ var sampler = g_mainPack.createObject('Sampler');
+ sampler.texture = g_refractionTexture;
+ sampler.addressModeU = g_o3d.Sampler.MIRROR;
+ sampler.addressModeV = g_o3d.Sampler.MIRROR;
+ setParam(material, 'refractionSampler', sampler);
+ sampler = g_mainPack.createObject('Sampler');
+ sampler.texture = g_reflectionTexture;
+ sampler.addressModeU = g_o3d.Sampler.MIRROR;
+ sampler.addressModeV = g_o3d.Sampler.MIRROR;
+ setParam(material, 'reflectionSampler', sampler);
+
+ var shape = o3djs.primitives.createPlane(g_mainPack, material,
+ 100000, 100000, 100, 100,
+ [[1, 0, 0, 0],
+ [0, 0, 1, 0],
+ [0, -1, 0, 0],
+ [0, 0, 0, 1]]);
+
+ g_waterTransform = g_mainPack.createObject('Transform');
+ g_waterTransform.name = 'watersurface';
+ g_waterTransform.addShape(shape);
+
+ function waterAssetsLoaded() {
+ g_waterTransform.parent = g_mainRoot;
+ setupSkyDome();
+ }
+
+ // Create a loader for the water so we can know when all its assets have
+ // loaded.
+ var loader = g_loader.createLoader(waterAssetsLoaded);
+
+ g_environmentSampler = loadTexture(loader, g_mainPack, material,
+ 'environmentSampler',
+ 'sky-cubemap.dds');
+
+ // Create some textures.
+ var textureInfo = [
+ {width: 128, height: 128, type: 0, name: 'noiseSampler'},
+ {width: 64, height: 64, type: 0, name: 'noiseSampler2'},
+ {width: 32, height: 32, type: 0, name: 'noiseSampler3'},
+ {width: 32, height: 1, type: 1, name: 'fresnelSampler'}
+ ];
+
+ for (var tt = 0; tt < textureInfo.length; ++tt) {
+ var info = textureInfo[tt];
+ var pixels = [];
+
+ switch (info.type) {
+ case 0:
+ // Create a noise texture.
+ for (var yy = 0; yy < info.height; ++yy) {
+ for (var xx = 0; xx < info.width; ++xx) {
+ for (var cc = 0; cc < 3; ++cc) {
+ pixels.push(g_math.pseudoRandom());
+ }
+ }
+ }
+ break;
+ case 1:
+ // Create a ramp texture. (this needs to be a fresnel ramp?)
+ for (var yy = 0; yy < info.height; ++yy) {
+ for (var xx = 0; xx < info.width; ++xx) {
+ // TODO: figure this out.
+ var color = Math.pow(1 - xx / info.width, 10);
+ for (var cc = 0; cc < 3; ++cc) {
+ pixels.push(color);
+ }
+ }
+ }
+ break;
+ }
+ var texture = g_mainPack.createTexture2D(
+ info.width, info.height, g_o3d.Texture.XRGB8, 1, false);
+ texture.set(0, pixels);
+ var sampler = g_mainPack.createObject('Sampler');
+ sampler.texture = texture;
+ setParam(material, info.name, sampler);
+ }
+
+ loader.finish();
+}
+
+/**
+ * Create particles.
+ */
+function setupParticles() {
+ setupTorches();
+ setupMist();
+}
+
+/**
+ * Create the torches.
+ */
+function setupTorches() {
+ g_torchEmitter = g_particleSystem.createParticleEmitter(g_torchTexture);
+ g_torchEmitter.setState(o3djs.particles.ParticleStateIds.ADD);
+ g_torchEmitter.setColorRamp(
+ [1, 1, 0, 1,
+ 1, 0, 0, 1,
+ 0, 0, 0, 1,
+ 0, 0, 0, 0.5,
+ 0, 0, 0, 0]);
+ g_torchEmitter.setParameters({
+ numParticles: 40,
+ lifeTime: 2,
+ timeRange: 2,
+ startSize: 50,
+ endSize: 90,
+ positionRange: [10, 10, 10],
+ velocity: [0, 0, 60], velocityRange: [15, 15, 15],
+ acceleration: [0, 0, -20],
+ spinSpeedRange: 4}
+ );
+
+ g_torchMaterial = g_torchEmitter.material;
+
+ // Add one to each torch.
+ var shape = g_torchEmitter.shape;
+ g_scenePack.getObjects('particle_torch01',
+ 'o3d.Transform')[0].addShape(shape);
+ g_scenePack.getObjects('particle_torch02',
+ 'o3d.Transform')[0].addShape(shape);
+ g_scenePack.getObjects('particle_torch03',
+ 'o3d.Transform')[0].addShape(shape);
+ g_scenePack.getObjects('particle_torch04',
+ 'o3d.Transform')[0].addShape(shape);
+}
+
+/**
+ * Create the mist.
+ */
+function setupMist() {
+ g_topMistEmitter = g_particleSystem.createParticleEmitter(g_mistTexture);
+ g_topMistEmitter.setState(o3djs.particles.ParticleStateIds.ADD);
+ g_topMistEmitter.setColorRamp(
+ [1, 1, 1, 2,
+ 1, 1, 1, 0]);
+ g_topMistEmitter.setParameters({
+ numParticles: 20,
+ timeRange: 3,
+ lifeTime: 3, lifeTimeRange: 1,
+ startSize: 400,
+ endSize: 600,
+ position: [-100, -100, 0], positionRange: [25, 25, 0],
+ velocity: [0, 0, 150], velocityRange: [15, 15, 15],
+ worldAcceleration: [0, 0, -500],
+ spinSpeedRange: 8}
+ );
+
+ // Add one to each top.
+ var shape = g_topMistEmitter.shape;
+ g_scenePack.getObjects('particle_falltop01',
+ 'o3d.Transform')[0].addShape(shape);
+ g_scenePack.getObjects('particle_falltop02',
+ 'o3d.Transform')[0].addShape(shape);
+ g_scenePack.getObjects('particle_falltop03',
+ 'o3d.Transform')[0].addShape(shape);
+
+ g_bottomMistEmitter = g_particleSystem.createParticleEmitter(g_mistTexture);
+ g_bottomMistEmitter.setState(o3djs.particles.ParticleStateIds.ADD);
+ g_bottomMistEmitter.setColorRamp(
+ [1, 1, 1, 1,
+ 1, 1, 1, 0]);
+ g_bottomMistEmitter.setParameters({
+ numParticles: 40,
+ lifeTime: 2,
+ timeRange: 2,
+ startSize: 800,
+ endSize: 1500,
+ position: [0, 0, 100], positionRange: [200, 200, 10],
+ velocityRange: [200, 200, 0],
+ acceleration: [0, 0, -20],
+ spinSpeedRange: 4}
+ );
+
+ // Add one to each bottom.
+ shape = g_bottomMistEmitter.shape;
+
+ g_scenePack.getObjects('particle_fallbottom01',
+ 'o3d.Transform')[0].addShape(shape);
+ g_scenePack.getObjects('particle_fallbottom02',
+ 'o3d.Transform')[0].addShape(shape);
+ g_scenePack.getObjects('particle_fallbottom03',
+ 'o3d.Transform')[0].addShape(shape);
+
+ g_rippleEmitter = g_particleSystem.createParticleEmitter(g_mistTexture);
+ g_rippleEmitter.setState(o3djs.particles.ParticleStateIds.ADD);
+ g_rippleEmitter.setColorRamp(
+ [0.7, 0.8, 1, 0.5,
+ 1, 1, 1, 0]);
+ g_rippleEmitter.setParameters({
+ numParticles: 20,
+ lifeTime: 2,
+ timeRange: 2,
+ startSize: 50,
+ endSize: 10000,
+ position: [0, 0, 10], positionRange: [250, 250, 0],
+ orientation: o3djs.quaternions.rotationX(Math.PI / 2),
+ billboard: false});
+
+ // Add one to each bottom.
+ shape = g_rippleEmitter.shape;
+
+ g_scenePack.getObjects('particle_fallbottom01',
+ 'o3d.Transform')[0].addShape(shape);
+ g_scenePack.getObjects('particle_fallbottom02',
+ 'o3d.Transform')[0].addShape(shape);
+ g_scenePack.getObjects('particle_fallbottom03',
+ 'o3d.Transform')[0].addShape(shape);
+}
+
+function setupSkyDome() {
+ // Create the skydome effect.
+ var effect = g_mainPack.createObject('Effect');
+ effect.name = 'skydome';
+ effect.loadFromFXString(g_shaders.skydomeshader);
+ g_editableEffects.push(effect);
+
+ var material = g_mainPack.createObject('Material');
+ g_skyDomeMaterial = material;
+ material.name = 'skydome';
+ material.drawList = g_mainViewInfo.performanceDrawList;
+ material.effect = effect;
+ effect.createUniformParameters(material);
+
+ material.getParam('environmentSampler').value = g_environmentSampler;
+
+ // Create a special quad to draw the sky. We won't transform this quad
+ // at all. It's already in clip-space.
+ var shape = o3djs.primitives.createPlane(g_mainPack, material,
+ 2, 2, 1, 1,
+ [[1, 0, 0, 0],
+ [0, 0, 1, 0],
+ [0, -1, 0, 0],
+ [0, 0, 0.99999, 1]]);
+
+ g_skyDomeTransform = g_mainPack.createObject('Transform');
+ g_skyDomeTransform.parent = g_mainRoot;
+ g_skyDomeTransform.addShape(shape);
+}
+
+/**
+ * Creates an Image object which is a transform and a child scaleTransform
+ * scaled to match the texture
+ *
+ * @constructor
+ * @param {!o3d.Transform} parent Transform to parent image too.
+ * @param {!o3d.Texture} texture The texture.
+ * @param {boolean} opt_topLeft If true the origin of the image will be it's
+ * topleft corner, the default is the center of the image.
+ */
+function Image(parent, texture, opt_topLeft) {
+ // create a transform for positioning
+ this.transform = g_mainPack.createObject('Transform');
+ this.transform.parent = parent;
+
+ // create a transform for scaling to the size of the image just so
+ // we don't have to manage that manually in the transform above.
+ this.scaleTransform = g_mainPack.createObject('Transform');
+ this.scaleTransform.parent = this.transform;
+
+ // setup the sampler for the texture
+ this.sampler = g_mainPack.createObject('Sampler');
+ this.sampler.addressModeU = g_o3d.Sampler.CLAMP;
+ this.sampler.addressModeV = g_o3d.Sampler.CLAMP;
+ this.paramSampler = this.scaleTransform.createParam('diffuseSampler',
+ 'ParamSampler');
+ this.paramSampler.value = this.sampler;
+
+ this.sampler.texture = texture;
+ this.scaleTransform.addShape(g_imageShape);
+ if (opt_topLeft) {
+ this.scaleTransform.translate(texture.width / 2, texture.height / 2, 0);
+ }
+ this.scaleTransform.scale(texture.width, -texture.height, 1);
+ this.colorParam = this.scaleTransform.createParam('colorMult', 'ParamFloat4');
+ this.colorParam.value = [1, 1, 1, 1];
+}
+
+/**
+ * Sets up the hud.
+ */
+function setupHud() {
+ var effect = g_mainPack.createObject('Effect');
+ effect.name = 'hud';
+ effect.loadFromFXString(g_shaders.imageshader);
+ g_editableEffects.push(effect);
+ // Make the default colorMult 1, 1, 1, 1 uncase it is not supplied by the
+ // material.
+ effect.createParam('colorMult', 'ParamFloat4').value = [1, 1, 1, 1];
+
+ g_imageEffect = effect;
+
+ var g_imageMaterial = g_mainPack.createObject('Material');
+ g_imageMaterial.drawList = g_hudViewInfo.zOrderedDrawList;
+ g_imageMaterial.effect = effect;
+ effect.createUniformParameters(g_imageMaterial);
+ g_imageMaterial.getParam('colorMult').value = [1, 1, 1, 1];
+
+ g_renderTargetDisplayRoot = g_mainPack.createObject('Transform');
+ g_renderTargetDisplayRoot.parent = g_hudRoot;
+ g_renderTargetDisplayRoot.visible = false;
+
+ g_imageShape = o3djs.primitives.createPlane(g_mainPack, g_imageMaterial,
+ 1, 1, 1, 1,
+ [[1, 0, 0, 0],
+ [0, 0, 1, 0],
+ [0, 1, 0, 0],
+ [0, 0, 0, 1]]);
+
+ // Because it's easier to make a texture here than manage another effect
+ var backTexture = g_mainPack.createTexture2D(
+ 1, 1, g_o3d.Texture.XRGB8, 1, false);
+ backTexture.set(0, [1, 1, 1]);
+
+ g_whiteTexture = backTexture;
+ g_whiteSampler = g_mainPack.createObject('Sampler');
+ g_whiteSampler.texture = g_whiteTexture;
+
+ // Make images to show the render targets.
+ for (var ii = 0; ii < 2; ++ii) {
+ var textureDisplaySquareSize = 256;
+ var renderTargetTexture = (ii == 0) ? g_reflectionTexture :
+ g_refractionTexture;
+ var x = 10;
+ var y = 10 + ii * (textureDisplaySquareSize + 10);
+ var borderSize = 2;
+ var image;
+ // make a back image to create a border around render target.
+ image = new Image(g_renderTargetDisplayRoot, backTexture, true);
+ image.transform.translate(x - borderSize, y - borderSize, -3);
+ image.transform.scale(textureDisplaySquareSize + borderSize * 2,
+ textureDisplaySquareSize + borderSize * 2,
+ 1);
+ image = new Image(g_renderTargetDisplayRoot, renderTargetTexture, true);
+ image.transform.translate(x, y, -2);
+ image.transform.scale(textureDisplaySquareSize / g_renderTargetWidth,
+ textureDisplaySquareSize / g_renderTargetHeight, 1);
+ if (ii == 0) {
+ g_reflectionImage = image;
+ } else {
+ g_refractionImage = image;
+ }
+ }
+
+ // Make a fader plane.
+ {
+ var image = new Image(g_hudRoot, backTexture, true);
+ g_faderTransform = image.transform;
+ g_faderTransform.visible = false;
+ g_faderColorParam = image.colorParam;
+ updateFaderPlane();
+ }
+
+ // Make a canvas for text.
+ var canvasLib = o3djs.canvas.create(g_mainPack,
+ g_hudRoot,
+ g_hudViewInfo);
+
+ g_hudQuad = canvasLib.createXYQuad(20, 20, -1, 512, 512, true);
+ g_paint = g_mainPack.createObject('CanvasPaint');
+
+ g_paint.setOutline(3, [1, 1, 1, 1]);
+ g_paint.textAlign = g_o3d.CanvasPaint.LEFT;
+ g_paint.textSize = 16;
+ g_paint.textTypeface = 'Arial';
+ g_paint.color = [0, 0, 0, 1];
+
+ setHudText('Loading...');
+}
+
+/**
+ * Sets the text on the hud.
+ * @param {string} text The text to display.
+ */
+function setHudText(text) {
+ if (g_showError) {
+ return;
+ }
+ var canvas = g_hudQuad.canvas;
+ canvas.clear([0, 0, 0, 0]);
+ canvas.saveMatrix();
+ var lines = text.split('\n');
+ for (var ll = 0; ll < lines.length; ++ll) {
+ var tabs = lines[ll].split('\t');
+ for (var tt = 0; tt < tabs.length; ++tt) {
+ canvas.drawText(tabs[tt], 10 + tt * 120, 30 + 20 * ll, g_paint);
+ }
+ }
+ canvas.restoreMatrix();
+
+ g_hudQuad.updateTexture();
+}
+
+/**
+ * Show a hint message.
+ */
+function showHint() {
+ g_hudQuad.transform.visible = true;
+ g_hudFadeTime = 0.0;
+ setHudText('press H for help.');
+}
+
+/**
+ * Show a help message.
+ */
+function toggleHelp() {
+ g_hudFadeTime = 0.0;
+ g_helpVisible = !g_helpVisible;
+ g_hudQuad.transform.visible = true;
+ if (g_helpVisible) {
+ setHudText('1 - 4\t: Camera Preset\n' +
+ 'Mouse\t: Look Around\n' +
+ 'Wheel\t: Field of View\n' +
+ 'Arrows\t: Move Camera\n' +
+ 'p\t: Toggle Props\n' +
+ 'm\t: Edit Materials\n' +
+ 'e\t: Edit Effects\n' +
+ 'r\t: Show Render Targets\n' +
+ 'c\t: Use Simple Shaders\n' +
+ 'f\t: Show FPS\n' +
+ 'h\t: Show Help\n' +
+ 'o\t: Change Water Effect\n' +
+ 'q\t: Toggle demo camera\n');
+ } else {
+ showHint();
+ }
+}
+
+/**
+ * Show error.
+ * @param {string} msg Msg to display.
+ */
+function showError(msg) {
+ g_hudQuad.transform.visible = true;
+ setHudText('Error: Could not load scene.\n' + msg);
+ g_showError = true;
+}
+
+/**
+ * Removes any callbacks so they don't get called after the page has unloaded.
+ */
+function uninit() {
+ if (g_client) {
+ g_client.cleanup();
+ }
+}
+