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().put("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>").ln(2);
139 ind().putBlockComment(TOP_COMMENT).ln(2);
141 ind().put("<vmdMotion").ln();
143 ind().putAttr("xmlns", VmdXmlResources.NS_VMDXML).ln();
144 ind().putAttr("xmlns:" + XSINS, XmlResourceResolver.NS_XSD).ln();
146 ind().put(XSINS).put(":schemaLocation=").put('"');
147 put(VmdXmlResources.NS_VMDXML).ln();
148 ind().sp(2).put(VmdXmlResources.SCHEMAURI_VMDXML).put('"').ln();
150 ind().putAttr("version", VmdXmlResources.VER_VMDXML).ln();
154 if(this.generator != null && this.generator.length() > 0){
156 putAttr("name", "generator").put(' ');
157 putAttr("content", this.generator);
161 if(vmdMotion.isModelMotion()){
162 putModelName(vmdMotion);
163 putBoneMotionSequence(vmdMotion);
164 putMorphSequence(vmdMotion);
166 putCameraSequence(vmdMotion);
167 putLuminousSequence(vmdMotion);
168 putShadowSequence(vmdMotion);
171 ind().put("</vmdMotion>").ln(2);
172 ind().put("<!-- EOF -->").ln();
179 * @param posCurve 移動補間情報
180 * @throws IOException 出力エラー
182 private void putPositionCurve(PosCurve posCurve)
184 BezierParam xCurve = posCurve.getIntpltXpos();
185 BezierParam yCurve = posCurve.getIntpltYpos();
186 BezierParam zCurve = posCurve.getIntpltZpos();
188 ind().putLineComment("X-Y-Z interpolation *3").ln();
191 putBezierCurve(xCurve);
195 putBezierCurve(yCurve);
199 putBezierCurve(zCurve);
206 * ベジェ曲線による補間曲線情報を出力する。
207 * @param bezier ベジェ曲線
208 * @throws IOException 出力エラー
210 private void putBezierCurve(BezierParam bezier)
212 if(bezier.isDefaultLinear()){
213 put("<defLinear />");
214 }else if(bezier.isDefaultEaseInOut()){
215 put("<defEaseInOut />");
218 putIntAttr("p1x", bezier.getP1x()).sp();
219 putIntAttr("p1y", bezier.getP1y()).sp();
220 putIntAttr("p2x", bezier.getP2x()).sp();
221 putIntAttr("p2y", bezier.getP2y()).sp();
229 * @param vmdMotion モーションデータ
230 * @throws IOException 出力エラー
232 private void putModelName(VmdMotion vmdMotion)
234 String modelName = vmdMotion.getModelName();
236 ind().putLineComment(modelName).ln();
237 ind().put("<modelName ");
238 putAttr("name", modelName).sp();
246 * @param vmdMotion モーションデータ
247 * @throws IOException 出力エラー
249 private void putBoneMotionSequence(VmdMotion vmdMotion)
251 ind().putBlockComment(QUATERNION_COMMENT);
252 ind().putBlockComment(BEZIER_COMMENT);
254 ind().put("<boneMotionSequence>").ln();
257 NamedListMap<BoneMotion> listmap = vmdMotion.getBonePartMap();
258 if( ! listmap.isEmpty() ) ln();
259 for(String boneName : listmap.getNames()){
260 List<BoneMotion> list = listmap.getNamedList(boneName);
261 putBonePart(boneName, list);
265 ind().put("</boneMotionSequence>").ln(2);
272 * @param boneName ボーン名
273 * @param list ボーンモーションのリスト
274 * @throws IOException 出力エラー
276 private void putBonePart(String boneName, List<BoneMotion> list)
278 ind().putLineComment(boneName);
279 String globalName = TypicalBone.primary2global(boneName);
280 if(globalName != null){
281 sp(2).putLineComment("Perhaps : [" + globalName + "]");
285 ind().put("<bonePart ");
286 putAttr("name", boneName).sp();
290 for(BoneMotion bone : list){
295 ind().put("</bonePart>").ln(2);
302 * @param boneMotion ボーンモーション
303 * @throws IOException 出力エラー
305 private void putBoneMotion(BoneMotion boneMotion)
307 ind().put("<boneMotion ");
308 int frameNo = boneMotion.getFrameNumber();
309 putIntAttr("frame", frameNo).sp();
313 putBonePosition(boneMotion);
314 if(this.isQuaternionMode){
315 putBoneRotQuat(boneMotion);
317 putBoneRotEyxz(boneMotion);
321 ind().put("</boneMotion>").ln(2);
328 * @param boneMotion ボーンモーション
329 * @throws IOException 出力エラー
331 private void putBonePosition(BoneMotion boneMotion)
333 if(boneMotion.hasImplicitPosition()){
337 ind().put("<bonePosition ");
338 MkPos3D position = boneMotion.getPosition();
339 float xPos = (float) position.getXpos();
340 float yPos = (float) position.getYpos();
341 float zPos = (float) position.getZpos();
342 putFloatAttr("xPos", xPos).sp();
343 putFloatAttr("yPos", yPos).sp();
344 putFloatAttr("zPos", zPos).sp();
346 PosCurve posCurve = boneMotion.getPosCurve();
347 if(posCurve.isDefaultLinear()){
353 putPositionCurve(posCurve);
356 ind().put("</bonePosition>").ln();
364 * @param boneMotion ボーンモーション
365 * @throws IOException 出力エラー
367 private void putBoneRotQuat(BoneMotion boneMotion)
369 MkQuat rotation = boneMotion.getRotation();
370 BezierParam rotCurve = boneMotion.getIntpltRotation();
372 ind().put("<boneRotQuat").ln();
374 ind().putFloatAttr("qx", (float) rotation.getQ1()).ln();
375 ind().putFloatAttr("qy", (float) rotation.getQ2()).ln();
376 ind().putFloatAttr("qz", (float) rotation.getQ3()).ln();
377 ind().putFloatAttr("qw", (float) rotation.getQW()).ln();
381 if(rotCurve.isDefaultLinear()){
387 putBezierCurve(rotCurve);
390 ind().put("</boneRotQuat>").ln();
398 * @param boneMotion ボーンモーション
399 * @throws IOException 出力エラー
401 private void putBoneRotEyxz(BoneMotion boneMotion)
403 MkQuat rotation = boneMotion.getRotation();
404 BezierParam rotCurve = boneMotion.getIntpltRotation();
406 EulerYXZ euler = new EulerYXZ();
407 rotation.toEulerYXZ(euler);
408 float xDeg = (float)StrictMath.toDegrees(euler.getXRot());
409 float yDeg = (float)StrictMath.toDegrees(euler.getYRot());
410 float zDeg = (float)StrictMath.toDegrees(euler.getZRot());
412 ind().put("<boneRotEyxz").ln();
414 ind().putFloatAttr("xDeg", xDeg).ln();
415 ind().putFloatAttr("yDeg", yDeg).ln();
416 ind().putFloatAttr("zDeg", zDeg).ln();
420 if(rotCurve.isDefaultLinear()){
426 putBezierCurve(rotCurve);
429 ind().put("</boneRotEyxz>").ln();
437 * @param vmdMotion モーションデータ
438 * @throws IOException 出力エラー
440 private void putMorphSequence(VmdMotion vmdMotion)
442 ind().put("<morphSequence>").ln();
445 NamedListMap<MorphMotion> listmap = vmdMotion.getMorphPartMap();
446 if( ! listmap.isEmpty() ) ln();
447 putMorphPartList(listmap);
450 ind().put("</morphSequence>").ln(2);
457 * @param listmap モーフデータの名前付きリストマップ
458 * @throws IOException 出力エラー
460 private void putMorphPartList(NamedListMap<MorphMotion> listmap)
462 for(String morphName : listmap.getNames()){
463 if(VmdConst.isBaseMorphName(morphName)) continue;
465 ind().putLineComment(morphName);
466 String globalName = TypicalMorph.primary2global(morphName);
467 if(globalName != null){
468 sp(2).putLineComment("Perhaps : [" + globalName + "]");
472 ind().put("<morphPart ");
473 putAttr("name", morphName).sp();
477 List<MorphMotion> list = listmap.getNamedList(morphName);
478 for(MorphMotion morph : list){
479 putMorphMotion(morph);
483 ind().put("</morphPart>").ln(2);
491 * @param morphMotion モーフモーション
492 * @throws IOException 出力エラー
494 private void putMorphMotion(MorphMotion morphMotion)
496 ind().put("<morphMotion ");
498 int frameNo = morphMotion.getFrameNumber();
499 float flex = morphMotion.getFlex();
501 putIntAttr("frame", frameNo).sp();
502 putFloatAttr("flex", flex).sp();
511 * @param vmdMotion 演出データ
512 * @throws IOException 出力エラー
514 private void putCameraSequence(VmdMotion vmdMotion)
516 ind().putBlockComment(BEZIER_COMMENT);
517 ind().putBlockComment(CAMERA_COMMENT);
519 ind().put("<cameraSequence>").ln();
522 List<CameraMotion> list = vmdMotion.getCameraMotionList();
523 if( ! list.isEmpty() ) ln();
524 for(CameraMotion camera : list){
525 putCameraMotion(camera);
529 ind().put("</cameraSequence>").ln(2);
536 * @param cameraMotion カメラモーション
537 * @throws IOException 出力エラー
539 private void putCameraMotion(CameraMotion cameraMotion)
541 ind().put("<cameraMotion ");
542 int frameNo = cameraMotion.getFrameNumber();
543 putIntAttr("frame", frameNo).sp();
544 if( ! cameraMotion.hasPerspective() ){
545 putAttr("hasPerspective", "false").sp();
550 putCameraTarget(cameraMotion);
551 putCameraRotation(cameraMotion);
552 putCameraRange(cameraMotion);
553 putProjection(cameraMotion);
556 ind().put("</cameraMotion>").ln(2);
563 * @param cameraMotion カメラモーション
564 * @throws IOException 出力エラー
566 private void putCameraTarget(CameraMotion cameraMotion)
568 ind().put("<cameraTarget ");
569 MkPos3D position = cameraMotion.getCameraTarget();
570 putFloatAttr("xPos", (float) position.getXpos()).sp();
571 putFloatAttr("yPos", (float) position.getYpos()).sp();
572 putFloatAttr("zPos", (float) position.getZpos()).sp();
574 PosCurve posCurve = cameraMotion.getTargetPosCurve();
575 if(posCurve.isDefaultLinear()){
581 putPositionCurve(posCurve);
584 ind().put("</cameraTarget>").ln();
592 * @param cameraMotion カメラモーション
593 * @throws IOException 出力エラー
595 private void putCameraRotation(CameraMotion cameraMotion)
597 ind().put("<cameraRotation ");
598 CameraRotation rotation = cameraMotion.getCameraRotation();
599 putFloatAttr("xRad", rotation.getLatitude()).sp();
600 putFloatAttr("yRad", rotation.getLongitude()).sp();
601 putFloatAttr("zRad", rotation.getRoll()).sp();
603 BezierParam rotCurve = cameraMotion.getIntpltRotation();
604 if(rotCurve.isDefaultLinear()){
610 putBezierCurve(rotCurve);
613 ind().put("</cameraRotation>").ln();
621 * @param cameraMotion カメラモーション
622 * @throws IOException 出力エラー
624 private void putCameraRange(CameraMotion cameraMotion)
626 ind().put("<cameraRange ");
627 float range = cameraMotion.getRange();
628 putFloatAttr("range", range).sp();
630 BezierParam rangeCurve = cameraMotion.getIntpltRange();
631 if(rangeCurve.isDefaultLinear()){
637 putBezierCurve(rangeCurve);
640 ind().put("</cameraRange>").ln();
648 * @param cameraMotion カメラモーション
649 * @throws IOException 出力エラー
651 private void putProjection(CameraMotion cameraMotion)
653 ind().put("<projection ");
654 putIntAttr("vertDeg", cameraMotion.getProjectionAngle()).sp();
656 BezierParam projCurve = cameraMotion.getIntpltProjection();
657 if(projCurve.isDefaultLinear()){
663 putBezierCurve(projCurve);
666 ind().put("</projection>").ln();
674 * @param vmdMotion 演出データ
675 * @throws IOException 出力エラー
677 private void putLuminousSequence(VmdMotion vmdMotion)
679 ind().put("<luminousSequence>").ln();
682 List<LuminousMotion> list = vmdMotion.getLuminousMotionList();
683 if( ! list.isEmpty() ) ln();
684 for(LuminousMotion luminous : list){
685 putLuminousMotion(luminous);
689 ind().put("</luminousSequence>").ln(2);
696 * @param luminousMotion 照明モーション
697 * @throws IOException 出力エラー
699 private void putLuminousMotion(LuminousMotion luminousMotion)
701 ind().put("<luminousAct ");
702 int frameNo = luminousMotion.getFrameNumber();
703 putIntAttr("frame", frameNo);
706 LuminousColor color = luminousMotion.getColor();
707 LuminousVector vector = luminousMotion.getDirection();
710 putLuminousColor(color);
711 putLuminousDirection(vector);
714 ind().put("</luminousAct>").ln(2);
722 * @throws IOException 出力エラー
724 private void putLuminousColor(LuminousColor color)
726 ind().put("<lumiColor ");
727 putFloatAttr("rCol", color.getColR()).sp();
728 putFloatAttr("gCol", color.getColG()).sp();
729 putFloatAttr("bCol", color.getColB()).sp();
738 * @throws IOException 出力エラー
740 private void putLuminousDirection(LuminousVector vector)
742 ind().put("<lumiDirection ");
743 putFloatAttr("xVec", vector.getVecX()).sp();
744 putFloatAttr("yVec", vector.getVecY()).sp();
745 putFloatAttr("zVec", vector.getVecZ()).sp();
753 * @param vmdMotion 演出データ
754 * @throws IOException 出力エラー
756 private void putShadowSequence(VmdMotion vmdMotion)
758 ind().putBlockComment(SHADOW_COMMENT);
760 ind().put("<shadowSequence>").ln();
763 List<ShadowMotion> list = vmdMotion.getShadowMotionList();
764 for(ShadowMotion shadow : list){
765 putShadowMotion(shadow);
769 ind().put("</shadowSequence>").ln(2);
776 * @param shadowMotion シャドウモーション
777 * @throws IOException 出力エラー
779 private void putShadowMotion(ShadowMotion shadowMotion)
781 ind().put("<shadowAct ");
783 int frameNo = shadowMotion.getFrameNumber();
784 ShadowMode mode = shadowMotion.getShadowMode();
785 float rawParam = shadowMotion.getRawScopeParam();
787 putIntAttr("frame", frameNo).sp();
788 putAttr("mode", mode.name()).sp();
789 putFloatAttr("rawParam", rawParam).sp();
793 double uiVal = ShadowMotion.rawParamToScope(rawParam);
794 long lVal = Math.round(uiVal);
795 sp().putLineComment("UI:" + lVal).ln();