From ab0f2745c1495f5ebb3d2779b5e0187c482acda7 Mon Sep 17 00:00:00 2001 From: Olyutorskii Date: Wed, 6 Oct 2010 22:05:19 +0900 Subject: [PATCH] =?utf8?q?PMD=E5=87=BA=E5=8A=9B=E6=A9=9F=E8=83=BD=E5=8F=8A?= =?utf8?q?=E3=81=B3XML=E5=85=A5=E5=87=BA=E5=8A=9B=E6=A9=9F=E8=83=BD?= =?utf8?q?=E3=81=AE=E3=82=BD=E3=83=BC=E3=82=B9=E3=82=92=E3=83=9E=E3=83=BC?= =?utf8?q?=E3=82=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- pom.xml | 12 + .../jp/sourceforge/mikutoga/corelib/I18nText.java | 308 ++++ .../jp/sourceforge/mikutoga/corelib/ListUtil.java | 145 ++ .../mikutoga/corelib/SerialNumbered.java | 66 + .../jp/sourceforge/mikutoga/corelib/WinFile.java | 55 + .../sourceforge/mikutoga/corelib/package-info.java | 14 + .../jp/sourceforge/mikutoga/pmd/BoneGroup.java | 122 ++ .../java/jp/sourceforge/mikutoga/pmd/BoneInfo.java | 202 +++ .../java/jp/sourceforge/mikutoga/pmd/BoneType.java | 123 ++ .../java/jp/sourceforge/mikutoga/pmd/Deg3d.java | 95 ++ .../jp/sourceforge/mikutoga/pmd/DynamicsInfo.java | 138 ++ .../java/jp/sourceforge/mikutoga/pmd/IKChain.java | 131 ++ .../jp/sourceforge/mikutoga/pmd/JointInfo.java | 149 ++ .../java/jp/sourceforge/mikutoga/pmd/Material.java | 245 ++++ .../jp/sourceforge/mikutoga/pmd/MorphPart.java | 109 ++ .../jp/sourceforge/mikutoga/pmd/MorphType.java | 117 ++ .../jp/sourceforge/mikutoga/pmd/MorphVertex.java | 130 ++ .../java/jp/sourceforge/mikutoga/pmd/PmdModel.java | 381 +++++ .../java/jp/sourceforge/mikutoga/pmd/Pos2d.java | 88 ++ .../java/jp/sourceforge/mikutoga/pmd/Pos3d.java | 109 ++ .../java/jp/sourceforge/mikutoga/pmd/Rad3d.java | 95 ++ .../mikutoga/pmd/RigidBehaviorType.java | 102 ++ .../jp/sourceforge/mikutoga/pmd/RigidGroup.java | 99 ++ .../jp/sourceforge/mikutoga/pmd/RigidInfo.java | 196 +++ .../jp/sourceforge/mikutoga/pmd/RigidShape.java | 148 ++ .../sourceforge/mikutoga/pmd/RigidShapeType.java | 102 ++ .../jp/sourceforge/mikutoga/pmd/ShadeInfo.java | 140 ++ .../java/jp/sourceforge/mikutoga/pmd/Surface.java | 175 +++ .../java/jp/sourceforge/mikutoga/pmd/ToonMap.java | 147 ++ .../jp/sourceforge/mikutoga/pmd/TripletRange.java | 182 +++ .../java/jp/sourceforge/mikutoga/pmd/Vec3d.java | 94 ++ .../java/jp/sourceforge/mikutoga/pmd/Vertex.java | 217 +++ .../jp/sourceforge/mikutoga/pmd/package-info.java | 14 + .../mikutoga/pmd/pmdexporter/AbstractExporter.java | 224 +++ .../pmd/pmdexporter/IllegalPmdException.java | 33 + .../pmd/pmdexporter/IllegalPmdTextException.java | 41 + .../mikutoga/pmd/pmdexporter/PmdExporter.java | 30 + .../mikutoga/pmd/pmdexporter/PmdExporterBase.java | 671 +++++++++ .../mikutoga/pmd/pmdexporter/PmdExporterExt1.java | 160 +++ .../mikutoga/pmd/pmdexporter/PmdExporterExt2.java | 72 + .../mikutoga/pmd/pmdexporter/PmdExporterExt3.java | 273 ++++ .../mikutoga/pmd/pmdexporter/package-info.java | 14 + .../mikutoga/pmd/pmdloader/BoneBuilder.java | 286 ++++ .../mikutoga/pmd/pmdloader/JointBuilder.java | 205 +++ .../mikutoga/pmd/pmdloader/MaterialBuilder.java | 180 +++ .../mikutoga/pmd/pmdloader/MorphBuilder.java | 184 +++ .../mikutoga/pmd/pmdloader/PmdLoader.java | 117 ++ .../mikutoga/pmd/pmdloader/RigidBuilder.java | 212 +++ .../mikutoga/pmd/pmdloader/ShapeBuilder.java | 215 +++ .../mikutoga/pmd/pmdloader/TextBuilder.java | 240 ++++ .../mikutoga/pmd/pmdloader/ToonBuilder.java | 79 + .../mikutoga/pmd/pmdloader/package-info.java | 15 + .../mikutoga/pmd/xml/PmdXmlExporter.java | 1275 +++++++++++++++++ .../mikutoga/pmd/xml/PmdXmlResources.java | 95 ++ .../mikutoga/pmd/xml/Xml2PmdLoader.java | 940 ++++++++++++ .../sourceforge/mikutoga/pmd/xml/pckage-info.java | 14 + .../sourceforge/mikutoga/xml/BasicXmlExporter.java | 471 ++++++ .../java/jp/sourceforge/mikutoga/xml/DomUtils.java | 356 +++++ .../sourceforge/mikutoga/xml/TogaXmlException.java | 52 + .../mikutoga/xml/XmlResourceResolver.java | 371 +++++ .../jp/sourceforge/mikutoga/xml/package-info.java | 14 + .../mikutoga/pmd/resources/BoneTypeName.properties | 14 + .../pmd/resources/BoneTypeName_ja.properties | 14 + .../pmd/resources/MorphTypeName.properties | 9 + .../pmd/resources/MorphTypeName_ja.properties | 9 + .../pmd/resources/RigidBehaviorTypeName.properties | 7 + .../resources/RigidBehaviorTypeName_ja.properties | 7 + .../pmd/resources/RigidShapeTypeName.properties | 7 + .../pmd/resources/RigidShapeTypeName_ja.properties | 7 + .../mikutoga/pmd/xml/resources/pmdxml-100923.dtd | 532 +++++++ .../mikutoga/pmd/xml/resources/pmdxml-100923.xsd | 1511 ++++++++++++++++++++ .../mikutoga/xml/resources/xml-2009-01.xsd | 286 ++++ .../jp/sourceforge/mikutoga/pmd/MorphTypeTest.java | 126 ++ 73 files changed, 13818 insertions(+) create mode 100644 src/main/java/jp/sourceforge/mikutoga/corelib/I18nText.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/corelib/ListUtil.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/corelib/SerialNumbered.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/corelib/WinFile.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/corelib/package-info.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/BoneGroup.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/BoneInfo.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/BoneType.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/Deg3d.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/DynamicsInfo.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/IKChain.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/JointInfo.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/Material.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/MorphPart.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/MorphType.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/MorphVertex.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/PmdModel.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/Pos2d.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/Pos3d.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/Rad3d.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/RigidBehaviorType.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/RigidGroup.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/RigidInfo.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/RigidShape.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/RigidShapeType.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/ShadeInfo.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/Surface.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/ToonMap.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/TripletRange.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/Vec3d.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/Vertex.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/package-info.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/pmdexporter/AbstractExporter.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/pmdexporter/IllegalPmdException.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/pmdexporter/IllegalPmdTextException.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/pmdexporter/PmdExporter.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/pmdexporter/PmdExporterBase.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/pmdexporter/PmdExporterExt1.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/pmdexporter/PmdExporterExt2.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/pmdexporter/PmdExporterExt3.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/pmdexporter/package-info.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/pmdloader/BoneBuilder.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/pmdloader/JointBuilder.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/pmdloader/MaterialBuilder.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/pmdloader/MorphBuilder.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/pmdloader/PmdLoader.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/pmdloader/RigidBuilder.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/pmdloader/ShapeBuilder.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/pmdloader/TextBuilder.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/pmdloader/ToonBuilder.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/pmdloader/package-info.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/xml/PmdXmlExporter.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/xml/PmdXmlResources.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/xml/Xml2PmdLoader.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/pmd/xml/pckage-info.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/xml/BasicXmlExporter.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/xml/DomUtils.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/xml/TogaXmlException.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/xml/XmlResourceResolver.java create mode 100644 src/main/java/jp/sourceforge/mikutoga/xml/package-info.java create mode 100644 src/main/resources/jp/sourceforge/mikutoga/pmd/resources/BoneTypeName.properties create mode 100644 src/main/resources/jp/sourceforge/mikutoga/pmd/resources/BoneTypeName_ja.properties create mode 100644 src/main/resources/jp/sourceforge/mikutoga/pmd/resources/MorphTypeName.properties create mode 100644 src/main/resources/jp/sourceforge/mikutoga/pmd/resources/MorphTypeName_ja.properties create mode 100644 src/main/resources/jp/sourceforge/mikutoga/pmd/resources/RigidBehaviorTypeName.properties create mode 100644 src/main/resources/jp/sourceforge/mikutoga/pmd/resources/RigidBehaviorTypeName_ja.properties create mode 100644 src/main/resources/jp/sourceforge/mikutoga/pmd/resources/RigidShapeTypeName.properties create mode 100644 src/main/resources/jp/sourceforge/mikutoga/pmd/resources/RigidShapeTypeName_ja.properties create mode 100644 src/main/resources/jp/sourceforge/mikutoga/pmd/xml/resources/pmdxml-100923.dtd create mode 100644 src/main/resources/jp/sourceforge/mikutoga/pmd/xml/resources/pmdxml-100923.xsd create mode 100644 src/main/resources/jp/sourceforge/mikutoga/xml/resources/xml-2009-01.xsd create mode 100644 src/test/java/jp/sourceforge/mikutoga/pmd/MorphTypeTest.java diff --git a/pom.xml b/pom.xml index 943fe0d..b0316c1 100644 --- a/pom.xml +++ b/pom.xml @@ -194,6 +194,18 @@ + + src/main/resources + + **/*.properties + **/*.xsd + **/*.dtd + + + **/version.properties + + + diff --git a/src/main/java/jp/sourceforge/mikutoga/corelib/I18nText.java b/src/main/java/jp/sourceforge/mikutoga/corelib/I18nText.java new file mode 100644 index 0000000..3cfffba --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/corelib/I18nText.java @@ -0,0 +1,308 @@ +/* + * international text + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.corelib; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +/** + * 多言語のバリアントを持つ文字列情報。 + * + */ +public class I18nText implements CharSequence { + + /** プライマリ言語のロケール。 */ + public static final Locale LOCALE_PRIMARY = Locale.JAPANESE; + /** プライマリ言語の言語コード。 */ + public static final String CODE639_PRIMARY = LOCALE_PRIMARY.getLanguage(); + + /** グローバル言語のロケール。 */ + public static final Locale LOCALE_GLOBAL = Locale.ENGLISH; + /** グローバル言語の言語コード。 */ + public static final String CODE639_GLOBAL = LOCALE_GLOBAL.getLanguage(); + + static{ + assert CODE639_PRIMARY.equals("ja"); + assert CODE639_GLOBAL .equals("en"); + } + + private final Map nameMap = new HashMap(); + + /** + * コンストラクタ。 + */ + public I18nText(){ + super(); + return; + } + + /** + * プライマリ文字列の登録。 + * @param seq プライマリ文字列。nullの場合は削除動作 + */ + public void setPrimaryText(CharSequence seq){ + setText(CODE639_PRIMARY, seq); + return; + } + + /** + * グローバル文字列の登録。 + * @param seq グローバル文字列。nullの場合は削除動作 + */ + public void setGlobalText(CharSequence seq){ + setText(CODE639_GLOBAL, seq); + return; + } + + /** + * 任意のロケールに関連付けられた文字列の登録。 + * @param locale ロケール + * @param seq 文字列。nullの場合は削除動作 + * @throws NullPointerException ロケール引数がnull + */ + public void setText(Locale locale, CharSequence seq) + throws NullPointerException{ + String code639 = locale.getLanguage(); + setText(code639, seq); + return; + } + + /** + * 任意の言語コードに関連付けられた文字列の登録。 + * @param code639 ISO639言語コード + * @param seq 文字列。nullの場合は削除動作 + * @throws NullPointerException 言語コードがnull + */ + public void setText(String code639, CharSequence seq) + throws NullPointerException{ + if(code639 == null) throw new NullPointerException(); + + if(seq != null){ + String text = seq.toString(); + this.nameMap.put(code639, text); + }else{ + this.nameMap.remove(code639); + } + + return; + } + + /** + * 言語コードに応じた文字列を返す。 + * @param code639 ISO639言語コード + * @return 文字列。見つからなければnullを返す。 + * @throws NullPointerException 引数がnull + */ + public String getText(String code639) throws NullPointerException{ + if(code639 == null) throw new NullPointerException(); + String result = this.nameMap.get(code639); + return result; + } + + /** + * ロケールに応じた文字列を返す。 + * @param locale ロケール + * @return 文字列。見つからなければnullを返す。 + * @throws NullPointerException 引数がnull + */ + public String getText(Locale locale) throws NullPointerException{ + String code639 = locale.getLanguage(); + String result = getText(code639); + return result; + } + + /** + * プライマリ文字列を返す。 + * @return 文字列。見つからなければnullを返す。 + */ + public String getPrimaryText(){ + String result = getText(CODE639_PRIMARY); + return result; + } + + /** + * グローバル文字列を返す。 + * @return 文字列。見つからなければnullを返す。 + */ + public String getGlobalText(){ + String result = getText(CODE639_GLOBAL); + return result; + } + + /** + * プライマリ文字列を返す。 + * 見つからなければグローバル文字列を返す。 + * それでも見つからなければ空文字列を返す。 + * @return 文字列 + */ + public String getText(){ + String result; + + result = getPrimaryText(); + + if(result == null){ + result = getGlobalText(); + } + + if(result == null){ + result = ""; + } + + return result; + } + + /** + * 実行環境のデフォルトロケールに応じた文字列を返す。 + * 見つからなければグローバル文字列、プライマリ文字列の順に返す。 + * それでも見つからなければ適当な言語コードの文字列を返す。 + * それでも見つからなければ空文字列を返す。 + * デフォルトロケールの確認はその都度行われる。 + * @return 文字列 + */ + public String getLocalizedText(){ + Locale locale = Locale.getDefault(); + String langCode = locale.getLanguage(); + + String result; + + result = this.nameMap.get(langCode); + + if(result == null){ + result = this.nameMap.get(CODE639_GLOBAL); + } + + if(result == null){ + result = this.nameMap.get(CODE639_PRIMARY); + } + + if(result == null){ + Set langSet = this.nameMap.keySet(); + Iterator it = langSet.iterator(); + while(it.hasNext()){ + String lang = it.next(); + result = this.nameMap.get(lang); + if(result != null) break; + } + } + + if(result == null){ + result = ""; + } + + return result; + } + + /** + * 全言語の文字列を削除する。 + */ + public void removeAllText(){ + this.nameMap.clear(); + return; + } + + /** + * 登録済みの全ISO639言語コードリストを返す。 + * 優先度はプライマリ、グローバル、その他の順。 + * @return 全ISO639言語コード + */ + public List lang639CodeList(){ + Set set = this.nameMap.keySet(); + List result = new ArrayList(set.size()); + + for(String lang : set){ + if(lang.equals(CODE639_PRIMARY)) result.add(lang); + } + + for(String lang : set){ + if(lang.equals(CODE639_GLOBAL)) result.add(lang); + } + + for(String lang : set){ + if(lang.equals(CODE639_PRIMARY)) continue; + if(lang.equals(CODE639_GLOBAL)) continue; + result.add(lang); + } + + return result; + } + + /** + * プライマリ文字列が登録されているか判定する。 + * @return 登録されていればtrue + */ + public boolean hasPrimaryText(){ + boolean result = this.nameMap.containsKey(CODE639_PRIMARY); + return result; + } + + /** + * グローバル文字列が登録されているか判定する。 + * @return 登録されていればtrue + */ + public boolean hasGlobalText(){ + boolean result = this.nameMap.containsKey(CODE639_GLOBAL); + return result; + } + + /** + * {@inheritDoc} + * {@link #getText()}に準ずる。 + * @param index {@inheritDoc} + * @return {@inheritDoc} + */ + public char charAt(int index){ + String text = getText(); + char result = text.charAt(index); + return result; + } + + /** + * {@inheritDoc} + * {@link #getText()}に準ずる。 + * @return {@inheritDoc} + */ + public int length(){ + String text = getText(); + int result = text.length(); + return result; + } + + /** + * {@inheritDoc} + * {@link #getText()}に準ずる。 + * @param start {@inheritDoc} + * @param end {@inheritDoc} + * @return {@inheritDoc} + */ + public CharSequence subSequence(int start, int end){ + String text = getText(); + CharSequence result = text.subSequence(start, end); + return result; + } + + /** + * {@inheritDoc} + * {@link #getText()}に準ずる。 + * @return {@inheritDoc} + */ + @Override + public String toString(){ + return getText(); + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/corelib/ListUtil.java b/src/main/java/jp/sourceforge/mikutoga/corelib/ListUtil.java new file mode 100644 index 0000000..af9e099 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/corelib/ListUtil.java @@ -0,0 +1,145 @@ +/* + * List utilities + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.corelib; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.Collection; +import java.util.List; + +/** + * リスト構造の各種ユーティリティ。 + */ +public final class ListUtil { + + /** + * 隠しコンストラクタ。 + */ + private ListUtil(){ + assert false; + throw new AssertionError(); + } + + /** + * リストの出現順にシリアルナンバーを割り振る。 + * シリアルナンバー先頭は0。 + * @param list リスト。なるべく{@link java.util.RandomAccess}型が望ましい。 + */ + public static void assignIndexedSerial( + List list){ + int size = list.size(); + for(int idx = 0; idx < size; idx++){ + SerialNumbered numbered = list.get(idx); + numbered.setSerialNumber(idx); + } + return; + } + + /** + * コレクションの要素数を拡張する。 + * 追加された要素にはnullが収められる。 + * コレクションがすでに指定サイズ以上の要素数を持つ場合、何もしない。 + * @param 型 + * @param coll コレクション + * @param newSize 新サイズ + */ + public static void extendCollection(Collection coll, int newSize){ + int remain = newSize - coll.size(); + if(remain <= 0) return; + + for(int ct = 1; ct <= remain; ct++){ + coll.add(null); + } + + assert coll.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 ){ + extendCollection(list, newSize); + int result = fillDefCons(list, klass); + return result; + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/corelib/SerialNumbered.java b/src/main/java/jp/sourceforge/mikutoga/corelib/SerialNumbered.java new file mode 100644 index 0000000..b1d9b84 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/corelib/SerialNumbered.java @@ -0,0 +1,66 @@ +/* + * serial-numbered interface + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.corelib; + +import java.util.Comparator; + +/** + * 通し番号を持つオブジェクトの抽象化インタフェース。 + */ +public interface SerialNumbered { + + /** 昇順での比較子。 */ + public Comparator COMPARATOR = new SerialComparator(); + + /** + * 通し番号を設定する。 + * @param num 通し番号 + */ + void setSerialNumber(int num); + + /** + * 通し番号を返す。 + * @return 通し番号 + */ + int getSerialNumber(); + + /** + * 通し番号による比較子Comparator。 + * 通し番号の昇順を定義づける。 + */ + public static class SerialComparator + implements Comparator { + + /** + * コンストラクタ。 + */ + public SerialComparator(){ + super(); + return; + } + + /** + * {@inheritDoc} + * @param o1 {@inheritDoc} + * @param o2 {@inheritDoc} + * @return {@inheritDoc} + */ + 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/corelib/WinFile.java b/src/main/java/jp/sourceforge/mikutoga/corelib/WinFile.java new file mode 100644 index 0000000..8de5060 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/corelib/WinFile.java @@ -0,0 +1,55 @@ +/* + * Windows File utils + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.corelib; + +/** + * Windowsに特化したFileユーティリティ。 + */ +public final class WinFile { + + public static final char SEPARATOR_CHAR = '\\'; // \ + public static final String SEPARATOR = + Character.toString(SEPARATOR_CHAR); + public static final String PFX_UNC = + SEPARATOR + SEPARATOR; // \\ + + static{ + assert '\\' == 0x005c; + } + + /** + * 隠しコンストラクタ。 + */ + private WinFile(){ + assert false; + throw new AssertionError(); + } + + /** + * Windowsファイル名の正規化を行う。 + * UNCも考慮される。 + * 相対パスは相対パスのまま。 + *
    + *
  • 頭の3回以上連続する\記号は2個の\記号に置き換えられる。 + *
  • 末尾の1回以上連続する\記号は削除。 + * ただし頭から連続している場合は削除しない。 + *
  • 2回以上連続する\記号は1個の\記号にまとめられる。 + * ただし頭から連続している場合はまとめない。 + *
+ * @param seq 対象ファイル名 + * @return 正規化されたファイル名 + */ + public static String normalizeWinFileName(CharSequence seq){ + String text = seq.toString(); + text = text.replaceAll("^\\\\{3,}", "\\\\\\\\"); + text = text.replaceAll("(.*[^\\\\])\\\\+$", "$1"); + text = text.replaceAll("([^\\\\])\\\\{2,}", "$1\\\\"); + return text; + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/corelib/package-info.java b/src/main/java/jp/sourceforge/mikutoga/corelib/package-info.java new file mode 100644 index 0000000..5f416f3 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/corelib/package-info.java @@ -0,0 +1,14 @@ +/* + * package information for Javadoc + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +/** + * MikuToga共通基盤ライブラリ。 + */ + +package jp.sourceforge.mikutoga.corelib; + +/* EOF */ diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/BoneGroup.java b/src/main/java/jp/sourceforge/mikutoga/pmd/BoneGroup.java new file mode 100644 index 0000000..52f3394 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/BoneGroup.java @@ -0,0 +1,122 @@ +/* + * bone group + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import jp.sourceforge.mikutoga.corelib.I18nText; +import jp.sourceforge.mikutoga.corelib.SerialNumbered; + +/** + * ボーングループ。 + * ボーングループ名と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} + */ + public Iterator iterator(){ + return this.boneList.iterator(); + } + + /** + * {@inheritDoc} + * @param num {@inheritDoc} + */ + public void setSerialNumber(int num){ + this.serialNo = num; + return; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + 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/BoneInfo.java b/src/main/java/jp/sourceforge/mikutoga/pmd/BoneInfo.java new file mode 100644 index 0000000..72e3c4b --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/BoneInfo.java @@ -0,0 +1,202 @@ +/* + * bone information + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd; + +import jp.sourceforge.mikutoga.corelib.I18nText; +import jp.sourceforge.mikutoga.corelib.SerialNumbered; + +/** + * ボーン情報。 + */ +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 Pos3d position = new Pos3d(); + + 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 Pos3d 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} + */ + public void setSerialNumber(int num){ + this.serialNo = num; + return; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + 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/BoneType.java b/src/main/java/jp/sourceforge/mikutoga/pmd/BoneType.java new file mode 100644 index 0000000..365b020 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/BoneType.java @@ -0,0 +1,123 @@ +/* + * bone type + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd; + +import java.util.Locale; +import java.util.ResourceBundle; + +/** + * ボーン種別。 + *
    + *
  • 0x00:回転 + *
  • 0x01:回転/移動 + *
  • 0x02:IK + *
  • 0x03:不明 + *
  • 0x04:IK影響下(回転) + *
  • 0x05:回転影響下 + *
  • 0x06:IK接続先 + *
  • 0x07:非表示 + *
  • 0x08:捩り + *
  • 0x09:回転連動 + *
+ */ +public enum BoneType { + + /** 回転。 */ + ROTATE(0x00), + /** 回転/移動。 */ + ROTMOV(0x01), + /** IK。 */ + IK(0x02), + /** 不明。 */ + UNKNOWN(0x03), + /** IK影響下(回転)。 */ + UNDERIK(0x04), + /** 回転影響下。 */ + UNDERROT(0x05), + /** IK接続先。 */ + IKCONNECTED(0x06), + /** 非表示。 */ + HIDDEN(0x07), + /** 捩り。 */ + TWIST(0x08), + /** 回転連動。 */ + LINKEDROT(0x09), + ; + + private static final String FAMILY_NAME = + "jp.sourceforge.mikutoga.pmd.resources.BoneTypeName"; + + private final byte encoded; + + /** + * コンストラクタ。 + * @param code 符号化int値 + */ + private BoneType(int code){ + this((byte)code); + return; + } + + /** + * コンストラクタ。 + * @param code 符号化byte値 + */ + private BoneType(byte code){ + this.encoded = code; + return; + } + + /** + * byte値からデコードする。 + * @param code byte値 + * @return デコードされた列挙子。該当するものがなければnull + */ + public static BoneType decode(byte code){ + BoneType result = null; + + for(BoneType type : values()){ + if(type.encode() == code){ + result = type; + break; + } + } + + return result; + } + + /** + * byte値にエンコードする。 + * @return byte値 + */ + public byte encode(){ + return this.encoded; + } + + /** + * デフォルトロケールでの表示名を返す。 + * @return 表示名 + */ + public String getGuiName(){ + Locale locale = Locale.getDefault(); + return getGuiName(locale); + } + + /** + * ロケールに準じた表示名を返す。 + * @param locale ロケール。nullならデフォルトロケールと解釈される。 + * @return 表示名 + */ + public String getGuiName(Locale locale){ + if(locale == null) return getGuiName(); + ResourceBundle rb = ResourceBundle.getBundle(FAMILY_NAME, locale); + String key = name(); + String result = rb.getString(key); + return result; + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/Deg3d.java b/src/main/java/jp/sourceforge/mikutoga/pmd/Deg3d.java new file mode 100644 index 0000000..7ae17e9 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/Deg3d.java @@ -0,0 +1,95 @@ +/* + * 3d rotation (degree) + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd; + +/** + * XYZ3軸による回転量(degree)。 + * radianではなくdegree。(直角は90.0) + */ +public class Deg3d { + + private float xDeg; + private float yDeg; + private float zDeg; + + /** + * コンストラクタ。 + */ + public Deg3d(){ + super(); + return; + } + + /** + * X軸回転量を設定する。 + * @param xDeg X軸回転量(degree) + */ + public void setXDeg(float xDeg){ + this.xDeg = xDeg; + return; + } + + /** + * X軸回転量を返す。 + * @return X軸回転量(degree) + */ + public float getXDeg(){ + return this.xDeg; + } + + /** + * Y軸回転量を設定する。 + * @param yDeg Y軸回転量(degree) + */ + public void setYDeg(float yDeg){ + this.yDeg = yDeg; + return; + } + + /** + * Y軸回転量を返す。 + * @return Y軸回転量(degree) + */ + public float getYDeg(){ + return this.yDeg; + } + + /** + * Z軸回転量を設定する。 + * @param zDeg Z軸回転量(degree) + */ + public void setZDeg(float zDeg){ + this.zDeg = zDeg; + return; + } + + /** + * Z軸回転量を返す。 + * @return Z軸回転量(degree) + */ + public float getZDeg(){ + return this.zDeg; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public String toString(){ + StringBuilder result = new StringBuilder(); + + result.append("deg=[") + .append(this.xDeg).append(", ") + .append(this.yDeg).append(", ") + .append(this.zDeg).append(']'); + + return result.toString(); + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/DynamicsInfo.java b/src/main/java/jp/sourceforge/mikutoga/pmd/DynamicsInfo.java new file mode 100644 index 0000000..3dbcf80 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/DynamicsInfo.java @@ -0,0 +1,138 @@ +/* + * dynamics parameter + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd; + +/** + * 剛体間力学演算の各種パラメータ。 + * 各剛体に設定可能なパラメータは + * 「質量」、「移動減衰率」、「回転減衰率」、「反発力」、「摩擦力」の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/IKChain.java b/src/main/java/jp/sourceforge/mikutoga/pmd/IKChain.java new file mode 100644 index 0000000..bb3738e --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/IKChain.java @@ -0,0 +1,131 @@ +/* + * IK chained bone + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd; + +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} + */ + 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/JointInfo.java b/src/main/java/jp/sourceforge/mikutoga/pmd/JointInfo.java new file mode 100644 index 0000000..44165e2 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/JointInfo.java @@ -0,0 +1,149 @@ +/* + * joint information + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd; + +import jp.sourceforge.mikutoga.corelib.I18nText; + +/** + * 剛体間ジョイント情報。 + */ +public class JointInfo { + + private final I18nText jointName = new I18nText(); + + private RigidInfo rigidA; + private RigidInfo rigidB; + + private final Pos3d position = new Pos3d(); + private final Rad3d rotation = new Rad3d(); + + private final Pos3d elaPosition = new Pos3d(); + 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 Pos3d getPosition(){ + return this.position; + } + + /** + * ジョイントの姿勢を返す。 + * @return ジョイントの姿勢 + */ + public Rad3d getRotation(){ + return this.rotation; + } + + /** + * ジョイントのバネ位置を返す。 + * @return ジョイントのバネ位置 + */ + public Pos3d 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/Material.java b/src/main/java/jp/sourceforge/mikutoga/pmd/Material.java new file mode 100644 index 0000000..c88161f --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/Material.java @@ -0,0 +1,245 @@ +/* + * material information + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd; + +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} + */ + 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/MorphPart.java b/src/main/java/jp/sourceforge/mikutoga/pmd/MorphPart.java new file mode 100644 index 0000000..50ac72b --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/MorphPart.java @@ -0,0 +1,109 @@ +/* + * morph information + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import jp.sourceforge.mikutoga.corelib.I18nText; +import jp.sourceforge.mikutoga.corelib.SerialNumbered; + +/** + * 個別モーフ情報。 + */ +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} + */ + public Iterator iterator(){ + return this.morphVertexList.iterator(); + } + + /** + * {@inheritDoc} + * @param num {@inheritDoc} + */ + public void setSerialNumber(int num){ + this.serialNo = num; + return; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + 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/MorphType.java b/src/main/java/jp/sourceforge/mikutoga/pmd/MorphType.java new file mode 100644 index 0000000..5dba949 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/MorphType.java @@ -0,0 +1,117 @@ +/* + * morph type + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd; + +import java.util.Locale; +import java.util.ResourceBundle; + +/** + * モーフ種別。 + *
    + *
  • 0:base + *
  • 1:まゆ + *
  • 2:目 + *
  • 3:リップ + *
  • 4:その他 + *
+ */ +public enum MorphType { + + /** base。 */ + BASE(0x00), + /** まゆ。 */ + EYEBROW(0x01), + /** 目。 */ + EYE(0x02), + /** リップ。 */ + LIP(0x03), + /** その他。 */ + EXTRA(0x04), + ; + + private static final String FAMILY_NAME = + "jp.sourceforge.mikutoga.pmd.resources.MorphTypeName"; + + private final byte encoded; + + /** + * コンストラクタ。 + * @param code 符号化int値 + */ + private MorphType(int code){ + this((byte)code); + return; + } + + /** + * コンストラクタ。 + * @param code 符号化byte値 + */ + private MorphType(byte code){ + this.encoded = code; + return; + } + + /** + * byte値からデコードする。 + * @param code byte値 + * @return デコードされた列挙子。該当するものがなければnull + */ + public static MorphType decode(byte code){ + MorphType result = null; + + for(MorphType type : values()){ + if(type.encode() == code){ + result = type; + break; + } + } + + return result; + } + + /** + * byte値にエンコードする。 + * @return byte値 + */ + public byte encode(){ + return this.encoded; + } + + /** + * デフォルトロケールでの表示名を返す。 + * @return 表示名 + */ + public String getGuiName(){ + Locale locale = Locale.getDefault(); + return getGuiName(locale); + } + + /** + * ロケールに準じた表示名を返す。 + * @param locale ロケール。nullならデフォルトロケールと解釈される。 + * @return 表示名 + */ + public String getGuiName(Locale locale){ + if(locale == null) return getGuiName(); + ResourceBundle rb = ResourceBundle.getBundle(FAMILY_NAME, locale); + String key = name(); + String result = rb.getString(key); + return result; + } + + /** + * モーフ種別がbaseか否か判定する。 + * @return baseならtrue + */ + public boolean isBase(){ + if(this == BASE) return true; + return false; + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/MorphVertex.java b/src/main/java/jp/sourceforge/mikutoga/pmd/MorphVertex.java new file mode 100644 index 0000000..719c3dd --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/MorphVertex.java @@ -0,0 +1,130 @@ +/* + * morph vertex information + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd; + +import java.util.Comparator; +import jp.sourceforge.mikutoga.corelib.SerialNumbered; + +/** + * モーフアニメーションを構成する個別の頂点移動の情報。 + */ +public class MorphVertex implements SerialNumbered{ + + /** 頂点IDを昇順に順序づけるComaparator。 */ + public static final Comparator VIDCOMPARATOR = + new VertexIdComparator(); + + private Vertex baseVertex; + private final Pos3d offset = new Pos3d(); + + 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 Pos3d getOffset(){ + return this.offset; + } + + /** + * {@inheritDoc} + * @param num {@inheritDoc} + */ + public void setSerialNumber(int num){ + this.serialNo = num; + return; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + 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} + */ + 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/PmdModel.java b/src/main/java/jp/sourceforge/mikutoga/pmd/PmdModel.java new file mode 100644 index 0000000..d6dd882 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/PmdModel.java @@ -0,0 +1,381 @@ +/* + * PMD model + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd; + +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; + +/** + * PMDモデルファイル一式に相当するもの。 + * 様々な基本構造のリストの集合から構成される。 + */ +public class PmdModel { + + /** デフォルトのヘッダバージョン。 */ + public static final float DEF_HEADERVER = 1.0f; + + private float headerVersion = DEF_HEADERVER; + + 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; + } + + /** + * PMDファイルのヘッダバージョンを返す。 + * @return PMDファイルのヘッダバージョン + */ + public float getHeaderVersion(){ + return this.headerVersion; + } + + /** + * PMDファイルのヘッダバージョンを設定する。 + * @param ver PMDファイルのヘッダバージョン + */ + public void setHeaderVersion(float ver){ + this.headerVersion = ver; + 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/Pos2d.java b/src/main/java/jp/sourceforge/mikutoga/pmd/Pos2d.java new file mode 100644 index 0000000..dd1643d --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/Pos2d.java @@ -0,0 +1,88 @@ +/* + * 2D position + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd; + +/** + * 二次元空間座標及び変量を表す。 + */ +public class Pos2d { + + private float xPos; + private float yPos; + + /** + * コンストラクタ。 + * [0,0]が設定される + */ + public Pos2d(){ + this(0.0f, 0.0f); + return; + } + + /** + * コンストラクタ。 + * @param xPos X座標 + * @param yPos Y座標 + */ + public Pos2d(float xPos, float yPos){ + super(); + this.xPos = xPos; + this.yPos = yPos; + return; + } + + /** + * X座標を設定する。 + * @param xPos X座標 + */ + public void setXPos(float xPos){ + this.xPos = xPos; + return; + } + + /** + * X座標を返す。 + * @return X座標 + */ + public float getXPos(){ + return this.xPos; + } + + /** + * Y座標を設定する。 + * @param yPos Y座標 + */ + public void setYPos(float yPos){ + this.yPos = yPos; + return; + } + + /** + * Y座標を返す。 + * @return Y座標 + */ + public float getYPos(){ + return this.yPos; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public String toString(){ + StringBuilder result = new StringBuilder(); + + result.append("pos=[") + .append(this.xPos).append(", ") + .append(this.yPos).append(']'); + + return result.toString(); + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/Pos3d.java b/src/main/java/jp/sourceforge/mikutoga/pmd/Pos3d.java new file mode 100644 index 0000000..aa5dbfe --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/Pos3d.java @@ -0,0 +1,109 @@ +/* + * 3D position + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd; + +/** + * 三次元空間座標及び変量を表す。 + */ +public class Pos3d { + + private float xPos; + private float yPos; + private float zPos; + + /** + * コンストラクタ。 + * [0,0,0]が設定される。 + */ + public Pos3d(){ + this(0.0f, 0.0f, 0.0f); + return; + } + + /** + * コンストラクタ。 + * @param xPos X座標 + * @param yPos Y座標 + * @param zPos Z座標 + */ + public Pos3d(float xPos, float yPos, float zPos){ + super(); + this.xPos = xPos; + this.yPos = yPos; + this.zPos = zPos; + return; + } + + /** + * X座標を設定する。 + * @param xPos X座標 + */ + public void setXPos(float xPos){ + this.xPos = xPos; + return; + } + + /** + * X座標を返す。 + * @return X座標 + */ + public float getXPos(){ + return this.xPos; + } + + /** + * Y座標を設定する。 + * @param yPos Y座標 + */ + public void setYPos(float yPos){ + this.yPos = yPos; + return; + } + + /** + * Y座標を返す。 + * @return Y座標 + */ + public float getYPos(){ + return this.yPos; + } + + /** + * Z座標を設定する。 + * @param zPos Z座標 + */ + public void setZPos(float zPos){ + this.zPos = zPos; + return; + } + + /** + * Z座標を返す。 + * @return Z座標 + */ + public float getZPos(){ + return this.zPos; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public String toString(){ + StringBuilder result = new StringBuilder(); + + result.append("pos=[") + .append(this.xPos).append(", ") + .append(this.yPos).append(", ") + .append(this.zPos).append(']'); + + return result.toString(); + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/Rad3d.java b/src/main/java/jp/sourceforge/mikutoga/pmd/Rad3d.java new file mode 100644 index 0000000..539dfc9 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/Rad3d.java @@ -0,0 +1,95 @@ +/* + * 3d rotation (radian) + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd; + +/** + * XYZ3軸による回転量(radian)。 + * degereeではなくradian。(直角はΠ/2) + */ +public class Rad3d { + + private float xRad; + private float yRad; + private float zRad; + + /** + * コンストラクタ。 + */ + public Rad3d(){ + super(); + return; + } + + /** + * X軸回転量を設定する。 + * @param xRad X軸回転量(radian) + */ + public void setXRad(float xRad){ + this.xRad = xRad; + return; + } + + /** + * X軸回転量を返す。 + * @return X軸回転量(radian) + */ + public float getXRad(){ + return this.xRad; + } + + /** + * Y軸回転量を設定する。 + * @param yRad Y軸回転量(radian) + */ + public void setYRad(float yRad){ + this.yRad = yRad; + return; + } + + /** + * Y軸回転量を返す。 + * @return Y軸回転量(radian) + */ + public float getYRad(){ + return this.yRad; + } + + /** + * Z軸回転量を設定する。 + * @param zRad Z軸回転量(radian) + */ + public void setZRad(float zRad){ + this.zRad = zRad; + return; + } + + /** + * Z軸回転量を返す。 + * @return Z軸回転量(radian) + */ + public float getZRad(){ + return this.zRad; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public String toString(){ + StringBuilder result = new StringBuilder(); + + result.append("rad=[") + .append(this.xRad).append(", ") + .append(this.yRad).append(", ") + .append(this.zRad).append(']'); + + return result.toString(); + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/RigidBehaviorType.java b/src/main/java/jp/sourceforge/mikutoga/pmd/RigidBehaviorType.java new file mode 100644 index 0000000..a17879d --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/RigidBehaviorType.java @@ -0,0 +1,102 @@ +/* + * rigid behavior type + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd; + +import java.util.Locale; +import java.util.ResourceBundle; + +/** + * 剛体の振る舞い種別。 + *

    + *
  • 0x00:ボーン追従 + *
  • 0x01:物理演算 + *
  • 0x02:物理演算+ボーン位置合わせ + *
+ */ +public enum RigidBehaviorType { + + /** ボーン追従。 */ + FOLLOWBONE(0x00), + /** 物理演算。 */ + ONLYDYNAMICS(0x01), + /** 物理演算+ボーン位置合わせ。 */ + BONEDDYNAMICS(0x02), + ; + + private static final String FAMILY_NAME = + "jp.sourceforge.mikutoga.pmd.resources.RigidBehaviorTypeName"; + + private final byte encoded; + + /** + * コンストラクタ。 + * @param code 符号化int値 + */ + private RigidBehaviorType(int code){ + this((byte)code); + return; + } + + /** + * コンストラクタ。 + * @param code 符号化byte値 + */ + private RigidBehaviorType(byte code){ + this.encoded = code; + return; + } + + /** + * byte値からデコードする。 + * @param code byte値 + * @return デコードされた列挙子。該当するものがなければnull + */ + public static RigidBehaviorType decode(byte code){ + RigidBehaviorType result = null; + + for(RigidBehaviorType type : values()){ + if(type.encode() == code){ + result = type; + break; + } + } + + return result; + } + + /** + * byte値にエンコードする。 + * @return byte値 + */ + public byte encode(){ + return this.encoded; + } + + /** + * デフォルトロケールでの表示名を返す。 + * @return 表示名 + */ + public String getGuiName(){ + Locale locale = Locale.getDefault(); + return getGuiName(locale); + } + + /** + * ロケールに準じた表示名を返す。 + * @param locale ロケール。nullならデフォルトロケールと解釈される。 + * @return 表示名 + */ + public String getGuiName(Locale locale){ + if(locale == null) return getGuiName(); + ResourceBundle rb = ResourceBundle.getBundle(FAMILY_NAME, locale); + String key = name(); + String result = rb.getString(key); + return result; + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/RigidGroup.java b/src/main/java/jp/sourceforge/mikutoga/pmd/RigidGroup.java new file mode 100644 index 0000000..a57a301 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/RigidGroup.java @@ -0,0 +1,99 @@ +/* + * rigid group + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import jp.sourceforge.mikutoga.corelib.SerialNumbered; + +/** + * 剛体グループ情報。 + * 剛体間の衝突設定の対象となる。 + */ +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} + */ + public Iterator iterator(){ + return this.rigidList.iterator(); + } + + /** + * {@inheritDoc} + * @param num {@inheritDoc} + */ + public void setSerialNumber(int num){ + this.serialNo = num; + return; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + 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/RigidInfo.java b/src/main/java/jp/sourceforge/mikutoga/pmd/RigidInfo.java new file mode 100644 index 0000000..b734602 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/RigidInfo.java @@ -0,0 +1,196 @@ +/* + * rigid information + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd; + +import java.util.ArrayList; +import java.util.Collection; +import jp.sourceforge.mikutoga.corelib.I18nText; +import jp.sourceforge.mikutoga.corelib.SerialNumbered; + +/** + * 個別の剛体の情報。 + */ +public class RigidInfo implements SerialNumbered { + + private final I18nText rigidName = new I18nText(); + + private RigidBehaviorType behaviorType = RigidBehaviorType.FOLLOWBONE; + + private final RigidShape rigidShape = new RigidShape(); + private final Pos3d position = new Pos3d(); + 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 Pos3d 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} + */ + public void setSerialNumber(int num){ + this.serialNo = num; + return; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + public int getSerialNumber(){ + return this.serialNo; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public String toString(){ + StringBuilder result = new StringBuilder(); + + result.append("Rigid(").append(this.rigidName).append(") "); + result.append("[=>") + .append(this.linkedBone.getBoneName()) + .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/RigidShape.java b/src/main/java/jp/sourceforge/mikutoga/pmd/RigidShape.java new file mode 100644 index 0000000..5906953 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/RigidShape.java @@ -0,0 +1,148 @@ +/* + * rigid shape information + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd; + +/** + * 剛体形状に関する情報。 + * 球及びカプセルの半径と箱の幅は同じ値が用いられる。 + */ +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/RigidShapeType.java b/src/main/java/jp/sourceforge/mikutoga/pmd/RigidShapeType.java new file mode 100644 index 0000000..33df15d --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/RigidShapeType.java @@ -0,0 +1,102 @@ +/* + * rigid shape type + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd; + +import java.util.Locale; +import java.util.ResourceBundle; + +/** + * 剛体の形状種別。 + *
    + *
  • 0x00:球 + *
  • 0x01:ç®± + *
  • 0x02:カプセル + *
+ */ +public enum RigidShapeType { + + /** 球。 */ + SPHERE(0x00), + /** 箱。 */ + BOX(0x01), + /** カプセル。 */ + CAPSULE(0x02), + ; + + private static final String FAMILY_NAME = + "jp.sourceforge.mikutoga.pmd.resources.RigidShapeTypeName"; + + private final byte encoded; + + /** + * コンストラクタ。 + * @param code 符号化int値 + */ + private RigidShapeType(int code){ + this((byte)code); + return; + } + + /** + * コンストラクタ。 + * @param code 符号化byte値 + */ + private RigidShapeType(byte code){ + this.encoded = code; + return; + } + + /** + * byte値からデコードする。 + * @param code byte値 + * @return デコードされた列挙子。該当するものがなければnull + */ + public static RigidShapeType decode(byte code){ + RigidShapeType result = null; + + for(RigidShapeType type : values()){ + if(type.encode() == code){ + result = type; + break; + } + } + + return result; + } + + /** + * byte値にエンコードする。 + * @return byte値 + */ + public byte encode(){ + return this.encoded; + } + + /** + * デフォルトロケールでの表示名を返す。 + * @return 表示名 + */ + public String getGuiName(){ + Locale locale = Locale.getDefault(); + return getGuiName(locale); + } + + /** + * ロケールに準じた表示名を返す。 + * @param locale ロケール。nullならデフォルトロケールと解釈される。 + * @return 表示名 + */ + public String getGuiName(Locale locale){ + if(locale == null) return getGuiName(); + ResourceBundle rb = ResourceBundle.getBundle(FAMILY_NAME, locale); + String key = name(); + String result = rb.getString(key); + return result; + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/ShadeInfo.java b/src/main/java/jp/sourceforge/mikutoga/pmd/ShadeInfo.java new file mode 100644 index 0000000..5970b0e --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/ShadeInfo.java @@ -0,0 +1,140 @@ +/* + * shading information + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd; + +/** + * シェーディング情報。 + */ +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/Surface.java b/src/main/java/jp/sourceforge/mikutoga/pmd/Surface.java new file mode 100644 index 0000000..d2c5560 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/Surface.java @@ -0,0 +1,175 @@ +/* + * triangle surface + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import jp.sourceforge.mikutoga.corelib.SerialNumbered; + +/** + * 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} + */ + 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} + */ + public void setSerialNumber(int num){ + this.serialNo = num; + return; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + 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/ToonMap.java b/src/main/java/jp/sourceforge/mikutoga/pmd/ToonMap.java new file mode 100644 index 0000000..153c0df --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/ToonMap.java @@ -0,0 +1,147 @@ +/* + * toon file mapping + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd; + +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/TripletRange.java b/src/main/java/jp/sourceforge/mikutoga/pmd/TripletRange.java new file mode 100644 index 0000000..6f61142 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/TripletRange.java @@ -0,0 +1,182 @@ +/* + * triplet-value range limitation + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd; + +/** + * XYZ三組float値の範囲制約。 + */ +public class TripletRange { + + private float xFrom; + private float xTo; + private float yFrom; + private float yTo; + private float zFrom; + private float zTo; + + /** + * コンストラクタ。 + */ + public TripletRange(){ + super(); + return; + } + + /** + * X値有効範囲を設定する。 + * 下限値が上限値より大きければ入れ替える。 + * @param xFrom X値下限 + * @param xTo X値上限 + */ + public void setXRange(float xFrom, float xTo){ + if(xFrom <= xTo){ + this.xFrom = xFrom; + this.xTo = xTo; + }else{ + this.xFrom = xTo; + this.xTo = xFrom; + } + return; + } + + /** + * Y値有効範囲を設定する。 + * 下限値が上限値より大きければ入れ替える。 + * @param yFrom Y値下限 + * @param yTo Y値上限 + */ + public void setYRange(float yFrom, float yTo){ + if(yFrom <= yTo){ + this.yFrom = yFrom; + this.yTo = yTo; + }else{ + this.yFrom = yTo; + this.yTo = yFrom; + } + return; + } + + /** + * Z値有効範囲を設定する。 + * 下限値が上限値より大きければ入れ替える。 + * @param zFrom Z値下限 + * @param zTo Z値上限 + */ + public void setZRange(float zFrom, float zTo){ + if(zFrom <= zTo){ + this.zFrom = zFrom; + this.zTo = zTo; + }else{ + this.zFrom = zTo; + this.zTo = zFrom; + } + return; + } + + /** + * X値下限を返す。 + * @return X値下限 + */ + public float getXFrom(){ + return this.xFrom; + } + + /** + * X値上限を返す。 + * @return X値上限 + */ + public float getXTo(){ + return this.xTo; + } + + /** + * Y値下限を返す。 + * @return Y値下限 + */ + public float getYFrom(){ + return this.yFrom; + } + + /** + * Y値上限を返す。 + * @return Y値上限 + */ + public float getYTo(){ + return this.yTo; + } + + /** + * Z値下限を返す。 + * @return Z値下限 + */ + public float getZFrom(){ + return this.zFrom; + } + + /** + * Z値上限を返す。 + * @return Z値上限 + */ + public float getZTo(){ + return this.zTo; + } + + /** + * X値が範囲制約を満たすか判定する。 + * @param xVal X値 + * @return 制約を満たすならtrue + */ + public boolean isValidX(float xVal){ + if(this.xFrom <= xVal && xVal <= this.xTo) return true; + return false; + } + + /** + * Y値が範囲制約を満たすか判定する。 + * @param yVal Y値 + * @return 制約を満たすならtrue + */ + public boolean isValidY(float yVal){ + if(this.yFrom <= yVal && yVal <= this.yTo) return true; + return false; + } + + /** + * Z値が範囲制約を満たすか判定する。 + * @param zVal Z値 + * @return 制約を満たすならtrue + */ + public boolean isValidZ(float zVal){ + if(this.zFrom <= zVal && zVal <= this.zTo) return true; + return false; + } + + @Override + public String toString(){ + StringBuilder result = new StringBuilder(); + + result.append("x=[") + .append(xFrom) + .append(" - ") + .append(xTo) + .append("] "); + result.append("y=[") + .append(yFrom) + .append(" - ") + .append(yTo) + .append("] "); + result.append("z=[") + .append(zFrom) + .append(" - ") + .append(zTo) + .append("]"); + + return result.toString(); + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/Vec3d.java b/src/main/java/jp/sourceforge/mikutoga/pmd/Vec3d.java new file mode 100644 index 0000000..ccbe2d3 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/Vec3d.java @@ -0,0 +1,94 @@ +/* + * 3D vector + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd; + +/** + * XYZ三次元ベクトル。 + */ +public class Vec3d { + + private float xVal; + private float yVal; + private float zVal; + + /** + * コンストラクタ。 + */ + public Vec3d(){ + super(); + return; + } + + /** + * X値を設定する。 + * @param xVal X値 + */ + public void setXVal(float xVal){ + this.xVal = xVal; + return; + } + + /** + * X値を返す。 + * @return X値 + */ + public float getXVal(){ + return this.xVal; + } + + /** + * Y値を設定する。 + * @param yVal Y値 + */ + public void setYVal(float yVal){ + this.yVal = yVal; + return; + } + + /** + * Y値を返す。 + * @return Y値 + */ + public float getYVal(){ + return this.yVal; + } + + /** + * Z値を設定する。 + * @param zVal Z値 + */ + public void setZVal(float zVal){ + this.zVal = zVal; + return; + } + + /** + * Z値を返す。 + * @return Z値 + */ + public float getZVal(){ + return this.zVal; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + @Override + public String toString(){ + StringBuilder result = new StringBuilder(); + + result.append("vec=[") + .append(this.xVal).append(", ") + .append(this.yVal).append(", ") + .append(this.zVal).append(']'); + + return result.toString(); + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/Vertex.java b/src/main/java/jp/sourceforge/mikutoga/pmd/Vertex.java new file mode 100644 index 0000000..119bd46 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/Vertex.java @@ -0,0 +1,217 @@ +/* + * vertex information + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd; + +import jp.sourceforge.mikutoga.corelib.SerialNumbered; + +/** + * 頂点情報。 + */ +public class Vertex implements SerialNumbered { + + private static final int MIN_WEIGHT = 0; + private static final int MAX_WEIGHT = 100; + + private final Pos3d position = new Pos3d(); + private final Vec3d normal = new Vec3d(); + + private final Pos2d uvPosition = new Pos2d(); + + 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 Pos3d getPosition(){ + return this.position; + } + + /** + * 法線ベクトルを返す。 + * @return 法線ベクトル + */ + public Vec3d getNormal(){ + return this.normal; + } + + /** + * UVマップ座標を返す。 + * @return UVマップ情報 + */ + public Pos2d 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} + */ + public void setSerialNumber(int num){ + this.serialNo = num; + return; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + 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/package-info.java b/src/main/java/jp/sourceforge/mikutoga/pmd/package-info.java new file mode 100644 index 0000000..4be99ed --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/package-info.java @@ -0,0 +1,14 @@ +/* + * package information for Javadoc + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +/** + * PMDモデルファイルに相当するオブジェクト各種。 + */ + +package jp.sourceforge.mikutoga.pmd; + +/* EOF */ diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/pmdexporter/AbstractExporter.java b/src/main/java/jp/sourceforge/mikutoga/pmd/pmdexporter/AbstractExporter.java new file mode 100644 index 0000000..45c463b --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/pmdexporter/AbstractExporter.java @@ -0,0 +1,224 @@ +/* + * abstract exporter + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.pmdexporter; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.BufferOverflowException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; +import java.nio.charset.CodingErrorAction; + +/** + * 抽象化されたエクスポーター共通部。 + */ +public abstract class AbstractExporter { + + private static final Charset CS_WIN31J = Charset.forName("windows-31j"); + + private static final String ERRMSG_TOOLONGTEXT = "too long text"; + private static final String ERRMSG_INVUCSSEQ = "invalid unicode sequence"; + private static final String ERRMSG_NONWIN31J = "no character in win31j"; + + private static final int BYTES_SHORT = Short .SIZE / Byte.SIZE; + private static final int BYTES_INT = Integer.SIZE / Byte.SIZE; + private static final int BYTES_FLOAT = Float .SIZE / Byte.SIZE; + + private static final int BUFSZ_BYTE = 512; + private static final int BUFSZ_CHAR = 512; + + private final OutputStream ostream; + + private final byte[] barray; + private final ByteBuffer bbuf; + + private final CharBuffer cbuf; + private final CharsetEncoder encoder; + + + /** + * コンストラクタ。 + * @param stream 出力ストリーム + * @throws NullPointerException 引数がnull + */ + protected AbstractExporter(OutputStream stream) + throws NullPointerException{ + super(); + + if(stream == null) throw new NullPointerException(); + this.ostream = stream; + + this.barray = new byte[BUFSZ_BYTE]; + this.bbuf = ByteBuffer.wrap(this.barray); + this.bbuf.order(ByteOrder.LITTLE_ENDIAN); + + this.cbuf = CharBuffer.allocate(BUFSZ_CHAR); + this.encoder = CS_WIN31J.newEncoder(); + this.encoder.onMalformedInput(CodingErrorAction.REPORT); + this.encoder.onUnmappableCharacter(CodingErrorAction.REPORT); + this.encoder.reset(); + + return; + } + + /** + * 出力をフラッシュする。 + * I/O効率とデバッグ効率のバランスを考え、ご利用は計画的に。 + * @throws IOException 出力エラー + */ + protected void flush() throws IOException{ + this.ostream.flush(); + return; + } + + /** + * byte値を出力する。 + * @param bVal byte値 + * @throws IOException 出力エラー + */ + protected void dumpByte(byte bVal) throws IOException{ + this.ostream.write((int)bVal); + return; + } + + /** + * int値をbyte値で出力する。 + * byte値に収まらない上位ビットはそのまま捨てられる。 + * @param iVal int値 + * @throws IOException 出力エラー + */ + protected void dumpByte(int iVal) throws IOException{ + dumpByte((byte)iVal); + return; + } + + /** + * short値を出力する。 + * @param sVal short値 + * @throws IOException 出力エラー + */ + protected void dumpShort(short sVal) throws IOException{ + this.bbuf.clear(); + this.bbuf.putShort(sVal); + this.ostream.write(this.barray, 0, BYTES_SHORT); + return; + } + + /** + * int値をshort値で出力する。 + * short値に収まらない上位ビットはそのまま捨てられる。 + * @param iVal int値 + * @throws IOException 出力エラー + */ + protected void dumpShort(int iVal) throws IOException{ + dumpShort((short)iVal); + return; + } + + /** + * int値を出力する。 + * @param iVal int値 + * @throws IOException 出力エラー + */ + protected void dumpInt(int iVal) throws IOException{ + this.bbuf.clear(); + this.bbuf.putInt(iVal); + this.ostream.write(this.barray, 0, BYTES_INT); + return; + } + + /** + * float値を出力する。 + * @param fVal float値 + * @throws IOException 出力エラー + */ + protected void dumpFloat(float fVal) throws IOException{ + this.bbuf.clear(); + this.bbuf.putFloat(fVal); + this.ostream.write(this.barray, 0, BYTES_FLOAT); + return; + } + + /** + * 文字列をwindows-31jエンコーディングで出力する。 + * @param seq 文字列 + * @return 出力されたbyte総数。 + * @throws IOException 出力エラー + * @throws IllegalPmdTextException 文字エンコーディングに関するエラー + */ + protected int dumpCharSequence(CharSequence seq) + throws IOException, IllegalPmdTextException{ + encodeToByteBuffer(seq); + this.bbuf.flip(); + int length = dumpByteBuffer(); + return length; + } + + /** + * windows-31jにエンコーディングした文字列を内部バッファに蓄積する。 + * @param seq 文字列 + * @throws IllegalPmdTextException 文字エンコーディングに関するエラー。 + * 考えられる状況としては、 + *
    + *
  • 入力文字列がUnicodeとしてすでにおかしい。 + * (サロゲートペアがペアになってないなど) + *
  • windows-31jにない文字をエンコーディングしようとした + *
  • エンコーディング結果が内部バッファに収まらなかった。 + *
+ * など。 + */ + private void encodeToByteBuffer(CharSequence seq) + throws IllegalPmdTextException{ + this.cbuf.clear(); + try{ + this.cbuf.append(seq); + }catch(BufferOverflowException e){ + throw new IllegalPmdTextException(ERRMSG_TOOLONGTEXT); + } + this.cbuf.flip(); + + this.bbuf.clear(); + + CoderResult result = this.encoder.encode(this.cbuf, this.bbuf, true); + if( ! result.isUnderflow()){ + if(result.isOverflow()){ + throw new IllegalPmdTextException(ERRMSG_TOOLONGTEXT); + }else if(result.isMalformed()){ + throw new IllegalPmdTextException(ERRMSG_INVUCSSEQ); + }else if(result.isUnmappable()){ + throw new IllegalPmdTextException(ERRMSG_NONWIN31J); + }else{ + assert false; + throw new AssertionError(); + } + } + + return; + } + + /** + * 内部バッファに蓄積されたbyte値並びを出力する。 + * @return 出力されたbyte数 + * @throws IOException 出力エラー + */ + private int dumpByteBuffer() throws IOException{ + int offset = this.bbuf.position(); + int limit = this.bbuf.limit(); + int length = limit - offset; + this.ostream.write(this.barray, offset, length); + + this.bbuf.position(limit); + + return length; + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/pmdexporter/IllegalPmdException.java b/src/main/java/jp/sourceforge/mikutoga/pmd/pmdexporter/IllegalPmdException.java new file mode 100644 index 0000000..3dfed28 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/pmdexporter/IllegalPmdException.java @@ -0,0 +1,33 @@ +/* + * illegal model exception + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.pmdexporter; + +/** + * モデルデータの不備を発見した場合の例外。 + */ +@SuppressWarnings("serial") +public class IllegalPmdException extends Exception{ + + /** + * コンストラクタ。 + */ + public IllegalPmdException(){ + super(); + return; + } + + /** + * コンストラクタ。 + * @param message メッセージ + */ + public IllegalPmdException(String message){ + super(message); + return; + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/pmdexporter/IllegalPmdTextException.java b/src/main/java/jp/sourceforge/mikutoga/pmd/pmdexporter/IllegalPmdTextException.java new file mode 100644 index 0000000..6ab1dbe --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/pmdexporter/IllegalPmdTextException.java @@ -0,0 +1,41 @@ +/* + * illegal text in model exception + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.pmdexporter; + +/** + * 不正なテキスト情報をモデルデータ中に発見した場合の例外。 + *

+ * 考えられる理由としては + *

    + *
  • 用意されたフォーマットに対し文字列が長すぎる。 + *
  • 文字エンコーディングできない文字が含まれている + *
  • ユニコード文字列として既に変。 + *
+ * など。 + */ +@SuppressWarnings("serial") +public class IllegalPmdTextException extends IllegalPmdException{ + + /** + * コンストラクタ。 + */ + public IllegalPmdTextException(){ + super(); + return; + } + + /** + * コンストラクタ。 + * @param message メッセージ + */ + public IllegalPmdTextException(String message){ + super(message); + return; + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/pmdexporter/PmdExporter.java b/src/main/java/jp/sourceforge/mikutoga/pmd/pmdexporter/PmdExporter.java new file mode 100644 index 0000000..7d11ed3 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/pmdexporter/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.pmdexporter; + +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/pmdexporter/PmdExporterBase.java b/src/main/java/jp/sourceforge/mikutoga/pmd/pmdexporter/PmdExporterBase.java new file mode 100644 index 0000000..d8866eb --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/pmdexporter/PmdExporterBase.java @@ -0,0 +1,671 @@ +/* + * model exporter for pmd-file + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.pmdexporter; + +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.corelib.SerialNumbered; +import jp.sourceforge.mikutoga.parser.pmd.PmdLimits; +import jp.sourceforge.mikutoga.pmd.BoneGroup; +import jp.sourceforge.mikutoga.pmd.BoneInfo; +import jp.sourceforge.mikutoga.pmd.BoneType; +import jp.sourceforge.mikutoga.pmd.IKChain; +import jp.sourceforge.mikutoga.pmd.Material; +import jp.sourceforge.mikutoga.pmd.MorphPart; +import jp.sourceforge.mikutoga.pmd.MorphType; +import jp.sourceforge.mikutoga.pmd.MorphVertex; +import jp.sourceforge.mikutoga.pmd.PmdModel; +import jp.sourceforge.mikutoga.pmd.Pos2d; +import jp.sourceforge.mikutoga.pmd.Pos3d; +import jp.sourceforge.mikutoga.pmd.ShadeInfo; +import jp.sourceforge.mikutoga.pmd.Surface; +import jp.sourceforge.mikutoga.pmd.Vec3d; +import jp.sourceforge.mikutoga.pmd.Vertex; + +/** + * PMDファイルのエクスポーター(拡張無し基本フォーマット)。 + *

+ * 英名対応以降のPMDファイルフォーマットを + * 使いたくない場合はこのエクスポーターを用いて出力せよ。 + */ +public class PmdExporterBase extends AbstractExporter{ + + /** 前(親)ボーンが無い場合の便宜的なボーン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 String MAGIC = "Pmd"; + + 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, IllegalPmdTextException{ + dumpText(text, maxByteLength, FDFILLER); + return; + } + + /** + * 文字列を指定されたバイト長で出力する。 + * 文字列の改行記号はLF(0x0a)に正規化される。 + * エンコード結果がバイト長に満たない場合は + * fillerがパディングされる。 + * @param text 文字列 + * @param maxByteLength バイト超指定 + * @param filler 出力結果が足りない場合の詰め物。 + * それでも足りない場合は最後のbyte要素が繰り返し出力される。 + * @throws IOException 出力エラー + * @throws IllegalPmdTextException エンコード結果が + * 指定バイト長をはみ出した + */ + protected void dumpText(String text, int maxByteLength, byte[] filler) + throws IOException, IllegalPmdTextException{ + String normalized = normalizeBreak(text); + int blen = dumpCharSequence(normalized); + int remain = maxByteLength - blen; + + if(remain < 0) throw new IllegalPmdTextException("too long text"); + + int fillerIdx = 0; + while(remain > 0){ + if(fillerIdx >= filler.length){ + fillerIdx = filler.length - 1; + } + dumpByte(filler[fillerIdx]); + fillerIdx++; + remain--; + } + + return; + } + + /** + * モデルデータをPMDファイル形式で出力する。 + * @param model モデルデータ + * @throws IOException 出力エラー + * @throws IllegalPmdException モデルデータに不備が発見された + */ + public void dumpPmdModel(PmdModel model) + throws IOException, IllegalPmdException{ + dumpBasic(model); + dumpVertexList(model); + dumpSurfaceList(model); + dumpMaterialList(model); + dumpBoneList(model); + dumpIKChainList(model); + dumpMorphList(model); + dumpMorphGroup(model); + dumpBoneGroupList(model); + + return; + } + + /** + * モデル基本情報を出力する。 + * @param model モデルデータ + * @throws IOException 出力エラー + * @throws IllegalPmdTextException モデル名もしくは説明が長すぎる + */ + private void dumpBasic(PmdModel model) + throws IOException, IllegalPmdTextException{ + dumpCharSequence(MAGIC); + float ver = model.getHeaderVersion(); + dumpFloat(ver); + + 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{ + Pos3d position = vertex.getPosition(); + dumpPos3d(position); + + Vec3d normal = vertex.getNormal(); + dumpVec3d(normal); + + Pos2d 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, IllegalPmdTextException{ + 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 IllegalPmdTextException ファイル名が長すぎる + */ + private void dumpShadeFileInfo(ShadeInfo shade) + throws IOException, IllegalPmdTextException{ + 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; + + dumpText(text.toString(), + PmdLimits.MAXBYTES_TEXTUREFILENAME, + filler ); + + return; + } + + /** + * ボーンリストを出力する。 + * @param model モデルデータ + * @throws IOException 出力エラー + * @throws IllegalPmdTextException ボーン名が長すぎる + */ + private void dumpBoneList(PmdModel model) + throws IOException, IllegalPmdTextException{ + 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, IllegalPmdTextException{ + 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); + } + + Pos3d 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, IllegalPmdTextException{ + 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 IllegalPmdTextException ボーングループ名が長すぎる + */ + private void dumpBoneGroupList(PmdModel model) + throws IOException, IllegalPmdTextException{ + List groupList = model.getBoneGroupList(); + int groupNum = groupList.size(); + dumpByte(groupNum - 1); + + int dispBoneNum = 0; + for(BoneGroup group : groupList){ + if(group.isDefaultBoneGroup()) continue; + dumpText(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(Pos2d position) throws IOException{ + float xPos = position.getXPos(); + float yPos = position.getYPos(); + + dumpFloat(xPos); + dumpFloat(yPos); + + return; + } + + /** + * 3次元位置情報を出力する。 + * @param position 3次元位置情報 + * @throws IOException 出力エラー + */ + protected void dumpPos3d(Pos3d position) throws IOException{ + float xPos = position.getXPos(); + float yPos = position.getYPos(); + float zPos = position.getZPos(); + + dumpFloat(xPos); + dumpFloat(yPos); + dumpFloat(zPos); + + return; + } + + /** + * 3次元ベクトル情報を出力する。 + * @param vector 3次元ベクトル + * @throws IOException 出力エラー + */ + protected void dumpVec3d(Vec3d vector) throws IOException{ + float xVal = vector.getXVal(); + float yVal = vector.getYVal(); + float zVal = vector.getZVal(); + + dumpFloat(xVal); + dumpFloat(yVal); + dumpFloat(zVal); + + return; + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/pmdexporter/PmdExporterExt1.java b/src/main/java/jp/sourceforge/mikutoga/pmd/pmdexporter/PmdExporterExt1.java new file mode 100644 index 0000000..73b1919 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/pmdexporter/PmdExporterExt1.java @@ -0,0 +1,160 @@ +/* + * model exporter for pmd-file(Ext1) + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.pmdexporter; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; +import java.util.Map; +import jp.sourceforge.mikutoga.parser.pmd.PmdLimits; +import jp.sourceforge.mikutoga.pmd.BoneGroup; +import jp.sourceforge.mikutoga.pmd.BoneInfo; +import jp.sourceforge.mikutoga.pmd.MorphPart; +import jp.sourceforge.mikutoga.pmd.MorphType; +import jp.sourceforge.mikutoga.pmd.PmdModel; + +/** + * 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 IllegalPmdException {@inheritDoc} + */ + @Override + public void dumpPmdModel(PmdModel model) + throws IOException, IllegalPmdException{ + super.dumpPmdModel(model); + + dumpGlobalInfo(model); + + return; + } + + /** + * 英語名情報を出力する。 + * @param model モデルデータ + * @throws IOException 出力エラー + * @throws IllegalPmdTextException 文字列が長すぎる。 + */ + private void dumpGlobalInfo(PmdModel model) + throws IOException, IllegalPmdTextException{ + boolean hasGlobal = model.hasGlobalText(); + byte globalFlag; + if(hasGlobal) globalFlag = 0x01; + else globalFlag = 0x00; + dumpByte(globalFlag); + + if(hasGlobal){ + dumpBasicGlobal(model); + dumpBoneGlobal(model); + dumpMorphGlobal(model); + dumpBoneGroupGlobal(model); + } + + flush(); + + return; + } + + /** + * モデル基本情報を英語で出力する。 + * @param model モデルデータ + * @throws IOException 出力エラー + * @throws IllegalPmdTextException 文字列が長すぎる。 + */ + private void dumpBasicGlobal(PmdModel model) + throws IOException, IllegalPmdTextException{ + 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, IllegalPmdTextException{ + 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, IllegalPmdTextException{ + 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, IllegalPmdTextException{ + 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/pmdexporter/PmdExporterExt2.java b/src/main/java/jp/sourceforge/mikutoga/pmd/pmdexporter/PmdExporterExt2.java new file mode 100644 index 0000000..fcd3b4f --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/pmdexporter/PmdExporterExt2.java @@ -0,0 +1,72 @@ +/* + * model exporter for pmd-file(Ext2) + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.pmdexporter; + +import java.io.IOException; +import java.io.OutputStream; +import jp.sourceforge.mikutoga.parser.pmd.PmdLimits; +import jp.sourceforge.mikutoga.pmd.PmdModel; +import jp.sourceforge.mikutoga.pmd.ToonMap; + +/** + * 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 IllegalPmdException {@inheritDoc} + */ + @Override + public void dumpPmdModel(PmdModel model) + throws IOException, IllegalPmdException{ + super.dumpPmdModel(model); + + dumpToonMap(model); + + return; + } + + /** + * 独自トゥーンファイルテーブルを出力する。 + * @param model モデルデータ + * @throws IOException 出力エラー + * @throws IllegalPmdTextException トゥーンファイル名が長すぎる + */ + private void dumpToonMap(PmdModel model) + throws IOException, IllegalPmdTextException{ + 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/pmdexporter/PmdExporterExt3.java b/src/main/java/jp/sourceforge/mikutoga/pmd/pmdexporter/PmdExporterExt3.java new file mode 100644 index 0000000..142c5ac --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/pmdexporter/PmdExporterExt3.java @@ -0,0 +1,273 @@ +/* + * model exporter for pmd-file(Ext3) + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.pmdexporter; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; +import jp.sourceforge.mikutoga.parser.pmd.PmdLimits; +import jp.sourceforge.mikutoga.pmd.BoneInfo; +import jp.sourceforge.mikutoga.pmd.Deg3d; +import jp.sourceforge.mikutoga.pmd.DynamicsInfo; +import jp.sourceforge.mikutoga.pmd.JointInfo; +import jp.sourceforge.mikutoga.pmd.PmdModel; +import jp.sourceforge.mikutoga.pmd.Rad3d; +import jp.sourceforge.mikutoga.pmd.RigidGroup; +import jp.sourceforge.mikutoga.pmd.RigidInfo; +import jp.sourceforge.mikutoga.pmd.RigidShape; +import jp.sourceforge.mikutoga.pmd.RigidShapeType; +import jp.sourceforge.mikutoga.pmd.TripletRange; + +/** + * 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 IllegalPmdException {@inheritDoc} + */ + @Override + public void dumpPmdModel(PmdModel model) + throws IOException, IllegalPmdException{ + super.dumpPmdModel(model); + + dumpRigidList(model); + dumpJointList(model); + + return; + } + + /** + * 剛体リストを出力する。 + * @param model モデルデータ + * @throws IOException 出力エラー + * @throws IllegalPmdTextException 長すぎる剛体名 + */ + private void dumpRigidList(PmdModel model) + throws IOException, IllegalPmdTextException{ + 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, IllegalPmdTextException{ + String rigidName = rigid.getRigidName().getPrimaryText(); + dumpText(rigidName, PmdLimits.MAXBYTES_RIGIDNAME); + + BoneInfo linkedBone = rigid.getLinkedBone(); + 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, IllegalPmdTextException{ + 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, IllegalPmdTextException{ + 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/pmdexporter/package-info.java b/src/main/java/jp/sourceforge/mikutoga/pmd/pmdexporter/package-info.java new file mode 100644 index 0000000..56f44ae --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/pmdexporter/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.pmdexporter; + +/* EOF */ diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/pmdloader/BoneBuilder.java b/src/main/java/jp/sourceforge/mikutoga/pmd/pmdloader/BoneBuilder.java new file mode 100644 index 0000000..9213010 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/pmdloader/BoneBuilder.java @@ -0,0 +1,286 @@ +/* + * building bone information + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.pmdloader; + +import jp.sourceforge.mikutoga.corelib.ListUtil; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import jp.sourceforge.mikutoga.parser.ParseStage; +import jp.sourceforge.mikutoga.parser.pmd.PmdBoneHandler; +import jp.sourceforge.mikutoga.parser.pmd.PmdLimits; +import jp.sourceforge.mikutoga.pmd.BoneGroup; +import jp.sourceforge.mikutoga.pmd.BoneInfo; +import jp.sourceforge.mikutoga.pmd.BoneType; +import jp.sourceforge.mikutoga.pmd.IKChain; +import jp.sourceforge.mikutoga.pmd.PmdModel; +import jp.sourceforge.mikutoga.pmd.Pos3d; + +/** + * ボーン関係の通知をパーサから受け取る。 + */ +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} + */ + public void loopStart(ParseStage stage, int loops){ + assert stage instanceof PmdBoneStage; + + 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} + */ + public void loopNext(ParseStage stage){ + assert stage instanceof PmdBoneStage; + + 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} + */ + public void loopEnd(ParseStage stage){ + assert stage instanceof PmdBoneStage; + + 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} + */ + 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} + */ + 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} + */ + public void pmdBonePosition(float xPos, float yPos, float zPos){ + Pos3d 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} + */ + 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} + */ + public void pmdIKChainInfo(int childId){ + BoneInfo chain = this.boneList.get(childId); + this.currentIkChain.getChainedBoneList().add(chain); + return; + } + + /** + * {@inheritDoc} + * @param groupName {@inheritDoc} + */ + public void pmdBoneGroupInfo(String groupName){ + this.currentBoneGroup.getGroupName().setPrimaryText(groupName); + return; + } + + /** + * {@inheritDoc} + * @param boneId {@inheritDoc} + * @param groupId {@inheritDoc} + */ + 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/pmdloader/JointBuilder.java b/src/main/java/jp/sourceforge/mikutoga/pmd/pmdloader/JointBuilder.java new file mode 100644 index 0000000..7ea24a6 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/pmdloader/JointBuilder.java @@ -0,0 +1,205 @@ +/* + * building joint information + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.pmdloader; + +import jp.sourceforge.mikutoga.corelib.ListUtil; +import java.util.Iterator; +import java.util.List; +import jp.sourceforge.mikutoga.parser.ParseStage; +import jp.sourceforge.mikutoga.parser.pmd.PmdJointHandler; +import jp.sourceforge.mikutoga.pmd.Deg3d; +import jp.sourceforge.mikutoga.pmd.JointInfo; +import jp.sourceforge.mikutoga.pmd.PmdModel; +import jp.sourceforge.mikutoga.pmd.Pos3d; +import jp.sourceforge.mikutoga.pmd.Rad3d; +import jp.sourceforge.mikutoga.pmd.RigidInfo; +import jp.sourceforge.mikutoga.pmd.TripletRange; + +/** + * ジョイント関係の通知をパーサから受け取る。 + */ +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} + */ + 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} + */ + public void loopNext(ParseStage stage){ + assert stage == PmdJointHandler.JOINT_LIST; + + if(this.jointIt.hasNext()){ + this.currentJoint = this.jointIt.next(); + } + + return; + } + + /** + * {@inheritDoc} + * @param stage {@inheritDoc} + */ + public void loopEnd(ParseStage stage){ + assert stage == PmdJointHandler.JOINT_LIST; + return; + } + + /** + * {@inheritDoc} + * @param jointName {@inheritDoc} + */ + public void pmdJointName(String jointName){ + this.currentJoint.getJointName().setPrimaryText(jointName); + return; + } + + /** + * {@inheritDoc} + * @param rigidIdA {@inheritDoc} + * @param rigidIdB {@inheritDoc} + */ + 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} + */ + public void pmdJointPosition(float posX, float posY, float posZ){ + Pos3d position = this.currentJoint.getPosition(); + position.setXPos(posX); + position.setYPos(posY); + position.setZPos(posZ); + return; + } + + /** + * {@inheritDoc} + * @param rotX {@inheritDoc} + * @param rotY {@inheritDoc} + * @param rotZ {@inheritDoc} + */ + 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} + */ + 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} + */ + 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} + */ + public void pmdElasticPosition(float elasticPosX, + float elasticPosY, + float elasticPosZ){ + Pos3d position = this.currentJoint.getElasticPosition(); + position.setXPos(elasticPosX); + position.setYPos(elasticPosY); + position.setZPos(elasticPosZ); + return; + } + + /** + * {@inheritDoc} + * @param elasticRotX {@inheritDoc} + * @param elasticRotY {@inheritDoc} + * @param elasticRotZ {@inheritDoc} + */ + 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/pmdloader/MaterialBuilder.java b/src/main/java/jp/sourceforge/mikutoga/pmd/pmdloader/MaterialBuilder.java new file mode 100644 index 0000000..9ac8294 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/pmdloader/MaterialBuilder.java @@ -0,0 +1,180 @@ +/* + * building material information + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.pmdloader; + +import jp.sourceforge.mikutoga.corelib.ListUtil; +import java.awt.Color; +import java.util.Iterator; +import java.util.List; +import jp.sourceforge.mikutoga.parser.ParseStage; +import jp.sourceforge.mikutoga.parser.pmd.PmdMaterialHandler; +import jp.sourceforge.mikutoga.pmd.Material; +import jp.sourceforge.mikutoga.pmd.PmdModel; +import jp.sourceforge.mikutoga.pmd.ShadeInfo; +import jp.sourceforge.mikutoga.pmd.Surface; +import jp.sourceforge.mikutoga.pmd.ToonMap; + +/** + * マテリアル素材関連の通知をパーサから受け取る。 + */ +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} + */ + 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} + */ + public void loopNext(ParseStage stage){ + assert stage == PmdMaterialHandler.MATERIAL_LIST; + + if(this.materialIt.hasNext()){ + this.currentMaterial = this.materialIt.next(); + } + + return; + } + + /** + * {@inheritDoc} + * @param stage {@inheritDoc} + */ + public void loopEnd(ParseStage stage){ + assert stage == PmdMaterialHandler.MATERIAL_LIST; + return; + } + + /** + * {@inheritDoc} + * @param red {@inheritDoc} + * @param green {@inheritDoc} + * @param blue {@inheritDoc} + * @param alpha {@inheritDoc} + */ + 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} + */ + 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} + */ + 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} + */ + 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} + */ + 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/pmdloader/MorphBuilder.java b/src/main/java/jp/sourceforge/mikutoga/pmd/pmdloader/MorphBuilder.java new file mode 100644 index 0000000..4727439 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/pmdloader/MorphBuilder.java @@ -0,0 +1,184 @@ +/* + * building morph information + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.pmdloader; + +import jp.sourceforge.mikutoga.corelib.ListUtil; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import jp.sourceforge.mikutoga.parser.ParseStage; +import jp.sourceforge.mikutoga.parser.pmd.PmdMorphHandler; +import jp.sourceforge.mikutoga.pmd.MorphPart; +import jp.sourceforge.mikutoga.pmd.MorphType; +import jp.sourceforge.mikutoga.pmd.MorphVertex; +import jp.sourceforge.mikutoga.pmd.PmdModel; +import jp.sourceforge.mikutoga.pmd.Pos3d; +import jp.sourceforge.mikutoga.pmd.Vertex; + +/** + * モーフ関係の通知をパーサから受け取る。 + */ +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} + */ + public void loopStart(ParseStage stage, int loops){ + assert stage instanceof PmdMorphStage; + + 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 + } + + return; + } + + /** + * {@inheritDoc} + * @param stage {@inheritDoc} + */ + public void loopNext(ParseStage stage){ + assert stage instanceof PmdMorphStage; + + 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 + } + + return; + } + + /** + * {@inheritDoc} + * @param stage {@inheritDoc} + */ + public void loopEnd(ParseStage stage){ + assert stage instanceof PmdMorphStage; + if(stage == PmdMorphHandler.MORPH_LIST){ + // NOTHING + }else if(stage == PmdMorphHandler.MORPHVERTEX_LIST){ + // NOTHING + }else if(stage == PmdMorphHandler.MORPHORDER_LIST){ + // NOTHING + } + return; + } + + /** + * {@inheritDoc} + * @param morphName {@inheritDoc} + * @param morphType {@inheritDoc} + */ + 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} + */ + public void pmdMorphVertexInfo(int serialId, + float xPos, float yPos, float zPos){ + MorphVertex morphVertex; + morphVertex = new MorphVertex(); + Pos3d 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} + */ + 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/pmdloader/PmdLoader.java b/src/main/java/jp/sourceforge/mikutoga/pmd/pmdloader/PmdLoader.java new file mode 100644 index 0000000..0b0c87b --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/pmdloader/PmdLoader.java @@ -0,0 +1,117 @@ +/* + * PMD file loader + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.pmdloader; + +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.parser.pmd.PmdParser; +import jp.sourceforge.mikutoga.pmd.MorphPart; +import jp.sourceforge.mikutoga.pmd.PmdModel; + +/** + * 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/pmdloader/RigidBuilder.java b/src/main/java/jp/sourceforge/mikutoga/pmd/pmdloader/RigidBuilder.java new file mode 100644 index 0000000..45a5d79 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/pmdloader/RigidBuilder.java @@ -0,0 +1,212 @@ +/* + * building rigid information + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.pmdloader; + +import jp.sourceforge.mikutoga.corelib.ListUtil; +import java.util.Iterator; +import java.util.List; +import jp.sourceforge.mikutoga.parser.ParseStage; +import jp.sourceforge.mikutoga.parser.pmd.PmdLimits; +import jp.sourceforge.mikutoga.parser.pmd.PmdRigidHandler; +import jp.sourceforge.mikutoga.pmd.BoneInfo; +import jp.sourceforge.mikutoga.pmd.DynamicsInfo; +import jp.sourceforge.mikutoga.pmd.PmdModel; +import jp.sourceforge.mikutoga.pmd.Pos3d; +import jp.sourceforge.mikutoga.pmd.Rad3d; +import jp.sourceforge.mikutoga.pmd.RigidBehaviorType; +import jp.sourceforge.mikutoga.pmd.RigidGroup; +import jp.sourceforge.mikutoga.pmd.RigidInfo; +import jp.sourceforge.mikutoga.pmd.RigidShape; +import jp.sourceforge.mikutoga.pmd.RigidShapeType; + +/** + * 剛体関係の通知をパーサから受け取る。 + */ +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} + */ + 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} + */ + public void loopNext(ParseStage stage){ + if(this.rigidIt.hasNext()){ + this.currentRigid = this.rigidIt.next(); + } + return; + } + + /** + * {@inheritDoc} + * @param stage {@inheritDoc} + */ + public void loopEnd(ParseStage stage){ + return; + } + + /** + * {@inheritDoc} + * @param rigidName {@inheritDoc} + */ + public void pmdRigidName(String rigidName){ + this.currentRigid.getRigidName().setPrimaryText(rigidName); + return; + } + + /** + * {@inheritDoc} + * @param rigidGroupId {@inheritDoc} + * @param linkedBoneId {@inheritDoc} + */ + public void pmdRigidInfo(int rigidGroupId, int linkedBoneId){ + BoneInfo 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} + */ + 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} + */ + public void pmdRigidPosition(float posX, float posY, float posZ){ + Pos3d position = this.currentRigid.getPosition(); + position.setXPos(posX); + position.setYPos(posY); + position.setZPos(posZ); + return; + } + + /** + * {@inheritDoc} + * @param radX {@inheritDoc} + * @param radY {@inheritDoc} + * @param radZ {@inheritDoc} + */ + 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} + */ + 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} + */ + 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/pmdloader/ShapeBuilder.java b/src/main/java/jp/sourceforge/mikutoga/pmd/pmdloader/ShapeBuilder.java new file mode 100644 index 0000000..669ef9c --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/pmdloader/ShapeBuilder.java @@ -0,0 +1,215 @@ +/* + * building shape information + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.pmdloader; + +import jp.sourceforge.mikutoga.corelib.ListUtil; +import java.util.Iterator; +import java.util.List; +import java.util.RandomAccess; +import jp.sourceforge.mikutoga.parser.ParseStage; +import jp.sourceforge.mikutoga.parser.pmd.PmdShapeHandler; +import jp.sourceforge.mikutoga.pmd.BoneInfo; +import jp.sourceforge.mikutoga.pmd.PmdModel; +import jp.sourceforge.mikutoga.pmd.Pos2d; +import jp.sourceforge.mikutoga.pmd.Pos3d; +import jp.sourceforge.mikutoga.pmd.Surface; +import jp.sourceforge.mikutoga.pmd.Vec3d; +import jp.sourceforge.mikutoga.pmd.Vertex; + +/** + * モデル形状に関する通知をパーサから受け取る。 + */ +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.extendCollection(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} + */ + 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} + */ + 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} + */ + public void loopEnd(ParseStage stage){ + return; + } + + /** + * {@inheritDoc} + * @param xPos {@inheritDoc} + * @param yPos {@inheritDoc} + * @param zPos {@inheritDoc} + */ + public void pmdVertexPosition(float xPos, float yPos, float zPos){ + Pos3d position = this.currentVertex.getPosition(); + position.setXPos(xPos); + position.setYPos(yPos); + position.setZPos(zPos); + return; + } + + /** + * {@inheritDoc} + * @param xVec {@inheritDoc} + * @param yVec {@inheritDoc} + * @param zVec {@inheritDoc} + */ + public void pmdVertexNormal(float xVec, float yVec, float zVec){ + Vec3d normal = this.currentVertex.getNormal(); + normal.setXVal(xVec); + normal.setYVal(yVec); + normal.setZVal(zVec); + return; + } + + /** + * {@inheritDoc} + * @param uVal {@inheritDoc} + * @param vVal {@inheritDoc} + */ + public void pmdVertexUV(float uVal, float vVal){ + Pos2d uv = this.currentVertex.getUVPosition(); + uv.setXPos(uVal); + uv.setYPos(vVal); + return; + } + + /** + * {@inheritDoc} + * @param boneId1 {@inheritDoc} + * @param boneId2 {@inheritDoc} + * @param weightForB1 {@inheritDoc} + */ + 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} + */ + public void pmdVertexEdge(boolean hideEdge){ + this.currentVertex.setEdgeAppearance( ! hideEdge ); + return; + } + + /** + * {@inheritDoc} + * @param vertexId1 {@inheritDoc} + * @param vertexId2 {@inheritDoc} + * @param vertexId3 {@inheritDoc} + */ + 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/pmdloader/TextBuilder.java b/src/main/java/jp/sourceforge/mikutoga/pmd/pmdloader/TextBuilder.java new file mode 100644 index 0000000..18b75e6 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/pmdloader/TextBuilder.java @@ -0,0 +1,240 @@ +/* + * building text info + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.pmdloader; + +import java.util.Iterator; +import java.util.List; +import jp.sourceforge.mikutoga.corelib.I18nText; +import jp.sourceforge.mikutoga.parser.ParseStage; +import jp.sourceforge.mikutoga.parser.pmd.PmdBasicHandler; +import jp.sourceforge.mikutoga.parser.pmd.PmdEngHandler; +import jp.sourceforge.mikutoga.pmd.BoneGroup; +import jp.sourceforge.mikutoga.pmd.BoneInfo; +import jp.sourceforge.mikutoga.pmd.MorphPart; +import jp.sourceforge.mikutoga.pmd.PmdModel; + +/** + * テキスト関係の通知をパーサから受け取る。 + */ +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} + */ + public void pmdParseStart(){ + return; + } + + /** + * {@inheritDoc} + * @param hasMoreData {@inheritDoc} + */ + public void pmdParseEnd(boolean hasMoreData){ + this.hasMoreData = hasMoreData; + return; + } + + /** + * {@inheritDoc} + * @param stage {@inheritDoc} + * @param loops {@inheritDoc} + */ + public void loopStart(ParseStage stage, int loops){ + assert stage instanceof PmdEngStage; + + 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} + */ + public void loopNext(ParseStage stage){ + assert stage instanceof PmdEngStage; + + 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} + */ + public void loopEnd(ParseStage stage){ + assert stage instanceof PmdEngStage; + return; + } + + /** + * {@inheritDoc} + * @param ver {@inheritDoc} + */ + public void pmdHeaderInfo(float ver){ + this.model.setHeaderVersion(ver); + return; + } + + /** + * {@inheritDoc} + * @param modelName {@inheritDoc} + * @param description {@inheritDoc} + */ + public void pmdModelInfo(String modelName, String description){ + this.modelName .setPrimaryText(modelName); + this.description.setPrimaryText(description); + return; + } + + /** + * {@inheritDoc} + * @param hasEnglishInfo {@inheritDoc} + */ + public void pmdEngEnabled(boolean hasEnglishInfo){ + return; + } + + /** + * {@inheritDoc} + * @param modelName {@inheritDoc} + * @param description {@inheritDoc} + */ + public void pmdEngModelInfo(String modelName, String description){ + this.modelName .setGlobalText(modelName); + this.description.setGlobalText(description); + return; + } + + /** + * {@inheritDoc} + * @param boneName {@inheritDoc} + */ + public void pmdEngBoneInfo(String boneName){ + this.currentBone.getBoneName().setGlobalText(boneName); + return; + } + + /** + * {@inheritDoc} + * @param morphName {@inheritDoc} + */ + public void pmdEngMorphInfo(String morphName){ + this.currentMorphPart.getMorphName().setGlobalText(morphName); + return; + } + + /** + * {@inheritDoc} + * @param groupName {@inheritDoc} + */ + 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/pmdloader/ToonBuilder.java b/src/main/java/jp/sourceforge/mikutoga/pmd/pmdloader/ToonBuilder.java new file mode 100644 index 0000000..bc92e23 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/pmdloader/ToonBuilder.java @@ -0,0 +1,79 @@ +/* + * building toon information + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.pmdloader; + +import jp.sourceforge.mikutoga.parser.ParseStage; +import jp.sourceforge.mikutoga.parser.pmd.PmdLimits; +import jp.sourceforge.mikutoga.parser.pmd.PmdToonHandler; +import jp.sourceforge.mikutoga.pmd.PmdModel; +import jp.sourceforge.mikutoga.pmd.ToonMap; + +/** + * トゥーン関係の通知をパーサから受け取る。 + */ +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} + */ + 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} + */ + public void loopNext(ParseStage stage){ + assert stage == PmdToonHandler.TOON_LIST; + this.index++; + return; + } + + /** + * {@inheritDoc} + * @param stage {@inheritDoc} + */ + public void loopEnd(ParseStage stage){ + assert stage == PmdToonHandler.TOON_LIST; + this.model.setToonMap(this.toonMap); + return; + } + + /** + * {@inheritDoc} + * @param toonFileName {@inheritDoc} + */ + public void pmdToonFileInfo(String toonFileName){ + this.toonMap.setIndexedToon(this.index, toonFileName); + return; + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/pmdloader/package-info.java b/src/main/java/jp/sourceforge/mikutoga/pmd/pmdloader/package-info.java new file mode 100644 index 0000000..c1f0ca6 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/pmdloader/package-info.java @@ -0,0 +1,15 @@ +/* + * package information for Javadoc + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +/** + * PMDモデルファイルをパースし、 + * 内容を反映したオブジェクト群を構築するためのライブラリ。 + */ + +package jp.sourceforge.mikutoga.pmd.pmdloader; + +/* EOF */ diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/xml/PmdXmlExporter.java b/src/main/java/jp/sourceforge/mikutoga/pmd/xml/PmdXmlExporter.java new file mode 100644 index 0000000..73e25f9 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/xml/PmdXmlExporter.java @@ -0,0 +1,1275 @@ +/* + * pmd-xml exporter + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.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.corelib.SerialNumbered; +import jp.sourceforge.mikutoga.pmd.BoneGroup; +import jp.sourceforge.mikutoga.pmd.BoneInfo; +import jp.sourceforge.mikutoga.pmd.BoneType; +import jp.sourceforge.mikutoga.pmd.Deg3d; +import jp.sourceforge.mikutoga.pmd.DynamicsInfo; +import jp.sourceforge.mikutoga.pmd.IKChain; +import jp.sourceforge.mikutoga.pmd.JointInfo; +import jp.sourceforge.mikutoga.pmd.Material; +import jp.sourceforge.mikutoga.pmd.MorphPart; +import jp.sourceforge.mikutoga.pmd.MorphType; +import jp.sourceforge.mikutoga.pmd.MorphVertex; +import jp.sourceforge.mikutoga.pmd.PmdModel; +import jp.sourceforge.mikutoga.pmd.Pos2d; +import jp.sourceforge.mikutoga.pmd.Pos3d; +import jp.sourceforge.mikutoga.pmd.Rad3d; +import jp.sourceforge.mikutoga.pmd.RigidGroup; +import jp.sourceforge.mikutoga.pmd.RigidInfo; +import jp.sourceforge.mikutoga.pmd.RigidShape; +import jp.sourceforge.mikutoga.pmd.RigidShapeType; +import jp.sourceforge.mikutoga.pmd.ShadeInfo; +import jp.sourceforge.mikutoga.pmd.Surface; +import jp.sourceforge.mikutoga.pmd.ToonMap; +import jp.sourceforge.mikutoga.pmd.TripletRange; +import jp.sourceforge.mikutoga.pmd.Vec3d; +import jp.sourceforge.mikutoga.pmd.Vertex; +import jp.sourceforge.mikutoga.xml.BasicXmlExporter; +import jp.sourceforge.mikutoga.xml.XmlResourceResolver; + +/** + * XML形式でPMDモデルデータを出力する。 + */ +public class PmdXmlExporter extends BasicXmlExporter{ + + private static final String GENERATOR = "Mikutoga" + " Ver 0.0.1"; + 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"; + + /** + * コンストラクタ。 + * 文字エンコーディングはUTF-8が用いられる。 + * @param stream 出力ストリーム + */ + public PmdXmlExporter(OutputStream stream){ + super(stream); + 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; + put(' ').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.getText(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(Pos3d 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.getText(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).put(' '); + } + + putAttr("showEdge", bool); + put(" "); + 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){ + put(' ').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(); + + Pos3d position = vertex.getPosition(); + ind().putPosition(position).ln(); + + Vec3d normal = vertex.getNormal(); + ind().put("").ln(); + + Pos2d 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); + + Pos3d 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){ + put(' '); + 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){ + Pos3d 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); + + ind().put(" "); + putLineComment("Ref:" + linkedBone.getBoneName().getText()); + ln(2); + + RigidShape shape = rigid.getRigidShape(); + putRigidShape(shape); + + Pos3d 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(""); + put(' '); + 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); + + Pos3d 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); + + Pos3d 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/xml/PmdXmlResources.java b/src/main/java/jp/sourceforge/mikutoga/pmd/xml/PmdXmlResources.java new file mode 100644 index 0000000..4e7b58e --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/xml/PmdXmlResources.java @@ -0,0 +1,95 @@ +/* + * xml resources for PMD-XML + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.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.SAXException; + +/** + * + */ +public final class PmdXmlResources { + + public static final String NS_PMDXML = + "http://mikutoga.sourceforge.jp/xml/ns/pmdxml/100923"; + public static final String SCHEMA_PMDXML = + "http://mikutoga.sourceforge.jp/xml/xsd/pmdxml-100923.xsd"; + public static final String DTD_PMDXML = + "http://mikutoga.sourceforge.jp/xml/dtd/pmdxml-100923.dtd"; + public static final String VER_PMDXML = + "100923"; + public static final String LOCAL_SCHEMA_PMDXML = + "./resources/pmdxml-100923.xsd"; + public static final String LOCAL_DTD_PMDXML = + "./resources/pmdxml-100923.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; + } + + public static DocumentBuilder newBuilder() + 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); + 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); + + return builder; + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/pmd/xml/Xml2PmdLoader.java b/src/main/java/jp/sourceforge/mikutoga/pmd/xml/Xml2PmdLoader.java new file mode 100644 index 0000000..ae48ea7 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/xml/Xml2PmdLoader.java @@ -0,0 +1,940 @@ +/* + * xml loader + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.pmd.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.corelib.ListUtil; +import jp.sourceforge.mikutoga.pmd.BoneGroup; +import jp.sourceforge.mikutoga.pmd.BoneInfo; +import jp.sourceforge.mikutoga.pmd.BoneType; +import jp.sourceforge.mikutoga.pmd.Deg3d; +import jp.sourceforge.mikutoga.pmd.DynamicsInfo; +import jp.sourceforge.mikutoga.pmd.IKChain; +import jp.sourceforge.mikutoga.pmd.JointInfo; +import jp.sourceforge.mikutoga.pmd.Material; +import jp.sourceforge.mikutoga.pmd.MorphPart; +import jp.sourceforge.mikutoga.pmd.MorphType; +import jp.sourceforge.mikutoga.pmd.MorphVertex; +import jp.sourceforge.mikutoga.pmd.PmdModel; +import jp.sourceforge.mikutoga.pmd.Pos2d; +import jp.sourceforge.mikutoga.pmd.Pos3d; +import jp.sourceforge.mikutoga.pmd.Rad3d; +import jp.sourceforge.mikutoga.pmd.RigidBehaviorType; +import jp.sourceforge.mikutoga.pmd.RigidGroup; +import jp.sourceforge.mikutoga.pmd.RigidInfo; +import jp.sourceforge.mikutoga.pmd.RigidShape; +import jp.sourceforge.mikutoga.pmd.RigidShapeType; +import jp.sourceforge.mikutoga.pmd.ShadeInfo; +import jp.sourceforge.mikutoga.pmd.Surface; +import jp.sourceforge.mikutoga.pmd.ToonMap; +import jp.sourceforge.mikutoga.pmd.TripletRange; +import jp.sourceforge.mikutoga.pmd.Vec3d; +import jp.sourceforge.mikutoga.pmd.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 文字列 + * @throw TogaXmlException 属性値が見つからなかった。 + */ + private static String getStringAttr(Element elem, String attrName) + throws TogaXmlException{ + return DomUtils.getStringAttr(elem, attrName); + } + + /** + * 要素からxsd:boolean型属性値を読み取る。 + * @param elem 要素 + * @param attrName 属性名 + * @return 真ならtrue + * @throw TogaXmlException 属性値が見つからなかった。 + */ + private static boolean getBooleanAttr(Element elem, String attrName) + throws TogaXmlException{ + return DomUtils.getBooleanAttr(elem, attrName); + } + + /** + * 要素からxsd:integer型属性値を読み取る。 + * @param elem 要素 + * @param attrName 属性名 + * @return int値 + * @throw TogaXmlException 属性値が見つからなかった。 + */ + private static int getIntegerAttr(Element elem, String attrName) + throws TogaXmlException{ + return DomUtils.getIntegerAttr(elem, attrName); + } + + /** + * 要素からxsd:float型属性値を読み取る。 + * @param elem 要素 + * @param attrName 属性名 + * @return float値 + * @throw 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 ファイル名 + * @throw TogaXmlException 属性値が見つからなかった。 + */ + private static String getSjisFileNameAttr(Element elem, String attrName) + throws TogaXmlException{ + return DomUtils.getSjisFileNameAttr(elem, attrName); + } + + /** + * 指定された名前の子要素を1つだけ返す。 + * @param parent 親要素 + * @param tagName 子要素名 + * @return 子要素 + * @throw 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); + } + + 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(); + } + + 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.setText(lang, text); + } + } + + return; + } + + 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"); + Pos3d 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"); + Pos3d 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"); + Vec3d 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"); + Pos2d 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); + Pos3d 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); + + 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"); + Pos3d 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; + + Pos3d 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); + + Pos3d 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/xml/pckage-info.java b/src/main/java/jp/sourceforge/mikutoga/pmd/xml/pckage-info.java new file mode 100644 index 0000000..649cfd0 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/pmd/xml/pckage-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.xml; + +/* EOF */ diff --git a/src/main/java/jp/sourceforge/mikutoga/xml/BasicXmlExporter.java b/src/main/java/jp/sourceforge/mikutoga/xml/BasicXmlExporter.java new file mode 100644 index 0000000..eaad199 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/xml/BasicXmlExporter.java @@ -0,0 +1,471 @@ +/* + * basic xml exporter + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.xml; + +import java.io.BufferedWriter; +import java.io.Closeable; +import java.io.Flushable; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.nio.charset.Charset; +import javax.xml.bind.DatatypeConverter; + +/** + * 各種XMLエクスポータの基本機能。 + * UCS4は未サポート。 + */ +public class BasicXmlExporter { + + /** デフォルトエンコーディング。 */ + private static final Charset CS_UTF8 = Charset.forName("UTF-8"); + + /** デフォルトの改行文字列。 */ + private static final String LF = "\n"; // 0x0a + /** デフォルトのインデント単位。 */ + private static final String DEFAULT_INDENT_UNIT = "\u0020\u0020"; + + private static final char[] HEXCHAR_TABLE = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F', + }; + + static{ + assert HEXCHAR_TABLE.length == 16; + } + + + private final Appendable appendable; + + private String newline = LF; + private String indentUnit = DEFAULT_INDENT_UNIT; + + private int indentNest = 0; + private boolean basicLatinOnlyOut = true; + + + /** + * コンストラクタ。 + * 文字エンコーディングはUTF-8が用いられる。 + * @param stream 出力ストリーム + */ + public BasicXmlExporter(OutputStream stream){ + this(stream, CS_UTF8); + return; + } + + /** + * コンストラクタ。 + * @param stream 出力ストリーム + * @param charSet 文字エンコーディング指定 + */ + public BasicXmlExporter(OutputStream stream, Charset charSet){ + this( + new BufferedWriter( + new OutputStreamWriter(stream, charSet) + ) + ); + return; + } + + /** + * コンストラクタ。 + * @param appendable 文字列出力 + */ + public BasicXmlExporter(Appendable appendable){ + super(); + this.appendable = appendable; + return; + } + + /** + * ASCIIコード相当(UCS:Basic-Latin)の文字か否か判定する。 + * @param ch 判定対象文字 + * @return Basic-Latin文字ならtrue + */ + public static boolean isBasicLatin(char ch){ + if(ch <= 0x7f) return true; + return false; + } + + /** + * 改行文字列を設定する。 + * @param newLine 改行文字列 + * @throws NullPointerException 引数がnull + */ + public void setNewLine(String newLine) throws NullPointerException{ + if(newLine == null) throw new NullPointerException(); + this.newline = newLine; + return; + } + + /** + * BasicLatin文字だけで出力するか設定する。 + * BasicLatin以外の文字(≒日本語)をそのまま出力するか + * 文字参照で出力するかの設定が可能。 + * コメント部中身は対象外。 + * @param bool BasicLatin文字だけで出力するならtrue + */ + public void setBasicLatinOnlyOut(boolean bool){ + this.basicLatinOnlyOut = bool; + return; + } + + /** + * BasicLatin文字だけを出力する状態か判定する。 + * コメント部中身は対象外。 + * @return BasicLatin文字だけで出力するならtrue + */ + public boolean isBasicLatinOnlyOut(){ + return this.basicLatinOnlyOut; + } + + /** + * 改行文字列を設定する。 + * デフォルトではLF(0x0a)\nが用いられる。 + * @param seq 改行文字列。nullは空文字列""と解釈される。 + */ + public void setNewLine(CharSequence seq){ + if(seq == null) this.newline = ""; + else this.newline = seq.toString(); + return; + } + + /** + * インデント単位文字列を設定する。 + * デフォルトでは空白2個。 + * @param seq インデント単位文字列。nullは空文字列""と解釈される。 + */ + public void setIndentUnit(CharSequence seq){ + if(seq == null) this.indentUnit = ""; + else this.indentUnit = seq.toString(); + } + + /** + * 可能であれば出力をフラッシュする。 + * @throws IOException 出力エラー + */ + public void flush() throws IOException{ + if(this.appendable instanceof Flushable){ + ((Flushable)this.appendable).flush(); + } + return; + } + + /** + * 可能であれば出力をクローズする。 + * @throws IOException 出力エラー + */ + public void close() throws IOException{ + if(this.appendable instanceof Closeable){ + ((Closeable)this.appendable).close(); + } + return; + } + + /** + * 1文字出力する。 + * @param ch 文字 + * @return this本体 + * @throws IOException 出力エラー + */ + public BasicXmlExporter put(char ch) throws IOException{ + this.appendable.append(ch); + return this; + } + + /** + * 文字列を出力する。 + * @param seq 文字列 + * @return this本体 + * @throws IOException 出力エラー + */ + public BasicXmlExporter put(CharSequence seq) throws IOException{ + this.appendable.append(seq); + return this; + } + + /** + * int値を出力する。 + * @param iVal int値 + * @return this本体 + * @throws IOException 出力エラー + * @see java.lang.Integer#toString(int) + */ + public BasicXmlExporter put(int iVal) throws IOException{ + String value = DatatypeConverter.printInt(iVal); + this.appendable.append(value); + return this; + } + + /** + * float値を出力する。 + * @param fVal float値 + * @return this本体 + * @throws IOException 出力エラー + * @see java.lang.Float#toString(float) + */ + public BasicXmlExporter put(float fVal) throws IOException{ + String value = DatatypeConverter.printFloat(fVal); + this.appendable.append(value); + return this; + } + + /** + * 改行を出力する。 + * @return this本体 + * @throws IOException 出力エラー + */ + public BasicXmlExporter ln() throws IOException{ + this.appendable.append(this.newline); + return this; + } + + /** + * 改行を指定回数出力する。 + * @param count 改行回数。0以下の場合は何も出力しない。 + * @return this本体 + * @throws IOException 出力エラー + */ + public BasicXmlExporter ln(int count) throws IOException{ + for(int ct = 1; ct <= count; ct++){ + this.appendable.append(this.newline); + } + return this; + } + + /** + * インデントを出力する。 + * インデント単位文字列をネストレベル回数分出力する。 + * @return this本体 + * @throws IOException 出力エラー + */ + public BasicXmlExporter ind() throws IOException{ + for(int ct = 1; ct <= this.indentNest; ct++){ + put(this.indentUnit); + } + return this; + } + + /** + * インデントレベルを一段下げる。 + */ + public void pushNest(){ + this.indentNest++; + return; + } + + /** + * インデントレベルを一段上げる。 + * インデントレベル0の状態をさらに上げようとした場合、何も起こらない。 + */ + public void popNest(){ + this.indentNest--; + if(this.indentNest < 0) this.indentNest = 0; + return; + } + + /** + * 指定された文字を16進2桁の文字参照形式で出力する。 + * 2桁で出力できない場合は4桁で出力する。 + * @param ch 文字 + * @return this本体 + * @throws IOException 出力エラー + */ + public BasicXmlExporter putCharRef2Hex(char ch) throws IOException{ + if(ch > 0xff) return putCharRef4Hex(ch); + + char hex3 = HEXCHAR_TABLE[(ch >> 4) & 0x000f]; + char hex4 = HEXCHAR_TABLE[(ch ) & 0x000f]; + + this.appendable.append("&#x"); + this.appendable.append(hex3); + this.appendable.append(hex4); + this.appendable.append(';'); + + return this; + } + + /** + * 指定された文字を16進4桁の文字参照形式で出力する。 + * UCS4に伴うサロゲートペアは未サポート + * @param ch 文字 + * @return this本体 + * @throws IOException 出力エラー + */ + public BasicXmlExporter putCharRef4Hex(char ch) throws IOException{ + char hex1 = HEXCHAR_TABLE[(ch >> 12) & 0x000f]; + char hex2 = HEXCHAR_TABLE[(ch >> 8) & 0x000f]; + char hex3 = HEXCHAR_TABLE[(ch >> 4) & 0x000f]; + char hex4 = HEXCHAR_TABLE[(ch ) & 0x000f]; + + this.appendable.append("&#x"); + this.appendable.append(hex1); + this.appendable.append(hex2); + this.appendable.append(hex3); + this.appendable.append(hex4); + this.appendable.append(';'); + + return this; + } + + /** + * 要素の中身および属性値中身を出力する。 + * 必要に応じてXML定義済み実体文字が割り振られた文字、 + * コントロールコード、および非BasicLatin文字がエスケープされる。 + * @param content 内容 + * @return this本体 + * @throws IOException 出力エラー + */ + public BasicXmlExporter putContent(CharSequence content) + throws IOException{ + int length = content.length(); + + for(int pos = 0; pos < length; pos++){ + char ch = content.charAt(pos); + if(Character.isISOControl(ch)){ + putCharRef2Hex(ch); + }else if( ! isBasicLatin(ch) && isBasicLatinOnlyOut()){ + putCharRef4Hex(ch); + }else{ + switch(ch){ + case '&': this.appendable.append("&"); break; + case '<': this.appendable.append("<"); break; + case '>': this.appendable.append(">"); break; + case '"': this.appendable.append("""); break; + case '\'': this.appendable.append("'"); break; + case '\u00a5': this.appendable.append('\u005c\u005c'); break; + default: this.appendable.append(ch); break; + } + } + } + + return this; + } + + /** + * 属性値を出力する。 + * @param attrName 属性名 + * @param content 属性内容 + * @return this本体 + * @throws IOException 出力エラー + */ + public BasicXmlExporter putAttr(CharSequence attrName, + CharSequence content) + throws IOException{ + put(attrName).put('=').put('"').putContent(content).put('"'); + return this; + } + + /** + * int型属性値を出力する。 + * @param attrName 属性名 + * @param iVal int値 + * @return this本体 + * @throws IOException 出力エラー + */ + public BasicXmlExporter putIntAttr(CharSequence attrName, + int iVal) + throws IOException{ + put(attrName).put('=').put('"').put(iVal).put('"'); + return this; + } + + /** + * float型属性値を出力する。 + * @param attrName 属性名 + * @param fVal float値 + * @return this本体 + * @throws IOException 出力エラー + */ + public BasicXmlExporter putFloatAttr(CharSequence attrName, + float fVal) + throws IOException{ + put(attrName).put('=').put('"').put(fVal).put('"'); + return this; + } + + /** + * コメントの内容を出力する。 + * コメント中の\n記号出現に伴い、 + * あらかじめ指定された改行文字が出力される。 + * \n以外のコントロールコード各種、 + * 及び非BasicLatin文字はそのまま出力される。 + * 連続するハイフン(-)記号間には強制的にスペースが挿入される。 + * @param comment コメント内容 + * @return this本体 + * @throws IOException 出力エラー + */ + public BasicXmlExporter putCommentContent(CharSequence comment) + throws IOException{ + int length = comment.length(); + + char prev = '\0'; + for(int pos = 0; pos < length; pos++){ + char ch = comment.charAt(pos); + if(ch == '\n'){ + ln(); + prev = ch; + continue; + } + if(prev == '-' && ch == '-') put(' '); + put(ch); + prev = ch; + } + + return this; + } + + /** + * 1行コメントを出力する。 + * コメント中の\n記号出現に伴い、 + * あらかじめ指定された改行文字が出力される。 + * \n以外のコントロールコード各種、 + * 及び非BasicLatin文字はそのまま出力される。 + * 連続するハイフン(-)記号間には強制的にスペースが挿入される。 + * @param comment コメント内容 + * @return this本体 + * @throws IOException 出力エラー + */ + public BasicXmlExporter putLineComment(CharSequence comment) + throws IOException{ + put(""); + return this; + } + + /** + * ブロックコメントを出力する。 + * コメント中の\n記号出現に伴い、 + * あらかじめ指定された改行文字が出力される。 + * \n以外のコントロールコード各種、 + * 及び非BasicLatin文字はそのまま出力される。 + * 連続するハイフン(-)記号間には強制的にスペースが挿入される。 + * @param comment コメント内容 + * @return this本体 + * @throws IOException 出力エラー + */ + public BasicXmlExporter putBlockComment(CharSequence comment) + throws IOException{ + put("").ln(); + + return this; + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/xml/DomUtils.java b/src/main/java/jp/sourceforge/mikutoga/xml/DomUtils.java new file mode 100644 index 0000000..6cd09ba --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/xml/DomUtils.java @@ -0,0 +1,356 @@ +/* + * XML DOM utilities + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.xml; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.NoSuchElementException; +import javax.xml.bind.DatatypeConverter; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +/** + * DOMユーティリティ。 + */ +public final class DomUtils { + + /** + * 隠しコンストラクタ。 + */ + private DomUtils(){ + super(); + assert false; + throw new AssertionError(); + } + + /** + * 要素からxsd:string型属性値を読み取る。 + * @param elem 要素 + * @param attrName 属性名 + * @return 文字列 + * @throw TogaXmlException 属性値が見つからなかった。 + */ + public static String getStringAttr(Element elem, String attrName) + throws TogaXmlException{ + if( ! elem.hasAttribute(attrName) ){ + String message = "Attr:[" + attrName + "] " + + "was not found in " + + "Elem:[" + elem.getTagName()+"]"; + throw new TogaXmlException(message); + } + + String result; + try{ + result = elem.getAttribute(attrName); + }catch(IllegalArgumentException e){ + String message = "Invalid attribute form [" + attrName + "]"; + throw new TogaXmlException(message, e); + } + + return result; + } + + /** + * 要素からxsd:boolean型属性値を読み取る。 + * @param elem 要素 + * @param attrName 属性名 + * @return 真ならtrue + * @throw TogaXmlException 属性値が見つからなかった。 + */ + public static boolean getBooleanAttr(Element elem, String attrName) + throws TogaXmlException{ + String value = getStringAttr(elem, attrName); + + boolean result; + try{ + result = DatatypeConverter.parseBoolean(value); + }catch(IllegalArgumentException e){ + String message = + "Invalid boolean attribute form " + + "[" + attrName + "][" + value + "]"; + throw new TogaXmlException(message, e); + } + + return result; + } + + /** + * 要素からxsd:integer型属性値を読み取る。 + * @param elem 要素 + * @param attrName 属性名 + * @return int値 + * @throw TogaXmlException 属性値が見つからなかった。 + */ + public static int getIntegerAttr(Element elem, String attrName) + throws TogaXmlException{ + String value = getStringAttr(elem, attrName); + + int result; + try{ + result = DatatypeConverter.parseInt(value); + }catch(IllegalArgumentException e){ + String message = + "Invalid integer attribute form " + + "[" + attrName + "][" + value + "]"; + throw new TogaXmlException(message, e); + } + + return result; + } + + /** + * 要素からxsd:float型属性値を読み取る。 + * @param elem 要素 + * @param attrName 属性名 + * @return float値 + * @throw TogaXmlException 属性値が見つからなかった。 + */ + public static float getFloatAttr(Element elem, String attrName) + throws TogaXmlException{ + String value = getStringAttr(elem, attrName); + + float result; + try{ + result = DatatypeConverter.parseFloat(value); + }catch(IllegalArgumentException e){ + String message = + "Invalid float attribute form " + + "[" + attrName + "][" + value + "]"; + throw new TogaXmlException(message, e); + } + + return result; + } + + /** + * 要素から日本語Windows用ファイル名を属性値として読み取る。 + * 念のため文字U+00A5は文字U-005Cに変換される。 + * @param elem 要素 + * @param attrName 属性名 + * @return ファイル名 + * @throw TogaXmlException 属性値が見つからなかった。 + */ + public static String getSjisFileNameAttr(Element elem, String attrName) + throws TogaXmlException{ + String result; + try{ + result = getStringAttr(elem, attrName); + }catch(IllegalArgumentException e){ + String message = + "Invalid winfile attribute form " + + "[" + attrName + "]"; + throw new TogaXmlException(message, e); + } + + result.replace("" + '\u00a5', "" + '\u005c\u005c'); + + return result; + } + + /** + * 指定された名前の子要素を1つだけ返す。 + * @param parent 親要素 + * @param tagName 子要素名 + * @return 子要素 + * @throw TogaXmlException 1つも見つからなかった + */ + public static Element getChild(Element parent, String tagName) + throws TogaXmlException{ + Element result = null; + + for(Node node = parent.getFirstChild(); + node != null; + node = node.getNextSibling() ){ + + if(node.getNodeType() != Node.ELEMENT_NODE) continue; + Element elem = (Element) node; + + String elemTagName = elem.getTagName(); + if( tagName.equals(elemTagName) ){ + result = elem; + break; + } + } + + if(result == null){ + String message = + "Elem:[" + tagName + "] was not found in " + +"Elem:[" + parent.getTagName() + "]"; + throw new TogaXmlException(message); + } + + return result; + } + + /** + * 親要素が指定された名前の子要素を持つか判定する。 + * @param parent 親要素 + * @param tagName 子要素名 + * @return 指定名の子要素が存在すればtrue + */ + public static boolean hasChild(Element parent, String tagName){ + for(Node node = parent.getFirstChild(); + node != null; + node = node.getNextSibling() ){ + + if(node.getNodeType() != Node.ELEMENT_NODE) continue; + Element elem = (Element) node; + + String elemTagName = elem.getTagName(); + if( tagName.equals(elemTagName) ) return true; + } + + return false; + } + + /** + * 指定された名前の子要素のリストを返す。 + * @param parent 親要素 + * @param childTag 子要素名 + * @return 子要素のリスト + */ + public static List getChildList(Element parent, + String childTag){ + List result = new LinkedList(); + + for(Node node = parent.getFirstChild(); + node != null; + node = node.getNextSibling() ){ + + if(node.getNodeType() != Node.ELEMENT_NODE) continue; + Element elem = (Element) node; + + String tagName = elem.getTagName(); + if( ! childTag.equals(tagName) ) continue; + + result.add(elem); + } + + return result; + } + + /** + * 指定された名前の子要素の列挙子を返す。 + * @param parent 親要素 + * @param childTag 子要素名 + * @return 子要素の列挙子 + */ + public static Iterator getChildIterator(Element parent, + String childTag){ + Element firstElem; + try{ + firstElem = getChild(parent, childTag); + }catch(TogaXmlException e){ + firstElem = null; + } + + Iterator result = new ElemIterator(firstElem); + + return result; + } + + /** + * 指定された名前の子要素のforeachを返す。 + * @param parent 親要素 + * @param childTag 子要素名 + * @return 子要素のforeach + */ + public static Iterable getEachChild(Element parent, + String childTag){ + final Iterator iterator = getChildIterator(parent, childTag); + Iterable result = new Iterable(){ + public Iterator iterator(){ + return iterator; + } + }; + return result; + } + + /** + * 要素の次の要素を返す。 + * @param elem 要素 + * @return 次の要素。なければnull + */ + public static Element nextElement(Element elem){ + Node nextNode = elem; + for(;;){ + nextNode = nextNode.getNextSibling(); + if(nextNode == null) break; + if(nextNode.getNodeType() == Node.ELEMENT_NODE){ + break; + } + } + + return (Element) nextNode; + } + + /** + * 同じ要素名を持つ次の要素を返す。 + * @param elem 要素 + * @return 次の要素。なければnull + */ + public static Element nextNamedElement(Element elem){ + String tagName = elem.getTagName(); + Element nextElem = elem; + for(;;){ + nextElem = nextElement(nextElem); + if(nextElem == null) break; + if(tagName.equals(nextElem.getTagName())) break; + } + + return nextElem; + } + + /** + * 同じ親要素と同じ要素名を持つ兄弟要素を列挙する列挙子。 + */ + private static class ElemIterator implements Iterator{ + private Element next; + + /** + * コンストラクタ。 + * @param elem 最初の要素。nullを指定すれば空列挙子となる。 + */ + private ElemIterator(Element elem){ + super(); + this.next = elem; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + public boolean hasNext(){ + if(this.next == null) return false; + return true; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + * @throws NoSuchElementException {@inheritDoc} + */ + public Element next() throws NoSuchElementException{ + if(this.next == null) throw new NoSuchElementException(); + Element result = this.next; + this.next = nextNamedElement(this.next); + return result; + } + + /** + * {@inheritDoc} + * ※ 未サポート。 + */ + public void remove(){ + throw new UnsupportedOperationException(); + } + + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/xml/TogaXmlException.java b/src/main/java/jp/sourceforge/mikutoga/xml/TogaXmlException.java new file mode 100644 index 0000000..1a3a955 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/xml/TogaXmlException.java @@ -0,0 +1,52 @@ +/* + * exception about xml + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +package jp.sourceforge.mikutoga.xml; + +/** + * 意図しないXML文書を検出した際の例外。 + */ +@SuppressWarnings("serial") +public class TogaXmlException extends Exception{ + + /** + * コンストラクタ。 + */ + public TogaXmlException(){ + super(); + return; + } + + /** + * コンストラクタ。 + * @param message メッセージ + */ + public TogaXmlException(String message){ + super(message); + return; + } + + /** + * コンストラクタ。 + * @param message メッセージ + * @param cause 原因の例外 + */ + public TogaXmlException(String message, Throwable cause){ + super(message, cause); + return; + } + + /** + * コンストラクタ。 + * @param cause 原因の例外 + */ + public TogaXmlException(Throwable cause){ + super(cause); + return; + } + +} diff --git a/src/main/java/jp/sourceforge/mikutoga/xml/XmlResourceResolver.java b/src/main/java/jp/sourceforge/mikutoga/xml/XmlResourceResolver.java new file mode 100644 index 0000000..779edec --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/xml/XmlResourceResolver.java @@ -0,0 +1,371 @@ +/* + * xml resource resolver + * + * License : The MIT License + * Copyright(c) 2009 olyutorskii + */ + +package jp.sourceforge.mikutoga.xml; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import org.w3c.dom.ls.LSInput; +import org.w3c.dom.ls.LSResourceResolver; +import org.xml.sax.EntityResolver; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +/** + * URL変換マップに従い、XML文書からの外部参照をリダイレクトする。 + * 相対URIはこのクラスをベースに解決される。 + * 主な用途は外部スキーマのリソース化など。 + */ +public class XmlResourceResolver + implements LSResourceResolver, EntityResolver { + + public static final String SCHEMA_XML = + "http://www.w3.org/2001/xml.xsd"; + public static final String NS_XSD = + "http://www.w3.org/2001/XMLSchema-instance"; + + private static final String LOCAL_SCHEMA_XML = + "./resources/xml-2009-01.xsd"; + private static final URI EMPTY_URI = URI.create(""); + private static final Class THISCLASS = XmlResourceResolver.class; + + + /** + * 絶対URIと相対URIを合成したURIを返す。 + * 正規化も行われる。 + * @param base 絶対URIでなければならない。nullでもよい。 + * @param relative 絶対URIでもよいがその場合baseは無視される。null可。 + * @return 合成結果のURLオブジェクト。必ず絶対URIになる。 + * @throws java.net.URISyntaxException URIとして変。 + * @throws java.lang.IllegalArgumentException 絶対URIが生成できない。 + */ + protected static URI buildBaseRelativeURI(String base, String relative) + throws URISyntaxException, + IllegalArgumentException { + URI baseURI = null; + if(base != null){ + baseURI = new URI(base); + if( ! baseURI.isAbsolute() ) throw new IllegalArgumentException(); + } + + URI relativeURI = EMPTY_URI; + if(relative != null){ + relativeURI = new URI(relative); + } + + URI resultURI; + if(baseURI == null || relativeURI.isAbsolute()){ + resultURI = relativeURI; + }else{ + resultURI = baseURI.resolve(relativeURI); + } + + if( ! resultURI.isAbsolute() ) throw new IllegalArgumentException(); + + resultURI = resultURI.normalize(); + + return resultURI; + } + + /** + * LSInput実装を生成する。 + * @return LSInput実装 + */ + public static LSInput createLSInput(){ + LSInput input = new LSInputImpl(); + return input; + } + + private final Map uriMap = new HashMap(); + + /** + * コンストラクタ。 + */ + public XmlResourceResolver(){ + super(); + + assert this.getClass().equals(THISCLASS); + + URI originalURI = URI.create(SCHEMA_XML); + URL redirectURL = THISCLASS.getResource(LOCAL_SCHEMA_XML); + URI redirectURI; + try{ + redirectURI = redirectURL.toURI(); + }catch(URISyntaxException e){ + assert false; + throw new AssertionError(e); + } + + this.uriMap.put(originalURI, redirectURI); + + return; + } + + /** + * オリジナルURIとリダイレクト先のURIを登録する。 + * オリジナルURIへのアクセスはリダイレクトされる。 + * @param original オリジナルURI + * @param redirect リダイレクトURI + */ + public void putURIMap(URI original, URI redirect){ + this.uriMap.put(original.normalize(), redirect.normalize()); + return; + } + + /** + * 変換後のリソースの入力ストリームを得る。 + * @param originalURI オリジナルURI + * @return 入力ストリーム + * @throws java.io.IOException 入出力エラー + */ + public InputStream getXMLResourceAsStream(URI originalURI) + throws IOException{ + URI resourceURI = this.uriMap.get(originalURI.normalize()); + URL resourceURL = resourceURI.toURL(); + InputStream is = resourceURL.openStream(); + + return is; + } + + /** + * {@inheritDoc} + * URL変換したあとの入力ソースを返す。 + * @param type {@inheritDoc} + * @param namespaceURI {@inheritDoc} + * @param publicId {@inheritDoc} + * @param systemId {@inheritDoc} + * @param baseURI {@inheritDoc} + * @return {@inheritDoc} + */ + public LSInput resolveResource(String type, + String namespaceURI, + String publicId, + String systemId, + String baseURI ){ + if(systemId == null) return null; + + URI originalURI; + try{ + originalURI = buildBaseRelativeURI(baseURI, systemId); + }catch(URISyntaxException e){ + return null; + } + + InputStream is; + try{ + is = getXMLResourceAsStream(originalURI); + }catch(IOException e){ + return null; + } + + LSInput input = createLSInput(); + input.setBaseURI(baseURI); + input.setPublicId(publicId); + input.setSystemId(systemId); + input.setByteStream(is); + + return input; + } + + /** + * {@inheritDoc} + * URL変換したあとの入力ソースを返す。 + * @param publicId {@inheritDoc} + * @param systemId {@inheritDoc} + * @return {@inheritDoc} + * @throws org.xml.sax.SAXException {@inheritDoc} + * @throws java.io.IOException {@inheritDoc} + */ + public InputSource resolveEntity(String publicId, String systemId) + throws SAXException, IOException{ + if(systemId == null) return null; + + URI originalUri; + try{ + originalUri = new URI(systemId); + }catch(URISyntaxException e){ + return null; + } + + InputStream is = getXMLResourceAsStream(originalUri); + + InputSource source = new InputSource(is); + source.setPublicId(publicId); + source.setSystemId(systemId); + + return source; + } + + /** + * JRE1.5用LSInput実装。 + * JRE1.6なら + * org.w3c.dom.ls.DOMImplementationLS#createLSInput() + * で生成可能かも。 + */ + private static class LSInputImpl implements LSInput { + + private String baseURI = null; + private InputStream byteStream = null; + private boolean certifiedText = false; + private Reader characterStream = null; + private String encoding = null; + private String publicId = null; + private String stringData = null; + private String systemId = null; + + /** + * コンストラクタ。 + */ + private LSInputImpl(){ + super(); + return; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + public String getBaseURI(){ + return this.baseURI; + } + + /** + * {@inheritDoc} + * @param baseURI {@inheritDoc} + */ + public void setBaseURI(String baseURI){ + this.baseURI = baseURI; + return; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + public InputStream getByteStream(){ + return this.byteStream; + } + + /** + * {@inheritDoc} + * @param byteStream {@inheritDoc} + */ + public void setByteStream(InputStream byteStream){ + this.byteStream = byteStream; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + public boolean getCertifiedText(){ + return this.certifiedText; + } + + /** + * {@inheritDoc} + * @param certifiedText {@inheritDoc} + */ + public void setCertifiedText(boolean certifiedText){ + this.certifiedText = certifiedText; + return; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + public Reader getCharacterStream(){ + return this.characterStream; + } + + /** + * {@inheritDoc} + * @param characterStream {@inheritDoc} + */ + public void setCharacterStream(Reader characterStream){ + this.characterStream = characterStream; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + public String getEncoding(){ + return this.encoding; + } + + /** + * {@inheritDoc} + * @param encoding {@inheritDoc} + */ + public void setEncoding(String encoding){ + this.encoding = encoding; + return; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + public String getPublicId(){ + return this.publicId; + } + + /** + * {@inheritDoc} + * @param publicId {@inheritDoc} + */ + public void setPublicId(String publicId){ + this.publicId = publicId; + return; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + public String getStringData(){ + return this.stringData; + } + + /** + * {@inheritDoc} + * @param stringData {@inheritDoc} + */ + public void setStringData(String stringData){ + this.stringData = stringData; + return; + } + + /** + * {@inheritDoc} + * @return {@inheritDoc} + */ + public String getSystemId(){ + return this.systemId; + } + + /** + * {@inheritDoc} + * @param systemId {@inheritDoc} + */ + public void setSystemId(String systemId){ + this.systemId = systemId; + return; + } + + } + + // TODO OASIS XML Catalog などと調和したい。 +} diff --git a/src/main/java/jp/sourceforge/mikutoga/xml/package-info.java b/src/main/java/jp/sourceforge/mikutoga/xml/package-info.java new file mode 100644 index 0000000..7514231 --- /dev/null +++ b/src/main/java/jp/sourceforge/mikutoga/xml/package-info.java @@ -0,0 +1,14 @@ +/* + * package information for Javadoc + * + * License : The MIT License + * Copyright(c) 2010 MikuToga Partners + */ + +/** + * MikuToga XML共通ライブラリ。 + */ + +package jp.sourceforge.mikutoga.xml; + +/* EOF */ diff --git a/src/main/resources/jp/sourceforge/mikutoga/pmd/resources/BoneTypeName.properties b/src/main/resources/jp/sourceforge/mikutoga/pmd/resources/BoneTypeName.properties new file mode 100644 index 0000000..fad6c23 --- /dev/null +++ b/src/main/resources/jp/sourceforge/mikutoga/pmd/resources/BoneTypeName.properties @@ -0,0 +1,14 @@ +# + +ROTATE = Rotate +ROTMOV = Rotate/Move +IK = IK +UNKNOWN = Unknown +UNDERIK = Under IK +UNDERROT = Under rotate +IKCONNECTED = IK connected +HIDDEN = Hidden +TWIST = Twist +LINKEDROT = Linked Rotate + +### EOF ### diff --git a/src/main/resources/jp/sourceforge/mikutoga/pmd/resources/BoneTypeName_ja.properties b/src/main/resources/jp/sourceforge/mikutoga/pmd/resources/BoneTypeName_ja.properties new file mode 100644 index 0000000..c363876 --- /dev/null +++ b/src/main/resources/jp/sourceforge/mikutoga/pmd/resources/BoneTypeName_ja.properties @@ -0,0 +1,14 @@ +# + +ROTATE = \u56de\u8ee2 +ROTMOV = \u56de\u8ee2/\u79fb\u52d5 +IK = IK +UNKNOWN = \u4e0d\u660e +UNDERIK = IK\u5f71\u97ff\u4e0b(\u56de\u8ee2) +UNDERROT = \u56de\u8ee2\u5f71\u97ff\u4e0b +IKCONNECTED = IK\u63a5\u7d9a\u5148 +HIDDEN = \u975e\u8868\u793a +TWIST = \u6369\u308a +LINKEDROT = \u56de\u8ee2\u9023\u52d5 + +### EOF ### diff --git a/src/main/resources/jp/sourceforge/mikutoga/pmd/resources/MorphTypeName.properties b/src/main/resources/jp/sourceforge/mikutoga/pmd/resources/MorphTypeName.properties new file mode 100644 index 0000000..9430c2c --- /dev/null +++ b/src/main/resources/jp/sourceforge/mikutoga/pmd/resources/MorphTypeName.properties @@ -0,0 +1,9 @@ +# + +BASE = base +EYEBROW = brow +EYE = eyes +LIP = mouse +EXTRA = other + +### EOF ### diff --git a/src/main/resources/jp/sourceforge/mikutoga/pmd/resources/MorphTypeName_ja.properties b/src/main/resources/jp/sourceforge/mikutoga/pmd/resources/MorphTypeName_ja.properties new file mode 100644 index 0000000..27e7fd3 --- /dev/null +++ b/src/main/resources/jp/sourceforge/mikutoga/pmd/resources/MorphTypeName_ja.properties @@ -0,0 +1,9 @@ +# + +BASE = base +EYEBROW = \u307e\u3086 +EYE = \u76ee +LIP = \u30ea\u30c3\u30d7 +EXTRA = \u305d\u306e\u4ed6 + +### EOF ### diff --git a/src/main/resources/jp/sourceforge/mikutoga/pmd/resources/RigidBehaviorTypeName.properties b/src/main/resources/jp/sourceforge/mikutoga/pmd/resources/RigidBehaviorTypeName.properties new file mode 100644 index 0000000..c92b47e --- /dev/null +++ b/src/main/resources/jp/sourceforge/mikutoga/pmd/resources/RigidBehaviorTypeName.properties @@ -0,0 +1,7 @@ +# + +FOLLOWBONE = static(to bone) +ONLYDYNAMICS = dynamic +BONEDDYNAMICS = bone matching + +### EOF ### diff --git a/src/main/resources/jp/sourceforge/mikutoga/pmd/resources/RigidBehaviorTypeName_ja.properties b/src/main/resources/jp/sourceforge/mikutoga/pmd/resources/RigidBehaviorTypeName_ja.properties new file mode 100644 index 0000000..c6cdab7 --- /dev/null +++ b/src/main/resources/jp/sourceforge/mikutoga/pmd/resources/RigidBehaviorTypeName_ja.properties @@ -0,0 +1,7 @@ +# + +FOLLOWBONE = \u30dc\u30fc\u30f3\u8ffd\u5f93 +ONLYDYNAMICS = \u7269\u7406\u6f14\u7b97 +BONEDDYNAMICS = \u30dc\u30fc\u30f3\u4f4d\u7f6e\u5408\u308f\u305b + +### EOF ### diff --git a/src/main/resources/jp/sourceforge/mikutoga/pmd/resources/RigidShapeTypeName.properties b/src/main/resources/jp/sourceforge/mikutoga/pmd/resources/RigidShapeTypeName.properties new file mode 100644 index 0000000..fcc2512 --- /dev/null +++ b/src/main/resources/jp/sourceforge/mikutoga/pmd/resources/RigidShapeTypeName.properties @@ -0,0 +1,7 @@ +# + +SPHERE = sphere +BOX = box +CAPSULE = capsule + +### EOF ### diff --git a/src/main/resources/jp/sourceforge/mikutoga/pmd/resources/RigidShapeTypeName_ja.properties b/src/main/resources/jp/sourceforge/mikutoga/pmd/resources/RigidShapeTypeName_ja.properties new file mode 100644 index 0000000..dc18557 --- /dev/null +++ b/src/main/resources/jp/sourceforge/mikutoga/pmd/resources/RigidShapeTypeName_ja.properties @@ -0,0 +1,7 @@ +# + +SPHERE = \u7403 +BOX = \u7bb1 +CAPSULE = \u30ab\u30d7\u30bb\u30eb + +### EOF ### diff --git a/src/main/resources/jp/sourceforge/mikutoga/pmd/xml/resources/pmdxml-100923.dtd b/src/main/resources/jp/sourceforge/mikutoga/pmd/xml/resources/pmdxml-100923.dtd new file mode 100644 index 0000000..ad94d32 --- /dev/null +++ b/src/main/resources/jp/sourceforge/mikutoga/pmd/xml/resources/pmdxml-100923.dtd @@ -0,0 +1,532 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/jp/sourceforge/mikutoga/pmd/xml/resources/pmdxml-100923.xsd b/src/main/resources/jp/sourceforge/mikutoga/pmd/xml/resources/pmdxml-100923.xsd new file mode 100644 index 0000000..bae4d94 --- /dev/null +++ b/src/main/resources/jp/sourceforge/mikutoga/pmd/xml/resources/pmdxml-100923.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. + + + + + + + + + + + + + + + + diff --git a/src/main/resources/jp/sourceforge/mikutoga/xml/resources/xml-2009-01.xsd b/src/main/resources/jp/sourceforge/mikutoga/xml/resources/xml-2009-01.xsd new file mode 100644 index 0000000..9e56d2e --- /dev/null +++ b/src/main/resources/jp/sourceforge/mikutoga/xml/resources/xml-2009-01.xsd @@ -0,0 +1,286 @@ + + + + + + +

+

About the XML namespace

+ +
+

+ This schema document describes the XML namespace, in a form + suitable for import by other schema documents. +

+

+ See + http://www.w3.org/XML/1998/namespace.html and + + http://www.w3.org/TR/REC-xml for information + about this namespace. +

+

+ Note that local names in this namespace are intended to be + defined only by the World Wide Web Consortium or its subgroups. + The names currently defined in this namespace are listed below. + They should not be used with conflicting semantics by any Working + Group, specification, or document instance. +

+

+ See further below in this document for more information about how to refer to this schema document from your own + XSD schema documents and about the + namespace-versioning policy governing this schema document. +

+
+
+ + + + + + +
+ +

lang (as an attribute name)

+

+ denotes an attribute whose value + is a language code for the natural language of the content of + any element; its value is inherited. This name is reserved + by virtue of its definition in the XML specification.

+ +
+
+

Notes

+

+ Attempting to install the relevant ISO 2- and 3-letter + codes as the enumerated possible values is probably never + going to be a realistic possibility. +

+

+ See BCP 47 at + http://www.rfc-editor.org/rfc/bcp/bcp47.txt + and the IANA language subtag registry at + + http://www.iana.org/assignments/language-subtag-registry + for further information. +

+

+ The union allows for the 'un-declaration' of xml:lang with + the empty string. +

+
+
+
+ + + + + + + + + +
+ + + + +
+ +

space (as an attribute name)

+

+ denotes an attribute whose + value is a keyword indicating what whitespace processing + discipline is intended for the content of the element; its + value is inherited. This name is reserved by virtue of its + definition in the XML specification.

+ +
+
+
+ + + + + + +
+ + + +
+ +

base (as an attribute name)

+

+ denotes an attribute whose value + provides a URI to be used as the base for interpreting any + relative URIs in the scope of the element on which it + appears; its value is inherited. This name is reserved + by virtue of its definition in the XML Base specification.

+ +

+ See http://www.w3.org/TR/xmlbase/ + for information about this attribute. +

+
+
+
+
+ + + + +
+ +

id (as an attribute name)

+

+ denotes an attribute whose value + should be interpreted as if declared to be of type ID. + This name is reserved by virtue of its definition in the + xml:id specification.

+ +

+ See http://www.w3.org/TR/xml-id/ + for information about this attribute. +

+
+
+
+
+ + + + + + + + + + +
+ +

Father (in any context at all)

+ +
+

+ denotes Jon Bosak, the chair of + the original XML Working Group. This name is reserved by + the following decision of the W3C XML Plenary and + XML Coordination groups: +

+
+

+ In appreciation for his vision, leadership and + dedication the W3C XML Plenary on this 10th day of + February, 2000, reserves for Jon Bosak in perpetuity + the XML name "xml:Father". +

+
+
+
+
+
+ + + +
+

About this schema document

+ +
+

+ This schema defines attributes and an attribute group suitable + for use by schemas wishing to allow xml:base, + xml:lang, xml:space or + xml:id attributes on elements they define. +

+

+ To enable this, such a schema must import this schema for + the XML namespace, e.g. as follows: +

+
+          <schema . . .>
+           . . .
+           <import namespace="http://www.w3.org/XML/1998/namespace"
+                      schemaLocation="http://www.w3.org/2001/xml.xsd"/>
+     
+

+ or +

+
+           <import namespace="http://www.w3.org/XML/1998/namespace"
+                      schemaLocation="http://www.w3.org/2009/01/xml.xsd"/>
+     
+

+ Subsequently, qualified reference to any of the attributes or the + group defined below will have the desired effect, e.g. +

+
+          <type . . .>
+           . . .
+           <attributeGroup ref="xml:specialAttrs"/>
+     
+

+ will define a type which will schema-validate an instance element + with any of those attributes. +

+
+
+
+
+ + + +
+

Versioning policy for this schema document

+
+

+ In keeping with the XML Schema WG's standard versioning + policy, this schema document will persist at + + http://www.w3.org/2009/01/xml.xsd. +

+

+ At the date of issue it can also be found at + + http://www.w3.org/2001/xml.xsd. +

+

+ The schema document at that URI may however change in the future, + in order to remain compatible with the latest version of XML + Schema itself, or with the XML namespace itself. In other words, + if the XML Schema or XML namespaces change, the version of this + document at + http://www.w3.org/2001/xml.xsd + + will change accordingly; the version at + + http://www.w3.org/2009/01/xml.xsd + + will not change. +

+

+ Previous dated (and unchanging) versions of this schema + document are at: +

+ +
+
+
+
+ + diff --git a/src/test/java/jp/sourceforge/mikutoga/pmd/MorphTypeTest.java b/src/test/java/jp/sourceforge/mikutoga/pmd/MorphTypeTest.java new file mode 100644 index 0000000..21f3d36 --- /dev/null +++ b/src/test/java/jp/sourceforge/mikutoga/pmd/MorphTypeTest.java @@ -0,0 +1,126 @@ +/* + * + */ + +package jp.sourceforge.mikutoga.pmd; + +import java.util.Locale; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * + */ +public class MorphTypeTest { + + public MorphTypeTest() { + } + + @BeforeClass + public static void setUpClass() throws Exception{ + } + + @AfterClass + public static void tearDownClass() throws Exception{ + } + + @Before + public void setUp() { + } + + @After + public void tearDown() { + } + + /** + * Test of values method, of class MorphType. + */ + @Test + public void testValues(){ + System.out.println("values"); + + MorphType[] array = MorphType.values(); + + assertEquals(5, array.length); + + assertEquals(MorphType.BASE, array[0]); + assertEquals(MorphType.EYEBROW, array[1]); + assertEquals(MorphType.EYE, array[2]); + assertEquals(MorphType.LIP, array[3]); + assertEquals(MorphType.EXTRA, array[4]); + + return; + } + + /** + * Test of decode method, of class MorphType. + */ + @Test + public void testDecode(){ + System.out.println("decode"); + + assertEquals(MorphType.BASE, MorphType.decode((byte)0x00)); + assertEquals(MorphType.EYEBROW, MorphType.decode((byte)0x01)); + assertEquals(MorphType.EYE, MorphType.decode((byte)0x02)); + assertEquals(MorphType.LIP, MorphType.decode((byte)0x03)); + assertEquals(MorphType.EXTRA, MorphType.decode((byte)0x04)); + assertNull(MorphType.decode((byte)0x05)); + + return; + } + + /** + * Test of encode method, of class MorphType. + */ + @Test + public void testEncode(){ + System.out.println("encode"); + + assertEquals(0x00, MorphType.BASE.encode()); + assertEquals(0x01, MorphType.EYEBROW.encode()); + assertEquals(0x02, MorphType.EYE.encode()); + assertEquals(0x03, MorphType.LIP.encode()); + assertEquals(0x04, MorphType.EXTRA.encode()); + + return; + } + + /** + * Test of getGuiName method, of class MorphType. + */ + @Test + public void testGetGuiName_0args(){ + System.out.println("getGuiName"); + + Locale locale = Locale.getDefault(); + + for(MorphType type : MorphType.values()){ + assertEquals(type.getGuiName(locale), type.getGuiName()); + } + + return; + } + + /** + * Test of getGuiName method, of class MorphType. + */ + @Test + public void testGetGuiName_Locale(){ + System.out.println("getGuiName"); + + Locale locale = Locale.JAPANESE; + + assertEquals("base", MorphType.BASE.getGuiName(locale)); + assertEquals("まゆ", MorphType.EYEBROW.getGuiName(locale)); + assertEquals("目", MorphType.EYE.getGuiName(locale)); + assertEquals("リップ", MorphType.LIP.getGuiName(locale)); + assertEquals("その他", MorphType.EXTRA.getGuiName(locale)); + + return; + } + +} -- 2.11.0