summaryrefslogtreecommitdiffstats
path: root/o3d/samples/home-configurators/viewer.js
diff options
context:
space:
mode:
Diffstat (limited to 'o3d/samples/home-configurators/viewer.js')
-rw-r--r--o3d/samples/home-configurators/viewer.js578
1 files changed, 578 insertions, 0 deletions
diff --git a/o3d/samples/home-configurators/viewer.js b/o3d/samples/home-configurators/viewer.js
new file mode 100644
index 0000000..69c4fb7
--- /dev/null
+++ b/o3d/samples/home-configurators/viewer.js
@@ -0,0 +1,578 @@
+/*
+ * 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.
+ */
+
+
+/**
+ * This file contains definitions for the common functions used by all the home
+ * configurator pages.
+ */
+o3djs.require('o3djs.util');
+o3djs.require('o3djs.arcball');
+o3djs.require('o3djs.dump');
+o3djs.require('o3djs.rendergraph');
+o3djs.require('o3djs.shape');
+o3djs.require('o3djs.effect');
+o3djs.require('o3djs.material');
+o3djs.require('o3djs.pack');
+o3djs.require('o3djs.picking');
+o3djs.require('o3djs.scene');
+
+var g_root;
+var g_o3d;
+var g_math;
+var g_client;
+var g_thisRot;
+var g_lastRot;
+var g_pack = null;
+var g_mainPack;
+var g_viewInfo;
+var g_lightPosParam;
+var g_currentTool = null;
+var g_floorplanRoot = null;
+var g_placedModesRoot = null;
+var TOOL_ORBIT = 3;
+var TOOL_MOVE = 1;
+var TOOL_ZOOM_EXTENTS = 6;
+var g_urlToInsert;
+var g_isDraggingItem = false;
+var g_o3dElement = null;
+var g_lastMouseButtonState = 0;
+
+/**
+ * Retrieve the absolute position of an element on the screen.
+ */
+function getAbsolutePosition(element) {
+ var r = { x: element.offsetLeft, y: element.offsetTop };
+ if (element.offsetParent) {
+ var tmp = getAbsolutePosition(element.offsetParent);
+ r.x += tmp.x;
+ r.y += tmp.y;
+ }
+ return r;
+}
+
+/**
+ * Retrieve the coordinates of the given event relative to the center
+ * of the widget.
+ *
+ * @param event
+ * A mouse-related DOM event.
+ * @param reference
+ * A DOM element whose position we want to transform the mouse coordinates to.
+ * @return
+ * An object containing keys 'x' and 'y'.
+ */
+function getRelativeCoordinates(event, reference) {
+ var x, y;
+ event = event || window.event;
+ var el = event.target || event.srcElement;
+ if (!window.opera && typeof event.offsetX != 'undefined') {
+ // Use offset coordinates and find common offsetParent
+ var pos = { x: event.offsetX, y: event.offsetY };
+ // Send the coordinates upwards through the offsetParent chain.
+ var e = el;
+ while (e) {
+ e.mouseX = pos.x;
+ e.mouseY = pos.y;
+ pos.x += e.offsetLeft;
+ pos.y += e.offsetTop;
+ e = e.offsetParent;
+ }
+ // Look for the coordinates starting from the reference element.
+ var e = reference;
+ var offset = { x: 0, y: 0 }
+ while (e) {
+ if (typeof e.mouseX != 'undefined') {
+ x = e.mouseX - offset.x;
+ y = e.mouseY - offset.y;
+ break;
+ }
+ offset.x += e.offsetLeft;
+ offset.y += e.offsetTop;
+ e = e.offsetParent;
+ }
+ // Reset stored coordinates
+ e = el;
+ while (e) {
+ delete e.mouseX;
+ delete e.mouseY;
+ e = e.offsetParent;
+ }
+ }
+ else {
+ // Use absolute coordinates
+ var pos = getAbsolutePosition(reference);
+ x = event.pageX - pos.x;
+ y = event.pageY - pos.y;
+ }
+
+ return { x: x, y: y };
+}
+
+
+// The target camera has its z and y flipped because that's the way Scott
+// Lininger thinks.
+function TargetCamera() {
+ this.eye = {
+ rotZ: -Math.PI / 3,
+ rotH: Math.PI / 3,
+ distanceFromTarget: 700 };
+ this.target = { x: 0, y: 0, z: 0 };
+}
+
+TargetCamera.prototype.update = function() {
+ var target = [this.target.x, this.target.y, this.target.z];
+
+ this.eye.x = this.target.x + Math.cos(this.eye.rotZ) *
+ this.eye.distanceFromTarget * Math.sin(this.eye.rotH);
+ this.eye.y = this.target.y + Math.sin(this.eye.rotZ) *
+ this.eye.distanceFromTarget * Math.sin(this.eye.rotH);
+ this.eye.z = this.target.z + Math.cos(this.eye.rotH) *
+ this.eye.distanceFromTarget;
+
+ var eye = [this.eye.x, this.eye.y, this.eye.z];
+ var up = [0, 0, 1];
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt(eye, target, up);
+ g_lightPosParam.value = eye;
+};
+
+var g_camera = new TargetCamera();
+
+function peg(value, lower, upper) {
+ if (value < lower) {
+ return lower;
+ } else if (value > upper) {
+ return upper;
+ } else {
+ return value;
+ }
+}
+
+/**
+ * Keyboard constants.
+ */
+var BACKSPACE = 8;
+var TAB = 9;
+var ENTER = 13;
+var SHIFT = 16;
+var CTRL = 17;
+var ALT = 18;
+var ESCAPE = 27;
+var PAGEUP = 33;
+var PAGEDOWN = 34;
+var END = 35;
+var HOME = 36;
+var LEFT = 37;
+var UP = 38;
+var RIGHT = 39;
+var DOWN = 40;
+var DELETE = 46;
+var SPACE = 32;
+
+/**
+ * Create some global key capturing. Keys that are pressed will be stored in
+ * this global array.
+ */
+g_keyIsDown = [];
+
+document.onkeydown = function(e) {
+ var keycode;
+ if (window.event) {
+ keycode = window.event.keyCode;
+ } else if (e) {
+ keycode = e.which;
+ }
+ g_keyIsDown[keycode] = true;
+ if (g_currentTool != null) {
+ g_currentTool.handleKeyDown(keycode);
+ }
+};
+
+document.onkeyup = function(e) {
+ var keycode;
+ if (window.event) {
+ keycode = window.event.keyCode;
+ } else if (e) {
+ keycode = e.which;
+ }
+ g_keyIsDown[keycode] = false;
+ if (g_currentTool != null) {
+ g_currentTool.handleKeyUp(keycode);
+ }
+};
+
+document.onmouseup = function(e) {
+ if (g_currentTool != null) {
+ g_currentTool.handleMouseUp(e);
+ } else {
+ cancelInsertDrag();
+ }
+};
+
+// NOTE: mouseDown, mouseMove and mouseUp are mouse event handlers for events
+// taking place inside the o3d area. They typically pass the events down
+// to the currently selected tool (e.g. Orbit, Move, etc). Tool and item
+// selection mouse events are registered seperately on their respective DOM
+// elements.
+
+// This function handles the mousedown events that happen inside the o3d
+// area. If a tool is currently selected (e.g. Orbit, Move, etc.) the event
+// is forwarded over to it. If the middle mouse button is pressed then we
+// temporarily switch over to the orbit tool to emulate the SketchUp behavior.
+function mouseDown(e) {
+ // If the middle mouse button is used, then switch into the orbit tool,
+ // Sketchup-style.
+ if (e.button == g_o3d.Event.BUTTON_MIDDLE) {
+ g_lastTool = g_currentTool;
+ g_lastSelectorLeft = $('toolselector').style.left;
+ selectTool(null, TOOL_ORBIT);
+ }
+
+ if (g_currentTool != null) {
+ g_currentTool.handleMouseDown(e);
+ }
+}
+
+// This function handles mouse move events inside the o3d area. It simply
+// forwards them down to the currently selected tool.
+function mouseMove(e) {
+ if (g_currentTool != null) {
+ g_currentTool.handleMouseMove(e);
+ }
+}
+
+// This function handles mouse up events that take place in the o3d area.
+// If the middle mouse button is lifted then we switch out of the temporary
+// orbit tool mode.
+function mouseUp(e) {
+ // If the middle mouse button was used, then switch out of the orbit tool
+ // and reset to their last tool.
+ if (e.button == g_o3d.Event.BUTTON_MIDDLE) {
+ $('toolselector').style.left = g_lastSelectorLeft;
+ g_currentTool = g_lastTool
+ }
+ if (g_currentTool != null) {
+ g_currentTool.handleMouseUp(e);
+ }
+}
+
+function scrollMe(e) {
+ e = e ? e : window.event;
+ var raw = e.detail ? e.detail : -e.wheelDelta;
+ if (raw < 0) {
+ g_camera.eye.distanceFromTarget *= 11 / 12;
+
+ } else {
+ g_camera.eye.distanceFromTarget *= (1 + 1 / 12);
+ }
+ g_camera.update();
+}
+
+function $(name) {
+ return document.getElementById(name);
+}
+
+
+// An array of tool objects that will get populated when our base model loads.
+var g_tools = [];
+
+function selectTool(e, opt_toolNumber) {
+ var ICON_WIDTH = 32;
+ var toolNumber = opt_toolNumber;
+
+ if (toolNumber == undefined) {
+ // Where you click determines your tool. But since our underlying toolbar
+ // graphic isn't perfectly uniform, perform some acrobatics to get the best
+ // toolNumber match.
+ var pt = getRelativeCoordinates(e, $('toolpanel'));
+ if (pt.x < 120) {
+ toolNumber = Math.floor((pt.x - 8) / ICON_WIDTH)
+ } else {
+ toolNumber = Math.floor((pt.x - 26) / ICON_WIDTH)
+ }
+ toolNumber = peg(toolNumber, 0, 9);
+ }
+
+ // Now place the selector graphic over the tool we clicked.
+ if (toolNumber < 3) {
+ $('toolselector').style.left = toolNumber * ICON_WIDTH + 8;
+ } else {
+ $('toolselector').style.left = toolNumber * ICON_WIDTH + 26;
+ }
+
+ // Finally, activate the appropriate tool.
+ // TODO: Replace this hacky zoom extents tool detection with
+ // tools that have an onActivate callback.
+ if (toolNumber == TOOL_ZOOM_EXTENTS) {
+ // Reset our camera. (Zooming to extents would be better, obviously ;)
+ g_camera.eye.distanceFromTarget = 900;
+ g_camera.target = { x: -100, y: 0, z: 0 };
+ g_camera.update();
+
+ // Then reset to the orbit tool, after pausing for a bit so the user
+ // sees the zoom extents button depress.
+ setTimeout(function() { selectTool(null, TOOL_ORBIT)}, 200);
+ } else {
+ g_currentTool = g_tools[toolNumber];
+ }
+
+}
+
+function loadFile(context, path) {
+ function callback(pack, start_move_tool_root, exception) {
+ if (exception) {
+ alert('Could not load: ' + path + '\n' + exception);
+ } else {
+ // Generate draw elements and setup material draw lists.
+ o3djs.pack.preparePack(g_pack, g_viewInfo);
+
+ // Manually connect all the materials' lightWorldPos params to the context
+ var materials = g_pack.getObjectsByClassName('o3d.Material');
+ for (var m = 0; m < materials.length; ++m) {
+ var material = materials[m];
+ var param = material.getParam('lightWorldPos');
+ if (param) {
+ param.bind(g_lightPosParam);
+ }
+ }
+
+ /*
+ o3djs.dump.dump('---dumping context---\n');
+ o3djs.dump.dumpParamObject(context);
+
+ o3djs.dump.dump('---dumping root---\n');
+ o3djs.dump.dumpTransformTree(g_client.root);
+
+ o3djs.dump.dump('---dumping render root---\n');
+ o3djs.dump.dumpRenderNodeTree(g_client.renderGraphRoot);
+
+ o3djs.dump.dump('---dump g_pack shapes---\n');
+ var shapes = g_pack.getObjectsByClassName('o3d.Shape');
+ for (var t = 0; t < shapes.length; t++) {
+ o3djs.dump.dumpShape(shapes[t]);
+ }
+
+ o3djs.dump.dump('---dump g_pack materials---\n');
+ var materials = g_pack.getObjectsByClassName('o3d.Material');
+ for (var t = 0; t < materials.length; t++) {
+ o3djs.dump.dump (
+ ' ' + t + ' : ' + materials[t].className +
+ ' : "' + materials[t].name + '"\n');
+ o3djs.dump.dumpParams(materials[t], ' ');
+ }
+
+ o3djs.dump.dump('---dump g_pack textures---\n');
+ var textures = g_pack.getObjectsByClassName('o3d.Texture');
+ for (var t = 0; t < textures.length; t++) {
+ o3djs.dump.dumpTexture(textures[t]);
+ }
+
+ o3djs.dump.dump('---dump g_pack effects---\n');
+ var effects = g_pack.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], ' ');
+ }
+ */
+ }
+ if (start_move_tool_root != g_floorplanRoot) {
+ selectTool(null, TOOL_MOVE);
+ g_tools[TOOL_MOVE].initializeWithShape(start_move_tool_root);
+ }
+ g_camera.update();
+ }
+
+ g_pack = g_client.createPack();
+ g_pack.name = 'load pack';
+
+ new_object_root = null;
+ if (g_floorplanRoot == null) {
+ // Assign as the floorplan
+ g_floorplanRoot = g_pack.createObject('o3d.Transform');
+ g_floorplanRoot.name = 'floorplan';
+ g_floorplanRoot.parent = g_client.root;
+
+ g_placedModelsRoot = g_pack.createObject('o3d.Transform');
+ g_placedModelsRoot.name = 'placedModels';
+ g_placedModelsRoot.parent = g_floorplanRoot;
+
+ // Put the object we're loading on the floorplan.
+ new_object_root = g_floorplanRoot;
+
+ // Create our set of tools that can be activated.
+ // Note: Currently only the Delete, Move, Rotate, Orbit, Pan and Zoom
+ // tools are implemented. The last four icons in the toolbar are unused.
+ g_tools = [
+ new DeleteTool(g_viewInfo.drawContext, g_placedModelsRoot),
+ new MoveTool(g_viewInfo.drawContext, g_placedModelsRoot),
+ new RotateTool(g_viewInfo.drawContext, g_placedModelsRoot),
+ new OrbitTool(g_camera),
+ new PanTool(g_camera),
+ new ZoomTool(g_camera),
+ null,
+ null,
+ null,
+ null
+ ]
+
+ selectTool(null, TOOL_ORBIT);
+ } else {
+ // Create a new transform for the loaded file
+ new_object_root = g_pack.createObject('o3d.Transform');
+ new_object_root.name = 'loaded object';
+ new_object_root.parent = g_placedModelsRoot;
+
+ }
+
+ if (path != null) {
+ o3djs.scene.loadScene(g_client, g_pack, new_object_root, path, callback);
+ }
+
+ return new_object_root;
+}
+
+function setClientSize() {
+ // Create a perspective projection matrix
+ g_viewInfo.drawContext.projection = g_math.matrix4.perspective(
+ 3.14 * 45 / 180, g_client.width / g_client.height, 0.1, 5000);
+}
+
+function resize() {
+ setClientSize();
+}
+
+function init() {
+ o3djs.util.makeClients(initStep2);
+}
+
+function initStep2(clientElements) {
+ g_o3dElement = clientElements[0];
+
+ var path = window.location.href;
+ var index = path.lastIndexOf('/');
+ path = path.substring(0, index + 1) + g_buildingAsset;
+ var url = document.getElementById('url').value = path;
+
+ g_o3d = g_o3dElement.o3d;
+ g_math = o3djs.math;
+ g_client = g_o3dElement.client;
+
+ g_mainPack = g_client.createPack();
+ g_mainPack.name = 'simple viewer pack';
+
+ // Create the render graph for a view.
+ g_viewInfo = o3djs.rendergraph.createBasicView(
+ g_mainPack,
+ g_client.root,
+ g_client.renderGraphRoot,
+ [1, 1, 1, 1]);
+
+ g_lastRot = g_math.identity(3);
+ g_thisRot = g_math.identity(3);
+
+ var root = g_client.root;
+
+ var target = [0, 0, 0];
+ var eye = [0, 0, 5];
+ var up = [0, 1, 0];
+ g_viewInfo.drawContext.view = g_math.matrix4.lookAt(eye, target, up);
+
+ setClientSize();
+
+ var paramObject = g_mainPack.createObject('ParamObject');
+ // Set the light at the same position as the camera to create a headlight
+ // that illuminates the object straight on.
+ g_lightPosParam = paramObject.createParam('lightWorldPos', 'ParamFloat3');
+ g_lightPosParam.value = eye;
+
+ doload();
+
+ o3djs.event.addEventListener(g_o3dElement, 'mousedown', mouseDown);
+ o3djs.event.addEventListener(g_o3dElement, 'mousemove', mouseMove);
+ o3djs.event.addEventListener(g_o3dElement, 'mouseup', mouseUp);
+
+ g_o3dElement.addEventListener('mouseover', dragOver, false);
+ // for Firefox
+ g_o3dElement.addEventListener('DOMMouseScroll', scrollMe, false);
+ // for Safari
+ g_o3dElement.onmousewheel = scrollMe;
+
+ document.getElementById('toolpanel').onmousedown = selectTool;
+
+ // Create our catalog list from the global list of items (g_items).
+ html = [];
+ for (var i = 0; i < g_items.length; i++) {
+ html.push('<div class="catalogItem" onmousedown="startInsertDrag(\'',
+ g_items[i].url, '\')" style="background-position:-', (i * 62) ,
+ 'px 0px" title="', g_items[i].title, '"></div>');
+ }
+ $('itemlist').innerHTML = html.join('');
+
+ // Register a mouse-move event listener to the entire window so that we can
+ // catch the click-and-drag events that originate from the list of items
+ // and end up in the o3d element.
+ document.addEventListener('mousemove', mouseMove, false);
+}
+
+function dragOver(e) {
+ if (g_urlToInsert != null) {
+ doload(g_urlToInsert);
+ }
+ g_urlToInsert = null;
+}
+
+function doload(opt_url) {
+ var url;
+ if (opt_url != null) {
+ url = opt_url
+ } else if ($('url')) {
+ url = $('url').value;
+ }
+ g_root = loadFile(g_viewInfo.drawContext, url);
+}
+
+function startInsertDrag(url) {
+ // If no absolute web path was passed, assume it's a local file
+ // coming from the assets directory.
+ if (url.indexOf('http') != 0) {
+ var path = window.location.href;
+ var index = path.lastIndexOf('/');
+ g_urlToInsert = path.substring(0, index + 1) + g_assetPath + url;
+ } else {
+ g_urlToInsert = url;
+ }
+}
+
+function cancelInsertDrag() {
+ g_urlToInsert = null;
+}
+