/*
* 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 This file creates all of the global o3d stuff that
* establishes the plugin window, sets its size, creates necessary light
* and camera parameters, and stuff like that.
*/
o3djs.require('o3djs.util');
o3djs.require('O3D.math');
o3djs.require('o3djs.rendergraph');
o3djs.require('o3djs.pack');
o3djs.require('o3djs.scene');
// Create some globals that store our main pointers to o3d objects.
var o3d;
var math;
var math;
var client;
var pack;
var globalParams;
var g_viewInfo;
var drawContext;
var blackSampler;
var g_loadInfo;
// This variable keeps track of whether a collada file is correctly loaded.
var isLoaded = false;
// This variable, surprisingly, contains the time since the last frame
// (in seconds)
var timeSinceLastFrame = 0;
var frameCount = 0;
// This is only a handy data structure for storing camera information. It's not
// actually a core O3D object.
var camera = {
eye: [0, 0, 500],
target: [0, 0, 0]
};
/**
* This is a top-level switch that tells us whether we're running inside a
* SketchUp web dialog. (If not, then we're in O3D.)
* TODO : Re-enable the SketchUp web-dialog functionality, but through
* a check different than simply testing if we are running in IE.
*/
//var isSU = (navigator.appVersion.indexOf('MSIE') != -1) ? true : false;
var isSU = false;
// Some parameters to pass to the shaders as the camera moves around.
var camera_eye_param;
var camera_target_param;
// Keep track of the lights in the level.
var light_array;
var max_number_of_lights = 4; // Determined from the GPU effect.
/**
* Creates the client area.
*/
function init() {
o3djs.util.makeClients(initStep2);
}
/**
* Gets called at the page's onLoad event. This function sets up our plugin and
* level selection UI.
* @param {Array} clientElements Array of o3d object elements.
*/
function initStep2(clientElements) {
// Walk across all levels loaded by includes.js and show a list for the user
// to choose from.
$('content').innerHTML = 'Choose a level...
';
for (var i = 0; i < levels.length; i++) {
var level = levels[i];
var str = '' +
level.name + '
'
$('content').innerHTML += str;
}
soundPlayer.play('sound/music.mp3', 100, 999);
// If we're NOT running inside SketchUp, then we need to set up our o3d
// window.
if (!isSU) {
var o3dElement = clientElements[0];
o3dElement.id = 'o3dObj';
o3d = o3dElement.o3d;
math = o3djs.math;
client = o3dElement.client;
pack = client.createPack();
var blackTexture = pack.createTexture2D(1, 1, o3d.Texture.XRGB8, 1, false);
blackTexture.set(0, [0, 0, 0]);
blackSampler = pack.createObject('Sampler');
blackSampler.texture = blackTexture;
// Create the render graph for a view.
g_viewInfo = o3djs.rendergraph.createBasicView(
pack,
client.root,
client.renderGraphRoot);
drawContext = g_viewInfo.drawContext;
var target = [0, 0, 0];
var eye = [0, 0, 5];
var up = [0, 1, 0];
var view_matrix = math.matrix4.lookAt(eye, target, up);
drawContext.view = view_matrix;
globalParams = pack.createObject('ParamObject');
// Initialize all the effect's required parameters.
var ambient_light_color_param = globalParams.createParam(
'ambientLightColor',
'ParamFloat4');
ambient_light_color_param.value = [0.27, 0.2, 0.2, 1];
var sunlight_direction_param = globalParams.createParam('sunlightDirection',
'ParamFloat3');
// 20 degrees off.
sunlight_direction_param.value = [-0.34202, 0.93969262, 0.0];
var sunlight_color_param = globalParams.createParam('sunlightColor',
'ParamFloat4');
sunlight_color_param.value = [0.55, 0.6, 0.7, 1.0];
// global parameter, this will change as the character moves.
camera_eye_param = globalParams.createParam('cameraEye', 'ParamFloat3');
camera_eye_param.value = eye;
camera_target_param = globalParams.createParam('cameraTarget',
'ParamFloat3');
camera_target_param.value = target;
InitializeLightParameters(globalParams, max_number_of_lights);
var fog_color_param = globalParams.createParam('fog_color', 'ParamFloat4');
fog_color_param.value = [0.5, 0.5, 0.5, 1.0];
// Create a perspective projection matrix.
var o3d_width = 845;
var o3d_height = 549;
var proj_matrix = math.matrix4.perspective(
45 * Math.PI / 180, o3d_width / o3d_height, 0.1, 5000);
drawContext.projection = proj_matrix;
o3dElement.onkeydown = document.onkeydown;
o3dElement.onkeyup = document.onkeyup;
o3djs.event.startKeyboardEventSynthesis(o3dElement);
}
}
/**
* Remove callbacks on the o3d client.
*/
function uninit() {
if (client) {
client.cleanup();
}
}
/**
* Helper function to shorten document.getElementById()
* @param {string} value The elementID to find.
* @return {string} The document element with our ID.
*/
function $(id) {
return document.getElementById(id);
}
/**
* Helper function to tell us if a value is set to something useful.
* @param {Object} value The thing to check out.
* @return {boolean} Whether the thing is empty.
* TODO: 'Tis silly to have both an isEmpty and exists function
* when they're probably doing the same thing.
*/
function isEmpty(value) {
return (value == undefined) || (value == '');
}
/**
* Returns the 2nd value if the first is rmpty.
* @param {Object} val1 The thing to check for emptyness.
* @param {Object} val2 The thing to maybe pass back.
* @return {Object} One of the above.
*/
function ifEmpty(val1, val2) {
if (val1 == undefined || val1 == '') {
return val2;
}
return val1;
}
/**
* Helper function to tell us if a value exists.
* @param {Object} item The thing to check out.
* @return {boolean} Whether the thing exists.
*/
function exists(item) {
if (item == undefined || item == null) {
return false;
} else {
return true;
}
}
/**
* Takes an index number into our global levels[] array, and it loads up that
* level.
* @param {number} levelID The number of the level to load in.
*/
function loadLevel(levelID) {
if (isLoaded) {
return;
}
world = levels[levelID];
// If we're running in 'SketchUp mode' inside the level editor, tell SU to
// do the loading, then bail out of here.
if (isSU) {
clearTimeout(timerID)
url = 'skp:do_open@';
url += 'level_name=' + world.colladaFile;
url += '&friendly_name=' + world.name;
window.location.href = url;
$('export-button').disabled = false;
$('play-button').disabled = false;
$('pause-button').disabled = true;
isLoaded = true;
return;
}
var path = window.location.href;
var index = path.lastIndexOf('/');
path = path.substring(0, index + 1) + 'levels/' + world.colladaFile;
$('o3d').style.width = '845px';
$('o3d').style.height = '549px';
// Create a callback function to prepare the transform graph once its loaded.
function callback(exception) {
if (exception) {
alert('Could not load: ' + path + '\n' + exception);
} else {
var current_light = 0;
var my_effect = pack.createObject('Effect');
my_effect.name = 'global_effect';
var effect_string = $('global_effect').value;
my_effect.loadFromFXString(effect_string);
var xform_nodes = client.root.getTransformsInTree();
for (var i = 0; i < xform_nodes.length; ++i) {
var shape_name = xform_nodes[i].name;
if (shape_name.indexOf('Light') == 0) {
var world_transform = xform_nodes[i].getUpdatedWorldMatrix();
var world_loc = world_transform[3];
var world_location = world_loc;
var color = [1.0, 0.7, 0.10, 150.0];
var light_index = AddLight(world_location, color);
xform_nodes[i].parent = null;
++current_light;
}
}
{
var materials = pack.getObjectsByClassName('o3d.Material');
for (var m = 0; m < materials.length; ++m) {
var material = materials[m];
if (!material.getParam('diffuseSampler')) {
diffuseParam = material.createParam('diffuseSampler',
'ParamSampler');
diffuseParam.value = blackSampler;
}
material.effect = my_effect;
my_effect.createUniformParameters(material);
// go through each param on the material
var params = material.params;
for (var p = 0; p < params.length; ++p) {
var dst_param = params[p];
// see if there is one of the same name on the paramObject we're
// using for global parameters.
var src_param = globalParams.getParam(dst_param.name);
if (src_param) {
// see if they are compatible
if (src_param.isAClassName(dst_param.className)) {
// bind them
dst_param.bind(src_param);
}
}
}
}
}
// Remove all line primitives as we don't really want them to render.
{
var primitives = pack.getObjectsByClassName('o3d.Primitive');
for (var p = 0; p < primitives.length; ++p) {
var primitive = primitives[p];
if (primitive.primitiveType == o3d.Primitive.LINELIST) {
primitive.owner = null;
pack.removeObject(primitive);
}
}
}
o3djs.pack.preparePack(pack, g_viewInfo);
avatar = world.actors[0];
isLoaded = true;
client.setRenderCallback(onRender);
$('container').style.visibility = 'visible';
}
}
// Create a new transform node to attach our world to.
var world_parent = pack.createObject('Transform');
world_parent.parent = client.root;
function loadSceneCallback(pack, parent, exception) {
g_loadInfo = null;
callback(exception);
}
g_loadInfo = o3djs.scene.loadScene(client, pack, world_parent, path,
loadSceneCallback);
}
/**
* This is our little callback handler that o3d calls after each frame.
* @param {Object} renderEvent An event object.
*/
function onRender(renderEvent) {
if (g_loadInfo) {
$('fps').innerHTML = 'Loading: ' +
g_loadInfo.getKnownProgressInfoSoFar().percent + '%';
}
if (isLoaded == false) {
return;
}
var elapsedTime = Math.min(renderEvent.elapsedTime, 1 / 20);
nextFrame(elapsedTime);
UpdateLightsInProgram(avatar);
// Every 20 frames, update our frame rate display
frameCount++;
if (frameCount % 20 == 0) {
$('fps').innerHTML = Math.floor(1.0 / elapsedTime + 0.5);
}
}
/**
* This cancels scrolling and keeps focus on a hidden element so spacebar
* doesn't page down.
*/
function cancelScroll() {
$('focusHolder').focus();
var pageWidth = document.body.clientWidth;
var pageHeight = document.body.clientHeight;
document.body.scrollTop = 0;
document.body.scrollLeft = 0;
}