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.sourceforge.mikutoga.xml.BasicXmlExporter;
27 import jp.sourceforge.mikutoga.xml.XmlResourceResolver;
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 boolean isQuaternionMode = true;
47 private String generator = "";
49 private final CameraXmlExporter cameraXmlExporter;
50 private final LightingXmlExpoter lightingExporter;
51 private final ExtraXmlExporter extraExporter;
57 public VmdXmlExporter(){
60 this.cameraXmlExporter = new CameraXmlExporter(this);
61 this.lightingExporter = new LightingXmlExpoter(this);
62 this.extraExporter = new ExtraXmlExporter(this);
69 * ボーン回転量をクォータニオンで出力するか否か設定する。
71 * @param mode trueだとクォータニオン、falseだとオイラー角で出力される。
73 public void setQuaternionMode(boolean mode){
74 this.isQuaternionMode = mode;
78 * ボーン回転量をクォータニオンで出力するか否か返す。
79 * @return クォータニオンで出力するならtrue
81 public boolean isQuaternionMode(){
82 return this.isQuaternionMode;
87 * @param generatorArg Generatorメタ情報。nullならXML出力しない。
89 public void setGenerator(String generatorArg){
90 this.generator = generatorArg;
96 * @return Generatorメタ情報。XML出力しないならnullを返す。
98 public String getGenerator(){
99 return this.generator;
103 * VMDモーションデータをXML形式で出力する。
104 * @param vmdMotion VMDモーションデータ
106 * @throws IOException 出力エラー
107 * @throws IllegalVmdDataException 不正なモーションデータを検出
109 public void putVmdXml(VmdMotion vmdMotion, Appendable xmlOut)
110 throws IOException, IllegalVmdDataException{
111 setAppendable(xmlOut);
114 putVmdXmlImpl(vmdMotion);
122 * VMDモーションデータをXML形式で出力する。
123 * @param vmdMotion VMDモーションデータ
124 * @throws IOException 出力エラー
125 * @throws IllegalVmdDataException 不正なモーションデータを検出
127 private void putVmdXmlImpl(VmdMotion vmdMotion)
128 throws IOException, IllegalVmdDataException{
129 ind().putRawText(XML_DECL).ln(2);
131 ind().putBlockComment(XmlComment.TOP_COMMENT).ln(2);
133 ind().putOpenSTag(VmdTag.VMD_MOTION.tag()).ln();
135 ind().putAttr("xmlns", Schema110820.NS_VMDXML).ln();
136 ind().putAttr("xmlns:" + XSINS, XmlResourceResolver.NS_XSD).ln();
138 ind().putRawText(XSINS).putRawText(":schemaLocation=")
140 putRawText(Schema110820.NS_VMDXML).ln();
141 ind().sp(2).putRawText(Schema110820.SCHEMA_VMDXML)
145 ind().putAttr(XmlAttr.ATTR_VERSION, Schema110820.VER_VMDXML).ln();
147 putCloseSTag().ln(2);
151 if(vmdMotion.isModelMotion()){
152 putModelName(vmdMotion);
153 putBoneMotionSequence(vmdMotion);
154 putMorphSequence(vmdMotion);
156 this.cameraXmlExporter.putCameraSequence(vmdMotion);
157 this.lightingExporter.putLuminousSequence(vmdMotion);
158 this.lightingExporter.putShadowSequence(vmdMotion);
161 ind().putETag(VmdTag.VMD_MOTION.tag()).ln(2);
162 ind().putLineComment("EOF").ln();
169 * @throws IOException 出力エラー
171 private void putGenerator() throws IOException{
172 String genTxt = getGenerator();
173 if(genTxt == null) return;
174 if(genTxt.isEmpty()) return;
176 ind().putOpenSTag(VmdTag.META.tag()).sp();
177 putAttr(XmlAttr.ATTR_NAME, "generator").sp();
178 putAttr(XmlAttr.ATTR_CONTENT, genTxt).sp();
179 putCloseEmpty().ln(2);
186 * @param vmdMotion モーションデータ
187 * @throws IOException 出力エラー
189 private void putModelName(VmdMotion vmdMotion)
191 String modelName = vmdMotion.getModelName();
194 if(modelName != null && modelName.length() > 0){
195 modelComm = modelName;
197 modelComm = "[NAMELESS]";
200 ind().putLineComment(modelComm).ln();
201 ind().putOpenSTag(VmdTag.MODEL_NAME.tag()).sp();
202 putAttr(XmlAttr.ATTR_NAME, modelName).sp();
203 putCloseEmpty().ln(2);
210 * @param vmdMotion モーションデータ
211 * @throws IOException 出力エラー
213 private void putBoneMotionSequence(VmdMotion vmdMotion)
215 Map<String, List<BoneMotion>> boneMap = vmdMotion.getBonePartMap();
217 if( ! boneMap.isEmpty() ){
218 ind().putBlockComment(XmlComment.QUATERNION_COMMENT);
219 ind().putBlockComment(XmlComment.BEZIER_COMMENT);
222 ind().putSimpleSTag(VmdTag.BONE_M_SEQUENCE.tag()).ln();
225 if( ! boneMap.isEmpty() ) ln();
226 for(Map.Entry<String, List<BoneMotion>> entry : boneMap.entrySet()){
227 putBonePart(entry.getKey(), entry.getValue());
231 ind().putETag(VmdTag.BONE_M_SEQUENCE.tag()).ln(2);
238 * @param boneName ボーン名
239 * @param list ボーンモーションのリスト
240 * @throws IOException 出力エラー
242 private void putBonePart(String boneName, List<BoneMotion> list)
244 ind().putLineComment(boneName);
245 String globalName = TypicalBone.primary2global(boneName);
246 if(globalName != null){
248 MessageFormat.format(MSG_MAYBE, globalName);
249 sp(2).putLineComment(gname);
253 ind().putOpenSTag(VmdTag.BONE_PART.tag()).sp();
254 putAttr(XmlAttr.ATTR_NAME, boneName).sp();
255 putCloseSTag().ln(2);
258 for(BoneMotion bone : list){
263 ind().putETag(VmdTag.BONE_PART.tag()).ln(2);
270 * @param boneMotion ボーンモーション
271 * @throws IOException 出力エラー
273 private void putBoneMotion(BoneMotion boneMotion)
275 ind().putOpenSTag(VmdTag.BONE_MOTION.tag()).sp();
276 int frameNo = boneMotion.getFrameNumber();
277 putIntAttr(XmlAttr.ATTR_FRAME, frameNo).sp();
281 putBonePosition(boneMotion);
282 if(isQuaternionMode()){
283 putBoneRotQuat(boneMotion);
285 putBoneRotEyxz(boneMotion);
289 ind().putETag(VmdTag.BONE_MOTION.tag()).ln(2);
296 * @param boneMotion ボーンモーション
297 * @throws IOException 出力エラー
299 private void putBonePosition(BoneMotion boneMotion)
301 if(boneMotion.hasImplicitPosition()){
305 ind().putOpenSTag(VmdTag.BONE_POSITION.tag()).sp();
307 MkPos3D position = boneMotion.getPosition();
309 float xPos = (float) position.getXpos();
310 float yPos = (float) position.getYpos();
311 float zPos = (float) position.getZpos();
313 putFloatAttr(XmlAttr.ATTR_X_POS, xPos).sp();
314 putFloatAttr(XmlAttr.ATTR_Y_POS, yPos).sp();
315 putFloatAttr(XmlAttr.ATTR_Z_POS, zPos).sp();
317 PosCurve posCurve = boneMotion.getPosCurve();
318 if(posCurve.isDefaultLinear()){
319 putCloseEmpty().ln();
324 this.extraExporter.putPositionCurve(posCurve);
327 ind().putETag(VmdTag.BONE_POSITION.tag()).ln();
335 * @param boneMotion ボーンモーション
336 * @throws IOException 出力エラー
338 private void putBoneRotQuat(BoneMotion boneMotion)
340 MkQuat rotation = boneMotion.getRotation();
341 BezierParam rotCurve = boneMotion.getIntpltRotation();
343 ind().putOpenSTag(VmdTag.BONE_ROT_QUAT.tag()).ln();
346 float qx = (float) rotation.getQ1();
347 float qy = (float) rotation.getQ2();
348 float qz = (float) rotation.getQ3();
349 float qw = (float) rotation.getQW();
351 ind().putFloatAttr(XmlAttr.ATTR_QX, qx).ln();
352 ind().putFloatAttr(XmlAttr.ATTR_QY, qy).ln();
353 ind().putFloatAttr(XmlAttr.ATTR_QZ, qz).ln();
354 ind().putFloatAttr(XmlAttr.ATTR_QW, qw).ln();
359 if(rotCurve.isDefaultLinear()){
360 putCloseEmpty().ln();
365 this.extraExporter.putBezierCurve(rotCurve);
368 ind().putETag(VmdTag.BONE_ROT_QUAT.tag()).ln();
376 * @param boneMotion ボーンモーション
377 * @throws IOException 出力エラー
379 private void putBoneRotEyxz(BoneMotion boneMotion)
381 MkQuat rotation = boneMotion.getRotation();
382 BezierParam rotCurve = boneMotion.getIntpltRotation();
384 EulerYXZ euler = new EulerYXZ();
385 rotation.toEulerYXZ(euler);
386 float xDeg = (float)StrictMath.toDegrees(euler.getXRot());
387 float yDeg = (float)StrictMath.toDegrees(euler.getYRot());
388 float zDeg = (float)StrictMath.toDegrees(euler.getZRot());
390 ind().putOpenSTag(VmdTag.BONE_ROT_EYXZ.tag()).ln();
392 ind().putFloatAttr(XmlAttr.ATTR_X_DEG, xDeg).ln();
393 ind().putFloatAttr(XmlAttr.ATTR_Y_DEG, yDeg).ln();
394 ind().putFloatAttr(XmlAttr.ATTR_Z_DEG, zDeg).ln();
398 if(rotCurve.isDefaultLinear()){
399 putCloseEmpty().ln();
404 this.extraExporter.putBezierCurve(rotCurve);
407 ind().putETag(VmdTag.BONE_ROT_EYXZ.tag()).ln();
415 * @param vmdMotion モーションデータ
416 * @throws IOException 出力エラー
418 private void putMorphSequence(VmdMotion vmdMotion)
420 ind().putSimpleSTag(VmdTag.MORPH_SEQUENCE.tag()).ln();
423 Map<String, List<MorphMotion>> listMap = vmdMotion.getMorphPartMap();
424 if( ! listMap.isEmpty() ) ln();
425 putMorphPartList(listMap);
428 ind().putETag(VmdTag.MORPH_SEQUENCE.tag()).ln(2);
435 * @param listMap モーフデータの名前付きリストマップ
436 * @throws IOException 出力エラー
438 private void putMorphPartList(Map<String, List<MorphMotion>> listMap)
440 for(Map.Entry<String, List<MorphMotion>> entry : listMap.entrySet()){
441 String morphName = entry.getKey();
442 List<MorphMotion> list = entry.getValue();
444 if(VmdUniq.isBaseMorphName(morphName)) continue;
446 ind().putLineComment(morphName);
447 String globalName = TypicalMorph.primary2global(morphName);
448 if(globalName != null){
450 MessageFormat.format(MSG_MAYBE, globalName);
451 sp(2).putLineComment(gname);
455 ind().putOpenSTag(VmdTag.MORPH_PART.tag()).sp();
456 putAttr(XmlAttr.ATTR_NAME, morphName).sp();
460 for(MorphMotion morph : list){
461 putMorphMotion(morph);
465 ind().putETag(VmdTag.MORPH_PART.tag()).ln(2);
473 * @param morphMotion モーフモーション
474 * @throws IOException 出力エラー
476 private void putMorphMotion(MorphMotion morphMotion)
478 ind().putOpenSTag(VmdTag.MORPH_MOTION.tag()).sp();
480 int frameNo = morphMotion.getFrameNumber();
481 float flex = morphMotion.getFlex();
483 putIntAttr(XmlAttr.ATTR_FRAME, frameNo).sp();
484 putFloatAttr(XmlAttr.ATTR_FLEX, flex).sp();
486 putCloseEmpty().ln();