/*
* 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');
var RENDER_TARGET_WIDTH = 256;
var RENDER_TARGET_HEIGHT = 256;
// client.root
// |
// g_mainRoot
// |
// +-----+--------+----------------+
// | | |
// g_baseRoot g_waterTransform g_skyDomeTransform
// |
// g_sceneRoot
var g_re;
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_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_renderTargetDisplayRoot;
var g_sceneElement;
var g_client;
var g_scenePack;
var g_mainPack;
var g_fadeParams = [];
var g_mainViewInfo; // main view
var g_hudRoot; // root transform for hud.
var g_mainRoot;
var g_waterLevel = 500;
var g_reflectionViewInfo;
var g_refractionViewInfo;
var g_hudViewInfo;
var g_loader;
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_globalParams;
var g_globalClockParam;
var g_clipHeightParam;
var g_lightPositionParam;
var g_lightDirectionParam;
var g_lightColorParam;
var g_viewPositionParam;
var g_underwaterMaterials;
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_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_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_camera = {
farPlane: 80000,
nearPlane: 10,
up: [0, 0, 1],
targetVector: [0.5522, 0.8336, -0.0071],
fieldOfView: Math.PI / 4, // 45 degrees
eye: [-4809, -4048, 2322 - g_waterLevel],
xAxis: [0.8335, -0.5522, -0.0157],
minFieldOfView: 5 * Math.PI / 180,
maxFieldOfView: 70 * Math.PI / 180
};
// 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 show 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) {
return {
list: list,
reflect: reflect,
refract: refract,
main: main,
type: type};
}
var g_materialLists = {
// ---------------------------- list reflect refract main adj
'default': makeInfo(1, false, false, true, 1), // palmTreeB
'Folg_BushA_mat': makeInfo(1, true, false, true, 1),
'Folg_BushB_mat': makeInfo(1, true, false, true, 1),
'Folg_BushC_mat': makeInfo(1, true, false, true, 1),
'Folg_coralD_mat': makeInfo(1, false, true, false, 1),
'Folg_coralG_mat': makeInfo(1, false, true, false, 1),
'Folg_coralRockA_mat': makeInfo(0, false, true, false, 1),
'Folg_coralRockB_mat': makeInfo(0, false, true, false, 1),
'Folg_FernA_mat': makeInfo(1, true, false, true, 1),
'Folg_hangingFerns_mat': makeInfo(1, true, false, true, 1),
'Folg_largeFernA_mat': makeInfo(1, true, false, true, 1),
'Folg_LeafyPlantA_mat': makeInfo(1, true, false, true, 1),
'Folg_palmTreeA': makeInfo(1, false, false, true, 1),
'Prop_brokenShip_mat': makeInfo(0, true, true, true, 0),
'Prop_pillarA_mat': makeInfo(0, false, false, true, 0),
'prop_tikiMaskA': makeInfo(0, false, false, true, 0),
'Prop_TorchA_mat': makeInfo(0, false, false, true, 0),
'Prop_wallA_mat': makeInfo(0, false, false, true, 0),
'Props_Bridge_mat': makeInfo(0, true, false, true, 0),
'Rock_Dark': makeInfo(0, true, true, true, 2),
'Sand_Dark': makeInfo(0, false, true, false, 0),
'Standard_2': makeInfo(0, true, true, false, 0), // ??
'Standard_3': makeInfo(1, false, true, true, 0)}; // waterfall
var g_randSeed = 0;
/**
* Returns a deterministic pseudorandom number bewteen 0 and 1
* @return {number} a random number between 0 and 1
*/
function pseudoRandom() {
var range = Math.pow(2, 32);
return (g_randSeed = (134775813 * g_randSeed + 1) % range) / range;
}
// ***************************** Mouse functions *******************************
/**
* Handler for onmousedown.
* @param {event} e A mouse event.
*/
function onMouseDown(e) {
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
};
}
/**
* 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.sub(worldRay.near,
g_camera.eye));
updateCamera();
}
}
/**
* 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) {
var target = g_camera.minFieldOfView;
if (e.deltaY < 0) {
target = g_camera.maxFieldOfView;
}
g_camera.fieldOfView = g_math.lerp(target, g_camera.fieldOfView, 0.9);
updateProjection();
}
// *************************** Keyboard functions ******************************
/**
* Tracks key down events.
* @param {Event} e keyboard event.
*/
function onKeyDown(e) {
++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) {
--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);
}
// **************************** Camera Functions *******************************
/**
* Updates the camera (the view matrix of the drawContext) with the current
* camera settings.
*/
function updateCamera() {
var target = g_math.add(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;
}
/**
* 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.negative(cameraMatrix[2].splice(0, 3));
//g_camera.fieldOfView = cameraInfo.fieldOfViewRadians;
g_camera.fieldOfView = o3djs.math.degToRad(45);
updateCamera();
updateProjection();
}
/**
* 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.add(g_camera.eye,
g_math.mul(g_camera.xAxis, direction));
updateCamera();
}
/**
* 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.add(g_camera.eye,
g_math.mul(g_camera.targetVector, direction));
updateCamera();
}
// ************************ Effect Editor Functions ****************************
/**
* Starts editing an effect.
* @param {number} effectId The clientId of the effect.
*/
function editEffect(effectId) {
if (g_currentEditEffect) {
// Remove the selected color.
var tabElement = o3djs.util.getElementById(
'effecttab_' + g_currentEditEffect.clientId);
tabElement.className = 'tab';
// 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);
var tabElement = o3djs.util.getElementById('effecttab_' + effectId);
g_effectTextAreaElement.value = g_editableEffectsSource[effectId];
tabElement.className = 'selected';
g_currentEditEffect = effect;
}
/**
* 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.
}
}
}
/**
* 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 = '';
for (var ii = 0; ii < g_editableEffects.length; ++ii) {
var effect = g_editableEffects[ii];
g_editableEffectsSource[effect.clientId] = effect.source;
html += '' +
'' +
'[ ' + effect.name + ' ] ' +
'';
}
g_effectTabsElement.innerHTML = html;
for (var ii = 0; ii < g_editableEffects.length; ++ii) {
var effect = g_editableEffects[ii];
var span = o3djs.util.getElementById('effecttab_' + effect.clientId);
span.onclick = o3djs.util.curry(editEffect, effect.clientId);
}
// 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;
}
}
}
// *********************** 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 materials to simple effects.
* @param {Event} e Event for key that was pressed.
*/
function toggleSimpleMaterials(e) {
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 % 3;
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: {
var effect = material.effect;
g_materialSwapTable[material.clientId] = effect;
if (!g_simpleEffects[effect.clientId]) {
pseudoRandom(); // eat a random number.
var newEffect = g_mainPack.createObject('Effect');
newEffect.loadFromFXString(
o3djs.util.getElementContentById('simpleshader'));
newEffect.createUniformParameters(newEffect);
newEffect.getParam('simpleColor').value = [
pseudoRandom(),
pseudoRandom(),
pseudoRandom(),
1];
g_simpleEffects[effect.clientId] = newEffect;
}
material.effect = g_simpleEffects[effect.clientId];
break;
}
case 2: {
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.negative(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 + ')';
}
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]);
}
}
}
/**
* Loads a scene.
* @param {string} path URL of scene to load.
* @param {!o3d.Transform} root Transform that scene will be loaded in.
*/
function loadFile(path, root) {
function callback(pack, parent, exception) {
if (exception) {
showError(exception);
} else {
setupWaterfall();
// Generate draw elements and setup material draw lists.
o3djs.pack.preparePack(g_scenePack, g_mainViewInfo);
// 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;
}
g_cameraInfos = o3djs.camera.getCameraInfos(parent,
g_o3dWidth,
g_o3dHeight);
setCamera(1);
setupUnderwater();
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]}];
// Manually connect all the materials' lightWorldPos params or a global
// light param.
var materials = g_scenePack.getObjectsByClassName('o3d.Material');
for (var m = 0; m < materials.length; ++m) {
var material = materials[m];
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 materialInfo = g_materialLists[material.name];
var type = materialInfo ? materialInfo.type : 0;
setParam(material, 'shininess', adjust[type].shininess);
setParam(material, 'specular', adjust[type].specular);
}
{
var drawLists = [g_mainViewInfo.performanceDrawList,
g_mainViewInfo.zOrderedDrawList];
for (var name in g_materialLists) {
var materialInfo = g_materialLists[name];
var material = g_scenePack.getObjects(name, 'o3d.Material')[0];
material.drawList = drawLists[materialInfo.list];
}
}
getSpeedTransforms(g_sceneRoot);
//dump("--------\n");
//dumpTransforms(g_sceneRoot, '');
// turn off the water and skydome
// (these should have been removed from the scene by the artists)
// var transforms = g_scenePack.getObjectsByClassName('o3d.Transform');
// for (var t = 0; t < transforms.length; ++t) {
// var transform = transforms[t];
// if (transform.name == 'Water' ||
// transform.name == 'Sphere01') {
// transform.visible = false;
// }
// }
setupMaterialEditor();
setupEffectEditor();
setupPropEditor();
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];
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') {
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++) {
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], ' ');
}
}
}
}
// Create a new transform for the loaded file
try {
g_particleLoader.loadScene(g_client, g_scenePack, root, path, callback);
} 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);
}
}
/**
* 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);
}
if (moveY) {
moveCameraForwardBack(moveY);
}
}
/**
* 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
*/
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;
}
}
}
/**
* 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 && 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_loader) {
var progressInfo = g_loader.loadInfo.getKnownProgressInfoSoFar();
if (progressInfo.percent != g_downloadPercent) {
g_downloadPercent = progressInfo.percent;
setHudText('Loading... ' + progressInfo.percent + '%' +
' (' + progressInfo.downloaded +
' of ' + progressInfo.totalBytes + progressInfo.suffix + ')');
}
}
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);
}
function onAllLoadingFinished() {
g_loader = null;
showHint();
window.g_finished = true; // for selenium testing.
}
/**
* 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);
}
/**
* Initializes O3D and loads the scene into the transform graph.
* @param {Array} clientElements Array of o3d object elements.
*/
function initStep2(clientElements) {
var url = o3djs.util.getAbsoluteURI('graphic-o3d-samples-beachdemo-assets-beachdemo.tgz');
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();
// Replace o3djs.effect.buildStandardShaderString with our own.
o3djs.effect.buildStandardShaderString =
beachDemoBuildStandardShaderString;
// Create Render Targets for the reflection and refraction.
g_reflectionTexture = g_mainPack.createTexture2D(RENDER_TARGET_WIDTH,
RENDER_TARGET_HEIGHT,
g_o3d.Texture.ARGB8, 1,
true);
var reflectionSurface = g_reflectionTexture.getRenderSurface(0, g_mainPack);
g_refractionTexture = g_mainPack.createTexture2D(RENDER_TARGET_WIDTH,
RENDER_TARGET_HEIGHT,
g_o3d.Texture.XRGB8, 1,
true);
var refractionSurface = g_refractionTexture.getRenderSurface(0, g_mainPack);
var depthSurface = g_mainPack.createDepthStencilSurface(RENDER_TARGET_WIDTH,
RENDER_TARGET_HEIGHT);
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);
// Setup the render graph to generate them.
g_reflectionSurfaceSet = g_mainPack.createObject('RenderSurfaceSet');
g_reflectionSurfaceSet.renderSurface = reflectionSurface;
g_reflectionSurfaceSet.renderDepthStencilSurface = depthSurface;
g_refractionSurfaceSet = g_mainPack.createObject('RenderSurfaceSet');
g_refractionSurfaceSet.renderSurface = refractionSurface;
g_refractionSurfaceSet.renderDepthStencilSurface = depthSurface;
// 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);
setClientSize();
// 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_particleSystem = o3djs.particles.createParticleSystem(g_mainPack,
g_mainViewInfo,
g_globalClockParam);
// 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.effect);
g_loader = o3djs.loader.createLoader(onAllLoadingFinished);
// We need to make a subloader because we can't make the particles
// until both the scene and the particle textures are loaded.
g_particleLoader = g_loader.createLoader(setupParticles);
g_particleLoader.loadTexture(
g_mainPack,
o3djs.util.getAbsoluteURI('graphic-o3d-samples-beachdemo-assets-pe-fire.jpg'),
function(texture, exception) {
if (exception) {
alert(exception);
} else {
g_torchTexture = texture;
}
});
g_particleLoader.loadTexture(
g_mainPack,
o3djs.util.getAbsoluteURI('graphic-o3d-samples-beachdemo-assets-pe-mist.jpg'),
function(texture, exception) {
if (exception) {
alert(exception);
} else {
g_mistTexture = texture;
}
});
setupWater();
setupHud();
loadFile(url, g_sceneRoot);
// This must happen after loadFile because loadFile loads using
// g_particleLoader so that the particles don't get created
// until both the scene and the particle textures are loaded.
g_particleLoader.finish()
// It's important to create stuff in g_mainPack and not g_scenePack because
// g_scenePack will be scanned and modified after loading.
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);
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);
// 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;
});
g_loader.finish();
}
/**
* 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 samplerParam = material.getParam(samplerName);
var sampler = pack.createObject('Sampler');
samplerParam.value = sampler;
var url = o3djs.util.getAbsoluteURI('graphic-o3d-samples-beachdemo-assets-' + textureName);
loader.loadTexture(pack, url, function(texture, exception) {
if (exception) {
alert(exception);
} else {
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(o3djs.util.getElementContentById('waterfallshader'));
effect.createUniformParameters(material);
g_editableEffects.push(effect);
// Set the waterfall to use additive blending.
var state = g_mainPack.createObject('State');
state.getStateParam('DestinationBlendFunction').value =
g_o3d.State.BLENDFUNC_ONE;
state.getStateParam('SourceBlendFunction').value =
g_o3d.State.BLENDFUNC_SOURCE_ALPHA;
//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(o3djs.util.getElementContentById('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');
material.effect = effect;
effect.createUniformParameters(material);
material.getParam('sunVector').bind(g_lightDirectionParam);
material.getParam('waterColor').value = g_waterColor;
material.getParam('fadeFudge').value = -1 / 1800;
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) {
// 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;
}
}
}
}
/**
* 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(o3djs.util.getElementContentById(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.
material.getParam('waterColor').value = g_waterColor;
material.getParam('reflectionRefractionOffset').value = 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;
material.getParam('refractionSampler').value = sampler;
sampler = g_mainPack.createObject('Sampler');
sampler.texture = g_reflectionTexture;
sampler.addressModeU = g_o3d.Sampler.MIRROR;
sampler.addressModeV = g_o3d.Sampler.MIRROR;
material.getParam('reflectionSampler').value = sampler;
var shape = o3djs.primitives.createPlane(g_mainPack, material,
100000, 100000, 256, 256,
[[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(Math.random());
}
}
}
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;
material.getParam(info.name).value = 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, 2,
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,
colorMult: [0.5, 0.6, 0.6, 0.2]}
);
// 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);
}
function setupSkyDome() {
// Create the skydome effect.
var effect = g_mainPack.createObject('Effect');
effect.name = 'skydome';
effect.loadFromFXString(o3djs.util.getElementContentById('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);
}
/**
* Sets up the hud.
*/
function setupHud() {
var effect = g_mainPack.createObject('Effect');
effect.name = 'hud';
effect.loadFromFXString(o3djs.util.getElementContentById('imageshader'));
g_editableEffects.push(effect);
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_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]);
// Make images to show the render targets.
for (var ii = 0; ii < 2; ++ii) {
var renderTargetTexture = (ii == 0) ? g_reflectionTexture :
g_refractionTexture;
var x = 10;
var y = 10 + ii * (g_reflectionTexture.height * 0.5 + 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(renderTargetTexture.width * 0.5 + borderSize * 2,
renderTargetTexture.height * 0.5 + borderSize * 2,
1);
image = new Image(g_renderTargetDisplayRoot, renderTargetTexture, true);
image.transform.translate(x, y, -2);
image.transform.scale(0.5, 0.5, 1);
}
var canvasLib = o3djs.canvas.create(g_mainPack,
g_hudRoot,
g_hudViewInfo);
g_hudQuad = canvasLib.createXYQuad(20, 20, -1, 512, 256, true);
g_paint = g_mainPack.createObject('CanvasPaint');
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) {
g_paint.setOutline(3, [0, 0, 0, 1]);
g_paint.textAlign = g_o3d.CanvasPaint.LEFT;
g_paint.textSize = 12;
g_paint.textTypeface = 'Arial';
g_paint.color = [1, 1, 0, 1];
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 = 6.0;
setHudText('press H for help.');
}
/**
* Show a help message.
*/
function toggleHelp() {
g_hudFadeTime = 0.0;
g_helpVisible = !g_helpVisible;
g_hudQuad.transform.visible = 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');
}
/**
* 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();
}
}