1 /*
  2  * OBJLoader.js
  3  *
  4  * Sweet Home 3D, Copyright (c) 2015 Emmanuel PUYBARET / eTeks <info@eteks.com>
  5  *
  6  * This program is free software; you can redistribute it and/or modify
  7  * it under the terms of the GNU General Public License as published by
  8  * the Free Software Foundation; either version 2 of the License, or
  9  * (at your option) any later version.
 10  *
 11  * This program is distributed in the hope that it will be useful,
 12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 14  * GNU General Public License for more details.
 15  *
 16  * You should have received a copy of the GNU General Public License
 17  * along with this program; if not, write to the Free Software
 18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 19  */
 20 
 21 // Requires core.js
 22 //          gl-matrix-min.js
 23 //          jszip.min.js
 24 //          scene3d.js
 25 //          Triangulator.js
 26 //          URLContent.js
 27 //          ModelLoader.js
 28 
 29 /**
 30  * Creates an instance of an OBJ + MTL loader.
 31  * @constructor
 32  * @extends ModelLoader
 33  * @author Emmanuel Puybaret
 34  */
 35 function OBJLoader() {
 36   ModelLoader.call(this, "obj");
 37 
 38   if (OBJLoader.defaultAppearances === null) {
 39     OBJLoader.defaultAppearances = {};
 40     OBJLoader.parseMaterial(
 41         // Description of the default Java 3D materials at MTL format 
 42         // (copied from com.sun.j3d.loaders.objectfile.DefaultMaterials class with inverse d transparency factor)
 43         "newmtl amber\n" +
 44         "Ka 0.0531 0.0531 0.0531\n" +
 45         "Kd 0.5755 0.2678 0.0000\n" +
 46         "Ks 0.3000 0.3000 0.3000\n" +
 47         "illum 2\n" +
 48         "Ns 60.0000\n" +
 49         "\n" +
 50         "newmtl amber_trans\n" +
 51         "Ka 0.0531 0.0531 0.0531\n" +
 52         "Kd 0.5755 0.2678 0.0000\n" +
 53         "Ks 0.3000 0.3000 0.3000\n" +
 54         "illum 2\n" +
 55         "d 0.1600\n" +
 56         "Ns 60.0000\n" +
 57         "\n" +
 58         "newmtl charcoal\n" +
 59         "Ka 0.0082 0.0082 0.0082\n" +
 60         "Kd 0.0041 0.0041 0.0041\n" +
 61         "Ks 0.3000 0.3000 0.3000\n" +
 62         "illum 2\n" +
 63         "Ns 60.0000\n" +
 64         "\n" +
 65         "newmtl lavendar\n" +
 66         "Ka 0.1281 0.0857 0.2122\n" +
 67         "Kd 0.2187 0.0906 0.3469\n" +
 68         "Ks 0.3000 0.3000 0.3000\n" +
 69         "illum 2\n" +
 70         "Ns 60.0000\n" +
 71         "\n" +
 72         "newmtl navy_blue\n" +
 73         "Ka 0.0000 0.0000 0.0490\n" +
 74         "Kd 0.0000 0.0000 0.0531\n" +
 75         "Ks 0.1878 0.1878 0.1878\n" +
 76         "illum 2\n" +
 77         "Ns 91.4700\n" +
 78         "\n" +
 79         "newmtl pale_green\n" +
 80         "Ka 0.0444 0.0898 0.0447\n" +
 81         "Kd 0.0712 0.3796 0.0490\n" +
 82         "Ks 0.1878 0.1878 0.1878\n" +
 83         "illum 2\n" +
 84         "Ns 91.4700\n" +
 85         "\n" +
 86         "newmtl pale_pink\n" +
 87         "Ka 0.0898 0.0444 0.0444\n" +
 88         "Kd 0.6531 0.2053 0.4160\n" +
 89         "Ks 0.1878 0.1878 0.1878\n" +
 90         "illum 2\n" +
 91         "Ns 91.4700\n" +
 92         "\n" +
 93         "newmtl pale_yellow\n" +
 94         "Ka 0.3606 0.3755 0.0935\n" +
 95         "Kd 0.6898 0.6211 0.1999\n" +
 96         "Ks 0.1878 0.1878 0.1878\n" +
 97         "illum 2\n" +
 98         "Ns 91.4700\n" +
 99         "\n" +
100         "newmtl peach\n" +
101         "Ka 0.3143 0.1187 0.0167\n" +
102         "Kd 0.6367 0.1829 0.0156\n" +
103         "Ks 0.1878 0.1878 0.1878\n" +
104         "illum 2\n" +
105         "Ns 91.4700\n" +
106         "\n" +
107         "newmtl periwinkle\n" +
108         "Ka 0.0000 0.0000 0.1184\n" +
109         "Kd 0.0000 0.0396 0.8286\n" +
110         "Ks 0.1878 0.1878 0.1878\n" +
111         "illum 2\n" +
112         "Ns 91.4700\n" +
113         "\n" +
114         "newmtl redwood\n" +
115         "Ka 0.0204 0.0027 0.0000\n" +
116         "Kd 0.2571 0.0330 0.0000\n" +
117         "Ks 0.1878 0.1878 0.1878\n" +
118         "illum 2\n" +
119         "Ns 91.4700\n" +
120         "\n" +
121         "newmtl smoked_glass\n" +
122         "Ka 0.0000 0.0000 0.0000\n" +
123         "Kd 0.0041 0.0041 0.0041\n" +
124         "Ks 0.1878 0.1878 0.1878\n" +
125         "illum 2\n" +
126         "d 0.0200\n" +
127         "Ns 91.4700\n" +
128         "\n" +
129         "newmtl aqua_filter\n" +
130         "Ka 0.0000 0.0000 0.0000\n" +
131         "Kd 0.3743 0.6694 0.5791\n" +
132         "Ks 0.1878 0.1878 0.1878\n" +
133         "illum 2\n" +
134         "d 0.0200\n" +
135         "Ns 91.4700\n" +
136         "\n" +
137         "newmtl yellow_green\n" +
138         "Ka 0.0000 0.0000 0.0000\n" +
139         "Kd 0.1875 0.4082 0.0017\n" +
140         "Ks 0.1878 0.1878 0.1878\n" +
141         "illum 2\n" +
142         "Ns 91.4700\n" +
143         "\n" +
144         "newmtl bluetint\n" +
145         "Ka 0.1100 0.4238 0.5388\n" +
146         "Kd 0.0468 0.7115 0.9551\n" +
147         "Ks 0.3184 0.3184 0.3184\n" +
148         "illum 9\n" +
149         "d 0.4300\n" +
150         "Ns 60.0000\n" +
151         "sharpness 60.0000\n" +
152         "\n" +
153         "newmtl plasma\n" +
154         "Ka 0.4082 0.0816 0.2129\n" +
155         "Kd 1.0000 0.0776 0.4478\n" +
156         "Ks 0.3000 0.3000 0.3000\n" +
157         "illum 9\n" +
158         "d 0.2500\n" +
159         "Ns 60.0000\n" +
160         "sharpness 60.0000\n" +
161         "\n" +
162         "newmtl emerald\n" +
163         "Ka 0.0470 1.0000 0.0000\n" +
164         "Kd 0.0470 1.0000 0.0000\n" +
165         "Ks 0.2000 0.2000 0.2000\n" +
166         "illum 9\n" +
167         "d 0.2500\n" +
168         "Ns 60.0000\n" +
169         "sharpness 60.0000\n" +
170         "\n" +
171         "newmtl ruby\n" +
172         "Ka 1.0000 0.0000 0.0000\n" +
173         "Kd 1.0000 0.0000 0.0000\n" +
174         "Ks 0.2000 0.2000 0.2000\n" +
175         "illum 9\n" +
176         "d 0.2500\n" +
177         "Ns 60.0000\n" +
178         "sharpness 60.0000\n" +
179         "\n" +
180         "newmtl sapphire\n" +
181         "Ka 0.0235 0.0000 1.0000\n" +
182         "Kd 0.0235 0.0000 1.0000\n" +
183         "Ks 0.2000 0.2000 0.2000\n" +
184         "illum 9\n" +
185         "d 0.2500\n" +
186         "Ns 60.0000\n" +
187         "sharpness 60.0000\n" +
188         "\n" +
189         "newmtl white\n" +
190         "Ka 0.4000 0.4000 0.4000\n" +
191         "Kd 1.0000 1.0000 1.0000\n" +
192         "Ks 0.3000 0.3000 0.3000\n" +
193         "illum 2\n" +
194         "Ns 60.0000\n" +
195         "\n" +
196         "newmtl red\n" +
197         "Ka 0.4449 0.0000 0.0000\n" +
198         "Kd 0.7714 0.0000 0.0000\n" +
199         "Ks 0.8857 0.0000 0.0000\n" +
200         "illum 2\n" +
201         "Ns 136.4300\n" +
202         "\n" +
203         "newmtl blue_pure\n" +
204         "Ka 0.0000 0.0000 0.5000\n" +
205         "Kd 0.0000 0.0000 1.0000\n" +
206         "Ks 0.0000 0.0000 0.5000\n" +
207         "illum 2\n" +
208         "Ns 65.8900\n" +
209         "\n" +
210         "newmtl lime\n" +
211         "Ka 0.0000 0.5000 0.0000\n" +
212         "Kd 0.0000 1.0000 0.0000\n" +
213         "Ks 0.0000 0.5000 0.0000\n" +
214         "illum 2\n" +
215         "Ns 65.8900\n" +
216         "\n" +
217         "newmtl green\n" +
218         "Ka 0.0000 0.2500 0.0000\n" +
219         "Kd 0.0000 0.2500 0.0000\n" +
220         "Ks 0.0000 0.2500 0.0000\n" +
221         "illum 2\n" +
222         "Ns 65.8900\n" +
223         "\n" +
224         "newmtl yellow\n" +
225         "Ka 1.0000 0.6667 0.0000\n" +
226         "Kd 1.0000 0.6667 0.0000\n" +
227         "Ks 1.0000 0.6667 0.0000\n" +
228         "illum 2\n" +
229         "Ns 65.8900\n" +
230         "\n" +
231         "newmtl purple\n" +
232         "Ka 0.5000 0.0000 1.0000\n" +
233         "Kd 0.5000 0.0000 1.0000\n" +
234         "Ks 0.5000 0.0000 1.0000\n" +
235         "illum 2\n" +
236         "Ns 65.8900\n" +
237         "\n" +
238         "newmtl orange\n" +
239         "Ka 1.0000 0.1667 0.0000\n" +
240         "Kd 1.0000 0.1667 0.0000\n" +
241         "Ks 1.0000 0.1667 0.0000\n" +
242         "illum 2\n" +
243         "Ns 65.8900\n" +
244         "\n" +
245         "newmtl grey\n" +
246         "Ka 0.5000 0.5000 0.5000\n" +
247         "Kd 0.1837 0.1837 0.1837\n" +
248         "Ks 0.5000 0.5000 0.5000\n" +
249         "illum 2\n" +
250         "Ns 65.8900\n" +
251         "\n" +
252         "newmtl rubber\n" +
253         "Ka 0.0000 0.0000 0.0000\n" +
254         "Kd 0.0100 0.0100 0.0100\n" +
255         "Ks 0.1000 0.1000 0.1000\n" +
256         "illum 2\n" +
257         "Ns 65.8900\n" +
258         "\n" +
259         "newmtl flaqua\n" +
260         "Ka 0.0000 0.4000 0.4000\n" +
261         "Kd 0.0000 0.5000 0.5000\n" +
262         "illum 1\n" +
263         "\n" +
264         "newmtl flblack\n" +
265         "Ka 0.0000 0.0000 0.0000\n" +
266         "Kd 0.0041 0.0041 0.0041\n" +
267         "illum 1\n" +
268         "\n" +
269         "newmtl flblue_pure\n" +
270         "Ka 0.0000 0.0000 0.5592\n" +
271         "Kd 0.0000 0.0000 0.7102\n" +
272         "illum 1\n" +
273         "\n" +
274         "newmtl flgrey\n" +
275         "Ka 0.2163 0.2163 0.2163\n" +
276         "Kd 0.5000 0.5000 0.5000\n" +
277         "illum 1\n" +
278         "\n" +
279         "newmtl fllime\n" +
280         "Ka 0.0000 0.3673 0.0000\n" +
281         "Kd 0.0000 1.0000 0.0000\n" +
282         "illum 1\n" +
283         "\n" +
284         "newmtl florange\n" +
285         "Ka 0.6857 0.1143 0.0000\n" +
286         "Kd 1.0000 0.1667 0.0000\n" +
287         "illum 1\n" +
288         "\n" +
289         "newmtl flpurple\n" +
290         "Ka 0.2368 0.0000 0.4735\n" +
291         "Kd 0.3755 0.0000 0.7510\n" +
292         "illum 1\n" +
293         "\n" +
294         "newmtl flred\n" +
295         "Ka 0.4000 0.0000 0.0000\n" +
296         "Kd 1.0000 0.0000 0.0000\n" +
297         "illum 1\n" +
298         "\n" +
299         "newmtl flyellow\n" +
300         "Ka 0.7388 0.4925 0.0000\n" +
301         "Kd 1.0000 0.6667 0.0000\n" +
302         "illum 1\n" +
303         "\n" +
304         "newmtl pink\n" +
305         "Ka 0.9469 0.0078 0.2845\n" +
306         "Kd 0.9878 0.1695 0.6702\n" +
307         "Ks 0.7429 0.2972 0.2972\n" +
308         "illum 2\n" +
309         "Ns 106.2000\n" +
310         "\n" +
311         "newmtl flbrown\n" +
312         "Ka 0.0571 0.0066 0.0011\n" +
313         "Kd 0.1102 0.0120 0.0013\n" +
314         "illum 1\n" +
315         "\n" +
316         "newmtl brown\n" +
317         "Ka 0.1020 0.0185 0.0013\n" +
318         "Kd 0.0857 0.0147 0.0000\n" +
319         "Ks 0.1633 0.0240 0.0000\n" +
320         "illum 2\n" +
321         "Ns 65.8900\n" +
322         "\n" +
323         "newmtl glass\n" +
324         "Ka 1.0000 1.0000 1.0000\n" +
325         "Kd 0.4873 0.4919 0.5306\n" +
326         "Ks 0.6406 0.6939 0.9020\n" +
327         "illum 2\n" +
328         "Ns 200.0000\n" +
329         "\n" +
330         "newmtl flesh\n" +
331         "Ka 0.4612 0.3638 0.2993\n" +
332         "Kd 0.5265 0.4127 0.3374\n" +
333         "Ks 0.3000 0.3000 0.3000\n" +
334         "illum 2\n" +
335         "Ns 60.0000\n" +
336         "\n" +
337         "newmtl aqua\n" +
338         "Ka 0.0000 0.4000 0.4000\n" +
339         "Kd 0.0000 0.5000 0.5000\n" +
340         "Ks 0.5673 0.5673 0.5673\n" +
341         "illum 2\n" +
342         "Ns 60.0000\n" +
343         "\n" +
344         "newmtl black\n" +
345         "Ka 0.0000 0.0000 0.0000\n" +
346         "Kd 0.0020 0.0020 0.0020\n" +
347         "Ks 0.5184 0.5184 0.5184\n" +
348         "illum 2\n" +
349         "Ns 157.3600\n" +
350         "\n" +
351         "newmtl silver\n" +
352         "Ka 0.9551 0.9551 0.9551\n" +
353         "Kd 0.6163 0.6163 0.6163\n" +
354         "Ks 0.3000 0.3000 0.3000\n" +
355         "illum 2\n" +
356         "Ns 60.0000\n" +
357         "\n" +
358         "newmtl dkblue_pure\n" +
359         "Ka 0.0000 0.0000 0.0449\n" +
360         "Kd 0.0000 0.0000 0.1347\n" +
361         "Ks 0.0000 0.0000 0.5673\n" +
362         "illum 2\n" +
363         "Ns 65.8900\n" +
364         "\n" +
365         "newmtl fldkblue_pure\n" +
366         "Ka 0.0000 0.0000 0.0449\n" +
367         "Kd 0.0000 0.0000 0.1347\n" +
368         "illum 1\n" +
369         "\n" +
370         "newmtl dkgreen\n" +
371         "Ka 0.0000 0.0122 0.0000\n" +
372         "Kd 0.0058 0.0245 0.0000\n" +
373         "Ks 0.0000 0.0490 0.0000\n" +
374         "illum 2\n" +
375         "Ns 60.0000\n" +
376         "\n" +
377         "newmtl dkgrey\n" +
378         "Ka 0.0490 0.0490 0.0490\n" +
379         "Kd 0.0490 0.0490 0.0490\n" +
380         "Ks 0.3000 0.3000 0.3000\n" +
381         "illum 2\n" +
382         "Ns 60.0000\n" +
383         "\n" +
384         "newmtl ltbrown\n" +
385         "Ka 0.1306 0.0538 0.0250\n" +
386         "Kd 0.2776 0.1143 0.0531\n" +
387         "Ks 0.3000 0.1235 0.0574\n" +
388         "illum 2\n" +
389         "Ns 60.0000\n" +
390         "\n" +
391         "newmtl fldkgreen\n" +
392         "Ka 0.0000 0.0122 0.0000\n" +
393         "Kd 0.0058 0.0245 0.0000\n" +
394         "illum 1\n" +
395         "\n" +
396         "newmtl flltbrown\n" +
397         "Ka 0.1306 0.0538 0.0250\n" +
398         "Kd 0.2776 0.1143 0.0531\n" +
399         "illum 1\n" +
400         "\n" +
401         "newmtl tan\n" +
402         "Ka 0.4000 0.3121 0.1202\n" +
403         "Kd 0.6612 0.5221 0.2186\n" +
404         "Ks 0.5020 0.4118 0.2152\n" +
405         "illum 2\n" +
406         "Ns 60.0000\n" +
407         "\n" +
408         "newmtl fltan\n" +
409         "Ka 0.4000 0.3121 0.1202\n" +
410         "Kd 0.6612 0.4567 0.1295\n" +
411         "illum 1\n" +
412         "\n" +
413         "newmtl brzskin\n" +
414         "Ka 0.4408 0.2694 0.1592\n" +
415         "Kd 0.3796 0.2898 0.2122\n" +
416         "Ks 0.3000 0.3000 0.3000\n" +
417         "illum 2\n" +
418         "Ns 25.0000\n" +
419         "\n" +
420         "newmtl lips\n" +
421         "Ka 0.4408 0.2694 0.1592\n" +
422         "Kd 0.9265 0.2612 0.2898\n" +
423         "Ks 0.3000 0.3000 0.3000\n" +
424         "illum 2\n" +
425         "Ns 25.0000\n" +
426         "\n" +
427         "newmtl redorange\n" +
428         "Ka 0.3918 0.0576 0.0000\n" +
429         "Kd 0.7551 0.0185 0.0000\n" +
430         "Ks 0.4694 0.3224 0.1667\n" +
431         "illum 2\n" +
432         "Ns 132.5600\n" +
433         "\n" +
434         "newmtl blutan\n" +
435         "Ka 0.4408 0.2694 0.1592\n" +
436         "Kd 0.0776 0.2571 0.2041\n" +
437         "Ks 0.1467 0.1469 0.0965\n" +
438         "illum 2\n" +
439         "Ns 25.0000\n" +
440         "\n" +
441         "newmtl bluteal\n" +
442         "Ka 0.0041 0.1123 0.1224\n" +
443         "Kd 0.0776 0.2571 0.2041\n" +
444         "Ks 0.1467 0.1469 0.0965\n" +
445         "illum 2\n" +
446         "Ns 25.0000\n" +
447         "\n" +
448         "newmtl pinktan\n" +
449         "Ka 0.4408 0.2694 0.1592\n" +
450         "Kd 0.6857 0.2571 0.2163\n" +
451         "Ks 0.1467 0.1469 0.0965\n" +
452         "illum 2\n" +
453         "Ns 25.0000\n" +
454         "\n" +
455         "newmtl brnhair\n" +
456         "Ka 0.0612 0.0174 0.0066\n" +
457         "Kd 0.0898 0.0302 0.0110\n" +
458         "Ks 0.1306 0.0819 0.0352\n" +
459         "illum 2\n" +
460         "Ns 60.4700\n" +
461         "\n" +
462         "newmtl blondhair\n" +
463         "Ka 0.4449 0.2632 0.0509\n" +
464         "Kd 0.5714 0.3283 0.0443\n" +
465         "Ks 0.7755 0.4602 0.0918\n" +
466         "illum 2\n" +
467         "Ns 4.6500\n" +
468         "\n" +
469         "newmtl flblonde\n" +
470         "Ka 0.4449 0.2632 0.0509\n" +
471         "Kd 0.5714 0.3283 0.0443\n" +
472         "illum 1\n" +
473         "\n" +
474         "newmtl yelloworng\n" +
475         "Ka 0.5837 0.1715 0.0000\n" +
476         "Kd 0.8857 0.2490 0.0000\n" +
477         "Ks 0.3000 0.3000 0.3000\n" +
478         "illum 2\n" +
479         "Ns 60.0000\n" +
480         "\n" +
481         "newmtl bone\n" +
482         "Ka 0.3061 0.1654 0.0650\n" +
483         "Kd 0.9000 0.7626 0.4261\n" +
484         "Ks 0.8939 0.7609 0.5509\n" +
485         "illum 2\n" +
486         "Ns 200.0000\n" +
487         "\n" +
488         "newmtl teeth\n" +
489         "Ka 0.6408 0.5554 0.3845\n" +
490         "Kd 0.9837 0.7959 0.4694\n" +
491         "illum 1\n" +
492         "\n" +
493         "newmtl brass\n" +
494         "Ka 0.2490 0.1102 0.0000\n" +
495         "Kd 0.4776 0.1959 0.0000\n" +
496         "Ks 0.5796 0.5796 0.5796\n" +
497         "illum 2\n" +
498         "Ns 134.8800\n" +
499         "\n" +
500         "newmtl dkred\n" +
501         "Ka 0.0939 0.0000 0.0000\n" +
502         "Kd 0.2286 0.0000 0.0000\n" +
503         "Ks 0.2490 0.0000 0.0000\n" +
504         "illum 2\n" +
505         "Ns 60.0000\n" +
506         "\n" +
507         "newmtl taupe\n" +
508         "Ka 0.1061 0.0709 0.0637\n" +
509         "Kd 0.2041 0.1227 0.1058\n" +
510         "Ks 0.3000 0.3000 0.3000\n" +
511         "illum 2\n" +
512         "Ns 84.5000\n" +
513         "\n" +
514         "newmtl dkteal\n" +
515         "Ka 0.0000 0.0245 0.0163\n" +
516         "Kd 0.0000 0.0653 0.0449\n" +
517         "Ks 0.3000 0.3000 0.3000\n" +
518         "illum 2\n" +
519         "Ns 55.0400\n" +
520         "\n" +
521         "newmtl dkdkgrey\n" +
522         "Ka 0.0000 0.0000 0.0000\n" +
523         "Kd 0.0122 0.0122 0.0122\n" +
524         "Ks 0.3000 0.3000 0.3000\n" +
525         "illum 2\n" +
526         "Ns 60.0000\n" +
527         "\n" +
528         "newmtl dkblue\n" +
529         "Ka 0.0000 0.0029 0.0408\n" +
530         "Kd 0.0000 0.0041 0.0571\n" +
531         "Ks 0.3000 0.3000 0.3000\n" +
532         "illum 2\n" +
533         "Ns 60.0000\n" +
534         "\n" +
535         "newmtl gold\n" +
536         "Ka 0.7224 0.1416 0.0000\n" +
537         "Kd 1.0000 0.4898 0.0000\n" +
538         "Ks 0.7184 0.3695 0.3695\n" +
539         "illum 2\n" +
540         "Ns 123.2600\n" +
541         "\n" +
542         "newmtl redbrick\n" +
543         "Ka 0.1102 0.0067 0.0067\n" +
544         "Kd 0.3306 0.0398 0.0081\n" +
545         "illum 1\n" +
546         "\n" +
547         "newmtl flmustard\n" +
548         "Ka 0.4245 0.2508 0.0000\n" +
549         "Kd 0.8898 0.3531 0.0073\n" +
550         "illum 1\n" +
551         "\n" +
552         "newmtl flpinegreen\n" +
553         "Ka 0.0367 0.0612 0.0204\n" +
554         "Kd 0.1061 0.2163 0.0857\n" +
555         "illum 1\n" +
556         "\n" +
557         "newmtl fldkred\n" +
558         "Ka 0.0939 0.0000 0.0000\n" +
559         "Kd 0.2286 0.0082 0.0082\n" +
560         "illum 1\n" +
561         "\n" +
562         "newmtl fldkgreen2\n" +
563         "Ka 0.0025 0.0122 0.0014\n" +
564         "Kd 0.0245 0.0694 0.0041\n" +
565         "illum 1\n" +
566         "\n" +
567         "newmtl flmintgreen\n" +
568         "Ka 0.0408 0.1429 0.0571\n" +
569         "Kd 0.1306 0.2898 0.1673\n" +
570         "illum 1\n" +
571         "\n" +
572         "newmtl olivegreen\n" +
573         "Ka 0.0167 0.0245 0.0000\n" +
574         "Kd 0.0250 0.0367 0.0000\n" +
575         "Ks 0.2257 0.2776 0.1167\n" +
576         "illum 2\n" +
577         "Ns 97.6700\n" +
578         "\n" +
579         "newmtl skin\n" +
580         "Ka 0.2286 0.0187 0.0187\n" +
581         "Kd 0.1102 0.0328 0.0139\n" +
582         "Ks 0.3000 0.3000 0.3000\n" +
583         "illum 2\n" +
584         "Ns 17.8300\n" +
585         "\n" +
586         "newmtl redbrown\n" +
587         "Ka 0.1469 0.0031 0.0000\n" +
588         "Kd 0.2816 0.0060 0.0000\n" +
589         "Ks 0.3714 0.3714 0.3714\n" +
590         "illum 2\n" +
591         "Ns 141.0900\n" +
592         "\n" +
593         "newmtl deepgreen\n" +
594         "Ka 0.0000 0.0050 0.0000\n" +
595         "Kd 0.0000 0.0204 0.0050\n" +
596         "Ks 0.3000 0.3000 0.3000\n" +
597         "illum 2\n" +
598         "Ns 113.1800\n" +
599         "\n" +
600         "newmtl flltolivegreen\n" +
601         "Ka 0.0167 0.0245 0.0000\n" +
602         "Kd 0.0393 0.0531 0.0100\n" +
603         "illum 1\n" +
604         "\n" +
605         "newmtl jetflame\n" +
606         "Ka 0.7714 0.0000 0.0000\n" +
607         "Kd 0.9510 0.4939 0.0980\n" +
608         "Ks 0.8531 0.5222 0.0000\n" +
609         "illum 2\n" +
610         "Ns 132.5600\n" +
611         "\n" +
612         "newmtl brownskn\n" +
613         "Ka 0.0122 0.0041 0.0000\n" +
614         "Kd 0.0204 0.0082 0.0000\n" +
615         "Ks 0.0735 0.0508 0.0321\n" +
616         "illum 2\n" +
617         "Ns 20.1600\n" +
618         "\n" +
619         "newmtl greenskn\n" +
620         "Ka 0.0816 0.0449 0.0000\n" +
621         "Kd 0.0000 0.0735 0.0000\n" +
622         "Ks 0.0490 0.1224 0.0898\n" +
623         "illum 3\n" +
624         "Ns 46.5100\n" +
625         "sharpness 146.5100\n" +
626         "\n" +
627         "newmtl ltgrey\n" +
628         "Ka 0.5000 0.5000 0.5000\n" +
629         "Kd 0.3837 0.3837 0.3837\n" +
630         "Ks 0.5000 0.5000 0.5000\n" +
631         "illum 2\n" +
632         "Ns 65.8900\n" +
633         "\n" +
634         "newmtl bronze\n" +
635         "Ka 0.0449 0.0204 0.0000\n" +
636         "Kd 0.0653 0.0367 0.0122\n" +
637         "Ks 0.0776 0.0408 0.0000\n" +
638         "illum 3\n" +
639         "Ns 137.2100\n" +
640         "sharpness 125.5800\n" +
641         "\n" +
642         "newmtl bone1\n" +
643         "Ka 0.6408 0.5554 0.3845\n" +
644         "Kd 0.9837 0.7959 0.4694\n" +
645         "illum 1\n" +
646         "\n" +
647         "newmtl flwhite1\n" +
648         "Ka 0.9306 0.9306 0.9306\n" +
649         "Kd 1.0000 1.0000 1.0000\n" +
650         "illum 1\n" +
651         "\n" +
652         "newmtl flwhite\n" +
653         "Ka 0.6449 0.6116 0.5447\n" +
654         "Kd 0.9837 0.9309 0.8392\n" +
655         "Ks 0.8082 0.7290 0.5708\n" +
656         "illum 2\n" +
657         "Ns 200.0000\n" +
658         "\n" +
659         "newmtl shadow\n" +
660         "Kd 0.0350 0.0248 0.0194\n" +
661         "illum 0\n" +
662         "d 0.2500\n" +
663         "\n" +
664         "newmtl fldkolivegreen\n" +
665         "Ka 0.0056 0.0082 0.0000\n" +
666         "Kd 0.0151 0.0204 0.0038\n" +
667         "illum 1\n" +
668         "\n" +
669         "newmtl fldkdkgrey\n" +
670         "Ka 0.0000 0.0000 0.0000\n" +
671         "Kd 0.0122 0.0122 0.0122\n" +
672         "illum 1\n" +
673         "\n" +
674         "newmtl lcdgreen\n" +
675         "Ka 0.4000 0.4000 0.4000\n" +
676         "Kd 0.5878 1.0000 0.5061\n" +
677         "Ks 0.3000 0.3000 0.3000\n" +
678         "illum 2\n" +
679         "Ns 60.0000\n" +
680         "\n" +
681         "newmtl brownlips\n" +
682         "Ka 0.1143 0.0694 0.0245\n" +
683         "Kd 0.1429 0.0653 0.0408\n" +
684         "Ks 0.3000 0.3000 0.3000\n" +
685         "illum 2\n" +
686         "Ns 25.0000\n" +
687         "\n" +
688         "newmtl muscle\n" +
689         "Ka 0.2122 0.0077 0.0154\n" +
690         "Kd 0.4204 0.0721 0.0856\n" +
691         "Ks 0.1184 0.1184 0.1184\n" +
692         "illum 2\n" +
693         "Ns 25.5800\n" +
694         "\n" +
695         "newmtl flltgrey\n" +
696         "Ka 0.5224 0.5224 0.5224\n" +
697         "Kd 0.8245 0.8245 0.8245\n" +
698         "illum 1\n" +
699         "\n" +
700         "newmtl offwhite.warm\n" +
701         "Ka 0.5184 0.4501 0.3703\n" +
702         "Kd 0.8367 0.6898 0.4490\n" +
703         "Ks 0.3000 0.3000 0.3000\n" +
704         "illum 2\n" +
705         "Ns 60.0000\n" +
706         "\n" +
707         "newmtl offwhite.cool\n" +
708         "Ka 0.5184 0.4501 0.3703\n" +
709         "Kd 0.8367 0.6812 0.5703\n" +
710         "Ks 0.3000 0.3000 0.3000\n" +
711         "illum 2\n" +
712         "Ns 60.0000\n" +
713         "\n" +
714         "newmtl yellowbrt\n" +
715         "Ka 0.4000 0.4000 0.4000\n" +
716         "Kd 1.0000 0.7837 0.0000\n" +
717         "Ks 0.3000 0.3000 0.3000\n" +
718         "illum 2\n" +
719         "Ns 60.0000\n" +
720         "\n" +
721         "newmtl chappie\n" +
722         "Ka 0.4000 0.4000 0.4000\n" +
723         "Kd 0.5837 0.1796 0.0367\n" +
724         "Ks 0.3000 0.3000 0.3000\n" +
725         "illum 2\n" +
726         "Ns 60.0000\n" +
727         "\n" +
728         "newmtl archwhite\n" +
729         "Ka 0.2816 0.2816 0.2816\n" +
730         "Kd 0.9959 0.9959 0.9959\n" +
731         "illum 1\n" +
732         "\n" +
733         "newmtl archwhite2\n" +
734         "Ka 0.2816 0.2816 0.2816\n" +
735         "Kd 0.8408 0.8408 0.8408\n" +
736         "illum 1\n" +
737         "\n" +
738         "newmtl lighttan\n" +
739         "Ka 0.0980 0.0536 0.0220\n" +
740         "Kd 0.7020 0.4210 0.2206\n" +
741         "Ks 0.8286 0.8057 0.5851\n" +
742         "illum 2\n" +
743         "Ns 177.5200\n" +
744         "\n" +
745         "newmtl lighttan2\n" +
746         "Ka 0.0980 0.0492 0.0144\n" +
747         "Kd 0.3143 0.1870 0.0962\n" +
748         "Ks 0.8286 0.8057 0.5851\n" +
749         "illum 2\n" +
750         "Ns 177.5200\n" +
751         "\n" +
752         "newmtl lighttan3\n" +
753         "Ka 0.0980 0.0492 0.0144\n" +
754         "Kd 0.1796 0.0829 0.0139\n" +
755         "Ks 0.8286 0.8057 0.5851\n" +
756         "illum 2\n" +
757         "Ns 177.5200\n" +
758         "\n" +
759         "newmtl lightyellow\n" +
760         "Ka 0.5061 0.1983 0.0000\n" +
761         "Kd 1.0000 0.9542 0.3388\n" +
762         "Ks 1.0000 0.9060 0.0000\n" +
763         "illum 2\n" +
764         "Ns 177.5200\n" +
765         "\n" +
766         "newmtl lighttannew\n" +
767         "Ka 0.0980 0.0492 0.0144\n" +
768         "Kd 0.7878 0.6070 0.3216\n" +
769         "Ks 0.8286 0.8057 0.5851\n" +
770         "illum 2\n" +
771         "Ns 177.5200\n" +
772         "\n" +
773         "newmtl default\n" +
774         "Ka 0.4000 0.4000 0.4000\n" +
775         "Kd 0.7102 0.7020 0.6531\n" +
776         "Ks 0.3000 0.3000 0.3000\n" +
777         "illum 2\n" +
778         "Ns 128.0000\n" +
779         "\n" +
780         "newmtl ship2\n" +
781         "Ka 0.0000 0.0000 0.0000\n" +
782         "Kd 1.0000 1.0000 1.0000\n" +
783         "Ks 0.1143 0.1143 0.1143\n" +
784         "illum 2\n" +
785         "Ns 60.0000\n" +
786         "\n" +
787         "newmtl dkpurple\n" +
788         "Ka 0.0082 0.0000 0.0163\n" +
789         "Kd 0.0245 0.0000 0.0490\n" +
790         "Ks 0.1266 0.0000 0.2531\n" +
791         "illum 2\n" +
792         "Ns 65.8900\n" +
793         "\n" +
794         "newmtl dkorange\n" +
795         "Ka 0.4041 0.0123 0.0000\n" +
796         "Kd 0.7143 0.0350 0.0000\n" +
797         "Ks 0.7102 0.0870 0.0000\n" +
798         "illum 2\n" +
799         "Ns 65.8900\n" +
800         "\n" +
801         "newmtl mintgrn\n" +
802         "Ka 0.0101 0.1959 0.0335\n" +
803         "Kd 0.0245 0.4776 0.0816\n" +
804         "Ks 0.0245 0.4776 0.0816\n" +
805         "illum 2\n" +
806         "Ns 65.8900\n" +
807         "\n" +
808         "newmtl fgreen\n" +
809         "Ka 0.0000 0.0449 0.0000\n" +
810         "Kd 0.0000 0.0449 0.0004\n" +
811         "Ks 0.0062 0.0694 0.0000\n" +
812         "illum 2\n" +
813         "Ns 106.2000\n" +
814         "\n" +
815         "newmtl glassblutint\n" +
816         "Ka 0.4000 0.4000 0.4000\n" +
817         "Kd 0.5551 0.8000 0.7730\n" +
818         "Ks 0.7969 0.9714 0.9223\n" +
819         "illum 4\n" +
820         "d 0.6700\n" +
821         "Ns 60.0000\n" +
822         "sharpness 60.0000\n" +
823         "\n" +
824         "newmtl bflesh\n" +
825         "Ka 0.0122 0.0122 0.0122\n" +
826         "Kd 0.0245 0.0081 0.0021\n" +
827         "Ks 0.0531 0.0460 0.0153\n" +
828         "illum 2\n" +
829         "Ns 20.1600\n" +
830         "\n" +
831         "newmtl meh\n" +
832         "Ka 0.4000 0.4000 0.4000\n" +
833         "Kd 0.5551 0.8000 0.7730\n" +
834         "Ks 0.7969 0.9714 0.9223\n" +
835         "illum 4\n" +
836         "d 0.2500\n" +
837         "Ns 183.7200\n" +
838         "sharpness 60.0000\n" +
839         "\n" +
840         "newmtl violet\n" +
841         "Ka 0.0083 0.0000 0.1265\n" +
842         "Kd 0.0287 0.0269 0.1347\n" +
843         "Ks 0.2267 0.4537 0.6612\n" +
844         "illum 2\n" +
845         "Ns 96.9000\n" +
846         "\n" +
847         "newmtl iris\n" +
848         "Ka 0.3061 0.0556 0.0037\n" +
849         "Kd 0.0000 0.0572 0.3184\n" +
850         "Ks 0.8041 0.6782 0.1477\n" +
851         "illum 2\n" +
852         "Ns 188.3700\n" +
853         "\n" +
854         "newmtl blugrn\n" +
855         "Ka 0.4408 0.4144 0.1592\n" +
856         "Kd 0.0811 0.6408 0.2775\n" +
857         "Ks 0.1467 0.1469 0.0965\n" +
858         "illum 2\n" +
859         "Ns 25.0000\n" +
860         "\n" +
861         "newmtl glasstransparent\n" +
862         "Ka 0.2163 0.2163 0.2163\n" +
863         "Kd 0.4694 0.4694 0.4694\n" +
864         "Ks 0.6082 0.6082 0.6082\n" +
865         "illum 4\n" +
866         "d 0.2500\n" +
867         "Ns 200.0000\n" +
868         "sharpness 60.0000\n" +
869         "\n" +
870         "newmtl fleshtransparent\n" +
871         "Ka 0.4000 0.2253 0.2253\n" +
872         "Kd 0.6898 0.2942 0.1295\n" +
873         "Ks 0.7388 0.4614 0.4614\n" +
874         "illum 4\n" +
875         "d 0.2500\n" +
876         "Ns 6.2000\n" +
877         "sharpness 60.0000\n" +
878         "\n" +
879         "newmtl fldkgrey\n" +
880         "Ka 0.0449 0.0449 0.0449\n" +
881         "Kd 0.0939 0.0939 0.0939\n" +
882         "illum 1\n" +
883         "\n" +
884         "newmtl sky_blue\n" +
885         "Ka 0.1363 0.2264 0.4122\n" +
886         "Kd 0.1241 0.5931 0.8000\n" +
887         "Ks 0.0490 0.0490 0.0490\n" +
888         "illum 2\n" +
889         "Ns 13.9500\n" +
890         "\n" +
891         "newmtl fldkpurple\n" +
892         "Ka 0.0443 0.0257 0.0776\n" +
893         "Kd 0.1612 0.0000 0.3347\n" +
894         "Ks 0.0000 0.0000 0.0000\n" +
895         "illum 2\n" +
896         "Ns 13.9500\n" +
897         "\n" +
898         "newmtl dkbrown\n" +
899         "Ka 0.0143 0.0062 0.0027\n" +
900         "Kd 0.0087 0.0038 0.0016\n" +
901         "Ks 0.2370 0.2147 0.1821\n" +
902         "illum 3\n" +
903         "Ns 60.0000\n" +
904         "sharpness 60.0000\n" +
905         "\n" +
906         "newmtl bone2\n" +
907         "Ka 0.6408 0.5388 0.3348\n" +
908         "Kd 0.9837 0.8620 0.6504\n" +
909         "illum 1\n" +
910         "\n" +
911         "newmtl bluegrey\n" +
912         "Ka 0.4000 0.4000 0.4000\n" +
913         "Kd 0.1881 0.2786 0.2898\n" +
914         "Ks 0.3000 0.3000 0.3000\n" +
915         "illum 2\n" +
916         "Ns 14.7300\n" +
917         "\n" +
918         "newmtl metal\n" +
919         "Ka 0.9102 0.8956 0.1932\n" +
920         "Kd 0.9000 0.7626 0.4261\n" +
921         "Ks 0.8939 0.8840 0.8683\n" +
922         "illum 2\n" +
923         "Ns 200.0000\n" +
924         "\n" +
925         "newmtl sand_stone\n" +
926         "Ka 0.1299 0.1177 0.0998\n" +
927         "Kd 0.1256 0.1138 0.0965\n" +
928         "Ks 0.2370 0.2147 0.1821\n" +
929         "illum 3\n" +
930         "Ns 60.0000\n" +
931         "sharpness 60.0000\n" +
932         "\n" +
933         "newmtl hair\n" +
934         "Ka 0.0013 0.0012 0.0010\n" +
935         "Kd 0.0008 0.0007 0.0006\n" +
936         "Ks 0.0000 0.0000 0.0000\n" +
937         "illum 3\n" +
938         "Ns 60.0000\n" +
939         "sharpness 60.0000\n", OBJLoader.defaultAppearances, null, null);
940   }
941 }
942 OBJLoader.prototype = Object.create(ModelLoader.prototype);
943 OBJLoader.prototype.constructor = OBJLoader;
944 
945 OBJLoader.defaultAppearances = null;
946 
947 /**
948  * Creates a new scene from the parsed <code>groups</code> and calls onmodelcreated asynchronously or 
949  * returns the created scene if onmodelcreated is null.
950  * <code>groups</code> is empty after method call.
951  * @private
952  */
953 OBJLoader.prototype.createScene = function(vertices, textureCoordinates, normals, groups, appearances, onmodelcreated, onprogression) {
954   var sceneRoot = new Group3D();
955   if (onmodelcreated === null) {
956     onprogression(ModelLoader.BUILDING_MODEL, "", 0);
957     for (var key in groups) {
958       this.createGroupShapes(vertices, textureCoordinates, normals, groups [key], appearances, sceneRoot);
959     }
960     onprogression(ModelLoader.BUILDING_MODEL, "", 1);
961     return sceneRoot;
962   } else {
963     var groupsGeometryCount = 0;
964     for (var key in groups) {
965       groupsGeometryCount += groups [key].geometries.length;
966     }
967     var builtGeometryCount = 0;
968     var loader = this;
969     var sceneBuilder = function() {
970         onprogression(ModelLoader.BUILDING_MODEL, "", groupsGeometryCount !== 0 ? builtGeometryCount / groupsGeometryCount : 0);
971         var start = Date.now();
972         for (var key in groups) {
973           loader.createGroupShapes(vertices, textureCoordinates, normals, groups [key], appearances, sceneRoot);
974           builtGeometryCount += groups [key].geometries.length;
975           delete groups[key];
976           if (builtGeometryCount < groupsGeometryCount 
977               && Date.now() - start > 10) {
978             // Continue shapes creation later
979             setTimeout(sceneBuilder, 0);
980             return;
981           }
982         }
983         // All shapes are created
984         setTimeout(
985             function() {
986               onprogression(ModelLoader.BUILDING_MODEL, "", 1);
987               onmodelcreated(sceneRoot);
988             }, 0);
989       };
990     sceneBuilder();
991   }
992 }
993 
994 /**
995  * Creates the 3D shapes matching the parsed data of a group, and adds them to <code>sceneRoot</code>.
996  * @private
997  */
998 OBJLoader.prototype.createGroupShapes = function(vertices, textureCoordinates, normals, group, appearances, sceneRoot) {
999   var geometries = group.geometries;
1000   if (geometries.length > 0) {
1001     var i = 0;
1002     while (i < geometries.length) {
1003       var firstGeometry = geometries [i];
1004       var firstGeometryHasTextureCoordinateIndices = firstGeometry.textureCoordinateIndices.length > 0;
1005       var firstFaceHasNormalIndices = (firstGeometry instanceof OBJLoader.OBJFace) && firstGeometry.normalIndices.length > 0;
1006       var firstFaceIsSmooth = (firstGeometry instanceof OBJLoader.OBJFace) && firstGeometry.smooth;
1007       
1008       var firstGeometryMaterial = firstGeometry.material;
1009       var appearance = OBJLoader.getAppearance(appearances, firstGeometryMaterial);
1010       // Search how many geometries share the same characteristics 
1011       var max = i;
1012       while (++max < geometries.length) {
1013         var geometry = geometries [max];
1014         var material = geometry.material;
1015         if ((geometry.constructor !== firstGeometry.constructor)
1016             || material === null && firstGeometryMaterial !== null
1017             || material !== null && OBJLoader.getAppearance(appearances, material) !== appearance
1018             || (firstFaceIsSmooth ^ ((geometry instanceof OBJLoader.OBJFace) && geometry.smooth))
1019             || (firstGeometryHasTextureCoordinateIndices ^ geometry.textureCoordinateIndices.length > 0)
1020             || (firstFaceHasNormalIndices ^ ((geometry instanceof OBJLoader.OBJFace) && geometry.normalIndices.length > 0))) {
1021           break;
1022         }
1023       }
1024       
1025       // Clone appearance to avoid sharing it
1026       if (appearance !== null) {
1027         appearance = appearance.clone();
1028       }
1029   
1030       // Create indices arrays for the geometries with an index between i and max
1031       var geometryCount = max - i;
1032       var coordinatesIndices = [];
1033       var stripCounts = []; 
1034       var onlyTriangles = true;
1035       for (var j = 0; j < geometryCount; j++) {
1036         var geometryVertexIndices = geometries [i + j].vertexIndices;
1037         coordinatesIndices.push.apply(coordinatesIndices, geometryVertexIndices);
1038         stripCounts.push(geometryVertexIndices.length);
1039         if (onlyTriangles && geometryVertexIndices.length !== 3) {
1040           onlyTriangles = false;
1041         }
1042       }
1043       var textureCoordinateIndices = [];
1044       if (firstGeometryHasTextureCoordinateIndices) {
1045         for (var j = 0; j < geometryCount; j++) {
1046           textureCoordinateIndices.push.apply(textureCoordinateIndices, geometries [i + j].textureCoordinateIndices);
1047         }
1048       }
1049       
1050       var geometryArray;
1051       if (firstGeometry instanceof OBJLoader.OBJFace) {
1052         var normalIndices = [];
1053         if (firstFaceHasNormalIndices) {
1054           for (var j = 0; j < geometryCount; j++) {
1055             normalIndices.push.apply(normalIndices, geometries [i + j].normalIndices);
1056           }
1057         }
1058         var geometryInfo = new GeometryInfo3D(onlyTriangles  
1059             ? GeometryInfo3D.TRIANGLE_ARRAY  
1060             : GeometryInfo3D.POLYGON_ARRAY);
1061         geometryInfo.setCoordinates(vertices);
1062         geometryInfo.setCoordinateIndices(coordinatesIndices);
1063         geometryInfo.setNormals(normals);
1064         geometryInfo.setNormalIndices(normalIndices);
1065         geometryInfo.setTextureCoordinates(textureCoordinates);
1066         geometryInfo.setTextureCoordinateIndices(textureCoordinateIndices);
1067         geometryInfo.setStripCounts(stripCounts);
1068         if (!firstFaceHasNormalIndices) {
1069           geometryInfo.setCreaseAngle(firstFaceIsSmooth  ? Math.PI / 2  : 0);
1070           geometryInfo.setGeneratedNormals(true);
1071         }
1072         geometryArray = geometryInfo.getIndexedGeometryArray();                  
1073       } else { // Line
1074         var lineCoordinatesIndices = [];
1075         var lineTextureCoordinateIndices = [];
1076         for (var j = 0, index = 0; j < geometryCount; index += stripCounts [j], j++) {
1077           for (var k = 0; k < stripCounts [j] - 1; k++) {
1078             lineCoordinatesIndices.push(coordinatesIndices [index + k]);
1079             lineCoordinatesIndices.push(coordinatesIndices [index + k + 1]);
1080             if (textureCoordinateIndices.length > 0) {
1081               lineTextureCoordinateIndices.push(textureCoordinateIndices [index + k]);
1082               lineTextureCoordinateIndices.push(textureCoordinateIndices [index + k + 1]);
1083             }
1084           }
1085         }
1086         geometryArray = new IndexedLineArray3D(vertices, lineCoordinatesIndices, 
1087             textureCoordinates, lineTextureCoordinateIndices);
1088       }
1089       
1090       var shape = new Shape3D(geometryArray, appearance);   
1091       sceneRoot.addChild(shape);
1092       shape.setName(group.name + (i === 0 ? "" : "_" + i));
1093       i = max;
1094     }
1095   }
1096 }
1097 
1098 /**
1099  * Returns the appearance matching a given material. 
1100  * @private
1101  */
1102 OBJLoader.getAppearance = function(appearances, material) {
1103   var appearance = undefined;
1104   if (material !== null) {
1105     appearance = appearances [material];
1106   }
1107   if (appearance === undefined) {
1108     appearance = OBJLoader.defaultAppearances ["default"];
1109   }
1110   return appearance;
1111 }
1112 
1113 /**
1114  * Parses the given OBJ content and stores the materials it describes in appearances attribute 
1115  * of <code>modelContext</code>.
1116  * @protected
1117  */
1118 OBJLoader.prototype.parseDependencies = function(objContent, objEntryName, zip, modelContext) {
1119   modelContext.appearances = {};
1120   for (var k in OBJLoader.defaultAppearances) {
1121     var appearance = OBJLoader.defaultAppearances [k];
1122     modelContext.appearances [appearance.getName()] = appearance;
1123   }
1124 
1125   try {
1126     var mtllibIndex = objContent.indexOf("mtllib");
1127     while (mtllibIndex !== -1) {
1128       var endOfLine = mtllibIndex + 6;
1129       while (endOfLine < objContent.length
1130           && objContent.charAt(endOfLine) != '\n'
1131             && objContent.charAt(endOfLine) != '\r') {
1132         endOfLine++;
1133       }
1134       var line = objContent.substring(mtllibIndex, endOfLine).trim();
1135       var mtllib = line.substring(7, line.length).trim();
1136       this.parseMaterialEntry(mtllib, modelContext.appearances, objEntryName, zip);
1137       
1138       mtllibIndex = objContent.indexOf("mtllib", endOfLine);
1139     }
1140   } catch (ex) {
1141     modelContext.appearances = {};
1142   }
1143 }
1144 
1145 /**
1146  * Parses the given OBJ content and calls onmodelloaded asynchronously or 
1147  * returns the scene it describes if onmodelloaded is null.
1148  * @protected
1149  */
1150 OBJLoader.prototype.parseEntryScene = function(objContent, objEntryName, zip, modelContext, onmodelloaded, onprogression) {
1151   var vertices = [];
1152   var textureCoordinates = [];
1153   var normals = [];
1154   var defaultGroup = new OBJLoader.OBJGroup("default");
1155   var groups = {"default" : defaultGroup};
1156   var materialGroupsWithNormals = {};
1157   var currentObjects = {group : defaultGroup,
1158                         material : "default", 
1159                         smooth : false}; 
1160   
1161   if (onmodelloaded === null) {
1162     try {
1163       onprogression(ModelLoader.PARSING_MODEL, objEntryName, 0);
1164       for (var startOfLine = 0; startOfLine <= objContent.length; ) {
1165         startOfLine = this.parseObjectLine(objContent, startOfLine, vertices, textureCoordinates, normals, groups,
1166             materialGroupsWithNormals, currentObjects);
1167       } 
1168       onprogression(ModelLoader.PARSING_MODEL, objEntryName, 1);
1169       return this.createScene(vertices, textureCoordinates, normals, groups, modelContext.appearances, null, onprogression);
1170     } catch (ex) {
1171       onprogression(ModelLoader.PARSING_MODEL, objEntryName, 1);
1172       return this.createScene([], [], [], {}, modelContext.appearances, null, onprogression);
1173     }
1174   } else {
1175     var startOfLine = 0;
1176     var loader = this;
1177     var objEntryParser = function() {
1178         try {
1179           onprogression(ModelLoader.PARSING_MODEL, objEntryName, startOfLine / objContent.length);
1180           var minimumIndexBeforeTimeout = startOfLine + 200000; 
1181           var start = Date.now();
1182           while (startOfLine <= objContent.length) {
1183             startOfLine = loader.parseObjectLine(objContent, startOfLine, vertices, textureCoordinates, normals, groups,
1184                 materialGroupsWithNormals, currentObjects);
1185             if (startOfLine <= objContent.length 
1186                 && startOfLine > minimumIndexBeforeTimeout // Don't call Date.now() after parsing each line!
1187                 && Date.now() - start > 10) { 
1188               // Continue entry parsing later
1189               setTimeout(objEntryParser, 0);
1190               return;
1191             }
1192           }
1193           // Parsing is finished
1194           setTimeout(
1195               function() {
1196                 onprogression(ModelLoader.PARSING_MODEL, objEntryName, 1);
1197                 loader.createScene(vertices, textureCoordinates, normals, groups, modelContext.appearances, 
1198                     function(scene) { 
1199                       onmodelloaded(scene); 
1200                     }, 
1201                     onprogression);
1202               }, 0);
1203         } catch (ex) {
1204           onprogression(ModelLoader.PARSING_MODEL, objEntryName, 1);
1205           loader.createScene([], [], [], {}, modelContext.appearances, onmodelloaded, onprogression);
1206         }
1207       };
1208     objEntryParser();
1209   }
1210 }
1211 
1212 /**
1213  * @private
1214  */
1215 OBJLoader.prototype.parseObjectLine = function(objContent, startOfLine, vertices, textureCoordinates, normals, groups,
1216                                                materialGroupsWithNormals, currentObjects) {
1217   var endOfLine = startOfLine + 1;
1218   while (endOfLine < objContent.length
1219          && objContent.charAt(endOfLine) != '\n'
1220          && objContent.charAt(endOfLine) != '\r') {
1221     endOfLine++;
1222   }
1223   var line = objContent.substring(startOfLine, endOfLine);
1224   if (line.indexOf("v") === 0 || line.indexOf("f ") === 0 || line.indexOf("l ") === 0) {
1225     // Append to line next lines if it ends by a back slash
1226     while (line.charAt(line.length - 1) === '\\') {
1227       // Remove back slash
1228       line = line.substring(0, line.length - 1) + " ";
1229       // Read next line
1230       startOfLine = endOfLine + 1;
1231       if (startOfLine < objContent.length
1232           && objContent.charAt(endOfLine) == '\r'
1233           && objContent.charAt(startOfLine) == '\n') {
1234         startOfLine++;
1235       }
1236       endOfLine = startOfLine + 1;
1237       while (endOfLine < objContent.length
1238              && objContent.charAt(endOfLine) != '\n'
1239              && objContent.charAt(endOfLine) != '\r') {
1240         endOfLine++;
1241       }
1242       line += objContent.substring(startOfLine, endOfLine);
1243     }
1244   }
1245   
1246   line = line.trim();
1247   var strings = line.split(/\s+/);
1248   var start = strings [0];
1249   if (start === "v") {
1250     vertices.push(OBJLoader.parseVector3f(strings));
1251   } else if (start === "vt") {
1252     textureCoordinates.push(OBJLoader.parseVector2f(strings));
1253   } else if (start === "vn") {
1254     try {
1255       normals.push(OBJLoader.parseVector3f(strings));
1256     } catch (e) {
1257       console.log(objContent + " " + line)
1258     }
1259   } else if (start === "l") {
1260     var line = this.parseLine(strings, currentObjects.material);
1261     if (line.vertexIndices.length > 1) {
1262       currentObjects.group.addGeometry(line);
1263     }
1264   } else if (start === "f") {
1265     var face = this.parseFace(strings, currentObjects.smooth, currentObjects.material);
1266     if (face.vertexIndices.length > 2) {
1267       if (face.normalIndices.length === 0
1268           || currentObjects.group.name.indexOf("sweethome3d_") === 0) {
1269         currentObjects.group.addGeometry(face);
1270       } else {
1271         // Except for group names starting with sweethome3d_ which faces group mustn't be changed,
1272         // add faces with normals to the group with the same material 
1273         // since there won't be any smooth normal to computes
1274         if (!(face.material in materialGroupsWithNormals)) {
1275           materialGroupsWithNormals [face.material] = currentObjects.group;
1276         }
1277         materialGroupsWithNormals [face.material].addGeometry(face);
1278       }
1279     }
1280   } else if (start === "g" || start === "o") {
1281     if (strings.length > 1) {
1282       var name = strings [1];
1283       currentObjects.group = groups [name];
1284       if (currentObjects.group === undefined) {
1285         currentObjects.group = new OBJLoader.OBJGroup(name);
1286         groups [name] = currentObjects.group;
1287       }        
1288     } else {
1289       currentObjects.group = groups ["default"];
1290     }
1291   } else if (start === "s") {
1292     currentObjects.smooth = strings [1] != "off";
1293   } else if (start === "usemtl") {
1294     currentObjects.material = line.substring(7, line.length).trim();
1295   }
1296   
1297   startOfLine = endOfLine + 1;
1298   if (startOfLine < objContent.length
1299       && objContent.charAt(endOfLine) == '\r'
1300       && objContent.charAt(startOfLine) == '\n') {
1301     startOfLine++;
1302   }
1303   
1304   return startOfLine;
1305 }
1306 
1307 /**
1308  * Returns the object line in strings.
1309  * @private
1310  */
1311 OBJLoader.prototype.parseLine = function(strings, material) {
1312   //    l v       v       v       ...
1313   // or l v/vt    v/vt    v/vt    ...
1314   var vertexIndices = [];
1315   var textureCoordinateIndices = [];
1316   for (var i = 0; i < strings.length; i++) {
1317     var indices = strings [i];
1318     if (i > 0
1319         && indices.length > 0) {
1320       var firstSlashIndex = indices.indexOf('/');
1321       if (firstSlashIndex === -1) {
1322         // l v 
1323         vertexIndices.push(OBJLoader.parseInteger(indices) - 1);
1324       } else {
1325         // l v/vt
1326         vertexIndices.push(OBJLoader.parseInteger(indices.substring(0, firstSlashIndex)) - 1);
1327         textureCoordinateIndices.push(OBJLoader.parseInteger(indices.substring(firstSlashIndex + 1)) - 1);
1328       }
1329     }
1330   }
1331   if (vertexIndices.length !== textureCoordinateIndices.length) {
1332     // Ignore unconsistent texture coordinate 
1333     textureCoordinateIndices = [];
1334   }
1335   return new OBJLoader.OBJLine(vertexIndices, textureCoordinateIndices, material);
1336 }
1337 
1338 /**
1339  * Returns the object face in strings.
1340  * @private
1341  */
1342 OBJLoader.prototype.parseFace = function(strings, smooth, material) {
1343   //    f v       v       v       ...
1344   // or f v//vn   v//vn   v//vn   ...
1345   // or f v/vt    v/vt    v/vt    ...
1346   // or f v/vt/vn v/vt/vn v/vt/vn ...
1347   var vertexIndices = [];
1348   var textureCoordinateIndices = [];
1349   var normalIndices = [];
1350   for (var i = 0; i < strings.length; i++) {
1351     var indices = strings [i];
1352     if (i > 0
1353         && indices.length > 0) {
1354       var firstSlashIndex = indices.indexOf('/');
1355       if (firstSlashIndex === -1) {
1356         // f v 
1357         vertexIndices.push(OBJLoader.parseInteger(indices) - 1);
1358       } else {
1359         vertexIndices.push(OBJLoader.parseInteger(indices.substring(0, firstSlashIndex)) - 1);
1360         var lastSlashIndex = indices.lastIndexOf('/');
1361         if (firstSlashIndex === lastSlashIndex) {
1362           // f v/vt
1363           textureCoordinateIndices.push(OBJLoader.parseInteger(indices.substring(firstSlashIndex + 1)) - 1);
1364         } else {
1365           if (firstSlashIndex + 1 !== lastSlashIndex) {
1366             // f v/vt/vn
1367             textureCoordinateIndices.push(OBJLoader.parseInteger(indices.substring(firstSlashIndex + 1, lastSlashIndex)) - 1);
1368           }
1369           //    f v//vn
1370           // or f v/vt/vn
1371           normalIndices.push(OBJLoader.parseInteger(indices.substring(lastSlashIndex + 1)) - 1);
1372         }
1373       }
1374     }
1375   }
1376   if (vertexIndices.length !== textureCoordinateIndices.length) {
1377     // Ignore unconsistent texture coordinate 
1378     textureCoordinateIndices = [];
1379   }
1380   if (vertexIndices.length !== normalIndices.length) {
1381     // Ignore unconsistent normals
1382     normalIndices = [];
1383   }
1384   return new OBJLoader.OBJFace(vertexIndices, textureCoordinateIndices, normalIndices, smooth, material);
1385 }
1386 
1387 /**
1388  * Parses appearances from the given material entry, then returns true if the given entry exists.
1389  * @private
1390  */
1391 OBJLoader.prototype.parseMaterialEntry = function(mtlEntryName, appearances, objEntryName, zip) {
1392   var lastSlash = objEntryName.lastIndexOf("/");
1393   if (lastSlash >= 0) {
1394     mtlEntryName = objEntryName.substring(0, lastSlash + 1) + mtlEntryName;
1395   }
1396   var mtlEntry = zip.file(mtlEntryName);
1397   if (mtlEntry !== null) {
1398     OBJLoader.parseMaterial(mtlEntry.asBinary(), appearances, objEntryName, zip);
1399   }
1400 }
1401 
1402 /**
1403  * Returns a vector created from the numbers in 2nd to 4th strings.
1404  * @private
1405  */
1406 OBJLoader.parseVector3f = function(strings) {
1407   //     v x y z
1408   // or vn x y z
1409   // or Ka r g b
1410   // or Kd r g b
1411   // or Ks r g b
1412   return vec3.fromValues(OBJLoader.parseNumber(strings [1]), 
1413       OBJLoader.parseNumber(strings [2]),
1414       OBJLoader.parseNumber(strings [3]));
1415 }
1416 
1417 /**
1418  * Returns a vector created from the numbers in 2nd and 3rd strings.
1419  * @private
1420  */
1421 OBJLoader.parseVector2f = function(strings) {
1422   // vt x y z
1423   return vec2.fromValues(OBJLoader.parseNumber(strings [1]), 
1424       OBJLoader.parseNumber(strings [2]));
1425 }
1426 
1427 /**
1428  * Returns the integer contained in the given parameter. 
1429  * @private
1430  */
1431 OBJLoader.parseInteger = function(string) {
1432   var i = parseInt(string);
1433   if (isNaN(i)) {
1434     throw new IncorrectFormat3DException("Incorrect integer " + string);
1435   }
1436   return i;
1437 }
1438 
1439 /**
1440  * Returns the number contained in the given parameter. 
1441  * @private
1442  */
1443 OBJLoader.parseNumber = function(string) {
1444   var x = parseFloat(string);
1445   if (isNaN(x)) {
1446     if (string == "NaN") {
1447       return NaN;
1448     }
1449     throw new IncorrectFormat3DException("Incorrect number " + string);
1450   }
1451   return x;
1452 }
1453 
1454 /**
1455  * Parses a map of appearances from the given content. 
1456  * @private
1457  */
1458 OBJLoader.parseMaterial = function(mtlContent, appearances, objEntryName, zip) {
1459   var currentAppearance = null; 
1460   var lines = mtlContent.match(/^.*$/mg);
1461   for (var i = 0; i < lines.length; i++) {
1462     var line = lines [i].trim();
1463     var strings = line.split(/\s+/);
1464     var start = strings [0];
1465     if (start == "newmtl") {
1466       currentAppearance = new Appearance3D(line.substring(7, line.length).trim());
1467       appearances [currentAppearance.getName()] = currentAppearance;
1468     } else if (currentAppearance !== null) {
1469       if (start == "Ka") {
1470         currentAppearance.setAmbientColor(OBJLoader.parseVector3f(strings));
1471       } else if (start == "Kd") {
1472         currentAppearance.setDiffuseColor(OBJLoader.parseVector3f(strings));
1473       } else if (start == "Ks") {
1474         currentAppearance.setSpecularColor(OBJLoader.parseVector3f(strings));
1475       } else if (start == "Ns") {
1476         currentAppearance.setShininess(Math.max(1, Math.min(OBJLoader.parseNumber(strings [1]), 128)));
1477       } else if (start == "d") {
1478         // Store transparency opposite value
1479         currentAppearance.setTransparency(1 - Math.max(0, OBJLoader.parseNumber(strings [1] == "-halo" ? strings [2] : strings [1])));
1480       } else if (start == "illum") {
1481         currentAppearance.setIllumination(OBJLoader.parseInteger(strings [1]));
1482       } else if (start == "map_Kd") {
1483         var imageEntryName = strings [strings.length - 1];
1484         var lastSlash = objEntryName.lastIndexOf("/");
1485         if (lastSlash >= 0) {
1486           imageEntryName = objEntryName.substring(0, lastSlash + 1) + imageEntryName;
1487         }
1488         var imageEntry = zip.file(imageEntryName);
1489         if (imageEntry === null
1490             && strings.length > 2) {
1491           imageEntryName = line.substring(7, line.length).trim();
1492           if (lastSlash >= 0) {
1493             imageEntryName = objEntryName.substring(0, lastSlash + 1) + imageEntryName;
1494           }
1495           imageEntry = zip.file(imageEntryName);
1496         }
1497         if (imageEntry !== null) {
1498           currentAppearance.imageEntryName = imageEntryName;
1499         }
1500       } 
1501       // Ignore Ni and sharpness
1502     }
1503   }
1504 }
1505 
1506 /**
1507  * Creates a group of geometries read in an OBJ file.
1508  * @constructor
1509  * @private
1510  */
1511 OBJLoader.OBJGroup = function(name) {
1512   this.name = name;
1513   this.geometries = [];
1514 }
1515 
1516 OBJLoader.OBJGroup.prototype.addGeometry = function(geometry) {
1517   this.geometries.push(geometry);
1518 };
1519 
1520 /**
1521  * Creates a line read in an OBJ file.
1522  * @constructor
1523  * @private
1524  */
1525 OBJLoader.OBJLine = function(vertexIndices, textureCoordinateIndices, material) {
1526   this.vertexIndices = vertexIndices;
1527   this.textureCoordinateIndices = textureCoordinateIndices;
1528   this.material = material;
1529 }
1530 
1531 /**
1532  * Creates a face read in an OBJ file.
1533  * @constructor
1534  * @private
1535  */
1536 OBJLoader.OBJFace = function(vertexIndices, textureCoordinateIndices, normalIndices, smooth, material) {
1537   this.vertexIndices = vertexIndices;
1538   this.textureCoordinateIndices = textureCoordinateIndices;
1539   this.normalIndices = normalIndices;
1540   this.smooth = smooth;
1541   this.material = material;
1542 }
1543