diff --git a/build/Three.js b/build/Three.js index 19803f9ad6106ff82eaddae38a30fde295ee2a3a..c99a5475dfc04e8b308d714388396eecd10c616b 100755 --- a/build/Three.js +++ b/build/Three.js @@ -1,576 +1,23872 @@ // Three.js r41/ROME - http://github.com/mrdoob/three.js -var THREE=THREE||{};if(!window.Int32Array)window.Int32Array=Array,window.Float32Array=Array;THREE.Color=function(b){this.setHex(b)}; -THREE.Color.prototype={copy:function(b){this.r=b.r;this.g=b.g;this.b=b.b;this.hex=b.hex},setHex:function(b){this.hex=~~b&16777215;this.updateRGB()},setRGB:function(b,c,d){this.r=b;this.g=c;this.b=d;this.updateHex()},setHSV:function(b,c,d){var e,f,g,j,k,m;if(d==0)e=f=g=0;else switch(j=Math.floor(b*6),k=b*6-j,b=d*(1-c),m=d*(1-c*k),c=d*(1-c*(1-k)),j){case 1:e=m;f=d;g=b;break;case 2:e=b;f=d;g=c;break;case 3:e=b;f=m;g=d;break;case 4:e=c;f=b;g=d;break;case 5:e=d;f=b;g=m;break;case 6:case 0:e=d,f=c,g=b}this.setRGB(e, -f,g)},updateHex:function(){this.hex=~~(this.r*255)<<16^~~(this.g*255)<<8^~~(this.b*255)},updateRGB:function(){this.r=(this.hex>>16&255)/255;this.g=(this.hex>>8&255)/255;this.b=(this.hex&255)/255},clone:function(){return new THREE.Color(this.hex)}};THREE.Vector2=function(b,c){this.set(b||0,c||0)}; -THREE.Vector2.prototype={set:function(b,c){this.x=b;this.y=c;return this},copy:function(b){this.x=b.x;this.y=b.y;return this},clone:function(){return new THREE.Vector2(this.x,this.y)},add:function(b,c){this.x=b.x+c.x;this.y=b.y+c.y;return this},addSelf:function(b){this.x+=b.x;this.y+=b.y;return this},sub:function(b,c){this.x=b.x-c.x;this.y=b.y-c.y;return this},subSelf:function(b){this.x-=b.x;this.y-=b.y;return this},multiplyScalar:function(b){this.x*=b;this.y*=b;return this},divideScalar:function(b){b? -(this.x/=b,this.y/=b):this.set(0,0);return this},negate:function(){return this.multiplyScalar(-1)},dot:function(b){return this.x*b.x+this.y*b.y},lengthSq:function(){return this.x*this.x+this.y*this.y},length:function(){return Math.sqrt(this.lengthSq())},normalize:function(){return this.divideScalar(this.length())},distanceTo:function(b){return Math.sqrt(this.distanceToSquared(b))},distanceToSquared:function(b){var c=this.x-b.x,b=this.y-b.y;return c*c+b*b},setLength:function(b){return this.normalize().multiplyScalar(b)}, -unit:function(){return this.normalize()},equals:function(b){return b.x==this.x&&b.y==this.y}};THREE.Vector3=function(b,c,d){this.set(b||0,c||0,d||0)}; -THREE.Vector3.prototype={set:function(b,c,d){this.x=b;this.y=c;this.z=d;return this},copy:function(b){this.x=b.x;this.y=b.y;this.z=b.z;return this},clone:function(){return new THREE.Vector3(this.x,this.y,this.z)},add:function(b,c){this.x=b.x+c.x;this.y=b.y+c.y;this.z=b.z+c.z;return this},addSelf:function(b){this.x+=b.x;this.y+=b.y;this.z+=b.z;return this},addScalar:function(b){this.x+=b;this.y+=b;this.z+=b;return this},sub:function(b,c){this.x=b.x-c.x;this.y=b.y-c.y;this.z=b.z-c.z;return this},subSelf:function(b){this.x-= -b.x;this.y-=b.y;this.z-=b.z;return this},multiply:function(b,c){this.x=b.x*c.x;this.y=b.y*c.y;this.z=b.z*c.z;return this},multiplySelf:function(b){this.x*=b.x;this.y*=b.y;this.z*=b.y;return this},multiplyScalar:function(b){this.x*=b;this.y*=b;this.z*=b;return this},divideSelf:function(b){return this.divide(this,b)},divideScalar:function(b){b?(this.x/=b,this.y/=b,this.z/=b):this.set(0,0,0);return this},negate:function(){return this.multiplyScalar(-1)},dot:function(b){return this.x*b.x+this.y*b.y+this.z* -b.z},lengthSq:function(){return this.x*this.x+this.y*this.y+this.z*this.z},length:function(){return Math.sqrt(this.lengthSq())},lengthManhattan:function(){return this.x+this.y+this.z},normalize:function(){return this.divideScalar(this.length())},setLength:function(b){return this.normalize().multiplyScalar(b)},cross:function(b,c){this.x=b.y*c.z-b.z*c.y;this.y=b.z*c.x-b.x*c.z;this.z=b.x*c.y-b.y*c.x;return this},crossSelf:function(b){return this.set(this.y*b.z-this.z*b.y,this.z*b.x-this.x*b.z,this.x* -b.y-this.y*b.x)},distanceTo:function(b){return Math.sqrt(this.distanceToSquared(b))},distanceToSquared:function(b){return(new THREE.Vector3).sub(this,b).lengthSq()},setPositionFromMatrix:function(b){this.x=b.n14;this.y=b.n24;this.z=b.n34},setRotationFromMatrix:function(b){var c=Math.cos(this.y);this.y=Math.asin(b.n13);Math.abs(c)>1.0E-5?(this.x=Math.atan2(-b.n23/c,b.n33/c),this.z=Math.atan2(-b.n12/c,b.n11/c)):(this.x=0,this.z=Math.atan2(b.n21,b.n22))},isZero:function(){return this.lengthSq()<1.0E-4}}; -THREE.Vector4=function(b,c,d,e){this.set(b||0,c||0,d||0,e||1)}; -THREE.Vector4.prototype={set:function(b,c,d,e){this.x=b;this.y=c;this.z=d;this.w=e;return this},copy:function(b){return this.set(b.x,b.y,b.z,b.w||1)},clone:function(){return new THREE.Vector4(this.x,this.y,this.z,this.w)},add:function(b,c){this.x=b.x+c.x;this.y=b.y+c.y;this.z=b.z+c.z;this.w=b.w+c.w;return this},addSelf:function(b){this.x+=b.x;this.y+=b.y;this.z+=b.z;this.w+=b.w;return this},sub:function(b,c){this.x=b.x-c.x;this.y=b.y-c.y;this.z=b.z-c.z;this.w=b.w-c.w;return this},subSelf:function(b){this.x-= -b.x;this.y-=b.y;this.z-=b.z;this.w-=b.w;return this},multiplyScalar:function(b){this.x*=b;this.y*=b;this.z*=b;this.w*=b;return this},divideScalar:function(b){b?(this.x/=b,this.y/=b,this.z/=b,this.w/=b):this.set(0,0,0,1);return this},negate:function(){return this.multiplyScalar(-1)},dot:function(b){return this.x*b.x+this.y*b.y+this.z*b.z+this.w*b.w},lengthSq:function(){return this.dot(this)},length:function(){return Math.sqrt(this.lengthSq())},normalize:function(){return this.divideScalar(this.length())}, -setLength:function(b){return this.normalize().multiplyScalar(b)},lerpSelf:function(b,c){this.x+=(b.x-this.x)*c;this.y+=(b.y-this.y)*c;this.z+=(b.z-this.z)*c;this.w+=(b.w-this.w)*c;return this}};THREE.Ray=function(b,c){this.origin=b||new THREE.Vector3;this.direction=c||new THREE.Vector3}; -THREE.Ray.prototype={intersectScene:function(b){return this.intersectObjects(b.objects)},intersectObjects:function(b){var c,d,e=[];c=0;for(d=b.length;c0&&b>0&&g+b<1}if(b instanceof THREE.Particle){var e=c(this.origin,this.direction,b);if(!e||e>b.scale.x)return[];return[{distance:e,point:b.position,face:null,object:b}]}else if(b instanceof THREE.Mesh){e=c(this.origin,this.direction,b);if(!e||e>b.geometry.boundingSphere.radius*Math.max(b.scale.x,Math.max(b.scale.y,b.scale.z)))return[];var f,g,j,k,m,o,n,t,p,u,v=b.geometry, -y=v.vertices,B=[],e=0;for(f=v.faces.length;e0:t<0))if(n=n.dot((new THREE.Vector3).sub(j,p))/t,p=p.addSelf(u.multiplyScalar(n)), -g instanceof THREE.Face3)d(p,j,k,m)&&(g={distance:this.origin.distanceTo(p),point:p,face:g,object:b},B.push(g));else if(g instanceof THREE.Face4&&(d(p,j,k,o)||d(p,k,m,o)))g={distance:this.origin.distanceTo(p),point:p,face:g,object:b},B.push(g);return B}else return[]}}; -THREE.Rectangle=function(){function b(){g=e-c;j=f-d}var c,d,e,f,g,j,k=!0;this.getX=function(){return c};this.getY=function(){return d};this.getWidth=function(){return g};this.getHeight=function(){return j};this.getLeft=function(){return c};this.getTop=function(){return d};this.getRight=function(){return e};this.getBottom=function(){return f};this.set=function(g,j,n,t){k=!1;c=g;d=j;e=n;f=t;b()};this.addPoint=function(g,j){k?(k=!1,c=g,d=j,e=g,f=j):(c=cg?e:g,f=f>j?f:j);b()};this.add3Points= -function(g,j,n,t,p,u){k?(k=!1,c=gn?g>p?g:p:n>p?n:p,f=j>t?j>u?j:u:t>u?t:u):(c=gn?g>p?g>e?g:e:p>e?p:e:n>p?n>e?n:e:p>e?p:e,f=j>t?j>u?j>f?j:f:u>f?u:f:t>u?t>f?t:f:u>f?u:f);b()};this.addRectangle=function(g){k?(k=!1,c=g.getLeft(),d=g.getTop(),e=g.getRight(),f=g.getBottom()):(c=cg.getRight()?e:g.getRight(),f=f> -g.getBottom()?f:g.getBottom());b()};this.inflate=function(g){c-=g;d-=g;e+=g;f+=g;b()};this.minSelf=function(g){c=c>g.getLeft()?c:g.getLeft();d=d>g.getTop()?d:g.getTop();e=e=0&&Math.min(f,b.getBottom())-Math.max(d,b.getTop())>=0};this.empty=function(){k=!0;f=e=d=c=0;b()};this.isEmpty=function(){return k}};THREE.Matrix3=function(){this.m=[]}; -THREE.Matrix3.prototype={transpose:function(){var b,c=this.m;b=c[1];c[1]=c[3];c[3]=b;b=c[2];c[2]=c[6];c[6]=b;b=c[5];c[5]=c[7];c[7]=b;return this},transposeIntoArray:function(b){var c=this.m;b[0]=c[0];b[1]=c[3];b[2]=c[6];b[3]=c[1];b[4]=c[4];b[5]=c[7];b[6]=c[2];b[7]=c[5];b[8]=c[8];return this}};THREE.Matrix4=function(b,c,d,e,f,g,j,k,m,o,n,t,p,u,v,y){this.set(b||1,c||0,d||0,e||0,f||0,g||1,j||0,k||0,m||0,o||0,n||1,t||0,p||0,u||0,v||0,y||1);this.flat=Array(16);this.m33=new THREE.Matrix3}; -THREE.Matrix4.prototype={set:function(b,c,d,e,f,g,j,k,m,o,n,t,p,u,v,y){this.n11=b;this.n12=c;this.n13=d;this.n14=e;this.n21=f;this.n22=g;this.n23=j;this.n24=k;this.n31=m;this.n32=o;this.n33=n;this.n34=t;this.n41=p;this.n42=u;this.n43=v;this.n44=y;return this},identity:function(){this.set(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1);return this},copy:function(b){this.set(b.n11,b.n12,b.n13,b.n14,b.n21,b.n22,b.n23,b.n24,b.n31,b.n32,b.n33,b.n34,b.n41,b.n42,b.n43,b.n44);return this},lookAt:function(b,c,d){var e=THREE.Matrix4.__v1, -f=THREE.Matrix4.__v2,g=THREE.Matrix4.__v3;g.sub(b,c).normalize();if(g.length()===0)g.z=1;e.cross(d,g).normalize();e.length()===0&&(g.x+=1.0E-4,e.cross(d,g).normalize());f.cross(g,e).normalize();this.n11=e.x;this.n12=f.x;this.n13=g.x;this.n21=e.y;this.n22=f.y;this.n23=g.y;this.n31=e.z;this.n32=f.z;this.n33=g.z;return this},multiplyVector3:function(b){var c=b.x,d=b.y,e=b.z,f=1/(this.n41*c+this.n42*d+this.n43*e+this.n44);b.x=(this.n11*c+this.n12*d+this.n13*e+this.n14)*f;b.y=(this.n21*c+this.n22*d+this.n23* -e+this.n24)*f;b.z=(this.n31*c+this.n32*d+this.n33*e+this.n34)*f;return b},multiplyVector4:function(b){var c=b.x,d=b.y,e=b.z,f=b.w;b.x=this.n11*c+this.n12*d+this.n13*e+this.n14*f;b.y=this.n21*c+this.n22*d+this.n23*e+this.n24*f;b.z=this.n31*c+this.n32*d+this.n33*e+this.n34*f;b.w=this.n41*c+this.n42*d+this.n43*e+this.n44*f;return b},rotateAxis:function(b){var c=b.x,d=b.y,e=b.z;b.x=c*this.n11+d*this.n12+e*this.n13;b.y=c*this.n21+d*this.n22+e*this.n23;b.z=c*this.n31+d*this.n32+e*this.n33;b.normalize(); -return b},crossVector:function(b){var c=new THREE.Vector4;c.x=this.n11*b.x+this.n12*b.y+this.n13*b.z+this.n14*b.w;c.y=this.n21*b.x+this.n22*b.y+this.n23*b.z+this.n24*b.w;c.z=this.n31*b.x+this.n32*b.y+this.n33*b.z+this.n34*b.w;c.w=b.w?this.n41*b.x+this.n42*b.y+this.n43*b.z+this.n44*b.w:1;return c},multiply:function(b,c){var d=b.n11,e=b.n12,f=b.n13,g=b.n14,j=b.n21,k=b.n22,m=b.n23,o=b.n24,n=b.n31,t=b.n32,p=b.n33,u=b.n34,v=b.n41,y=b.n42,B=b.n43,z=b.n44,I=c.n11,A=c.n12,G=c.n13,F=c.n14,C=c.n21,M=c.n22, -L=c.n23,O=c.n24,w=c.n31,W=c.n32,S=c.n33,T=c.n34,h=c.n41,R=c.n42,J=c.n43,P=c.n44;this.n11=d*I+e*C+f*w+g*h;this.n12=d*A+e*M+f*W+g*R;this.n13=d*G+e*L+f*S+g*J;this.n14=d*F+e*O+f*T+g*P;this.n21=j*I+k*C+m*w+o*h;this.n22=j*A+k*M+m*W+o*R;this.n23=j*G+k*L+m*S+o*J;this.n24=j*F+k*O+m*T+o*P;this.n31=n*I+t*C+p*w+u*h;this.n32=n*A+t*M+p*W+u*R;this.n33=n*G+t*L+p*S+u*J;this.n34=n*F+t*O+p*T+u*P;this.n41=v*I+y*C+B*w+z*h;this.n42=v*A+y*M+B*W+z*R;this.n43=v*G+y*L+B*S+z*J;this.n44=v*F+y*O+B*T+z*P;return this},multiplyToArray:function(b, -c,d){this.multiply(b,c);d[0]=this.n11;d[1]=this.n21;d[2]=this.n31;d[3]=this.n41;d[4]=this.n12;d[5]=this.n22;d[6]=this.n32;d[7]=this.n42;d[8]=this.n13;d[9]=this.n23;d[10]=this.n33;d[11]=this.n43;d[12]=this.n14;d[13]=this.n24;d[14]=this.n34;d[15]=this.n44;return this},multiplySelf:function(b){this.multiply(this,b);return this},multiplyScalar:function(b){this.n11*=b;this.n12*=b;this.n13*=b;this.n14*=b;this.n21*=b;this.n22*=b;this.n23*=b;this.n24*=b;this.n31*=b;this.n32*=b;this.n33*=b;this.n34*=b;this.n41*= -b;this.n42*=b;this.n43*=b;this.n44*=b;return this},determinant:function(){var b=this.n11,c=this.n12,d=this.n13,e=this.n14,f=this.n21,g=this.n22,j=this.n23,k=this.n24,m=this.n31,o=this.n32,n=this.n33,t=this.n34,p=this.n41,u=this.n42,v=this.n43,y=this.n44;return e*j*o*p-d*k*o*p-e*g*n*p+c*k*n*p+d*g*t*p-c*j*t*p-e*j*m*u+d*k*m*u+e*f*n*u-b*k*n*u-d*f*t*u+b*j*t*u+e*g*m*v-c*k*m*v-e*f*o*v+b*k*o*v+c*f*t*v-b*g*t*v-d*g*m*y+c*j*m*y+d*f*o*y-b*j*o*y-c*f*n*y+b*g*n*y},transpose:function(){var b;b=this.n21;this.n21= -this.n12;this.n12=b;b=this.n31;this.n31=this.n13;this.n13=b;b=this.n32;this.n32=this.n23;this.n23=b;b=this.n41;this.n41=this.n14;this.n14=b;b=this.n42;this.n42=this.n24;this.n24=b;b=this.n43;this.n43=this.n34;this.n43=b;return this},clone:function(){var b=new THREE.Matrix4;b.n11=this.n11;b.n12=this.n12;b.n13=this.n13;b.n14=this.n14;b.n21=this.n21;b.n22=this.n22;b.n23=this.n23;b.n24=this.n24;b.n31=this.n31;b.n32=this.n32;b.n33=this.n33;b.n34=this.n34;b.n41=this.n41;b.n42=this.n42;b.n43=this.n43;b.n44= -this.n44;return b},flatten:function(){this.flat[0]=this.n11;this.flat[1]=this.n21;this.flat[2]=this.n31;this.flat[3]=this.n41;this.flat[4]=this.n12;this.flat[5]=this.n22;this.flat[6]=this.n32;this.flat[7]=this.n42;this.flat[8]=this.n13;this.flat[9]=this.n23;this.flat[10]=this.n33;this.flat[11]=this.n43;this.flat[12]=this.n14;this.flat[13]=this.n24;this.flat[14]=this.n34;this.flat[15]=this.n44;return this.flat},flattenToArray:function(b){b[0]=this.n11;b[1]=this.n21;b[2]=this.n31;b[3]=this.n41;b[4]= -this.n12;b[5]=this.n22;b[6]=this.n32;b[7]=this.n42;b[8]=this.n13;b[9]=this.n23;b[10]=this.n33;b[11]=this.n43;b[12]=this.n14;b[13]=this.n24;b[14]=this.n34;b[15]=this.n44;return b},flattenToArrayOffset:function(b,c){b[c]=this.n11;b[c+1]=this.n21;b[c+2]=this.n31;b[c+3]=this.n41;b[c+4]=this.n12;b[c+5]=this.n22;b[c+6]=this.n32;b[c+7]=this.n42;b[c+8]=this.n13;b[c+9]=this.n23;b[c+10]=this.n33;b[c+11]=this.n43;b[c+12]=this.n14;b[c+13]=this.n24;b[c+14]=this.n34;b[c+15]=this.n44;return b},setTranslation:function(b, -c,d){this.set(1,0,0,b,0,1,0,c,0,0,1,d,0,0,0,1);return this},setScale:function(b,c,d){this.set(b,0,0,0,0,c,0,0,0,0,d,0,0,0,0,1);return this},setRotationX:function(b){var c=Math.cos(b),b=Math.sin(b);this.set(1,0,0,0,0,c,-b,0,0,b,c,0,0,0,0,1);return this},setRotationY:function(b){var c=Math.cos(b),b=Math.sin(b);this.set(c,0,b,0,0,1,0,0,-b,0,c,0,0,0,0,1);return this},setRotationZ:function(b){var c=Math.cos(b),b=Math.sin(b);this.set(c,-b,0,0,b,c,0,0,0,0,1,0,0,0,0,1);return this},setRotationAxis:function(b, -c){var d=Math.cos(c),e=Math.sin(c),f=1-d,g=b.x,j=b.y,k=b.z,m=f*g,o=f*j;this.set(m*g+d,m*j-e*k,m*k+e*j,0,m*j+e*k,o*j+d,o*k-e*g,0,m*k-e*j,o*k+e*g,f*k*k+d,0,0,0,0,1);return this},setPosition:function(b){this.n14=b.x;this.n24=b.y;this.n34=b.z;return this},getPosition:function(){if(!this.position)this.position=new THREE.Vector3;this.position.set(this.n14,this.n24,this.n34);return this.position},getColumnX:function(){if(!this.columnX)this.columnX=new THREE.Vector3;this.columnX.set(this.n11,this.n21,this.n31); -return this.columnX},getColumnY:function(){if(!this.columnY)this.columnY=new THREE.Vector3;this.columnY.set(this.n12,this.n22,this.n32);return this.columnY},getColumnZ:function(){if(!this.columnZ)this.columnZ=new THREE.Vector3;this.columnZ.set(this.n13,this.n23,this.n33);return this.columnZ},setRotationFromEuler:function(b,c){var d=b.x,e=b.y,f=b.z,g=Math.cos(d),d=Math.sin(d),j=Math.cos(e),e=Math.sin(e),k=Math.cos(f),f=Math.sin(f);switch(c){case "YXZ":var m=j*k,o=j*f,n=e*k,t=e*f;this.n11=m+t*d;this.n12= -n*d-o;this.n13=g*e;this.n21=g*f;this.n22=g*k;this.n23=-d;this.n31=o*d-n;this.n32=t+m*d;this.n33=g*j;break;case "ZXY":m=j*k;o=j*f;n=e*k;t=e*f;this.n11=m-t*d;this.n12=-g*f;this.n13=n+o*d;this.n21=o+n*d;this.n22=g*k;this.n23=t-m*d;this.n31=-g*e;this.n32=d;this.n33=g*j;break;case "ZYX":m=g*k;o=g*f;n=d*k;t=d*f;this.n11=j*k;this.n12=n*e-o;this.n13=m*e+t;this.n21=j*f;this.n22=t*e+m;this.n23=o*e-n;this.n31=-e;this.n32=d*j;this.n33=g*j;break;case "YZX":m=g*j;o=g*e;n=d*j;t=d*e;this.n11=j*k;this.n12=t-m*f;this.n13= -n*f+o;this.n21=f;this.n22=g*k;this.n23=-d*k;this.n31=-e*k;this.n32=o*f+n;this.n33=m-t*f;break;case "XZY":m=g*j;o=g*e;n=d*j;t=d*e;this.n11=j*k;this.n12=-f;this.n13=e*k;this.n21=m*f+t;this.n22=g*k;this.n23=o*f-n;this.n31=n*f-o;this.n32=d*k;this.n33=t*f+m;break;default:m=g*k,o=g*f,n=d*k,t=d*f,this.n11=j*k,this.n12=-j*f,this.n13=e,this.n21=o+n*e,this.n22=m-t*e,this.n23=-d*j,this.n31=t-m*e,this.n32=n+o*e,this.n33=g*j}return this},setRotationFromQuaternion:function(b){var c=b.x,d=b.y,e=b.z,f=b.w,g=c+c, -j=d+d,k=e+e,b=c*g,m=c*j;c*=k;var o=d*j;d*=k;e*=k;g*=f;j*=f;f*=k;this.n11=1-(o+e);this.n12=m-f;this.n13=c+j;this.n21=m+f;this.n22=1-(b+e);this.n23=d-g;this.n31=c-j;this.n32=d+g;this.n33=1-(b+o);return this},scale:function(b){var c=b.x,d=b.y,b=b.z;this.n11*=c;this.n12*=d;this.n13*=b;this.n21*=c;this.n22*=d;this.n23*=b;this.n31*=c;this.n32*=d;this.n33*=b;this.n41*=c;this.n42*=d;this.n43*=b;return this},extractPosition:function(b){this.n14=b.n14;this.n24=b.n24;this.n34=b.n34},extractRotation:function(b, -c){var d=1/c.x,e=1/c.y,f=1/c.z;this.n11=b.n11*d;this.n21=b.n21*d;this.n31=b.n31*d;this.n12=b.n12*e;this.n22=b.n22*e;this.n32=b.n32*e;this.n13=b.n13*f;this.n23=b.n23*f;this.n33=b.n33*f}}; -THREE.Matrix4.makeInvert=function(b,c){var d=b.n11,e=b.n12,f=b.n13,g=b.n14,j=b.n21,k=b.n22,m=b.n23,o=b.n24,n=b.n31,t=b.n32,p=b.n33,u=b.n34,v=b.n41,y=b.n42,B=b.n43,z=b.n44;c===void 0&&(c=new THREE.Matrix4);c.n11=m*u*y-o*p*y+o*t*B-k*u*B-m*t*z+k*p*z;c.n12=g*p*y-f*u*y-g*t*B+e*u*B+f*t*z-e*p*z;c.n13=f*o*y-g*m*y+g*k*B-e*o*B-f*k*z+e*m*z;c.n14=g*m*t-f*o*t-g*k*p+e*o*p+f*k*u-e*m*u;c.n21=o*p*v-m*u*v-o*n*B+j*u*B+m*n*z-j*p*z;c.n22=f*u*v-g*p*v+g*n*B-d*u*B-f*n*z+d*p*z;c.n23=g*m*v-f*o*v-g*j*B+d*o*B+f*j*z-d*m*z;c.n24= -f*o*n-g*m*n+g*j*p-d*o*p-f*j*u+d*m*u;c.n31=k*u*v-o*t*v+o*n*y-j*u*y-k*n*z+j*t*z;c.n32=g*t*v-e*u*v-g*n*y+d*u*y+e*n*z-d*t*z;c.n33=f*o*v-g*k*v+g*j*y-d*o*y-e*j*z+d*k*z;c.n34=g*k*n-e*o*n-g*j*t+d*o*t+e*j*u-d*k*u;c.n41=m*t*v-k*p*v-m*n*y+j*p*y+k*n*B-j*t*B;c.n42=e*p*v-f*t*v+f*n*y-d*p*y-e*n*B+d*t*B;c.n43=f*k*v-e*m*v-f*j*y+d*m*y+e*j*B-d*k*B;c.n44=e*m*n-f*k*n+f*j*t-d*m*t-e*j*p+d*k*p;c.multiplyScalar(1/b.determinant());return c}; -THREE.Matrix4.makeInvert3x3=function(b){var c=b.m33,d=c.m,e=b.n33*b.n22-b.n32*b.n23,f=-b.n33*b.n21+b.n31*b.n23,g=b.n32*b.n21-b.n31*b.n22,j=-b.n33*b.n12+b.n32*b.n13,k=b.n33*b.n11-b.n31*b.n13,m=-b.n32*b.n11+b.n31*b.n12,o=b.n23*b.n12-b.n22*b.n13,n=-b.n23*b.n11+b.n21*b.n13,t=b.n22*b.n11-b.n21*b.n12,b=b.n11*e+b.n21*j+b.n31*o;b==0&&console.error("THREE.Matrix4.makeInvert3x3: Matrix not invertible.");b=1/b;d[0]=b*e;d[1]=b*f;d[2]=b*g;d[3]=b*j;d[4]=b*k;d[5]=b*m;d[6]=b*o;d[7]=b*n;d[8]=b*t;return c}; -THREE.Matrix4.makeFrustum=function(b,c,d,e,f,g){var j;j=new THREE.Matrix4;j.n11=2*f/(c-b);j.n12=0;j.n13=(c+b)/(c-b);j.n14=0;j.n21=0;j.n22=2*f/(e-d);j.n23=(e+d)/(e-d);j.n24=0;j.n31=0;j.n32=0;j.n33=-(g+f)/(g-f);j.n34=-2*g*f/(g-f);j.n41=0;j.n42=0;j.n43=-1;j.n44=0;return j};THREE.Matrix4.makePerspective=function(b,c,d,e){var f,b=d*Math.tan(b*Math.PI/360);f=-b;return THREE.Matrix4.makeFrustum(f*c,b*c,f,b,d,e)}; -THREE.Matrix4.makeOrtho=function(b,c,d,e,f,g){var j,k,m,o;j=new THREE.Matrix4;k=c-b;m=d-e;o=g-f;j.n11=2/k;j.n12=0;j.n13=0;j.n14=-((c+b)/k);j.n21=0;j.n22=2/m;j.n23=0;j.n24=-((d+e)/m);j.n31=0;j.n32=0;j.n33=-2/o;j.n34=-((g+f)/o);j.n41=0;j.n42=0;j.n43=0;j.n44=1;return j};THREE.Matrix4.__v1=new THREE.Vector3;THREE.Matrix4.__v2=new THREE.Vector3;THREE.Matrix4.__v3=new THREE.Vector3; -THREE.Object3D=function(){this.parent=void 0;this.children=[];this.up=new THREE.Vector3(0,1,0);this.position=new THREE.Vector3;this.rotation=new THREE.Vector3;this.eulerOrder="XYZ";this.scale=new THREE.Vector3(1,1,1);this.flipSided=this.doubleSided=this.dynamic=!1;this.renderDepth=null;this.rotationAutoUpdate=!0;this.matrix=new THREE.Matrix4;this.matrixWorld=new THREE.Matrix4;this.matrixRotationWorld=new THREE.Matrix4;this.matrixWorldNeedsUpdate=this.matrixAutoUpdate=!0;this.quaternion=new THREE.Quaternion; -this.useQuaternion=!1;this.boundRadius=0;this.boundRadiusScale=1;this.visible=!0;this._vector=new THREE.Vector3;this.name=""}; -THREE.Object3D.prototype={translate:function(b,c){this.matrix.rotateAxis(c);this.position.addSelf(c.multiplyScalar(b))},translateX:function(b){this.translate(b,this._vector.set(1,0,0))},translateY:function(b){this.translate(b,this._vector.set(0,1,0))},translateZ:function(b){this.translate(b,this._vector.set(0,0,1))},lookAt:function(b){this.matrix.lookAt(b,this.position,this.up);this.rotationAutoUpdate&&this.rotation.setRotationFromMatrix(this.matrix)},addChild:function(b){if(this.children.indexOf(b)=== --1){b.parent!==void 0&&b.parent.removeChild(b);b.parent=this;this.children.push(b);for(var c=this;c.parent!==void 0;)c=c.parent;c!==void 0&&c instanceof THREE.Scene&&c.addChildRecurse(b)}},removeChild:function(b){var c=this.children.indexOf(b);if(c!==-1)b.parent=void 0,this.children.splice(c,1)},getChildByName:function(b,c){var d,e,f;d=0;for(e=this.children.length;d=1)return d.w=b.w,d.x=b.x,d.y=b.y,d.z=b.z,d;var g=Math.acos(f),j=Math.sqrt(1-f*f);if(Math.abs(j)<0.001)return d.w=0.5*(b.w+c.w),d.x=0.5*(b.x+c.x),d.y=0.5*(b.y+c.y),d.z=0.5*(b.z+c.z),d;f=Math.sin((1-e)*g)/j;e=Math.sin(e*g)/j;d.w=b.w*f+c.w*e;d.x=b.x*f+c.x*e;d.y=b.y*f+c.y*e;d.z=b.z*f+c.z*e;return d};THREE.Vertex=function(b){this.position=b||new THREE.Vector3}; -THREE.Face3=function(b,c,d,e,f,g){this.a=b;this.b=c;this.c=d;this.normal=e instanceof THREE.Vector3?e:new THREE.Vector3;this.vertexNormals=e instanceof Array?e:[];this.color=f instanceof THREE.Color?f:new THREE.Color;this.vertexColors=f instanceof Array?f:[];this.vertexTangents=[];this.materials=g instanceof Array?g:[g];this.centroid=new THREE.Vector3}; -THREE.Face4=function(b,c,d,e,f,g,j){this.a=b;this.b=c;this.c=d;this.d=e;this.normal=f instanceof THREE.Vector3?f:new THREE.Vector3;this.vertexNormals=f instanceof Array?f:[];this.color=g instanceof THREE.Color?g:new THREE.Color;this.vertexColors=g instanceof Array?g:[];this.vertexTangents=[];this.materials=j instanceof Array?j:[j];this.centroid=new THREE.Vector3};THREE.UV=function(b,c){this.set(b||0,c||0)}; -THREE.UV.prototype={set:function(b,c){this.u=b;this.v=c;return this},copy:function(b){this.set(b.u,b.v);return this}};THREE.Geometry=function(){this.id="Geometry"+THREE.GeometryIdCounter++;this.vertices=[];this.colors=[];this.faces=[];this.edges=[];this.faceUvs=[[]];this.faceVertexUvs=[[]];this.morphTargets=[];this.morphColors=[];this.skinWeights=[];this.skinIndices=[];this.boundingSphere=this.boundingBox=null;this.hasTangents=!1}; -THREE.Geometry.prototype={computeCentroids:function(){var b,c,d;b=0;for(c=this.faces.length;b0){this.boundingBox={x:[this.vertices[0].position.x,this.vertices[0].position.x],y:[this.vertices[0].position.y,this.vertices[0].position.y],z:[this.vertices[0].position.z,this.vertices[0].position.z]};for(var c=1,d=this.vertices.length;cthis.boundingBox.x[1])this.boundingBox.x[1]=b.position.x;if(b.position.ythis.boundingBox.y[1])this.boundingBox.y[1]=b.position.y;if(b.position.zthis.boundingBox.z[1])this.boundingBox.z[1]=b.position.z}}},computeBoundingSphere:function(){for(var b=0,c=0,d=this.vertices.length;cthis.points.length-2?g:g+1;d[3]=g>this.points.length-3?g:g+2;o=this.points[d[0]];n=this.points[d[1]]; -t=this.points[d[2]];p=this.points[d[3]];k=j*j;m=j*k;e.x=c(o.x,n.x,t.x,p.x,j,k,m);e.y=c(o.y,n.y,t.y,p.y,j,k,m);e.z=c(o.z,n.z,t.z,p.z,j,k,m);return e};this.getControlPointsArray=function(){var b,d,c=this.points.length,e=[];for(b=0;b1){b=d.matrixWorldInverse;b=-(b.n31*this.position.x+b.n32*this.position.y+b.n33*this.position.z+b.n34);this.LODs[0].object3D.visible=!0;for(var e=1;e=this.LODs[e].visibleAtDistance)this.LODs[e-1].object3D.visible=!1, -this.LODs[e].object3D.visible=!0;else break;for(;et&&(d=n,n=t,t=d):nu&&(d=p,p=u,u=d):p=0&&g>=0&&j>=0&&k>=0?!0:f<0&&g<0||j<0&&k<0?!1:(f<0?c=Math.max(c,f/(f-g)):g<0&&(e=Math.min(e,f/(f-g))),j<0?c=Math.max(c,j/(j-k)):k<0&&(e=Math.min(e,j/(j-k))),eG&&j.positionScreen.z0&&F.z<1))H=A[I]=A[I]||new THREE.RenderableParticle,I++,z=H,z.x=F.x/F.w,z.y=F.y/F.w,z.z=F.z,z.rotation=D.rotation.z,z.scale.x=D.scale.x*Math.abs(z.x-(F.x+f.projectionMatrix.n11)/(F.w+f.projectionMatrix.n14)),z.scale.y=D.scale.y*Math.abs(z.y-(F.y+ -f.projectionMatrix.n22)/(F.w+f.projectionMatrix.n24)),z.materials=D.materials,h.push(z);g&&h.sort(c);return h}}; -THREE.DOMRenderer=function(){THREE.Renderer.call(this);var b=null,c=new THREE.Projector,d,e,f,g;this.domElement=document.createElement("div");this.setSize=function(b,c){d=b;e=c;f=d/2;g=e/2};this.render=function(d,e){var m,o,n,t,p,u,v,y;b=c.projectScene(d,e);m=0;for(o=b.length;m>1,t=o.height>> -1,g=h.scale.x*p,m=h.scale.y*u,j=g*aa,k=m*t,na.set(b.x-j,b.y-k,b.x+j,b.y+k),Da.instersects(na)&&(v.save(),v.translate(b.x,b.y),v.rotate(-h.rotation),v.scale(g,-m),v.translate(-aa,-t),v.drawImage(o,0,0),v.restore())}else g instanceof THREE.ParticleCanvasMaterial&&(j=h.scale.x*p,k=h.scale.y*u,na.set(b.x-j,b.y-k,b.x+j,b.y+k),Da.instersects(na)&&(e(g.color),f(g.color),v.save(),v.translate(b.x,b.y),v.rotate(-h.rotation),v.scale(j,k),g.program(v),v.restore()))}function z(b,h,f,g){c(g.opacity);d(g.blending); -v.beginPath();v.moveTo(b.positionScreen.x,b.positionScreen.y);v.lineTo(h.positionScreen.x,h.positionScreen.y);v.closePath();if(g instanceof THREE.LineBasicMaterial){b=g.linewidth;if(F!=b)v.lineWidth=F=b;b=g.linecap;if(C!=b)v.lineCap=C=b;b=g.linejoin;if(M!=b)v.lineJoin=M=b;e(g.color);v.stroke();na.inflate(g.linewidth*2)}}function y(b,e,f,k,m,o,n,p,u){j.data.vertices+=3;j.data.faces++;c(p.opacity);d(p.blending);h=b.positionScreen.x;R=b.positionScreen.y;J=e.positionScreen.x;P=e.positionScreen.y;Q=f.positionScreen.x; -N=f.positionScreen.y;B(h,R,J,P,Q,N);if(p instanceof THREE.MeshBasicMaterial)if(p.map)p.map.mapping instanceof THREE.UVMapping&&(ta=n.uvs[0],K(h,R,J,P,Q,N,p.map.image,ta[k].u,ta[k].v,ta[m].u,ta[m].v,ta[o].u,ta[o].v));else if(p.envMap){if(p.envMap.mapping instanceof THREE.SphericalReflectionMapping)b=g.matrixWorldInverse,ja.copy(n.vertexNormalsWorld[0]),$a=(ja.x*b.n11+ja.y*b.n12+ja.z*b.n13)*0.5+0.5,ab=-(ja.x*b.n21+ja.y*b.n22+ja.z*b.n23)*0.5+0.5,ja.copy(n.vertexNormalsWorld[1]),bb=(ja.x*b.n11+ja.y*b.n12+ -ja.z*b.n13)*0.5+0.5,cb=-(ja.x*b.n21+ja.y*b.n22+ja.z*b.n23)*0.5+0.5,ja.copy(n.vertexNormalsWorld[2]),db=(ja.x*b.n11+ja.y*b.n12+ja.z*b.n13)*0.5+0.5,eb=-(ja.x*b.n21+ja.y*b.n22+ja.z*b.n23)*0.5+0.5,K(h,R,J,P,Q,N,p.envMap.image,$a,ab,bb,cb,db,eb)}else p.wireframe?G(p.color,p.wireframeLinewidth,p.wireframeLinecap,p.wireframeLinejoin):Aa(p.color);else if(p instanceof THREE.MeshLambertMaterial)p.map&&!p.wireframe&&(p.map.mapping instanceof THREE.UVMapping&&(ta=n.uvs[0],K(h,R,J,P,Q,N,p.map.image,ta[k].u,ta[k].v, -ta[m].u,ta[m].v,ta[o].u,ta[o].v)),d(THREE.SubtractiveBlending)),Sa?!p.wireframe&&p.shading==THREE.SmoothShading&&n.vertexNormalsWorld.length==3?(H.r=$.r=ca.r=qa.r,H.g=$.g=ca.g=qa.g,H.b=$.b=ca.b=qa.b,t(u,n.v1.positionWorld,n.vertexNormalsWorld[0],H),t(u,n.v2.positionWorld,n.vertexNormalsWorld[1],$),t(u,n.v3.positionWorld,n.vertexNormalsWorld[2],ca),aa.r=($.r+ca.r)*0.5,aa.g=($.g+ca.g)*0.5,aa.b=($.b+ca.b)*0.5,pa=Qa(H,$,ca,aa),K(h,R,J,P,Q,N,pa,0,0,1,0,0,1)):(ua.r=qa.r,ua.g=qa.g,ua.b=qa.b,t(u,n.centroidWorld, -n.normalWorld,ua),D.r=Math.max(0,Math.min(p.color.r*ua.r,1)),D.g=Math.max(0,Math.min(p.color.g*ua.g,1)),D.b=Math.max(0,Math.min(p.color.b*ua.b,1)),D.updateHex(),p.wireframe?G(D,p.wireframeLinewidth,p.wireframeLinecap,p.wireframeLinejoin):Aa(D)):p.wireframe?G(p.color,p.wireframeLinewidth,p.wireframeLinecap,p.wireframeLinejoin):Aa(p.color);else if(p instanceof THREE.MeshDepthMaterial)oa=g.near,va=g.far,H.r=H.g=H.b=1-Ea(b.positionScreen.z,oa,va),$.r=$.g=$.b=1-Ea(e.positionScreen.z,oa,va),ca.r=ca.g=ca.b= -1-Ea(f.positionScreen.z,oa,va),aa.r=($.r+ca.r)*0.5,aa.g=($.g+ca.g)*0.5,aa.b=($.b+ca.b)*0.5,pa=Qa(H,$,ca,aa),K(h,R,J,P,Q,N,pa,0,0,1,0,0,1);else if(p instanceof THREE.MeshNormalMaterial)D.r=Ka(n.normalWorld.x),D.g=Ka(n.normalWorld.y),D.b=Ka(n.normalWorld.z),D.updateHex(),p.wireframe?G(D,p.wireframeLinewidth,p.wireframeLinecap,p.wireframeLinejoin):Aa(D)}function A(b,e,f,k,m,o,n,p,u){j.data.vertices+=4;j.data.faces++;c(p.opacity);d(p.blending);if(p.map||p.envMap)y(b,e,k,0,1,3,n,p,u),y(m,f,o,1,2,3,n,p, -u);else if(h=b.positionScreen.x,R=b.positionScreen.y,J=e.positionScreen.x,P=e.positionScreen.y,Q=f.positionScreen.x,N=f.positionScreen.y,Y=k.positionScreen.x,V=k.positionScreen.y,Z=m.positionScreen.x,U=m.positionScreen.y,ea=o.positionScreen.x,ia=o.positionScreen.y,p instanceof THREE.MeshBasicMaterial)I(h,R,J,P,Q,N,Y,V),p.wireframe?G(p.color,p.wireframeLinewidth,p.wireframeLinecap,p.wireframeLinejoin):Aa(p.color);else if(p instanceof THREE.MeshLambertMaterial)Sa?!p.wireframe&&p.shading==THREE.SmoothShading&& -n.vertexNormalsWorld.length==4?(H.r=$.r=ca.r=aa.r=qa.r,H.g=$.g=ca.g=aa.g=qa.g,H.b=$.b=ca.b=aa.b=qa.b,t(u,n.v1.positionWorld,n.vertexNormalsWorld[0],H),t(u,n.v2.positionWorld,n.vertexNormalsWorld[1],$),t(u,n.v4.positionWorld,n.vertexNormalsWorld[3],ca),t(u,n.v3.positionWorld,n.vertexNormalsWorld[2],aa),pa=Qa(H,$,ca,aa),B(h,R,J,P,Y,V),K(h,R,J,P,Y,V,pa,0,0,1,0,0,1),B(Z,U,Q,N,ea,ia),K(Z,U,Q,N,ea,ia,pa,1,0,1,1,0,1)):(ua.r=qa.r,ua.g=qa.g,ua.b=qa.b,t(u,n.centroidWorld,n.normalWorld,ua),D.r=Math.max(0,Math.min(p.color.r* -ua.r,1)),D.g=Math.max(0,Math.min(p.color.g*ua.g,1)),D.b=Math.max(0,Math.min(p.color.b*ua.b,1)),D.updateHex(),I(h,R,J,P,Q,N,Y,V),p.wireframe?G(D,p.wireframeLinewidth,p.wireframeLinecap,p.wireframeLinejoin):Aa(D)):(I(h,R,J,P,Q,N,Y,V),p.wireframe?G(p.color,p.wireframeLinewidth,p.wireframeLinecap,p.wireframeLinejoin):Aa(p.color));else if(p instanceof THREE.MeshNormalMaterial)D.r=Ka(n.normalWorld.x),D.g=Ka(n.normalWorld.y),D.b=Ka(n.normalWorld.z),D.updateHex(),I(h,R,J,P,Q,N,Y,V),p.wireframe?G(D,p.wireframeLinewidth, -p.wireframeLinecap,p.wireframeLinejoin):Aa(D);else if(p instanceof THREE.MeshDepthMaterial)oa=g.near,va=g.far,H.r=H.g=H.b=1-Ea(b.positionScreen.z,oa,va),$.r=$.g=$.b=1-Ea(e.positionScreen.z,oa,va),ca.r=ca.g=ca.b=1-Ea(k.positionScreen.z,oa,va),aa.r=aa.g=aa.b=1-Ea(f.positionScreen.z,oa,va),pa=Qa(H,$,ca,aa),B(h,R,J,P,Y,V),K(h,R,J,P,Y,V,pa,0,0,1,0,0,1),B(Z,U,Q,N,ea,ia),K(Z,U,Q,N,ea,ia,pa,1,0,1,1,0,1)}function B(b,d,c,e,h,f){v.beginPath();v.moveTo(b,d);v.lineTo(c,e);v.lineTo(h,f);v.lineTo(b,d);v.closePath()} -function I(b,d,c,e,h,f,g,j){v.beginPath();v.moveTo(b,d);v.lineTo(c,e);v.lineTo(h,f);v.lineTo(g,j);v.lineTo(b,d);v.closePath()}function G(b,d,c,h){if(F!=d)v.lineWidth=F=d;if(C!=c)v.lineCap=C=c;if(M!=h)v.lineJoin=M=h;e(b);v.stroke();na.inflate(d*2)}function Aa(b){f(b);v.fill()}function K(b,d,c,e,h,f,g,j,k,m,o,aa,p){var n,t;n=g.width-1;t=g.height-1;j*=n;k*=t;m*=n;o*=t;aa*=n;p*=t;c-=b;e-=d;h-=b;f-=d;m-=j;o-=k;aa-=j;p-=k;n=m*p-aa*o;n!=0&&(t=1/n,n=(p*c-o*h)*t,o=(p*e-o*f)*t,c=(m*h-aa*c)*t,e=(m*f-aa*e)*t, -b=b-n*j-c*k,d=d-o*j-e*k,v.save(),v.transform(n,o,c,e,b,d),v.clip(),v.drawImage(g,0,0),v.restore())}function Qa(b,d,c,e){var h=~~(b.r*255),f=~~(b.g*255),b=~~(b.b*255),g=~~(d.r*255),j=~~(d.g*255),d=~~(d.b*255),k=~~(c.r*255),m=~~(c.g*255),c=~~(c.b*255),o=~~(e.r*255),aa=~~(e.g*255),e=~~(e.b*255);xa[0]=h<0?0:h>255?255:h;xa[1]=f<0?0:f>255?255:f;xa[2]=b<0?0:b>255?255:b;xa[4]=g<0?0:g>255?255:g;xa[5]=j<0?0:j>255?255:j;xa[6]=d<0?0:d>255?255:d;xa[8]=k<0?0:k>255?255:k;xa[9]=m<0?0:m>255?255:m;xa[10]=c<0?0:c>255? -255:c;xa[12]=o<0?0:o>255?255:o;xa[13]=aa<0?0:aa>255?255:aa;xa[14]=e<0?0:e>255?255:e;Ma.putImageData(Ta,0,0);Pa.drawImage(La,0,0);return E}function Ea(b,d,c){b=(b-d)/(c-d);return b*b*(3-2*b)}function Ka(b){b=(b+1)*0.5;return b<0?0:b>1?1:b}function Ba(b,d){var c=d.x-b.x,e=d.y-b.y,h=1/Math.sqrt(c*c+e*e);c*=h;e*=h;d.x+=c;d.y+=e;b.x-=c;b.y-=e}var Ra,hb,X,da,fa,ga,ha,la;this.autoClear?this.clear():v.setTransform(1,0,0,-1,p,u);j.data.vertices=0;j.data.faces=0;k=m.projectScene(b,g,this.sortElements);(Sa= -b.lights.length>0)&&o(b);Ra=0;for(hb=k.length;Ra0&&(c.r+=f.color.r*g,c.g+=f.color.g*g,c.b+=f.color.b*g)):f instanceof THREE.PointLight&&(W.sub(f.position,d.centroidWorld),W.normalize(),g=d.normalWorld.dot(W)*f.intensity,g>0&&(c.r+=f.color.r*g,c.g+=f.color.g*g,c.b+=f.color.b*g))}function c(d,c,k,m,p,n){j.data.vertices+=3;j.data.faces++;h=e(R++);h.setAttribute("d", -"M "+d.positionScreen.x+" "+d.positionScreen.y+" L "+c.positionScreen.x+" "+c.positionScreen.y+" L "+k.positionScreen.x+","+k.positionScreen.y+"z");p instanceof THREE.MeshBasicMaterial?F.hex=p.color.hex:p instanceof THREE.MeshLambertMaterial?G?(C.r=M.r,C.g=M.g,C.b=M.b,b(n,m,C),F.r=Math.max(0,Math.min(p.color.r*C.r,1)),F.g=Math.max(0,Math.min(p.color.g*C.g,1)),F.b=Math.max(0,Math.min(p.color.b*C.b,1)),F.updateHex()):F.hex=p.color.hex:p instanceof THREE.MeshDepthMaterial?(w=1-p.__2near/(p.__farPlusNear- -m.z*p.__farMinusNear),F.setRGB(w,w,w)):p instanceof THREE.MeshNormalMaterial&&F.setRGB(f(m.normalWorld.x),f(m.normalWorld.y),f(m.normalWorld.z));p.wireframe?h.setAttribute("style","fill: none; stroke: #"+g(F.hex.toString(16))+"; stroke-width: "+p.wireframeLinewidth+"; stroke-opacity: "+p.opacity+"; stroke-linecap: "+p.wireframeLinecap+"; stroke-linejoin: "+p.wireframeLinejoin):h.setAttribute("style","fill: #"+g(F.hex.toString(16))+"; fill-opacity: "+p.opacity);o.appendChild(h)}function d(d,c,k,m, -p,n,t){j.data.vertices+=4;j.data.faces++;h=e(R++);h.setAttribute("d","M "+d.positionScreen.x+" "+d.positionScreen.y+" L "+c.positionScreen.x+" "+c.positionScreen.y+" L "+k.positionScreen.x+","+k.positionScreen.y+" L "+m.positionScreen.x+","+m.positionScreen.y+"z");n instanceof THREE.MeshBasicMaterial?F.hex=n.color.hex:n instanceof THREE.MeshLambertMaterial?G?(C.r=M.r,C.g=M.g,C.b=M.b,b(t,p,C),F.r=Math.max(0,Math.min(n.color.r*C.r,1)),F.g=Math.max(0,Math.min(n.color.g*C.g,1)),F.b=Math.max(0,Math.min(n.color.b* -C.b,1)),F.updateHex()):F.hex=n.color.hex:n instanceof THREE.MeshDepthMaterial?(w=1-n.__2near/(n.__farPlusNear-p.z*n.__farMinusNear),F.setRGB(w,w,w)):n instanceof THREE.MeshNormalMaterial&&F.setRGB(f(p.normalWorld.x),f(p.normalWorld.y),f(p.normalWorld.z));n.wireframe?h.setAttribute("style","fill: none; stroke: #"+g(F.hex.toString(16))+"; stroke-width: "+n.wireframeLinewidth+"; stroke-opacity: "+n.opacity+"; stroke-linecap: "+n.wireframeLinecap+"; stroke-linejoin: "+n.wireframeLinejoin):h.setAttribute("style", -"fill: #"+g(F.hex.toString(16))+"; fill-opacity: "+n.opacity);o.appendChild(h)}function e(b){S[b]==null&&(S[b]=document.createElementNS("http://www.w3.org/2000/svg","path"),P==0&&S[b].setAttribute("shape-rendering","crispEdges"));return S[b]}function f(b){b=(b+1)*0.5;return b<0?0:b>1?1:b}function g(b){for(;b.length<6;)b="0"+b;return b}var j=this,k=null,m=new THREE.Projector,o=document.createElementNS("http://www.w3.org/2000/svg","svg"),n,t,p,u,v,y,B,z,I=new THREE.Rectangle,A=new THREE.Rectangle,G= -!1,F=new THREE.Color(16777215),C=new THREE.Color(16777215),M=new THREE.Color(0),L=new THREE.Color(0),O=new THREE.Color(0),w,W=new THREE.Vector3,S=[],T=[],h,R,J,P=1;this.domElement=o;this.sortElements=this.sortObjects=this.autoClear=!0;this.data={vertices:0,faces:0};this.setQuality=function(b){switch(b){case "high":P=1;break;case "low":P=0}};this.setSize=function(b,d){n=b;t=d;p=n/2;u=t/2;o.setAttribute("viewBox",-p+" "+-u+" "+n+" "+t);o.setAttribute("width",n);o.setAttribute("height",t);I.set(-p,-u, -p,u)};this.clear=function(){for(;o.childNodes.length>0;)o.removeChild(o.childNodes[0])};this.render=function(b,e){var f,n,t,F,C,w,D,H;this.autoClear&&this.clear();j.data.vertices=0;j.data.faces=0;k=m.projectScene(b,e,this.sortElements);J=R=0;if(G=b.lights.length>0){D=b.lights;M.setRGB(0,0,0);L.setRGB(0,0,0);O.setRGB(0,0,0);f=0;for(n=D.length;f 0\nuniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];\nuniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n#endif\n#if MAX_POINT_LIGHTS > 0\nuniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];\nuniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];\nuniform float pointLightDistance[ MAX_POINT_LIGHTS ];\n#ifdef PHONG\nvarying vec4 vPointLight[ MAX_POINT_LIGHTS ];\n#endif\n#endif", -lights_vertex:"if ( !enableLighting ) {\nvLightWeighting = vec3( 1.0 );\n} else {\nvLightWeighting = ambientLightColor;\n#if MAX_DIR_LIGHTS > 0\nfor( int i = 0; i < MAX_DIR_LIGHTS; i++ ) {\nvec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );\nfloat directionalLightWeighting = max( dot( transformedNormal, normalize( lDirection.xyz ) ), 0.0 );\nvLightWeighting += directionalLightColor[ i ] * directionalLightWeighting;\n}\n#endif\n#if MAX_POINT_LIGHTS > 0\nfor( int i = 0; i < MAX_POINT_LIGHTS; i++ ) {\nvec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );\nvec3 lVector = lPosition.xyz - mvPosition.xyz;\nfloat lDistance = 1.0;\nif ( pointLightDistance[ i ] > 0.0 )\nlDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );\nlVector = normalize( lVector );\nfloat pointLightWeighting = max( dot( transformedNormal, lVector ), 0.0 );\nvLightWeighting += pointLightColor[ i ] * pointLightWeighting * lDistance;\n#ifdef PHONG\nvPointLight[ i ] = vec4( lVector, lDistance );\n#endif\n}\n#endif\n}", -lights_pars_fragment:"#if MAX_DIR_LIGHTS > 0\nuniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];\n#endif\n#if MAX_POINT_LIGHTS > 0\nvarying vec4 vPointLight[ MAX_POINT_LIGHTS ];\n#endif\nvarying vec3 vViewPosition;\nvarying vec3 vNormal;",lights_fragment:"vec3 normal = normalize( vNormal );\nvec3 viewPosition = normalize( vViewPosition );\nvec4 mColor = vec4( diffuse, opacity );\nvec4 mSpecular = vec4( specular, opacity );\n#if MAX_POINT_LIGHTS > 0\nvec4 pointDiffuse = vec4( 0.0 );\nvec4 pointSpecular = vec4( 0.0 );\nfor ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {\nvec3 pointVector = normalize( vPointLight[ i ].xyz );\nvec3 pointHalfVector = normalize( vPointLight[ i ].xyz + vViewPosition );\nfloat pointDistance = vPointLight[ i ].w;\nfloat pointDotNormalHalf = dot( normal, pointHalfVector );\nfloat pointDiffuseWeight = max( dot( normal, pointVector ), 0.0 );\nfloat pointSpecularWeight = 0.0;\nif ( pointDotNormalHalf >= 0.0 )\npointSpecularWeight = pow( pointDotNormalHalf, shininess );\npointDiffuse += mColor * pointDiffuseWeight * pointDistance;\npointSpecular += mSpecular * pointSpecularWeight * pointDistance;\n}\n#endif\n#if MAX_DIR_LIGHTS > 0\nvec4 dirDiffuse = vec4( 0.0 );\nvec4 dirSpecular = vec4( 0.0 );\nfor( int i = 0; i < MAX_DIR_LIGHTS; i++ ) {\nvec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );\nvec3 dirVector = normalize( lDirection.xyz );\nvec3 dirHalfVector = normalize( lDirection.xyz + vViewPosition );\nfloat dirDotNormalHalf = dot( normal, dirHalfVector );\nfloat dirDiffuseWeight = max( dot( normal, dirVector ), 0.0 );\nfloat dirSpecularWeight = 0.0;\nif ( dirDotNormalHalf >= 0.0 )\ndirSpecularWeight = pow( dirDotNormalHalf, shininess );\ndirDiffuse += mColor * dirDiffuseWeight;\ndirSpecular += mSpecular * dirSpecularWeight;\n}\n#endif\nvec4 totalLight = vec4( ambient, opacity );\n#if MAX_DIR_LIGHTS > 0\ntotalLight += dirDiffuse + dirSpecular;\n#endif\n#if MAX_POINT_LIGHTS > 0\ntotalLight += pointDiffuse + pointSpecular;\n#endif\ngl_FragColor = gl_FragColor * totalLight;", -color_pars_fragment:"#ifdef USE_COLOR\nvarying vec3 vColor;\n#endif",color_fragment:"#ifdef USE_COLOR\ngl_FragColor = gl_FragColor * vec4( vColor, opacity );\n#endif",color_pars_vertex:"#ifdef USE_COLOR\nvarying vec3 vColor;\n#endif",color_vertex:"#ifdef USE_COLOR\nvColor = color;\n#endif",skinning_pars_vertex:"#ifdef USE_SKINNING\nuniform mat4 boneGlobalMatrices[ MAX_BONES ];\n#endif",skinning_vertex:"#ifdef USE_SKINNING\ngl_Position = ( boneGlobalMatrices[ int( skinIndex.x ) ] * skinVertexA ) * skinWeight.x;\ngl_Position += ( boneGlobalMatrices[ int( skinIndex.y ) ] * skinVertexB ) * skinWeight.y;\ngl_Position = projectionMatrix * viewMatrix * objectMatrix * gl_Position;\n#endif", -morphtarget_pars_vertex:"#ifdef USE_MORPHTARGETS\nuniform float morphTargetInfluences[ 8 ];\n#endif",morphtarget_vertex:"#ifdef USE_MORPHTARGETS\nvec3 morphed = vec3( 0.0, 0.0, 0.0 );\nmorphed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];\nmorphed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];\nmorphed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];\nmorphed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];\nmorphed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];\nmorphed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];\nmorphed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];\nmorphed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];\nmorphed += position;\ngl_Position = projectionMatrix * modelViewMatrix * vec4( morphed, 1.0 );\n#endif", -default_vertex:"#ifndef USE_MORPHTARGETS\n#ifndef USE_SKINNING\ngl_Position = projectionMatrix * mvPosition;\n#endif\n#endif"};THREE.UniformsUtils={merge:function(b){var c,d,e,f={};for(c=0;c=0)h.bindBuffer(h.ARRAY_BUFFER,g.__webglVertexBuffer),h.vertexAttribPointer(b.position, -3,h.FLOAT,!1,0,0);else{c=f.program.attributes;j.morphTargetBase!==-1?(h.bindBuffer(h.ARRAY_BUFFER,g.__webglMorphTargetsBuffers[j.morphTargetBase]),h.vertexAttribPointer(c.position,3,h.FLOAT,!1,0,0)):c.position>=0&&(h.bindBuffer(h.ARRAY_BUFFER,g.__webglVertexBuffer),h.vertexAttribPointer(c.position,3,h.FLOAT,!1,0,0));if(j.morphTargetForcedOrder.length)for(var e=0,m=j.morphTargetForcedOrder,o=j.morphTargetInfluences;ep&&(n=t,p=o[n]);h.bindBuffer(h.ARRAY_BUFFER,g.__webglMorphTargetsBuffers[n]);h.vertexAttribPointer(c["morphTarget"+e],3,h.FLOAT,!1,0,0);j.__webglMorphTargetInfluences[e]=p;m[n]=1;p=-1;e++}}f.program.uniforms.morphTargetInfluences!== -null&&h.uniform1fv(f.program.uniforms.morphTargetInfluences,j.__webglMorphTargetInfluences)}if(g.__webglCustomAttributes)for(k in g.__webglCustomAttributes)b[k]>=0&&(c=g.__webglCustomAttributes[k],h.bindBuffer(h.ARRAY_BUFFER,c.buffer),h.vertexAttribPointer(b[k],c.size,h.FLOAT,!1,0,0));b.color>=0&&(h.bindBuffer(h.ARRAY_BUFFER,g.__webglColorBuffer),h.vertexAttribPointer(b.color,3,h.FLOAT,!1,0,0));b.normal>=0&&(h.bindBuffer(h.ARRAY_BUFFER,g.__webglNormalBuffer),h.vertexAttribPointer(b.normal,3,h.FLOAT, -!1,0,0));b.tangent>=0&&(h.bindBuffer(h.ARRAY_BUFFER,g.__webglTangentBuffer),h.vertexAttribPointer(b.tangent,4,h.FLOAT,!1,0,0));b.uv>=0&&(g.__webglUVBuffer?(h.bindBuffer(h.ARRAY_BUFFER,g.__webglUVBuffer),h.vertexAttribPointer(b.uv,2,h.FLOAT,!1,0,0),h.enableVertexAttribArray(b.uv)):h.disableVertexAttribArray(b.uv));b.uv2>=0&&(g.__webglUV2Buffer?(h.bindBuffer(h.ARRAY_BUFFER,g.__webglUV2Buffer),h.vertexAttribPointer(b.uv2,2,h.FLOAT,!1,0,0),h.enableVertexAttribArray(b.uv2)):h.disableVertexAttribArray(b.uv2)); -f.skinning&&b.skinVertexA>=0&&b.skinVertexB>=0&&b.skinIndex>=0&&b.skinWeight>=0&&(h.bindBuffer(h.ARRAY_BUFFER,g.__webglSkinVertexABuffer),h.vertexAttribPointer(b.skinVertexA,4,h.FLOAT,!1,0,0),h.bindBuffer(h.ARRAY_BUFFER,g.__webglSkinVertexBBuffer),h.vertexAttribPointer(b.skinVertexB,4,h.FLOAT,!1,0,0),h.bindBuffer(h.ARRAY_BUFFER,g.__webglSkinIndicesBuffer),h.vertexAttribPointer(b.skinIndex,4,h.FLOAT,!1,0,0),h.bindBuffer(h.ARRAY_BUFFER,g.__webglSkinWeightsBuffer),h.vertexAttribPointer(b.skinWeight, -4,h.FLOAT,!1,0,0));j instanceof THREE.Mesh?(f.wireframe?(h.lineWidth(f.wireframeLinewidth),h.bindBuffer(h.ELEMENT_ARRAY_BUFFER,g.__webglLineBuffer),h.drawElements(h.LINES,g.__webglLineCount,h.UNSIGNED_SHORT,0)):(h.bindBuffer(h.ELEMENT_ARRAY_BUFFER,g.__webglFaceBuffer),h.drawElements(h.TRIANGLES,g.__webglFaceCount,h.UNSIGNED_SHORT,0)),T.data.vertices+=g.__webglFaceCount,T.data.faces+=g.__webglFaceCount/3,T.data.drawCalls++):j instanceof THREE.Line?(j=j.type==THREE.LineStrip?h.LINE_STRIP:h.LINES,h.lineWidth(f.linewidth), -h.drawArrays(j,0,g.__webglLineCount),T.data.drawCalls++):j instanceof THREE.ParticleSystem?(h.drawArrays(h.POINTS,0,g.__webglParticleCount),T.data.drawCalls++):j instanceof THREE.Ribbon&&(h.drawArrays(h.TRIANGLE_STRIP,0,g.__webglVertexCount),T.data.drawCalls++)}}function f(b,d,c){if(!b.__webglVertexBuffer)b.__webglVertexBuffer=h.createBuffer();if(!b.__webglNormalBuffer)b.__webglNormalBuffer=h.createBuffer();b.hasPos&&(h.bindBuffer(h.ARRAY_BUFFER,b.__webglVertexBuffer),h.bufferData(h.ARRAY_BUFFER, -b.positionArray,h.DYNAMIC_DRAW),h.enableVertexAttribArray(d.attributes.position),h.vertexAttribPointer(d.attributes.position,3,h.FLOAT,!1,0,0));if(b.hasNormal){h.bindBuffer(h.ARRAY_BUFFER,b.__webglNormalBuffer);if(c==THREE.FlatShading){var e,f,g,j,k,m,o,p,n,t,u=b.count*3;for(t=0;t0&&z[0]<_viewportWidth&&z[1]>0&&z[1]<_viewportHeight){h.bindTexture(h.TEXTURE_2D,H.tempTexture); -h.copyTexImage2D(h.TEXTURE_2D,0,h.RGB,z[0]-8,z[1]-8,16,16,0);h.uniform1i(y.renderType,0);h.uniform2fv(y.scale,u);h.uniform3fv(y.screenPosition,v);h.disable(h.BLEND);h.enable(h.DEPTH_TEST);h.drawElements(h.TRIANGLES,6,h.UNSIGNED_SHORT,0);h.bindTexture(h.TEXTURE_2D,H.occlusionTexture);h.copyTexImage2D(h.TEXTURE_2D,0,h.RGBA,z[0]-8,z[1]-8,16,16,0);h.uniform1i(y.renderType,1);h.disable(h.DEPTH_TEST);h.bindTexture(h.TEXTURE_2D,H.tempTexture);h.drawElements(h.TRIANGLES,6,h.UNSIGNED_SHORT,0);c.positionScreen.x= -v[0];c.positionScreen.y=v[1];c.positionScreen.z=v[2];c.customUpdateCallback?c.customUpdateCallback(c):c.updateLensFlares();h.uniform1i(y.renderType,2);h.enable(h.BLEND);g=0;for(j=c.lensFlares.length;g0.001&&k.scale>0.001)v[0]=k.x,v[1]=k.y,v[2]=k.z,t=k.size*k.scale/_viewportHeight,u[0]=t*o,u[1]=t,h.uniform3fv(y.screenPosition,v),h.uniform2fv(y.scale,u),h.uniform1f(y.rotation,k.rotation),h.uniform1f(y.opacity,k.opacity),C(k.blending),L(k.texture,1),h.drawElements(h.TRIANGLES, -6,h.UNSIGNED_SHORT,0)}h.enable(h.CULL_FACE);h.enable(h.DEPTH_TEST);h.depthMask(Q)}function z(b,d){b._modelViewMatrix.multiplyToArray(d.matrixWorldInverse,b.matrixWorld,b._modelViewMatrixArray);THREE.Matrix4.makeInvert3x3(b._modelViewMatrix).transposeIntoArray(b._normalMatrixArray)}function I(b){var d,e,f,g;if(b instanceof THREE.Mesh){e=b.geometry;for(d in e.geometryGroups){f=e.geometryGroups[d];a:{for(var j=g=void 0,k=void 0,m=void 0,o=void 0,o=f.__materials,j=0,k=o.length;j0&&(h.bindBuffer(h.ARRAY_BUFFER,g.__webglColorBuffer),h.bufferData(h.ARRAY_BUFFER,X,k));Wa&&(h.bindBuffer(h.ARRAY_BUFFER,g.__webglNormalBuffer),h.bufferData(h.ARRAY_BUFFER,Z,k));Xa&&ka.hasTangents&& -(h.bindBuffer(h.ARRAY_BUFFER,g.__webglTangentBuffer),h.bufferData(h.ARRAY_BUFFER,U,k));Ja&&W>0&&(h.bindBuffer(h.ARRAY_BUFFER,g.__webglUVBuffer),h.bufferData(h.ARRAY_BUFFER,$,k));Ja&&V>0&&(h.bindBuffer(h.ARRAY_BUFFER,g.__webglUV2Buffer),h.bufferData(h.ARRAY_BUFFER,ca,k));Va&&(h.bindBuffer(h.ELEMENT_ARRAY_BUFFER,g.__webglFaceBuffer),h.bufferData(h.ELEMENT_ARRAY_BUFFER,ra,k),h.bindBuffer(h.ELEMENT_ARRAY_BUFFER,g.__webglLineBuffer),h.bufferData(h.ELEMENT_ARRAY_BUFFER,sa,k));K>0&&(h.bindBuffer(h.ARRAY_BUFFER, -g.__webglSkinVertexABuffer),h.bufferData(h.ARRAY_BUFFER,da,k),h.bindBuffer(h.ARRAY_BUFFER,g.__webglSkinVertexBBuffer),h.bufferData(h.ARRAY_BUFFER,fa,k),h.bindBuffer(h.ARRAY_BUFFER,g.__webglSkinIndicesBuffer),h.bufferData(h.ARRAY_BUFFER,ga,k),h.bindBuffer(h.ARRAY_BUFFER,g.__webglSkinWeightsBuffer),h.bufferData(h.ARRAY_BUFFER,ha,k));j.dynamic||(delete g.__inittedArrays,delete g.__colorArray,delete g.__normalArray,delete g.__tangentArray,delete g.__uvArray,delete g.__uv2Array,delete g.__faceArray,delete g.__vertexArray, -delete g.__lineArray,delete g.__skinVertexAArray,delete g.__skinVertexBArray,delete g.__skinIndexArray,delete g.__skinWeightArray)}}e.__dirtyVertices=!1;e.__dirtyMorphTargets=!1;e.__dirtyElements=!1;e.__dirtyUvs=!1;e.__dirtyNormals=!1;e.__dirtyTangents=!1;e.__dirtyColors=!1;var ma;f=f.__materials;e=0;for(b=f.length;e=0;c--)b[c].object==d&&b.splice(c,1)}function G(b){function d(b){var g=[];c=0;for(e=b.length;c65535&&(m[k].counter+=1,n=m[k].hash+"_"+m[k].counter,b.geometryGroups[n]==void 0&&(b.geometryGroups[n]={faces:[],materials:j,vertices:0,numMorphTargets:p})),b.geometryGroups[n].faces.push(g), -b.geometryGroups[n].vertices+=h}function F(b,d,c){b.push({buffer:d,object:c,opaque:{list:[],count:0},transparent:{list:[],count:0}})}function C(b){if(b!=V){switch(b){case THREE.AdditiveBlending:h.blendEquation(h.FUNC_ADD);h.blendFunc(h.SRC_ALPHA,h.ONE);break;case THREE.SubtractiveBlending:h.blendEquation(h.FUNC_ADD);h.blendFunc(h.ZERO,h.ONE_MINUS_SRC_COLOR);break;case THREE.MultiplyBlending:h.blendEquation(h.FUNC_ADD);h.blendFunc(h.ZERO,h.SRC_COLOR);break;default:h.blendEquationSeparate(h.FUNC_ADD, -h.FUNC_ADD),h.blendFuncSeparate(h.SRC_ALPHA,h.ONE_MINUS_SRC_ALPHA,h.ONE,h.ONE_MINUS_SRC_ALPHA)}V=b}}function M(b,d,c){(c.width&c.width-1)==0&&(c.height&c.height-1)==0?(h.texParameteri(b,h.TEXTURE_WRAP_S,S(d.wrapS)),h.texParameteri(b,h.TEXTURE_WRAP_T,S(d.wrapT)),h.texParameteri(b,h.TEXTURE_MAG_FILTER,S(d.magFilter)),h.texParameteri(b,h.TEXTURE_MIN_FILTER,S(d.minFilter)),h.generateMipmap(b)):(h.texParameteri(b,h.TEXTURE_WRAP_S,h.CLAMP_TO_EDGE),h.texParameteri(b,h.TEXTURE_WRAP_T,h.CLAMP_TO_EDGE),h.texParameteri(b, -h.TEXTURE_MAG_FILTER,W(d.magFilter)),h.texParameteri(b,h.TEXTURE_MIN_FILTER,W(d.minFilter)))}function L(b,d){if(b.needsUpdate)b.__webglInit?(h.bindTexture(h.TEXTURE_2D,b.__webglTexture),h.texSubImage2D(h.TEXTURE_2D,0,0,0,h.RGBA,h.UNSIGNED_BYTE,b.image)):(b.__webglTexture=h.createTexture(),h.bindTexture(h.TEXTURE_2D,b.__webglTexture),h.texImage2D(h.TEXTURE_2D,0,h.RGBA,h.RGBA,h.UNSIGNED_BYTE,b.image),b.__webglInit=!0),M(h.TEXTURE_2D,b,b.image),h.bindTexture(h.TEXTURE_2D,null),b.needsUpdate=!1;h.activeTexture(h.TEXTURE0+ -d);h.bindTexture(h.TEXTURE_2D,b.__webglTexture)}function O(b){if(b&&!b.__webglFramebuffer){if(b.depthBuffer===void 0)b.depthBuffer=!0;if(b.stencilBuffer===void 0)b.stencilBuffer=!0;b.__webglFramebuffer=h.createFramebuffer();b.__webglRenderbuffer=h.createRenderbuffer();b.__webglTexture=h.createTexture();h.bindTexture(h.TEXTURE_2D,b.__webglTexture);h.texParameteri(h.TEXTURE_2D,h.TEXTURE_WRAP_S,S(b.wrapS));h.texParameteri(h.TEXTURE_2D,h.TEXTURE_WRAP_T,S(b.wrapT));h.texParameteri(h.TEXTURE_2D,h.TEXTURE_MAG_FILTER, -S(b.magFilter));h.texParameteri(h.TEXTURE_2D,h.TEXTURE_MIN_FILTER,S(b.minFilter));h.texImage2D(h.TEXTURE_2D,0,S(b.format),b.width,b.height,0,S(b.format),S(b.type),null);h.bindRenderbuffer(h.RENDERBUFFER,b.__webglRenderbuffer);h.bindFramebuffer(h.FRAMEBUFFER,b.__webglFramebuffer);h.framebufferTexture2D(h.FRAMEBUFFER,h.COLOR_ATTACHMENT0,h.TEXTURE_2D,b.__webglTexture,0);b.depthBuffer&&!b.stencilBuffer?(h.renderbufferStorage(h.RENDERBUFFER,h.DEPTH_COMPONENT16,b.width,b.height),h.framebufferRenderbuffer(h.FRAMEBUFFER, -h.DEPTH_ATTACHMENT,h.RENDERBUFFER,b.__webglRenderbuffer)):b.depthBuffer&&b.stencilBuffer?(h.renderbufferStorage(h.RENDERBUFFER,h.DEPTH_STENCIL,b.width,b.height),h.framebufferRenderbuffer(h.FRAMEBUFFER,h.DEPTH_STENCIL_ATTACHMENT,h.RENDERBUFFER,b.__webglRenderbuffer)):h.renderbufferStorage(h.RENDERBUFFER,h.RGBA4,b.width,b.height);h.bindTexture(h.TEXTURE_2D,null);h.bindRenderbuffer(h.RENDERBUFFER,null);h.bindFramebuffer(h.FRAMEBUFFER,null)}var d,c;b?(d=b.__webglFramebuffer,c=b.width,b=b.height):(d=null, -c=_viewportWidth,b=_viewportHeight);d!=P&&(h.bindFramebuffer(h.FRAMEBUFFER,d),h.viewport(_viewportX,_viewportY,c,b),P=d)}function w(b,d){var c;b=="fragment"?c=h.createShader(h.FRAGMENT_SHADER):b=="vertex"&&(c=h.createShader(h.VERTEX_SHADER));h.shaderSource(c,d);h.compileShader(c);if(!h.getShaderParameter(c,h.COMPILE_STATUS))return console.error(h.getShaderInfoLog(c)),console.error(d),null;return c}function W(b){switch(b){case THREE.NearestFilter:case THREE.NearestMipMapNearestFilter:case THREE.NearestMipMapLinearFilter:return h.NEAREST; -default:return h.LINEAR}}function S(b){switch(b){case THREE.RepeatWrapping:return h.REPEAT;case THREE.ClampToEdgeWrapping:return h.CLAMP_TO_EDGE;case THREE.MirroredRepeatWrapping:return h.MIRRORED_REPEAT;case THREE.NearestFilter:return h.NEAREST;case THREE.NearestMipMapNearestFilter:return h.NEAREST_MIPMAP_NEAREST;case THREE.NearestMipMapLinearFilter:return h.NEAREST_MIPMAP_LINEAR;case THREE.LinearFilter:return h.LINEAR;case THREE.LinearMipMapNearestFilter:return h.LINEAR_MIPMAP_NEAREST;case THREE.LinearMipMapLinearFilter:return h.LINEAR_MIPMAP_LINEAR; -case THREE.ByteType:return h.BYTE;case THREE.UnsignedByteType:return h.UNSIGNED_BYTE;case THREE.ShortType:return h.SHORT;case THREE.UnsignedShortType:return h.UNSIGNED_SHORT;case THREE.IntType:return h.INT;case THREE.UnsignedShortType:return h.UNSIGNED_INT;case THREE.FloatType:return h.FLOAT;case THREE.AlphaFormat:return h.ALPHA;case THREE.RGBFormat:return h.RGB;case THREE.RGBAFormat:return h.RGBA;case THREE.LuminanceFormat:return h.LUMINANCE;case THREE.LuminanceAlphaFormat:return h.LUMINANCE_ALPHA}return 0} -var T=this,h,R=[],J=null,P=null,Q=!0,N=null,Y=null,V=null,Z=null,U=null;_oldPolygonOffsetUnits=_oldPolygonOffsetFactor=null;_cullEnabled=!0;_viewportHeight=_viewportWidth=_viewportY=_viewportX=0;_frustum=[new THREE.Vector4,new THREE.Vector4,new THREE.Vector4,new THREE.Vector4,new THREE.Vector4,new THREE.Vector4];_projScreenMatrix=new THREE.Matrix4;_projectionMatrixArray=new Float32Array(16);_viewMatrixArray=new Float32Array(16);_vector3=new THREE.Vector4;_lights={ambient:[0,0,0],directional:{length:0, -colors:[],positions:[]},point:{length:0,colors:[],positions:[],distances:[]}};b=b||{};_canvas=b.canvas!==void 0?b.canvas:document.createElement("canvas");_stencil=b.stencil!==void 0?b.stencil:!0;_antialias=b.antialias!==void 0?b.antialias:!1;_clearColor=b.clearColor!==void 0?new THREE.Color(b.clearColor):new THREE.Color(0);_clearAlpha=b.clearAlpha!==void 0?b.clearAlpha:0;this.data={vertices:0,faces:0,drawCalls:0};this.maxMorphTargets=8;this.domElement=_canvas;this.sortObjects=this.autoClear=!0;try{if(!(h= -_canvas.getContext("experimental-webgl",{antialias:_antialias,stencil:_stencil})))throw"Error creating WebGL context.";}catch(ea){console.error(ea)}console.log(navigator.userAgent+" | "+h.getParameter(h.VERSION)+" | "+h.getParameter(h.VENDOR)+" | "+h.getParameter(h.RENDERER)+" | "+h.getParameter(h.SHADING_LANGUAGE_VERSION));h.clearColor(0,0,0,1);h.clearDepth(1);h.enable(h.DEPTH_TEST);h.depthFunc(h.LEQUAL);h.frontFace(h.CCW);h.cullFace(h.BACK);h.enable(h.CULL_FACE);h.enable(h.BLEND);h.blendEquation(h.FUNC_ADD); -h.blendFunc(h.SRC_ALPHA,h.ONE_MINUS_SRC_ALPHA);h.clearColor(_clearColor.r,_clearColor.g,_clearColor.b,_clearAlpha);_cullEnabled=!0;this.context=h;var ia=h.getParameter(h.MAX_VERTEX_TEXTURE_IMAGE_UNITS)>0;if(_stencil){var D={};D.vertices=new Float32Array(12);D.faces=new Uint16Array(6);D.darkness=0.5;D.vertices[0]=-20;D.vertices[1]=-20;D.vertices[2]=-1;D.vertices[3]=20;D.vertices[4]=-20;D.vertices[5]=-1;D.vertices[6]=20;D.vertices[7]=20;D.vertices[8]=-1;D.vertices[9]=-20;D.vertices[10]=20;D.vertices[11]= --1;D.faces[0]=0;D.faces[1]=1;D.faces[2]=2;D.faces[3]=0;D.faces[4]=2;D.faces[5]=3;D.vertexBuffer=h.createBuffer();D.elementBuffer=h.createBuffer();h.bindBuffer(h.ARRAY_BUFFER,D.vertexBuffer);h.bufferData(h.ARRAY_BUFFER,D.vertices,h.STATIC_DRAW);h.bindBuffer(h.ELEMENT_ARRAY_BUFFER,D.elementBuffer);h.bufferData(h.ELEMENT_ARRAY_BUFFER,D.faces,h.STATIC_DRAW);D.program=h.createProgram();h.attachShader(D.program,w("fragment",THREE.ShaderLib.shadowPost.fragmentShader));h.attachShader(D.program,w("vertex", -THREE.ShaderLib.shadowPost.vertexShader));h.linkProgram(D.program);D.vertexLocation=h.getAttribLocation(D.program,"position");D.projectionLocation=h.getUniformLocation(D.program,"projectionMatrix");D.darknessLocation=h.getUniformLocation(D.program,"darkness")}var H={};H.vertices=new Float32Array(16);H.faces=new Uint16Array(6);b=0;H.vertices[b++]=-1;H.vertices[b++]=-1;H.vertices[b++]=0;H.vertices[b++]=0;H.vertices[b++]=1;H.vertices[b++]=-1;H.vertices[b++]=1;H.vertices[b++]=0;H.vertices[b++]=1;H.vertices[b++]= -1;H.vertices[b++]=1;H.vertices[b++]=1;H.vertices[b++]=-1;H.vertices[b++]=1;H.vertices[b++]=0;H.vertices[b++]=1;b=0;H.faces[b++]=0;H.faces[b++]=1;H.faces[b++]=2;H.faces[b++]=0;H.faces[b++]=2;H.faces[b++]=3;H.vertexBuffer=h.createBuffer();H.elementBuffer=h.createBuffer();H.tempTexture=h.createTexture();H.occlusionTexture=h.createTexture();h.bindBuffer(h.ARRAY_BUFFER,H.vertexBuffer);h.bufferData(h.ARRAY_BUFFER,H.vertices,h.STATIC_DRAW);h.bindBuffer(h.ELEMENT_ARRAY_BUFFER,H.elementBuffer);h.bufferData(h.ELEMENT_ARRAY_BUFFER, -H.faces,h.STATIC_DRAW);h.bindTexture(h.TEXTURE_2D,H.tempTexture);h.texImage2D(h.TEXTURE_2D,0,h.RGB,16,16,0,h.RGB,h.UNSIGNED_BYTE,null);h.texParameteri(h.TEXTURE_2D,h.TEXTURE_WRAP_S,h.CLAMP_TO_EDGE);h.texParameteri(h.TEXTURE_2D,h.TEXTURE_WRAP_T,h.CLAMP_TO_EDGE);h.texParameteri(h.TEXTURE_2D,h.TEXTURE_MAG_FILTER,h.NEAREST);h.texParameteri(h.TEXTURE_2D,h.TEXTURE_MIN_FILTER,h.NEAREST);h.bindTexture(h.TEXTURE_2D,H.occlusionTexture);h.texImage2D(h.TEXTURE_2D,0,h.RGBA,16,16,0,h.RGBA,h.UNSIGNED_BYTE,null); -h.texParameteri(h.TEXTURE_2D,h.TEXTURE_WRAP_S,h.CLAMP_TO_EDGE);h.texParameteri(h.TEXTURE_2D,h.TEXTURE_WRAP_T,h.CLAMP_TO_EDGE);h.texParameteri(h.TEXTURE_2D,h.TEXTURE_MAG_FILTER,h.NEAREST);h.texParameteri(h.TEXTURE_2D,h.TEXTURE_MIN_FILTER,h.NEAREST);h.getParameter(h.MAX_VERTEX_TEXTURE_IMAGE_UNITS)<=0?(H.hasVertexTexture=!1,H.program=h.createProgram(),h.attachShader(H.program,w("fragment",THREE.ShaderLib.lensFlare.fragmentShader)),h.attachShader(H.program,w("vertex",THREE.ShaderLib.lensFlare.vertexShader))): -(H.hasVertexTexture=!0,H.program=h.createProgram(),h.attachShader(H.program,w("fragment",THREE.ShaderLib.lensFlareVertexTexture.fragmentShader)),h.attachShader(H.program,w("vertex",THREE.ShaderLib.lensFlareVertexTexture.vertexShader)));h.linkProgram(H.program);H.attributes={};H.uniforms={};H.attributes.vertex=h.getAttribLocation(H.program,"position");H.attributes.uv=h.getAttribLocation(H.program,"UV");H.uniforms.renderType=h.getUniformLocation(H.program,"renderType");H.uniforms.map=h.getUniformLocation(H.program, -"map");H.uniforms.occlusionMap=h.getUniformLocation(H.program,"occlusionMap");H.uniforms.opacity=h.getUniformLocation(H.program,"opacity");H.uniforms.scale=h.getUniformLocation(H.program,"scale");H.uniforms.rotation=h.getUniformLocation(H.program,"rotation");H.uniforms.screenPosition=h.getUniformLocation(H.program,"screenPosition");var $=!1;_sprite={};_sprite.vertices=new Float32Array(16);_sprite.faces=new Uint16Array(6);b=0;_sprite.vertices[b++]=-1;_sprite.vertices[b++]=-1;_sprite.vertices[b++]= -0;_sprite.vertices[b++]=0;_sprite.vertices[b++]=1;_sprite.vertices[b++]=-1;_sprite.vertices[b++]=1;_sprite.vertices[b++]=0;_sprite.vertices[b++]=1;_sprite.vertices[b++]=1;_sprite.vertices[b++]=1;_sprite.vertices[b++]=1;_sprite.vertices[b++]=-1;_sprite.vertices[b++]=1;_sprite.vertices[b++]=0;_sprite.vertices[b++]=1;b=0;_sprite.faces[b++]=0;_sprite.faces[b++]=1;_sprite.faces[b++]=2;_sprite.faces[b++]=0;_sprite.faces[b++]=2;_sprite.faces[b++]=3;_sprite.vertexBuffer=h.createBuffer();_sprite.elementBuffer= -h.createBuffer();h.bindBuffer(h.ARRAY_BUFFER,_sprite.vertexBuffer);h.bufferData(h.ARRAY_BUFFER,_sprite.vertices,h.STATIC_DRAW);h.bindBuffer(h.ELEMENT_ARRAY_BUFFER,_sprite.elementBuffer);h.bufferData(h.ELEMENT_ARRAY_BUFFER,_sprite.faces,h.STATIC_DRAW);_sprite.program=h.createProgram();h.attachShader(_sprite.program,w("fragment",THREE.ShaderLib.sprite.fragmentShader));h.attachShader(_sprite.program,w("vertex",THREE.ShaderLib.sprite.vertexShader));h.linkProgram(_sprite.program);_sprite.attributes={}; -_sprite.uniforms={};_sprite.attributes.position=h.getAttribLocation(_sprite.program,"position");_sprite.attributes.uv=h.getAttribLocation(_sprite.program,"uv");_sprite.uniforms.uvOffset=h.getUniformLocation(_sprite.program,"uvOffset");_sprite.uniforms.uvScale=h.getUniformLocation(_sprite.program,"uvScale");_sprite.uniforms.rotation=h.getUniformLocation(_sprite.program,"rotation");_sprite.uniforms.scale=h.getUniformLocation(_sprite.program,"scale");_sprite.uniforms.alignment=h.getUniformLocation(_sprite.program, -"alignment");_sprite.uniforms.map=h.getUniformLocation(_sprite.program,"map");_sprite.uniforms.opacity=h.getUniformLocation(_sprite.program,"opacity");_sprite.uniforms.useScreenCoordinates=h.getUniformLocation(_sprite.program,"useScreenCoordinates");_sprite.uniforms.affectedByDistance=h.getUniformLocation(_sprite.program,"affectedByDistance");_sprite.uniforms.screenPosition=h.getUniformLocation(_sprite.program,"screenPosition");_sprite.uniforms.modelViewMatrix=h.getUniformLocation(_sprite.program, -"modelViewMatrix");_sprite.uniforms.projectionMatrix=h.getUniformLocation(_sprite.program,"projectionMatrix");var ca=!1;this.setSize=function(b,d){_canvas.width=b;_canvas.height=d;this.setViewport(0,0,_canvas.width,_canvas.height)};this.setViewport=function(b,d,c,e){_viewportX=b;_viewportY=d;_viewportWidth=c;_viewportHeight=e;h.viewport(_viewportX,_viewportY,_viewportWidth,_viewportHeight)};this.setScissor=function(b,d,c,e){h.scissor(b,d,c,e)};this.enableScissorTest=function(b){b?h.enable(h.SCISSOR_TEST): -h.disable(h.SCISSOR_TEST)};this.enableDepthBufferWrite=function(b){Q=b;h.depthMask(b)};this.setClearColorHex=function(b,d){_clearColor.setHex(b);_clearAlpha=d;h.clearColor(_clearColor.r,_clearColor.g,_clearColor.b,_clearAlpha)};this.setClearColor=function(b,d){_clearColor.copy(b);_clearAlpha=d;h.clearColor(_clearColor.r,_clearColor.g,_clearColor.b,_clearAlpha)};this.clear=function(){h.clear(h.COLOR_BUFFER_BIT|h.DEPTH_BUFFER_BIT|h.STENCIL_BUFFER_BIT)};this.setStencilShadowDarkness=function(b){D.darkness= -b};this.getContext=function(){return h};this.initMaterial=function(b,d,c,e){var g,f,j;b instanceof THREE.MeshDepthMaterial?j="depth":b instanceof THREE.ShadowVolumeDynamicMaterial?j="shadowVolumeDynamic":b instanceof THREE.MeshNormalMaterial?j="normal":b instanceof THREE.MeshBasicMaterial?j="basic":b instanceof THREE.MeshLambertMaterial?j="lambert":b instanceof THREE.MeshPhongMaterial?j="phong":b instanceof THREE.LineBasicMaterial?j="basic":b instanceof THREE.ParticleBasicMaterial&&(j="particle_basic"); -if(j){var k=THREE.ShaderLib[j];b.uniforms=THREE.UniformsUtils.clone(k.uniforms);b.vertexShader=k.vertexShader;b.fragmentShader=k.fragmentShader}var n,m,p;n=p=k=0;for(m=d.length;n=0&&h.enableVertexAttribArray(o.position);o.color>=0&&h.enableVertexAttribArray(o.color);o.normal>= -0&&h.enableVertexAttribArray(o.normal);o.tangent>=0&&h.enableVertexAttribArray(o.tangent);b.skinning&&o.skinVertexA>=0&&o.skinVertexB>=0&&o.skinIndex>=0&&o.skinWeight>=0&&(h.enableVertexAttribArray(o.skinVertexA),h.enableVertexAttribArray(o.skinVertexB),h.enableVertexAttribArray(o.skinIndex),h.enableVertexAttribArray(o.skinWeight));if(b.attributes)for(g in b.attributes)o[g]!==void 0&&o[g]>=0&&h.enableVertexAttribArray(o[g]);if(b.morphTargets){b.numSupportedMorphTargets=0;o.morphTarget0>=0&&(h.enableVertexAttribArray(o.morphTarget0), -b.numSupportedMorphTargets++);o.morphTarget1>=0&&(h.enableVertexAttribArray(o.morphTarget1),b.numSupportedMorphTargets++);o.morphTarget2>=0&&(h.enableVertexAttribArray(o.morphTarget2),b.numSupportedMorphTargets++);o.morphTarget3>=0&&(h.enableVertexAttribArray(o.morphTarget3),b.numSupportedMorphTargets++);o.morphTarget4>=0&&(h.enableVertexAttribArray(o.morphTarget4),b.numSupportedMorphTargets++);o.morphTarget5>=0&&(h.enableVertexAttribArray(o.morphTarget5),b.numSupportedMorphTargets++);o.morphTarget6>= -0&&(h.enableVertexAttribArray(o.morphTarget6),b.numSupportedMorphTargets++);o.morphTarget7>=0&&(h.enableVertexAttribArray(o.morphTarget7),b.numSupportedMorphTargets++);e.__webglMorphTargetInfluences=new Float32Array(this.maxMorphTargets);b=0;for(g=this.maxMorphTargets;b0||t.faceVertexUvs.length>0)j.__uvArray=new Float32Array(n*2);if(t.faceUvs.length>1||t.faceVertexUvs.length>1)j.__uv2Array=new Float32Array(n*2)}if(k.geometry.skinWeights.length&&k.geometry.skinIndices.length)j.__skinVertexAArray=new Float32Array(n*4),j.__skinVertexBArray=new Float32Array(n*4),j.__skinIndexArray=new Float32Array(n*4),j.__skinWeightArray=new Float32Array(n*4);j.__faceArray=new Uint16Array(v*3+(k.geometry.edgeFaces?k.geometry.edgeFaces.length* -6:0));j.__lineArray=new Uint16Array(z*2);if(j.numMorphTargets){j.__morphTargetsArrays=[];t=0;for(u=j.numMorphTargets;t1&&(d-=1)}c===void 0&&(c={h:0,s:0,v:0});c.h=d;c.s=j;c.v=g;return c}, -clamp:function(b,c,d){return bd?d:b}};THREE.ColorUtils.__hsv={h:0,s:0,v:0}; -var GeometryUtils={merge:function(b,c){var d=c instanceof THREE.Mesh,e=b.vertices.length,f=d?c.geometry:c,g=b.vertices,j=f.vertices,k=b.faces,m=f.faces,o=b.faceVertexUvs[0],f=f.faceVertexUvs[0];d&&c.matrixAutoUpdate&&c.updateMatrix();for(var n=0,t=j.length;n= 0.0 )\npointSpecularWeight = specularTex.r * pow( pointDotNormalHalf, uShininess );\npointDiffuse += vec4( uDiffuseColor, 1.0 ) * pointDiffuseWeight;\npointSpecular += vec4( uSpecularColor, 1.0 ) * pointSpecularWeight * pointDiffuseWeight;\nvec4 dirDiffuse = vec4( 0.0, 0.0, 0.0, 0.0 );\nvec4 dirSpecular = vec4( 0.0, 0.0, 0.0, 0.0 );\nvec4 lDirection = viewMatrix * vec4( uDirLightPos, 0.0 );\nvec3 dirVector = normalize( lDirection.xyz );\nvec3 dirHalfVector = normalize( lDirection.xyz + vViewPosition );\nfloat dirDotNormalHalf = dot( normal, dirHalfVector );\nfloat dirDiffuseWeight = max( dot( normal, dirVector ), 0.0 );\nfloat dirSpecularWeight = 0.0;\nif ( dirDotNormalHalf >= 0.0 )\ndirSpecularWeight = specularTex.r * pow( dirDotNormalHalf, uShininess );\ndirDiffuse += vec4( uDiffuseColor, 1.0 ) * dirDiffuseWeight;\ndirSpecular += vec4( uSpecularColor, 1.0 ) * dirSpecularWeight * dirDiffuseWeight;\nvec4 totalLight = vec4( uAmbientLightColor * uAmbientColor, 1.0 );\ntotalLight += vec4( uDirLightColor, 1.0 ) * ( dirDiffuse + dirSpecular );\ntotalLight += vec4( uPointLightColor, 1.0 ) * ( pointDiffuse + pointSpecular );\ngl_FragColor = vec4( totalLight.xyz * aoTex * diffuseTex, 1.0 );\n}", -vertexShader:"attribute vec4 tangent;\nuniform vec3 uPointLightPos;\n#ifdef VERTEX_TEXTURES\nuniform sampler2D tDisplacement;\nuniform float uDisplacementScale;\nuniform float uDisplacementBias;\n#endif\nvarying vec3 vTangent;\nvarying vec3 vBinormal;\nvarying vec3 vNormal;\nvarying vec2 vUv;\nvarying vec3 vPointLightVector;\nvarying vec3 vViewPosition;\nvoid main() {\nvec4 mPosition = objectMatrix * vec4( position, 1.0 );\nvViewPosition = cameraPosition - mPosition.xyz;\nvec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\nvNormal = normalize( normalMatrix * normal );\nvTangent = normalize( normalMatrix * tangent.xyz );\nvBinormal = cross( vNormal, vTangent ) * tangent.w;\nvBinormal = normalize( vBinormal );\nvUv = uv;\nvec4 lPosition = viewMatrix * vec4( uPointLightPos, 1.0 );\nvPointLightVector = normalize( lPosition.xyz - mvPosition.xyz );\n#ifdef VERTEX_TEXTURES\nvec3 dv = texture2D( tDisplacement, uv ).xyz;\nfloat df = uDisplacementScale * dv.x + uDisplacementBias;\nvec4 displacedPosition = vec4( vNormal.xyz * df, 0.0 ) + mvPosition;\ngl_Position = projectionMatrix * displacedPosition;\n#else\ngl_Position = projectionMatrix * mvPosition;\n#endif\n}"}, -cube:{uniforms:{tCube:{type:"t",value:1,texture:null}},vertexShader:"varying vec3 vViewPosition;\nvoid main() {\nvec4 mPosition = objectMatrix * vec4( position, 1.0 );\nvViewPosition = cameraPosition - mPosition.xyz;\ngl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}",fragmentShader:"uniform samplerCube tCube;\nvarying vec3 vViewPosition;\nvoid main() {\nvec3 wPos = cameraPosition - vViewPosition;\ngl_FragColor = textureCube( tCube, vec3( - wPos.x, wPos.yz ) );\n}"},convolution:{uniforms:{tDiffuse:{type:"t", -value:0,texture:null},uImageIncrement:{type:"v2",value:new THREE.Vector2(0.001953125,0)},cKernel:{type:"fv1",value:[]}},vertexShader:"varying vec2 vUv;\nuniform vec2 uImageIncrement;\nvoid main(void) {\nvUv = uv - ((KERNEL_SIZE - 1.0) / 2.0) * uImageIncrement;\ngl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}",fragmentShader:"varying vec2 vUv;\nuniform sampler2D tDiffuse;\nuniform vec2 uImageIncrement;\nuniform float cKernel[KERNEL_SIZE];\nvoid main(void) {\nvec2 imageCoord = vUv;\nvec4 sum = vec4( 0.0, 0.0, 0.0, 0.0 );\nfor( int i=0; i25&&(g=25);f=(g-1)*0.5;d=Array(g);for(c=e=0;c1)console.log("THREE.Animation.update: Warning! Scale out of bounds:"+e+" on bone "+u),e=e<0?0:1;if(d==="pos")if(d=b.position,this.interpolationType===THREE.AnimationHandler.LINEAR)d.x=f[0]+(g[0]-f[0])*e,d.y=f[1]+(g[1]-f[1])*e,d.z=f[2]+(g[2]-f[2])*e;else{if(this.interpolationType===THREE.AnimationHandler.CATMULLROM||this.interpolationType===THREE.AnimationHandler.CATMULLROM_FORWARD)if(this.points[0]= -this.getPrevKeyWith("pos",u,j.index-1).pos,this.points[1]=f,this.points[2]=g,this.points[3]=this.getNextKeyWith("pos",u,k.index+1).pos,e=e*0.33+0.33,f=this.interpolateCatmullRom(this.points,e),d.x=f[0],d.y=f[1],d.z=f[2],this.interpolationType===THREE.AnimationHandler.CATMULLROM_FORWARD)e=this.interpolateCatmullRom(this.points,e*1.01),this.target.set(e[0],e[1],e[2]),this.target.subSelf(d),this.target.y=0,this.target.normalize(),e=Math.atan2(this.target.x,this.target.z),b.rotation.set(0,e,0)}else if(d=== -"rot")THREE.Quaternion.slerp(f,g,b.quaternion,e);else if(d==="scl")d=b.scale,d.x=f[0]+(g[0]-f[0])*e,d.y=f[1]+(g[1]-f[1])*e,d.z=f[2]+(g[2]-f[2])*e}}if(this.JITCompile&&n[0][o]===void 0){this.hierarchy[0].update(void 0,!0);for(u=0;ub.length-2?g:g+1;d[3]=g>b.length-3?g:g+2;g=b[d[0]];k=b[d[1]];m=b[d[2]];o=b[d[3]];d=f*f;j=f*d;e[0]=this.interpolate(g[0],k[0],m[0],o[0],f,d,j);e[1]=this.interpolate(g[1],k[1],m[1],o[1],f,d,j);e[2]=this.interpolate(g[2],k[2],m[2],o[2],f,d,j);return e}; -THREE.Animation.prototype.interpolate=function(b,c,d,e,f,g,j){b=(d-b)*0.5;e=(e-c)*0.5;return(2*(c-d)+b+e)*j+(-3*(c-d)-2*b-e)*g+b*f+c};THREE.Animation.prototype.getNextKeyWith=function(b,c,d){var e=this.data.hierarchy[c].keys;for(this.interpolationType===THREE.AnimationHandler.CATMULLROM||this.interpolationType===THREE.AnimationHandler.CATMULLROM_FORWARD?d=d0?d:0:d>=0?d:d+e.length;d>=0;d--)if(e[d][b]!==void 0)return e[d];return this.data.hierarchy[c].keys[e.length-1]}; -THREE.FPCamera=function(b){function c(b,c){return function(){c.apply(b,arguments)}}THREE.Camera.call(this,b.fov,b.aspect,b.near,b.far,b.target);this.movementSpeed=1;this.lookSpeed=0.005;this.noFly=!1;this.lookVertical=!0;this.autoForward=!1;this.activeLook=!0;this.heightSpeed=!1;this.heightCoef=1;this.heightMin=0;this.constrainVertical=!1;this.verticalMin=0;this.verticalMax=3.14;this.domElement=document;this.lastUpdate=(new Date).getTime();this.tdiff=0;if(b){if(b.movementSpeed!==void 0)this.movementSpeed= -b.movementSpeed;if(b.lookSpeed!==void 0)this.lookSpeed=b.lookSpeed;if(b.noFly!==void 0)this.noFly=b.noFly;if(b.lookVertical!==void 0)this.lookVertical=b.lookVertical;if(b.autoForward!==void 0)this.autoForward=b.autoForward;if(b.activeLook!==void 0)this.activeLook=b.activeLook;if(b.heightSpeed!==void 0)this.heightSpeed=b.heightSpeed;if(b.heightCoef!==void 0)this.heightCoef=b.heightCoef;if(b.heightMin!==void 0)this.heightMin=b.heightMin;if(b.heightMax!==void 0)this.heightMax=b.heightMax;if(b.constrainVertical!== -void 0)this.constrainVertical=b.constrainVertical;if(b.verticalMin!==void 0)this.verticalMin=b.verticalMin;if(b.verticalMax!==void 0)this.verticalMax=b.verticalMax;if(b.domElement!==void 0)this.domElement=b.domElement}this.theta=this.phi=this.lon=this.lat=this.mouseY=this.mouseX=this.autoSpeedFactor=0;this.mouseDragOn=this.freeze=this.moveRight=this.moveLeft=this.moveBackward=this.moveForward=!1;this.windowHalfX=window.innerWidth/2;this.windowHalfY=window.innerHeight/2;this.onMouseDown=function(b){b.preventDefault(); -b.stopPropagation();if(this.activeLook)switch(b.button){case 0:this.moveForward=!0;break;case 2:this.moveBackward=!0}this.mouseDragOn=!0};this.onMouseUp=function(b){b.preventDefault();b.stopPropagation();if(this.activeLook)switch(b.button){case 0:this.moveForward=!1;break;case 2:this.moveBackward=!1}this.mouseDragOn=!1};this.onMouseMove=function(b){this.mouseX=b.clientX-this.windowHalfX;this.mouseY=b.clientY-this.windowHalfY};this.onKeyDown=function(b){switch(b.keyCode){case 38:case 87:this.moveForward= -!0;break;case 37:case 65:this.moveLeft=!0;break;case 40:case 83:this.moveBackward=!0;break;case 39:case 68:this.moveRight=!0;break;case 81:this.freeze=!this.freeze}};this.onKeyUp=function(b){switch(b.keyCode){case 38:case 87:this.moveForward=!1;break;case 37:case 65:this.moveLeft=!1;break;case 40:case 83:this.moveBackward=!1;break;case 39:case 68:this.moveRight=!1}};this.update=function(){var b=(new Date).getTime();this.tdiff=(b-this.lastUpdate)/1E3;this.lastUpdate=b;if(!this.freeze){this.autoSpeedFactor= -this.heightSpeed?this.tdiff*((this.position.ythis.heightMax?this.heightMax:this.position.y)-this.heightMin)*this.heightCoef:0;var c=this.tdiff*this.movementSpeed;(this.moveForward||this.autoForward&&!this.moveBackward)&&this.translateZ(-(c+this.autoSpeedFactor));this.moveBackward&&this.translateZ(c);this.moveLeft&&this.translateX(-c);this.moveRight&&this.translateX(c);c=this.tdiff*this.lookSpeed;this.activeLook||(c=0);this.lon+=this.mouseX*c;this.lookVertical&& -(this.lat-=this.mouseY*c);this.lat=Math.max(-85,Math.min(85,this.lat));this.phi=(90-this.lat)*Math.PI/180;this.theta=this.lon*Math.PI/180;var b=this.target.position,f=this.position;b.x=f.x+100*Math.sin(this.phi)*Math.cos(this.theta);b.y=f.y+100*Math.cos(this.phi);b.z=f.z+100*Math.sin(this.phi)*Math.sin(this.theta)}this.lon+=this.mouseX*c;this.lookVertical&&(this.lat-=this.mouseY*c);this.lat=Math.max(-85,Math.min(85,this.lat));this.phi=(90-this.lat)*Math.PI/180;this.theta=this.lon*Math.PI/180;if(this.constrainVertical)this.phi= -(this.phi-0)*(this.verticalMax-this.verticalMin)/3.14+this.verticalMin;b=this.target.position;f=this.position;b.x=f.x+100*Math.sin(this.phi)*Math.cos(this.theta);b.y=f.y+100*Math.cos(this.phi);b.z=f.z+100*Math.sin(this.phi)*Math.sin(this.theta);this.supr.update.call(this)};this.domElement.addEventListener("contextmenu",function(b){b.preventDefault()},!1);this.domElement.addEventListener("mousemove",c(this,this.onMouseMove),!1);this.domElement.addEventListener("mousedown",c(this,this.onMouseDown), -!1);this.domElement.addEventListener("mouseup",c(this,this.onMouseUp),!1);this.domElement.addEventListener("keydown",c(this,this.onKeyDown),!1);this.domElement.addEventListener("keyup",c(this,this.onKeyUp),!1)};THREE.FPCamera.prototype=new THREE.Camera;THREE.FPCamera.prototype.constructor=THREE.FPCamera;THREE.FPCamera.prototype.supr=THREE.Camera.prototype;THREE.FPCamera.prototype.translate=function(b,c){this.matrix.rotateAxis(c);if(this.noFly)c.y=0;this.position.addSelf(c.multiplyScalar(b));this.target.position.addSelf(c.multiplyScalar(b))}; -THREE.PathCamera=function(b){function c(b,d,c,e){var g={name:c,fps:0.6,length:e,hierarchy:[]},f,j=d.getControlPointsArray(),k=d.getLength(),m=j.length,I=0;f=m-1;d={parent:-1,keys:[]};d.keys[0]={time:0,pos:j[0],rot:[0,0,0,1],scl:[1,1,1]};d.keys[f]={time:e,pos:j[f],rot:[0,0,0,1],scl:[1,1,1]};for(f=1;f=0?e:e+f;e=this.verticalAngleMap.srcRange;j=this.verticalAngleMap.dstRange;var k=j[1]-j[0];this.phi= -TWEEN.Easing.Quadratic.EaseInOut(((this.phi-e[0])*(j[1]-j[0])/(e[1]-e[0])+j[0]-j[0])/k)*k+j[0];e=this.horizontalAngleMap.srcRange;j=this.horizontalAngleMap.dstRange;k=j[1]-j[0];this.theta=TWEEN.Easing.Quadratic.EaseInOut(((this.theta-e[0])*(j[1]-j[0])/(e[1]-e[0])+j[0]-j[0])/k)*k+j[0];e=this.target.position;e.x=100*Math.sin(this.phi)*Math.cos(this.theta);e.y=100*Math.cos(this.phi);e.z=100*Math.sin(this.phi)*Math.sin(this.theta);this.supr.update.call(this,b,d,c)};this.onMouseMove=function(b){this.mouseX= -b.clientX-this.windowHalfX;this.mouseY=b.clientY-this.windowHalfY};this.spline=new THREE.Spline;this.spline.initFromArray(this.waypoints);this.useConstantSpeed&&this.spline.reparametrizeByArcLength(this.resamplingCoef);if(this.createDebugDummy){var b=new THREE.MeshLambertMaterial({color:30719}),j=new THREE.MeshLambertMaterial({color:65280}),k=new THREE.CubeGeometry(10,10,20),m=new THREE.CubeGeometry(2,2,10);this.animationParent=new THREE.Mesh(k,b);b=new THREE.Mesh(m,j);b.position.set(0,10,0);this.animation= -c(this.animationParent,this.spline,this.id,this.duration);this.animationParent.addChild(this);this.animationParent.addChild(this.target);this.animationParent.addChild(b)}else this.animation=c(this.animationParent,this.spline,this.id,this.duration),this.animationParent.addChild(this.target),this.animationParent.addChild(this);this.createDebugPath&&e(this.debugPath,this.spline);this.domElement.addEventListener("mousemove",function(b,d){return function(){d.apply(b,arguments)}}(this,this.onMouseMove), -!1)};THREE.PathCamera.prototype=new THREE.Camera;THREE.PathCamera.prototype.constructor=THREE.PathCamera;THREE.PathCamera.prototype.supr=THREE.Camera.prototype;THREE.PathCameraIdCounter=0; -THREE.FlyCamera=function(b){function c(b,c){return function(){c.apply(b,arguments)}}THREE.Camera.call(this,b.fov,b.aspect,b.near,b.far,b.target);this.tmpQuaternion=new THREE.Quaternion;this.movementSpeed=1;this.rollSpeed=0.005;this.autoForward=this.dragToLook=!1;this.domElement=document;if(b){if(b.movementSpeed!==void 0)this.movementSpeed=b.movementSpeed;if(b.rollSpeed!==void 0)this.rollSpeed=b.rollSpeed;if(b.dragToLook!==void 0)this.dragToLook=b.dragToLook;if(b.autoForward!==void 0)this.autoForward= -b.autoForward;if(b.domElement!==void 0)this.domElement=b.domElement}this.useTarget=!1;this.useQuaternion=!0;this.mouseStatus=0;this.moveState={up:0,down:0,left:0,right:0,forward:0,back:0,pitchUp:0,pitchDown:0,yawLeft:0,yawRight:0,rollLeft:0,rollRight:0};this.moveVector=new THREE.Vector3(0,0,0);this.rotationVector=new THREE.Vector3(0,0,0);this.lastUpdate=-1;this.tdiff=0;this.handleEvent=function(b){if(typeof this[b.type]=="function")this[b.type](b)};this.keydown=function(b){if(!b.altKey){switch(b.keyCode){case 16:this.movementSpeedMultiplier= -0.1;break;case 87:this.moveState.forward=1;break;case 83:this.moveState.back=1;break;case 65:this.moveState.left=1;break;case 68:this.moveState.right=1;break;case 82:this.moveState.up=1;break;case 70:this.moveState.down=1;break;case 38:this.moveState.pitchUp=1;break;case 40:this.moveState.pitchDown=1;break;case 37:this.moveState.yawLeft=1;break;case 39:this.moveState.yawRight=1;break;case 81:this.moveState.rollLeft=1;break;case 69:this.moveState.rollRight=1}this.updateMovementVector();this.updateRotationVector()}}; -this.keyup=function(b){switch(b.keyCode){case 16:this.movementSpeedMultiplier=1;break;case 87:this.moveState.forward=0;break;case 83:this.moveState.back=0;break;case 65:this.moveState.left=0;break;case 68:this.moveState.right=0;break;case 82:this.moveState.up=0;break;case 70:this.moveState.down=0;break;case 38:this.moveState.pitchUp=0;break;case 40:this.moveState.pitchDown=0;break;case 37:this.moveState.yawLeft=0;break;case 39:this.moveState.yawRight=0;break;case 81:this.moveState.rollLeft=0;break; -case 69:this.moveState.rollRight=0}this.updateMovementVector();this.updateRotationVector()};this.mousedown=function(b){b.preventDefault();b.stopPropagation();if(this.dragToLook)this.mouseStatus++;else switch(b.button){case 0:this.moveForward=!0;break;case 2:this.moveBackward=!0}};this.mousemove=function(b){if(!this.dragToLook||this.mouseStatus>0){var c=this.getContainerDimensions(),f=c.size[0]/2,g=c.size[1]/2;this.moveState.yawLeft=-(b.clientX-c.offset[0]-f)/f;this.moveState.pitchDown=(b.clientY- -c.offset[1]-g)/g;this.updateRotationVector()}};this.mouseup=function(b){b.preventDefault();b.stopPropagation();if(this.dragToLook)this.mouseStatus--,this.moveState.yawLeft=this.moveState.pitchDown=0;else switch(b.button){case 0:this.moveForward=!1;break;case 2:this.moveBackward=!1}this.updateRotationVector()};this.update=function(){var b=(new Date).getTime();if(this.lastUpdate==-1)this.lastUpdate=b;this.tdiff=(b-this.lastUpdate)/1E3;this.lastUpdate=b;var b=this.tdiff*this.movementSpeed,c=this.tdiff* -this.rollSpeed;this.translateX(this.moveVector.x*b);this.translateY(this.moveVector.y*b);this.translateZ(this.moveVector.z*b);this.tmpQuaternion.set(this.rotationVector.x*c,this.rotationVector.y*c,this.rotationVector.z*c,1).normalize();this.quaternion.multiplySelf(this.tmpQuaternion);this.matrix.setPosition(this.position);this.matrix.setRotationFromQuaternion(this.quaternion);this.matrixWorldNeedsUpdate=!0;this.supr.update.call(this)};this.updateMovementVector=function(){var b=this.moveState.forward|| -this.autoForward&&!this.moveState.back?1:0;this.moveVector.x=-this.moveState.left+this.moveState.right;this.moveVector.y=-this.moveState.down+this.moveState.up;this.moveVector.z=-b+this.moveState.back};this.updateRotationVector=function(){this.rotationVector.x=-this.moveState.pitchDown+this.moveState.pitchUp;this.rotationVector.y=-this.moveState.yawRight+this.moveState.yawLeft;this.rotationVector.z=-this.moveState.rollRight+this.moveState.rollLeft};this.getContainerDimensions=function(){return this.domElement!= -document?{size:[this.domElement.offsetWidth,this.domElement.offsetHeight],offset:[this.domElement.offsetLeft,this.domElement.offsetTop]}:{size:[window.innerWidth,window.innerHeight],offset:[0,0]}};this.domElement.addEventListener("mousemove",c(this,this.mousemove),!1);this.domElement.addEventListener("mousedown",c(this,this.mousedown),!1);this.domElement.addEventListener("mouseup",c(this,this.mouseup),!1);window.addEventListener("keydown",c(this,this.keydown),!1);window.addEventListener("keyup",c(this, -this.keyup),!1);this.updateMovementVector();this.updateRotationVector()};THREE.FlyCamera.prototype=new THREE.Camera;THREE.FlyCamera.prototype.constructor=THREE.FlyCamera;THREE.FlyCamera.prototype.supr=THREE.Camera.prototype; -THREE.RollCamera=function(b,c,d,e){THREE.Camera.call(this,b,c,d,e);this.mouseLook=!0;this.autoForward=!1;this.rollSpeed=this.movementSpeed=this.lookSpeed=1;this.constrainVertical=[-0.9,0.9];this.domElement=document;this.matrixAutoUpdate=this.useTarget=!1;this.forward=new THREE.Vector3(0,0,1);this.roll=0;this.lastUpdate=-1;this.delta=0;var f=new THREE.Vector3,g=new THREE.Vector3,j=new THREE.Vector3,k=new THREE.Matrix4,m=!1,o=1,n=0,t=0,p=0,u=0,v=0,y=window.innerWidth/2,B=window.innerHeight/2;this.update= -function(){var b=(new Date).getTime();if(this.lastUpdate==-1)this.lastUpdate=b;this.delta=(b-this.lastUpdate)/1E3;this.lastUpdate=b;this.mouseLook&&(b=this.delta*this.lookSpeed,this.rotateHorizontally(b*u),this.rotateVertically(b*v));b=this.delta*this.movementSpeed;this.translateZ(b*(n>0||this.autoForward&&!(n<0)?1:n));this.translateX(b*t);this.translateY(b*p);m&&(this.roll+=this.rollSpeed*this.delta*o);if(this.forward.y>this.constrainVertical[1])this.forward.y=this.constrainVertical[1],this.forward.normalize(); -else if(this.forward.y1?d.normalize():d.z=Math.sqrt(1-e*e);f=this.position.clone().subSelf(this.target.position);e=this.up.clone().setLength(d.y);e.addSelf(this.up.clone().crossSelf(f).setLength(d.x));e.addSelf(f.setLength(d.z));return e}; -this.rotateCamera=function(){var b=Math.acos(g.dot(j)/g.length()/j.length());if(b){var c=(new THREE.Vector3).cross(g,j).normalize(),d=new THREE.Quaternion;b*=this.rotateSpeed;d.setFromAxisAngle(c,-b);d.multiplyVector3(f);d.multiplyVector3(this.up);d.multiplyVector3(j);this.staticMoving?g=j:(d.setFromAxisAngle(c,b*(this.dynamicDampingFactor-1)),d.multiplyVector3(g))}};this.zoomCamera=function(){var b=1+(m.y-k.y)*this.zoomSpeed;b!==1&&b>0&&(f.multiplyScalar(b),this.staticMoving?k=m:k.y+=(m.y-k.y)*this.dynamicDampingFactor)}; -this.panCamera=function(){var b=n.clone().subSelf(o);if(b.lengthSq()){b.multiplyScalar(f.length()*this.panSpeed);var d=f.clone().crossSelf(this.up).setLength(b.x);d.addSelf(this.up.clone().setLength(b.y));this.position.addSelf(d);this.target.position.addSelf(d);this.staticMoving?o=n:o.addSelf(b.sub(n,o).multiplyScalar(this.dynamicDampingFactor))}};this.checkDistances=function(){if(!this.noZoom||!this.noPan)this.position.lengthSq()>this.maxDistance*this.maxDistance&&this.position.setLength(this.maxDistance), -f.lengthSq()0){j(0,0,-n-(g||0));for(m=b;m0){j(0,0,n+ -(f||0));for(m=b+b/2;m<2*b;m++)k.faces.push(new THREE.Face4(2*b+1,(2*m-2*b+2)%b+b,(2*m-2*b+1)%b+b,(2*m-2*b)%b+b))}m=0;for(b=this.faces.length;m0||(n=this.vertices.push(new THREE.Vertex(new THREE.Vector3(t,k,p)))-1);o.push(n)}c.push(o)}for(var u,v,y,f=c.length,d=0;d0)for(e=0;e1&&(u= -this.vertices[j].position.clone(),v=this.vertices[m].position.clone(),y=this.vertices[o].position.clone(),u.normalize(),v.normalize(),y.normalize(),this.faces.push(new THREE.Face3(j,m,o,[new THREE.Vector3(u.x,u.y,u.z),new THREE.Vector3(v.x,v.y,v.z),new THREE.Vector3(y.x,y.y,y.z)])),this.faceVertexUvs[0].push([n,t,B]))}this.computeCentroids();this.computeFaceNormals();this.computeVertexNormals();this.boundingSphere={radius:b}};THREE.SphereGeometry.prototype=new THREE.Geometry; -THREE.SphereGeometry.prototype.constructor=THREE.SphereGeometry;THREE.TextGeometry=function(b,c){THREE.Geometry.call(this);this.parameters=c||{};this.set(b)};THREE.TextGeometry.prototype=new THREE.Geometry;THREE.TextGeometry.prototype.constructor=THREE.TextGeometry; -THREE.TextGeometry.prototype.set=function(b,c){function d(b,d,c){v.vertices.push(new THREE.Vertex(new THREE.Vector3(b,d,c)))}function e(b,d,c,e){v.faces.push(new THREE.Face4(b,d,c,e))}this.text=b;var c=c||this.parameters,f=c.height!==void 0?c.height:50,g=c.curveSegments!==void 0?c.curveSegments:4,j=c.font!==void 0?c.font:"helvetiker",k=c.weight!==void 0?c.weight:"normal",m=c.style!==void 0?c.style:"normal",o=c.bezelThickness!==void 0?c.bezelThickness:10,n=c.bezelSize!==void 0?c.bezelSize:8,t=c.bezelEnabled!== -void 0?c.bezelEnabled:!1;THREE.FontUtils.size=c.size!==void 0?c.size:100;THREE.FontUtils.divisions=g;THREE.FontUtils.face=j;THREE.FontUtils.weight=k;THREE.FontUtils.style=m;THREE.FontUtils.bezelSize=n;var k=THREE.FontUtils.drawText(b),g=k.points,p=k.faces,j=k.contour,u=k.bezel,v=this;v.vertices=[];v.faces=[];for(var y,m=g.length,B=p.length,n=u.length,k=0;k0;){if(z){if(z.equals(j[k])){z=null;continue}}else z=j[k];o=m*2+k;p=o-1;e(o,p,p+n,o+n);for(t=0;t0;){if(z){if(z.equals(j[k])){z=null;continue}}else z=j[k];for(t=0;t0&&j.push({shape:f,holes:e}),f=t,e=[]):e.push(t):this.Triangulate.area(t)<0?(j.push({shape:t,holes:e}),e=[]):e.push(t);o++}n&&j.push({shape:f,holes:e})}var p,u,v,y,B,z;g=[];for(k=0;k=0?u-1:f.length-1;var t=p-1>=0?p-1:o.length-1,I=[];I.push(o[p]);I.push(f[u]);I.push(f[d]);v=this.Triangulate.area(I);var A=[];A.push(o[p]);A.push(o[t]);A.push(f[u]);B=this.Triangulate.area(A);y=u;z=p;u+=1;p+=-1;u<0&&(u+=f.length);u%=f.length;p<0&&(p+=o.length);p%=f.length;d=u-1>=0?u-1:f.length-1;t=p-1>=0?p-1:o.length- -1;I=[];I.push(o[p]);I.push(f[u]);I.push(f[d]);I=this.Triangulate.area(I);A=[];A.push(o[p]);A.push(o[t]);A.push(f[u]);A=this.Triangulate.area(A);v+B>I+A&&(u=y,p=z,u<0&&(u+=f.length),u%=f.length,p<0&&(p+=o.length),p%=f.length,d=u-1>=0?u-1:f.length-1,t=p-1>=0?p-1:o.length-1);v=f.slice(0,u);B=f.slice(u);y=o.slice(p);z=o.slice(0,p);g.push(o[p]);g.push(f[u]);g.push(f[d]);g.push(o[p]);g.push(o[t]);g.push(f[u]);f=v.concat(y).concat(z).concat(B)}m.shape=f}b=[];p=[];for(k=n=0;k=0;){if(o){if(o.equals(d[b])){o=null;m=this.Triangulate.area(e)>0;j.push(m);g.push(k.divideScalar(e.length));e=[];k=new THREE.Vector2;continue}}else o=d[b];k.addSelf(d[b]);e.push(d[b])}b=d.length;e=0;for(var n;--b>=0;)m=d[b],k=g[e],m=m.clone().subSelf(k),n=this.bezelSize/m.length(), -j[e]?n+=1:n=1-n,n=m.multiplyScalar(n).addSelf(k),f.unshift(n),o?o.equals(d[b])&&(o=null,e++):o=d[b];c.bezel=f;return c},b2p0:function(b,c){var d=1-b;return d*d*c},b2p1:function(b,c){return 2*(1-b)*b*c},b2p2:function(b,c){return b*b*c},b2:function(b,c,d,e){return this.b2p0(b,c)+this.b2p1(b,d)+this.b2p2(b,e)},b3p0:function(b,c){var d=1-b;return d*d*d*c},b3p1:function(b,c){var d=1-b;return 3*d*d*b*c},b3p2:function(b,c){return 3*(1-b)*b*b*c},b3p3:function(b,c){return b*b*b*c},b3:function(b,c,d,e,f){return this.b3p0(b, -c)+this.b3p1(b,d)+this.b3p2(b,e)+this.b3p3(b,f)},extractGlyphPoints:function(b,c,d,e){var f=[],g,j,k,m,o,n,t,p,u,v,y=c.glyphs[b]||c.glyphs[ctxt.options.fallbackCharacter];if(y){if(y.o){c=y._cachedOutline||(y._cachedOutline=y.o.split(" "));k=c.length;for(b=0;b0)for(o=0;o2;){if(t--<=0){console.log("Warning, unable to triangulate polygon!");if(e)return k;return g}m=o;f<=m&&(m=0);o=m+1;f<=o&&(o=0);n=o+1;f<=n&&(n=0);var p;a:{p=b;var u=m,v=o,y=n,B=f,z=j,I=void 0,A=void 0,G=void 0, -F=void 0,C=void 0,M=void 0,L=void 0,O=void 0,w=void 0,A=p[z[u]].x,G=p[z[u]].y,F=p[z[v]].x,C=p[z[v]].y,M=p[z[y]].x,L=p[z[y]].y;if(1.0E-10>(F-A)*(L-G)-(C-G)*(M-A))p=!1;else{for(I=0;I=0&&T>=0&&R>=0){p=!1;break a}}p=!0}}if(p){t= -j[m];p=j[o];u=j[n];g.push(b[t]);g.push(b[p]);g.push(b[u]);k.push([j[m],j[o],j[n]]);m=o;for(n=o+1;n>7)-127;e|=(g&127)<<16|f<<8;if(e==0&&j==-127)return 0;return(1-2*(h>>7))*(1+e*Math.pow(2,-23))*Math.pow(2,j)}function f(b,d){var c=n(b,d),e=n(b,d+1),g=n(b,d+2);return(n(b,d+3)<<24)+(g<<16)+(e<<8)+c}function m(b,d){var c=n(b,d);return(n(b,d+1)<<8)+c}function o(b,d){var c=n(b,d);return c>127?c-256:c}function n(b, -d){return b.charCodeAt(d)&255}function t(d){var c,e,g;c=f(b,d);e=f(b,d+C);g=f(b,d+M);d=m(b,d+L);THREE.BinaryLoader.prototype.f3(z,c,e,g,d)}function p(d){var c,e,g,h,j,n;c=f(b,d);e=f(b,d+C);g=f(b,d+M);h=m(b,d+L);j=f(b,d+O);n=f(b,d+w);d=f(b,d+W);THREE.BinaryLoader.prototype.f3n(z,G,c,e,g,h,j,n,d)}function u(d){var c,e,g,j;c=f(b,d);e=f(b,d+S);g=f(b,d+T);j=f(b,d+h);d=m(b,d+R);THREE.BinaryLoader.prototype.f4(z,c,e,g,j,d)}function v(d){var c,e,g,j,n,o,p,t;c=f(b,d);e=f(b,d+S);g=f(b,d+T);j=f(b,d+h);n=m(b, -d+R);o=f(b,d+J);p=f(b,d+P);t=f(b,d+Q);d=f(b,d+N);THREE.BinaryLoader.prototype.f4n(z,G,c,e,g,j,n,o,p,t,d)}function y(d){var c,e;c=f(b,d);e=f(b,d+Y);d=f(b,d+V);THREE.BinaryLoader.prototype.uv3(z.faceVertexUvs[0],F[c*2],F[c*2+1],F[e*2],F[e*2+1],F[d*2],F[d*2+1])}function B(d){var c,e,g;c=f(b,d);e=f(b,d+Z);g=f(b,d+U);d=f(b,d+ea);THREE.BinaryLoader.prototype.uv4(z.faceVertexUvs[0],F[c*2],F[c*2+1],F[e*2],F[e*2+1],F[g*2],F[g*2+1],F[d*2],F[d*2+1])}var z=this,I=0,A,G=[],F=[],C,M,L,O,w,W,S,T,h,R,J,P,Q,N,Y,V, -Z,U,ea,ia,D,H,$,ca,aa;THREE.Geometry.call(this);THREE.Loader.prototype.init_materials(z,e,d);A={signature:b.substr(I,8),header_bytes:n(b,I+8),vertex_coordinate_bytes:n(b,I+9),normal_coordinate_bytes:n(b,I+10),uv_coordinate_bytes:n(b,I+11),vertex_index_bytes:n(b,I+12),normal_index_bytes:n(b,I+13),uv_index_bytes:n(b,I+14),material_index_bytes:n(b,I+15),nvertices:f(b,I+16),nnormals:f(b,I+16+4),nuvs:f(b,I+16+8),ntri_flat:f(b,I+16+12),ntri_smooth:f(b,I+16+16),ntri_flat_uv:f(b,I+16+20),ntri_smooth_uv:f(b, -I+16+24),nquad_flat:f(b,I+16+28),nquad_smooth:f(b,I+16+32),nquad_flat_uv:f(b,I+16+36),nquad_smooth_uv:f(b,I+16+40)};I+=A.header_bytes;C=A.vertex_index_bytes;M=A.vertex_index_bytes*2;L=A.vertex_index_bytes*3;O=A.vertex_index_bytes*3+A.material_index_bytes;w=A.vertex_index_bytes*3+A.material_index_bytes+A.normal_index_bytes;W=A.vertex_index_bytes*3+A.material_index_bytes+A.normal_index_bytes*2;S=A.vertex_index_bytes;T=A.vertex_index_bytes*2;h=A.vertex_index_bytes*3;R=A.vertex_index_bytes*4;J=A.vertex_index_bytes* -4+A.material_index_bytes;P=A.vertex_index_bytes*4+A.material_index_bytes+A.normal_index_bytes;Q=A.vertex_index_bytes*4+A.material_index_bytes+A.normal_index_bytes*2;N=A.vertex_index_bytes*4+A.material_index_bytes+A.normal_index_bytes*3;Y=A.uv_index_bytes;V=A.uv_index_bytes*2;Z=A.uv_index_bytes;U=A.uv_index_bytes*2;ea=A.uv_index_bytes*3;d=A.vertex_index_bytes*3+A.material_index_bytes;aa=A.vertex_index_bytes*4+A.material_index_bytes;ia=A.ntri_flat*d;D=A.ntri_smooth*(d+A.normal_index_bytes*3);H=A.ntri_flat_uv* -(d+A.uv_index_bytes*3);$=A.ntri_smooth_uv*(d+A.normal_index_bytes*3+A.uv_index_bytes*3);ca=A.nquad_flat*aa;d=A.nquad_smooth*(aa+A.normal_index_bytes*4);aa=A.nquad_flat_uv*(aa+A.uv_index_bytes*4);I+=function(d){for(var e,f,g,h=A.vertex_coordinate_bytes*3,k=d+A.nvertices*h;d1&&(O=[new THREE.MeshFaceMaterial]);object=new THREE.Mesh(C,O);object.name=u;object.position.set(A[0],A[1],A[2]);q?(object.quaternion.set(q[0],q[1],q[2],q[3]),object.useQuaternion=!0):object.rotation.set(r[0],r[1],r[2]);object.scale.set(s[0],s[1],s[2]);object.visible=I.visible;J.scene.addObject(object);J.objects[u]=object;if(I.meshCollider){var b=THREE.CollisionUtils.MeshColliderWBox(object);J.scene.collisions.colliders.push(b)}if(I.castsShadow)b=new THREE.ShadowVolume(C),J.scene.addChild(b), -b.position=object.position,b.rotation=object.rotation,b.scale=object.scale;I.trigger&&I.trigger.toLowerCase()!="none"&&(b={type:I.trigger,object:I},J.triggers[object.name]=b)}}else A=I.position,r=I.rotation,q=I.quaternion,s=I.scale,q=0,object=new THREE.Object3D,object.name=u,object.position.set(A[0],A[1],A[2]),q?(object.quaternion.set(q[0],q[1],q[2],q[3]),object.useQuaternion=!0):object.rotation.set(r[0],r[1],r[2]),object.scale.set(s[0],s[1],s[2]),object.visible=I.visible!==void 0?I.visible:!1,J.scene.addObject(object), -J.objects[u]=object,J.empties[u]=object,I.trigger&&I.trigger.toLowerCase()!="none"&&(b={type:I.trigger,object:I},J.triggers[object.name]=b)}function m(b){return function(c){J.geometries[b]=c;k();S-=1;d.onLoadComplete();n()}}function o(b){return function(d){J.geometries[b]=d}}function n(){d.callbackProgress({totalModels:h,totalTextures:R,loadedModels:h-S,loadedTextures:R-T},J);d.onLoadProgress();S==0&&T==0&&c(J)}var t,p,u,v,y,B,z,I,A,G,F,C,M,L,O,w,W,S,T,h,R,J;w=b.data;b=new THREE.BinaryLoader;W=new THREE.JSONLoader; -T=S=0;J={scene:new THREE.Scene,geometries:{},materials:{},textures:{},objects:{},cameras:{},lights:{},fogs:{},triggers:{},empties:{}};var P=!1;for(u in w.objects)if(I=w.objects[u],I.meshCollider){P=!0;break}if(P)J.scene.collisions=new THREE.CollisionSystem;if(w.transform){P=w.transform.position;G=w.transform.rotation;var Q=w.transform.scale;P&&J.scene.position.set(P[0],P[1],P[2]);G&&J.scene.rotation.set(G[0],G[1],G[2]);Q&&J.scene.scale.set(Q[0],Q[1],Q[2]);(P||G||Q)&&J.scene.updateMatrix()}P=function(){T-= -1;n();d.onLoadComplete()};for(y in w.cameras){G=w.cameras[y];if(G.type=="perspective")M=new THREE.Camera(G.fov,G.aspect,G.near,G.far);else if(G.type=="ortho")M=new THREE.Camera,M.projectionMatrix=THREE.Matrix4.makeOrtho(G.left,G.right,G.top,G.bottom,G.near,G.far);A=G.position;G=G.target;M.position.set(A[0],A[1],A[2]);M.target.position.set(G[0],G[1],G[2]);J.cameras[y]=M}for(v in w.lights){y=w.lights[v];M=y.color!==void 0?y.color:16777215;G=y.intensity!==void 0?y.intensity:1;if(y.type=="directional")A= -y.direction,light=new THREE.DirectionalLight(M,G),light.position.set(A[0],A[1],A[2]),light.position.normalize();else if(y.type=="point")A=y.position,light=new THREE.PointLight(M,G),light.position.set(A[0],A[1],A[2]);J.scene.addLight(light);J.lights[v]=light}for(B in w.fogs)v=w.fogs[B],v.type=="linear"?L=new THREE.Fog(0,v.near,v.far):v.type=="exp2"&&(L=new THREE.FogExp2(0,v.density)),G=v.color,L.color.setRGB(G[0],G[1],G[2]),J.fogs[B]=L;if(J.cameras&&w.defaults.camera)J.currentCamera=J.cameras[w.defaults.camera]; -if(J.fogs&&w.defaults.fog)J.scene.fog=J.fogs[w.defaults.fog];G=w.defaults.bgcolor;J.bgColor=new THREE.Color;J.bgColor.setRGB(G[0],G[1],G[2]);J.bgColorAlpha=w.defaults.bgalpha;for(t in w.geometries)if(B=w.geometries[t],B.type=="bin_mesh"||B.type=="ascii_mesh")S+=1,d.onLoadStart();h=S;for(t in w.geometries)B=w.geometries[t],B.type=="cube"?(C=new THREE.CubeGeometry(B.width,B.height,B.depth,B.segmentsWidth,B.segmentsHeight,B.segmentsDepth,null,B.flipped,B.sides),J.geometries[t]=C):B.type=="plane"?(C= -new THREE.PlaneGeometry(B.width,B.height,B.segmentsWidth,B.segmentsHeight),J.geometries[t]=C):B.type=="sphere"?(C=new THREE.SphereGeometry(B.radius,B.segmentsWidth,B.segmentsHeight),J.geometries[t]=C):B.type=="cylinder"?(C=new THREE.CylinderGeometry(B.numSegs,B.topRad,B.botRad,B.height,B.topOffset,B.botOffset),J.geometries[t]=C):B.type=="torus"?(C=new THREE.TorusGeometry(B.radius,B.tube,B.segmentsR,B.segmentsT),J.geometries[t]=C):B.type=="icosahedron"?(C=new THREE.IcosahedronGeometry(B.subdivisions), -J.geometries[t]=C):B.type=="bin_mesh"?b.load({model:e(B.url,w.urlBaseType),callback:m(t)}):B.type=="ascii_mesh"?W.load({model:e(B.url,w.urlBaseType),callback:m(t)}):B.type=="embedded_mesh"&&(B=w.embeds[B.id])&&W.createModel(B,o(t),"");for(z in w.textures)if(t=w.textures[z],t.url instanceof Array){T+=t.url.length;for(b=0;b=this.maxCount-3&&k(this)};this.begin=function(){this.count=0; -this.hasNormal=this.hasPos=!1};this.end=function(b){if(this.count!=0){for(var c=this.count*3;cthis.size-1&&(m=this.size-1);var p=Math.floor(o-k);p<1&&(p=1);o=Math.floor(o+k);o>this.size-1&&(o=this.size-1);var u=Math.floor(n-k);u<1&&(u=1);k=Math.floor(n+k);k>this.size-1&&(k=this.size- -1);for(var v,y,B,z,I,A;t0&&(this.field[B+v]+=z)}}};this.addPlaneX=function(b,c){var f,g,j,k,m,o=this.size,n=this.yd,t=this.zd,p=this.field,u=o*Math.sqrt(b/c);u>o&&(u=o);for(f=0;f0)for(g=0;gn&&(v=n);for(g=0;g0){m=g*t;for(f=0;fsize&&(dist=size);for(j=0;j0){m=zd*j;for(g=0;gg?this.hits.push(f):this.hits.unshift(f),g=e;return this.hits}; -THREE.CollisionSystem.prototype.rayCastNearest=function(b){var c=this.rayCastAll(b);if(c.length==0)return null;for(var d=0;c[d]instanceof THREE.MeshCollider;){var e=this.rayMesh(b,c[d]);if(e.distc.length)return null;return c[d]}; -THREE.CollisionSystem.prototype.rayCast=function(b,c){if(c instanceof THREE.PlaneCollider)return this.rayPlane(b,c);else if(c instanceof THREE.SphereCollider)return this.raySphere(b,c);else if(c instanceof THREE.BoxCollider)return this.rayBox(b,c);else if(c instanceof THREE.MeshCollider&&c.box)return this.rayBox(b,c.box)}; -THREE.CollisionSystem.prototype.rayMesh=function(b,c){for(var d=this.makeRayLocal(b,c.mesh),e=Number.MAX_VALUE,f,g=0;g=k*f))return Number.MAX_VALUE;j/=k;k=THREE.CollisionSystem.__v3;k.copy(b.direction);k.multiplyScalar(j);k.addSelf(b.origin);Math.abs(g.x)>Math.abs(g.y)?Math.abs(g.x)>Math.abs(g.z)?(b=k.y-c.y,g=d.y-c.y, -f=e.y-c.y,k=k.z-c.z,d=d.z-c.z,e=e.z-c.z):(b=k.x-c.x,g=d.x-c.x,f=e.x-c.x,k=k.y-c.y,d=d.y-c.y,e=e.y-c.y):Math.abs(g.y)>Math.abs(g.z)?(b=k.x-c.x,g=d.x-c.x,f=e.x-c.x,k=k.z-c.z,d=d.z-c.z,e=e.z-c.z):(b=k.x-c.x,g=d.x-c.x,f=e.x-c.x,k=k.y-c.y,d=d.y-c.y,e=e.y-c.y);c=g*e-d*f;if(c==0)return Number.MAX_VALUE;c=1/c;e=(b*e-k*f)*c;if(!(e>=0))return Number.MAX_VALUE;c*=g*k-d*b;if(!(c>=0))return Number.MAX_VALUE;if(!(1-e-c>=0))return Number.MAX_VALUE;return j}; -THREE.CollisionSystem.prototype.makeRayLocal=function(b,c){var d=THREE.CollisionSystem.__m;THREE.Matrix4.makeInvert(c.matrixWorld,d);var e=THREE.CollisionSystem.__r;e.origin.copy(b.origin);e.direction.copy(b.direction);d.multiplyVector3(e.origin);d.rotateAxis(e.direction);e.direction.normalize();return e}; -THREE.CollisionSystem.prototype.rayBox=function(b,c){var d;c.dynamic&&c.mesh&&c.mesh.matrixWorld?d=this.makeRayLocal(b,c.mesh):(d=THREE.CollisionSystem.__r,d.origin.copy(b.origin),d.direction.copy(b.direction));var e=0,f=0,g=0,j=0,k=0,m=0,o=!0;d.origin.xc.max.x&&(e=c.max.x-d.origin.x,e/=d.direction.x,o=!1,j=1);d.origin.yc.max.y&&(f=c.max.y-d.origin.y,f/=d.direction.y, -o=!1,k=1);d.origin.zc.max.z&&(g=c.max.z-d.origin.z,g/=d.direction.z,o=!1,m=1);if(o)return-1;o=0;f>e&&(o=1,e=f);g>e&&(o=2,e=g);switch(o){case 0:k=d.origin.y+d.direction.y*e;if(kc.max.y)return Number.MAX_VALUE;d=d.origin.z+d.direction.z*e;if(dc.max.z)return Number.MAX_VALUE;c.normal.set(j,0,0);break;case 1:j=d.origin.x+d.direction.x*e;if(jc.max.x)return Number.MAX_VALUE;d=d.origin.z+d.direction.z* -e;if(dc.max.z)return Number.MAX_VALUE;c.normal.set(0,k,0);break;case 2:j=d.origin.x+d.direction.x*e;if(jc.max.x)return Number.MAX_VALUE;k=d.origin.y+d.direction.y*e;if(kc.max.y)return Number.MAX_VALUE;c.normal.set(0,0,m)}return e};THREE.CollisionSystem.prototype.rayPlane=function(b,c){var d=b.direction.dot(c.normal),e=c.point.dot(c.normal);if(d<0)d=(e-b.origin.dot(c.normal))/d;else return Number.MAX_VALUE;return d>0?d:Number.MAX_VALUE}; -THREE.CollisionSystem.prototype.raySphere=function(b,c){var d=c.center.clone().subSelf(b.origin);if(d.lengthSq=0)return Math.abs(e)-Math.sqrt(d);return Number.MAX_VALUE};THREE.CollisionSystem.__v1=new THREE.Vector3;THREE.CollisionSystem.__v2=new THREE.Vector3;THREE.CollisionSystem.__v3=new THREE.Vector3;THREE.CollisionSystem.__nr=new THREE.Vector3;THREE.CollisionSystem.__m=new THREE.Matrix4; -THREE.CollisionSystem.__r=new THREE.Ray;THREE.CollisionUtils={};THREE.CollisionUtils.MeshOBB=function(b){b.geometry.computeBoundingBox();var c=b.geometry.boundingBox,d=new THREE.Vector3(c.x[0],c.y[0],c.z[0]),c=new THREE.Vector3(c.x[1],c.y[1],c.z[1]),d=new THREE.BoxCollider(d,c);d.mesh=b;return d};THREE.CollisionUtils.MeshAABB=function(b){var c=THREE.CollisionUtils.MeshOBB(b);c.min.addSelf(b.position);c.max.addSelf(b.position);c.dynamic=!1;return c}; -THREE.CollisionUtils.MeshColliderWBox=function(b){return new THREE.MeshCollider(b,THREE.CollisionUtils.MeshOBB(b))}; -if(THREE.WebGLRenderer)THREE.AnaglyphWebGLRenderer=function(b){THREE.WebGLRenderer.call(this,b);var c=this,d=this.setSize,e=this.render,f=new THREE.Camera,g=new THREE.Camera,j=new THREE.Matrix4,k=new THREE.Matrix4,m,o,n;f.useTarget=g.useTarget=!1;f.matrixAutoUpdate=g.matrixAutoUpdate=!1;var b={minFilter:THREE.LinearFilter,magFilter:THREE.NearestFilter,format:THREE.RGBAFormat},t=new THREE.WebGLRenderTarget(512,512,b),p=new THREE.WebGLRenderTarget(512,512,b),u=new THREE.Camera(53,1,1,1E4);u.position.z= -2;_material=new THREE.MeshShaderMaterial({uniforms:{mapLeft:{type:"t",value:0,texture:t},mapRight:{type:"t",value:1,texture:p}},vertexShader:"varying vec2 vUv;\nvoid main() {\nvUv = vec2( uv.x, 1.0 - uv.y );\ngl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}",fragmentShader:"uniform sampler2D mapLeft;\nuniform sampler2D mapRight;\nvarying vec2 vUv;\nvoid main() {\nvec4 colorL, colorR;\nvec2 uv = vUv;\ncolorL = texture2D( mapLeft, uv );\ncolorR = texture2D( mapRight, uv );\ngl_FragColor = vec4( colorL.g * 0.7 + colorL.b * 0.3, colorR.g, colorR.b, colorL.a + colorR.a ) * 1.1;\n}"}); -var v=new THREE.Scene;v.addObject(new THREE.Mesh(new THREE.PlaneGeometry(2,2),_material));this.setSize=function(b,e){d.call(c,b,e);t.width=b;t.height=e;p.width=b;p.height=e};this.render=function(b,d){d.update(null,!0);if(m!==d.aspect||o!==d.near||n!==d.fov){m=d.aspect;o=d.near;n=d.fov;var z=d.projectionMatrix.clone(),I=125/30*0.5,A=I*o/125,G=o*Math.tan(n*Math.PI/360),F;j.n14=I;k.n14=-I;I=-G*m+A;F=G*m+A;z.n11=2*o/(F-I);z.n13=(F+I)/(F-I);f.projectionMatrix=z.clone();I=-G*m-A;F=G*m-A;z.n11=2*o/(F-I); -z.n13=(F+I)/(F-I);g.projectionMatrix=z.clone()}f.matrix=d.matrixWorld.clone().multiplySelf(k);f.update(null,!0);f.position.copy(d.position);f.near=o;f.far=d.far;e.call(c,b,f,t,!0);g.matrix=d.matrixWorld.clone().multiplySelf(j);g.update(null,!0);g.position.copy(d.position);g.near=o;g.far=d.far;e.call(c,b,g,p,!0);e.call(c,v,u)}}; -if(THREE.WebGLRenderer)THREE.CrosseyedWebGLRenderer=function(b){THREE.WebGLRenderer.call(this,b);this.autoClear=!1;var c=this,d=this.setSize,e=this.render,f,g,j=new THREE.Camera,k=new THREE.Camera;c.separation=10;if(b&&b.separation!==void 0)c.separation=b.separation;(new THREE.Camera(53,window.innerWidth/2/window.innerHeight,1,1E4)).position.z=-10;this.setSize=function(b,e){d.call(c,b,e);f=b/2;g=e};this.render=function(b,d){this.clear();j.fov=d.fov;j.aspect=0.5*d.aspect;j.near=d.near;j.far=d.far; -j.updateProjectionMatrix();j.position.copy(d.position);j.target.position.copy(d.target.position);j.translateX(c.separation);k.projectionMatrix=j.projectionMatrix;k.position.copy(d.position);k.target.position.copy(d.target.position);k.translateX(-c.separation);this.setViewport(0,0,f,g);e.call(c,b,j);this.setViewport(f,0,f,g);e.call(c,b,k,!1)}}; +/** + * @author mr.doob / http://mrdoob.com/ + */ + +var THREE = THREE || {}; + +if ( ! window.Int32Array ) { + + window.Int32Array = Array; + window.Float32Array = Array; + +} +/** + * @author mr.doob / http://mrdoob.com/ + */ + +THREE.Color = function ( hex ) { + + this.setHex( hex ); + +}; + +THREE.Color.prototype = { + + copy : function ( color ) { + + this.r = color.r; + this.g = color.g; + this.b = color.b; + this.hex = color.hex; + + }, + + setHex : function ( hex ) { + + this.hex = ( ~~ hex ) & 0xffffff; + this.updateRGB(); + + }, + + setRGB : function ( r, g, b ) { + + this.r = r; + this.g = g; + this.b = b; + + this.updateHex(); + + }, + + setHSV : function ( h, s, v ) { + + // based on MochiKit implementation by Bob Ippolito + // h,s,v ranges are < 0.0 - 1.0 > + + var r, g, b, i, f, p, q, t; + + if ( v == 0.0 ) { + + r = g = b = 0; + + } else { + + i = Math.floor( h * 6 ); + f = ( h * 6 ) - i; + p = v * ( 1 - s ); + q = v * ( 1 - ( s * f ) ); + t = v * ( 1 - ( s * ( 1 - f ) ) ); + + switch ( i ) { + + case 1: r = q; g = v; b = p; break; + case 2: r = p; g = v; b = t; break; + case 3: r = p; g = q; b = v; break; + case 4: r = t; g = p; b = v; break; + case 5: r = v; g = p; b = q; break; + case 6: // fall through + case 0: r = v; g = t; b = p; break; + + } + + } + + this.setRGB( r, g, b ); + + }, + + updateHex : function () { + + this.hex = ~~ ( this.r * 255 ) << 16 ^ ~~ ( this.g * 255 ) << 8 ^ ~~ ( this.b * 255 ); + + }, + + updateRGB : function () { + + this.r = ( this.hex >> 16 & 255 ) / 255; + this.g = ( this.hex >> 8 & 255 ) / 255; + this.b = ( this.hex & 255 ) / 255; + + }, + + clone : function () { + + return new THREE.Color( this.hex ); + + } + +}; +/** + * @author mr.doob / http://mrdoob.com/ + * @author philogb / http://blog.thejit.org/ + * @author egraether / http://egraether.com/ + * @author zz85 / http://www.lab4games.net/zz85/blog + */ + +THREE.Vector2 = function ( x, y ) { + + this.set( + + x || 0, + y || 0 + + ); + +}; + +THREE.Vector2.prototype = { + + set : function ( x, y ) { + + this.x = x; + this.y = y; + + return this; + + }, + + copy : function ( v ) { + + this.x = v.x; + this.y = v.y; + + return this; + + }, + + clone : function () { + + return new THREE.Vector2( this.x, this.y ); + + }, + + + add : function ( v1, v2 ) { + + this.x = v1.x + v2.x; + this.y = v1.y + v2.y; + + return this; + + }, + + addSelf : function ( v ) { + + this.x += v.x; + this.y += v.y; + + return this; + + }, + + sub : function ( v1, v2 ) { + + this.x = v1.x - v2.x; + this.y = v1.y - v2.y; + + return this; + + }, + + subSelf : function ( v ) { + + this.x -= v.x; + this.y -= v.y; + + return this; + + }, + + multiplyScalar : function ( s ) { + + this.x *= s; + this.y *= s; + + return this; + + }, + + divideScalar : function ( s ) { + + if ( s ) { + + this.x /= s; + this.y /= s; + + } else { + + this.set( 0, 0 ); + + } + + return this; + + }, + + + negate : function() { + + return this.multiplyScalar( -1 ); + + }, + + dot : function ( v ) { + + return this.x * v.x + this.y * v.y; + + }, + + lengthSq : function () { + + return this.x * this.x + this.y * this.y; + + }, + + length : function () { + + return Math.sqrt( this.lengthSq() ); + + }, + + normalize : function () { + + return this.divideScalar( this.length() ); + + }, + + distanceTo : function ( v ) { + + return Math.sqrt( this.distanceToSquared( v ) ); + + }, + + distanceToSquared : function ( v ) { + + var dx = this.x - v.x, dy = this.y - v.y; + return dx * dx + dy * dy; + + }, + + + setLength : function ( l ) { + + return this.normalize().multiplyScalar( l ); + + }, + + // deprecated: same as normalize + + unit : function () { + + return this.normalize(); + + }, + + // danger, works only on numbers which are exactly the same + // (which may be not what is expected thanks to floating point precision) + // (should be probably using some tiny epsilon instead of equality) + + equals : function( v ) { + + return ( ( v.x == this.x ) && ( v.y == this.y ) ); + + } + +}; +/** + * @author mr.doob / http://mrdoob.com/ + * @author kile / http://kile.stravaganza.org/ + * @author philogb / http://blog.thejit.org/ + * @author mikael emtinger / http://gomo.se/ + * @author egraether / http://egraether.com/ + */ + +THREE.Vector3 = function ( x, y, z ) { + + this.set( + + x || 0, + y || 0, + z || 0 + + ); + +}; + + +THREE.Vector3.prototype = { + + set : function ( x, y, z ) { + + this.x = x; + this.y = y; + this.z = z; + + return this; + + }, + + copy : function ( v ) { + + this.x = v.x; + this.y = v.y; + this.z = v.z; + + return this; + + }, + + clone : function () { + + return new THREE.Vector3( this.x, this.y, this.z ); + + }, + + + add : function ( v1, v2 ) { + + this.x = v1.x + v2.x; + this.y = v1.y + v2.y; + this.z = v1.z + v2.z; + + return this; + + }, + + addSelf : function ( v ) { + + this.x += v.x; + this.y += v.y; + this.z += v.z; + + return this; + + }, + + addScalar : function ( s ) { + + this.x += s; + this.y += s; + this.z += s; + + return this; + + }, + + sub : function ( v1, v2 ) { + + this.x = v1.x - v2.x; + this.y = v1.y - v2.y; + this.z = v1.z - v2.z; + + return this; + + }, + + subSelf : function ( v ) { + + this.x -= v.x; + this.y -= v.y; + this.z -= v.z; + + return this; + + }, + + multiply : function ( a, b ) { + + this.x = a.x * b.x; + this.y = a.y * b.y; + this.z = a.z * b.z; + + return this; + + }, + + multiplySelf : function ( v ) { + + this.x *= v.x; + this.y *= v.y; + this.z *= v.y; + + return this; + + }, + + multiplyScalar : function ( s ) { + + this.x *= s; + this.y *= s; + this.z *= s; + + return this; + + }, + + divideSelf : function ( v ) { + + return this.divide( this, v ); + + }, + + divideScalar : function ( s ) { + + if ( s ) { + + this.x /= s; + this.y /= s; + this.z /= s; + + } else { + + this.set( 0, 0, 0 ); + + } + + return this; + + }, + + + negate : function() { + + return this.multiplyScalar( -1 ); + + }, + + dot : function ( v ) { + + return this.x * v.x + this.y * v.y + this.z * v.z; + + }, + + lengthSq : function () { + + return this.x * this.x + this.y * this.y + this.z * this.z; + + }, + + length : function () { + + return Math.sqrt( this.lengthSq() ); + + }, + + lengthManhattan : function () { + + // correct version + // return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ); + + return this.x + this.y + this.z; + + }, + + normalize : function () { + + return this.divideScalar( this.length() ); + + }, + + setLength : function ( l ) { + + return this.normalize().multiplyScalar( l ); + + }, + + + cross : function ( a, b ) { + + this.x = a.y * b.z - a.z * b.y; + this.y = a.z * b.x - a.x * b.z; + this.z = a.x * b.y - a.y * b.x; + + return this; + + }, + + crossSelf : function ( v ) { + + return this.set( + + this.y * v.z - this.z * v.y, + this.z * v.x - this.x * v.z, + this.x * v.y - this.y * v.x + + ); + + }, + + + distanceTo : function ( v ) { + + return Math.sqrt( this.distanceToSquared( v ) ); + + }, + + distanceToSquared : function ( v ) { + + return new THREE.Vector3().sub( this, v ).lengthSq(); + + }, + + + setPositionFromMatrix : function ( m ) { + + this.x = m.n14; + this.y = m.n24; + this.z = m.n34; + + }, + + setRotationFromMatrix : function ( m ) { + + var cosY = Math.cos( this.y ); + + this.y = Math.asin( m.n13 ); + + if ( Math.abs( cosY ) > 0.00001 ) { + + this.x = Math.atan2( - m.n23 / cosY, m.n33 / cosY ); + this.z = Math.atan2( - m.n12 / cosY, m.n11 / cosY ); + + } else { + + this.x = 0; + this.z = Math.atan2( m.n21, m.n22 ); + + } + + }, + + isZero : function () { + + return ( this.lengthSq() < 0.0001 /* almostZero */ ); + + } + +}; +/** + * @author supereggbert / http://www.paulbrunt.co.uk/ + * @author philogb / http://blog.thejit.org/ + * @author mikael emtinger / http://gomo.se/ + * @author egraether / http://egraether.com/ + */ + +THREE.Vector4 = function ( x, y, z, w ) { + + this.set( + + x || 0, + y || 0, + z || 0, + w || 1 + + ); + +}; + +THREE.Vector4.prototype = { + + set : function ( x, y, z, w ) { + + this.x = x; + this.y = y; + this.z = z; + this.w = w; + + return this; + + }, + + copy : function ( v ) { + + return this.set( + + v.x, + v.y, + v.z, + v.w || 1.0 + + ); + + }, + + clone : function () { + + return new THREE.Vector4( this.x, this.y, this.z, this.w ); + + }, + + + add : function ( v1, v2 ) { + + this.x = v1.x + v2.x; + this.y = v1.y + v2.y; + this.z = v1.z + v2.z; + this.w = v1.w + v2.w; + + return this; + + }, + + addSelf : function ( v ) { + + this.x += v.x; + this.y += v.y; + this.z += v.z; + this.w += v.w; + + return this; + + }, + + sub : function ( v1, v2 ) { + + this.x = v1.x - v2.x; + this.y = v1.y - v2.y; + this.z = v1.z - v2.z; + this.w = v1.w - v2.w; + + return this; + + }, + + subSelf : function ( v ) { + + this.x -= v.x; + this.y -= v.y; + this.z -= v.z; + this.w -= v.w; + + return this; + + }, + + multiplyScalar : function ( s ) { + + this.x *= s; + this.y *= s; + this.z *= s; + this.w *= s; + + return this; + + }, + + divideScalar : function ( s ) { + + if ( s ) { + + this.x /= s; + this.y /= s; + this.z /= s; + this.w /= s; + + } else { + + this.set( 0, 0, 0, 1 ); + + } + + return this; + + }, + + + negate : function() { + + return this.multiplyScalar( -1 ); + + }, + + dot : function ( v ) { + + return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w; + + }, + + lengthSq : function () { + + return this.dot( this ); + + }, + + length : function () { + + return Math.sqrt( this.lengthSq() ); + + }, + + normalize : function () { + + return this.divideScalar( this.length() ); + + }, + + setLength : function ( l ) { + + return this.normalize().multiplyScalar( l ); + + }, + + + lerpSelf : function ( v, alpha ) { + + this.x += (v.x - this.x) * alpha; + this.y += (v.y - this.y) * alpha; + this.z += (v.z - this.z) * alpha; + this.w += (v.w - this.w) * alpha; + + return this; + + }, + +}; +/** + * @author mr.doob / http://mrdoob.com/ + */ + +THREE.Ray = function ( origin, direction ) { + + this.origin = origin || new THREE.Vector3(); + this.direction = direction || new THREE.Vector3(); + +} + +THREE.Ray.prototype = { + + intersectScene: function ( scene ) { + + return this.intersectObjects( scene.objects ); + + }, + + intersectObjects: function ( objects ) { + + var i, l, object, + intersects = []; + + for ( i = 0, l = objects.length; i < l; i ++ ) { + + intersects = intersects.concat( this.intersectObject( objects[ i ] ) ); + + } + + intersects.sort( function ( a, b ) { return a.distance - b.distance; } ); + + return intersects; + + }, + + intersectObject: function ( object ) { + + if ( object instanceof THREE.Particle ) { + + var distance = distanceFromIntersection( this.origin, this.direction, object ); + + if ( ! distance || distance > object.scale.x ) { + + return []; + + } + + return [ { + + distance: distance, + point: object.position, + face: null, + object: object + + } ]; + + } else if ( object instanceof THREE.Mesh ) { + + // Checking boundingSphere + + var distance = distanceFromIntersection( this.origin, this.direction, object ); + + if ( ! distance || distance > object.geometry.boundingSphere.radius * Math.max( object.scale.x, Math.max( object.scale.y, object.scale.z ) ) ) { + + return []; + + } + + // Checking faces + + var f, fl, face, a, b, c, d, normal, + dot, scalar, + origin, direction, + geometry = object.geometry, + vertices = geometry.vertices, + objMatrix, + intersect, intersects = [], + intersectPoint; + + for ( f = 0, fl = geometry.faces.length; f < fl; f ++ ) { + + face = geometry.faces[ f ]; + + origin = this.origin.clone(); + direction = this.direction.clone(); + + objMatrix = object.matrixWorld; + + a = objMatrix.multiplyVector3( vertices[ face.a ].position.clone() ); + b = objMatrix.multiplyVector3( vertices[ face.b ].position.clone() ); + c = objMatrix.multiplyVector3( vertices[ face.c ].position.clone() ); + d = face instanceof THREE.Face4 ? objMatrix.multiplyVector3( vertices[ face.d ].position.clone() ) : null; + + normal = object.matrixRotationWorld.multiplyVector3( face.normal.clone() ); + dot = direction.dot( normal ); + + if ( object.doubleSided || ( object.flipSided ? dot > 0 : dot < 0 ) ) { // Math.abs( dot ) > 0.0001 + + scalar = normal.dot( new THREE.Vector3().sub( a, origin ) ) / dot; + intersectPoint = origin.addSelf( direction.multiplyScalar( scalar ) ); + + if ( face instanceof THREE.Face3 ) { + + if ( pointInFace3( intersectPoint, a, b, c ) ) { + + intersect = { + + distance: this.origin.distanceTo( intersectPoint ), + point: intersectPoint, + face: face, + object: object + + }; + + intersects.push( intersect ); + + } + + } else if ( face instanceof THREE.Face4 ) { + + if ( pointInFace3( intersectPoint, a, b, d ) || pointInFace3( intersectPoint, b, c, d ) ) { + + intersect = { + + distance: this.origin.distanceTo( intersectPoint ), + point: intersectPoint, + face: face, + object: object + + }; + + intersects.push( intersect ); + + } + + } + + } + + } + + return intersects; + + } else { + + return []; + + } + + function distanceFromIntersection( origin, direction, object ) { + + var vector, dot, intersect, distance, + position = object.matrixWorld.getPosition(); + + vector = position.clone().subSelf( origin ); + dot = vector.dot( direction ); + + intersect = origin.clone().addSelf( direction.clone().multiplyScalar( dot ) ); + distance = position.distanceTo( intersect ); + + // TODO: Check if distance is negative (object behind camera). + + return distance; + + } + + // http://www.blackpawn.com/texts/pointinpoly/default.html + + function pointInFace3( p, a, b, c ) { + + var v0 = c.clone().subSelf( a ), v1 = b.clone().subSelf( a ), v2 = p.clone().subSelf( a ), + dot00 = v0.dot( v0 ), dot01 = v0.dot( v1 ), dot02 = v0.dot( v2 ), dot11 = v1.dot( v1 ), dot12 = v1.dot( v2 ), + + invDenom = 1 / ( dot00 * dot11 - dot01 * dot01 ), + u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom, + v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom; + + return ( u > 0 ) && ( v > 0 ) && ( u + v < 1 ); + + } + + } + +}; +/** + * @author mr.doob / http://mrdoob.com/ + */ + +THREE.Rectangle = function () { + + var _left, _top, _right, _bottom, + _width, _height, _isEmpty = true; + + function resize() { + + _width = _right - _left; + _height = _bottom - _top; + + } + + this.getX = function () { + + return _left; + + }; + + this.getY = function () { + + return _top; + + }; + + this.getWidth = function () { + + return _width; + + }; + + this.getHeight = function () { + + return _height; + + }; + + this.getLeft = function() { + + return _left; + + }; + + this.getTop = function() { + + return _top; + + }; + + this.getRight = function() { + + return _right; + + }; + + this.getBottom = function() { + + return _bottom; + + }; + + this.set = function ( left, top, right, bottom ) { + + _isEmpty = false; + + _left = left; _top = top; + _right = right; _bottom = bottom; + + resize(); + + }; + + this.addPoint = function ( x, y ) { + + if ( _isEmpty ) { + + _isEmpty = false; + _left = x; _top = y; + _right = x; _bottom = y; + + resize(); + + } else { + + _left = _left < x ? _left : x; // Math.min( _left, x ); + _top = _top < y ? _top : y; // Math.min( _top, y ); + _right = _right > x ? _right : x; // Math.max( _right, x ); + _bottom = _bottom > y ? _bottom : y; // Math.max( _bottom, y ); + + resize(); + } + + }; + + this.add3Points = function ( x1, y1, x2, y2, x3, y3 ) { + + if (_isEmpty) { + + _isEmpty = false; + _left = x1 < x2 ? ( x1 < x3 ? x1 : x3 ) : ( x2 < x3 ? x2 : x3 ); + _top = y1 < y2 ? ( y1 < y3 ? y1 : y3 ) : ( y2 < y3 ? y2 : y3 ); + _right = x1 > x2 ? ( x1 > x3 ? x1 : x3 ) : ( x2 > x3 ? x2 : x3 ); + _bottom = y1 > y2 ? ( y1 > y3 ? y1 : y3 ) : ( y2 > y3 ? y2 : y3 ); + + resize(); + + } else { + + _left = x1 < x2 ? ( x1 < x3 ? ( x1 < _left ? x1 : _left ) : ( x3 < _left ? x3 : _left ) ) : ( x2 < x3 ? ( x2 < _left ? x2 : _left ) : ( x3 < _left ? x3 : _left ) ); + _top = y1 < y2 ? ( y1 < y3 ? ( y1 < _top ? y1 : _top ) : ( y3 < _top ? y3 : _top ) ) : ( y2 < y3 ? ( y2 < _top ? y2 : _top ) : ( y3 < _top ? y3 : _top ) ); + _right = x1 > x2 ? ( x1 > x3 ? ( x1 > _right ? x1 : _right ) : ( x3 > _right ? x3 : _right ) ) : ( x2 > x3 ? ( x2 > _right ? x2 : _right ) : ( x3 > _right ? x3 : _right ) ); + _bottom = y1 > y2 ? ( y1 > y3 ? ( y1 > _bottom ? y1 : _bottom ) : ( y3 > _bottom ? y3 : _bottom ) ) : ( y2 > y3 ? ( y2 > _bottom ? y2 : _bottom ) : ( y3 > _bottom ? y3 : _bottom ) ); + + resize(); + + }; + + }; + + this.addRectangle = function ( r ) { + + if ( _isEmpty ) { + + _isEmpty = false; + _left = r.getLeft(); _top = r.getTop(); + _right = r.getRight(); _bottom = r.getBottom(); + + resize(); + + } else { + + _left = _left < r.getLeft() ? _left : r.getLeft(); // Math.min(_left, r.getLeft() ); + _top = _top < r.getTop() ? _top : r.getTop(); // Math.min(_top, r.getTop() ); + _right = _right > r.getRight() ? _right : r.getRight(); // Math.max(_right, r.getRight() ); + _bottom = _bottom > r.getBottom() ? _bottom : r.getBottom(); // Math.max(_bottom, r.getBottom() ); + + resize(); + + } + + }; + + this.inflate = function ( v ) { + + _left -= v; _top -= v; + _right += v; _bottom += v; + + resize(); + + }; + + this.minSelf = function ( r ) { + + _left = _left > r.getLeft() ? _left : r.getLeft(); // Math.max( _left, r.getLeft() ); + _top = _top > r.getTop() ? _top : r.getTop(); // Math.max( _top, r.getTop() ); + _right = _right < r.getRight() ? _right : r.getRight(); // Math.min( _right, r.getRight() ); + _bottom = _bottom < r.getBottom() ? _bottom : r.getBottom(); // Math.min( _bottom, r.getBottom() ); + + resize(); + + }; + + /* + this.contains = function ( x, y ) { + + return x > _left && x < _right && y > _top && y < _bottom; + + }; + */ + + this.instersects = function ( r ) { + + // return this.contains( r.getLeft(), r.getTop() ) || this.contains( r.getRight(), r.getTop() ) || this.contains( r.getLeft(), r.getBottom() ) || this.contains( r.getRight(), r.getBottom() ); + + return Math.min( _right, r.getRight() ) - Math.max( _left, r.getLeft() ) >= 0 && + Math.min( _bottom, r.getBottom() ) - Math.max( _top, r.getTop() ) >= 0; + + }; + + this.empty = function () { + + _isEmpty = true; + + _left = 0; _top = 0; + _right = 0; _bottom = 0; + + resize(); + + }; + + this.isEmpty = function () { + + return _isEmpty; + + }; + +}; +THREE.Matrix3 = function () { + + this.m = []; + +}; + +THREE.Matrix3.prototype = { + + transpose : function () { + + var tmp, m = this.m; + + tmp = m[1]; m[1] = m[3]; m[3] = tmp; + tmp = m[2]; m[2] = m[6]; m[6] = tmp; + tmp = m[5]; m[5] = m[7]; m[7] = tmp; + + return this; + + }, + + transposeIntoArray : function ( r ) { + + var m = this.m; + + r[ 0 ] = m[ 0 ]; + r[ 1 ] = m[ 3 ]; + r[ 2 ] = m[ 6 ]; + r[ 3 ] = m[ 1 ]; + r[ 4 ] = m[ 4 ]; + r[ 5 ] = m[ 7 ]; + r[ 6 ] = m[ 2 ]; + r[ 7 ] = m[ 5 ]; + r[ 8 ] = m[ 8 ]; + + return this; + + } + +}; +/** + * @author mr.doob / http://mrdoob.com/ + * @author supereggbert / http://www.paulbrunt.co.uk/ + * @author philogb / http://blog.thejit.org/ + * @author jordi_ros / http://plattsoft.com + * @author D1plo1d / http://github.com/D1plo1d + * @author alteredq / http://alteredqualia.com/ + * @author mikael emtinger / http://gomo.se/ + */ + +THREE.Matrix4 = function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) { + + this.set( + + n11 || 1, n12 || 0, n13 || 0, n14 || 0, + n21 || 0, n22 || 1, n23 || 0, n24 || 0, + n31 || 0, n32 || 0, n33 || 1, n34 || 0, + n41 || 0, n42 || 0, n43 || 0, n44 || 1 + + ); + + this.flat = new Array( 16 ); + this.m33 = new THREE.Matrix3(); + +}; + +THREE.Matrix4.prototype = { + + set : function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) { + + this.n11 = n11; this.n12 = n12; this.n13 = n13; this.n14 = n14; + this.n21 = n21; this.n22 = n22; this.n23 = n23; this.n24 = n24; + this.n31 = n31; this.n32 = n32; this.n33 = n33; this.n34 = n34; + this.n41 = n41; this.n42 = n42; this.n43 = n43; this.n44 = n44; + + return this; + + }, + + identity : function () { + + this.set( + + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + copy : function ( m ) { + + this.set( + + m.n11, m.n12, m.n13, m.n14, + m.n21, m.n22, m.n23, m.n24, + m.n31, m.n32, m.n33, m.n34, + m.n41, m.n42, m.n43, m.n44 + + ); + + return this; + + }, + + lookAt : function ( eye, center, up ) { + + var x = THREE.Matrix4.__v1, y = THREE.Matrix4.__v2, z = THREE.Matrix4.__v3; + + z.sub( eye, center ).normalize(); + + if ( z.length() === 0 ) { + + z.z = 1; + + } + + x.cross( up, z ).normalize(); + + if ( x.length() === 0 ) { + + z.x += 0.0001; + x.cross( up, z ).normalize(); + + } + + y.cross( z, x ).normalize(); + + + this.n11 = x.x; this.n12 = y.x; this.n13 = z.x; + this.n21 = x.y; this.n22 = y.y; this.n23 = z.y; + this.n31 = x.z; this.n32 = y.z; this.n33 = z.z; + + return this; + + }, + + multiplyVector3 : function ( v ) { + + var vx = v.x, vy = v.y, vz = v.z, + d = 1 / ( this.n41 * vx + this.n42 * vy + this.n43 * vz + this.n44 ); + + v.x = ( this.n11 * vx + this.n12 * vy + this.n13 * vz + this.n14 ) * d; + v.y = ( this.n21 * vx + this.n22 * vy + this.n23 * vz + this.n24 ) * d; + v.z = ( this.n31 * vx + this.n32 * vy + this.n33 * vz + this.n34 ) * d; + + return v; + + }, + + multiplyVector4 : function ( v ) { + + var vx = v.x, vy = v.y, vz = v.z, vw = v.w; + + v.x = this.n11 * vx + this.n12 * vy + this.n13 * vz + this.n14 * vw; + v.y = this.n21 * vx + this.n22 * vy + this.n23 * vz + this.n24 * vw; + v.z = this.n31 * vx + this.n32 * vy + this.n33 * vz + this.n34 * vw; + v.w = this.n41 * vx + this.n42 * vy + this.n43 * vz + this.n44 * vw; + + return v; + + }, + + rotateAxis : function ( v ) { + + var vx = v.x, vy = v.y, vz = v.z; + + v.x = vx * this.n11 + vy * this.n12 + vz * this.n13; + v.y = vx * this.n21 + vy * this.n22 + vz * this.n23; + v.z = vx * this.n31 + vy * this.n32 + vz * this.n33; + + v.normalize(); + + return v; + + }, + + crossVector : function ( a ) { + + var v = new THREE.Vector4(); + + v.x = this.n11 * a.x + this.n12 * a.y + this.n13 * a.z + this.n14 * a.w; + v.y = this.n21 * a.x + this.n22 * a.y + this.n23 * a.z + this.n24 * a.w; + v.z = this.n31 * a.x + this.n32 * a.y + this.n33 * a.z + this.n34 * a.w; + + v.w = ( a.w ) ? this.n41 * a.x + this.n42 * a.y + this.n43 * a.z + this.n44 * a.w : 1; + + return v; + + }, + + multiply : function ( a, b ) { + + var a11 = a.n11, a12 = a.n12, a13 = a.n13, a14 = a.n14, + a21 = a.n21, a22 = a.n22, a23 = a.n23, a24 = a.n24, + a31 = a.n31, a32 = a.n32, a33 = a.n33, a34 = a.n34, + a41 = a.n41, a42 = a.n42, a43 = a.n43, a44 = a.n44, + + b11 = b.n11, b12 = b.n12, b13 = b.n13, b14 = b.n14, + b21 = b.n21, b22 = b.n22, b23 = b.n23, b24 = b.n24, + b31 = b.n31, b32 = b.n32, b33 = b.n33, b34 = b.n34, + b41 = b.n41, b42 = b.n42, b43 = b.n43, b44 = b.n44; + + this.n11 = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; + this.n12 = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; + this.n13 = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; + this.n14 = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; + + this.n21 = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; + this.n22 = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; + this.n23 = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; + this.n24 = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; + + this.n31 = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; + this.n32 = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; + this.n33 = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; + this.n34 = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; + + this.n41 = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; + this.n42 = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; + this.n43 = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; + this.n44 = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; + + return this; + + }, + + multiplyToArray : function ( a, b, r ) { + + this.multiply( a, b ); + + r[ 0 ] = this.n11; r[ 1 ] = this.n21; r[ 2 ] = this.n31; r[ 3 ] = this.n41; + r[ 4 ] = this.n12; r[ 5 ] = this.n22; r[ 6 ] = this.n32; r[ 7 ] = this.n42; + r[ 8 ] = this.n13; r[ 9 ] = this.n23; r[ 10 ] = this.n33; r[ 11 ] = this.n43; + r[ 12 ] = this.n14; r[ 13 ] = this.n24; r[ 14 ] = this.n34; r[ 15 ] = this.n44; + + return this; + + }, + + multiplySelf : function ( m ) { + + this.multiply( this, m ); + + return this; + + }, + + multiplyScalar : function ( s ) { + + this.n11 *= s; this.n12 *= s; this.n13 *= s; this.n14 *= s; + this.n21 *= s; this.n22 *= s; this.n23 *= s; this.n24 *= s; + this.n31 *= s; this.n32 *= s; this.n33 *= s; this.n34 *= s; + this.n41 *= s; this.n42 *= s; this.n43 *= s; this.n44 *= s; + + return this; + + }, + + determinant : function () { + + var n11 = this.n11, n12 = this.n12, n13 = this.n13, n14 = this.n14, + n21 = this.n21, n22 = this.n22, n23 = this.n23, n24 = this.n24, + n31 = this.n31, n32 = this.n32, n33 = this.n33, n34 = this.n34, + n41 = this.n41, n42 = this.n42, n43 = this.n43, n44 = this.n44; + + //TODO: make this more efficient + //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm ) + return ( + n14 * n23 * n32 * n41- + n13 * n24 * n32 * n41- + n14 * n22 * n33 * n41+ + n12 * n24 * n33 * n41+ + + n13 * n22 * n34 * n41- + n12 * n23 * n34 * n41- + n14 * n23 * n31 * n42+ + n13 * n24 * n31 * n42+ + + n14 * n21 * n33 * n42- + n11 * n24 * n33 * n42- + n13 * n21 * n34 * n42+ + n11 * n23 * n34 * n42+ + + n14 * n22 * n31 * n43- + n12 * n24 * n31 * n43- + n14 * n21 * n32 * n43+ + n11 * n24 * n32 * n43+ + + n12 * n21 * n34 * n43- + n11 * n22 * n34 * n43- + n13 * n22 * n31 * n44+ + n12 * n23 * n31 * n44+ + + n13 * n21 * n32 * n44- + n11 * n23 * n32 * n44- + n12 * n21 * n33 * n44+ + n11 * n22 * n33 * n44 + ); + + }, + + transpose : function () { + + var tmp; + + tmp = this.n21; this.n21 = this.n12; this.n12 = tmp; + tmp = this.n31; this.n31 = this.n13; this.n13 = tmp; + tmp = this.n32; this.n32 = this.n23; this.n23 = tmp; + + tmp = this.n41; this.n41 = this.n14; this.n14 = tmp; + tmp = this.n42; this.n42 = this.n24; this.n24 = tmp; + tmp = this.n43; this.n43 = this.n34; this.n43 = tmp; + + return this; + + }, + + clone : function () { + + var m = new THREE.Matrix4(); + + m.n11 = this.n11; m.n12 = this.n12; m.n13 = this.n13; m.n14 = this.n14; + m.n21 = this.n21; m.n22 = this.n22; m.n23 = this.n23; m.n24 = this.n24; + m.n31 = this.n31; m.n32 = this.n32; m.n33 = this.n33; m.n34 = this.n34; + m.n41 = this.n41; m.n42 = this.n42; m.n43 = this.n43; m.n44 = this.n44; + + return m; + + }, + + flatten : function () { + + this.flat[ 0 ] = this.n11; this.flat[ 1 ] = this.n21; this.flat[ 2 ] = this.n31; this.flat[ 3 ] = this.n41; + this.flat[ 4 ] = this.n12; this.flat[ 5 ] = this.n22; this.flat[ 6 ] = this.n32; this.flat[ 7 ] = this.n42; + this.flat[ 8 ] = this.n13; this.flat[ 9 ] = this.n23; this.flat[ 10 ] = this.n33; this.flat[ 11 ] = this.n43; + this.flat[ 12 ] = this.n14; this.flat[ 13 ] = this.n24; this.flat[ 14 ] = this.n34; this.flat[ 15 ] = this.n44; + + return this.flat; + + }, + + flattenToArray : function ( flat ) { + + flat[ 0 ] = this.n11; flat[ 1 ] = this.n21; flat[ 2 ] = this.n31; flat[ 3 ] = this.n41; + flat[ 4 ] = this.n12; flat[ 5 ] = this.n22; flat[ 6 ] = this.n32; flat[ 7 ] = this.n42; + flat[ 8 ] = this.n13; flat[ 9 ] = this.n23; flat[ 10 ] = this.n33; flat[ 11 ] = this.n43; + flat[ 12 ] = this.n14; flat[ 13 ] = this.n24; flat[ 14 ] = this.n34; flat[ 15 ] = this.n44; + + return flat; + + }, + + flattenToArrayOffset : function( flat, offset ) { + + flat[ offset ] = this.n11; + flat[ offset + 1 ] = this.n21; + flat[ offset + 2 ] = this.n31; + flat[ offset + 3 ] = this.n41; + + flat[ offset + 4 ] = this.n12; + flat[ offset + 5 ] = this.n22; + flat[ offset + 6 ] = this.n32; + flat[ offset + 7 ] = this.n42; + + flat[ offset + 8 ] = this.n13; + flat[ offset + 9 ] = this.n23; + flat[ offset + 10 ] = this.n33; + flat[ offset + 11 ] = this.n43; + + flat[ offset + 12 ] = this.n14; + flat[ offset + 13 ] = this.n24; + flat[ offset + 14 ] = this.n34; + flat[ offset + 15 ] = this.n44; + + return flat; + + }, + + setTranslation : function( x, y, z ) { + + this.set( + + 1, 0, 0, x, + 0, 1, 0, y, + 0, 0, 1, z, + 0, 0, 0, 1 + + ); + + return this; + + }, + + setScale : function ( x, y, z ) { + + this.set( + + x, 0, 0, 0, + 0, y, 0, 0, + 0, 0, z, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + setRotationX : function ( theta ) { + + var c = Math.cos( theta ), s = Math.sin( theta ); + + this.set( + + 1, 0, 0, 0, + 0, c, -s, 0, + 0, s, c, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + setRotationY : function( theta ) { + + var c = Math.cos( theta ), s = Math.sin( theta ); + + this.set( + + c, 0, s, 0, + 0, 1, 0, 0, + -s, 0, c, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + setRotationZ : function( theta ) { + + var c = Math.cos( theta ), s = Math.sin( theta ); + + this.set( + + c, -s, 0, 0, + s, c, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + setRotationAxis : function( axis, angle ) { + + // Based on http://www.gamedev.net/reference/articles/article1199.asp + + var c = Math.cos( angle ), + s = Math.sin( angle ), + t = 1 - c, + x = axis.x, y = axis.y, z = axis.z, + tx = t * x, ty = t * y; + + this.set( + + tx * x + c, tx * y - s * z, tx * z + s * y, 0, + tx * y + s * z, ty * y + c, ty * z - s * x, 0, + tx * z - s * y, ty * z + s * x, t * z * z + c, 0, + 0, 0, 0, 1 + + ); + + return this; + + }, + + setPosition : function( v ) { + + this.n14 = v.x; + this.n24 = v.y; + this.n34 = v.z; + + return this; + + }, + + getPosition: function() { + + if ( ! this.position ) { + + this.position = new THREE.Vector3(); + + } + + this.position.set( this.n14, this.n24, this.n34 ); + + return this.position; + + }, + + getColumnX: function() { + + if ( ! this.columnX ) { + + this.columnX = new THREE.Vector3(); + + } + + this.columnX.set( this.n11, this.n21, this.n31 ); + + return this.columnX; + }, + + getColumnY: function() { + + if ( ! this.columnY ) { + + this.columnY = new THREE.Vector3(); + + } + + this.columnY.set( this.n12, this.n22, this.n32 ); + + return this.columnY; + + }, + + getColumnZ: function() { + + if ( ! this.columnZ ) { + + this.columnZ = new THREE.Vector3(); + + } + + this.columnZ.set( this.n13, this.n23, this.n33 ); + + return this.columnZ; + + }, + + setRotationFromEuler: function( v, order ) { + + var x = v.x, y = v.y, z = v.z, + a = Math.cos( x ), b = Math.sin( x ), + c = Math.cos( y ), d = Math.sin( y ), + e = Math.cos( z ), f = Math.sin( z ); + + switch ( order ) { + case 'YXZ': + var ce = c * e, cf = c * f, de = d * e, df = d * f; + + this.n11 = ce + df * b; + this.n12 = de * b - cf; + this.n13 = a * d; + + this.n21 = a * f; + this.n22 = a * e; + this.n23 = - b; + + this.n31 = cf * b - de; + this.n32 = df + ce * b; + this.n33 = a * c; + break; + + case 'ZXY': + var ce = c * e, cf = c * f, de = d * e, df = d * f; + + this.n11 = ce - df * b; + this.n12 = - a * f; + this.n13 = de + cf * b; + + this.n21 = cf + de * b; + this.n22 = a * e; + this.n23 = df - ce * b; + + this.n31 = - a * d; + this.n32 = b; + this.n33 = a * c; + break; + + case 'ZYX': + var ae = a * e, af = a * f, be = b * e, bf = b * f; + + this.n11 = c * e; + this.n12 = be * d - af; + this.n13 = ae * d + bf; + + this.n21 = c * f; + this.n22 = bf * d + ae; + this.n23 = af * d - be; + + this.n31 = - d; + this.n32 = b * c; + this.n33 = a * c; + break; + + case 'YZX': + var ac = a * c, ad = a * d, bc = b * c, bd = b * d; + + this.n11 = c * e; + this.n12 = bd - ac * f; + this.n13 = bc * f + ad; + + this.n21 = f; + this.n22 = a * e; + this.n23 = - b * e; + + this.n31 = - d * e; + this.n32 = ad * f + bc; + this.n33 = ac - bd * f; + break; + + case 'XZY': + var ac = a * c, ad = a * d, bc = b * c, bd = b * d; + + this.n11 = c * e; + this.n12 = - f; + this.n13 = d * e; + + this.n21 = ac * f + bd; + this.n22 = a * e; + this.n23 = ad * f - bc; + + this.n31 = bc * f - ad; + this.n32 = b * e; + this.n33 = bd * f + ac; + break; + + default: // 'XYZ' + var ae = a * e, af = a * f, be = b * e, bf = b * f; + + this.n11 = c * e; + this.n12 = - c * f; + this.n13 = d; + + this.n21 = af + be * d; + this.n22 = ae - bf * d; + this.n23 = - b * c; + + this.n31 = bf - ae * d; + this.n32 = be + af * d; + this.n33 = a * c; + break; + } + + return this; + + }, + + + setRotationFromQuaternion: function( q ) { + + var x = q.x, y = q.y, z = q.z, w = q.w, + x2 = x + x, y2 = y + y, z2 = z + z, + xx = x * x2, xy = x * y2, xz = x * z2, + yy = y * y2, yz = y * z2, zz = z * z2, + wx = w * x2, wy = w * y2, wz = w * z2; + + this.n11 = 1 - ( yy + zz ); + this.n12 = xy - wz; + this.n13 = xz + wy; + + this.n21 = xy + wz; + this.n22 = 1 - ( xx + zz ); + this.n23 = yz - wx; + + this.n31 = xz - wy; + this.n32 = yz + wx; + this.n33 = 1 - ( xx + yy ); + + return this; + + }, + + scale: function ( v ) { + + var x = v.x, y = v.y, z = v.z; + + this.n11 *= x; this.n12 *= y; this.n13 *= z; + this.n21 *= x; this.n22 *= y; this.n23 *= z; + this.n31 *= x; this.n32 *= y; this.n33 *= z; + this.n41 *= x; this.n42 *= y; this.n43 *= z; + + return this; + + }, + + extractPosition: function ( m ) { + + this.n14 = m.n14; + this.n24 = m.n24; + this.n34 = m.n34; + + }, + + extractRotation: function ( m, s ) { + + var invScaleX = 1 / s.x, invScaleY = 1 / s.y, invScaleZ = 1 / s.z; + + this.n11 = m.n11 * invScaleX; + this.n21 = m.n21 * invScaleX; + this.n31 = m.n31 * invScaleX; + + this.n12 = m.n12 * invScaleY; + this.n22 = m.n22 * invScaleY; + this.n32 = m.n32 * invScaleY; + + this.n13 = m.n13 * invScaleZ; + this.n23 = m.n23 * invScaleZ; + this.n33 = m.n33 * invScaleZ; + + } + +}; + +THREE.Matrix4.makeInvert = function ( m1, m2 ) { + + // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm + + var n11 = m1.n11, n12 = m1.n12, n13 = m1.n13, n14 = m1.n14, + n21 = m1.n21, n22 = m1.n22, n23 = m1.n23, n24 = m1.n24, + n31 = m1.n31, n32 = m1.n32, n33 = m1.n33, n34 = m1.n34, + n41 = m1.n41, n42 = m1.n42, n43 = m1.n43, n44 = m1.n44; + + if ( m2 === undefined ) m2 = new THREE.Matrix4(); + + m2.n11 = n23*n34*n42 - n24*n33*n42 + n24*n32*n43 - n22*n34*n43 - n23*n32*n44 + n22*n33*n44; + m2.n12 = n14*n33*n42 - n13*n34*n42 - n14*n32*n43 + n12*n34*n43 + n13*n32*n44 - n12*n33*n44; + m2.n13 = n13*n24*n42 - n14*n23*n42 + n14*n22*n43 - n12*n24*n43 - n13*n22*n44 + n12*n23*n44; + m2.n14 = n14*n23*n32 - n13*n24*n32 - n14*n22*n33 + n12*n24*n33 + n13*n22*n34 - n12*n23*n34; + m2.n21 = n24*n33*n41 - n23*n34*n41 - n24*n31*n43 + n21*n34*n43 + n23*n31*n44 - n21*n33*n44; + m2.n22 = n13*n34*n41 - n14*n33*n41 + n14*n31*n43 - n11*n34*n43 - n13*n31*n44 + n11*n33*n44; + m2.n23 = n14*n23*n41 - n13*n24*n41 - n14*n21*n43 + n11*n24*n43 + n13*n21*n44 - n11*n23*n44; + m2.n24 = n13*n24*n31 - n14*n23*n31 + n14*n21*n33 - n11*n24*n33 - n13*n21*n34 + n11*n23*n34; + m2.n31 = n22*n34*n41 - n24*n32*n41 + n24*n31*n42 - n21*n34*n42 - n22*n31*n44 + n21*n32*n44; + m2.n32 = n14*n32*n41 - n12*n34*n41 - n14*n31*n42 + n11*n34*n42 + n12*n31*n44 - n11*n32*n44; + m2.n33 = n13*n24*n41 - n14*n22*n41 + n14*n21*n42 - n11*n24*n42 - n12*n21*n44 + n11*n22*n44; + m2.n34 = n14*n22*n31 - n12*n24*n31 - n14*n21*n32 + n11*n24*n32 + n12*n21*n34 - n11*n22*n34; + m2.n41 = n23*n32*n41 - n22*n33*n41 - n23*n31*n42 + n21*n33*n42 + n22*n31*n43 - n21*n32*n43; + m2.n42 = n12*n33*n41 - n13*n32*n41 + n13*n31*n42 - n11*n33*n42 - n12*n31*n43 + n11*n32*n43; + m2.n43 = n13*n22*n41 - n12*n23*n41 - n13*n21*n42 + n11*n23*n42 + n12*n21*n43 - n11*n22*n43; + m2.n44 = n12*n23*n31 - n13*n22*n31 + n13*n21*n32 - n11*n23*n32 - n12*n21*n33 + n11*n22*n33; + m2.multiplyScalar( 1 / m1.determinant() ); + + return m2; + +}; + +THREE.Matrix4.makeInvert3x3 = function ( m1 ) { + + // input: THREE.Matrix4, output: THREE.Matrix3 + // ( based on http://code.google.com/p/webgl-mjs/ ) + + var m33 = m1.m33, m33m = m33.m, + a11 = m1.n33 * m1.n22 - m1.n32 * m1.n23, + a21 = - m1.n33 * m1.n21 + m1.n31 * m1.n23, + a31 = m1.n32 * m1.n21 - m1.n31 * m1.n22, + a12 = - m1.n33 * m1.n12 + m1.n32 * m1.n13, + a22 = m1.n33 * m1.n11 - m1.n31 * m1.n13, + a32 = - m1.n32 * m1.n11 + m1.n31 * m1.n12, + a13 = m1.n23 * m1.n12 - m1.n22 * m1.n13, + a23 = - m1.n23 * m1.n11 + m1.n21 * m1.n13, + a33 = m1.n22 * m1.n11 - m1.n21 * m1.n12, + + det = m1.n11 * a11 + m1.n21 * a12 + m1.n31 * a13, + + idet; + + // no inverse + if ( det == 0 ) { + + console.error( 'THREE.Matrix4.makeInvert3x3: Matrix not invertible.' ); + + } + + idet = 1.0 / det; + + m33m[ 0 ] = idet * a11; m33m[ 1 ] = idet * a21; m33m[ 2 ] = idet * a31; + m33m[ 3 ] = idet * a12; m33m[ 4 ] = idet * a22; m33m[ 5 ] = idet * a32; + m33m[ 6 ] = idet * a13; m33m[ 7 ] = idet * a23; m33m[ 8 ] = idet * a33; + + return m33; + +} + +THREE.Matrix4.makeFrustum = function ( left, right, bottom, top, near, far ) { + + var m, x, y, a, b, c, d; + + m = new THREE.Matrix4(); + x = 2 * near / ( right - left ); + y = 2 * near / ( top - bottom ); + a = ( right + left ) / ( right - left ); + b = ( top + bottom ) / ( top - bottom ); + c = - ( far + near ) / ( far - near ); + d = - 2 * far * near / ( far - near ); + + m.n11 = x; m.n12 = 0; m.n13 = a; m.n14 = 0; + m.n21 = 0; m.n22 = y; m.n23 = b; m.n24 = 0; + m.n31 = 0; m.n32 = 0; m.n33 = c; m.n34 = d; + m.n41 = 0; m.n42 = 0; m.n43 = - 1; m.n44 = 0; + + return m; + +}; + +THREE.Matrix4.makePerspective = function ( fov, aspect, near, far ) { + + var ymax, ymin, xmin, xmax; + + ymax = near * Math.tan( fov * Math.PI / 360 ); + ymin = - ymax; + xmin = ymin * aspect; + xmax = ymax * aspect; + + return THREE.Matrix4.makeFrustum( xmin, xmax, ymin, ymax, near, far ); + +}; + +THREE.Matrix4.makeOrtho = function ( left, right, top, bottom, near, far ) { + + var m, x, y, z, w, h, p; + + m = new THREE.Matrix4(); + w = right - left; + h = top - bottom; + p = far - near; + x = ( right + left ) / w; + y = ( top + bottom ) / h; + z = ( far + near ) / p; + + m.n11 = 2 / w; m.n12 = 0; m.n13 = 0; m.n14 = -x; + m.n21 = 0; m.n22 = 2 / h; m.n23 = 0; m.n24 = -y; + m.n31 = 0; m.n32 = 0; m.n33 = -2 / p; m.n34 = -z; + m.n41 = 0; m.n42 = 0; m.n43 = 0; m.n44 = 1; + + return m; + +}; + +THREE.Matrix4.__v1 = new THREE.Vector3(); +THREE.Matrix4.__v2 = new THREE.Vector3(); +THREE.Matrix4.__v3 = new THREE.Vector3(); +/** + * @author mr.doob / http://mrdoob.com/ + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Object3D = function() { + + this.parent = undefined; + this.children = []; + + this.up = new THREE.Vector3( 0, 1, 0 ); + + this.position = new THREE.Vector3(); + this.rotation = new THREE.Vector3(); + this.eulerOrder = 'XYZ'; + this.scale = new THREE.Vector3( 1, 1, 1 ); + + this.dynamic = false; // when true it retains arrays so they can be updated with __dirty* + + this.doubleSided = false; + this.flipSided = false; + + this.renderDepth = null; + + this.rotationAutoUpdate = true; + + this.matrix = new THREE.Matrix4(); + this.matrixWorld = new THREE.Matrix4(); + this.matrixRotationWorld = new THREE.Matrix4(); + + this.matrixAutoUpdate = true; + this.matrixWorldNeedsUpdate = true; + + this.quaternion = new THREE.Quaternion(); + this.useQuaternion = false; + + this.boundRadius = 0.0; + this.boundRadiusScale = 1.0; + + this.visible = true; + + this._vector = new THREE.Vector3(); + + this.name = ""; + +}; + + +THREE.Object3D.prototype = { + + translate : function ( distance, axis ) { + + this.matrix.rotateAxis( axis ); + this.position.addSelf( axis.multiplyScalar( distance ) ); + + }, + + translateX : function ( distance ) { + + this.translate( distance, this._vector.set( 1, 0, 0 ) ); + + }, + + translateY : function ( distance ) { + + this.translate( distance, this._vector.set( 0, 1, 0 ) ); + + }, + + translateZ : function ( distance ) { + + this.translate( distance, this._vector.set( 0, 0, 1 ) ); + + }, + + lookAt : function ( vector ) { + + // TODO: Add hierarchy support. + + this.matrix.lookAt( vector, this.position, this.up ); + + if ( this.rotationAutoUpdate ) { + + this.rotation.setRotationFromMatrix( this.matrix ); + + } + + }, + + addChild: function ( child ) { + + if ( this.children.indexOf( child ) === - 1 ) { + + if( child.parent !== undefined ) { + + child.parent.removeChild( child ); + + } + + child.parent = this; + this.children.push( child ); + + // add to scene + + var scene = this; + + while ( scene.parent !== undefined ) { + + scene = scene.parent; + + } + + if ( scene !== undefined && scene instanceof THREE.Scene ) { + + scene.addChildRecurse( child ); + + } + + } + + }, + + removeChild: function ( child ) { + + var childIndex = this.children.indexOf( child ); + + if ( childIndex !== - 1 ) { + + child.parent = undefined; + this.children.splice( childIndex, 1 ); + + } + + }, + + getChildByName: function ( name, doRecurse ) { + + var c, cl, child, recurseResult; + + for ( c = 0, cl = this.children.length; c < cl; c++ ) { + + child = this.children[ c ]; + + if ( child.name === name ) { + + return child; + + } + + if ( doRecurse ) { + + recurseResult = child.getChildByName( name, doRecurse ); + + if ( recurseResult !== undefined ) { + + return recurseResult; + + } + + } + + } + + return undefined; + + }, + + updateMatrix: function () { + + this.matrix.setPosition( this.position ); + + if ( this.useQuaternion ) { + + this.matrix.setRotationFromQuaternion( this.quaternion ); + + } else { + + this.matrix.setRotationFromEuler( this.rotation, this.eulerOrder ); + + } + + if ( this.scale.x !== 1 || this.scale.y !== 1 || this.scale.z !== 1 ) { + + this.matrix.scale( this.scale ); + this.boundRadiusScale = Math.max( this.scale.x, Math.max( this.scale.y, this.scale.z ) ); + + } + + this.matrixWorldNeedsUpdate = true; + + }, + + update: function ( parentMatrixWorld, forceUpdate, camera ) { + + this.matrixAutoUpdate && this.updateMatrix(); + + // update matrixWorld + + if ( this.matrixWorldNeedsUpdate || forceUpdate ) { + + if ( parentMatrixWorld ) { + + this.matrixWorld.multiply( parentMatrixWorld, this.matrix ); + + } else { + + this.matrixWorld.copy( this.matrix ); + + } + + this.matrixRotationWorld.extractRotation( this.matrixWorld, this.scale ); + + this.matrixWorldNeedsUpdate = false; + + forceUpdate = true; + + } + + // update children + + for ( var i = 0, l = this.children.length; i < l; i ++ ) { + + this.children[ i ].update( this.matrixWorld, forceUpdate, camera ); + + } + + } + +}; +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Quaternion = function( x, y, z, w ) { + + this.set( + + x || 0, + y || 0, + z || 0, + w !== undefined ? w : 1 + + ); + +}; + +THREE.Quaternion.prototype = { + + set : function ( x, y, z, w ) { + + this.x = x; + this.y = y; + this.z = z; + this.w = w; + + return this; + + }, + + copy : function ( q ) { + + this.x = q.x; + this.y = q.y; + this.z = q.z; + this.w = q.w; + + return this; + + }, + + setFromEuler : function ( vec3 ) { + + var c = 0.5 * Math.PI / 360, // 0.5 is an optimization + x = vec3.x * c, + y = vec3.y * c, + z = vec3.z * c, + + c1 = Math.cos( y ), + s1 = Math.sin( y ), + c2 = Math.cos( -z ), + s2 = Math.sin( -z ), + c3 = Math.cos( x ), + s3 = Math.sin( x ), + + c1c2 = c1 * c2, + s1s2 = s1 * s2; + + this.w = c1c2 * c3 - s1s2 * s3; + this.x = c1c2 * s3 + s1s2 * c3; + this.y = s1 * c2 * c3 + c1 * s2 * s3; + this.z = c1 * s2 * c3 - s1 * c2 * s3; + + return this; + + }, + + setFromAxisAngle: function ( axis, angle ) { + + // from http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm + // axis have to be normalized + + var halfAngle = angle / 2, + s = Math.sin( halfAngle ); + + this.x = axis.x * s; + this.y = axis.y * s; + this.z = axis.z * s; + this.w = Math.cos( halfAngle ); + + return this; + + }, + + calculateW : function () { + + this.w = - Math.sqrt( Math.abs( 1.0 - this.x * this.x - this.y * this.y - this.z * this.z ) ); + + return this; + + }, + + inverse : function () { + + this.x *= -1; + this.y *= -1; + this.z *= -1; + + return this; + + }, + + length : function () { + + return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w ); + + }, + + normalize : function () { + + var l = Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w ); + + if ( l == 0 ) { + + this.x = 0; + this.y = 0; + this.z = 0; + this.w = 0; + + } else { + + l = 1 / l; + + this.x = this.x * l; + this.y = this.y * l; + this.z = this.z * l; + this.w = this.w * l; + + } + + return this; + + }, + + multiplySelf : function ( quat2 ) { + + var qax = this.x, qay = this.y, qaz = this.z, qaw = this.w, + qbx = quat2.x, qby = quat2.y, qbz = quat2.z, qbw = quat2.w; + + this.x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; + this.y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; + this.z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; + this.w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; + + return this; + + }, + + multiply: function ( q1, q2 ) { + + // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm + + this.x = q1.x * q2.w + q1.y * q2.z - q1.z * q2.y + q1.w * q2.x; + this.y = -q1.x * q2.z + q1.y * q2.w + q1.z * q2.x + q1.w * q2.y; + this.z = q1.x * q2.y - q1.y * q2.x + q1.z * q2.w + q1.w * q2.z; + this.w = -q1.x * q2.x - q1.y * q2.y - q1.z * q2.z + q1.w * q2.w; + + return this; + + }, + + multiplyVector3 : function ( vec, dest ) { + + if( !dest ) { dest = vec; } + + var x = vec.x, y = vec.y, z = vec.z, + qx = this.x, qy = this.y, qz = this.z, qw = this.w; + + // calculate quat * vec + + var ix = qw * x + qy * z - qz * y, + iy = qw * y + qz * x - qx * z, + iz = qw * z + qx * y - qy * x, + iw = -qx * x - qy * y - qz * z; + + // calculate result * inverse quat + + dest.x = ix * qw + iw * -qx + iy * -qz - iz * -qy; + dest.y = iy * qw + iw * -qy + iz * -qx - ix * -qz; + dest.z = iz * qw + iw * -qz + ix * -qy - iy * -qx; + + return dest; + + } + +} + +THREE.Quaternion.slerp = function ( qa, qb, qm, t ) { + + var cosHalfTheta = qa.w * qb.w + qa.x * qb.x + qa.y * qb.y + qa.z * qb.z; + + if ( Math.abs( cosHalfTheta ) >= 1.0 ) { + + qm.w = qa.w; qm.x = qa.x; qm.y = qa.y; qm.z = qa.z; + return qm; + + } + + var halfTheta = Math.acos( cosHalfTheta ), + sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta ); + + if ( Math.abs( sinHalfTheta ) < 0.001 ) { + + qm.w = 0.5 * ( qa.w + qb.w ); + qm.x = 0.5 * ( qa.x + qb.x ); + qm.y = 0.5 * ( qa.y + qb.y ); + qm.z = 0.5 * ( qa.z + qb.z ); + + return qm; + + } + + var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta, + ratioB = Math.sin( t * halfTheta ) / sinHalfTheta; + + qm.w = ( qa.w * ratioA + qb.w * ratioB ); + qm.x = ( qa.x * ratioA + qb.x * ratioB ); + qm.y = ( qa.y * ratioA + qb.y * ratioB ); + qm.z = ( qa.z * ratioA + qb.z * ratioB ); + + return qm; + +} +/** + * @author mr.doob / http://mrdoob.com/ + */ + +THREE.Vertex = function ( position ) { + + this.position = position || new THREE.Vector3(); + +}; +/** + * @author mr.doob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Face3 = function ( a, b, c, normal, color, materials ) { + + this.a = a; + this.b = b; + this.c = c; + + this.normal = normal instanceof THREE.Vector3 ? normal : new THREE.Vector3(); + this.vertexNormals = normal instanceof Array ? normal : [ ]; + + this.color = color instanceof THREE.Color ? color : new THREE.Color(); + this.vertexColors = color instanceof Array ? color : []; + + this.vertexTangents = []; + + this.materials = materials instanceof Array ? materials : [ materials ]; + + this.centroid = new THREE.Vector3(); + +}; +/** + * @author mr.doob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Face4 = function ( a, b, c, d, normal, color, materials ) { + + this.a = a; + this.b = b; + this.c = c; + this.d = d; + + this.normal = normal instanceof THREE.Vector3 ? normal : new THREE.Vector3(); + this.vertexNormals = normal instanceof Array ? normal : [ ]; + + this.color = color instanceof THREE.Color ? color : new THREE.Color(); + this.vertexColors = color instanceof Array ? color : []; + + this.vertexTangents = []; + + this.materials = materials instanceof Array ? materials : [ materials ]; + + this.centroid = new THREE.Vector3(); + +}; +/** + * @author mr.doob / http://mrdoob.com/ + */ + +THREE.UV = function ( u, v ) { + + this.set( + + u || 0, + v || 0 + + ); + +}; + +THREE.UV.prototype = { + + set : function ( u, v ) { + + this.u = u; + this.v = v; + + return this; + + }, + + copy : function ( uv ) { + + this.set( + + uv.u, + uv.v + + ); + + return this; + + } + +}; +/** + * @author mr.doob / http://mrdoob.com/ + * @author kile / http://kile.stravaganza.org/ + * @author alteredq / http://alteredqualia.com/ + * @author mikael emtinger / http://gomo.se/ + */ + +THREE.Geometry = function () { + + this.id = "Geometry" + THREE.GeometryIdCounter ++; + + this.vertices = []; + this.colors = []; // one-to-one vertex colors, used in ParticleSystem, Line and Ribbon + + this.faces = []; + + this.edges = []; + + this.faceUvs = [[]]; + this.faceVertexUvs = [[]]; + + this.morphTargets = []; + this.morphColors = []; + + this.skinWeights = []; + this.skinIndices = []; + + this.boundingBox = null; + this.boundingSphere = null; + + this.hasTangents = false; + +}; + +THREE.Geometry.prototype = { + + computeCentroids: function () { + + var f, fl, face; + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + face.centroid.set( 0, 0, 0 ); + + if ( face instanceof THREE.Face3 ) { + + face.centroid.addSelf( this.vertices[ face.a ].position ); + face.centroid.addSelf( this.vertices[ face.b ].position ); + face.centroid.addSelf( this.vertices[ face.c ].position ); + face.centroid.divideScalar( 3 ); + + } else if ( face instanceof THREE.Face4 ) { + + face.centroid.addSelf( this.vertices[ face.a ].position ); + face.centroid.addSelf( this.vertices[ face.b ].position ); + face.centroid.addSelf( this.vertices[ face.c ].position ); + face.centroid.addSelf( this.vertices[ face.d ].position ); + face.centroid.divideScalar( 4 ); + + } + + } + + }, + + computeFaceNormals: function ( useVertexNormals ) { + + var n, nl, v, vl, vertex, f, fl, face, vA, vB, vC, + cb = new THREE.Vector3(), ab = new THREE.Vector3(); + + /* + for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { + + vertex = this.vertices[ v ]; + vertex.normal.set( 0, 0, 0 ); + + } + */ + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + + if ( useVertexNormals && face.vertexNormals.length ) { + + cb.set( 0, 0, 0 ); + + for ( n = 0, nl = face.vertexNormals.length; n < nl; n++ ) { + + cb.addSelf( face.vertexNormals[n] ); + + } + + cb.divideScalar( 3 ); + + if ( ! cb.isZero() ) { + + cb.normalize(); + + } + + face.normal.copy( cb ); + + } else { + + vA = this.vertices[ face.a ]; + vB = this.vertices[ face.b ]; + vC = this.vertices[ face.c ]; + + cb.sub( vC.position, vB.position ); + ab.sub( vA.position, vB.position ); + cb.crossSelf( ab ); + + if ( !cb.isZero() ) { + + cb.normalize(); + + } + + face.normal.copy( cb ); + + } + + } + + }, + + computeVertexNormals: function () { + + var v, vl, f, fl, face, vertices; + + // create internal buffers for reuse when calling this method repeatedly + // (otherwise memory allocation / deallocation every frame is big resource hog) + + if ( this.__tmpVertices == undefined ) { + + this.__tmpVertices = new Array( this.vertices.length ); + vertices = this.__tmpVertices; + + for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { + + vertices[ v ] = new THREE.Vector3(); + + } + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + + if ( face instanceof THREE.Face3 ) { + + face.vertexNormals = [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ]; + + } else if ( face instanceof THREE.Face4 ) { + + face.vertexNormals = [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ]; + + } + + } + + } else { + + vertices = this.__tmpVertices; + + for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { + + vertices[ v ].set( 0, 0, 0 ); + + } + + } + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + + if ( face instanceof THREE.Face3 ) { + + vertices[ face.a ].addSelf( face.normal ); + vertices[ face.b ].addSelf( face.normal ); + vertices[ face.c ].addSelf( face.normal ); + + } else if ( face instanceof THREE.Face4 ) { + + vertices[ face.a ].addSelf( face.normal ); + vertices[ face.b ].addSelf( face.normal ); + vertices[ face.c ].addSelf( face.normal ); + vertices[ face.d ].addSelf( face.normal ); + + } + + } + + for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { + + vertices[ v ].normalize(); + + } + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + + if ( face instanceof THREE.Face3 ) { + + face.vertexNormals[ 0 ].copy( vertices[ face.a ] ); + face.vertexNormals[ 1 ].copy( vertices[ face.b ] ); + face.vertexNormals[ 2 ].copy( vertices[ face.c ] ); + + } else if ( face instanceof THREE.Face4 ) { + + face.vertexNormals[ 0 ].copy( vertices[ face.a ] ); + face.vertexNormals[ 1 ].copy( vertices[ face.b ] ); + face.vertexNormals[ 2 ].copy( vertices[ face.c ] ); + face.vertexNormals[ 3 ].copy( vertices[ face.d ] ); + + } + + } + + }, + + computeTangents: function () { + + // based on http://www.terathon.com/code/tangent.html + // tangents go to vertices + + var f, fl, v, vl, i, il, vertexIndex, + face, uv, vA, vB, vC, uvA, uvB, uvC, + x1, x2, y1, y2, z1, z2, + s1, s2, t1, t2, r, t, test, + tan1 = [], tan2 = [], + sdir = new THREE.Vector3(), tdir = new THREE.Vector3(), + tmp = new THREE.Vector3(), tmp2 = new THREE.Vector3(), + n = new THREE.Vector3(), w; + + for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { + + tan1[ v ] = new THREE.Vector3(); + tan2[ v ] = new THREE.Vector3(); + + } + + function handleTriangle( context, a, b, c, ua, ub, uc ) { + + vA = context.vertices[ a ].position; + vB = context.vertices[ b ].position; + vC = context.vertices[ c ].position; + + uvA = uv[ ua ]; + uvB = uv[ ub ]; + uvC = uv[ uc ]; + + x1 = vB.x - vA.x; + x2 = vC.x - vA.x; + y1 = vB.y - vA.y; + y2 = vC.y - vA.y; + z1 = vB.z - vA.z; + z2 = vC.z - vA.z; + + s1 = uvB.u - uvA.u; + s2 = uvC.u - uvA.u; + t1 = uvB.v - uvA.v; + t2 = uvC.v - uvA.v; + + r = 1.0 / ( s1 * t2 - s2 * t1 ); + sdir.set( ( t2 * x1 - t1 * x2 ) * r, + ( t2 * y1 - t1 * y2 ) * r, + ( t2 * z1 - t1 * z2 ) * r ); + tdir.set( ( s1 * x2 - s2 * x1 ) * r, + ( s1 * y2 - s2 * y1 ) * r, + ( s1 * z2 - s2 * z1 ) * r ); + + tan1[ a ].addSelf( sdir ); + tan1[ b ].addSelf( sdir ); + tan1[ c ].addSelf( sdir ); + + tan2[ a ].addSelf( tdir ); + tan2[ b ].addSelf( tdir ); + tan2[ c ].addSelf( tdir ); + + } + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + uv = this.faceVertexUvs[ 0 ][ f ]; // use UV layer 0 for tangents + + if ( face instanceof THREE.Face3 ) { + + handleTriangle( this, face.a, face.b, face.c, 0, 1, 2 ); + + } else if ( face instanceof THREE.Face4 ) { + + handleTriangle( this, face.a, face.b, face.c, 0, 1, 2 ); + handleTriangle( this, face.a, face.b, face.d, 0, 1, 3 ); + + } + + } + + var faceIndex = [ 'a', 'b', 'c', 'd' ]; + + for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { + + face = this.faces[ f ]; + + for ( i = 0; i < face.vertexNormals.length; i++ ) { + + n.copy( face.vertexNormals[ i ] ); + + vertexIndex = face[ faceIndex[ i ] ]; + + t = tan1[ vertexIndex ]; + + // Gram-Schmidt orthogonalize + + tmp.copy( t ); + tmp.subSelf( n.multiplyScalar( n.dot( t ) ) ).normalize(); + + // Calculate handedness + + tmp2.cross( face.vertexNormals[ i ], t ); + test = tmp2.dot( tan2[ vertexIndex ] ); + w = (test < 0.0) ? -1.0 : 1.0; + + face.vertexTangents[ i ] = new THREE.Vector4( tmp.x, tmp.y, tmp.z, w ); + + } + + } + + this.hasTangents = true; + + }, + + computeBoundingBox: function () { + + var vertex; + + if ( this.vertices.length > 0 ) { + + this.boundingBox = { 'x': [ this.vertices[ 0 ].position.x, this.vertices[ 0 ].position.x ], + 'y': [ this.vertices[ 0 ].position.y, this.vertices[ 0 ].position.y ], + 'z': [ this.vertices[ 0 ].position.z, this.vertices[ 0 ].position.z ] }; + + for ( var v = 1, vl = this.vertices.length; v < vl; v ++ ) { + + vertex = this.vertices[ v ]; + + if ( vertex.position.x < this.boundingBox.x[ 0 ] ) { + + this.boundingBox.x[ 0 ] = vertex.position.x; + + } else if ( vertex.position.x > this.boundingBox.x[ 1 ] ) { + + this.boundingBox.x[ 1 ] = vertex.position.x; + + } + + if ( vertex.position.y < this.boundingBox.y[ 0 ] ) { + + this.boundingBox.y[ 0 ] = vertex.position.y; + + } else if ( vertex.position.y > this.boundingBox.y[ 1 ] ) { + + this.boundingBox.y[ 1 ] = vertex.position.y; + + } + + if ( vertex.position.z < this.boundingBox.z[ 0 ] ) { + + this.boundingBox.z[ 0 ] = vertex.position.z; + + } else if ( vertex.position.z > this.boundingBox.z[ 1 ] ) { + + this.boundingBox.z[ 1 ] = vertex.position.z; + + } + + } + + } + + }, + + computeBoundingSphere: function () { + + // var radius = this.boundingSphere === null ? 0 : this.boundingSphere.radius; + + var radius = 0; + + for ( var v = 0, vl = this.vertices.length; v < vl; v ++ ) { + + radius = Math.max( radius, this.vertices[ v ].position.length() ); + + } + + this.boundingSphere = { radius: radius }; + + }, + + computeEdgeFaces: function () { + + function edge_hash( a, b ) { + + return Math.min( a, b ) + "_" + Math.max( a, b ); + + }; + + function addToMap( map, hash, i ) { + + if ( map[ hash ] === undefined ) { + + map[ hash ] = { "set": {}, "array": [] }; + map[ hash ].set[ i ] = 1; + map[ hash ].array.push( i ); + + } else { + + if( map[ hash ].set[ i ] === undefined ) { + + map[ hash ].set[ i ] = 1; + map[ hash ].array.push( i ); + + } + + } + + }; + + var i, il, v1, v2, j, k, + face, faceIndices, faceIndex, + edge, + hash, + vfMap = {}; + + // construct vertex -> face map + + for( i = 0, il = this.faces.length; i < il; i ++ ) { + + face = this.faces[ i ]; + + if ( face instanceof THREE.Face3 ) { + + hash = edge_hash( face.a, face.b ); + addToMap( vfMap, hash, i ); + + hash = edge_hash( face.b, face.c ); + addToMap( vfMap, hash, i ); + + hash = edge_hash( face.a, face.c ); + addToMap( vfMap, hash, i ); + + } else if ( face instanceof THREE.Face4 ) { + + // in WebGLRenderer quad is tesselated + // to triangles: a,b,d / b,c,d + // shared edge is: b,d + + // should shared edge be included? + // comment out if not + + hash = edge_hash( face.b, face.d ); + addToMap( vfMap, hash, i ); + + hash = edge_hash( face.a, face.b ); + addToMap( vfMap, hash, i ); + + hash = edge_hash( face.a, face.d ); + addToMap( vfMap, hash, i ); + + hash = edge_hash( face.b, face.c ); + addToMap( vfMap, hash, i ); + + hash = edge_hash( face.c, face.d ); + addToMap( vfMap, hash, i ); + + } + + } + + // extract faces + + for( i = 0, il = this.edges.length; i < il; i ++ ) { + + edge = this.edges[ i ]; + + v1 = edge.vertexIndices[ 0 ]; + v2 = edge.vertexIndices[ 1 ]; + + edge.faceIndices = vfMap[ edge_hash( v1, v2 ) ].array; + + for( j = 0; j < edge.faceIndices.length; j ++ ) { + + faceIndex = edge.faceIndices[ j ]; + edge.faces.push( this.faces[ faceIndex ] ); + + } + + } + + } + +}; + +THREE.GeometryIdCounter = 0; +/** + * Spline from Tween.js, slightly optimized (and trashed) + * http://sole.github.com/tween.js/examples/05_spline.html + * + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Spline = function ( points ) { + + this.points = points; + + var c = [], v3 = { x: 0, y: 0, z: 0 }, + point, intPoint, weight, w2, w3, + pa, pb, pc, pd; + + this.initFromArray = function( a ) { + + this.points = []; + + for ( var i = 0; i < a.length; i++ ) { + + this.points[ i ] = { x: a[ i ][ 0 ], y: a[ i ][ 1 ], z: a[ i ][ 2 ] }; + + } + + }; + + this.getPoint = function ( k ) { + + point = ( this.points.length - 1 ) * k; + intPoint = Math.floor( point ); + weight = point - intPoint; + + c[ 0 ] = intPoint == 0 ? intPoint : intPoint - 1; + c[ 1 ] = intPoint; + c[ 2 ] = intPoint > this.points.length - 2 ? intPoint : intPoint + 1; + c[ 3 ] = intPoint > this.points.length - 3 ? intPoint : intPoint + 2; + + pa = this.points[ c[ 0 ] ]; + pb = this.points[ c[ 1 ] ]; + pc = this.points[ c[ 2 ] ]; + pd = this.points[ c[ 3 ] ]; + + w2 = weight * weight; + w3 = weight * w2; + + v3.x = interpolate( pa.x, pb.x, pc.x, pd.x, weight, w2, w3 ); + v3.y = interpolate( pa.y, pb.y, pc.y, pd.y, weight, w2, w3 ); + v3.z = interpolate( pa.z, pb.z, pc.z, pd.z, weight, w2, w3 ); + + return v3; + + }; + + this.getControlPointsArray = function () { + + var i, p, l = this.points.length, + coords = []; + + for ( i = 0; i < l; i ++ ) { + + p = this.points[ i ]; + coords[ i ] = [ p.x, p.y, p.z ]; + + } + + return coords; + + }; + + // approximate length by summing linear segments + + this.getLength = function ( nSubDivisions ) { + + var i, index, nSamples, + point = 0, intPoint = 0, oldIntPoint = 0, + oldPosition = new THREE.Vector3(), + tmpVec = new THREE.Vector3(), + chunkLengths = [], + totalLength = 0; + + // first point has 0 length + + chunkLengths[ 0 ] = 0; + + if ( !nSubDivisions ) nSubDivisions = 100; + + nSamples = this.points.length * nSubDivisions; + + oldPosition.copy( this.points[ 0 ] ); + + for ( i = 1; i < nSamples; i ++ ) { + + index = i / nSamples; + + position = this.getPoint( index ); + tmpVec.copy( position ); + + totalLength += tmpVec.distanceTo( oldPosition ); + + oldPosition.copy( position ); + + point = ( this.points.length - 1 ) * index; + intPoint = Math.floor( point ); + + if ( intPoint != oldIntPoint ) { + + chunkLengths[ intPoint ] = totalLength; + oldIntPoint = intPoint; + + } + + } + + // last point ends with total length + + chunkLengths[ chunkLengths.length ] = totalLength; + + return { chunks: chunkLengths, total: totalLength }; + + }; + + this.reparametrizeByArcLength = function ( samplingCoef ) { + + var i, j, + index, indexCurrent, indexNext, + linearDistance, realDistance, + sampling, + newpoints = [], + tmpVec = new THREE.Vector3(), + sl = this.getLength(); + + newpoints.push( tmpVec.copy( this.points[ 0 ] ).clone() ); + + for ( i = 1; i < this.points.length; i++ ) { + + //tmpVec.copy( this.points[ i - 1 ] ); + //linearDistance = tmpVec.distanceTo( this.points[ i ] ); + + realDistance = sl.chunks[ i ] - sl.chunks[ i - 1 ]; + + sampling = Math.ceil( samplingCoef * realDistance / sl.total ); + + indexCurrent = ( i - 1 ) / ( this.points.length - 1 ); + indexNext = i / ( this.points.length - 1 ); + + for ( j = 1; j < sampling - 1; j++ ) { + + index = indexCurrent + j * ( 1 / sampling ) * ( indexNext - indexCurrent ); + + position = this.getPoint( index ); + newpoints.push( tmpVec.copy( position ).clone() ); + + } + + newpoints.push( tmpVec.copy( this.points[ i ] ).clone() ); + + } + + this.points = newpoints; + + }; + + // Catmull-Rom + + function interpolate( p0, p1, p2, p3, t, t2, t3 ) { + + var v0 = ( p2 - p0 ) * 0.5, + v1 = ( p3 - p1 ) * 0.5; + + return ( 2 * ( p1 - p2 ) + v0 + v1 ) * t3 + ( - 3 * ( p1 - p2 ) - 2 * v0 - v1 ) * t2 + v0 * t + p1; + + }; + +}; +/** + * @author alteredq / http://alteredqualia.com/ + * @author mikael emtinger / http://gomo.se/ + */ + +THREE.Edge = function( v1, v2, vi1, vi2 ) { + + this.vertices = [ v1, v2 ]; // vertex references + this.vertexIndices = [ vi1, vi2 ]; // vertex indices + + this.faces = []; // face references + this.faceIndices = []; // face indices + +}; +/** + * @author mr.doob / http://mrdoob.com/ + * @author mikael emtinger / http://gomo.se/ + * @author greggman / http://games.greggman.com/ + */ + +THREE.Camera = function ( fov, aspect, near, far, target ) { + + THREE.Object3D.call( this ); + + this.fov = fov || 50; + this.aspect = aspect || 1; + this.near = near || 0.1; + this.far = far || 2000; + + this.target = target || new THREE.Object3D(); + this.useTarget = true; + + this.matrixWorldInverse = new THREE.Matrix4(); + this.projectionMatrix = null; + + this.updateProjectionMatrix(); + +}; + +THREE.Camera.prototype = new THREE.Object3D(); +THREE.Camera.prototype.constructor = THREE.Camera; +THREE.Camera.prototype.supr = THREE.Object3D.prototype; + + +THREE.Camera.prototype.translate = function ( distance, axis ) { + + this.matrix.rotateAxis( axis ); + + axis.multiplyScalar( distance ) + + this.position.addSelf( axis ); + this.target.position.addSelf( axis ); + +}; + + +THREE.Camera.prototype.updateProjectionMatrix = function () { + + if ( this.fullWidth ) { + + var aspect = this.fullWidth / this.fullHeight; + var top = Math.tan( this.fov * Math.PI / 360 ) * this.near; + var bottom = -top; + var left = aspect * bottom; + var right = aspect * top; + var width = Math.abs( right - left ); + var height = Math.abs( top - bottom ); + + this.projectionMatrix = THREE.Matrix4.makeFrustum( + left + this.x * width / this.fullWidth, + left + ( this.x + this.width ) * width / this.fullWidth, + top - ( this.y + this.height ) * height / this.fullHeight, + top - this.y * height / this.fullHeight, + this.near, + this.far ); + + } else { + + this.projectionMatrix = THREE.Matrix4.makePerspective( this.fov, this.aspect, this.near, this.far ); + + } + +}; + +/** + * Sets an offset in a larger frustum. This is useful for multi-window or + * multi-monitor/multi-machine setups. + * + * For example, if you have 3x2 monitors and each monitor is 1920x1080 and + * the monitors are in grid like this + * + * +---+---+---+ + * | A | B | C | + * +---+---+---+ + * | D | E | F | + * +---+---+---+ + * + * then for monitor each monitor you would call it like this + * + * var w = 1920; + * var h = 1080; + * var fullWidth = w * 3; + * var fullHeight = h * 2; + * + * --A-- + * camera.setOffset( fullWidth, fullHeight, w * 0, h * 0, w, h ); + * --B-- + * camera.setOffset( fullWidth, fullHeight, w * 1, h * 0, w, h ); + * --C-- + * camera.setOffset( fullWidth, fullHeight, w * 2, h * 0, w, h ); + * --D-- + * camera.setOffset( fullWidth, fullHeight, w * 0, h * 1, w, h ); + * --E-- + * camera.setOffset( fullWidth, fullHeight, w * 1, h * 1, w, h ); + * --F-- + * camera.setOffset( fullWidth, fullHeight, w * 2, h * 1, w, h ); + * + * Note there is no reason monitors have to be the same size or in a grid. + */ + +THREE.Camera.prototype.setViewOffset = function( fullWidth, fullHeight, x, y, width, height ) { + + this.fullWidth = fullWidth; + this.fullHeight = fullHeight; + this.x = x; + this.y = y; + this.width = width; + this.height = height; + + this.updateProjectionMatrix(); + +}; + +THREE.Camera.prototype.update = function ( parentMatrixWorld, forceUpdate, camera ) { + + if ( this.useTarget ) { + + // local + + this.matrix.lookAt( this.position, this.target.position, this.up ); + this.matrix.setPosition( this.position ); + + + // global + + if( parentMatrixWorld ) { + + this.matrixWorld.multiply( parentMatrixWorld, this.matrix ); + + } else { + + this.matrixWorld.copy( this.matrix ); + + } + + THREE.Matrix4.makeInvert( this.matrixWorld, this.matrixWorldInverse ); + + forceUpdate = true; + + } else { + + this.matrixAutoUpdate && this.updateMatrix(); + + if ( forceUpdate || this.matrixWorldNeedsUpdate ) { + + if ( parentMatrixWorld ) { + + this.matrixWorld.multiply( parentMatrixWorld, this.matrix ); + + } else { + + this.matrixWorld.copy( this.matrix ); + + } + + this.matrixWorldNeedsUpdate = false; + forceUpdate = true; + + THREE.Matrix4.makeInvert( this.matrixWorld, this.matrixWorldInverse ); + + } + + } + + // update children + + for ( var i = 0; i < this.children.length; i ++ ) { + + this.children[ i ].update( this.matrixWorld, forceUpdate, camera ); + + } + +}; +/** + * @author mr.doob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Light = function ( hex ) { + + THREE.Object3D.call( this ); + + this.color = new THREE.Color( hex ); + +}; + +THREE.Light.prototype = new THREE.Object3D(); +THREE.Light.prototype.constructor = THREE.Light; +THREE.Light.prototype.supr = THREE.Object3D.prototype; +/** + * @author mr.doob / http://mrdoob.com/ + */ + +THREE.AmbientLight = function ( hex ) { + + THREE.Light.call( this, hex ); + +}; + +THREE.AmbientLight.prototype = new THREE.Light(); +THREE.AmbientLight.prototype.constructor = THREE.AmbientLight; +/** + * @author mr.doob / http://mrdoob.com/ + */ + +THREE.DirectionalLight = function ( hex, intensity, distance, castShadow ) { + + THREE.Light.call( this, hex ); + + this.position = new THREE.Vector3( 0, 1, 0 ); + this.intensity = intensity || 1; + this.distance = distance || 0; + this.castShadow = castShadow !== undefined ? castShadow : false; + +}; + +THREE.DirectionalLight.prototype = new THREE.Light(); +THREE.DirectionalLight.prototype.constructor = THREE.DirectionalLight; +/** + * @author mr.doob / http://mrdoob.com/ + */ + +THREE.PointLight = function ( hex, intensity, distance ) { + + THREE.Light.call( this, hex ); + + this.position = new THREE.Vector3(); + this.intensity = intensity || 1; + this.distance = distance || 0; + +}; + +THREE.PointLight.prototype = new THREE.Light(); +THREE.PointLight.prototype.constructor = THREE.PointLight; +/** + * @author Mikael Emtinger + */ + +THREE.LensFlare = function ( texture, size, distance, blending ) { + + THREE.Object3D.call( this ); + + this.positionScreen = new THREE.Vector3(); + this.lensFlares = []; + this.customUpdateCallback = undefined; + + if( texture !== undefined ) { + + this.add( texture, size, distance, blending ); + + } + +}; + +THREE.LensFlare.prototype = new THREE.Object3D(); +THREE.LensFlare.prototype.constructor = THREE.LensFlare; +THREE.LensFlare.prototype.supr = THREE.Object3D.prototype; + + +/* + * Add: adds another flare + */ + +THREE.LensFlare.prototype.add = function( texture, size, distance, blending ) { + + if( size === undefined ) size = -1; + if( distance === undefined ) distance = 0; + if( blending === undefined ) blending = THREE.BillboardBlending; + + distance = Math.min( distance, Math.max( 0, distance )); + + this.lensFlares.push( { texture: texture, // THREE.Texture + size: size, // size in pixels (-1 = use texture.width) + distance: distance, // distance (0-1) from light source (0=at light source) + x: 0, y: 0, z: 0, // screen position (-1 => 1) z = 0 is ontop z = 1 is back + scale: 1, // scale + rotation: 1, // rotation + opacity: 1, // opacity + blending: blending } ); // blending + +}; + + +/* + * Update lens flares update positions on all flares based on the screen position + * Set myLensFlare.customUpdateCallback to alter the flares in your project specific way. + */ + +THREE.LensFlare.prototype.updateLensFlares = function() { + + var f, fl = this.lensFlares.length; + var flare; + var vecX = -this.positionScreen.x * 2; + var vecY = -this.positionScreen.y * 2; + + + for( f = 0; f < fl; f++ ) { + + flare = this.lensFlares[ f ]; + + flare.x = this.positionScreen.x + vecX * flare.distance; + flare.y = this.positionScreen.y + vecY * flare.distance; + + flare.wantedRotation = flare.x * Math.PI * 0.25; + flare.rotation += ( flare.wantedRotation - flare.rotation ) * 0.25; + + } + +}; + + + + + + + + + + + + +/** + * @author mr.doob / http://mrdoob.com/ + */ + +THREE.Material = function ( parameters ) { + + this.id = THREE.MaterialCounter.value ++; + + parameters = parameters || {}; + + this.opacity = parameters.opacity !== undefined ? parameters.opacity : 1; + this.transparent = parameters.transparent !== undefined ? parameters.transparent : false; + + this.blending = parameters.blending !== undefined ? parameters.blending : THREE.NormalBlending; + this.depthTest = parameters.depthTest !== undefined ? parameters.depthTest : true; + + this.polygonOffset = parameters.polygonOffset !== undefined ? parameters.polygonOffset : false; + this.polygonOffsetFactor = parameters.polygonOffsetFactor !== undefined ? parameters.polygonOffsetFactor : 0; + this.polygonOffsetUnits = parameters.polygonOffsetUnits !== undefined ? parameters.polygonOffsetUnits : 0; + +} + +THREE.NoShading = 0; +THREE.FlatShading = 1; +THREE.SmoothShading = 2; + +THREE.NoColors = 0; +THREE.FaceColors = 1; +THREE.VertexColors = 2; + +THREE.NormalBlending = 0; +THREE.AdditiveBlending = 1; +THREE.SubtractiveBlending = 2; +THREE.MultiplyBlending = 3; +THREE.AdditiveAlphaBlending = 4; + + +THREE.MaterialCounter = { value: 0 }; +/** + * @author mr.doob / http://mrdoob.com/ + */ + +THREE.CubeReflectionMapping = function () {}; +THREE.CubeRefractionMapping = function () {}; + +THREE.LatitudeReflectionMapping = function () {}; +THREE.LatitudeRefractionMapping = function () {}; + +THREE.SphericalReflectionMapping = function () {}; +THREE.SphericalRefractionMapping = function () {}; + +THREE.UVMapping = function () {}; +/** + * @author mr.doob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * opacity: , + + * blending: THREE.NormalBlending, + * depthTest: , + + * linewidth: , + * linecap: "round", + * linejoin: "round", + + * vertexColors: + * } + */ + +THREE.LineBasicMaterial = function ( parameters ) { + + THREE.Material.call( this, parameters ); + + parameters = parameters || {}; + + this.color = parameters.color !== undefined ? new THREE.Color( parameters.color ) : new THREE.Color( 0xffffff ); + + this.linewidth = parameters.linewidth !== undefined ? parameters.linewidth : 1; + this.linecap = parameters.linecap !== undefined ? parameters.linecap : 'round'; + this.linejoin = parameters.linejoin !== undefined ? parameters.linejoin : 'round'; + + this.vertexColors = parameters.vertexColors ? parameters.vertexColors : false; + +}; + +THREE.LineBasicMaterial.prototype = new THREE.Material(); +THREE.LineBasicMaterial.prototype.constructor = THREE.LineBasicMaterial; +/** + * @author mr.doob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * opacity: , + * map: new THREE.Texture( ), + * + * lightMap: new THREE.Texture( ), + * + * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ), + * combine: THREE.Multiply, + * reflectivity: , + * refractionRatio: , + * + * shading: THREE.SmoothShading, + * blending: THREE.NormalBlending, + * depthTest: , + * + * wireframe: , + * wireframeLinewidth: , + * + * vertexColors: false / THREE.VertexColors / THREE.FaceColors, + * skinning: + * } + */ + +THREE.MeshBasicMaterial = function ( parameters ) { + + THREE.Material.call( this, parameters ); + + parameters = parameters || {}; + + this.color = parameters.color !== undefined ? new THREE.Color( parameters.color ) : new THREE.Color( 0xffffff ); + + this.map = parameters.map !== undefined ? parameters.map : null; + + this.lightMap = parameters.lightMap !== undefined ? parameters.lightMap : null; + + this.envMap = parameters.envMap !== undefined ? parameters.envMap : null; + this.combine = parameters.combine !== undefined ? parameters.combine : THREE.MultiplyOperation; + this.reflectivity = parameters.reflectivity !== undefined ? parameters.reflectivity : 1; + this.refractionRatio = parameters.refractionRatio !== undefined ? parameters.refractionRatio : 0.98; + + // this.enableFog = parameters.enableFog ? parameters.enableFog : true; + + this.shading = parameters.shading !== undefined ? parameters.shading : THREE.SmoothShading; + + this.wireframe = parameters.wireframe !== undefined ? parameters.wireframe : false; + this.wireframeLinewidth = parameters.wireframeLinewidth !== undefined ? parameters.wireframeLinewidth : 1; + this.wireframeLinecap = parameters.wireframeLinecap !== undefined ? parameters.wireframeLinecap : 'round'; + this.wireframeLinejoin = parameters.wireframeLinejoin !== undefined ? parameters.wireframeLinejoin : 'round'; + + this.vertexColors = parameters.vertexColors !== undefined ? parameters.vertexColors : false; + + this.skinning = parameters.skinning !== undefined ? parameters.skinning : false; + this.morphTargets = parameters.morphTargets !== undefined ? parameters.morphTargets : false; + +}; + +THREE.MeshBasicMaterial.prototype = new THREE.Material(); +THREE.MeshBasicMaterial.prototype.constructor = THREE.MeshBasicMaterial; +/** + * @author mr.doob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * opacity: , + * map: new THREE.Texture( ), + * + * lightMap: new THREE.Texture( ), + * + * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ), + * combine: THREE.Multiply, + * reflectivity: , + * refractionRatio: , + * + * shading: THREE.SmoothShading, + * blending: THREE.NormalBlending, + * depthTest: , + * + * wireframe: , + * wireframeLinewidth: , + * + * vertexColors: false / THREE.VertexColors / THREE.FaceColors, + * skinning: + * } + */ + +THREE.MeshLambertMaterial = function ( parameters ) { + + THREE.Material.call( this, parameters ); + + parameters = parameters || {}; + + this.color = parameters.color !== undefined ? new THREE.Color( parameters.color ) : new THREE.Color( 0xffffff ); + + this.map = parameters.map !== undefined ? parameters.map : null; + + this.lightMap = parameters.lightMap !== undefined ? parameters.lightMap : null; + + this.envMap = parameters.envMap !== undefined ? parameters.envMap : null; + this.combine = parameters.combine !== undefined ? parameters.combine : THREE.MultiplyOperation; + this.reflectivity = parameters.reflectivity !== undefined ? parameters.reflectivity : 1; + this.refractionRatio = parameters.refractionRatio !== undefined ? parameters.refractionRatio : 0.98; + + // this.enableFog = parameters.enableFog ? parameters.enableFog : true; + + this.shading = parameters.shading !== undefined ? parameters.shading : THREE.SmoothShading; + + this.wireframe = parameters.wireframe !== undefined ? parameters.wireframe : false; + this.wireframeLinewidth = parameters.wireframeLinewidth !== undefined ? parameters.wireframeLinewidth : 1; + this.wireframeLinecap = parameters.wireframeLinecap !== undefined ? parameters.wireframeLinecap : 'round'; + this.wireframeLinejoin = parameters.wireframeLinejoin !== undefined ? parameters.wireframeLinejoin : 'round'; + + this.vertexColors = parameters.vertexColors !== undefined ? parameters.vertexColors : false; + + this.skinning = parameters.skinning !== undefined ? parameters.skinning : false; + this.morphTargets = parameters.morphTargets !== undefined ? parameters.morphTargets : false; + +}; + +THREE.MeshLambertMaterial.prototype = new THREE.Material(); +THREE.MeshLambertMaterial.prototype.constructor = THREE.MeshLambertMaterial; +/** + * @author mr.doob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * ambient: , + * specular: , + * shininess: , + * opacity: , + * + * map: new THREE.Texture( ), + * + * lightMap: new THREE.Texture( ), + * + * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ), + * combine: THREE.Multiply, + * reflectivity: , + * refractionRatio: , + * + * shading: THREE.SmoothShading, + * blending: THREE.NormalBlending, + * depthTest: , + * + * wireframe: , + * wireframeLinewidth: , + * + * vertexColors: false / THREE.VertexColors / THREE.FaceColors, + * skinning: + * } + */ + +THREE.MeshPhongMaterial = function ( parameters ) { + + THREE.Material.call( this, parameters ); + + parameters = parameters || {}; + + this.color = parameters.color !== undefined ? new THREE.Color( parameters.color ) : new THREE.Color( 0xffffff ); + this.ambient = parameters.ambient !== undefined ? new THREE.Color( parameters.ambient ) : new THREE.Color( 0x050505 ); + this.specular = parameters.specular !== undefined ? new THREE.Color( parameters.specular ) : new THREE.Color( 0x111111 ); + this.shininess = parameters.shininess !== undefined ? parameters.shininess : 30; + + this.map = parameters.map !== undefined ? parameters.map : null; + + this.lightMap = parameters.lightMap !== undefined ? parameters.lightMap : null; + + this.envMap = parameters.envMap !== undefined ? parameters.envMap : null; + this.combine = parameters.combine !== undefined ? parameters.combine : THREE.MultiplyOperation; + this.reflectivity = parameters.reflectivity !== undefined ? parameters.reflectivity : 1; + this.refractionRatio = parameters.refractionRatio !== undefined ? parameters.refractionRatio : 0.98; + + // this.enableFog = parameters.enableFog ? parameters.enableFog : true; + + this.shading = parameters.shading !== undefined ? parameters.shading : THREE.SmoothShading; + + this.wireframe = parameters.wireframe !== undefined ? parameters.wireframe : false; + this.wireframeLinewidth = parameters.wireframeLinewidth !== undefined ? parameters.wireframeLinewidth : 1; + this.wireframeLinecap = parameters.wireframeLinecap !== undefined ? parameters.wireframeLinecap : 'round'; + this.wireframeLinejoin = parameters.wireframeLinejoin !== undefined ? parameters.wireframeLinejoin : 'round'; + + this.vertexColors = parameters.vertexColors !== undefined ? parameters.vertexColors : false; + + this.skinning = parameters.skinning !== undefined ? parameters.skinning : false; + this.morphTargets = parameters.morphTargets !== undefined ? parameters.morphTargets : false; + +}; + +THREE.MeshPhongMaterial.prototype = new THREE.Material(); +THREE.MeshPhongMaterial.prototype.constructor = THREE.MeshPhongMaterial; +/** + * @author mr.doob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * opacity: , + + * blending: THREE.NormalBlending, + * depthTest: , + + * wireframe: , + * wireframeLinewidth: + * } + */ + +THREE.MeshDepthMaterial = function ( parameters ) { + + THREE.Material.call( this, parameters ); + + parameters = parameters || {}; + + this.shading = parameters.shading !== undefined ? parameters.shading : THREE.SmoothShading; // doesn't really apply here, normals are not used + + this.wireframe = parameters.wireframe !== undefined ? parameters.wireframe : false; + this.wireframeLinewidth = parameters.wireframeLinewidth !== undefined ? parameters.wireframeLinewidth : 1; + +}; + +THREE.MeshDepthMaterial.prototype = new THREE.Material(); +THREE.MeshDepthMaterial.prototype.constructor = THREE.MeshDepthMaterial; +/** + * @author mr.doob / http://mrdoob.com/ + * + * parameters = { + * opacity: , + + * shading: THREE.FlatShading, + * blending: THREE.NormalBlending, + * depthTest: , + + * wireframe: , + * wireframeLinewidth: + * } + */ + +THREE.MeshNormalMaterial = function ( parameters ) { + + THREE.Material.call( this, parameters ); + + parameters = parameters || {}; + + this.shading = parameters.shading ? parameters.shading : THREE.FlatShading; + + this.wireframe = parameters.wireframe ? parameters.wireframe : false; + this.wireframeLinewidth = parameters.wireframeLinewidth ? parameters.wireframeLinewidth : 1; + +}; + +THREE.MeshNormalMaterial.prototype = new THREE.Material(); +THREE.MeshNormalMaterial.prototype.constructor = THREE.MeshNormalMaterial; +/** + * @author mr.doob / http://mrdoob.com/ + */ + +THREE.MeshFaceMaterial = function () { + +}; +/** + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * fragmentShader: , + * vertexShader: , + + * uniforms: { "parameter1": { type: "f", value: 1.0 }, "parameter2": { type: "i" value2: 2 } }, + + * shading: THREE.SmoothShading, + * blending: THREE.NormalBlending, + * depthTest: , + + * wireframe: , + * wireframeLinewidth: , + + * lights: , + * vertexColors: , + * skinning: , + * morphTargets: , + * } + */ + +THREE.MeshShaderMaterial = function ( parameters ) { + + THREE.Material.call( this, parameters ); + + parameters = parameters || {}; + + this.fragmentShader = parameters.fragmentShader !== undefined ? parameters.fragmentShader : "void main() {}"; + this.vertexShader = parameters.vertexShader !== undefined ? parameters.vertexShader : "void main() {}"; + this.uniforms = parameters.uniforms !== undefined ? parameters.uniforms : {}; + this.attributes = parameters.attributes; + + this.shading = parameters.shading !== undefined ? parameters.shading : THREE.SmoothShading; + + this.wireframe = parameters.wireframe !== undefined ? parameters.wireframe : false; + this.wireframeLinewidth = parameters.wireframeLinewidth !== undefined ? parameters.wireframeLinewidth : 1; + + this.fog = parameters.fog !== undefined ? parameters.fog : false; // set to use scene fog + this.lights = parameters.lights !== undefined ? parameters.lights : false; // set to use scene lights + this.vertexColors = parameters.vertexColors !== undefined ? parameters.vertexColors : false; // set to use "color" attribute stream + this.skinning = parameters.skinning !== undefined ? parameters.skinning : false; // set to use skinning attribute streams + this.morphTargets = parameters.morphTargets !== undefined ? parameters.morphTargets : false; // set to use morph targets + +}; + +THREE.MeshShaderMaterial.prototype = new THREE.Material(); +THREE.MeshShaderMaterial.prototype.constructor = THREE.MeshShaderMaterial; +/** + * @author mr.doob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * opacity: , + * map: new THREE.Texture( ), + + * lightMap: new THREE.Texture( ), + + * envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ), + * combine: THREE.Multiply, + * reflectivity: , + * refractionRatio: , + + * shading: THREE.SmoothShading, + * blending: THREE.NormalBlending, + * depthTest: , + + * wireframe: , + * wireframeLinewidth: , + + * vertexColors: , + * skinning: + * } + */ + +THREE.ShadowVolumeDynamicMaterial = function ( parameters ) { + + THREE.Material.call( this, parameters ); + + parameters = parameters || {}; + + this.color = parameters.color !== undefined ? new THREE.Color( parameters.color ) : new THREE.Color( 0xffffff ); + + this.map = parameters.map !== undefined ? parameters.map : null; + + this.lightMap = parameters.lightMap !== undefined ? parameters.lightMap : null; + + this.envMap = parameters.envMap !== undefined ? parameters.envMap : null; + this.combine = parameters.combine !== undefined ? parameters.combine : THREE.MultiplyOperation; + this.reflectivity = parameters.reflectivity !== undefined ? parameters.reflectivity : 1; + this.refractionRatio = parameters.refractionRatio !== undefined ? parameters.refractionRatio : 0.98; + + // this.enableFog = parameters.enableFog ? parameters.enableFog : true; + + this.shading = parameters.shading !== undefined ? parameters.shading : THREE.SmoothShading; + + this.wireframe = parameters.wireframe !== undefined ? parameters.wireframe : false; + this.wireframeLinewidth = parameters.wireframeLinewidth !== undefined ? parameters.wireframeLinewidth : 1; + this.wireframeLinecap = parameters.wireframeLinecap !== undefined ? parameters.wireframeLinecap : 'round'; + this.wireframeLinejoin = parameters.wireframeLinejoin !== undefined ? parameters.wireframeLinejoin : 'round'; + + this.vertexColors = parameters.vertexColors !== undefined ? parameters.vertexColors : false; + + this.skinning = parameters.skinning !== undefined ? parameters.skinning : false; + this.morphTargets = parameters.morphTargets !== undefined ? parameters.morphTargets : false; + +}; + +THREE.ShadowVolumeDynamicMaterial.prototype = new THREE.Material(); +THREE.ShadowVolumeDynamicMaterial.prototype.constructor = THREE.ShadowVolumeDynamicMaterial; +/** + * @author mr.doob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * color: , + * opacity: , + * map: new THREE.Texture( ), + + * size: , + + * blending: THREE.NormalBlending, + * depthTest: , + + * vertexColors: + * } + */ + +THREE.ParticleBasicMaterial = function ( parameters ) { + + THREE.Material.call( this, parameters ); + + parameters = parameters || {}; + + this.color = parameters.color !== undefined ? new THREE.Color( parameters.color ) : new THREE.Color( 0xffffff ); + + this.map = parameters.map !== undefined ? parameters.map : null; + + this.size = parameters.size !== undefined ? parameters.size : 1; + this.sizeAttenuation = parameters.sizeAttenuation !== undefined ? parameters.sizeAttenuation : true; + + this.vertexColors = parameters.vertexColors !== undefined ? parameters.vertexColors : false; + +}; + +THREE.ParticleBasicMaterial.prototype = new THREE.Material(); +THREE.ParticleBasicMaterial.prototype.constructor = THREE.ParticleBasicMaterial; +/** + * @author mr.doob / http://mrdoob.com/ + * + * parameters = { + * color: , + * program: , + * opacity: , + * blending: THREE.NormalBlending + * } + */ + +THREE.ParticleCanvasMaterial = function ( parameters ) { + + THREE.Material.call( this, parameters ); + + parameters = parameters || {}; + + this.color = parameters.color !== undefined ? new THREE.Color( parameters.color ) : new THREE.Color( 0xffffff ); + this.program = parameters.program !== undefined ? parameters.program : function ( context, color ) {}; + +}; + +THREE.ParticleCanvasMaterial.prototype = new THREE.Material(); +THREE.ParticleCanvasMaterial.prototype.constructor = THREE.ParticleCanvasMaterial; +/** + * @author mr.doob / http://mrdoob.com/ + */ + +THREE.ParticleDOMMaterial = function ( domElement ) { + + THREE.Material.call( this ); + + this.domElement = domElement; + +}; +/** + * @author mr.doob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author szimek / https://github.com/szimek/ + */ + +THREE.Texture = function ( image, mapping, wrapS, wrapT, magFilter, minFilter ) { + + this.image = image; + + this.mapping = mapping !== undefined ? mapping : new THREE.UVMapping(); + + this.wrapS = wrapS !== undefined ? wrapS : THREE.ClampToEdgeWrapping; + this.wrapT = wrapT !== undefined ? wrapT : THREE.ClampToEdgeWrapping; + + this.magFilter = magFilter !== undefined ? magFilter : THREE.LinearFilter; + this.minFilter = minFilter !== undefined ? minFilter : THREE.LinearMipMapLinearFilter; + + this.offset = new THREE.Vector2( 0, 0 ); + this.repeat = new THREE.Vector2( 1, 1 ); + + this.needsUpdate = false; + +}; + +THREE.Texture.prototype = { + + clone: function () { + + return new THREE.Texture( this.image, this.mapping, this.wrapS, this.wrapT, this.magFilter, this.minFilter ); + + } + +}; + +THREE.MultiplyOperation = 0; +THREE.MixOperation = 1; + +// Wrapping modes + +THREE.RepeatWrapping = 0; +THREE.ClampToEdgeWrapping = 1; +THREE.MirroredRepeatWrapping = 2; + +// Filters + +THREE.NearestFilter = 3; +THREE.NearestMipMapNearestFilter = 4; +THREE.NearestMipMapLinearFilter = 5; +THREE.LinearFilter = 6; +THREE.LinearMipMapNearestFilter = 7; +THREE.LinearMipMapLinearFilter = 8; + +// Types + +THREE.ByteType = 9; +THREE.UnsignedByteType = 10; +THREE.ShortType = 11; +THREE.UnsignedShortType = 12; +THREE.IntType = 13; +THREE.UnsignedIntType = 14; +THREE.FloatType = 15; + +// Formats + +THREE.AlphaFormat = 16; +THREE.RGBFormat = 17; +THREE.RGBAFormat = 18; +THREE.LuminanceFormat = 19; +THREE.LuminanceAlphaFormat = 20; +/** + * @author mr.doob / http://mrdoob.com/ + */ + +THREE.Particle = function ( materials ) { + + THREE.Object3D.call( this ); + + this.materials = materials instanceof Array ? materials : [ materials ]; + +}; + +THREE.Particle.prototype = new THREE.Object3D(); +THREE.Particle.prototype.constructor = THREE.Particle; +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.ParticleSystem = function ( geometry, materials ) { + + THREE.Object3D.call( this ); + + this.geometry = geometry; + this.materials = materials instanceof Array ? materials : [ materials ]; + + this.sortParticles = false; + +}; + +THREE.ParticleSystem.prototype = new THREE.Object3D(); +THREE.ParticleSystem.prototype.constructor = THREE.ParticleSystem; +/** + * @author mr.doob / http://mrdoob.com/ + */ + +THREE.Line = function ( geometry, materials, type ) { + + THREE.Object3D.call( this ); + + this.geometry = geometry; + this.materials = materials instanceof Array ? materials : [ materials ]; + + this.type = ( type != undefined ) ? type : THREE.LineStrip; + +}; + +THREE.LineStrip = 0; +THREE.LinePieces = 1; + +THREE.Line.prototype = new THREE.Object3D(); +THREE.Line.prototype.constructor = THREE.Line; +/** + * @author mr.doob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author mikael emtinger / http://gomo.se/ + */ + +THREE.Mesh = function ( geometry, materials ) { + + THREE.Object3D.call( this ); + + this.geometry = geometry; + this.materials = materials && materials.length ? materials : [ materials ]; + + this.overdraw = false; // TODO: Move to material? + + + if ( this.geometry ) { + + // calc bound radius + + if( !this.geometry.boundingSphere ) { + + this.geometry.computeBoundingSphere(); + + } + + this.boundRadius = geometry.boundingSphere.radius; + + + // setup morph targets + + if( this.geometry.morphTargets.length ) { + + this.morphTargetBase = -1; + this.morphTargetForcedOrder = []; + this.morphTargetInfluences = []; + this.morphTargetDictionary = {}; + + for( var m = 0; m < this.geometry.morphTargets.length; m++ ) { + + this.morphTargetInfluences.push( 0 ); + this.morphTargetDictionary[ this.geometry.morphTargets[ m ].name ] = m; + + } + + } + + } + +} + +THREE.Mesh.prototype = new THREE.Object3D(); +THREE.Mesh.prototype.constructor = THREE.Mesh; +THREE.Mesh.prototype.supr = THREE.Object3D.prototype; + + +/* + * Get Morph Target Index by Name + */ + +THREE.Mesh.prototype.getMorphTargetIndexByName = function( name ) { + + if ( this.morphTargetDictionary[ name ] !== undefined ) { + + return this.morphTargetDictionary[ name ]; + } + + console.log( "THREE.Mesh.getMorphTargetIndexByName: morph target " + name + " does not exist. Returning 0." ); + return 0; +} +/** + * @author mikael emtinger / http://gomo.se/ + */ + +THREE.Bone = function( belongsToSkin ) { + + THREE.Object3D.call( this ); + + this.skin = belongsToSkin; + this.skinMatrix = new THREE.Matrix4(); + this.hasNoneBoneChildren = false; + +}; + +THREE.Bone.prototype = new THREE.Object3D(); +THREE.Bone.prototype.constructor = THREE.Bone; +THREE.Bone.prototype.supr = THREE.Object3D.prototype; + + +/* + * Update + */ + +THREE.Bone.prototype.update = function( parentSkinMatrix, forceUpdate, camera ) { + + // update local + + if ( this.matrixAutoUpdate ) { + + forceUpdate |= this.updateMatrix(); + + } + + // update skin matrix + + if ( forceUpdate || this.matrixWorldNeedsUpdate ) { + + if( parentSkinMatrix ) { + + this.skinMatrix.multiply( parentSkinMatrix, this.matrix ); + + } else { + + this.skinMatrix.copy( this.matrix ); + + } + + this.matrixWorldNeedsUpdate = false; + forceUpdate = true; + + } + + // update children + + var child, i, l = this.children.length; + + if ( this.hasNoneBoneChildren ) { + + this.matrixWorld.multiply( this.skin.matrixWorld, this.skinMatrix ); + + + for ( i = 0; i < l; i++ ) { + + child = this.children[ i ]; + + if ( ! ( child instanceof THREE.Bone ) ) { + + child.update( this.matrixWorld, true, camera ); + + } else { + + child.update( this.skinMatrix, forceUpdate, camera ); + + } + + } + + } else { + + for ( i = 0; i < l; i++ ) { + + this.children[ i ].update( this.skinMatrix, forceUpdate, camera ); + + } + + } + +}; + + +/* + * Add child + */ + +THREE.Bone.prototype.addChild = function( child ) { + + if ( this.children.indexOf( child ) === - 1 ) { + + if ( child.parent !== undefined ) { + + child.parent.removeChild( child ); + + } + + child.parent = this; + this.children.push( child ); + + if ( ! ( child instanceof THREE.Bone ) ) { + + this.hasNoneBoneChildren = true; + + } + + } + +}; + +/* + * TODO: Remove Children: see if any remaining are none-Bone + */ +/** + * @author mikael emtinger / http://gomo.se/ + */ + +THREE.SkinnedMesh = function( geometry, materials ) { + + THREE.Mesh.call( this, geometry, materials ); + + // init bones + + this.identityMatrix = new THREE.Matrix4(); + this.bones = []; + this.boneMatrices = []; + + var b, bone, gbone, p, q, s; + + if ( this.geometry.bones !== undefined ) { + + for ( b = 0; b < this.geometry.bones.length; b++ ) { + + gbone = this.geometry.bones[ b ]; + + p = gbone.pos; + q = gbone.rotq; + s = gbone.scl; + + bone = this.addBone(); + + bone.name = gbone.name; + bone.position.set( p[0], p[1], p[2] ); + bone.quaternion.set( q[0], q[1], q[2], q[3] ); + bone.useQuaternion = true; + + if ( s !== undefined ) { + + bone.scale.set( s[0], s[1], s[2] ); + + } else { + + bone.scale.set( 1, 1, 1 ); + + } + + } + + for ( b = 0; b < this.bones.length; b++ ) { + + gbone = this.geometry.bones[ b ]; + bone = this.bones[ b ]; + + if ( gbone.parent === -1 ) { + + this.addChild( bone ); + + } else { + + this.bones[ gbone.parent ].addChild( bone ); + + } + + } + + this.boneMatrices = new Float32Array( 16 * this.bones.length ); + + this.pose(); + + } + +}; + +THREE.SkinnedMesh.prototype = new THREE.Mesh(); +THREE.SkinnedMesh.prototype.constructor = THREE.SkinnedMesh; + + +/* + * Update + */ + +THREE.SkinnedMesh.prototype.update = function ( parentMatrixWorld, forceUpdate, camera ) { + + // visible? + + if ( this.visible ) { + + // update local + + if ( this.matrixAutoUpdate ) { + + forceUpdate |= this.updateMatrix(); + + } + + + // update global + + if ( forceUpdate || this.matrixWorldNeedsUpdate ) { + + if ( parentMatrixWorld ) { + + this.matrixWorld.multiply( parentMatrixWorld, this.matrix ); + + } else { + + this.matrixWorld.copy( this.matrix ); + + } + + this.matrixWorldNeedsUpdate = false; + forceUpdate = true; + + } + + + // update children + + var child, i, l = this.children.length; + + for ( i = 0; i < l; i++ ) { + + child = this.children[ i ]; + + if ( child instanceof THREE.Bone ) { + + child.update( this.identityMatrix, false, camera ); + + } else { + + child.update( this.matrixWorld, forceUpdate, camera ); + + } + + } + + + // flatten to array + + var b, bl = this.bones.length; + ba = this.bones; + bm = this.boneMatrices; + + for ( b = 0; b < bl; b++ ) { + + ba[ b ].skinMatrix.flattenToArrayOffset( bm, b * 16 ); + + } + + } + +}; + + +/* + * Add + */ + +THREE.SkinnedMesh.prototype.addBone = function( bone ) { + + if ( bone === undefined ) { + + bone = new THREE.Bone( this ); + + } + + this.bones.push( bone ); + + return bone; + +}; + +/* + * Pose + */ + +THREE.SkinnedMesh.prototype.pose = function() { + + this.update( undefined, true ); + + var bim, bone, boneInverses = []; + + for ( var b = 0; b < this.bones.length; b++ ) { + + bone = this.bones[ b ]; + + boneInverses.push( THREE.Matrix4.makeInvert( bone.skinMatrix ) ); + + bone.skinMatrix.flattenToArrayOffset( this.boneMatrices, b * 16 ); + + } + + // project vertices to local + + if ( this.geometry.skinVerticesA === undefined ) { + + this.geometry.skinVerticesA = []; + this.geometry.skinVerticesB = []; + + var orgVertex, vertex; + + for ( var i = 0; i < this.geometry.skinIndices.length; i++ ) { + + orgVertex = this.geometry.vertices[ i ].position; + + var indexA = this.geometry.skinIndices[ i ].x; + var indexB = this.geometry.skinIndices[ i ].y; + + vertex = new THREE.Vector3( orgVertex.x, orgVertex.y, orgVertex.z ); + this.geometry.skinVerticesA.push( boneInverses[ indexA ].multiplyVector3( vertex ) ); + + vertex = new THREE.Vector3( orgVertex.x, orgVertex.y, orgVertex.z ); + this.geometry.skinVerticesB.push( boneInverses[ indexB ].multiplyVector3( vertex ) ); + + // todo: add more influences + + // normalize weights + + if ( this.geometry.skinWeights[ i ].x + this.geometry.skinWeights[ i ].y !== 1 ) { + + var len = ( 1.0 - ( this.geometry.skinWeights[ i ].x + this.geometry.skinWeights[ i ].y )) * 0.5; + this.geometry.skinWeights[ i ].x += len; + this.geometry.skinWeights[ i ].y += len; + + } + + } + + } + +}; +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Ribbon = function ( geometry, materials ) { + + THREE.Object3D.call( this ); + + this.geometry = geometry; + this.materials = materials instanceof Array ? materials : [ materials ]; + +}; + +THREE.Ribbon.prototype = new THREE.Object3D(); +THREE.Ribbon.prototype.constructor = THREE.Ribbon; +/** + * @author mikael emtinger / http://gomo.se/ + */ + +THREE.Sound = function ( sources, radius, volume, loop ) { + + THREE.Object3D.call( this ); + + this.isLoaded = false; + this.isAddedToDOM = false; + this.isPlaying = false; + this.duration = -1; + this.radius = radius !== undefined ? Math.abs( radius ) : 100; + this.volume = Math.min( 1, Math.max( 0, volume !== undefined ? volume : 1 ) ); + + this.domElement = document.createElement( 'audio' ); + this.domElement.volume = 0; + this.domElement.pan = 0; + this.domElement.loop = loop !== undefined ? loop : true; + + // init sources + + this.sources = sources instanceof Array ? sources : [ sources ]; + + var element, source, type, s, sl = this.sources.length; + + for ( s = 0; s < sl; s++ ) { + + source = this.sources[ s ]; + source.toLowerCase(); + + if ( source.indexOf( ".mp3" ) !== -1 ) { + + type = "audio/mpeg"; + + } else if( source.indexOf( ".ogg" ) !== -1 ) { + + type = "audio/ogg"; + + } else if( source.indexOf( ".wav" ) !== -1 ) { + + type = "audio/wav"; + + } + + if ( this.domElement.canPlayType( type ) ) { + + element = document.createElement( "source" ); + element.src = this.sources[ s ]; + + this.domElement.THREESound = this; + this.domElement.appendChild( element ); + this.domElement.addEventListener( "canplay", this.onLoad, true ); + this.domElement.load(); + + break; + + } + + } + +}; + + +THREE.Sound.prototype = new THREE.Object3D(); +THREE.Sound.prototype.constructor = THREE.Sound; +THREE.Sound.prototype.supr = THREE.Object3D.prototype; + + +THREE.Sound.prototype.onLoad = function () { + + var sound = this.THREESound; + + if ( sound.isLoaded ) { + + return; + + } + + this.removeEventListener( "canplay", this.onLoad, true ); + + sound.isLoaded = true; + sound.duration = this.duration; + + if ( sound.isPlaying ) { + + sound.play(); + + } + +}; + +THREE.Sound.prototype.addToDOM = function ( parent ) { + + this.isAddedToDOM = true; + parent.appendChild( this.domElement ); + +}; + +THREE.Sound.prototype.play = function ( startTime ) { + + this.isPlaying = true; + + if ( this.isLoaded ) { + + this.domElement.play(); + + if ( startTime ) { + + this.domElement.currentTime = startTime % this.duration; + + } + + } + +}; + +THREE.Sound.prototype.pause = function () { + + this.isPlaying = false; + this.domElement.pause(); + +}; + +THREE.Sound.prototype.stop = function (){ + + this.isPlaying = false; + this.domElement.pause(); + this.domElement.currentTime = 0; + +}; + +THREE.Sound.prototype.calculateVolumeAndPan = function ( cameraRelativePosition ) { + + var distance = cameraRelativePosition.length(); + + if( distance <= this.radius ) { + + this.domElement.volume = this.volume * ( 1 - distance / this.radius ); + + } else { + + this.domElement.volume = 0; + + } + +}; + +THREE.Sound.prototype.update = function ( parentMatrixWorld, forceUpdate, camera ) { + + // update local (rotation/scale is not used) + + if ( this.matrixAutoUpdate ) { + + this.matrix.setPosition( this.position ); + forceUpdate = true; + + } + + // update global + + if ( forceUpdate || this.matrixWorldNeedsUpdate ) { + + if ( parentMatrixWorld ) { + + this.matrixWorld.multiply( parentMatrixWorld, this.matrix ); + + } else { + + this.matrixWorld.copy( this.matrix ); + + } + + this.matrixWorldNeedsUpdate = false; + forceUpdate = true; + + } + + // update children + + var i, l = this.children.length; + + for ( i = 0; i < l; i++ ) { + + this.children[ i ].update( this.matrixWorld, forceUpdate, camera ); + + } + +}; +/** + * @author mikael emtinger / http://gomo.se/ + */ + +THREE.LOD = function() { + + THREE.Object3D.call( this ); + + this.LODs = []; + +}; + +THREE.LOD.prototype = new THREE.Object3D(); +THREE.LOD.prototype.constructor = THREE.LOD; +THREE.LOD.prototype.supr = THREE.Object3D.prototype; + +/* + * Add + */ + +THREE.LOD.prototype.add = function ( object3D, visibleAtDistance ) { + + if ( visibleAtDistance === undefined ) { + + visibleAtDistance = 0; + + } + + visibleAtDistance = Math.abs( visibleAtDistance ); + + for ( var l = 0; l < this.LODs.length; l++ ) { + + if ( visibleAtDistance < this.LODs[ l ].visibleAtDistance ) { + + break; + + } + + } + + this.LODs.splice( l, 0, { visibleAtDistance: visibleAtDistance, object3D: object3D } ); + this.addChild( object3D ); + +}; + + +/* + * Update + */ + +THREE.LOD.prototype.update = function ( parentMatrixWorld, forceUpdate, camera ) { + + // update local + + if ( this.matrixAutoUpdate ) { + + forceUpdate |= this.updateMatrix(); + + } + + // update global + + if ( forceUpdate || this.matrixWorldNeedsUpdate ) { + + if ( parentMatrixWorld ) { + + this.matrixWorld.multiply( parentMatrixWorld, this.matrix ); + + } else { + + this.matrixWorld.copy( this.matrix ); + + } + + this.matrixWorldNeedsUpdate = false; + forceUpdate = true; + + } + + + // update LODs + + if ( this.LODs.length > 1 ) { + + + var inverse = camera.matrixWorldInverse; + var radius = this.boundRadius * this.boundRadiusScale; + var distance = -( inverse.n31 * this.position.x + inverse.n32 * this.position.y + inverse.n33 * this.position.z + inverse.n34 ); + + this.LODs[ 0 ].object3D.visible = true; + + for ( var l = 1; l < this.LODs.length; l++ ) { + + if( distance >= this.LODs[ l ].visibleAtDistance ) { + + this.LODs[ l - 1 ].object3D.visible = false; + this.LODs[ l ].object3D.visible = true; + + } else { + + break; + + } + + } + + for( ; l < this.LODs.length; l++ ) { + + this.LODs[ l ].object3D.visible = false; + + } + + } + + // update children + + for ( var c = 0; c < this.children.length; c++ ) { + + this.children[ c ].update( this.matrixWorld, forceUpdate, camera ); + + } + + +}; +/** + * @author mikael emtinger / http://gomo.se/ + */ + +THREE.ShadowVolume = function( meshOrGeometry, isStatic ) { + + if( meshOrGeometry instanceof THREE.Mesh ) { + + THREE.Mesh.call( this, meshOrGeometry.geometry, isStatic ? [ new THREE.ShadowVolumeDynamicMaterial() ] : [ new THREE.ShadowVolumeDynamicMaterial() ] ); + meshOrGeometry.addChild( this ); + + } else { + + THREE.Mesh.call( this, meshOrGeometry, isStatic ? [ new THREE.ShadowVolumeDynamicMaterial() ] : [ new THREE.ShadowVolumeDynamicMaterial() ] ); + + } + + this.calculateShadowVolumeGeometry(); + +}; + +THREE.ShadowVolume.prototype = new THREE.Mesh(); +THREE.ShadowVolume.prototype.constructor = THREE.ShadowVolume; +THREE.ShadowVolume.prototype.supr = THREE.Mesh.prototype; + + +/* + * Calculate Shadow Faces + */ + +THREE.ShadowVolume.prototype.calculateShadowVolumeGeometry = function() { + + if ( this.geometry.edges && this.geometry.edges.length ) { + + var f, fl, face, faceA, faceB, faceAIndex, faceBIndex, vertexA, vertexB; + var faceACombination, faceBCombination; + var faceAvertexAIndex, faceAvertexBIndex, faceBvertexAIndex, faceBvertexBIndex; + var e, el, edge, temp; + + var newGeometry = new THREE.Geometry(); + var vertices = newGeometry.vertices = this.geometry.vertices; + var faces = newGeometry.faces = this.geometry.faces; + var edges = newGeometry.egdes = this.geometry.edges; + var edgeFaces = newGeometry.edgeFaces = []; + + var vertexOffset = 0; + var vertexOffsetPerFace = []; + + for( f = 0, fl = faces.length; f < fl; f++ ) { + + face = faces[ f ]; + + // calculate faces vertex offset + + vertexOffsetPerFace.push( vertexOffset ); + vertexOffset += face instanceof THREE.Face3 ? 3 : 4; + + // set vertex normals to face normal + + face.vertexNormals[ 0 ] = face.normal; + face.vertexNormals[ 1 ] = face.normal; + face.vertexNormals[ 2 ] = face.normal; + + if( face instanceof THREE.Face4 ) face.vertexNormals[ 3 ] = face.normal; + + } + + + // setup edge faces + + for( e = 0, el = edges.length; e < el; e++ ) { + + edge = edges[ e ]; + + faceA = edge.faces[ 0 ]; + faceB = edge.faces[ 1 ]; + + faceAIndex = edge.faceIndices[ 0 ]; + faceBIndex = edge.faceIndices[ 1 ]; + + vertexA = edge.vertexIndices[ 0 ]; + vertexB = edge.vertexIndices[ 1 ]; + + // find combination and processed vertex index (vertices are split up by renderer) + + if( faceA.a === vertexA ) { faceACombination = "a"; faceAvertexAIndex = vertexOffsetPerFace[ faceAIndex ] + 0; } + else if( faceA.b === vertexA ) { faceACombination = "b"; faceAvertexAIndex = vertexOffsetPerFace[ faceAIndex ] + 1; } + else if( faceA.c === vertexA ) { faceACombination = "c"; faceAvertexAIndex = vertexOffsetPerFace[ faceAIndex ] + 2; } + else if( faceA.d === vertexA ) { faceACombination = "d"; faceAvertexAIndex = vertexOffsetPerFace[ faceAIndex ] + 3; } + + if( faceA.a === vertexB ) { faceACombination += "a"; faceAvertexBIndex = vertexOffsetPerFace[ faceAIndex ] + 0; } + else if( faceA.b === vertexB ) { faceACombination += "b"; faceAvertexBIndex = vertexOffsetPerFace[ faceAIndex ] + 1; } + else if( faceA.c === vertexB ) { faceACombination += "c"; faceAvertexBIndex = vertexOffsetPerFace[ faceAIndex ] + 2; } + else if( faceA.d === vertexB ) { faceACombination += "d"; faceAvertexBIndex = vertexOffsetPerFace[ faceAIndex ] + 3; } + + if( faceB.a === vertexA ) { faceBCombination = "a"; faceBvertexAIndex = vertexOffsetPerFace[ faceBIndex ] + 0; } + else if( faceB.b === vertexA ) { faceBCombination = "b"; faceBvertexAIndex = vertexOffsetPerFace[ faceBIndex ] + 1; } + else if( faceB.c === vertexA ) { faceBCombination = "c"; faceBvertexAIndex = vertexOffsetPerFace[ faceBIndex ] + 2; } + else if( faceB.d === vertexA ) { faceBCombination = "d"; faceBvertexAIndex = vertexOffsetPerFace[ faceBIndex ] + 3; } + + if( faceB.a === vertexB ) { faceBCombination += "a"; faceBvertexBIndex = vertexOffsetPerFace[ faceBIndex ] + 0; } + else if( faceB.b === vertexB ) { faceBCombination += "b"; faceBvertexBIndex = vertexOffsetPerFace[ faceBIndex ] + 1; } + else if( faceB.c === vertexB ) { faceBCombination += "c"; faceBvertexBIndex = vertexOffsetPerFace[ faceBIndex ] + 2; } + else if( faceB.d === vertexB ) { faceBCombination += "d"; faceBvertexBIndex = vertexOffsetPerFace[ faceBIndex ] + 3; } + + if( faceACombination === "ac" || + faceACombination === "ad" || + faceACombination === "ca" || + faceACombination === "da" ) { + + if( faceAvertexAIndex > faceAvertexBIndex ) { + + temp = faceAvertexAIndex; + faceAvertexAIndex = faceAvertexBIndex; + faceAvertexBIndex = temp; + + } + + } else { + + if( faceAvertexAIndex < faceAvertexBIndex ) { + + temp = faceAvertexAIndex; + faceAvertexAIndex = faceAvertexBIndex; + faceAvertexBIndex = temp; + + } + + } + + if( faceBCombination === "ac" || + faceBCombination === "ad" || + faceBCombination === "ca" || + faceBCombination === "da" ) { + + if( faceBvertexAIndex > faceBvertexBIndex ) { + + temp = faceBvertexAIndex; + faceBvertexAIndex = faceBvertexBIndex; + faceBvertexBIndex = temp; + + } + + } else { + + if( faceBvertexAIndex < faceBvertexBIndex ) { + + temp = faceBvertexAIndex; + faceBvertexAIndex = faceBvertexBIndex; + faceBvertexBIndex = temp; + + } + + } + + face = new THREE.Face4( faceAvertexAIndex, faceAvertexBIndex, faceBvertexAIndex, faceBvertexBIndex ); + face.normal.set( 1, 0, 0 ); + edgeFaces.push( face ); + + } + + this.geometry = newGeometry; + + } else { + + this.calculateShadowVolumeGeometryWithoutEdgeInfo( this.geometry ); + + } +} + + +THREE.ShadowVolume.prototype.calculateShadowVolumeGeometryWithoutEdgeInfo = function( originalGeometry ) { + + // create geometry + + this.geometry = new THREE.Geometry(); + this.geometry.boundingSphere = originalGeometry.boundingSphere; + this.geometry.edgeFaces = []; + + // copy vertices / faces from original mesh + + var vertexTypes = this.geometry.vertexTypes; + var vertices = this.geometry.vertices; + var faces = this.geometry.faces; + var edgeFaces = this.geometry.edgeFaces; + + var originalFaces = originalGeometry.faces; + var originalVertices = originalGeometry.vertices; + var fl = originalFaces.length; + + var originalFace, face, i, f, n, vertex, numVertices; + var indices = [ "a", "b", "c", "d" ]; + + + for( f = 0; f < fl; f++ ) { + + numVertices = vertices.length; + originalFace = originalFaces[ f ]; + + if ( originalFace instanceof THREE.Face4 ) { + + n = 4; + face = new THREE.Face4( numVertices, numVertices + 1, numVertices + 2, numVertices + 3 ); + + } else { + + n = 3; + face = new THREE.Face3( numVertices, numVertices + 1, numVertices + 2 ); + + } + + face.normal.copy( originalFace.normal ); + faces.push( face ); + + + for( i = 0; i < n; i++ ) { + + vertex = originalVertices[ originalFace[ indices[ i ]]]; + vertices.push( new THREE.Vertex( vertex.position.clone())); + + } + + } + + + // calculate edge faces + + var result, faceA, faceB, v, vl; + + for( var fa = 0; fa < originalFaces.length - 1; fa++ ) { + + faceA = faces[ fa ]; + + for( var fb = fa + 1; fb < originalFaces.length; fb++ ) { + + faceB = faces[ fb ]; + result = this.facesShareEdge( vertices, faceA, faceB ); + + if( result !== undefined ) { + + numVertices = vertices.length; + face = new THREE.Face4( result.indices[ 0 ], result.indices[ 3 ], result.indices[ 2 ], result.indices[ 1 ] ); + face.normal.set( 1, 0, 0 ); + edgeFaces.push( face ); + + } + + } + + } + +}; + + +THREE.ShadowVolume.prototype.facesShareEdge = function( vertices, faceA, faceB ) { + + var indicesA, + indicesB, + indexA, + indexB, + vertexA, + vertexB, + savedVertexA, + savedVertexB, + savedIndexA, + savedIndexB, + indexLetters, + a, b, + numMatches = 0, + indices = [ "a", "b", "c", "d" ]; + + if( faceA instanceof THREE.Face4 ) indicesA = 4; + else indicesA = 3; + + if( faceB instanceof THREE.Face4 ) indicesB = 4; + else indicesB = 3; + + + for( a = 0; a < indicesA; a++ ) { + + indexA = faceA[ indices[ a ] ]; + vertexA = vertices[ indexA ]; + + for( b = 0; b < indicesB; b++ ) { + + indexB = faceB[ indices[ b ] ]; + vertexB = vertices[ indexB ]; + + if( Math.abs( vertexA.position.x - vertexB.position.x ) < 0.0001 && + Math.abs( vertexA.position.y - vertexB.position.y ) < 0.0001 && + Math.abs( vertexA.position.z - vertexB.position.z ) < 0.0001 ) { + + numMatches++; + + if( numMatches === 1 ) { + + savedVertexA = vertexA; + savedVertexB = vertexB; + savedIndexA = indexA; + savedIndexB = indexB; + indexLetters = indices[ a ]; + + } + + if( numMatches === 2 ) { + + indexLetters += indices[ a ]; + + if( indexLetters === "ad" || indexLetters === "ac" ) { + + return { + + faces : [ faceA, faceB ], + vertices : [ savedVertexA, savedVertexB, vertexB, vertexA ], + indices : [ savedIndexA, savedIndexB, indexB, indexA ], + vertexTypes : [ 1, 2, 2, 1 ], + extrudable : true + + }; + + } else { + + return { + + faces : [ faceA, faceB ], + vertices : [ savedVertexA, vertexA, vertexB, savedVertexB ], + indices : [ savedIndexA, indexA, indexB, savedIndexB ], + vertexTypes : [ 1, 1, 2, 2 ], + extrudable : true + + }; + + } + + } + + } + + } + + } + + return undefined; + +}; +/** + * @author mikael emtinger / http://gomo.se/ + */ + +THREE.Sprite = function( parameters ) { + + THREE.Object3D.call( this ); + + if( parameters.material !== undefined ) { + + this.material = parameters.material; + this.map = undefined; + this.blending = material.blending; + + } else if( parameters.map !== undefined ) { + + this.map = parameters.map instanceof THREE.Texture ? parameters.map : THREE.ImageUtils.loadTexture( parameters.map ); + this.material = undefined; + this.blending = parameters.blending !== undefined ? parameters.blending : THREE.NormalBlending; + + } + + this.useScreenCoordinates = parameters.useScreenCoordinates !== undefined ? parameters.useScreenCoordinates : true; + this.mergeWith3D = parameters.mergeWith3D !== undefined ? parameters.mergeWith3D : !this.useScreenCoordinates; + this.affectedByDistance = parameters.affectedByDistance !== undefined ? parameters.affectedByDistance : !this.useScreenCoordinates; + this.scaleByViewport = parameters.scaleByViewport !== undefined ? parameters.scaleByViewport : !this.affectedByDistance; + this.alignment = parameters.alignment instanceof THREE.Vector2 ? parameters.alignment : THREE.SpriteAlignment.center; + + this.rotation3d = this.rotation; + this.rotation = 0; + this.opacity = 1; + + this.uvOffset = new THREE.Vector2( 0, 0 ); + this.uvScale = new THREE.Vector2( 1, 1 ); + +}; + +THREE.Sprite.prototype = new THREE.Object3D(); +THREE.Sprite.prototype.constructor = THREE.Sprite; +THREE.Sprite.prototype.supr = THREE.Object3D.prototype; + + +/* + * Custom update matrix + */ + +THREE.Sprite.prototype.updateMatrix = function () { + + this.matrix.setPosition( this.position ); + + this.rotation3d.set( 0, 0, this.rotation ); + this.matrix.setRotationFromEuler( this.rotation3d ); + + if ( this.scale.x !== 1 || this.scale.y !== 1 ) { + + this.matrix.scale( this.scale ); + this.boundRadiusScale = Math.max( this.scale.x, this.scale.y ); + + } + + this.matrixWorldNeedsUpdate = true; + +}; + +/* + * Alignment + */ + +THREE.SpriteAlignment = {}; +THREE.SpriteAlignment.topLeft = new THREE.Vector2( 1, -1 ); +THREE.SpriteAlignment.topCenter = new THREE.Vector2( 0, -1 ); +THREE.SpriteAlignment.topRight = new THREE.Vector2( -1, -1 ); +THREE.SpriteAlignment.centerLeft = new THREE.Vector2( 1, 0 ); +THREE.SpriteAlignment.center = new THREE.Vector2( 0, 0 ); +THREE.SpriteAlignment.centerRight = new THREE.Vector2( -1, 0 ); +THREE.SpriteAlignment.bottomLeft = new THREE.Vector2( 1, 1 ); +THREE.SpriteAlignment.bottomCenter = new THREE.Vector2( 0, 1 ); +THREE.SpriteAlignment.bottomRight = new THREE.Vector2( -1, 1 ); +/** + * @author mr.doob / http://mrdoob.com/ + * @author mikael emtinger / http://gomo.se/ + */ + +THREE.Scene = function () { + + THREE.Object3D.call( this ); + + this.matrixAutoUpdate = false; + + this.fog = null; + + this.collisions = null; + + this.objects = []; + this.lights = []; + this.sounds = []; + + this.__objectsAdded = []; + this.__objectsRemoved = []; + +}; + +THREE.Scene.prototype = new THREE.Object3D(); +THREE.Scene.prototype.constructor = THREE.Scene; +THREE.Scene.prototype.supr = THREE.Object3D.prototype; + +THREE.Scene.prototype.addChild = function( child ) { + + this.supr.addChild.call( this, child ); + this.addChildRecurse( child ); + +} + +THREE.Scene.prototype.addChildRecurse = function( child ) { + + if ( child instanceof THREE.Light ) { + + if ( this.lights.indexOf( child ) === -1 ) { + + this.lights.push( child ); + + } + + } else if ( child instanceof THREE.Sound ) { + + if ( this.sounds.indexOf( child ) === -1 ) { + + this.sounds.push( child ); + + } + + } else if ( !( child instanceof THREE.Camera || child instanceof THREE.Bone ) ) { + + if ( this.objects.indexOf( child ) === -1 ) { + + this.objects.push( child ); + this.__objectsAdded.push( child ); + + } + + } + + for ( var c = 0; c < child.children.length; c++ ) { + + this.addChildRecurse( child.children[ c ] ); + + } + +} + + +THREE.Scene.prototype.removeChild = function( child ) { + + this.supr.removeChild.call( this, child ); + this.removeChildRecurse( child ); + +} + +THREE.Scene.prototype.removeChildRecurse = function( child ) { + + if ( child instanceof THREE.Light ) { + + var i = this.lights.indexOf( child ); + + if ( i !== -1 ) { + + this.lights.splice( i, 1 ); + + } + + } else if ( child instanceof THREE.Sound ) { + + var i = this.sounds.indexOf( child ); + + if( i !== -1 ) { + + this.sounds.splice( i, 1 ); + + } + + } else if ( !( child instanceof THREE.Camera ) ) { + + var i = this.objects.indexOf( child ); + + if( i !== -1 ) { + + this.objects.splice( i, 1 ); + this.__objectsRemoved.push( child ); + + } + + } + + for ( var c = 0; c < child.children.length; c++ ) { + + this.removeChildRecurse( child.children[ c ] ); + + } + +} + +THREE.Scene.prototype.addObject = THREE.Scene.prototype.addChild; +THREE.Scene.prototype.removeObject = THREE.Scene.prototype.removeChild; +THREE.Scene.prototype.addLight = THREE.Scene.prototype.addChild; +THREE.Scene.prototype.removeLight = THREE.Scene.prototype.removeChild; +/** + * @author mr.doob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Fog = function ( hex, near, far ) { + + this.color = new THREE.Color( hex ); + this.near = near || 1; + this.far = far || 1000; + +}; +/** + * @author mr.doob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.FogExp2 = function ( hex, density ) { + + this.color = new THREE.Color( hex ); + this.density = ( density !== undefined ) ? density : 0.00025; + +}; +/** + * @author mr.doob / http://mrdoob.com/ + * @author supereggbert / http://www.paulbrunt.co.uk/ + * @author julianwa / https://github.com/julianwa + */ + +THREE.Projector = function() { + + var _object, _objectCount, _objectPool = [], + _vertex, _vertexCount, _vertexPool = [], + _face, _face3Count, _face3Pool = [], _face4Count, _face4Pool = [], + _line, _lineCount, _linePool = [], + _particle, _particleCount, _particlePool = [], + + _vector3 = new THREE.Vector4(), + _vector4 = new THREE.Vector4(), + _projScreenMatrix = new THREE.Matrix4(), + _projScreenObjectMatrix = new THREE.Matrix4(), + + _frustum = [ + new THREE.Vector4(), + new THREE.Vector4(), + new THREE.Vector4(), + new THREE.Vector4(), + new THREE.Vector4(), + new THREE.Vector4() + ], + + _clippedVertex1PositionScreen = new THREE.Vector4(), + _clippedVertex2PositionScreen = new THREE.Vector4(), + + _face3VertexNormals; + + + this.projectVector = function ( vector, camera ) { + + _projScreenMatrix.multiply( camera.projectionMatrix, camera.matrixWorldInverse ); + _projScreenMatrix.multiplyVector3( vector ); + + return vector; + + }; + + this.unprojectVector = function ( vector, camera ) { + + _projScreenMatrix.multiply( camera.matrixWorld, THREE.Matrix4.makeInvert( camera.projectionMatrix ) ); + _projScreenMatrix.multiplyVector3( vector ); + + return vector; + + }; + + this.projectObjects = function ( scene, camera, sort ) { + + var renderList = [], + o, ol, objects, object, matrix; + + _objectCount = 0; + + objects = scene.objects; + + for ( o = 0, ol = objects.length; o < ol; o ++ ) { + + object = objects[ o ]; + + if ( !object.visible || ( object instanceof THREE.Mesh && !isInFrustum( object ) ) ) continue; + + _object = getNextObjectInPool(); + + _vector3.copy( object.position ); + _projScreenMatrix.multiplyVector3( _vector3 ); + + _object.object = object; + _object.z = _vector3.z; + + renderList.push( _object ); + + } + + sort && renderList.sort( painterSort ); + + return renderList; + + }; + + // TODO: Rename to projectElements? + + this.projectScene = function ( scene, camera, sort ) { + + var renderList = [], near = camera.near, far = camera.far, + o, ol, v, vl, f, fl, n, nl, c, cl, u, ul, objects, object, + objectMatrix, objectMatrixRotation, objectMaterials, objectOverdraw, + geometry, vertices, vertex, vertexPositionScreen, + faces, face, faceVertexNormals, normal, faceVertexUvs, uvs, + v1, v2, v3, v4; + + _face3Count = 0; + _face4Count = 0; + _lineCount = 0; + _particleCount = 0; + + camera.matrixAutoUpdate && camera.update( undefined, true ); + + scene.update( undefined, false, camera ); + + _projScreenMatrix.multiply( camera.projectionMatrix, camera.matrixWorldInverse ); + computeFrustum( _projScreenMatrix ); + + objects = this.projectObjects( scene, camera, true ); + + for ( o = 0, ol = objects.length; o < ol; o++ ) { + + object = objects[ o ].object; + + if ( !object.visible ) continue; + + objectMatrix = object.matrixWorld; + objectMatrixRotation = object.matrixRotationWorld; + + objectMaterials = object.materials; + objectOverdraw = object.overdraw; + + _vertexCount = 0; + + if ( object instanceof THREE.Mesh ) { + + geometry = object.geometry; + vertices = geometry.vertices; + faces = geometry.faces; + faceVertexUvs = geometry.faceVertexUvs; + + for ( v = 0, vl = vertices.length; v < vl; v ++ ) { + + _vertex = getNextVertexInPool(); + _vertex.positionWorld.copy( vertices[ v ].position ); + + objectMatrix.multiplyVector3( _vertex.positionWorld ); + + _vertex.positionScreen.copy( _vertex.positionWorld ); + _projScreenMatrix.multiplyVector4( _vertex.positionScreen ); + + _vertex.positionScreen.x /= _vertex.positionScreen.w; + _vertex.positionScreen.y /= _vertex.positionScreen.w; + + _vertex.visible = _vertex.positionScreen.z > near && _vertex.positionScreen.z < far; + + } + + for ( f = 0, fl = faces.length; f < fl; f ++ ) { + + face = faces[ f ]; + + if ( face instanceof THREE.Face3 ) { + + v1 = _vertexPool[ face.a ]; + v2 = _vertexPool[ face.b ]; + v3 = _vertexPool[ face.c ]; + + if ( v1.visible && v2.visible && v3.visible && + ( object.doubleSided || ( object.flipSided != + ( v3.positionScreen.x - v1.positionScreen.x ) * ( v2.positionScreen.y - v1.positionScreen.y ) - + ( v3.positionScreen.y - v1.positionScreen.y ) * ( v2.positionScreen.x - v1.positionScreen.x ) < 0 ) ) ) { + + _face = getNextFace3InPool(); + + _face.v1.copy( v1 ); + _face.v2.copy( v2 ); + _face.v3.copy( v3 ); + + } else { + + continue; + + } + + } else if ( face instanceof THREE.Face4 ) { + + v1 = _vertexPool[ face.a ]; + v2 = _vertexPool[ face.b ]; + v3 = _vertexPool[ face.c ]; + v4 = _vertexPool[ face.d ]; + + if ( v1.visible && v2.visible && v3.visible && v4.visible && + ( object.doubleSided || ( object.flipSided != + ( ( v4.positionScreen.x - v1.positionScreen.x ) * ( v2.positionScreen.y - v1.positionScreen.y ) - + ( v4.positionScreen.y - v1.positionScreen.y ) * ( v2.positionScreen.x - v1.positionScreen.x ) < 0 || + ( v2.positionScreen.x - v3.positionScreen.x ) * ( v4.positionScreen.y - v3.positionScreen.y ) - + ( v2.positionScreen.y - v3.positionScreen.y ) * ( v4.positionScreen.x - v3.positionScreen.x ) < 0 ) ) ) ) { + + _face = getNextFace4InPool(); + + _face.v1.copy( v1 ); + _face.v2.copy( v2 ); + _face.v3.copy( v3 ); + _face.v4.copy( v4 ); + + } else { + + continue; + + } + + } + + _face.normalWorld.copy( face.normal ); + objectMatrixRotation.multiplyVector3( _face.normalWorld ); + + _face.centroidWorld.copy( face.centroid ); + objectMatrix.multiplyVector3( _face.centroidWorld ); + + _face.centroidScreen.copy( _face.centroidWorld ); + _projScreenMatrix.multiplyVector3( _face.centroidScreen ); + + faceVertexNormals = face.vertexNormals; + + for ( n = 0, nl = faceVertexNormals.length; n < nl; n ++ ) { + + normal = _face.vertexNormalsWorld[ n ]; + normal.copy( faceVertexNormals[ n ] ); + objectMatrixRotation.multiplyVector3( normal ); + + } + + for ( c = 0, cl = faceVertexUvs.length; c < cl; c ++ ) { + + uvs = faceVertexUvs[ c ][ f ]; + + if ( !uvs ) continue; + + for ( u = 0, ul = uvs.length; u < ul; u ++ ) { + + _face.uvs[ c ][ u ] = uvs[ u ]; + + } + + } + + _face.meshMaterials = objectMaterials; + _face.faceMaterials = face.materials; + _face.overdraw = objectOverdraw; + + _face.z = _face.centroidScreen.z; + + renderList.push( _face ); + + } + + } else if ( object instanceof THREE.Line ) { + + _projScreenObjectMatrix.multiply( _projScreenMatrix, objectMatrix ); + + vertices = object.geometry.vertices; + + v1 = getNextVertexInPool(); + v1.positionScreen.copy( vertices[ 0 ].position ); + _projScreenObjectMatrix.multiplyVector4( v1.positionScreen ); + + for ( v = 1, vl = vertices.length; v < vl; v++ ) { + + v1 = getNextVertexInPool(); + v1.positionScreen.copy( vertices[ v ].position ); + _projScreenObjectMatrix.multiplyVector4( v1.positionScreen ); + + v2 = _vertexPool[ _vertexCount - 2 ]; + + _clippedVertex1PositionScreen.copy( v1.positionScreen ); + _clippedVertex2PositionScreen.copy( v2.positionScreen ); + + if ( clipLine( _clippedVertex1PositionScreen, _clippedVertex2PositionScreen ) ) { + + // Perform the perspective divide + _clippedVertex1PositionScreen.multiplyScalar( 1 / _clippedVertex1PositionScreen.w ); + _clippedVertex2PositionScreen.multiplyScalar( 1 / _clippedVertex2PositionScreen.w ); + + _line = getNextLineInPool(); + _line.v1.positionScreen.copy( _clippedVertex1PositionScreen ); + _line.v2.positionScreen.copy( _clippedVertex2PositionScreen ); + + _line.z = Math.max( _clippedVertex1PositionScreen.z, _clippedVertex2PositionScreen.z ); + + _line.materials = object.materials; + + renderList.push( _line ); + + } + } + + } else if ( object instanceof THREE.Particle ) { + + _vector4.set( object.matrixWorld.n14, object.matrixWorld.n24, object.matrixWorld.n34, 1 ); + _projScreenMatrix.multiplyVector4( _vector4 ); + + _vector4.z /= _vector4.w; + + if ( _vector4.z > 0 && _vector4.z < 1 ) { + + _particle = getNextParticleInPool(); + _particle.x = _vector4.x / _vector4.w; + _particle.y = _vector4.y / _vector4.w; + _particle.z = _vector4.z; + + _particle.rotation = object.rotation.z; + + _particle.scale.x = object.scale.x * Math.abs( _particle.x - ( _vector4.x + camera.projectionMatrix.n11 ) / ( _vector4.w + camera.projectionMatrix.n14 ) ); + _particle.scale.y = object.scale.y * Math.abs( _particle.y - ( _vector4.y + camera.projectionMatrix.n22 ) / ( _vector4.w + camera.projectionMatrix.n24 ) ); + + _particle.materials = object.materials; + + renderList.push( _particle ); + + } + + } + + } + + sort && renderList.sort( painterSort ); + + return renderList; + + }; + + // Pools + + function getNextObjectInPool() { + + var object = _objectPool[ _objectCount ] = _objectPool[ _objectCount ] || new THREE.RenderableObject(); + + _objectCount ++; + + return object; + + } + + function getNextVertexInPool() { + + var vertex = _vertexPool[ _vertexCount ] = _vertexPool[ _vertexCount ] || new THREE.RenderableVertex(); + + _vertexCount ++; + + return vertex; + + } + + function getNextFace3InPool() { + + var face = _face3Pool[ _face3Count ] = _face3Pool[ _face3Count ] || new THREE.RenderableFace3(); + + _face3Count ++; + + return face; + + } + + function getNextFace4InPool() { + + var face = _face4Pool[ _face4Count ] = _face4Pool[ _face4Count ] || new THREE.RenderableFace4(); + + _face4Count ++; + + return face; + + } + + function getNextLineInPool() { + + var line = _linePool[ _lineCount ] = _linePool[ _lineCount ] || new THREE.RenderableLine(); + + _lineCount ++; + + return line; + + } + + function getNextParticleInPool() { + + var particle = _particlePool[ _particleCount ] = _particlePool[ _particleCount ] || new THREE.RenderableParticle(); + _particleCount ++; + return particle; + + } + + // + + function painterSort( a, b ) { + + return b.z - a.z; + + } + + function computeFrustum( m ) { + + _frustum[ 0 ].set( m.n41 - m.n11, m.n42 - m.n12, m.n43 - m.n13, m.n44 - m.n14 ); + _frustum[ 1 ].set( m.n41 + m.n11, m.n42 + m.n12, m.n43 + m.n13, m.n44 + m.n14 ); + _frustum[ 2 ].set( m.n41 + m.n21, m.n42 + m.n22, m.n43 + m.n23, m.n44 + m.n24 ); + _frustum[ 3 ].set( m.n41 - m.n21, m.n42 - m.n22, m.n43 - m.n23, m.n44 - m.n24 ); + _frustum[ 4 ].set( m.n41 - m.n31, m.n42 - m.n32, m.n43 - m.n33, m.n44 - m.n34 ); + _frustum[ 5 ].set( m.n41 + m.n31, m.n42 + m.n32, m.n43 + m.n33, m.n44 + m.n34 ); + + for ( var i = 0; i < 6; i ++ ) { + + var plane = _frustum[ i ]; + plane.divideScalar( Math.sqrt( plane.x * plane.x + plane.y * plane.y + plane.z * plane.z ) ); + + } + + } + + function isInFrustum( object ) { + + var distance, matrix = object.matrixWorld, + radius = - object.geometry.boundingSphere.radius * Math.max( object.scale.x, Math.max( object.scale.y, object.scale.z ) ); + + for ( var i = 0; i < 6; i ++ ) { + + distance = _frustum[ i ].x * matrix.n14 + _frustum[ i ].y * matrix.n24 + _frustum[ i ].z * matrix.n34 + _frustum[ i ].w; + if ( distance <= radius ) return false; + + } + + return true; + + }; + + function clipLine( s1, s2 ) { + + var alpha1 = 0, alpha2 = 1, + + // Calculate the boundary coordinate of each vertex for the near and far clip planes, + // Z = -1 and Z = +1, respectively. + bc1near = s1.z + s1.w, + bc2near = s2.z + s2.w, + bc1far = - s1.z + s1.w, + bc2far = - s2.z + s2.w; + + if ( bc1near >= 0 && bc2near >= 0 && bc1far >= 0 && bc2far >= 0 ) { + + // Both vertices lie entirely within all clip planes. + return true; + + } else if ( ( bc1near < 0 && bc2near < 0) || (bc1far < 0 && bc2far < 0 ) ) { + + // Both vertices lie entirely outside one of the clip planes. + return false; + + } else { + + // The line segment spans at least one clip plane. + + if ( bc1near < 0 ) { + + // v1 lies outside the near plane, v2 inside + alpha1 = Math.max( alpha1, bc1near / ( bc1near - bc2near ) ); + + } else if ( bc2near < 0 ) { + + // v2 lies outside the near plane, v1 inside + alpha2 = Math.min( alpha2, bc1near / ( bc1near - bc2near ) ); + + } + + if ( bc1far < 0 ) { + + // v1 lies outside the far plane, v2 inside + alpha1 = Math.max( alpha1, bc1far / ( bc1far - bc2far ) ); + + } else if ( bc2far < 0 ) { + + // v2 lies outside the far plane, v2 inside + alpha2 = Math.min( alpha2, bc1far / ( bc1far - bc2far ) ); + + } + + if ( alpha2 < alpha1 ) { + + // The line segment spans two boundaries, but is outside both of them. + // (This can't happen when we're only clipping against just near/far but good + // to leave the check here for future usage if other clip planes are added.) + return false; + + } else { + + // Update the s1 and s2 vertices to match the clipped line segment. + s1.lerpSelf( s2, alpha1 ); + s2.lerpSelf( s1, 1 - alpha2 ); + + return true; + + } + + } + + } + +}; +/** + * @author mr.doob / http://mrdoob.com/ + */ + +THREE.DOMRenderer = function () { + + THREE.Renderer.call( this ); + + var _renderList = null, + _projector = new THREE.Projector(), + _div = document.createElement( 'div' ), + _width, _height, _widthHalf, _heightHalf; + + this.domElement = _div; + + this.setSize = function ( width, height ) { + + _width = width; _height = height; + _widthHalf = _width / 2; _heightHalf = _height / 2; + + }; + + this.render = function ( scene, camera ) { + + var e, el, m, ml, element, material, dom, v1x, v1y; + + _renderList = _projector.projectScene( scene, camera ); + + for ( e = 0, el = _renderList.length; e < el; e++ ) { + + element = _renderList[ e ]; + + if ( element instanceof THREE.RenderableParticle ) { + + v1x = element.x * _widthHalf + _widthHalf; v1y = element.y * _heightHalf + _heightHalf; + + for ( m = 0, ml = element.material.length; m < ml; m++ ) { + + material = element.material[ m ]; + + if ( material instanceof THREE.ParticleDOMMaterial ) { + + dom = material.domElement; + dom.style.left = v1x + 'px'; + dom.style.top = v1y + 'px'; + + } + + } + + } + + } + + }; + +} +/** + * @author mr.doob / http://mrdoob.com/ + */ + +THREE.CanvasRenderer = function ( parameters ) { + + var _this = this, + _renderList = null, + _projector = new THREE.Projector(), + + parameters = parameters || {}, + + _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElement( 'canvas' ), + + _canvasWidth, _canvasHeight, _canvasWidthHalf, _canvasHeightHalf, + _context = _canvas.getContext( '2d' ), + + _clearColor = new THREE.Color( 0x000000 ), + _clearOpacity = 0, + + _contextGlobalAlpha = 1, + _contextGlobalCompositeOperation = 0, + _contextStrokeStyle = null, + _contextFillStyle = null, + _contextLineWidth = null, + _contextLineCap = null, + _contextLineJoin = null, + + _v1, _v2, _v3, _v4, + _v5 = new THREE.RenderableVertex(), + _v6 = new THREE.RenderableVertex(), + + _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, + _v4x, _v4y, _v5x, _v5y, _v6x, _v6y, + + _color = new THREE.Color( 0x000000 ), + _color1 = new THREE.Color( 0x000000 ), + _color2 = new THREE.Color( 0x000000 ), + _color3 = new THREE.Color( 0x000000 ), + _color4 = new THREE.Color( 0x000000 ), + + _near, _far, + + _bitmap, _uvs, + _uv1x, _uv1y, _uv2x, _uv2y, _uv3x, _uv3y, + + _clipRect = new THREE.Rectangle(), + _clearRect = new THREE.Rectangle(), + _bboxRect = new THREE.Rectangle(), + + _enableLighting = false, + _light = new THREE.Color(), + _ambientLight = new THREE.Color(), + _directionalLights = new THREE.Color(), + _pointLights = new THREE.Color(), + + _pi2 = Math.PI * 2, + _vector3 = new THREE.Vector3(), // Needed for PointLight + + _pixelMap, _pixelMapContext, _pixelMapImage, _pixelMapData, + _gradientMap, _gradientMapContext, _gradientMapQuality = 16; + + _pixelMap = document.createElement( 'canvas' ); + _pixelMap.width = _pixelMap.height = 2; + + _pixelMapContext = _pixelMap.getContext( '2d' ); + _pixelMapContext.fillStyle = 'rgba(0,0,0,1)'; + _pixelMapContext.fillRect( 0, 0, 2, 2 ); + + _pixelMapImage = _pixelMapContext.getImageData( 0, 0, 2, 2 ); + _pixelMapData = _pixelMapImage.data; + + _gradientMap = document.createElement( 'canvas' ); + _gradientMap.width = _gradientMap.height = _gradientMapQuality; + + _gradientMapContext = _gradientMap.getContext( '2d' ); + _gradientMapContext.translate( - _gradientMapQuality / 2, - _gradientMapQuality / 2 ); + _gradientMapContext.scale( _gradientMapQuality, _gradientMapQuality ); + + _gradientMapQuality --; // Fix UVs + + this.domElement = _canvas; + + this.autoClear = true; + this.sortObjects = true; + this.sortElements = true; + + this.data = { + + vertices: 0, + faces: 0 + + } + + this.setSize = function ( width, height ) { + + _canvasWidth = width; + _canvasHeight = height; + _canvasWidthHalf = _canvasWidth / 2; + _canvasHeightHalf = _canvasHeight / 2; + + _canvas.width = _canvasWidth; + _canvas.height = _canvasHeight; + + _clipRect.set( - _canvasWidthHalf, - _canvasHeightHalf, _canvasWidthHalf, _canvasHeightHalf ); + + _contextGlobalAlpha = 1; + _contextGlobalCompositeOperation = 0; + _contextStrokeStyle = null; + _contextFillStyle = null; + _contextLineWidth = null; + _contextLineCap = null; + _contextLineJoin = null; + + }; + + this.setClearColor = function( color, opacity ) { + + _clearColor = color; + _clearOpacity = opacity; + + }; + + this.setClearColorHex = function( hex, opacity ) { + + _clearColor.setHex( hex ); + _clearOpacity = opacity; + + }; + + this.clear = function () { + + _context.setTransform( 1, 0, 0, - 1, _canvasWidthHalf, _canvasHeightHalf ); + + if ( !_clearRect.isEmpty() ) { + + _clearRect.inflate( 1 ); + _clearRect.minSelf( _clipRect ); + + if ( _clearColor.hex == 0x000000 && _clearOpacity == 0 ) { + + _context.clearRect( _clearRect.getX(), _clearRect.getY(), _clearRect.getWidth(), _clearRect.getHeight() ); + + } else { + + setBlending( THREE.NormalBlending ); + setOpacity( 1 ); + + _context.fillStyle = 'rgba(' + Math.floor( _clearColor.r * 255 ) + ',' + Math.floor( _clearColor.g * 255 ) + ',' + Math.floor( _clearColor.b * 255 ) + ',' + _clearOpacity + ')'; + _context.fillRect( _clearRect.getX(), _clearRect.getY(), _clearRect.getWidth(), _clearRect.getHeight() ); + + } + + _clearRect.empty(); + + } + + }; + + this.render = function ( scene, camera ) { + + var e, el, element, m, ml, fm, fml, material; + + this.autoClear ? this.clear() : _context.setTransform( 1, 0, 0, - 1, _canvasWidthHalf, _canvasHeightHalf ); + + _this.data.vertices = 0; + _this.data.faces = 0; + + _renderList = _projector.projectScene( scene, camera, this.sortElements ); + + /* DEBUG + _context.fillStyle = 'rgba( 0, 255, 255, 0.5 )'; + _context.fillRect( _clipRect.getX(), _clipRect.getY(), _clipRect.getWidth(), _clipRect.getHeight() ); + */ + + _enableLighting = scene.lights.length > 0; + + if ( _enableLighting ) { + + calculateLights( scene ); + + } + + for ( e = 0, el = _renderList.length; e < el; e++ ) { + + element = _renderList[ e ]; + + _bboxRect.empty(); + + if ( element instanceof THREE.RenderableParticle ) { + + _v1 = element; + _v1.x *= _canvasWidthHalf; _v1.y *= _canvasHeightHalf; + + m = 0; ml = element.materials.length; + + while( m < ml ) { + + material = element.materials[ m ++ ]; + material.opacity != 0 && renderParticle( _v1, element, material, scene ); + + } + + } else if ( element instanceof THREE.RenderableLine ) { + + _v1 = element.v1; _v2 = element.v2; + + _v1.positionScreen.x *= _canvasWidthHalf; _v1.positionScreen.y *= _canvasHeightHalf; + _v2.positionScreen.x *= _canvasWidthHalf; _v2.positionScreen.y *= _canvasHeightHalf; + + _bboxRect.addPoint( _v1.positionScreen.x, _v1.positionScreen.y ); + _bboxRect.addPoint( _v2.positionScreen.x, _v2.positionScreen.y ); + + if ( _clipRect.instersects( _bboxRect ) ) { + + m = 0; ml = element.materials.length; + + while ( m < ml ) { + + material = element.materials[ m ++ ]; + material.opacity != 0 && renderLine( _v1, _v2, element, material, scene ); + + } + + } + + + } else if ( element instanceof THREE.RenderableFace3 ) { + + _v1 = element.v1; _v2 = element.v2; _v3 = element.v3; + + _v1.positionScreen.x *= _canvasWidthHalf; _v1.positionScreen.y *= _canvasHeightHalf; + _v2.positionScreen.x *= _canvasWidthHalf; _v2.positionScreen.y *= _canvasHeightHalf; + _v3.positionScreen.x *= _canvasWidthHalf; _v3.positionScreen.y *= _canvasHeightHalf; + + if ( element.overdraw ) { + + expand( _v1.positionScreen, _v2.positionScreen ); + expand( _v2.positionScreen, _v3.positionScreen ); + expand( _v3.positionScreen, _v1.positionScreen ); + + } + + _bboxRect.add3Points( _v1.positionScreen.x, _v1.positionScreen.y, + _v2.positionScreen.x, _v2.positionScreen.y, + _v3.positionScreen.x, _v3.positionScreen.y ); + + if ( _clipRect.instersects( _bboxRect ) ) { + + m = 0; ml = element.meshMaterials.length; + + while ( m < ml ) { + + material = element.meshMaterials[ m ++ ]; + + if ( material instanceof THREE.MeshFaceMaterial ) { + + fm = 0; fml = element.faceMaterials.length; + + while ( fm < fml ) { + + material = element.faceMaterials[ fm ++ ]; + material && material.opacity != 0 && renderFace3( _v1, _v2, _v3, 0, 1, 2, element, material, scene ); + + } + + continue; + + } + + material.opacity != 0 && renderFace3( _v1, _v2, _v3, 0, 1, 2, element, material, scene ); + + } + + } + + } else if ( element instanceof THREE.RenderableFace4 ) { + + _v1 = element.v1; _v2 = element.v2; _v3 = element.v3; _v4 = element.v4; + + _v1.positionScreen.x *= _canvasWidthHalf; _v1.positionScreen.y *= _canvasHeightHalf; + _v2.positionScreen.x *= _canvasWidthHalf; _v2.positionScreen.y *= _canvasHeightHalf; + _v3.positionScreen.x *= _canvasWidthHalf; _v3.positionScreen.y *= _canvasHeightHalf; + _v4.positionScreen.x *= _canvasWidthHalf; _v4.positionScreen.y *= _canvasHeightHalf; + + _v5.positionScreen.copy( _v2.positionScreen ); + _v6.positionScreen.copy( _v4.positionScreen ); + + if ( element.overdraw ) { + + expand( _v1.positionScreen, _v2.positionScreen ); + expand( _v2.positionScreen, _v4.positionScreen ); + expand( _v4.positionScreen, _v1.positionScreen ); + + expand( _v3.positionScreen, _v5.positionScreen ); + expand( _v3.positionScreen, _v6.positionScreen ); + + } + + _bboxRect.addPoint( _v1.positionScreen.x, _v1.positionScreen.y ); + _bboxRect.addPoint( _v2.positionScreen.x, _v2.positionScreen.y ); + _bboxRect.addPoint( _v3.positionScreen.x, _v3.positionScreen.y ); + _bboxRect.addPoint( _v4.positionScreen.x, _v4.positionScreen.y ); + + if ( _clipRect.instersects( _bboxRect ) ) { + + m = 0; ml = element.meshMaterials.length; + + while ( m < ml ) { + + material = element.meshMaterials[ m ++ ]; + + if ( material instanceof THREE.MeshFaceMaterial ) { + + fm = 0; fml = element.faceMaterials.length; + + while ( fm < fml ) { + + material = element.faceMaterials[ fm ++ ]; + material && material.opacity != 0 && renderFace4( _v1, _v2, _v3, _v4, _v5, _v6, element, material, scene ); + + } + + continue; + + } + + material.opacity != 0 && renderFace4( _v1, _v2, _v3, _v4, _v5, _v6, element, material, scene ); + + } + + } + + } + + /* + _context.lineWidth = 1; + _context.strokeStyle = 'rgba( 0, 255, 0, 0.5 )'; + _context.strokeRect( _bboxRect.getX(), _bboxRect.getY(), _bboxRect.getWidth(), _bboxRect.getHeight() ); + */ + + _clearRect.addRectangle( _bboxRect ); + + + } + + /* DEBUG + _context.lineWidth = 1; + _context.strokeStyle = 'rgba( 255, 0, 0, 0.5 )'; + _context.strokeRect( _clearRect.getX(), _clearRect.getY(), _clearRect.getWidth(), _clearRect.getHeight() ); + */ + + _context.setTransform( 1, 0, 0, 1, 0, 0 ); + + // + + function calculateLights( scene ) { + + var l, ll, light, lightColor, + lights = scene.lights; + + _ambientLight.setRGB( 0, 0, 0 ); + _directionalLights.setRGB( 0, 0, 0 ); + _pointLights.setRGB( 0, 0, 0 ); + + for ( l = 0, ll = lights.length; l < ll; l ++ ) { + + light = lights[ l ]; + lightColor = light.color; + + if ( light instanceof THREE.AmbientLight ) { + + _ambientLight.r += lightColor.r; + _ambientLight.g += lightColor.g; + _ambientLight.b += lightColor.b; + + } else if ( light instanceof THREE.DirectionalLight ) { + + // for particles + + _directionalLights.r += lightColor.r; + _directionalLights.g += lightColor.g; + _directionalLights.b += lightColor.b; + + } else if ( light instanceof THREE.PointLight ) { + + // for particles + + _pointLights.r += lightColor.r; + _pointLights.g += lightColor.g; + _pointLights.b += lightColor.b; + + } + + } + + } + + function calculateLight( scene, position, normal, color ) { + + var l, ll, light, lightColor, + amount, lights = scene.lights; + + for ( l = 0, ll = lights.length; l < ll; l ++ ) { + + light = lights[ l ]; + lightColor = light.color; + + if ( light instanceof THREE.DirectionalLight ) { + + amount = normal.dot( light.position ); + + if ( amount <= 0 ) continue; + + amount *= light.intensity; + + color.r += lightColor.r * amount; + color.g += lightColor.g * amount; + color.b += lightColor.b * amount; + + } else if ( light instanceof THREE.PointLight ) { + + amount = normal.dot( _vector3.sub( light.position, position ).normalize() ); + + if ( amount <= 0 ) continue; + + amount *= light.distance == 0 ? 1 : 1 - Math.min( position.distanceTo( light.position ) / light.distance, 1 ); + + if ( amount == 0 ) continue; + + amount *= light.intensity; + + color.r += lightColor.r * amount; + color.g += lightColor.g * amount; + color.b += lightColor.b * amount; + + } + + } + + } + + function renderParticle ( v1, element, material, scene ) { + + setOpacity( material.opacity ); + setBlending( material.blending ); + + var width, height, scaleX, scaleY, + bitmap, bitmapWidth, bitmapHeight; + + if ( material instanceof THREE.ParticleBasicMaterial ) { + + if ( material.map ) { + + bitmap = material.map.image; + bitmapWidth = bitmap.width >> 1; + bitmapHeight = bitmap.height >> 1; + + scaleX = element.scale.x * _canvasWidthHalf; + scaleY = element.scale.y * _canvasHeightHalf; + + width = scaleX * bitmapWidth; + height = scaleY * bitmapHeight; + + // TODO: Rotations break this... + + _bboxRect.set( v1.x - width, v1.y - height, v1.x + width, v1.y + height ); + + if ( !_clipRect.instersects( _bboxRect ) ) { + + return; + + } + + _context.save(); + _context.translate( v1.x, v1.y ); + _context.rotate( - element.rotation ); + _context.scale( scaleX, - scaleY ); + + _context.translate( - bitmapWidth, - bitmapHeight ); + _context.drawImage( bitmap, 0, 0 ); + + _context.restore(); + + } + + /* DEBUG + _context.beginPath(); + _context.moveTo( v1.x - 10, v1.y ); + _context.lineTo( v1.x + 10, v1.y ); + _context.moveTo( v1.x, v1.y - 10 ); + _context.lineTo( v1.x, v1.y + 10 ); + _context.closePath(); + _context.strokeStyle = 'rgb(255,255,0)'; + _context.stroke(); + */ + + } else if ( material instanceof THREE.ParticleCanvasMaterial ) { + + width = element.scale.x * _canvasWidthHalf; + height = element.scale.y * _canvasHeightHalf; + + _bboxRect.set( v1.x - width, v1.y - height, v1.x + width, v1.y + height ); + + if ( !_clipRect.instersects( _bboxRect ) ) { + + return; + + } + + setStrokeStyle( material.color ); + setFillStyle( material.color ); + + _context.save(); + _context.translate( v1.x, v1.y ); + _context.rotate( - element.rotation ); + _context.scale( width, height ); + + material.program( _context ); + + _context.restore(); + + } + + } + + function renderLine( v1, v2, element, material, scene ) { + + setOpacity( material.opacity ); + setBlending( material.blending ); + + _context.beginPath(); + _context.moveTo( v1.positionScreen.x, v1.positionScreen.y ); + _context.lineTo( v2.positionScreen.x, v2.positionScreen.y ); + _context.closePath(); + + if ( material instanceof THREE.LineBasicMaterial ) { + + setLineWidth( material.linewidth ); + setLineCap( material.linecap ); + setLineJoin( material.linejoin ); + setStrokeStyle( material.color ); + + _context.stroke(); + _bboxRect.inflate( material.linewidth * 2 ); + + } + + } + + function renderFace3( v1, v2, v3, uv1, uv2, uv3, element, material, scene ) { + + _this.data.vertices += 3; + _this.data.faces ++; + + setOpacity( material.opacity ); + setBlending( material.blending ); + + _v1x = v1.positionScreen.x; _v1y = v1.positionScreen.y; + _v2x = v2.positionScreen.x; _v2y = v2.positionScreen.y; + _v3x = v3.positionScreen.x; _v3y = v3.positionScreen.y; + + drawTriangle( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y ); + + if ( material instanceof THREE.MeshBasicMaterial ) { + + if ( material.map/* && !material.wireframe*/ ) { + + if ( material.map.mapping instanceof THREE.UVMapping ) { + + _uvs = element.uvs[ 0 ]; + texturePath( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, material.map.image, _uvs[ uv1 ].u, _uvs[ uv1 ].v, _uvs[ uv2 ].u, _uvs[ uv2 ].v, _uvs[ uv3 ].u, _uvs[ uv3 ].v ); + + } + + + } else if ( material.envMap ) { + + if ( material.envMap.mapping instanceof THREE.SphericalReflectionMapping ) { + + var cameraMatrix = camera.matrixWorldInverse; + + _vector3.copy( element.vertexNormalsWorld[ 0 ] ); + _uv1x = ( _vector3.x * cameraMatrix.n11 + _vector3.y * cameraMatrix.n12 + _vector3.z * cameraMatrix.n13 ) * 0.5 + 0.5; + _uv1y = - ( _vector3.x * cameraMatrix.n21 + _vector3.y * cameraMatrix.n22 + _vector3.z * cameraMatrix.n23 ) * 0.5 + 0.5; + + _vector3.copy( element.vertexNormalsWorld[ 1 ] ); + _uv2x = ( _vector3.x * cameraMatrix.n11 + _vector3.y * cameraMatrix.n12 + _vector3.z * cameraMatrix.n13 ) * 0.5 + 0.5; + _uv2y = - ( _vector3.x * cameraMatrix.n21 + _vector3.y * cameraMatrix.n22 + _vector3.z * cameraMatrix.n23 ) * 0.5 + 0.5; + + _vector3.copy( element.vertexNormalsWorld[ 2 ] ); + _uv3x = ( _vector3.x * cameraMatrix.n11 + _vector3.y * cameraMatrix.n12 + _vector3.z * cameraMatrix.n13 ) * 0.5 + 0.5; + _uv3y = - ( _vector3.x * cameraMatrix.n21 + _vector3.y * cameraMatrix.n22 + _vector3.z * cameraMatrix.n23 ) * 0.5 + 0.5; + + texturePath( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, material.envMap.image, _uv1x, _uv1y, _uv2x, _uv2y, _uv3x, _uv3y ); + + }/* else if ( material.envMap.mapping == THREE.SphericalRefractionMapping ) { + + + + }*/ + + + } else { + + material.wireframe ? strokePath( material.color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) : fillPath( material.color ); + + } + + } else if ( material instanceof THREE.MeshLambertMaterial ) { + + if ( material.map && !material.wireframe ) { + + if ( material.map.mapping instanceof THREE.UVMapping ) { + + _uvs = element.uvs[ 0 ]; + texturePath( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, material.map.image, _uvs[ uv1 ].u, _uvs[ uv1 ].v, _uvs[ uv2 ].u, _uvs[ uv2 ].v, _uvs[ uv3 ].u, _uvs[ uv3 ].v ); + + } + + setBlending( THREE.SubtractiveBlending ); + + } + + if ( _enableLighting ) { + + if ( !material.wireframe && material.shading == THREE.SmoothShading && element.vertexNormalsWorld.length == 3 ) { + + _color1.r = _color2.r = _color3.r = _ambientLight.r; + _color1.g = _color2.g = _color3.g = _ambientLight.g; + _color1.b = _color2.b = _color3.b = _ambientLight.b; + + calculateLight( scene, element.v1.positionWorld, element.vertexNormalsWorld[ 0 ], _color1 ); + calculateLight( scene, element.v2.positionWorld, element.vertexNormalsWorld[ 1 ], _color2 ); + calculateLight( scene, element.v3.positionWorld, element.vertexNormalsWorld[ 2 ], _color3 ); + + _color4.r = ( _color2.r + _color3.r ) * 0.5; + _color4.g = ( _color2.g + _color3.g ) * 0.5; + _color4.b = ( _color2.b + _color3.b ) * 0.5; + + _bitmap = getGradientTexture( _color1, _color2, _color3, _color4 ); + + texturePath( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _bitmap, 0, 0, 1, 0, 0, 1 ); + + } else { + + _light.r = _ambientLight.r; + _light.g = _ambientLight.g; + _light.b = _ambientLight.b; + + calculateLight( scene, element.centroidWorld, element.normalWorld, _light ); + + _color.r = Math.max( 0, Math.min( material.color.r * _light.r, 1 ) ); + _color.g = Math.max( 0, Math.min( material.color.g * _light.g, 1 ) ); + _color.b = Math.max( 0, Math.min( material.color.b * _light.b, 1 ) ); + _color.updateHex(); + + material.wireframe ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) : fillPath( _color ); + + } + + } else { + + material.wireframe ? strokePath( material.color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) : fillPath( material.color ); + + } + + } else if ( material instanceof THREE.MeshDepthMaterial ) { + + _near = camera.near; + _far = camera.far; + + _color1.r = _color1.g = _color1.b = 1 - smoothstep( v1.positionScreen.z, _near, _far ); + _color2.r = _color2.g = _color2.b = 1 - smoothstep( v2.positionScreen.z, _near, _far ); + _color3.r = _color3.g = _color3.b = 1 - smoothstep( v3.positionScreen.z, _near, _far ); + + _color4.r = ( _color2.r + _color3.r ) * 0.5; + _color4.g = ( _color2.g + _color3.g ) * 0.5; + _color4.b = ( _color2.b + _color3.b ) * 0.5; + + _bitmap = getGradientTexture( _color1, _color2, _color3, _color4 ); + + texturePath( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _bitmap, 0, 0, 1, 0, 0, 1 ); + + } else if ( material instanceof THREE.MeshNormalMaterial ) { + + _color.r = normalToComponent( element.normalWorld.x ); + _color.g = normalToComponent( element.normalWorld.y ); + _color.b = normalToComponent( element.normalWorld.z ); + _color.updateHex(); + + material.wireframe ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) : fillPath( _color ); + + } + + } + + function renderFace4( v1, v2, v3, v4, v5, v6, element, material, scene ) { + + _this.data.vertices += 4; + _this.data.faces ++; + + setOpacity( material.opacity ); + setBlending( material.blending ); + + if ( material.map || material.envMap ) { + + // Let renderFace3() handle this + + renderFace3( v1, v2, v4, 0, 1, 3, element, material, scene ); + renderFace3( v5, v3, v6, 1, 2, 3, element, material, scene ); + + return; + + } + + _v1x = v1.positionScreen.x; _v1y = v1.positionScreen.y; + _v2x = v2.positionScreen.x; _v2y = v2.positionScreen.y; + _v3x = v3.positionScreen.x; _v3y = v3.positionScreen.y; + _v4x = v4.positionScreen.x; _v4y = v4.positionScreen.y; + _v5x = v5.positionScreen.x; _v5y = v5.positionScreen.y; + _v6x = v6.positionScreen.x; _v6y = v6.positionScreen.y; + + if ( material instanceof THREE.MeshBasicMaterial ) { + + drawQuad( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _v4x, _v4y ); + + material.wireframe ? strokePath( material.color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) : fillPath( material.color ); + + } else if ( material instanceof THREE.MeshLambertMaterial ) { + + if ( _enableLighting ) { + + if ( !material.wireframe && material.shading == THREE.SmoothShading && element.vertexNormalsWorld.length == 4 ) { + + _color1.r = _color2.r = _color3.r = _color4.r = _ambientLight.r; + _color1.g = _color2.g = _color3.g = _color4.g = _ambientLight.g; + _color1.b = _color2.b = _color3.b = _color4.b = _ambientLight.b; + + calculateLight( scene, element.v1.positionWorld, element.vertexNormalsWorld[ 0 ], _color1 ); + calculateLight( scene, element.v2.positionWorld, element.vertexNormalsWorld[ 1 ], _color2 ); + calculateLight( scene, element.v4.positionWorld, element.vertexNormalsWorld[ 3 ], _color3 ); + calculateLight( scene, element.v3.positionWorld, element.vertexNormalsWorld[ 2 ], _color4 ); + + _bitmap = getGradientTexture( _color1, _color2, _color3, _color4 ); + + // TODO: UVs are incorrect, v4->v3? + + drawTriangle( _v1x, _v1y, _v2x, _v2y, _v4x, _v4y ); + texturePath( _v1x, _v1y, _v2x, _v2y, _v4x, _v4y, _bitmap, 0, 0, 1, 0, 0, 1 ); + + drawTriangle( _v5x, _v5y, _v3x, _v3y, _v6x, _v6y ); + texturePath( _v5x, _v5y, _v3x, _v3y, _v6x, _v6y, _bitmap, 1, 0, 1, 1, 0, 1 ); + + } else { + + _light.r = _ambientLight.r; + _light.g = _ambientLight.g; + _light.b = _ambientLight.b; + + calculateLight( scene, element.centroidWorld, element.normalWorld, _light ); + + _color.r = Math.max( 0, Math.min( material.color.r * _light.r, 1 ) ); + _color.g = Math.max( 0, Math.min( material.color.g * _light.g, 1 ) ); + _color.b = Math.max( 0, Math.min( material.color.b * _light.b, 1 ) ); + _color.updateHex(); + + drawQuad( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _v4x, _v4y ); + + material.wireframe ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) : fillPath( _color ); + + } + + } else { + + drawQuad( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _v4x, _v4y ); + + material.wireframe ? strokePath( material.color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) : fillPath( material.color ); + + } + + } else if ( material instanceof THREE.MeshNormalMaterial ) { + + _color.r = normalToComponent( element.normalWorld.x ); + _color.g = normalToComponent( element.normalWorld.y ); + _color.b = normalToComponent( element.normalWorld.z ); + _color.updateHex(); + + drawQuad( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _v4x, _v4y ); + + material.wireframe ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) : fillPath( _color ); + + } else if ( material instanceof THREE.MeshDepthMaterial ) { + + _near = camera.near; + _far = camera.far; + + _color1.r = _color1.g = _color1.b = 1 - smoothstep( v1.positionScreen.z, _near, _far ); + _color2.r = _color2.g = _color2.b = 1 - smoothstep( v2.positionScreen.z, _near, _far ); + _color3.r = _color3.g = _color3.b = 1 - smoothstep( v4.positionScreen.z, _near, _far ); + _color4.r = _color4.g = _color4.b = 1 - smoothstep( v3.positionScreen.z, _near, _far ); + + _bitmap = getGradientTexture( _color1, _color2, _color3, _color4 ); + + // TODO: UVs are incorrect, v4->v3? + + drawTriangle( _v1x, _v1y, _v2x, _v2y, _v4x, _v4y ); + texturePath( _v1x, _v1y, _v2x, _v2y, _v4x, _v4y, _bitmap, 0, 0, 1, 0, 0, 1 ); + + drawTriangle( _v5x, _v5y, _v3x, _v3y, _v6x, _v6y ); + texturePath( _v5x, _v5y, _v3x, _v3y, _v6x, _v6y, _bitmap, 1, 0, 1, 1, 0, 1 ); + + } + + } + + // + + function drawTriangle( x0, y0, x1, y1, x2, y2 ) { + + _context.beginPath(); + _context.moveTo( x0, y0 ); + _context.lineTo( x1, y1 ); + _context.lineTo( x2, y2 ); + _context.lineTo( x0, y0 ); + _context.closePath(); + + } + + function drawQuad( x0, y0, x1, y1, x2, y2, x3, y3 ) { + + _context.beginPath(); + _context.moveTo( x0, y0 ); + _context.lineTo( x1, y1 ); + _context.lineTo( x2, y2 ); + _context.lineTo( x3, y3 ); + _context.lineTo( x0, y0 ); + _context.closePath(); + + } + + function strokePath( color, linewidth, linecap, linejoin ) { + + setLineWidth( linewidth ); + setLineCap( linecap ); + setLineJoin( linejoin ); + setStrokeStyle( color ); + + _context.stroke(); + + _bboxRect.inflate( linewidth * 2 ); + + } + + function fillPath( color ) { + + setFillStyle( color ); + _context.fill(); + + } + + function texturePath( x0, y0, x1, y1, x2, y2, bitmap, u0, v0, u1, v1, u2, v2 ) { + + /* + // http://tulrich.com/geekstuff/canvas/jsgl.js + + var m11, m12, m21, m22, dx, dy, denom, + width = bitmap.width - 1, + height = bitmap.height - 1; + + u0 *= width; v0 *= height; + u1 *= width; v1 *= height; + u2 *= width; v2 *= height; + + _context.save(); + + denom = u0 * (v2 - v1) - u1 * v2 + u2 * v1 + (u1 - u2) * v0; + + if ( denom == 0 ) return; + + m11 = - (v0 * (x2 - x1) - v1 * x2 + v2 * x1 + (v1 - v2) * x0) / denom; + m12 = (v1 * y2 + v0 * (y1 - y2) - v2 * y1 + (v2 - v1) * y0) / denom; + m21 = (u0 * (x2 - x1) - u1 * x2 + u2 * x1 + (u1 - u2) * x0) / denom; + m22 = - (u1 * y2 + u0 * (y1 - y2) - u2 * y1 + (u2 - u1) * y0) / denom; + dx = (u0 * (v2 * x1 - v1 * x2) + v0 * (u1 * x2 - u2 * x1) + (u2 * v1 - u1 * v2) * x0) / denom; + dy = (u0 * (v2 * y1 - v1 * y2) + v0 * (u1 * y2 - u2 * y1) + (u2 * v1 - u1 * v2) * y0) / denom; + + _context.transform( m11, m12, m21, m22, dx, dy ); + + _context.clip(); + _context.drawImage( bitmap, 0, 0 ); + _context.restore(); + */ + + // http://extremelysatisfactorytotalitarianism.com/blog/?p=2120 + + var a, b, c, d, e, f, det, idet, + width = bitmap.width - 1, + height = bitmap.height - 1; + + u0 *= width; v0 *= height; + u1 *= width; v1 *= height; + u2 *= width; v2 *= height; + + x1 -= x0; y1 -= y0; + x2 -= x0; y2 -= y0; + + u1 -= u0; v1 -= v0; + u2 -= u0; v2 -= v0; + + det = u1 * v2 - u2 * v1; + + if ( det == 0 ) return; + + idet = 1 / det; + + a = ( v2 * x1 - v1 * x2 ) * idet; + b = ( v2 * y1 - v1 * y2 ) * idet; + c = ( u1 * x2 - u2 * x1 ) * idet; + d = ( u1 * y2 - u2 * y1 ) * idet; + + e = x0 - a * u0 - c * v0; + f = y0 - b * u0 - d * v0; + + _context.save(); + _context.transform( a, b, c, d, e, f ); + _context.clip(); + _context.drawImage( bitmap, 0, 0 ); + _context.restore(); + + } + + function getGradientTexture( color1, color2, color3, color4 ) { + + // http://mrdoob.com/blog/post/710 + + var c1r = ~~ ( color1.r * 255 ), c1g = ~~ ( color1.g * 255 ), c1b = ~~ ( color1.b * 255 ), + c2r = ~~ ( color2.r * 255 ), c2g = ~~ ( color2.g * 255 ), c2b = ~~ ( color2.b * 255 ), + c3r = ~~ ( color3.r * 255 ), c3g = ~~ ( color3.g * 255 ), c3b = ~~ ( color3.b * 255 ), + c4r = ~~ ( color4.r * 255 ), c4g = ~~ ( color4.g * 255 ), c4b = ~~ ( color4.b * 255 ); + + _pixelMapData[ 0 ] = c1r < 0 ? 0 : c1r > 255 ? 255 : c1r; + _pixelMapData[ 1 ] = c1g < 0 ? 0 : c1g > 255 ? 255 : c1g; + _pixelMapData[ 2 ] = c1b < 0 ? 0 : c1b > 255 ? 255 : c1b; + + _pixelMapData[ 4 ] = c2r < 0 ? 0 : c2r > 255 ? 255 : c2r; + _pixelMapData[ 5 ] = c2g < 0 ? 0 : c2g > 255 ? 255 : c2g; + _pixelMapData[ 6 ] = c2b < 0 ? 0 : c2b > 255 ? 255 : c2b; + + _pixelMapData[ 8 ] = c3r < 0 ? 0 : c3r > 255 ? 255 : c3r; + _pixelMapData[ 9 ] = c3g < 0 ? 0 : c3g > 255 ? 255 : c3g; + _pixelMapData[ 10 ] = c3b < 0 ? 0 : c3b > 255 ? 255 : c3b; + + _pixelMapData[ 12 ] = c4r < 0 ? 0 : c4r > 255 ? 255 : c4r; + _pixelMapData[ 13 ] = c4g < 0 ? 0 : c4g > 255 ? 255 : c4g; + _pixelMapData[ 14 ] = c4b < 0 ? 0 : c4b > 255 ? 255 : c4b; + + _pixelMapContext.putImageData( _pixelMapImage, 0, 0 ); + _gradientMapContext.drawImage( _pixelMap, 0, 0 ); + + return _gradientMap; + + } + + function smoothstep( value, min, max ) { + + /* + if ( value <= min ) return 0; + if ( value >= max ) return 1; + */ + + var x = ( value - min ) / ( max - min ); + return x * x * ( 3 - 2 * x ); + + } + + function normalToComponent( normal ) { + + var component = ( normal + 1 ) * 0.5; + return component < 0 ? 0 : ( component > 1 ? 1 : component ); + + } + + // Hide anti-alias gaps + + function expand( v1, v2 ) { + + var x = v2.x - v1.x, y = v2.y - v1.y, + unit = 1 / Math.sqrt( x * x + y * y ); + + x *= unit; y *= unit; + + v2.x += x; v2.y += y; + v1.x -= x; v1.y -= y; + + } + + }; + + // Context cached methods. + + function setOpacity( value ) { + + if ( _contextGlobalAlpha != value ) { + + _context.globalAlpha = _contextGlobalAlpha = value; + + } + + } + + function setBlending( value ) { + + if ( _contextGlobalCompositeOperation != value ) { + + switch ( value ) { + + case THREE.NormalBlending: + + _context.globalCompositeOperation = 'source-over'; + + break; + + case THREE.AdditiveBlending: + + _context.globalCompositeOperation = 'lighter'; + + break; + + case THREE.SubtractiveBlending: + + _context.globalCompositeOperation = 'darker'; + + break; + + } + + _contextGlobalCompositeOperation = value; + + } + + } + + function setLineWidth( value ) { + + if ( _contextLineWidth != value ) { + + _context.lineWidth = _contextLineWidth = value; + + } + + } + + function setLineCap( value ) { + + // "butt", "round", "square" + + if ( _contextLineCap != value ) { + + _context.lineCap = _contextLineCap = value; + + } + + } + + function setLineJoin( value ) { + + // "round", "bevel", "miter" + + if ( _contextLineJoin != value ) { + + _context.lineJoin = _contextLineJoin = value; + + } + + } + + function setStrokeStyle( color ) { + + if ( _contextStrokeStyle != color.hex ) { + + _contextStrokeStyle = color.hex; + _context.strokeStyle = '#' + pad( _contextStrokeStyle.toString( 16 ) ); + + } + + } + + function setFillStyle( color ) { + + if ( _contextFillStyle != color.hex ) { + + _contextFillStyle = color.hex; + _context.fillStyle = '#' + pad( _contextFillStyle.toString( 16 ) ); + + } + + } + + function pad( str ) { + + while ( str.length < 6 ) str = '0' + str; + return str; + + } + +}; +/** + * @author mr.doob / http://mrdoob.com/ + */ + +THREE.SVGRenderer = function () { + + var _this = this, + _renderList = null, + _projector = new THREE.Projector(), + _svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'), + _svgWidth, _svgHeight, _svgWidthHalf, _svgHeightHalf, + + _v1, _v2, _v3, _v4, + + _clipRect = new THREE.Rectangle(), + _bboxRect = new THREE.Rectangle(), + + _enableLighting = false, + _color = new THREE.Color( 0xffffff ), + _light = new THREE.Color( 0xffffff ), + _ambientLight = new THREE.Color( 0x000000 ), + _directionalLights = new THREE.Color( 0x000000 ), + _pointLights = new THREE.Color( 0x000000 ), + + _w, // z-buffer to w-buffer + _vector3 = new THREE.Vector3(), // Needed for PointLight + + _svgPathPool = [], _svgCirclePool = [], _svgLinePool = [], + _svgNode, _pathCount, _circleCount, _lineCount, + _quality = 1; + + this.domElement = _svg; + + this.autoClear = true; + this.sortObjects = true; + this.sortElements = true; + + this.data = { + + vertices: 0, + faces: 0 + + } + + this.setQuality = function( quality ) { + + switch(quality) { + + case "high": _quality = 1; break; + case "low": _quality = 0; break; + + } + + }; + + this.setSize = function( width, height ) { + + _svgWidth = width; _svgHeight = height; + _svgWidthHalf = _svgWidth / 2; _svgHeightHalf = _svgHeight / 2; + + _svg.setAttribute( 'viewBox', ( - _svgWidthHalf ) + ' ' + ( - _svgHeightHalf ) + ' ' + _svgWidth + ' ' + _svgHeight ); + _svg.setAttribute( 'width', _svgWidth ); + _svg.setAttribute( 'height', _svgHeight ); + + _clipRect.set( - _svgWidthHalf, - _svgHeightHalf, _svgWidthHalf, _svgHeightHalf ); + + }; + + this.clear = function () { + + while ( _svg.childNodes.length > 0 ) { + + _svg.removeChild( _svg.childNodes[ 0 ] ); + + } + + }; + + this.render = function( scene, camera ) { + + var e, el, m, ml, fm, fml, element, material; + + this.autoClear && this.clear(); + + _this.data.vertices = 0; + _this.data.faces = 0; + + _renderList = _projector.projectScene( scene, camera, this.sortElements ); + + _pathCount = 0; _circleCount = 0; _lineCount = 0; + + _enableLighting = scene.lights.length > 0; + + if ( _enableLighting ) { + + calculateLights( scene ); + + } + + for ( e = 0, el = _renderList.length; e < el; e ++ ) { + + element = _renderList[ e ]; + + _bboxRect.empty(); + + if ( element instanceof THREE.RenderableParticle ) { + + _v1 = element; + _v1.x *= _svgWidthHalf; _v1.y *= -_svgHeightHalf; + + m = 0; ml = element.materials.length; + + while ( m < ml ) { + + material = element.materials[ m ++ ]; + material && renderParticle( _v1, element, material, scene ); + + } + + } else if ( element instanceof THREE.RenderableLine ) { + + _v1 = element.v1; _v2 = element.v2; + + _v1.positionScreen.x *= _svgWidthHalf; _v1.positionScreen.y *= - _svgHeightHalf; + _v2.positionScreen.x *= _svgWidthHalf; _v2.positionScreen.y *= - _svgHeightHalf; + + _bboxRect.addPoint( _v1.positionScreen.x, _v1.positionScreen.y ); + _bboxRect.addPoint( _v2.positionScreen.x, _v2.positionScreen.y ); + + if ( !_clipRect.instersects( _bboxRect ) ) { + + continue; + + } + + m = 0; ml = element.materials.length; + + while ( m < ml ) { + + material = element.materials[ m ++ ]; + material && material.opacity != 0 && renderLine( _v1, _v2, element, material, scene ); + + } + + } else if ( element instanceof THREE.RenderableFace3 ) { + + _v1 = element.v1; _v2 = element.v2; _v3 = element.v3; + + _v1.positionScreen.x *= _svgWidthHalf; _v1.positionScreen.y *= - _svgHeightHalf; + _v2.positionScreen.x *= _svgWidthHalf; _v2.positionScreen.y *= - _svgHeightHalf; + _v3.positionScreen.x *= _svgWidthHalf; _v3.positionScreen.y *= - _svgHeightHalf; + + _bboxRect.addPoint( _v1.positionScreen.x, _v1.positionScreen.y ); + _bboxRect.addPoint( _v2.positionScreen.x, _v2.positionScreen.y ); + _bboxRect.addPoint( _v3.positionScreen.x, _v3.positionScreen.y ); + + if ( !_clipRect.instersects( _bboxRect ) ) { + + continue; + + } + + m = 0; ml = element.meshMaterials.length; + + while ( m < ml ) { + + material = element.meshMaterials[ m ++ ]; + + if ( material instanceof THREE.MeshFaceMaterial ) { + + fm = 0; fml = element.faceMaterials.length; + + while ( fm < fml ) { + + material = element.faceMaterials[ fm ++ ]; + material && material.opacity != 0 && renderFace3( _v1, _v2, _v3, element, material, scene ); + + } + + continue; + + } + + material && material.opacity != 0 && renderFace3( _v1, _v2, _v3, element, material, scene ); + + } + + } else if ( element instanceof THREE.RenderableFace4 ) { + + _v1 = element.v1; _v2 = element.v2; _v3 = element.v3; _v4 = element.v4; + + _v1.positionScreen.x *= _svgWidthHalf; _v1.positionScreen.y *= -_svgHeightHalf; + _v2.positionScreen.x *= _svgWidthHalf; _v2.positionScreen.y *= -_svgHeightHalf; + _v3.positionScreen.x *= _svgWidthHalf; _v3.positionScreen.y *= -_svgHeightHalf; + _v4.positionScreen.x *= _svgWidthHalf; _v4.positionScreen.y *= -_svgHeightHalf; + + _bboxRect.addPoint( _v1.positionScreen.x, _v1.positionScreen.y ); + _bboxRect.addPoint( _v2.positionScreen.x, _v2.positionScreen.y ); + _bboxRect.addPoint( _v3.positionScreen.x, _v3.positionScreen.y ); + _bboxRect.addPoint( _v4.positionScreen.x, _v4.positionScreen.y ); + + if ( !_clipRect.instersects( _bboxRect) ) { + + continue; + + } + + m = 0; ml = element.meshMaterials.length; + + while ( m < ml ) { + + material = element.meshMaterials[ m ++ ]; + + if ( material instanceof THREE.MeshFaceMaterial ) { + + fm = 0; fml = element.faceMaterials.length; + + while ( fm < fml ) { + + material = element.faceMaterials[ fm ++ ]; + material && material.opacity != 0 && renderFace4( _v1, _v2, _v3, _v4, element, material, scene ); + + } + + continue; + + } + + material && material.opacity != 0 && renderFace4( _v1, _v2, _v3, _v4, element, material, scene ); + + } + + } + + } + + }; + + function calculateLights( scene ) { + + var l, ll, light, lightColor, + lights = scene.lights; + + _ambientLight.setRGB( 0, 0, 0 ); + _directionalLights.setRGB( 0, 0, 0 ); + _pointLights.setRGB( 0, 0, 0 ); + + for ( l = 0, ll = lights.length; l < ll; l++ ) { + + light = lights[ l ]; + lightColor = light.color; + + if ( light instanceof THREE.AmbientLight ) { + + _ambientLight.r += lightColor.r; + _ambientLight.g += lightColor.g; + _ambientLight.b += lightColor.b; + + } else if ( light instanceof THREE.DirectionalLight ) { + + _directionalLights.r += lightColor.r; + _directionalLights.g += lightColor.g; + _directionalLights.b += lightColor.b; + + } else if ( light instanceof THREE.PointLight ) { + + _pointLights.r += lightColor.r; + _pointLights.g += lightColor.g; + _pointLights.b += lightColor.b; + + } + + } + + } + + function calculateFaceLight( scene, element, color ) { + + var l, ll, light, amount; + + for ( l = 0, ll = scene.lights.length; l < ll; l++ ) { + + light = scene.lights[ l ]; + + if ( light instanceof THREE.DirectionalLight ) { + + amount = element.normalWorld.dot( light.position ) * light.intensity; + + if ( amount > 0 ) { + + color.r += light.color.r * amount; + color.g += light.color.g * amount; + color.b += light.color.b * amount; + + } + + } else if ( light instanceof THREE.PointLight ) { + + _vector3.sub( light.position, element.centroidWorld ); + _vector3.normalize(); + + amount = element.normalWorld.dot( _vector3 ) * light.intensity; + + if ( amount > 0 ) { + + color.r += light.color.r * amount; + color.g += light.color.g * amount; + color.b += light.color.b * amount; + + } + + } + + } + + } + + function renderParticle( v1, element, material, scene ) { + + /* + _svgNode = getCircleNode( _circleCount++ ); + _svgNode.setAttribute( 'cx', v1.x ); + _svgNode.setAttribute( 'cy', v1.y ); + _svgNode.setAttribute( 'r', element.scale.x * _svgWidthHalf ); + + if ( material instanceof THREE.ParticleCircleMaterial ) { + + if ( _enableLighting ) { + + _light.r = _ambientLight.r + _directionalLights.r + _pointLights.r; + _light.g = _ambientLight.g + _directionalLights.g + _pointLights.g; + _light.b = _ambientLight.b + _directionalLights.b + _pointLights.b; + + _color.r = material.color.r * _light.r; + _color.g = material.color.g * _light.g; + _color.b = material.color.b * _light.b; + + _color.updateStyleString(); + + } else { + + _color = material.color; + + } + + _svgNode.setAttribute( 'style', 'fill: ' + _color.__styleString ); + + } + + _svg.appendChild( _svgNode ); + */ + + } + + function renderLine ( v1, v2, element, material, scene ) { + + _svgNode = getLineNode( _lineCount ++ ); + + _svgNode.setAttribute( 'x1', v1.positionScreen.x ); + _svgNode.setAttribute( 'y1', v1.positionScreen.y ); + _svgNode.setAttribute( 'x2', v2.positionScreen.x ); + _svgNode.setAttribute( 'y2', v2.positionScreen.y ); + + if ( material instanceof THREE.LineBasicMaterial ) { + + _svgNode.setAttribute( 'style', 'fill: none; stroke: #' + '#' + pad( material.color.hex.toString( 16 ) ) + '; stroke-width: ' + material.linewidth + '; stroke-opacity: ' + material.opacity + '; stroke-linecap: ' + material.linecap + '; stroke-linejoin: ' + material.linejoin ); + + _svg.appendChild( _svgNode ); + + } + + } + + function renderFace3( v1, v2, v3, element, material, scene ) { + + _this.data.vertices += 3; + _this.data.faces ++; + + _svgNode = getPathNode( _pathCount ++ ); + _svgNode.setAttribute( 'd', 'M ' + v1.positionScreen.x + ' ' + v1.positionScreen.y + ' L ' + v2.positionScreen.x + ' ' + v2.positionScreen.y + ' L ' + v3.positionScreen.x + ',' + v3.positionScreen.y + 'z' ); + + if ( material instanceof THREE.MeshBasicMaterial ) { + + _color.hex = material.color.hex; + + } else if ( material instanceof THREE.MeshLambertMaterial ) { + + if ( _enableLighting ) { + + _light.r = _ambientLight.r; + _light.g = _ambientLight.g; + _light.b = _ambientLight.b; + + calculateFaceLight( scene, element, _light ); + + _color.r = Math.max( 0, Math.min( material.color.r * _light.r, 1 ) ); + _color.g = Math.max( 0, Math.min( material.color.g * _light.g, 1 ) ); + _color.b = Math.max( 0, Math.min( material.color.b * _light.b, 1 ) ); + + _color.updateHex(); + + } else { + + _color.hex = material.color.hex; + + } + + } else if ( material instanceof THREE.MeshDepthMaterial ) { + + _w = 1 - ( material.__2near / (material.__farPlusNear - element.z * material.__farMinusNear) ); + _color.setRGB( _w, _w, _w ); + + } else if ( material instanceof THREE.MeshNormalMaterial ) { + + _color.setRGB( normalToComponent( element.normalWorld.x ), normalToComponent( element.normalWorld.y ), normalToComponent( element.normalWorld.z ) ); + + } + + if ( material.wireframe ) { + + _svgNode.setAttribute( 'style', 'fill: none; stroke: #' + pad( _color.hex.toString( 16 ) ) + '; stroke-width: ' + material.wireframeLinewidth + '; stroke-opacity: ' + material.opacity + '; stroke-linecap: ' + material.wireframeLinecap + '; stroke-linejoin: ' + material.wireframeLinejoin ); + + } else { + + _svgNode.setAttribute( 'style', 'fill: #' + pad( _color.hex.toString( 16 ) ) + '; fill-opacity: ' + material.opacity ); + + } + + _svg.appendChild( _svgNode ); + + } + + function renderFace4( v1, v2, v3, v4, element, material, scene ) { + + _this.data.vertices += 4; + _this.data.faces ++; + + _svgNode = getPathNode( _pathCount ++ ); + _svgNode.setAttribute( 'd', 'M ' + v1.positionScreen.x + ' ' + v1.positionScreen.y + ' L ' + v2.positionScreen.x + ' ' + v2.positionScreen.y + ' L ' + v3.positionScreen.x + ',' + v3.positionScreen.y + ' L ' + v4.positionScreen.x + ',' + v4.positionScreen.y + 'z' ); + + if ( material instanceof THREE.MeshBasicMaterial ) { + + _color.hex = material.color.hex; + + } else if ( material instanceof THREE.MeshLambertMaterial ) { + + if ( _enableLighting ) { + + _light.r = _ambientLight.r; + _light.g = _ambientLight.g; + _light.b = _ambientLight.b; + + calculateFaceLight( scene, element, _light ); + + _color.r = Math.max( 0, Math.min( material.color.r * _light.r, 1 ) ); + _color.g = Math.max( 0, Math.min( material.color.g * _light.g, 1 ) ); + _color.b = Math.max( 0, Math.min( material.color.b * _light.b, 1 ) ); + + _color.updateHex(); + + } else { + + _color.hex = material.color.hex; + + } + + } else if ( material instanceof THREE.MeshDepthMaterial ) { + + _w = 1 - ( material.__2near / (material.__farPlusNear - element.z * material.__farMinusNear) ); + _color.setRGB( _w, _w, _w ); + + } else if ( material instanceof THREE.MeshNormalMaterial ) { + + _color.setRGB( normalToComponent( element.normalWorld.x ), normalToComponent( element.normalWorld.y ), normalToComponent( element.normalWorld.z ) ); + + } + + if ( material.wireframe ) { + + _svgNode.setAttribute( 'style', 'fill: none; stroke: #' + pad( _color.hex.toString( 16 ) ) + '; stroke-width: ' + material.wireframeLinewidth + '; stroke-opacity: ' + material.opacity + '; stroke-linecap: ' + material.wireframeLinecap + '; stroke-linejoin: ' + material.wireframeLinejoin ); + + } else { + + _svgNode.setAttribute( 'style', 'fill: #' + pad( _color.hex.toString( 16 ) ) + '; fill-opacity: ' + material.opacity ); + + } + + _svg.appendChild( _svgNode ); + + } + + function getLineNode( id ) { + + if ( _svgLinePool[ id ] == null ) { + + _svgLinePool[ id ] = document.createElementNS( 'http://www.w3.org/2000/svg', 'line' ); + + if ( _quality == 0 ) { + + _svgLinePool[ id ].setAttribute( 'shape-rendering', 'crispEdges' ); //optimizeSpeed + + } + + return _svgLinePool[ id ]; + + } + + return _svgLinePool[ id ]; + + } + + function getPathNode( id ) { + + if ( _svgPathPool[ id ] == null ) { + + _svgPathPool[ id ] = document.createElementNS( 'http://www.w3.org/2000/svg', 'path' ); + + if ( _quality == 0 ) { + + _svgPathPool[ id ].setAttribute( 'shape-rendering', 'crispEdges' ); //optimizeSpeed + + } + + return _svgPathPool[ id ]; + + } + + return _svgPathPool[ id ]; + + } + + function getCircleNode( id ) { + + if ( _svgCirclePool[id] == null ) { + + _svgCirclePool[ id ] = document.createElementNS( 'http://www.w3.org/2000/svg', 'circle' ); + + if ( _quality == 0 ) { + + _svgCirclePool[id].setAttribute( 'shape-rendering', 'crispEdges' ); //optimizeSpeed + + } + + return _svgCirclePool[ id ]; + + } + + return _svgCirclePool[ id ]; + + } + + function normalToComponent( normal ) { + + var component = ( normal + 1 ) * 0.5; + return component < 0 ? 0 : ( component > 1 ? 1 : component ); + + } + + function pad( str ) { + + while ( str.length < 6 ) str = '0' + str; + return str; + + } + +}; +/** + * @author alteredq / http://alteredqualia.com/ + * @author mrdoob / http://mrdoob.com/ + * @author mikael emtinger / http://gomo.se/ + */ + +THREE.ShaderChunk = { + + + // FOG + + fog_pars_fragment: [ + + "#ifdef USE_FOG", + + "uniform vec3 fogColor;", + + "#ifdef FOG_EXP2", + "uniform float fogDensity;", + "#else", + "uniform float fogNear;", + "uniform float fogFar;", + "#endif", + + "#endif" + + ].join("\n"), + + fog_fragment: [ + + "#ifdef USE_FOG", + + "float depth = gl_FragCoord.z / gl_FragCoord.w;", + + "#ifdef FOG_EXP2", + "const float LOG2 = 1.442695;", + "float fogFactor = exp2( - fogDensity * fogDensity * depth * depth * LOG2 );", + "fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );", + "#else", + "float fogFactor = smoothstep( fogNear, fogFar, depth );", + "#endif", + + "gl_FragColor = mix( gl_FragColor, vec4( fogColor, gl_FragColor.w ), fogFactor );", + + "#endif" + + ].join("\n"), + + // ENVIRONMENT MAP + + envmap_pars_fragment: [ + + "#ifdef USE_ENVMAP", + + "varying vec3 vReflect;", + "uniform float reflectivity;", + "uniform samplerCube envMap;", + "uniform int combine;", + + "#endif" + + ].join("\n"), + + envmap_fragment: [ + + "#ifdef USE_ENVMAP", + + "vec4 cubeColor = textureCube( envMap, vec3( -vReflect.x, vReflect.yz ) );", + + "if ( combine == 1 ) {", + + //"gl_FragColor = mix( gl_FragColor, cubeColor, reflectivity );", + "gl_FragColor = vec4( mix( gl_FragColor.xyz, cubeColor.xyz, reflectivity ), opacity );", + + "} else {", + + "gl_FragColor = gl_FragColor * cubeColor;", + + "}", + + "#endif" + + ].join("\n"), + + envmap_pars_vertex: [ + + "#ifdef USE_ENVMAP", + + "varying vec3 vReflect;", + "uniform float refractionRatio;", + "uniform bool useRefract;", + + "#endif" + + ].join("\n"), + + envmap_vertex : [ + + "#ifdef USE_ENVMAP", + + "vec4 mPosition = objectMatrix * vec4( position, 1.0 );", + "vec3 nWorld = mat3( objectMatrix[0].xyz, objectMatrix[1].xyz, objectMatrix[2].xyz ) * normal;", + + "if ( useRefract ) {", + + "vReflect = refract( normalize( mPosition.xyz - cameraPosition ), normalize( nWorld.xyz ), refractionRatio );", + + "} else {", + + "vReflect = reflect( normalize( mPosition.xyz - cameraPosition ), normalize( nWorld.xyz ) );", + + "}", + + "#endif" + + ].join("\n"), + + // COLOR MAP (particles) + + map_particle_pars_fragment: [ + + "#ifdef USE_MAP", + + "uniform sampler2D map;", + + "#endif" + + ].join("\n"), + + + map_particle_fragment: [ + + "#ifdef USE_MAP", + + "gl_FragColor = gl_FragColor * texture2D( map, gl_PointCoord );", + + "#endif" + + ].join("\n"), + + // COLOR MAP (triangles) + + map_pars_fragment: [ + + "#ifdef USE_MAP", + + "varying vec2 vUv;", + "uniform sampler2D map;", + + "#endif" + + ].join("\n"), + + map_pars_vertex: [ + + "#ifdef USE_MAP", + + "varying vec2 vUv;", + "uniform vec4 offsetRepeat;", + + "#endif" + + ].join("\n"), + + map_fragment: [ + + "#ifdef USE_MAP", + + "gl_FragColor = gl_FragColor * texture2D( map, vUv );", + + "#endif" + + ].join("\n"), + + map_vertex: [ + + "#ifdef USE_MAP", + + "vUv = uv * offsetRepeat.zw + offsetRepeat.xy;", + + "#endif" + + ].join("\n"), + + // LIGHT MAP + + lightmap_pars_fragment: [ + + "#ifdef USE_LIGHTMAP", + + "varying vec2 vUv2;", + "uniform sampler2D lightMap;", + + "#endif" + + ].join("\n"), + + lightmap_pars_vertex: [ + + "#ifdef USE_LIGHTMAP", + + "varying vec2 vUv2;", + + "#endif" + + ].join("\n"), + + lightmap_fragment: [ + + "#ifdef USE_LIGHTMAP", + + "gl_FragColor = gl_FragColor * texture2D( lightMap, vUv2 );", + + "#endif" + + ].join("\n"), + + lightmap_vertex: [ + + "#ifdef USE_LIGHTMAP", + + "vUv2 = uv2;", + + "#endif" + + ].join("\n"), + + lights_pars_vertex: [ + + "uniform bool enableLighting;", + "uniform vec3 ambientLightColor;", + + "#if MAX_DIR_LIGHTS > 0", + + "uniform vec3 directionalLightColor[ MAX_DIR_LIGHTS ];", + "uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];", + + "#endif", + + "#if MAX_POINT_LIGHTS > 0", + + "uniform vec3 pointLightColor[ MAX_POINT_LIGHTS ];", + "uniform vec3 pointLightPosition[ MAX_POINT_LIGHTS ];", + "uniform float pointLightDistance[ MAX_POINT_LIGHTS ];", + + "#ifdef PHONG", + "varying vec4 vPointLight[ MAX_POINT_LIGHTS ];", + "#endif", + + "#endif" + + ].join("\n"), + + // LIGHTS + + lights_vertex: [ + + "if ( !enableLighting ) {", + + "vLightWeighting = vec3( 1.0 );", + + "} else {", + + "vLightWeighting = ambientLightColor;", + + "#if MAX_DIR_LIGHTS > 0", + + "for( int i = 0; i < MAX_DIR_LIGHTS; i++ ) {", + + "vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );", + "float directionalLightWeighting = max( dot( transformedNormal, normalize( lDirection.xyz ) ), 0.0 );", + "vLightWeighting += directionalLightColor[ i ] * directionalLightWeighting;", + + "}", + + "#endif", + + "#if MAX_POINT_LIGHTS > 0", + + "for( int i = 0; i < MAX_POINT_LIGHTS; i++ ) {", + + "vec4 lPosition = viewMatrix * vec4( pointLightPosition[ i ], 1.0 );", + + "vec3 lVector = lPosition.xyz - mvPosition.xyz;", + + "float lDistance = 1.0;", + + "if ( pointLightDistance[ i ] > 0.0 )", + "lDistance = 1.0 - min( ( length( lVector ) / pointLightDistance[ i ] ), 1.0 );", + + "lVector = normalize( lVector );", + + "float pointLightWeighting = max( dot( transformedNormal, lVector ), 0.0 );", + "vLightWeighting += pointLightColor[ i ] * pointLightWeighting * lDistance;", + + "#ifdef PHONG", + "vPointLight[ i ] = vec4( lVector, lDistance );", + "#endif", + + "}", + + "#endif", + + "}" + + ].join("\n"), + + lights_pars_fragment: [ + + "#if MAX_DIR_LIGHTS > 0", + "uniform vec3 directionalLightDirection[ MAX_DIR_LIGHTS ];", + "#endif", + + "#if MAX_POINT_LIGHTS > 0", + "varying vec4 vPointLight[ MAX_POINT_LIGHTS ];", + "#endif", + + "varying vec3 vViewPosition;", + "varying vec3 vNormal;" + + ].join("\n"), + + lights_fragment: [ + + "vec3 normal = normalize( vNormal );", + "vec3 viewPosition = normalize( vViewPosition );", + + "vec4 mColor = vec4( diffuse, opacity );", + "vec4 mSpecular = vec4( specular, opacity );", + + "#if MAX_POINT_LIGHTS > 0", + + "vec4 pointDiffuse = vec4( 0.0 );", + "vec4 pointSpecular = vec4( 0.0 );", + + "for ( int i = 0; i < MAX_POINT_LIGHTS; i ++ ) {", + + "vec3 pointVector = normalize( vPointLight[ i ].xyz );", + "vec3 pointHalfVector = normalize( vPointLight[ i ].xyz + vViewPosition );", + "float pointDistance = vPointLight[ i ].w;", + + "float pointDotNormalHalf = dot( normal, pointHalfVector );", + "float pointDiffuseWeight = max( dot( normal, pointVector ), 0.0 );", + + "float pointSpecularWeight = 0.0;", + + "if ( pointDotNormalHalf >= 0.0 )", + "pointSpecularWeight = pow( pointDotNormalHalf, shininess );", + + "pointDiffuse += mColor * pointDiffuseWeight * pointDistance;", + "pointSpecular += mSpecular * pointSpecularWeight * pointDistance;", + + "}", + + "#endif", + + "#if MAX_DIR_LIGHTS > 0", + + "vec4 dirDiffuse = vec4( 0.0 );", + "vec4 dirSpecular = vec4( 0.0 );" , + + "for( int i = 0; i < MAX_DIR_LIGHTS; i++ ) {", + + "vec4 lDirection = viewMatrix * vec4( directionalLightDirection[ i ], 0.0 );", + + "vec3 dirVector = normalize( lDirection.xyz );", + "vec3 dirHalfVector = normalize( lDirection.xyz + vViewPosition );", + + "float dirDotNormalHalf = dot( normal, dirHalfVector );", + + "float dirDiffuseWeight = max( dot( normal, dirVector ), 0.0 );", + + "float dirSpecularWeight = 0.0;", + "if ( dirDotNormalHalf >= 0.0 )", + "dirSpecularWeight = pow( dirDotNormalHalf, shininess );", + + "dirDiffuse += mColor * dirDiffuseWeight;", + "dirSpecular += mSpecular * dirSpecularWeight;", + + "}", + + "#endif", + + "vec4 totalLight = vec4( ambient, opacity );", + + "#if MAX_DIR_LIGHTS > 0", + "totalLight += dirDiffuse + dirSpecular;", + "#endif", + + "#if MAX_POINT_LIGHTS > 0", + "totalLight += pointDiffuse + pointSpecular;", + "#endif", + + "gl_FragColor = gl_FragColor * totalLight;" + + ].join("\n"), + + // VERTEX COLORS + + color_pars_fragment: [ + + "#ifdef USE_COLOR", + + "varying vec3 vColor;", + + "#endif" + + ].join("\n"), + + + color_fragment: [ + + "#ifdef USE_COLOR", + + "gl_FragColor = gl_FragColor * vec4( vColor, opacity );", + + "#endif" + + ].join("\n"), + + color_pars_vertex: [ + + "#ifdef USE_COLOR", + + "varying vec3 vColor;", + + "#endif" + + ].join("\n"), + + + color_vertex: [ + + "#ifdef USE_COLOR", + + "vColor = color;", + + "#endif" + + ].join("\n"), + + // skinning + + skinning_pars_vertex: [ + + "#ifdef USE_SKINNING", + + "uniform mat4 boneGlobalMatrices[ MAX_BONES ];", + + "#endif" + + ].join("\n"), + + skinning_vertex: [ + + "#ifdef USE_SKINNING", + + "gl_Position = ( boneGlobalMatrices[ int( skinIndex.x ) ] * skinVertexA ) * skinWeight.x;", + "gl_Position += ( boneGlobalMatrices[ int( skinIndex.y ) ] * skinVertexB ) * skinWeight.y;", + + // this doesn't work, no idea why + //"gl_Position = projectionMatrix * cameraInverseMatrix * objectMatrix * gl_Position;", + + "gl_Position = projectionMatrix * viewMatrix * objectMatrix * gl_Position;", + + "#endif" + + ].join("\n"), + + // morphing + + morphtarget_pars_vertex: [ + + "#ifdef USE_MORPHTARGETS", + + "uniform float morphTargetInfluences[ 8 ];", + + "#endif" + + ].join("\n"), + + morphtarget_vertex: [ + + "#ifdef USE_MORPHTARGETS", + + "vec3 morphed = vec3( 0.0, 0.0, 0.0 );", + "morphed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];", + "morphed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];", + "morphed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];", + "morphed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];", + "morphed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];", + "morphed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];", + "morphed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];", + "morphed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];", + "morphed += position;", + + "gl_Position = projectionMatrix * modelViewMatrix * vec4( morphed, 1.0 );", + + "#endif" + + ].join("\n"), + + default_vertex : [ + + "#ifndef USE_MORPHTARGETS", + "#ifndef USE_SKINNING", + + "gl_Position = projectionMatrix * mvPosition;", + + "#endif", + "#endif" + + ].join("\n") + +}; + +THREE.UniformsUtils = { + + merge: function ( uniforms ) { + + var u, p, tmp, merged = {}; + + for ( u = 0; u < uniforms.length; u++ ) { + + tmp = this.clone( uniforms[ u ] ); + + for ( p in tmp ) { + + merged[ p ] = tmp[ p ]; + + } + + } + + return merged; + + }, + + clone: function ( uniforms_src ) { + + var u, p, parameter, parameter_src, uniforms_dst = {}; + + for ( u in uniforms_src ) { + + uniforms_dst[ u ] = {}; + + for ( p in uniforms_src[ u ] ) { + + parameter_src = uniforms_src[ u ][ p ]; + + if ( parameter_src instanceof THREE.Color || + parameter_src instanceof THREE.Vector3 || + parameter_src instanceof THREE.Texture ) { + + uniforms_dst[ u ][ p ] = parameter_src.clone(); + + } else { + + uniforms_dst[ u ][ p ] = parameter_src; + + } + + } + + } + + return uniforms_dst; + + } + +}; + +THREE.UniformsLib = { + + common: { + + "diffuse" : { type: "c", value: new THREE.Color( 0xeeeeee ) }, + "opacity" : { type: "f", value: 1.0 }, + + "map" : { type: "t", value: 0, texture: null }, + "offsetRepeat" : { type: "v4", value: new THREE.Vector4( 0, 0, 1, 1 ) }, + + "lightMap" : { type: "t", value: 2, texture: null }, + + "envMap" : { type: "t", value: 1, texture: null }, + "useRefract" : { type: "i", value: 0 }, + "reflectivity" : { type: "f", value: 1.0 }, + "refractionRatio" : { type: "f", value: 0.98 }, + "combine" : { type: "i", value: 0 }, + + "fogDensity" : { type: "f", value: 0.00025 }, + "fogNear" : { type: "f", value: 1 }, + "fogFar" : { type: "f", value: 2000 }, + "fogColor" : { type: "c", value: new THREE.Color( 0xffffff ) }, + + "morphTargetInfluences" : { type: "f", value: 0 } + + }, + + lights: { + + "enableLighting" : { type: "i", value: 1 }, + "ambientLightColor" : { type: "fv", value: [] }, + "directionalLightDirection" : { type: "fv", value: [] }, + "directionalLightColor" : { type: "fv", value: [] }, + "pointLightColor" : { type: "fv", value: [] }, + "pointLightPosition" : { type: "fv", value: [] }, + "pointLightDistance" : { type: "fv1", value: [] } + + }, + + particle: { + + "psColor" : { type: "c", value: new THREE.Color( 0xeeeeee ) }, + "opacity" : { type: "f", value: 1.0 }, + "size" : { type: "f", value: 1.0 }, + "scale" : { type: "f", value: 1.0 }, + "map" : { type: "t", value: 0, texture: null }, + + "fogDensity" : { type: "f", value: 0.00025 }, + "fogNear" : { type: "f", value: 1 }, + "fogFar" : { type: "f", value: 2000 }, + "fogColor" : { type: "c", value: new THREE.Color( 0xffffff ) } + + } + +}; + +THREE.ShaderLib = { + + 'lensFlareVertexTexture': { + + vertexShader: [ + + "uniform vec3 screenPosition;", + "uniform vec2 scale;", + "uniform float rotation;", + "uniform int renderType;", + + "uniform sampler2D occlusionMap;", + + "attribute vec2 position;", + "attribute vec2 UV;", + "varying vec2 vUV;", + "varying float vVisibility;", + + "void main(void)", + "{", + "vUV = UV;", + + "vec2 pos = position;", + + "if( renderType == 2 ) {", + + "vec4 visibility = texture2D( occlusionMap, vec2( 0.1, 0.1 )) +", + "texture2D( occlusionMap, vec2( 0.5, 0.1 )) +", + "texture2D( occlusionMap, vec2( 0.9, 0.1 )) +", + "texture2D( occlusionMap, vec2( 0.9, 0.5 )) +", + "texture2D( occlusionMap, vec2( 0.9, 0.9 )) +", + "texture2D( occlusionMap, vec2( 0.5, 0.9 )) +", + "texture2D( occlusionMap, vec2( 0.1, 0.9 )) +", + "texture2D( occlusionMap, vec2( 0.1, 0.5 )) +", + "texture2D( occlusionMap, vec2( 0.5, 0.5 ));", + + "vVisibility = ( visibility.r / 9.0 ) *", + "( 1.0 - visibility.g / 9.0 ) *", + "( visibility.b / 9.0 ) *", + "( 1.0 - visibility.a / 9.0 );", + + "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;", + "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;", + "}", + + "gl_Position = vec4(( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );", + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "#ifdef GL_ES", + "precision highp float;", + "#endif", + + "uniform sampler2D map;", + "uniform float opacity;", + "uniform int renderType;", + + "varying vec2 vUV;", + "varying float vVisibility;", + + "void main( void )", + "{", + // pink square + + "if( renderType == 0 ) {", + + "gl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );", + + // restore + + "} else if( renderType == 1 ) {", + + "gl_FragColor = texture2D( map, vUV );", + + // flare + + "} else {", + + "vec4 color = texture2D( map, vUV );", + "color.a *= opacity * vVisibility;", + "gl_FragColor = color;", + "}", + "}" + ].join( "\n" ) + + }, + + + 'lensFlare': { + + vertexShader: [ + + "uniform vec3 screenPosition;", + "uniform vec2 scale;", + "uniform float rotation;", + "uniform int renderType;", + + "attribute vec2 position;", + "attribute vec2 UV;", + + "varying vec2 vUV;", + + "void main(void)", + "{", + "vUV = UV;", + + "vec2 pos = position;", + + "if( renderType == 2 ) {", + + "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;", + "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;", + "}", + + "gl_Position = vec4(( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );", + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "#ifdef GL_ES", + "precision highp float;", + "#endif", + + "uniform sampler2D map;", + "uniform sampler2D occlusionMap;", + "uniform float opacity;", + "uniform int renderType;", + + "varying vec2 vUV;", + + "void main( void )", + "{", + // pink square + + "if( renderType == 0 ) {", + + "gl_FragColor = vec4( texture2D( map, vUV ).rgb, 0.0 );", + + // restore + + "} else if( renderType == 1 ) {", + + "gl_FragColor = texture2D( map, vUV );", + + // flare + + "} else {", + + "float visibility = texture2D( occlusionMap, vec2( 0.5, 0.1 )).a +", + "texture2D( occlusionMap, vec2( 0.9, 0.5 )).a +", + "texture2D( occlusionMap, vec2( 0.5, 0.9 )).a +", + "texture2D( occlusionMap, vec2( 0.1, 0.5 )).a;", + + "visibility = ( 1.0 - visibility / 4.0 );", + + "vec4 color = texture2D( map, vUV );", + "color.a *= opacity * visibility;", + "gl_FragColor = color;", + "}", + "}" + ].join( "\n" ) + + }, + + 'sprite': { + + vertexShader: [ + "uniform int useScreenCoordinates;", + "uniform int affectedByDistance;", + "uniform vec3 screenPosition;", + "uniform mat4 modelViewMatrix;", + "uniform mat4 projectionMatrix;", + "uniform float rotation;", + "uniform vec2 scale;", + "uniform vec2 alignment;", + "uniform vec2 uvOffset;", + "uniform vec2 uvScale;", + + "attribute vec2 position;", + "attribute vec2 uv;", + + "varying vec2 vUV;", + + "void main(void)", + "{", + "vUV = uvOffset + uv * uvScale;", + + "vec2 alignedPosition = position + alignment;", + + "vec2 rotatedPosition;", + "rotatedPosition.x = ( cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y ) * scale.x;", + "rotatedPosition.y = ( sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y ) * scale.y;", + + "vec4 finalPosition;", + + "if( useScreenCoordinates != 0 ) {", + + "finalPosition = vec4( screenPosition.xy + rotatedPosition, screenPosition.z, 1.0 );", + + "} else {", + + "finalPosition = projectionMatrix * modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );", + "finalPosition.xy += rotatedPosition * ( affectedByDistance == 1 ? 1.0 : finalPosition.z );", + + "}", + + "gl_Position = finalPosition;", + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "#ifdef GL_ES", + "precision highp float;", + "#endif", + + "uniform sampler2D map;", + "uniform float opacity;", + + "varying vec2 vUV;", + + "void main( void )", + "{", + "vec4 color = texture2D( map, vUV );", + "color.a *= opacity;", + "gl_FragColor = color;", +// "gl_FragColor = vec4( 1.0, 0.0, 1.0, 1.0 );", + "}" + ].join( "\n" ) + + }, + + + + 'shadowPost': { + + vertexShader: [ + + "uniform mat4 projectionMatrix;", + "attribute vec3 position;", + + "void main(void)", + "{", + "gl_Position = projectionMatrix * vec4( position, 1.0 );", + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "#ifdef GL_ES", + "precision highp float;", + "#endif", + + "uniform float darkness;", + + "void main( void )", + "{", + "gl_FragColor = vec4( 0, 0, 0, darkness );", + "}" + + ].join( "\n" ) + + }, + + + 'shadowVolumeDynamic': { + + uniforms: { "directionalLightDirection": { type: "fv", value: [] }}, + + vertexShader: [ + + "uniform vec3 directionalLightDirection;", + + "void main() {", + + "vec4 pos = objectMatrix * vec4( position, 1.0 );", + "vec3 norm = mat3( objectMatrix[0].xyz, objectMatrix[1].xyz, objectMatrix[2].xyz ) * normal;", + "vec4 extruded = vec4( directionalLightDirection * 5000.0 * step( 0.0, dot( directionalLightDirection, norm )), 0.0 );", + + "gl_Position = projectionMatrix * viewMatrix * ( pos + extruded );", + "}" + + ].join( "\n" ), + + fragmentShader: [ + + "void main() {", + + "gl_FragColor = vec4( 1.0 );", + + "}" + + ].join( "\n" ) + }, + + + 'depth': { + + uniforms: { "mNear": { type: "f", value: 1.0 }, + "mFar" : { type: "f", value: 2000.0 }, + "opacity" : { type: "f", value: 1.0 } + }, + + fragmentShader: [ + + "uniform float mNear;", + "uniform float mFar;", + "uniform float opacity;", + + "void main() {", + + "float depth = gl_FragCoord.z / gl_FragCoord.w;", + "float color = 1.0 - smoothstep( mNear, mFar, depth );", + "gl_FragColor = vec4( vec3( color ), opacity );", + + "}" + + ].join("\n"), + + vertexShader: [ + + "void main() {", + + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join("\n") + + }, + + 'normal': { + + uniforms: { "opacity" : { type: "f", value: 1.0 } }, + + fragmentShader: [ + + "uniform float opacity;", + "varying vec3 vNormal;", + + "void main() {", + + "gl_FragColor = vec4( 0.5 * normalize( vNormal ) + 0.5, opacity );", + + "}" + + ].join("\n"), + + vertexShader: [ + + "varying vec3 vNormal;", + + "void main() {", + + "vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", + "vNormal = normalize( normalMatrix * normal );", + + "gl_Position = projectionMatrix * mvPosition;", + + "}" + + ].join("\n") + + }, + + 'basic': { + + uniforms: THREE.UniformsLib[ "common" ], + + fragmentShader: [ + + "uniform vec3 diffuse;", + "uniform float opacity;", + + THREE.ShaderChunk[ "color_pars_fragment" ], + THREE.ShaderChunk[ "map_pars_fragment" ], + THREE.ShaderChunk[ "lightmap_pars_fragment" ], + THREE.ShaderChunk[ "envmap_pars_fragment" ], + THREE.ShaderChunk[ "fog_pars_fragment" ], + + "void main() {", + + "gl_FragColor = vec4( diffuse, opacity );", + + THREE.ShaderChunk[ "map_fragment" ], + THREE.ShaderChunk[ "lightmap_fragment" ], + THREE.ShaderChunk[ "color_fragment" ], + THREE.ShaderChunk[ "envmap_fragment" ], + THREE.ShaderChunk[ "fog_fragment" ], + + "}" + + ].join("\n"), + + vertexShader: [ + + THREE.ShaderChunk[ "map_pars_vertex" ], + THREE.ShaderChunk[ "lightmap_pars_vertex" ], + THREE.ShaderChunk[ "envmap_pars_vertex" ], + THREE.ShaderChunk[ "color_pars_vertex" ], + THREE.ShaderChunk[ "skinning_pars_vertex" ], + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + + "void main() {", + + "vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", + + THREE.ShaderChunk[ "map_vertex" ], + THREE.ShaderChunk[ "lightmap_vertex" ], + THREE.ShaderChunk[ "envmap_vertex" ], + THREE.ShaderChunk[ "color_vertex" ], + THREE.ShaderChunk[ "skinning_vertex" ], + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "default_vertex" ], + + "}" + + ].join("\n") + + }, + + 'lambert': { + + uniforms: THREE.UniformsUtils.merge( [ THREE.UniformsLib[ "common" ], THREE.UniformsLib[ "lights" ] ] ), + + fragmentShader: [ + + "uniform vec3 diffuse;", + "uniform float opacity;", + + "varying vec3 vLightWeighting;", + + THREE.ShaderChunk[ "color_pars_fragment" ], + THREE.ShaderChunk[ "map_pars_fragment" ], + THREE.ShaderChunk[ "lightmap_pars_fragment" ], + THREE.ShaderChunk[ "envmap_pars_fragment" ], + THREE.ShaderChunk[ "fog_pars_fragment" ], + + "void main() {", + + "gl_FragColor = vec4( diffuse, opacity );", + "gl_FragColor = gl_FragColor * vec4( vLightWeighting, 1.0 );", + + THREE.ShaderChunk[ "map_fragment" ], + THREE.ShaderChunk[ "lightmap_fragment" ], + THREE.ShaderChunk[ "color_fragment" ], + THREE.ShaderChunk[ "envmap_fragment" ], + THREE.ShaderChunk[ "fog_fragment" ], + + "}" + + ].join("\n"), + + vertexShader: [ + + "varying vec3 vLightWeighting;", + + THREE.ShaderChunk[ "map_pars_vertex" ], + THREE.ShaderChunk[ "lightmap_pars_vertex" ], + THREE.ShaderChunk[ "envmap_pars_vertex" ], + THREE.ShaderChunk[ "lights_pars_vertex" ], + THREE.ShaderChunk[ "color_pars_vertex" ], + THREE.ShaderChunk[ "skinning_pars_vertex" ], + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + + "void main() {", + + "vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", + + THREE.ShaderChunk[ "map_vertex" ], + THREE.ShaderChunk[ "lightmap_vertex" ], + THREE.ShaderChunk[ "envmap_vertex" ], + THREE.ShaderChunk[ "color_vertex" ], + + "vec3 transformedNormal = normalize( normalMatrix * normal );", + + THREE.ShaderChunk[ "lights_vertex" ], + THREE.ShaderChunk[ "skinning_vertex" ], + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "default_vertex" ], + + "}" + + ].join("\n") + + }, + + 'phong': { + + uniforms: THREE.UniformsUtils.merge( [ + + THREE.UniformsLib[ "common" ], + THREE.UniformsLib[ "lights" ], + { + "ambient" : { type: "c", value: new THREE.Color( 0x050505 ) }, + "specular" : { type: "c", value: new THREE.Color( 0x111111 ) }, + "shininess": { type: "f", value: 30 } + } + + ] ), + + fragmentShader: [ + + "uniform vec3 diffuse;", + "uniform float opacity;", + + "uniform vec3 ambient;", + "uniform vec3 specular;", + "uniform float shininess;", + + "varying vec3 vLightWeighting;", + + THREE.ShaderChunk[ "color_pars_fragment" ], + THREE.ShaderChunk[ "map_pars_fragment" ], + THREE.ShaderChunk[ "lightmap_pars_fragment" ], + THREE.ShaderChunk[ "envmap_pars_fragment" ], + THREE.ShaderChunk[ "fog_pars_fragment" ], + THREE.ShaderChunk[ "lights_pars_fragment" ], + + "void main() {", + + "gl_FragColor = vec4( vLightWeighting, 1.0 );", + THREE.ShaderChunk[ "lights_fragment" ], + + THREE.ShaderChunk[ "map_fragment" ], + THREE.ShaderChunk[ "lightmap_fragment" ], + THREE.ShaderChunk[ "color_fragment" ], + THREE.ShaderChunk[ "envmap_fragment" ], + THREE.ShaderChunk[ "fog_fragment" ], + + "}" + + ].join("\n"), + + vertexShader: [ + + "#define PHONG", + + "varying vec3 vLightWeighting;", + "varying vec3 vViewPosition;", + "varying vec3 vNormal;", + + THREE.ShaderChunk[ "map_pars_vertex" ], + THREE.ShaderChunk[ "lightmap_pars_vertex" ], + THREE.ShaderChunk[ "envmap_pars_vertex" ], + THREE.ShaderChunk[ "lights_pars_vertex" ], + THREE.ShaderChunk[ "color_pars_vertex" ], + THREE.ShaderChunk[ "skinning_pars_vertex" ], + THREE.ShaderChunk[ "morphtarget_pars_vertex" ], + + "void main() {", + + "vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", + + THREE.ShaderChunk[ "map_vertex" ], + THREE.ShaderChunk[ "lightmap_vertex" ], + THREE.ShaderChunk[ "envmap_vertex" ], + THREE.ShaderChunk[ "color_vertex" ], + + "#ifndef USE_ENVMAP", + "vec4 mPosition = objectMatrix * vec4( position, 1.0 );", + "#endif", + + "vViewPosition = cameraPosition - mPosition.xyz;", + + "vec3 transformedNormal = normalize( normalMatrix * normal );", + "vNormal = transformedNormal;", + + THREE.ShaderChunk[ "lights_vertex" ], + THREE.ShaderChunk[ "skinning_vertex" ], + THREE.ShaderChunk[ "morphtarget_vertex" ], + THREE.ShaderChunk[ "default_vertex" ], + + "}" + + ].join("\n") + + }, + + 'particle_basic': { + + uniforms: THREE.UniformsLib[ "particle" ], + + fragmentShader: [ + + "uniform vec3 psColor;", + "uniform float opacity;", + + THREE.ShaderChunk[ "color_pars_fragment" ], + THREE.ShaderChunk[ "map_particle_pars_fragment" ], + THREE.ShaderChunk[ "fog_pars_fragment" ], + + "void main() {", + + "gl_FragColor = vec4( psColor, opacity );", + + THREE.ShaderChunk[ "map_particle_fragment" ], + THREE.ShaderChunk[ "color_fragment" ], + THREE.ShaderChunk[ "fog_fragment" ], + + "}" + + ].join("\n"), + + vertexShader: [ + + "uniform float size;", + "uniform float scale;", + + THREE.ShaderChunk[ "color_pars_vertex" ], + + "void main() {", + + THREE.ShaderChunk[ "color_vertex" ], + + "vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", + + "#ifdef USE_SIZEATTENUATION", + "gl_PointSize = size * ( scale / length( mvPosition.xyz ) );", + "#else", + "gl_PointSize = size;", + "#endif", + + "gl_Position = projectionMatrix * mvPosition;", + + "}" + + ].join("\n") + + } + +}; +/** + * @author supereggbert / http://www.paulbrunt.co.uk/ + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author szimek / https://github.com/szimek/ + */ + +THREE.WebGLRenderer = function ( parameters ) { + + // Currently you can use just up to 4 directional / point lights total. + // Chrome barfs on shader linking when there are more than 4 lights :( + + // The problem comes from shader using too many varying vectors. + + // This is not GPU limitation as the same shader works ok in Firefox + // and Chrome with "--use-gl=desktop" flag. + + // Difference comes from Chrome on Windows using by default ANGLE, + // thus going DirectX9 route (while FF uses OpenGL). + + // See http://code.google.com/p/chromium/issues/detail?id=63491 + + var _this = this, + _gl, _programs = [], + _currentProgram = null, + _currentFramebuffer = null, + _currentDepthMask = true, + + // gl state cache + + _oldDoubleSided = null, + _oldFlipSided = null, + _oldBlending = null, + _oldDepth = null, + _oldPolygonOffset = null; + _oldPolygonOffsetFactor = null; + _oldPolygonOffsetUnits = null; + _cullEnabled = true, + + _viewportX = 0, + _viewportY = 0, + _viewportWidth = 0, + _viewportHeight = 0, + + // camera matrices caches + + _frustum = [ + new THREE.Vector4(), + new THREE.Vector4(), + new THREE.Vector4(), + new THREE.Vector4(), + new THREE.Vector4(), + new THREE.Vector4() + ], + + _projScreenMatrix = new THREE.Matrix4(), + _projectionMatrixArray = new Float32Array( 16 ), + _viewMatrixArray = new Float32Array( 16 ), + + _vector3 = new THREE.Vector4(), + + // light arrays cache + + _lights = { + + ambient: [ 0, 0, 0 ], + directional: { length: 0, colors: new Array(), positions: new Array() }, + point: { length: 0, colors: new Array(), positions: new Array(), distances: new Array() } + + }, + + // parameters + + parameters = parameters || {}, + + _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElement( 'canvas' ), + _stencil = parameters.stencil !== undefined ? parameters.stencil : true, + _antialias = parameters.antialias !== undefined ? parameters.antialias : false, + _clearColor = parameters.clearColor !== undefined ? new THREE.Color( parameters.clearColor ) : new THREE.Color( 0x000000 ), + _clearAlpha = parameters.clearAlpha !== undefined ? parameters.clearAlpha : 0; + + this.data = { + + vertices: 0, + faces: 0, + drawCalls: 0 + + }; + + this.maxMorphTargets = 8; + this.domElement = _canvas; + this.autoClear = true; + this.sortObjects = true; + + // Init GL + + try { + + if ( ! ( _gl = _canvas.getContext( 'experimental-webgl', { antialias: _antialias, stencil: _stencil } ) ) ) { + + throw 'Error creating WebGL context.'; + + } + + } catch ( error ) { + + console.error( error ); + + } + + console.log( + navigator.userAgent + " | " + + _gl.getParameter( _gl.VERSION ) + " | " + + _gl.getParameter( _gl.VENDOR ) + " | " + + _gl.getParameter( _gl.RENDERER ) + " | " + + _gl.getParameter( _gl.SHADING_LANGUAGE_VERSION ) + ); + + _gl.clearColor( 0, 0, 0, 1 ); + _gl.clearDepth( 1 ); + + _gl.enable( _gl.DEPTH_TEST ); + _gl.depthFunc( _gl.LEQUAL ); + + _gl.frontFace( _gl.CCW ); + _gl.cullFace( _gl.BACK ); + _gl.enable( _gl.CULL_FACE ); + + _gl.enable( _gl.BLEND ); + _gl.blendEquation( _gl.FUNC_ADD ); + _gl.blendFunc( _gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA ); + + _gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); + + // _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, true ); + + _cullEnabled = true; + + // + + this.context = _gl; + + var _supportsVertexTextures = ( maxVertexTextures() > 0 ); + + // prepare stencil shadow polygon + + if ( _stencil ) { + + var _stencilShadow = {}; + + _stencilShadow.vertices = new Float32Array( 12 ); + _stencilShadow.faces = new Uint16Array( 6 ); + _stencilShadow.darkness = 0.5; + + _stencilShadow.vertices[ 0 * 3 + 0 ] = -20; _stencilShadow.vertices[ 0 * 3 + 1 ] = -20; _stencilShadow.vertices[ 0 * 3 + 2 ] = -1; + _stencilShadow.vertices[ 1 * 3 + 0 ] = 20; _stencilShadow.vertices[ 1 * 3 + 1 ] = -20; _stencilShadow.vertices[ 1 * 3 + 2 ] = -1; + _stencilShadow.vertices[ 2 * 3 + 0 ] = 20; _stencilShadow.vertices[ 2 * 3 + 1 ] = 20; _stencilShadow.vertices[ 2 * 3 + 2 ] = -1; + _stencilShadow.vertices[ 3 * 3 + 0 ] = -20; _stencilShadow.vertices[ 3 * 3 + 1 ] = 20; _stencilShadow.vertices[ 3 * 3 + 2 ] = -1; + + _stencilShadow.faces[ 0 ] = 0; _stencilShadow.faces[ 1 ] = 1; _stencilShadow.faces[ 2 ] = 2; + _stencilShadow.faces[ 3 ] = 0; _stencilShadow.faces[ 4 ] = 2; _stencilShadow.faces[ 5 ] = 3; + + _stencilShadow.vertexBuffer = _gl.createBuffer(); + _stencilShadow.elementBuffer = _gl.createBuffer(); + + _gl.bindBuffer( _gl.ARRAY_BUFFER, _stencilShadow.vertexBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, _stencilShadow.vertices, _gl.STATIC_DRAW ); + + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _stencilShadow.elementBuffer ); + _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, _stencilShadow.faces, _gl.STATIC_DRAW ); + + _stencilShadow.program = _gl.createProgram(); + + _gl.attachShader( _stencilShadow.program, getShader( "fragment", THREE.ShaderLib.shadowPost.fragmentShader )); + _gl.attachShader( _stencilShadow.program, getShader( "vertex", THREE.ShaderLib.shadowPost.vertexShader )); + + _gl.linkProgram( _stencilShadow.program ); + + _stencilShadow.vertexLocation = _gl.getAttribLocation ( _stencilShadow.program, "position" ); + _stencilShadow.projectionLocation = _gl.getUniformLocation( _stencilShadow.program, "projectionMatrix" ); + _stencilShadow.darknessLocation = _gl.getUniformLocation( _stencilShadow.program, "darkness" ); + } + + + // prepare lens flare + + var _lensFlare = {}; + var i; + + _lensFlare.vertices = new Float32Array( 8 + 8 ); + _lensFlare.faces = new Uint16Array( 6 ); + + i = 0; + _lensFlare.vertices[ i++ ] = -1; _lensFlare.vertices[ i++ ] = -1; // vertex + _lensFlare.vertices[ i++ ] = 0; _lensFlare.vertices[ i++ ] = 0; // uv... etc. + _lensFlare.vertices[ i++ ] = 1; _lensFlare.vertices[ i++ ] = -1; + _lensFlare.vertices[ i++ ] = 1; _lensFlare.vertices[ i++ ] = 0; + _lensFlare.vertices[ i++ ] = 1; _lensFlare.vertices[ i++ ] = 1; + _lensFlare.vertices[ i++ ] = 1; _lensFlare.vertices[ i++ ] = 1; + _lensFlare.vertices[ i++ ] = -1; _lensFlare.vertices[ i++ ] = 1; + _lensFlare.vertices[ i++ ] = 0; _lensFlare.vertices[ i++ ] = 1; + + i = 0; + _lensFlare.faces[ i++ ] = 0; _lensFlare.faces[ i++ ] = 1; _lensFlare.faces[ i++ ] = 2; + _lensFlare.faces[ i++ ] = 0; _lensFlare.faces[ i++ ] = 2; _lensFlare.faces[ i++ ] = 3; + + _lensFlare.vertexBuffer = _gl.createBuffer(); + _lensFlare.elementBuffer = _gl.createBuffer(); + _lensFlare.tempTexture = _gl.createTexture(); + _lensFlare.occlusionTexture = _gl.createTexture(); + + _gl.bindBuffer( _gl.ARRAY_BUFFER, _lensFlare.vertexBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, _lensFlare.vertices, _gl.STATIC_DRAW ); + + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _lensFlare.elementBuffer ); + _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, _lensFlare.faces, _gl.STATIC_DRAW ); + + _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.tempTexture ); + _gl.texImage2D( _gl.TEXTURE_2D, 0, _gl.RGB, 16, 16, 0, _gl.RGB, _gl.UNSIGNED_BYTE, null ); + _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE ); + _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE ); + _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MAG_FILTER, _gl.NEAREST ); + _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MIN_FILTER, _gl.NEAREST ); + + _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.occlusionTexture ); + _gl.texImage2D( _gl.TEXTURE_2D, 0, _gl.RGBA, 16, 16, 0, _gl.RGBA, _gl.UNSIGNED_BYTE, null ); + _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE ); + _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE ); + _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MAG_FILTER, _gl.NEAREST ); + _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MIN_FILTER, _gl.NEAREST ); + + if( _gl.getParameter( _gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ) <= 0 ) { + + _lensFlare.hasVertexTexture = false; + + _lensFlare.program = _gl.createProgram(); + _gl.attachShader( _lensFlare.program, getShader( "fragment", THREE.ShaderLib.lensFlare.fragmentShader )); + _gl.attachShader( _lensFlare.program, getShader( "vertex", THREE.ShaderLib.lensFlare.vertexShader )); + _gl.linkProgram( _lensFlare.program ); + + + } else { + + _lensFlare.hasVertexTexture = true; + + _lensFlare.program = _gl.createProgram(); + _gl.attachShader( _lensFlare.program, getShader( "fragment", THREE.ShaderLib.lensFlareVertexTexture.fragmentShader )); + _gl.attachShader( _lensFlare.program, getShader( "vertex", THREE.ShaderLib.lensFlareVertexTexture.vertexShader )); + _gl.linkProgram( _lensFlare.program ); + + } + + _lensFlare.attributes = {}; + _lensFlare.uniforms = {}; + _lensFlare.attributes.vertex = _gl.getAttribLocation ( _lensFlare.program, "position" ); + _lensFlare.attributes.uv = _gl.getAttribLocation ( _lensFlare.program, "UV" ); + _lensFlare.uniforms.renderType = _gl.getUniformLocation( _lensFlare.program, "renderType" ); + _lensFlare.uniforms.map = _gl.getUniformLocation( _lensFlare.program, "map" ); + _lensFlare.uniforms.occlusionMap = _gl.getUniformLocation( _lensFlare.program, "occlusionMap" ); + _lensFlare.uniforms.opacity = _gl.getUniformLocation( _lensFlare.program, "opacity" ); + _lensFlare.uniforms.scale = _gl.getUniformLocation( _lensFlare.program, "scale" ); + _lensFlare.uniforms.rotation = _gl.getUniformLocation( _lensFlare.program, "rotation" ); + _lensFlare.uniforms.screenPosition = _gl.getUniformLocation( _lensFlare.program, "screenPosition" ); + + //_gl.enableVertexAttribArray( _lensFlare.attributes.vertex ); + //_gl.enableVertexAttribArray( _lensFlare.attributes.uv ); + + var _lensFlareAttributesEnabled = false; + + // prepare sprites + + _sprite = {}; + + _sprite.vertices = new Float32Array( 8 + 8 ); + _sprite.faces = new Uint16Array( 6 ); + + i = 0; + _sprite.vertices[ i++ ] = -1; _sprite.vertices[ i++ ] = -1; // vertex + _sprite.vertices[ i++ ] = 0; _sprite.vertices[ i++ ] = 0; // uv... etc. + _sprite.vertices[ i++ ] = 1; _sprite.vertices[ i++ ] = -1; + _sprite.vertices[ i++ ] = 1; _sprite.vertices[ i++ ] = 0; + _sprite.vertices[ i++ ] = 1; _sprite.vertices[ i++ ] = 1; + _sprite.vertices[ i++ ] = 1; _sprite.vertices[ i++ ] = 1; + _sprite.vertices[ i++ ] = -1; _sprite.vertices[ i++ ] = 1; + _sprite.vertices[ i++ ] = 0; _sprite.vertices[ i++ ] = 1; + + i = 0; + _sprite.faces[ i++ ] = 0; _sprite.faces[ i++ ] = 1; _sprite.faces[ i++ ] = 2; + _sprite.faces[ i++ ] = 0; _sprite.faces[ i++ ] = 2; _sprite.faces[ i++ ] = 3; + + _sprite.vertexBuffer = _gl.createBuffer(); + _sprite.elementBuffer = _gl.createBuffer(); + + _gl.bindBuffer( _gl.ARRAY_BUFFER, _sprite.vertexBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, _sprite.vertices, _gl.STATIC_DRAW ); + + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _sprite.elementBuffer ); + _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, _sprite.faces, _gl.STATIC_DRAW ); + + + _sprite.program = _gl.createProgram(); + _gl.attachShader( _sprite.program, getShader( "fragment", THREE.ShaderLib.sprite.fragmentShader )); + _gl.attachShader( _sprite.program, getShader( "vertex", THREE.ShaderLib.sprite.vertexShader )); + _gl.linkProgram( _sprite.program ); + + _sprite.attributes = {}; + _sprite.uniforms = {}; + _sprite.attributes.position = _gl.getAttribLocation ( _sprite.program, "position" ); + _sprite.attributes.uv = _gl.getAttribLocation ( _sprite.program, "uv" ); + _sprite.uniforms.uvOffset = _gl.getUniformLocation( _sprite.program, "uvOffset" ); + _sprite.uniforms.uvScale = _gl.getUniformLocation( _sprite.program, "uvScale" ); + _sprite.uniforms.rotation = _gl.getUniformLocation( _sprite.program, "rotation" ); + _sprite.uniforms.scale = _gl.getUniformLocation( _sprite.program, "scale" ); + _sprite.uniforms.alignment = _gl.getUniformLocation( _sprite.program, "alignment" ); + _sprite.uniforms.map = _gl.getUniformLocation( _sprite.program, "map" ); + _sprite.uniforms.opacity = _gl.getUniformLocation( _sprite.program, "opacity" ); + _sprite.uniforms.useScreenCoordinates = _gl.getUniformLocation( _sprite.program, "useScreenCoordinates" ); + _sprite.uniforms.affectedByDistance = _gl.getUniformLocation( _sprite.program, "affectedByDistance" ); + _sprite.uniforms.screenPosition = _gl.getUniformLocation( _sprite.program, "screenPosition" ); + _sprite.uniforms.modelViewMatrix = _gl.getUniformLocation( _sprite.program, "modelViewMatrix" ); + _sprite.uniforms.projectionMatrix = _gl.getUniformLocation( _sprite.program, "projectionMatrix" ); + + //_gl.enableVertexAttribArray( _sprite.attributes.position ); + //_gl.enableVertexAttribArray( _sprite.attributes.uv ); + + var _spriteAttributesEnabled = false; + + this.setSize = function ( width, height ) { + + _canvas.width = width; + _canvas.height = height; + + this.setViewport( 0, 0, _canvas.width, _canvas.height ); + + }; + + this.setViewport = function ( x, y, width, height ) { + + _viewportX = x; + _viewportY = y; + + _viewportWidth = width; + _viewportHeight = height; + + _gl.viewport( _viewportX, _viewportY, _viewportWidth, _viewportHeight ); + + }; + + this.setScissor = function ( x, y, width, height ) { + + _gl.scissor( x, y, width, height ); + + }; + + this.enableScissorTest = function ( enable ) { + + if ( enable ) + _gl.enable( _gl.SCISSOR_TEST ); + else + _gl.disable( _gl.SCISSOR_TEST ); + + }; + + this.enableDepthBufferWrite = function ( enable ) { + + _currentDepthMask = enable; + _gl.depthMask( enable ); + + }; + + this.setClearColorHex = function ( hex, alpha ) { + + _clearColor.setHex( hex ); + _clearAlpha = alpha; + + _gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); + + }; + + this.setClearColor = function ( color, alpha ) { + + _clearColor.copy( color ); + _clearAlpha = alpha; + + _gl.clearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); + + }; + + this.clear = function () { + + _gl.clear( _gl.COLOR_BUFFER_BIT | _gl.DEPTH_BUFFER_BIT | _gl.STENCIL_BUFFER_BIT ); + + }; + + this.setStencilShadowDarkness = function( value ) { + + _stencilShadow.darkness = value; + }; + + this.getContext = function() { + + return _gl; + + } + + + function setupLights ( program, lights ) { + + var l, ll, light, r = 0, g = 0, b = 0, + color, position, intensity, distance, + + zlights = _lights, + + dcolors = zlights.directional.colors, + dpositions = zlights.directional.positions, + + pcolors = zlights.point.colors, + ppositions = zlights.point.positions, + pdistances = zlights.point.distances, + + dlength = 0, + plength = 0, + + doffset = 0, + poffset = 0; + + for ( l = 0, ll = lights.length; l < ll; l++ ) { + + light = lights[ l ]; + color = light.color; + + position = light.position; + intensity = light.intensity; + distance = light.distance; + + if ( light instanceof THREE.AmbientLight ) { + + r += color.r; + g += color.g; + b += color.b; + + } else if ( light instanceof THREE.DirectionalLight ) { + + doffset = dlength * 3; + + dcolors[ doffset ] = color.r * intensity; + dcolors[ doffset + 1 ] = color.g * intensity; + dcolors[ doffset + 2 ] = color.b * intensity; + + dpositions[ doffset ] = position.x; + dpositions[ doffset + 1 ] = position.y; + dpositions[ doffset + 2 ] = position.z; + + dlength += 1; + + } else if( light instanceof THREE.PointLight ) { + + poffset = plength * 3; + + pcolors[ poffset ] = color.r * intensity; + pcolors[ poffset + 1 ] = color.g * intensity; + pcolors[ poffset + 2 ] = color.b * intensity; + + ppositions[ poffset ] = position.x; + ppositions[ poffset + 1 ] = position.y; + ppositions[ poffset + 2 ] = position.z; + + pdistances[ plength ] = distance; + + plength += 1; + + } + + } + + // null eventual remains from removed lights + // (this is to avoid if in shader) + + for( l = dlength * 3; l < dcolors.length; l++ ) dcolors[ l ] = 0.0; + for( l = plength * 3; l < pcolors.length; l++ ) pcolors[ l ] = 0.0; + + zlights.point.length = plength; + zlights.directional.length = dlength; + + zlights.ambient[ 0 ] = r; + zlights.ambient[ 1 ] = g; + zlights.ambient[ 2 ] = b; + + }; + + function createParticleBuffers ( geometry ) { + + geometry.__webglVertexBuffer = _gl.createBuffer(); + geometry.__webglColorBuffer = _gl.createBuffer(); + + }; + + function createLineBuffers( geometry ) { + + geometry.__webglVertexBuffer = _gl.createBuffer(); + geometry.__webglColorBuffer = _gl.createBuffer(); + + }; + + function createRibbonBuffers( geometry ) { + + geometry.__webglVertexBuffer = _gl.createBuffer(); + geometry.__webglColorBuffer = _gl.createBuffer(); + + }; + + function createMeshBuffers( geometryGroup ) { + + geometryGroup.__webglVertexBuffer = _gl.createBuffer(); + geometryGroup.__webglNormalBuffer = _gl.createBuffer(); + geometryGroup.__webglTangentBuffer = _gl.createBuffer(); + geometryGroup.__webglColorBuffer = _gl.createBuffer(); + geometryGroup.__webglUVBuffer = _gl.createBuffer(); + geometryGroup.__webglUV2Buffer = _gl.createBuffer(); + + geometryGroup.__webglSkinVertexABuffer = _gl.createBuffer(); + geometryGroup.__webglSkinVertexBBuffer = _gl.createBuffer(); + geometryGroup.__webglSkinIndicesBuffer = _gl.createBuffer(); + geometryGroup.__webglSkinWeightsBuffer = _gl.createBuffer(); + + geometryGroup.__webglFaceBuffer = _gl.createBuffer(); + geometryGroup.__webglLineBuffer = _gl.createBuffer(); + + if ( geometryGroup.numMorphTargets ) { + + var m, ml; + geometryGroup.__webglMorphTargetsBuffers = []; + + for ( m = 0, ml = geometryGroup.numMorphTargets; m < ml; m++ ) { + + geometryGroup.__webglMorphTargetsBuffers.push( _gl.createBuffer() ); + + } + + } + + }; + + function initLineBuffers ( geometry ) { + + var nvertices = geometry.vertices.length; + + geometry.__vertexArray = new Float32Array( nvertices * 3 ); + geometry.__colorArray = new Float32Array( nvertices * 3 ); + + geometry.__webglLineCount = nvertices; + + }; + + function initRibbonBuffers ( geometry ) { + + var nvertices = geometry.vertices.length; + + geometry.__vertexArray = new Float32Array( nvertices * 3 ); + geometry.__colorArray = new Float32Array( nvertices * 3 ); + + geometry.__webglVertexCount = nvertices; + + }; + + function initParticleBuffers ( geometry ) { + + var nvertices = geometry.vertices.length; + + geometry.__vertexArray = new Float32Array( nvertices * 3 ); + geometry.__colorArray = new Float32Array( nvertices * 3 ); + + geometry.__sortArray = []; + + geometry.__webglParticleCount = nvertices; + + }; + + function initMeshBuffers ( geometryGroup, object ) { + + var f, fl, fi, face, + m, ml, size, + nvertices = 0, ntris = 0, nlines = 0, + + uvType, + vertexColorType, + normalType, + materials, material, + attribute, property, originalAttribute, + + geometry = object.geometry, + obj_faces = geometry.faces, + chunk_faces = geometryGroup.faces; + + for ( f = 0, fl = chunk_faces.length; f < fl; f++ ) { + + fi = chunk_faces[ f ]; + face = obj_faces[ fi ]; + + if ( face instanceof THREE.Face3 ) { + + nvertices += 3; + ntris += 1; + nlines += 3; + + } else if ( face instanceof THREE.Face4 ) { + + nvertices += 4; + ntris += 2; + nlines += 4; + + } + + } + + materials = unrollGroupMaterials( geometryGroup, object ); + + // this will not work if materials would change in run-time + // it should be refreshed every frame + // but need to do unrollGroupMaterials + // more properly without push to array + // like unrollBufferMaterials + + geometryGroup.__materials = materials; + + uvType = bufferGuessUVType( materials, geometryGroup, object ); + normalType = bufferGuessNormalType( materials, geometryGroup, object ); + vertexColorType = bufferGuessVertexColorType( materials, geometryGroup, object ); + + //console.log("uvType",uvType, "normalType",normalType, "vertexColorType",vertexColorType, object, geometryGroup, materials ); + + geometryGroup.__vertexArray = new Float32Array( nvertices * 3 ); + + if ( normalType ) { + + geometryGroup.__normalArray = new Float32Array( nvertices * 3 ); + + } + + if ( geometry.hasTangents ) { + + geometryGroup.__tangentArray = new Float32Array( nvertices * 4 ); + + } + + if ( vertexColorType ) { + + geometryGroup.__colorArray = new Float32Array( nvertices * 3 ); + + } + + if ( uvType ) { + + if ( geometry.faceUvs.length > 0 || geometry.faceVertexUvs.length > 0 ) { + + geometryGroup.__uvArray = new Float32Array( nvertices * 2 ); + + } + + if ( geometry.faceUvs.length > 1 || geometry.faceVertexUvs.length > 1 ) { + + geometryGroup.__uv2Array = new Float32Array( nvertices * 2 ); + + } + + } + + if ( object.geometry.skinWeights.length && object.geometry.skinIndices.length ) { + + geometryGroup.__skinVertexAArray = new Float32Array( nvertices * 4 ); + geometryGroup.__skinVertexBArray = new Float32Array( nvertices * 4 ); + geometryGroup.__skinIndexArray = new Float32Array( nvertices * 4 ); + geometryGroup.__skinWeightArray = new Float32Array( nvertices * 4 ); + + } + + geometryGroup.__faceArray = new Uint16Array( ntris * 3 + ( object.geometry.edgeFaces ? object.geometry.edgeFaces.length * 2 * 3 : 0 )); + geometryGroup.__lineArray = new Uint16Array( nlines * 2 ); + + if ( geometryGroup.numMorphTargets ) { + + geometryGroup.__morphTargetsArrays = []; + + for ( m = 0, ml = geometryGroup.numMorphTargets; m < ml; m++ ) { + + geometryGroup.__morphTargetsArrays.push( new Float32Array( nvertices * 3 ) ); + + } + + } + + geometryGroup.__needsSmoothNormals = ( normalType == THREE.SmoothShading ); + + geometryGroup.__uvType = uvType; + geometryGroup.__vertexColorType = vertexColorType; + geometryGroup.__normalType = normalType; + + geometryGroup.__webglFaceCount = ntris * 3 + ( object.geometry.edgeFaces ? object.geometry.edgeFaces.length * 2 * 3 : 0 ); + geometryGroup.__webglLineCount = nlines * 2; + + + // custom attributes + + for ( m = 0, ml = materials.length; m < ml; m ++ ) { + + material = materials[ m ]; + + if ( material.attributes ) { + + geometryGroup.__webglCustomAttributes = {}; + + for ( a in material.attributes ) { + + // Do a shallow copy of the attribute object so different geometryGroup chunks use different + // attribute buffers which are correctly indexed in the setMeshBuffers function + + originalAttribute = material.attributes[ a ]; + + attribute = {}; + + for ( property in originalAttribute ) { + + attribute[ property ] = originalAttribute[ property ]; + + } + + if( !attribute.__webglInitialized || attribute.createUniqueBuffers ) { + + attribute.__webglInitialized = true; + + size = 1; // "f" and "i" + + if( attribute.type === "v2" ) size = 2; + else if( attribute.type === "v3" ) size = 3; + else if( attribute.type === "v4" ) size = 4; + else if( attribute.type === "c" ) size = 3; + + attribute.size = size; + attribute.array = new Float32Array( nvertices * size ); + attribute.buffer = _gl.createBuffer(); + attribute.buffer.belongsToAttribute = a; + + originalAttribute.needsUpdate = true; + attribute.__original = originalAttribute; + + } + + geometryGroup.__webglCustomAttributes[ a ] = attribute; + + } + + } + + } + + geometryGroup.__inittedArrays = true; + + }; + + + function setMeshBuffers ( geometryGroup, object, hint ) { + + if ( ! geometryGroup.__inittedArrays ) { + + // console.log( object ); + return; + + } + + var f, fl, fi, face, + vertexNormals, faceNormal, normal, + vertexColors, faceColor, + vertexTangents, + uvType, vertexColorType, normalType, + uv, uv2, v1, v2, v3, v4, t1, t2, t3, t4, + c1, c2, c3, c4, + sw1, sw2, sw3, sw4, + si1, si2, si3, si4, + sa1, sa2, sa3, sa4, + sb1, sb2, sb3, sb4, + m, ml, i, + vn, uvi, uv2i, + vk, vkl, vka, + a, + + vertexIndex = 0, + + offset = 0, + offset_uv = 0, + offset_uv2 = 0, + offset_face = 0, + offset_normal = 0, + offset_tangent = 0, + offset_line = 0, + offset_color = 0, + offset_skin = 0, + offset_morphTarget = 0, + offset_custom = 0, + offset_customSrc = 0, + + vertexArray = geometryGroup.__vertexArray, + uvArray = geometryGroup.__uvArray, + uv2Array = geometryGroup.__uv2Array, + normalArray = geometryGroup.__normalArray, + tangentArray = geometryGroup.__tangentArray, + colorArray = geometryGroup.__colorArray, + + skinVertexAArray = geometryGroup.__skinVertexAArray, + skinVertexBArray = geometryGroup.__skinVertexBArray, + skinIndexArray = geometryGroup.__skinIndexArray, + skinWeightArray = geometryGroup.__skinWeightArray, + + morphTargetsArrays = geometryGroup.__morphTargetsArrays, + + customAttributes = geometryGroup.__webglCustomAttributes, + customAttribute, + + faceArray = geometryGroup.__faceArray, + lineArray = geometryGroup.__lineArray, + + needsSmoothNormals = geometryGroup.__needsSmoothNormals, + + vertexColorType = geometryGroup.__vertexColorType, + uvType = geometryGroup.__uvType, + normalType = geometryGroup.__normalType, + + geometry = object.geometry, // this is shared for all chunks + + dirtyVertices = geometry.__dirtyVertices, + dirtyElements = geometry.__dirtyElements, + dirtyUvs = geometry.__dirtyUvs, + dirtyNormals = geometry.__dirtyNormals, + dirtyTangents = geometry.__dirtyTangents, + dirtyColors = geometry.__dirtyColors, + dirtyMorphTargets = geometry.__dirtyMorphTargets, + + vertices = geometry.vertices, + chunk_faces = geometryGroup.faces, + obj_faces = geometry.faces, + + obj_uvs = geometry.faceVertexUvs[ 0 ], + obj_uvs2 = geometry.faceVertexUvs[ 1 ], + + obj_colors = geometry.colors, + + obj_skinVerticesA = geometry.skinVerticesA, + obj_skinVerticesB = geometry.skinVerticesB, + obj_skinIndices = geometry.skinIndices, + obj_skinWeights = geometry.skinWeights, + obj_edgeFaces = object instanceof THREE.ShadowVolume ? geometry.edgeFaces : undefined, + + morphTargets = geometry.morphTargets; + + if ( customAttributes ) { + + for ( a in customAttributes ) { + + customAttributes[ a ].offset = 0; + customAttributes[ a ].offsetSrc = 0; + + } + + } + + + for ( f = 0, fl = chunk_faces.length; f < fl; f ++ ) { + + fi = chunk_faces[ f ]; + face = obj_faces[ fi ]; + + if ( obj_uvs ) { + + uv = obj_uvs[ fi ]; + + } + + if ( obj_uvs2 ) { + + uv2 = obj_uvs2[ fi ]; + + } + + vertexNormals = face.vertexNormals; + faceNormal = face.normal; + + vertexColors = face.vertexColors; + faceColor = face.color; + + vertexTangents = face.vertexTangents; + + if ( face instanceof THREE.Face3 ) { + + if ( dirtyVertices ) { + + v1 = vertices[ face.a ].position; + v2 = vertices[ face.b ].position; + v3 = vertices[ face.c ].position; + + vertexArray[ offset ] = v1.x; + vertexArray[ offset + 1 ] = v1.y; + vertexArray[ offset + 2 ] = v1.z; + + vertexArray[ offset + 3 ] = v2.x; + vertexArray[ offset + 4 ] = v2.y; + vertexArray[ offset + 5 ] = v2.z; + + vertexArray[ offset + 6 ] = v3.x; + vertexArray[ offset + 7 ] = v3.y; + vertexArray[ offset + 8 ] = v3.z; + + offset += 9; + + } + + if ( customAttributes ) { + + for ( a in customAttributes ) { + + customAttribute = customAttributes[ a ]; + + if ( customAttribute.__original.needsUpdate ) { + + offset_custom = customAttribute.offset; + offset_customSrc = customAttribute.offsetSrc; + + if ( customAttribute.size === 1 ) { + + if ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) { + + customAttribute.array[ offset_custom + 0 ] = customAttribute.value[ face.a ]; + customAttribute.array[ offset_custom + 1 ] = customAttribute.value[ face.b ]; + customAttribute.array[ offset_custom + 2 ] = customAttribute.value[ face.c ]; + + } else if ( customAttribute.boundTo === "faces" ) { + + customAttribute.array[ offset_custom + 0 ] = customAttribute.value[ offset_customSrc ]; + customAttribute.array[ offset_custom + 1 ] = customAttribute.value[ offset_customSrc ]; + customAttribute.array[ offset_custom + 2 ] = customAttribute.value[ offset_customSrc ]; + + customAttribute.offsetSrc ++; + + } else if ( customAttribute.boundTo === "faceVertices" ) { + + customAttribute.array[ offset_custom + 0 ] = customAttribute.value[ offset_customSrc + 0 ]; + customAttribute.array[ offset_custom + 1 ] = customAttribute.value[ offset_customSrc + 1 ]; + customAttribute.array[ offset_custom + 2 ] = customAttribute.value[ offset_customSrc + 2 ]; + + customAttribute.offsetSrc += 3; + + } + + customAttribute.offset += 3; + + } else { + + if ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) { + + v1 = customAttribute.value[ face.a ]; + v2 = customAttribute.value[ face.b ]; + v3 = customAttribute.value[ face.c ]; + + } else if ( customAttribute.boundTo === "faces" ) { + + v1 = customAttribute.value[ offset_customSrc ]; + v2 = customAttribute.value[ offset_customSrc ]; + v3 = customAttribute.value[ offset_customSrc ]; + + customAttribute.offsetSrc ++; + + } else if ( customAttribute.boundTo === "faceVertices" ) { + + v1 = customAttribute.value[ offset_customSrc + 0 ]; + v2 = customAttribute.value[ offset_customSrc + 1 ]; + v3 = customAttribute.value[ offset_customSrc + 2 ]; + + customAttribute.offsetSrc += 3; + } + + + if ( customAttribute.size === 2 ) { + + customAttribute.array[ offset_custom + 0 ] = v1.x; + customAttribute.array[ offset_custom + 1 ] = v1.y; + + customAttribute.array[ offset_custom + 2 ] = v2.x; + customAttribute.array[ offset_custom + 3 ] = v2.y; + + customAttribute.array[ offset_custom + 4 ] = v3.x; + customAttribute.array[ offset_custom + 5 ] = v3.y; + + customAttribute.offset += 6; + + } else if ( customAttribute.size === 3 ) { + + if ( customAttribute.type === "c" ) { + + customAttribute.array[ offset_custom + 0 ] = v1.r; + customAttribute.array[ offset_custom + 1 ] = v1.g; + customAttribute.array[ offset_custom + 2 ] = v1.b; + + customAttribute.array[ offset_custom + 3 ] = v2.r; + customAttribute.array[ offset_custom + 4 ] = v2.g; + customAttribute.array[ offset_custom + 5 ] = v2.b; + + customAttribute.array[ offset_custom + 6 ] = v3.r; + customAttribute.array[ offset_custom + 7 ] = v3.g; + customAttribute.array[ offset_custom + 8 ] = v3.b; + + } else { + + customAttribute.array[ offset_custom + 0 ] = v1.x; + customAttribute.array[ offset_custom + 1 ] = v1.y; + customAttribute.array[ offset_custom + 2 ] = v1.z; + + customAttribute.array[ offset_custom + 3 ] = v2.x; + customAttribute.array[ offset_custom + 4 ] = v2.y; + customAttribute.array[ offset_custom + 5 ] = v2.z; + + customAttribute.array[ offset_custom + 6 ] = v3.x; + customAttribute.array[ offset_custom + 7 ] = v3.y; + customAttribute.array[ offset_custom + 8 ] = v3.z; + + } + + customAttribute.offset += 9; + + } else { + + customAttribute.array[ offset_custom + 0 ] = v1.x; + customAttribute.array[ offset_custom + 1 ] = v1.y; + customAttribute.array[ offset_custom + 2 ] = v1.z; + customAttribute.array[ offset_custom + 3 ] = v1.w; + + customAttribute.array[ offset_custom + 4 ] = v2.x; + customAttribute.array[ offset_custom + 5 ] = v2.y; + customAttribute.array[ offset_custom + 6 ] = v2.z; + customAttribute.array[ offset_custom + 7 ] = v2.w; + + customAttribute.array[ offset_custom + 8 ] = v3.x; + customAttribute.array[ offset_custom + 9 ] = v3.y; + customAttribute.array[ offset_custom + 10 ] = v3.z; + customAttribute.array[ offset_custom + 11 ] = v3.w; + + customAttribute.offset += 12; + + } + + } + + } + + } + + } + + + if ( dirtyMorphTargets ) { + + for ( vk = 0, vkl = morphTargets.length; vk < vkl; vk ++ ) { + + v1 = morphTargets[ vk ].vertices[ face.a ].position; + v2 = morphTargets[ vk ].vertices[ face.b ].position; + v3 = morphTargets[ vk ].vertices[ face.c ].position; + + vka = morphTargetsArrays[ vk ]; + + vka[ offset_morphTarget + 0 ] = v1.x; + vka[ offset_morphTarget + 1 ] = v1.y; + vka[ offset_morphTarget + 2 ] = v1.z; + + vka[ offset_morphTarget + 3 ] = v2.x; + vka[ offset_morphTarget + 4 ] = v2.y; + vka[ offset_morphTarget + 5 ] = v2.z; + + vka[ offset_morphTarget + 6 ] = v3.x; + vka[ offset_morphTarget + 7 ] = v3.y; + vka[ offset_morphTarget + 8 ] = v3.z; + } + + offset_morphTarget += 9; + + } + + if ( obj_skinWeights.length ) { + + // weights + + sw1 = obj_skinWeights[ face.a ]; + sw2 = obj_skinWeights[ face.b ]; + sw3 = obj_skinWeights[ face.c ]; + + skinWeightArray[ offset_skin ] = sw1.x; + skinWeightArray[ offset_skin + 1 ] = sw1.y; + skinWeightArray[ offset_skin + 2 ] = sw1.z; + skinWeightArray[ offset_skin + 3 ] = sw1.w; + + skinWeightArray[ offset_skin + 4 ] = sw2.x; + skinWeightArray[ offset_skin + 5 ] = sw2.y; + skinWeightArray[ offset_skin + 6 ] = sw2.z; + skinWeightArray[ offset_skin + 7 ] = sw2.w; + + skinWeightArray[ offset_skin + 8 ] = sw3.x; + skinWeightArray[ offset_skin + 9 ] = sw3.y; + skinWeightArray[ offset_skin + 10 ] = sw3.z; + skinWeightArray[ offset_skin + 11 ] = sw3.w; + + // indices + + si1 = obj_skinIndices[ face.a ]; + si2 = obj_skinIndices[ face.b ]; + si3 = obj_skinIndices[ face.c ]; + + skinIndexArray[ offset_skin ] = si1.x; + skinIndexArray[ offset_skin + 1 ] = si1.y; + skinIndexArray[ offset_skin + 2 ] = si1.z; + skinIndexArray[ offset_skin + 3 ] = si1.w; + + skinIndexArray[ offset_skin + 4 ] = si2.x; + skinIndexArray[ offset_skin + 5 ] = si2.y; + skinIndexArray[ offset_skin + 6 ] = si2.z; + skinIndexArray[ offset_skin + 7 ] = si2.w; + + skinIndexArray[ offset_skin + 8 ] = si3.x; + skinIndexArray[ offset_skin + 9 ] = si3.y; + skinIndexArray[ offset_skin + 10 ] = si3.z; + skinIndexArray[ offset_skin + 11 ] = si3.w; + + // vertices A + + sa1 = obj_skinVerticesA[ face.a ]; + sa2 = obj_skinVerticesA[ face.b ]; + sa3 = obj_skinVerticesA[ face.c ]; + + skinVertexAArray[ offset_skin ] = sa1.x; + skinVertexAArray[ offset_skin + 1 ] = sa1.y; + skinVertexAArray[ offset_skin + 2 ] = sa1.z; + skinVertexAArray[ offset_skin + 3 ] = 1; // pad for faster vertex shader + + skinVertexAArray[ offset_skin + 4 ] = sa2.x; + skinVertexAArray[ offset_skin + 5 ] = sa2.y; + skinVertexAArray[ offset_skin + 6 ] = sa2.z; + skinVertexAArray[ offset_skin + 7 ] = 1; + + skinVertexAArray[ offset_skin + 8 ] = sa3.x; + skinVertexAArray[ offset_skin + 9 ] = sa3.y; + skinVertexAArray[ offset_skin + 10 ] = sa3.z; + skinVertexAArray[ offset_skin + 11 ] = 1; + + // vertices B + + sb1 = obj_skinVerticesB[ face.a ]; + sb2 = obj_skinVerticesB[ face.b ]; + sb3 = obj_skinVerticesB[ face.c ]; + + skinVertexBArray[ offset_skin ] = sb1.x; + skinVertexBArray[ offset_skin + 1 ] = sb1.y; + skinVertexBArray[ offset_skin + 2 ] = sb1.z; + skinVertexBArray[ offset_skin + 3 ] = 1; // pad for faster vertex shader + + skinVertexBArray[ offset_skin + 4 ] = sb2.x; + skinVertexBArray[ offset_skin + 5 ] = sb2.y; + skinVertexBArray[ offset_skin + 6 ] = sb2.z; + skinVertexBArray[ offset_skin + 7 ] = 1; + + skinVertexBArray[ offset_skin + 8 ] = sb3.x; + skinVertexBArray[ offset_skin + 9 ] = sb3.y; + skinVertexBArray[ offset_skin + 10 ] = sb3.z; + skinVertexBArray[ offset_skin + 11 ] = 1; + + offset_skin += 12; + + } + + if ( dirtyColors && vertexColorType ) { + + if ( vertexColors.length == 3 && vertexColorType == THREE.VertexColors ) { + + c1 = vertexColors[ 0 ]; + c2 = vertexColors[ 1 ]; + c3 = vertexColors[ 2 ]; + + } else { + + c1 = faceColor; + c2 = faceColor; + c3 = faceColor; + + } + + colorArray[ offset_color ] = c1.r; + colorArray[ offset_color + 1 ] = c1.g; + colorArray[ offset_color + 2 ] = c1.b; + + colorArray[ offset_color + 3 ] = c2.r; + colorArray[ offset_color + 4 ] = c2.g; + colorArray[ offset_color + 5 ] = c2.b; + + colorArray[ offset_color + 6 ] = c3.r; + colorArray[ offset_color + 7 ] = c3.g; + colorArray[ offset_color + 8 ] = c3.b; + + offset_color += 9; + + } + + if ( dirtyTangents && geometry.hasTangents ) { + + t1 = vertexTangents[ 0 ]; + t2 = vertexTangents[ 1 ]; + t3 = vertexTangents[ 2 ]; + + tangentArray[ offset_tangent ] = t1.x; + tangentArray[ offset_tangent + 1 ] = t1.y; + tangentArray[ offset_tangent + 2 ] = t1.z; + tangentArray[ offset_tangent + 3 ] = t1.w; + + tangentArray[ offset_tangent + 4 ] = t2.x; + tangentArray[ offset_tangent + 5 ] = t2.y; + tangentArray[ offset_tangent + 6 ] = t2.z; + tangentArray[ offset_tangent + 7 ] = t2.w; + + tangentArray[ offset_tangent + 8 ] = t3.x; + tangentArray[ offset_tangent + 9 ] = t3.y; + tangentArray[ offset_tangent + 10 ] = t3.z; + tangentArray[ offset_tangent + 11 ] = t3.w; + + offset_tangent += 12; + + } + + if ( dirtyNormals && normalType ) { + + if ( vertexNormals.length == 3 && needsSmoothNormals ) { + + for ( i = 0; i < 3; i ++ ) { + + vn = vertexNormals[ i ]; + + normalArray[ offset_normal ] = vn.x; + normalArray[ offset_normal + 1 ] = vn.y; + normalArray[ offset_normal + 2 ] = vn.z; + + offset_normal += 3; + + } + + } else { + + for ( i = 0; i < 3; i ++ ) { + + normalArray[ offset_normal ] = faceNormal.x; + normalArray[ offset_normal + 1 ] = faceNormal.y; + normalArray[ offset_normal + 2 ] = faceNormal.z; + + offset_normal += 3; + + } + + } + + } + + if ( dirtyUvs && uv !== undefined && uvType ) { + + for ( i = 0; i < 3; i ++ ) { + + uvi = uv[ i ]; + + uvArray[ offset_uv ] = uvi.u; + uvArray[ offset_uv + 1 ] = uvi.v; + + offset_uv += 2; + + } + + } + + if ( dirtyUvs && uv2 !== undefined && uvType ) { + + for ( i = 0; i < 3; i ++ ) { + + uv2i = uv2[ i ]; + + uv2Array[ offset_uv2 ] = uv2i.u; + uv2Array[ offset_uv2 + 1 ] = uv2i.v; + + offset_uv2 += 2; + + } + + } + + if ( dirtyElements ) { + + faceArray[ offset_face ] = vertexIndex; + faceArray[ offset_face + 1 ] = vertexIndex + 1; + faceArray[ offset_face + 2 ] = vertexIndex + 2; + + offset_face += 3; + + lineArray[ offset_line ] = vertexIndex; + lineArray[ offset_line + 1 ] = vertexIndex + 1; + + lineArray[ offset_line + 2 ] = vertexIndex; + lineArray[ offset_line + 3 ] = vertexIndex + 2; + + lineArray[ offset_line + 4 ] = vertexIndex + 1; + lineArray[ offset_line + 5 ] = vertexIndex + 2; + + offset_line += 6; + + vertexIndex += 3; + + } + + + } else if ( face instanceof THREE.Face4 ) { + + if ( dirtyVertices ) { + + v1 = vertices[ face.a ].position; + v2 = vertices[ face.b ].position; + v3 = vertices[ face.c ].position; + v4 = vertices[ face.d ].position; + + vertexArray[ offset ] = v1.x; + vertexArray[ offset + 1 ] = v1.y; + vertexArray[ offset + 2 ] = v1.z; + + vertexArray[ offset + 3 ] = v2.x; + vertexArray[ offset + 4 ] = v2.y; + vertexArray[ offset + 5 ] = v2.z; + + vertexArray[ offset + 6 ] = v3.x; + vertexArray[ offset + 7 ] = v3.y; + vertexArray[ offset + 8 ] = v3.z; + + vertexArray[ offset + 9 ] = v4.x; + vertexArray[ offset + 10 ] = v4.y; + vertexArray[ offset + 11 ] = v4.z; + + offset += 12; + + } + + if ( customAttributes ) { + + for ( a in customAttributes ) { + + customAttribute = customAttributes[ a ]; + + if ( customAttribute.__original.needsUpdate ) { + + offset_custom = customAttribute.offset; + offset_customSrc = customAttribute.offsetSrc; + + if ( customAttribute.size === 1 ) { + + if ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) { + + customAttribute.array[ offset_custom + 0 ] = customAttribute.value[ face.a ]; + customAttribute.array[ offset_custom + 1 ] = customAttribute.value[ face.b ]; + customAttribute.array[ offset_custom + 2 ] = customAttribute.value[ face.c ]; + customAttribute.array[ offset_custom + 3 ] = customAttribute.value[ face.d ]; + + } else if ( customAttribute.boundTo === "faces" ) { + + customAttribute.array[ offset_custom + 0 ] = customAttribute.value[ offset_customSrc ]; + customAttribute.array[ offset_custom + 1 ] = customAttribute.value[ offset_customSrc ]; + customAttribute.array[ offset_custom + 2 ] = customAttribute.value[ offset_customSrc ]; + customAttribute.array[ offset_custom + 3 ] = customAttribute.value[ offset_customSrc ]; + + customAttribute.offsetSrc++; + + } else if ( customAttribute.boundTo === "faceVertices" ) { + + customAttribute.array[ offset_custom + 0 ] = customAttribute.value[ offset_customSrc + 0 ]; + customAttribute.array[ offset_custom + 1 ] = customAttribute.value[ offset_customSrc + 1 ]; + customAttribute.array[ offset_custom + 2 ] = customAttribute.value[ offset_customSrc + 2 ]; + customAttribute.array[ offset_custom + 3 ] = customAttribute.value[ offset_customSrc + 3 ]; + + customAttribute.offsetSrc += 4; + } + + customAttribute.offset += 4; + + } else { + + if ( customAttribute.boundTo === undefined || customAttribute.boundTo === "vertices" ) { + + v1 = customAttribute.value[ face.a ]; + v2 = customAttribute.value[ face.b ]; + v3 = customAttribute.value[ face.c ]; + v4 = customAttribute.value[ face.d ]; + + } else if ( customAttribute.boundTo === "faces" ) { + + v1 = customAttribute.value[ offset_customSrc ]; + v2 = customAttribute.value[ offset_customSrc ]; + v3 = customAttribute.value[ offset_customSrc ]; + v4 = customAttribute.value[ offset_customSrc ]; + + customAttribute.offsetSrc++; + + } else if ( customAttribute.boundTo === "faceVertices" ) { + + v1 = customAttribute.value[ offset_customSrc + 0 ]; + v2 = customAttribute.value[ offset_customSrc + 1 ]; + v3 = customAttribute.value[ offset_customSrc + 2 ]; + v4 = customAttribute.value[ offset_customSrc + 3 ]; + + customAttribute.offsetSrc += 4; + } + + + if ( customAttribute.size === 2 ) { + + customAttribute.array[ offset_custom + 0 ] = v1.x; + customAttribute.array[ offset_custom + 1 ] = v1.y; + + customAttribute.array[ offset_custom + 2 ] = v2.x; + customAttribute.array[ offset_custom + 3 ] = v2.y; + + customAttribute.array[ offset_custom + 4 ] = v3.x; + customAttribute.array[ offset_custom + 5 ] = v3.y; + + customAttribute.array[ offset_custom + 6 ] = v4.x; + customAttribute.array[ offset_custom + 7 ] = v4.y; + + customAttribute.offset += 8; + + } else if ( customAttribute.size === 3 ) { + + if ( customAttribute.type === "c" ) { + + customAttribute.array[ offset_custom + 0 ] = v1.r; + customAttribute.array[ offset_custom + 1 ] = v1.g; + customAttribute.array[ offset_custom + 2 ] = v1.b; + + customAttribute.array[ offset_custom + 3 ] = v2.r; + customAttribute.array[ offset_custom + 4 ] = v2.g; + customAttribute.array[ offset_custom + 5 ] = v2.b; + + customAttribute.array[ offset_custom + 6 ] = v3.r; + customAttribute.array[ offset_custom + 7 ] = v3.g; + customAttribute.array[ offset_custom + 8 ] = v3.b; + + customAttribute.array[ offset_custom + 9 ] = v4.r; + customAttribute.array[ offset_custom + 10 ] = v4.g; + customAttribute.array[ offset_custom + 11 ] = v4.b; + + } else { + + customAttribute.array[ offset_custom + 0 ] = v1.x; + customAttribute.array[ offset_custom + 1 ] = v1.y; + customAttribute.array[ offset_custom + 2 ] = v1.z; + + customAttribute.array[ offset_custom + 3 ] = v2.x; + customAttribute.array[ offset_custom + 4 ] = v2.y; + customAttribute.array[ offset_custom + 5 ] = v2.z; + + customAttribute.array[ offset_custom + 6 ] = v3.x; + customAttribute.array[ offset_custom + 7 ] = v3.y; + customAttribute.array[ offset_custom + 8 ] = v3.z; + + customAttribute.array[ offset_custom + 9 ] = v4.x; + customAttribute.array[ offset_custom + 10 ] = v4.y; + customAttribute.array[ offset_custom + 11 ] = v4.z; + + } + + customAttribute.offset += 12; + + } else { + + customAttribute.array[ offset_custom + 0 ] = v1.x; + customAttribute.array[ offset_custom + 1 ] = v1.y; + customAttribute.array[ offset_custom + 2 ] = v1.z; + customAttribute.array[ offset_custom + 3 ] = v1.w; + + customAttribute.array[ offset_custom + 4 ] = v2.x; + customAttribute.array[ offset_custom + 5 ] = v2.y; + customAttribute.array[ offset_custom + 6 ] = v2.z; + customAttribute.array[ offset_custom + 7 ] = v2.w; + + customAttribute.array[ offset_custom + 8 ] = v3.x; + customAttribute.array[ offset_custom + 9 ] = v3.y; + customAttribute.array[ offset_custom + 10 ] = v3.z; + customAttribute.array[ offset_custom + 11 ] = v3.w; + + customAttribute.array[ offset_custom + 12 ] = v4.x; + customAttribute.array[ offset_custom + 13 ] = v4.y; + customAttribute.array[ offset_custom + 14 ] = v4.z; + customAttribute.array[ offset_custom + 15 ] = v4.w; + + customAttribute.offset += 16; + + } + + } + + } + + } + + } + + + if ( dirtyMorphTargets ) { + + for ( vk = 0, vkl = morphTargets.length; vk < vkl; vk++ ) { + + v1 = morphTargets[ vk ].vertices[ face.a ].position; + v2 = morphTargets[ vk ].vertices[ face.b ].position; + v3 = morphTargets[ vk ].vertices[ face.c ].position; + v4 = morphTargets[ vk ].vertices[ face.d ].position; + + vka = morphTargetsArrays[ vk ]; + + vka[ offset_morphTarget + 0 ] = v1.x; + vka[ offset_morphTarget + 1 ] = v1.y; + vka[ offset_morphTarget + 2 ] = v1.z; + + vka[ offset_morphTarget + 3 ] = v2.x; + vka[ offset_morphTarget + 4 ] = v2.y; + vka[ offset_morphTarget + 5 ] = v2.z; + + vka[ offset_morphTarget + 6 ] = v3.x; + vka[ offset_morphTarget + 7 ] = v3.y; + vka[ offset_morphTarget + 8 ] = v3.z; + + vka[ offset_morphTarget + 9 ] = v4.x; + vka[ offset_morphTarget + 10 ] = v4.y; + vka[ offset_morphTarget + 11 ] = v4.z; + } + + offset_morphTarget += 12; + + } + + if ( obj_skinWeights.length ) { + + // weights + + sw1 = obj_skinWeights[ face.a ]; + sw2 = obj_skinWeights[ face.b ]; + sw3 = obj_skinWeights[ face.c ]; + sw4 = obj_skinWeights[ face.d ]; + + skinWeightArray[ offset_skin ] = sw1.x; + skinWeightArray[ offset_skin + 1 ] = sw1.y; + skinWeightArray[ offset_skin + 2 ] = sw1.z; + skinWeightArray[ offset_skin + 3 ] = sw1.w; + + skinWeightArray[ offset_skin + 4 ] = sw2.x; + skinWeightArray[ offset_skin + 5 ] = sw2.y; + skinWeightArray[ offset_skin + 6 ] = sw2.z; + skinWeightArray[ offset_skin + 7 ] = sw2.w; + + skinWeightArray[ offset_skin + 8 ] = sw3.x; + skinWeightArray[ offset_skin + 9 ] = sw3.y; + skinWeightArray[ offset_skin + 10 ] = sw3.z; + skinWeightArray[ offset_skin + 11 ] = sw3.w; + + skinWeightArray[ offset_skin + 12 ] = sw4.x; + skinWeightArray[ offset_skin + 13 ] = sw4.y; + skinWeightArray[ offset_skin + 14 ] = sw4.z; + skinWeightArray[ offset_skin + 15 ] = sw4.w; + + // indices + + si1 = obj_skinIndices[ face.a ]; + si2 = obj_skinIndices[ face.b ]; + si3 = obj_skinIndices[ face.c ]; + si4 = obj_skinIndices[ face.d ]; + + skinIndexArray[ offset_skin ] = si1.x; + skinIndexArray[ offset_skin + 1 ] = si1.y; + skinIndexArray[ offset_skin + 2 ] = si1.z; + skinIndexArray[ offset_skin + 3 ] = si1.w; + + skinIndexArray[ offset_skin + 4 ] = si2.x; + skinIndexArray[ offset_skin + 5 ] = si2.y; + skinIndexArray[ offset_skin + 6 ] = si2.z; + skinIndexArray[ offset_skin + 7 ] = si2.w; + + skinIndexArray[ offset_skin + 8 ] = si3.x; + skinIndexArray[ offset_skin + 9 ] = si3.y; + skinIndexArray[ offset_skin + 10 ] = si3.z; + skinIndexArray[ offset_skin + 11 ] = si3.w; + + skinIndexArray[ offset_skin + 12 ] = si4.x; + skinIndexArray[ offset_skin + 13 ] = si4.y; + skinIndexArray[ offset_skin + 14 ] = si4.z; + skinIndexArray[ offset_skin + 15 ] = si4.w; + + // vertices A + + sa1 = obj_skinVerticesA[ face.a ]; + sa2 = obj_skinVerticesA[ face.b ]; + sa3 = obj_skinVerticesA[ face.c ]; + sa4 = obj_skinVerticesA[ face.d ]; + + skinVertexAArray[ offset_skin ] = sa1.x; + skinVertexAArray[ offset_skin + 1 ] = sa1.y; + skinVertexAArray[ offset_skin + 2 ] = sa1.z; + skinVertexAArray[ offset_skin + 3 ] = 1; // pad for faster vertex shader + + skinVertexAArray[ offset_skin + 4 ] = sa2.x; + skinVertexAArray[ offset_skin + 5 ] = sa2.y; + skinVertexAArray[ offset_skin + 6 ] = sa2.z; + skinVertexAArray[ offset_skin + 7 ] = 1; + + skinVertexAArray[ offset_skin + 8 ] = sa3.x; + skinVertexAArray[ offset_skin + 9 ] = sa3.y; + skinVertexAArray[ offset_skin + 10 ] = sa3.z; + skinVertexAArray[ offset_skin + 11 ] = 1; + + skinVertexAArray[ offset_skin + 12 ] = sa4.x; + skinVertexAArray[ offset_skin + 13 ] = sa4.y; + skinVertexAArray[ offset_skin + 14 ] = sa4.z; + skinVertexAArray[ offset_skin + 15 ] = 1; + + // vertices B + + sb1 = obj_skinVerticesB[ face.a ]; + sb2 = obj_skinVerticesB[ face.b ]; + sb3 = obj_skinVerticesB[ face.c ]; + sb4 = obj_skinVerticesB[ face.d ]; + + skinVertexBArray[ offset_skin ] = sb1.x; + skinVertexBArray[ offset_skin + 1 ] = sb1.y; + skinVertexBArray[ offset_skin + 2 ] = sb1.z; + skinVertexBArray[ offset_skin + 3 ] = 1; // pad for faster vertex shader + + skinVertexBArray[ offset_skin + 4 ] = sb2.x; + skinVertexBArray[ offset_skin + 5 ] = sb2.y; + skinVertexBArray[ offset_skin + 6 ] = sb2.z; + skinVertexBArray[ offset_skin + 7 ] = 1; + + skinVertexBArray[ offset_skin + 8 ] = sb3.x; + skinVertexBArray[ offset_skin + 9 ] = sb3.y; + skinVertexBArray[ offset_skin + 10 ] = sb3.z; + skinVertexBArray[ offset_skin + 11 ] = 1; + + skinVertexBArray[ offset_skin + 12 ] = sb4.x; + skinVertexBArray[ offset_skin + 13 ] = sb4.y; + skinVertexBArray[ offset_skin + 14 ] = sb4.z; + skinVertexBArray[ offset_skin + 15 ] = 1; + + offset_skin += 16; + + } + + if ( dirtyColors && vertexColorType ) { + + if ( vertexColors.length == 4 && vertexColorType == THREE.VertexColors ) { + + c1 = vertexColors[ 0 ]; + c2 = vertexColors[ 1 ]; + c3 = vertexColors[ 2 ]; + c4 = vertexColors[ 3 ]; + + } else { + + c1 = faceColor; + c2 = faceColor; + c3 = faceColor; + c4 = faceColor; + + } + + colorArray[ offset_color ] = c1.r; + colorArray[ offset_color + 1 ] = c1.g; + colorArray[ offset_color + 2 ] = c1.b; + + colorArray[ offset_color + 3 ] = c2.r; + colorArray[ offset_color + 4 ] = c2.g; + colorArray[ offset_color + 5 ] = c2.b; + + colorArray[ offset_color + 6 ] = c3.r; + colorArray[ offset_color + 7 ] = c3.g; + colorArray[ offset_color + 8 ] = c3.b; + + colorArray[ offset_color + 9 ] = c4.r; + colorArray[ offset_color + 10 ] = c4.g; + colorArray[ offset_color + 11 ] = c4.b; + + offset_color += 12; + + } + + if ( dirtyTangents && geometry.hasTangents ) { + + t1 = vertexTangents[ 0 ]; + t2 = vertexTangents[ 1 ]; + t3 = vertexTangents[ 2 ]; + t4 = vertexTangents[ 3 ]; + + tangentArray[ offset_tangent ] = t1.x; + tangentArray[ offset_tangent + 1 ] = t1.y; + tangentArray[ offset_tangent + 2 ] = t1.z; + tangentArray[ offset_tangent + 3 ] = t1.w; + + tangentArray[ offset_tangent + 4 ] = t2.x; + tangentArray[ offset_tangent + 5 ] = t2.y; + tangentArray[ offset_tangent + 6 ] = t2.z; + tangentArray[ offset_tangent + 7 ] = t2.w; + + tangentArray[ offset_tangent + 8 ] = t3.x; + tangentArray[ offset_tangent + 9 ] = t3.y; + tangentArray[ offset_tangent + 10 ] = t3.z; + tangentArray[ offset_tangent + 11 ] = t3.w; + + tangentArray[ offset_tangent + 12 ] = t4.x; + tangentArray[ offset_tangent + 13 ] = t4.y; + tangentArray[ offset_tangent + 14 ] = t4.z; + tangentArray[ offset_tangent + 15 ] = t4.w; + + offset_tangent += 16; + + } + + if ( dirtyNormals && normalType ) { + + if ( vertexNormals.length == 4 && needsSmoothNormals ) { + + for ( i = 0; i < 4; i ++ ) { + + vn = vertexNormals[ i ]; + + normalArray[ offset_normal ] = vn.x; + normalArray[ offset_normal + 1 ] = vn.y; + normalArray[ offset_normal + 2 ] = vn.z; + + offset_normal += 3; + + } + + } else { + + for ( i = 0; i < 4; i ++ ) { + + normalArray[ offset_normal ] = faceNormal.x; + normalArray[ offset_normal + 1 ] = faceNormal.y; + normalArray[ offset_normal + 2 ] = faceNormal.z; + + offset_normal += 3; + + } + + } + + } + + if ( dirtyUvs && uv !== undefined && uvType ) { + + for ( i = 0; i < 4; i ++ ) { + + uvi = uv[ i ]; + + uvArray[ offset_uv ] = uvi.u; + uvArray[ offset_uv + 1 ] = uvi.v; + + offset_uv += 2; + + } + + } + + if ( dirtyUvs && uv2 !== undefined && uvType ) { + + for ( i = 0; i < 4; i ++ ) { + + uv2i = uv2[ i ]; + + uv2Array[ offset_uv2 ] = uv2i.u; + uv2Array[ offset_uv2 + 1 ] = uv2i.v; + + offset_uv2 += 2; + + } + + } + + if ( dirtyElements ) { + + faceArray[ offset_face ] = vertexIndex; + faceArray[ offset_face + 1 ] = vertexIndex + 1; + faceArray[ offset_face + 2 ] = vertexIndex + 3; + + faceArray[ offset_face + 3 ] = vertexIndex + 1; + faceArray[ offset_face + 4 ] = vertexIndex + 2; + faceArray[ offset_face + 5 ] = vertexIndex + 3; + + offset_face += 6; + + lineArray[ offset_line ] = vertexIndex; + lineArray[ offset_line + 1 ] = vertexIndex + 1; + + lineArray[ offset_line + 2 ] = vertexIndex; + lineArray[ offset_line + 3 ] = vertexIndex + 3; + + lineArray[ offset_line + 4 ] = vertexIndex + 1; + lineArray[ offset_line + 5 ] = vertexIndex + 2; + + lineArray[ offset_line + 6 ] = vertexIndex + 2; + lineArray[ offset_line + 7 ] = vertexIndex + 3; + + offset_line += 8; + + vertexIndex += 4; + + } + + } + + } + + if ( obj_edgeFaces ) { + + for ( f = 0, fl = obj_edgeFaces.length; f < fl; f ++ ) { + + faceArray[ offset_face ] = obj_edgeFaces[ f ].a; + faceArray[ offset_face + 1 ] = obj_edgeFaces[ f ].b; + faceArray[ offset_face + 2 ] = obj_edgeFaces[ f ].c; + + faceArray[ offset_face + 3 ] = obj_edgeFaces[ f ].a; + faceArray[ offset_face + 4 ] = obj_edgeFaces[ f ].c; + faceArray[ offset_face + 5 ] = obj_edgeFaces[ f ].d; + + offset_face += 6; + } + + } + + if ( dirtyVertices ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint ); + + } + + if ( customAttributes ) { + + for ( a in customAttributes ) { + + customAttribute = customAttributes[ a ]; + + if ( customAttribute.__original.needsUpdate ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, customAttribute.buffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, customAttribute.array, hint ); + + } + + } + + } + + if ( dirtyMorphTargets ) { + + for ( vk = 0, vkl = morphTargets.length; vk < vkl; vk ++ ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ vk ] ); + _gl.bufferData( _gl.ARRAY_BUFFER, morphTargetsArrays[ vk ], hint ); + + } + } + + if ( dirtyColors && offset_color > 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglColorBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint ); + + } + + if ( dirtyNormals ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglNormalBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, normalArray, hint ); + + } + + if ( dirtyTangents && geometry.hasTangents ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglTangentBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, tangentArray, hint ); + + } + + if ( dirtyUvs && offset_uv > 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUVBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, uvArray, hint ); + + } + + if ( dirtyUvs && offset_uv2 > 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUV2Buffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, uv2Array, hint ); + + } + + if ( dirtyElements ) { + + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglFaceBuffer ); + _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, faceArray, hint ); + + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglLineBuffer ); + _gl.bufferData( _gl.ELEMENT_ARRAY_BUFFER, lineArray, hint ); + + } + + if ( offset_skin > 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinVertexABuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, skinVertexAArray, hint ); + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinVertexBBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, skinVertexBArray, hint ); + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinIndicesBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, skinIndexArray, hint ); + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinWeightsBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, skinWeightArray, hint ); + + } + + if ( ! object.dynamic ) { + + delete geometryGroup.__inittedArrays; + delete geometryGroup.__colorArray; + delete geometryGroup.__normalArray; + delete geometryGroup.__tangentArray; + delete geometryGroup.__uvArray; + delete geometryGroup.__uv2Array; + delete geometryGroup.__faceArray; + delete geometryGroup.__vertexArray; + delete geometryGroup.__lineArray; + delete geometryGroup.__skinVertexAArray; + delete geometryGroup.__skinVertexBArray; + delete geometryGroup.__skinIndexArray; + delete geometryGroup.__skinWeightArray; + + } + + }; + + function setLineBuffers ( geometry, hint ) { + + var v, c, vertex, offset, + vertices = geometry.vertices, + colors = geometry.colors, + vl = vertices.length, + cl = colors.length, + + vertexArray = geometry.__vertexArray, + colorArray = geometry.__colorArray, + + dirtyVertices = geometry.__dirtyVertices, + dirtyColors = geometry.__dirtyColors; + + if ( dirtyVertices ) { + + for ( v = 0; v < vl; v++ ) { + + vertex = vertices[ v ].position; + + offset = v * 3; + + vertexArray[ offset ] = vertex.x; + vertexArray[ offset + 1 ] = vertex.y; + vertexArray[ offset + 2 ] = vertex.z; + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglVertexBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint ); + + } + + if ( dirtyColors ) { + + for ( c = 0; c < cl; c++ ) { + + color = colors[ c ]; + + offset = c * 3; + + colorArray[ offset ] = color.r; + colorArray[ offset + 1 ] = color.g; + colorArray[ offset + 2 ] = color.b; + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglColorBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint ); + + } + + }; + + function setRibbonBuffers ( geometry, hint ) { + + var v, c, vertex, offset, + vertices = geometry.vertices, + colors = geometry.colors, + vl = vertices.length, + cl = colors.length, + + vertexArray = geometry.__vertexArray, + colorArray = geometry.__colorArray, + + dirtyVertices = geometry.__dirtyVertices, + dirtyColors = geometry.__dirtyColors; + + if ( dirtyVertices ) { + + for ( v = 0; v < vl; v++ ) { + + vertex = vertices[ v ].position; + + offset = v * 3; + + vertexArray[ offset ] = vertex.x; + vertexArray[ offset + 1 ] = vertex.y; + vertexArray[ offset + 2 ] = vertex.z; + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglVertexBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint ); + + } + + if ( dirtyColors ) { + + for ( c = 0; c < cl; c++ ) { + + color = colors[ c ]; + + offset = c * 3; + + colorArray[ offset ] = color.r; + colorArray[ offset + 1 ] = color.g; + colorArray[ offset + 2 ] = color.b; + + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglColorBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint ); + + } + + }; + + function setParticleBuffers ( geometry, hint, object ) { + + var v, c, vertex, offset, + vertices = geometry.vertices, + vl = vertices.length, + + colors = geometry.colors, + cl = colors.length, + + vertexArray = geometry.__vertexArray, + colorArray = geometry.__colorArray, + + sortArray = geometry.__sortArray, + + dirtyVertices = geometry.__dirtyVertices, + dirtyElements = geometry.__dirtyElements, + dirtyColors = geometry.__dirtyColors; + + if ( object.sortParticles ) { + + _projScreenMatrix.multiplySelf( object.matrixWorld ); + + for ( v = 0; v < vl; v++ ) { + + vertex = vertices[ v ].position; + + _vector3.copy( vertex ); + _projScreenMatrix.multiplyVector3( _vector3 ); + + sortArray[ v ] = [ _vector3.z, v ]; + + } + + sortArray.sort( function(a,b) { return b[0] - a[0]; } ); + + for ( v = 0; v < vl; v++ ) { + + vertex = vertices[ sortArray[v][1] ].position; + + offset = v * 3; + + vertexArray[ offset ] = vertex.x; + vertexArray[ offset + 1 ] = vertex.y; + vertexArray[ offset + 2 ] = vertex.z; + + } + + for ( c = 0; c < cl; c++ ) { + + offset = c * 3; + + color = colors[ sortArray[c][1] ]; + + colorArray[ offset ] = color.r; + colorArray[ offset + 1 ] = color.g; + colorArray[ offset + 2 ] = color.b; + + } + + + } else { + + if ( dirtyVertices ) { + + for ( v = 0; v < vl; v++ ) { + + vertex = vertices[ v ].position; + + offset = v * 3; + + vertexArray[ offset ] = vertex.x; + vertexArray[ offset + 1 ] = vertex.y; + vertexArray[ offset + 2 ] = vertex.z; + + } + + } + + if ( dirtyColors ) { + + for ( c = 0; c < cl; c++ ) { + + color = colors[ c ]; + + offset = c * 3; + + colorArray[ offset ] = color.r; + colorArray[ offset + 1 ] = color.g; + colorArray[ offset + 2 ] = color.b; + + } + + } + + } + + if ( dirtyVertices || object.sortParticles ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglVertexBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, vertexArray, hint ); + + } + + if ( dirtyColors || object.sortParticles ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometry.__webglColorBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, colorArray, hint ); + + } + + }; + + function setMaterialShaders( material, shaders ) { + + material.uniforms = THREE.UniformsUtils.clone( shaders.uniforms ); + material.vertexShader = shaders.vertexShader; + material.fragmentShader = shaders.fragmentShader; + + }; + + function refreshUniformsCommon( uniforms, material ) { + + uniforms.diffuse.value = material.color; + uniforms.opacity.value = material.opacity; + + uniforms.map.texture = material.map; + if ( material.map ) { + + uniforms.offsetRepeat.value.set( material.map.offset.x, material.map.offset.y, material.map.repeat.x, material.map.repeat.y ); + + } + + uniforms.lightMap.texture = material.lightMap; + + uniforms.envMap.texture = material.envMap; + uniforms.reflectivity.value = material.reflectivity; + uniforms.refractionRatio.value = material.refractionRatio; + uniforms.combine.value = material.combine; + uniforms.useRefract.value = material.envMap && material.envMap.mapping instanceof THREE.CubeRefractionMapping; + + }; + + function refreshUniformsLine( uniforms, material ) { + + uniforms.diffuse.value = material.color; + uniforms.opacity.value = material.opacity; + + }; + + function refreshUniformsParticle( uniforms, material ) { + + uniforms.psColor.value = material.color; + uniforms.opacity.value = material.opacity; + uniforms.size.value = material.size; + uniforms.scale.value = _canvas.height / 2.0; // TODO: Cache this. + + uniforms.map.texture = material.map; + + }; + + function refreshUniformsFog( uniforms, fog ) { + + uniforms.fogColor.value = fog.color; + + if ( fog instanceof THREE.Fog ) { + + uniforms.fogNear.value = fog.near; + uniforms.fogFar.value = fog.far; + + } else if ( fog instanceof THREE.FogExp2 ) { + + uniforms.fogDensity.value = fog.density; + + } + + }; + + function refreshUniformsPhong( uniforms, material ) { + + uniforms.ambient.value = material.ambient; + uniforms.specular.value = material.specular; + uniforms.shininess.value = material.shininess; + + }; + + + function refreshUniformsLights( uniforms, lights ) { + + uniforms.enableLighting.value = lights.directional.length + lights.point.length; + uniforms.ambientLightColor.value = lights.ambient; + + uniforms.directionalLightColor.value = lights.directional.colors; + uniforms.directionalLightDirection.value = lights.directional.positions; + + uniforms.pointLightColor.value = lights.point.colors; + uniforms.pointLightPosition.value = lights.point.positions; + uniforms.pointLightDistance.value = lights.point.distances; + + }; + + this.initMaterial = function ( material, lights, fog, object ) { + + var u, a, identifiers, i, parameters, maxLightCount, maxBones, shaderID; + + if ( material instanceof THREE.MeshDepthMaterial ) { + + shaderID = 'depth'; + + } else if ( material instanceof THREE.ShadowVolumeDynamicMaterial ) { + + shaderID = 'shadowVolumeDynamic'; + + } else if ( material instanceof THREE.MeshNormalMaterial ) { + + shaderID = 'normal'; + + } else if ( material instanceof THREE.MeshBasicMaterial ) { + + shaderID = 'basic'; + + } else if ( material instanceof THREE.MeshLambertMaterial ) { + + shaderID = 'lambert'; + + } else if ( material instanceof THREE.MeshPhongMaterial ) { + + shaderID = 'phong'; + + } else if ( material instanceof THREE.LineBasicMaterial ) { + + shaderID = 'basic'; + + } else if ( material instanceof THREE.ParticleBasicMaterial ) { + + shaderID = 'particle_basic'; + + } + + if ( shaderID ) { + + setMaterialShaders( material, THREE.ShaderLib[ shaderID ] ); + + } + + // heuristics to create shader parameters according to lights in the scene + // (not to blow over maxLights budget) + + maxLightCount = allocateLights( lights, 4 ); + + maxBones = allocateBones( object ); + + parameters = { + map: !!material.map, envMap: !!material.envMap, lightMap: !!material.lightMap, + vertexColors: material.vertexColors, + fog: fog, sizeAttenuation: material.sizeAttenuation, + skinning: material.skinning, + morphTargets: material.morphTargets, + maxMorphTargets: this.maxMorphTargets, + maxDirLights: maxLightCount.directional, maxPointLights: maxLightCount.point, + maxBones: maxBones + }; + + material.program = buildProgram( shaderID, material.fragmentShader, material.vertexShader, material.uniforms, material.attributes, parameters ); + + var attributes = material.program.attributes; + + if ( attributes.position >= 0 ) _gl.enableVertexAttribArray( attributes.position ); + if ( attributes.color >= 0 ) _gl.enableVertexAttribArray( attributes.color ); + if ( attributes.normal >= 0 ) _gl.enableVertexAttribArray( attributes.normal ); + if ( attributes.tangent >= 0 ) _gl.enableVertexAttribArray( attributes.tangent ); + + if ( material.skinning && + attributes.skinVertexA >=0 && attributes.skinVertexB >= 0 && + attributes.skinIndex >= 0 && attributes.skinWeight >= 0 ) { + + _gl.enableVertexAttribArray( attributes.skinVertexA ); + _gl.enableVertexAttribArray( attributes.skinVertexB ); + _gl.enableVertexAttribArray( attributes.skinIndex ); + _gl.enableVertexAttribArray( attributes.skinWeight ); + + } + + if ( material.attributes ) { + + for ( a in material.attributes ) { + + if( attributes[ a ] !== undefined && attributes[ a ] >= 0 ) _gl.enableVertexAttribArray( attributes[ a ] ); + + } + + } + + + + if ( material.morphTargets ) { + + material.numSupportedMorphTargets = 0; + + + if ( attributes.morphTarget0 >= 0 ) { + + _gl.enableVertexAttribArray( attributes.morphTarget0 ); + material.numSupportedMorphTargets ++; + + } + + if ( attributes.morphTarget1 >= 0 ) { + + _gl.enableVertexAttribArray( attributes.morphTarget1 ); + material.numSupportedMorphTargets ++; + + } + + if ( attributes.morphTarget2 >= 0 ) { + + _gl.enableVertexAttribArray( attributes.morphTarget2 ); + material.numSupportedMorphTargets ++; + + } + + if ( attributes.morphTarget3 >= 0 ) { + + _gl.enableVertexAttribArray( attributes.morphTarget3 ); + material.numSupportedMorphTargets ++; + + } + + if ( attributes.morphTarget4 >= 0 ) { + + _gl.enableVertexAttribArray( attributes.morphTarget4 ); + material.numSupportedMorphTargets ++; + + } + + if ( attributes.morphTarget5 >= 0 ) { + + _gl.enableVertexAttribArray( attributes.morphTarget5 ); + material.numSupportedMorphTargets ++; + + } + + if ( attributes.morphTarget6 >= 0 ) { + + _gl.enableVertexAttribArray( attributes.morphTarget6 ); + material.numSupportedMorphTargets ++; + + } + + if ( attributes.morphTarget7 >= 0 ) { + + _gl.enableVertexAttribArray( attributes.morphTarget7 ); + material.numSupportedMorphTargets ++; + + } + + object.__webglMorphTargetInfluences = new Float32Array( this.maxMorphTargets ); + + for ( var i = 0, il = this.maxMorphTargets; i < il; i ++ ) { + + object.__webglMorphTargetInfluences[ i ] = 0; + + } + + } + + }; + + function setProgram( camera, lights, fog, material, object ) { + + if ( ! material.program ) { + + _this.initMaterial( material, lights, fog, object ); + + } + + var program = material.program, + p_uniforms = program.uniforms, + m_uniforms = material.uniforms; + + if ( program != _currentProgram ) { + + _gl.useProgram( program ); + _currentProgram = program; + + } + + _gl.uniformMatrix4fv( p_uniforms.projectionMatrix, false, _projectionMatrixArray ); + + // refresh uniforms common to several materials + + if ( fog && ( + material instanceof THREE.MeshBasicMaterial || + material instanceof THREE.MeshLambertMaterial || + material instanceof THREE.MeshPhongMaterial || + material instanceof THREE.LineBasicMaterial || + material instanceof THREE.ParticleBasicMaterial || + material.fog ) + ) { + + refreshUniformsFog( m_uniforms, fog ); + + } + + if ( material instanceof THREE.MeshPhongMaterial || + material instanceof THREE.MeshLambertMaterial || + material.lights ) { + + setupLights( program, lights ); + refreshUniformsLights( m_uniforms, _lights ); + + } + + if ( material instanceof THREE.MeshBasicMaterial || + material instanceof THREE.MeshLambertMaterial || + material instanceof THREE.MeshPhongMaterial ) { + + refreshUniformsCommon( m_uniforms, material ); + + } + + // refresh single material specific uniforms + + if ( material instanceof THREE.LineBasicMaterial ) { + + refreshUniformsLine( m_uniforms, material ); + + } else if ( material instanceof THREE.ParticleBasicMaterial ) { + + refreshUniformsParticle( m_uniforms, material ); + + } else if ( material instanceof THREE.MeshPhongMaterial ) { + + refreshUniformsPhong( m_uniforms, material ); + + } else if ( material instanceof THREE.MeshDepthMaterial ) { + + m_uniforms.mNear.value = camera.near; + m_uniforms.mFar.value = camera.far; + m_uniforms.opacity.value = material.opacity; + + } else if ( material instanceof THREE.MeshNormalMaterial ) { + + m_uniforms.opacity.value = material.opacity; + } + + // load common uniforms + + loadUniformsGeneric( program, m_uniforms ); + loadUniformsMatrices( p_uniforms, object ); + + // load material specific uniforms + // (shader material also gets them for the sake of genericity) + + if ( material instanceof THREE.MeshShaderMaterial || + material instanceof THREE.MeshPhongMaterial || + material.envMap ) { + + if( p_uniforms.cameraPosition !== null ) { + + _gl.uniform3f( p_uniforms.cameraPosition, camera.position.x, camera.position.y, camera.position.z ); + + } + + } + + if ( material instanceof THREE.MeshShaderMaterial || + material.envMap || + material.skinning ) { + + if ( p_uniforms.objectMatrix !== null ) { + + _gl.uniformMatrix4fv( p_uniforms.objectMatrix, false, object._objectMatrixArray ); + + } + + } + + if ( material instanceof THREE.MeshPhongMaterial || + material instanceof THREE.MeshLambertMaterial || + material instanceof THREE.MeshShaderMaterial || + material.skinning ) { + + if( p_uniforms.viewMatrix !== null ) { + + _gl.uniformMatrix4fv( p_uniforms.viewMatrix, false, _viewMatrixArray ); + + } + + } + + if ( material instanceof THREE.ShadowVolumeDynamicMaterial ) { + + var dirLight = m_uniforms.directionalLightDirection.value; + + dirLight[ 0 ] = -lights[ 1 ].position.x; + dirLight[ 1 ] = -lights[ 1 ].position.y; + dirLight[ 2 ] = -lights[ 1 ].position.z; + + _gl.uniform3fv( p_uniforms.directionalLightDirection, dirLight ); + _gl.uniformMatrix4fv( p_uniforms.objectMatrix, false, object._objectMatrixArray ); + _gl.uniformMatrix4fv( p_uniforms.viewMatrix, false, _viewMatrixArray ); + } + + + if ( material.skinning ) { + + loadUniformsSkinning( p_uniforms, object ); + + } + + return program; + + }; + + function renderBuffer( camera, lights, fog, material, geometryGroup, object ) { + + if ( material.opacity == 0 ) return; + + var program, attributes, linewidth, primitives, a, attribute; + + program = setProgram( camera, lights, fog, material, object ); + + attributes = program.attributes; + + // vertices + + if ( !material.morphTargets && attributes.position >= 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer ); + _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 ); + + } else { + + setupMorphTargets( material, geometryGroup, object ); + + } + + + // custom attributes + + // Use the per-geometryGroup custom attribute arrays which are setup in initMeshBuffers + + if ( geometryGroup.__webglCustomAttributes ) { + + for( a in geometryGroup.__webglCustomAttributes ) { + + if( attributes[ a ] >= 0 ) { + + attribute = geometryGroup.__webglCustomAttributes[ a ]; + + _gl.bindBuffer( _gl.ARRAY_BUFFER, attribute.buffer ); + _gl.vertexAttribPointer( attributes[ a ], attribute.size, _gl.FLOAT, false, 0, 0 ); + + } + + } + + } + + +/* if ( material.attributes ) { + + for( a in material.attributes ) { + + if( attributes[ a ] !== undefined && attributes[ a ] >= 0 ) { + + attribute = material.attributes[ a ]; + + if( attribute.buffer ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, attribute.buffer ); + _gl.vertexAttribPointer( attributes[ a ], attribute.size, _gl.FLOAT, false, 0, 0 ); + + } + + } + + } + + }*/ + + + + // colors + + if ( attributes.color >= 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglColorBuffer ); + _gl.vertexAttribPointer( attributes.color, 3, _gl.FLOAT, false, 0, 0 ); + + } + + // normals + + if ( attributes.normal >= 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglNormalBuffer ); + _gl.vertexAttribPointer( attributes.normal, 3, _gl.FLOAT, false, 0, 0 ); + + } + + // tangents + + if ( attributes.tangent >= 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglTangentBuffer ); + _gl.vertexAttribPointer( attributes.tangent, 4, _gl.FLOAT, false, 0, 0 ); + + } + + // uvs + + if ( attributes.uv >= 0 ) { + + if ( geometryGroup.__webglUVBuffer ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUVBuffer ); + _gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 0, 0 ); + + _gl.enableVertexAttribArray( attributes.uv ); + + } else { + + _gl.disableVertexAttribArray( attributes.uv ); + + } + + } + + if ( attributes.uv2 >= 0 ) { + + if ( geometryGroup.__webglUV2Buffer ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglUV2Buffer ); + _gl.vertexAttribPointer( attributes.uv2, 2, _gl.FLOAT, false, 0, 0 ); + + _gl.enableVertexAttribArray( attributes.uv2 ); + + } else { + + _gl.disableVertexAttribArray( attributes.uv2 ); + + } + + } + + if ( material.skinning && + attributes.skinVertexA >= 0 && attributes.skinVertexB >= 0 && + attributes.skinIndex >= 0 && attributes.skinWeight >= 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinVertexABuffer ); + _gl.vertexAttribPointer( attributes.skinVertexA, 4, _gl.FLOAT, false, 0, 0 ); + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinVertexBBuffer ); + _gl.vertexAttribPointer( attributes.skinVertexB, 4, _gl.FLOAT, false, 0, 0 ); + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinIndicesBuffer ); + _gl.vertexAttribPointer( attributes.skinIndex, 4, _gl.FLOAT, false, 0, 0 ); + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglSkinWeightsBuffer ); + _gl.vertexAttribPointer( attributes.skinWeight, 4, _gl.FLOAT, false, 0, 0 ); + + } + + // render mesh + + if ( object instanceof THREE.Mesh ) { + + // wireframe + + if ( material.wireframe ) { + + _gl.lineWidth( material.wireframeLinewidth ); + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglLineBuffer ); + _gl.drawElements( _gl.LINES, geometryGroup.__webglLineCount, _gl.UNSIGNED_SHORT, 0 ); + + // triangles + + } else { + + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglFaceBuffer ); + _gl.drawElements( _gl.TRIANGLES, geometryGroup.__webglFaceCount, _gl.UNSIGNED_SHORT, 0 ); + + } + + _this.data.vertices += geometryGroup.__webglFaceCount; + _this.data.faces += geometryGroup.__webglFaceCount / 3; + _this.data.drawCalls ++; + + // render lines + + } else if ( object instanceof THREE.Line ) { + + primitives = ( object.type == THREE.LineStrip ) ? _gl.LINE_STRIP : _gl.LINES; + + _gl.lineWidth( material.linewidth ); + _gl.drawArrays( primitives, 0, geometryGroup.__webglLineCount ); + + _this.data.drawCalls ++; + + // render particles + + } else if ( object instanceof THREE.ParticleSystem ) { + + _gl.drawArrays( _gl.POINTS, 0, geometryGroup.__webglParticleCount ); + + _this.data.drawCalls ++; + + // render ribbon + + } else if ( object instanceof THREE.Ribbon ) { + + _gl.drawArrays( _gl.TRIANGLE_STRIP, 0, geometryGroup.__webglVertexCount ); + + _this.data.drawCalls ++; + + } + + }; + + + function setupMorphTargets( material, geometryGroup, object ) { + + // set base + + var attributes = material.program.attributes; + + if ( object.morphTargetBase !== - 1 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ object.morphTargetBase ] ); + _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 ); + + } else if ( attributes.position >= 0 ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer ); + _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 ); + + } + + if ( object.morphTargetForcedOrder.length ) { + + // set forced order + + var m = 0; + var order = object.morphTargetForcedOrder; + var influences = object.morphTargetInfluences; + + while ( m < material.numSupportedMorphTargets && m < order.length ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ order[ m ] ] ); + _gl.vertexAttribPointer( attributes[ "morphTarget" + m ], 3, _gl.FLOAT, false, 0, 0 ); + + object.__webglMorphTargetInfluences[ m ] = influences[ order[ m ]]; + + m ++; + } + + } else { + + // find most influencing + + var used = []; + var candidateInfluence = - 1; + var candidate = 0; + var influences = object.morphTargetInfluences; + var i, il = influences.length; + var m = 0; + + if ( object.morphTargetBase !== - 1 ) { + + used[ object.morphTargetBase ] = true; + + } + + while ( m < material.numSupportedMorphTargets ) { + + for ( i = 0; i < il; i ++ ) { + + if ( !used[ i ] && influences[ i ] > candidateInfluence ) { + + candidate = i; + candidateInfluence = influences[ candidate ]; + + } + } + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglMorphTargetsBuffers[ candidate ] ); + _gl.vertexAttribPointer( attributes[ "morphTarget" + m ], 3, _gl.FLOAT, false, 0, 0 ); + + object.__webglMorphTargetInfluences[ m ] = candidateInfluence; + + used[ candidate ] = 1; + candidateInfluence = -1; + m ++; + } + } + + // load updated influences uniform + + if( material.program.uniforms.morphTargetInfluences !== null ) { + + _gl.uniform1fv( material.program.uniforms.morphTargetInfluences, object.__webglMorphTargetInfluences ); + + } + + } + + + function renderBufferImmediate( object, program, shading ) { + + if ( ! object.__webglVertexBuffer ) object.__webglVertexBuffer = _gl.createBuffer(); + if ( ! object.__webglNormalBuffer ) object.__webglNormalBuffer = _gl.createBuffer(); + + if ( object.hasPos ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglVertexBuffer ); + _gl.bufferData( _gl.ARRAY_BUFFER, object.positionArray, _gl.DYNAMIC_DRAW ); + _gl.enableVertexAttribArray( program.attributes.position ); + _gl.vertexAttribPointer( program.attributes.position, 3, _gl.FLOAT, false, 0, 0 ); + + } + + if ( object.hasNormal ) { + + _gl.bindBuffer( _gl.ARRAY_BUFFER, object.__webglNormalBuffer ); + + if ( shading == THREE.FlatShading ) { + + var nx, ny, nz, + nax, nbx, ncx, nay, nby, ncy, naz, nbz, ncz, + normalArray, + i, il = object.count * 3; + + for( i = 0; i < il; i += 9 ) { + + normalArray = object.normalArray; + + nax = normalArray[ i ]; + nay = normalArray[ i + 1 ]; + naz = normalArray[ i + 2 ]; + + nbx = normalArray[ i + 3 ]; + nby = normalArray[ i + 4 ]; + nbz = normalArray[ i + 5 ]; + + ncx = normalArray[ i + 6 ]; + ncy = normalArray[ i + 7 ]; + ncz = normalArray[ i + 8 ]; + + nx = ( nax + nbx + ncx ) / 3; + ny = ( nay + nby + ncy ) / 3; + nz = ( naz + nbz + ncz ) / 3; + + normalArray[ i ] = nx; + normalArray[ i + 1 ] = ny; + normalArray[ i + 2 ] = nz; + + normalArray[ i + 3 ] = nx; + normalArray[ i + 4 ] = ny; + normalArray[ i + 5 ] = nz; + + normalArray[ i + 6 ] = nx; + normalArray[ i + 7 ] = ny; + normalArray[ i + 8 ] = nz; + + } + + } + + _gl.bufferData( _gl.ARRAY_BUFFER, object.normalArray, _gl.DYNAMIC_DRAW ); + _gl.enableVertexAttribArray( program.attributes.normal ); + _gl.vertexAttribPointer( program.attributes.normal, 3, _gl.FLOAT, false, 0, 0 ); + + } + + _gl.drawArrays( _gl.TRIANGLES, 0, object.count ); + + object.count = 0; + + }; + + function setObjectFaces( object ) { + + if ( _oldDoubleSided != object.doubleSided ) { + + if( object.doubleSided ) { + + _gl.disable( _gl.CULL_FACE ); + + } else { + + _gl.enable( _gl.CULL_FACE ); + + } + + _oldDoubleSided = object.doubleSided; + + } + + if ( _oldFlipSided != object.flipSided ) { + + if( object.flipSided ) { + + _gl.frontFace( _gl.CW ); + + } else { + + _gl.frontFace( _gl.CCW ); + + } + + _oldFlipSided = object.flipSided; + + } + + }; + + function setDepthTest( test ) { + + if ( _oldDepth != test ) { + + if( test ) { + + _gl.enable( _gl.DEPTH_TEST ); + + } else { + + _gl.disable( _gl.DEPTH_TEST ); + + } + + _oldDepth = test; + + } + + }; + + function setPolygonOffset ( polygonoffset, factor, units ) { + + if ( _oldPolygonOffset != polygonoffset ) { + + if ( polygonoffset ) { + + _gl.enable( _gl.POLYGON_OFFSET_FILL ); + + } else { + + _gl.disable( _gl.POLYGON_OFFSET_FILL ); + + } + + _oldPolygonOffset = polygonoffset; + + } + + if ( polygonoffset && ( _oldPolygonOffsetFactor != factor || _oldPolygonOffsetUnits != units ) ) { + + _gl.polygonOffset( factor, units ); + + _oldPolygonOffsetFactor = factor; + _oldPolygonOffsetUnits = units; + + } + + }; + + function computeFrustum( m ) { + + _frustum[ 0 ].set( m.n41 - m.n11, m.n42 - m.n12, m.n43 - m.n13, m.n44 - m.n14 ); + _frustum[ 1 ].set( m.n41 + m.n11, m.n42 + m.n12, m.n43 + m.n13, m.n44 + m.n14 ); + _frustum[ 2 ].set( m.n41 + m.n21, m.n42 + m.n22, m.n43 + m.n23, m.n44 + m.n24 ); + _frustum[ 3 ].set( m.n41 - m.n21, m.n42 - m.n22, m.n43 - m.n23, m.n44 - m.n24 ); + _frustum[ 4 ].set( m.n41 - m.n31, m.n42 - m.n32, m.n43 - m.n33, m.n44 - m.n34 ); + _frustum[ 5 ].set( m.n41 + m.n31, m.n42 + m.n32, m.n43 + m.n33, m.n44 + m.n34 ); + + var i, plane; + + for ( i = 0; i < 6; i ++ ) { + + plane = _frustum[ i ]; + plane.divideScalar( Math.sqrt( plane.x * plane.x + plane.y * plane.y + plane.z * plane.z ) ); + + } + + }; + + function isInFrustum( object ) { + + var distance, matrix = object.matrixWorld, + radius = - object.geometry.boundingSphere.radius * Math.max( object.scale.x, Math.max( object.scale.y, object.scale.z ) ); + + for ( var i = 0; i < 6; i ++ ) { + + distance = _frustum[ i ].x * matrix.n14 + _frustum[ i ].y * matrix.n24 + _frustum[ i ].z * matrix.n34 + _frustum[ i ].w; + if ( distance <= radius ) return false; + + } + + return true; + + }; + + function addToFixedArray( where, what ) { + + where.list[ where.count ] = what; + where.count += 1; + + }; + + function unrollImmediateBufferMaterials( globject ) { + + var i, l, m, ml, material, + object = globject.object, + opaque = globject.opaque, + transparent = globject.transparent; + + transparent.count = 0; + opaque.count = 0; + + for ( m = 0, ml = object.materials.length; m < ml; m++ ) { + + material = object.materials[ m ]; + material.transparent ? addToFixedArray( transparent, material ) : addToFixedArray( opaque, material ); + + } + + }; + + function unrollBufferMaterials( globject ) { + + var i, l, m, ml, material, meshMaterial, + object = globject.object, + buffer = globject.buffer, + opaque = globject.opaque, + transparent = globject.transparent; + + transparent.count = 0; + opaque.count = 0; + + for ( m = 0, ml = object.materials.length; m < ml; m++ ) { + + meshMaterial = object.materials[ m ]; + + if ( meshMaterial instanceof THREE.MeshFaceMaterial ) { + + for ( i = 0, l = buffer.materials.length; i < l; i++ ) { + + material = buffer.materials[ i ]; + if ( material ) material.transparent ? addToFixedArray( transparent, material ) : addToFixedArray( opaque, material ); + + } + + } else { + + material = meshMaterial; + if ( material ) material.transparent ? addToFixedArray( transparent, material ) : addToFixedArray( opaque, material ); + + } + + } + + }; + + + function painterSort( a, b ) { + + return b.z - a.z; + + }; + + this.render = function( scene, camera, renderTarget, forceClear ) { + + var i, program, opaque, transparent, material, + o, ol, oil, webglObject, object, buffer, + lights = scene.lights, + fog = scene.fog; + + _this.data.vertices = 0; + _this.data.faces = 0; + _this.data.drawCalls = 0; + + camera.matrixAutoUpdate && camera.update( undefined, true ); + + scene.update( undefined, false, camera ); + + camera.matrixWorldInverse.flattenToArray( _viewMatrixArray ); + camera.projectionMatrix.flattenToArray( _projectionMatrixArray ); + + _projScreenMatrix.multiply( camera.projectionMatrix, camera.matrixWorldInverse ); + computeFrustum( _projScreenMatrix ); + + this.initWebGLObjects( scene ); + + setRenderTarget( renderTarget ); + + if ( this.autoClear || forceClear ) { + + this.clear(); + + } + + // set matrices + + ol = scene.__webglObjects.length; + + for ( o = 0; o < ol; o++ ) { + + webglObject = scene.__webglObjects[ o ]; + object = webglObject.object; + + if ( object.visible ) { + + if ( ! ( object instanceof THREE.Mesh ) || isInFrustum( object ) ) { + + object.matrixWorld.flattenToArray( object._objectMatrixArray ); + + setupMatrices( object, camera ); + + unrollBufferMaterials( webglObject ); + + webglObject.render = true; + + if ( this.sortObjects ) { + + if ( webglObject.object.renderDepth ) { + + webglObject.z = webglObject.object.renderDepth; + + } else { + + _vector3.copy( object.position ); + _projScreenMatrix.multiplyVector3( _vector3 ); + + webglObject.z = _vector3.z; + + } + + } + + } else { + + webglObject.render = false; + + } + + } else { + + webglObject.render = false; + + } + + } + + if ( this.sortObjects ) { + + scene.__webglObjects.sort( painterSort ); + + } + + oil = scene.__webglObjectsImmediate.length; + + for ( o = 0; o < oil; o++ ) { + + webglObject = scene.__webglObjectsImmediate[ o ]; + object = webglObject.object; + + if ( object.visible ) { + + if( object.matrixAutoUpdate ) { + + object.matrixWorld.flattenToArray( object._objectMatrixArray ); + + } + + setupMatrices( object, camera ); + + unrollImmediateBufferMaterials( webglObject ); + + } + + } + + // opaque pass + + setBlending( THREE.NormalBlending ); + + for ( o = 0; o < ol; o ++ ) { + + webglObject = scene.__webglObjects[ o ]; + + if ( webglObject.render ) { + + object = webglObject.object; + buffer = webglObject.buffer; + opaque = webglObject.opaque; + + setObjectFaces( object ); + + for ( i = 0; i < opaque.count; i ++ ) { + + material = opaque.list[ i ]; + + setDepthTest( material.depthTest ); + setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); + renderBuffer( camera, lights, fog, material, buffer, object ); + + } + + } + + } + + // opaque pass (immediate simulator) + + for ( o = 0; o < oil; o++ ) { + + webglObject = scene.__webglObjectsImmediate[ o ]; + object = webglObject.object; + + if ( object.visible ) { + + opaque = webglObject.opaque; + + setObjectFaces( object ); + + for( i = 0; i < opaque.count; i++ ) { + + material = opaque.list[ i ]; + + setDepthTest( material.depthTest ); + setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); + + program = setProgram( camera, lights, fog, material, object ); + object.render( function( object ) { renderBufferImmediate( object, program, material.shading ); } ); + + } + + } + + } + + // transparent pass + + for ( o = 0; o < ol; o ++ ) { + + webglObject = scene.__webglObjects[ o ]; + + if ( webglObject.render ) { + + object = webglObject.object; + buffer = webglObject.buffer; + transparent = webglObject.transparent; + + setObjectFaces( object ); + + for ( i = 0; i < transparent.count; i ++ ) { + + material = transparent.list[ i ]; + + setBlending( material.blending ); + setDepthTest( material.depthTest ); + setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); + + renderBuffer( camera, lights, fog, material, buffer, object ); + + } + + } + + } + + // transparent pass (immediate simulator) + + for ( o = 0; o < oil; o++ ) { + + webglObject = scene.__webglObjectsImmediate[ o ]; + object = webglObject.object; + + if ( object.visible ) { + + transparent = webglObject.transparent; + + setObjectFaces( object ); + + for ( i = 0; i < transparent.count; i ++ ) { + + material = transparent.list[ i ]; + + setBlending( material.blending ); + setDepthTest( material.depthTest ); + setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits ); + + program = setProgram( camera, lights, fog, material, object ); + object.render( function( object ) { renderBufferImmediate( object, program, material.shading ); } ); + + } + + } + + } + + // render 2d + + if ( scene.__webglSprites.length ) { + + renderSprites( scene, camera ); + + } + + // render stencil shadows + + if ( _stencil && scene.__webglShadowVolumes.length && scene.lights.length ) { + + renderStencilShadows( scene ); + + } + + + // render lens flares + + if ( scene.__webglLensFlares.length ) { + + renderLensFlares( scene, camera ); + + } + + + // Generate mipmap if we're using any kind of mipmap filtering + + if ( renderTarget && renderTarget.minFilter !== THREE.NearestFilter && renderTarget.minFilter !== THREE.LinearFilter ) { + + updateRenderTargetMipmap( renderTarget ); + + } + + //_gl.finish(); + + }; + + + + /* + * Stencil Shadows + * method: we're rendering the world in light, then the shadow + * volumes into the stencil and last a big darkening + * quad over the whole thing. This is not how "you're + * supposed to" do stencil shadows but is much faster + * + */ + + function renderStencilShadows( scene ) { + + // setup stencil + + _gl.enable( _gl.POLYGON_OFFSET_FILL ); + _gl.polygonOffset( 0.1, 1.0 ); + _gl.enable( _gl.STENCIL_TEST ); + _gl.enable( _gl.DEPTH_TEST ); + _gl.depthMask( false ); + _gl.colorMask( false, false, false, false ); + + _gl.stencilFunc( _gl.ALWAYS, 1, 0xFF ); + _gl.stencilOpSeparate( _gl.BACK, _gl.KEEP, _gl.INCR, _gl.KEEP ); + _gl.stencilOpSeparate( _gl.FRONT, _gl.KEEP, _gl.DECR, _gl.KEEP ); + + + // loop through all directional lights + + var l, ll = scene.lights.length; + var p; + var light, lights = scene.lights; + var dirLight = []; + var object, geometryGroup, material; + var program; + var p_uniforms; + var m_uniforms; + var attributes; + var o, ol = scene.__webglShadowVolumes.length; + + for ( l = 0; l < ll; l++ ) { + + light = scene.lights[ l ]; + + if ( light instanceof THREE.DirectionalLight && light.castShadow ) { + + dirLight[ 0 ] = -light.position.x; + dirLight[ 1 ] = -light.position.y; + dirLight[ 2 ] = -light.position.z; + + // render all volumes + + for ( o = 0; o < ol; o++ ) { + + object = scene.__webglShadowVolumes[ o ].object; + geometryGroup = scene.__webglShadowVolumes[ o ].buffer; + material = object.materials[ 0 ]; + + if ( !material.program ) _this.initMaterial( material, lights, undefined, object ); + + program = material.program, + p_uniforms = program.uniforms, + m_uniforms = material.uniforms, + attributes = program.attributes; + + if ( _currentProgram !== program ) { + + _gl.useProgram( program ); + _currentProgram = program; + + _gl.uniformMatrix4fv( p_uniforms.projectionMatrix, false, _projectionMatrixArray ); + _gl.uniformMatrix4fv( p_uniforms.viewMatrix, false, _viewMatrixArray ); + _gl.uniform3fv( p_uniforms.directionalLightDirection, dirLight ); + } + + + object.matrixWorld.flattenToArray( object._objectMatrixArray ); + _gl.uniformMatrix4fv( p_uniforms.objectMatrix, false, object._objectMatrixArray ); + + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglVertexBuffer ); + _gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 ); + + _gl.bindBuffer( _gl.ARRAY_BUFFER, geometryGroup.__webglNormalBuffer ); + _gl.vertexAttribPointer( attributes.normal, 3, _gl.FLOAT, false, 0, 0 ); + + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, geometryGroup.__webglFaceBuffer ); + + _gl.cullFace( _gl.FRONT ); + _gl.drawElements( _gl.TRIANGLES, geometryGroup.__webglFaceCount, _gl.UNSIGNED_SHORT, 0 ); + + _gl.cullFace( _gl.BACK ); + _gl.drawElements( _gl.TRIANGLES, geometryGroup.__webglFaceCount, _gl.UNSIGNED_SHORT, 0 ); + + } + + } + + } + + + // setup color+stencil + + _gl.disable( _gl.POLYGON_OFFSET_FILL ); + _gl.colorMask( true, true, true, true ); + _gl.stencilFunc( _gl.NOTEQUAL, 0, 0xFF ); + _gl.stencilOp( _gl.KEEP, _gl.KEEP, _gl.KEEP ); + _gl.disable( _gl.DEPTH_TEST ); + + + // draw darkening polygon + + _oldBlending = -1; + _currentProgram = _stencilShadow.program; + + _gl.useProgram( _stencilShadow.program ); + _gl.uniformMatrix4fv( _stencilShadow.projectionLocation, false, _projectionMatrixArray ); + _gl.uniform1f( _stencilShadow.darknessLocation, _stencilShadow.darkness ); + + _gl.bindBuffer( _gl.ARRAY_BUFFER, _stencilShadow.vertexBuffer ); + _gl.vertexAttribPointer( _stencilShadow.vertexLocation, 3, _gl.FLOAT, false, 0, 0 ); + _gl.enableVertexAttribArray( _stencilShadow.vertexLocation ); + + _gl.blendFunc( _gl.ONE, _gl.ONE_MINUS_SRC_ALPHA ); + _gl.blendEquation( _gl.FUNC_ADD ); + + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _stencilShadow.elementBuffer ); + _gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 ); + + + // disable stencil + + _gl.disable( _gl.STENCIL_TEST ); + _gl.enable( _gl.DEPTH_TEST ); + _gl.depthMask( _currentDepthMask ); + + } + + + /* + * Render sprites + * + */ + + function renderSprites( scene, camera ) { + + var o, ol, object; + var attributes = _sprite.attributes; + var uniforms = _sprite.uniforms; + var anyCustom = false; + var invAspect = _viewportHeight / _viewportWidth; + var size, scale = []; + var screenPosition; + var halfViewportWidth = _viewportWidth * 0.5; + var halfViewportHeight = _viewportHeight * 0.5; + var mergeWith3D = true; + + // setup gl + + _gl.useProgram( _sprite.program ); + _currentProgram = _sprite.program; + _oldBlending = -1; + + if ( !_spriteAttributesEnabled ) { + + _gl.enableVertexAttribArray( _sprite.attributes.position ); + _gl.enableVertexAttribArray( _sprite.attributes.uv ); + + _spriteAttributesEnabled = true; + + } + + _gl.disable( _gl.CULL_FACE ); + _gl.enable( _gl.BLEND ); + _gl.depthMask( true ); + + _gl.bindBuffer( _gl.ARRAY_BUFFER, _sprite.vertexBuffer ); + _gl.vertexAttribPointer( attributes.position, 2, _gl.FLOAT, false, 2 * 8, 0 ); + _gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 2 * 8, 8 ); + + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _sprite.elementBuffer ); + + _gl.uniformMatrix4fv( uniforms.projectionMatrix, false, _projectionMatrixArray ); + + _gl.activeTexture( _gl.TEXTURE0 ); + _gl.uniform1i( uniforms.map, 0 ); + + // update positions and sort + + for( o = 0, ol = scene.__webglSprites.length; o < ol; o++ ) { + + object = scene.__webglSprites[ o ]; + + if( !object.useScreenCoordinates ) { + + object._modelViewMatrix.multiplyToArray( camera.matrixWorldInverse, object.matrixWorld, object._modelViewMatrixArray ); + object.z = -object._modelViewMatrix.n34; + + } else { + + object.z = -object.position.z; + + } + + } + + scene.__webglSprites.sort( painterSort ); + + // render all non-custom shader sprites + + for ( o = 0, ol = scene.__webglSprites.length; o < ol; o++ ) { + + object = scene.__webglSprites[ o ]; + + if ( object.material === undefined ) { + + if ( object.map && object.map.image && object.map.image.width ) { + + if ( object.useScreenCoordinates ) { + + _gl.uniform1i( uniforms.useScreenCoordinates, 1 ); + _gl.uniform3f( uniforms.screenPosition, ( object.position.x - halfViewportWidth ) / halfViewportWidth, + ( halfViewportHeight - object.position.y ) / halfViewportHeight, + Math.max( 0, Math.min( 1, object.position.z ))); + + } else { + + + + _gl.uniform1i( uniforms.useScreenCoordinates, 0 ); + _gl.uniform1i( uniforms.affectedByDistance, object.affectedByDistance ? 1 : 0 ); + _gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, object._modelViewMatrixArray ); + + } + + size = object.map.image.width / ( object.scaleByViewport ? _viewportHeight : 1 ); + scale[ 0 ] = size * invAspect * object.scale.x; + scale[ 1 ] = size * object.scale.y; + + _gl.uniform2f( uniforms.uvScale, object.uvScale.x, object.uvScale.y ); + _gl.uniform2f( uniforms.uvOffset, object.uvOffset.x, object.uvOffset.y ); + _gl.uniform2f( uniforms.alignment, object.alignment.x, object.alignment.y ); + _gl.uniform1f( uniforms.opacity, object.opacity ); + _gl.uniform1f( uniforms.rotation, object.rotation ); + _gl.uniform2fv( uniforms.scale, scale ); + + if ( object.mergeWith3D && !mergeWith3D ) { + + _gl.enable( _gl.DEPTH_TEST ); + mergeWith3D = true; + + } else if ( !object.mergeWith3D && mergeWith3D ) { + + _gl.disable( _gl.DEPTH_TEST ); + mergeWith3D = false; + + } + + setBlending( object.blending ); + setTexture( object.map, 0 ); + + _gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 ); + } + + } else { + + anyCustom = true; + + } + + } + + + // loop through all custom + + /* + if( anyCustom ) { + + } + */ + + // restore gl + + _gl.enable( _gl.CULL_FACE ); + _gl.enable( _gl.DEPTH_TEST ); + _gl.depthMask( _currentDepthMask ); + + } + + + + /* + * Render lens flares + * Method: renders 16x16 0xff00ff-colored points scattered over the light source area, + * reads these back and calculates occlusion. + * Then LensFlare.updateLensFlares() is called to re-position and + * update transparency of flares. Then they are rendered. + * + */ + + function renderLensFlares( scene, camera ) { + + var object, objectZ, geometryGroup, material; + var o, ol = scene.__webglLensFlares.length; + var f, fl, flare; + var tempPosition = new THREE.Vector3(); + var invAspect = _viewportHeight / _viewportWidth; + var halfViewportWidth = _viewportWidth * 0.5; + var halfViewportHeight = _viewportHeight * 0.5; + var size = 16 / _viewportHeight; + var scale = [ size * invAspect, size ]; + var screenPosition = [ 1, 1, 0 ]; + var screenPositionPixels = [ 1, 1 ]; + var sampleX, sampleY, readBackPixels = _lensFlare.readBackPixels; + var sampleMidX = 7 * 4; + var sampleMidY = 7 * 16 * 4; + var sampleIndex, visibility; + var uniforms = _lensFlare.uniforms; + var attributes = _lensFlare.attributes; + + + // set lensflare program and reset blending + + _gl.useProgram( _lensFlare.program ); + _currentProgram = _lensFlare.program; + _oldBlending = -1; + + + if ( ! _lensFlareAttributesEnabled ) { + + _gl.enableVertexAttribArray( _lensFlare.attributes.vertex ); + _gl.enableVertexAttribArray( _lensFlare.attributes.uv ); + + _lensFlareAttributesEnabled = true; + + } + + // loop through all lens flares to update their occlusion and positions + // setup gl and common used attribs/unforms + + _gl.uniform1i( uniforms.occlusionMap, 0 ); + _gl.uniform1i( uniforms.map, 1 ); + + _gl.bindBuffer( _gl.ARRAY_BUFFER, _lensFlare.vertexBuffer ); + _gl.vertexAttribPointer( attributes.vertex, 2, _gl.FLOAT, false, 2 * 8, 0 ); + _gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 2 * 8, 8 ); + + + _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _lensFlare.elementBuffer ); + + _gl.disable( _gl.CULL_FACE ); + _gl.depthMask( false ); + + _gl.activeTexture( _gl.TEXTURE0 ); + _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.occlusionTexture ); + + _gl.activeTexture( _gl.TEXTURE1 ); + + for ( o = 0; o < ol; o ++ ) { + + // calc object screen position + + object = scene.__webglLensFlares[ o ].object; + + tempPosition.set( object.matrixWorld.n14, object.matrixWorld.n24, object.matrixWorld.n34 ); + + camera.matrixWorldInverse.multiplyVector3( tempPosition ); + objectZ = tempPosition.z; + camera.projectionMatrix.multiplyVector3( tempPosition ); + + + // setup arrays for gl programs + + screenPosition[ 0 ] = tempPosition.x; + screenPosition[ 1 ] = tempPosition.y; + screenPosition[ 2 ] = tempPosition.z; + + screenPositionPixels[ 0 ] = screenPosition[ 0 ] * halfViewportWidth + halfViewportWidth; + screenPositionPixels[ 1 ] = screenPosition[ 1 ] * halfViewportHeight + halfViewportHeight; + + + // screen cull + + if ( _lensFlare.hasVertexTexture || ( screenPositionPixels[ 0 ] > 0 && + screenPositionPixels[ 0 ] < _viewportWidth && + screenPositionPixels[ 1 ] > 0 && + screenPositionPixels[ 1 ] < _viewportHeight )) { + + + // save current RGB to temp texture + + _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.tempTexture ); + _gl.copyTexImage2D( _gl.TEXTURE_2D, 0, _gl.RGB, screenPositionPixels[ 0 ] - 8, screenPositionPixels[ 1 ] - 8, 16, 16, 0 ); + + + // render pink quad + + _gl.uniform1i( uniforms.renderType, 0 ); + _gl.uniform2fv( uniforms.scale, scale ); + _gl.uniform3fv( uniforms.screenPosition, screenPosition ); + + _gl.disable( _gl.BLEND ); + _gl.enable( _gl.DEPTH_TEST ); + + _gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 ); + + + // copy result to occlusionMap + + _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.occlusionTexture ); + _gl.copyTexImage2D( _gl.TEXTURE_2D, 0, _gl.RGBA, screenPositionPixels[ 0 ] - 8, screenPositionPixels[ 1 ] - 8, 16, 16, 0 ); + + + // restore graphics + + _gl.uniform1i( uniforms.renderType, 1 ); + _gl.disable( _gl.DEPTH_TEST ); + _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.tempTexture ); + _gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 ); + + + // update object positions + + object.positionScreen.x = screenPosition[ 0 ]; + object.positionScreen.y = screenPosition[ 1 ]; + object.positionScreen.z = screenPosition[ 2 ]; + + if ( object.customUpdateCallback ) { + + object.customUpdateCallback( object ); + + } else { + + object.updateLensFlares(); + + } + + + // render flares + + _gl.uniform1i( uniforms.renderType, 2 ); + _gl.enable( _gl.BLEND ); + + for ( f = 0, fl = object.lensFlares.length; f < fl; f ++ ) { + + flare = object.lensFlares[ f ]; + + if ( flare.opacity > 0.001 && flare.scale > 0.001 ) { + + screenPosition[ 0 ] = flare.x; + screenPosition[ 1 ] = flare.y; + screenPosition[ 2 ] = flare.z; + + size = flare.size * flare.scale / _viewportHeight; + scale[ 0 ] = size * invAspect; + scale[ 1 ] = size; + + _gl.uniform3fv( uniforms.screenPosition, screenPosition ); + _gl.uniform2fv( uniforms.scale, scale ); + _gl.uniform1f( uniforms.rotation, flare.rotation ); + _gl.uniform1f( uniforms.opacity, flare.opacity ); + + setBlending( flare.blending ); + setTexture( flare.texture, 1 ); + + _gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 ); + + } + + } + + } + + } + + // restore gl + + _gl.enable( _gl.CULL_FACE ); + _gl.enable( _gl.DEPTH_TEST ); + _gl.depthMask( _currentDepthMask ); + + }; + + + function setupMatrices( object, camera ) { + + object._modelViewMatrix.multiplyToArray( camera.matrixWorldInverse, object.matrixWorld, object._modelViewMatrixArray ); + THREE.Matrix4.makeInvert3x3( object._modelViewMatrix ).transposeIntoArray( object._normalMatrixArray ); + + }; + + this.initWebGLObjects = function ( scene ) { + + if ( !scene.__webglObjects ) { + + scene.__webglObjects = []; + scene.__webglObjectsImmediate = []; + scene.__webglShadowVolumes = []; + scene.__webglLensFlares = []; + scene.__webglSprites = []; + + } + + while ( scene.__objectsAdded.length ) { + + addObject( scene.__objectsAdded[ 0 ], scene ); + scene.__objectsAdded.splice( 0, 1 ); + + } + + while ( scene.__objectsRemoved.length ) { + + removeObject( scene.__objectsRemoved[ 0 ], scene ); + scene.__objectsRemoved.splice( 0, 1 ); + + } + + // update must be called after objects adding / removal + + for ( var o = 0, ol = scene.__webglObjects.length; o < ol; o ++ ) { + + updateObject( scene.__webglObjects[ o ].object, scene ); + + } + + for ( var o = 0, ol = scene.__webglShadowVolumes.length; o < ol; o ++ ) { + + updateObject( scene.__webglShadowVolumes[ o ].object, scene ); + + } + + for ( var o = 0, ol = scene.__webglLensFlares.length; o < ol; o ++ ) { + + updateObject( scene.__webglLensFlares[ o ].object, scene ); + + } + + /* + for ( var o = 0, ol = scene.__webglSprites.length; o < ol; o ++ ) { + + updateObject( scene.__webglSprites[ o ].object, scene ); + + } + */ + + }; + + function addObject( object, scene ) { + + var g, geometry, geometryGroup; + + if ( object._modelViewMatrix == undefined ) { + + object._modelViewMatrix = new THREE.Matrix4(); + + object._normalMatrixArray = new Float32Array( 9 ); + object._modelViewMatrixArray = new Float32Array( 16 ); + object._objectMatrixArray = new Float32Array( 16 ); + + object.matrixWorld.flattenToArray( object._objectMatrixArray ); + + } + + if ( object instanceof THREE.Mesh ) { + + geometry = object.geometry; + + if ( geometry.geometryGroups == undefined ) { + + sortFacesByMaterial( geometry ); + + } + + // create separate VBOs per geometry chunk + + for ( g in geometry.geometryGroups ) { + + geometryGroup = geometry.geometryGroups[ g ]; + + // initialise VBO on the first access + + if ( ! geometryGroup.__webglVertexBuffer ) { + + createMeshBuffers( geometryGroup ); + initMeshBuffers( geometryGroup, object ); + + geometry.__dirtyVertices = true; + geometry.__dirtyMorphTargets = true; + geometry.__dirtyElements = true; + geometry.__dirtyUvs = true; + geometry.__dirtyNormals = true; + geometry.__dirtyTangents = true; + geometry.__dirtyColors = true; + + } + + // create separate wrapper per each use of VBO + + if ( object instanceof THREE.ShadowVolume ) { + + addBuffer( scene.__webglShadowVolumes, geometryGroup, object ); + + } else { + + addBuffer( scene.__webglObjects, geometryGroup, object ); + + } + + } + + } else if ( object instanceof THREE.LensFlare ) { + + addBuffer( scene.__webglLensFlares, undefined, object ); + + } else if ( object instanceof THREE.Ribbon ) { + + geometry = object.geometry; + + if( ! geometry.__webglVertexBuffer ) { + + createRibbonBuffers( geometry ); + initRibbonBuffers( geometry ); + + geometry.__dirtyVertices = true; + geometry.__dirtyColors = true; + + } + + addBuffer( scene.__webglObjects, geometry, object ); + + } else if ( object instanceof THREE.Line ) { + + geometry = object.geometry; + + if( ! geometry.__webglVertexBuffer ) { + + createLineBuffers( geometry ); + initLineBuffers( geometry ); + + geometry.__dirtyVertices = true; + geometry.__dirtyColors = true; + + } + + addBuffer( scene.__webglObjects, geometry, object ); + + } else if ( object instanceof THREE.ParticleSystem ) { + + geometry = object.geometry; + + if ( ! geometry.__webglVertexBuffer ) { + + createParticleBuffers( geometry ); + initParticleBuffers( geometry ); + + geometry.__dirtyVertices = true; + geometry.__dirtyColors = true; + + } + + addBuffer( scene.__webglObjects, geometry, object ); + + } else if ( THREE.MarchingCubes !== undefined && object instanceof THREE.MarchingCubes ) { + + addBufferImmediate( scene.__webglObjectsImmediate, object ); + + } else if ( object instanceof THREE.Sprite ) { + + scene.__webglSprites.push( object ); + + } + + /*else if ( object instanceof THREE.Particle ) { + + }*/ + + }; + + function areCustomAttributesDirty( geometryGroup ) { + + var a, m, ml, material, materials; + + materials = geometryGroup.__materials; + + for ( m = 0, ml = materials.length; m < ml; m ++ ) { + + material = materials[ m ]; + + if ( material.attributes ) { + + for ( a in material.attributes ) { + + if ( material.attributes[ a ].needsUpdate ) return true; + + } + + } + + } + + + return false; + + }; + + function clearCustomAttributes( geometryGroup ) { + + var a, m, ml, material, materials; + + materials = geometryGroup.__materials; + + for ( m = 0, ml = materials.length; m < ml; m ++ ) { + + material = materials[ m ]; + + if ( material.attributes ) { + + for ( a in material.attributes ) { + + material.attributes[ a ].needsUpdate = false; + + } + + } + + } + + }; + + function updateObject( object, scene ) { + + var g, geometry, geometryGroup, a, customAttributeDirty; + + if ( object instanceof THREE.Mesh ) { + + geometry = object.geometry; + + // check all geometry groups + + for ( g in geometry.geometryGroups ) { + + geometryGroup = geometry.geometryGroups[ g ]; + + customAttributeDirty = areCustomAttributesDirty( geometryGroup ); + + if ( geometry.__dirtyVertices || geometry.__dirtyMorphTargets || geometry.__dirtyElements || + geometry.__dirtyUvs || geometry.__dirtyNormals || + geometry.__dirtyColors || geometry.__dirtyTangents || customAttributeDirty ) { + + setMeshBuffers( geometryGroup, object, _gl.DYNAMIC_DRAW ); + + } + + } + + geometry.__dirtyVertices = false; + geometry.__dirtyMorphTargets = false; + geometry.__dirtyElements = false; + geometry.__dirtyUvs = false; + geometry.__dirtyNormals = false; + geometry.__dirtyTangents = false; + geometry.__dirtyColors = false; + + clearCustomAttributes( geometryGroup ); + + } else if ( object instanceof THREE.Ribbon ) { + + geometry = object.geometry; + + if( geometry.__dirtyVertices || geometry.__dirtyColors ) { + + setRibbonBuffers( geometry, _gl.DYNAMIC_DRAW ); + + } + + geometry.__dirtyVertices = false; + geometry.__dirtyColors = false; + + } else if ( object instanceof THREE.Line ) { + + geometry = object.geometry; + + if( geometry.__dirtyVertices || geometry.__dirtyColors ) { + + setLineBuffers( geometry, _gl.DYNAMIC_DRAW ); + + } + + geometry.__dirtyVertices = false; + geometry.__dirtyColors = false; + + } else if ( object instanceof THREE.ParticleSystem ) { + + geometry = object.geometry; + + if ( geometry.__dirtyVertices || geometry.__dirtyColors || object.sortParticles ) { + + setParticleBuffers( geometry, _gl.DYNAMIC_DRAW, object ); + + } + + geometry.__dirtyVertices = false; + geometry.__dirtyColors = false; + + }/* else if ( THREE.MarchingCubes !== undefined && object instanceof THREE.MarchingCubes ) { + + // it updates itself in render callback + + } else if ( object instanceof THREE.Particle ) { + + }*/ + + /* + delete geometry.vertices; + delete geometry.faces; + delete geometryGroup.faces; + */ + + }; + + function removeInstances( objlist, object ) { + + var o, ol; + + for ( o = objlist.length - 1; o >= 0; o -- ) { + + if ( objlist[ o ].object == object ) { + + objlist.splice( o, 1 ); + + } + + } + + }; + + function removeObject( object, scene ) { + + // must check as shadow volume before mesh (as they are also meshes) + + if ( object instanceof THREE.ShadowVolume ) { + + removeInstances( scene.__webglShadowVolumes, object ); + + } else if ( object instanceof THREE.Mesh || + object instanceof THREE.ParticleSystem || + object instanceof THREE.Ribbon || + object instanceof THREE.Line ) { + + removeInstances( scene.__webglObjects, object ); + + } else if ( object instanceof THREE.Sprite ) { + + removeInstances( scene.__webglSprites, object ); + + } else if ( object instanceof THREE.LensFlare ) { + + removeInstances( scene.__webglLensFlares, object ); + + } else if ( object instanceof THREE.MarchingCubes ) { + + removeInstances( scene.__webglObjectsImmediate, object ); + + } + + }; + + function sortFacesByMaterial( geometry ) { + + // TODO + // Should optimize by grouping faces with ColorFill / ColorStroke materials + // which could then use vertex color attributes instead of each being + // in its separate VBO + + var i, l, f, fl, face, material, materials, vertices, mhash, ghash, hash_map = {}; + var numMorphTargets = geometry.morphTargets !== undefined ? geometry.morphTargets.length : 0; + + geometry.geometryGroups = {}; + + function materialHash( material ) { + + var hash_array = []; + + for ( i = 0, l = material.length; i < l; i++ ) { + + if ( material[ i ] == undefined ) { + + hash_array.push( "undefined" ); + + } else { + + hash_array.push( material[ i ].id ); + + } + + } + + return hash_array.join( '_' ); + + } + + for ( f = 0, fl = geometry.faces.length; f < fl; f++ ) { + + face = geometry.faces[ f ]; + materials = face.materials; + + mhash = materialHash( materials ); + + if ( hash_map[ mhash ] == undefined ) { + + hash_map[ mhash ] = { 'hash': mhash, 'counter': 0 }; + + } + + ghash = hash_map[ mhash ].hash + '_' + hash_map[ mhash ].counter; + + if ( geometry.geometryGroups[ ghash ] == undefined ) { + + geometry.geometryGroups[ ghash ] = { 'faces': [], 'materials': materials, 'vertices': 0, 'numMorphTargets': numMorphTargets }; + + } + + vertices = face instanceof THREE.Face3 ? 3 : 4; + + if ( geometry.geometryGroups[ ghash ].vertices + vertices > 65535 ) { + + hash_map[ mhash ].counter += 1; + ghash = hash_map[ mhash ].hash + '_' + hash_map[ mhash ].counter; + + if ( geometry.geometryGroups[ ghash ] == undefined ) { + + geometry.geometryGroups[ ghash ] = { 'faces': [], 'materials': materials, 'vertices': 0, 'numMorphTargets': numMorphTargets }; + + } + + } + + geometry.geometryGroups[ ghash ].faces.push( f ); + geometry.geometryGroups[ ghash ].vertices += vertices; + + } + + }; + + function addBuffer( objlist, buffer, object ) { + + objlist.push( { + buffer: buffer, object: object, + opaque: { list: [], count: 0 }, + transparent: { list: [], count: 0 } + } ); + + }; + + function addBufferImmediate( objlist, object ) { + + objlist.push( { + object: object, + opaque: { list: [], count: 0 }, + transparent: { list: [], count: 0 } + } ); + + }; + + this.setFaceCulling = function ( cullFace, frontFace ) { + + if ( cullFace ) { + + if ( !frontFace || frontFace == "ccw" ) { + + _gl.frontFace( _gl.CCW ); + + } else { + + _gl.frontFace( _gl.CW ); + + } + + if( cullFace == "back" ) { + + _gl.cullFace( _gl.BACK ); + + } else if( cullFace == "front" ) { + + _gl.cullFace( _gl.FRONT ); + + } else { + + _gl.cullFace( _gl.FRONT_AND_BACK ); + + } + + _gl.enable( _gl.CULL_FACE ); + + } else { + + _gl.disable( _gl.CULL_FACE ); + + } + + }; + + this.supportsVertexTextures = function () { + + return _supportsVertexTextures; + + }; + + function maxVertexTextures() { + + return _gl.getParameter( _gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ); + + }; + + function buildProgram( shaderID, fragmentShader, vertexShader, uniforms, attributes, parameters ) { + + var p, pl, program, code; + var chunks = []; + + // Generate code + + if ( shaderID ) { + + chunks.push( shaderID ); + + } else { + + chunks.push( fragmentShader ); + chunks.push( vertexShader ); + + } + + for ( p in parameters ) { + + chunks.push( p ); + chunks.push( parameters[ p ] ); + + } + + code = chunks.join(); + + // Check if code has been already compiled + + for ( p = 0, pl = _programs.length; p < pl; p ++ ) { + + if ( _programs[ p ].code == code ) { + + // console.log( "Code already compiled." /*: \n\n" + code*/ ); + + return _programs[ p ].program; + + } + + } + + //console.log( "building new program " ); + + // + + program = _gl.createProgram(); + + var prefix_fragment = [ + + "#ifdef GL_ES", + "precision highp float;", + "#endif", + + "#define MAX_DIR_LIGHTS " + parameters.maxDirLights, + "#define MAX_POINT_LIGHTS " + parameters.maxPointLights, + + parameters.fog ? "#define USE_FOG" : "", + parameters.fog instanceof THREE.FogExp2 ? "#define FOG_EXP2" : "", + + parameters.map ? "#define USE_MAP" : "", + parameters.envMap ? "#define USE_ENVMAP" : "", + parameters.lightMap ? "#define USE_LIGHTMAP" : "", + parameters.vertexColors ? "#define USE_COLOR" : "", + + "uniform mat4 viewMatrix;", + "uniform vec3 cameraPosition;", + "" + + ].join("\n"); + + var prefix_vertex = [ + + _supportsVertexTextures ? "#define VERTEX_TEXTURES" : "", + + "#define MAX_DIR_LIGHTS " + parameters.maxDirLights, + "#define MAX_POINT_LIGHTS " + parameters.maxPointLights, + + "#define MAX_BONES " + parameters.maxBones, + + parameters.map ? "#define USE_MAP" : "", + parameters.envMap ? "#define USE_ENVMAP" : "", + parameters.lightMap ? "#define USE_LIGHTMAP" : "", + parameters.vertexColors ? "#define USE_COLOR" : "", + parameters.skinning ? "#define USE_SKINNING" : "", + parameters.morphTargets ? "#define USE_MORPHTARGETS" : "", + + parameters.sizeAttenuation ? "#define USE_SIZEATTENUATION" : "", + + "uniform mat4 objectMatrix;", + "uniform mat4 modelViewMatrix;", + "uniform mat4 projectionMatrix;", + "uniform mat4 viewMatrix;", + "uniform mat3 normalMatrix;", + "uniform vec3 cameraPosition;", + + "uniform mat4 cameraInverseMatrix;", + + "attribute vec3 position;", + "attribute vec3 normal;", + "attribute vec2 uv;", + "attribute vec2 uv2;", + + "#ifdef USE_COLOR", + + "attribute vec3 color;", + + "#endif", + + "#ifdef USE_MORPHTARGETS", + + "attribute vec3 morphTarget0;", + "attribute vec3 morphTarget1;", + "attribute vec3 morphTarget2;", + "attribute vec3 morphTarget3;", + "attribute vec3 morphTarget4;", + "attribute vec3 morphTarget5;", + "attribute vec3 morphTarget6;", + "attribute vec3 morphTarget7;", + + "#endif", + + "#ifdef USE_SKINNING", + + "attribute vec4 skinVertexA;", + "attribute vec4 skinVertexB;", + "attribute vec4 skinIndex;", + "attribute vec4 skinWeight;", + + "#endif", + + "" + + ].join("\n"); + + _gl.attachShader( program, getShader( "fragment", prefix_fragment + fragmentShader ) ); + _gl.attachShader( program, getShader( "vertex", prefix_vertex + vertexShader ) ); + + _gl.linkProgram( program ); + + if ( !_gl.getProgramParameter( program, _gl.LINK_STATUS ) ) { + + console.error( "Could not initialise shader\n" + "VALIDATE_STATUS: " + _gl.getProgramParameter( program, _gl.VALIDATE_STATUS ) + ", gl error [" + _gl.getError() + "]" ); + + } + + //console.log( prefix_fragment + fragmentShader ); + //console.log( prefix_vertex + vertexShader ); + + program.uniforms = {}; + program.attributes = {}; + + var identifiers, u, a, i; + + // cache uniform locations + + identifiers = [ + + 'viewMatrix', 'modelViewMatrix', 'projectionMatrix', 'normalMatrix', 'objectMatrix', 'cameraPosition', + 'cameraInverseMatrix', 'boneGlobalMatrices', 'morphTargetInfluences' + + ]; + + for ( u in uniforms ) { + + identifiers.push( u ); + + } + + cacheUniformLocations( program, identifiers ); + + // cache attributes locations + + identifiers = [ + + "position", "normal", "uv", "uv2", "tangent", "color", + "skinVertexA", "skinVertexB", "skinIndex", "skinWeight" + + ]; + + for ( i = 0; i < parameters.maxMorphTargets; i++ ) { + + identifiers.push( "morphTarget" + i ); + + } + + for ( a in attributes ) { + + identifiers.push( a ); + + } + + cacheAttributeLocations( program, identifiers ); + + _programs.push( { program: program, code: code } ); + + return program; + + }; + + function loadUniformsSkinning( uniforms, object ) { + + _gl.uniformMatrix4fv( uniforms.cameraInverseMatrix, false, _viewMatrixArray ); + _gl.uniformMatrix4fv( uniforms.boneGlobalMatrices, false, object.boneMatrices ); + + }; + + + function loadUniformsMatrices( uniforms, object ) { + + _gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, object._modelViewMatrixArray ); + _gl.uniformMatrix3fv( uniforms.normalMatrix, false, object._normalMatrixArray ); + + }; + + function loadUniformsGeneric( program, uniforms ) { + + var u, uniform, value, type, location, texture; + + for( u in uniforms ) { + + location = program.uniforms[u]; + if ( !location ) continue; + + uniform = uniforms[u]; + + type = uniform.type; + value = uniform.value; + + if( type == "i" ) { + + _gl.uniform1i( location, value ); + + } else if( type == "f" ) { + + _gl.uniform1f( location, value ); + + } else if( type == "fv1" ) { + + _gl.uniform1fv( location, value ); + + } else if( type == "fv" ) { + + _gl.uniform3fv( location, value ); + + } else if( type == "v2" ) { + + _gl.uniform2f( location, value.x, value.y ); + + } else if( type == "v3" ) { + + _gl.uniform3f( location, value.x, value.y, value.z ); + + } else if( type == "v4" ) { + + _gl.uniform4f( location, value.x, value.y, value.z, value.w ); + + } else if( type == "c" ) { + + _gl.uniform3f( location, value.r, value.g, value.b ); + + } else if( type == "t" ) { + + _gl.uniform1i( location, value ); + + texture = uniform.texture; + + if ( !texture ) continue; + + if ( texture.image instanceof Array && texture.image.length == 6 ) { + + setCubeTexture( texture, value ); + + } else { + + setTexture( texture, value ); + + } + + } + + } + + }; + + function setBlending( blending ) { + + if ( blending != _oldBlending ) { + + switch ( blending ) { + + case THREE.AdditiveBlending: + + _gl.blendEquation( _gl.FUNC_ADD ); + _gl.blendFunc( _gl.SRC_ALPHA, _gl.ONE ); + + break; + + case THREE.SubtractiveBlending: + + // TODO: Find blendFuncSeparate() combination + + _gl.blendEquation( _gl.FUNC_ADD ); + _gl.blendFunc( _gl.ZERO, _gl.ONE_MINUS_SRC_COLOR ); + + break; + + case THREE.MultiplyBlending: + + // TODO: Find blendFuncSeparate() combination + + _gl.blendEquation( _gl.FUNC_ADD ); + _gl.blendFunc( _gl.ZERO, _gl.SRC_COLOR ); + + break; + + default: + + _gl.blendEquationSeparate( _gl.FUNC_ADD, _gl.FUNC_ADD ); + _gl.blendFuncSeparate( _gl.SRC_ALPHA, _gl.ONE_MINUS_SRC_ALPHA, _gl.ONE, _gl.ONE_MINUS_SRC_ALPHA ); + + break; + + } + + _oldBlending = blending; + + } + + }; + + function setTextureParameters( textureType, texture, image ) { + + if ( isPowerOfTwo( image.width ) && isPowerOfTwo( image.height ) ) { + + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, paramThreeToGL( texture.wrapS ) ); + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, paramThreeToGL( texture.wrapT ) ); + + _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, paramThreeToGL( texture.magFilter ) ); + _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, paramThreeToGL( texture.minFilter ) ); + + _gl.generateMipmap( textureType ); + + } else { + + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE ); + _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE ); + + _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterFallback( texture.magFilter ) ); + _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterFallback( texture.minFilter ) ); + + } + + }; + + function setTexture( texture, slot ) { + + if ( texture.needsUpdate ) { + + if ( !texture.__webglInit ) { + + texture.__webglTexture = _gl.createTexture(); + + _gl.bindTexture( _gl.TEXTURE_2D, texture.__webglTexture ); + _gl.texImage2D( _gl.TEXTURE_2D, 0, _gl.RGBA, _gl.RGBA, _gl.UNSIGNED_BYTE, texture.image ); + + texture.__webglInit = true; + + } else { + + _gl.bindTexture( _gl.TEXTURE_2D, texture.__webglTexture ); + // _gl.texImage2D( _gl.TEXTURE_2D, 0, _gl.RGBA, _gl.RGBA, _gl.UNSIGNED_BYTE, texture.image ); + _gl.texSubImage2D( _gl.TEXTURE_2D, 0, 0, 0, _gl.RGBA, _gl.UNSIGNED_BYTE, texture.image ); + + } + + setTextureParameters( _gl.TEXTURE_2D, texture, texture.image ); + _gl.bindTexture( _gl.TEXTURE_2D, null ); + + texture.needsUpdate = false; + + } + + /* + if ( texture.needsUpdate ) { + + if ( texture.__webglTexture ) { + + texture.__webglTexture = _gl.deleteTexture( texture.__webglTexture ); + + } + + texture.__webglTexture = _gl.createTexture(); + + _gl.bindTexture( _gl.TEXTURE_2D, texture.__webglTexture ); + _gl.texImage2D( _gl.TEXTURE_2D, 0, _gl.RGBA, _gl.RGBA, _gl.UNSIGNED_BYTE, texture.image ); + + setTextureParameters( _gl.TEXTURE_2D, texture, texture.image ); + + _gl.bindTexture( _gl.TEXTURE_2D, null ); + + texture.needsUpdate = false; + + } + */ + + _gl.activeTexture( _gl.TEXTURE0 + slot ); + _gl.bindTexture( _gl.TEXTURE_2D, texture.__webglTexture ); + + + }; + + function setCubeTexture( texture, slot ) { + + if ( texture.image.length == 6 ) { + + if ( texture.needsUpdate ) { + + if ( !texture.__webglInit ) { + + texture.image.__webglTextureCube = _gl.createTexture(); + + _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.image.__webglTextureCube ); + + for ( var i = 0; i < 6; ++i ) { + + _gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, _gl.RGBA, _gl.RGBA, _gl.UNSIGNED_BYTE, texture.image[ i ] ); + + } + + texture.__webglInit = true; + + } else { + + _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.image.__webglTextureCube ); + + for ( var i = 0; i < 6; ++i ) { + + // _gl.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, _gl.RGBA, _gl.RGBA, _gl.UNSIGNED_BYTE, texture.image[ i ] ); + _gl.texSubImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, 0, 0, _gl.RGBA, _gl.UNSIGNED_BYTE, texture.image[ i ] ); + + } + + } + + setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, texture.image[0] ); + _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, null ); + + texture.needsUpdate = false; + + } + + _gl.activeTexture( _gl.TEXTURE0 + slot ); + _gl.bindTexture( _gl.TEXTURE_CUBE_MAP, texture.image.__webglTextureCube ); + + } + + }; + + function setRenderTarget( renderTexture ) { + + if ( renderTexture && !renderTexture.__webglFramebuffer ) { + + if( renderTexture.depthBuffer === undefined ) renderTexture.depthBuffer = true; + if( renderTexture.stencilBuffer === undefined ) renderTexture.stencilBuffer = true; + + renderTexture.__webglFramebuffer = _gl.createFramebuffer(); + renderTexture.__webglRenderbuffer = _gl.createRenderbuffer(); + renderTexture.__webglTexture = _gl.createTexture(); + + + // Setup texture + + _gl.bindTexture( _gl.TEXTURE_2D, renderTexture.__webglTexture ); + _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_WRAP_S, paramThreeToGL( renderTexture.wrapS ) ); + _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_WRAP_T, paramThreeToGL( renderTexture.wrapT ) ); + _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MAG_FILTER, paramThreeToGL( renderTexture.magFilter ) ); + _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MIN_FILTER, paramThreeToGL( renderTexture.minFilter ) ); + _gl.texImage2D( _gl.TEXTURE_2D, 0, paramThreeToGL( renderTexture.format ), renderTexture.width, renderTexture.height, 0, paramThreeToGL( renderTexture.format ), paramThreeToGL( renderTexture.type ), null ); + + // Setup render and frame buffer + + _gl.bindRenderbuffer( _gl.RENDERBUFFER, renderTexture.__webglRenderbuffer ); + _gl.bindFramebuffer( _gl.FRAMEBUFFER, renderTexture.__webglFramebuffer ); + + _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D, renderTexture.__webglTexture, 0 ); + + if ( renderTexture.depthBuffer && !renderTexture.stencilBuffer ) { + + _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_COMPONENT16, renderTexture.width, renderTexture.height ); + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderTexture.__webglRenderbuffer ); + + /* For some reason this is not working. Defaulting to RGBA4. + } else if( !renderTexture.depthBuffer && renderTexture.stencilBuffer ) { + + _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.STENCIL_INDEX8, renderTexture.width, renderTexture.height ); + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderTexture.__webglRenderbuffer ); + */ + } else if( renderTexture.depthBuffer && renderTexture.stencilBuffer ) { + + _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTexture.width, renderTexture.height ); + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderTexture.__webglRenderbuffer ); + + } else { + + _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.RGBA4, renderTexture.width, renderTexture.height ); + + } + + + // Release everything + + _gl.bindTexture( _gl.TEXTURE_2D, null ); + _gl.bindRenderbuffer( _gl.RENDERBUFFER, null ); + _gl.bindFramebuffer( _gl.FRAMEBUFFER, null); + + } + + var framebuffer, width, height; + + if ( renderTexture ) { + + framebuffer = renderTexture.__webglFramebuffer; + width = renderTexture.width; + height = renderTexture.height; + + } else { + + framebuffer = null; + width = _viewportWidth; + height = _viewportHeight; + + } + + if ( framebuffer != _currentFramebuffer ) { + + _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ); + _gl.viewport( _viewportX, _viewportY, width, height ); + + _currentFramebuffer = framebuffer; + + } + + }; + + function updateRenderTargetMipmap( renderTarget ) { + + _gl.bindTexture( _gl.TEXTURE_2D, renderTarget.__webglTexture ); + _gl.generateMipmap( _gl.TEXTURE_2D ); + _gl.bindTexture( _gl.TEXTURE_2D, null ); + + }; + + function cacheUniformLocations( program, identifiers ) { + + var i, l, id; + + for( i = 0, l = identifiers.length; i < l; i++ ) { + + id = identifiers[ i ]; + program.uniforms[ id ] = _gl.getUniformLocation( program, id ); + + } + + }; + + function cacheAttributeLocations( program, identifiers ) { + + var i, l, id; + + for( i = 0, l = identifiers.length; i < l; i++ ) { + + id = identifiers[ i ]; + program.attributes[ id ] = _gl.getAttribLocation( program, id ); + + } + + }; + + function getShader( type, string ) { + + var shader; + + if ( type == "fragment" ) { + + shader = _gl.createShader( _gl.FRAGMENT_SHADER ); + + } else if ( type == "vertex" ) { + + shader = _gl.createShader( _gl.VERTEX_SHADER ); + + } + + _gl.shaderSource( shader, string ); + _gl.compileShader( shader ); + + if ( !_gl.getShaderParameter( shader, _gl.COMPILE_STATUS ) ) { + + console.error( _gl.getShaderInfoLog( shader ) ); + console.error( string ); + return null; + + } + + return shader; + + }; + + // fallback filters for non-power-of-2 textures + + function filterFallback( f ) { + + switch ( f ) { + + case THREE.NearestFilter: + case THREE.NearestMipMapNearestFilter: + case THREE.NearestMipMapLinearFilter: return _gl.NEAREST; break; + + case THREE.LinearFilter: + case THREE.LinearMipMapNearestFilter: + case THREE.LinearMipMapLinearFilter: + default: + + return _gl.LINEAR; break; + + } + + }; + + function paramThreeToGL( p ) { + + switch ( p ) { + + case THREE.RepeatWrapping: return _gl.REPEAT; break; + case THREE.ClampToEdgeWrapping: return _gl.CLAMP_TO_EDGE; break; + case THREE.MirroredRepeatWrapping: return _gl.MIRRORED_REPEAT; break; + + case THREE.NearestFilter: return _gl.NEAREST; break; + case THREE.NearestMipMapNearestFilter: return _gl.NEAREST_MIPMAP_NEAREST; break; + case THREE.NearestMipMapLinearFilter: return _gl.NEAREST_MIPMAP_LINEAR; break; + + case THREE.LinearFilter: return _gl.LINEAR; break; + case THREE.LinearMipMapNearestFilter: return _gl.LINEAR_MIPMAP_NEAREST; break; + case THREE.LinearMipMapLinearFilter: return _gl.LINEAR_MIPMAP_LINEAR; break; + + case THREE.ByteType: return _gl.BYTE; break; + case THREE.UnsignedByteType: return _gl.UNSIGNED_BYTE; break; + case THREE.ShortType: return _gl.SHORT; break; + case THREE.UnsignedShortType: return _gl.UNSIGNED_SHORT; break; + case THREE.IntType: return _gl.INT; break; + case THREE.UnsignedShortType: return _gl.UNSIGNED_INT; break; + case THREE.FloatType: return _gl.FLOAT; break; + + case THREE.AlphaFormat: return _gl.ALPHA; break; + case THREE.RGBFormat: return _gl.RGB; break; + case THREE.RGBAFormat: return _gl.RGBA; break; + case THREE.LuminanceFormat: return _gl.LUMINANCE; break; + case THREE.LuminanceAlphaFormat: return _gl.LUMINANCE_ALPHA; break; + + } + + return 0; + + }; + + function isPowerOfTwo( value ) { + + return ( value & ( value - 1 ) ) == 0; + + }; + + function materialNeedsSmoothNormals( material ) { + + return material && material.shading != undefined && material.shading == THREE.SmoothShading; + + }; + + function bufferNeedsSmoothNormals( geometryGroup, object ) { + + var m, ml, i, l, meshMaterial, needsSmoothNormals = false; + + for ( m = 0, ml = object.materials.length; m < ml; m++ ) { + + meshMaterial = object.materials[ m ]; + + if ( meshMaterial instanceof THREE.MeshFaceMaterial ) { + + for ( i = 0, l = geometryGroup.materials.length; i < l; i++ ) { + + if ( materialNeedsSmoothNormals( geometryGroup.materials[ i ] ) ) { + + needsSmoothNormals = true; + break; + + } + + } + + } else { + + if ( materialNeedsSmoothNormals( meshMaterial ) ) { + + needsSmoothNormals = true; + break; + + } + + } + + if ( needsSmoothNormals ) break; + + } + + return needsSmoothNormals; + + }; + + function unrollGroupMaterials( geometryGroup, object ) { + + var m, ml, i, il, + material, meshMaterial, + materials = []; + + for ( m = 0, ml = object.materials.length; m < ml; m++ ) { + + meshMaterial = object.materials[ m ]; + + if ( meshMaterial instanceof THREE.MeshFaceMaterial ) { + + for ( i = 0, l = geometryGroup.materials.length; i < l; i++ ) { + + material = geometryGroup.materials[ i ]; + + if ( material ) { + + materials.push( material ); + + } + + } + + } else { + + material = meshMaterial; + + if ( material ) { + + materials.push( material ); + + } + + } + + } + + return materials; + + }; + + function bufferGuessVertexColorType( materials, geometryGroup, object ) { + + var i, m, ml = materials.length; + + // use vertexColor type from the first material in unrolled materials + + for ( i = 0; i < ml; i++ ) { + + m = materials[ i ]; + + if ( m.vertexColors ) { + + return m.vertexColors; + + } + + } + + return false; + + }; + + function bufferGuessNormalType( materials, geometryGroup, object ) { + + var i, m, ml = materials.length; + + // only MeshBasicMaterial and MeshDepthMaterial don't need normals + + for ( i = 0; i < ml; i++ ) { + + m = materials[ i ]; + + if ( ( m instanceof THREE.MeshBasicMaterial && !m.envMap ) || m instanceof THREE.MeshDepthMaterial ) continue; + + if ( materialNeedsSmoothNormals( m ) ) { + + return THREE.SmoothShading; + + } else { + + return THREE.FlatShading; + + } + + } + + return false; + + }; + + function bufferGuessUVType( materials, geometryGroup, object ) { + + var i, m, ml = materials.length; + + // material must use some texture to require uvs + + for ( i = 0; i < ml; i++ ) { + + m = materials[ i ]; + + if ( m.map || m.lightMap || m instanceof THREE.MeshShaderMaterial ) { + + return true; + + } + + } + + return false; + + }; + + function allocateBones( object ) { + + // default for when object is not specified + // ( for example when prebuilding shader + // to be used with multiple objects ) + // + // - leave some extra space for other uniforms + // - limit here is ANGLE's 254 max uniform vectors + // (up to 54 should be safe) + + var maxBones = 50; + + if ( object !== undefined && object instanceof THREE.SkinnedMesh ) { + + maxBones = object.bones.length; + + } + + return maxBones; + + }; + + function allocateLights( lights, maxLights ) { + + var l, ll, light, dirLights, pointLights, maxDirLights, maxPointLights; + dirLights = pointLights = maxDirLights = maxPointLights = 0; + + for ( l = 0, ll = lights.length; l < ll; l++ ) { + + light = lights[ l ]; + + if ( light instanceof THREE.DirectionalLight ) dirLights++; + if ( light instanceof THREE.PointLight ) pointLights++; + + } + + if ( ( pointLights + dirLights ) <= maxLights ) { + + maxDirLights = dirLights; + maxPointLights = pointLights; + + } else { + + maxDirLights = Math.ceil( maxLights * dirLights / ( pointLights + dirLights ) ); + maxPointLights = maxLights - maxDirLights; + + } + + return { 'directional' : maxDirLights, 'point' : maxPointLights }; + + }; + + /* DEBUG + function getGLParams() { + + var params = { + + 'MAX_VARYING_VECTORS': _gl.getParameter( _gl.MAX_VARYING_VECTORS ), + 'MAX_VERTEX_ATTRIBS': _gl.getParameter( _gl.MAX_VERTEX_ATTRIBS ), + + 'MAX_TEXTURE_IMAGE_UNITS': _gl.getParameter( _gl.MAX_TEXTURE_IMAGE_UNITS ), + 'MAX_VERTEX_TEXTURE_IMAGE_UNITS': _gl.getParameter( _gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ), + 'MAX_COMBINED_TEXTURE_IMAGE_UNITS' : _gl.getParameter( _gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS ), + + 'MAX_VERTEX_UNIFORM_VECTORS': _gl.getParameter( _gl.MAX_VERTEX_UNIFORM_VECTORS ), + 'MAX_FRAGMENT_UNIFORM_VECTORS': _gl.getParameter( _gl.MAX_FRAGMENT_UNIFORM_VECTORS ) + } + + return params; + }; + + function dumpObject( obj ) { + + var p, str = ""; + for ( p in obj ) { + + str += p + ": " + obj[p] + "\n"; + + } + + return str; + } + */ +}; + +/** + * @author szimek / https://github.com/szimek/ + */ + +THREE.WebGLRenderTarget = function ( width, height, options ) { + + this.width = width; + this.height = height; + + options = options || {}; + + this.wrapS = options.wrapS !== undefined ? options.wrapS : THREE.ClampToEdgeWrapping; + this.wrapT = options.wrapT !== undefined ? options.wrapT : THREE.ClampToEdgeWrapping; + + this.magFilter = options.magFilter !== undefined ? options.magFilter : THREE.LinearFilter; + this.minFilter = options.minFilter !== undefined ? options.minFilter : THREE.LinearMipMapLinearFilter; + + this.offset = new THREE.Vector2( 0, 0 ); + this.repeat = new THREE.Vector2( 1, 1 ); + + this.format = options.format !== undefined ? options.format : THREE.RGBAFormat; + this.type = options.type !== undefined ? options.type : THREE.UnsignedByteType; + + this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true; + this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : true; + +}; +/** + * @author mikael emtinger / http://gomo.se/ + */ + +THREE.SoundRenderer = function() { + + this.volume = 1; + this.domElement = document.createElement( "div" ); + this.domElement.id = "THREESound"; + + this.cameraPosition = new THREE.Vector3(); + this.soundPosition = new THREE.Vector3(); + + this.render = function ( scene, camera, callSceneUpdate ) { + + if ( callSceneUpdate ) { + + scene.update( undefined, false, camera ); + + } + + var sound; + var sounds = scene.sounds; + var s, l = sounds.length; + + for ( s = 0; s < l; s++ ) { + + sound = sounds[ s ]; + + this.soundPosition.set( + + sound.matrixWorld.n14, + sound.matrixWorld.n24, + sound.matrixWorld.n34 + + ); + + this.soundPosition.subSelf( camera.position ); + + if( sound.isPlaying && sound.isLoaded ) { + + if ( !sound.isAddedToDOM ) { + + sound.addToDOM( this.domElement ); + + } + + sound.calculateVolumeAndPan( this.soundPosition ); + + } + + } + + } + +} +/** + * @author mr.doob / http://mrdoob.com/ + */ + +THREE.RenderableVertex = function () { + + this.positionWorld = new THREE.Vector3(); + this.positionScreen = new THREE.Vector4(); + + this.visible = true; + +}; + +THREE.RenderableVertex.prototype.copy = function ( vertex ) { + + this.positionWorld.copy( vertex.positionWorld ); + this.positionScreen.copy( vertex.positionScreen ); + +} +/** + * @author mr.doob / http://mrdoob.com/ + */ + +THREE.RenderableFace3 = function () { + + this.v1 = new THREE.RenderableVertex(); + this.v2 = new THREE.RenderableVertex(); + this.v3 = new THREE.RenderableVertex(); + + this.centroidWorld = new THREE.Vector3(); + this.centroidScreen = new THREE.Vector3(); + + this.normalWorld = new THREE.Vector3(); + this.vertexNormalsWorld = [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ]; + + this.meshMaterials = null; + this.faceMaterials = null; + this.overdraw = false; + this.uvs = [[]]; + + this.z = null; + +}; +/** + * @author mr.doob / http://mrdoob.com/ + */ + +THREE.RenderableFace4 = function () { + + this.v1 = new THREE.RenderableVertex(); + this.v2 = new THREE.RenderableVertex(); + this.v3 = new THREE.RenderableVertex(); + this.v4 = new THREE.RenderableVertex(); + + this.centroidWorld = new THREE.Vector3(); + this.centroidScreen = new THREE.Vector3(); + + this.normalWorld = new THREE.Vector3(); + this.vertexNormalsWorld = [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ]; + + this.meshMaterials = null; + this.faceMaterials = null; + this.overdraw = false; + this.uvs = [[]]; + + this.z = null; + +}; +/** + * @author mr.doob / http://mrdoob.com/ + */ + +THREE.RenderableObject = function () { + + this.object = null; + this.z = null; + +}; +/** + * @author mr.doob / http://mrdoob.com/ + */ + +THREE.RenderableParticle = function () { + + this.x = null; + this.y = null; + this.z = null; + + this.rotation = null; + this.scale = new THREE.Vector2(); + + this.materials = null; + +}; +/** + * @author mr.doob / http://mrdoob.com/ + */ + +THREE.RenderableLine = function () { + + this.z = null; + + this.v1 = new THREE.RenderableVertex(); + this.v2 = new THREE.RenderableVertex(); + + this.materials = null; + +}; +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.ColorUtils = { + + adjustHSV : function ( color, h, s, v ) { + + var hsv = THREE.ColorUtils.__hsv; + + THREE.ColorUtils.rgbToHsv( color, hsv ); + + hsv.h = THREE.ColorUtils.clamp( hsv.h + h, 0, 1 ); + hsv.s = THREE.ColorUtils.clamp( hsv.s + s, 0, 1 ); + hsv.v = THREE.ColorUtils.clamp( hsv.v + v, 0, 1 ); + + color.setHSV( hsv.h, hsv.s, hsv.v ); + + }, + + // based on MochiKit implementation by Bob Ippolito + + rgbToHsv : function ( color, hsv ) { + + var r = color.r; + var g = color.g; + var b = color.b; + + var max = Math.max( Math.max( r, g ), b ); + var min = Math.min( Math.min( r, g ), b ); + + var hue; + var saturation; + var value = max; + + if ( min == max ) { + + hue = 0; + saturation = 0; + + } else { + + var delta = ( max - min ); + saturation = delta / max; + + if ( r == max ) { + + hue = ( g - b ) / delta; + + } else if ( g == max ) { + + hue = 2 + ( ( b - r ) / delta ); + + } else { + + hue = 4 + ( ( r - g ) / delta ); + } + + hue /= 6; + + if ( hue < 0 ) { + + hue += 1; + + } + + if ( hue > 1 ) { + + hue -= 1; + + } + + } + + if ( hsv === undefined ) { + + hsv = { h: 0, s: 0, v: 0 }; + + } + + hsv.h = hue; + hsv.s = saturation; + hsv.v = value; + + return hsv; + + }, + + clamp: function ( x, a, b ) { + + return x < a ? a : ( x > b ? b : x ); + + } + +}; + +THREE.ColorUtils.__hsv = { h: 0, s: 0, v: 0 };var GeometryUtils = { + + merge: function ( geometry1, object2 /* mesh | geometry */ ) { + + var isMesh = object2 instanceof THREE.Mesh, + vertexOffset = geometry1.vertices.length, + uvPosition = geometry1.faceVertexUvs[ 0 ].length, + geometry2 = isMesh ? object2.geometry : object2, + vertices1 = geometry1.vertices, + vertices2 = geometry2.vertices, + faces1 = geometry1.faces, + faces2 = geometry2.faces, + uvs1 = geometry1.faceVertexUvs[ 0 ], + uvs2 = geometry2.faceVertexUvs[ 0 ]; + + isMesh && object2.matrixAutoUpdate && object2.updateMatrix(); + + for ( var i = 0, il = vertices2.length; i < il; i ++ ) { + + var vertex = vertices2[ i ]; + + var vertexCopy = new THREE.Vertex( vertex.position.clone() ); + + isMesh && object2.matrix.multiplyVector3( vertexCopy.position ); + + vertices1.push( vertexCopy ); + + } + + for ( i = 0, il = faces2.length; i < il; i ++ ) { + + var face = faces2[ i ], faceCopy, normal, color, + faceVertexNormals = face.vertexNormals, + faceVertexColors = face.vertexColors; + + if ( face instanceof THREE.Face3 ) { + + faceCopy = new THREE.Face3( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset ); + + } else if ( face instanceof THREE.Face4 ) { + + faceCopy = new THREE.Face4( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset, face.d + vertexOffset ); + + } + + faceCopy.normal.copy( face.normal ); + + for ( var j = 0, jl = faceVertexNormals.length; j < jl; j ++ ) { + + normal = faceVertexNormals[ j ]; + faceCopy.vertexNormals.push( normal.clone() ); + + } + + faceCopy.color.copy( face.color ); + + for ( var j = 0, jl = faceVertexColors.length; j < jl; j ++ ) { + + color = faceVertexColors[ j ]; + faceCopy.vertexColors.push( color.clone() ); + + } + + faceCopy.materials = face.materials.slice(); + + faceCopy.centroid.copy( face.centroid ); + + faces1.push( faceCopy ); + + } + + for ( i = 0, il = uvs2.length; i < il; i ++ ) { + + var uv = uvs2[ i ], uvCopy = []; + + for ( var j = 0, jl = uv.length; j < jl; j ++ ) { + + uvCopy.push( new THREE.UV( uv[ j ].u, uv[ j ].v ) ); + + } + + uvs1.push( uvCopy ); + + } + + } + +}; +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.ImageUtils = { + + loadTexture: function ( path, mapping, callback ) { + + var image = new Image(), + texture = new THREE.Texture( image, mapping ); + + image.onload = function () { texture.needsUpdate = true; if ( callback ) callback( this ); }; + image.src = path; + + return texture; + + }, + + loadTextureCube: function ( array, mapping, callback ) { + + var i, l, + images = [], + texture = new THREE.Texture( images, mapping ); + + images.loadCount = 0; + + for ( i = 0, l = array.length; i < l; ++i ) { + + images[ i ] = new Image(); + images[ i ].onload = function () { + + images.loadCount += 1; + if ( images.loadCount == 6 ) texture.needsUpdate = true; + if ( callback ) callback( this ); + + }; + + images[ i ].src = array[ i ]; + + } + + return texture; + + } + +}; +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.SceneUtils = { + + addMesh : function ( scene, geometry, scale, x, y, z, rx, ry, rz, material ) { + + var mesh = new THREE.Mesh( geometry, material ); + mesh.scale.x = mesh.scale.y = mesh.scale.z = scale; + mesh.position.x = x; + mesh.position.y = y; + mesh.position.z = z; + mesh.rotation.x = rx; + mesh.rotation.y = ry; + mesh.rotation.z = rz; + scene.addObject( mesh ); + + return mesh; + + }, + + addPanoramaCubeWebGL : function ( scene, size, textureCube ) { + + var shader = THREE.ShaderUtils.lib["cube"]; + shader.uniforms["tCube"].texture = textureCube; + + var material = new THREE.MeshShaderMaterial( { + + fragmentShader: shader.fragmentShader, + vertexShader: shader.vertexShader, + uniforms: shader.uniforms + + } ), + + mesh = new THREE.Mesh( new THREE.CubeGeometry( size, size, size, 1, 1, 1, null, true ), material ); + scene.addObject( mesh ); + + return mesh; + + }, + + addPanoramaCube : function( scene, size, images ) { + + var materials = [], mesh; + + materials.push( new THREE.MeshBasicMaterial( { map: new THREE.Texture( images[ 0 ] ) } ) ); + materials.push( new THREE.MeshBasicMaterial( { map: new THREE.Texture( images[ 1 ] ) } ) ); + materials.push( new THREE.MeshBasicMaterial( { map: new THREE.Texture( images[ 2 ] ) } ) ); + materials.push( new THREE.MeshBasicMaterial( { map: new THREE.Texture( images[ 3 ] ) } ) ); + materials.push( new THREE.MeshBasicMaterial( { map: new THREE.Texture( images[ 4 ] ) } ) ); + materials.push( new THREE.MeshBasicMaterial( { map: new THREE.Texture( images[ 5 ] ) } ) ); + + mesh = new THREE.Mesh( new THREE.Cube( size, size, size, 1, 1, materials, true ), new THREE.MeshFaceMaterial() ); + scene.addObject( mesh ); + + return mesh; + + }, + + addPanoramaCubePlanes : function ( scene, size, images ) { + + + var hsize = size / 2, plane = new THREE.Plane( size, size ), pi = Math.PI, pi2 = Math.PI / 2; + + THREE.SceneUtils.addMesh( scene, plane, 1, 0, 0, -hsize, 0, 0, 0, new THREE.MeshBasicMaterial( { map: new THREE.Texture( images[5] ) } ) ); + THREE.SceneUtils.addMesh( scene, plane, 1, -hsize, 0, 0, 0, pi2, 0, new THREE.MeshBasicMaterial( { map: new THREE.Texture( images[0] ) } ) ); + THREE.SceneUtils.addMesh( scene, plane, 1, hsize, 0, 0, 0, -pi2, 0, new THREE.MeshBasicMaterial( { map: new THREE.Texture( images[1] ) } ) ); + THREE.SceneUtils.addMesh( scene, plane, 1, 0, hsize, 0, pi2, 0, pi, new THREE.MeshBasicMaterial( { map: new THREE.Texture( images[2] ) } ) ); + THREE.SceneUtils.addMesh( scene, plane, 1, 0, -hsize, 0, -pi2, 0, pi, new THREE.MeshBasicMaterial( { map: new THREE.Texture( images[3] ) } ) ); + + }, + + showHierarchy : function ( root, visible ) { + + THREE.SceneUtils.traverseHierarchy( root, function( node ) { node.visible = visible; } ); + + }, + + traverseHierarchy : function ( root, callback ) { + + var n, i, l = root.children.length; + + for ( i = 0; i < l; i ++ ) { + + n = root.children[ i ]; + + callback( n ); + + THREE.SceneUtils.traverseHierarchy( n, callback ); + + } + + } + +}; +/** + * @author alteredq / http://alteredqualia.com/ + * @author mr.doob / http://mrdoob.com/ + */ + +THREE.ShaderUtils = { + + lib: { + + /* ------------------------------------------------------------------------- + // Fresnel shader + // - based on Nvidia Cg tutorial + ------------------------------------------------------------------------- */ + + 'fresnel': { + + uniforms: { + + "mRefractionRatio": { type: "f", value: 1.02 }, + "mFresnelBias": { type: "f", value: 0.1 }, + "mFresnelPower": { type: "f", value: 2.0 }, + "mFresnelScale": { type: "f", value: 1.0 }, + "tCube": { type: "t", value: 1, texture: null } + + }, + + fragmentShader: [ + + "uniform samplerCube tCube;", + + "varying vec3 vReflect;", + "varying vec3 vRefract[3];", + "varying float vReflectionFactor;", + + "void main() {", + + "vec4 reflectedColor = textureCube( tCube, vec3( -vReflect.x, vReflect.yz ) );", + "vec4 refractedColor = vec4( 1.0, 1.0, 1.0, 1.0 );", + + "refractedColor.r = textureCube( tCube, vec3( -vRefract[0].x, vRefract[0].yz ) ).r;", + "refractedColor.g = textureCube( tCube, vec3( -vRefract[1].x, vRefract[1].yz ) ).g;", + "refractedColor.b = textureCube( tCube, vec3( -vRefract[2].x, vRefract[2].yz ) ).b;", + "refractedColor.a = 1.0;", + + "gl_FragColor = mix( refractedColor, reflectedColor, clamp( vReflectionFactor, 0.0, 1.0 ) );", + + "}" + + ].join("\n"), + + vertexShader: [ + + "uniform float mRefractionRatio;", + "uniform float mFresnelBias;", + "uniform float mFresnelScale;", + "uniform float mFresnelPower;", + + "varying vec3 vReflect;", + "varying vec3 vRefract[3];", + "varying float vReflectionFactor;", + + "void main() {", + + "vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", + "vec4 mPosition = objectMatrix * vec4( position, 1.0 );", + + "vec3 nWorld = normalize ( mat3( objectMatrix[0].xyz, objectMatrix[1].xyz, objectMatrix[2].xyz ) * normal );", + + "vec3 I = mPosition.xyz - cameraPosition;", + + "vReflect = reflect( I, nWorld );", + "vRefract[0] = refract( normalize( I ), nWorld, mRefractionRatio );", + "vRefract[1] = refract( normalize( I ), nWorld, mRefractionRatio * 0.99 );", + "vRefract[2] = refract( normalize( I ), nWorld, mRefractionRatio * 0.98 );", + "vReflectionFactor = mFresnelBias + mFresnelScale * pow( 1.0 + dot( normalize( I ), nWorld ), mFresnelPower );", + + "gl_Position = projectionMatrix * mvPosition;", + + "}" + + ].join("\n") + + }, + + /* ------------------------------------------------------------------------- + // Normal map shader + // - Blinn-Phong + // - normal + diffuse + specular + AO + displacement maps + // - 1 point and 1 directional lights + ------------------------------------------------------------------------- */ + + 'normal' : { + + uniforms: { + + "enableAO" : { type: "i", value: 0 }, + "enableDiffuse" : { type: "i", value: 0 }, + "enableSpecular": { type: "i", value: 0 }, + + "tDiffuse" : { type: "t", value: 0, texture: null }, + "tNormal" : { type: "t", value: 2, texture: null }, + "tSpecular" : { type: "t", value: 3, texture: null }, + "tAO" : { type: "t", value: 4, texture: null }, + + "uNormalScale": { type: "f", value: 1.0 }, + + "tDisplacement": { type: "t", value: 5, texture: null }, + "uDisplacementBias": { type: "f", value: -0.5 }, + "uDisplacementScale": { type: "f", value: 2.5 }, + + "uPointLightPos": { type: "v3", value: new THREE.Vector3() }, + "uPointLightColor": { type: "c", value: new THREE.Color( 0xeeeeee ) }, + + "uDirLightPos": { type: "v3", value: new THREE.Vector3() }, + "uDirLightColor": { type: "c", value: new THREE.Color( 0xeeeeee ) }, + + "uAmbientLightColor": { type: "c", value: new THREE.Color( 0x050505 ) }, + + "uDiffuseColor": { type: "c", value: new THREE.Color( 0xeeeeee ) }, + "uSpecularColor": { type: "c", value: new THREE.Color( 0x111111 ) }, + "uAmbientColor": { type: "c", value: new THREE.Color( 0x050505 ) }, + "uShininess": { type: "f", value: 30 } + + }, + + fragmentShader: [ + + "uniform vec3 uDirLightPos;", + + "uniform vec3 uAmbientLightColor;", + "uniform vec3 uDirLightColor;", + "uniform vec3 uPointLightColor;", + + "uniform vec3 uAmbientColor;", + "uniform vec3 uDiffuseColor;", + "uniform vec3 uSpecularColor;", + "uniform float uShininess;", + + "uniform bool enableDiffuse;", + "uniform bool enableSpecular;", + "uniform bool enableAO;", + + "uniform sampler2D tDiffuse;", + "uniform sampler2D tNormal;", + "uniform sampler2D tSpecular;", + "uniform sampler2D tAO;", + + "uniform float uNormalScale;", + + "varying vec3 vTangent;", + "varying vec3 vBinormal;", + "varying vec3 vNormal;", + "varying vec2 vUv;", + + "varying vec3 vPointLightVector;", + "varying vec3 vViewPosition;", + + "void main() {", + + "vec3 diffuseTex = vec3( 1.0, 1.0, 1.0 );", + "vec3 aoTex = vec3( 1.0, 1.0, 1.0 );", + "vec3 specularTex = vec3( 1.0, 1.0, 1.0 );", + + "vec3 normalTex = texture2D( tNormal, vUv ).xyz * 2.0 - 1.0;", + "normalTex.xy *= uNormalScale;", + "normalTex = normalize( normalTex );", + + "if( enableDiffuse )", + "diffuseTex = texture2D( tDiffuse, vUv ).xyz;", + + "if( enableAO )", + "aoTex = texture2D( tAO, vUv ).xyz;", + + "if( enableSpecular )", + "specularTex = texture2D( tSpecular, vUv ).xyz;", + + "mat3 tsb = mat3( vTangent, vBinormal, vNormal );", + "vec3 finalNormal = tsb * normalTex;", + + "vec3 normal = normalize( finalNormal );", + "vec3 viewPosition = normalize( vViewPosition );", + + // point light + + "vec4 pointDiffuse = vec4( 0.0, 0.0, 0.0, 0.0 );", + "vec4 pointSpecular = vec4( 0.0, 0.0, 0.0, 0.0 );", + + "vec3 pointVector = normalize( vPointLightVector );", + "vec3 pointHalfVector = normalize( vPointLightVector + vViewPosition );", + + "float pointDotNormalHalf = dot( normal, pointHalfVector );", + "float pointDiffuseWeight = max( dot( normal, pointVector ), 0.0 );", + + "float pointSpecularWeight = 0.0;", + "if ( pointDotNormalHalf >= 0.0 )", + "pointSpecularWeight = specularTex.r * pow( pointDotNormalHalf, uShininess );", + + "pointDiffuse += vec4( uDiffuseColor, 1.0 ) * pointDiffuseWeight;", + "pointSpecular += vec4( uSpecularColor, 1.0 ) * pointSpecularWeight * pointDiffuseWeight;", + + // directional light + + "vec4 dirDiffuse = vec4( 0.0, 0.0, 0.0, 0.0 );", + "vec4 dirSpecular = vec4( 0.0, 0.0, 0.0, 0.0 );", + + "vec4 lDirection = viewMatrix * vec4( uDirLightPos, 0.0 );", + + "vec3 dirVector = normalize( lDirection.xyz );", + "vec3 dirHalfVector = normalize( lDirection.xyz + vViewPosition );", + + "float dirDotNormalHalf = dot( normal, dirHalfVector );", + "float dirDiffuseWeight = max( dot( normal, dirVector ), 0.0 );", + + "float dirSpecularWeight = 0.0;", + "if ( dirDotNormalHalf >= 0.0 )", + "dirSpecularWeight = specularTex.r * pow( dirDotNormalHalf, uShininess );", + + "dirDiffuse += vec4( uDiffuseColor, 1.0 ) * dirDiffuseWeight;", + "dirSpecular += vec4( uSpecularColor, 1.0 ) * dirSpecularWeight * dirDiffuseWeight;", + + // all lights contribution summation + + "vec4 totalLight = vec4( uAmbientLightColor * uAmbientColor, 1.0 );", + "totalLight += vec4( uDirLightColor, 1.0 ) * ( dirDiffuse + dirSpecular );", + "totalLight += vec4( uPointLightColor, 1.0 ) * ( pointDiffuse + pointSpecular );", + + "gl_FragColor = vec4( totalLight.xyz * aoTex * diffuseTex, 1.0 );", + + "}" + + ].join("\n"), + + vertexShader: [ + + "attribute vec4 tangent;", + + "uniform vec3 uPointLightPos;", + + "#ifdef VERTEX_TEXTURES", + + "uniform sampler2D tDisplacement;", + "uniform float uDisplacementScale;", + "uniform float uDisplacementBias;", + + "#endif", + + "varying vec3 vTangent;", + "varying vec3 vBinormal;", + "varying vec3 vNormal;", + "varying vec2 vUv;", + + "varying vec3 vPointLightVector;", + "varying vec3 vViewPosition;", + + "void main() {", + + "vec4 mPosition = objectMatrix * vec4( position, 1.0 );", + "vViewPosition = cameraPosition - mPosition.xyz;", + + "vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", + "vNormal = normalize( normalMatrix * normal );", + + // tangent and binormal vectors + + "vTangent = normalize( normalMatrix * tangent.xyz );", + + "vBinormal = cross( vNormal, vTangent ) * tangent.w;", + "vBinormal = normalize( vBinormal );", + + "vUv = uv;", + + // point light + + "vec4 lPosition = viewMatrix * vec4( uPointLightPos, 1.0 );", + "vPointLightVector = normalize( lPosition.xyz - mvPosition.xyz );", + + // displacement mapping + + "#ifdef VERTEX_TEXTURES", + + "vec3 dv = texture2D( tDisplacement, uv ).xyz;", + "float df = uDisplacementScale * dv.x + uDisplacementBias;", + "vec4 displacedPosition = vec4( vNormal.xyz * df, 0.0 ) + mvPosition;", + "gl_Position = projectionMatrix * displacedPosition;", + + "#else", + + "gl_Position = projectionMatrix * mvPosition;", + + "#endif", + + "}" + + ].join("\n") + + }, + + /* ------------------------------------------------------------------------- + // Cube map shader + ------------------------------------------------------------------------- */ + + 'cube': { + + uniforms: { "tCube": { type: "t", value: 1, texture: null } }, + + vertexShader: [ + + "varying vec3 vViewPosition;", + + "void main() {", + + "vec4 mPosition = objectMatrix * vec4( position, 1.0 );", + "vViewPosition = cameraPosition - mPosition.xyz;", + + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join("\n"), + + fragmentShader: [ + + "uniform samplerCube tCube;", + + "varying vec3 vViewPosition;", + + "void main() {", + + "vec3 wPos = cameraPosition - vViewPosition;", + "gl_FragColor = textureCube( tCube, vec3( - wPos.x, wPos.yz ) );", + + "}" + + ].join("\n") + + }, + + /* ------------------------------------------------------------------------ + // Convolution shader + // - ported from o3d sample to WebGL / GLSL + // http://o3d.googlecode.com/svn/trunk/samples/convolution.html + ------------------------------------------------------------------------ */ + + 'convolution': { + + uniforms: { + + "tDiffuse" : { type: "t", value: 0, texture: null }, + "uImageIncrement" : { type: "v2", value: new THREE.Vector2( 0.001953125, 0.0 ) }, + "cKernel" : { type: "fv1", value: [] } + + }, + + vertexShader: [ + + "varying vec2 vUv;", + + "uniform vec2 uImageIncrement;", + //"#define KERNEL_SIZE 25.0", + + "void main(void) {", + + "vUv = uv - ((KERNEL_SIZE - 1.0) / 2.0) * uImageIncrement;", + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join("\n"), + + fragmentShader: [ + + "varying vec2 vUv;", + + "uniform sampler2D tDiffuse;", + "uniform vec2 uImageIncrement;", + + //"#define KERNEL_SIZE 25", + "uniform float cKernel[KERNEL_SIZE];", + + "void main(void) {", + + "vec2 imageCoord = vUv;", + "vec4 sum = vec4( 0.0, 0.0, 0.0, 0.0 );", + "for( int i=0; i kMaxKernelSize ) kernelSize = kMaxKernelSize; + halfWidth = ( kernelSize - 1 ) * 0.5 + + values = new Array( kernelSize ); + sum = 0.0; + for ( i = 0; i < kernelSize; ++i ) { + + values[ i ] = gauss( i - halfWidth, sigma ); + sum += values[ i ]; + + } + + // normalize the kernel + + for ( i = 0; i < kernelSize; ++i ) values[ i ] /= sum; + + return values; + + } + +}; +/** + * @author mikael emtinger / http://gomo.se/ + */ + +THREE.AnimationHandler = (function() { + + var playing = []; + var library = {}; + var that = {}; + + + //--- update --- + + that.update = function( deltaTimeMS ) { + + for( var i = 0; i < playing.length; i++ ) + playing[ i ].update( deltaTimeMS ); + + }; + + + //--- add --- + + that.addToUpdate = function( animation ) { + + if( playing.indexOf( animation ) === -1 ) + playing.push( animation ); + + }; + + + //--- remove --- + + that.removeFromUpdate = function( animation ) { + + var index = playing.indexOf( animation ); + + if( index !== -1 ) + playing.splice( index, 1 ); + + }; + + + //--- add --- + + that.add = function( data ) { + + if( library[ data.name ] !== undefined ) + console.log( "THREE.AnimationHandler.add: Warning! " + data.name + " already exists in library. Overwriting." ); + + library[ data.name ] = data; + initData( data ); + + }; + + + //--- get --- + + that.get = function( name ) { + + if( typeof name === "string" ) { + + if( library[ name ] ) { + + return library[ name ]; + + } else { + + console.log( "THREE.AnimationHandler.get: Couldn't find animation " + name ); + return null; + + } + + } else { + + // todo: add simple tween library + + } + + }; + + //--- parse --- + + that.parse = function( root ) { + + // setup hierarchy + + var hierarchy = []; + + if ( root instanceof THREE.SkinnedMesh ) { + + for( var b = 0; b < root.bones.length; b++ ) { + + hierarchy.push( root.bones[ b ] ); + + } + + } else { + + parseRecurseHierarchy( root, hierarchy ); + + } + + return hierarchy; + + }; + + var parseRecurseHierarchy = function( root, hierarchy ) { + + hierarchy.push( root ); + + for( var c = 0; c < root.children.length; c++ ) + parseRecurseHierarchy( root.children[ c ], hierarchy ); + + } + + + //--- init data --- + + var initData = function( data ) { + + if( data.initialized === true ) + return; + + + // loop through all keys + + for( var h = 0; h < data.hierarchy.length; h++ ) { + + for( var k = 0; k < data.hierarchy[ h ].keys.length; k++ ) { + + // remove minus times + + if( data.hierarchy[ h ].keys[ k ].time < 0 ) + data.hierarchy[ h ].keys[ k ].time = 0; + + + // create quaternions + + if( data.hierarchy[ h ].keys[ k ].rot !== undefined && + !( data.hierarchy[ h ].keys[ k ].rot instanceof THREE.Quaternion ) ) { + + var quat = data.hierarchy[ h ].keys[ k ].rot; + data.hierarchy[ h ].keys[ k ].rot = new THREE.Quaternion( quat[0], quat[1], quat[2], quat[3] ); + + } + } + + + // prepare morph target keys + + if( data.hierarchy[ h ].keys[ 0 ].morphTargets !== undefined ) { + + // get all used + + var usedMorphTargets = {}; + + for( var k = 0; k < data.hierarchy[ h ].keys.length; k++ ) { + + for( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m++ ) { + + var morphTargetName = data.hierarchy[ h ].keys[ k ].morphTargets[ m ]; + usedMorphTargets[ morphTargetName ] = -1; + } + + } + + data.hierarchy[ h ].usedMorphTargets = usedMorphTargets; + + + // set all used on all frames + + for( var k = 0; k < data.hierarchy[ h ].keys.length; k++ ) { + + var influences = {}; + + for( var morphTargetName in usedMorphTargets ) { + + for( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m++ ) { + + if( data.hierarchy[ h ].keys[ k ].morphTargets[ m ] === morphTargetName ) { + + influences[ morphTargetName ] = data.hierarchy[ h ].keys[ k ].morphTargetsInfluences[ m ]; + break; + } + + } + + if( m === data.hierarchy[ h ].keys[ k ].morphTargets.length ) { + + influences[ morphTargetName ] = 0; + } + + } + + data.hierarchy[ h ].keys[ k ].morphTargetsInfluences = influences; + } + + } + + + // remove all keys that are on the same time + + for( var k = 1; k < data.hierarchy[ h ].keys.length; k++ ) { + + if( data.hierarchy[ h ].keys[ k ].time === data.hierarchy[ h ].keys[ k - 1 ].time ) { + + data.hierarchy[ h ].keys.splice( k, 1 ); + k--; + + } + + } + + + // set index + + for( var k = 1; k < data.hierarchy[ h ].keys.length; k++ ) { + + data.hierarchy[ h ].keys[ k ].index = k; + + } + + } + + + // JIT + + var lengthInFrames = parseInt( data.length * data.fps, 10 ); + + data.JIT = {}; + data.JIT.hierarchy = []; + + for( var h = 0; h < data.hierarchy.length; h++ ) + data.JIT.hierarchy.push( new Array( lengthInFrames )); + + + // done + + data.initialized = true; + + }; + + + // interpolation types + + that.LINEAR = 0; + that.CATMULLROM = 1; + that.CATMULLROM_FORWARD = 2; + + return that; +}()); +/** + * @author mikael emtinger / http://gomo.se/ + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Animation = function( root, data, interpolationType, JITCompile ) { + + this.root = root; + this.data = THREE.AnimationHandler.get( data ); + this.hierarchy = THREE.AnimationHandler.parse( root ); + this.currentTime = 0; + this.timeScale = 1; + this.isPlaying = false; + this.isPaused = true; + this.loop = true; + this.interpolationType = interpolationType !== undefined ? interpolationType : THREE.AnimationHandler.LINEAR; + this.JITCompile = JITCompile !== undefined ? JITCompile : true; + + this.points = []; + this.target = new THREE.Vector3(); + +}; + +// Play + +THREE.Animation.prototype.play = function( loop, startTimeMS ) { + + if( !this.isPlaying ) { + + this.isPlaying = true; + this.loop = loop !== undefined ? loop : true; + this.currentTime = startTimeMS !== undefined ? startTimeMS : 0; + + + // reset key cache + + var h, hl = this.hierarchy.length, + object; + + for ( h = 0; h < hl; h++ ) { + + object = this.hierarchy[ h ]; + + if ( this.interpolationType !== THREE.AnimationHandler.CATMULLROM_FORWARD ) { + + object.useQuaternion = true; + + } + + object.matrixAutoUpdate = true; + + if ( object.animationCache === undefined ) { + + object.animationCache = {}; + object.animationCache.prevKey = { pos: 0, rot: 0, scl: 0 }; + object.animationCache.nextKey = { pos: 0, rot: 0, scl: 0 }; + object.animationCache.originalMatrix = object instanceof THREE.Bone ? object.skinMatrix : object.matrix; + + } + + var prevKey = object.animationCache.prevKey; + var nextKey = object.animationCache.nextKey; + + prevKey.pos = this.data.hierarchy[ h ].keys[ 0 ]; + prevKey.rot = this.data.hierarchy[ h ].keys[ 0 ]; + prevKey.scl = this.data.hierarchy[ h ].keys[ 0 ]; + + nextKey.pos = this.getNextKeyWith( "pos", h, 1 ); + nextKey.rot = this.getNextKeyWith( "rot", h, 1 ); + nextKey.scl = this.getNextKeyWith( "scl", h, 1 ); + + } + + this.update( 0 ); + + } + + this.isPaused = false; + + THREE.AnimationHandler.addToUpdate( this ); + +}; + + + +// Pause + +THREE.Animation.prototype.pause = function() { + + if( this.isPaused ) { + + THREE.AnimationHandler.addToUpdate( this ); + + } else { + + THREE.AnimationHandler.removeFromUpdate( this ); + + } + + this.isPaused = !this.isPaused; + +}; + + +// Stop + +THREE.Animation.prototype.stop = function() { + + this.isPlaying = false; + this.isPaused = false; + THREE.AnimationHandler.removeFromUpdate( this ); + + + // reset JIT matrix and remove cache + + for ( var h = 0; h < this.hierarchy.length; h++ ) { + + if ( this.hierarchy[ h ].animationCache !== undefined ) { + + if( this.hierarchy[ h ] instanceof THREE.Bone ) { + + this.hierarchy[ h ].skinMatrix = this.hierarchy[ h ].animationCache.originalMatrix; + + } else { + + this.hierarchy[ h ].matrix = this.hierarchy[ h ].animationCache.originalMatrix; + + } + + + delete this.hierarchy[ h ].animationCache; + + } + + } + +}; + + +// Update + +THREE.Animation.prototype.update = function( deltaTimeMS ) { + + // early out + + if( !this.isPlaying ) return; + + + // vars + + var types = [ "pos", "rot", "scl" ]; + var type; + var scale; + var vector; + var prevXYZ, nextXYZ; + var prevKey, nextKey; + var object; + var animationCache; + var frame; + var JIThierarchy = this.data.JIT.hierarchy; + var currentTime, unloopedCurrentTime; + var currentPoint, forwardPoint, angle; + + + // update + + this.currentTime += deltaTimeMS * this.timeScale; + + unloopedCurrentTime = this.currentTime; + currentTime = this.currentTime = this.currentTime % this.data.length; + frame = parseInt( Math.min( currentTime * this.data.fps, this.data.length * this.data.fps ), 10 ); + + + // update + + for ( var h = 0, hl = this.hierarchy.length; h < hl; h++ ) { + + object = this.hierarchy[ h ]; + animationCache = object.animationCache; + + // use JIT? + + if ( this.JITCompile && JIThierarchy[ h ][ frame ] !== undefined ) { + + if( object instanceof THREE.Bone ) { + + object.skinMatrix = JIThierarchy[ h ][ frame ]; + + object.matrixAutoUpdate = false; + object.matrixWorldNeedsUpdate = false; + + } else { + + object.matrix = JIThierarchy[ h ][ frame ]; + + object.matrixAutoUpdate = false; + object.matrixWorldNeedsUpdate = true; + + } + + // use interpolation + + } else { + + // make sure so original matrix and not JIT matrix is set + + if ( this.JITCompile ) { + + if( object instanceof THREE.Bone ) { + + object.skinMatrix = object.animationCache.originalMatrix; + + } else { + + object.matrix = object.animationCache.originalMatrix; + + } + + } + + + // loop through pos/rot/scl + + for ( var t = 0; t < 3; t++ ) { + + // get keys + + type = types[ t ]; + prevKey = animationCache.prevKey[ type ]; + nextKey = animationCache.nextKey[ type ]; + + // switch keys? + + if ( nextKey.time <= unloopedCurrentTime ) { + + // did we loop? + + if ( currentTime < unloopedCurrentTime ) { + + if ( this.loop ) { + + prevKey = this.data.hierarchy[ h ].keys[ 0 ]; + nextKey = this.getNextKeyWith( type, h, 1 ); + + while( nextKey.time < currentTime ) { + + prevKey = nextKey; + nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 ); + + } + + } else { + + this.stop(); + return; + + } + + } else { + + do { + + prevKey = nextKey; + nextKey = this.getNextKeyWith( type, h, nextKey.index + 1 ); + + } while( nextKey.time < currentTime ) + + } + + animationCache.prevKey[ type ] = prevKey; + animationCache.nextKey[ type ] = nextKey; + + } + + + object.matrixAutoUpdate = true; + object.matrixWorldNeedsUpdate = true; + + scale = ( currentTime - prevKey.time ) / ( nextKey.time - prevKey.time ); + prevXYZ = prevKey[ type ]; + nextXYZ = nextKey[ type ]; + + + // check scale error + + if ( scale < 0 || scale > 1 ) { + + console.log( "THREE.Animation.update: Warning! Scale out of bounds:" + scale + " on bone " + h ); + scale = scale < 0 ? 0 : 1; + + } + + // interpolate + + if ( type === "pos" ) { + + vector = object.position; + + if( this.interpolationType === THREE.AnimationHandler.LINEAR ) { + + vector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale; + vector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale; + vector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale; + + } else if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM || + this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { + + this.points[ 0 ] = this.getPrevKeyWith( "pos", h, prevKey.index - 1 )[ "pos" ]; + this.points[ 1 ] = prevXYZ; + this.points[ 2 ] = nextXYZ; + this.points[ 3 ] = this.getNextKeyWith( "pos", h, nextKey.index + 1 )[ "pos" ]; + + scale = scale * 0.33 + 0.33; + + currentPoint = this.interpolateCatmullRom( this.points, scale ); + + vector.x = currentPoint[ 0 ]; + vector.y = currentPoint[ 1 ]; + vector.z = currentPoint[ 2 ]; + + if( this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { + + forwardPoint = this.interpolateCatmullRom( this.points, scale * 1.01 ); + + this.target.set( forwardPoint[ 0 ], forwardPoint[ 1 ], forwardPoint[ 2 ] ); + this.target.subSelf( vector ); + this.target.y = 0; + this.target.normalize(); + + angle = Math.atan2( this.target.x, this.target.z ); + object.rotation.set( 0, angle, 0 ); + + } + + } + + } else if ( type === "rot" ) { + + THREE.Quaternion.slerp( prevXYZ, nextXYZ, object.quaternion, scale ); + + } else if( type === "scl" ) { + + vector = object.scale; + + vector.x = prevXYZ[ 0 ] + ( nextXYZ[ 0 ] - prevXYZ[ 0 ] ) * scale; + vector.y = prevXYZ[ 1 ] + ( nextXYZ[ 1 ] - prevXYZ[ 1 ] ) * scale; + vector.z = prevXYZ[ 2 ] + ( nextXYZ[ 2 ] - prevXYZ[ 2 ] ) * scale; + + } + + } + + } + + } + + // update JIT? + + if ( this.JITCompile ) { + + if ( JIThierarchy[ 0 ][ frame ] === undefined ) { + + this.hierarchy[ 0 ].update( undefined, true ); + + for ( var h = 0; h < this.hierarchy.length; h++ ) { + + if( this.hierarchy[ h ] instanceof THREE.Bone ) { + + JIThierarchy[ h ][ frame ] = this.hierarchy[ h ].skinMatrix.clone(); + + } else { + + JIThierarchy[ h ][ frame ] = this.hierarchy[ h ].matrix.clone(); + + } + + } + + } + + } + +}; + +// Catmull-Rom spline + +THREE.Animation.prototype.interpolateCatmullRom = function ( points, scale ) { + + var c = [], v3 = [], + point, intPoint, weight, w2, w3, + pa, pb, pc, pd; + + point = ( points.length - 1 ) * scale; + intPoint = Math.floor( point ); + weight = point - intPoint; + + c[ 0 ] = intPoint == 0 ? intPoint : intPoint - 1; + c[ 1 ] = intPoint; + c[ 2 ] = intPoint > points.length - 2 ? intPoint : intPoint + 1; + c[ 3 ] = intPoint > points.length - 3 ? intPoint : intPoint + 2; + + pa = points[ c[ 0 ] ]; + pb = points[ c[ 1 ] ]; + pc = points[ c[ 2 ] ]; + pd = points[ c[ 3 ] ]; + + w2 = weight * weight; + w3 = weight * w2; + + v3[ 0 ] = this.interpolate( pa[ 0 ], pb[ 0 ], pc[ 0 ], pd[ 0 ], weight, w2, w3 ); + v3[ 1 ] = this.interpolate( pa[ 1 ], pb[ 1 ], pc[ 1 ], pd[ 1 ], weight, w2, w3 ); + v3[ 2 ] = this.interpolate( pa[ 2 ], pb[ 2 ], pc[ 2 ], pd[ 2 ], weight, w2, w3 ); + + return v3; + +}; + +THREE.Animation.prototype.interpolate = function( p0, p1, p2, p3, t, t2, t3 ) { + + var v0 = ( p2 - p0 ) * 0.5, + v1 = ( p3 - p1 ) * 0.5; + + return ( 2 * ( p1 - p2 ) + v0 + v1 ) * t3 + ( - 3 * ( p1 - p2 ) - 2 * v0 - v1 ) * t2 + v0 * t + p1; + +}; + + + +// Get next key with + +THREE.Animation.prototype.getNextKeyWith = function( type, h, key ) { + + var keys = this.data.hierarchy[ h ].keys; + + if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM || + this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { + + key = key < keys.length - 1 ? key : keys.length - 1; + + } else { + + key = key % keys.length; + + } + + for ( ; key < keys.length; key++ ) { + + if ( keys[ key ][ type ] !== undefined ) { + + return keys[ key ]; + + } + + } + + return this.data.hierarchy[ h ].keys[ 0 ]; + +}; + +// Get previous key with + +THREE.Animation.prototype.getPrevKeyWith = function( type, h, key ) { + + var keys = this.data.hierarchy[ h ].keys; + + if ( this.interpolationType === THREE.AnimationHandler.CATMULLROM || + this.interpolationType === THREE.AnimationHandler.CATMULLROM_FORWARD ) { + + key = key > 0 ? key : 0; + + } else { + + key = key >= 0 ? key : key + keys.length; + + } + + + for ( ; key >= 0; key-- ) { + + if ( keys[ key ][ type ] !== undefined ) { + + return keys[ key ]; + + } + + } + + return this.data.hierarchy[ h ].keys[ keys.length - 1 ]; + +}; +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + * @author paulirish / http://paulirish.com/ + * + * parameters = { + * fov: , + * aspect: , + * near: , + * far: , + * target: , + + * movementSpeed: , + * lookSpeed: , + + * noFly: , + * lookVertical: , + * autoForward: , + + * constrainVertical: , + * verticalMin: , + * verticalMax: , + + * heightSpeed: , + * heightCoef: , + * heightMin: , + * heightMax: , + + * domElement: , + * } + */ + +THREE.FPCamera = function ( parameters ) { + + THREE.Camera.call( this, parameters.fov, parameters.aspect, parameters.near, parameters.far, parameters.target ); + + this.movementSpeed = 1.0; + this.lookSpeed = 0.005; + + this.noFly = false; + this.lookVertical = true; + this.autoForward = false; + + this.activeLook = true; + + this.heightSpeed = false; + this.heightCoef = 1.0; + this.heightMin = 0.0; + + this.constrainVertical = false; + this.verticalMin = 0; + this.verticalMax = 3.14; + + this.domElement = document; + + this.lastUpdate = new Date().getTime(); + this.tdiff = 0; + + if ( parameters ) { + + if ( parameters.movementSpeed !== undefined ) this.movementSpeed = parameters.movementSpeed; + if ( parameters.lookSpeed !== undefined ) this.lookSpeed = parameters.lookSpeed; + if ( parameters.noFly !== undefined ) this.noFly = parameters.noFly; + if ( parameters.lookVertical !== undefined ) this.lookVertical = parameters.lookVertical; + + if ( parameters.autoForward !== undefined ) this.autoForward = parameters.autoForward; + + if ( parameters.activeLook !== undefined ) this.activeLook = parameters.activeLook; + + if ( parameters.heightSpeed !== undefined ) this.heightSpeed = parameters.heightSpeed; + if ( parameters.heightCoef !== undefined ) this.heightCoef = parameters.heightCoef; + if ( parameters.heightMin !== undefined ) this.heightMin = parameters.heightMin; + if ( parameters.heightMax !== undefined ) this.heightMax = parameters.heightMax; + + if ( parameters.constrainVertical !== undefined ) this.constrainVertical = parameters.constrainVertical; + if ( parameters.verticalMin !== undefined ) this.verticalMin = parameters.verticalMin; + if ( parameters.verticalMax !== undefined ) this.verticalMax = parameters.verticalMax; + + if ( parameters.domElement !== undefined ) this.domElement = parameters.domElement; + + } + + this.autoSpeedFactor = 0.0; + + this.mouseX = 0; + this.mouseY = 0; + + this.lat = 0; + this.lon = 0; + this.phi = 0; + this.theta = 0; + + this.moveForward = false; + this.moveBackward = false; + this.moveLeft = false; + this.moveRight = false; + this.freeze = false; + + this.mouseDragOn = false; + + this.windowHalfX = window.innerWidth / 2; + this.windowHalfY = window.innerHeight / 2; + + this.onMouseDown = function ( event ) { + + event.preventDefault(); + event.stopPropagation(); + + if ( this.activeLook ) { + + switch ( event.button ) { + + case 0: this.moveForward = true; break; + case 2: this.moveBackward = true; break; + + } + + } + + this.mouseDragOn = true; + + }; + + this.onMouseUp = function ( event ) { + + event.preventDefault(); + event.stopPropagation(); + + if ( this.activeLook ) { + + switch ( event.button ) { + + case 0: this.moveForward = false; break; + case 2: this.moveBackward = false; break; + + } + + } + + this.mouseDragOn = false; + + }; + + this.onMouseMove = function ( event ) { + + this.mouseX = event.clientX - this.windowHalfX; + this.mouseY = event.clientY - this.windowHalfY; + + }; + + this.onKeyDown = function ( event ) { + + switch( event.keyCode ) { + + case 38: /*up*/ + case 87: /*W*/ this.moveForward = true; break; + + case 37: /*left*/ + case 65: /*A*/ this.moveLeft = true; break; + + case 40: /*down*/ + case 83: /*S*/ this.moveBackward = true; break; + + case 39: /*right*/ + case 68: /*D*/ this.moveRight = true; break; + + case 81: this.freeze = !this.freeze; break; + + } + + }; + + this.onKeyUp = function ( event ) { + + switch( event.keyCode ) { + + case 38: /*up*/ + case 87: /*W*/ this.moveForward = false; break; + + case 37: /*left*/ + case 65: /*A*/ this.moveLeft = false; break; + + case 40: /*down*/ + case 83: /*S*/ this.moveBackward = false; break; + + case 39: /*right*/ + case 68: /*D*/ this.moveRight = false; break; + + } + + }; + + this.update = function() { + + var now = new Date().getTime(); + this.tdiff = ( now - this.lastUpdate ) / 1000; + this.lastUpdate = now; + + if ( !this.freeze ) { + + + if ( this.heightSpeed ) { + + var y = clamp( this.position.y, this.heightMin, this.heightMax ), + delta = y - this.heightMin; + + this.autoSpeedFactor = this.tdiff * ( delta * this.heightCoef ); + + } else { + + this.autoSpeedFactor = 0.0; + + } + + var actualMoveSpeed = this.tdiff * this.movementSpeed; + + if ( this.moveForward || ( this.autoForward && !this.moveBackward ) ) this.translateZ( - ( actualMoveSpeed + this.autoSpeedFactor ) ); + if ( this.moveBackward ) this.translateZ( actualMoveSpeed ); + if ( this.moveLeft ) this.translateX( - actualMoveSpeed ); + if ( this.moveRight ) this.translateX( actualMoveSpeed ); + + var actualLookSpeed = this.tdiff * this.lookSpeed; + + if ( !this.activeLook ) { + + actualLookSpeed = 0; + + } + + this.lon += this.mouseX * actualLookSpeed; + if( this.lookVertical ) this.lat -= this.mouseY * actualLookSpeed; + + this.lat = Math.max( - 85, Math.min( 85, this.lat ) ); + this.phi = ( 90 - this.lat ) * Math.PI / 180; + this.theta = this.lon * Math.PI / 180; + + var targetPosition = this.target.position, + position = this.position; + + targetPosition.x = position.x + 100 * Math.sin( this.phi ) * Math.cos( this.theta ); + targetPosition.y = position.y + 100 * Math.cos( this.phi ); + targetPosition.z = position.z + 100 * Math.sin( this.phi ) * Math.sin( this.theta ); + + } + + this.lon += this.mouseX * actualLookSpeed; + if( this.lookVertical ) this.lat -= this.mouseY * actualLookSpeed; + + this.lat = Math.max( - 85, Math.min( 85, this.lat ) ); + this.phi = ( 90 - this.lat ) * Math.PI / 180; + this.theta = this.lon * Math.PI / 180; + + if ( this.constrainVertical ) { + + this.phi = map_linear( this.phi, 0, 3.14, this.verticalMin, this.verticalMax ); + + } + + var targetPosition = this.target.position, + position = this.position; + + targetPosition.x = position.x + 100 * Math.sin( this.phi ) * Math.cos( this.theta ); + targetPosition.y = position.y + 100 * Math.cos( this.phi ); + targetPosition.z = position.z + 100 * Math.sin( this.phi ) * Math.sin( this.theta ); + + this.supr.update.call( this ); + + }; + + + this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false ); + + this.domElement.addEventListener( 'mousemove', bind( this, this.onMouseMove ), false ); + this.domElement.addEventListener( 'mousedown', bind( this, this.onMouseDown ), false ); + this.domElement.addEventListener( 'mouseup', bind( this, this.onMouseUp ), false ); + this.domElement.addEventListener( 'keydown', bind( this, this.onKeyDown ), false ); + this.domElement.addEventListener( 'keyup', bind( this, this.onKeyUp ), false ); + + function bind( scope, fn ) { + + return function () { + + fn.apply( scope, arguments ); + + }; + + }; + + function map_linear( x, sa, sb, ea, eb ) { + + return ( x - sa ) * ( eb - ea ) / ( sb - sa ) + ea; + + }; + + function clamp_bottom( x, a ) { + + return x < a ? a : x; + + }; + + function clamp( x, a, b ) { + + return x < a ? a : ( x > b ? b : x ); + + }; + +}; + + +THREE.FPCamera.prototype = new THREE.Camera(); +THREE.FPCamera.prototype.constructor = THREE.FPCamera; +THREE.FPCamera.prototype.supr = THREE.Camera.prototype; + + +THREE.FPCamera.prototype.translate = function ( distance, axis ) { + + this.matrix.rotateAxis( axis ); + + if ( this.noFly ) { + + axis.y = 0; + + } + + this.position.addSelf( axis.multiplyScalar( distance ) ); + this.target.position.addSelf( axis.multiplyScalar( distance ) ); + +}; +/** + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * fov: , + * aspect: , + * near: , + * far: , + * target: , + + * waypoints: , // [ [x,y,z], [x,y,z] ... ] + * duration: , // seconds + + * useConstantSpeed: , + * resamplingCoef: , + + * createDebugPath: , + * createDebugDummy: , + + * lookSpeed: , + * lookVertical: , + * lookHorizontal: , + * verticalAngleMap: { srcRange: [ , ], dstRange: [ , ] } + * horizontalAngleMap: { srcRange: [ , ], dstRange: [ , ] } + + * domElement: , + * } + */ + +THREE.PathCamera = function ( parameters ) { + + THREE.Camera.call( this, parameters.fov, parameters.aspect, parameters.near, parameters.far, parameters.target ); + + this.id = "PathCamera" + THREE.PathCameraIdCounter ++; + + this.duration = 10 * 1000; // milliseconds + this.waypoints = []; + + this.useConstantSpeed = true; + this.resamplingCoef = 50; + + this.debugPath = new THREE.Object3D(); + this.debugDummy = new THREE.Object3D(); + + this.animationParent = new THREE.Object3D(); + + this.lookSpeed = 0.005; + this.lookVertical = true; + this.lookHorizontal = true; + this.verticalAngleMap = { srcRange: [ 0, 6.28 ], dstRange: [ 0, 6.28 ] }; + this.horizontalAngleMap = { srcRange: [ 0, 6.28 ], dstRange: [ 0, 6.28 ] }; + + this.domElement = document; + + if ( parameters ) { + + if ( parameters.duration !== undefined ) this.duration = parameters.duration * 1000; + if ( parameters.waypoints !== undefined ) this.waypoints = parameters.waypoints; + + if ( parameters.useConstantSpeed !== undefined ) this.useConstantSpeed = parameters.useConstantSpeed; + if ( parameters.resamplingCoef !== undefined ) this.resamplingCoef = parameters.resamplingCoef; + + if ( parameters.createDebugPath !== undefined ) this.createDebugPath = parameters.createDebugPath; + if ( parameters.createDebugDummy !== undefined ) this.createDebugDummy = parameters.createDebugDummy; + + if ( parameters.lookSpeed !== undefined ) this.lookSpeed = parameters.lookSpeed; + if ( parameters.lookVertical !== undefined ) this.lookVertical = parameters.lookVertical; + if ( parameters.lookHorizontal !== undefined ) this.lookHorizontal = parameters.lookHorizontal; + if ( parameters.verticalAngleMap !== undefined ) this.verticalAngleMap = parameters.verticalAngleMap; + if ( parameters.horizontalAngleMap !== undefined ) this.horizontalAngleMap = parameters.horizontalAngleMap; + + if ( parameters.domElement !== undefined ) this.domElement = parameters.domElement; + + } + + this.mouseX = 0; + this.mouseY = 0; + + this.lat = 0; + this.lon = 0; + + this.phi = 0; + this.theta = 0; + + this.windowHalfX = window.innerWidth / 2; + this.windowHalfY = window.innerHeight / 2; + + var PI2 = Math.PI * 2, + PI180 = Math.PI / 180; + + // methods + + this.update = function ( parentMatrixWorld, forceUpdate, camera ) { + + var srcRange, dstRange; + + if( this.lookHorizontal ) this.lon += this.mouseX * this.lookSpeed; + if( this.lookVertical ) this.lat -= this.mouseY * this.lookSpeed; + + this.lon = Math.max( 0, Math.min( 360, this.lon ) ); + this.lat = Math.max( - 85, Math.min( 85, this.lat ) ); + + this.phi = ( 90 - this.lat ) * PI180; + this.theta = this.lon * PI180; + + this.phi = normalize_angle_rad( this.phi ); + + // constrain vertical look angle + + srcRange = this.verticalAngleMap.srcRange; + dstRange = this.verticalAngleMap.dstRange; + + //this.phi = map_linear( this.phi, srcRange[ 0 ], srcRange[ 1 ], dstRange[ 0 ], dstRange[ 1 ] ); + + var tmpPhi = map_linear( this.phi, srcRange[ 0 ], srcRange[ 1 ], dstRange[ 0 ], dstRange[ 1 ] ); + var tmpPhiFullRange = dstRange[ 1 ] - dstRange[ 0 ]; + var tmpPhiNormalized = ( tmpPhi - dstRange[ 0 ] ) / tmpPhiFullRange; + + this.phi = TWEEN.Easing.Quadratic.EaseInOut( tmpPhiNormalized ) * tmpPhiFullRange + dstRange[ 0 ]; + + // constrain horizontal look angle + + srcRange = this.horizontalAngleMap.srcRange; + dstRange = this.horizontalAngleMap.dstRange; + + //this.theta = map_linear( this.theta, srcRange[ 0 ], srcRange[ 1 ], dstRange[ 0 ], dstRange[ 1 ] ); + + var tmpTheta = map_linear( this.theta, srcRange[ 0 ], srcRange[ 1 ], dstRange[ 0 ], dstRange[ 1 ] ); + var tmpThetaFullRange = dstRange[ 1 ] - dstRange[ 0 ]; + var tmpThetaNormalized = ( tmpTheta - dstRange[ 0 ] ) / tmpThetaFullRange; + + this.theta = TWEEN.Easing.Quadratic.EaseInOut( tmpThetaNormalized ) * tmpThetaFullRange + dstRange[ 0 ]; + + var targetPosition = this.target.position, + position = this.position; + + /* + targetPosition.x = position.x + 100 * Math.sin( this.phi ) * Math.cos( this.theta ); + targetPosition.y = position.y + 100 * Math.cos( this.phi ); + targetPosition.z = position.z + 100 * Math.sin( this.phi ) * Math.sin( this.theta ); + */ + + targetPosition.x = 100 * Math.sin( this.phi ) * Math.cos( this.theta ); + targetPosition.y = 100 * Math.cos( this.phi ); + targetPosition.z = 100 * Math.sin( this.phi ) * Math.sin( this.theta ); + + this.supr.update.call( this, parentMatrixWorld, forceUpdate, camera ); + + }; + + this.onMouseMove = function ( event ) { + + this.mouseX = event.clientX - this.windowHalfX; + this.mouseY = event.clientY - this.windowHalfY; + + }; + + // utils + + function normalize_angle_rad( a ) { + + var b = a % PI2; + return b >= 0 ? b : b + PI2; + + }; + + function cap( x, a, b ) { + + return ( x < a ) ? a : ( ( x > b ) ? b : x ); + + }; + + function map_linear( x, sa, sb, ea, eb ) { + + return ( x - sa ) * ( eb - ea ) / ( sb - sa ) + ea; + + }; + + function distance( a, b ) { + + var dx = a[ 0 ] - b[ 0 ], + dy = a[ 1 ] - b[ 1 ], + dz = a[ 2 ] - b[ 2 ]; + + return Math.sqrt( dx * dx + dy * dy + dz * dz ); + + }; + + function bind( scope, fn ) { + + return function () { + + fn.apply( scope, arguments ); + + }; + + }; + + function initAnimationPath( parent, spline, name, duration ) { + + var animationData = { + + name: name, + fps: 0.6, + length: duration, + + hierarchy: [] + + }; + + var i, + parentAnimation, childAnimation, + path = spline.getControlPointsArray(), + sl = spline.getLength(), + pl = path.length, + t = 0, + first = 0, + last = pl - 1; + + parentAnimation = { parent: -1, keys: [] }; + parentAnimation.keys[ first ] = { time: 0, pos: path[ first ], rot: [ 0, 0, 0, 1 ], scl: [ 1, 1, 1 ] }; + parentAnimation.keys[ last ] = { time: duration, pos: path[ last ], rot: [ 0, 0, 0, 1 ], scl: [ 1, 1, 1 ] }; + + for ( i = 1; i < pl - 1; i++ ) { + + // real distance (approximation via linear segments) + + t = duration * sl.chunks[ i ] / sl.total; + + // equal distance + + //t = duration * ( i / pl ); + + // linear distance + + //t += duration * distance( path[ i ], path[ i - 1 ] ) / sl.total; + + parentAnimation.keys[ i ] = { time: t, pos: path[ i ] }; + + } + + animationData.hierarchy[ 0 ] = parentAnimation; + + THREE.AnimationHandler.add( animationData ); + + return new THREE.Animation( parent, name, THREE.AnimationHandler.CATMULLROM_FORWARD, false ); + + }; + + + function createSplineGeometry( spline, n_sub ) { + + var i, index, position, + geometry = new THREE.Geometry(); + + for ( i = 0; i < spline.points.length * n_sub; i ++ ) { + + index = i / ( spline.points.length * n_sub ); + position = spline.getPoint( index ); + + geometry.vertices[ i ] = new THREE.Vertex( new THREE.Vector3( position.x, position.y, position.z ) ); + + } + + return geometry; + + }; + + function createPath( parent, spline ) { + + var lineGeo = createSplineGeometry( spline, 10 ), + particleGeo = createSplineGeometry( spline, 10 ), + lineMat = new THREE.LineBasicMaterial( { color: 0xff0000, linewidth: 3 } ); + lineObj = new THREE.Line( lineGeo, lineMat ); + particleObj = new THREE.ParticleSystem( particleGeo, new THREE.ParticleBasicMaterial( { color: 0xffaa00, size: 3 } ) ); + + lineObj.scale.set( 1, 1, 1 ); + parent.addChild( lineObj ); + + particleObj.scale.set( 1, 1, 1 ); + parent.addChild( particleObj ); + + var waypoint, + geo = new THREE.SphereGeometry( 1, 16, 8 ), + mat = new THREE.MeshBasicMaterial( { color: 0x00ff00 } ); + + for ( i = 0; i < spline.points.length; i++ ) { + + waypoint = new THREE.Mesh( geo, mat ); + waypoint.position.copy( spline.points[ i ] ); + waypoint.updateMatrix(); + parent.addChild( waypoint ); + + } + + }; + + // constructor + + this.spline = new THREE.Spline(); + this.spline.initFromArray( this.waypoints ); + + if ( this.useConstantSpeed ) { + + this.spline.reparametrizeByArcLength( this.resamplingCoef ); + + } + + if ( this.createDebugDummy ) { + + var dummyParentMaterial = new THREE.MeshLambertMaterial( { color: 0x0077ff } ), + dummyChildMaterial = new THREE.MeshLambertMaterial( { color: 0x00ff00 } ), + dummyParentGeo = new THREE.CubeGeometry( 10, 10, 20 ), + dummyChildGeo = new THREE.CubeGeometry( 2, 2, 10 ); + + this.animationParent = new THREE.Mesh( dummyParentGeo, dummyParentMaterial ); + + var dummyChild = new THREE.Mesh( dummyChildGeo, dummyChildMaterial ); + dummyChild.position.set( 0, 10, 0 ); + + this.animation = initAnimationPath( this.animationParent, this.spline, this.id, this.duration ); + + this.animationParent.addChild( this ); + this.animationParent.addChild( this.target ); + this.animationParent.addChild( dummyChild ); + + } else { + + this.animation = initAnimationPath( this.animationParent, this.spline, this.id, this.duration ); + this.animationParent.addChild( this.target ); + this.animationParent.addChild( this ); + + } + + if ( this.createDebugPath ) { + + createPath( this.debugPath, this.spline ); + + } + + this.domElement.addEventListener( 'mousemove', bind( this, this.onMouseMove ), false ); + +}; + +THREE.PathCamera.prototype = new THREE.Camera(); +THREE.PathCamera.prototype.constructor = THREE.PathCamera; +THREE.PathCamera.prototype.supr = THREE.Camera.prototype; + +THREE.PathCameraIdCounter = 0; +/** + * @author James Baicoianu / http://www.baicoianu.com/ + + * parameters = { + * fov: , + * aspect: , + * near: , + * far: , + * target: , + + * movementSpeed: , + * rollSpeed: , + + * noFly: , + * lookVertical: , + * autoForward: , + + * heightSpeed: , + * heightCoef: , + * heightMin: , + * heightMax: , + + * domElement: , + * } + */ + +THREE.FlyCamera = function ( parameters ) { + + THREE.Camera.call( this, parameters.fov, parameters.aspect, parameters.near, parameters.far, parameters.target ); + + this.tmpQuaternion = new THREE.Quaternion(); + + this.movementSpeed = 1.0; + this.rollSpeed = 0.005; + + this.dragToLook = false; + this.autoForward = false; + + this.domElement = document; + + if ( parameters ) { + + if ( parameters.movementSpeed !== undefined ) this.movementSpeed = parameters.movementSpeed; + if ( parameters.rollSpeed !== undefined ) this.rollSpeed = parameters.rollSpeed; + + if ( parameters.dragToLook !== undefined ) this.dragToLook = parameters.dragToLook; + if ( parameters.autoForward !== undefined ) this.autoForward = parameters.autoForward; + + if ( parameters.domElement !== undefined ) this.domElement = parameters.domElement; + + } + + this.useTarget = false; + this.useQuaternion = true; + + this.mouseStatus = 0; + + this.moveState = { up: 0, down: 0, left: 0, right: 0, forward: 0, back: 0, pitchUp: 0, pitchDown: 0, yawLeft: 0, yawRight: 0, rollLeft: 0, rollRight: 0 }; + this.moveVector = new THREE.Vector3( 0, 0, 0 ); + this.rotationVector = new THREE.Vector3( 0, 0, 0 ); + + this.lastUpdate = -1; + this.tdiff = 0; + + this.handleEvent = function ( event ) { + + if ( typeof this[ event.type ] == 'function' ) { + + this[ event.type ]( event ); + + } + + }; + + this.keydown = function( event ) { + + if ( event.altKey ) { + + return; + + } + + switch( event.keyCode ) { + + case 16: /* shift */ this.movementSpeedMultiplier = .1; break; + + case 87: /*W*/ this.moveState.forward = 1; break; + case 83: /*S*/ this.moveState.back = 1; break; + + case 65: /*A*/ this.moveState.left = 1; break; + case 68: /*D*/ this.moveState.right = 1; break; + + case 82: /*R*/ this.moveState.up = 1; break; + case 70: /*F*/ this.moveState.down = 1; break; + + case 38: /*up*/ this.moveState.pitchUp = 1; break; + case 40: /*down*/ this.moveState.pitchDown = 1; break; + + case 37: /*left*/ this.moveState.yawLeft = 1; break; + case 39: /*right*/ this.moveState.yawRight = 1; break; + + case 81: /*Q*/ this.moveState.rollLeft = 1; break; + case 69: /*E*/ this.moveState.rollRight = 1; break; + + } + + this.updateMovementVector(); + this.updateRotationVector(); + + }; + + this.keyup = function( event ) { + + switch( event.keyCode ) { + + case 16: /* shift */ this.movementSpeedMultiplier = 1; break; + + case 87: /*W*/ this.moveState.forward = 0; break; + case 83: /*S*/ this.moveState.back = 0; break; + + case 65: /*A*/ this.moveState.left = 0; break; + case 68: /*D*/ this.moveState.right = 0; break; + + case 82: /*R*/ this.moveState.up = 0; break; + case 70: /*F*/ this.moveState.down = 0; break; + + case 38: /*up*/ this.moveState.pitchUp = 0; break; + case 40: /*down*/ this.moveState.pitchDown = 0; break; + + case 37: /*left*/ this.moveState.yawLeft = 0; break; + case 39: /*right*/ this.moveState.yawRight = 0; break; + + case 81: /*Q*/ this.moveState.rollLeft = 0; break; + case 69: /*E*/ this.moveState.rollRight = 0; break; + + } + + this.updateMovementVector(); + this.updateRotationVector(); + + }; + + this.mousedown = function(event) { + + event.preventDefault(); + event.stopPropagation(); + + if ( this.dragToLook ) { + + this.mouseStatus ++; + + } else { + + switch ( event.button ) { + + case 0: this.moveForward = true; break; + case 2: this.moveBackward = true; break; + + } + + } + + }; + + this.mousemove = function( event ) { + + if ( !this.dragToLook || this.mouseStatus > 0 ) { + + var container = this.getContainerDimensions(); + var halfWidth = container.size[ 0 ] / 2; + var halfHeight = container.size[ 1 ] / 2; + + this.moveState.yawLeft = - ( ( event.clientX - container.offset[ 0 ] ) - halfWidth ) / halfWidth; + this.moveState.pitchDown = ( ( event.clientY - container.offset[ 1 ] ) - halfHeight ) / halfHeight; + this.updateRotationVector(); + + } + + }; + + this.mouseup = function( event ) { + + event.preventDefault(); + event.stopPropagation(); + + if ( this.dragToLook ) { + + this.mouseStatus --; + + //this.mouseX = this.mouseY = 0; + this.moveState.yawLeft = this.moveState.pitchDown = 0; + + } else { + + switch ( event.button ) { + + case 0: this.moveForward = false; break; + case 2: this.moveBackward = false; break; + + } + + } + + this.updateRotationVector(); + + }; + + this.update = function( parentMatrixWorld, forceUpdate, camera ) { + + var now = new Date().getTime(); + + if ( this.lastUpdate == -1 ) this.lastUpdate = now; + + this.tdiff = ( now - this.lastUpdate ) / 1000; + this.lastUpdate = now; + + //this.position.addSelf( this.moveVector.multiplyScalar( tdiff ) ); + var moveMult = this.tdiff * this.movementSpeed; + var rotMult = this.tdiff * this.rollSpeed; + + this.translateX( this.moveVector.x * moveMult ); + this.translateY( this.moveVector.y * moveMult ); + this.translateZ( this.moveVector.z * moveMult ); + + this.tmpQuaternion.set( this.rotationVector.x * rotMult, this.rotationVector.y * rotMult, this.rotationVector.z * rotMult, 1 ).normalize(); + this.quaternion.multiplySelf( this.tmpQuaternion ); + + this.matrix.setPosition( this.position ); + this.matrix.setRotationFromQuaternion( this.quaternion ); + this.matrixWorldNeedsUpdate = true; + + this.supr.update.call( this ); + + }; + + this.updateMovementVector = function() { + + var forward = ( this.moveState.forward || ( this.autoForward && !this.moveState.back ) ) ? 1 : 0; + + this.moveVector.x = ( -this.moveState.left + this.moveState.right ); + this.moveVector.y = ( -this.moveState.down + this.moveState.up ); + this.moveVector.z = ( -forward + this.moveState.back ); + + //console.log( 'move:', [ this.moveVector.x, this.moveVector.y, this.moveVector.z ] ); + + }; + + this.updateRotationVector = function() { + + this.rotationVector.x = ( -this.moveState.pitchDown + this.moveState.pitchUp ); + this.rotationVector.y = ( -this.moveState.yawRight + this.moveState.yawLeft ); + this.rotationVector.z = ( -this.moveState.rollRight + this.moveState.rollLeft ); + + //console.log( 'rotate:', [ this.rotationVector.x, this.rotationVector.y, this.rotationVector.z ] ); + + }; + + this.getContainerDimensions = function() { + + if ( this.domElement != document ) { + + return { + size : [ this.domElement.offsetWidth, this.domElement.offsetHeight ], + offset : [ this.domElement.offsetLeft, this.domElement.offsetTop ] + }; + + } else { + + return { + size : [ window.innerWidth, window.innerHeight ], + offset : [ 0, 0 ] + }; + + } + + }; + + function bind( scope, fn ) { + + return function () { + + fn.apply( scope, arguments ); + + }; + + }; + + this.domElement.addEventListener( 'mousemove', bind( this, this.mousemove ), false ); + this.domElement.addEventListener( 'mousedown', bind( this, this.mousedown ), false ); + this.domElement.addEventListener( 'mouseup', bind( this, this.mouseup ), false ); + + window.addEventListener( 'keydown', bind( this, this.keydown ), false ); + window.addEventListener( 'keyup', bind( this, this.keyup ), false ); + + this.updateMovementVector(); + this.updateRotationVector(); + +}; + +THREE.FlyCamera.prototype = new THREE.Camera(); +THREE.FlyCamera.prototype.constructor = THREE.FlyCamera; +THREE.FlyCamera.prototype.supr = THREE.Camera.prototype; +/** + * @author mikael emtinger / http://gomo.se/ + * @author alteredq / http://alteredqualia.com/ + * + * parameters = { + * fov: , + * aspect: , + * near: , + * far: , + + * movementSpeed: , + * lookSpeed: , + * rollSpeed: , + + * autoForward: , + * mouseLook: , + + * domElement: , + * } + */ + +THREE.RollCamera = function ( fov, aspect, near, far ) { + + THREE.Camera.call( this, fov, aspect, near, far ); + + // API + + this.mouseLook = true; + this.autoForward = false; + + this.lookSpeed = 1; + this.movementSpeed = 1; + this.rollSpeed = 1; + + this.constrainVertical = [ -0.9, 0.9 ]; + + this.domElement = document; + + // disable default camera behavior + + this.useTarget = false; + this.matrixAutoUpdate = false; + + // internals + + this.forward = new THREE.Vector3( 0, 0, 1 ); + this.roll = 0; + + this.lastUpdate = -1; + this.delta = 0; + + var xTemp = new THREE.Vector3(); + var yTemp = new THREE.Vector3(); + var zTemp = new THREE.Vector3(); + var rollMatrix = new THREE.Matrix4(); + + var doRoll = false, rollDirection = 1, forwardSpeed = 0, sideSpeed = 0, upSpeed = 0; + + var mouseX = 0, mouseY = 0; + + var windowHalfX = window.innerWidth / 2; + var windowHalfY = window.innerHeight / 2; + + // custom update + + this.update = function() { + + var now = new Date().getTime(); + + if ( this.lastUpdate == -1 ) this.lastUpdate = now; + + this.delta = ( now - this.lastUpdate ) / 1000; + this.lastUpdate = now; + + if ( this.mouseLook ) { + + var actualLookSpeed = this.delta * this.lookSpeed; + + this.rotateHorizontally( actualLookSpeed * mouseX ); + this.rotateVertically( actualLookSpeed * mouseY ); + + } + + var actualSpeed = this.delta * this.movementSpeed; + var forwardOrAuto = ( forwardSpeed > 0 || ( this.autoForward && ! ( forwardSpeed < 0 ) ) ) ? 1 : forwardSpeed; + + this.translateZ( actualSpeed * forwardOrAuto ); + this.translateX( actualSpeed * sideSpeed ); + this.translateY( actualSpeed * upSpeed ); + + if( doRoll ) { + + this.roll += this.rollSpeed * this.delta * rollDirection; + + } + + // cap forward up / down + + if( this.forward.y > this.constrainVertical[ 1 ] ) { + + this.forward.y = this.constrainVertical[ 1 ]; + this.forward.normalize(); + + } else if( this.forward.y < this.constrainVertical[ 0 ] ) { + + this.forward.y = this.constrainVertical[ 0 ]; + this.forward.normalize(); + + } + + + // construct unrolled camera matrix + + zTemp.copy( this.forward ); + yTemp.set( 0, 1, 0 ); + + xTemp.cross( yTemp, zTemp ).normalize(); + yTemp.cross( zTemp, xTemp ).normalize(); + + this.matrix.n11 = xTemp.x; this.matrix.n12 = yTemp.x; this.matrix.n13 = zTemp.x; + this.matrix.n21 = xTemp.y; this.matrix.n22 = yTemp.y; this.matrix.n23 = zTemp.y; + this.matrix.n31 = xTemp.z; this.matrix.n32 = yTemp.z; this.matrix.n33 = zTemp.z; + + + // calculate roll matrix + + rollMatrix.identity(); + rollMatrix.n11 = Math.cos( this.roll ); rollMatrix.n12 = -Math.sin( this.roll ); + rollMatrix.n21 = Math.sin( this.roll ); rollMatrix.n22 = Math.cos( this.roll ); + + + // multiply camera with roll + + this.matrix.multiplySelf( rollMatrix ); + this.matrixWorldNeedsUpdate = true; + + + // set position + + this.matrix.n14 = this.position.x; + this.matrix.n24 = this.position.y; + this.matrix.n34 = this.position.z; + + + // call supr + + this.supr.update.call( this ); + + }; + + this.translateX = function ( distance ) { + + this.position.x += this.matrix.n11 * distance; + this.position.y += this.matrix.n21 * distance; + this.position.z += this.matrix.n31 * distance; + + }; + + this.translateY = function ( distance ) { + + this.position.x += this.matrix.n12 * distance; + this.position.y += this.matrix.n22 * distance; + this.position.z += this.matrix.n32 * distance; + + }; + + this.translateZ = function ( distance ) { + + this.position.x -= this.matrix.n13 * distance; + this.position.y -= this.matrix.n23 * distance; + this.position.z -= this.matrix.n33 * distance; + + }; + + + this.rotateHorizontally = function ( amount ) { + + // please note that the amount is NOT degrees, but a scale value + + xTemp.set( this.matrix.n11, this.matrix.n21, this.matrix.n31 ); + xTemp.multiplyScalar( amount ); + + this.forward.subSelf( xTemp ); + this.forward.normalize(); + + }; + + this.rotateVertically = function ( amount ) { + + // please note that the amount is NOT degrees, but a scale value + + yTemp.set( this.matrix.n12, this.matrix.n22, this.matrix.n32 ); + yTemp.multiplyScalar( amount ); + + this.forward.addSelf( yTemp ); + this.forward.normalize(); + + }; + + function onKeyDown( event ) { + + switch( event.keyCode ) { + + case 38: /*up*/ + case 87: /*W*/ forwardSpeed = 1; break; + + case 37: /*left*/ + case 65: /*A*/ sideSpeed = -1; break; + + case 40: /*down*/ + case 83: /*S*/ forwardSpeed = -1; break; + + case 39: /*right*/ + case 68: /*D*/ sideSpeed = 1; break; + + case 81: /*Q*/ doRoll = true; rollDirection = 1; break; + case 69: /*E*/ doRoll = true; rollDirection = -1; break; + + case 82: /*R*/ upSpeed = 1; break; + case 70: /*F*/ upSpeed = -1; break; + + } + + }; + + function onKeyUp( event ) { + + switch( event.keyCode ) { + + case 38: /*up*/ + case 87: /*W*/ forwardSpeed = 0; break; + + case 37: /*left*/ + case 65: /*A*/ sideSpeed = 0; break; + + case 40: /*down*/ + case 83: /*S*/ forwardSpeed = 0; break; + + case 39: /*right*/ + case 68: /*D*/ sideSpeed = 0; break; + + case 81: /*Q*/ doRoll = false; break; + case 69: /*E*/ doRoll = false; break; + + case 82: /*R*/ upSpeed = 0; break; + case 70: /*F*/ upSpeed = 0; break; + + } + + }; + + function onMouseMove( event ) { + + mouseX = ( event.clientX - windowHalfX ) / window.innerWidth; + mouseY = ( event.clientY - windowHalfY ) / window.innerHeight; + + }; + + function onMouseDown ( event ) { + + event.preventDefault(); + event.stopPropagation(); + + switch ( event.button ) { + + case 0: forwardSpeed = 1; break; + case 2: forwardSpeed = -1; break; + + } + + }; + + function onMouseUp ( event ) { + + event.preventDefault(); + event.stopPropagation(); + + switch ( event.button ) { + + case 0: forwardSpeed = 0; break; + case 2: forwardSpeed = 0; break; + + } + + }; + + this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false ); + + this.domElement.addEventListener( 'mousemove', onMouseMove, false ); + this.domElement.addEventListener( 'mousedown', onMouseDown, false ); + this.domElement.addEventListener( 'mouseup', onMouseUp, false ); + this.domElement.addEventListener( 'keydown', onKeyDown, false ); + this.domElement.addEventListener( 'keyup', onKeyUp, false ); + +}; + + +THREE.RollCamera.prototype = new THREE.Camera(); +THREE.RollCamera.prototype.constructor = THREE.RollCamera; +THREE.RollCamera.prototype.supr = THREE.Camera.prototype; + + +/** + * @author Eberhard Graether / http://egraether.com/ + + * parameters = { + * fov: , + * aspect: , + * near: , + * far: , + * target: , + + * radius: , + * screen: { width : , height : , offsetLeft : , offsetTop : }, + + * rotateSpeed: , + * zoomSpeed: , + * panSpeed: , + + * noZoom: , + * noPan: , + + * staticMoving: , + * dynamicDampingFactor: , + + * minDistance: , + * maxDistance: , + + * keys: , // [ rotateKey, zoomKey, panKey ], + + * domElement: , + * } + */ + +THREE.TrackballCamera = function ( parameters ) { + + // target.position is modified when panning + + parameters = parameters || {}; + + THREE.Camera.call( this, parameters.fov, parameters.aspect, parameters.near, parameters.far, parameters.target ); + + this.domElement = parameters.domElement || document; + + this.screen = parameters.screen || { width : window.innerWidth, height : window.innerHeight, offsetLeft : 0, offsetTop : 0 }; + this.radius = parameters.radius || ( this.screen.width + this.screen.height ) / 4; + + this.rotateSpeed = parameters.rotateSpeed || 1.0; + this.zoomSpeed = parameters.zoomSpeed || 1.2; + this.panSpeed = parameters.panSpeed || 0.3; + + this.noZoom = parameters.noZoom || false; + this.noPan = parameters.noPan || false; + + this.staticMoving = parameters.staticMoving || false; + this.dynamicDampingFactor = parameters.dynamicDampingFactor || 0.2; + + this.minDistance = parameters.minDistance || 0; + this.maxDistance = parameters.maxDistance || Infinity; + + this.keys = parameters.keys || [ 65 /*A*/, 83 /*S*/, 68 /*D*/ ]; + + this.useTarget = true; + + + //internals + + var _keyPressed = false, + _state = this.STATE.NONE, + + _eye = new THREE.Vector3(), + + _rotateStart = new THREE.Vector3(), + _rotateEnd = new THREE.Vector3(), + + _zoomStart = new THREE.Vector2(), + _zoomEnd = new THREE.Vector2(), + + _panStart = new THREE.Vector2(), + _panEnd = new THREE.Vector2(); + + + // methods + + this.handleEvent = function ( event ) { + + if ( typeof this[ event.type ] == 'function' ) { + + this[ event.type ]( event ); + + } + + }; + + this.getMouseOnScreen = function( clientX, clientY ) { + + return new THREE.Vector2( + ( clientX - this.screen.offsetLeft ) / this.radius * 0.5, + ( clientY - this.screen.offsetTop ) / this.radius * 0.5 + ); + + }; + + this.getMouseProjectionOnBall = function( clientX, clientY ) { + + var mouseOnBall = new THREE.Vector3( + ( clientX - this.screen.width * 0.5 - this.screen.offsetLeft ) / this.radius, + ( this.screen.height * 0.5 + this.screen.offsetTop - clientY ) / this.radius, + 0.0 + ); + + var length = mouseOnBall.length(); + + if ( length > 1.0 ) { + + mouseOnBall.normalize(); + + } else { + + mouseOnBall.z = Math.sqrt( 1.0 - length * length ); + + } + + _eye = this.position.clone().subSelf( this.target.position ); + + var projection = this.up.clone().setLength( mouseOnBall.y ); + projection.addSelf( this.up.clone().crossSelf( _eye ).setLength( mouseOnBall.x ) ); + projection.addSelf( _eye.setLength( mouseOnBall.z ) ); + + return projection; + + }; + + this.rotateCamera = function() { + + var angle = Math.acos( _rotateStart.dot( _rotateEnd ) / _rotateStart.length() / _rotateEnd.length() ); + + if ( angle ) { + + var axis = (new THREE.Vector3()).cross( _rotateStart, _rotateEnd ).normalize(), + quaternion = new THREE.Quaternion(); + + angle *= this.rotateSpeed; + + quaternion.setFromAxisAngle( axis, -angle ); + + quaternion.multiplyVector3( _eye ); + quaternion.multiplyVector3( this.up ); + + quaternion.multiplyVector3( _rotateEnd ); + + if ( this.staticMoving ) { + + _rotateStart = _rotateEnd; + + } else { + + quaternion.setFromAxisAngle( axis, angle * ( this.dynamicDampingFactor - 1.0 ) ); + quaternion.multiplyVector3( _rotateStart ); + + } + + } + + }; + + this.zoomCamera = function() { + + var factor = 1.0 + ( _zoomEnd.y - _zoomStart.y ) * this.zoomSpeed; + + if ( factor !== 1.0 && factor > 0.0 ) { + + _eye.multiplyScalar( factor ); + + if ( this.staticMoving ) { + + _zoomStart = _zoomEnd; + + } else { + + _zoomStart.y += ( _zoomEnd.y - _zoomStart.y ) * this.dynamicDampingFactor; + + } + + } + + }; + + this.panCamera = function() { + + var mouseChange = _panEnd.clone().subSelf( _panStart ); + + if ( mouseChange.lengthSq() ) { + + mouseChange.multiplyScalar( _eye.length() * this.panSpeed ); + + var pan = _eye.clone().crossSelf( this.up ).setLength( mouseChange.x ); + pan.addSelf( this.up.clone().setLength( mouseChange.y ) ); + + this.position.addSelf( pan ); + this.target.position.addSelf( pan ); + + if ( this.staticMoving ) { + + _panStart = _panEnd; + + } else { + + _panStart.addSelf( mouseChange.sub( _panEnd, _panStart ).multiplyScalar( this.dynamicDampingFactor ) ); + + } + + } + + }; + + this.checkDistances = function() { + + if ( !this.noZoom || !this.noPan ) { + + if ( this.position.lengthSq() > this.maxDistance * this.maxDistance ) { + + this.position.setLength( this.maxDistance ); + + } + + if ( _eye.lengthSq() < this.minDistance * this.minDistance ) { + + this.position.add( this.target.position, _eye.setLength( this.minDistance ) ); + + } + + } + + }; + + this.update = function( parentMatrixWorld, forceUpdate, camera ) { + + _eye = this.position.clone().subSelf( this.target.position ), + + this.rotateCamera(); + + if ( !this.noZoom ) { + + this.zoomCamera(); + + } + + if ( !this.noPan ) { + + this.panCamera(); + + } + + this.position.add( this.target.position, _eye ); + + this.checkDistances(); + + this.supr.update.call( this, parentMatrixWorld, forceUpdate, camera ); + + }; + + + // listeners + + function keydown( event ) { + + if ( _state !== this.STATE.NONE ) { + + return; + + } else if ( event.keyCode === this.keys[ this.STATE.ROTATE ] ) { + + _state = this.STATE.ROTATE; + + } else if ( event.keyCode === this.keys[ this.STATE.ZOOM ] && !this.noZoom ) { + + _state = this.STATE.ZOOM; + + } else if ( event.keyCode === this.keys[ this.STATE.PAN ] && !this.noPan ) { + + _state = this.STATE.PAN; + + } + + if ( _state !== this.STATE.NONE ) { + + _keyPressed = true; + + } + + }; + + function keyup( event ) { + + if ( _state !== this.STATE.NONE ) { + + _state = this.STATE.NONE; + + } + + }; + + function mousedown(event) { + + event.preventDefault(); + event.stopPropagation(); + + if ( _state === this.STATE.NONE ) { + + _state = event.button; + + if ( _state === this.STATE.ROTATE ) { + + _rotateStart = _rotateEnd = this.getMouseProjectionOnBall( event.clientX, event.clientY ); + + } else if ( _state === this.STATE.ZOOM && !this.noZoom ) { + + _zoomStart = _zoomEnd = this.getMouseOnScreen( event.clientX, event.clientY ); + + } else if ( !this.noPan ) { + + _panStart = _panEnd = this.getMouseOnScreen( event.clientX, event.clientY ); + + } + + } + + }; + + function mousemove( event ) { + + if ( _keyPressed ) { + + _rotateStart = _rotateEnd = this.getMouseProjectionOnBall( event.clientX, event.clientY ); + _zoomStart = _zoomEnd = this.getMouseOnScreen( event.clientX, event.clientY ); + _panStart = _panEnd = this.getMouseOnScreen( event.clientX, event.clientY ); + + _keyPressed = false; + + } + + if ( _state === this.STATE.NONE ) { + + return; + + } else if ( _state === this.STATE.ROTATE ) { + + _rotateEnd = this.getMouseProjectionOnBall( event.clientX, event.clientY ); + + } else if ( _state === this.STATE.ZOOM && !this.noZoom ) { + + _zoomEnd = this.getMouseOnScreen( event.clientX, event.clientY ); + + } else if ( _state === this.STATE.PAN && !this.noPan ) { + + _panEnd = this.getMouseOnScreen( event.clientX, event.clientY ); + + } + + }; + + function mouseup( event ) { + + event.preventDefault(); + event.stopPropagation(); + + _state = this.STATE.NONE; + + }; + + function bind( scope, fn ) { + + return function () { + + fn.apply( scope, arguments ); + + }; + + }; + + this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false ); + + this.domElement.addEventListener( 'mousemove', bind( this, mousemove ), false ); + this.domElement.addEventListener( 'mousedown', bind( this, mousedown ), false ); + this.domElement.addEventListener( 'mouseup', bind( this, mouseup ), false ); + + window.addEventListener( 'keydown', bind( this, keydown ), false ); + window.addEventListener( 'keyup', bind( this, keyup ), false ); + +}; + +THREE.TrackballCamera.prototype = new THREE.Camera(); +THREE.TrackballCamera.prototype.constructor = THREE.TrackballCamera; +THREE.TrackballCamera.prototype.supr = THREE.Camera.prototype; + +THREE.TrackballCamera.prototype.STATE = { NONE : -1, ROTATE : 0, ZOOM : 1, PAN : 2 }; +/** + * @author chriskillpack / http://chriskillpack.com/ + * + * QuakeCamera has been renamed FPCamera. This property exists for backwards + * compatibility only. + */ +THREE.QuakeCamera = THREE.FPCamera; +/** + * @author mr.doob / http://mrdoob.com/ + * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Cube.as + */ + +THREE.CubeGeometry = function ( width, height, depth, segmentsWidth, segmentsHeight, segmentsDepth, materials, flipped, sides ) { + + THREE.Geometry.call( this ); + + var scope = this, + width_half = width / 2, + height_half = height / 2, + depth_half = depth / 2, + flip = flipped ? - 1 : 1; + + if ( materials !== undefined ) { + + if ( materials instanceof Array ) { + + this.materials = materials; + + } else { + + this.materials = []; + + for ( var i = 0; i < 6; i ++ ) { + + this.materials.push( [ materials ] ); + + } + + } + + } else { + + this.materials = []; + + } + + this.sides = { px: true, nx: true, py: true, ny: true, pz: true, nz: true }; + + if( sides != undefined ) { + + for( var s in sides ) { + + if ( this.sides[ s ] != undefined ) { + + this.sides[ s ] = sides[ s ]; + + } + + } + + } + + this.sides.px && buildPlane( 'z', 'y', 1 * flip, - 1, depth, height, - width_half, this.materials[ 0 ] ); // px + this.sides.nx && buildPlane( 'z', 'y', - 1 * flip, - 1, depth, height, width_half, this.materials[ 1 ] ); // nx + this.sides.py && buildPlane( 'x', 'z', 1 * flip, 1, width, depth, height_half, this.materials[ 2 ] ); // py + this.sides.ny && buildPlane( 'x', 'z', 1 * flip, - 1, width, depth, - height_half, this.materials[ 3 ] ); // ny + this.sides.pz && buildPlane( 'x', 'y', 1 * flip, - 1, width, height, depth_half, this.materials[ 4 ] ); // pz + this.sides.nz && buildPlane( 'x', 'y', - 1 * flip, - 1, width, height, - depth_half, this.materials[ 5 ] ); // nz + + mergeVertices(); + + function buildPlane( u, v, udir, vdir, width, height, depth, material ) { + + var w, ix, iy, + gridX = segmentsWidth || 1, + gridY = segmentsHeight || 1, + width_half = width / 2, + height_half = height / 2, + offset = scope.vertices.length; + + if ( ( u == 'x' && v == 'y' ) || ( u == 'y' && v == 'x' ) ) { + + w = 'z'; + + } else if ( ( u == 'x' && v == 'z' ) || ( u == 'z' && v == 'x' ) ) { + + w = 'y'; + gridY = segmentsDepth || 1; + + } else if ( ( u == 'z' && v == 'y' ) || ( u == 'y' && v == 'z' ) ) { + + w = 'x'; + gridX = segmentsDepth || 1; + + } + + var gridX1 = gridX + 1, + gridY1 = gridY + 1, + segment_width = width / gridX, + segment_height = height / gridY; + + for( iy = 0; iy < gridY1; iy++ ) { + + for( ix = 0; ix < gridX1; ix++ ) { + + var vector = new THREE.Vector3(); + vector[ u ] = ( ix * segment_width - width_half ) * udir; + vector[ v ] = ( iy * segment_height - height_half ) * vdir; + vector[ w ] = depth; + + scope.vertices.push( new THREE.Vertex( vector ) ); + + } + + } + + for( iy = 0; iy < gridY; iy++ ) { + + for( ix = 0; ix < gridX; ix++ ) { + + var a = ix + gridX1 * iy; + var b = ix + gridX1 * ( iy + 1 ); + var c = ( ix + 1 ) + gridX1 * ( iy + 1 ); + var d = ( ix + 1 ) + gridX1 * iy; + + scope.faces.push( new THREE.Face4( a + offset, b + offset, c + offset, d + offset, null, null, material ) ); + scope.faceVertexUvs[ 0 ].push( [ + new THREE.UV( ix / gridX, iy / gridY ), + new THREE.UV( ix / gridX, ( iy + 1 ) / gridY ), + new THREE.UV( ( ix + 1 ) / gridX, ( iy + 1 ) / gridY ), + new THREE.UV( ( ix + 1 ) / gridX, iy / gridY ) + ] ); + + } + + } + + } + + function mergeVertices() { + + var unique = [], changes = []; + + for ( var i = 0, il = scope.vertices.length; i < il; i ++ ) { + + var v = scope.vertices[ i ], + duplicate = false; + + for ( var j = 0, jl = unique.length; j < jl; j ++ ) { + + var vu = unique[ j ]; + + if( v.position.x == vu.position.x && v.position.y == vu.position.y && v.position.z == vu.position.z ) { + + changes[ i ] = j; + duplicate = true; + break; + + } + + } + + if ( ! duplicate ) { + + changes[ i ] = unique.length; + unique.push( new THREE.Vertex( v.position.clone() ) ); + + } + + } + + for ( i = 0, il = scope.faces.length; i < il; i ++ ) { + + var face = scope.faces[ i ]; + + face.a = changes[ face.a ]; + face.b = changes[ face.b ]; + face.c = changes[ face.c ]; + face.d = changes[ face.d ]; + + } + + scope.vertices = unique; + + } + + this.computeCentroids(); + this.computeFaceNormals(); + +}; + +THREE.CubeGeometry.prototype = new THREE.Geometry(); +THREE.CubeGeometry.prototype.constructor = THREE.CubeGeometry; +/** + * @author kile / http://kile.stravaganza.org/ + * @author mr.doob / http://mrdoob.com/ + * @author fuzzthink + */ + +THREE.CylinderGeometry = function ( numSegs, topRad, botRad, height, topOffset, botOffset ) { + + THREE.Geometry.call( this ); + + var scope = this, i, PI2 = Math.PI * 2, halfHeight = height / 2; + + // Top circle vertices + + for ( i = 0; i < numSegs; i ++ ) { + + v( Math.sin( PI2 * i / numSegs ) * topRad, Math.cos( PI2 * i / numSegs ) * topRad, - halfHeight ); + + } + + // Bottom circle vertices + + for ( i = 0; i < numSegs; i ++ ) { + + v( Math.sin( PI2 * i / numSegs ) * botRad, Math.cos( PI2 * i / numSegs ) * botRad, halfHeight ); + + } + + // Body faces + + for ( i = 0; i < numSegs; i++ ) { + + f4( + i, + i + numSegs, + numSegs + ( i + 1 ) % numSegs, + ( i + 1 ) % numSegs + ); + + } + + // Bottom circle faces + + if ( botRad > 0 ) { + + v( 0, 0, - halfHeight - ( botOffset || 0 ) ); + + for ( i = numSegs; i < numSegs + ( numSegs / 2 ); i++ ) { + + f4( + 2 * numSegs, + ( 2 * i - 2 * numSegs ) % numSegs, + ( 2 * i - 2 * numSegs + 1 ) % numSegs, + ( 2 * i - 2 * numSegs + 2 ) % numSegs + ); + + } + + } + + // Top circle faces + + if ( topRad > 0 ) { + + v( 0, 0, halfHeight + ( topOffset || 0 ) ); + + for ( i = numSegs + ( numSegs / 2 ); i < 2 * numSegs; i ++ ) { + + f4( + 2 * numSegs + 1, + ( 2 * i - 2 * numSegs + 2 ) % numSegs + numSegs, + ( 2 * i - 2 * numSegs + 1 ) % numSegs + numSegs, + ( 2 * i - 2 * numSegs ) % numSegs + numSegs + ); + + } + + } + + // Cylindrical mapping + + for ( var i = 0, il = this.faces.length; i < il; i ++ ) { + + var uvs = [], face = this.faces[ i ], + a = this.vertices[ face.a ], + b = this.vertices[ face.b ], + c = this.vertices[ face.c ], + d = this.vertices[ face.d ]; + + uvs.push( new THREE.UV( 0.5 + Math.atan2( a.position.x, a.position.y ) / PI2, 0.5 + ( a.position.z / height ) ) ); + uvs.push( new THREE.UV( 0.5 + Math.atan2( b.position.x, b.position.y ) / PI2, 0.5 + ( b.position.z / height ) ) ); + uvs.push( new THREE.UV( 0.5 + Math.atan2( c.position.x, c.position.y ) / PI2, 0.5 + ( c.position.z / height ) ) ); + + if ( face instanceof THREE.Face4 ) { + + uvs.push( new THREE.UV( 0.5 + ( Math.atan2( d.position.x, d.position.y ) / PI2 ), 0.5 + ( d.position.z / height ) ) ); + + } + + this.faceVertexUvs[ 0 ].push( uvs ); + + } + + this.computeCentroids(); + this.computeFaceNormals(); + // this.computeVertexNormals(); + + function v( x, y, z ) { + + scope.vertices.push( new THREE.Vertex( new THREE.Vector3( x, y, z ) ) ); + + } + + function f4( a, b, c, d ) { + + scope.faces.push( new THREE.Face4( a, b, c, d ) ); + + } + +}; + +THREE.CylinderGeometry.prototype = new THREE.Geometry(); +THREE.CylinderGeometry.prototype.constructor = THREE.CylinderGeometry; +/** + * @author oosmoxiecode + + * uvs are messed up in this one, and commented away for now. There is an ugly "seam" by the shared vertices + * when it "wraps" around, that needs to be fixed. It´s because they share the first and the last vertices + * so it draws the entire texture on the seam-faces, I think... + */ + +THREE.IcosahedronGeometry = function ( subdivisions ) { + + var scope = this; + var tempScope = new THREE.Geometry(); + var tempFaces; + this.subdivisions = subdivisions || 0; + + //var temp_uv = []; + + THREE.Geometry.call(this); + + // create 12 vertices of a Icosahedron + var t = (1 + Math.sqrt(5)) / 2; + + v(-1, t, 0); + v( 1, t, 0); + v(-1, -t, 0); + v( 1, -t, 0); + + v( 0, -1, t); + v( 0, 1, t); + v( 0, -1, -t); + v( 0, 1, -t); + + v( t, 0, -1); + v( t, 0, 1); + v(-t, 0, -1); + v(-t, 0, 1); + + // 5 faces around point 0 + f3(0, 11, 5, tempScope); + f3(0, 5, 1, tempScope); + f3(0, 1, 7, tempScope); + f3(0, 7, 10, tempScope); + f3(0, 10, 11, tempScope); + + // 5 adjacent faces + f3(1, 5, 9, tempScope); + f3(5, 11, 4, tempScope); + f3(11, 10, 2, tempScope); + f3(10, 7, 6, tempScope); + f3(7, 1, 8, tempScope); + + // 5 faces around point 3 + f3(3, 9, 4, tempScope); + f3(3, 4, 2, tempScope); + f3(3, 2, 6, tempScope); + f3(3, 6, 8, tempScope); + f3(3, 8, 9, tempScope); + + // 5 adjacent faces + f3(4, 9, 5, tempScope); + f3(2, 4, 11, tempScope); + f3(6, 2, 10, tempScope); + f3(8, 6, 7, tempScope); + f3(9, 8, 1, tempScope); + + // subdivide faces to refine the triangles + for (var i=0; i < this.subdivisions; i++) { + tempFaces = new THREE.Geometry(); + for (var tri in tempScope.faces) { + // replace each triangle by 4 triangles + var a = getMiddlePoint(tempScope.faces[tri].a, tempScope.faces[tri].b); + var b = getMiddlePoint(tempScope.faces[tri].b, tempScope.faces[tri].c); + var c = getMiddlePoint(tempScope.faces[tri].c, tempScope.faces[tri].a); + + f3(tempScope.faces[tri].a, a, c, tempFaces); + f3(tempScope.faces[tri].b, b, a, tempFaces); + f3(tempScope.faces[tri].c, c, b, tempFaces); + f3(a, b, c, tempFaces); + } + tempScope.faces = tempFaces.faces; + //tempScope.uvs = tempFaces.uvs; + } + + scope.faces = tempScope.faces; + //scope.uvs = tempScope.uvs; + + delete tempScope; + delete tempFaces; + + this.computeCentroids(); + this.computeFaceNormals(); + this.computeVertexNormals(); + + function v( x, y, z ) { + var length = Math.sqrt(x * x + y * y + z * z); + var i = scope.vertices.push( new THREE.Vertex( new THREE.Vector3( x/length, y/length, z/length ) ) ); + + //var uv = getUv(x, y, z); + //temp_uv.push(uv); + + return i-1; + } + + function f3( a, b, c, inscope ) { + inscope.faces.push( new THREE.Face3( a, b, c ) ); + + /*inscope.uvs.push( [new THREE.UV( temp_uv[a].u, temp_uv[a].v ), + new THREE.UV( temp_uv[b].u, temp_uv[b].v ), + new THREE.UV( temp_uv[c].u, temp_uv[c].v ) + ] ); + */ + } + + function getMiddlePoint(p1,p2) { + var pos1 = scope.vertices[p1].position; + var pos2 = scope.vertices[p2].position; + + var x = (pos1.x + pos2.x) / 2; + var y = (pos1.y + pos2.y) / 2; + var z = (pos1.z + pos2.z) / 2; + + var i = v(x, y, z); + return i; + } + + /*function getUv(x,y,z) { + + var u,v; + var px,py,pz,d; + + d = Math.sqrt( x*x+y*y+z*z ); + + px = x/d; + py = y/d; + pz = z/d; + + var normalisedX = 0; + var normalisedZ = -1; + + if (((px * px) + (pz * pz)) > 0) { + normalisedX = Math.sqrt((px * px) / ((px * px) + (pz * pz))); + + if (px < 0) { + normalisedX = -normalisedX; + } + + normalisedZ = Math.sqrt((pz * pz) / ((px * px) + (pz * pz))); + + if (pz < 0) { + normalisedZ = -normalisedZ; + } + } + + if (normalisedZ == 0) { + u = ((normalisedX * Math.PI) / 2); + } else { + u = Math.atan(normalisedX / normalisedZ); + + if (normalisedZ < 0) { + u += Math.PI; + } + } + + if (u < 0) { + u += 2 * Math.PI; + } + + u /= 2 * Math.PI; + v = (-py + 1) / 2; + + return {u:u,v:v}; + }*/ + +} + +THREE.IcosahedronGeometry.prototype = new THREE.Geometry(); +THREE.IcosahedronGeometry.prototype.constructor = THREE.IcosahedronGeometry; +/** + * @author astrodud / http://astrodud.isgreat.org/ + */ + +THREE.LatheGeometry = function ( points, steps, angle ) { + + THREE.Geometry.call( this ); + + this.steps = steps || 12; + this.angle = angle || 2 * Math.PI; + + var stepSize = this.angle / this.steps, + newV = [], oldInds = [], newInds = [], startInds = [], + matrix = new THREE.Matrix4().setRotationZ( stepSize ); + + for ( var j = 0; j < points.length; j ++ ) { + + this.vertices.push( new THREE.Vertex( points[ j ] ) ); + + newV[ j ] = points[ j ].clone(); + oldInds[ j ] = this.vertices.length - 1; + + } + + for ( var r = 0; r <= this.angle + 0.001; r += stepSize ) { // need the +0.001 for it go up to angle + + for ( var j = 0; j < newV.length; j ++ ) { + + if ( r < this.angle ) { + + newV[ j ] = matrix.multiplyVector3( newV[ j ].clone() ); + this.vertices.push( new THREE.Vertex( newV[ j ] ) ); + newInds[ j ] = this.vertices.length - 1; + + } else { + + newInds = startInds; // wrap it up! + + } + + } + + if ( r == 0 ) startInds = oldInds; + + for ( var j = 0; j < oldInds.length - 1; j ++ ) { + + this.faces.push( new THREE.Face4( newInds[ j ], newInds[ j + 1 ], oldInds[ j + 1 ], oldInds[ j ] ) ); + this.faceVertexUvs[ 0 ].push( [ + + new THREE.UV( 1 - r / this.angle, j / points.length ), + new THREE.UV( 1 - r / this.angle, ( j + 1 ) / points.length ), + new THREE.UV( 1 - ( r - stepSize ) / this.angle, ( j + 1 ) / points.length ), + new THREE.UV( 1 - ( r - stepSize ) / this.angle, j / points.length ) + + ] ); + + } + + oldInds = newInds; + newInds = []; + + } + + this.computeCentroids(); + this.computeFaceNormals(); + this.computeVertexNormals(); + +}; + +THREE.LatheGeometry.prototype = new THREE.Geometry(); +THREE.LatheGeometry.prototype.constructor = THREE.LatheGeometry; +/** + * @author mr.doob / http://mrdoob.com/ + * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Plane.as + */ + +THREE.PlaneGeometry = function ( width, height, segmentsWidth, segmentsHeight ) { + + THREE.Geometry.call( this ); + + var ix, iy, + width_half = width / 2, + height_half = height / 2, + gridX = segmentsWidth || 1, + gridY = segmentsHeight || 1, + gridX1 = gridX + 1, + gridY1 = gridY + 1, + segment_width = width / gridX, + segment_height = height / gridY; + + + for( iy = 0; iy < gridY1; iy++ ) { + + for( ix = 0; ix < gridX1; ix++ ) { + + var x = ix * segment_width - width_half; + var y = iy * segment_height - height_half; + + this.vertices.push( new THREE.Vertex( new THREE.Vector3( x, - y, 0 ) ) ); + + } + + } + + for( iy = 0; iy < gridY; iy++ ) { + + for( ix = 0; ix < gridX; ix++ ) { + + var a = ix + gridX1 * iy; + var b = ix + gridX1 * ( iy + 1 ); + var c = ( ix + 1 ) + gridX1 * ( iy + 1 ); + var d = ( ix + 1 ) + gridX1 * iy; + + this.faces.push( new THREE.Face4( a, b, c, d ) ); + this.faceVertexUvs[ 0 ].push( [ + new THREE.UV( ix / gridX, iy / gridY ), + new THREE.UV( ix / gridX, ( iy + 1 ) / gridY ), + new THREE.UV( ( ix + 1 ) / gridX, ( iy + 1 ) / gridY ), + new THREE.UV( ( ix + 1 ) / gridX, iy / gridY ) + ] ); + + } + + } + + this.computeCentroids(); + this.computeFaceNormals(); + +}; + +THREE.PlaneGeometry.prototype = new THREE.Geometry(); +THREE.PlaneGeometry.prototype.constructor = THREE.PlaneGeometry; +/** + * @author mr.doob / http://mrdoob.com/ + * based on http://papervision3d.googlecode.com/svn/trunk/as3/trunk/src/org/papervision3d/objects/primitives/Sphere.as + */ + +THREE.SphereGeometry = function ( radius, segmentsWidth, segmentsHeight ) { + + THREE.Geometry.call( this ); + + var radius = radius || 50, + gridX = segmentsWidth || 8, + gridY = segmentsHeight || 6; + + var i, j, pi = Math.PI; + var iHor = Math.max( 3, gridX ); + var iVer = Math.max( 2, gridY ); + var aVtc = []; + + for ( j = 0; j < ( iVer + 1 ) ; j++ ) { + + var fRad1 = j / iVer; + var fZ = radius * Math.cos( fRad1 * pi ); + var fRds = radius * Math.sin( fRad1 * pi ); + var aRow = []; + var oVtx = 0; + + for ( i = 0; i < iHor; i++ ) { + + var fRad2 = 2 * i / iHor; + var fX = fRds * Math.sin( fRad2 * pi ); + var fY = fRds * Math.cos( fRad2 * pi ); + + if ( !( ( j == 0 || j == iVer ) && i > 0 ) ) { + + oVtx = this.vertices.push( new THREE.Vertex( new THREE.Vector3( fY, fZ, fX ) ) ) - 1; + + } + + aRow.push( oVtx ); + + } + + aVtc.push( aRow ); + + } + + var n1, n2, n3, iVerNum = aVtc.length; + + for ( j = 0; j < iVerNum; j++ ) { + + var iHorNum = aVtc[ j ].length; + + if ( j > 0 ) { + + for ( i = 0; i < iHorNum; i++ ) { + + var bEnd = i == ( iHorNum - 1 ); + var aP1 = aVtc[ j ][ bEnd ? 0 : i + 1 ]; + var aP2 = aVtc[ j ][ ( bEnd ? iHorNum - 1 : i ) ]; + var aP3 = aVtc[ j - 1 ][ ( bEnd ? iHorNum - 1 : i ) ]; + var aP4 = aVtc[ j - 1 ][ bEnd ? 0 : i + 1 ]; + + var fJ0 = j / ( iVerNum - 1 ); + var fJ1 = ( j - 1 ) / ( iVerNum - 1 ); + var fI0 = ( i + 1 ) / iHorNum; + var fI1 = i / iHorNum; + + var aP1uv = new THREE.UV( 1 - fI0, fJ0 ); + var aP2uv = new THREE.UV( 1 - fI1, fJ0 ); + var aP3uv = new THREE.UV( 1 - fI1, fJ1 ); + var aP4uv = new THREE.UV( 1 - fI0, fJ1 ); + + if ( j < ( aVtc.length - 1 ) ) { + + n1 = this.vertices[ aP1 ].position.clone(); + n2 = this.vertices[ aP2 ].position.clone(); + n3 = this.vertices[ aP3 ].position.clone(); + n1.normalize(); + n2.normalize(); + n3.normalize(); + + this.faces.push( new THREE.Face3( aP1, aP2, aP3, [ new THREE.Vector3( n1.x, n1.y, n1.z ), new THREE.Vector3( n2.x, n2.y, n2.z ), new THREE.Vector3( n3.x, n3.y, n3.z ) ] ) ); + + this.faceVertexUvs[ 0 ].push( [ aP1uv, aP2uv, aP3uv ] ); + + } + + if ( j > 1 ) { + + n1 = this.vertices[aP1].position.clone(); + n2 = this.vertices[aP3].position.clone(); + n3 = this.vertices[aP4].position.clone(); + n1.normalize(); + n2.normalize(); + n3.normalize(); + + this.faces.push( new THREE.Face3( aP1, aP3, aP4, [ new THREE.Vector3( n1.x, n1.y, n1.z ), new THREE.Vector3( n2.x, n2.y, n2.z ), new THREE.Vector3( n3.x, n3.y, n3.z ) ] ) ); + + this.faceVertexUvs[ 0 ].push( [ aP1uv, aP3uv, aP4uv ] ); + + } + + } + } + } + + this.computeCentroids(); + this.computeFaceNormals(); + this.computeVertexNormals(); + + this.boundingSphere = { radius: radius }; + +}; + +THREE.SphereGeometry.prototype = new THREE.Geometry(); +THREE.SphereGeometry.prototype.constructor = THREE.SphereGeometry; +/** + * @author zz85 / http://www.lab4games.net/zz85/blog + * @author alteredq / http://alteredqualia.com/ + * + * For creating 3D text geometry in three.js + * + * Text = 3D Text + * + * parameters = { + * size: , // size of the text + * height: , // thickness to extrude text + * curveSegments: , // number of points on the curves + * + * font: , // font name + * weight: , // font weight (normal, bold) + * style: , // font style (normal, italics) + * + * bezelEnabled: , // turn on bezel + * bezelThickness: , // how deep into text bezel goes + * bezelSize: , // how far from text outline is bezel + * } + * + * It uses techniques used in + * + * typeface.js and canvastext + * For converting fonts and rendering with javascript + * http://typeface.neocracy.org + * + * Triangulation ported from AS3 + * Simple Polygon Triangulation + * http://actionsnippet.com/?p=1462 + * + * A Method to triangulate shapes with holes + * http://www.sakri.net/blog/2009/06/12/an-approach-to-triangulating-polygons-with-holes/ + * + */ + +THREE.TextGeometry = function ( text, parameters ) { + + THREE.Geometry.call( this ); + + this.parameters = parameters || {}; + this.set( text ); + +}; + +THREE.TextGeometry.prototype = new THREE.Geometry(); +THREE.TextGeometry.prototype.constructor = THREE.TextGeometry; + +THREE.TextGeometry.prototype.set = function ( text, parameters ) { + + this.text = text; + var parameters = parameters || this.parameters; + + var size = parameters.size !== undefined ? parameters.size : 100; + var height = parameters.height !== undefined ? parameters.height : 50; + var curveSegments = parameters.curveSegments !== undefined ? parameters.curveSegments: 4; + + var font = parameters.font !== undefined ? parameters.font : "helvetiker"; + var weight = parameters.weight !== undefined ? parameters.weight : "normal"; + var style = parameters.style !== undefined ? parameters.style : "normal"; + + var bezelThickness = parameters.bezelThickness !== undefined ? parameters.bezelThickness : 10; + var bezelSize = parameters.bezelSize !== undefined ? parameters.bezelSize : 8; + var bezelEnabled = parameters.bezelEnabled !== undefined ? parameters.bezelEnabled : false; + + THREE.FontUtils.size = size; + THREE.FontUtils.divisions = curveSegments; + + THREE.FontUtils.face = font; + THREE.FontUtils.weight = weight; + THREE.FontUtils.style = style; + + THREE.FontUtils.bezelSize = bezelSize; + + // Get a Font data json object + + var data = THREE.FontUtils.drawText( text ); + + //console.log("data", data); + + var vertices = data.points; + var faces = data.faces; + var contour = data.contour; + var bezelPoints = data.bezel; + + var scope = this; + + scope.vertices = []; + scope.faces = []; + + //console.log(this); + + var i, + vert, vlen = vertices.length, + face, flen = faces.length, + bezelPt, blen = bezelPoints.length; + + // Back facing vertices + + for ( i = 0; i < vlen; i++ ) { + + vert = vertices[ i ]; + v( vert.x, vert.y, 0 ); + + } + + // Front facing vertices + + for ( i = 0; i < vlen; i++ ) { + + vert = vertices[ i ]; + v( vert.x, vert.y, height ); + + } + + if ( bezelEnabled ) { + + for ( i = 0; i < blen; i++ ) { + + bezelPt = bezelPoints[ i ]; + v( bezelPt.x, bezelPt.y, bezelThickness ); + + } + + for ( i = 0; i < blen; i++ ) { + + bezelPt = bezelPoints[ i ]; + v( bezelPt.x, bezelPt.y, height - bezelThickness ); + + } + + } + + // Bottom faces + + for ( i = 0; i < flen; i++ ) { + + face = faces[ i ]; + f3( face[ 2 ], face[ 1 ], face[ 0 ] ); + + } + + // Top faces + + for ( i = 0; i < flen; i++ ) { + + face = faces[ i ]; + f3( face[ 0 ] + vlen, face[ 1 ] + vlen, face[ 2 ] + vlen ); + + } + + var lastV; + var j, k, l, m; + + if ( bezelEnabled ) { + + i = bezelPoints.length; + + while ( --i > 0 ) { + + if ( !lastV ) { + + lastV = contour[ i ]; + + } else if ( lastV.equals( contour[ i ] ) ) { + + // We reached the last point of a closed loop + + lastV = null; + continue; + + } + + l = vlen * 2 + i; + m = l - 1; + + // Create faces for the z-sides of the text + + f4( l, m, m + blen, l + blen ); + + for ( j = 0; j < vlen; j++ ) { + + if ( vertices[ j ].equals( contour[ i ] ) ) break; + + } + + for ( k = 0; k < vlen; k++ ) { + + if ( vertices[ k ].equals( contour[ i - 1 ] ) ) break; + + } + + // Create faces for the z-sides of the text + + f4( j, k, m, l ); + f4( l + blen, m + blen, k + vlen, j + vlen ); + + } + + } else { + + i = contour.length; + + while ( --i > 0 ) { + + if ( !lastV ) { + + lastV = contour[ i ]; + + } else if ( lastV.equals( contour[ i ] ) ) { + + // We reached the last point of a closed loop + + lastV = null; + continue; + + } + + for ( j = 0; j < vlen; j++ ) { + + if ( vertices[ j ].equals( contour[ i ] ) ) break; + + } + + for ( k = 0; k < vlen; k++ ) { + + if ( vertices[ k ].equals( contour[ i - 1 ] ) ) break; + + } + + // Create faces for the z-sides of the text + + f4( j, k, k + vlen, j + vlen ); + + } + } + + + // UVs to be added + + this.computeCentroids(); + this.computeFaceNormals(); + //this.computeVertexNormals(); + + function v( x, y, z ) { + + scope.vertices.push( new THREE.Vertex( new THREE.Vector3( x, y, z ) ) ); + + } + + function f3( a, b, c ) { + + scope.faces.push( new THREE.Face3( a, b, c) ); + + } + + function f4( a, b, c, d ) { + + scope.faces.push( new THREE.Face4( a, b, c, d) ); + + } + + +}; + +THREE.FontUtils = { + + faces : {}, + + // Just for now. face[weight][style] + + face : "helvetiker", + weight: "normal", + style : "normal", + size : 150, + divisions : 10, + + getFace : function() { + + return this.faces[ this.face ][ this.weight ][ this.style ]; + + }, + + loadFace : function( data ) { + + var family = data.familyName.toLowerCase(); + + var ThreeFont = this; + + ThreeFont.faces[ family ] = ThreeFont.faces[ family ] || {}; + + ThreeFont.faces[ family ][ data.cssFontWeight ] = ThreeFont.faces[ family ][ data.cssFontWeight ] || {}; + ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data; + + var face = ThreeFont.faces[ family ][ data.cssFontWeight ][ data.cssFontStyle ] = data; + + return data; + + }, + + + extractPoints : function( points, characters ) { + + // Quick exit + + if ( points.length < 3 ) { + + //throw "not valid polygon"; + + console.log( "not valid polygon" ); + + return { + + points: points, + faces: [] + + }; + + } + + // Try to split shapes and holes. + + var p, point, shape, + all, + isolatedShapes = []; + + // Use a quick hashmap for locating duplicates + + for ( var c in characters ) { + + points = characters[ c ]; + + + all = []; + + // Use a quick hashmap for locating duplicates + + for ( var p in points ) { + + point = points[ p ]; + all.push( point.x + "," + point.y ); + + } + + var firstIndex, firstPt, endPt, holes; + + // We check the first loop whether its CW or CCW direction to determine + // whether its shapes or holes first + + endPt = all.slice(1).indexOf( all[0] ); + var shapesFirst = this.Triangulate.area( points.slice( 0, endPt + 1 ) ) < 0; + + //console.log(points.length, "shapesFirst",shapesFirst); + + holes = []; + endPt = -1; + + while ( endPt < all.length ) { + + firstIndex = endPt + 1; + firstPt = all[ firstIndex ]; + endPt = all.slice( firstIndex + 1 ).indexOf( firstPt ) + firstIndex; + + if ( endPt <= firstIndex ) break; + + var contours = points.slice( firstIndex, endPt + 1 ); + + if ( shapesFirst ) { + + if ( this.Triangulate.area( contours ) < 0 ) { + + // we got new isolated shape + + if ( firstIndex > 0 ) { + + isolatedShapes.push( { shape: shape, holes: holes } ); + + } + + // Save the old shapes, then work on new additional separated shape + + shape = contours; + holes = []; + + } else { + + holes.push( contours ); + + } + + } else { + + if ( this.Triangulate.area( contours ) < 0 ) { + + isolatedShapes.push( {shape: contours, holes: holes } ); + holes = []; + + } else { + + holes.push( contours ); + + } + + } + + endPt++; + + } + + if ( shapesFirst ) { + + isolatedShapes.push( { shape: shape, holes: holes } ); + + } + + } + + //console.log("isolatedShapes", isolatedShapes); + + /* For each isolated shape, find the closest points and break to the hole to allow triangulation*/ + + // Find closest points between holes + + // we could optimize with + // http://en.wikipedia.org/wiki/Proximity_problems + // http://en.wikipedia.org/wiki/Closest_pair_of_points + // http://stackoverflow.com/questions/1602164/shortest-distance-between-points-algorithm + + var prevShapeVert, nextShapeVert, + prevHoleVert, nextHoleVert, + holeIndex, shapeIndex, + shapeId, shapeGroup, + h, h2, + hole, shortest, d, + p, pts1, pts2, + tmpShape1, tmpShape2, + tmpHole1, tmpHole2, + verts = []; + + for ( shapeId = 0; shapeId < isolatedShapes.length; shapeId ++ ) { + + shapeGroup = isolatedShapes[ shapeId ]; + + shape = shapeGroup.shape; + holes = shapeGroup.holes; + + for ( h = 0; h < holes.length; h++ ) { + + // we slice to each hole when neccessary + + hole = holes[ h ]; + shortest = Number.POSITIVE_INFINITY; + + for ( h2 = 0; h2 < hole.length; h2++ ) { + + pts1 = hole[ h2 ]; + + for ( p = 0; p < shape.length; p++ ) { + + pts2 = shape[ p ]; + d = pts1.distanceTo( pts2 ); + + if ( d < shortest ) { + + shortest = d; + holeIndex = h2; + shapeIndex = p; + + } + + } + + } + + prevShapeVert = ( shapeIndex - 1 ) >= 0 ? shapeIndex - 1 : shape.length - 1; + nextShapeVert = ( shapeIndex + 1 ) < shape.length ? shapeIndex + 1 : 0; + + prevHoleVert = ( holeIndex - 1 ) >= 0 ? holeIndex - 1 : hole.length - 1; + nextHoleVert = ( holeIndex + 1 ) < hole.length ? holeIndex + 1 : 0 ; + + var areaapts = []; + areaapts.push( hole[ holeIndex ] ); + areaapts.push( shape[ shapeIndex ] ); + areaapts.push( shape[ prevShapeVert ] ); + + var areaa = this.Triangulate.area( areaapts ); + + var areabpts = []; + areabpts.push( hole[ holeIndex ] ); + areabpts.push( hole[ prevHoleVert ] ); + areabpts.push( shape[ shapeIndex ] ); + + var areab = this.Triangulate.area( areabpts ); + + var shapeOffset =1; + var holeOffset = -1; + + var oldShapeIndex = shapeIndex, oldHoleIndex = holeIndex; + shapeIndex += shapeOffset; + holeIndex += holeOffset; + + if ( shapeIndex < 0 ) { shapeIndex += shape.length; } + shapeIndex %= shape.length; + + if ( holeIndex < 0 ) { holeIndex += hole.length; } + holeIndex %= shape.length; + + prevShapeVert = ( shapeIndex - 1 ) >= 0 ? shapeIndex - 1 : shape.length - 1; + nextShapeVert = ( shapeIndex + 1 ) < shape.length ? shapeIndex + 1 : 0; + + prevHoleVert = ( holeIndex - 1 ) >= 0 ? holeIndex - 1 : hole.length - 1; + nextHoleVert = ( holeIndex + 1 ) < hole.length ? holeIndex + 1 : 0 ; + + + areaapts = []; + areaapts.push( hole[ holeIndex ] ); + areaapts.push( shape[ shapeIndex ] ); + areaapts.push( shape[ prevShapeVert ] ); + + var areaa2 = this.Triangulate.area( areaapts ); + + areabpts = []; + areabpts.push( hole[ holeIndex ] ); + areabpts.push( hole[ prevHoleVert ] ); + areabpts.push( shape[ shapeIndex ] ); + + var areab2 = this.Triangulate.area( areabpts ); + + if ( ( areaa + areab ) > ( areaa2 + areab2 ) ) { + + shapeIndex = oldShapeIndex; + holeIndex = oldHoleIndex ; + + if ( shapeIndex < 0 ) { shapeIndex += shape.length; } + shapeIndex %= shape.length; + + if ( holeIndex < 0 ) { holeIndex += hole.length; } + holeIndex %= shape.length; + + + prevShapeVert = ( shapeIndex - 1 ) >= 0 ? shapeIndex - 1 : shape.length - 1; + nextShapeVert = ( shapeIndex + 1 ) < shape.length ? shapeIndex + 1 : 0; + + prevHoleVert = ( holeIndex - 1 ) >= 0 ? holeIndex - 1 : hole.length - 1; + nextHoleVert = ( holeIndex + 1 ) < hole.length ? holeIndex + 1 : 0 ; + + } + + tmpShape1 = shape.slice( 0, shapeIndex ); + tmpShape2 = shape.slice( shapeIndex ); + tmpHole1 = hole.slice( holeIndex ); + tmpHole2 = hole.slice( 0, holeIndex ); + + verts.push( hole[ holeIndex ] ); + verts.push( shape[ shapeIndex ] ); + verts.push( shape[ prevShapeVert ] ); + + verts.push( hole[ holeIndex ] ); + verts.push( hole[ prevHoleVert ] ); + verts.push( shape[ shapeIndex ] ); + + shape = tmpShape1.concat( tmpHole1 ).concat( tmpHole2 ).concat( tmpShape2 ); + + } + + shapeGroup.shape = shape; + + } + + var points = []; + var triangulatedVertices = []; + var lastTriangles = 0; + + for ( shapeId = 0; shapeId < isolatedShapes.length; shapeId ++ ) { + + shapeGroup = isolatedShapes[ shapeId ]; + + shape = shapeGroup.shape; + points = points.concat( shape ); + + var triangles = THREE.FontUtils.Triangulate( shape, true ); + + // We need to offset vertex indices for faces + + for ( var v = 0; v < triangles.length; v++ ) { + + var face = triangles[ v ]; + + face[ 0 ] += lastTriangles; + face[ 1 ] += lastTriangles; + face[ 2 ] += lastTriangles; + + } + + triangulatedVertices = triangulatedVertices.concat( triangles ); + lastTriangles += shape.length; + + } + + + // Now we push the "cut" vertices back to the triangulated indices. + + //console.log("we have verts.length",verts.length,verts); + + var v, j, k, l, found, face; + + for ( v = 0; v < verts.length / 3; v++ ) { + + face = []; + + for ( k = 0; k < 3; k++ ) { + + found = false; + + for ( j=0; j < points.length && !found; j++ ) { + + l = v * 3 + k; + + if ( points[ j ].equals( verts[ l ] ) ) { + + face.push( j ); + found = true; + + } + + } + + // you would not wish to reach this point of code, something went wrong + + if ( !found ) { + + points.push( verts[ l ] ); + face.push( points.length - 1 ); + + console.log( "not found" ) + + } + + } + + triangulatedVertices.push( face ); + + } + + + //console.log("triangles", triangulatedVertices.length, "points", points); + + return { + + points: points, + faces: triangulatedVertices + + }; + + }, + + drawText : function( text ) { + + var characterpts = [], pts = []; + + // RenderText + + var i, p, + face = this.getFace(), + scale = this.size / face.resolution, + offset = 0, + chars = String( text ).split( '' ), + length = chars.length; + + for ( i = 0; i < length; i++ ) { + + var ret = this.extractGlyphPoints( chars[ i ], face, scale, offset ); + offset += ret.offset; + characterpts.push(ret.points); + pts = pts.concat(ret.points); + + } + + // get the width + + var width = offset / 2; + + for ( p = 0; p < pts.length; p++ ) { + + pts[ p ].x -= width; + + } + + var extract = this.extractPoints( pts, characterpts ); + + extract.contour = pts; + + var bezelPoints = []; + + var centroids = [], forCentroids = [], expandOutwards = [], sum = new THREE.Vector2(), lastV; + + i = pts.length; + + while ( --i >= 0 ) { + + if ( !lastV ) { + + lastV = pts[ i ]; + + } else if ( lastV.equals( pts[ i ] ) ) { + + // We reached the last point of a closed loop + + lastV = null; + + var bool = this.Triangulate.area( forCentroids ) > 0; + expandOutwards.push( bool ); + centroids.push( sum.divideScalar( forCentroids.length ) ); + forCentroids = []; + + sum = new THREE.Vector2(); + continue; + + } + + sum.addSelf( pts[ i ] ); + forCentroids.push( pts[ i ] ); + + } + + + i = pts.length; + p = 0; + var pt, centroid ; + var dirV, adj; + + while ( --i >= 0 ) { + + pt = pts[ i ]; + centroid = centroids[p]; + + dirV = pt.clone().subSelf( centroid) ; + adj = this.bezelSize / dirV.length(); + + if ( expandOutwards[ p ] ) { + + adj += 1; + + } else { + + adj = 1 - adj; + + } + + adj = dirV.multiplyScalar( adj ).addSelf( centroid ); + bezelPoints.unshift( adj ); + + + if ( !lastV ) { + + lastV = pts[ i ]; + + } else if ( lastV.equals( pts[ i ] ) ) { + + // We reached the last point of a closed loop + + lastV = null; + p++; + continue; + + } + + } + + + /* + for ( p = 0; p < pts.length; p++ ) { + pt = pts[ p ]; + bezelPoints.push (new THREE.Vector2(pt.x + this.bezelSize, pt.y + this.bezelSize)); + + }*/ + + extract.bezel = bezelPoints; + + return extract; + + }, + + + // Bezier Curves formulas obtained from + // http://en.wikipedia.org/wiki/B%C3%A9zier_curve + + // Quad Bezier Functions + + b2p0: function ( t, p ) { + + var k = 1 - t; + return k * k * p; + + }, + + b2p1: function ( t, p ) { + + return 2 * ( 1 - t ) * t * p; + + }, + + b2p2: function ( t, p ) { + + return t * t * p; + + }, + + b2: function ( t, p0, p1, p2 ) { + + return this.b2p0( t, p0 ) + this.b2p1( t, p1 ) + this.b2p2( t, p2 ); + + }, + + // Cubic Bezier Functions + + b3p0: function ( t, p ) { + + var k = 1 - t; + return k * k * k * p; + + }, + + b3p1: function ( t, p ) { + + var k = 1 - t; + return 3 * k * k * t * p; + + }, + + b3p2: function ( t, p ) { + + var k = 1 - t; + return 3 * k * t * t * p; + + }, + + b3p3: function ( t, p ) { + + return t * t * t * p; + + }, + + b3: function ( t, p0, p1, p2, p3 ) { + + return this.b3p0( t, p0 ) + this.b3p1( t, p1 ) + this.b3p2( t, p2 ) + this.b3p3( t, p3 ); + + }, + + + extractGlyphPoints : function( c, face, scale, offset ) { + + var pts = []; + + var i, i2, + outline, action, length, + scaleX, scaleY, + x, y, cpx, cpy, cpx0, cpy0, cpx1, cpy1, cpx2, cpy2, + laste, + glyph = face.glyphs[ c ] || face.glyphs[ ctxt.options.fallbackCharacter ]; + + if ( !glyph ) return; + + if ( glyph.o ) { + + outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) ); + length = outline.length; + + scaleX = scale; + scaleY = scale; + + for ( i = 0; i < length; ) { + + action = outline[ i++ ]; + + switch( action ) { + + case 'm': + + // Move To + + x = outline[ i++ ] * scaleX + offset; + y = outline[ i++ ] * scaleY; + pts.push( new THREE.Vector2( x, y ) ); + break; + + case 'l': + + // Line To + + x = outline[ i++ ] * scaleX + offset; + y = outline[ i++ ] * scaleY; + pts.push( new THREE.Vector2( x, y ) ); + break; + + case 'q': + + // QuadraticCurveTo + + cpx = outline[ i++ ] * scaleX + offset; + cpy = outline[ i++ ] * scaleY; + cpx1 = outline[ i++ ] * scaleX + offset; + cpy1 = outline[ i++ ] * scaleY; + + laste = pts[ pts.length - 1 ]; + + if ( laste ) { + + cpx0 = laste.x; + cpy0 = laste.y; + + for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2++ ) { + + var t = i2 / divisions; + var tx = THREE.FontUtils.b2( t, cpx0, cpx1, cpx ); + var ty = THREE.FontUtils.b2( t, cpy0, cpy1, cpy ); + pts.push( new THREE.Vector2( tx, ty ) ); + + } + + } + + break; + + case 'b': + + // Cubic Bezier Curve + + cpx = outline[ i++ ] * scaleX + offset; + cpy = outline[ i++ ] * scaleY; + cpx1 = outline[ i++ ] * scaleX + offset; + cpy1 = outline[ i++ ] * -scaleY; + cpx2 = outline[ i++ ] * scaleX + offset; + cpy2 = outline[ i++ ] * -scaleY; + + laste = pts[ pts.length - 1 ]; + + if ( laste ) { + + cpx0 = laste.x; + cpy0 = laste.y; + + for ( i2 = 1, divisions = this.divisions; i2 <= divisions; i2++ ) { + + var t = i2 / divisions; + var tx = THREE.FontUtils.b3( t, cpx0, cpx1, cpx2, cpx ); + var ty = THREE.FontUtils.b3( t, cpy0, cpy1, cpy2, cpy ); + pts.push( new THREE.Vector2( tx, ty ) ); + + } + + } + + break; + + } + + } + } + + return { offset: glyph.ha*scale, points:pts }; + } + +}; + + + +/** + * This code is a quick port of code written in C++ which was submitted to + * flipcode.com by John W. Ratcliff // July 22, 2000 + * See original code and more information here: + * http://www.flipcode.com/archives/Efficient_Polygon_Triangulation.shtml + * + * ported to actionscript by Zevan Rosser + * www.actionsnippet.com + * + * ported to javascript by Joshua Koo + * http://www.lab4games.net/zz85/blog + * + */ + + +( function( namespace ) { + + var EPSILON = 0.0000000001; + + // takes in an contour array and returns + + var process = function( contour, indices ) { + + var n = contour.length; + + if ( n < 3 ) return null; + + var result = [], + verts = [], + vertIndices = []; + + /* we want a counter-clockwise polygon in verts */ + + var u, v, w; + + if ( area( contour ) > 0.0 ) { + + for ( v = 0; v < n; v++ ) verts[ v ] = v; + + } else { + + for ( v = 0; v < n; v++ ) verts[ v ] = ( n - 1 ) - v; + + } + + var nv = n; + + /* remove nv - 2 vertices, creating 1 triangle every time */ + + var count = 2 * nv; /* error detection */ + + for( v = nv - 1; nv > 2; ) { + + /* if we loop, it is probably a non-simple polygon */ + + if ( ( count-- ) <= 0 ) { + + //** Triangulate: ERROR - probable bad polygon! + + //throw ( "Warning, unable to triangulate polygon!" ); + //return null; + + console.log( "Warning, unable to triangulate polygon!" ); + + if ( indices ) return vertIndices; + return result; + + } + + /* three consecutive vertices in current polygon, */ + + u = v; if ( nv <= u ) u = 0; /* previous */ + v = u + 1; if ( nv <= v ) v = 0; /* new v */ + w = v + 1; if ( nv <= w ) w = 0; /* next */ + + if ( snip( contour, u, v, w, nv, verts ) ) { + + var a, b, c, s, t; + + /* true names of the vertices */ + + a = verts[ u ]; + b = verts[ v ]; + c = verts[ w ]; + + /* output Triangle */ + + result.push( contour[ a ] ); + result.push( contour[ b ] ); + result.push( contour[ c ] ); + + vertIndices.push( [ verts[ u ], verts[ v ], verts[ w ] ] ); + + /* remove v from the remaining polygon */ + + for( s = v, t = v + 1; t < nv; s++, t++ ) { + + verts[ s ] = verts[ t ]; + + } + + nv--; + + /* reset error detection counter */ + + count = 2 * nv; + + } + + } + + if ( indices ) return vertIndices; + return result; + + }; + + // calculate area of the contour polygon + + var area = function ( contour ) { + + var n = contour.length; + var a = 0.0; + + for( var p = n - 1, q = 0; q < n; p = q++ ) { + + a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y; + + } + + return a * 0.5; + + }; + + // see if p is inside triangle abc + + var insideTriangle = function( ax, ay, + bx, by, + cx, cy, + px, py ) { + + var aX, aY, bX, bY; + var cX, cY, apx, apy; + var bpx, bpy, cpx, cpy; + var cCROSSap, bCROSScp, aCROSSbp; + + aX = cx - bx; aY = cy - by; + bX = ax - cx; bY = ay - cy; + cX = bx - ax; cY = by - ay; + apx= px -ax; apy= py - ay; + bpx= px - bx; bpy= py - by; + cpx= px - cx; cpy= py - cy; + + aCROSSbp = aX*bpy - aY*bpx; + cCROSSap = cX*apy - cY*apx; + bCROSScp = bX*cpy - bY*cpx; + + return ( (aCROSSbp >= 0.0) && (bCROSScp >= 0.0) && (cCROSSap >= 0.0) ); + + }; + + + var snip = function ( contour, u, v, w, n, verts ) { + + var p; + var ax, ay, bx, by; + var cx, cy, px, py; + + ax = contour[ verts[ u ] ].x; + ay = contour[ verts[ u ] ].y; + + bx = contour[ verts[ v ] ].x; + by = contour[ verts[ v ] ].y; + + cx = contour[ verts[ w ] ].x; + cy = contour[ verts[ w ] ].y; + + if ( EPSILON > (((bx-ax)*(cy-ay)) - ((by-ay)*(cx-ax))) ) return false; + + for ( p = 0; p < n; p++ ) { + + if( (p == u) || (p == v) || (p == w) ) continue; + + px = contour[ verts[ p ] ].x + py = contour[ verts[ p ] ].y + + if ( insideTriangle( ax, ay, bx, by, cx, cy, px, py ) ) return false; + + } + + return true; + + }; + + + namespace.Triangulate = process; + namespace.Triangulate.area = area; + + return namespace; + +})(THREE.FontUtils); + +// To use the typeface.js face files, hook up the API +window._typeface_js = { faces: THREE.FontUtils.faces, loadFace: THREE.FontUtils.loadFace }; +/** + * @author oosmoxiecode + * based on http://code.google.com/p/away3d/source/browse/trunk/fp10/Away3DLite/src/away3dlite/primitives/Torus.as?r=2888 + */ + +THREE.TorusGeometry = function ( radius, tube, segmentsR, segmentsT ) { + + THREE.Geometry.call( this ); + + var scope = this; + + this.radius = radius || 100; + this.tube = tube || 40; + this.segmentsR = segmentsR || 8; + this.segmentsT = segmentsT || 6; + + var temp_uv = []; + + for ( var j = 0; j <= this.segmentsR; ++j ) { + + for ( var i = 0; i <= this.segmentsT; ++i ) { + + var u = i / this.segmentsT * 2 * Math.PI; + var v = j / this.segmentsR * 2 * Math.PI; + var x = (this.radius + this.tube*Math.cos(v))*Math.cos(u); + var y = (this.radius + this.tube*Math.cos(v))*Math.sin(u); + var z = this.tube*Math.sin(v); + + vert( x, y, z ); + + temp_uv.push( [i/this.segmentsT, 1 - j/this.segmentsR] ); + + } + } + + + for ( var j = 1; j <= this.segmentsR; ++j ) { + + for ( var i = 1; i <= this.segmentsT; ++i ) { + + var a = (this.segmentsT + 1)*j + i; + var b = (this.segmentsT + 1)*j + i - 1; + var c = (this.segmentsT + 1)*(j - 1) + i - 1; + var d = (this.segmentsT + 1)*(j - 1) + i; + + f4( a, b, c,d ); + + this.faceVertexUvs[ 0 ].push( [new THREE.UV( temp_uv[a][0], temp_uv[a][1] ), + new THREE.UV( temp_uv[b][0], temp_uv[b][1] ), + new THREE.UV( temp_uv[c][0], temp_uv[c][1] ), + new THREE.UV( temp_uv[d][0], temp_uv[d][1] ) + ] ); + } + + } + + delete temp_uv; + + this.computeCentroids(); + this.computeFaceNormals(); + this.computeVertexNormals(); + + function vert( x, y, z ) { + + scope.vertices.push( new THREE.Vertex( new THREE.Vector3( x, y, z ) ) ); + + }; + + function f4( a, b, c, d ) { + + scope.faces.push( new THREE.Face4( a, b, c, d ) ); + + }; + +}; + +THREE.TorusGeometry.prototype = new THREE.Geometry(); +THREE.TorusGeometry.prototype.constructor = THREE.TorusGeometry; +/** + * @author oosmoxiecode + * based on http://code.google.com/p/away3d/source/browse/trunk/fp10/Away3D/src/away3d/primitives/TorusKnot.as?spec=svn2473&r=2473 + */ + +THREE.TorusKnotGeometry = function ( radius, tube, segmentsR, segmentsT, p, q, heightScale ) { + + THREE.Geometry.call( this ); + + var scope = this; + + this.radius = radius || 200; + this.tube = tube || 40; + this.segmentsR = segmentsR || 64; + this.segmentsT = segmentsT || 8; + this.p = p || 2; + this.q = q || 3; + this.heightScale = heightScale || 1; + this.grid = new Array(this.segmentsR); + + var tang = new THREE.Vector3(); + var n = new THREE.Vector3(); + var bitan = new THREE.Vector3(); + + for ( var i = 0; i < this.segmentsR; ++ i ) { + + this.grid[ i ] = new Array( this.segmentsT ); + + for ( var j = 0; j < this.segmentsT; ++ j ) { + + var u = i / this.segmentsR * 2 * this.p * Math.PI; + var v = j / this.segmentsT * 2 * Math.PI; + var p = getPos( u, v, this.q, this.p, this.radius, this.heightScale ); + var p2 = getPos( u + 0.01, v, this.q, this.p, this.radius, this.heightScale ); + var cx, cy; + + tang.x = p2.x - p.x; tang.y = p2.y - p.y; tang.z = p2.z - p.z; + n.x = p2.x + p.x; n.y = p2.y + p.y; n.z = p2.z + p.z; + bitan.cross( tang, n ); + n.cross( bitan, tang ); + bitan.normalize(); + n.normalize(); + + cx = - this.tube * Math.cos( v ); // TODO: Hack: Negating it so it faces outside. + cy = this.tube * Math.sin( v ); + + p.x += cx * n.x + cy * bitan.x; + p.y += cx * n.y + cy * bitan.y; + p.z += cx * n.z + cy * bitan.z; + + this.grid[ i ][ j ] = vert( p.x, p.y, p.z ); + + } + + } + + for ( var i = 0; i < this.segmentsR; ++ i ) { + + for ( var j = 0; j < this.segmentsT; ++ j ) { + + var ip = ( i + 1 ) % this.segmentsR; + var jp = ( j + 1 ) % this.segmentsT; + var a = this.grid[ i ][ j ]; + var b = this.grid[ ip ][ j ]; + var c = this.grid[ ip ][ jp ]; + var d = this.grid[ i ][ jp ]; + + var uva = new THREE.UV( i / this.segmentsR, j / this.segmentsT ); + var uvb = new THREE.UV( ( i + 1 ) / this.segmentsR, j / this.segmentsT ); + var uvc = new THREE.UV( ( i + 1 ) / this.segmentsR, ( j + 1 ) / this.segmentsT ); + var uvd = new THREE.UV( i / this.segmentsR, ( j + 1 ) / this.segmentsT ); + + this.faces.push( new THREE.Face4( a, b, c, d ) ); + this.faceVertexUvs[ 0 ].push( [ uva,uvb,uvc, uvd ] ); + + } + } + + this.computeCentroids(); + this.computeFaceNormals(); + this.computeVertexNormals(); + + function vert( x, y, z ) { + + return scope.vertices.push( new THREE.Vertex( new THREE.Vector3( x, y, z ) ) ) - 1; + + } + + function getPos( u, v, in_q, in_p, radius, heightScale ) { + + var cu = Math.cos( u ); + var cv = Math.cos( v ); + var su = Math.sin( u ); + var quOverP = in_q / in_p * u; + var cs = Math.cos( quOverP ); + + var tx = radius * ( 2 + cs ) * 0.5 * cu; + var ty = radius * ( 2 + cs ) * su * 0.5; + var tz = heightScale * radius * Math.sin( quOverP ) * 0.5; + + return new THREE.Vector3( tx, ty, tz ); + + } + +}; + +THREE.TorusKnotGeometry.prototype = new THREE.Geometry(); +THREE.TorusKnotGeometry.prototype.constructor = THREE.TorusKnotGeometry; +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.Loader = function ( showStatus ) { + + this.showStatus = showStatus; + this.statusDomElement = showStatus ? THREE.Loader.prototype.addStatusElement() : null; + + this.onLoadStart = function () {}; + this.onLoadProgress = function() {}; + this.onLoadComplete = function () {}; + +}; + +THREE.Loader.prototype = { + + addStatusElement: function () { + + var e = document.createElement( "div" ); + + e.style.position = "absolute"; + e.style.right = "0px"; + e.style.top = "0px"; + e.style.fontSize = "0.8em"; + e.style.textAlign = "left"; + e.style.background = "rgba(0,0,0,0.25)"; + e.style.color = "#fff"; + e.style.width = "120px"; + e.style.padding = "0.5em 0.5em 0.5em 0.5em"; + e.style.zIndex = 1000; + + e.innerHTML = "Loading ..."; + + return e; + + }, + + updateProgress: function ( progress ) { + + var message = "Loaded "; + + if ( progress.total ) { + + message += ( 100 * progress.loaded / progress.total ).toFixed(0) + "%"; + + + } else { + + message += ( progress.loaded / 1000 ).toFixed(2) + " KB"; + + } + + this.statusDomElement.innerHTML = message; + + }, + + extractUrlbase: function( url ) { + + var chunks = url.split( "/" ); + chunks.pop(); + return chunks.join( "/" ); + + }, + + init_materials: function( scope, materials, texture_path ) { + + scope.materials = []; + + for ( var i = 0; i < materials.length; ++i ) { + + scope.materials[ i ] = [ THREE.Loader.prototype.createMaterial( materials[ i ], texture_path ) ]; + + } + + }, + + createMaterial: function ( m, texture_path ) { + + function is_pow2( n ) { + + var l = Math.log( n ) / Math.LN2; + return Math.floor( l ) == l; + + } + + function nearest_pow2( n ) { + + var l = Math.log( n ) / Math.LN2; + return Math.pow( 2, Math.round( l ) ); + + } + + function load_image( where, url ) { + + var image = new Image(); + + image.onload = function () { + + if ( !is_pow2( this.width ) || !is_pow2( this.height ) ) { + + var w = nearest_pow2( this.width ), + h = nearest_pow2( this.height ); + + where.image.width = w; + where.image.height = h; + where.image.getContext("2d").drawImage( this, 0, 0, w, h ); + + } else { + + where.image = this; + + } + + where.needsUpdate = true; + + }; + + image.src = url; + + } + + var material, mtype, mpars, texture, color, vertexColors; + + // defaults + + mtype = "MeshLambertMaterial"; + + // vertexColors + + mpars = { color: 0xeeeeee, opacity: 1.0, map: null, lightMap: null, wireframe: m.wireframe }; + + // parameters from model file + + if ( m.shading ) { + + if ( m.shading == "Phong" ) mtype = "MeshPhongMaterial"; + else if ( m.shading == "Basic" ) mtype = "MeshBasicMaterial"; + + } + + if ( m.blending ) { + + if ( m.blending == "Additive" ) mpars.blending = THREE.AdditiveBlending; + else if ( m.blending == "Subtractive" ) mpars.blending = THREE.SubtractiveBlending; + else if ( m.blending == "Multiply" ) mpars.blending = THREE.MultiplyBlending; + + } + + if ( m.transparent !== undefined || m.opacity < 1.0 ) { + + mpars.transparent = m.transparent; + + } + + if ( m.depthTest !== undefined ) { + + mpars.depthTest = m.depthTest; + + } + + if ( m.vertexColors !== undefined ) { + + if ( m.vertexColors == "face" ) { + + mpars.vertexColors = THREE.FaceColors; + + } else if ( m.vertexColors ) { + + mpars.vertexColors = THREE.VertexColors; + + } + + } + + if ( m.mapDiffuse && texture_path ) { + + texture = document.createElement( 'canvas' ); + + mpars.map = new THREE.Texture( texture ); + mpars.map.sourceFile = m.mapDiffuse; + + if( m.mapDiffuseRepeat ) { + + mpars.map.repeat.set( m.mapDiffuseRepeat[ 0 ], m.mapDiffuseRepeat[ 1 ] ); + mpars.map.wrapS = mpars.map.wrapT = THREE.RepeatWrapping; + + } + + if( m.mapDiffuseOffset ) { + + mpars.map.offset.set( m.mapDiffuseOffset[ 0 ], m.mapDiffuseOffset[ 1 ] ); + + } + + load_image( mpars.map, texture_path + "/" + m.mapDiffuse ); + + } else if ( m.colorDiffuse ) { + + color = ( m.colorDiffuse[0] * 255 << 16 ) + ( m.colorDiffuse[1] * 255 << 8 ) + m.colorDiffuse[2] * 255; + mpars.color = color; + mpars.opacity = m.transparency; + + } else if ( m.DbgColor ) { + + mpars.color = m.DbgColor; + + } + + if ( m.mapLight && texture_path ) { + + texture = document.createElement( 'canvas' ); + + mpars.lightMap = new THREE.Texture( texture ); + mpars.lightMap.sourceFile = m.mapLight; + + if( m.mapLightmapRepeat ) { + + mpars.lightMap.repeat.set( m.mapLightRepeat[ 0 ], m.mapLightRepeat[ 1 ] ); + mpars.lightMap.wrapS = mpars.lightMap.wrapT = THREE.RepeatWrapping; + + } + + if( m.mapLightmapOffset ) { + + mpars.lightMap.offset.set( m.mapLightmapOffset[ 0 ], m.mapLightmapOffset[ 1 ] ); + + } + + load_image( mpars.lightMap, texture_path + "/" + m.mapLightmap ); + + } + + material = new THREE[ mtype ]( mpars ); + + return material; + + } + +}; +/** + * @author mrdoob / http://mrdoob.com/ + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.JSONLoader = function ( showStatus ) { + + THREE.Loader.call( this, showStatus ); + +}; + +THREE.JSONLoader.prototype = new THREE.Loader(); +THREE.JSONLoader.prototype.constructor = THREE.JSONLoader; +THREE.JSONLoader.prototype.supr = THREE.Loader.prototype; + + +/** + * Load models generated by slim OBJ converter with ASCII option (converter_obj_three_slim.py -t ascii) + * - parameters + * - model (required) + * - callback (required) + * - texture_path (optional: if not specified, textures will be assumed to be in the same folder as JS model file) + */ + +THREE.JSONLoader.prototype.load = function ( parameters ) { + + var scope = this, + url = parameters.model, + callback = parameters.callback, + texture_path = parameters.texture_path ? parameters.texture_path : this.extractUrlbase( url ), + worker = new Worker( url ); + + worker.onmessage = function ( event ) { + + scope.createModel( event.data, callback, texture_path ); + scope.onLoadComplete(); + + }; + + this.onLoadStart(); + worker.postMessage( new Date().getTime() ); + +}; + +THREE.JSONLoader.prototype.createModel = function ( json, callback, texture_path ) { + + var scope = this, + geometry = new THREE.Geometry(), + scale = ( json.scale !== undefined ) ? 1.0 / json.scale : 1.0; + + this.init_materials( geometry, json.materials, texture_path ); + + parseModel( scale ); + + parseSkin(); + parseMorphing( scale ); + parseEdges(); + + geometry.computeCentroids(); + geometry.computeFaceNormals(); + + // geometry.computeEdgeFaces(); + + function parseModel( scale ) { + + if ( json.version === undefined || json.version != 2 ) { + + console.error( 'Deprecated file format.' ); + return; + + } + + function isBitSet( value, position ) { + + return value & ( 1 << position ); + + }; + + var i, j, fi, + + offset, zLength, nVertices, + + colorIndex, normalIndex, uvIndex, materialIndex, + + type, + isQuad, + hasMaterial, + hasFaceUv, hasFaceVertexUv, + hasFaceNormal, hasFaceVertexNormal, + hasFaceColor, hasFaceVertexColor, + + vertex, face, color, normal, + + uvLayer, uvs, u, v, + + faces = json.faces, + vertices = json.vertices, + normals = json.normals, + colors = json.colors, + + nUvLayers = 0; + + // disregard empty arrays + + for ( i = 0; i < json.uvs.length; i++ ) { + + if ( json.uvs[ i ].length ) nUvLayers ++; + + } + + for ( i = 0; i < nUvLayers; i++ ) { + + geometry.faceUvs[ i ] = []; + geometry.faceVertexUvs[ i ] = []; + + } + + offset = 0; + zLength = vertices.length; + + while ( offset < zLength ) { + + vertex = new THREE.Vertex(); + + vertex.position.x = vertices[ offset ++ ] * scale; + vertex.position.y = vertices[ offset ++ ] * scale; + vertex.position.z = vertices[ offset ++ ] * scale; + + geometry.vertices.push( vertex ); + + } + + offset = 0; + zLength = faces.length; + + while ( offset < zLength ) { + + type = faces[ offset ++ ]; + + + isQuad = isBitSet( type, 0 ); + hasMaterial = isBitSet( type, 1 ); + hasFaceUv = isBitSet( type, 2 ); + hasFaceVertexUv = isBitSet( type, 3 ); + hasFaceNormal = isBitSet( type, 4 ); + hasFaceVertexNormal = isBitSet( type, 5 ); + hasFaceColor = isBitSet( type, 6 ); + hasFaceVertexColor = isBitSet( type, 7 ); + + //console.log("type", type, "bits", isQuad, hasMaterial, hasFaceUv, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor); + + if ( isQuad ) { + + face = new THREE.Face4(); + + face.a = faces[ offset ++ ]; + face.b = faces[ offset ++ ]; + face.c = faces[ offset ++ ]; + face.d = faces[ offset ++ ]; + + nVertices = 4; + + } else { + + face = new THREE.Face3(); + + face.a = faces[ offset ++ ]; + face.b = faces[ offset ++ ]; + face.c = faces[ offset ++ ]; + + nVertices = 3; + + } + + if ( hasMaterial ) { + + materialIndex = faces[ offset ++ ]; + face.materials = geometry.materials[ materialIndex ]; + + } + + // to get face <=> uv index correspondence + + fi = geometry.faces.length; + + if ( hasFaceUv ) { + + for ( i = 0; i < nUvLayers; i++ ) { + + uvLayer = json.uvs[ i ]; + + uvIndex = faces[ offset ++ ]; + + u = uvLayer[ uvIndex * 2 ]; + v = uvLayer[ uvIndex * 2 + 1 ]; + + geometry.faceUvs[ i ][ fi ] = new THREE.UV( u, v ); + + } + + } + + if ( hasFaceVertexUv ) { + + for ( i = 0; i < nUvLayers; i++ ) { + + uvLayer = json.uvs[ i ]; + + uvs = []; + + for ( j = 0; j < nVertices; j ++ ) { + + uvIndex = faces[ offset ++ ]; + + u = uvLayer[ uvIndex * 2 ]; + v = uvLayer[ uvIndex * 2 + 1 ]; + + uvs[ j ] = new THREE.UV( u, v ); + + } + + geometry.faceVertexUvs[ i ][ fi ] = uvs; + + } + + } + + if ( hasFaceNormal ) { + + normalIndex = faces[ offset ++ ] * 3; + + normal = new THREE.Vector3(); + + normal.x = normals[ normalIndex ++ ]; + normal.y = normals[ normalIndex ++ ]; + normal.z = normals[ normalIndex ]; + + face.normal = normal; + + } + + if ( hasFaceVertexNormal ) { + + for ( i = 0; i < nVertices; i++ ) { + + normalIndex = faces[ offset ++ ] * 3; + + normal = new THREE.Vector3(); + + normal.x = normals[ normalIndex ++ ]; + normal.y = normals[ normalIndex ++ ]; + normal.z = normals[ normalIndex ]; + + face.vertexNormals.push( normal ); + + } + + } + + + if ( hasFaceColor ) { + + colorIndex = faces[ offset ++ ]; + + color = new THREE.Color( colors[ colorIndex ] ); + face.color = color; + + } + + + if ( hasFaceVertexColor ) { + + for ( i = 0; i < nVertices; i++ ) { + + colorIndex = faces[ offset ++ ]; + + color = new THREE.Color( colors[ colorIndex ] ); + face.vertexColors.push( color ); + + } + + } + + geometry.faces.push( face ); + + } + + }; + + function parseSkin() { + + var i, l, x, y, z, w, a, b, c, d; + + if ( json.skinWeights ) { + + for ( i = 0, l = json.skinWeights.length; i < l; i += 2 ) { + + x = json.skinWeights[ i ]; + y = json.skinWeights[ i + 1 ]; + z = 0; + w = 0; + + geometry.skinWeights.push( new THREE.Vector4( x, y, z, w ) ); + + } + + } + + if ( json.skinIndices ) { + + for ( i = 0, l = json.skinIndices.length; i < l; i += 2 ) { + + a = json.skinIndices[ i ]; + b = json.skinIndices[ i + 1 ]; + c = 0; + d = 0; + + geometry.skinIndices.push( new THREE.Vector4( a, b, c, d ) ); + + } + + } + + geometry.bones = json.bones; + geometry.animation = json.animation; + + }; + + function parseMorphing( scale ) { + + if ( json.morphTargets !== undefined ) { + + var i, l, v, vl, x, y, z, dstVertices, srcVertices; + + for ( i = 0, l = json.morphTargets.length; i < l; i++ ) { + + geometry.morphTargets[ i ] = {}; + geometry.morphTargets[ i ].name = json.morphTargets[ i ].name; + geometry.morphTargets[ i ].vertices = []; + + dstVertices = geometry.morphTargets[ i ].vertices; + srcVertices = json.morphTargets [ i ].vertices; + + for( v = 0, vl = srcVertices.length; v < vl; v += 3 ) { + + x = srcVertices[ v ] * scale; + y = srcVertices[ v + 1 ] * scale; + z = srcVertices[ v + 2 ] * scale; + + dstVertices.push( new THREE.Vertex( new THREE.Vector3( x, y, z ) ) ); + + } + + } + + } + + if ( json.morphColors !== undefined ) { + + var i, l, c, cl, dstColors, srcColors, color; + + for ( i = 0, l = json.morphColors.length; i < l; i++ ) { + + geometry.morphColors[ i ] = {}; + geometry.morphColors[ i ].name = json.morphColors[ i ].name; + geometry.morphColors[ i ].colors = []; + + dstColors = geometry.morphColors[ i ].colors; + srcColors = json.morphColors [ i ].colors; + + for ( c = 0, cl = srcColors.length; c < cl; c += 3 ) { + + color = new THREE.Color( 0xffaa00 ); + color.setRGB( srcColors[ c ], srcColors[ c + 1 ], srcColors[ c + 2 ] ); + dstColors.push( color ); + + } + + } + + } + + }; + + function parseEdges() { + + if( json.edges !== undefined ) { + + var i, il, v1, v2; + + for ( i = 0; i < json.edges.length; i+= 2 ) { + + v1 = json.edges[ i ]; + v2 = json.edges[ i + 1 ]; + + geometry.edges.push( new THREE.Edge( geometry.vertices[ v1 ], geometry.vertices[ v2 ], v1, v2 ) ); + + } + + } + + }; + + callback( geometry ); + +} +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.BinaryLoader = function ( showStatus ) { + + THREE.Loader.call( this, showStatus ); + +}; + +THREE.BinaryLoader.prototype = new THREE.Loader(); +THREE.BinaryLoader.prototype.constructor = THREE.BinaryLoader; +THREE.BinaryLoader.prototype.supr = THREE.Loader.prototype; + + +THREE.BinaryLoader.prototype = { + + // Load models generated by slim OBJ converter with BINARY option (converter_obj_three_slim.py -t binary) + // - binary models consist of two files: JS and BIN + // - parameters + // - model (required) + // - callback (required) + // - bin_path (optional: if not specified, binary file will be assumed to be in the same folder as JS model file) + // - texture_path (optional: if not specified, textures will be assumed to be in the same folder as JS model file) + + load: function( parameters ) { + + // #1 load JS part via web worker + + // This isn't really necessary, JS part is tiny, + // could be done by more ordinary means. + + var url = parameters.model, + callback = parameters.callback, + texture_path = parameters.texture_path ? parameters.texture_path : THREE.Loader.prototype.extractUrlbase( url ), + bin_path = parameters.bin_path ? parameters.bin_path : THREE.Loader.prototype.extractUrlbase( url ), + + s = (new Date).getTime(), + worker = new Worker( url ), + callback_progress = this.showProgress ? THREE.Loader.prototype.updateProgress : null; + + worker.onmessage = function( event ) { + + var materials = event.data.materials, + buffers = event.data.buffers; + + // #2 load BIN part via Ajax + + // For some reason it is faster doing loading from here than from within the worker. + // Maybe passing of ginormous string as message between threads is costly? + // Also, worker loading huge data by Ajax still freezes browser. Go figure, + // worker with baked ascii JSON data keeps browser more responsive. + + THREE.BinaryLoader.prototype.loadAjaxBuffers( buffers, materials, callback, bin_path, texture_path, callback_progress ); + + }; + + worker.onerror = function (event) { + + alert( "worker.onerror: " + event.message + "\n" + event.data ); + event.preventDefault(); + + }; + + worker.postMessage( s ); + + }, + + // Binary AJAX parser based on Magi binary loader + // https://github.com/kig/magi + + // Should look more into HTML5 File API + // See also other suggestions by Gregg Tavares + // https://groups.google.com/group/o3d-discuss/browse_thread/thread/a8967bc9ce1e0978 + + loadAjaxBuffers: function( buffers, materials, callback, bin_path, texture_path, callback_progress ) { + + var xhr = new XMLHttpRequest(), + url = bin_path + "/" + buffers; + + var length = 0; + + xhr.onreadystatechange = function() { + + if ( xhr.readyState == 4 ) { + + if ( xhr.status == 200 || xhr.status == 0 ) { + + THREE.BinaryLoader.prototype.createBinModel( xhr.responseText, callback, texture_path, materials ); + + } else { + + alert( "Couldn't load [" + url + "] [" + xhr.status + "]" ); + + } + + } else if ( xhr.readyState == 3 ) { + + if ( callback_progress ) { + + if ( length == 0 ) { + + length = xhr.getResponseHeader( "Content-Length" ); + + } + + callback_progress( { total: length, loaded: xhr.responseText.length } ); + + } + + } else if ( xhr.readyState == 2 ) { + + length = xhr.getResponseHeader( "Content-Length" ); + + } + + } + + xhr.open("GET", url, true); + xhr.overrideMimeType("text/plain; charset=x-user-defined"); + xhr.setRequestHeader("Content-Type", "text/plain"); + xhr.send(null); + + }, + + createBinModel: function ( data, callback, texture_path, materials ) { + + var Model = function ( texture_path ) { + + //var s = (new Date).getTime(); + + var scope = this, + currentOffset = 0, + md, + normals = [], + uvs = [], + tri_b, tri_c, tri_m, tri_na, tri_nb, tri_nc, + quad_b, quad_c, quad_d, quad_m, quad_na, quad_nb, quad_nc, quad_nd, + tri_uvb, tri_uvc, quad_uvb, quad_uvc, quad_uvd, + start_tri_flat, start_tri_smooth, start_tri_flat_uv, start_tri_smooth_uv, + start_quad_flat, start_quad_smooth, start_quad_flat_uv, start_quad_smooth_uv, + tri_size, quad_size, + len_tri_flat, len_tri_smooth, len_tri_flat_uv, len_tri_smooth_uv, + len_quad_flat, len_quad_smooth, len_quad_flat_uv, len_quad_smooth_uv; + + + THREE.Geometry.call( this ); + + THREE.Loader.prototype.init_materials( scope, materials, texture_path ); + + md = parseMetaData( data, currentOffset ); + currentOffset += md.header_bytes; + + // cache offsets + + tri_b = md.vertex_index_bytes, + tri_c = md.vertex_index_bytes*2, + tri_m = md.vertex_index_bytes*3, + tri_na = md.vertex_index_bytes*3 + md.material_index_bytes, + tri_nb = md.vertex_index_bytes*3 + md.material_index_bytes + md.normal_index_bytes, + tri_nc = md.vertex_index_bytes*3 + md.material_index_bytes + md.normal_index_bytes*2, + + quad_b = md.vertex_index_bytes, + quad_c = md.vertex_index_bytes*2, + quad_d = md.vertex_index_bytes*3, + quad_m = md.vertex_index_bytes*4, + quad_na = md.vertex_index_bytes*4 + md.material_index_bytes, + quad_nb = md.vertex_index_bytes*4 + md.material_index_bytes + md.normal_index_bytes, + quad_nc = md.vertex_index_bytes*4 + md.material_index_bytes + md.normal_index_bytes*2, + quad_nd = md.vertex_index_bytes*4 + md.material_index_bytes + md.normal_index_bytes*3, + + tri_uvb = md.uv_index_bytes, + tri_uvc = md.uv_index_bytes * 2, + + quad_uvb = md.uv_index_bytes, + quad_uvc = md.uv_index_bytes * 2, + quad_uvd = md.uv_index_bytes * 3; + + // buffers sizes + + tri_size = md.vertex_index_bytes * 3 + md.material_index_bytes; + quad_size = md.vertex_index_bytes * 4 + md.material_index_bytes; + + len_tri_flat = md.ntri_flat * ( tri_size ); + len_tri_smooth = md.ntri_smooth * ( tri_size + md.normal_index_bytes * 3 ); + len_tri_flat_uv = md.ntri_flat_uv * ( tri_size + md.uv_index_bytes * 3 ); + len_tri_smooth_uv = md.ntri_smooth_uv * ( tri_size + md.normal_index_bytes * 3 + md.uv_index_bytes * 3 ); + + len_quad_flat = md.nquad_flat * ( quad_size ); + len_quad_smooth = md.nquad_smooth * ( quad_size + md.normal_index_bytes * 4 ); + len_quad_flat_uv = md.nquad_flat_uv * ( quad_size + md.uv_index_bytes * 4 ); + len_quad_smooth_uv = md.nquad_smooth_uv * ( quad_size + md.normal_index_bytes * 4 + md.uv_index_bytes * 4 ); + + // read buffers + + currentOffset += init_vertices( currentOffset ); + currentOffset += init_normals( currentOffset ); + currentOffset += init_uvs( currentOffset ); + + start_tri_flat = currentOffset; + start_tri_smooth = start_tri_flat + len_tri_flat; + start_tri_flat_uv = start_tri_smooth + len_tri_smooth; + start_tri_smooth_uv = start_tri_flat_uv + len_tri_flat_uv; + + start_quad_flat = start_tri_smooth_uv + len_tri_smooth_uv; + start_quad_smooth = start_quad_flat + len_quad_flat; + start_quad_flat_uv = start_quad_smooth + len_quad_smooth; + start_quad_smooth_uv= start_quad_flat_uv +len_quad_flat_uv; + + // have to first process faces with uvs + // so that face and uv indices match + + init_triangles_flat_uv( start_tri_flat_uv ); + init_triangles_smooth_uv( start_tri_smooth_uv ); + + init_quads_flat_uv( start_quad_flat_uv ); + init_quads_smooth_uv( start_quad_smooth_uv ); + + // now we can process untextured faces + + init_triangles_flat( start_tri_flat ); + init_triangles_smooth( start_tri_smooth ); + + init_quads_flat( start_quad_flat ); + init_quads_smooth( start_quad_smooth ); + + this.computeCentroids(); + this.computeFaceNormals(); + + //var e = (new Date).getTime(); + + //log( "binary data parse time: " + (e-s) + " ms" ); + + function parseMetaData( data, offset ) { + + var metaData = { + + 'signature' :parseString( data, offset, 8 ), + 'header_bytes' :parseUChar8( data, offset + 8 ), + + 'vertex_coordinate_bytes' :parseUChar8( data, offset + 9 ), + 'normal_coordinate_bytes' :parseUChar8( data, offset + 10 ), + 'uv_coordinate_bytes' :parseUChar8( data, offset + 11 ), + + 'vertex_index_bytes' :parseUChar8( data, offset + 12 ), + 'normal_index_bytes' :parseUChar8( data, offset + 13 ), + 'uv_index_bytes' :parseUChar8( data, offset + 14 ), + 'material_index_bytes' :parseUChar8( data, offset + 15 ), + + 'nvertices' :parseUInt32( data, offset + 16 ), + 'nnormals' :parseUInt32( data, offset + 16 + 4*1 ), + 'nuvs' :parseUInt32( data, offset + 16 + 4*2 ), + + 'ntri_flat' :parseUInt32( data, offset + 16 + 4*3 ), + 'ntri_smooth' :parseUInt32( data, offset + 16 + 4*4 ), + 'ntri_flat_uv' :parseUInt32( data, offset + 16 + 4*5 ), + 'ntri_smooth_uv' :parseUInt32( data, offset + 16 + 4*6 ), + + 'nquad_flat' :parseUInt32( data, offset + 16 + 4*7 ), + 'nquad_smooth' :parseUInt32( data, offset + 16 + 4*8 ), + 'nquad_flat_uv' :parseUInt32( data, offset + 16 + 4*9 ), + 'nquad_smooth_uv' :parseUInt32( data, offset + 16 + 4*10 ) + + }; + + /* + log( "signature: " + metaData.signature ); + + log( "header_bytes: " + metaData.header_bytes ); + log( "vertex_coordinate_bytes: " + metaData.vertex_coordinate_bytes ); + log( "normal_coordinate_bytes: " + metaData.normal_coordinate_bytes ); + log( "uv_coordinate_bytes: " + metaData.uv_coordinate_bytes ); + + log( "vertex_index_bytes: " + metaData.vertex_index_bytes ); + log( "normal_index_bytes: " + metaData.normal_index_bytes ); + log( "uv_index_bytes: " + metaData.uv_index_bytes ); + log( "material_index_bytes: " + metaData.material_index_bytes ); + + log( "nvertices: " + metaData.nvertices ); + log( "nnormals: " + metaData.nnormals ); + log( "nuvs: " + metaData.nuvs ); + + log( "ntri_flat: " + metaData.ntri_flat ); + log( "ntri_smooth: " + metaData.ntri_smooth ); + log( "ntri_flat_uv: " + metaData.ntri_flat_uv ); + log( "ntri_smooth_uv: " + metaData.ntri_smooth_uv ); + + log( "nquad_flat: " + metaData.nquad_flat ); + log( "nquad_smooth: " + metaData.nquad_smooth ); + log( "nquad_flat_uv: " + metaData.nquad_flat_uv ); + log( "nquad_smooth_uv: " + metaData.nquad_smooth_uv ); + + var total = metaData.header_bytes + + metaData.nvertices * metaData.vertex_coordinate_bytes * 3 + + metaData.nnormals * metaData.normal_coordinate_bytes * 3 + + metaData.nuvs * metaData.uv_coordinate_bytes * 2 + + metaData.ntri_flat * ( metaData.vertex_index_bytes*3 + metaData.material_index_bytes ) + + metaData.ntri_smooth * ( metaData.vertex_index_bytes*3 + metaData.material_index_bytes + metaData.normal_index_bytes*3 ) + + metaData.ntri_flat_uv * ( metaData.vertex_index_bytes*3 + metaData.material_index_bytes + metaData.uv_index_bytes*3 ) + + metaData.ntri_smooth_uv * ( metaData.vertex_index_bytes*3 + metaData.material_index_bytes + metaData.normal_index_bytes*3 + metaData.uv_index_bytes*3 ) + + metaData.nquad_flat * ( metaData.vertex_index_bytes*4 + metaData.material_index_bytes ) + + metaData.nquad_smooth * ( metaData.vertex_index_bytes*4 + metaData.material_index_bytes + metaData.normal_index_bytes*4 ) + + metaData.nquad_flat_uv * ( metaData.vertex_index_bytes*4 + metaData.material_index_bytes + metaData.uv_index_bytes*4 ) + + metaData.nquad_smooth_uv * ( metaData.vertex_index_bytes*4 + metaData.material_index_bytes + metaData.normal_index_bytes*4 + metaData.uv_index_bytes*4 ); + log( "total bytes: " + total ); + */ + + return metaData; + + } + + function parseString( data, offset, length ) { + + return data.substr( offset, length ); + + } + + function parseFloat32( data, offset ) { + + var b3 = parseUChar8( data, offset ), + b2 = parseUChar8( data, offset + 1 ), + b1 = parseUChar8( data, offset + 2 ), + b0 = parseUChar8( data, offset + 3 ), + + sign = 1 - ( 2 * ( b0 >> 7 ) ), + exponent = ((( b0 << 1 ) & 0xff) | ( b1 >> 7 )) - 127, + mantissa = (( b1 & 0x7f ) << 16) | (b2 << 8) | b3; + + if (mantissa == 0 && exponent == -127) + return 0.0; + + return sign * ( 1 + mantissa * Math.pow( 2, -23 ) ) * Math.pow( 2, exponent ); + + } + + function parseUInt32( data, offset ) { + + var b0 = parseUChar8( data, offset ), + b1 = parseUChar8( data, offset + 1 ), + b2 = parseUChar8( data, offset + 2 ), + b3 = parseUChar8( data, offset + 3 ); + + return (b3 << 24) + (b2 << 16) + (b1 << 8) + b0; + } + + function parseUInt16( data, offset ) { + + var b0 = parseUChar8( data, offset ), + b1 = parseUChar8( data, offset + 1 ); + + return (b1 << 8) + b0; + + } + + function parseSChar8( data, offset ) { + + var b = parseUChar8( data, offset ); + return b > 127 ? b - 256 : b; + + } + + function parseUChar8( data, offset ) { + + return data.charCodeAt( offset ) & 0xff; + } + + function init_vertices( start ) { + + var i, x, y, z, + stride = md.vertex_coordinate_bytes * 3, + end = start + md.nvertices * stride; + + for( i = start; i < end; i += stride ) { + + x = parseFloat32( data, i ); + y = parseFloat32( data, i + md.vertex_coordinate_bytes ); + z = parseFloat32( data, i + md.vertex_coordinate_bytes*2 ); + + THREE.BinaryLoader.prototype.v( scope, x, y, z ); + + } + + return md.nvertices * stride; + + } + + function init_normals( start ) { + + var i, x, y, z, + stride = md.normal_coordinate_bytes * 3, + end = start + md.nnormals * stride; + + for( i = start; i < end; i += stride ) { + + x = parseSChar8( data, i ); + y = parseSChar8( data, i + md.normal_coordinate_bytes ); + z = parseSChar8( data, i + md.normal_coordinate_bytes*2 ); + + normals.push( x/127, y/127, z/127 ); + + } + + return md.nnormals * stride; + + } + + function init_uvs( start ) { + + var i, u, v, + stride = md.uv_coordinate_bytes * 2, + end = start + md.nuvs * stride; + + for( i = start; i < end; i += stride ) { + + u = parseFloat32( data, i ); + v = parseFloat32( data, i + md.uv_coordinate_bytes ); + + uvs.push( u, v ); + + } + + return md.nuvs * stride; + + } + + function add_tri( i ) { + + var a, b, c, m; + + a = parseUInt32( data, i ); + b = parseUInt32( data, i + tri_b ); + c = parseUInt32( data, i + tri_c ); + + m = parseUInt16( data, i + tri_m ); + + THREE.BinaryLoader.prototype.f3( scope, a, b, c, m ); + + } + + function add_tri_n( i ) { + + var a, b, c, m, na, nb, nc; + + a = parseUInt32( data, i ); + b = parseUInt32( data, i + tri_b ); + c = parseUInt32( data, i + tri_c ); + + m = parseUInt16( data, i + tri_m ); + + na = parseUInt32( data, i + tri_na ); + nb = parseUInt32( data, i + tri_nb ); + nc = parseUInt32( data, i + tri_nc ); + + THREE.BinaryLoader.prototype.f3n( scope, normals, a, b, c, m, na, nb, nc ); + + } + + function add_quad( i ) { + + var a, b, c, d, m; + + a = parseUInt32( data, i ); + b = parseUInt32( data, i + quad_b ); + c = parseUInt32( data, i + quad_c ); + d = parseUInt32( data, i + quad_d ); + + m = parseUInt16( data, i + quad_m ); + + THREE.BinaryLoader.prototype.f4( scope, a, b, c, d, m ); + + } + + function add_quad_n( i ) { + + var a, b, c, d, m, na, nb, nc, nd; + + a = parseUInt32( data, i ); + b = parseUInt32( data, i + quad_b ); + c = parseUInt32( data, i + quad_c ); + d = parseUInt32( data, i + quad_d ); + + m = parseUInt16( data, i + quad_m ); + + na = parseUInt32( data, i + quad_na ); + nb = parseUInt32( data, i + quad_nb ); + nc = parseUInt32( data, i + quad_nc ); + nd = parseUInt32( data, i + quad_nd ); + + THREE.BinaryLoader.prototype.f4n( scope, normals, a, b, c, d, m, na, nb, nc, nd ); + + } + + function add_uv3( i ) { + + var uva, uvb, uvc, u1, u2, u3, v1, v2, v3; + + uva = parseUInt32( data, i ); + uvb = parseUInt32( data, i + tri_uvb ); + uvc = parseUInt32( data, i + tri_uvc ); + + u1 = uvs[ uva*2 ]; + v1 = uvs[ uva*2 + 1 ]; + + u2 = uvs[ uvb*2 ]; + v2 = uvs[ uvb*2 + 1 ]; + + u3 = uvs[ uvc*2 ]; + v3 = uvs[ uvc*2 + 1 ]; + + THREE.BinaryLoader.prototype.uv3( scope.faceVertexUvs[ 0 ], u1, v1, u2, v2, u3, v3 ); + + } + + function add_uv4( i ) { + + var uva, uvb, uvc, uvd, u1, u2, u3, u4, v1, v2, v3, v4; + + uva = parseUInt32( data, i ); + uvb = parseUInt32( data, i + quad_uvb ); + uvc = parseUInt32( data, i + quad_uvc ); + uvd = parseUInt32( data, i + quad_uvd ); + + u1 = uvs[ uva*2 ]; + v1 = uvs[ uva*2 + 1 ]; + + u2 = uvs[ uvb*2 ]; + v2 = uvs[ uvb*2 + 1 ]; + + u3 = uvs[ uvc*2 ]; + v3 = uvs[ uvc*2 + 1 ]; + + u4 = uvs[ uvd*2 ]; + v4 = uvs[ uvd*2 + 1 ]; + + THREE.BinaryLoader.prototype.uv4( scope.faceVertexUvs[ 0 ], u1, v1, u2, v2, u3, v3, u4, v4 ); + + } + + function init_triangles_flat( start ) { + + var i, stride = md.vertex_index_bytes * 3 + md.material_index_bytes, + end = start + md.ntri_flat * stride; + + for( i = start; i < end; i += stride ) { + + add_tri( i ); + + } + + return end - start; + + } + + function init_triangles_flat_uv( start ) { + + var i, offset = md.vertex_index_bytes * 3 + md.material_index_bytes, + stride = offset + md.uv_index_bytes * 3, + end = start + md.ntri_flat_uv * stride; + + for( i = start; i < end; i += stride ) { + + add_tri( i ); + add_uv3( i + offset ); + + } + + return end - start; + + } + + function init_triangles_smooth( start ) { + + var i, stride = md.vertex_index_bytes * 3 + md.material_index_bytes + md.normal_index_bytes * 3, + end = start + md.ntri_smooth * stride; + + for( i = start; i < end; i += stride ) { + + add_tri_n( i ); + + } + + return end - start; + + } + + function init_triangles_smooth_uv( start ) { + + var i, offset = md.vertex_index_bytes * 3 + md.material_index_bytes + md.normal_index_bytes * 3, + stride = offset + md.uv_index_bytes * 3, + end = start + md.ntri_smooth_uv * stride; + + for( i = start; i < end; i += stride ) { + + add_tri_n( i ); + add_uv3( i + offset ); + + } + + return end - start; + + } + + function init_quads_flat( start ) { + + var i, stride = md.vertex_index_bytes * 4 + md.material_index_bytes, + end = start + md.nquad_flat * stride; + + for( i = start; i < end; i += stride ) { + + add_quad( i ); + + } + + return end - start; + + } + + function init_quads_flat_uv( start ) { + + var i, offset = md.vertex_index_bytes * 4 + md.material_index_bytes, + stride = offset + md.uv_index_bytes * 4, + end = start + md.nquad_flat_uv * stride; + + for( i = start; i < end; i += stride ) { + + add_quad( i ); + add_uv4( i + offset ); + + } + + return end - start; + + } + + function init_quads_smooth( start ) { + + var i, stride = md.vertex_index_bytes * 4 + md.material_index_bytes + md.normal_index_bytes * 4, + end = start + md.nquad_smooth * stride; + + for( i = start; i < end; i += stride ) { + + add_quad_n( i ); + } + + return end - start; + + } + + function init_quads_smooth_uv( start ) { + + var i, offset = md.vertex_index_bytes * 4 + md.material_index_bytes + md.normal_index_bytes * 4, + stride = offset + md.uv_index_bytes * 4, + end = start + md.nquad_smooth_uv * stride; + + for( i = start; i < end; i += stride ) { + + add_quad_n( i ); + add_uv4( i + offset ); + + } + + return end - start; + + } + + } + + Model.prototype = new THREE.Geometry(); + Model.prototype.constructor = Model; + + callback( new Model( texture_path ) ); + + }, + + + v: function( scope, x, y, z ) { + + scope.vertices.push( new THREE.Vertex( new THREE.Vector3( x, y, z ) ) ); + + }, + + f3: function( scope, a, b, c, mi ) { + + var material = scope.materials[ mi ]; + scope.faces.push( new THREE.Face3( a, b, c, null, null, material ) ); + + }, + + f4: function( scope, a, b, c, d, mi ) { + + var material = scope.materials[ mi ]; + scope.faces.push( new THREE.Face4( a, b, c, d, null, null, material ) ); + + }, + + f3n: function( scope, normals, a, b, c, mi, na, nb, nc ) { + + var material = scope.materials[ mi ], + nax = normals[ na*3 ], + nay = normals[ na*3 + 1 ], + naz = normals[ na*3 + 2 ], + + nbx = normals[ nb*3 ], + nby = normals[ nb*3 + 1 ], + nbz = normals[ nb*3 + 2 ], + + ncx = normals[ nc*3 ], + ncy = normals[ nc*3 + 1 ], + ncz = normals[ nc*3 + 2 ]; + + scope.faces.push( new THREE.Face3( a, b, c, + [new THREE.Vector3( nax, nay, naz ), + new THREE.Vector3( nbx, nby, nbz ), + new THREE.Vector3( ncx, ncy, ncz )], + null, + material ) ); + + }, + + f4n: function( scope, normals, a, b, c, d, mi, na, nb, nc, nd ) { + + var material = scope.materials[ mi ], + nax = normals[ na*3 ], + nay = normals[ na*3 + 1 ], + naz = normals[ na*3 + 2 ], + + nbx = normals[ nb*3 ], + nby = normals[ nb*3 + 1 ], + nbz = normals[ nb*3 + 2 ], + + ncx = normals[ nc*3 ], + ncy = normals[ nc*3 + 1 ], + ncz = normals[ nc*3 + 2 ], + + ndx = normals[ nd*3 ], + ndy = normals[ nd*3 + 1 ], + ndz = normals[ nd*3 + 2 ]; + + scope.faces.push( new THREE.Face4( a, b, c, d, + [new THREE.Vector3( nax, nay, naz ), + new THREE.Vector3( nbx, nby, nbz ), + new THREE.Vector3( ncx, ncy, ncz ), + new THREE.Vector3( ndx, ndy, ndz )], + null, + material ) ); + + }, + + uv3: function( where, u1, v1, u2, v2, u3, v3 ) { + + var uv = []; + uv.push( new THREE.UV( u1, v1 ) ); + uv.push( new THREE.UV( u2, v2 ) ); + uv.push( new THREE.UV( u3, v3 ) ); + where.push( uv ); + + }, + + uv4: function( where, u1, v1, u2, v2, u3, v3, u4, v4 ) { + + var uv = []; + uv.push( new THREE.UV( u1, v1 ) ); + uv.push( new THREE.UV( u2, v2 ) ); + uv.push( new THREE.UV( u3, v3 ) ); + uv.push( new THREE.UV( u4, v4 ) ); + where.push( uv ); + + } + +}; +/** + * @author alteredq / http://alteredqualia.com/ + */ + +THREE.SceneLoader = function () { + + this.onLoadStart = function () {}; + this.onLoadProgress = function() {}; + this.onLoadComplete = function () {}; + + this.callbackSync = function () {}; + this.callbackProgress = function () {}; + +}; + +THREE.SceneLoader.prototype = { + + load : function ( url, callbackFinished ) { + + var scope = this; + + var worker = new Worker( url ); + worker.postMessage( 0 ); + + var urlBase = THREE.Loader.prototype.extractUrlbase( url ); + + worker.onmessage = function( event ) { + + var dg, dm, dd, dl, dc, df, dt, + g, o, m, l, p, c, t, f, tt, pp, + geometry, material, camera, fog, + texture, images, + materials, + data, binLoader, jsonLoader, + counter_models, counter_textures, + total_models, total_textures, + result; + + data = event.data; + + binLoader = new THREE.BinaryLoader(); + jsonLoader = new THREE.JSONLoader(); + + counter_models = 0; + counter_textures = 0; + + result = { + + scene: new THREE.Scene(), + geometries: {}, + materials: {}, + textures: {}, + objects: {}, + cameras: {}, + lights: {}, + fogs: {}, + triggers: {}, + empties: {} + + }; + + // find out if there are some colliders + + var hasColliders = false; + + for( dd in data.objects ) { + + o = data.objects[ dd ]; + + if ( o.meshCollider ) { + + hasColliders = true; + break; + + } + + } + + if ( hasColliders ) { + + result.scene.collisions = new THREE.CollisionSystem(); + + } + + if ( data.transform ) { + + var position = data.transform.position, + rotation = data.transform.rotation, + scale = data.transform.scale; + + if ( position ) + result.scene.position.set( position[ 0 ], position[ 1 ], position [ 2 ] ); + + if ( rotation ) + result.scene.rotation.set( rotation[ 0 ], rotation[ 1 ], rotation [ 2 ] ); + + if ( scale ) + result.scene.scale.set( scale[ 0 ], scale[ 1 ], scale [ 2 ] ); + + if ( position || rotation || scale ) + result.scene.updateMatrix(); + + } + + function get_url( source_url, url_type ) { + + if ( url_type == "relativeToHTML" ) { + + return source_url; + + } else { + + return urlBase + "/" + source_url; + + } + + }; + + function handle_objects() { + + for( dd in data.objects ) { + + if ( !result.objects[ dd ] ) { + + o = data.objects[ dd ]; + + if ( o.geometry !== undefined ) { + + geometry = result.geometries[ o.geometry ]; + + // geometry already loaded + + if ( geometry ) { + + materials = []; + for( i = 0; i < o.materials.length; i++ ) { + + materials[ i ] = result.materials[ o.materials[i] ]; + + } + + p = o.position; + r = o.rotation; + q = o.quaternion; + s = o.scale; + + // turn off quaternions, for the moment + + q = 0; + + if ( materials.length == 0 ) { + + materials[ 0 ] = new THREE.MeshFaceMaterial(); + + } + + // dirty hack to handle meshes with multiple materials + // just use face materials defined in model + + if ( materials.length > 1 ) { + + materials = [ new THREE.MeshFaceMaterial() ]; + + } + + object = new THREE.Mesh( geometry, materials ); + object.name = dd; + object.position.set( p[0], p[1], p[2] ); + + if ( q ) { + + object.quaternion.set( q[0], q[1], q[2], q[3] ); + object.useQuaternion = true; + + } else { + + object.rotation.set( r[0], r[1], r[2] ); + + } + + object.scale.set( s[0], s[1], s[2] ); + object.visible = o.visible; + + result.scene.addObject( object ); + + result.objects[ dd ] = object; + + if ( o.meshCollider ) { + + var meshCollider = THREE.CollisionUtils.MeshColliderWBox( object ); + result.scene.collisions.colliders.push( meshCollider ); + + } + + if ( o.castsShadow ) { + + //object.visible = true; + //object.materials = [ new THREE.MeshBasicMaterial( { color: 0xff0000 } ) ]; + + var shadow = new THREE.ShadowVolume( geometry ) + result.scene.addChild( shadow ); + + shadow.position = object.position; + shadow.rotation = object.rotation; + shadow.scale = object.scale; + + } + + if ( o.trigger && o.trigger.toLowerCase() != "none" ) { + + var trigger = { + "type" : o.trigger, + "object" : o + }; + + result.triggers[ object.name ] = trigger; + + } + + } + + // pure Object3D + + } else { + + p = o.position; + r = o.rotation; + q = o.quaternion; + s = o.scale; + + // turn off quaternions, for the moment + + q = 0; + + object = new THREE.Object3D(); + object.name = dd; + object.position.set( p[0], p[1], p[2] ); + + if ( q ) { + + object.quaternion.set( q[0], q[1], q[2], q[3] ); + object.useQuaternion = true; + + } else { + + object.rotation.set( r[0], r[1], r[2] ); + + } + + object.scale.set( s[0], s[1], s[2] ); + object.visible = ( o.visible !== undefined ) ? o.visible : false; + + result.scene.addObject( object ); + + result.objects[ dd ] = object; + result.empties[ dd ] = object; + + + if ( o.trigger && o.trigger.toLowerCase() != "none" ) { + + var trigger = { + "type" : o.trigger, + "object" : o + }; + + result.triggers[ object.name ] = trigger; + + } + + } + + } + + } + + }; + + function handle_mesh( geo, id ) { + + result.geometries[ id ] = geo; + handle_objects(); + + }; + + function create_callback( id ) { + + return function( geo ) { + + handle_mesh( geo, id ); + + counter_models -= 1; + + scope.onLoadComplete(); + + async_callback_gate(); + + } + + }; + + function create_callback_embed( id ) { + + return function( geo ) { + + result.geometries[ id ] = geo; + + } + + }; + + function async_callback_gate() { + + var progress = { + + totalModels : total_models, + totalTextures : total_textures, + loadedModels : total_models - counter_models, + loadedTextures : total_textures - counter_textures + + }; + + scope.callbackProgress( progress, result ); + + scope.onLoadProgress(); + + if( counter_models == 0 && counter_textures == 0 ) { + + callbackFinished( result ); + + } + + }; + + var callbackTexture = function( images ) { + + counter_textures -= 1; + async_callback_gate(); + + scope.onLoadComplete(); + + }; + + // first go synchronous elements + + // cameras + + for( dc in data.cameras ) { + + c = data.cameras[ dc ]; + + if ( c.type == "perspective" ) { + + camera = new THREE.Camera( c.fov, c.aspect, c.near, c.far ); + + } else if ( c.type == "ortho" ) { + + camera = new THREE.Camera(); + camera.projectionMatrix = THREE.Matrix4.makeOrtho( c.left, c.right, c.top, c.bottom, c.near, c.far ); + + } + + p = c.position; + t = c.target; + camera.position.set( p[0], p[1], p[2] ); + camera.target.position.set( t[0], t[1], t[2] ); + + result.cameras[ dc ] = camera; + + } + + // lights + + var hex, intensity; + + for ( dl in data.lights ) { + + l = data.lights[ dl ]; + + hex = ( l.color !== undefined ) ? l.color : 0xffffff; + intensity = ( l.intensity !== undefined ) ? l.intensity : 1; + + if ( l.type == "directional" ) { + + p = l.direction; + + light = new THREE.DirectionalLight( hex, intensity ); + light.position.set( p[0], p[1], p[2] ); + light.position.normalize(); + + } else if ( l.type == "point" ) { + + p = l.position; + + light = new THREE.PointLight( hex, intensity ); + light.position.set( p[0], p[1], p[2] ); + + } + + result.scene.addLight( light ); + + result.lights[ dl ] = light; + + } + + // fogs + + for( df in data.fogs ) { + + f = data.fogs[ df ]; + + if ( f.type == "linear" ) { + + fog = new THREE.Fog( 0x000000, f.near, f.far ); + + } else if ( f.type == "exp2" ) { + + fog = new THREE.FogExp2( 0x000000, f.density ); + + } + + c = f.color; + fog.color.setRGB( c[0], c[1], c[2] ); + + result.fogs[ df ] = fog; + + } + + // defaults + + if ( result.cameras && data.defaults.camera ) { + + result.currentCamera = result.cameras[ data.defaults.camera ]; + + } + + if ( result.fogs && data.defaults.fog ) { + + result.scene.fog = result.fogs[ data.defaults.fog ]; + + } + + c = data.defaults.bgcolor; + result.bgColor = new THREE.Color(); + result.bgColor.setRGB( c[0], c[1], c[2] ); + + result.bgColorAlpha = data.defaults.bgalpha; + + // now come potentially asynchronous elements + + // geometries + + // count how many models will be loaded asynchronously + + for( dg in data.geometries ) { + + g = data.geometries[ dg ]; + + if ( g.type == "bin_mesh" || g.type == "ascii_mesh" ) { + + counter_models += 1; + + scope.onLoadStart(); + + } + + } + + total_models = counter_models; + + for ( dg in data.geometries ) { + + g = data.geometries[ dg ]; + + if ( g.type == "cube" ) { + + geometry = new THREE.CubeGeometry( g.width, g.height, g.depth, g.segmentsWidth, g.segmentsHeight, g.segmentsDepth, null, g.flipped, g.sides ); + result.geometries[ dg ] = geometry; + + } else if ( g.type == "plane" ) { + + geometry = new THREE.PlaneGeometry( g.width, g.height, g.segmentsWidth, g.segmentsHeight ); + result.geometries[ dg ] = geometry; + + } else if ( g.type == "sphere" ) { + + geometry = new THREE.SphereGeometry( g.radius, g.segmentsWidth, g.segmentsHeight ); + result.geometries[ dg ] = geometry; + + } else if ( g.type == "cylinder" ) { + + geometry = new THREE.CylinderGeometry( g.numSegs, g.topRad, g.botRad, g.height, g.topOffset, g.botOffset ); + result.geometries[ dg ] = geometry; + + } else if ( g.type == "torus" ) { + + geometry = new THREE.TorusGeometry( g.radius, g.tube, g.segmentsR, g.segmentsT ); + result.geometries[ dg ] = geometry; + + } else if ( g.type == "icosahedron" ) { + + geometry = new THREE.IcosahedronGeometry( g.subdivisions ); + result.geometries[ dg ] = geometry; + + } else if ( g.type == "bin_mesh" ) { + + binLoader.load( { model: get_url( g.url, data.urlBaseType ), + callback: create_callback( dg ) + } ); + + } else if ( g.type == "ascii_mesh" ) { + + jsonLoader.load( { model: get_url( g.url, data.urlBaseType ), + callback: create_callback( dg ) + } ); + + } else if ( g.type == "embedded_mesh" ) { + + var modelJson = data.embeds[ g.id ], + texture_path = ""; + + if ( modelJson ) { + + jsonLoader.createModel( modelJson, create_callback_embed( dg ), texture_path ); + + } + + } + + } + + // textures + + // count how many textures will be loaded asynchronously + + for( dt in data.textures ) { + + tt = data.textures[ dt ]; + + if( tt.url instanceof Array ) { + + counter_textures += tt.url.length; + + for( var n = 0; n < tt.url.length; n ++ ) { + + scope.onLoadStart(); + + } + + } else { + + counter_textures += 1; + + scope.onLoadStart(); + + } + + } + + total_textures = counter_textures; + + for( dt in data.textures ) { + + tt = data.textures[ dt ]; + + if ( tt.mapping != undefined && THREE[ tt.mapping ] != undefined ) { + + tt.mapping = new THREE[ tt.mapping ](); + + } + + if( tt.url instanceof Array ) { + + var url_array = []; + + for( var i = 0; i < tt.url.length; i ++ ) { + + url_array[ i ] = get_url( tt.url[ i ], data.urlBaseType ); + + } + + texture = THREE.ImageUtils.loadTextureCube( url_array, tt.mapping, callbackTexture ); + + } else { + + texture = THREE.ImageUtils.loadTexture( get_url( tt.url, data.urlBaseType ), tt.mapping, callbackTexture ); + + if ( THREE[ tt.minFilter ] != undefined ) + texture.minFilter = THREE[ tt.minFilter ]; + + if ( THREE[ tt.magFilter ] != undefined ) + texture.magFilter = THREE[ tt.magFilter ]; + + if ( tt.repeat ) { + + texture.repeat.set( tt.repeat[ 0 ], tt.repeat[ 1 ] ); + texture.wrapS = texture.wrapT = THREE.RepeatWrapping; + + } + + } + + result.textures[ dt ] = texture; + + } + + // materials + + for ( dm in data.materials ) { + + m = data.materials[ dm ]; + + for ( pp in m.parameters ) { + + if ( pp == "envMap" || pp == "map" || pp == "lightMap" ) { + + m.parameters[ pp ] = result.textures[ m.parameters[ pp ] ]; + + } else if ( pp == "shading" ) { + + m.parameters[ pp ] = ( m.parameters[ pp ] == "flat" ) ? THREE.FlatShading : THREE.SmoothShading; + + } else if ( pp == "blending" ) { + + m.parameters[ pp ] = THREE[ m.parameters[ pp ] ] ? THREE[ m.parameters[ pp ] ] : THREE.NormalBlending; + + } else if ( pp == "combine" ) { + + m.parameters[ pp ] = ( m.parameters[ pp ] == "MixOperation" ) ? THREE.MixOperation : THREE.MultiplyOperation; + + } else if ( pp == "vertexColors" ) { + + if ( m.parameters[ pp ] == "face" ) { + + m.parameters[ pp ] = THREE.FaceColors; + + // default to vertex colors if "vertexColors" is anything else face colors or 0 / null / false + + } else if ( m.parameters[ pp ] ) { + + m.parameters[ pp ] = THREE.VertexColors; + + } + + } + + } + + if ( m.parameters.opacity !== undefined && m.parameters.opacity < 1.0 ) { + + m.parameters.transparent = true; + + } + + material = new THREE[ m.type ]( m.parameters ); + result.materials[ dm ] = material; + + } + + // objects ( synchronous init of procedural primitives ) + + handle_objects(); + + // synchronous callback + + scope.callbackSync( result ); + + }; + + } + +}; +/** + * @author alteredq / http://alteredqualia.com/ + * + * Port of greggman's ThreeD version of marching cubes to Three.js + * http://webglsamples.googlecode.com/hg/blob/blob.html + */ + +// do not crash if somebody includes the file in oldie browser + +THREE.MarchingCubes = function ( resolution, materials ) { + + THREE.Object3D.call( this ); + + this.materials = materials instanceof Array ? materials : [ materials ]; + + // functions have to be object properties + // prototype functions kill performance + // (tested and it was 4x slower !!!) + + this.init = function( resolution ) { + + // parameters + + this.isolation = 80.0; + + // size of field, 32 is pushing it in Javascript :) + + this.size = resolution; + this.size2 = this.size * this.size; + this.size3 = this.size2 * this.size; + this.halfsize = this.size / 2.0; + + // deltas + + this.delta = 2.0 / this.size; + this.yd = this.size; + this.zd = this.size2; + + this.field = new Float32Array( this.size3 ); + this.normal_cache = new Float32Array( this.size3 * 3 ); + + // temp buffers used in polygonize + + this.vlist = new Float32Array( 12 * 3 ); + this.nlist = new Float32Array( 12 * 3 ); + + this.firstDraw = true; + + // immediate render mode simulator + + this.maxCount = 4096; // TODO: find the fastest size for this buffer + this.count = 0; + this.hasPos = false; + this.hasNormal = false; + + this.positionArray = new Float32Array( this.maxCount * 3 ); + this.normalArray = new Float32Array( this.maxCount * 3 ); + + }; + + /////////////////////// + // Polygonization + /////////////////////// + + this.lerp = function( a, b, t ) { return a + ( b - a ) * t; }; + + this.VIntX = function( q, pout, nout, offset, isol, x, y, z, valp1, valp2 ) { + + var mu = ( isol - valp1 ) / ( valp2 - valp1 ), + nc = this.normal_cache; + + pout[ offset ] = x + mu * this.delta; + pout[ offset + 1 ] = y; + pout[ offset + 2 ] = z; + + nout[ offset ] = this.lerp( nc[ q ], nc[ q + 3 ], mu ); + nout[ offset + 1 ] = this.lerp( nc[ q + 1 ], nc[ q + 4 ], mu ); + nout[ offset + 2 ] = this.lerp( nc[ q + 2 ], nc[ q + 5 ], mu ); + + }; + + this.VIntY = function( q, pout, nout, offset, isol, x, y, z, valp1, valp2 ) { + + var mu = ( isol - valp1 ) / ( valp2 - valp1 ), + nc = this.normal_cache; + + pout[ offset ] = x; + pout[ offset + 1 ] = y + mu * this.delta; + pout[ offset + 2 ] = z; + + var q2 = q + this.yd * 3; + + nout[ offset ] = this.lerp( nc[ q ], nc[ q2 ], mu ); + nout[ offset + 1 ] = this.lerp( nc[ q + 1 ], nc[ q2 + 1 ], mu ); + nout[ offset + 2 ] = this.lerp( nc[ q + 2 ], nc[ q2 + 2 ], mu ); + + }; + + this.VIntZ = function( q, pout, nout, offset, isol, x, y, z, valp1, valp2 ) { + + var mu = ( isol - valp1 ) / ( valp2 - valp1 ), + nc = this.normal_cache; + + pout[ offset ] = x; + pout[ offset + 1 ] = y; + pout[ offset + 2 ] = z + mu * this.delta; + + var q2 = q + this.zd * 3; + + nout[ offset ] = this.lerp( nc[ q ], nc[ q2 ], mu ); + nout[ offset + 1 ] = this.lerp( nc[ q + 1 ], nc[ q2 + 1 ], mu ); + nout[ offset + 2 ] = this.lerp( nc[ q + 2 ], nc[ q2 + 2 ], mu ); + + }; + + this.compNorm = function( q ) { + + var q3 = q * 3; + + if ( this.normal_cache [ q3 ] == 0.0 ) { + + this.normal_cache[ q3 ] = this.field[ q - 1 ] - this.field[ q + 1 ]; + this.normal_cache[ q3 + 1 ] = this.field[ q - this.yd ] - this.field[ q + this.yd ]; + this.normal_cache[ q3 + 2 ] = this.field[ q - this.zd ] - this.field[ q + this.zd ]; + + } + + }; + + // Returns total number of triangles. Fills triangles. + // (this is where most of time is spent - it's inner work of O(n3) loop ) + + this.polygonize = function( fx, fy, fz, q, isol, render_callback ) { + + // cache indices + var q1 = q + 1, + qy = q + this.yd, + qz = q + this.zd, + q1y = q1 + this.yd, + q1z = q1 + this.zd, + qyz = q + this.yd + this.zd, + q1yz = q1 + this.yd + this.zd; + + var cubeindex = 0, + field0 = this.field[ q ], + field1 = this.field[ q1 ], + field2 = this.field[ qy ], + field3 = this.field[ q1y ], + field4 = this.field[ qz ], + field5 = this.field[ q1z ], + field6 = this.field[ qyz ], + field7 = this.field[ q1yz ]; + + if ( field0 < isol ) cubeindex |= 1; + if ( field1 < isol ) cubeindex |= 2; + if ( field2 < isol ) cubeindex |= 8; + if ( field3 < isol ) cubeindex |= 4; + if ( field4 < isol ) cubeindex |= 16; + if ( field5 < isol ) cubeindex |= 32; + if ( field6 < isol ) cubeindex |= 128; + if ( field7 < isol ) cubeindex |= 64; + + // if cube is entirely in/out of the surface - bail, nothing to draw + + var bits = THREE.edgeTable[ cubeindex ]; + if ( bits == 0 ) return 0; + + var d = this.delta, + fx2 = fx + d, + fy2 = fy + d, + fz2 = fz + d; + + // top of the cube + + if ( bits & 1 ) { + + this.compNorm( q ); + this.compNorm( q1 ); + this.VIntX( q * 3, this.vlist, this.nlist, 0, isol, fx, fy, fz, field0, field1 ); + + }; + + if ( bits & 2 ) { + + this.compNorm( q1 ); + this.compNorm( q1y ); + this.VIntY( q1 * 3, this.vlist, this.nlist, 3, isol, fx2, fy, fz, field1, field3 ); + + }; + + if ( bits & 4 ) { + + this.compNorm( qy ); + this.compNorm( q1y ); + this.VIntX( qy * 3, this.vlist, this.nlist, 6, isol, fx, fy2, fz, field2, field3 ); + + }; + + if ( bits & 8 ) { + + this.compNorm( q ); + this.compNorm( qy ); + this.VIntY( q * 3, this.vlist, this.nlist, 9, isol, fx, fy, fz, field0, field2 ); + + }; + + // bottom of the cube + + if ( bits & 16 ) { + + this.compNorm( qz ); + this.compNorm( q1z ); + this.VIntX( qz * 3, this.vlist, this.nlist, 12, isol, fx, fy, fz2, field4, field5 ); + + }; + + if ( bits & 32 ) { + + this.compNorm( q1z ); + this.compNorm( q1yz ); + this.VIntY( q1z * 3, this.vlist, this.nlist, 15, isol, fx2, fy, fz2, field5, field7 ); + + }; + + if ( bits & 64 ) { + + this.compNorm( qyz ); + this.compNorm( q1yz ); + this.VIntX( qyz * 3, this.vlist, this.nlist, 18, isol, fx, fy2, fz2, field6, field7 ); + + }; + + if ( bits & 128 ) { + + this.compNorm( qz ); + this.compNorm( qyz ); + this.VIntY( qz * 3, this.vlist, this.nlist, 21, isol, fx, fy, fz2, field4, field6 ); + + }; + + // vertical lines of the cube + + if ( bits & 256 ) { + + this.compNorm( q ); + this.compNorm( qz ); + this.VIntZ( q * 3, this.vlist, this.nlist, 24, isol, fx, fy, fz, field0, field4 ); + + }; + + if ( bits & 512 ) { + + this.compNorm( q1 ); + this.compNorm( q1z ); + this.VIntZ( q1 * 3, this.vlist, this.nlist, 27, isol, fx2, fy, fz, field1, field5 ); + + }; + + if ( bits & 1024 ) { + + this.compNorm( q1y ); + this.compNorm( q1yz ); + this.VIntZ( q1y * 3, this.vlist, this.nlist, 30, isol, fx2, fy2, fz, field3, field7 ); + + }; + + if ( bits & 2048 ) { + + this.compNorm( qy ); + this.compNorm( qyz ); + this.VIntZ( qy * 3, this.vlist, this.nlist, 33, isol, fx, fy2, fz, field2, field6 ); + + }; + + cubeindex <<= 4; // re-purpose cubeindex into an offset into triTable + + var o1, o2, o3, numtris = 0, i = 0; + + // here is where triangles are created + + while ( THREE.triTable[ cubeindex + i ] != -1 ) { + + o1 = cubeindex + i; + o2 = o1 + 1; + o3 = o1 + 2; + + this.posnormtriv( this.vlist, this.nlist, + 3 * THREE.triTable[ o1 ], + 3 * THREE.triTable[ o2 ], + 3 * THREE.triTable[ o3 ], + render_callback ); + + i += 3; + numtris ++; + + } + + return numtris; + + }; + + ///////////////////////////////////// + // Immediate render mode simulator + ///////////////////////////////////// + + this.posnormtriv = function( pos, norm, o1, o2, o3, render_callback ) { + + var c = this.count * 3; + + this.positionArray[ c ] = pos[ o1 ]; + this.positionArray[ c + 1 ] = pos[ o1 + 1 ]; + this.positionArray[ c + 2 ] = pos[ o1 + 2 ]; + + this.positionArray[ c + 3 ] = pos[ o2 ]; + this.positionArray[ c + 4 ] = pos[ o2 + 1 ]; + this.positionArray[ c + 5 ] = pos[ o2 + 2 ]; + + this.positionArray[ c + 6 ] = pos[ o3 ]; + this.positionArray[ c + 7 ] = pos[ o3 + 1 ]; + this.positionArray[ c + 8 ] = pos[ o3 + 2 ]; + + this.normalArray[ c ] = norm[ o1 ]; + this.normalArray[ c + 1 ] = norm[ o1 + 1 ]; + this.normalArray[ c + 2 ] = norm[ o1 + 2 ]; + + this.normalArray[ c + 3 ] = norm[ o2 ]; + this.normalArray[ c + 4 ] = norm[ o2 + 1 ]; + this.normalArray[ c + 5 ] = norm[ o2 + 2 ]; + + this.normalArray[ c + 6 ] = norm[ o3 ]; + this.normalArray[ c + 7 ] = norm[ o3 + 1 ]; + this.normalArray[ c + 8 ] = norm[ o3 + 2 ]; + + this.hasPos = true; + this.hasNormal = true; + + this.count += 3; + + if ( this.count >= this.maxCount - 3 ) { + + render_callback( this ); + + } + + }; + + this.begin = function( ) { + + this.count = 0; + this.hasPos = false; + this.hasNormal = false; + + }; + + this.end = function( render_callback ) { + + if ( this.count == 0 ) + return; + + for ( var i = this.count * 3; i < this.positionArray.length; i++ ) + this.positionArray[ i ] = 0.0; + + render_callback( this ); + + }; + + ///////////////////////////////////// + // Metaballs + ///////////////////////////////////// + + // Adds a reciprocal ball (nice and blobby) that, to be fast, fades to zero after + // a fixed distance, determined by strength and subtract. + + this.addBall = function( ballx, bally, ballz, strength, subtract ) { + + // Let's solve the equation to find the radius: + // 1.0 / (0.000001 + radius^2) * strength - subtract = 0 + // strength / (radius^2) = subtract + // strength = subtract * radius^2 + // radius^2 = strength / subtract + // radius = sqrt(strength / subtract) + + var radius = this.size * Math.sqrt( strength / subtract ), + zs = ballz * this.size, + ys = bally * this.size, + xs = ballx * this.size; + + var min_z = Math.floor( zs - radius ); if ( min_z < 1 ) min_z = 1; + var max_z = Math.floor( zs + radius ); if ( max_z > this.size - 1 ) max_z = this.size - 1; + var min_y = Math.floor( ys - radius ); if ( min_y < 1 ) min_y = 1; + var max_y = Math.floor( ys + radius ); if ( max_y > this.size - 1 ) max_y = this.size - 1; + var min_x = Math.floor( xs - radius ); if ( min_x < 1 ) min_x = 1; + var max_x = Math.floor( xs + radius ); if ( max_x > this.size - 1 ) max_x = this.size - 1; + + + // Don't polygonize in the outer layer because normals aren't + // well-defined there. + + var x, y, z, y_offset, z_offset, fx, fy, fz, fz2, fy2, val; + + for ( z = min_z; z < max_z; z++ ) { + + z_offset = this.size2 * z, + fz = z / this.size - ballz, + fz2 = fz * fz; + + for ( y = min_y; y < max_y; y++ ) { + + y_offset = z_offset + this.size * y; + fy = y / this.size - bally; + fy2 = fy * fy; + + for ( x = min_x; x < max_x; x++ ) { + + fx = x / this.size - ballx; + val = strength / ( 0.000001 + fx*fx + fy2 + fz2 ) - subtract; + if ( val > 0.0 ) this.field[ y_offset + x ] += val; + + } + + } + + } + + }; + + this.addPlaneX = function( strength, subtract ) { + + var x, y, z, xx, val, xdiv, cxy, + + // cache attribute lookups + size = this.size, + yd = this.yd, + zd = this.zd, + field = this.field, + + dist = size * Math.sqrt( strength / subtract ); + + if ( dist > size ) dist = size; + + for ( x = 0; x < dist; x++ ) { + + xdiv = x / size; + xx = xdiv * xdiv; + val = strength / ( 0.0001 + xx ) - subtract; + + if ( val > 0.0 ) { + + for ( y = 0; y < size; y++ ) { + + cxy = x + y * yd; + + for ( z = 0; z < size; z++ ) { + + field[ zd * z + cxy ] += val; + + } + + } + + } + + } + + }; + + this.addPlaneY = function( strength, subtract ) { + + var x, y, z, yy, val, ydiv, cy, cxy, + + // cache attribute lookups + size = this.size, + yd = this.yd, + zd = this.zd, + field = this.field, + + dist = size * Math.sqrt( strength / subtract ); + + if ( dist > size ) dist = size; + + for ( y = 0; y < dist; y++ ) { + + ydiv = y / size; + yy = ydiv * ydiv; + val = strength / ( 0.0001 + yy ) - subtract; + + if ( val > 0.0 ) { + + cy = y * yd; + + for ( x = 0; x < size; x++ ) { + + cxy = cy + x; + + for ( z = 0; z < size; z++ ) + field[ zd * z + cxy ] += val; + + } + + } + + } + + }; + + this.addPlaneZ = function( strength, subtract ) { + + var x, y, z, zz, val, zdiv, cz, cyz; + + // cache attribute lookups + size = this.size, + yd = this.yd, + zd = this.zd, + field = this.field, + + dist = size * Math.sqrt( strength / subtract ); + + if ( dist > size ) dist = size; + + for ( z = 0; z < dist; z++ ) { + + zdiv = z / size; + zz = zdiv * zdiv; + val = strength / ( 0.0001 + zz ) - subtract; + if ( val > 0.0 ) { + + cz = zd * z; + + for ( y = 0; y < size; y++ ) { + + cyz = cz + y * yd; + + for ( x = 0; x < size; x++ ) + field[ cyz + x ] += val; + + } + + } + + } + + }; + + ///////////////////////////////////// + // Updates + ///////////////////////////////////// + + this.reset = function() { + + var i; + + // wipe the normal cache + + for ( i = 0; i < this.size3; i++ ) { + + this.normal_cache[ i * 3 ] = 0.0; + this.field[ i ] = 0.0; + + } + + }; + + this.render = function( render_callback ) { + + this.begin(); + + // Triangulate. Yeah, this is slow. + + var q, x, y, z, fx, fy, fz, y_offset, z_offset, smin2 = this.size - 2; + + for ( z = 1; z < smin2; z++ ) { + + z_offset = this.size2 * z; + fz = ( z - this.halfsize ) / this.halfsize; //+ 1 + + for ( y = 1; y < smin2; y++ ) { + + y_offset = z_offset + this.size * y; + fy = ( y - this.halfsize ) / this.halfsize; //+ 1 + + for ( x = 1; x < smin2; x++ ) { + + fx = ( x - this.halfsize ) / this.halfsize; //+ 1 + q = y_offset + x; + + this.polygonize( fx, fy, fz, q, this.isolation, render_callback ); + + } + + } + + } + + this.end( render_callback ); + + }; + + this.generateGeometry = function() { + + var start = 0, geo = new THREE.Geometry(); + var normals = []; + + var geo_callback = function( object ) { + + var i, x, y, z, vertex, position, normal, + face, a, b, c, na, nb, nc; + + + for ( i = 0; i < object.count; i++ ) { + + a = i * 3; + b = a + 1; + c = a + 2; + + x = object.positionArray[ a ]; + y = object.positionArray[ b ]; + z = object.positionArray[ c ]; + position = new THREE.Vector3( x, y, z ); + + x = object.normalArray[ a ]; + y = object.normalArray[ b ]; + z = object.normalArray[ c ]; + normal = new THREE.Vector3( x, y, z ); + normal.normalize(); + + vertex = new THREE.Vertex( position ); + + geo.vertices.push( vertex ); + normals.push( normal ); + + } + + nfaces = object.count / 3; + + for ( i = 0; i < nfaces; i++ ) { + + a = ( start + i ) * 3; + b = a + 1; + c = a + 2; + + na = normals[ a ]; + nb = normals[ b ]; + nc = normals[ c ]; + + face = new THREE.Face3( a, b, c, [ na, nb, nc ] ); + + geo.faces.push( face ); + + } + + start += nfaces; + object.count = 0; + + }; + + this.render( geo_callback ); + + // console.log( "generated " + geo.faces.length + " triangles" ); + + return geo; + + }; + + this.init( resolution ); + +}; + +THREE.MarchingCubes.prototype = new THREE.Object3D(); +THREE.MarchingCubes.prototype.constructor = THREE.MarchingCubes; + + +///////////////////////////////////// +// Marching cubes lookup tables +///////////////////////////////////// + +// These tables are straight from Paul Bourke's page: +// http://local.wasp.uwa.edu.au/~pbourke/geometry/polygonise/ +// who in turn got them from Cory Gene Bloyd. + +THREE.edgeTable = new Int32Array([ +0x0 , 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, +0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00, +0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, +0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90, +0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c, +0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30, +0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac, +0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0, +0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c, +0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60, +0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc, +0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0, +0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c, +0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950, +0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc , +0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0, +0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, +0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0, +0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, +0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650, +0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, +0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0, +0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, +0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460, +0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, +0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0, +0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, +0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230, +0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, +0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190, +0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, +0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0]) + +THREE.triTable = new Int32Array([ +-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1, +3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1, +3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1, +3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1, +9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1, +1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, +9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, +2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1, +8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1, +9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, +4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1, +3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1, +1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1, +4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1, +4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1, +9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1, +1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1, +5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1, +2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1, +9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1, +0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, +2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1, +10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, +4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1, +5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1, +5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, +9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1, +0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1, +1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1, +10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1, +8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1, +2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, +7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, +9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1, +2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1, +11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1, +9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1, +5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1, +11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1, +11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, +1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1, +9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1, +5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1, +2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, +0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, +5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1, +6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1, +0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1, +3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1, +6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1, +5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1, +1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, +10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1, +6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, +1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1, +8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1, +7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1, +3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, +5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1, +0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, +9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1, +8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1, +5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1, +0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1, +6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1, +10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, +10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1, +8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1, +1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1, +3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1, +0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, +10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1, +0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1, +3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1, +6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1, +9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1, +8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1, +3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1, +6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1, +0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1, +10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1, +10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1, +1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1, +2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1, +7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1, +7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1, +2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1, +1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1, +11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1, +8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1, +0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1, +7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, +10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, +2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, +6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1, +7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1, +2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1, +1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1, +10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1, +10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1, +0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1, +7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1, +6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1, +8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1, +9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1, +6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1, +1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1, +4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1, +10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1, +8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, +0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1, +1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1, +8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1, +10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1, +4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1, +10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, +5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, +11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1, +9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, +6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1, +7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1, +3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1, +7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1, +9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1, +3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1, +6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1, +9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1, +1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1, +4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1, +7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1, +6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1, +3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1, +0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1, +6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1, +1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1, +0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1, +11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1, +6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1, +5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1, +9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1, +1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1, +1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1, +10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1, +0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1, +5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1, +10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1, +11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1, +0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1, +9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1, +7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1, +2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, +8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1, +9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1, +9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1, +1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1, +9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1, +9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, +5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1, +0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1, +10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1, +2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1, +0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1, +0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1, +9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1, +5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1, +3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1, +5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1, +8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1, +0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1, +9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1, +0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1, +1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1, +3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1, +4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1, +9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1, +11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1, +11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1, +2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1, +9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1, +3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1, +1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1, +4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1, +4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1, +0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1, +3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1, +3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1, +0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1, +9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1, +1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]); +/** + * @author sroucheray / http://sroucheray.org/ + */ + +/** + * @constructor + * Three axis representing the cartesian coordinates + * @param xAxisColor {number} + * @param yAxisColor {number} + * @param zAxisColor {number} + * @param showArrows {Boolean} + * @param length {number} + * @param scale {number} + * + * @see THREE.Trident.defaultParams + */ +THREE.Trident = function ( params /** Object */) { + + THREE.Object3D.call( this ); + + var hPi = Math.PI / 2, cone; + + params = params || THREE.Trident.defaultParams; + + if(params !== THREE.Trident.defaultParams){ + for ( var key in THREE.Trident.defaultParams) { + if(!params.hasOwnProperty(key)){ + params[key] = THREE.Trident.defaultParams[key]; + } + } + } + + this.scale = new THREE.Vector3( params.scale, params.scale, params.scale ); + this.addChild( getSegment( new THREE.Vector3(params.length,0,0), params.xAxisColor ) ); + this.addChild( getSegment( new THREE.Vector3(0,params.length,0), params.yAxisColor ) ); + this.addChild( getSegment( new THREE.Vector3(0,0,params.length), params.zAxisColor ) ); + + if(params.showArrows){ + cone = getCone(params.xAxisColor); + cone.rotation.y = - hPi; + cone.position.x = params.length; + this.addChild( cone ); + + cone = getCone(params.yAxisColor); + cone.rotation.x = hPi; + cone.position.y = params.length; + this.addChild( cone ); + + cone = getCone(params.zAxisColor); + cone.rotation.y = Math.PI; + cone.position.z = params.length; + this.addChild( cone ); + } + + function getCone ( color ) { + //0.1 required to get a cone with a mapped bottom face + return new THREE.Mesh( new THREE.CylinderGeometry( 30, 0.1, params.length / 20, params.length / 5 ), new THREE.MeshBasicMaterial( { color : color } ) ); + } + + function getSegment ( point, color ){ + var geom = new THREE.Geometry(); + geom.vertices = [new THREE.Vertex(), new THREE.Vertex(point)]; + return new THREE.Line( geom, new THREE.LineBasicMaterial( { color : color } ) ); + } +}; + +THREE.Trident.prototype = new THREE.Object3D(); +THREE.Trident.prototype.constructor = THREE.Trident; + +THREE.Trident.defaultParams = { + xAxisColor : 0xFF0000, + yAxisColor : 0x00FF00, + zAxisColor : 0x0000FF, + showArrows : true, + length : 100, + scale : 1 +}; +/** + * @author bartek drozdz / http://everyday3d.com/ + */ + +THREE.PlaneCollider = function( point, normal ) { + + this.point = point; + this.normal = normal; + +}; + + +THREE.SphereCollider = function( center, radius ) { + + this.center = center; + this.radius = radius; + this.radiusSq = radius * radius; + +}; + +THREE.BoxCollider = function( min, max ) { + + this.min = min; + this.max = max; + this.dynamic = true; + + this.normal = new THREE.Vector3(); + +}; + +THREE.MeshCollider = function( mesh, box ) { + + this.mesh = mesh; + this.box = box; + this.numFaces = this.mesh.geometry.faces.length; + + this.normal = new THREE.Vector3(); + +}; + +THREE.CollisionSystem = function() { + + this.collisionNormal = new THREE.Vector3(); + this.colliders = []; + this.hits = []; + + // console.log("Collision system init / 004"); + +}; + +THREE.Collisions = new THREE.CollisionSystem(); + +THREE.CollisionSystem.prototype.merge = function( collisionSystem ) { + + this.colliders = this.colliders.concat( collisionSystem.colliders ); + this.hits = this.hits.concat( collisionSystem.hits ); + +}; + +THREE.CollisionSystem.prototype.rayCastAll = function( ray ) { + + ray.direction.normalize(); + + this.hits.length = 0; + + var i, l, d, collider, + ld = 0; + + for ( i = 0, l = this.colliders.length; i < l; i++ ) { + + collider = this.colliders[ i ]; + + d = this.rayCast( ray, collider ); + + if ( d < Number.MAX_VALUE ) { + + collider.distance = d; + + if ( d > ld ) + this.hits.push( collider ); + else + this.hits.unshift( collider ); + + ld = d; + + } + + } + + return this.hits; + +}; + +THREE.CollisionSystem.prototype.rayCastNearest = function( ray ) { + + var cs = this.rayCastAll( ray ); + + if( cs.length == 0 ) return null; + + var i = 0; + + while( cs[ i ] instanceof THREE.MeshCollider ) { + + var dist_index = this.rayMesh ( ray, cs[ i ] ); + + if( dist_index.dist < Number.MAX_VALUE ) { + + cs[ i ].distance = dist_index.dist; + cs[ i ].faceIndex = dist_index.faceIndex; + break; + + } + + i++; + + } + + if ( i > cs.length ) return null; + + return cs[ i ]; + +}; + +THREE.CollisionSystem.prototype.rayCast = function( ray, collider ) { + + if ( collider instanceof THREE.PlaneCollider ) + return this.rayPlane( ray, collider ); + + else if ( collider instanceof THREE.SphereCollider ) + return this.raySphere( ray, collider ); + + else if ( collider instanceof THREE.BoxCollider ) + return this.rayBox( ray, collider ); + + else if ( collider instanceof THREE.MeshCollider && collider.box ) + return this.rayBox( ray, collider.box ); + +}; + +THREE.CollisionSystem.prototype.rayMesh = function( r, me ) { + + var rt = this.makeRayLocal( r, me.mesh ); + + var d = Number.MAX_VALUE; + var nearestface; + + for( var i = 0; i < me.numFaces; i++ ) { + var face = me.mesh.geometry.faces[i]; + var p0 = me.mesh.geometry.vertices[ face.a ].position; + var p1 = me.mesh.geometry.vertices[ face.b ].position; + var p2 = me.mesh.geometry.vertices[ face.c ].position; + var p3 = face instanceof THREE.Face4 ? me.mesh.geometry.vertices[ face.d ].position : null; + + if (face instanceof THREE.Face3) { + var nd = this.rayTriangle( rt, p0, p1, p2, d, this.collisionNormal ); + + if( nd < d ) { + + d = nd; + nearestface = i; + me.normal.copy( this.collisionNormal ); + me.normal.normalize(); + + } + + } + + else if (face instanceof THREE.Face4) { + + var nd = this.rayTriangle( rt, p0, p1, p3, d, this.collisionNormal ); + + if( nd < d ) { + + d = nd; + nearestface = i; + me.normal.copy( this.collisionNormal ); + me.normal.normalize(); + + } + + nd = this.rayTriangle( rt, p1, p2, p3, d, this.collisionNormal ); + + if( nd < d ) { + + d = nd; + nearestface = i; + me.normal.copy( this.collisionNormal ); + me.normal.normalize(); + + } + + } + + } + + return {dist: d, faceIndex: nearestface}; + +}; + +THREE.CollisionSystem.prototype.rayTriangle = function( ray, p0, p1, p2, mind, n ) { + + var e1 = THREE.CollisionSystem.__v1, + e2 = THREE.CollisionSystem.__v2; + + n.set( 0, 0, 0 ); + + // do not crash on quads, fail instead + + e1.sub( p1, p0 ); + e2.sub( p2, p1 ); + n.cross( e1, e2 ) + + var dot = n.dot( ray.direction ); + if ( !( dot < 0 ) ) return Number.MAX_VALUE; + + var d = n.dot( p0 ); + var t = d - n.dot( ray.origin ); + + if ( !( t <= 0 ) ) return Number.MAX_VALUE; + if ( !( t >= dot * mind ) ) return Number.MAX_VALUE; + + t = t / dot; + + var p = THREE.CollisionSystem.__v3; + + p.copy( ray.direction ); + p.multiplyScalar( t ); + p.addSelf( ray.origin ); + + var u0, u1, u2, v0, v1, v2; + + if ( Math.abs( n.x ) > Math.abs( n.y ) ) { + + if ( Math.abs( n.x ) > Math.abs( n.z ) ) { + + u0 = p.y - p0.y; + u1 = p1.y - p0.y; + u2 = p2.y - p0.y; + + v0 = p.z - p0.z; + v1 = p1.z - p0.z; + v2 = p2.z - p0.z; + + } else { + + u0 = p.x - p0.x; + u1 = p1.x - p0.x; + u2 = p2.x - p0.x; + + v0 = p.y - p0.y; + v1 = p1.y - p0.y; + v2 = p2.y - p0.y; + + } + + } else { + + if( Math.abs( n.y ) > Math.abs( n.z ) ) { + + u0 = p.x - p0.x; + u1 = p1.x - p0.x; + u2 = p2.x - p0.x; + + v0 = p.z - p0.z; + v1 = p1.z - p0.z; + v2 = p2.z - p0.z; + + } else { + + u0 = p.x - p0.x; + u1 = p1.x - p0.x; + u2 = p2.x - p0.x; + + v0 = p.y - p0.y; + v1 = p1.y - p0.y; + v2 = p2.y - p0.y; + + } + + } + + var temp = u1 * v2 - v1 * u2; + if( !(temp != 0) ) return Number.MAX_VALUE; + //console.log("temp: " + temp); + temp = 1 / temp; + + var alpha = ( u0 * v2 - v0 * u2 ) * temp; + if( !(alpha >= 0) ) return Number.MAX_VALUE; + //console.log("alpha: " + alpha); + + var beta = ( u1 * v0 - v1 * u0 ) * temp; + if( !(beta >= 0) ) return Number.MAX_VALUE; + //console.log("beta: " + beta); + + var gamma = 1 - alpha - beta; + if( !(gamma >= 0) ) return Number.MAX_VALUE; + //console.log("gamma: " + gamma); + + return t; + +}; + +THREE.CollisionSystem.prototype.makeRayLocal = function( ray, m ) { + + var mt = THREE.CollisionSystem.__m; + THREE.Matrix4.makeInvert( m.matrixWorld, mt ); + + var rt = THREE.CollisionSystem.__r; + rt.origin.copy( ray.origin ); + rt.direction.copy( ray.direction ); + + mt.multiplyVector3( rt.origin ); + mt.rotateAxis( rt.direction ); + rt.direction.normalize(); + //m.localRay = rt; + + return rt; + +}; + +THREE.CollisionSystem.prototype.rayBox = function( ray, ab ) { + + var rt; + + if ( ab.dynamic && ab.mesh && ab.mesh.matrixWorld ) { + + rt = this.makeRayLocal( ray, ab.mesh ); + + } else { + + rt = THREE.CollisionSystem.__r; + rt.origin.copy( ray.origin ); + rt.direction.copy( ray.direction ); + + } + + var xt = 0, yt = 0, zt = 0; + var xn = 0, yn = 0, zn = 0; + var ins = true; + + if( rt.origin.x < ab.min.x ) { + + xt = ab.min.x - rt.origin.x; + //if(xt > ray.direction.x) return return Number.MAX_VALUE; + xt /= rt.direction.x; + ins = false; + xn = -1; + + } else if( rt.origin.x > ab.max.x ) { + + xt = ab.max.x - rt.origin.x; + //if(xt < ray.direction.x) return return Number.MAX_VALUE; + xt /= rt.direction.x; + ins = false; + xn = 1; + + } + + if( rt.origin.y < ab.min.y ) { + + yt = ab.min.y - rt.origin.y; + //if(yt > ray.direction.y) return return Number.MAX_VALUE; + yt /= rt.direction.y; + ins = false; + yn = -1; + + } else if( rt.origin.y > ab.max.y ) { + + yt = ab.max.y - rt.origin.y; + //if(yt < ray.direction.y) return return Number.MAX_VALUE; + yt /= rt.direction.y; + ins = false; + yn = 1; + + } + + if( rt.origin.z < ab.min.z ) { + + zt = ab.min.z - rt.origin.z; + //if(zt > ray.direction.z) return return Number.MAX_VALUE; + zt /= rt.direction.z; + ins = false; + zn = -1; + + } else if( rt.origin.z > ab.max.z ) { + + zt = ab.max.z - rt.origin.z; + //if(zt < ray.direction.z) return return Number.MAX_VALUE; + zt /= rt.direction.z; + ins = false; + zn = 1; + + } + + if( ins ) return -1; + + var which = 0; + var t = xt; + + if( yt > t ) { + + which = 1; + t = yt; + + } + + if ( zt > t ) { + + which = 2; + t = zt; + + } + + switch( which ) { + + case 0: + + var y = rt.origin.y + rt.direction.y * t; + if ( y < ab.min.y || y > ab.max.y ) return Number.MAX_VALUE; + var z = rt.origin.z + rt.direction.z * t; + if ( z < ab.min.z || z > ab.max.z ) return Number.MAX_VALUE; + ab.normal.set( xn, 0, 0 ); + break; + + case 1: + + var x = rt.origin.x + rt.direction.x * t; + if ( x < ab.min.x || x > ab.max.x ) return Number.MAX_VALUE; + var z = rt.origin.z + rt.direction.z * t; + if ( z < ab.min.z || z > ab.max.z ) return Number.MAX_VALUE; + ab.normal.set( 0, yn, 0) ; + break; + + case 2: + + var x = rt.origin.x + rt.direction.x * t; + if ( x < ab.min.x || x > ab.max.x ) return Number.MAX_VALUE; + var y = rt.origin.y + rt.direction.y * t; + if ( y < ab.min.y || y > ab.max.y ) return Number.MAX_VALUE; + ab.normal.set( 0, 0, zn ); + break; + + } + + return t; + +}; + +THREE.CollisionSystem.prototype.rayPlane = function( r, p ) { + + var t = r.direction.dot( p.normal ); + var d = p.point.dot( p.normal ); + var ds; + + if( t < 0 ) ds = ( d - r.origin.dot( p.normal ) ) / t; + else return Number.MAX_VALUE; + + if( ds > 0 ) return ds; + else return Number.MAX_VALUE; + +}; + +THREE.CollisionSystem.prototype.raySphere = function( r, s ) { + + var e = s.center.clone().subSelf( r.origin ); + if ( e.lengthSq < s.radiusSq ) return -1; + + var a = e.dot( r.direction.clone() ); + if ( a <= 0 ) return Number.MAX_VALUE; + + var t = s.radiusSq - ( e.lengthSq() - a * a ); + if ( t >= 0 ) return Math.abs( a ) - Math.sqrt( t ); + + return Number.MAX_VALUE; + +}; + +THREE.CollisionSystem.__v1 = new THREE.Vector3(); +THREE.CollisionSystem.__v2 = new THREE.Vector3(); +THREE.CollisionSystem.__v3 = new THREE.Vector3(); +THREE.CollisionSystem.__nr = new THREE.Vector3(); +THREE.CollisionSystem.__m = new THREE.Matrix4(); +THREE.CollisionSystem.__r = new THREE.Ray(); +/** + * @author bartek drozdz / http://everyday3d.com/ + */ + +THREE.CollisionUtils = {}; + +// @params m THREE.Mesh +// @returns CBox dynamic Object Bounding Box + +THREE.CollisionUtils.MeshOBB = function( m ) { + + m.geometry.computeBoundingBox(); + var b = m.geometry.boundingBox; + var min = new THREE.Vector3( b.x[0], b.y[0], b.z[0] ); + var max = new THREE.Vector3( b.x[1], b.y[1], b.z[1] ); + var box = new THREE.BoxCollider( min, max ); + box.mesh = m; + return box; + +} + +// @params m THREE.Mesh +// @returns CBox static Axis-Aligned Bounding Box +// +// The AABB is calculated based on current +// position of the object (assumes it won't move) + +THREE.CollisionUtils.MeshAABB = function( m ) { + + var box = THREE.CollisionUtils.MeshOBB( m ); + box.min.addSelf( m.position ); + box.max.addSelf( m.position ); + box.dynamic = false; + return box; + +}; + +// @params m THREE.Mesh +// @returns CMesh with aOOB attached (that speeds up intersection tests) + +THREE.CollisionUtils.MeshColliderWBox = function( m ) { + + var mc = new THREE.MeshCollider( m, THREE.CollisionUtils.MeshOBB( m ) ); + + return mc; + +}; +/** + * @author mrdoob / http://mrdoob.com/ + * @author marklundin / http://mark-lundin.com/ + */ + +if ( THREE.WebGLRenderer ) { + + THREE.AnaglyphWebGLRenderer = function ( parameters ) { + + THREE.WebGLRenderer.call( this, parameters ); + + var _this = this, _setSize = this.setSize, _render = this.render; + var _cameraL = new THREE.Camera(), _cameraR = new THREE.Camera(); + var eyeRight = new THREE.Matrix4(), + eyeLeft = new THREE.Matrix4(), + focalLength = 125, + aspect, near, fov; + + _cameraL.useTarget = _cameraR.useTarget = false; + _cameraL.matrixAutoUpdate = _cameraR.matrixAutoUpdate = false; + + var _params = { minFilter: THREE.LinearFilter, magFilter: THREE.NearestFilter, format: THREE.RGBAFormat }; + var _renderTargetL = new THREE.WebGLRenderTarget( 512, 512, _params ), _renderTargetR = new THREE.WebGLRenderTarget( 512, 512, _params ); + + var _camera = new THREE.Camera( 53, 1, 1, 10000 ); + _camera.position.z = 2; + + _material = new THREE.MeshShaderMaterial( { + + uniforms: { + + "mapLeft": { type: "t", value: 0, texture: _renderTargetL }, + "mapRight": { type: "t", value: 1, texture: _renderTargetR } + + }, + vertexShader: [ + + "varying vec2 vUv;", + + "void main() {", + + "vUv = vec2( uv.x, 1.0 - uv.y );", + "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", + + "}" + + ].join("\n"), + fragmentShader: [ + + "uniform sampler2D mapLeft;", + "uniform sampler2D mapRight;", + "varying vec2 vUv;", + + "void main() {", + + "vec4 colorL, colorR;", + "vec2 uv = vUv;", + + "colorL = texture2D( mapLeft, uv );", + "colorR = texture2D( mapRight, uv );", + + // http://3dtv.at/Knowhow/AnaglyphComparison_en.aspx + + "gl_FragColor = vec4( colorL.g * 0.7 + colorL.b * 0.3, colorR.g, colorR.b, colorL.a + colorR.a ) * 1.1;", + + "}" + + ].join("\n") + + } ); + + var _scene = new THREE.Scene(); + _scene.addObject( new THREE.Mesh( new THREE.PlaneGeometry( 2, 2 ), _material ) ); + + this.setSize = function ( width, height ) { + + _setSize.call( _this, width, height ); + + _renderTargetL.width = width; + _renderTargetL.height = height; + + _renderTargetR.width = width; + _renderTargetR.height = height; + + }; + + /* + * Renderer now uses an asymmetric perspective projection (http://paulbourke.net/miscellaneous/stereographics/stereorender/). + * Each camera is offset by the eye seperation and its projection matrix is also skewed asymetrically back to converge on the same + * projection plane. Added a focal length parameter to, this is where the parallax is equal to 0. + */ + this.render = function ( scene, camera, renderTarget, forceClear ) { + + + camera.update( null, true ); + + var hasCameraChanged = aspect !== camera.aspect || near !== camera.near || fov !== camera.fov; + + if( hasCameraChanged ) { + + aspect = camera.aspect; + near = camera.near; + fov = camera.fov; + + var projectionMatrix = camera.projectionMatrix.clone(), + eyeSep = focalLength / 30 * 0.5, + eyeSepOnProjection = eyeSep * near / focalLength, + ymax = near * Math.tan( fov * Math.PI / 360 ), + xmin, xmax; + + //translate xOffset + eyeRight.n14 = eyeSep; + eyeLeft.n14 = -eyeSep; + + //For left eye + xmin = -ymax * aspect + eyeSepOnProjection; + xmax = ymax * aspect + eyeSepOnProjection; + projectionMatrix.n11 = 2 * near / ( xmax - xmin ); + projectionMatrix.n13 = ( xmax + xmin ) / ( xmax - xmin ); + _cameraL.projectionMatrix = projectionMatrix.clone(); + + //for right eye + xmin = -ymax * aspect - eyeSepOnProjection; + xmax = ymax * aspect - eyeSepOnProjection; + projectionMatrix.n11 = 2 * near / ( xmax - xmin ); + projectionMatrix.n13 = ( xmax + xmin ) / ( xmax - xmin ); + _cameraR.projectionMatrix = projectionMatrix.clone(); + + } + + _cameraL.matrix = camera.matrixWorld.clone().multiplySelf( eyeLeft ); + _cameraL.update(null, true); + _cameraL.position.copy( camera.position ); + _cameraL.near = near; + _cameraL.far = camera.far; + _render.call( _this, scene, _cameraL, _renderTargetL, true ); + + _cameraR.matrix = camera.matrixWorld.clone().multiplySelf( eyeRight ); + _cameraR.update(null, true); + _cameraR.position.copy( camera.position ); + _cameraR.near = near; + _cameraR.far = camera.far; + _render.call( _this, scene, _cameraR, _renderTargetR, true ); + + _render.call( _this, _scene, _camera ); + + }; + + }; + +} +/** + * @author alteredq / http://alteredqualia.com/ + */ + +if ( THREE.WebGLRenderer ) { + + THREE.CrosseyedWebGLRenderer = function ( parameters ) { + + THREE.WebGLRenderer.call( this, parameters ); + + this.autoClear = false; + + var _this = this, _setSize = this.setSize, _render = this.render; + + var _width, _height; + + var _cameraL = new THREE.Camera(), + _cameraR = new THREE.Camera(); + + _this.separation = 10; + if ( parameters && parameters.separation !== undefined ) _this.separation = parameters.separation; + + var SCREEN_WIDTH = window.innerWidth; + var SCREEN_HEIGHT = window.innerHeight; + var HALF_WIDTH = SCREEN_WIDTH / 2; + + var _camera = new THREE.Camera( 53, HALF_WIDTH / SCREEN_HEIGHT, 1, 10000 ); + _camera.position.z = -10; + + + this.setSize = function ( width, height ) { + + _setSize.call( _this, width, height ); + + _width = width/2; + _height = height; + + }; + + this.render = function ( scene, camera, renderTarget, forceClear ) { + + this.clear(); + + _cameraL.fov = camera.fov; + _cameraL.aspect = 0.5 * camera.aspect; + _cameraL.near = camera.near; + _cameraL.far = camera.far; + _cameraL.updateProjectionMatrix(); + + _cameraL.position.copy( camera.position ); + _cameraL.target.position.copy( camera.target.position ); + _cameraL.translateX( _this.separation ); + + _cameraR.projectionMatrix = _cameraL.projectionMatrix; + + _cameraR.position.copy( camera.position ); + _cameraR.target.position.copy( camera.target.position ); + _cameraR.translateX( - _this.separation ); + + this.setViewport( 0, 0, _width, _height ); + _render.call( _this, scene, _cameraL ); + + this.setViewport( _width, 0, _width, _height ); + _render.call( _this, scene, _cameraR, false ); + + }; + + }; + +} diff --git a/examples/webgl_collisions_box.html b/examples/webgl_collisions_box.html index 330b6df94b0dbf051d6276d6c10ca8970d3ef874..cddd0b43e7e73afa6ff919c2f5c53e37225f32fa 100644 --- a/examples/webgl_collisions_box.html +++ b/examples/webgl_collisions_box.html @@ -44,7 +44,8 @@ body {