/* GLGE WebGL Graphics Engine Copyright (c) 2010, Paul Brunt 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 GLGE 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 PAUL BRUNT 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 * @name glge_collada.js * @author me@paulbrunt.co.uk */ if(!GLGE){ var GLGE={}; } (function(GLGE){ GLGE.ColladaDocuments=[]; /** * Exceptions for the bad exports out there, I'm sure there will be many more :-( */ var exceptions={ "default":{}, "COLLADA Mixamo exporter":{badAccessor:true} } /** * @class Class to represent a collada object * @augments GLGE.Group */ GLGE.Collada=function(){ this.children=[]; this.actions={}; }; GLGE.augment(GLGE.Group,GLGE.Collada); GLGE.Collada.prototype.type=GLGE.G_NODE; /** * Gets the absolute path given an import path and the path it's relative to * @param {string} path the path to get the absolute path for * @param {string} relativeto the path the supplied path is relativeto * @returns {string} absolute path * @private */ GLGE.Collada.prototype.getAbsolutePath=function(path,relativeto){ if(path.substr(0,7)=="http://" || path.substr(0,7)=="file://" || path.substr(0,7)=="https://"){ return path; } else { if(!relativeto){ relativeto=window.location.href; } //find the path compoents var bits=relativeto.split("/"); var domain=bits[2]; var proto=bits[0]; var initpath=[]; for(var i=3;i0) polygons[i].getElementsByTagName("p")[0].data=tris; } //create a mesh for each set of faces var triangles=[]; var tris=meshNode.getElementsByTagName("triangles"); for(i=0;i0) triangles.push(polygons[i])}; for(i=0;i8){ var newjoints=[]; var newweights=[]; for(var j=0;j * * The material getter below borked if there is e.g. a scene node with the same name as the material. * This is used to fix that by only looking for materials in the library_materials element. */ function getChildElementById( dNode, id ) { var dResult = null; if ( dNode.getAttribute('id') == id ) return dNode; for ( var i = 0; i < dNode.childNodes.length; i++ ) { if ( dNode.childNodes[i].nodeType == 1 ) { dResult = getChildElementById( dNode.childNodes[i], id ); //note: 1-level deep would suffice here, doesn't need to recurse into further childs. but this works. if ( dResult != null ) break; } } return dResult; } /** * Gets the sampler for a texture * @param {string} id the id or the material element * @private */ GLGE.Collada.prototype.getMaterial=function(id){ var materialLib=this.xml.getElementsByTagName("library_materials")[0]; var materialNode=getChildElementById(materialLib, id); //this.xml.getElementById(id); var effectid=materialNode.getElementsByTagName("instance_effect")[0].getAttribute("url").substr(1); var effect=this.xml.getElementById(effectid); var common=effect.getElementsByTagName("profile_COMMON")[0]; //glge only supports one technique currently so try and match as best we can var technique=common.getElementsByTagName("technique")[0]; var returnMaterial=new GLGE.Material(); returnMaterial.setSpecular(0); var child; var color; //do diffuse color var diffuse=technique.getElementsByTagName("diffuse"); if(diffuse.length>0){ child=diffuse[0].firstChild; do{ switch(child.tagName){ case "color": color=child.firstChild.nodeValue.replace(/\s+/g,' ').split(" "); returnMaterial.setColor({r:color[0],g:color[1],b:color[2]}); break; case "param": color=this.getFloat4(common,child.getAttribute("ref")).replace(/\s+/g,' ').split(" "); returnMaterial.setColor({r:color[0],g:color[1],b:color[2]}); break; case "texture": this.createMaterialLayer(child,returnMaterial,common,GLGE.M_COLOR); break; } }while(child=child.nextSibling); } var bump=technique.getElementsByTagName("bump"); if(bump.length>0){ child=bump[0].firstChild; do{ switch(child.tagName){ case "texture": this.createMaterialLayer(child,returnMaterial,common,GLGE.M_NOR); break; } }while(child=child.nextSibling); } //do shininess var shininess=technique.getElementsByTagName("shininess"); if(shininess.length>0){ returnMaterial.setSpecular(1); child=technique.getElementsByTagName("shininess")[0].firstChild; do{ switch(child.tagName){ case "float": if(parseFloat(child.firstChild.nodeValue)>1) returnMaterial.setShininess(parseFloat(child.firstChild.nodeValue)); else returnMaterial.setShininess(parseFloat(child.firstChild.nodeValue)*128); break; case "param": var value=parseFloat(this.getFloat(common,child.getAttribute("ref"))); if(value>1) returnMaterial.setShininess(value); else returnMaterial.setShininess(value*128); break; // MCB: texture is invalid here. should remove this case. case "texture": this.createMaterialLayer(child,returnMaterial,common,GLGE.M_SHINE); break; } }while(child=child.nextSibling); } //do specular color var specular=technique.getElementsByTagName("specular"); if(specular.length>0){ returnMaterial.setSpecular(1); child=specular[0].firstChild; do{ switch(child.tagName){ case "color": color=child.firstChild.nodeValue.replace(/\s+/g,' ').split(" "); returnMaterial.setSpecularColor({r:color[0],g:color[1],b:color[2]}); break; case "param": color=this.getFloat4(common,child.getAttribute("ref")).replace(/\s+/g,' ').split(" "); returnMaterial.setSpecularColor({r:color[0],g:color[1],b:color[2]}); break; case "texture": this.createMaterialLayer(child,returnMaterial,common,GLGE.M_SPECCOLOR); break; } }while(child=child.nextSibling); } //do reflectivity /* var reflectivity=technique.getElementsByTagName("reflectivity"); if(reflectivity.length>0){ child=reflectivity[0].firstChild; do{ switch(child.tagName){ case "float": //returnMaterial.setReflectivity(parseFloat(child.firstChild.nodeValue)) break; case "param": //returnMaterial.setReflectivity(parseFloat(this.getFloat(common,child.getAttribute("ref")))); break; // MCB: texture is invalid here. should remove this case. case "texture": var imageid=this.getSurface(common,this.getSampler(common,child.getAttribute("texture"))); textureImage=this.getImage(imageid); var texture=new GLGE.Texture(textureImage); returnMaterial.addTexture(texture); returnMaterial.addMaterialLayer(new GLGE.MaterialLayer(texture,GLGE.M_REFLECT,GLGE.UV1)); break; } }while(child=child.nextSibling); }*/ //do emission color /*var emission=technique.getElementsByTagName("emission"); if(emission.length>0){ child=emission[0].firstChild; do{ switch(child.tagName){ case "color": color=child.firstChild.nodeValue.split(" "); returnMaterial.setEmit(color[0]); break; case "param": color=this.getFloat4(common,child.getAttribute("ref")).split(" "); returnMaterial.setEmit(color[0]); break; case "texture": this.createMaterialLayer(child,returnMaterial,common,GLGE.M_EMIT); break; } }while(child=child.nextSibling); }*/ //do reflective color var reflective=technique.getElementsByTagName("reflective"); if(reflective.length>0){ child=reflective[0].firstChild; do{ switch(child.tagName){ case "color": color=child.firstChild.nodeValue.replace(/\s+/g,' ').split(" "); //TODO returnMaterial.setReflectiveColor({r:color[0],g:color[1],b:color[2]}); break; case "param": color=this.getFloat4(common,child.getAttribute("ref")).replace(/\s+/g,' ').split(" "); //TODO returnMaterial.setReflectiveColor({r:color[0],g:color[1],b:color[2]}); break; case "texture": this.createMaterialLayer(child,returnMaterial,common,GLGE.M_REFLECT); break; } }while(child=child.nextSibling); } //do transparency var transparency=technique.getElementsByTagName("transparency"); if(transparency.length>0){ child=transparency[0].firstChild; do{ switch(child.tagName){ case "float": //TODO returnMaterial.setTransparency(parseFloat(child.firstChild.nodeValue)) break; case "param": //TODO returnMaterial.setTransparency(parseFloat(this.getFloat(common,child.getAttribute("ref")))); break; } }while(child=child.nextSibling); } //do transparent color var transparent=technique.getElementsByTagName("transparent"); if(transparent.length>0){ var opaque=transparent[0].getAttribute("opaque"); if(!opaque) opaque="A_ONE"; // schema default child=transparent[0].firstChild; do{ switch(child.tagName){ // MCB: float is invalid here. should remove this case. case "float": var alpha=parseFloat(child.firstChild.nodeValue); if(alpha<1){ returnMaterial.setAlpha(parseFloat(child.firstChild.nodeValue)); returnMaterial.trans=true; } break; case "color": color=child.firstChild.nodeValue.replace(/\s+/g,' ').split(" "); var alpha=this.getMaterialAlpha(color,opaque,1); //TODO var alpha=this.getMaterialAlpha(color,opaque,returnMaterial.getTransparency()); if(alpha<1){ returnMaterial.setAlpha(alpha); returnMaterial.trans=true; } break; case "param": color=this.getFloat4(common,child.getAttribute("ref")).replace(/\s+/g,' ').split(" "); var alpha=this.getMaterialAlpha(color,opaque,1); //TODO var alpha=this.getMaterialAlpha(color,opaque,returnMaterial.getTransparency()); if(alpha<1){ returnMaterial.setAlpha(alpha); returnMaterial.trans=true; } break; // MCB: this case assumes opaque="A_ONE" and transparency="1.0" case "texture": this.createMaterialLayer(child,returnMaterial,common,GLGE.M_ALPHA); returnMaterial.trans=true; break; } }while(child=child.nextSibling); } return returnMaterial; }; /** * gets the material alpha from the transparent color * @param {color} the transparent color * @param {opaque} the transparent color opaque attribute value * @param {transparency} the transparency value * @private */ GLGE.Collada.prototype.getMaterialAlpha=function(color,opaque,transparency){ var returnAlpha; switch(opaque){ case "A_ONE": returnAlpha=parseFloat(color[3])*transparency; break; case "A_ZERO": returnAlpha=1-parseFloat(color[3])*transparency; break; case "RGB_ONE": var luminance=parseFloat(color[0])*0.212671 +parseFloat(color[1])*0.715160 +parseFloat(color[2])*0.072169; returnAlpha=luminance*transparency; break; case "RGB_ZERO": var luminance=parseFloat(color[0])*0.212671 +parseFloat(color[1])*0.715160 +parseFloat(color[2])*0.072169; returnAlpha=1-luminance*transparency; break; } return returnAlpha; }; /** * creates a GLGE Object from a given instance Geomertry * @param {node} node the element to parse * @private */ GLGE.Collada.prototype.getInstanceGeometry=function(node){ if(node.GLGEObj){ var obj=new GLGE.ObjectInstance(); obj.setObject(node.GLGEObj); return obj; }else{ var meshes=this.getMeshes(node.getAttribute("url").substr(1)); var materials=node.getElementsByTagName("instance_material"); var objMaterials={}; for(var i=0; i1) idx=parseInt(idx[0])+4*parseInt(idx[1]); else idx=parseInt(idx[0]); sids[sidtarget].animations[idx]=animcurves[0]; }else{ //do all for(var j=0;j0){ bindShapeMatrix=this.parseArray(controller.getElementsByTagName("bind_shape_matrix")[0]); }else{ //assume identity bindShapeMatrix=GLGE.identMatrix(); } var inverseBindMatrix=[bindShapeMatrix]; var joints=[new GLGE.Group()]; var mat; for(var i=0; i0){ this.addGroup(this.getNode(scene[0])); }else{ GLGE.error("Please indicate the asset to render in Collada Document"+this.url); } }else{ var root=this.xml.getElementById(this.rootId); if(root){ this.addGroup(this.getNode(root)); }else{ GLGE.error("Asset "+this.rootId+" not found in document"+this.url); } } }; /** * Called when a collada document has is loaded * @param {string} url the url of the loaded document * @param {DOM Document} xml the xml document * @private */ GLGE.Collada.prototype.loaded=function(url,xml){ if(xml.getElementsByTagName("authoring_tool").length>0) this.exceptions=exceptions[xml.getElementsByTagName("authoring_tool")[0].firstChild.nodeValue]; if(!this.exceptions) this.exceptions=exceptions.default; this.xml=xml; this.initVisualScene(); this.getAnimations(); }; GLGE.Scene.prototype.addCollada=GLGE.Scene.prototype.addGroup; GLGE.Group.prototype.addCollada=GLGE.Group.prototype.addGroup; if(GLGE.Document){ /** * Parses the dom element and creates a collada object * @param {domelement} ele the element to create the objects from * @private */ GLGE.Document.prototype.getCollada=function(ele){ if(!ele.object){ ele.object=new GLGE[this.classString(ele.tagName)](); ele.object.setDocument(ele.getAttribute("document"),this.getAbsolutePath(this.rootURL,null)); ele.removeAttribute("document"); this.setProperties(ele); } return ele.object; } } })(GLGE);