MD5模型的解析器

虽然之前看了相关MD5的文件结构资料,自认为实现解析应该没问题,嘛~毕竟是停留在理论, 还是动手来实践一下。
之后看到某同学写了个MD5的解析Demo,于是研究了下发现,这实际上是个 Away3D移植出来的精简解析程序,只支持单个Mesh. 近期把它改进成支持多个Mesh吧!

网络上有很多人都发过MD5的文件格式讲解,嘛~我觉得按自己的思路整理一下也发一次。 just for myself;

numJoints 110 //总关节数
numMeshes 4 //总网格数

joints {} 关节节点,其数据顺序为
关节名称,关节父级ID,关节位置,关节旋转

关节在对无动画的纯md5Mesh模型来说没有意义,渲染时无需用到。

mesh {} 网格节点,其数据顺序为
vert token
顶点索引,顶点纹理U,顶点纹理V,顶点初始权重,顶点最大权重

tri token
三角形索引,构成三角形对应的三个顶点的索引

weight token
权重索引,权重对应的关节索引,权重值,权重位置

如果有多个网格,就是以上数据的重复。

之后是精简版解析器的源码,附带的中文的注释。 这几天看看把它扩展成多网格的版本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
package C3.MD5
{
    import flash.events.Event;
    import flash.events.EventDispatcher;
    import flash.geom.Vector3D;
    import flash.utils.ByteArray;

    public class MD5MeshParser extends EventDispatcher
    {
        public function MD5MeshParser()
        {
            _rotationQuat = new Quaternion();
            _rotationQuat.fromAxisAngle(Vector3D.X_AXIS, -Math.PI * .5);
        }
       
        public function load(data : ByteArray) : void
        {
            _textData = data.readUTFBytes(data.bytesAvailable);
            handleData();
        }
       
        private function handleData() : void
        {
            var token : String;
            while(true){
                token = getNextToken();
                switch(token)
                {
                    case COMMENT_TOKEN:
                        ignoreLine();
                        break;
                    case VERSION_TOKEN:
                        _version = getNextInt();
                        if(_version != 10) throw new Error("版本错误,支持的是10");
                        break;
                    case COMMAND_LINE_TOKEN:
                        parseCMD();
                        break;
                    case NUM_JOINTS_TOKEN:
                        num_joints = getNextInt();
                        md5_joint = new Vector.<MD5Joint>();
                        break;
                    case NUM_MESHES_TOKEN:
                        num_meshes = getNextInt();
                        md5_mesh = new MeshData();
                        break;
                    case JOINTS_TOKEN:
                        parseJoints();
                        break
                    case MESH_TOKEN:
                        parseMesh();
                        break;
                    default:
                        if(!_reachedEOF)
                            throw new Error("关键词错误");
                }
               
                if(_reachedEOF){
                    calculateMaxJointCount();
                    this.dispatchEvent(new Event(Event.COMPLETE));
                    break;
                }
            }
        }
       
        /**
         * 计算总关节数
         */

        private function calculateMaxJointCount() : void
        {
            maxJointCount = 0;
           
            var vertexData : Vector.<MD5Vertex> = md5_mesh.md5_vertex;
            var numVerts : int = vertexData.length;
           
            for(var j : int = 0; j < numVerts; ++j)
            {
                var zeroWeights : int = countZeroWeightJoints(vertexData[j], md5_mesh.md5_weight);
                var totalJoints : int = vertexData[j].weight_count - zeroWeights; //忽略0权重的关节
                if(totalJoints > maxJointCount)
                    maxJointCount = totalJoints;
            }
        }
       
        /**
         * 计算0权重关节
         */

        private function countZeroWeightJoints(vertex : MD5Vertex, weights : Vector.<MD5Weight>) : int
        {
            var start : int = vertex.weight_index;
            var end : int = vertex.weight_index + vertex.weight_count;
            var count : int = 0;
            var weight : Number;
           
            for(var i : int = start; i < end; ++i){
                weight = weights[i].bias;
                if(weight == 0) ++count;
            }
           
            return count;
        }
       
        /**
         * 解析网格几何体
         */

        private function parseMesh() : void
        {
            var token : String = getNextToken();
            var ch : String;
           
            if(token != "{") throw new Error("关键字错误");
           
            _shaders ||= new Vector.<String>();
           
            while(ch != "}") {
                ch = getNextToken();
                switch(ch){
                    case COMMENT_TOKEN:
                        ignoreLine();
                        break;
                    case MESH_SHADER_TOKEN:
                        _shaders.push(parseLiteralString());
                        break;
                    case MESH_NUM_VERTS_TOKEN:
                        md5_mesh.num_verts = getNextInt();
                        md5_mesh.md5_vertex = new Vector.<MD5Vertex>();
                        break;
                    case MESH_NUM_TRIS_TOKEN:
                        md5_mesh.num_tris = getNextInt();
                        md5_mesh.md5_triangle = new Vector.<MD5Triangle>();
                        break;
                    case MESH_VERT_TOKEN:
                        parseVertex(md5_mesh.md5_vertex);
                        break;
                    case MESH_TRI_TOKEN:
                        parseTri(md5_mesh.md5_triangle);
                        break;
                    case MESH_WEIGHT_TOKEN:
                        parseWeight(md5_mesh.md5_weight)
                        break;
                }
            }
        }
       
        /**
         * 从数据流中读取下一个关节权重
         */

        private function parseWeight(weights : Vector.<MD5Weight>) : void
        {
            var weight : MD5Weight = new MD5Weight();
            weight.id = getNextInt();
            weight.jointID = getNextInt();
            weight.bias = getNextNumber();
            weight.pos = parseVector3D();
            weights.push(weight);
        }
       
        /**
         * 从数据流中读取下一个三角形,并存入列表中
         */

        private function parseTri(indices : Vector.<MD5Triangle>) : void
        {
            var tri : MD5Triangle = new MD5Triangle();
            tri.id = getNextInt();
            tri.indexVec = new Vector.<uint>();
            tri.indexVec.push(getNextInt());
            tri.indexVec.push(getNextInt());
            tri.indexVec.push(getNextInt());
           
            indices.push(tri);
        }
       
        /**
         * 从数据流中取出下一个顶点,并存入列表中
         */

        private function parseVertex(vertexData : Vector.<MD5Vertex>) : void
        {
            var vertex : MD5Vertex = new MD5Vertex();
            vertex.id = getNextInt();
            parseUV(vertex);
            vertex.weight_index = getNextInt();
            vertex.weight_count = getNextInt();
           
            vertexData.push(vertex);
        }
       
        /**
         * 从数据流中读取uv坐标,并存入顶点数据中
         */

        private function parseUV(vertexData : MD5Vertex) : void
        {
            var ch : String = getNextToken();
            if(ch != "(") throw new Error("解析错误,错误的(");
            vertexData.uv_x = getNextNumber();
            vertexData.uv_y = getNextNumber();
           
            if(getNextToken() != ")") throw new Error("解析错误,错误的)");
        }
       
        /**
         * 解析关节
         */

        private function parseJoints() : void
        {
            var ch : String;
            var i : int = 0;
            var token : String = getNextToken();
            var joint : MD5Joint;
           
            if(token != "{") throw new Error("关键字错误");
            do{
                if(_reachedEOF) throw new Error("到达文件尾");
               
                joint = new MD5Joint();
                joint.name = parseLiteralString();
                joint.parentIndex = getNextInt();
                var pos : Vector3D = parseVector3D();
//              pos = _rotationQuat.rotatePoint(pos);
                var quat : Quaternion = parseQuaternion();
                joint.bindPose = quat.toMatrix3D();
                joint.bindPose.appendTranslation(pos.x,pos.y,pos.z);
                joint.inverseBindPose = joint.bindPose.clone();
                joint.inverseBindPose.invert();
               
//              var m1 : Matrix3D = joint.bindPose;
//              var m2 : Matrix3D = joint.inverseBindPose;
//             
//              m1.append(m2);
//              m2.transpose();
               
                md5_joint.push(joint);
               
                ch = getNextChar();
               
                if(ch == "/"){
                    putBack();
                    ch = getNextToken();
                    if(ch == COMMENT_TOKEN) ignoreLine();
                    ch = getNextChar();
                }
                if(ch != "}") putBack();
            }while(ch != "}");
        }
       
        /**
         * 从数据流中解析下一个四元数
         */

        private function parseQuaternion() : Quaternion
        {
            var quat : Quaternion = new Quaternion();
            var ch : String = getNextToken();
           
            if(ch != "(") throw new Error("解析错误,错误的(");
            quat.x = getNextNumber();
            quat.y = getNextNumber();
            quat.z = getNextNumber();
           
            //四元数需要一个单位长度
            var t : Number = 1 - quat.x * quat.x - quat.y * quat.y - quat.z * quat.z;
            quat.w = t < 0 ? 0 : - Math.sqrt(t);
           
            if (getNextToken() != ")") throw new Error("解析错误,错误的)");
           
            return quat;
        }
       
        /**
         * 获取Vector3d
         */

        private function parseVector3D() : Vector3D
        {
            var vec : Vector3D = new Vector3D();
            var ch : String = getNextToken();
           
            if(ch != "(") throw new Error("解析错误,错误的(");
            vec.x = getNextNumber();
            vec.y = getNextNumber();
            vec.z = getNextNumber();
           
            if(getNextToken() != ")") throw new Error("解析错误,错误的)");
           
            return vec;
        }
       
        /**
         * 从数据流中解析下一个浮点型数值
         */

        private function getNextNumber() : Number
        {
            var f : Number = parseFloat(getNextToken());
            if(isNaN(f)) throw new Error("float type");
            return f;
        }
       
        /**
         * 解析指令行数据
         */

        private function parseCMD() : void
        {
            //仅仅忽略指令行属性
            parseLiteralString();
        }
       
        /**
         * 解析数据流中的逐字符串,一个逐字符是一个序列字符,被两个引号包围
         */

        private function parseLiteralString() : String
        {
            skipWhiteSpace();
           
            var ch : String = getNextChar();
            var str : String = "";
           
            if(ch != """) throw new Error("引号解析错误");
            do{
                if(_reachedEOF) throw new Error("
到达文件结尾");
                ch = getNextChar();
                if(ch != "
"") str += ch;
            }while(ch != """);
           
            return str;
        }
       
        private function getNextToken() : String
        {
            var ch : String;
            var token : String = "
";
           
            while(!_reachedEOF){
                ch = getNextChar();
                if(ch == "
" || ch == "r" || ch == "n" || ch == "t"){
                    //如果不为注释,跳过
                    if(token != COMMENT_TOKEN)
                        skipWhiteSpace();
                    //如果不为空白, 返回
                    if(token != "
")
                        return token;
                }else token += ch;
               
                if(token == COMMENT_TOKEN) return token;
            }
           
            return token;
        }
       
        /**
         * 读出数据流中的下一个整型
         */
        private function getNextInt() : int
        {
            var i : Number = parseInt(getNextToken());
            if(isNaN(i)) throw new Error("
解析错误 int type");
            return i;
        }
       
        /**
         * 跳过下一行
         */
        private function ignoreLine() : void
        {
            var ch : String;
            while(!_reachedEOF && ch != "
n")
                ch = getNextChar();
        }
       
        /**
         * 跳过数据流中的空白
         */
        private function skipWhiteSpace() : void
        {
            var ch : String;
            do {
                ch = getNextChar();
            }while(ch == "
n" || ch == " " || ch == "r" || ch == "t");
           
            putBack();
        }
       
        /**
         * 将最后读出的字符放回数据流
         */
        private function putBack() : void
        {
            _parseIndex--;
            _chatLineIndex--;
            _reachedEOF = _parseIndex >= _textData.length;
        }
       
        /**
         * 从数据流中读取下一个字符
         */
        private function getNextChar() : String
        {
            var ch : String = _textData.charAt(_parseIndex++);
           
            //如果遇到换行符
            if(ch == "
n"){
                ++_line;
                _chatLineIndex = 0;
            }
            //如果遇到回车符
            else if(ch != "
r"){
                ++_chatLineIndex;
            }
           
            if(_parseIndex >= _textData.length)
                _reachedEOF = true;
           
            return ch;
        }
       
        public var num_joints : int;
        public var md5_joint : Vector.<MD5Joint>;
        public var num_meshes : int;
        public var md5_mesh : MeshData;
        public var maxJointCount : int;
       
       
        private var _textData : String;
        private var _reachedEOF : Boolean;
        private var _parseIndex : int = 0;
        private var _line : int = 0;
        private var _chatLineIndex : int = 0;
        private var _version : int;
        private var _shaders : Vector.<String>;
        private var _rotationQuat : Quaternion;
       
        /**注释**/
        private static const COMMENT_TOKEN : String = "
//";
        /**版本**/
        private static const VERSION_TOKEN : String = "MD5Version";
        /**指令**/
        private static const COMMAND_LINE_TOKEN : String = "commandline";
        /**总骨骼数**/
        private static const NUM_JOINTS_TOKEN : String = "numJoints";
        /**总网格数**/
        private static const NUM_MESHES_TOKEN : String = "numMeshes";
        /**骨骼令牌**/
        private static const JOINTS_TOKEN : String = "joints";
        /**网格令牌**/
        private static const MESH_TOKEN : String = "mesh";
        /**网格shader令牌**/
        private static const MESH_SHADER_TOKEN : String = "shader";
        /**网格总顶点令牌**/
        private static const MESH_NUM_VERTS_TOKEN : String = "numverts";
        /**网格顶点令牌**/
        private static const MESH_VERT_TOKEN : String = "vert";
        /**网格总三角形令牌**/
        private static const MESH_NUM_TRIS_TOKEN : String = "numtris";
        /**网格三角形令牌**/
        private static const MESH_TRI_TOKEN : String = "tri";
        /**网格总权重令牌**/
        private static const MESH_NUM_WEIGHTS_TOKEN : String = "numweights";
        /**网格权重令牌**/
        private static const MESH_WEIGHT_TOKEN : String = "weight";
    }
}

实际上,花了半个下午将多网格解析搞定了。
只需要将meshData 修改成为一个MeshData的数组,缓存所有的MeshData即可。
修改后的 parseMesh() 函数如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
        /**
         * 解析网格几何体
         */

        private function parseMesh() : void
        {
            var token : String = getNextToken();
            var ch : String;
           
            if(token != "{") throw new Error("关键字错误");
           
            _shaders ||= new Vector.<String>();
            md5_mesh ||= new Vector.<MeshData>();
           
            var mesh : MeshData = new MeshData();
           
            while(ch != "}") {
                ch = getNextToken();
                switch(ch){
                    case COMMENT_TOKEN:
                        ignoreLine();
                        break;
                    case MESH_SHADER_TOKEN:
                        _shaders.push(parseLiteralString());
                        break;
                    case MESH_NUM_VERTS_TOKEN:
                        mesh.num_verts = getNextInt();
                        mesh.md5_vertex = new Vector.<MD5Vertex>();
                        break;
                    case MESH_NUM_TRIS_TOKEN:
                        mesh.num_tris = getNextInt();
                        mesh.md5_triangle = new Vector.<MD5Triangle>();
                        break;
                    case MESH_VERT_TOKEN:
                        parseVertex(mesh.md5_vertex);
                        break;
                    case MESH_TRI_TOKEN:
                        parseTri(mesh.md5_triangle);
                        break;
                    case MESH_WEIGHT_TOKEN:
                        parseWeight(mesh.md5_weight)
                        break;
                }
            }
           
            md5_mesh.push(mesh);
        }

最后的最后,对应每个MeshData都创建他们对应的 vetexBuffer,uvBuffer,indicesBuffer 即可。
在绘制的时候,只需要遍历各个MeshData进行绘制即可
just like this!

1
2
3
4
5
6
7
8
9
10
11
12
13
            for(var i : int = 0; i < md5Result.meshDataNum; i++){
                var vertexBuffer : VertexBuffer3D = md5Result.vertexBufferList[i];
                var uvBuffer : VertexBuffer3D = md5Result.uvBufferList[i];
                var indexBuffer : IndexBuffer3D = md5Result.indexBufferList[i];
                var texture : Texture = m_textureList[i];
               
                m_context.setTextureAt(0, texture);
                m_context.setVertexBufferAt(0, vertexBuffer,0, Context3DVertexBufferFormat.FLOAT_3);
                m_context.setVertexBufferAt(1,uvBuffer,0,Context3DVertexBufferFormat.FLOAT_2);
                m_context.drawTriangles(indexBuffer);
                m_context.setTextureAt(0, null);
            }
            m_context.present();

接下来,将解析骨骼动画,这就丢给明天来做吧~

发表评论

电子邮件地址不会被公开。 必填项已用*标注

This site uses Akismet to reduce spam. Learn how your comment data is processed.