4 * License : The MIT License
5 * Copyright(c) 2011 MikuToga Partners
8 package jp.sourceforge.mikutoga.vmd.parser;
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;
17 * VMDモーションファイルの基本部パーサ。
18 * <p>ボーンのモーション情報およびモーフモーション情報のパース処理を含む。
20 class VmdBasicParser extends CommonParser{
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;
27 private static final String ERRMSG_INVINTPLT =
28 "there is potential inconsistency in motion interpolation data. "
32 private final byte[] motionIntplt = new byte[BZTOTAL_SIZE];
34 private VmdBasicHandler handler = null;
36 private boolean hasStageActName = false;
37 private boolean strictMode = true;
44 VmdBasicParser(MmdInputStream source){
51 * パースしたモデル名がカメラ及びライティング用モデル名だったか判定する。
52 * @return カメラ及びライティング用モデル名だったらtrue
54 boolean hasStageActName(){
55 return this.hasStageActName;
60 * @param basicHandler ハンドラ
62 void setBasicHandler(VmdBasicHandler basicHandler){
63 this.handler = basicHandler;
68 * 厳密なパース(Strict-mode)を行うか否か設定する。
69 * デフォルトではStrict-modeはオン。
71 * ボーンモーションの冗長な補間情報の一貫性チェックが行わる。
72 * @param mode Strict-modeに設定したければtrue
74 void setStrictMode(boolean mode){
75 this.strictMode = mode;
80 * VMDファイル基本部のパースを開始する。
81 * @throws IOException IOエラー
82 * @throws MmdFormatException フォーマットエラー
84 void parse() throws IOException, MmdFormatException {
85 this.hasStageActName = false;
97 * @throws IOException IOエラー
98 * @throws MmdFormatException フォーマットエラー
100 private void parseVmdHeader() throws IOException, MmdFormatException{
101 byte[] header = new byte[VmdConst.HEADER_LENGTH];
102 parseByteArray(header);
104 if( ! VmdConst.startsWithMagic(header) ){
105 throw new MmdFormatException("unknown VMD-header type");
108 if(this.handler != null){
109 this.handler.vmdHeaderInfo(header);
117 * @throws IOException IOエラー
118 * @throws MmdFormatException フォーマットエラー
120 private void parseVmdModelName() throws IOException, MmdFormatException{
121 String modelName = parseZeroTermWin31J(VmdConst.MODELNAME_MAX);
123 if(VmdConst.isStageActName(modelName)){
124 this.hasStageActName = true;
127 if(this.handler != null){
128 this.handler.vmdModelName(modelName);
135 * ボーンモーションデータのパースと通知。
136 * @throws IOException IOエラー
137 * @throws MmdFormatException フォーマットエラー
139 private void parseVmdBoneMotion()
140 throws IOException, MmdFormatException{
141 int boneMotionNo = parseLeInt();
143 if(this.handler == null){
144 skip(VmdConst.BONEMOTION_DATA_SZ * boneMotionNo);
148 this.handler.loopStart(
149 VmdBasicHandler.BONEMOTION_LIST, boneMotionNo);
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);
156 float xPos = parseLeFloat();
157 float yPos = parseLeFloat();
158 float zPos = parseLeFloat();
159 this.handler.vmdBonePosition(xPos, yPos, zPos);
161 float qx = parseLeFloat();
162 float qy = parseLeFloat();
163 float qz = parseLeFloat();
164 float qw = parseLeFloat();
165 this.handler.vmdBoneRotationQt(qx, qy, qz, qw);
167 parseVmdMotionInterpolation();
169 this.handler.loopNext(VmdBasicHandler.BONEMOTION_LIST);
172 this.handler.loopEnd(VmdBasicHandler.BONEMOTION_LIST);
178 * ボーンモーション補間データのパースと通知。
179 * @throws IOException IOエラー
180 * @throws MmdFormatException フォーマットエラー
182 private void parseVmdMotionInterpolation()
183 throws IOException, MmdFormatException{
184 if(this.handler == null){
185 skip(this.motionIntplt.length);
189 parseByteArray(this.motionIntplt);
197 byte xP1x = this.motionIntplt[idx++];
198 byte yP1x = this.motionIntplt[idx++];
199 byte zP1x = this.motionIntplt[idx++];
200 byte rP1x = this.motionIntplt[idx++];
202 byte xP1y = this.motionIntplt[idx++];
203 byte yP1y = this.motionIntplt[idx++];
204 byte zP1y = this.motionIntplt[idx++];
205 byte rP1y = this.motionIntplt[idx++];
207 byte xP2x = this.motionIntplt[idx++];
208 byte yP2x = this.motionIntplt[idx++];
209 byte zP2x = this.motionIntplt[idx++];
210 byte rP2x = this.motionIntplt[idx++];
212 byte xP2y = this.motionIntplt[idx++];
213 byte yP2y = this.motionIntplt[idx++];
214 byte zP2y = this.motionIntplt[idx++];
215 byte rP2y = this.motionIntplt[idx++];
217 assert idx == BZXYZR_SIZE;
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);
228 * 補間情報の冗長箇所の整合性チェックを行う。
229 * @throws MmdFormatException 冗長箇所の不整合を検出した。
231 private void checkIntpltStrict() throws MmdFormatException{
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;
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,
247 int onePos = targetIdx + span;
248 if(this.motionIntplt[onePos] != (byte) 0x01){
249 throw new MmdFormatException(ERRMSG_INVINTPLT, getPosition());
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,
268 * モーフモーションデータのパースと通知。
269 * @throws IOException IOエラー
270 * @throws MmdFormatException フォーマットエラー
272 private void parseVmdMorph() throws IOException, MmdFormatException{
273 int morphMotionNo = parseLeInt();
275 if(this.handler == null){
276 skip(VmdConst.MORPH_DATA_SZ * morphMotionNo);
280 this.handler.loopStart(
281 VmdBasicHandler.MORPH_LIST, morphMotionNo);
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);
289 this.handler.loopNext(VmdBasicHandler.MORPH_LIST);
292 this.handler.loopEnd(VmdBasicHandler.MORPH_LIST);