From f331d569481d6e2cd5cbcad842554e0495df2cfd Mon Sep 17 00:00:00 2001 From: Olyutorskii Date: Wed, 24 Aug 2011 20:42:39 +0900 Subject: [PATCH] =?utf8?q?TogaGem=E3=81=8B=E3=82=89=E3=81=AE=E3=83=91?= =?utf8?q?=E3=83=83=E3=82=B1=E3=83=BC=E3=82=B8=E7=A7=BB=E7=AE=A1=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- .../sourceforge/mikutoga/pmd/model/BoneGroup.java | 124 ++ .../sourceforge/mikutoga/pmd/model/BoneInfo.java | 205 +++ .../mikutoga/pmd/model/DynamicsInfo.java | 138 ++ .../jp/sourceforge/mikutoga/pmd/model/IKChain.java | 133 ++ .../sourceforge/mikutoga/pmd/model/JointInfo.java | 153 ++ .../sourceforge/mikutoga/pmd/model/ListUtil.java | 145 ++ .../sourceforge/mikutoga/pmd/model/Material.java | 246 ++++ .../sourceforge/mikutoga/pmd/model/MorphPart.java | 112 ++ .../mikutoga/pmd/model/MorphVertex.java | 133 ++ .../sourceforge/mikutoga/pmd/model/PmdModel.java | 360 +++++ .../sourceforge/mikutoga/pmd/model/RigidGroup.java | 101 ++ .../sourceforge/mikutoga/pmd/model/RigidInfo.java | 207 +++ .../sourceforge/mikutoga/pmd/model/RigidShape.java | 150 ++ .../mikutoga/pmd/model/SerialNumbered.java | 67 + .../sourceforge/mikutoga/pmd/model/ShadeInfo.java | 140 ++ .../jp/sourceforge/mikutoga/pmd/model/Surface.java | 177 +++ .../jp/sourceforge/mikutoga/pmd/model/ToonMap.java | 147 ++ .../jp/sourceforge/mikutoga/pmd/model/Vertex.java | 221 +++ .../mikutoga/pmd/model/binio/BoneBuilder.java | 290 ++++ .../mikutoga/pmd/model/binio/JointBuilder.java | 216 +++ .../mikutoga/pmd/model/binio/MaterialBuilder.java | 188 +++ .../mikutoga/pmd/model/binio/MorphBuilder.java | 194 +++ .../mikutoga/pmd/model/binio/PmdExporter.java | 30 + .../mikutoga/pmd/model/binio/PmdExporterBase.java | 647 +++++++++ .../mikutoga/pmd/model/binio/PmdExporterExt1.java | 166 +++ .../mikutoga/pmd/model/binio/PmdExporterExt2.java | 78 + .../mikutoga/pmd/model/binio/PmdExporterExt3.java | 283 ++++ .../mikutoga/pmd/model/binio/PmdLoader.java | 117 ++ .../mikutoga/pmd/model/binio/RigidBuilder.java | 227 +++ .../mikutoga/pmd/model/binio/ShapeBuilder.java | 224 +++ .../mikutoga/pmd/model/binio/TextBuilder.java | 246 ++++ .../mikutoga/pmd/model/binio/ToonBuilder.java | 83 ++ .../mikutoga/pmd/model/binio/package-info.java | 14 + .../mikutoga/pmd/model/xml/PmdXmlExporter.java | 1294 +++++++++++++++++ .../mikutoga/pmd/model/xml/PmdXmlResources.java | 105 ++ .../mikutoga/pmd/model/xml/Xml2PmdLoader.java | 977 +++++++++++++ .../mikutoga/pmd/model/xml/package-info.java | 14 + .../jp/sourceforge/mikutoga/pmd2xml/Pmd2Xml.java | 34 +- .../pmd/model/xml/resources/pmdxml-101009.dtd | 532 +++++++ .../pmd/model/xml/resources/pmdxml-101009.xsd | 1511 ++++++++++++++++++++ 40 files changed, 10412 insertions(+), 17 deletions(-) create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/model/BoneGroup.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/model/BoneInfo.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/model/DynamicsInfo.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/model/IKChain.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/model/JointInfo.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/model/ListUtil.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/model/Material.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/model/MorphPart.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/model/MorphVertex.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/model/PmdModel.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/model/RigidGroup.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/model/RigidInfo.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/model/RigidShape.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/model/SerialNumbered.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/model/ShadeInfo.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/model/Surface.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/model/ToonMap.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/model/Vertex.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/BoneBuilder.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/JointBuilder.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/MaterialBuilder.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/MorphBuilder.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/PmdExporter.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/PmdExporterBase.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/PmdExporterExt1.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/PmdExporterExt2.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/PmdExporterExt3.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/PmdLoader.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/RigidBuilder.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/ShapeBuilder.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/TextBuilder.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/ToonBuilder.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/package-info.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/model/xml/PmdXmlExporter.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/model/xml/PmdXmlResources.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/model/xml/Xml2PmdLoader.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/model/xml/package-info.java create mode 100644 src/main/resources/jp/sourceforge/mikutoga/pmd/model/xml/resources/pmdxml-101009.dtd create mode 100644 src/main/resources/jp/sourceforge/mikutoga/pmd/model/xml/resources/pmdxml-101009.xsd diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/model/BoneGroup.java b/src/main/java/jp/sourceforge/mikutoga/pmd/model/BoneGroup.java new file mode 100644 index 0000000..6c598d2 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/model/BoneGroup.java @@ -0,0 +1,124 @@ +/* + * bone group + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.model; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import jp.sourceforge.mikutoga.corelib.I18nText; + +/** + * ボーングループ。 + * ボーングループ名と0個以上のボーンを配下に持つ。 + * 通し番号0のボーングループは、暗黙に用意される「デフォルトボーングループ」とする。 + */ +public class BoneGroup implements SerialNumbered , Iterable { + + private final I18nText groupName = new I18nText(); + + private final List boneList = new ArrayList(); + + private int serialNo = -1; + + /** + * コンストラクタ。 + */ + public BoneGroup(){ + super(); + return; + } + + /** + * ボーングループ名を返す。 + * @return ボーングループ名 + */ + public I18nText getGroupName(){ + return this.groupName; + } + + /** + * ボーンリストを取得する。 + * @return ボーンリスト + */ + public List getBoneList(){ + return this.boneList; + } + + /** + * デフォルトボーングループか否か判定する。 + * 通し番号が0ならデフォルトボーングループ。 + * @return デフォルトボーングループならtrue + */ + public boolean isDefaultBoneGroup(){ + if(this.serialNo == 0) return true; + return false; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public Iterator iterator(){ + return this.boneList.iterator(); + } + + /** + * {@inheritDoc} + * @param num {@inheritDoc} + */ + @Override + public void setSerialNumber(int num){ + this.serialNo = num; + return; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public int getSerialNumber(){ + return this.serialNo; + } + + /** + * ボーングループ番号(ボーン枠番号)を返す。 + * 常に通し番号より1少ない値となる。 + * デフォルトボーングループは-1となる。 + * @return ボーングループ番号 + */ + public int getBoneGroupNumber(){ + return this.serialNo - 1; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public String toString(){ + StringBuilder result = new StringBuilder(); + + result.append("BoneGroup(") + .append(this.groupName) + .append(") ["); + + boolean dumped = false; + for(BoneInfo bone : this){ + if(dumped) result.append(", "); + result.append(bone.getBoneName()); + dumped = true; + } + + result.append(']'); + + return result.toString(); + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/model/BoneInfo.java b/src/main/java/jp/sourceforge/mikutoga/pmd/model/BoneInfo.java new file mode 100644 index 0000000..5f17397 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/model/BoneInfo.java @@ -0,0 +1,205 @@ +/* + * bone information + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.model; + +import jp.sourceforge.mikutoga.corelib.I18nText; +import jp.sourceforge.mikutoga.math.MkPos3D; +import jp.sourceforge.mikutoga.pmd.BoneType; + +/** + * ボーン情報。 + */ +public class BoneInfo implements SerialNumbered { + + private final I18nText boneName = new I18nText(); + private BoneType boneType; + + private BoneInfo prevBone; + private BoneInfo nextBone; + private BoneInfo ikBone; + + private final MkPos3D position = new MkPos3D(); + + private int rotationRatio; + + private int serialNo = -1; + + /** + * コンストラクタ。 + */ + public BoneInfo(){ + super(); + return; + } + + /** + * ボーン名を返す。 + * @return ボーン名 + */ + public I18nText getBoneName(){ + return this.boneName; + } + + /** + * ボーン種別を設定する。 + * @param type ボーン種別 + * @throws NullPointerException 引数がnull + */ + public void setBoneType(BoneType type) throws NullPointerException{ + if(type == null) throw new NullPointerException(); + this.boneType = type; + return; + } + + /** + * ボーン種別を返す。 + * @return ボーン種別 + */ + public BoneType getBoneType(){ + return this.boneType; + } + + /** + * 親(前)ボーンを設定する。 + * @param prevBone 前ボーン。ない場合はnullを指定。 + */ + public void setPrevBone(BoneInfo prevBone){ + this.prevBone = prevBone; + return; + } + + /** + * 親(前)ボーンを返す。 + * @return 前ボーン。ない場合はnullを返す。 + */ + public BoneInfo getPrevBone(){ + return this.prevBone; + } + + /** + * 子(次)ボーンを設定する。 + * 捩りボーンでは軸方向に位置するボーン、 + * 回転連動ボーンでは影響元ボーン。 + * @param nextBone 次ボーン。ない場合はnullを指定。 + */ + public void setNextBone(BoneInfo nextBone){ + this.nextBone = nextBone; + return; + } + + /** + * 子(次)ボーンを返す。 + * 捩りボーンでは軸方向に位置するボーン、 + * 回転連動ボーンでは影響元ボーン。 + * @return 次ボーン。ない場合はnullを返す。 + */ + public BoneInfo getNextBone(){ + return this.nextBone; + } + + /** + * このボーンが影響を受けるIKボーンを設定する。 + * @param ikBoneArg IKボーン。ない場合はnullを指定。 + */ + public void setIKBone(BoneInfo ikBoneArg){ + this.ikBone = ikBoneArg; + return; + } + + /** + * このボーンが影響を受けるIKボーンを返す。 + * @return IKボーン。ない場合はnull + */ + public BoneInfo getIKBone(){ + return this.ikBone; + } + + /** + * ボーン位置を返す。 + * @return ボーン位置 + */ + public MkPos3D getPosition(){ + return this.position; + } + + /** + * 回転連動の影響度を返す。 + * 回転連動ボーンの場合のみ有効。 + * @return 回転連動の影響度 + */ + public int getRotationRatio(){ + return this.rotationRatio; + } + + /** + * 回転連動の影響度を設定する。 + * 回転連動ボーンの場合のみ有効。 + * @param ratio 回転連動の影響度 + */ + public void setRotationRatio(int ratio){ + this.rotationRatio = ratio; + } + + /** + * {@inheritDoc} + * @param num {@inheritDoc} + */ + @Override + public void setSerialNumber(int num){ + this.serialNo = num; + return; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public int getSerialNumber(){ + return this.serialNo; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public String toString(){ + StringBuilder result = new StringBuilder(); + + result.append("Bone") + .append(this.serialNo) + .append("(") + .append(this.boneName.getPrimaryText()) + .append(")"); + + result.append(" type=") + .append(this.boneType); + + result.append(" prev="); + if(this.prevBone != null) result.append(this.prevBone.getBoneName()); + else result.append("NONE"); + + result.append(" next="); + if(this.nextBone != null) result.append(this.nextBone.getBoneName()); + else result.append("NONE"); + + if(this.boneType == BoneType.LINKEDROT){ + result.append(" rotraio=").append(this.rotationRatio); + }else{ + result.append(" ik="); + if(this.ikBone != null) result.append(this.ikBone.getBoneName()); + else result.append("NONE"); + } + + result.append(" ").append(this.position); + + return result.toString(); + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/model/DynamicsInfo.java b/src/main/java/jp/sourceforge/mikutoga/pmd/model/DynamicsInfo.java new file mode 100644 index 0000000..6b6f68e --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/model/DynamicsInfo.java @@ -0,0 +1,138 @@ +/* + * dynamics parameter + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.model; + +/** + * 剛体間力学演算の各種パラメータ。 + * 各剛体に設定可能なパラメータは + * 「質量」、「移動減衰率」、「回転減衰率」、「反発力」、「摩擦力」の5種類。 + */ +public class DynamicsInfo { + + /** 質量。 */ + private float mass; + /** 移動減衰率。 */ + private float dampingPos; + /** 回転減衰率。 */ + private float dampingRot; + /** 反発力。 */ + private float restitution; + /** 摩擦力。 */ + private float friction; + + /** + * コンストラクタ。 + */ + public DynamicsInfo(){ + super(); + return; + } + + /** + * 質量を返す。 + * @return 質量 + */ + public float getMass(){ + return this.mass; + } + + /** + * 質量を設定する。 + * @param mass 質量 + */ + public void setMass(float mass){ + this.mass = mass; + return; + } + + /** + * 移動減衰率を返す。 + * @return 移動減衰率 + */ + public float getDampingPosition(){ + return this.dampingPos; + } + + /** + * 移動減衰率を設定する。 + * @param damping 移動減衰率 + */ + public void setDampingPosition(float damping){ + this.dampingPos = damping; + return; + } + + /** + * 回転減衰率を返す。 + * @return 回転減衰率 + */ + public float getDampingRotation(){ + return this.dampingRot; + } + + /** + * 回転減衰率を設定する。 + * @param damping 回転減衰率 + */ + public void setDampingRotation(float damping){ + this.dampingRot = damping; + return; + } + + /** + * 反発力を返す。 + * @return 反発力 + */ + public float getRestitution(){ + return this.restitution; + } + + /** + * 反発力を設定する。 + * @param restitution 反発力 + */ + public void setRestitution(float restitution){ + this.restitution = restitution; + return; + } + + /** + * 摩擦力を返す。 + * @return 摩擦力 + */ + public float getFriction(){ + return this.friction; + } + + /** + * 摩擦力を設定する。 + * @param friction 摩擦力 + */ + public void setFriction(float friction){ + this.friction = friction; + return; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public String toString(){ + StringBuilder result = new StringBuilder(); + + result.append("mass=").append(this.mass).append(", "); + result.append("damping(Pos)=").append(this.dampingPos).append(", "); + result.append("damping(Rot)=").append(this.dampingRot).append(", "); + result.append("restitution=").append(this.restitution).append(", "); + result.append("friction=").append(this.friction); + + return result.toString(); + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/model/IKChain.java b/src/main/java/jp/sourceforge/mikutoga/pmd/model/IKChain.java new file mode 100644 index 0000000..ece4d56 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/model/IKChain.java @@ -0,0 +1,133 @@ +/* + * IK chained bone + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.model; + +import jp.sourceforge.mikutoga.pmd.model.BoneInfo; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * IK連鎖ボーン群。 + */ +public class IKChain implements Iterable { + + private BoneInfo ikBone; + + private int ikDepth; + private float ikWeight; + + private final List chainList = new ArrayList(); + + /** + * コンストラクタ。 + */ + public IKChain(){ + super(); + return; + } + + /** + * IKボーンを設定する。 + * @param bone IKボーン + */ + public void setIkBone(BoneInfo bone){ + this.ikBone = bone; + return; + } + + /** + * IKボーンを返す。 + * @return IKボーン + */ + public BoneInfo getIkBone(){ + return this.ikBone; + } + + /** + * IK演算再帰深度を設定する。 + * @param depth IK演算再帰深度 + */ + public void setIKDepth(int depth){ + this.ikDepth = depth; + return; + } + + /** + * IK演算再帰深度を返す。 + * @return IK演算再帰深度 + */ + public int getIKDepth(){ + return this.ikDepth; + } + + /** + * IKウェイトを設定する。 + * @param weight IKウェイト + */ + public void setIKWeight(float weight){ + this.ikWeight = weight; + return; + } + + /** + * IKウェイトを返す。 + * @return IKウェイト + */ + public float getIKWeight(){ + return this.ikWeight; + } + + /** + * IK連鎖ボーンリストを返す。 + * 最初の要素は必ずIK接続先ボーン。それ以降はIK影響下ボーン。 + * @return IK連鎖ボーンリスト + */ + public List getChainedBoneList(){ + return this.chainList; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public Iterator iterator(){ + return this.chainList.iterator(); + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public String toString(){ + StringBuilder result = new StringBuilder(); + + result.append("IKChain"); + + result.append(" depth:").append(this.ikDepth); + result.append(" weight:").append(this.ikWeight); + + result.append(" IKbone:").append(this.ikBone.getBoneName()); + + result.append(" ["); + + boolean chaindumped = false; + for(BoneInfo chain : this.chainList){ + if(chaindumped) result.append(" => "); + result.append(chain.getBoneName()); + chaindumped = true; + } + + result.append("]"); + + return result.toString(); + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/model/JointInfo.java b/src/main/java/jp/sourceforge/mikutoga/pmd/model/JointInfo.java new file mode 100644 index 0000000..38967ae --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/model/JointInfo.java @@ -0,0 +1,153 @@ +/* + * joint information + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.model; + +import jp.sourceforge.mikutoga.corelib.I18nText; +import jp.sourceforge.mikutoga.math.MkPos3D; +import jp.sourceforge.mikutoga.pmd.Deg3d; +import jp.sourceforge.mikutoga.pmd.Rad3d; +import jp.sourceforge.mikutoga.pmd.TripletRange; + +/** + * 剛体間ジョイント情報。 + */ +public class JointInfo { + + private final I18nText jointName = new I18nText(); + + private RigidInfo rigidA; + private RigidInfo rigidB; + + private final MkPos3D position = new MkPos3D(); + private final Rad3d rotation = new Rad3d(); + + private final MkPos3D elaPosition = new MkPos3D(); + private final Deg3d elaRotation = new Deg3d(); + + private final TripletRange posRange = new TripletRange(); + private final TripletRange rotRange = new TripletRange(); + + /** + * コンストラクタ。 + */ + public JointInfo(){ + super(); + return; + } + + /** + * ジョイント名を返す。 + * @return ジョイント名 + */ + public I18nText getJointName(){ + return this.jointName; + } + + /** + * 連結剛体Aを返す。 + * @return 連結剛体A + */ + public RigidInfo getRigidA(){ + return this.rigidA; + } + + /** + * 連結剛体Bを返す。 + * @return 連結剛体B + */ + public RigidInfo getRigidB(){ + return this.rigidB; + } + + /** + * 連結する剛体を設定する。 + * @param rigidA 連結剛体A + * @param rigidB 連結剛体B + */ + public void setRigidPair(RigidInfo rigidA, RigidInfo rigidB){ + this.rigidA = rigidA; + this.rigidB = rigidB; + return; + } + + /** + * ジョイントの位置を返す。 + * @return ジョイントの位置 + */ + public MkPos3D getPosition(){ + return this.position; + } + + /** + * ジョイントの姿勢を返す。 + * @return ジョイントの姿勢 + */ + public Rad3d getRotation(){ + return this.rotation; + } + + /** + * ジョイントのバネ位置を返す。 + * @return ジョイントのバネ位置 + */ + public MkPos3D getElasticPosition(){ + return this.elaPosition; + } + + /** + * ジョイントのバネ姿勢を返す。 + * @return ジョイントのバネ姿勢 + */ + public Deg3d getElasticRotation(){ + return this.elaRotation; + } + + /** + * ジョイントの位置制約を返す。 + * @return ジョイントの位置制約 + */ + public TripletRange getPositionRange(){ + return this.posRange; + } + + /** + * ジョイントの姿勢制約を返す。 + * @return ジョイントの姿勢制約 + */ + public TripletRange getRotationRange(){ + return this.rotRange; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public String toString(){ + StringBuilder result = new StringBuilder(); + + result.append("Joint "); + result.append(this.jointName); + result.append("[") + .append(this.rigidA.getRigidName()) + .append("<=>") + .append(this.rigidB.getRigidName()) + .append("] "); + result.append(this.position).append(' '); + result.append(this.rotation).append(' '); + + result.append("poslim{").append(this.posRange).append("} "); + result.append("rotlim{").append(this.rotRange).append("} "); + + result.append("ela:").append(this.elaPosition).append(' '); + result.append("ela:").append(this.elaRotation); + + return result.toString(); + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/model/ListUtil.java b/src/main/java/jp/sourceforge/mikutoga/pmd/model/ListUtil.java new file mode 100644 index 0000000..0cc79cb --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/model/ListUtil.java @@ -0,0 +1,145 @@ +/* + * List utilities + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.model; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.List; + +/** + * リスト構造の各種ユーティリティ。 + */ +public final class ListUtil { + + /** + * 隠しコンストラクタ。 + */ + private ListUtil(){ + assert false; + throw new AssertionError(); + } + + /** + * リストの出現順にシリアルナンバーを割り振る。 + * シリアルナンバー先頭は0。 + * @param list リスト。 + */ + public static void assignIndexedSerial( + List list){ + int serial = 0; + for(SerialNumbered numbered : list){ + numbered.setSerialNumber(serial); + serial++; + } + + return; + } + + /** + * リストの要素数を拡張する。 + * 追加された要素にはnullが収められる。 + * リストがすでに指定サイズ以上の要素数を持つ場合、何もしない。 + * @param 型 + * @param list リスト + * @param newSize 新サイズ + */ + public static void extendList(List list, int newSize){ + int remain = newSize - list.size(); + if(remain <= 0) return; + + for(int ct = 1; ct <= remain; ct++){ + list.add(null); + } + + assert list.size() == newSize; + + return; + } + + /** + * リストのnull要素をデフォルトコンストラクタによるインスタンスで埋める。 + * null要素がなければなにもしない。 + * @param 型 + * @param list リスト + * @param cons コンストラクタ + * @return 埋めた数 + */ + public static int fillDefCons(List list, + Constructor cons){ + int result = 0; + + int size = list.size(); + for(int pt = 0; pt < size; pt++){ + E elem = list.get(pt); + if(elem != null) continue; + + try{ + elem = cons.newInstance(); + }catch(InstantiationException e){ + assert false; + throw new AssertionError(e); + }catch(IllegalAccessException e){ + assert false; + throw new AssertionError(e); + }catch(InvocationTargetException e){ + Throwable cause = e.getTargetException(); + if(cause instanceof RuntimeException){ + throw (RuntimeException) cause; + } + if(cause instanceof Error){ + throw (Error) cause; + } + assert false; + throw new AssertionError(e); + } + + list.set(pt, elem); + result++; + } + + return result; + } + + /** + * リストのnull要素をデフォルトコンストラクタによるインスタンスで埋める。 + * @param 型 + * @param list リスト + * @param klass デフォルトコンストラクタの属するクラス + * @return 埋めた数 + */ + public static int fillDefCons(List list, Class klass){ + Constructor cons; + try{ + cons = klass.getConstructor(); + }catch(NoSuchMethodException e){ + assert false; + throw new AssertionError(e); + } + + return fillDefCons(list, cons); + } + + /** + * リスト要素数の拡張とデフォルトコンストラクタによる要素埋めを行う。 + * 追加された要素および既存のnull要素には + * デフォルトコンストラクタによるインスタンスが収められる。 + * @param 型 + * @param list リスト + * @param klass デフォルトコンストラクタの属するクラス + * @param newSize 新サイズ + * @return 埋めた数。 + */ + public static int prepareDefConsList(List list, + Class klass, + int newSize ){ + extendList(list, newSize); + int result = fillDefCons(list, klass); + return result; + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/model/Material.java b/src/main/java/jp/sourceforge/mikutoga/pmd/model/Material.java new file mode 100644 index 0000000..4431227 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/model/Material.java @@ -0,0 +1,246 @@ +/* + * material information + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.model; + +import java.awt.Color; +import java.awt.Transparency; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import jp.sourceforge.mikutoga.corelib.I18nText; + +/** + * マテリアル素材情報。 + */ +public class Material implements Iterable { + + private final I18nText materialName = new I18nText(); + + private Color diffuseColor; + + private Color specularColor; + private float shininess; + + private Color ambientColor; + + private final ShadeInfo shadeInfo = new ShadeInfo(); + + private boolean edgeAppearance = true; + + private final List surfaceList = new ArrayList(); + + /** + * コンストラクタ。 + */ + public Material(){ + super(); + return; + } + + /** + * 色を不透明化する。 + * @param color 色 + * @return 不透明化した色。引数と同じ場合もありうる。 + */ + private static Color forceOpaque(Color color){ + if(color.getTransparency() == Transparency.OPAQUE){ + return color; + } + + float[] rgba = new float[4]; + color.getRGBColorComponents(rgba); + + Color result = new Color(rgba[0], rgba[1], rgba[2], 1.0f); + + return result; + } + + /** + * マテリアル名を返す。 + * PMDEditorのみでのサポート? + * @return マテリアル名 + */ + public I18nText getMaterialName(){ + return this.materialName; + } + + /** + * 拡散光を設定する。 + * アルファ成分も反映される。 + * @param color 拡散光 + * @throws NullPointerException 引数がnull + */ + public void setDiffuseColor(Color color) throws NullPointerException{ + if(color == null) throw new NullPointerException(); + this.diffuseColor = color; + return; + } + + /** + * 拡散光を返す。 + * @return 拡散光 + */ + public Color getDiffuseColor(){ + return this.diffuseColor; + } + + /** + * 反射光を設定する。 + * 透過成分があれば不透明化される。 + * @param color 反射光 + * @throws NullPointerException 引数がnull + */ + public void setSpecularColor(Color color) + throws NullPointerException{ + if(color == null) throw new NullPointerException(); + this.specularColor = forceOpaque(color); + return; + } + + /** + * 反射光を返す。 + * @return 反射光 + */ + public Color getSpecularColor(){ + return this.specularColor; + } + + /** + * 環境光を設定する。 + * 透過成分があれば不透明化される。 + * @param color 環境光 + * @throws NullPointerException 引数がnull + */ + public void setAmbientColor(Color color) + throws NullPointerException{ + if(color == null) throw new NullPointerException(); + this.ambientColor = forceOpaque(color); + return; + } + + /** + * 環境光を返す。 + * @return 環境光 + */ + public Color getAmbientColor(){ + return this.ambientColor; + } + + /** + * 光沢強度を設定する。 + * MMDで用いられるのは1.0〜15.0あたり。 + * @param shininess 光沢強度 + */ + public void setShininess(float shininess){ + this.shininess = shininess; + return; + } + + /** + * 光沢強度を返す。 + * @return 光沢強度 + */ + public float getShininess(){ + return this.shininess; + } + + /** + * シェーディング設定を返す。 + * @return シェーディング設定 + */ + public ShadeInfo getShadeInfo(){ + return this.shadeInfo; + } + + /** + * エッジを表示するか設定する。 + * 頂点単位の設定より優先度は低い。 + * @param show 表示するならtrue + */ + public void setEdgeAppearance(boolean show){ + this.edgeAppearance = show; + return; + } + + /** + * エッジを表示するか判定する。 + * 頂点単位の設定より優先度は低い。 + * @return 表示するならtrue + */ + public boolean getEdgeAppearance(){ + return this.edgeAppearance; + } + + /** + * 面リストを返す。 + * @return 面リスト + */ + public List getSurfaceList(){ + return this.surfaceList; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public Iterator iterator(){ + return this.surfaceList.iterator(); + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public String toString(){ + StringBuilder result = new StringBuilder(); + + result.append("Material "); + + float[] rgba = new float[4]; + + this.diffuseColor.getRGBComponents(rgba); + result.append("diffuse=[") + .append(rgba[0]).append(", ") + .append(rgba[1]).append(", ") + .append(rgba[2]).append(", ") + .append(rgba[3]).append(']') + .append(' '); + + this.specularColor.getRGBComponents(rgba); + result.append("specular=[") + .append(rgba[0]).append(", ") + .append(rgba[1]).append(", ") + .append(rgba[2]).append(", ") + .append(rgba[3]).append(']') + .append(' '); + + this.ambientColor.getRGBComponents(rgba); + result.append("ambient=[") + .append(rgba[0]).append(", ") + .append(rgba[1]).append(", ") + .append(rgba[2]).append(", ") + .append(rgba[3]).append(']') + .append(' '); + + result.append("shininess=") + .append(this.shininess) + .append(' '); + result.append("edge=") + .append(this.edgeAppearance) + .append(' '); + result.append("surfaces=") + .append(this.surfaceList.size()) + .append(' '); + result.append(this.shadeInfo); + + return result.toString(); + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/model/MorphPart.java b/src/main/java/jp/sourceforge/mikutoga/pmd/model/MorphPart.java new file mode 100644 index 0000000..6e6e26c --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/model/MorphPart.java @@ -0,0 +1,112 @@ +/* + * morph information + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.model; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import jp.sourceforge.mikutoga.corelib.I18nText; +import jp.sourceforge.mikutoga.pmd.MorphType; + +/** + * 個別モーフ情報。 + */ +public class MorphPart implements SerialNumbered, Iterable { + + private final I18nText morphName = new I18nText(); + + private MorphType type; + + private final List morphVertexList = + new ArrayList(); + + private int serialNo = -1; + + /** + * コンストラクタ。 + */ + public MorphPart(){ + super(); + return; + } + + /** + * モーフ名を返す。 + * @return モーフ名 + */ + public I18nText getMorphName(){ + return this.morphName; + } + + /** + * モーフ種別を設定する。 + * @param typeArg モーフ種別 + * @throws NullPointerException 引数がnull + */ + public void setMorphType(MorphType typeArg) throws NullPointerException{ + if(typeArg == null) throw new NullPointerException(); + this.type = typeArg; + return; + } + + /** + * モーフ種別を返す。 + * @return モーフ種別。 + */ + public MorphType getMorphType(){ + return this.type; + } + + /** + * モーフ頂点情報リストを返す。 + * @return モーフ頂点情報リスト + */ + public List getMorphVertexList(){ + return this.morphVertexList; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public Iterator iterator(){ + return this.morphVertexList.iterator(); + } + + /** + * {@inheritDoc} + * @param num {@inheritDoc} + */ + @Override + public void setSerialNumber(int num){ + this.serialNo = num; + return; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public int getSerialNumber(){ + return this.serialNo; + } + + @Override + public String toString(){ + StringBuilder result = new StringBuilder(); + + result.append("Morph(").append(this.morphName).append(") "); + result.append("type=").append(this.type); + result.append(" vertexNum=").append(this.morphVertexList.size()); + + return result.toString(); + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/model/MorphVertex.java b/src/main/java/jp/sourceforge/mikutoga/pmd/model/MorphVertex.java new file mode 100644 index 0000000..1fd04b7 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/model/MorphVertex.java @@ -0,0 +1,133 @@ +/* + * morph vertex information + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.model; + +import java.util.Comparator; +import jp.sourceforge.mikutoga.math.MkPos3D; + +/** + * モーフアニメーションを構成する個別の頂点移動の情報。 + */ +public class MorphVertex implements SerialNumbered{ + + /** 頂点IDを昇順に順序づけるComaparator。 */ + public static final Comparator VIDCOMPARATOR = + new VertexIdComparator(); + + private Vertex baseVertex; + private final MkPos3D offset = new MkPos3D(); + + private int serialNo = -1; + + /** + * コンストラクタ。 + */ + public MorphVertex(){ + super(); + return; + } + + /** + * 移動元頂点情報を返す。 + * @return 移動元頂点 + */ + public Vertex getBaseVertex(){ + return this.baseVertex; + } + + /** + * 移動元頂点情報を設定する。 + * @param vertex 移動元頂点 + * @throws NullPointerException 引数がnull + */ + public void setBaseVertex(Vertex vertex) throws NullPointerException{ + if(vertex == null) throw new NullPointerException(); + this.baseVertex = vertex; + return; + } + + /** + * 頂点移動量を返す。 + * @return 頂点移動量 + */ + public MkPos3D getOffset(){ + return this.offset; + } + + /** + * {@inheritDoc} + * @param num {@inheritDoc} + */ + @Override + public void setSerialNumber(int num){ + this.serialNo = num; + return; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public int getSerialNumber(){ + return this.serialNo; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public String toString(){ + StringBuilder result = new StringBuilder(); + + result.append("vid(") + .append(this.baseVertex.getSerialNumber()) + .append(") "); + result.append(this.baseVertex.getPosition()); + result.append(" >> "); + result.append(this.offset); + + return result.toString(); + } + + /** + * 頂点IDによる比較子Comparator。 + */ + private static final class VertexIdComparator + implements Comparator { + + /** + * コンストラクタ。 + */ + private VertexIdComparator(){ + super(); + return; + } + + /** + * {@inheritDoc} + * @param o1 {@inheritDoc} + * @param o2 {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public int compare(MorphVertex o1, MorphVertex o2){ + if(o1 == o2) return 0; + if(o1 == null) return -1; + if(o2 == null) return +1; + + int ser1 = o1.getBaseVertex().getSerialNumber(); + int ser2 = o2.getBaseVertex().getSerialNumber(); + + return ser1 - ser2; + } + + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/model/PmdModel.java b/src/main/java/jp/sourceforge/mikutoga/pmd/model/PmdModel.java new file mode 100644 index 0000000..bfa1b34 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/model/PmdModel.java @@ -0,0 +1,360 @@ +/* + * PMD model + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.model; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.RandomAccess; +import java.util.Set; +import jp.sourceforge.mikutoga.corelib.I18nText; +import jp.sourceforge.mikutoga.pmd.MorphType; + +/** + * PMDモデルファイル一式に相当するもの。 + * 様々な基本構造のリストの集合から構成される。 + */ +public class PmdModel { + + private final I18nText modelName = new I18nText(); + private final I18nText description = new I18nText(); + + private final List vertexList = new ArrayList(); + private final List surfaceList = new ArrayList(); + private final List materialList = new LinkedList(); + + private final List boneList = new ArrayList(); + private final List boneGroupList = new ArrayList(); + + private final List ikChainList = new ArrayList(); + + private final Map> morphMap = + new EnumMap>(MorphType.class); + + private final List rigidList = new ArrayList(); + private final List rigidGroupList = + new ArrayList(); + + private final List jointList = new ArrayList(); + + private ToonMap toonMap = new ToonMap(); + + /** + * コンストラクタ。 + */ + public PmdModel(){ + super(); + + assert this.vertexList instanceof RandomAccess; + assert this.surfaceList instanceof RandomAccess; + + this.morphMap.put(MorphType.EYEBROW, new ArrayList()); + this.morphMap.put(MorphType.EYE, new ArrayList()); + this.morphMap.put(MorphType.LIP, new ArrayList()); + this.morphMap.put(MorphType.EXTRA, new ArrayList()); + + return; + } + + /** + * モデル名を返す。 + * @return モデル名 + */ + public I18nText getModelName(){ + return this.modelName; + } + + /** + * モデル説明文を返す。 + * 改行表現には{@literal \n}が用いられる + * @return モデル説明文 + */ + public I18nText getDescription(){ + return this.description; + } + + /** + * 頂点リストを返す。 + * @return 頂点リスト。 + */ + public List getVertexList(){ + return this.vertexList; + } + + /** + * 面リストを返す。 + * @return 面リスト + */ + public List getSurfaceList(){ + return this.surfaceList; + } + + /** + * 素材リストを返す。 + * @return 素材リスト + */ + public List getMaterialList(){ + return this.materialList; + } + + /** + * ボーンリストを返す。 + * @return ボーンリスト + */ + public List getBoneList(){ + return this.boneList; + } + + /** + * ボーングループリストを返す。 + * @return ボーングループリスト + */ + public List getBoneGroupList(){ + return this.boneGroupList; + } + + /** + * IKチェーンリストを返す。 + * @return IKチェーンリスト + */ + public List getIKChainList(){ + return this.ikChainList; + } + + /** + * 種類別モーフリストのマップを返す。 + * @return 種類別モーフリストのマップ + */ + public Map> getMorphMap(){ + return this.morphMap; + } + + /** + * 剛体リストを返す。 + * @return 剛体リスト + */ + public List getRigidList(){ + return this.rigidList; + } + + /** + * 剛体グループリストを返す。 + * @return 剛体グループリスト。 + */ + public List getRigidGroupList(){ + return this.rigidGroupList; + } + + /** + * 剛体間ジョイントリストを返す。 + * @return 剛体間ジョイントリスト + */ + public List getJointList(){ + return this.jointList; + } + + /** + * トゥーンファイルマップを返す。 + * @return トゥーンファイルマップ + */ + public ToonMap getToonMap(){ + return this.toonMap; + } + + /** + * トゥーンファイルマップを設定する。 + * 各素材のシェーディングで参照するトゥーンファイルマップも更新される。 + * @param map トゥーンファイルマップ + */ + public void setToonMap(ToonMap map){ + this.toonMap = map; + for(Material material : this.materialList){ + ShadeInfo info = material.getShadeInfo(); + info.setToonMap(this.toonMap); + } + return; + } + + /** + * このモデルがグローバル名を含むか判定する。 + * ボーン名、ボーングループ名、モーフ名、モデル説明文が判定対象。 + * @return グローバル名を持つならtrue + */ + public boolean hasGlobalText(){ + if(this.modelName.hasGlobalText()) return true; + if(this.description.hasGlobalText()) return true; + + for(BoneInfo bone : this.boneList){ + if(bone.getBoneName().hasGlobalText()) return true; + } + + List typeList = new ArrayList(); + typeList.addAll(this.morphMap.keySet()); + for(MorphType type : typeList){ + List partList = this.morphMap.get(type); + for(MorphPart part : partList){ + if(part.getMorphName().hasGlobalText()) return true; + } + } + + for(BoneGroup group : this.boneGroupList){ + if(group.getGroupName().hasGlobalText()) return true; + } + + return false; + } + + /** + * モーフで使われる全てのモーフ頂点のリストを返す。 + * モーフ間で重複する頂点はマージされる。 + * 頂点IDでソートされる。 + *

+ * 0から始まる通し番号がリナンバリングされる。 + * 通し番号は返されるモーフ頂点リストの添え字番号と一致する。 + * @return モーフに使われるモーフ頂点のリスト + */ + public List mergeMorphVertex(){ + List result = new ArrayList(); + + Set mergedVertexSet = new HashSet(); + for(MorphType type : this.morphMap.keySet()){ + if(type.isBase()) continue; + List partList = this.morphMap.get(type); + if(partList == null) continue; + for(MorphPart part : partList){ + for(MorphVertex morphVertex : part){ + Vertex vertex = morphVertex.getBaseVertex(); + if(mergedVertexSet.contains(vertex)) continue; + mergedVertexSet.add(vertex); + result.add(morphVertex); + } + } + } + + Collections.sort(result, MorphVertex.VIDCOMPARATOR); + for(int idx = 0; idx < result.size(); idx++){ + MorphVertex morphVertex = result.get(idx); + morphVertex.setSerialNumber(idx); + } + + Map numberedMap = + new HashMap(); + for(MorphVertex morphVertex : result){ + numberedMap.put(morphVertex.getBaseVertex(), morphVertex); + } + + for(MorphType type : this.morphMap.keySet()){ + if(type.isBase()) continue; + List partList = this.morphMap.get(type); + if(partList == null) continue; + for(MorphPart part : partList){ + for(MorphVertex morphVertex : part){ + Vertex vertex = morphVertex.getBaseVertex(); + MorphVertex numbered = numberedMap.get(vertex); + assert numbered != null; + morphVertex.setSerialNumber(numbered.getSerialNumber()); + } + } + } + + return result; + } + + /** + * 永続化可能な状態へトリミングする。 + * 各種オブジェクトの通し番号が変化する可能性がある。 + */ + public void trimming(){ + List trimmedSurfaceList = trimmingSurfaceList(); + this.surfaceList.clear(); + this.surfaceList.addAll(trimmedSurfaceList); + + List trimmedVertexList = trimmingVertexList(); + this.vertexList.clear(); + this.vertexList.addAll(trimmedVertexList); + + return; + } + + /** + * 面リストをトリミングする。 + * 所属マテリアル順に再配置し、通し番号を割り振り直す。 + * 所属マテリアルの無い面はリストの末端に配置される。 + * 面リスト中のnullは削除され詰められる。 + * @return トリミングされた面リスト + */ + private List trimmingSurfaceList(){ + Set materialedSurfaceSet = new HashSet(); + for(Material material : this.materialList){ + if(material == null) continue; + for(Surface surface : material){ + if(surface == null) continue; + materialedSurfaceSet.add(surface); + } + } + + materialedSurfaceSet.removeAll(this.surfaceList); + + List result = new ArrayList(); + for(Surface surface : this.surfaceList){ + if(surface == null) continue; + result.add(surface); + } + + result.addAll(materialedSurfaceSet); + + int serialNum = 0; + for(Surface surface : result){ + surface.setSerialNumber(serialNum); + serialNum++; + } + + return result; + } + + /** + * 頂点リストをトリミングする。 + * 通し番号を振り直す。 + * 所属面の無い頂点はリストの末端に配置される。 + * 頂点リスト中のnullは削除され詰められる。 + * @return トリミングされた頂点リスト + */ + private List trimmingVertexList(){ + Set surfacedVertexSet = new HashSet(); + for(Surface surface : this.surfaceList){ + if(surface == null) continue; + for(Vertex vertex : surface){ + surfacedVertexSet.add(vertex); + } + } + + surfacedVertexSet.removeAll(this.vertexList); + + List result = new ArrayList(); + for(Vertex vertex : this.vertexList){ + if(vertex == null) continue; + result.add(vertex); + } + + result.addAll(surfacedVertexSet); + + int serialNum = 0; + for(Vertex vertex : result){ + vertex.setSerialNumber(serialNum); + serialNum++; + } + + return result; + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/model/RigidGroup.java b/src/main/java/jp/sourceforge/mikutoga/pmd/model/RigidGroup.java new file mode 100644 index 0000000..266964c --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/model/RigidGroup.java @@ -0,0 +1,101 @@ +/* + * rigid group + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.model; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * 剛体グループ情報。 + * 剛体間の衝突設定の対象となる。 + */ +public class RigidGroup implements SerialNumbered, Iterable { + + private final List rigidList = new ArrayList(); + + private int serialNo = -1; + + /** + * コンストラクタ。 + */ + public RigidGroup(){ + super(); + return; + } + + /** + * 所属する剛体のリストを返す。 + * @return 剛体リスト + */ + public List getRigidList(){ + return this.rigidList; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public Iterator iterator(){ + return this.rigidList.iterator(); + } + + /** + * {@inheritDoc} + * @param num {@inheritDoc} + */ + @Override + public void setSerialNumber(int num){ + this.serialNo = num; + return; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public int getSerialNumber(){ + return this.serialNo; + } + + /** + * グループ番号を返す。 + * MMDでは1〜16までが使われる。 + * 通し番号に1を加えた値と等しい。 + * @return グループ番号 + */ + public int getGroupNumber(){ + return this.serialNo + 1; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public String toString(){ + StringBuilder result = new StringBuilder(); + + result.append("RigidGroup(").append(getGroupNumber()).append(") ["); + + boolean dumped; + + dumped = false; + for(RigidInfo rigid : this.rigidList){ + if(dumped) result.append(", "); + result.append(rigid.getRigidName()); + dumped = true; + } + result.append(']'); + + return result.toString(); + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/model/RigidInfo.java b/src/main/java/jp/sourceforge/mikutoga/pmd/model/RigidInfo.java new file mode 100644 index 0000000..ff5bd34 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/model/RigidInfo.java @@ -0,0 +1,207 @@ +/* + * rigid information + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.model; + +import java.util.ArrayList; +import java.util.Collection; +import jp.sourceforge.mikutoga.corelib.I18nText; +import jp.sourceforge.mikutoga.math.MkPos3D; +import jp.sourceforge.mikutoga.pmd.Rad3d; +import jp.sourceforge.mikutoga.pmd.RigidBehaviorType; + +/** + * 個別の剛体の情報。 + */ +public class RigidInfo implements SerialNumbered { + + private final I18nText rigidName = new I18nText(); + + private RigidBehaviorType behaviorType = RigidBehaviorType.FOLLOWBONE; + + private final RigidShape rigidShape = new RigidShape(); + private final MkPos3D position = new MkPos3D(); + private final Rad3d rotation = new Rad3d(); + + private BoneInfo linkedBone; + + private final DynamicsInfo dynamicsInfo = new DynamicsInfo(); + + private final Collection throughGroupColl = + new ArrayList(); + + private RigidGroup rigidGroup; + + private int serialNo = -1; + + /** + * コンストラクタ。 + */ + public RigidInfo(){ + super(); + return; + } + + /** + * 剛体名を返す。 + * @return 剛体名 + */ + public I18nText getRigidName(){ + return this.rigidName; + } + + /** + * 剛体の振る舞い種別を返す。 + * @return 剛体の振る舞い種別 + */ + public RigidBehaviorType getBehaviorType(){ + return this.behaviorType; + } + + /** + * 剛体の振る舞い種別を設定する。 + * @param type 剛体の振る舞い種別。 + * @throws NullPointerException 引数がnull + */ + public void setBehaviorType(RigidBehaviorType type) + throws NullPointerException{ + if(type == null) throw new NullPointerException(); + this.behaviorType = type; + return; + } + + /** + * 剛体形状を返す。 + * @return 剛体形状 + */ + public RigidShape getRigidShape(){ + return this.rigidShape; + } + + /** + * 剛体位置を返す。 + * @return 剛体位置 + */ + public MkPos3D getPosition(){ + return this.position; + } + + /** + * 剛体姿勢を返す。 + * @return 剛体姿勢 + */ + public Rad3d getRotation(){ + return this.rotation; + } + + /** + * 接続ボーンを返す。 + * @return 接続ボーン + */ + public BoneInfo getLinkedBone(){ + return this.linkedBone; + } + + /** + * 接続ボーンを設定する。 + * @param bone 接続ボーン + */ + public void setLinkedBone(BoneInfo bone){ + this.linkedBone = bone; + return; + } + + /** + * 剛体の力学パラメータを返す。 + * @return 力学パラメータ + */ + public DynamicsInfo getDynamicsInfo(){ + return this.dynamicsInfo; + } + + /** + * 非衝突グループ集合を返す。 + * @return 非衝突グループ集合 + */ + public Collection getThroughGroupColl(){ + return this.throughGroupColl; + } + + /** + * 所属する剛体グループを返す。 + * @return 所属する剛体グループ + */ + public RigidGroup getRigidGroup(){ + return this.rigidGroup; + } + + /** + * 所属する剛体グループを設定する。 + * @param group 所属する剛体グループ + */ + public void setRigidGroup(RigidGroup group){ + this.rigidGroup = group; + return; + } + + /** + * {@inheritDoc} + * @param num {@inheritDoc} + */ + @Override + public void setSerialNumber(int num){ + this.serialNo = num; + return; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public int getSerialNumber(){ + return this.serialNo; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public String toString(){ + StringBuilder result = new StringBuilder(); + + String boneName; + if(this.linkedBone == null){ + boneName = "NOBONE"; + }else{ + boneName = this.linkedBone.getBoneName().toString(); + } + + result.append("Rigid(").append(this.rigidName).append(") "); + result.append("[=>") + .append(boneName) + .append("bone]"); + result.append(" [").append(this.rigidShape).append("]"); + result.append(" ").append(this.position); + result.append(" ").append(this.rotation); + result.append(" [").append(this.dynamicsInfo).append("]"); + result.append(" [").append(this.behaviorType).append("]"); + + result.append(" through["); + boolean dumped = false; + for(RigidGroup group : this.throughGroupColl){ + if(dumped) result.append(" "); + result.append(group.getGroupNumber()); + dumped = true; + } + result.append("]"); + + return result.toString(); + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/model/RigidShape.java b/src/main/java/jp/sourceforge/mikutoga/pmd/model/RigidShape.java new file mode 100644 index 0000000..7a4773d --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/model/RigidShape.java @@ -0,0 +1,150 @@ +/* + * rigid shape information + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.model; + +import jp.sourceforge.mikutoga.pmd.RigidShapeType; + +/** + * 剛体形状に関する情報。 + * 球及びカプセルの半径と箱の幅は同じ値が用いられる。 + */ +public class RigidShape { + + private RigidShapeType type = RigidShapeType.BOX; + private float width = 0.1f; + private float height = 0.1f; + private float depth = 0.1f; + + /** + * コンストラクタ。 + */ + public RigidShape(){ + super(); + return; + } + + /** + * 剛体形状種別を返す。 + * @return 剛体形状種別 + */ + public RigidShapeType getShapeType(){ + return this.type; + } + + /** + * 剛体形状種別を設定する。 + * @param typeArg 剛体形状種別 + * @throws NullPointerException 引数がnull + */ + public void setShapeType(RigidShapeType typeArg) + throws NullPointerException{ + if(typeArg == null) throw new NullPointerException(); + this.type = typeArg; + return; + } + + /** + * 箱の幅を返す。 + * @return 箱の幅 + */ + public float getWidth(){ + return this.width; + } + + /** + * 箱の幅を設定する。 + * @param width 箱の幅 + */ + public void setWidth(float width){ + this.width = width; + return; + } + + /** + * 箱及びカプセルの高さを返す。 + * @return 箱及びカプセルの高さ + */ + public float getHeight(){ + return this.height; + } + + /** + * 箱及びカプセルの高さを設定する。 + * @param height 箱及びカプセルの高さ + */ + public void setHeight(float height){ + this.height = height; + return; + } + + /** + * 箱の奥行きを返す。 + * @return 箱の奥行き + */ + public float getDepth(){ + return this.depth; + } + + /** + * 箱の奥行きを設定する。 + * @param depth 箱の奥行き + */ + public void setDepth(float depth){ + this.depth = depth; + return; + } + + /** + * 球及びカプセルの半径を返す。 + * @return 球及びカプセルの半径 + */ + public float getRadius(){ + return this.width; + } + + /** + * 球及びカプセルの半径を設定する。 + * @param radius 球及びカプセルの半径 + */ + public void setRadius(float radius){ + this.width = radius; + return; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public String toString(){ + StringBuilder result = new StringBuilder(); + + result.append(this.type).append(' '); + + switch(this.type){ + case SPHERE: + result.append("r=").append(this.width); + break; + case BOX: + result.append("w=").append(this.width).append(", "); + result.append("h=").append(this.height).append(", "); + result.append("d=").append(this.depth); + break; + case CAPSULE: + result.append("r=").append(this.width).append(", "); + result.append("h=").append(this.height); + break; + default: + assert false; + throw new AssertionError(); + } + + return result.toString(); + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/model/SerialNumbered.java b/src/main/java/jp/sourceforge/mikutoga/pmd/model/SerialNumbered.java new file mode 100644 index 0000000..fdecaf5 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/model/SerialNumbered.java @@ -0,0 +1,67 @@ +/* + * serial-numbered interface + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.model; + +import java.util.Comparator; + +/** + * 通し番号を持つオブジェクトの抽象化インタフェース。 + */ +public interface SerialNumbered { + + /** 昇順での比較子。 */ + Comparator COMPARATOR = new SerialComparator(); + + /** + * 通し番号を設定する。 + * @param num 通し番号 + */ + void setSerialNumber(int num); + + /** + * 通し番号を返す。 + * @return 通し番号 + */ + int getSerialNumber(); + + /** + * 通し番号による比較子Comparator。 + * 通し番号の昇順を定義づける。 + */ + class SerialComparator + implements Comparator { + + /** + * コンストラクタ。 + */ + public SerialComparator(){ + super(); + return; + } + + /** + * {@inheritDoc} + * @param o1 {@inheritDoc} + * @param o2 {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public int compare(SerialNumbered o1, SerialNumbered o2){ + if(o1 == o2) return 0; + if(o1 == null) return -1; + if(o2 == null) return +1; + + int ser1 = o1.getSerialNumber(); + int ser2 = o2.getSerialNumber(); + + return ser1 - ser2; + } + + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/model/ShadeInfo.java b/src/main/java/jp/sourceforge/mikutoga/pmd/model/ShadeInfo.java new file mode 100644 index 0000000..0d9ccf9 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/model/ShadeInfo.java @@ -0,0 +1,140 @@ +/* + * shading information + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.model; + +/** + * シェーディング情報。 + */ +public class ShadeInfo { + + private ToonMap toonMap = new ToonMap(); + private int toonIdx; + + private String textureFileName = null; + private String spheremapFileName = null; + + /** + * コンストラクタ。 + */ + public ShadeInfo(){ + super(); + return; + } + + /** + * トゥーンマップを設定する。 + * @param map トゥーンマップ + */ + public void setToonMap(ToonMap map){ + this.toonMap = map; + return; + } + + /** + * トゥーンマップを返す。 + * @return トゥーンマップ + */ + public ToonMap getToonMap(){ + return this.toonMap; + } + + /** + * トゥーンインデックスを設定する。 + * @param idx トゥーンインデックス + */ + public void setToonIndex(int idx){ + this.toonIdx = idx; + return; + } + + /** + * トゥーンインデックス値を返す。 + * @return トゥーンインデックス値 + */ + public int getToonIndex(){ + return this.toonIdx; + } + + /** + * トゥーンインデックス値が有効か判定する。 + * 現時点では0から9までの値を有効とする。 + * @return 有効ならtrue + */ + public boolean isValidToonIndex(){ + if(0 <= this.toonIdx && this.toonIdx <= 9) return true; + return false; + } + + /** + * トゥーンファイル名を返す。 + * @return トゥーンファイル名 + * @throws IllegalStateException トゥーンマップが設定されていない。 + */ + public String getToonFileName() throws IllegalStateException{ + if(this.toonMap == null) throw new IllegalStateException(); + String result = this.toonMap.getIndexedToon(this.toonIdx); + return result; + } + + /** + * テクスチャファイル名を設定する。 + * @param fileName テクスチャファイル名 + */ + public void setTextureFileName(String fileName){ + this.textureFileName = fileName; + return; + } + + /** + * テクスチャファイル名を返す。 + * @return テクスチャファイル名 + */ + public String getTextureFileName(){ + return this.textureFileName; + } + + /** + * スフィアマップファイル名を設定する。 + * @param fileName スフィアマップファイル名 + */ + public void setSpheremapFileName(String fileName){ + this.spheremapFileName = fileName; + return; + } + + /** + * スフィアマップファイル名を返す。 + * @return スフィアマップファイル名 + */ + public String getSpheremapFileName(){ + return this.spheremapFileName; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public String toString(){ + StringBuilder result = new StringBuilder(); + + result.append("toon(") + .append(this.toonIdx) + .append(")=") + .append(getToonFileName()) + .append(' '); + result.append("texture=") + .append(this.textureFileName) + .append(' '); + result.append("sphere=") + .append(this.spheremapFileName); + + return result.toString(); + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/model/Surface.java b/src/main/java/jp/sourceforge/mikutoga/pmd/model/Surface.java new file mode 100644 index 0000000..b710c12 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/model/Surface.java @@ -0,0 +1,177 @@ +/* + * triangle surface + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.model; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * 3頂点の三角形からなる面情報。 + */ +public class Surface implements SerialNumbered, Iterable { + + private Vertex vertex1; + private Vertex vertex2; + private Vertex vertex3; + + private int serialNo = -1; + + /** + * コンストラクタ。 + * 3頂点がnullの状態で生成される。 + */ + public Surface(){ + super(); + return; + } + + /** + * 3頂点を設定する。 + * @param vertex1 頂点1 + * @param vertex2 頂点2 + * @param vertex3 頂点3 + * @throws IllegalArgumentException 重複する頂点が引数に含まれた + */ + public void setTriangle(Vertex vertex1, Vertex vertex2, Vertex vertex3) + throws IllegalArgumentException{ + if(vertex1 != null && (vertex1 == vertex2 || vertex1 == vertex3)){ + throw new IllegalArgumentException(); + } + if(vertex2 != null && vertex2 == vertex3){ + throw new IllegalArgumentException(); + } + + this.vertex1 = vertex1; + this.vertex2 = vertex2; + this.vertex3 = vertex3; + + return; + } + + /** + * 3頂点を返す。 + * @param store 頂点格納用配列。nullもしくは3要素に満たない場合は無視され、 + * 新規に格納用配列が生成される。 + * @return 先頭3要素に3頂点が収められた配列。未設定要素にはnullが入る。 + * 引数が長さ3以上の配列であれば引数と同じ配列が返る。 + */ + public Vertex[] getTriangle(Vertex[] store){ + Vertex[] result; + if(store == null || store.length < 3){ + result = new Vertex[3]; + }else{ + result = store; + } + + result[0] = this.vertex1; + result[1] = this.vertex2; + result[2] = this.vertex3; + + return result; + } + + /** + * 頂点その1を返す。 + * @return 頂点その1 + */ + public Vertex getVertex1(){ + return this.vertex1; + } + + /** + * 頂点その2を返す。 + * @return 頂点その2 + */ + public Vertex getVertex2(){ + return this.vertex2; + } + + /** + * 頂点その3を返す。 + * @return 頂点その3 + */ + public Vertex getVertex3(){ + return this.vertex3; + } + + /** + * {@inheritDoc} + * 頂点を返す反復子を生成する。 + * 反復子がnullを返す可能性もありうる。 + * @return {@inheritDoc} + */ + @Override + public Iterator iterator(){ + List list = new ArrayList(3); + + list.add(this.vertex1); + list.add(this.vertex2); + list.add(this.vertex3); + + return list.iterator(); + } + + /** + * 3頂点全てが設定されているか判定する。 + * @return 3頂点とも非nullが設定されていればtrue + */ + public boolean isCompleted(){ + if( this.vertex1 != null + && this.vertex2 != null + && this.vertex3 != null ){ + return true; + } + return false; + } + + /** + * {@inheritDoc} + * @param num {@inheritDoc} + */ + @Override + public void setSerialNumber(int num){ + this.serialNo = num; + return; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public int getSerialNumber(){ + return this.serialNo; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public String toString(){ + StringBuilder result = new StringBuilder(); + + result.append("Surface(") + .append(getSerialNumber()) + .append(")"); + + if(isCompleted()){ + result.append(" VID=[") + .append(this.vertex1.getSerialNumber()) + .append(',') + .append(this.vertex2.getSerialNumber()) + .append(',') + .append(this.vertex3.getSerialNumber()) + .append(']'); + } + + return result.toString(); + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/model/ToonMap.java b/src/main/java/jp/sourceforge/mikutoga/pmd/model/ToonMap.java new file mode 100644 index 0000000..ae9f561 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/model/ToonMap.java @@ -0,0 +1,147 @@ +/* + * toon file mapping + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.model; + +import java.util.Collections; +import java.util.Map; +import java.util.TreeMap; + +/** + * インデックス化されたトゥーンファイル構成。 + * 既存のトゥーンファイル構成と異なるトゥーンファイル名を用いることが可能。 + *

デフォルトのトゥーンファイル構成。

+ *
    + *
  • 0x00:toon01.bmp + *
  • 0x01:toon02.bmp + *
  • ..... + *
  • 0x09:toon10.bmp + *
  • 0xff:toon0.bmp + *
+ */ +public class ToonMap { + + private static final Map DEF_TOONMAP; + + static{ + Map map = new TreeMap(); + + map.put(0x00, "toon01.bmp"); + map.put(0x01, "toon02.bmp"); + map.put(0x02, "toon03.bmp"); + map.put(0x03, "toon04.bmp"); + map.put(0x04, "toon05.bmp"); + map.put(0x05, "toon06.bmp"); + map.put(0x06, "toon07.bmp"); + map.put(0x07, "toon08.bmp"); + map.put(0x08, "toon09.bmp"); + map.put(0x09, "toon10.bmp"); + map.put(0xff, "toon0.bmp"); + + DEF_TOONMAP = Collections.unmodifiableMap(map); + } + + private final Map toonMap = + new TreeMap(DEF_TOONMAP); + + /** + * コンストラクタ。 + */ + public ToonMap(){ + super(); + return; + } + + /** + * 指定したインデックス値に対応したトゥーンファイル名を返す。 + * @param idx インデックス値 + * @return トゥーンファイル名。該当するものがなければnull + */ + public String getIndexedToon(int idx){ + String result = this.toonMap.get(idx); + return result; + } + + /** + * 指定したインデックス値にトゥーンファイル名を設定する。 + * @param idx インデックス値 + * @param toonFileName トゥーンフィル名 + * @throws NullPointerException トゥーンファイル名がnull + */ + public void setIndexedToon(int idx, String toonFileName) + throws NullPointerException{ + if(toonFileName == null) throw new NullPointerException(); + this.toonMap.put(idx, toonFileName); + return; + } + + /** + * このトゥーンファイル構成がデフォルトのトゥーンファイル構成と等しいか判定する。 + * @return 等しければtrue + */ + public boolean isDefaultMap(){ + if(this.toonMap.equals(DEF_TOONMAP)) return true; + return false; + } + + /** + * 指定インデックスのトゥーンファイル名がデフォルトと等しいか判定する。 + * @param idx インデックス + * @return デフォルトと等しければtrue。 + */ + public boolean isDefaultToon(int idx){ + String thisToon = this.toonMap.get(idx); + if(thisToon == null) return false; + + String defToon = DEF_TOONMAP.get(idx); + if(thisToon.equals(defToon)) return true; + + return false; + } + + /** + * このトゥーンファイル構成をデフォルト構成内容でリセットする。 + */ + public void resetDefaultMap(){ + this.toonMap.clear(); + this.toonMap.putAll(DEF_TOONMAP); + return; + } + + /** + * 指定インデックスのトゥーンファイル名をデフォルトのトゥーンファイル名にリセットする。 + * @param idx インデックス値 + */ + public void resetIndexedToon(int idx){ + String toonFile = DEF_TOONMAP.get(idx); + this.toonMap.put(idx, toonFile); + return; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public String toString(){ + StringBuilder result = new StringBuilder(); + + boolean dumped = false; + for(Map.Entry entry : this.toonMap.entrySet()){ + Integer idx = entry.getKey(); + String toonFile = entry.getValue(); + + if(dumped) result.append(", "); + result.append('(').append(idx).append(')'); + result.append(toonFile); + dumped = true; + } + + return result.toString(); + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/model/Vertex.java b/src/main/java/jp/sourceforge/mikutoga/pmd/model/Vertex.java new file mode 100644 index 0000000..7ba6949 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/model/Vertex.java @@ -0,0 +1,221 @@ +/* + * vertex information + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.model; + +import jp.sourceforge.mikutoga.math.MkPos2D; +import jp.sourceforge.mikutoga.math.MkPos3D; +import jp.sourceforge.mikutoga.math.MkVec3D; + +/** + * 頂点情報。 + */ +public class Vertex implements SerialNumbered { + + private static final int MIN_WEIGHT = 0; + private static final int MAX_WEIGHT = 100; + + private final MkPos3D position = new MkPos3D(); + private final MkVec3D normal = new MkVec3D(); + + private final MkPos2D uvPosition = new MkPos2D(); + + private BoneInfo boneA = null; + private BoneInfo boneB = null; + + private int boneWeight = 50; + + private boolean edgeAppearance = true; + + private int serialNo = -1; + + /** + * コンストラクタ。 + */ + public Vertex(){ + super(); + return; + } + + /** + * 頂点位置座標を返す。 + * @return 頂点の位置座標 + */ + public MkPos3D getPosition(){ + return this.position; + } + + /** + * 法線ベクトルを返す。 + * @return 法線ベクトル + */ + public MkVec3D getNormal(){ + return this.normal; + } + + /** + * UVマップ座標を返す。 + * @return UVマップ情報 + */ + public MkPos2D getUVPosition(){ + return this.uvPosition; + } + + /** + * 頂点の属するボーンを設定する。 + * @param boneA ボーンA + * @param boneB ボーンB + * @throws NullPointerException 引数がnull + */ + public void setBonePair(BoneInfo boneA, BoneInfo boneB) + throws NullPointerException{ + if(boneA == null || boneB == null) throw new NullPointerException(); + this.boneA = boneA; + this.boneB = boneB; + return; + } + + /** + * ボーンAを返す。 + * @return ボーンA + */ + public BoneInfo getBoneA(){ + return this.boneA; + } + + /** + * ボーンBを返す。 + * @return ボーンB + */ + public BoneInfo getBoneB(){ + return this.boneB; + } + + /** + * ボーンAのウェイト値を設定する。 + * @param weight ウェイト値。0(影響小)-100(影響大) + * @throws IllegalArgumentException ウェイト値が範囲外 + */ + public void setWeightA(int weight) throws IllegalArgumentException{ + if( weight < MIN_WEIGHT + || MAX_WEIGHT < weight ){ + throw new IllegalArgumentException(); + } + this.boneWeight = weight; + return; + } + + /** + * ボーンBのウェイト値を設定する。 + * @param weight ウェイト値。0(影響小)-100(影響大) + * @throws IllegalArgumentException ウェイト値が範囲外 + */ + public void setWeightB(int weight) throws IllegalArgumentException{ + setWeightA(MAX_WEIGHT - weight); + return; + } + + /** + * ボーンAのウェイト値を返す。 + * @return ウェイト値 + */ + public int getWeightA(){ + return this.boneWeight; + } + + /** + * ボーンBのウェイト値を返す。 + * @return ウェイト値 + */ + public int getWeightB(){ + int result = MAX_WEIGHT - this.boneWeight; + return result; + } + + /** + * ボーンAのウェイト率を返す。 + * @return ウェイト率。0.0(影響小)-1.0(影響大) + */ + public float getWeightRatioA(){ + return ((float)this.boneWeight) / (float)MAX_WEIGHT; + } + + /** + * ボーンBのウェイト率を返す。 + * @return ウェイト率。0.0(影響小)-1.0(影響大) + */ + public float getWeightRatioB(){ + return ((float)MAX_WEIGHT - (float)this.boneWeight) + / (float)MAX_WEIGHT; + } + + /** + * エッジを表示するか設定する。 + * マテリアル材質単位の設定より優先度は高い。 + * @param show 表示するならtrue + */ + public void setEdgeAppearance(boolean show){ + this.edgeAppearance = show; + return; + } + + /** + * エッジを表示するか判定する。 + * マテリアル材質単位の設定より優先度は高い。 + * @return 表示するならtrue + */ + public boolean getEdgeAppearance(){ + return this.edgeAppearance; + } + + /** + * {@inheritDoc} + * @param num {@inheritDoc} + */ + @Override + public void setSerialNumber(int num){ + this.serialNo = num; + return; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public int getSerialNumber(){ + return this.serialNo; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public String toString(){ + StringBuilder result = new StringBuilder(); + + result.append("Vertex(").append(this.serialNo).append(") "); + result.append(this.position).append(' '); + result.append(this.normal).append(' '); + result.append("UV").append(this.uvPosition).append(' '); + + result.append("[") + .append(this.boneA.getBoneName()) + .append("<>") + .append(this.boneB.getBoneName()) + .append("] "); + + result.append("weight=").append(this.boneWeight).append(' '); + + if(this.edgeAppearance) result.append("showEdge"); + else result.append("hideEdge"); + + return result.toString(); + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/BoneBuilder.java b/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/BoneBuilder.java new file mode 100644 index 0000000..0ae6376 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/BoneBuilder.java @@ -0,0 +1,290 @@ +/* + * building bone information + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.model.binio; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import jp.sourceforge.mikutoga.pmd.model.ListUtil; +import jp.sourceforge.mikutoga.math.MkPos3D; +import jp.sourceforge.mikutoga.parser.ParseStage; +import jp.sourceforge.mikutoga.pmd.BoneType; +import jp.sourceforge.mikutoga.pmd.model.BoneGroup; +import jp.sourceforge.mikutoga.pmd.model.BoneInfo; +import jp.sourceforge.mikutoga.pmd.model.IKChain; +import jp.sourceforge.mikutoga.pmd.model.PmdModel; +import jp.sourceforge.mikutoga.pmd.parser.PmdBoneHandler; +import jp.sourceforge.mikutoga.pmd.parser.PmdLimits; + +/** + * ボーン関係の通知をパーサから受け取る。 + */ +class BoneBuilder implements PmdBoneHandler { + + private final List boneList; + private Iterator boneIt; + private BoneInfo currentBone = null; + + private final List ikChainList; + private Iterator ikChainIt; + private IKChain currentIkChain = null; + + private final List boneGroupList; + private Iterator boneGroupIt; + private BoneGroup currentBoneGroup = null; + + /** + * コンストラクタ。 + * @param model モデル + */ + BoneBuilder(PmdModel model){ + super(); + + this.boneList = model.getBoneList(); + this.ikChainList = model.getIKChainList(); + this.boneGroupList = model.getBoneGroupList(); + + return; + } + + /** + * {@inheritDoc} + * @param stage {@inheritDoc} + * @param loops {@inheritDoc} + */ + @Override + public void loopStart(ParseStage stage, int loops){ + if(stage == PmdBoneHandler.BONE_LIST){ + ListUtil.prepareDefConsList(this.boneList, BoneInfo.class, loops); + ListUtil.assignIndexedSerial(this.boneList); + + this.boneIt = this.boneList.iterator(); + if(this.boneIt.hasNext()){ + this.currentBone = this.boneIt.next(); + } + }else if(stage == PmdBoneHandler.IK_LIST){ + ListUtil.prepareDefConsList(this.ikChainList, + IKChain.class, + loops ); + + this.ikChainIt = this.ikChainList.iterator(); + if(this.ikChainIt.hasNext()){ + this.currentIkChain = this.ikChainIt.next(); + } + }else if(stage == PmdBoneHandler.IKCHAIN_LIST){ + //NOTHING + }else if(stage == PmdBoneHandler.BONEGROUP_LIST){ + ListUtil.prepareDefConsList(this.boneGroupList, + BoneGroup.class, + loops + 1 ); + ListUtil.assignIndexedSerial(this.boneGroupList); + + this.boneGroupIt = this.boneGroupList.iterator(); + + assert this.boneGroupIt.hasNext(); + this.boneGroupIt.next(); // デフォルトボーングループを読み飛ばす + + if(this.boneGroupIt.hasNext()){ + this.currentBoneGroup = this.boneGroupIt.next(); + } + }else if(stage == PmdBoneHandler.GROUPEDBONE_LIST){ + //NOTHING + }else{ + assert false; + throw new AssertionError(); + } + + return; + } + + /** + * {@inheritDoc} + * @param stage {@inheritDoc} + */ + @Override + public void loopNext(ParseStage stage){ + if(stage == PmdBoneHandler.BONE_LIST){ + if(this.boneIt.hasNext()){ + this.currentBone = this.boneIt.next(); + } + }else if(stage == PmdBoneHandler.IK_LIST){ + if(this.ikChainIt.hasNext()){ + this.currentIkChain = this.ikChainIt.next(); + } + }else if(stage == PmdBoneHandler.IKCHAIN_LIST){ + //NOTHING + }else if(stage == PmdBoneHandler.BONEGROUP_LIST){ + if(this.boneGroupIt.hasNext()){ + this.currentBoneGroup = this.boneGroupIt.next(); + } + }else if(stage == PmdBoneHandler.GROUPEDBONE_LIST){ + //NOTHING + }else{ + assert false; + throw new AssertionError(); + } + return; + } + + /** + * {@inheritDoc} + * @param stage {@inheritDoc} + */ + @Override + public void loopEnd(ParseStage stage){ + if(stage == PmdBoneHandler.BONE_LIST){ + //NOTHING + }else if(stage == PmdBoneHandler.IK_LIST){ + //NOTHING + }else if(stage == PmdBoneHandler.IKCHAIN_LIST){ + //NOTHING + }else if(stage == PmdBoneHandler.BONEGROUP_LIST){ + //NOTHING + }else if(stage == PmdBoneHandler.GROUPEDBONE_LIST){ + pickOrphanBone(); + }else{ + assert false; + throw new AssertionError(); + } + return; + } + + /** + * 所属グループの無いボーンをデフォルトボーングループへ登録する。 + */ + private void pickOrphanBone(){ + List orpahnList = new LinkedList(); + orpahnList.addAll(this.boneList); + for(BoneGroup group : this.boneGroupList){ + orpahnList.removeAll(group.getBoneList()); + } + + BoneGroup defaultGroup = this.boneGroupList.get(0); + defaultGroup.getBoneList().addAll(orpahnList); + + return; + } + + /** + * {@inheritDoc} + * @param boneName {@inheritDoc} + * @param boneKind {@inheritDoc} + */ + @Override + public void pmdBoneInfo(String boneName, byte boneKind){ + this.currentBone.getBoneName().setPrimaryText(boneName); + BoneType type = BoneType.decode(boneKind); + this.currentBone.setBoneType(type); + return; + } + + /** + * {@inheritDoc} + * @param parentId {@inheritDoc} + * @param tailId {@inheritDoc} + * @param ikId {@inheritDoc} + */ + @Override + public void pmdBoneLink(int parentId, int tailId, int ikId){ + BoneInfo prevBone = null; + if(0 <= parentId && parentId < PmdLimits.MAX_BONE){ + prevBone = this.boneList.get(parentId); + } + + BoneInfo nextBone = null; + if(tailId != 0){ + nextBone = this.boneList.get(tailId); + } + + BoneInfo ikBone = null; + if(this.currentBone.getBoneType() == BoneType.LINKEDROT){ + ikBone = null; + int ratio = ikId; + this.currentBone.setRotationRatio(ratio); + }else if(0 < ikId && ikId < PmdLimits.MAX_BONE){ + ikBone = this.boneList.get(ikId); + } + + this.currentBone.setPrevBone(prevBone); + this.currentBone.setNextBone(nextBone); + this.currentBone.setIKBone(ikBone); + + return; + } + + /** + * {@inheritDoc} + * @param xPos {@inheritDoc} + * @param yPos {@inheritDoc} + * @param zPos {@inheritDoc} + */ + @Override + public void pmdBonePosition(float xPos, float yPos, float zPos){ + MkPos3D position = this.currentBone.getPosition(); + position.setXpos(xPos); + position.setYpos(yPos); + position.setZpos(zPos); + return; + } + + /** + * {@inheritDoc} + * @param boneId {@inheritDoc} + * @param targetId {@inheritDoc} + * @param depth {@inheritDoc} + * @param weight {@inheritDoc} + */ + @Override + public void pmdIKInfo(int boneId, int targetId, int depth, float weight){ + BoneInfo bone = this.boneList.get(boneId); + this.currentIkChain.setIkBone(bone); + + BoneInfo target = this.boneList.get(targetId); + this.currentIkChain.getChainedBoneList().add(0, target); + + this.currentIkChain.setIKDepth(depth); + this.currentIkChain.setIKWeight(weight); + + return; + } + + /** + * {@inheritDoc} + * @param childId {@inheritDoc} + */ + @Override + public void pmdIKChainInfo(int childId){ + BoneInfo chain = this.boneList.get(childId); + this.currentIkChain.getChainedBoneList().add(chain); + return; + } + + /** + * {@inheritDoc} + * @param groupName {@inheritDoc} + */ + @Override + public void pmdBoneGroupInfo(String groupName){ + this.currentBoneGroup.getGroupName().setPrimaryText(groupName); + return; + } + + /** + * {@inheritDoc} + * @param boneId {@inheritDoc} + * @param groupId {@inheritDoc} + */ + @Override + public void pmdGroupedBoneInfo(int boneId, int groupId){ + BoneInfo bone = this.boneList.get(boneId); + BoneGroup group = this.boneGroupList.get(groupId); + group.getBoneList().add(bone); + return; + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/JointBuilder.java b/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/JointBuilder.java new file mode 100644 index 0000000..a03099b --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/JointBuilder.java @@ -0,0 +1,216 @@ +/* + * building joint information + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.model.binio; + +import java.util.Iterator; +import java.util.List; +import jp.sourceforge.mikutoga.pmd.model.ListUtil; +import jp.sourceforge.mikutoga.math.MkPos3D; +import jp.sourceforge.mikutoga.parser.ParseStage; +import jp.sourceforge.mikutoga.pmd.Deg3d; +import jp.sourceforge.mikutoga.pmd.Rad3d; +import jp.sourceforge.mikutoga.pmd.TripletRange; +import jp.sourceforge.mikutoga.pmd.model.JointInfo; +import jp.sourceforge.mikutoga.pmd.model.PmdModel; +import jp.sourceforge.mikutoga.pmd.model.RigidInfo; +import jp.sourceforge.mikutoga.pmd.parser.PmdJointHandler; + +/** + * ジョイント関係の通知をパーサから受け取る。 + */ +class JointBuilder implements PmdJointHandler { + + private final List rigidList; + + private final List jointList; + private Iterator jointIt; + private JointInfo currentJoint = null; + + /** + * コンストラクタ。 + * @param model モデル + */ + JointBuilder(PmdModel model){ + super(); + this.rigidList = model.getRigidList(); + this.jointList = model.getJointList(); + return; + } + + /** + * {@inheritDoc} + * @param stage {@inheritDoc} + * @param loops {@inheritDoc} + */ + @Override + public void loopStart(ParseStage stage, int loops){ + assert stage == PmdJointHandler.JOINT_LIST; + + ListUtil.prepareDefConsList(this.jointList, JointInfo.class, loops); + + this.jointIt = this.jointList.iterator(); + if(this.jointIt.hasNext()){ + this.currentJoint = this.jointIt.next(); + } + + return; + } + + /** + * {@inheritDoc} + * @param stage {@inheritDoc} + */ + @Override + public void loopNext(ParseStage stage){ + assert stage == PmdJointHandler.JOINT_LIST; + + if(this.jointIt.hasNext()){ + this.currentJoint = this.jointIt.next(); + } + + return; + } + + /** + * {@inheritDoc} + * @param stage {@inheritDoc} + */ + @Override + public void loopEnd(ParseStage stage){ + assert stage == PmdJointHandler.JOINT_LIST; + return; + } + + /** + * {@inheritDoc} + * @param jointName {@inheritDoc} + */ + @Override + public void pmdJointName(String jointName){ + this.currentJoint.getJointName().setPrimaryText(jointName); + return; + } + + /** + * {@inheritDoc} + * @param rigidIdA {@inheritDoc} + * @param rigidIdB {@inheritDoc} + */ + @Override + public void pmdJointLink(int rigidIdA, int rigidIdB){ + RigidInfo rigidA = this.rigidList.get(rigidIdA); + RigidInfo rigidB = this.rigidList.get(rigidIdB); + this.currentJoint.setRigidPair(rigidA, rigidB); + return; + } + + /** + * {@inheritDoc} + * @param posX {@inheritDoc} + * @param posY {@inheritDoc} + * @param posZ {@inheritDoc} + */ + @Override + public void pmdJointPosition(float posX, float posY, float posZ){ + MkPos3D position = this.currentJoint.getPosition(); + position.setXpos(posX); + position.setYpos(posY); + position.setZpos(posZ); + return; + } + + /** + * {@inheritDoc} + * @param rotX {@inheritDoc} + * @param rotY {@inheritDoc} + * @param rotZ {@inheritDoc} + */ + @Override + public void pmdJointRotation(float rotX, float rotY, float rotZ){ + Rad3d rotation = this.currentJoint.getRotation(); + rotation.setXRad(rotX); + rotation.setYRad(rotY); + rotation.setZRad(rotZ); + return; + } + + /** + * {@inheritDoc} + * @param posXlim1 {@inheritDoc} + * @param posXlim2 {@inheritDoc} + * @param posYlim1 {@inheritDoc} + * @param posYlim2 {@inheritDoc} + * @param posZlim1 {@inheritDoc} + * @param posZlim2 {@inheritDoc} + */ + @Override + public void pmdPositionLimit(float posXlim1, float posXlim2, + float posYlim1, float posYlim2, + float posZlim1, float posZlim2){ + TripletRange range = this.currentJoint.getPositionRange(); + range.setXRange(posXlim1, posXlim2); + range.setYRange(posYlim1, posYlim2); + range.setZRange(posZlim1, posZlim2); + return; + } + + /** + * {@inheritDoc} + * @param rotXlim1 {@inheritDoc} + * @param rotXlim2 {@inheritDoc} + * @param rotYlim1 {@inheritDoc} + * @param rotYlim2 {@inheritDoc} + * @param rotZlim1 {@inheritDoc} + * @param rotZlim2 {@inheritDoc} + */ + @Override + public void pmdRotationLimit(float rotXlim1, float rotXlim2, + float rotYlim1, float rotYlim2, + float rotZlim1, float rotZlim2){ + TripletRange range = this.currentJoint.getRotationRange(); + range.setXRange(rotXlim1, rotXlim2); + range.setYRange(rotYlim1, rotYlim2); + range.setZRange(rotZlim1, rotZlim2); + return; + } + + /** + * {@inheritDoc} + * @param elasticPosX {@inheritDoc} + * @param elasticPosY {@inheritDoc} + * @param elasticPosZ {@inheritDoc} + */ + @Override + public void pmdElasticPosition(float elasticPosX, + float elasticPosY, + float elasticPosZ){ + MkPos3D position = this.currentJoint.getElasticPosition(); + position.setXpos(elasticPosX); + position.setYpos(elasticPosY); + position.setZpos(elasticPosZ); + return; + } + + /** + * {@inheritDoc} + * @param elasticRotX {@inheritDoc} + * @param elasticRotY {@inheritDoc} + * @param elasticRotZ {@inheritDoc} + */ + @Override + public void pmdElasticRotation(float elasticRotX, + float elasticRotY, + float elasticRotZ){ + Deg3d rotation = this.currentJoint.getElasticRotation(); + rotation.setXDeg(elasticRotX); + rotation.setYDeg(elasticRotY); + rotation.setZDeg(elasticRotZ); + return; + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/MaterialBuilder.java b/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/MaterialBuilder.java new file mode 100644 index 0000000..8ffdaec --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/MaterialBuilder.java @@ -0,0 +1,188 @@ +/* + * building material information + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.model.binio; + +import java.awt.Color; +import java.util.Iterator; +import java.util.List; +import jp.sourceforge.mikutoga.pmd.model.ListUtil; +import jp.sourceforge.mikutoga.parser.ParseStage; +import jp.sourceforge.mikutoga.pmd.model.Material; +import jp.sourceforge.mikutoga.pmd.model.PmdModel; +import jp.sourceforge.mikutoga.pmd.model.ShadeInfo; +import jp.sourceforge.mikutoga.pmd.model.Surface; +import jp.sourceforge.mikutoga.pmd.model.ToonMap; +import jp.sourceforge.mikutoga.pmd.parser.PmdMaterialHandler; + +/** + * マテリアル素材関連の通知をパーサから受け取る。 + */ +class MaterialBuilder implements PmdMaterialHandler { + + private final List materialList; + private Iterator materialIt; + private Material currentMaterial = null; + + private final List surfacelList; + private Iterator surfaceIt; + + private final ToonMap toonMap; + + /** + * コンストラクタ。 + * @param model モデル + */ + MaterialBuilder(PmdModel model){ + super(); + + this.materialList = model.getMaterialList(); + this.surfacelList = model.getSurfaceList(); + this.toonMap = model.getToonMap(); + + return; + } + + /** + * {@inheritDoc} + * @param stage {@inheritDoc} + * @param loops {@inheritDoc} + */ + @Override + public void loopStart(ParseStage stage, int loops){ + assert stage == PmdMaterialHandler.MATERIAL_LIST; + + ListUtil.prepareDefConsList(this.materialList, Material.class, loops); + + this.materialIt = this.materialList.iterator(); + if(this.materialIt.hasNext()){ + this.currentMaterial = this.materialIt.next(); + } + + this.surfaceIt = this.surfacelList.iterator(); + + return; + } + + /** + * {@inheritDoc} + * @param stage {@inheritDoc} + */ + @Override + public void loopNext(ParseStage stage){ + assert stage == PmdMaterialHandler.MATERIAL_LIST; + + if(this.materialIt.hasNext()){ + this.currentMaterial = this.materialIt.next(); + } + + return; + } + + /** + * {@inheritDoc} + * @param stage {@inheritDoc} + */ + @Override + public void loopEnd(ParseStage stage){ + assert stage == PmdMaterialHandler.MATERIAL_LIST; + return; + } + + /** + * {@inheritDoc} + * @param red {@inheritDoc} + * @param green {@inheritDoc} + * @param blue {@inheritDoc} + * @param alpha {@inheritDoc} + */ + @Override + public void pmdMaterialDiffuse(float red, + float green, + float blue, + float alpha ){ + Color diffuse = new Color(red, green, blue, alpha); + this.currentMaterial.setDiffuseColor(diffuse); + return; + } + + /** + * {@inheritDoc} + * @param red {@inheritDoc} + * @param green {@inheritDoc} + * @param blue {@inheritDoc} + */ + @Override + public void pmdMaterialAmbient(float red, + float green, + float blue ){ + Color ambient = new Color(red, green, blue); + this.currentMaterial.setAmbientColor(ambient); + return; + } + + /** + * {@inheritDoc} + * @param red {@inheritDoc} + * @param green {@inheritDoc} + * @param blue {@inheritDoc} + * @param shininess {@inheritDoc} + */ + @Override + public void pmdMaterialSpecular(float red, + float green, + float blue, + float shininess ){ + Color specular = new Color(red, green, blue); + this.currentMaterial.setSpecularColor(specular); + this.currentMaterial.setShininess(shininess); + return; + } + + /** + * {@inheritDoc} + * @param hasEdge {@inheritDoc} + * @param vertexNum {@inheritDoc} + */ + @Override + public void pmdMaterialInfo(boolean hasEdge, int vertexNum){ + this.currentMaterial.setEdgeAppearance(hasEdge); + + List list = this.currentMaterial.getSurfaceList(); + + int surfaceNum = vertexNum / 3; + for(int ct = 1; ct <= surfaceNum; ct++){ + Surface surface = this.surfaceIt.next(); + list.add(surface); + } + + return; + } + + /** + * {@inheritDoc} + * @param toonIdx {@inheritDoc} + * @param textureFile {@inheritDoc} + * @param sphereFile {@inheritDoc} + */ + @Override + public void pmdMaterialShading(int toonIdx, + String textureFile, + String sphereFile ){ + ShadeInfo info = this.currentMaterial.getShadeInfo(); + + ToonMap map = this.toonMap; + + info.setToonMap(map); + info.setToonIndex(toonIdx); + info.setTextureFileName(textureFile); + info.setSpheremapFileName(sphereFile); + + return; + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/MorphBuilder.java b/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/MorphBuilder.java new file mode 100644 index 0000000..4e4df22 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/MorphBuilder.java @@ -0,0 +1,194 @@ +/* + * building morph information + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.model.binio; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import jp.sourceforge.mikutoga.pmd.model.ListUtil; +import jp.sourceforge.mikutoga.math.MkPos3D; +import jp.sourceforge.mikutoga.parser.ParseStage; +import jp.sourceforge.mikutoga.pmd.MorphType; +import jp.sourceforge.mikutoga.pmd.model.MorphPart; +import jp.sourceforge.mikutoga.pmd.model.MorphVertex; +import jp.sourceforge.mikutoga.pmd.model.PmdModel; +import jp.sourceforge.mikutoga.pmd.model.Vertex; +import jp.sourceforge.mikutoga.pmd.parser.PmdMorphHandler; + +/** + * モーフ関係の通知をパーサから受け取る。 + */ +class MorphBuilder implements PmdMorphHandler { + + private final Map> morphMap; + + private List morphPartList; + private Iterator morphPartIt; + private MorphPart currentMorphPart; + private final List vertexList; + + private final List morphVertexList = new ArrayList(); + + /** + * コンストラクタ。 + * @param model モデル + */ + MorphBuilder(PmdModel model){ + super(); + this.vertexList = model.getVertexList(); + this.morphMap = model.getMorphMap(); + return; + } + + /** + * PMDファイル中の出現順で各モーフを格納するためのリストを設定する。 + * 主な用途はモーフ英名との突き合わせ作業。 + * @param list モーフ格納リスト + */ + void setMorphPartList(List list){ + this.morphPartList = list; + return; + } + + /** + * {@inheritDoc} + * @param stage {@inheritDoc} + * @param loops {@inheritDoc} + */ + @Override + public void loopStart(ParseStage stage, int loops){ + if(stage == PmdMorphHandler.MORPH_LIST){ + ListUtil.prepareDefConsList(this.morphPartList, + MorphPart.class, + loops ); + ListUtil.assignIndexedSerial(this.morphPartList); + + this.morphPartIt = this.morphPartList.iterator(); + if(this.morphPartIt.hasNext()){ + this.currentMorphPart = this.morphPartIt.next(); + } + }else if(stage == PmdMorphHandler.MORPHVERTEX_LIST){ + // NOTHING + }else if(stage == PmdMorphHandler.MORPHORDER_LIST){ + // NOTHING + }else{ + assert false; + throw new AssertionError(); + } + + return; + } + + /** + * {@inheritDoc} + * @param stage {@inheritDoc} + */ + @Override + public void loopNext(ParseStage stage){ + if(stage == PmdMorphHandler.MORPH_LIST){ + if(this.morphPartIt.hasNext()){ + this.currentMorphPart = this.morphPartIt.next(); + } + }else if(stage == PmdMorphHandler.MORPHVERTEX_LIST){ + // NOTHING + }else if(stage == PmdMorphHandler.MORPHORDER_LIST){ + // NOTHING + }else{ + assert false; + throw new AssertionError(); + } + + return; + } + + /** + * {@inheritDoc} + * @param stage {@inheritDoc} + */ + @Override + public void loopEnd(ParseStage stage){ + if(stage == PmdMorphHandler.MORPH_LIST){ + // NOTHING + }else if(stage == PmdMorphHandler.MORPHVERTEX_LIST){ + // NOTHING + }else if(stage == PmdMorphHandler.MORPHORDER_LIST){ + // NOTHING + }else{ + assert false; + throw new AssertionError(); + } + return; + } + + /** + * {@inheritDoc} + * @param morphName {@inheritDoc} + * @param morphType {@inheritDoc} + */ + @Override + public void pmdMorphInfo(String morphName, byte morphType){ + this.currentMorphPart.getMorphName().setPrimaryText(morphName); + MorphType type = MorphType.decode(morphType); + this.currentMorphPart.setMorphType(type); + + return; + } + + /** + * {@inheritDoc} + * @param serialId {@inheritDoc} + * @param xPos {@inheritDoc} + * @param yPos {@inheritDoc} + * @param zPos {@inheritDoc} + */ + @Override + public void pmdMorphVertexInfo(int serialId, + float xPos, float yPos, float zPos){ + MorphVertex morphVertex; + morphVertex = new MorphVertex(); + MkPos3D position = morphVertex.getOffset(); + position.setXpos(xPos); + position.setYpos(yPos); + position.setZpos(zPos); + + Vertex vertex; + if(this.currentMorphPart.getMorphType().isBase()){ + vertex = this.vertexList.get(serialId); + this.morphVertexList.add(vertex); + }else{ + vertex = this.morphVertexList.get(serialId); + } + morphVertex.setBaseVertex(vertex); + + this.currentMorphPart.getMorphVertexList().add(morphVertex); + + return; + } + + /** + * {@inheritDoc} + * @param morphId {@inheritDoc} + */ + @Override + public void pmdMorphOrderInfo(int morphId){ + MorphPart part = this.morphPartList.get(morphId); + MorphType type = part.getMorphType(); + + List partList = this.morphMap.get(type); + if(partList == null){ + partList = new LinkedList(); + this.morphMap.put(type, partList); + } + partList.add(part); + + return; + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/PmdExporter.java b/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/PmdExporter.java new file mode 100644 index 0000000..0cc9280 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/PmdExporter.java @@ -0,0 +1,30 @@ +/* + * model exporter for pmd-file(up to date) + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.model.binio; + +import java.io.OutputStream; + +/** + * 最新仕様のPMDファイルエクスポーター。 + * 将来のリリースにおいて、 + * 常に最新のPMDモデルファイルフォーマットに対応したエクスポーターの + * 別名であることが保証される。つもり。 + */ +public class PmdExporter extends PmdExporterExt3{ + + /** + * コンストラクタ。 + * @param stream 出力ストリーム + * @throws NullPointerException 引数がnull + */ + public PmdExporter(OutputStream stream) throws NullPointerException{ + super(stream); + return; + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/PmdExporterBase.java b/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/PmdExporterBase.java new file mode 100644 index 0000000..6122d05 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/PmdExporterBase.java @@ -0,0 +1,647 @@ +/* + * model exporter for pmd-file + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.model.binio; + +import jp.sourceforge.mikutoga.pmd.IllegalPmdDataException; +import java.awt.Color; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import jp.sourceforge.mikutoga.binio.BinaryExporter; +import jp.sourceforge.mikutoga.binio.IllegalTextExportException; +import jp.sourceforge.mikutoga.math.MkPos2D; +import jp.sourceforge.mikutoga.math.MkPos3D; +import jp.sourceforge.mikutoga.math.MkVec3D; +import jp.sourceforge.mikutoga.pmd.BoneType; +import jp.sourceforge.mikutoga.pmd.MorphType; +import jp.sourceforge.mikutoga.pmd.model.BoneGroup; +import jp.sourceforge.mikutoga.pmd.model.BoneInfo; +import jp.sourceforge.mikutoga.pmd.model.IKChain; +import jp.sourceforge.mikutoga.pmd.model.Material; +import jp.sourceforge.mikutoga.pmd.model.MorphPart; +import jp.sourceforge.mikutoga.pmd.model.MorphVertex; +import jp.sourceforge.mikutoga.pmd.model.PmdModel; +import jp.sourceforge.mikutoga.pmd.model.SerialNumbered; +import jp.sourceforge.mikutoga.pmd.model.ShadeInfo; +import jp.sourceforge.mikutoga.pmd.model.Surface; +import jp.sourceforge.mikutoga.pmd.model.Vertex; +import jp.sourceforge.mikutoga.pmd.parser.PmdLimits; + +/** + * PMDファイルのエクスポーター(拡張無し基本フォーマット)。 + *

+ * 英名対応以降のPMDファイルフォーマットを + * 使いたくない場合はこのエクスポーターを用いて出力せよ。 + */ +public class PmdExporterBase extends BinaryExporter{ + + /** 前(親)ボーンが無い場合の便宜的なボーンID。 */ + public static final int NOPREVBONE_ID = 0xffff; + /** 次(子)ボーンが無い場合の便宜的なボーンID。 */ + public static final int NONEXTBONE_ID = 0x0000; + /** 影響元IKボーンが無い場合の便宜的なボーンID。 */ + public static final int NOIKBONE_ID = 0x0000; + + private static final byte[] MAGIC_BYTES = { + (byte)0x50, (byte)0x6d, (byte)0x64, // "Pmd" + (byte)0x00, (byte)0x00, (byte)0x80, (byte)0x3f, // 1.0f + }; + + private static final byte[] NULLFILLER = + { (byte)0x00 }; + private static final byte[] FDFILLER = + { (byte)0x00, (byte)0xfd }; + private static final byte[] LFFILLER = + { (byte)0x0a, (byte)0x00, (byte)0xfd }; + + /** 改行文字列 CR。 */ + private static final String CR = "\r"; // 0x0d + /** 改行文字列 LF。 */ + private static final String LF = "\n"; // 0x0a + /** 改行文字列 CRLF。 */ + private static final String CRLF = CR + LF; // 0x0d, 0x0a + + static{ + assert NOPREVBONE_ID > PmdLimits.MAX_BONE - 1; + } + + /** + * コンストラクタ。 + * @param stream 出力ストリーム + * @throws NullPointerException 引数がnull + */ + public PmdExporterBase(OutputStream stream) + throws NullPointerException{ + super(stream); + return; + } + + /** + * 改行文字の正規化を行う。 + * CR(0x0d)およびCRLF(0x0d0a)がLF(0x0a)へと正規化される。 + * @param text 文字列 + * @return 正規化の行われた文字列。 + */ + protected static String normalizeBreak(String text){ + String result = text; + + result = result.replace(CRLF, LF); + result = result.replace(CR, LF); + + return result; + } + + /** + * 文字列を指定されたバイト長で出力する。 + * 文字列の改行記号はLF(0x0a)に正規化される。 + * エンコード結果がバイト長に満たない場合は + * 1つの0x00及びそれに続く複数の0xfdがパディングされる。 + * @param text 文字列 + * @param maxByteLength バイト長指定 + * @throws IOException 出力エラー + * @throws IllegalPmdTextException エンコード結果が + * 指定バイト長をはみ出した。 + */ + protected void dumpText(String text, int maxByteLength) + throws IOException, IllegalTextExportException{ + dumpFixedW31j(text, maxByteLength, FDFILLER); + return; + } + + /** + * モデルデータをPMDファイル形式で出力する。 + * @param model モデルデータ + * @throws IOException 出力エラー + * @throws IllegalPmdDataException モデルデータに不備が発見された + */ + public void dumpPmdModel(PmdModel model) + throws IOException, IllegalPmdDataException{ + try{ + dumpBasic(model); + dumpVertexList(model); + dumpSurfaceList(model); + dumpMaterialList(model); + dumpBoneList(model); + dumpIKChainList(model); + dumpMorphList(model); + dumpMorphGroup(model); + dumpBoneGroupList(model); + }catch(IllegalTextExportException e){ + throw new IllegalPmdDataException(e); + } + + return; + } + + /** + * モデル基本情報を出力する。 + * @param model モデルデータ + * @throws IOException 出力エラー + * @throws IllegalPmdTextException モデル名もしくは説明が長すぎる + */ + private void dumpBasic(PmdModel model) + throws IOException, IllegalTextExportException{ + for(int idx=0; idx < MAGIC_BYTES.length; idx++){ + dumpByte(MAGIC_BYTES[idx]); + } + + String modelName = model.getModelName() .getPrimaryText(); + String description = model.getDescription().getPrimaryText(); + + dumpText(modelName, PmdLimits.MAXBYTES_MODELNAME); + dumpText(description, PmdLimits.MAXBYTES_MODELDESC); + + flush(); + + return; + } + + /** + * 頂点リストを出力する。 + * @param model モデルデータ + * @throws IOException 出力エラー + */ + private void dumpVertexList(PmdModel model) + throws IOException{ + List vList = model.getVertexList(); + + int vertexNum = vList.size(); + dumpInt(vertexNum); + + for(Vertex vertex : vList){ + dumpVertex(vertex); + } + + flush(); + + return; + } + + /** + * 個別の頂点データを出力する。 + * @param vertex 頂点 + * @throws IOException 出力エラー + */ + private void dumpVertex(Vertex vertex) + throws IOException{ + MkPos3D position = vertex.getPosition(); + dumpPos3D(position); + + MkVec3D normal = vertex.getNormal(); + dumpVec3D(normal); + + MkPos2D uv = vertex.getUVPosition(); + dumpPos2d(uv); + + BoneInfo boneA = vertex.getBoneA(); + BoneInfo boneB = vertex.getBoneB(); + dumpSerialIdAsShort(boneA); + dumpSerialIdAsShort(boneB); + + int weight = vertex.getWeightA(); + dumpByte((byte)weight); + + byte edgeFlag; + boolean hasEdge = vertex.getEdgeAppearance(); + if(hasEdge) edgeFlag = 0x00; + else edgeFlag = 0x01; + dumpByte(edgeFlag); + + return; + } + + /** + * 面リストを出力する。 + * @param model モデルデータ + * @throws IOException 出力エラー + */ + private void dumpSurfaceList(PmdModel model) + throws IOException{ + int surfaceNum = 0; + List materialList = model.getMaterialList(); + for(Material material : materialList){ + surfaceNum += material.getSurfaceList().size(); + } + dumpInt(surfaceNum * 3); + + Vertex[] triangle = new Vertex[3]; + for(Material material : materialList){ + for(Surface surface : material){ + surface.getTriangle(triangle); + dumpShort(triangle[0].getSerialNumber()); + dumpShort(triangle[1].getSerialNumber()); + dumpShort(triangle[2].getSerialNumber()); + } + } + + flush(); + + return; + } + + /** + * マテリアル素材リストを出力する。 + * @param model モデルデータ + * @throws IOException 出力エラー + * @throws IllegalPmdTextException シェーディングファイル情報が長すぎる + */ + private void dumpMaterialList(PmdModel model) + throws IOException, IllegalTextExportException{ + List materialList = model.getMaterialList(); + + int materialNum = materialList.size(); + dumpInt(materialNum); + + float[] rgba = new float[4]; + + for(Material material : materialList){ + Color diffuse = material.getDiffuseColor(); + diffuse.getRGBComponents(rgba); + dumpFloat(rgba[0]); + dumpFloat(rgba[1]); + dumpFloat(rgba[2]); + dumpFloat(rgba[3]); + + float shininess = material.getShininess(); + dumpFloat(shininess); + + Color specular = material.getSpecularColor(); + specular.getRGBComponents(rgba); + dumpFloat(rgba[0]); + dumpFloat(rgba[1]); + dumpFloat(rgba[2]); + + Color ambient = material.getAmbientColor(); + ambient.getRGBComponents(rgba); + dumpFloat(rgba[0]); + dumpFloat(rgba[1]); + dumpFloat(rgba[2]); + + ShadeInfo shade = material.getShadeInfo(); + int toonIdx = shade.getToonIndex(); + dumpByte(toonIdx); + + byte edgeFlag; + boolean showEdge = material.getEdgeAppearance(); + if(showEdge) edgeFlag = 0x01; + else edgeFlag = 0x00; + dumpByte(edgeFlag); + + int surfaceNum = material.getSurfaceList().size(); + dumpInt(surfaceNum * 3); + + dumpShadeFileInfo(shade); + } + + flush(); + + return; + } + + /** + * シェーディングファイル情報を出力する。 + * @param shade シェーディング情報 + * @throws IOException 出力エラー + * @throws IllegalTextExportException ファイル名が長すぎる + */ + private void dumpShadeFileInfo(ShadeInfo shade) + throws IOException, IllegalTextExportException{ + String textureFile = shade.getTextureFileName(); + String spheremapFile = shade.getSpheremapFileName(); + + StringBuilder text = new StringBuilder(); + if(textureFile != null) text.append(textureFile); + if(spheremapFile != null && spheremapFile.length() > 0){ + text.append('*') + .append(spheremapFile); + } + + byte[] filler; + if(text.length() <= 0) filler = NULLFILLER; + else filler = FDFILLER; + + dumpFixedW31j(text.toString(), + PmdLimits.MAXBYTES_TEXTUREFILENAME, + filler ); + + return; + } + + /** + * ボーンリストを出力する。 + * @param model モデルデータ + * @throws IOException 出力エラー + * @throws IllegalPmdTextException ボーン名が長すぎる + */ + private void dumpBoneList(PmdModel model) + throws IOException, IllegalTextExportException{ + List boneList = model.getBoneList(); + + int boneNum = boneList.size(); + dumpShort(boneNum); + + for(BoneInfo bone : boneList){ + dumpBone(bone); + } + + flush(); + + return; + } + + /** + * 個別のボーン情報を出力する。 + * @param bone ボーン情報 + * @throws IOException 出力エラー + * @throws IllegalPmdTextException ボーン名が長すぎる + */ + private void dumpBone(BoneInfo bone) + throws IOException, IllegalTextExportException{ + String boneName = bone.getBoneName().getPrimaryText(); + dumpText(boneName, PmdLimits.MAXBYTES_BONENAME); + + BoneInfo prev = bone.getPrevBone(); + if(prev != null) dumpSerialIdAsShort(prev); + else dumpShort(NOPREVBONE_ID); + + BoneInfo next = bone.getNextBone(); + if(next != null) dumpSerialIdAsShort(next); + else dumpShort(NONEXTBONE_ID); + + BoneType type = bone.getBoneType(); + dumpByte(type.encode()); + + if(type == BoneType.LINKEDROT){ + int ratio = bone.getRotationRatio(); + dumpShort(ratio); + }else{ + BoneInfo ik = bone.getIKBone(); + if(ik != null) dumpSerialIdAsShort(ik); + else dumpShort(NOIKBONE_ID); + } + + MkPos3D position = bone.getPosition(); + dumpPos3D(position); + + return; + } + + /** + * IKチェーンリストを出力する。 + * @param model モデルデータ + * @throws IOException 出力エラー + */ + private void dumpIKChainList(PmdModel model) + throws IOException{ + List ikChainList = model.getIKChainList(); + + int ikNum = ikChainList.size(); + dumpShort(ikNum); + + for(IKChain chain : ikChainList){ + dumpIKChain(chain); + } + + flush(); + + return; + } + + /** + * IKチェーンを出力する。 + * @param chain IKチェーン + * @throws IOException 出力エラー + */ + // TODO ボーンリストから自動抽出できる情報ではないのか? + private void dumpIKChain(IKChain chain) + throws IOException{ + BoneInfo ikBone = chain.getIkBone(); + dumpSerialIdAsShort(ikBone); + + List boneList = chain.getChainedBoneList(); + + BoneInfo bone1st = boneList.get(0); + dumpSerialIdAsShort(bone1st); + + int boneNum = boneList.size(); + dumpByte(boneNum - 1); + + int depth = chain.getIKDepth(); + float weight = chain.getIKWeight(); + + dumpShort(depth); + dumpFloat(weight); + + for(int idx = 1; idx < boneNum; idx++){ // リストの2番目以降全て + BoneInfo bone = boneList.get(idx); + dumpSerialIdAsShort(bone); + } + + return; + } + + /** + * モーフリストを出力する。 + * @param model モデルデータ + * @throws IOException 出力エラー + * @throws IllegalPmdTextException モーフ名が長すぎる + */ + private void dumpMorphList(PmdModel model) + throws IOException, IllegalTextExportException{ + Map> morphMap = model.getMorphMap(); + Set typeSet = morphMap.keySet(); + List mergedMorphVertexList = model.mergeMorphVertex(); + + int totalMorphPart = 0; + for(MorphType type : typeSet){ + List partList = morphMap.get(type); + if(partList == null) continue; + totalMorphPart += partList.size(); + } + + if(totalMorphPart <= 0){ + dumpShort(0); + return; + }else{ + totalMorphPart++; // baseの分 + dumpShort(totalMorphPart); + } + + dumpText("base", PmdLimits.MAXBYTES_MORPHNAME); + int totalVertex = mergedMorphVertexList.size(); + dumpInt(totalVertex); + dumpByte(MorphType.BASE.encode()); + for(MorphVertex morphVertex : mergedMorphVertexList){ + Vertex baseVertex = morphVertex.getBaseVertex(); + dumpInt(baseVertex.getSerialNumber()); + dumpPos3D(baseVertex.getPosition()); + } + + for(MorphType type : typeSet){ + List partList = morphMap.get(type); + if(partList == null) continue; + for(MorphPart part : partList){ + dumpText(part.getMorphName().getPrimaryText(), + PmdLimits.MAXBYTES_MORPHNAME ); + List morphVertexList = part.getMorphVertexList(); + dumpInt(morphVertexList.size()); + dumpByte(part.getMorphType().encode()); + + for(MorphVertex morphVertex : morphVertexList){ + dumpInt(morphVertex.getSerialNumber()); + dumpPos3D(morphVertex.getOffset()); + } + } + } + + flush(); + + return; + } + + /** + * モーフグループを出力する。 + * @param model モデルデータ + * @throws IOException 出力エラー + */ + private void dumpMorphGroup(PmdModel model) + throws IOException{ + Map> morphMap = model.getMorphMap(); + Set typeSet = morphMap.keySet(); + + int totalMorph = 0; + for(MorphType type : typeSet){ + List partList = morphMap.get(type); + if(partList == null) continue; + totalMorph += partList.size(); + } + dumpByte(totalMorph); + + List typeList = new LinkedList(); + for(MorphType type : typeSet){ + assert ! type.isBase(); + typeList.add(type); + } + Collections.reverse(typeList); // 一応本家と互換性を + + for(MorphType type : typeList){ + List partList = morphMap.get(type); + if(partList == null) continue; + for(MorphPart part : partList){ + dumpSerialIdAsShort(part); + } + } + + flush(); + + return; + } + + /** + * ボーングループリストを出力する。 + * デフォルトボーングループ内訳は出力されない。 + * @param model モデルデータ + * @throws IOException 出力エラー + * @throws IllegalTextExportException ボーングループ名が長すぎる + */ + private void dumpBoneGroupList(PmdModel model) + throws IOException, IllegalTextExportException{ + List groupList = model.getBoneGroupList(); + int groupNum = groupList.size(); + dumpByte(groupNum - 1); + + int dispBoneNum = 0; + for(BoneGroup group : groupList){ + if(group.isDefaultBoneGroup()) continue; + dumpFixedW31j(group.getGroupName().getPrimaryText(), + PmdLimits.MAXBYTES_BONEGROUPNAME, LFFILLER ); + dispBoneNum += group.getBoneList().size(); + } + dumpInt(dispBoneNum); + + for(BoneGroup group : groupList){ + if(group.isDefaultBoneGroup()) continue; + for(BoneInfo bone : group){ + dumpSerialIdAsShort(bone); + int groupId = group.getSerialNumber(); + dumpByte(groupId); + } + } + + flush(); + + return; + } + + /** + * 各種通し番号をshort値で出力する。 + * short値に収まらない上位ビットは捨てられる。 + * @param obj 番号づけられたオブジェクト + * @throws IOException 出力エラー + */ + protected void dumpSerialIdAsShort(SerialNumbered obj) + throws IOException{ + int serialId = obj.getSerialNumber(); + dumpShort(serialId); + return; + } + + /** + * 2次元位置情報を出力する。 + * @param position 2次元位置情報 + * @throws IOException 出力エラー + */ + protected void dumpPos2d(MkPos2D position) throws IOException{ + float xPos = (float) position.getXpos(); + float yPos = (float) position.getYpos(); + + dumpFloat(xPos); + dumpFloat(yPos); + + return; + } + + /** + * 3次元位置情報を出力する。 + * @param position 3次元位置情報 + * @throws IOException 出力エラー + */ + protected void dumpPos3D(MkPos3D position) throws IOException{ + float xPos = (float) position.getXpos(); + float yPos = (float) position.getYpos(); + float zPos = (float) position.getZpos(); + + dumpFloat(xPos); + dumpFloat(yPos); + dumpFloat(zPos); + + return; + } + + /** + * 3次元ベクトル情報を出力する。 + * @param vector 3次元ベクトル + * @throws IOException 出力エラー + */ + protected void dumpVec3D(MkVec3D vector) throws IOException{ + float xVal = (float) vector.getXVal(); + float yVal = (float) vector.getYVal(); + float zVal = (float) vector.getZVal(); + + dumpFloat(xVal); + dumpFloat(yVal); + dumpFloat(zVal); + + return; + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/PmdExporterExt1.java b/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/PmdExporterExt1.java new file mode 100644 index 0000000..c55cedd --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/PmdExporterExt1.java @@ -0,0 +1,166 @@ +/* + * model exporter for pmd-file(Ext1) + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.model.binio; + +import jp.sourceforge.mikutoga.pmd.IllegalPmdDataException; +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; +import java.util.Map; +import jp.sourceforge.mikutoga.binio.IllegalTextExportException; +import jp.sourceforge.mikutoga.pmd.MorphType; +import jp.sourceforge.mikutoga.pmd.model.BoneGroup; +import jp.sourceforge.mikutoga.pmd.model.BoneInfo; +import jp.sourceforge.mikutoga.pmd.model.MorphPart; +import jp.sourceforge.mikutoga.pmd.model.PmdModel; +import jp.sourceforge.mikutoga.pmd.parser.PmdLimits; + +/** + * PMDファイルのエクスポーター(拡張1:英名対応)。 + *

+ * 任意のトゥーンファイル名対応以降のPMDファイルフォーマットを + * 使いたくない場合はこのエクスポーターを用いて出力せよ。 + */ +public class PmdExporterExt1 extends PmdExporterBase{ + + /** + * コンストラクタ。 + * @param stream 出力ストリーム + * @throws NullPointerException 引数がnull + */ + public PmdExporterExt1(OutputStream stream) + throws NullPointerException{ + super(stream); + return; + } + + /** + * {@inheritDoc} + * @param model {@inheritDoc} + * @throws IOException {@inheritDoc} + * @throws IllegalPmdDataException {@inheritDoc} + */ + @Override + public void dumpPmdModel(PmdModel model) + throws IOException, IllegalPmdDataException{ + super.dumpPmdModel(model); + + dumpGlobalInfo(model); + + return; + } + + /** + * 英語名情報を出力する。 + * @param model モデルデータ + * @throws IOException 出力エラー + * @throws IllegalPmdTextException 文字列が長すぎる。 + */ + private void dumpGlobalInfo(PmdModel model) + throws IOException, IllegalPmdDataException{ + boolean hasGlobal = model.hasGlobalText(); + byte globalFlag; + if(hasGlobal) globalFlag = 0x01; + else globalFlag = 0x00; + dumpByte(globalFlag); + + if(hasGlobal){ + try{ + dumpBasicGlobal(model); + dumpBoneGlobal(model); + dumpMorphGlobal(model); + dumpBoneGroupGlobal(model); + }catch(IllegalTextExportException e){ + throw new IllegalPmdDataException(e); + } + } + + flush(); + + return; + } + + /** + * モデル基本情報を英語で出力する。 + * @param model モデルデータ + * @throws IOException 出力エラー + * @throws IllegalPmdTextException 文字列が長すぎる。 + */ + private void dumpBasicGlobal(PmdModel model) + throws IOException, IllegalTextExportException{ + String modelName = model.getModelName().getGlobalText(); + if(modelName == null) modelName = ""; + dumpText(modelName, PmdLimits.MAXBYTES_MODELNAME); + + String description = model.getDescription().getGlobalText(); + if(description == null) description = ""; + dumpText(description, PmdLimits.MAXBYTES_MODELDESC); + + flush(); + } + + /** + * ボーン英語名情報を出力する。 + * @param model モデルデータ + * @throws IOException 出力エラー + * @throws IllegalPmdTextException 文字列が長すぎる。 + */ + private void dumpBoneGlobal(PmdModel model) + throws IOException, IllegalTextExportException{ + for(BoneInfo bone : model.getBoneList()){ + String boneName = bone.getBoneName().getGlobalText(); + if(boneName == null) boneName = ""; + dumpText(boneName, PmdLimits.MAXBYTES_BONENAME); + } + + flush(); + } + + /** + * モーフ英語名情報を出力する。 + * @param model モデルデータ + * @throws IOException 出力エラー + * @throws IllegalPmdTextException 文字列が長すぎる。 + */ + private void dumpMorphGlobal(PmdModel model) + throws IOException, IllegalTextExportException{ + Map> morphMap = model.getMorphMap(); + + for(MorphType type : MorphType.values()){ + if(type.isBase()) continue; + List partList = morphMap.get(type); + if(partList == null) continue; + for(MorphPart part : partList){ + String morphName = part.getMorphName().getGlobalText(); + if(morphName == null) morphName = ""; + dumpText(morphName, PmdLimits.MAXBYTES_MORPHNAME); + } + } + + flush(); + } + + /** + * ボーングループ英語名情報を出力する。 + * @param model モデルデータ + * @throws IOException 出力エラー + * @throws IllegalPmdTextException 文字列が長すぎる + */ + private void dumpBoneGroupGlobal(PmdModel model) + throws IOException, IllegalTextExportException{ + for(BoneGroup group : model.getBoneGroupList()){ + if(group.isDefaultBoneGroup()) continue; + String groupName = group.getGroupName().getGlobalText(); + if(groupName == null) groupName = ""; + dumpText(groupName, PmdLimits.MAXBYTES_BONEGROUPNAME); + } + + flush(); + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/PmdExporterExt2.java b/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/PmdExporterExt2.java new file mode 100644 index 0000000..4fcc46e --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/PmdExporterExt2.java @@ -0,0 +1,78 @@ +/* + * model exporter for pmd-file(Ext2) + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.model.binio; + +import jp.sourceforge.mikutoga.pmd.IllegalPmdDataException; +import java.io.IOException; +import java.io.OutputStream; +import jp.sourceforge.mikutoga.binio.IllegalTextExportException; +import jp.sourceforge.mikutoga.pmd.model.PmdModel; +import jp.sourceforge.mikutoga.pmd.model.ToonMap; +import jp.sourceforge.mikutoga.pmd.parser.PmdLimits; + +/** + * PMDファイルのエクスポーター(拡張2:任意のトゥーンファイル名対応)。 + *

+ * 物理演算対応以降のPMDファイルフォーマットを + * 使いたくない場合はこのエクスポーターを用いて出力せよ。 + */ +public class PmdExporterExt2 extends PmdExporterExt1{ + + /** + * コンストラクタ。 + * @param stream 出力ストリーム + * @throws NullPointerException 引数がnull + */ + public PmdExporterExt2(OutputStream stream) + throws NullPointerException{ + super(stream); + return; + } + + /** + * {@inheritDoc} + * @param model {@inheritDoc} + * @throws IOException {@inheritDoc} + * @throws IllegalPmdDataException {@inheritDoc} + */ + @Override + public void dumpPmdModel(PmdModel model) + throws IOException, IllegalPmdDataException{ + super.dumpPmdModel(model); + + try{ + dumpToonMap(model); + }catch(IllegalTextExportException e){ + throw new IllegalPmdDataException(e); + } + + return; + } + + /** + * 独自トゥーンファイルテーブルを出力する。 + * @param model モデルデータ + * @throws IOException 出力エラー + * @throws IllegalPmdTextException トゥーンファイル名が長すぎる + */ + private void dumpToonMap(PmdModel model) + throws IOException, IllegalTextExportException{ + ToonMap map = model.getToonMap(); + + for(int idx = 0; idx < PmdLimits.TOON_FIXEDNUM; idx++){ + String toonName = map.getIndexedToon(idx); + if(toonName == null) toonName = ""; + dumpText(toonName, PmdLimits.MAXBYTES_TOONFILENAME); + } + + flush(); + + return; + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/PmdExporterExt3.java b/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/PmdExporterExt3.java new file mode 100644 index 0000000..b496a54 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/PmdExporterExt3.java @@ -0,0 +1,283 @@ +/* + * model exporter for pmd-file(Ext3) + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.model.binio; + +import jp.sourceforge.mikutoga.pmd.IllegalPmdDataException; +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; +import jp.sourceforge.mikutoga.binio.IllegalTextExportException; +import jp.sourceforge.mikutoga.pmd.Deg3d; +import jp.sourceforge.mikutoga.pmd.Rad3d; +import jp.sourceforge.mikutoga.pmd.RigidShapeType; +import jp.sourceforge.mikutoga.pmd.TripletRange; +import jp.sourceforge.mikutoga.pmd.model.BoneInfo; +import jp.sourceforge.mikutoga.pmd.model.DynamicsInfo; +import jp.sourceforge.mikutoga.pmd.model.JointInfo; +import jp.sourceforge.mikutoga.pmd.model.PmdModel; +import jp.sourceforge.mikutoga.pmd.model.RigidGroup; +import jp.sourceforge.mikutoga.pmd.model.RigidInfo; +import jp.sourceforge.mikutoga.pmd.model.RigidShape; +import jp.sourceforge.mikutoga.pmd.parser.PmdLimits; + +/** + * PMDファイルのエクスポーター(拡張3:物理演算対応)。 + *

+ * 物理演算対応のPMDファイルフォーマットを + * 使いたい場合はこのエクスポーターを用いて出力せよ。 + */ +public class PmdExporterExt3 extends PmdExporterExt2{ + + private static final short MASK_FULLCOLLISION = (short) 0xffff; + + /** + * コンストラクタ。 + * @param stream 出力ストリーム + * @throws NullPointerException 引数がnull + */ + public PmdExporterExt3(OutputStream stream) + throws NullPointerException{ + super(stream); + return; + } + + /** + * {@inheritDoc} + * @param model {@inheritDoc} + * @throws IOException {@inheritDoc} + * @throws IllegalPmdDataException {@inheritDoc} + */ + @Override + public void dumpPmdModel(PmdModel model) + throws IOException, IllegalPmdDataException{ + super.dumpPmdModel(model); + + try{ + dumpRigidList(model); + dumpJointList(model); + }catch(IllegalTextExportException e){ + throw new IllegalPmdDataException(e); + } + + return; + } + + /** + * 剛体リストを出力する。 + * @param model モデルデータ + * @throws IOException 出力エラー + * @throws IllegalPmdTextException 長すぎる剛体名 + */ + private void dumpRigidList(PmdModel model) + throws IOException, IllegalTextExportException{ + List rigidList = model.getRigidList(); + int rigidNum = rigidList.size(); + dumpInt(rigidNum); + + for(RigidInfo rigid : rigidList){ + dumpRigid(rigid); + } + + flush(); + + return; + } + + /** + * 個別の剛体情報を出力する。 + * @param rigid 剛体 + * @throws IOException 出力エラー + * @throws IllegalPmdTextException 長すぎる剛体名 + */ + private void dumpRigid(RigidInfo rigid) + throws IOException, IllegalTextExportException{ + String rigidName = rigid.getRigidName().getPrimaryText(); + dumpText(rigidName, PmdLimits.MAXBYTES_RIGIDNAME); + + BoneInfo linkedBone = rigid.getLinkedBone(); + if(linkedBone == null){ + dumpShort(-1); + }else{ + dumpShort(linkedBone.getSerialNumber()); + } + + RigidGroup group = rigid.getRigidGroup(); + dumpByte(group.getSerialNumber()); + + short mask = MASK_FULLCOLLISION; + for(RigidGroup throughGroup : rigid.getThroughGroupColl()){ + int serialId = throughGroup.getSerialNumber(); + mask &= ~(0x0001 << serialId); + } + dumpShort(mask); + + dumpRigidShape(rigid.getRigidShape()); + + dumpPos3D(rigid.getPosition()); + dumpRad3d(rigid.getRotation()); + + dumpDynamics(rigid.getDynamicsInfo()); + + dumpByte(rigid.getBehaviorType().encode()); + + return; + } + + /** + * 剛体形状を出力する。 + * @param shape 剛体形状 + * @throws IOException 出力エラー + */ + private void dumpRigidShape(RigidShape shape) + throws IOException{ + RigidShapeType type = shape.getShapeType(); + dumpByte(type.encode()); + + float width = shape.getWidth(); + float height = shape.getHeight(); + float depth = shape.getDepth(); + + dumpFloat(width); + dumpFloat(height); + dumpFloat(depth); + + return; + } + + /** + * 力学設定を出力する。 + * @param dynamics 力学設定 + * @throws IOException 出力エラー + */ + private void dumpDynamics(DynamicsInfo dynamics) + throws IOException{ + float mass = dynamics.getMass(); + float dampPos = dynamics.getDampingPosition(); + float dampRot = dynamics.getDampingRotation(); + float restitution = dynamics.getRestitution(); + float friction = dynamics.getFriction(); + + dumpFloat(mass); + dumpFloat(dampPos); + dumpFloat(dampRot); + dumpFloat(restitution); + dumpFloat(friction); + + return; + } + + /** + * ジョイントリストを出力する。 + * @param model モデルデータ + * @throws IOException 出力エラー + * @throws IllegalPmdTextException 長すぎるジョイント名 + */ + private void dumpJointList(PmdModel model) + throws IOException, IllegalTextExportException{ + List jointList = model.getJointList(); + int jointNum = jointList.size(); + dumpInt(jointNum); + + for(JointInfo joint : jointList){ + dumpJoint(joint); + } + + flush(); + + return; + } + + /** + * 個別のジョイント情報を出力する。 + * @param joint ジョイント + * @throws IOException 出力エラー + * @throws IllegalPmdTextException 長すぎるジョイント名 + */ + private void dumpJoint(JointInfo joint) + throws IOException, IllegalTextExportException{ + String jointName = joint.getJointName().getPrimaryText(); + dumpText(jointName, PmdLimits.MAXBYTES_JOINTNAME); + + RigidInfo rigidA = joint.getRigidA(); + RigidInfo rigidB = joint.getRigidB(); + + dumpInt(rigidA.getSerialNumber()); + dumpInt(rigidB.getSerialNumber()); + + dumpPos3D(joint.getPosition()); + dumpRad3d(joint.getRotation()); + + dumpTripletRange(joint.getPositionRange()); + dumpTripletRange(joint.getRotationRange()); + + dumpPos3D(joint.getElasticPosition()); + dumpDeg3d(joint.getElasticRotation()); + + return; + } + + /** + * 3次元範囲制約を出力する。 + * @param range 3次元範囲制約 + * @throws IOException 出力エラー + */ + protected void dumpTripletRange(TripletRange range) throws IOException{ + float xFrom = range.getXFrom(); + float yFrom = range.getYFrom(); + float zFrom = range.getZFrom(); + + dumpFloat(xFrom); + dumpFloat(yFrom); + dumpFloat(zFrom); + + float xTo = range.getXTo(); + float yTo = range.getYTo(); + float zTo = range.getZTo(); + + dumpFloat(xTo); + dumpFloat(yTo); + dumpFloat(zTo); + + return; + } + + /** + * ラジアンによる3次元姿勢情報を出力する。 + * @param rad 3次元姿勢情報 + * @throws IOException 出力エラー + */ + protected void dumpRad3d(Rad3d rad) throws IOException{ + float xVal = rad.getXRad(); + float yVal = rad.getYRad(); + float zVal = rad.getZRad(); + + dumpFloat(xVal); + dumpFloat(yVal); + dumpFloat(zVal); + + return; + } + + /** + * 度数法による3次元姿勢情報を出力する。 + * @param deg 3次元姿勢情報 + * @throws IOException 出力エラー + */ + protected void dumpDeg3d(Deg3d deg) throws IOException{ + float xVal = deg.getXDeg(); + float yVal = deg.getYDeg(); + float zVal = deg.getZDeg(); + + dumpFloat(xVal); + dumpFloat(yVal); + dumpFloat(zVal); + + return; + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/PmdLoader.java b/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/PmdLoader.java new file mode 100644 index 0000000..921cfed --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/PmdLoader.java @@ -0,0 +1,117 @@ +/* + * PMD file loader + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.model.binio; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import jp.sourceforge.mikutoga.parser.MmdFormatException; +import jp.sourceforge.mikutoga.parser.MmdSource; +import jp.sourceforge.mikutoga.pmd.model.MorphPart; +import jp.sourceforge.mikutoga.pmd.model.PmdModel; +import jp.sourceforge.mikutoga.pmd.parser.PmdParser; + +/** + * PMDモデルファイルを読み込むためのローダ。 + */ +public class PmdLoader { + + private PmdModel model; + private PmdParser parser; + private TextBuilder textBuilder; + + private boolean loaded = false; + private boolean hasMoreData = false; + + /** + * コンストラクタ。 + * @param source PMDファイル入力ソース + */ + public PmdLoader(MmdSource source){ + super(); + + this.model = new PmdModel(); + this.parser = new PmdParser(source); + this.textBuilder = new TextBuilder(this.model); + + setHandler(); + + return; + } + + /** + * パーサに各種ハンドラの設定を行う。 + */ + private void setHandler(){ + ShapeBuilder shapeBuilder = new ShapeBuilder(this.model); + MaterialBuilder materialBuilder = new MaterialBuilder(this.model); + BoneBuilder boneBuilder = new BoneBuilder(this.model); + MorphBuilder morphBuilder = new MorphBuilder(this.model); + ToonBuilder toonBuilder = new ToonBuilder(this.model); + RigidBuilder rigidBuilder = new RigidBuilder(this.model); + JointBuilder jointBuilder = new JointBuilder(this.model); + + this.parser.setBasicHandler(this.textBuilder); + this.parser.setShapeHandler(shapeBuilder); + this.parser.setMaterialHandler(materialBuilder); + this.parser.setBoneHandler(boneBuilder); + this.parser.setMorphHandler(morphBuilder); + this.parser.setEngHandler(this.textBuilder); + this.parser.setToonHandler(toonBuilder); + this.parser.setRigidHandler(rigidBuilder); + this.parser.setJointHandler(jointBuilder); + + List morphPartList = new ArrayList(); + morphBuilder.setMorphPartList(morphPartList); + this.textBuilder.setMorphPartList(morphPartList); + morphPartList.clear(); + + return; + } + + /** + * PMDファイルの読み込みを行いモデル情報を返す。 + * 1インスタンスにつき一度しかロードできない。 + * @return モデル情報 + * @throws IOException 入力エラー + * @throws MmdFormatException PMDファイルフォーマットの異常を検出 + * @throws IllegalStateException このインスタンスで再度のロードを試みた。 + */ + public PmdModel load() + throws IOException, + MmdFormatException, + IllegalStateException { + if(this.loaded) throw new IllegalStateException(); + + PmdModel result; + try{ + this.parser.parsePmd(); + }finally{ + this.loaded = true; + + result = this.model; + this.hasMoreData = this.textBuilder.hasMoreData(); + + this.model = null; + this.parser = null; + this.textBuilder = null; + } + + return result; + } + + /** + * ロード処理が正常終了したのにまだ読み込んでいない部分が放置されているか判定する。 + * MMDでの仕様拡張によるPMDファイルフォーマットの拡張が行われた場合を想定。 + * @return 読み込んでいない部分があればtrue + */ + public boolean hasMoreData(){ + return this.hasMoreData; + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/RigidBuilder.java b/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/RigidBuilder.java new file mode 100644 index 0000000..caeb862 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/RigidBuilder.java @@ -0,0 +1,227 @@ +/* + * building rigid information + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.model.binio; + +import java.util.Iterator; +import java.util.List; +import jp.sourceforge.mikutoga.pmd.model.ListUtil; +import jp.sourceforge.mikutoga.math.MkPos3D; +import jp.sourceforge.mikutoga.parser.ParseStage; +import jp.sourceforge.mikutoga.pmd.Rad3d; +import jp.sourceforge.mikutoga.pmd.RigidBehaviorType; +import jp.sourceforge.mikutoga.pmd.RigidShapeType; +import jp.sourceforge.mikutoga.pmd.model.BoneInfo; +import jp.sourceforge.mikutoga.pmd.model.DynamicsInfo; +import jp.sourceforge.mikutoga.pmd.model.PmdModel; +import jp.sourceforge.mikutoga.pmd.model.RigidGroup; +import jp.sourceforge.mikutoga.pmd.model.RigidInfo; +import jp.sourceforge.mikutoga.pmd.model.RigidShape; +import jp.sourceforge.mikutoga.pmd.parser.PmdLimits; +import jp.sourceforge.mikutoga.pmd.parser.PmdRigidHandler; + +/** + * 剛体関係の通知をパーサから受け取る。 + */ +class RigidBuilder implements PmdRigidHandler { + + private final List boneList; + + private final List rigidList; + private Iterator rigidIt; + private RigidInfo currentRigid = null; + + private final List rigidGroupList; + + /** + * コンストラクタ。 + * @param model モデル + */ + RigidBuilder(PmdModel model){ + super(); + this.boneList = model.getBoneList(); + this.rigidList = model.getRigidList(); + this.rigidGroupList = model.getRigidGroupList(); + return; + } + + /** + * {@inheritDoc} + * @param stage {@inheritDoc} + * @param loops {@inheritDoc} + */ + @Override + public void loopStart(ParseStage stage, int loops){ + ListUtil.prepareDefConsList(this.rigidList, RigidInfo.class, loops); + ListUtil.assignIndexedSerial(this.rigidList); + + this.rigidIt = this.rigidList.iterator(); + if(this.rigidIt.hasNext()){ + this.currentRigid = this.rigidIt.next(); + } + + ListUtil.prepareDefConsList(this.rigidGroupList, + RigidGroup.class, + PmdLimits.RIGIDGROUP_FIXEDNUM ); + ListUtil.assignIndexedSerial(this.rigidGroupList); + + return; + } + + /** + * {@inheritDoc} + * @param stage {@inheritDoc} + */ + @Override + public void loopNext(ParseStage stage){ + if(this.rigidIt.hasNext()){ + this.currentRigid = this.rigidIt.next(); + } + return; + } + + /** + * {@inheritDoc} + * @param stage {@inheritDoc} + */ + @Override + public void loopEnd(ParseStage stage){ + return; + } + + /** + * {@inheritDoc} + * @param rigidName {@inheritDoc} + */ + @Override + public void pmdRigidName(String rigidName){ + this.currentRigid.getRigidName().setPrimaryText(rigidName); + return; + } + + /** + * {@inheritDoc} + * @param rigidGroupId {@inheritDoc} + * @param linkedBoneId {@inheritDoc} + */ + @Override + public void pmdRigidInfo(int rigidGroupId, int linkedBoneId){ + BoneInfo bone; + if(linkedBoneId < 0 || 65535 <= linkedBoneId){ + bone = null; + }else{ + bone = this.boneList.get(linkedBoneId); + } + RigidGroup group = this.rigidGroupList.get(rigidGroupId); + + this.currentRigid.setLinkedBone(bone); + this.currentRigid.setRigidGroup(group); + group.getRigidList().add(this.currentRigid); + + return; + } + + /** + * {@inheritDoc} + * @param shapeType {@inheritDoc} + * @param width {@inheritDoc} + * @param height {@inheritDoc} + * @param depth {@inheritDoc} + */ + @Override + public void pmdRigidShape(byte shapeType, + float width, float height, float depth){ + RigidShape shape = this.currentRigid.getRigidShape(); + + shape.setWidth(width); + shape.setHeight(height); + shape.setDepth(depth); + + RigidShapeType type = RigidShapeType.decode(shapeType); + shape.setShapeType(type); + + return; + } + + /** + * {@inheritDoc} + * @param posX {@inheritDoc} + * @param posY {@inheritDoc} + * @param posZ {@inheritDoc} + */ + @Override + public void pmdRigidPosition(float posX, float posY, float posZ){ + MkPos3D position = this.currentRigid.getPosition(); + position.setXpos(posX); + position.setYpos(posY); + position.setZpos(posZ); + return; + } + + /** + * {@inheritDoc} + * @param radX {@inheritDoc} + * @param radY {@inheritDoc} + * @param radZ {@inheritDoc} + */ + @Override + public void pmdRigidRotation(float radX, float radY, float radZ){ + Rad3d rotation = this.currentRigid.getRotation(); + rotation.setXRad(radX); + rotation.setYRad(radY); + rotation.setZRad(radZ); + return; + } + + /** + * {@inheritDoc} + * @param mass {@inheritDoc} + * @param dampingPos {@inheritDoc} + * @param dampingRot {@inheritDoc} + * @param restitution {@inheritDoc} + * @param friction {@inheritDoc} + */ + @Override + public void pmdRigidPhysics(float mass, + float dampingPos, + float dampingRot, + float restitution, + float friction ){ + DynamicsInfo info = this.currentRigid.getDynamicsInfo(); + + info.setMass(mass); + info.setDampingPosition(dampingPos); + info.setDampingRotation(dampingRot); + info.setRestitution(restitution); + info.setFriction(friction); + + return; + } + + /** + * {@inheritDoc} + * @param behaveType {@inheritDoc} + * @param collisionMap {@inheritDoc} + */ + @Override + public void pmdRigidBehavior(byte behaveType, short collisionMap){ + RigidBehaviorType type = RigidBehaviorType.decode(behaveType); + this.currentRigid.setBehaviorType(type); + + for(int bitPos = 0; bitPos < PmdLimits.RIGIDGROUP_FIXEDNUM; bitPos++){ + short mask = 0x0001; + mask <<= bitPos; + if((collisionMap & mask) == 0){ + RigidGroup group = this.rigidGroupList.get(bitPos); + this.currentRigid.getThroughGroupColl().add(group); + } + } + + return; + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/ShapeBuilder.java b/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/ShapeBuilder.java new file mode 100644 index 0000000..02821f5 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/ShapeBuilder.java @@ -0,0 +1,224 @@ +/* + * building shape information + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.model.binio; + +import java.util.Iterator; +import java.util.List; +import java.util.RandomAccess; +import jp.sourceforge.mikutoga.pmd.model.ListUtil; +import jp.sourceforge.mikutoga.math.MkPos2D; +import jp.sourceforge.mikutoga.math.MkPos3D; +import jp.sourceforge.mikutoga.math.MkVec3D; +import jp.sourceforge.mikutoga.parser.ParseStage; +import jp.sourceforge.mikutoga.pmd.model.BoneInfo; +import jp.sourceforge.mikutoga.pmd.model.PmdModel; +import jp.sourceforge.mikutoga.pmd.model.Surface; +import jp.sourceforge.mikutoga.pmd.model.Vertex; +import jp.sourceforge.mikutoga.pmd.parser.PmdShapeHandler; + +/** + * モデル形状に関する通知をパーサから受け取る。 + */ +class ShapeBuilder implements PmdShapeHandler { + + private final List vertexList; + private final List boneList; + private final List surfaceList; + + private Iterator vertexIt; + private Vertex currentVertex = null; + + private Iterator surfaceIt; + private Surface currentSurface = null; + + /** + * コンストラクタ。 + * @param model モデル + */ + ShapeBuilder(PmdModel model){ + super(); + + this.vertexList = model.getVertexList(); + this.boneList = model.getBoneList(); + this.surfaceList = model.getSurfaceList(); + + assert this.vertexList instanceof RandomAccess; + assert this.surfaceList instanceof RandomAccess; + assert this.boneList instanceof RandomAccess; + + return; + } + + /** + * ボーンリスト上にボーンを用意する。 + * すでに指定位置にボーンがあればなにもしない。 + * @param id 0から始まるボーン番号 + * @return 用意されたボーン + */ + private BoneInfo prepareBone(int id){ + ListUtil.extendList(this.boneList, id + 1); + BoneInfo bone = this.boneList.get(id); + if(bone == null){ + bone = new BoneInfo(); + bone.setSerialNumber(id); + this.boneList.set(id, bone); + } + return bone; + } + + /** + * {@inheritDoc} + * @param stage {@inheritDoc} + * @param loops {@inheritDoc} + */ + @Override + public void loopStart(ParseStage stage, int loops){ + if(stage == PmdShapeHandler.VERTEX_LIST){ + ListUtil.prepareDefConsList(this.vertexList, Vertex.class, loops); + ListUtil.assignIndexedSerial(this.vertexList); + + this.vertexIt = this.vertexList.iterator(); + if(this.vertexIt.hasNext()){ + this.currentVertex = this.vertexIt.next(); + } + }else if(stage == PmdShapeHandler.SURFACE_LIST){ + ListUtil.prepareDefConsList(this.surfaceList, + Surface.class, loops ); + ListUtil.assignIndexedSerial(this.surfaceList); + + this.surfaceIt = this.surfaceList.iterator(); + if(this.surfaceIt.hasNext()){ + this.currentSurface = this.surfaceIt.next(); + } + }else{ + assert false; + throw new AssertionError(); + } + return; + } + + /** + * {@inheritDoc} + * @param stage {@inheritDoc} + */ + @Override + public void loopNext(ParseStage stage){ + if(stage == PmdShapeHandler.VERTEX_LIST){ + if(this.vertexIt.hasNext()){ + this.currentVertex = this.vertexIt.next(); + } + }else if(stage == PmdShapeHandler.SURFACE_LIST){ + if(this.surfaceIt.hasNext()){ + this.currentSurface = this.surfaceIt.next(); + } + }else{ + assert false; + throw new AssertionError(); + } + return; + } + + /** + * {@inheritDoc} + * @param stage {@inheritDoc} + */ + @Override + public void loopEnd(ParseStage stage){ + return; + } + + /** + * {@inheritDoc} + * @param xPos {@inheritDoc} + * @param yPos {@inheritDoc} + * @param zPos {@inheritDoc} + */ + @Override + public void pmdVertexPosition(float xPos, float yPos, float zPos){ + MkPos3D position = this.currentVertex.getPosition(); + position.setXpos(xPos); + position.setYpos(yPos); + position.setZpos(zPos); + return; + } + + /** + * {@inheritDoc} + * @param xVec {@inheritDoc} + * @param yVec {@inheritDoc} + * @param zVec {@inheritDoc} + */ + @Override + public void pmdVertexNormal(float xVec, float yVec, float zVec){ + MkVec3D normal = this.currentVertex.getNormal(); + normal.setXVal(xVec); + normal.setYVal(yVec); + normal.setZVal(zVec); + return; + } + + /** + * {@inheritDoc} + * @param uVal {@inheritDoc} + * @param vVal {@inheritDoc} + */ + @Override + public void pmdVertexUV(float uVal, float vVal){ + MkPos2D uv = this.currentVertex.getUVPosition(); + uv.setXpos(uVal); + uv.setYpos(vVal); + return; + } + + /** + * {@inheritDoc} + * @param boneId1 {@inheritDoc} + * @param boneId2 {@inheritDoc} + * @param weightForB1 {@inheritDoc} + */ + @Override + public void pmdVertexWeight(int boneId1, int boneId2, int weightForB1){ + BoneInfo bone1 = prepareBone(boneId1); + BoneInfo bone2 = prepareBone(boneId2); + + this.currentVertex.setBonePair(bone1, bone2); + this.currentVertex.setWeightA(weightForB1); + + return; + } + + /** + * {@inheritDoc} + * @param hideEdge {@inheritDoc} + */ + @Override + public void pmdVertexEdge(boolean hideEdge){ + this.currentVertex.setEdgeAppearance( ! hideEdge ); + return; + } + + /** + * {@inheritDoc} + * @param vertexId1 {@inheritDoc} + * @param vertexId2 {@inheritDoc} + * @param vertexId3 {@inheritDoc} + */ + @Override + public void pmdSurfaceTriangle(int vertexId1, + int vertexId2, + int vertexId3 ){ + Vertex vtx1 = this.vertexList.get(vertexId1); + Vertex vtx2 = this.vertexList.get(vertexId2); + Vertex vtx3 = this.vertexList.get(vertexId3); + + this.currentSurface.setTriangle(vtx1, vtx2, vtx3); + + return; + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/TextBuilder.java b/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/TextBuilder.java new file mode 100644 index 0000000..51c0ac8 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/TextBuilder.java @@ -0,0 +1,246 @@ +/* + * building text info + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.model.binio; + +import java.util.Iterator; +import java.util.List; +import jp.sourceforge.mikutoga.corelib.I18nText; +import jp.sourceforge.mikutoga.parser.ParseStage; +import jp.sourceforge.mikutoga.pmd.model.BoneGroup; +import jp.sourceforge.mikutoga.pmd.model.BoneInfo; +import jp.sourceforge.mikutoga.pmd.model.MorphPart; +import jp.sourceforge.mikutoga.pmd.model.PmdModel; +import jp.sourceforge.mikutoga.pmd.parser.PmdBasicHandler; +import jp.sourceforge.mikutoga.pmd.parser.PmdEngHandler; + +/** + * テキスト関係の通知をパーサから受け取る。 + */ +class TextBuilder implements PmdBasicHandler, PmdEngHandler { + + private final PmdModel model; + + private final I18nText modelName; + private final I18nText description; + + private final List boneList; + private Iterator boneIt; + private BoneInfo currentBone = null; + + private List morphPartList; + private Iterator morphPartIt; + private MorphPart currentMorphPart = null; + + private final List boneGroupList; + private Iterator boneGroupIt; + private BoneGroup currentBoneGroup = null; + + private boolean hasMoreData = false; + + /** + * コンストラクタ。 + * @param model モデル + */ + TextBuilder(PmdModel model){ + super(); + + this.model = model; + + this.modelName = model.getModelName(); + this.description = model.getDescription(); + + this.boneList = model.getBoneList(); + this.boneGroupList = model.getBoneGroupList(); + + return; + } + + /** + * PMDファイル中の出現順で各モーフを格納するためのリストを設定する。 + * 主な用途はモーフ和英名の突き合わせ作業。 + * @param list モーフ格納リスト + */ + void setMorphPartList(List list){ + this.morphPartList = list; + return; + } + + /** + * {@inheritDoc} + */ + @Override + public void pmdParseStart(){ + return; + } + + /** + * {@inheritDoc} + * @param hasMoreData {@inheritDoc} + */ + @Override + public void pmdParseEnd(boolean hasMoreData){ + this.hasMoreData = hasMoreData; + return; + } + + /** + * {@inheritDoc} + * @param stage {@inheritDoc} + * @param loops {@inheritDoc} + */ + @Override + public void loopStart(ParseStage stage, int loops){ + if(stage == PmdEngHandler.ENGBONE_LIST){ + this.boneIt = this.boneList.iterator(); + if(this.boneIt.hasNext()){ + this.currentBone = this.boneIt.next(); + } + }else if(stage == PmdEngHandler.ENGMORPH_LIST){ + this.morphPartIt = this.morphPartList.iterator(); + + // 「base」モーフを読み飛ばす + assert this.morphPartIt.hasNext(); + MorphPart part = this.morphPartIt.next(); + assert part != null; + + if(this.morphPartIt.hasNext()){ + this.currentMorphPart = this.morphPartIt.next(); + } + }else if(stage == PmdEngHandler.ENGBONEGROUP_LIST){ + this.boneGroupIt = this.boneGroupList.iterator(); + + // デフォルトボーングループを読み飛ばす + assert this.boneGroupIt.hasNext(); + BoneGroup group = this.boneGroupIt.next(); + assert group != null; + + if(this.boneGroupIt.hasNext()){ + this.currentBoneGroup = this.boneGroupIt.next(); + } + }else{ + assert false; + throw new AssertionError(); + } + return; + } + + /** + * {@inheritDoc} + * @param stage {@inheritDoc} + */ + @Override + public void loopNext(ParseStage stage){ + if(stage == PmdEngHandler.ENGBONE_LIST){ + if(this.boneIt.hasNext()){ + this.currentBone = this.boneIt.next(); + } + }else if(stage == PmdEngHandler.ENGMORPH_LIST){ + if(this.morphPartIt.hasNext()){ + this.currentMorphPart = this.morphPartIt.next(); + } + }else if(stage == PmdEngHandler.ENGBONEGROUP_LIST){ + if(this.boneGroupIt.hasNext()){ + this.currentBoneGroup = this.boneGroupIt.next(); + } + }else{ + assert false; + throw new AssertionError(); + } + return; + } + + /** + * {@inheritDoc} + * @param stage {@inheritDoc} + */ + @Override + public void loopEnd(ParseStage stage){ + return; + } + + /** + * {@inheritDoc} + * @param header {@inheritDoc} + */ + @Override + public void pmdHeaderInfo(byte[] header){ + return; + } + + /** + * {@inheritDoc} + * @param modelName {@inheritDoc} + * @param description {@inheritDoc} + */ + @Override + public void pmdModelInfo(String modelName, String description){ + this.modelName .setPrimaryText(modelName); + this.description.setPrimaryText(description); + return; + } + + /** + * {@inheritDoc} + * @param hasEnglishInfo {@inheritDoc} + */ + @Override + public void pmdEngEnabled(boolean hasEnglishInfo){ + return; + } + + /** + * {@inheritDoc} + * @param modelName {@inheritDoc} + * @param description {@inheritDoc} + */ + @Override + public void pmdEngModelInfo(String modelName, String description){ + this.modelName .setGlobalText(modelName); + this.description.setGlobalText(description); + return; + } + + /** + * {@inheritDoc} + * @param boneName {@inheritDoc} + */ + @Override + public void pmdEngBoneInfo(String boneName){ + this.currentBone.getBoneName().setGlobalText(boneName); + return; + } + + /** + * {@inheritDoc} + * @param morphName {@inheritDoc} + */ + @Override + public void pmdEngMorphInfo(String morphName){ + this.currentMorphPart.getMorphName().setGlobalText(morphName); + return; + } + + /** + * {@inheritDoc} + * @param groupName {@inheritDoc} + */ + @Override + public void pmdEngBoneGroupInfo(String groupName){ + this.currentBoneGroup.getGroupName().setGlobalText(groupName); + return; + } + + /** + * 読み残したデータがあるか判定する。 + * @return 読み残したデータがあればtrue + */ + public boolean hasMoreData(){ + return this.hasMoreData; + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/ToonBuilder.java b/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/ToonBuilder.java new file mode 100644 index 0000000..a143d80 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/ToonBuilder.java @@ -0,0 +1,83 @@ +/* + * building toon information + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.model.binio; + +import jp.sourceforge.mikutoga.parser.ParseStage; +import jp.sourceforge.mikutoga.pmd.model.PmdModel; +import jp.sourceforge.mikutoga.pmd.model.ToonMap; +import jp.sourceforge.mikutoga.pmd.parser.PmdLimits; +import jp.sourceforge.mikutoga.pmd.parser.PmdToonHandler; + +/** + * トゥーン関係の通知をパーサから受け取る。 + */ +class ToonBuilder implements PmdToonHandler { + + private final PmdModel model; + + private ToonMap toonMap; + private int index; + + /** + * コンストラクタ。 + * @param model モデル + */ + ToonBuilder(PmdModel model){ + this.model = model; + return; + } + + /** + * {@inheritDoc} + * @param stage {@inheritDoc} + * @param loops {@inheritDoc} + */ + @Override + public void loopStart(ParseStage stage, int loops){ + assert stage == PmdToonHandler.TOON_LIST; + assert loops == PmdLimits.TOON_FIXEDNUM; + + this.toonMap = new ToonMap(); + this.index = 0; + + return; + } + + /** + * {@inheritDoc} + * @param stage {@inheritDoc} + */ + @Override + public void loopNext(ParseStage stage){ + assert stage == PmdToonHandler.TOON_LIST; + this.index++; + return; + } + + /** + * {@inheritDoc} + * @param stage {@inheritDoc} + */ + @Override + public void loopEnd(ParseStage stage){ + assert stage == PmdToonHandler.TOON_LIST; + this.model.setToonMap(this.toonMap); + return; + } + + /** + * {@inheritDoc} + * @param toonFileName {@inheritDoc} + */ + @Override + public void pmdToonFileInfo(String toonFileName){ + this.toonMap.setIndexedToon(this.index, toonFileName); + return; + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/package-info.java b/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/package-info.java new file mode 100644 index 0000000..09c41bc --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/model/binio/package-info.java @@ -0,0 +1,14 @@ +/* + * package information for Javadoc + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +/** + * PMDモデルファイル(*.pmd)用入出力。 + */ + +package jp.sourceforge.mikutoga.pmd.model.binio; + +/* EOF */ diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/model/xml/PmdXmlExporter.java b/src/main/java/jp/sourceforge/mikutoga/pmd/model/xml/PmdXmlExporter.java new file mode 100644 index 0000000..705a67c --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/model/xml/PmdXmlExporter.java @@ -0,0 +1,1294 @@ +/* + * pmd-xml exporter + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.model.xml; + +import java.awt.Color; +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; +import java.util.Map; +import jp.sourceforge.mikutoga.corelib.I18nText; +import jp.sourceforge.mikutoga.math.MkPos2D; +import jp.sourceforge.mikutoga.math.MkPos3D; +import jp.sourceforge.mikutoga.math.MkVec3D; +import jp.sourceforge.mikutoga.pmd.BoneType; +import jp.sourceforge.mikutoga.pmd.Deg3d; +import jp.sourceforge.mikutoga.pmd.MorphType; +import jp.sourceforge.mikutoga.pmd.Rad3d; +import jp.sourceforge.mikutoga.pmd.RigidShapeType; +import jp.sourceforge.mikutoga.pmd.TripletRange; +import jp.sourceforge.mikutoga.pmd.model.BoneGroup; +import jp.sourceforge.mikutoga.pmd.model.BoneInfo; +import jp.sourceforge.mikutoga.pmd.model.DynamicsInfo; +import jp.sourceforge.mikutoga.pmd.model.IKChain; +import jp.sourceforge.mikutoga.pmd.model.JointInfo; +import jp.sourceforge.mikutoga.pmd.model.Material; +import jp.sourceforge.mikutoga.pmd.model.MorphPart; +import jp.sourceforge.mikutoga.pmd.model.MorphVertex; +import jp.sourceforge.mikutoga.pmd.model.PmdModel; +import jp.sourceforge.mikutoga.pmd.model.RigidGroup; +import jp.sourceforge.mikutoga.pmd.model.RigidInfo; +import jp.sourceforge.mikutoga.pmd.model.RigidShape; +import jp.sourceforge.mikutoga.pmd.model.SerialNumbered; +import jp.sourceforge.mikutoga.pmd.model.ShadeInfo; +import jp.sourceforge.mikutoga.pmd.model.Surface; +import jp.sourceforge.mikutoga.pmd.model.ToonMap; +import jp.sourceforge.mikutoga.pmd.model.Vertex; +import jp.sourceforge.mikutoga.xml.BasicXmlExporter; +import jp.sourceforge.mikutoga.xml.XmlResourceResolver; + +/** + * XML形式でPMDモデルデータを出力する。 + */ +public class PmdXmlExporter extends BasicXmlExporter{ + + private static final String TOP_COMMENT = + " MikuMikuDance\n model-data(*.pmd) on XML"; + private static final String SCHEMA_LOCATION = + PmdXmlResources.NS_PMDXML + " " + PmdXmlResources.SCHEMA_PMDXML; + + /** 改行文字列 CR。 */ + private static final String CR = "\r"; // 0x0d + /** 改行文字列 LF。 */ + private static final String LF = "\n"; // 0x0a + /** 改行文字列 CRLF。 */ + private static final String CRLF = CR + LF; // 0x0d, 0x0a + + private static final String PFX_SURFACEGROUP = "sg"; + private static final String PFX_TOONFILE = "tf"; + private static final String PFX_VERTEX = "vtx"; + private static final String PFX_BONE = "bn"; + private static final String PFX_RIGID = "rd"; + private static final String PFX_RIGIDGROUP = "rg"; + + private static final String BONETYPE_COMMENT = + "Bone types:\n" + + "[0 : ROTATE : Rotate : 回転 :]\n" + + "[1 : ROTMOV : Rotate/Move : 回転/移動 :]\n" + + "[2 : IK : IK : IK :]\n" + + "[3 : UNKNOWN : Unknown : 不明 :]\n" + + "[4 : UNDERIK : Under IK : IK影響下(回転) :]\n" + + "[5 : UNDERROT : Under rotate : 回転影響下 :]\n" + + "[6 : IKCONNECTED : IK connected : IK接続先 :]\n" + + "[7 : HIDDEN : Hidden : 非表示 :]\n" + + "[8 : TWIST : Twist : 捩り :]\n" + + "[9 : LINKEDROT : Linked Rotate: 回転連動 :]\n"; + + private static final String MORPHTYPE_COMMENT = + "Morph types:\n" + + "[1 : EYEBROW : まゆ ]\n" + + "[2 : EYE : 目 ]\n" + + "[3 : LIP : リップ ]\n" + + "[4 : EXTRA : その他 ]\n"; + + private static final String RIGIDBEHAVIOR_COMMENT = + "Rigid behavior types:\n" + + "[0 : FOLLOWBONE : ボーン追従 ]\n" + + "[1 : ONLYDYNAMICS : 物理演算 ]\n" + + "[2 : BONEDDYNAMICS : ボーン位置合わせ ]\n"; + + private String generator = ""; + + /** + * コンストラクタ。 + * 文字エンコーディングはUTF-8が用いられる。 + * @param stream 出力ストリーム + */ + public PmdXmlExporter(OutputStream stream){ + super(stream); + return; + } + + /** + * Generatorメタ情報を設定する。 + * @param generatorArg Generatorメタ情報 + * @throws NullPointerException 引数がnull + */ + public void setGenerator(String generatorArg) + throws NullPointerException{ + if(generatorArg == null) throw new NullPointerException(); + this.generator = generatorArg; + return; + } + + /** + * 任意の文字列がBasicLatin文字のみから構成されるか判定する。 + * @param seq 文字列 + * @return null、長さ0もしくはBasicLatin文字のみから構成されるならtrue + */ + public static boolean hasOnlyBasicLatin(CharSequence seq){ + if(seq == null) return true; + int length = seq.length(); + for(int pos = 0; pos < length; pos++){ + char ch = seq.charAt(pos); + if(ch > 0x007f) return false; + } + return true; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + * @throws IOException {@inheritDoc} + */ + @Override + public PmdXmlExporter ind() throws IOException{ + super.ind(); + return this; + } + + /** + * 文字参照によるエスケープを補佐するためのコメントを出力する。 + * @param seq 文字列 + * @return this本体 + * @throws IOException 出力エラー + */ + protected PmdXmlExporter putUnescapedComment(CharSequence seq) + throws IOException{ + if( ! isBasicLatinOnlyOut() ) return this; + if(hasOnlyBasicLatin(seq)) return this; + sp().putLineComment(seq); + return this; + } + + /** + * 多言語化された各種識別名を出力する。 + * プライマリ名は出力対象外。 + * @param text 多言語文字列 + * @return this本体 + * @throws IOException 出力エラー + */ + protected PmdXmlExporter putI18nName(I18nText text) throws IOException{ + for(String lang639 : text.lang639CodeList()){ + if(lang639.equals(I18nText.CODE639_PRIMARY)) continue; + String name = text.getI18nText(lang639); + ind().put(""); + putUnescapedComment(name); + ln(); + } + return this; + } + + /** + * 番号付けされたID(IDREF)属性を出力する。 + * @param attrName 属性名 + * @param prefix IDプレフィクス + * @param num 番号 + * @return this本体 + * @throws IOException 出力エラー + */ + protected PmdXmlExporter putNumberedIdAttr(CharSequence attrName, + CharSequence prefix, + int num ) + throws IOException{ + put(attrName).put("=\""); + put(prefix).put(num); + put('"'); + return this; + } + + /** + * 番号付けされたID(IDREF)属性を出力する。 + * @param attrName 属性名 + * @param prefix IDプレフィクス + * @param numbered 番号付けされたオブジェクト + * @return this本体 + * @throws IOException 出力エラー + */ + protected PmdXmlExporter putNumberedIdAttr(CharSequence attrName, + CharSequence prefix, + SerialNumbered numbered ) + throws IOException{ + putNumberedIdAttr(attrName, prefix, numbered.getSerialNumber()); + return this; + } + + /** + * 位置情報を出力する。 + * @param position 位置情報 + * @return this本体 + * @throws IOException 出力エラー + */ + protected PmdXmlExporter putPosition(MkPos3D position) + throws IOException{ + put(""); + return this; + } + + /** + * 姿勢情報(ラジアン)を出力する。 + * @param rotation 姿勢情報 + * @return this本体 + * @throws IOException 出力エラー + */ + protected PmdXmlExporter putRadRotation(Rad3d rotation) + throws IOException{ + put(""); + return this; + } + + /** + * 多言語識別名属性のローカルな名前をコメント出力する。 + * @param name 多言語識別名 + * @return this本体 + * @throws IOException 出力エラー + */ + protected PmdXmlExporter putLocalNameComment(I18nText name) + throws IOException{ + String localName = name.getText(); + ind().putLineComment(localName); + return this; + } + + /** + * 多言語識別名属性のプライマリな名前を出力する。 + * @param attrName 属性名 + * @param name 多言語識別名 + * @return this本体 + * @throws IOException 出力エラー + */ + protected PmdXmlExporter putPrimaryNameAttr(CharSequence attrName, + I18nText name) + throws IOException{ + String primaryName = name.getPrimaryText(); + putAttr(attrName, primaryName); + return this; + } + + /** + * PMDモデルデータをXML形式で出力する。 + * @param model PMDモデルデータ + * @throws IOException 出力エラー + */ + public void putPmdModel(PmdModel model) throws IOException{ + ind().put("").ln(2); + + ind().putBlockComment(TOP_COMMENT).ln(2); + + /* + ind().put("") + .ln(3); + */ + + I18nText modelName = model.getModelName(); + ind().putLocalNameComment(modelName).ln(); + ind().put("").ln(2); + + putModelInfo(model).flush(); + putMetaInfo(model).flush(); + putMaterialList(model).flush(); + putToonMap(model).flush(); + putBoneList(model).flush(); + putBoneGroupList(model).flush(); + putIKChainList(model).flush(); + putMorphList(model).flush(); + putRigidList(model).flush(); + putRigidGroupList(model).flush(); + putJointList(model).flush(); + putSurfaceGroupList(model).flush(); + putVertexList(model).flush(); + + ind().put("").ln(2); + ind().put("").ln(); + + return; + } + + /** + * モデル基本情報を出力する。 + * @param model モデル情報 + * @return this本体 + * @throws IOException 出力エラー + */ + private PmdXmlExporter putModelInfo(PmdModel model) + throws IOException{ + I18nText modelName = model.getModelName(); + putI18nName(modelName); + ln(); + + I18nText description = model.getDescription(); + for(String lang639 : description.lang639CodeList()){ + String descText = description.getI18nText(lang639); + putDescription(lang639, descText); + ln(); + } + + return this; + } + + /** + * モデル詳細テキストを出力する。 + * @param lang639 言語コード + * @param content 詳細内容 + * @return this本体 + * @throws IOException 出力エラー + */ + private PmdXmlExporter putDescription(CharSequence lang639, + CharSequence content) + throws IOException{ + String text = content.toString(); + text = text.replace(CRLF, LF); + text = text.replace(CR, LF); + + ind().put("").ln(); + + putBRedContent(text); + + ln(); + ind().put("").ln(); + + if( ! hasOnlyBasicLatin(text) && isBasicLatinOnlyOut() ){ + putBlockComment(text); + } + + return this; + } + + /** + * break要素を含む要素内容を出力する。 + * 必要に応じてXML定義済み実体文字が割り振られた文字、 + * コントロールコード、および非BasicLatin文字がエスケープされる。 + * \nはbrタグに変換される。 + * @param content 内容 + * @return this本体 + * @throws IOException 出力エラー + */ + protected BasicXmlExporter putBRedContent(CharSequence content) + throws IOException{ + int length = content.length(); + + for(int pos = 0; pos < length; pos++){ + char ch = content.charAt(pos); + if(ch == '\n'){ + put("
").ln(); + }else if(Character.isISOControl(ch)){ + putCharRef2Hex(ch); + }else if( ! isBasicLatin(ch) && isBasicLatinOnlyOut()){ + putCharRef4Hex(ch); + }else{ + switch(ch){ + case '&': put("&"); break; + case '<': put("<"); break; + case '>': put(">"); break; + case '"': put("""); break; + case '\'': put("'"); break; + default: put(ch); break; + } + } + } + + return this; + } + + /** + * 各種メタ情報を出力する。 + * @param model モデルデータ + * @return this本体 + * @throws IOException 出力エラー + */ + private PmdXmlExporter putMetaInfo(PmdModel model) throws IOException{ + ind().put("").ln(); + ind().put("").ln(2); + + ind().put("").ln(); + ind().put("").ln(2); + + ind().put("").ln(); + ind().put("").ln(); + ind().put("").ln(2); + + return this; + } + + /** + * マテリアル素材一覧を出力する。 + * @param model モデルデータ + * @return this本体 + * @throws IOException 出力エラー + */ + private PmdXmlExporter putMaterialList(PmdModel model) + throws IOException{ + ind().put("").ln(2); + pushNest(); + + int ct = 0; + for(Material material : model.getMaterialList()){ + putMaterial(material, ct++); + } + + popNest(); + ind().put("").ln(2); + + return this; + } + + /** + * マテリアル素材情報を出力する。 + * @param material マテリアル素材 + * @param no マテリアル通し番号 + * @return this本体 + * @throws IOException 出力エラー + */ + private PmdXmlExporter putMaterial(Material material, int no) + throws IOException{ + String bool; + if(material.getEdgeAppearance()) bool = "true"; + else bool = "false"; + I18nText name = material.getMaterialName(); + String primary = name.getPrimaryText(); + String local = name.getText(); + + if(local != null && local.length() > 0){ + ind().putLineComment(local).ln(); + } + ind().put(" 0){ + putAttr("name", primary).sp(); + } + + putAttr("showEdge", bool); + sp(); + putNumberedIdAttr("surfaceGroupIdRef", PFX_SURFACEGROUP, no); + put('>').ln(); + pushNest(); + + putI18nName(name); + + float[] rgba = new float[4]; + + Color diffuse = material.getDiffuseColor(); + diffuse.getRGBComponents(rgba); + ind().put("").ln(); + + Color specular = material.getSpecularColor(); + specular.getRGBComponents(rgba); + float shininess = material.getShininess(); + ind().put("").ln(); + + Color ambient = material.getAmbientColor(); + ambient.getRGBComponents(rgba); + ind().put("").ln(); + + ShadeInfo shade = material.getShadeInfo(); + String textureFileName = shade.getTextureFileName(); + String spheremapFileName = shade.getSpheremapFileName(); + + if(shade.isValidToonIndex()){ + ind().put(""); + String toonFileName = shade.getToonFileName(); + if(toonFileName != null && toonFileName.length() > 0){ + sp().putLineComment(toonFileName); + } + ln(); + } + + if(textureFileName != null && textureFileName.length() > 0){ + ind().put("").ln(); + } + + if(spheremapFileName != null && spheremapFileName.length() > 0){ + ind().put("").ln(); + } + + popNest(); + ind().put("").ln(2); + + return this; + } + + /** + * トゥーンファイルマッピング情報を出力する。 + * @param model モデルデータ + * @return this本体 + * @throws IOException 出力エラー + */ + private PmdXmlExporter putToonMap(PmdModel model) + throws IOException{ + ind().put("").ln(); + pushNest(); + + ToonMap map = model.getToonMap(); + for(int index = 0; index <= 9; index++){ + ind().putToon(map, index).ln(); + } + + popNest(); + ind().put("").ln(2); + return this; + } + + /** + * 個別のトゥーンファイル情報を出力する。 + * @param map トゥーンマップ + * @param index インデックス値 + * @return this本体 + * @throws IOException 出力エラー + */ + private PmdXmlExporter putToon(ToonMap map, int index) + throws IOException{ + put(""); + putUnescapedComment(toonFile); + return this; + } + + /** + * サーフェイスグループリストを出力する。 + * @param model モデルデータ + * @return this本体 + * @throws IOException 出力エラー + */ + private PmdXmlExporter putSurfaceGroupList(PmdModel model) + throws IOException{ + ind().put("").ln(2); + pushNest(); + + int ct = 0; + for(Material material : model.getMaterialList()){ + List surfaceList = material.getSurfaceList(); + putSurfaceList(surfaceList, ct++); + } + + popNest(); + ind().put("").ln(2); + + return this; + } + + /** + * 個別のサーフェイスグループを出力する。 + * @param surfaceList サーフェイスのリスト + * @param index グループインデックス + * @return this本体 + * @throws IOException 出力エラー + */ + private PmdXmlExporter putSurfaceList(List surfaceList, + int index) + throws IOException{ + ind().put("").ln(); + pushNest(); + + for(Surface surface : surfaceList){ + putSurface(surface); + } + + popNest(); + ind().put("").ln(2); + + return this; + } + + /** + * 個別のサーフェイスを出力する。 + * @param surface サーフェイス + * @return this本体 + * @throws IOException 出力エラー + */ + private PmdXmlExporter putSurface(Surface surface) + throws IOException{ + ind().put("").ln(); + return this; + } + + /** + * 頂点リストを出力する。 + * @param model モデルデータ + * @return this本体 + * @throws IOException 出力エラー + */ + private PmdXmlExporter putVertexList(PmdModel model) + throws IOException{ + ind().put("").ln(2); + pushNest(); + + for(Vertex vertex : model.getVertexList()){ + putVertex(vertex); + } + + popNest(); + ind().put("").ln(2); + + return this; + } + + /** + * 個別の頂点情報を出力する。 + * @param vertex 頂点 + * @return this本体 + * @throws IOException 出力エラー + */ + private PmdXmlExporter putVertex(Vertex vertex) + throws IOException{ + String bool; + if(vertex.getEdgeAppearance()) bool = "true"; + else bool = "false"; + + ind().put("").ln(); + pushNest(); + + MkPos3D position = vertex.getPosition(); + ind().putPosition(position).ln(); + + MkVec3D normal = vertex.getNormal(); + ind().put("").ln(); + + MkPos2D uvPos = vertex.getUVPosition(); + ind().put("").ln(); + + BoneInfo boneA = vertex.getBoneA(); + BoneInfo boneB = vertex.getBoneB(); + int weight = vertex.getWeightA(); + ind().put("").ln(); + + popNest(); + ind().put("").ln(2); + + return this; + } + + /** + * ボーンリストを出力する。 + * @param model モデルデータ + * @return this本体 + * @throws IOException 出力エラー + */ + private PmdXmlExporter putBoneList(PmdModel model) + throws IOException{ + ind().put("").ln(2); + pushNest(); + + putBlockComment(BONETYPE_COMMENT).ln(); + + for(BoneInfo bone : model.getBoneList()){ + putBone(bone); + } + + popNest(); + ind().put("").ln(2); + + return this; + } + + /** + * 個別のボーン情報を出力する。 + * @param bone ボーン情報 + * @return this本体 + * @throws IOException 出力エラー + */ + private PmdXmlExporter putBone(BoneInfo bone) + throws IOException{ + I18nText i18nName = bone.getBoneName(); + BoneType type = bone.getBoneType(); + + putLocalNameComment(i18nName).putLineComment(type.getGuiName()).ln(); + ind().put("").ln(); + pushNest(); + + putI18nName(i18nName); + + MkPos3D position = bone.getPosition(); + ind().putPosition(position).ln(); + + BoneInfo ikBone = bone.getIKBone(); + if(bone.getBoneType() == BoneType.LINKEDROT){ + ind().put("").ln(); + }else if(ikBone != null){ + ind().put(" "); + String ikBoneName = "Ref:" + ikBone.getBoneName().getText(); + putLineComment(ikBoneName); + ln(); + } + + StringBuilder chainComment = new StringBuilder(); + ind().put(" #"); + } + if(next != null){ + sp(); + putNumberedIdAttr("nextBoneIdRef", PFX_BONE, next); + if(chainComment.length() <= 0) chainComment.append("#"); + chainComment.append(" =>") + .append('[') + .append(next.getBoneName().getPrimaryText()) + .append(']'); + } + put(" />").ln(); + ind().putLineComment(chainComment).ln(); + + popNest(); + ind().put("").ln(2); + + return this; + } + + /** + * ボーングループリストを出力する。 + * @param model モデルデータ + * @return this本体 + * @throws IOException 出力エラー + */ + private PmdXmlExporter putBoneGroupList(PmdModel model) + throws IOException{ + ind().put("").ln(2); + pushNest(); + + for(BoneGroup group : model.getBoneGroupList()){ + if(group.isDefaultBoneGroup()) continue; + putBoneGroup(group); + } + + popNest(); + ind().put("").ln(2); + + return this; + } + + /** + * 個別のボーングループ情報を出力する。 + * @param group ボーングループ情報 + * @return this本体 + * @throws IOException 出力エラー + */ + private PmdXmlExporter putBoneGroup(BoneGroup group) + throws IOException{ + I18nText i18nName = group.getGroupName(); + + putLocalNameComment(i18nName).ln(); + ind().put("").ln(); + pushNest(); + + putI18nName(i18nName); + + for(BoneInfo bone : group){ + ind().put(" "); + String boneName = "Ref:" + bone.getBoneName().getText(); + putLineComment(boneName).ln(); + } + + popNest(); + ind().put("").ln(2); + + return this; + } + + /** + * IKチェーンリストを出力する。 + * @param model モデルデータ + * @return this本体 + * @throws IOException 出力エラー + */ + private PmdXmlExporter putIKChainList(PmdModel model) + throws IOException{ + ind().put("").ln(2); + pushNest(); + + for(IKChain chain : model.getIKChainList()){ + putIKChain(chain); + } + + popNest(); + ind().put("").ln(2); + + return this; + } + + /** + * 個別のIKチェーン情報を出力する。 + * @param chain チェーン情報 + * @return this本体 + * @throws IOException 出力エラー + */ + private PmdXmlExporter putIKChain(IKChain chain) + throws IOException{ + int depth = chain.getIKDepth(); + float weight = chain.getIKWeight(); + BoneInfo ikBone = chain.getIkBone(); + + ind().putLineComment("Ref:" + ikBone.getBoneName().getText()).ln(); + ind().put(" ").ln(); + pushNest(); + + for(BoneInfo bone : chain){ + ind().put(" "); + putLineComment("Ref:" + bone.getBoneName().getText()); + ln(); + } + + popNest(); + ind().put("").ln(2); + + return this; + } + + /** + * モーフリストを出力する。 + * @param model モデルデータ + * @return this本体 + * @throws IOException 出力エラー + */ + private PmdXmlExporter putMorphList(PmdModel model) + throws IOException{ + ind().put("").ln(2); + pushNest(); + + putBlockComment(MORPHTYPE_COMMENT).ln(); + + Map> morphMap = model.getMorphMap(); + for(MorphType type : MorphType.values()){ + if(type == MorphType.BASE) continue; + List partList = morphMap.get(type); + if(partList == null) continue; + for(MorphPart part : partList){ + putMorphPart(part); + } + } + + popNest(); + ind().put("").ln(2); + + return this; + } + + /** + * 個別のモーフ情報を出力する。 + * @param part モーフ情報 + * @return this本体 + * @throws IOException 出力エラー + */ + private PmdXmlExporter putMorphPart(MorphPart part) + throws IOException{ + I18nText i18nName = part.getMorphName(); + String primary = i18nName.getPrimaryText(); + + ind().put(""); + putUnescapedComment(primary); + ln(); + pushNest(); + + putI18nName(i18nName); + + for(MorphVertex mvertex : part){ + MkPos3D offset = mvertex.getOffset(); + Vertex base = mvertex.getBaseVertex(); + + ind().put(""); + ln(); + } + + popNest(); + ind().put("").ln(2); + + return this; + } + + /** + * 剛体リストを出力する。 + * @param model モデルデータ + * @return this本体 + * @throws IOException 出力エラー + */ + private PmdXmlExporter putRigidList(PmdModel model) + throws IOException{ + ind().put("").ln(2); + pushNest(); + + putBlockComment(RIGIDBEHAVIOR_COMMENT).ln(); + + for(RigidInfo rigid : model.getRigidList()){ + putRigid(rigid); + } + + popNest(); + ind().put("").ln(2); + + return this; + } + + /** + * 個別の剛体情報を出力する。 + * @param rigid 剛体情報 + * @return this本体 + * @throws IOException 出力エラー + */ + private PmdXmlExporter putRigid(RigidInfo rigid) + throws IOException{ + BoneInfo linkedBone = rigid.getLinkedBone(); + I18nText i18nName = rigid.getRigidName(); + String primary = i18nName.getPrimaryText(); + + putLocalNameComment(i18nName).ln(); + ind().put("").ln(); + pushNest(); + + putI18nName(i18nName); + + if(linkedBone != null){ + ind().put(" "); + putLineComment("Ref:" + linkedBone.getBoneName().getText()); + ln(2); + } + + RigidShape shape = rigid.getRigidShape(); + putRigidShape(shape); + + MkPos3D position = rigid.getPosition(); + ind().putPosition(position).ln(); + + Rad3d rotation = rigid.getRotation(); + ind().putRadRotation(rotation).ln(); + + DynamicsInfo dynamics = rigid.getDynamicsInfo(); + putDynamics(dynamics).ln(); + + for(RigidGroup group : rigid.getThroughGroupColl()){ + ind().put("").ln(); + } + + popNest(); + ind().put("").ln(2); + + return this; + } + + /** + * 剛体形状を出力する。 + * @param shape 剛体形状 + * @return this本体 + * @throws IOException 出力エラー + */ + private PmdXmlExporter putRigidShape(RigidShape shape) + throws IOException{ + RigidShapeType type = shape.getShapeType(); + + switch(type){ + case BOX: + ind().put("").ln(); + + return this; + } + + /** + * 力学設定を出力する。 + * @param dynamics 力学設定 + * @return this本体 + * @throws IOException 出力エラー + */ + private PmdXmlExporter putDynamics(DynamicsInfo dynamics) + throws IOException{ + ind().put("").ln(); + + return this; + } + + /** + * 剛体グループリストを出力する。 + * @param model モデルデータ + * @return this本体 + * @throws IOException 出力エラー + */ + private PmdXmlExporter putRigidGroupList(PmdModel model) + throws IOException{ + ind().put("").ln(2); + pushNest(); + + for(RigidGroup group : model.getRigidGroupList()){ + ind().put(" rigidList = group.getRigidList(); + if(rigidList.size() <= 0){ + put(" />").ln(2); + continue; + } + put(">").ln(); + pushNest(); + + for(RigidInfo rigid : rigidList){ + ind().put(""); + sp(); + putLineComment("Ref:" + rigid.getRigidName().getText()); + ln(); + } + + popNest(); + ind().put("").ln(2); + } + + popNest(); + ind().put("").ln(2); + + return this; + } + + /** + * ジョイントリストを出力する。 + * @param model モデルデータ + * @return this本体 + * @throws IOException 出力エラー + */ + private PmdXmlExporter putJointList(PmdModel model) + throws IOException{ + ind().put("").ln(2); + pushNest(); + + for(JointInfo joint : model.getJointList()){ + putJoint(joint); + } + + popNest(); + ind().put("").ln(2); + + return this; + } + + /** + * 個別のジョイント情報を出力する。 + * @param joint ジョイント情報 + * @return this本体 + * @throws IOException 出力エラー + */ + private PmdXmlExporter putJoint(JointInfo joint) + throws IOException{ + I18nText i18nName = joint.getJointName(); + + putLocalNameComment(i18nName).ln(); + ind().put("").ln(); + pushNest(); + + putI18nName(i18nName); + + RigidInfo rigidA = joint.getRigidA(); + RigidInfo rigidB = joint.getRigidB(); + ind().put("").ln(); + ind(); + putLineComment("[" + rigidA.getRigidName().getText() + "]" + + " <=> [" + rigidB.getRigidName().getText() + "]"); + ln(2); + + MkPos3D position = joint.getPosition(); + ind().putPosition(position).ln(); + + TripletRange posRange = joint.getPositionRange(); + ind().put("").ln(2); + + Rad3d rotation = joint.getRotation(); + ind().putRadRotation(rotation).ln(); + TripletRange rotRange = joint.getRotationRange(); + ind().put("").ln(2); + + MkPos3D elaPosition = joint.getElasticPosition(); + ind().put("").ln(); + + Deg3d elaRotation = joint.getElasticRotation(); + ind().put("").ln(2); + + popNest(); + ind().put("").ln(2); + + return this; + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/model/xml/PmdXmlResources.java b/src/main/java/jp/sourceforge/mikutoga/pmd/model/xml/PmdXmlResources.java new file mode 100644 index 0000000..6c80df3 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/model/xml/PmdXmlResources.java @@ -0,0 +1,105 @@ +/* + * xml resources for PMD-XML + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.model.xml; + +import java.net.URI; +import java.net.URISyntaxException; +import javax.xml.XMLConstants; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.validation.Schema; +import javax.xml.validation.SchemaFactory; +import jp.sourceforge.mikutoga.xml.XmlResourceResolver; +import org.xml.sax.ErrorHandler; +import org.xml.sax.SAXException; + +/** + * XML各種リソースの定義。 + */ +public final class PmdXmlResources { + + public static final String NS_PMDXML = + "http://mikutoga.sourceforge.jp/xml/ns/pmdxml/101009"; + public static final String SCHEMA_PMDXML = + "http://mikutoga.sourceforge.jp/xml/xsd/pmdxml-101009.xsd"; + public static final String DTD_PMDXML = + "http://mikutoga.sourceforge.jp/xml/dtd/pmdxml-101009.dtd"; + public static final String VER_PMDXML = + "101009"; + public static final String LOCAL_SCHEMA_PMDXML = + "resources/pmdxml-101009.xsd"; + public static final String LOCAL_DTD_PMDXML = + "resources/pmdxml-101009.dtd"; + + public static final URI URI_SCHEMA_PMDXML = URI.create(SCHEMA_PMDXML); + public static final URI URI_DTD_PMDXML = URI.create(DTD_PMDXML); + public static final URI RES_SCHEMA_PMDXML; + public static final URI RES_DTD_PMDXML; + + private static final Class THISCLASS = PmdXmlResources.class; + + static{ + Object dummy = new PmdXmlResources(); + + try{ + RES_SCHEMA_PMDXML = + THISCLASS.getResource(LOCAL_SCHEMA_PMDXML).toURI(); + RES_DTD_PMDXML = + THISCLASS.getResource(LOCAL_DTD_PMDXML).toURI(); + }catch(URISyntaxException e){ + throw new ExceptionInInitializerError(e); + } + } + + /** + * 隠しコンストラクタ。 + */ + private PmdXmlResources(){ + super(); + assert this.getClass().equals(THISCLASS); + return; + } + + /** + * ビルダの生成。 + * @param handler エラーハンドラ + * @return ビルダ + * @throws SAXException パースエラー + * @throws ParserConfigurationException 構成エラー + */ + public static DocumentBuilder newBuilder(ErrorHandler handler) + throws SAXException, ParserConfigurationException { + XmlResourceResolver resolver = new XmlResourceResolver(); + resolver.putURIMap(URI_SCHEMA_PMDXML, RES_SCHEMA_PMDXML); + resolver.putURIMap(URI_DTD_PMDXML, RES_DTD_PMDXML); + + SchemaFactory schemaFactory = + SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); + schemaFactory.setResourceResolver(resolver); + schemaFactory.setErrorHandler(handler); + Schema schema = schemaFactory.newSchema(); + + DocumentBuilderFactory builderFactory = + DocumentBuilderFactory.newInstance(); + builderFactory.setCoalescing(true); + builderFactory.setExpandEntityReferences(true); + builderFactory.setIgnoringComments(true); + builderFactory.setIgnoringElementContentWhitespace(false); + builderFactory.setNamespaceAware(true); + builderFactory.setValidating(false); + builderFactory.setSchema(schema); + + DocumentBuilder builder = builderFactory.newDocumentBuilder(); + builder.setEntityResolver(resolver); + builder.setErrorHandler(handler); + + return builder; + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/model/xml/Xml2PmdLoader.java b/src/main/java/jp/sourceforge/mikutoga/pmd/model/xml/Xml2PmdLoader.java new file mode 100644 index 0000000..d5235b0 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/model/xml/Xml2PmdLoader.java @@ -0,0 +1,977 @@ +/* + * xml loader + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.model.xml; + +import java.awt.Color; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import javax.xml.parsers.DocumentBuilder; +import jp.sourceforge.mikutoga.corelib.I18nText; +import jp.sourceforge.mikutoga.pmd.model.ListUtil; +import jp.sourceforge.mikutoga.math.MkPos2D; +import jp.sourceforge.mikutoga.math.MkPos3D; +import jp.sourceforge.mikutoga.math.MkVec3D; +import jp.sourceforge.mikutoga.pmd.BoneType; +import jp.sourceforge.mikutoga.pmd.Deg3d; +import jp.sourceforge.mikutoga.pmd.MorphType; +import jp.sourceforge.mikutoga.pmd.Rad3d; +import jp.sourceforge.mikutoga.pmd.RigidBehaviorType; +import jp.sourceforge.mikutoga.pmd.RigidShapeType; +import jp.sourceforge.mikutoga.pmd.TripletRange; +import jp.sourceforge.mikutoga.pmd.model.BoneGroup; +import jp.sourceforge.mikutoga.pmd.model.BoneInfo; +import jp.sourceforge.mikutoga.pmd.model.DynamicsInfo; +import jp.sourceforge.mikutoga.pmd.model.IKChain; +import jp.sourceforge.mikutoga.pmd.model.JointInfo; +import jp.sourceforge.mikutoga.pmd.model.Material; +import jp.sourceforge.mikutoga.pmd.model.MorphPart; +import jp.sourceforge.mikutoga.pmd.model.MorphVertex; +import jp.sourceforge.mikutoga.pmd.model.PmdModel; +import jp.sourceforge.mikutoga.pmd.model.RigidGroup; +import jp.sourceforge.mikutoga.pmd.model.RigidInfo; +import jp.sourceforge.mikutoga.pmd.model.RigidShape; +import jp.sourceforge.mikutoga.pmd.model.ShadeInfo; +import jp.sourceforge.mikutoga.pmd.model.Surface; +import jp.sourceforge.mikutoga.pmd.model.ToonMap; +import jp.sourceforge.mikutoga.pmd.model.Vertex; +import jp.sourceforge.mikutoga.xml.DomUtils; +import jp.sourceforge.mikutoga.xml.TogaXmlException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +/** + * XML形式でのモデルファイルを読み込む。 + */ +public class Xml2PmdLoader { + + private final DocumentBuilder builder; + + private PmdModel model; + + private final Map toonIdxMap = + new HashMap(); + private final Map boneMap = + new HashMap(); + private final Map vertexMap = + new HashMap(); + private final Map> surfaceGroupMap = + new HashMap>(); + private final Map rigidMap = + new HashMap(); + private final Map rigidGroupMap = + new HashMap(); + + + /** + * コンストラクタ。 + * @param builder ビルダ + */ + public Xml2PmdLoader(DocumentBuilder builder){ + super(); + this.builder = builder; + return; + } + + /** + * 要素からxsd:string型属性値を読み取る。 + * @param elem 要素 + * @param attrName 属性名 + * @return 文字列 + * @throws TogaXmlException 属性値が見つからなかった。 + */ + private static String getStringAttr(Element elem, String attrName) + throws TogaXmlException{ + return DomUtils.getStringAttr(elem, attrName); + } + + /** + * 要素からxsd:boolean型属性値を読み取る。 + * @param elem 要素 + * @param attrName 属性名 + * @return 真ならtrue + * @throws TogaXmlException 属性値が見つからなかった。 + */ + private static boolean getBooleanAttr(Element elem, String attrName) + throws TogaXmlException{ + return DomUtils.getBooleanAttr(elem, attrName); + } + + /** + * 要素からxsd:integer型属性値を読み取る。 + * @param elem 要素 + * @param attrName 属性名 + * @return int値 + * @throws TogaXmlException 属性値が見つからなかった。 + */ + private static int getIntegerAttr(Element elem, String attrName) + throws TogaXmlException{ + return DomUtils.getIntegerAttr(elem, attrName); + } + + /** + * 要素からxsd:float型属性値を読み取る。 + * @param elem 要素 + * @param attrName 属性名 + * @return float値 + * @throws TogaXmlException 属性値が見つからなかった。 + */ + private static float getFloatAttr(Element elem, String attrName) + throws TogaXmlException{ + return DomUtils.getFloatAttr(elem, attrName); + } + + /** + * 要素から日本語Windows用ファイル名を属性値として読み取る。 + * 念のため文字U+00A5は文字U-005Cに変換される。 + * @param elem 要素 + * @param attrName 属性名 + * @return ファイル名 + * @throws TogaXmlException 属性値が見つからなかった。 + */ + private static String getSjisFileNameAttr(Element elem, String attrName) + throws TogaXmlException{ + return DomUtils.getSjisFileNameAttr(elem, attrName); + } + + /** + * 指定された名前の子要素を1つだけ返す。 + * @param parent 親要素 + * @param tagName 子要素名 + * @return 子要素 + * @throws TogaXmlException 1つも見つからなかった + */ + private static Element getChild(Element parent, String tagName) + throws TogaXmlException{ + return DomUtils.getChild(parent, tagName); + } + + /** + * 親要素が指定された名前の子要素を持つか判定する。 + * @param parent 親要素 + * @param tagName 子要素名 + * @return 指定名の子要素が存在すればtrue + */ + private static boolean hasChild(Element parent, String tagName){ + return DomUtils.hasChild(parent, tagName); + } + + /** + * 指定された名前の子要素のforeachを返す。 + * @param parent 親要素 + * @param childTag 子要素名 + * @return 子要素のforeach + */ + private static Iterable eachChild(Element parent, + String childTag){ + return DomUtils.getEachChild(parent, childTag); + } + + /** + * グローバル名を取得する。 + * 元要素のname属性及びi18nNameタグを持つ子要素が検索対象 + * @param parent 元要素 + * @return グローバル名。なければnull + */ + private static String getGlobalName(Element parent){ + NodeList nodeList = parent.getElementsByTagName("i18nName"); + int length = nodeList.getLength(); + for(int idx = 0; idx < length; idx++){ + Node i18nNameNode = nodeList.item(idx); + Element i18nNameElem = (Element)i18nNameNode; + String lang = i18nNameElem.getAttribute("lang"); + if(lang == null || lang.length() <= 0) continue; + if(lang.equals("en")){ + String name = i18nNameElem.getAttribute("name"); + return name; + } + } + return null; + } + + /** + * brタグで区切られた文字列内容(Mixed content)を改行付き文字列に変換する。 + * brタグはその出現回数だけ\nに変換される。 + * 生文字列コンテンツ中の\n,\rは削除される。 + * 改行文字以外のホワイトスペースは保持される。 + * @param parent br要素及び文字列コンテンツを含む要素 + * @return 変換された文字列 + */ + private static String getBRedContent(Element parent){ + StringBuilder result = new StringBuilder(); + + for(Node node = parent.getFirstChild(); + node != null; + node = node.getNextSibling() ){ + + switch(node.getNodeType()){ + case Node.ELEMENT_NODE: + Element elem = (Element) node; + if("br".equals(elem.getTagName())){ + result.append('\n'); + } + break; + case Node.TEXT_NODE: + case Node.CDATA_SECTION_NODE: + String content = node.getTextContent(); + content = content.replace("\r", ""); + content = content.replace("\n", ""); + result.append(content); + break; + default: + break; + } + } + + return result.toString(); + } + + /** + * 多言語名を取得する。 + * @param baseElement 元要素 + * @param text 多言語名 + * @throws TogaXmlException あるべき属性が存在しない。 + */ + private static void buildI18nName(Element baseElement, I18nText text) + throws TogaXmlException{ + String primaryText; + primaryText = getStringAttr(baseElement, "name"); + text.setPrimaryText(primaryText); + + for(Element i18nNameElem : eachChild(baseElement, "i18nName")){ + String lang = getStringAttr(i18nNameElem, "lang"); + String name = getStringAttr(i18nNameElem, "name"); + if("en".equals(lang)){ + text.setGlobalText(name); + }else{ + text.setI18nText(lang, text); + } + } + + return; + } + + /** + * XMLのパースを開始する。 + * @param source XML入力 + * @return モデルデータ + * @throws SAXException 構文エラー + * @throws IOException 入力エラー + * @throws TogaXmlException 構文エラー + */ + public PmdModel parse(InputSource source) + throws SAXException, IOException, TogaXmlException{ + Document document = this.builder.parse(source); + + this.model = new PmdModel(); + + Element pmdModelElem = document.getDocumentElement(); + + buildBasicInfo(pmdModelElem); + + buildBoneList(pmdModelElem); + buildVertexList(pmdModelElem); + buildSurfaceList(pmdModelElem); + + buildToonMap(pmdModelElem); + buildMaterialList(pmdModelElem); + buildIkChainList(pmdModelElem); + buildMorphList(pmdModelElem); + buildBoneGroupList(pmdModelElem); + + buildRigidList(pmdModelElem); + buildRigidGroupList(pmdModelElem); + resolveThroughRigidGroup(pmdModelElem); + + buildJointList(pmdModelElem); + + return this.model; + } + + private void buildBasicInfo(Element pmdModelElem) + throws TogaXmlException{ + String primaryName = getStringAttr(pmdModelElem, "name"); + String globalName = getGlobalName(pmdModelElem); + + I18nText modelName = this.model.getModelName(); + modelName.setPrimaryText(primaryName); + modelName.setGlobalText(globalName); + + String primaryDescription = null; + String globalDescription = null; + for(Element descriptionElem : + eachChild(pmdModelElem, "description")){ + String descriptionText = getBRedContent(descriptionElem); + if( ! descriptionElem.hasAttribute("lang") ){ + primaryDescription = descriptionText; + }else{ + String lang = getStringAttr(descriptionElem, "lang"); + if(lang.equals("ja")){ + primaryDescription = descriptionText; + }else if(lang.equals("en")){ + globalDescription = descriptionText; + } + } + } + + I18nText description = this.model.getDescription(); + description.setPrimaryText(primaryDescription); + description.setGlobalText(globalDescription); + + return; + } + + private void buildToonMap(Element pmdModelElem) + throws TogaXmlException{ + ToonMap toonMap = this.model.getToonMap(); + + Element toonMapElem = getChild(pmdModelElem, "toonMap"); + + for(Element toonDefElem : eachChild(toonMapElem, "toonDef")){ + String toonFileId = getStringAttr(toonDefElem, "toonFileId"); + int toonIndex = getIntegerAttr(toonDefElem, "index"); + String toonFile = getSjisFileNameAttr(toonDefElem, "winFileName"); + + toonMap.setIndexedToon(toonIndex, toonFile); + this.toonIdxMap.put(toonFileId, toonIndex); + } + + return; + } + + private void buildBoneList(Element pmdModelElem) + throws TogaXmlException{ + Element boneListElem = getChild(pmdModelElem, "boneList"); + + List boneList = this.model.getBoneList(); + + for(Element boneElem : eachChild(boneListElem, "bone")){ + BoneInfo boneInfo = new BoneInfo(); + boneList.add(boneInfo); + + I18nText boneName = boneInfo.getBoneName(); + buildI18nName(boneElem, boneName); + + String boneType = getStringAttr(boneElem, "type"); + BoneType type = BoneType.valueOf(boneType); + boneInfo.setBoneType(type); + + String boneId = getStringAttr(boneElem, "boneId"); + this.boneMap.put(boneId, boneInfo); + + Element positionElem = getChild(boneElem, "position"); + float xPos = getFloatAttr(positionElem, "x"); + float yPos = getFloatAttr(positionElem, "y"); + float zPos = getFloatAttr(positionElem, "z"); + MkPos3D position = boneInfo.getPosition(); + position.setXpos(xPos); + position.setYpos(yPos); + position.setZpos(zPos); + } + + ListUtil.assignIndexedSerial(boneList); + + int serial = 0; + for(Element boneElem : eachChild(boneListElem, "bone")){ + BoneInfo boneInfo = boneList.get(serial++); + + if(hasChild(boneElem, "ikBone")){ + Element ikBoneElem = getChild(boneElem, "ikBone"); + String ikBoneId = getStringAttr(ikBoneElem, "boneIdRef"); + BoneInfo ikBone = this.boneMap.get(ikBoneId); + boneInfo.setIKBone(ikBone); + }else if(hasChild(boneElem, "rotationRatio")){ + Element ikBoneElem = getChild(boneElem, "rotationRatio"); + int ratio = getIntegerAttr(ikBoneElem, "ratio"); + boneInfo.setRotationRatio(ratio); + } + + Element boneChainElem = getChild(boneElem, "boneChain"); + if(boneChainElem.hasAttribute("prevBoneIdRef")){ + String prevId = getStringAttr(boneChainElem, "prevBoneIdRef"); + BoneInfo prevBone = this.boneMap.get(prevId); + boneInfo.setPrevBone(prevBone); + } + if(boneChainElem.hasAttribute("nextBoneIdRef")){ + String nextId = getStringAttr(boneChainElem, "nextBoneIdRef"); + BoneInfo nextBone = this.boneMap.get(nextId); + boneInfo.setNextBone(nextBone); + } + } + + return; + } + + private void buildVertexList(Element pmdModelElem) + throws TogaXmlException{ + Element vertexListElem = getChild(pmdModelElem, "vertexList"); + + List vertexList = this.model.getVertexList(); + + for(Element vertexElem : eachChild(vertexListElem, "vertex")){ + Vertex vertex = new Vertex(); + vertexList.add(vertex); + + String vertexId = getStringAttr(vertexElem, "vtxId"); + this.vertexMap.put(vertexId, vertex); + + boolean showEdge = getBooleanAttr(vertexElem, "showEdge"); + vertex.setEdgeAppearance(showEdge); + + float xVal; + float yVal; + float zVal; + + Element positionElem = getChild(vertexElem, "position"); + xVal = getFloatAttr(positionElem, "x"); + yVal = getFloatAttr(positionElem, "y"); + zVal = getFloatAttr(positionElem, "z"); + MkPos3D position = vertex.getPosition(); + position.setXpos(xVal); + position.setYpos(yVal); + position.setZpos(zVal); + + Element normalElem = getChild(vertexElem, "normal"); + xVal = getFloatAttr(normalElem, "x"); + yVal = getFloatAttr(normalElem, "y"); + zVal = getFloatAttr(normalElem, "z"); + MkVec3D normal = vertex.getNormal(); + normal.setXVal(xVal); + normal.setYVal(yVal); + normal.setZVal(zVal); + + Element uvElem = getChild(vertexElem, "uvMap"); + float uVal = getFloatAttr(uvElem, "u"); + float vVal = getFloatAttr(uvElem, "v"); + MkPos2D uv = vertex.getUVPosition(); + uv.setXpos(uVal); + uv.setYpos(vVal); + + Element skinningElem = getChild(vertexElem, "skinning"); + String boneId1 = getStringAttr(skinningElem, "boneIdRef1"); + String boneId2 = getStringAttr(skinningElem, "boneIdRef2"); + int weight = getIntegerAttr(skinningElem, "weightBalance"); + BoneInfo boneA = this.boneMap.get(boneId1); + BoneInfo boneB = this.boneMap.get(boneId2); + vertex.setBonePair(boneA, boneB); + vertex.setWeightA(weight); + } + + ListUtil.assignIndexedSerial(vertexList); + + return; + } + + private void buildSurfaceList(Element pmdModelElem) + throws TogaXmlException{ + Element surfaceGroupListElem = + getChild(pmdModelElem, "surfaceGroupList"); + + for(Element surfaceGroupElem : + eachChild(surfaceGroupListElem, "surfaceGroup") ){ + + String groupId = + getStringAttr(surfaceGroupElem, "surfaceGroupId"); + List surfaceList = buildSurface(surfaceGroupElem); + + this.surfaceGroupMap.put(groupId, surfaceList); + } + } + + private List buildSurface(Element surfaceGroupElem) + throws TogaXmlException{ + List result = new ArrayList(); + + for(Element surfaceElem : eachChild(surfaceGroupElem, "surface")){ + Surface surface = new Surface(); + result.add(surface); + + String id1 = getStringAttr(surfaceElem, "vtxIdRef1"); + String id2 = getStringAttr(surfaceElem, "vtxIdRef2"); + String id3 = getStringAttr(surfaceElem, "vtxIdRef3"); + + Vertex vertex1 = this.vertexMap.get(id1); + Vertex vertex2 = this.vertexMap.get(id2); + Vertex vertex3 = this.vertexMap.get(id3); + + surface.setTriangle(vertex1, vertex2, vertex3); + } + + return result; + } + + private void buildMaterialList(Element pmdModelElem) + throws TogaXmlException{ + Element materialListElem = + getChild(pmdModelElem, "materialList"); + + List surfaceList = this.model.getSurfaceList(); + List materialList = this.model.getMaterialList(); + + for(Element materialElem : eachChild(materialListElem, "material")){ + Material material = new Material(); + materialList.add(material); + + material.getShadeInfo().setToonMap(this.model.getToonMap()); + + String surfaceGroupId = + getStringAttr(materialElem, "surfaceGroupIdRef"); + List surfaceGroup = + this.surfaceGroupMap.get(surfaceGroupId); + surfaceList.addAll(surfaceGroup); + material.getSurfaceList().addAll(surfaceGroup); + + boolean hasEdge = getBooleanAttr(materialElem, "showEdge"); + material.setEdgeAppearance(hasEdge); + + ShadeInfo shadeInfo = material.getShadeInfo(); + + int toonIdx; + if(hasChild(materialElem, "toon")){ + Element toonElem = getChild(materialElem, "toon"); + String toonId = getStringAttr(toonElem, "toonFileIdRef"); + toonIdx = this.toonIdxMap.get(toonId); + }else{ + toonIdx = 255; + } + shadeInfo.setToonIndex(toonIdx); + + if(hasChild(materialElem, "textureFile")){ + Element textureFileElem = + getChild(materialElem, "textureFile"); + String textureFile = + getSjisFileNameAttr(textureFileElem, "winFileName"); + shadeInfo.setTextureFileName(textureFile); + } + + if(hasChild(materialElem, "spheremapFile")){ + Element spheremapFileElem = + getChild(materialElem, "spheremapFile"); + String spheremapFile = + getSjisFileNameAttr(spheremapFileElem, "winFileName"); + shadeInfo.setSpheremapFileName(spheremapFile); + } + + float red; + float green; + float blue; + + Element diffuseElem = getChild(materialElem, "diffuse"); + red = getFloatAttr(diffuseElem, "r"); + green = getFloatAttr(diffuseElem, "g"); + blue = getFloatAttr(diffuseElem, "b"); + float alpha = getFloatAttr(diffuseElem, "alpha"); + Color diffuse = new Color(red, green, blue, alpha); + material.setDiffuseColor(diffuse); + + Element specularElem = getChild(materialElem, "specular"); + red = getFloatAttr(specularElem, "r"); + green = getFloatAttr(specularElem, "g"); + blue = getFloatAttr(specularElem, "b"); + float shininess = getFloatAttr(specularElem, "shininess"); + Color specular = new Color(red, green, blue); + material.setSpecularColor(specular); + material.setShininess(shininess); + + Element ambientElem = getChild(materialElem, "ambient"); + red = getFloatAttr(ambientElem, "r"); + green = getFloatAttr(ambientElem, "g"); + blue = getFloatAttr(ambientElem, "b"); + Color ambient = new Color(red, green, blue); + material.setAmbientColor(ambient); + } + + return; + } + + private void buildIkChainList(Element pmdModelElem) + throws TogaXmlException{ + Element ikChainListElem = + getChild(pmdModelElem, "ikChainList"); + + List ikChainList = this.model.getIKChainList(); + + for(Element ikChainElem : eachChild(ikChainListElem, "ikChain")){ + IKChain ikChain = new IKChain(); + ikChainList.add(ikChain); + + String ikBoneIdRef = getStringAttr(ikChainElem, "ikBoneIdRef"); + int rucursiveDepth = + getIntegerAttr(ikChainElem, "recursiveDepth"); + float weight = getFloatAttr(ikChainElem, "weight"); + + BoneInfo ikBone = this.boneMap.get(ikBoneIdRef); + ikChain.setIkBone(ikBone); + ikChain.setIKDepth(rucursiveDepth); + ikChain.setIKWeight(weight); + + List chainList = ikChain.getChainedBoneList(); + + for(Element orderElem : eachChild(ikChainElem, "chainOrder")){ + String boneIdRef = getStringAttr(orderElem, "boneIdRef"); + BoneInfo chaindBone = this.boneMap.get(boneIdRef); + chainList.add(chaindBone); + } + } + + return; + } + + private void buildMorphList(Element pmdModelElem) + throws TogaXmlException{ + Element morphListElem = + getChild(pmdModelElem, "morphList"); + + Map> morphMap = this.model.getMorphMap(); + + for(Element morphElem : eachChild(morphListElem, "morph")){ + MorphPart morphPart = new MorphPart(); + + I18nText name = morphPart.getMorphName(); + buildI18nName(morphElem, name); + + String type = getStringAttr(morphElem, "type"); + MorphType morphType = MorphType.valueOf(type); + morphPart.setMorphType(morphType); + + List morphVertexList = + morphPart.getMorphVertexList(); + + for(Element morphVertexElem + : eachChild(morphElem, "morphVertex")){ + String vtxIdRef = getStringAttr(morphVertexElem, "vtxIdRef"); + Vertex baseVertex = this.vertexMap.get(vtxIdRef); + float xOff = getFloatAttr(morphVertexElem, "xOff"); + float yOff = getFloatAttr(morphVertexElem, "yOff"); + float zOff = getFloatAttr(morphVertexElem, "zOff"); + + MorphVertex morphVertex = new MorphVertex(); + morphVertex.setBaseVertex(baseVertex); + MkPos3D position = morphVertex.getOffset(); + position.setXpos(xOff); + position.setYpos(yOff); + position.setZpos(zOff); + + morphVertexList.add(morphVertex); + } + + morphMap.get(morphType).add(morphPart); + } + + List serialList = new LinkedList(); + MorphPart baseDummy = new MorphPart(); + serialList.add(baseDummy); + for(MorphPart part : morphMap.get(MorphType.EYEBROW)){ + serialList.add(part); + } + for(MorphPart part : morphMap.get(MorphType.EYE)){ + serialList.add(part); + } + for(MorphPart part : morphMap.get(MorphType.LIP)){ + serialList.add(part); + } + for(MorphPart part : morphMap.get(MorphType.EXTRA)){ + serialList.add(part); + } + ListUtil.assignIndexedSerial(serialList); + + return; + } + + private void buildBoneGroupList(Element pmdModelElem) + throws TogaXmlException{ + Element boneGroupListElem = + getChild(pmdModelElem, "boneGroupList"); + + List boneGroupList = this.model.getBoneGroupList(); + BoneGroup defaultGroup = new BoneGroup(); + boneGroupList.add(defaultGroup); + + for(Element boneGroupElem + : eachChild(boneGroupListElem, "boneGroup")){ + BoneGroup group = new BoneGroup(); + boneGroupList.add(group); + + I18nText name = group.getGroupName(); + buildI18nName(boneGroupElem, name); + + for(Element boneGroupMemberElem + : eachChild(boneGroupElem, "boneGroupMember")){ + String boneIdRef = + getStringAttr(boneGroupMemberElem, "boneIdRef"); + BoneInfo bone = this.boneMap.get(boneIdRef); + group.getBoneList().add(bone); + } + } + + ListUtil.assignIndexedSerial(boneGroupList); + + return; + } + + private void buildRigidList(Element pmdModelElem) + throws TogaXmlException{ + Element rigidListElem = + getChild(pmdModelElem, "rigidList"); + + List rigidList = this.model.getRigidList(); + + for(Element rigidElem : eachChild(rigidListElem, "rigid")){ + RigidInfo rigid = new RigidInfo(); + rigidList.add(rigid); + + I18nText name = rigid.getRigidName(); + buildI18nName(rigidElem, name); + + String behavior = getStringAttr(rigidElem, "behavior"); + RigidBehaviorType type = RigidBehaviorType.valueOf(behavior); + rigid.setBehaviorType(type); + + String rigidId = getStringAttr(rigidElem, "rigidId"); + this.rigidMap.put(rigidId, rigid); + + if(hasChild(rigidElem, "linkedBone")){ + Element linkedBoneElem = getChild(rigidElem, "linkedBone"); + String boneIdRef = getStringAttr(linkedBoneElem, "boneIdRef"); + BoneInfo linkedBone = this.boneMap.get(boneIdRef); + rigid.setLinkedBone(linkedBone); + } + + RigidShape rigidShape = rigid.getRigidShape(); + if(hasChild(rigidElem, "rigidShapeSphere")){ + Element shapeElem = + getChild(rigidElem, "rigidShapeSphere"); + float radius = getFloatAttr(shapeElem, "radius"); + rigidShape.setShapeType(RigidShapeType.SPHERE); + rigidShape.setRadius(radius); + } + if(hasChild(rigidElem, "rigidShapeBox")){ + Element shapeElem = + getChild(rigidElem, "rigidShapeBox"); + float width = getFloatAttr(shapeElem, "width"); + float height = getFloatAttr(shapeElem, "height"); + float depth = getFloatAttr(shapeElem, "depth"); + rigidShape.setShapeType(RigidShapeType.BOX); + rigidShape.setWidth(width); + rigidShape.setHeight(height); + rigidShape.setDepth(depth); + } + if(hasChild(rigidElem, "rigidShapeCapsule")){ + Element shapeElem = + getChild(rigidElem, "rigidShapeCapsule"); + float height = getFloatAttr(shapeElem, "height"); + float radius = getFloatAttr(shapeElem, "radius"); + rigidShape.setShapeType(RigidShapeType.CAPSULE); + rigidShape.setHeight(height); + rigidShape.setRadius(radius); + } + + float xVal; + float yVal; + float zVal; + + Element positionElem = getChild(rigidElem, "position"); + xVal = getFloatAttr(positionElem, "x"); + yVal = getFloatAttr(positionElem, "y"); + zVal = getFloatAttr(positionElem, "z"); + MkPos3D position = rigid.getPosition(); + position.setXpos(xVal); + position.setYpos(yVal); + position.setZpos(zVal); + + Element radRotationElem = getChild(rigidElem, "radRotation"); + xVal = getFloatAttr(radRotationElem, "xRad"); + yVal = getFloatAttr(radRotationElem, "yRad"); + zVal = getFloatAttr(radRotationElem, "zRad"); + Rad3d rotation = rigid.getRotation(); + rotation.setXRad(xVal); + rotation.setYRad(yVal); + rotation.setZRad(zVal); + + Element dynamicsElem = getChild(rigidElem, "dynamics"); + float mass = getFloatAttr(dynamicsElem, "mass"); + float dampingPosition = + getFloatAttr(dynamicsElem, "dampingPosition"); + float dampingRotation = + getFloatAttr(dynamicsElem, "dampingRotation"); + float restitution = getFloatAttr(dynamicsElem, "restitution"); + float friction = getFloatAttr(dynamicsElem, "friction"); + DynamicsInfo dynamics = rigid.getDynamicsInfo(); + dynamics.setMass(mass); + dynamics.setDampingPosition(dampingPosition); + dynamics.setDampingRotation(dampingRotation); + dynamics.setRestitution(restitution); + dynamics.setFriction(friction); + } + + ListUtil.assignIndexedSerial(rigidList); + + return; + } + + private void buildRigidGroupList(Element pmdModelElem) + throws TogaXmlException{ + Element rigidGroupListElem = + getChild(pmdModelElem, "rigidGroupList"); + + List groupList = this.model.getRigidGroupList(); + + for(Element rigidGroupElem + : eachChild(rigidGroupListElem, "rigidGroup")){ + RigidGroup rigidGroup = new RigidGroup(); + groupList.add(rigidGroup); + + String rigidGroupId = + getStringAttr(rigidGroupElem, "rigidGroupId"); + this.rigidGroupMap.put(rigidGroupId, rigidGroup); + + for(Element memberElem + : eachChild(rigidGroupElem, "rigidGroupMember")){ + String rigidIdRef = getStringAttr(memberElem, "rigidIdRef"); + RigidInfo rigid = this.rigidMap.get(rigidIdRef); + rigidGroup.getRigidList().add(rigid); + rigid.setRigidGroup(rigidGroup); + } + } + + while(groupList.size() < 16){ + RigidGroup rigidGroup = new RigidGroup(); + groupList.add(rigidGroup); + } + + ListUtil.assignIndexedSerial(groupList); + + return; + } + + private void resolveThroughRigidGroup(Element pmdModelElem) + throws TogaXmlException{ + Element rigidListElem = + getChild(pmdModelElem, "rigidList"); + + List rigidList = this.model.getRigidList(); + + int serialNum = 0; + for(Element rigidElem : eachChild(rigidListElem, "rigid")){ + RigidInfo rigid = rigidList.get(serialNum++); + for(Element groupElem + : eachChild(rigidElem, "throughRigidGroup")){ + String groupId = getStringAttr(groupElem, "rigidGroupIdRef"); + RigidGroup group = this.rigidGroupMap.get(groupId); + rigid.getThroughGroupColl().add(group); + } + } + + return; + } + + private void buildJointList(Element pmdModelElem) + throws TogaXmlException{ + Element jointListElem = + getChild(pmdModelElem, "jointList"); + + List jointList = this.model.getJointList(); + + for(Element jointElem : eachChild(jointListElem, "joint")){ + JointInfo joint = new JointInfo(); + jointList.add(joint); + + I18nText name = joint.getJointName(); + buildI18nName(jointElem, name); + + Element rigidPairElem = getChild(jointElem, "jointedRigidPair"); + String rigidIdRef1 = getStringAttr(rigidPairElem, "rigidIdRef1"); + String rigidIdRef2 = getStringAttr(rigidPairElem, "rigidIdRef2"); + RigidInfo rigid1 = this.rigidMap.get(rigidIdRef1); + RigidInfo rigid2 = this.rigidMap.get(rigidIdRef2); + joint.setRigidPair(rigid1, rigid2); + + float xVal; + float yVal; + float zVal; + float xFrom; + float xTo; + float yFrom; + float yTo; + float zFrom; + float zTo; + + MkPos3D position = joint.getPosition(); + Element positionElem = getChild(jointElem, "position"); + xVal = getFloatAttr(positionElem, "x"); + yVal = getFloatAttr(positionElem, "y"); + zVal = getFloatAttr(positionElem, "z"); + position.setXpos(xVal); + position.setYpos(yVal); + position.setZpos(zVal); + + TripletRange limitPosition = joint.getPositionRange(); + Element limitPositionElem = getChild(jointElem, "limitPosition"); + xFrom = getFloatAttr(limitPositionElem, "xFrom"); + xTo = getFloatAttr(limitPositionElem, "xTo"); + yFrom = getFloatAttr(limitPositionElem, "yFrom"); + yTo = getFloatAttr(limitPositionElem, "yTo"); + zFrom = getFloatAttr(limitPositionElem, "zFrom"); + zTo = getFloatAttr(limitPositionElem, "zTo"); + limitPosition.setXRange(xFrom, xTo); + limitPosition.setYRange(yFrom, yTo); + limitPosition.setZRange(zFrom, zTo); + + Rad3d rotation = joint.getRotation(); + Element rotationElem = getChild(jointElem, "radRotation"); + xVal = getFloatAttr(rotationElem, "xRad"); + yVal = getFloatAttr(rotationElem, "yRad"); + zVal = getFloatAttr(rotationElem, "zRad"); + rotation.setXRad(xVal); + rotation.setYRad(yVal); + rotation.setZRad(zVal); + + TripletRange limitRotation = joint.getRotationRange(); + Element limitRotationElem = getChild(jointElem, "limitRotation"); + xFrom = getFloatAttr(limitRotationElem, "xFrom"); + xTo = getFloatAttr(limitRotationElem, "xTo"); + yFrom = getFloatAttr(limitRotationElem, "yFrom"); + yTo = getFloatAttr(limitRotationElem, "yTo"); + zFrom = getFloatAttr(limitRotationElem, "zFrom"); + zTo = getFloatAttr(limitRotationElem, "zTo"); + limitRotation.setXRange(xFrom, xTo); + limitRotation.setYRange(yFrom, yTo); + limitRotation.setZRange(zFrom, zTo); + + MkPos3D elasticPosition = joint.getElasticPosition(); + Element elasticPositionElem = + getChild(jointElem, "elasticPosition"); + xVal = getFloatAttr(elasticPositionElem, "x"); + yVal = getFloatAttr(elasticPositionElem, "y"); + zVal = getFloatAttr(elasticPositionElem, "z"); + elasticPosition.setXpos(xVal); + elasticPosition.setYpos(yVal); + elasticPosition.setZpos(zVal); + + Deg3d elasticRotation = joint.getElasticRotation(); + Element elasticRotationElem = + getChild(jointElem, "elasticRotation"); + xVal = getFloatAttr(elasticRotationElem, "xDeg"); + yVal = getFloatAttr(elasticRotationElem, "yDeg"); + zVal = getFloatAttr(elasticRotationElem, "zDeg"); + elasticRotation.setXDeg(xVal); + elasticRotation.setYDeg(yVal); + elasticRotation.setZDeg(zVal); + } + + return; + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/model/xml/package-info.java b/src/main/java/jp/sourceforge/mikutoga/pmd/model/xml/package-info.java new file mode 100644 index 0000000..4ae0dda --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/model/xml/package-info.java @@ -0,0 +1,14 @@ +/* + * package information for Javadoc + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +/** + * PMDモデル内容をXMLで出力するためのライブラリ。 + */ + +package jp.sourceforge.mikutoga.pmd.model.xml; + +/* EOF */ diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd2xml/Pmd2Xml.java b/src/main/java/jp/sourceforge/mikutoga/pmd2xml/Pmd2Xml.java index 400a8e9..20d1192 100644 --- a/src/main/java/jp/sourceforge/mikutoga/pmd2xml/Pmd2Xml.java +++ b/src/main/java/jp/sourceforge/mikutoga/pmd2xml/Pmd2Xml.java @@ -21,13 +21,13 @@ import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.ParserConfigurationException; import jp.sourceforge.mikutoga.parser.MmdFormatException; import jp.sourceforge.mikutoga.parser.MmdSource; -import jp.sourceforge.mikutoga.pmd.PmdModel; -import jp.sourceforge.mikutoga.pmd.pmdexporter.IllegalPmdException; -import jp.sourceforge.mikutoga.pmd.pmdexporter.PmdExporter; -import jp.sourceforge.mikutoga.pmd.pmdloader.PmdLoader; -import jp.sourceforge.mikutoga.pmd.xml.PmdXmlExporter; -import jp.sourceforge.mikutoga.pmd.xml.PmdXmlResources; -import jp.sourceforge.mikutoga.pmd.xml.Xml2PmdLoader; +import jp.sourceforge.mikutoga.pmd.IllegalPmdDataException; +import jp.sourceforge.mikutoga.pmd.model.PmdModel; +import jp.sourceforge.mikutoga.pmd.model.binio.PmdExporter; +import jp.sourceforge.mikutoga.pmd.model.binio.PmdLoader; +import jp.sourceforge.mikutoga.pmd.model.xml.PmdXmlExporter; +import jp.sourceforge.mikutoga.pmd.model.xml.PmdXmlResources; +import jp.sourceforge.mikutoga.pmd.model.xml.Xml2PmdLoader; import jp.sourceforge.mikutoga.xml.TogaXmlException; import org.xml.sax.InputSource; import org.xml.sax.SAXException; @@ -37,7 +37,7 @@ import org.xml.sax.SAXException; */ public final class Pmd2Xml { - private static final Class THISCLASS; + private static final Class THISCLASS; private static final String APPNAME; private static final String APPVER; private static final String APPLICENSE; @@ -179,7 +179,7 @@ public final class Pmd2Xml { ioError(e); }catch(ParserConfigurationException e){ internalError(e); - }catch(IllegalPmdException e){ + }catch(IllegalPmdDataException e){ internalError(e); }catch(MmdFormatException e){ pmdError(e); @@ -273,10 +273,10 @@ public final class Pmd2Xml { * @param outputFile 出力ファイル名 * @throws IOException 入出力エラー * @throws MmdFormatException 不正なPMDファイル - * @throws IllegalPmdException 不正なモデルデータ + * @throws IllegalPmdDataException 不正なモデルデータ */ private static void pmd2xml(String inputFile, String outputFile) - throws IOException, MmdFormatException, IllegalPmdException{ + throws IOException, MmdFormatException, IllegalPmdDataException{ File iFile = new File(inputFile); InputStream is = new FileInputStream(iFile); is = new BufferedInputStream(is); @@ -302,14 +302,14 @@ public final class Pmd2Xml { * @throws ParserConfigurationException XML構成のエラー * @throws SAXException 不正なXMLファイル * @throws TogaXmlException 不正なXMLファイル - * @throws IllegalPmdException 不正なPMDモデルデータ + * @throws IllegalPmdDataException 不正なPMDモデルデータ */ private static void xml2pmd(String inputFile, String outputFile) throws IOException, ParserConfigurationException, SAXException, TogaXmlException, - IllegalPmdException { + IllegalPmdDataException { File iFile = new File(inputFile); InputStream is = new FileInputStream(iFile); is = new BufferedInputStream(is); @@ -392,10 +392,10 @@ public final class Pmd2Xml { * @param model モデルデータ * @param ostream 出力ストリーム * @throws IOException 出力エラー - * @throws IllegalPmdException 不正なモデルデータ + * @throws IllegalPmdDataException 不正なモデルデータ */ private static void pmdOut(PmdModel model, OutputStream ostream) - throws IOException, IllegalPmdException{ + throws IOException, IllegalPmdDataException{ PmdExporter exporter = new PmdExporter(ostream); exporter.dumpPmdModel(model); ostream.close(); @@ -407,10 +407,10 @@ public final class Pmd2Xml { * @param model モデルデータ * @param ostream 出力ストリーム * @throws IOException 出力エラー - * @throws IllegalPmdException 不正なモデルデータ + * @throws IllegalPmdDataException 不正なモデルデータ */ private static void xmlOut(PmdModel model, OutputStream ostream) - throws IOException, IllegalPmdException{ + throws IOException, IllegalPmdDataException{ PmdXmlExporter exporter = new PmdXmlExporter(ostream); exporter.setNewLine("\r\n"); exporter.setGenerator(APPNAME + ' ' + APPVER); diff --git a/src/main/resources/jp/sourceforge/mikutoga/pmd/model/xml/resources/pmdxml-101009.dtd b/src/main/resources/jp/sourceforge/mikutoga/pmd/model/xml/resources/pmdxml-101009.dtd new file mode 100644 index 0000000..851f3aa --- /dev/null +++ b/src/main/resources/jp/sourceforge/mikutoga/pmd/model/xml/resources/pmdxml-101009.dtd @@ -0,0 +1,532 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/jp/sourceforge/mikutoga/pmd/model/xml/resources/pmdxml-101009.xsd b/src/main/resources/jp/sourceforge/mikutoga/pmd/model/xml/resources/pmdxml-101009.xsd new file mode 100644 index 0000000..424b7bb --- /dev/null +++ b/src/main/resources/jp/sourceforge/mikutoga/pmd/model/xml/resources/pmdxml-101009.xsd @@ -0,0 +1,1511 @@ + + + + + + + + + + MikuMikuDance model-data(*.pmd) on XML. + License : The MIT License + Copyright(c) 2010 MikuToga Partners + + + + + + + + + + + + Root element. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + xsd:language except "ja" (Primary-language in MMD) + + + + + + + + + + + + + + Multilingual name. + + + + + + + + + + + + + + + Break line. + + + + + + + + + Free paragraph with break-line. + Any raw-newline(CR,CRLF) will be ignored later. + But, other white-spaces will be preserved. + + + + + + + + + + + + + + + + + Description note about model. + + + + + + + + + License term of usage. + + + + + + + + + Credits for someone. + + + + + + + + + Meta-information of model. + Use free. + but, some meta-name has recommended usage. + + "generator" (Generator application name) + + "siteURL" (Website URL) + + "imageURL" (Thumbnail image URL) + + + + + + + + + + + + + + + Material list. + All visual things can be tracked from here. + + + + + + + + + + + + + + + + + + + + + Material definition. + Colors, shading, any other definitions. + + + + + + + + + + + + + + + + + + + + + + + + + + sRGB component value. (0.0 - 1.0) + + + + + + + + + + + + + + + Diffuse color definition. + + + + + + + + + + + + + + + + + Specular color definition. + + + + + + + + + + + + + + + + + Ambient color definition. + + + + + + + + + + + + + + + + Reference for Toon-image. + + + + + + + + + + + + + + Texture file information. + + + + + + + + + + + + + + Sphere-map file information. + + + + + + + + + + + + + + Toon-file mappings. + + + + + + + + + + + + + + + + + + + + + + + + + + Toon-file information. + + + + + + + + + + + + + + + + List of bone information. + + + + + + + + + + + + + + + + + + + + + bone types. + + + + + + + + + + + + + + + + + + + + + + + Bone definition. + + + + + + + + + + + + + + + + + + + + + + + + + + Relationship-info between bones. + + + + + + + + + + + + + + + Rotation ratio between Linked-rotationed bones. + + + + + + + + + + + + + + Reference to IK-Bone. + + + + + + + + + + + + + + List of Bone-group. + + + + + + + + + + + + + + + + + Bone-group. + + + + + + + + + + + + + + + + + + Member of Bone-group. + + + + + + + + + + + + + + List of IK chain. + + + + + + + + + + + + + + + + + + + + + IK chained bones definition. + + + + + + + + + + + + + + + + + + + + Part of IK chained bones. + + + + + + + + + + + + + + List of morphing definition. + + + + + + + + + + + + + + + + + Morph types. + + + + + + + + + + + + + + + + + Morphing definition. + + + + + + + + + + + + + + + + + + + Morphing vertex information. + + + + + + + + + + + + + + + + + list of Rigid-body definition. + + + + + + + + + + + + + + + + + + + + + Rigid bahavior types. + + + + + + + + + + + + + + + + Rigid-body definition. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Referenced Bone from Rigid-body. + + + + + + + + + + + + + + Sphere shape of Rigid-body. + + + + + + + + + + + + + + Box shape of Rigid-body. + + + + + + + + + + + + + + + + Capsule shape of Rigid-body. + + + + + + + + + + + + + + + Rotaion information by radian. + + + + + + + + + + + + + + + + Dynamics parameters. + + + + + + + + + + + + + + + + + + Reference for non-collision rigid-group. + + + + + + + + + + + + + + List of Rigid-body group. + + + + + + + + + + + + + + + + + + + + + Rigid-body group. + + + + + + + + + + + + + + + + + + + + + + + Member of Rigid-body group. + + + + + + + + + + + + + + List of joint definition. + + + + + + + + + + + + + + + + Joint definition. + + + + + + + + + + + + + + + + + + + + + + + + + + Jointed-bones information. + + + + + + + + + + + + + + + Limit of Position. + + + + + + + + + + + + + + + + + + + Limit of Rotation. + + + + + + + + + + + + + + + + + + + Elastic position of joint. + + + + + + + + + + + + + + + + Elastic rotation of joint by degree. + + + + + + + + + + + + + + + + List of surface group. + + + + + + + + + + + + + + + + + + + + + Surface group. + + + + + + + + + + + + + + + + + + Each surface with Triangle. + + + + + + + + + + + + + + + + List of vertex. + + + + + + + + + + + + + + + + + + + + + Vertex definition. + + + + + + + + + + + + + + + + + + + + + + Position definition. + + + + + + + + + + + + + + + + Normal vector definition. + + + + + + + + + + + + + + + + UV-mapping information. + + + + + + + + + + + + + + + Skinning definition from vertex to bone. + + + + + + + + + + + + + + + + -- 2.11.0