OSDN Git Service

初回コミット
authorOlyutorskii <olyutorskii@users.osdn.me>
Wed, 24 Aug 2011 16:01:59 +0000 (01:01 +0900)
committerOlyutorskii <olyutorskii@users.osdn.me>
Wed, 24 Aug 2011 16:01:59 +0000 (01:01 +0900)
47 files changed:
.hgignore [new file with mode: 0644]
CHANGELOG.txt [new file with mode: 0644]
LICENSE.txt [new file with mode: 0644]
README.txt [new file with mode: 0644]
build.xml [new file with mode: 0644]
pom.xml [new file with mode: 0644]
src/main/assembly/descriptor.xml [new file with mode: 0644]
src/main/config/checks.xml [new file with mode: 0644]
src/main/config/pmdrules.xml [new file with mode: 0644]
src/main/config/suppressions.xml [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/vmd/model/BezierParam.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/vmd/model/BoneMotion.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/vmd/model/CameraMotion.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/vmd/model/CameraRotation.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/vmd/model/LuminousColor.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/vmd/model/LuminousMotion.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/vmd/model/LuminousVector.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/vmd/model/MorphMotion.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/vmd/model/NamedListMap.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/vmd/model/PosCurve.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/vmd/model/ShadowMode.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/vmd/model/ShadowMotion.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/vmd/model/VmdMotion.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/vmd/model/binio/BasicExporter.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/vmd/model/binio/BasicLoader.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/vmd/model/binio/CameraExporter.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/vmd/model/binio/CameraLoader.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/vmd/model/binio/LightingExporter.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/vmd/model/binio/LightingLoader.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/vmd/model/binio/VmdExporter.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/vmd/model/binio/VmdLoader.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/vmd/model/binio/package-info.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/vmd/model/package-info.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/vmd/model/xml/VmdXmlExporter.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/vmd/model/xml/VmdXmlResources.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/vmd/model/xml/Xml.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/vmd/model/xml/Xml2VmdLoader.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/vmd/model/xml/XmlCameraLoader.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/vmd/model/xml/XmlLightingLoader.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/vmd/model/xml/package-info.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/vmd2xml/ArgInfo.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/vmd2xml/ValidationHandler.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/vmd2xml/Vmd2Xml.java [new file with mode: 0644]
src/main/java/jp/sourceforge/mikutoga/vmd2xml/package-info.java [new file with mode: 0644]
src/main/resources/jp/sourceforge/mikutoga/vmd/model/xml/resources/vmdxml-110820.dtd [new file with mode: 0644]
src/main/resources/jp/sourceforge/mikutoga/vmd/model/xml/resources/vmdxml-110820.xsd [new file with mode: 0644]
src/main/resources/jp/sourceforge/mikutoga/vmd2xml/resources/version.properties [new file with mode: 0644]

diff --git a/.hgignore b/.hgignore
new file mode 100644 (file)
index 0000000..0da911a
--- /dev/null
+++ b/.hgignore
@@ -0,0 +1,13 @@
+syntax: regexp
+
+\.orig$
+\.orig\..*$
+\.chg\..*$
+\.rej$
+\.conflict\~$
+^maven-build\.properties$
+^maven-build\.xml$
+^nb-configuration\.xml$
+^nbactions\.xml$
+^target$
+^tmp$
diff --git a/CHANGELOG.txt b/CHANGELOG.txt
new file mode 100644 (file)
index 0000000..5ece224
--- /dev/null
@@ -0,0 +1,11 @@
+[UTF-8 Japanese]
+
+
+Vmd2XML 変更履歴
+
+
+1.101.2 (2011-08-XX)
+    ・初回リリース。
+
+
+--- EOF ---
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644 (file)
index 0000000..7386b8a
--- /dev/null
@@ -0,0 +1,34 @@
+[UTF-8 English & Japanese]
+
+The MIT License
+
+
+Copyright(c) 2010 olyutorskii
+
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+
+Vmd2XMLオリジナル制作者自身からのコメント:
+
+  ※ 少なくともこのソフトウェアの実行、複製、配布、改造は自由です。
+  ※ 少なくともこのソフトウェアは無保証です。
+
+
+--- EOF ---
diff --git a/README.txt b/README.txt
new file mode 100644 (file)
index 0000000..abb3bca
--- /dev/null
@@ -0,0 +1,80 @@
+[UTF-8 Japanese]
+
+                               V m d 2 X M L
+                                  Readme
+
+                                              Copyright(c) 2011 olyutorskii
+
+
+=== Vmd2XMLとは ===
+
+Vmd2XMLは、3D動画制作ツール、MikuMikuDance(MMD)で用いられる
+モーションデータファイル(*.vmd)の内容を、XML形式のデータファイルと
+交換するためのアプリケーションです。
+Vmd2XMLは、MikuTogaプロジェクトの派生物として誕生しました。
+
+MikuTogaプロジェクトは、MMDによる3Dアニメーション制作を支援するプログラムの
+整備のために発足した、オープンソースプロジェクトです。
+
+Vmd2XMLは、同じMikuTogaプロジェクト内のTogaGemライブラリを用いて
+開発が進められています。
+
+※ MMD開発者の樋口M氏は、MikuTogaの開発活動に一切関与していません。
+  Vmd2XMLに関する問い合わせをを樋口M氏へ投げかけないように!約束だよ!
+
+
+=== 実行環境 ===
+
+ - Vmd2XMLはJava言語(JLS3)で記述されたプログラムです。
+ - Vmd2XMLはJRE1.6に準拠したJava実行環境で利用できるように作られています。
+   原則として、JRE1.6に準拠した実行系であれば、プラットフォームを選びません。
+
+
+=== アーカイブ管理体制 ===
+
+  このアーカイブは、UTF-8による開発環境を前提として構成されています。
+  このアーカイブの原本となる開発資産は、
+      http://hg.sourceforge.jp/view/mikutoga/Vmd2XML
+  を上位に持つMercurialリポジトリで管理されています。
+  アーカイブの代わりにMercurialを通じての開発資産へのアクセスが可能です。
+  # hg clone http://hg.sourceforge.jp/view/mikutoga/Vmd2XML
+
+
+=== 開発プロジェクト運営元 ===
+
+  http://sourceforge.jp/projects/mikutoga/ まで。
+
+
+=== ディレクトリ内訳構成 ===
+
+基本的にはMaven2のmaven-archetype-quickstart構成に準じます。
+
+./README.txt
+    あなたが今見てるこれ。
+
+./CHANGELOG.txt
+    変更履歴。
+
+./LICENSE.txt
+    ライセンスに関して。
+
+./pom.xml
+    Maven2用プロジェクト構成定義ファイル。
+
+./src/main/java/
+    Javaのソースコード。
+
+./src/test/java/
+    JUnit 4.* 用のユニットテストコード。
+
+./src/main/config/
+    各種ビルド・構成管理に必要なファイル群。
+
+./src/main/config/checks.xml
+    Checkstyle用configファイル。
+
+./src/main/config/pmdrules.xml
+    PMD用ルール定義ファイル。
+
+
+--- EOF ---
diff --git a/build.xml b/build.xml
new file mode 100644 (file)
index 0000000..9da929c
--- /dev/null
+++ b/build.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- ====================================================================== -->
+<!-- Ant build file (http://ant.apache.org/) for Ant 1.6.2 or above.        -->
+<!-- ====================================================================== -->
+
+<project name="vmd2xml" default="package" basedir=".">
+
+<!-- ====================================================================== -->
+<!-- Import maven-build.xml into the current project                        -->
+<!-- Maven2 command "mvn ant:ant" will put maven-build.xml                  -->
+<!-- ====================================================================== -->
+
+    <import optional="true" file="maven-build.xml"/>
+
+<!-- ====================================================================== -->
+<!-- Help target                                                            -->
+<!-- ====================================================================== -->
+
+    <target name="help">
+        <echo message="Please run: $ant -projecthelp"/>
+    </target>
+
+<!-- ====================================================================== -->
+<!-- sanitize files for native environment                                  -->
+<!-- ====================================================================== -->
+
+    <target description="sanitize files" name="sanitize" >
+        <echo message="sanitize files..." />
+
+        <fixcrlf
+            srcDir="./src/" includes="**/*.java"
+            encoding="UTF-8" outputencoding="UTF-8"
+            tablength="4" tab="remove" javafiles="true"
+            eof="remove"
+        />
+
+        <fixcrlf
+            srcDir="./src/" includes="**/*.xml,**/*.xsd"
+            encoding="UTF-8" outputencoding="UTF-8"
+            tablength="8" tab="remove"
+            eol="lf"
+            eof="remove"
+        />
+
+        <fixcrlf
+            srcDir="./src/" includes="**/*.properties"
+            encoding="ISO-8859-1" outputencoding="ISO-8859-1"
+            eol="lf"
+            eof="remove"
+        />
+
+        <fixcrlf
+            srcDir="." includes="*.txt"
+            encoding="UTF-8" outputencoding="UTF-8"
+            tablength="8" tab="remove"
+            eof="remove"
+        />
+
+        <chmod type="file" perm="a-x">
+            <fileset dir="." includes="**/*" excludes="**/*.sh" />
+        </chmod>
+
+    </target>
+
+</project>
+
+<!-- EOF -->
diff --git a/pom.xml b/pom.xml
new file mode 100644 (file)
index 0000000..62f3fcc
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,392 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<!--
+    Vmd2XML
+    Maven2用プロジェクト構成定義ファイル
+-->
+
+<project
+  xmlns="http://maven.apache.org/POM/4.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
+  http://maven.apache.org/maven-v4_0_0.xsd"
+>
+    <modelVersion>4.0.0</modelVersion>
+    <!--parent/-->
+
+    <groupId>jp.sourceforge.mikutoga</groupId>
+    <artifactId>vmd2xml</artifactId>
+
+    <version>1.101.2-SNAPSHOT</version>
+
+    <packaging>jar</packaging>
+    <name>Vmd2XML</name>
+
+    <description><!--
+-->Vmd2XML is a VMD-XML converter for MikuMikuDance.<!--
+ -->
+    </description>
+
+    <url>http://sourceforge.jp/projects/mikutoga/</url>
+    <inceptionYear>2011</inceptionYear>
+
+    <organization>
+        <name>MikuToga Partners</name>
+        <url>http://sourceforge.jp/projects/mikutoga/devel/</url>
+    </organization>
+
+    <licenses>
+        <license>
+            <name>The MIT License</name>
+            <url>http://www.opensource.org/licenses/mit-license.php</url>
+            <distribution>manual</distribution>
+        </license>
+    </licenses>
+
+    <developers>
+        <developer>
+            <id>olyutorskii</id>
+            <url>http://sites.google.com/site/olyutorskiipit/</url>
+            <organization>MikuToga Partners</organization>
+            <organizationUrl>http://sourceforge.jp/projects/mikutoga/devel/</organizationUrl>
+            <roles>
+                <role>Project Founder</role>
+                <role>Java Developer</role>
+            </roles>
+        </developer>
+    </developers>
+
+    <contributors/>
+    <mailingLists/>
+
+    <prerequisites>
+        <maven>2.2.1</maven>
+    </prerequisites>
+
+    <modules/>
+
+    <scm>
+        <connection>scm:hg:http://hg.sourceforge.jp/view/mikutoga/Vmd2XML</connection>
+        <developerConnection>scm:hg:ssh://hg.sourceforge.jp//hgroot/mikutoga/Vmd2XML</developerConnection>
+        <url>http://hg.sourceforge.jp/view/mikutoga/Vmd2XML</url>
+    </scm>
+
+    <issueManagement>
+        <system>SourceForge.JP</system>
+        <url>http://sourceforge.jp/projects/mikutoga/ticket/</url>
+    </issueManagement>
+
+    <ciManagement/>
+    <distributionManagement/>
+
+    <properties>
+        <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
+
+        <maven.compiler.source>1.6</maven.compiler.source>
+        <maven.compiler.target>1.6</maven.compiler.target>
+
+        <maven.compiler.showDeprecation>true</maven.compiler.showDeprecation>
+        <maven.compiler.showWarnings>true</maven.compiler.showWarnings>
+
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+
+        <project.myrepoconf>${project.basedir}/src/main/config</project.myrepoconf>
+
+        <project.transformer>
+            org.apache.maven.plugins.shade.resource.ManifestResourceTransformer
+        </project.transformer>
+        <project.mainentry>jp.sourceforge.mikutoga.vmd2xml.Vmd2Xml</project.mainentry>
+
+        <!-- Walkaround for Maven3&PMD-plugin bug -->
+        <targetJdk>${maven.compiler.source}</targetJdk>
+
+        <!-- Walkaround for Maven3&checkstyle bug -->
+        <checkstyle.config.location>${project.myrepoconf}/checks.xml</checkstyle.config.location>
+
+    </properties>
+
+    <dependencyManagement/>
+
+    <dependencies>
+
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>[4.8.2,)</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>jp.sourceforge.mikutoga</groupId>
+            <artifactId>togagem</artifactId>
+            <version>2.101.2</version>
+            <scope>compile</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>java3d</groupId>
+            <artifactId>vecmath</artifactId>
+            <version>1.3.1</version>
+        </dependency>
+
+    </dependencies>
+
+    <repositories/>
+    <pluginRepositories/>
+
+    <build>
+        <pluginManagement/>
+
+        <plugins>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-clean-plugin</artifactId>
+                <version>2.4.1</version>
+                <configuration>
+                    <filesets>
+                        <fileset>
+                            <directory>${project.basedir}</directory>
+                            <includes>
+                                <include>**/.DS_Store</include>
+                                <include>**/Thumbs.db</include>
+                                <include>**/core</include>
+                            </includes>
+                        </fileset>
+                    </filesets>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>2.3.2</version>
+                <configuration>
+                    <source>1.6</source>  <!-- for NetBeans IDE -->
+                    <target>1.6</target>
+                    <showDeprecation>true</showDeprecation>
+                    <showWarnings>true</showWarnings>
+                    <compilerArguments>
+                        <Xlint/>
+                    </compilerArguments>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <version>2.3.2</version>
+                <configuration>
+                    <archive>
+                        <manifestEntries>
+                            <Main-Class>${project.mainentry}</Main-Class>
+                            <Built-By>${project.organization.name}</Built-By>
+                        </manifestEntries>
+                    </archive>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+                <version>2.2.1</version>
+                <configuration>
+                    <descriptors>
+                        <descriptor>src/main/assembly/descriptor.xml</descriptor>
+                    </descriptors>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-shade-plugin</artifactId>
+                <version>1.4</version>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                          <goal>shade</goal>
+                        </goals>
+                        <configuration>
+                            <artifactSet>
+                                <includes>
+                                    <include>jp.sourceforge.mikutoga:togagem</include>
+                                </includes>
+                                <excludes>
+                                    <exclude>junit:junit</exclude>
+                                </excludes>
+                            </artifactSet>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-enforcer-plugin</artifactId>
+                <version>1.0.1</version>
+                <configuration>
+                    <rules>
+                        <requireMavenVersion>
+                            <version>[2.2,3)</version>
+                        </requireMavenVersion>
+                        <requireJavaVersion>
+                            <version>[1.6,)</version>
+                        </requireJavaVersion>
+                    </rules>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-source-plugin</artifactId>
+                <version>2.1.2</version>
+                <configuration>
+                    <includePom>true</includePom>
+                    <archive>
+                        <manifestEntries>
+                            <Built-By>${project.organization.name}</Built-By>
+                        </manifestEntries>
+                    </archive>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>attach-sources</id>
+                        <phase>verify</phase>
+                        <goals>
+                          <goal>jar-no-fork</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+
+        </plugins>
+
+        <resources>
+
+            <resource>
+                <directory>src/main/resources</directory>
+                <filtering>true</filtering>
+                <includes>
+                    <include>**/version.properties</include>
+                </includes>
+            </resource>
+
+            <resource>
+                <directory>src/main/resources</directory>
+                <includes>
+                    <include>**/*.css</include>
+                    <include>**/*.html</include>
+                    <include>**/*.png</include>
+                    <include>**/*.properties</include>
+                    <include>**/*.txt</include>
+                    <include>**/*.xml</include>
+                    <include>**/*.xsd</include>
+                    <include>**/*.dtd</include>
+                </includes>
+                <excludes>
+                    <exclude>**/version.properties</exclude>
+                </excludes>
+            </resource>
+
+        </resources>
+
+    </build>
+
+    <reporting>
+
+        <plugins>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-site-plugin</artifactId>
+                <version>3.0</version>
+                <configuration>
+                    <locales>ja</locales>
+                    <generateReports>true</generateReports>
+                    <generateSitemap>true</generateSitemap>
+                    <inputEncoding>${project.build.sourceEncoding}</inputEncoding>
+                    <outputEncoding>${project.reporting.outputEncoding}</outputEncoding>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <version>2.8</version>
+                <configuration>
+                    <show>protected</show>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-checkstyle-plugin</artifactId>
+                <version>2.6</version>
+                <configuration>
+                    <configLocation>${checkstyle.config.location}</configLocation>
+                    <encoding>UTF-8</encoding>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-pmd-plugin</artifactId>
+                <version>2.5</version>
+                <configuration>
+                    <targetJdk>${maven.compiler.target}</targetJdk>
+                    <rulesets>
+                        <ruleset>${project.myrepoconf}/pmdrules.xml</ruleset>
+                    </rulesets>
+                </configuration>
+                <reportSets>
+                    <reportSet>
+                        <reports>
+                            <report>pmd</report>
+                            <report>cpd</report>
+                        </reports>
+                    </reportSet>
+                </reportSets>
+            </plugin>
+
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>findbugs-maven-plugin</artifactId>
+                <version>2.3.2</version>
+                <configuration>
+                    <effort>Max</effort>
+                    <threshold>Low</threshold>
+                    <inputEncoding>${project.build.sourceEncoding}</inputEncoding>
+                    <outputEncoding>${project.reporting.outputEncoding}</outputEncoding>
+                    <!--excludeFilterFile/-->
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>cobertura-maven-plugin</artifactId>
+                <version>2.5.1</version>
+            </plugin>
+
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>javancss-maven-plugin</artifactId>
+                <version>2.0</version>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jxr-plugin</artifactId>
+                <version>2.2</version>
+            </plugin>
+
+        </plugins>
+
+    </reporting>
+
+    <profiles/>
+
+</project>
+
+<!-- EOF -->
diff --git a/src/main/assembly/descriptor.xml b/src/main/assembly/descriptor.xml
new file mode 100644 (file)
index 0000000..b744cbd
--- /dev/null
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<assembly
+  xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0
+  http://maven.apache.org/xsd/assembly-1.1.0.xsd"
+>
+
+<!--
+    SourceForge.JP用リリースファイル構成定義ファイル
+    Maven2 assembly用
+-->
+
+    <id>src</id>
+
+    <formats>
+        <format>zip</format>
+    </formats>
+
+    <fileSets>
+        <fileSet>
+            <includes>
+                <include>*.txt</include>
+                <include>pom.xml</include>
+                <include>build.xml</include>
+            </includes>
+            <useDefaultExcludes>true</useDefaultExcludes>
+        </fileSet>
+        <fileSet>
+            <directory>src/</directory>
+            <useDefaultExcludes>true</useDefaultExcludes>
+        </fileSet>
+    </fileSets>
+
+</assembly>
+
+<!-- EOF -->
diff --git a/src/main/config/checks.xml b/src/main/config/checks.xml
new file mode 100644 (file)
index 0000000..0b45e05
--- /dev/null
@@ -0,0 +1,392 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!DOCTYPE module PUBLIC
+    "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
+    "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
+
+<!--
+    Checkstyle用チェック項目定義。
+
+    Checkstyle 5.0 以降向けに記述。
+
+    [ http://checkstyle.sourceforge.net/ ]
+
+    Copyright(c) 2010 olyutorskii
+-->
+
+
+<module name="Checker">
+
+    <property name="charset" value="UTF-8" />
+    <property name="localeCountry" value="JP" />
+    <property name="localeLanguage" value="ja" />
+    <property name="severity" value="error" />
+
+
+    <!-- Filters -->
+    <module name="SeverityMatchFilter" />
+    <module name="SuppressionFilter">
+        <property name="file" value="src/main/config/suppressions.xml" />
+    </module>
+    <module name="SuppressionCommentFilter" />
+    <module name="SuppressWithNearbyCommentFilter" />
+
+
+    <!-- Duplicate Code -->
+    <module name="StrictDuplicateCode">
+        <property name="min" value="20" />
+    </module>
+
+
+    <!-- Headers -->
+    <module name="RegexpHeader">
+        <property name="header" value="^/\*$\n^ \*( .*)?$\n^ \*/$\n" />
+        <property name="multiLines" value="2" />
+    </module>
+
+
+    <!-- Javadoc Comments -->
+    <module name="JavadocPackage" />
+
+
+    <!-- Miscellaneous -->
+    <module name="NewlineAtEndOfFile" />
+    <module name="Translation" />
+
+
+    <!-- Regexp -->
+    <module name="RegexpSingleline">
+        <property name="format" value="\s+$" />
+        <property name="minimum" value="0" />
+        <property name="maximum" value="0" />
+    </module>
+
+
+    <!-- Size Violations -->
+    <module name="FileLength" />
+
+
+    <!-- Whitespace -->
+    <module name="FileTabCharacter" />
+
+
+    <module name="TreeWalker">
+
+        <module name="FileContentsHolder" />
+
+
+    <!-- Annotations -->
+
+        <module name="AnnotationUseStyle" />
+        <module name="MissingDeprecated" />
+        <module name="MissingOverride" />
+        <module name="PackageAnnotation" />
+        <module name="SuppressWarnings" />
+
+
+    <!-- Block Checks -->
+
+        <module name="EmptyBlock" />
+        <module name="LeftCurly" />
+        <module name="NeedBraces">
+            <property name="tokens" value="LITERAL_DO" />
+        </module>
+        <module name="RightCurly" />
+        <module name="AvoidNestedBlocks" />
+
+
+    <!-- Class Design -->
+
+        <module name="VisibilityModifier" />
+        <module name="FinalClass" />
+        <module name="InterfaceIsType" />
+        <module name="HideUtilityClassConstructor" />
+<!--    <module name="DesignForExtension" />  -->
+        <module name="MutableException" />
+        <module name="ThrowsCount">
+            <property name="max" value="3" />
+        </module>
+
+
+    <!-- Coding -->
+
+        <module name="ArrayTrailingComma" />
+        <module name="AvoidInlineConditionals" />
+        <module name="CovariantEquals" />
+        <module name="DoubleCheckedLocking" />
+        <module name="EmptyStatement" />
+        <module name="EqualsAvoidNull" />
+        <module name="EqualsHashCode" />
+<!--    <module name="FinalLocalVariable" />  -->
+        <module name="HiddenField">
+            <property name="ignoreConstructorParameter" value="true" />
+            <property name="ignoreSetter" value="true" />
+            <property name="ignoreAbstractMethods" value="true" />
+        </module>
+        <module name="IllegalInstantiation" />
+        <module name="IllegalToken">
+            <property name="tokens" value="LITERAL_NATIVE, STATIC_IMPORT" />
+        </module>
+        <module name="IllegalTokenText">
+            <property name="tokens" value="NUM_INT, NUM_LONG" />
+            <property name="format" value="^0[^lx]" />
+            <property name="ignoreCase" value="true" />
+        </module>
+        <module name="InnerAssignment" />
+        <module name="MagicNumber" />
+        <module name="MissingSwitchDefault" />
+        <module name="ModifiedControlVariable" />
+        <module name="RedundantThrows">
+            <property name="allowUnchecked" value="true" />
+            <property name="allowSubclasses" value="true" />
+        </module>
+        <module name="SimplifyBooleanExpression" />
+        <module name="SimplifyBooleanReturn" />
+        <module name="StringLiteralEquality" />
+        <module name="NestedIfDepth" />
+        <module name="NestedTryDepth" />
+        <module name="NoClone" />
+        <module name="NoFinalizer" />
+        <module name="SuperClone" />
+        <module name="SuperFinalize" />
+        <module name="IllegalCatch" />
+        <module name="IllegalThrows" />
+        <module name="PackageDeclaration" />
+        <module name="JUnitTestCase" />
+        <module name="ReturnCount">
+            <property name="max" value="5" />
+        </module>
+        <module name="IllegalType" />
+        <module name="DeclarationOrder" />
+        <module name="ParameterAssignment" />
+<!--    <module name="ExplicitInitialization" />  -->
+        <module name="DefaultComesLast" />
+        <module name="MissingCtor" />
+        <module name="FallThrough" />
+        <module name="MultipleStringLiterals">
+            <property
+                name="ignoreStringsRegexp"
+                value="^&quot;&quot;$|^&quot;.&quot;$"
+            />
+        </module>
+        <module name="MultipleVariableDeclarations" />
+        <module name="UnnecessaryParentheses" />
+
+
+    <!-- Imports -->
+
+        <module name="AvoidStarImport" />
+        <module name="AvoidStaticImport" />
+        <module name="IllegalImport" />
+        <module name="RedundantImport" />
+        <module name="UnusedImports" />
+        <module name="ImportOrder" />
+<!--    <module name="ImportControl" />  -->
+
+
+    <!-- Javadoc Comments -->
+
+        <module name="JavadocType" />
+        <module name="JavadocMethod" />
+        <module name="JavadocVariable">
+            <property name="scope" value="protected" />
+        </module>
+        <module name="JavadocStyle">
+            <property
+                name="endOfSentenceFormat"
+                value="([。.?!][ \t\n\r\f&lt;])|([。.?!]$)" />
+            <property name="checkEmptyJavadoc" value="true" />
+            <property name="checkHtml" value="true" />
+        </module>
+<!--    <module name="WriteTag" />  -->
+
+
+    <!-- Metrics -->
+
+        <module name="BooleanExpressionComplexity" />
+        <module name="ClassDataAbstractionCoupling" />
+        <module name="ClassFanOutComplexity" />
+        <module name="CyclomaticComplexity" />
+        <module name="NPathComplexity" />
+        <module name="JavaNCSS" />
+
+
+    <!-- Miscellaneous -->
+
+        <module name="TodoComment">
+            <property name="format" value="TODO" />
+        </module>
+        <module name="UncommentedMain" />
+        <module name="UpperEll" />
+        <module name="ArrayTypeStyle" />
+<!--    <module name="FinalParameters" />  -->
+<!--    <module name="DescendantToken" />  -->
+<!--
+        <module name="Indentation">
+            <property name="caseIndent" value="0" />
+        </module>
+-->
+<!--    <module name="TrailingComment" />  -->
+        <module name="Regexp">
+            <property name="format" value="@author" />
+            <property name="illegalPattern" value="true" />
+        </module>
+        <module name="Regexp">
+            <property name="format" value="^ \* Copyright\(c\)" />
+        </module>
+        <module name="Regexp">
+            <property name="format" value="^ \* License : The MIT License" />
+            <property name="duplicateLimit" value="1" />
+        </module>
+
+
+    <!-- Modifiers -->
+
+        <module name="ModifierOrder" />
+        <module name="RedundantModifier" />
+
+
+    <!-- Naming Conventions -->
+
+        <module name="AbstractClassName">
+            <property
+                name="format"
+                value="^Abstract.*$|^.*Factory$|^.*Builder$|^.*Adapter$"
+            />
+        </module>
+        <module name="ClassTypeParameterName" />
+        <module name="ConstantName" />
+        <module name="LocalFinalVariableName">
+            <property name="format" value="^[a-z][_a-zA-Z0-9]*$" />
+        </module>
+        <module name="LocalVariableName">
+            <property name="format" value="^[a-z][_a-zA-Z0-9]*$" />
+        </module>
+        <module name="MemberName">
+            <property name="format" value="^[a-z][_a-zA-Z0-9]*$" />
+        </module>
+        <module name="MethodName" />
+        <module name="MethodTypeParameterName" />
+        <module name="PackageName" />
+        <module name="ParameterName">
+            <property name="format" value="^[a-z][_a-zA-Z0-9]*$" />
+        </module>
+        <module name="StaticVariableName" />
+        <module name="TypeName" />
+
+
+    <!-- Size Violations -->
+
+        <module name="ExecutableStatementCount" />
+        <module name="LineLength">
+            <property name="max" value="78" />
+        </module>
+        <module name="MethodLength" />
+        <module name="AnonInnerLength" />
+        <module name="ParameterNumber" />
+        <module name="OuterTypeNumber" />
+
+
+    <!-- Whitespace -->
+
+        <module name="GenericWhitespace" />
+        <module name="EmptyForInitializerPad" />
+        <module name="EmptyForIteratorPad" />
+        <module name="MethodParamPad">
+            <property
+                name="tokens"
+                value="CTOR_DEF, LITERAL_NEW, METHOD_DEF, SUPER_CTOR_CALL"
+            />
+        </module>
+        <module name="NoWhitespaceAfter">
+            <property name="allowLineBreaks" value="false" />
+            <property name="tokens" value="DEC, DOT, INC" />
+        </module>
+        <module name="NoWhitespaceBefore">
+            <property name="allowLineBreaks" value="false" />
+            <property name="tokens" value="POST_DEC, POST_INC" />
+        </module>
+        <module name="NoWhitespaceBefore">
+            <property name="allowLineBreaks" value="true" />
+            <property name="tokens" value="SEMI" />
+        </module>
+        <module name="OperatorWrap">
+            <property name="option" value="eol" />
+            <property
+                name="tokens"
+                value="ASSIGN,
+                       BAND_ASSIGN, BOR_ASSIGN, BXOR_ASSIGN,
+                       PLUS_ASSIGN, MINUS_ASSIGN,
+                       STAR_ASSIGN, DIV_ASSIGN, MOD_ASSIGN,
+                       SL_ASSIGN, SR_ASSIGN"
+            />
+        </module>
+        <module name="OperatorWrap">
+            <property name="option" value="nl" />
+            <property
+                name="tokens"
+                value="BAND, BOR, BXOR,
+                       MINUS, STAR, DIV, MOD,
+                       LAND, LOR,
+                       EQUAL"
+            />
+        </module>
+        <module name="ParenPad">
+            <property name="option" value="nospace" />
+            <property
+                name="tokens"
+                value="CTOR_CALL, METHOD_CALL, SUPER_CTOR_CALL"
+            />
+        </module>
+        <module name="TypecastParenPad" />
+        <module name="WhitespaceAfter">
+            <property name="tokens" value="COMMA, SEMI" />
+        </module>
+        <module name="WhitespaceAround">
+            <property
+                name="tokens"
+                value="ASSIGN,
+                       LAND, LOR,
+                       BAND, BOR, BXOR, BSR,
+                       BAND_ASSIGN, BOR_ASSIGN, BXOR_ASSIGN, BSR_ASSIGN,
+                       SL, SR,
+                       SL_ASSIGN, SR_ASSIGN,
+                       MINUS, STAR, DIV, MOD,
+                       PLUS_ASSIGN, MINUS_ASSIGN,
+                       STAR_ASSIGN, DIV_ASSIGN, MOD_ASSIGN,
+                       EQUAL, NOT_EQUAL, GT, GE, LT, LE,
+                       "
+            />
+        </module>
+
+
+<!-- 代用品で解決
+        <module name="Header" />
+        <module name="RegexpSingleline" />
+        <module name="RegexpMultiline" />
+        <module name="RegexpSinglelineJava" />
+-->
+
+<!-- バグ?
+        <module name="RequireThis" />
+-->
+<!-- 5.2 or later
+        <module name="InnerTypeLast" />
+-->
+
+<!-- 5.3 or later
+        <module name="NestedForDepth" />
+        <module name="OneStatementPerLine" />
+        <module name="OuterTypeFilename" />
+        <module name="MethodCount" />
+-->
+
+<!-- Obsolated
+        <module name="TabCharacter" />
+-->
+
+    </module>
+
+</module>
+
+<!-- EOF -->
diff --git a/src/main/config/pmdrules.xml b/src/main/config/pmdrules.xml
new file mode 100644 (file)
index 0000000..ad3dba9
--- /dev/null
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+    PMD用ルールセット定義
+
+    PMD [ http://pmd.sourceforge.net/ ] 4.2.5 以降用に記述されています。
+
+    Copyright(c) 2010 olyutorskii
+-->
+
+<ruleset
+  xmlns="http://pmd.sf.net/ruleset/1.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0
+  http://pmd.sf.net/ruleset_xml_schema.xsd"
+  xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd"
+  name="Custom ruleset"
+>
+
+    <rule ref="rulesets/basic.xml">
+        <exclude name="UnnecessaryReturn" />
+    </rule>
+
+    <rule ref="rulesets/braces.xml">
+        <exclude name="IfElseStmtsMustUseBraces" />
+        <exclude name="IfStmtsMustUseBraces" />
+    </rule>
+
+    <rule ref="rulesets/codesize.xml">
+        <exclude name="TooManyMethods" />
+    </rule>
+    <rule ref="rulesets/codesize.xml/TooManyMethods">
+        <properties>
+            <property name="maxmethods" value="25"/>
+        </properties>
+    </rule>
+
+    <rule ref="rulesets/clone.xml" />
+
+    <rule ref="rulesets/controversial.xml">
+        <exclude name="NullAssignment" />
+        <exclude name="OnlyOneReturn" />
+        <exclude name="DefaultPackage" />
+        <exclude name="DataflowAnomalyAnalysis" />
+    </rule>
+
+    <rule ref="rulesets/coupling.xml" />
+
+    <rule ref="rulesets/design.xml">
+        <exclude name="ConfusingTernary" />
+        <exclude name="UnnecessaryLocalBeforeReturn" />
+    </rule>
+
+    <rule ref="rulesets/finalizers.xml" />
+
+    <rule ref="rulesets/imports.xml" />
+
+    <rule ref="rulesets/logging-java.xml" />
+
+    <rule ref="rulesets/migrating.xml" />
+    <rule ref="rulesets/migrating_to_15.xml" />
+
+    <rule ref="rulesets/naming.xml">
+        <exclude name="ShortVariable" />
+        <exclude name="LongVariable" />
+        <exclude name="AvoidFieldNameMatchingMethodName" />
+    </rule>
+    <rule ref="rulesets/naming.xml/LongVariable">
+        <properties>
+            <property name="minimum" value="25"/>
+        </properties>
+    </rule>
+
+    <rule ref="rulesets/optimizations.xml">
+        <exclude name="LocalVariableCouldBeFinal" />
+        <exclude name="MethodArgumentCouldBeFinal" />
+    </rule>
+
+    <rule ref="rulesets/strictexception.xml">
+        <exclude name="AvoidThrowingNullPointerException" />
+    </rule>
+
+    <rule ref="rulesets/strings.xml" />
+
+    <rule ref="rulesets/sunsecure.xml" />
+
+    <rule ref="rulesets/typeresolution.xml" />
+
+    <rule ref="rulesets/unusedcode.xml" />
+
+</ruleset>
+
+<!-- EOF -->
diff --git a/src/main/config/suppressions.xml b/src/main/config/suppressions.xml
new file mode 100644 (file)
index 0000000..1d736c1
--- /dev/null
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!DOCTYPE suppressions PUBLIC
+    "-//Puppy Crawl//DTD Suppressions 1.1//EN"
+    "http://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
+
+<!--
+    Checkstyle用警告抑止設定
+    ※ SuppressionFilter モジュール用
+
+    [ http://checkstyle.sourceforge.net/ ]
+
+    Copyright(c) 2010 olyutorskii
+-->
+
+
+<suppressions>
+
+<!--
+    <suppress files="" checks="ExecutableStatementCount" />
+
+    <suppress files="" checks="ClassDataAbstractionCoupling" />
+    <suppress files="" checks="ClassFanOutComplexity" />
+    <suppress files="" checks="CyclomaticComplexity" />
+    <suppress files="" checks="NPathComplexity" />
+    <suppress files="" checks="JavaNCSS" />
+-->
+
+<!--
+    <suppress files="" checks="MagicNumber" />
+    <suppress files="" checks="MultipleStringLiterals" />
+-->
+
+</suppressions>
+
+
+<!-- EOF -->
diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/model/BezierParam.java b/src/main/java/jp/sourceforge/mikutoga/vmd/model/BezierParam.java
new file mode 100644 (file)
index 0000000..f90e88a
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * 3D bezier intaerpolation curve params
+ *
+ * License : The MIT License
+ * Copyright(c) 2011 MikuToga Partners
+ */
+
+package jp.sourceforge.mikutoga.vmd.model;
+
+/**
+ * 三次ベジェ曲線による補間カーブを記述する。
+ * <p>制御点P0,P1,P2,P3の座標により記述される。
+ * <p>P0は(0,0)で固定。P3は(127,127)で固定。
+ * 残りのP1,P2は、P0,P3を対角線とする正方形の内部に位置しなければならない。
+ */
+public class BezierParam {
+
+    /**
+     * 制御点P1のXデフォルト値(直線補間)。
+     */
+    public static final byte DEF_P1X = 20;
+    /**
+     * 制御点P1のYデフォルト値(直線補間)。
+     */
+    public static final byte DEF_P1Y = 20;
+    /**
+     * 制御点P2のXデフォルト値(直線補間)。
+     */
+    public static final byte DEF_P2X = 107;
+    /**
+     * 制御点P2のYデフォルト値(直線補間)。
+     */
+    public static final byte DEF_P2Y = 107;
+
+    /**
+     * 制御点P1のXデフォルト値(EaseInOut)。
+     */
+    public static final byte EIO_P1X = 64;
+    /**
+     * 制御点P1のYデフォルト値(EaseInOut)。
+     */
+    public static final byte EIO_P1Y =  0;
+    /**
+     * 制御点P2のXデフォルト値(EaseInOut)。
+     */
+    public static final byte EIO_P2X = 64;
+    /**
+     * 制御点P2のYデフォルト値(EaseInOut)。
+     */
+    public static final byte EIO_P2Y = 127;
+
+    /**
+     * 制御点空間最小値。
+     */
+    public static final byte MIN_VAL = 0;
+
+    /**
+     * 制御点空間最大値。
+     */
+    public static final byte MAX_VAL = 127;
+
+
+    private byte p1x = DEF_P1X;
+    private byte p1y = DEF_P1Y;
+    private byte p2x = DEF_P2X;
+    private byte p2y = DEF_P2Y;
+
+
+    /**
+     * コンストラクタ。
+     * <p>デフォルトの直線補間が設定される。
+     */
+    public BezierParam(){
+        super();
+        return;
+    }
+
+    /**
+     * 制御点P1のX座標を返す。
+     * @return 制御点P1のX座標
+     */
+    public byte getP1x() {
+        return this.p1x;
+    }
+
+    /**
+     * 制御点P1のY座標を返す。
+     * @return 制御点P1のY座標
+     */
+    public byte getP1y() {
+        return this.p1y;
+    }
+
+    /**
+     * 制御点P2のX座標を返す。
+     * @return 制御点P2のX座標
+     */
+    public byte getP2x() {
+        return this.p2x;
+    }
+
+    /**
+     * 制御点P2のY座標を返す。
+     * @return 制御点P2のY座標
+     */
+    public byte getP2y() {
+        return this.p2y;
+    }
+
+    /**
+     * 制御点P1のX座標を設定する。
+     * @param p1x 制御点P1のX座標
+     */
+    public void setP1x(byte p1x) {
+        this.p1x = p1x;
+        return;
+    }
+
+    /**
+     * 制御点P1のY座標を設定する。
+     * @param p1y 制御点P1のY座標
+     */
+    public void setP1y(byte p1y) {
+        this.p1y = p1y;
+        return;
+    }
+
+    /**
+     * 制御点P2のX座標を設定する。
+     * @param p2x 制御点P2のX座標
+     */
+    public void setP2x(byte p2x) {
+        this.p2x = p2x;
+        return;
+    }
+
+    /**
+     * 制御点P2のY座標を設定する。
+     * @param p2y 制御点P2のY座標
+     */
+    public void setP2y(byte p2y) {
+        this.p2y = p2y;
+        return;
+    }
+
+    /**
+     * 制御点P1の座標を設定する。
+     * @param p1xArg 制御点P1のX座標
+     * @param p1yArg 制御点P1のY座標
+     */
+    public void setP1(byte p1xArg, byte p1yArg) {
+        this.p1x = p1xArg;
+        this.p1y = p1yArg;
+        return;
+    }
+
+    /**
+     * 制御点P2の座標を設定する。
+     * @param p2xArg 制御点P2のX座標
+     * @param p2yArg 制御点P2のY座標
+     */
+    public void setP2(byte p2xArg, byte p2yArg) {
+        this.p2x = p2xArg;
+        this.p2y = p2yArg;
+        return;
+    }
+
+    /**
+     * 直線補間か判定する。
+     * <p>P1,P2双方がP0-P3対角線上に存在する場合を直線補間とする。
+     * @return 直線補間ならtrue
+     */
+    public boolean isLinear(){
+        if(this.p1x != this.p1y) return false;
+        if(this.p2x != this.p2y) return false;
+        return true;
+    }
+
+    /**
+     * MMDデフォルトの直線補間か判定する。
+     * @return MMDデフォルトの直線補間ならtrue
+     */
+    public boolean isDefaultLinear(){
+        if(this.p1x != DEF_P1X) return false;
+        if(this.p1y != DEF_P1Y) return false;
+
+        if(this.p2x != DEF_P2X) return false;
+        if(this.p2y != DEF_P2Y) return false;
+
+        assert isLinear();
+
+        return true;
+    }
+
+    /**
+     * MMDデフォルトのEaseInOutカーブか判定する。
+     * @return  MMDデフォルトのEaseInOutカーブならtrue
+     */
+    public boolean isDefaultEaseInOut(){
+        if(this.p1x != EIO_P1X) return false;
+        if(this.p1y != EIO_P1Y) return false;
+
+        if(this.p2x != EIO_P2X) return false;
+        if(this.p2y != EIO_P2Y) return false;
+
+        return true;
+    }
+
+    /**
+     * MMDデフォルトの直線補間に設定する。
+     */
+    public void setDefaultLinear(){
+        this.p1x = DEF_P1X;
+        this.p1y = DEF_P1Y;
+        this.p2x = DEF_P2X;
+        this.p2y = DEF_P2Y;
+        return;
+    }
+
+    /**
+     * MMDデフォルトのEaseInOutカーブに設定する。
+     */
+    public void setDefaultEaseInOut(){
+        this.p1x = EIO_P1X;
+        this.p1y = EIO_P1Y;
+        this.p2x = EIO_P2X;
+        this.p2y = EIO_P2Y;
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @return {@inheritDoc}
+     */
+    @Override
+    public String toString(){
+        StringBuilder result = new StringBuilder();
+
+        String delim = ", ";
+        result.append("P1=(")
+                .append(this.p1x).append(delim).append(this.p1y).append(") ");
+        result.append("P2=(")
+                .append(this.p2x).append(delim).append(this.p2y).append(")");
+
+        return result.toString();
+    }
+
+}
diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/model/BoneMotion.java b/src/main/java/jp/sourceforge/mikutoga/vmd/model/BoneMotion.java
new file mode 100644 (file)
index 0000000..42e564b
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * bone motion
+ *
+ * License : The MIT License
+ * Copyright(c) 2011 MikuToga Partners
+ */
+
+package jp.sourceforge.mikutoga.vmd.model;
+
+import jp.sourceforge.mikutoga.math.MkPos3D;
+import jp.sourceforge.mikutoga.math.MkQuat;
+import jp.sourceforge.mikutoga.vmd.AbstractNumbered;
+
+/**
+ * ボーンのモーション情報。
+ * <p>ボーン名、ボーン位置、ボーン回転量、及び補間カーブ情報を持つ。
+ */
+public class BoneMotion extends AbstractNumbered {
+
+    private String boneName;
+
+    private final MkQuat rotation = new MkQuat();
+    private final BezierParam intpltRotation = new BezierParam();
+
+    private final MkPos3D position = new MkPos3D();
+    private final PosCurve posCurve = new PosCurve();
+
+
+    /**
+     * コンストラクタ。
+     */
+    public BoneMotion(){
+        super();
+        return;
+    }
+
+
+    /**
+     * ボーン名を返す。
+     * @return ボーン名
+     */
+    public String getBoneName(){
+        return this.boneName;
+    }
+
+    /**
+     * ボーン名を設定する。
+     * @param boneName ボーン名
+     */
+    public void setBoneName(String boneName){
+        this.boneName = boneName;
+        return;
+    }
+
+    /**
+     * ボーン回転量を返す。
+     * @return ボーン回転量
+     */
+    public MkQuat getRotation(){
+        return this.rotation;
+    }
+
+    /**
+     * ボーン回転の補間曲線情報を返す。
+     * @return ボーン回転の補間曲線情報
+     */
+    public BezierParam getIntpltRotation(){
+        return this.intpltRotation;
+    }
+
+    /**
+     * ボーン位置を返す。
+     * @return ボーン位置
+     */
+    public MkPos3D getPosition(){
+        return this.position;
+    }
+
+    /**
+     * ボーン位置移動の補間情報を返す。
+     * @return ボーン位置移動の補間情報
+     */
+    public PosCurve getPosCurve(){
+        return this.posCurve;
+    }
+
+    /**
+     * このモーションが暗黙の位置情報を持つか判定する。
+     * <p>ボーン位置が原点(0,0,0)にあり、
+     * XYZ3軸の移動補間カーブがデフォルト直線補間の場合、
+     * 暗黙の位置情報と見なされる。
+     * <p>MMDは、位置情報を持たないボーンのモーションに対し
+     * 便宜的にこの暗黙の位置情報を割り当てる。
+     * <p>通常の位置モーションが暗黙の位置情報と一致する場合もありうる。
+     * @return 暗黙の位置情報であるならtrue
+     */
+    public boolean hasImplicitPosition(){
+        if(this.position.isOriginPoint() && this.posCurve.isDefaultLinear()){
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @return {@inheritDoc}
+     */
+    @Override
+    public String toString(){
+        StringBuilder result = new StringBuilder();
+
+        result.append("bone name : [").append(this.boneName);
+        result.append("] #").append(getFrameNumber()).append('\n');
+
+        result.append("rotation ").append(this.rotation);
+        result.append(" R-Bezier ").append(this.intpltRotation).append('\n');
+
+        result.append("position ").append(this.position).append('\n');
+        result.append(this.posCurve);
+
+        return result.toString();
+    }
+
+}
diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/model/CameraMotion.java b/src/main/java/jp/sourceforge/mikutoga/vmd/model/CameraMotion.java
new file mode 100644 (file)
index 0000000..d418ca1
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * camera motion
+ *
+ * License : The MIT License
+ * Copyright(c) 2011 MikuToga Partners
+ */
+
+package jp.sourceforge.mikutoga.vmd.model;
+
+import jp.sourceforge.mikutoga.math.MkPos3D;
+import jp.sourceforge.mikutoga.vmd.AbstractNumbered;
+
+/**
+ * カメラモーション情報。
+ * <p>カメラの回転は極座標で表される。
+ * <p>カメラ-ターゲット間の距離は球座標(極座標)の動径に相当する。
+ * 通常はターゲットより手前に位置するカメラまでの距離が負の値で表される。
+ * カメラ位置がターゲットを突き抜けた場合は正の値もとりうる。
+ * ※MMDのUIと符号が逆なので注意。
+ * <p>パースペクティブモードがOFFの場合、
+ * 画角は無視され遠近感処理が行われなくなる。(平行投影?)
+ */
+public class CameraMotion extends AbstractNumbered {
+
+    private final MkPos3D cameraTarget = new MkPos3D();
+    private final PosCurve posCurve = new PosCurve();
+
+    private final CameraRotation cameraRotation = new CameraRotation();
+    private final BezierParam intpltRotation = new BezierParam();
+
+    private float range;
+    private final BezierParam intpltRange = new BezierParam();
+
+    private boolean hasPerspective;
+    private int projectionAngle;
+    private final BezierParam intpltProjection = new BezierParam();
+
+
+    /**
+     * コンストラクタ。
+     */
+    public CameraMotion(){
+        super();
+        return;
+    }
+
+
+    /**
+     * ターゲット位置情報を返す。
+     * @return ターゲット位置情報
+     */
+    public MkPos3D getCameraTarget(){
+        return this.cameraTarget;
+    }
+
+    /**
+     * ターゲット位置移動の補間情報を返す。
+     * @return ターゲット位置移動の補間情報
+     */
+    public PosCurve getTargetPosCurve(){
+        return this.posCurve;
+    }
+
+    /**
+     * カメラ回転情報を返す。
+     * @return カメラ回転情報
+     */
+    public CameraRotation getCameraRotation(){
+        return this.cameraRotation;
+    }
+
+    /**
+     * カメラ回転の補間曲線情報を返す。
+     * @return カメラ回転の補間曲線情報
+     */
+    public BezierParam getIntpltRotation(){
+        return this.intpltRotation;
+    }
+
+    /**
+     * カメラ-ターゲット間の距離を返す。
+     * @return カメラ-ターゲット間の距離
+     */
+    public float getRange(){
+        return this.range;
+    }
+
+    /**
+     * カメラ-ターゲット間の距離を設定する。
+     * @param range カメラ-ターゲット間の距離
+     */
+    public void setRange(float range){
+        this.range = range;
+        return;
+    }
+
+    /**
+     * カメラ-ターゲット間距離の補間曲線情報を返す。
+     * @return カメラ-ターゲット間距離の補間曲線情報
+     */
+    public BezierParam getIntpltRange(){
+        return this.intpltRange;
+    }
+
+    /**
+     * パースペクティブが有効か判定する。
+     * @return パースペクティブが有効ならtrue
+     */
+    public boolean hasPerspective(){
+        return this.hasPerspective;
+    }
+
+    /**
+     * パースペクティブモードを設定する。
+     * @param mode trueを渡すとパースペクティブが有効になる。
+     */
+    public void setPerspectiveMode(boolean mode){
+        this.hasPerspective = mode;
+        return;
+    }
+
+    /**
+     * 投影角度(スクリーン縦画角)を返す。
+     * @return 投影角度(度数法)
+     */
+    public int getProjectionAngle(){
+        return this.projectionAngle;
+    }
+
+    /**
+     * 投影角度(スクリーン縦画角)を設定する。
+     * @param angle 投影角度(度数法)
+     */
+    public void setProjectionAngle(int angle){
+        this.projectionAngle = angle;
+        return;
+    }
+
+    /**
+     * スクリーン投射の補間曲線情報を返す。
+     * @return スクリーン投射の補間曲線情報
+     */
+    public BezierParam getIntpltProjection(){
+        return this.intpltProjection;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @return {@inheritDoc}
+     */
+    @Override
+    public String toString(){
+        StringBuilder result = new StringBuilder();
+
+        result.append("#").append(getFrameNumber()).append(' ');
+        result.append(this.cameraRotation);
+        result.append(" Rot-Bezier ")
+                .append(this.intpltRotation).append('\n');
+
+        result.append("range : ").append(this.range);
+        result.append(" Range-Bezier ").append(this.intpltRange).append('\n');
+
+        result.append("target-pos : ").append(this.cameraTarget).append('\n');
+        result.append(this.posCurve).append('\n');
+
+        result.append("perspective : ");
+        if(this.hasPerspective) result.append("ON");
+        else                    result.append("OFF");
+        result.append('\n');
+
+        result.append("projection angle : ").append(this.projectionAngle);
+        result.append("deg Bezier ").append(this.intpltProjection);
+
+        return result.toString();
+    }
+
+}
diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/model/CameraRotation.java b/src/main/java/jp/sourceforge/mikutoga/vmd/model/CameraRotation.java
new file mode 100644 (file)
index 0000000..958e87b
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * VMD camerawork handler
+ *
+ * License : The MIT License
+ * Copyright(c) 2011 MikuToga Partners
+ */
+
+package jp.sourceforge.mikutoga.vmd.model;
+
+/**
+ * カメラの回転情報。
+ * <p>latitudeはターゲットから見たカメラの仰俯角(≒緯度)。
+ * 単位はラジアン。
+ * Y軸回転量が0の時のZ正軸がY正軸へ倒れる方向が正回転。
+ * (MMDのUIとは符号が逆になるので注意)
+ * 仰俯角が0の場合、
+ * カメラはターゲットに対しXZ平面(水平)と平行な箇所に位置する。
+ * <p>longitudeはY軸周りの回転量(≒経度)。単位はラジアン。
+ * X正軸がZ正軸へ倒れる方向が正回転。(ボーン回転と逆)
+ * 仰俯角およびY軸回転量が0の場合、
+ * カメラレンズはZ軸-∞方向からZ軸+∞方向を向く。
+ * <p>rollはレンズをターゲットを向けたカメラのロール回転量。
+ * 仰俯角とY軸回転量が0の時にY正軸がX正軸に倒れる方向が正回転。
+ * 仰俯角およびロール回転量が0の場合、カメラ上部はY軸+∞の方を向く。
+ */
+public class CameraRotation {
+
+    private float latitude;
+    private float longitude;
+    private float roll;
+
+
+    /**
+     * コンストラクタ。
+     */
+    public CameraRotation(){
+        super();
+        return;
+    }
+
+
+    /**
+     * ターゲットから見たカメラの仰俯角(≒緯度)を返す。
+     * @return ターゲットから見たカメラの仰俯角(≒緯度)
+     */
+    public float getLatitude(){
+        return this.latitude;
+    }
+
+    /**
+     * ターゲットから見たカメラの仰俯角(≒緯度)を設定する。
+     * @param latitude ターゲットから見たカメラの仰俯角(≒緯度)
+     */
+    public void setLatitude(float latitude){
+        this.latitude = latitude;
+        return;
+    }
+
+    /**
+     * Y軸周りの回転量(≒経度)を返す。
+     * @return Y軸周りの回転量(≒経度)
+     */
+    public float getLongitude(){
+        return this.longitude;
+    }
+
+    /**
+     * Y軸周りの回転量(≒経度)を設定する。
+     * @param longitude Y軸周りの回転量(≒経度)
+     */
+    public void setLongitude(float longitude){
+        this.longitude = longitude;
+        return;
+    }
+
+    /**
+     * レンズをターゲットを向けたカメラのロール回転量を返す。
+     * @return レンズをターゲットを向けたカメラのロール回転量
+     */
+    public float getRoll(){
+        return this.roll;
+    }
+
+    /**
+     * レンズをターゲットを向けたカメラのロール回転量を設定する。
+     * @param roll レンズをターゲットを向けたカメラのロール回転量
+     */
+    public void setRoll(float roll){
+        this.roll = roll;
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @return {@inheritDoc}
+     */
+    @Override
+    public String toString(){
+        StringBuilder result = new StringBuilder();
+
+        result.append("latitude=").append(this.latitude);
+        result.append(" longitude=").append(this.longitude);
+        result.append(" roll=").append(this.roll);
+
+        return result.toString();
+    }
+
+}
diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/model/LuminousColor.java b/src/main/java/jp/sourceforge/mikutoga/vmd/model/LuminousColor.java
new file mode 100644 (file)
index 0000000..b28b7c6
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * luminous color
+ *
+ * License : The MIT License
+ * Copyright(c) 2011 MikuToga Partners
+ */
+
+package jp.sourceforge.mikutoga.vmd.model;
+
+/**
+ * 光源の色設定。
+ * <p>色情報はRGB色空間で記述される。
+ * <p>MMDのUI上の各色成分指定0~255定義域に便宜上256を追加したものが、
+ * 0.0以上1.0以下にマップされる。
+ * <ul>
+ * <li>0は正しく0.0にマップされる。
+ * <li>128は正しく0.5にマップされる。
+ * <li>255は1.0より少しだけ小さい数(≒0.99609375)にマップされる。
+ * </ul>
+ */
+public class LuminousColor {
+
+    /** デフォルトの成分値。 */
+    public static final float DEF_BRIGHT = 0.602f; // ≒ (154.0 / 256.0)
+
+
+    private float colR = DEF_BRIGHT;
+    private float colG = DEF_BRIGHT;
+    private float colB = DEF_BRIGHT;
+
+
+    /**
+     * コンストラクタ。
+     * <p>MMDデフォルトの光源色(154,154,154)が設定される。
+     */
+    public LuminousColor(){
+        super();
+        return;
+    }
+
+
+    /**
+     * 光源の赤成分を設定する。
+     * @param colR 赤成分
+     */
+    public void setColR(float colR) {
+        this.colR = colR;
+        return;
+    }
+
+    /**
+     * 光源の緑成分を設定する。
+     * @param colG 緑成分
+     */
+    public void setColG(float colG) {
+        this.colG = colG;
+        return;
+    }
+
+    /**
+     * 光源の青成分を設定する。
+     * @param colB 青成分
+     */
+    public void setColB(float colB) {
+        this.colB = colB;
+        return;
+    }
+
+    /**
+     * 光源の赤成分を返す。
+     * @return 赤成分
+     */
+    public float getColR(){
+        return this.colR;
+    }
+
+    /**
+     * 光源の緑成分を返す。
+     * @return 緑成分
+     */
+    public float getColG(){
+        return this.colG;
+    }
+
+    /**
+     * 光源の青成分を返す。
+     * @return 青成分
+     */
+    public float getColB(){
+        return this.colB;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @return {@inheritDoc}
+     */
+    @Override
+    public String toString(){
+        StringBuilder result = new StringBuilder();
+
+        result.append("r=").append(this.colR);
+        result.append(" g=").append(this.colG);
+        result.append(" b=").append(this.colB);
+
+        return result.toString();
+    }
+
+}
diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/model/LuminousMotion.java b/src/main/java/jp/sourceforge/mikutoga/vmd/model/LuminousMotion.java
new file mode 100644 (file)
index 0000000..b2eb05c
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * luminous motion
+ *
+ * License : The MIT License
+ * Copyright(c) 2011 MikuToga Partners
+ */
+
+package jp.sourceforge.mikutoga.vmd.model;
+
+import jp.sourceforge.mikutoga.vmd.AbstractNumbered;
+
+/**
+ * 光源演出情報。
+ */
+public class LuminousMotion extends AbstractNumbered {
+
+    private final LuminousColor color = new LuminousColor();
+    private final LuminousVector direction = new LuminousVector();
+
+
+    /**
+     * コンストラクタ。
+     */
+    public LuminousMotion(){
+        super();
+        return;
+    }
+
+
+    /**
+     * 光源の色情報を返す。
+     * @return 光源の色情報
+     */
+    public LuminousColor getColor(){
+        return this.color;
+    }
+
+    /**
+     * 光源からの照射方向情報を返す。
+     * @return 光源からの照射方向情報
+     */
+    public LuminousVector getDirection(){
+        return this.direction;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @return {@inheritDoc}
+     */
+    @Override
+    public String toString(){
+        StringBuilder result = new StringBuilder();
+
+        result.append("#").append(getFrameNumber());
+        result.append(" luminous color : ").append(this.color);
+        result.append(" direction : ").append(this.direction);
+
+        return result.toString();
+    }
+
+}
diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/model/LuminousVector.java b/src/main/java/jp/sourceforge/mikutoga/vmd/model/LuminousVector.java
new file mode 100644 (file)
index 0000000..78f2a7f
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * luminous direction vector
+ *
+ * License : The MIT License
+ * Copyright(c) 2011 MikuToga Partners
+ */
+
+package jp.sourceforge.mikutoga.vmd.model;
+
+/**
+ * 光源からの照射方向を表す方向ベクトル。
+ * <p>照明方向は、
+ * ワールド座標原点から伸びる方向ベクトルとして記述される。
+ * この方向ベクトルに向けて、無限遠の光源から照明が当たる。
+ * <p>MMDのスライダUI上では各軸成分の定義域は-1.0以上+1.0以下だが、
+ * さらに絶対値の大きな値を指定することも可能。
+ * <p>方向ベクトルの長さは演出上の意味を持たないが、
+ * キーフレーム間の照明方向の補間に影響を及ぼすかもしれない。
+ * <p>方向ベクトルが零ベクトル(0,0,0)の場合、MMDでは全ポリゴンに影が落ちる。
+ */
+public class LuminousVector {
+
+    /** デフォルトのX成分。 */
+    public static final float DEF_VECX = -0.5f;
+    /** デフォルトのY成分。 */
+    public static final float DEF_VECY = -1.0f;
+    /** デフォルトのZ成分。 */
+    public static final float DEF_VECZ = +0.5f;
+
+
+    private float vecX = DEF_VECX;
+    private float vecY = DEF_VECY;
+    private float vecZ = DEF_VECZ;
+
+
+    /**
+     * コンストラクタ。
+     */
+    public LuminousVector(){
+        super();
+        return;
+    }
+
+
+    /**
+     * 照射方向ベクトルのX成分を設定する。
+     * @param vecX ベクトルのX成分
+     */
+    public void setVecX(float vecX) {
+        this.vecX = vecX;
+        return;
+    }
+
+    /**
+     * 照射方向ベクトルのY成分を設定する。
+     * @param vecY ベクトルのY成分
+     */
+    public void setVecY(float vecY) {
+        this.vecY = vecY;
+        return;
+    }
+
+    /**
+     * 照射方向ベクトルのZ成分を設定する。
+     * @param vecZ ベクトルのZ成分
+     */
+    public void setVecZ(float vecZ) {
+        this.vecZ = vecZ;
+        return;
+    }
+
+    /**
+     * 照射方向ベクトルのX成分を返す。
+     * @return 方向ベクトルX成分
+     */
+    public float getVecX() {
+        return this.vecX;
+    }
+
+    /**
+     * 照射方向ベクトルのY成分を返す。
+     * @return 方向ベクトルY成分
+     */
+    public float getVecY() {
+        return this.vecY;
+    }
+
+    /**
+     * 照射方向ベクトルのZ成分を返す。
+     * @return 方向ベクトルZ成分
+     */
+    public float getVecZ() {
+        return this.vecZ;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @return {@inheritDoc}
+     */
+    @Override
+    public String toString(){
+        StringBuilder result = new StringBuilder();
+
+        result.append("x=").append(this.vecX);
+        result.append(" y=").append(this.vecY);
+        result.append(" z=").append(this.vecZ);
+
+        return result.toString();
+    }
+
+}
diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/model/MorphMotion.java b/src/main/java/jp/sourceforge/mikutoga/vmd/model/MorphMotion.java
new file mode 100644 (file)
index 0000000..fe77f0d
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * morph motion
+ *
+ * License : The MIT License
+ * Copyright(c) 2011 MikuToga Partners
+ */
+
+package jp.sourceforge.mikutoga.vmd.model;
+
+import jp.sourceforge.mikutoga.vmd.AbstractNumbered;
+
+/**
+ * モーフ情報。
+ * <p>通常、モーフ量は0.0以上1.0以下の値をとる。
+ */
+public class MorphMotion extends AbstractNumbered {
+
+    private String morphName = "";
+    private float flex = 0.0f;
+
+
+    /**
+     * コンストラクタ。
+     */
+    public MorphMotion(){
+        super();
+        return;
+    }
+
+
+    /**
+     * モーフ名を設定する。
+     * @param morphName モーフ名
+     */
+    public void setMorphName(String morphName) {
+        this.morphName = morphName;
+        return;
+    }
+
+    /**
+     * モーフ名を返す。
+     * @return モーフ名
+     */
+    public String getMorphName() {
+        return this.morphName;
+    }
+
+    /**
+     * モーフ変量を設定する。
+     * @param flex 変量
+     */
+    public void setFlex(float flex) {
+        this.flex = flex;
+        return;
+    }
+
+    /**
+     * モーフ変量を返す。
+     * @return 変量
+     */
+    public float getFlex() {
+        return this.flex;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @return {@inheritDoc}
+     */
+    @Override
+    public String toString(){
+        StringBuilder result = new StringBuilder();
+
+        result.append("morph name : [").append(this.morphName);
+        result.append("] #").append(getFrameNumber());
+        result.append(" flex = ").append(this.flex);
+
+        return result.toString();
+    }
+
+}
diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/model/NamedListMap.java b/src/main/java/jp/sourceforge/mikutoga/vmd/model/NamedListMap.java
new file mode 100644 (file)
index 0000000..01aedfc
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * string named list map
+ *
+ * License : The MIT License
+ * Copyright(c) 2011 MikuToga Partners
+ */
+
+package jp.sourceforge.mikutoga.vmd.model;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 名前付けされたリストのマップ。
+ * 登録名の追加順が保持される。
+ * @param <E> リスト要素の型
+ */
+public class NamedListMap<E> {
+
+    private final List<String> nameList;
+    private final Map<String, List<E>> listMap;
+
+
+    /**
+     * コンストラクタ。
+     */
+    public NamedListMap(){
+        super();
+        this.nameList = new LinkedList<String>();
+        this.listMap = new HashMap<String, List<E>>();
+        return;
+    }
+
+
+    /**
+     * マップをクリアする。
+     */
+    public void clear(){
+        this.nameList.clear();
+        this.listMap.clear();
+        return;
+    }
+
+    /**
+     * マップが空か否か判定する。
+     * @return 空ならtrue
+     */
+    public boolean isEmpty(){
+        if(this.listMap.isEmpty()) return true;
+        return false;
+    }
+
+    /**
+     * 名前一覧を返す。
+     * <p>名前は登録順に並ぶ。
+     * <p>ここで返されるListへの修正操作は不可能。
+     * @return 名前一覧のリスト
+     */
+    public List<String> getNames(){
+        List<String> result = Collections.unmodifiableList(this.nameList);
+        return result;
+    }
+
+    /**
+     * 名前付けされたリストを返す。
+     * @param name 名前
+     * @return 名前付けされたリスト。リストが存在しなければnull。
+     */
+    public List<E> getNamedList(String name){
+        List<E> result = this.listMap.get(name);
+        return result;
+    }
+
+    /**
+     * 名前付けされたリストを削除する。
+     * 存在しない名前が渡された場合、何もしない。
+     * @param name 名前
+     */
+    public void removeNamedList(String name){
+        if(this.listMap.remove(name) == null) return;
+        this.nameList.remove(name);
+        return;
+    }
+
+    /**
+     * 名前付けされたリストに新要素を追加する。
+     * 未登録の名前であれば新たにリストが作成される。
+     * @param name 名前
+     * @param elem 新要素
+     * @throws NullPointerException 引数がnull
+     */
+    public void addNamedElement(String name, E elem)
+            throws NullPointerException{
+        if(name == null || elem == null) throw new NullPointerException();
+
+        List<E> list = this.listMap.get(name);
+        if(list == null){
+            list = new LinkedList<E>();
+            this.listMap.put(name, list);
+            this.nameList.add(name);
+        }
+
+        list.add(elem);
+
+        return;
+    }
+
+}
diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/model/PosCurve.java b/src/main/java/jp/sourceforge/mikutoga/vmd/model/PosCurve.java
new file mode 100644 (file)
index 0000000..6cf489c
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * position curve
+ *
+ * License : The MIT License
+ * Copyright(c) 2011 MikuToga Partners
+ */
+
+package jp.sourceforge.mikutoga.vmd.model;
+
+/**
+ * 3次元位置移動の補間情報。
+ * <p>XYZ3軸それぞれに対応するベジェ曲線を3本持つ。
+ */
+public class PosCurve {
+
+    private final BezierParam intpltXpos = new BezierParam();
+    private final BezierParam intpltYpos = new BezierParam();
+    private final BezierParam intpltZpos = new BezierParam();
+
+
+    /**
+     * コンストラクタ。
+     */
+    public PosCurve(){
+        super();
+        return;
+    }
+
+
+    /**
+     * ボーンX軸移動の補間曲線情報を返す。
+     * @return ボーンX軸移動の補間曲線情報
+     */
+    public BezierParam getIntpltXpos(){
+        return this.intpltXpos;
+    }
+
+    /**
+     * ボーンY軸移動の補間曲線情報を返す。
+     * @return ボーンY軸移動の補間曲線情報
+     */
+    public BezierParam getIntpltYpos(){
+        return this.intpltYpos;
+    }
+
+    /**
+     * ボーンZ軸移動の補間曲線情報を返す。
+     * @return ボーンZ軸移動の補間曲線情報
+     */
+    public BezierParam getIntpltZpos(){
+        return this.intpltZpos;
+    }
+
+    /**
+     * インデックス指定された各軸の補間曲線情報を返す。
+     * <p>インデックス値0がX軸、1がY軸、2がZ軸に対応する。
+     * @param no インデックス値[0-2]
+     * @return 各軸の補間曲線情報
+     * @throws IllegalArgumentException インデックス値が範囲外
+     */
+    public BezierParam item(int no) throws IllegalArgumentException {
+        BezierParam result;
+        switch(no){
+        case 0: result = this.intpltXpos; break;
+        case 1: result = this.intpltYpos; break;
+        case 2: result = this.intpltZpos; break;
+        default:
+            throw new IllegalArgumentException();
+        }
+        return result;
+    }
+
+    /**
+     * 3軸ともMMDデフォルトの直線補間か判定する。
+     * @return 3軸ともMMDデフォルトの直線補間ならtrue
+     */
+    public boolean isDefaultLinear(){
+        if( ! this.intpltXpos.isDefaultLinear() ) return false;
+        if( ! this.intpltYpos.isDefaultLinear() ) return false;
+        if( ! this.intpltZpos.isDefaultLinear() ) return false;
+
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @return {@inheritDoc}
+     */
+    @Override
+    public String toString(){
+        StringBuilder result = new StringBuilder();
+
+        result.append("X-Bezier ").append(this.intpltXpos).append('\n');
+        result.append("Y-Bezier ").append(this.intpltYpos).append('\n');
+        result.append("Z-Bezier ").append(this.intpltZpos);
+
+        return result.toString();
+    }
+
+}
diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/model/ShadowMode.java b/src/main/java/jp/sourceforge/mikutoga/vmd/model/ShadowMode.java
new file mode 100644 (file)
index 0000000..2cac757
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * shadow mode
+ *
+ * License : The MIT License
+ * Copyright(c) 2011 MikuToga Partners
+ */
+
+package jp.sourceforge.mikutoga.vmd.model;
+
+/**
+ * セルフシャドウ描画モード。
+ */
+public enum ShadowMode {
+
+    /**
+     * シャドウを描画しないモード。
+     */
+    NONE(0x00),
+
+    /**
+     * シャドウ描画の質がカメラからの距離の影響をあまり受けないモード。
+     * <p>※MMDのUIにおけるデフォルト。
+     */
+    MODE_1(0x01),
+
+    /**
+     * シャドウ描画の質をカメラからの距離に応じて劣化させることにより、
+     * カメラに近いオブジェクトのシャドウ描画の質を向上させるモード。
+     */
+    MODE_2(0x02),
+
+    ;
+
+
+    private static final int MASK_8 = 0xff;
+
+
+    private final byte code;
+
+
+    /**
+     * コンストラクタ。
+     * @param code 割り当て番号(byte)
+     */
+    private ShadowMode(byte code){
+        this.code = code;
+        return;
+    }
+
+    /**
+     * コンストラクタ。
+     * @param iVal 割り当て番号(int)
+     * ※上位24bitは捨てられる。
+     */
+    private ShadowMode(int iVal){
+        this((byte)(iVal & MASK_8));
+        return;
+    }
+
+
+    /**
+     * byte型値に符号化された時の値を返す。
+     * @return byte型値に符号化された時の値
+     */
+    public byte getEncodedByte(){
+        return this.code;
+    }
+
+    /**
+     * byte型値から描画モードを復号する。
+     * @param bVal byte型値
+     * @return 描画モード。未定義の値が与えられた場合はnull。
+     */
+    public static ShadowMode decode(byte bVal){
+        ShadowMode result = null;
+
+        if     (bVal == NONE  .code) result = NONE;
+        else if(bVal == MODE_1.code) result = MODE_1;
+        else if(bVal == MODE_2.code) result = MODE_2;
+
+        return result;
+    }
+
+    /**
+     * int型値から描画モードを復号する。
+     * <p>上位24bitは無視される。
+     * @param iVal int型値
+     * @return 描画モード。未定義の値が与えられた場合はnull。
+     */
+    public static ShadowMode decode(int iVal){
+        return decode((byte)(iVal & MASK_8));
+    }
+
+}
diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/model/ShadowMotion.java b/src/main/java/jp/sourceforge/mikutoga/vmd/model/ShadowMotion.java
new file mode 100644 (file)
index 0000000..85c502f
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * shadow motion
+ *
+ * License : The MIT License
+ * Copyright(c) 2011 MikuToga Partners
+ */
+
+package jp.sourceforge.mikutoga.vmd.model;
+
+import jp.sourceforge.mikutoga.vmd.AbstractNumbered;
+
+/**
+ * 影(セルフシャドウ)演出情報。
+ * <p>カメラからの距離情報(幾何距離の100倍?)による影演出対象の範囲指定は、
+ * MMDのスライダUI上では0から9999までが指定可能。
+ * <p>MMDのスライダUI値SからVMDファイル上の生パラメターへの変換式は、
+ * 「 0.1 - (S / 1.0E+5) 」
+ * となる。
+ */
+public class ShadowMotion extends AbstractNumbered {
+
+    /**
+     * デフォルトの影描画モード。
+     */
+    public static final ShadowMode DEF_MODE = ShadowMode.MODE_1;
+
+    /**
+     * デフォルトの範囲指定生パラメータ。
+     * <p>MMDのスライダUI値「8875」にほぼ相当。
+     */
+    public static final float DEF_SCOPE = 0.01125f;
+
+    private static final double OFFSET = 0.1;
+    private static final double SCALE = 1.0E+5;
+
+
+    private ShadowMode shadowMode = DEF_MODE;
+    private float rawScopeParam = DEF_SCOPE;
+
+
+    /**
+     * コンストラクタ。
+     */
+    public ShadowMotion(){
+        super();
+        return;
+    }
+
+
+    /**
+     * VMDファイル上の生パラメータ数値による演出対象範囲指定を、
+     * MMDのUI上の距離情報(カメラからの幾何距離×100倍?)に変換する。
+     * @param param 生パラメータ
+     * @return MMDのスライダUI上の距離情報
+     */
+    public static double rawParamToScope(float param){
+        double result;
+        result = OFFSET - param;
+        result *= SCALE;
+        return result;
+    }
+
+    /**
+     * MMDのUI上の距離情報(カメラからの幾何距離×100倍?)を、
+     * VMDファイル上の生パラメータ数値に変換する。
+     * @param scope MMDのスライダUI上の距離情報
+     * @return 生パラメータ
+     */
+    public static float scopeToRawParam(double scope){
+        double result;
+        result = scope / SCALE;
+        result = OFFSET - result;
+        return (float) result;
+    }
+
+
+    /**
+     * 影演出の範囲指定の生パラメータを設定する。
+     * @param rawScopeParam 生パラメータ
+     */
+    public void setRawScopeParam(float rawScopeParam) {
+        this.rawScopeParam = rawScopeParam;
+        return;
+    }
+
+    /**
+     * 影演出の範囲指定の生パラメータを返す。
+     * @return 生パラメータ
+     */
+    public float getRawScopeParam() {
+        return this.rawScopeParam;
+    }
+
+    /**
+     * 影演出の範囲指定のスライダUI値を設定する。
+     * @param scope スライダUI値
+     */
+    public void setScope(double scope){
+        float rawVal = scopeToRawParam(scope);
+        setRawScopeParam(rawVal);
+        return;
+    }
+
+    /**
+     * 影演出の範囲指定のスライダUI値を返す。
+     * @return スライダUI値
+     */
+    public double getScope(){
+        float rawVal = getRawScopeParam();
+        double scope = rawParamToScope(rawVal);
+        return scope;
+    }
+
+    /**
+     * 影描画モードを設定する。
+     * @param shadowMode 影描画モード
+     * @throws NullPointerException 引数がnull
+     */
+    public void setShadowMode(ShadowMode shadowMode)
+            throws NullPointerException{
+        if(shadowMode == null) throw new NullPointerException();
+        this.shadowMode = shadowMode;
+        return;
+    }
+
+    /**
+     * 影描画モードを返す。
+     * @return 影描画モード
+     */
+    public ShadowMode getShadowMode(){
+        return this.shadowMode;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @return {@inheritDoc}
+     */
+    @Override
+    public String toString(){
+        StringBuilder result = new StringBuilder();
+
+        result.append("#").append(getFrameNumber());
+        result.append(" shadow mode : ").append(this.shadowMode);
+        result.append(" rawparam=").append(this.rawScopeParam);
+
+        return result.toString();
+    }
+
+}
diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/model/VmdMotion.java b/src/main/java/jp/sourceforge/mikutoga/vmd/model/VmdMotion.java
new file mode 100644 (file)
index 0000000..36812ff
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * motion & stage act
+ *
+ * License : The MIT License
+ * Copyright(c) 2011 MikuToga Partners
+ */
+
+package jp.sourceforge.mikutoga.vmd.model;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import jp.sourceforge.mikutoga.vmd.FrameNumbered;
+import jp.sourceforge.mikutoga.vmd.VmdConst;
+
+/**
+ * モーション及び演出情報。
+ */
+public class VmdMotion {
+
+    private String modelName = VmdConst.MODELNAME_STAGEACT;
+
+    private final NamedListMap<BoneMotion> bonePartMap;
+    private final NamedListMap<MorphMotion> morphPartMap;
+
+    private final List<CameraMotion>   cameraMotionList;
+    private final List<LuminousMotion> luminousMotionList;
+    private final List<ShadowMotion>   shadowMotionList;
+
+
+    /**
+     * コンストラクタ。
+     */
+    public VmdMotion(){
+        super();
+
+        this.bonePartMap  = new NamedListMap<BoneMotion>();
+        this.morphPartMap = new NamedListMap<MorphMotion>();
+
+        this.cameraMotionList   = new LinkedList<CameraMotion>();
+        this.luminousMotionList = new LinkedList<LuminousMotion>();
+        this.shadowMotionList   = new LinkedList<ShadowMotion>();
+
+        return;
+    }
+
+
+    /**
+     * モデル名を返す。
+     * @return モデル名
+     */
+    public String getModelName(){
+        return this.modelName;
+    }
+
+    /**
+     * モデル名を設定する。
+     * <p>このモーションがモデルモーションかステージ演出情報かは、
+     * このモデル名で判別される。
+     * @param modelName モデル名
+     * @throws NullPointerException 引数がnull
+     */
+    public void setModelName(String modelName) throws NullPointerException{
+        if(modelName == null) throw new NullPointerException();
+        this.modelName = modelName;
+        return;
+    }
+
+    /**
+     * モデルモーションか否か判別する。
+     * <p>判別は特殊なモデル名を持つか否かで決定される。
+     * @return モデルモーションならtrue
+     */
+    public boolean isModelMotion(){
+        if(VmdConst.isStageActName(this.modelName)){
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * 名前付きボーンモーションマップを返す。
+     * @return 名前付きボーンモーションマップ
+     */
+    public NamedListMap<BoneMotion> getBonePartMap(){
+        return this.bonePartMap;
+    }
+
+    /**
+     * 名前付きモーフモーションマップを返す。
+     * @return 名前付きモーフモーションマップ
+     */
+    public NamedListMap<MorphMotion> getMorphPartMap(){
+        return this.morphPartMap;
+    }
+
+    /**
+     * カメラモーションのリストを返す。
+     * @return カメラモーションのリスト
+     */
+    public List<CameraMotion> getCameraMotionList(){
+        return this.cameraMotionList;
+    }
+
+    /**
+     * 照明モーションのリストを返す。
+     * @return 照明モーションのリスト
+     */
+    public List<LuminousMotion> getLuminousMotionList(){
+        return this.luminousMotionList;
+    }
+
+    /**
+     * シャドウモーションのリストを返す。
+     * @return シャドウモーションのリスト
+     */
+    public List<ShadowMotion> getShadowMotionList(){
+        return this.shadowMotionList;
+    }
+
+    /**
+     * ボーンモーションを追加する。
+     * @param motion ボーンモーション
+     */
+    public void addBoneMotion(BoneMotion motion){
+        String name = motion.getBoneName();
+        this.bonePartMap.addNamedElement(name, motion);
+        return;
+    }
+
+    /**
+     * モーフモーションを追加する。
+     * @param motion モーフモーション
+     */
+    public void addMorphMotion(MorphMotion motion){
+        String name = motion.getMorphName();
+        this.morphPartMap.addNamedElement(name, motion);
+        return;
+    }
+
+    /**
+     * 各データをフレーム番号順に昇順ソートする。
+     */
+    public void frameSort(){
+        for(String name : this.bonePartMap.getNames()){
+            List<BoneMotion> motionList =
+                    this.bonePartMap.getNamedList(name);
+            Collections.sort(motionList, FrameNumbered.COMPARATOR);
+        }
+
+        for(String name : this.morphPartMap.getNames()){
+            List<MorphMotion> motionList =
+                    this.morphPartMap.getNamedList(name);
+            Collections.sort(motionList, FrameNumbered.COMPARATOR);
+        }
+
+        Collections.sort(this.cameraMotionList,   FrameNumbered.COMPARATOR);
+        Collections.sort(this.luminousMotionList, FrameNumbered.COMPARATOR);
+        Collections.sort(this.shadowMotionList,   FrameNumbered.COMPARATOR);
+
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @return {@inheritDoc}
+     */
+    @Override
+    public String toString(){
+        String dash = "---";
+
+        StringBuilder result = new StringBuilder();
+
+        result.append("model name : ").append(this.modelName).append('\n');
+
+        for(String boneName : this.bonePartMap.getNames()){
+            List<BoneMotion> motionList =
+                    this.bonePartMap.getNamedList(boneName);
+            for(BoneMotion motion : motionList){
+                result.append(dash).append('\n');
+                result.append(motion.toString()).append('\n');
+            }
+        }
+
+        for(String morphName : this.morphPartMap.getNames()){
+            if(VmdConst.isBaseMorphName(morphName)) continue;
+            List<MorphMotion> motionList =
+                    this.morphPartMap.getNamedList(morphName);
+            for(MorphMotion morph : motionList){
+                result.append(morph.toString()).append('\n');
+            }
+        }
+
+        for(CameraMotion camera : this.cameraMotionList){
+            result.append(dash).append('\n');
+            result.append(camera.toString()).append('\n');
+        }
+
+        for(LuminousMotion luminous : this.luminousMotionList){
+            result.append(luminous.toString()).append('\n');
+        }
+
+        for(ShadowMotion shadow : this.shadowMotionList){
+            result.append(shadow.toString()).append('\n');
+        }
+
+        return result.toString();
+    }
+
+}
diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/model/binio/BasicExporter.java b/src/main/java/jp/sourceforge/mikutoga/vmd/model/binio/BasicExporter.java
new file mode 100644 (file)
index 0000000..5500853
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ * bone motion & morph exporter
+ *
+ * License : The MIT License
+ * Copyright(c) 2011 MikuToga Partners
+ */
+
+package jp.sourceforge.mikutoga.vmd.model.binio;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.LinkedList;
+import java.util.List;
+import jp.sourceforge.mikutoga.binio.BinaryExporter;
+import jp.sourceforge.mikutoga.binio.IllegalTextExportException;
+import jp.sourceforge.mikutoga.math.MkPos3D;
+import jp.sourceforge.mikutoga.math.MkQuat;
+import jp.sourceforge.mikutoga.vmd.VmdConst;
+import jp.sourceforge.mikutoga.vmd.model.BezierParam;
+import jp.sourceforge.mikutoga.vmd.model.BoneMotion;
+import jp.sourceforge.mikutoga.vmd.model.MorphMotion;
+import jp.sourceforge.mikutoga.vmd.model.NamedListMap;
+import jp.sourceforge.mikutoga.vmd.model.PosCurve;
+import jp.sourceforge.mikutoga.vmd.model.VmdMotion;
+
+/**
+ * ボーンモーション及びモーフ情報のエクスポーター。
+ */
+class BasicExporter extends BinaryExporter {
+
+    private static final int BZ_SIZE = 4;           // 4byte Bezier parameter
+    private static final int BZXYZR_SIZE = BZ_SIZE * 4; // XYZR Bezier
+    private static final int BZ_REDUNDANT = 4;          // redundant spare
+    private static final int BZTOTAL_SIZE = BZXYZR_SIZE * BZ_REDUNDANT;
+
+    private static final byte[] FDFILLER =
+        { (byte)0x00, (byte)0xfd };
+
+
+    private final byte[] motionIntplt = new byte[BZTOTAL_SIZE];
+
+
+    /**
+     * コンストラクタ。
+     * @param stream 出力ストリーム
+     */
+    BasicExporter(OutputStream stream){
+        super(stream);
+        return;
+    }
+
+
+    /**
+     * ボーンモーション情報を出力する。
+     * @param motion モーションデータ
+     * @throws IOException 出力エラー
+     * @throws IllegalTextExportException 不正なボーン名の出現
+     */
+    void dumpBoneMotion(VmdMotion motion)
+            throws IOException, IllegalTextExportException{
+        NamedListMap<BoneMotion> map = motion.getBonePartMap();
+        List<String> nameList = map.getNames();
+
+        List<BoneMotion> bmotionList = new LinkedList<BoneMotion>();
+
+        int count = 0;
+        for(String name : nameList){
+            List<BoneMotion> namedList = map.getNamedList(name);
+            for(BoneMotion boneMotion : namedList){
+                bmotionList.add(boneMotion);
+                count++;
+            }
+        }
+        dumpInt(count);
+
+        for(BoneMotion boneMotion : bmotionList){
+            String boneName = boneMotion.getBoneName();
+            dumpFixedW31j(boneName, VmdConst.BONENAME_MAX, FDFILLER);
+
+            int frame = boneMotion.getFrameNumber();
+            dumpInt(frame);
+
+            MkPos3D position = boneMotion.getPosition();
+            dumpBonePosition(position);
+
+            MkQuat rotation = boneMotion.getRotation();
+            dumpBoneRotation(rotation);
+
+            dumpBoneInterpolation(boneMotion);
+        }
+
+        return;
+    }
+
+    /**
+     * ボーン位置情報を出力する。
+     * @param position ボーン位置情報
+     * @throws IOException 出力エラー
+     */
+    private void dumpBonePosition(MkPos3D position)
+            throws IOException{
+        float xPos = (float) position.getXpos();
+        float yPos = (float) position.getYpos();
+        float zPos = (float) position.getZpos();
+
+        dumpFloat(xPos);
+        dumpFloat(yPos);
+        dumpFloat(zPos);
+
+        return;
+    }
+
+    /**
+     * ボーン回転情報を出力する。
+     * @param rotation ボーン回転情報
+     * @throws IOException 出力エラー
+     */
+    private void dumpBoneRotation(MkQuat rotation)
+            throws IOException{
+        float qx = (float) rotation.getQ1();
+        float qy = (float) rotation.getQ2();
+        float qz = (float) rotation.getQ3();
+        float qw = (float) rotation.getQW();
+
+        dumpFloat(qx);
+        dumpFloat(qy);
+        dumpFloat(qz);
+        dumpFloat(qw);
+
+        return;
+    }
+
+    /**
+     * ボーンモーションの補間情報を出力する。
+     * @param boneMotion ボーンモーション
+     * @throws IOException 出力エラー
+     */
+    private void dumpBoneInterpolation(BoneMotion boneMotion)
+            throws IOException{
+        PosCurve posCurve = boneMotion.getPosCurve();
+        BezierParam xCurve = posCurve.getIntpltXpos();
+        BezierParam yCurve = posCurve.getIntpltYpos();
+        BezierParam zCurve = posCurve.getIntpltZpos();
+        BezierParam rCurve = boneMotion.getIntpltRotation();
+
+        int idx = 0;
+
+        this.motionIntplt[idx++] = xCurve.getP1x();
+        this.motionIntplt[idx++] = yCurve.getP1x();
+        this.motionIntplt[idx++] = zCurve.getP1x();
+        this.motionIntplt[idx++] = rCurve.getP1x();
+
+        this.motionIntplt[idx++] = xCurve.getP1y();
+        this.motionIntplt[idx++] = yCurve.getP1y();
+        this.motionIntplt[idx++] = zCurve.getP1y();
+        this.motionIntplt[idx++] = rCurve.getP1y();
+
+        this.motionIntplt[idx++] = xCurve.getP2x();
+        this.motionIntplt[idx++] = yCurve.getP2x();
+        this.motionIntplt[idx++] = zCurve.getP2x();
+        this.motionIntplt[idx++] = rCurve.getP2x();
+
+        this.motionIntplt[idx++] = xCurve.getP2y();
+        this.motionIntplt[idx++] = yCurve.getP2y();
+        this.motionIntplt[idx++] = zCurve.getP2y();
+        this.motionIntplt[idx++] = rCurve.getP2y();
+
+        assert idx == BZXYZR_SIZE;
+
+        redundantCopy();
+
+        dumpByteArray(this.motionIntplt);
+
+        return;
+    }
+
+    /**
+     * 補間情報冗長部の組み立て。
+     */
+    private void redundantCopy(){
+        int lack = 1;
+        for(int ct = 1; ct < BZ_REDUNDANT; ct++){
+            int sourceIdx = 0 + lack;
+            int targetIdx = BZXYZR_SIZE * ct;
+            int span = BZXYZR_SIZE - lack;
+
+            System.arraycopy(this.motionIntplt, sourceIdx,
+                             this.motionIntplt, targetIdx,
+                             span );
+
+            int onePos = targetIdx + span;
+            this.motionIntplt[onePos] = (byte) 0x01;
+
+            int zeroPosStart = onePos + 1;
+            int zeroPosEnd = targetIdx + BZXYZR_SIZE;
+            for(int idx = zeroPosStart; idx < zeroPosEnd; idx++){
+                this.motionIntplt[idx] = (byte) 0x00;
+            }
+
+            lack++;
+        }
+
+        return;
+    }
+
+    /**
+     * モーフ情報を出力する。
+     * @param motion モーションデータ
+     * @throws IOException 出力エラー
+     * @throws IllegalTextExportException 不正なモーフ名の出現
+     */
+    void dumpMorphMotion(VmdMotion motion)
+            throws IOException, IllegalTextExportException{
+        NamedListMap<MorphMotion> map = motion.getMorphPartMap();
+        List<String> nameList = map.getNames();
+
+        List<MorphMotion> morphList = new LinkedList<MorphMotion>();
+
+        int count = 0;
+        for(String name : nameList){
+            List<MorphMotion> namedList = map.getNamedList(name);
+            for(MorphMotion morphMotion : namedList){
+                morphList.add(morphMotion);
+                count++;
+            }
+        }
+        dumpInt(count);
+
+        for(MorphMotion morphMotion : morphList){
+            String morphName = morphMotion.getMorphName();
+            dumpFixedW31j(morphName, VmdConst.MORPHNAME_MAX, FDFILLER);
+
+            int frame = morphMotion.getFrameNumber();
+            dumpInt(frame);
+
+            float flex = morphMotion.getFlex();
+            dumpFloat(flex);
+        }
+
+        return;
+    }
+
+}
diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/model/binio/BasicLoader.java b/src/main/java/jp/sourceforge/mikutoga/vmd/model/binio/BasicLoader.java
new file mode 100644 (file)
index 0000000..d7f6114
--- /dev/null
@@ -0,0 +1,314 @@
+/*
+ * basic information builder
+ *
+ * License : The MIT License
+ * Copyright(c) 2011 MikuToga Partners
+ */
+
+package jp.sourceforge.mikutoga.vmd.model.binio;
+
+import jp.sourceforge.mikutoga.math.MkPos3D;
+import jp.sourceforge.mikutoga.math.MkQuat;
+import jp.sourceforge.mikutoga.parser.MmdFormatException;
+import jp.sourceforge.mikutoga.parser.ParseStage;
+import jp.sourceforge.mikutoga.vmd.model.BezierParam;
+import jp.sourceforge.mikutoga.vmd.model.BoneMotion;
+import jp.sourceforge.mikutoga.vmd.model.MorphMotion;
+import jp.sourceforge.mikutoga.vmd.model.PosCurve;
+import jp.sourceforge.mikutoga.vmd.model.VmdMotion;
+import jp.sourceforge.mikutoga.vmd.parser.VmdBasicHandler;
+
+/**
+ * ボーンモーション、モーフ情報のビルダ。
+ */
+class BasicLoader implements VmdBasicHandler {
+
+    private final VmdMotion vmdMotion;
+
+    private BoneMotion currentBoneMotion;
+    private MorphMotion currentMorphMotion;
+
+    private boolean hasMoreData;
+
+
+    /**
+     * コンストラクタ。
+     * @param vmdMotion モーション情報
+     */
+    BasicLoader(VmdMotion vmdMotion){
+        super();
+        this.vmdMotion = vmdMotion;
+
+        this.currentBoneMotion  = null;
+        this.currentMorphMotion = null;
+
+        this.hasMoreData = false;
+
+        return;
+    }
+
+
+    /**
+     * ボーンモーションループか否か判定する。
+     * @param stage 判定対象
+     * @return モーションループならtrue
+     */
+    private static boolean isBoneMotionList(ParseStage stage){
+        if(stage == VmdBasicHandler.BONEMOTION_LIST) return true;
+        return false;
+    }
+
+    /**
+     * モーフループか否か判定する。
+     * @param stage 判定対象
+     * @return モーフループならtrue
+     */
+    private static boolean isMorphList(ParseStage stage){
+        if(stage == VmdBasicHandler.MORPH_LIST) return true;
+        return false;
+    }
+
+
+    /**
+     * {@inheritDoc}
+     * @throws MmdFormatException {@inheritDoc}
+     */
+    @Override
+    public void vmdParseStart() throws MmdFormatException{
+        // NOTHING
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param hasMoreDataArg {@inheritDoc}
+     * @throws MmdFormatException {@inheritDoc}
+     */
+    @Override
+    public void vmdParseEnd(boolean hasMoreDataArg)
+            throws MmdFormatException{
+        this.hasMoreData = hasMoreDataArg;
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param stage {@inheritDoc}
+     * @param loops {@inheritDoc}
+     * @throws MmdFormatException {@inheritDoc}
+     */
+    @Override
+    public void loopStart(ParseStage stage, int loops)
+            throws MmdFormatException{
+        if(isBoneMotionList(stage)){
+            this.currentBoneMotion = new BoneMotion();
+        }else if(isMorphList(stage)){
+            this.currentMorphMotion = new MorphMotion();
+        }
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param stage {@inheritDoc}
+     * @throws MmdFormatException {@inheritDoc}
+     */
+    @Override
+    public void loopNext(ParseStage stage)
+            throws MmdFormatException{
+        if(isBoneMotionList(stage)){
+            this.vmdMotion.addBoneMotion(this.currentBoneMotion);
+            this.currentBoneMotion = new BoneMotion();
+        }else if(isMorphList(stage)){
+            this.vmdMotion.addMorphMotion(this.currentMorphMotion);
+            this.currentMorphMotion = new MorphMotion();
+        }
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param stage {@inheritDoc}
+     * @throws MmdFormatException {@inheritDoc}
+     */
+    @Override
+    public void loopEnd(ParseStage stage)
+            throws MmdFormatException{
+        if(isBoneMotionList(stage)){
+            this.currentBoneMotion = null;
+        }else if(isMorphList(stage)){
+            this.currentMorphMotion = null;
+        }
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param header {@inheritDoc}
+     * @throws MmdFormatException {@inheritDoc}
+     */
+    @Override
+    public void vmdHeaderInfo(byte[] header) throws MmdFormatException{
+        // NOTHING
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param modelName {@inheritDoc}
+     * @throws MmdFormatException {@inheritDoc}
+     */
+    @Override
+    public void vmdModelName(String modelName) throws MmdFormatException{
+        this.vmdMotion.setModelName(modelName);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param boneName {@inheritDoc}
+     * @param keyFrameNo {@inheritDoc}
+     * @throws MmdFormatException {@inheritDoc}
+     */
+    @Override
+    public void vmdBoneMotion(String boneName, int keyFrameNo)
+            throws MmdFormatException{
+        this.currentBoneMotion.setBoneName(boneName);
+        this.currentBoneMotion.setFrameNumber(keyFrameNo);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param qx {@inheritDoc}
+     * @param qy {@inheritDoc}
+     * @param qz {@inheritDoc}
+     * @param qw {@inheritDoc}
+     * @throws MmdFormatException {@inheritDoc}
+     */
+    @Override
+    public void vmdBoneRotationQt(float qx, float qy, float qz, float qw)
+            throws MmdFormatException{
+        MkQuat rotation = this.currentBoneMotion.getRotation();
+        rotation.setQ1(qx);
+        rotation.setQ2(qy);
+        rotation.setQ3(qz);
+        rotation.setQW(qw);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param xPos {@inheritDoc}
+     * @param yPos {@inheritDoc}
+     * @param zPos {@inheritDoc}
+     * @throws MmdFormatException {@inheritDoc}
+     */
+    @Override
+    public void vmdBonePosition(float xPos, float yPos, float zPos)
+            throws MmdFormatException{
+        MkPos3D position = this.currentBoneMotion.getPosition();
+        position.setPosition(xPos, yPos, zPos);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param rP1x {@inheritDoc}
+     * @param rP1y {@inheritDoc}
+     * @param rP2x {@inheritDoc}
+     * @param rP2y {@inheritDoc}
+     * @throws MmdFormatException {@inheritDoc}
+     */
+    @Override
+    public void vmdBoneIntpltRot(byte rP1x, byte rP1y,
+                                      byte rP2x, byte rP2y )
+            throws MmdFormatException{
+        BezierParam bezier = this.currentBoneMotion.getIntpltRotation();
+        bezier.setP1(rP1x, rP1y);
+        bezier.setP2(rP2x, rP2y);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param xP1x {@inheritDoc}
+     * @param xP1y {@inheritDoc}
+     * @param xP2x {@inheritDoc}
+     * @param xP2y {@inheritDoc}
+     * @throws MmdFormatException {@inheritDoc}
+     */
+    @Override
+    public void vmdBoneIntpltXpos(byte xP1x, byte xP1y,
+                                       byte xP2x, byte xP2y )
+            throws MmdFormatException{
+        PosCurve posCurve = this.currentBoneMotion.getPosCurve();
+        BezierParam bezier = posCurve.getIntpltXpos();
+        bezier.setP1(xP1x, xP1y);
+        bezier.setP2(xP2x, xP2y);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param yP1x {@inheritDoc}
+     * @param yP1y {@inheritDoc}
+     * @param yP2x {@inheritDoc}
+     * @param yP2y {@inheritDoc}
+     * @throws MmdFormatException {@inheritDoc}
+     */
+    @Override
+    public void vmdBoneIntpltYpos(byte yP1x, byte yP1y,
+                                       byte yP2x, byte yP2y )
+            throws MmdFormatException{
+        PosCurve posCurve = this.currentBoneMotion.getPosCurve();
+        BezierParam bezier = posCurve.getIntpltYpos();
+        bezier.setP1(yP1x, yP1y);
+        bezier.setP2(yP2x, yP2y);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param zP1x {@inheritDoc}
+     * @param zP1y {@inheritDoc}
+     * @param zP2x {@inheritDoc}
+     * @param zP2y {@inheritDoc}
+     * @throws MmdFormatException {@inheritDoc}
+     */
+    @Override
+    public void vmdBoneIntpltZpos(byte zP1x, byte zP1y,
+                                       byte zP2x, byte zP2y )
+            throws MmdFormatException{
+        PosCurve posCurve = this.currentBoneMotion.getPosCurve();
+        BezierParam bezier = posCurve.getIntpltZpos();
+        bezier.setP1(zP1x, zP1y);
+        bezier.setP2(zP2x, zP2y);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param morphName {@inheritDoc}
+     * @param keyFrameNo {@inheritDoc}
+     * @param flex {@inheritDoc}
+     * @throws MmdFormatException {@inheritDoc}
+     */
+    @Override
+    public void vmdMorphMotion(String morphName, int keyFrameNo, float flex)
+            throws MmdFormatException{
+        this.currentMorphMotion.setMorphName(morphName);
+        this.currentMorphMotion.setFrameNumber(keyFrameNo);
+        this.currentMorphMotion.setFlex(flex);
+        return;
+    }
+
+    /**
+     * 読み残したデータがあるか判定する。
+     * @return 読み残したデータがあればtrue
+     */
+    boolean hasMoreData(){
+        return this.hasMoreData;
+    }
+
+}
diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/model/binio/CameraExporter.java b/src/main/java/jp/sourceforge/mikutoga/vmd/model/binio/CameraExporter.java
new file mode 100644 (file)
index 0000000..01fbec1
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * camera information exporter
+ *
+ * License : The MIT License
+ * Copyright(c) 2011 MikuToga Partners
+ */
+
+package jp.sourceforge.mikutoga.vmd.model.binio;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.List;
+import jp.sourceforge.mikutoga.binio.BinaryExporter;
+import jp.sourceforge.mikutoga.math.MkPos3D;
+import jp.sourceforge.mikutoga.vmd.model.BezierParam;
+import jp.sourceforge.mikutoga.vmd.model.CameraMotion;
+import jp.sourceforge.mikutoga.vmd.model.CameraRotation;
+import jp.sourceforge.mikutoga.vmd.model.PosCurve;
+import jp.sourceforge.mikutoga.vmd.model.VmdMotion;
+
+/**
+ * カメラ情報のエクスポーター。
+ */
+class CameraExporter extends BinaryExporter {
+
+    /**
+     * コンストラクタ。
+     * @param stream 出力ストリーム
+     */
+    CameraExporter(OutputStream stream){
+        super(stream);
+        return;
+    }
+
+
+    /**
+     * カメラモーション情報を出力する。
+     * @param motion モーションデータ
+     * @throws IOException 出力エラー
+     */
+    void dumpCameraMotion(VmdMotion motion) throws IOException{
+        List<CameraMotion> list = motion.getCameraMotionList();
+
+        int count = list.size();
+        dumpInt(count);
+
+        for(CameraMotion cameraMotion : list){
+            int frame = cameraMotion.getFrameNumber();
+            dumpInt(frame);
+
+            float range = cameraMotion.getRange();
+            dumpFloat(range);
+
+            MkPos3D targetPos = cameraMotion.getCameraTarget();
+            dumpFloat((float) targetPos.getXpos());
+            dumpFloat((float) targetPos.getYpos());
+            dumpFloat((float) targetPos.getZpos());
+
+            CameraRotation rotation = cameraMotion.getCameraRotation();
+            dumpFloat(rotation.getLatitude());
+            dumpFloat(rotation.getLongitude());
+            dumpFloat(rotation.getRoll());
+
+            dumpCameraCurve(cameraMotion);
+
+            dumpInt(cameraMotion.getProjectionAngle());
+
+            byte perspectiveSwitch;
+            if(cameraMotion.hasPerspective()) perspectiveSwitch = 0x00;
+            else                              perspectiveSwitch = 0x01;
+            dumpByte(perspectiveSwitch);
+        }
+
+        return;
+    }
+
+    /**
+     * カメラ補間情報を出力する。
+     * @param cameraMotion モーションデータ
+     * @throws IOException 出力エラー
+     */
+    private void dumpCameraCurve(CameraMotion cameraMotion)
+            throws IOException{
+        PosCurve posCurve = cameraMotion.getTargetPosCurve();
+        BezierParam xCurve = posCurve.getIntpltXpos();
+        BezierParam yCurve = posCurve.getIntpltYpos();
+        BezierParam zCurve = posCurve.getIntpltZpos();
+        dumpCameraBezier(xCurve);
+        dumpCameraBezier(yCurve);
+        dumpCameraBezier(zCurve);
+
+        BezierParam rotCurve   = cameraMotion.getIntpltRotation();
+        BezierParam rangeCurve = cameraMotion.getIntpltRange();
+        BezierParam projCurve  = cameraMotion.getIntpltProjection();
+        dumpCameraBezier(rotCurve);
+        dumpCameraBezier(rangeCurve);
+        dumpCameraBezier(projCurve);
+
+        return;
+    }
+
+    /**
+     * ベジェ曲線情報を出力する。
+     * @param bezier ベジェ曲線
+     * @throws IOException 出力エラー
+     */
+    private void dumpCameraBezier(BezierParam bezier) throws IOException{
+        dumpByte(bezier.getP1x());
+        dumpByte(bezier.getP2x());
+        dumpByte(bezier.getP1y());
+        dumpByte(bezier.getP2y());
+
+        return;
+    }
+
+}
diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/model/binio/CameraLoader.java b/src/main/java/jp/sourceforge/mikutoga/vmd/model/binio/CameraLoader.java
new file mode 100644 (file)
index 0000000..1193af2
--- /dev/null
@@ -0,0 +1,258 @@
+/*
+ * camera information builder
+ *
+ * License : The MIT License
+ * Copyright(c) 2011 MikuToga Partners
+ */
+
+package jp.sourceforge.mikutoga.vmd.model.binio;
+
+import java.util.List;
+import jp.sourceforge.mikutoga.parser.MmdFormatException;
+import jp.sourceforge.mikutoga.parser.ParseStage;
+import jp.sourceforge.mikutoga.vmd.model.BezierParam;
+import jp.sourceforge.mikutoga.vmd.model.CameraMotion;
+import jp.sourceforge.mikutoga.vmd.model.CameraRotation;
+import jp.sourceforge.mikutoga.vmd.model.PosCurve;
+import jp.sourceforge.mikutoga.vmd.model.VmdMotion;
+import jp.sourceforge.mikutoga.vmd.parser.VmdCameraHandler;
+
+/**
+ * カメラ情報のビルダ。
+ */
+class CameraLoader implements VmdCameraHandler {
+
+    private final List<CameraMotion> cameraMotionList;
+
+    private CameraMotion currentCameraMotion;
+
+
+    /**
+     * コンストラクタ。
+     * @param vmdMotion モーションデータ。
+     */
+    CameraLoader(VmdMotion vmdMotion){
+        super();
+        this.cameraMotionList = vmdMotion.getCameraMotionList();
+        this.currentCameraMotion = null;
+        return;
+    }
+
+
+    /**
+     * {@inheritDoc}
+     * @param stage {@inheritDoc}
+     * @param loops {@inheritDoc}
+     * @throws MmdFormatException {@inheritDoc}
+     */
+    @Override
+    public void loopStart(ParseStage stage, int loops)
+            throws MmdFormatException{
+        this.currentCameraMotion = new CameraMotion();
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param stage {@inheritDoc}
+     * @throws MmdFormatException {@inheritDoc}
+     */
+    @Override
+    public void loopNext(ParseStage stage)
+            throws MmdFormatException{
+        this.cameraMotionList.add(this.currentCameraMotion);
+        this.currentCameraMotion = new CameraMotion();
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param stage {@inheritDoc}
+     * @throws MmdFormatException {@inheritDoc}
+     */
+    @Override
+    public void loopEnd(ParseStage stage)
+            throws MmdFormatException{
+        this.currentCameraMotion = null;
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param keyFrameNo {@inheritDoc}
+     * @throws MmdFormatException {@inheritDoc}
+     */
+    @Override
+    public void vmdCameraMotion(int keyFrameNo) throws MmdFormatException{
+        this.currentCameraMotion.setFrameNumber(keyFrameNo);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param xPos {@inheritDoc}
+     * @param yPos {@inheritDoc}
+     * @param zPos {@inheritDoc}
+     * @throws MmdFormatException {@inheritDoc}
+     */
+    @Override
+    public void vmdCameraPosition(float xPos, float yPos, float zPos)
+            throws MmdFormatException{
+        this.currentCameraMotion.getCameraTarget()
+                .setPosition(xPos, yPos, zPos);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param latitude {@inheritDoc}
+     * @param longitude {@inheritDoc}
+     * @param roll {@inheritDoc}
+     * @throws MmdFormatException {@inheritDoc}
+     */
+    @Override
+    public void vmdCameraRotation(float latitude,
+                                    float longitude,
+                                    float roll )
+            throws MmdFormatException{
+        CameraRotation rotation =
+                this.currentCameraMotion.getCameraRotation();
+        rotation.setLatitude(latitude);
+        rotation.setLongitude(longitude);
+        rotation.setRoll(roll);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param range {@inheritDoc}
+     * @throws MmdFormatException {@inheritDoc}
+     */
+    @Override
+    public void vmdCameraRange(float range) throws MmdFormatException{
+        this.currentCameraMotion.setRange(range);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param angle {@inheritDoc}
+     * @param hasPerspective {@inheritDoc}
+     * @throws MmdFormatException {@inheritDoc}
+     */
+    @Override
+    public void vmdCameraProjection(int angle, boolean hasPerspective)
+            throws MmdFormatException{
+        this.currentCameraMotion.setProjectionAngle(angle);
+        this.currentCameraMotion.setPerspectiveMode(hasPerspective);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param p1x {@inheritDoc}
+     * @param p1y {@inheritDoc}
+     * @param p2x {@inheritDoc}
+     * @param p2y {@inheritDoc}
+     * @throws MmdFormatException {@inheritDoc}
+     */
+    @Override
+    public void vmdCameraIntpltXpos(byte p1x, byte p1y, byte p2x, byte p2y)
+            throws MmdFormatException{
+        PosCurve posCurve = this.currentCameraMotion.getTargetPosCurve();
+        BezierParam bezier = posCurve.getIntpltXpos();
+        bezier.setP1(p1x, p1y);
+        bezier.setP2(p2x, p2y);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param p1x {@inheritDoc}
+     * @param p1y {@inheritDoc}
+     * @param p2x {@inheritDoc}
+     * @param p2y {@inheritDoc}
+     * @throws MmdFormatException {@inheritDoc}
+     */
+    @Override
+    public void vmdCameraIntpltYpos(byte p1x, byte p1y, byte p2x, byte p2y)
+            throws MmdFormatException{
+        PosCurve posCurve = this.currentCameraMotion.getTargetPosCurve();
+        BezierParam bezier = posCurve.getIntpltYpos();
+        bezier.setP1(p1x, p1y);
+        bezier.setP2(p2x, p2y);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param p1x {@inheritDoc}
+     * @param p1y {@inheritDoc}
+     * @param p2x {@inheritDoc}
+     * @param p2y {@inheritDoc}
+     * @throws MmdFormatException {@inheritDoc}
+     */
+    @Override
+    public void vmdCameraIntpltZpos(byte p1x, byte p1y, byte p2x, byte p2y)
+            throws MmdFormatException{
+        PosCurve posCurve = this.currentCameraMotion.getTargetPosCurve();
+        BezierParam bezier = posCurve.getIntpltZpos();
+        bezier.setP1(p1x, p1y);
+        bezier.setP2(p2x, p2y);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param p1x {@inheritDoc}
+     * @param p1y {@inheritDoc}
+     * @param p2x {@inheritDoc}
+     * @param p2y {@inheritDoc}
+     * @throws MmdFormatException {@inheritDoc}
+     */
+    @Override
+    public void vmdCameraIntpltRotation(byte p1x, byte p1y,
+                                           byte p2x, byte p2y )
+            throws MmdFormatException{
+        BezierParam bezier = this.currentCameraMotion.getIntpltRotation();
+        bezier.setP1(p1x, p1y);
+        bezier.setP2(p2x, p2y);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param p1x {@inheritDoc}
+     * @param p1y {@inheritDoc}
+     * @param p2x {@inheritDoc}
+     * @param p2y {@inheritDoc}
+     * @throws MmdFormatException {@inheritDoc}
+     */
+    @Override
+    public void vmdCameraIntpltRange(byte p1x, byte p1y, byte p2x, byte p2y)
+            throws MmdFormatException{
+        BezierParam bezier = this.currentCameraMotion.getIntpltRange();
+        bezier.setP1(p1x, p1y);
+        bezier.setP2(p2x, p2y);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param p1x {@inheritDoc}
+     * @param p1y {@inheritDoc}
+     * @param p2x {@inheritDoc}
+     * @param p2y {@inheritDoc}
+     * @throws MmdFormatException {@inheritDoc}
+     */
+    @Override
+    public void vmdCameraIntpltProjection(byte p1x, byte p1y,
+                                              byte p2x, byte p2y )
+            throws MmdFormatException{
+        BezierParam bezier = this.currentCameraMotion.getIntpltProjection();
+        bezier.setP1(p1x, p1y);
+        bezier.setP2(p2x, p2y);
+        return;
+    }
+
+}
diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/model/binio/LightingExporter.java b/src/main/java/jp/sourceforge/mikutoga/vmd/model/binio/LightingExporter.java
new file mode 100644 (file)
index 0000000..cd67ac2
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * lighting information exporter
+ *
+ * License : The MIT License
+ * Copyright(c) 2011 MikuToga Partners
+ */
+
+package jp.sourceforge.mikutoga.vmd.model.binio;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.List;
+import jp.sourceforge.mikutoga.binio.BinaryExporter;
+import jp.sourceforge.mikutoga.vmd.model.LuminousColor;
+import jp.sourceforge.mikutoga.vmd.model.LuminousMotion;
+import jp.sourceforge.mikutoga.vmd.model.LuminousVector;
+import jp.sourceforge.mikutoga.vmd.model.ShadowMode;
+import jp.sourceforge.mikutoga.vmd.model.ShadowMotion;
+import jp.sourceforge.mikutoga.vmd.model.VmdMotion;
+
+/**
+ * ライティング情報のエクスポーター。
+ */
+class LightingExporter extends BinaryExporter {
+
+    /**
+     * コンストラクタ。
+     * @param stream 出力ストリーム
+     */
+    LightingExporter(OutputStream stream){
+        super(stream);
+        return;
+    }
+
+
+    /**
+     * 照明情報を出力する。
+     * @param motion モーションデータ
+     * @throws IOException 出力エラー
+     */
+    void dumpLuminousMotion(VmdMotion motion) throws IOException{
+        List<LuminousMotion> list = motion.getLuminousMotionList();
+
+        int count = list.size();
+        dumpInt(count);
+
+        for(LuminousMotion luminousMotion : list){
+            int frame = luminousMotion.getFrameNumber();
+            dumpInt(frame);
+
+            LuminousColor color = luminousMotion.getColor();
+            dumpFloat(color.getColR());
+            dumpFloat(color.getColG());
+            dumpFloat(color.getColB());
+
+            LuminousVector vector = luminousMotion.getDirection();
+            dumpFloat(vector.getVecX());
+            dumpFloat(vector.getVecY());
+            dumpFloat(vector.getVecZ());
+        }
+
+        return;
+    }
+
+    /**
+     * シャドウ演出情報を出力する。
+     * @param motion モーションデータ
+     * @throws IOException 出力エラー
+     */
+    void dumpShadowMotion(VmdMotion motion) throws IOException{
+        List<ShadowMotion> list = motion.getShadowMotionList();
+
+        int count = list.size();
+        dumpInt(count);
+
+        for(ShadowMotion shadowMotion : list){
+            int frame = shadowMotion.getFrameNumber();
+            dumpInt(frame);
+
+            ShadowMode mode = shadowMotion.getShadowMode();
+            byte shadowType = mode.getEncodedByte();
+            dumpByte(shadowType);
+
+            float rawParam = shadowMotion.getRawScopeParam();
+            dumpFloat(rawParam);
+        }
+
+        return;
+    }
+
+}
diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/model/binio/LightingLoader.java b/src/main/java/jp/sourceforge/mikutoga/vmd/model/binio/LightingLoader.java
new file mode 100644 (file)
index 0000000..01e0dd9
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * lighting information builder
+ *
+ * License : The MIT License
+ * Copyright(c) 2011 MikuToga Partners
+ */
+
+package jp.sourceforge.mikutoga.vmd.model.binio;
+
+import java.util.List;
+import jp.sourceforge.mikutoga.parser.MmdFormatException;
+import jp.sourceforge.mikutoga.parser.ParseStage;
+import jp.sourceforge.mikutoga.vmd.model.LuminousColor;
+import jp.sourceforge.mikutoga.vmd.model.LuminousMotion;
+import jp.sourceforge.mikutoga.vmd.model.LuminousVector;
+import jp.sourceforge.mikutoga.vmd.model.ShadowMode;
+import jp.sourceforge.mikutoga.vmd.model.ShadowMotion;
+import jp.sourceforge.mikutoga.vmd.model.VmdMotion;
+import jp.sourceforge.mikutoga.vmd.parser.VmdLightingHandler;
+
+/**
+ * ライティング情報のビルダ。
+ */
+class LightingLoader implements VmdLightingHandler {
+
+    private final List<LuminousMotion> luminousMotionList;
+    private final List<ShadowMotion> shadowMotionList;
+
+    private LuminousMotion currentLuminousMotion;
+    private ShadowMotion currentShadowMotion;
+
+
+    /**
+     * コンストラクタ。
+     * @param vmdMotion モーションデータ。
+     */
+    LightingLoader(VmdMotion vmdMotion){
+        super();
+
+        this.luminousMotionList = vmdMotion.getLuminousMotionList();
+        this.shadowMotionList   = vmdMotion.getShadowMotionList();
+
+        this.currentLuminousMotion = null;
+        this.currentShadowMotion = null;
+
+        return;
+    }
+
+
+    /**
+     * 照明ループか否か判定する。
+     * @param stage 判定対象
+     * @return 照明ループならtrue
+     */
+    private static boolean isLuminousList(ParseStage stage){
+        if(stage == VmdLightingHandler.LUMINOUS_LIST) return true;
+        return false;
+    }
+
+    /**
+     * シャドウループか否か判定する。
+     * @param stage 判定対象
+     * @return シャドウループならtrue
+     */
+    private static boolean isShadowList(ParseStage stage){
+        if(stage == VmdLightingHandler.SHADOW_LIST) return true;
+        return false;
+    }
+
+
+    /**
+     * {@inheritDoc}
+     * @param stage {@inheritDoc}
+     * @param loops {@inheritDoc}
+     * @throws MmdFormatException {@inheritDoc}
+     */
+    @Override
+    public void loopStart(ParseStage stage, int loops)
+            throws MmdFormatException{
+        if(isLuminousList(stage)){
+            this.currentLuminousMotion = new LuminousMotion();
+        }else if(isShadowList(stage)){
+            this.currentShadowMotion = new ShadowMotion();
+        }
+
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param stage {@inheritDoc}
+     * @throws MmdFormatException {@inheritDoc}
+     */
+    @Override
+    public void loopNext(ParseStage stage)
+            throws MmdFormatException{
+        if(isLuminousList(stage)){
+            this.luminousMotionList.add(this.currentLuminousMotion);
+            this.currentLuminousMotion = new LuminousMotion();
+        }else if(isShadowList(stage)){
+            this.shadowMotionList.add(this.currentShadowMotion);
+            this.currentShadowMotion = new ShadowMotion();
+        }
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param stage {@inheritDoc}
+     * @throws MmdFormatException {@inheritDoc}
+     */
+    @Override
+    public void loopEnd(ParseStage stage)
+            throws MmdFormatException{
+        if(isLuminousList(stage)){
+            this.currentLuminousMotion = null;
+        }else if(isShadowList(stage)){
+            this.currentShadowMotion = null;
+        }
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param keyFrameNo {@inheritDoc}
+     * @throws MmdFormatException {@inheritDoc}
+     */
+    @Override
+    public void vmdLuminousMotion(int keyFrameNo) throws MmdFormatException{
+        this.currentLuminousMotion.setFrameNumber(keyFrameNo);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param rVal {@inheritDoc}
+     * @param gVal {@inheritDoc}
+     * @param bVal {@inheritDoc}
+     * @throws MmdFormatException {@inheritDoc}
+     */
+    @Override
+    public void vmdLuminousColor(float rVal, float gVal, float bVal)
+            throws MmdFormatException{
+        LuminousColor color = this.currentLuminousMotion.getColor();
+        color.setColR(rVal);
+        color.setColG(gVal);
+        color.setColB(bVal);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param xVec {@inheritDoc}
+     * @param yVec {@inheritDoc}
+     * @param zVec {@inheritDoc}
+     * @throws MmdFormatException {@inheritDoc}
+     */
+    @Override
+    public void vmdLuminousDirection(float xVec, float yVec, float zVec)
+            throws MmdFormatException{
+        LuminousVector direction = this.currentLuminousMotion.getDirection();
+        direction.setVecX(xVec);
+        direction.setVecY(yVec);
+        direction.setVecZ(zVec);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param keyFrameNo {@inheritDoc}
+     * @throws MmdFormatException {@inheritDoc}
+     */
+    @Override
+    public void vmdShadowMotion(int keyFrameNo) throws MmdFormatException{
+        this.currentShadowMotion.setFrameNumber(keyFrameNo);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param shadowMode {@inheritDoc}
+     * @throws MmdFormatException {@inheritDoc}
+     */
+    @Override
+    public void vmdShadowMode(byte shadowMode) throws MmdFormatException{
+        ShadowMode mode = ShadowMode.decode(shadowMode);
+        this.currentShadowMotion.setShadowMode(mode);
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param shadowScope {@inheritDoc}
+     * @throws MmdFormatException {@inheritDoc}
+     */
+    @Override
+    public void vmdShadowScopeRaw(float shadowScope)
+            throws MmdFormatException{
+        this.currentShadowMotion.setRawScopeParam(shadowScope);
+        return;
+    }
+
+}
diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/model/binio/VmdExporter.java b/src/main/java/jp/sourceforge/mikutoga/vmd/model/binio/VmdExporter.java
new file mode 100644 (file)
index 0000000..a27ee9d
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * vmd exporter
+ *
+ * License : The MIT License
+ * Copyright(c) 2011 MikuToga Partners
+ */
+
+package jp.sourceforge.mikutoga.vmd.model.binio;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import jp.sourceforge.mikutoga.binio.BinaryExporter;
+import jp.sourceforge.mikutoga.binio.IllegalTextExportException;
+import jp.sourceforge.mikutoga.vmd.IllegalVmdDataException;
+import jp.sourceforge.mikutoga.vmd.VmdConst;
+import jp.sourceforge.mikutoga.vmd.model.VmdMotion;
+
+/**
+ * VMDファイルのエクスポーター。
+ */
+public class VmdExporter extends BinaryExporter {
+
+    private static final byte[] HEADFILLER = {
+        (byte)'J',
+        (byte)'K',
+        (byte)'L',
+        (byte)'M',
+    };
+
+    private static final byte[] FDFILLER =
+        { (byte)0x00, (byte)0xfd };
+
+
+    private final BasicExporter    basicExporter;
+    private final CameraExporter   cameraExporter;
+    private final LightingExporter lightingExporter;
+
+
+    /**
+     * コンストラクタ。
+     * @param stream 出力ストリーム
+     */
+    public VmdExporter(OutputStream stream){
+        super(stream);
+
+        this.basicExporter    = new BasicExporter(stream);
+        this.cameraExporter   = new CameraExporter(stream);
+        this.lightingExporter = new LightingExporter(stream);
+
+        return;
+    }
+
+    /**
+     * モーションデータをVMDファイル形式で出力する。
+     * <p>異常時には出力データのフラッシュが試みられる。
+     * @param motion モーションデータ
+     * @throws IOException 出力エラー
+     * @throws IllegalVmdDataException モーションデータに不備が発見された
+     */
+    public void dumpVmdMotion(VmdMotion motion)
+            throws IOException, IllegalVmdDataException{
+        try{
+            dumpVmdMotionImpl(motion);
+        }finally{
+            flush();
+        }
+
+        return;
+    }
+
+    /**
+     * モーションデータをVMDファイル形式で出力する。
+     * @param motion モーションデータ
+     * @throws IOException 出力エラー
+     * @throws IllegalVmdDataException モーションデータに不備が発見された
+     */
+    private void dumpVmdMotionImpl(VmdMotion motion)
+            throws IOException, IllegalVmdDataException{
+        dumpHeader();
+
+        try{
+            dumpModelName(motion);
+            this.basicExporter.dumpBoneMotion(motion);
+            this.basicExporter.dumpMorphMotion(motion);
+        }catch(IllegalTextExportException e){
+            throw new IllegalVmdDataException(e);
+        }
+
+        this.cameraExporter.dumpCameraMotion(motion);
+        this.lightingExporter.dumpLuminousMotion(motion);
+        this.lightingExporter.dumpShadowMotion(motion);
+
+        return;
+    }
+
+    /**
+     * ヘッダ情報を出力する。
+     * @throws IOException 出力エラー
+     */
+    private void dumpHeader() throws IOException{
+        byte[] header = VmdConst.createMagicHeader();
+        dumpByteArray(header);
+        dumpByteArray(HEADFILLER);
+
+        assert header.length + HEADFILLER.length == VmdConst.HEADER_LENGTH;
+
+        return;
+    }
+
+    /**
+     * モデル名を出力する。
+     * <p>演出データのモデル名には
+     * 便宜的に{@link VmdConst.MODELNAME_STAGEACT}が使われる。
+     * @param motion モーションデータ
+     * @throws IOException 出力エラー
+     * @throws IllegalTextExportException 不正なモデル名の出現
+     */
+    private void dumpModelName(VmdMotion motion)
+            throws IOException, IllegalTextExportException{
+        String modelName = motion.getModelName();
+        if(modelName == null) modelName = VmdConst.MODELNAME_STAGEACT;
+
+        dumpFixedW31j(modelName, VmdConst.MODELNAME_MAX, FDFILLER);
+
+        return;
+    }
+
+}
diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/model/binio/VmdLoader.java b/src/main/java/jp/sourceforge/mikutoga/vmd/model/binio/VmdLoader.java
new file mode 100644 (file)
index 0000000..aa92058
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * VMD loader
+ *
+ * License : The MIT License
+ * Copyright(c) 2011 MikuToga Partners
+ */
+
+package jp.sourceforge.mikutoga.vmd.model.binio;
+
+import java.io.IOException;
+import jp.sourceforge.mikutoga.parser.MmdFormatException;
+import jp.sourceforge.mikutoga.parser.MmdSource;
+import jp.sourceforge.mikutoga.vmd.model.VmdMotion;
+import jp.sourceforge.mikutoga.vmd.parser.VmdParser;
+
+/**
+ * VMDモーションファイルを読み込むためのバイナリローダ。
+ */
+public final class VmdLoader {
+
+    /**
+     * コンストラクタ。
+     */
+    private VmdLoader(){
+        super();
+        assert false;
+        throw new AssertionError();
+    }
+
+
+    /**
+     * VMDファイルの読み込みを行いモーション情報を返す。
+     * @param source 入力ソース
+     * @return モーション情報
+     * @throws IOException 入力エラー
+     * @throws MmdFormatException VMDファイルフォーマットの異常を検出
+     */
+    public static VmdMotion load(MmdSource source)
+            throws IOException,
+                   MmdFormatException {
+        VmdParser parser = new VmdParser(source);
+
+        VmdMotion result = new VmdMotion();
+
+        BasicLoader basicBuilder       = new BasicLoader(result);
+        CameraLoader cameraBuilder     = new CameraLoader(result);
+        LightingLoader lightingBuilder = new LightingLoader(result);
+
+        parser.setBasicHandler(basicBuilder);
+        parser.setCameraHandler(cameraBuilder);
+        parser.setLightingHandler(lightingBuilder);
+
+        parser.parseVmd();
+
+        return result;
+    }
+
+}
diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/model/binio/package-info.java b/src/main/java/jp/sourceforge/mikutoga/vmd/model/binio/package-info.java
new file mode 100644 (file)
index 0000000..616da2a
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * package information for Javadoc
+ *
+ * License : The MIT License
+ * Copyright(c) 2011 MikuToga Partners
+ */
+
+/**
+ * 独自データモデル向けVMDモーションファイル(*.vmd)用入出力ライブラリ。
+ */
+
+package jp.sourceforge.mikutoga.vmd.model.binio;
+
+/* EOF */
diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/model/package-info.java b/src/main/java/jp/sourceforge/mikutoga/vmd/model/package-info.java
new file mode 100644 (file)
index 0000000..c8ae7fa
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * package information for Javadoc
+ *
+ * License : The MIT License
+ * Copyright(c) 2011 MikuToga Partners
+ */
+
+/**
+ * VMDモーションファイル内の各要素に相当するオブジェクト各種。
+ * <p>VMDを正確に記述するのが主目的。使い勝手は二の次。
+ */
+
+package jp.sourceforge.mikutoga.vmd.model;
+
+/* EOF */
diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/model/xml/VmdXmlExporter.java b/src/main/java/jp/sourceforge/mikutoga/vmd/model/xml/VmdXmlExporter.java
new file mode 100644 (file)
index 0000000..e1fcf04
--- /dev/null
@@ -0,0 +1,800 @@
+/*
+ * vmd-xml exporter
+ *
+ * License : The MIT License
+ * Copyright(c) 2011 MikuToga Partners
+ */
+
+package jp.sourceforge.mikutoga.vmd.model.xml;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.List;
+import jp.sourceforge.mikutoga.math.EulerYXZ;
+import jp.sourceforge.mikutoga.math.MkPos3D;
+import jp.sourceforge.mikutoga.math.MkQuat;
+import jp.sourceforge.mikutoga.typical.TypicalBone;
+import jp.sourceforge.mikutoga.typical.TypicalMorph;
+import jp.sourceforge.mikutoga.vmd.IllegalVmdDataException;
+import jp.sourceforge.mikutoga.vmd.VmdConst;
+import jp.sourceforge.mikutoga.vmd.model.BezierParam;
+import jp.sourceforge.mikutoga.vmd.model.BoneMotion;
+import jp.sourceforge.mikutoga.vmd.model.CameraMotion;
+import jp.sourceforge.mikutoga.vmd.model.CameraRotation;
+import jp.sourceforge.mikutoga.vmd.model.LuminousColor;
+import jp.sourceforge.mikutoga.vmd.model.LuminousMotion;
+import jp.sourceforge.mikutoga.vmd.model.LuminousVector;
+import jp.sourceforge.mikutoga.vmd.model.MorphMotion;
+import jp.sourceforge.mikutoga.vmd.model.NamedListMap;
+import jp.sourceforge.mikutoga.vmd.model.PosCurve;
+import jp.sourceforge.mikutoga.vmd.model.ShadowMode;
+import jp.sourceforge.mikutoga.vmd.model.ShadowMotion;
+import jp.sourceforge.mikutoga.vmd.model.VmdMotion;
+import jp.sourceforge.mikutoga.xml.BasicXmlExporter;
+import jp.sourceforge.mikutoga.xml.XmlResourceResolver;
+
+/**
+ * VMDモーションデータをXMLへエクスポートする。
+ */
+public class VmdXmlExporter extends BasicXmlExporter {
+
+    private static final String XSINS = "xsi";
+
+    private static final String TOP_COMMENT =
+            "  MikuMikuDance\n    motion-data(*.vmd) on XML";
+
+    private static final String QUATERNION_COMMENT =
+          "  bone-rotation has Quaternion parameters [boneRotQuat]\n"
+        + "  or YXZ-Euler angles [boneRotEyxz].\n"
+        + "  Quaternion is strongly recommended"
+        +   " if you are data-exchanging.";
+
+    private static final String BEZIER_COMMENT =
+          "  motion interpolation is defined by Bezier-cubic-curve.\n"
+        + "  implicit bezier curve point : P0=(0,0) P3=(127,127)\n"
+        + "  defLinear : MMD default linear curve."
+        + " P1=(20,20) P2=(107,107) [DEFAULT]\n"
+        + "  defEaseInOut : MMD default ease-in-out curve."
+        + " P1=(64,0) P2=(64,127)";
+
+    private static final String CAMERA_COMMENT =
+          "  camera-rotation has polar-coordinates parameters.\n"
+        + "  xRad = -radian(UI_X) [latitude]\n"
+        + "  yRad =  radian(UI_Y) [longitude]\n"
+        + "  zRad =  radian(UI_Z) [roll]\n"
+        + "  range = -(UI_RANGE)";
+
+    private static final String SHADOW_COMMENT =
+             "  UI_VALUE = EFFECTIVE_RANGE * 100 ???\n"
+            +"  rawParam = 0.1 - (UI_VALUE / 1.0E+5)\n\n"
+            +"  NONE   : no self-shadow\n"
+            +"  MODE_1 : reduce shadow-quality suddenly at range\n"
+            +"  MODE_2 : reduce shadow-quality gradually with range";
+
+
+    private boolean isQuaternionMode = true;
+    private String generator = "";
+
+
+    /**
+     * コンストラクタ。
+     * 文字エンコーディングはUTF-8が用いられる。
+     * @param stream 出力ストリーム
+     */
+    public VmdXmlExporter(OutputStream stream){
+        super(stream);
+        return;
+    }
+
+
+    /**
+     * ボーン回転量をクォータニオンで出力するか否か設定する。
+     * <p>デフォルトではtrue
+     * @param mode trueだとクォータニオン、falseだとオイラー角で出力される。
+     */
+    public void setQuaternionMode(boolean mode){
+        this.isQuaternionMode = mode;
+    }
+
+    /**
+     * Generatorメタ情報を設定する。
+     * @param generatorArg Generatorメタ情報
+     * @throws NullPointerException 引数がnull
+     */
+    public void setGenerator(String generatorArg)
+            throws NullPointerException{
+        if(generatorArg == null) throw new NullPointerException();
+        this.generator = generatorArg;
+        return;
+    }
+
+    /**
+     * VMDモーションデータをXML形式で出力する。
+     * <p>モーションデータと演出データで振り分けられる。
+     * @param vmdMotion VMDモーションデータ
+     * @throws IOException 出力エラー
+     * @throws IllegalVmdDataException 不正なモーションデータを検出
+     */
+    public void putVmdXml(VmdMotion vmdMotion)
+            throws IOException, IllegalVmdDataException{
+        try{
+            putVmdXmlImpl(vmdMotion);
+        }finally{
+            flush();
+        }
+        return;
+    }
+
+    /**
+     * VMDモーションデータをXML形式で出力する。
+     * <p>モーションデータと演出データで振り分けられる。
+     * @param vmdMotion VMDモーションデータ
+     * @throws IOException 出力エラー
+     * @throws IllegalVmdDataException 不正なモーションデータを検出
+     */
+    private void putVmdXmlImpl(VmdMotion vmdMotion)
+            throws IOException, IllegalVmdDataException{
+        ind().put("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>").ln(2);
+
+        ind().putBlockComment(TOP_COMMENT).ln(2);
+
+        ind().put("<vmdMotion").ln();
+        pushNest();
+        ind().putAttr("xmlns", VmdXmlResources.NS_VMDXML).ln();
+        ind().putAttr("xmlns:" + XSINS, XmlResourceResolver.NS_XSD).ln();
+
+        ind().put(XSINS).put(":schemaLocation=").put('"');
+        put(VmdXmlResources.NS_VMDXML).ln();
+        ind().sp(2).put(VmdXmlResources.SCHEMAURI_VMDXML).put('"').ln();
+
+        ind().putAttr("version", VmdXmlResources.VER_VMDXML).ln();
+        popNest();
+        put(">").ln(2);
+
+        if(this.generator != null && this.generator.length() > 0){
+            ind().put("<meta ");
+            putAttr("name", "generator").put(' ');
+            putAttr("content", this.generator);
+            put(" />").ln(2);
+        }
+
+        if(vmdMotion.isModelMotion()){
+            putModelName(vmdMotion);
+            putBoneMotionSequence(vmdMotion);
+            putMorphSequence(vmdMotion);
+        }else{
+            putCameraSequence(vmdMotion);
+            putLuminousSequence(vmdMotion);
+            putShadowSequence(vmdMotion);
+        }
+
+        ind().put("</vmdMotion>").ln(2);
+        ind().put("<!-- EOF -->").ln();
+
+        return;
+    }
+
+    /**
+     * 位置移動補間カーブを出力する。
+     * @param posCurve 移動補間情報
+     * @throws IOException 出力エラー
+     */
+    private void putPositionCurve(PosCurve posCurve)
+            throws IOException{
+        BezierParam xCurve = posCurve.getIntpltXpos();
+        BezierParam yCurve = posCurve.getIntpltYpos();
+        BezierParam zCurve = posCurve.getIntpltZpos();
+
+        ind().putLineComment("X-Y-Z interpolation *3").ln();
+
+        ind();
+        putBezierCurve(xCurve);
+        ln();
+
+        ind();
+        putBezierCurve(yCurve);
+        ln();
+
+        ind();
+        putBezierCurve(zCurve);
+        ln();
+
+        return;
+    }
+
+    /**
+     * ベジェ曲線による補間曲線情報を出力する。
+     * @param bezier ベジェ曲線
+     * @throws IOException 出力エラー
+     */
+    private void putBezierCurve(BezierParam bezier)
+            throws IOException{
+        if(bezier.isDefaultLinear()){
+            put("<defLinear />");
+        }else if(bezier.isDefaultEaseInOut()){
+            put("<defEaseInOut />");
+        }else{
+            put("<bezier ");
+            putIntAttr("p1x", bezier.getP1x()).sp();
+            putIntAttr("p1y", bezier.getP1y()).sp();
+            putIntAttr("p2x", bezier.getP2x()).sp();
+            putIntAttr("p2y", bezier.getP2y()).sp();
+            put("/>");
+        }
+        return;
+    }
+
+    /**
+     * モデル名を出力する。
+     * @param vmdMotion モーションデータ
+     * @throws IOException 出力エラー
+     */
+    private void putModelName(VmdMotion vmdMotion)
+            throws IOException{
+        String modelName = vmdMotion.getModelName();
+
+        ind().putLineComment(modelName).ln();
+        ind().put("<modelName ");
+        putAttr("name", modelName).sp();
+        put("/>").ln(2);
+
+        return;
+    }
+
+    /**
+     * ボーンモーションデータを出力する。
+     * @param vmdMotion モーションデータ
+     * @throws IOException 出力エラー
+     */
+    private void putBoneMotionSequence(VmdMotion vmdMotion)
+            throws IOException{
+        ind().putBlockComment(QUATERNION_COMMENT);
+        ind().putBlockComment(BEZIER_COMMENT);
+
+        ind().put("<boneMotionSequence>").ln();
+
+        pushNest();
+        NamedListMap<BoneMotion> listmap = vmdMotion.getBonePartMap();
+        if( ! listmap.isEmpty() ) ln();
+        for(String boneName : listmap.getNames()){
+            List<BoneMotion> list = listmap.getNamedList(boneName);
+            putBonePart(boneName, list);
+        }
+        popNest();
+
+        ind().put("</boneMotionSequence>").ln(2);
+
+        return;
+    }
+
+    /**
+     * ボーン別モーションデータを出力する。
+     * @param boneName ボーン名
+     * @param list ボーンモーションのリスト
+     * @throws IOException 出力エラー
+     */
+    private void putBonePart(String boneName, List<BoneMotion> list)
+            throws IOException{
+        ind().putLineComment(boneName);
+        String globalName = TypicalBone.primary2global(boneName);
+        if(globalName != null){
+            sp(2).putLineComment("Perhaps : [" + globalName + "]");
+        }
+        ln();
+
+        ind().put("<bonePart ");
+        putAttr("name", boneName).sp();
+        put(">").ln(2);
+
+        pushNest();
+        for(BoneMotion bone : list){
+            putBoneMotion(bone);
+        }
+        popNest();
+
+        ind().put("</bonePart>").ln(2);
+
+        return;
+    }
+
+    /**
+     * ボーンモーションを出力する。
+     * @param boneMotion ボーンモーション
+     * @throws IOException 出力エラー
+     */
+    private void putBoneMotion(BoneMotion boneMotion)
+            throws IOException{
+        ind().put("<boneMotion ");
+        int frameNo = boneMotion.getFrameNumber();
+        putIntAttr("frame", frameNo).sp();
+        put(">").ln();
+
+        pushNest();
+        putBonePosition(boneMotion);
+        if(this.isQuaternionMode){
+            putBoneRotQuat(boneMotion);
+        }else{
+            putBoneRotEyxz(boneMotion);
+        }
+        popNest();
+
+        ind().put("</boneMotion>").ln(2);
+
+        return;
+    }
+
+    /**
+     * ボーン位置を出力する。
+     * @param boneMotion ボーンモーション
+     * @throws IOException 出力エラー
+     */
+    private void putBonePosition(BoneMotion boneMotion)
+            throws IOException{
+        if(boneMotion.hasImplicitPosition()){
+            return;
+        }
+
+        ind().put("<bonePosition ");
+        MkPos3D position = boneMotion.getPosition();
+        float xPos = (float) position.getXpos();
+        float yPos = (float) position.getYpos();
+        float zPos = (float) position.getZpos();
+        putFloatAttr("xPos", xPos).sp();
+        putFloatAttr("yPos", yPos).sp();
+        putFloatAttr("zPos", zPos).sp();
+
+        PosCurve posCurve = boneMotion.getPosCurve();
+        if(posCurve.isDefaultLinear()){
+            put("/>").ln();
+        }else{
+            put(">").ln();
+
+            pushNest();
+            putPositionCurve(posCurve);
+            popNest();
+
+            ind().put("</bonePosition>").ln();
+        }
+
+        return;
+    }
+
+    /**
+     * ボーン回転を出力する。
+     * @param boneMotion ボーンモーション
+     * @throws IOException 出力エラー
+     */
+    private void putBoneRotQuat(BoneMotion boneMotion)
+            throws IOException{
+        MkQuat rotation = boneMotion.getRotation();
+        BezierParam rotCurve = boneMotion.getIntpltRotation();
+
+        ind().put("<boneRotQuat").ln();
+        pushNest();
+        ind().putFloatAttr("qx", (float) rotation.getQ1()).ln();
+        ind().putFloatAttr("qy", (float) rotation.getQ2()).ln();
+        ind().putFloatAttr("qz", (float) rotation.getQ3()).ln();
+        ind().putFloatAttr("qw", (float) rotation.getQW()).ln();
+        popNest();
+        ind();
+
+        if(rotCurve.isDefaultLinear()){
+            put("/>").ln();
+        }else{
+            put(">").ln();
+            pushNest();
+            ind();
+            putBezierCurve(rotCurve);
+            ln();
+            popNest();
+            ind().put("</boneRotQuat>").ln();
+        }
+
+        return;
+    }
+
+    /**
+     * ボーン回転を出力する。
+     * @param boneMotion ボーンモーション
+     * @throws IOException 出力エラー
+     */
+    private void putBoneRotEyxz(BoneMotion boneMotion)
+            throws IOException{
+        MkQuat rotation = boneMotion.getRotation();
+        BezierParam rotCurve = boneMotion.getIntpltRotation();
+
+        EulerYXZ euler = new EulerYXZ();
+        rotation.toEulerYXZ(euler);
+        float xDeg = (float)StrictMath.toDegrees(euler.getXRot());
+        float yDeg = (float)StrictMath.toDegrees(euler.getYRot());
+        float zDeg = (float)StrictMath.toDegrees(euler.getZRot());
+
+        ind().put("<boneRotEyxz").ln();
+        pushNest();
+        ind().putFloatAttr("xDeg", xDeg).ln();
+        ind().putFloatAttr("yDeg", yDeg).ln();
+        ind().putFloatAttr("zDeg", zDeg).ln();
+        popNest();
+        ind();
+
+        if(rotCurve.isDefaultLinear()){
+            put("/>").ln();
+        }else{
+            put(">").ln();
+            pushNest();
+            ind();
+            putBezierCurve(rotCurve);
+            ln();
+            popNest();
+            ind().put("</boneRotEyxz>").ln();
+        }
+
+        return;
+    }
+
+    /**
+     * モーフデータを出力する。
+     * @param vmdMotion モーションデータ
+     * @throws IOException 出力エラー
+     */
+    private void putMorphSequence(VmdMotion vmdMotion)
+            throws IOException{
+        ind().put("<morphSequence>").ln();
+
+        pushNest();
+        NamedListMap<MorphMotion> listmap = vmdMotion.getMorphPartMap();
+        if( ! listmap.isEmpty() ) ln();
+        putMorphPartList(listmap);
+        popNest();
+
+        ind().put("</morphSequence>").ln(2);
+
+        return;
+    }
+
+    /**
+     * 箇所別モーフデータを出力する。
+     * @param listmap モーフデータの名前付きリストマップ
+     * @throws IOException 出力エラー
+     */
+    private void putMorphPartList(NamedListMap<MorphMotion> listmap)
+            throws IOException{
+        for(String morphName : listmap.getNames()){
+            if(VmdConst.isBaseMorphName(morphName)) continue;
+
+            ind().putLineComment(morphName);
+            String globalName = TypicalMorph.primary2global(morphName);
+            if(globalName != null){
+                sp(2).putLineComment("Perhaps : [" + globalName + "]");
+            }
+            ln();
+
+            ind().put("<morphPart ");
+            putAttr("name", morphName).sp();
+            put(">").ln();
+
+            pushNest();
+            List<MorphMotion> list = listmap.getNamedList(morphName);
+            for(MorphMotion morph : list){
+                putMorphMotion(morph);
+            }
+            popNest();
+
+            ind().put("</morphPart>").ln(2);
+        }
+
+        return;
+    }
+
+    /**
+     * 個別のモーフモーションを出力する。
+     * @param morphMotion モーフモーション
+     * @throws IOException 出力エラー
+     */
+    private void putMorphMotion(MorphMotion morphMotion)
+            throws IOException{
+        ind().put("<morphMotion ");
+
+        int frameNo = morphMotion.getFrameNumber();
+        float flex = morphMotion.getFlex();
+
+        putIntAttr("frame", frameNo).sp();
+        putFloatAttr("flex", flex).sp();
+
+        put("/>").ln();
+
+        return;
+    }
+
+    /**
+     * カメラ演出データを出力する。
+     * @param vmdMotion 演出データ
+     * @throws IOException 出力エラー
+     */
+    private void putCameraSequence(VmdMotion vmdMotion)
+            throws IOException{
+        ind().putBlockComment(BEZIER_COMMENT);
+        ind().putBlockComment(CAMERA_COMMENT);
+
+        ind().put("<cameraSequence>").ln();
+
+        pushNest();
+        List<CameraMotion> list = vmdMotion.getCameraMotionList();
+        if( ! list.isEmpty() ) ln();
+        for(CameraMotion camera : list){
+            putCameraMotion(camera);
+        }
+        popNest();
+
+        ind().put("</cameraSequence>").ln(2);
+
+        return;
+    }
+
+    /**
+     * カメラモーションを出力する。
+     * @param cameraMotion カメラモーション
+     * @throws IOException 出力エラー
+     */
+    private void putCameraMotion(CameraMotion cameraMotion)
+            throws IOException{
+        ind().put("<cameraMotion ");
+        int frameNo = cameraMotion.getFrameNumber();
+        putIntAttr("frame", frameNo).sp();
+        if( ! cameraMotion.hasPerspective() ){
+            putAttr("hasPerspective", "false").sp();
+        }
+        put(">").ln();
+
+        pushNest();
+        putCameraTarget(cameraMotion);
+        putCameraRotation(cameraMotion);
+        putCameraRange(cameraMotion);
+        putProjection(cameraMotion);
+        popNest();
+
+        ind().put("</cameraMotion>").ln(2);
+
+        return;
+    }
+
+    /**
+     * カメラターゲット情報を出力する。
+     * @param cameraMotion カメラモーション
+     * @throws IOException 出力エラー
+     */
+    private void putCameraTarget(CameraMotion cameraMotion)
+            throws IOException{
+        ind().put("<cameraTarget ");
+        MkPos3D position = cameraMotion.getCameraTarget();
+        putFloatAttr("xPos", (float) position.getXpos()).sp();
+        putFloatAttr("yPos", (float) position.getYpos()).sp();
+        putFloatAttr("zPos", (float) position.getZpos()).sp();
+
+        PosCurve posCurve = cameraMotion.getTargetPosCurve();
+        if(posCurve.isDefaultLinear()){
+            put("/>").ln();
+        }else{
+            put(">").ln();
+
+            pushNest();
+            putPositionCurve(posCurve);
+            popNest();
+
+            ind().put("</cameraTarget>").ln();
+        }
+
+        return;
+    }
+
+    /**
+     * カメラ回転情報を出力する。
+     * @param cameraMotion カメラモーション
+     * @throws IOException 出力エラー
+     */
+    private void putCameraRotation(CameraMotion cameraMotion)
+            throws IOException{
+        ind().put("<cameraRotation ");
+        CameraRotation rotation = cameraMotion.getCameraRotation();
+        putFloatAttr("xRad", rotation.getLatitude()).sp();
+        putFloatAttr("yRad", rotation.getLongitude()).sp();
+        putFloatAttr("zRad", rotation.getRoll()).sp();
+
+        BezierParam rotCurve = cameraMotion.getIntpltRotation();
+        if(rotCurve.isDefaultLinear()){
+            put("/>").ln();
+        }else{
+            put(">").ln();
+            pushNest();
+            ind();
+            putBezierCurve(rotCurve);
+            ln();
+            popNest();
+            ind().put("</cameraRotation>").ln();
+        }
+
+        return;
+    }
+
+    /**
+     * カメラ距離情報を出力する。
+     * @param cameraMotion カメラモーション
+     * @throws IOException 出力エラー
+     */
+    private void putCameraRange(CameraMotion cameraMotion)
+            throws IOException{
+        ind().put("<cameraRange ");
+        float range = cameraMotion.getRange();
+        putFloatAttr("range", range).sp();
+
+        BezierParam rangeCurve = cameraMotion.getIntpltRange();
+        if(rangeCurve.isDefaultLinear()){
+            put("/>").ln();
+        }else{
+            put(">").ln();
+            pushNest();
+            ind();
+            putBezierCurve(rangeCurve);
+            ln();
+            popNest();
+            ind().put("</cameraRange>").ln();
+        }
+
+        return;
+    }
+
+    /**
+     * スクリーン投影情報を出力する。
+     * @param cameraMotion カメラモーション
+     * @throws IOException 出力エラー
+     */
+    private void putProjection(CameraMotion cameraMotion)
+            throws IOException{
+        ind().put("<projection ");
+        putIntAttr("vertDeg", cameraMotion.getProjectionAngle()).sp();
+
+        BezierParam projCurve = cameraMotion.getIntpltProjection();
+        if(projCurve.isDefaultLinear()){
+            put("/>").ln();
+        }else{
+            put(">").ln();
+            pushNest();
+            ind();
+            putBezierCurve(projCurve);
+            ln();
+            popNest();
+            ind().put("</projection>").ln();
+        }
+
+        return;
+    }
+
+    /**
+     * 照明演出データを出力する。
+     * @param vmdMotion 演出データ
+     * @throws IOException 出力エラー
+     */
+    private void putLuminousSequence(VmdMotion vmdMotion)
+            throws IOException{
+        ind().put("<luminousSequence>").ln();
+
+        pushNest();
+        List<LuminousMotion> list = vmdMotion.getLuminousMotionList();
+        if( ! list.isEmpty() ) ln();
+        for(LuminousMotion luminous : list){
+            putLuminousMotion(luminous);
+        }
+        popNest();
+
+        ind().put("</luminousSequence>").ln(2);
+
+        return;
+    }
+
+    /**
+     * 照明モーションを出力する。
+     * @param luminousMotion 照明モーション
+     * @throws IOException 出力エラー
+     */
+    private void putLuminousMotion(LuminousMotion luminousMotion)
+            throws IOException{
+        ind().put("<luminousAct ");
+        int frameNo = luminousMotion.getFrameNumber();
+        putIntAttr("frame", frameNo);
+        put(" >").ln();
+
+        LuminousColor color = luminousMotion.getColor();
+        LuminousVector vector = luminousMotion.getDirection();
+
+        pushNest();
+        putLuminousColor(color);
+        putLuminousDirection(vector);
+        popNest();
+
+        ind().put("</luminousAct>").ln(2);
+
+        return;
+    }
+
+    /**
+     * 光源色情報を出力する。
+     * @param color 光源色
+     * @throws IOException 出力エラー
+     */
+    private void putLuminousColor(LuminousColor color)
+            throws IOException{
+        ind().put("<lumiColor ");
+        putFloatAttr("rCol", color.getColR()).sp();
+        putFloatAttr("gCol", color.getColG()).sp();
+        putFloatAttr("bCol", color.getColB()).sp();
+        put("/>").ln();
+
+        return;
+    }
+
+    /**
+     * 照明方向情報を出力する。
+     * @param vector 照明方向
+     * @throws IOException 出力エラー
+     */
+    private void putLuminousDirection(LuminousVector vector)
+            throws IOException{
+        ind().put("<lumiDirection ");
+        putFloatAttr("xVec", vector.getVecX()).sp();
+        putFloatAttr("yVec", vector.getVecY()).sp();
+        putFloatAttr("zVec", vector.getVecZ()).sp();
+        put("/>").ln();
+
+        return;
+    }
+
+    /**
+     * シャドウ演出データを出力する。
+     * @param vmdMotion 演出データ
+     * @throws IOException 出力エラー
+     */
+    private void putShadowSequence(VmdMotion vmdMotion)
+            throws IOException{
+        ind().putBlockComment(SHADOW_COMMENT);
+
+        ind().put("<shadowSequence>").ln();
+
+        pushNest();
+        List<ShadowMotion> list = vmdMotion.getShadowMotionList();
+        for(ShadowMotion shadow : list){
+            putShadowMotion(shadow);
+        }
+        popNest();
+
+        ind().put("</shadowSequence>").ln(2);
+
+        return;
+    }
+
+    /**
+     * シャドウモーションを出力する。
+     * @param shadowMotion シャドウモーション
+     * @throws IOException 出力エラー
+     */
+    private void putShadowMotion(ShadowMotion shadowMotion)
+            throws IOException{
+        ind().put("<shadowAct ");
+
+        int frameNo = shadowMotion.getFrameNumber();
+        ShadowMode mode = shadowMotion.getShadowMode();
+        float rawParam = shadowMotion.getRawScopeParam();
+
+        putIntAttr("frame", frameNo).sp();
+        putAttr("mode", mode.name()).sp();
+        putFloatAttr("rawParam", rawParam).sp();
+
+        put("/>");
+
+        double uiVal = ShadowMotion.rawParamToScope(rawParam);
+        long lVal = Math.round(uiVal);
+        sp().putLineComment("UI:" + lVal).ln();
+
+        return;
+    }
+
+}
diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/model/xml/VmdXmlResources.java b/src/main/java/jp/sourceforge/mikutoga/vmd/model/xml/VmdXmlResources.java
new file mode 100644 (file)
index 0000000..b602ade
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * xml resources for VMD-XML
+ *
+ * License : The MIT License
+ * Copyright(c) 2011 MikuToga Partners
+ */
+
+package jp.sourceforge.mikutoga.vmd.model.xml;
+
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import javax.xml.XMLConstants;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+import jp.sourceforge.mikutoga.xml.XmlResourceResolver;
+import org.w3c.dom.ls.LSResourceResolver;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.SAXException;
+
+/**
+ * モーションデータ用各種XMLリソースの定義。
+ */
+public final class VmdXmlResources {
+
+    /** 定義の版数。 */
+    public static final String VER_VMDXML =
+            "110820";
+    /** XML名前空間識別子。 */
+    public static final String NS_VMDXML =
+            "http://mikutoga.sourceforge.jp/xml/ns/vmdxml/110820";
+    /** XMLスキーマURI名。 */
+    public static final String SCHEMAURI_VMDXML =
+            "http://mikutoga.sourceforge.jp/xml/xsd/vmdxml-110820.xsd";
+    /** ローカルなスキーマファイル名。 */
+    public static final String LOCAL_SCHEMA_VMDXML =
+            "resources/vmdxml-110820.xsd";
+
+    /** XMLスキーマURI。 */
+    public static final URI URI_SCHEMA_VMDXML;
+    /** XMLスキーマのローカルリソース。 */
+    public static final URI RES_SCHEMA_VMDXML;
+
+
+    private static final Class<?> THISCLASS = VmdXmlResources.class;
+
+
+    static{
+        try{
+            URI_SCHEMA_VMDXML = URI.create(SCHEMAURI_VMDXML);
+            RES_SCHEMA_VMDXML =
+                    THISCLASS.getResource(LOCAL_SCHEMA_VMDXML).toURI();
+        }catch(URISyntaxException e){
+            throw new ExceptionInInitializerError(e);
+        }
+
+        new VmdXmlResources().hashCode();
+    }
+
+
+    /**
+     * 隠しコンストラクタ。
+     */
+    private VmdXmlResources(){
+        super();
+        assert this.getClass().equals(THISCLASS);
+        return;
+    }
+
+
+    /**
+     * ビルダの生成。
+     * @param handler エラーハンドラ
+     * @return ビルダ
+     */
+    public static DocumentBuilder newBuilder(ErrorHandler handler){
+        XmlResourceResolver resolver = createResolver();
+
+        Schema schema = createSchema(resolver);
+
+        DocumentBuilderFactory builderFactory = createBuilderFactory();
+        builderFactory.setSchema(schema);
+
+        DocumentBuilder builder;
+        try{
+            builder = builderFactory.newDocumentBuilder();
+        }catch(ParserConfigurationException e){
+            assert false;
+            throw new AssertionError(e);
+        }
+        builder.setEntityResolver(resolver);
+        builder.setErrorHandler(handler);
+
+        return builder;
+    }
+
+    /**
+     * URI参照をローカルなリソースアクセスへとリダイレクトするリゾルバを生成する。
+     * @return リゾルバ
+     */
+    private static XmlResourceResolver createResolver(){
+        XmlResourceResolver resolver = new XmlResourceResolver();
+        resolver.putURIMap(URI_SCHEMA_VMDXML, RES_SCHEMA_VMDXML);
+//        resolver.putURIMap(URI_DTD_VMDXML, RES_DTD_VMDXML);
+
+        return resolver;
+    }
+
+    /**
+     * 検証用スキーマ(XML schema)を生成する。
+     * @param resolver リゾルバ
+     * @return スキーマ
+     */
+    private static Schema createSchema(LSResourceResolver resolver){
+        SchemaFactory schemaFactory =
+                SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+        schemaFactory.setResourceResolver(resolver);
+
+        URL localXsd;
+        try{
+            localXsd = RES_SCHEMA_VMDXML.toURL();
+        }catch(MalformedURLException e){
+            assert false;
+            throw new AssertionError(e);
+        }
+
+        Schema schema;
+        try{
+            schema = schemaFactory.newSchema(localXsd);
+        }catch(SAXException e){
+            assert false;
+            throw new AssertionError(e);
+        }
+
+        return schema;
+    }
+
+    /**
+     * DocumentBuilderFavtoryを生成する。
+     * @return ファクトリ
+     */
+    private static DocumentBuilderFactory createBuilderFactory(){
+        DocumentBuilderFactory builderFactory =
+                DocumentBuilderFactory.newInstance();
+
+        builderFactory.setNamespaceAware(true);
+        builderFactory.setValidating(false);    // DTD validation off
+        builderFactory.setIgnoringComments(true);
+        try{
+            builderFactory.setXIncludeAware(true);
+        }catch(UnsupportedOperationException e){
+            // NOTHING
+            assert true;
+        }
+
+        return builderFactory;
+    }
+
+}
diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/model/xml/Xml.java b/src/main/java/jp/sourceforge/mikutoga/vmd/model/xml/Xml.java
new file mode 100644 (file)
index 0000000..5bd1e72
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+ * xml common
+ *
+ * License : The MIT License
+ * Copyright(c) 2011 MikuToga Partners
+ */
+
+package jp.sourceforge.mikutoga.vmd.model.xml;
+
+import java.util.Iterator;
+import jp.sourceforge.mikutoga.vmd.model.BezierParam;
+import jp.sourceforge.mikutoga.vmd.model.PosCurve;
+import jp.sourceforge.mikutoga.xml.DomNsUtils;
+import jp.sourceforge.mikutoga.xml.SiblingElemIterator;
+import jp.sourceforge.mikutoga.xml.TogaXmlException;
+import org.w3c.dom.Element;
+
+/**
+ * XMLユーティリティ集。
+ * <p>VMDに特化したDomNsUtilsのWrapper群や共通要素の共通処理を含む。
+ * <p>要素に関する名前空間は{@link VmdXmlResources.NS_VMDXML}が暗黙で用いられる。
+ * <p>非グローバル属性に関する名前空間は{@NS_NULL}が暗黙で用いられる。
+ * @see jp.sourceforge.mikutoga.xml.DomNsUtils
+ */
+final class Xml {
+
+    /** 非グローバル属性用名前空間。 */
+    static final String NS_NULL = null;
+
+
+    /**
+     * 隠しコンストラクタ。
+     */
+    private Xml(){
+        assert false;
+        throw new AssertionError();
+    }
+
+
+    /**
+     * ローカル名が一致する要素か判定する。
+     * @param elem 要素
+     * @param localName ローカル名。
+     * @return ローカル名が一致する要素であればtrue
+     */
+    static boolean hasNsLocalNameElem(Element elem,
+                                      String localName ){
+        return DomNsUtils.hasNsLocalNameElem(elem,
+                                             VmdXmlResources.NS_VMDXML,
+                                             localName );
+    }
+
+    /**
+     * ローカル名に合致する最初の直下子要素を返す。
+     * @param parent 親要素
+     * @param localName 子要素名
+     * @return 最初の直下子要素。見つからなければnull。
+     */
+    static Element pickChild(Element parent, String localName){
+        return DomNsUtils.pickFirstChild(parent,
+                                         VmdXmlResources.NS_VMDXML,
+                                         localName );
+    }
+
+    /**
+     * ローカル名に合致する最初の直下子要素を返す。
+     * <p>見つからなければ例外を投げる。
+     * @param parent 親要素
+     * @param localName 子要素名
+     * @return 最初の直下子要素
+     * @throws TogaXmlException 1つも見つからなかった
+     */
+    static Element getChild(Element parent, String localName)
+            throws TogaXmlException{
+        return DomNsUtils.getFirstChild(parent,
+                                        VmdXmlResources.NS_VMDXML,
+                                        localName );
+    }
+
+    /**
+     * 指定された名前の子要素のforeachを返す。
+     * @param parent 親要素
+     * @param localName 子要素名
+     * @return 子要素のforeach
+     */
+    static Iterable<Element> eachChild(Element parent, String localName){
+        return DomNsUtils.getEachChild(parent,
+                                       VmdXmlResources.NS_VMDXML,
+                                       localName );
+    }
+
+    /**
+     * 要素からxsd:string型属性値を読み取る。
+     * @param elem 要素
+     * @param attrName 属性名
+     * @return 文字列
+     * @throws TogaXmlException 属性値が見つからなかった。
+     */
+    static String getStringAttr(Element elem, String attrName)
+            throws TogaXmlException{
+        return DomNsUtils.getStringAttrNS(elem, NS_NULL, attrName);
+    }
+
+    /**
+     * 要素からxsd:boolean型属性値を読み取る。
+     * @param elem 要素
+     * @param attrName 属性名
+     * @return 真ならtrue
+     * @throws TogaXmlException 属性値が見つからなかった。
+     */
+    static boolean getBooleanAttr(Element elem, String attrName)
+            throws TogaXmlException{
+        return DomNsUtils.getBooleanAttrNS(elem, NS_NULL, attrName);
+    }
+
+    /**
+     * 要素からxsd:integer型属性値を読み取る。
+     * @param elem 要素
+     * @param attrName 属性名
+     * @return int値
+     * @throws TogaXmlException 属性値が見つからなかった。
+     */
+    static int getIntegerAttr(Element elem, String attrName)
+            throws TogaXmlException{
+        return DomNsUtils.getIntegerAttrNS(elem, NS_NULL, attrName);
+    }
+
+    /**
+     * 要素から符号付きbyte型整数属性値を読み取る。
+     * @param elem 要素
+     * @param attrName 属性名
+     * @return byte値
+     * @throws TogaXmlException 属性値が見つからなかった。
+     */
+    static byte getByteAttr(Element elem, String attrName)
+            throws TogaXmlException{
+        int iVal = getIntegerAttr(elem, attrName);
+        byte result = (byte) iVal;
+        return result;
+    }
+
+    /**
+     * 要素からxsd:float型属性値を読み取る。
+     * @param elem 要素
+     * @param attrName 属性名
+     * @return float値
+     * @throws TogaXmlException 属性値が見つからなかった。
+     */
+    static float getFloatAttr(Element elem, String attrName)
+            throws TogaXmlException{
+        return DomNsUtils.getFloatAttrNS(elem, NS_NULL, attrName);
+    }
+
+    /**
+     * ベジェ曲線による補間カーブ記述を読み込む。
+     * @param elem defLinear,defEaseInOut,bezier要素のいずれか
+     * @param bezier ベジェ曲線
+     * @throws TogaXmlException 構文エラー
+     */
+    static void setBezier(Element elem, BezierParam bezier)
+            throws TogaXmlException{
+        byte p1x;
+        byte p1y;
+        byte p2x;
+        byte p2y;
+
+        if(hasNsLocalNameElem(elem, "defLinear")){
+            p1x = BezierParam.DEF_P1X;
+            p1y = BezierParam.DEF_P1Y;
+            p2x = BezierParam.DEF_P2X;
+            p2y = BezierParam.DEF_P2Y;
+        }else if(hasNsLocalNameElem(elem, "defEaseInOut")){
+            p1x = BezierParam.EIO_P1X;
+            p1y = BezierParam.EIO_P1Y;
+            p2x = BezierParam.EIO_P2X;
+            p2y = BezierParam.EIO_P2Y;
+        }else if(hasNsLocalNameElem(elem, "bezier")){
+            p1x = getByteAttr(elem, "p1x");
+            p1y = getByteAttr(elem, "p1y");
+            p2x = getByteAttr(elem, "p2x");
+            p2y = getByteAttr(elem, "p2y");
+        }else{
+            assert false;
+            throw new AssertionError();
+        }
+
+        bezier.setP1(p1x, p1y);
+        bezier.setP2(p2x, p2y);
+
+        return;
+    }
+
+    /**
+     * 補間カーブ情報を読み込む。
+     * @param parentElem 親要素
+     * @param bezier 補間カーブ
+     * @throws TogaXmlException 構文エラー
+     */
+    static void buildCurve(Element parentElem, BezierParam bezier)
+            throws TogaXmlException{
+        Element bezierElem = pickChild(parentElem, null);
+        if(bezierElem == null) return;
+
+        setBezier(bezierElem, bezier);
+
+        return;
+    }
+
+    /**
+     * 位置補間情報を読み込む。
+     * @param positionElem 親要素
+     * @param curve 位置補間カーブ
+     * @throws TogaXmlException 構文エラー
+     */
+    static void buildPosCurve(Element positionElem, PosCurve curve)
+            throws TogaXmlException{
+        Iterator<Element> it =
+                new SiblingElemIterator(positionElem,
+                                        VmdXmlResources.NS_VMDXML, null);
+
+        int ct = 0;
+        while(it.hasNext()){
+            Element curveElem = it.next();
+            BezierParam bz = curve.item(ct);
+            setBezier(curveElem, bz);
+            ct++;
+        }
+
+        assert ct == 0 || ct == 3;
+
+        return;
+    }
+
+}
diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/model/xml/Xml2VmdLoader.java b/src/main/java/jp/sourceforge/mikutoga/vmd/model/xml/Xml2VmdLoader.java
new file mode 100644 (file)
index 0000000..287ad25
--- /dev/null
@@ -0,0 +1,323 @@
+/*
+ * xml loader
+ *
+ * License : The MIT License
+ * Copyright(c) 2011 MikuToga Partners
+ */
+
+package jp.sourceforge.mikutoga.vmd.model.xml;
+
+import java.io.IOException;
+import java.text.MessageFormat;
+import javax.xml.parsers.DocumentBuilder;
+import jp.sourceforge.mikutoga.math.MkPos3D;
+import jp.sourceforge.mikutoga.math.MkQuat;
+import jp.sourceforge.mikutoga.vmd.model.BezierParam;
+import jp.sourceforge.mikutoga.vmd.model.BoneMotion;
+import jp.sourceforge.mikutoga.vmd.model.MorphMotion;
+import jp.sourceforge.mikutoga.vmd.model.NamedListMap;
+import jp.sourceforge.mikutoga.vmd.model.PosCurve;
+import jp.sourceforge.mikutoga.vmd.model.VmdMotion;
+import jp.sourceforge.mikutoga.xml.TogaXmlException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+/**
+ * XML形式でのモーションファイルを読み込む。
+ */
+public class Xml2VmdLoader {
+
+    private static final String ERRMSG_INVROOT =
+            "RootElem:[{0}] must be [vmdMotion]";
+
+
+    private final DocumentBuilder builder;
+
+
+    /**
+     * コンストラクタ。
+     * @param builder ビルダ
+     */
+    public Xml2VmdLoader(DocumentBuilder builder){
+        super();
+        assert builder.isNamespaceAware();
+        this.builder = builder;
+        return;
+    }
+
+
+    /**
+     * ルート要素の取得とチェックを行う。
+     * @param document XMLドキュメント
+     * @return ルート要素
+     * @throws TogaXmlException 不正なルート要素の検出
+     */
+    private static Element getRootElem(Document document)
+            throws TogaXmlException {
+        Element vmdMotionElem = document.getDocumentElement();
+        if( ! Xml.hasNsLocalNameElem(vmdMotionElem, "vmdMotion") ){
+            String message =
+                    MessageFormat.format(ERRMSG_INVROOT,
+                                         vmdMotionElem.getLocalName() );
+            throw new TogaXmlException(message);
+        }
+        return vmdMotionElem;
+    }
+
+    /**
+     * モーションのモデル名を読み込む。
+     * @param vmdMotionElem vmdMotion要素
+     * @param vmdMotion モーション
+     * @throws TogaXmlException 構文エラー
+     */
+    private static void buildModelName(Element vmdMotionElem,
+                                       VmdMotion vmdMotion)
+            throws TogaXmlException{
+        Element modelNameElem = Xml.getChild(vmdMotionElem, "modelName");
+        String modelName = Xml.getStringAttr(modelNameElem, "name");
+        vmdMotion.setModelName(modelName);
+        return;
+    }
+
+    /**
+     * ボーンシーケンスを読み込む。
+     * @param vmdMotionElem vmdMotion要素
+     * @param vmdMotion モーション
+     * @throws TogaXmlException 構文エラー
+     */
+    private static void buildBoneSeq(Element vmdMotionElem,
+                                     VmdMotion vmdMotion )
+            throws TogaXmlException{
+        NamedListMap<BoneMotion> boneMap = vmdMotion.getBonePartMap();
+
+        Element boneSeqElem =
+                Xml.getChild(vmdMotionElem, "boneMotionSequence");
+
+        for(Element bonePartElem : Xml.eachChild(boneSeqElem, "bonePart")){
+            buildBonePart(bonePartElem, boneMap);
+        }
+
+        return;
+    }
+
+    /**
+     * ボーンパートを読み込む。
+     * @param bonePartElem bonePart要素
+     * @param boneMap 名前マップ
+     * @throws TogaXmlException 構文エラー
+     */
+    private static void buildBonePart(Element bonePartElem,
+                                      NamedListMap<BoneMotion> boneMap )
+            throws TogaXmlException{
+        String boneName = Xml.getStringAttr(bonePartElem, "name");
+
+        for(Element boneMotionElem :
+                Xml.eachChild(bonePartElem, "boneMotion")){
+            BoneMotion boneMotion = buildBoneMotion(boneMotionElem);
+            boneMotion.setBoneName(boneName);
+            boneMap.addNamedElement(boneName, boneMotion);
+        }
+
+        return;
+    }
+
+    /**
+     * ボーンモーションを読み込む。
+     * @param boneMotionElem boneMotion要素
+     * @return ボーンモーション
+     * @throws TogaXmlException 構文エラー
+     */
+    private static BoneMotion buildBoneMotion(Element boneMotionElem)
+            throws TogaXmlException {
+        BoneMotion boneMotion = new BoneMotion();
+        int frameNo = Xml.getIntegerAttr(boneMotionElem, "frame");
+        boneMotion.setFrameNumber(frameNo);
+
+        buildBonePosition(boneMotionElem, boneMotion);
+        if(Xml.pickChild(boneMotionElem, "boneRotQuat") != null){
+            buildBoneRotQuat(boneMotionElem, boneMotion);
+        }else{
+            buildBoneRotEyxz(boneMotionElem, boneMotion);
+        }
+
+        return boneMotion;
+    }
+
+    /**
+     * ボーン位置を読み込む。
+     * @param boneMotionElem boneMotion要素
+     * @param boneMotion ボーンモーション
+     * @throws TogaXmlException 構文エラー
+     */
+    private static void buildBonePosition(Element boneMotionElem,
+                                          BoneMotion boneMotion )
+            throws TogaXmlException {
+        Element bonePositionElem =
+                Xml.pickChild(boneMotionElem, "bonePosition");
+        if(bonePositionElem == null) return;
+
+        MkPos3D position = boneMotion.getPosition();
+        float xPos = Xml.getFloatAttr(bonePositionElem, "xPos");
+        float yPos = Xml.getFloatAttr(bonePositionElem, "yPos");
+        float zPos = Xml.getFloatAttr(bonePositionElem, "zPos");
+        position.setXpos(xPos);
+        position.setYpos(yPos);
+        position.setZpos(zPos);
+
+        PosCurve curve = boneMotion.getPosCurve();
+        Xml.buildPosCurve(bonePositionElem, curve);
+
+        return;
+    }
+
+    /**
+     * ボーン回転をクォータニオン形式で読み込む。
+     * @param boneMotionElem boneMotion要素
+     * @param boneMotion ボーンモーション
+     * @throws TogaXmlException 構文エラー
+     */
+    private static void buildBoneRotQuat(Element boneMotionElem,
+                                          BoneMotion boneMotion )
+            throws TogaXmlException{
+        Element boneRotationElem =
+                Xml.getChild(boneMotionElem, "boneRotQuat");
+
+        MkQuat rotation = boneMotion.getRotation();
+        float qx = Xml.getFloatAttr(boneRotationElem, "qx");
+        float qy = Xml.getFloatAttr(boneRotationElem, "qy");
+        float qz = Xml.getFloatAttr(boneRotationElem, "qz");
+        float qw = Xml.getFloatAttr(boneRotationElem, "qw");
+
+        rotation.setQ1(qx);
+        rotation.setQ2(qy);
+        rotation.setQ3(qz);
+        rotation.setQW(qw);
+
+        BezierParam rotationCurve = boneMotion.getIntpltRotation();
+        Xml.buildCurve(boneRotationElem, rotationCurve);
+
+        return;
+    }
+
+    /**
+     * ボーン回転をオイラー角で読み込む。
+     * @param boneMotionElem boneMotion要素
+     * @param boneMotion ボーンモーション
+     * @throws TogaXmlException 構文エラー
+     */
+    private static void buildBoneRotEyxz(Element boneMotionElem,
+                                         BoneMotion boneMotion )
+            throws TogaXmlException{
+        Element boneRotationElem =
+                Xml.getChild(boneMotionElem, "boneRotEyxz");
+
+        MkQuat rotation = boneMotion.getRotation();
+
+        float xDeg = Xml.getFloatAttr(boneRotationElem, "xDeg");
+        float yDeg = Xml.getFloatAttr(boneRotationElem, "yDeg");
+        float zDeg = Xml.getFloatAttr(boneRotationElem, "zDeg");
+        float xRad = (float)StrictMath.toRadians(xDeg);
+        float yRad = (float)StrictMath.toRadians(yDeg);
+        float zRad = (float)StrictMath.toRadians(zDeg);
+        rotation.readEulerYXZ(xRad, yRad, zRad);
+
+        BezierParam rotationCurve = boneMotion.getIntpltRotation();
+        Xml.buildCurve(boneRotationElem, rotationCurve);
+
+        return;
+    }
+
+    /**
+     * モーフシーケンスを読み込む。
+     * @param vmdMotionElem vmdMotion要素
+     * @param vmdMotion モーション
+     * @throws TogaXmlException 構文エラー
+     */
+    private static void buildMorphSeq(Element vmdMotionElem,
+                                      VmdMotion vmdMotion )
+            throws TogaXmlException{
+        NamedListMap<MorphMotion> morphMap = vmdMotion.getMorphPartMap();
+
+        Element morphSeqElem = Xml.getChild(vmdMotionElem, "morphSequence");
+
+        for(Element morphPartElem :
+                Xml.eachChild(morphSeqElem, "morphPart")){
+            buildMorphPart(morphPartElem, morphMap);
+        }
+
+        return;
+    }
+
+    /**
+     * モーフパートを読み込む。
+     * @param morphPartElem morphPart要素
+     * @param morphMap 名前マップ
+     * @throws TogaXmlException 構文エラー
+     */
+    private static void buildMorphPart(Element morphPartElem,
+                                       NamedListMap<MorphMotion> morphMap )
+            throws TogaXmlException{
+        String morphName = Xml.getStringAttr(morphPartElem, "name");
+
+        Iterable<Element> childs =
+                Xml.eachChild(morphPartElem, "morphMotion");
+        for(Element morphMotionElem : childs){
+            MorphMotion morphMotion = buildMorphMotion(morphMotionElem);
+            morphMotion.setMorphName(morphName);
+            morphMap.addNamedElement(morphName, morphMotion);
+        }
+
+        return;
+    }
+
+    /**
+     * モーフモーションを読み込む。
+     * @param morphMotionElem morphMotion要素
+     * @return モーフモーション
+     * @throws TogaXmlException 構文エラー
+     */
+    private static MorphMotion buildMorphMotion(Element morphMotionElem)
+            throws TogaXmlException {
+        MorphMotion morphMotion = new MorphMotion();
+
+        int frameNo = Xml.getIntegerAttr(morphMotionElem, "frame");
+        float flex = Xml.getFloatAttr(morphMotionElem, "flex");
+
+        morphMotion.setFrameNumber(frameNo);
+        morphMotion.setFlex(flex);
+
+        return morphMotion;
+    }
+
+    /**
+     * XMLのパースを開始する。
+     * @param source XML入力
+     * @return モーションデータ
+     * @throws SAXException 構文エラー
+     * @throws IOException 入力エラー
+     * @throws TogaXmlException 構文エラー
+     */
+    public VmdMotion parse(InputSource source)
+            throws SAXException, IOException, TogaXmlException{
+        Document document = this.builder.parse(source);
+        Element vmdMotionElem = getRootElem(document);
+        VmdMotion vmdMotion = new VmdMotion();
+
+        // ignore <meta>
+
+        if(Xml.pickChild(vmdMotionElem, "modelName") != null){
+            buildModelName(vmdMotionElem, vmdMotion);
+            buildBoneSeq(vmdMotionElem, vmdMotion);
+            buildMorphSeq(vmdMotionElem, vmdMotion);
+        }else{
+            XmlCameraLoader.buildCameraSeq(vmdMotionElem, vmdMotion);
+            XmlLightingLoader.buildLuminousSeq(vmdMotionElem, vmdMotion);
+            XmlLightingLoader.buildShadowSeq(vmdMotionElem, vmdMotion);
+        }
+
+        return vmdMotion;
+    }
+
+}
diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/model/xml/XmlCameraLoader.java b/src/main/java/jp/sourceforge/mikutoga/vmd/model/xml/XmlCameraLoader.java
new file mode 100644 (file)
index 0000000..78a87f4
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * xml camera loader
+ *
+ * License : The MIT License
+ * Copyright(c) 2011 MikuToga Partners
+ */
+
+package jp.sourceforge.mikutoga.vmd.model.xml;
+
+import java.util.List;
+import jp.sourceforge.mikutoga.math.MkPos3D;
+import jp.sourceforge.mikutoga.vmd.model.BezierParam;
+import jp.sourceforge.mikutoga.vmd.model.CameraMotion;
+import jp.sourceforge.mikutoga.vmd.model.CameraRotation;
+import jp.sourceforge.mikutoga.vmd.model.PosCurve;
+import jp.sourceforge.mikutoga.vmd.model.VmdMotion;
+import jp.sourceforge.mikutoga.xml.TogaXmlException;
+import org.w3c.dom.Element;
+
+/**
+ * XMLによるカメラ制御データを読み取る。
+ */
+final class XmlCameraLoader {
+
+    /**
+     * 隠しコンストラクタ。
+     */
+    private XmlCameraLoader(){
+        assert false;
+        throw new AssertionError();
+    }
+
+
+    /**
+     * カメラシーケンスを読み込む。
+     * @param vmdMotionElem vmdMotion要素
+     * @param vmdMotion モーション
+     * @throws TogaXmlException 構文エラー
+     */
+    static void buildCameraSeq(Element vmdMotionElem, VmdMotion vmdMotion)
+            throws TogaXmlException{
+        List<CameraMotion> cameraList = vmdMotion.getCameraMotionList();
+
+        Element cameraSeqElem =
+                Xml.getChild(vmdMotionElem, "cameraSequence");
+
+        Iterable<Element> childs =
+                Xml.eachChild(cameraSeqElem, "cameraMotion");
+        for(Element cameraMotionElem : childs){
+            buildCameraMotion(cameraMotionElem, cameraList);
+        }
+
+        return;
+    }
+
+    /**
+     * カメラモーションを読み込む。
+     * @param cameraMotionElem cameraMotion要素
+     * @param cameraList カメラモーションリスト
+     * @throws TogaXmlException 構文エラー
+     */
+    private static void buildCameraMotion(Element cameraMotionElem,
+                                          List<CameraMotion> cameraList)
+            throws TogaXmlException{
+        CameraMotion cameraMotion = new CameraMotion();
+
+        int frameNo = Xml.getIntegerAttr(cameraMotionElem, "frame");
+        cameraMotion.setFrameNumber(frameNo);
+
+        if(cameraMotionElem.hasAttributeNS(Xml.NS_NULL, "hasPerspective")){
+            boolean hasPerspective =
+                    Xml.getBooleanAttr(cameraMotionElem, "hasPerspective");
+            cameraMotion.setPerspectiveMode(hasPerspective);
+        }
+
+        buildCameraTarget(cameraMotionElem, cameraMotion);
+        buildCameraRotation(cameraMotionElem, cameraMotion);
+        buildCameraRange(cameraMotionElem, cameraMotion);
+        buildProjection(cameraMotionElem, cameraMotion);
+
+        cameraList.add(cameraMotion);
+
+        return;
+    }
+
+    /**
+     * カメラターゲット情報を読み込む。
+     * @param cameraMotionElem cameraMotion要素
+     * @param cameraMotion カメラモーション
+     * @throws TogaXmlException 構文エラー
+     */
+    private static void buildCameraTarget(Element cameraMotionElem,
+                                          CameraMotion cameraMotion)
+            throws TogaXmlException{
+        Element cameraTargetElem =
+                Xml.getChild(cameraMotionElem, "cameraTarget");
+        MkPos3D targetPos = cameraMotion.getCameraTarget();
+
+        float xPos = Xml.getFloatAttr(cameraTargetElem, "xPos");
+        float yPos = Xml.getFloatAttr(cameraTargetElem, "yPos");
+        float zPos = Xml.getFloatAttr(cameraTargetElem, "zPos");
+        targetPos.setPosition(xPos, yPos, zPos);
+
+        PosCurve curve = cameraMotion.getTargetPosCurve();
+        Xml.buildPosCurve(cameraTargetElem, curve);
+
+        return;
+    }
+
+    /**
+     * カメラ回転情報を読み込む。
+     * @param cameraMotionElem cameraMotion要素
+     * @param cameraMotion カメラモーション
+     * @throws TogaXmlException 構文エラー
+     */
+    private static void buildCameraRotation(Element cameraMotionElem,
+                                            CameraMotion cameraMotion)
+            throws TogaXmlException{
+        Element cameraRotationElem =
+                Xml.getChild(cameraMotionElem, "cameraRotation");
+        CameraRotation cameraRotation = cameraMotion.getCameraRotation();
+
+        float latitude  = Xml.getFloatAttr(cameraRotationElem, "xRad");
+        float longitude = Xml.getFloatAttr(cameraRotationElem, "yRad");
+        float roll      = Xml.getFloatAttr(cameraRotationElem, "zRad");
+        cameraRotation.setLatitude(latitude);
+        cameraRotation.setLongitude(longitude);
+        cameraRotation.setRoll(roll);
+
+        BezierParam rotationCurve = cameraMotion.getIntpltRotation();
+        Xml.buildCurve(cameraRotationElem, rotationCurve);
+
+        return;
+    }
+
+    /**
+     * カメラ距離情報を読み込む。
+     * @param cameraMotionElem cameraMotion要素
+     * @param cameraMotion カメラモーション
+     * @throws TogaXmlException 構文エラー
+     */
+    private static void buildCameraRange(Element cameraMotionElem,
+                                         CameraMotion cameraMotion)
+            throws TogaXmlException{
+        Element cameraRangeElem =
+                Xml.getChild(cameraMotionElem, "cameraRange");
+
+        float range = Xml.getFloatAttr(cameraRangeElem, "range");
+        cameraMotion.setRange(range);
+
+        BezierParam rangeCurve = cameraMotion.getIntpltRange();
+        Xml.buildCurve(cameraRangeElem, rangeCurve);
+
+        return;
+    }
+
+    /**
+     * カメラ投影情報を読み込む。
+     * @param cameraMotionElem cameraMotion要素
+     * @param cameraMotion カメラモーション
+     * @throws TogaXmlException 構文エラー
+     */
+    private static void buildProjection(Element cameraMotionElem,
+                                        CameraMotion cameraMotion)
+            throws TogaXmlException{
+        Element projectionElem =
+                Xml.getChild(cameraMotionElem, "projection");
+
+        int vertDeg = Xml.getIntegerAttr(projectionElem, "vertDeg");
+        cameraMotion.setProjectionAngle(vertDeg);
+
+        BezierParam projCurve = cameraMotion.getIntpltProjection();
+        Xml.buildCurve(projectionElem, projCurve);
+
+        return;
+    }
+
+}
diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/model/xml/XmlLightingLoader.java b/src/main/java/jp/sourceforge/mikutoga/vmd/model/xml/XmlLightingLoader.java
new file mode 100644 (file)
index 0000000..49cb91b
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * xml lighting loader
+ *
+ * License : The MIT License
+ * Copyright(c) 2011 MikuToga Partners
+ */
+
+package jp.sourceforge.mikutoga.vmd.model.xml;
+
+import java.util.List;
+import jp.sourceforge.mikutoga.vmd.model.LuminousColor;
+import jp.sourceforge.mikutoga.vmd.model.LuminousMotion;
+import jp.sourceforge.mikutoga.vmd.model.LuminousVector;
+import jp.sourceforge.mikutoga.vmd.model.ShadowMode;
+import jp.sourceforge.mikutoga.vmd.model.ShadowMotion;
+import jp.sourceforge.mikutoga.vmd.model.VmdMotion;
+import jp.sourceforge.mikutoga.xml.TogaXmlException;
+import org.w3c.dom.Element;
+
+/**
+ * XMLによるカメラ制御データを読み取る。
+ */
+final class XmlLightingLoader {
+
+    /**
+     * 隠しコンストラクタ。
+     */
+    private XmlLightingLoader(){
+        assert false;
+        throw new AssertionError();
+    }
+
+
+    /**
+     * 照明シーケンスを読み込む。
+     * @param vmdMotionElem vmdMotion要素
+     * @param vmdMotion モーション
+     * @throws TogaXmlException 構文エラー
+     */
+    static void buildLuminousSeq(Element vmdMotionElem, VmdMotion vmdMotion)
+            throws TogaXmlException{
+        List<LuminousMotion> luminousList =
+                vmdMotion.getLuminousMotionList();
+
+        Element luminousSeqElem =
+                Xml.getChild(vmdMotionElem, "luminousSequence");
+
+        Iterable<Element> childs =
+                Xml.eachChild(luminousSeqElem, "luminousAct");
+        for(Element luminousActElem : childs){
+            buildLuminousAct(luminousActElem, luminousList);
+        }
+
+        return;
+    }
+
+    /**
+     * 照明モーションを読み込む。
+     * @param luminousActElem luminousAct要素
+     * @param luminousList 照明モーションリスト
+     * @throws TogaXmlException 構文エラー
+     */
+    private static void buildLuminousAct(Element luminousActElem,
+                                         List<LuminousMotion> luminousList)
+            throws TogaXmlException{
+        LuminousMotion luminousMotion = new LuminousMotion();
+
+        int frameNo = Xml.getIntegerAttr(luminousActElem, "frame");
+        luminousMotion.setFrameNumber(frameNo);
+
+        Element lumiColorElem = Xml.getChild(luminousActElem, "lumiColor");
+        LuminousColor color = luminousMotion.getColor();
+        float rCol = Xml.getFloatAttr(lumiColorElem, "rCol");
+        float gCol = Xml.getFloatAttr(lumiColorElem, "gCol");
+        float bCol = Xml.getFloatAttr(lumiColorElem, "bCol");
+        color.setColR(rCol);
+        color.setColG(gCol);
+        color.setColB(bCol);
+
+        Element lumiDirectionElem =
+                Xml.getChild(luminousActElem, "lumiDirection");
+        LuminousVector vec = luminousMotion.getDirection();
+        float xVec = Xml.getFloatAttr(lumiDirectionElem, "xVec");
+        float yVec = Xml.getFloatAttr(lumiDirectionElem, "yVec");
+        float zVec = Xml.getFloatAttr(lumiDirectionElem, "zVec");
+        vec.setVecX(xVec);
+        vec.setVecY(yVec);
+        vec.setVecZ(zVec);
+
+        luminousList.add(luminousMotion);
+
+        return;
+    }
+
+    /**
+     * シャドウシーケンスを読み込む。
+     * @param vmdMotionElem vmdMotion要素
+     * @param vmdMotion モーション
+     * @throws TogaXmlException 構文エラー
+     */
+    static void buildShadowSeq(Element vmdMotionElem,
+                               VmdMotion vmdMotion)
+            throws TogaXmlException{
+        List<ShadowMotion> shadowMotionList =
+                vmdMotion.getShadowMotionList();
+
+        Element shadowSeqElem =
+                Xml.getChild(vmdMotionElem, "shadowSequence");
+
+        for(Element shadowActElem :
+                Xml.eachChild(shadowSeqElem, "shadowAct")){
+            buildShadowAct(shadowActElem, shadowMotionList);
+        }
+
+        return;
+    }
+
+    /**
+     * シャドウモーションを読み込む。
+     * @param shadowActElem shadowAct要素
+     * @param shadowMotionList シャドウモーションリスト
+     * @throws TogaXmlException 構文エラー
+     */
+    private static void buildShadowAct(Element shadowActElem,
+                                       List<ShadowMotion> shadowMotionList)
+            throws TogaXmlException{
+        ShadowMotion shadowMotion = new ShadowMotion();
+
+        int frameNo = Xml.getIntegerAttr(shadowActElem, "frame");
+        shadowMotion.setFrameNumber(frameNo);
+
+        float rawParam = Xml.getFloatAttr(shadowActElem, "rawParam");
+        shadowMotion.setRawScopeParam(rawParam);
+
+        String modeAttr = Xml.getStringAttr(shadowActElem, "mode");
+        ShadowMode mode = ShadowMode.valueOf(modeAttr);
+        shadowMotion.setShadowMode(mode);
+
+        shadowMotionList.add(shadowMotion);
+
+        return;
+    }
+
+}
diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd/model/xml/package-info.java b/src/main/java/jp/sourceforge/mikutoga/vmd/model/xml/package-info.java
new file mode 100644 (file)
index 0000000..fba3b9c
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * package information for Javadoc
+ *
+ * License : The MIT License
+ * Copyright(c) 2011 MikuToga Partners
+ */
+
+/**
+ * VMDモーション独自データモデルとXML間でデータ交換を行うためのライブラリ。
+ */
+
+package jp.sourceforge.mikutoga.vmd.model.xml;
+
+/* EOF */
diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd2xml/ArgInfo.java b/src/main/java/jp/sourceforge/mikutoga/vmd2xml/ArgInfo.java
new file mode 100644 (file)
index 0000000..53dabe0
--- /dev/null
@@ -0,0 +1,312 @@
+/*
+ * command argument information
+ *
+ * License : The MIT License
+ * Copyright(c) 2011 MikuToga Partners
+ */
+
+package jp.sourceforge.mikutoga.vmd2xml;
+
+import java.util.Arrays;
+import java.util.Iterator;
+
+/**
+ * コマンドライン引数情報。
+ */
+final class ArgInfo {
+
+    /** ヘルプ文字列。 */
+    public static final String CMD_HELP =
+          "-h       : put help massage\n\n"
+        + "-vmd2xml : convert *.vmd to *.xml\n"
+        + "-xml2vmd : convert *.xml to *.vmd\n\n"
+        + "-i file  : specify input file\n"
+        + "-o file  : specify output file\n"
+        + "-f       : force overwriting\n\n"
+        + "-quat    : Quaternion output mode [default]\n"
+        + "-eyxz    : YXZ-Euler output mode\n";
+
+    private static final String ERRMSG_UNKNOWN = "Unknown option : ";
+    private static final String ERRMSG_INCOMP_I = "You need -i argument.";
+    private static final String ERRMSG_INCOMP_O = "You need -o argument.";
+    private static final String ERRMSG_EXCLUSIVE =
+            "You must specify -vmd2xml or -xml2vmd.";
+    private static final String ERRMSG_NOINPUT =
+            "You must specify input file with -i.";
+    private static final String ERRMSG_NOOUTPUT =
+            "You must specify output file with -o.";
+
+
+    private String inputFile = null;
+    private String outputFile = null;
+    private boolean vmd2xml = false;
+    private boolean xml2vmd = false;
+    private boolean force = false;
+    private boolean helpMode = false;
+    private boolean quaternionMode = true;
+
+    private boolean hasOptionError = false;
+    private String errorMessage = null;
+
+
+    /**
+     * コンストラクタ。
+     */
+    private ArgInfo(){
+        super();
+        return;
+    }
+
+
+    /**
+     * コマンドライン引数を解析する。
+     * @param args コマンドライン引数
+     * @return 解析結果
+     */
+    public static ArgInfo buildArgInfo(String[] args){
+        return buildArgInfo(Arrays.asList(args));
+    }
+
+    /**
+     * コマンドライン引数を解析する。
+     * @param args コマンドライン引数
+     * @return 解析結果
+     */
+    public static ArgInfo buildArgInfo(Iterable<String> args){
+        return buildArgInfo(args.iterator());
+    }
+
+    /**
+     * コマンドライン引数を解析する。
+     * @param argIter コマンドライン引数
+     * @return 解析結果
+     */
+    public static ArgInfo buildArgInfo(Iterator<String> argIter){
+        ArgInfo result = new ArgInfo();
+
+        while(argIter.hasNext()){
+            String arg = argIter.next();
+            OptSwitch option = OptSwitch.parse(arg);
+            if(option == null){
+                result.setOptionError(ERRMSG_UNKNOWN + arg);
+                break;
+            }
+
+            result.parseOption(option, argIter);
+
+            if(result.hasOptionError()) break;
+            if(result.isHelpMode()) break;
+        }
+
+        result.checkOptionError();
+
+        return result;
+    }
+
+    /**
+     * 単一もしくは引数を持つオプション種別を解析する。
+     * @param argIter コマンドライン引数
+     * @param option オプション種別
+     */
+    private void parseOption(OptSwitch option, Iterator<String> argIter){
+        switch(option){
+        case HELP:
+            this.helpMode = true;
+            break;
+        case VMD2XML:
+            this.vmd2xml = true;
+            this.xml2vmd = false;
+            break;
+        case XML2VMD:
+            this.vmd2xml = false;
+            this.xml2vmd = true;
+            break;
+        case FORCE:
+            this.force = true;
+            break;
+        case INPUTFILE:
+            if( ! argIter.hasNext() ){
+                this.setOptionError(ERRMSG_INCOMP_I);
+                break;
+            }
+            this.inputFile = argIter.next();
+            break;
+        case OUTPUTFILE:
+            if( ! argIter.hasNext() ){
+                this.setOptionError(ERRMSG_INCOMP_O);
+                break;
+            }
+            this.outputFile = argIter.next();
+            break;
+        case QUATERNION:
+            this.quaternionMode = true;
+            break;
+        case EULERYXZ:
+            this.quaternionMode = false;
+            break;
+        default:
+            assert false;
+            throw new AssertionError();
+        }
+
+        return;
+    }
+
+    /**
+     * オプション指定の整合性をチェックする。
+     * <p>必要に応じてエラーの有無とメッセージが登録される。
+     */
+    private void checkOptionError(){
+        if( hasOptionError() || isHelpMode() ) return;
+
+        if( ( ! isVmd2XmlMode() ) && ( ! isXml2VmdMode() ) ){
+            setOptionError(ERRMSG_EXCLUSIVE);
+        }else if(getInputFile() == null){
+            setOptionError(ERRMSG_NOINPUT);
+        }else if(getOutputFile() == null){
+            setOptionError(ERRMSG_NOOUTPUT);
+        }
+
+        return;
+    }
+
+    /**
+     * オプションエラーを設定する。
+     * @param text メッセージ
+     */
+    private void setOptionError(CharSequence text){
+        if(text == null){
+            this.errorMessage = null;
+            this.hasOptionError = false;
+        }else{
+            this.errorMessage = text.toString();
+            this.hasOptionError = true;
+        }
+
+        return;
+    }
+
+    /**
+     * 入力ファイル名を返す。
+     * @return 入力ファイル名
+     */
+    public String getInputFile(){
+        return this.inputFile;
+    }
+
+    /**
+     * 出力ファイル名を返す。
+     * @return 出力ファイル名
+     */
+    public String getOutputFile(){
+        return this.outputFile;
+    }
+
+    /**
+     * 強制上書きモードか判定を返す。
+     * @return 強制上書きモードならtrue
+     */
+    public boolean isForceMode(){
+        return this.force;
+    }
+
+    /**
+     * VMD-XML変換モードか判定を返す。
+     * @return VMD-XML変換モードならtrue
+     */
+    public boolean isVmd2XmlMode(){
+        return this.vmd2xml;
+    }
+
+    /**
+     * XML-VMD変換モードか判定を返す。
+     * @return XML-VMD変換モードならtrue
+     */
+    public boolean isXml2VmdMode(){
+        return this.xml2vmd;
+    }
+
+    /**
+     * ヘルプモードか否か判定を返す。
+     * @return ヘルプモードならtrue
+     */
+    public boolean isHelpMode(){
+        return this.helpMode;
+    }
+
+    /**
+     * クォータニオン出力モードか否か判定を返す。
+     * @return クォータニオン出力モードならtrue
+     */
+    public boolean isQuaternionMode(){
+        return this.quaternionMode;
+    }
+
+    /**
+     * コマンドライン解析中にエラーが検出されたか判定する。
+     * @return エラーが検出されればtrue
+     */
+    public boolean hasOptionError(){
+        return this.hasOptionError;
+    }
+
+    /**
+     * コマンドライン解析中のエラーメッセージを返す。
+     * @return エラーメッセージ。エラーが無ければnull
+     */
+    public String getErrorMessage(){
+        return this.errorMessage;
+    }
+
+    /**
+     * オプション列挙子。
+     */
+    private static enum OptSwitch implements Iterable<String> {
+
+        HELP       ("-h", "-help", "-?"),
+        VMD2XML    ("-vmd2xml"),
+        XML2VMD    ("-xml2vmd"),
+        FORCE      ("-f"),
+        INPUTFILE  ("-i"),
+        OUTPUTFILE ("-o"),
+        QUATERNION ("-quat"),
+        EULERYXZ   ("-eyxz"),
+        ;
+
+        private final Iterable<String> optLines;
+
+        /**
+         * コンストラクタ。
+         * @param optLines オプション文字列候補群。
+         */
+        private OptSwitch(String... optLines){
+            this.optLines = Arrays.asList(optLines);
+            return;
+        }
+
+        /**
+         * 文字列候補群を返す。
+         * @return 文字列候補群
+         */
+        @Override
+        public Iterator<String> iterator(){
+            return this.optLines.iterator();
+        }
+
+        /**
+         * 文字列に合致する列挙子を返す。
+         * @param arg 文字列
+         * @return 列挙子。見つからなければnull
+         */
+        private static OptSwitch parse(String arg){
+            for(OptSwitch opt : values()){
+                for(String optLine : opt){
+                    if(optLine.equals(arg)) return opt;
+                }
+            }
+            return null;
+        }
+
+    }
+
+}
diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd2xml/ValidationHandler.java b/src/main/java/jp/sourceforge/mikutoga/vmd2xml/ValidationHandler.java
new file mode 100644 (file)
index 0000000..5174077
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * custom error-handler for validation
+ *
+ * License : The MIT License
+ * Copyright(c) 2011 MikuToga Partners
+ */
+
+package jp.sourceforge.mikutoga.vmd2xml;
+
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+/**
+ * 妥当性検証用エラーハンドラ。
+ * <p>XML SchemaによるXML文書検証を目的とするエラーハンドラ。
+ */
+final class ValidationHandler implements ErrorHandler {
+
+    /**
+     * 唯一のシングルトン。
+     */
+    static final ErrorHandler HANDLER = new ValidationHandler();
+
+    /**
+     * 隠しコンストラクタ。
+     */
+    private ValidationHandler(){
+        super();
+        return;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param exception {@inheritDoc}
+     * @throws SAXException {@inheritDoc}
+     */
+    @Override
+    public void error(SAXParseException exception) throws SAXException{
+        throw exception;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param exception {@inheritDoc}
+     * @throws SAXException {@inheritDoc}
+     */
+    @Override
+    public void fatalError(SAXParseException exception) throws SAXException{
+        throw exception;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param exception {@inheritDoc}
+     * @throws SAXException {@inheritDoc}
+     */
+    @Override
+    public void warning(SAXParseException exception) throws SAXException{
+        throw exception;
+    }
+
+}
diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd2xml/Vmd2Xml.java b/src/main/java/jp/sourceforge/mikutoga/vmd2xml/Vmd2Xml.java
new file mode 100644 (file)
index 0000000..41ad213
--- /dev/null
@@ -0,0 +1,533 @@
+/*
+ * vmd to xml converter main entry
+ *
+ * License : The MIT License
+ * Copyright(c) 2011 MikuToga Partners
+ */
+
+package jp.sourceforge.mikutoga.vmd2xml;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.text.MessageFormat;
+import java.util.Properties;
+import javax.xml.parsers.DocumentBuilder;
+import jp.sourceforge.mikutoga.binio.FileUtils;
+import jp.sourceforge.mikutoga.parser.MmdFormatException;
+import jp.sourceforge.mikutoga.parser.MmdSource;
+import jp.sourceforge.mikutoga.vmd.IllegalVmdDataException;
+import jp.sourceforge.mikutoga.vmd.model.VmdMotion;
+import jp.sourceforge.mikutoga.vmd.model.binio.VmdExporter;
+import jp.sourceforge.mikutoga.vmd.model.binio.VmdLoader;
+import jp.sourceforge.mikutoga.vmd.model.xml.VmdXmlExporter;
+import jp.sourceforge.mikutoga.vmd.model.xml.VmdXmlResources;
+import jp.sourceforge.mikutoga.vmd.model.xml.Xml2VmdLoader;
+import jp.sourceforge.mikutoga.xml.TogaXmlException;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+
+/**
+ * VMDモーションファイルとXMLとの間で変換を行うアプリケーション。
+ */
+public final class Vmd2Xml {
+
+    private static final Class<?> THISCLASS;
+    private static final String APPNAME;
+    private static final String APPVER;
+    private static final String APPLICENSE;
+    private static final String APPURL;
+
+    private static final int EXIT_OK     = 0;
+    private static final int EXIT_IOERR  = 1;
+    private static final int EXIT_ENVERR = 4;
+    private static final int EXIT_OPTERR = 5;
+    private static final int EXIT_INTERR = 6;
+    private static final int EXIT_VMDERR = 7;
+    private static final int EXIT_XMLERR = 8;
+
+    private static final String ERRMSG_TXTONLY =
+            "ERROR : {0}\n";
+    private static final String ERRMSG_SAXPARSE =
+            "ERROR : {0}\nline={1}, columun={2}\n";
+
+    static{
+        THISCLASS = Vmd2Xml.class;
+        InputStream verDef =
+                THISCLASS.getResourceAsStream("resources/version.properties");
+        Properties verProps = new Properties();
+        try{
+            try{
+                verProps.load(verDef);
+            }finally{
+                verDef.close();
+            }
+        }catch(IOException e){
+            throw new ExceptionInInitializerError(e);
+        }
+
+        APPNAME    = verProps.getProperty("app.name");
+        APPVER     = verProps.getProperty("app.version");
+        APPLICENSE = verProps.getProperty("app.license");
+        APPURL     = verProps.getProperty("app.url");
+
+        new Vmd2Xml().hashCode();
+    }
+
+
+    /**
+     * 隠しコンストラクタ。
+     */
+    private Vmd2Xml(){
+        super();
+        assert this.getClass().equals(THISCLASS);
+        return;
+    }
+
+
+    /**
+     * Mainエントリ。
+     * @param args コマンドパラメータ
+     */
+    public static void main(String[] args){
+        checkJRE();
+
+        ArgInfo argInfo = ArgInfo.buildArgInfo(args);
+        if(argInfo.hasOptionError()){
+            String message = argInfo.getErrorMessage() + "\n"
+                    + "(-h for help)\n";
+            errExit(EXIT_OPTERR, message);
+        }
+
+        if(argInfo.isHelpMode()){
+            putHelp();
+            exit(EXIT_OK);
+        }
+
+        checkFiles(argInfo);
+
+        String input  = argInfo.getInputFile();
+        String output = argInfo.getOutputFile();
+
+        InputStream is = null;
+        OutputStream os = null;
+        try{
+            is = openInputStream(input);
+            os = openTruncatedOutputStream(output);
+        }catch(FileNotFoundException e){
+            ioError(e);
+        }catch(IOException e){
+            ioError(e);
+        }
+
+        if(argInfo.isVmd2XmlMode()) vmd2xml(is, os, argInfo);
+        else                        xml2vmd(is, os);
+
+        exit(EXIT_OK);
+
+        return;
+    }
+
+    /**
+     * JREのバージョン判定を行う。
+     * 不適切ならVMごと終了。
+     */
+    private static void checkJRE(){
+        Package jrePackage = java.lang.Object.class.getPackage();
+        if( ! jrePackage.isCompatibleWith("1.6")){
+            errExit(EXIT_ENVERR, "You need JRE 1.6 or later.\n");
+        }
+        return;
+    }
+
+    /**
+     * ヘルプメッセージを出力する。
+     */
+    private static void putHelp(){
+        StringBuilder text = new StringBuilder();
+
+        text.append(APPNAME).append(' ').append(APPVER).append('\n');
+        text.append("  License : ").append(APPLICENSE).append('\n');
+        text.append("  ").append(APPURL).append('\n');
+        text.append('\n');
+        text.append(ArgInfo.CMD_HELP);
+
+        errprint(text);
+
+        return;
+    }
+
+    /**
+     * ファイルの各種状態を事前にチェックする。
+     * @param argInfo コマンドライン引数
+     */
+    private static void checkFiles(ArgInfo argInfo){
+        String input  = argInfo.getInputFile();
+        String output = argInfo.getOutputFile();
+        File iFile = new File(input);
+        File oFile = new File(output);
+
+        if( ! FileUtils.isExistsNormalFile(iFile) ){
+            errExit(EXIT_IOERR, "Can't find input file:"
+                    + iFile.getAbsolutePath() + '\n');
+        }
+
+        if(argInfo.isForceMode()){
+            if(FileUtils.isExistsUnnormalFile(oFile)){
+                errExit(EXIT_IOERR, oFile.getAbsolutePath()
+                        + " is not file.\n");
+            }
+        }else if(oFile.exists()){
+            errExit(EXIT_IOERR, oFile.getAbsolutePath()
+                    + " already exists.\n"
+                    + "If you want to overwrite, use -f.\n");
+        }
+
+        return;
+    }
+
+    /**
+     * VMD->XML変換を行う。
+     * @param istream 入力ストリーム
+     * @param ostream 出力ストリーム
+     * @param argInfo オプション設定
+     */
+    private static void vmd2xml(InputStream istream, OutputStream ostream,
+                                ArgInfo argInfo ){
+        try{
+            vmd2xmlImpl(istream, ostream, argInfo);
+        }catch(IOException e){
+            ioError(e);
+        }catch(MmdFormatException e){
+            vmdError(e);
+        }catch(IllegalVmdDataException e){
+            internalError(e);
+        }
+
+        return;
+    }
+
+    /**
+     * VMD->XML変換を行う。
+     * @param istream 入力ストリーム
+     * @param ostream 出力ストリーム
+     * @param argInfo オプション設定
+     * @throws IOException 入出力エラー
+     * @throws MmdFormatException 不正なVMDファイル
+     * @throws IllegalVmdDataException 不正なモーションデータ
+     */
+    private static void vmd2xmlImpl(InputStream istream, OutputStream ostream,
+                                    ArgInfo argInfo )
+            throws IOException, MmdFormatException, IllegalVmdDataException{
+        VmdMotion motion;
+        try{
+            motion = vmdRead(istream);
+        }finally{
+            istream.close();
+        }
+
+        motion.frameSort();
+
+        try{
+            xmlOut(motion, ostream, argInfo);
+        }finally{
+            ostream.close();
+        }
+
+        return;
+    }
+
+    /**
+     * XML->VMD変換を行う。
+     * @param istream 入力ストリーム
+     * @param ostream 出力ストリーム
+     */
+    private static void xml2vmd(InputStream istream, OutputStream ostream){
+        try{
+            xml2vmdImpl(istream, ostream);
+        }catch(IOException e){
+            ioError(e);
+        }catch(SAXException e){
+            xmlError(e);
+        }catch(TogaXmlException e){
+            xmlError(e);
+        }catch(IllegalVmdDataException e){
+            internalError(e);
+        }
+
+        return;
+    }
+
+    /**
+     * XML->VMD変換を行う。
+     * @param istream 入力ストリーム
+     * @param ostream 出力ストリーム
+     * @throws IOException 入出力エラー
+     * @throws SAXException 不正なXMLファイル
+     * @throws TogaXmlException 不正なXMLファイル
+     * @throws IllegalVmdDataException 不正なVMDモーションデータ
+     */
+    private static void xml2vmdImpl(InputStream istream, OutputStream ostream)
+            throws IOException,
+                   SAXException,
+                   TogaXmlException,
+                   IllegalVmdDataException {
+        InputSource source = new InputSource(istream);
+        VmdMotion motion;
+        try{
+            motion = xmlRead(source);
+        }finally{
+            istream.close();
+        }
+
+        motion.frameSort();
+
+        try{
+            vmdOut(motion, ostream);
+        }finally{
+            ostream.close();
+        }
+
+        return;
+    }
+
+    /**
+     * VMDファイルからモーションデータを読み込む。
+     * @param is 入力ストリーム
+     * @return モーションデータ
+     * @throws IOException 入力エラー
+     * @throws MmdFormatException 不正なVMDファイルフォーマット
+     */
+    private static VmdMotion vmdRead(InputStream is)
+            throws IOException, MmdFormatException{
+        MmdSource source = new MmdSource(is);
+        VmdMotion vmdMotion = VmdLoader.load(source);
+        return vmdMotion;
+    }
+
+    /**
+     * モーションデータをXMLファイルに出力する。
+     * @param motion モーションデータ
+     * @param ostream 出力ストリーム
+     * @param argInfo オプション設定
+     * @throws IOException 出力エラー
+     * @throws IllegalVmdDataException 不正なモーションデータ
+     */
+    private static void xmlOut(VmdMotion motion, OutputStream ostream,
+                               ArgInfo argInfo)
+            throws IOException, IllegalVmdDataException{
+        VmdXmlExporter exporter = new VmdXmlExporter(ostream);
+
+        exporter.setNewLine("\r\n");
+        exporter.setGenerator(APPNAME + ' ' + APPVER);
+
+        boolean isQuaternionMode = argInfo.isQuaternionMode();
+        exporter.setQuaternionMode(isQuaternionMode);
+
+        exporter.putVmdXml(motion);
+        exporter.close();
+
+        return;
+    }
+
+    /**
+     * XMLファイルからモーションデータを読み込む。
+     * @param source 入力ソース
+     * @return モーションデータ
+     * @throws IOException 入力エラー
+     * @throws SAXException XML構文エラー
+     * @throws TogaXmlException 不正なXMLデータ
+     */
+    private static VmdMotion xmlRead(InputSource source)
+            throws IOException,
+                   SAXException,
+                   TogaXmlException {
+        DocumentBuilder builder =
+                VmdXmlResources.newBuilder(ValidationHandler.HANDLER);
+        Xml2VmdLoader loader = new Xml2VmdLoader(builder);
+
+        VmdMotion motion = loader.parse(source);
+
+        return motion;
+    }
+
+    /**
+     * モーションデータをVMDファイルに出力する。
+     * @param motion モーションデータ
+     * @param ostream 出力ストリーム
+     * @throws IOException 出力エラー
+     * @throws IllegalVmdDataException 不正なモーションデータ
+     */
+    private static void vmdOut(VmdMotion motion, OutputStream ostream)
+            throws IOException, IllegalVmdDataException{
+        VmdExporter exporter = new VmdExporter(ostream);
+        exporter.dumpVmdMotion(motion);
+        ostream.close();
+        return;
+    }
+
+    /**
+     * 入力ストリームを得る。
+     * @param fileName 入力ファイル名
+     * @return 入力ストリーム
+     * @throws FileNotFoundException 入力ファイルが見つからない。
+     */
+    private static InputStream openInputStream(String fileName)
+            throws FileNotFoundException {
+        File file = new File(fileName);
+        InputStream result = new FileInputStream(file);
+        result = new BufferedInputStream(result);
+        return result;
+    }
+
+    /**
+     * 出力ストリームを得る。
+     * @param fileName 出力ファイル名
+     * @return 出力ストリーム
+     * @throws FileNotFoundException 出力ファイルが見つからない
+     * @throws IOException 出力エラー
+     */
+    private static OutputStream openTruncatedOutputStream(String fileName)
+            throws FileNotFoundException, IOException {
+        File file = new File(fileName);
+        FileUtils.trunc(file);
+        OutputStream result = new FileOutputStream(file, false);
+        result = new BufferedOutputStream(result);
+        return result;
+    }
+
+    /**
+     * 入出力エラー処理。
+     * 例外を出力してVM終了する。
+     * @param ex 例外
+     */
+    private static void ioError(IOException ex){
+        errprint(ex);
+        errprint('\n');
+        exit(EXIT_IOERR);
+
+        return;
+    }
+
+    /**
+     * 内部エラー処理。
+     * 例外を出力してVM終了する。
+     * @param ex 例外
+     */
+    private static void internalError(Throwable ex){
+        errprint(ex);
+        errprint('\n');
+        ex.printStackTrace(System.err);
+        exit(EXIT_INTERR);
+
+        return;
+    }
+
+    /**
+     * VMDファイルフォーマットエラー処理。
+     * 例外を出力してVM終了する。
+     * @param ex 例外
+     */
+    private static void vmdError(MmdFormatException ex){
+        errprint(ex);
+        errprint('\n');
+        ex.printStackTrace(System.err);
+        exit(EXIT_VMDERR);
+
+        return;
+    }
+
+    /**
+     * XML構文エラー処理。
+     * 例外を出力してVM終了する。
+     * @param ex 例外
+     */
+    private static void xmlError(SAXException ex){
+        if(ex instanceof SAXParseException){
+            xmlError((SAXParseException)ex);
+        }
+
+        String txt = ex.getLocalizedMessage();
+        String message = MessageFormat.format(ERRMSG_TXTONLY, txt);
+        errprint(message);
+
+        exit(EXIT_XMLERR);
+
+        return;
+    }
+
+    /**
+     * XML構文エラー処理。
+     * 例外を出力してVM終了する。
+     * @param ex 例外
+     */
+    private static void xmlError(SAXParseException ex){
+        String txt = ex.getLocalizedMessage();
+        int line = ex.getLineNumber();
+        int col = ex.getColumnNumber();
+
+        String message =
+                MessageFormat.format(ERRMSG_SAXPARSE, txt, line, col);
+        errprint(message);
+
+        exit(EXIT_XMLERR);
+
+        return;
+    }
+
+    /**
+     * XML構文エラー処理。
+     * 例外を出力してVM終了する。
+     * @param ex 例外
+     */
+    private static void xmlError(TogaXmlException ex){
+        String txt = ex.getLocalizedMessage();
+        String message = MessageFormat.format(ERRMSG_TXTONLY, txt);
+        errprint(message);
+
+        exit(EXIT_XMLERR);
+
+        return;
+    }
+
+    /**
+     * VMを終了する。
+     * @param code 終了コード
+     */
+    private static void exit(int code){
+        System.exit(code);
+        assert false;
+        throw new AssertionError();
+    }
+
+    /**
+     * 標準エラー出力に文字列出力を行う。
+     * @param obj 文字列
+     */
+    @SuppressWarnings("PMD.SystemPrintln")
+    private static void errprint(Object obj){
+        System.err.print(obj.toString());
+        return;
+    }
+
+    /**
+     * エラーを表示した後VMを終了させる。
+     * @param code 終了コード
+     * @param text メッセージ
+     */
+    private static void errExit(int code, CharSequence text){
+        errprint("ERROR:\n");
+        errprint(text);
+        exit(code);
+        return;
+    }
+
+}
diff --git a/src/main/java/jp/sourceforge/mikutoga/vmd2xml/package-info.java b/src/main/java/jp/sourceforge/mikutoga/vmd2xml/package-info.java
new file mode 100644 (file)
index 0000000..0029933
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * package information for Javadoc
+ *
+ * License : The MIT License
+ * Copyright(c) 2011 MikuToga Partners
+ */
+
+/**
+ * VMD-XML変換ユーティリティ、およびそのmainエントリ。
+ */
+
+package jp.sourceforge.mikutoga.vmd2xml;
+
+/* EOF */
diff --git a/src/main/resources/jp/sourceforge/mikutoga/vmd/model/xml/resources/vmdxml-110820.dtd b/src/main/resources/jp/sourceforge/mikutoga/vmd/model/xml/resources/vmdxml-110820.dtd
new file mode 100644 (file)
index 0000000..8092eef
--- /dev/null
@@ -0,0 +1,327 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<!--
+  MikuMikuDance
+    motion-data(*.vmd) on XML
+    DTD definition
+
+  License : The MIT License
+  Copyright(c) 2011 MikuToga Partners
+
+  SYSTEM ID: http://mikutoga.sourceforge.jp/xml/dtd/vmdxml-110820.dtd
+-->
+
+
+<!--
+    ROOT
+-->
+<!ELEMENT vmdMotion (
+    meta*,
+    (
+          ( modelName, boneMotionSequence, morphSequence )
+        | ( cameraSequence, luminousSequence, shadowSequence )
+    )
+) >
+<!ATTLIST vmdMotion
+    xmlns              CDATA #IMPLIED
+    xmlns:xsi          CDATA #IMPLIED
+    xsi:schemaLocation CDATA #IMPLIED
+    version            CDATA #FIXED "110820"
+>
+
+
+<!--
+    Meta-information of model.
+    Use free.
+    but, some meta-name has recommended usage.
+    + "generator" (Generator application name)
+    + "siteURL" (Website URL)
+-->
+<!ELEMENT meta EMPTY >
+<!ATTLIST meta
+    name    CDATA #REQUIRED
+    content CDATA #REQUIRED
+>
+
+
+<!--
+    bezier cubic curve parameters for interpolation.
+    p0=(0, 0) p3=(127, 127) [implicit points]
+    P1 and P2 points are required.
+-->
+<!ELEMENT bezier EMPTY >
+<!ATTLIST bezier
+    p1x CDATA #REQUIRED
+    p1y CDATA #REQUIRED
+    p2x CDATA #REQUIRED
+    p2y CDATA #REQUIRED
+>
+
+
+<!--
+    default linear bezier curve for interpolation.
+    p0=(0, 0) p1=(20, 20) p2=(107, 107) p3=(127, 127)
+-->
+<!ELEMENT defLinear EMPTY >
+
+
+<!--
+    default ease-in-out bezier curve for interpolation.
+    p0=(0, 0) p1=(64, 0) p2=(64, 127) p3=(127, 127)
+-->
+<!ELEMENT defEaseInOut EMPTY >
+
+
+<!--
+    model name.
+-->
+<!ELEMENT modelName EMPTY>
+<!ATTLIST modelName name CDATA #REQUIRED>
+
+
+<!--
+    bone motion sequence
+-->
+<!ELEMENT boneMotionSequence (
+    bonePart*
+) >
+
+
+<!--
+    bone part grouped by bone-name
+-->
+<!ELEMENT bonePart (
+    boneMotion*
+) >
+<!ATTLIST bonePart name CDATA #REQUIRED>
+
+
+<!--
+    bone motion
+-->
+<!ELEMENT boneMotion (
+    bonePosition?, ( boneRotQuat | boneRotEyxz )
+) >
+<!ATTLIST boneMotion frame CDATA #REQUIRED>
+
+
+<!--
+    bone position
+    with XYZ *3 interpolations.
+-->
+<!ELEMENT bonePosition (
+    ( ( bezier | defLinear | defEaseInOut ),
+      ( bezier | defLinear | defEaseInOut ),
+      ( bezier | defLinear | defEaseInOut )  )?
+) >
+<!ATTLIST bonePosition
+    xPos CDATA #REQUIRED
+    yPos CDATA #REQUIRED
+    zPos CDATA #REQUIRED
+>
+
+
+<!--
+    bone rotation [Quaternion]
+-->
+<!ELEMENT boneRotQuat (
+    ( bezier | defLinear | defEaseInOut )?
+) >
+<!ATTLIST boneRotQuat
+    qx CDATA #REQUIRED
+    qy CDATA #REQUIRED
+    qz CDATA #REQUIRED
+    qw CDATA #REQUIRED
+>
+
+
+<!--
+    bone rotation [YXZ-Euler]
+    degree but not radian
+-->
+<!ELEMENT boneRotEyxz (
+    ( bezier | defLinear | defEaseInOut )?
+) >
+<!ATTLIST boneRotEyxz
+    xDeg CDATA #REQUIRED
+    yDeg CDATA #REQUIRED
+    zDeg CDATA #REQUIRED
+>
+
+
+<!--
+    morph sequence
+-->
+<!ELEMENT morphSequence (
+    morphPart*
+) >
+
+
+<!--
+    morph part grouped by morph-name
+-->
+<!ELEMENT morphPart (
+    morphMotion*
+) >
+<!ATTLIST morphPart name CDATA #REQUIRED>
+
+
+<!--
+    morph motion
+    flex : morph flexible value. [0.0-1.0]
+-->
+<!ELEMENT morphMotion EMPTY >
+<!ATTLIST morphMotion
+    frame CDATA #REQUIRED
+    flex  CDATA #REQUIRED
+>
+
+
+<!--
+    camera sequence
+-->
+<!ELEMENT cameraSequence (
+    cameraMotion*
+) >
+
+
+<!--
+    camera motion
+-->
+<!ELEMENT cameraMotion (
+    cameraTarget, cameraRotation, cameraRange, projection
+) >
+<!ATTLIST cameraMotion
+    frame          CDATA #REQUIRED
+    hasPerspective ( true | false | 1 | 0 ) "true"
+>
+
+
+<!--
+    camera target position
+    with XYZ *3 interpolations.
+-->
+<!ELEMENT cameraTarget (
+    ( ( bezier | defLinear | defEaseInOut ),
+      ( bezier | defLinear | defEaseInOut ),
+      ( bezier | defLinear | defEaseInOut )  )?
+) >
+<!ATTLIST cameraTarget
+    xPos CDATA #REQUIRED
+    yPos CDATA #REQUIRED
+    zPos CDATA #REQUIRED
+>
+
+
+<!--
+    camera-rotation around camera-target
+      with polar-coordinates parameters.
+
+    xRad = -radian(UI_X) [latitude]
+    yRad =  radian(UI_Y) [longitude]
+    zRad =  radian(UI_Z) [roll]
+-->
+<!ELEMENT cameraRotation (
+    ( bezier | defLinear | defEaseInOut )?
+) >
+<!ATTLIST cameraRotation
+    xRad CDATA #REQUIRED
+    yRad CDATA #REQUIRED
+    zRad CDATA #REQUIRED
+>
+
+
+<!--
+    camera range.
+    sign was negated from UI_RANGE.
+-->
+<!ELEMENT cameraRange (
+    ( bezier | defLinear | defEaseInOut )?
+) >
+<!ATTLIST cameraRange range CDATA #REQUIRED >
+
+
+<!--
+    screen-projection data.
+    vertDeg : vertical screen-projection angle by degree.
+-->
+<!ELEMENT projection (
+    ( bezier | defLinear | defEaseInOut )?
+) >
+<!ATTLIST projection vertDeg CDATA #REQUIRED >
+
+
+<!--
+    luminous sequence
+-->
+<!ELEMENT luminousSequence (
+    luminousAct*
+) >
+
+
+<!--
+    luminous act
+-->
+<!ELEMENT luminousAct (
+    lumiColor, lumiDirection
+) >
+<!ATTLIST luminousAct frame CDATA #REQUIRED >
+
+
+<!--
+    luminous color by RGB color space.
+    color component value. [0.0-1.0]
+-->
+<!ELEMENT lumiColor EMPTY >
+<!ATTLIST lumiColor
+    rCol CDATA #REQUIRED
+    gCol CDATA #REQUIRED
+    bCol CDATA #REQUIRED
+>
+
+
+<!--
+    luminous direction by vector
+    vector component [0.0-1.0]
+-->
+<!ELEMENT lumiDirection EMPTY >
+<!ATTLIST lumiDirection
+    xVec CDATA #REQUIRED
+    yVec CDATA #REQUIRED
+    zVec CDATA #REQUIRED
+>
+
+
+<!--
+    shadow sequence
+-->
+<!ELEMENT shadowSequence (
+    shadowAct*
+) >
+
+
+<!--
+    shadow act
+
+    mode : type of shadow
+    + NONE   : no self-shadow
+    + MODE_1 : reduce shadow-quality suddenly at range
+    + MODE_2 : reduce shadow-quality gradually with range
+
+    rawParam : shadow range raw value.
+    UI_VALUE = EFFECTIVE_RANGE * 100 ???
+    rawParam = 0.1 - (UI_VALUE / 1.0E+5)
+
+    UI_VALUE:0    => rawParam:0.1
+    UI_VALUE:8875 => rawParam:0.01125
+    UI_VALUE:9999 => rawParam:1.0E-5
+-->
+<!ELEMENT shadowAct EMPTY >
+<!ATTLIST shadowAct
+    frame CDATA #REQUIRED
+    mode ( NONE | MODE_1 | MODE_2 ) #REQUIRED
+    rawParam CDATA #REQUIRED
+>
+
+
+<!-- EOF -->
diff --git a/src/main/resources/jp/sourceforge/mikutoga/vmd/model/xml/resources/vmdxml-110820.xsd b/src/main/resources/jp/sourceforge/mikutoga/vmd/model/xml/resources/vmdxml-110820.xsd
new file mode 100644 (file)
index 0000000..3f74ed3
--- /dev/null
@@ -0,0 +1,868 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<!--
+  MikuMikuDance
+    motion-data(*.vmd) on XML
+    schema definition
+
+  License : The MIT License
+  Copyright(c) 2011 MikuToga Partners
+-->
+
+
+<!DOCTYPE xsd:schema [
+    <!ENTITY schemaVer "110820" >
+    <!ENTITY schemaNS "http://mikutoga.sourceforge.jp/xml/ns/vmdxml/110820" >
+]>
+
+
+<xsd:schema
+    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+
+    targetNamespace="&schemaNS;"
+    xmlns:tns      ="&schemaNS;"
+
+    elementFormDefault="qualified"
+    version="&schemaVer;"
+>
+
+    <xsd:annotation>
+        <xsd:documentation>
+            MikuMikuDance motion-data(*.vmd) on XML.
+            License : The MIT License
+            Copyright(c) 2011 MikuToga Partners
+        </xsd:documentation>
+    </xsd:annotation>
+
+
+    <!-- ROOT element for motion -->
+    <xsd:element name="vmdMotion" >
+        <xsd:annotation>
+            <xsd:documentation>
+                Root element.
+            </xsd:documentation>
+        </xsd:annotation>
+
+        <xsd:complexType>
+            <xsd:sequence>
+                <xsd:element
+                    ref="tns:meta"
+                    minOccurs="0" maxOccurs="unbounded" />
+                <xsd:choice>
+                    <xsd:sequence>  <!-- model motion -->
+                        <xsd:element ref="tns:modelName" />
+                        <xsd:element ref="tns:boneMotionSequence" />
+                        <xsd:element ref="tns:morphSequence" />
+                    </xsd:sequence>
+                    <xsd:sequence>  <!-- stage act -->
+                        <xsd:element ref="tns:cameraSequence" />
+                        <xsd:element ref="tns:luminousSequence" />
+                        <xsd:element ref="tns:shadowSequence" />
+                    </xsd:sequence>
+                </xsd:choice>
+            </xsd:sequence>
+            <xsd:attribute
+                name="version"
+                type="xsd:token"
+                use="required"
+                fixed="&schemaVer;" />
+        </xsd:complexType>
+
+    </xsd:element>
+
+
+    <xsd:simpleType name="FrameNo" >
+        <xsd:annotation>
+            <xsd:documentation>
+                frame No.
+            </xsd:documentation>
+        </xsd:annotation>
+
+        <xsd:restriction base="xsd:int" >
+<!--
+            <xsd:minInclusive value="0" />
+-->
+        </xsd:restriction>
+
+    </xsd:simpleType>
+
+
+    <xsd:simpleType name="MorphFlex" >
+        <xsd:annotation>
+            <xsd:documentation>
+                morph flexible value. [0.0-1.0]
+            </xsd:documentation>
+        </xsd:annotation>
+
+        <xsd:restriction base="xsd:float" >
+            <xsd:minInclusive value="-0.0" />
+<!--
+            <xsd:maxInclusive value="1.0" />
+-->
+        </xsd:restriction>
+
+    </xsd:simpleType>
+
+
+    <xsd:simpleType name="ProjectionAngle" >
+        <xsd:annotation>
+            <xsd:documentation>
+                screen-projection angle(degree).
+            </xsd:documentation>
+        </xsd:annotation>
+
+        <xsd:restriction base="xsd:int" >
+<!--
+            <xsd:minInclusive value="-0" />
+            <xsd:maxInclusive value="+180" />
+-->
+        </xsd:restriction>
+
+    </xsd:simpleType>
+
+
+    <xsd:simpleType name="ColorComponent" >
+        <xsd:annotation>
+            <xsd:documentation>
+                color component value.
+            </xsd:documentation>
+        </xsd:annotation>
+
+        <xsd:restriction base="xsd:float" >
+<!--
+            <xsd:minInclusive value="-0.0" />
+            <xsd:maxInclusive value="1.0" />
+-->
+        </xsd:restriction>
+
+    </xsd:simpleType>
+
+
+    <xsd:simpleType name="DirVecComponent" >
+        <xsd:annotation>
+            <xsd:documentation>
+                direction vector component value.
+            </xsd:documentation>
+        </xsd:annotation>
+
+        <xsd:restriction base="xsd:float" >
+<!--
+            <xsd:minInclusive value="-1.0" />
+            <xsd:maxInclusive value="+1.0" />
+-->
+        </xsd:restriction>
+
+    </xsd:simpleType>
+
+
+    <xsd:simpleType name="ShadowType" >
+        <xsd:annotation>
+            <xsd:documentation>
+                type of shadow
+                + NONE   : no self-shadow
+                + MODE_1 : reduce shadow-quality suddenly at range
+                + MODE_2 : reduce shadow-quality gradually with range
+            </xsd:documentation>
+        </xsd:annotation>
+
+        <xsd:restriction base="xsd:string" >
+            <xsd:enumeration value="NONE"   />
+            <xsd:enumeration value="MODE_1" />
+            <xsd:enumeration value="MODE_2" />
+        </xsd:restriction>
+
+    </xsd:simpleType>
+
+
+    <xsd:simpleType name="ShadowRawParam" >
+        <xsd:annotation>
+            <xsd:documentation>
+                shadow range raw value.
+
+                UI_VALUE = EFFECTIVE_RANGE * 100 ???
+                rawParam = 0.1 - (UI_VALUE / 1.0E+5)
+
+                UI_VALUE:0    => rawParam:0.1
+                UI_VALUE:8875 => rawParam:0.01125
+                UI_VALUE:9999 => rawParam:1.0E-5
+            </xsd:documentation>
+        </xsd:annotation>
+
+        <xsd:restriction base="xsd:float" >
+<!--
+            <xsd:minInclusive value="-0.0" />
+            <xsd:maxInclusive value="0.1" />
+-->
+        </xsd:restriction>
+
+    </xsd:simpleType>
+
+
+    <xsd:simpleType name="BezPt" >
+        <xsd:annotation>
+            <xsd:documentation>
+                Bezier points coordinates value. [XY:0-127]
+            </xsd:documentation>
+        </xsd:annotation>
+
+        <xsd:restriction base="xsd:byte" >
+            <xsd:minInclusive value=  "0" />
+            <xsd:maxInclusive value="127" />
+        </xsd:restriction>
+
+    </xsd:simpleType>
+
+
+    <xsd:complexType name="FrameNumbered" >
+        <xsd:annotation>
+            <xsd:documentation>
+                frame numbered type.
+            </xsd:documentation>
+        </xsd:annotation>
+
+        <xsd:attribute name="frame" type="tns:FrameNo" use="required" />
+
+    </xsd:complexType>
+
+
+    <xsd:complexType name="BezParam" >
+        <xsd:annotation>
+            <xsd:documentation>
+                bezier cubic curve parameters.
+                p0=(0, 0) p3=(127, 127) [implicit points]
+            </xsd:documentation>
+        </xsd:annotation>
+
+        <xsd:attribute name="p1x" type="tns:BezPt" use="optional" />
+        <xsd:attribute name="p1y" type="tns:BezPt" use="optional" />
+        <xsd:attribute name="p2x" type="tns:BezPt" use="optional" />
+        <xsd:attribute name="p2y" type="tns:BezPt" use="optional" />
+
+    </xsd:complexType>
+
+
+    <xsd:element name="bezier" >
+        <xsd:annotation>
+            <xsd:documentation>
+                bezier cubic curve parameters.
+                p0=(0, 0) p3=(127, 127) [implicit points]
+                P1 and P2 points are required.
+            </xsd:documentation>
+        </xsd:annotation>
+
+        <xsd:complexType>
+            <xsd:complexContent>
+                <xsd:restriction base="tns:BezParam" >
+                    <xsd:attribute name="p1x" type="tns:BezPt" use="required" />
+                    <xsd:attribute name="p1y" type="tns:BezPt" use="required" />
+                    <xsd:attribute name="p2x" type="tns:BezPt" use="required" />
+                    <xsd:attribute name="p2y" type="tns:BezPt" use="required" />
+                </xsd:restriction>
+            </xsd:complexContent>
+        </xsd:complexType>
+
+    </xsd:element>
+
+
+    <xsd:element name="defLinear" >
+        <xsd:annotation>
+            <xsd:documentation>
+                default linear bezier curve.
+                p0=(0, 0) p1=(20, 20) p2=(107, 107) p3=(127, 127)
+            </xsd:documentation>
+        </xsd:annotation>
+
+        <xsd:complexType>
+            <xsd:complexContent>
+                <xsd:restriction base="tns:BezParam" >
+                    <xsd:attribute name="p1x" use="prohibited" fixed= "20" />
+                    <xsd:attribute name="p1y" use="prohibited" fixed= "20" />
+                    <xsd:attribute name="p2x" use="prohibited" fixed="107" />
+                    <xsd:attribute name="p2y" use="prohibited" fixed="107" />
+                </xsd:restriction>
+            </xsd:complexContent>
+        </xsd:complexType>
+
+    </xsd:element>
+
+
+    <xsd:element name="defEaseInOut" >
+        <xsd:annotation>
+            <xsd:documentation>
+                default ease-in-out bezier curve.
+                p0=(0, 0) p1=(64, 0) p2=(64, 127) p3=(127, 127)
+            </xsd:documentation>
+        </xsd:annotation>
+
+        <xsd:complexType>
+            <xsd:complexContent>
+                <xsd:restriction base="tns:BezParam" >
+                    <xsd:attribute name="p1x" use="prohibited" fixed= "64" />
+                    <xsd:attribute name="p1y" use="prohibited" fixed=  "0" />
+                    <xsd:attribute name="p2x" use="prohibited" fixed= "64" />
+                    <xsd:attribute name="p2y" use="prohibited" fixed="127" />
+                </xsd:restriction>
+            </xsd:complexContent>
+        </xsd:complexType>
+
+    </xsd:element>
+
+
+    <xsd:complexType name="InterpolationSingle" >
+        <xsd:annotation>
+            <xsd:documentation>
+                has single interpolation curve.
+                If omitted, it means default linear curve.
+            </xsd:documentation>
+        </xsd:annotation>
+
+        <xsd:choice minOccurs="0" >
+            <xsd:element ref="tns:bezier" />
+            <xsd:element ref="tns:defLinear" />
+            <xsd:element ref="tns:defEaseInOut" />
+        </xsd:choice>
+
+    </xsd:complexType>
+
+
+    <xsd:complexType name="PosWithInterpolation" >
+        <xsd:annotation>
+            <xsd:documentation>
+                3D-position with XYZ-interpolation curve.
+                If omitted, it means default linear curve *3 [XYZ].
+            </xsd:documentation>
+        </xsd:annotation>
+
+        <xsd:sequence minOccurs="0" maxOccurs="1" >
+            <xsd:choice minOccurs="3" maxOccurs="3" >
+                <xsd:element ref="tns:bezier" />
+                <xsd:element ref="tns:defLinear" />
+                <xsd:element ref="tns:defEaseInOut" />
+            </xsd:choice>
+        </xsd:sequence>
+
+        <xsd:attribute name="xPos" type="xsd:float" use="required" />
+        <xsd:attribute name="yPos" type="xsd:float" use="required" />
+        <xsd:attribute name="zPos" type="xsd:float" use="required" />
+
+    </xsd:complexType>
+
+
+    <xsd:element name="meta" >
+        <xsd:annotation>
+            <xsd:documentation>
+                Meta-information of motion.
+                Use free.
+                but, some meta-name has recommended usage.
+                + "generator" (Generator application name)
+                + "siteURL" (about motion creator)
+            </xsd:documentation>
+        </xsd:annotation>
+
+        <xsd:complexType>
+            <xsd:attribute
+                name="name"
+                type="xsd:NCName"
+                use="required" />
+            <xsd:attribute
+                name="content"
+                type="xsd:string"
+                use="required" />
+        </xsd:complexType>
+
+    </xsd:element>
+
+
+    <xsd:element name="modelName" >
+        <xsd:annotation>
+            <xsd:documentation>
+                name of motion-model.
+            </xsd:documentation>
+        </xsd:annotation>
+
+        <xsd:complexType>
+            <xsd:attribute
+                name="name"
+                type="xsd:string"
+                use="required" />
+        </xsd:complexType>
+
+    </xsd:element>
+
+
+    <xsd:element name="boneMotionSequence" >
+        <xsd:annotation>
+            <xsd:documentation>
+                bone motion data sequence.
+            </xsd:documentation>
+        </xsd:annotation>
+
+        <xsd:complexType>
+            <xsd:sequence>
+                <xsd:element
+                    ref="tns:bonePart"
+                    minOccurs="0" maxOccurs="unbounded" />
+            </xsd:sequence>
+        </xsd:complexType>
+
+        <xsd:unique name="BoneName" >
+            <xsd:selector xpath="./tns:bonePart" />
+            <xsd:field xpath="@name" />
+        </xsd:unique>
+
+    </xsd:element>
+
+
+    <xsd:element name="bonePart" >
+        <xsd:annotation>
+            <xsd:documentation>
+                bone motion data group by bone-name.
+            </xsd:documentation>
+        </xsd:annotation>
+
+        <xsd:complexType>
+            <xsd:sequence>
+                <xsd:element
+                    ref="tns:boneMotion"
+                    minOccurs="0" maxOccurs="unbounded" />
+            </xsd:sequence>
+
+            <xsd:attribute
+                name="name"
+                type="xsd:string"
+                use="required" />
+        </xsd:complexType>
+
+        <xsd:unique name="BoneFrameNo" >
+            <xsd:selector xpath="./tns:boneMotion" />
+            <xsd:field xpath="@frame" />
+        </xsd:unique>
+
+    </xsd:element>
+
+
+    <xsd:element name="boneMotion" >
+        <xsd:annotation>
+            <xsd:documentation>
+                bone motion data.
+            </xsd:documentation>
+        </xsd:annotation>
+
+        <xsd:complexType>
+            <xsd:complexContent>
+                <xsd:extension base="tns:FrameNumbered" >
+                    <xsd:sequence>
+                        <xsd:element
+                            ref="tns:bonePosition"
+                            minOccurs="0" maxOccurs="1" />
+                        <xsd:choice>
+                            <xsd:element ref="tns:boneRotQuat" />
+                            <xsd:element ref="tns:boneRotEyxz" />
+                        </xsd:choice>
+                    </xsd:sequence>
+                </xsd:extension>
+            </xsd:complexContent>
+        </xsd:complexType>
+
+    </xsd:element>
+
+
+    <xsd:element name="bonePosition" type="tns:PosWithInterpolation" >
+        <xsd:annotation>
+            <xsd:documentation>
+                bone position.
+            </xsd:documentation>
+        </xsd:annotation>
+
+    </xsd:element>
+
+
+    <xsd:element name="boneRotQuat" >
+        <xsd:annotation>
+            <xsd:documentation>
+                bone rotation. (Quaternion parameters)
+            </xsd:documentation>
+        </xsd:annotation>
+
+        <xsd:complexType>
+            <xsd:complexContent>
+                <xsd:extension base="tns:InterpolationSingle" >
+                    <xsd:attribute name="qx" type="xsd:float" use="required" />
+                    <xsd:attribute name="qy" type="xsd:float" use="required" />
+                    <xsd:attribute name="qz" type="xsd:float" use="required" />
+                    <xsd:attribute name="qw" type="xsd:float" use="required" />
+                </xsd:extension>
+            </xsd:complexContent>
+        </xsd:complexType>
+
+    </xsd:element>
+
+
+    <xsd:element name="boneRotEyxz" >
+        <xsd:annotation>
+            <xsd:documentation>
+                bone rotation. (YXZ-Euler rotation parameters)
+            </xsd:documentation>
+        </xsd:annotation>
+
+        <xsd:complexType>
+            <xsd:complexContent>
+                <xsd:extension base="tns:InterpolationSingle" >
+                    <xsd:attribute name="xDeg" type="xsd:float" use="required" />
+                    <xsd:attribute name="yDeg" type="xsd:float" use="required" />
+                    <xsd:attribute name="zDeg" type="xsd:float" use="required" />
+                </xsd:extension>
+            </xsd:complexContent>
+        </xsd:complexType>
+
+    </xsd:element>
+
+
+    <xsd:element name="morphSequence" >
+        <xsd:annotation>
+            <xsd:documentation>
+                morphing data sequence.
+            </xsd:documentation>
+        </xsd:annotation>
+
+        <xsd:complexType>
+            <xsd:sequence>
+                <xsd:element
+                    ref="tns:morphPart"
+                    minOccurs="0" maxOccurs="unbounded" />
+            </xsd:sequence>
+        </xsd:complexType>
+
+        <xsd:unique name="MorphName" >
+            <xsd:selector xpath="./tns:morphPart" />
+            <xsd:field xpath="@name" />
+        </xsd:unique>
+
+    </xsd:element>
+
+
+    <xsd:element name="morphPart" >
+        <xsd:annotation>
+            <xsd:documentation>
+                morphing data group by morph-name.
+            </xsd:documentation>
+        </xsd:annotation>
+
+        <xsd:complexType>
+            <xsd:sequence>
+                <xsd:element
+                    ref="tns:morphMotion"
+                    minOccurs="0" maxOccurs="unbounded" />
+            </xsd:sequence>
+
+            <xsd:attribute
+                name="name"
+                type="xsd:string"
+                use="required" />
+        </xsd:complexType>
+
+        <xsd:unique name="MorphFrameNo" >
+            <xsd:selector xpath="./tns:morphMotion" />
+            <xsd:field xpath="@frame" />
+        </xsd:unique>
+
+    </xsd:element>
+
+
+    <xsd:element name="morphMotion" >
+        <xsd:annotation>
+            <xsd:documentation>
+                morphing data.
+            </xsd:documentation>
+        </xsd:annotation>
+
+        <xsd:complexType>
+            <xsd:complexContent>
+                <xsd:extension base="tns:FrameNumbered" >
+                    <xsd:attribute
+                        name="flex"
+                        type="tns:MorphFlex"
+                        use="required" />
+                </xsd:extension>
+            </xsd:complexContent>
+        </xsd:complexType>
+
+    </xsd:element>
+
+
+    <xsd:element name="cameraSequence" >
+        <xsd:annotation>
+            <xsd:documentation>
+                camera sequence.
+            </xsd:documentation>
+        </xsd:annotation>
+
+        <xsd:complexType>
+            <xsd:sequence>
+                <xsd:element
+                    ref="tns:cameraMotion"
+                    minOccurs="0" maxOccurs="unbounded" />
+            </xsd:sequence>
+        </xsd:complexType>
+
+        <xsd:unique name="CameraFrameNo" >
+            <xsd:selector xpath="./tns:cameraMotion" />
+            <xsd:field xpath="@frame" />
+        </xsd:unique>
+
+    </xsd:element>
+
+
+    <xsd:element name="cameraMotion" >
+        <xsd:annotation>
+            <xsd:documentation>
+                camera data.
+            </xsd:documentation>
+        </xsd:annotation>
+
+        <xsd:complexType>
+            <xsd:complexContent>
+                <xsd:extension base="tns:FrameNumbered" >
+                    <xsd:sequence>
+                        <xsd:element ref="tns:cameraTarget" />
+                        <xsd:element ref="tns:cameraRotation" />
+                        <xsd:element ref="tns:cameraRange" />
+                        <xsd:element ref="tns:projection" />
+                    </xsd:sequence>
+                    <xsd:attribute
+                        name="hasPerspective"
+                        type="xsd:boolean"
+                        use="optional"
+                        default="true" />
+                </xsd:extension>
+            </xsd:complexContent>
+        </xsd:complexType>
+
+    </xsd:element>
+
+
+    <xsd:element name="cameraTarget" type="tns:PosWithInterpolation" >
+        <xsd:annotation>
+            <xsd:documentation>
+                camera target.
+            </xsd:documentation>
+        </xsd:annotation>
+
+    </xsd:element>
+
+
+    <xsd:element name="cameraRotation" >
+        <xsd:annotation>
+            <xsd:documentation>
+                camera-rotation around camera-target
+                  with polar-coordinates parameters.
+
+                xRad = -radian(UI_X) [latitude]
+                yRad =  radian(UI_Y) [longitude]
+                zRad =  radian(UI_Z) [roll]
+            </xsd:documentation>
+        </xsd:annotation>
+
+        <xsd:complexType>
+            <xsd:complexContent>
+                <xsd:extension base="tns:InterpolationSingle" >
+                    <xsd:attribute name="xRad" type="xsd:float" use="required" />
+                    <xsd:attribute name="yRad" type="xsd:float" use="required" />
+                    <xsd:attribute name="zRad" type="xsd:float" use="required" />
+                </xsd:extension>
+            </xsd:complexContent>
+        </xsd:complexType>
+
+    </xsd:element>
+
+
+    <xsd:element name="cameraRange" >
+        <xsd:annotation>
+            <xsd:documentation>
+                camera range.
+                sign was negated from UI_RANGE.
+            </xsd:documentation>
+        </xsd:annotation>
+
+        <xsd:complexType>
+            <xsd:complexContent>
+                <xsd:extension base="tns:InterpolationSingle" >
+                    <xsd:attribute
+                        name="range"
+                        type="xsd:float"
+                        use="required" />
+                </xsd:extension>
+            </xsd:complexContent>
+        </xsd:complexType>
+
+    </xsd:element>
+
+
+    <xsd:element name="projection" >
+        <xsd:annotation>
+            <xsd:documentation>
+                projection data.
+                vertDeg : vertical screen-projection angle by degree.
+            </xsd:documentation>
+        </xsd:annotation>
+
+        <xsd:complexType>
+            <xsd:complexContent>
+                <xsd:extension base="tns:InterpolationSingle" >
+                    <xsd:attribute
+                        name="vertDeg"
+                        type="tns:ProjectionAngle"
+                        use="required" />
+                </xsd:extension>
+            </xsd:complexContent>
+        </xsd:complexType>
+
+    </xsd:element>
+
+
+    <xsd:element name="luminousSequence" >
+        <xsd:annotation>
+            <xsd:documentation>
+                luminous sequence.
+            </xsd:documentation>
+        </xsd:annotation>
+
+        <xsd:complexType>
+            <xsd:sequence>
+                <xsd:element
+                    ref="tns:luminousAct"
+                    minOccurs="0" maxOccurs="unbounded" />
+            </xsd:sequence>
+        </xsd:complexType>
+
+        <xsd:unique name="LuminousFrameNo" >
+            <xsd:selector xpath="./tns:luminousAct" />
+            <xsd:field xpath="@frame" />
+        </xsd:unique>
+
+    </xsd:element>
+
+
+    <xsd:element name="luminousAct" >
+        <xsd:annotation>
+            <xsd:documentation>
+                luminous data.
+            </xsd:documentation>
+        </xsd:annotation>
+
+        <xsd:complexType>
+            <xsd:complexContent>
+                <xsd:extension base="tns:FrameNumbered" >
+                    <xsd:sequence>
+                        <xsd:element ref="tns:lumiColor" />
+                        <xsd:element ref="tns:lumiDirection" />
+                    </xsd:sequence>
+                </xsd:extension>
+            </xsd:complexContent>
+        </xsd:complexType>
+
+    </xsd:element>
+
+
+    <xsd:element name="lumiColor" >
+        <xsd:annotation>
+            <xsd:documentation>
+                luminous color by RGB color space.
+            </xsd:documentation>
+        </xsd:annotation>
+
+        <xsd:complexType>
+            <xsd:attribute
+                name="rCol"
+                type="tns:ColorComponent"
+                use="required" />
+            <xsd:attribute
+                name="gCol"
+                type="tns:ColorComponent"
+                use="required" />
+            <xsd:attribute
+                name="bCol"
+                type="tns:ColorComponent"
+                use="required" />
+        </xsd:complexType>
+
+    </xsd:element>
+
+
+    <xsd:element name="lumiDirection" >
+        <xsd:annotation>
+            <xsd:documentation>
+                luminous direction by vector.
+            </xsd:documentation>
+        </xsd:annotation>
+
+        <xsd:complexType>
+            <xsd:attribute
+                name="xVec"
+                type="tns:DirVecComponent"
+                use="required" />
+            <xsd:attribute
+                name="yVec"
+                type="tns:DirVecComponent"
+                use="required" />
+            <xsd:attribute
+                name="zVec"
+                type="tns:DirVecComponent"
+                use="required" />
+        </xsd:complexType>
+
+    </xsd:element>
+
+
+    <xsd:element name="shadowSequence" >
+        <xsd:annotation>
+            <xsd:documentation>
+                shadow sequence.
+            </xsd:documentation>
+        </xsd:annotation>
+
+        <xsd:complexType>
+            <xsd:sequence>
+                <xsd:element
+                    ref="tns:shadowAct"
+                    minOccurs="0" maxOccurs="unbounded" />
+            </xsd:sequence>
+        </xsd:complexType>
+
+        <xsd:unique name="ShadowFrameNo" >
+            <xsd:selector xpath="./tns:shadowAct" />
+            <xsd:field xpath="@frame" />
+        </xsd:unique>
+
+    </xsd:element>
+
+
+    <xsd:element name="shadowAct" >
+        <xsd:annotation>
+            <xsd:documentation>
+                shadow data.
+            </xsd:documentation>
+        </xsd:annotation>
+
+        <xsd:complexType>
+            <xsd:complexContent>
+                <xsd:extension base="tns:FrameNumbered" >
+                    <xsd:attribute
+                        name="mode"
+                        type="tns:ShadowType"
+                        use="required" />
+                    <xsd:attribute
+                        name="rawParam"
+                        type="tns:ShadowRawParam"
+                        use="required" />
+                </xsd:extension>
+            </xsd:complexContent>
+        </xsd:complexType>
+
+    </xsd:element>
+
+
+</xsd:schema>
+
+
+<!-- EOF -->
diff --git a/src/main/resources/jp/sourceforge/mikutoga/vmd2xml/resources/version.properties b/src/main/resources/jp/sourceforge/mikutoga/vmd2xml/resources/version.properties
new file mode 100644 (file)
index 0000000..71fb597
--- /dev/null
@@ -0,0 +1,9 @@
+# Version definition
+#   [ with Maven resource filtering ]
+
+app.name = ${pom.name}
+app.version = ${pom.version}
+app.license = The MIT License
+app.url = ${pom.url}
+
+# EOF #