OSDN Git Service

TogaGem1.103.2版より移行。
[mikutoga/TogaGem.git] / src / main / java / jp / sourceforge / mikutoga / parser / pmd / PmdParserBase.java
1 /*\r
2  * pmd file parser\r
3  *\r
4  * License : The MIT License\r
5  * Copyright(c) 2010 MikuToga Partners\r
6  */\r
7 \r
8 package jp.sourceforge.mikutoga.parser.pmd;\r
9 \r
10 import java.io.IOException;\r
11 import jp.sourceforge.mikutoga.parser.CommonParser;\r
12 import jp.sourceforge.mikutoga.parser.MmdFormatException;\r
13 import jp.sourceforge.mikutoga.parser.MmdSource;\r
14 \r
15 /**\r
16  * PMDモデルファイルのパーサ基本部。\r
17  */\r
18 public class PmdParserBase extends CommonParser {\r
19 \r
20     /** 改行文字列 CR。 */\r
21     protected static final String CR = "\r";       // 0x0d\r
22     /** 改行文字列 LF。 */\r
23     protected static final String LF = "\n";       // 0x0a\r
24     /** 改行文字列 CRLF。 */\r
25     protected static final String CRLF = CR + LF;  // 0x0d, 0x0a\r
26 \r
27     private static final String MAGIC = "Pmd";\r
28     private static final int MAGIC_SZ = MAGIC.getBytes(CS_WIN31J).length;\r
29 \r
30     private static final int VERTEX_DATA_SZ      = 38;\r
31     private static final int SURFACE_DATA_SZ     =  6;\r
32     private static final int MATERIAL_DATA_SZ    = 70;\r
33     private static final int BONE_DATA_SZ        = 39;\r
34     private static final int MORPHVERTEX_DATA_SZ = 16;\r
35     private static final int MORPHORDER_DATA_SZ  =  2;\r
36     private static final int BONEGROUP_DATA_SZ   = 50;\r
37     private static final int GROUPEDBONE_DATA_SZ =  3;\r
38 \r
39 \r
40     private PmdBasicHandler basicHandler = null;\r
41     private PmdShapeHandler shapeHandler = null;\r
42     private PmdMaterialHandler materialHandler = null;\r
43     private PmdBoneHandler boneHandler = null;\r
44     private PmdMorphHandler morphHandler = null;\r
45 \r
46     private int boneCount      = -1;\r
47     private int morphCount     = -1;\r
48     private int boneGroupCount = -1;\r
49 \r
50 \r
51     /**\r
52      * コンストラクタ。\r
53      * @param source 入力ソース\r
54      */\r
55     public PmdParserBase(MmdSource source){\r
56         super(source);\r
57         return;\r
58     }\r
59 \r
60     /**\r
61      * 文字列の最後がLF(0x0a)の場合削除する。\r
62      * @param name 文字列\r
63      * @return 末尾LFが削除された文字列\r
64      */\r
65     public static String chopLastLF(String name){\r
66         String result;\r
67 \r
68         if(name.endsWith(LF)){\r
69             result = name.substring(0, name.length() - 1);\r
70         }else{\r
71             result = name;\r
72         }\r
73 \r
74         return result;\r
75     }\r
76 \r
77     /**\r
78      * シェーディング用ファイル情報から\r
79      * テクスチャファイル名とスフィアマップファイル名を分離する。\r
80      * @param shadingFile シェーディング用ファイル情報\r
81      * @return [0]:テクスチャファイル名 [1]:スフィアマップファイル名。\r
82      * 該当ファイル名が無い場合は空文字列。\r
83      */\r
84     public static String[] splitShadingFileInfo(String shadingFile){\r
85         String[] result;\r
86 \r
87         result = shadingFile.split('\\'+"*", 2);\r
88         assert result.length == 1 || result.length == 2;\r
89 \r
90         if(result.length == 1){\r
91             String onlyFile = result[0];\r
92             result = new String[2];\r
93             result[0] = "";\r
94             result[1] = "";\r
95             if(onlyFile.endsWith(".sph") || onlyFile.endsWith(".spa")){\r
96                 result[1] = onlyFile;\r
97             }else{\r
98                 result[0] = onlyFile;\r
99             }\r
100         }\r
101 \r
102         assert result.length == 2;\r
103 \r
104         return result;\r
105     }\r
106 \r
107     /**\r
108      * 基本情報通知ハンドラを登録する。\r
109      * @param handler ハンドラ\r
110      */\r
111     public void setBasicHandler(PmdBasicHandler handler){\r
112         this.basicHandler = handler;\r
113         return;\r
114     }\r
115 \r
116     /**\r
117      * 形状情報通知ハンドラを登録する。\r
118      * @param handler ハンドラ\r
119      */\r
120     public void setShapeHandler(PmdShapeHandler handler){\r
121         this.shapeHandler = handler;\r
122         return;\r
123     }\r
124 \r
125     /**\r
126      * 材質情報通知ハンドラを登録する。\r
127      * @param handler ハンドラ\r
128      */\r
129     public void setMaterialHandler(PmdMaterialHandler handler){\r
130         this.materialHandler = handler;\r
131         return;\r
132     }\r
133 \r
134     /**\r
135      * ボーン情報通知ハンドラを登録する。\r
136      * @param handler ハンドラ\r
137      */\r
138     public void setBoneHandler(PmdBoneHandler handler){\r
139         this.boneHandler = handler;\r
140         return;\r
141     }\r
142 \r
143     /**\r
144      * モーフ情報通知ハンドラを登録する。\r
145      * @param handler ハンドラ\r
146      */\r
147     public void setMorphHandler(PmdMorphHandler handler){\r
148         this.morphHandler = handler;\r
149         return;\r
150     }\r
151 \r
152     /**\r
153      * パースによって得られたボーン数を返す。\r
154      * @return ボーン数\r
155      */\r
156     protected int getBoneCount(){\r
157         return this.boneCount;\r
158     }\r
159 \r
160     /**\r
161      * パースによって得られたモーフ数を返す。\r
162      * @return モーフ数\r
163      */\r
164     protected int getMorphCount(){\r
165         return this.morphCount;\r
166     }\r
167 \r
168     /**\r
169      * パースによって得られたボーングループ数を返す。\r
170      * @return ボーングループ数\r
171      */\r
172     protected int getBoneGroupCount(){\r
173         return this.boneGroupCount;\r
174     }\r
175 \r
176     /**\r
177      * PMDファイルのパースを開始する。\r
178      * @throws IOException IOエラー\r
179      * @throws MmdFormatException フォーマットエラー\r
180      */\r
181     public void parsePmd()\r
182             throws IOException, MmdFormatException {\r
183         if(this.basicHandler != null){\r
184             this.basicHandler.pmdParseStart();\r
185         }\r
186 \r
187         parseBody();\r
188 \r
189         boolean hasMoreData = hasMore();\r
190         if(this.basicHandler != null){\r
191             this.basicHandler.pmdParseEnd(hasMoreData);\r
192         }\r
193 \r
194         return;\r
195     }\r
196 \r
197     /**\r
198      * PMDファイル本体のパースを開始する。\r
199      * パーサを拡張する場合はこのメソッドをオーバーライドする。\r
200      * @throws IOException IOエラー\r
201      * @throws MmdFormatException フォーマットエラー\r
202      */\r
203     protected void parseBody() throws IOException, MmdFormatException{\r
204         parsePmdHeader();\r
205 \r
206         parseVertexList();\r
207         parseSurfaceList();\r
208         parseMaterialList();\r
209         parseBoneList();\r
210         parseIKList();\r
211         parseMorphList();\r
212         parseMorphOrderList();\r
213         parseBoneGroupList();\r
214         parseGroupedBoneList();\r
215 \r
216         return;\r
217     }\r
218 \r
219     /**\r
220      * PMDファイルヘッダ部のパースと通知。\r
221      * @throws IOException IOエラー\r
222      * @throws MmdFormatException フォーマットエラー\r
223      */\r
224     private void parsePmdHeader() throws IOException, MmdFormatException{\r
225         String magic = parseZeroTermString(MAGIC_SZ);\r
226         if( ! magic.equals(MAGIC) ){\r
227             throw new MmdFormatException("unrecognized magic data");\r
228         }\r
229 \r
230         float ver = parseFloat();\r
231         String modelName   =\r
232                 parseZeroTermString(PmdLimits.MAXBYTES_MODELNAME);\r
233         String description =\r
234                 parseZeroTermString(PmdLimits.MAXBYTES_MODELDESC);\r
235         description = description.replace(CRLF, LF);\r
236 \r
237         if(this.basicHandler != null){\r
238             this.basicHandler.pmdHeaderInfo(ver);\r
239             this.basicHandler.pmdModelInfo(modelName, description);\r
240         }\r
241 \r
242         return;\r
243     }\r
244 \r
245     /**\r
246      * 頂点情報のパースと通知。\r
247      * @throws IOException IOエラー\r
248      * @throws MmdFormatException フォーマットエラー\r
249      */\r
250     private void parseVertexList() throws IOException, MmdFormatException{\r
251         int vertexNum = parseInteger();\r
252 \r
253         if(this.shapeHandler == null){\r
254             skip(VERTEX_DATA_SZ * vertexNum);\r
255             return;\r
256         }\r
257 \r
258         this.shapeHandler.loopStart(PmdShapeHandler.VERTEX_LIST, vertexNum);\r
259 \r
260         for(int ct = 0; ct < vertexNum; ct++){\r
261             float xPos = parseFloat();\r
262             float yPos = parseFloat();\r
263             float zPos = parseFloat();\r
264             this.shapeHandler.pmdVertexPosition(xPos, yPos, zPos);\r
265 \r
266             float xVec = parseFloat();\r
267             float yVec = parseFloat();\r
268             float zVec = parseFloat();\r
269             this.shapeHandler.pmdVertexNormal(xVec, yVec, zVec);\r
270 \r
271             float uVal = parseFloat();\r
272             float vVal = parseFloat();\r
273             this.shapeHandler.pmdVertexUV(uVal, vVal);\r
274 \r
275             int boneId1 = parseUShortAsInteger();\r
276             int boneId2 = parseUShortAsInteger();\r
277             int weightForB1 = parseUByteAsInteger();\r
278             this.shapeHandler.pmdVertexWeight(boneId1, boneId2, weightForB1);\r
279 \r
280             boolean hideEdge = parseBoolean();\r
281             this.shapeHandler.pmdVertexEdge(hideEdge);\r
282 \r
283             this.shapeHandler.loopNext(PmdShapeHandler.VERTEX_LIST);\r
284         }\r
285 \r
286         this.shapeHandler.loopEnd(PmdShapeHandler.VERTEX_LIST);\r
287 \r
288         return;\r
289     }\r
290 \r
291     /**\r
292      * 面情報のパースと通知。\r
293      * @throws IOException IOエラー\r
294      * @throws MmdFormatException フォーマットエラー\r
295      */\r
296     private void parseSurfaceList() throws IOException, MmdFormatException{\r
297         int vertexNum = parseInteger();\r
298         if(vertexNum % 3 != 0) throw new MmdFormatException();\r
299         int surfaceNum = vertexNum / 3;\r
300 \r
301         if(this.shapeHandler == null){\r
302             skip(SURFACE_DATA_SZ * surfaceNum);\r
303             return;\r
304         }\r
305 \r
306         this.shapeHandler.loopStart(PmdShapeHandler.SURFACE_LIST, surfaceNum);\r
307 \r
308         for(int ct = 0; ct < surfaceNum; ct++){\r
309             int vertexId1 = parseUShortAsInteger();\r
310             int vertexId2 = parseUShortAsInteger();\r
311             int vertexId3 = parseUShortAsInteger();\r
312             this.shapeHandler.pmdSurfaceTriangle(vertexId1,\r
313                                                  vertexId2,\r
314                                                  vertexId3 );\r
315             this.shapeHandler.loopNext(PmdShapeHandler.SURFACE_LIST);\r
316         }\r
317 \r
318         this.shapeHandler.loopEnd(PmdShapeHandler.SURFACE_LIST);\r
319 \r
320         return;\r
321     }\r
322 \r
323     /**\r
324      * 材質情報のパースと通知。\r
325      * @throws IOException IOエラー\r
326      * @throws MmdFormatException フォーマットエラー\r
327      */\r
328     private void parseMaterialList() throws IOException, MmdFormatException{\r
329         int materialNum = parseInteger();\r
330 \r
331         if(this.materialHandler == null){\r
332             skip(MATERIAL_DATA_SZ * materialNum);\r
333             return;\r
334         }\r
335 \r
336         this.materialHandler.loopStart(PmdMaterialHandler.MATERIAL_LIST,\r
337                                        materialNum );\r
338 \r
339         for(int ct = 0; ct < materialNum; ct++){\r
340             float red;\r
341             float green;\r
342             float blue;\r
343 \r
344             red   = parseFloat();\r
345             green = parseFloat();\r
346             blue  = parseFloat();\r
347             float alpha = parseFloat();\r
348             this.materialHandler.pmdMaterialDiffuse(red, green, blue, alpha);\r
349 \r
350             float shininess = parseFloat();\r
351             red   = parseFloat();\r
352             green = parseFloat();\r
353             blue  = parseFloat();\r
354             this.materialHandler.pmdMaterialSpecular(red, green, blue,\r
355                                                      shininess);\r
356 \r
357             red   = parseFloat();\r
358             green = parseFloat();\r
359             blue  = parseFloat();\r
360             this.materialHandler.pmdMaterialAmbient(red, green, blue);\r
361 \r
362             int toonidx = parseUByteAsInteger();\r
363             boolean hasEdge = parseBoolean();\r
364             int surfaceCount = parseInteger();\r
365             String shadingFile =\r
366                     parseZeroTermString(PmdLimits.MAXBYTES_TEXTUREFILENAME);\r
367             String[] splitted = splitShadingFileInfo(shadingFile);\r
368             String textureFile = splitted[0];\r
369             String sphereFile = splitted[1];\r
370 \r
371             this.materialHandler.pmdMaterialShading(toonidx,\r
372                                                     textureFile, sphereFile );\r
373             this.materialHandler.pmdMaterialInfo(hasEdge, surfaceCount);\r
374 \r
375             this.materialHandler.loopNext(PmdMaterialHandler.MATERIAL_LIST);\r
376         }\r
377 \r
378         this.materialHandler.loopEnd(PmdMaterialHandler.MATERIAL_LIST);\r
379 \r
380         return;\r
381     }\r
382 \r
383     /**\r
384      * ボーン情報のパースと通知。\r
385      * @throws IOException IOエラー\r
386      * @throws MmdFormatException フォーマットエラー\r
387      */\r
388     private void parseBoneList() throws IOException, MmdFormatException{\r
389         this.boneCount = parseUShortAsInteger();\r
390 \r
391         if(this.boneHandler == null){\r
392             skip(BONE_DATA_SZ * this.boneCount);\r
393             return;\r
394         }\r
395 \r
396         this.boneHandler.loopStart(PmdBoneHandler.BONE_LIST, this.boneCount);\r
397 \r
398         for(int ct = 0; ct < this.boneCount; ct++){\r
399             String boneName =\r
400                     parseZeroTermString(PmdLimits.MAXBYTES_BONENAME);\r
401             int parentId = parseUShortAsInteger();\r
402             int tailId = parseUShortAsInteger();\r
403             byte boneKind = parseByte();\r
404             int ikId = parseUShortAsInteger();\r
405 \r
406             this.boneHandler.pmdBoneInfo(boneName, boneKind);\r
407             this.boneHandler.pmdBoneLink(parentId, tailId, ikId);\r
408 \r
409             float xPos = parseFloat();\r
410             float yPos = parseFloat();\r
411             float zPos = parseFloat();\r
412 \r
413             this.boneHandler.pmdBonePosition(xPos, yPos, zPos);\r
414 \r
415             this.boneHandler.loopNext(PmdBoneHandler.BONE_LIST);\r
416         }\r
417 \r
418         this.boneHandler.loopEnd(PmdBoneHandler.BONE_LIST);\r
419 \r
420         return;\r
421     }\r
422 \r
423     /**\r
424      * IKリスト情報のパースと通知。\r
425      * @throws IOException IOエラー\r
426      * @throws MmdFormatException フォーマットエラー\r
427      */\r
428     private void parseIKList() throws IOException, MmdFormatException{\r
429         int ikCount = parseUShortAsInteger();\r
430 \r
431         if(this.boneHandler != null){\r
432             this.boneHandler.loopStart(PmdBoneHandler.IK_LIST, ikCount);\r
433         }\r
434 \r
435         for(int ct = 0; ct < ikCount; ct++){\r
436             int boneId = parseUShortAsInteger();\r
437             int targetId = parseUShortAsInteger();\r
438             int chainLength = parseUByteAsInteger();\r
439             int depth = parseUShortAsInteger();\r
440             float weight = parseFloat();\r
441 \r
442             parseIKChainList(chainLength);\r
443 \r
444             if(this.boneHandler != null){\r
445                 this.boneHandler.pmdIKInfo(boneId, targetId, depth, weight);\r
446                 this.boneHandler.loopNext(PmdBoneHandler.IK_LIST);\r
447             }\r
448         }\r
449 \r
450         if(this.boneHandler != null){\r
451             this.boneHandler.loopEnd(PmdBoneHandler.IK_LIST);\r
452         }\r
453 \r
454         return;\r
455     }\r
456 \r
457     /**\r
458      * IKチェーン情報のパースと通知。\r
459      * @param chainLength チェーン長\r
460      * @throws IOException IOエラー\r
461      * @throws MmdFormatException フォーマットエラー\r
462      */\r
463     private void parseIKChainList(int chainLength)\r
464             throws IOException, MmdFormatException{\r
465         if(this.boneHandler != null){\r
466             this.boneHandler.loopStart(PmdBoneHandler.IKCHAIN_LIST,\r
467                                        chainLength);\r
468         }\r
469 \r
470         for(int ct = 0; ct < chainLength; ct++){\r
471             int childId = parseUShortAsInteger();\r
472             if(this.boneHandler != null){\r
473                 this.boneHandler.pmdIKChainInfo(childId);\r
474                 this.boneHandler.loopNext(PmdBoneHandler.IKCHAIN_LIST);\r
475             }\r
476         }\r
477 \r
478         if(this.boneHandler != null){\r
479             this.boneHandler.loopEnd(PmdBoneHandler.IKCHAIN_LIST);\r
480         }\r
481 \r
482         return;\r
483     }\r
484 \r
485     /**\r
486      * モーフ情報のパースと通知。\r
487      * @throws IOException IOエラー\r
488      * @throws MmdFormatException フォーマットエラー\r
489      */\r
490     private void parseMorphList() throws IOException, MmdFormatException{\r
491         this.morphCount = parseUShortAsInteger();\r
492 \r
493         if(this.morphHandler != null){\r
494             this.morphHandler.loopStart(PmdMorphHandler.MORPH_LIST,\r
495                                         this.morphCount );\r
496         }\r
497 \r
498         for(int ct = 0; ct < this.morphCount; ct++){\r
499             String morphName =\r
500                     parseZeroTermString(PmdLimits.MAXBYTES_MORPHNAME);\r
501             int vertexCount = parseInteger();\r
502             byte morphType = parseByte();\r
503 \r
504             if(this.morphHandler != null){\r
505                 this.morphHandler.pmdMorphInfo(morphName, morphType);\r
506             }\r
507 \r
508             parseMorphVertexList(vertexCount);\r
509 \r
510             if(this.morphHandler != null){\r
511                 this.morphHandler.loopNext(PmdMorphHandler.MORPH_LIST);\r
512             }\r
513         }\r
514 \r
515         if(this.morphHandler != null){\r
516             this.morphHandler.loopEnd(PmdMorphHandler.MORPH_LIST);\r
517         }\r
518 \r
519         return;\r
520     }\r
521 \r
522     /**\r
523      * モーフ形状のパースと通知。\r
524      * @param vertexCount 頂点数\r
525      * @throws IOException IOエラー\r
526      * @throws MmdFormatException フォーマットエラー\r
527      */\r
528     private void parseMorphVertexList(int vertexCount)\r
529             throws IOException, MmdFormatException{\r
530         if(this.morphHandler == null){\r
531             skip(MORPHVERTEX_DATA_SZ * vertexCount);\r
532             return;\r
533         }\r
534 \r
535         this.morphHandler.loopStart(PmdMorphHandler.MORPHVERTEX_LIST,\r
536                                     vertexCount );\r
537 \r
538         for(int ct = 0; ct < vertexCount; ct++){\r
539             int vertexId = parseInteger();\r
540             float xPos = parseFloat();\r
541             float yPos = parseFloat();\r
542             float zPos = parseFloat();\r
543             this.morphHandler.pmdMorphVertexInfo(vertexId, xPos, yPos, zPos);\r
544             this.morphHandler.loopNext(PmdMorphHandler.MORPHVERTEX_LIST);\r
545         }\r
546 \r
547         this.morphHandler.loopEnd(PmdMorphHandler.MORPHVERTEX_LIST);\r
548 \r
549         return;\r
550     }\r
551 \r
552     /**\r
553      * モーフGUI表示順のパースと通知。\r
554      * @throws IOException IOエラー\r
555      * @throws MmdFormatException フォーマットエラー\r
556      */\r
557     private void parseMorphOrderList()\r
558             throws IOException, MmdFormatException{\r
559         int morphOrderCount = parseUByteAsInteger();\r
560 \r
561         if(this.morphHandler == null){\r
562             skip(MORPHORDER_DATA_SZ * morphOrderCount);\r
563             return;\r
564         }\r
565 \r
566         this.morphHandler.loopStart(PmdMorphHandler.MORPHORDER_LIST,\r
567                                     morphOrderCount );\r
568 \r
569         for(int ct = 0; ct < morphOrderCount; ct++){\r
570             int morphId = parseUShortAsInteger();\r
571             this.morphHandler.pmdMorphOrderInfo(morphId);\r
572 \r
573             this.morphHandler.loopNext(PmdMorphHandler.MORPHORDER_LIST);\r
574         }\r
575 \r
576         this.morphHandler.loopEnd(PmdMorphHandler.MORPHORDER_LIST);\r
577 \r
578         return;\r
579     }\r
580 \r
581     /**\r
582      * ボーングループ名のパースと通知。\r
583      * @throws IOException IOエラー\r
584      * @throws MmdFormatException フォーマットエラー\r
585      */\r
586     private void parseBoneGroupList()\r
587             throws IOException, MmdFormatException{\r
588         this.boneGroupCount = parseUByteAsInteger();\r
589 \r
590         if(this.boneHandler == null){\r
591             skip(BONEGROUP_DATA_SZ * this.boneGroupCount);\r
592             return;\r
593         }\r
594 \r
595         this.boneHandler.loopStart(PmdBoneHandler.BONEGROUP_LIST,\r
596                                    this.boneGroupCount);\r
597 \r
598         for(int ct = 0; ct < this.boneGroupCount; ct++){\r
599             String groupName =\r
600                     parseZeroTermString(PmdLimits.MAXBYTES_BONEGROUPNAME);\r
601             groupName = chopLastLF(groupName);\r
602             this.boneHandler.pmdBoneGroupInfo(groupName);\r
603 \r
604             this.boneHandler.loopNext(PmdBoneHandler.BONEGROUP_LIST);\r
605         }\r
606 \r
607         this.boneHandler.loopEnd(PmdBoneHandler.BONEGROUP_LIST);\r
608 \r
609         return;\r
610     }\r
611 \r
612     /**\r
613      * ボーングループ内訳のパースと通知。\r
614      * @throws IOException IOエラー\r
615      * @throws MmdFormatException フォーマットエラー\r
616      */\r
617     private void parseGroupedBoneList()\r
618             throws IOException, MmdFormatException{\r
619         int groupedBoneCount = parseInteger();\r
620 \r
621         if(this.boneHandler == null){\r
622             skip(GROUPEDBONE_DATA_SZ * groupedBoneCount);\r
623             return;\r
624         }\r
625 \r
626         this.boneHandler.loopStart(PmdBoneHandler.GROUPEDBONE_LIST,\r
627                                    groupedBoneCount);\r
628 \r
629         for(int ct = 0; ct < groupedBoneCount; ct++){\r
630             int boneId = parseUShortAsInteger();\r
631             int groupId = parseUByteAsInteger();\r
632             this.boneHandler.pmdGroupedBoneInfo(boneId, groupId);\r
633 \r
634             this.boneHandler.loopNext(PmdBoneHandler.GROUPEDBONE_LIST);\r
635         }\r
636 \r
637         this.boneHandler.loopEnd(PmdBoneHandler.GROUPEDBONE_LIST);\r
638 \r
639         return;\r
640     }\r
641 \r
642 }\r