OSDN Git Service

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