4 * License : The MIT License
5 * Copyright(c) 2011 MikuToga Partners
8 package jp.sourceforge.mikutoga.vmd.model.xml;
10 import java.io.IOException;
11 import java.io.OutputStream;
12 import java.util.List;
13 import jp.sourceforge.mikutoga.math.EulerYXZ;
14 import jp.sourceforge.mikutoga.math.MkPos3D;
15 import jp.sourceforge.mikutoga.math.MkQuat;
16 import jp.sourceforge.mikutoga.typical.TypicalBone;
17 import jp.sourceforge.mikutoga.typical.TypicalMorph;
18 import jp.sourceforge.mikutoga.vmd.IllegalVmdDataException;
19 import jp.sourceforge.mikutoga.vmd.VmdConst;
20 import jp.sourceforge.mikutoga.vmd.model.BezierParam;
21 import jp.sourceforge.mikutoga.vmd.model.BoneMotion;
22 import jp.sourceforge.mikutoga.vmd.model.CameraMotion;
23 import jp.sourceforge.mikutoga.vmd.model.CameraRotation;
24 import jp.sourceforge.mikutoga.vmd.model.LuminousColor;
25 import jp.sourceforge.mikutoga.vmd.model.LuminousMotion;
26 import jp.sourceforge.mikutoga.vmd.model.LuminousVector;
27 import jp.sourceforge.mikutoga.vmd.model.MorphMotion;
28 import jp.sourceforge.mikutoga.vmd.model.NamedListMap;
29 import jp.sourceforge.mikutoga.vmd.model.PosCurve;
30 import jp.sourceforge.mikutoga.vmd.model.ShadowMode;
31 import jp.sourceforge.mikutoga.vmd.model.ShadowMotion;
32 import jp.sourceforge.mikutoga.vmd.model.VmdMotion;
33 import jp.sourceforge.mikutoga.xml.BasicXmlExporter;
34 import jp.sourceforge.mikutoga.xml.XmlResourceResolver;
37 * VMDモーションデータをXMLへエクスポートする。
39 public class VmdXmlExporter extends BasicXmlExporter {
41 private static final String XSINS = "xsi";
43 private static final String TOP_COMMENT =
44 " MikuMikuDance\n motion-data(*.vmd) on XML";
46 private static final String QUATERNION_COMMENT =
47 " bone-rotation has Quaternion parameters [boneRotQuat]\n"
48 + " or YXZ-Euler angles [boneRotEyxz].\n"
49 + " Quaternion is strongly recommended"
50 + " if you are data-exchanging.";
52 private static final String BEZIER_COMMENT =
53 " motion interpolation is defined by Bezier-cubic-curve.\n"
54 + " implicit bezier curve point : P0=(0,0) P3=(127,127)\n"
55 + " defLinear : MMD default linear curve."
56 + " P1=(20,20) P2=(107,107) [DEFAULT]\n"
57 + " defEaseInOut : MMD default ease-in-out curve."
58 + " P1=(64,0) P2=(64,127)";
60 private static final String CAMERA_COMMENT =
61 " camera-rotation has polar-coordinates parameters.\n"
62 + " xRad = -radian(UI_X) [latitude]\n"
63 + " yRad = radian(UI_Y) [longitude]\n"
64 + " zRad = radian(UI_Z) [roll]\n"
65 + " range = -(UI_RANGE)";
67 private static final String SHADOW_COMMENT =
68 " UI_VALUE = EFFECTIVE_RANGE * 100 ???\n"
69 +" rawParam = 0.1 - (UI_VALUE / 1.0E+5)\n\n"
70 +" NONE : no self-shadow\n"
71 +" MODE_1 : reduce shadow-quality suddenly at range\n"
72 +" MODE_2 : reduce shadow-quality gradually with range";
75 private boolean isQuaternionMode = true;
76 private String generator = "";
81 * 文字エンコーディングはUTF-8が用いられる。
82 * @param stream 出力ストリーム
84 public VmdXmlExporter(OutputStream stream){
91 * ボーン回転量をクォータニオンで出力するか否か設定する。
93 * @param mode trueだとクォータニオン、falseだとオイラー角で出力される。
95 public void setQuaternionMode(boolean mode){
96 this.isQuaternionMode = mode;
100 * Generatorメタ情報を設定する。
101 * @param generatorArg Generatorメタ情報
102 * @throws NullPointerException 引数がnull
104 public void setGenerator(String generatorArg)
105 throws NullPointerException{
106 if(generatorArg == null) throw new NullPointerException();
107 this.generator = generatorArg;
112 * VMDモーションデータをXML形式で出力する。
113 * <p>モーションデータと演出データで振り分けられる。
114 * @param vmdMotion VMDモーションデータ
115 * @throws IOException 出力エラー
116 * @throws IllegalVmdDataException 不正なモーションデータを検出
118 public void putVmdXml(VmdMotion vmdMotion)
119 throws IOException, IllegalVmdDataException{
121 putVmdXmlImpl(vmdMotion);
129 * VMDモーションデータをXML形式で出力する。
130 * <p>モーションデータと演出データで振り分けられる。
131 * @param vmdMotion VMDモーションデータ
132 * @throws IOException 出力エラー
133 * @throws IllegalVmdDataException 不正なモーションデータを検出
135 private void putVmdXmlImpl(VmdMotion vmdMotion)
136 throws IOException, IllegalVmdDataException{
137 ind().putRawText("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>")
140 ind().putBlockComment(TOP_COMMENT).ln(2);
142 ind().putRawText("<vmdMotion").ln();
144 ind().putAttr("xmlns", VmdXmlResources.NS_VMDXML).ln();
145 ind().putAttr("xmlns:" + XSINS, XmlResourceResolver.NS_XSD).ln();
147 ind().putRawText(XSINS).putRawText(":schemaLocation=")
149 putRawText(VmdXmlResources.NS_VMDXML).ln();
150 ind().sp(2).putRawText(VmdXmlResources.SCHEMAURI_VMDXML)
154 ind().putAttr("version", VmdXmlResources.VER_VMDXML).ln();
156 putRawText(">").ln(2);
158 if(this.generator != null && this.generator.length() > 0){
159 ind().putRawText("<meta ");
160 putAttr("name", "generator").putRawCh(' ');
161 putAttr("content", this.generator);
162 putRawText(" />").ln(2);
165 if(vmdMotion.isModelMotion()){
166 putModelName(vmdMotion);
167 putBoneMotionSequence(vmdMotion);
168 putMorphSequence(vmdMotion);
170 putCameraSequence(vmdMotion);
171 putLuminousSequence(vmdMotion);
172 putShadowSequence(vmdMotion);
175 ind().putRawText("</vmdMotion>").ln(2);
176 ind().putRawText("<!-- EOF -->").ln();
183 * @param posCurve 移動補間情報
184 * @throws IOException 出力エラー
186 private void putPositionCurve(PosCurve posCurve)
188 BezierParam xCurve = posCurve.getIntpltXpos();
189 BezierParam yCurve = posCurve.getIntpltYpos();
190 BezierParam zCurve = posCurve.getIntpltZpos();
192 ind().putLineComment("X-Y-Z interpolation *3").ln();
195 putBezierCurve(xCurve);
199 putBezierCurve(yCurve);
203 putBezierCurve(zCurve);
210 * ベジェ曲線による補間曲線情報を出力する。
211 * @param bezier ベジェ曲線
212 * @throws IOException 出力エラー
214 private void putBezierCurve(BezierParam bezier)
216 if(bezier.isDefaultLinear()){
217 putRawText("<defLinear />");
218 }else if(bezier.isDefaultEaseInOut()){
219 putRawText("<defEaseInOut />");
221 putRawText("<bezier ");
222 putIntAttr("p1x", bezier.getP1x()).sp();
223 putIntAttr("p1y", bezier.getP1y()).sp();
224 putIntAttr("p2x", bezier.getP2x()).sp();
225 putIntAttr("p2y", bezier.getP2y()).sp();
233 * @param vmdMotion モーションデータ
234 * @throws IOException 出力エラー
236 private void putModelName(VmdMotion vmdMotion)
238 String modelName = vmdMotion.getModelName();
240 ind().putLineComment(modelName).ln();
241 ind().putRawText("<modelName ");
242 putAttr("name", modelName).sp();
243 putRawText("/>").ln(2);
250 * @param vmdMotion モーションデータ
251 * @throws IOException 出力エラー
253 private void putBoneMotionSequence(VmdMotion vmdMotion)
255 ind().putBlockComment(QUATERNION_COMMENT);
256 ind().putBlockComment(BEZIER_COMMENT);
258 ind().putRawText("<boneMotionSequence>").ln();
261 NamedListMap<BoneMotion> listmap = vmdMotion.getBonePartMap();
262 if( ! listmap.isEmpty() ) ln();
263 for(String boneName : listmap.getNames()){
264 List<BoneMotion> list = listmap.getNamedList(boneName);
265 putBonePart(boneName, list);
269 ind().putRawText("</boneMotionSequence>").ln(2);
276 * @param boneName ボーン名
277 * @param list ボーンモーションのリスト
278 * @throws IOException 出力エラー
280 private void putBonePart(String boneName, List<BoneMotion> list)
282 ind().putLineComment(boneName);
283 String globalName = TypicalBone.primary2global(boneName);
284 if(globalName != null){
285 sp(2).putLineComment("Perhaps : [" + globalName + "]");
289 ind().putRawText("<bonePart ");
290 putAttr("name", boneName).sp();
291 putRawText(">").ln(2);
294 for(BoneMotion bone : list){
299 ind().putRawText("</bonePart>").ln(2);
306 * @param boneMotion ボーンモーション
307 * @throws IOException 出力エラー
309 private void putBoneMotion(BoneMotion boneMotion)
311 ind().putRawText("<boneMotion ");
312 int frameNo = boneMotion.getFrameNumber();
313 putIntAttr("frame", frameNo).sp();
314 putRawText(">").ln();
317 putBonePosition(boneMotion);
318 if(this.isQuaternionMode){
319 putBoneRotQuat(boneMotion);
321 putBoneRotEyxz(boneMotion);
325 ind().putRawText("</boneMotion>").ln(2);
332 * @param boneMotion ボーンモーション
333 * @throws IOException 出力エラー
335 private void putBonePosition(BoneMotion boneMotion)
337 if(boneMotion.hasImplicitPosition()){
341 ind().putRawText("<bonePosition ");
342 MkPos3D position = boneMotion.getPosition();
343 float xPos = (float) position.getXpos();
344 float yPos = (float) position.getYpos();
345 float zPos = (float) position.getZpos();
346 putFloatAttr("xPos", xPos).sp();
347 putFloatAttr("yPos", yPos).sp();
348 putFloatAttr("zPos", zPos).sp();
350 PosCurve posCurve = boneMotion.getPosCurve();
351 if(posCurve.isDefaultLinear()){
352 putRawText("/>").ln();
354 putRawText(">").ln();
357 putPositionCurve(posCurve);
360 ind().putRawText("</bonePosition>").ln();
368 * @param boneMotion ボーンモーション
369 * @throws IOException 出力エラー
371 private void putBoneRotQuat(BoneMotion boneMotion)
373 MkQuat rotation = boneMotion.getRotation();
374 BezierParam rotCurve = boneMotion.getIntpltRotation();
376 ind().putRawText("<boneRotQuat").ln();
378 ind().putFloatAttr("qx", (float) rotation.getQ1()).ln();
379 ind().putFloatAttr("qy", (float) rotation.getQ2()).ln();
380 ind().putFloatAttr("qz", (float) rotation.getQ3()).ln();
381 ind().putFloatAttr("qw", (float) rotation.getQW()).ln();
385 if(rotCurve.isDefaultLinear()){
386 putRawText("/>").ln();
388 putRawText(">").ln();
391 putBezierCurve(rotCurve);
394 ind().putRawText("</boneRotQuat>").ln();
402 * @param boneMotion ボーンモーション
403 * @throws IOException 出力エラー
405 private void putBoneRotEyxz(BoneMotion boneMotion)
407 MkQuat rotation = boneMotion.getRotation();
408 BezierParam rotCurve = boneMotion.getIntpltRotation();
410 EulerYXZ euler = new EulerYXZ();
411 rotation.toEulerYXZ(euler);
412 float xDeg = (float)StrictMath.toDegrees(euler.getXRot());
413 float yDeg = (float)StrictMath.toDegrees(euler.getYRot());
414 float zDeg = (float)StrictMath.toDegrees(euler.getZRot());
416 ind().putRawText("<boneRotEyxz").ln();
418 ind().putFloatAttr("xDeg", xDeg).ln();
419 ind().putFloatAttr("yDeg", yDeg).ln();
420 ind().putFloatAttr("zDeg", zDeg).ln();
424 if(rotCurve.isDefaultLinear()){
425 putRawText("/>").ln();
427 putRawText(">").ln();
430 putBezierCurve(rotCurve);
433 ind().putRawText("</boneRotEyxz>").ln();
441 * @param vmdMotion モーションデータ
442 * @throws IOException 出力エラー
444 private void putMorphSequence(VmdMotion vmdMotion)
446 ind().putRawText("<morphSequence>").ln();
449 NamedListMap<MorphMotion> listmap = vmdMotion.getMorphPartMap();
450 if( ! listmap.isEmpty() ) ln();
451 putMorphPartList(listmap);
454 ind().putRawText("</morphSequence>").ln(2);
461 * @param listmap モーフデータの名前付きリストマップ
462 * @throws IOException 出力エラー
464 private void putMorphPartList(NamedListMap<MorphMotion> listmap)
466 for(String morphName : listmap.getNames()){
467 if(VmdConst.isBaseMorphName(morphName)) continue;
469 ind().putLineComment(morphName);
470 String globalName = TypicalMorph.primary2global(morphName);
471 if(globalName != null){
472 sp(2).putLineComment("Perhaps : [" + globalName + "]");
476 ind().putRawText("<morphPart ");
477 putAttr("name", morphName).sp();
478 putRawText(">").ln();
481 List<MorphMotion> list = listmap.getNamedList(morphName);
482 for(MorphMotion morph : list){
483 putMorphMotion(morph);
487 ind().putRawText("</morphPart>").ln(2);
495 * @param morphMotion モーフモーション
496 * @throws IOException 出力エラー
498 private void putMorphMotion(MorphMotion morphMotion)
500 ind().putRawText("<morphMotion ");
502 int frameNo = morphMotion.getFrameNumber();
503 float flex = morphMotion.getFlex();
505 putIntAttr("frame", frameNo).sp();
506 putFloatAttr("flex", flex).sp();
508 putRawText("/>").ln();
515 * @param vmdMotion 演出データ
516 * @throws IOException 出力エラー
518 private void putCameraSequence(VmdMotion vmdMotion)
520 ind().putBlockComment(BEZIER_COMMENT);
521 ind().putBlockComment(CAMERA_COMMENT);
523 ind().putRawText("<cameraSequence>").ln();
526 List<CameraMotion> list = vmdMotion.getCameraMotionList();
527 if( ! list.isEmpty() ) ln();
528 for(CameraMotion camera : list){
529 putCameraMotion(camera);
533 ind().putRawText("</cameraSequence>").ln(2);
540 * @param cameraMotion カメラモーション
541 * @throws IOException 出力エラー
543 private void putCameraMotion(CameraMotion cameraMotion)
545 ind().putRawText("<cameraMotion ");
546 int frameNo = cameraMotion.getFrameNumber();
547 putIntAttr("frame", frameNo).sp();
548 if( ! cameraMotion.hasPerspective() ){
549 putAttr("hasPerspective", "false").sp();
551 putRawText(">").ln();
554 putCameraTarget(cameraMotion);
555 putCameraRotation(cameraMotion);
556 putCameraRange(cameraMotion);
557 putProjection(cameraMotion);
560 ind().putRawText("</cameraMotion>").ln(2);
567 * @param cameraMotion カメラモーション
568 * @throws IOException 出力エラー
570 private void putCameraTarget(CameraMotion cameraMotion)
572 ind().putRawText("<cameraTarget ");
573 MkPos3D position = cameraMotion.getCameraTarget();
574 putFloatAttr("xPos", (float) position.getXpos()).sp();
575 putFloatAttr("yPos", (float) position.getYpos()).sp();
576 putFloatAttr("zPos", (float) position.getZpos()).sp();
578 PosCurve posCurve = cameraMotion.getTargetPosCurve();
579 if(posCurve.isDefaultLinear()){
580 putRawText("/>").ln();
582 putRawText(">").ln();
585 putPositionCurve(posCurve);
588 ind().putRawText("</cameraTarget>").ln();
596 * @param cameraMotion カメラモーション
597 * @throws IOException 出力エラー
599 private void putCameraRotation(CameraMotion cameraMotion)
601 ind().putRawText("<cameraRotation ");
602 CameraRotation rotation = cameraMotion.getCameraRotation();
603 putFloatAttr("xRad", rotation.getLatitude()).sp();
604 putFloatAttr("yRad", rotation.getLongitude()).sp();
605 putFloatAttr("zRad", rotation.getRoll()).sp();
607 BezierParam rotCurve = cameraMotion.getIntpltRotation();
608 if(rotCurve.isDefaultLinear()){
609 putRawText("/>").ln();
611 putRawText(">").ln();
614 putBezierCurve(rotCurve);
617 ind().putRawText("</cameraRotation>").ln();
625 * @param cameraMotion カメラモーション
626 * @throws IOException 出力エラー
628 private void putCameraRange(CameraMotion cameraMotion)
630 ind().putRawText("<cameraRange ");
631 float range = cameraMotion.getRange();
632 putFloatAttr("range", range).sp();
634 BezierParam rangeCurve = cameraMotion.getIntpltRange();
635 if(rangeCurve.isDefaultLinear()){
636 putRawText("/>").ln();
638 putRawText(">").ln();
641 putBezierCurve(rangeCurve);
644 ind().putRawText("</cameraRange>").ln();
652 * @param cameraMotion カメラモーション
653 * @throws IOException 出力エラー
655 private void putProjection(CameraMotion cameraMotion)
657 ind().putRawText("<projection ");
658 putIntAttr("vertDeg", cameraMotion.getProjectionAngle()).sp();
660 BezierParam projCurve = cameraMotion.getIntpltProjection();
661 if(projCurve.isDefaultLinear()){
662 putRawText("/>").ln();
664 putRawText(">").ln();
667 putBezierCurve(projCurve);
670 ind().putRawText("</projection>").ln();
678 * @param vmdMotion 演出データ
679 * @throws IOException 出力エラー
681 private void putLuminousSequence(VmdMotion vmdMotion)
683 ind().putRawText("<luminousSequence>").ln();
686 List<LuminousMotion> list = vmdMotion.getLuminousMotionList();
687 if( ! list.isEmpty() ) ln();
688 for(LuminousMotion luminous : list){
689 putLuminousMotion(luminous);
693 ind().putRawText("</luminousSequence>").ln(2);
700 * @param luminousMotion 照明モーション
701 * @throws IOException 出力エラー
703 private void putLuminousMotion(LuminousMotion luminousMotion)
705 ind().putRawText("<luminousAct ");
706 int frameNo = luminousMotion.getFrameNumber();
707 putIntAttr("frame", frameNo);
708 putRawText(" >").ln();
710 LuminousColor color = luminousMotion.getColor();
711 LuminousVector vector = luminousMotion.getDirection();
714 putLuminousColor(color);
715 putLuminousDirection(vector);
718 ind().putRawText("</luminousAct>").ln(2);
726 * @throws IOException 出力エラー
728 private void putLuminousColor(LuminousColor color)
730 ind().putRawText("<lumiColor ");
731 putFloatAttr("rCol", color.getColR()).sp();
732 putFloatAttr("gCol", color.getColG()).sp();
733 putFloatAttr("bCol", color.getColB()).sp();
734 putRawText("/>").ln();
742 * @throws IOException 出力エラー
744 private void putLuminousDirection(LuminousVector vector)
746 ind().putRawText("<lumiDirection ");
747 putFloatAttr("xVec", vector.getVecX()).sp();
748 putFloatAttr("yVec", vector.getVecY()).sp();
749 putFloatAttr("zVec", vector.getVecZ()).sp();
750 putRawText("/>").ln();
757 * @param vmdMotion 演出データ
758 * @throws IOException 出力エラー
760 private void putShadowSequence(VmdMotion vmdMotion)
762 ind().putBlockComment(SHADOW_COMMENT);
764 ind().putRawText("<shadowSequence>").ln();
767 List<ShadowMotion> list = vmdMotion.getShadowMotionList();
768 for(ShadowMotion shadow : list){
769 putShadowMotion(shadow);
773 ind().putRawText("</shadowSequence>").ln(2);
780 * @param shadowMotion シャドウモーション
781 * @throws IOException 出力エラー
783 private void putShadowMotion(ShadowMotion shadowMotion)
785 ind().putRawText("<shadowAct ");
787 int frameNo = shadowMotion.getFrameNumber();
788 ShadowMode mode = shadowMotion.getShadowMode();
789 float rawParam = shadowMotion.getRawScopeParam();
791 putIntAttr("frame", frameNo).sp();
792 putAttr("mode", mode.name()).sp();
793 putFloatAttr("rawParam", rawParam).sp();
797 double uiVal = ShadowMotion.rawParamToScope(rawParam);
798 long lVal = Math.round(uiVal);
799 sp().putLineComment("UI:" + lVal).ln();