Fork me on GitHub

API Documentation


Warning: DOMDocument::loadHTMLFile() [domdocument.loadhtmlfile]: htmlParseEntityRef: no name in jsdoc/symbols/src/src_geometry_glge_mesh.js.html, line: 375 in /homepages/22/d163487924/htdocs/glge/wp-content/themes/glge/manual.php on line 29

Warning: DOMDocument::loadHTMLFile() [domdocument.loadhtmlfile]: htmlParseEntityRef: no name in jsdoc/symbols/src/src_geometry_glge_mesh.js.html, line: 375 in /homepages/22/d163487924/htdocs/glge/wp-content/themes/glge/manual.php on line 29

Warning: DOMDocument::loadHTMLFile() [domdocument.loadhtmlfile]: htmlParseEntityRef: no name in jsdoc/symbols/src/src_geometry_glge_mesh.js.html, line: 608 in /homepages/22/d163487924/htdocs/glge/wp-content/themes/glge/manual.php on line 29

Warning: DOMDocument::loadHTMLFile() [domdocument.loadhtmlfile]: htmlParseEntityRef: no name in jsdoc/symbols/src/src_geometry_glge_mesh.js.html, line: 608 in /homepages/22/d163487924/htdocs/glge/wp-content/themes/glge/manual.php on line 29

Warning: DOMDocument::loadHTMLFile() [domdocument.loadhtmlfile]: htmlParseEntityRef: no name in jsdoc/symbols/src/src_geometry_glge_mesh.js.html, line: 639 in /homepages/22/d163487924/htdocs/glge/wp-content/themes/glge/manual.php on line 29

Warning: DOMDocument::loadHTMLFile() [domdocument.loadhtmlfile]: htmlParseEntityRef: no name in jsdoc/symbols/src/src_geometry_glge_mesh.js.html, line: 639 in /homepages/22/d163487924/htdocs/glge/wp-content/themes/glge/manual.php on line 29

Warning: DOMDocument::loadHTMLFile() [domdocument.loadhtmlfile]: htmlParseEntityRef: no name in jsdoc/symbols/src/src_geometry_glge_mesh.js.html, line: 656 in /homepages/22/d163487924/htdocs/glge/wp-content/themes/glge/manual.php on line 29

Warning: DOMDocument::loadHTMLFile() [domdocument.loadhtmlfile]: htmlParseEntityRef: no name in jsdoc/symbols/src/src_geometry_glge_mesh.js.html, line: 656 in /homepages/22/d163487924/htdocs/glge/wp-content/themes/glge/manual.php on line 29

Warning: DOMDocument::loadHTMLFile() [domdocument.loadhtmlfile]: htmlParseEntityRef: no name in jsdoc/symbols/src/src_geometry_glge_mesh.js.html, line: 690 in /homepages/22/d163487924/htdocs/glge/wp-content/themes/glge/manual.php on line 29

Warning: DOMDocument::loadHTMLFile() [domdocument.loadhtmlfile]: htmlParseEntityRef: no name in jsdoc/symbols/src/src_geometry_glge_mesh.js.html, line: 690 in /homepages/22/d163487924/htdocs/glge/wp-content/themes/glge/manual.php on line 29

Warning: DOMDocument::loadHTMLFile() [domdocument.loadhtmlfile]: htmlParseEntityRef: no name in jsdoc/symbols/src/src_geometry_glge_mesh.js.html, line: 690 in /homepages/22/d163487924/htdocs/glge/wp-content/themes/glge/manual.php on line 29

Warning: DOMDocument::loadHTMLFile() [domdocument.loadhtmlfile]: htmlParseEntityRef: no name in jsdoc/symbols/src/src_geometry_glge_mesh.js.html, line: 690 in /homepages/22/d163487924/htdocs/glge/wp-content/themes/glge/manual.php on line 29
  1 /*
  2 GLGE WebGL Graphics Engine
  3 Copyright (c) 2010, Paul Brunt
  4 All rights reserved.
  5 
  6 Redistribution and use in source and binary forms, with or without
  7 modification, are permitted provided that the following conditions are met:
  8     * Redistributions of source code must retain the above copyright
  9       notice, this list of conditions and the following disclaimer.
 10     * Redistributions in binary form must reproduce the above copyright
 11       notice, this list of conditions and the following disclaimer in the
 12       documentation and/or other materials provided with the distribution.
 13     * Neither the name of GLGE nor the
 14       names of its contributors may be used to endorse or promote products
 15       derived from this software without specific prior written permission.
 16 
 17 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 18 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 19 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 20 DISCLAIMED. IN NO EVENT SHALL PAUL BRUNT BE LIABLE FOR ANY
 21 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 22 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 23 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 24 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 26 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 27 */
 28 
 29 /**
 30  * @fileOverview
 31  * @name glge_mesh.js
 32  * @author me@paulbrunt.co.uk
 33  */
 34 
 35 
 36 (function(GLGE){
 37 
 38 
 39 
 40 
 41 
 42 /**
 43 * @class Creates a new mesh
 44 * @see GLGE.Object
 45 * @augments GLGE.QuickNotation
 46 * @augments GLGE.JSONLoader
 47 * @augments GLGE.Events
 48 */
 49 GLGE.Mesh=function(uid,windingOrder){
 50 	this.GLbuffers=[];
 51 	this.buffers=[];
 52 	this.framePositions=[];
 53 	this.frameNormals=[];
 54 	this.frameTangents=[];
 55 	this.UV=[];
 56 	this.boneWeights=[];
 57 	this.setBuffers=[];
 58 	this.faces={};
 59     if (windingOrder!==undefined)
 60         this.windingOrder=windingOrder;
 61     else
 62         this.windingOrder=GLGE.Mesh.WINDING_ORDER_UNKNOWN;
 63 
 64 	GLGE.Assets.registerAsset(this,uid);
 65 };
 66 
 67 GLGE.Mesh.WINDING_ORDER_UNKNOWN=2;
 68 GLGE.Mesh.WINDING_ORDER_CLOCKWISE=1;
 69 GLGE.Mesh.WINDING_ORDER_COUNTER=0;
 70 
 71 GLGE.augment(GLGE.QuickNotation,GLGE.Mesh);
 72 GLGE.augment(GLGE.JSONLoader,GLGE.Mesh);
 73 GLGE.augment(GLGE.Events,GLGE.Mesh);
 74 GLGE.Mesh.prototype.gl=null;
 75 GLGE.Mesh.prototype.className="Mesh";
 76 GLGE.Mesh.prototype.GLbuffers=null;
 77 GLGE.Mesh.prototype.buffers=null;
 78 GLGE.Mesh.prototype.setBuffers=null;
 79 GLGE.Mesh.prototype.GLfaces=null;
 80 GLGE.Mesh.prototype.faces=null;
 81 GLGE.Mesh.prototype.UV=null;
 82 GLGE.Mesh.prototype.joints=null;
 83 GLGE.Mesh.prototype.invBind=null;
 84 GLGE.Mesh.prototype.loaded=false;
 85 /**
 86  * @name GLGE.Mesh#shaderupdate
 87  * @event fired when the shader needs updating
 88  * @param {object} data
 89  */
 90 
 91 /**
 92 * Gets the bounding volume for the mesh
 93 * @returns {GLGE.BoundingVolume} 
 94 */
 95 GLGE.Mesh.prototype.getBoundingVolume=function(){
 96 	if(!this.positions) return new GLGE.BoundingVolume(0,0,0,0,0,0);
 97 	if(!this.boundingVolume){
 98 		var minX,maxX,minY,maxY,minZ,maxZ;
 99 		var positions=this.positions;
100 		for(var i=0;i<positions.length;i=i+3){
101 			if(i==0){
102 				minX=maxX=positions[i];
103 				minY=maxY=positions[i+1];
104 				minZ=maxZ=positions[i+2];
105 			}else{
106 				minX=Math.min(minX,positions[i]);
107 				maxX=Math.max(maxX,positions[i]);
108 				minY=Math.min(minY,positions[i+1]);
109 				maxY=Math.max(maxY,positions[i+1]);
110 				minZ=Math.min(minZ,positions[i+2]);
111 				maxZ=Math.max(maxZ,positions[i+2]);
112 			}
113 		}
114 		this.boundingVolume=new GLGE.BoundingVolume(minX,maxX,minY,maxY,minZ,maxZ);
115 	}
116 	return this.boundingVolume;
117 }
118 /**
119 * Sets the joints
120 * @param {string[]} jsArray set joint objects
121 */
122 GLGE.Mesh.prototype.setJoints=function(jsArray){
123 	this.joints=jsArray;
124 	this.fireEvent("shaderupdate",{});
125 	return this;
126 }
127 /**
128 * Sets the inverse bind matrix for each joint
129 * @param {GLGE.Matrix[]} jsArray set joint names
130 */
131 GLGE.Mesh.prototype.setInvBindMatrix=function(jsArray){
132 	this.invBind=jsArray;
133 	this.fireEvent("shaderupdate",{});
134 	return this;
135 }
136 /**
137 * Sets the joint channels for each vertex 
138 * @param {Number[]} jsArray The 1 dimentional array of bones
139 * @param {Number} num the number of chanels in this mesh
140 */
141 GLGE.Mesh.prototype.setVertexJoints=function(jsArray,num){
142 	if(!num){
143 		num=jsArray.length*3/this.positions.length;
144 	}
145 	if(num<5){
146 		this.setBuffer("joints1",jsArray,num);
147 	}else{
148 		var jsArray1=[];
149 		var jsArray2=[];
150 		for(var i=0;i<jsArray.length;i++){
151 			if(i%num<4){
152 				jsArray1.push(jsArray[i]);
153 			}else{
154 				jsArray2.push(jsArray[i]);
155 			}
156 157 		}
158 		this.setBuffer("joints1",jsArray1,4);
159 		this.setBuffer("joints2",jsArray2,num-4);
160 	}
161 	this.fireEvent("shaderupdate",{});
162 	return this;
163 }
164 /**
165 * Sets the joint weights on each vertex
166 * @param {Number[]} jsArray The 1 dimentional array of weights
167 * @param {Number} num the number of chanels in this mesh
168 */
169 GLGE.Mesh.prototype.setVertexWeights=function(jsArray,num){
170 	if(!num){
171 		num=jsArray.length*3/this.positions.length;
172 	}
173 	//normalize the weights!
174 	for(var i=0;i<jsArray.length;i=i+parseInt(num)){
175 		var total=0;
176 		for(var n=0;n<num;n++){
177 			total+=parseFloat(jsArray[i+n]);
178 		}
179 		if(total==0) total=1;
180 		for(var n=0;n<num;n++){
181 			jsArray[i+n]=jsArray[i+n]/total;
182 		}
183 	}
184 
185 
186 	if(num<4){
187 		this.setBuffer("weights1",jsArray,num);
188 	}else{
189 		var jsArray1=[];
190 		var jsArray2=[];
191 		for(var i=0;i<jsArray.length;i++){
192 			if(i%num<4){
193 				jsArray1.push(jsArray[i]);
194 			}else{
195 				jsArray2.push(jsArray[i]);
196 			}
197 		}
198 		this.setBuffer("weights1",jsArray1,4);
199 		this.setBuffer("weights2",jsArray2,num-4);
200 	}
201 	this.fireEvent("shaderupdate",{});
202 	return this;
203 }
204 /**
205 * clears any buffers currently set
206 * @param {Number[]} jsArray the UV coords in a 1 dimentional array
207 */
208 GLGE.Mesh.prototype.clearBuffers=function(){
209 	//if(this.GLfaces) this.gl.deleteBuffer(this.GLfaces);
210 	this.GLFaces=null;
211 	delete(this.GLFaces);
212 	for(var i in this.buffers){
213 		//if(this.buffers[i].GL) this.gl.deleteBuffer(this.buffers[i].GL);
214 		this.buffers[i]=null;
215 		delete(this.buffers[i]);
216 	}
217 	this.buffers=[];
218 	this.loaded=false;
219 }
220 /**
221 * Set the UV coord for the first UV layer
222 * @param {Number[]} jsArray the UV coords in a 1 dimentional array
223 */
224 GLGE.Mesh.prototype.setUV=function(jsArray){
225 	this.uv1set=jsArray;
226 	var idx=0;
227 	for(var i=0; i<jsArray.length;i=i+2){
228 		this.UV[idx]=jsArray[i];
229 		this.UV[idx+1]=jsArray[i+1];
230 		if(!this.UV[idx+2]) this.UV[idx+2]=jsArray[i];//<-- hack in case the collada file only specified UV1 but accesses UV2 and expects the UV1 coordinates to be properly reflected there
231 		if(!this.UV[idx+3]) this.UV[idx+3]=jsArray[i+1];
232 		idx=idx+4;
233 	}
234 	this.setBuffer("UV",this.UV,4);
235 	return this;
236 }
237 /**
238 * Set the UV coord for the second UV layer
239 * @param {Number[]} jsArray the UV coords in a 1 dimentional array
240 */
241 GLGE.Mesh.prototype.setUV2=function(jsArray){
242 	this.uv2set=jsArray;
243 	var idx=0;
244 	for(var i=0; i<jsArray.length;i=i+2){
245 		if(!this.UV[idx]) this.UV[idx]=jsArray[i];
246 		if(!this.UV[idx+1]) this.UV[idx+1]=jsArray[i+1];
247 		this.UV[idx+2]=jsArray[i];
248 		this.UV[idx+3]=jsArray[i+1];
249 		idx=idx+4;
250 	}
251 	this.setBuffer("UV",this.UV,4);
252 	return this;
253 }
254 /**
255 * Sets the positions of the verticies
256 * @param {Number[]} jsArray The 1 dimentional array of positions
257 * @param {number} frame optional mesh frame number
258 */
259 GLGE.Mesh.prototype.setPositions=function(jsArray,frame){
260 	if(!frame) frame=0;
261 	this.loaded=true;
262 	if(frame==0) this.positions=jsArray;
263 	this.framePositions[frame]=jsArray;
264 	this.setBuffer("position"+frame,jsArray,3,true);
265 	this.boundingVolume=null;
266 	this.fireEvent("updatebound");
267 	return this;
268 }
269 /**
270 * Sets the colors of the verticies
271 * @param {Number[]} jsArray The vertex colors
272 */
273 GLGE.Mesh.prototype.setVertexColors=function(jsArray){
274 	this.colors=jsArray;
275 	this.setBuffer("color",jsArray,4);
276 	return this;
277 }
278 /**
279 * Sets the normals of the verticies
280 * @param {Number[]} jsArray The 1 dimentional array of normals
281 * @param {number} frame optional mesh frame number
282 */
283 GLGE.Mesh.prototype.setNormals=function(jsArray,frame){
284 	if(!frame) frame=0;
285 	if(frame==0) this.normals=jsArray;
286 	this.frameNormals[frame]=jsArray;
287 	this.setBuffer("normal"+frame,jsArray,3,true);
288 	return this;
289 290 }
291 /**
292 * Sets the tangents of the verticies
293 * @param {Number[]} jsArray The 1 dimentional array of tangents
294 * @param {number} frame optional mesh frame number
295 */
296 GLGE.Mesh.prototype.setTangents=function(jsArray,frame){
297 	if(!frame) frame=0;
298 	if(frame==0) this.tangents=jsArray;
299 	this.frameTangents[frame]=jsArray;
300 	this.setBuffer("tangent"+frame,jsArray,3,true);
301 	return this;
302 }
303 
304 
305 /**
306 * Sets a buffer for the
307 * @param {String} boneName The name of the bone
308 * @param {Number[]} jsArray The 1 dimentional array of weights
309 * @private
310 */
311 GLGE.Mesh.prototype.setBuffer=function(bufferName,jsArray,size,exclude){
312 	//make sure all jsarray items are floats
313 	if(typeof jsArray[0] !="number") for(var i=0;i<jsArray.length;i++) jsArray[i]=parseFloat(jsArray[i]);
314 	
315 	var buffer;
316 	for(var i=0;i<this.buffers.length;i++){
317 		if(this.buffers[i].name==bufferName) buffer=i;
318 	}
319 	if(!buffer){
320 		this.buffers.push({name:bufferName,data:jsArray,size:size,GL:false,exclude:exclude});
321 	}
322         else 
323 	{
324 		this.buffers[buffer]={name:bufferName,data:jsArray,size:size,GL:false,exclude:exclude};
325 	}
326 	return this;
327 }
328 
329 /**
330 * gets a vert tangent
331 * @private
332 */
333 GLGE.Mesh.prototype.tangentFromUV=function(p1,p2,p3,uv1,uv2,uv3,n){
334 	var toUnitVec3=GLGE.toUnitVec3;
335 	var subVec3=GLGE.subVec3;
336 	var scaleVec3=GLGE.scaleVec3;
337 	var dotVec3=GLGE.dotVec3;
338 	var crossVec3=GLGE.crossVec3;
339 	
340 	uv21=[uv2[0]-uv1[0],uv2[1]-uv1[1]];
341 	uv31=[uv3[0]-uv1[0],uv3[1]-uv1[1]];
342 	
343 	p21=GLGE.subVec3(p2,p1);
344 	p31=GLGE.subVec3(p3,p1);
345 	var s=(uv21[0]*uv31[1]-uv31[0]*uv21[1]);
346 
347 	if(s!=0){
348 		s=1/s;
349 		var t=subVec3(scaleVec3(p21,uv31[1]*s),scaleVec3(p31,uv21[1]*s));
350 		var b=subVec3(scaleVec3(p31,uv21[0]*s),scaleVec3(p21,uv31[0]*s));
351 	}else{
352 		t=[0,0,0];
353 		b=[0,0,0];
354 	}
355 	if(GLGE.dotVec3(GLGE.crossVec3(p21,p31),n)>0){
356 		t=scaleVec3(t,-1);
357 		b=scaleVec3(b,-1);
358 	}
359 	return [t,b];
360 }
361 
362 /**
363 * Sets the faces for this mesh
364 * @param {Number[]} jsArray The 1 dimentional array of normals
365 */
366 GLGE.Mesh.prototype.setFaces=function(jsArray){
367 	this.faces={data:jsArray,GL:false};	
368 	//if at this point calculate normals if we haven't got them yet
369 	if(!this.normals) this.calcNormals();
370 	if(!this.tangents && this.UV.length>0) this.calcTangents();
371 	
372 	return this;
373 }
374 
375 
376 /**
377 * Calculates the tangents for this mesh - this is messy FIX ME!
378 * @private
379 */
380 GLGE.Mesh.prototype.calcTangents=function(){
381 	
382 	for(var j=0;j<this.framePositions.length;j++){
383 		var position=this.framePositions[j];
384 		var normal=this.frameNormals[j];
385 		var uv=this.UV;
386 		var tangentArray=[];
387 		var data={};
388 		var ref;
389 		for(var i=0;i<position.length;i++){
390 			tangentArray[i]=0;
391 		}
392 		for(var i=0;i<this.faces.data.length;i=i+3){
393 			var p1=[position[(parseInt(this.faces.data[i]))*3],position[(parseInt(this.faces.data[i]))*3+1],position[(parseInt(this.faces.data[i]))*3+2]];
394 			var p2=[position[(parseInt(this.faces.data[i+1]))*3],position[(parseInt(this.faces.data[i+1]))*3+1],position[(parseInt(this.faces.data[i+1]))*3+2]];
395 			var p3=[position[(parseInt(this.faces.data[i+2]))*3],position[(parseInt(this.faces.data[i+2]))*3+1],position[(parseInt(this.faces.data[i+2]))*3+2]];
396 			
397 			var n1=[normal[(parseInt(this.faces.data[i]))*3],normal[(parseInt(this.faces.data[i]))*3+1],normal[(parseInt(this.faces.data[i]))*3+2]];
398 			var n2=[normal[(parseInt(this.faces.data[i+1]))*3],normal[(parseInt(this.faces.data[i+1]))*3+1],normal[(parseInt(this.faces.data[i+1]))*3+2]];
399 			var n3=[normal[(parseInt(this.faces.data[i+2]))*3],normal[(parseInt(this.faces.data[i+2]))*3+1],normal[(parseInt(this.faces.data[i+2]))*3+2]];
400 			
401 			var uv1=[uv[(parseInt(this.faces.data[i]))*4],uv[(parseInt(this.faces.data[i]))*4+1]];
402 			var uv2=[uv[(parseInt(this.faces.data[i+1]))*4],uv[(parseInt(this.faces.data[i+1]))*4+1]];
403 			var uv3=[uv[(parseInt(this.faces.data[i+2]))*4],uv[(parseInt(this.faces.data[i+2]))*4+1]];
404 			
405 			var tb=this.tangentFromUV(p2,p1,p3,uv2,uv1,uv3,n2);
406 			
407 			if(!data[[p1[0],p1[1],p1[2],uv1[0],uv1[1],n1[0],n1[1],n1[2]].join(",")]){
408 				data[[p1[0],p1[1],p1[2],uv1[0],uv1[1],n1[0],n1[1],n1[2]].join(",")]=tb;
409 			}else{
410 				data[[p1[0],p1[1],p1[2],uv1[0],uv1[1],n1[0],n1[1],n1[2]].join(",")][0][0]+=tb[0][0];
411 				data[[p1[0],p1[1],p1[2],uv1[0],uv1[1],n1[0],n1[1],n1[2]].join(",")][0][1]+=tb[0][1];
412 				data[[p1[0],p1[1],p1[2],uv1[0],uv1[1],n1[0],n1[1],n1[2]].join(",")][0][2]+=tb[0][2];
413 				data[[p1[0],p1[1],p1[2],uv1[0],uv1[1],n1[0],n1[1],n1[2]].join(",")][1][0]+=tb[1][0];
414 				data[[p1[0],p1[1],p1[2],uv1[0],uv1[1],n1[0],n1[1],n1[2]].join(",")][1][1]+=tb[1][1];
415 				data[[p1[0],p1[1],p1[2],uv1[0],uv1[1],n1[0],n1[1],n1[2]].join(",")][1][2]+=tb[1][2];
416 			}
417 			if(!data[[p2[0],p2[1],p2[2],uv2[0],uv2[1],n2[0],n2[1],n2[2]].join(",")]){
418 				data[[p2[0],p2[1],p2[2],uv2[0],uv2[1],n2[0],n2[1],n2[2]].join(",")]=tb;
419 			}else{
420 				data[[p2[0],p2[1],p2[2],uv2[0],uv2[1],n2[0],n2[1],n2[2]].join(",")][0][0]+=tb[0][0];
421 				data[[p2[0],p2[1],p2[2],uv2[0],uv2[1],n2[0],n2[1],n2[2]].join(",")][0][1]+=tb[0][1];
422 				data[[p2[0],p2[1],p2[2],uv2[0],uv2[1],n2[0],n2[1],n2[2]].join(",")][0][2]+=tb[0][2];
423 				data[[p2[0],p2[1],p2[2],uv2[0],uv2[1],n2[0],n2[1],n2[2]].join(",")][1][0]+=tb[1][0];
424 				data[[p2[0],p2[1],p2[2],uv2[0],uv2[1],n2[0],n2[1],n2[2]].join(",")][1][1]+=tb[1][1];
425 				data[[p2[0],p2[1],p2[2],uv2[0],uv2[1],n2[0],n2[1],n2[2]].join(",")][1][2]+=tb[1][2];
426 			}
427 			if(!data[[p3[0],p3[1],p3[2],uv3[0],uv3[1],n3[0],n3[1],n3[2]].join(",")]){
428 				data[[p3[0],p3[1],p3[2],uv3[0],uv3[1],n3[0],n3[1],n3[2]].join(",")]=tb;
429 			}else{
430 				data[[p3[0],p3[1],p3[2],uv3[0],uv3[1],n3[0],n3[1],n3[2]].join(",")][0][0]+=tb[0][0];
431 				data[[p3[0],p3[1],p3[2],uv3[0],uv3[1],n3[0],n3[1],n3[2]].join(",")][0][1]+=tb[0][1];
432 				data[[p3[0],p3[1],p3[2],uv3[0],uv3[1],n3[0],n3[1],n3[2]].join(",")][0][2]+=tb[0][2];
433 				data[[p3[0],p3[1],p3[2],uv3[0],uv3[1],n3[0],n3[1],n3[2]].join(",")][1][0]+=tb[1][0];
434 				data[[p3[0],p3[1],p3[2],uv3[0],uv3[1],n3[0],n3[1],n3[2]].join(",")][1][1]+=tb[1][1];
435 				data[[p3[0],p3[1],p3[2],uv3[0],uv3[1],n3[0],n3[1],n3[2]].join(",")][1][2]+=tb[1][2];
436 			}
437 
438 		}		
439 		for(var i=0;i<position.length/3;i++){
440 			var p1=[position[i*3],position[i*3+1],position[i*3+2]];
441 			var n1=[normal[i*3],normal[i*3+1],normal[i*3+2]];
442 			var uv1=[uv[i*4],uv[i*4+1]];
443 			try{
444 			var t=GLGE.toUnitVec3(data[[p1[0],p1[1],p1[2],uv1[0],uv1[1],n1[0],n1[1],n1[2]].join(",")][0]);
445 			var b=GLGE.toUnitVec3(data[[p1[0],p1[1],p1[2],uv1[0],uv1[1],n1[0],n1[1],n1[2]].join(",")][1]);
446 			}catch(e){
447 				//if we fail probably a exporter bug carry on anyway
448 			}
449 			if(t){
450 				tangentArray[i*3]=t[0];
451 				tangentArray[i*3+1]=t[1];
452 				tangentArray[i*3+2]=t[2];
453 			}
454 		}
455 		this.setTangents(tangentArray,j);
456 	}
457 	
458 }
459 
460 /**
461 * Sets the faces for this mesh
462 * @param {Number[]} jsArray The 1 dimentional array of normals
463 * @private
464 */
465 GLGE.Mesh.prototype.GLSetFaceBuffer=function(gl){
466 	if(!this.GLfaces) this.GLfaces = gl.createBuffer();
467 	gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.GLfaces);
468 	gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(this.faces.data), gl.STATIC_DRAW);
469 	this.GLfaces.itemSize = 1;
470 	this.GLfaces.numItems = this.faces.data.length;
471 }
472 /**
473 * Sets up a GL Buffer
474 * @param {WebGLContext} gl The context being drawn on
475 * @param {String} bufferName The name of the buffer to create
476 * @param {Number[]}  jsArray The data to add to the buffer
477 * @param {Number}  size Size of a single element within the array
478 * @private
479 */
480 GLGE.Mesh.prototype.GLSetBuffer=function(gl,bufferName,jsArray,size){
481 	if(!this.GLbuffers[bufferName]) this.GLbuffers[bufferName] = gl.createBuffer();
482 	gl.bindBuffer(gl.ARRAY_BUFFER, this.GLbuffers[bufferName]);
483 	if(!jsArray.byteLength) jsArray=new Float32Array(jsArray);
484 	gl.bufferData(gl.ARRAY_BUFFER, jsArray, gl.STATIC_DRAW);
485 	this.GLbuffers[bufferName].itemSize = size;
486 	this.GLbuffers[bufferName].numItems = jsArray.length/size;
487 };
488 /**
489 * Calculates the normals for this mesh
490 * @private
491 */
492 GLGE.Mesh.prototype.calcNormals=function(){
493 	for(var n=0;n<this.framePositions.length;n++){
494 		var normals=[];
495 		var positions=this.framePositions[n];
496 		var faces=this.faces.data;
497 		if(!faces){
498 			faces=[];
499 			for(var i=0;i<positions.length/3;i++) faces[i]=i;
500 		}
501 		for(var i=0;i<faces.length;i=i+3){
502 			var v1=[positions[faces[i]*3],positions[faces[i]*3+1],positions[faces[i]*3+2]];
503 			var v2=[positions[faces[i+1]*3],positions[faces[i+1]*3+1],positions[faces[i+1]*3+2]];
504 			var v3=[positions[faces[i+2]*3],positions[faces[i+2]*3+1],positions[faces[i+2]*3+2]];
505 			var vec1=GLGE.subVec3(v2,v1);
506 			var vec2=GLGE.subVec3(v3,v1);
507 			var norm=GLGE.toUnitVec3(GLGE.crossVec3(vec1,vec2));
508 			if(normals[faces[i]]==undefined) normals[faces[i]]=[];
509 			normals[faces[i]].push(norm);
510 			if(normals[faces[i+1]]==undefined) normals[faces[i+1]]=[];
511 			normals[faces[i+1]].push(norm);
512 			if(normals[faces[i+2]]==undefined) normals[faces[i+2]]=[];
513 			normals[faces[i+2]].push(norm);
514 		}
515 		var norms=[];
516 		for(i=0;i<normals.length;i++){
517 			var x=0,y=0,z=0;
518 			if(normals[i]!=undefined){
519 				for(var j=0;j<normals[i].length;j++){
520 					x+=normals[i][j][0];
521 					y+=normals[i][j][1];
522 					z+=normals[i][j][2];
523 				}
524 				x/=normals[i].length;
525 				y/=normals[i].length;
526 				z/=normals[i].length;
527 				norms[i*3]=x;
528 				norms[i*3+1]=y;
529 				norms[i*3+2]=z;
530 			}
531 		}
532 		this.setNormals(norms,n);
533 	}
534 }
535 /**
536 * Calculates a ambient occlution effect and sets the vertex color with AO level
537 */
538 GLGE.Mesh.prototype.calcFauxAO=function(){	
539 	this.optimize();
540 	
541 	//calculate ambient color based on vertex angles
542 	var verts=this.positions;
543 	var faces=this.faces.data;
544 	var normals=this.normals;
545 	
546 	var idx=[];
547 	var len=verts.length/3
548 	for(var i=0;i<len;i++){
549 		idx.push([]);
550 	}
551 	for(var i=0;i<faces.length;i=i+3){
552 		idx[faces[i]].push(faces[i+1]);
553 		idx[faces[i]].push(faces[i+2]);
554 		idx[faces[i+1]].push(faces[i]);
555 		idx[faces[i+1]].push(faces[i+2]);
556 		idx[faces[i+2]].push(faces[i]);
557 		idx[faces[i+2]].push(faces[i+1]);
558 	}
559 	var ao=[];
560 	for(var i=0;i<len;i++){
561 		var AOfactor=0;
562 		var normal=[normals[i*3],normals[i*3+1],normals[i*3+2]];
563 		for(var j=0;j<idx[i].length;j++){
564 			var f=idx[i][j];
565 			var v=[verts[f*3]-verts[i*3],verts[f*3+1]-verts[i*3+1],verts[f*3+2]-verts[i*3+2]];
566 			v=GLGE.toUnitVec3(v);
567 			AOfactor+=v[0]*normal[0]+v[1]*normal[1]+v[2]*normal[2];
568 		}
569 		AOfactor/=idx[i].length;
570 		AOfactor=1.0-(AOfactor+1)*0.5;
571 		ao.push(AOfactor);
572 		ao.push(AOfactor);
573 		ao.push(AOfactor);
574 		ao.push(1);
575 	}
576 	this.setVertexColors(ao);
577 }
578 /**
579 * optimize geometry
580 * @private
581 */
582 GLGE.Mesh.prototype.optimize=function(){
583 	var verts=this.positions;
584 	var normals=this.normals;
585 	var faces=this.faces.data;
586 	var tangents=this.tangents;
587 	var uv1=this.uv1set;
588 	var uv2=this.uv2set;
589 	//expand out the faces
590 	var vertsTemp=[];
591 	var normalsTemp=[];
592 	var uv1Temp=[];
593 	var uv2Temp=[];
594 	var tangentsTemp=[];
595 	if(faces){
596 		for(var i=0;i<faces.length;i++){
597 			vertsTemp.push(verts[faces[i]*3]);
598 			vertsTemp.push(verts[faces[i]*3+1]);
599 			vertsTemp.push(verts[faces[i]*3+2]);
600 			normalsTemp.push(normals[faces[i]*3]);
601 			normalsTemp.push(normals[faces[i]*3+1]);
602 			normalsTemp.push(normals[faces[i]*3+2]);
603 			if(tangents && tangents.length>0){
604 				tangentsTemp.push(tangents[faces[i]*3]);
605 				tangentsTemp.push(tangents[faces[i]*3+1]);
606 				tangentsTemp.push(tangents[faces[i]*3+2]);
607 			}
608 			if(uv1){
609 				uv1Temp.push(uv1[faces[i]*2]);
610 				uv1Temp.push(uv1[faces[i]*2+1]);
611 			}
612 			if(uv2){
613 				uv2Temp.push(uv2[faces[i]*2]);
614 				uv2Temp.push(uv2[faces[i]*2+1]);
615 			}
616 		}
617 	}else{
618 		vertsTemp=verts;
619 		normalsTemp=normals;
620 		tangentsTemp=tangents;
621 		uv1Temp=uv1;
622 		uv2Temp=uv2;
623 	}
624 
625 	var newVerts=[];
626 	var newNormals=[];
627 	var newFaces=[];
628 	var newUV1s=[];
629 	var newUV2s=[];
630 	var newTangents=[];
631 	var stack=[];
632 	
633 	for(var i=0;i<vertsTemp.length;i=i+3){
634 		if(uv1 && uv2){
635 			var idx=[vertsTemp[i],vertsTemp[i+1],vertsTemp[i+2],normalsTemp[i],normalsTemp[i+1],normalsTemp[i+2],uv1Temp[i/3*2],uv1Temp[i/3*2+1]].join(" ");
636 		}else if(uv1){
637 			var idx=[vertsTemp[i],vertsTemp[i+1],vertsTemp[i+2],normalsTemp[i],normalsTemp[i+1],normalsTemp[i+2],uv1Temp[i/3*2],uv1Temp[i/3*2+1]].join(" ");
638 		}else{
639 			var idx=[vertsTemp[i],vertsTemp[i+1],vertsTemp[i+2],normalsTemp[i],normalsTemp[i+1],normalsTemp[i+2]].join(" ");
640 		}
641 		var vertIdx=stack.indexOf(idx);
642 		if(vertIdx<0){
643 			stack.push(idx);
644 			vertIdx=stack.length-1;
645 			newVerts.push(vertsTemp[i]);
646 			newVerts.push(vertsTemp[i+1]);
647 			newVerts.push(vertsTemp[i+2]);
648 			newNormals.push(normalsTemp[i]);
649 			newNormals.push(normalsTemp[i+1]);
650 			newNormals.push(normalsTemp[i+2]);
651 			if(tangents && tangents.length>0){
652 				newTangents.push(tangentsTemp[i]);
653 				newTangents.push(tangentsTemp[i+1]);
654 				newTangents.push(tangentsTemp[i+2]);
655 			}
656 			if(uv1){
657 				newUV1s.push(uv1Temp[i/3*2]);
658 				newUV1s.push(uv1Temp[i/3*2+1]);
659 			}
660 			if(uv2){
661 				newUV2s.push(uv2Temp[i/3*2]);
662 				newUV2s.push(uv2Temp[i/3*2+1]);
663 			}
664 		}
665 		newFaces.push(vertIdx);
666 	}
667 	this.setPositions(newVerts).setNormals(newNormals).setFaces(newFaces).setUV(newUV1s).setUV2(newUV2s).setTangents(newTangents);
668 }
669 
670 
671 
672 /**
673 * Sets the Attributes for this mesh
674 * @param {WebGLContext} gl The context being drawn on
675 * @private
676 */
677 GLGE.Mesh.prototype.GLAttributes=function(gl,shaderProgram,frame1, frame2){
678 	this.gl=gl;
679 	if(!frame1) frame1=0;
680 	//if at this point we have no normals set then calculate them
681 	if(!this.normals) this.calcNormals();
682 	//disable all the attribute initially arrays - do I really need this?
683 	for(var i=0; i<8; i++) gl.disableVertexAttribArray(i);
684 	//check if the faces have been updated
685 	if(!this.faces.GL && this.faces.data && this.faces.data.length>0){
686 		this.GLSetFaceBuffer(gl);
687 		this.faces.GL=true;
688 	}
689 	//loop though the buffers
690 	for(i=0; i<this.buffers.length;i++){
691 		if(!this.buffers[i].GL){
692 			this.GLSetBuffer(gl,this.buffers[i].name,this.buffers[i].data,this.buffers[i].size);
693 			this.buffers[i].GL=true;
694 		}
695 		attribslot=GLGE.getAttribLocation(gl,shaderProgram, this.buffers[i].name);
696 		if(attribslot>-1){
697 			gl.bindBuffer(gl.ARRAY_BUFFER, this.GLbuffers[this.buffers[i].name]);
698 			gl.enableVertexAttribArray(attribslot);
699 			gl.vertexAttribPointer(attribslot, this.GLbuffers[this.buffers[i].name].itemSize, gl.FLOAT, false, 0, 0);
700 		}
701 	}
702 
703 	//do the position normal and if we have tangent then tangent
704 	var positionSlot=GLGE.getAttribLocation(gl,shaderProgram, "position");
705 	if(positionSlot>-1){
706 		gl.bindBuffer(gl.ARRAY_BUFFER, this.GLbuffers["position"+frame1]);
707 		gl.enableVertexAttribArray(positionSlot);
708 		gl.vertexAttribPointer(positionSlot, this.GLbuffers["position"+frame1].itemSize, gl.FLOAT, false, 0, 0);
709 	}
710 	var normalSlot=GLGE.getAttribLocation(gl,shaderProgram, "normal");
711 	if(normalSlot>-1){
712 		gl.bindBuffer(gl.ARRAY_BUFFER, this.GLbuffers["normal"+frame1]);
713 		gl.enableVertexAttribArray(normalSlot);
714 		gl.vertexAttribPointer(normalSlot, this.GLbuffers["normal"+frame1].itemSize, gl.FLOAT, false, 0, 0);
715 	}
716 	var tangentSlot=GLGE.getAttribLocation(gl,shaderProgram, "tangent");
717 	if(tangentSlot>-1){
718 		gl.bindBuffer(gl.ARRAY_BUFFER, this.GLbuffers["tangent"+frame1]);
719 		gl.enableVertexAttribArray(tangentSlot);
720 		gl.vertexAttribPointer(tangentSlot, this.GLbuffers["tangent"+frame1].itemSize, gl.FLOAT, false, 0, 0);
721 	}
722 	if(frame2!=undefined){
723 		var positionSlot2=GLGE.getAttribLocation(gl,shaderProgram, "position2");
724 		if(positionSlot2>-1){
725 			gl.bindBuffer(gl.ARRAY_BUFFER, this.GLbuffers["position"+frame2]);
726 			gl.enableVertexAttribArray(positionSlot2);
727 			gl.vertexAttribPointer(positionSlot2, this.GLbuffers["position"+frame2].itemSize, gl.FLOAT, false, 0, 0);
728 		}
729 		var normalSlot2=GLGE.getAttribLocation(gl,shaderProgram, "normal2");
730 		if(normalSlot2>-1){
731 			gl.bindBuffer(gl.ARRAY_BUFFER, this.GLbuffers["normal"+frame2]);
732 			gl.enableVertexAttribArray(normalSlot2);
733 			gl.vertexAttribPointer(normalSlot2, this.GLbuffers["normal"+frame2].itemSize, gl.FLOAT, false, 0, 0);
734 		}
735 		var tangentSlot2=GLGE.getAttribLocation(gl,shaderProgram, "tangent2");
736 		if(tangentSlot2>-1){
737 			gl.bindBuffer(gl.ARRAY_BUFFER, this.GLbuffers["tangent"+frame2]);
738 			gl.enableVertexAttribArray(tangentSlot2);
739 			gl.vertexAttribPointer(tangentSlot2, this.GLbuffers["tangent"+frame2].itemSize, gl.FLOAT, false, 0, 0);
740 		}	
741 	}
742 }
743 
744 
745 })(GLGE);