Merge branch 'master' into python-fixes

pull/2813/head
Kim Kulling 2019-12-08 12:58:50 +01:00 committed by GitHub
commit 6b5384afa6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
194 changed files with 14630 additions and 3034 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 /Zi /Od")
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

@ -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,
@ -78,7 +76,6 @@ static const aiImporterDesc desc = {
"b3d" "b3d"
}; };
// (fixme, Aramis) quick workaround to get rid of all those signed to unsigned warnings
#ifdef _MSC_VER #ifdef _MSC_VER
# pragma warning (disable: 4018) # pragma warning (disable: 4018)
#endif #endif
@ -86,10 +83,8 @@ static const aiImporterDesc desc = {
//#define DEBUG_B3D //#define DEBUG_B3D
template<typename T> template<typename T>
void DeleteAllBarePointers(std::vector<T>& x) void DeleteAllBarePointers(std::vector<T>& x) {
{ for(auto p : x) {
for(auto p : x)
{
delete p; delete p;
} }
} }
@ -102,10 +97,14 @@ B3DImporter::~B3DImporter()
bool B3DImporter::CanRead( const std::string& pFile, IOSystem* /*pIOHandler*/, bool /*checkSig*/) const{ bool B3DImporter::CanRead( const std::string& pFile, IOSystem* /*pIOHandler*/, bool /*checkSig*/) const{
size_t pos=pFile.find_last_of( '.' ); size_t pos=pFile.find_last_of( '.' );
if( pos==string::npos ) return false; if( pos==string::npos ) {
return false;
}
string ext=pFile.substr( pos+1 ); string ext=pFile.substr( pos+1 );
if( ext.size()!=3 ) return false; if( ext.size()!=3 ) {
return false;
}
return (ext[0]=='b' || ext[0]=='B') && (ext[1]=='3') && (ext[2]=='d' || ext[2]=='D'); return (ext[0]=='b' || ext[0]=='B') && (ext[1]=='3') && (ext[2]=='d' || ext[2]=='D');
} }
@ -117,30 +116,21 @@ const aiImporterDesc* B3DImporter::GetInfo () const
return &desc; return &desc;
} }
#ifdef DEBUG_B3D
extern "C"{ void _stdcall AllocConsole(); }
#endif
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void B3DImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler){ void B3DImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler){
#ifdef DEBUG_B3D
AllocConsole();
freopen( "conin$","r",stdin );
freopen( "conout$","w",stdout );
freopen( "conout$","w",stderr );
cout<<"Hello world from the B3DImporter!"<<endl;
#endif
std::unique_ptr<IOStream> file( pIOHandler->Open( pFile)); std::unique_ptr<IOStream> file( pIOHandler->Open( pFile));
// Check whether we can read from the file // Check whether we can read from the file
if( file.get() == NULL) if( file.get() == nullptr) {
throw DeadlyImportError( "Failed to open B3D file " + pFile + "."); throw DeadlyImportError( "Failed to open B3D file " + pFile + ".");
}
// check whether the .b3d file is large enough to contain // check whether the .b3d file is large enough to contain
// at least one chunk. // at least one chunk.
size_t fileSize = file->FileSize(); size_t fileSize = file->FileSize();
if( fileSize<8 ) throw DeadlyImportError( "B3D File is too small."); if( fileSize<8 ) {
throw DeadlyImportError( "B3D File is too small.");
}
_pos=0; _pos=0;
_buf.resize( fileSize ); _buf.resize( fileSize );
@ -158,14 +148,17 @@ AI_WONT_RETURN void B3DImporter::Oops(){
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
AI_WONT_RETURN void B3DImporter::Fail( string str ){ AI_WONT_RETURN void B3DImporter::Fail( string str ){
#ifdef DEBUG_B3D #ifdef DEBUG_B3D
cout<<"Error in B3D file data: "<<str<<endl; ASSIMP_LOG_ERROR_F("Error in B3D file data: ", str);
#endif #endif
throw DeadlyImportError( "B3D Importer - error in B3D file data: "+str ); throw DeadlyImportError( "B3D Importer - error in B3D file data: "+str );
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
int B3DImporter::ReadByte(){ int B3DImporter::ReadByte(){
if( _pos<_buf.size() ) return _buf[_pos++]; if( _pos<_buf.size() ) {
return _buf[_pos++];
}
Fail( "EOF" ); Fail( "EOF" );
return 0; return 0;
} }
@ -224,7 +217,9 @@ string B3DImporter::ReadString(){
string str; string str;
while( _pos<_buf.size() ){ while( _pos<_buf.size() ){
char c=(char)ReadByte(); char c=(char)ReadByte();
if( !c ) return str; if( !c ) {
return str;
}
str+=c; str+=c;
} }
Fail( "EOF" ); Fail( "EOF" );
@ -238,7 +233,7 @@ string B3DImporter::ReadChunk(){
tag+=char( ReadByte() ); tag+=char( ReadByte() );
} }
#ifdef DEBUG_B3D #ifdef DEBUG_B3D
// cout<<"ReadChunk:"<<tag<<endl; ASSIMP_LOG_DEBUG_F("ReadChunk: ", tag);
#endif #endif
unsigned sz=(unsigned)ReadInt(); unsigned sz=(unsigned)ReadInt();
_stack.push_back( _pos+sz ); _stack.push_back( _pos+sz );
@ -269,7 +264,6 @@ T *B3DImporter::to_array( const vector<T> &v ){
return p; return p;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
template<class T> template<class T>
T **unique_to_array( vector<std::unique_ptr<T> > &v ){ T **unique_to_array( vector<std::unique_ptr<T> > &v ){
@ -283,7 +277,6 @@ T **unique_to_array( vector<std::unique_ptr<T> > &v ){
return p; return p;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void B3DImporter::ReadTEXS(){ void B3DImporter::ReadTEXS(){
while( ChunkSize() ){ while( ChunkSize() ){
@ -376,9 +369,13 @@ void B3DImporter::ReadVRTS(){
v.vertex=ReadVec3(); v.vertex=ReadVec3();
if( _vflags & 1 ) v.normal=ReadVec3(); if( _vflags & 1 ) {
v.normal=ReadVec3();
}
if( _vflags & 2 ) ReadQuat(); //skip v 4bytes... if( _vflags & 2 ) {
ReadQuat(); //skip v 4bytes...
}
for( int i=0;i<_tcsets;++i ){ for( int i=0;i<_tcsets;++i ){
float t[4]={0,0,0,0}; float t[4]={0,0,0,0};
@ -386,7 +383,9 @@ void B3DImporter::ReadVRTS(){
t[j]=ReadFloat(); t[j]=ReadFloat();
} }
t[1]=1-t[1]; t[1]=1-t[1];
if( !i ) v.texcoords=aiVector3D( t[0],t[1],t[2] ); if( !i ) {
v.texcoords=aiVector3D( t[0],t[1],t[2] );
}
} }
} }
} }
@ -398,7 +397,7 @@ void B3DImporter::ReadTRIS( int v0 ){
matid = 0; matid = 0;
} else if (matid < 0 || matid >= (int)_materials.size()) { } else if (matid < 0 || matid >= (int)_materials.size()) {
#ifdef DEBUG_B3D #ifdef DEBUG_B3D
cout<<"material id="<<matid<<endl; ASSIMP_LOG_ERROR_F("material id=", matid);
#endif #endif
Fail("Bad material id"); Fail("Bad material id");
} }
@ -418,7 +417,7 @@ void B3DImporter::ReadTRIS( int v0 ){
int i2 = ReadInt() + v0; int i2 = ReadInt() + v0;
if (i0 < 0 || i0 >= (int)_vertices.size() || i1 < 0 || i1 >= (int)_vertices.size() || i2 < 0 || i2 >= (int)_vertices.size()) { if (i0 < 0 || i0 >= (int)_vertices.size() || i1 < 0 || i1 >= (int)_vertices.size() || i2 < 0 || i2 >= (int)_vertices.size()) {
#ifdef DEBUG_B3D #ifdef DEBUG_B3D
cout<<"Bad triangle index: i0="<<i0<<", i1="<<i1<<", i2="<<i2<<endl; ASSIMP_LOG_ERROR_F("Bad triangle index: i0=", i0, ", i1=", i1, ", i2=", i2);
#endif #endif
Fail("Bad triangle index"); Fail("Bad triangle index");
continue; continue;
@ -462,19 +461,13 @@ void B3DImporter::ReadBONE( int id ){
} }
Vertex &v = _vertices[vertex]; Vertex &v = _vertices[vertex];
int i; for (int i = 0; i < 4; ++i) {
for( i=0;i<4;++i ){
if (!v.weights[i]) { if (!v.weights[i]) {
v.bones[i] = id; v.bones[i] = id;
v.weights[i] = weight; v.weights[i] = weight;
break; break;
} }
} }
#ifdef DEBUG_B3D
if( i==4 ){
cout<<"Too many bone weights"<<endl;
}
#endif
} }
} }
@ -633,9 +626,13 @@ void B3DImporter::ReadBB3D( aiScene *scene ){
} }
ExitChunk(); ExitChunk();
if( !_nodes.size() ) Fail( "No nodes" ); if( !_nodes.size() ) {
Fail( "No nodes" );
}
if( !_meshes.size() ) Fail( "No meshes" ); if( !_meshes.size() ) {
Fail( "No meshes" );
}
// Fix nodes/meshes/bones // Fix nodes/meshes/bones
for(size_t i=0;i<_nodes.size();++i ){ for(size_t i=0;i<_nodes.size();++i ){
@ -648,8 +645,12 @@ void B3DImporter::ReadBB3D( aiScene *scene ){
int n_verts=mesh->mNumVertices=n_tris * 3; int n_verts=mesh->mNumVertices=n_tris * 3;
aiVector3D *mv=mesh->mVertices=new aiVector3D[ n_verts ],*mn=0,*mc=0; aiVector3D *mv=mesh->mVertices=new aiVector3D[ n_verts ],*mn=0,*mc=0;
if( _vflags & 1 ) mn=mesh->mNormals=new aiVector3D[ n_verts ]; if( _vflags & 1 ) {
if( _tcsets ) mc=mesh->mTextureCoords[0]=new aiVector3D[ n_verts ]; mn=mesh->mNormals=new aiVector3D[ n_verts ];
}
if( _tcsets ) {
mc=mesh->mTextureCoords[0]=new aiVector3D[ n_verts ];
}
aiFace *face=mesh->mFaces; aiFace *face=mesh->mFaces;

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,20 @@ ADD_ASSIMP_IMPORTER( LWS
LWS/LWSLoader.h LWS/LWSLoader.h
) )
ADD_ASSIMP_IMPORTER( M3D
M3D/M3DMaterials.h
M3D/M3DImporter.h
M3D/M3DImporter.cpp
M3D/M3DWrapper.h
M3D/M3DWrapper.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 +684,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 +1074,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

@ -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

@ -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) {
@ -3170,13 +3234,12 @@ void ColladaParser::ReadScene()
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Aborts the file reading with an exception // Aborts the file reading with an exception
AI_WONT_RETURN void ColladaParser::ThrowException(const std::string& pError) const AI_WONT_RETURN void ColladaParser::ThrowException(const std::string& pError) const {
{
throw DeadlyImportError(format() << "Collada: " << mFileName << " - " << pError); throw DeadlyImportError(format() << "Collada: " << mFileName << " - " << pError);
} }
void ColladaParser::ReportWarning(const char* msg, ...)
{ void ColladaParser::ReportWarning(const char* msg, ...) {
ai_assert(NULL != msg); ai_assert(nullptr != msg);
va_list args; va_list args;
va_start(args, msg); va_start(args, msg);
@ -3191,11 +3254,11 @@ void ColladaParser::ReportWarning(const char* msg, ...)
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Skips all data until the end node of the current element // Skips all data until the end node of the current element
void ColladaParser::SkipElement() void ColladaParser::SkipElement() {
{
// nothing to skip if it's an <element /> // nothing to skip if it's an <element />
if (mReader->isEmptyElement()) if (mReader->isEmptyElement()) {
return; return;
}
// reroute // reroute
SkipElement(mReader->getNodeName()); SkipElement(mReader->getNodeName());
@ -3203,63 +3266,75 @@ void ColladaParser::SkipElement()
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Skips all data until the end node of the given element // Skips all data until the end node of the given element
void ColladaParser::SkipElement(const char* pElement) void ColladaParser::SkipElement(const char* pElement) {
{
// copy the current node's name because it'a pointer to the reader's internal buffer, // copy the current node's name because it'a pointer to the reader's internal buffer,
// which is going to change with the upcoming parsing // which is going to change with the upcoming parsing
std::string element = pElement; std::string element = pElement;
while (mReader->read()) while (mReader->read()) {
{ if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) if (mReader->getNodeName() == element) {
if (mReader->getNodeName() == element)
break; break;
} }
} }
}
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Tests for an opening element of the given name, throws an exception if not found // Tests for an opening element of the given name, throws an exception if not found
void ColladaParser::TestOpening(const char* pName) void ColladaParser::TestOpening(const char* pName) {
{
// read element start // read element start
if (!mReader->read()) if (!mReader->read()) {
ThrowException(format() << "Unexpected end of file while beginning of <" << pName << "> element."); ThrowException(format() << "Unexpected end of file while beginning of <" << pName << "> element.");
}
// whitespace in front is ok, just read again if found // whitespace in front is ok, just read again if found
if (mReader->getNodeType() == irr::io::EXN_TEXT) if (mReader->getNodeType() == irr::io::EXN_TEXT) {
if (!mReader->read()) if (!mReader->read()) {
ThrowException(format() << "Unexpected end of file while reading beginning of <" << pName << "> element."); ThrowException(format() << "Unexpected end of file while reading beginning of <" << pName << "> element.");
}
}
if (mReader->getNodeType() != irr::io::EXN_ELEMENT || strcmp(mReader->getNodeName(), pName) != 0) if (mReader->getNodeType() != irr::io::EXN_ELEMENT || strcmp(mReader->getNodeName(), pName) != 0) {
ThrowException(format() << "Expected start of <" << pName << "> element."); ThrowException(format() << "Expected start of <" << pName << "> element.");
} }
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Tests for the closing tag of the given element, throws an exception if not found // Tests for the closing tag of the given element, throws an exception if not found
void ColladaParser::TestClosing(const char* pName) void ColladaParser::TestClosing(const char* pName) {
{ // check if we have an empty (self-closing) element
// check if we're already on the closing tag and return right away if (mReader->isEmptyElement()) {
if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END && strcmp(mReader->getNodeName(), pName) == 0)
return; return;
}
// check if we're already on the closing tag and return right away
if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END && strcmp(mReader->getNodeName(), pName) == 0) {
return;
}
// if not, read some more // if not, read some more
if (!mReader->read()) if (!mReader->read()) {
ThrowException(format() << "Unexpected end of file while reading end of <" << pName << "> element."); ThrowException(format() << "Unexpected end of file while reading end of <" << pName << "> element.");
}
// whitespace in front is ok, just read again if found // whitespace in front is ok, just read again if found
if (mReader->getNodeType() == irr::io::EXN_TEXT) if (mReader->getNodeType() == irr::io::EXN_TEXT) {
if (!mReader->read()) if (!mReader->read()) {
ThrowException(format() << "Unexpected end of file while reading end of <" << pName << "> element."); ThrowException(format() << "Unexpected end of file while reading end of <" << pName << "> element.");
}
}
// but this has the be the closing tag, or we're lost // but this has the be the closing tag, or we're lost
if (mReader->getNodeType() != irr::io::EXN_ELEMENT_END || strcmp(mReader->getNodeName(), pName) != 0) if (mReader->getNodeType() != irr::io::EXN_ELEMENT_END || strcmp(mReader->getNodeName(), pName) != 0) {
ThrowException(format() << "Expected end of <" << pName << "> element."); ThrowException(format() << "Expected end of <" << pName << "> element.");
} }
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns the index of the named attribute or -1 if not found. Does not throw, therefore useful for optional attributes // Returns the index of the named attribute or -1 if not found. Does not throw, therefore useful for optional attributes
int ColladaParser::GetAttribute(const char* pAttr) const int ColladaParser::GetAttribute(const char* pAttr) const {
{
int index = TestAttribute(pAttr); int index = TestAttribute(pAttr);
if (index != -1) if (index != -1) {
return index; return index;
}
// attribute not found -> throw an exception // attribute not found -> throw an exception
ThrowException(format() << "Expected attribute \"" << pAttr << "\" for element <" << mReader->getNodeName() << ">."); ThrowException(format() << "Expected attribute \"" << pAttr << "\" for element <" << mReader->getNodeName() << ">.");

View File

@ -66,6 +66,9 @@ 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;

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
@ -445,8 +445,7 @@ aiReturn Exporter::Export( const aiScene* pScene, const char* pFormatId, const c
ExportProperties emptyProperties; // Never pass NULL ExportProperties so Exporters don't have to worry. ExportProperties emptyProperties; // Never pass NULL ExportProperties so Exporters don't have to worry.
ExportProperties* pProp = pProperties ? (ExportProperties*)pProperties : &emptyProperties; ExportProperties* pProp = pProperties ? (ExportProperties*)pProperties : &emptyProperties;
pProp->SetPropertyBool("bJoinIdenticalVertices", must_join_again); pProp->SetPropertyBool("bJoinIdenticalVertices", pp & aiProcess_JoinIdenticalVertices);
exp.mExportFunction(pPath,pimpl->mIOSystem.get(),scenecopy.get(), pProp);
exp.mExportFunction(pPath,pimpl->mIOSystem.get(),scenecopy.get(), pProp); exp.mExportFunction(pPath,pimpl->mIOSystem.get(),scenecopy.get(), pProp);
pimpl->mProgressHandler->UpdateFileWrite(4, 4); pimpl->mProgressHandler->UpdateFileWrite(4, 4);
@ -466,11 +465,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,11 +480,13 @@ 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 {
ai_assert(nullptr != pimpl);
const aiExportDataBlob *tmp = pimpl->blob; const aiExportDataBlob *tmp = pimpl->blob;
pimpl->blob = nullptr; pimpl->blob = nullptr;
return tmp; return tmp;
@ -491,18 +494,20 @@ const aiExportDataBlob* Exporter::GetOrphanedBlob() const {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
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,6 +515,7 @@ const aiExportFormatDesc* Exporter::GetExportFormatDescription( size_t index ) c
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
aiReturn Exporter::RegisterExporter(const ExportFormatEntry& desc) { aiReturn Exporter::RegisterExporter(const ExportFormatEntry& desc) {
ai_assert(nullptr != pimpl);
for (const ExportFormatEntry &e : pimpl->mExporters) { 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,6 +528,7 @@ aiReturn Exporter::RegisterExporter(const ExportFormatEntry& desc) {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Exporter::UnregisterExporter(const char* id) { void Exporter::UnregisterExporter(const char* id) {
ai_assert(nullptr != pimpl);
for (std::vector<ExportFormatEntry>::iterator it = pimpl->mExporters.begin(); 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)) {

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,
@ -78,6 +76,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/TinyFormatter.h> #include <assimp/TinyFormatter.h>
#include <assimp/Exceptional.h> #include <assimp/Exceptional.h>
#include <assimp/Profiler.h> #include <assimp/Profiler.h>
#include <assimp/commonMetaData.h>
#include <set> #include <set>
#include <memory> #include <memory>
#include <cctype> #include <cctype>
@ -119,7 +119,7 @@ void* AllocateFromAssimpHeap::operator new ( size_t num_bytes, const std::nothro
return AllocateFromAssimpHeap::operator new( num_bytes ); return AllocateFromAssimpHeap::operator new( num_bytes );
} }
catch( ... ) { catch( ... ) {
return NULL; return nullptr;
} }
} }
@ -134,9 +134,8 @@ void* AllocateFromAssimpHeap::operator new[] ( size_t num_bytes) {
void* AllocateFromAssimpHeap::operator new[] ( size_t num_bytes, const std::nothrow_t& ) throw() { void* AllocateFromAssimpHeap::operator new[] ( size_t num_bytes, const std::nothrow_t& ) throw() {
try { try {
return AllocateFromAssimpHeap::operator new[]( num_bytes ); return AllocateFromAssimpHeap::operator new[]( num_bytes );
} } catch( ... ) {
catch( ... ) { return nullptr;
return NULL;
} }
} }
@ -148,7 +147,7 @@ void AllocateFromAssimpHeap::operator delete[] ( void* data) {
// Importer constructor. // Importer constructor.
Importer::Importer() Importer::Importer()
: pimpl( new ImporterPimpl ) { : pimpl( new ImporterPimpl ) {
pimpl->mScene = NULL; pimpl->mScene = nullptr;
pimpl->mErrorString = ""; pimpl->mErrorString = "";
// Allocate a default IO handler // Allocate a default IO handler
@ -174,14 +173,14 @@ Importer::Importer()
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Destructor of Importer // Destructor of Importer
Importer::~Importer() Importer::~Importer() {
{
// Delete all import plugins // Delete all import plugins
DeleteImporterInstanceList(pimpl->mImporter); DeleteImporterInstanceList(pimpl->mImporter);
// Delete all post-processing plug-ins // Delete all post-processing plug-ins
for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); ++a ) {
delete pimpl->mPostProcessingSteps[a]; delete pimpl->mPostProcessingSteps[a];
}
// Delete the assigned IO and progress handler // Delete the assigned IO and progress handler
delete pimpl->mIOHandler; delete pimpl->mIOHandler;
@ -199,9 +198,9 @@ Importer::~Importer()
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Register a custom post-processing step // Register a custom post-processing step
aiReturn Importer::RegisterPPStep(BaseProcess* pImp) aiReturn Importer::RegisterPPStep(BaseProcess* pImp) {
{ ai_assert( nullptr != pImp );
ai_assert(NULL != pImp);
ASSIMP_BEGIN_EXCEPTION_REGION(); ASSIMP_BEGIN_EXCEPTION_REGION();
pimpl->mPostProcessingSteps.push_back(pImp); pimpl->mPostProcessingSteps.push_back(pImp);
@ -213,9 +212,9 @@ aiReturn Importer::RegisterPPStep(BaseProcess* pImp)
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Register a custom loader plugin // Register a custom loader plugin
aiReturn Importer::RegisterLoader(BaseImporter* pImp) aiReturn Importer::RegisterLoader(BaseImporter* pImp) {
{ ai_assert(nullptr != pImp);
ai_assert(NULL != pImp);
ASSIMP_BEGIN_EXCEPTION_REGION(); ASSIMP_BEGIN_EXCEPTION_REGION();
// -------------------------------------------------------------------- // --------------------------------------------------------------------
@ -242,13 +241,13 @@ aiReturn Importer::RegisterLoader(BaseImporter* pImp)
pimpl->mImporter.push_back(pImp); pimpl->mImporter.push_back(pImp);
ASSIMP_LOG_INFO_F("Registering custom importer for these file extensions: ", baked); ASSIMP_LOG_INFO_F("Registering custom importer for these file extensions: ", baked);
ASSIMP_END_EXCEPTION_REGION(aiReturn); ASSIMP_END_EXCEPTION_REGION(aiReturn);
return AI_SUCCESS; return AI_SUCCESS;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Unregister a custom loader plugin // Unregister a custom loader plugin
aiReturn Importer::UnregisterLoader(BaseImporter* pImp) aiReturn Importer::UnregisterLoader(BaseImporter* pImp) {
{
if(!pImp) { if(!pImp) {
// unregistering a NULL importer is no problem for us ... really! // unregistering a NULL importer is no problem for us ... really!
return AI_SUCCESS; return AI_SUCCESS;
@ -265,13 +264,13 @@ aiReturn Importer::UnregisterLoader(BaseImporter* pImp)
} }
ASSIMP_LOG_WARN("Unable to remove custom importer: I can't find you ..."); ASSIMP_LOG_WARN("Unable to remove custom importer: I can't find you ...");
ASSIMP_END_EXCEPTION_REGION(aiReturn); ASSIMP_END_EXCEPTION_REGION(aiReturn);
return AI_FAILURE; return AI_FAILURE;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Unregister a custom loader plugin // Unregister a custom loader plugin
aiReturn Importer::UnregisterPPStep(BaseProcess* pImp) aiReturn Importer::UnregisterPPStep(BaseProcess* pImp) {
{
if(!pImp) { if(!pImp) {
// unregistering a NULL ppstep is no problem for us ... really! // unregistering a NULL ppstep is no problem for us ... really!
return AI_SUCCESS; return AI_SUCCESS;
@ -288,24 +287,22 @@ aiReturn Importer::UnregisterPPStep(BaseProcess* pImp)
} }
ASSIMP_LOG_WARN("Unable to remove custom post-processing step: I can't find you .."); ASSIMP_LOG_WARN("Unable to remove custom post-processing step: I can't find you ..");
ASSIMP_END_EXCEPTION_REGION(aiReturn); ASSIMP_END_EXCEPTION_REGION(aiReturn);
return AI_FAILURE; return AI_FAILURE;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Supplies a custom IO handler to the importer to open and access files. // Supplies a custom IO handler to the importer to open and access files.
void Importer::SetIOHandler( IOSystem* pIOHandler) void Importer::SetIOHandler( IOSystem* pIOHandler) {
{ ai_assert(nullptr != pimpl);
ASSIMP_BEGIN_EXCEPTION_REGION(); ASSIMP_BEGIN_EXCEPTION_REGION();
// If the new handler is zero, allocate a default IO implementation. // If the new handler is zero, allocate a default IO implementation.
if (!pIOHandler) if (!pIOHandler) {
{
// Release pointer in the possession of the caller // Release pointer in the possession of the caller
pimpl->mIOHandler = new DefaultIOSystem(); pimpl->mIOHandler = new DefaultIOSystem();
pimpl->mIsDefaultHandler = true; pimpl->mIsDefaultHandler = true;
} } else if (pimpl->mIOHandler != pIOHandler) { // Otherwise register the custom handler
// Otherwise register the custom handler
else if (pimpl->mIOHandler != pIOHandler)
{
delete pimpl->mIOHandler; delete pimpl->mIOHandler;
pimpl->mIOHandler = pIOHandler; pimpl->mIOHandler = pIOHandler;
pimpl->mIsDefaultHandler = false; pimpl->mIsDefaultHandler = false;
@ -316,29 +313,32 @@ void Importer::SetIOHandler( IOSystem* pIOHandler)
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Get the currently set IO handler // Get the currently set IO handler
IOSystem* Importer::GetIOHandler() const { IOSystem* Importer::GetIOHandler() const {
ai_assert(nullptr != pimpl);
return pimpl->mIOHandler; return pimpl->mIOHandler;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Check whether a custom IO handler is currently set // Check whether a custom IO handler is currently set
bool Importer::IsDefaultIOHandler() const { bool Importer::IsDefaultIOHandler() const {
ai_assert(nullptr != pimpl);
return pimpl->mIsDefaultHandler; return pimpl->mIsDefaultHandler;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Supplies a custom progress handler to get regular callbacks during importing // Supplies a custom progress handler to get regular callbacks during importing
void Importer::SetProgressHandler ( ProgressHandler* pHandler ) { void Importer::SetProgressHandler ( ProgressHandler* pHandler ) {
ai_assert(nullptr != pimpl);
ASSIMP_BEGIN_EXCEPTION_REGION(); ASSIMP_BEGIN_EXCEPTION_REGION();
// If the new handler is zero, allocate a default implementation. // If the new handler is zero, allocate a default implementation.
if (!pHandler) if (!pHandler) {
{
// Release pointer in the possession of the caller // Release pointer in the possession of the caller
pimpl->mProgressHandler = new DefaultProgressHandler(); pimpl->mProgressHandler = new DefaultProgressHandler();
pimpl->mIsDefaultProgressHandler = true; pimpl->mIsDefaultProgressHandler = true;
} } else if (pimpl->mProgressHandler != pHandler) { // Otherwise register the custom handler
// Otherwise register the custom handler
else if (pimpl->mProgressHandler != pHandler)
{
delete pimpl->mProgressHandler; delete pimpl->mProgressHandler;
pimpl->mProgressHandler = pHandler; pimpl->mProgressHandler = pHandler;
pimpl->mIsDefaultProgressHandler = false; pimpl->mIsDefaultProgressHandler = false;
@ -349,19 +349,22 @@ void Importer::SetProgressHandler ( ProgressHandler* pHandler ) {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Get the currently set progress handler // Get the currently set progress handler
ProgressHandler* Importer::GetProgressHandler() const { ProgressHandler* Importer::GetProgressHandler() const {
ai_assert(nullptr != pimpl);
return pimpl->mProgressHandler; return pimpl->mProgressHandler;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Check whether a custom progress handler is currently set // Check whether a custom progress handler is currently set
bool Importer::IsDefaultProgressHandler() const { bool Importer::IsDefaultProgressHandler() const {
ai_assert(nullptr != pimpl);
return pimpl->mIsDefaultProgressHandler; return pimpl->mIsDefaultProgressHandler;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Validate post process step flags // Validate post process step flags
bool _ValidateFlags(unsigned int pFlags) bool _ValidateFlags(unsigned int pFlags) {
{
if (pFlags & aiProcess_GenSmoothNormals && pFlags & aiProcess_GenNormals) { if (pFlags & aiProcess_GenSmoothNormals && pFlags & aiProcess_GenNormals) {
ASSIMP_LOG_ERROR("#aiProcess_GenSmoothNormals and #aiProcess_GenNormals are incompatible"); ASSIMP_LOG_ERROR("#aiProcess_GenSmoothNormals and #aiProcess_GenNormals are incompatible");
return false; return false;
@ -375,12 +378,13 @@ bool _ValidateFlags(unsigned int pFlags)
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Free the current scene // Free the current scene
void Importer::FreeScene( ) void Importer::FreeScene( ) {
{ ai_assert(nullptr != pimpl);
ASSIMP_BEGIN_EXCEPTION_REGION(); ASSIMP_BEGIN_EXCEPTION_REGION();
delete pimpl->mScene; delete pimpl->mScene;
pimpl->mScene = NULL; pimpl->mScene = nullptr;
pimpl->mErrorString = ""; pimpl->mErrorString = "";
ASSIMP_END_EXCEPTION_REGION(void); ASSIMP_END_EXCEPTION_REGION(void);
@ -388,44 +392,48 @@ void Importer::FreeScene( )
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Get the current error string, if any // Get the current error string, if any
const char* Importer::GetErrorString() const const char* Importer::GetErrorString() const {
{ ai_assert(nullptr != pimpl);
/* Must remain valid as long as ReadFile() or FreeFile() are not called */
// Must remain valid as long as ReadFile() or FreeFile() are not called
return pimpl->mErrorString.c_str(); return pimpl->mErrorString.c_str();
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Enable extra-verbose mode // Enable extra-verbose mode
void Importer::SetExtraVerbose(bool bDo) void Importer::SetExtraVerbose(bool bDo) {
{ ai_assert(nullptr != pimpl);
pimpl->bExtraVerbose = bDo; pimpl->bExtraVerbose = bDo;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Get the current scene // Get the current scene
const aiScene* Importer::GetScene() const const aiScene* Importer::GetScene() const {
{ ai_assert(nullptr != pimpl);
return pimpl->mScene; return pimpl->mScene;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Orphan the current scene and return it. // Orphan the current scene and return it.
aiScene* Importer::GetOrphanedScene() aiScene* Importer::GetOrphanedScene() {
{ ai_assert(nullptr != pimpl);
aiScene* s = pimpl->mScene; aiScene* s = pimpl->mScene;
ASSIMP_BEGIN_EXCEPTION_REGION(); ASSIMP_BEGIN_EXCEPTION_REGION();
pimpl->mScene = NULL; pimpl->mScene = nullptr;
pimpl->mErrorString = ""; /* reset error string */ pimpl->mErrorString = ""; // reset error string
ASSIMP_END_EXCEPTION_REGION(aiScene*); ASSIMP_END_EXCEPTION_REGION(aiScene*);
return s; return s;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Validate post-processing flags // Validate post-processing flags
bool Importer::ValidateFlags(unsigned int pFlags) const bool Importer::ValidateFlags(unsigned int pFlags) const {
{
ASSIMP_BEGIN_EXCEPTION_REGION(); ASSIMP_BEGIN_EXCEPTION_REGION();
// run basic checks for mutually exclusive flags // run basic checks for mutually exclusive flags
if(!_ValidateFlags(pFlags)) { if(!_ValidateFlags(pFlags)) {
@ -467,8 +475,9 @@ bool Importer::ValidateFlags(unsigned int pFlags) const
const aiScene* Importer::ReadFileFromMemory( const void* pBuffer, const aiScene* Importer::ReadFileFromMemory( const void* pBuffer,
size_t pLength, size_t pLength,
unsigned int pFlags, unsigned int pFlags,
const char* pHint /*= ""*/) const char* pHint /*= ""*/) {
{ ai_assert(nullptr != pimpl);
ASSIMP_BEGIN_EXCEPTION_REGION(); ASSIMP_BEGIN_EXCEPTION_REGION();
if (!pHint) { if (!pHint) {
pHint = ""; pHint = "";
@ -476,12 +485,12 @@ const aiScene* Importer::ReadFileFromMemory( const void* pBuffer,
if (!pBuffer || !pLength || strlen(pHint) > MaxLenHint ) { if (!pBuffer || !pLength || strlen(pHint) > MaxLenHint ) {
pimpl->mErrorString = "Invalid parameters passed to ReadFileFromMemory()"; pimpl->mErrorString = "Invalid parameters passed to ReadFileFromMemory()";
return NULL; return nullptr;
} }
// prevent deletion of the previous IOHandler // prevent deletion of the previous IOHandler
IOSystem* io = pimpl->mIOHandler; IOSystem* io = pimpl->mIOHandler;
pimpl->mIOHandler = NULL; pimpl->mIOHandler = nullptr;
SetIOHandler(new MemoryIOSystem((const uint8_t*)pBuffer,pLength,io)); SetIOHandler(new MemoryIOSystem((const uint8_t*)pBuffer,pLength,io));
@ -493,13 +502,13 @@ const aiScene* Importer::ReadFileFromMemory( const void* pBuffer,
ReadFile(fbuff,pFlags); ReadFile(fbuff,pFlags);
SetIOHandler(io); SetIOHandler(io);
ASSIMP_END_EXCEPTION_REGION(const aiScene*); ASSIMP_END_EXCEPTION_REGION_WITH_ERROR_STRING(const aiScene*, pimpl->mErrorString);
return pimpl->mScene; return pimpl->mScene;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void WriteLogOpening(const std::string& file) void WriteLogOpening(const std::string& file) {
{
ASSIMP_LOG_INFO_F("Load ", file); ASSIMP_LOG_INFO_F("Load ", file);
// print a full version dump. This is nice because we don't // print a full version dump. This is nice because we don't
@ -550,8 +559,9 @@ void WriteLogOpening(const std::string& file)
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Reads the given file and returns its contents if successful. // Reads the given file and returns its contents if successful.
const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags) const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags) {
{ ai_assert(nullptr != pimpl);
ASSIMP_BEGIN_EXCEPTION_REGION(); ASSIMP_BEGIN_EXCEPTION_REGION();
const std::string pFile(_pFile); const std::string pFile(_pFile);
@ -580,7 +590,7 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
pimpl->mErrorString = "Unable to open file \"" + pFile + "\"."; pimpl->mErrorString = "Unable to open file \"" + pFile + "\".";
ASSIMP_LOG_ERROR(pimpl->mErrorString); ASSIMP_LOG_ERROR(pimpl->mErrorString);
return NULL; return nullptr;
} }
std::unique_ptr<Profiler> profiler(GetPropertyInteger(AI_CONFIG_GLOB_MEASURE_TIME,0)?new Profiler():NULL); std::unique_ptr<Profiler> profiler(GetPropertyInteger(AI_CONFIG_GLOB_MEASURE_TIME,0)?new Profiler():NULL);
@ -589,7 +599,7 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
} }
// Find an worker class which can handle the file // Find an worker class which can handle the file
BaseImporter* imp = NULL; BaseImporter* imp = nullptr;
SetPropertyInteger("importerIndex", -1); SetPropertyInteger("importerIndex", -1);
for( unsigned int a = 0; a < pimpl->mImporter.size(); a++) { for( unsigned int a = 0; a < pimpl->mImporter.size(); a++) {
@ -617,7 +627,7 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
if( !imp) { if( !imp) {
pimpl->mErrorString = "No suitable reader found for the file format of file \"" + pFile + "\"."; pimpl->mErrorString = "No suitable reader found for the file format of file \"" + pFile + "\".";
ASSIMP_LOG_ERROR(pimpl->mErrorString); ASSIMP_LOG_ERROR(pimpl->mErrorString);
return NULL; return nullptr;
} }
} }
@ -633,7 +643,7 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
// Dispatch the reading to the worker class for this format // Dispatch the reading to the worker class for this format
const aiImporterDesc *desc( imp->GetInfo() ); const aiImporterDesc *desc( imp->GetInfo() );
std::string ext( "unknown" ); std::string ext( "unknown" );
if ( NULL != desc ) { if ( nullptr != desc ) {
ext = desc->mName; ext = desc->mName;
} }
ASSIMP_LOG_INFO("Found a matching importer for this file format: " + ext + "." ); ASSIMP_LOG_INFO("Found a matching importer for this file format: " + ext + "." );
@ -654,15 +664,20 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
// If successful, apply all active post processing steps to the imported data // If successful, apply all active post processing steps to the imported data
if( pimpl->mScene) { if( pimpl->mScene) {
if (!pimpl->mScene->mMetaData || !pimpl->mScene->mMetaData->HasKey(AI_METADATA_SOURCE_FORMAT)) {
if (!pimpl->mScene->mMetaData) {
pimpl->mScene->mMetaData = new aiMetadata;
}
pimpl->mScene->mMetaData->Add(AI_METADATA_SOURCE_FORMAT, aiString(ext));
}
#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS #ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
// The ValidateDS process is an exception. It is executed first, even before ScenePreprocessor is called. // The ValidateDS process is an exception. It is executed first, even before ScenePreprocessor is called.
if (pFlags & aiProcess_ValidateDataStructure) if (pFlags & aiProcess_ValidateDataStructure) {
{
ValidateDSProcess ds; ValidateDSProcess ds;
ds.ExecuteOnScene (this); ds.ExecuteOnScene (this);
if (!pimpl->mScene) { if (!pimpl->mScene) {
return NULL; return nullptr;
} }
} }
#endif // no validation #endif // no validation
@ -695,8 +710,7 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
} }
} }
#ifdef ASSIMP_CATCH_GLOBAL_EXCEPTIONS #ifdef ASSIMP_CATCH_GLOBAL_EXCEPTIONS
catch (std::exception &e) catch (std::exception &e) {
{
#if (defined _MSC_VER) && (defined _CPPRTTI) #if (defined _MSC_VER) && (defined _CPPRTTI)
// if we have RTTI get the full name of the exception that occurred // if we have RTTI get the full name of the exception that occurred
pimpl->mErrorString = std::string(typeid( e ).name()) + ": " + e.what(); pimpl->mErrorString = std::string(typeid( e ).name()) + ": " + e.what();
@ -705,24 +719,26 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
#endif #endif
ASSIMP_LOG_ERROR(pimpl->mErrorString); ASSIMP_LOG_ERROR(pimpl->mErrorString);
delete pimpl->mScene; pimpl->mScene = NULL; delete pimpl->mScene; pimpl->mScene = nullptr;
} }
#endif // ! ASSIMP_CATCH_GLOBAL_EXCEPTIONS #endif // ! ASSIMP_CATCH_GLOBAL_EXCEPTIONS
// either successful or failure - the pointer expresses it anyways // either successful or failure - the pointer expresses it anyways
ASSIMP_END_EXCEPTION_REGION(const aiScene*); ASSIMP_END_EXCEPTION_REGION_WITH_ERROR_STRING(const aiScene*, pimpl->mErrorString);
return pimpl->mScene; return pimpl->mScene;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Apply post-processing to the currently bound scene // Apply post-processing to the currently bound scene
const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags) const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags) {
{ ai_assert(nullptr != pimpl);
ASSIMP_BEGIN_EXCEPTION_REGION(); ASSIMP_BEGIN_EXCEPTION_REGION();
// Return immediately if no scene is active // Return immediately if no scene is active
if (!pimpl->mScene) { if (!pimpl->mScene) {
return NULL; return nullptr;
} }
// If no flags are given, return the current scene with no further action // If no flags are given, return the current scene with no further action
@ -737,12 +753,11 @@ const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags)
#ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS #ifndef ASSIMP_BUILD_NO_VALIDATEDS_PROCESS
// The ValidateDS process plays an exceptional role. It isn't contained in the global // The ValidateDS process plays an exceptional role. It isn't contained in the global
// list of post-processing steps, so we need to call it manually. // list of post-processing steps, so we need to call it manually.
if (pFlags & aiProcess_ValidateDataStructure) if (pFlags & aiProcess_ValidateDataStructure) {
{
ValidateDSProcess ds; ValidateDSProcess ds;
ds.ExecuteOnScene (this); ds.ExecuteOnScene (this);
if (!pimpl->mScene) { if (!pimpl->mScene) {
return NULL; return nullptr;
} }
} }
#endif // no validation #endif // no validation
@ -762,11 +777,9 @@ const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags)
std::unique_ptr<Profiler> profiler(GetPropertyInteger(AI_CONFIG_GLOB_MEASURE_TIME,0)?new Profiler():NULL); std::unique_ptr<Profiler> profiler(GetPropertyInteger(AI_CONFIG_GLOB_MEASURE_TIME,0)?new Profiler():NULL);
for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) { for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) {
BaseProcess* process = pimpl->mPostProcessingSteps[a]; BaseProcess* process = pimpl->mPostProcessingSteps[a];
pimpl->mProgressHandler->UpdatePostProcess(static_cast<int>(a), static_cast<int>(pimpl->mPostProcessingSteps.size()) ); pimpl->mProgressHandler->UpdatePostProcess(static_cast<int>(a), static_cast<int>(pimpl->mPostProcessingSteps.size()) );
if( process->IsActive( pFlags)) { if( process->IsActive( pFlags)) {
if (profiler) { if (profiler) {
profiler->BeginRegion("postprocess"); profiler->BeginRegion("postprocess");
} }
@ -803,24 +816,28 @@ const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags)
static_cast<int>(pimpl->mPostProcessingSteps.size()) ); static_cast<int>(pimpl->mPostProcessingSteps.size()) );
// update private scene flags // update private scene flags
if( pimpl->mScene ) if( pimpl->mScene ) {
ScenePriv(pimpl->mScene)->mPPStepsApplied |= pFlags; ScenePriv(pimpl->mScene)->mPPStepsApplied |= pFlags;
}
// clear any data allocated by post-process steps // clear any data allocated by post-process steps
pimpl->mPPShared->Clean(); pimpl->mPPShared->Clean();
ASSIMP_LOG_INFO("Leaving post processing pipeline"); ASSIMP_LOG_INFO("Leaving post processing pipeline");
ASSIMP_END_EXCEPTION_REGION(const aiScene*); ASSIMP_END_EXCEPTION_REGION(const aiScene*);
return pimpl->mScene; return pimpl->mScene;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
const aiScene* Importer::ApplyCustomizedPostProcessing( BaseProcess *rootProcess, bool requestValidation ) { const aiScene* Importer::ApplyCustomizedPostProcessing( BaseProcess *rootProcess, bool requestValidation ) {
ai_assert(nullptr != pimpl);
ASSIMP_BEGIN_EXCEPTION_REGION(); ASSIMP_BEGIN_EXCEPTION_REGION();
// Return immediately if no scene is active // Return immediately if no scene is active
if ( NULL == pimpl->mScene ) { if ( nullptr == pimpl->mScene ) {
return NULL; return nullptr;
} }
// If no flags are given, return the current scene with no further action // If no flags are given, return the current scene with no further action
@ -839,7 +856,7 @@ const aiScene* Importer::ApplyCustomizedPostProcessing( BaseProcess *rootProcess
ValidateDSProcess ds; ValidateDSProcess ds;
ds.ExecuteOnScene( this ); ds.ExecuteOnScene( this );
if ( !pimpl->mScene ) { if ( !pimpl->mScene ) {
return NULL; return nullptr;
} }
} }
#endif // no validation #endif // no validation
@ -890,46 +907,50 @@ const aiScene* Importer::ApplyCustomizedPostProcessing( BaseProcess *rootProcess
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Helper function to check whether an extension is supported by ASSIMP // Helper function to check whether an extension is supported by ASSIMP
bool Importer::IsExtensionSupported(const char* szExtension) const bool Importer::IsExtensionSupported(const char* szExtension) const {
{
return nullptr != GetImporter(szExtension); return nullptr != GetImporter(szExtension);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
size_t Importer::GetImporterCount() const size_t Importer::GetImporterCount() const {
{ ai_assert(nullptr != pimpl);
return pimpl->mImporter.size(); return pimpl->mImporter.size();
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
const aiImporterDesc* Importer::GetImporterInfo(size_t index) const const aiImporterDesc* Importer::GetImporterInfo(size_t index) const {
{ ai_assert(nullptr != pimpl);
if (index >= pimpl->mImporter.size()) { if (index >= pimpl->mImporter.size()) {
return NULL; return nullptr;
} }
return pimpl->mImporter[index]->GetInfo(); return pimpl->mImporter[index]->GetInfo();
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
BaseImporter* Importer::GetImporter (size_t index) const BaseImporter* Importer::GetImporter (size_t index) const {
{ ai_assert(nullptr != pimpl);
if (index >= pimpl->mImporter.size()) { if (index >= pimpl->mImporter.size()) {
return NULL; return nullptr;
} }
return pimpl->mImporter[index]; return pimpl->mImporter[index];
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Find a loader plugin for a given file extension // Find a loader plugin for a given file extension
BaseImporter* Importer::GetImporter (const char* szExtension) const BaseImporter* Importer::GetImporter (const char* szExtension) const {
{ ai_assert(nullptr != pimpl);
return GetImporter(GetImporterIndex(szExtension)); return GetImporter(GetImporterIndex(szExtension));
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Find a loader plugin for a given file extension // Find a loader plugin for a given file extension
size_t Importer::GetImporterIndex (const char* szExtension) const { size_t Importer::GetImporterIndex (const char* szExtension) const {
ai_assert(nullptr != pimpl);
ai_assert(nullptr != szExtension); ai_assert(nullptr != szExtension);
ASSIMP_BEGIN_EXCEPTION_REGION(); ASSIMP_BEGIN_EXCEPTION_REGION();
@ -960,8 +981,9 @@ size_t Importer::GetImporterIndex (const char* szExtension) const {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Helper function to build a list of all file extensions supported by ASSIMP // Helper function to build a list of all file extensions supported by ASSIMP
void Importer::GetExtensionList(aiString& szOut) const void Importer::GetExtensionList(aiString& szOut) const {
{ ai_assert(nullptr != pimpl);
ASSIMP_BEGIN_EXCEPTION_REGION(); ASSIMP_BEGIN_EXCEPTION_REGION();
std::set<std::string> str; std::set<std::string> str;
for (std::vector<BaseImporter*>::const_iterator i = pimpl->mImporter.begin();i != pimpl->mImporter.end();++i) { for (std::vector<BaseImporter*>::const_iterator i = pimpl->mImporter.begin();i != pimpl->mImporter.end();++i) {
@ -985,8 +1007,9 @@ void Importer::GetExtensionList(aiString& szOut) const
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Set a configuration property // Set a configuration property
bool Importer::SetPropertyInteger(const char* szName, int iValue) bool Importer::SetPropertyInteger(const char* szName, int iValue) {
{ ai_assert(nullptr != pimpl);
bool existing; bool existing;
ASSIMP_BEGIN_EXCEPTION_REGION(); ASSIMP_BEGIN_EXCEPTION_REGION();
existing = SetGenericProperty<int>(pimpl->mIntProperties, szName,iValue); existing = SetGenericProperty<int>(pimpl->mIntProperties, szName,iValue);
@ -996,8 +1019,9 @@ bool Importer::SetPropertyInteger(const char* szName, int iValue)
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Set a configuration property // Set a configuration property
bool Importer::SetPropertyFloat(const char* szName, ai_real iValue) bool Importer::SetPropertyFloat(const char* szName, ai_real iValue) {
{ ai_assert(nullptr != pimpl);
bool existing; bool existing;
ASSIMP_BEGIN_EXCEPTION_REGION(); ASSIMP_BEGIN_EXCEPTION_REGION();
existing = SetGenericProperty<ai_real>(pimpl->mFloatProperties, szName,iValue); existing = SetGenericProperty<ai_real>(pimpl->mFloatProperties, szName,iValue);
@ -1007,8 +1031,9 @@ bool Importer::SetPropertyFloat(const char* szName, ai_real iValue)
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Set a configuration property // Set a configuration property
bool Importer::SetPropertyString(const char* szName, const std::string& value) bool Importer::SetPropertyString(const char* szName, const std::string& value) {
{ ai_assert(nullptr != pimpl);
bool existing; bool existing;
ASSIMP_BEGIN_EXCEPTION_REGION(); ASSIMP_BEGIN_EXCEPTION_REGION();
existing = SetGenericProperty<std::string>(pimpl->mStringProperties, szName,value); existing = SetGenericProperty<std::string>(pimpl->mStringProperties, szName,value);
@ -1018,8 +1043,9 @@ bool Importer::SetPropertyString(const char* szName, const std::string& value)
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Set a configuration property // Set a configuration property
bool Importer::SetPropertyMatrix(const char* szName, const aiMatrix4x4& value) bool Importer::SetPropertyMatrix(const char* szName, const aiMatrix4x4& value) {
{ ai_assert(nullptr != pimpl);
bool existing; bool existing;
ASSIMP_BEGIN_EXCEPTION_REGION(); ASSIMP_BEGIN_EXCEPTION_REGION();
existing = SetGenericProperty<aiMatrix4x4>(pimpl->mMatrixProperties, szName,value); existing = SetGenericProperty<aiMatrix4x4>(pimpl->mMatrixProperties, szName,value);
@ -1029,40 +1055,43 @@ bool Importer::SetPropertyMatrix(const char* szName, const aiMatrix4x4& value)
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Get a configuration property // Get a configuration property
int Importer::GetPropertyInteger(const char* szName, int Importer::GetPropertyInteger(const char* szName, int iErrorReturn /*= 0xffffffff*/) const {
int iErrorReturn /*= 0xffffffff*/) const ai_assert(nullptr != pimpl);
{
return GetGenericProperty<int>(pimpl->mIntProperties,szName,iErrorReturn); return GetGenericProperty<int>(pimpl->mIntProperties,szName,iErrorReturn);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Get a configuration property // Get a configuration property
ai_real Importer::GetPropertyFloat(const char* szName, ai_real Importer::GetPropertyFloat(const char* szName, ai_real iErrorReturn /*= 10e10*/) const {
ai_real iErrorReturn /*= 10e10*/) const ai_assert(nullptr != pimpl);
{
return GetGenericProperty<ai_real>(pimpl->mFloatProperties,szName,iErrorReturn); return GetGenericProperty<ai_real>(pimpl->mFloatProperties,szName,iErrorReturn);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Get a configuration property // Get a configuration property
const std::string Importer::GetPropertyString(const char* szName, const std::string Importer::GetPropertyString(const char* szName, const std::string& iErrorReturn /*= ""*/) const {
const std::string& iErrorReturn /*= ""*/) const ai_assert(nullptr != pimpl);
{
return GetGenericProperty<std::string>(pimpl->mStringProperties,szName,iErrorReturn); return GetGenericProperty<std::string>(pimpl->mStringProperties,szName,iErrorReturn);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Get a configuration property // Get a configuration property
const aiMatrix4x4 Importer::GetPropertyMatrix(const char* szName, const aiMatrix4x4 Importer::GetPropertyMatrix(const char* szName, const aiMatrix4x4& iErrorReturn /*= aiMatrix4x4()*/) const {
const aiMatrix4x4& iErrorReturn /*= aiMatrix4x4()*/) const ai_assert(nullptr != pimpl);
{
return GetGenericProperty<aiMatrix4x4>(pimpl->mMatrixProperties,szName,iErrorReturn); return GetGenericProperty<aiMatrix4x4>(pimpl->mMatrixProperties,szName,iErrorReturn);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Get the memory requirements of a single node // Get the memory requirements of a single node
inline void AddNodeWeight(unsigned int& iScene,const aiNode* pcNode) inline
{ void AddNodeWeight(unsigned int& iScene,const aiNode* pcNode) {
if ( nullptr == pcNode ) {
return;
}
iScene += sizeof(aiNode); iScene += sizeof(aiNode);
iScene += sizeof(unsigned int) * pcNode->mNumMeshes; iScene += sizeof(unsigned int) * pcNode->mNumMeshes;
iScene += sizeof(void*) * pcNode->mNumChildren; iScene += sizeof(void*) * pcNode->mNumChildren;
@ -1074,8 +1103,9 @@ inline void AddNodeWeight(unsigned int& iScene,const aiNode* pcNode)
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Get the memory requirements of the scene // Get the memory requirements of the scene
void Importer::GetMemoryRequirements(aiMemoryInfo& in) const void Importer::GetMemoryRequirements(aiMemoryInfo& in) const {
{ ai_assert(nullptr != pimpl);
in = aiMemoryInfo(); in = aiMemoryInfo();
aiScene* mScene = pimpl->mScene; aiScene* mScene = pimpl->mScene;
@ -1087,8 +1117,7 @@ void Importer::GetMemoryRequirements(aiMemoryInfo& in) const
in.total = sizeof(aiScene); in.total = sizeof(aiScene);
// add all meshes // add all meshes
for (unsigned int i = 0; i < mScene->mNumMeshes;++i) for (unsigned int i = 0; i < mScene->mNumMeshes;++i) {
{
in.meshes += sizeof(aiMesh); in.meshes += sizeof(aiMesh);
if (mScene->mMeshes[i]->HasPositions()) { if (mScene->mMeshes[i]->HasPositions()) {
in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices; in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices;
@ -1105,14 +1134,16 @@ void Importer::GetMemoryRequirements(aiMemoryInfo& in) const
for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS;++a) { for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS;++a) {
if (mScene->mMeshes[i]->HasVertexColors(a)) { if (mScene->mMeshes[i]->HasVertexColors(a)) {
in.meshes += sizeof(aiColor4D) * mScene->mMeshes[i]->mNumVertices; in.meshes += sizeof(aiColor4D) * mScene->mMeshes[i]->mNumVertices;
} else {
break;
} }
else break;
} }
for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS;++a) { for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS;++a) {
if (mScene->mMeshes[i]->HasTextureCoords(a)) { if (mScene->mMeshes[i]->HasTextureCoords(a)) {
in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices; in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices;
} else {
break;
} }
else break;
} }
if (mScene->mMeshes[i]->HasBones()) { if (mScene->mMeshes[i]->HasBones()) {
in.meshes += sizeof(void*) * mScene->mMeshes[i]->mNumBones; in.meshes += sizeof(void*) * mScene->mMeshes[i]->mNumBones;
@ -1131,8 +1162,9 @@ void Importer::GetMemoryRequirements(aiMemoryInfo& in) const
in.textures += sizeof(aiTexture); in.textures += sizeof(aiTexture);
if (pc->mHeight) { if (pc->mHeight) {
in.textures += 4 * pc->mHeight * pc->mWidth; in.textures += 4 * pc->mHeight * pc->mWidth;
} else {
in.textures += pc->mWidth;
} }
else in.textures += pc->mWidth;
} }
in.total += in.textures; in.total += in.textures;

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"
; ;
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -67,16 +66,22 @@ ASSIMP_API const char* aiGetLegalString () {
return LEGAL_INFORMATION; return LEGAL_INFORMATION;
} }
// ------------------------------------------------------------------------------------------------
// Get Assimp patch version
ASSIMP_API unsigned int aiGetVersionPatch() {
return VER_PATCH;
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// 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 +109,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

@ -60,6 +60,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/scene.h> #include <assimp/scene.h>
#include <assimp/CreateAnimMesh.h> #include <assimp/CreateAnimMesh.h>
#include <assimp/commonMetaData.h>
#include <assimp/StringUtils.h>
#include <tuple> #include <tuple>
#include <memory> #include <memory>
@ -68,7 +70,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 +80,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 +99,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 +156,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 +190,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 +205,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 +279,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 +301,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 +318,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 +830,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 +932,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 +945,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 +959,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 +992,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 +1014,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 +1049,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 +1066,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 +1144,7 @@ namespace Assimp {
binormals = &tempBinormals; binormals = &tempBinormals;
} }
else { else {
binormals = NULL; binormals = nullptr;
} }
} }
@ -1163,8 +1194,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 +1241,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 +1255,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);
} }
} }
@ -1231,16 +1265,16 @@ namespace Assimp {
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 +1334,7 @@ namespace Assimp {
binormals = &tempBinormals; binormals = &tempBinormals;
} }
else { else {
binormals = NULL; binormals = nullptr;
} }
} }
@ -1399,7 +1433,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;
@ -1450,8 +1484,8 @@ namespace Assimp {
} }
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 +1497,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 +1516,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 +1558,74 @@ 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)) {
ASSIMP_LOG_DEBUG_F("retrieved bone from lookup ", bone_name.C_Str(), ". Deformer:", deformer_name);
bone = bone_map[deformer_name];
} else {
ASSIMP_LOG_DEBUG_F("created new bone ", bone_name.C_Str(), ". Deformer: ", deformer_name);
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 +1637,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));
}
ASSIMP_LOG_DEBUG_F("bone research: Indicies size: ", out_indices.size());
// 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 +1783,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 +1791,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 +2315,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 +2749,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 +2780,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);
@ -3533,7 +3605,9 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa
return; return;
} }
out->mMetaData = aiMetadata::Alloc(15); const bool hasGenerator = !doc.Creator().empty();
out->mMetaData = aiMetadata::Alloc(16 + (hasGenerator ? 1 : 0));
out->mMetaData->Set(0, "UpAxis", doc.GlobalSettings().UpAxis()); out->mMetaData->Set(0, "UpAxis", doc.GlobalSettings().UpAxis());
out->mMetaData->Set(1, "UpAxisSign", doc.GlobalSettings().UpAxisSign()); out->mMetaData->Set(1, "UpAxisSign", doc.GlobalSettings().UpAxisSign());
out->mMetaData->Set(2, "FrontAxis", doc.GlobalSettings().FrontAxis()); out->mMetaData->Set(2, "FrontAxis", doc.GlobalSettings().FrontAxis());
@ -3549,6 +3623,11 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa
out->mMetaData->Set(12, "TimeSpanStart", doc.GlobalSettings().TimeSpanStart()); out->mMetaData->Set(12, "TimeSpanStart", doc.GlobalSettings().TimeSpanStart());
out->mMetaData->Set(13, "TimeSpanStop", doc.GlobalSettings().TimeSpanStop()); out->mMetaData->Set(13, "TimeSpanStop", doc.GlobalSettings().TimeSpanStop());
out->mMetaData->Set(14, "CustomFrameRate", doc.GlobalSettings().CustomFrameRate()); out->mMetaData->Set(14, "CustomFrameRate", doc.GlobalSettings().CustomFrameRate());
out->mMetaData->Set(15, AI_METADATA_SOURCE_FORMAT_VERSION, aiString(to_string(doc.FBXVersion())));
if (hasGenerator)
{
out->mMetaData->Set(16, AI_METADATA_SOURCE_GENERATOR, aiString(doc.Creator()));
}
} }
void FBXConverter::TransferDataToScene() void FBXConverter::TransferDataToScene()
@ -3556,7 +3635,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 +3682,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,
@ -418,12 +421,18 @@ private:
double& minTime, double& minTime,
Model::RotOrder order); Model::RotOrder order);
// ------------------------------------------------------------------------------------------------
// Copy global geometric data and some information about the source asset into scene metadata.
void ConvertGlobalSettings(); void ConvertGlobalSettings();
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// 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 +444,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");
@ -1860,6 +1860,7 @@ void FBXExporter::WriteObjects ()
sdnode.AddChild("Version", int32_t(100)); sdnode.AddChild("Version", int32_t(100));
sdnode.AddChild("UserData", "", ""); sdnode.AddChild("UserData", "", "");
std::set<int32_t> setWeightedVertex;
// add indices and weights, if any // add indices and weights, if any
if (b) { if (b) {
std::vector<int32_t> subdef_indices; std::vector<int32_t> subdef_indices;
@ -1867,7 +1868,8 @@ void FBXExporter::WriteObjects ()
int32_t last_index = -1; int32_t last_index = -1;
for (size_t wi = 0; wi < b->mNumWeights; ++wi) { for (size_t wi = 0; wi < b->mNumWeights; ++wi) {
int32_t vi = vertex_indices[b->mWeights[wi].mVertexId]; int32_t vi = vertex_indices[b->mWeights[wi].mVertexId];
if (vi == last_index) { bool bIsWeightedAlready = (setWeightedVertex.find(vi) != setWeightedVertex.end());
if (vi == last_index || bIsWeightedAlready) {
// only for vertices we exported to fbx // only for vertices we exported to fbx
// TODO, FIXME: this assumes identically-located vertices // TODO, FIXME: this assumes identically-located vertices
// will always deform in the same way. // will always deform in the same way.
@ -1877,6 +1879,7 @@ void FBXExporter::WriteObjects ()
// identical vertex. // identical vertex.
continue; continue;
} }
setWeightedVertex.insert(vi);
subdef_indices.push_back(vi); subdef_indices.push_back(vi);
subdef_weights.push_back(b->mWeights[wi].mWeight); subdef_weights.push_back(b->mWeights[wi].mWeight);
last_index = vi; last_index = vi;

View File

@ -48,16 +48,16 @@ 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 {
@ -67,7 +67,7 @@ const char* LogFunctions<FBXImporter>::Prefix() {
return prefix; return prefix;
} }
} } // namespace Assimp
using namespace Assimp; using namespace Assimp;
using namespace Assimp::Formatter; using namespace Assimp::Formatter;
@ -91,20 +91,17 @@ 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;
@ -120,15 +117,13 @@ bool FBXImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// 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,8 +141,7 @@ 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");
@ -173,8 +167,7 @@ void FBXImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS
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);
} }
@ -196,8 +189,7 @@ void FBXImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS
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

@ -138,8 +138,9 @@ void ProcessPolygonBoundaries(TempMesh& result, const TempMesh& inmesh, size_t m
} }
} }
} }
if (outer_polygon_it == end) {
ai_assert(outer_polygon_it != end); return;
}
const size_t outer_polygon_size = *outer_polygon_it; const size_t outer_polygon_size = *outer_polygon_it;
const IfcVector3& master_normal = normals[std::distance(begin, outer_polygon_it)]; const IfcVector3& master_normal = normals[std::distance(begin, outer_polygon_it)];

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

@ -586,7 +586,6 @@ void LWOImporter::GenerateNodeGraph(std::map<uint16_t,aiNode*>& apcNodes)
root->mName.Set("<LWORoot>"); root->mName.Set("<LWORoot>");
//Set parent of all children, inserting pivots //Set parent of all children, inserting pivots
//std::cout << "Set parent of all children" << std::endl;
std::map<uint16_t, aiNode*> mapPivot; std::map<uint16_t, aiNode*> mapPivot;
for (auto itapcNodes = apcNodes.begin(); itapcNodes != apcNodes.end(); ++itapcNodes) { for (auto itapcNodes = apcNodes.begin(); itapcNodes != apcNodes.end(); ++itapcNodes) {
@ -618,7 +617,6 @@ void LWOImporter::GenerateNodeGraph(std::map<uint16_t,aiNode*>& apcNodes)
} }
//Merge pivot map into node map //Merge pivot map into node map
//std::cout << "Merge pivot map into node map" << std::endl;
for (auto itMapPivot = mapPivot.begin(); itMapPivot != mapPivot.end(); ++itMapPivot) { for (auto itMapPivot = mapPivot.begin(); itMapPivot != mapPivot.end(); ++itMapPivot) {
apcNodes[itMapPivot->first] = itMapPivot->second; apcNodes[itMapPivot->first] = itMapPivot->second;
} }

View File

@ -0,0 +1,412 @@
/*
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/Exceptional.h> // DeadlyExportError
#include <assimp/StreamWriter.h> // StreamWriterLE
#include <assimp/material.h> // aiTextureType
#include <assimp/mesh.h>
#include <assimp/scene.h>
#include <assimp/version.h> // aiGetVersion
#include <assimp/DefaultLogger.hpp>
#include <assimp/Exporter.hpp>
#include <assimp/IOSystem.hpp>
#include "M3DExporter.h"
#include "M3DMaterials.h"
#include "M3DWrapper.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)
*/
// ------------------------------------------------------------------------------------------------
// Conversion functions
// ------------------------------------------------------------------------------------------------
// helper to add a vertex (private to NodeWalk)
m3dv_t *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 *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;
}
// ------------------------------------------------------------------------------------------------
// convert aiColor4D into uint32_t
uint32_t 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 property to the output
void 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;
}
// ------------------------------------------------------------------------------------------------
// add a material to the output
M3D_INDEX addMaterial(const Assimp::M3DWrapper &m3d, 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;
}
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);
}
// ------------------------------------------------------------------------------------------------
M3DExporter::M3DExporter(const aiScene *pScene, const ExportProperties *pProperties) :
mScene(pScene),
mProperties(pProperties),
outfile() {}
// ------------------------------------------------------------------------------------------------
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));
}
M3DWrapper m3d;
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(m3d, mScene->mRootNode, m);
// serialize the structures
unsigned int size;
unsigned char *output = m3d.Save(M3D_EXP_FLOAT, M3D_EXP_EXTRA | (toAscii ? M3D_EXP_ASCII : 0), size);
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();
}
// ------------------------------------------------------------------------------------------------
// recursive node walker
void M3DExporter::NodeWalk(const M3DWrapper &m3d, 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(m3d, 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(m3d, pNode->mChildren[i], nm);
}
}
} // namespace Assimp
#endif // ASSIMP_BUILD_NO_M3D_EXPORTER
#endif // ASSIMP_BUILD_NO_EXPORT

View File

@ -0,0 +1,94 @@
/*
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 <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;
class M3DWrapper;
// ---------------------------------------------------------------------
/** 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
// helper to do the recursive walking
void NodeWalk(const M3DWrapper &m3d, const aiNode* pNode, aiMatrix4x4 m);
};
}
#endif // ASSIMP_BUILD_NO_M3D_EXPORTER
#endif // AI_M3DEXPORTER_H_INC

View File

@ -0,0 +1,742 @@
/*
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/DefaultIOSystem.h>
#include <assimp/IOStreamBuffer.h>
#include <assimp/ai_assert.h>
#include <assimp/importerdesc.h>
#include <assimp/scene.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/Importer.hpp>
#include <memory>
#include "M3DImporter.h"
#include "M3DMaterials.h"
#include "M3DWrapper.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_SupportTextFlavour | aiImporterFlags_SupportBinaryFlavour,
0,
0,
0,
0,
"m3d a3d"
};
namespace Assimp {
using namespace std;
// ------------------------------------------------------------------------------------------------
// Default constructor
M3DImporter::M3DImporter() :
mScene(nullptr) {}
// ------------------------------------------------------------------------------------------------
// 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::vector<unsigned char> buffer(fileSize);
if (fileSize != pStream->Read(buffer.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);
}
}
//DefaultLogger::create("/dev/stderr", Logger::VERBOSE);
ASSIMP_LOG_DEBUG_F("M3D: loading ", file);
// let the C SDK do the hard work for us
M3DWrapper m3d(pIOHandler, buffer);
if (!m3d) {
throw DeadlyImportError("Unable to parse " + file + " as M3D.");
}
// create the root node
pScene->mRootNode = new aiNode;
pScene->mRootNode->mName = aiString(m3d.Name());
pScene->mRootNode->mTransformation = aiMatrix4x4();
pScene->mRootNode->mNumChildren = 0;
mScene = pScene;
ASSIMP_LOG_DEBUG("M3D: root node " + m3d.Name());
// now we just have to fill up the Assimp structures in pScene
importMaterials(m3d);
importTextures(m3d);
importBones(m3d, -1U, pScene->mRootNode);
importMeshes(m3d);
importAnimations(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(const M3DWrapper &m3d_wrap) {
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_wrap);
mScene->mNumMaterials = m3d_wrap->nummaterial + 1;
mScene->mMaterials = new aiMaterial *[mScene->mNumMaterials];
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.0f;
c.b = c.g = c.r = 0.6f;
mat->AddProperty(&c, 1, AI_MATKEY_COLOR_DIFFUSE);
mScene->mMaterials[0] = mat;
for (i = 0; i < m3d_wrap->nummaterial; i++) {
m = &m3d_wrap->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_wrap->numtexture &&
m3d_wrap->texture[m->prop[j].value.textureid].name) {
name.Set(std::string(std::string(m3d_wrap->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(const M3DWrapper &m3d) {
unsigned int i;
const char *formatHint[] = { "rgba0800", "rgba0808", "rgba8880", "rgba8888" };
m3dtx_t *t;
ai_assert(mScene != nullptr);
ai_assert(m3d);
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(const M3DWrapper &m3d) {
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);
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(m3d, 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 = static_cast<unsigned int>(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(m3d, pMesh, faces, vertices, normals, texcoords, colors, vertexids);
meshes->push_back(pMesh);
}
// create global mesh list in scene
mScene->mNumMeshes = static_cast<unsigned int>(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 = static_cast<unsigned int>(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(const M3DWrapper &m3d, unsigned int parentid, aiNode *pParent) {
unsigned int i, n;
ai_assert(pParent != nullptr);
ai_assert(mScene != nullptr);
ai_assert(m3d);
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(m3d, &pChild->mTransformation, m3d->bone[i].pos, m3d->bone[i].ori);
pChild->mNumChildren = 0;
pParent->mChildren[pParent->mNumChildren] = pChild;
pParent->mNumChildren++;
importBones(m3d, 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(const M3DWrapper &m3d) {
unsigned int i, j, k, l, pos, ori;
double t;
m3da_t *a;
ai_assert(mScene != nullptr);
ai_assert(m3d);
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(const M3DWrapper &m3d, aiMatrix4x4 *m, unsigned int posid, unsigned int orientid) {
ai_assert(m != nullptr);
ai_assert(m3d);
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(const M3DWrapper &m3d, 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);
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 = static_cast<unsigned int>(faces->size());
pMesh->mFaces = new aiFace[pMesh->mNumFaces];
std::copy(faces->begin(), faces->end(), pMesh->mFaces);
pMesh->mNumVertices = static_cast<unsigned int>(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,103 @@
/*
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 <assimp/BaseImporter.h>
#include <assimp/material.h>
#include <vector>
struct aiMesh;
struct aiNode;
struct aiMaterial;
struct aiFace;
namespace Assimp {
class M3DWrapper;
class M3DImporter : public BaseImporter {
public:
/// \brief Default constructor
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 = nullptr; // the scene to import to
//! \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(const M3DWrapper &m3d);
void importTextures(const M3DWrapper &m3d);
void importMeshes(const M3DWrapper &m3d);
void importBones(const M3DWrapper &m3d, unsigned int parentid, aiNode *pParent);
void importAnimations(const M3DWrapper &m3d);
// helper functions
aiColor4D mkColor(uint32_t c);
void convertPose(const M3DWrapper &m3d, aiMatrix4x4 *m, unsigned int posid, unsigned int orientid);
aiNode *findNode(aiNode *pNode, aiString name);
void calculateOffsetMatrix(aiNode *pNode, aiMatrix4x4 *m);
void populateMesh(const M3DWrapper &m3d, 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

View File

@ -0,0 +1,142 @@
/*
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.
----------------------------------------------------------------------
*/
#if !(ASSIMP_BUILD_NO_EXPORT || ASSIMP_BUILD_NO_M3D_EXPORTER) || !ASSIMP_BUILD_NO_M3D_IMPORTER
#include "M3DWrapper.h"
#include <assimp/DefaultIOSystem.h>
#include <assimp/IOStreamBuffer.h>
#include <assimp/ai_assert.h>
#ifndef AI_M3D_USE_STDMUTEX
#if (__cplusplus >= 201103L) || (_MSC_VER >= 1900) // C++11 and MSVC 2015 onwards
#define AI_M3D_USE_STDMUTEX 1
#else
#define AI_M3D_USE_STDMUTEX 0
#endif
#endif
#if AI_M3D_USE_STDMUTEX
#include <mutex>
std::mutex file_mutex;
#endif
// workaround: the M3D SDK expects a C callback, but we want to use Assimp::IOSystem to implement that
// This makes it non-rentrant so lock a mutex (requires C++11)
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 {
M3DWrapper::M3DWrapper() {
// use malloc() here because m3d_free() will call free()
m3d_ = (m3d_t *)calloc(1, sizeof(m3d_t));
}
M3DWrapper::M3DWrapper(IOSystem *pIOHandler, const std::vector<unsigned char> &buffer) {
#if AI_M3D_USE_STDMUTEX
// M3D is NOT thread-safe, so lock the global mutex
const std::lock_guard<std::mutex> lock(file_mutex);
#endif
// pass this IOHandler to the C callback
m3dimporter_pIOHandler = pIOHandler;
m3d_ = m3d_load(const_cast<unsigned char *>(buffer.data()), m3dimporter_readfile, free, nullptr);
// Clear the C callback
m3dimporter_pIOHandler = nullptr;
}
M3DWrapper::~M3DWrapper() {
reset();
}
void M3DWrapper::reset() {
ClearSave();
if (m3d_)
m3d_free(m3d_);
m3d_ = nullptr;
}
unsigned char *M3DWrapper::Save(int quality, int flags, unsigned int &size) {
#if (!(ASSIMP_BUILD_NO_EXPORT || ASSIMP_BUILD_NO_M3D_EXPORTER))
ClearSave();
saved_output_ = m3d_save(m3d_, quality, flags, &size);
return saved_output_;
#else
return nullptr;
#endif
}
void M3DWrapper::ClearSave() {
if (saved_output_)
M3D_FREE(saved_output_);
saved_output_ = nullptr;
}
} // namespace Assimp
#endif

View File

@ -0,0 +1,97 @@
#pragma once
/*
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 M3DWrapper.h
* @brief Declares a class to wrap the C m3d SDK
*/
#ifndef AI_M3DWRAPPER_H_INC
#define AI_M3DWRAPPER_H_INC
#if !(ASSIMP_BUILD_NO_EXPORT || ASSIMP_BUILD_NO_M3D_EXPORTER) || !ASSIMP_BUILD_NO_M3D_IMPORTER
#include <memory>
#include <vector>
#include <string>
#include "m3d.h"
namespace Assimp {
class IOSystem;
class M3DWrapper {
m3d_t *m3d_ = nullptr;
unsigned char *saved_output_ = nullptr;
public:
// Construct an empty M3D model
explicit M3DWrapper();
// Construct an M3D model from provided buffer
// NOTE: The m3d.h SDK function does not mark the data as const. Have assumed it does not write.
// BUG: SECURITY: The m3d.h SDK cannot be informed of the buffer size. BUFFER OVERFLOW IS CERTAIN
explicit M3DWrapper(IOSystem *pIOHandler, const std::vector<unsigned char> &buffer);
~M3DWrapper();
void reset();
// Name
inline std::string Name() const {
if (m3d_) return std::string(m3d_->name);
return std::string();
}
// Execute a save
unsigned char *Save(int quality, int flags, unsigned int &size);
void ClearSave();
inline explicit operator bool() const { return m3d_ != nullptr; }
// Allow direct access to M3D API
inline m3d_t *operator->() const { return m3d_; }
inline m3d_t *M3D() const { return m3d_; }
};
} // namespace Assimp
#endif
#endif // AI_M3DWRAPPER_H_INC

5599
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;
@ -274,14 +273,14 @@ aiReturn aiGetMaterialColor(const aiMaterial* pMat,
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Get a aiUVTransform (4 floats) from the material // Get a aiUVTransform (5 floats) from the material
aiReturn aiGetMaterialUVTransform(const aiMaterial* pMat, aiReturn aiGetMaterialUVTransform(const aiMaterial* pMat,
const char* pKey, const char* pKey,
unsigned int type, unsigned int type,
unsigned int index, unsigned int index,
aiUVTransform* pOut) aiUVTransform* pOut)
{ {
unsigned int iMax = 4; unsigned int iMax = 5;
return aiGetMaterialFloatArray(pMat,pKey,type,index,(ai_real*)pOut,&iMax); return aiGetMaterialFloatArray(pMat,pKey,type,index,(ai_real*)pOut,&iMax);
} }
@ -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

@ -362,8 +362,8 @@ namespace glTF
ComponentType componentType; //!< The datatype of components in the attribute. (required) ComponentType componentType; //!< The datatype of components in the attribute. (required)
unsigned int count; //!< The number of attributes referenced by this accessor. (required) unsigned int count; //!< The number of attributes referenced by this accessor. (required)
AttribType::Value type; //!< Specifies if the attribute is a scalar, vector, or matrix. (required) AttribType::Value type; //!< Specifies if the attribute is a scalar, vector, or matrix. (required)
std::vector<float> max; //!< Maximum value of each component in this attribute. std::vector<double> max; //!< Maximum value of each component in this attribute.
std::vector<float> min; //!< Minimum value of each component in this attribute. std::vector<double> min; //!< Minimum value of each component in this attribute.
unsigned int GetNumComponents(); unsigned int GetNumComponents();
unsigned int GetBytesPerComponent(); unsigned int GetBytesPerComponent();

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

@ -54,9 +54,9 @@ namespace glTF {
namespace { namespace {
template<size_t N> template<typename T, size_t N>
inline inline
Value& MakeValue(Value& val, float(&r)[N], MemoryPoolAllocator<>& al) { Value& MakeValue(Value& val, T(&r)[N], MemoryPoolAllocator<>& al) {
val.SetArray(); val.SetArray();
val.Reserve(N, al); val.Reserve(N, al);
for (decltype(N) i = 0; i < N; ++i) { for (decltype(N) i = 0; i < N; ++i) {
@ -65,8 +65,9 @@ namespace glTF {
return val; return val;
} }
template<typename T>
inline inline
Value& MakeValue(Value& val, const std::vector<float> & r, MemoryPoolAllocator<>& al) { Value& MakeValue(Value& val, const std::vector<T> & r, MemoryPoolAllocator<>& al) {
val.SetArray(); val.SetArray();
val.Reserve(static_cast<rapidjson::SizeType>(r.size()), al); val.Reserve(static_cast<rapidjson::SizeType>(r.size()), al);
for (unsigned int i = 0; i < r.size(); ++i) { for (unsigned int i = 0; i < r.size(); ++i) {
@ -75,6 +76,16 @@ namespace glTF {
return val; return val;
} }
template<typename C, typename T>
inline Value& MakeValueCast(Value& val, const std::vector<T> & r, MemoryPoolAllocator<>& al) {
val.SetArray();
val.Reserve(static_cast<rapidjson::SizeType>(r.size()), al);
for (unsigned int i = 0; i < r.size(); ++i) {
val.PushBack(static_cast<C>(r[i]), al);
}
return val;
}
template<class T> template<class T>
inline void AddRefsVector(Value& obj, const char* fieldId, std::vector< Ref<T> >& v, MemoryPoolAllocator<>& al) { inline void AddRefsVector(Value& obj, const char* fieldId, std::vector< Ref<T> >& v, MemoryPoolAllocator<>& al) {
if (v.empty()) return; if (v.empty()) return;
@ -100,8 +111,13 @@ namespace glTF {
obj.AddMember("type", StringRef(AttribType::ToString(a.type)), w.mAl); obj.AddMember("type", StringRef(AttribType::ToString(a.type)), w.mAl);
Value vTmpMax, vTmpMin; Value vTmpMax, vTmpMin;
if (a.componentType == ComponentType_FLOAT) {
obj.AddMember("max", MakeValue(vTmpMax, a.max, w.mAl), w.mAl); obj.AddMember("max", MakeValue(vTmpMax, a.max, w.mAl), w.mAl);
obj.AddMember("min", MakeValue(vTmpMin, a.min, w.mAl), w.mAl); obj.AddMember("min", MakeValue(vTmpMin, a.min, w.mAl), w.mAl);
} else {
obj.AddMember("max", MakeValueCast<int64_t>(vTmpMax, a.max, w.mAl), w.mAl);
obj.AddMember("min", MakeValueCast<int64_t>(vTmpMin, a.min, w.mAl), w.mAl);
}
} }
inline void Write(Value& obj, Animation& a, AssetWriter& w) inline void Write(Value& obj, Animation& a, AssetWriter& w)

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

@ -58,6 +58,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Header files, standard library. // Header files, standard library.
#include <memory> #include <memory>
#include <limits>
#include <inttypes.h> #include <inttypes.h>
#ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC #ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC
@ -173,6 +174,62 @@ static void IdentityMatrix4(glTF::mat4& o)
o[12] = 0; o[13] = 0; o[14] = 0; o[15] = 1; o[12] = 0; o[13] = 0; o[14] = 0; o[15] = 1;
} }
template<typename T>
void SetAccessorRange(Ref<Accessor> acc, void* data, unsigned int count,
unsigned int numCompsIn, unsigned int numCompsOut)
{
ai_assert(numCompsOut <= numCompsIn);
// Allocate and initialize with large values.
for (unsigned int i = 0 ; i < numCompsOut ; i++) {
acc->min.push_back( std::numeric_limits<double>::max());
acc->max.push_back(-std::numeric_limits<double>::max());
}
size_t totalComps = count * numCompsIn;
T* buffer_ptr = static_cast<T*>(data);
T* buffer_end = buffer_ptr + totalComps;
// Search and set extreme values.
for (; buffer_ptr < buffer_end ; buffer_ptr += numCompsIn) {
for (unsigned int j = 0 ; j < numCompsOut ; j++) {
double valueTmp = buffer_ptr[j];
if (valueTmp < acc->min[j]) {
acc->min[j] = valueTmp;
}
if (valueTmp > acc->max[j]) {
acc->max[j] = valueTmp;
}
}
}
}
inline void SetAccessorRange(ComponentType compType, Ref<Accessor> acc, void* data,
unsigned int count, unsigned int numCompsIn, unsigned int numCompsOut)
{
switch (compType) {
case ComponentType_SHORT:
SetAccessorRange<short>(acc, data, count, numCompsIn, numCompsOut);
return;
case ComponentType_UNSIGNED_SHORT:
SetAccessorRange<unsigned short>(acc, data, count, numCompsIn, numCompsOut);
return;
case ComponentType_UNSIGNED_INT:
SetAccessorRange<unsigned int>(acc, data, count, numCompsIn, numCompsOut);
return;
case ComponentType_FLOAT:
SetAccessorRange<float>(acc, data, count, numCompsIn, numCompsOut);
return;
case ComponentType_BYTE:
SetAccessorRange<int8_t>(acc, data, count, numCompsIn, numCompsOut);
return;
case ComponentType_UNSIGNED_BYTE:
SetAccessorRange<uint8_t>(acc, data, count, numCompsIn, numCompsOut);
return;
}
}
inline Ref<Accessor> ExportData(Asset& a, std::string& meshName, Ref<Buffer>& buffer, inline Ref<Accessor> ExportData(Asset& a, std::string& meshName, Ref<Buffer>& buffer,
unsigned int count, void* data, AttribType::Value typeIn, AttribType::Value typeOut, ComponentType compType, bool isIndices = false) unsigned int count, void* data, AttribType::Value typeIn, AttribType::Value typeOut, ComponentType compType, bool isIndices = false)
{ {
@ -206,33 +263,7 @@ inline Ref<Accessor> ExportData(Asset& a, std::string& meshName, Ref<Buffer>& bu
acc->type = typeOut; acc->type = typeOut;
// calculate min and max values // calculate min and max values
{ SetAccessorRange(compType, acc, data, count, numCompsIn, numCompsOut);
// Allocate and initialize with large values.
float float_MAX = 10000000000000.0f;
for (unsigned int i = 0 ; i < numCompsOut ; i++) {
acc->min.push_back( float_MAX);
acc->max.push_back(-float_MAX);
}
// Search and set extreme values.
float valueTmp;
for (unsigned int i = 0 ; i < count ; i++) {
for (unsigned int j = 0 ; j < numCompsOut ; j++) {
if (numCompsOut == 1) {
valueTmp = static_cast<unsigned short*>(data)[i];
} else {
valueTmp = static_cast<aiVector3D*>(data)[i][j];
}
if (valueTmp < acc->min[j]) {
acc->min[j] = valueTmp;
}
if (valueTmp > acc->max[j]) {
acc->max[j] = valueTmp;
}
}
}
}
// copy the data // copy the data
acc->WriteData(count, data, numCompsIn*bytesPerComp); acc->WriteData(count, data, numCompsIn*bytesPerComp);

View File

@ -54,6 +54,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/ai_assert.h> #include <assimp/ai_assert.h>
#include <assimp/DefaultLogger.hpp> #include <assimp/DefaultLogger.hpp>
#include <assimp/importerdesc.h> #include <assimp/importerdesc.h>
#include <assimp/commonMetaData.h>
#include <memory> #include <memory>
@ -170,6 +171,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 +333,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 +360,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 +406,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 +433,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);
@ -679,6 +698,25 @@ void glTFImporter::ImportEmbeddedTextures(glTF::Asset& r)
} }
} }
void glTFImporter::ImportCommonMetadata(glTF::Asset& a)
{
ai_assert(mScene->mMetaData == nullptr);
const bool hasVersion = !a.asset.version.empty();
const bool hasGenerator = !a.asset.generator.empty();
if (hasVersion || hasGenerator)
{
mScene->mMetaData = new aiMetadata;
if (hasVersion)
{
mScene->mMetaData->Add(AI_METADATA_SOURCE_FORMAT_VERSION, aiString(a.asset.version));
}
if (hasGenerator)
{
mScene->mMetaData->Add(AI_METADATA_SOURCE_GENERATOR, aiString(a.asset.generator));
}
}
}
void glTFImporter::InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) void glTFImporter::InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
{ {
// clean all member arrays // clean all member arrays
@ -705,7 +743,7 @@ void glTFImporter::InternReadFile(const std::string& pFile, aiScene* pScene, IOS
ImportLights(asset); ImportLights(asset);
ImportNodes(asset); ImportNodes(asset);
ImportCommonMetadata(asset);
if (pScene->mNumMeshes == 0) { if (pScene->mNumMeshes == 0) {
pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;

View File

@ -83,7 +83,7 @@ private:
void ImportCameras(glTF::Asset& a); void ImportCameras(glTF::Asset& a);
void ImportLights(glTF::Asset& a); void ImportLights(glTF::Asset& a);
void ImportNodes(glTF::Asset& a); void ImportNodes(glTF::Asset& a);
void ImportCommonMetadata(glTF::Asset& a);
}; };
} // Namespace assimp } // Namespace assimp

View File

@ -387,8 +387,8 @@ namespace glTF2
ComponentType componentType; //!< The datatype of components in the attribute. (required) ComponentType componentType; //!< The datatype of components in the attribute. (required)
size_t count; //!< The number of attributes referenced by this accessor. (required) size_t count; //!< The number of attributes referenced by this accessor. (required)
AttribType::Value type; //!< Specifies if the attribute is a scalar, vector, or matrix. (required) AttribType::Value type; //!< Specifies if the attribute is a scalar, vector, or matrix. (required)
std::vector<float> max; //!< Maximum value of each component in this attribute. std::vector<double> max; //!< Maximum value of each component in this attribute.
std::vector<float> min; //!< Minimum value of each component in this attribute. std::vector<double> min; //!< Minimum value of each component in this attribute.
unsigned int GetNumComponents(); unsigned int GetNumComponents();
unsigned int GetBytesPerComponent(); unsigned int GetBytesPerComponent();
@ -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,9 +1031,15 @@ 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;
//! Keeps info about the required extensions
struct RequiredExtensions
{
bool KHR_draco_mesh_compression;
} extensionsRequired;
AssetMetadata asset; AssetMetadata asset;
@ -1069,6 +1082,7 @@ namespace glTF2
, textures (*this, "textures") , textures (*this, "textures")
{ {
memset(&extensionsUsed, 0, sizeof(extensionsUsed)); memset(&extensionsUsed, 0, sizeof(extensionsUsed));
memset(&extensionsRequired, 0, sizeof(extensionsRequired));
} }
//! Main function //! Main function
@ -1087,6 +1101,7 @@ namespace glTF2
void ReadBinaryHeader(IOStream& stream, std::vector<char>& sceneData); void ReadBinaryHeader(IOStream& stream, std::vector<char>& sceneData);
void ReadExtensionsUsed(Document& doc); void ReadExtensionsUsed(Document& doc);
void ReadExtensionsRequired(Document& doc);
IOStream* OpenFile(std::string path, const char* mode, bool absolute = false); IOStream* OpenFile(std::string path, const char* mode, bool absolute = false);
}; };

View File

@ -270,13 +270,14 @@ Ref<T> LazyDict<T>::Retrieve(unsigned int i)
throw DeadlyImportError("GLTF: Object at index \"" + to_string(i) + "\" is not a JSON object"); throw DeadlyImportError("GLTF: Object at index \"" + to_string(i) + "\" is not a JSON object");
} }
T* inst = new T(); // Unique ptr prevents memory leak in case of Read throws an exception
auto inst = std::unique_ptr<T>(new T());
inst->id = std::string(mDictId) + "_" + to_string(i); inst->id = std::string(mDictId) + "_" + to_string(i);
inst->oIndex = i; inst->oIndex = i;
ReadMember(obj, "name", inst->name); ReadMember(obj, "name", inst->name);
inst->Read(obj, mAsset); inst->Read(obj, mAsset);
return Add(inst); return Add(inst.release());
} }
template<class T> template<class T>
@ -800,8 +801,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 +904,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");
} }
} }
@ -1403,6 +1433,12 @@ inline void Asset::Load(const std::string& pFile, bool isBinary)
// Load the metadata // Load the metadata
asset.Read(doc); asset.Read(doc);
ReadExtensionsUsed(doc); ReadExtensionsUsed(doc);
ReadExtensionsRequired(doc);
// Currently Draco is not supported
if (extensionsRequired.KHR_draco_mesh_compression) {
throw DeadlyImportError("GLTF: Draco mesh compression not currently supported.");
}
// Prepare the dictionaries // Prepare the dictionaries
for (size_t i = 0; i < mDicts.size(); ++i) { for (size_t i = 0; i < mDicts.size(); ++i) {
@ -1449,6 +1485,29 @@ inline void Asset::SetAsBinary()
} }
} }
// As required extensions are only a concept in glTF 2.0, this is here
// instead of glTFCommon.h
#define CHECK_REQUIRED_EXT(EXT) \
if (exts.find(#EXT) != exts.end()) extensionsRequired.EXT = true;
inline void Asset::ReadExtensionsRequired(Document& doc)
{
Value* extsRequired = FindArray(doc, "extensionsRequired");
if (nullptr == extsRequired) {
return;
}
std::gltf_unordered_map<std::string, bool> exts;
for (unsigned int i = 0; i < extsRequired->Size(); ++i) {
if ((*extsRequired)[i].IsString()) {
exts[(*extsRequired)[i].GetString()] = true;
}
}
CHECK_REQUIRED_EXT(KHR_draco_mesh_compression);
#undef CHECK_REQUIRED_EXT
}
inline void Asset::ReadExtensionsUsed(Document& doc) inline void Asset::ReadExtensionsUsed(Document& doc)
{ {
@ -1463,12 +1522,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

@ -54,8 +54,8 @@ namespace glTF2 {
namespace { namespace {
template<size_t N> template<typename T, size_t N>
inline Value& MakeValue(Value& val, float(&r)[N], MemoryPoolAllocator<>& al) { inline Value& MakeValue(Value& val, T(&r)[N], MemoryPoolAllocator<>& al) {
val.SetArray(); val.SetArray();
val.Reserve(N, al); val.Reserve(N, al);
for (decltype(N) i = 0; i < N; ++i) { for (decltype(N) i = 0; i < N; ++i) {
@ -64,7 +64,8 @@ namespace glTF2 {
return val; return val;
} }
inline Value& MakeValue(Value& val, const std::vector<float> & r, MemoryPoolAllocator<>& al) { template<typename T>
inline Value& MakeValue(Value& val, const std::vector<T> & r, MemoryPoolAllocator<>& al) {
val.SetArray(); val.SetArray();
val.Reserve(static_cast<rapidjson::SizeType>(r.size()), al); val.Reserve(static_cast<rapidjson::SizeType>(r.size()), al);
for (unsigned int i = 0; i < r.size(); ++i) { for (unsigned int i = 0; i < r.size(); ++i) {
@ -73,8 +74,19 @@ namespace glTF2 {
return val; return val;
} }
inline Value& MakeValue(Value& val, float r, MemoryPoolAllocator<>& /*al*/) { template<typename C, typename T>
val.SetDouble(r); inline Value& MakeValueCast(Value& val, const std::vector<T> & r, MemoryPoolAllocator<>& al) {
val.SetArray();
val.Reserve(static_cast<rapidjson::SizeType>(r.size()), al);
for (unsigned int i = 0; i < r.size(); ++i) {
val.PushBack(static_cast<C>(r[i]), al);
}
return val;
}
template<typename T>
inline Value& MakeValue(Value& val, T r, MemoryPoolAllocator<>& /*al*/) {
val.Set(r);
return val; return val;
} }
@ -104,8 +116,13 @@ namespace glTF2 {
obj.AddMember("type", StringRef(AttribType::ToString(a.type)), w.mAl); obj.AddMember("type", StringRef(AttribType::ToString(a.type)), w.mAl);
Value vTmpMax, vTmpMin; Value vTmpMax, vTmpMin;
if (a.componentType == ComponentType_FLOAT) {
obj.AddMember("max", MakeValue(vTmpMax, a.max, w.mAl), w.mAl); obj.AddMember("max", MakeValue(vTmpMax, a.max, w.mAl), w.mAl);
obj.AddMember("min", MakeValue(vTmpMin, a.min, w.mAl), w.mAl); obj.AddMember("min", MakeValue(vTmpMin, a.min, w.mAl), w.mAl);
} else {
obj.AddMember("max", MakeValueCast<int64_t>(vTmpMax, a.max, w.mAl), w.mAl);
obj.AddMember("min", MakeValueCast<int64_t>(vTmpMin, a.min, w.mAl), w.mAl);
}
} }
inline void Write(Value& obj, Animation& a, AssetWriter& w) inline void Write(Value& obj, Animation& a, AssetWriter& w)
@ -358,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

@ -58,6 +58,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Header files, standard library. // Header files, standard library.
#include <memory> #include <memory>
#include <limits>
#include <inttypes.h> #include <inttypes.h>
using namespace rapidjson; using namespace rapidjson;
@ -152,6 +153,62 @@ static void IdentityMatrix4(mat4& o) {
o[12] = 0; o[13] = 0; o[14] = 0; o[15] = 1; o[12] = 0; o[13] = 0; o[14] = 0; o[15] = 1;
} }
template<typename T>
void SetAccessorRange(Ref<Accessor> acc, void* data, size_t count,
unsigned int numCompsIn, unsigned int numCompsOut)
{
ai_assert(numCompsOut <= numCompsIn);
// Allocate and initialize with large values.
for (unsigned int i = 0 ; i < numCompsOut ; i++) {
acc->min.push_back( std::numeric_limits<double>::max());
acc->max.push_back(-std::numeric_limits<double>::max());
}
size_t totalComps = count * numCompsIn;
T* buffer_ptr = static_cast<T*>(data);
T* buffer_end = buffer_ptr + totalComps;
// Search and set extreme values.
for (; buffer_ptr < buffer_end ; buffer_ptr += numCompsIn) {
for (unsigned int j = 0 ; j < numCompsOut ; j++) {
double valueTmp = buffer_ptr[j];
if (valueTmp < acc->min[j]) {
acc->min[j] = valueTmp;
}
if (valueTmp > acc->max[j]) {
acc->max[j] = valueTmp;
}
}
}
}
inline void SetAccessorRange(ComponentType compType, Ref<Accessor> acc, void* data,
size_t count, unsigned int numCompsIn, unsigned int numCompsOut)
{
switch (compType) {
case ComponentType_SHORT:
SetAccessorRange<short>(acc, data, count, numCompsIn, numCompsOut);
return;
case ComponentType_UNSIGNED_SHORT:
SetAccessorRange<unsigned short>(acc, data, count, numCompsIn, numCompsOut);
return;
case ComponentType_UNSIGNED_INT:
SetAccessorRange<unsigned int>(acc, data, count, numCompsIn, numCompsOut);
return;
case ComponentType_FLOAT:
SetAccessorRange<float>(acc, data, count, numCompsIn, numCompsOut);
return;
case ComponentType_BYTE:
SetAccessorRange<int8_t>(acc, data, count, numCompsIn, numCompsOut);
return;
case ComponentType_UNSIGNED_BYTE:
SetAccessorRange<uint8_t>(acc, data, count, numCompsIn, numCompsOut);
return;
}
}
inline Ref<Accessor> ExportData(Asset& a, std::string& meshName, Ref<Buffer>& buffer, inline Ref<Accessor> ExportData(Asset& a, std::string& meshName, Ref<Buffer>& buffer,
size_t count, void* data, AttribType::Value typeIn, AttribType::Value typeOut, ComponentType compType, bool isIndices = false) size_t count, void* data, AttribType::Value typeIn, AttribType::Value typeOut, ComponentType compType, bool isIndices = false)
{ {
@ -187,33 +244,7 @@ inline Ref<Accessor> ExportData(Asset& a, std::string& meshName, Ref<Buffer>& bu
acc->type = typeOut; acc->type = typeOut;
// calculate min and max values // calculate min and max values
{ SetAccessorRange(compType, acc, data, count, numCompsIn, numCompsOut);
// Allocate and initialize with large values.
float float_MAX = 10000000000000.0f;
for (unsigned int i = 0 ; i < numCompsOut ; i++) {
acc->min.push_back( float_MAX);
acc->max.push_back(-float_MAX);
}
// Search and set extreme values.
float valueTmp;
for (unsigned int i = 0 ; i < count ; i++) {
for (unsigned int j = 0 ; j < numCompsOut ; j++) {
if (numCompsOut == 1) {
valueTmp = static_cast<unsigned short*>(data)[i];
} else {
valueTmp = static_cast<aiVector3D*>(data)[i][j];
}
if (valueTmp < acc->min[j]) {
acc->min[j] = valueTmp;
}
if (valueTmp > acc->max[j]) {
acc->max[j] = valueTmp;
}
}
}
}
// copy the data // copy the data
acc->WriteData(count, data, numCompsIn*bytesPerComp); acc->WriteData(count, data, numCompsIn*bytesPerComp);
@ -320,7 +351,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,19 @@ 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 <assimp/commonMetaData.h>
#include <memory> #include <memory>
#include <unordered_map> #include <unordered_map>
@ -91,11 +92,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 +104,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 +124,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 +178,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 +202,26 @@ 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.mScaling.x = prop.TextureTransformExt_t.scale[0];
transform.mScaling.y = prop.TextureTransformExt_t.scale[1];
transform.mRotation = -prop.TextureTransformExt_t.rotation; // must be negated
// A change of coordinates is required to map glTF UV transformations into the space used by
// Assimp. In glTF all UV origins are at 0,1 (top left of texture) in Assimp space. In Assimp
// rotation occurs around the image center (0.5,0.5) where as in glTF rotation is around the
// texture origin. All three can be corrected for solely by a change of the translation since
// the transformations available are shape preserving. Note the importer already flips the V
// coordinate of the actual meshes during import.
const ai_real rcos(cos(-transform.mRotation));
const ai_real rsin(sin(-transform.mRotation));
transform.mTranslation.x = (0.5 * transform.mScaling.x) * (-rcos + rsin + 1) + prop.TextureTransformExt_t.offset[0];
transform.mTranslation.y = ((0.5 * transform.mScaling.y) * (rsin + rcos - 1)) + 1 - transform.mScaling.y - prop.TextureTransformExt_t.offset[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,8 +248,7 @@ 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) {
@ -243,8 +256,7 @@ inline void SetMaterialTextureProperty(std::vector<int>& embeddedTexIdxs, Asset&
} }
} }
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) {
@ -252,8 +264,7 @@ inline void SetMaterialTextureProperty(std::vector<int>& embeddedTexIdxs, Asset&
} }
} }
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()) {
@ -311,8 +322,7 @@ 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;
@ -325,24 +335,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 +357,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,8 +369,7 @@ 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;
@ -406,7 +410,6 @@ 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;
@ -508,7 +511,6 @@ void glTF2Importer::ImportMeshes(glTF2::Asset& r)
} }
} }
aiFace *faces = 0; aiFace *faces = 0;
size_t nFaces = 0; size_t nFaces = 0;
@ -530,6 +532,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 +559,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 +574,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 +593,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 +610,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 +637,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,13 +652,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], 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);
} }
@ -670,11 +681,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,8 +692,7 @@ 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();
@ -716,8 +724,7 @@ void glTF2Importer::ImportCameras(glTF2::Asset& r)
} }
} }
void glTF2Importer::ImportLights(glTF2::Asset& r) void glTF2Importer::ImportLights(glTF2::Asset &r) {
{
if (!r.lights.Size()) if (!r.lights.Size())
return; return;
@ -729,18 +736,19 @@ void glTF2Importer::ImportLights(glTF2::Asset& r)
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 +758,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,8 +776,7 @@ 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;
} }
@ -782,8 +786,7 @@ void glTF2Importer::ImportLights(glTF2::Asset& r)
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,8 +811,7 @@ 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,12 +822,18 @@ 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 {
float values[4];
};
Weights *weights = nullptr; 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];
};
struct Indices16 {
uint16_t values[4];
};
Indices8 *indices8 = nullptr; Indices8 *indices8 = nullptr;
Indices16 *indices16 = nullptr; Indices16 *indices16 = nullptr;
if (attr.joint[0]->GetElementSize() == 4) { if (attr.joint[0]->GetElementSize() == 4) {
@ -856,13 +864,11 @@ 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));
@ -964,8 +970,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,8 +979,7 @@ 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;
@ -984,8 +988,7 @@ void glTF2Importer::ImportNodes(glTF2::Asset& r)
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) {
@ -995,27 +998,24 @@ void glTF2Importer::ImportNodes(glTF2::Asset& r)
} }
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);
@ -1036,7 +1036,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];
@ -1094,8 +1094,43 @@ 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];
@ -1112,14 +1147,15 @@ std::unordered_map<unsigned int, AnimationSamplers> GatherSamplers(Animation& an
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();
@ -1138,15 +1174,41 @@ void glTF2Importer::ImportAnimations(glTF2::Asset& r)
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 +1237,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;
@ -1228,8 +1302,25 @@ void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset& r)
} }
} }
void glTF2Importer::InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) void glTF2Importer::ImportCommonMetadata(glTF2::Asset& a) {
ai_assert(mScene->mMetaData == nullptr);
const bool hasVersion = !a.asset.version.empty();
const bool hasGenerator = !a.asset.generator.empty();
if (hasVersion || hasGenerator)
{ {
mScene->mMetaData = new aiMetadata;
if (hasVersion)
{
mScene->mMetaData->Add(AI_METADATA_SOURCE_FORMAT_VERSION, aiString(a.asset.version));
}
if (hasGenerator)
{
mScene->mMetaData->Add(AI_METADATA_SOURCE_GENERATOR, aiString(a.asset.generator));
}
}
}
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();
@ -1256,10 +1347,11 @@ void glTF2Importer::InternReadFile(const std::string& pFile, aiScene* pScene, IO
ImportAnimations(asset); ImportAnimations(asset);
ImportCommonMetadata(asset);
if (pScene->mNumMeshes == 0) { if (pScene->mNumMeshes == 0) {
pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
} }
} }
#endif // ASSIMP_BUILD_NO_GLTF_IMPORTER #endif // ASSIMP_BUILD_NO_GLTF_IMPORTER

View File

@ -84,6 +84,7 @@ private:
void ImportLights(glTF2::Asset& a); void ImportLights(glTF2::Asset& a);
void ImportNodes(glTF2::Asset& a); void ImportNodes(glTF2::Asset& a);
void ImportAnimations(glTF2::Asset& a); void ImportAnimations(glTF2::Asset& a);
void ImportCommonMetadata(glTF2::Asset& a);
}; };
} // Namespace assimp } // Namespace assimp

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.
*
Args: * @param zip zip archive handler.
zip: zip archive handler. * @param buf preallocated output buffer.
buf: preallocated output buffer. * @param bufsize output buffer size (in bytes).
bufsize: output buffer size (in bytes). *
* @note ensure supplied output buffer is large enough.
Note: * zip_entry_size function (returns uncompressed size for the current
- ensure supplied output buffer is large enough. * entry) can be handy to estimate how big buffer is needed. for large
- zip_entry_size function (returns uncompressed size for the current entry) * entries, please take a look at zip_entry_extract function.
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).
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); extern ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf,
size_t bufsize);
/* /**
Extracts the current zip entry into output file. * Extracts the current zip entry into output file.
*
Args: * @param zip zip archive handler.
zip: zip archive handler. * @param filename output file.
filename: output 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_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;
} }
}; };
@ -116,10 +119,20 @@ struct ExceptionSwallower<void> {
{\ {\
try { try {
#define ASSIMP_END_EXCEPTION_REGION_WITH_ERROR_STRING(type, ASSIMP_END_EXCEPTION_REGION_errorString)\
} catch(const DeadlyImportError& e) {\
ASSIMP_END_EXCEPTION_REGION_errorString = e.what();\
return ExceptionSwallower<type>()();\
} catch(...) {\
ASSIMP_END_EXCEPTION_REGION_errorString = "Unknown exception";\
return ExceptionSwallower<type>()();\
}\
}
#define ASSIMP_END_EXCEPTION_REGION(type)\ #define ASSIMP_END_EXCEPTION_REGION(type)\
} catch(...) {\ } catch(...) {\
return ExceptionSwallower<type>()();\ return ExceptionSwallower<type>()();\
}\ }\
} }
#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 {
@ -68,7 +72,7 @@ for(LineSplitter splitter(stream);splitter;++splitter) {
if (strtol(splitter[2]) > 5) { .. } if (strtol(splitter[2]) > 5) { .. }
} }
std::cout << "Current line is: " << splitter.get_index() << std::endl; ASSIMP_LOG_DEBUG_F("Current line is: ", splitter.get_index());
} }
@endcode @endcode
*/ */
@ -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 {

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