/*
* 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 = '';
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 = '
';
var count = 0;
for (var prefix in propPrefixes) {
html += '' +
'
' +
'' +
prefix +
'
';
++count;
}
g_propPanelElement.innerHTML = html + '
';
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 <
* @param {string} str to escape.
* @return {string} escaped string.
*/
function escapeHTML(str) {
return str.replace(/' +
'
' + escapeHTML(paramObject.name) + '
' +
'';
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 += '' +
'
' +
'
' +
'' +
'
' +
'
' +
'' +
'
' +
'
';
}
}
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 = '
';
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 + '
';
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();
}
}