Subversion Repositories AndroidProjects

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1051 chris 1
package com.gebauz.bauzoid.graphics.model;
2
 
3
import java.io.IOException;
4
 
5
import com.badlogic.gdx.Gdx;
6
import com.badlogic.gdx.files.FileHandle;
7
import com.badlogic.gdx.graphics.Texture;
8
import com.gebauz.bauzoid.app.Consts;
9
import com.gebauz.bauzoid.file.File;
10
import com.gebauz.bauzoid.graphics.Graphics;
11
import com.gebauz.bauzoid.graphics.model.Mesh;
12
import com.gebauz.bauzoid.graphics.renderstates.RenderStatesConfiguration;
13
import com.gebauz.bauzoid.graphics.shader.Effect;
14
import com.gebauz.bauzoid.graphics.shader.ShaderProgram;
15
import com.gebauz.bauzoid.graphics.shader.ShaderUtil;
16
import com.gebauz.bauzoid.graphics.shader.Technique;
17
import com.gebauz.bauzoid.math.BoundingBox;
18
import com.gebauz.bauzoid.math.Matrix4;
19
import com.gebauz.bauzoid.math.Vector2;
20
import com.gebauz.bauzoid.math.Vector3;
21
import com.gebauz.bauzoid.math.Vector4;
22
 
23
public class ModelUtil
24
{
25
        public static final String LOG_TAG = Consts.LOG_TAG + ":ModelUtil";
26
        public static boolean verbose = false;
27
 
28
        private ModelUtil() {}
29
 
30
        public static void log(String tag, String msg)
31
        {
32
                if (verbose)
33
                        Gdx.app.log(tag, msg);
34
        }
35
 
36
        public static Model createModelFromFile(Graphics graphics, FileHandle fileHandle) throws IOException
37
        {
38
                File file = new File(fileHandle.read());
39
                Model model = loadModel(graphics, file);
40
                file.close();
41
                return model;
42
        }
43
 
44
 
45
        public static Model loadModel(Graphics graphics, File modelFile) throws IOException
46
        {
47
                log(LOG_TAG, "Loading file");
48
 
49
                byte byteOrdering = modelFile.readByte();
50
                log(LOG_TAG, "Byte Ordering: " + (byteOrdering == 0 ? "Little Endian" : "Big Endian"));
51
 
52
                if (byteOrdering == 0)
53
                        modelFile.setEndianness(File.Endianness.LITTLE_ENDIAN);
54
                else
55
                        modelFile.setEndianness(File.Endianness.BIG_ENDIAN);
56
 
57
                String header = modelFile.readString(8);
58
                log(LOG_TAG, "Header: " + header);
59
 
60
                if (header.compareTo("SUXMODEL") != 0)
61
                        throw new IOException("Header incorrect, must be 'SUXMODEL': " + header);
62
 
63
                int version = modelFile.readInt();
64
                log(LOG_TAG, "Version: " + version);
65
                if (version != 0)
66
                        throw new IOException("Version incorrect, must be 0: " + version);
67
 
68
                String name = modelFile.readString();
69
                log(LOG_TAG, "Name: " + name);
70
 
71
                int meshCount = modelFile.readInt();
72
                log(LOG_TAG, "Mesh Count: " + meshCount);
73
 
74
                Model model = new Model(name);
75
 
76
                Mesh[] meshes = new Mesh[meshCount];
77
 
78
                for (int i = 0; i < meshCount; i++)
79
                {
80
                        String meshName = modelFile.readString();
81
                        log(LOG_TAG, "[" + i + "] Mesh Name: " + meshName);
82
 
83
                        meshes[i] = new Mesh(graphics, meshName);
84
 
85
                        BoundingBox box = new BoundingBox();
86
 
87
                        box.min = modelFile.readVector3();
88
                        log(LOG_TAG, "[" + i + "] BB Min: " + box.min.x + ", " + box.min.y + ", " + box.min.z);
89
 
90
                        box.max = modelFile.readVector3();
91
                        log(LOG_TAG, "[" + i + "] BB Max: " + box.max.x + ", " + box.max.y + ", " + box.max.z);
92
 
93
                        meshes[i].setBoundingBox(box);                 
94
 
95
                        boolean dynamic = modelFile.readBool();
96
                        log(LOG_TAG, "[" + i + "] Dynamic: " + (dynamic ? "yes" : "no"));
97
 
98
                        boolean deleteSource = modelFile.readBool();
99
                        log(LOG_TAG, "[" + i + "] DeleteSource: " + (deleteSource ? "yes" : "no"));
100
 
101
                        // geometry
102
                        Geometry geometry = loadGeometry(graphics, modelFile);
103
 
104
                        meshes[i].setGeometry(geometry);
105
 
106
                        // bones - currently ignored
107
                        int boneCount = modelFile.readInt();
108
                        for (int bone = 0; bone < boneCount; bone++)
109
                        {
110
                                String boneName = modelFile.readString();
111
                                Matrix4 boneTransform = modelFile.readMatrix4();
112
                                Matrix4 boneTransformInverse = modelFile.readMatrix4();
113
                                Vector3 bonePosition = modelFile.readVector3();
114
                                // TODO: Quaternion
115
                                Vector4 boneOrientation = modelFile.readVector4();
116
                                Vector3 boneScale = modelFile.readVector3();
117
                        }
118
                        for (int bone = 0; bone < boneCount; bone++)
119
                        {
120
                                int parentBoneIndex = modelFile.readInt();
121
                        }
122
 
123
                        // groups
124
                        int groupCount = modelFile.readInt();
125
                        log(LOG_TAG, "Group Count: " + groupCount);
126
 
127
                        MeshGroup[] groups = new MeshGroup[groupCount];
128
 
129
                        for (int group = 0; group < groupCount; group++)
130
                        {
131
                                groups[group] = loadMeshGroup(meshes[i], modelFile);                           
132
                        }
133
 
134
                        meshes[i].setGroups(groups);
135
 
136
                        break;                         
137
                }
138
 
139
                model.setMeshes(meshes);
140
 
141
                log(LOG_TAG, "Loading finished");
142
 
143
                return model;
144
        }
145
 
146
        public static Geometry loadGeometry(Graphics graphics, File modelFile) throws IOException
147
        {
148
                log(LOG_TAG, "=== Geometry Begin ===");
149
 
150
                int version = modelFile.readInt();
151
                log(LOG_TAG, "Geometry Version: " + version);
152
                if (version != 2)
153
                        throw new IOException("Geometry Version incorrect, must be 2: " + version);
154
 
155
                int primitiveType = modelFile.readInt();
156
                switch (primitiveType)
157
                {
158
                case 0:
159
                        log(LOG_TAG, "PrimitiveType: Triangles");
160
                        break;
161
                case 1:
162
                        log(LOG_TAG, "PrimitiveType: Triangle Strip");
163
                        break;
164
                case 2:
165
                        log(LOG_TAG, "PrimitiveType: Lines");
166
                        break;
167
                case 3:
168
                        log(LOG_TAG, "PrimitiveType: Line Strip");
169
                        break;
170
                case 4:
171
                        log(LOG_TAG, "PrimitiveType: Points");
172
                        break;
173
                }
174
 
175
                int vertexCount = modelFile.readInt();
176
                log(LOG_TAG, "Vertex Count: " + vertexCount);
177
 
178
                int indexCount = modelFile.readInt();
179
                log(LOG_TAG, "Index Count: " + indexCount);
180
 
181
                int streamCount = modelFile.readInt();
182
                log(LOG_TAG, "Stream Count: " + streamCount);
183
 
184
                VertexStream[] streams = new VertexStream[streamCount];
185
 
186
                for (int i = 0; i < streamCount; i++)
187
                {
188
                        log(LOG_TAG, "=== BEGIN VertexStream [" + i + "] ===");
189
                        streams[i] = loadVertexStream(graphics, modelFile);
190
                        log(LOG_TAG, "=== END VertexStream [" + i + "] ===");
191
                }
192
 
193
                boolean hasIndexStream = modelFile.readBool();
194
                log(LOG_TAG, "Has Index Stream" + (hasIndexStream ? "Yes" : "No"));
195
 
196
                IndexStream indexStream = null;
197
                if (hasIndexStream)
198
                {
199
                        log(LOG_TAG, "=== BEGIN IndexStream ===");
200
                        indexStream = loadIndexStream(graphics, modelFile);
201
                        log(LOG_TAG, "=== END IndexStream ===");
202
                }
203
 
204
                Geometry geometry = new Geometry(graphics, Geometry.PrimitiveType.values()[primitiveType], vertexCount, indexCount);
205
                geometry.setVertexStreams(streams);    
206
                geometry.setIndexStream(indexStream);
207
 
208
                log(LOG_TAG, "=== Geometry End ===");
209
 
210
                return geometry;
211
        }
212
 
213
        public static VertexStream loadVertexStream(Graphics graphics, File modelFile) throws IOException
214
        {              
215
                VertexStream.StreamType streamType = (modelFile.readInt() == 1) ? VertexStream.StreamType.INDIVIDUAL : VertexStream.StreamType.INTERLEAVED;
216
                log(LOG_TAG, "Stream Type: " + streamType.toString());
217
 
218
                String streamName = modelFile.readString();
219
                log(LOG_TAG, "Stream Name: " + streamName);
220
 
221
                int streamVertexCount = modelFile.readInt();
222
                log(LOG_TAG, "Stream Vertex Count: " + streamVertexCount);
223
 
224
                int coordsPerElement = modelFile.readInt();
225
                log(LOG_TAG, "Coords per Element: " + coordsPerElement);
226
 
227
                int bytesPerElement = modelFile.readInt();
228
                log(LOG_TAG, "Bytes per Element: " + bytesPerElement);
229
 
230
                // Stream Begin
231
                byte[] streamBuffer = loadStream(modelFile);
232
                // Stream End           
233
 
234
                VertexAttribute[] attribs = null;
235
 
236
                if (streamType == VertexStream.StreamType.INDIVIDUAL)
237
                {
238
                        attribs = new VertexAttribute[1];
239
                        attribs[0] = loadAttribute(modelFile);
240
                }
241
                else if (streamType == VertexStream.StreamType.INTERLEAVED)
242
                {
243
                        int vertexAttribCount = modelFile.readInt();
244
                        log(LOG_TAG, "Vertex Attrib Count: " + vertexAttribCount);
245
 
246
                        attribs = new VertexAttribute[vertexAttribCount];
247
 
248
                        for (int attrib = 0; attrib < vertexAttribCount; attrib++)
249
                        {
250
                                log(LOG_TAG, "=== BEGIN VertexAttribute [" + attrib + "] ===");
251
                                // Vertex Attribute
252
                                attribs[attrib] = loadAttribute(modelFile);
253
                                log(LOG_TAG, "=== END VertexAttribute [" + attrib + "] ===");
254
                        }
255
                }
256
                else
257
                {
258
                        throw new IOException();
259
                }
260
 
261
                VertexStream stream = new VertexStream(graphics, streamType, streamName, attribs, false);
262
                stream.setData(streamBuffer, coordsPerElement, bytesPerElement);
263
 
264
                return stream;
265
        }
266
 
267
        public static VertexAttribute loadAttribute(File modelFile) throws IOException
268
        {
269
                int suxType = modelFile.readInt();                                     
270
                log(LOG_TAG, "Attrib Data Type: " + suxType);
271
 
272
                if (!VertexAttribute.isValidType(suxType))
273
                        throw new IOException("Invalid vertex attribute type: " + suxType);
274
 
275
                int dataFormat = modelFile.readInt();
276
                log(LOG_TAG, "Attrib Data Format: " + dataFormat);
277
 
278
                if (dataFormat != 0)
279
                        throw new IOException("Unsupported Data Format (must be 0 / Float): " + dataFormat);
280
 
281
                int coordsPerElement = modelFile.readInt();
282
                log(LOG_TAG, "Attrib Coords Per Element: " + coordsPerElement);
283
 
284
                int byteOffset = modelFile.readInt();
285
                log(LOG_TAG, "Attrib Byte Offset: " + byteOffset);
286
 
287
                VertexAttribute attrib = new VertexAttribute(VertexAttribute.toInternalType(suxType), coordsPerElement, byteOffset);
288
 
289
                return attrib;
290
        }
291
 
292
        public static IndexStream loadIndexStream(Graphics graphics, File modelFile) throws IOException
293
        {
294
                int indexCount = modelFile.readInt();
295
                log(LOG_TAG, "Index Count: " + indexCount);
296
 
297
                int dataFormat = modelFile.readInt();
298
                log(LOG_TAG, "Data Format: " + dataFormat);
299
                if (dataFormat != 1)
300
                        throw new IOException("Data Format unsupported, must be 1 (Int16): " + dataFormat);
301
 
302
                // Stream Begin
303
                byte[] streamBuffer = loadStream(modelFile);
304
                // Stream End
305
 
306
                IndexStream indexStream = new IndexStream(graphics);
307
                indexStream.setData(streamBuffer);
308
 
309
                return indexStream;
310
        }
311
 
312
        public static byte[] loadStream(File modelFile) throws IOException
313
        {
314
                // Store endianness because the stream might change it
315
                File.Endianness endianness = modelFile.getEndianness();
316
 
317
                byte byteOrdering = modelFile.readByte();
318
                log(LOG_TAG, "Stream Byte Ordering: " + (byteOrdering == 0 ? "Little Endian" : "Big Endian"));
319
 
320
                if (byteOrdering == 0)
321
                        modelFile.setEndianness(File.Endianness.LITTLE_ENDIAN);
322
                else
323
                        modelFile.setEndianness(File.Endianness.BIG_ENDIAN);
324
 
325
                byte charFormat = modelFile.readByte();
326
                log(LOG_TAG, "Stream CharFormat: " + (charFormat == 0 ? "ASCII" : "UTF16"));
327
 
328
                int byteLength = modelFile.readInt();
329
                log(LOG_TAG, "Stream Byte Length: " + byteLength);
330
 
331
                byte[] buffer = new byte[byteLength];
332
                modelFile.readFully(buffer, 0, byteLength);
333
 
334
                // Restore endianness
335
                modelFile.setEndianness(endianness);
336
 
337
                return buffer;
338
        }
339
 
340
        public static MeshGroup loadMeshGroup(Mesh mesh, File modelFile) throws IOException
341
        {
342
                log(LOG_TAG, "=== Mesh.Group Begin ===");
343
 
344
                String name = modelFile.readString();
345
                log(LOG_TAG, "Name: " + name);
346
 
347
                String effectFile = modelFile.readString();
348
                log(LOG_TAG, "-- Effect file: " + effectFile);
349
 
350
                MeshGroup group = new MeshGroup(mesh, name);
351
 
352
                // TODO: do actual parsing of external technique
353
                Effect effect = new Effect(mesh.getGraphics(), effectFile);
354
 
355
                ShaderProgram shader = ShaderUtil.createShaderFromFile(mesh.getGraphics(), Gdx.files.internal("data/shaders/default.vs"), Gdx.files.internal("data/shaders/default.fs"));
356
                Technique[] effectTechs = new Technique[1];
357
                effectTechs[0] = new Technique(effect, "diffuse", shader);
358
 
359
                effect.setTechniques(effectTechs);
360
 
361
                int techniqueCount = modelFile.readInt();
362
                log(LOG_TAG, "-- Technique Count: " + techniqueCount);
363
 
364
                MeshGroup.TechniqueRef[] techRefs = new MeshGroup.TechniqueRef[techniqueCount];
365
 
366
                for (int i = 0; i < techniqueCount; i++)
367
                {
368
                        String staticTechniqueName = modelFile.readString();
369
                        log(LOG_TAG, "-- Static Technique: " + staticTechniqueName);
370
                        String skinnedTechniqueName = modelFile.readString();
371
                        log(LOG_TAG, "-- Skinned Technique: " + skinnedTechniqueName);
372
 
373
                        // TODO: read technique from parsed effect file
374
                        techRefs[i] = group.new TechniqueRef(effectTechs[0], effectTechs[0]);
375
                }
376
 
377
                group.setTechniqueRefs(techRefs);
378
 
379
                String bonesParameterName = modelFile.readString();
380
 
381
                int firstIndex = modelFile.readInt();
382
                log(LOG_TAG, "First Index: " + firstIndex);
383
                int lastIndex = modelFile.readInt();
384
                log(LOG_TAG, "Last Index: " + lastIndex);
385
 
386
                int firstVertex = modelFile.readInt();
387
                log(LOG_TAG, "First Vertex: " + firstVertex);
388
                int lastVertex = modelFile.readInt();
389
                log(LOG_TAG, "Last Vertex: " + lastVertex);
390
 
391
                group.setIndices(firstIndex, lastIndex, firstVertex, lastVertex);
392
 
393
                // TODO: make render state class
394
                RenderStatesConfiguration rs = loadRenderState(mesh.getGraphics(), modelFile);
395
 
396
                group.setRenderStates(rs);
397
 
398
                int variablesCount = modelFile.readInt();
399
                log(LOG_TAG, "== Variable Count: " + variablesCount);
400
 
401
                Effect.Variable[] vars = new Effect.Variable[variablesCount];
402
 
403
                for (int i = 0; i < variablesCount; i++)
404
                {
405
                        String variableName = modelFile.readString();
406
 
407
                        log(LOG_TAG, "-- [" + i + "] Variable Name: " + variableName);
408
 
409
                        int variableType = modelFile.readInt();
410
 
411
                        switch (variableType)
412
                        {
413
                        case Effect.Variable.TYPE_TEXTURE2D: // Texture2D
414
                        case Effect.Variable.TYPE_TEXTURE3D: // Texture3D
415
                        case Effect.Variable.TYPE_TEXTURECUBE: // TextureCube
416
                                {
417
                                        String texFile = modelFile.readString();
418
                                        log(LOG_TAG, "-- [" + i + "] Variable Type: Texture (" + texFile + ")");
419
 
420
                                        Texture texture = new Texture(Gdx.files.internal("data/" + texFile));
421
                                        vars[i] = effect.new VariableTexture(variableName, texture);
422
 
423
                                        break;
424
                                }
425
                        case Effect.Variable.TYPE_BOOL: // Bool
426
                                {
427
                                        boolean b = modelFile.readBool();
428
                                        log(LOG_TAG, "-- [" + i + "] Variable Type: bool (" + (b ? "true" : "false") + ")");
429
 
430
                                        vars[i] = effect.new VariableBool(variableName, b);
431
 
432
                                        break;
433
                                }
434
                        case Effect.Variable.TYPE_INT: // Int
435
                                {
436
                                        int n = modelFile.readInt();
437
                                        log(LOG_TAG, "-- [" + i + "] Variable Type: int (" + n + ")");
438
 
439
                                        vars[i] = effect.new VariableInt(variableName, n);
440
 
441
                                        break;
442
                                }
443
                        case Effect.Variable.TYPE_FLOAT: // Float
444
                                {
445
                                        float f = modelFile.readFloat();
446
                                        log(LOG_TAG, "-- [" + i + "] Variable Type: float (" + f + ")");
447
 
448
                                        vars[i] = effect.new VariableFloat(variableName, f);
449
 
450
                                        break;
451
                                }
452
                        case Effect.Variable.TYPE_VECTOR2: // Vector2
453
                                {
454
                                        Vector2 v2 = modelFile.readVector2();
455
                                        log(LOG_TAG, "-- [" + i + "] Variable Type: Vector2 (" + v2.x + ", " + v2.y + ")");
456
 
457
                                        vars[i] = effect.new VariableVector2(variableName, v2);
458
 
459
                                        break;
460
                                }
461
                        case Effect.Variable.TYPE_VECTOR3: // Vector3
462
                                {
463
                                        Vector3 v3 = modelFile.readVector3();
464
                                        log(LOG_TAG, "-- [" + i + "] Variable Type: Vector3 (" + v3.x + ", " + v3.y + ", " + v3.z + ")");
465
 
466
                                        vars[i] = effect.new VariableVector3(variableName, v3);
467
 
468
                                        break;
469
                                }
470
                        case Effect.Variable.TYPE_VECTOR4: // Vector4
471
                                {
472
                                        Vector4 v4 = modelFile.readVector4();
473
                                        log(LOG_TAG, "-- [" + i + "] Variable Type: Vector3 (" + v4.x + ", " + v4.y + ", " + v4.z + ", " + v4.w + ")");
474
 
475
                                        vars[i] = effect.new VariableVector4(variableName, v4);
476
 
477
                                        break;
478
                                }
479
                        }
480
                }
481
 
482
                effect.setVariables(vars);
483
                effect.connectVariables();
484
                group.setEffect(effect);
485
 
486
                // Bone Reindexing for tighter usage of uniforms
487
                int boneIndexCount = modelFile.readInt();
488
                for (int i = 0; i < boneIndexCount; i++)
489
                {
490
                        int originalIndex = modelFile.readInt();
491
                        int newIndex = modelFile.readInt();
492
                }
493
 
494
                log(LOG_TAG, "=== Mesh.Group End===");
495
                return group;
496
        }
497
 
498
        @SuppressWarnings("unused")
499
        public static RenderStatesConfiguration loadRenderState(Graphics graphics, File modelFile) throws IOException
500
        {
501
                RenderStatesConfiguration rs = new RenderStatesConfiguration(graphics);
502
 
503
                // Alpha test not supported
504
                boolean alphaTestEnabled = modelFile.readBool();
505
                float alphaTestThreshold = modelFile.readFloat();
506
                int alphaTestComparisonFunc = modelFile.readInt();
507
 
508
                boolean blendingEnabled = modelFile.readBool();
509
                int blendingMode = modelFile.readInt();
510
 
511
                rs.setBlendingEnabled(blendingEnabled);
512
                rs.setBlendingMode(blendingMode);
513
 
514
                boolean cullingEnabled = modelFile.readBool();
515
                int cullingWinding = modelFile.readInt();
516
 
517
                rs.setCullingEnabled(cullingEnabled);
518
                rs.setCullingWinding(cullingWinding);
519
 
520
                boolean depthTestEnabled = modelFile.readBool();
521
                boolean depthBufferWriteEnabled = modelFile.readBool();
522
                int depthTestComparisonFunc = modelFile.readInt();
523
 
524
                rs.setDepthTestEnabled(depthTestEnabled);
525
                rs.setDepthBufferWriteEnabled(depthBufferWriteEnabled);
526
                rs.setDepthTestComparisonFunc(depthTestComparisonFunc);
527
 
528
                // Polygon Mode not supported
529
                int polygonMode = modelFile.readInt();
530
 
531
                return rs;
532
        }
533
}
534
 
535
 
536