diff options
Diffstat (limited to 'o3d/samples/o3d-webgl')
-rw-r--r-- | o3d/samples/o3d-webgl/archive_request.js | 268 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/base.js | 14 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/file_request.js | 4 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/pack.js | 41 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/param_object.js | 13 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/raw_data.js | 2 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/texture.js | 14 | ||||
-rw-r--r-- | o3d/samples/o3d-webgl/transform.js | 22 |
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(); }; |