diff --git a/CMakeLists.txt b/CMakeLists.txt index 8aaaf36de..3079d13d1 100644 --- a/CMakeLists.txt +++ b/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/ ) diff --git a/FindDevIL.cmake b/FindDevIL.cmake new file mode 100644 index 000000000..381a75dd2 --- /dev/null +++ b/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) diff --git a/FindICU.cmake b/FindICU.cmake new file mode 100644 index 000000000..59dd891af --- /dev/null +++ b/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__FOUND : were 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= +# - 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) + +########## ########## + +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 ) +# +# 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) +# +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 ) +# ... +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- --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) + ########## ########## + 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}") + ########## ########## +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) + # + # 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}) + # + 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 +) + +########## ########## + +########## ########## + +########## 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 ] [PACKAGE] [DESTINATION ] [FILES ]) +# +# Common arguments: +# - NAME : name of output package and to create dummy targets +# - FILES ... : list of resource bundles sources +# - DEPENDS ... : 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 : optional, directory where to install final binary file(s) +# - FORMAT : 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 : 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 : 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) + + ##### ##### + 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) + ##### ##### + + ##### ##### + 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") + ##### ##### + + ##### ##### + # 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) + ##### ##### + + 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") + + ##### ##### + 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}") + ##### ##### + + # build .txt from .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++) for each locale to build the .res file from its .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 _) 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/cmake-commits@cmake.org/msg01135.html + set(PACKAGE_INTERMEDIATE_TARGET_NAME "${PACKAGE_TARGET_NAME}${TARGET_SEPARATOR}DUMMY") + # dummy intermediate target (ICU+PKG++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+) 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++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) + +########## ########## + +########## ########## + +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} = ") + 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") + # _(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) + +########## ########## diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 92199c234..2442513cb 100644 --- a/code/CMakeLists.txt +++ b/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) diff --git a/code/ImporterRegistry.cpp b/code/ImporterRegistry.cpp index 95a1867e0..7f1751654 100644 --- a/code/ImporterRegistry.cpp +++ b/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. */ diff --git a/code/MMDCpp14.h b/code/MMDCpp14.h new file mode 100644 index 000000000..212897dc2 --- /dev/null +++ b/code/MMDCpp14.h @@ -0,0 +1,41 @@ +#pragma once + +#ifndef MMD_CPP14_H + +#include +#include +#include +#include + +namespace std { + template struct _Unique_if { + typedef unique_ptr _Single_object; + }; + + template struct _Unique_if { + typedef unique_ptr _Unknown_bound; + }; + + template struct _Unique_if { + typedef void _Known_bound; + }; + + template + typename _Unique_if::_Single_object + make_unique(Args&&... args) { + return unique_ptr(new T(std::forward(args)...)); + } + + template + typename _Unique_if::_Unknown_bound + make_unique(size_t n) { + typedef typename remove_extent::type U; + return unique_ptr(new U[n]()); + } + + template + typename _Unique_if::_Known_bound + make_unique(Args&&...) = delete; +} + +#endif \ No newline at end of file diff --git a/code/MMDImporter.cpp b/code/MMDImporter.cpp new file mode 100644 index 000000000..4179a7cc9 --- /dev/null +++ b/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 +#include +#include +#include +#include +#include +#include + +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 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 diff --git a/code/MMDImporter.h b/code/MMDImporter.h new file mode 100644 index 000000000..64ae1eb60 --- /dev/null +++ b/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 +#include + +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 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 \ No newline at end of file diff --git a/code/MMDPmdParser.h b/code/MMDPmdParser.h new file mode 100644 index 000000000..c1c7656f5 --- /dev/null +++ b/code/MMDPmdParser.h @@ -0,0 +1,630 @@ +#pragma once + +#include +#include +#include +#include +#include +#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 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 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 vertices; + std::vector indices; + std::vector materials; + std::vector bones; + std::vector iks; + std::vector faces; + std::vector faces_indices; + std::vector bone_disp_name; + std::vector bone_disp; + std::vector toon_filenames; + std::vector rigid_bodies; + std::vector constraints; + + static std::unique_ptr 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 LoadFromStream(std::ifstream *stream) + { + auto result = std::make_unique(); + 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; + } + }; +} \ No newline at end of file diff --git a/code/MMDPmxParser.cpp b/code/MMDPmxParser.cpp new file mode 100644 index 000000000..639574a59 --- /dev/null +++ b/code/MMDPmxParser.cpp @@ -0,0 +1,625 @@ +#include +#include "MMDPmxParser.h" +#ifndef __unix__ +#include "EncodingHelper.h" +#else +#include +#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 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 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(); + break; + case PmxVertexSkinningType::BDEF2: + this->skinning = std::make_unique(); + break; + case PmxVertexSkinningType::BDEF4: + this->skinning = std::make_unique(); + break; + case PmxVertexSkinningType::SDEF: + this->skinning = std::make_unique(); + break; + case PmxVertexSkinningType::QDEF: + this->skinning = std::make_unique(); + 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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(this->soft_body_count); + // for (int i = 0; i < this->soft_body_count; i++) + // { + // this->soft_bodies[i].Read(stream, &setting); + // } + //} + } + + //std::unique_ptr 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 ReadFromStream(std::istream *stream) + //{ + // auto pmx = std::make_unique(); + // pmx->Read(stream); + // return pmx; + //} +} diff --git a/code/MMDPmxParser.h b/code/MMDPmxParser.h new file mode 100644 index 000000000..1387fef58 --- /dev/null +++ b/code/MMDPmxParser.h @@ -0,0 +1,865 @@ +#pragma once + +#include +#include +#include +#include +#include +#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 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 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 vertex_offsets; + /// UVモーフ配列 + std::unique_ptr uv_offsets; + /// ボーンモーフ配列 + std::unique_ptr bone_offsets; + /// マテリアルモーフ配列 + std::unique_ptr material_offsets; + /// グループモーフ配列 + std::unique_ptr group_offsets; + /// フリップモーフ配列 + std::unique_ptr flip_offsets; + /// インパルスモーフ配列 + std::unique_ptr 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 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 anchers; + int pin_vertex_count; + std::unique_ptr 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 vertices; + /// インデックス数 + int index_count; + /// インデックス配列 + std::unique_ptr indices; + /// テクスチャ数 + int texture_count; + /// テクスチャ配列 + std::unique_ptr< utfstring []> textures; + /// マテリアル数 + int material_count; + /// マテリアル + std::unique_ptr materials; + /// ボーン数 + int bone_count; + /// ボーン配列 + std::unique_ptr bones; + /// モーフ数 + int morph_count; + /// モーフ配列 + std::unique_ptr morphs; + /// 表示枠数 + int frame_count; + /// 表示枠配列 + std::unique_ptr frames; + /// 剛体数 + int rigid_body_count; + /// 剛体配列 + std::unique_ptr rigid_bodies; + /// ジョイント数 + int joint_count; + /// ジョイント配列 + std::unique_ptr joints; + /// ソフトボディ数 + int soft_body_count; + /// ソフトボディ配列 + std::unique_ptr soft_bodies; + /// モデル初期化 + void Init(); + /// モデル読み込み + void Read(std::istream *stream); + ///// ファイルからモデルの読み込み + //static std::unique_ptr ReadFromFile(const char *filename); + ///// 入力ストリームからモデルの読み込み + //static std::unique_ptr ReadFromStream(std::istream *stream); + }; +} diff --git a/code/MMDVmdParser.h b/code/MMDVmdParser.h new file mode 100644 index 000000000..889a579db --- /dev/null +++ b/code/MMDVmdParser.h @@ -0,0 +1,367 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#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 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(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 bone_frames; + /// 表情フレーム + std::vector face_frames; + /// カメラフレーム + std::vector camera_frames; + /// ライトフレーム + std::vector light_frames; + /// IKフレーム + std::vector ik_frames; + + static std::unique_ptr LoadFromFile(char const *filename) + { + std::ifstream stream(filename, std::ios::binary); + auto result = LoadFromStream(&stream); + stream.close(); + return result; + } + + static std::unique_ptr LoadFromStream(std::ifstream *stream) + { + + char buffer[30]; + auto result = std::make_unique(); + + // 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(bone_frames.size()); + stream->write(reinterpret_cast(&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(face_frames.size()); + stream->write(reinterpret_cast(&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(camera_frames.size()); + stream->write(reinterpret_cast(&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(light_frames.size()); + stream->write(reinterpret_cast(&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(&self_shadow_num), sizeof(int)); + + // ik frames + const int ik_num = static_cast(ik_frames.size()); + stream->write(reinterpret_cast(&ik_num), sizeof(int)); + for (int i = 0; i < ik_num; i++) + { + ik_frames[i].Write(stream); + } + + return true; + } + }; +} \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index b6035d951..5b7fc6b91 100644 --- a/test/CMakeLists.txt +++ b/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 diff --git a/test/unit/utPMXImporter.cpp b/test/unit/utPMXImporter.cpp new file mode 100644 index 000000000..cf70fd65d --- /dev/null +++ b/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 + +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() ); +} diff --git a/tools/assimp_qt_viewer/CMakeLists.txt b/tools/assimp_qt_viewer/CMakeLists.txt index 2985d6e30..42ef0fb34 100644 --- a/tools/assimp_qt_viewer/CMakeLists.txt +++ b/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)