View difference between Paste ID: 3vMjDgkf and QcyMmhqq
SHOW: | | - or go back to the newest paste.
1
package com.ginkage.postcard;
2
3
import java.io.BufferedInputStream;
4
import java.io.File;
5
import java.io.FileInputStream;
6
import java.io.FileNotFoundException;
7
import java.io.IOException;
8
import java.util.ArrayList;
9
10
import android.opengl.Matrix;
11
import android.os.Environment;
12
13
public class Load3DS {
14
	private final int CHUNK_MAIN     = 0x4D4D;
15
	private final int CHUNK_OBJMESH  = 0x3D3D;
16
	private final int CHUNK_OBJBLOCK = 0x4000;
17
	private final int CHUNK_TRIMESH  = 0x4100;
18
	private final int CHUNK_VERTLIST = 0x4110;
19
	private final int CHUNK_FACELIST = 0x4120;
20
	private final int CHUNK_FACEMAT  = 0x4130;
21
	private final int CHUNK_MAPLIST  = 0x4140;
22
	private final int CHUNK_SMOOTHG  = 0x4150;
23
	private final int CHUNK_TRMATRIX = 0x4160;
24
	private final int CHUNK_LIGHT    = 0x4600;
25
	private final int CHUNK_SPOTL    = 0x4610;
26
	private final int CHUNK_ONOFF    = 0x4620;
27
	private final int CHUNK_CAMERA   = 0x4700;
28
	private final int CHUNK_RGBC     = 0x0010;
29
	private final int CHUNK_RGB24    = 0x0011;
30
	private final int CHUNK_SHORT    = 0x0030;
31
	private final int CHUNK_BACKCOL  = 0x1200;
32
	private final int CHUNK_AMB      = 0x2100;
33
	private final int CHUNK_MATERIAL = 0xAFFF;
34
	private final int CHUNK_MATNAME  = 0xA000;
35
	private final int CHUNK_AMBIENT  = 0xA010;
36
	private final int CHUNK_DIFFUSE  = 0xA020;
37
	private final int CHUNK_SPECULAR = 0xA030;
38
	private final int CHUNK_SHININES = 0xA040;
39
	private final int CHUNK_SHINSTRN = 0xA041;
40
	private final int CHUNK_TRANSP   = 0xA050;
41
	private final int CHUNK_SELFILL  = 0xA084;
42
	private final int CHUNK_MTLTYPE  = 0xA100;
43
	private final int CHUNK_TEXTURE  = 0xA200;
44
	private final int CHUNK_REFLMAP  = 0xA220;
45
	private final int CHUNK_BUMPMAP  = 0xA230;
46
	private final int CHUNK_MAPFILE  = 0xA300;
47
	private final int CHUNK_MAPPARAM = 0xA351;
48
	private final int CHUNK_KEYFRAMER = 0xB000;
49
	private final int CHUNK_TRACKINFO = 0xB002;
50
	private final int CHUNK_SPOTINFO  = 0xB007;
51
	private final int CHUNK_FRAMES    = 0xB008;
52
	private final int CHUNK_OBJNAME   = 0xB010;
53
	private final int CHUNK_PIVOT     = 0xB013;
54
	private final int CHUNK_TRACKPOS  = 0xB020;
55
	private final int CHUNK_TRACKROT  = 0xB021;
56
	private final int CHUNK_TRACKSCL  = 0xB022;
57
	private final int CHUNK_HIERARCHY = 0xB030;
58
59
	private BufferedInputStream file;
60
	private byte[] bytes = new byte[8];
61
	private long filePos;
62
63
	public Scene3D Load(String fileName)
64
	{
65
		file = null;
66
		Scene3D scene = null; 
67
		File dir = new File(Environment.getExternalStorageDirectory().getAbsolutePath());
68
		File fil = new File(dir.getAbsolutePath() + File.separator + fileName);
69
		if (!fil.exists()) return scene;
70
71
		try {
72
			filePos = 0;
73
			file = new BufferedInputStream(new FileInputStream(fil));
74
			scene = ProcessFile(fil.length());
75
		} catch (FileNotFoundException e) {
76
			e.printStackTrace();
77
		} catch (IOException e) {
78
			e.printStackTrace();
79
		}
80
81
		try {
82
			if (file != null)
83
				file.close();
84
		} catch (IOException e) {
85
			e.printStackTrace();
86
		}
87
88
		return scene;
89
	}
90
91
	private void Skip(long count) throws IOException
92
	{
93
		file.skip(count);
94
		filePos += count;
95
	}
96
97
	private void Seek(long end) throws IOException
98
	{
99
		if (filePos < end) {
100
			Skip(end - filePos);
101
			filePos = end;
102
		}
103
	}
104
105
	private byte ReadByte() throws IOException
106
	{
107
		file.read(bytes, 0, 1);
108
		filePos++;
109
		return bytes[0];
110
	}
111
112
	private int ReadUnsignedByte() throws IOException
113
	{
114
		file.read(bytes, 0, 1);
115
		filePos++;
116
		return (bytes[0]&0xff);
117
	}
118
119
	private int ReadUnsignedShort() throws IOException
120
	{
121
		file.read(bytes, 0, 2);
122
		filePos += 2;
123
		return ((bytes[1]&0xff) << 8 | (bytes[0]&0xff));
124
	}
125
126
	private int ReadInt() throws IOException
127
	{
128
		file.read(bytes, 0, 4);
129
		filePos += 4;
130
		return (bytes[3]) << 24 | (bytes[2]&0xff) << 16 | (bytes[1]&0xff) <<  8 | (bytes[0]&0xff);
131
	}
132
133
	private float ReadFloat() throws IOException
134
	{
135
		return Float.intBitsToFloat(ReadInt());
136
	}
137
138
	private Scene3D ProcessFile(long fileLen) throws IOException
139
	{
140
		Scene3D scene = null;
141
142
		while (filePos < fileLen) {
143
			int chunkID = ReadUnsignedShort();
144
			int chunkLen = ReadInt() - 6;
145
146
			switch (chunkID) {
147
			case CHUNK_MAIN:
148
				if (scene == null)
149
					scene = ChunkMain(chunkLen);
150
				else
151
					Skip(chunkLen);
152
				break;
153
154
			default:
155
				Skip(chunkLen);
156
			}
157
		}
158
159
		return scene;
160
	}
161
162
	private Scene3D ChunkMain(int len) throws IOException
163
	{
164
		Scene3D scene = new Scene3D();
165
		scene.materials = new ArrayList<Material3D>();
166
		scene.objects = new ArrayList<Object3D>();
167
		scene.lights = new ArrayList<Light3D>();
168
		scene.animations = new ArrayList<Animation>();
169
170
		long end = filePos + len;
171
		while (filePos < end) {
172
			int chunkID = ReadUnsignedShort();
173
			int chunkLen = ReadInt() - 6;
174
175
			switch (chunkID) {
176
			case CHUNK_OBJMESH:
177
				Chunk3DEditor(scene, chunkLen);
178
				break;
179
180
			case CHUNK_KEYFRAMER:
181
				ChunkKeyframer(scene, chunkLen);
182
				break;
183
184
			case CHUNK_BACKCOL:
185
				scene.background = new float[4];
186
				ChunkColor(chunkLen, scene.background);
187
				break;
188
189
			case CHUNK_AMB:
190
				scene.ambient = new float[4];
191
				ChunkColor(chunkLen, scene.ambient);
192
				break;
193
194
			default:
195
				Skip(chunkLen);
196
			}
197
		}
198
		Seek(end);
199
200
		scene.Compute(0);
201
202
		return scene;
203
	}
204
205
	private void Chunk3DEditor(Scene3D scene, int len) throws IOException
206
	{
207
		long end = filePos + len;
208
		while (filePos < end) {
209
			int chunkID = ReadUnsignedShort();
210
			int chunkLen = ReadInt() - 6;
211
212
			switch (chunkID) {
213
			case CHUNK_OBJBLOCK:
214
				ChunkObject(scene, chunkLen);
215
				break;
216
217
			case CHUNK_MATERIAL:
218
				Material3D mat = ChunkMaterial(scene, chunkLen);
219
				if (mat != null)
220
					scene.materials.add(mat);
221
				break;
222
223
			default:
224
				Skip(chunkLen);
225
			}
226
		}
227
		Seek(end);
228
	}
229
230
	private void ChunkObject(Scene3D scene, int len) throws IOException
231
	{
232
		long end = filePos + len;
233
234
		if (len == 0) return;
235
		String name = ChunkName(0);
236
237
		while (filePos < end) {
238
			int chunkID = ReadUnsignedShort();
239
			int chunkLen = ReadInt() - 6;
240
241
			switch (chunkID) {
242
			case CHUNK_TRIMESH:
243
				Object3D obj = ChunkTrimesh(chunkLen, name, scene);
244
				if (obj != null)
245
					scene.objects.add(obj);
246
				break;
247
248
			case CHUNK_LIGHT:
249
				Light3D light = ChunkLight(chunkLen, name);
250
				if (light != null)
251
					scene.lights.add(light);
252
				break;
253
254
			case CHUNK_CAMERA:
255
			default:
256
				Skip(chunkLen);
257
			}
258
		}
259
		Seek(end);
260
	}
261
262
	private Object3D ChunkTrimesh(int len, String name, Scene3D scene) throws IOException
263
	{
264
		long end = filePos + len;
265
266
		Object3D obj = new Object3D();
267
		obj.name = name;
268
		obj.faceMats = new ArrayList<FaceMat>();
269
		obj.indCount = 0;
270
271
		int i, k, num;
272
273
		while (filePos < end) {
274
			int chunkID = ReadUnsignedShort();
275
			int chunkLen = ReadInt() - 6;
276
277
			switch (chunkID) {
278
			case CHUNK_FACELIST:
279
				ChunkFaceList(chunkLen, obj, scene);
280
				break;
281
282
			case CHUNK_MAPLIST:
283
				num = ReadUnsignedShort();
284
				for (i = 0, k = 6; i < num; i++, k += 8) {
285
					obj.vertexBuffer[k + 0] = ReadFloat();
286
					obj.vertexBuffer[k + 1] = 1 - ReadFloat();
287
				}
288
				break;
289
290
			case CHUNK_VERTLIST:
291
				num = ReadUnsignedShort();
292
				obj.vertCount = num;
293
				obj.vertexBuffer = new float[8*num];
294
				for (i = 0, k = 0; i < num; i++, k += 8) {
295
					ChunkVector(obj.vertexBuffer, k);
296
					obj.vertexBuffer[k + 3] = 0;
297
					obj.vertexBuffer[k + 4] = 0;
298
					obj.vertexBuffer[k + 5] = 0;
299
					obj.vertexBuffer[k + 6] = 0;
300
					obj.vertexBuffer[k + 7] = 0;
301
				}
302
				break;
303
304
			case CHUNK_TRMATRIX:
305
				float[] localCoord = new float[16];
306
				ChunkVector(localCoord, 4*0);
307
				ChunkVector(localCoord, 4*2);
308
				ChunkVector(localCoord, 4*1);
309
				ChunkVector(localCoord, 4*3);
310
				localCoord[3] = localCoord[7] = localCoord[11] = 0;
311
				localCoord[15] = 1;
312
313
				obj.trMatrix = new float[16];
314
				Matrix.invertM(obj.trMatrix, 0, localCoord, 0);
315
				break;
316
317
			default:
318
				Skip(chunkLen);
319
			}
320
		}
321
		Seek(end);
322
323
		return obj;
324
	}
325
326
	private static void CrossProduct(float[] res, float[] v1, float[] v2)
327
	{
328
		res[0] = v1[1]*v2[2] - v1[2]*v2[1];
329
		res[1] = v1[2]*v2[0] - v1[0]*v2[2];
330
		res[2] = v1[0]*v2[1] - v1[1]*v2[0];
331
	}
332
333
	private static float DotSquare(float[] v, int offset)
334
	{
335
		return v[offset + 0]*v[offset + 0] + v[offset + 1]*v[offset + 1] + v[offset + 2]*v[offset + 2];
336
	}
337
338
	private static void VecSubstract(float[] res, float[] v, int offset1, int offset2)
339
	{
340
		res[0] = v[offset1 + 0] - v[offset2 + 0];
341
		res[1] = v[offset1 + 1] - v[offset2 + 1];
342
		res[2] = v[offset1 + 2] - v[offset2 + 2];
343-
	private static void VecAdd(float[] v, int offset, float[] a)
343+
344
345-
		v[offset + 0] += a[0];
345+
	private static void VecAdd(float[] v, int offset, float[] a, int off)
346-
		v[offset + 1] += a[1];
346+
347-
		v[offset + 2] += a[2];
347+
		v[offset + 0] += a[off + 0];
348
		v[offset + 1] += a[off + 1];
349
		v[offset + 2] += a[off + 2];
350
	}
351
352
	private void VecNormalize(float[] v, int offset)
353
	{
354
		double nlen = 1 / Math.sqrt(DotSquare(v, offset));
355
		v[offset + 0] *= nlen;
356
		v[offset + 1] *= nlen;
357
		v[offset + 2] *= nlen;
358
	}
359
360
	private void ChunkFaceList(int len, Object3D obj, Scene3D scene) throws IOException
361
	{
362-
		int i, j, k, l, num = ReadUnsignedShort(), unused = num, idx;
362+
363
364
		int i, j, k, l, m, t, num = ReadUnsignedShort(), unused = num, idx;
365-
		int[] faceBuffer = new int[3*num];
365+
366-
		boolean[] faceUsed = new boolean[num];
366+
367
		int[] faceBuffer = new int[3*faceCount];
368
		boolean[] faceUsed = new boolean[faceCount];
369
		float[] v = new float[3]; 
370
		float[] v1 = new float[3]; 
371-
		for (i = 0, idx = 0; i < num; i++, idx += 3) {
371+
372
373
		float[] vgNorm = new float[3*3*faceCount]; // per-vertex normal for each face
374
		int[] vertGroup = new int[3*faceCount]; // per-vertex group for each face
375
		boolean[] vgUsed = new boolean[3*faceCount]; // per-vertex "group used" bit for each face
376
		int[] vgNum = new int[obj.vertCount + 1]; // per-vertex face count, and then offset
377
		int[] faceGroup = new int[faceCount]; // per-face smoothing group
378
379
		int[] vgUniqs = new int[obj.vertCount + 1]; // per-vertex unique groups count
380
		int[] vertUGroup = new int[3*faceCount]; // per-vertex unique groups list for each face
381
382-
			j *= 8;
382+
		for (i = 0; i <= obj.vertCount; i++)
383-
			k *= 8;
383+
			vgNum[i] = vgUniqs[i] = 0;
384-
			l *= 8;
384+
385-
			VecSubstract(v1, obj.vertexBuffer, l, j);
385+
		for (i = 0, idx = 0; i < faceCount; i++, idx += 3) {
386-
			VecSubstract(v2, obj.vertexBuffer, k, j);
386+
387
			k = ReadUnsignedShort();
388
			l = ReadUnsignedShort();
389-
			VecAdd(obj.vertexBuffer, j + 3, v);
389+
390-
			VecAdd(obj.vertexBuffer, k + 3, v);
390+
391-
			VecAdd(obj.vertexBuffer, l + 3, v);
391+
392
			faceBuffer[idx + 0] = j;
393
			faceBuffer[idx + 2] = k;
394
			faceBuffer[idx + 1] = l;
395
396
			// initialize smoothing groups data
397-
		v = null;
397+
			faceGroup[i] = 0;
398-
		v1 = null;
398+
399-
		v2 = null;
399+
			for (t = 0; t < 9; t++)
400
				vgNorm[i * 9 + t] = 0;
401
402
			for (t = 0; t < 3; t++) {
403
				vertGroup[idx + t] = 0;
404
				vertUGroup[idx + t] = 0;
405
				vgUsed[idx + t] = false;
406
			}
407
408
			vgNum[j]++;
409
			vgNum[k]++;
410
			vgNum[l]++;
411-
				mat.indexBuffer = new short[3*num];
411+
412
413
		int a, sum = 0;
414
		for (i = 0; i < obj.vertCount; i++) {
415
			a = vgNum[i];
416
			vgNum[i] = sum;
417
			sum += a;
418
		}
419
		vgNum[obj.vertCount] = sum; // now we can store all the faces and their normals and groups per-vertex
420
421
		for (i = 0, idx = 0; i < faceCount; i++, idx += 3) {
422-
					mat.indexBuffer[k++] = (short) faceBuffer[j + 0];
422+
			j = faceBuffer[idx + 0];
423-
					mat.indexBuffer[k++] = (short) faceBuffer[j + 1];
423+
			k = faceBuffer[idx + 2];
424-
					mat.indexBuffer[k++] = (short) faceBuffer[j + 2];
424+
			l = faceBuffer[idx + 1];
425
426
			VecSubstract(v1, obj.vertexBuffer, l*8, j*8);
427
			VecSubstract(v2, obj.vertexBuffer, k*8, j*8);
428
			CrossProduct(v, v1, v2);
429
430
			VecAdd(vgNorm, vgNum[j]*3, v, 0);
431
			VecAdd(vgNorm, vgNum[k]*3, v, 0);
432
			VecAdd(vgNorm, vgNum[l]*3, v, 0);
433
			
434
			vgNum[j]++;
435
			vgNum[k]++;
436
			vgNum[l]++;
437
		}
438-
			mat.indexBuffer = new short[3*unused];
438+
439
		for (i = obj.vertCount - 1; i > 0; i--) // offsets were shifted, so shift them back
440
			vgNum[i] = vgNum[i - 1];
441
		vgNum[0] = 0;
442
443
		boolean gotSmoothGroups = false;
444
445
		while (filePos < end) {
446-
					mat.indexBuffer[k++] = (short) faceBuffer[j + 0];
446+
447-
					mat.indexBuffer[k++] = (short) faceBuffer[j + 1];
447+
448-
					mat.indexBuffer[k++] = (short) faceBuffer[j + 2];
448+
449
			switch (chunkID) {
450
			case CHUNK_FACEMAT:
451
				FaceMat mat = new FaceMat();
452
				String name = ChunkName(0);
453-
		faceBuffer = null;
453+
454-
		faceUsed = null;
454+
455
				mat.indCount = num;
456
				mat.indexBuffer = new int[3*num];
457
				mat.bufOffset = obj.indCount;
458
				obj.indCount += 3*num;
459
				k = 0;
460
				for (i = 0; i < num; i++) {
461
					j = ReadUnsignedShort();
462
					if (!faceUsed[j]) {
463
						faceUsed[j] = true;
464
						unused--;
465
					}
466
					j *= 3;
467
					for (t = 0; t < 3; t++)
468
						mat.indexBuffer[k++] = j;
469
				}
470
				obj.faceMats.add(mat);
471
				break;
472
473
			case CHUNK_SMOOTHG:
474
				for (i = 0, idx = 0; i < faceCount; i++, idx += 3) {
475
					faceGroup[i] = ReadInt();
476
					
477
					for (t = 0; t < 3; t++) {
478
						j = faceBuffer[idx + t];
479
						vertGroup[vgNum[j]] = faceGroup[i];
480
						vgNum[j]++;
481
					}
482
				}
483
				for (i = obj.vertCount - 1; i > 0; i--)
484
					vgNum[i] = vgNum[i - 1];
485
				vgNum[0] = 0;
486
				gotSmoothGroups = true;
487
				break;
488
489
			default:
490
				Skip(chunkLen);
491
			}
492
		}
493
		Seek(end);
494
495
		int newVertCount = 0, g;
496
497
		if (gotSmoothGroups) {
498
			for (i = 0; i < obj.vertCount; i++) {
499
				for (m = vgNum[i]; m < vgNum[i + 1]; m++) { // for every normal and face of this vertex
500
					if (!vgUsed[m]) {
501
						// vertGroup[m] is a new group...
502
						vertUGroup[vgNum[i] + vgUniqs[i]] = vertGroup[m];
503
						vgUniqs[i]++;
504
						newVertCount++;
505
					}
506
	
507
					for (t = m; t < vgNum[i + 1]; t++) // mark all equal groups (including this one) as duplicates
508
						if (vertGroup[t] == vertGroup[m])
509
							vgUsed[t] = true;
510
				}
511
			}
512
513
			if (newVertCount == obj.vertCount)
514
				gotSmoothGroups = false;
515
		}
516
517
		if (gotSmoothGroups) {
518
			// reindex all vertices, build new normals
519
			int newIndex = 0;
520
			int[] vertIndex = new int[faceCount*3]; // new vertex indices
521
			float[] newVertexBuffer = new float[newVertCount*8];
522
523
			for (i = 0, idx = 0; i < faceCount; i++, idx += 3)
524
				for (t = 0; t < 3; t++)
525
					vertIndex[idx + t] = 0;
526
527
			idx = 3;
528
			for (i = 0; i < obj.vertCount; i++) {
529
				for (t = 0; t < vgUniqs[i]; t++) { // for every unique normal and face of this vertex
530
					for (m = 0; m < 8; m++)
531
						newVertexBuffer[newIndex*8 + m] = obj.vertexBuffer[i*8 + m]; // duplicate all vertex data (including zero normals) 
532
533
					g = vertUGroup[vgNum[i] + t]; // unique group mask for this vertex
534
					for (m = vgNum[i]; m < vgNum[i + 1]; m++) // for every NON-unique normal and face of this vertex
535
						if ((vertGroup[m] & g) != 0 || vertGroup[m] == g) // also works for zero group
536
							VecAdd(newVertexBuffer, idx, vgNorm, m*3); // add normal to vertex
537
	
538
					vertIndex[vgNum[i] + t] = newIndex;
539
					newIndex++;
540
					idx += 8;
541
				}
542
			}
543
544
			int fg, vi;
545
			for (i = 0, idx = 0; i < faceCount; i++, idx += 3) {
546
				fg = faceGroup[i];
547
				for (m = 0; m < 3; m++) {
548
					vi = faceBuffer[idx + m]; // face vertex
549
					for (t = 0; t < vgUniqs[vi]; t++) // for every unique group of this vertex
550
						if (fg == vertUGroup[vgNum[vi] + t]) { // found the right one
551
							faceBuffer[idx + m] = vertIndex[vgNum[vi] + t];
552
							break;
553
						}
554
				}
555
			}
556
557
			obj.vertCount = newVertCount;
558
			obj.vertexBuffer = newVertexBuffer;
559
		}
560
		else // nothing changed, no need to recalculate anything
561
			for (i = 0, idx = 3; i < obj.vertCount; i++, idx += 8) // just copy all the normals
562
				for (m = vgNum[i]; m < vgNum[i + 1]; m++) // for every NON-unique normal and face of this vertex
563
					VecAdd(obj.vertexBuffer, idx, vgNorm, m*3); // add normal to vertex
564
565
		for (i = 0, k = 3; i < obj.vertCount; i++, k += 8)
566
			VecNormalize(obj.vertexBuffer, k);
567
568
		for (m = 0; m < obj.faceMats.size(); m++) {
569
			FaceMat mat = obj.faceMats.get(m);
570
			k = 0;
571
			for (i = 0; i < mat.indCount; i++)
572
				for (t = 0; t < 3; t++) {
573
					j = mat.indexBuffer[k];
574
					mat.indexBuffer[k++] = faceBuffer[j + t];
575
				}
576
		}
577
578
		if (unused > 0) {
579
			FaceMat mat = new FaceMat();
580
			mat.indexBuffer = new int[3*unused];
581
			mat.bufOffset = obj.indCount;
582
			obj.indCount += 3*unused;
583
			k = 0;
584
			for (i = 0; i < faceCount; i++)
585
				if (!faceUsed[i]) {
586
					faceUsed[i] = true;
587
					j = i * 3;
588
					for (t = 0; t < 3; t++)
589
						mat.indexBuffer[k++] = faceBuffer[j + t];
590
				}
591
			obj.faceMats.add(mat);
592
		}
593
	}
594
595
	private Light3D ChunkLight(int len, String name) throws IOException
596
	{
597
		long end = filePos + len;
598
599
		Light3D light = new Light3D();
600
		light.name = name;
601
		light.pos = new float[3];
602
		ChunkVector(light.pos, 0);
603
604
		while (filePos < end) {
605
			int chunkID = ReadUnsignedShort();
606
			int chunkLen = ReadInt() - 6;
607
608
			switch (chunkID) {
609
			case CHUNK_RGBC:
610
				light.color = new float[4];
611
				ChunkRGBC(light.color);
612
				break;
613
614
			case CHUNK_RGB24:
615
				light.color = new float[4];
616
				ChunkRGB24(light.color);
617
				break;
618
619
			case CHUNK_SPOTL:
620
				light.dir = new float[4];
621
				ChunkVector(light.dir, 0);
622
				light.theta = (float) (ReadFloat() * Math.PI / 180.0f);
623
				light.phi = (float) (ReadFloat() * Math.PI / 180.0f);
624
				break;
625
626
			case CHUNK_ONOFF:
627
			default:
628
				Skip(chunkLen);
629
			}
630
		}
631
		Seek(end);
632
633
		return light;
634
	}
635
636
	private Material3D ChunkMaterial(Scene3D scene, int len) throws IOException
637
	{
638
		long end = filePos + len;
639
640
		Material3D mat = new Material3D();
641
642
		while (filePos < end) {
643
			int chunkID = ReadUnsignedShort();
644
			int chunkLen = ReadInt() - 6;
645
646
			switch (chunkID) {
647
			case CHUNK_TEXTURE:
648
				mat.texture = ChunkMap(chunkLen);
649
				break;
650
651
			case CHUNK_BUMPMAP:
652
			case CHUNK_REFLMAP:
653
				ChunkMap(chunkLen);
654
				break;
655
656
			case CHUNK_AMBIENT:
657
				mat.ambient = new float[4];
658
				ChunkColor(chunkLen, mat.ambient);
659
				break;
660
661
			case CHUNK_DIFFUSE:
662
				mat.diffuse = new float[4];
663
				ChunkColor(chunkLen, mat.diffuse);
664
				break;
665
666
			case CHUNK_SPECULAR:
667
				mat.specular = new float[4];
668
				ChunkColor(chunkLen, mat.specular);
669
				break;
670
671
			case CHUNK_MATNAME:
672
				mat.name = ChunkName(chunkLen);
673
				break;
674
675
			case CHUNK_MTLTYPE:
676
				mat.type = ReadUnsignedShort();
677
				break;
678
679
			case CHUNK_SHININES:
680
				mat.shininess = 100 - ChunkPercent(chunkLen);
681
				break;
682
683
			case CHUNK_SHINSTRN:
684
				mat.shinStren = ChunkPercent(chunkLen);
685
				break;
686
687
			case CHUNK_TRANSP:
688
				mat.transparency = ChunkPercent(chunkLen);
689
				break;
690
691
			case CHUNK_SELFILL:
692
				mat.selfIllum = ChunkPercent(chunkLen);
693
				break;
694
695
			default:
696
				Skip(chunkLen);
697
			}
698
		}
699
		Seek(end);
700
701
		return mat;
702
	}
703
704
	private String ChunkMap(int len) throws IOException
705
	{
706
		long end = filePos + len;
707
708
		String name = null;
709
710
		while (filePos < end) {
711
			int chunkID = ReadUnsignedShort();
712
			int chunkLen = ReadInt() - 6;
713
714
			switch (chunkID) {
715
			case CHUNK_MAPFILE:
716
				name = ChunkName(chunkLen);
717
				break;
718
719
			case CHUNK_MAPPARAM:
720
			default:
721
				Skip(chunkLen);
722
			}
723
		}
724
		Seek(end);
725
726
		return name;
727
	}
728
729
	private void ChunkKeyframer(Scene3D scene, int len) throws IOException
730
	{
731
		int fstart = 0, fend = 100;
732
733
		long end = filePos + len;
734
		while (filePos < end) {
735
			int chunkID = ReadUnsignedShort();
736
			int chunkLen = ReadInt() - 6;
737
738
			switch (chunkID) {
739
			case CHUNK_FRAMES:
740
				fstart = ReadInt();
741
				fend = ReadInt();
742
				break;
743
744
			case CHUNK_TRACKINFO:
745
				Animation anim = ChunkMeshTrack(chunkLen, scene);
746
				if (anim != null)
747
					scene.animations.add(anim);
748
				break;
749
750
			case CHUNK_SPOTINFO:
751
			default:
752
				Skip(chunkLen);
753
			}
754
		}
755
756
		if (fstart < fend)
757
			for (int i = 0; i < scene.animations.size(); i++) {
758
				Animation anim = scene.animations.get(i);
759
				if (anim.position != null)
760
					for (int j = 0; j < anim.position.length; j++)
761
						anim.position[j].time = (anim.position[j].time - fstart) / (fend - fstart);
762
				if (anim.rotation != null)
763
					for (int j = 0; j < anim.rotation.length; j++)
764
						anim.rotation[j].time = (anim.rotation[j].time - fstart) / (fend - fstart);
765
				if (anim.scaling != null)
766
					for (int j = 0; j < anim.scaling.length; j++)
767
						anim.scaling[j].time = (anim.scaling[j].time - fstart) / (fend - fstart);
768
			}
769
770
		Seek(end);
771
	}
772
773
	private Animation ChunkMeshTrack(int len, Scene3D scene) throws IOException
774
	{
775
		Animation anim = new Animation();
776
		int num, i, j, k;
777
778
		anim.result = new float[16];
779
		Matrix.setIdentityM(anim.result, 0);
780
781
		anim.world = new float[16];
782
		Matrix.setIdentityM(anim.world, 0);
783
784
		long end = filePos + len;
785
		while (filePos < end) {
786
			int chunkID = ReadUnsignedShort();
787
			int chunkLen = ReadInt() - 6;
788
789
			switch (chunkID) {
790
			case CHUNK_HIERARCHY:
791
				anim.id = ReadUnsignedShort();
792
				break;
793
794
			case CHUNK_OBJNAME:
795
				String name = ChunkName(0);
796
				anim.light = scene.FindLight(name);
797
				anim.object = scene.FindObject(name);
798
				Skip(4);
799
				anim.parent = scene.FindAnimation(ReadUnsignedShort());
800
				break;
801
802
			case CHUNK_PIVOT:
803
				anim.pivot = new float[3];
804
				ChunkVector(anim.pivot, 0);
805
				break;
806
807
			case CHUNK_TRACKPOS:
808
				Skip(10);
809
				num = ReadInt();
810
				anim.position = new AnimKey[num];
811
				for (i = 0; i < num; i++) {
812
					anim.position[i] = new AnimKey();
813
					anim.position[i].time = ReadInt();
814
					k = ReadUnsignedShort();
815
					for (j = 0; j < 5; j++)
816
						if ((k & (1 << j)) != 0)
817
							Skip(4);
818
					anim.position[i].data = new float[3];
819
					ChunkVector(anim.position[i].data, 0);
820
				}
821
				break;
822
823
			case CHUNK_TRACKROT:
824
				Skip(10);
825
				num = ReadInt();
826
				anim.rotation = new AnimKey[num];
827
				for (i = 0; i < num; i++) {
828
					anim.rotation[i] = new AnimKey();
829
					anim.rotation[i].time = ReadInt();
830
					k = ReadUnsignedShort();
831
					for (j = 0; j < 5; j++)
832
						if ((k & (1 << j)) != 0)
833
							Skip(4);
834
					anim.rotation[i].data = new float[4];
835
					anim.rotation[i].data[3] = ReadFloat();
836
					ChunkVector(anim.rotation[i].data, 0);
837
				}
838
				break;
839
840
			case CHUNK_TRACKSCL:
841
				Skip(10);
842
				num = ReadInt();
843
				anim.scaling = new AnimKey[num];
844
				for (i = 0; i < num; i++) {
845
					anim.scaling[i] = new AnimKey();
846
					anim.scaling[i].time = ReadInt();
847
					k = ReadUnsignedShort();
848
					for (j = 0; j < 5; j++)
849
						if ((k & (1 << j)) != 0)
850
							Skip(4);
851
					anim.scaling[i].data = new float[3];
852
					ChunkVector(anim.scaling[i].data, 0);
853
				}
854
				break;
855
856
			default:
857
				Skip(chunkLen);
858
			}
859
		}
860
		Seek(end);
861
862
		return anim;
863
	}
864
865
	private void ChunkColor(int len, float[] color) throws IOException
866
	{
867
		long end = filePos + len;
868
		while (filePos < end) {
869
			int chunkID = ReadUnsignedShort();
870
			int chunkLen = ReadInt() - 6;
871
872
			switch (chunkID) {
873
			case CHUNK_RGBC:
874
				ChunkRGBC(color);
875
				break;
876
877
			case CHUNK_RGB24:
878
				ChunkRGB24(color);
879
				break;
880
881
			default:
882
				Skip(chunkLen);
883
			}
884
		}
885
		Seek(end);
886
	}
887
888
	private float ChunkPercent(int len) throws IOException
889
	{
890
		float v = 0;
891
892
		long end = filePos + len;
893
		while (filePos < end) {
894
			int chunkID = ReadUnsignedShort();
895
			int chunkLen = ReadInt() - 6;
896
897
			switch (chunkID) {
898
			case CHUNK_SHORT:
899
				v = ReadUnsignedShort() / 100.0f;
900
				break;
901
902
			default:
903
				Skip(chunkLen);
904
			}
905
		}
906
		Seek(end);
907
908
		return v;
909
	}
910
911
	private String ChunkName(int len) throws IOException
912
	{
913
		long end = filePos + len;
914
		int slen = 0;
915
		byte[] buffer = new byte[128];
916
		byte c;
917
918
		do {
919
			c = ReadByte();
920
			if (c != 0)
921
				buffer[slen++] = c;
922
		} while (c != 0);
923
924
		if (len != 0)
925
			Seek(end);
926
927
		String name = new String(buffer, 0, slen);
928
929
		return name;
930
	}
931
932
	private void ChunkVector(float[] vec, int offset) throws IOException
933
	{
934
		vec[offset + 0] = ReadFloat();
935
		vec[offset + 2] = ReadFloat();
936
		vec[offset + 1] = ReadFloat();
937
	}
938
939
	private void ChunkRGBC(float[] c) throws IOException
940
	{
941
		c[0] = ReadFloat();
942
		c[1] = ReadFloat();
943
		c[2] = ReadFloat();
944
		c[3] = 1;
945
	}
946
947
	private void ChunkRGB24(float[] c) throws IOException
948
	{
949
		c[0] = ReadUnsignedByte() / 255.0f;
950
		c[1] = ReadUnsignedByte() / 255.0f;
951
		c[2] = ReadUnsignedByte() / 255.0f;
952
		c[3] = 1;
953
	}
954
}