OSDN Git Service

PMD出力機能及びXML入出力機能のソースをマージ
authorOlyutorskii <olyutorskii@users.osdn.me>
Wed, 6 Oct 2010 13:05:19 +0000 (22:05 +0900)
committerOlyutorskii <olyutorskii@users.osdn.me>
Wed, 6 Oct 2010 13:05:19 +0000 (22:05 +0900)
73 files changed:
pom.xml
src/main/java/jp/sourceforge/mikutoga/corelib/I18nText.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/corelib/ListUtil.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/corelib/SerialNumbered.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/corelib/WinFile.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/corelib/package-info.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/BoneGroup.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/BoneInfo.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/BoneType.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/Deg3d.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/DynamicsInfo.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/IKChain.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/JointInfo.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/Material.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/MorphPart.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/MorphType.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/MorphVertex.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/PmdModel.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/Pos2d.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/Pos3d.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/Rad3d.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/RigidBehaviorType.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/RigidGroup.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/RigidInfo.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/RigidShape.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/RigidShapeType.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/ShadeInfo.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/Surface.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/ToonMap.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/TripletRange.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/Vec3d.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/Vertex.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/package-info.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/pmdexporter/AbstractExporter.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/pmdexporter/IllegalPmdException.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/pmdexporter/IllegalPmdTextException.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/pmdexporter/PmdExporter.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/pmdexporter/PmdExporterBase.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/pmdexporter/PmdExporterExt1.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/pmdexporter/PmdExporterExt2.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/pmdexporter/PmdExporterExt3.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/pmdexporter/package-info.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/pmdloader/BoneBuilder.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/pmdloader/JointBuilder.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/pmdloader/MaterialBuilder.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/pmdloader/MorphBuilder.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/pmdloader/PmdLoader.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/pmdloader/RigidBuilder.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/pmdloader/ShapeBuilder.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/pmdloader/TextBuilder.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/pmdloader/ToonBuilder.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/pmdloader/package-info.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/xml/PmdXmlExporter.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/xml/PmdXmlResources.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/xml/Xml2PmdLoader.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/pmd/xml/pckage-info.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/xml/BasicXmlExporter.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/xml/DomUtils.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/xml/TogaXmlException.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/xml/XmlResourceResolver.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/xml/package-info.java [new file with mode: 0644]
src/main/resources/jp/sourceforge/mikutoga/pmd/resources/BoneTypeName.properties [new file with mode: 0644]
src/main/resources/jp/sourceforge/mikutoga/pmd/resources/BoneTypeName_ja.properties [new file with mode: 0644]
src/main/resources/jp/sourceforge/mikutoga/pmd/resources/MorphTypeName.properties [new file with mode: 0644]
src/main/resources/jp/sourceforge/mikutoga/pmd/resources/MorphTypeName_ja.properties [new file with mode: 0644]
src/main/resources/jp/sourceforge/mikutoga/pmd/resources/RigidBehaviorTypeName.properties [new file with mode: 0644]
src/main/resources/jp/sourceforge/mikutoga/pmd/resources/RigidBehaviorTypeName_ja.properties [new file with mode: 0644]
src/main/resources/jp/sourceforge/mikutoga/pmd/resources/RigidShapeTypeName.properties [new file with mode: 0644]
src/main/resources/jp/sourceforge/mikutoga/pmd/resources/RigidShapeTypeName_ja.properties [new file with mode: 0644]
src/main/resources/jp/sourceforge/mikutoga/pmd/xml/resources/pmdxml-100923.dtd [new file with mode: 0644]
src/main/resources/jp/sourceforge/mikutoga/pmd/xml/resources/pmdxml-100923.xsd [new file with mode: 0644]
src/main/resources/jp/sourceforge/mikutoga/xml/resources/xml-2009-01.xsd [new file with mode: 0644]
src/test/java/jp/sourceforge/mikutoga/pmd/MorphTypeTest.java [new file with mode: 0644]

diff --git a/pom.xml b/pom.xml
index 943fe0d..b0316c1 100644 (file)
--- a/pom.xml
+++ b/pom.xml
                 </includes>
             </resource>
 
+            <resource>
+                <directory>src/main/resources</directory>
+                <includes>
+                    <include>**/*.properties</include>
+                    <include>**/*.xsd</include>
+                    <include>**/*.dtd</include>
+                </includes>
+                <excludes>
+                    <exclude>**/version.properties</exclude>
+                </excludes>
+            </resource>
+
         </resources>
 
     </build>
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 (file)
index 0000000..3cfffba
--- /dev/null
@@ -0,0 +1,308 @@
+/*\r
+ * international text\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.corelib;\r
+\r
+import java.util.ArrayList;\r
+import java.util.HashMap;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+import java.util.Locale;\r
+import java.util.Map;\r
+import java.util.Set;\r
+\r
+/**\r
+ * 多言語のバリアントを持つ文字列情報。\r
+ * <ul>\r
+ * <li>プライマリ:識別子にはこちらを使う。基本は日本語。\r
+ * <li>グローバル:基本は英語。UCS Basic-Latinオンリーの貧弱な言語環境でも\r
+ * 読める文字列が望ましい。\r
+ * <li>その他:必要に応じて好きな言語を。\r
+ * </ul>\r
+ */\r
+public class I18nText implements CharSequence {\r
+\r
+    /** プライマリ言語のロケール。 */\r
+    public static final Locale LOCALE_PRIMARY = Locale.JAPANESE;\r
+    /** プライマリ言語の言語コード。 */\r
+    public static final String CODE639_PRIMARY = LOCALE_PRIMARY.getLanguage();\r
+\r
+    /** グローバル言語のロケール。 */\r
+    public static final Locale LOCALE_GLOBAL = Locale.ENGLISH;\r
+    /** グローバル言語の言語コード。 */\r
+    public static final String CODE639_GLOBAL = LOCALE_GLOBAL.getLanguage();\r
+\r
+    static{\r
+        assert CODE639_PRIMARY.equals("ja");\r
+        assert CODE639_GLOBAL .equals("en");\r
+    }\r
+\r
+    private final Map<String, String> nameMap = new HashMap<String, String>();\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     */\r
+    public I18nText(){\r
+        super();\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * プライマリ文字列の登録。\r
+     * @param seq プライマリ文字列。nullの場合は削除動作\r
+     */\r
+    public void setPrimaryText(CharSequence seq){\r
+        setText(CODE639_PRIMARY, seq);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * グローバル文字列の登録。\r
+     * @param seq グローバル文字列。nullの場合は削除動作\r
+     */\r
+    public void setGlobalText(CharSequence seq){\r
+        setText(CODE639_GLOBAL, seq);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 任意のロケールに関連付けられた文字列の登録。\r
+     * @param locale ロケール\r
+     * @param seq 文字列。nullの場合は削除動作\r
+     * @throws NullPointerException ロケール引数がnull\r
+     */\r
+    public void setText(Locale locale, CharSequence seq)\r
+            throws NullPointerException{\r
+        String code639 = locale.getLanguage();\r
+        setText(code639, seq);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 任意の言語コードに関連付けられた文字列の登録。\r
+     * @param code639 ISO639言語コード\r
+     * @param seq 文字列。nullの場合は削除動作\r
+     * @throws NullPointerException 言語コードがnull\r
+     */\r
+    public void setText(String code639, CharSequence seq)\r
+            throws NullPointerException{\r
+        if(code639 == null) throw new NullPointerException();\r
+\r
+        if(seq != null){\r
+            String text = seq.toString();\r
+            this.nameMap.put(code639, text);\r
+        }else{\r
+            this.nameMap.remove(code639);\r
+        }\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 言語コードに応じた文字列を返す。\r
+     * @param code639 ISO639言語コード\r
+     * @return 文字列。見つからなければnullを返す。\r
+     * @throws NullPointerException 引数がnull\r
+     */\r
+    public String getText(String code639) throws NullPointerException{\r
+        if(code639 == null) throw new NullPointerException();\r
+        String result = this.nameMap.get(code639);\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * ロケールに応じた文字列を返す。\r
+     * @param locale ロケール\r
+     * @return 文字列。見つからなければnullを返す。\r
+     * @throws NullPointerException 引数がnull\r
+     */\r
+    public String getText(Locale locale) throws NullPointerException{\r
+        String code639 = locale.getLanguage();\r
+        String result = getText(code639);\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * プライマリ文字列を返す。\r
+     * @return 文字列。見つからなければnullを返す。\r
+     */\r
+    public String getPrimaryText(){\r
+        String result = getText(CODE639_PRIMARY);\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * グローバル文字列を返す。\r
+     * @return 文字列。見つからなければnullを返す。\r
+     */\r
+    public String getGlobalText(){\r
+        String result = getText(CODE639_GLOBAL);\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * プライマリ文字列を返す。\r
+     * 見つからなければグローバル文字列を返す。\r
+     * それでも見つからなければ空文字列を返す。\r
+     * @return 文字列\r
+     */\r
+    public String getText(){\r
+        String result;\r
+\r
+        result = getPrimaryText();\r
+\r
+        if(result == null){\r
+            result = getGlobalText();\r
+        }\r
+\r
+        if(result == null){\r
+            result = "";\r
+        }\r
+\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * 実行環境のデフォルトロケールに応じた文字列を返す。\r
+     * 見つからなければグローバル文字列、プライマリ文字列の順に返す。\r
+     * それでも見つからなければ適当な言語コードの文字列を返す。\r
+     * それでも見つからなければ空文字列を返す。\r
+     * デフォルトロケールの確認はその都度行われる。\r
+     * @return 文字列\r
+     */\r
+    public String getLocalizedText(){\r
+        Locale locale = Locale.getDefault();\r
+        String langCode = locale.getLanguage();\r
+\r
+        String result;\r
+\r
+        result = this.nameMap.get(langCode);\r
+\r
+        if(result == null){\r
+            result = this.nameMap.get(CODE639_GLOBAL);\r
+        }\r
+\r
+        if(result == null){\r
+            result = this.nameMap.get(CODE639_PRIMARY);\r
+        }\r
+\r
+        if(result == null){\r
+            Set<String> langSet = this.nameMap.keySet();\r
+            Iterator<String> it = langSet.iterator();\r
+            while(it.hasNext()){\r
+                String lang = it.next();\r
+                result = this.nameMap.get(lang);\r
+                if(result != null) break;\r
+            }\r
+        }\r
+\r
+        if(result == null){\r
+            result = "";\r
+        }\r
+\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * 全言語の文字列を削除する。\r
+     */\r
+    public void removeAllText(){\r
+        this.nameMap.clear();\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 登録済みの全ISO639言語コードリストを返す。\r
+     * 優先度はプライマリ、グローバル、その他の順。\r
+     * @return 全ISO639言語コード\r
+     */\r
+    public List<String> lang639CodeList(){\r
+        Set<String> set = this.nameMap.keySet();\r
+        List<String> result = new ArrayList<String>(set.size());\r
+\r
+        for(String lang : set){\r
+            if(lang.equals(CODE639_PRIMARY)) result.add(lang);\r
+        }\r
+\r
+        for(String lang : set){\r
+            if(lang.equals(CODE639_GLOBAL)) result.add(lang);\r
+        }\r
+\r
+        for(String lang : set){\r
+            if(lang.equals(CODE639_PRIMARY)) continue;\r
+            if(lang.equals(CODE639_GLOBAL)) continue;\r
+            result.add(lang);\r
+        }\r
+\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * プライマリ文字列が登録されているか判定する。\r
+     * @return 登録されていればtrue\r
+     */\r
+    public boolean hasPrimaryText(){\r
+        boolean result = this.nameMap.containsKey(CODE639_PRIMARY);\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * グローバル文字列が登録されているか判定する。\r
+     * @return 登録されていればtrue\r
+     */\r
+    public boolean hasGlobalText(){\r
+        boolean result = this.nameMap.containsKey(CODE639_GLOBAL);\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * {@link #getText()}に準ずる。\r
+     * @param index {@inheritDoc}\r
+     * @return {@inheritDoc}\r
+     */\r
+    public char charAt(int index){\r
+        String text = getText();\r
+        char result = text.charAt(index);\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * {@link #getText()}に準ずる。\r
+     * @return {@inheritDoc}\r
+     */\r
+    public int length(){\r
+        String text = getText();\r
+        int result = text.length();\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * {@link #getText()}に準ずる。\r
+     * @param start {@inheritDoc}\r
+     * @param end {@inheritDoc}\r
+     * @return {@inheritDoc}\r
+     */\r
+    public CharSequence subSequence(int start, int end){\r
+        String text = getText();\r
+        CharSequence result = text.subSequence(start, end);\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * {@link #getText()}に準ずる。\r
+     * @return {@inheritDoc}\r
+     */\r
+    @Override\r
+    public String toString(){\r
+        return getText();\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..af9e099
--- /dev/null
@@ -0,0 +1,145 @@
+/*\r
+ * List utilities\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.corelib;\r
+\r
+import java.lang.reflect.Constructor;\r
+import java.lang.reflect.InvocationTargetException;\r
+import java.util.Collection;\r
+import java.util.List;\r
+\r
+/**\r
+ * リスト構造の各種ユーティリティ。\r
+ */\r
+public final class ListUtil {\r
+\r
+    /**\r
+     * 隠しコンストラクタ。\r
+     */\r
+    private ListUtil(){\r
+        assert false;\r
+        throw new AssertionError();\r
+    }\r
+\r
+    /**\r
+     * リストの出現順にシリアルナンバーを割り振る。\r
+     * シリアルナンバー先頭は0。\r
+     * @param list リスト。なるべく{@link java.util.RandomAccess}型が望ましい。\r
+     */\r
+    public static void assignIndexedSerial(\r
+            List<? extends SerialNumbered> list){\r
+        int size = list.size();\r
+        for(int idx = 0; idx < size; idx++){\r
+            SerialNumbered numbered = list.get(idx);\r
+            numbered.setSerialNumber(idx);\r
+        }\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * コレクションの要素数を拡張する。\r
+     * 追加された要素にはnullが収められる。\r
+     * コレクションがすでに指定サイズ以上の要素数を持つ場合、何もしない。\r
+     * @param <E> 型\r
+     * @param coll コレクション\r
+     * @param newSize 新サイズ\r
+     */\r
+    public static <E> void extendCollection(Collection<E> coll, int newSize){\r
+        int remain = newSize - coll.size();\r
+        if(remain <= 0) return;\r
+\r
+        for(int ct = 1; ct <= remain; ct++){\r
+            coll.add(null);\r
+        }\r
+\r
+        assert coll.size() == newSize;\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * リストのnull要素をデフォルトコンストラクタによるインスタンスで埋める。\r
+     * null要素がなければなにもしない。\r
+     * @param <E> 型\r
+     * @param list リスト\r
+     * @param cons コンストラクタ\r
+     * @return 埋めた数\r
+     */\r
+    public static <E> int fillDefCons(List<E> list,\r
+                                      Constructor<? extends E> cons){\r
+        int result = 0;\r
+\r
+        int size = list.size();\r
+        for(int pt = 0; pt < size; pt++){\r
+            E elem = list.get(pt);\r
+            if(elem != null) continue;\r
+\r
+            try{\r
+                elem = cons.newInstance();\r
+            }catch(InstantiationException e){\r
+                assert false;\r
+                throw new AssertionError(e);\r
+            }catch(IllegalAccessException e){\r
+                assert false;\r
+                throw new AssertionError(e);\r
+            }catch(InvocationTargetException e){\r
+                Throwable cause = e.getTargetException();\r
+                if(cause instanceof RuntimeException){\r
+                    throw (RuntimeException) cause;\r
+                }\r
+                if(cause instanceof Error){\r
+                    throw (Error) cause;\r
+                }\r
+                assert false;\r
+                throw new AssertionError(e);\r
+            }\r
+\r
+            list.set(pt, elem);\r
+            result++;\r
+        }\r
+\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * リストのnull要素をデフォルトコンストラクタによるインスタンスで埋める。\r
+     * @param <E> 型\r
+     * @param list リスト\r
+     * @param klass デフォルトコンストラクタの属するクラス\r
+     * @return 埋めた数\r
+     */\r
+    public static <E> int fillDefCons(List<E> list, Class<? extends E> klass){\r
+        Constructor<? extends E> cons;\r
+        try{\r
+            cons = klass.getConstructor();\r
+        }catch(NoSuchMethodException e){\r
+            assert false;\r
+            throw new AssertionError(e);\r
+        }\r
+\r
+        return fillDefCons(list, cons);\r
+    }\r
+\r
+    /**\r
+     * リスト要素数の拡張とデフォルトコンストラクタによる要素埋めを行う。\r
+     * 追加された要素および既存のnull要素には\r
+     * デフォルトコンストラクタによるインスタンスが収められる。\r
+     * @param <E> 型\r
+     * @param list リスト\r
+     * @param klass デフォルトコンストラクタの属するクラス\r
+     * @param newSize 新サイズ\r
+     * @return 埋めた数。\r
+     */\r
+    public static <E> int prepareDefConsList(List<E> list,\r
+                                             Class<? extends E> klass,\r
+                                             int newSize ){\r
+        extendCollection(list, newSize);\r
+        int result = fillDefCons(list, klass);\r
+        return result;\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..b1d9b84
--- /dev/null
@@ -0,0 +1,66 @@
+/*\r
+ * serial-numbered interface\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.corelib;\r
+\r
+import java.util.Comparator;\r
+\r
+/**\r
+ * 通し番号を持つオブジェクトの抽象化インタフェース。\r
+ */\r
+public interface SerialNumbered {\r
+\r
+    /** 昇順での比較子。 */\r
+    public Comparator<SerialNumbered> COMPARATOR = new SerialComparator();\r
+\r
+    /**\r
+     * 通し番号を設定する。\r
+     * @param num 通し番号\r
+     */\r
+    void setSerialNumber(int num);\r
+\r
+    /**\r
+     * 通し番号を返す。\r
+     * @return 通し番号\r
+     */\r
+    int getSerialNumber();\r
+\r
+    /**\r
+     * 通し番号による比較子Comparator。\r
+     * 通し番号の昇順を定義づける。\r
+     */\r
+    public static class SerialComparator\r
+            implements Comparator<SerialNumbered> {\r
+\r
+        /**\r
+         * コンストラクタ。\r
+         */\r
+        public SerialComparator(){\r
+            super();\r
+            return;\r
+        }\r
+\r
+        /**\r
+         * {@inheritDoc}\r
+         * @param o1 {@inheritDoc}\r
+         * @param o2 {@inheritDoc}\r
+         * @return {@inheritDoc}\r
+         */\r
+        public int compare(SerialNumbered o1, SerialNumbered o2){\r
+            if(o1 == o2) return 0;\r
+            if(o1 == null) return -1;\r
+            if(o2 == null) return +1;\r
+\r
+            int ser1 = o1.getSerialNumber();\r
+            int ser2 = o2.getSerialNumber();\r
+\r
+            return ser1 - ser2;\r
+        }\r
+\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..8de5060
--- /dev/null
@@ -0,0 +1,55 @@
+/*\r
+ * Windows File utils\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.corelib;\r
+\r
+/**\r
+ * Windowsに特化したFileユーティリティ。\r
+ */\r
+public final class WinFile {\r
+\r
+    public static final char SEPARATOR_CHAR = '\\';  // \\r
+    public static final String SEPARATOR =\r
+            Character.toString(SEPARATOR_CHAR);\r
+    public static final String PFX_UNC =\r
+            SEPARATOR + SEPARATOR;                   // \\\r
+\r
+    static{\r
+        assert '\\' == 0x005c;\r
+    }\r
+\r
+    /**\r
+     * 隠しコンストラクタ。\r
+     */\r
+    private WinFile(){\r
+        assert false;\r
+        throw new AssertionError();\r
+    }\r
+\r
+    /**\r
+     * Windowsファイル名の正規化を行う。\r
+     * UNCも考慮される。\r
+     * 相対パスは相対パスのまま。\r
+     * <ul>\r
+     * <li>頭の3回以上連続する\記号は2個の\記号に置き換えられる。\r
+     * <li>末尾の1回以上連続する\記号は削除。\r
+     * ただし頭から連続している場合は削除しない。\r
+     * <li>2回以上連続する\記号は1個の\記号にまとめられる。\r
+     * ただし頭から連続している場合はまとめない。\r
+     * </ul>\r
+     * @param seq 対象ファイル名\r
+     * @return 正規化されたファイル名\r
+     */\r
+    public static String normalizeWinFileName(CharSequence seq){\r
+        String text = seq.toString();\r
+        text = text.replaceAll("^\\\\{3,}", "\\\\\\\\");\r
+        text = text.replaceAll("(.*[^\\\\])\\\\+$", "$1");\r
+        text = text.replaceAll("([^\\\\])\\\\{2,}", "$1\\\\");\r
+        return text;\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..5f416f3
--- /dev/null
@@ -0,0 +1,14 @@
+/*\r
+ * package information for Javadoc\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+/**\r
+ * MikuToga共通基盤ライブラリ。\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.corelib;\r
+\r
+/* EOF */\r
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 (file)
index 0000000..52f3394
--- /dev/null
@@ -0,0 +1,122 @@
+/*\r
+ * bone group\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+import jp.sourceforge.mikutoga.corelib.I18nText;\r
+import jp.sourceforge.mikutoga.corelib.SerialNumbered;\r
+\r
+/**\r
+ * ボーングループ。\r
+ * ボーングループ名と0個以上のボーンを配下に持つ。\r
+ * 通し番号0のボーングループは、暗黙に用意される「デフォルトボーングループ」とする。\r
+ */\r
+public class BoneGroup implements SerialNumbered , Iterable<BoneInfo> {\r
+\r
+    private final I18nText groupName = new I18nText();\r
+\r
+    private final List<BoneInfo> boneList = new ArrayList<BoneInfo>();\r
+\r
+    private int serialNo = -1;\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     */\r
+    public BoneGroup(){\r
+        super();\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * ボーングループ名を返す。\r
+     * @return ボーングループ名\r
+     */\r
+    public I18nText getGroupName(){\r
+        return this.groupName;\r
+    }\r
+\r
+    /**\r
+     * ボーンリストを取得する。\r
+     * @return ボーンリスト\r
+     */\r
+    public List<BoneInfo> getBoneList(){\r
+        return this.boneList;\r
+    }\r
+\r
+    /**\r
+     * デフォルトボーングループか否か判定する。\r
+     * 通し番号が0ならデフォルトボーングループ。\r
+     * @return デフォルトボーングループならtrue\r
+     */\r
+    public boolean isDefaultBoneGroup(){\r
+        if(this.serialNo == 0) return true;\r
+        return false;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @return {@inheritDoc}\r
+     */\r
+    public Iterator<BoneInfo> iterator(){\r
+        return this.boneList.iterator();\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param num {@inheritDoc}\r
+     */\r
+    public void setSerialNumber(int num){\r
+        this.serialNo = num;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @return {@inheritDoc}\r
+     */\r
+    public int getSerialNumber(){\r
+        return this.serialNo;\r
+    }\r
+\r
+    /**\r
+     * ボーングループ番号(ボーン枠番号)を返す。\r
+     * 常に通し番号より1少ない値となる。\r
+     * デフォルトボーングループは-1となる。\r
+     * @return ボーングループ番号\r
+     */\r
+    public int getBoneGroupNumber(){\r
+        return this.serialNo - 1;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @return {@inheritDoc}\r
+     */\r
+    @Override\r
+    public String toString(){\r
+        StringBuilder result = new StringBuilder();\r
+\r
+        result.append("BoneGroup(")\r
+              .append(this.groupName)\r
+              .append(") [");\r
+\r
+        boolean dumped = false;\r
+        for(BoneInfo bone : this){\r
+            if(dumped) result.append(", ");\r
+            result.append(bone.getBoneName());\r
+            dumped = true;\r
+        }\r
+\r
+        result.append(']');\r
+\r
+        return result.toString();\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..72e3c4b
--- /dev/null
@@ -0,0 +1,202 @@
+/*\r
+ * bone information\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd;\r
+\r
+import jp.sourceforge.mikutoga.corelib.I18nText;\r
+import jp.sourceforge.mikutoga.corelib.SerialNumbered;\r
+\r
+/**\r
+ * ボーン情報。\r
+ */\r
+public class BoneInfo implements SerialNumbered {\r
+\r
+    private final I18nText boneName = new I18nText();\r
+    private BoneType boneType;\r
+\r
+    private BoneInfo prevBone;\r
+    private BoneInfo nextBone;\r
+    private BoneInfo ikBone;\r
+\r
+    private final Pos3d position = new Pos3d();\r
+\r
+    private int rotationRatio;\r
+\r
+    private int serialNo = -1;\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     */\r
+    public BoneInfo(){\r
+        super();\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * ボーン名を返す。\r
+     * @return ボーン名\r
+     */\r
+    public I18nText getBoneName(){\r
+        return this.boneName;\r
+    }\r
+\r
+    /**\r
+     * ボーン種別を設定する。\r
+     * @param type ボーン種別\r
+     * @throws NullPointerException 引数がnull\r
+     */\r
+    public void setBoneType(BoneType type) throws NullPointerException{\r
+        if(type == null) throw new NullPointerException();\r
+        this.boneType = type;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * ボーン種別を返す。\r
+     * @return ボーン種別\r
+     */\r
+    public BoneType getBoneType(){\r
+        return this.boneType;\r
+    }\r
+\r
+    /**\r
+     * 親(前)ボーンを設定する。\r
+     * @param prevBone 前ボーン。ない場合はnullを指定。\r
+     */\r
+    public void setPrevBone(BoneInfo prevBone){\r
+        this.prevBone = prevBone;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 親(前)ボーンを返す。\r
+     * @return 前ボーン。ない場合はnullを返す。\r
+     */\r
+    public BoneInfo getPrevBone(){\r
+        return this.prevBone;\r
+    }\r
+\r
+    /**\r
+     * 子(次)ボーンを設定する。\r
+     * 捩りボーンでは軸方向に位置するボーン、\r
+     * 回転連動ボーンでは影響元ボーン。\r
+     * @param nextBone 次ボーン。ない場合はnullを指定。\r
+     */\r
+    public void setNextBone(BoneInfo nextBone){\r
+        this.nextBone = nextBone;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 子(次)ボーンを返す。\r
+     * 捩りボーンでは軸方向に位置するボーン、\r
+     * 回転連動ボーンでは影響元ボーン。\r
+     * @return 次ボーン。ない場合はnullを返す。\r
+     */\r
+    public BoneInfo getNextBone(){\r
+        return this.nextBone;\r
+    }\r
+\r
+    /**\r
+     * このボーンが影響を受けるIKボーンを設定する。\r
+     * @param ikBoneArg IKボーン。ない場合はnullを指定。\r
+     */\r
+    public void setIKBone(BoneInfo ikBoneArg){\r
+        this.ikBone = ikBoneArg;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * このボーンが影響を受けるIKボーンを返す。\r
+     * @return IKボーン。ない場合はnull\r
+     */\r
+    public BoneInfo getIKBone(){\r
+        return this.ikBone;\r
+    }\r
+\r
+    /**\r
+     * ボーン位置を返す。\r
+     * @return ボーン位置\r
+     */\r
+    public Pos3d getPosition(){\r
+        return this.position;\r
+    }\r
+\r
+    /**\r
+     * 回転連動の影響度を返す。\r
+     * 回転連動ボーンの場合のみ有効。\r
+     * @return 回転連動の影響度\r
+     */\r
+    public int getRotationRatio(){\r
+        return this.rotationRatio;\r
+    }\r
+\r
+    /**\r
+     * 回転連動の影響度を設定する。\r
+     * 回転連動ボーンの場合のみ有効。\r
+     * @param ratio 回転連動の影響度\r
+     */\r
+    public void setRotationRatio(int ratio){\r
+        this.rotationRatio = ratio;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param num {@inheritDoc}\r
+     */\r
+    public void setSerialNumber(int num){\r
+        this.serialNo = num;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @return {@inheritDoc}\r
+     */\r
+    public int getSerialNumber(){\r
+        return this.serialNo;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @return {@inheritDoc}\r
+     */\r
+    @Override\r
+    public String toString(){\r
+        StringBuilder result = new StringBuilder();\r
+\r
+        result.append("Bone")\r
+              .append(this.serialNo)\r
+              .append("(")\r
+              .append(this.boneName.getPrimaryText())\r
+              .append(")");\r
+\r
+        result.append(" type=")\r
+              .append(this.boneType);\r
+\r
+        result.append(" prev=");\r
+        if(this.prevBone != null) result.append(this.prevBone.getBoneName());\r
+        else                      result.append("NONE");\r
+\r
+        result.append(" next=");\r
+        if(this.nextBone != null) result.append(this.nextBone.getBoneName());\r
+        else                      result.append("NONE");\r
+\r
+        if(this.boneType == BoneType.LINKEDROT){\r
+            result.append(" rotraio=").append(this.rotationRatio);\r
+        }else{\r
+            result.append(" ik=");\r
+            if(this.ikBone != null) result.append(this.ikBone.getBoneName());\r
+            else                    result.append("NONE");\r
+        }\r
+\r
+        result.append(" ").append(this.position);\r
+\r
+        return result.toString();\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..365b020
--- /dev/null
@@ -0,0 +1,123 @@
+/*\r
+ * bone type\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd;\r
+\r
+import java.util.Locale;\r
+import java.util.ResourceBundle;\r
+\r
+/**\r
+ * ボーン種別。\r
+ * <ul>\r
+ * <li>0x00:回転\r
+ * <li>0x01:回転/移動\r
+ * <li>0x02:IK\r
+ * <li>0x03:不明\r
+ * <li>0x04:IK影響下(回転)\r
+ * <li>0x05:回転影響下\r
+ * <li>0x06:IK接続先\r
+ * <li>0x07:非表示\r
+ * <li>0x08:捩り\r
+ * <li>0x09:回転連動\r
+ * </ul>\r
+ */\r
+public enum BoneType {\r
+\r
+    /** 回転。 */\r
+    ROTATE(0x00),\r
+    /** 回転/移動。 */\r
+    ROTMOV(0x01),\r
+    /** IK。 */\r
+    IK(0x02),\r
+    /** 不明。 */\r
+    UNKNOWN(0x03),\r
+    /** IK影響下(回転)。 */\r
+    UNDERIK(0x04),\r
+    /** 回転影響下。 */\r
+    UNDERROT(0x05),\r
+    /** IK接続先。 */\r
+    IKCONNECTED(0x06),\r
+    /** 非表示。 */\r
+    HIDDEN(0x07),\r
+    /** 捩り。 */\r
+    TWIST(0x08),\r
+    /** 回転連動。 */\r
+    LINKEDROT(0x09),\r
+    ;\r
+\r
+    private static final String FAMILY_NAME =\r
+            "jp.sourceforge.mikutoga.pmd.resources.BoneTypeName";\r
+\r
+    private final byte encoded;\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     * @param code 符号化int値\r
+     */\r
+    private BoneType(int code){\r
+        this((byte)code);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     * @param code 符号化byte値\r
+     */\r
+    private BoneType(byte code){\r
+        this.encoded = code;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * byte値からデコードする。\r
+     * @param code byte値\r
+     * @return デコードされた列挙子。該当するものがなければnull\r
+     */\r
+    public static BoneType decode(byte code){\r
+        BoneType result = null;\r
+\r
+        for(BoneType type : values()){\r
+            if(type.encode() == code){\r
+                result = type;\r
+                break;\r
+            }\r
+        }\r
+\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * byte値にエンコードする。\r
+     * @return byte値\r
+     */\r
+    public byte encode(){\r
+        return this.encoded;\r
+    }\r
+\r
+    /**\r
+     * デフォルトロケールでの表示名を返す。\r
+     * @return 表示名\r
+     */\r
+    public String getGuiName(){\r
+        Locale locale = Locale.getDefault();\r
+        return getGuiName(locale);\r
+    }\r
+\r
+    /**\r
+     * ロケールに準じた表示名を返す。\r
+     * @param locale ロケール。nullならデフォルトロケールと解釈される。\r
+     * @return 表示名\r
+     */\r
+    public String getGuiName(Locale locale){\r
+        if(locale == null) return getGuiName();\r
+        ResourceBundle rb = ResourceBundle.getBundle(FAMILY_NAME, locale);\r
+        String key = name();\r
+        String result = rb.getString(key);\r
+        return result;\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..7ae17e9
--- /dev/null
@@ -0,0 +1,95 @@
+/*\r
+ * 3d rotation (degree)\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd;\r
+\r
+/**\r
+ * XYZ3軸による回転量(degree)。\r
+ * radianではなくdegree。(直角は90.0)\r
+ */\r
+public class Deg3d {\r
+\r
+    private float xDeg;\r
+    private float yDeg;\r
+    private float zDeg;\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     */\r
+    public Deg3d(){\r
+        super();\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * X軸回転量を設定する。\r
+     * @param xDeg X軸回転量(degree)\r
+     */\r
+    public void setXDeg(float xDeg){\r
+        this.xDeg = xDeg;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * X軸回転量を返す。\r
+     * @return X軸回転量(degree)\r
+     */\r
+    public float getXDeg(){\r
+        return this.xDeg;\r
+    }\r
+\r
+    /**\r
+     * Y軸回転量を設定する。\r
+     * @param yDeg Y軸回転量(degree)\r
+     */\r
+    public void setYDeg(float yDeg){\r
+        this.yDeg = yDeg;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * Y軸回転量を返す。\r
+     * @return Y軸回転量(degree)\r
+     */\r
+    public float getYDeg(){\r
+        return this.yDeg;\r
+    }\r
+\r
+    /**\r
+     * Z軸回転量を設定する。\r
+     * @param zDeg Z軸回転量(degree)\r
+     */\r
+    public void setZDeg(float zDeg){\r
+        this.zDeg = zDeg;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * Z軸回転量を返す。\r
+     * @return Z軸回転量(degree)\r
+     */\r
+    public float getZDeg(){\r
+        return this.zDeg;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @return {@inheritDoc}\r
+     */\r
+    @Override\r
+    public String toString(){\r
+        StringBuilder result = new StringBuilder();\r
+\r
+        result.append("deg=[")\r
+              .append(this.xDeg).append(", ")\r
+              .append(this.yDeg).append(", ")\r
+              .append(this.zDeg).append(']');\r
+\r
+        return result.toString();\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..3dbcf80
--- /dev/null
@@ -0,0 +1,138 @@
+/*\r
+ * dynamics parameter\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd;\r
+\r
+/**\r
+ * 剛体間力学演算の各種パラメータ。\r
+ * 各剛体に設定可能なパラメータは\r
+ * 「質量」、「移動減衰率」、「回転減衰率」、「反発力」、「摩擦力」の5種類。\r
+ */\r
+public class DynamicsInfo {\r
+\r
+    /** 質量。 */\r
+    private float mass;\r
+    /** 移動減衰率。 */\r
+    private float dampingPos;\r
+    /** 回転減衰率。 */\r
+    private float dampingRot;\r
+    /** 反発力。 */\r
+    private float restitution;\r
+    /** 摩擦力。 */\r
+    private float friction;\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     */\r
+    public DynamicsInfo(){\r
+        super();\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 質量を返す。\r
+     * @return 質量\r
+     */\r
+    public float getMass(){\r
+        return this.mass;\r
+    }\r
+\r
+    /**\r
+     * 質量を設定する。\r
+     * @param mass 質量\r
+     */\r
+    public void setMass(float mass){\r
+        this.mass = mass;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 移動減衰率を返す。\r
+     * @return 移動減衰率\r
+     */\r
+    public float getDampingPosition(){\r
+        return this.dampingPos;\r
+    }\r
+\r
+    /**\r
+     * 移動減衰率を設定する。\r
+     * @param damping 移動減衰率\r
+     */\r
+    public void setDampingPosition(float damping){\r
+        this.dampingPos = damping;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 回転減衰率を返す。\r
+     * @return 回転減衰率\r
+     */\r
+    public float getDampingRotation(){\r
+        return this.dampingRot;\r
+    }\r
+\r
+    /**\r
+     * 回転減衰率を設定する。\r
+     * @param damping 回転減衰率\r
+     */\r
+    public void setDampingRotation(float damping){\r
+        this.dampingRot = damping;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 反発力を返す。\r
+     * @return 反発力\r
+     */\r
+    public float getRestitution(){\r
+        return this.restitution;\r
+    }\r
+\r
+    /**\r
+     * 反発力を設定する。\r
+     * @param restitution 反発力\r
+     */\r
+    public void setRestitution(float restitution){\r
+        this.restitution = restitution;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 摩擦力を返す。\r
+     * @return 摩擦力\r
+     */\r
+    public float getFriction(){\r
+        return this.friction;\r
+    }\r
+\r
+    /**\r
+     * 摩擦力を設定する。\r
+     * @param friction 摩擦力\r
+     */\r
+    public void setFriction(float friction){\r
+        this.friction = friction;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @return {@inheritDoc}\r
+     */\r
+    @Override\r
+    public String toString(){\r
+        StringBuilder result = new StringBuilder();\r
+\r
+        result.append("mass=").append(this.mass).append(", ");\r
+        result.append("damping(Pos)=").append(this.dampingPos).append(", ");\r
+        result.append("damping(Rot)=").append(this.dampingRot).append(", ");\r
+        result.append("restitution=").append(this.restitution).append(", ");\r
+        result.append("friction=").append(this.friction);\r
+\r
+        return result.toString();\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..bb3738e
--- /dev/null
@@ -0,0 +1,131 @@
+/*\r
+ * IK chained bone\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+\r
+/**\r
+ * IK連鎖ボーン群。\r
+ */\r
+public class IKChain implements Iterable<BoneInfo> {\r
+\r
+    private BoneInfo ikBone;\r
+\r
+    private int ikDepth;\r
+    private float ikWeight;\r
+\r
+    private final List<BoneInfo> chainList = new ArrayList<BoneInfo>();\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     */\r
+    public IKChain(){\r
+        super();\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * IKボーンを設定する。\r
+     * @param bone IKボーン\r
+     */\r
+    public void setIkBone(BoneInfo bone){\r
+        this.ikBone = bone;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * IKボーンを返す。\r
+     * @return IKボーン\r
+     */\r
+    public BoneInfo getIkBone(){\r
+        return this.ikBone;\r
+    }\r
+\r
+    /**\r
+     * IK演算再帰深度を設定する。\r
+     * @param depth IK演算再帰深度\r
+     */\r
+    public void setIKDepth(int depth){\r
+        this.ikDepth = depth;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * IK演算再帰深度を返す。\r
+     * @return IK演算再帰深度\r
+     */\r
+    public int getIKDepth(){\r
+        return this.ikDepth;\r
+    }\r
+\r
+    /**\r
+     * IKウェイトを設定する。\r
+     * @param weight IKウェイト\r
+     */\r
+    public void setIKWeight(float weight){\r
+        this.ikWeight = weight;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * IKウェイトを返す。\r
+     * @return IKウェイト\r
+     */\r
+    public float getIKWeight(){\r
+        return this.ikWeight;\r
+    }\r
+\r
+    /**\r
+     * IK連鎖ボーンリストを返す。\r
+     * 最初の要素は必ずIK接続先ボーン。それ以降はIK影響下ボーン。\r
+     * @return IK連鎖ボーンリスト\r
+     */\r
+    public List<BoneInfo> getChainedBoneList(){\r
+        return this.chainList;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @return {@inheritDoc}\r
+     */\r
+    public Iterator<BoneInfo> iterator(){\r
+        return this.chainList.iterator();\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @return {@inheritDoc}\r
+     */\r
+    @Override\r
+    public String toString(){\r
+        StringBuilder result = new StringBuilder();\r
+\r
+        result.append("IKChain");\r
+\r
+        result.append(" depth:").append(this.ikDepth);\r
+        result.append(" weight:").append(this.ikWeight);\r
+\r
+        result.append(" IKbone:").append(this.ikBone.getBoneName());\r
+\r
+        result.append(" [");\r
+\r
+        boolean chaindumped = false;\r
+        for(BoneInfo chain : this.chainList){\r
+            if(chaindumped) result.append(" => ");\r
+            result.append(chain.getBoneName());\r
+            chaindumped = true;\r
+        }\r
+\r
+        result.append("]");\r
+\r
+        return result.toString();\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..44165e2
--- /dev/null
@@ -0,0 +1,149 @@
+/*\r
+ * joint information\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd;\r
+\r
+import jp.sourceforge.mikutoga.corelib.I18nText;\r
+\r
+/**\r
+ * 剛体間ジョイント情報。\r
+ */\r
+public class JointInfo {\r
+\r
+    private final I18nText jointName = new I18nText();\r
+\r
+    private RigidInfo rigidA;\r
+    private RigidInfo rigidB;\r
+\r
+    private final Pos3d position = new Pos3d();\r
+    private final Rad3d rotation = new Rad3d();\r
+\r
+    private final Pos3d elaPosition = new Pos3d();\r
+    private final Deg3d elaRotation = new Deg3d();\r
+\r
+    private final TripletRange posRange = new TripletRange();\r
+    private final TripletRange rotRange = new TripletRange();\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     */\r
+    public JointInfo(){\r
+        super();\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * ジョイント名を返す。\r
+     * @return ジョイント名\r
+     */\r
+    public I18nText getJointName(){\r
+        return this.jointName;\r
+    }\r
+\r
+    /**\r
+     * 連結剛体Aを返す。\r
+     * @return 連結剛体A\r
+     */\r
+    public RigidInfo getRigidA(){\r
+        return this.rigidA;\r
+    }\r
+\r
+    /**\r
+     * 連結剛体Bを返す。\r
+     * @return 連結剛体B\r
+     */\r
+    public RigidInfo getRigidB(){\r
+        return this.rigidB;\r
+    }\r
+\r
+    /**\r
+     * 連結する剛体を設定する。\r
+     * @param rigidA 連結剛体A\r
+     * @param rigidB 連結剛体B\r
+     */\r
+    public void setRigidPair(RigidInfo rigidA, RigidInfo rigidB){\r
+        this.rigidA = rigidA;\r
+        this.rigidB = rigidB;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * ジョイントの位置を返す。\r
+     * @return ジョイントの位置\r
+     */\r
+    public Pos3d getPosition(){\r
+        return this.position;\r
+    }\r
+\r
+    /**\r
+     * ジョイントの姿勢を返す。\r
+     * @return ジョイントの姿勢\r
+     */\r
+    public Rad3d getRotation(){\r
+        return this.rotation;\r
+    }\r
+\r
+    /**\r
+     * ジョイントのバネ位置を返す。\r
+     * @return ジョイントのバネ位置\r
+     */\r
+    public Pos3d getElasticPosition(){\r
+        return this.elaPosition;\r
+    }\r
+\r
+    /**\r
+     * ジョイントのバネ姿勢を返す。\r
+     * @return ジョイントのバネ姿勢\r
+     */\r
+    public Deg3d getElasticRotation(){\r
+        return this.elaRotation;\r
+    }\r
+\r
+    /**\r
+     * ジョイントの位置制約を返す。\r
+     * @return ジョイントの位置制約\r
+     */\r
+    public TripletRange getPositionRange(){\r
+        return this.posRange;\r
+    }\r
+\r
+    /**\r
+     * ジョイントの姿勢制約を返す。\r
+     * @return ジョイントの姿勢制約\r
+     */\r
+    public TripletRange getRotationRange(){\r
+        return this.rotRange;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @return {@inheritDoc}\r
+     */\r
+    @Override\r
+    public String toString(){\r
+        StringBuilder result = new StringBuilder();\r
+\r
+        result.append("Joint ");\r
+        result.append(this.jointName);\r
+        result.append("[")\r
+              .append(this.rigidA.getRigidName())\r
+              .append("<=>")\r
+              .append(this.rigidB.getRigidName())\r
+              .append("] ");\r
+        result.append(this.position).append(' ');\r
+        result.append(this.rotation).append(' ');\r
+\r
+        result.append("poslim{").append(this.posRange).append("} ");\r
+        result.append("rotlim{").append(this.rotRange).append("} ");\r
+\r
+        result.append("ela:").append(this.elaPosition).append(' ');\r
+        result.append("ela:").append(this.elaRotation);\r
+\r
+        return result.toString();\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..c88161f
--- /dev/null
@@ -0,0 +1,245 @@
+/*\r
+ * material information\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd;\r
+\r
+import java.awt.Color;\r
+import java.awt.Transparency;\r
+import java.util.ArrayList;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+import jp.sourceforge.mikutoga.corelib.I18nText;\r
+\r
+/**\r
+ * マテリアル素材情報。\r
+ */\r
+public class Material implements Iterable<Surface> {\r
+\r
+    private final I18nText materialName = new I18nText();\r
+\r
+    private Color diffuseColor;\r
+\r
+    private Color specularColor;\r
+    private float shininess;\r
+\r
+    private Color ambientColor;\r
+\r
+    private final ShadeInfo shadeInfo = new ShadeInfo();\r
+\r
+    private boolean edgeAppearance = true;\r
+\r
+    private final List<Surface> surfaceList = new ArrayList<Surface>();\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     */\r
+    public Material(){\r
+        super();\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 色を不透明化する。\r
+     * @param color 色\r
+     * @return 不透明化した色。引数と同じ場合もありうる。\r
+     */\r
+    private static Color forceOpaque(Color color){\r
+        if(color.getTransparency() == Transparency.OPAQUE){\r
+            return color;\r
+        }\r
+\r
+        float[] rgba = new float[4];\r
+        color.getRGBColorComponents(rgba);\r
+\r
+        Color result = new Color(rgba[0], rgba[1], rgba[2], 1.0f);\r
+\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * マテリアル名を返す。\r
+     * PMDEditorのみでのサポート?\r
+     * @return マテリアル名\r
+     */\r
+    public I18nText getMaterialName(){\r
+        return this.materialName;\r
+    }\r
+\r
+    /**\r
+     * 拡散光を設定する。\r
+     * アルファ成分も反映される。\r
+     * @param color 拡散光\r
+     * @throws NullPointerException 引数がnull\r
+     */\r
+    public void setDiffuseColor(Color color) throws NullPointerException{\r
+        if(color == null) throw new NullPointerException();\r
+        this.diffuseColor = color;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 拡散光を返す。\r
+     * @return 拡散光\r
+     */\r
+    public Color getDiffuseColor(){\r
+        return this.diffuseColor;\r
+    }\r
+\r
+    /**\r
+     * 反射光を設定する。\r
+     * 透過成分があれば不透明化される。\r
+     * @param color 反射光\r
+     * @throws NullPointerException 引数がnull\r
+     */\r
+    public void setSpecularColor(Color color)\r
+            throws NullPointerException{\r
+        if(color == null) throw new NullPointerException();\r
+        this.specularColor = forceOpaque(color);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 反射光を返す。\r
+     * @return 反射光\r
+     */\r
+    public Color getSpecularColor(){\r
+        return this.specularColor;\r
+    }\r
+\r
+    /**\r
+     * 環境光を設定する。\r
+     * 透過成分があれば不透明化される。\r
+     * @param color 環境光\r
+     * @throws NullPointerException 引数がnull\r
+     */\r
+    public void setAmbientColor(Color color)\r
+            throws NullPointerException{\r
+        if(color == null) throw new NullPointerException();\r
+        this.ambientColor = forceOpaque(color);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 環境光を返す。\r
+     * @return 環境光\r
+     */\r
+    public Color getAmbientColor(){\r
+        return this.ambientColor;\r
+    }\r
+\r
+    /**\r
+     * 光沢強度を設定する。\r
+     * MMDで用いられるのは1.0〜15.0あたり。\r
+     * @param shininess 光沢強度\r
+     */\r
+    public void setShininess(float shininess){\r
+        this.shininess = shininess;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 光沢強度を返す。\r
+     * @return 光沢強度\r
+     */\r
+    public float getShininess(){\r
+        return this.shininess;\r
+    }\r
+\r
+    /**\r
+     * シェーディング設定を返す。\r
+     * @return シェーディング設定\r
+     */\r
+    public ShadeInfo getShadeInfo(){\r
+        return this.shadeInfo;\r
+    }\r
+\r
+    /**\r
+     * エッジを表示するか設定する。\r
+     * 頂点単位の設定より優先度は低い。\r
+     * @param show 表示するならtrue\r
+     */\r
+    public void setEdgeAppearance(boolean show){\r
+        this.edgeAppearance = show;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * エッジを表示するか判定する。\r
+     * 頂点単位の設定より優先度は低い。\r
+     * @return 表示するならtrue\r
+     */\r
+    public boolean getEdgeAppearance(){\r
+        return this.edgeAppearance;\r
+    }\r
+\r
+    /**\r
+     * 面リストを返す。\r
+     * @return 面リスト\r
+     */\r
+    public List<Surface> getSurfaceList(){\r
+        return this.surfaceList;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @return {@inheritDoc}\r
+     */\r
+    public Iterator<Surface> iterator(){\r
+        return this.surfaceList.iterator();\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @return {@inheritDoc}\r
+     */\r
+    @Override\r
+    public String toString(){\r
+        StringBuilder result = new StringBuilder();\r
+\r
+        result.append("Material ");\r
+\r
+        float[] rgba = new float[4];\r
+\r
+        this.diffuseColor.getRGBComponents(rgba);\r
+        result.append("diffuse=[")\r
+              .append(rgba[0]).append(", ")\r
+              .append(rgba[1]).append(", ")\r
+              .append(rgba[2]).append(", ")\r
+              .append(rgba[3]).append(']')\r
+              .append(' ');\r
+\r
+        this.specularColor.getRGBComponents(rgba);\r
+        result.append("specular=[")\r
+              .append(rgba[0]).append(", ")\r
+              .append(rgba[1]).append(", ")\r
+              .append(rgba[2]).append(", ")\r
+              .append(rgba[3]).append(']')\r
+              .append(' ');\r
+\r
+        this.ambientColor.getRGBComponents(rgba);\r
+        result.append("ambient=[")\r
+              .append(rgba[0]).append(", ")\r
+              .append(rgba[1]).append(", ")\r
+              .append(rgba[2]).append(", ")\r
+              .append(rgba[3]).append(']')\r
+              .append(' ');\r
+\r
+        result.append("shininess=")\r
+              .append(this.shininess)\r
+              .append(' ');\r
+        result.append("edge=")\r
+              .append(this.edgeAppearance)\r
+              .append(' ');\r
+        result.append("surfaces=")\r
+              .append(this.surfaceList.size())\r
+              .append(' ');\r
+        result.append(this.shadeInfo);\r
+\r
+        return result.toString();\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..50ac72b
--- /dev/null
@@ -0,0 +1,109 @@
+/*\r
+ * morph information\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+import jp.sourceforge.mikutoga.corelib.I18nText;\r
+import jp.sourceforge.mikutoga.corelib.SerialNumbered;\r
+\r
+/**\r
+ * 個別モーフ情報。\r
+ */\r
+public class MorphPart implements SerialNumbered, Iterable<MorphVertex> {\r
+\r
+    private final I18nText morphName = new I18nText();\r
+\r
+    private MorphType type;\r
+\r
+    private final List<MorphVertex> morphVertexList =\r
+            new ArrayList<MorphVertex>();\r
+\r
+    private int serialNo = -1;\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     */\r
+    public MorphPart(){\r
+        super();\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * モーフ名を返す。\r
+     * @return モーフ名\r
+     */\r
+    public I18nText getMorphName(){\r
+        return this.morphName;\r
+    }\r
+\r
+    /**\r
+     * モーフ種別を設定する。\r
+     * @param typeArg モーフ種別\r
+     * @throws NullPointerException 引数がnull\r
+     */\r
+    public void setMorphType(MorphType typeArg) throws NullPointerException{\r
+        if(typeArg == null) throw new NullPointerException();\r
+        this.type = typeArg;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * モーフ種別を返す。\r
+     * @return モーフ種別。\r
+     */\r
+    public MorphType getMorphType(){\r
+        return this.type;\r
+    }\r
+\r
+    /**\r
+     * モーフ頂点情報リストを返す。\r
+     * @return モーフ頂点情報リスト\r
+     */\r
+    public List<MorphVertex> getMorphVertexList(){\r
+        return this.morphVertexList;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @return {@inheritDoc}\r
+     */\r
+    public Iterator<MorphVertex> iterator(){\r
+        return this.morphVertexList.iterator();\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param num {@inheritDoc}\r
+     */\r
+    public void setSerialNumber(int num){\r
+        this.serialNo = num;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @return {@inheritDoc}\r
+     */\r
+    public int getSerialNumber(){\r
+        return this.serialNo;\r
+    }\r
+\r
+    @Override\r
+    public String toString(){\r
+        StringBuilder result = new StringBuilder();\r
+\r
+        result.append("Morph(").append(this.morphName).append(") ");\r
+        result.append("type=").append(this.type);\r
+        result.append(" vertexNum=").append(this.morphVertexList.size());\r
+\r
+        return result.toString();\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..5dba949
--- /dev/null
@@ -0,0 +1,117 @@
+/*\r
+ * morph type\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd;\r
+\r
+import java.util.Locale;\r
+import java.util.ResourceBundle;\r
+\r
+/**\r
+ * モーフ種別。\r
+ * <ul>\r
+ * <li>0:base\r
+ * <li>1:まゆ\r
+ * <li>2:目\r
+ * <li>3:リップ\r
+ * <li>4:その他\r
+ * </ul>\r
+ */\r
+public enum MorphType {\r
+\r
+    /** base。 */\r
+    BASE(0x00),\r
+    /** まゆ。 */\r
+    EYEBROW(0x01),\r
+    /** 目。 */\r
+    EYE(0x02),\r
+    /** リップ。 */\r
+    LIP(0x03),\r
+    /** その他。 */\r
+    EXTRA(0x04),\r
+    ;\r
+\r
+    private static final String FAMILY_NAME =\r
+            "jp.sourceforge.mikutoga.pmd.resources.MorphTypeName";\r
+\r
+    private final byte encoded;\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     * @param code 符号化int値\r
+     */\r
+    private MorphType(int code){\r
+        this((byte)code);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     * @param code 符号化byte値\r
+     */\r
+    private MorphType(byte code){\r
+        this.encoded = code;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * byte値からデコードする。\r
+     * @param code byte値\r
+     * @return デコードされた列挙子。該当するものがなければnull\r
+     */\r
+    public static MorphType decode(byte code){\r
+        MorphType result = null;\r
+\r
+        for(MorphType type : values()){\r
+            if(type.encode() == code){\r
+                result = type;\r
+                break;\r
+            }\r
+        }\r
+\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * byte値にエンコードする。\r
+     * @return byte値\r
+     */\r
+    public byte encode(){\r
+        return this.encoded;\r
+    }\r
+\r
+    /**\r
+     * デフォルトロケールでの表示名を返す。\r
+     * @return 表示名\r
+     */\r
+    public String getGuiName(){\r
+        Locale locale = Locale.getDefault();\r
+        return getGuiName(locale);\r
+    }\r
+\r
+    /**\r
+     * ロケールに準じた表示名を返す。\r
+     * @param locale ロケール。nullならデフォルトロケールと解釈される。\r
+     * @return 表示名\r
+     */\r
+    public String getGuiName(Locale locale){\r
+        if(locale == null) return getGuiName();\r
+        ResourceBundle rb = ResourceBundle.getBundle(FAMILY_NAME, locale);\r
+        String key = name();\r
+        String result = rb.getString(key);\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * モーフ種別がbaseか否か判定する。\r
+     * @return baseならtrue\r
+     */\r
+    public boolean isBase(){\r
+        if(this == BASE) return true;\r
+        return false;\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..719c3dd
--- /dev/null
@@ -0,0 +1,130 @@
+/*\r
+ * morph vertex information\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd;\r
+\r
+import java.util.Comparator;\r
+import jp.sourceforge.mikutoga.corelib.SerialNumbered;\r
+\r
+/**\r
+ * モーフアニメーションを構成する個別の頂点移動の情報。\r
+ */\r
+public class MorphVertex implements SerialNumbered{\r
+\r
+    /** 頂点IDを昇順に順序づけるComaparator。 */\r
+    public static final Comparator<MorphVertex> VIDCOMPARATOR =\r
+            new VertexIdComparator();\r
+\r
+    private Vertex baseVertex;\r
+    private final Pos3d offset = new Pos3d();\r
+\r
+    private int serialNo = -1;\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     */\r
+    public MorphVertex(){\r
+        super();\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 移動元頂点情報を返す。\r
+     * @return 移動元頂点\r
+     */\r
+    public Vertex getBaseVertex(){\r
+        return this.baseVertex;\r
+    }\r
+\r
+    /**\r
+     * 移動元頂点情報を設定する。\r
+     * @param vertex 移動元頂点\r
+     * @throws NullPointerException 引数がnull\r
+     */\r
+    public void setBaseVertex(Vertex vertex) throws NullPointerException{\r
+        if(vertex == null) throw new NullPointerException();\r
+        this.baseVertex = vertex;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 頂点移動量を返す。\r
+     * @return 頂点移動量\r
+     */\r
+    public Pos3d getOffset(){\r
+        return this.offset;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param num {@inheritDoc}\r
+     */\r
+    public void setSerialNumber(int num){\r
+        this.serialNo = num;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @return {@inheritDoc}\r
+     */\r
+    public int getSerialNumber(){\r
+        return this.serialNo;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @return {@inheritDoc}\r
+     */\r
+    @Override\r
+    public String toString(){\r
+        StringBuilder result = new StringBuilder();\r
+\r
+        result.append("vid(")\r
+              .append(this.baseVertex.getSerialNumber())\r
+              .append(") ");\r
+        result.append(this.baseVertex.getPosition());\r
+        result.append(" >> ");\r
+        result.append(this.offset);\r
+\r
+        return result.toString();\r
+    }\r
+\r
+    /**\r
+     * 頂点IDによる比較子Comparator。\r
+     */\r
+    private static final class VertexIdComparator\r
+            implements Comparator<MorphVertex> {\r
+\r
+        /**\r
+         * コンストラクタ。\r
+         */\r
+        private VertexIdComparator(){\r
+            super();\r
+            return;\r
+        }\r
+\r
+        /**\r
+         * {@inheritDoc}\r
+         * @param o1 {@inheritDoc}\r
+         * @param o2 {@inheritDoc}\r
+         * @return {@inheritDoc}\r
+         */\r
+        public int compare(MorphVertex o1, MorphVertex o2){\r
+            if(o1 == o2) return 0;\r
+            if(o1 == null) return -1;\r
+            if(o2 == null) return +1;\r
+\r
+            int ser1 = o1.getBaseVertex().getSerialNumber();\r
+            int ser2 = o2.getBaseVertex().getSerialNumber();\r
+\r
+            return ser1 - ser2;\r
+        }\r
+\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..d6dd882
--- /dev/null
@@ -0,0 +1,381 @@
+/*\r
+ * PMD model\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collections;\r
+import java.util.EnumMap;\r
+import java.util.HashMap;\r
+import java.util.HashSet;\r
+import java.util.LinkedList;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.RandomAccess;\r
+import java.util.Set;\r
+import jp.sourceforge.mikutoga.corelib.I18nText;\r
+\r
+/**\r
+ * PMDモデルファイル一式に相当するもの。\r
+ * 様々な基本構造のリストの集合から構成される。\r
+ */\r
+public class PmdModel {\r
+\r
+    /** デフォルトのヘッダバージョン。 */\r
+    public static final float DEF_HEADERVER = 1.0f;\r
+\r
+    private float headerVersion = DEF_HEADERVER;\r
+\r
+    private final I18nText modelName = new I18nText();\r
+    private final I18nText description = new I18nText();\r
+\r
+    private final List<Vertex> vertexList = new ArrayList<Vertex>();\r
+    private final List<Surface> surfaceList = new ArrayList<Surface>();\r
+    private final List<Material> materialList = new LinkedList<Material>();\r
+\r
+    private final List<BoneInfo> boneList = new ArrayList<BoneInfo>();\r
+    private final List<BoneGroup> boneGroupList = new ArrayList<BoneGroup>();\r
+\r
+    private final List<IKChain> ikChainList = new ArrayList<IKChain>();\r
+\r
+    private final Map<MorphType, List<MorphPart>> morphMap =\r
+            new EnumMap<MorphType, List<MorphPart>>(MorphType.class);\r
+\r
+    private final List<RigidInfo> rigidList = new ArrayList<RigidInfo>();\r
+    private final List<RigidGroup> rigidGroupList =\r
+            new ArrayList<RigidGroup>();\r
+\r
+    private final List<JointInfo> jointList = new ArrayList<JointInfo>();\r
+\r
+    private ToonMap toonMap = new ToonMap();\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     */\r
+    public PmdModel(){\r
+        super();\r
+\r
+        assert this.vertexList instanceof RandomAccess;\r
+        assert this.surfaceList instanceof RandomAccess;\r
+\r
+        this.morphMap.put(MorphType.EYEBROW, new ArrayList<MorphPart>());\r
+        this.morphMap.put(MorphType.EYE,     new ArrayList<MorphPart>());\r
+        this.morphMap.put(MorphType.LIP,     new ArrayList<MorphPart>());\r
+        this.morphMap.put(MorphType.EXTRA,   new ArrayList<MorphPart>());\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * PMDファイルのヘッダバージョンを返す。\r
+     * @return PMDファイルのヘッダバージョン\r
+     */\r
+    public float getHeaderVersion(){\r
+        return this.headerVersion;\r
+    }\r
+\r
+    /**\r
+     * PMDファイルのヘッダバージョンを設定する。\r
+     * @param ver PMDファイルのヘッダバージョン\r
+     */\r
+    public void setHeaderVersion(float ver){\r
+        this.headerVersion = ver;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * モデル名を返す。\r
+     * @return モデル名\r
+     */\r
+    public I18nText getModelName(){\r
+        return this.modelName;\r
+    }\r
+\r
+    /**\r
+     * モデル説明文を返す。\r
+     * 改行表現には{@literal \n}が用いられる\r
+     * @return モデル説明文\r
+     */\r
+    public I18nText getDescription(){\r
+        return this.description;\r
+    }\r
+\r
+    /**\r
+     * 頂点リストを返す。\r
+     * @return 頂点リスト。\r
+     */\r
+    public List<Vertex> getVertexList(){\r
+        return this.vertexList;\r
+    }\r
+\r
+    /**\r
+     * 面リストを返す。\r
+     * @return 面リスト\r
+     */\r
+    public List<Surface> getSurfaceList(){\r
+        return this.surfaceList;\r
+    }\r
+\r
+    /**\r
+     * 素材リストを返す。\r
+     * @return 素材リスト\r
+     */\r
+    public List<Material> getMaterialList(){\r
+        return this.materialList;\r
+    }\r
+\r
+    /**\r
+     * ボーンリストを返す。\r
+     * @return ボーンリスト\r
+     */\r
+    public List<BoneInfo> getBoneList(){\r
+        return this.boneList;\r
+    }\r
+\r
+    /**\r
+     * ボーングループリストを返す。\r
+     * @return ボーングループリスト\r
+     */\r
+    public List<BoneGroup> getBoneGroupList(){\r
+        return this.boneGroupList;\r
+    }\r
+\r
+    /**\r
+     * IKチェーンリストを返す。\r
+     * @return IKチェーンリスト\r
+     */\r
+    public List<IKChain> getIKChainList(){\r
+        return this.ikChainList;\r
+    }\r
+\r
+    /**\r
+     * 種類別モーフリストのマップを返す。\r
+     * @return 種類別モーフリストのマップ\r
+     */\r
+    public Map<MorphType, List<MorphPart>> getMorphMap(){\r
+        return this.morphMap;\r
+    }\r
+\r
+    /**\r
+     * 剛体リストを返す。\r
+     * @return 剛体リスト\r
+     */\r
+    public List<RigidInfo> getRigidList(){\r
+        return this.rigidList;\r
+    }\r
+\r
+    /**\r
+     * 剛体グループリストを返す。\r
+     * @return 剛体グループリスト。\r
+     */\r
+    public List<RigidGroup> getRigidGroupList(){\r
+        return this.rigidGroupList;\r
+    }\r
+\r
+    /**\r
+     * 剛体間ジョイントリストを返す。\r
+     * @return 剛体間ジョイントリスト\r
+     */\r
+    public List<JointInfo> getJointList(){\r
+        return this.jointList;\r
+    }\r
+\r
+    /**\r
+     * トゥーンファイルマップを返す。\r
+     * @return トゥーンファイルマップ\r
+     */\r
+    public ToonMap getToonMap(){\r
+        return this.toonMap;\r
+    }\r
+\r
+    /**\r
+     * トゥーンファイルマップを設定する。\r
+     * 各素材のシェーディングで参照するトゥーンファイルマップも更新される。\r
+     * @param map トゥーンファイルマップ\r
+     */\r
+    public void setToonMap(ToonMap map){\r
+        this.toonMap = map;\r
+        for(Material material : this.materialList){\r
+            ShadeInfo info = material.getShadeInfo();\r
+            info.setToonMap(this.toonMap);\r
+        }\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * このモデルがグローバル名を含むか判定する。\r
+     * ボーン名、ボーングループ名、モーフ名、モデル説明文が判定対象。\r
+     * @return グローバル名を持つならtrue\r
+     */\r
+    public boolean hasGlobalText(){\r
+        if(this.modelName.hasGlobalText()) return true;\r
+        if(this.description.hasGlobalText()) return true;\r
+\r
+        for(BoneInfo bone : this.boneList){\r
+            if(bone.getBoneName().hasGlobalText()) return true;\r
+        }\r
+\r
+        List<MorphType> typeList = new ArrayList<MorphType>();\r
+        typeList.addAll(this.morphMap.keySet());\r
+        for(MorphType type : typeList){\r
+            List<MorphPart> partList = this.morphMap.get(type);\r
+            for(MorphPart part : partList){\r
+                if(part.getMorphName().hasGlobalText()) return true;\r
+            }\r
+        }\r
+\r
+        for(BoneGroup group : this.boneGroupList){\r
+            if(group.getGroupName().hasGlobalText()) return true;\r
+        }\r
+\r
+        return false;\r
+    }\r
+\r
+    /**\r
+     * モーフで使われる全てのモーフ頂点のリストを返す。\r
+     * モーフ間で重複する頂点はマージされる。\r
+     * 頂点IDでソートされる。\r
+     * <p>\r
+     * 0から始まる通し番号がリナンバリングされる。\r
+     * 通し番号は返されるモーフ頂点リストの添え字番号と一致する。\r
+     * @return モーフに使われるモーフ頂点のリスト\r
+     */\r
+    public List<MorphVertex> mergeMorphVertex(){\r
+        List<MorphVertex> result = new ArrayList<MorphVertex>();\r
+\r
+        Set<Vertex> mergedVertexSet = new HashSet<Vertex>();\r
+        for(MorphType type : this.morphMap.keySet()){\r
+            if(type.isBase()) continue;\r
+            List<MorphPart> partList = this.morphMap.get(type);\r
+            if(partList == null) continue;\r
+            for(MorphPart part : partList){\r
+                for(MorphVertex morphVertex : part){\r
+                    Vertex vertex = morphVertex.getBaseVertex();\r
+                    if(mergedVertexSet.contains(vertex)) continue;\r
+                    mergedVertexSet.add(vertex);\r
+                    result.add(morphVertex);\r
+                }\r
+            }\r
+        }\r
+\r
+        Collections.sort(result, MorphVertex.VIDCOMPARATOR);\r
+        for(int idx = 0; idx < result.size(); idx++){\r
+            MorphVertex morphVertex = result.get(idx);\r
+            morphVertex.setSerialNumber(idx);\r
+        }\r
+\r
+        Map<Vertex, MorphVertex> numberedMap =\r
+                new HashMap<Vertex, MorphVertex>();\r
+        for(MorphVertex morphVertex : result){\r
+            numberedMap.put(morphVertex.getBaseVertex(), morphVertex);\r
+        }\r
+\r
+        for(MorphType type : this.morphMap.keySet()){\r
+            if(type.isBase()) continue;\r
+            List<MorphPart> partList = this.morphMap.get(type);\r
+            if(partList == null) continue;\r
+            for(MorphPart part : partList){\r
+                for(MorphVertex morphVertex : part){\r
+                    Vertex vertex = morphVertex.getBaseVertex();\r
+                    MorphVertex numbered = numberedMap.get(vertex);\r
+                    assert numbered != null;\r
+                    morphVertex.setSerialNumber(numbered.getSerialNumber());\r
+                }\r
+            }\r
+        }\r
+\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * 永続化可能な状態へトリミングする。\r
+     * 各種オブジェクトの通し番号が変化する可能性がある。\r
+     */\r
+    public void trimming(){\r
+        List<Surface> trimmedSurfaceList = trimmingSurfaceList();\r
+        this.surfaceList.clear();\r
+        this.surfaceList.addAll(trimmedSurfaceList);\r
+\r
+        List<Vertex> trimmedVertexList = trimmingVertexList();\r
+        this.vertexList.clear();\r
+        this.vertexList.addAll(trimmedVertexList);\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 面リストをトリミングする。\r
+     * 所属マテリアル順に再配置し、通し番号を割り振り直す。\r
+     * 所属マテリアルの無い面はリストの末端に配置される。\r
+     * 面リスト中のnullは削除され詰められる。\r
+     * @return トリミングされた面リスト\r
+     */\r
+    private List<Surface> trimmingSurfaceList(){\r
+        Set<Surface> materialedSurfaceSet = new HashSet<Surface>();\r
+        for(Material material : this.materialList){\r
+            if(material == null) continue;\r
+            for(Surface surface : material){\r
+                if(surface == null) continue;\r
+                materialedSurfaceSet.add(surface);\r
+            }\r
+        }\r
+\r
+        materialedSurfaceSet.removeAll(this.surfaceList);\r
+\r
+        List<Surface> result = new ArrayList<Surface>();\r
+        for(Surface surface : this.surfaceList){\r
+            if(surface == null) continue;\r
+            result.add(surface);\r
+        }\r
+\r
+        result.addAll(materialedSurfaceSet);\r
+\r
+        int serialNum = 0;\r
+        for(Surface surface : result){\r
+            surface.setSerialNumber(serialNum);\r
+            serialNum++;\r
+        }\r
+\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * 頂点リストをトリミングする。\r
+     * 通し番号を振り直す。\r
+     * 所属面の無い頂点はリストの末端に配置される。\r
+     * 頂点リスト中のnullは削除され詰められる。\r
+     * @return トリミングされた頂点リスト\r
+     */\r
+    private List<Vertex> trimmingVertexList(){\r
+        Set<Vertex> surfacedVertexSet = new HashSet<Vertex>();\r
+        for(Surface surface : this.surfaceList){\r
+            if(surface == null) continue;\r
+            for(Vertex vertex : surface){\r
+                surfacedVertexSet.add(vertex);\r
+            }\r
+        }\r
+\r
+        surfacedVertexSet.removeAll(this.vertexList);\r
+\r
+        List<Vertex> result = new ArrayList<Vertex>();\r
+        for(Vertex vertex : this.vertexList){\r
+            if(vertex == null) continue;\r
+            result.add(vertex);\r
+        }\r
+\r
+        result.addAll(surfacedVertexSet);\r
+\r
+        int serialNum = 0;\r
+        for(Vertex vertex : result){\r
+            vertex.setSerialNumber(serialNum);\r
+            serialNum++;\r
+        }\r
+\r
+        return result;\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..dd1643d
--- /dev/null
@@ -0,0 +1,88 @@
+/*\r
+ * 2D position\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd;\r
+\r
+/**\r
+ * 二次元空間座標及び変量を表す。\r
+ */\r
+public class Pos2d {\r
+\r
+    private float xPos;\r
+    private float yPos;\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     * [0,0]が設定される\r
+     */\r
+    public Pos2d(){\r
+        this(0.0f, 0.0f);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     * @param xPos X座標\r
+     * @param yPos Y座標\r
+     */\r
+    public Pos2d(float xPos, float yPos){\r
+        super();\r
+        this.xPos = xPos;\r
+        this.yPos = yPos;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * X座標を設定する。\r
+     * @param xPos X座標\r
+     */\r
+    public void setXPos(float xPos){\r
+        this.xPos = xPos;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * X座標を返す。\r
+     * @return X座標\r
+     */\r
+    public float getXPos(){\r
+        return this.xPos;\r
+    }\r
+\r
+    /**\r
+     * Y座標を設定する。\r
+     * @param yPos Y座標\r
+     */\r
+    public void setYPos(float yPos){\r
+        this.yPos = yPos;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * Y座標を返す。\r
+     * @return Y座標\r
+     */\r
+    public float getYPos(){\r
+        return this.yPos;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @return {@inheritDoc}\r
+     */\r
+    @Override\r
+    public String toString(){\r
+        StringBuilder result = new StringBuilder();\r
+\r
+        result.append("pos=[")\r
+              .append(this.xPos).append(", ")\r
+              .append(this.yPos).append(']');\r
+\r
+        return result.toString();\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..aa5dbfe
--- /dev/null
@@ -0,0 +1,109 @@
+/*\r
+ * 3D position\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd;\r
+\r
+/**\r
+ * 三次元空間座標及び変量を表す。\r
+ */\r
+public class Pos3d {\r
+\r
+    private float xPos;\r
+    private float yPos;\r
+    private float zPos;\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     * [0,0,0]が設定される。\r
+     */\r
+    public Pos3d(){\r
+        this(0.0f, 0.0f, 0.0f);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     * @param xPos X座標\r
+     * @param yPos Y座標\r
+     * @param zPos Z座標\r
+     */\r
+    public Pos3d(float xPos, float yPos, float zPos){\r
+        super();\r
+        this.xPos = xPos;\r
+        this.yPos = yPos;\r
+        this.zPos = zPos;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * X座標を設定する。\r
+     * @param xPos X座標\r
+     */\r
+    public void setXPos(float xPos){\r
+        this.xPos = xPos;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * X座標を返す。\r
+     * @return X座標\r
+     */\r
+    public float getXPos(){\r
+        return this.xPos;\r
+    }\r
+\r
+    /**\r
+     * Y座標を設定する。\r
+     * @param yPos Y座標\r
+     */\r
+    public void setYPos(float yPos){\r
+        this.yPos = yPos;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * Y座標を返す。\r
+     * @return Y座標\r
+     */\r
+    public float getYPos(){\r
+        return this.yPos;\r
+    }\r
+\r
+    /**\r
+     * Z座標を設定する。\r
+     * @param zPos Z座標\r
+     */\r
+    public void setZPos(float zPos){\r
+        this.zPos = zPos;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * Z座標を返す。\r
+     * @return Z座標\r
+     */\r
+    public float getZPos(){\r
+        return this.zPos;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @return {@inheritDoc}\r
+     */\r
+    @Override\r
+    public String toString(){\r
+        StringBuilder result = new StringBuilder();\r
+\r
+        result.append("pos=[")\r
+              .append(this.xPos).append(", ")\r
+              .append(this.yPos).append(", ")\r
+              .append(this.zPos).append(']');\r
+\r
+        return result.toString();\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..539dfc9
--- /dev/null
@@ -0,0 +1,95 @@
+/*\r
+ * 3d rotation (radian)\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd;\r
+\r
+/**\r
+ * XYZ3軸による回転量(radian)。\r
+ * degereeではなくradian。(直角はΠ/2)\r
+ */\r
+public class Rad3d {\r
+\r
+    private float xRad;\r
+    private float yRad;\r
+    private float zRad;\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     */\r
+    public Rad3d(){\r
+        super();\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * X軸回転量を設定する。\r
+     * @param xRad X軸回転量(radian)\r
+     */\r
+    public void setXRad(float xRad){\r
+        this.xRad = xRad;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * X軸回転量を返す。\r
+     * @return X軸回転量(radian)\r
+     */\r
+    public float getXRad(){\r
+        return this.xRad;\r
+    }\r
+\r
+    /**\r
+     * Y軸回転量を設定する。\r
+     * @param yRad Y軸回転量(radian)\r
+     */\r
+    public void setYRad(float yRad){\r
+        this.yRad = yRad;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * Y軸回転量を返す。\r
+     * @return Y軸回転量(radian)\r
+     */\r
+    public float getYRad(){\r
+        return this.yRad;\r
+    }\r
+\r
+    /**\r
+     * Z軸回転量を設定する。\r
+     * @param zRad Z軸回転量(radian)\r
+     */\r
+    public void setZRad(float zRad){\r
+        this.zRad = zRad;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * Z軸回転量を返す。\r
+     * @return Z軸回転量(radian)\r
+     */\r
+    public float getZRad(){\r
+        return this.zRad;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @return {@inheritDoc}\r
+     */\r
+    @Override\r
+    public String toString(){\r
+        StringBuilder result = new StringBuilder();\r
+\r
+        result.append("rad=[")\r
+              .append(this.xRad).append(", ")\r
+              .append(this.yRad).append(", ")\r
+              .append(this.zRad).append(']');\r
+\r
+        return result.toString();\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..a17879d
--- /dev/null
@@ -0,0 +1,102 @@
+/*\r
+ * rigid behavior type\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd;\r
+\r
+import java.util.Locale;\r
+import java.util.ResourceBundle;\r
+\r
+/**\r
+ * 剛体の振る舞い種別。\r
+ * <ul>\r
+ * <li>0x00:ボーン追従\r
+ * <li>0x01:物理演算\r
+ * <li>0x02:物理演算+ボーン位置合わせ\r
+ * </ul>\r
+ */\r
+public enum RigidBehaviorType {\r
+\r
+    /** ボーン追従。 */\r
+    FOLLOWBONE(0x00),\r
+    /** 物理演算。 */\r
+    ONLYDYNAMICS(0x01),\r
+    /** 物理演算+ボーン位置合わせ。 */\r
+    BONEDDYNAMICS(0x02),\r
+    ;\r
+\r
+    private static final String FAMILY_NAME =\r
+        "jp.sourceforge.mikutoga.pmd.resources.RigidBehaviorTypeName";\r
+\r
+    private final byte encoded;\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     * @param code 符号化int値\r
+     */\r
+    private RigidBehaviorType(int code){\r
+        this((byte)code);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     * @param code 符号化byte値\r
+     */\r
+    private RigidBehaviorType(byte code){\r
+        this.encoded = code;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * byte値からデコードする。\r
+     * @param code byte値\r
+     * @return デコードされた列挙子。該当するものがなければnull\r
+     */\r
+    public static RigidBehaviorType decode(byte code){\r
+        RigidBehaviorType result = null;\r
+\r
+        for(RigidBehaviorType type : values()){\r
+            if(type.encode() == code){\r
+                result = type;\r
+                break;\r
+            }\r
+        }\r
+\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * byte値にエンコードする。\r
+     * @return byte値\r
+     */\r
+    public byte encode(){\r
+        return this.encoded;\r
+    }\r
+\r
+    /**\r
+     * デフォルトロケールでの表示名を返す。\r
+     * @return 表示名\r
+     */\r
+    public String getGuiName(){\r
+        Locale locale = Locale.getDefault();\r
+        return getGuiName(locale);\r
+    }\r
+\r
+    /**\r
+     * ロケールに準じた表示名を返す。\r
+     * @param locale ロケール。nullならデフォルトロケールと解釈される。\r
+     * @return 表示名\r
+     */\r
+    public String getGuiName(Locale locale){\r
+        if(locale == null) return getGuiName();\r
+        ResourceBundle rb = ResourceBundle.getBundle(FAMILY_NAME, locale);\r
+        String key = name();\r
+        String result = rb.getString(key);\r
+        return result;\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..a57a301
--- /dev/null
@@ -0,0 +1,99 @@
+/*\r
+ * rigid group\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+import jp.sourceforge.mikutoga.corelib.SerialNumbered;\r
+\r
+/**\r
+ * 剛体グループ情報。\r
+ * 剛体間の衝突設定の対象となる。\r
+ */\r
+public class RigidGroup implements SerialNumbered, Iterable<RigidInfo> {\r
+\r
+    private final List<RigidInfo> rigidList = new ArrayList<RigidInfo>();\r
+\r
+    private int serialNo = -1;\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     */\r
+    public RigidGroup(){\r
+        super();\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 所属する剛体のリストを返す。\r
+     * @return 剛体リスト\r
+     */\r
+    public List<RigidInfo> getRigidList(){\r
+        return this.rigidList;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @return {@inheritDoc}\r
+     */\r
+    public Iterator<RigidInfo> iterator(){\r
+        return this.rigidList.iterator();\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param num {@inheritDoc}\r
+     */\r
+    public void setSerialNumber(int num){\r
+        this.serialNo = num;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @return {@inheritDoc}\r
+     */\r
+    public int getSerialNumber(){\r
+        return this.serialNo;\r
+    }\r
+\r
+    /**\r
+     * グループ番号を返す。\r
+     * MMDでは1〜16までが使われる。\r
+     * 通し番号に1を加えた値と等しい。\r
+     * @return グループ番号\r
+     */\r
+    public int getGroupNumber(){\r
+        return this.serialNo + 1;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @return {@inheritDoc}\r
+     */\r
+    @Override\r
+    public String toString(){\r
+        StringBuilder result = new StringBuilder();\r
+\r
+        result.append("RigidGroup(").append(getGroupNumber()).append(") [");\r
+\r
+        boolean dumped;\r
+\r
+        dumped = false;\r
+        for(RigidInfo rigid : this.rigidList){\r
+            if(dumped) result.append(", ");\r
+            result.append(rigid.getRigidName());\r
+            dumped = true;\r
+        }\r
+        result.append(']');\r
+\r
+        return result.toString();\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..b734602
--- /dev/null
@@ -0,0 +1,196 @@
+/*\r
+ * rigid information\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import jp.sourceforge.mikutoga.corelib.I18nText;\r
+import jp.sourceforge.mikutoga.corelib.SerialNumbered;\r
+\r
+/**\r
+ * 個別の剛体の情報。\r
+ */\r
+public class RigidInfo implements SerialNumbered {\r
+\r
+    private final I18nText rigidName = new I18nText();\r
+\r
+    private RigidBehaviorType behaviorType = RigidBehaviorType.FOLLOWBONE;\r
+\r
+    private final RigidShape rigidShape = new RigidShape();\r
+    private final Pos3d position = new Pos3d();\r
+    private final Rad3d rotation = new Rad3d();\r
+\r
+    private BoneInfo linkedBone;\r
+\r
+    private final DynamicsInfo dynamicsInfo = new DynamicsInfo();\r
+\r
+    private final Collection<RigidGroup> throughGroupColl =\r
+            new ArrayList<RigidGroup>();\r
+\r
+    private RigidGroup rigidGroup;\r
+\r
+    private int serialNo = -1;\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     */\r
+    public RigidInfo(){\r
+        super();\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 剛体名を返す。\r
+     * @return 剛体名\r
+     */\r
+    public I18nText getRigidName(){\r
+        return this.rigidName;\r
+    }\r
+\r
+    /**\r
+     * 剛体の振る舞い種別を返す。\r
+     * @return 剛体の振る舞い種別\r
+     */\r
+    public RigidBehaviorType getBehaviorType(){\r
+        return this.behaviorType;\r
+    }\r
+\r
+    /**\r
+     * 剛体の振る舞い種別を設定する。\r
+     * @param type 剛体の振る舞い種別。\r
+     * @throws NullPointerException 引数がnull\r
+     */\r
+    public void setBehaviorType(RigidBehaviorType type)\r
+            throws NullPointerException{\r
+        if(type == null) throw new NullPointerException();\r
+        this.behaviorType = type;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 剛体形状を返す。\r
+     * @return 剛体形状\r
+     */\r
+    public RigidShape getRigidShape(){\r
+        return this.rigidShape;\r
+    }\r
+\r
+    /**\r
+     * 剛体位置を返す。\r
+     * @return 剛体位置\r
+     */\r
+    public Pos3d getPosition(){\r
+        return this.position;\r
+    }\r
+\r
+    /**\r
+     * 剛体姿勢を返す。\r
+     * @return 剛体姿勢\r
+     */\r
+    public Rad3d getRotation(){\r
+        return this.rotation;\r
+    }\r
+\r
+    /**\r
+     * 接続ボーンを返す。\r
+     * @return 接続ボーン\r
+     */\r
+    public BoneInfo getLinkedBone(){\r
+        return this.linkedBone;\r
+    }\r
+\r
+    /**\r
+     * 接続ボーンを設定する。\r
+     * @param bone 接続ボーン\r
+     */\r
+    public void setLinkedBone(BoneInfo bone){\r
+        this.linkedBone = bone;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 剛体の力学パラメータを返す。\r
+     * @return 力学パラメータ\r
+     */\r
+    public DynamicsInfo getDynamicsInfo(){\r
+        return this.dynamicsInfo;\r
+    }\r
+\r
+    /**\r
+     * 非衝突グループ集合を返す。\r
+     * @return 非衝突グループ集合\r
+     */\r
+    public Collection<RigidGroup> getThroughGroupColl(){\r
+        return this.throughGroupColl;\r
+    }\r
+\r
+    /**\r
+     * 所属する剛体グループを返す。\r
+     * @return 所属する剛体グループ\r
+     */\r
+    public RigidGroup getRigidGroup(){\r
+        return this.rigidGroup;\r
+    }\r
+\r
+    /**\r
+     * 所属する剛体グループを設定する。\r
+     * @param group 所属する剛体グループ\r
+     */\r
+    public void setRigidGroup(RigidGroup group){\r
+        this.rigidGroup = group;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param num {@inheritDoc}\r
+     */\r
+    public void setSerialNumber(int num){\r
+        this.serialNo = num;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @return {@inheritDoc}\r
+     */\r
+    public int getSerialNumber(){\r
+        return this.serialNo;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @return {@inheritDoc}\r
+     */\r
+    @Override\r
+    public String toString(){\r
+        StringBuilder result = new StringBuilder();\r
+\r
+        result.append("Rigid(").append(this.rigidName).append(") ");\r
+        result.append("[=>")\r
+              .append(this.linkedBone.getBoneName())\r
+              .append("bone]");\r
+        result.append(" [").append(this.rigidShape).append("]");\r
+        result.append(" ").append(this.position);\r
+        result.append(" ").append(this.rotation);\r
+        result.append(" [").append(this.dynamicsInfo).append("]");\r
+        result.append("  [").append(this.behaviorType).append("]");\r
+\r
+        result.append(" through[");\r
+        boolean dumped = false;\r
+        for(RigidGroup group : this.throughGroupColl){\r
+            if(dumped) result.append(" ");\r
+            result.append(group.getGroupNumber());\r
+            dumped = true;\r
+        }\r
+        result.append("]");\r
+\r
+        return result.toString();\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..5906953
--- /dev/null
@@ -0,0 +1,148 @@
+/*\r
+ * rigid shape information\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd;\r
+\r
+/**\r
+ * 剛体形状に関する情報。\r
+ * 球及びカプセルの半径と箱の幅は同じ値が用いられる。\r
+ */\r
+public class RigidShape {\r
+\r
+    private RigidShapeType type = RigidShapeType.BOX;\r
+    private float width  = 0.1f;\r
+    private float height = 0.1f;\r
+    private float depth  = 0.1f;\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     */\r
+    public RigidShape(){\r
+        super();\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 剛体形状種別を返す。\r
+     * @return 剛体形状種別\r
+     */\r
+    public RigidShapeType getShapeType(){\r
+        return this.type;\r
+    }\r
+\r
+    /**\r
+     * 剛体形状種別を設定する。\r
+     * @param typeArg 剛体形状種別\r
+     * @throws NullPointerException 引数がnull\r
+     */\r
+    public void setShapeType(RigidShapeType typeArg)\r
+            throws NullPointerException{\r
+        if(typeArg == null) throw new NullPointerException();\r
+        this.type = typeArg;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 箱の幅を返す。\r
+     * @return 箱の幅\r
+     */\r
+    public float getWidth(){\r
+        return this.width;\r
+    }\r
+\r
+    /**\r
+     * 箱の幅を設定する。\r
+     * @param width 箱の幅\r
+     */\r
+    public void setWidth(float width){\r
+        this.width = width;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 箱及びカプセルの高さを返す。\r
+     * @return 箱及びカプセルの高さ\r
+     */\r
+    public float getHeight(){\r
+        return this.height;\r
+    }\r
+\r
+    /**\r
+     * 箱及びカプセルの高さを設定する。\r
+     * @param height 箱及びカプセルの高さ\r
+     */\r
+    public void setHeight(float height){\r
+        this.height = height;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 箱の奥行きを返す。\r
+     * @return 箱の奥行き\r
+     */\r
+    public float getDepth(){\r
+        return this.depth;\r
+    }\r
+\r
+    /**\r
+     * 箱の奥行きを設定する。\r
+     * @param depth 箱の奥行き\r
+     */\r
+    public void setDepth(float depth){\r
+        this.depth = depth;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 球及びカプセルの半径を返す。\r
+     * @return 球及びカプセルの半径\r
+     */\r
+    public float getRadius(){\r
+        return this.width;\r
+    }\r
+\r
+    /**\r
+     * 球及びカプセルの半径を設定する。\r
+     * @param radius 球及びカプセルの半径\r
+     */\r
+    public void setRadius(float radius){\r
+        this.width = radius;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @return {@inheritDoc}\r
+     */\r
+    @Override\r
+    public String toString(){\r
+        StringBuilder result = new StringBuilder();\r
+\r
+        result.append(this.type).append(' ');\r
+\r
+        switch(this.type){\r
+        case SPHERE:\r
+            result.append("r=").append(this.width);\r
+            break;\r
+        case BOX:\r
+            result.append("w=").append(this.width).append(", ");\r
+            result.append("h=").append(this.height).append(", ");\r
+            result.append("d=").append(this.depth);\r
+            break;\r
+        case CAPSULE:\r
+            result.append("r=").append(this.width).append(", ");\r
+            result.append("h=").append(this.height);\r
+            break;\r
+        default:\r
+            assert false;\r
+            throw new AssertionError();\r
+        }\r
+\r
+        return  result.toString();\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..33df15d
--- /dev/null
@@ -0,0 +1,102 @@
+/*\r
+ * rigid shape type\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd;\r
+\r
+import java.util.Locale;\r
+import java.util.ResourceBundle;\r
+\r
+/**\r
+ * 剛体の形状種別。\r
+ * <ul>\r
+ * <li>0x00:球\r
+ * <li>0x01:箱\r
+ * <li>0x02:カプセル\r
+ * </ul>\r
+ */\r
+public enum RigidShapeType {\r
+\r
+    /** 球。 */\r
+    SPHERE(0x00),\r
+    /** 箱。 */\r
+    BOX(0x01),\r
+    /** カプセル。 */\r
+    CAPSULE(0x02),\r
+    ;\r
+\r
+    private static final String FAMILY_NAME =\r
+            "jp.sourceforge.mikutoga.pmd.resources.RigidShapeTypeName";\r
+\r
+    private final byte encoded;\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     * @param code 符号化int値\r
+     */\r
+    private RigidShapeType(int code){\r
+        this((byte)code);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     * @param code 符号化byte値\r
+     */\r
+    private RigidShapeType(byte code){\r
+        this.encoded = code;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * byte値からデコードする。\r
+     * @param code byte値\r
+     * @return デコードされた列挙子。該当するものがなければnull\r
+     */\r
+    public static RigidShapeType decode(byte code){\r
+        RigidShapeType result = null;\r
+\r
+        for(RigidShapeType type : values()){\r
+            if(type.encode() == code){\r
+                result = type;\r
+                break;\r
+            }\r
+        }\r
+\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * byte値にエンコードする。\r
+     * @return byte値\r
+     */\r
+    public byte encode(){\r
+        return this.encoded;\r
+    }\r
+\r
+    /**\r
+     * デフォルトロケールでの表示名を返す。\r
+     * @return 表示名\r
+     */\r
+    public String getGuiName(){\r
+        Locale locale = Locale.getDefault();\r
+        return getGuiName(locale);\r
+    }\r
+\r
+    /**\r
+     * ロケールに準じた表示名を返す。\r
+     * @param locale ロケール。nullならデフォルトロケールと解釈される。\r
+     * @return 表示名\r
+     */\r
+    public String getGuiName(Locale locale){\r
+        if(locale == null) return getGuiName();\r
+        ResourceBundle rb = ResourceBundle.getBundle(FAMILY_NAME, locale);\r
+        String key = name();\r
+        String result = rb.getString(key);\r
+        return result;\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..5970b0e
--- /dev/null
@@ -0,0 +1,140 @@
+/*\r
+ * shading information\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd;\r
+\r
+/**\r
+ * シェーディング情報。\r
+ */\r
+public class ShadeInfo {\r
+\r
+    private ToonMap toonMap = new ToonMap();\r
+    private int toonIdx;\r
+\r
+    private String textureFileName = null;\r
+    private String spheremapFileName = null;\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     */\r
+    public ShadeInfo(){\r
+        super();\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * トゥーンマップを設定する。\r
+     * @param map トゥーンマップ\r
+     */\r
+    public void setToonMap(ToonMap map){\r
+        this.toonMap = map;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * トゥーンマップを返す。\r
+     * @return トゥーンマップ\r
+     */\r
+    public ToonMap getToonMap(){\r
+        return this.toonMap;\r
+    }\r
+\r
+    /**\r
+     * トゥーンインデックスを設定する。\r
+     * @param idx トゥーンインデックス\r
+     */\r
+    public void setToonIndex(int idx){\r
+        this.toonIdx = idx;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * トゥーンインデックス値を返す。\r
+     * @return トゥーンインデックス値\r
+     */\r
+    public int getToonIndex(){\r
+        return this.toonIdx;\r
+    }\r
+\r
+    /**\r
+     * トゥーンインデックス値が有効か判定する。\r
+     * 現時点では0から9までの値を有効とする。\r
+     * @return 有効ならtrue\r
+     */\r
+    public boolean isValidToonIndex(){\r
+        if(0 <= this.toonIdx && this.toonIdx <= 9) return true;\r
+        return false;\r
+    }\r
+\r
+    /**\r
+     * トゥーンファイル名を返す。\r
+     * @return トゥーンファイル名\r
+     * @throws IllegalStateException トゥーンマップが設定されていない。\r
+     */\r
+    public String getToonFileName() throws IllegalStateException{\r
+        if(this.toonMap == null) throw new IllegalStateException();\r
+        String result = this.toonMap.getIndexedToon(this.toonIdx);\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * テクスチャファイル名を設定する。\r
+     * @param fileName テクスチャファイル名\r
+     */\r
+    public void setTextureFileName(String fileName){\r
+        this.textureFileName = fileName;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * テクスチャファイル名を返す。\r
+     * @return テクスチャファイル名\r
+     */\r
+    public String getTextureFileName(){\r
+        return this.textureFileName;\r
+    }\r
+\r
+    /**\r
+     * スフィアマップファイル名を設定する。\r
+     * @param fileName スフィアマップファイル名\r
+     */\r
+    public void setSpheremapFileName(String fileName){\r
+        this.spheremapFileName = fileName;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * スフィアマップファイル名を返す。\r
+     * @return スフィアマップファイル名\r
+     */\r
+    public String getSpheremapFileName(){\r
+        return this.spheremapFileName;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @return {@inheritDoc}\r
+     */\r
+    @Override\r
+    public String toString(){\r
+        StringBuilder result = new StringBuilder();\r
+\r
+        result.append("toon(")\r
+              .append(this.toonIdx)\r
+              .append(")=")\r
+              .append(getToonFileName())\r
+              .append(' ');\r
+        result.append("texture=")\r
+              .append(this.textureFileName)\r
+              .append(' ');\r
+        result.append("sphere=")\r
+              .append(this.spheremapFileName);\r
+\r
+        return result.toString();\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..d2c5560
--- /dev/null
@@ -0,0 +1,175 @@
+/*\r
+ * triangle surface\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+import jp.sourceforge.mikutoga.corelib.SerialNumbered;\r
+\r
+/**\r
+ * 3頂点の三角形からなる面情報。\r
+ */\r
+public class Surface implements SerialNumbered, Iterable<Vertex> {\r
+\r
+    private Vertex vertex1;\r
+    private Vertex vertex2;\r
+    private Vertex vertex3;\r
+\r
+    private int serialNo = -1;\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     * 3頂点がnullの状態で生成される。\r
+     */\r
+    public Surface(){\r
+        super();\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 3頂点を設定する。\r
+     * @param vertex1 頂点1\r
+     * @param vertex2 頂点2\r
+     * @param vertex3 頂点3\r
+     * @throws  IllegalArgumentException 重複する頂点が引数に含まれた\r
+     */\r
+    public void setTriangle(Vertex vertex1, Vertex vertex2, Vertex vertex3)\r
+            throws IllegalArgumentException{\r
+        if(vertex1 != null && (vertex1 == vertex2 || vertex1 == vertex3)){\r
+            throw new IllegalArgumentException();\r
+        }\r
+        if(vertex2 != null && vertex2 == vertex3){\r
+            throw new IllegalArgumentException();\r
+        }\r
+\r
+        this.vertex1 = vertex1;\r
+        this.vertex2 = vertex2;\r
+        this.vertex3 = vertex3;\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 3頂点を返す。\r
+     * @param store 頂点格納用配列。nullもしくは3要素に満たない場合は無視され、\r
+     * 新規に格納用配列が生成される。\r
+     * @return 先頭3要素に3頂点が収められた配列。未設定要素にはnullが入る。\r
+     * 引数が長さ3以上の配列であれば引数と同じ配列が返る。\r
+     */\r
+    public Vertex[] getTriangle(Vertex[] store){\r
+        Vertex[] result;\r
+        if(store == null || store.length < 3){\r
+            result = new Vertex[3];\r
+        }else{\r
+            result = store;\r
+        }\r
+\r
+        result[0] = this.vertex1;\r
+        result[1] = this.vertex2;\r
+        result[2] = this.vertex3;\r
+\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * 頂点その1を返す。\r
+     * @return 頂点その1\r
+     */\r
+    public Vertex getVertex1(){\r
+        return this.vertex1;\r
+    }\r
+\r
+    /**\r
+     * 頂点その2を返す。\r
+     * @return 頂点その2\r
+     */\r
+    public Vertex getVertex2(){\r
+        return this.vertex2;\r
+    }\r
+\r
+    /**\r
+     * 頂点その3を返す。\r
+     * @return 頂点その3\r
+     */\r
+    public Vertex getVertex3(){\r
+        return this.vertex3;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * 頂点を返す反復子を生成する。\r
+     * 反復子がnullを返す可能性もありうる。\r
+     * @return {@inheritDoc}\r
+     */\r
+    public Iterator<Vertex> iterator(){\r
+        List<Vertex> list = new ArrayList<Vertex>(3);\r
+\r
+        list.add(this.vertex1);\r
+        list.add(this.vertex2);\r
+        list.add(this.vertex3);\r
+\r
+        return list.iterator();\r
+    }\r
+\r
+    /**\r
+     * 3頂点全てが設定されているか判定する。\r
+     * @return 3頂点とも非nullが設定されていればtrue\r
+     */\r
+    public boolean isCompleted(){\r
+        if(   this.vertex1 != null\r
+           && this.vertex2 != null\r
+           && this.vertex3 != null ){\r
+            return true;\r
+        }\r
+        return false;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param num {@inheritDoc}\r
+     */\r
+    public void setSerialNumber(int num){\r
+        this.serialNo = num;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @return {@inheritDoc}\r
+     */\r
+    public int getSerialNumber(){\r
+        return this.serialNo;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @return {@inheritDoc}\r
+     */\r
+    @Override\r
+    public String toString(){\r
+        StringBuilder result = new StringBuilder();\r
+\r
+        result.append("Surface(")\r
+              .append(getSerialNumber())\r
+              .append(")");\r
+\r
+        if(isCompleted()){\r
+            result.append(" VID=[")\r
+                  .append(this.vertex1.getSerialNumber())\r
+                  .append(',')\r
+                  .append(this.vertex2.getSerialNumber())\r
+                  .append(',')\r
+                  .append(this.vertex3.getSerialNumber())\r
+                  .append(']');\r
+        }\r
+\r
+        return result.toString();\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..153c0df
--- /dev/null
@@ -0,0 +1,147 @@
+/*\r
+ * toon file mapping\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd;\r
+\r
+import java.util.Collections;\r
+import java.util.Map;\r
+import java.util.TreeMap;\r
+\r
+/**\r
+ * インデックス化されたトゥーンファイル構成。\r
+ * 既存のトゥーンファイル構成と異なるトゥーンファイル名を用いることが可能。\r
+ * <h1>デフォルトのトゥーンファイル構成。</h1>\r
+ * <ul>\r
+ * <li>0x00:toon01.bmp\r
+ * <li>0x01:toon02.bmp\r
+ * <li>.....\r
+ * <li>0x09:toon10.bmp\r
+ * <li>0xff:toon0.bmp\r
+ * </ul>\r
+ */\r
+public class ToonMap {\r
+\r
+    private static final Map<Integer, String> DEF_TOONMAP;\r
+\r
+    static{\r
+        Map<Integer, String> map = new TreeMap<Integer, String>();\r
+\r
+        map.put(0x00, "toon01.bmp");\r
+        map.put(0x01, "toon02.bmp");\r
+        map.put(0x02, "toon03.bmp");\r
+        map.put(0x03, "toon04.bmp");\r
+        map.put(0x04, "toon05.bmp");\r
+        map.put(0x05, "toon06.bmp");\r
+        map.put(0x06, "toon07.bmp");\r
+        map.put(0x07, "toon08.bmp");\r
+        map.put(0x08, "toon09.bmp");\r
+        map.put(0x09, "toon10.bmp");\r
+        map.put(0xff, "toon0.bmp");\r
+\r
+        DEF_TOONMAP = Collections.unmodifiableMap(map);\r
+    }\r
+\r
+    private final Map<Integer, String> toonMap =\r
+            new TreeMap<Integer, String>(DEF_TOONMAP);\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     */\r
+    public ToonMap(){\r
+        super();\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 指定したインデックス値に対応したトゥーンファイル名を返す。\r
+     * @param idx インデックス値\r
+     * @return トゥーンファイル名。該当するものがなければnull\r
+     */\r
+    public String getIndexedToon(int idx){\r
+        String result = this.toonMap.get(idx);\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * 指定したインデックス値にトゥーンファイル名を設定する。\r
+     * @param idx インデックス値\r
+     * @param toonFileName トゥーンフィル名\r
+     * @throws NullPointerException トゥーンファイル名がnull\r
+     */\r
+    public void setIndexedToon(int idx, String toonFileName)\r
+            throws NullPointerException{\r
+        if(toonFileName == null) throw new NullPointerException();\r
+        this.toonMap.put(idx, toonFileName);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * このトゥーンファイル構成がデフォルトのトゥーンファイル構成と等しいか判定する。\r
+     * @return 等しければtrue\r
+     */\r
+    public boolean isDefaultMap(){\r
+        if(this.toonMap.equals(DEF_TOONMAP)) return true;\r
+        return false;\r
+    }\r
+\r
+    /**\r
+     * 指定インデックスのトゥーンファイル名がデフォルトと等しいか判定する。\r
+     * @param idx インデックス\r
+     * @return デフォルトと等しければtrue。\r
+     */\r
+    public boolean isDefaultToon(int idx){\r
+        String thisToon = this.toonMap.get(idx);\r
+        if(thisToon == null) return false;\r
+\r
+        String defToon = DEF_TOONMAP.get(idx);\r
+        if(thisToon.equals(defToon)) return true;\r
+\r
+        return false;\r
+    }\r
+\r
+    /**\r
+     * このトゥーンファイル構成をデフォルト構成内容でリセットする。\r
+     */\r
+    public void resetDefaultMap(){\r
+        this.toonMap.clear();\r
+        this.toonMap.putAll(DEF_TOONMAP);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 指定インデックスのトゥーンファイル名をデフォルトのトゥーンファイル名にリセットする。\r
+     * @param idx インデックス値\r
+     */\r
+    public void resetIndexedToon(int idx){\r
+        String toonFile = DEF_TOONMAP.get(idx);\r
+        this.toonMap.put(idx, toonFile);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @return {@inheritDoc}\r
+     */\r
+    @Override\r
+    public String toString(){\r
+        StringBuilder result = new StringBuilder();\r
+\r
+        boolean dumped = false;\r
+        for(Map.Entry<Integer, String> entry : this.toonMap.entrySet()){\r
+            Integer idx = entry.getKey();\r
+            String toonFile = entry.getValue();\r
+\r
+            if(dumped) result.append(", ");\r
+            result.append('(').append(idx).append(')');\r
+            result.append(toonFile);\r
+            dumped = true;\r
+        }\r
+\r
+        return result.toString();\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..6f61142
--- /dev/null
@@ -0,0 +1,182 @@
+/*\r
+ * triplet-value range limitation\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd;\r
+\r
+/**\r
+ * XYZ三組float値の範囲制約。\r
+ */\r
+public class TripletRange {\r
+\r
+    private float xFrom;\r
+    private float xTo;\r
+    private float yFrom;\r
+    private float yTo;\r
+    private float zFrom;\r
+    private float zTo;\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     */\r
+    public TripletRange(){\r
+        super();\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * X値有効範囲を設定する。\r
+     * 下限値が上限値より大きければ入れ替える。\r
+     * @param xFrom X値下限\r
+     * @param xTo X値上限\r
+     */\r
+    public void setXRange(float xFrom, float xTo){\r
+        if(xFrom <= xTo){\r
+            this.xFrom = xFrom;\r
+            this.xTo = xTo;\r
+        }else{\r
+            this.xFrom = xTo;\r
+            this.xTo = xFrom;\r
+        }\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * Y値有効範囲を設定する。\r
+     * 下限値が上限値より大きければ入れ替える。\r
+     * @param yFrom Y値下限\r
+     * @param yTo Y値上限\r
+     */\r
+    public void setYRange(float yFrom, float yTo){\r
+        if(yFrom <= yTo){\r
+            this.yFrom = yFrom;\r
+            this.yTo = yTo;\r
+        }else{\r
+            this.yFrom = yTo;\r
+            this.yTo = yFrom;\r
+        }\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * Z値有効範囲を設定する。\r
+     * 下限値が上限値より大きければ入れ替える。\r
+     * @param zFrom Z値下限\r
+     * @param zTo Z値上限\r
+     */\r
+    public void setZRange(float zFrom, float zTo){\r
+        if(zFrom <= zTo){\r
+            this.zFrom = zFrom;\r
+            this.zTo = zTo;\r
+        }else{\r
+            this.zFrom = zTo;\r
+            this.zTo = zFrom;\r
+        }\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * X値下限を返す。\r
+     * @return X値下限\r
+     */\r
+    public float getXFrom(){\r
+        return this.xFrom;\r
+    }\r
+\r
+    /**\r
+     * X値上限を返す。\r
+     * @return X値上限\r
+     */\r
+    public float getXTo(){\r
+        return this.xTo;\r
+    }\r
+\r
+    /**\r
+     * Y値下限を返す。\r
+     * @return Y値下限\r
+     */\r
+    public float getYFrom(){\r
+        return this.yFrom;\r
+    }\r
+\r
+    /**\r
+     * Y値上限を返す。\r
+     * @return Y値上限\r
+     */\r
+    public float getYTo(){\r
+        return this.yTo;\r
+    }\r
+\r
+    /**\r
+     * Z値下限を返す。\r
+     * @return Z値下限\r
+     */\r
+    public float getZFrom(){\r
+        return this.zFrom;\r
+    }\r
+\r
+    /**\r
+     * Z値上限を返す。\r
+     * @return Z値上限\r
+     */\r
+    public float getZTo(){\r
+        return this.zTo;\r
+    }\r
+\r
+    /**\r
+     * X値が範囲制約を満たすか判定する。\r
+     * @param xVal X値\r
+     * @return 制約を満たすならtrue\r
+     */\r
+    public boolean isValidX(float xVal){\r
+        if(this.xFrom <= xVal && xVal <= this.xTo) return true;\r
+        return false;\r
+    }\r
+\r
+    /**\r
+     * Y値が範囲制約を満たすか判定する。\r
+     * @param yVal Y値\r
+     * @return 制約を満たすならtrue\r
+     */\r
+    public boolean isValidY(float yVal){\r
+        if(this.yFrom <= yVal && yVal <= this.yTo) return true;\r
+        return false;\r
+    }\r
+\r
+    /**\r
+     * Z値が範囲制約を満たすか判定する。\r
+     * @param zVal Z値\r
+     * @return 制約を満たすならtrue\r
+     */\r
+    public boolean isValidZ(float zVal){\r
+        if(this.zFrom <= zVal && zVal <= this.zTo) return true;\r
+        return false;\r
+    }\r
+\r
+    @Override\r
+    public String toString(){\r
+        StringBuilder result = new StringBuilder();\r
+\r
+        result.append("x=[")\r
+              .append(xFrom)\r
+              .append(" - ")\r
+              .append(xTo)\r
+              .append("] ");\r
+        result.append("y=[")\r
+              .append(yFrom)\r
+              .append(" - ")\r
+              .append(yTo)\r
+              .append("] ");\r
+        result.append("z=[")\r
+              .append(zFrom)\r
+              .append(" - ")\r
+              .append(zTo)\r
+              .append("]");\r
+\r
+        return result.toString();\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..ccbe2d3
--- /dev/null
@@ -0,0 +1,94 @@
+/*\r
+ * 3D vector\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd;\r
+\r
+/**\r
+ * XYZ三次元ベクトル。\r
+ */\r
+public class Vec3d {\r
+\r
+    private float xVal;\r
+    private float yVal;\r
+    private float zVal;\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     */\r
+    public Vec3d(){\r
+        super();\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * X値を設定する。\r
+     * @param xVal X値\r
+     */\r
+    public void setXVal(float xVal){\r
+        this.xVal = xVal;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * X値を返す。\r
+     * @return X値\r
+     */\r
+    public float getXVal(){\r
+        return this.xVal;\r
+    }\r
+\r
+    /**\r
+     * Y値を設定する。\r
+     * @param yVal Y値\r
+     */\r
+    public void setYVal(float yVal){\r
+        this.yVal = yVal;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * Y値を返す。\r
+     * @return Y値\r
+     */\r
+    public float getYVal(){\r
+        return this.yVal;\r
+    }\r
+\r
+    /**\r
+     * Z値を設定する。\r
+     * @param zVal Z値\r
+     */\r
+    public void setZVal(float zVal){\r
+        this.zVal = zVal;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * Z値を返す。\r
+     * @return Z値\r
+     */\r
+    public float getZVal(){\r
+        return this.zVal;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @return {@inheritDoc}\r
+     */\r
+    @Override\r
+    public String toString(){\r
+        StringBuilder result = new StringBuilder();\r
+\r
+        result.append("vec=[")\r
+              .append(this.xVal).append(", ")\r
+              .append(this.yVal).append(", ")\r
+              .append(this.zVal).append(']');\r
+\r
+        return result.toString();\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..119bd46
--- /dev/null
@@ -0,0 +1,217 @@
+/*\r
+ * vertex information\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd;\r
+\r
+import jp.sourceforge.mikutoga.corelib.SerialNumbered;\r
+\r
+/**\r
+ * 頂点情報。\r
+ */\r
+public class Vertex implements SerialNumbered {\r
+\r
+    private static final int MIN_WEIGHT = 0;\r
+    private static final int MAX_WEIGHT = 100;\r
+\r
+    private final Pos3d position = new Pos3d();\r
+    private final Vec3d normal = new Vec3d();\r
+\r
+    private final Pos2d uvPosition = new Pos2d();\r
+\r
+    private BoneInfo boneA = null;\r
+    private BoneInfo boneB = null;\r
+\r
+    private int boneWeight = 50;\r
+\r
+    private boolean edgeAppearance = true;\r
+\r
+    private int serialNo = -1;\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     */\r
+    public Vertex(){\r
+        super();\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 頂点位置座標を返す。\r
+     * @return 頂点の位置座標\r
+     */\r
+    public Pos3d getPosition(){\r
+        return this.position;\r
+    }\r
+\r
+    /**\r
+     * 法線ベクトルを返す。\r
+     * @return 法線ベクトル\r
+     */\r
+    public Vec3d getNormal(){\r
+        return this.normal;\r
+    }\r
+\r
+    /**\r
+     * UVマップ座標を返す。\r
+     * @return UVマップ情報\r
+     */\r
+    public Pos2d getUVPosition(){\r
+        return this.uvPosition;\r
+    }\r
+\r
+    /**\r
+     * 頂点の属するボーンを設定する。\r
+     * @param boneA ボーンA\r
+     * @param boneB ボーンB\r
+     * @throws NullPointerException 引数がnull\r
+     */\r
+    public void setBonePair(BoneInfo boneA, BoneInfo boneB)\r
+            throws NullPointerException{\r
+        if(boneA == null || boneB == null) throw new NullPointerException();\r
+        this.boneA = boneA;\r
+        this.boneB = boneB;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * ボーンAを返す。\r
+     * @return ボーンA\r
+     */\r
+    public BoneInfo getBoneA(){\r
+        return this.boneA;\r
+    }\r
+\r
+    /**\r
+     * ボーンBを返す。\r
+     * @return ボーンB\r
+     */\r
+    public BoneInfo getBoneB(){\r
+        return this.boneB;\r
+    }\r
+\r
+    /**\r
+     * ボーンAのウェイト値を設定する。\r
+     * @param weight ウェイト値。0(影響小)-100(影響大)\r
+     * @throws IllegalArgumentException ウェイト値が範囲外\r
+     */\r
+    public void setWeightA(int weight) throws IllegalArgumentException{\r
+        if(   weight < MIN_WEIGHT\r
+           || MAX_WEIGHT < weight ){\r
+            throw new IllegalArgumentException();\r
+        }\r
+        this.boneWeight = weight;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * ボーンBのウェイト値を設定する。\r
+     * @param weight ウェイト値。0(影響小)-100(影響大)\r
+     * @throws IllegalArgumentException ウェイト値が範囲外\r
+     */\r
+    public void setWeightB(int weight) throws IllegalArgumentException{\r
+        setWeightA(MAX_WEIGHT - weight);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * ボーンAのウェイト値を返す。\r
+     * @return ウェイト値\r
+     */\r
+    public int getWeightA(){\r
+        return this.boneWeight;\r
+    }\r
+\r
+    /**\r
+     * ボーンBのウェイト値を返す。\r
+     * @return ウェイト値\r
+     */\r
+    public int getWeightB(){\r
+        int result = MAX_WEIGHT - this.boneWeight;\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * ボーンAのウェイト率を返す。\r
+     * @return ウェイト率。0.0(影響小)-1.0(影響大)\r
+     */\r
+    public float getWeightRatioA(){\r
+        return ((float)this.boneWeight) / (float)MAX_WEIGHT;\r
+    }\r
+\r
+    /**\r
+     * ボーンBのウェイト率を返す。\r
+     * @return ウェイト率。0.0(影響小)-1.0(影響大)\r
+     */\r
+    public float getWeightRatioB(){\r
+        return ((float)MAX_WEIGHT - (float)this.boneWeight)\r
+                / (float)MAX_WEIGHT;\r
+    }\r
+\r
+    /**\r
+     * エッジを表示するか設定する。\r
+     * マテリアル材質単位の設定より優先度は高い。\r
+     * @param show 表示するならtrue\r
+     */\r
+    public void setEdgeAppearance(boolean show){\r
+        this.edgeAppearance = show;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * エッジを表示するか判定する。\r
+     * マテリアル材質単位の設定より優先度は高い。\r
+     * @return 表示するならtrue\r
+     */\r
+    public boolean getEdgeAppearance(){\r
+        return this.edgeAppearance;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param num {@inheritDoc}\r
+     */\r
+    public void setSerialNumber(int num){\r
+        this.serialNo = num;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @return {@inheritDoc}\r
+     */\r
+    public int getSerialNumber(){\r
+        return this.serialNo;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @return {@inheritDoc}\r
+     */\r
+    @Override\r
+    public String toString(){\r
+        StringBuilder result = new StringBuilder();\r
+\r
+        result.append("Vertex(").append(this.serialNo).append(") ");\r
+        result.append(this.position).append(' ');\r
+        result.append(this.normal).append(' ');\r
+        result.append("UV").append(this.uvPosition).append(' ');\r
+\r
+        result.append("[")\r
+              .append(this.boneA.getBoneName())\r
+              .append("<>")\r
+              .append(this.boneB.getBoneName())\r
+              .append("] ");\r
+\r
+        result.append("weight=").append(this.boneWeight).append(' ');\r
+\r
+        if(this.edgeAppearance) result.append("showEdge");\r
+        else                    result.append("hideEdge");\r
+\r
+        return result.toString();\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..4be99ed
--- /dev/null
@@ -0,0 +1,14 @@
+/*\r
+ * package information for Javadoc\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+/**\r
+ * PMDモデルファイルに相当するオブジェクト各種。\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd;\r
+\r
+/* EOF */\r
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 (file)
index 0000000..45c463b
--- /dev/null
@@ -0,0 +1,224 @@
+/*\r
+ * abstract exporter\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd.pmdexporter;\r
+\r
+import java.io.IOException;\r
+import java.io.OutputStream;\r
+import java.nio.BufferOverflowException;\r
+import java.nio.ByteBuffer;\r
+import java.nio.ByteOrder;\r
+import java.nio.CharBuffer;\r
+import java.nio.charset.Charset;\r
+import java.nio.charset.CharsetEncoder;\r
+import java.nio.charset.CoderResult;\r
+import java.nio.charset.CodingErrorAction;\r
+\r
+/**\r
+ * 抽象化されたエクスポーター共通部。\r
+ */\r
+public abstract class AbstractExporter {\r
+\r
+    private static final Charset CS_WIN31J = Charset.forName("windows-31j");\r
+\r
+    private static final String ERRMSG_TOOLONGTEXT = "too long text";\r
+    private static final String ERRMSG_INVUCSSEQ = "invalid unicode sequence";\r
+    private static final String ERRMSG_NONWIN31J = "no character in win31j";\r
+\r
+    private static final int BYTES_SHORT = Short  .SIZE / Byte.SIZE;\r
+    private static final int BYTES_INT   = Integer.SIZE / Byte.SIZE;\r
+    private static final int BYTES_FLOAT = Float  .SIZE / Byte.SIZE;\r
+\r
+    private static final int BUFSZ_BYTE = 512;\r
+    private static final int BUFSZ_CHAR = 512;\r
+\r
+    private final OutputStream ostream;\r
+\r
+    private final byte[] barray;\r
+    private final ByteBuffer bbuf;\r
+\r
+    private final CharBuffer cbuf;\r
+    private final CharsetEncoder encoder;\r
+\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     * @param stream 出力ストリーム\r
+     * @throws NullPointerException 引数がnull\r
+     */\r
+    protected AbstractExporter(OutputStream stream)\r
+            throws NullPointerException{\r
+        super();\r
+\r
+        if(stream == null) throw new NullPointerException();\r
+        this.ostream = stream;\r
+\r
+        this.barray = new byte[BUFSZ_BYTE];\r
+        this.bbuf = ByteBuffer.wrap(this.barray);\r
+        this.bbuf.order(ByteOrder.LITTLE_ENDIAN);\r
+\r
+        this.cbuf = CharBuffer.allocate(BUFSZ_CHAR);\r
+        this.encoder = CS_WIN31J.newEncoder();\r
+        this.encoder.onMalformedInput(CodingErrorAction.REPORT);\r
+        this.encoder.onUnmappableCharacter(CodingErrorAction.REPORT);\r
+        this.encoder.reset();\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 出力をフラッシュする。\r
+     * I/O効率とデバッグ効率のバランスを考え、ご利用は計画的に。\r
+     * @throws IOException 出力エラー\r
+     */\r
+    protected void flush() throws IOException{\r
+        this.ostream.flush();\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * byte値を出力する。\r
+     * @param bVal byte値\r
+     * @throws IOException 出力エラー\r
+     */\r
+    protected void dumpByte(byte bVal) throws IOException{\r
+        this.ostream.write((int)bVal);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * int値をbyte値で出力する。\r
+     * byte値に収まらない上位ビットはそのまま捨てられる。\r
+     * @param iVal int値\r
+     * @throws IOException 出力エラー\r
+     */\r
+    protected void dumpByte(int iVal) throws IOException{\r
+        dumpByte((byte)iVal);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * short値を出力する。\r
+     * @param sVal short値\r
+     * @throws IOException 出力エラー\r
+     */\r
+    protected void dumpShort(short sVal) throws IOException{\r
+        this.bbuf.clear();\r
+        this.bbuf.putShort(sVal);\r
+        this.ostream.write(this.barray, 0, BYTES_SHORT);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * int値をshort値で出力する。\r
+     * short値に収まらない上位ビットはそのまま捨てられる。\r
+     * @param iVal int値\r
+     * @throws IOException 出力エラー\r
+     */\r
+    protected void dumpShort(int iVal) throws IOException{\r
+        dumpShort((short)iVal);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * int値を出力する。\r
+     * @param iVal int値\r
+     * @throws IOException 出力エラー\r
+     */\r
+    protected void dumpInt(int iVal) throws IOException{\r
+        this.bbuf.clear();\r
+        this.bbuf.putInt(iVal);\r
+        this.ostream.write(this.barray, 0, BYTES_INT);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * float値を出力する。\r
+     * @param fVal float値\r
+     * @throws IOException 出力エラー\r
+     */\r
+    protected void dumpFloat(float fVal) throws IOException{\r
+        this.bbuf.clear();\r
+        this.bbuf.putFloat(fVal);\r
+        this.ostream.write(this.barray, 0, BYTES_FLOAT);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 文字列をwindows-31jエンコーディングで出力する。\r
+     * @param seq 文字列\r
+     * @return 出力されたbyte総数。\r
+     * @throws IOException 出力エラー\r
+     * @throws IllegalPmdTextException 文字エンコーディングに関するエラー\r
+     */\r
+    protected int dumpCharSequence(CharSequence seq)\r
+            throws IOException, IllegalPmdTextException{\r
+        encodeToByteBuffer(seq);\r
+        this.bbuf.flip();\r
+        int length = dumpByteBuffer();\r
+        return length;\r
+    }\r
+\r
+    /**\r
+     * windows-31jにエンコーディングした文字列を内部バッファに蓄積する。\r
+     * @param seq 文字列\r
+     * @throws IllegalPmdTextException 文字エンコーディングに関するエラー。\r
+     * 考えられる状況としては、\r
+     * <ul>\r
+     * <li>入力文字列がUnicodeとしてすでにおかしい。\r
+     * (サロゲートペアがペアになってないなど)\r
+     * <li>windows-31jにない文字をエンコーディングしようとした\r
+     * <li>エンコーディング結果が内部バッファに収まらなかった。\r
+     * </ul>\r
+     * など。\r
+     */\r
+    private void encodeToByteBuffer(CharSequence seq)\r
+            throws IllegalPmdTextException{\r
+        this.cbuf.clear();\r
+        try{\r
+            this.cbuf.append(seq);\r
+        }catch(BufferOverflowException e){\r
+            throw new IllegalPmdTextException(ERRMSG_TOOLONGTEXT);\r
+        }\r
+        this.cbuf.flip();\r
+\r
+        this.bbuf.clear();\r
+\r
+        CoderResult result = this.encoder.encode(this.cbuf, this.bbuf, true);\r
+        if( ! result.isUnderflow()){\r
+            if(result.isOverflow()){\r
+                throw new IllegalPmdTextException(ERRMSG_TOOLONGTEXT);\r
+            }else if(result.isMalformed()){\r
+                throw new IllegalPmdTextException(ERRMSG_INVUCSSEQ);\r
+            }else if(result.isUnmappable()){\r
+                throw new IllegalPmdTextException(ERRMSG_NONWIN31J);\r
+            }else{\r
+                assert false;\r
+                throw new AssertionError();\r
+            }\r
+        }\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 内部バッファに蓄積されたbyte値並びを出力する。\r
+     * @return 出力されたbyte数\r
+     * @throws IOException 出力エラー\r
+     */\r
+    private int dumpByteBuffer() throws IOException{\r
+        int offset = this.bbuf.position();\r
+        int limit = this.bbuf.limit();\r
+        int length = limit - offset;\r
+        this.ostream.write(this.barray, offset, length);\r
+\r
+        this.bbuf.position(limit);\r
+\r
+        return length;\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..3dfed28
--- /dev/null
@@ -0,0 +1,33 @@
+/*\r
+ * illegal model exception\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd.pmdexporter;\r
+\r
+/**\r
+ * モデルデータの不備を発見した場合の例外。\r
+ */\r
+@SuppressWarnings("serial")\r
+public class IllegalPmdException extends Exception{\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     */\r
+    public IllegalPmdException(){\r
+        super();\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     * @param message メッセージ\r
+     */\r
+    public IllegalPmdException(String message){\r
+        super(message);\r
+        return;\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..6ab1dbe
--- /dev/null
@@ -0,0 +1,41 @@
+/*\r
+ * illegal text in model exception\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd.pmdexporter;\r
+\r
+/**\r
+ * 不正なテキスト情報をモデルデータ中に発見した場合の例外。\r
+ * <p>\r
+ * 考えられる理由としては\r
+ * <ul>\r
+ * <li>用意されたフォーマットに対し文字列が長すぎる。\r
+ * <li>文字エンコーディングできない文字が含まれている\r
+ * <li>ユニコード文字列として既に変。\r
+ * </ul>\r
+ * など。\r
+ */\r
+@SuppressWarnings("serial")\r
+public class IllegalPmdTextException extends IllegalPmdException{\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     */\r
+    public IllegalPmdTextException(){\r
+        super();\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     * @param message メッセージ\r
+     */\r
+    public IllegalPmdTextException(String message){\r
+        super(message);\r
+        return;\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..7d11ed3
--- /dev/null
@@ -0,0 +1,30 @@
+/*\r
+ * model exporter for pmd-file(up to date)\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd.pmdexporter;\r
+\r
+import java.io.OutputStream;\r
+\r
+/**\r
+ * 最新仕様のPMDファイルエクスポーター。\r
+ * 将来のリリースにおいて、\r
+ * 常に最新のPMDモデルファイルフォーマットに対応したエクスポーターの\r
+ * 別名であることが保証される。つもり。\r
+ */\r
+public class PmdExporter extends PmdExporterExt3{\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     * @param stream 出力ストリーム\r
+     * @throws NullPointerException 引数がnull\r
+     */\r
+    public PmdExporter(OutputStream stream) throws NullPointerException{\r
+        super(stream);\r
+        return;\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..d8866eb
--- /dev/null
@@ -0,0 +1,671 @@
+/*\r
+ * model exporter for pmd-file\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd.pmdexporter;\r
+\r
+import java.awt.Color;\r
+import java.io.IOException;\r
+import java.io.OutputStream;\r
+import java.util.Collections;\r
+import java.util.LinkedList;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.Set;\r
+import jp.sourceforge.mikutoga.corelib.SerialNumbered;\r
+import jp.sourceforge.mikutoga.parser.pmd.PmdLimits;\r
+import jp.sourceforge.mikutoga.pmd.BoneGroup;\r
+import jp.sourceforge.mikutoga.pmd.BoneInfo;\r
+import jp.sourceforge.mikutoga.pmd.BoneType;\r
+import jp.sourceforge.mikutoga.pmd.IKChain;\r
+import jp.sourceforge.mikutoga.pmd.Material;\r
+import jp.sourceforge.mikutoga.pmd.MorphPart;\r
+import jp.sourceforge.mikutoga.pmd.MorphType;\r
+import jp.sourceforge.mikutoga.pmd.MorphVertex;\r
+import jp.sourceforge.mikutoga.pmd.PmdModel;\r
+import jp.sourceforge.mikutoga.pmd.Pos2d;\r
+import jp.sourceforge.mikutoga.pmd.Pos3d;\r
+import jp.sourceforge.mikutoga.pmd.ShadeInfo;\r
+import jp.sourceforge.mikutoga.pmd.Surface;\r
+import jp.sourceforge.mikutoga.pmd.Vec3d;\r
+import jp.sourceforge.mikutoga.pmd.Vertex;\r
+\r
+/**\r
+ * PMDファイルのエクスポーター(拡張無し基本フォーマット)。\r
+ * <p>\r
+ * 英名対応以降のPMDファイルフォーマットを\r
+ * 使いたくない場合はこのエクスポーターを用いて出力せよ。\r
+ */\r
+public class PmdExporterBase extends AbstractExporter{\r
+\r
+    /** 前(親)ボーンが無い場合の便宜的なボーンID。 */\r
+    public static final int NOPREVBONE_ID = 0xffff;\r
+    /** 次(子)ボーンが無い場合の便宜的なボーンID。 */\r
+    public static final int NONEXTBONE_ID = 0x0000;\r
+    /** 影響元IKボーンが無い場合の便宜的なボーンID。 */\r
+    public static final int NOIKBONE_ID = 0x0000;\r
+\r
+    private static final String MAGIC = "Pmd";\r
+\r
+    private static final byte[] NULLFILLER =\r
+        { (byte)0x00 };\r
+    private static final byte[] FDFILLER =\r
+        { (byte)0x00, (byte)0xfd };\r
+    private static final byte[] LFFILLER =\r
+        { (byte)0x0a, (byte)0x00, (byte)0xfd };\r
+\r
+    /** 改行文字列 CR。 */\r
+    private static final String CR = "\r";       // 0x0d\r
+    /** 改行文字列 LF。 */\r
+    private static final String LF = "\n";       // 0x0a\r
+    /** 改行文字列 CRLF。 */\r
+    private static final String CRLF = CR + LF;  // 0x0d, 0x0a\r
+\r
+    static{\r
+        assert NOPREVBONE_ID > PmdLimits.MAX_BONE - 1;\r
+    }\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     * @param stream 出力ストリーム\r
+     * @throws NullPointerException 引数がnull\r
+     */\r
+    public PmdExporterBase(OutputStream stream)\r
+            throws NullPointerException{\r
+        super(stream);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 改行文字の正規化を行う。\r
+     * CR(0x0d)およびCRLF(0x0d0a)がLF(0x0a)へと正規化される。\r
+     * @param text 文字列\r
+     * @return 正規化の行われた文字列。\r
+     */\r
+    protected static String normalizeBreak(String text){\r
+        String result = text;\r
+\r
+        result = result.replace(CRLF, LF);\r
+        result = result.replace(CR, LF);\r
+\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * 文字列を指定されたバイト長で出力する。\r
+     * 文字列の改行記号はLF(0x0a)に正規化される。\r
+     * エンコード結果がバイト長に満たない場合は\r
+     * 1つの0x00及びそれに続く複数の0xfdがパディングされる。\r
+     * @param text 文字列\r
+     * @param maxByteLength バイト長指定\r
+     * @throws IOException 出力エラー\r
+     * @throws IllegalPmdTextException エンコード結果が\r
+     * 指定バイト長をはみ出した。\r
+     */\r
+    protected void dumpText(String text, int maxByteLength)\r
+            throws IOException, IllegalPmdTextException{\r
+        dumpText(text, maxByteLength, FDFILLER);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 文字列を指定されたバイト長で出力する。\r
+     * 文字列の改行記号はLF(0x0a)に正規化される。\r
+     * エンコード結果がバイト長に満たない場合は\r
+     * fillerがパディングされる。\r
+     * @param text 文字列\r
+     * @param maxByteLength バイト超指定\r
+     * @param filler 出力結果が足りない場合の詰め物。\r
+     * それでも足りない場合は最後のbyte要素が繰り返し出力される。\r
+     * @throws IOException 出力エラー\r
+     * @throws IllegalPmdTextException エンコード結果が\r
+     * 指定バイト長をはみ出した\r
+     */\r
+    protected void dumpText(String text, int maxByteLength, byte[] filler)\r
+            throws IOException, IllegalPmdTextException{\r
+        String normalized = normalizeBreak(text);\r
+        int blen = dumpCharSequence(normalized);\r
+        int remain = maxByteLength - blen;\r
+\r
+        if(remain < 0) throw new IllegalPmdTextException("too long text");\r
+\r
+        int fillerIdx = 0;\r
+        while(remain > 0){\r
+            if(fillerIdx >= filler.length){\r
+                fillerIdx = filler.length - 1;\r
+            }\r
+            dumpByte(filler[fillerIdx]);\r
+            fillerIdx++;\r
+            remain--;\r
+        }\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * モデルデータをPMDファイル形式で出力する。\r
+     * @param model モデルデータ\r
+     * @throws IOException 出力エラー\r
+     * @throws IllegalPmdException モデルデータに不備が発見された\r
+     */\r
+    public void dumpPmdModel(PmdModel model)\r
+            throws IOException, IllegalPmdException{\r
+        dumpBasic(model);\r
+        dumpVertexList(model);\r
+        dumpSurfaceList(model);\r
+        dumpMaterialList(model);\r
+        dumpBoneList(model);\r
+        dumpIKChainList(model);\r
+        dumpMorphList(model);\r
+        dumpMorphGroup(model);\r
+        dumpBoneGroupList(model);\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * モデル基本情報を出力する。\r
+     * @param model モデルデータ\r
+     * @throws IOException 出力エラー\r
+     * @throws IllegalPmdTextException モデル名もしくは説明が長すぎる\r
+     */\r
+    private void dumpBasic(PmdModel model)\r
+            throws IOException, IllegalPmdTextException{\r
+        dumpCharSequence(MAGIC);\r
+        float ver = model.getHeaderVersion();\r
+        dumpFloat(ver);\r
+\r
+        String modelName   = model.getModelName()  .getPrimaryText();\r
+        String description = model.getDescription().getPrimaryText();\r
+\r
+        dumpText(modelName, PmdLimits.MAXBYTES_MODELNAME);\r
+        dumpText(description, PmdLimits.MAXBYTES_MODELDESC);\r
+\r
+        flush();\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 頂点リストを出力する。\r
+     * @param model モデルデータ\r
+     * @throws IOException 出力エラー\r
+     */\r
+    private void dumpVertexList(PmdModel model)\r
+            throws IOException{\r
+        List<Vertex> vList = model.getVertexList();\r
+\r
+        int vertexNum = vList.size();\r
+        dumpInt(vertexNum);\r
+\r
+        for(Vertex vertex : vList){\r
+            dumpVertex(vertex);\r
+        }\r
+\r
+        flush();\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 個別の頂点データを出力する。\r
+     * @param vertex 頂点\r
+     * @throws IOException 出力エラー\r
+     */\r
+    private void dumpVertex(Vertex vertex)\r
+            throws IOException{\r
+        Pos3d position = vertex.getPosition();\r
+        dumpPos3d(position);\r
+\r
+        Vec3d normal = vertex.getNormal();\r
+        dumpVec3d(normal);\r
+\r
+        Pos2d uv = vertex.getUVPosition();\r
+        dumpPos2d(uv);\r
+\r
+        BoneInfo boneA = vertex.getBoneA();\r
+        BoneInfo boneB = vertex.getBoneB();\r
+        dumpSerialIdAsShort(boneA);\r
+        dumpSerialIdAsShort(boneB);\r
+\r
+        int weight = vertex.getWeightA();\r
+        dumpByte((byte)weight);\r
+\r
+        byte edgeFlag;\r
+        boolean hasEdge = vertex.getEdgeAppearance();\r
+        if(hasEdge) edgeFlag = 0x00;\r
+        else        edgeFlag = 0x01;\r
+        dumpByte(edgeFlag);\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 面リストを出力する。\r
+     * @param model モデルデータ\r
+     * @throws IOException 出力エラー\r
+     */\r
+    private void dumpSurfaceList(PmdModel model)\r
+            throws IOException{\r
+        int surfaceNum = 0;\r
+        List<Material> materialList = model.getMaterialList();\r
+        for(Material material : materialList){\r
+            surfaceNum += material.getSurfaceList().size();\r
+        }\r
+        dumpInt(surfaceNum * 3);\r
+\r
+        Vertex[] triangle = new Vertex[3];\r
+        for(Material material : materialList){\r
+            for(Surface surface : material){\r
+                surface.getTriangle(triangle);\r
+                dumpShort(triangle[0].getSerialNumber());\r
+                dumpShort(triangle[1].getSerialNumber());\r
+                dumpShort(triangle[2].getSerialNumber());\r
+            }\r
+        }\r
+\r
+        flush();\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * マテリアル素材リストを出力する。\r
+     * @param model モデルデータ\r
+     * @throws IOException 出力エラー\r
+     * @throws IllegalPmdTextException シェーディングファイル情報が長すぎる\r
+     */\r
+    private void dumpMaterialList(PmdModel model)\r
+            throws IOException, IllegalPmdTextException{\r
+        List<Material> materialList = model.getMaterialList();\r
+\r
+        int materialNum = materialList.size();\r
+        dumpInt(materialNum);\r
+\r
+        float[] rgba = new float[4];\r
+\r
+        for(Material material : materialList){\r
+            Color diffuse = material.getDiffuseColor();\r
+            diffuse.getRGBComponents(rgba);\r
+            dumpFloat(rgba[0]);\r
+            dumpFloat(rgba[1]);\r
+            dumpFloat(rgba[2]);\r
+            dumpFloat(rgba[3]);\r
+\r
+            float shininess = material.getShininess();\r
+            dumpFloat(shininess);\r
+\r
+            Color specular = material.getSpecularColor();\r
+            specular.getRGBComponents(rgba);\r
+            dumpFloat(rgba[0]);\r
+            dumpFloat(rgba[1]);\r
+            dumpFloat(rgba[2]);\r
+\r
+            Color ambient = material.getAmbientColor();\r
+            ambient.getRGBComponents(rgba);\r
+            dumpFloat(rgba[0]);\r
+            dumpFloat(rgba[1]);\r
+            dumpFloat(rgba[2]);\r
+\r
+            ShadeInfo shade = material.getShadeInfo();\r
+            int toonIdx = shade.getToonIndex();\r
+            dumpByte(toonIdx);\r
+\r
+            byte edgeFlag;\r
+            boolean showEdge = material.getEdgeAppearance();\r
+            if(showEdge) edgeFlag = 0x01;\r
+            else         edgeFlag = 0x00;\r
+            dumpByte(edgeFlag);\r
+\r
+            int surfaceNum = material.getSurfaceList().size();\r
+            dumpInt(surfaceNum * 3);\r
+\r
+            dumpShadeFileInfo(shade);\r
+        }\r
+\r
+        flush();\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * シェーディングファイル情報を出力する。\r
+     * @param shade シェーディング情報\r
+     * @throws IOException 出力エラー\r
+     * @throws IllegalPmdTextException ファイル名が長すぎる\r
+     */\r
+    private void dumpShadeFileInfo(ShadeInfo shade)\r
+            throws IOException, IllegalPmdTextException{\r
+        String textureFile   = shade.getTextureFileName();\r
+        String spheremapFile = shade.getSpheremapFileName();\r
+\r
+        StringBuilder text = new StringBuilder();\r
+        if(textureFile != null) text.append(textureFile);\r
+        if(spheremapFile != null && spheremapFile.length() > 0){\r
+            text.append('*')\r
+                  .append(spheremapFile);\r
+        }\r
+\r
+        byte[] filler;\r
+        if(text.length() <= 0) filler = NULLFILLER;\r
+        else                   filler = FDFILLER;\r
+\r
+        dumpText(text.toString(),\r
+                 PmdLimits.MAXBYTES_TEXTUREFILENAME,\r
+                 filler );\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * ボーンリストを出力する。\r
+     * @param model モデルデータ\r
+     * @throws IOException 出力エラー\r
+     * @throws IllegalPmdTextException ボーン名が長すぎる\r
+     */\r
+    private void dumpBoneList(PmdModel model)\r
+            throws IOException, IllegalPmdTextException{\r
+        List<BoneInfo> boneList = model.getBoneList();\r
+\r
+        int boneNum = boneList.size();\r
+        dumpShort(boneNum);\r
+\r
+        for(BoneInfo bone : boneList){\r
+            dumpBone(bone);\r
+        }\r
+\r
+        flush();\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 個別のボーン情報を出力する。\r
+     * @param bone ボーン情報\r
+     * @throws IOException 出力エラー\r
+     * @throws IllegalPmdTextException ボーン名が長すぎる\r
+     */\r
+    private void dumpBone(BoneInfo bone)\r
+            throws IOException, IllegalPmdTextException{\r
+        String boneName = bone.getBoneName().getPrimaryText();\r
+        dumpText(boneName, PmdLimits.MAXBYTES_BONENAME);\r
+\r
+        BoneInfo prev = bone.getPrevBone();\r
+        if(prev != null) dumpSerialIdAsShort(prev);\r
+        else             dumpShort(NOPREVBONE_ID);\r
+\r
+        BoneInfo next = bone.getNextBone();\r
+        if(next != null) dumpSerialIdAsShort(next);\r
+        else             dumpShort(NONEXTBONE_ID);\r
+\r
+        BoneType type = bone.getBoneType();\r
+        dumpByte(type.encode());\r
+\r
+        if(type == BoneType.LINKEDROT){\r
+            int ratio = bone.getRotationRatio();\r
+            dumpShort(ratio);\r
+        }else{\r
+            BoneInfo ik = bone.getIKBone();\r
+            if(ik != null) dumpSerialIdAsShort(ik);\r
+            else           dumpShort(NOIKBONE_ID);\r
+        }\r
+\r
+        Pos3d position = bone.getPosition();\r
+        dumpPos3d(position);\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * IKチェーンリストを出力する。\r
+     * @param model モデルデータ\r
+     * @throws IOException 出力エラー\r
+     */\r
+    private void dumpIKChainList(PmdModel model)\r
+            throws IOException{\r
+        List<IKChain> ikChainList = model.getIKChainList();\r
+\r
+        int ikNum = ikChainList.size();\r
+        dumpShort(ikNum);\r
+\r
+        for(IKChain chain : ikChainList){\r
+            dumpIKChain(chain);\r
+        }\r
+\r
+        flush();\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * IKチェーンを出力する。\r
+     * @param chain IKチェーン\r
+     * @throws IOException 出力エラー\r
+     */\r
+    // TODO ボーンリストから自動抽出できる情報ではないのか?\r
+    private void dumpIKChain(IKChain chain)\r
+            throws IOException{\r
+        BoneInfo ikBone = chain.getIkBone();\r
+        dumpSerialIdAsShort(ikBone);\r
+\r
+        List<BoneInfo> boneList = chain.getChainedBoneList();\r
+\r
+        BoneInfo bone1st = boneList.get(0);\r
+        dumpSerialIdAsShort(bone1st);\r
+\r
+        int boneNum = boneList.size();\r
+        dumpByte(boneNum - 1);\r
+\r
+        int depth = chain.getIKDepth();\r
+        float weight = chain.getIKWeight();\r
+\r
+        dumpShort(depth);\r
+        dumpFloat(weight);\r
+\r
+        for(int idx = 1; idx < boneNum; idx++){ // リストの2番目以降全て\r
+            BoneInfo bone = boneList.get(idx);\r
+            dumpSerialIdAsShort(bone);\r
+        }\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * モーフリストを出力する。\r
+     * @param model モデルデータ\r
+     * @throws IOException 出力エラー\r
+     * @throws IllegalPmdTextException モーフ名が長すぎる\r
+     */\r
+    private void dumpMorphList(PmdModel model)\r
+            throws IOException, IllegalPmdTextException{\r
+        Map<MorphType, List<MorphPart>> morphMap = model.getMorphMap();\r
+        Set<MorphType> typeSet = morphMap.keySet();\r
+        List<MorphVertex> mergedMorphVertexList = model.mergeMorphVertex();\r
+\r
+        int totalMorphPart = 0;\r
+        for(MorphType type : typeSet){\r
+            List<MorphPart> partList = morphMap.get(type);\r
+            if(partList == null) continue;\r
+            totalMorphPart += partList.size();\r
+        }\r
+\r
+        if(totalMorphPart <= 0){\r
+            dumpShort(0);\r
+            return;\r
+        }else{\r
+            totalMorphPart++;  // baseの分\r
+            dumpShort(totalMorphPart);\r
+        }\r
+\r
+        dumpText("base", PmdLimits.MAXBYTES_MORPHNAME);\r
+        int totalVertex = mergedMorphVertexList.size();\r
+        dumpInt(totalVertex);\r
+        dumpByte(MorphType.BASE.encode());\r
+        for(MorphVertex morphVertex : mergedMorphVertexList){\r
+            Vertex baseVertex = morphVertex.getBaseVertex();\r
+            dumpInt(baseVertex.getSerialNumber());\r
+            dumpPos3d(baseVertex.getPosition());\r
+        }\r
+\r
+        for(MorphType type : typeSet){\r
+            List<MorphPart> partList = morphMap.get(type);\r
+            if(partList == null) continue;\r
+            for(MorphPart part : partList){\r
+                dumpText(part.getMorphName().getPrimaryText(),\r
+                         PmdLimits.MAXBYTES_MORPHNAME );\r
+                List<MorphVertex> morphVertexList = part.getMorphVertexList();\r
+                dumpInt(morphVertexList.size());\r
+                dumpByte(part.getMorphType().encode());\r
+\r
+                for(MorphVertex morphVertex : morphVertexList){\r
+                    dumpInt(morphVertex.getSerialNumber());\r
+                    dumpPos3d(morphVertex.getOffset());\r
+                }\r
+            }\r
+        }\r
+\r
+        flush();\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * モーフグループを出力する。\r
+     * @param model モデルデータ\r
+     * @throws IOException 出力エラー\r
+     */\r
+    private void dumpMorphGroup(PmdModel model)\r
+            throws IOException{\r
+        Map<MorphType, List<MorphPart>> morphMap = model.getMorphMap();\r
+        Set<MorphType> typeSet = morphMap.keySet();\r
+\r
+        int totalMorph = 0;\r
+        for(MorphType type : typeSet){\r
+            List<MorphPart> partList = morphMap.get(type);\r
+            if(partList == null) continue;\r
+            totalMorph += partList.size();\r
+        }\r
+        dumpByte(totalMorph);\r
+\r
+        List<MorphType> typeList = new LinkedList<MorphType>();\r
+        for(MorphType type : typeSet){\r
+            assert ! type.isBase();\r
+            typeList.add(type);\r
+        }\r
+        Collections.reverse(typeList);  // 一応本家と互換性を\r
+\r
+        for(MorphType type : typeList){\r
+            List<MorphPart> partList = morphMap.get(type);\r
+            if(partList == null) continue;\r
+            for(MorphPart part : partList){\r
+                dumpSerialIdAsShort(part);\r
+            }\r
+        }\r
+\r
+        flush();\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * ボーングループリストを出力する。\r
+     * デフォルトボーングループ内訳は出力されない。\r
+     * @param model モデルデータ\r
+     * @throws IOException 出力エラー\r
+     * @throws IllegalPmdTextException ボーングループ名が長すぎる\r
+     */\r
+    private void dumpBoneGroupList(PmdModel model)\r
+            throws IOException, IllegalPmdTextException{\r
+        List<BoneGroup> groupList = model.getBoneGroupList();\r
+        int groupNum = groupList.size();\r
+        dumpByte(groupNum - 1);\r
+\r
+        int dispBoneNum = 0;\r
+        for(BoneGroup group : groupList){\r
+            if(group.isDefaultBoneGroup()) continue;\r
+            dumpText(group.getGroupName().getPrimaryText(),\r
+                     PmdLimits.MAXBYTES_BONEGROUPNAME, LFFILLER );\r
+            dispBoneNum += group.getBoneList().size();\r
+        }\r
+        dumpInt(dispBoneNum);\r
+\r
+        for(BoneGroup group : groupList){\r
+            if(group.isDefaultBoneGroup()) continue;\r
+            for(BoneInfo bone : group){\r
+                dumpSerialIdAsShort(bone);\r
+                int groupId = group.getSerialNumber();\r
+                dumpByte(groupId);\r
+            }\r
+        }\r
+\r
+        flush();\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 各種通し番号をshort値で出力する。\r
+     * short値に収まらない上位ビットは捨てられる。\r
+     * @param obj 番号づけられたオブジェクト\r
+     * @throws IOException 出力エラー\r
+     */\r
+    protected void dumpSerialIdAsShort(SerialNumbered obj)\r
+            throws IOException{\r
+        int serialId = obj.getSerialNumber();\r
+        dumpShort(serialId);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 2次元位置情報を出力する。\r
+     * @param position 2次元位置情報\r
+     * @throws IOException 出力エラー\r
+     */\r
+    protected void dumpPos2d(Pos2d position) throws IOException{\r
+        float xPos = position.getXPos();\r
+        float yPos = position.getYPos();\r
+\r
+        dumpFloat(xPos);\r
+        dumpFloat(yPos);\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 3次元位置情報を出力する。\r
+     * @param position 3次元位置情報\r
+     * @throws IOException 出力エラー\r
+     */\r
+    protected void dumpPos3d(Pos3d position) throws IOException{\r
+        float xPos = position.getXPos();\r
+        float yPos = position.getYPos();\r
+        float zPos = position.getZPos();\r
+\r
+        dumpFloat(xPos);\r
+        dumpFloat(yPos);\r
+        dumpFloat(zPos);\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 3次元ベクトル情報を出力する。\r
+     * @param vector 3次元ベクトル\r
+     * @throws IOException 出力エラー\r
+     */\r
+    protected void dumpVec3d(Vec3d vector) throws IOException{\r
+        float xVal = vector.getXVal();\r
+        float yVal = vector.getYVal();\r
+        float zVal = vector.getZVal();\r
+\r
+        dumpFloat(xVal);\r
+        dumpFloat(yVal);\r
+        dumpFloat(zVal);\r
+\r
+        return;\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..73b1919
--- /dev/null
@@ -0,0 +1,160 @@
+/*\r
+ * model exporter for pmd-file(Ext1)\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd.pmdexporter;\r
+\r
+import java.io.IOException;\r
+import java.io.OutputStream;\r
+import java.util.List;\r
+import java.util.Map;\r
+import jp.sourceforge.mikutoga.parser.pmd.PmdLimits;\r
+import jp.sourceforge.mikutoga.pmd.BoneGroup;\r
+import jp.sourceforge.mikutoga.pmd.BoneInfo;\r
+import jp.sourceforge.mikutoga.pmd.MorphPart;\r
+import jp.sourceforge.mikutoga.pmd.MorphType;\r
+import jp.sourceforge.mikutoga.pmd.PmdModel;\r
+\r
+/**\r
+ * PMDファイルのエクスポーター(拡張1:英名対応)。\r
+ * <p>\r
+ * 任意のトゥーンファイル名対応以降のPMDファイルフォーマットを\r
+ * 使いたくない場合はこのエクスポーターを用いて出力せよ。\r
+ */\r
+public class PmdExporterExt1 extends PmdExporterBase{\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     * @param stream 出力ストリーム\r
+     * @throws NullPointerException 引数がnull\r
+     */\r
+    public PmdExporterExt1(OutputStream stream)\r
+            throws NullPointerException{\r
+        super(stream);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param model {@inheritDoc}\r
+     * @throws IOException {@inheritDoc}\r
+     * @throws IllegalPmdException {@inheritDoc}\r
+     */\r
+    @Override\r
+    public void dumpPmdModel(PmdModel model)\r
+            throws IOException, IllegalPmdException{\r
+        super.dumpPmdModel(model);\r
+\r
+        dumpGlobalInfo(model);\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 英語名情報を出力する。\r
+     * @param model モデルデータ\r
+     * @throws IOException 出力エラー\r
+     * @throws IllegalPmdTextException 文字列が長すぎる。\r
+     */\r
+    private void dumpGlobalInfo(PmdModel model)\r
+            throws IOException, IllegalPmdTextException{\r
+        boolean hasGlobal = model.hasGlobalText();\r
+        byte globalFlag;\r
+        if(hasGlobal) globalFlag = 0x01;\r
+        else          globalFlag = 0x00;\r
+        dumpByte(globalFlag);\r
+\r
+        if(hasGlobal){\r
+            dumpBasicGlobal(model);\r
+            dumpBoneGlobal(model);\r
+            dumpMorphGlobal(model);\r
+            dumpBoneGroupGlobal(model);\r
+        }\r
+\r
+        flush();\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * モデル基本情報を英語で出力する。\r
+     * @param model モデルデータ\r
+     * @throws IOException 出力エラー\r
+     * @throws IllegalPmdTextException 文字列が長すぎる。\r
+     */\r
+    private void dumpBasicGlobal(PmdModel model)\r
+            throws IOException, IllegalPmdTextException{\r
+        String modelName = model.getModelName().getGlobalText();\r
+        if(modelName == null) modelName = "";\r
+        dumpText(modelName, PmdLimits.MAXBYTES_MODELNAME);\r
+\r
+        String description = model.getDescription().getGlobalText();\r
+        if(description == null) description = "";\r
+        dumpText(description, PmdLimits.MAXBYTES_MODELDESC);\r
+\r
+        flush();\r
+    }\r
+\r
+    /**\r
+     * ボーン英語名情報を出力する。\r
+     * @param model モデルデータ\r
+     * @throws IOException 出力エラー\r
+     * @throws IllegalPmdTextException 文字列が長すぎる。\r
+     */\r
+    private void dumpBoneGlobal(PmdModel model)\r
+            throws IOException, IllegalPmdTextException{\r
+        for(BoneInfo bone : model.getBoneList()){\r
+            String boneName = bone.getBoneName().getGlobalText();\r
+            if(boneName == null) boneName = "";\r
+            dumpText(boneName, PmdLimits.MAXBYTES_BONENAME);\r
+        }\r
+\r
+        flush();\r
+    }\r
+\r
+    /**\r
+     * モーフ英語名情報を出力する。\r
+     * @param model モデルデータ\r
+     * @throws IOException 出力エラー\r
+     * @throws IllegalPmdTextException 文字列が長すぎる。\r
+     */\r
+    private void dumpMorphGlobal(PmdModel model)\r
+            throws IOException, IllegalPmdTextException{\r
+        Map<MorphType, List<MorphPart>> morphMap = model.getMorphMap();\r
+\r
+        for(MorphType type : MorphType.values()){\r
+            if(type.isBase()) continue;\r
+            List<MorphPart> partList = morphMap.get(type);\r
+            if(partList == null) continue;\r
+            for(MorphPart part : partList){\r
+                String morphName = part.getMorphName().getGlobalText();\r
+                if(morphName == null) morphName = "";\r
+                dumpText(morphName, PmdLimits.MAXBYTES_MORPHNAME);\r
+            }\r
+        }\r
+\r
+        flush();\r
+    }\r
+\r
+    /**\r
+     * ボーングループ英語名情報を出力する。\r
+     * @param model モデルデータ\r
+     * @throws IOException 出力エラー\r
+     * @throws IllegalPmdTextException 文字列が長すぎる\r
+     */\r
+    private void dumpBoneGroupGlobal(PmdModel model)\r
+            throws IOException, IllegalPmdTextException{\r
+        for(BoneGroup group : model.getBoneGroupList()){\r
+            if(group.isDefaultBoneGroup()) continue;\r
+            String groupName = group.getGroupName().getGlobalText();\r
+            if(groupName == null) groupName = "";\r
+            dumpText(groupName, PmdLimits.MAXBYTES_BONEGROUPNAME);\r
+        }\r
+\r
+        flush();\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..fcd3b4f
--- /dev/null
@@ -0,0 +1,72 @@
+/*\r
+ * model exporter for pmd-file(Ext2)\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd.pmdexporter;\r
+\r
+import java.io.IOException;\r
+import java.io.OutputStream;\r
+import jp.sourceforge.mikutoga.parser.pmd.PmdLimits;\r
+import jp.sourceforge.mikutoga.pmd.PmdModel;\r
+import jp.sourceforge.mikutoga.pmd.ToonMap;\r
+\r
+/**\r
+ * PMDファイルのエクスポーター(拡張2:任意のトゥーンファイル名対応)。\r
+ * <p>\r
+ * 物理演算対応以降のPMDファイルフォーマットを\r
+ * 使いたくない場合はこのエクスポーターを用いて出力せよ。\r
+ */\r
+public class PmdExporterExt2 extends PmdExporterExt1{\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     * @param stream 出力ストリーム\r
+     * @throws NullPointerException 引数がnull\r
+     */\r
+    public PmdExporterExt2(OutputStream stream)\r
+            throws NullPointerException{\r
+        super(stream);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param model {@inheritDoc}\r
+     * @throws IOException {@inheritDoc}\r
+     * @throws IllegalPmdException {@inheritDoc}\r
+     */\r
+    @Override\r
+    public void dumpPmdModel(PmdModel model)\r
+            throws IOException, IllegalPmdException{\r
+        super.dumpPmdModel(model);\r
+\r
+        dumpToonMap(model);\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 独自トゥーンファイルテーブルを出力する。\r
+     * @param model モデルデータ\r
+     * @throws IOException 出力エラー\r
+     * @throws IllegalPmdTextException トゥーンファイル名が長すぎる\r
+     */\r
+    private void dumpToonMap(PmdModel model)\r
+            throws IOException, IllegalPmdTextException{\r
+        ToonMap map = model.getToonMap();\r
+\r
+        for(int idx = 0; idx < PmdLimits.TOON_FIXEDNUM; idx++){\r
+            String toonName = map.getIndexedToon(idx);\r
+            if(toonName == null) toonName = "";\r
+            dumpText(toonName, PmdLimits.MAXBYTES_TOONFILENAME);\r
+        }\r
+\r
+        flush();\r
+\r
+        return;\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..142c5ac
--- /dev/null
@@ -0,0 +1,273 @@
+/*\r
+ * model exporter for pmd-file(Ext3)\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd.pmdexporter;\r
+\r
+import java.io.IOException;\r
+import java.io.OutputStream;\r
+import java.util.List;\r
+import jp.sourceforge.mikutoga.parser.pmd.PmdLimits;\r
+import jp.sourceforge.mikutoga.pmd.BoneInfo;\r
+import jp.sourceforge.mikutoga.pmd.Deg3d;\r
+import jp.sourceforge.mikutoga.pmd.DynamicsInfo;\r
+import jp.sourceforge.mikutoga.pmd.JointInfo;\r
+import jp.sourceforge.mikutoga.pmd.PmdModel;\r
+import jp.sourceforge.mikutoga.pmd.Rad3d;\r
+import jp.sourceforge.mikutoga.pmd.RigidGroup;\r
+import jp.sourceforge.mikutoga.pmd.RigidInfo;\r
+import jp.sourceforge.mikutoga.pmd.RigidShape;\r
+import jp.sourceforge.mikutoga.pmd.RigidShapeType;\r
+import jp.sourceforge.mikutoga.pmd.TripletRange;\r
+\r
+/**\r
+ * PMDファイルのエクスポーター(拡張3:物理演算対応)。\r
+ * <p>\r
+ * 物理演算対応のPMDファイルフォーマットを\r
+ * 使いたい場合はこのエクスポーターを用いて出力せよ。\r
+ */\r
+public class PmdExporterExt3 extends PmdExporterExt2{\r
+\r
+    private static final short MASK_FULLCOLLISION = (short) 0xffff;\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     * @param stream 出力ストリーム\r
+     * @throws NullPointerException 引数がnull\r
+     */\r
+    public PmdExporterExt3(OutputStream stream)\r
+            throws NullPointerException{\r
+        super(stream);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param model {@inheritDoc}\r
+     * @throws IOException {@inheritDoc}\r
+     * @throws IllegalPmdException {@inheritDoc}\r
+     */\r
+    @Override\r
+    public void dumpPmdModel(PmdModel model)\r
+            throws IOException, IllegalPmdException{\r
+        super.dumpPmdModel(model);\r
+\r
+        dumpRigidList(model);\r
+        dumpJointList(model);\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 剛体リストを出力する。\r
+     * @param model モデルデータ\r
+     * @throws IOException 出力エラー\r
+     * @throws IllegalPmdTextException 長すぎる剛体名\r
+     */\r
+    private void dumpRigidList(PmdModel model)\r
+            throws IOException, IllegalPmdTextException{\r
+        List<RigidInfo> rigidList = model.getRigidList();\r
+        int rigidNum = rigidList.size();\r
+        dumpInt(rigidNum);\r
+\r
+        for(RigidInfo rigid : rigidList){\r
+            dumpRigid(rigid);\r
+        }\r
+\r
+        flush();\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 個別の剛体情報を出力する。\r
+     * @param rigid 剛体\r
+     * @throws IOException 出力エラー\r
+     * @throws IllegalPmdTextException 長すぎる剛体名\r
+     */\r
+    private void dumpRigid(RigidInfo rigid)\r
+            throws IOException, IllegalPmdTextException{\r
+        String rigidName = rigid.getRigidName().getPrimaryText();\r
+        dumpText(rigidName, PmdLimits.MAXBYTES_RIGIDNAME);\r
+\r
+        BoneInfo linkedBone = rigid.getLinkedBone();\r
+        dumpShort(linkedBone.getSerialNumber());\r
+\r
+        RigidGroup group = rigid.getRigidGroup();\r
+        dumpByte(group.getSerialNumber());\r
+\r
+        short mask = MASK_FULLCOLLISION;\r
+        for(RigidGroup throughGroup : rigid.getThroughGroupColl()){\r
+            int serialId = throughGroup.getSerialNumber();\r
+            mask &= ~(0x0001 << serialId);\r
+        }\r
+        dumpShort(mask);\r
+\r
+        dumpRigidShape(rigid.getRigidShape());\r
+\r
+        dumpPos3d(rigid.getPosition());\r
+        dumpRad3d(rigid.getRotation());\r
+\r
+        dumpDynamics(rigid.getDynamicsInfo());\r
+\r
+        dumpByte(rigid.getBehaviorType().encode());\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 剛体形状を出力する。\r
+     * @param shape 剛体形状\r
+     * @throws IOException 出力エラー\r
+     */\r
+    private void dumpRigidShape(RigidShape shape)\r
+            throws IOException{\r
+        RigidShapeType type = shape.getShapeType();\r
+        dumpByte(type.encode());\r
+\r
+        float width = shape.getWidth();\r
+        float height = shape.getHeight();\r
+        float depth = shape.getDepth();\r
+\r
+        dumpFloat(width);\r
+        dumpFloat(height);\r
+        dumpFloat(depth);\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 力学設定を出力する。\r
+     * @param dynamics 力学設定\r
+     * @throws IOException 出力エラー\r
+     */\r
+    private void dumpDynamics(DynamicsInfo dynamics)\r
+            throws IOException{\r
+        float mass        = dynamics.getMass();\r
+        float dampPos     = dynamics.getDampingPosition();\r
+        float dampRot     = dynamics.getDampingRotation();\r
+        float restitution = dynamics.getRestitution();\r
+        float friction    = dynamics.getFriction();\r
+\r
+        dumpFloat(mass);\r
+        dumpFloat(dampPos);\r
+        dumpFloat(dampRot);\r
+        dumpFloat(restitution);\r
+        dumpFloat(friction);\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * ジョイントリストを出力する。\r
+     * @param model モデルデータ\r
+     * @throws IOException 出力エラー\r
+     * @throws IllegalPmdTextException 長すぎるジョイント名\r
+     */\r
+    private void dumpJointList(PmdModel model)\r
+            throws IOException, IllegalPmdTextException{\r
+        List<JointInfo> jointList = model.getJointList();\r
+        int jointNum = jointList.size();\r
+        dumpInt(jointNum);\r
+\r
+        for(JointInfo joint : jointList){\r
+            dumpJoint(joint);\r
+        }\r
+\r
+        flush();\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 個別のジョイント情報を出力する。\r
+     * @param joint ジョイント\r
+     * @throws IOException 出力エラー\r
+     * @throws IllegalPmdTextException 長すぎるジョイント名\r
+     */\r
+    private void dumpJoint(JointInfo joint)\r
+            throws IOException, IllegalPmdTextException{\r
+        String jointName = joint.getJointName().getPrimaryText();\r
+        dumpText(jointName, PmdLimits.MAXBYTES_JOINTNAME);\r
+\r
+        RigidInfo rigidA = joint.getRigidA();\r
+        RigidInfo rigidB = joint.getRigidB();\r
+\r
+        dumpInt(rigidA.getSerialNumber());\r
+        dumpInt(rigidB.getSerialNumber());\r
+\r
+        dumpPos3d(joint.getPosition());\r
+        dumpRad3d(joint.getRotation());\r
+\r
+        dumpTripletRange(joint.getPositionRange());\r
+        dumpTripletRange(joint.getRotationRange());\r
+\r
+        dumpPos3d(joint.getElasticPosition());\r
+        dumpDeg3d(joint.getElasticRotation());\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 3次元範囲制約を出力する。\r
+     * @param range 3次元範囲制約\r
+     * @throws IOException 出力エラー\r
+     */\r
+    protected void dumpTripletRange(TripletRange range) throws IOException{\r
+        float xFrom = range.getXFrom();\r
+        float yFrom = range.getYFrom();\r
+        float zFrom = range.getZFrom();\r
+\r
+        dumpFloat(xFrom);\r
+        dumpFloat(yFrom);\r
+        dumpFloat(zFrom);\r
+\r
+        float xTo = range.getXTo();\r
+        float yTo = range.getYTo();\r
+        float zTo = range.getZTo();\r
+\r
+        dumpFloat(xTo);\r
+        dumpFloat(yTo);\r
+        dumpFloat(zTo);\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * ラジアンによる3次元姿勢情報を出力する。\r
+     * @param rad 3次元姿勢情報\r
+     * @throws IOException 出力エラー\r
+     */\r
+    protected void dumpRad3d(Rad3d rad) throws IOException{\r
+        float xVal = rad.getXRad();\r
+        float yVal = rad.getYRad();\r
+        float zVal = rad.getZRad();\r
+\r
+        dumpFloat(xVal);\r
+        dumpFloat(yVal);\r
+        dumpFloat(zVal);\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 度数法による3次元姿勢情報を出力する。\r
+     * @param deg 3次元姿勢情報\r
+     * @throws IOException 出力エラー\r
+     */\r
+    protected void dumpDeg3d(Deg3d deg) throws IOException{\r
+        float xVal = deg.getXDeg();\r
+        float yVal = deg.getYDeg();\r
+        float zVal = deg.getZDeg();\r
+\r
+        dumpFloat(xVal);\r
+        dumpFloat(yVal);\r
+        dumpFloat(zVal);\r
+\r
+        return;\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..56f44ae
--- /dev/null
@@ -0,0 +1,14 @@
+/*\r
+ * package information for Javadoc\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+/**\r
+ * PMDモデルファイル(*.pmd)用エクスポーター。\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd.pmdexporter;\r
+\r
+/* EOF */\r
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 (file)
index 0000000..9213010
--- /dev/null
@@ -0,0 +1,286 @@
+/*\r
+ * building bone information\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd.pmdloader;\r
+\r
+import jp.sourceforge.mikutoga.corelib.ListUtil;\r
+import java.util.Iterator;\r
+import java.util.LinkedList;\r
+import java.util.List;\r
+import jp.sourceforge.mikutoga.parser.ParseStage;\r
+import jp.sourceforge.mikutoga.parser.pmd.PmdBoneHandler;\r
+import jp.sourceforge.mikutoga.parser.pmd.PmdLimits;\r
+import jp.sourceforge.mikutoga.pmd.BoneGroup;\r
+import jp.sourceforge.mikutoga.pmd.BoneInfo;\r
+import jp.sourceforge.mikutoga.pmd.BoneType;\r
+import jp.sourceforge.mikutoga.pmd.IKChain;\r
+import jp.sourceforge.mikutoga.pmd.PmdModel;\r
+import jp.sourceforge.mikutoga.pmd.Pos3d;\r
+\r
+/**\r
+ * ボーン関係の通知をパーサから受け取る。\r
+ */\r
+class BoneBuilder implements PmdBoneHandler {\r
+\r
+    private final List<BoneInfo> boneList;\r
+    private Iterator<BoneInfo> boneIt;\r
+    private BoneInfo currentBone = null;\r
+\r
+    private final List<IKChain> ikChainList;\r
+    private Iterator<IKChain> ikChainIt;\r
+    private IKChain currentIkChain = null;\r
+\r
+    private final List<BoneGroup> boneGroupList;\r
+    private Iterator<BoneGroup> boneGroupIt;\r
+    private BoneGroup currentBoneGroup = null;\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     * @param model モデル\r
+     */\r
+    BoneBuilder(PmdModel model){\r
+        super();\r
+\r
+        this.boneList      = model.getBoneList();\r
+        this.ikChainList   = model.getIKChainList();\r
+        this.boneGroupList = model.getBoneGroupList();\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param stage {@inheritDoc}\r
+     * @param loops {@inheritDoc}\r
+     */\r
+    public void loopStart(ParseStage stage, int loops){\r
+        assert stage instanceof PmdBoneStage;\r
+\r
+        if(stage == PmdBoneHandler.BONE_LIST){\r
+            ListUtil.prepareDefConsList(this.boneList, BoneInfo.class, loops);\r
+            ListUtil.assignIndexedSerial(this.boneList);\r
+\r
+            this.boneIt = this.boneList.iterator();\r
+            if(this.boneIt.hasNext()){\r
+                this.currentBone = this.boneIt.next();\r
+            }\r
+        }else if(stage == PmdBoneHandler.IK_LIST){\r
+            ListUtil.prepareDefConsList(this.ikChainList,\r
+                                        IKChain.class,\r
+                                        loops );\r
+\r
+            this.ikChainIt = this.ikChainList.iterator();\r
+            if(this.ikChainIt.hasNext()){\r
+                this.currentIkChain = this.ikChainIt.next();\r
+            }\r
+        }else if(stage == PmdBoneHandler.IKCHAIN_LIST){\r
+            //NOTHING\r
+        }else if(stage == PmdBoneHandler.BONEGROUP_LIST){\r
+            ListUtil.prepareDefConsList(this.boneGroupList,\r
+                                        BoneGroup.class,\r
+                                        loops + 1 );\r
+            ListUtil.assignIndexedSerial(this.boneGroupList);\r
+\r
+            this.boneGroupIt = this.boneGroupList.iterator();\r
+\r
+            assert this.boneGroupIt.hasNext();\r
+            this.boneGroupIt.next();     // デフォルトボーングループを読み飛ばす\r
+\r
+            if(this.boneGroupIt.hasNext()){\r
+                this.currentBoneGroup = this.boneGroupIt.next();\r
+            }\r
+        }else if(stage == PmdBoneHandler.GROUPEDBONE_LIST){\r
+            //NOTHING\r
+        }else{\r
+            assert false;\r
+            throw new AssertionError();\r
+        }\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param stage {@inheritDoc}\r
+     */\r
+    public void loopNext(ParseStage stage){\r
+        assert stage instanceof PmdBoneStage;\r
+\r
+        if(stage == PmdBoneHandler.BONE_LIST){\r
+            if(this.boneIt.hasNext()){\r
+                this.currentBone = this.boneIt.next();\r
+            }\r
+        }else if(stage == PmdBoneHandler.IK_LIST){\r
+            if(this.ikChainIt.hasNext()){\r
+                this.currentIkChain = this.ikChainIt.next();\r
+            }\r
+        }else if(stage == PmdBoneHandler.IKCHAIN_LIST){\r
+            //NOTHING\r
+        }else if(stage == PmdBoneHandler.BONEGROUP_LIST){\r
+            if(this.boneGroupIt.hasNext()){\r
+                this.currentBoneGroup = this.boneGroupIt.next();\r
+            }\r
+        }else if(stage == PmdBoneHandler.GROUPEDBONE_LIST){\r
+            //NOTHING\r
+        }else{\r
+            assert false;\r
+            throw new AssertionError();\r
+        }\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param stage {@inheritDoc}\r
+     */\r
+    public void loopEnd(ParseStage stage){\r
+        assert stage instanceof PmdBoneStage;\r
+\r
+        if(stage == PmdBoneHandler.BONE_LIST){\r
+            //NOTHING\r
+        }else if(stage == PmdBoneHandler.IK_LIST){\r
+            //NOTHING\r
+        }else if(stage == PmdBoneHandler.IKCHAIN_LIST){\r
+            //NOTHING\r
+        }else if(stage == PmdBoneHandler.BONEGROUP_LIST){\r
+            //NOTHING\r
+        }else if(stage == PmdBoneHandler.GROUPEDBONE_LIST){\r
+            pickOrphanBone();\r
+        }else{\r
+            assert false;\r
+            throw new AssertionError();\r
+        }\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 所属グループの無いボーンをデフォルトボーングループへ登録する。\r
+     */\r
+    private void pickOrphanBone(){\r
+        List<BoneInfo> orpahnList = new LinkedList<BoneInfo>();\r
+        orpahnList.addAll(this.boneList);\r
+        for(BoneGroup group : this.boneGroupList){\r
+            orpahnList.removeAll(group.getBoneList());\r
+        }\r
+\r
+        BoneGroup defaultGroup = this.boneGroupList.get(0);\r
+        defaultGroup.getBoneList().addAll(orpahnList);\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param boneName {@inheritDoc}\r
+     * @param boneKind {@inheritDoc}\r
+     */\r
+    public void pmdBoneInfo(String boneName, byte boneKind){\r
+        this.currentBone.getBoneName().setPrimaryText(boneName);\r
+        BoneType type = BoneType.decode(boneKind);\r
+        this.currentBone.setBoneType(type);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param parentId {@inheritDoc}\r
+     * @param tailId {@inheritDoc}\r
+     * @param ikId {@inheritDoc}\r
+     */\r
+    public void pmdBoneLink(int parentId, int tailId, int ikId){\r
+        BoneInfo prevBone = null;\r
+        if(0 <= parentId && parentId < PmdLimits.MAX_BONE){\r
+            prevBone = this.boneList.get(parentId);\r
+        }\r
+\r
+        BoneInfo nextBone = null;\r
+        if(tailId != 0){\r
+            nextBone = this.boneList.get(tailId);\r
+        }\r
+\r
+        BoneInfo ikBone = null;\r
+        if(this.currentBone.getBoneType() == BoneType.LINKEDROT){\r
+            ikBone = null;\r
+            int ratio = ikId;\r
+            this.currentBone.setRotationRatio(ratio);\r
+        }else if(0 < ikId && ikId < PmdLimits.MAX_BONE){\r
+            ikBone = this.boneList.get(ikId);\r
+        }\r
+\r
+        this.currentBone.setPrevBone(prevBone);\r
+        this.currentBone.setNextBone(nextBone);\r
+        this.currentBone.setIKBone(ikBone);\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param xPos {@inheritDoc}\r
+     * @param yPos {@inheritDoc}\r
+     * @param zPos {@inheritDoc}\r
+     */\r
+    public void pmdBonePosition(float xPos, float yPos, float zPos){\r
+        Pos3d position = this.currentBone.getPosition();\r
+        position.setXPos(xPos);\r
+        position.setYPos(yPos);\r
+        position.setZPos(zPos);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param boneId {@inheritDoc}\r
+     * @param targetId {@inheritDoc}\r
+     * @param depth {@inheritDoc}\r
+     * @param weight {@inheritDoc}\r
+     */\r
+    public void pmdIKInfo(int boneId, int targetId, int depth, float weight){\r
+        BoneInfo bone = this.boneList.get(boneId);\r
+        this.currentIkChain.setIkBone(bone);\r
+\r
+        BoneInfo target = this.boneList.get(targetId);\r
+        this.currentIkChain.getChainedBoneList().add(0, target);\r
+\r
+        this.currentIkChain.setIKDepth(depth);\r
+        this.currentIkChain.setIKWeight(weight);\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param childId {@inheritDoc}\r
+     */\r
+    public void pmdIKChainInfo(int childId){\r
+        BoneInfo chain = this.boneList.get(childId);\r
+        this.currentIkChain.getChainedBoneList().add(chain);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param groupName {@inheritDoc}\r
+     */\r
+    public void pmdBoneGroupInfo(String groupName){\r
+        this.currentBoneGroup.getGroupName().setPrimaryText(groupName);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param boneId {@inheritDoc}\r
+     * @param groupId {@inheritDoc}\r
+     */\r
+    public void pmdGroupedBoneInfo(int boneId, int groupId){\r
+        BoneInfo bone = this.boneList.get(boneId);\r
+        BoneGroup group = this.boneGroupList.get(groupId);\r
+        group.getBoneList().add(bone);\r
+        return;\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..7ea24a6
--- /dev/null
@@ -0,0 +1,205 @@
+/*\r
+ * building joint information\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd.pmdloader;\r
+\r
+import jp.sourceforge.mikutoga.corelib.ListUtil;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+import jp.sourceforge.mikutoga.parser.ParseStage;\r
+import jp.sourceforge.mikutoga.parser.pmd.PmdJointHandler;\r
+import jp.sourceforge.mikutoga.pmd.Deg3d;\r
+import jp.sourceforge.mikutoga.pmd.JointInfo;\r
+import jp.sourceforge.mikutoga.pmd.PmdModel;\r
+import jp.sourceforge.mikutoga.pmd.Pos3d;\r
+import jp.sourceforge.mikutoga.pmd.Rad3d;\r
+import jp.sourceforge.mikutoga.pmd.RigidInfo;\r
+import jp.sourceforge.mikutoga.pmd.TripletRange;\r
+\r
+/**\r
+ * ジョイント関係の通知をパーサから受け取る。\r
+ */\r
+class JointBuilder implements PmdJointHandler {\r
+\r
+    private final List<RigidInfo> rigidList;\r
+\r
+    private final List<JointInfo> jointList;\r
+    private Iterator<JointInfo> jointIt;\r
+    private JointInfo currentJoint = null;\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     * @param model モデル\r
+     */\r
+    JointBuilder(PmdModel model){\r
+        super();\r
+        this.rigidList = model.getRigidList();\r
+        this.jointList = model.getJointList();\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param stage {@inheritDoc}\r
+     * @param loops {@inheritDoc}\r
+     */\r
+    public void loopStart(ParseStage stage, int loops){\r
+        assert stage == PmdJointHandler.JOINT_LIST;\r
+\r
+        ListUtil.prepareDefConsList(this.jointList, JointInfo.class, loops);\r
+\r
+        this.jointIt = this.jointList.iterator();\r
+        if(this.jointIt.hasNext()){\r
+            this.currentJoint = this.jointIt.next();\r
+        }\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param stage {@inheritDoc}\r
+     */\r
+    public void loopNext(ParseStage stage){\r
+        assert stage == PmdJointHandler.JOINT_LIST;\r
+\r
+        if(this.jointIt.hasNext()){\r
+            this.currentJoint = this.jointIt.next();\r
+        }\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param stage {@inheritDoc}\r
+     */\r
+    public void loopEnd(ParseStage stage){\r
+        assert stage == PmdJointHandler.JOINT_LIST;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param jointName {@inheritDoc}\r
+     */\r
+    public void pmdJointName(String jointName){\r
+        this.currentJoint.getJointName().setPrimaryText(jointName);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param rigidIdA {@inheritDoc}\r
+     * @param rigidIdB {@inheritDoc}\r
+     */\r
+    public void pmdJointLink(int rigidIdA, int rigidIdB){\r
+        RigidInfo rigidA = this.rigidList.get(rigidIdA);\r
+        RigidInfo rigidB = this.rigidList.get(rigidIdB);\r
+        this.currentJoint.setRigidPair(rigidA, rigidB);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param posX {@inheritDoc}\r
+     * @param posY {@inheritDoc}\r
+     * @param posZ {@inheritDoc}\r
+     */\r
+    public void pmdJointPosition(float posX, float posY, float posZ){\r
+        Pos3d position = this.currentJoint.getPosition();\r
+        position.setXPos(posX);\r
+        position.setYPos(posY);\r
+        position.setZPos(posZ);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param rotX {@inheritDoc}\r
+     * @param rotY {@inheritDoc}\r
+     * @param rotZ {@inheritDoc}\r
+     */\r
+    public void pmdJointRotation(float rotX, float rotY, float rotZ){\r
+        Rad3d rotation = this.currentJoint.getRotation();\r
+        rotation.setXRad(rotX);\r
+        rotation.setYRad(rotY);\r
+        rotation.setZRad(rotZ);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param posXlim1 {@inheritDoc}\r
+     * @param posXlim2 {@inheritDoc}\r
+     * @param posYlim1 {@inheritDoc}\r
+     * @param posYlim2 {@inheritDoc}\r
+     * @param posZlim1 {@inheritDoc}\r
+     * @param posZlim2 {@inheritDoc}\r
+     */\r
+    public void pmdPositionLimit(float posXlim1, float posXlim2,\r
+                                 float posYlim1, float posYlim2,\r
+                                 float posZlim1, float posZlim2){\r
+        TripletRange range = this.currentJoint.getPositionRange();\r
+        range.setXRange(posXlim1, posXlim2);\r
+        range.setYRange(posYlim1, posYlim2);\r
+        range.setZRange(posZlim1, posZlim2);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param rotXlim1 {@inheritDoc}\r
+     * @param rotXlim2 {@inheritDoc}\r
+     * @param rotYlim1 {@inheritDoc}\r
+     * @param rotYlim2 {@inheritDoc}\r
+     * @param rotZlim1 {@inheritDoc}\r
+     * @param rotZlim2 {@inheritDoc}\r
+     */\r
+    public void pmdRotationLimit(float rotXlim1, float rotXlim2,\r
+                                 float rotYlim1, float rotYlim2,\r
+                                 float rotZlim1, float rotZlim2){\r
+        TripletRange range = this.currentJoint.getRotationRange();\r
+        range.setXRange(rotXlim1, rotXlim2);\r
+        range.setYRange(rotYlim1, rotYlim2);\r
+        range.setZRange(rotZlim1, rotZlim2);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param elasticPosX {@inheritDoc}\r
+     * @param elasticPosY {@inheritDoc}\r
+     * @param elasticPosZ {@inheritDoc}\r
+     */\r
+    public void pmdElasticPosition(float elasticPosX,\r
+                                   float elasticPosY,\r
+                                   float elasticPosZ){\r
+        Pos3d position = this.currentJoint.getElasticPosition();\r
+        position.setXPos(elasticPosX);\r
+        position.setYPos(elasticPosY);\r
+        position.setZPos(elasticPosZ);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param elasticRotX {@inheritDoc}\r
+     * @param elasticRotY {@inheritDoc}\r
+     * @param elasticRotZ {@inheritDoc}\r
+     */\r
+    public void pmdElasticRotation(float elasticRotX,\r
+                                   float elasticRotY,\r
+                                   float elasticRotZ){\r
+        Deg3d rotation = this.currentJoint.getElasticRotation();\r
+        rotation.setXDeg(elasticRotX);\r
+        rotation.setYDeg(elasticRotY);\r
+        rotation.setZDeg(elasticRotZ);\r
+        return;\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..9ac8294
--- /dev/null
@@ -0,0 +1,180 @@
+/*\r
+ * building material information\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd.pmdloader;\r
+\r
+import jp.sourceforge.mikutoga.corelib.ListUtil;\r
+import java.awt.Color;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+import jp.sourceforge.mikutoga.parser.ParseStage;\r
+import jp.sourceforge.mikutoga.parser.pmd.PmdMaterialHandler;\r
+import jp.sourceforge.mikutoga.pmd.Material;\r
+import jp.sourceforge.mikutoga.pmd.PmdModel;\r
+import jp.sourceforge.mikutoga.pmd.ShadeInfo;\r
+import jp.sourceforge.mikutoga.pmd.Surface;\r
+import jp.sourceforge.mikutoga.pmd.ToonMap;\r
+\r
+/**\r
+ * マテリアル素材関連の通知をパーサから受け取る。\r
+ */\r
+class MaterialBuilder implements PmdMaterialHandler {\r
+\r
+    private final List<Material> materialList;\r
+    private Iterator<Material> materialIt;\r
+    private Material currentMaterial = null;\r
+\r
+    private final List<Surface> surfacelList;\r
+    private Iterator<Surface> surfaceIt;\r
+\r
+    private final ToonMap toonMap;\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     * @param model モデル\r
+     */\r
+    MaterialBuilder(PmdModel model){\r
+        super();\r
+\r
+        this.materialList = model.getMaterialList();\r
+        this.surfacelList = model.getSurfaceList();\r
+        this.toonMap = model.getToonMap();\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param stage {@inheritDoc}\r
+     * @param loops {@inheritDoc}\r
+     */\r
+    public void loopStart(ParseStage stage, int loops){\r
+        assert stage == PmdMaterialHandler.MATERIAL_LIST;\r
+\r
+        ListUtil.prepareDefConsList(this.materialList, Material.class, loops);\r
+\r
+        this.materialIt = this.materialList.iterator();\r
+        if(this.materialIt.hasNext()){\r
+            this.currentMaterial = this.materialIt.next();\r
+        }\r
+\r
+        this.surfaceIt = this.surfacelList.iterator();\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param stage {@inheritDoc}\r
+     */\r
+    public void loopNext(ParseStage stage){\r
+        assert stage == PmdMaterialHandler.MATERIAL_LIST;\r
+\r
+        if(this.materialIt.hasNext()){\r
+            this.currentMaterial = this.materialIt.next();\r
+        }\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param stage {@inheritDoc}\r
+     */\r
+    public void loopEnd(ParseStage stage){\r
+        assert stage == PmdMaterialHandler.MATERIAL_LIST;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param red {@inheritDoc}\r
+     * @param green {@inheritDoc}\r
+     * @param blue {@inheritDoc}\r
+     * @param alpha {@inheritDoc}\r
+     */\r
+    public void pmdMaterialDiffuse(float red,\r
+                                   float green,\r
+                                   float blue,\r
+                                   float alpha ){\r
+        Color diffuse = new Color(red, green, blue, alpha);\r
+        this.currentMaterial.setDiffuseColor(diffuse);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param red {@inheritDoc}\r
+     * @param green {@inheritDoc}\r
+     * @param blue {@inheritDoc}\r
+     */\r
+    public void pmdMaterialAmbient(float red,\r
+                                   float green,\r
+                                   float blue ){\r
+        Color ambient = new Color(red, green, blue);\r
+        this.currentMaterial.setAmbientColor(ambient);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param red {@inheritDoc}\r
+     * @param green {@inheritDoc}\r
+     * @param blue {@inheritDoc}\r
+     * @param shininess {@inheritDoc}\r
+     */\r
+    public void pmdMaterialSpecular(float red,\r
+                                    float green,\r
+                                    float blue,\r
+                                    float shininess ){\r
+        Color specular = new Color(red, green, blue);\r
+        this.currentMaterial.setSpecularColor(specular);\r
+        this.currentMaterial.setShininess(shininess);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param hasEdge {@inheritDoc}\r
+     * @param vertexNum {@inheritDoc}\r
+     */\r
+    public void pmdMaterialInfo(boolean hasEdge, int vertexNum){\r
+        this.currentMaterial.setEdgeAppearance(hasEdge);\r
+\r
+        List<Surface> list = this.currentMaterial.getSurfaceList();\r
+\r
+        int surfaceNum = vertexNum / 3;\r
+        for(int ct = 1; ct <= surfaceNum; ct++){\r
+            Surface surface = this.surfaceIt.next();\r
+            list.add(surface);\r
+        }\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param toonIdx {@inheritDoc}\r
+     * @param textureFile {@inheritDoc}\r
+     * @param sphereFile {@inheritDoc}\r
+     */\r
+    public void pmdMaterialShading(int toonIdx,\r
+                                   String textureFile,\r
+                                   String sphereFile ){\r
+        ShadeInfo info = this.currentMaterial.getShadeInfo();\r
+\r
+        ToonMap map = this.toonMap;\r
+\r
+        info.setToonMap(map);\r
+        info.setToonIndex(toonIdx);\r
+        info.setTextureFileName(textureFile);\r
+        info.setSpheremapFileName(sphereFile);\r
+\r
+        return;\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..4727439
--- /dev/null
@@ -0,0 +1,184 @@
+/*\r
+ * building morph information\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd.pmdloader;\r
+\r
+import jp.sourceforge.mikutoga.corelib.ListUtil;\r
+import java.util.ArrayList;\r
+import java.util.Iterator;\r
+import java.util.LinkedList;\r
+import java.util.List;\r
+import java.util.Map;\r
+import jp.sourceforge.mikutoga.parser.ParseStage;\r
+import jp.sourceforge.mikutoga.parser.pmd.PmdMorphHandler;\r
+import jp.sourceforge.mikutoga.pmd.MorphPart;\r
+import jp.sourceforge.mikutoga.pmd.MorphType;\r
+import jp.sourceforge.mikutoga.pmd.MorphVertex;\r
+import jp.sourceforge.mikutoga.pmd.PmdModel;\r
+import jp.sourceforge.mikutoga.pmd.Pos3d;\r
+import jp.sourceforge.mikutoga.pmd.Vertex;\r
+\r
+/**\r
+ * モーフ関係の通知をパーサから受け取る。\r
+ */\r
+class MorphBuilder implements PmdMorphHandler {\r
+\r
+    private final Map<MorphType, List<MorphPart>> morphMap;\r
+\r
+    private List<MorphPart> morphPartList;\r
+    private Iterator<MorphPart> morphPartIt;\r
+    private MorphPart currentMorphPart;\r
+    private final List<Vertex> vertexList;\r
+\r
+    private final List<Vertex> morphVertexList = new ArrayList<Vertex>();\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     * @param model モデル\r
+     */\r
+    MorphBuilder(PmdModel model){\r
+        super();\r
+        this.vertexList = model.getVertexList();\r
+        this.morphMap = model.getMorphMap();\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * PMDファイル中の出現順で各モーフを格納するためのリストを設定する。\r
+     * 主な用途はモーフ英名との突き合わせ作業。\r
+     * @param list モーフ格納リスト\r
+     */\r
+    void setMorphPartList(List<MorphPart> list){\r
+        this.morphPartList = list;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param stage {@inheritDoc}\r
+     * @param loops {@inheritDoc}\r
+     */\r
+    public void loopStart(ParseStage stage, int loops){\r
+        assert stage instanceof PmdMorphStage;\r
+\r
+        if(stage == PmdMorphHandler.MORPH_LIST){\r
+            ListUtil.prepareDefConsList(this.morphPartList,\r
+                                        MorphPart.class,\r
+                                        loops );\r
+            ListUtil.assignIndexedSerial(this.morphPartList);\r
+\r
+            this.morphPartIt = this.morphPartList.iterator();\r
+            if(this.morphPartIt.hasNext()){\r
+                this.currentMorphPart = this.morphPartIt.next();\r
+            }\r
+        }else if(stage == PmdMorphHandler.MORPHVERTEX_LIST){\r
+            // NOTHING\r
+        }else if(stage == PmdMorphHandler.MORPHORDER_LIST){\r
+            // NOTHING\r
+        }\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param stage {@inheritDoc}\r
+     */\r
+    public void loopNext(ParseStage stage){\r
+        assert stage instanceof PmdMorphStage;\r
+\r
+        if(stage == PmdMorphHandler.MORPH_LIST){\r
+            if(this.morphPartIt.hasNext()){\r
+                this.currentMorphPart = this.morphPartIt.next();\r
+            }\r
+        }else if(stage == PmdMorphHandler.MORPHVERTEX_LIST){\r
+            // NOTHING\r
+        }else if(stage == PmdMorphHandler.MORPHORDER_LIST){\r
+            // NOTHING\r
+        }\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param stage {@inheritDoc}\r
+     */\r
+    public void loopEnd(ParseStage stage){\r
+        assert stage instanceof PmdMorphStage;\r
+        if(stage == PmdMorphHandler.MORPH_LIST){\r
+            // NOTHING\r
+        }else if(stage == PmdMorphHandler.MORPHVERTEX_LIST){\r
+            // NOTHING\r
+        }else if(stage == PmdMorphHandler.MORPHORDER_LIST){\r
+            // NOTHING\r
+        }\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param morphName {@inheritDoc}\r
+     * @param morphType {@inheritDoc}\r
+     */\r
+    public void pmdMorphInfo(String morphName, byte morphType){\r
+        this.currentMorphPart.getMorphName().setPrimaryText(morphName);\r
+        MorphType type = MorphType.decode(morphType);\r
+        this.currentMorphPart.setMorphType(type);\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param serialId {@inheritDoc}\r
+     * @param xPos {@inheritDoc}\r
+     * @param yPos {@inheritDoc}\r
+     * @param zPos {@inheritDoc}\r
+     */\r
+    public void pmdMorphVertexInfo(int serialId,\r
+                                   float xPos, float yPos, float zPos){\r
+        MorphVertex morphVertex;\r
+        morphVertex = new MorphVertex();\r
+        Pos3d position = morphVertex.getOffset();\r
+        position.setXPos(xPos);\r
+        position.setYPos(yPos);\r
+        position.setZPos(zPos);\r
+\r
+        Vertex vertex;\r
+        if(this.currentMorphPart.getMorphType().isBase()){\r
+            vertex = this.vertexList.get(serialId);\r
+            this.morphVertexList.add(vertex);\r
+        }else{\r
+            vertex = this.morphVertexList.get(serialId);\r
+        }\r
+        morphVertex.setBaseVertex(vertex);\r
+\r
+        this.currentMorphPart.getMorphVertexList().add(morphVertex);\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param morphId {@inheritDoc}\r
+     */\r
+    public void pmdMorphOrderInfo(int morphId){\r
+        MorphPart part = this.morphPartList.get(morphId);\r
+        MorphType type = part.getMorphType();\r
+\r
+        List<MorphPart> partList = this.morphMap.get(type);\r
+        if(partList == null){\r
+            partList = new LinkedList<MorphPart>();\r
+            this.morphMap.put(type, partList);\r
+        }\r
+        partList.add(part);\r
+\r
+        return;\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..0b0c87b
--- /dev/null
@@ -0,0 +1,117 @@
+/*\r
+ * PMD file loader\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd.pmdloader;\r
+\r
+import java.io.IOException;\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+import jp.sourceforge.mikutoga.parser.MmdFormatException;\r
+import jp.sourceforge.mikutoga.parser.MmdSource;\r
+import jp.sourceforge.mikutoga.parser.pmd.PmdParser;\r
+import jp.sourceforge.mikutoga.pmd.MorphPart;\r
+import jp.sourceforge.mikutoga.pmd.PmdModel;\r
+\r
+/**\r
+ * PMDモデルファイルを読み込むためのローダ。\r
+ */\r
+public class PmdLoader {\r
+\r
+    private PmdModel model;\r
+    private PmdParser parser;\r
+    private TextBuilder textBuilder;\r
+\r
+    private boolean loaded = false;\r
+    private boolean hasMoreData = false;\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     * @param source PMDファイル入力ソース\r
+     */\r
+    public PmdLoader(MmdSource source){\r
+        super();\r
+\r
+        this.model = new PmdModel();\r
+        this.parser = new PmdParser(source);\r
+        this.textBuilder = new TextBuilder(this.model);\r
+\r
+        setHandler();\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * パーサに各種ハンドラの設定を行う。\r
+     */\r
+    private void setHandler(){\r
+        ShapeBuilder    shapeBuilder    = new ShapeBuilder(this.model);\r
+        MaterialBuilder materialBuilder = new MaterialBuilder(this.model);\r
+        BoneBuilder     boneBuilder     = new BoneBuilder(this.model);\r
+        MorphBuilder    morphBuilder    = new MorphBuilder(this.model);\r
+        ToonBuilder     toonBuilder     = new ToonBuilder(this.model);\r
+        RigidBuilder    rigidBuilder    = new RigidBuilder(this.model);\r
+        JointBuilder    jointBuilder    = new JointBuilder(this.model);\r
+\r
+        this.parser.setBasicHandler(this.textBuilder);\r
+        this.parser.setShapeHandler(shapeBuilder);\r
+        this.parser.setMaterialHandler(materialBuilder);\r
+        this.parser.setBoneHandler(boneBuilder);\r
+        this.parser.setMorphHandler(morphBuilder);\r
+        this.parser.setEngHandler(this.textBuilder);\r
+        this.parser.setToonHandler(toonBuilder);\r
+        this.parser.setRigidHandler(rigidBuilder);\r
+        this.parser.setJointHandler(jointBuilder);\r
+\r
+        List<MorphPart> morphPartList = new ArrayList<MorphPart>();\r
+        morphBuilder.setMorphPartList(morphPartList);\r
+        this.textBuilder.setMorphPartList(morphPartList);\r
+        morphPartList.clear();\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * PMDファイルの読み込みを行いモデル情報を返す。\r
+     * 1インスタンスにつき一度しかロードできない。\r
+     * @return モデル情報\r
+     * @throws IOException 入力エラー\r
+     * @throws MmdFormatException PMDファイルフォーマットの異常を検出\r
+     * @throws IllegalStateException このインスタンスで再度のロードを試みた。\r
+     */\r
+    public PmdModel load()\r
+            throws IOException,\r
+                   MmdFormatException,\r
+                   IllegalStateException {\r
+        if(this.loaded) throw new IllegalStateException();\r
+\r
+        PmdModel result;\r
+        try{\r
+            this.parser.parsePmd();\r
+        }finally{\r
+            this.loaded = true;\r
+\r
+            result = this.model;\r
+            this.hasMoreData = this.textBuilder.hasMoreData();\r
+\r
+            this.model = null;\r
+            this.parser = null;\r
+            this.textBuilder = null;\r
+        }\r
+\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * ロード処理が正常終了したのにまだ読み込んでいない部分が放置されているか判定する。\r
+     * MMDでの仕様拡張によるPMDファイルフォーマットの拡張が行われた場合を想定。\r
+     * @return 読み込んでいない部分があればtrue\r
+     */\r
+    public boolean hasMoreData(){\r
+        return this.hasMoreData;\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..45a5d79
--- /dev/null
@@ -0,0 +1,212 @@
+/*\r
+ * building rigid information\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd.pmdloader;\r
+\r
+import jp.sourceforge.mikutoga.corelib.ListUtil;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+import jp.sourceforge.mikutoga.parser.ParseStage;\r
+import jp.sourceforge.mikutoga.parser.pmd.PmdLimits;\r
+import jp.sourceforge.mikutoga.parser.pmd.PmdRigidHandler;\r
+import jp.sourceforge.mikutoga.pmd.BoneInfo;\r
+import jp.sourceforge.mikutoga.pmd.DynamicsInfo;\r
+import jp.sourceforge.mikutoga.pmd.PmdModel;\r
+import jp.sourceforge.mikutoga.pmd.Pos3d;\r
+import jp.sourceforge.mikutoga.pmd.Rad3d;\r
+import jp.sourceforge.mikutoga.pmd.RigidBehaviorType;\r
+import jp.sourceforge.mikutoga.pmd.RigidGroup;\r
+import jp.sourceforge.mikutoga.pmd.RigidInfo;\r
+import jp.sourceforge.mikutoga.pmd.RigidShape;\r
+import jp.sourceforge.mikutoga.pmd.RigidShapeType;\r
+\r
+/**\r
+ * 剛体関係の通知をパーサから受け取る。\r
+ */\r
+class RigidBuilder implements PmdRigidHandler {\r
+\r
+    private final List<BoneInfo> boneList;\r
+\r
+    private final List<RigidInfo> rigidList;\r
+    private Iterator<RigidInfo> rigidIt;\r
+    private RigidInfo currentRigid = null;\r
+\r
+    private final List<RigidGroup> rigidGroupList;\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     * @param model モデル\r
+     */\r
+    RigidBuilder(PmdModel model){\r
+        super();\r
+        this.boneList = model.getBoneList();\r
+        this.rigidList = model.getRigidList();\r
+        this.rigidGroupList = model.getRigidGroupList();\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param stage {@inheritDoc}\r
+     * @param loops {@inheritDoc}\r
+     */\r
+    public void loopStart(ParseStage stage, int loops){\r
+        ListUtil.prepareDefConsList(this.rigidList, RigidInfo.class, loops);\r
+        ListUtil.assignIndexedSerial(this.rigidList);\r
+\r
+        this.rigidIt = this.rigidList.iterator();\r
+        if(this.rigidIt.hasNext()){\r
+            this.currentRigid = this.rigidIt.next();\r
+        }\r
+\r
+        ListUtil.prepareDefConsList(this.rigidGroupList,\r
+                                    RigidGroup.class,\r
+                                    PmdLimits.RIGIDGROUP_FIXEDNUM );\r
+        ListUtil.assignIndexedSerial(this.rigidGroupList);\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param stage {@inheritDoc}\r
+     */\r
+    public void loopNext(ParseStage stage){\r
+        if(this.rigidIt.hasNext()){\r
+            this.currentRigid = this.rigidIt.next();\r
+        }\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param stage {@inheritDoc}\r
+     */\r
+    public void loopEnd(ParseStage stage){\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param rigidName {@inheritDoc}\r
+     */\r
+    public void pmdRigidName(String rigidName){\r
+        this.currentRigid.getRigidName().setPrimaryText(rigidName);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param rigidGroupId {@inheritDoc}\r
+     * @param linkedBoneId {@inheritDoc}\r
+     */\r
+    public void pmdRigidInfo(int rigidGroupId, int linkedBoneId){\r
+        BoneInfo bone = this.boneList.get(linkedBoneId);\r
+        RigidGroup group = this.rigidGroupList.get(rigidGroupId);\r
+\r
+        this.currentRigid.setLinkedBone(bone);\r
+        this.currentRigid.setRigidGroup(group);\r
+        group.getRigidList().add(this.currentRigid);\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param shapeType {@inheritDoc}\r
+     * @param width {@inheritDoc}\r
+     * @param height {@inheritDoc}\r
+     * @param depth {@inheritDoc}\r
+     */\r
+    public void pmdRigidShape(byte shapeType,\r
+                              float width, float height, float depth){\r
+        RigidShape shape = this.currentRigid.getRigidShape();\r
+\r
+        shape.setWidth(width);\r
+        shape.setHeight(height);\r
+        shape.setDepth(depth);\r
+\r
+        RigidShapeType type = RigidShapeType.decode(shapeType);\r
+        shape.setShapeType(type);\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param posX {@inheritDoc}\r
+     * @param posY {@inheritDoc}\r
+     * @param posZ {@inheritDoc}\r
+     */\r
+    public void pmdRigidPosition(float posX, float posY, float posZ){\r
+        Pos3d position = this.currentRigid.getPosition();\r
+        position.setXPos(posX);\r
+        position.setYPos(posY);\r
+        position.setZPos(posZ);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param radX {@inheritDoc}\r
+     * @param radY {@inheritDoc}\r
+     * @param radZ {@inheritDoc}\r
+     */\r
+    public void pmdRigidRotation(float radX, float radY, float radZ){\r
+        Rad3d rotation = this.currentRigid.getRotation();\r
+        rotation.setXRad(radX);\r
+        rotation.setYRad(radY);\r
+        rotation.setZRad(radZ);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param mass {@inheritDoc}\r
+     * @param dampingPos {@inheritDoc}\r
+     * @param dampingRot {@inheritDoc}\r
+     * @param restitution {@inheritDoc}\r
+     * @param friction {@inheritDoc}\r
+     */\r
+    public void pmdRigidPhysics(float mass,\r
+                                  float dampingPos,\r
+                                  float dampingRot,\r
+                                  float restitution,\r
+                                  float friction ){\r
+        DynamicsInfo info = this.currentRigid.getDynamicsInfo();\r
+\r
+        info.setMass(mass);\r
+        info.setDampingPosition(dampingPos);\r
+        info.setDampingRotation(dampingRot);\r
+        info.setRestitution(restitution);\r
+        info.setFriction(friction);\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param behaveType {@inheritDoc}\r
+     * @param collisionMap {@inheritDoc}\r
+     */\r
+    public void pmdRigidBehavior(byte behaveType, short collisionMap){\r
+        RigidBehaviorType type = RigidBehaviorType.decode(behaveType);\r
+        this.currentRigid.setBehaviorType(type);\r
+\r
+        for(int bitPos = 0; bitPos < PmdLimits.RIGIDGROUP_FIXEDNUM; bitPos++){\r
+            short mask = 0x0001;\r
+            mask <<= bitPos;\r
+            if((collisionMap & mask) == 0){\r
+                RigidGroup group = this.rigidGroupList.get(bitPos);\r
+                this.currentRigid.getThroughGroupColl().add(group);\r
+            }\r
+        }\r
+\r
+        return;\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..669ef9c
--- /dev/null
@@ -0,0 +1,215 @@
+/*\r
+ * building shape information\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd.pmdloader;\r
+\r
+import jp.sourceforge.mikutoga.corelib.ListUtil;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+import java.util.RandomAccess;\r
+import jp.sourceforge.mikutoga.parser.ParseStage;\r
+import jp.sourceforge.mikutoga.parser.pmd.PmdShapeHandler;\r
+import jp.sourceforge.mikutoga.pmd.BoneInfo;\r
+import jp.sourceforge.mikutoga.pmd.PmdModel;\r
+import jp.sourceforge.mikutoga.pmd.Pos2d;\r
+import jp.sourceforge.mikutoga.pmd.Pos3d;\r
+import jp.sourceforge.mikutoga.pmd.Surface;\r
+import jp.sourceforge.mikutoga.pmd.Vec3d;\r
+import jp.sourceforge.mikutoga.pmd.Vertex;\r
+\r
+/**\r
+ * モデル形状に関する通知をパーサから受け取る。\r
+ */\r
+class ShapeBuilder implements PmdShapeHandler {\r
+\r
+    private final List<Vertex> vertexList;\r
+    private final List<BoneInfo> boneList;\r
+    private final List<Surface> surfaceList;\r
+\r
+    private Iterator<Vertex> vertexIt;\r
+    private Vertex currentVertex = null;\r
+\r
+    private Iterator<Surface> surfaceIt;\r
+    private Surface currentSurface = null;\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     * @param model モデル\r
+     */\r
+    ShapeBuilder(PmdModel model){\r
+        super();\r
+\r
+        this.vertexList  = model.getVertexList();\r
+        this.boneList    = model.getBoneList();\r
+        this.surfaceList = model.getSurfaceList();\r
+\r
+        assert this.vertexList instanceof RandomAccess;\r
+        assert this.surfaceList instanceof RandomAccess;\r
+        assert this.boneList instanceof RandomAccess;\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * ボーンリスト上にボーンを用意する。\r
+     * すでに指定位置にボーンがあればなにもしない。\r
+     * @param id 0から始まるボーン番号\r
+     * @return 用意されたボーン\r
+     */\r
+    private BoneInfo prepareBone(int id){\r
+        ListUtil.extendCollection(this.boneList, id + 1);\r
+        BoneInfo bone = this.boneList.get(id);\r
+        if(bone == null){\r
+            bone = new BoneInfo();\r
+            bone.setSerialNumber(id);\r
+            this.boneList.set(id, bone);\r
+        }\r
+        return bone;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param stage {@inheritDoc}\r
+     * @param loops {@inheritDoc}\r
+     */\r
+    public void loopStart(ParseStage stage, int loops){\r
+        if(stage == PmdShapeHandler.VERTEX_LIST){\r
+            ListUtil.prepareDefConsList(this.vertexList, Vertex.class, loops);\r
+            ListUtil.assignIndexedSerial(this.vertexList);\r
+\r
+            this.vertexIt = this.vertexList.iterator();\r
+            if(this.vertexIt.hasNext()){\r
+                this.currentVertex = this.vertexIt.next();\r
+            }\r
+        }else if(stage == PmdShapeHandler.SURFACE_LIST){\r
+            ListUtil.prepareDefConsList(this.surfaceList,\r
+                                        Surface.class, loops );\r
+            ListUtil.assignIndexedSerial(this.surfaceList);\r
+\r
+            this.surfaceIt = this.surfaceList.iterator();\r
+            if(this.surfaceIt.hasNext()){\r
+                this.currentSurface = this.surfaceIt.next();\r
+            }\r
+        }else{\r
+            assert false;\r
+            throw new AssertionError();\r
+        }\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param stage {@inheritDoc}\r
+     */\r
+    public void loopNext(ParseStage stage){\r
+        if(stage == PmdShapeHandler.VERTEX_LIST){\r
+            if(this.vertexIt.hasNext()){\r
+                this.currentVertex = this.vertexIt.next();\r
+            }\r
+        }else if(stage == PmdShapeHandler.SURFACE_LIST){\r
+            if(this.surfaceIt.hasNext()){\r
+                this.currentSurface = this.surfaceIt.next();\r
+            }\r
+        }else{\r
+            assert false;\r
+            throw new AssertionError();\r
+        }\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param stage {@inheritDoc}\r
+     */\r
+    public void loopEnd(ParseStage stage){\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param xPos {@inheritDoc}\r
+     * @param yPos {@inheritDoc}\r
+     * @param zPos {@inheritDoc}\r
+     */\r
+    public void pmdVertexPosition(float xPos, float yPos, float zPos){\r
+        Pos3d position = this.currentVertex.getPosition();\r
+        position.setXPos(xPos);\r
+        position.setYPos(yPos);\r
+        position.setZPos(zPos);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param xVec {@inheritDoc}\r
+     * @param yVec {@inheritDoc}\r
+     * @param zVec {@inheritDoc}\r
+     */\r
+    public void pmdVertexNormal(float xVec, float yVec, float zVec){\r
+        Vec3d normal = this.currentVertex.getNormal();\r
+        normal.setXVal(xVec);\r
+        normal.setYVal(yVec);\r
+        normal.setZVal(zVec);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param uVal {@inheritDoc}\r
+     * @param vVal {@inheritDoc}\r
+     */\r
+    public void pmdVertexUV(float uVal, float vVal){\r
+        Pos2d uv = this.currentVertex.getUVPosition();\r
+        uv.setXPos(uVal);\r
+        uv.setYPos(vVal);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param boneId1 {@inheritDoc}\r
+     * @param boneId2 {@inheritDoc}\r
+     * @param weightForB1 {@inheritDoc}\r
+     */\r
+    public void pmdVertexWeight(int boneId1, int boneId2, int weightForB1){\r
+        BoneInfo bone1 = prepareBone(boneId1);\r
+        BoneInfo bone2 = prepareBone(boneId2);\r
+\r
+        this.currentVertex.setBonePair(bone1, bone2);\r
+        this.currentVertex.setWeightA(weightForB1);\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param hideEdge {@inheritDoc}\r
+     */\r
+    public void pmdVertexEdge(boolean hideEdge){\r
+        this.currentVertex.setEdgeAppearance( ! hideEdge );\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param vertexId1 {@inheritDoc}\r
+     * @param vertexId2 {@inheritDoc}\r
+     * @param vertexId3 {@inheritDoc}\r
+     */\r
+    public void pmdSurfaceTriangle(int vertexId1,\r
+                                      int vertexId2,\r
+                                      int vertexId3 ){\r
+        Vertex vtx1 = this.vertexList.get(vertexId1);\r
+        Vertex vtx2 = this.vertexList.get(vertexId2);\r
+        Vertex vtx3 = this.vertexList.get(vertexId3);\r
+\r
+        this.currentSurface.setTriangle(vtx1, vtx2, vtx3);\r
+\r
+        return;\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..18b75e6
--- /dev/null
@@ -0,0 +1,240 @@
+/*\r
+ * building text info\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd.pmdloader;\r
+\r
+import java.util.Iterator;\r
+import java.util.List;\r
+import jp.sourceforge.mikutoga.corelib.I18nText;\r
+import jp.sourceforge.mikutoga.parser.ParseStage;\r
+import jp.sourceforge.mikutoga.parser.pmd.PmdBasicHandler;\r
+import jp.sourceforge.mikutoga.parser.pmd.PmdEngHandler;\r
+import jp.sourceforge.mikutoga.pmd.BoneGroup;\r
+import jp.sourceforge.mikutoga.pmd.BoneInfo;\r
+import jp.sourceforge.mikutoga.pmd.MorphPart;\r
+import jp.sourceforge.mikutoga.pmd.PmdModel;\r
+\r
+/**\r
+ * テキスト関係の通知をパーサから受け取る。\r
+ */\r
+class TextBuilder implements PmdBasicHandler, PmdEngHandler {\r
+\r
+    private final PmdModel model;\r
+\r
+    private final I18nText modelName;\r
+    private final I18nText description;\r
+\r
+    private final List<BoneInfo> boneList;\r
+    private Iterator<BoneInfo> boneIt;\r
+    private BoneInfo currentBone = null;\r
+\r
+    private List<MorphPart> morphPartList;\r
+    private Iterator<MorphPart> morphPartIt;\r
+    private MorphPart currentMorphPart = null;\r
+\r
+    private final List<BoneGroup> boneGroupList;\r
+    private Iterator<BoneGroup> boneGroupIt;\r
+    private BoneGroup currentBoneGroup = null;\r
+\r
+    private boolean hasMoreData = false;\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     * @param model モデル\r
+     */\r
+    TextBuilder(PmdModel model){\r
+        super();\r
+\r
+        this.model = model;\r
+\r
+        this.modelName   = model.getModelName();\r
+        this.description = model.getDescription();\r
+\r
+        this.boneList      = model.getBoneList();\r
+        this.boneGroupList = model.getBoneGroupList();\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * PMDファイル中の出現順で各モーフを格納するためのリストを設定する。\r
+     * 主な用途はモーフ和英名の突き合わせ作業。\r
+     * @param list モーフ格納リスト\r
+     */\r
+    void setMorphPartList(List<MorphPart> list){\r
+        this.morphPartList = list;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     */\r
+    public void pmdParseStart(){\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param hasMoreData {@inheritDoc}\r
+     */\r
+    public void pmdParseEnd(boolean hasMoreData){\r
+        this.hasMoreData = hasMoreData;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param stage {@inheritDoc}\r
+     * @param loops {@inheritDoc}\r
+     */\r
+    public void loopStart(ParseStage stage, int loops){\r
+        assert stage instanceof PmdEngStage;\r
+\r
+        if(stage == PmdEngHandler.ENGBONE_LIST){\r
+            this.boneIt = this.boneList.iterator();\r
+            if(this.boneIt.hasNext()){\r
+                this.currentBone = this.boneIt.next();\r
+            }\r
+        }else if(stage == PmdEngHandler.ENGMORPH_LIST){\r
+            this.morphPartIt = this.morphPartList.iterator();\r
+\r
+            // 「base」モーフを読み飛ばす\r
+            assert this.morphPartIt.hasNext();\r
+            MorphPart part = this.morphPartIt.next();\r
+            assert part != null;\r
+\r
+            if(this.morphPartIt.hasNext()){\r
+                this.currentMorphPart = this.morphPartIt.next();\r
+            }\r
+        }else if(stage == PmdEngHandler.ENGBONEGROUP_LIST){\r
+            this.boneGroupIt = this.boneGroupList.iterator();\r
+\r
+            // デフォルトボーングループを読み飛ばす\r
+            assert this.boneGroupIt.hasNext();\r
+            BoneGroup group = this.boneGroupIt.next();\r
+            assert group != null;\r
+\r
+            if(this.boneGroupIt.hasNext()){\r
+                this.currentBoneGroup = this.boneGroupIt.next();\r
+            }\r
+        }else{\r
+            assert false;\r
+            throw new AssertionError();\r
+        }\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param stage {@inheritDoc}\r
+     */\r
+    public void loopNext(ParseStage stage){\r
+        assert stage instanceof PmdEngStage;\r
+\r
+        if(stage == PmdEngHandler.ENGBONE_LIST){\r
+            if(this.boneIt.hasNext()){\r
+                this.currentBone = this.boneIt.next();\r
+            }\r
+        }else if(stage == PmdEngHandler.ENGMORPH_LIST){\r
+            if(this.morphPartIt.hasNext()){\r
+                this.currentMorphPart = this.morphPartIt.next();\r
+            }\r
+        }else if(stage == PmdEngHandler.ENGBONEGROUP_LIST){\r
+            if(this.boneGroupIt.hasNext()){\r
+                this.currentBoneGroup = this.boneGroupIt.next();\r
+            }\r
+        }else{\r
+            assert false;\r
+            throw new AssertionError();\r
+        }\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param stage {@inheritDoc}\r
+     */\r
+    public void loopEnd(ParseStage stage){\r
+        assert stage instanceof PmdEngStage;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param ver {@inheritDoc}\r
+     */\r
+    public void pmdHeaderInfo(float ver){\r
+        this.model.setHeaderVersion(ver);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param modelName {@inheritDoc}\r
+     * @param description {@inheritDoc}\r
+     */\r
+    public void pmdModelInfo(String modelName, String description){\r
+        this.modelName  .setPrimaryText(modelName);\r
+        this.description.setPrimaryText(description);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param hasEnglishInfo {@inheritDoc}\r
+     */\r
+    public void pmdEngEnabled(boolean hasEnglishInfo){\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param modelName {@inheritDoc}\r
+     * @param description {@inheritDoc}\r
+     */\r
+    public void pmdEngModelInfo(String modelName, String description){\r
+        this.modelName  .setGlobalText(modelName);\r
+        this.description.setGlobalText(description);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param boneName {@inheritDoc}\r
+     */\r
+    public void pmdEngBoneInfo(String boneName){\r
+        this.currentBone.getBoneName().setGlobalText(boneName);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param morphName {@inheritDoc}\r
+     */\r
+    public void pmdEngMorphInfo(String morphName){\r
+        this.currentMorphPart.getMorphName().setGlobalText(morphName);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param groupName {@inheritDoc}\r
+     */\r
+    public void pmdEngBoneGroupInfo(String groupName){\r
+        this.currentBoneGroup.getGroupName().setGlobalText(groupName);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 読み残したデータがあるか判定する。\r
+     * @return 読み残したデータがあればtrue\r
+     */\r
+    public boolean hasMoreData(){\r
+        return this.hasMoreData;\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..bc92e23
--- /dev/null
@@ -0,0 +1,79 @@
+/*\r
+ * building toon information\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd.pmdloader;\r
+\r
+import jp.sourceforge.mikutoga.parser.ParseStage;\r
+import jp.sourceforge.mikutoga.parser.pmd.PmdLimits;\r
+import jp.sourceforge.mikutoga.parser.pmd.PmdToonHandler;\r
+import jp.sourceforge.mikutoga.pmd.PmdModel;\r
+import jp.sourceforge.mikutoga.pmd.ToonMap;\r
+\r
+/**\r
+ * トゥーン関係の通知をパーサから受け取る。\r
+ */\r
+class ToonBuilder implements PmdToonHandler {\r
+\r
+    private final PmdModel model;\r
+\r
+    private ToonMap toonMap;\r
+    private int index;\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     * @param model モデル\r
+     */\r
+    ToonBuilder(PmdModel model){\r
+        this.model = model;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param stage {@inheritDoc}\r
+     * @param loops {@inheritDoc}\r
+     */\r
+    public void loopStart(ParseStage stage, int loops){\r
+        assert stage == PmdToonHandler.TOON_LIST;\r
+        assert loops == PmdLimits.TOON_FIXEDNUM;\r
+\r
+        this.toonMap = new ToonMap();\r
+        this.index = 0;\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param stage {@inheritDoc}\r
+     */\r
+    public void loopNext(ParseStage stage){\r
+        assert stage == PmdToonHandler.TOON_LIST;\r
+        this.index++;\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param stage {@inheritDoc}\r
+     */\r
+    public void loopEnd(ParseStage stage){\r
+        assert stage == PmdToonHandler.TOON_LIST;\r
+        this.model.setToonMap(this.toonMap);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @param toonFileName {@inheritDoc}\r
+     */\r
+    public void pmdToonFileInfo(String toonFileName){\r
+        this.toonMap.setIndexedToon(this.index, toonFileName);\r
+        return;\r
+    }\r
+\r
+}\r
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 (file)
index 0000000..c1f0ca6
--- /dev/null
@@ -0,0 +1,15 @@
+/*\r
+ * package information for Javadoc\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+/**\r
+ * PMDモデルファイルをパースし、\r
+ * 内容を反映したオブジェクト群を構築するためのライブラリ。\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd.pmdloader;\r
+\r
+/* EOF */\r
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 (file)
index 0000000..73e25f9
--- /dev/null
@@ -0,0 +1,1275 @@
+/*\r
+ * pmd-xml exporter\r
+ *\r
+ * License : The MIT License\r
+ * Copyright(c) 2010 MikuToga Partners\r
+ */\r
+\r
+package jp.sourceforge.mikutoga.pmd.xml;\r
+\r
+import java.awt.Color;\r
+import java.io.IOException;\r
+import java.io.OutputStream;\r
+import java.util.List;\r
+import java.util.Map;\r
+import jp.sourceforge.mikutoga.corelib.I18nText;\r
+import jp.sourceforge.mikutoga.corelib.SerialNumbered;\r
+import jp.sourceforge.mikutoga.pmd.BoneGroup;\r
+import jp.sourceforge.mikutoga.pmd.BoneInfo;\r
+import jp.sourceforge.mikutoga.pmd.BoneType;\r
+import jp.sourceforge.mikutoga.pmd.Deg3d;\r
+import jp.sourceforge.mikutoga.pmd.DynamicsInfo;\r
+import jp.sourceforge.mikutoga.pmd.IKChain;\r
+import jp.sourceforge.mikutoga.pmd.JointInfo;\r
+import jp.sourceforge.mikutoga.pmd.Material;\r
+import jp.sourceforge.mikutoga.pmd.MorphPart;\r
+import jp.sourceforge.mikutoga.pmd.MorphType;\r
+import jp.sourceforge.mikutoga.pmd.MorphVertex;\r
+import jp.sourceforge.mikutoga.pmd.PmdModel;\r
+import jp.sourceforge.mikutoga.pmd.Pos2d;\r
+import jp.sourceforge.mikutoga.pmd.Pos3d;\r
+import jp.sourceforge.mikutoga.pmd.Rad3d;\r
+import jp.sourceforge.mikutoga.pmd.RigidGroup;\r
+import jp.sourceforge.mikutoga.pmd.RigidInfo;\r
+import jp.sourceforge.mikutoga.pmd.RigidShape;\r
+import jp.sourceforge.mikutoga.pmd.RigidShapeType;\r
+import jp.sourceforge.mikutoga.pmd.ShadeInfo;\r
+import jp.sourceforge.mikutoga.pmd.Surface;\r
+import jp.sourceforge.mikutoga.pmd.ToonMap;\r
+import jp.sourceforge.mikutoga.pmd.TripletRange;\r
+import jp.sourceforge.mikutoga.pmd.Vec3d;\r
+import jp.sourceforge.mikutoga.pmd.Vertex;\r
+import jp.sourceforge.mikutoga.xml.BasicXmlExporter;\r
+import jp.sourceforge.mikutoga.xml.XmlResourceResolver;\r
+\r
+/**\r
+ * XML形式でPMDモデルデータを出力する。\r
+ */\r
+public class PmdXmlExporter extends BasicXmlExporter{\r
+\r
+    private static final String GENERATOR = "Mikutoga" + " Ver 0.0.1";\r
+    private static final String TOP_COMMENT =\r
+            "  MikuMikuDance\n    model-data(*.pmd) on XML";\r
+    private static final String SCHEMA_LOCATION =\r
+            PmdXmlResources.NS_PMDXML + " " + PmdXmlResources.SCHEMA_PMDXML;\r
+\r
+    /** 改行文字列 CR。 */\r
+    private static final String CR = "\r";       // 0x0d\r
+    /** 改行文字列 LF。 */\r
+    private static final String LF = "\n";       // 0x0a\r
+    /** 改行文字列 CRLF。 */\r
+    private static final String CRLF = CR + LF;  // 0x0d, 0x0a\r
+\r
+    private static final String PFX_SURFACEGROUP = "sg";\r
+    private static final String PFX_TOONFILE = "tf";\r
+    private static final String PFX_VERTEX = "vtx";\r
+    private static final String PFX_BONE = "bn";\r
+    private static final String PFX_RIGID = "rd";\r
+    private static final String PFX_RIGIDGROUP = "rg";\r
+\r
+    private static final String BONETYPE_COMMENT =\r
+          "Bone types:\n"\r
+        + "[0 : ROTATE      : Rotate       : 回転           :]\n"\r
+        + "[1 : ROTMOV      : Rotate/Move  : 回転/移動      :]\n"\r
+        + "[2 : IK          : IK           : IK             :]\n"\r
+        + "[3 : UNKNOWN     : Unknown      : 不明           :]\n"\r
+        + "[4 : UNDERIK     : Under IK     : IK影響下(回転) :]\n"\r
+        + "[5 : UNDERROT    : Under rotate : 回転影響下     :]\n"\r
+        + "[6 : IKCONNECTED : IK connected : IK接続先       :]\n"\r
+        + "[7 : HIDDEN      : Hidden       : 非表示         :]\n"\r
+        + "[8 : TWIST       : Twist        : 捩り           :]\n"\r
+        + "[9 : LINKEDROT   : Linked Rotate: 回転連動       :]\n";\r
+\r
+    private static final String MORPHTYPE_COMMENT =\r
+          "Morph types:\n"\r
+        + "[1 : EYEBROW : まゆ   ]\n"\r
+        + "[2 : EYE     : 目     ]\n"\r
+        + "[3 : LIP     : リップ ]\n"\r
+        + "[4 : EXTRA   : その他 ]\n";\r
+\r
+    private static final String RIGIDBEHAVIOR_COMMENT =\r
+          "Rigid behavior types:\n"\r
+        + "[0 : FOLLOWBONE    : ボーン追従       ]\n"\r
+        + "[1 : ONLYDYNAMICS  : 物理演算         ]\n"\r
+        + "[2 : BONEDDYNAMICS : ボーン位置合わせ ]\n";\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     * 文字エンコーディングはUTF-8が用いられる。\r
+     * @param stream 出力ストリーム\r
+     */\r
+    public PmdXmlExporter(OutputStream stream){\r
+        super(stream);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 任意の文字列がBasicLatin文字のみから構成されるか判定する。\r
+     * @param seq 文字列\r
+     * @return null、長さ0もしくはBasicLatin文字のみから構成されるならtrue\r
+     */\r
+    public static boolean hasOnlyBasicLatin(CharSequence seq){\r
+        if(seq == null) return true;\r
+        int length = seq.length();\r
+        for(int pos = 0; pos < length; pos++){\r
+            char ch = seq.charAt(pos);\r
+            if(ch > 0x007f) return false;\r
+        }\r
+        return true;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * @return {@inheritDoc}\r
+     * @throws IOException {@inheritDoc}\r
+     */\r
+    @Override\r
+    public PmdXmlExporter ind() throws IOException{\r
+        super.ind();\r
+        return this;\r
+    }\r
+\r
+    /**\r
+     * 文字参照によるエスケープを補佐するためのコメントを出力する。\r
+     * @param seq 文字列\r
+     * @return this本体\r
+     * @throws IOException 出力エラー\r
+     */\r
+    protected PmdXmlExporter putUnescapedComment(CharSequence seq)\r
+            throws IOException{\r
+        if( ! isBasicLatinOnlyOut() ) return this;\r
+        if(hasOnlyBasicLatin(seq)) return this;\r
+        put(' ').putLineComment(seq);\r
+        return this;\r
+    }\r
+\r
+    /**\r
+     * 多言語化された各種識別名を出力する。\r
+     * プライマリ名は出力対象外。\r
+     * @param text 多言語文字列\r
+     * @return this本体\r
+     * @throws IOException 出力エラー\r
+     */\r
+    protected PmdXmlExporter putI18nName(I18nText text) throws IOException{\r
+        for(String lang639 : text.lang639CodeList()){\r
+            if(lang639.equals(I18nText.CODE639_PRIMARY)) continue;\r
+            String name = text.getText(lang639);\r
+            ind().put("<i18nName ");\r
+            putAttr("lang", lang639).put(' ');\r
+            putAttr("name", name);\r
+            put(" />");\r
+            putUnescapedComment(name);\r
+            ln();\r
+        }\r
+        return this;\r
+    }\r
+\r
+    /**\r
+     * 番号付けされたID(IDREF)属性を出力する。\r
+     * @param attrName 属性名\r
+     * @param prefix IDプレフィクス\r
+     * @param num 番号\r<