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