OSDN Git Service

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