Browse Source

pass through but panic

aoowweenn 8 years ago
parent
commit
4045466f73

+ 4 - 0
CMakeLists.txt

@@ -344,7 +344,11 @@ IF ( ASSIMP_BUILD_ASSIMP_TOOLS )
   # Why assimp_qt_viewer/CMakeLists.txt still contain similar check?
   # Because viewer can be build independently of Assimp.
   FIND_PACKAGE(Qt5Widgets QUIET)
+  set(CMAKE_MODULE_PATH_BACKUP ${CMAKE_MODULE_PATH})
+  set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR})
   FIND_PACKAGE(DevIL QUIET)
+  set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR})
+  set(CMAKE_MODULE_PATH_BACKUP ${CMAKE_MODULE_PATH})
   FIND_PACKAGE(OpenGL QUIET)
   IF ( Qt5Widgets_FOUND AND IL_FOUND AND OPENGL_FOUND)
     ADD_SUBDIRECTORY( tools/assimp_qt_viewer/ )

+ 72 - 0
FindDevIL.cmake

@@ -0,0 +1,72 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+#.rst:
+# FindDevIL
+# ---------
+#
+#
+#
+# This module locates the developer's image library.
+# http://openil.sourceforge.net/
+#
+# This module sets:
+#
+# ::
+#
+#    IL_LIBRARIES -   the name of the IL library. These include the full path to
+#                     the core DevIL library. This one has to be linked into the
+#                     application.
+#    ILU_LIBRARIES -  the name of the ILU library. Again, the full path. This
+#                     library is for filters and effects, not actual loading. It
+#                     doesn't have to be linked if the functionality it provides
+#                     is not used.
+#    ILUT_LIBRARIES - the name of the ILUT library. Full path. This part of the
+#                     library interfaces with OpenGL. It is not strictly needed
+#                     in applications.
+#    IL_INCLUDE_DIR - where to find the il.h, ilu.h and ilut.h files.
+#    IL_FOUND -       this is set to TRUE if all the above variables were set.
+#                     This will be set to false if ILU or ILUT are not found,
+#                     even if they are not needed. In most systems, if one
+#                     library is found all the others are as well. That's the
+#                     way the DevIL developers release it.
+
+# TODO: Add version support.
+# Tested under Linux and Windows (MSVC)
+
+#include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake)
+include(FindPackageHandleStandardArgs)
+
+find_path(IL_INCLUDE_DIR il.h
+  PATH_SUFFIXES include IL
+  DOC "The path to the directory that contains il.h"
+)
+
+#message("IL_INCLUDE_DIR is ${IL_INCLUDE_DIR}")
+
+find_library(IL_LIBRARIES
+  NAMES IL DEVIL
+  PATH_SUFFIXES lib64 lib lib32
+  DOC "The file that corresponds to the base il library."
+)
+
+#message("IL_LIBRARIES is ${IL_LIBRARIES}")
+
+find_library(ILUT_LIBRARIES
+  NAMES ILUT
+  PATH_SUFFIXES lib64 lib lib32
+  DOC "The file that corresponds to the il (system?) utility library."
+)
+
+#message("ILUT_LIBRARIES is ${ILUT_LIBRARIES}")
+
+find_library(ILU_LIBRARIES
+  NAMES ILU
+  PATH_SUFFIXES lib64 lib lib32
+  DOC "The file that corresponds to the il utility library."
+)
+
+#message("ILU_LIBRARIES is ${ILU_LIBRARIES}")
+
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(IL DEFAULT_MSG
+                                  IL_LIBRARIES IL_INCLUDE_DIR)

+ 690 - 0
FindICU.cmake

@@ -0,0 +1,690 @@
+# This module can find the International Components for Unicode (ICU) libraries
+#
+# Requirements:
+# - CMake >= 2.8.3 (for new version of find_package_handle_standard_args)
+#
+# The following variables will be defined for your use:
+#   - ICU_FOUND             : were all of your specified components found?
+#   - ICU_INCLUDE_DIRS      : ICU include directory
+#   - ICU_LIBRARIES         : ICU libraries
+#   - ICU_VERSION           : complete version of ICU (x.y.z)
+#   - ICU_VERSION_MAJOR     : major version of ICU
+#   - ICU_VERSION_MINOR     : minor version of ICU
+#   - ICU_VERSION_PATCH     : patch version of ICU
+#   - ICU_<COMPONENT>_FOUND : were <COMPONENT> found? (FALSE for non specified component if it is not a dependency)
+#
+# For windows or non standard installation, define ICU_ROOT_DIR variable to point to the root installation of ICU. Two ways:
+#   - run cmake with -DICU_ROOT_DIR=<PATH>
+#   - define an environment variable with the same name before running cmake
+# With cmake-gui, before pressing "Configure":
+#   1) Press "Add Entry" button
+#   2) Add a new entry defined as:
+#     - Name: ICU_ROOT_DIR
+#     - Type: choose PATH in the selection list
+#     - Press "..." button and select the root installation of ICU
+#
+# Example Usage:
+#
+#   1. Copy this file in the root of your project source directory
+#   2. Then, tell CMake to search this non-standard module in your project directory by adding to your CMakeLists.txt:
+#     set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR})
+#   3. Finally call find_package() once, here are some examples to pick from
+#
+#   Require ICU 4.4 or later
+#     find_package(ICU 4.4 REQUIRED)
+#
+#   if(ICU_FOUND)
+#      add_executable(myapp myapp.c)
+#      include_directories(${ICU_INCLUDE_DIRS})
+#      target_link_libraries(myapp ${ICU_LIBRARIES})
+#      # with CMake >= 3.0.0, the last two lines can be replaced by the following
+#      target_link_libraries(myapp ICU::ICU)
+#   endif(ICU_FOUND)
+
+########## <ICU finding> ##########
+
+find_package(PkgConfig QUIET)
+
+########## Private ##########
+if(NOT DEFINED ICU_PUBLIC_VAR_NS)
+    set(ICU_PUBLIC_VAR_NS "ICU")                          # Prefix for all ICU relative public variables
+endif(NOT DEFINED ICU_PUBLIC_VAR_NS)
+if(NOT DEFINED ICU_PRIVATE_VAR_NS)
+    set(ICU_PRIVATE_VAR_NS "_${ICU_PUBLIC_VAR_NS}")       # Prefix for all ICU relative internal variables
+endif(NOT DEFINED ICU_PRIVATE_VAR_NS)
+if(NOT DEFINED PC_ICU_PRIVATE_VAR_NS)
+    set(PC_ICU_PRIVATE_VAR_NS "_PC${ICU_PRIVATE_VAR_NS}") # Prefix for all pkg-config relative internal variables
+endif(NOT DEFINED PC_ICU_PRIVATE_VAR_NS)
+
+set(${ICU_PRIVATE_VAR_NS}_HINTS )
+# <deprecated>
+# for future removal
+if(DEFINED ENV{ICU_ROOT})
+    list(APPEND ${ICU_PRIVATE_VAR_NS}_HINTS "$ENV{ICU_ROOT}")
+    message(AUTHOR_WARNING "ENV{ICU_ROOT} is deprecated in favor of ENV{ICU_ROOT_DIR}")
+endif(DEFINED ENV{ICU_ROOT})
+if (DEFINED ICU_ROOT)
+    list(APPEND ${ICU_PRIVATE_VAR_NS}_HINTS "${ICU_ROOT}")
+    message(AUTHOR_WARNING "ICU_ROOT is deprecated in favor of ICU_ROOT_DIR")
+endif(DEFINED ICU_ROOT)
+# </deprecated>
+if(DEFINED ENV{ICU_ROOT_DIR})
+    list(APPEND ${ICU_PRIVATE_VAR_NS}_HINTS "$ENV{ICU_ROOT_DIR}")
+endif(DEFINED ENV{ICU_ROOT_DIR})
+if (DEFINED ICU_ROOT_DIR)
+    list(APPEND ${ICU_PRIVATE_VAR_NS}_HINTS "${ICU_ROOT_DIR}")
+endif(DEFINED ICU_ROOT_DIR)
+
+set(${ICU_PRIVATE_VAR_NS}_COMPONENTS )
+# <icu component name> <library name 1> ... <library name N>
+macro(_icu_declare_component _NAME)
+    list(APPEND ${ICU_PRIVATE_VAR_NS}_COMPONENTS ${_NAME})
+    set("${ICU_PRIVATE_VAR_NS}_COMPONENTS_${_NAME}" ${ARGN})
+endmacro(_icu_declare_component)
+
+_icu_declare_component(data icudata)
+_icu_declare_component(uc   icuuc)         # Common and Data libraries
+_icu_declare_component(i18n icui18n icuin) # Internationalization library
+_icu_declare_component(io   icuio ustdio)  # Stream and I/O Library
+_icu_declare_component(le   icule)         # Layout library
+_icu_declare_component(lx   iculx)         # Paragraph Layout library
+
+########## Public ##########
+set(${ICU_PUBLIC_VAR_NS}_FOUND FALSE)
+set(${ICU_PUBLIC_VAR_NS}_LIBRARIES )
+set(${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS )
+set(${ICU_PUBLIC_VAR_NS}_C_FLAGS "")
+set(${ICU_PUBLIC_VAR_NS}_CXX_FLAGS "")
+set(${ICU_PUBLIC_VAR_NS}_CPP_FLAGS "")
+set(${ICU_PUBLIC_VAR_NS}_C_SHARED_FLAGS "")
+set(${ICU_PUBLIC_VAR_NS}_CXX_SHARED_FLAGS "")
+set(${ICU_PUBLIC_VAR_NS}_CPP_SHARED_FLAGS "")
+
+foreach(${ICU_PRIVATE_VAR_NS}_COMPONENT ${${ICU_PRIVATE_VAR_NS}_COMPONENTS})
+    string(TOUPPER "${${ICU_PRIVATE_VAR_NS}_COMPONENT}" ${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT)
+    set("${ICU_PUBLIC_VAR_NS}_${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}_FOUND" FALSE) # may be done in the _icu_declare_component macro
+endforeach(${ICU_PRIVATE_VAR_NS}_COMPONENT)
+
+# Check components
+if(NOT ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS) # uc required at least
+    set(${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS uc)
+else(NOT ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS)
+    list(APPEND ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS uc)
+    list(REMOVE_DUPLICATES ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS)
+    foreach(${ICU_PRIVATE_VAR_NS}_COMPONENT ${${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS})
+        if(NOT DEFINED ${ICU_PRIVATE_VAR_NS}_COMPONENTS_${${ICU_PRIVATE_VAR_NS}_COMPONENT})
+            message(FATAL_ERROR "Unknown ICU component: ${${ICU_PRIVATE_VAR_NS}_COMPONENT}")
+        endif(NOT DEFINED ${ICU_PRIVATE_VAR_NS}_COMPONENTS_${${ICU_PRIVATE_VAR_NS}_COMPONENT})
+    endforeach(${ICU_PRIVATE_VAR_NS}_COMPONENT)
+endif(NOT ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS)
+
+# if pkg-config is available check components dependencies and append `pkg-config icu-<component> --variable=prefix` to hints
+if(PKG_CONFIG_FOUND)
+    set(${ICU_PRIVATE_VAR_NS}_COMPONENTS_DUP ${${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS})
+    foreach(${ICU_PRIVATE_VAR_NS}_COMPONENT ${${ICU_PRIVATE_VAR_NS}_COMPONENTS_DUP})
+        pkg_check_modules(${PC_ICU_PRIVATE_VAR_NS} "icu-${${ICU_PRIVATE_VAR_NS}_COMPONENT}" QUIET)
+
+        if(${PC_ICU_PRIVATE_VAR_NS}_FOUND)
+            list(APPEND ${ICU_PRIVATE_VAR_NS}_HINTS ${${PC_ICU_PRIVATE_VAR_NS}_PREFIX})
+            foreach(${PC_ICU_PRIVATE_VAR_NS}_LIBRARY ${${PC_ICU_PRIVATE_VAR_NS}_LIBRARIES})
+                string(REGEX REPLACE "^icu" "" ${PC_ICU_PRIVATE_VAR_NS}_STRIPPED_LIBRARY ${${PC_ICU_PRIVATE_VAR_NS}_LIBRARY})
+                if(NOT ${PC_ICU_PRIVATE_VAR_NS}_STRIPPED_LIBRARY STREQUAL "data")
+                    list(FIND ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS ${${PC_ICU_PRIVATE_VAR_NS}_STRIPPED_LIBRARY} ${ICU_PRIVATE_VAR_NS}_COMPONENT_INDEX)
+                    if(${ICU_PRIVATE_VAR_NS}_COMPONENT_INDEX EQUAL -1)
+                        message(WARNING "Missing component dependency: ${${PC_ICU_PRIVATE_VAR_NS}_STRIPPED_LIBRARY}. Add it to your find_package(ICU) line as COMPONENTS to fix this warning.")
+                        list(APPEND ${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS ${${PC_ICU_PRIVATE_VAR_NS}_STRIPPED_LIBRARY})
+                    endif(${ICU_PRIVATE_VAR_NS}_COMPONENT_INDEX EQUAL -1)
+                endif(NOT ${PC_ICU_PRIVATE_VAR_NS}_STRIPPED_LIBRARY STREQUAL "data")
+            endforeach(${PC_ICU_PRIVATE_VAR_NS}_LIBRARY)
+        endif(${PC_ICU_PRIVATE_VAR_NS}_FOUND)
+    endforeach(${ICU_PRIVATE_VAR_NS}_COMPONENT)
+endif(PKG_CONFIG_FOUND)
+# list(APPEND ${ICU_PRIVATE_VAR_NS}_HINTS ENV ICU_ROOT_DIR)
+# message("${ICU_PRIVATE_VAR_NS}_HINTS = ${${ICU_PRIVATE_VAR_NS}_HINTS}")
+
+# Includes
+find_path(
+    ${ICU_PUBLIC_VAR_NS}_INCLUDE_DIR
+    NAMES unicode/utypes.h utypes.h
+    HINTS ${${ICU_PRIVATE_VAR_NS}_HINTS}
+    PATH_SUFFIXES "include"
+    DOC "Include directories for ICU"
+)
+
+if(${ICU_PUBLIC_VAR_NS}_INCLUDE_DIR)
+    ########## <part to keep synced with tests/version/CMakeLists.txt> ##########
+    if(EXISTS "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIR}/unicode/uvernum.h") # ICU >= 4.4
+        file(READ "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIR}/unicode/uvernum.h" ${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS)
+    elseif(EXISTS "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIR}/unicode/uversion.h") # ICU [2;4.4[
+        file(READ "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIR}/unicode/uversion.h" ${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS)
+    elseif(EXISTS "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIR}/unicode/utypes.h") # ICU [1.4;2[
+        file(READ "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIR}/unicode/utypes.h" ${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS)
+    elseif(EXISTS "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIR}/utypes.h") # ICU 1.3
+        file(READ "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIR}/utypes.h" ${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS)
+    else()
+        message(FATAL_ERROR "ICU version header not found")
+    endif()
+
+    if(${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS MATCHES ".*# *define *ICU_VERSION *\"([0-9]+)\".*") # ICU 1.3
+        # [1.3;1.4[ as #define ICU_VERSION "3" (no patch version, ie all 1.3.X versions will be detected as 1.3.0)
+        set(${ICU_PUBLIC_VAR_NS}_VERSION_MAJOR "1")
+        set(${ICU_PUBLIC_VAR_NS}_VERSION_MINOR "${CMAKE_MATCH_1}")
+        set(${ICU_PUBLIC_VAR_NS}_VERSION_PATCH "0")
+    elseif(${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS MATCHES ".*# *define *U_ICU_VERSION_MAJOR_NUM *([0-9]+).*")
+        #
+        # Since version 4.9.1, ICU release version numbering was totaly changed, see:
+        # - http://site.icu-project.org/download/49
+        # - http://userguide.icu-project.org/design#TOC-Version-Numbers-in-ICU
+        #
+        set(${ICU_PUBLIC_VAR_NS}_VERSION_MAJOR "${CMAKE_MATCH_1}")
+        string(REGEX REPLACE ".*# *define *U_ICU_VERSION_MINOR_NUM *([0-9]+).*" "\\1" ${ICU_PUBLIC_VAR_NS}_VERSION_MINOR "${${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS}")
+        string(REGEX REPLACE ".*# *define *U_ICU_VERSION_PATCHLEVEL_NUM *([0-9]+).*" "\\1" ${ICU_PUBLIC_VAR_NS}_VERSION_PATCH "${${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS}")
+    elseif(${ICU_PRIVATE_VAR_NS}_VERSION_HEADER_CONTENTS MATCHES ".*# *define *U_ICU_VERSION *\"(([0-9]+)(\\.[0-9]+)*)\".*") # ICU [1.4;1.8[
+        # [1.4;1.8[ as #define U_ICU_VERSION "1.4.1.2" but it seems that some 1.4.[12](?:\.\d)? have releasing error and appears as 1.4.0
+        set(${ICU_PRIVATE_VAR_NS}_FULL_VERSION "${CMAKE_MATCH_1}") # copy CMAKE_MATCH_1, no longer valid on the following if
+        if(${ICU_PRIVATE_VAR_NS}_FULL_VERSION MATCHES "^([0-9]+)\\.([0-9]+)$")
+            set(${ICU_PUBLIC_VAR_NS}_VERSION_MAJOR "${CMAKE_MATCH_1}")
+            set(${ICU_PUBLIC_VAR_NS}_VERSION_MINOR "${CMAKE_MATCH_2}")
+            set(${ICU_PUBLIC_VAR_NS}_VERSION_PATCH "0")
+        elseif(${ICU_PRIVATE_VAR_NS}_FULL_VERSION MATCHES "^([0-9]+)\\.([0-9]+)\\.([0-9]+)")
+            set(${ICU_PUBLIC_VAR_NS}_VERSION_MAJOR "${CMAKE_MATCH_1}")
+            set(${ICU_PUBLIC_VAR_NS}_VERSION_MINOR "${CMAKE_MATCH_2}")
+            set(${ICU_PUBLIC_VAR_NS}_VERSION_PATCH "${CMAKE_MATCH_3}")
+        endif()
+    else()
+        message(FATAL_ERROR "failed to detect ICU version")
+    endif()
+    set(${ICU_PUBLIC_VAR_NS}_VERSION "${${ICU_PUBLIC_VAR_NS}_VERSION_MAJOR}.${${ICU_PUBLIC_VAR_NS}_VERSION_MINOR}.${${ICU_PUBLIC_VAR_NS}_VERSION_PATCH}")
+    ########## </part to keep synced with tests/version/CMakeLists.txt> ##########
+endif(${ICU_PUBLIC_VAR_NS}_INCLUDE_DIR)
+
+# Check libraries
+if(MSVC)
+    include(SelectLibraryConfigurations)
+endif(MSVC)
+foreach(${ICU_PRIVATE_VAR_NS}_COMPONENT ${${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS})
+    string(TOUPPER "${${ICU_PRIVATE_VAR_NS}_COMPONENT}" ${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT)
+    if(MSVC)
+        set(${ICU_PRIVATE_VAR_NS}_POSSIBLE_RELEASE_NAMES )
+        set(${ICU_PRIVATE_VAR_NS}_POSSIBLE_DEBUG_NAMES )
+        foreach(${ICU_PRIVATE_VAR_NS}_BASE_NAME ${${ICU_PRIVATE_VAR_NS}_COMPONENTS_${${ICU_PRIVATE_VAR_NS}_COMPONENT}})
+            list(APPEND ${ICU_PRIVATE_VAR_NS}_POSSIBLE_RELEASE_NAMES "${${ICU_PRIVATE_VAR_NS}_BASE_NAME}")
+            list(APPEND ${ICU_PRIVATE_VAR_NS}_POSSIBLE_DEBUG_NAMES "${${ICU_PRIVATE_VAR_NS}_BASE_NAME}d")
+            list(APPEND ${ICU_PRIVATE_VAR_NS}_POSSIBLE_RELEASE_NAMES "${${ICU_PRIVATE_VAR_NS}_BASE_NAME}${${ICU_PUBLIC_VAR_NS}_VERSION_MAJOR}${${ICU_PUBLIC_VAR_NS}_VERSION_MINOR}")
+            list(APPEND ${ICU_PRIVATE_VAR_NS}_POSSIBLE_DEBUG_NAMES "${${ICU_PRIVATE_VAR_NS}_BASE_NAME}${${ICU_PUBLIC_VAR_NS}_VERSION_MAJOR}${${ICU_PUBLIC_VAR_NS}_VERSION_MINOR}d")
+        endforeach(${ICU_PRIVATE_VAR_NS}_BASE_NAME)
+
+        find_library(
+            ${ICU_PUBLIC_VAR_NS}_${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}_LIBRARY_RELEASE
+            NAMES ${${ICU_PRIVATE_VAR_NS}_POSSIBLE_RELEASE_NAMES}
+            HINTS ${${ICU_PRIVATE_VAR_NS}_HINTS}
+            DOC "Release library for ICU ${${ICU_PRIVATE_VAR_NS}_COMPONENT} component"
+        )
+        find_library(
+            ${ICU_PUBLIC_VAR_NS}_${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}_LIBRARY_DEBUG
+            NAMES ${${ICU_PRIVATE_VAR_NS}_POSSIBLE_DEBUG_NAMES}
+            HINTS ${${ICU_PRIVATE_VAR_NS}_HINTS}
+            DOC "Debug library for ICU ${${ICU_PRIVATE_VAR_NS}_COMPONENT} component"
+        )
+
+        select_library_configurations("${ICU_PUBLIC_VAR_NS}_${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}")
+        list(APPEND ${ICU_PUBLIC_VAR_NS}_LIBRARY ${${ICU_PUBLIC_VAR_NS}_${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}_LIBRARY})
+    else(MSVC)
+        find_library(
+            ${ICU_PUBLIC_VAR_NS}_${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}_LIBRARY
+            NAMES ${${ICU_PRIVATE_VAR_NS}_COMPONENTS_${${ICU_PRIVATE_VAR_NS}_COMPONENT}}
+            PATHS ${${ICU_PRIVATE_VAR_NS}_HINTS}
+            DOC "Library for ICU ${${ICU_PRIVATE_VAR_NS}_COMPONENT} component"
+        )
+
+        if(${ICU_PUBLIC_VAR_NS}_${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}_LIBRARY)
+            set("${ICU_PUBLIC_VAR_NS}_${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}_FOUND" TRUE)
+            list(APPEND ${ICU_PUBLIC_VAR_NS}_LIBRARY ${${ICU_PUBLIC_VAR_NS}_${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}_LIBRARY})
+        endif(${ICU_PUBLIC_VAR_NS}_${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}_LIBRARY)
+    endif(MSVC)
+endforeach(${ICU_PRIVATE_VAR_NS}_COMPONENT)
+
+# Try to find out compiler flags
+find_program(${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE icu-config HINTS ${${ICU_PRIVATE_VAR_NS}_HINTS})
+if(${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE)
+    execute_process(COMMAND ${${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE} --cflags OUTPUT_VARIABLE ${ICU_PUBLIC_VAR_NS}_C_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE)
+    execute_process(COMMAND ${${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE} --cxxflags OUTPUT_VARIABLE ${ICU_PUBLIC_VAR_NS}_CXX_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE)
+    execute_process(COMMAND ${${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE} --cppflags OUTPUT_VARIABLE ${ICU_PUBLIC_VAR_NS}_CPP_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+    execute_process(COMMAND ${${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE} --cflags-dynamic OUTPUT_VARIABLE ${ICU_PUBLIC_VAR_NS}_C_SHARED_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE)
+    execute_process(COMMAND ${${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE} --cxxflags-dynamic OUTPUT_VARIABLE ${ICU_PUBLIC_VAR_NS}_CXX_SHARED_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE)
+    execute_process(COMMAND ${${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE} --cppflags-dynamic OUTPUT_VARIABLE ${ICU_PUBLIC_VAR_NS}_CPP_SHARED_FLAGS OUTPUT_STRIP_TRAILING_WHITESPACE)
+endif(${ICU_PUBLIC_VAR_NS}_CONFIG_EXECUTABLE)
+
+# Check find_package arguments
+include(FindPackageHandleStandardArgs)
+if(${ICU_PUBLIC_VAR_NS}_FIND_REQUIRED AND NOT ${ICU_PUBLIC_VAR_NS}_FIND_QUIETLY)
+    find_package_handle_standard_args(
+        ${ICU_PUBLIC_VAR_NS}
+        REQUIRED_VARS ${ICU_PUBLIC_VAR_NS}_LIBRARY ${ICU_PUBLIC_VAR_NS}_INCLUDE_DIR
+        VERSION_VAR ${ICU_PUBLIC_VAR_NS}_VERSION
+    )
+else(${ICU_PUBLIC_VAR_NS}_FIND_REQUIRED AND NOT ${ICU_PUBLIC_VAR_NS}_FIND_QUIETLY)
+    find_package_handle_standard_args(${ICU_PUBLIC_VAR_NS} "Could NOT find ICU" ${ICU_PUBLIC_VAR_NS}_LIBRARY ${ICU_PUBLIC_VAR_NS}_INCLUDE_DIR)
+endif(${ICU_PUBLIC_VAR_NS}_FIND_REQUIRED AND NOT ${ICU_PUBLIC_VAR_NS}_FIND_QUIETLY)
+
+if(${ICU_PUBLIC_VAR_NS}_FOUND)
+    # <deprecated>
+    # for compatibility with previous versions, alias old ICU_(MAJOR|MINOR|PATCH)_VERSION to ICU_VERSION_$1
+    set(${ICU_PUBLIC_VAR_NS}_MAJOR_VERSION ${${ICU_PUBLIC_VAR_NS}_VERSION_MAJOR})
+    set(${ICU_PUBLIC_VAR_NS}_MINOR_VERSION ${${ICU_PUBLIC_VAR_NS}_VERSION_MINOR})
+    set(${ICU_PUBLIC_VAR_NS}_PATCH_VERSION ${${ICU_PUBLIC_VAR_NS}_VERSION_PATCH})
+    # </deprecated>
+    set(${ICU_PUBLIC_VAR_NS}_LIBRARIES ${${ICU_PUBLIC_VAR_NS}_LIBRARY})
+    set(${ICU_PUBLIC_VAR_NS}_INCLUDE_DIRS ${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIR})
+
+    if(NOT CMAKE_VERSION VERSION_LESS "3.0.0")
+        if(NOT TARGET ICU::ICU)
+            add_library(ICU::ICU INTERFACE IMPORTED)
+        endif(NOT TARGET ICU::ICU)
+        set_target_properties(ICU::ICU PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIR}")
+        foreach(${ICU_PRIVATE_VAR_NS}_COMPONENT ${${ICU_PUBLIC_VAR_NS}_FIND_COMPONENTS})
+            string(TOUPPER "${${ICU_PRIVATE_VAR_NS}_COMPONENT}" ${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT)
+            add_library("ICU::${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}" UNKNOWN IMPORTED)
+            if(${ICU_PUBLIC_VAR_NS}_${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}_LIBRARY_RELEASE)
+                set_property(TARGET "ICU::${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}" APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
+                set_target_properties("ICU::${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}" PROPERTIES IMPORTED_LOCATION_RELEASE "${${ICU_PUBLIC_VAR_NS}_${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}_LIBRARY_RELEASE}")
+            endif(${ICU_PUBLIC_VAR_NS}_${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}_LIBRARY_RELEASE)
+            if(${ICU_PUBLIC_VAR_NS}_${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}_LIBRARY_DEBUG)
+                set_property(TARGET "ICU::${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}" APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG)
+                set_target_properties("ICU::${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}" PROPERTIES IMPORTED_LOCATION_DEBUG "${${ICU_PUBLIC_VAR_NS}_${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}_LIBRARY_DEBUG}")
+            endif(${ICU_PUBLIC_VAR_NS}_${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}_LIBRARY_DEBUG)
+            if(${ICU_PUBLIC_VAR_NS}_${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}_LIBRARY)
+                set_target_properties("ICU::${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}" PROPERTIES IMPORTED_LOCATION "${${ICU_PUBLIC_VAR_NS}_${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}_LIBRARY}")
+            endif(${ICU_PUBLIC_VAR_NS}_${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}_LIBRARY)
+            set_property(TARGET ICU::ICU APPEND PROPERTY INTERFACE_LINK_LIBRARIES "ICU::${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}")
+#             set_target_properties("ICU::${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}" PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${${ICU_PUBLIC_VAR_NS}_INCLUDE_DIR}")
+        endforeach(${ICU_PRIVATE_VAR_NS}_COMPONENT)
+    endif(NOT CMAKE_VERSION VERSION_LESS "3.0.0")
+endif(${ICU_PUBLIC_VAR_NS}_FOUND)
+
+mark_as_advanced(
+    ${ICU_PUBLIC_VAR_NS}_INCLUDE_DIR
+    ${ICU_PUBLIC_VAR_NS}_LIBRARY
+)
+
+########## </ICU finding> ##########
+
+########## <resource bundle support> ##########
+
+########## Private ##########
+function(_icu_extract_locale_from_rb _BUNDLE_SOURCE _RETURN_VAR_NAME)
+    file(READ "${_BUNDLE_SOURCE}" _BUNDLE_CONTENTS)
+    string(REGEX REPLACE "//[^\n]*\n" "" _BUNDLE_CONTENTS_WITHOUT_COMMENTS ${_BUNDLE_CONTENTS})
+    string(REGEX REPLACE "[ \t\n]" "" _BUNDLE_CONTENTS_WITHOUT_COMMENTS_AND_SPACES ${_BUNDLE_CONTENTS_WITHOUT_COMMENTS})
+    string(REGEX MATCH "^([a-zA-Z_-]+)(:table)?{" LOCALE_FOUND ${_BUNDLE_CONTENTS_WITHOUT_COMMENTS_AND_SPACES})
+    set("${_RETURN_VAR_NAME}" "${CMAKE_MATCH_1}" PARENT_SCOPE)
+endfunction(_icu_extract_locale_from_rb)
+
+########## Public ##########
+
+#
+# Prototype:
+#   icu_generate_resource_bundle([NAME <name>] [PACKAGE] [DESTINATION <location>] [FILES <list of files>])
+#
+# Common arguments:
+#   - NAME <name>                      : name of output package and to create dummy targets
+#   - FILES <file 1> ... <file N>      : list of resource bundles sources
+#   - DEPENDS <target1> ... <target N> : required to package as library (shared or static), a list of cmake parent targets to link to
+#                                        Note: only (PREVIOUSLY DECLARED) add_executable and add_library as dependencies
+#   - DESTINATION <location>           : optional, directory where to install final binary file(s)
+#   - FORMAT <name>                    : optional, one of none (ICU4C binary format, default), java (plain java) or xliff (XML), see below
+#
+# Arguments depending on FORMAT:
+#   - none (default):
+#       * PACKAGE     : if present, package all resource bundles together. Default is to stop after building individual *.res files
+#       * TYPE <name> : one of :
+#           + common or archive (default) : archive all ressource bundles into a single .dat file
+#           + library or dll              : assemble all ressource bundles into a separate and loadable library (.dll/.so)
+#           + static                      : integrate all ressource bundles to targets designed by DEPENDS parameter (as a static library)
+#       * NO_SHARED_FLAGS                 : only with TYPE in ['library', 'dll', 'static'], do not append ICU_C(XX)_SHARED_FLAGS to targets given as DEPENDS argument
+#   - JAVA:
+#       * BUNDLE <name> : required, prefix for generated classnames
+#   - XLIFF:
+#       (none)
+#
+
+#
+# For an archive, the idea is to generate the following dependencies:
+#
+#   root.txt => root.res \
+#                        |
+#   en.txt   => en.res   |
+#                        | => pkglist.txt => application.dat
+#   fr.txt   => fr.res   |
+#                        |
+#   and so on            /
+#
+# Lengend: 'A => B' means B depends on A
+#
+# Steps (correspond to arrows):
+#   1) genrb (from .txt to .res)
+#   2) generate a file text (pkglist.txt) with all .res files to put together
+#   3) build final archive (from *.res/pkglist.txt to .dat)
+#
+
+function(icu_generate_resource_bundle)
+
+    ##### <check for pkgdata/genrb availability> #####
+    find_program(${ICU_PUBLIC_VAR_NS}_GENRB_EXECUTABLE genrb HINTS ${${ICU_PRIVATE_VAR_NS}_HINTS})
+    find_program(${ICU_PUBLIC_VAR_NS}_PKGDATA_EXECUTABLE pkgdata HINTS ${${ICU_PRIVATE_VAR_NS}_HINTS})
+
+    if(NOT ${ICU_PUBLIC_VAR_NS}_GENRB_EXECUTABLE)
+        message(FATAL_ERROR "genrb not found")
+    endif(NOT ${ICU_PUBLIC_VAR_NS}_GENRB_EXECUTABLE)
+    if(NOT ${ICU_PUBLIC_VAR_NS}_PKGDATA_EXECUTABLE)
+        message(FATAL_ERROR "pkgdata not found")
+    endif(NOT ${ICU_PUBLIC_VAR_NS}_PKGDATA_EXECUTABLE)
+    ##### </check for pkgdata/genrb availability> #####
+
+    ##### <constants> #####
+    set(TARGET_SEPARATOR "+")
+    set(__FUNCTION__ "icu_generate_resource_bundle")
+    set(PACKAGE_TARGET_PREFIX "ICU${TARGET_SEPARATOR}PKG")
+    set(RESOURCE_TARGET_PREFIX "ICU${TARGET_SEPARATOR}RB")
+    ##### </constants> #####
+
+    ##### <hash constants> #####
+    # filename extension of built resource bundle (without dot)
+    set(BUNDLES__SUFFIX "res")
+    set(BUNDLES_JAVA_SUFFIX "java")
+    set(BUNDLES_XLIFF_SUFFIX "xlf")
+    # alias: none (default) = common = archive ; dll = library ; static
+    set(PKGDATA__ALIAS "")
+    set(PKGDATA_COMMON_ALIAS "")
+    set(PKGDATA_ARCHIVE_ALIAS "")
+    set(PKGDATA_DLL_ALIAS "LIBRARY")
+    set(PKGDATA_LIBRARY_ALIAS "LIBRARY")
+    set(PKGDATA_STATIC_ALIAS "STATIC")
+    # filename prefix of built package
+    set(PKGDATA__PREFIX "")
+    set(PKGDATA_LIBRARY_PREFIX "${CMAKE_SHARED_LIBRARY_PREFIX}")
+    set(PKGDATA_STATIC_PREFIX "${CMAKE_STATIC_LIBRARY_PREFIX}")
+    # filename extension of built package (with dot)
+    set(PKGDATA__SUFFIX ".dat")
+    set(PKGDATA_LIBRARY_SUFFIX "${CMAKE_SHARED_LIBRARY_SUFFIX}")
+    set(PKGDATA_STATIC_SUFFIX "${CMAKE_STATIC_LIBRARY_SUFFIX}")
+    # pkgdata option mode specific
+    set(PKGDATA__OPTIONS "-m" "common")
+    set(PKGDATA_STATIC_OPTIONS "-m" "static")
+    set(PKGDATA_LIBRARY_OPTIONS "-m" "library")
+    # cmake library type for output package
+    set(PKGDATA_LIBRARY__TYPE "")
+    set(PKGDATA_LIBRARY_STATIC_TYPE STATIC)
+    set(PKGDATA_LIBRARY_LIBRARY_TYPE SHARED)
+    ##### </hash constants> #####
+
+    include(CMakeParseArguments)
+    cmake_parse_arguments(
+        PARSED_ARGS # output variable name
+        # options (true/false) (default value: false)
+        "PACKAGE;NO_SHARED_FLAGS"
+        # univalued parameters (default value: "")
+        "NAME;DESTINATION;TYPE;FORMAT;BUNDLE"
+        # multivalued parameters (default value: "")
+        "FILES;DEPENDS"
+        ${ARGN}
+    )
+
+    # assert(${PARSED_ARGS_NAME} != "")
+    if(NOT PARSED_ARGS_NAME)
+        message(FATAL_ERROR "${__FUNCTION__}(): no name given, NAME parameter missing")
+    endif(NOT PARSED_ARGS_NAME)
+
+    # assert(length(PARSED_ARGS_FILES) > 0)
+    list(LENGTH PARSED_ARGS_FILES PARSED_ARGS_FILES_LEN)
+    if(PARSED_ARGS_FILES_LEN LESS 1)
+        message(FATAL_ERROR "${__FUNCTION__}() expects at least 1 resource bundle as FILES argument, 0 given")
+    endif(PARSED_ARGS_FILES_LEN LESS 1)
+
+    string(TOUPPER "${PARSED_ARGS_FORMAT}" UPPER_FORMAT)
+    # assert(${UPPER_FORMAT} in ['', 'java', 'xlif'])
+    if(NOT DEFINED BUNDLES_${UPPER_FORMAT}_SUFFIX)
+        message(FATAL_ERROR "${__FUNCTION__}(): unknown FORMAT '${PARSED_ARGS_FORMAT}'")
+    endif(NOT DEFINED BUNDLES_${UPPER_FORMAT}_SUFFIX)
+
+    if(UPPER_FORMAT STREQUAL "JAVA")
+        # assert(${PARSED_ARGS_BUNDLE} != "")
+        if(NOT PARSED_ARGS_BUNDLE)
+            message(FATAL_ERROR "${__FUNCTION__}(): java bundle name expected, BUNDLE parameter missing")
+        endif(NOT PARSED_ARGS_BUNDLE)
+    endif(UPPER_FORMAT STREQUAL "JAVA")
+
+    if(PARSED_ARGS_PACKAGE)
+        # assert(${PARSED_ARGS_FORMAT} == "")
+        if(PARSED_ARGS_FORMAT)
+            message(FATAL_ERROR "${__FUNCTION__}(): packaging is only supported for binary format, not xlif neither java outputs")
+        endif(PARSED_ARGS_FORMAT)
+
+        string(TOUPPER "${PARSED_ARGS_TYPE}" UPPER_MODE)
+        # assert(${UPPER_MODE} in ['', 'common', 'archive', 'dll', library'])
+        if(NOT DEFINED PKGDATA_${UPPER_MODE}_ALIAS)
+            message(FATAL_ERROR "${__FUNCTION__}(): unknown TYPE '${PARSED_ARGS_TYPE}'")
+        else(NOT DEFINED PKGDATA_${UPPER_MODE}_ALIAS)
+            set(TYPE "${PKGDATA_${UPPER_MODE}_ALIAS}")
+        endif(NOT DEFINED PKGDATA_${UPPER_MODE}_ALIAS)
+
+        # Package name: strip file extension if present
+        get_filename_component(PACKAGE_NAME_WE ${PARSED_ARGS_NAME} NAME_WE)
+        # Target name to build package
+        set(PACKAGE_TARGET_NAME "${PACKAGE_TARGET_PREFIX}${TARGET_SEPARATOR}${PACKAGE_NAME_WE}")
+        # Target name to build intermediate list file
+        set(PACKAGE_LIST_TARGET_NAME "${PACKAGE_TARGET_NAME}${TARGET_SEPARATOR}PKGLIST")
+        # Directory (absolute) to set as "current directory" for genrb (does not include package directory, -p)
+        # We make our "cook" there to prevent any conflict
+        if(DEFINED CMAKE_PLATFORM_ROOT_BIN) # CMake < 2.8.10
+            set(RESOURCE_GENRB_CHDIR_DIR "${CMAKE_PLATFORM_ROOT_BIN}/${PACKAGE_TARGET_NAME}.dir/")
+        else(DEFINED CMAKE_PLATFORM_ROOT_BIN) # CMake >= 2.8.10
+            set(RESOURCE_GENRB_CHDIR_DIR "${CMAKE_PLATFORM_INFO_DIR}/${PACKAGE_TARGET_NAME}.dir/")
+        endif(DEFINED CMAKE_PLATFORM_ROOT_BIN)
+        # Directory (absolute) where resource bundles are built: concatenation of RESOURCE_GENRB_CHDIR_DIR and package name
+        set(RESOURCE_OUTPUT_DIR "${RESOURCE_GENRB_CHDIR_DIR}/${PACKAGE_NAME_WE}/")
+        # Output (relative) path for built package
+        if(MSVC AND TYPE STREQUAL PKGDATA_LIBRARY_ALIAS)
+            set(PACKAGE_OUTPUT_PATH "${RESOURCE_GENRB_CHDIR_DIR}/${PACKAGE_NAME_WE}/${PKGDATA_${TYPE}_PREFIX}${PACKAGE_NAME_WE}${PKGDATA_${TYPE}_SUFFIX}")
+        else(MSVC AND TYPE STREQUAL PKGDATA_LIBRARY_ALIAS)
+            set(PACKAGE_OUTPUT_PATH "${RESOURCE_GENRB_CHDIR_DIR}/${PKGDATA_${TYPE}_PREFIX}${PACKAGE_NAME_WE}${PKGDATA_${TYPE}_SUFFIX}")
+        endif(MSVC AND TYPE STREQUAL PKGDATA_LIBRARY_ALIAS)
+        # Output (absolute) path for the list file
+        set(PACKAGE_LIST_OUTPUT_PATH "${RESOURCE_GENRB_CHDIR_DIR}/pkglist.txt")
+
+        file(MAKE_DIRECTORY "${RESOURCE_OUTPUT_DIR}")
+    else(PARSED_ARGS_PACKAGE)
+        set(RESOURCE_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/")
+#         set(RESOURCE_GENRB_CHDIR_DIR "UNUSED")
+    endif(PARSED_ARGS_PACKAGE)
+
+    set(TARGET_RESOURCES )
+    set(COMPILED_RESOURCES_PATH )
+    set(COMPILED_RESOURCES_BASENAME )
+    foreach(RESOURCE_SOURCE ${PARSED_ARGS_FILES})
+        _icu_extract_locale_from_rb(${RESOURCE_SOURCE} RESOURCE_NAME_WE)
+        get_filename_component(SOURCE_BASENAME ${RESOURCE_SOURCE} NAME)
+        get_filename_component(ABSOLUTE_SOURCE ${RESOURCE_SOURCE} ABSOLUTE)
+
+        if(UPPER_FORMAT STREQUAL "XLIFF")
+            if(RESOURCE_NAME_WE STREQUAL "root")
+                set(XLIFF_LANGUAGE "en")
+            else(RESOURCE_NAME_WE STREQUAL "root")
+                string(REGEX REPLACE "[^a-z].*$" "" XLIFF_LANGUAGE "${RESOURCE_NAME_WE}")
+            endif(RESOURCE_NAME_WE STREQUAL "root")
+        endif(UPPER_FORMAT STREQUAL "XLIFF")
+
+        ##### <templates> #####
+        set(RESOURCE_TARGET_NAME "${RESOURCE_TARGET_PREFIX}${TARGET_SEPARATOR}${PARSED_ARGS_NAME}${TARGET_SEPARATOR}${RESOURCE_NAME_WE}")
+
+        set(RESOURCE_OUTPUT__PATH "${RESOURCE_NAME_WE}.res")
+        if(RESOURCE_NAME_WE STREQUAL "root")
+            set(RESOURCE_OUTPUT_JAVA_PATH "${PARSED_ARGS_BUNDLE}.java")
+        else(RESOURCE_NAME_WE STREQUAL "root")
+            set(RESOURCE_OUTPUT_JAVA_PATH "${PARSED_ARGS_BUNDLE}_${RESOURCE_NAME_WE}.java")
+        endif(RESOURCE_NAME_WE STREQUAL "root")
+        set(RESOURCE_OUTPUT_XLIFF_PATH "${RESOURCE_NAME_WE}.xlf")
+
+        set(GENRB__OPTIONS "")
+        set(GENRB_JAVA_OPTIONS "-j" "-b" "${PARSED_ARGS_BUNDLE}")
+        set(GENRB_XLIFF_OPTIONS "-x" "-l" "${XLIFF_LANGUAGE}")
+        ##### </templates> #####
+
+        # build <locale>.txt from <locale>.res
+        if(PARSED_ARGS_PACKAGE)
+            add_custom_command(
+                OUTPUT "${RESOURCE_OUTPUT_DIR}${RESOURCE_OUTPUT_${UPPER_FORMAT}_PATH}"
+                COMMAND ${CMAKE_COMMAND} -E chdir ${RESOURCE_GENRB_CHDIR_DIR} ${${ICU_PUBLIC_VAR_NS}_GENRB_EXECUTABLE} ${GENRB_${UPPER_FORMAT}_OPTIONS} -d ${PACKAGE_NAME_WE} ${ABSOLUTE_SOURCE}
+                DEPENDS ${RESOURCE_SOURCE}
+            )
+        else(PARSED_ARGS_PACKAGE)
+            add_custom_command(
+                OUTPUT "${RESOURCE_OUTPUT_DIR}${RESOURCE_OUTPUT_${UPPER_FORMAT}_PATH}"
+                COMMAND ${${ICU_PUBLIC_VAR_NS}_GENRB_EXECUTABLE} ${GENRB_${UPPER_FORMAT}_OPTIONS} -d ${RESOURCE_OUTPUT_DIR} ${ABSOLUTE_SOURCE}
+                DEPENDS ${RESOURCE_SOURCE}
+            )
+        endif(PARSED_ARGS_PACKAGE)
+        # dummy target (ICU+RB+<name>+<locale>) for each locale to build the <locale>.res file from its <locale>.txt by the add_custom_command above
+        add_custom_target(
+            "${RESOURCE_TARGET_NAME}" ALL
+            COMMENT ""
+            DEPENDS "${RESOURCE_OUTPUT_DIR}${RESOURCE_OUTPUT_${UPPER_FORMAT}_PATH}"
+            SOURCES ${RESOURCE_SOURCE}
+        )
+
+        if(PARSED_ARGS_DESTINATION AND NOT PARSED_ARGS_PACKAGE)
+            install(FILES "${RESOURCE_OUTPUT_DIR}${RESOURCE_OUTPUT_${UPPER_FORMAT}_PATH}" DESTINATION ${PARSED_ARGS_DESTINATION} PERMISSIONS OWNER_READ GROUP_READ WORLD_READ)
+        endif(PARSED_ARGS_DESTINATION AND NOT PARSED_ARGS_PACKAGE)
+
+        list(APPEND TARGET_RESOURCES "${RESOURCE_TARGET_NAME}")
+        list(APPEND COMPILED_RESOURCES_PATH "${RESOURCE_OUTPUT_DIR}${RESOURCE_OUTPUT_${UPPER_FORMAT}_PATH}")
+        list(APPEND COMPILED_RESOURCES_BASENAME "${RESOURCE_NAME_WE}.${BUNDLES_${UPPER_FORMAT}_SUFFIX}")
+    endforeach(RESOURCE_SOURCE)
+    # convert semicolon separated list to a space separated list
+    # NOTE: if the pkglist.txt file starts (or ends?) with a whitespace, pkgdata add an undefined symbol (named <package>_) for it
+    string(REPLACE ";" " " COMPILED_RESOURCES_BASENAME "${COMPILED_RESOURCES_BASENAME}")
+
+    if(PARSED_ARGS_PACKAGE)
+        # create a text file (pkglist.txt) with the list of the *.res to package together
+        add_custom_command(
+            OUTPUT "${PACKAGE_LIST_OUTPUT_PATH}"
+            COMMAND ${CMAKE_COMMAND} -E echo "${COMPILED_RESOURCES_BASENAME}" > "${PACKAGE_LIST_OUTPUT_PATH}"
+            DEPENDS ${COMPILED_RESOURCES_PATH}
+        )
+        # run pkgdata from pkglist.txt
+        add_custom_command(
+            OUTPUT "${PACKAGE_OUTPUT_PATH}"
+            COMMAND ${CMAKE_COMMAND} -E chdir ${RESOURCE_GENRB_CHDIR_DIR} ${${ICU_PUBLIC_VAR_NS}_PKGDATA_EXECUTABLE} -F ${PKGDATA_${TYPE}_OPTIONS} -s ${PACKAGE_NAME_WE} -p ${PACKAGE_NAME_WE} ${PACKAGE_LIST_OUTPUT_PATH}
+            DEPENDS "${PACKAGE_LIST_OUTPUT_PATH}"
+            VERBATIM
+        )
+        if(PKGDATA_LIBRARY_${TYPE}_TYPE)
+            # assert(${PARSED_ARGS_DEPENDS} != "")
+            if(NOT PARSED_ARGS_DEPENDS)
+                message(FATAL_ERROR "${__FUNCTION__}(): static and library mode imply a list of targets to link to, DEPENDS parameter missing")
+            endif(NOT PARSED_ARGS_DEPENDS)
+            add_library(${PACKAGE_TARGET_NAME} ${PKGDATA_LIBRARY_${TYPE}_TYPE} IMPORTED)
+            if(MSVC)
+                string(REGEX REPLACE "${PKGDATA_LIBRARY_SUFFIX}\$" "${CMAKE_IMPORT_LIBRARY_SUFFIX}" PACKAGE_OUTPUT_LIB "${PACKAGE_OUTPUT_PATH}")
+                set_target_properties(${PACKAGE_TARGET_NAME} PROPERTIES IMPORTED_LOCATION ${PACKAGE_OUTPUT_PATH} IMPORTED_IMPLIB ${PACKAGE_OUTPUT_LIB})
+            else(MSVC)
+                set_target_properties(${PACKAGE_TARGET_NAME} PROPERTIES IMPORTED_LOCATION ${PACKAGE_OUTPUT_PATH})
+            endif(MSVC)
+            foreach(DEPENDENCY ${PARSED_ARGS_DEPENDS})
+                target_link_libraries(${DEPENDENCY} ${PACKAGE_TARGET_NAME})
+                if(NOT PARSED_ARGS_NO_SHARED_FLAGS)
+                    get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
+                    list(LENGTH "${ENABLED_LANGUAGES}" ENABLED_LANGUAGES_LENGTH)
+                    if(ENABLED_LANGUAGES_LENGTH GREATER 1)
+                        message(WARNING "Project has more than one language enabled, skip automatic shared flags appending")
+                    else(ENABLED_LANGUAGES_LENGTH GREATER 1)
+                        set_property(TARGET "${DEPENDENCY}" APPEND PROPERTY COMPILE_FLAGS "${${ICU_PUBLIC_VAR_NS}_${ENABLED_LANGUAGES}_SHARED_FLAGS}")
+                    endif(ENABLED_LANGUAGES_LENGTH GREATER 1)
+                endif(NOT PARSED_ARGS_NO_SHARED_FLAGS)
+            endforeach(DEPENDENCY)
+            # http://www.mail-archive.com/[email protected]/msg01135.html
+            set(PACKAGE_INTERMEDIATE_TARGET_NAME "${PACKAGE_TARGET_NAME}${TARGET_SEPARATOR}DUMMY")
+            # dummy intermediate target (ICU+PKG+<name>+DUMMY) to link the package to the produced library by running pkgdata (see add_custom_command above)
+            add_custom_target(
+                ${PACKAGE_INTERMEDIATE_TARGET_NAME}
+                COMMENT ""
+                DEPENDS "${PACKAGE_OUTPUT_PATH}"
+            )
+            add_dependencies("${PACKAGE_TARGET_NAME}" "${PACKAGE_INTERMEDIATE_TARGET_NAME}")
+        else(PKGDATA_LIBRARY_${TYPE}_TYPE)
+            # dummy target (ICU+PKG+<name>) to run pkgdata (see add_custom_command above)
+            add_custom_target(
+                "${PACKAGE_TARGET_NAME}" ALL
+                COMMENT ""
+                DEPENDS "${PACKAGE_OUTPUT_PATH}"
+            )
+        endif(PKGDATA_LIBRARY_${TYPE}_TYPE)
+        # dummy target (ICU+PKG+<name>+PKGLIST) to build the file pkglist.txt
+        add_custom_target(
+            "${PACKAGE_LIST_TARGET_NAME}" ALL
+            COMMENT ""
+            DEPENDS "${PACKAGE_LIST_OUTPUT_PATH}"
+        )
+        # package => pkglist.txt
+        add_dependencies("${PACKAGE_TARGET_NAME}" "${PACKAGE_LIST_TARGET_NAME}")
+        # pkglist.txt => *.res
+        add_dependencies("${PACKAGE_LIST_TARGET_NAME}" ${TARGET_RESOURCES})
+
+        if(PARSED_ARGS_DESTINATION)
+            install(FILES "${PACKAGE_OUTPUT_PATH}" DESTINATION ${PARSED_ARGS_DESTINATION} PERMISSIONS OWNER_READ GROUP_READ WORLD_READ)
+        endif(PARSED_ARGS_DESTINATION)
+    endif(PARSED_ARGS_PACKAGE)
+
+endfunction(icu_generate_resource_bundle)
+
+########## </resource bundle support> ##########
+
+########## <debug> ##########
+
+if(${ICU_PUBLIC_VAR_NS}_DEBUG)
+
+    function(icudebug _VARNAME)
+        if(DEFINED ${ICU_PUBLIC_VAR_NS}_${_VARNAME})
+            message("${ICU_PUBLIC_VAR_NS}_${_VARNAME} = ${${ICU_PUBLIC_VAR_NS}_${_VARNAME}}")
+        else(DEFINED ${ICU_PUBLIC_VAR_NS}_${_VARNAME})
+            message("${ICU_PUBLIC_VAR_NS}_${_VARNAME} = <UNDEFINED>")
+        endif(DEFINED ${ICU_PUBLIC_VAR_NS}_${_VARNAME})
+    endfunction(icudebug)
+
+    # IN (args)
+    icudebug("FIND_COMPONENTS")
+    icudebug("FIND_REQUIRED")
+    icudebug("FIND_QUIETLY")
+    icudebug("FIND_VERSION")
+
+    # OUT
+    # Found
+    icudebug("FOUND")
+    # Flags
+    icudebug("C_FLAGS")
+    icudebug("CPP_FLAGS")
+    icudebug("CXX_FLAGS")
+    icudebug("C_SHARED_FLAGS")
+    icudebug("CPP_SHARED_FLAGS")
+    icudebug("CXX_SHARED_FLAGS")
+    # Linking
+    icudebug("INCLUDE_DIRS")
+    icudebug("LIBRARIES")
+    # Version
+    icudebug("VERSION_MAJOR")
+    icudebug("VERSION_MINOR")
+    icudebug("VERSION_PATCH")
+    icudebug("VERSION")
+    # <COMPONENT>_(FOUND|LIBRARY)
+    set(${ICU_PRIVATE_VAR_NS}_COMPONENT_VARIABLES "FOUND" "LIBRARY" "LIBRARY_RELEASE" "LIBRARY_DEBUG")
+    foreach(${ICU_PRIVATE_VAR_NS}_COMPONENT ${${ICU_PRIVATE_VAR_NS}_COMPONENTS})
+        string(TOUPPER "${${ICU_PRIVATE_VAR_NS}_COMPONENT}" ${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT)
+        foreach(${ICU_PRIVATE_VAR_NS}_COMPONENT_VARIABLE ${${ICU_PRIVATE_VAR_NS}_COMPONENT_VARIABLES})
+            icudebug("${${ICU_PRIVATE_VAR_NS}_UPPER_COMPONENT}_${${ICU_PRIVATE_VAR_NS}_COMPONENT_VARIABLE}")
+        endforeach(${ICU_PRIVATE_VAR_NS}_COMPONENT_VARIABLE)
+    endforeach(${ICU_PRIVATE_VAR_NS}_COMPONENT)
+
+endif(${ICU_PUBLIC_VAR_NS}_DEBUG)
+
+########## </debug> ##########

+ 15 - 1
code/CMakeLists.txt

@@ -657,6 +657,18 @@ ADD_ASSIMP_IMPORTER( 3MF
     D3MFOpcPackage.cpp
 )
 
+ADD_ASSIMP_IMPORTER( MMD
+  MMDCpp14.h
+  MMDImporter.cpp
+  MMDImporter.h
+  MMDPmdParser.h
+  MMDPmxParser.h
+  MMDPmxParser.cpp
+  MMDVmdParser.h
+)
+
+find_package(ICU COMPONENTS uc io REQUIRED)
+
 SET( Step_SRCS
   StepExporter.h
   StepExporter.cpp
@@ -850,9 +862,11 @@ IF (ASSIMP_BUILD_NONFREE_C4D_IMPORTER)
   INCLUDE_DIRECTORIES(${C4D_INCLUDES})
 ENDIF (ASSIMP_BUILD_NONFREE_C4D_IMPORTER)
 
+INCLUDE_DIRECTORIES(${ICU_INCLUDE_DIRS})
+
 ADD_LIBRARY( assimp ${assimp_src} )
 
-TARGET_LINK_LIBRARIES(assimp ${ZLIB_LIBRARIES} ${OPENDDL_PARSER_LIBRARIES} )
+TARGET_LINK_LIBRARIES(assimp ${ZLIB_LIBRARIES} ${OPENDDL_PARSER_LIBRARIES} ${ICU_LIBRARIES} )
 
 if(ANDROID AND ASSIMP_ANDROID_JNIIOSYSTEM)
   set(ASSIMP_ANDROID_JNIIOSYSTEM_PATH port/AndroidJNI)

+ 7 - 1
code/ImporterRegistry.cpp

@@ -188,6 +188,9 @@ corresponding preprocessor flag to selectively disable formats.
 #ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
 #   include "X3DImporter.hpp"
 #endif
+#ifndef ASSIMP_BUILD_NO_MMD_IMPORTER
+#   include "MMDImporter.h"
+#endif
 
 namespace Assimp {
 
@@ -332,11 +335,14 @@ void GetImporterInstanceList(std::vector< BaseImporter* >& out)
     out.push_back( new C4DImporter() );
 #endif
 #if ( !defined ASSIMP_BUILD_NO_3MF_IMPORTER )
-    out.push_back(new D3MFImporter() );
+    out.push_back( new D3MFImporter() );
 #endif
 #ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
     out.push_back( new X3DImporter() );
 #endif
+#ifndef ASSIMP_BUILD_NO_MMD_IMPORTER
+    out.push_back( new MMDImporter() );
+#endif
 }
 
 /** will delete all registered importers. */

+ 41 - 0
code/MMDCpp14.h

@@ -0,0 +1,41 @@
+#pragma once
+
+#ifndef MMD_CPP14_H
+
+#include <cstddef>
+#include <memory>
+#include <type_traits>
+#include <utility>
+
+namespace std {
+    template<class T> struct _Unique_if {
+        typedef unique_ptr<T> _Single_object;
+    };
+
+    template<class T> struct _Unique_if<T[]> {
+        typedef unique_ptr<T[]> _Unknown_bound;
+    };
+
+    template<class T, size_t N> struct _Unique_if<T[N]> {
+        typedef void _Known_bound;
+    };
+
+    template<class T, class... Args>
+        typename _Unique_if<T>::_Single_object
+        make_unique(Args&&... args) {
+            return unique_ptr<T>(new T(std::forward<Args>(args)...));
+        }
+
+    template<class T>
+        typename _Unique_if<T>::_Unknown_bound
+        make_unique(size_t n) {
+            typedef typename remove_extent<T>::type U;
+            return unique_ptr<T>(new U[n]());
+        }
+
+    template<class T, class... Args>
+        typename _Unique_if<T>::_Known_bound
+        make_unique(Args&&...) = delete;
+}
+
+#endif

+ 190 - 0
code/MMDImporter.cpp

@@ -0,0 +1,190 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2016, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+#ifndef ASSIMP_BUILD_NO_MMD_IMPORTER
+
+#include "DefaultIOSystem.h"
+#include "MMDImporter.h"
+#include "MMDPmxParser.h"
+#include "MMDPmdParser.h"
+#include "MMDVmdParser.h"
+//#include "IOStreamBuffer.h"
+#include <memory>
+#include <assimp/Importer.hpp>
+#include <assimp/scene.h>
+#include <assimp/ai_assert.h>
+#include <assimp/DefaultLogger.hpp>
+#include <fstream>
+#include <iomanip>
+
+static const aiImporterDesc desc = {
+    "MMD Importer",
+    "",
+    "",
+    "surfaces supported?",
+    aiImporterFlags_SupportTextFlavour,
+    0,
+    0,
+    0,
+    0,
+    "pmx"
+};
+
+namespace Assimp {
+
+using namespace std;
+
+// ------------------------------------------------------------------------------------------------
+//  Default constructor
+MMDImporter::MMDImporter() :
+    m_Buffer(),
+    //m_pRootObject( NULL ),
+    m_strAbsPath( "" )
+{
+    DefaultIOSystem io;
+    m_strAbsPath = io.getOsSeparator();
+}
+
+// ------------------------------------------------------------------------------------------------
+//  Destructor.
+MMDImporter::~MMDImporter()
+{
+    //delete m_pRootObject;
+    //m_pRootObject = NULL;
+}
+
+// ------------------------------------------------------------------------------------------------
+//  Returns true, if file is an pmx file.
+bool MMDImporter::CanRead( const std::string& pFile, IOSystem*  pIOHandler , bool checkSig ) const
+{
+    if(!checkSig) //Check File Extension
+    {
+        return SimpleExtensionCheck(pFile,"pmx");
+    }
+    else //Check file Header
+    {
+        static const char *pTokens[] = { "PMX " };
+        return BaseImporter::SearchFileHeaderForToken(pIOHandler, pFile, pTokens, 1 );
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+const aiImporterDesc* MMDImporter::GetInfo () const
+{
+    return &desc;
+}
+
+// ------------------------------------------------------------------------------------------------
+//  MMD import implementation
+void MMDImporter::InternReadFile( const std::string &file, aiScene* pScene, IOSystem* pIOHandler)
+{
+    // Read file by istream
+    std::filebuf fb;
+    if( !fb.open(file, std::ios::in | std::ios::binary ) ) {
+        throw DeadlyImportError( "Failed to open file " + file + "." );
+    }
+
+    std::istream fileStream( &fb );
+
+    // Get the file-size and validate it, throwing an exception when fails
+    fileStream.seekg(0, fileStream.end);
+    size_t fileSize = fileStream.tellg();
+    fileStream.seekg(0, fileStream.beg);
+
+    if( fileSize < sizeof(pmx::PmxModel) ) {
+        throw DeadlyImportError( file + " is too small." );
+    }
+
+    pmx::PmxModel model;
+    model.Read(&fileStream);
+
+    CreateDataFromImport(&model, pScene);
+}
+
+// ------------------------------------------------------------------------------------------------
+void MMDImporter::CreateDataFromImport(const pmx::PmxModel* pModel, aiScene* pScene)
+{
+    if( pModel == NULL ) {
+        return;
+    }
+
+    pScene->mRootNode = new aiNode;
+    if ( !pModel->model_name.empty() ) {
+        pScene->mRootNode->mName.Set(pModel->model_name);
+    }
+    else {
+        ai_assert(false);
+    }
+    
+    std::cout << pScene->mRootNode->mName.C_Str() << std::endl;
+
+    // workaround, must be deleted
+    pScene->mNumMeshes = 1;
+    pScene->mNumMaterials = 1;
+    pScene->mRootNode->mMeshes = new unsigned int[1];
+    aiMesh *pMesh = new aiMesh;
+    pScene->mRootNode->mMeshes[0] = 100;
+    // workaround
+
+/*
+    // Create nodes for the whole scene
+    std::vector<aiMesh*> MeshArray;
+    for ( size_t index = 0; index < pModel->bone_count; index++ ) {
+        createNodes( pModel, pModel->bones[i], pScene, MeshArray);
+    }
+
+    if ( pScene->mNumMeshes > 0 ) {
+        pScene->mMeshes = new aiMesh*[ MeshArray.size() ];
+        for ( size_t index = 0; index < MeshArray.size(); index++ ) {
+            pScene->mMeshes[ index ] = MeshArray[ index ];
+        }
+    }
+
+    // Create all materials
+    createMaterials( pModel, pScene );
+    */
+}
+
+// ------------------------------------------------------------------------------------------------
+
+}   // Namespace Assimp
+
+#endif // !! ASSIMP_BUILD_NO_MMD_IMPORTER

+ 100 - 0
code/MMDImporter.h

@@ -0,0 +1,100 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2016, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+#ifndef MMD_FILE_IMPORTER_H_INC
+#define MMD_FILE_IMPORTER_H_INC
+
+#include "BaseImporter.h"
+#include "MMDPmxParser.h"
+#include <assimp/material.h>
+#include <vector>
+
+struct aiMesh;
+struct aiNode;
+
+namespace Assimp {
+
+/*
+namespace MMDFile {
+    struct Object;
+    struct Model;
+}
+*/
+
+// ------------------------------------------------------------------------------------------------
+/// \class  MMDImporter
+/// \brief  Imports MMD a pmx/pmd/vmd file
+// ------------------------------------------------------------------------------------------------
+class MMDImporter : public BaseImporter {
+public:
+    /// \brief  Default constructor
+    MMDImporter();
+
+    /// \brief  Destructor
+    ~MMDImporter();
+
+public:
+    /// \brief  Returns whether the class can handle the format of the given file.
+    /// \remark See BaseImporter::CanRead() for details.
+    bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const;
+
+private:
+    //! \brief  Appends the supported extension.
+    const aiImporterDesc* GetInfo () const;
+
+    //! \brief  File import implementation.
+    void InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
+
+    //! \brief  Create the data from imported content.
+    void CreateDataFromImport(const pmx::PmxModel* pModel, aiScene* pScene);
+
+private:
+    //! Data buffer
+    std::vector<char> m_Buffer;
+    //! Pointer to root object instance
+    //MMDFile::Object *m_pRootObject;
+    //! Absolute pathname of model in file system
+    std::string m_strAbsPath;
+};
+
+// ------------------------------------------------------------------------------------------------
+
+} // Namespace Assimp
+
+#endif

+ 630 - 0
code/MMDPmdParser.h

@@ -0,0 +1,630 @@
+#pragma once
+
+#include <vector>
+#include <string>
+#include <memory>
+#include <iostream>
+#include <fstream>
+#include "MMDCpp14.h"
+
+namespace pmd
+{
+	/// ヘッダ
+	class PmdHeader
+	{
+	public:
+		/// モデル名
+		std::string name;
+		/// モデル名(英語)
+		std::string name_english;
+		/// コメント
+		std::string comment;
+		/// コメント(英語)
+		std::string comment_english;
+
+		bool Read(std::ifstream* stream)
+		{
+			char buffer[256];
+			stream->read(buffer, 20);
+			name = std::string(buffer);
+			stream->read(buffer, 256);
+			comment = std::string(buffer);
+			return true;
+		}
+
+		bool ReadExtension(std::ifstream* stream)
+		{
+			char buffer[256];
+			stream->read(buffer, 20);
+			name_english = std::string(buffer);
+			stream->read(buffer, 256);
+			comment_english = std::string(buffer);
+			return true;
+		}
+	};
+
+	/// 頂点
+	class PmdVertex
+	{
+	public:
+		/// 位置
+		float position[3];
+
+		/// 法線
+		float normal[3];
+
+		/// UV座標
+		float uv[2];
+
+		/// 関連ボーンインデックス
+		uint16_t bone_index[2];
+
+		/// ボーンウェイト
+		uint8_t bone_weight;
+
+		/// エッジ不可視
+		bool edge_invisible;
+
+		bool Read(std::ifstream* stream)
+		{
+			stream->read((char*) position, sizeof(float) * 3);
+			stream->read((char*) normal, sizeof(float) * 3);
+			stream->read((char*) uv, sizeof(float) * 2);
+			stream->read((char*) bone_index, sizeof(uint16_t) * 2);
+			stream->read((char*) &bone_weight, sizeof(uint8_t));
+			stream->read((char*) &edge_invisible, sizeof(uint8_t));
+			return true;
+		}
+	};
+
+	/// 材質
+	class PmdMaterial
+	{
+	public:
+		/// 減衰色
+		float diffuse[4];
+		/// 光沢度
+		float power;
+		/// 光沢色
+		float specular[3];
+		/// 環境色
+		float ambient[3];
+		/// トーンインデックス
+		uint8_t toon_index;
+		/// エッジ
+		uint8_t edge_flag;
+		/// インデックス数
+		uint32_t index_count;
+		/// テクスチャファイル名
+		std::string texture_filename;
+		/// スフィアファイル名
+		std::string sphere_filename;
+
+		bool Read(std::ifstream* stream)
+		{
+			char buffer[20];
+			stream->read((char*) &diffuse, sizeof(float) * 4);
+			stream->read((char*) &power, sizeof(float));
+			stream->read((char*) &specular, sizeof(float) * 3);
+			stream->read((char*) &ambient, sizeof(float) * 3);
+			stream->read((char*) &toon_index, sizeof(uint8_t));
+			stream->read((char*) &edge_flag, sizeof(uint8_t));
+			stream->read((char*) &index_count, sizeof(uint32_t));
+			stream->read((char*) &buffer, sizeof(char) * 20);
+			char* pstar = strchr(buffer, '*');
+			if (NULL == pstar)
+			{
+				texture_filename = std::string(buffer);
+				sphere_filename.clear();
+			}
+			else {
+				*pstar = NULL;
+				texture_filename = std::string(buffer);
+				sphere_filename = std::string(pstar+1);
+			}
+			return true;
+		}
+	};
+
+	enum class BoneType : uint8_t
+	{
+		Rotation,
+		RotationAndMove,
+		IkEffector,
+		Unknown,
+		IkEffectable,
+		RotationEffectable,
+		IkTarget,
+		Invisible,
+		Twist,
+		RotationMovement
+	};
+
+	/// ボーン
+	class PmdBone
+	{
+	public:
+		/// ボーン名
+		std::string name;
+		/// ボーン名(英語)
+		std::string name_english;
+		/// 親ボーン番号
+		uint16_t parent_bone_index;
+		/// 末端ボーン番号
+		uint16_t tail_pos_bone_index;
+		/// ボーン種類
+		BoneType bone_type;
+		/// IKボーン番号
+		uint16_t ik_parent_bone_index;
+		/// ボーンのヘッドの位置
+		float bone_head_pos[3];
+
+		void Read(std::istream *stream)
+		{
+			char buffer[20];
+			stream->read(buffer, 20);
+			name = std::string(buffer);
+			stream->read((char*) &parent_bone_index, sizeof(uint16_t));
+			stream->read((char*) &tail_pos_bone_index, sizeof(uint16_t));
+			stream->read((char*) &bone_type, sizeof(uint8_t));
+			stream->read((char*) &ik_parent_bone_index, sizeof(uint16_t));
+			stream->read((char*) &bone_head_pos, sizeof(float) * 3);
+		}
+
+		void ReadExpantion(std::istream *stream)
+		{
+			char buffer[20];
+			stream->read(buffer, 20);
+			name_english = std::string(buffer);
+		}
+	};
+
+	/// IK
+	class PmdIk
+	{
+	public:
+		/// IKボーン番号
+		uint16_t ik_bone_index;
+		/// IKターゲットボーン番号
+		uint16_t target_bone_index;
+		/// 再帰回数
+		uint16_t interations;
+		/// 角度制限
+		float angle_limit;
+		/// 影響下ボーン番号
+		std::vector<uint16_t> ik_child_bone_index;
+
+		void Read(std::istream *stream)
+		{
+			stream->read((char *) &ik_bone_index, sizeof(uint16_t));
+			stream->read((char *) &target_bone_index, sizeof(uint16_t));
+			uint8_t ik_chain_length;
+			stream->read((char*) &ik_chain_length, sizeof(uint8_t));
+			stream->read((char *) &interations, sizeof(uint16_t));
+			stream->read((char *) &angle_limit, sizeof(float));
+			ik_child_bone_index.resize(ik_chain_length);
+			for (int i = 0; i < ik_chain_length; i++)
+			{
+				stream->read((char *) &ik_child_bone_index[i], sizeof(uint16_t));
+			}
+		}
+	};
+
+	class PmdFaceVertex
+	{
+	public:
+		int vertex_index;
+		float position[3];
+
+		void Read(std::istream *stream)
+		{
+			stream->read((char *) &vertex_index, sizeof(int));
+			stream->read((char *) position, sizeof(float) * 3);
+		}
+	};
+
+	enum class FaceCategory : uint8_t
+	{
+		Base,
+		Eyebrow,
+		Eye,
+		Mouth,
+		Other
+	};
+
+	class PmdFace
+	{
+	public:
+		std::string name;
+		FaceCategory type;
+		std::vector<PmdFaceVertex> vertices;
+		std::string name_english;
+
+		void Read(std::istream *stream)
+		{
+			char buffer[20];
+			stream->read(buffer, 20);
+			name = std::string(buffer);
+			int vertex_count;
+			stream->read((char*) &vertex_count, sizeof(int));
+			stream->read((char*) &type, sizeof(uint8_t));
+			vertices.resize(vertex_count);
+			for (int i = 0; i < vertex_count; i++)
+			{
+				vertices[i].Read(stream);
+			}
+		}
+
+		void ReadExpantion(std::istream *stream)
+		{
+			char buffer[20];
+			stream->read(buffer, 20);
+			name_english = std::string(buffer);
+		}
+	};
+
+	/// ボーン枠用の枠名
+	class PmdBoneDispName
+	{
+	public:
+		std::string bone_disp_name;
+		std::string bone_disp_name_english;
+
+		void Read(std::istream *stream)
+		{
+			char buffer[50];
+			stream->read(buffer, 50);
+			bone_disp_name = std::string(buffer);
+			bone_disp_name_english.clear();
+		}
+		void ReadExpantion(std::istream *stream)
+		{
+			char buffer[50];
+			stream->read(buffer, 50);
+			bone_disp_name_english = std::string(buffer);
+		}
+	};
+
+	class PmdBoneDisp
+	{
+	public:
+		uint16_t bone_index;
+		uint8_t bone_disp_index;
+
+		void Read(std::istream *stream)
+		{
+			stream->read((char*) &bone_index, sizeof(uint16_t));
+			stream->read((char*) &bone_disp_index, sizeof(uint8_t));
+		}
+	};
+
+	/// 衝突形状
+	enum class RigidBodyShape : uint8_t
+	{
+		/// 球
+		Sphere = 0,
+		/// 直方体
+		Box = 1,
+		/// カプセル
+		Cpusel = 2
+	};
+
+	/// 剛体タイプ
+	enum class RigidBodyType : uint8_t
+	{
+		/// ボーン追従
+		BoneConnected = 0,
+		/// 物理演算
+		Physics = 1,
+		/// 物理演算(Bone位置合せ)
+		ConnectedPhysics = 2
+	};
+
+	/// 剛体
+	class PmdRigidBody
+	{
+	public:
+		/// 名前
+		std::string name;
+		/// 関連ボーン番号
+		uint16_t related_bone_index;
+		/// グループ番号
+		uint8_t group_index;
+		/// マスク
+		uint16_t mask;
+		/// 形状
+		RigidBodyShape shape;
+		/// 大きさ
+		float size[3];
+		/// 位置
+		float position[3];
+		/// 回転
+		float orientation[3];
+		/// 質量
+		float weight;
+		/// 移動ダンピング
+		float linear_damping;
+		/// 回転ダンピング
+		float anglar_damping;
+		/// 反発係数
+		float restitution;
+		/// 摩擦係数
+		float friction;
+		/// 演算方法
+		RigidBodyType rigid_type;
+
+		void Read(std::istream *stream)
+		{
+			char buffer[20];
+			stream->read(buffer, sizeof(char) * 20);
+			name = (std::string(buffer));
+			stream->read((char*) &related_bone_index, sizeof(uint16_t));
+			stream->read((char*) &group_index, sizeof(uint8_t));
+			stream->read((char*) &mask, sizeof(uint16_t));
+			stream->read((char*) &shape, sizeof(uint8_t));
+			stream->read((char*) size, sizeof(float) * 3);
+			stream->read((char*) position, sizeof(float) * 3);
+			stream->read((char*) orientation, sizeof(float) * 3);
+			stream->read((char*) &weight, sizeof(float));
+			stream->read((char*) &linear_damping, sizeof(float));
+			stream->read((char*) &anglar_damping, sizeof(float));
+			stream->read((char*) &restitution, sizeof(float));
+			stream->read((char*) &friction, sizeof(float));
+			stream->read((char*) &rigid_type, sizeof(char));
+		}
+	};
+
+	/// 剛体の拘束
+	class PmdConstraint
+	{
+	public:
+		/// 名前
+		std::string name;
+		/// 剛体Aのインデックス
+		uint32_t rigid_body_index_a;
+		/// 剛体Bのインデックス
+		uint32_t rigid_body_index_b;
+		/// 位置
+		float position[3];
+		/// 回転
+		float orientation[3];
+		/// 最小移動制限
+		float linear_lower_limit[3];
+		/// 最大移動制限
+		float linear_upper_limit[3];
+		/// 最小回転制限
+		float angular_lower_limit[3];
+		/// 最大回転制限
+		float angular_upper_limit[3];
+		/// 移動に対する復元力
+		float linear_stiffness[3];
+		/// 回転に対する復元力
+		float angular_stiffness[3];
+
+		void Read(std::istream *stream)
+		{
+			char buffer[20];
+			stream->read(buffer, 20);
+			name = std::string(buffer);
+			stream->read((char *) &rigid_body_index_a, sizeof(uint32_t));
+			stream->read((char *) &rigid_body_index_b, sizeof(uint32_t));
+			stream->read((char *) position, sizeof(float) * 3);
+			stream->read((char *) orientation, sizeof(float) * 3);
+			stream->read((char *) linear_lower_limit, sizeof(float) * 3);
+			stream->read((char *) linear_upper_limit, sizeof(float) * 3);
+			stream->read((char *) angular_lower_limit, sizeof(float) * 3);
+			stream->read((char *) angular_upper_limit, sizeof(float) * 3);
+			stream->read((char *) linear_stiffness, sizeof(float) * 3);
+			stream->read((char *) angular_stiffness, sizeof(float) * 3);
+		}
+	};
+
+	/// PMDモデル
+	class PmdModel
+	{
+	public:
+		float version;
+		PmdHeader header;
+		std::vector<PmdVertex> vertices;
+		std::vector<uint16_t> indices;
+		std::vector<PmdMaterial> materials;
+		std::vector<PmdBone> bones;
+		std::vector<PmdIk> iks;
+		std::vector<PmdFace> faces;
+		std::vector<uint16_t> faces_indices;
+		std::vector<PmdBoneDispName> bone_disp_name;
+		std::vector<PmdBoneDisp> bone_disp;
+		std::vector<std::string> toon_filenames;
+		std::vector<PmdRigidBody> rigid_bodies;
+		std::vector<PmdConstraint> constraints;
+
+		static std::unique_ptr<PmdModel> LoadFromFile(const char *filename)
+		{
+			std::ifstream stream(filename, std::ios::binary);
+			if (stream.fail())
+			{
+				std::cerr << "could not open \"" << filename << "\"" << std::endl;
+				return nullptr;
+			}
+			auto result = LoadFromStream(&stream);
+			stream.close();
+			return result;
+		}
+
+		/// ファイルからPmdModelを生成する
+		static std::unique_ptr<PmdModel> LoadFromStream(std::ifstream *stream)
+		{
+			auto result = std::make_unique<PmdModel>();
+			char buffer[100];
+
+			// magic
+			char magic[3];
+			stream->read(magic, 3);
+			if (magic[0] != 'P' || magic[1] != 'm' || magic[2] != 'd')
+			{
+				std::cerr << "invalid file" << std::endl;
+				return nullptr;
+			}
+
+			// version
+			stream->read((char*) &(result->version), sizeof(float));
+			if (result ->version != 1.0f)
+			{
+				std::cerr << "invalid version" << std::endl;
+				return nullptr;
+			}
+
+			// header
+			result->header.Read(stream);
+
+			// vertices
+			uint32_t vertex_num;
+			stream->read((char*) &vertex_num, sizeof(uint32_t));
+			result->vertices.resize(vertex_num);
+			for (uint32_t i = 0; i < vertex_num; i++)
+			{
+				result->vertices[i].Read(stream);
+			}
+
+			// indices
+			uint32_t index_num;
+			stream->read((char*) &index_num, sizeof(uint32_t));
+			result->indices.resize(index_num);
+			for (uint32_t i = 0; i < index_num; i++)
+			{
+				stream->read((char*) &result->indices[i], sizeof(uint16_t));
+			}
+
+			// materials
+			uint32_t material_num;
+			stream->read((char*) &material_num, sizeof(uint32_t));
+			result->materials.resize(material_num);
+			for (uint32_t i = 0; i < material_num; i++)
+			{
+				result->materials[i].Read(stream);
+			}
+
+			// bones
+			uint16_t bone_num;
+			stream->read((char*) &bone_num, sizeof(uint16_t));
+			result->bones.resize(bone_num);
+			for (uint32_t i = 0; i < bone_num; i++)
+			{
+				result->bones[i].Read(stream);
+			}
+
+			// iks
+			uint16_t ik_num;
+			stream->read((char*) &ik_num, sizeof(uint16_t));
+			result->iks.resize(ik_num);
+			for (uint32_t i = 0; i < ik_num; i++)
+			{
+				result->iks[i].Read(stream);
+			}
+
+			// faces
+			uint16_t face_num;
+			stream->read((char*) &face_num, sizeof(uint16_t));
+			result->faces.resize(face_num);
+			for (uint32_t i = 0; i < face_num; i++)
+			{
+				result->faces[i].Read(stream);
+			}
+
+			// face frames
+			uint8_t face_frame_num;
+			stream->read((char*) &face_frame_num, sizeof(uint8_t));
+			result->faces_indices.resize(face_frame_num);
+			for (uint32_t i = 0; i < face_frame_num; i++)
+			{
+				stream->read((char*) &result->faces_indices[i], sizeof(uint16_t));
+			}
+
+			// bone names
+			uint8_t bone_disp_num;
+			stream->read((char*) &bone_disp_num, sizeof(uint8_t));
+			result->bone_disp_name.resize(bone_disp_num);
+			for (uint32_t i = 0; i < bone_disp_num; i++)
+			{
+				result->bone_disp_name[i].Read(stream);
+			}
+
+			// bone frame
+			uint32_t bone_frame_num;
+			stream->read((char*) &bone_frame_num, sizeof(uint32_t));
+			result->bone_disp.resize(bone_frame_num);
+			for (uint32_t i = 0; i < bone_frame_num; i++)
+			{
+				result->bone_disp[i].Read(stream);
+			}
+
+			// english name
+			bool english;
+			stream->read((char*) &english, sizeof(char));
+			if (english)
+			{
+				result->header.ReadExtension(stream);
+				for (uint32_t i = 0; i < bone_num; i++)
+				{
+					result->bones[i].ReadExpantion(stream);
+				}
+				for (uint32_t i = 0; i < face_num; i++)
+				{
+					if (result->faces[i].type == pmd::FaceCategory::Base)
+					{
+						continue;
+					}
+					result->faces[i].ReadExpantion(stream);
+				}
+				for (uint32_t i = 0; i < result->bone_disp_name.size(); i++)
+				{
+					result->bone_disp_name[i].ReadExpantion(stream);
+				}
+			}
+
+			// toon textures
+			if (stream->peek() == std::ios::traits_type::eof())
+			{
+				result->toon_filenames.clear();
+			}
+			else {
+				result->toon_filenames.resize(10);
+				for (uint32_t i = 0; i < 10; i++)
+				{
+					stream->read(buffer, 100);
+					result->toon_filenames[i] = std::string(buffer);
+				}
+			}
+
+			// physics
+			if (stream->peek() == std::ios::traits_type::eof())
+			{
+				result->rigid_bodies.clear();
+				result->constraints.clear();
+			}
+			else {
+				uint32_t rigid_body_num;
+				stream->read((char*) &rigid_body_num, sizeof(uint32_t));
+				result->rigid_bodies.resize(rigid_body_num);
+				for (uint32_t i = 0; i < rigid_body_num; i++)
+				{
+					result->rigid_bodies[i].Read(stream);
+				}
+				uint32_t constraint_num;
+				stream->read((char*) &constraint_num, sizeof(uint32_t));
+				result->constraints.resize(constraint_num);
+				for (uint32_t i = 0; i < constraint_num; i++)
+				{
+					result->constraints[i].Read(stream);
+				}
+			}
+
+			if (stream->peek() != std::ios::traits_type::eof())
+			{
+				std::cerr << "there is unknown data" << std::endl;
+			}
+
+			return result;
+		}
+	};
+}

+ 625 - 0
code/MMDPmxParser.cpp

@@ -0,0 +1,625 @@
+#include <utility>
+#include "MMDPmxParser.h"
+#ifndef __unix__
+#include "EncodingHelper.h"
+#else
+#include <unicode/ucnv.h>
+#endif
+
+namespace pmx
+{
+	/// インデックス値を読み込む
+	int ReadIndex(std::istream *stream, int size)
+	{
+		switch (size)
+		{
+		case 1:
+			uint8_t tmp8;
+			stream->read((char*) &tmp8, sizeof(uint8_t));
+			if (255 == tmp8)
+			{
+				return -1;
+			}
+			else {
+				return (int) tmp8;
+			}
+		case 2:
+			uint16_t tmp16;
+			stream->read((char*) &tmp16, sizeof(uint16_t));
+			if (65535 == tmp16)
+			{
+				return -1;
+			}
+			else {
+				return (int) tmp16;
+			}
+		case 4:
+			int tmp32;
+			stream->read((char*) &tmp32, sizeof(int));
+			return tmp32;
+		default:
+			return -1;
+		}
+	}
+
+	/// 文字列を読み込む
+	utfstring ReadString(std::istream *stream, uint8_t encoding)
+	{
+#ifndef __unix__
+		oguna::EncodingConverter converter = oguna::EncodingConverter();
+#endif
+		int size;
+		stream->read((char*) &size, sizeof(int));
+		std::vector<char> buffer;
+		if (size == 0)
+		{
+#ifndef __unix__
+			return utfstring(L"");
+#else
+			return utfstring("");
+#endif
+		}
+		buffer.reserve(size);
+		stream->read((char*) buffer.data(), size);
+		if (encoding == 0)
+		{
+			// UTF16
+#ifndef __unix__
+			return utfstring((wchar_t*) buffer.data(), size / 2);
+#else
+			utfstring result;
+			std::vector<char> outbuf;
+			outbuf.reserve(size*2);
+
+			// Always remember to set U_ZERO_ERROR before calling ucnv_convert(),
+			// otherwise the function will fail.
+			UErrorCode err = U_ZERO_ERROR;
+			size = ucnv_convert("UTF-8", "UTF-16LE", (char*)outbuf.data(), outbuf.capacity(), buffer.data(), size, &err);
+			if(!U_SUCCESS(err)) {
+				std::cout << "oops, something wrong?" << std::endl;
+				std::cout << u_errorName(err) << std::endl;
+				exit(-1);
+			}
+			
+			result.assign((const char*)outbuf.data(), size);
+			return result;
+#endif
+		}
+		else
+		{
+			// UTF8
+#ifndef __unix__
+			utfstring result;
+			converter.Utf8ToUtf16(buffer.data(), size, &result);
+			return result;
+#else
+			return utfstring((const char*)buffer.data(), size);
+#endif
+		}
+	}
+
+	void PmxSetting::Read(std::istream *stream)
+	{
+		uint8_t count;
+		stream->read((char*) &count, sizeof(uint8_t));
+		if (count < 8)
+		{
+			throw;
+		}
+		stream->read((char*) &encoding, sizeof(uint8_t));
+		stream->read((char*) &uv, sizeof(uint8_t));
+		stream->read((char*) &vertex_index_size, sizeof(uint8_t));
+		stream->read((char*) &texture_index_size, sizeof(uint8_t));
+		stream->read((char*) &material_index_size, sizeof(uint8_t));
+		stream->read((char*) &bone_index_size, sizeof(uint8_t));
+		stream->read((char*) &morph_index_size, sizeof(uint8_t));
+		stream->read((char*) &rigidbody_index_size, sizeof(uint8_t));
+		uint8_t temp;
+		for (int i = 8; i < count; i++)
+		{
+			stream->read((char*)&temp, sizeof(uint8_t));
+		}
+	}
+
+	void PmxVertexSkinningBDEF1::Read(std::istream *stream, PmxSetting *setting)
+	{
+		this->bone_index = ReadIndex(stream, setting->bone_index_size);
+	}
+
+	void PmxVertexSkinningBDEF2::Read(std::istream *stream, PmxSetting *setting)
+	{
+		this->bone_index1 = ReadIndex(stream, setting->bone_index_size);
+		this->bone_index2 = ReadIndex(stream, setting->bone_index_size);
+		stream->read((char*) &this->bone_weight, sizeof(float));
+	}
+
+	void PmxVertexSkinningBDEF4::Read(std::istream *stream, PmxSetting *setting)
+	{
+		this->bone_index1 = ReadIndex(stream, setting->bone_index_size);
+		this->bone_index2 = ReadIndex(stream, setting->bone_index_size);
+		this->bone_index3 = ReadIndex(stream, setting->bone_index_size);
+		this->bone_index4 = ReadIndex(stream, setting->bone_index_size);
+		stream->read((char*) &this->bone_weight1, sizeof(float));
+		stream->read((char*) &this->bone_weight2, sizeof(float));
+		stream->read((char*) &this->bone_weight3, sizeof(float));
+		stream->read((char*) &this->bone_weight4, sizeof(float));
+	}
+
+	void PmxVertexSkinningSDEF::Read(std::istream *stream, PmxSetting *setting)
+	{
+		this->bone_index1 = ReadIndex(stream, setting->bone_index_size);
+		this->bone_index2 = ReadIndex(stream, setting->bone_index_size);
+		stream->read((char*) &this->bone_weight, sizeof(float));
+		stream->read((char*) this->sdef_c, sizeof(float) * 3);
+		stream->read((char*) this->sdef_r0, sizeof(float) * 3);
+		stream->read((char*) this->sdef_r1, sizeof(float) * 3);
+	}
+
+	void PmxVertexSkinningQDEF::Read(std::istream *stream, PmxSetting *setting)
+	{
+		this->bone_index1 = ReadIndex(stream, setting->bone_index_size);
+		this->bone_index2 = ReadIndex(stream, setting->bone_index_size);
+		this->bone_index3 = ReadIndex(stream, setting->bone_index_size);
+		this->bone_index4 = ReadIndex(stream, setting->bone_index_size);
+		stream->read((char*) &this->bone_weight1, sizeof(float));
+		stream->read((char*) &this->bone_weight2, sizeof(float));
+		stream->read((char*) &this->bone_weight3, sizeof(float));
+		stream->read((char*) &this->bone_weight4, sizeof(float));
+	}
+
+	void PmxVertex::Read(std::istream *stream, PmxSetting *setting)
+	{
+		stream->read((char*) this->positon, sizeof(float) * 3);
+		stream->read((char*) this->normal, sizeof(float) * 3);
+		stream->read((char*) this->uv, sizeof(float) * 2);
+		for (int i = 0; i < setting->uv; ++i)
+		{
+			stream->read((char*) this->uva[i], sizeof(float) * 4);
+		}
+		stream->read((char*) &this->skinning_type, sizeof(PmxVertexSkinningType));
+		switch (this->skinning_type)
+		{
+		case PmxVertexSkinningType::BDEF1:
+			this->skinning = std::make_unique<PmxVertexSkinningBDEF1>();
+			break;
+		case PmxVertexSkinningType::BDEF2:
+			this->skinning = std::make_unique<PmxVertexSkinningBDEF2>();
+			break;
+		case PmxVertexSkinningType::BDEF4:
+			this->skinning = std::make_unique<PmxVertexSkinningBDEF4>();
+			break;
+		case PmxVertexSkinningType::SDEF:
+			this->skinning = std::make_unique<PmxVertexSkinningSDEF>();
+			break;
+		case PmxVertexSkinningType::QDEF:
+			this->skinning = std::make_unique<PmxVertexSkinningQDEF>();
+			break;
+		default:
+			throw "invalid skinning type";
+		}
+		this->skinning->Read(stream, setting);
+		stream->read((char*) &this->edge, sizeof(float));
+	}
+
+	void PmxMaterial::Read(std::istream *stream, PmxSetting *setting)
+	{
+		this->material_name = std::move(ReadString(stream, setting->encoding));
+		this->material_english_name = std::move(ReadString(stream, setting->encoding));
+		stream->read((char*) this->diffuse, sizeof(float) * 4);
+		stream->read((char*) this->specular, sizeof(float) * 3);
+		stream->read((char*) &this->specularlity, sizeof(float));
+		stream->read((char*) this->ambient, sizeof(float) * 3);
+		stream->read((char*) &this->flag, sizeof(uint8_t));
+		stream->read((char*) this->edge_color, sizeof(float) * 4);
+		stream->read((char*) &this->edge_size, sizeof(float));
+		this->diffuse_texture_index = ReadIndex(stream, setting->texture_index_size);
+		this->sphere_texture_index = ReadIndex(stream, setting->texture_index_size);
+		stream->read((char*) &this->sphere_op_mode, sizeof(uint8_t));
+		stream->read((char*) &this->common_toon_flag, sizeof(uint8_t));
+		if (this->common_toon_flag)
+		{
+			stream->read((char*) &this->toon_texture_index, sizeof(uint8_t));
+		}
+		else {
+			this->toon_texture_index = ReadIndex(stream, setting->texture_index_size);
+		}
+		this->memo = std::move(ReadString(stream, setting->encoding));
+		stream->read((char*) &this->index_count, sizeof(int));
+	}
+
+	void PmxIkLink::Read(std::istream *stream, PmxSetting *setting)
+	{
+		this->link_target = ReadIndex(stream, setting->bone_index_size);
+		stream->read((char*) &this->angle_lock, sizeof(uint8_t));
+		if (angle_lock == 1)
+		{
+			stream->read((char*) this->max_radian, sizeof(float) * 3);
+			stream->read((char*) this->min_radian, sizeof(float) * 3);
+		}
+	}
+
+	void PmxBone::Read(std::istream *stream, PmxSetting *setting)
+	{
+		this->bone_name = std::move(ReadString(stream, setting->encoding));
+		this->bone_english_name = std::move(ReadString(stream, setting->encoding));
+		stream->read((char*) this->position, sizeof(float) * 3);
+		this->parent_index = ReadIndex(stream, setting->bone_index_size);
+		stream->read((char*) &this->level, sizeof(int));
+		stream->read((char*) &this->bone_flag, sizeof(uint16_t));
+		if (this->bone_flag & 0x0001) {
+			this->target_index = ReadIndex(stream, setting->bone_index_size);
+		}
+		else {
+			stream->read((char*)this->offset, sizeof(float) * 3);
+		}
+		if (this->bone_flag & (0x0100 | 0x0200)) {
+			this->grant_parent_index = ReadIndex(stream, setting->bone_index_size);
+			stream->read((char*) &this->grant_weight, sizeof(float));
+		}
+		if (this->bone_flag & 0x0400) {
+			stream->read((char*)this->lock_axis_orientation, sizeof(float) * 3);
+		}
+		if (this->bone_flag & 0x0800) {
+			stream->read((char*)this->local_axis_x_orientation, sizeof(float) * 3);
+			stream->read((char*)this->local_axis_y_orientation, sizeof(float) * 3);
+		}
+		if (this->bone_flag & 0x2000) {
+			stream->read((char*) &this->key, sizeof(int));
+		}
+		if (this->bone_flag & 0x0020) {
+			this->ik_target_bone_index = ReadIndex(stream, setting->bone_index_size);
+			stream->read((char*) &ik_loop, sizeof(int));
+			stream->read((char*) &ik_loop_angle_limit, sizeof(float));
+			stream->read((char*) &ik_link_count, sizeof(int));
+			this->ik_links = std::make_unique<PmxIkLink []>(ik_link_count);
+			for (int i = 0; i < ik_link_count; i++) {
+				ik_links[i].Read(stream, setting);
+			}
+		}
+	}
+
+	void PmxMorphVertexOffset::Read(std::istream *stream, PmxSetting *setting)
+	{
+		this->vertex_index = ReadIndex(stream, setting->vertex_index_size);
+		stream->read((char*)this->position_offset, sizeof(float) * 3);
+	}
+
+	void PmxMorphUVOffset::Read(std::istream *stream, PmxSetting *setting)
+	{
+		this->vertex_index = ReadIndex(stream, setting->vertex_index_size);
+		stream->read((char*)this->uv_offset, sizeof(float) * 4);
+	}
+
+	void PmxMorphBoneOffset::Read(std::istream *stream, PmxSetting *setting)
+	{
+		this->bone_index = ReadIndex(stream, setting->bone_index_size);
+		stream->read((char*)this->translation, sizeof(float) * 3);
+		stream->read((char*)this->rotation, sizeof(float) * 4);
+	}
+
+	void PmxMorphMaterialOffset::Read(std::istream *stream, PmxSetting *setting)
+	{
+		this->material_index = ReadIndex(stream, setting->material_index_size);
+		stream->read((char*) &this->offset_operation, sizeof(uint8_t));
+		stream->read((char*)this->diffuse, sizeof(float) * 4);
+		stream->read((char*)this->specular, sizeof(float) * 3);
+		stream->read((char*) &this->specularity, sizeof(float));
+		stream->read((char*)this->ambient, sizeof(float) * 3);
+		stream->read((char*)this->edge_color, sizeof(float) * 4);
+		stream->read((char*) &this->edge_size, sizeof(float));
+		stream->read((char*)this->texture_argb, sizeof(float) * 4);
+		stream->read((char*)this->sphere_texture_argb, sizeof(float) * 4);
+		stream->read((char*)this->toon_texture_argb, sizeof(float) * 4);
+	}
+
+	void PmxMorphGroupOffset::Read(std::istream *stream, PmxSetting *setting)
+	{
+		this->morph_index = ReadIndex(stream, setting->morph_index_size);
+		stream->read((char*) &this->morph_weight, sizeof(float));
+	}
+
+	void PmxMorphFlipOffset::Read(std::istream *stream, PmxSetting *setting)
+	{
+		this->morph_index = ReadIndex(stream, setting->morph_index_size);
+		stream->read((char*) &this->morph_value, sizeof(float));
+	}
+
+	void PmxMorphImplusOffset::Read(std::istream *stream, PmxSetting *setting)
+	{
+		this->rigid_body_index = ReadIndex(stream, setting->rigidbody_index_size);
+		stream->read((char*) &this->is_local, sizeof(uint8_t));
+		stream->read((char*)this->velocity, sizeof(float) * 3);
+		stream->read((char*)this->angular_torque, sizeof(float) * 3);
+	}
+
+	void PmxMorph::Read(std::istream *stream, PmxSetting *setting)
+	{
+		this->morph_name = ReadString(stream, setting->encoding);
+		this->morph_english_name = ReadString(stream, setting->encoding);
+		stream->read((char*) &category, sizeof(MorphCategory));
+		stream->read((char*) &morph_type, sizeof(MorphType));
+		stream->read((char*) &this->offset_count, sizeof(int));
+		switch (this->morph_type)
+		{
+		case MorphType::Group:
+			group_offsets = std::make_unique<PmxMorphGroupOffset []>(this->offset_count);
+			for (int i = 0; i < offset_count; i++)
+			{
+				group_offsets[i].Read(stream, setting);
+			}
+			break;
+		case MorphType::Vertex:
+			vertex_offsets = std::make_unique<PmxMorphVertexOffset []>(this->offset_count);
+			for (int i = 0; i < offset_count; i++)
+			{
+				vertex_offsets[i].Read(stream, setting);
+			}
+			break;
+		case MorphType::Bone:
+			bone_offsets = std::make_unique<PmxMorphBoneOffset []>(this->offset_count);
+			for (int i = 0; i < offset_count; i++)
+			{
+				bone_offsets[i].Read(stream, setting);
+			}
+			break;
+		case MorphType::Matrial:
+			material_offsets = std::make_unique<PmxMorphMaterialOffset []>(this->offset_count);
+			for (int i = 0; i < offset_count; i++)
+			{
+				material_offsets[i].Read(stream, setting);
+			}
+			break;
+		case MorphType::UV:
+		case MorphType::AdditionalUV1:
+		case MorphType::AdditionalUV2:
+		case MorphType::AdditionalUV3:
+		case MorphType::AdditionalUV4:
+			uv_offsets = std::make_unique<PmxMorphUVOffset []>(this->offset_count);
+			for (int i = 0; i < offset_count; i++)
+			{
+				uv_offsets[i].Read(stream, setting);
+			}
+			break;
+		default:
+			throw;
+		}
+	}
+
+	void PmxFrameElement::Read(std::istream *stream, PmxSetting *setting)
+	{
+		stream->read((char*) &this->element_target, sizeof(uint8_t));
+		if (this->element_target == 0x00)
+		{
+			this->index = ReadIndex(stream, setting->bone_index_size);
+		}
+		else {
+			this->index = ReadIndex(stream, setting->morph_index_size);
+		}
+	}
+
+	void PmxFrame::Read(std::istream *stream, PmxSetting *setting)
+	{
+		this->frame_name = ReadString(stream, setting->encoding);
+		this->frame_english_name = ReadString(stream, setting->encoding);
+		stream->read((char*) &this->frame_flag, sizeof(uint8_t));
+		stream->read((char*) &this->element_count, sizeof(int));
+		this->elements = std::make_unique<PmxFrameElement []>(this->element_count);
+		for (int i = 0; i < this->element_count; i++)
+		{
+			this->elements[i].Read(stream, setting);
+		}
+	}
+
+	void PmxRigidBody::Read(std::istream *stream, PmxSetting *setting)
+	{
+		this->girid_body_name = ReadString(stream, setting->encoding);
+		this->girid_body_english_name = ReadString(stream, setting->encoding);
+		this->target_bone = ReadIndex(stream, setting->bone_index_size);
+		stream->read((char*) &this->group, sizeof(uint8_t));
+		stream->read((char*) &this->mask, sizeof(uint16_t));
+		stream->read((char*) &this->shape, sizeof(uint8_t));
+		stream->read((char*) this->size, sizeof(float) * 3);
+		stream->read((char*) this->position, sizeof(float) * 3);
+		stream->read((char*) this->orientation, sizeof(float) * 3);
+		stream->read((char*) &this->mass, sizeof(float));
+		stream->read((char*) &this->move_attenuation, sizeof(float));
+		stream->read((char*) &this->rotation_attenuation, sizeof(float));
+		stream->read((char*) &this->repulsion, sizeof(float));
+		stream->read((char*) &this->friction, sizeof(float));
+		stream->read((char*) &this->physics_calc_type, sizeof(uint8_t));
+	}
+
+	void PmxJointParam::Read(std::istream *stream, PmxSetting *setting)
+	{
+		this->rigid_body1 = ReadIndex(stream, setting->rigidbody_index_size);
+		this->rigid_body2 = ReadIndex(stream, setting->rigidbody_index_size);
+		stream->read((char*) this->position, sizeof(float) * 3);
+		stream->read((char*) this->orientaiton, sizeof(float) * 3);
+		stream->read((char*) this->move_limitation_min, sizeof(float) * 3);
+		stream->read((char*) this->move_limitation_max, sizeof(float) * 3);
+		stream->read((char*) this->rotation_limitation_min, sizeof(float) * 3);
+		stream->read((char*) this->rotation_limitation_max, sizeof(float) * 3);
+		stream->read((char*) this->spring_move_coefficient, sizeof(float) * 3);
+		stream->read((char*) this->spring_rotation_coefficient, sizeof(float) * 3);
+	}
+
+	void PmxJoint::Read(std::istream *stream, PmxSetting *setting)
+	{
+		this->joint_name = ReadString(stream, setting->encoding);
+		this->joint_english_name = ReadString(stream, setting->encoding);
+		stream->read((char*) &this->joint_type, sizeof(uint8_t));
+		this->param.Read(stream, setting);
+	}
+
+	void PmxAncherRigidBody::Read(std::istream *stream, PmxSetting *setting)
+	{
+		this->related_rigid_body = ReadIndex(stream, setting->rigidbody_index_size);
+		this->related_vertex = ReadIndex(stream, setting->vertex_index_size);
+		stream->read((char*) &this->is_near, sizeof(uint8_t));
+	}
+
+	void PmxSoftBody::Read(std::istream *stream, PmxSetting *setting)
+	{
+		// 未実装
+		std::cerr << "Not Implemented Exception" << std::endl;
+		throw;
+	}
+
+	void PmxModel::Init()
+	{
+		this->version = 0.0f;
+		this->model_name.clear();
+		this->model_english_name.clear();
+		this->model_comment.clear();
+		this->model_english_comment.clear();
+		this->vertex_count = 0;
+		this->vertices = nullptr;
+		this->index_count = 0;
+		this->indices = nullptr;
+		this->texture_count = 0;
+		this->textures = nullptr;
+		this->material_count = 0;
+		this->materials = nullptr;
+		this->bone_count = 0;
+		this->bones = nullptr;
+		this->morph_count = 0;
+		this->morphs = nullptr;
+		this->frame_count = 0;
+		this->frames = nullptr;
+		this->rigid_body_count = 0;
+		this->rigid_bodies = nullptr;
+		this->joint_count = 0;
+		this->joints = nullptr;
+		this->soft_body_count = 0;
+		this->soft_bodies = nullptr;
+	}
+
+	void PmxModel::Read(std::istream *stream)
+	{
+		// マジック
+		char magic[4];
+		stream->read((char*) magic, sizeof(char) * 4);
+		if (magic[0] != 0x50 || magic[1] != 0x4d || magic[2] != 0x58 || magic[3] != 0x20)
+		{
+			std::cerr << "invalid magic number." << std::endl;
+			throw;
+		}
+		// バージョン
+		stream->read((char*) &version, sizeof(float));
+		if (version != 2.0f && version != 2.1f)
+		{
+			std::cerr << "this is not ver2.0 or ver2.1 but " << version << "." << std::endl;
+			throw;
+		}
+		// ファイル設定
+		this->setting.Read(stream);
+
+		// モデル情報
+		this->model_name = std::move(ReadString(stream, setting.encoding));
+		this->model_english_name = std::move(ReadString(stream, setting.encoding));
+		this->model_comment = std::move(ReadString(stream, setting.encoding));
+		this->model_english_comment = std::move(ReadString(stream, setting.encoding));
+
+		// 頂点
+		stream->read((char*) &vertex_count, sizeof(int));
+		this->vertices = std::make_unique<PmxVertex []>(vertex_count);
+		for (int i = 0; i < vertex_count; i++)
+		{
+			vertices[i].Read(stream, &setting);
+		}
+
+		// 面
+		stream->read((char*) &index_count, sizeof(int));
+		this->indices = std::make_unique<int []>(index_count);
+		for (int i = 0; i < index_count; i++)
+		{
+			this->indices[i] = ReadIndex(stream, setting.vertex_index_size);
+		}
+
+		// テクスチャ
+		stream->read((char*) &texture_count, sizeof(int));
+		this->textures = std::make_unique<utfstring []>(texture_count);
+		for (int i = 0; i < texture_count; i++)
+		{
+			this->textures[i] = ReadString(stream, setting.encoding);
+		}
+
+		// マテリアル
+		stream->read((char*) &material_count, sizeof(int));
+		this->materials = std::make_unique<PmxMaterial []>(material_count);
+		for (int i = 0; i < material_count; i++)
+		{
+			this->materials[i].Read(stream, &setting);
+		}
+
+		// ボーン
+		stream->read((char*) &this->bone_count, sizeof(int));
+		this->bones = std::make_unique<PmxBone []>(this->bone_count);
+		for (int i = 0; i < this->bone_count; i++)
+		{
+			this->bones[i].Read(stream, &setting);
+		}
+
+		// モーフ
+		stream->read((char*) &this->morph_count, sizeof(int));
+		this->morphs = std::make_unique<PmxMorph []>(this->morph_count);
+		for (int i = 0; i < this->morph_count; i++)
+		{
+			this->morphs[i].Read(stream, &setting);
+		}
+
+		// 表示枠
+		stream->read((char*) &this->frame_count, sizeof(int));
+		this->frames = std::make_unique<PmxFrame []>(this->frame_count);
+		for (int i = 0; i < this->frame_count; i++)
+		{
+			this->frames[i].Read(stream, &setting);
+		}
+
+		// 剛体
+		stream->read((char*) &this->rigid_body_count, sizeof(int));
+		this->rigid_bodies = std::make_unique<PmxRigidBody []>(this->rigid_body_count);
+		for (int i = 0; i < this->rigid_body_count; i++)
+		{
+			this->rigid_bodies[i].Read(stream, &setting);
+		}
+
+		// ジョイント
+		stream->read((char*) &this->joint_count, sizeof(int));
+		this->joints = std::make_unique<PmxJoint []>(this->joint_count);
+		for (int i = 0; i < this->joint_count; i++)
+		{
+			this->joints[i].Read(stream, &setting);
+		}
+
+		//// ソフトボディ
+		//if (this->version == 2.1f)
+		//{
+		//	stream->read((char*) &this->soft_body_count, sizeof(int));
+		//	this->soft_bodies = std::make_unique<PmxSoftBody []>(this->soft_body_count);
+		//	for (int i = 0; i < this->soft_body_count; i++)
+		//	{
+		//		this->soft_bodies[i].Read(stream, &setting);
+		//	}
+		//}
+	}
+
+	//std::unique_ptr<PmxModel> ReadFromFile(const char *filename)
+	//{
+	//	auto stream = std::ifstream(filename, std::ios_base::binary);
+	//	auto pmx = PmxModel::ReadFromStream(&stream);
+	//	if (!stream.eof())
+	//	{
+	//		std::cerr << "don't reach the end of file." << std::endl;
+	//	}
+	//	stream.close();
+	//	return pmx;
+	//}
+
+	//std::unique_ptr<PmxModel> ReadFromStream(std::istream *stream)
+	//{
+	//	auto pmx = std::make_unique<PmxModel>();
+	//	pmx->Read(stream);
+	//	return pmx;
+	//}
+}

+ 865 - 0
code/MMDPmxParser.h

@@ -0,0 +1,865 @@
+#pragma once
+
+#include <vector>
+#include <string>
+#include <iostream>
+#include <fstream>
+#include <memory>
+#include "MMDCpp14.h"
+
+namespace pmx
+{
+#ifndef __unix__
+#define utfstring std::wstring
+#else
+#define utfstring std::string
+#endif
+	/// インデックス設定
+	class PmxSetting
+	{
+	public:
+		PmxSetting()
+			: encoding(0)
+			, uv(0)
+			, vertex_index_size(0)
+			, texture_index_size(0)
+			, material_index_size(0)
+			, bone_index_size(0)
+			, morph_index_size(0)
+			, rigidbody_index_size(0)
+		{}
+
+		/// エンコード方式
+		uint8_t encoding;
+		/// 追加UV数
+		uint8_t uv;
+		/// 頂点インデックスサイズ
+		uint8_t vertex_index_size;
+		/// テクスチャインデックスサイズ
+		uint8_t texture_index_size;
+		/// マテリアルインデックスサイズ
+		uint8_t material_index_size;
+		/// ボーンインデックスサイズ
+		uint8_t bone_index_size;
+		/// モーフインデックスサイズ
+		uint8_t morph_index_size;
+		/// 剛体インデックスサイズ
+		uint8_t rigidbody_index_size;
+		void Read(std::istream *stream);
+	};
+
+	/// 頂点スキニングタイプ
+	enum class PmxVertexSkinningType : uint8_t
+	{
+		BDEF1 = 0,
+		BDEF2 = 1,
+		BDEF4 = 2,
+		SDEF = 3,
+		QDEF = 4,
+	};
+
+	/// 頂点スキニング
+	class PmxVertexSkinning
+	{
+	public:
+		virtual void Read(std::istream *stream, PmxSetting *setting) = 0;
+	};
+
+	class PmxVertexSkinningBDEF1 : public PmxVertexSkinning
+	{
+	public:
+		PmxVertexSkinningBDEF1()
+			: bone_index(0)
+		{}
+
+		int bone_index;
+		void Read(std::istream *stresam, PmxSetting *setting);
+	};
+
+	class PmxVertexSkinningBDEF2 : public PmxVertexSkinning
+	{
+	public:
+		PmxVertexSkinningBDEF2()
+			: bone_index1(0)
+			, bone_index2(0)
+			, bone_weight(0.0f)
+		{}
+
+		int bone_index1;
+		int bone_index2;
+		float bone_weight;
+		void Read(std::istream *stresam, PmxSetting *setting);
+	};
+
+	class PmxVertexSkinningBDEF4 : public PmxVertexSkinning
+	{
+	public:
+		PmxVertexSkinningBDEF4()
+			: bone_index1(0)
+			, bone_index2(0)
+			, bone_index3(0)
+			, bone_index4(0)
+			, bone_weight1(0.0f)
+			, bone_weight2(0.0f)
+			, bone_weight3(0.0f)
+			, bone_weight4(0.0f)
+		{}
+
+		int bone_index1;
+		int bone_index2;
+		int bone_index3;
+		int bone_index4;
+		float bone_weight1;
+		float bone_weight2;
+		float bone_weight3;
+		float bone_weight4;
+		void Read(std::istream *stresam, PmxSetting *setting);
+	};
+
+	class PmxVertexSkinningSDEF : public PmxVertexSkinning
+	{
+	public:
+		PmxVertexSkinningSDEF()
+			: bone_index1(0)
+			, bone_index2(0)
+			, bone_weight(0.0f)
+		{
+			for (int i = 0; i < 3; ++i) {
+				sdef_c[i] = 0.0f;
+				sdef_r0[i] = 0.0f;
+				sdef_r1[i] = 0.0f;
+			}
+		}
+
+		int bone_index1;
+		int bone_index2;
+		float bone_weight;
+		float sdef_c[3];
+		float sdef_r0[3];
+		float sdef_r1[3];
+		void Read(std::istream *stresam, PmxSetting *setting);
+	};
+
+	class PmxVertexSkinningQDEF : public PmxVertexSkinning
+	{
+	public:
+		PmxVertexSkinningQDEF()
+			: bone_index1(0)
+			, bone_index2(0)
+			, bone_index3(0)
+			, bone_index4(0)
+			, bone_weight1(0.0f)
+			, bone_weight2(0.0f)
+			, bone_weight3(0.0f)
+			, bone_weight4(0.0f)
+		{}
+
+		int bone_index1;
+		int bone_index2;
+		int bone_index3;
+		int bone_index4;
+		float bone_weight1;
+		float bone_weight2;
+		float bone_weight3;
+		float bone_weight4;
+		void Read(std::istream *stresam, PmxSetting *setting);
+	};
+
+	/// 頂点
+	class PmxVertex
+	{
+	public:
+		PmxVertex()
+			: edge(0.0f)
+		{
+			uv[0] = uv[1] = 0.0f;
+			for (int i = 0; i < 3; ++i) {
+				positon[i] = 0.0f;
+				normal[i] = 0.0f;
+			}
+			for (int i = 0; i < 4; ++i) {
+				for (int k = 0; k < 4; ++k) {
+					uva[i][k] = 0.0f;
+				}
+			}
+		}
+
+		/// 位置
+		float positon[3];
+		/// 法線
+		float normal[3];
+		/// テクスチャ座標
+		float uv[2];
+		/// 追加テクスチャ座標
+		float uva[4][4];
+		/// スキニングタイプ
+		PmxVertexSkinningType skinning_type;
+		/// スキニング
+		std::unique_ptr<PmxVertexSkinning> skinning;
+		/// エッジ倍率
+		float edge;
+		void Read(std::istream *stream, PmxSetting *setting);
+	};
+
+	/// マテリアル
+	class PmxMaterial
+	{
+	public:
+		PmxMaterial()
+			: specularlity(0.0f)
+			, flag(0)
+			, edge_size(0.0f)
+			, diffuse_texture_index(0)
+			, sphere_texture_index(0)
+			, sphere_op_mode(0)
+			, common_toon_flag(0)
+			, toon_texture_index(0)
+			, index_count(0)
+		{
+			for (int i = 0; i < 3; ++i) {
+				specular[i] = 0.0f;
+				ambient[i] = 0.0f;
+				edge_color[i] = 0.0f;
+			}
+			for (int i = 0; i < 4; ++i) {
+				diffuse[i] = 0.0f;
+			}
+		}
+
+		/// モデル名
+		utfstring material_name;
+		/// モデル英名
+		utfstring material_english_name;
+		/// 減衰色
+		float diffuse[4];
+		/// 光沢色
+		float specular[3];
+		/// 光沢度
+		float specularlity;
+		/// 環境色
+		float ambient[3];
+		/// 描画フラグ
+		uint8_t flag;
+		/// エッジ色
+		float edge_color[4];
+		/// エッジサイズ
+		float edge_size;
+		/// アルベドテクスチャインデックス
+		int diffuse_texture_index;
+		/// スフィアテクスチャインデックス
+		int sphere_texture_index;
+		/// スフィアテクスチャ演算モード
+		uint8_t sphere_op_mode;
+		/// 共有トゥーンフラグ
+		uint8_t common_toon_flag;
+		/// トゥーンテクスチャインデックス
+		int toon_texture_index;
+		/// メモ
+		utfstring memo;
+		/// 頂点インデックス数
+		int index_count;
+		void Read(std::istream *stream, PmxSetting *setting);
+	};
+
+	/// リンク
+	class PmxIkLink
+	{
+	public:
+		PmxIkLink()
+			: link_target(0)
+			, angle_lock(0)
+		{
+			for (int i = 0; i < 3; ++i) {
+				max_radian[i] = 0.0f;
+				min_radian[i] = 0.0f;
+			}
+		}
+
+		/// リンクボーンインデックス
+		int link_target;
+		/// 角度制限
+		uint8_t angle_lock;
+		/// 最大制限角度
+		float max_radian[3];
+		/// 最小制限角度
+		float min_radian[3];
+		void Read(std::istream *stream, PmxSetting *settingn);
+	};
+
+	/// ボーン
+	class PmxBone
+	{
+	public:
+		PmxBone()
+			: parent_index(0)
+			, level(0)
+			, bone_flag(0)
+			, target_index(0)
+			, grant_parent_index(0)
+			, grant_weight(0.0f)
+			, key(0)
+			, ik_target_bone_index(0)
+			, ik_loop(0)
+			, ik_loop_angle_limit(0.0f)
+			, ik_link_count(0)
+		{
+			for (int i = 0; i < 3; ++i) {
+				position[i] = 0.0f;
+				offset[i] = 0.0f;
+				lock_axis_orientation[i] = 0.0f;
+				local_axis_x_orientation[i] = 0.0f;
+				local_axis_y_orientation[i] = 0.0f;
+			}
+		}
+
+		/// ボーン名
+		utfstring bone_name;
+		/// ボーン英名
+		utfstring bone_english_name;
+		/// 位置
+		float position[3];
+		/// 親ボーンインデックス
+		int parent_index;
+		/// 階層
+		int level;
+		/// ボーンフラグ
+		uint16_t bone_flag;
+		/// 座標オフセット(has Target)
+		float offset[3];
+		/// 接続先ボーンインデックス(not has Target)
+		int target_index;
+		/// 付与親ボーンインデックス
+		int grant_parent_index;
+		/// 付与率
+		float grant_weight;
+		/// 固定軸の方向
+		float lock_axis_orientation[3];
+		/// ローカル軸のX軸方向
+		float local_axis_x_orientation[3];
+		/// ローカル軸のY軸方向
+		float local_axis_y_orientation[3];
+		/// 外部親変形のkey値
+		int key;
+		/// IKターゲットボーン
+		int ik_target_bone_index;
+		/// IKループ回数
+		int ik_loop;
+		/// IKループ計算時の角度制限(ラジアン)
+		float ik_loop_angle_limit;
+		/// IKリンク数
+		int ik_link_count;
+		/// IKリンク
+		std::unique_ptr<PmxIkLink []> ik_links;
+		void Read(std::istream *stream, PmxSetting *setting);
+	};
+
+	enum class MorphType : uint8_t
+	{
+		Group = 0,
+		Vertex = 1,
+		Bone = 2,
+		UV = 3,
+		AdditionalUV1 = 4,
+		AdditionalUV2 = 5,
+		AdditionalUV3 = 6,
+		AdditionalUV4 = 7,
+		Matrial = 8,
+		Flip = 9,
+		Implus = 10,
+	};
+
+	enum class MorphCategory : uint8_t
+	{
+		ReservedCategory = 0,
+		Eyebrow = 1,
+		Eye = 2,
+		Mouth = 3,
+		Other = 4,
+	};
+
+	class PmxMorphOffset
+	{
+	public:
+		void virtual Read(std::istream *stream, PmxSetting *setting) = 0;
+	};
+
+	class PmxMorphVertexOffset : public PmxMorphOffset
+	{
+	public:
+		PmxMorphVertexOffset()
+			: vertex_index(0)
+		{
+			for (int i = 0; i < 3; ++i) {
+				position_offset[i] = 0.0f;
+			}
+		}
+		int vertex_index;
+		float position_offset[3];
+		void Read(std::istream *stream, PmxSetting *setting) override;
+	};
+
+	class PmxMorphUVOffset : public PmxMorphOffset
+	{
+	public:
+		PmxMorphUVOffset()
+			: vertex_index(0)
+		{
+			for (int i = 0; i < 4; ++i) {
+				uv_offset[i] = 0.0f;
+			}
+		}
+		int vertex_index;
+		float uv_offset[4];
+		void Read(std::istream *stream, PmxSetting *setting) override;
+	};
+
+	class PmxMorphBoneOffset : public PmxMorphOffset
+	{
+	public:
+		PmxMorphBoneOffset()
+			: bone_index(0)
+		{
+			for (int i = 0; i < 3; ++i) {
+				translation[i] = 0.0f;
+			}
+			for (int i = 0; i < 4; ++i) {
+				rotation[i] = 0.0f;
+			}
+		}
+		int bone_index;
+		float translation[3];
+		float rotation[4];
+		void Read(std::istream *stream, PmxSetting *setting) override;
+	};
+
+	class PmxMorphMaterialOffset : public PmxMorphOffset
+	{
+	public:
+		PmxMorphMaterialOffset()
+			: specularity(0.0f)
+			, edge_size(0.0f)
+		{
+			for (int i = 0; i < 3; ++i) {
+				specular[i] = 0.0f;
+				ambient[i] = 0.0f;
+			}
+			for (int i = 0; i < 4; ++i) {
+				diffuse[i] = 0.0f;
+				edge_color[i] = 0.0f;
+				texture_argb[i] = 0.0f;
+				sphere_texture_argb[i] = 0.0f;
+				toon_texture_argb[i] = 0.0f;
+			}
+		}
+		int material_index;
+		uint8_t offset_operation;
+		float diffuse[4];
+		float specular[3];
+		float specularity;
+		float ambient[3];
+		float edge_color[4];
+		float edge_size;
+		float texture_argb[4];
+		float sphere_texture_argb[4];
+		float toon_texture_argb[4];
+		void Read(std::istream *stream, PmxSetting *setting) override;
+	};
+
+	class PmxMorphGroupOffset : public PmxMorphOffset
+	{
+	public:
+		PmxMorphGroupOffset()
+			: morph_index(0)
+			, morph_weight(0.0f)
+		{}
+		int morph_index;
+		float morph_weight;
+		void Read(std::istream *stream, PmxSetting *setting) override;
+	};
+
+	class PmxMorphFlipOffset : public PmxMorphOffset
+	{
+	public:
+		PmxMorphFlipOffset()
+			: morph_index(0)
+			, morph_value(0.0f)
+		{}
+		int morph_index;
+		float morph_value;
+		void Read(std::istream *stream, PmxSetting *setting) override;
+	};
+
+	class PmxMorphImplusOffset : public PmxMorphOffset
+	{
+	public:
+		PmxMorphImplusOffset()
+			: rigid_body_index(0)
+			, is_local(0)
+		{
+			for (int i = 0; i < 3; ++i) {
+				velocity[i] = 0.0f;
+				angular_torque[i] = 0.0f;
+			}
+		}
+		int rigid_body_index;
+		uint8_t is_local;
+		float velocity[3];
+		float angular_torque[3];
+		void Read(std::istream *stream, PmxSetting *setting) override;
+	};
+
+	/// モーフ
+	class PmxMorph
+	{
+	public:
+		PmxMorph()
+			: offset_count(0)
+		{
+		}
+		/// モーフ名
+		utfstring morph_name;
+		/// モーフ英名
+		utfstring morph_english_name;
+		/// カテゴリ
+		MorphCategory category;
+		/// モーフタイプ
+		MorphType morph_type;
+		/// オフセット数
+		int offset_count;
+		/// 頂点モーフ配列
+		std::unique_ptr<PmxMorphVertexOffset []> vertex_offsets;
+		/// UVモーフ配列
+		std::unique_ptr<PmxMorphUVOffset []> uv_offsets;
+		/// ボーンモーフ配列
+		std::unique_ptr<PmxMorphBoneOffset []> bone_offsets;
+		/// マテリアルモーフ配列
+		std::unique_ptr<PmxMorphMaterialOffset []> material_offsets;
+		/// グループモーフ配列
+		std::unique_ptr<PmxMorphGroupOffset []> group_offsets;
+		/// フリップモーフ配列
+		std::unique_ptr<PmxMorphFlipOffset []> flip_offsets;
+		/// インパルスモーフ配列
+		std::unique_ptr<PmxMorphImplusOffset []> implus_offsets;
+		void Read(std::istream *stream, PmxSetting *setting);
+	};
+
+	/// 枠内要素
+	class PmxFrameElement
+	{
+	public:
+		PmxFrameElement()
+			: element_target(0)
+			, index(0)
+		{
+		}
+		/// 要素対象
+		uint8_t element_target;
+		/// 要素対象インデックス
+		int index;
+		void Read(std::istream *stream, PmxSetting *setting);
+	};
+
+	/// 表示枠
+	class PmxFrame
+	{
+	public:
+		PmxFrame()
+			: frame_flag(0)
+			, element_count(0)
+		{
+		}
+		/// 枠名
+		utfstring frame_name;
+		/// 枠英名
+		utfstring frame_english_name;
+		/// 特殊枠フラグ
+		uint8_t frame_flag;
+		/// 枠内要素数
+		int element_count;
+		/// 枠内要素配列
+		std::unique_ptr<PmxFrameElement []> elements;
+		void Read(std::istream *stream, PmxSetting *setting);
+	};
+
+	class PmxRigidBody
+	{
+	public:
+		PmxRigidBody()
+			: target_bone(0)
+			, group(0)
+			, mask(0)
+			, shape(0)
+			, mass(0.0f)
+			, move_attenuation(0.0f)
+			, rotation_attenuation(0.0f)
+			, repulsion(0.0f)
+			, friction(0.0f)
+			, physics_calc_type(0)
+		{
+			for (int i = 0; i < 3; ++i) {
+				size[i] = 0.0f;
+				position[i] = 0.0f;
+				orientation[i] = 0.0f;
+			}
+		}
+		/// 剛体名
+		utfstring girid_body_name;
+		/// 剛体英名
+		utfstring girid_body_english_name;
+		/// 関連ボーンインデックス
+		int target_bone;
+		/// グループ
+		uint8_t group;
+		/// マスク
+		uint16_t mask;
+		/// 形状
+		uint8_t shape;
+		float size[3];
+		float position[3];
+		float orientation[3];
+		float mass;
+		float move_attenuation;
+		float rotation_attenuation;
+		float repulsion;
+		float friction;
+		uint8_t physics_calc_type;
+		void Read(std::istream *stream, PmxSetting *setting);
+	};
+
+	enum class PmxJointType : uint8_t
+	{
+		Generic6DofSpring = 0,
+		Generic6Dof = 1,
+		Point2Point = 2,
+		ConeTwist = 3,
+		Slider = 5,
+		Hinge = 6
+	};
+
+	class PmxJointParam
+	{
+	public:
+		PmxJointParam()
+			: rigid_body1(0)
+			, rigid_body2(0)
+		{
+			for (int i = 0; i < 3; ++i) {
+				position[i] = 0.0f;
+				orientaiton[i] = 0.0f;
+				move_limitation_min[i] = 0.0f;
+				move_limitation_max[i] = 0.0f;
+				rotation_limitation_min[i] = 0.0f;
+				rotation_limitation_max[i] = 0.0f;
+				spring_move_coefficient[i] = 0.0f;
+				spring_rotation_coefficient[i] = 0.0f;
+			}
+		}
+		int rigid_body1;
+		int rigid_body2;
+		float position[3];
+		float orientaiton[3];
+		float move_limitation_min[3];
+		float move_limitation_max[3];
+		float rotation_limitation_min[3];
+		float rotation_limitation_max[3];
+		float spring_move_coefficient[3];
+		float spring_rotation_coefficient[3];
+		void Read(std::istream *stream, PmxSetting *setting);
+	};
+
+	class PmxJoint
+	{
+	public:
+		utfstring joint_name;
+		utfstring joint_english_name;
+		PmxJointType joint_type;
+		PmxJointParam param;
+		void Read(std::istream *stream, PmxSetting *setting);
+	};
+
+	enum PmxSoftBodyFlag : uint8_t
+	{
+		BLink = 0x01,
+		Cluster = 0x02,
+		Link = 0x04
+	};
+
+	class PmxAncherRigidBody
+	{
+	public:
+		PmxAncherRigidBody()
+			: related_rigid_body(0)
+			, related_vertex(0)
+			, is_near(false)
+		{}
+		int related_rigid_body;
+		int related_vertex;
+		bool is_near;
+		void Read(std::istream *stream, PmxSetting *setting);
+	};
+
+	class PmxSoftBody
+	{
+	public:
+		PmxSoftBody()
+			: shape(0)
+			, target_material(0)
+			, group(0)
+			, mask(0)
+			, blink_distance(0)
+			, cluster_count(0)
+			, mass(0.0)
+			, collisioni_margin(0.0)
+			, aero_model(0)
+			, VCF(0.0f)
+			, DP(0.0f)
+			, DG(0.0f)
+			, LF(0.0f)
+			, PR(0.0f)
+			, VC(0.0f)
+			, DF(0.0f)
+			, MT(0.0f)
+			, CHR(0.0f)
+			, KHR(0.0f)
+			, SHR(0.0f)
+			, AHR(0.0f)
+			, SRHR_CL(0.0f)
+			, SKHR_CL(0.0f)
+			, SSHR_CL(0.0f)
+			, SR_SPLT_CL(0.0f)
+			, SK_SPLT_CL(0.0f)
+			, SS_SPLT_CL(0.0f)
+			, V_IT(0)
+			, P_IT(0)
+			, D_IT(0)
+			, C_IT(0)
+			, LST(0.0f)
+			, AST(0.0f)
+			, VST(0.0f)
+			, anchor_count(0)
+			, pin_vertex_count(0)
+		{}
+		utfstring soft_body_name;
+		utfstring soft_body_english_name;
+		uint8_t shape;
+		int target_material;
+		uint8_t group;
+		uint16_t mask;
+		PmxSoftBodyFlag flag;
+		int blink_distance;
+		int cluster_count;
+		float mass;
+		float collisioni_margin;
+		int aero_model;
+		float VCF;
+		float DP;
+		float DG;
+		float LF;
+		float PR;
+		float VC;
+		float DF;
+		float MT;
+		float CHR;
+		float KHR;
+		float SHR;
+		float AHR;
+		float SRHR_CL;
+		float SKHR_CL;
+		float SSHR_CL;
+		float SR_SPLT_CL;
+		float SK_SPLT_CL;
+		float SS_SPLT_CL;
+		int V_IT;
+		int P_IT;
+		int D_IT;
+		int C_IT;
+		float LST;
+		float AST;
+		float VST;
+		int anchor_count;
+		std::unique_ptr<PmxAncherRigidBody []> anchers;
+		int pin_vertex_count;
+		std::unique_ptr<int []> pin_vertices;
+		void Read(std::istream *stream, PmxSetting *setting);
+	};
+
+	/// PMXモデル
+	class PmxModel
+	{
+	public:
+		PmxModel()
+			: version(0.0f)
+			, vertex_count(0)
+			, index_count(0)
+			, texture_count(0)
+			, material_count(0)
+			, bone_count(0)
+			, morph_count(0)
+			, frame_count(0)
+			, rigid_body_count(0)
+			, joint_count(0)
+			, soft_body_count(0)
+		{}
+
+		/// バージョン
+		float version;
+		/// 設定
+		PmxSetting setting;
+		/// モデル名
+		utfstring model_name;
+		/// モデル英名
+		utfstring model_english_name;
+		/// コメント
+		utfstring model_comment;
+		/// 英語コメント
+		utfstring model_english_comment;
+		/// 頂点数
+		int vertex_count;
+		/// 頂点配列
+		std::unique_ptr<PmxVertex []> vertices;
+		/// インデックス数
+		int index_count;
+		/// インデックス配列
+		std::unique_ptr<int []> indices;
+		/// テクスチャ数
+		int texture_count;
+		/// テクスチャ配列
+		std::unique_ptr< utfstring []> textures;
+		/// マテリアル数
+		int material_count;
+		/// マテリアル
+		std::unique_ptr<PmxMaterial []> materials;
+		/// ボーン数
+		int bone_count;
+		/// ボーン配列
+		std::unique_ptr<PmxBone []> bones;
+		/// モーフ数
+		int morph_count;
+		/// モーフ配列
+		std::unique_ptr<PmxMorph []> morphs;
+		/// 表示枠数
+		int frame_count;
+		/// 表示枠配列
+		std::unique_ptr<PmxFrame [] > frames;
+		/// 剛体数
+		int rigid_body_count;
+		/// 剛体配列
+		std::unique_ptr<PmxRigidBody []> rigid_bodies;
+		/// ジョイント数
+		int joint_count;
+		/// ジョイント配列
+		std::unique_ptr<PmxJoint []> joints;
+		/// ソフトボディ数
+		int soft_body_count;
+		/// ソフトボディ配列
+		std::unique_ptr<PmxSoftBody []> soft_bodies;
+		/// モデル初期化
+		void Init();
+		/// モデル読み込み
+		void Read(std::istream *stream);
+		///// ファイルからモデルの読み込み
+		//static std::unique_ptr<PmxModel> ReadFromFile(const char *filename);
+		///// 入力ストリームからモデルの読み込み
+		//static std::unique_ptr<PmxModel> ReadFromStream(std::istream *stream);
+	};
+}

+ 367 - 0
code/MMDVmdParser.h

@@ -0,0 +1,367 @@
+#pragma once
+
+#include <vector>
+#include <string>
+#include <memory>
+#include <iostream>
+#include <fstream>
+#include <ostream>
+#include "MMDCpp14.h"
+
+namespace vmd
+{
+	/// ボーンフレーム
+	class VmdBoneFrame
+	{
+	public:
+		/// ボーン名
+		std::string name;
+		/// フレーム番号
+		int frame;
+		/// 位置
+		float position[3];
+		/// 回転
+		float orientation[4];
+		/// 補間曲線
+		char interpolation[4][4][4];
+
+		void Read(std::istream* stream)
+		{
+			char buffer[15];
+			stream->read((char*) buffer, sizeof(char)*15);
+			name = std::string(buffer);
+			stream->read((char*) &frame, sizeof(int));
+			stream->read((char*) position, sizeof(float)*3);
+			stream->read((char*) orientation, sizeof(float)*4);
+			stream->read((char*) interpolation, sizeof(char) * 4 * 4 * 4);
+		}
+
+		void Write(std::ostream* stream)
+		{
+			stream->write((char*)name.c_str(), sizeof(char) * 15);
+			stream->write((char*)&frame, sizeof(int));
+			stream->write((char*)position, sizeof(float) * 3);
+			stream->write((char*)orientation, sizeof(float) * 4);
+			stream->write((char*)interpolation, sizeof(char) * 4 * 4 * 4);
+		}
+	};
+
+	/// 表情フレーム
+	class VmdFaceFrame
+	{
+	public:
+		/// 表情名
+		std::string face_name;
+		/// 表情の重み
+		float weight;
+		/// フレーム番号
+		uint32_t frame;
+
+		void Read(std::istream* stream)
+		{
+			char buffer[15];
+			stream->read((char*) &buffer, sizeof(char) * 15);
+			face_name = std::string(buffer);
+			stream->read((char*) &frame, sizeof(int));
+			stream->read((char*) &weight, sizeof(float));
+		}
+
+		void Write(std::ostream* stream)
+		{
+			stream->write((char*)face_name.c_str(), sizeof(char) * 15);
+			stream->write((char*)&frame, sizeof(int));
+			stream->write((char*)&weight, sizeof(float));
+		}
+	};
+
+	/// カメラフレーム
+	class VmdCameraFrame
+	{
+	public:
+		/// フレーム番号
+		int frame;
+		/// 距離
+		float distance;
+		/// 位置
+		float position[3];
+		/// 回転
+		float orientation[3];
+		/// 補間曲線
+		char interpolation[6][4];
+		/// 視野角
+		float angle;
+		/// 不明データ
+		char unknown[3];
+
+		void Read(std::istream *stream)
+		{
+			stream->read((char*) &frame, sizeof(int));
+			stream->read((char*) &distance, sizeof(float));
+			stream->read((char*) position, sizeof(float) * 3);
+			stream->read((char*) orientation, sizeof(float) * 3);
+			stream->read((char*) interpolation, sizeof(char) * 24);
+			stream->read((char*) &angle, sizeof(float));
+			stream->read((char*) unknown, sizeof(char) * 3);
+		}
+
+		void Write(std::ostream *stream)
+		{
+			stream->write((char*)&frame, sizeof(int));
+			stream->write((char*)&distance, sizeof(float));
+			stream->write((char*)position, sizeof(float) * 3);
+			stream->write((char*)orientation, sizeof(float) * 3);
+			stream->write((char*)interpolation, sizeof(char) * 24);
+			stream->write((char*)&angle, sizeof(float));
+			stream->write((char*)unknown, sizeof(char) * 3);
+		}
+	};
+
+	/// ライトフレーム
+	class VmdLightFrame
+	{
+	public:
+		/// フレーム番号
+		int frame;
+		/// 色
+		float color[3];
+		/// 位置
+		float position[3];
+
+		void Read(std::istream* stream)
+		{
+			stream->read((char*) &frame, sizeof(int));
+			stream->read((char*) color, sizeof(float) * 3);
+			stream->read((char*) position, sizeof(float) * 3);
+		}
+
+		void Write(std::ostream* stream)
+		{
+			stream->write((char*)&frame, sizeof(int));
+			stream->write((char*)color, sizeof(float) * 3);
+			stream->write((char*)position, sizeof(float) * 3);
+		}
+	};
+
+	/// IKの有効無効
+	class VmdIkEnable
+	{
+	public:
+		std::string ik_name;
+		bool enable;
+	};
+
+	/// IKフレーム
+	class VmdIkFrame
+	{
+	public:
+		int frame;
+		bool display;
+		std::vector<VmdIkEnable> ik_enable;
+
+		void Read(std::istream *stream)
+		{
+			char buffer[20];
+			stream->read((char*) &frame, sizeof(int));
+			stream->read((char*) &display, sizeof(uint8_t));
+			int ik_count;
+			stream->read((char*) &ik_count, sizeof(int));
+			ik_enable.resize(ik_count);
+			for (int i = 0; i < ik_count; i++)
+			{
+				stream->read(buffer, 20);
+				ik_enable[i].ik_name = std::string(buffer);
+				stream->read((char*) &ik_enable[i].enable, sizeof(uint8_t));
+			}
+		}
+
+		void Write(std::ostream *stream)
+		{
+			stream->write((char*)&frame, sizeof(int));
+			stream->write((char*)&display, sizeof(uint8_t));
+			int ik_count = static_cast<int>(ik_enable.size());
+			stream->write((char*)&ik_count, sizeof(int));
+			for (int i = 0; i < ik_count; i++)
+			{
+				const VmdIkEnable& ik_enable = this->ik_enable.at(i);
+				stream->write(ik_enable.ik_name.c_str(), 20);
+				stream->write((char*)&ik_enable.enable, sizeof(uint8_t));
+			}
+		}
+	};
+
+	/// VMDモーション
+	class VmdMotion
+	{
+	public:
+		/// モデル名
+		std::string model_name;
+		/// バージョン
+		int version;
+		/// ボーンフレーム
+		std::vector<VmdBoneFrame> bone_frames;
+		/// 表情フレーム
+		std::vector<VmdFaceFrame> face_frames;
+		/// カメラフレーム
+		std::vector<VmdCameraFrame> camera_frames;
+		/// ライトフレーム
+		std::vector<VmdLightFrame> light_frames;
+		/// IKフレーム
+		std::vector<VmdIkFrame> ik_frames;
+
+		static std::unique_ptr<VmdMotion> LoadFromFile(char const *filename)
+		{
+			std::ifstream stream(filename, std::ios::binary);
+			auto result = LoadFromStream(&stream);
+			stream.close();
+			return result;
+		}
+
+		static std::unique_ptr<VmdMotion> LoadFromStream(std::ifstream *stream)
+		{
+
+			char buffer[30];
+			auto result = std::make_unique<VmdMotion>();
+
+			// magic and version
+			stream->read((char*) buffer, 30);
+			if (strncmp(buffer, "Vocaloid Motion Data", 20))
+			{
+				std::cerr << "invalid vmd file." << std::endl;
+				return nullptr;
+			}
+			result->version = std::atoi(buffer + 20);
+
+			// name
+			stream->read(buffer, 20);
+			result->model_name = std::string(buffer);
+
+			// bone frames
+			int bone_frame_num;
+			stream->read((char*) &bone_frame_num, sizeof(int));
+			result->bone_frames.resize(bone_frame_num);
+			for (int i = 0; i < bone_frame_num; i++)
+			{
+				result->bone_frames[i].Read(stream);
+			}
+
+			// face frames
+			int face_frame_num;
+			stream->read((char*) &face_frame_num, sizeof(int));
+			result->face_frames.resize(face_frame_num);
+			for (int i = 0; i < face_frame_num; i++)
+			{
+				result->face_frames[i].Read(stream);
+			}
+
+			// camera frames
+			int camera_frame_num;
+			stream->read((char*) &camera_frame_num, sizeof(int));
+			result->camera_frames.resize(camera_frame_num);
+			for (int i = 0; i < camera_frame_num; i++)
+			{
+				result->camera_frames[i].Read(stream);
+			}
+
+			// light frames
+			int light_frame_num;
+			stream->read((char*) &light_frame_num, sizeof(int));
+			result->light_frames.resize(light_frame_num);
+			for (int i = 0; i < light_frame_num; i++)
+			{
+				result->light_frames[i].Read(stream);
+			}
+
+			// unknown2
+			stream->read(buffer, 4);
+
+			// ik frames
+			if (stream->peek() != std::ios::traits_type::eof())
+			{
+				int ik_num;
+				stream->read((char*) &ik_num, sizeof(int));
+				result->ik_frames.resize(ik_num);
+				for (int i = 0; i < ik_num; i++)
+				{
+					result->ik_frames[i].Read(stream);
+				}
+			}
+
+			if (stream->peek() != std::ios::traits_type::eof())
+			{
+				std::cerr << "vmd stream has unknown data." << std::endl;
+			}
+
+			return result;
+		}
+
+		bool SaveToFile(const std::u16string& filename)
+		{
+			// TODO: How to adapt u16string to string?
+			/*
+			std::ofstream stream(filename.c_str(), std::ios::binary);
+			auto result = SaveToStream(&stream);
+			stream.close();
+			return result;
+			*/
+			return false;
+		}
+
+		bool SaveToStream(std::ofstream *stream)
+		{
+			std::string magic = "Vocaloid Motion Data 0002\0";
+			magic.resize(30);
+
+			// magic and version
+			stream->write(magic.c_str(), 30);
+
+			// name
+			stream->write(model_name.c_str(), 20);
+
+			// bone frames
+			const int bone_frame_num = static_cast<int>(bone_frames.size());
+			stream->write(reinterpret_cast<const char*>(&bone_frame_num), sizeof(int));
+			for (int i = 0; i < bone_frame_num; i++)
+			{
+				bone_frames[i].Write(stream);
+			}
+
+			// face frames
+			const int face_frame_num = static_cast<int>(face_frames.size());
+			stream->write(reinterpret_cast<const char*>(&face_frame_num), sizeof(int));
+			for (int i = 0; i < face_frame_num; i++)
+			{
+				face_frames[i].Write(stream);
+			}
+
+			// camera frames
+			const int camera_frame_num = static_cast<int>(camera_frames.size());
+			stream->write(reinterpret_cast<const char*>(&camera_frame_num), sizeof(int));
+			for (int i = 0; i < camera_frame_num; i++)
+			{
+				camera_frames[i].Write(stream);
+			}
+
+			// light frames
+			const int light_frame_num = static_cast<int>(light_frames.size());
+			stream->write(reinterpret_cast<const char*>(&light_frame_num), sizeof(int));
+			for (int i = 0; i < light_frame_num; i++)
+			{
+				light_frames[i].Write(stream);
+			}
+
+			// self shadow datas
+			const int self_shadow_num = 0;
+			stream->write(reinterpret_cast<const char*>(&self_shadow_num), sizeof(int));
+
+			// ik frames
+			const int ik_num = static_cast<int>(ik_frames.size());
+			stream->write(reinterpret_cast<const char*>(&ik_num), sizeof(int));
+			for (int i = 0; i < ik_num; i++)
+			{
+				ik_frames[i].Write(stream);
+			}
+
+			return true;
+		}
+	};
+}

+ 1 - 0
test/CMakeLists.txt

@@ -105,6 +105,7 @@ SET( TEST_SRCS
   unit/utObjImportExport.cpp
   unit/utPretransformVertices.cpp
   unit/utPLYImportExport.cpp
+  unit/utPMXImporter.cpp
   unit/utRemoveComments.cpp
   unit/utRemoveComponent.cpp
   unit/utRemoveRedundantMaterials.cpp

+ 62 - 0
test/unit/utPMXImporter.cpp

@@ -0,0 +1,62 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2016, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+#include "UnitTestPCH.h"
+#include "SceneDiffer.h"
+#include "AbstractImportExportBase.h"
+#include "MMDImporter.h"
+
+#include <assimp/Importer.hpp>
+
+using namespace ::Assimp;
+
+class utPMXImporter : public AbstractImportExportBase {
+public:
+    virtual bool importerTest() {
+        Assimp::Importer importer;
+        const aiScene *scene = importer.ReadFile( ASSIMP_TEST_MODELS_DIR "/../models-nonbsd/MMD/kawakaze.pmx", 0 );
+        return nullptr != scene;
+    }
+};
+
+TEST_F( utPMXImporter, importTest ) {
+    EXPECT_TRUE( importerTest() );
+}

+ 1 - 1
tools/assimp_qt_viewer/CMakeLists.txt

@@ -1,5 +1,5 @@
-project(assimp_qt_viewer)
 set(PROJECT_VERSION "")
+project(assimp_qt_viewer)
 
 cmake_minimum_required(VERSION 2.6)