/* GLGE WebGL Graphics Engine Copyright (C)2009 Paul Brunt This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * @fileOverview * @name glge.js * @author me@paulbrunt.co.uk */ // Start of compatibility code try { WebGLFloatArray; } catch (e) { try { WebGLFloatArray = CanvasFloatArray; WebGLUnsignedShortArray = CanvasUnsignedShortArray; } catch (e) { alert("Could not find any WebGL array types."); } } // End of compatibility code /** * @namespace Holds the functionality of the library */ var GLGE={}; (function(GLGE){ /** * @constant * @description Enumeration for TRUE */ GLGE.TRUE=1; /** * @constant * @description Enumeration for FALSE */ GLGE.FALSE=0; /** * @constant * @description Enumeration for rendering using default shader */ GLGE.RENDER_DEFAULT=0; /** * @constant * @description Enumeration for rendering using shadow shader */ GLGE.RENDER_SHADOW=1; /** * @constant * @description Enumeration for rendering using pick shader */ GLGE.RENDER_PICK=2; /** * function to parse a colour input into RGB eg #ff00ff, red, rgb(100,100,100) * @param {string} color the color to parse */ GLGE.colorParse=function(color){ var red,green,blue; //defines the color names var color_names = { aliceblue: 'f0f8ff', antiquewhite: 'faebd7', aqua: '00ffff', aquamarine: '7fffd4', azure: 'f0ffff', beige: 'f5f5dc', bisque: 'ffe4c4', black: '000000', blanchedalmond: 'ffebcd', blue: '0000ff', blueviolet: '8a2be2', brown: 'a52a2a', burlywood: 'deb887', cadetblue: '5f9ea0', chartreuse: '7fff00', chocolate: 'd2691e', coral: 'ff7f50', cornflowerblue: '6495ed', cornsilk: 'fff8dc', crimson: 'dc143c', cyan: '00ffff', darkblue: '00008b', darkcyan: '008b8b', darkgoldenrod: 'b8860b', darkgray: 'a9a9a9', darkgreen: '006400', darkkhaki: 'bdb76b', darkmagenta: '8b008b', darkolivegreen: '556b2f', darkorange: 'ff8c00', darkorchid: '9932cc', darkred: '8b0000', darksalmon: 'e9967a', darkseagreen: '8fbc8f', darkslateblue: '483d8b', darkslategray: '2f4f4f', darkturquoise: '00ced1', darkviolet: '9400d3', deeppink: 'ff1493', deepskyblue: '00bfff', dimgray: '696969', dodgerblue: '1e90ff', feldspar: 'd19275', firebrick: 'b22222', floralwhite: 'fffaf0', forestgreen: '228b22', fuchsia: 'ff00ff', gainsboro: 'dcdcdc', ghostwhite: 'f8f8ff', gold: 'ffd700', goldenrod: 'daa520', gray: '808080', green: '008000', greenyellow: 'adff2f', honeydew: 'f0fff0', hotpink: 'ff69b4', indianred : 'cd5c5c', indigo : '4b0082', ivory: 'fffff0', khaki: 'f0e68c', lavender: 'e6e6fa', lavenderblush: 'fff0f5', lawngreen: '7cfc00', lemonchiffon: 'fffacd', lightblue: 'add8e6', lightcoral: 'f08080', lightcyan: 'e0ffff', lightgoldenrodyellow: 'fafad2', lightgrey: 'd3d3d3', lightgreen: '90ee90', lightpink: 'ffb6c1', lightsalmon: 'ffa07a', lightseagreen: '20b2aa', lightskyblue: '87cefa', lightslateblue: '8470ff', lightslategray: '778899', lightsteelblue: 'b0c4de', lightyellow: 'ffffe0', lime: '00ff00', limegreen: '32cd32', linen: 'faf0e6', magenta: 'ff00ff', maroon: '800000', mediumaquamarine: '66cdaa', mediumblue: '0000cd', mediumorchid: 'ba55d3', mediumpurple: '9370d8', mediumseagreen: '3cb371', mediumslateblue: '7b68ee', mediumspringgreen: '00fa9a', mediumturquoise: '48d1cc', mediumvioletred: 'c71585', midnightblue: '191970', mintcream: 'f5fffa', mistyrose: 'ffe4e1', moccasin: 'ffe4b5', navajowhite: 'ffdead', navy: '000080', oldlace: 'fdf5e6', olive: '808000', olivedrab: '6b8e23', orange: 'ffa500', orangered: 'ff4500', orchid: 'da70d6', palegoldenrod: 'eee8aa', palegreen: '98fb98', paleturquoise: 'afeeee', palevioletred: 'd87093', papayawhip: 'ffefd5', peachpuff: 'ffdab9', peru: 'cd853f', pink: 'ffc0cb', plum: 'dda0dd', powderblue: 'b0e0e6', purple: '800080', red: 'ff0000', rosybrown: 'bc8f8f', royalblue: '4169e1', saddlebrown: '8b4513', salmon: 'fa8072', sandybrown: 'f4a460', seagreen: '2e8b57', seashell: 'fff5ee', sienna: 'a0522d', silver: 'c0c0c0', skyblue: '87ceeb', slateblue: '6a5acd', slategray: '708090', snow: 'fffafa', springgreen: '00ff7f', steelblue: '4682b4', tan: 'd2b48c', teal: '008080', thistle: 'd8bfd8', tomato: 'ff6347', turquoise: '40e0d0', violet: 'ee82ee', violetred: 'd02090', wheat: 'f5deb3', white: 'ffffff', whitesmoke: 'f5f5f5', yellow: 'ffff00', yellowgreen: '9acd32' }; if(color_names[color]) color="#"+color_names[color]; if(color.substr(0,1)=="#"){ color=color.substr(1); if(color.length==6){ red=parseInt("0x"+color.substr(0,2))/255; green=parseInt("0x"+color.substr(2,2))/255; blue=parseInt("0x"+color.substr(4,2))/255; } else if(color.length==3){ red=parseInt("0x"+color.substr(0,1))/15; green=parseInt("0x"+color.substr(1,1))/15; blue=parseInt("0x"+color.substr(2,1))/15; } }else if(color.substr(0,4)=="rgb("){ var colors=color.substr(4).split(","); red=parseInt(colors[0])/255; green=parseInt(colors[1])/255; blue=parseInt(colors[2])/255; } else { red=0; green=0; blue=0; } return {r:red,g:green,b:blue}; } /** * @class Document class to load scene, object, mesh etc from an external XML file * @param {string} url URL of the resource to load */ GLGE.Document=function(url){ this.listeners=[]; this.documents=[]; this.rootURL=url; this.loadDocument(url,null); } GLGE.Document.prototype.listeners=null; GLGE.Document.prototype.documents=null; GLGE.Document.prototype.rootURL=null; GLGE.Document.prototype.loadCount=0; /** * This is just a fix for a bug in webkit * @param {string} id the id name to get * @returns {object} node with teh specified id * @private */ GLGE.Document.prototype.getElementById=function(id){ var tags=this.getElementsByTagName("*"); for(var i=0; i1){ frame=((parseFloat(now)-parseFloat(this.animationStart))/1000*this.frameRate)%(this.animation.frames-1)+1; }else{ frame=1; } if(frame!=this.lastFrame){ this.lastFrame=frame; for(property in this.animation.curves){ if(this["set"+property]) this["set"+property](this.animation.curves[property].getValue(parseFloat(frame))); } } } /** * Sets the animation vector of this object * @param {GLGE.AnimationVector} animationVector the animation to apply to this object */ GLGE.Animatable.prototype.setAnimation=function(animationVector){ this.animationStart=parseInt(new Date().getTime()); this.animation=animationVector; } /** * Gets the animation vector of this object * @returns {AnimationVector} */ GLGE.Animatable.prototype.getAnimation=function(){ return this.animation; } /** * Sets the frame rate of the animation * @param {number} value the frame rate to set */ GLGE.Animatable.prototype.setFrameRate=function(value){ this.frameRate=value; } /** * Gets the frame rate of the animation * @return {number} the current frame rate */ GLGE.Animatable.prototype.getFrameRate=function(){ return this.frameRate; } /** * @class A bezier class to add points to the Animation Curve * @param {number} x1 x-coord of the first control point * @param {number} y1 y-coord of the first control point * @param {number} x2 x-coord of the second control point * @param {number} y2 y-coord of the second control point * @param {number} x3 x-coord of the third control point * @param {number} y3 y-coord of the third control point */ GLGE.BezTriple=function(x1,y1,x2,y2,x3,y3){ this.x1=parseFloat(x1); this.y1=parseFloat(y1); this.x2=parseFloat(x2); this.y2=parseFloat(y2); this.x3=parseFloat(x3); this.y3=parseFloat(y3); }; /** * @class A curve which interpolates between control points */ GLGE.AnimationCurve=function(){ this.keyFrames=[]; this.solutions={}; }; GLGE.AnimationCurve.prototype.keyFrames=null; /** * Adds a point to the curve * @param {BezPoint} bezPoint The bezier point to add * @returns {Number} Index of the newly added point */ GLGE.AnimationCurve.prototype.addPoint=function(bezPoint){ this.keyFrames.push(bezPoint); return this.keyFrames.length-1; }; /** * Get the value of the curve at any point * @param {Number} frame The frame(x-coord) to return the value for * @returns {Number} The value of the curve at the given point */ GLGE.AnimationCurve.prototype.coord=function(x,y){ return {x:x,y:y} } GLGE.AnimationCurve.prototype.getValue=function(frame){ var startKey=0; var endKey=0; //find the key frame bounds for(var i=0; ithis.keyFrames[startKey].x2 || this.keyFrames[startKey].x2>frame)) startKey=i; if(this.keyFrames[i].x2>frame && (this.keyFrames[i].x2 0) { // one real, two complex s = r + Math.sqrt(discrim); s = ((s < 0) ? -Math.pow(-s, (1.0/3.0)) : Math.pow(s, (1.0/3.0))); t = r - Math.sqrt(discrim); t = ((t < 0) ? -Math.pow(-t, (1.0/3.0)) : Math.pow(t, (1.0/3.0))); result[0] = -t1 + s + t; t1 = t1 + (s + t)/2.0; result[1] = result[2] = -t1; t1 = Math.sqrt(3.0)*(-t + s)/2; } else if (discrim == 0){ // All roots real r13 = ((r < 0) ? -Math.pow(-r,(1.0/3.0)) : Math.pow(r,(1.0/3.0))); result[1] = -t1 + 2.0*r13; result[1] = result[2] = -(r13 + t1); } else { q = -q; d1 = q*q*q; d1 = Math.acos(r/Math.sqrt(1)); r13 = 2.0*Math.sqrt(q); result[0] = -t1 + r13*Math.cos(d1/3.0); result[1] = -t1 + r13*Math.cos((d1 + 2.0*Math.PI)/3.0); result[2] = -t1 + r13*Math.cos((d1 + 4.0*Math.PI)/3.0); } var toreturn=false; //determine which is the correct result if(result[0]>=0 && result[0]<=1) toreturn=result[0]; if(!toreturn && result[1]>=0 && result[1]<=1) toreturn=result[1]; if(!toreturn && result[2]>=0 && result[2]<=1) toreturn=result[2]; //cache result for next time this.solutions[ref]=toreturn; return toreturn; } }; /** * Get the value of the a single bezier curve * @param {Number} x xcoord of point to get * @param {Number} C1 First bezier control point * @param {Number} C2 Second bezier control point * @param {Number} C3 Third bezier control point * @param {Number} C4 Forth bezier control point * @returns {Number} The value of the curve at the given x */ GLGE.AnimationCurve.prototype.atX=function(x,C1,C2,C3,C4){ a=C1.x-C2.x*3+C3.x*3-C4.x; b=C2.x*3-C3.x*6+C4.x*3; c=C3.x*3-C4.x*3; d=C4.x-x; return this.getBezier(this.Quad3Solve(a,b,c,d),C1,C2,C3,C4); }; /** * @class The AnimationVectors class allows you to specify the 2D Animation curves that define specific channels of animation within the engine. */ GLGE.AnimationVector=function(){ this.curves=[]; } GLGE.AnimationVector.prototype.curves=[]; GLGE.AnimationVector.prototype.frames=250; /** * Adds an Animation Curve to a channel * @param {String} channel The name of the curve to be added * @param {GLGE.AnimationCurve} curve The animation curve to add */ GLGE.AnimationVector.prototype.addCurve=function(name,curve){ this.curves[name]=curve; } /** * Removes an Animation Curve form a channel * @param {String} channel The name of the curve to be removed */ GLGE.AnimationVector.prototype.removeCurve=function(name){ delete(this.curves[name]); } /** * Sets the number of frames in the animation * @param {number} value The number of frames in the animation */ GLGE.AnimationVector.prototype.setFrames=function(value){ this.frames=value; } /** * Sets the number of frames in the animation * @returns {number} The number of frames in the animation */ GLGE.AnimationVector.prototype.getFrames=function(){ return this.frames; } /** * Function to augment one object with another * @param {object} obj1 Source Object * @param {object} obj2 Destination Object */ GLGE.augment=function(obj1,obj2){ for(proto in obj1.prototype){ obj2.prototype[proto]=obj1.prototype[proto]; } } /** * @class Class defining a bones name and position in a skeleton * @param {string} name the name of the bone * @param {number} x the x co-ordinate of the bone * @param {number} y the y co-ordinate of the bone * @param {number} z the z co-ordinate of the bone */ GLGE.Bone=function(name,x,y,z){ this.name=name; this.x=x; this.y=y; this.z=z; } GLGE.error=function(message){ alert(message) } /** * @class Class specifing bones,locations and relationships */ GLGE.Skeleton=function(){ this.bones=[]; }; GLGE.Skeleton.prototype.bones=[]; /** * Adds a bone to the skeleton * @param {GLGE.Bone} bone the bone to add * NOTE why don't I just add the parent into the bone object???? */ GLGE.Skeleton.prototype.addBone=function(bone,parent){ if(parent) bone.parent=parent; this.bones.push(bone); } /** * Gets the transform of a bone for a given action at a specified frame * @param {string} bone the bone to get the matrix for * @param {GLGE.SkeletalAction} action the action to the the matrix for * @param {number} frame The frame number to get the matrix for * @returns Matrix */ GLGE.Skeleton.prototype.getBoneTransforms=function(bone,action,frame){ var TRANS1=Matrix.transMat(bone.x*-1,bone.y*-1,bone.z*-1); var TRANS2=Matrix.transMat(bone.x,bone.y,bone.z); var result=Matrix.I(4); if(action && action.cache[frame][bone.name]) result=TRANS2.x(action.cache[frame][bone.name]).x(TRANS1); return result; } /** * Gets the bone object from it's name * @param {string} name the name of the bone to get * @returns GLGE.Bone */ GLGE.Skeleton.prototype.boneFromName=function(name){ for(var i=0; i1){ frame=((now-this.actionStart)/1000*this.action.frameRate)%(this.action.frames-1)+1; }else{ frame=1; } frame=Math.round(frame); if(this.actionCache[frame]){ var transforms=this.actionCache[frame]; } else { var transforms=this.skeleton.getTransforms(this.action,frame); if(this.blendState){ var blendframe; var blendpoint=(now-this.actionStart)/this.blendDuration; if(blendpoint>=1){ this.blendState=null; this.actionCache=[]; } else { //combine the two sets of transforms var newtransforms={}; for(bone in transforms){ if(transforms[bone].matrix){ newtransforms[bone]={matrix:this.blendState[bone].matrix.x(1-blendpoint).add(transforms[bone].matrix.x(blendpoint))}; } } transforms=newtransforms; } } else { this.actionCache[frame]=transforms; } } return transforms; } /** * Sets the Z Transparency of this object * @param {boolean} value Does this object need blending? */ GLGE.Object.prototype.setZtransparent=function(value){ this.zTrans=value; } /** * Gets the z transparency * @returns boolean */ GLGE.Object.prototype.isZtransparent=function(){ return this.zTrans; } /** * Sets the skeletal action of this object * @param {GLGE.SkeletalAction} action The action to be blended to */ GLGE.Object.prototype.setSkeleton=function(skeleton){ this.skeleton=skeleton; } /** * Gets the current skeletal action of this object * @returns GLGE.SkeletalAction */ GLGE.Object.prototype.getSkeleton=function(){ return this.skeleton; } /** * Sets the material associated with the object * @param GLGE.Material */ GLGE.Object.prototype.setMaterial=function(material){ this.material=material; this.updateProgram(); } /** * Gets the material associated with the object * @returns GLGE.Material */ GLGE.Object.prototype.getMaterial=function(){ return this.material; } GLGE.Object.prototype.setScene=function(scene){ this.scene=scene; } GLGE.Object.prototype.getScene=function(){ return this.scene; } /** * Sets the mesh associated with the object * @param GLGE.Mesh */ GLGE.Object.prototype.setMesh=function(mesh){ if(this.mesh) this.mesh.removeObject(this); this.mesh=mesh; mesh.addObject(this); } /** * Gets the mesh associated with the object * @returns GLGE.Mesh */ GLGE.Object.prototype.getMesh=function(){ return this.mesh; } /** * Initiallize all the GL stuff needed to render to screen * @private */ GLGE.Object.prototype.GLInit=function(gl){ this.gl=gl; this.GLGenerateShader(gl); } /** * Cleans up all the GL stuff we sets * @private */ GLGE.Object.prototype.GLDestory=function(gl){ } /** * Updates the GL shader program for the object * @private */ GLGE.Object.prototype.updateProgram=function(){ if(this.gl) this.GLGenerateShader(this.gl); } /** * Creates the shader program for the object * @private */ GLGE.Object.prototype.GLGenerateShader=function(gl){ if(this.GLShaderProgram) gl.deleteProgram(this.GLShaderProgram); if(this.GLShaderProgramShadow) gl.deleteProgram(this.GLShaderProgramShadow); if(this.GLShaderProgramPick) gl.deleteProgram(this.GLShaderProgramPick); //create the programs strings //Vertex Shader var vertexStr=""; for(var i=0;i1) vertexStr=vertexStr+"attribute vec"+this.mesh.buffers[i].size+" "+this.mesh.buffers[i].name+";\n"; else vertexStr=vertexStr+"attribute float "+this.mesh.buffers[i].name+";\n"; } vertexStr=vertexStr+"uniform mat4 MVMatrix;\n"; vertexStr=vertexStr+"uniform mat4 PMatrix;\n"; //normals needed for lighting vertexStr=vertexStr+"uniform mat4 uNMatrix;\n"; for(var i=0; i0.0){\n"; cnt=0; for(var i=0; i-1){ gl.bindBuffer(gl.ARRAY_BUFFER, this.GLbuffers[this.buffers[i].name]); gl.enableVertexAttribArray(attribslot); gl.vertexAttribPointer(attribslot, this.GLbuffers[this.buffers[i].name].itemSize, gl.FLOAT, false, 0, 0); } } } /** * updates the programs for the objects when buffers are added/removed * @private */ GLGE.Mesh.prototype.updatePrograms=function(){ for(var i=0;i0){ return this.objects[index-1]; }else{ return false; } }else{ return false; } } /** * @class Sets the scene to render * @param {GLGE.Scene} scene The scene to be rendered */ GLGE.Renderer=function(canvas){ this.canvas=canvas; try { this.gl = canvas.getContext("experimental-webgl"); } catch(e) {} if (!this.gl) { alert("What, What Whaaat? No WebGL!"); return false; } //chome compatibility //TODO: Remove this when chome is right if (!this.gl.getProgramParameter) { this.gl.getProgramParameter = this.gl.getProgrami } if (!this.gl.getShaderParameter) { this.gl.getShaderParameter = this.gl.getShaderi } // End of Chrome compatibility code //set up defaults this.gl.clearDepth(1.0); this.gl.enable(this.gl.DEPTH_TEST); //this.gl.enable(this.gl.SAMPLE_ALPHA_TO_COVERAGE); this.gl.depthFunc(this.gl.LEQUAL); this.gl.blendFuncSeparate(this.gl.SRC_ALPHA,this.gl.ONE_MINUS_SRC_ALPHA,this.gl.ZERO,this.gl.ONE); this.gl.enable(this.gl.CULL_FACE); }; GLGE.Renderer.prototype.gl=null; GLGE.Renderer.prototype.scene=null; /** * Gets the scene which is set to be rendered * @returns the current render scene */ GLGE.Renderer.prototype.getScene=function(gcene){ return this.scene; }; /** * Sets the scene to render * @param {GLGE.Scene} scene The scene to be rendered */ GLGE.Renderer.prototype.setScene=function(scene){ scene.renderer=this; this.scene=scene; scene.init(); }; /** * Renders the current scene to the canvas */ GLGE.Renderer.prototype.render=function(){ this.scene.render(this.gl); }; /** * @class A texture to be included in a material * @param {string} url the url of the image to use as the texture * @see GLGE.Material */ GLGE.Texture=function(url){ this.image=new Image(); this.image.texture=this; this.image.onload = function(){ this.texture.state=1; } this.image.src=url; this.state=0; this.glTexture=null; } GLGE.Texture.prototype.image=null; GLGE.Texture.prototype.texture=null; GLGE.Texture.prototype.glTexture=null; /** * @class The material layer describes how to apply this layer to the material * @param {number} texture the texture index to apply to the layer * @param {number} mapto how to map this layer on to the material see M_XXXXX constants * @param {number} mapinput the UV layer to map to, UV1 or UV2 * @param {Object} scale how much scaling the texture eg {x: 10, y:10, z:1} * @param {Object} offset how much to offset the texture eg {x: 10, y:10, z:1} * @see GLGE.Material * @augments GLGE.Animatable */ GLGE.MaterialLayer=function(texture,mapto,mapinput,scale,offset){ this.texture=texture; this.mapinput=mapinput; this.uv=mapinput; this.blendeMode=GLGE.BL_MIX; this.mapto=mapto; if(scale){ this.setScaleX(scale.x); this.setScaleY(scale.y); this.setScaleZ(scale.z); } if(offset){ this.setOffsetX(offset.x); this.setOffsetY(offset.y); this.setOffsetZ(offset.z); } this.scale=scale; this.offset=offset; }; GLGE.augment(GLGE.Animatable,GLGE.MaterialLayer); GLGE.MaterialLayer.prototype.texture=null; GLGE.MaterialLayer.prototype.blendMode=null; GLGE.MaterialLayer.prototype.mapto=GLGE.M_COLOR; GLGE.MaterialLayer.prototype.mapinput=GLGE.UV1; GLGE.MaterialLayer.prototype.scaleX=1; GLGE.MaterialLayer.prototype.offsetX=0; GLGE.MaterialLayer.prototype.rotX=0; GLGE.MaterialLayer.prototype.scaleY=1; GLGE.MaterialLayer.prototype.offsetY=0; GLGE.MaterialLayer.prototype.rotY=0; GLGE.MaterialLayer.prototype.scaleZ=1; GLGE.MaterialLayer.prototype.offsetZ=0; GLGE.MaterialLayer.prototype.rotZ=0; GLGE.MaterialLayer.prototype.dScaleX=0; GLGE.MaterialLayer.prototype.dOffsetX=0; GLGE.MaterialLayer.prototype.dRotX=0; GLGE.MaterialLayer.prototype.dScaleY=0; GLGE.MaterialLayer.prototype.dOffsetY=0; GLGE.MaterialLayer.prototype.dRotY=0; GLGE.MaterialLayer.prototype.dScaleZ=0; GLGE.MaterialLayer.prototype.dOffsetZ=0; GLGE.MaterialLayer.prototype.dRotZ=0; GLGE.MaterialLayer.prototype.matrix=null; /** * Gets the textures used by the layer * @return {GLGE.Texture} The current shininess of the material */ GLGE.MaterialLayer.prototype.getMatrix=function(){ if(!this.matrix){ var offset=this.getOffset(); var scale=this.getScale(); var rotation=this.getRotation(); this.matrix=Matrix.transMat(offset.x,offset.y,offset.z).x(Matrix.scaleMat(scale.x,scale.y,scale.z).x(Matrix.rotMat(rotation.x,rotation.y,rotation.z))); } return this.matrix; }; /** * Sets the textures used by the layer * @param {GLGE.Texture} value the teture to associate with this layer */ GLGE.MaterialLayer.prototype.setTexture=function(value){ this.texture=value; }; /** * Gets the textures used by the layer * @return {GLGE.Texture} The current shininess of the material */ GLGE.MaterialLayer.prototype.getTexture=function(){ return this.texture; }; /** * Sets the flag for how this layer maps to the material * @param {Number} value the flags to set for this layer */ GLGE.MaterialLayer.prototype.setMapto=function(value){ this.mapto=value; }; /** * Gets the flag representing the way the layer maps to the material * @return {Number} The flags currently set for this layer */ GLGE.MaterialLayer.prototype.getMapto=function(){ return this.mapto; }; /** * Sets the texture coordinate system * @param {Number} value the mapping to use */ GLGE.MaterialLayer.prototype.setMapinput=function(value){ this.mapinput=value; }; /** * Gets the texture coordinate system * @return {Number} The current mapping */ GLGE.MaterialLayer.prototype.getMapinput=function(){ return this.mapinput; }; /** * Gets the layers texture offset * @return {object} the current offset */ GLGE.MaterialLayer.prototype.getOffset=function(){ var offset={}; offset.x=parseFloat(this.getOffsetX())+parseFloat(this.getDOffsetX()); offset.y=parseFloat(this.getOffsetY())+parseFloat(this.getDOffsetY()); offset.z=parseFloat(this.getOffsetZ())+parseFloat(this.getDOffsetZ()); return offset; }; /** * Gets the layers texture rotation * @return {object} the current rotation */ GLGE.MaterialLayer.prototype.getRotation=function(){ var rotation={}; rotation.x=parseFloat(this.getRotX())+parseFloat(this.getDRotX()); rotation.y=parseFloat(this.getRotY())+parseFloat(this.getDRotY()); rotation.z=parseFloat(this.getRotZ())+parseFloat(this.getDRotZ()); return rotation; }; /** * Gets the layers texture scale * @return {object} the current scale */ GLGE.MaterialLayer.prototype.getScale=function(){ var scale={}; scale.x=parseFloat(this.getScaleX())+parseFloat(this.getDScaleX()); scale.y=parseFloat(this.getScaleY())+parseFloat(this.getDScaleY()); scale.z=parseFloat(this.getScaleZ())+parseFloat(this.getDScaleZ()); return scale; }; /** * Sets the layers texture X offset * @param {Number} value the amount to offset the texture */ GLGE.MaterialLayer.prototype.setOffsetX=function(value){ this.matrix=null; this.offsetX=value; }; /** * Gets the layers texture X offset * @return {Number} the current offset */ GLGE.MaterialLayer.prototype.getOffsetX=function(){ return this.offsetX; }; /** * Sets the layers texture Y offset * @param {Number} value the amount to offset the texture */ GLGE.MaterialLayer.prototype.setOffsetY=function(value){ this.matrix=null; this.offsetY=value; }; /** * Gets the layers texture Y offset * @return {Number} the current offset */ GLGE.MaterialLayer.prototype.getOffsetY=function(){ return this.offsetY; }; /** * Sets the layers texture Z offset * @param {Number} value the amount to offset the texture */ GLGE.MaterialLayer.prototype.setOffsetZ=function(value){ this.matrix=null; this.offsetZ=value; }; /** * Gets the layers texture Z offset * @return {Number} the current offset */ GLGE.MaterialLayer.prototype.getOffsetZ=function(){ return this.offsetZ; }; /** * Sets the layers texture X displacment offset, useful for animation * @param {Number} value the amount to offset the texture */ GLGE.MaterialLayer.prototype.setDOffsetX=function(value){ this.matrix=null; this.dOffsetX=value; }; /** * Gets the layers texture X displacment offset, useful for animation * @return {Number} the current offset */ GLGE.MaterialLayer.prototype.getDOffsetX=function(){ return this.dOffsetX; }; /** * Sets the layers texture Y displacment offset, useful for animation * @param {Number} value the amount to offset the texture */ GLGE.MaterialLayer.prototype.setDOffsetY=function(value){ this.matrix=null; this.dOffsetY=value; }; /** * Gets the layers texture Y displacment offset, useful for animation * @return {Number} the current offset */ GLGE.MaterialLayer.prototype.getDOffsetY=function(){ return this.dOffsetY; }; /** * Sets the layers texture Z displacment offset, useful for animation * @param {Number} value the amount to offset the texture */ GLGE.MaterialLayer.prototype.setDOffsetZ=function(value){ this.matrix=null; this.dOffsetZ=value; }; /** * Gets the layers texture X displacment offset, useful for animation * @return {Number} the current offset */ GLGE.MaterialLayer.prototype.getDOffsetZ=function(){ return this.dOffsetZ; }; /** * Sets the layers texture X scale * @param {Number} value the amount to scale the texture */ GLGE.MaterialLayer.prototype.setScaleX=function(value){ this.matrix=null; this.scaleX=value; }; /** * Gets the layers texture X scale * @return {Number} the current scale */ GLGE.MaterialLayer.prototype.getScaleX=function(){ return this.scaleX; }; /** * Sets the layers texture Y scale * @param {Number} value the amount to scale the texture */ GLGE.MaterialLayer.prototype.setScaleY=function(value){ this.matrix=null; this.scaleY=value; }; /** * Gets the layers texture Y scale * @return {Number} the current scale */ GLGE.MaterialLayer.prototype.getScaleY=function(){ return this.scaleY; }; /** * Sets the layers texture Z scale * @param {Number} value the amount to scale the texture */ GLGE.MaterialLayer.prototype.setScaleZ=function(value){ this.matrix=null; this.scaleZ=value; }; /** * Gets the layers texture Z offset * @return {Number} the current offset */ GLGE.MaterialLayer.prototype.getScaleZ=function(){ return this.scaleZ; }; /** * Sets the layers texture X displacment scale, useful for animation * @param {Number} value the amount to scale the texture */ GLGE.MaterialLayer.prototype.setDScaleX=function(value){ this.matrix=null; this.dScaleX=value; }; /** * Gets the layers texture X displacment scale, useful for animation * @return {Number} the current scale */ GLGE.MaterialLayer.prototype.getDScaleX=function(){ return this.dScaleX; }; /** * Sets the layers texture Y displacment scale, useful for animation * @param {Number} value the amount to scale the texture */ GLGE.MaterialLayer.prototype.setDScaleY=function(value){ this.matrix=null; this.dScaleY=value; }; /** * Gets the layers texture Y displacment scale, useful for animation * @return {Number} the current scale */ GLGE.MaterialLayer.prototype.getDScaleY=function(){ return this.dScaleY; }; /** * Sets the layers texture Z displacment scale, useful for animation * @param {Number} value the amount to scale the texture */ GLGE.MaterialLayer.prototype.setDScaleZ=function(value){ this.matrix=null; this.dScaleZ=value; }; /** * Gets the layers texture X displacment scale, useful for animation * @return {Number} the current scale */ GLGE.MaterialLayer.prototype.getDScaleZ=function(){ return this.dScaleZ; }; /** * Sets the layers texture X Rotation * @param {Number} value the amount to roate the texture */ GLGE.MaterialLayer.prototype.setRotX=function(value){ this.matrix=null; this.rotX=value; }; /** * Gets the layers texture X rotate * @return {Number} the current rotate */ GLGE.MaterialLayer.prototype.getRotX=function(){ return this.rotX; }; /** * Sets the layers texture Y rotate * @param {Number} value the amount to rotate the texture */ GLGE.MaterialLayer.prototype.setRotY=function(value){ this.matrix=null; this.rotY=value; }; /** * Gets the layers texture Y rotate * @return {Number} the current rotate */ GLGE.MaterialLayer.prototype.getRotY=function(){ return this.rotY; }; /** * Sets the layers texture Z rotate * @param {Number} value the amount to rotate the texture */ GLGE.MaterialLayer.prototype.setRotZ=function(value){ this.matrix=null; this.rotZ=value; }; /** * Gets the layers texture Z rotate * @return {Number} the current rotate */ GLGE.MaterialLayer.prototype.getRotZ=function(){ return this.rotZ; }; /** * Sets the layers texture X displacment rotation, useful for animation * @param {Number} value the amount to rotation the texture */ GLGE.MaterialLayer.prototype.setDRotX=function(value){ this.matrix=null; this.dRotX=value; }; /** * Gets the layers texture X displacment rotation, useful for animation * @return {Number} the current rotation */ GLGE.MaterialLayer.prototype.getDRotX=function(){ return this.dRotX; }; /** * Sets the layers texture Y displacment rotation, useful for animation * @param {Number} value the amount to rotaion the texture */ GLGE.MaterialLayer.prototype.setDRotY=function(value){ this.matrix=null; this.dRotY=value; }; /** * Gets the layers texture Y displacment rotation, useful for animation * @return {Number} the current rotation */ GLGE.MaterialLayer.prototype.getDRotY=function(){ return this.dRotY; }; /** * Sets the layers texture Z displacment rotation, useful for animation * @param {Number} value the amount to rotation the texture */ GLGE.MaterialLayer.prototype.setDRotZ=function(value){ this.matrix=null; this.dRotZ=value; }; /** * Gets the layers texture X displacment rotation, useful for animation * @return {Number} the current rotation */ GLGE.MaterialLayer.prototype.getDRotZ=function(){ return this.dRotZ; }; /** * Sets the layers blending mode * @param {Number} value the amount to rotation the texture */ GLGE.MaterialLayer.prototype.setBlendMode=function(value){ this.blendMode=value; }; /** * Gets the layers texture X displacment rotation, useful for animation * @return {Number} the current rotation */ GLGE.MaterialLayer.prototype.getBlendMode=function(){ return this.blendMode; }; /** * @class The Material class creates materials to be applied to objects in the graphics engine * @see GLGE.Object * @augments GLGE.Animatable */ GLGE.Material=function(){ this.layers=[]; this.textures=[]; this.lights=[]; this.color={r:1,g:1,b:1,a:1}; this.specColor={r:1,g:1,b:1}; this.reflect=0.8; this.shine=50; this.specular=1; this.emit=0; this.alpha=1; }; GLGE.augment(GLGE.Animatable,GLGE.Material); /** * @constant * @description Flag for material colour */ GLGE.M_COLOR=1; /** * @constant * @description Flag for material normal */ GLGE.M_NOR=2; /** * @constant * @description Flag for material alpha */ GLGE.M_ALPHA=4; /** * @constant * @description Flag for material specular color */ GLGE.M_SPECCOLOR=8; /** * @constant * @description Flag for material specular cvalue */ GLGE.M_SPECULAR=16; /** * @constant * @description Flag for material shineiness */ GLGE.M_SHINE=32; /** * @constant * @description Flag for material reflectivity */ GLGE.M_REFLECT=64; /** * @constant * @description Flag for material emision */ GLGE.M_EMIT=128; /** * @constant * @description Flag for material alpha */ GLGE.M_ALPHA=256; /** * @constant * @description Flag for masking with textures red value */ GLGE.M_MSKR=512; /** * @constant * @description Flag for masking with textures green value */ GLGE.M_MSKG=1024; /** * @constant * @description Flag for masking with textures blue value */ GLGE.M_MSKB=2048; /** * @constant * @description Flag for masking with textures alpha value */ GLGE.M_MSKA=4096; /** * @constant * @description Enumeration for first UV layer */ GLGE.UV1=0; /** * @constant * @description Enumeration for second UV layer */ GLGE.UV2=1; /** * @constant * @description Enumeration for second normal texture coords */ GLGE.MAP_NORM=3; /** * @constant * @description Enumeration for second object texture coords */ GLGE.MAP_OBJ=4; /** * @constant * @description Enumeration for mix blending mode */ GLGE.BL_MIX=0; /** * @constant * @description Enumeration for mix blending mode */ GLGE.BL_MUL=1; GLGE.Material.prototype.layers=null; GLGE.Material.prototype.textures=null; GLGE.Material.prototype.color=null; GLGE.Material.prototype.specColor=null; GLGE.Material.prototype.specular=null; GLGE.Material.prototype.emit=null; GLGE.Material.prototype.shine=null; GLGE.Material.prototype.reflect=null; GLGE.Material.prototype.lights=null; GLGE.Material.prototype.alpha=null; GLGE.Material.prototype.shadow=true; /** * Sets the flag indicateing the material should or shouldn't recieve shadows * @param {boolean} value The recieving shadow flag */ GLGE.Material.prototype.setShadow=function(value){ this.shadow=value }; /** * gets the show flag * @returns {boolean} The shadow flag */ GLGE.Material.prototype.getShadow=function(value){ return this.shadow; }; /** * Sets the base colour of the material * @param {string} color The colour of the material */ GLGE.Material.prototype.setColor=function(color){ color=GLGE.colorParse(color); this.color={r:color.r,g:color.g,b:color.b}; }; /** * Sets the red base colour of the material * @param {Number} r The new red level 0-1 */ GLGE.Material.prototype.setColorR=function(value){ this.color.r=value; }; /** * Sets the green base colour of the material * @param {Number} g The new green level 0-1 */ GLGE.Material.prototype.setColorG=function(value){ this.color.g=value; }; /** * Sets the blue base colour of the material * @param {Number} b The new blue level 0-1 */ GLGE.Material.prototype.setColorB=function(value){ this.color.b=value; }; /** * Gets the current base color of the material * @return {[r,g,b]} The current base color */ GLGE.Material.prototype.getColor=function(){ return this.color; }; /** * Sets the base specular colour of the material * @param {string} color The new specular colour */ GLGE.Material.prototype.setSpecularColor=function(color){ color=GLGE.colorParse(color); this.specColor={r:color.r,g:color.g,b:color.b}; }; /** * Gets the current base specular color of the material * @return {[r,g,b]} The current base specular color */ GLGE.Material.prototype.getSpecularColor=function(){ return this.specColor; }; /** * Sets the alpha of the material * @param {Number} value how much alpha */ GLGE.Material.prototype.setAlpha=function(value){ this.alpha=value; }; /** * Gets the alpha of the material * @return {Number} The current alpha of the material */ GLGE.Material.prototype.getAlpha=function(){ return this.alpha; }; /** * Sets the specular of the material * @param {Number} value how much specular */ GLGE.Material.prototype.setSpecular=function(value){ this.specular=value; }; /** * Gets the specular of the material * @return {Number} The current specular of the material */ GLGE.Material.prototype.getSpecular=function(){ return this.specular; }; /** * Sets the shininess of the material * @param {Number} value how much shine */ GLGE.Material.prototype.setShininess=function(value){ this.shine=value; }; /** * Gets the shininess of the material * @return {Number} The current shininess of the material */ GLGE.Material.prototype.getShininess=function(){ return this.shine; }; /** * Sets how much the material should emit * @param {Number} value how much to emit (0-1) */ GLGE.Material.prototype.setEmit=function(value){ this.emit=value; }; /** * Gets the amount this material emits * @return {Number} The emit value for the material */ GLGE.Material.prototype.getEmit=function(){ return this.emit; }; /** * Sets reflectivity of the material * @param {Number} value how much to reflect (0-1) */ GLGE.Material.prototype.setReflectivity=function(value){ this.reflect=value; }; /** * Gets the materials reflectivity * @return {Number} The reflectivity of the material */ GLGE.Material.prototype.getReflectivity=function(){ return this.reflect; }; /** * Add a new layer to the material * @param {MaterialLayer} layer The material layer to add to the material * @returns {Number} index of the added layer */ GLGE.Material.prototype.addMaterialLayer=function(layer){ this.layers.push(layer); return this.layers.length-1; }; /** * Gets all the materials layers * @returns {GLGE.MaterialLayer[]} all of the layers contained within this material */ GLGE.Material.prototype.getLayers=function(){ return this.layers; }; /** * Generate the fragment shader program for this material * @private */ GLGE.Material.prototype.getFragmentShader=function(lights){ var shader=""; for(var i=0; i0.0){\n"; shader=shader+"att = 1.0 / (lightAttenuation"+i+"[0] + lightAttenuation"+i+"[1] * lightdist"+i+" + lightAttenuation"+i+"[2] * lightdist"+i+" * lightdist"+i+");\n"; if(lights[i].diffuse){ shader=shader+"lightvalue += att * dotN * lightcolor"+i+";\n"; } if(lights[i].specular){ shader=shader+"specvalue += att * specC * lightcolor"+i+" * spec * pow(max(dot(normal,normalize(eyevec)),0.0), sh);\n"; } shader=shader+"}\n"; } shader=shader+"spotEffect = 0.0;\n"; if(lights[i].type==GLGE.L_SPOT){ shader=shader+"spotEffect = dot(normalize(lightdir"+i+"), normalize(-lightvec"+i+"));"; shader=shader+"if (spotEffect > spotCosCutOff"+i+") {\n"; shader=shader+"spotEffect = pow(spotEffect, spotExp"+i+");"; //spot shadow stuff if(lights[i].getCastShadows() && this.shadow){ shader=shader+"if(castshadows"+i+"){\n"; shader=shader+"spotCoords=lightmat"+i+"*vec4(OBJCoord,1.0);"; shader=shader+"spotCoords=(vec4(spotCoords.xy/(spotCoords.z*tan(acos(spotCosCutOff"+i+"))),0.0,1.0)/2.0)-0.5;"; shader=shader+"vec4 dist = texture2D(TEXTURE"+shadowlights[i]+", -spotCoords.xy);"; shader=shader+"float depth = (dist.r*65536.0+dist.g*256.0+dist.b)/65536.0;"; shader=shader+"if(depth*(1000.0+shadowbias"+i+")0.0){\n"; shader=shader+"att = spotEffect / (lightAttenuation"+i+"[0] + lightAttenuation"+i+"[1] * lightdist"+i+" + lightAttenuation"+i+"[2] * lightdist"+i+" * lightdist"+i+");\n"; if(lights[i].diffuse){ shader=shader+"lightvalue += att * dotN * lightcolor"+i+";\n"; } if(lights[i].specular){ shader=shader+"specvalue += att * specC * lightcolor"+i+" * spec * pow(max(dot(normal,normalize(eyevec)),0.0), sh);\n"; } shader=shader+"}\n}\n"; } if(lights[i].type==GLGE.L_DIR){ shader=shader+"dotN=max(dot(normal,normalize(-lightdir"+i+")),0.0);\n"; if(lights[i].diffuse){ shader=shader+"lightvalue += dotN * lightcolor"+i+";\n"; } if(lights[i].specular){ shader=shader+"specvalue += specC * lightcolor"+i+" * spec * pow(max(dot(normal,normalize(eyevec)),0.0), sh);\n"; } } } shader=shader+"lightvalue *= ref;\n" shader=shader+"if(al<0.01){gl_FragDepth=1.0; al=max(al-0.5,0.0);}else gl_FragDepth=min(eyevec.z/1000.0,1.0);\n"; shader=shader+"gl_FragColor = vec4(specvalue,0.0)+vec4(color.r*em+(color.r*lightvalue.r*(1.0-em)),color.g*em+(color.g*lightvalue.g*(1.0-em)),color.b*em+(color.b*lightvalue.b*(1.0-em)),al);\n"; shader=shader+"}\n"; return shader; }; /** * Set the uniforms needed to render this material * @private */ GLGE.Material.prototype.textureUniforms=function(gl,shaderProgram,lights){ if(this.animation) this.animate(); gl.uniform4f(gl.getUniformLocation(shaderProgram, "baseColor"), this.color.r,this.color.g,this.color.b,this.color.a); gl.uniform3f(gl.getUniformLocation(shaderProgram, "specColor"), this.specColor.r,this.specColor.g,this.specColor.b); gl.uniform1f(gl.getUniformLocation(shaderProgram, "specular"), this.specular); gl.uniform1f(gl.getUniformLocation(shaderProgram, "shine"), this.shine); gl.uniform1f(gl.getUniformLocation(shaderProgram, "reflect"), this.reflect); gl.uniform1f(gl.getUniformLocation(shaderProgram, "emit"), this.emit); gl.uniform1f(gl.getUniformLocation(shaderProgram, "alpha"), this.alpha); var cnt=0; var num=0; for(var i=0; i