Merge branch 'master' into fix_gltf_accessor_overflow

pull/2779/head
Cory Fabre 2019-11-22 00:14:11 -06:00 committed by GitHub
commit 36ca37f9ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
181 changed files with 13460 additions and 2709 deletions

127
.clang-format 100644
View File

@ -0,0 +1,127 @@
# Commented out parameters are those with the same value as base LLVM style
# We can uncomment them if we want to change their value, or enforce the
# chosen value in case the base style changes (last sync: Clang 6.0.1).
---
### General config, applies to all languages ###
BasedOnStyle: LLVM
AccessModifierOffset: -4
AlignAfterOpenBracket: DontAlign
# AlignConsecutiveAssignments: false
# AlignConsecutiveDeclarations: false
# AlignEscapedNewlines: Right
# AlignOperands: true
AlignTrailingComments: false
AllowAllParametersOfDeclarationOnNextLine: false
# AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: true
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: true
# AllowShortLoopsOnASingleLine: false
# AlwaysBreakAfterDefinitionReturnType: None
# AlwaysBreakAfterReturnType: None
# AlwaysBreakBeforeMultilineStrings: false
# AlwaysBreakTemplateDeclarations: false
# BinPackArguments: true
# BinPackParameters: true
# BraceWrapping:
# AfterClass: false
# AfterControlStatement: false
# AfterEnum: false
# AfterFunction: false
# AfterNamespace: false
# AfterObjCDeclaration: false
# AfterStruct: false
# AfterUnion: false
# AfterExternBlock: false
# BeforeCatch: false
# BeforeElse: false
# IndentBraces: false
# SplitEmptyFunction: true
# SplitEmptyRecord: true
# SplitEmptyNamespace: true
# BreakBeforeBinaryOperators: None
# BreakBeforeBraces: Attach
# BreakBeforeInheritanceComma: false
BreakBeforeTernaryOperators: false
# BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: AfterColon
# BreakStringLiterals: true
ColumnLimit: 0
# CommentPragmas: '^ IWYU pragma:'
# CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 8
ContinuationIndentWidth: 8
Cpp11BracedListStyle: false
# DerivePointerAlignment: false
# DisableFormat: false
# ExperimentalAutoDetectBinPacking: false
# FixNamespaceComments: true
# ForEachMacros:
# - foreach
# - Q_FOREACH
# - BOOST_FOREACH
# IncludeBlocks: Preserve
IncludeCategories:
- Regex: '".*"'
Priority: 1
- Regex: '^<.*\.h>'
Priority: 2
- Regex: '^<.*'
Priority: 3
# IncludeIsMainRegex: '(Test)?$'
IndentCaseLabels: true
# IndentPPDirectives: None
IndentWidth: 4
# IndentWrappedFunctionNames: false
# JavaScriptQuotes: Leave
# JavaScriptWrapImports: true
# KeepEmptyLinesAtTheStartOfBlocks: true
# MacroBlockBegin: ''
# MacroBlockEnd: ''
# MaxEmptyLinesToKeep: 1
# NamespaceIndentation: None
# PenaltyBreakAssignment: 2
# PenaltyBreakBeforeFirstCallParameter: 19
# PenaltyBreakComment: 300
# PenaltyBreakFirstLessLess: 120
# PenaltyBreakString: 1000
# PenaltyExcessCharacter: 1000000
# PenaltyReturnTypeOnItsOwnLine: 60
# PointerAlignment: Right
# RawStringFormats:
# - Delimiter: pb
# Language: TextProto
# BasedOnStyle: google
# ReflowComments: true
# SortIncludes: true
# SortUsingDeclarations: true
# SpaceAfterCStyleCast: false
# SpaceAfterTemplateKeyword: true
# SpaceBeforeAssignmentOperators: true
# SpaceBeforeParens: ControlStatements
# SpaceInEmptyParentheses: false
# SpacesBeforeTrailingComments: 1
# SpacesInAngles: false
# SpacesInContainerLiterals: true
# SpacesInCStyleCastParentheses: false
# SpacesInParentheses: false
# SpacesInSquareBrackets: false
TabWidth: 4
UseTab: Always
---
### C++ specific config ###
Language: Cpp
Standard: Cpp11
---
### ObjC specific config ###
Language: ObjC
Standard: Cpp11
ObjCBlockIndentWidth: 4
# ObjCSpaceAfterProperty: false
# ObjCSpaceBeforeProtocolList: true
---
### Java specific config ###
Language: Java
# BreakAfterJavaFieldAnnotations: false
...

5
.gitignore vendored
View File

@ -2,6 +2,11 @@
build build
.project .project
*.kdev4* *.kdev4*
.DS_Store
# build artefacts
*.o
*.a
# Visual Studio # Visual Studio
*.sln *.sln

View File

@ -1,17 +1,31 @@
# Build Instructions # Build Instructions
## Install CMake
## Build on all platforms using vcpkg
You can download and install assimp using the [vcpkg](https://github.com/Microsoft/vcpkg/) dependency manager:
```bash
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg integrate install
vcpkg install assimp
```
The assimp port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.
## Manual build instructions
### Install CMake
Asset-Importer-Lib can be build for a lot of different platforms. We are using cmake to generate the build environment for these via cmake. So you have to make sure that you have a working cmake-installation on your system. You can download it at https://cmake.org/ or for linux install it via Asset-Importer-Lib can be build for a lot of different platforms. We are using cmake to generate the build environment for these via cmake. So you have to make sure that you have a working cmake-installation on your system. You can download it at https://cmake.org/ or for linux install it via
```bash ```bash
sudo apt-get install cmake sudo apt-get install cmake
``` ```
## Get the source ### Get the source
Make sure you have a working git-installation. Open a command prompt and clone the Asset-Importer-Lib via: Make sure you have a working git-installation. Open a command prompt and clone the Asset-Importer-Lib via:
```bash ```bash
git clone https://github.com/assimp/assimp.git git clone https://github.com/assimp/assimp.git
``` ```
## Build instructions for Windows with Visual-Studio ### Build instructions for Windows with Visual-Studio
First you have to install Visual-Studio on your windows-system. You can get the Community-Version for free here: https://visualstudio.microsoft.com/de/downloads/ First you have to install Visual-Studio on your windows-system. You can get the Community-Version for free here: https://visualstudio.microsoft.com/de/downloads/
To generate the build environment for your IDE open a command prompt, navigate to your repo and type: To generate the build environment for your IDE open a command prompt, navigate to your repo and type:
@ -20,10 +34,10 @@ cmake CMakeLists.txt
``` ```
This will generate the project files for the visual studio. All dependencies used to build Asset-IMporter-Lib shall be part of the repo. If you want to use you own zlib.installation this is possible as well. Check the options for it. This will generate the project files for the visual studio. All dependencies used to build Asset-IMporter-Lib shall be part of the repo. If you want to use you own zlib.installation this is possible as well. Check the options for it.
## Build instructions for Windows with UWP ### Build instructions for Windows with UWP
See <https://stackoverflow.com/questions/40803170/cmake-uwp-using-cmake-to-build-universal-windows-app> See <https://stackoverflow.com/questions/40803170/cmake-uwp-using-cmake-to-build-universal-windows-app>
## Build instructions for Linux / Unix ### Build instructions for Linux / Unix
Open a terminal and got to your repository. You can generate the makefiles and build the library via: Open a terminal and got to your repository. You can generate the makefiles and build the library via:
```bash ```bash
@ -34,7 +48,7 @@ The option -j descripes the number of parallel processes for the build. In this
If you want to use a IDE for linux you can try QTCreator for instance. If you want to use a IDE for linux you can try QTCreator for instance.
## Build instructions for MinGW ### Build instructions for MinGW
Older versions of MinGW's compiler (e.g. 5.1.0) do not support the -mbig_obj flag Older versions of MinGW's compiler (e.g. 5.1.0) do not support the -mbig_obj flag
required to compile some of assimp's files, especially for debug builds. required to compile some of assimp's files, especially for debug builds.
Version 7.3.0 of g++-mingw-w64 & gcc-mingw-w64 appears to work. Version 7.3.0 of g++-mingw-w64 & gcc-mingw-w64 appears to work.
@ -50,7 +64,7 @@ The following toolchain may or may not be helpful for building assimp using MinG
Besides the toolchain, compilation should be the same as for Linux / Unix. Besides the toolchain, compilation should be the same as for Linux / Unix.
## CMake build options ### CMake build options
The cmake-build-environment provides options to configure the build. The following options can be used: The cmake-build-environment provides options to configure the build. The following options can be used:
- **BUILD_SHARED_LIBS ( default ON )**: Generation of shared libs ( dll for windows, so for Linux ). Set this to OFF to get a static lib. - **BUILD_SHARED_LIBS ( default ON )**: Generation of shared libs ( dll for windows, so for Linux ). Set this to OFF to get a static lib.
- **BUILD_FRAMEWORK ( default OFF, MacOnly)**: Build package as Mac OS X Framework bundle - **BUILD_FRAMEWORK ( default OFF, MacOnly)**: Build package as Mac OS X Framework bundle

View File

@ -253,7 +253,7 @@ ELSEIF(MSVC)
IF(MSVC12) IF(MSVC12)
ADD_COMPILE_OPTIONS(/wd4351) ADD_COMPILE_OPTIONS(/wd4351)
ENDIF() ENDIF()
SET(CMAKE_CXX_FLAGS_DEBUG "/D_DEBUG /MDd /Ob2 /DEBUG:FULL /Zi") SET(CMAKE_CXX_FLAGS_DEBUG "/D_DEBUG /MDd /Ob2 /Zi")
ELSEIF ( "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" ) ELSEIF ( "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" )
IF(NOT HUNTER_ENABLED) IF(NOT HUNTER_ENABLED)
SET(CMAKE_CXX_FLAGS "-fPIC -std=c++11 ${CMAKE_CXX_FLAGS}") SET(CMAKE_CXX_FLAGS "-fPIC -std=c++11 ${CMAKE_CXX_FLAGS}")
@ -391,6 +391,11 @@ IF(HUNTER_ENABLED)
) )
ELSE(HUNTER_ENABLED) ELSE(HUNTER_ENABLED)
# cmake configuration files # cmake configuration files
if(${BUILD_SHARED_LIBS})
set(BUILD_LIB_TYPE SHARED)
else()
set(BUILD_LIB_TYPE STATIC)
endif()
CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/assimp-config.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/assimp-config.cmake" @ONLY IMMEDIATE) CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/assimp-config.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/assimp-config.cmake" @ONLY IMMEDIATE)
CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/assimpTargets.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/assimpTargets.cmake" @ONLY IMMEDIATE) CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/assimpTargets.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/assimpTargets.cmake" @ONLY IMMEDIATE)
IF (is_multi_config) IF (is_multi_config)

View File

@ -60,13 +60,19 @@ __Importers__:
- ENFF - ENFF
- [FBX](https://en.wikipedia.org/wiki/FBX) - [FBX](https://en.wikipedia.org/wiki/FBX)
- [glTF 1.0](https://en.wikipedia.org/wiki/GlTF#glTF_1.0) + GLB - [glTF 1.0](https://en.wikipedia.org/wiki/GlTF#glTF_1.0) + GLB
- [glTF 2.0](https://en.wikipedia.org/wiki/GlTF#glTF_2.0) - [glTF 2.0](https://en.wikipedia.org/wiki/GlTF#glTF_2.0):
At the moment for glTF2.0 the following extensions are supported:
+ KHR_lights_punctual ( 5.0 )
+ KHR_materials_pbrSpecularGlossiness ( 5.0 )
+ KHR_materials_unlit ( 5.0 )
+ KHR_texture_transform ( 5.1 under test )
- HMB - HMB
- IFC-STEP - IFC-STEP
- IRR / IRRMESH - IRR / IRRMESH
- [LWO](https://en.wikipedia.org/wiki/LightWave_3D) - [LWO](https://en.wikipedia.org/wiki/LightWave_3D)
- LWS - LWS
- LXO - LXO
- [M3D](https://bztsrc.gitlab.io/model3d)
- MD2 - MD2
- MD3 - MD3
- MD5 - MD5
@ -120,7 +126,7 @@ __Exporters__:
- FBX ( experimental ) - FBX ( experimental )
### Building ### ### Building ###
Take a look into the https://github.com/assimp/assimp/blob/master/Build.md file. Our build system is CMake, if you used CMake before there is a good chance you know what to do. Take a look into the https://github.com/assimp/assimp/blob/master/Build.md file. We are available in vcpkg, and our build system is CMake; if you used CMake before there is a good chance you know what to do.
### Ports ### ### Ports ###
* [Android](port/AndroidJNI/README.md) * [Android](port/AndroidJNI/README.md)

View File

@ -14,8 +14,10 @@ matrix:
fast_finish: true fast_finish: true
image: image:
- Visual Studio 2013
- Visual Studio 2015 - Visual Studio 2015
- Visual Studio 2017 - Visual Studio 2017
- Visual Studio 2019
- MinGW - MinGW
platform: platform:
@ -28,11 +30,13 @@ install:
- set PATH=C:\Ruby24-x64\bin;%PATH% - set PATH=C:\Ruby24-x64\bin;%PATH%
- set CMAKE_DEFINES -DASSIMP_WERROR=ON - set CMAKE_DEFINES -DASSIMP_WERROR=ON
- if [%COMPILER%]==[MinGW] set PATH=C:\MinGW\bin;%PATH% - if [%COMPILER%]==[MinGW] set PATH=C:\MinGW\bin;%PATH%
- if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2013" set CMAKE_GENERATOR_NAME=Visual Studio 12 2013
- if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2015" set CMAKE_GENERATOR_NAME=Visual Studio 14 2015 - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2015" set CMAKE_GENERATOR_NAME=Visual Studio 14 2015
- if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" set CMAKE_GENERATOR_NAME=Visual Studio 15 2017 - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" set CMAKE_GENERATOR_NAME=Visual Studio 15 2017
- if "%platform%"=="x64" set CMAKE_GENERATOR_NAME=%CMAKE_GENERATOR_NAME% Win64 - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2019" set CMAKE_GENERATOR_NAME=Visual Studio 16 2019
- cmake %CMAKE_DEFINES% -G "%CMAKE_GENERATOR_NAME%" . - cmake %CMAKE_DEFINES% -G "%CMAKE_GENERATOR_NAME%" -A %platform% .
# Rename sh.exe as sh.exe in PATH interferes with MinGW # Rename sh.exe as sh.exe in PATH interferes with MinGW - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2015" set CMAKE_GENERATOR_NAME=Visual Studio 14 2015
- rename "C:\Program Files\Git\usr\bin\sh.exe" "sh2.exe" - rename "C:\Program Files\Git\usr\bin\sh.exe" "sh2.exe"
- set PATH=%PATH%;"C:\\Program Files (x86)\\Inno Setup 5" - set PATH=%PATH%;"C:\\Program Files (x86)\\Inno Setup 5"
- ps: Invoke-WebRequest -Uri https://download.microsoft.com/download/5/7/b/57b2947c-7221-4f33-b35e-2fc78cb10df4/vc_redist.x64.exe -OutFile .\packaging\windows-innosetup\vc_redist.x64.exe - ps: Invoke-WebRequest -Uri https://download.microsoft.com/download/5/7/b/57b2947c-7221-4f33-b35e-2fc78cb10df4/vc_redist.x64.exe -OutFile .\packaging\windows-innosetup\vc_redist.x64.exe

View File

@ -35,6 +35,8 @@ if(MSVC)
endif() endif()
set(ASSIMP_LIBRARY_SUFFIX "@ASSIMP_LIBRARY_SUFFIX@-${MSVC_PREFIX}-mt" CACHE STRING "the suffix for the assimp windows library" ) set(ASSIMP_LIBRARY_SUFFIX "@ASSIMP_LIBRARY_SUFFIX@-${MSVC_PREFIX}-mt" CACHE STRING "the suffix for the assimp windows library" )
file(TO_NATIVE_PATH ${_IMPORT_PREFIX} _IMPORT_PREFIX)
if(ASSIMP_BUILD_SHARED_LIBS) if(ASSIMP_BUILD_SHARED_LIBS)
set(sharedLibraryName "assimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@@CMAKE_SHARED_LIBRARY_SUFFIX@") set(sharedLibraryName "assimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@@CMAKE_SHARED_LIBRARY_SUFFIX@")
set(importLibraryName "assimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@@CMAKE_IMPORT_LIBRARY_SUFFIX@") set(importLibraryName "assimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@@CMAKE_IMPORT_LIBRARY_SUFFIX@")
@ -63,7 +65,14 @@ if(MSVC)
else() else()
set(ASSIMP_LIBRARY_SUFFIX "@ASSIMP_LIBRARY_SUFFIX@" CACHE STRING "the suffix for the assimp libraries" ) set(ASSIMP_LIBRARY_SUFFIX "@ASSIMP_LIBRARY_SUFFIX@" CACHE STRING "the suffix for the assimp libraries" )
if(ASSIMP_BUILD_SHARED_LIBS) if(ASSIMP_BUILD_SHARED_LIBS)
if(WIN32)
# Handle MinGW compiler.
set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@@CMAKE_SHARED_LIBRARY_SUFFIX@@CMAKE_STATIC_LIBRARY_SUFFIX@")
elseif(APPLE)
set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@.@ASSIMP_VERSION_MAJOR@@CMAKE_SHARED_LIBRARY_SUFFIX@")
else()
set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@@CMAKE_SHARED_LIBRARY_SUFFIX@.@ASSIMP_VERSION_MAJOR@") set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@@CMAKE_SHARED_LIBRARY_SUFFIX@.@ASSIMP_VERSION_MAJOR@")
endif()
set_target_properties(assimp::assimp PROPERTIES set_target_properties(assimp::assimp PROPERTIES
IMPORTED_SONAME_DEBUG "${sharedLibraryName}" IMPORTED_SONAME_DEBUG "${sharedLibraryName}"
IMPORTED_LOCATION_DEBUG "${_IMPORT_PREFIX}/lib/${sharedLibraryName}" IMPORTED_LOCATION_DEBUG "${_IMPORT_PREFIX}/lib/${sharedLibraryName}"

View File

@ -35,6 +35,8 @@ if(MSVC)
endif() endif()
set(ASSIMP_LIBRARY_SUFFIX "@ASSIMP_LIBRARY_SUFFIX@-${MSVC_PREFIX}-mt" CACHE STRING "the suffix for the assimp windows library" ) set(ASSIMP_LIBRARY_SUFFIX "@ASSIMP_LIBRARY_SUFFIX@-${MSVC_PREFIX}-mt" CACHE STRING "the suffix for the assimp windows library" )
file(TO_NATIVE_PATH ${_IMPORT_PREFIX} _IMPORT_PREFIX)
if(ASSIMP_BUILD_SHARED_LIBS) if(ASSIMP_BUILD_SHARED_LIBS)
set(sharedLibraryName "assimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_SHARED_LIBRARY_SUFFIX@") set(sharedLibraryName "assimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_SHARED_LIBRARY_SUFFIX@")
set(importLibraryName "assimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_IMPORT_LIBRARY_SUFFIX@") set(importLibraryName "assimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_IMPORT_LIBRARY_SUFFIX@")
@ -63,9 +65,17 @@ if(MSVC)
else() else()
set(ASSIMP_LIBRARY_SUFFIX "@ASSIMP_LIBRARY_SUFFIX@" CACHE STRING "the suffix for the assimp libraries" ) set(ASSIMP_LIBRARY_SUFFIX "@ASSIMP_LIBRARY_SUFFIX@" CACHE STRING "the suffix for the assimp libraries" )
if(ASSIMP_BUILD_SHARED_LIBS) if(ASSIMP_BUILD_SHARED_LIBS)
if(WIN32)
# Handle MinGW compiler.
set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@@CMAKE_SHARED_LIBRARY_SUFFIX@@CMAKE_STATIC_LIBRARY_SUFFIX@")
elseif(APPLE)
set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}.@ASSIMP_VERSION_MAJOR@@CMAKE_SHARED_LIBRARY_SUFFIX@")
else()
set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_SHARED_LIBRARY_SUFFIX@.@ASSIMP_VERSION_MAJOR@") set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_SHARED_LIBRARY_SUFFIX@.@ASSIMP_VERSION_MAJOR@")
endif()
set_target_properties(assimp::assimp PROPERTIES set_target_properties(assimp::assimp PROPERTIES
IMPORTED_SONAME_RELEASE "${sharedLibraryName}" IMPORTED_SONAME_RELEASE "${sharedLibraryName}"
IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/lib/${sharedLibraryName}" IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/lib/${sharedLibraryName}"
) )
list(APPEND _IMPORT_CHECK_TARGETS assimp::assimp ) list(APPEND _IMPORT_CHECK_TARGETS assimp::assimp )

View File

@ -5,6 +5,9 @@ if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" LESS 2.5)
endif() endif()
cmake_policy(PUSH) cmake_policy(PUSH)
cmake_policy(VERSION 2.6) cmake_policy(VERSION 2.6)
# Required for the evaluation of "if(@BUILD_SHARED_LIBS@)" below to function
cmake_policy(SET CMP0012 NEW)
#---------------------------------------------------------------- #----------------------------------------------------------------
# Generated CMake target import file. # Generated CMake target import file.
#---------------------------------------------------------------- #----------------------------------------------------------------
@ -51,11 +54,7 @@ if(_IMPORT_PREFIX STREQUAL "/")
endif() endif()
# Create imported target assimp::assimp # Create imported target assimp::assimp
if(@BUILD_SHARED_LIBS@) add_library(assimp::assimp @BUILD_LIB_TYPE@ IMPORTED)
add_library(assimp::assimp SHARED IMPORTED)
else()
add_library(assimp::assimp STATIC IMPORTED)
endif()
set_target_properties(assimp::assimp PROPERTIES set_target_properties(assimp::assimp PROPERTIES
COMPATIBLE_INTERFACE_STRING "assimp_MAJOR_VERSION" COMPATIBLE_INTERFACE_STRING "assimp_MAJOR_VERSION"

View File

@ -54,14 +54,18 @@ else(WIN32)
find_path( find_path(
assimp_INCLUDE_DIRS assimp_INCLUDE_DIRS
NAMES postprocess.h scene.h version.h config.h cimport.h NAMES assimp/postprocess.h assimp/scene.h assimp/version.h assimp/config.h assimp/cimport.h
PATHS /usr/local/include/ PATHS /usr/local/include
PATHS /usr/include/
) )
find_library( find_library(
assimp_LIBRARIES assimp_LIBRARIES
NAMES assimp NAMES assimp
PATHS /usr/local/lib/ PATHS /usr/local/lib/
PATHS /usr/lib64/
PATHS /usr/lib/
) )
if (assimp_INCLUDE_DIRS AND assimp_LIBRARIES) if (assimp_INCLUDE_DIRS AND assimp_LIBRARIES)

View File

@ -72,7 +72,7 @@ void Discreet3DSImporter::ReplaceDefaultMaterial()
unsigned int idx( NotSet ); unsigned int idx( NotSet );
for (unsigned int i = 0; i < mScene->mMaterials.size();++i) for (unsigned int i = 0; i < mScene->mMaterials.size();++i)
{ {
std::string &s = mScene->mMaterials[i].mName; std::string s = mScene->mMaterials[i].mName;
for ( std::string::iterator it = s.begin(); it != s.end(); ++it ) { for ( std::string::iterator it = s.begin(); it != s.end(); ++it ) {
*it = static_cast< char >( ::tolower( *it ) ); *it = static_cast< char >( ::tolower( *it ) );
} }

View File

@ -50,9 +50,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER #ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
// internal headers
#include "3DSLoader.h" #include "3DSLoader.h"
#include <assimp/Macros.h>
#include <assimp/IOSystem.hpp> #include <assimp/IOSystem.hpp>
#include <assimp/scene.h> #include <assimp/scene.h>
#include <assimp/DefaultLogger.hpp> #include <assimp/DefaultLogger.hpp>

View File

@ -137,7 +137,6 @@ SET( PUBLIC_HEADERS
${HEADER_PATH}/irrXMLWrapper.h ${HEADER_PATH}/irrXMLWrapper.h
${HEADER_PATH}/BlobIOSystem.h ${HEADER_PATH}/BlobIOSystem.h
${HEADER_PATH}/MathFunctions.h ${HEADER_PATH}/MathFunctions.h
${HEADER_PATH}/Macros.h
${HEADER_PATH}/Exceptional.h ${HEADER_PATH}/Exceptional.h
${HEADER_PATH}/ByteSwapper.h ${HEADER_PATH}/ByteSwapper.h
) )
@ -408,6 +407,18 @@ ADD_ASSIMP_IMPORTER( LWS
LWS/LWSLoader.h LWS/LWSLoader.h
) )
ADD_ASSIMP_IMPORTER( M3D
M3D/M3DMaterials.h
M3D/M3DImporter.h
M3D/M3DImporter.cpp
M3D/m3d.h
)
ADD_ASSIMP_EXPORTER( M3D
M3D/M3DExporter.h
M3D/M3DExporter.cpp
)
ADD_ASSIMP_IMPORTER( MD2 ADD_ASSIMP_IMPORTER( MD2
MD2/MD2FileData.h MD2/MD2FileData.h
MD2/MD2Loader.cpp MD2/MD2Loader.cpp
@ -671,6 +682,8 @@ SET( PostProcessing_SRCS
PostProcessing/MakeVerboseFormat.h PostProcessing/MakeVerboseFormat.h
PostProcessing/ScaleProcess.cpp PostProcessing/ScaleProcess.cpp
PostProcessing/ScaleProcess.h PostProcessing/ScaleProcess.h
PostProcessing/ArmaturePopulate.cpp
PostProcessing/ArmaturePopulate.h
PostProcessing/GenBoundingBoxesProcess.cpp PostProcessing/GenBoundingBoxesProcess.cpp
PostProcessing/GenBoundingBoxesProcess.h PostProcessing/GenBoundingBoxesProcess.h
) )
@ -1059,6 +1072,8 @@ MESSAGE(STATUS "Disabled importer formats:${ASSIMP_IMPORTERS_DISABLED}")
MESSAGE(STATUS "Enabled exporter formats:${ASSIMP_EXPORTERS_ENABLED}") MESSAGE(STATUS "Enabled exporter formats:${ASSIMP_EXPORTERS_ENABLED}")
MESSAGE(STATUS "Disabled exporter formats:${ASSIMP_EXPORTERS_DISABLED}") MESSAGE(STATUS "Disabled exporter formats:${ASSIMP_EXPORTERS_DISABLED}")
SOURCE_GROUP( include\\assimp FILES ${PUBLIC_HEADERS} )
SET( assimp_src SET( assimp_src
# Assimp Files # Assimp Files
${Core_SRCS} ${Core_SRCS}

View File

@ -92,6 +92,36 @@ void ExportSceneCollada(const char* pFile, IOSystem* pIOSystem, const aiScene* p
} // end of namespace Assimp } // end of namespace Assimp
// ------------------------------------------------------------------------------------------------
// Encodes a string into a valid XML ID using the xsd:ID schema qualifications.
static const std::string XMLIDEncode(const std::string& name) {
const char XML_ID_CHARS[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-.";
const unsigned int XML_ID_CHARS_COUNT = sizeof(XML_ID_CHARS) / sizeof(char);
if (name.length() == 0) {
return name;
}
std::stringstream idEncoded;
// xsd:ID must start with letter or underscore
if (!((name[0] >= 'A' && name[0] <= 'z') || name[0] == '_')) {
idEncoded << '_';
}
for (std::string::const_iterator it = name.begin(); it != name.end(); ++it) {
// xsd:ID can only contain letters, digits, underscores, hyphens and periods
if (strchr(XML_ID_CHARS, *it) != nullptr) {
idEncoded << *it;
} else {
// Select placeholder character based on invalid character to prevent name collisions
idEncoded << XML_ID_CHARS[(*it) % XML_ID_CHARS_COUNT];
}
}
return idEncoded.str();
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Constructor for a specific scene to export // Constructor for a specific scene to export
ColladaExporter::ColladaExporter( const aiScene* pScene, IOSystem* pIOSystem, const std::string& path, const std::string& file) ColladaExporter::ColladaExporter( const aiScene* pScene, IOSystem* pIOSystem, const std::string& path, const std::string& file)
@ -146,7 +176,7 @@ void ColladaExporter::WriteFile() {
// useless Collada fu at the end, just in case we haven't had enough indirections, yet. // useless Collada fu at the end, just in case we haven't had enough indirections, yet.
mOutput << startstr << "<scene>" << endstr; mOutput << startstr << "<scene>" << endstr;
PushTag(); PushTag();
mOutput << startstr << "<instance_visual_scene url=\"#" + XMLEscape(mScene->mRootNode->mName.C_Str()) + "\" />" << endstr; mOutput << startstr << "<instance_visual_scene url=\"#" + XMLIDEncode(mScene->mRootNode->mName.C_Str()) + "\" />" << endstr;
PopTag(); PopTag();
mOutput << startstr << "</scene>" << endstr; mOutput << startstr << "</scene>" << endstr;
PopTag(); PopTag();
@ -356,9 +386,10 @@ void ColladaExporter::WriteCamerasLibrary() {
void ColladaExporter::WriteCamera(size_t pIndex){ void ColladaExporter::WriteCamera(size_t pIndex){
const aiCamera *cam = mScene->mCameras[pIndex]; const aiCamera *cam = mScene->mCameras[pIndex];
const std::string idstrEscaped = XMLEscape(cam->mName.C_Str()); const std::string cameraName = XMLEscape(cam->mName.C_Str());
const std::string cameraId = XMLIDEncode(cam->mName.C_Str());
mOutput << startstr << "<camera id=\"" << idstrEscaped << "-camera\" name=\"" << idstrEscaped << "_name\" >" << endstr; mOutput << startstr << "<camera id=\"" << cameraId << "-camera\" name=\"" << cameraName << "\" >" << endstr;
PushTag(); PushTag();
mOutput << startstr << "<optics>" << endstr; mOutput << startstr << "<optics>" << endstr;
PushTag(); PushTag();
@ -412,10 +443,11 @@ void ColladaExporter::WriteLightsLibrary() {
void ColladaExporter::WriteLight(size_t pIndex){ void ColladaExporter::WriteLight(size_t pIndex){
const aiLight *light = mScene->mLights[pIndex]; const aiLight *light = mScene->mLights[pIndex];
const std::string idstrEscaped = XMLEscape(light->mName.C_Str()); const std::string lightName = XMLEscape(light->mName.C_Str());
const std::string lightId = XMLIDEncode(light->mName.C_Str());
mOutput << startstr << "<light id=\"" << idstrEscaped << "-light\" name=\"" mOutput << startstr << "<light id=\"" << lightId << "-light\" name=\""
<< idstrEscaped << "_name\" >" << endstr; << lightName << "\" >" << endstr;
PushTag(); PushTag();
mOutput << startstr << "<technique_common>" << endstr; mOutput << startstr << "<technique_common>" << endstr;
PushTag(); PushTag();
@ -586,7 +618,7 @@ static bool isalnum_C(char c) {
void ColladaExporter::WriteImageEntry( const Surface& pSurface, const std::string& pNameAdd) { void ColladaExporter::WriteImageEntry( const Surface& pSurface, const std::string& pNameAdd) {
if( !pSurface.texture.empty() ) if( !pSurface.texture.empty() )
{ {
mOutput << startstr << "<image id=\"" << XMLEscape(pNameAdd) << "\">" << endstr; mOutput << startstr << "<image id=\"" << XMLIDEncode(pNameAdd) << "\">" << endstr;
PushTag(); PushTag();
mOutput << startstr << "<init_from>"; mOutput << startstr << "<init_from>";
@ -619,7 +651,7 @@ void ColladaExporter::WriteTextureColorEntry( const Surface& pSurface, const std
} }
else else
{ {
mOutput << startstr << "<texture texture=\"" << XMLEscape(pImageName) << "\" texcoord=\"CHANNEL" << pSurface.channel << "\" />" << endstr; mOutput << startstr << "<texture texture=\"" << XMLIDEncode(pImageName) << "\" texcoord=\"CHANNEL" << pSurface.channel << "\" />" << endstr;
} }
PopTag(); PopTag();
mOutput << startstr << "</" << pTypeName << ">" << endstr; mOutput << startstr << "</" << pTypeName << ">" << endstr;
@ -633,21 +665,21 @@ void ColladaExporter::WriteTextureParamEntry( const Surface& pSurface, const std
// if surface is a texture, write out the sampler and the surface parameters necessary to reference the texture // if surface is a texture, write out the sampler and the surface parameters necessary to reference the texture
if( !pSurface.texture.empty() ) if( !pSurface.texture.empty() )
{ {
mOutput << startstr << "<newparam sid=\"" << XMLEscape(pMatName) << "-" << pTypeName << "-surface\">" << endstr; mOutput << startstr << "<newparam sid=\"" << XMLIDEncode(pMatName) << "-" << pTypeName << "-surface\">" << endstr;
PushTag(); PushTag();
mOutput << startstr << "<surface type=\"2D\">" << endstr; mOutput << startstr << "<surface type=\"2D\">" << endstr;
PushTag(); PushTag();
mOutput << startstr << "<init_from>" << XMLEscape(pMatName) << "-" << pTypeName << "-image</init_from>" << endstr; mOutput << startstr << "<init_from>" << XMLIDEncode(pMatName) << "-" << pTypeName << "-image</init_from>" << endstr;
PopTag(); PopTag();
mOutput << startstr << "</surface>" << endstr; mOutput << startstr << "</surface>" << endstr;
PopTag(); PopTag();
mOutput << startstr << "</newparam>" << endstr; mOutput << startstr << "</newparam>" << endstr;
mOutput << startstr << "<newparam sid=\"" << XMLEscape(pMatName) << "-" << pTypeName << "-sampler\">" << endstr; mOutput << startstr << "<newparam sid=\"" << XMLIDEncode(pMatName) << "-" << pTypeName << "-sampler\">" << endstr;
PushTag(); PushTag();
mOutput << startstr << "<sampler2D>" << endstr; mOutput << startstr << "<sampler2D>" << endstr;
PushTag(); PushTag();
mOutput << startstr << "<source>" << XMLEscape(pMatName) << "-" << pTypeName << "-surface</source>" << endstr; mOutput << startstr << "<source>" << XMLIDEncode(pMatName) << "-" << pTypeName << "-surface</source>" << endstr;
PopTag(); PopTag();
mOutput << startstr << "</sampler2D>" << endstr; mOutput << startstr << "</sampler2D>" << endstr;
PopTag(); PopTag();
@ -699,11 +731,6 @@ void ColladaExporter::WriteMaterials()
materials[a].name = std::string(name.C_Str()) + to_string(materialCountWithThisName); materials[a].name = std::string(name.C_Str()) + to_string(materialCountWithThisName);
} }
} }
for( std::string::iterator it = materials[a].name.begin(); it != materials[a].name.end(); ++it ) {
if( !isalnum_C( *it ) ) {
*it = '_';
}
}
aiShadingMode shading = aiShadingMode_Flat; aiShadingMode shading = aiShadingMode_Flat;
materials[a].shading_model = "phong"; materials[a].shading_model = "phong";
@ -768,7 +795,7 @@ void ColladaExporter::WriteMaterials()
{ {
const Material& mat = *it; const Material& mat = *it;
// this is so ridiculous it must be right // this is so ridiculous it must be right
mOutput << startstr << "<effect id=\"" << XMLEscape(mat.name) << "-fx\" name=\"" << XMLEscape(mat.name) << "\">" << endstr; mOutput << startstr << "<effect id=\"" << XMLIDEncode(mat.name) << "-fx\" name=\"" << XMLEscape(mat.name) << "\">" << endstr;
PushTag(); PushTag();
mOutput << startstr << "<profile_COMMON>" << endstr; mOutput << startstr << "<profile_COMMON>" << endstr;
PushTag(); PushTag();
@ -819,9 +846,9 @@ void ColladaExporter::WriteMaterials()
for( std::vector<Material>::const_iterator it = materials.begin(); it != materials.end(); ++it ) for( std::vector<Material>::const_iterator it = materials.begin(); it != materials.end(); ++it )
{ {
const Material& mat = *it; const Material& mat = *it;
mOutput << startstr << "<material id=\"" << XMLEscape(mat.name) << "\" name=\"" << mat.name << "\">" << endstr; mOutput << startstr << "<material id=\"" << XMLIDEncode(mat.name) << "\" name=\"" << XMLEscape(mat.name) << "\">" << endstr;
PushTag(); PushTag();
mOutput << startstr << "<instance_effect url=\"#" << XMLEscape(mat.name) << "-fx\"/>" << endstr; mOutput << startstr << "<instance_effect url=\"#" << XMLIDEncode(mat.name) << "-fx\"/>" << endstr;
PopTag(); PopTag();
mOutput << startstr << "</material>" << endstr; mOutput << startstr << "</material>" << endstr;
} }
@ -850,8 +877,8 @@ void ColladaExporter::WriteControllerLibrary()
void ColladaExporter::WriteController( size_t pIndex) void ColladaExporter::WriteController( size_t pIndex)
{ {
const aiMesh* mesh = mScene->mMeshes[pIndex]; const aiMesh* mesh = mScene->mMeshes[pIndex];
const std::string idstr = GetMeshId( pIndex); const std::string idstr = mesh->mName.length == 0 ? GetMeshId(pIndex) : mesh->mName.C_Str();
const std::string idstrEscaped = XMLEscape(idstr); const std::string idstrEscaped = XMLIDEncode(idstr);
if ( mesh->mNumFaces == 0 || mesh->mNumVertices == 0 ) if ( mesh->mNumFaces == 0 || mesh->mNumVertices == 0 )
return; return;
@ -886,7 +913,7 @@ void ColladaExporter::WriteController( size_t pIndex)
mOutput << startstr << "<Name_array id=\"" << idstrEscaped << "-skin-joints-array\" count=\"" << mesh->mNumBones << "\">"; mOutput << startstr << "<Name_array id=\"" << idstrEscaped << "-skin-joints-array\" count=\"" << mesh->mNumBones << "\">";
for( size_t i = 0; i < mesh->mNumBones; ++i ) for( size_t i = 0; i < mesh->mNumBones; ++i )
mOutput << XMLEscape(mesh->mBones[i]->mName.C_Str()) << " "; mOutput << XMLIDEncode(mesh->mBones[i]->mName.C_Str()) << " ";
mOutput << "</Name_array>" << endstr; mOutput << "</Name_array>" << endstr;
@ -1021,14 +1048,15 @@ void ColladaExporter::WriteGeometryLibrary()
void ColladaExporter::WriteGeometry( size_t pIndex) void ColladaExporter::WriteGeometry( size_t pIndex)
{ {
const aiMesh* mesh = mScene->mMeshes[pIndex]; const aiMesh* mesh = mScene->mMeshes[pIndex];
const std::string idstr = GetMeshId( pIndex); const std::string idstr = mesh->mName.length == 0 ? GetMeshId(pIndex) : mesh->mName.C_Str();
const std::string idstrEscaped = XMLEscape(idstr); const std::string geometryName = XMLEscape(idstr);
const std::string geometryId = XMLIDEncode(idstr);
if ( mesh->mNumFaces == 0 || mesh->mNumVertices == 0 ) if ( mesh->mNumFaces == 0 || mesh->mNumVertices == 0 )
return; return;
// opening tag // opening tag
mOutput << startstr << "<geometry id=\"" << idstrEscaped << "\" name=\"" << idstrEscaped << "_name\" >" << endstr; mOutput << startstr << "<geometry id=\"" << geometryId << "\" name=\"" << geometryName << "\" >" << endstr;
PushTag(); PushTag();
mOutput << startstr << "<mesh>" << endstr; mOutput << startstr << "<mesh>" << endstr;
@ -1059,9 +1087,9 @@ void ColladaExporter::WriteGeometry( size_t pIndex)
// assemble vertex structure // assemble vertex structure
// Only write input for POSITION since we will write other as shared inputs in polygon definition // Only write input for POSITION since we will write other as shared inputs in polygon definition
mOutput << startstr << "<vertices id=\"" << idstrEscaped << "-vertices" << "\">" << endstr; mOutput << startstr << "<vertices id=\"" << geometryId << "-vertices" << "\">" << endstr;
PushTag(); PushTag();
mOutput << startstr << "<input semantic=\"POSITION\" source=\"#" << idstrEscaped << "-positions\" />" << endstr; mOutput << startstr << "<input semantic=\"POSITION\" source=\"#" << geometryId << "-positions\" />" << endstr;
PopTag(); PopTag();
mOutput << startstr << "</vertices>" << endstr; mOutput << startstr << "</vertices>" << endstr;
@ -1079,18 +1107,18 @@ void ColladaExporter::WriteGeometry( size_t pIndex)
{ {
mOutput << startstr << "<lines count=\"" << countLines << "\" material=\"defaultMaterial\">" << endstr; mOutput << startstr << "<lines count=\"" << countLines << "\" material=\"defaultMaterial\">" << endstr;
PushTag(); PushTag();
mOutput << startstr << "<input offset=\"0\" semantic=\"VERTEX\" source=\"#" << idstrEscaped << "-vertices\" />" << endstr; mOutput << startstr << "<input offset=\"0\" semantic=\"VERTEX\" source=\"#" << geometryId << "-vertices\" />" << endstr;
if( mesh->HasNormals() ) if( mesh->HasNormals() )
mOutput << startstr << "<input semantic=\"NORMAL\" source=\"#" << idstrEscaped << "-normals\" />" << endstr; mOutput << startstr << "<input semantic=\"NORMAL\" source=\"#" << geometryId << "-normals\" />" << endstr;
for( size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a ) for( size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a )
{ {
if( mesh->HasTextureCoords(static_cast<unsigned int>(a)) ) if( mesh->HasTextureCoords(static_cast<unsigned int>(a)) )
mOutput << startstr << "<input semantic=\"TEXCOORD\" source=\"#" << idstrEscaped << "-tex" << a << "\" " << "set=\"" << a << "\"" << " />" << endstr; mOutput << startstr << "<input semantic=\"TEXCOORD\" source=\"#" << geometryId << "-tex" << a << "\" " << "set=\"" << a << "\"" << " />" << endstr;
} }
for( size_t a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a ) for( size_t a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a )
{ {
if( mesh->HasVertexColors(static_cast<unsigned int>(a) ) ) if( mesh->HasVertexColors(static_cast<unsigned int>(a) ) )
mOutput << startstr << "<input semantic=\"COLOR\" source=\"#" << idstrEscaped << "-color" << a << "\" " << "set=\"" << a << "\"" << " />" << endstr; mOutput << startstr << "<input semantic=\"COLOR\" source=\"#" << geometryId << "-color" << a << "\" " << "set=\"" << a << "\"" << " />" << endstr;
} }
mOutput << startstr << "<p>"; mOutput << startstr << "<p>";
@ -1113,18 +1141,18 @@ void ColladaExporter::WriteGeometry( size_t pIndex)
{ {
mOutput << startstr << "<polylist count=\"" << countPoly << "\" material=\"defaultMaterial\">" << endstr; mOutput << startstr << "<polylist count=\"" << countPoly << "\" material=\"defaultMaterial\">" << endstr;
PushTag(); PushTag();
mOutput << startstr << "<input offset=\"0\" semantic=\"VERTEX\" source=\"#" << idstrEscaped << "-vertices\" />" << endstr; mOutput << startstr << "<input offset=\"0\" semantic=\"VERTEX\" source=\"#" << geometryId << "-vertices\" />" << endstr;
if( mesh->HasNormals() ) if( mesh->HasNormals() )
mOutput << startstr << "<input offset=\"0\" semantic=\"NORMAL\" source=\"#" << idstrEscaped << "-normals\" />" << endstr; mOutput << startstr << "<input offset=\"0\" semantic=\"NORMAL\" source=\"#" << geometryId << "-normals\" />" << endstr;
for( size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a ) for( size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a )
{ {
if( mesh->HasTextureCoords(static_cast<unsigned int>(a)) ) if( mesh->HasTextureCoords(static_cast<unsigned int>(a)) )
mOutput << startstr << "<input offset=\"0\" semantic=\"TEXCOORD\" source=\"#" << idstrEscaped << "-tex" << a << "\" " << "set=\"" << a << "\"" << " />" << endstr; mOutput << startstr << "<input offset=\"0\" semantic=\"TEXCOORD\" source=\"#" << geometryId << "-tex" << a << "\" " << "set=\"" << a << "\"" << " />" << endstr;
} }
for( size_t a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a ) for( size_t a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a )
{ {
if( mesh->HasVertexColors(static_cast<unsigned int>(a) ) ) if( mesh->HasVertexColors(static_cast<unsigned int>(a) ) )
mOutput << startstr << "<input offset=\"0\" semantic=\"COLOR\" source=\"#" << idstrEscaped << "-color" << a << "\" " << "set=\"" << a << "\"" << " />" << endstr; mOutput << startstr << "<input offset=\"0\" semantic=\"COLOR\" source=\"#" << geometryId << "-color" << a << "\" " << "set=\"" << a << "\"" << " />" << endstr;
} }
mOutput << startstr << "<vcount>"; mOutput << startstr << "<vcount>";
@ -1173,13 +1201,13 @@ void ColladaExporter::WriteFloatArray( const std::string& pIdString, FloatDataTy
return; return;
} }
std::string arrayId = pIdString + "-array"; std::string arrayId = XMLIDEncode(pIdString) + "-array";
mOutput << startstr << "<source id=\"" << XMLEscape(pIdString) << "\" name=\"" << XMLEscape(pIdString) << "\">" << endstr; mOutput << startstr << "<source id=\"" << XMLIDEncode(pIdString) << "\" name=\"" << XMLEscape(pIdString) << "\">" << endstr;
PushTag(); PushTag();
// source array // source array
mOutput << startstr << "<float_array id=\"" << XMLEscape(arrayId) << "\" count=\"" << pElementCount * floatsPerElement << "\"> "; mOutput << startstr << "<float_array id=\"" << arrayId << "\" count=\"" << pElementCount * floatsPerElement << "\"> ";
PushTag(); PushTag();
if( pType == FloatType_TexCoord2 ) if( pType == FloatType_TexCoord2 )
@ -1265,11 +1293,12 @@ void ColladaExporter::WriteFloatArray( const std::string& pIdString, FloatDataTy
// Writes the scene library // Writes the scene library
void ColladaExporter::WriteSceneLibrary() void ColladaExporter::WriteSceneLibrary()
{ {
const std::string scene_name_escaped = XMLEscape(mScene->mRootNode->mName.C_Str()); const std::string sceneName = XMLEscape(mScene->mRootNode->mName.C_Str());
const std::string sceneId = XMLIDEncode(mScene->mRootNode->mName.C_Str());
mOutput << startstr << "<library_visual_scenes>" << endstr; mOutput << startstr << "<library_visual_scenes>" << endstr;
PushTag(); PushTag();
mOutput << startstr << "<visual_scene id=\"" + scene_name_escaped + "\" name=\"" + scene_name_escaped + "\">" << endstr; mOutput << startstr << "<visual_scene id=\"" + sceneId + "\" name=\"" + sceneName + "\">" << endstr;
PushTag(); PushTag();
// start recursive write at the root node // start recursive write at the root node
@ -1300,7 +1329,7 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex)
idstr = idstr + ending; idstr = idstr + ending;
} }
const std::string idstrEscaped = XMLEscape(idstr); const std::string idstrEscaped = XMLIDEncode(idstr);
mOutput << startstr << "<animation id=\"" + idstrEscaped + "\" name=\"" + animation_name_escaped + "\">" << endstr; mOutput << startstr << "<animation id=\"" + idstrEscaped + "\" name=\"" + animation_name_escaped + "\">" << endstr;
PushTag(); PushTag();
@ -1372,13 +1401,13 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex)
} }
const std::string node_idstr = nodeAnim->mNodeName.data + std::string("_matrix-interpolation"); const std::string node_idstr = nodeAnim->mNodeName.data + std::string("_matrix-interpolation");
std::string arrayId = node_idstr + "-array"; std::string arrayId = XMLIDEncode(node_idstr) + "-array";
mOutput << startstr << "<source id=\"" << XMLEscape(node_idstr) << "\">" << endstr; mOutput << startstr << "<source id=\"" << XMLIDEncode(node_idstr) << "\">" << endstr;
PushTag(); PushTag();
// source array // source array
mOutput << startstr << "<Name_array id=\"" << XMLEscape(arrayId) << "\" count=\"" << names.size() << "\"> "; mOutput << startstr << "<Name_array id=\"" << arrayId << "\" count=\"" << names.size() << "\"> ";
for( size_t a = 0; a < names.size(); ++a ) { for( size_t a = 0; a < names.size(); ++a ) {
mOutput << names[a] << " "; mOutput << names[a] << " ";
} }
@ -1387,7 +1416,7 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex)
mOutput << startstr << "<technique_common>" << endstr; mOutput << startstr << "<technique_common>" << endstr;
PushTag(); PushTag();
mOutput << startstr << "<accessor source=\"#" << XMLEscape(arrayId) << "\" count=\"" << names.size() << "\" stride=\"" << 1 << "\">" << endstr; mOutput << startstr << "<accessor source=\"#" << arrayId << "\" count=\"" << names.size() << "\" stride=\"" << 1 << "\">" << endstr;
PushTag(); PushTag();
mOutput << startstr << "<param name=\"INTERPOLATION\" type=\"name\"></param>" << endstr; mOutput << startstr << "<param name=\"INTERPOLATION\" type=\"name\"></param>" << endstr;
@ -1409,12 +1438,12 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex)
{ {
// samplers // samplers
const std::string node_idstr = nodeAnim->mNodeName.data + std::string("_matrix-sampler"); const std::string node_idstr = nodeAnim->mNodeName.data + std::string("_matrix-sampler");
mOutput << startstr << "<sampler id=\"" << XMLEscape(node_idstr) << "\">" << endstr; mOutput << startstr << "<sampler id=\"" << XMLIDEncode(node_idstr) << "\">" << endstr;
PushTag(); PushTag();
mOutput << startstr << "<input semantic=\"INPUT\" source=\"#" << XMLEscape( nodeAnim->mNodeName.data + std::string("_matrix-input") ) << "\"/>" << endstr; mOutput << startstr << "<input semantic=\"INPUT\" source=\"#" << XMLIDEncode( nodeAnim->mNodeName.data + std::string("_matrix-input") ) << "\"/>" << endstr;
mOutput << startstr << "<input semantic=\"OUTPUT\" source=\"#" << XMLEscape( nodeAnim->mNodeName.data + std::string("_matrix-output") ) << "\"/>" << endstr; mOutput << startstr << "<input semantic=\"OUTPUT\" source=\"#" << XMLIDEncode( nodeAnim->mNodeName.data + std::string("_matrix-output") ) << "\"/>" << endstr;
mOutput << startstr << "<input semantic=\"INTERPOLATION\" source=\"#" << XMLEscape( nodeAnim->mNodeName.data + std::string("_matrix-interpolation") ) << "\"/>" << endstr; mOutput << startstr << "<input semantic=\"INTERPOLATION\" source=\"#" << XMLIDEncode( nodeAnim->mNodeName.data + std::string("_matrix-interpolation") ) << "\"/>" << endstr;
PopTag(); PopTag();
mOutput << startstr << "</sampler>" << endstr; mOutput << startstr << "</sampler>" << endstr;
@ -1426,7 +1455,7 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex)
{ {
// channels // channels
mOutput << startstr << "<channel source=\"#" << XMLEscape( nodeAnim->mNodeName.data + std::string("_matrix-sampler") ) << "\" target=\"" << XMLEscape(nodeAnim->mNodeName.data) << "/matrix\"/>" << endstr; mOutput << startstr << "<channel source=\"#" << XMLIDEncode( nodeAnim->mNodeName.data + std::string("_matrix-sampler") ) << "\" target=\"" << XMLIDEncode(nodeAnim->mNodeName.data) << "/matrix\"/>" << endstr;
} }
} }
@ -1437,8 +1466,6 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex)
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void ColladaExporter::WriteAnimationsLibrary() void ColladaExporter::WriteAnimationsLibrary()
{ {
const std::string scene_name_escaped = XMLEscape(mScene->mRootNode->mName.C_Str());
if ( mScene->mNumAnimations > 0 ) { if ( mScene->mNumAnimations > 0 ) {
mOutput << startstr << "<library_animations>" << endstr; mOutput << startstr << "<library_animations>" << endstr;
PushTag(); PushTag();
@ -1546,16 +1573,17 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode)
} }
} }
const std::string node_name_escaped = XMLEscape(pNode->mName.data); const std::string node_id = XMLIDEncode(pNode->mName.data);
const std::string node_name = XMLEscape(pNode->mName.data);
mOutput << startstr << "<node "; mOutput << startstr << "<node ";
if(is_skeleton_root) { if(is_skeleton_root) {
mOutput << "id=\"" << node_name_escaped << "\" " << (is_joint ? "sid=\"" + node_name_escaped +"\"" : "") ; // For now, only support one skeleton in a scene. mOutput << "id=\"" << node_id << "\" " << (is_joint ? "sid=\"" + node_id +"\"" : "") ; // For now, only support one skeleton in a scene.
mFoundSkeletonRootNodeID = node_name_escaped; mFoundSkeletonRootNodeID = node_id;
} else { } else {
mOutput << "id=\"" << node_name_escaped << "\" " << (is_joint ? "sid=\"" + node_name_escaped +"\"": "") ; mOutput << "id=\"" << node_id << "\" " << (is_joint ? "sid=\"" + node_id +"\"": "") ;
} }
mOutput << " name=\"" << node_name_escaped mOutput << " name=\"" << node_name
<< "\" type=\"" << node_type << "\" type=\"" << node_type
<< "\">" << endstr; << "\">" << endstr;
PushTag(); PushTag();
@ -1594,14 +1622,14 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode)
//check if it is a camera node //check if it is a camera node
for(size_t i=0; i<mScene->mNumCameras; i++){ for(size_t i=0; i<mScene->mNumCameras; i++){
if(mScene->mCameras[i]->mName == pNode->mName){ if(mScene->mCameras[i]->mName == pNode->mName){
mOutput << startstr <<"<instance_camera url=\"#" << node_name_escaped << "-camera\"/>" << endstr; mOutput << startstr <<"<instance_camera url=\"#" << node_id << "-camera\"/>" << endstr;
break; break;
} }
} }
//check if it is a light node //check if it is a light node
for(size_t i=0; i<mScene->mNumLights; i++){ for(size_t i=0; i<mScene->mNumLights; i++){
if(mScene->mLights[i]->mName == pNode->mName){ if(mScene->mLights[i]->mName == pNode->mName){
mOutput << startstr <<"<instance_light url=\"#" << node_name_escaped << "-light\"/>" << endstr; mOutput << startstr <<"<instance_light url=\"#" << node_id << "-light\"/>" << endstr;
break; break;
} }
} }
@ -1615,15 +1643,17 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode)
if( mesh->mNumFaces == 0 || mesh->mNumVertices == 0 ) if( mesh->mNumFaces == 0 || mesh->mNumVertices == 0 )
continue; continue;
const std::string meshName = mesh->mName.length == 0 ? GetMeshId(pNode->mMeshes[a]) : mesh->mName.C_Str();
if( mesh->mNumBones == 0 ) if( mesh->mNumBones == 0 )
{ {
mOutput << startstr << "<instance_geometry url=\"#" << XMLEscape(GetMeshId( pNode->mMeshes[a])) << "\">" << endstr; mOutput << startstr << "<instance_geometry url=\"#" << XMLIDEncode(meshName) << "\">" << endstr;
PushTag(); PushTag();
} }
else else
{ {
mOutput << startstr mOutput << startstr
<< "<instance_controller url=\"#" << XMLEscape(GetMeshId( pNode->mMeshes[a])) << "-skin\">" << "<instance_controller url=\"#" << XMLIDEncode(meshName) << "-skin\">"
<< endstr; << endstr;
PushTag(); PushTag();
@ -1631,7 +1661,7 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode)
// use the first bone to find skeleton root // use the first bone to find skeleton root
const aiNode * skeletonRootBoneNode = findSkeletonRootNode( pScene, mesh ); const aiNode * skeletonRootBoneNode = findSkeletonRootNode( pScene, mesh );
if ( skeletonRootBoneNode ) { if ( skeletonRootBoneNode ) {
mFoundSkeletonRootNodeID = XMLEscape( skeletonRootBoneNode->mName.C_Str() ); mFoundSkeletonRootNodeID = XMLIDEncode( skeletonRootBoneNode->mName.C_Str() );
} }
mOutput << startstr << "<skeleton>#" << mFoundSkeletonRootNodeID << "</skeleton>" << endstr; mOutput << startstr << "<skeleton>#" << mFoundSkeletonRootNodeID << "</skeleton>" << endstr;
} }
@ -1639,7 +1669,7 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode)
PushTag(); PushTag();
mOutput << startstr << "<technique_common>" << endstr; mOutput << startstr << "<technique_common>" << endstr;
PushTag(); PushTag();
mOutput << startstr << "<instance_material symbol=\"defaultMaterial\" target=\"#" << XMLEscape(materials[mesh->mMaterialIndex].name) << "\">" << endstr; mOutput << startstr << "<instance_material symbol=\"defaultMaterial\" target=\"#" << XMLIDEncode(materials[mesh->mMaterialIndex].name) << "\">" << endstr;
PushTag(); PushTag();
for( size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a ) for( size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a )
{ {

View File

@ -583,7 +583,7 @@ struct Image
/** Embedded image data */ /** Embedded image data */
std::vector<uint8_t> mImageData; std::vector<uint8_t> mImageData;
/** File format hint ofembedded image data */ /** File format hint of embedded image data */
std::string mEmbeddedFormat; std::string mEmbeddedFormat;
}; };

View File

@ -1735,6 +1735,7 @@ void ColladaLoader::BuildMaterials(ColladaParser& pParser, aiScene* /*pScene*/)
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Resolves the texture name for the given effect texture entry // Resolves the texture name for the given effect texture entry
// and loads the texture data
aiString ColladaLoader::FindFilenameForEffectTexture(const ColladaParser& pParser, aiString ColladaLoader::FindFilenameForEffectTexture(const ColladaParser& pParser,
const Collada::Effect& pEffect, const std::string& pName) const Collada::Effect& pEffect, const std::string& pName)
{ {
@ -1762,7 +1763,7 @@ aiString ColladaLoader::FindFilenameForEffectTexture(const ColladaParser& pParse
//set default texture file name //set default texture file name
result.Set(name + ".jpg"); result.Set(name + ".jpg");
ConvertPath(result); ColladaParser::UriDecodePath(result);
return result; return result;
} }
@ -1781,7 +1782,7 @@ aiString ColladaLoader::FindFilenameForEffectTexture(const ColladaParser& pParse
// setup format hint // setup format hint
if (imIt->second.mEmbeddedFormat.length() > 3) { if (imIt->second.mEmbeddedFormat.length() >= HINTMAXTEXTURELEN) {
ASSIMP_LOG_WARN("Collada: texture format hint is too long, truncating to 3 characters"); ASSIMP_LOG_WARN("Collada: texture format hint is too long, truncating to 3 characters");
} }
strncpy(tex->achFormatHint, imIt->second.mEmbeddedFormat.c_str(), 3); strncpy(tex->achFormatHint, imIt->second.mEmbeddedFormat.c_str(), 3);
@ -1802,61 +1803,10 @@ aiString ColladaLoader::FindFilenameForEffectTexture(const ColladaParser& pParse
} }
result.Set(imIt->second.mFileName); result.Set(imIt->second.mFileName);
ConvertPath(result);
} }
return result; return result;
} }
// ------------------------------------------------------------------------------------------------
// Convert a path read from a collada file to the usual representation
void ColladaLoader::ConvertPath(aiString& ss)
{
// TODO: collada spec, p 22. Handle URI correctly.
// For the moment we're just stripping the file:// away to make it work.
// Windows doesn't seem to be able to find stuff like
// 'file://..\LWO\LWO2\MappingModes\earthSpherical.jpg'
if (0 == strncmp(ss.data, "file://", 7))
{
ss.length -= 7;
memmove(ss.data, ss.data + 7, ss.length);
ss.data[ss.length] = '\0';
}
// Maxon Cinema Collada Export writes "file:///C:\andsoon" with three slashes...
// I need to filter it without destroying linux paths starting with "/somewhere"
#if defined( _MSC_VER )
if (ss.data[0] == '/' && isalpha((unsigned char)ss.data[1]) && ss.data[2] == ':') {
#else
if (ss.data[0] == '/' && isalpha(ss.data[1]) && ss.data[2] == ':') {
#endif
--ss.length;
::memmove(ss.data, ss.data + 1, ss.length);
ss.data[ss.length] = 0;
}
// find and convert all %xy special chars
char* out = ss.data;
for (const char* it = ss.data; it != ss.data + ss.length; /**/)
{
if (*it == '%' && (it + 3) < ss.data + ss.length)
{
// separate the number to avoid dragging in chars from behind into the parsing
char mychar[3] = { it[1], it[2], 0 };
size_t nbr = strtoul16(mychar);
it += 3;
*out++ = (char)(nbr & 0xFF);
}
else
{
*out++ = *it++;
}
}
// adjust length and terminator of the shortened string
*out = 0;
ss.length = (ptrdiff_t)(out - ss.data);
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Reads a float value from an accessor and its data array. // Reads a float value from an accessor and its data array.
ai_real ColladaLoader::ReadFloat(const Collada::Accessor& pAccessor, const Collada::Data& pData, size_t pIndex, size_t pOffset) const ai_real ColladaLoader::ReadFloat(const Collada::Accessor& pAccessor, const Collada::Data& pData, size_t pIndex, size_t pOffset) const

View File

@ -94,7 +94,7 @@ public:
public: public:
/** Returns whether the class can handle the format of the given file. /** Returns whether the class can handle the format of the given file.
* See BaseImporter::CanRead() for details. */ * See BaseImporter::CanRead() for details. */
bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const override; bool CanRead(const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const override;
protected: protected:
/** Return importer meta information. /** Return importer meta information.
@ -184,9 +184,6 @@ protected:
aiString FindFilenameForEffectTexture( const ColladaParser& pParser, aiString FindFilenameForEffectTexture( const ColladaParser& pParser,
const Collada::Effect& pEffect, const std::string& pName); const Collada::Effect& pEffect, const std::string& pName);
/** Converts a path read from a collada file to the usual representation */
void ConvertPath( aiString& ss);
/** Reads a float value from an accessor and its data array. /** Reads a float value from an accessor and its data array.
* @param pAccessor The accessor to use for reading * @param pAccessor The accessor to use for reading
* @param pData The data array to read from * @param pData The data array to read from

View File

@ -183,13 +183,67 @@ std::string ColladaParser::ReadZaeManifest(ZipArchiveIOSystem &zip_archive) {
if (filepath == nullptr) if (filepath == nullptr)
return std::string(); return std::string();
return std::string(filepath); aiString ai_str(filepath);
UriDecodePath(ai_str);
return std::string(ai_str.C_Str());
} }
} }
} }
return std::string(); return std::string();
} }
// ------------------------------------------------------------------------------------------------
// Convert a path read from a collada file to the usual representation
void ColladaParser::UriDecodePath(aiString& ss)
{
// TODO: collada spec, p 22. Handle URI correctly.
// For the moment we're just stripping the file:// away to make it work.
// Windows doesn't seem to be able to find stuff like
// 'file://..\LWO\LWO2\MappingModes\earthSpherical.jpg'
if (0 == strncmp(ss.data, "file://", 7))
{
ss.length -= 7;
memmove(ss.data, ss.data + 7, ss.length);
ss.data[ss.length] = '\0';
}
// Maxon Cinema Collada Export writes "file:///C:\andsoon" with three slashes...
// I need to filter it without destroying linux paths starting with "/somewhere"
#if defined( _MSC_VER )
if (ss.data[0] == '/' && isalpha((unsigned char)ss.data[1]) && ss.data[2] == ':') {
#else
if (ss.data[0] == '/' && isalpha(ss.data[1]) && ss.data[2] == ':') {
#endif
--ss.length;
::memmove(ss.data, ss.data + 1, ss.length);
ss.data[ss.length] = 0;
}
// find and convert all %xy special chars
char* out = ss.data;
for (const char* it = ss.data; it != ss.data + ss.length; /**/)
{
if (*it == '%' && (it + 3) < ss.data + ss.length)
{
// separate the number to avoid dragging in chars from behind into the parsing
char mychar[3] = { it[1], it[2], 0 };
size_t nbr = strtoul16(mychar);
it += 3;
*out++ = (char)(nbr & 0xFF);
}
else
{
*out++ = *it++;
}
}
// adjust length and terminator of the shortened string
*out = 0;
ai_assert(out > ss.data);
ss.length = static_cast<ai_uint32>(out - ss.data);
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Read bool from text contents of current element // Read bool from text contents of current element
bool ColladaParser::ReadBoolFromTextContent() bool ColladaParser::ReadBoolFromTextContent()
@ -1120,7 +1174,12 @@ void ColladaParser::ReadImage(Collada::Image& pImage)
if (!mReader->isEmptyElement()) { if (!mReader->isEmptyElement()) {
// element content is filename - hopefully // element content is filename - hopefully
const char* sz = TestTextContent(); const char* sz = TestTextContent();
if (sz)pImage.mFileName = sz; if (sz)
{
aiString filepath(sz);
UriDecodePath(filepath);
pImage.mFileName = filepath.C_Str();
}
TestClosing("init_from"); TestClosing("init_from");
} }
if (!pImage.mFileName.length()) { if (!pImage.mFileName.length()) {
@ -1153,7 +1212,12 @@ void ColladaParser::ReadImage(Collada::Image& pImage)
{ {
// element content is filename - hopefully // element content is filename - hopefully
const char* sz = TestTextContent(); const char* sz = TestTextContent();
if (sz)pImage.mFileName = sz; if (sz)
{
aiString filepath(sz);
UriDecodePath(filepath);
pImage.mFileName = filepath.C_Str();
}
TestClosing("ref"); TestClosing("ref");
} }
else if (IsElement("hex") && !pImage.mFileName.length()) else if (IsElement("hex") && !pImage.mFileName.length())
@ -3056,7 +3120,7 @@ void ColladaParser::ReadMaterialVertexInputBinding(Collada::SemanticMappingTable
} }
} }
void Assimp::ColladaParser::ReadEmbeddedTextures(ZipArchiveIOSystem& zip_archive) void ColladaParser::ReadEmbeddedTextures(ZipArchiveIOSystem& zip_archive)
{ {
// Attempt to load any undefined Collada::Image in ImageLibrary // Attempt to load any undefined Collada::Image in ImageLibrary
for (ImageLibrary::iterator it = mImageLibrary.begin(); it != mImageLibrary.end(); ++it) { for (ImageLibrary::iterator it = mImageLibrary.begin(); it != mImageLibrary.end(); ++it) {

View File

@ -66,12 +66,15 @@ namespace Assimp
{ {
friend class ColladaLoader; friend class ColladaLoader;
/** Converts a path read from a collada file to the usual representation */
static void UriDecodePath(aiString& ss);
protected: protected:
/** Map for generic metadata as aiString */ /** Map for generic metadata as aiString */
typedef std::map<std::string, aiString> StringMetaData; typedef std::map<std::string, aiString> StringMetaData;
/** Constructor from XML file */ /** Constructor from XML file */
ColladaParser( IOSystem* pIOHandler, const std::string& pFile); ColladaParser(IOSystem* pIOHandler, const std::string& pFile);
/** Destructor */ /** Destructor */
~ColladaParser(); ~ColladaParser();

View File

@ -67,7 +67,20 @@ using namespace Assimp;
// Constructor to be privately used by Importer // Constructor to be privately used by Importer
BaseImporter::BaseImporter() AI_NO_EXCEPT BaseImporter::BaseImporter() AI_NO_EXCEPT
: m_progress() { : m_progress() {
// nothing to do here /**
* Assimp Importer
* unit conversions available
* if you need another measurment unit add it below.
* it's currently defined in assimp that we prefer meters.
*
* NOTE: Initialised here rather than in the header file
* to workaround a VS2013 bug with brace initialisers
* */
importerUnits[ImporterUnits::M] = 1.0;
importerUnits[ImporterUnits::CM] = 0.01;
importerUnits[ImporterUnits::MM] = 0.001;
importerUnits[ImporterUnits::INCHES] = 0.0254;
importerUnits[ImporterUnits::FEET] = 0.3048;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -52,6 +52,35 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using namespace Assimp; using namespace Assimp;
namespace
{
template<size_t sizeOfPointer>
size_t select_ftell(FILE* file)
{
return ::ftell(file);
}
template<size_t sizeOfPointer>
int select_fseek(FILE* file, int64_t offset, int origin)
{
return ::fseek(file, static_cast<long>(offset), origin);
}
#if defined _WIN32 && (!defined __GNUC__ || __MSVCRT_VERSION__ >= 0x0601)
template<>
size_t select_ftell<8>(FILE* file)
{
return ::_ftelli64(file);
}
template<>
int select_fseek<8>(FILE* file, int64_t offset, int origin)
{
return ::_fseeki64(file, offset, origin);
}
#endif
}
// ---------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------
DefaultIOStream::~DefaultIOStream() DefaultIOStream::~DefaultIOStream()
{ {
@ -93,7 +122,7 @@ aiReturn DefaultIOStream::Seek(size_t pOffset,
aiOrigin_END == SEEK_END && aiOrigin_SET == SEEK_SET"); aiOrigin_END == SEEK_END && aiOrigin_SET == SEEK_SET");
// do the seek // do the seek
return (0 == ::fseek(mFile, (long)pOffset,(int)pOrigin) ? AI_SUCCESS : AI_FAILURE); return (0 == select_fseek<sizeof(void*)>(mFile, (int64_t)pOffset,(int)pOrigin) ? AI_SUCCESS : AI_FAILURE);
} }
// ---------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------
@ -102,7 +131,7 @@ size_t DefaultIOStream::Tell() const
if (!mFile) { if (!mFile) {
return 0; return 0;
} }
return ::ftell(mFile); return select_ftell<sizeof(void*)>(mFile);
} }
// ---------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------

View File

@ -121,7 +121,7 @@ LogStream* LogStream::createDefaultStream(aiDefaultLogStream streams,
}; };
// For compilers without dead code path detection // For compilers without dead code path detection
return NULL; return nullptr;
} }
// ---------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------

View File

@ -102,94 +102,92 @@ void ExportSceneX3D(const char*, IOSystem*, const aiScene*, const ExportProperti
void ExportSceneFBX(const char*, IOSystem*, const aiScene*, const ExportProperties*); void ExportSceneFBX(const char*, IOSystem*, const aiScene*, const ExportProperties*);
void ExportSceneFBXA(const char*, IOSystem*, const aiScene*, const ExportProperties*); void ExportSceneFBXA(const char*, IOSystem*, const aiScene*, const ExportProperties*);
void ExportScene3MF( const char*, IOSystem*, const aiScene*, const ExportProperties* ); void ExportScene3MF( const char*, IOSystem*, const aiScene*, const ExportProperties* );
void ExportSceneM3D(const char*, IOSystem*, const aiScene*, const ExportProperties*);
void ExportSceneA3D(const char*, IOSystem*, const aiScene*, const ExportProperties*);
void ExportAssimp2Json(const char* , IOSystem*, const aiScene* , const Assimp::ExportProperties*); void ExportAssimp2Json(const char* , IOSystem*, const aiScene* , const Assimp::ExportProperties*);
// ------------------------------------------------------------------------------------------------
// global array of all export formats which Assimp supports in its current build static void setupExporterArray(std::vector<Exporter::ExportFormatEntry> &exporters) {
Exporter::ExportFormatEntry gExporters[] =
{
#ifndef ASSIMP_BUILD_NO_COLLADA_EXPORTER #ifndef ASSIMP_BUILD_NO_COLLADA_EXPORTER
Exporter::ExportFormatEntry( "collada", "COLLADA - Digital Asset Exchange Schema", "dae", &ExportSceneCollada ), exporters.push_back(Exporter::ExportFormatEntry("collada", "COLLADA - Digital Asset Exchange Schema", "dae", &ExportSceneCollada));
#endif #endif
#ifndef ASSIMP_BUILD_NO_X_EXPORTER #ifndef ASSIMP_BUILD_NO_X_EXPORTER
Exporter::ExportFormatEntry( "x", "X Files", "x", &ExportSceneXFile, exporters.push_back(Exporter::ExportFormatEntry("x", "X Files", "x", &ExportSceneXFile,
aiProcess_MakeLeftHanded | aiProcess_FlipWindingOrder | aiProcess_FlipUVs ), aiProcess_MakeLeftHanded | aiProcess_FlipWindingOrder | aiProcess_FlipUVs));
#endif #endif
#ifndef ASSIMP_BUILD_NO_STEP_EXPORTER #ifndef ASSIMP_BUILD_NO_STEP_EXPORTER
Exporter::ExportFormatEntry( "stp", "Step Files", "stp", &ExportSceneStep, 0 ), exporters.push_back(Exporter::ExportFormatEntry("stp", "Step Files", "stp", &ExportSceneStep, 0));
#endif #endif
#ifndef ASSIMP_BUILD_NO_OBJ_EXPORTER #ifndef ASSIMP_BUILD_NO_OBJ_EXPORTER
Exporter::ExportFormatEntry( "obj", "Wavefront OBJ format", "obj", &ExportSceneObj, exporters.push_back(Exporter::ExportFormatEntry("obj", "Wavefront OBJ format", "obj", &ExportSceneObj,
aiProcess_GenSmoothNormals /*| aiProcess_PreTransformVertices */ ), aiProcess_GenSmoothNormals /*| aiProcess_PreTransformVertices */));
Exporter::ExportFormatEntry( "objnomtl", "Wavefront OBJ format without material file", "obj", &ExportSceneObjNoMtl, exporters.push_back(Exporter::ExportFormatEntry("objnomtl", "Wavefront OBJ format without material file", "obj", &ExportSceneObjNoMtl,
aiProcess_GenSmoothNormals /*| aiProcess_PreTransformVertices */ ), aiProcess_GenSmoothNormals /*| aiProcess_PreTransformVertices */));
#endif #endif
#ifndef ASSIMP_BUILD_NO_STL_EXPORTER #ifndef ASSIMP_BUILD_NO_STL_EXPORTER
Exporter::ExportFormatEntry( "stl", "Stereolithography", "stl" , &ExportSceneSTL, exporters.push_back(Exporter::ExportFormatEntry("stl", "Stereolithography", "stl", &ExportSceneSTL,
aiProcess_Triangulate | aiProcess_GenNormals | aiProcess_PreTransformVertices aiProcess_Triangulate | aiProcess_GenNormals | aiProcess_PreTransformVertices));
), exporters.push_back(Exporter::ExportFormatEntry("stlb", "Stereolithography (binary)", "stl", &ExportSceneSTLBinary,
Exporter::ExportFormatEntry( "stlb", "Stereolithography (binary)", "stl" , &ExportSceneSTLBinary, aiProcess_Triangulate | aiProcess_GenNormals | aiProcess_PreTransformVertices));
aiProcess_Triangulate | aiProcess_GenNormals | aiProcess_PreTransformVertices
),
#endif #endif
#ifndef ASSIMP_BUILD_NO_PLY_EXPORTER #ifndef ASSIMP_BUILD_NO_PLY_EXPORTER
Exporter::ExportFormatEntry( "ply", "Stanford Polygon Library", "ply" , &ExportScenePly, exporters.push_back(Exporter::ExportFormatEntry("ply", "Stanford Polygon Library", "ply", &ExportScenePly,
aiProcess_PreTransformVertices aiProcess_PreTransformVertices));
), exporters.push_back(Exporter::ExportFormatEntry("plyb", "Stanford Polygon Library (binary)", "ply", &ExportScenePlyBinary,
Exporter::ExportFormatEntry( "plyb", "Stanford Polygon Library (binary)", "ply", &ExportScenePlyBinary, aiProcess_PreTransformVertices));
aiProcess_PreTransformVertices
),
#endif #endif
#ifndef ASSIMP_BUILD_NO_3DS_EXPORTER #ifndef ASSIMP_BUILD_NO_3DS_EXPORTER
Exporter::ExportFormatEntry( "3ds", "Autodesk 3DS (legacy)", "3ds" , &ExportScene3DS, exporters.push_back(Exporter::ExportFormatEntry("3ds", "Autodesk 3DS (legacy)", "3ds", &ExportScene3DS,
aiProcess_Triangulate | aiProcess_SortByPType | aiProcess_JoinIdenticalVertices ), aiProcess_Triangulate | aiProcess_SortByPType | aiProcess_JoinIdenticalVertices));
#endif #endif
#ifndef ASSIMP_BUILD_NO_GLTF_EXPORTER #ifndef ASSIMP_BUILD_NO_GLTF_EXPORTER
Exporter::ExportFormatEntry( "gltf2", "GL Transmission Format v. 2", "gltf", &ExportSceneGLTF2, exporters.push_back(Exporter::ExportFormatEntry("gltf2", "GL Transmission Format v. 2", "gltf", &ExportSceneGLTF2,
aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ), aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType));
Exporter::ExportFormatEntry( "glb2", "GL Transmission Format v. 2 (binary)", "glb", &ExportSceneGLB2, exporters.push_back(Exporter::ExportFormatEntry("glb2", "GL Transmission Format v. 2 (binary)", "glb", &ExportSceneGLB2,
aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ), aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType));
Exporter::ExportFormatEntry( "gltf", "GL Transmission Format", "gltf", &ExportSceneGLTF, exporters.push_back(Exporter::ExportFormatEntry("gltf", "GL Transmission Format", "gltf", &ExportSceneGLTF,
aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ), aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType));
Exporter::ExportFormatEntry( "glb", "GL Transmission Format (binary)", "glb", &ExportSceneGLB, exporters.push_back(Exporter::ExportFormatEntry("glb", "GL Transmission Format (binary)", "glb", &ExportSceneGLB,
aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ), aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType));
#endif #endif
#ifndef ASSIMP_BUILD_NO_ASSBIN_EXPORTER #ifndef ASSIMP_BUILD_NO_ASSBIN_EXPORTER
Exporter::ExportFormatEntry( "assbin", "Assimp Binary File", "assbin" , &ExportSceneAssbin, 0 ), exporters.push_back(Exporter::ExportFormatEntry("assbin", "Assimp Binary File", "assbin", &ExportSceneAssbin, 0));
#endif #endif
#ifndef ASSIMP_BUILD_NO_ASSXML_EXPORTER #ifndef ASSIMP_BUILD_NO_ASSXML_EXPORTER
Exporter::ExportFormatEntry( "assxml", "Assimp XML Document", "assxml" , &ExportSceneAssxml, 0 ), exporters.push_back(Exporter::ExportFormatEntry("assxml", "Assimp XML Document", "assxml", &ExportSceneAssxml, 0));
#endif #endif
#ifndef ASSIMP_BUILD_NO_X3D_EXPORTER #ifndef ASSIMP_BUILD_NO_X3D_EXPORTER
Exporter::ExportFormatEntry( "x3d", "Extensible 3D", "x3d" , &ExportSceneX3D, 0 ), exporters.push_back(Exporter::ExportFormatEntry("x3d", "Extensible 3D", "x3d", &ExportSceneX3D, 0));
#endif #endif
#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER #ifndef ASSIMP_BUILD_NO_FBX_EXPORTER
Exporter::ExportFormatEntry( "fbx", "Autodesk FBX (binary)", "fbx", &ExportSceneFBX, 0 ), exporters.push_back(Exporter::ExportFormatEntry("fbx", "Autodesk FBX (binary)", "fbx", &ExportSceneFBX, 0));
Exporter::ExportFormatEntry( "fbxa", "Autodesk FBX (ascii)", "fbx", &ExportSceneFBXA, 0 ), exporters.push_back(Exporter::ExportFormatEntry("fbxa", "Autodesk FBX (ascii)", "fbx", &ExportSceneFBXA, 0));
#endif
#ifndef ASSIMP_BUILD_NO_M3D_EXPORTER
exporters.push_back(Exporter::ExportFormatEntry("m3d", "Model 3D (binary)", "m3d", &ExportSceneM3D, 0));
exporters.push_back(Exporter::ExportFormatEntry("a3d", "Model 3D (ascii)", "m3d", &ExportSceneA3D, 0));
#endif #endif
#ifndef ASSIMP_BUILD_NO_3MF_EXPORTER #ifndef ASSIMP_BUILD_NO_3MF_EXPORTER
Exporter::ExportFormatEntry( "3mf", "The 3MF-File-Format", "3mf", &ExportScene3MF, 0 ), exporters.push_back(Exporter::ExportFormatEntry("3mf", "The 3MF-File-Format", "3mf", &ExportScene3MF, 0));
#endif #endif
#ifndef ASSIMP_BUILD_NO_ASSJSON_EXPORTER #ifndef ASSIMP_BUILD_NO_ASSJSON_EXPORTER
Exporter::ExportFormatEntry( "assjson", "Assimp JSON Document", "json", &ExportAssimp2Json, 0) exporters.push_back(Exporter::ExportFormatEntry("assjson", "Assimp JSON Document", "json", &ExportAssimp2Json, 0));
#endif #endif
}; }
#define ASSIMP_NUM_EXPORTERS (sizeof(gExporters)/sizeof(gExporters[0]))
class ExporterPimpl { class ExporterPimpl {
public: public:
@ -205,10 +203,7 @@ public:
GetPostProcessingStepInstanceList(mPostProcessingSteps); GetPostProcessingStepInstanceList(mPostProcessingSteps);
// grab all built-in exporters // grab all built-in exporters
if ( 0 != ( ASSIMP_NUM_EXPORTERS ) ) { setupExporterArray(mExporters);
mExporters.resize( ASSIMP_NUM_EXPORTERS );
std::copy( gExporters, gExporters + ASSIMP_NUM_EXPORTERS, mExporters.begin() );
}
} }
~ExporterPimpl() { ~ExporterPimpl() {
@ -252,23 +247,27 @@ Exporter :: Exporter()
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
Exporter::~Exporter() { Exporter::~Exporter() {
ai_assert(nullptr != pimpl);
FreeBlob(); FreeBlob();
delete pimpl; delete pimpl;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Exporter::SetIOHandler( IOSystem* pIOHandler) { void Exporter::SetIOHandler( IOSystem* pIOHandler) {
ai_assert(nullptr != pimpl);
pimpl->mIsDefaultIOHandler = !pIOHandler; pimpl->mIsDefaultIOHandler = !pIOHandler;
pimpl->mIOSystem.reset(pIOHandler); pimpl->mIOSystem.reset(pIOHandler);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
IOSystem* Exporter::GetIOHandler() const { IOSystem* Exporter::GetIOHandler() const {
ai_assert(nullptr != pimpl);
return pimpl->mIOSystem.get(); return pimpl->mIOSystem.get();
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
bool Exporter::IsDefaultIOHandler() const { bool Exporter::IsDefaultIOHandler() const {
ai_assert(nullptr != pimpl);
return pimpl->mIsDefaultIOHandler; return pimpl->mIsDefaultIOHandler;
} }
@ -295,6 +294,7 @@ void Exporter::SetProgressHandler(ProgressHandler* pHandler) {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
const aiExportDataBlob* Exporter::ExportToBlob( const aiScene* pScene, const char* pFormatId, const aiExportDataBlob* Exporter::ExportToBlob( const aiScene* pScene, const char* pFormatId,
unsigned int pPreprocessing, const ExportProperties* pProperties) { unsigned int pPreprocessing, const ExportProperties* pProperties) {
ai_assert(nullptr != pimpl);
if (pimpl->blob) { if (pimpl->blob) {
delete pimpl->blob; delete pimpl->blob;
pimpl->blob = nullptr; pimpl->blob = nullptr;
@ -319,7 +319,7 @@ const aiExportDataBlob* Exporter::ExportToBlob( const aiScene* pScene, const cha
aiReturn Exporter::Export( const aiScene* pScene, const char* pFormatId, const char* pPath, aiReturn Exporter::Export( const aiScene* pScene, const char* pFormatId, const char* pPath,
unsigned int pPreprocessing, const ExportProperties* pProperties) { unsigned int pPreprocessing, const ExportProperties* pProperties) {
ASSIMP_BEGIN_EXCEPTION_REGION(); ASSIMP_BEGIN_EXCEPTION_REGION();
ai_assert(nullptr != pimpl);
// when they create scenes from scratch, users will likely create them not in verbose // when they create scenes from scratch, users will likely create them not in verbose
// format. They will likely not be aware that there is a flag in the scene to indicate // format. They will likely not be aware that there is a flag in the scene to indicate
// this, however. To avoid surprises and bug reports, we check for duplicates in // this, however. To avoid surprises and bug reports, we check for duplicates in
@ -466,11 +466,13 @@ aiReturn Exporter::Export( const aiScene* pScene, const char* pFormatId, const c
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
const char* Exporter::GetErrorString() const { const char* Exporter::GetErrorString() const {
ai_assert(nullptr != pimpl);
return pimpl->mError.c_str(); return pimpl->mError.c_str();
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Exporter::FreeBlob() { void Exporter::FreeBlob() {
ai_assert(nullptr != pimpl);
delete pimpl->blob; delete pimpl->blob;
pimpl->blob = nullptr; pimpl->blob = nullptr;
@ -479,30 +481,34 @@ void Exporter::FreeBlob() {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
const aiExportDataBlob* Exporter::GetBlob() const { const aiExportDataBlob* Exporter::GetBlob() const {
ai_assert(nullptr != pimpl);
return pimpl->blob; return pimpl->blob;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
const aiExportDataBlob* Exporter::GetOrphanedBlob() const { const aiExportDataBlob* Exporter::GetOrphanedBlob() const {
const aiExportDataBlob* tmp = pimpl->blob; ai_assert(nullptr != pimpl);
const aiExportDataBlob *tmp = pimpl->blob;
pimpl->blob = nullptr; pimpl->blob = nullptr;
return tmp; return tmp;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
size_t Exporter::GetExportFormatCount() const { size_t Exporter::GetExportFormatCount() const {
ai_assert(nullptr != pimpl);
return pimpl->mExporters.size(); return pimpl->mExporters.size();
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
const aiExportFormatDesc* Exporter::GetExportFormatDescription( size_t index ) const { const aiExportFormatDesc* Exporter::GetExportFormatDescription( size_t index ) const {
ai_assert(nullptr != pimpl);
if (index >= GetExportFormatCount()) { if (index >= GetExportFormatCount()) {
return nullptr; return nullptr;
} }
// Return from static storage if the requested index is built-in. // Return from static storage if the requested index is built-in.
if (index < sizeof(gExporters) / sizeof(gExporters[0])) { if (index < pimpl->mExporters.size()) {
return &gExporters[index].mDescription; return &pimpl->mExporters[index].mDescription;
} }
return &pimpl->mExporters[index].mDescription; return &pimpl->mExporters[index].mDescription;
@ -510,7 +516,8 @@ const aiExportFormatDesc* Exporter::GetExportFormatDescription( size_t index ) c
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
aiReturn Exporter::RegisterExporter(const ExportFormatEntry& desc) { aiReturn Exporter::RegisterExporter(const ExportFormatEntry& desc) {
for(const ExportFormatEntry& e : pimpl->mExporters) { ai_assert(nullptr != pimpl);
for (const ExportFormatEntry &e : pimpl->mExporters) {
if (!strcmp(e.mDescription.id,desc.mDescription.id)) { if (!strcmp(e.mDescription.id,desc.mDescription.id)) {
return aiReturn_FAILURE; return aiReturn_FAILURE;
} }
@ -522,7 +529,8 @@ aiReturn Exporter::RegisterExporter(const ExportFormatEntry& desc) {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Exporter::UnregisterExporter(const char* id) { void Exporter::UnregisterExporter(const char* id) {
for(std::vector<ExportFormatEntry>::iterator it = pimpl->mExporters.begin(); ai_assert(nullptr != pimpl);
for (std::vector<ExportFormatEntry>::iterator it = pimpl->mExporters.begin();
it != pimpl->mExporters.end(); ++it) { it != pimpl->mExporters.end(); ++it) {
if (!strcmp((*it).mDescription.id,id)) { if (!strcmp((*it).mDescription.id,id)) {
pimpl->mExporters.erase(it); pimpl->mExporters.erase(it);

View File

@ -197,6 +197,9 @@ corresponding preprocessor flag to selectively disable formats.
#ifndef ASSIMP_BUILD_NO_MMD_IMPORTER #ifndef ASSIMP_BUILD_NO_MMD_IMPORTER
# include "MMD/MMDImporter.h" # include "MMD/MMDImporter.h"
#endif #endif
#ifndef ASSIMP_BUILD_NO_M3D_IMPORTER
# include "M3D/M3DImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_STEP_IMPORTER #ifndef ASSIMP_BUILD_NO_STEP_IMPORTER
# include "Importer/StepFile/StepFileImporter.h" # include "Importer/StepFile/StepFileImporter.h"
#endif #endif
@ -223,6 +226,9 @@ void GetImporterInstanceList(std::vector< BaseImporter* >& out)
#if (!defined ASSIMP_BUILD_NO_3DS_IMPORTER) #if (!defined ASSIMP_BUILD_NO_3DS_IMPORTER)
out.push_back( new Discreet3DSImporter()); out.push_back( new Discreet3DSImporter());
#endif #endif
#if (!defined ASSIMP_BUILD_NO_M3D_IMPORTER)
out.push_back( new M3DImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_MD3_IMPORTER) #if (!defined ASSIMP_BUILD_NO_MD3_IMPORTER)
out.push_back( new MD3Importer()); out.push_back( new MD3Importer());
#endif #endif

View File

@ -131,11 +131,15 @@ corresponding preprocessor flag to selectively disable steps.
#if (!defined ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS) #if (!defined ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS)
# include "PostProcessing/ScaleProcess.h" # include "PostProcessing/ScaleProcess.h"
#endif #endif
#if (!defined ASSIMP_BUILD_NO_ARMATUREPOPULATE_PROCESS)
# include "PostProcessing/ArmaturePopulate.h"
#endif
#if (!defined ASSIMP_BUILD_NO_GENBOUNDINGBOXES_PROCESS) #if (!defined ASSIMP_BUILD_NO_GENBOUNDINGBOXES_PROCESS)
# include "PostProcessing/GenBoundingBoxesProcess.h" # include "PostProcessing/GenBoundingBoxesProcess.h"
#endif #endif
namespace Assimp { namespace Assimp {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -180,6 +184,9 @@ void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out)
#if (!defined ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS) #if (!defined ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS)
out.push_back( new ScaleProcess()); out.push_back( new ScaleProcess());
#endif #endif
#if (!defined ASSIMP_BUILD_NO_ARMATUREPOPULATE_PROCESS)
out.push_back( new ArmaturePopulate());
#endif
#if (!defined ASSIMP_BUILD_NO_PRETRANSFORMVERTICES_PROCESS) #if (!defined ASSIMP_BUILD_NO_PRETRANSFORMVERTICES_PROCESS)
out.push_back( new PretransformVertices()); out.push_back( new PretransformVertices());
#endif #endif

View File

@ -1196,6 +1196,7 @@ void SceneCombiner::Copy( aiAnimation** _dest, const aiAnimation* src ) {
// and reallocate all arrays // and reallocate all arrays
CopyPtrArray( dest->mChannels, src->mChannels, dest->mNumChannels ); CopyPtrArray( dest->mChannels, src->mChannels, dest->mNumChannels );
CopyPtrArray( dest->mMorphMeshChannels, src->mMorphMeshChannels, dest->mNumMorphMeshChannels );
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -1215,6 +1216,26 @@ void SceneCombiner::Copy(aiNodeAnim** _dest, const aiNodeAnim* src) {
GetArrayCopy( dest->mRotationKeys, dest->mNumRotationKeys ); GetArrayCopy( dest->mRotationKeys, dest->mNumRotationKeys );
} }
void SceneCombiner::Copy(aiMeshMorphAnim** _dest, const aiMeshMorphAnim* src) {
if ( nullptr == _dest || nullptr == src ) {
return;
}
aiMeshMorphAnim* dest = *_dest = new aiMeshMorphAnim();
// get a flat copy
::memcpy(dest,src,sizeof(aiMeshMorphAnim));
// and reallocate all arrays
GetArrayCopy( dest->mKeys, dest->mNumKeys );
for (ai_uint i = 0; i < dest->mNumKeys;++i) {
dest->mKeys[i].mValues = new unsigned int[dest->mKeys[i].mNumValuesAndWeights];
dest->mKeys[i].mWeights = new double[dest->mKeys[i].mNumValuesAndWeights];
::memcpy(dest->mKeys[i].mValues, src->mKeys[i].mValues, dest->mKeys[i].mNumValuesAndWeights * sizeof(unsigned int));
::memcpy(dest->mKeys[i].mWeights, src->mKeys[i].mWeights, dest->mKeys[i].mNumValuesAndWeights * sizeof(double));
}
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void SceneCombiner::Copy( aiCamera** _dest,const aiCamera* src) { void SceneCombiner::Copy( aiCamera** _dest,const aiCamera* src) {
if ( nullptr == _dest || nullptr == src ) { if ( nullptr == _dest || nullptr == src ) {

View File

@ -46,8 +46,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/scene.h> #include <assimp/scene.h>
#include "ScenePrivate.h" #include "ScenePrivate.h"
static const unsigned int MajorVersion = 4; #include "revision.h"
static const unsigned int MinorVersion = 1;
// -------------------------------------------------------------------------------- // --------------------------------------------------------------------------------
// Legal information string - don't remove this. // Legal information string - don't remove this.
@ -56,9 +55,9 @@ static const char* LEGAL_INFORMATION =
"Open Asset Import Library (Assimp).\n" "Open Asset Import Library (Assimp).\n"
"A free C/C++ library to import various 3D file formats into applications\n\n" "A free C/C++ library to import various 3D file formats into applications\n\n"
"(c) 2008-2017, assimp team\n" "(c) 2006-2019, assimp team\n"
"License under the terms and conditions of the 3-clause BSD license\n" "License under the terms and conditions of the 3-clause BSD license\n"
"http://assimp.sourceforge.net\n" "http://assimp.org\n"
; ;
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -70,13 +69,13 @@ ASSIMP_API const char* aiGetLegalString () {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Get Assimp minor version // Get Assimp minor version
ASSIMP_API unsigned int aiGetVersionMinor () { ASSIMP_API unsigned int aiGetVersionMinor () {
return MinorVersion; return VER_MINOR;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Get Assimp major version // Get Assimp major version
ASSIMP_API unsigned int aiGetVersionMajor () { ASSIMP_API unsigned int aiGetVersionMajor () {
return MajorVersion; return VER_MAJOR;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -104,9 +103,6 @@ ASSIMP_API unsigned int aiGetCompileFlags () {
return flags; return flags;
} }
// include current build revision, which is even updated from time to time -- :-)
#include "revision.h"
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
ASSIMP_API unsigned int aiGetVersionRevision() { ASSIMP_API unsigned int aiGetVersionRevision() {
return GitVersion; return GitVersion;

View File

@ -44,23 +44,23 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
aiNode::aiNode() aiNode::aiNode()
: mName("") : mName("")
, mParent(NULL) , mParent(nullptr)
, mNumChildren(0) , mNumChildren(0)
, mChildren(NULL) , mChildren(nullptr)
, mNumMeshes(0) , mNumMeshes(0)
, mMeshes(NULL) , mMeshes(nullptr)
, mMetaData(NULL) { , mMetaData(nullptr) {
// empty // empty
} }
aiNode::aiNode(const std::string& name) aiNode::aiNode(const std::string& name)
: mName(name) : mName(name)
, mParent(NULL) , mParent(nullptr)
, mNumChildren(0) , mNumChildren(0)
, mChildren(NULL) , mChildren(nullptr)
, mNumMeshes(0) , mNumMeshes(0)
, mMeshes(NULL) , mMeshes(nullptr)
, mMetaData(NULL) { , mMetaData(nullptr) {
// empty // empty
} }
@ -68,7 +68,7 @@ aiNode::aiNode(const std::string& name)
aiNode::~aiNode() { aiNode::~aiNode() {
// delete all children recursively // delete all children recursively
// to make sure we won't crash if the data is invalid ... // to make sure we won't crash if the data is invalid ...
if (mChildren && mNumChildren) if (mNumChildren && mChildren)
{ {
for (unsigned int a = 0; a < mNumChildren; a++) for (unsigned int a = 0; a < mNumChildren; a++)
delete mChildren[a]; delete mChildren[a];

View File

@ -50,9 +50,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace Assimp { namespace Assimp {
namespace FBX namespace FBX
{ {
const std::string NULL_RECORD = { // 13 null bytes const std::string NULL_RECORD = { // 25 null bytes in 64-bit and 13 null bytes in 32-bit
'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0' '\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0',
}; // who knows why '\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0'
}; // who knows why, it looks like two integers 32/64 bit (compressed and uncompressed sizes?) + 1 byte (might be compression type?)
const std::string SEPARATOR = {'\x00', '\x01'}; // for use inside strings const std::string SEPARATOR = {'\x00', '\x01'}; // for use inside strings
const std::string MAGIC_NODE_TAG = "_$AssimpFbx$"; // from import const std::string MAGIC_NODE_TAG = "_$AssimpFbx$"; // from import
const int64_t SECOND = 46186158000; // FBX's kTime unit const int64_t SECOND = 46186158000; // FBX's kTime unit

View File

@ -47,6 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define INCLUDED_AI_FBX_COMPILECONFIG_H #define INCLUDED_AI_FBX_COMPILECONFIG_H
#include <map> #include <map>
#include <set>
// //
#if _MSC_VER > 1500 || (defined __GNUC___) #if _MSC_VER > 1500 || (defined __GNUC___)
@ -54,16 +55,23 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# else # else
# define fbx_unordered_map map # define fbx_unordered_map map
# define fbx_unordered_multimap multimap # define fbx_unordered_multimap multimap
# define fbx_unordered_set set
# define fbx_unordered_multiset multiset
#endif #endif
#ifdef ASSIMP_FBX_USE_UNORDERED_MULTIMAP #ifdef ASSIMP_FBX_USE_UNORDERED_MULTIMAP
# include <unordered_map> # include <unordered_map>
# include <unordered_set>
# if _MSC_VER > 1600 # if _MSC_VER > 1600
# define fbx_unordered_map unordered_map # define fbx_unordered_map unordered_map
# define fbx_unordered_multimap unordered_multimap # define fbx_unordered_multimap unordered_multimap
# define fbx_unordered_set unordered_set
# define fbx_unordered_multiset unordered_multiset
# else # else
# define fbx_unordered_map tr1::unordered_map # define fbx_unordered_map tr1::unordered_map
# define fbx_unordered_multimap tr1::unordered_multimap # define fbx_unordered_multimap tr1::unordered_multimap
# define fbx_unordered_set tr1::unordered_set
# define fbx_unordered_multiset tr1::unordered_multiset
# endif # endif
#endif #endif

View File

@ -68,7 +68,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <sstream> #include <sstream>
#include <iomanip> #include <iomanip>
#include <cstdint> #include <cstdint>
#include <iostream>
#include <stdlib.h>
namespace Assimp { namespace Assimp {
namespace FBX { namespace FBX {
@ -77,7 +78,7 @@ namespace Assimp {
#define MAGIC_NODE_TAG "_$AssimpFbx$" #define MAGIC_NODE_TAG "_$AssimpFbx$"
#define CONVERT_FBX_TIME(time) static_cast<double>(time) / 46186158000L #define CONVERT_FBX_TIME(time) static_cast<double>(time) / 46186158000LL
FBXConverter::FBXConverter(aiScene* out, const Document& doc, bool removeEmptyBones ) FBXConverter::FBXConverter(aiScene* out, const Document& doc, bool removeEmptyBones )
: defaultMaterialIndex() : defaultMaterialIndex()
@ -96,6 +97,14 @@ namespace Assimp {
// populate the node_anim_chain_bits map, which is needed // populate the node_anim_chain_bits map, which is needed
// to determine which nodes need to be generated. // to determine which nodes need to be generated.
ConvertAnimations(); ConvertAnimations();
// Embedded textures in FBX could be connected to nothing but to itself,
// for instance Texture -> Video connection only but not to the main graph,
// The idea here is to traverse all objects to find these Textures and convert them,
// so later during material conversion it will find converted texture in the textures_converted array.
if (doc.Settings().readTextures)
{
ConvertOrphantEmbeddedTextures();
}
ConvertRootNode(); ConvertRootNode();
if (doc.Settings().readAllMaterials) { if (doc.Settings().readAllMaterials) {
@ -145,7 +154,7 @@ namespace Assimp {
out->mRootNode->mName.Set(unique_name); out->mRootNode->mName.Set(unique_name);
// root has ID 0 // root has ID 0
ConvertNodes(0L, *out->mRootNode); ConvertNodes(0L, out->mRootNode, out->mRootNode);
} }
static std::string getAncestorBaseName(const aiNode* node) static std::string getAncestorBaseName(const aiNode* node)
@ -179,8 +188,11 @@ namespace Assimp {
GetUniqueName(original_name, unique_name); GetUniqueName(original_name, unique_name);
return unique_name; return unique_name;
} }
/// todo: pre-build node hierarchy
void FBXConverter::ConvertNodes(uint64_t id, aiNode& parent, const aiMatrix4x4& parent_transform) { /// todo: get bone from stack
/// todo: make map of aiBone* to aiNode*
/// then update convert clusters to the new format
void FBXConverter::ConvertNodes(uint64_t id, aiNode *parent, aiNode *root_node) {
const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(id, "Model"); const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(id, "Model");
std::vector<aiNode*> nodes; std::vector<aiNode*> nodes;
@ -191,62 +203,69 @@ namespace Assimp {
try { try {
for (const Connection* con : conns) { for (const Connection* con : conns) {
// ignore object-property links // ignore object-property links
if (con->PropertyName().length()) { if (con->PropertyName().length()) {
continue; // really important we document why this is ignored.
FBXImporter::LogInfo("ignoring property link - no docs on why this is ignored");
continue; //?
} }
// convert connection source object into Object base class
const Object* const object = con->SourceObject(); const Object* const object = con->SourceObject();
if (nullptr == object) { if (nullptr == object) {
FBXImporter::LogWarn("failed to convert source object for Model link"); FBXImporter::LogError("failed to convert source object for Model link");
continue; continue;
} }
// FBX Model::Cube, Model::Bone001, etc elements
// This detects if we can cast the object into this model structure.
const Model* const model = dynamic_cast<const Model*>(object); const Model* const model = dynamic_cast<const Model*>(object);
if (nullptr != model) { if (nullptr != model) {
nodes_chain.clear(); nodes_chain.clear();
post_nodes_chain.clear(); post_nodes_chain.clear();
aiMatrix4x4 new_abs_transform = parent_transform; aiMatrix4x4 new_abs_transform = parent->mTransformation;
std::string node_name = FixNodeName(model->Name());
std::string unique_name = MakeUniqueNodeName(model, parent);
// even though there is only a single input node, the design of // even though there is only a single input node, the design of
// assimp (or rather: the complicated transformation chain that // assimp (or rather: the complicated transformation chain that
// is employed by fbx) means that we may need multiple aiNode's // is employed by fbx) means that we may need multiple aiNode's
// to represent a fbx node's transformation. // to represent a fbx node's transformation.
const bool need_additional_node = GenerateTransformationNodeChain(*model, unique_name, nodes_chain, post_nodes_chain);
// generate node transforms - this includes pivot data
// if need_additional_node is true then you t
const bool need_additional_node = GenerateTransformationNodeChain(*model, node_name, nodes_chain, post_nodes_chain);
// assert that for the current node we must have at least a single transform
ai_assert(nodes_chain.size()); ai_assert(nodes_chain.size());
if (need_additional_node) { if (need_additional_node) {
nodes_chain.push_back(new aiNode(unique_name)); nodes_chain.push_back(new aiNode(node_name));
} }
//setup metadata on newest node //setup metadata on newest node
SetupNodeMetadata(*model, *nodes_chain.back()); SetupNodeMetadata(*model, *nodes_chain.back());
// link all nodes in a row // link all nodes in a row
aiNode* last_parent = &parent; aiNode* last_parent = parent;
for (aiNode* prenode : nodes_chain) { for (aiNode* child : nodes_chain) {
ai_assert(prenode); ai_assert(child);
if (last_parent != &parent) { if (last_parent != parent) {
last_parent->mNumChildren = 1; last_parent->mNumChildren = 1;
last_parent->mChildren = new aiNode*[1]; last_parent->mChildren = new aiNode*[1];
last_parent->mChildren[0] = prenode; last_parent->mChildren[0] = child;
} }
prenode->mParent = last_parent; child->mParent = last_parent;
last_parent = prenode; last_parent = child;
new_abs_transform *= prenode->mTransformation; new_abs_transform *= child->mTransformation;
} }
// attach geometry // attach geometry
ConvertModel(*model, *nodes_chain.back(), new_abs_transform); ConvertModel(*model, nodes_chain.back(), root_node, new_abs_transform);
// check if there will be any child nodes // check if there will be any child nodes
const std::vector<const Connection*>& child_conns const std::vector<const Connection*>& child_conns
@ -258,7 +277,7 @@ namespace Assimp {
for (aiNode* postnode : post_nodes_chain) { for (aiNode* postnode : post_nodes_chain) {
ai_assert(postnode); ai_assert(postnode);
if (last_parent != &parent) { if (last_parent != parent) {
last_parent->mNumChildren = 1; last_parent->mNumChildren = 1;
last_parent->mChildren = new aiNode*[1]; last_parent->mChildren = new aiNode*[1];
last_parent->mChildren[0] = postnode; last_parent->mChildren[0] = postnode;
@ -280,15 +299,15 @@ namespace Assimp {
); );
} }
// attach sub-nodes (if any) // recursion call - child nodes
ConvertNodes(model->ID(), *last_parent, new_abs_transform); ConvertNodes(model->ID(), last_parent, root_node);
if (doc.Settings().readLights) { if (doc.Settings().readLights) {
ConvertLights(*model, unique_name); ConvertLights(*model, node_name);
} }
if (doc.Settings().readCameras) { if (doc.Settings().readCameras) {
ConvertCameras(*model, unique_name); ConvertCameras(*model, node_name);
} }
nodes.push_back(nodes_chain.front()); nodes.push_back(nodes_chain.front());
@ -297,11 +316,17 @@ namespace Assimp {
} }
if (nodes.size()) { if (nodes.size()) {
parent.mChildren = new aiNode*[nodes.size()](); parent->mChildren = new aiNode*[nodes.size()]();
parent.mNumChildren = static_cast<unsigned int>(nodes.size()); parent->mNumChildren = static_cast<unsigned int>(nodes.size());
std::swap_ranges(nodes.begin(), nodes.end(), parent.mChildren); std::swap_ranges(nodes.begin(), nodes.end(), parent->mChildren);
} }
else
{
parent->mNumChildren = 0;
parent->mChildren = nullptr;
}
} }
catch (std::exception&) { catch (std::exception&) {
Util::delete_fun<aiNode> deleter; Util::delete_fun<aiNode> deleter;
@ -803,7 +828,7 @@ namespace Assimp {
// is_complex needs to be consistent with NeedsComplexTransformationChain() // is_complex needs to be consistent with NeedsComplexTransformationChain()
// or the interplay between this code and the animation converter would // or the interplay between this code and the animation converter would
// not be guaranteed. // not be guaranteed.
ai_assert(NeedsComplexTransformationChain(model) == ((chainBits & chainMaskComplex) != 0)); //ai_assert(NeedsComplexTransformationChain(model) == ((chainBits & chainMaskComplex) != 0));
// now, if we have more than just Translation, Scaling and Rotation, // now, if we have more than just Translation, Scaling and Rotation,
// we need to generate a full node chain to accommodate for assimp's // we need to generate a full node chain to accommodate for assimp's
@ -905,7 +930,8 @@ namespace Assimp {
} }
} }
void FBXConverter::ConvertModel(const Model& model, aiNode& nd, const aiMatrix4x4& node_global_transform) void FBXConverter::ConvertModel(const Model &model, aiNode *parent, aiNode *root_node,
const aiMatrix4x4 &absolute_transform)
{ {
const std::vector<const Geometry*>& geos = model.GetGeometry(); const std::vector<const Geometry*>& geos = model.GetGeometry();
@ -917,11 +943,12 @@ namespace Assimp {
const MeshGeometry* const mesh = dynamic_cast<const MeshGeometry*>(geo); const MeshGeometry* const mesh = dynamic_cast<const MeshGeometry*>(geo);
const LineGeometry* const line = dynamic_cast<const LineGeometry*>(geo); const LineGeometry* const line = dynamic_cast<const LineGeometry*>(geo);
if (mesh) { if (mesh) {
const std::vector<unsigned int>& indices = ConvertMesh(*mesh, model, node_global_transform, nd); const std::vector<unsigned int>& indices = ConvertMesh(*mesh, model, parent, root_node,
absolute_transform);
std::copy(indices.begin(), indices.end(), std::back_inserter(meshes)); std::copy(indices.begin(), indices.end(), std::back_inserter(meshes));
} }
else if (line) { else if (line) {
const std::vector<unsigned int>& indices = ConvertLine(*line, model, node_global_transform, nd); const std::vector<unsigned int>& indices = ConvertLine(*line, model, parent, root_node);
std::copy(indices.begin(), indices.end(), std::back_inserter(meshes)); std::copy(indices.begin(), indices.end(), std::back_inserter(meshes));
} }
else { else {
@ -930,15 +957,16 @@ namespace Assimp {
} }
if (meshes.size()) { if (meshes.size()) {
nd.mMeshes = new unsigned int[meshes.size()](); parent->mMeshes = new unsigned int[meshes.size()]();
nd.mNumMeshes = static_cast<unsigned int>(meshes.size()); parent->mNumMeshes = static_cast<unsigned int>(meshes.size());
std::swap_ranges(meshes.begin(), meshes.end(), nd.mMeshes); std::swap_ranges(meshes.begin(), meshes.end(), parent->mMeshes);
} }
} }
std::vector<unsigned int> FBXConverter::ConvertMesh(const MeshGeometry& mesh, const Model& model, std::vector<unsigned int>
const aiMatrix4x4& node_global_transform, aiNode& nd) FBXConverter::ConvertMesh(const MeshGeometry &mesh, const Model &model, aiNode *parent, aiNode *root_node,
const aiMatrix4x4 &absolute_transform)
{ {
std::vector<unsigned int> temp; std::vector<unsigned int> temp;
@ -962,18 +990,18 @@ namespace Assimp {
const MatIndexArray::value_type base = mindices[0]; const MatIndexArray::value_type base = mindices[0];
for (MatIndexArray::value_type index : mindices) { for (MatIndexArray::value_type index : mindices) {
if (index != base) { if (index != base) {
return ConvertMeshMultiMaterial(mesh, model, node_global_transform, nd); return ConvertMeshMultiMaterial(mesh, model, parent, root_node, absolute_transform);
} }
} }
} }
// faster code-path, just copy the data // faster code-path, just copy the data
temp.push_back(ConvertMeshSingleMaterial(mesh, model, node_global_transform, nd)); temp.push_back(ConvertMeshSingleMaterial(mesh, model, absolute_transform, parent, root_node));
return temp; return temp;
} }
std::vector<unsigned int> FBXConverter::ConvertLine(const LineGeometry& line, const Model& model, std::vector<unsigned int> FBXConverter::ConvertLine(const LineGeometry& line, const Model& model,
const aiMatrix4x4& node_global_transform, aiNode& nd) aiNode *parent, aiNode *root_node)
{ {
std::vector<unsigned int> temp; std::vector<unsigned int> temp;
@ -984,7 +1012,7 @@ namespace Assimp {
return temp; return temp;
} }
aiMesh* const out_mesh = SetupEmptyMesh(line, nd); aiMesh* const out_mesh = SetupEmptyMesh(line, root_node);
out_mesh->mPrimitiveTypes |= aiPrimitiveType_LINE; out_mesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
// copy vertices // copy vertices
@ -1019,7 +1047,7 @@ namespace Assimp {
return temp; return temp;
} }
aiMesh* FBXConverter::SetupEmptyMesh(const Geometry& mesh, aiNode& nd) aiMesh* FBXConverter::SetupEmptyMesh(const Geometry& mesh, aiNode *parent)
{ {
aiMesh* const out_mesh = new aiMesh(); aiMesh* const out_mesh = new aiMesh();
meshes.push_back(out_mesh); meshes.push_back(out_mesh);
@ -1036,17 +1064,18 @@ namespace Assimp {
} }
else else
{ {
out_mesh->mName = nd.mName; out_mesh->mName = parent->mName;
} }
return out_mesh; return out_mesh;
} }
unsigned int FBXConverter::ConvertMeshSingleMaterial(const MeshGeometry& mesh, const Model& model, unsigned int FBXConverter::ConvertMeshSingleMaterial(const MeshGeometry &mesh, const Model &model,
const aiMatrix4x4& node_global_transform, aiNode& nd) const aiMatrix4x4 &absolute_transform, aiNode *parent,
aiNode *root_node)
{ {
const MatIndexArray& mindices = mesh.GetMaterialIndices(); const MatIndexArray& mindices = mesh.GetMaterialIndices();
aiMesh* const out_mesh = SetupEmptyMesh(mesh, nd); aiMesh* const out_mesh = SetupEmptyMesh(mesh, parent);
const std::vector<aiVector3D>& vertices = mesh.GetVertices(); const std::vector<aiVector3D>& vertices = mesh.GetVertices();
const std::vector<unsigned int>& faces = mesh.GetFaceIndexCounts(); const std::vector<unsigned int>& faces = mesh.GetFaceIndexCounts();
@ -1113,7 +1142,7 @@ namespace Assimp {
binormals = &tempBinormals; binormals = &tempBinormals;
} }
else { else {
binormals = NULL; binormals = nullptr;
} }
} }
@ -1163,8 +1192,9 @@ namespace Assimp {
ConvertMaterialForMesh(out_mesh, model, mesh, mindices[0]); ConvertMaterialForMesh(out_mesh, model, mesh, mindices[0]);
} }
if (doc.Settings().readWeights && mesh.DeformerSkin() != NULL) { if (doc.Settings().readWeights && mesh.DeformerSkin() != nullptr) {
ConvertWeights(out_mesh, model, mesh, node_global_transform, NO_MATERIAL_SEPARATION); ConvertWeights(out_mesh, model, mesh, absolute_transform, parent, root_node, NO_MATERIAL_SEPARATION,
nullptr);
} }
std::vector<aiAnimMesh*> animMeshes; std::vector<aiAnimMesh*> animMeshes;
@ -1209,8 +1239,10 @@ namespace Assimp {
return static_cast<unsigned int>(meshes.size() - 1); return static_cast<unsigned int>(meshes.size() - 1);
} }
std::vector<unsigned int> FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model, std::vector<unsigned int>
const aiMatrix4x4& node_global_transform, aiNode& nd) FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, aiNode *parent,
aiNode *root_node,
const aiMatrix4x4 &absolute_transform)
{ {
const MatIndexArray& mindices = mesh.GetMaterialIndices(); const MatIndexArray& mindices = mesh.GetMaterialIndices();
ai_assert(mindices.size()); ai_assert(mindices.size());
@ -1221,7 +1253,7 @@ namespace Assimp {
for (MatIndexArray::value_type index : mindices) { for (MatIndexArray::value_type index : mindices) {
if (had.find(index) == had.end()) { if (had.find(index) == had.end()) {
indices.push_back(ConvertMeshMultiMaterial(mesh, model, index, node_global_transform, nd)); indices.push_back(ConvertMeshMultiMaterial(mesh, model, index, parent, root_node, absolute_transform));
had.insert(index); had.insert(index);
} }
} }
@ -1229,18 +1261,18 @@ namespace Assimp {
return indices; return indices;
} }
unsigned int FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model, unsigned int FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model,
MatIndexArray::value_type index, MatIndexArray::value_type index,
const aiMatrix4x4& node_global_transform, aiNode *parent, aiNode *root_node,
aiNode& nd) const aiMatrix4x4 &absolute_transform)
{ {
aiMesh* const out_mesh = SetupEmptyMesh(mesh, nd); aiMesh* const out_mesh = SetupEmptyMesh(mesh, parent);
const MatIndexArray& mindices = mesh.GetMaterialIndices(); const MatIndexArray& mindices = mesh.GetMaterialIndices();
const std::vector<aiVector3D>& vertices = mesh.GetVertices(); const std::vector<aiVector3D>& vertices = mesh.GetVertices();
const std::vector<unsigned int>& faces = mesh.GetFaceIndexCounts(); const std::vector<unsigned int>& faces = mesh.GetFaceIndexCounts();
const bool process_weights = doc.Settings().readWeights && mesh.DeformerSkin() != NULL; const bool process_weights = doc.Settings().readWeights && mesh.DeformerSkin() != nullptr;
unsigned int count_faces = 0; unsigned int count_faces = 0;
unsigned int count_vertices = 0; unsigned int count_vertices = 0;
@ -1300,7 +1332,7 @@ namespace Assimp {
binormals = &tempBinormals; binormals = &tempBinormals;
} }
else { else {
binormals = NULL; binormals = nullptr;
} }
} }
@ -1399,7 +1431,7 @@ namespace Assimp {
ConvertMaterialForMesh(out_mesh, model, mesh, index); ConvertMaterialForMesh(out_mesh, model, mesh, index);
if (process_weights) { if (process_weights) {
ConvertWeights(out_mesh, model, mesh, node_global_transform, index, &reverseMapping); ConvertWeights(out_mesh, model, mesh, absolute_transform, parent, root_node, index, &reverseMapping);
} }
std::vector<aiAnimMesh*> animMeshes; std::vector<aiAnimMesh*> animMeshes;
@ -1449,10 +1481,10 @@ namespace Assimp {
return static_cast<unsigned int>(meshes.size() - 1); return static_cast<unsigned int>(meshes.size() - 1);
} }
void FBXConverter::ConvertWeights(aiMesh* out, const Model& model, const MeshGeometry& geo, void FBXConverter::ConvertWeights(aiMesh *out, const Model &model, const MeshGeometry &geo,
const aiMatrix4x4& node_global_transform, const aiMatrix4x4 &absolute_transform,
unsigned int materialIndex, aiNode *parent, aiNode *root_node, unsigned int materialIndex,
std::vector<unsigned int>* outputVertStartIndices) std::vector<unsigned int> *outputVertStartIndices)
{ {
ai_assert(geo.DeformerSkin()); ai_assert(geo.DeformerSkin());
@ -1463,13 +1495,12 @@ namespace Assimp {
const Skin& sk = *geo.DeformerSkin(); const Skin& sk = *geo.DeformerSkin();
std::vector<aiBone*> bones; std::vector<aiBone*> bones;
bones.reserve(sk.Clusters().size());
const bool no_mat_check = materialIndex == NO_MATERIAL_SEPARATION; const bool no_mat_check = materialIndex == NO_MATERIAL_SEPARATION;
ai_assert(no_mat_check || outputVertStartIndices); ai_assert(no_mat_check || outputVertStartIndices);
try { try {
// iterate over the sub deformers
for (const Cluster* cluster : sk.Clusters()) { for (const Cluster* cluster : sk.Clusters()) {
ai_assert(cluster); ai_assert(cluster);
@ -1483,15 +1514,16 @@ namespace Assimp {
index_out_indices.clear(); index_out_indices.clear();
out_indices.clear(); out_indices.clear();
// now check if *any* of these weights is contained in the output mesh, // now check if *any* of these weights is contained in the output mesh,
// taking notes so we don't need to do it twice. // taking notes so we don't need to do it twice.
for (WeightIndexArray::value_type index : indices) { for (WeightIndexArray::value_type index : indices) {
unsigned int count = 0; unsigned int count = 0;
const unsigned int* const out_idx = geo.ToOutputVertexIndex(index, count); const unsigned int* const out_idx = geo.ToOutputVertexIndex(index, count);
// ToOutputVertexIndex only returns NULL if index is out of bounds // ToOutputVertexIndex only returns nullptr if index is out of bounds
// which should never happen // which should never happen
ai_assert(out_idx != NULL); ai_assert(out_idx != nullptr);
index_out_indices.push_back(no_index_sentinel); index_out_indices.push_back(no_index_sentinel);
count_out_indices.push_back(0); count_out_indices.push_back(0);
@ -1524,47 +1556,75 @@ namespace Assimp {
// if we found at least one, generate the output bones // if we found at least one, generate the output bones
// XXX this could be heavily simplified by collecting the bone // XXX this could be heavily simplified by collecting the bone
// data in a single step. // data in a single step.
ConvertCluster(bones, model, *cluster, out_indices, index_out_indices, ConvertCluster(bones, cluster, out_indices, index_out_indices,
count_out_indices, node_global_transform); count_out_indices, absolute_transform, parent, root_node);
} }
bone_map.clear();
} }
catch (std::exception&) { catch (std::exception&e) {
std::for_each(bones.begin(), bones.end(), Util::delete_fun<aiBone>()); std::for_each(bones.begin(), bones.end(), Util::delete_fun<aiBone>());
throw; throw;
} }
if (bones.empty()) { if (bones.empty()) {
out->mBones = nullptr;
out->mNumBones = 0;
return; return;
} } else {
out->mBones = new aiBone *[bones.size()]();
out->mBones = new aiBone*[bones.size()]();
out->mNumBones = static_cast<unsigned int>(bones.size()); out->mNumBones = static_cast<unsigned int>(bones.size());
std::swap_ranges(bones.begin(), bones.end(), out->mBones); std::swap_ranges(bones.begin(), bones.end(), out->mBones);
} }
}
void FBXConverter::ConvertCluster(std::vector<aiBone*>& bones, const Model& /*model*/, const Cluster& cl, const aiNode* FBXConverter::GetNodeByName( const aiString& name, aiNode *current_node )
std::vector<size_t>& out_indices,
std::vector<size_t>& index_out_indices,
std::vector<size_t>& count_out_indices,
const aiMatrix4x4& node_global_transform)
{ {
aiNode * iter = current_node;
//printf("Child count: %d", iter->mNumChildren);
return iter;
}
aiBone* const bone = new aiBone(); void FBXConverter::ConvertCluster(std::vector<aiBone *> &local_mesh_bones, const Cluster *cl,
bones.push_back(bone); std::vector<size_t> &out_indices, std::vector<size_t> &index_out_indices,
std::vector<size_t> &count_out_indices, const aiMatrix4x4 &absolute_transform,
aiNode *parent, aiNode *root_node) {
ai_assert(cl); // make sure cluster valid
std::string deformer_name = cl->TargetNode()->Name();
aiString bone_name = aiString(FixNodeName(deformer_name));
bone->mName = FixNodeName(cl.TargetNode()->Name()); aiBone *bone = nullptr;
bone->mOffsetMatrix = cl.TransformLink(); if (bone_map.count(deformer_name)) {
std::cout << "retrieved bone from lookup " << bone_name.C_Str() << ". Deformer: " << deformer_name
<< std::endl;
bone = bone_map[deformer_name];
} else {
std::cout << "created new bone " << bone_name.C_Str() << ". Deformer: " << deformer_name << std::endl;
bone = new aiBone();
bone->mName = bone_name;
// store local transform link for post processing
bone->mOffsetMatrix = cl->TransformLink();
bone->mOffsetMatrix.Inverse(); bone->mOffsetMatrix.Inverse();
bone->mOffsetMatrix = bone->mOffsetMatrix * node_global_transform; aiMatrix4x4 matrix = (aiMatrix4x4)absolute_transform;
bone->mOffsetMatrix = bone->mOffsetMatrix * matrix; // * mesh_offset
//
// Now calculate the aiVertexWeights
//
aiVertexWeight *cursor = nullptr;
bone->mNumWeights = static_cast<unsigned int>(out_indices.size()); bone->mNumWeights = static_cast<unsigned int>(out_indices.size());
aiVertexWeight* cursor = bone->mWeights = new aiVertexWeight[out_indices.size()]; cursor = bone->mWeights = new aiVertexWeight[out_indices.size()];
const size_t no_index_sentinel = std::numeric_limits<size_t>::max(); const size_t no_index_sentinel = std::numeric_limits<size_t>::max();
const WeightArray& weights = cl.GetWeights(); const WeightArray& weights = cl->GetWeights();
const size_t c = index_out_indices.size(); const size_t c = index_out_indices.size();
for (size_t i = 0; i < c; ++i) { for (size_t i = 0; i < c; ++i) {
@ -1576,12 +1636,23 @@ namespace Assimp {
const size_t cc = count_out_indices[i]; const size_t cc = count_out_indices[i];
for (size_t j = 0; j < cc; ++j) { for (size_t j = 0; j < cc; ++j) {
// cursor runs from first element relative to the start
// or relative to the start of the next indexes.
aiVertexWeight& out_weight = *cursor++; aiVertexWeight& out_weight = *cursor++;
out_weight.mVertexId = static_cast<unsigned int>(out_indices[index_index + j]); out_weight.mVertexId = static_cast<unsigned int>(out_indices[index_index + j]);
out_weight.mWeight = weights[i]; out_weight.mWeight = weights[i];
} }
} }
bone_map.insert(std::pair<const std::string, aiBone *>(deformer_name, bone));
}
std::cout << "bone research: Indicies size: " << out_indices.size() << std::endl;
// lookup must be populated in case something goes wrong
// this also allocates bones to mesh instance outside
local_mesh_bones.push_back(bone);
} }
void FBXConverter::ConvertMaterialForMesh(aiMesh* out, const Model& model, const MeshGeometry& geo, void FBXConverter::ConvertMaterialForMesh(aiMesh* out, const Model& model, const MeshGeometry& geo,
@ -1711,7 +1782,7 @@ namespace Assimp {
bool textureReady = false; //tells if our texture is ready (if it was loaded or if it was found) bool textureReady = false; //tells if our texture is ready (if it was loaded or if it was found)
unsigned int index; unsigned int index;
VideoMap::const_iterator it = textures_converted.find(media); VideoMap::const_iterator it = textures_converted.find(*media);
if (it != textures_converted.end()) { if (it != textures_converted.end()) {
index = (*it).second; index = (*it).second;
textureReady = true; textureReady = true;
@ -1719,7 +1790,7 @@ namespace Assimp {
else { else {
if (media->ContentLength() > 0) { if (media->ContentLength() > 0) {
index = ConvertVideo(*media); index = ConvertVideo(*media);
textures_converted[media] = index; textures_converted[*media] = index;
textureReady = true; textureReady = true;
} }
} }
@ -2243,13 +2314,13 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa
if (media != nullptr && media->ContentLength() > 0) { if (media != nullptr && media->ContentLength() > 0) {
unsigned int index; unsigned int index;
VideoMap::const_iterator it = textures_converted.find(media); VideoMap::const_iterator it = textures_converted.find(*media);
if (it != textures_converted.end()) { if (it != textures_converted.end()) {
index = (*it).second; index = (*it).second;
} }
else { else {
index = ConvertVideo(*media); index = ConvertVideo(*media);
textures_converted[media] = index; textures_converted[*media] = index;
} }
// setup texture reference string (copied from ColladaLoader::FindFilenameForEffectTexture) // setup texture reference string (copied from ColladaLoader::FindFilenameForEffectTexture)
@ -2677,7 +2748,7 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa
// sanity check whether the input is ok // sanity check whether the input is ok
static void validateAnimCurveNodes(const std::vector<const AnimationCurveNode*>& curves, static void validateAnimCurveNodes(const std::vector<const AnimationCurveNode*>& curves,
bool strictMode) { bool strictMode) {
const Object* target(NULL); const Object* target(nullptr);
for (const AnimationCurveNode* node : curves) { for (const AnimationCurveNode* node : curves) {
if (!target) { if (!target) {
target = node->Target(); target = node->Target();
@ -2708,7 +2779,7 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa
#ifdef ASSIMP_BUILD_DEBUG #ifdef ASSIMP_BUILD_DEBUG
validateAnimCurveNodes(curves, doc.Settings().strictMode); validateAnimCurveNodes(curves, doc.Settings().strictMode);
#endif #endif
const AnimationCurveNode* curve_node = NULL; const AnimationCurveNode* curve_node = nullptr;
for (const AnimationCurveNode* node : curves) { for (const AnimationCurveNode* node : curves) {
ai_assert(node); ai_assert(node);
@ -3556,7 +3627,7 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa
ai_assert(!out->mMeshes); ai_assert(!out->mMeshes);
ai_assert(!out->mNumMeshes); ai_assert(!out->mNumMeshes);
// note: the trailing () ensures initialization with NULL - not // note: the trailing () ensures initialization with nullptr - not
// many C++ users seem to know this, so pointing it out to avoid // many C++ users seem to know this, so pointing it out to avoid
// confusion why this code works. // confusion why this code works.
@ -3603,6 +3674,47 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa
} }
} }
void FBXConverter::ConvertOrphantEmbeddedTextures()
{
// in C++14 it could be:
// for (auto&& [id, object] : objects)
for (auto&& id_and_object : doc.Objects())
{
auto&& id = std::get<0>(id_and_object);
auto&& object = std::get<1>(id_and_object);
// If an object doesn't have parent
if (doc.ConnectionsBySource().count(id) == 0)
{
const Texture* realTexture = nullptr;
try
{
const auto& element = object->GetElement();
const Token& key = element.KeyToken();
const char* obtype = key.begin();
const size_t length = static_cast<size_t>(key.end() - key.begin());
if (strncmp(obtype, "Texture", length) == 0)
{
const Texture* texture = static_cast<const Texture*>(object->Get());
if (texture->Media() && texture->Media()->ContentLength() > 0)
{
realTexture = texture;
}
}
}
catch (...)
{
// do nothing
}
if (realTexture)
{
const Video* media = realTexture->Media();
unsigned int index = ConvertVideo(*media);
textures_converted[*media] = index;
}
}
}
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void ConvertToAssimpScene(aiScene* out, const Document& doc, bool removeEmptyBones) void ConvertToAssimpScene(aiScene* out, const Document& doc, bool removeEmptyBones)
{ {

View File

@ -123,7 +123,7 @@ private:
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// collect and assign child nodes // collect and assign child nodes
void ConvertNodes(uint64_t id, aiNode& parent, const aiMatrix4x4& parent_transform = aiMatrix4x4()); void ConvertNodes(uint64_t id, aiNode *parent, aiNode *root_node);
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void ConvertLights(const Model& model, const std::string &orig_name ); void ConvertLights(const Model& model, const std::string &orig_name );
@ -179,32 +179,35 @@ private:
void SetupNodeMetadata(const Model& model, aiNode& nd); void SetupNodeMetadata(const Model& model, aiNode& nd);
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void ConvertModel(const Model& model, aiNode& nd, const aiMatrix4x4& node_global_transform); void ConvertModel(const Model &model, aiNode *parent, aiNode *root_node,
const aiMatrix4x4 &absolute_transform);
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// MeshGeometry -> aiMesh, return mesh index + 1 or 0 if the conversion failed // MeshGeometry -> aiMesh, return mesh index + 1 or 0 if the conversion failed
std::vector<unsigned int> ConvertMesh(const MeshGeometry& mesh, const Model& model, std::vector<unsigned int>
const aiMatrix4x4& node_global_transform, aiNode& nd); ConvertMesh(const MeshGeometry &mesh, const Model &model, aiNode *parent, aiNode *root_node,
const aiMatrix4x4 &absolute_transform);
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
std::vector<unsigned int> ConvertLine(const LineGeometry& line, const Model& model, std::vector<unsigned int> ConvertLine(const LineGeometry& line, const Model& model,
const aiMatrix4x4& node_global_transform, aiNode& nd); aiNode *parent, aiNode *root_node);
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
aiMesh* SetupEmptyMesh(const Geometry& mesh, aiNode& nd); aiMesh* SetupEmptyMesh(const Geometry& mesh, aiNode *parent);
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
unsigned int ConvertMeshSingleMaterial(const MeshGeometry& mesh, const Model& model, unsigned int ConvertMeshSingleMaterial(const MeshGeometry &mesh, const Model &model,
const aiMatrix4x4& node_global_transform, aiNode& nd); const aiMatrix4x4 &absolute_transform, aiNode *parent,
aiNode *root_node);
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
std::vector<unsigned int> ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model, std::vector<unsigned int>
const aiMatrix4x4& node_global_transform, aiNode& nd); ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, aiNode *parent, aiNode *root_node,
const aiMatrix4x4 &absolute_transform);
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
unsigned int ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model, unsigned int ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, MatIndexArray::value_type index,
MatIndexArray::value_type index, aiNode *parent, aiNode *root_node, const aiMatrix4x4 &absolute_transform);
const aiMatrix4x4& node_global_transform, aiNode& nd);
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
static const unsigned int NO_MATERIAL_SEPARATION = /* std::numeric_limits<unsigned int>::max() */ static const unsigned int NO_MATERIAL_SEPARATION = /* std::numeric_limits<unsigned int>::max() */
@ -217,17 +220,17 @@ private:
* - outputVertStartIndices is only used when a material index is specified, it gives for * - outputVertStartIndices is only used when a material index is specified, it gives for
* each output vertex the DOM index it maps to. * each output vertex the DOM index it maps to.
*/ */
void ConvertWeights(aiMesh* out, const Model& model, const MeshGeometry& geo, void ConvertWeights(aiMesh *out, const Model &model, const MeshGeometry &geo, const aiMatrix4x4 &absolute_transform,
const aiMatrix4x4& node_global_transform = aiMatrix4x4(), aiNode *parent = NULL, aiNode *root_node = NULL,
unsigned int materialIndex = NO_MATERIAL_SEPARATION, unsigned int materialIndex = NO_MATERIAL_SEPARATION,
std::vector<unsigned int>* outputVertStartIndices = NULL); std::vector<unsigned int> *outputVertStartIndices = NULL);
// lookup
static const aiNode* GetNodeByName( const aiString& name, aiNode *current_node );
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void ConvertCluster(std::vector<aiBone*>& bones, const Model& /*model*/, const Cluster& cl, void ConvertCluster(std::vector<aiBone *> &local_mesh_bones, const Cluster *cl,
std::vector<size_t>& out_indices, std::vector<size_t> &out_indices, std::vector<size_t> &index_out_indices,
std::vector<size_t>& index_out_indices, std::vector<size_t> &count_out_indices, const aiMatrix4x4 &absolute_transform,
std::vector<size_t>& count_out_indices, aiNode *parent, aiNode *root_node);
const aiMatrix4x4& node_global_transform);
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void ConvertMaterialForMesh(aiMesh* out, const Model& model, const MeshGeometry& geo, void ConvertMaterialForMesh(aiMesh* out, const Model& model, const MeshGeometry& geo,
@ -424,6 +427,10 @@ private:
// copy generated meshes, animations, lights, cameras and textures to the output scene // copy generated meshes, animations, lights, cameras and textures to the output scene
void TransferDataToScene(); void TransferDataToScene();
// ------------------------------------------------------------------------------------------------
// FBX file could have embedded textures not connected to anything
void ConvertOrphantEmbeddedTextures();
private: private:
// 0: not assigned yet, others: index is value - 1 // 0: not assigned yet, others: index is value - 1
unsigned int defaultMaterialIndex; unsigned int defaultMaterialIndex;
@ -435,27 +442,47 @@ private:
std::vector<aiCamera*> cameras; std::vector<aiCamera*> cameras;
std::vector<aiTexture*> textures; std::vector<aiTexture*> textures;
using MaterialMap = std::map<const Material*, unsigned int>; using MaterialMap = std::fbx_unordered_map<const Material*, unsigned int>;
MaterialMap materials_converted; MaterialMap materials_converted;
using VideoMap = std::map<const Video*, unsigned int>; using VideoMap = std::fbx_unordered_map<const Video, unsigned int>;
VideoMap textures_converted; VideoMap textures_converted;
using MeshMap = std::map<const Geometry*, std::vector<unsigned int> >; using MeshMap = std::fbx_unordered_map<const Geometry*, std::vector<unsigned int> >;
MeshMap meshes_converted; MeshMap meshes_converted;
// fixed node name -> which trafo chain components have animations? // fixed node name -> which trafo chain components have animations?
using NodeAnimBitMap = std::map<std::string, unsigned int> ; using NodeAnimBitMap = std::fbx_unordered_map<std::string, unsigned int> ;
NodeAnimBitMap node_anim_chain_bits; NodeAnimBitMap node_anim_chain_bits;
// number of nodes with the same name // number of nodes with the same name
using NodeNameCache = std::unordered_map<std::string, unsigned int>; using NodeNameCache = std::fbx_unordered_map<std::string, unsigned int>;
NodeNameCache mNodeNames; NodeNameCache mNodeNames;
// Deformer name is not the same as a bone name - it does contain the bone name though :)
// Deformer names in FBX are always unique in an FBX file.
std::map<const std::string, aiBone *> bone_map;
double anim_fps; double anim_fps;
aiScene* const out; aiScene* const out;
const FBX::Document& doc; const FBX::Document& doc;
static void BuildBoneList(aiNode *current_node, const aiNode *root_node, const aiScene *scene,
std::vector<aiBone*>& bones);
void BuildBoneStack(aiNode *current_node, const aiNode *root_node, const aiScene *scene,
const std::vector<aiBone *> &bones,
std::map<aiBone *, aiNode *> &bone_stack,
std::vector<aiNode*> &node_stack );
static void BuildNodeList(aiNode *current_node, std::vector<aiNode *> &nodes);
static aiNode *GetNodeFromStack(const aiString &node_name, std::vector<aiNode *> &nodes);
static aiNode *GetArmatureRoot(aiNode *bone_node, std::vector<aiBone*> &bone_list);
static bool IsBoneNode(const aiString &bone_name, std::vector<aiBone *> &bones);
}; };
} }

View File

@ -637,6 +637,20 @@ public:
return ptr; return ptr;
} }
bool operator==(const Video& other) const
{
return (
type == other.type
&& relativeFileName == other.relativeFileName
&& fileName == other.fileName
);
}
bool operator<(const Video& other) const
{
return std::tie(type, relativeFileName, fileName) < std::tie(other.type, other.relativeFileName, other.fileName);
}
private: private:
std::string type; std::string type;
std::string relativeFileName; std::string relativeFileName;
@ -1005,10 +1019,10 @@ public:
// during their entire lifetime (Document). FBX files have // during their entire lifetime (Document). FBX files have
// up to many thousands of objects (most of which we never use), // up to many thousands of objects (most of which we never use),
// so the memory overhead for them should be kept at a minimum. // so the memory overhead for them should be kept at a minimum.
typedef std::map<uint64_t, LazyObject*> ObjectMap; typedef std::fbx_unordered_map<uint64_t, LazyObject*> ObjectMap;
typedef std::fbx_unordered_map<std::string, std::shared_ptr<const PropertyTable> > PropertyTemplateMap; typedef std::fbx_unordered_map<std::string, std::shared_ptr<const PropertyTable> > PropertyTemplateMap;
typedef std::multimap<uint64_t, const Connection*> ConnectionMap; typedef std::fbx_unordered_multimap<uint64_t, const Connection*> ConnectionMap;
/** DOM class for global document settings, a single instance per document can /** DOM class for global document settings, a single instance per document can
* be accessed via Document.Globals(). */ * be accessed via Document.Globals(). */
@ -1177,4 +1191,25 @@ private:
} // Namespace FBX } // Namespace FBX
} // Namespace Assimp } // Namespace Assimp
namespace std
{
template <>
struct hash<const Assimp::FBX::Video>
{
std::size_t operator()(const Assimp::FBX::Video& video) const
{
using std::size_t;
using std::hash;
using std::string;
size_t res = 17;
res = res * 31 + hash<string>()(video.Name());
res = res * 31 + hash<string>()(video.RelativeFilename());
res = res * 31 + hash<string>()(video.Type());
return res;
}
};
}
#endif // INCLUDED_AI_FBX_DOCUMENT_H #endif // INCLUDED_AI_FBX_DOCUMENT_H

View File

@ -325,9 +325,9 @@ void FBX::Node::BeginBinary(Assimp::StreamWriterLE &s)
this->start_pos = s.Tell(); this->start_pos = s.Tell();
// placeholders for end pos and property section info // placeholders for end pos and property section info
s.PutU4(0); // end pos s.PutU8(0); // end pos
s.PutU4(0); // number of properties s.PutU8(0); // number of properties
s.PutU4(0); // total property section length s.PutU8(0); // total property section length
// node name // node name
s.PutU1(uint8_t(name.size())); // length of node name s.PutU1(uint8_t(name.size())); // length of node name
@ -352,9 +352,9 @@ void FBX::Node::EndPropertiesBinary(
size_t pos = s.Tell(); size_t pos = s.Tell();
ai_assert(pos > property_start); ai_assert(pos > property_start);
size_t property_section_size = pos - property_start; size_t property_section_size = pos - property_start;
s.Seek(start_pos + 4); s.Seek(start_pos + 8); // 8 bytes of uint64_t of end_pos
s.PutU4(uint32_t(num_properties)); s.PutU8(num_properties);
s.PutU4(uint32_t(property_section_size)); s.PutU8(property_section_size);
s.Seek(pos); s.Seek(pos);
} }
@ -375,7 +375,7 @@ void FBX::Node::EndBinary(
// now go back and write initial pos // now go back and write initial pos
this->end_pos = s.Tell(); this->end_pos = s.Tell();
s.Seek(start_pos); s.Seek(start_pos);
s.PutU4(uint32_t(end_pos)); s.PutU8(end_pos);
s.Seek(end_pos); s.Seek(end_pos);
} }

View File

@ -81,8 +81,8 @@ using namespace Assimp::FBX;
// some constants that we'll use for writing metadata // some constants that we'll use for writing metadata
namespace Assimp { namespace Assimp {
namespace FBX { namespace FBX {
const std::string EXPORT_VERSION_STR = "7.4.0"; const std::string EXPORT_VERSION_STR = "7.5.0";
const uint32_t EXPORT_VERSION_INT = 7400; // 7.4 == 2014/2015 const uint32_t EXPORT_VERSION_INT = 7500; // 7.5 == 2016+
// FBX files have some hashed values that depend on the creation time field, // FBX files have some hashed values that depend on the creation time field,
// but for now we don't actually know how to generate these. // but for now we don't actually know how to generate these.
// what we can do is set them to a known-working version. // what we can do is set them to a known-working version.
@ -1289,7 +1289,7 @@ void FBXExporter::WriteObjects ()
for(unsigned int lr = 1; lr < m->GetNumUVChannels(); ++ lr) for(unsigned int lr = 1; lr < m->GetNumUVChannels(); ++ lr)
{ {
FBX::Node layerExtra("Layer", int32_t(1)); FBX::Node layerExtra("Layer", int32_t(lr));
layerExtra.AddChild("Version", int32_t(100)); layerExtra.AddChild("Version", int32_t(100));
FBX::Node leExtra("LayerElement"); FBX::Node leExtra("LayerElement");
leExtra.AddChild("Type", "LayerElementUV"); leExtra.AddChild("Type", "LayerElementUV");

View File

@ -48,26 +48,26 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "FBXImporter.h" #include "FBXImporter.h"
#include "FBXTokenizer.h"
#include "FBXParser.h"
#include "FBXUtil.h"
#include "FBXDocument.h"
#include "FBXConverter.h" #include "FBXConverter.h"
#include "FBXDocument.h"
#include "FBXParser.h"
#include "FBXTokenizer.h"
#include "FBXUtil.h"
#include <assimp/StreamReader.h>
#include <assimp/MemoryIOWrapper.h> #include <assimp/MemoryIOWrapper.h>
#include <assimp/Importer.hpp> #include <assimp/StreamReader.h>
#include <assimp/importerdesc.h> #include <assimp/importerdesc.h>
#include <assimp/Importer.hpp>
namespace Assimp { namespace Assimp {
template<> template <>
const char* LogFunctions<FBXImporter>::Prefix() { const char *LogFunctions<FBXImporter>::Prefix() {
static auto prefix = "FBX: "; static auto prefix = "FBX: ";
return prefix; return prefix;
} }
} } // namespace Assimp
using namespace Assimp; using namespace Assimp;
using namespace Assimp::Formatter; using namespace Assimp::Formatter;
@ -91,44 +91,39 @@ static const aiImporterDesc desc = {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Constructor to be privately used by #Importer // Constructor to be privately used by #Importer
FBXImporter::FBXImporter() FBXImporter::FBXImporter() {
{
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Destructor, private as well // Destructor, private as well
FBXImporter::~FBXImporter() FBXImporter::~FBXImporter() {
{
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file. // Returns whether the class can handle the format of the given file.
bool FBXImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const bool FBXImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const {
{ const std::string &extension = GetExtension(pFile);
const std::string& extension = GetExtension(pFile); if (extension == std::string(desc.mFileExtensions)) {
if (extension == std::string( desc.mFileExtensions ) ) {
return true; return true;
} }
else if ((!extension.length() || checkSig) && pIOHandler) { else if ((!extension.length() || checkSig) && pIOHandler) {
// at least ASCII-FBX files usually have a 'FBX' somewhere in their head // at least ASCII-FBX files usually have a 'FBX' somewhere in their head
const char* tokens[] = {"fbx"}; const char *tokens[] = { "fbx" };
return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1); return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
} }
return false; return false;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// List all extensions handled by this loader // List all extensions handled by this loader
const aiImporterDesc* FBXImporter::GetInfo () const const aiImporterDesc *FBXImporter::GetInfo() const {
{
return &desc; return &desc;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Setup configuration properties for the loader // Setup configuration properties for the loader
void FBXImporter::SetupProperties(const Importer* pImp) void FBXImporter::SetupProperties(const Importer *pImp) {
{
settings.readAllLayers = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_GEOMETRY_LAYERS, true); settings.readAllLayers = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_GEOMETRY_LAYERS, true);
settings.readAllMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_MATERIALS, false); settings.readAllMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_MATERIALS, false);
settings.readMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_MATERIALS, true); settings.readMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_MATERIALS, true);
@ -146,9 +141,8 @@ void FBXImporter::SetupProperties(const Importer* pImp)
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Imports the given file into the given scene structure. // Imports the given file into the given scene structure.
void FBXImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) void FBXImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
{ std::unique_ptr<IOStream> stream(pIOHandler->Open(pFile, "rb"));
std::unique_ptr<IOStream> stream(pIOHandler->Open(pFile,"rb"));
if (!stream) { if (!stream) {
ThrowException("Could not open file for reading"); ThrowException("Could not open file for reading");
} }
@ -159,10 +153,10 @@ void FBXImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS
// streaming for its output data structures so the net win with // streaming for its output data structures so the net win with
// streaming input data would be very low. // streaming input data would be very low.
std::vector<char> contents; std::vector<char> contents;
contents.resize(stream->FileSize()+1); contents.resize(stream->FileSize() + 1);
stream->Read( &*contents.begin(), 1, contents.size()-1 ); stream->Read(&*contents.begin(), 1, contents.size() - 1);
contents[ contents.size() - 1 ] = 0; contents[contents.size() - 1] = 0;
const char* const begin = &*contents.begin(); const char *const begin = &*contents.begin();
// broadphase tokenizing pass in which we identify the core // broadphase tokenizing pass in which we identify the core
// syntax elements of FBX (brackets, commas, key:value mappings) // syntax elements of FBX (brackets, commas, key:value mappings)
@ -170,12 +164,11 @@ void FBXImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS
try { try {
bool is_binary = false; bool is_binary = false;
if (!strncmp(begin,"Kaydara FBX Binary",18)) { if (!strncmp(begin, "Kaydara FBX Binary", 18)) {
is_binary = true; is_binary = true;
TokenizeBinary(tokens,begin,contents.size()); TokenizeBinary(tokens, begin, contents.size());
} } else {
else { Tokenize(tokens, begin);
Tokenize(tokens,begin);
} }
// use this information to construct a very rudimentary // use this information to construct a very rudimentary
@ -183,7 +176,7 @@ void FBXImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS
Parser parser(tokens, is_binary); Parser parser(tokens, is_binary);
// take the raw parse-tree and convert it to a FBX DOM // take the raw parse-tree and convert it to a FBX DOM
Document doc(parser,settings); Document doc(parser, settings);
// convert the FBX DOM to aiScene // convert the FBX DOM to aiScene
ConvertToAssimpScene(pScene, doc, settings.removeEmptyBones); ConvertToAssimpScene(pScene, doc, settings.removeEmptyBones);
@ -193,12 +186,11 @@ void FBXImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS
// Set FBX file scale is relative to CM must be converted to M for // Set FBX file scale is relative to CM must be converted to M for
// assimp universal format (M) // assimp universal format (M)
SetFileScale( size_relative_to_cm * 0.01f); SetFileScale(size_relative_to_cm * 0.01f);
std::for_each(tokens.begin(),tokens.end(),Util::delete_fun<Token>()); std::for_each(tokens.begin(), tokens.end(), Util::delete_fun<Token>());
} } catch (std::exception &) {
catch(std::exception&) { std::for_each(tokens.begin(), tokens.end(), Util::delete_fun<Token>());
std::for_each(tokens.begin(),tokens.end(),Util::delete_fun<Token>());
throw; throw;
} }
} }

View File

@ -57,7 +57,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/material.h> #include <assimp/material.h>
#include <assimp/scene.h> #include <assimp/scene.h>
#include <assimp/importerdesc.h> #include <assimp/importerdesc.h>
#include <assimp/Macros.h>
using namespace Assimp; using namespace Assimp;
using namespace irr; using namespace irr;

View File

@ -0,0 +1,420 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2019 bzt
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_EXPORT
#ifndef ASSIMP_BUILD_NO_M3D_EXPORTER
#define M3D_IMPLEMENTATION
#define M3D_NOIMPORTER
#define M3D_EXPORTER
#define M3D_ASCII
#ifndef ASSIMP_BUILD_NO_M3D_IMPORTER
#define M3D_NODUP
#endif
// Header files, standard library.
#include <memory> // shared_ptr
#include <string>
#include <vector>
#include <assimp/version.h> // aiGetVersion
#include <assimp/IOSystem.hpp>
#include <assimp/Exporter.hpp>
#include <assimp/DefaultLogger.hpp>
#include <assimp/StreamWriter.h> // StreamWriterLE
#include <assimp/Exceptional.h> // DeadlyExportError
#include <assimp/material.h> // aiTextureType
#include <assimp/scene.h>
#include <assimp/mesh.h>
#include "M3DExporter.h"
#include "M3DMaterials.h"
// RESOURCES:
// https://gitlab.com/bztsrc/model3d/blob/master/docs/m3d_format.md
// https://gitlab.com/bztsrc/model3d/blob/master/docs/a3d_format.md
/*
* Currently supports static meshes, vertex colors, materials, textures
*
* For animation, it would require the following conversions:
* - aiNode (bones) -> m3d_t.bone (with parent id, position vector and oriantation quaternion)
* - aiMesh.aiBone -> m3d_t.skin (per vertex, with bone id, weight pairs)
* - aiAnimation -> m3d_action (frame with timestamp and list of bone id, position, orientation
* triplets, instead of per bone timestamp + lists)
*/
using namespace Assimp;
namespace Assimp {
// ---------------------------------------------------------------------
// Worker function for exporting a scene to binary M3D.
// Prototyped and registered in Exporter.cpp
void ExportSceneM3D (
const char* pFile,
IOSystem* pIOSystem,
const aiScene* pScene,
const ExportProperties* pProperties
){
// initialize the exporter
M3DExporter exporter(pScene, pProperties);
// perform binary export
exporter.doExport(pFile, pIOSystem, false);
}
// ---------------------------------------------------------------------
// Worker function for exporting a scene to ASCII A3D.
// Prototyped and registered in Exporter.cpp
void ExportSceneA3D (
const char* pFile,
IOSystem* pIOSystem,
const aiScene* pScene,
const ExportProperties* pProperties
){
// initialize the exporter
M3DExporter exporter(pScene, pProperties);
// perform ascii export
exporter.doExport(pFile, pIOSystem, true);
}
} // end of namespace Assimp
// ------------------------------------------------------------------------------------------------
M3DExporter::M3DExporter ( const aiScene* pScene, const ExportProperties* pProperties )
: mScene(pScene)
, mProperties(pProperties)
, outfile()
, m3d(nullptr) { }
// ------------------------------------------------------------------------------------------------
void M3DExporter::doExport (
const char* pFile,
IOSystem* pIOSystem,
bool toAscii
){
// TODO: convert mProperties into M3D_EXP_* flags
(void)mProperties;
// open the indicated file for writing (in binary / ASCII mode)
outfile.reset(pIOSystem->Open(pFile, toAscii ? "wt" : "wb"));
if (!outfile) {
throw DeadlyExportError( "could not open output .m3d file: " + std::string(pFile) );
}
// use malloc() here because m3d_free() will call free()
m3d = (m3d_t*)calloc(1, sizeof(m3d_t));
if(!m3d) {
throw DeadlyExportError( "memory allocation error" );
}
m3d->name = _m3d_safestr((char*)&mScene->mRootNode->mName.data, 2);
// Create a model from assimp structures
aiMatrix4x4 m;
NodeWalk(mScene->mRootNode, m);
// serialize the structures
unsigned int size;
unsigned char *output = m3d_save(m3d, M3D_EXP_FLOAT,
M3D_EXP_EXTRA | (toAscii ? M3D_EXP_ASCII : 0), &size);
m3d_free(m3d);
if(!output || size < 8) {
throw DeadlyExportError( "unable to serialize into Model 3D" );
}
// Write out serialized model
outfile->Write(output, size, 1);
// explicitly release file pointer,
// so we don't have to rely on class destruction.
outfile.reset();
}
// ------------------------------------------------------------------------------------------------
// helper to add a vertex (private to NodeWalk)
m3dv_t *M3DExporter::AddVrtx(m3dv_t *vrtx, uint32_t *numvrtx, m3dv_t *v, uint32_t *idx)
{
if(v->x == (M3D_FLOAT)-0.0) v->x = (M3D_FLOAT)0.0;
if(v->y == (M3D_FLOAT)-0.0) v->y = (M3D_FLOAT)0.0;
if(v->z == (M3D_FLOAT)-0.0) v->z = (M3D_FLOAT)0.0;
if(v->w == (M3D_FLOAT)-0.0) v->w = (M3D_FLOAT)0.0;
vrtx = (m3dv_t*)M3D_REALLOC(vrtx, ((*numvrtx) + 1) * sizeof(m3dv_t));
memcpy(&vrtx[*numvrtx], v, sizeof(m3dv_t));
*idx = *numvrtx;
(*numvrtx)++;
return vrtx;
}
// ------------------------------------------------------------------------------------------------
// helper to add a tmap (private to NodeWalk)
m3dti_t *M3DExporter::AddTmap(m3dti_t *tmap, uint32_t *numtmap, m3dti_t *ti, uint32_t *idx)
{
tmap = (m3dti_t*)M3D_REALLOC(tmap, ((*numtmap) + 1) * sizeof(m3dti_t));
memcpy(&tmap[*numtmap], ti, sizeof(m3dti_t));
*idx = *numtmap;
(*numtmap)++;
return tmap;
}
// ------------------------------------------------------------------------------------------------
// recursive node walker
void M3DExporter::NodeWalk(const aiNode* pNode, aiMatrix4x4 m)
{
aiMatrix4x4 nm = m * pNode->mTransformation;
for(unsigned int i = 0; i < pNode->mNumMeshes; i++) {
const aiMesh *mesh = mScene->mMeshes[pNode->mMeshes[i]];
unsigned int mi = (M3D_INDEX)-1U;
if(mScene->mMaterials) {
// get the material for this mesh
mi = addMaterial(mScene->mMaterials[mesh->mMaterialIndex]);
}
// iterate through the mesh faces
for(unsigned int j = 0; j < mesh->mNumFaces; j++) {
unsigned int n;
const aiFace* face = &(mesh->mFaces[j]);
// only triangle meshes supported for now
if(face->mNumIndices != 3) {
throw DeadlyExportError( "use aiProcess_Triangulate before export" );
}
// add triangle to the output
n = m3d->numface++;
m3d->face = (m3df_t*)M3D_REALLOC(m3d->face,
m3d->numface * sizeof(m3df_t));
if(!m3d->face) {
throw DeadlyExportError( "memory allocation error" );
}
/* set all index to -1 by default */
m3d->face[n].vertex[0] = m3d->face[n].vertex[1] = m3d->face[n].vertex[2] =
m3d->face[n].normal[0] = m3d->face[n].normal[1] = m3d->face[n].normal[2] =
m3d->face[n].texcoord[0] = m3d->face[n].texcoord[1] = m3d->face[n].texcoord[2] = -1U;
m3d->face[n].materialid = mi;
for(unsigned int k = 0; k < face->mNumIndices; k++) {
// get the vertex's index
unsigned int l = face->mIndices[k];
unsigned int idx;
m3dv_t vertex;
m3dti_t ti;
// multiply the position vector by the transformation matrix
aiVector3D v = mesh->mVertices[l];
v *= nm;
vertex.x = v.x;
vertex.y = v.y;
vertex.z = v.z;
vertex.w = 1.0;
vertex.color = 0;
vertex.skinid = -1U;
// add color if defined
if(mesh->HasVertexColors(0))
vertex.color = mkColor(&mesh->mColors[0][l]);
// save the vertex to the output
m3d->vertex = AddVrtx(m3d->vertex, &m3d->numvertex,
&vertex, &idx);
m3d->face[n].vertex[k] = (M3D_INDEX)idx;
// do we have texture coordinates?
if(mesh->HasTextureCoords(0)) {
ti.u = mesh->mTextureCoords[0][l].x;
ti.v = mesh->mTextureCoords[0][l].y;
m3d->tmap = AddTmap(m3d->tmap, &m3d->numtmap, &ti, &idx);
m3d->face[n].texcoord[k] = (M3D_INDEX)idx;
}
// do we have normal vectors?
if(mesh->HasNormals()) {
vertex.x = mesh->mNormals[l].x;
vertex.y = mesh->mNormals[l].y;
vertex.z = mesh->mNormals[l].z;
vertex.color = 0;
m3d->vertex = AddVrtx(m3d->vertex, &m3d->numvertex, &vertex, &idx);
m3d->face[n].normal[k] = (M3D_INDEX)idx;
}
}
}
}
// repeat for the children nodes
for (unsigned int i = 0; i < pNode->mNumChildren; i++) {
NodeWalk(pNode->mChildren[i], nm);
}
}
// ------------------------------------------------------------------------------------------------
// convert aiColor4D into uint32_t
uint32_t M3DExporter::mkColor(aiColor4D* c)
{
return ((uint8_t)(c->a*255) << 24L) |
((uint8_t)(c->b*255) << 16L) |
((uint8_t)(c->g*255) << 8L) |
((uint8_t)(c->r*255) << 0L);
}
// ------------------------------------------------------------------------------------------------
// add a material to the output
M3D_INDEX M3DExporter::addMaterial(const aiMaterial *mat)
{
unsigned int mi = -1U;
aiColor4D c;
aiString name;
ai_real f;
char *fn;
if(mat && mat->Get(AI_MATKEY_NAME, name) == AI_SUCCESS && name.length &&
strcmp((char*)&name.data, AI_DEFAULT_MATERIAL_NAME)) {
// check if we have saved a material by this name. This has to be done
// because only the referenced materials should be added to the output
for(unsigned int i = 0; i < m3d->nummaterial; i++)
if(!strcmp((char*)&name.data, m3d->material[i].name)) {
mi = i;
break;
}
// if not found, add the material to the output
if(mi == -1U) {
unsigned int k;
mi = m3d->nummaterial++;
m3d->material = (m3dm_t*)M3D_REALLOC(m3d->material, m3d->nummaterial
* sizeof(m3dm_t));
if(!m3d->material) {
throw DeadlyExportError( "memory allocation error" );
}
m3d->material[mi].name = _m3d_safestr((char*)&name.data, 0);
m3d->material[mi].numprop = 0;
m3d->material[mi].prop = NULL;
// iterate through the material property table and see what we got
for(k = 0; k < 15; k++) {
unsigned int j;
if(m3d_propertytypes[k].format == m3dpf_map)
continue;
if(aiProps[k].pKey) {
switch(m3d_propertytypes[k].format) {
case m3dpf_color:
if(mat->Get(aiProps[k].pKey, aiProps[k].type,
aiProps[k].index, c) == AI_SUCCESS)
addProp(&m3d->material[mi],
m3d_propertytypes[k].id, mkColor(&c));
break;
case m3dpf_float:
if(mat->Get(aiProps[k].pKey, aiProps[k].type,
aiProps[k].index, f) == AI_SUCCESS)
addProp(&m3d->material[mi],
m3d_propertytypes[k].id,
/* not (uint32_t)f, because we don't want to convert
* it, we want to see it as 32 bits of memory */
*((uint32_t*)&f));
break;
case m3dpf_uint8:
if(mat->Get(aiProps[k].pKey, aiProps[k].type,
aiProps[k].index, j) == AI_SUCCESS) {
// special conversion for illumination model property
if(m3d_propertytypes[k].id == m3dp_il) {
switch(j) {
case aiShadingMode_NoShading: j = 0; break;
case aiShadingMode_Phong: j = 2; break;
default: j = 1; break;
}
}
addProp(&m3d->material[mi],
m3d_propertytypes[k].id, j);
}
break;
default:
if(mat->Get(aiProps[k].pKey, aiProps[k].type,
aiProps[k].index, j) == AI_SUCCESS)
addProp(&m3d->material[mi],
m3d_propertytypes[k].id, j);
break;
}
}
if(aiTxProps[k].pKey &&
mat->GetTexture((aiTextureType)aiTxProps[k].type,
aiTxProps[k].index, &name, NULL, NULL, NULL,
NULL, NULL) == AI_SUCCESS) {
unsigned int i;
for(j = name.length-1; j > 0 && name.data[j]!='.'; j++);
if(j && name.data[j]=='.' &&
(name.data[j+1]=='p' || name.data[j+1]=='P') &&
(name.data[j+1]=='n' || name.data[j+1]=='N') &&
(name.data[j+1]=='g' || name.data[j+1]=='G'))
name.data[j]=0;
// do we have this texture saved already?
fn = _m3d_safestr((char*)&name.data, 0);
for(j = 0, i = -1U; j < m3d->numtexture; j++)
if(!strcmp(fn, m3d->texture[j].name)) {
i = j;
free(fn);
break;
}
if(i == -1U) {
i = m3d->numtexture++;
m3d->texture = (m3dtx_t*)M3D_REALLOC(
m3d->texture,
m3d->numtexture * sizeof(m3dtx_t));
if(!m3d->texture) {
throw DeadlyExportError( "memory allocation error" );
}
// we don't need the texture itself, only its name
m3d->texture[i].name = fn;
m3d->texture[i].w = 0;
m3d->texture[i].h = 0;
m3d->texture[i].d = NULL;
}
addProp(&m3d->material[mi],
m3d_propertytypes[k].id + 128, i);
}
}
}
}
return mi;
}
// ------------------------------------------------------------------------------------------------
// add a material property to the output
void M3DExporter::addProp(m3dm_t *m, uint8_t type, uint32_t value)
{
unsigned int i;
i = m->numprop++;
m->prop = (m3dp_t*)M3D_REALLOC(m->prop, m->numprop * sizeof(m3dp_t));
if(!m->prop) { throw DeadlyExportError( "memory allocation error" ); }
m->prop[i].type = type;
m->prop[i].value.num = value;
}
#endif // ASSIMP_BUILD_NO_M3D_EXPORTER
#endif // ASSIMP_BUILD_NO_EXPORT

View File

@ -0,0 +1,100 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2019 bzt
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.
----------------------------------------------------------------------
*/
/** @file M3DExporter.h
* @brief Declares the exporter class to write a scene to a Model 3D file
*/
#ifndef AI_M3DEXPORTER_H_INC
#define AI_M3DEXPORTER_H_INC
#ifndef ASSIMP_BUILD_NO_M3D_EXPORTER
#include "m3d.h"
#include <assimp/types.h>
//#include <assimp/material.h>
#include <assimp/StreamWriter.h> // StreamWriterLE
#include <assimp/Exceptional.h> // DeadlyExportError
#include <memory> // shared_ptr
struct aiScene;
struct aiNode;
struct aiMaterial;
struct aiFace;
namespace Assimp
{
class IOSystem;
class IOStream;
class ExportProperties;
// ---------------------------------------------------------------------
/** Helper class to export a given scene to an M3D file. */
// ---------------------------------------------------------------------
class M3DExporter
{
public:
/// Constructor for a specific scene to export
M3DExporter(const aiScene* pScene, const ExportProperties* pProperties);
// call this to do the actual export
void doExport(const char* pFile, IOSystem* pIOSystem, bool toAscii);
private:
const aiScene* mScene; // the scene to export
const ExportProperties* mProperties; // currently unused
std::shared_ptr<IOStream> outfile; // file to write to
m3d_t *m3d; // model for the C library to convert to
// helper to do the recursive walking
void NodeWalk(const aiNode* pNode, aiMatrix4x4 m);
m3dv_t *AddVrtx(m3dv_t *vrtx, uint32_t *numvrtx, m3dv_t *v, uint32_t *idx);
m3dti_t *AddTmap(m3dti_t *tmap, uint32_t *numtmap, m3dti_t *ti, uint32_t *idx);
uint32_t mkColor(aiColor4D* c);
M3D_INDEX addMaterial(const aiMaterial *mat);
void addProp(m3dm_t *m, uint8_t type, uint32_t value);
};
}
#endif // ASSIMP_BUILD_NO_M3D_EXPORTER
#endif // AI_M3DEXPORTER_H_INC

View File

@ -0,0 +1,766 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2019 bzt
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_M3D_IMPORTER
#define M3D_IMPLEMENTATION
#define M3D_ASCII
#define M3D_NONORMALS /* leave the post-processing to Assimp */
#define M3D_NOWEIGHTS
#define M3D_NOANIMATION
#include <assimp/IOStreamBuffer.h>
#include <memory>
#include <assimp/DefaultIOSystem.h>
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/ai_assert.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/importerdesc.h>
#include "M3DImporter.h"
#include "M3DMaterials.h"
// RESOURCES:
// https://gitlab.com/bztsrc/model3d/blob/master/docs/m3d_format.md
// https://gitlab.com/bztsrc/model3d/blob/master/docs/a3d_format.md
/*
Unfortunately aiNode has bone structures and meshes too, yet we can't assign
the mesh to a bone aiNode as a skin may refer to several aiNodes. Therefore
I've decided to import into this structure:
aiScene->mRootNode
| |->mMeshes (all the meshes)
| \->children (empty if there's no skeleton imported, no meshes)
| \->skeleton root aiNode*
| |->bone aiNode
| | \->subbone aiNode
| |->bone aiNode
| | ...
| \->bone aiNode
\->mMeshes[]
\->aiBone, referencing mesh-less aiNodes from above
* - normally one, but if a model has several skeleton roots, then all of them
are listed in aiScene->mRootNode->children, but all without meshes
*/
static const aiImporterDesc desc = {
"Model 3D Importer",
"",
"",
"",
aiImporterFlags_SupportBinaryFlavour,
0,
0,
0,
0,
"m3d a3d"
};
// workaround: the SDK expects a C callback, but we want to use Assimp::IOSystem to implement that
extern "C" {
void* m3dimporter_pIOHandler;
unsigned char *m3dimporter_readfile(char *fn, unsigned int *size) {
ai_assert( nullptr != fn );
ai_assert( nullptr != size );
std::string file(fn);
std::unique_ptr<Assimp::IOStream> pStream(
(reinterpret_cast<Assimp::IOSystem*>(m3dimporter_pIOHandler))->Open( file, "rb"));
size_t fileSize = 0;
unsigned char *data = NULL;
// sometimes pStream is nullptr for some reason (should be an empty object returning nothing I guess)
if(pStream) {
fileSize = pStream->FileSize();
// should be allocated with malloc(), because the library will call free() to deallocate
data = (unsigned char*)malloc(fileSize);
if( !data || !pStream.get() || !fileSize || fileSize != pStream->Read(data,1,fileSize)) {
pStream.reset();
*size = 0;
// don't throw a deadly exception, it's not fatal if we can't read an external asset
return nullptr;
}
pStream.reset();
}
*size = (int)fileSize;
return data;
}
}
namespace Assimp {
using namespace std;
// ------------------------------------------------------------------------------------------------
// Default constructor
M3DImporter::M3DImporter()
: mScene(nullptr)
, m3d(nullptr) { }
// ------------------------------------------------------------------------------------------------
// Destructor.
M3DImporter::~M3DImporter() {}
// ------------------------------------------------------------------------------------------------
// Returns true, if file is a binary or ASCII Model 3D file.
bool M3DImporter::CanRead(const std::string& pFile, IOSystem* pIOHandler , bool checkSig) const {
const std::string extension = GetExtension(pFile);
if (extension == "m3d" || extension == "a3d")
return true;
else if (!extension.length() || checkSig) {
if (!pIOHandler) {
return true;
}
/*
* don't use CheckMagicToken because that checks with swapped bytes too, leading to false
* positives. This magic is not uint32_t, but char[4], so memcmp is the best way
const char* tokens[] = {"3DMO", "3dmo"};
return CheckMagicToken(pIOHandler,pFile,tokens,2,0,4);
*/
std::unique_ptr<IOStream> pStream (pIOHandler->Open(pFile, "rb"));
unsigned char data[4];
if(4 != pStream->Read(data,1,4)) {
return false;
}
return !memcmp(data, "3DMO", 4) /* bin */ || !memcmp(data, "3dmo", 4) /* ASCII */;
}
return false;
}
// ------------------------------------------------------------------------------------------------
const aiImporterDesc* M3DImporter::GetInfo() const {
return &desc;
}
// ------------------------------------------------------------------------------------------------
// Model 3D import implementation
void M3DImporter::InternReadFile( const std::string &file, aiScene* pScene, IOSystem* pIOHandler) {
// Read file into memory
std::unique_ptr<IOStream> pStream( pIOHandler->Open( file, "rb"));
if( !pStream.get() ) {
throw DeadlyImportError( "Failed to open file " + file + "." );
}
// Get the file-size and validate it, throwing an exception when fails
size_t fileSize = pStream->FileSize();
if( fileSize < 8 ) {
throw DeadlyImportError( "M3D-file " + file + " is too small." );
}
std::unique_ptr<unsigned char[]> _buffer (new unsigned char[fileSize]);
unsigned char *data( _buffer.get() );
if(fileSize != pStream->Read(data,1,fileSize)) {
throw DeadlyImportError( "Failed to read the file " + file + "." );
}
// Get the path for external assets
std::string folderName( "./" );
std::string::size_type pos = file.find_last_of( "\\/" );
if ( pos != std::string::npos ) {
folderName = file.substr( 0, pos );
if ( !folderName.empty() ) {
pIOHandler->PushDirectory( folderName );
}
}
// pass this IOHandler to the C callback
m3dimporter_pIOHandler = pIOHandler;
//DefaultLogger::create("/dev/stderr", Logger::VERBOSE);
ASSIMP_LOG_DEBUG_F("M3D: loading ", file);
// let the C SDK do the hard work for us
m3d = m3d_load(&data[0], m3dimporter_readfile, free, nullptr);
m3dimporter_pIOHandler = nullptr;
if( !m3d ) {
throw DeadlyImportError( "Unable to parse " + file + " as M3D." );
}
// create the root node
pScene->mRootNode = new aiNode;
pScene->mRootNode->mName = aiString(std::string(std::string(m3d->name)));
pScene->mRootNode->mTransformation = aiMatrix4x4();
pScene->mRootNode->mNumChildren = 0;
mScene = pScene;
ASSIMP_LOG_DEBUG("M3D: root node " + std::string(m3d->name));
// now we just have to fill up the Assimp structures in pScene
importMaterials();
importTextures();
importBones(-1U, pScene->mRootNode);
importMeshes();
importAnimations();
// we don't need the SDK's version any more
m3d_free(m3d);
// Pop directory stack
if ( pIOHandler->StackSize() > 0 ) {
pIOHandler->PopDirectory();
}
}
// ------------------------------------------------------------------------------------------------
// convert materials. properties are converted using a static table in M3DMaterials.h
void M3DImporter::importMaterials()
{
unsigned int i, j, k, l, n;
m3dm_t *m;
aiString name = aiString(AI_DEFAULT_MATERIAL_NAME);
aiColor4D c;
ai_real f;
ai_assert(mScene != nullptr);
ai_assert(m3d != nullptr);
mScene->mNumMaterials = m3d->nummaterial + 1;
mScene->mMaterials = new aiMaterial*[ m3d->nummaterial + 1 ];
ASSIMP_LOG_DEBUG_F("M3D: importMaterials ", mScene->mNumMaterials);
// add a default material as first
aiMaterial* mat = new aiMaterial;
mat->AddProperty( &name, AI_MATKEY_NAME );
c.a = 1.0; c.b = c.g = c.r = 0.6;
mat->AddProperty( &c, 1, AI_MATKEY_COLOR_DIFFUSE);
mScene->mMaterials[0] = mat;
for(i = 0; i < m3d->nummaterial; i++) {
m = &m3d->material[i];
aiMaterial* mat = new aiMaterial;
name.Set(std::string(m->name));
mat->AddProperty( &name, AI_MATKEY_NAME );
for(j = 0; j < m->numprop; j++) {
// look up property type
// 0 - 127 scalar values,
// 128 - 255 the same properties but for texture maps
k = 256;
for(l = 0; l < sizeof(m3d_propertytypes)/sizeof(m3d_propertytypes[0]); l++)
if(m->prop[j].type == m3d_propertytypes[l].id ||
m->prop[j].type == m3d_propertytypes[l].id + 128) {
k = l;
break;
}
// should never happen, but be safe than sorry
if(k == 256) continue;
// scalar properties
if(m->prop[j].type < 128 && aiProps[k].pKey) {
switch(m3d_propertytypes[k].format) {
case m3dpf_color:
c = mkColor(m->prop[j].value.color);
mat->AddProperty(&c, 1, aiProps[k].pKey, aiProps[k].type, aiProps[k].index);
break;
case m3dpf_float:
f = m->prop[j].value.fnum;
mat->AddProperty(&f, 1, aiProps[k].pKey, aiProps[k].type, aiProps[k].index);
break;
default:
n = m->prop[j].value.num;
if(m->prop[j].type == m3dp_il) {
switch(n) {
case 0: n = aiShadingMode_NoShading; break;
case 2: n = aiShadingMode_Phong; break;
default: n = aiShadingMode_Gouraud; break;
}
}
mat->AddProperty(&n, 1, aiProps[k].pKey, aiProps[k].type, aiProps[k].index);
break;
}
}
// texture map properties
if(m->prop[j].type >= 128 && aiTxProps[k].pKey &&
// extra check, should never happen, do we have the refered texture?
m->prop[j].value.textureid < m3d->numtexture &&
m3d->texture[m->prop[j].value.textureid].name) {
name.Set(std::string(std::string(m3d->texture[m->prop[j].value.textureid].name) + ".png"));
mat->AddProperty(&name, aiTxProps[k].pKey, aiTxProps[k].type, aiTxProps[k].index);
n = 0;
mat->AddProperty(&n, 1, _AI_MATKEY_UVWSRC_BASE, aiProps[k].type, aiProps[k].index);
}
}
mScene->mMaterials[i + 1] = mat;
}
}
// ------------------------------------------------------------------------------------------------
// import textures, this is the simplest of all
void M3DImporter::importTextures()
{
unsigned int i;
const char *formatHint[] = { "rgba0800", "rgba0808", "rgba8880", "rgba8888" };
m3dtx_t *t;
ai_assert(mScene != nullptr);
ai_assert(m3d != nullptr);
mScene->mNumTextures = m3d->numtexture;
ASSIMP_LOG_DEBUG_F("M3D: importTextures ", mScene->mNumTextures);
if(!m3d->numtexture)
return;
mScene->mTextures = new aiTexture*[m3d->numtexture];
for(i = 0; i < m3d->numtexture; i++) {
unsigned int j, k;
t = &m3d->texture[i];
if(!t->w || !t->h || !t->f || !t->d) continue;
aiTexture *tx = new aiTexture;
strcpy(tx->achFormatHint, formatHint[t->f - 1]);
tx->mFilename = aiString(std::string(t->name) + ".png");
tx->mWidth = t->w;
tx->mHeight = t->h;
tx->pcData = new aiTexel[ tx->mWidth*tx->mHeight ];
for(j = k = 0; j < tx->mWidth*tx->mHeight; j++) {
switch(t->f) {
case 1: tx->pcData[j].g = t->d[k++]; break;
case 2: tx->pcData[j].g = t->d[k++]; tx->pcData[j].a = t->d[k++]; break;
case 3:
tx->pcData[j].r = t->d[k++]; tx->pcData[j].g = t->d[k++];
tx->pcData[j].b = t->d[k++]; tx->pcData[j].a = 255;
break;
case 4:
tx->pcData[j].r = t->d[k++]; tx->pcData[j].g = t->d[k++];
tx->pcData[j].b = t->d[k++]; tx->pcData[j].a = t->d[k++];
break;
}
}
mScene->mTextures[i] = tx;
}
}
// ------------------------------------------------------------------------------------------------
// this is tricky. M3D has a global vertex and UV list, and faces are indexing them
// individually. In assimp there're per mesh vertex and UV lists, and they must be
// indexed simultaneously.
void M3DImporter::importMeshes()
{
unsigned int i, j, k, l, numpoly = 3, lastMat = -2U;
std::vector<aiMesh*> *meshes = new std::vector<aiMesh*>();
std::vector<aiFace> *faces = nullptr;
std::vector<aiVector3D> *vertices = nullptr;
std::vector<aiVector3D> *normals = nullptr;
std::vector<aiVector3D> *texcoords = nullptr;
std::vector<aiColor4D> *colors = nullptr;
std::vector<unsigned int> *vertexids = nullptr;
aiMesh *pMesh = nullptr;
ai_assert(mScene != nullptr);
ai_assert(m3d != nullptr);
ai_assert(mScene->mRootNode != nullptr);
ASSIMP_LOG_DEBUG_F("M3D: importMeshes ", m3d->numface);
for(i = 0; i < m3d->numface; i++) {
// we must switch mesh if material changes
if(lastMat != m3d->face[i].materialid) {
lastMat = m3d->face[i].materialid;
if(pMesh && vertices && vertices->size() && faces && faces->size()) {
populateMesh(pMesh, faces, vertices, normals, texcoords, colors, vertexids);
meshes->push_back(pMesh);
delete faces;
delete vertices;
delete normals;
delete texcoords;
delete colors;
delete vertexids; // this is not stored in pMesh, just to collect bone vertices
}
pMesh = new aiMesh;
pMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
pMesh->mMaterialIndex = lastMat + 1;
faces = new std::vector<aiFace>();
vertices = new std::vector<aiVector3D>();
normals = new std::vector<aiVector3D>();
texcoords = new std::vector<aiVector3D>();
colors = new std::vector<aiColor4D>();
vertexids = new std::vector<unsigned int>();
}
// add a face to temporary vector
aiFace *pFace = new aiFace;
pFace->mNumIndices = numpoly;
pFace->mIndices = new unsigned int[numpoly];
for(j = 0; j < numpoly; j++) {
aiVector3D pos, uv, norm;
k = vertices->size();
pFace->mIndices[j] = k;
l = m3d->face[i].vertex[j];
pos.x = m3d->vertex[l].x;
pos.y = m3d->vertex[l].y;
pos.z = m3d->vertex[l].z;
vertices->push_back(pos);
colors->push_back(mkColor(m3d->vertex[l].color));
// add a bone to temporary vector
if(m3d->vertex[l].skinid != -1U &&m3d->vertex[l].skinid != -2U && m3d->skin && m3d->bone) {
// this is complicated, because M3D stores a list of bone id / weight pairs per
// vertex but assimp uses lists of local vertex id/weight pairs per local bone list
vertexids->push_back(l);
}
l = m3d->face[i].texcoord[j];
if(l != -1U) {
uv.x = m3d->tmap[l].u;
uv.y = m3d->tmap[l].v;
uv.z = 0.0;
texcoords->push_back(uv);
}
l = m3d->face[i].normal[j];
if(l != -1U) {
norm.x = m3d->vertex[l].x;
norm.y = m3d->vertex[l].y;
norm.z = m3d->vertex[l].z;
normals->push_back(norm);
}
}
faces->push_back(*pFace);
delete pFace;
}
// if there's data left in the temporary vectors, flush them
if(pMesh && vertices->size() && faces->size()) {
populateMesh(pMesh, faces, vertices, normals, texcoords, colors, vertexids);
meshes->push_back(pMesh);
}
// create global mesh list in scene
mScene->mNumMeshes = meshes->size();
mScene->mMeshes = new aiMesh*[mScene->mNumMeshes];
std::copy(meshes->begin(), meshes->end(), mScene->mMeshes);
// create mesh indeces in root node
mScene->mRootNode->mNumMeshes = meshes->size();
mScene->mRootNode->mMeshes = new unsigned int[meshes->size()];
for(i = 0; i < meshes->size(); i++) {
mScene->mRootNode->mMeshes[i] = i;
}
delete meshes;
if(faces) delete faces;
if(vertices) delete vertices;
if(normals) delete normals;
if(texcoords) delete texcoords;
if(colors) delete colors;
if(vertexids) delete vertexids;
}
// ------------------------------------------------------------------------------------------------
// a reentrant node parser. Otherwise this is simple
void M3DImporter::importBones(unsigned int parentid, aiNode *pParent)
{
unsigned int i, n;
ai_assert(pParent != nullptr);
ai_assert(mScene != nullptr);
ai_assert(m3d != nullptr);
ASSIMP_LOG_DEBUG_F("M3D: importBones ", m3d->numbone, " parentid ", (int)parentid);
for(n = 0, i = parentid + 1; i < m3d->numbone; i++)
if(m3d->bone[i].parent == parentid) n++;
pParent->mChildren = new aiNode*[n];
for(i = parentid + 1; i < m3d->numbone; i++) {
if(m3d->bone[i].parent == parentid) {
aiNode *pChild = new aiNode;
pChild->mParent = pParent;
pChild->mName = aiString(std::string(m3d->bone[i].name));
convertPose(&pChild->mTransformation, m3d->bone[i].pos, m3d->bone[i].ori);
pChild->mNumChildren = 0;
pParent->mChildren[pParent->mNumChildren] = pChild;
pParent->mNumChildren++;
importBones(i, pChild);
}
}
}
// ------------------------------------------------------------------------------------------------
// this is another headache. M3D stores list of changed bone id/position/orientation triplets and
// a timestamp per frame, but assimp needs timestamp and lists of position, orientation lists per
// bone, so we have to convert between the two conceptually different representation forms
void M3DImporter::importAnimations()
{
unsigned int i, j, k, l, pos, ori;
double t;
m3da_t *a;
ai_assert(mScene != nullptr);
ai_assert(m3d != nullptr);
mScene->mNumAnimations = m3d->numaction;
ASSIMP_LOG_DEBUG_F("M3D: importAnimations ", mScene->mNumAnimations);
if(!m3d->numaction || !m3d->numbone)
return;
mScene->mAnimations = new aiAnimation*[m3d->numaction];
for(i = 0; i < m3d->numaction; i++) {
a = &m3d->action[i];
aiAnimation *pAnim = new aiAnimation;
pAnim->mName = aiString(std::string(a->name));
pAnim->mDuration = ((double)a->durationmsec) / 10;
pAnim->mTicksPerSecond = 100;
// now we know how many bones are referenced in this animation
pAnim->mNumChannels = m3d->numbone;
pAnim->mChannels = new aiNodeAnim*[pAnim->mNumChannels];
for(l = 0; l < m3d->numbone; l++) {
unsigned int n;
pAnim->mChannels[l] = new aiNodeAnim;
pAnim->mChannels[l]->mNodeName = aiString(std::string(m3d->bone[l].name));
// now n is the size of positions / orientations arrays
pAnim->mChannels[l]->mNumPositionKeys = pAnim->mChannels[l]->mNumRotationKeys = a->numframe;
pAnim->mChannels[l]->mPositionKeys = new aiVectorKey[a->numframe];
pAnim->mChannels[l]->mRotationKeys = new aiQuatKey[a->numframe];
pos = m3d->bone[l].pos;
ori = m3d->bone[l].ori;
for(j = n = 0; j < a->numframe; j++) {
t = ((double)a->frame[j].msec) / 10;
for(k = 0; k < a->frame[j].numtransform; k++) {
if(a->frame[j].transform[k].boneid == l) {
pos = a->frame[j].transform[k].pos;
ori = a->frame[j].transform[k].ori;
}
}
m3dv_t *v = &m3d->vertex[pos];
m3dv_t *q = &m3d->vertex[ori];
pAnim->mChannels[l]->mPositionKeys[j].mTime = t;
pAnim->mChannels[l]->mPositionKeys[j].mValue.x = v->x;
pAnim->mChannels[l]->mPositionKeys[j].mValue.y = v->y;
pAnim->mChannels[l]->mPositionKeys[j].mValue.z = v->z;
pAnim->mChannels[l]->mRotationKeys[j].mTime = t;
pAnim->mChannels[l]->mRotationKeys[j].mValue.w = q->w;
pAnim->mChannels[l]->mRotationKeys[j].mValue.x = q->x;
pAnim->mChannels[l]->mRotationKeys[j].mValue.y = q->y;
pAnim->mChannels[l]->mRotationKeys[j].mValue.z = q->z;
}// foreach frame
}// foreach bones
mScene->mAnimations[i] = pAnim;
}
}
// ------------------------------------------------------------------------------------------------
// convert uint32_t into aiColor4D
aiColor4D M3DImporter::mkColor(uint32_t c) {
aiColor4D color;
color.a = ((float)((c >> 24)&0xff)) / 255;
color.b = ((float)((c >> 16)&0xff)) / 255;
color.g = ((float)((c >> 8)&0xff)) / 255;
color.r = ((float)((c >> 0)&0xff)) / 255;
return color;
}
// ------------------------------------------------------------------------------------------------
// convert a position id and orientation id into a 4 x 4 transformation matrix
void M3DImporter::convertPose(aiMatrix4x4 *m, unsigned int posid, unsigned int orientid)
{
ai_assert(m != nullptr);
ai_assert(m3d != nullptr);
ai_assert(posid != -1U && posid < m3d->numvertex);
ai_assert(orientid != -1U && orientid < m3d->numvertex);
m3dv_t *p = &m3d->vertex[posid];
m3dv_t *q = &m3d->vertex[orientid];
/* quaternion to matrix. Do NOT use aiQuaternion to aiMatrix3x3, gives bad results */
if(q->x == 0.0 && q->y == 0.0 && q->z >= 0.7071065 && q->z <= 0.7071075 && q->w == 0.0) {
m->a2 = m->a3 = m->b1 = m->b3 = m->c1 = m->c2 = 0.0;
m->a1 = m->b2 = m->c3 = -1.0;
} else {
m->a1 = 1 - 2 * (q->y * q->y + q->z * q->z); if(m->a1 > -M3D_EPSILON && m->a1 < M3D_EPSILON) m->a1 = 0.0;
m->a2 = 2 * (q->x * q->y - q->z * q->w); if(m->a2 > -M3D_EPSILON && m->a2 < M3D_EPSILON) m->a2 = 0.0;
m->a3 = 2 * (q->x * q->z + q->y * q->w); if(m->a3 > -M3D_EPSILON && m->a3 < M3D_EPSILON) m->a3 = 0.0;
m->b1 = 2 * (q->x * q->y + q->z * q->w); if(m->b1 > -M3D_EPSILON && m->b1 < M3D_EPSILON) m->b1 = 0.0;
m->b2 = 1 - 2 * (q->x * q->x + q->z * q->z); if(m->b2 > -M3D_EPSILON && m->b2 < M3D_EPSILON) m->b2 = 0.0;
m->b3 = 2 * (q->y * q->z - q->x * q->w); if(m->b3 > -M3D_EPSILON && m->b3 < M3D_EPSILON) m->b3 = 0.0;
m->c1 = 2 * (q->x * q->z - q->y * q->w); if(m->c1 > -M3D_EPSILON && m->c1 < M3D_EPSILON) m->c1 = 0.0;
m->c2 = 2 * (q->y * q->z + q->x * q->w); if(m->c2 > -M3D_EPSILON && m->c2 < M3D_EPSILON) m->c2 = 0.0;
m->c3 = 1 - 2 * (q->x * q->x + q->y * q->y); if(m->c3 > -M3D_EPSILON && m->c3 < M3D_EPSILON) m->c3 = 0.0;
}
/* set translation */
m->a4 = p->x; m->b4 = p->y; m->c4 = p->z;
m->d1 = 0; m->d2 = 0; m->d3 = 0; m->d4 = 1;
}
// ------------------------------------------------------------------------------------------------
// find a node by name
aiNode *M3DImporter::findNode(aiNode *pNode, aiString name)
{
unsigned int i;
ai_assert(pNode != nullptr);
ai_assert(mScene != nullptr);
if(pNode->mName == name)
return pNode;
for(i = 0; i < pNode->mNumChildren; i++) {
aiNode *pChild = findNode(pNode->mChildren[i], name);
if(pChild) return pChild;
}
return nullptr;
}
// ------------------------------------------------------------------------------------------------
// fills up offsetmatrix in mBones
void M3DImporter::calculateOffsetMatrix(aiNode *pNode, aiMatrix4x4 *m)
{
ai_assert(pNode != nullptr);
ai_assert(mScene != nullptr);
if(pNode->mParent) {
calculateOffsetMatrix(pNode->mParent, m);
*m *= pNode->mTransformation;
} else {
*m = pNode->mTransformation;
}
}
// ------------------------------------------------------------------------------------------------
// because M3D has a global mesh, global vertex ids and stores materialid on the face, we need
// temporary lists to collect data for an aiMesh, which requires local arrays and local indeces
// this function fills up an aiMesh with those temporary lists
void M3DImporter::populateMesh(aiMesh *pMesh, std::vector<aiFace> *faces, std::vector<aiVector3D> *vertices,
std::vector<aiVector3D> *normals, std::vector<aiVector3D> *texcoords, std::vector<aiColor4D> *colors,
std::vector<unsigned int> *vertexids) {
ai_assert(pMesh != nullptr);
ai_assert(faces != nullptr);
ai_assert(vertices != nullptr);
ai_assert(normals != nullptr);
ai_assert(texcoords != nullptr);
ai_assert(colors != nullptr);
ai_assert(vertexids != nullptr);
ai_assert(m3d != nullptr);
ASSIMP_LOG_DEBUG_F("M3D: populateMesh numvertices ", vertices->size(), " numfaces ", faces->size(),
" numnormals ", normals->size(), " numtexcoord ", texcoords->size(), " numbones ", m3d->numbone);
if(vertices->size() && faces->size()) {
pMesh->mNumFaces = faces->size();
pMesh->mFaces = new aiFace[pMesh->mNumFaces];
std::copy(faces->begin(), faces->end(), pMesh->mFaces);
pMesh->mNumVertices = vertices->size();
pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
std::copy(vertices->begin(), vertices->end(), pMesh->mVertices);
if(normals->size() == vertices->size()) {
pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
std::copy(normals->begin(), normals->end(), pMesh->mNormals);
}
if(texcoords->size() == vertices->size()) {
pMesh->mTextureCoords[0] = new aiVector3D[pMesh->mNumVertices];
std::copy(texcoords->begin(), texcoords->end(), pMesh->mTextureCoords[0]);
pMesh->mNumUVComponents[0] = 2;
}
if(colors->size() == vertices->size()) {
pMesh->mColors[0] = new aiColor4D[pMesh->mNumVertices];
std::copy(colors->begin(), colors->end(), pMesh->mColors[0]);
}
// this is complicated, because M3D stores a list of bone id / weight pairs per
// vertex but assimp uses lists of local vertex id/weight pairs per local bone list
pMesh->mNumBones = m3d->numbone;
/* we need aiBone with mOffsetMatrix for bones without weights as well */
if(pMesh->mNumBones) {
pMesh->mBones = new aiBone*[pMesh->mNumBones];
for(unsigned int i = 0; i < m3d->numbone; i++) {
aiNode *pNode;
pMesh->mBones[i] = new aiBone;
pMesh->mBones[i]->mName = aiString(std::string(m3d->bone[i].name));
pMesh->mBones[i]->mNumWeights = 0;
pNode = findNode(mScene->mRootNode, pMesh->mBones[i]->mName);
if(pNode) {
calculateOffsetMatrix(pNode, &pMesh->mBones[i]->mOffsetMatrix);
pMesh->mBones[i]->mOffsetMatrix.Inverse();
} else
pMesh->mBones[i]->mOffsetMatrix = aiMatrix4x4();
}
if(vertexids->size()) {
unsigned int i, j;
// first count how many vertices we have per bone
for(i = 0; i < vertexids->size(); i++) {
unsigned int s = m3d->vertex[vertexids->at(i)].skinid;
if(s != -1U && s!= -2U) {
for(unsigned int k = 0; k < M3D_NUMBONE && m3d->skin[s].weight[k] > 0.0; k++) {
aiString name = aiString(std::string(m3d->bone[m3d->skin[s].boneid[k]].name));
for(j = 0; j < pMesh->mNumBones; j++) {
if(pMesh->mBones[j]->mName == name) {
pMesh->mBones[j]->mNumWeights++;
break;
}
}
}
}
}
// allocate mWeights
for(j = 0; j < pMesh->mNumBones; j++) {
aiBone *pBone = pMesh->mBones[j];
if(pBone->mNumWeights) {
pBone->mWeights = new aiVertexWeight[pBone->mNumWeights];
pBone->mNumWeights = 0;
}
}
// fill up with data
for(i = 0; i < vertexids->size(); i++) {
unsigned int s = m3d->vertex[vertexids->at(i)].skinid;
if(s != -1U && s!= -2U) {
for(unsigned int k = 0; k < M3D_NUMBONE && m3d->skin[s].weight[k] > 0.0; k++) {
aiString name = aiString(std::string(m3d->bone[m3d->skin[s].boneid[k]].name));
for(j = 0; j < pMesh->mNumBones; j++) {
if(pMesh->mBones[j]->mName == name) {
aiBone *pBone = pMesh->mBones[j];
pBone->mWeights[pBone->mNumWeights].mVertexId = i;
pBone->mWeights[pBone->mNumWeights].mWeight = m3d->skin[s].weight[k];
pBone->mNumWeights++;
break;
}
}
} // foreach skin
}
} // foreach vertexids
}
}
}
}
// ------------------------------------------------------------------------------------------------
} // Namespace Assimp
#endif // !! ASSIMP_BUILD_NO_M3D_IMPORTER

View File

@ -0,0 +1,106 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2019 bzt
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.
----------------------------------------------------------------------
*/
/** @file M3DImporter.h
* @brief Declares the importer class to read a scene from a Model 3D file
*/
#ifndef AI_M3DIMPORTER_H_INC
#define AI_M3DIMPORTER_H_INC
#ifndef ASSIMP_BUILD_NO_M3D_IMPORTER
#include "m3d.h"
#include <assimp/BaseImporter.h>
#include <assimp/material.h>
#include <vector>
struct aiMesh;
struct aiNode;
struct aiMaterial;
struct aiFace;
namespace Assimp {
class M3DImporter : public BaseImporter {
public:
/// \brief Default constructor
M3DImporter();
/// \brief Destructor
~M3DImporter();
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:
aiScene* mScene; // the scene to import to
m3d_t *m3d; // model for the C library to convert from
//! \brief Appends the supported extension.
const aiImporterDesc* GetInfo () const;
//! \brief File import implementation.
void InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler);
void importMaterials();
void importTextures();
void importMeshes();
void importBones(unsigned int parentid, aiNode *pParent);
void importAnimations();
// helper functions
aiColor4D mkColor(uint32_t c);
void convertPose(aiMatrix4x4 *m, unsigned int posid, unsigned int orientid);
aiNode *findNode(aiNode *pNode, aiString name);
void calculateOffsetMatrix(aiNode *pNode, aiMatrix4x4 *m);
void populateMesh(aiMesh *pMesh, std::vector<aiFace> *faces, std::vector<aiVector3D> *verteces,
std::vector<aiVector3D> *normals, std::vector<aiVector3D> *texcoords, std::vector<aiColor4D> *colors,
std::vector<unsigned int> *vertexids);
};
} // Namespace Assimp
#endif // ASSIMP_BUILD_NO_M3D_IMPORTER
#endif // AI_M3DIMPORTER_H_INC

View File

@ -0,0 +1,106 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2019 bzt
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.
----------------------------------------------------------------------
*/
/** @file M3DMaterials.h
* @brief Declares the Assimp and Model 3D file material type relations
*/
#ifndef AI_M3DMATERIALS_H_INC
#define AI_M3DMATERIALS_H_INC
/*
* In the m3d.h header, there's a static array which defines the material
* properties, called m3d_propertytypes. These must have the same size, and
* list the matching Assimp materials for those properties. Used by both the
* M3DImporter and the M3DExporter, so you have to define these relations
* only once. D.R.Y. and K.I.S.S.
*/
typedef struct {
const char *pKey;
unsigned int type;
unsigned int index;
} aiMatProp;
/* --- Scalar Properties --- !!!!! must match m3d_propertytypes !!!!! */
static const aiMatProp aiProps[] = {
{ AI_MATKEY_COLOR_DIFFUSE }, /* m3dp_Kd */
{ AI_MATKEY_COLOR_AMBIENT }, /* m3dp_Ka */
{ AI_MATKEY_COLOR_SPECULAR }, /* m3dp_Ks */
{ AI_MATKEY_SHININESS }, /* m3dp_Ns */
{ AI_MATKEY_COLOR_EMISSIVE }, /* m3dp_Ke */
{ AI_MATKEY_COLOR_REFLECTIVE }, /* m3dp_Tf */
{ AI_MATKEY_BUMPSCALING }, /* m3dp_Km */
{ AI_MATKEY_OPACITY }, /* m3dp_d */
{ AI_MATKEY_SHADING_MODEL }, /* m3dp_il */
{ NULL, 0, 0 }, /* m3dp_Pr */
{ AI_MATKEY_REFLECTIVITY }, /* m3dp_Pm */
{ NULL, 0, 0 }, /* m3dp_Ps */
{ AI_MATKEY_REFRACTI }, /* m3dp_Ni */
{ NULL, 0, 0 }, /* m3dp_Nt */
{ NULL, 0, 0 },
{ NULL, 0, 0 },
{ NULL, 0, 0 }
};
/* --- Texture Map Properties --- !!!!! must match m3d_propertytypes !!!!! */
static const aiMatProp aiTxProps[] = {
{ AI_MATKEY_TEXTURE_DIFFUSE(0) }, /* m3dp_map_Kd */
{ AI_MATKEY_TEXTURE_AMBIENT(0) }, /* m3dp_map_Ka */
{ AI_MATKEY_TEXTURE_SPECULAR(0) }, /* m3dp_map_Ks */
{ AI_MATKEY_TEXTURE_SHININESS(0) }, /* m3dp_map_Ns */
{ AI_MATKEY_TEXTURE_EMISSIVE(0) }, /* m3dp_map_Ke */
{ NULL, 0, 0 }, /* m3dp_map_Tf */
{ AI_MATKEY_TEXTURE_HEIGHT(0) }, /* m3dp_bump */
{ AI_MATKEY_TEXTURE_OPACITY(0) }, /* m3dp_map_d */
{ AI_MATKEY_TEXTURE_REFLECTION(0) }, /* m3dp_refl */
{ AI_MATKEY_TEXTURE(aiTextureType_DIFFUSE_ROUGHNESS,0) },/* m3dp_map_Pr */
{ AI_MATKEY_TEXTURE(aiTextureType_METALNESS,0) }, /* m3dp_map_Pm */
{ NULL, 0, 0 }, /* m3dp_map_Ps */
{ AI_MATKEY_TEXTURE(aiTextureType_AMBIENT_OCCLUSION,0) },/* m3dp_map_Ni */
{ NULL, 0, 0 }, /* m3dp_map_Nt */
{ NULL, 0, 0 },
{ NULL, 0, 0 },
{ NULL, 0, 0 }
};
#endif // AI_M3DMATERIALS_H_INC

5568
code/M3D/m3d.h 100644

File diff suppressed because it is too large Load Diff

View File

@ -52,7 +52,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "MDL/MDLDefaultColorMap.h" #include "MDL/MDLDefaultColorMap.h"
#include "MD2/MD2FileData.h" #include "MD2/MD2FileData.h"
#include <assimp/Macros.h>
#include <assimp/qnan.h> #include <assimp/qnan.h>
#include <assimp/StringUtils.h> #include <assimp/StringUtils.h>
#include <assimp/Importer.hpp> #include <assimp/Importer.hpp>

View File

@ -51,7 +51,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/types.h> #include <assimp/types.h>
#include <assimp/material.h> #include <assimp/material.h>
#include <assimp/DefaultLogger.hpp> #include <assimp/DefaultLogger.hpp>
#include <assimp/Macros.h>
using namespace Assimp; using namespace Assimp;
@ -472,12 +471,12 @@ aiReturn aiMaterial::AddBinaryProperty (const void* pInput,
aiPropertyTypeInfo pType aiPropertyTypeInfo pType
) )
{ {
ai_assert( pInput != NULL ); ai_assert( pInput != nullptr );
ai_assert( pKey != NULL ); ai_assert(pKey != nullptr );
ai_assert( 0 != pSizeInBytes ); ai_assert( 0 != pSizeInBytes );
if ( 0 == pSizeInBytes ) { if ( 0 == pSizeInBytes ) {
return AI_FAILURE;
} }
// first search the list whether there is already an entry with this key // first search the list whether there is already an entry with this key

View File

@ -49,7 +49,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// internal headers // internal headers
#include "PlyLoader.h" #include "PlyLoader.h"
#include <assimp/IOStreamBuffer.h> #include <assimp/IOStreamBuffer.h>
#include <assimp/Macros.h>
#include <memory> #include <memory>
#include <assimp/IOSystem.hpp> #include <assimp/IOSystem.hpp>
#include <assimp/scene.h> #include <assimp/scene.h>

View File

@ -0,0 +1,268 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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 "ArmaturePopulate.h"
#include <assimp/BaseImporter.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/postprocess.h>
#include <assimp/scene.h>
#include <iostream>
namespace Assimp {
/// The default class constructor.
ArmaturePopulate::ArmaturePopulate() : BaseProcess()
{}
/// The class destructor.
ArmaturePopulate::~ArmaturePopulate()
{}
bool ArmaturePopulate::IsActive(unsigned int pFlags) const {
return (pFlags & aiProcess_PopulateArmatureData) != 0;
}
void ArmaturePopulate::SetupProperties(const Importer *pImp) {
// do nothing
}
void ArmaturePopulate::Execute(aiScene *out) {
// Now convert all bone positions to the correct mOffsetMatrix
std::vector<aiBone *> bones;
std::vector<aiNode *> nodes;
std::map<aiBone *, aiNode *> bone_stack;
BuildBoneList(out->mRootNode, out->mRootNode, out, bones);
BuildNodeList(out->mRootNode, nodes);
BuildBoneStack(out->mRootNode, out->mRootNode, out, bones, bone_stack, nodes);
ASSIMP_LOG_DEBUG_F("Bone stack size: ", bone_stack.size());
for (std::pair<aiBone *, aiNode *> kvp : bone_stack) {
aiBone *bone = kvp.first;
aiNode *bone_node = kvp.second;
ASSIMP_LOG_DEBUG_F("active node lookup: ", bone->mName.C_Str());
// lcl transform grab - done in generate_nodes :)
// bone->mOffsetMatrix = bone_node->mTransformation;
aiNode *armature = GetArmatureRoot(bone_node, bones);
ai_assert(armature);
// set up bone armature id
bone->mArmature = armature;
// set this bone node to be referenced properly
ai_assert(bone_node);
bone->mNode = bone_node;
}
}
/* Reprocess all nodes to calculate bone transforms properly based on the REAL
* mOffsetMatrix not the local. */
/* Before this would use mesh transforms which is wrong for bone transforms */
/* Before this would work for simple character skeletons but not complex meshes
* with multiple origins */
/* Source: sketch fab log cutter fbx */
void ArmaturePopulate::BuildBoneList(aiNode *current_node,
const aiNode *root_node,
const aiScene *scene,
std::vector<aiBone *> &bones) {
ai_assert(scene);
for (unsigned int nodeId = 0; nodeId < current_node->mNumChildren; ++nodeId) {
aiNode *child = current_node->mChildren[nodeId];
ai_assert(child);
// check for bones
for (unsigned int meshId = 0; meshId < child->mNumMeshes; ++meshId) {
ai_assert(child->mMeshes);
unsigned int mesh_index = child->mMeshes[meshId];
aiMesh *mesh = scene->mMeshes[mesh_index];
ai_assert(mesh);
for (unsigned int boneId = 0; boneId < mesh->mNumBones; ++boneId) {
aiBone *bone = mesh->mBones[boneId];
ai_assert(bone);
// duplicate meshes exist with the same bones sometimes :)
// so this must be detected
if (std::find(bones.begin(), bones.end(), bone) == bones.end()) {
// add the element once
bones.push_back(bone);
}
}
// find mesh and get bones
// then do recursive lookup for bones in root node hierarchy
}
BuildBoneList(child, root_node, scene, bones);
}
}
/* Prepare flat node list which can be used for non recursive lookups later */
void ArmaturePopulate::BuildNodeList(const aiNode *current_node,
std::vector<aiNode *> &nodes) {
ai_assert(current_node);
for (unsigned int nodeId = 0; nodeId < current_node->mNumChildren; ++nodeId) {
aiNode *child = current_node->mChildren[nodeId];
ai_assert(child);
nodes.push_back(child);
BuildNodeList(child, nodes);
}
}
/* A bone stack allows us to have multiple armatures, with the same bone names
* A bone stack allows us also to retrieve bones true transform even with
* duplicate names :)
*/
void ArmaturePopulate::BuildBoneStack(aiNode *current_node,
const aiNode *root_node,
const aiScene *scene,
const std::vector<aiBone *> &bones,
std::map<aiBone *, aiNode *> &bone_stack,
std::vector<aiNode *> &node_stack) {
ai_assert(scene);
ai_assert(root_node);
ai_assert(!node_stack.empty());
for (aiBone *bone : bones) {
ai_assert(bone);
aiNode *node = GetNodeFromStack(bone->mName, node_stack);
if (node == nullptr) {
node_stack.clear();
BuildNodeList(root_node, node_stack);
ASSIMP_LOG_DEBUG_F("Resetting bone stack: nullptr element ", bone->mName.C_Str());
node = GetNodeFromStack(bone->mName, node_stack);
if (!node) {
ASSIMP_LOG_ERROR("serious import issue node for bone was not detected");
continue;
}
}
ASSIMP_LOG_DEBUG_F("Successfully added bone[", bone->mName.C_Str(), "] to stack and bone node is: ", node->mName.C_Str());
bone_stack.insert(std::pair<aiBone *, aiNode *>(bone, node));
}
}
/* Returns the armature root node */
/* This is required to be detected for a bone initially, it will recurse up
* until it cannot find another bone and return the node No known failure
* points. (yet)
*/
aiNode *ArmaturePopulate::GetArmatureRoot(aiNode *bone_node,
std::vector<aiBone *> &bone_list) {
while (bone_node) {
if (!IsBoneNode(bone_node->mName, bone_list)) {
ASSIMP_LOG_DEBUG_F("GetArmatureRoot() Found valid armature: ", bone_node->mName.C_Str());
return bone_node;
}
bone_node = bone_node->mParent;
}
ASSIMP_LOG_ERROR("GetArmatureRoot() can't find armature!");
return nullptr;
}
/* Simple IsBoneNode check if this could be a bone */
bool ArmaturePopulate::IsBoneNode(const aiString &bone_name,
std::vector<aiBone *> &bones) {
for (aiBone *bone : bones) {
if (bone->mName == bone_name) {
return true;
}
}
return false;
}
/* Pop this node by name from the stack if found */
/* Used in multiple armature situations with duplicate node / bone names */
/* Known flaw: cannot have nodes with bone names, will be fixed in later release
*/
/* (serious to be fixed) Known flaw: nodes which have more than one bone could
* be prematurely dropped from stack */
aiNode *ArmaturePopulate::GetNodeFromStack(const aiString &node_name,
std::vector<aiNode *> &nodes) {
std::vector<aiNode *>::iterator iter;
aiNode *found = nullptr;
for (iter = nodes.begin(); iter < nodes.end(); ++iter) {
aiNode *element = *iter;
ai_assert(element);
// node valid and node name matches
if (element->mName == node_name) {
found = element;
break;
}
}
if (found != nullptr) {
ASSIMP_LOG_INFO_F("Removed node from stack: ", found->mName.C_Str());
// now pop the element from the node list
nodes.erase(iter);
return found;
}
// unique names can cause this problem
ASSIMP_LOG_ERROR("[Serious] GetNodeFromStack() can't find node from stack!");
return nullptr;
}
} // Namespace Assimp

View File

@ -0,0 +1,112 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, 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 ARMATURE_POPULATE_H_
#define ARMATURE_POPULATE_H_
#include "Common/BaseProcess.h"
#include <assimp/BaseImporter.h>
#include <vector>
#include <map>
struct aiNode;
struct aiBone;
namespace Assimp {
// ---------------------------------------------------------------------------
/** Armature Populate: This is a post process designed
* To save you time when importing models into your game engines
* This was originally designed only for fbx but will work with other formats
* it is intended to auto populate aiBone data with armature and the aiNode
* This is very useful when dealing with skinned meshes
* or when dealing with many different skeletons
* It's off by default but recommend that you try it and use it
* It should reduce down any glue code you have in your
* importers
* You can contact RevoluPowered <gordon@gordonite.tech>
* For more info about this
*/
class ASSIMP_API ArmaturePopulate : public BaseProcess {
public:
/// The default class constructor.
ArmaturePopulate();
/// The class destructor.
virtual ~ArmaturePopulate();
/// Overwritten, @see BaseProcess
virtual bool IsActive( unsigned int pFlags ) const;
/// Overwritten, @see BaseProcess
virtual void SetupProperties( const Importer* pImp );
/// Overwritten, @see BaseProcess
virtual void Execute( aiScene* pScene );
static aiNode *GetArmatureRoot(aiNode *bone_node,
std::vector<aiBone *> &bone_list);
static bool IsBoneNode(const aiString &bone_name,
std::vector<aiBone *> &bones);
static aiNode *GetNodeFromStack(const aiString &node_name,
std::vector<aiNode *> &nodes);
static void BuildNodeList(const aiNode *current_node,
std::vector<aiNode *> &nodes);
static void BuildBoneList(aiNode *current_node, const aiNode *root_node,
const aiScene *scene,
std::vector<aiBone *> &bones);
static void BuildBoneStack(aiNode *current_node, const aiNode *root_node,
const aiScene *scene,
const std::vector<aiBone *> &bones,
std::map<aiBone *, aiNode *> &bone_stack,
std::vector<aiNode *> &node_stack);
};
} // Namespace Assimp
#endif // SCALE_PROCESS_H_

View File

@ -52,7 +52,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "FindInvalidDataProcess.h" #include "FindInvalidDataProcess.h"
#include "ProcessHelper.h" #include "ProcessHelper.h"
#include <assimp/Macros.h>
#include <assimp/Exceptional.h> #include <assimp/Exceptional.h>
#include <assimp/qnan.h> #include <assimp/qnan.h>

View File

@ -538,13 +538,17 @@ void ValidateDSProcess::Validate( const aiAnimation* pAnimation)
{ {
Validate(&pAnimation->mName); Validate(&pAnimation->mName);
// validate all materials // validate all animations
if (pAnimation->mNumChannels) if (pAnimation->mNumChannels || pAnimation->mNumMorphMeshChannels)
{ {
if (!pAnimation->mChannels) { if (!pAnimation->mChannels && pAnimation->mNumChannels) {
ReportError("aiAnimation::mChannels is NULL (aiAnimation::mNumChannels is %i)", ReportError("aiAnimation::mChannels is NULL (aiAnimation::mNumChannels is %i)",
pAnimation->mNumChannels); pAnimation->mNumChannels);
} }
if (!pAnimation->mMorphMeshChannels && pAnimation->mNumMorphMeshChannels) {
ReportError("aiAnimation::mMorphMeshChannels is NULL (aiAnimation::mNumMorphMeshChannels is %i)",
pAnimation->mNumMorphMeshChannels);
}
for (unsigned int i = 0; i < pAnimation->mNumChannels;++i) for (unsigned int i = 0; i < pAnimation->mNumChannels;++i)
{ {
if (!pAnimation->mChannels[i]) if (!pAnimation->mChannels[i])
@ -554,6 +558,15 @@ void ValidateDSProcess::Validate( const aiAnimation* pAnimation)
} }
Validate(pAnimation, pAnimation->mChannels[i]); Validate(pAnimation, pAnimation->mChannels[i]);
} }
for (unsigned int i = 0; i < pAnimation->mNumMorphMeshChannels;++i)
{
if (!pAnimation->mMorphMeshChannels[i])
{
ReportError("aiAnimation::mMorphMeshChannels[%i] is NULL (aiAnimation::mNumMorphMeshChannels is %i)",
i, pAnimation->mNumMorphMeshChannels);
}
Validate(pAnimation, pAnimation->mMorphMeshChannels[i]);
}
} }
else { else {
ReportError("aiAnimation::mNumChannels is 0. At least one node animation channel must be there."); ReportError("aiAnimation::mNumChannels is 0. At least one node animation channel must be there.");
@ -590,15 +603,18 @@ void ValidateDSProcess::SearchForInvalidTextures(const aiMaterial* pMaterial,
ReportError("%s #%i is set, but there are only %i %s textures", ReportError("%s #%i is set, but there are only %i %s textures",
szType,iIndex,iNumIndices,szType); szType,iIndex,iNumIndices,szType);
} }
if (!iNumIndices)return; if (!iNumIndices) {
return;
}
std::vector<aiTextureMapping> mappings(iNumIndices); std::vector<aiTextureMapping> mappings(iNumIndices);
// Now check whether all UV indices are valid ... // Now check whether all UV indices are valid ...
bool bNoSpecified = true; bool bNoSpecified = true;
for (unsigned int i = 0; i < pMaterial->mNumProperties;++i) for (unsigned int i = 0; i < pMaterial->mNumProperties;++i) {
{
aiMaterialProperty* prop = pMaterial->mProperties[i]; aiMaterialProperty* prop = pMaterial->mProperties[i];
if (prop->mSemantic != type)continue; if (prop->mSemantic != type) {
continue;
}
if ((int)prop->mIndex >= iNumIndices) if ((int)prop->mIndex >= iNumIndices)
{ {
@ -621,7 +637,7 @@ void ValidateDSProcess::SearchForInvalidTextures(const aiMaterial* pMaterial,
ReportError("Material property %s%i is expected to be 5 floats large (size is %i)", ReportError("Material property %s%i is expected to be 5 floats large (size is %i)",
prop->mKey.data,prop->mIndex, prop->mDataLength); prop->mKey.data,prop->mIndex, prop->mDataLength);
} }
mappings[prop->mIndex] = *((aiTextureMapping*)prop->mData); //mappings[prop->mIndex] = ((aiUVTransform*)prop->mData);
} }
else if (!::strcmp(prop->mKey.data,"$tex.uvwsrc")) { else if (!::strcmp(prop->mKey.data,"$tex.uvwsrc")) {
if (aiPTI_Integer != prop->mType || sizeof(int) > prop->mDataLength) if (aiPTI_Integer != prop->mType || sizeof(int) > prop->mDataLength)
@ -903,6 +919,48 @@ void ValidateDSProcess::Validate( const aiAnimation* pAnimation,
} }
} }
void ValidateDSProcess::Validate( const aiAnimation* pAnimation,
const aiMeshMorphAnim* pMeshMorphAnim)
{
Validate(&pMeshMorphAnim->mName);
if (!pMeshMorphAnim->mNumKeys) {
ReportError("Empty mesh morph animation channel");
}
// otherwise check whether one of the keys exceeds the total duration of the animation
if (pMeshMorphAnim->mNumKeys)
{
if (!pMeshMorphAnim->mKeys)
{
ReportError("aiMeshMorphAnim::mKeys is NULL (aiMeshMorphAnim::mNumKeys is %i)",
pMeshMorphAnim->mNumKeys);
}
double dLast = -10e10;
for (unsigned int i = 0; i < pMeshMorphAnim->mNumKeys;++i)
{
// ScenePreprocessor will compute the duration if still the default value
// (Aramis) Add small epsilon, comparison tended to fail if max_time == duration,
// seems to be due the compilers register usage/width.
if (pAnimation->mDuration > 0. && pMeshMorphAnim->mKeys[i].mTime > pAnimation->mDuration+0.001)
{
ReportError("aiMeshMorphAnim::mKeys[%i].mTime (%.5f) is larger "
"than aiAnimation::mDuration (which is %.5f)",i,
(float)pMeshMorphAnim->mKeys[i].mTime,
(float)pAnimation->mDuration);
}
if (i && pMeshMorphAnim->mKeys[i].mTime <= dLast)
{
ReportWarning("aiMeshMorphAnim::mKeys[%i].mTime (%.5f) is smaller "
"than aiMeshMorphAnim::mKeys[%i] (which is %.5f)",i,
(float)pMeshMorphAnim->mKeys[i].mTime,
i-1, (float)dLast);
}
dLast = pMeshMorphAnim->mKeys[i].mTime;
}
}
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void ValidateDSProcess::Validate( const aiNode* pNode) void ValidateDSProcess::Validate( const aiNode* pNode)
{ {

View File

@ -55,6 +55,7 @@ struct aiBone;
struct aiMesh; struct aiMesh;
struct aiAnimation; struct aiAnimation;
struct aiNodeAnim; struct aiNodeAnim;
struct aiMeshMorphAnim;
struct aiTexture; struct aiTexture;
struct aiMaterial; struct aiMaterial;
struct aiNode; struct aiNode;
@ -150,6 +151,13 @@ protected:
void Validate( const aiAnimation* pAnimation, void Validate( const aiAnimation* pAnimation,
const aiNodeAnim* pBoneAnim); const aiNodeAnim* pBoneAnim);
/** Validates a mesh morph animation channel.
* @param pAnimation Input animation.
* @param pMeshMorphAnim Mesh morph animation channel.
* */
void Validate( const aiAnimation* pAnimation,
const aiMeshMorphAnim* pMeshMorphAnim);
// ------------------------------------------------------------------- // -------------------------------------------------------------------
/** Validates a node and all of its subnodes /** Validates a node and all of its subnodes
* @param Node Input node*/ * @param Node Input node*/

View File

@ -1427,9 +1427,6 @@ inline void Asset::ReadExtensionsUsed(Document& doc)
} }
} }
#define CHECK_EXT(EXT) \
if (exts.find(#EXT) != exts.end()) extensionsUsed.EXT = true;
CHECK_EXT(KHR_binary_glTF); CHECK_EXT(KHR_binary_glTF);
CHECK_EXT(KHR_materials_common); CHECK_EXT(KHR_materials_common);

View File

@ -228,18 +228,15 @@ namespace glTFCommon {
inline inline
uint8_t DecodeCharBase64(char c) { uint8_t DecodeCharBase64(char c) {
return DATA<true>::tableDecodeBase64[size_t(c)]; // TODO faster with lookup table or ifs? return DATA<true>::tableDecodeBase64[size_t(c)]; // TODO faster with lookup table or ifs?
/*if (c >= 'A' && c <= 'Z') return c - 'A';
if (c >= 'a' && c <= 'z') return c - 'a' + 26;
if (c >= '0' && c <= '9') return c - '0' + 52;
if (c == '+') return 62;
if (c == '/') return 63;
return 64; // '-' */
} }
size_t DecodeBase64(const char* in, size_t inLength, uint8_t*& out); size_t DecodeBase64(const char* in, size_t inLength, uint8_t*& out);
void EncodeBase64(const uint8_t* in, size_t inLength, std::string& out); void EncodeBase64(const uint8_t* in, size_t inLength, std::string& out);
} } // namespace Util
#define CHECK_EXT(EXT) \
if (exts.find(#EXT) != exts.end()) extensionsUsed.EXT = true;
} }

View File

@ -170,6 +170,8 @@ void glTFImporter::ImportMaterials(glTF::Asset& r) {
if (mScene->mNumMaterials == 0) { if (mScene->mNumMaterials == 0) {
mScene->mNumMaterials = 1; mScene->mNumMaterials = 1;
// Delete the array of length zero created above.
delete[] mScene->mMaterials;
mScene->mMaterials = new aiMaterial*[1]; mScene->mMaterials = new aiMaterial*[1];
mScene->mMaterials[0] = new aiMaterial(); mScene->mMaterials[0] = new aiMaterial();
} }
@ -330,6 +332,10 @@ void glTFImporter::ImportMeshes(glTF::Asset& r)
case PrimitiveMode_LINES: { case PrimitiveMode_LINES: {
nFaces = count / 2; nFaces = count / 2;
if (nFaces * 2 != count) {
ASSIMP_LOG_WARN("The number of vertices was not compatible with the LINES mode. Some vertices were dropped.");
count = nFaces * 2;
}
faces = new aiFace[nFaces]; faces = new aiFace[nFaces];
for (unsigned int i = 0; i < count; i += 2) { for (unsigned int i = 0; i < count; i += 2) {
SetFace(faces[i / 2], data.GetUInt(i), data.GetUInt(i + 1)); SetFace(faces[i / 2], data.GetUInt(i), data.GetUInt(i + 1));
@ -353,6 +359,10 @@ void glTFImporter::ImportMeshes(glTF::Asset& r)
case PrimitiveMode_TRIANGLES: { case PrimitiveMode_TRIANGLES: {
nFaces = count / 3; nFaces = count / 3;
if (nFaces * 3 != count) {
ASSIMP_LOG_WARN("The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped.");
count = nFaces * 3;
}
faces = new aiFace[nFaces]; faces = new aiFace[nFaces];
for (unsigned int i = 0; i < count; i += 3) { for (unsigned int i = 0; i < count; i += 3) {
SetFace(faces[i / 3], data.GetUInt(i), data.GetUInt(i + 1), data.GetUInt(i + 2)); SetFace(faces[i / 3], data.GetUInt(i), data.GetUInt(i + 1), data.GetUInt(i + 2));
@ -395,6 +405,10 @@ void glTFImporter::ImportMeshes(glTF::Asset& r)
case PrimitiveMode_LINES: { case PrimitiveMode_LINES: {
nFaces = count / 2; nFaces = count / 2;
if (nFaces * 2 != count) {
ASSIMP_LOG_WARN("The number of vertices was not compatible with the LINES mode. Some vertices were dropped.");
count = nFaces * 2;
}
faces = new aiFace[nFaces]; faces = new aiFace[nFaces];
for (unsigned int i = 0; i < count; i += 2) { for (unsigned int i = 0; i < count; i += 2) {
SetFace(faces[i / 2], i, i + 1); SetFace(faces[i / 2], i, i + 1);
@ -418,6 +432,10 @@ void glTFImporter::ImportMeshes(glTF::Asset& r)
case PrimitiveMode_TRIANGLES: { case PrimitiveMode_TRIANGLES: {
nFaces = count / 3; nFaces = count / 3;
if (nFaces * 3 != count) {
ASSIMP_LOG_WARN("The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped.");
count = nFaces * 3;
}
faces = new aiFace[nFaces]; faces = new aiFace[nFaces];
for (unsigned int i = 0; i < count; i += 3) { for (unsigned int i = 0; i < count; i += 3) {
SetFace(faces[i / 3], i, i + 1, i + 2); SetFace(faces[i / 3], i, i + 1, i + 2);

View File

@ -685,6 +685,13 @@ namespace glTF2
Ref<Texture> texture; Ref<Texture> texture;
unsigned int index; unsigned int index;
unsigned int texCoord = 0; unsigned int texCoord = 0;
bool textureTransformSupported = false;
struct TextureTransformExt {
float offset[2];
float rotation;
float scale[2];
} TextureTransformExt_t;
}; };
struct NormalTextureInfo : TextureInfo struct NormalTextureInfo : TextureInfo
@ -1024,7 +1031,7 @@ namespace glTF2
bool KHR_materials_pbrSpecularGlossiness; bool KHR_materials_pbrSpecularGlossiness;
bool KHR_materials_unlit; bool KHR_materials_unlit;
bool KHR_lights_punctual; bool KHR_lights_punctual;
bool KHR_texture_transform;
} extensionsUsed; } extensionsUsed;
AssetMetadata asset; AssetMetadata asset;

View File

@ -800,8 +800,34 @@ inline void Texture::Read(Value& obj, Asset& r)
} }
namespace { namespace {
inline void SetTextureProperties(Asset& r, Value* prop, TextureInfo& out) inline void SetTextureProperties(Asset& r, Value* prop, TextureInfo& out) {
{ if (r.extensionsUsed.KHR_texture_transform) {
if (Value *extensions = FindObject(*prop, "extensions")) {
out.textureTransformSupported = true;
if (Value *pKHR_texture_transform = FindObject(*extensions, "KHR_texture_transform")) {
if (Value *array = FindArray(*pKHR_texture_transform, "offset")) {
out.TextureTransformExt_t.offset[0] = (*array)[0].GetFloat();
out.TextureTransformExt_t.offset[1] = (*array)[1].GetFloat();
} else {
out.TextureTransformExt_t.offset[0] = 0;
out.TextureTransformExt_t.offset[1] = 0;
}
if (!ReadMember(*pKHR_texture_transform, "rotation", out.TextureTransformExt_t.rotation)) {
out.TextureTransformExt_t.rotation = 0;
}
if (Value *array = FindArray(*pKHR_texture_transform, "scale")) {
out.TextureTransformExt_t.scale[0] = (*array)[0].GetFloat();
out.TextureTransformExt_t.scale[1] = (*array)[1].GetFloat();
} else {
out.TextureTransformExt_t.scale[0] = 1;
out.TextureTransformExt_t.scale[1] = 1;
}
}
}
}
if (Value* index = FindUInt(*prop, "index")) { if (Value* index = FindUInt(*prop, "index")) {
out.texture = r.textures.Retrieve(index->GetUint()); out.texture = r.textures.Retrieve(index->GetUint());
} }
@ -877,6 +903,9 @@ inline void Material::Read(Value& material, Asset& r)
} }
} }
if (r.extensionsUsed.KHR_texture_transform) {
}
unlit = nullptr != FindObject(*extensions, "KHR_materials_unlit"); unlit = nullptr != FindObject(*extensions, "KHR_materials_unlit");
} }
} }
@ -1463,12 +1492,10 @@ inline void Asset::ReadExtensionsUsed(Document& doc)
} }
} }
#define CHECK_EXT(EXT) \
if (exts.find(#EXT) != exts.end()) extensionsUsed.EXT = true;
CHECK_EXT(KHR_materials_pbrSpecularGlossiness); CHECK_EXT(KHR_materials_pbrSpecularGlossiness);
CHECK_EXT(KHR_materials_unlit); CHECK_EXT(KHR_materials_unlit);
CHECK_EXT(KHR_lights_punctual); CHECK_EXT(KHR_lights_punctual);
CHECK_EXT(KHR_texture_transform);
#undef CHECK_EXT #undef CHECK_EXT
} }

View File

@ -375,7 +375,7 @@ namespace glTF2 {
WriteVec(pbrSpecularGlossiness, pbrSG.specularFactor, "specularFactor", defaultSpecularFactor, w.mAl); WriteVec(pbrSpecularGlossiness, pbrSG.specularFactor, "specularFactor", defaultSpecularFactor, w.mAl);
if (pbrSG.glossinessFactor != 1) { if (pbrSG.glossinessFactor != 1) {
WriteFloat(obj, pbrSG.glossinessFactor, "glossinessFactor", w.mAl); WriteFloat(pbrSpecularGlossiness, pbrSG.glossinessFactor, "glossinessFactor", w.mAl);
} }
WriteTex(pbrSpecularGlossiness, pbrSG.diffuseTexture, "diffuseTexture", w.mAl); WriteTex(pbrSpecularGlossiness, pbrSG.diffuseTexture, "diffuseTexture", w.mAl);

View File

@ -350,7 +350,9 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref<Texture>& texture, aiTe
if (path[0] == '*') { // embedded if (path[0] == '*') { // embedded
aiTexture* tex = mScene->mTextures[atoi(&path[1])]; aiTexture* tex = mScene->mTextures[atoi(&path[1])];
uint8_t* data = reinterpret_cast<uint8_t*>(tex->pcData); // copy data since lifetime control is handed over to the asset
uint8_t* data = new uint8_t[tex->mWidth];
memcpy(data, tex->pcData, tex->mWidth);
texture->source->SetData(data, tex->mWidth, *mAsset); texture->source->SetData(data, tex->mWidth, *mAsset);
if (tex->achFormatHint[0]) { if (tex->achFormatHint[0]) {

View File

@ -74,6 +74,7 @@ namespace glTF2
struct Texture; struct Texture;
// Vec/matrix types, as raw float arrays // Vec/matrix types, as raw float arrays
typedef float (vec2)[2];
typedef float (vec3)[3]; typedef float (vec3)[3];
typedef float (vec4)[4]; typedef float (vec4)[4];
} }

View File

@ -43,18 +43,18 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef ASSIMP_BUILD_NO_GLTF_IMPORTER #ifndef ASSIMP_BUILD_NO_GLTF_IMPORTER
#include "glTF2/glTF2Importer.h" #include "glTF2/glTF2Importer.h"
#include "PostProcessing/MakeVerboseFormat.h"
#include "glTF2/glTF2Asset.h" #include "glTF2/glTF2Asset.h"
#include "glTF2/glTF2AssetWriter.h" #include "glTF2/glTF2AssetWriter.h"
#include "PostProcessing/MakeVerboseFormat.h"
#include <assimp/CreateAnimMesh.h>
#include <assimp/StringComparison.h> #include <assimp/StringComparison.h>
#include <assimp/StringUtils.h> #include <assimp/StringUtils.h>
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/ai_assert.h> #include <assimp/ai_assert.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/importerdesc.h> #include <assimp/importerdesc.h>
#include <assimp/CreateAnimMesh.h> #include <assimp/scene.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/Importer.hpp>
#include <memory> #include <memory>
#include <unordered_map> #include <unordered_map>
@ -67,11 +67,11 @@ using namespace glTF2;
using namespace glTFCommon; using namespace glTFCommon;
namespace { namespace {
// generate bi-tangents from normals and tangents according to spec // generate bi-tangents from normals and tangents according to spec
struct Tangent { struct Tangent {
aiVector3D xyz; aiVector3D xyz;
ai_real w; ai_real w;
}; };
} // namespace } // namespace
// //
@ -91,11 +91,11 @@ static const aiImporterDesc desc = {
"gltf glb" "gltf glb"
}; };
glTF2Importer::glTF2Importer() glTF2Importer::glTF2Importer() :
: BaseImporter() BaseImporter(),
, meshOffsets() meshOffsets(),
, embeddedTexIdxs() embeddedTexIdxs(),
, mScene( NULL ) { mScene(NULL) {
// empty // empty
} }
@ -103,13 +103,11 @@ glTF2Importer::~glTF2Importer() {
// empty // empty
} }
const aiImporterDesc* glTF2Importer::GetInfo() const const aiImporterDesc *glTF2Importer::GetInfo() const {
{
return &desc; return &desc;
} }
bool glTF2Importer::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool /* checkSig */) const bool glTF2Importer::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /* checkSig */) const {
{
const std::string &extension = GetExtension(pFile); const std::string &extension = GetExtension(pFile);
if (extension != "gltf" && extension != "glb") if (extension != "gltf" && extension != "glb")
@ -125,8 +123,7 @@ bool glTF2Importer::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool
return false; return false;
} }
static aiTextureMapMode ConvertWrappingMode(SamplerWrap gltfWrapMode) static aiTextureMapMode ConvertWrappingMode(SamplerWrap gltfWrapMode) {
{
switch (gltfWrapMode) { switch (gltfWrapMode) {
case SamplerWrap::Mirrored_Repeat: case SamplerWrap::Mirrored_Repeat:
return aiTextureMapMode_Mirror; return aiTextureMapMode_Mirror;
@ -180,22 +177,19 @@ static void CopyValue(const glTF2::vec4& v, aiQuaternion& out)
o.a4 = v[12]; o.b4 = v[13]; o.c4 = v[14]; o.d4 = v[15]; o.a4 = v[12]; o.b4 = v[13]; o.c4 = v[14]; o.d4 = v[15];
}*/ }*/
inline void SetMaterialColorProperty(Asset& /*r*/, vec4& prop, aiMaterial* mat, const char* pKey, unsigned int type, unsigned int idx) inline void SetMaterialColorProperty(Asset & /*r*/, vec4 &prop, aiMaterial *mat, const char *pKey, unsigned int type, unsigned int idx) {
{
aiColor4D col; aiColor4D col;
CopyValue(prop, col); CopyValue(prop, col);
mat->AddProperty(&col, 1, pKey, type, idx); mat->AddProperty(&col, 1, pKey, type, idx);
} }
inline void SetMaterialColorProperty(Asset& /*r*/, vec3& prop, aiMaterial* mat, const char* pKey, unsigned int type, unsigned int idx) inline void SetMaterialColorProperty(Asset & /*r*/, vec3 &prop, aiMaterial *mat, const char *pKey, unsigned int type, unsigned int idx) {
{
aiColor4D col; aiColor4D col;
glTFCommon::CopyValue(prop, col); glTFCommon::CopyValue(prop, col);
mat->AddProperty(&col, 1, pKey, type, idx); mat->AddProperty(&col, 1, pKey, type, idx);
} }
inline void SetMaterialTextureProperty(std::vector<int>& embeddedTexIdxs, Asset& /*r*/, glTF2::TextureInfo prop, aiMaterial* mat, aiTextureType texType, unsigned int texSlot = 0) inline void SetMaterialTextureProperty(std::vector<int> &embeddedTexIdxs, Asset & /*r*/, glTF2::TextureInfo prop, aiMaterial *mat, aiTextureType texType, unsigned int texSlot = 0) {
{
if (prop.texture && prop.texture->source) { if (prop.texture && prop.texture->source) {
aiString uri(prop.texture->source->uri); aiString uri(prop.texture->source->uri);
@ -207,7 +201,16 @@ inline void SetMaterialTextureProperty(std::vector<int>& embeddedTexIdxs, Asset&
} }
mat->AddProperty(&uri, AI_MATKEY_TEXTURE(texType, texSlot)); mat->AddProperty(&uri, AI_MATKEY_TEXTURE(texType, texSlot));
mat->AddProperty(&prop.texCoord, 1, _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE, texType, texSlot);
if (prop.textureTransformSupported) {
aiUVTransform transform;
transform.mTranslation.x = prop.TextureTransformExt_t.offset[0];
transform.mTranslation.y = prop.TextureTransformExt_t.offset[0];
transform.mRotation = prop.TextureTransformExt_t.rotation;
transform.mScaling.x = prop.TextureTransformExt_t.scale[0];
transform.mScaling.y = prop.TextureTransformExt_t.scale[1];
mat->AddProperty(&transform, 1, _AI_MATKEY_UVTRANSFORM_BASE, texType, texSlot);
}
if (prop.texture->sampler) { if (prop.texture->sampler) {
Ref<Sampler> sampler = prop.texture->sampler; Ref<Sampler> sampler = prop.texture->sampler;
@ -234,27 +237,24 @@ inline void SetMaterialTextureProperty(std::vector<int>& embeddedTexIdxs, Asset&
} }
} }
inline void SetMaterialTextureProperty(std::vector<int>& embeddedTexIdxs, Asset& r, glTF2::NormalTextureInfo& prop, aiMaterial* mat, aiTextureType texType, unsigned int texSlot = 0) inline void SetMaterialTextureProperty(std::vector<int> &embeddedTexIdxs, Asset &r, glTF2::NormalTextureInfo &prop, aiMaterial *mat, aiTextureType texType, unsigned int texSlot = 0) {
{ SetMaterialTextureProperty(embeddedTexIdxs, r, (glTF2::TextureInfo)prop, mat, texType, texSlot);
SetMaterialTextureProperty( embeddedTexIdxs, r, (glTF2::TextureInfo) prop, mat, texType, texSlot );
if (prop.texture && prop.texture->source) { if (prop.texture && prop.texture->source) {
mat->AddProperty(&prop.scale, 1, AI_MATKEY_GLTF_TEXTURE_SCALE(texType, texSlot)); mat->AddProperty(&prop.scale, 1, AI_MATKEY_GLTF_TEXTURE_SCALE(texType, texSlot));
} }
} }
inline void SetMaterialTextureProperty(std::vector<int>& embeddedTexIdxs, Asset& r, glTF2::OcclusionTextureInfo& prop, aiMaterial* mat, aiTextureType texType, unsigned int texSlot = 0) inline void SetMaterialTextureProperty(std::vector<int> &embeddedTexIdxs, Asset &r, glTF2::OcclusionTextureInfo &prop, aiMaterial *mat, aiTextureType texType, unsigned int texSlot = 0) {
{ SetMaterialTextureProperty(embeddedTexIdxs, r, (glTF2::TextureInfo)prop, mat, texType, texSlot);
SetMaterialTextureProperty( embeddedTexIdxs, r, (glTF2::TextureInfo) prop, mat, texType, texSlot );
if (prop.texture && prop.texture->source) { if (prop.texture && prop.texture->source) {
mat->AddProperty(&prop.strength, 1, AI_MATKEY_GLTF_TEXTURE_STRENGTH(texType, texSlot)); mat->AddProperty(&prop.strength, 1, AI_MATKEY_GLTF_TEXTURE_STRENGTH(texType, texSlot));
} }
} }
static aiMaterial* ImportMaterial(std::vector<int>& embeddedTexIdxs, Asset& r, Material& mat) static aiMaterial *ImportMaterial(std::vector<int> &embeddedTexIdxs, Asset &r, Material &mat) {
{ aiMaterial *aimat = new aiMaterial();
aiMaterial* aimat = new aiMaterial();
if (!mat.name.empty()) { if (!mat.name.empty()) {
aiString str(mat.name); aiString str(mat.name);
@ -311,13 +311,12 @@ static aiMaterial* ImportMaterial(std::vector<int>& embeddedTexIdxs, Asset& r, M
return aimat; return aimat;
} }
void glTF2Importer::ImportMaterials(glTF2::Asset& r) void glTF2Importer::ImportMaterials(glTF2::Asset &r) {
{
const unsigned int numImportedMaterials = unsigned(r.materials.Size()); const unsigned int numImportedMaterials = unsigned(r.materials.Size());
Material defaultMaterial; Material defaultMaterial;
mScene->mNumMaterials = numImportedMaterials + 1; mScene->mNumMaterials = numImportedMaterials + 1;
mScene->mMaterials = new aiMaterial*[mScene->mNumMaterials]; mScene->mMaterials = new aiMaterial *[mScene->mNumMaterials];
mScene->mMaterials[numImportedMaterials] = ImportMaterial(embeddedTexIdxs, r, defaultMaterial); mScene->mMaterials[numImportedMaterials] = ImportMaterial(embeddedTexIdxs, r, defaultMaterial);
for (unsigned int i = 0; i < numImportedMaterials; ++i) { for (unsigned int i = 0; i < numImportedMaterials; ++i) {
@ -325,24 +324,20 @@ void glTF2Importer::ImportMaterials(glTF2::Asset& r)
} }
} }
static inline void SetFace(aiFace &face, int a) {
static inline void SetFace(aiFace& face, int a)
{
face.mNumIndices = 1; face.mNumIndices = 1;
face.mIndices = new unsigned int[1]; face.mIndices = new unsigned int[1];
face.mIndices[0] = a; face.mIndices[0] = a;
} }
static inline void SetFace(aiFace& face, int a, int b) static inline void SetFace(aiFace &face, int a, int b) {
{
face.mNumIndices = 2; face.mNumIndices = 2;
face.mIndices = new unsigned int[2]; face.mIndices = new unsigned int[2];
face.mIndices[0] = a; face.mIndices[0] = a;
face.mIndices[1] = b; face.mIndices[1] = b;
} }
static inline void SetFace(aiFace& face, int a, int b, int c) static inline void SetFace(aiFace &face, int a, int b, int c) {
{
face.mNumIndices = 3; face.mNumIndices = 3;
face.mIndices = new unsigned int[3]; face.mIndices = new unsigned int[3];
face.mIndices[0] = a; face.mIndices[0] = a;
@ -351,8 +346,7 @@ static inline void SetFace(aiFace& face, int a, int b, int c)
} }
#ifdef ASSIMP_BUILD_DEBUG #ifdef ASSIMP_BUILD_DEBUG
static inline bool CheckValidFacesIndices(aiFace* faces, unsigned nFaces, unsigned nVerts) static inline bool CheckValidFacesIndices(aiFace *faces, unsigned nFaces, unsigned nVerts) {
{
for (unsigned i = 0; i < nFaces; ++i) { for (unsigned i = 0; i < nFaces; ++i) {
for (unsigned j = 0; j < faces[i].mNumIndices; ++j) { for (unsigned j = 0; j < faces[i].mNumIndices; ++j) {
unsigned idx = faces[i].mIndices[j]; unsigned idx = faces[i].mIndices[j];
@ -364,28 +358,27 @@ static inline bool CheckValidFacesIndices(aiFace* faces, unsigned nFaces, unsign
} }
#endif // ASSIMP_BUILD_DEBUG #endif // ASSIMP_BUILD_DEBUG
void glTF2Importer::ImportMeshes(glTF2::Asset& r) void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
{ std::vector<aiMesh *> meshes;
std::vector<aiMesh*> meshes;
unsigned int k = 0; unsigned int k = 0;
for (unsigned int m = 0; m < r.meshes.Size(); ++m) { for (unsigned int m = 0; m < r.meshes.Size(); ++m) {
Mesh& mesh = r.meshes[m]; Mesh &mesh = r.meshes[m];
meshOffsets.push_back(k); meshOffsets.push_back(k);
k += unsigned(mesh.primitives.size()); k += unsigned(mesh.primitives.size());
for (unsigned int p = 0; p < mesh.primitives.size(); ++p) { for (unsigned int p = 0; p < mesh.primitives.size(); ++p) {
Mesh::Primitive& prim = mesh.primitives[p]; Mesh::Primitive &prim = mesh.primitives[p];
aiMesh* aim = new aiMesh(); aiMesh *aim = new aiMesh();
meshes.push_back(aim); meshes.push_back(aim);
aim->mName = mesh.name.empty() ? mesh.id : mesh.name; aim->mName = mesh.name.empty() ? mesh.id : mesh.name;
if (mesh.primitives.size() > 1) { if (mesh.primitives.size() > 1) {
ai_uint32& len = aim->mName.length; ai_uint32 &len = aim->mName.length;
aim->mName.data[len] = '-'; aim->mName.data[len] = '-';
len += 1 + ASSIMP_itoa10(aim->mName.data + len + 1, unsigned(MAXLEN - len - 1), p); len += 1 + ASSIMP_itoa10(aim->mName.data + len + 1, unsigned(MAXLEN - len - 1), p);
} }
@ -406,10 +399,9 @@ void glTF2Importer::ImportMeshes(glTF2::Asset& r)
case PrimitiveMode_TRIANGLE_FAN: case PrimitiveMode_TRIANGLE_FAN:
aim->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; aim->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
break; break;
} }
Mesh::Primitive::Attributes& attr = prim.attributes; Mesh::Primitive::Attributes &attr = prim.attributes;
if (attr.position.size() > 0 && attr.position[0]) { if (attr.position.size() > 0 && attr.position[0]) {
aim->mNumVertices = static_cast<unsigned int>(attr.position[0]->count); aim->mNumVertices = static_cast<unsigned int>(attr.position[0]->count);
@ -434,7 +426,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset& r)
aim->mBitangents[i] = (aim->mNormals[i] ^ tangents[i].xyz) * tangents[i].w; aim->mBitangents[i] = (aim->mNormals[i] ^ tangents[i].xyz) * tangents[i].w;
} }
delete [] tangents; delete[] tangents;
} }
} }
@ -456,36 +448,36 @@ void glTF2Importer::ImportMeshes(glTF2::Asset& r)
attr.texcoord[tc]->ExtractData(aim->mTextureCoords[tc]); attr.texcoord[tc]->ExtractData(aim->mTextureCoords[tc]);
aim->mNumUVComponents[tc] = attr.texcoord[tc]->GetNumComponents(); aim->mNumUVComponents[tc] = attr.texcoord[tc]->GetNumComponents();
aiVector3D* values = aim->mTextureCoords[tc]; aiVector3D *values = aim->mTextureCoords[tc];
for (unsigned int i = 0; i < aim->mNumVertices; ++i) { for (unsigned int i = 0; i < aim->mNumVertices; ++i) {
values[i].y = 1 - values[i].y; // Flip Y coords values[i].y = 1 - values[i].y; // Flip Y coords
} }
} }
std::vector<Mesh::Primitive::Target>& targets = prim.targets; std::vector<Mesh::Primitive::Target> &targets = prim.targets;
if (targets.size() > 0) { if (targets.size() > 0) {
aim->mNumAnimMeshes = (unsigned int)targets.size(); aim->mNumAnimMeshes = (unsigned int)targets.size();
aim->mAnimMeshes = new aiAnimMesh*[aim->mNumAnimMeshes]; aim->mAnimMeshes = new aiAnimMesh *[aim->mNumAnimMeshes];
for (size_t i = 0; i < targets.size(); i++) { for (size_t i = 0; i < targets.size(); i++) {
aim->mAnimMeshes[i] = aiCreateAnimMesh(aim); aim->mAnimMeshes[i] = aiCreateAnimMesh(aim);
aiAnimMesh& aiAnimMesh = *(aim->mAnimMeshes[i]); aiAnimMesh &aiAnimMesh = *(aim->mAnimMeshes[i]);
Mesh::Primitive::Target& target = targets[i]; Mesh::Primitive::Target &target = targets[i];
if (target.position.size() > 0) { if (target.position.size() > 0) {
aiVector3D *positionDiff = nullptr; aiVector3D *positionDiff = nullptr;
target.position[0]->ExtractData(positionDiff); target.position[0]->ExtractData(positionDiff);
for(unsigned int vertexId = 0; vertexId < aim->mNumVertices; vertexId++) { for (unsigned int vertexId = 0; vertexId < aim->mNumVertices; vertexId++) {
aiAnimMesh.mVertices[vertexId] += positionDiff[vertexId]; aiAnimMesh.mVertices[vertexId] += positionDiff[vertexId];
} }
delete [] positionDiff; delete[] positionDiff;
} }
if (target.normal.size() > 0) { if (target.normal.size() > 0) {
aiVector3D *normalDiff = nullptr; aiVector3D *normalDiff = nullptr;
target.normal[0]->ExtractData(normalDiff); target.normal[0]->ExtractData(normalDiff);
for(unsigned int vertexId = 0; vertexId < aim->mNumVertices; vertexId++) { for (unsigned int vertexId = 0; vertexId < aim->mNumVertices; vertexId++) {
aiAnimMesh.mNormals[vertexId] += normalDiff[vertexId]; aiAnimMesh.mNormals[vertexId] += normalDiff[vertexId];
} }
delete [] normalDiff; delete[] normalDiff;
} }
if (target.tangent.size() > 0) { if (target.tangent.size() > 0) {
Tangent *tangent = nullptr; Tangent *tangent = nullptr;
@ -499,8 +491,8 @@ void glTF2Importer::ImportMeshes(glTF2::Asset& r)
aiAnimMesh.mTangents[vertexId] = tangent[vertexId].xyz; aiAnimMesh.mTangents[vertexId] = tangent[vertexId].xyz;
aiAnimMesh.mBitangents[vertexId] = (aiAnimMesh.mNormals[vertexId] ^ tangent[vertexId].xyz) * tangent[vertexId].w; aiAnimMesh.mBitangents[vertexId] = (aiAnimMesh.mNormals[vertexId] ^ tangent[vertexId].xyz) * tangent[vertexId].w;
} }
delete [] tangent; delete[] tangent;
delete [] tangentDiff; delete[] tangentDiff;
} }
if (mesh.weights.size() > i) { if (mesh.weights.size() > i) {
aiAnimMesh.mWeight = mesh.weights[i]; aiAnimMesh.mWeight = mesh.weights[i];
@ -508,8 +500,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset& r)
} }
} }
aiFace *faces = 0;
aiFace* faces = 0;
size_t nFaces = 0; size_t nFaces = 0;
if (prim.indices) { if (prim.indices) {
@ -530,6 +521,10 @@ void glTF2Importer::ImportMeshes(glTF2::Asset& r)
case PrimitiveMode_LINES: { case PrimitiveMode_LINES: {
nFaces = count / 2; nFaces = count / 2;
if (nFaces * 2 != count) {
ASSIMP_LOG_WARN("The number of vertices was not compatible with the LINES mode. Some vertices were dropped.");
count = nFaces * 2;
}
faces = new aiFace[nFaces]; faces = new aiFace[nFaces];
for (unsigned int i = 0; i < count; i += 2) { for (unsigned int i = 0; i < count; i += 2) {
SetFace(faces[i / 2], data.GetUInt(i), data.GetUInt(i + 1)); SetFace(faces[i / 2], data.GetUInt(i), data.GetUInt(i + 1));
@ -553,6 +548,10 @@ void glTF2Importer::ImportMeshes(glTF2::Asset& r)
case PrimitiveMode_TRIANGLES: { case PrimitiveMode_TRIANGLES: {
nFaces = count / 3; nFaces = count / 3;
if (nFaces * 3 != count) {
ASSIMP_LOG_WARN("The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped.");
count = nFaces * 3;
}
faces = new aiFace[nFaces]; faces = new aiFace[nFaces];
for (unsigned int i = 0; i < count; i += 3) { for (unsigned int i = 0; i < count; i += 3) {
SetFace(faces[i / 3], data.GetUInt(i), data.GetUInt(i + 1), data.GetUInt(i + 2)); SetFace(faces[i / 3], data.GetUInt(i), data.GetUInt(i + 1), data.GetUInt(i + 2));
@ -564,13 +563,10 @@ void glTF2Importer::ImportMeshes(glTF2::Asset& r)
faces = new aiFace[nFaces]; faces = new aiFace[nFaces];
for (unsigned int i = 0; i < nFaces; ++i) { for (unsigned int i = 0; i < nFaces; ++i) {
//The ordering is to ensure that the triangles are all drawn with the same orientation //The ordering is to ensure that the triangles are all drawn with the same orientation
if ((i + 1) % 2 == 0) if ((i + 1) % 2 == 0) {
{
//For even n, vertices n + 1, n, and n + 2 define triangle n //For even n, vertices n + 1, n, and n + 2 define triangle n
SetFace(faces[i], data.GetUInt(i + 1), data.GetUInt(i), data.GetUInt(i + 2)); SetFace(faces[i], data.GetUInt(i + 1), data.GetUInt(i), data.GetUInt(i + 2));
} } else {
else
{
//For odd n, vertices n, n+1, and n+2 define triangle n //For odd n, vertices n, n+1, and n+2 define triangle n
SetFace(faces[i], data.GetUInt(i), data.GetUInt(i + 1), data.GetUInt(i + 2)); SetFace(faces[i], data.GetUInt(i), data.GetUInt(i + 1), data.GetUInt(i + 2));
} }
@ -586,8 +582,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset& r)
} }
break; break;
} }
} } else { // no indices provided so directly generate from counts
else { // no indices provided so directly generate from counts
// use the already determined count as it includes checks // use the already determined count as it includes checks
unsigned int count = aim->mNumVertices; unsigned int count = aim->mNumVertices;
@ -604,6 +599,10 @@ void glTF2Importer::ImportMeshes(glTF2::Asset& r)
case PrimitiveMode_LINES: { case PrimitiveMode_LINES: {
nFaces = count / 2; nFaces = count / 2;
if (nFaces * 2 != count) {
ASSIMP_LOG_WARN("The number of vertices was not compatible with the LINES mode. Some vertices were dropped.");
count = (unsigned int)nFaces * 2;
}
faces = new aiFace[nFaces]; faces = new aiFace[nFaces];
for (unsigned int i = 0; i < count; i += 2) { for (unsigned int i = 0; i < count; i += 2) {
SetFace(faces[i / 2], i, i + 1); SetFace(faces[i / 2], i, i + 1);
@ -627,6 +626,10 @@ void glTF2Importer::ImportMeshes(glTF2::Asset& r)
case PrimitiveMode_TRIANGLES: { case PrimitiveMode_TRIANGLES: {
nFaces = count / 3; nFaces = count / 3;
if (nFaces * 3 != count) {
ASSIMP_LOG_WARN("The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped.");
count = (unsigned int)nFaces * 3;
}
faces = new aiFace[nFaces]; faces = new aiFace[nFaces];
for (unsigned int i = 0; i < count; i += 3) { for (unsigned int i = 0; i < count; i += 3) {
SetFace(faces[i / 3], i, i + 1, i + 2); SetFace(faces[i / 3], i, i + 1, i + 2);
@ -638,15 +641,12 @@ void glTF2Importer::ImportMeshes(glTF2::Asset& r)
faces = new aiFace[nFaces]; faces = new aiFace[nFaces];
for (unsigned int i = 0; i < nFaces; ++i) { for (unsigned int i = 0; i < nFaces; ++i) {
//The ordering is to ensure that the triangles are all drawn with the same orientation //The ordering is to ensure that the triangles are all drawn with the same orientation
if ((i+1) % 2 == 0) if ((i + 1) % 2 == 0) {
{
//For even n, vertices n + 1, n, and n + 2 define triangle n //For even n, vertices n + 1, n, and n + 2 define triangle n
SetFace(faces[i], i+1, i, i+2); SetFace(faces[i], i + 1, i, i + 2);
} } else {
else
{
//For odd n, vertices n, n+1, and n+2 define triangle n //For odd n, vertices n, n+1, and n+2 define triangle n
SetFace(faces[i], i, i+1, i+2); SetFace(faces[i], i, i + 1, i + 2);
} }
} }
break; break;
@ -670,11 +670,9 @@ void glTF2Importer::ImportMeshes(glTF2::Asset& r)
if (prim.material) { if (prim.material) {
aim->mMaterialIndex = prim.material.GetIndex(); aim->mMaterialIndex = prim.material.GetIndex();
} } else {
else {
aim->mMaterialIndex = mScene->mNumMaterials - 1; aim->mMaterialIndex = mScene->mNumMaterials - 1;
} }
} }
} }
@ -683,20 +681,19 @@ void glTF2Importer::ImportMeshes(glTF2::Asset& r)
CopyVector(meshes, mScene->mMeshes, mScene->mNumMeshes); CopyVector(meshes, mScene->mMeshes, mScene->mNumMeshes);
} }
void glTF2Importer::ImportCameras(glTF2::Asset& r) void glTF2Importer::ImportCameras(glTF2::Asset &r) {
{
if (!r.cameras.Size()) return; if (!r.cameras.Size()) return;
mScene->mNumCameras = r.cameras.Size(); mScene->mNumCameras = r.cameras.Size();
mScene->mCameras = new aiCamera*[r.cameras.Size()]; mScene->mCameras = new aiCamera *[r.cameras.Size()];
for (size_t i = 0; i < r.cameras.Size(); ++i) { for (size_t i = 0; i < r.cameras.Size(); ++i) {
Camera& cam = r.cameras[i]; Camera &cam = r.cameras[i];
aiCamera* aicam = mScene->mCameras[i] = new aiCamera(); aiCamera *aicam = mScene->mCameras[i] = new aiCamera();
// cameras point in -Z by default, rest is specified in node transform // cameras point in -Z by default, rest is specified in node transform
aicam->mLookAt = aiVector3D(0.f,0.f,-1.f); aicam->mLookAt = aiVector3D(0.f, 0.f, -1.f);
if (cam.type == Camera::Perspective) { if (cam.type == Camera::Perspective) {
@ -709,38 +706,38 @@ void glTF2Importer::ImportCameras(glTF2::Asset& r)
aicam->mClipPlaneNear = cam.cameraProperties.ortographic.znear; aicam->mClipPlaneNear = cam.cameraProperties.ortographic.znear;
aicam->mHorizontalFOV = 0.0; aicam->mHorizontalFOV = 0.0;
aicam->mAspect = 1.0f; aicam->mAspect = 1.0f;
if (0.f != cam.cameraProperties.ortographic.ymag ) { if (0.f != cam.cameraProperties.ortographic.ymag) {
aicam->mAspect = cam.cameraProperties.ortographic.xmag / cam.cameraProperties.ortographic.ymag; aicam->mAspect = cam.cameraProperties.ortographic.xmag / cam.cameraProperties.ortographic.ymag;
} }
} }
} }
} }
void glTF2Importer::ImportLights(glTF2::Asset& r) void glTF2Importer::ImportLights(glTF2::Asset &r) {
{
if (!r.lights.Size()) if (!r.lights.Size())
return; return;
mScene->mNumLights = r.lights.Size(); mScene->mNumLights = r.lights.Size();
mScene->mLights = new aiLight*[r.lights.Size()]; mScene->mLights = new aiLight *[r.lights.Size()];
for (size_t i = 0; i < r.lights.Size(); ++i) { for (size_t i = 0; i < r.lights.Size(); ++i) {
Light& light = r.lights[i]; Light &light = r.lights[i];
aiLight* ail = mScene->mLights[i] = new aiLight(); aiLight *ail = mScene->mLights[i] = new aiLight();
switch (light.type) switch (light.type) {
{
case Light::Directional: case Light::Directional:
ail->mType = aiLightSource_DIRECTIONAL; break; ail->mType = aiLightSource_DIRECTIONAL;
break;
case Light::Point: case Light::Point:
ail->mType = aiLightSource_POINT; break; ail->mType = aiLightSource_POINT;
break;
case Light::Spot: case Light::Spot:
ail->mType = aiLightSource_SPOT; break; ail->mType = aiLightSource_SPOT;
break;
} }
if (ail->mType != aiLightSource_POINT) if (ail->mType != aiLightSource_POINT) {
{
ail->mDirection = aiVector3D(0.0f, 0.0f, -1.0f); ail->mDirection = aiVector3D(0.0f, 0.0f, -1.0f);
ail->mUp = aiVector3D(0.0f, 1.0f, 0.0f); ail->mUp = aiVector3D(0.0f, 1.0f, 0.0f);
} }
@ -750,14 +747,11 @@ void glTF2Importer::ImportLights(glTF2::Asset& r)
CopyValue(colorWithIntensity, ail->mColorDiffuse); CopyValue(colorWithIntensity, ail->mColorDiffuse);
CopyValue(colorWithIntensity, ail->mColorSpecular); CopyValue(colorWithIntensity, ail->mColorSpecular);
if (ail->mType == aiLightSource_DIRECTIONAL) if (ail->mType == aiLightSource_DIRECTIONAL) {
{
ail->mAttenuationConstant = 1.0; ail->mAttenuationConstant = 1.0;
ail->mAttenuationLinear = 0.0; ail->mAttenuationLinear = 0.0;
ail->mAttenuationQuadratic = 0.0; ail->mAttenuationQuadratic = 0.0;
} } else {
else
{
//in PBR attenuation is calculated using inverse square law which can be expressed //in PBR attenuation is calculated using inverse square law which can be expressed
//using assimps equation: 1/(att0 + att1 * d + att2 * d*d) with the following parameters //using assimps equation: 1/(att0 + att1 * d + att2 * d*d) with the following parameters
//this is correct equation for the case when range (see //this is correct equation for the case when range (see
@ -771,19 +765,17 @@ void glTF2Importer::ImportLights(glTF2::Asset& r)
ail->mAttenuationQuadratic = 1.0; ail->mAttenuationQuadratic = 1.0;
} }
if (ail->mType == aiLightSource_SPOT) if (ail->mType == aiLightSource_SPOT) {
{
ail->mAngleInnerCone = light.innerConeAngle; ail->mAngleInnerCone = light.innerConeAngle;
ail->mAngleOuterCone = light.outerConeAngle; ail->mAngleOuterCone = light.outerConeAngle;
} }
} }
} }
static void GetNodeTransform(aiMatrix4x4& matrix, const glTF2::Node& node) { static void GetNodeTransform(aiMatrix4x4 &matrix, const glTF2::Node &node) {
if (node.matrix.isPresent) { if (node.matrix.isPresent) {
CopyValue(node.matrix.value, matrix); CopyValue(node.matrix.value, matrix);
} } else {
else {
if (node.translation.isPresent) { if (node.translation.isPresent) {
aiVector3D trans; aiVector3D trans;
CopyValue(node.translation.value, trans); CopyValue(node.translation.value, trans);
@ -808,9 +800,8 @@ static void GetNodeTransform(aiMatrix4x4& matrix, const glTF2::Node& node) {
} }
} }
static void BuildVertexWeightMapping(Mesh::Primitive& primitive, std::vector<std::vector<aiVertexWeight>>& map) static void BuildVertexWeightMapping(Mesh::Primitive &primitive, std::vector<std::vector<aiVertexWeight>> &map) {
{ Mesh::Primitive::Attributes &attr = primitive.attributes;
Mesh::Primitive::Attributes& attr = primitive.attributes;
if (attr.weight.empty() || attr.joint.empty()) { if (attr.weight.empty() || attr.joint.empty()) {
return; return;
} }
@ -820,17 +811,23 @@ static void BuildVertexWeightMapping(Mesh::Primitive& primitive, std::vector<std
size_t num_vertices = attr.weight[0]->count; size_t num_vertices = attr.weight[0]->count;
struct Weights { float values[4]; }; struct Weights {
Weights* weights = nullptr; float values[4];
};
Weights *weights = nullptr;
attr.weight[0]->ExtractData(weights); attr.weight[0]->ExtractData(weights);
struct Indices8 { uint8_t values[4]; }; struct Indices8 {
struct Indices16 { uint16_t values[4]; }; uint8_t values[4];
Indices8* indices8 = nullptr; };
Indices16* indices16 = nullptr; struct Indices16 {
uint16_t values[4];
};
Indices8 *indices8 = nullptr;
Indices16 *indices16 = nullptr;
if (attr.joint[0]->GetElementSize() == 4) { if (attr.joint[0]->GetElementSize() == 4) {
attr.joint[0]->ExtractData(indices8); attr.joint[0]->ExtractData(indices8);
}else { } else {
attr.joint[0]->ExtractData(indices16); attr.joint[0]->ExtractData(indices16);
} }
// //
@ -842,7 +839,7 @@ static void BuildVertexWeightMapping(Mesh::Primitive& primitive, std::vector<std
for (size_t i = 0; i < num_vertices; ++i) { for (size_t i = 0; i < num_vertices; ++i) {
for (int j = 0; j < 4; ++j) { for (int j = 0; j < 4; ++j) {
const unsigned int bone = (indices8!=nullptr) ? indices8[i].values[j] : indices16[i].values[j]; const unsigned int bone = (indices8 != nullptr) ? indices8[i].values[j] : indices16[i].values[j];
const float weight = weights[i].values[j]; const float weight = weights[i].values[j];
if (weight > 0 && bone < map.size()) { if (weight > 0 && bone < map.size()) {
map[bone].reserve(8); map[bone].reserve(8);
@ -856,23 +853,21 @@ static void BuildVertexWeightMapping(Mesh::Primitive& primitive, std::vector<std
delete[] indices16; delete[] indices16;
} }
static std::string GetNodeName(const Node& node) static std::string GetNodeName(const Node &node) {
{
return node.name.empty() ? node.id : node.name; return node.name.empty() ? node.id : node.name;
} }
aiNode* ImportNode(aiScene* pScene, glTF2::Asset& r, std::vector<unsigned int>& meshOffsets, glTF2::Ref<glTF2::Node>& ptr) aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector<unsigned int> &meshOffsets, glTF2::Ref<glTF2::Node> &ptr) {
{ Node &node = *ptr;
Node& node = *ptr;
aiNode* ainode = new aiNode(GetNodeName(node)); aiNode *ainode = new aiNode(GetNodeName(node));
if (!node.children.empty()) { if (!node.children.empty()) {
ainode->mNumChildren = unsigned(node.children.size()); ainode->mNumChildren = unsigned(node.children.size());
ainode->mChildren = new aiNode*[ainode->mNumChildren]; ainode->mChildren = new aiNode *[ainode->mNumChildren];
for (unsigned int i = 0; i < ainode->mNumChildren; ++i) { for (unsigned int i = 0; i < ainode->mNumChildren; ++i) {
aiNode* child = ImportNode(pScene, r, meshOffsets, node.children[i]); aiNode *child = ImportNode(pScene, r, meshOffsets, node.children[i]);
child->mParent = ainode; child->mParent = ainode;
ainode->mChildren[i] = child; ainode->mChildren[i] = child;
} }
@ -891,9 +886,9 @@ aiNode* ImportNode(aiScene* pScene, glTF2::Asset& r, std::vector<unsigned int>&
if (node.skin) { if (node.skin) {
for (int primitiveNo = 0; primitiveNo < count; ++primitiveNo) { for (int primitiveNo = 0; primitiveNo < count; ++primitiveNo) {
aiMesh* mesh = pScene->mMeshes[meshOffsets[mesh_idx]+primitiveNo]; aiMesh *mesh = pScene->mMeshes[meshOffsets[mesh_idx] + primitiveNo];
mesh->mNumBones = static_cast<unsigned int>(node.skin->jointNames.size()); mesh->mNumBones = static_cast<unsigned int>(node.skin->jointNames.size());
mesh->mBones = new aiBone*[mesh->mNumBones]; mesh->mBones = new aiBone *[mesh->mNumBones];
// GLTF and Assimp choose to store bone weights differently. // GLTF and Assimp choose to store bone weights differently.
// GLTF has each vertex specify which bones influence the vertex. // GLTF has each vertex specify which bones influence the vertex.
@ -907,11 +902,11 @@ aiNode* ImportNode(aiScene* pScene, glTF2::Asset& r, std::vector<unsigned int>&
std::vector<std::vector<aiVertexWeight>> weighting(mesh->mNumBones); std::vector<std::vector<aiVertexWeight>> weighting(mesh->mNumBones);
BuildVertexWeightMapping(node.meshes[0]->primitives[primitiveNo], weighting); BuildVertexWeightMapping(node.meshes[0]->primitives[primitiveNo], weighting);
mat4* pbindMatrices = nullptr; mat4 *pbindMatrices = nullptr;
node.skin->inverseBindMatrices->ExtractData(pbindMatrices); node.skin->inverseBindMatrices->ExtractData(pbindMatrices);
for (uint32_t i = 0; i < mesh->mNumBones; ++i) { for (uint32_t i = 0; i < mesh->mNumBones; ++i) {
aiBone* bone = new aiBone(); aiBone *bone = new aiBone();
Ref<Node> joint = node.skin->jointNames[i]; Ref<Node> joint = node.skin->jointNames[i];
if (!joint->name.empty()) { if (!joint->name.empty()) {
@ -919,7 +914,7 @@ aiNode* ImportNode(aiScene* pScene, glTF2::Asset& r, std::vector<unsigned int>&
} else { } else {
// Assimp expects each bone to have a unique name. // Assimp expects each bone to have a unique name.
static const std::string kDefaultName = "bone_"; static const std::string kDefaultName = "bone_";
char postfix[10] = {0}; char postfix[10] = { 0 };
ASSIMP_itoa10(postfix, i); ASSIMP_itoa10(postfix, i);
bone->mName = (kDefaultName + postfix); bone->mName = (kDefaultName + postfix);
} }
@ -927,7 +922,7 @@ aiNode* ImportNode(aiScene* pScene, glTF2::Asset& r, std::vector<unsigned int>&
CopyValue(pbindMatrices[i], bone->mOffsetMatrix); CopyValue(pbindMatrices[i], bone->mOffsetMatrix);
std::vector<aiVertexWeight>& weights = weighting[i]; std::vector<aiVertexWeight> &weights = weighting[i];
bone->mNumWeights = static_cast<uint32_t>(weights.size()); bone->mNumWeights = static_cast<uint32_t>(weights.size());
if (bone->mNumWeights > 0) { if (bone->mNumWeights > 0) {
@ -964,8 +959,7 @@ aiNode* ImportNode(aiScene* pScene, glTF2::Asset& r, std::vector<unsigned int>&
//range is optional - see https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual //range is optional - see https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual
//it is added to meta data of parent node, because there is no other place to put it //it is added to meta data of parent node, because there is no other place to put it
if (node.light->range.isPresent) if (node.light->range.isPresent) {
{
ainode->mMetaData = aiMetadata::Alloc(1); ainode->mMetaData = aiMetadata::Alloc(1);
ainode->mMetaData->Set(0, "PBR_LightRange", node.light->range.value); ainode->mMetaData->Set(0, "PBR_LightRange", node.light->range.value);
} }
@ -974,57 +968,52 @@ aiNode* ImportNode(aiScene* pScene, glTF2::Asset& r, std::vector<unsigned int>&
return ainode; return ainode;
} }
void glTF2Importer::ImportNodes(glTF2::Asset& r) void glTF2Importer::ImportNodes(glTF2::Asset &r) {
{
if (!r.scene) return; if (!r.scene) return;
std::vector< Ref<Node> > rootNodes = r.scene->nodes; std::vector<Ref<Node>> rootNodes = r.scene->nodes;
// The root nodes // The root nodes
unsigned int numRootNodes = unsigned(rootNodes.size()); unsigned int numRootNodes = unsigned(rootNodes.size());
if (numRootNodes == 1) { // a single root node: use it if (numRootNodes == 1) { // a single root node: use it
mScene->mRootNode = ImportNode(mScene, r, meshOffsets, rootNodes[0]); mScene->mRootNode = ImportNode(mScene, r, meshOffsets, rootNodes[0]);
} } else if (numRootNodes > 1) { // more than one root node: create a fake root
else if (numRootNodes > 1) { // more than one root node: create a fake root aiNode *root = new aiNode("ROOT");
aiNode* root = new aiNode("ROOT"); root->mChildren = new aiNode *[numRootNodes];
root->mChildren = new aiNode*[numRootNodes];
for (unsigned int i = 0; i < numRootNodes; ++i) { for (unsigned int i = 0; i < numRootNodes; ++i) {
aiNode* node = ImportNode(mScene, r, meshOffsets, rootNodes[i]); aiNode *node = ImportNode(mScene, r, meshOffsets, rootNodes[i]);
node->mParent = root; node->mParent = root;
root->mChildren[root->mNumChildren++] = node; root->mChildren[root->mNumChildren++] = node;
} }
mScene->mRootNode = root; mScene->mRootNode = root;
} }
//if (!mScene->mRootNode) {
// mScene->mRootNode = new aiNode("EMPTY");
//}
} }
struct AnimationSamplers { struct AnimationSamplers {
AnimationSamplers() AnimationSamplers() :
: translation(nullptr) translation(nullptr),
, rotation(nullptr) rotation(nullptr),
, scale(nullptr) { scale(nullptr),
weight(nullptr) {
// empty // empty
} }
Animation::Sampler* translation; Animation::Sampler *translation;
Animation::Sampler* rotation; Animation::Sampler *rotation;
Animation::Sampler* scale; Animation::Sampler *scale;
Animation::Sampler *weight;
}; };
aiNodeAnim* CreateNodeAnim(glTF2::Asset& r, Node& node, AnimationSamplers& samplers) aiNodeAnim *CreateNodeAnim(glTF2::Asset &r, Node &node, AnimationSamplers &samplers) {
{ aiNodeAnim *anim = new aiNodeAnim();
aiNodeAnim* anim = new aiNodeAnim();
anim->mNodeName = GetNodeName(node); anim->mNodeName = GetNodeName(node);
static const float kMillisecondsFromSeconds = 1000.f; static const float kMillisecondsFromSeconds = 1000.f;
if (samplers.translation) { if (samplers.translation) {
float* times = nullptr; float *times = nullptr;
samplers.translation->input->ExtractData(times); samplers.translation->input->ExtractData(times);
aiVector3D* values = nullptr; aiVector3D *values = nullptr;
samplers.translation->output->ExtractData(values); samplers.translation->output->ExtractData(values);
anim->mNumPositionKeys = static_cast<uint32_t>(samplers.translation->input->count); anim->mNumPositionKeys = static_cast<uint32_t>(samplers.translation->input->count);
anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys]; anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
@ -1036,7 +1025,7 @@ aiNodeAnim* CreateNodeAnim(glTF2::Asset& r, Node& node, AnimationSamplers& sampl
delete[] values; delete[] values;
} else if (node.translation.isPresent) { } else if (node.translation.isPresent) {
anim->mNumPositionKeys = 1; anim->mNumPositionKeys = 1;
anim->mPositionKeys = new aiVectorKey(); anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
anim->mPositionKeys->mTime = 0.f; anim->mPositionKeys->mTime = 0.f;
anim->mPositionKeys->mValue.x = node.translation.value[0]; anim->mPositionKeys->mValue.x = node.translation.value[0];
anim->mPositionKeys->mValue.y = node.translation.value[1]; anim->mPositionKeys->mValue.y = node.translation.value[1];
@ -1044,9 +1033,9 @@ aiNodeAnim* CreateNodeAnim(glTF2::Asset& r, Node& node, AnimationSamplers& sampl
} }
if (samplers.rotation) { if (samplers.rotation) {
float* times = nullptr; float *times = nullptr;
samplers.rotation->input->ExtractData(times); samplers.rotation->input->ExtractData(times);
aiQuaternion* values = nullptr; aiQuaternion *values = nullptr;
samplers.rotation->output->ExtractData(values); samplers.rotation->output->ExtractData(values);
anim->mNumRotationKeys = static_cast<uint32_t>(samplers.rotation->input->count); anim->mNumRotationKeys = static_cast<uint32_t>(samplers.rotation->input->count);
anim->mRotationKeys = new aiQuatKey[anim->mNumRotationKeys]; anim->mRotationKeys = new aiQuatKey[anim->mNumRotationKeys];
@ -1070,9 +1059,9 @@ aiNodeAnim* CreateNodeAnim(glTF2::Asset& r, Node& node, AnimationSamplers& sampl
} }
if (samplers.scale) { if (samplers.scale) {
float* times = nullptr; float *times = nullptr;
samplers.scale->input->ExtractData(times); samplers.scale->input->ExtractData(times);
aiVector3D* values = nullptr; aiVector3D *values = nullptr;
samplers.scale->output->ExtractData(values); samplers.scale->output->ExtractData(values);
anim->mNumScalingKeys = static_cast<uint32_t>(samplers.scale->input->count); anim->mNumScalingKeys = static_cast<uint32_t>(samplers.scale->input->count);
anim->mScalingKeys = new aiVectorKey[anim->mNumScalingKeys]; anim->mScalingKeys = new aiVectorKey[anim->mNumScalingKeys];
@ -1094,32 +1083,68 @@ aiNodeAnim* CreateNodeAnim(glTF2::Asset& r, Node& node, AnimationSamplers& sampl
return anim; return anim;
} }
std::unordered_map<unsigned int, AnimationSamplers> GatherSamplers(Animation& anim) aiMeshMorphAnim *CreateMeshMorphAnim(glTF2::Asset &r, Node &node, AnimationSamplers &samplers) {
{ aiMeshMorphAnim *anim = new aiMeshMorphAnim();
anim->mName = GetNodeName(node);
static const float kMillisecondsFromSeconds = 1000.f;
if (nullptr != samplers.weight) {
float *times = nullptr;
samplers.weight->input->ExtractData(times);
float *values = nullptr;
samplers.weight->output->ExtractData(values);
anim->mNumKeys = static_cast<uint32_t>(samplers.weight->input->count);
const unsigned int numMorphs = (unsigned int)samplers.weight->output->count / anim->mNumKeys;
anim->mKeys = new aiMeshMorphKey[anim->mNumKeys];
unsigned int k = 0u;
for (unsigned int i = 0u; i < anim->mNumKeys; ++i) {
anim->mKeys[i].mTime = times[i] * kMillisecondsFromSeconds;
anim->mKeys[i].mNumValuesAndWeights = numMorphs;
anim->mKeys[i].mValues = new unsigned int[numMorphs];
anim->mKeys[i].mWeights = new double[numMorphs];
for (unsigned int j = 0u; j < numMorphs; ++j, ++k) {
anim->mKeys[i].mValues[j] = j;
anim->mKeys[i].mWeights[j] = (0.f > values[k]) ? 0.f : values[k];
}
}
delete[] times;
delete[] values;
}
return anim;
}
std::unordered_map<unsigned int, AnimationSamplers> GatherSamplers(Animation &anim) {
std::unordered_map<unsigned int, AnimationSamplers> samplers; std::unordered_map<unsigned int, AnimationSamplers> samplers;
for (unsigned int c = 0; c < anim.channels.size(); ++c) { for (unsigned int c = 0; c < anim.channels.size(); ++c) {
Animation::Channel& channel = anim.channels[c]; Animation::Channel &channel = anim.channels[c];
if (channel.sampler >= static_cast<int>(anim.samplers.size())) { if (channel.sampler >= static_cast<int>(anim.samplers.size())) {
continue; continue;
} }
const unsigned int node_index = channel.target.node.GetIndex(); const unsigned int node_index = channel.target.node.GetIndex();
AnimationSamplers& sampler = samplers[node_index]; AnimationSamplers &sampler = samplers[node_index];
if (channel.target.path == AnimationPath_TRANSLATION) { if (channel.target.path == AnimationPath_TRANSLATION) {
sampler.translation = &anim.samplers[channel.sampler]; sampler.translation = &anim.samplers[channel.sampler];
} else if (channel.target.path == AnimationPath_ROTATION) { } else if (channel.target.path == AnimationPath_ROTATION) {
sampler.rotation = &anim.samplers[channel.sampler]; sampler.rotation = &anim.samplers[channel.sampler];
} else if (channel.target.path == AnimationPath_SCALE) { } else if (channel.target.path == AnimationPath_SCALE) {
sampler.scale = &anim.samplers[channel.sampler]; sampler.scale = &anim.samplers[channel.sampler];
} else if (channel.target.path == AnimationPath_WEIGHTS) {
sampler.weight = &anim.samplers[channel.sampler];
} }
} }
return samplers; return samplers;
} }
void glTF2Importer::ImportAnimations(glTF2::Asset& r) void glTF2Importer::ImportAnimations(glTF2::Asset &r) {
{
if (!r.scene) return; if (!r.scene) return;
mScene->mNumAnimations = r.animations.Size(); mScene->mNumAnimations = r.animations.Size();
@ -1127,26 +1152,52 @@ void glTF2Importer::ImportAnimations(glTF2::Asset& r)
return; return;
} }
mScene->mAnimations = new aiAnimation*[mScene->mNumAnimations]; mScene->mAnimations = new aiAnimation *[mScene->mNumAnimations];
for (unsigned int i = 0; i < r.animations.Size(); ++i) { for (unsigned int i = 0; i < r.animations.Size(); ++i) {
Animation& anim = r.animations[i]; Animation &anim = r.animations[i];
aiAnimation* ai_anim = new aiAnimation(); aiAnimation *ai_anim = new aiAnimation();
ai_anim->mName = anim.name; ai_anim->mName = anim.name;
ai_anim->mDuration = 0; ai_anim->mDuration = 0;
ai_anim->mTicksPerSecond = 0; ai_anim->mTicksPerSecond = 0;
std::unordered_map<unsigned int, AnimationSamplers> samplers = GatherSamplers(anim); std::unordered_map<unsigned int, AnimationSamplers> samplers = GatherSamplers(anim);
ai_anim->mNumChannels = static_cast<uint32_t>(samplers.size()); uint32_t numChannels = 0u;
uint32_t numMorphMeshChannels = 0u;
for (auto &iter : samplers) {
if ((nullptr != iter.second.rotation) || (nullptr != iter.second.scale) || (nullptr != iter.second.translation)) {
++numChannels;
}
if (nullptr != iter.second.weight) {
++numMorphMeshChannels;
}
}
ai_anim->mNumChannels = numChannels;
if (ai_anim->mNumChannels > 0) { if (ai_anim->mNumChannels > 0) {
ai_anim->mChannels = new aiNodeAnim*[ai_anim->mNumChannels]; ai_anim->mChannels = new aiNodeAnim *[ai_anim->mNumChannels];
int j = 0; int j = 0;
for (auto& iter : samplers) { for (auto &iter : samplers) {
if ((nullptr != iter.second.rotation) || (nullptr != iter.second.scale) || (nullptr != iter.second.translation)) {
ai_anim->mChannels[j] = CreateNodeAnim(r, r.nodes[iter.first], iter.second); ai_anim->mChannels[j] = CreateNodeAnim(r, r.nodes[iter.first], iter.second);
++j; ++j;
} }
} }
}
ai_anim->mNumMorphMeshChannels = numMorphMeshChannels;
if (ai_anim->mNumMorphMeshChannels > 0) {
ai_anim->mMorphMeshChannels = new aiMeshMorphAnim *[ai_anim->mNumMorphMeshChannels];
int j = 0;
for (auto &iter : samplers) {
if (nullptr != iter.second.weight) {
ai_anim->mMorphMeshChannels[j] = CreateMeshMorphAnim(r, r.nodes[iter.first], iter.second);
++j;
}
}
}
// Use the latest keyframe for the duration of the animation // Use the latest keyframe for the duration of the animation
double maxDuration = 0; double maxDuration = 0;
@ -1175,15 +1226,27 @@ void glTF2Importer::ImportAnimations(glTF2::Asset& r)
maxNumberOfKeys = std::max(maxNumberOfKeys, chan->mNumScalingKeys); maxNumberOfKeys = std::max(maxNumberOfKeys, chan->mNumScalingKeys);
} }
} }
for (unsigned int j = 0; j < ai_anim->mNumMorphMeshChannels; ++j) {
const auto *const chan = ai_anim->mMorphMeshChannels[j];
if (0u != chan->mNumKeys) {
const auto &lastKey = chan->mKeys[chan->mNumKeys - 1u];
if (lastKey.mTime > maxDuration) {
maxDuration = lastKey.mTime;
}
maxNumberOfKeys = std::max(maxNumberOfKeys, chan->mNumKeys);
}
}
ai_anim->mDuration = maxDuration; ai_anim->mDuration = maxDuration;
ai_anim->mTicksPerSecond = (maxNumberOfKeys > 0 && maxDuration > 0) ? (maxNumberOfKeys / (maxDuration/1000)) : 30; ai_anim->mTicksPerSecond = 1000.0;
mScene->mAnimations[i] = ai_anim; mScene->mAnimations[i] = ai_anim;
} }
} }
void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset& r) void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset &r) {
{
embeddedTexIdxs.resize(r.images.Size(), -1); embeddedTexIdxs.resize(r.images.Size(), -1);
int numEmbeddedTexs = 0; int numEmbeddedTexs = 0;
@ -1195,7 +1258,7 @@ void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset& r)
if (numEmbeddedTexs == 0) if (numEmbeddedTexs == 0)
return; return;
mScene->mTextures = new aiTexture*[numEmbeddedTexs]; mScene->mTextures = new aiTexture *[numEmbeddedTexs];
// Add the embedded textures // Add the embedded textures
for (size_t i = 0; i < r.images.Size(); ++i) { for (size_t i = 0; i < r.images.Size(); ++i) {
@ -1205,17 +1268,17 @@ void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset& r)
int idx = mScene->mNumTextures++; int idx = mScene->mNumTextures++;
embeddedTexIdxs[i] = idx; embeddedTexIdxs[i] = idx;
aiTexture* tex = mScene->mTextures[idx] = new aiTexture(); aiTexture *tex = mScene->mTextures[idx] = new aiTexture();
size_t length = img.GetDataLength(); size_t length = img.GetDataLength();
void* data = img.StealData(); void *data = img.StealData();
tex->mWidth = static_cast<unsigned int>(length); tex->mWidth = static_cast<unsigned int>(length);
tex->mHeight = 0; tex->mHeight = 0;
tex->pcData = reinterpret_cast<aiTexel*>(data); tex->pcData = reinterpret_cast<aiTexel *>(data);
if (!img.mimeType.empty()) { if (!img.mimeType.empty()) {
const char* ext = strchr(img.mimeType.c_str(), '/') + 1; const char *ext = strchr(img.mimeType.c_str(), '/') + 1;
if (ext) { if (ext) {
if (strcmp(ext, "jpeg") == 0) ext = "jpg"; if (strcmp(ext, "jpeg") == 0) ext = "jpg";
@ -1228,8 +1291,7 @@ void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset& r)
} }
} }
void glTF2Importer::InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) void glTF2Importer::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
{
// clean all member arrays // clean all member arrays
meshOffsets.clear(); meshOffsets.clear();
embeddedTexIdxs.clear(); embeddedTexIdxs.clear();
@ -1262,4 +1324,3 @@ void glTF2Importer::InternReadFile(const std::string& pFile, aiScene* pScene, IO
} }
#endif // ASSIMP_BUILD_NO_GLTF_IMPORTER #endif // ASSIMP_BUILD_NO_GLTF_IMPORTER

View File

@ -15,6 +15,9 @@
#include <cstdint> #include <cstdint>
//using namespace Assimp; //using namespace Assimp;
// For locale independent number conversion
#include <sstream>
#include <locale>
#ifdef _DEBUG #ifdef _DEBUG
#define IRR_DEBUGPRINT(x) printf((x)); #define IRR_DEBUGPRINT(x) printf((x));
@ -178,8 +181,11 @@ public:
return 0; return 0;
core::stringc c = attrvalue; core::stringc c = attrvalue;
return static_cast<float>(atof(c.c_str())); std::istringstream sstr(c.c_str());
//return fast_atof(c.c_str()); sstr.imbue(std::locale("C")); // Locale free number convert
float fNum;
sstr >> fNum;
return fNum;
} }

View File

@ -1,6 +1,7 @@
/build/ /build/
/test/build/ /test/build/
/xcodeproj/ /xcodeproj/
.vscode/
# Object files # Object files
*.o *.o
@ -54,3 +55,4 @@ zip.dir/
test/test.exe.vcxproj.filters test/test.exe.vcxproj.filters
test/test.exe.vcxproj test/test.exe.vcxproj
test/test.exe.dir/ test/test.exe.dir/

View File

@ -1,10 +1,14 @@
cmake_minimum_required(VERSION 2.8) cmake_minimum_required(VERSION 3.0)
project(zip)
enable_language(C) project(zip
LANGUAGES C
VERSION "0.1.15")
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
option(CMAKE_DISABLE_TESTING "Disable test creation" OFF)
if (MSVC) if (MSVC)
# Use secure functions by defaualt and suppress warnings about "deprecated" functions # Use secure functions by default and suppress warnings about "deprecated" functions
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT=1") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT=1")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_NONSTDC_NO_WARNINGS=1 /D _CRT_SECURE_NO_WARNINGS=1") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_NONSTDC_NO_WARNINGS=1 /D _CRT_SECURE_NO_WARNINGS=1")
@ -12,28 +16,80 @@ elseif ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR
"${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR
"${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang") "${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -Wall -Wextra -Werror -pedantic") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -Wall -Wextra -Werror -pedantic")
if(ENABLE_COVERAGE)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage")
endif()
endif (MSVC) endif (MSVC)
# zip # zip
set(SRC src/miniz.h src/zip.h src/zip.c) set(SRC src/miniz.h src/zip.h src/zip.c)
add_library(${PROJECT_NAME} ${SRC}) add_library(${PROJECT_NAME} ${SRC})
target_include_directories(${PROJECT_NAME} INTERFACE src) target_include_directories(${PROJECT_NAME} PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
$<INSTALL_INTERFACE:include>
)
# test # test
if (NOT CMAKE_DISABLE_TESTING) if (NOT CMAKE_DISABLE_TESTING)
enable_testing() enable_testing()
add_subdirectory(test) add_subdirectory(test)
find_package(Sanitizers) find_package(Sanitizers)
add_sanitizers(${PROJECT_NAME} test.exe) add_sanitizers(${PROJECT_NAME} ${test_out} ${test_miniz_out})
add_sanitizers(${PROJECT_NAME} test_miniz.exe)
endif() endif()
####
# Installation (https://github.com/forexample/package-example) {
set(CONFIG_INSTALL_DIR "lib/cmake/${PROJECT_NAME}")
set(INCLUDE_INSTALL_DIR "include")
set(GENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated")
# Configuration
set(VERSION_CONFIG "${GENERATED_DIR}/${PROJECT_NAME}ConfigVersion.cmake")
set(PROJECT_CONFIG "${GENERATED_DIR}/${PROJECT_NAME}Config.cmake")
set(TARGETS_EXPORT_NAME "${PROJECT_NAME}Targets")
set(NAMESPACE "${PROJECT_NAME}::")
# Include module with fuction 'write_basic_package_version_file'
include(CMakePackageConfigHelpers)
# Note: PROJECT_VERSION is used as a VERSION
write_basic_package_version_file(
"${VERSION_CONFIG}" COMPATIBILITY SameMajorVersion
)
# Use variables:
# * TARGETS_EXPORT_NAME
# * PROJECT_NAME
configure_package_config_file(
"cmake/Config.cmake.in"
"${PROJECT_CONFIG}"
INSTALL_DESTINATION "${CONFIG_INSTALL_DIR}"
)
install(
FILES "${PROJECT_CONFIG}" "${VERSION_CONFIG}"
DESTINATION "${CONFIG_INSTALL_DIR}"
)
install(
EXPORT "${TARGETS_EXPORT_NAME}"
NAMESPACE "${NAMESPACE}"
DESTINATION "${CONFIG_INSTALL_DIR}"
)
# }
install(TARGETS ${PROJECT_NAME} install(TARGETS ${PROJECT_NAME}
EXPORT ${TARGETS_EXPORT_NAME}
RUNTIME DESTINATION bin RUNTIME DESTINATION bin
ARCHIVE DESTINATION lib ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib LIBRARY DESTINATION lib
COMPONENT library) INCLUDES DESTINATION ${INCLUDE_INSTALL_DIR}
install(FILES ${PROJECT_SOURCE_DIR}/src/zip.h DESTINATION include) )
install(FILES ${PROJECT_SOURCE_DIR}/src/zip.h DESTINATION ${INCLUDE_INSTALL_DIR}/zip)
# uninstall target (https://gitlab.kitware.com/cmake/community/wikis/FAQ#can-i-do-make-uninstall-with-cmake) # uninstall target (https://gitlab.kitware.com/cmake/community/wikis/FAQ#can-i-do-make-uninstall-with-cmake)
if(NOT TARGET uninstall) if(NOT TARGET uninstall)
@ -45,3 +101,12 @@ if(NOT TARGET uninstall)
add_custom_target(uninstall add_custom_target(uninstall
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake/cmake_uninstall.cmake) COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake/cmake_uninstall.cmake)
endif() endif()
find_package(Doxygen)
if(DOXYGEN_FOUND)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY)
add_custom_target(doc
${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Generating API documentation with Doxygen" VERBATIM)
endif()

View File

@ -174,7 +174,7 @@ for (i = 0; i < n; ++i) {
zip_close(zip); zip_close(zip);
``` ```
## Bindings # Bindings
Compile zip library as a dynamic library. Compile zip library as a dynamic library.
```shell ```shell
$ mkdir build $ mkdir build

View File

@ -1,4 +1,4 @@
version: zip-0.1.9.{build} version: zip-0.1.15.{build}
build_script: build_script:
- cmd: >- - cmd: >-
cd c:\projects\zip cd c:\projects\zip

View File

@ -221,6 +221,7 @@
#ifndef MINIZ_HEADER_INCLUDED #ifndef MINIZ_HEADER_INCLUDED
#define MINIZ_HEADER_INCLUDED #define MINIZ_HEADER_INCLUDED
#include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
// Defines to completely disable specific portions of miniz.c: // Defines to completely disable specific portions of miniz.c:
@ -284,7 +285,8 @@
/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES only if not set */ /* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES only if not set */
#if !defined(MINIZ_USE_UNALIGNED_LOADS_AND_STORES) #if !defined(MINIZ_USE_UNALIGNED_LOADS_AND_STORES)
#if MINIZ_X86_OR_X64_CPU #if MINIZ_X86_OR_X64_CPU
/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. */ /* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient
* integer loads and stores from unaligned addresses. */
#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1
#define MINIZ_UNALIGNED_USE_MEMCPY #define MINIZ_UNALIGNED_USE_MEMCPY
#else #else
@ -354,6 +356,44 @@ enum {
MZ_FIXED = 4 MZ_FIXED = 4
}; };
/* miniz error codes. Be sure to update mz_zip_get_error_string() if you add or
* modify this enum. */
typedef enum {
MZ_ZIP_NO_ERROR = 0,
MZ_ZIP_UNDEFINED_ERROR,
MZ_ZIP_TOO_MANY_FILES,
MZ_ZIP_FILE_TOO_LARGE,
MZ_ZIP_UNSUPPORTED_METHOD,
MZ_ZIP_UNSUPPORTED_ENCRYPTION,
MZ_ZIP_UNSUPPORTED_FEATURE,
MZ_ZIP_FAILED_FINDING_CENTRAL_DIR,
MZ_ZIP_NOT_AN_ARCHIVE,
MZ_ZIP_INVALID_HEADER_OR_CORRUPTED,
MZ_ZIP_UNSUPPORTED_MULTIDISK,
MZ_ZIP_DECOMPRESSION_FAILED,
MZ_ZIP_COMPRESSION_FAILED,
MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE,
MZ_ZIP_CRC_CHECK_FAILED,
MZ_ZIP_UNSUPPORTED_CDIR_SIZE,
MZ_ZIP_ALLOC_FAILED,
MZ_ZIP_FILE_OPEN_FAILED,
MZ_ZIP_FILE_CREATE_FAILED,
MZ_ZIP_FILE_WRITE_FAILED,
MZ_ZIP_FILE_READ_FAILED,
MZ_ZIP_FILE_CLOSE_FAILED,
MZ_ZIP_FILE_SEEK_FAILED,
MZ_ZIP_FILE_STAT_FAILED,
MZ_ZIP_INVALID_PARAMETER,
MZ_ZIP_INVALID_FILENAME,
MZ_ZIP_BUF_TOO_SMALL,
MZ_ZIP_INTERNAL_ERROR,
MZ_ZIP_FILE_NOT_FOUND,
MZ_ZIP_ARCHIVE_TOO_LARGE,
MZ_ZIP_VALIDATION_FAILED,
MZ_ZIP_WRITE_CALLBACK_FAILED,
MZ_ZIP_TOTAL_ERRORS
} mz_zip_error;
// Method // Method
#define MZ_DEFLATED 8 #define MZ_DEFLATED 8
@ -696,6 +736,7 @@ typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs,
void *pBuf, size_t n); void *pBuf, size_t n);
typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs,
const void *pBuf, size_t n); const void *pBuf, size_t n);
typedef mz_bool (*mz_file_needs_keepalive)(void *pOpaque);
struct mz_zip_internal_state_tag; struct mz_zip_internal_state_tag;
typedef struct mz_zip_internal_state_tag mz_zip_internal_state; typedef struct mz_zip_internal_state_tag mz_zip_internal_state;
@ -707,13 +748,27 @@ typedef enum {
MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3 MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3
} mz_zip_mode; } mz_zip_mode;
typedef struct mz_zip_archive_tag { typedef enum {
MZ_ZIP_TYPE_INVALID = 0,
MZ_ZIP_TYPE_USER,
MZ_ZIP_TYPE_MEMORY,
MZ_ZIP_TYPE_HEAP,
MZ_ZIP_TYPE_FILE,
MZ_ZIP_TYPE_CFILE,
MZ_ZIP_TOTAL_TYPES
} mz_zip_type;
typedef struct {
mz_uint64 m_archive_size; mz_uint64 m_archive_size;
mz_uint64 m_central_directory_file_ofs; mz_uint64 m_central_directory_file_ofs;
mz_uint m_total_files;
mz_zip_mode m_zip_mode;
mz_uint m_file_offset_alignment; /* We only support up to UINT32_MAX files in zip64 mode. */
mz_uint32 m_total_files;
mz_zip_mode m_zip_mode;
mz_zip_type m_zip_type;
mz_zip_error m_last_error;
mz_uint64 m_file_offset_alignment;
mz_alloc_func m_pAlloc; mz_alloc_func m_pAlloc;
mz_free_func m_pFree; mz_free_func m_pFree;
@ -722,6 +777,7 @@ typedef struct mz_zip_archive_tag {
mz_file_read_func m_pRead; mz_file_read_func m_pRead;
mz_file_write_func m_pWrite; mz_file_write_func m_pWrite;
mz_file_needs_keepalive m_pNeeds_keepalive;
void *m_pIO_opaque; void *m_pIO_opaque;
mz_zip_internal_state *m_pState; mz_zip_internal_state *m_pState;
@ -1263,6 +1319,9 @@ mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits,
int strategy); int strategy);
#endif // #ifndef MINIZ_NO_ZLIB_APIS #endif // #ifndef MINIZ_NO_ZLIB_APIS
#define MZ_UINT16_MAX (0xFFFFU)
#define MZ_UINT32_MAX (0xFFFFFFFFU)
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
@ -1311,6 +1370,11 @@ typedef unsigned char mz_validate_uint64[sizeof(mz_uint64) == 8 ? 1 : -1];
((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U)) ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U))
#endif #endif
#define MZ_READ_LE64(p) \
(((mz_uint64)MZ_READ_LE32(p)) | \
(((mz_uint64)MZ_READ_LE32((const mz_uint8 *)(p) + sizeof(mz_uint32))) \
<< 32U))
#ifdef _MSC_VER #ifdef _MSC_VER
#define MZ_FORCEINLINE __forceinline #define MZ_FORCEINLINE __forceinline
#elif defined(__GNUC__) #elif defined(__GNUC__)
@ -4160,6 +4224,17 @@ enum {
MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30,
MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46,
MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22,
/* ZIP64 archive identifier and record sizes */
MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06064b50,
MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG = 0x07064b50,
MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE = 56,
MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE = 20,
MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID = 0x0001,
MZ_ZIP_DATA_DESCRIPTOR_ID = 0x08074b50,
MZ_ZIP_DATA_DESCRIPTER_SIZE64 = 24,
MZ_ZIP_DATA_DESCRIPTER_SIZE32 = 16,
// Central directory header record offsets // Central directory header record offsets
MZ_ZIP_CDH_SIG_OFS = 0, MZ_ZIP_CDH_SIG_OFS = 0,
MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4,
@ -4199,6 +4274,31 @@ enum {
MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12,
MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, MZ_ZIP_ECDH_CDIR_OFS_OFS = 16,
MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20, MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20,
/* ZIP64 End of central directory locator offsets */
MZ_ZIP64_ECDL_SIG_OFS = 0, /* 4 bytes */
MZ_ZIP64_ECDL_NUM_DISK_CDIR_OFS = 4, /* 4 bytes */
MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS = 8, /* 8 bytes */
MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS = 16, /* 4 bytes */
/* ZIP64 End of central directory header offsets */
MZ_ZIP64_ECDH_SIG_OFS = 0, /* 4 bytes */
MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS = 4, /* 8 bytes */
MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS = 12, /* 2 bytes */
MZ_ZIP64_ECDH_VERSION_NEEDED_OFS = 14, /* 2 bytes */
MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS = 16, /* 4 bytes */
MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS = 20, /* 4 bytes */
MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 24, /* 8 bytes */
MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS = 32, /* 8 bytes */
MZ_ZIP64_ECDH_CDIR_SIZE_OFS = 40, /* 8 bytes */
MZ_ZIP64_ECDH_CDIR_OFS_OFS = 48, /* 8 bytes */
MZ_ZIP_VERSION_MADE_BY_DOS_FILESYSTEM_ID = 0,
MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG = 0x10,
MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED = 1,
MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG = 32,
MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION = 64,
MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED = 8192,
MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 = 1 << 11
}; };
typedef struct { typedef struct {
@ -4211,7 +4311,24 @@ struct mz_zip_internal_state_tag {
mz_zip_array m_central_dir; mz_zip_array m_central_dir;
mz_zip_array m_central_dir_offsets; mz_zip_array m_central_dir_offsets;
mz_zip_array m_sorted_central_dir_offsets; mz_zip_array m_sorted_central_dir_offsets;
/* The flags passed in when the archive is initially opened. */
uint32_t m_init_flags;
/* MZ_TRUE if the archive has a zip64 end of central directory headers, etc.
*/
mz_bool m_zip64;
/* MZ_TRUE if we found zip64 extended info in the central directory (m_zip64
* will also be slammed to true too, even if we didn't find a zip64 end of
* central dir header, etc.) */
mz_bool m_zip64_has_extended_info_fields;
/* These fields are used by the file, FILE, memory, and memory/heap read/write
* helpers. */
MZ_FILE *m_pFile; MZ_FILE *m_pFile;
mz_uint64 m_file_archive_start_ofs;
void *m_pMem; void *m_pMem;
size_t m_mem_size; size_t m_mem_size;
size_t m_mem_capacity; size_t m_mem_capacity;
@ -4363,6 +4480,13 @@ static mz_bool mz_zip_set_file_times(const char *pFilename, time_t access_time,
#endif /* #ifndef MINIZ_NO_STDIO */ #endif /* #ifndef MINIZ_NO_STDIO */
#endif /* #ifndef MINIZ_NO_TIME */ #endif /* #ifndef MINIZ_NO_TIME */
static MZ_FORCEINLINE mz_bool mz_zip_set_error(mz_zip_archive *pZip,
mz_zip_error err_num) {
if (pZip)
pZip->m_last_error = err_num;
return MZ_FALSE;
}
static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip,
mz_uint32 flags) { mz_uint32 flags) {
(void)flags; (void)flags;
@ -4480,127 +4604,346 @@ mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip) {
} }
} }
static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, static mz_bool mz_zip_reader_locate_header_sig(mz_zip_archive *pZip,
mz_uint32 flags) { mz_uint32 record_sig,
mz_uint cdir_size, num_this_disk, cdir_disk_index; mz_uint32 record_size,
mz_uint64 cdir_ofs; mz_int64 *pOfs) {
mz_int64 cur_file_ofs; mz_int64 cur_file_ofs;
const mz_uint8 *p;
mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; mz_uint32 buf_u32[4096 / sizeof(mz_uint32)];
mz_uint8 *pBuf = (mz_uint8 *)buf_u32; mz_uint8 *pBuf = (mz_uint8 *)buf_u32;
mz_bool sort_central_dir =
((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0); /* Basic sanity checks - reject files which are too small */
// Basic sanity checks - reject files which are too small, and check the first if (pZip->m_archive_size < record_size)
// 4 bytes of the file to make sure a local header is there.
if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)
return MZ_FALSE; return MZ_FALSE;
// Find the end of central directory record by scanning the file from the end
// towards the beginning. /* Find the record by scanning the file from the end towards the beginning. */
cur_file_ofs = cur_file_ofs =
MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0); MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0);
for (;;) { for (;;) {
int i, int i,
n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs); n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs);
if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n) if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n)
return MZ_FALSE; return MZ_FALSE;
for (i = n - 4; i >= 0; --i)
if (MZ_READ_LE32(pBuf + i) == MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) for (i = n - 4; i >= 0; --i) {
mz_uint s = MZ_READ_LE32(pBuf + i);
if (s == record_sig) {
if ((pZip->m_archive_size - (cur_file_ofs + i)) >= record_size)
break; break;
}
}
if (i >= 0) { if (i >= 0) {
cur_file_ofs += i; cur_file_ofs += i;
break; break;
} }
/* Give up if we've searched the entire file, or we've gone back "too far"
* (~64kb) */
if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >=
(0xFFFF + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE))) (MZ_UINT16_MAX + record_size)))
return MZ_FALSE; return MZ_FALSE;
cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0); cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0);
} }
// Read and verify the end of central directory record.
*pOfs = cur_file_ofs;
return MZ_TRUE;
}
static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip,
mz_uint flags) {
mz_uint cdir_size = 0, cdir_entries_on_this_disk = 0, num_this_disk = 0,
cdir_disk_index = 0;
mz_uint64 cdir_ofs = 0;
mz_int64 cur_file_ofs = 0;
const mz_uint8 *p;
mz_uint32 buf_u32[4096 / sizeof(mz_uint32)];
mz_uint8 *pBuf = (mz_uint8 *)buf_u32;
mz_bool sort_central_dir =
((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0);
mz_uint32 zip64_end_of_central_dir_locator_u32
[(MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + sizeof(mz_uint32) - 1) /
sizeof(mz_uint32)];
mz_uint8 *pZip64_locator = (mz_uint8 *)zip64_end_of_central_dir_locator_u32;
mz_uint32 zip64_end_of_central_dir_header_u32
[(MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) /
sizeof(mz_uint32)];
mz_uint8 *pZip64_end_of_central_dir =
(mz_uint8 *)zip64_end_of_central_dir_header_u32;
mz_uint64 zip64_end_of_central_dir_ofs = 0;
/* Basic sanity checks - reject files which are too small, and check the first
* 4 bytes of the file to make sure a local header is there. */
if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)
return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE);
if (!mz_zip_reader_locate_header_sig(
pZip, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG,
MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE, &cur_file_ofs))
return mz_zip_set_error(pZip, MZ_ZIP_FAILED_FINDING_CENTRAL_DIR);
/* Read and verify the end of central directory record. */
if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf,
MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) !=
MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)
return MZ_FALSE; return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
if ((MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) !=
MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) ||
((pZip->m_total_files =
MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS)) !=
MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS)))
return MZ_FALSE;
if (MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) !=
MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG)
return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE);
if (cur_file_ofs >= (MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE +
MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) {
if (pZip->m_pRead(pZip->m_pIO_opaque,
cur_file_ofs - MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE,
pZip64_locator,
MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) ==
MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) {
if (MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_SIG_OFS) ==
MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG) {
zip64_end_of_central_dir_ofs = MZ_READ_LE64(
pZip64_locator + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS);
if (zip64_end_of_central_dir_ofs >
(pZip->m_archive_size - MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE))
return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE);
if (pZip->m_pRead(pZip->m_pIO_opaque, zip64_end_of_central_dir_ofs,
pZip64_end_of_central_dir,
MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) ==
MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) {
if (MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIG_OFS) ==
MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG) {
pZip->m_pState->m_zip64 = MZ_TRUE;
}
}
}
}
}
pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS);
cdir_entries_on_this_disk =
MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS);
num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS); num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS);
cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS); cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS);
cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS);
cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS);
if (pZip->m_pState->m_zip64) {
mz_uint32 zip64_total_num_of_disks =
MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS);
mz_uint64 zip64_cdir_total_entries = MZ_READ_LE64(
pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS);
mz_uint64 zip64_cdir_total_entries_on_this_disk = MZ_READ_LE64(
pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS);
mz_uint64 zip64_size_of_end_of_central_dir_record = MZ_READ_LE64(
pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS);
mz_uint64 zip64_size_of_central_directory =
MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_SIZE_OFS);
if (zip64_size_of_end_of_central_dir_record <
(MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - 12))
return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
if (zip64_total_num_of_disks != 1U)
return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK);
/* Check for miniz's practical limits */
if (zip64_cdir_total_entries > MZ_UINT32_MAX)
return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);
pZip->m_total_files = (mz_uint32)zip64_cdir_total_entries;
if (zip64_cdir_total_entries_on_this_disk > MZ_UINT32_MAX)
return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);
cdir_entries_on_this_disk =
(mz_uint32)zip64_cdir_total_entries_on_this_disk;
/* Check for miniz's current practical limits (sorry, this should be enough
* for millions of files) */
if (zip64_size_of_central_directory > MZ_UINT32_MAX)
return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE);
cdir_size = (mz_uint32)zip64_size_of_central_directory;
num_this_disk = MZ_READ_LE32(pZip64_end_of_central_dir +
MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS);
cdir_disk_index = MZ_READ_LE32(pZip64_end_of_central_dir +
MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS);
cdir_ofs =
MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_OFS_OFS);
}
if (pZip->m_total_files != cdir_entries_on_this_disk)
return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK);
if (((num_this_disk | cdir_disk_index) != 0) && if (((num_this_disk | cdir_disk_index) != 0) &&
((num_this_disk != 1) || (cdir_disk_index != 1))) ((num_this_disk != 1) || (cdir_disk_index != 1)))
return MZ_FALSE; return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK);
if ((cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS)) < if (cdir_size < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)
pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
return MZ_FALSE;
cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS);
if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size) if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size)
return MZ_FALSE; return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
pZip->m_central_directory_file_ofs = cdir_ofs; pZip->m_central_directory_file_ofs = cdir_ofs;
if (pZip->m_total_files) { if (pZip->m_total_files) {
mz_uint i, n; mz_uint i, n;
/* Read the entire central directory into a heap block, and allocate another
// Read the entire central directory into a heap block, and allocate another * heap block to hold the unsorted central dir file record offsets, and
// heap block to hold the unsorted central dir file record offsets, and * possibly another to hold the sorted indices. */
// another to hold the sorted indices.
if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size,
MZ_FALSE)) || MZ_FALSE)) ||
(!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets,
pZip->m_total_files, MZ_FALSE))) pZip->m_total_files, MZ_FALSE)))
return MZ_FALSE; return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
if (sort_central_dir) { if (sort_central_dir) {
if (!mz_zip_array_resize(pZip, if (!mz_zip_array_resize(pZip,
&pZip->m_pState->m_sorted_central_dir_offsets, &pZip->m_pState->m_sorted_central_dir_offsets,
pZip->m_total_files, MZ_FALSE)) pZip->m_total_files, MZ_FALSE))
return MZ_FALSE; return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
} }
if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs,
pZip->m_pState->m_central_dir.m_p, pZip->m_pState->m_central_dir.m_p,
cdir_size) != cdir_size) cdir_size) != cdir_size)
return MZ_FALSE; return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
// Now create an index into the central directory file records, do some /* Now create an index into the central directory file records, do some
// basic sanity checking on each record, and check for zip64 entries (which * basic sanity checking on each record */
// are not yet supported).
p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p; p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p;
for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i) { for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i) {
mz_uint total_header_size, comp_size, decomp_size, disk_index; mz_uint total_header_size, disk_index, bit_flags, filename_size,
ext_data_size;
mz_uint64 comp_size, decomp_size, local_header_ofs;
if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) ||
(MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG)) (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG))
return MZ_FALSE; return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32,
i) = i) =
(mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p); (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p);
if (sort_central_dir) if (sort_central_dir)
MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets,
mz_uint32, i) = i; mz_uint32, i) = i;
comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS);
decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS);
local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS);
filename_size = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS);
ext_data_size = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS);
if ((!pZip->m_pState->m_zip64_has_extended_info_fields) &&
(ext_data_size) &&
(MZ_MAX(MZ_MAX(comp_size, decomp_size), local_header_ofs) ==
MZ_UINT32_MAX)) {
/* Attempt to find zip64 extended information field in the entry's extra
* data */
mz_uint32 extra_size_remaining = ext_data_size;
if (extra_size_remaining) {
const mz_uint8 *pExtra_data;
void *buf = NULL;
if (MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + ext_data_size >
n) {
buf = MZ_MALLOC(ext_data_size);
if (buf == NULL)
return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
if (pZip->m_pRead(pZip->m_pIO_opaque,
cdir_ofs + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE +
filename_size,
buf, ext_data_size) != ext_data_size) {
MZ_FREE(buf);
return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
}
pExtra_data = (mz_uint8 *)buf;
} else {
pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size;
}
do {
mz_uint32 field_id;
mz_uint32 field_data_size;
if (extra_size_remaining < (sizeof(mz_uint16) * 2)) {
MZ_FREE(buf);
return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
}
field_id = MZ_READ_LE16(pExtra_data);
field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16));
if ((field_data_size + sizeof(mz_uint16) * 2) >
extra_size_remaining) {
MZ_FREE(buf);
return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
}
if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) {
/* Ok, the archive didn't have any zip64 headers but it uses a
* zip64 extended information field so mark it as zip64 anyway
* (this can occur with infozip's zip util when it reads
* compresses files from stdin). */
pZip->m_pState->m_zip64 = MZ_TRUE;
pZip->m_pState->m_zip64_has_extended_info_fields = MZ_TRUE;
break;
}
pExtra_data += sizeof(mz_uint16) * 2 + field_data_size;
extra_size_remaining =
extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size;
} while (extra_size_remaining);
MZ_FREE(buf);
}
}
/* I've seen archives that aren't marked as zip64 that uses zip64 ext
* data, argh */
if ((comp_size != MZ_UINT32_MAX) && (decomp_size != MZ_UINT32_MAX)) {
if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) &&
(decomp_size != comp_size)) || (decomp_size != comp_size)) ||
(decomp_size && !comp_size) || (decomp_size == 0xFFFFFFFF) || (decomp_size && !comp_size))
(comp_size == 0xFFFFFFFF)) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
return MZ_FALSE; }
disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS); disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS);
if ((disk_index != num_this_disk) && (disk_index != 1)) if ((disk_index == MZ_UINT16_MAX) ||
return MZ_FALSE; ((disk_index != num_this_disk) && (disk_index != 1)))
return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK);
if (comp_size != MZ_UINT32_MAX) {
if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) +
MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size) MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size)
return MZ_FALSE; return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
}
bit_flags = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS);
if (bit_flags & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED)
return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION);
if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE +
MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) +
MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) +
MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) >
n) n)
return MZ_FALSE; return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
n -= total_header_size; n -= total_header_size;
p += total_header_size; p += total_header_size;
} }

View File

@ -24,7 +24,6 @@
((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) && \ ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) && \
(P)[1] == ':') (P)[1] == ':')
#define FILESYSTEM_PREFIX_LEN(P) (HAS_DEVICE(P) ? 2 : 0) #define FILESYSTEM_PREFIX_LEN(P) (HAS_DEVICE(P) ? 2 : 0)
#define ISSLASH(C) ((C) == '/' || (C) == '\\')
#else #else
@ -48,7 +47,7 @@ int symlink(const char *target, const char *linkpath); // needed on Linux
#endif #endif
#ifndef ISSLASH #ifndef ISSLASH
#define ISSLASH(C) ((C) == '/') #define ISSLASH(C) ((C) == '/' || (C) == '\\')
#endif #endif
#define CLEANUP(ptr) \ #define CLEANUP(ptr) \
@ -78,27 +77,35 @@ static const char *base_name(const char *name) {
return base; return base;
} }
static int mkpath(const char *path) { static int mkpath(char *path) {
char const *p; char *p;
char npath[MAX_PATH + 1]; char npath[MAX_PATH + 1];
int len = 0; int len = 0;
int has_device = HAS_DEVICE(path); int has_device = HAS_DEVICE(path);
memset(npath, 0, MAX_PATH + 1); memset(npath, 0, MAX_PATH + 1);
if (has_device) {
#ifdef _WIN32 // only on windows
// only on windows fix the path
npath[0] = path[0]; npath[0] = path[0];
npath[1] = path[1]; npath[1] = path[1];
len = 2; len = 2;
#endif // _WIN32 }
for (p = path + len; *p && len < MAX_PATH; p++) { for (p = path + len; *p && len < MAX_PATH; p++) {
if (ISSLASH(*p) && ((!has_device && len > 0) || (has_device && len > 2))) { if (ISSLASH(*p) && ((!has_device && len > 0) || (has_device && len > 2))) {
if (MKDIR(npath) == -1) #if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) || \
if (errno != EEXIST) defined(__MINGW32__)
#else
if ('\\' == *p) {
*p = '/';
}
#endif
if (MKDIR(npath) == -1) {
if (errno != EEXIST) {
return -1; return -1;
} }
}
}
npath[len++] = *p; npath[len++] = *p;
} }
@ -279,7 +286,14 @@ int zip_entry_open(struct zip_t *zip, const char *entryname) {
zip->entry.header_offset = zip->archive.m_archive_size; zip->entry.header_offset = zip->archive.m_archive_size;
memset(zip->entry.header, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE * sizeof(mz_uint8)); memset(zip->entry.header, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE * sizeof(mz_uint8));
zip->entry.method = 0; zip->entry.method = 0;
// UNIX or APPLE
#if MZ_PLATFORM == 3 || MZ_PLATFORM == 19
// regular file with rw-r--r-- persmissions
zip->entry.external_attr = (mz_uint32)(0100644) << 16;
#else
zip->entry.external_attr = 0; zip->entry.external_attr = 0;
#endif
num_alignment_padding_bytes = num_alignment_padding_bytes =
mz_zip_writer_compute_padding_needed_for_file_alignment(pzip); mz_zip_writer_compute_padding_needed_for_file_alignment(pzip);
@ -670,10 +684,7 @@ ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf, size_t bufsize) {
int zip_entry_fread(struct zip_t *zip, const char *filename) { int zip_entry_fread(struct zip_t *zip, const char *filename) {
mz_zip_archive *pzip = NULL; mz_zip_archive *pzip = NULL;
mz_uint idx; mz_uint idx;
#if defined(_MSC_VER)
#else
mz_uint32 xattr = 0; mz_uint32 xattr = 0;
#endif
mz_zip_archive_file_stat info; mz_zip_archive_file_stat info;
if (!zip) { if (!zip) {
@ -875,12 +886,19 @@ int zip_extract(const char *zipname, const char *dir,
goto out; goto out;
} }
if ((((info.m_version_made_by >> 8) == 3) || ((info.m_version_made_by >> 8) == 19)) // if zip is produced on Unix or macOS (3 and 19 from section 4.4.2.2 of zip standard) if ((((info.m_version_made_by >> 8) == 3) ||
&& info.m_external_attr & (0x20 << 24)) { // and has sym link attribute (0x80 is file, 0x40 is directory) ((info.m_version_made_by >> 8) ==
19)) // if zip is produced on Unix or macOS (3 and 19 from
// section 4.4.2.2 of zip standard)
&& info.m_external_attr &
(0x20 << 24)) { // and has sym link attribute (0x80 is file, 0x40
// is directory)
#if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) || \ #if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) || \
defined(__MINGW32__) defined(__MINGW32__)
#else #else
if (info.m_uncomp_size > MAX_PATH || !mz_zip_reader_extract_to_mem_no_alloc(&zip_archive, i, symlink_to, MAX_PATH, 0, NULL, 0)) { if (info.m_uncomp_size > MAX_PATH ||
!mz_zip_reader_extract_to_mem_no_alloc(&zip_archive, i, symlink_to,
MAX_PATH, 0, NULL, 0)) {
goto out; goto out;
} }
symlink_to[info.m_uncomp_size] = '\0'; symlink_to[info.m_uncomp_size] = '\0';

View File

@ -20,8 +20,9 @@ extern "C" {
#endif #endif
#if !defined(_SSIZE_T_DEFINED) && !defined(_SSIZE_T_DEFINED_) && \ #if !defined(_SSIZE_T_DEFINED) && !defined(_SSIZE_T_DEFINED_) && \
!defined(_SSIZE_T) && !defined(_SSIZE_T_) && !defined(__ssize_t_defined) !defined(__DEFINED_ssize_t) && !defined(__ssize_t_defined) && \
#define _SSIZE_T !defined(_SSIZE_T) && !defined(_SSIZE_T_)
// 64-bit Windows is the only mainstream platform // 64-bit Windows is the only mainstream platform
// where sizeof(long) != sizeof(void*) // where sizeof(long) != sizeof(void*)
#ifdef _WIN64 #ifdef _WIN64
@ -29,232 +30,230 @@ typedef long long ssize_t; /* byte count or error */
#else #else
typedef long ssize_t; /* byte count or error */ typedef long ssize_t; /* byte count or error */
#endif #endif
#define _SSIZE_T_DEFINED
#define _SSIZE_T_DEFINED_
#define __DEFINED_ssize_t
#define __ssize_t_defined
#define _SSIZE_T
#define _SSIZE_T_
#endif #endif
#ifndef MAX_PATH #ifndef MAX_PATH
#define MAX_PATH 32767 /* # chars in a path name including NULL */ #define MAX_PATH 32767 /* # chars in a path name including NULL */
#endif #endif
/**
* @mainpage
*
* Documenation for @ref zip.
*/
/**
* @addtogroup zip
* @{
*/
/**
* Default zip compression level.
*/
#define ZIP_DEFAULT_COMPRESSION_LEVEL 6 #define ZIP_DEFAULT_COMPRESSION_LEVEL 6
/* /**
This data structure is used throughout the library to represent zip archive * @struct zip_t
- forward declaration. *
*/ * This data structure is used throughout the library to represent zip archive -
* forward declaration.
*/
struct zip_t; struct zip_t;
/* /**
Opens zip archive with compression level using the given mode. * Opens zip archive with compression level using the given mode.
*
Args: * @param zipname zip archive file name.
zipname: zip archive file name. * @param level compression level (0-9 are the standard zlib-style levels).
level: compression level (0-9 are the standard zlib-style levels). * @param mode file access mode.
mode: file access mode. * - 'r': opens a file for reading/extracting (the file must exists).
'r': opens a file for reading/extracting (the file must exists). * - 'w': creates an empty file for writing.
'w': creates an empty file for writing. * - 'a': appends to an existing archive.
'a': appends to an existing archive. *
* @return the zip archive handler or NULL on error
Returns: */
The zip archive handler or NULL on error
*/
extern struct zip_t *zip_open(const char *zipname, int level, char mode); extern struct zip_t *zip_open(const char *zipname, int level, char mode);
/* /**
Closes the zip archive, releases resources - always finalize. * Closes the zip archive, releases resources - always finalize.
*
Args: * @param zip zip archive handler.
zip: zip archive handler. */
*/
extern void zip_close(struct zip_t *zip); extern void zip_close(struct zip_t *zip);
/* /**
Opens an entry by name in the zip archive. * Opens an entry by name in the zip archive.
For zip archive opened in 'w' or 'a' mode the function will append *
a new entry. In readonly mode the function tries to locate the entry * For zip archive opened in 'w' or 'a' mode the function will append
in global dictionary. * a new entry. In readonly mode the function tries to locate the entry
* in global dictionary.
Args: *
zip: zip archive handler. * @param zip zip archive handler.
entryname: an entry name in local dictionary. * @param entryname an entry name in local dictionary.
*
Returns: * @return the return code - 0 on success, negative number (< 0) on error.
The return code - 0 on success, negative number (< 0) on error. */
*/
extern int zip_entry_open(struct zip_t *zip, const char *entryname); extern int zip_entry_open(struct zip_t *zip, const char *entryname);
/* /**
Opens a new entry by index in the zip archive. * Opens a new entry by index in the zip archive.
This function is only valid if zip archive was opened in 'r' (readonly) mode. *
* This function is only valid if zip archive was opened in 'r' (readonly) mode.
Args: *
zip: zip archive handler. * @param zip zip archive handler.
index: index in local dictionary. * @param index index in local dictionary.
*
Returns: * @return the return code - 0 on success, negative number (< 0) on error.
The return code - 0 on success, negative number (< 0) on error. */
*/
extern int zip_entry_openbyindex(struct zip_t *zip, int index); extern int zip_entry_openbyindex(struct zip_t *zip, int index);
/* /**
Closes a zip entry, flushes buffer and releases resources. * Closes a zip entry, flushes buffer and releases resources.
*
Args: * @param zip zip archive handler.
zip: zip archive handler. *
* @return the return code - 0 on success, negative number (< 0) on error.
Returns: */
The return code - 0 on success, negative number (< 0) on error.
*/
extern int zip_entry_close(struct zip_t *zip); extern int zip_entry_close(struct zip_t *zip);
/* /**
Returns a local name of the current zip entry. * Returns a local name of the current zip entry.
The main difference between user's entry name and local entry name *
is optional relative path. * The main difference between user's entry name and local entry name
Following .ZIP File Format Specification - the path stored MUST not contain * is optional relative path.
a drive or device letter, or a leading slash. * Following .ZIP File Format Specification - the path stored MUST not contain
All slashes MUST be forward slashes '/' as opposed to backwards slashes '\' * a drive or device letter, or a leading slash.
for compatibility with Amiga and UNIX file systems etc. * All slashes MUST be forward slashes '/' as opposed to backwards slashes '\'
* for compatibility with Amiga and UNIX file systems etc.
Args: *
zip: zip archive handler. * @param zip: zip archive handler.
*
Returns: * @return the pointer to the current zip entry name, or NULL on error.
The pointer to the current zip entry name, or NULL on error. */
*/
extern const char *zip_entry_name(struct zip_t *zip); extern const char *zip_entry_name(struct zip_t *zip);
/* /**
Returns an index of the current zip entry. * Returns an index of the current zip entry.
*
Args: * @param zip zip archive handler.
zip: zip archive handler. *
* @return the index on success, negative number (< 0) on error.
Returns: */
The index on success, negative number (< 0) on error.
*/
extern int zip_entry_index(struct zip_t *zip); extern int zip_entry_index(struct zip_t *zip);
/* /**
Determines if the current zip entry is a directory entry. * Determines if the current zip entry is a directory entry.
*
Args: * @param zip zip archive handler.
zip: zip archive handler. *
* @return the return code - 1 (true), 0 (false), negative number (< 0) on
Returns: * error.
The return code - 1 (true), 0 (false), negative number (< 0) on error. */
*/
extern int zip_entry_isdir(struct zip_t *zip); extern int zip_entry_isdir(struct zip_t *zip);
/* /**
Returns an uncompressed size of the current zip entry. * Returns an uncompressed size of the current zip entry.
*
Args: * @param zip zip archive handler.
zip: zip archive handler. *
* @return the uncompressed size in bytes.
Returns: */
The uncompressed size in bytes.
*/
extern unsigned long long zip_entry_size(struct zip_t *zip); extern unsigned long long zip_entry_size(struct zip_t *zip);
/* /**
Returns CRC-32 checksum of the current zip entry. * Returns CRC-32 checksum of the current zip entry.
*
Args: * @param zip zip archive handler.
zip: zip archive handler. *
* @return the CRC-32 checksum.
Returns: */
The CRC-32 checksum.
*/
extern unsigned int zip_entry_crc32(struct zip_t *zip); extern unsigned int zip_entry_crc32(struct zip_t *zip);
/* /**
Compresses an input buffer for the current zip entry. * Compresses an input buffer for the current zip entry.
*
Args: * @param zip zip archive handler.
zip: zip archive handler. * @param buf input buffer.
buf: input buffer. * @param bufsize input buffer size (in bytes).
bufsize: input buffer size (in bytes). *
* @return the return code - 0 on success, negative number (< 0) on error.
Returns: */
The return code - 0 on success, negative number (< 0) on error.
*/
extern int zip_entry_write(struct zip_t *zip, const void *buf, size_t bufsize); extern int zip_entry_write(struct zip_t *zip, const void *buf, size_t bufsize);
/* /**
Compresses a file for the current zip entry. * Compresses a file for the current zip entry.
*
Args: * @param zip zip archive handler.
zip: zip archive handler. * @param filename input file.
filename: input file. *
* @return the return code - 0 on success, negative number (< 0) on error.
Returns: */
The return code - 0 on success, negative number (< 0) on error.
*/
extern int zip_entry_fwrite(struct zip_t *zip, const char *filename); extern int zip_entry_fwrite(struct zip_t *zip, const char *filename);
/* /**
Extracts the current zip entry into output buffer. * Extracts the current zip entry into output buffer.
The function allocates sufficient memory for a output buffer. *
* The function allocates sufficient memory for a output buffer.
Args: *
zip: zip archive handler. * @param zip zip archive handler.
buf: output buffer. * @param buf output buffer.
bufsize: output buffer size (in bytes). * @param bufsize output buffer size (in bytes).
*
Note: * @note remember to release memory allocated for a output buffer.
- remember to release memory allocated for a output buffer. * for large entries, please take a look at zip_entry_extract function.
- for large entries, please take a look at zip_entry_extract function. *
* @return the return code - the number of bytes actually read on success.
Returns: * Otherwise a -1 on error.
The return code - the number of bytes actually read on success. */
Otherwise a -1 on error.
*/
extern ssize_t zip_entry_read(struct zip_t *zip, void **buf, size_t *bufsize); extern ssize_t zip_entry_read(struct zip_t *zip, void **buf, size_t *bufsize);
/* /**
Extracts the current zip entry into a memory buffer using no memory * Extracts the current zip entry into a memory buffer using no memory
allocation. * allocation.
*
* @param zip zip archive handler.
* @param buf preallocated output buffer.
* @param bufsize output buffer size (in bytes).
*
* @note ensure supplied output buffer is large enough.
* zip_entry_size function (returns uncompressed size for the current
* entry) can be handy to estimate how big buffer is needed. for large
* entries, please take a look at zip_entry_extract function.
*
* @return the return code - the number of bytes actually read on success.
* Otherwise a -1 on error (e.g. bufsize is not large enough).
*/
extern ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf,
size_t bufsize);
Args: /**
zip: zip archive handler. * Extracts the current zip entry into output file.
buf: preallocated output buffer. *
bufsize: output buffer size (in bytes). * @param zip zip archive handler.
* @param filename output file.
Note: *
- ensure supplied output buffer is large enough. * @return the return code - 0 on success, negative number (< 0) on error.
- zip_entry_size function (returns uncompressed size for the current entry) */
can be handy to estimate how big buffer is needed.
- for large entries, please take a look at zip_entry_extract function.
Returns:
The return code - the number of bytes actually read on success.
Otherwise a -1 on error (e.g. bufsize is not large enough).
*/
extern ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf, size_t bufsize);
/*
Extracts the current zip entry into output file.
Args:
zip: zip archive handler.
filename: output file.
Returns:
The return code - 0 on success, negative number (< 0) on error.
*/
extern int zip_entry_fread(struct zip_t *zip, const char *filename); extern int zip_entry_fread(struct zip_t *zip, const char *filename);
/* /**
Extracts the current zip entry using a callback function (on_extract). * Extracts the current zip entry using a callback function (on_extract).
*
Args: * @param zip zip archive handler.
zip: zip archive handler. * @param on_extract callback function.
on_extract: callback function. * @param arg opaque pointer (optional argument, which you can pass to the
arg: opaque pointer (optional argument, * on_extract callback)
which you can pass to the on_extract callback) *
* @return the return code - 0 on success, negative number (< 0) on error.
Returns:
The return code - 0 on success, negative number (< 0) on error.
*/ */
extern int extern int
zip_entry_extract(struct zip_t *zip, zip_entry_extract(struct zip_t *zip,
@ -262,53 +261,49 @@ zip_entry_extract(struct zip_t *zip,
const void *data, size_t size), const void *data, size_t size),
void *arg); void *arg);
/* /**
Returns the number of all entries (files and directories) in the zip archive. * Returns the number of all entries (files and directories) in the zip archive.
*
Args: * @param zip zip archive handler.
zip: zip archive handler. *
* @return the return code - the number of entries on success, negative number
Returns: * (< 0) on error.
The return code - the number of entries on success, */
negative number (< 0) on error.
*/
extern int zip_total_entries(struct zip_t *zip); extern int zip_total_entries(struct zip_t *zip);
/* /**
Creates a new archive and puts files into a single zip archive. * Creates a new archive and puts files into a single zip archive.
*
Args: * @param zipname zip archive file.
zipname: zip archive file. * @param filenames input files.
filenames: input files. * @param len: number of input files.
len: number of input files. *
* @return the return code - 0 on success, negative number (< 0) on error.
Returns: */
The return code - 0 on success, negative number (< 0) on error.
*/
extern int zip_create(const char *zipname, const char *filenames[], size_t len); extern int zip_create(const char *zipname, const char *filenames[], size_t len);
/* /**
Extracts a zip archive file into directory. * Extracts a zip archive file into directory.
*
If on_extract_entry is not NULL, the callback will be called after * If on_extract_entry is not NULL, the callback will be called after
successfully extracted each zip entry. * successfully extracted each zip entry.
Returning a negative value from the callback will cause abort and return an * Returning a negative value from the callback will cause abort and return an
error. The last argument (void *arg) is optional, which you can use to pass * error. The last argument (void *arg) is optional, which you can use to pass
data to the on_extract_entry callback. * data to the on_extract_entry callback.
*
Args: * @param zipname zip archive file.
zipname: zip archive file. * @param dir output directory.
dir: output directory. * @param on_extract_entry on extract callback.
on_extract_entry: on extract callback. * @param arg opaque pointer.
arg: opaque pointer. *
* @return the return code - 0 on success, negative number (< 0) on error.
Returns: */
The return code - 0 on success, negative number (< 0) on error.
*/
extern int zip_extract(const char *zipname, const char *dir, extern int zip_extract(const char *zipname, const char *dir,
int (*on_extract_entry)(const char *filename, void *arg), int (*on_extract_entry)(const char *filename, void *arg),
void *arg); void *arg);
/** @} */
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -1,19 +1,16 @@
cmake_minimum_required(VERSION 2.8) cmake_minimum_required(VERSION 2.8)
if ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang")
if(ENABLE_COVERAGE)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g ")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ftest-coverage")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage")
endif()
endif ()
# test # test
include_directories(../src) set(test_out test.out)
add_executable(test.exe test.c ../src/zip.c) set(test_miniz_out test_miniz.out)
add_executable(test_miniz.exe test_miniz.c)
add_test(NAME test COMMAND test.exe) add_executable(${test_out} test.c)
add_test(NAME test_miniz COMMAND test_miniz.exe) target_link_libraries(${test_out} zip)
add_executable(${test_miniz_out} test_miniz.c)
target_link_libraries(${test_miniz_out} zip)
add_test(NAME ${test_out} COMMAND ${test_out})
add_test(NAME ${test_miniz_out} COMMAND ${test_miniz_out})
set(test_out ${test_out} PARENT_SCOPE)
set(test_miniz_out ${test_miniz_out} PARENT_SCOPE)

View File

@ -29,6 +29,8 @@
#define XFILE "7.txt\0" #define XFILE "7.txt\0"
#define XMODE 0100777 #define XMODE 0100777
#define UNIXMODE 0100644
#define UNUSED(x) (void)x #define UNUSED(x) (void)x
static int total_entries = 0; static int total_entries = 0;
@ -102,6 +104,7 @@ static void test_read(void) {
assert(0 == zip_entry_close(zip)); assert(0 == zip_entry_close(zip));
free(buf); free(buf);
buf = NULL; buf = NULL;
bufsize = 0;
assert(0 == zip_entry_open(zip, "test/test-2.txt")); assert(0 == zip_entry_open(zip, "test/test-2.txt"));
assert(strlen(TESTDATA2) == zip_entry_size(zip)); assert(strlen(TESTDATA2) == zip_entry_size(zip));
@ -131,6 +134,7 @@ static void test_read(void) {
assert(0 == zip_entry_close(zip)); assert(0 == zip_entry_close(zip));
free(buf); free(buf);
buf = NULL; buf = NULL;
bufsize = 0;
buftmp = strlen(TESTDATA1); buftmp = strlen(TESTDATA1);
buf = calloc(buftmp, sizeof(char)); buf = calloc(buftmp, sizeof(char));
@ -433,6 +437,35 @@ static void test_mtime(void) {
remove(ZIPNAME); remove(ZIPNAME);
} }
static void test_unix_permissions(void) {
#if defined(_WIN64) || defined(_WIN32) || defined(__WIN32__)
#else
// UNIX or APPLE
struct MZ_FILE_STAT_STRUCT file_stats;
remove(ZIPNAME);
struct zip_t *zip = zip_open(ZIPNAME, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w');
assert(zip != NULL);
assert(0 == zip_entry_open(zip, RFILE));
assert(0 == zip_entry_write(zip, TESTDATA1, strlen(TESTDATA1)));
assert(0 == zip_entry_close(zip));
zip_close(zip);
remove(RFILE);
assert(0 == zip_extract(ZIPNAME, ".", NULL, NULL));
assert(0 == MZ_FILE_STAT(RFILE, &file_stats));
assert(UNIXMODE == file_stats.st_mode);
remove(RFILE);
remove(ZIPNAME);
#endif
}
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
UNUSED(argc); UNUSED(argc);
UNUSED(argv); UNUSED(argv);
@ -453,6 +486,7 @@ int main(int argc, char *argv[]) {
test_write_permissions(); test_write_permissions();
test_exe_permissions(); test_exe_permissions();
test_mtime(); test_mtime();
test_unix_permissions();
remove(ZIPNAME); remove(ZIPNAME);
return 0; return 0;

View File

@ -23,16 +23,39 @@ int main(int argc, char *argv[]) {
uint step = 0; uint step = 0;
int cmp_status; int cmp_status;
uLong src_len = (uLong)strlen(s_pStr); uLong src_len = (uLong)strlen(s_pStr);
uLong cmp_len = compressBound(src_len);
uLong uncomp_len = src_len; uLong uncomp_len = src_len;
uLong cmp_len;
uint8 *pCmp, *pUncomp; uint8 *pCmp, *pUncomp;
size_t sz;
uint total_succeeded = 0; uint total_succeeded = 0;
(void)argc, (void)argv; (void)argc, (void)argv;
printf("miniz.c version: %s\n", MZ_VERSION); printf("miniz.c version: %s\n", MZ_VERSION);
do { do {
pCmp = (uint8 *)tdefl_compress_mem_to_heap(s_pStr, src_len, &cmp_len, 0);
if (!pCmp) {
printf("tdefl_compress_mem_to_heap failed\n");
return EXIT_FAILURE;
}
if (src_len <= cmp_len) {
printf("tdefl_compress_mem_to_heap failed: from %u to %u bytes\n",
(mz_uint32)uncomp_len, (mz_uint32)cmp_len);
free(pCmp);
return EXIT_FAILURE;
}
sz = tdefl_compress_mem_to_mem(pCmp, cmp_len, s_pStr, src_len, 0);
if (sz != cmp_len) {
printf("tdefl_compress_mem_to_mem failed: expected %u, got %u\n",
(mz_uint32)cmp_len, (mz_uint32)sz);
free(pCmp);
return EXIT_FAILURE;
}
// Allocate buffers to hold compressed and uncompressed data. // Allocate buffers to hold compressed and uncompressed data.
free(pCmp);
cmp_len = compressBound(src_len);
pCmp = (mz_uint8 *)malloc((size_t)cmp_len); pCmp = (mz_uint8 *)malloc((size_t)cmp_len);
pUncomp = (mz_uint8 *)malloc((size_t)src_len); pUncomp = (mz_uint8 *)malloc((size_t)src_len);
if ((!pCmp) || (!pUncomp)) { if ((!pCmp) || (!pUncomp)) {

View File

@ -41,9 +41,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
/** @file Definition of the base class for all importer worker classes. */ /** @file Definition of the base class for all importer worker classes. */
#pragma once
#ifndef INCLUDED_AI_BASEIMPORTER_H #ifndef INCLUDED_AI_BASEIMPORTER_H
#define INCLUDED_AI_BASEIMPORTER_H #define INCLUDED_AI_BASEIMPORTER_H
#ifdef __GNUC__
# pragma GCC system_header
#endif
#include "Exceptional.h" #include "Exceptional.h"
#include <vector> #include <vector>
@ -191,16 +196,13 @@ public:
/** /**
* Assimp Importer * Assimp Importer
* unit conversions available * unit conversions available
* if you need another measurment unit add it below. * NOTE: Valid options are initialised in the
* it's currently defined in assimp that we prefer meters. * constructor in the implementation file to
* work around a VS2013 compiler bug if support
* for that compiler is dropped in the future
* initialisation can be moved back here
* */ * */
std::map<ImporterUnits, double> importerUnits = { std::map<ImporterUnits, double> importerUnits;
{ImporterUnits::M, 1},
{ImporterUnits::CM, 0.01},
{ImporterUnits::MM, 0.001},
{ImporterUnits::INCHES, 0.0254},
{ImporterUnits::FEET, 0.3048}
};
virtual void SetApplicationUnits( const ImporterUnits& unit ) virtual void SetApplicationUnits( const ImporterUnits& unit )
{ {

View File

@ -46,10 +46,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
* Used for file formats which embed their textures into the model file. * Used for file formats which embed their textures into the model file.
*/ */
#pragma once
#ifndef AI_BITMAP_H_INC #ifndef AI_BITMAP_H_INC
#define AI_BITMAP_H_INC #define AI_BITMAP_H_INC
#ifdef __GNUC__
# pragma GCC system_header
#endif
#include "defs.h" #include "defs.h"
#include <stdint.h> #include <stdint.h>
#include <cstddef> #include <cstddef>

View File

@ -42,9 +42,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/** @file Helper class tp perform various byte oder swappings /** @file Helper class tp perform various byte oder swappings
(e.g. little to big endian) */ (e.g. little to big endian) */
#pragma once
#ifndef AI_BYTESWAPPER_H_INC #ifndef AI_BYTESWAPPER_H_INC
#define AI_BYTESWAPPER_H_INC #define AI_BYTESWAPPER_H_INC
#ifdef __GNUC__
# pragma GCC system_header
#endif
#include <assimp/ai_assert.h> #include <assimp/ai_assert.h>
#include <assimp/types.h> #include <assimp/types.h>
#include <stdint.h> #include <stdint.h>

View File

@ -43,16 +43,26 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/** @file CreateAnimMesh.h /** @file CreateAnimMesh.h
* Create AnimMesh from Mesh * Create AnimMesh from Mesh
*/ */
#pragma once
#ifndef INCLUDED_AI_CREATE_ANIM_MESH_H #ifndef INCLUDED_AI_CREATE_ANIM_MESH_H
#define INCLUDED_AI_CREATE_ANIM_MESH_H #define INCLUDED_AI_CREATE_ANIM_MESH_H
#ifdef __GNUC__
# pragma GCC system_header
#endif
#include <assimp/mesh.h> #include <assimp/mesh.h>
namespace Assimp { namespace Assimp {
/** Create aiAnimMesh from aiMesh. */ /**
* Create aiAnimMesh from aiMesh.
* @param mesh The input mesh to create an animated mesh from.
* @return The new created animated mesh.
*/
ASSIMP_API aiAnimMesh *aiCreateAnimMesh(const aiMesh *mesh); ASSIMP_API aiAnimMesh *aiCreateAnimMesh(const aiMesh *mesh);
} // end of namespace Assimp } // end of namespace Assimp
#endif // INCLUDED_AI_CREATE_ANIM_MESH_H #endif // INCLUDED_AI_CREATE_ANIM_MESH_H

View File

@ -41,9 +41,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
/** @file Default file I/O using fXXX()-family of functions */ /** @file Default file I/O using fXXX()-family of functions */
#pragma once
#ifndef AI_DEFAULTIOSTREAM_H_INC #ifndef AI_DEFAULTIOSTREAM_H_INC
#define AI_DEFAULTIOSTREAM_H_INC #define AI_DEFAULTIOSTREAM_H_INC
#ifdef __GNUC__
# pragma GCC system_header
#endif
#include <stdio.h> #include <stdio.h>
#include <assimp/IOStream.hpp> #include <assimp/IOStream.hpp>
#include <assimp/importerdesc.h> #include <assimp/importerdesc.h>
@ -57,8 +62,7 @@ namespace Assimp {
//! @note An instance of this class can exist without a valid file handle //! @note An instance of this class can exist without a valid file handle
//! attached to it. All calls fail, but the instance can nevertheless be //! attached to it. All calls fail, but the instance can nevertheless be
//! used with no restrictions. //! used with no restrictions.
class ASSIMP_API DefaultIOStream : public IOStream class ASSIMP_API DefaultIOStream : public IOStream {
{
friend class DefaultIOSystem; friend class DefaultIOSystem;
#if __ANDROID__ #if __ANDROID__
# if __ANDROID_API__ > 9 # if __ANDROID_API__ > 9
@ -82,7 +86,6 @@ public:
size_t pSize, size_t pSize,
size_t pCount); size_t pCount);
// ------------------------------------------------------------------- // -------------------------------------------------------------------
/// Write to stream /// Write to stream
size_t Write(const void* pvBuffer, size_t Write(const void* pvBuffer,
@ -107,16 +110,13 @@ public:
void Flush(); void Flush();
private: private:
// File data-structure, using clib
FILE* mFile; FILE* mFile;
// Filename
std::string mFilename; std::string mFilename;
// Cached file size
mutable size_t mCachedSize; mutable size_t mCachedSize;
}; };
// ---------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------
inline AI_FORCE_INLINE
DefaultIOStream::DefaultIOStream() AI_NO_EXCEPT DefaultIOStream::DefaultIOStream() AI_NO_EXCEPT
: mFile(nullptr) : mFile(nullptr)
, mFilename("") , mFilename("")
@ -125,7 +125,7 @@ DefaultIOStream::DefaultIOStream() AI_NO_EXCEPT
} }
// ---------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------
inline AI_FORCE_INLINE
DefaultIOStream::DefaultIOStream (FILE* pFile, const std::string &strFilename) DefaultIOStream::DefaultIOStream (FILE* pFile, const std::string &strFilename)
: mFile(pFile) : mFile(pFile)
, mFilename(strFilename) , mFilename(strFilename)
@ -137,4 +137,3 @@ DefaultIOStream::DefaultIOStream (FILE* pFile, const std::string &strFilename)
} // ns assimp } // ns assimp
#endif //!!AI_DEFAULTIOSTREAM_H_INC #endif //!!AI_DEFAULTIOSTREAM_H_INC

View File

@ -41,9 +41,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
/** @file Default implementation of IOSystem using the standard C file functions */ /** @file Default implementation of IOSystem using the standard C file functions */
#pragma once
#ifndef AI_DEFAULTIOSYSTEM_H_INC #ifndef AI_DEFAULTIOSYSTEM_H_INC
#define AI_DEFAULTIOSYSTEM_H_INC #define AI_DEFAULTIOSYSTEM_H_INC
#ifdef __GNUC__
# pragma GCC system_header
#endif
#include <assimp/IOSystem.hpp> #include <assimp/IOSystem.hpp>
namespace Assimp { namespace Assimp {

View File

@ -38,6 +38,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------- ----------------------------------------------------------------------
*/ */
#pragma once
#ifndef AI_DEFINES_H_INC
#define AI_DEFINES_H_INC
#ifdef __GNUC__
# pragma GCC system_header
#endif
// We need those constants, workaround for any platforms where nobody defined them yet // We need those constants, workaround for any platforms where nobody defined them yet
#if (!defined SIZE_MAX) #if (!defined SIZE_MAX)
# define SIZE_MAX (~((size_t)0)) # define SIZE_MAX (~((size_t)0))
@ -47,3 +55,4 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# define UINT_MAX (~((unsigned int)0)) # define UINT_MAX (~((unsigned int)0))
#endif #endif
#endif // AI_DEINES_H_INC

View File

@ -38,11 +38,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------- ----------------------------------------------------------------------
*/ */
#ifndef INCLUDED_EXCEPTIONAL_H #pragma once
#define INCLUDED_EXCEPTIONAL_H #ifndef AI_INCLUDED_EXCEPTIONAL_H
#define AI_INCLUDED_EXCEPTIONAL_H
#ifdef __GNUC__
# pragma GCC system_header
#endif
#include <stdexcept> #include <stdexcept>
#include <assimp/DefaultIOStream.h> #include <assimp/DefaultIOStream.h>
using std::runtime_error; using std::runtime_error;
#ifdef _MSC_VER #ifdef _MSC_VER
@ -53,17 +59,14 @@ using std::runtime_error;
/** FOR IMPORTER PLUGINS ONLY: Simple exception class to be thrown if an /** FOR IMPORTER PLUGINS ONLY: Simple exception class to be thrown if an
* unrecoverable error occurs while importing. Loading APIs return * unrecoverable error occurs while importing. Loading APIs return
* NULL instead of a valid aiScene then. */ * NULL instead of a valid aiScene then. */
class DeadlyImportError class DeadlyImportError : public runtime_error {
: public runtime_error
{
public: public:
/** Constructor with arguments */ /** Constructor with arguments */
explicit DeadlyImportError( const std::string& errorText) explicit DeadlyImportError( const std::string& errorText)
: runtime_error(errorText) : runtime_error(errorText) {
{ // empty
} }
private:
}; };
typedef DeadlyImportError DeadlyExportError; typedef DeadlyImportError DeadlyExportError;
@ -84,7 +87,7 @@ struct ExceptionSwallower {
template <typename T> template <typename T>
struct ExceptionSwallower<T*> { struct ExceptionSwallower<T*> {
T* operator ()() const { T* operator ()() const {
return NULL; return nullptr;
} }
}; };
@ -122,4 +125,4 @@ struct ExceptionSwallower<void> {
}\ }\
} }
#endif // INCLUDED_EXCEPTIONAL_H #endif // AI_INCLUDED_EXCEPTIONAL_H

View File

@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2019, assimp team Copyright (c) 2006-2019, assimp team
All rights reserved. All rights reserved.
Redistribution and use of this software in source and binary forms, Redistribution and use of this software in source and binary forms,
@ -48,6 +46,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef AI_EXPORT_HPP_INC #ifndef AI_EXPORT_HPP_INC
#define AI_EXPORT_HPP_INC #define AI_EXPORT_HPP_INC
#ifdef __GNUC__
# pragma GCC system_header
#endif
#ifndef ASSIMP_BUILD_NO_EXPORT #ifndef ASSIMP_BUILD_NO_EXPORT
#include "cexport.h" #include "cexport.h"

View File

@ -40,12 +40,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------- ----------------------------------------------------------------------
*/ */
#pragma once
#ifndef AI_GENERIC_PROPERTY_H_INCLUDED #ifndef AI_GENERIC_PROPERTY_H_INCLUDED
#define AI_GENERIC_PROPERTY_H_INCLUDED #define AI_GENERIC_PROPERTY_H_INCLUDED
#ifdef __GNUC__
# pragma GCC system_header
#endif
#include <assimp/Importer.hpp> #include <assimp/Importer.hpp>
#include <assimp/ai_assert.h> #include <assimp/ai_assert.h>
#include "Hash.h" #include <assimp/Hash.h>
#include <map> #include <map>

View File

@ -39,10 +39,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------- ----------------------------------------------------------------------
*/ */
#pragma once
#ifndef AI_HASH_H_INCLUDED #ifndef AI_HASH_H_INCLUDED
#define AI_HASH_H_INCLUDED #define AI_HASH_H_INCLUDED
#ifdef __GNUC__
# pragma GCC system_header
#endif
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>

View File

@ -48,7 +48,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef AI_IOSTREAM_H_INC #ifndef AI_IOSTREAM_H_INC
#define AI_IOSTREAM_H_INC #define AI_IOSTREAM_H_INC
#include "types.h" #ifdef __GNUC__
# pragma GCC system_header
#endif
#include <assimp/types.h>
#ifndef __cplusplus #ifndef __cplusplus
# error This header requires C++ to be used. aiFileIO.h is the \ # error This header requires C++ to be used. aiFileIO.h is the \
@ -125,13 +129,13 @@ public:
}; //! class IOStream }; //! class IOStream
// ---------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------
inline AI_FORCE_INLINE
IOStream::IOStream() AI_NO_EXCEPT { IOStream::IOStream() AI_NO_EXCEPT {
// empty // empty
} }
// ---------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------
inline AI_FORCE_INLINE
IOStream::~IOStream() { IOStream::~IOStream() {
// empty // empty
} }

View File

@ -1,5 +1,3 @@
#pragma once
/* /*
Open Asset Import Library (assimp) Open Asset Import Library (assimp)
---------------------------------------------------------------------- ----------------------------------------------------------------------
@ -42,10 +40,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------- ----------------------------------------------------------------------
*/ */
#pragma once
#ifndef AI_IOSTREAMBUFFER_H_INC
#define AI_IOSTREAMBUFFER_H_INC
#ifdef __GNUC__
# pragma GCC system_header
#endif
#include <assimp/types.h> #include <assimp/types.h>
#include <assimp/IOStream.hpp> #include <assimp/IOStream.hpp>
#include <assimp/ParsingUtils.h>
#include "ParsingUtils.h"
#include <vector> #include <vector>
@ -124,7 +129,7 @@ private:
}; };
template<class T> template<class T>
inline AI_FORCE_INLINE
IOStreamBuffer<T>::IOStreamBuffer( size_t cache ) IOStreamBuffer<T>::IOStreamBuffer( size_t cache )
: m_stream( nullptr ) : m_stream( nullptr )
, m_filesize( 0 ) , m_filesize( 0 )
@ -138,13 +143,13 @@ IOStreamBuffer<T>::IOStreamBuffer( size_t cache )
} }
template<class T> template<class T>
inline AI_FORCE_INLINE
IOStreamBuffer<T>::~IOStreamBuffer() { IOStreamBuffer<T>::~IOStreamBuffer() {
// empty // empty
} }
template<class T> template<class T>
inline AI_FORCE_INLINE
bool IOStreamBuffer<T>::open( IOStream *stream ) { bool IOStreamBuffer<T>::open( IOStream *stream ) {
// file still opened! // file still opened!
if ( nullptr != m_stream ) { if ( nullptr != m_stream ) {
@ -174,7 +179,7 @@ bool IOStreamBuffer<T>::open( IOStream *stream ) {
} }
template<class T> template<class T>
inline AI_FORCE_INLINE
bool IOStreamBuffer<T>::close() { bool IOStreamBuffer<T>::close() {
if ( nullptr == m_stream ) { if ( nullptr == m_stream ) {
return false; return false;
@ -192,19 +197,19 @@ bool IOStreamBuffer<T>::close() {
} }
template<class T> template<class T>
inline AI_FORCE_INLINE
size_t IOStreamBuffer<T>::size() const { size_t IOStreamBuffer<T>::size() const {
return m_filesize; return m_filesize;
} }
template<class T> template<class T>
inline AI_FORCE_INLINE
size_t IOStreamBuffer<T>::cacheSize() const { size_t IOStreamBuffer<T>::cacheSize() const {
return m_cacheSize; return m_cacheSize;
} }
template<class T> template<class T>
inline AI_FORCE_INLINE
bool IOStreamBuffer<T>::readNextBlock() { bool IOStreamBuffer<T>::readNextBlock() {
m_stream->Seek( m_filePos, aiOrigin_SET ); m_stream->Seek( m_filePos, aiOrigin_SET );
size_t readLen = m_stream->Read( &m_cache[ 0 ], sizeof( T ), m_cacheSize ); size_t readLen = m_stream->Read( &m_cache[ 0 ], sizeof( T ), m_cacheSize );
@ -222,25 +227,25 @@ bool IOStreamBuffer<T>::readNextBlock() {
} }
template<class T> template<class T>
inline AI_FORCE_INLINE
size_t IOStreamBuffer<T>::getNumBlocks() const { size_t IOStreamBuffer<T>::getNumBlocks() const {
return m_numBlocks; return m_numBlocks;
} }
template<class T> template<class T>
inline AI_FORCE_INLINE
size_t IOStreamBuffer<T>::getCurrentBlockIndex() const { size_t IOStreamBuffer<T>::getCurrentBlockIndex() const {
return m_blockIdx; return m_blockIdx;
} }
template<class T> template<class T>
inline AI_FORCE_INLINE
size_t IOStreamBuffer<T>::getFilePos() const { size_t IOStreamBuffer<T>::getFilePos() const {
return m_filePos; return m_filePos;
} }
template<class T> template<class T>
inline AI_FORCE_INLINE
bool IOStreamBuffer<T>::getNextDataLine( std::vector<T> &buffer, T continuationToken ) { bool IOStreamBuffer<T>::getNextDataLine( std::vector<T> &buffer, T continuationToken ) {
buffer.resize( m_cacheSize ); buffer.resize( m_cacheSize );
if ( m_cachePos >= m_cacheSize || 0 == m_filePos ) { if ( m_cachePos >= m_cacheSize || 0 == m_filePos ) {
@ -289,13 +294,13 @@ bool IOStreamBuffer<T>::getNextDataLine( std::vector<T> &buffer, T continuationT
return true; return true;
} }
static inline static AI_FORCE_INLINE
bool isEndOfCache( size_t pos, size_t cacheSize ) { bool isEndOfCache( size_t pos, size_t cacheSize ) {
return ( pos == cacheSize ); return ( pos == cacheSize );
} }
template<class T> template<class T>
inline AI_FORCE_INLINE
bool IOStreamBuffer<T>::getNextLine(std::vector<T> &buffer) { bool IOStreamBuffer<T>::getNextLine(std::vector<T> &buffer) {
buffer.resize(m_cacheSize); buffer.resize(m_cacheSize);
if ( isEndOfCache( m_cachePos, m_cacheSize ) || 0 == m_filePos) { if ( isEndOfCache( m_cachePos, m_cacheSize ) || 0 == m_filePos) {
@ -335,7 +340,7 @@ bool IOStreamBuffer<T>::getNextLine(std::vector<T> &buffer) {
} }
template<class T> template<class T>
inline AI_FORCE_INLINE
bool IOStreamBuffer<T>::getNextBlock( std::vector<T> &buffer) { bool IOStreamBuffer<T>::getNextBlock( std::vector<T> &buffer) {
// Return the last block-value if getNextLine was used before // Return the last block-value if getNextLine was used before
if ( 0 != m_cachePos ) { if ( 0 != m_cachePos ) {
@ -353,3 +358,5 @@ bool IOStreamBuffer<T>::getNextBlock( std::vector<T> &buffer) {
} }
} // !ns Assimp } // !ns Assimp
#endif // AI_IOSTREAMBUFFER_H_INC

View File

@ -50,6 +50,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef AI_IOSYSTEM_H_INC #ifndef AI_IOSYSTEM_H_INC
#define AI_IOSYSTEM_H_INC #define AI_IOSYSTEM_H_INC
#ifdef __GNUC__
# pragma GCC system_header
#endif
#ifndef __cplusplus #ifndef __cplusplus
# error This header requires C++ to be used. aiFileIO.h is the \ # error This header requires C++ to be used. aiFileIO.h is the \
corresponding C interface. corresponding C interface.

View File

@ -48,6 +48,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef AI_ASSIMP_HPP_INC #ifndef AI_ASSIMP_HPP_INC
#define AI_ASSIMP_HPP_INC #define AI_ASSIMP_HPP_INC
#ifdef __GNUC__
# pragma GCC system_header
#endif
#ifndef __cplusplus #ifndef __cplusplus
# error This header requires C++ to be used. Use assimp.h for plain C. # error This header requires C++ to be used. Use assimp.h for plain C.
#endif // __cplusplus #endif // __cplusplus

View File

@ -48,9 +48,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef INCLUDED_LINE_SPLITTER_H #ifndef INCLUDED_LINE_SPLITTER_H
#define INCLUDED_LINE_SPLITTER_H #define INCLUDED_LINE_SPLITTER_H
#ifdef __GNUC__
# pragma GCC system_header
#endif
#include <stdexcept> #include <stdexcept>
#include "StreamReader.h" #include <assimp/StreamReader.h>
#include "ParsingUtils.h" #include <assimp/ParsingUtils.h>
namespace Assimp { namespace Assimp {
@ -140,7 +144,7 @@ private:
bool mSwallow, mSkip_empty_lines, mTrim; bool mSwallow, mSkip_empty_lines, mTrim;
}; };
inline AI_FORCE_INLINE
LineSplitter::LineSplitter(StreamReaderLE& stream, bool skip_empty_lines, bool trim ) LineSplitter::LineSplitter(StreamReaderLE& stream, bool skip_empty_lines, bool trim )
: mIdx(0) : mIdx(0)
, mCur() , mCur()
@ -153,12 +157,12 @@ LineSplitter::LineSplitter(StreamReaderLE& stream, bool skip_empty_lines, bool t
mIdx = 0; mIdx = 0;
} }
inline AI_FORCE_INLINE
LineSplitter::~LineSplitter() { LineSplitter::~LineSplitter() {
// empty // empty
} }
inline AI_FORCE_INLINE
LineSplitter& LineSplitter::operator++() { LineSplitter& LineSplitter::operator++() {
if (mSwallow) { if (mSwallow) {
mSwallow = false; mSwallow = false;
@ -199,12 +203,12 @@ LineSplitter& LineSplitter::operator++() {
return *this; return *this;
} }
inline AI_FORCE_INLINE
LineSplitter &LineSplitter::operator++(int) { LineSplitter &LineSplitter::operator++(int) {
return ++(*this); return ++(*this);
} }
inline AI_FORCE_INLINE
const char *LineSplitter::operator[] (size_t idx) const { const char *LineSplitter::operator[] (size_t idx) const {
const char* s = operator->()->c_str(); const char* s = operator->()->c_str();
@ -222,7 +226,7 @@ const char *LineSplitter::operator[] (size_t idx) const {
} }
template <size_t N> template <size_t N>
inline AI_FORCE_INLINE
void LineSplitter::get_tokens(const char* (&tokens)[N]) const { void LineSplitter::get_tokens(const char* (&tokens)[N]) const {
const char* s = operator->()->c_str(); const char* s = operator->()->c_str();
@ -238,44 +242,44 @@ void LineSplitter::get_tokens(const char* (&tokens)[N]) const {
} }
} }
inline AI_FORCE_INLINE
const std::string* LineSplitter::operator -> () const { const std::string* LineSplitter::operator -> () const {
return &mCur; return &mCur;
} }
inline AI_FORCE_INLINE
std::string LineSplitter::operator* () const { std::string LineSplitter::operator* () const {
return mCur; return mCur;
} }
inline AI_FORCE_INLINE
LineSplitter::operator bool() const { LineSplitter::operator bool() const {
return mStream.GetRemainingSize() > 0; return mStream.GetRemainingSize() > 0;
} }
inline AI_FORCE_INLINE
LineSplitter::operator line_idx() const { LineSplitter::operator line_idx() const {
return mIdx; return mIdx;
} }
inline AI_FORCE_INLINE
LineSplitter::line_idx LineSplitter::get_index() const { LineSplitter::line_idx LineSplitter::get_index() const {
return mIdx; return mIdx;
} }
inline AI_FORCE_INLINE
StreamReaderLE &LineSplitter::get_stream() { StreamReaderLE &LineSplitter::get_stream() {
return mStream; return mStream;
} }
inline AI_FORCE_INLINE
bool LineSplitter::match_start(const char* check) { bool LineSplitter::match_start(const char* check) {
const size_t len = ::strlen(check); const size_t len = ::strlen(check);
return len <= mCur.length() && std::equal(check, check + len, mCur.begin()); return len <= mCur.length() && std::equal(check, check + len, mCur.begin());
} }
inline AI_FORCE_INLINE
void LineSplitter::swallow_next_increment() { void LineSplitter::swallow_next_increment() {
mSwallow = true; mSwallow = true;
} }

View File

@ -43,9 +43,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/** @file LogAux.h /** @file LogAux.h
* @brief Common logging usage patterns for importer implementations * @brief Common logging usage patterns for importer implementations
*/ */
#pragma once
#ifndef INCLUDED_AI_LOGAUX_H #ifndef INCLUDED_AI_LOGAUX_H
#define INCLUDED_AI_LOGAUX_H #define INCLUDED_AI_LOGAUX_H
#ifdef __GNUC__
# pragma GCC system_header
#endif
#include <assimp/TinyFormatter.h> #include <assimp/TinyFormatter.h>
#include <assimp/Exceptional.h> #include <assimp/Exceptional.h>
#include <assimp/DefaultLogger.hpp> #include <assimp/DefaultLogger.hpp>

View File

@ -41,6 +41,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma once #pragma once
#ifdef __GNUC__
# pragma GCC system_header
#endif
/** @file MathFunctions.h /** @file MathFunctions.h
* @brief Implementation of math utility functions. * @brief Implementation of math utility functions.
* *

View File

@ -42,12 +42,18 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/** @file MemoryIOWrapper.h /** @file MemoryIOWrapper.h
* Handy IOStream/IOSystem implemetation to read directly from a memory buffer */ * Handy IOStream/IOSystem implemetation to read directly from a memory buffer */
#pragma once
#ifndef AI_MEMORYIOSTREAM_H_INC #ifndef AI_MEMORYIOSTREAM_H_INC
#define AI_MEMORYIOSTREAM_H_INC #define AI_MEMORYIOSTREAM_H_INC
#ifdef __GNUC__
# pragma GCC system_header
#endif
#include <assimp/IOStream.hpp> #include <assimp/IOStream.hpp>
#include <assimp/IOSystem.hpp> #include <assimp/IOSystem.hpp>
#include <assimp/ai_assert.h> #include <assimp/ai_assert.h>
#include <stdint.h> #include <stdint.h>
namespace Assimp { namespace Assimp {

View File

@ -44,11 +44,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/** @file ParsingUtils.h /** @file ParsingUtils.h
* @brief Defines helper functions for text parsing * @brief Defines helper functions for text parsing
*/ */
#pragma once
#ifndef AI_PARSING_UTILS_H_INC #ifndef AI_PARSING_UTILS_H_INC
#define AI_PARSING_UTILS_H_INC #define AI_PARSING_UTILS_H_INC
#include "StringComparison.h" #ifdef __GNUC__
#include "StringUtils.h" # pragma GCC system_header
#endif
#include <assimp/StringComparison.h>
#include <assimp/StringUtils.h>
#include <assimp/defs.h> #include <assimp/defs.h>
namespace Assimp { namespace Assimp {

View File

@ -43,12 +43,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/** @file Profiler.h /** @file Profiler.h
* @brief Utility to measure the respective runtime of each import step * @brief Utility to measure the respective runtime of each import step
*/ */
#ifndef INCLUDED_PROFILER_H #pragma once
#define INCLUDED_PROFILER_H #ifndef AI_INCLUDED_PROFILER_H
#define AI_INCLUDED_PROFILER_H
#ifdef __GNUC__
# pragma GCC system_header
#endif
#include <chrono> #include <chrono>
#include <assimp/DefaultLogger.hpp> #include <assimp/DefaultLogger.hpp>
#include "TinyFormatter.h" #include <assimp/TinyFormatter.h>
#include <map> #include <map>
@ -67,7 +72,6 @@ public:
// empty // empty
} }
public:
/** Start a named timer */ /** Start a named timer */
void BeginRegion(const std::string& region) { void BeginRegion(const std::string& region) {
@ -95,5 +99,5 @@ private:
} }
} }
#endif #endif // AI_INCLUDED_PROFILER_H

View File

@ -47,7 +47,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef AI_PROGRESSHANDLER_H_INC #ifndef AI_PROGRESSHANDLER_H_INC
#define AI_PROGRESSHANDLER_H_INC #define AI_PROGRESSHANDLER_H_INC
#include "types.h" #ifdef __GNUC__
# pragma GCC system_header
#endif
#include <assimp/types.h>
namespace Assimp { namespace Assimp {

View File

@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2019, assimp team Copyright (c) 2006-2019, assimp team
All rights reserved. All rights reserved.
Redistribution and use of this software in source and binary forms, Redistribution and use of this software in source and binary forms,
@ -43,9 +42,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/** @file Declares a helper class, "CommentRemover", which can be /** @file Declares a helper class, "CommentRemover", which can be
* used to remove comments (single and multi line) from a text file. * used to remove comments (single and multi line) from a text file.
*/ */
#pragma once
#ifndef AI_REMOVE_COMMENTS_H_INC #ifndef AI_REMOVE_COMMENTS_H_INC
#define AI_REMOVE_COMMENTS_H_INC #define AI_REMOVE_COMMENTS_H_INC
#ifdef __GNUC__
# pragma GCC system_header
#endif
#include <assimp/defs.h> #include <assimp/defs.h>
@ -58,8 +61,7 @@ namespace Assimp {
* to those in C or C++ so this code has been moved to a separate * to those in C or C++ so this code has been moved to a separate
* module. * module.
*/ */
class ASSIMP_API CommentRemover class ASSIMP_API CommentRemover {
{
// class cannot be instanced // class cannot be instanced
CommentRemover() {} CommentRemover() {}

View File

@ -42,9 +42,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/** Small helper classes to optimize finding vertices close to a given location /** Small helper classes to optimize finding vertices close to a given location
*/ */
#pragma once
#ifndef AI_D3DSSPATIALSORT_H_INC #ifndef AI_D3DSSPATIALSORT_H_INC
#define AI_D3DSSPATIALSORT_H_INC #define AI_D3DSSPATIALSORT_H_INC
#ifdef __GNUC__
# pragma GCC system_header
#endif
#include <assimp/types.h> #include <assimp/types.h>
#include <vector> #include <vector>
#include <stdint.h> #include <stdint.h>

View File

@ -43,17 +43,22 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/** @file Declares a helper class, "SceneCombiner" providing various /** @file Declares a helper class, "SceneCombiner" providing various
* utilities to merge scenes. * utilities to merge scenes.
*/ */
#pragma once
#ifndef AI_SCENE_COMBINER_H_INC #ifndef AI_SCENE_COMBINER_H_INC
#define AI_SCENE_COMBINER_H_INC #define AI_SCENE_COMBINER_H_INC
#ifdef __GNUC__
# pragma GCC system_header
#endif
#include <assimp/ai_assert.h> #include <assimp/ai_assert.h>
#include <assimp/types.h> #include <assimp/types.h>
#include <assimp/Defines.h> #include <assimp/Defines.h>
#include <stddef.h> #include <stddef.h>
#include <set> #include <set>
#include <list> #include <list>
#include <stdint.h> #include <stdint.h>
#include <vector> #include <vector>
struct aiScene; struct aiScene;
@ -68,6 +73,7 @@ struct aiMesh;
struct aiAnimMesh; struct aiAnimMesh;
struct aiAnimation; struct aiAnimation;
struct aiNodeAnim; struct aiNodeAnim;
struct aiMeshMorphAnim;
namespace Assimp { namespace Assimp {
@ -372,6 +378,7 @@ public:
static void Copy (aiBone** dest, const aiBone* src); static void Copy (aiBone** dest, const aiBone* src);
static void Copy (aiLight** dest, const aiLight* src); static void Copy (aiLight** dest, const aiLight* src);
static void Copy (aiNodeAnim** dest, const aiNodeAnim* src); static void Copy (aiNodeAnim** dest, const aiNodeAnim* src);
static void Copy (aiMeshMorphAnim** dest, const aiMeshMorphAnim* src);
static void Copy (aiMetadata** dest, const aiMetadata* src); static void Copy (aiMetadata** dest, const aiMetadata* src);
// recursive, of course // recursive, of course

View File

@ -47,9 +47,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* for animation skeletons. * for animation skeletons.
*/ */
#pragma once
#ifndef AI_SKELETONMESHBUILDER_H_INC #ifndef AI_SKELETONMESHBUILDER_H_INC
#define AI_SKELETONMESHBUILDER_H_INC #define AI_SKELETONMESHBUILDER_H_INC
#ifdef __GNUC__
# pragma GCC system_header
#endif
#include <vector> #include <vector>
#include <assimp/mesh.h> #include <assimp/mesh.h>

View File

@ -43,10 +43,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/** @file Defines the helper data structures for importing 3DS files. /** @file Defines the helper data structures for importing 3DS files.
http://www.jalix.org/ressources/graphics/3DS/_unofficials/3ds-unofficial.txt */ http://www.jalix.org/ressources/graphics/3DS/_unofficials/3ds-unofficial.txt */
#pragma once
#ifndef AI_SMOOTHINGGROUPS_H_INC #ifndef AI_SMOOTHINGGROUPS_H_INC
#define AI_SMOOTHINGGROUPS_H_INC #define AI_SMOOTHINGGROUPS_H_INC
#ifdef __GNUC__
# pragma GCC system_header
#endif
#include <assimp/vector3.h> #include <assimp/vector3.h>
#include <stdint.h> #include <stdint.h>
#include <vector> #include <vector>

View File

@ -41,13 +41,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/** @file Generation of normal vectors basing on smoothing groups */ /** @file Generation of normal vectors basing on smoothing groups */
#pragma once
#ifndef AI_SMOOTHINGGROUPS_INL_INCLUDED #ifndef AI_SMOOTHINGGROUPS_INL_INCLUDED
#define AI_SMOOTHINGGROUPS_INL_INCLUDED #define AI_SMOOTHINGGROUPS_INL_INCLUDED
// internal headers #ifdef __GNUC__
# pragma GCC system_header
#endif
#include <assimp/SGSpatialSort.h> #include <assimp/SGSpatialSort.h>
// CRT header
#include <algorithm> #include <algorithm>
using namespace Assimp; using namespace Assimp;

View File

@ -41,9 +41,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
/** Small helper classes to optimise finding vertizes close to a given location */ /** Small helper classes to optimise finding vertizes close to a given location */
#pragma once
#ifndef AI_SPATIALSORT_H_INC #ifndef AI_SPATIALSORT_H_INC
#define AI_SPATIALSORT_H_INC #define AI_SPATIALSORT_H_INC
#ifdef __GNUC__
# pragma GCC system_header
#endif
#include <vector> #include <vector>
#include <assimp/types.h> #include <assimp/types.h>

Some files were not shown because too many files have changed in this diff Show More