OSDN Git Service

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