OSDN Git Service

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