4 * License : The MIT License
5 * Copyright(c) 2011 MikuToga Partners
8 package jp.sfjp.mikutoga.vmd.model.xml;
10 import java.io.IOException;
11 import java.text.MessageFormat;
12 import java.util.List;
14 import jp.sfjp.mikutoga.math.EulerYXZ;
15 import jp.sfjp.mikutoga.math.MkPos3D;
16 import jp.sfjp.mikutoga.math.MkQuat;
17 import jp.sfjp.mikutoga.typical.TypicalBone;
18 import jp.sfjp.mikutoga.typical.TypicalMorph;
19 import jp.sfjp.mikutoga.vmd.IllegalVmdDataException;
20 import jp.sfjp.mikutoga.vmd.VmdUniq;
21 import jp.sfjp.mikutoga.vmd.model.BezierParam;
22 import jp.sfjp.mikutoga.vmd.model.BoneMotion;
23 import jp.sfjp.mikutoga.vmd.model.MorphMotion;
24 import jp.sfjp.mikutoga.vmd.model.PosCurve;
25 import jp.sfjp.mikutoga.vmd.model.VmdMotion;
26 import jp.sfjp.mikutoga.xml.BasicXmlExporter;
27 import jp.sfjp.mikutoga.xml.SchemaUtil;
30 * VMDモーションデータをXMLへエクスポートする。
32 public class VmdXmlExporter extends BasicXmlExporter {
34 private static final String XML_VER = "1.0";
35 private static final String XML_ENC = "UTF-8";
36 private static final String XML_DECL =
37 "<?xml version=\"" + XML_VER
38 + "\" encoding=\"" + XML_ENC
41 private static final String XSINS = "xsi";
43 private static final String MSG_MAYBE = "Perhaps : [{0}]";
46 private XmlMotionFileType xmlType = XmlMotionFileType.XML_110820;
48 private boolean isQuaternionMode = true;
49 private String generator = "";
51 private final CameraXmlExporter cameraXmlExporter;
52 private final LightingXmlExpoter lightingExporter;
53 private final ExtraXmlExporter extraExporter;
54 private final FlagXmlExporter flagXmlExporter;
60 public VmdXmlExporter(){
63 this.cameraXmlExporter = new CameraXmlExporter(this);
64 this.lightingExporter = new LightingXmlExpoter(this);
65 this.extraExporter = new ExtraXmlExporter(this);
66 this.flagXmlExporter = new FlagXmlExporter(this);
76 public void setXmlFileType(XmlMotionFileType type){
83 this.xmlType = XmlMotionFileType.XML_130609;
87 throw new AssertionError();
90 assert this.xmlType == XmlMotionFileType.XML_110820
91 || this.xmlType == XmlMotionFileType.XML_130609;
100 public XmlMotionFileType getXmlFileType(){
105 * ボーン回転量をクォータニオンで出力するか否か設定する。
107 * @param mode trueだとクォータニオン、falseだとオイラー角で出力される。
109 public void setQuaternionMode(boolean mode){
110 this.isQuaternionMode = mode;
114 * ボーン回転量をクォータニオンで出力するか否か返す。
115 * @return クォータニオンで出力するならtrue
117 public boolean isQuaternionMode(){
118 return this.isQuaternionMode;
122 * Generatorメタ情報を設定する。
123 * @param generatorArg Generatorメタ情報。nullならXML出力しない。
125 public void setGenerator(String generatorArg){
126 this.generator = generatorArg;
131 * Generatorメタ情報を取得する。
132 * @return Generatorメタ情報。XML出力しないならnullを返す。
134 public String getGenerator(){
135 return this.generator;
139 * VMDモーションデータをXML形式で出力する。
140 * @param vmdMotion VMDモーションデータ
142 * @throws IOException 出力エラー
143 * @throws IllegalVmdDataException 不正なモーションデータを検出
145 public void putVmdXml(VmdMotion vmdMotion, Appendable xmlOut)
146 throws IOException, IllegalVmdDataException{
147 setAppendable(xmlOut);
150 putVmdXmlImpl(vmdMotion);
158 * VMDモーションデータをXML形式で出力する。
159 * @param vmdMotion VMDモーションデータ
160 * @throws IOException 出力エラー
161 * @throws IllegalVmdDataException 不正なモーションデータを検出
163 private void putVmdXmlImpl(VmdMotion vmdMotion)
164 throws IOException, IllegalVmdDataException{
169 if(vmdMotion.isModelMotion()){
170 putModelName(vmdMotion);
171 putBoneMotionSequence(vmdMotion);
172 putMorphSequence(vmdMotion);
173 if(this.xmlType == XmlMotionFileType.XML_130609){
174 this.flagXmlExporter.putFlagSequence(vmdMotion);
177 this.cameraXmlExporter.putCameraSequence(vmdMotion);
178 this.lightingExporter.putLuminousSequence(vmdMotion);
179 this.lightingExporter.putShadowSequence(vmdMotion);
182 ind().putETag(VmdTag.VMD_MOTION.tag()).ln(2);
183 ind().putLineComment("EOF").ln();
189 * ルート要素がオープンするまでの各種宣言を出力する。
190 * @throws IOException 出力エラー
192 private void putVmdRootOpen() throws IOException{
193 ind().putRawText(XML_DECL).ln(2);
195 ind().putBlockComment(XmlComment.TOP_COMMENT).ln(2);
201 switch(this.xmlType){
203 namespace = Schema110820.NS_VMDXML;
204 schemaUrl = Schema110820.SCHEMA_VMDXML;
205 schemaVer = Schema110820.VER_VMDXML;
208 namespace = Schema130609.NS_VMDXML;
209 schemaUrl = Schema130609.SCHEMA_VMDXML;
210 schemaVer = Schema130609.VER_VMDXML;
214 throw new AssertionError();
217 ind().putOpenSTag(VmdTag.VMD_MOTION.tag()).ln();
219 ind().putAttr("xmlns", namespace).ln();
220 ind().putAttr("xmlns:" + XSINS, SchemaUtil.NS_XSD).ln();
222 ind().putRawText(XSINS).putRawText(":schemaLocation=")
224 putRawText(namespace).ln();
225 ind().sp(2).putRawText(schemaUrl)
229 ind().putAttr(XmlAttr.ATTR_VERSION, schemaVer).ln();
231 putCloseSTag().ln(2);
238 * @throws IOException 出力エラー
240 private void putGenerator() throws IOException{
241 String genTxt = getGenerator();
242 if(genTxt == null) return;
243 if(genTxt.isEmpty()) return;
245 ind().putOpenSTag(VmdTag.META.tag()).sp();
246 putAttr(XmlAttr.ATTR_NAME, "generator").sp();
247 putAttr(XmlAttr.ATTR_CONTENT, genTxt).sp();
248 putCloseEmpty().ln(2);
255 * @param vmdMotion モーションデータ
256 * @throws IOException 出力エラー
258 private void putModelName(VmdMotion vmdMotion)
260 String modelName = vmdMotion.getModelName();
263 if(modelName != null && modelName.length() > 0){
264 modelComm = modelName;
266 modelComm = "[NAMELESS]";
269 ind().putLineComment(modelComm).ln();
270 ind().putOpenSTag(VmdTag.MODEL_NAME.tag()).sp();
271 putAttr(XmlAttr.ATTR_NAME, modelName).sp();
272 putCloseEmpty().ln(2);
279 * @param vmdMotion モーションデータ
280 * @throws IOException 出力エラー
282 private void putBoneMotionSequence(VmdMotion vmdMotion)
284 Map<String, List<BoneMotion>> boneMap = vmdMotion.getBonePartMap();
286 if( ! boneMap.isEmpty() ){
287 ind().putBlockComment(XmlComment.QUATERNION_COMMENT);
288 ind().putBlockComment(XmlComment.BEZIER_COMMENT);
291 ind().putSimpleSTag(VmdTag.BONE_M_SEQUENCE.tag()).ln();
294 if( ! boneMap.isEmpty() ) ln();
295 for(Map.Entry<String, List<BoneMotion>> entry : boneMap.entrySet()){
296 putBonePart(entry.getKey(), entry.getValue());
300 ind().putETag(VmdTag.BONE_M_SEQUENCE.tag()).ln(2);
307 * @param boneName ボーン名
308 * @param list ボーンモーションのリスト
309 * @throws IOException 出力エラー
311 private void putBonePart(String boneName, List<BoneMotion> list)
313 ind().putLineComment(boneName);
314 String globalName = TypicalBone.primary2global(boneName);
315 if(globalName != null){
317 MessageFormat.format(MSG_MAYBE, globalName);
318 sp(2).putLineComment(gname);
322 ind().putOpenSTag(VmdTag.BONE_PART.tag()).sp();
323 putAttr(XmlAttr.ATTR_NAME, boneName).sp();
324 putCloseSTag().ln(2);
327 for(BoneMotion bone : list){
332 ind().putETag(VmdTag.BONE_PART.tag()).ln(2);
339 * @param boneMotion ボーンモーション
340 * @throws IOException 出力エラー
342 private void putBoneMotion(BoneMotion boneMotion)
344 ind().putOpenSTag(VmdTag.BONE_MOTION.tag()).sp();
345 int frameNo = boneMotion.getFrameNumber();
346 putIntAttr(XmlAttr.ATTR_FRAME, frameNo).sp();
350 putBonePosition(boneMotion);
351 if(isQuaternionMode()){
352 putBoneRotQuat(boneMotion);
354 putBoneRotEyxz(boneMotion);
358 ind().putETag(VmdTag.BONE_MOTION.tag()).ln(2);
365 * @param boneMotion ボーンモーション
366 * @throws IOException 出力エラー
368 private void putBonePosition(BoneMotion boneMotion)
370 if(boneMotion.hasImplicitPosition()){
374 ind().putOpenSTag(VmdTag.BONE_POSITION.tag()).sp();
376 MkPos3D position = boneMotion.getPosition();
378 float xPos = (float) position.getXpos();
379 float yPos = (float) position.getYpos();
380 float zPos = (float) position.getZpos();
382 putFloatAttr(XmlAttr.ATTR_X_POS, xPos).sp();
383 putFloatAttr(XmlAttr.ATTR_Y_POS, yPos).sp();
384 putFloatAttr(XmlAttr.ATTR_Z_POS, zPos).sp();
386 PosCurve posCurve = boneMotion.getPosCurve();
387 if(posCurve.isDefaultLinear()){
388 putCloseEmpty().ln();
393 this.extraExporter.putPositionCurve(posCurve);
396 ind().putETag(VmdTag.BONE_POSITION.tag()).ln();
404 * @param boneMotion ボーンモーション
405 * @throws IOException 出力エラー
407 private void putBoneRotQuat(BoneMotion boneMotion)
409 MkQuat rotation = boneMotion.getRotation();
410 BezierParam rotCurve = boneMotion.getIntpltRotation();
412 ind().putOpenSTag(VmdTag.BONE_ROT_QUAT.tag()).ln();
415 float qx = (float) rotation.getQ1();
416 float qy = (float) rotation.getQ2();
417 float qz = (float) rotation.getQ3();
418 float qw = (float) rotation.getQW();
420 ind().putFloatAttr(XmlAttr.ATTR_QX, qx).ln();
421 ind().putFloatAttr(XmlAttr.ATTR_QY, qy).ln();
422 ind().putFloatAttr(XmlAttr.ATTR_QZ, qz).ln();
423 ind().putFloatAttr(XmlAttr.ATTR_QW, qw).ln();
428 if(rotCurve.isDefaultLinear()){
429 putCloseEmpty().ln();
434 this.extraExporter.putBezierCurve(rotCurve);
437 ind().putETag(VmdTag.BONE_ROT_QUAT.tag()).ln();
445 * @param boneMotion ボーンモーション
446 * @throws IOException 出力エラー
448 private void putBoneRotEyxz(BoneMotion boneMotion)
450 MkQuat rotation = boneMotion.getRotation();
451 BezierParam rotCurve = boneMotion.getIntpltRotation();
453 EulerYXZ euler = new EulerYXZ();
454 rotation.toEulerYXZ(euler);
455 float xDeg = (float)StrictMath.toDegrees(euler.getXRot());
456 float yDeg = (float)StrictMath.toDegrees(euler.getYRot());
457 float zDeg = (float)StrictMath.toDegrees(euler.getZRot());
459 ind().putOpenSTag(VmdTag.BONE_ROT_EYXZ.tag()).ln();
461 ind().putFloatAttr(XmlAttr.ATTR_X_DEG, xDeg).ln();
462 ind().putFloatAttr(XmlAttr.ATTR_Y_DEG, yDeg).ln();
463 ind().putFloatAttr(XmlAttr.ATTR_Z_DEG, zDeg).ln();
467 if(rotCurve.isDefaultLinear()){
468 putCloseEmpty().ln();
473 this.extraExporter.putBezierCurve(rotCurve);
476 ind().putETag(VmdTag.BONE_ROT_EYXZ.tag()).ln();
484 * @param vmdMotion モーションデータ
485 * @throws IOException 出力エラー
487 private void putMorphSequence(VmdMotion vmdMotion)
489 ind().putSimpleSTag(VmdTag.MORPH_SEQUENCE.tag()).ln();
492 Map<String, List<MorphMotion>> listMap = vmdMotion.getMorphPartMap();
493 if( ! listMap.isEmpty() ) ln();
494 putMorphPartList(listMap);
497 ind().putETag(VmdTag.MORPH_SEQUENCE.tag()).ln(2);
504 * @param listMap モーフデータの名前付きリストマップ
505 * @throws IOException 出力エラー
507 private void putMorphPartList(Map<String, List<MorphMotion>> listMap)
509 for(Map.Entry<String, List<MorphMotion>> entry : listMap.entrySet()){
510 String morphName = entry.getKey();
511 List<MorphMotion> list = entry.getValue();
513 if(VmdUniq.isBaseMorphName(morphName)) continue;
515 ind().putLineComment(morphName);
516 String globalName = TypicalMorph.primary2global(morphName);
517 if(globalName != null){
519 MessageFormat.format(MSG_MAYBE, globalName);
520 sp(2).putLineComment(gname);
524 ind().putOpenSTag(VmdTag.MORPH_PART.tag()).sp();
525 putAttr(XmlAttr.ATTR_NAME, morphName).sp();
529 for(MorphMotion morph : list){
530 putMorphMotion(morph);
534 ind().putETag(VmdTag.MORPH_PART.tag()).ln(2);
542 * @param morphMotion モーフモーション
543 * @throws IOException 出力エラー
545 private void putMorphMotion(MorphMotion morphMotion)
547 ind().putOpenSTag(VmdTag.MORPH_MOTION.tag()).sp();
549 int frameNo = morphMotion.getFrameNumber();
550 float flex = morphMotion.getFlex();
552 putIntAttr(XmlAttr.ATTR_FRAME, frameNo).sp();
553 putFloatAttr(XmlAttr.ATTR_FLEX, flex).sp();
555 putCloseEmpty().ln();