/* * 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 += '' + ''; ++count; } g_propPanelElement.innerHTML = html + '
' + '' + prefix + '
'; 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(); } }