OSDN Git Service

b15f211d328e57289e5a0ad42434128ba15d7e2c
[mikutoga/TogaGem.git] / src / main / java / jp / sourceforge / mikutoga / vmd / parser / VmdBasicParser.java
1 /*
2  * VMD basic parser
3  *
4  * License : The MIT License
5  * Copyright(c) 2011 MikuToga Partners
6  */
7
8 package jp.sourceforge.mikutoga.vmd.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.MmdInputStream;
14 import jp.sourceforge.mikutoga.vmd.VmdConst;
15
16 /**
17  * VMDモーションファイルの基本部パーサ。
18  * <p>ボーンのモーション情報およびモーフモーション情報のパース処理を含む。
19  */
20 class VmdBasicParser extends CommonParser{
21
22     private static final int BZ_SIZE = 4;           // 4byte Bezier parameter
23     private static final int BZXYZR_SIZE = BZ_SIZE * 4; // XYZR Bezier
24     private static final int BZ_REDUNDANT = 4;          // redundant spare
25     private static final int BZTOTAL_SIZE = BZXYZR_SIZE * BZ_REDUNDANT;
26
27     private static final String ERRMSG_INVINTPLT =
28             "there is potential inconsistency in motion interpolation data. "
29             +"(Strict-mode)";
30
31
32     private final byte[] motionIntplt = new byte[BZTOTAL_SIZE];
33
34     private VmdBasicHandler handler = null;
35
36     private boolean hasStageActName = false;
37     private boolean strictMode = true;
38
39
40     /**
41      * コンストラクタ。
42      * @param source 入力ソース
43      */
44     VmdBasicParser(MmdInputStream source){
45         super(source);
46         return;
47     }
48
49
50     /**
51      * パースしたモデル名がカメラ及びライティング用モデル名だったか判定する。
52      * @return カメラ及びライティング用モデル名だったらtrue
53      */
54     boolean hasStageActName(){
55         return this.hasStageActName;
56     }
57
58     /**
59      * 基本情報通知用ハンドラを登録する。
60      * @param basicHandler ハンドラ
61      */
62     void setBasicHandler(VmdBasicHandler basicHandler){
63         this.handler = basicHandler;
64         return;
65     }
66
67     /**
68      * 厳密なパース(Strict-mode)を行うか否か設定する。
69      * デフォルトではStrict-modeはオン。
70      * <p>Strict-mode下では、
71      * ボーンモーションの冗長な補間情報の一貫性チェックが行わる。
72      * @param mode Strict-modeに設定したければtrue
73      */
74     void setStrictMode(boolean mode){
75         this.strictMode = mode;
76         return;
77     }
78
79     /**
80      * VMDファイル基本部のパースを開始する。
81      * @throws IOException IOエラー
82      * @throws MmdFormatException フォーマットエラー
83      */
84     void parse() throws IOException, MmdFormatException {
85         this.hasStageActName = false;
86
87         parseVmdHeader();
88         parseVmdModelName();
89         parseVmdBoneMotion();
90         parseVmdMorph();
91
92         return;
93     }
94
95     /**
96      * VMDファイルヘッダ部のパースと通知。
97      * @throws IOException IOエラー
98      * @throws MmdFormatException フォーマットエラー
99      */
100     private void parseVmdHeader() throws IOException, MmdFormatException{
101         byte[] header = new byte[VmdConst.HEADER_LENGTH];
102         parseByteArray(header);
103
104         if( ! VmdConst.startsWithMagic(header) ){
105             throw new MmdFormatException("unknown VMD-header type");
106         }
107
108         if(this.handler != null){
109             this.handler.vmdHeaderInfo(header);
110         }
111
112         return;
113     }
114
115     /**
116      * モデル名のパースと通知。
117      * @throws IOException IOエラー
118      * @throws MmdFormatException フォーマットエラー
119      */
120     private void parseVmdModelName() throws IOException, MmdFormatException{
121         String modelName = parseZeroTermWin31J(VmdConst.MODELNAME_MAX);
122
123         if(VmdConst.isStageActName(modelName)){
124             this.hasStageActName = true;
125         }
126
127         if(this.handler != null){
128             this.handler.vmdModelName(modelName);
129         }
130
131         return;
132     }
133
134     /**
135      * ボーンモーションデータのパースと通知。
136      * @throws IOException IOエラー
137      * @throws MmdFormatException フォーマットエラー
138      */
139     private void parseVmdBoneMotion()
140             throws IOException, MmdFormatException{
141         int boneMotionNo = parseLeInt();
142
143         if(this.handler == null){
144             skip(VmdConst.BONEMOTION_DATA_SZ * boneMotionNo);
145             return;
146         }
147
148         this.handler.loopStart(
149                 VmdBasicHandler.BONEMOTION_LIST, boneMotionNo);
150
151         for(int ct = 0; ct < boneMotionNo; ct++){
152             String boneName = parseZeroTermWin31J(VmdConst.BONENAME_MAX);
153             int keyFrameNo = parseLeInt();
154             this.handler.vmdBoneMotion(boneName, keyFrameNo);
155
156             float xPos = parseLeFloat();
157             float yPos = parseLeFloat();
158             float zPos = parseLeFloat();
159             this.handler.vmdBonePosition(xPos, yPos, zPos);
160
161             float qx = parseLeFloat();
162             float qy = parseLeFloat();
163             float qz = parseLeFloat();
164             float qw = parseLeFloat();
165             this.handler.vmdBoneRotationQt(qx, qy, qz, qw);
166
167             parseVmdMotionInterpolation();
168
169             this.handler.loopNext(VmdBasicHandler.BONEMOTION_LIST);
170         }
171
172         this.handler.loopEnd(VmdBasicHandler.BONEMOTION_LIST);
173
174         return;
175     }
176
177     /**
178      * ボーンモーション補間データのパースと通知。
179      * @throws IOException IOエラー
180      * @throws MmdFormatException フォーマットエラー
181      */
182     private void parseVmdMotionInterpolation()
183             throws IOException, MmdFormatException{
184         if(this.handler == null){
185             skip(this.motionIntplt.length);
186             return;
187         }
188
189         parseByteArray(this.motionIntplt);
190
191         if(this.strictMode){
192             checkIntpltStrict();
193         }
194
195         int idx = 0;
196
197         byte xP1x = this.motionIntplt[idx++];
198         byte yP1x = this.motionIntplt[idx++];
199         byte zP1x = this.motionIntplt[idx++];
200         byte rP1x = this.motionIntplt[idx++];
201
202         byte xP1y = this.motionIntplt[idx++];
203         byte yP1y = this.motionIntplt[idx++];
204         byte zP1y = this.motionIntplt[idx++];
205         byte rP1y = this.motionIntplt[idx++];
206
207         byte xP2x = this.motionIntplt[idx++];
208         byte yP2x = this.motionIntplt[idx++];
209         byte zP2x = this.motionIntplt[idx++];
210         byte rP2x = this.motionIntplt[idx++];
211
212         byte xP2y = this.motionIntplt[idx++];
213         byte yP2y = this.motionIntplt[idx++];
214         byte zP2y = this.motionIntplt[idx++];
215         byte rP2y = this.motionIntplt[idx++];
216
217         assert idx == BZXYZR_SIZE;
218
219         this.handler.vmdBoneIntpltXpos(xP1x, xP1y, xP2x, xP2y);
220         this.handler.vmdBoneIntpltYpos(yP1x, yP1y, yP2x, yP2y);
221         this.handler.vmdBoneIntpltZpos(zP1x, zP1y, zP2x, zP2y);
222         this.handler.vmdBoneIntpltRot (rP1x, rP1y, rP2x, rP2y);
223
224         return;
225     }
226
227     /**
228      * 補間情報の冗長箇所の整合性チェックを行う。
229      * @throws MmdFormatException 冗長箇所の不整合を検出した。
230      */
231     private void checkIntpltStrict() throws MmdFormatException{
232         int lack = 1;
233         for(int ct = 1; ct < BZ_REDUNDANT; ct++){
234             int sourceIdx = 0 + lack;
235             int targetIdx = BZXYZR_SIZE * ct;
236             int span = BZXYZR_SIZE - lack;
237
238             for(int idx = 0; idx < span; idx++){
239                 byte sourceVal = this.motionIntplt[sourceIdx + idx];
240                 byte targetVal = this.motionIntplt[targetIdx + idx];
241                 if(sourceVal != targetVal){
242                     throw new MmdFormatException(ERRMSG_INVINTPLT,
243                                                  getPosition());
244                 }
245             }
246
247             int onePos = targetIdx + span;
248             if(this.motionIntplt[onePos] != (byte) 0x01){
249                 throw new MmdFormatException(ERRMSG_INVINTPLT, getPosition());
250             }
251
252             int zeroPosStart = onePos + 1;
253             int zeroPosEnd = targetIdx + BZXYZR_SIZE;
254             for(int idx = zeroPosStart; idx < zeroPosEnd; idx++){
255                 if(this.motionIntplt[idx] != (byte) 0x00){
256                     throw new MmdFormatException(ERRMSG_INVINTPLT,
257                                                  getPosition());
258                 }
259             }
260
261             lack++;
262         }
263
264         return;
265     }
266
267     /**
268      * モーフモーションデータのパースと通知。
269      * @throws IOException IOエラー
270      * @throws MmdFormatException フォーマットエラー
271      */
272     private void parseVmdMorph() throws IOException, MmdFormatException{
273         int morphMotionNo = parseLeInt();
274
275         if(this.handler == null){
276             skip(VmdConst.MORPH_DATA_SZ * morphMotionNo);
277             return;
278         }
279
280         this.handler.loopStart(
281                 VmdBasicHandler.MORPH_LIST, morphMotionNo);
282
283         for(int ct = 0; ct < morphMotionNo; ct++){
284             String morphName = parseZeroTermWin31J(VmdConst.MORPHNAME_MAX);
285             int keyFrameNo = parseLeInt();
286             float flex = parseLeFloat();
287             this.handler.vmdMorphMotion(morphName, keyFrameNo, flex);
288
289             this.handler.loopNext(VmdBasicHandler.MORPH_LIST);
290         }
291
292         this.handler.loopEnd(VmdBasicHandler.MORPH_LIST);
293
294         return;
295     }
296
297 }