summaryrefslogtreecommitdiffstats
path: root/o3d/samples/o3d-webgl
diff options
context:
space:
mode:
Diffstat (limited to 'o3d/samples/o3d-webgl')
-rw-r--r--o3d/samples/o3d-webgl/archive_request.js268
-rw-r--r--o3d/samples/o3d-webgl/base.js14
-rw-r--r--o3d/samples/o3d-webgl/file_request.js4
-rw-r--r--o3d/samples/o3d-webgl/pack.js41
-rw-r--r--o3d/samples/o3d-webgl/param_object.js13
-rw-r--r--o3d/samples/o3d-webgl/raw_data.js2
-rw-r--r--o3d/samples/o3d-webgl/texture.js14
-rw-r--r--o3d/samples/o3d-webgl/transform.js22
8 files changed, 360 insertions, 18 deletions
diff --git a/o3d/samples/o3d-webgl/archive_request.js b/o3d/samples/o3d-webgl/archive_request.js
new file mode 100644
index 0000000..e35282a
--- /dev/null
+++ b/o3d/samples/o3d-webgl/archive_request.js
@@ -0,0 +1,268 @@
+/*
+ * Copyright 2010, 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.
+ */
+
+// TODO(kbr): figure out how we can reuse the o3djs.io package from
+// within here.
+// o3djs.require('o3djs.io');
+
+// TODO(kbr): factor this out into e.g. o3djs.json and require
+// o3djs.json here.
+if(!this.JSON){this.JSON={};}
+(function(){function f(n){return n<10?'0'+n:n;}
+if(typeof Date.prototype.toJSON!=='function'){Date.prototype.toJSON=function(key){return isFinite(this.valueOf())?this.getUTCFullYear()+'-'+
+f(this.getUTCMonth()+1)+'-'+
+f(this.getUTCDate())+'T'+
+f(this.getUTCHours())+':'+
+f(this.getUTCMinutes())+':'+
+f(this.getUTCSeconds())+'Z':null;};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf();};}
+var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==='string'?c:'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4);})+'"':'"'+string+'"';}
+function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==='object'&&typeof value.toJSON==='function'){value=value.toJSON(key);}
+if(typeof rep==='function'){value=rep.call(holder,key,value);}
+switch(typeof value){case'string':return quote(value);case'number':return isFinite(value)?String(value):'null';case'boolean':case'null':return String(value);case'object':if(!value){return'null';}
+gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==='[object Array]'){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||'null';}
+v=partial.length===0?'[]':gap?'[\n'+gap+
+partial.join(',\n'+gap)+'\n'+
+mind+']':'['+partial.join(',')+']';gap=mind;return v;}
+if(rep&&typeof rep==='object'){length=rep.length;for(i=0;i<length;i+=1){k=rep[i];if(typeof k==='string'){v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}else{for(k in value){if(Object.hasOwnProperty.call(value,k)){v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}
+v=partial.length===0?'{}':gap?'{\n'+gap+partial.join(',\n'+gap)+'\n'+
+mind+'}':'{'+partial.join(',')+'}';gap=mind;return v;}}
+if(typeof JSON.stringify!=='function'){JSON.stringify=function(value,replacer,space){var i;gap='';indent='';if(typeof space==='number'){for(i=0;i<space;i+=1){indent+=' ';}}else if(typeof space==='string'){indent=space;}
+rep=replacer;if(replacer&&typeof replacer!=='function'&&(typeof replacer!=='object'||typeof replacer.length!=='number')){throw new Error('JSON.stringify');}
+return str('',{'':value});};}
+if(typeof JSON.parse!=='function'){JSON.parse=function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==='object'){for(k in value){if(Object.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v;}else{delete value[k];}}}}
+return reviver.call(holder,key,value);}
+text=String(text);cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return'\\u'+
+('0000'+a.charCodeAt(0).toString(16)).slice(-4);});}
+if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,'@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']').replace(/(?:^|:|,)(?:\s*\[)+/g,''))){j=eval('('+text+')');return typeof reviver==='function'?walk({'':j},''):j;}
+throw new SyntaxError('JSON.parse');};}}());
+
+
+/**
+ An ArchiveRequest object is used to carry out an asynchronous request for a
+ compressed archive (containing multiple files).
+
+ Note: The archive must have as its first file a file named 'aaaaaaaa.o3d'
+ who's contents is 'o3d'. This is to prevent O3D being used to open
+ archive files that were not meant for it.
+
+ \code
+ var request = pack.createArchiveRequest();
+ request.open("GET", url);
+
+ request.onfileavailable = myFileAvailableCallback;
+ request.onreadystatechange = myReadyStateChangeCallback;
+ request.send();
+
+ function myFileAvailableCallback(rawData) {
+ dump("uri: " + rawData.uri + "\n");
+ dump("content: " + rawData.stringValue + "\n");
+
+ // You can pass a RawData to various creation functions. Note: rawData
+ // is only valid until you remove the request.
+ // Examples:
+ if (rawData.uri == 'mytexture.jpg')
+ pack.createTexture2d(rawData, makeMips);
+ if (rawData.uri == 'myvertices.bin')
+ vertexBuffer.set(rawData);
+ if (rawData.uri == 'myAudio.mp3')
+ audioSystem.createSound(rawData);
+ }
+
+ function myReadyStateChangeCallback() {
+ if (request.done) {
+ if (request.success) {
+ // there were no errors trying to read the archive
+ } else {
+ dump(request.error);
+ }
+ }
+ }
+
+ // When you are done with the RawDatas loaded by the request, remove
+ // the request from the pack to free them.
+ pack.removeObject(request);
+*/
+
+o3d.ArchiveRequest = function() {
+ o3d.ObjectBase.call(this);
+ this.method_ = null;
+};
+o3d.inherit('ArchiveRequest', 'ObjectBase');
+
+/**
+ * The URI this request is for.
+ * @type {string}
+ */
+o3d.ArchiveRequest.prototype.uri = '';
+
+/**
+ * Set up several of the request fields.
+ * @param {string} method "GET" is the only supported method at this time
+ * @param {string} uri the location of the file to fetch
+ * @param {boolean} async true is the only legal value at this time.
+ */
+o3d.ArchiveRequest.prototype.open =
+ function(method, uri) {
+ this.uri = uri;
+
+ // Compute the parent directory of this URI.
+ var parentURI = uri;
+ var lastSlash = uri.lastIndexOf('/');
+ if (lastSlash != -1) {
+ parentURI = parentURI.substring(0, lastSlash + 1);
+ }
+
+ this.parentURI_ = parentURI;
+};
+
+/**
+ * Send the request.
+ * Unlike XMLHttpRequest the onreadystatechange callback will be called no
+ * matter what, with success or failure.
+ */
+o3d.ArchiveRequest.prototype.send = function() {
+ var that = this;
+ this.done = false;
+ this.success = true;
+ this.error = null;
+ var callback = function(sourceJSON, exc) {
+ // Don't send down the original scene.json because 'eval' is used
+ // elsewhere to reconstitute it, which is risky.
+ var filteredJSON = JSON.stringify(JSON.parse(sourceJSON));
+
+ if (that.onfileavailable) {
+ var rawData = new o3d.RawData();
+ rawData.uri = 'scene.json';
+ rawData.stringValue = filteredJSON;
+ that.onfileavailable(rawData);
+ }
+
+ // In o3d-webgl, the "archive" is really just the top-level
+ // scene.json. We run a regexp on it to find URIs for certain
+ // well-known file types (.fx, .png, .jpg) and issue file requests
+ // for them.
+ var uriRegex = /\"([^\"]*\.(fx|png|jpg))\"/g;
+ var matchArray;
+ var uris = [];
+ while ((matchArray = uriRegex.exec(sourceJSON)) != null) {
+ uris.push(matchArray[1]);
+ }
+
+ that.pendingRequests_ = uris.length;
+
+ // Issue requests for each of these URIs.
+ for (var ii = 0; ii < uris.length; ++ii) {
+ if (that.stringEndsWith_(uris[ii], ".fx")) {
+ var func = function(uri) {
+ var completion = function(value, exc) {
+ var rawData = null;
+ if (exc == null) {
+ rawData = new o3d.RawData();
+ rawData.uri = uri;
+ rawData.stringValue = value;
+ }
+ that.decrementPendingRequests_(rawData, exc);
+ };
+ o3djs.io.loadTextFile(that.relativeToAbsoluteURI_(uri),
+ completion);
+ };
+ func(uris[ii]);
+ } else if (that.stringEndsWith_(uris[ii], ".png") ||
+ that.stringEndsWith_(uris[ii], ".jpg")) {
+ var func = function(uri) {
+ var image = new Image();
+ image.onload = function() {
+ var rawData = new o3d.RawData();
+ rawData.uri = uri;
+ rawData.image_ = image;
+ that.decrementPendingRequests_(rawData, exc);
+ };
+ image.onerror = function() {
+ that.decrementPendingRequests_(null, exc);
+ }
+ image.src = that.relativeToAbsoluteURI_(uri);
+ };
+ func(uris[ii]);
+ }
+ }
+ };
+
+ o3djs.io.loadTextFile(this.uri, callback);
+};
+
+/**
+ * A callback to call whenever the ready state of the request changes.
+ * @type {function(): void}
+ */
+o3d.ArchiveRequest.prototype.onreadystatechange = null;
+
+/**
+ * A callback to call when each file comes in.
+ * @type {function(!o3d.RawData): void}
+ */
+o3d.ArchiveRequest.prototype.onfileavailable = null;
+
+/**
+ * Converts a local URI to an absolute URI.
+ * @private
+ */
+o3d.ArchiveRequest.prototype.relativeToAbsoluteURI_ = function(relativeURI) {
+ return this.parentURI_ + relativeURI;
+};
+
+/**
+ * Indicates whether one string ends with another.
+ * @private
+ */
+o3d.ArchiveRequest.prototype.stringEndsWith_ = function(string, suffix) {
+ return string.substring(string.length - suffix.length) == suffix;
+};
+
+/**
+ * Decrements the number of pending requests.
+ * @private
+ */
+o3d.ArchiveRequest.prototype.decrementPendingRequests_ =
+ function(rawData, opt_exc) {
+ this.success = this.success && rawData && (!opt_exc);
+ if (opt_exc != null) {
+ this.error = "" + opt_exc;
+ }
+ if (rawData && this.onfileavailable) {
+ this.onfileavailable(rawData);
+ }
+ if (--this.pendingRequests_ == 0) {
+ this.done = true;
+ if (this.onreadystatechange) {
+ this.onreadystatechange();
+ }
+ }
+};
diff --git a/o3d/samples/o3d-webgl/base.js b/o3d/samples/o3d-webgl/base.js
index 9962de5..d5e01cc 100644
--- a/o3d/samples/o3d-webgl/base.js
+++ b/o3d/samples/o3d-webgl/base.js
@@ -121,6 +121,19 @@ o3d.writeScriptTag_ = function(src) {
};
/**
+ * Filters any "o3d." prefix from the given type name.
+ * @param {string} type_name The type name to filter.
+ * @return {string} Filtered type name.
+ * @private
+ */
+o3d.filterTypeName_ = function(type_name) {
+ if (type_name.length >= 4 && type_name.substr(0, 4) == 'o3d.') {
+ type_name = type_name.substr(4);
+ }
+ return type_name;
+};
+
+/**
* Includes the file indicated by the rule by adding a script tag.
* @param {string} rule Rule to include, in the form o3d.package.part.
*/
@@ -220,5 +233,6 @@ o3d.include('primitive');
o3d.include('shape');
o3d.include('effect');
o3d.include('material');
+o3d.include('archive_request');
diff --git a/o3d/samples/o3d-webgl/file_request.js b/o3d/samples/o3d-webgl/file_request.js
index 8c0d44c..164e184 100644
--- a/o3d/samples/o3d-webgl/file_request.js
+++ b/o3d/samples/o3d-webgl/file_request.js
@@ -187,6 +187,10 @@ o3d.FileRequest.prototype.imageLoaded_ = function() {
o3d.FileRequest.prototype.open =
function(method, uri, async) {
this.uri = uri;
+ // TODO(petersont): I think there is a race condition here -- calling
+ // code expects that it can still set up the onreadystatechange callback
+ // between open() and send(), but if open() actually initiates the XHR
+ // then the caller may miss the crucial completion callback!
if (this.isImageUrl_(uri)) {
this.image_ = new Image();
var that = this;
diff --git a/o3d/samples/o3d-webgl/pack.js b/o3d/samples/o3d-webgl/pack.js
index 7ff3577..a9f79bb 100644
--- a/o3d/samples/o3d-webgl/pack.js
+++ b/o3d/samples/o3d-webgl/pack.js
@@ -171,7 +171,7 @@ o3d.Pack.prototype.removeObject =
*/
o3d.Pack.prototype.createObject =
function(type_name) {
- var foo = o3d.global.o3d[type_name];
+ var foo = o3d.global.o3d[o3d.filterTypeName_(type_name)];
if (typeof foo != 'function') {
throw 'cannot find type in o3d namespace: ' + type_name
}
@@ -232,18 +232,37 @@ o3d.Pack.prototype.createTexture2D =
* Note: If enable_render_surfaces is true, then the dimensions must be a
* power of two.
*
- * @param {number} edge_length The edge of the texture area in texels
+ * @param {number} edgeLength The edge of the texture area in texels
* (max = 2048)
* @param {o3d.Texture.Format} format The memory format of each texel.
* @param {number} levels The number of mipmap levels. Use zero to create
* the compelete mipmap chain.
- * @param {boolean} enable_render_surfaces If true, the texture object
+ * @param {boolean} enableRenderSurfaces If true, the texture object
* will expose RenderSurface objects through GetRenderSurface(...).
* @return {!o3d.TextureCUBE} The TextureCUBE object.
*/
o3d.Pack.prototype.createTextureCUBE =
- function(edge_length, format, levels, enable_render_surfaces) {
- o3d.notImplemented();
+ function(edgeLength, format, levels, enableRenderSurfaces) {
+ var texture = this.createObject('TextureCUBE');
+ texture.edgeLength = edgeLength;
+ texture.texture_ = this.gl.createTexture();
+
+ this.gl.bindTexture(this.gl.TEXTURE_CUBE_MAP, texture.texture_);
+ for (var ii = 0; ii < 6; ++ii) {
+ this.gl.texImage2D(this.gl.TEXTURE_CUBE_MAP_POSITIVE_X + ii,
+ 0, this.gl.RGBA, edgeLength, edgeLength, 0,
+ this.gl.RGBA, this.gl.UNSIGNED_BYTE, null);
+ }
+ this.gl.texParameteri(this.gl.TEXTURE_CUBE_MAP,
+ this.gl.TEXTURE_MAG_FILTER, this.gl.LINEAR);
+ this.gl.texParameteri(this.gl.TEXTURE_CUBE_MAP,
+ this.gl.TEXTURE_MIN_FILTER, this.gl.LINEAR);
+ this.gl.texParameteri(this.gl.TEXTURE_CUBE_MAP,
+ this.gl.TEXTURE_WRAP_S, this.gl.CLAMP_TO_EDGE);
+ this.gl.texParameteri(this.gl.TEXTURE_CUBE_MAP,
+ this.gl.TEXTURE_WRAP_T, this.gl.CLAMP_TO_EDGE);
+
+ return texture;
};
@@ -280,9 +299,7 @@ o3d.Pack.prototype.createDepthStencilSurface =
*/
o3d.Pack.prototype.getObjects =
function(name, class_type_name) {
- if (class_type_name.substr(0, 4) == 'o3d.') {
- class_type_name = class_type_name.substr(4);
- }
+ class_type_name = o3d.filterTypeName_(class_type_name);
var found = [];
@@ -361,6 +378,14 @@ o3d.Pack.prototype.createFileRequest =
return this.createObject('FileRequest');
};
+/**
+ * Creates an ArchiveRequest so we can stream in assets from an archive.
+ * @return {!o3d.ArchiveRequest} an ArchiveRequest
+ */
+o3d.Pack.prototype.createArchiveRequest =
+ function() {
+ return this.createObject('ArchiveRequest');
+};
/**
* Create Bitmaps from RawData.
diff --git a/o3d/samples/o3d-webgl/param_object.js b/o3d/samples/o3d-webgl/param_object.js
index 7749df2..f59f674 100644
--- a/o3d/samples/o3d-webgl/param_object.js
+++ b/o3d/samples/o3d-webgl/param_object.js
@@ -102,13 +102,14 @@ o3d.ParamObject.prototype.createParam =
function(param_name, param_type_name) {
if (this.params_[param_name])
return null;
+ param_type_name = o3d.filterTypeName_(param_type_name);
if (!o3d.global.o3d[param_type_name])
throw ('Invalid param type name: ' + param_type_name);
var param = new o3d.global.o3d[param_type_name];
param.gl = this.gl;
param.owner_ = this;
this.params_[param_name] = param;
- return this.params_[param_name];
+ return this.filterResult_(this.params_[param_name]);
};
@@ -120,7 +121,7 @@ o3d.ParamObject.prototype.createParam =
*/
o3d.ParamObject.prototype.getParam =
function(param_name) {
- return this.params_[param_name];
+ return this.filterResult_(this.params_[param_name]);
};
@@ -169,4 +170,10 @@ o3d.ParamObject.prototype.copyParams =
o3d.notImplemented();
};
-
+/**
+ * Filters results, turning 'undefined' into 'null'.
+ * @private
+ */
+o3d.ParamObject.prototype.filterResult_= function(result) {
+ return (result ? result : null);
+};
diff --git a/o3d/samples/o3d-webgl/raw_data.js b/o3d/samples/o3d-webgl/raw_data.js
index 59a441c..80a3489f 100644
--- a/o3d/samples/o3d-webgl/raw_data.js
+++ b/o3d/samples/o3d-webgl/raw_data.js
@@ -55,7 +55,7 @@ o3d.inherit('RawData', 'NamedObject');
* and the uri must end in .json, .txt, .xml, .ini or .csv
* @type {string}
*/
-o3d.RawData.prototype.string_value = '';
+o3d.RawData.prototype.stringValue = '';
/**
diff --git a/o3d/samples/o3d-webgl/texture.js b/o3d/samples/o3d-webgl/texture.js
index 90bda54..ce78349 100644
--- a/o3d/samples/o3d-webgl/texture.js
+++ b/o3d/samples/o3d-webgl/texture.js
@@ -376,7 +376,7 @@ o3d.TextureCUBE.FACE_NEGATIVE_Z = 5;
* The length of each edge of the cube, in texels.
* @type {number}
*/
-o3d.TextureCUBE.prototype.edge_length = 0;
+o3d.TextureCUBE.prototype.edgeLength = 0;
/**
@@ -469,7 +469,17 @@ o3d.TextureCUBE.prototype.getRect =
*/
o3d.TextureCUBE.prototype.setFromBitmap =
function(face, bitmap) {
- o3d.notImplemented();
+ this.gl.bindTexture(this.gl.TEXTURE_CUBE_MAP, this.texture_);
+ this.gl.texImage2D(this.gl.TEXTURE_CUBE_MAP_POSITIVE_X + face,
+ 0, // Level.
+ bitmap.canvas_,
+ false); // Do not flip cube maps' faces.
+ // TODO(petersont): figure out when we should call generateMipmaps.
+ // TODO(petersont): move generateMips to Texture2D / TextureCUBE
+ // classes because the target needs to differ.
+ // if (bitmap.defer_mipmaps_to_texture_) {
+ // this.generateMips();
+ // }
};
diff --git a/o3d/samples/o3d-webgl/transform.js b/o3d/samples/o3d-webgl/transform.js
index 68b02f3..6ed797b 100644
--- a/o3d/samples/o3d-webgl/transform.js
+++ b/o3d/samples/o3d-webgl/transform.js
@@ -135,10 +135,25 @@ o3d.Transform.prototype.addChild = function(child) {
*/
o3d.Transform.prototype.getTransformsInTree =
function() {
-
+ var result = [];
+ o3d.Transform.getTransformInTreeRecursive_(this, result);
+ return result;
};
+/**
+ * Recursive helper function for getTransformInTree.
+ * @private
+ */
+o3d.Transform.getTransformInTreeRecursive_ =
+ function(treeRoot, children) {
+ children.push(treeRoot);
+ var childrenArray = treeRoot.children;
+ for (var ii = 0; ii < childrenArray.length; ++ii) {
+ o3d.Transform.getTransformInTreeRecursive_(childrenArray[ii], children);
+ }
+};
+
/**
* Searches for transforms that match the given name in the hierarchy under and
@@ -155,10 +170,9 @@ o3d.Transform.prototype.getTransformsInTree =
*/
o3d.Transform.prototype.getTransformsByNameInTree =
function(name) {
-
+ o3d.notImplemented();
};
-
/**
* Evaluates and returns the current world matrix.
*
@@ -166,7 +180,7 @@ o3d.Transform.prototype.getTransformsByNameInTree =
*/
o3d.Transform.prototype.getUpdatedWorldMatrix =
function() {
-
+ o3d.notImplemented();
};