Merge pull request #3 from assimp/master

Update to the latest version from upstream
pull/2987/head
Tomas Mariancik 2019-12-28 16:45:08 +01:00 committed by GitHub
commit 45601a49f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
184 changed files with 18922 additions and 3464 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

@ -77,6 +77,7 @@ The cmake-build-environment provides options to configure the build. The followi
- **ASSIMP_BUILD_SAMPLES ( default OFF )**: If the official samples are built as well (needs Glut). - **ASSIMP_BUILD_SAMPLES ( default OFF )**: If the official samples are built as well (needs Glut).
- **ASSIMP_BUILD_TESTS ( default ON )**: If the test suite for Assimp is built in addition to the library. - **ASSIMP_BUILD_TESTS ( default ON )**: If the test suite for Assimp is built in addition to the library.
- **ASSIMP_COVERALLS ( default OFF )**: Enable this to measure test coverage. - **ASSIMP_COVERALLS ( default OFF )**: Enable this to measure test coverage.
- **ASSIMP_ERROR_MAX( default OFF)**: Enable all warnings.
- **ASSIMP_WERROR( default OFF )**: Treat warnings as errors. - **ASSIMP_WERROR( default OFF )**: Treat warnings as errors.
- **ASSIMP_ASAN ( default OFF )**: Enable AddressSanitizer. - **ASSIMP_ASAN ( default OFF )**: Enable AddressSanitizer.
- **ASSIMP_UBSAN ( default OFF )**: Enable Undefined Behavior sanitizer. - **ASSIMP_UBSAN ( default OFF )**: Enable Undefined Behavior sanitizer.

View File

@ -100,6 +100,10 @@ OPTION ( ASSIMP_COVERALLS
"Enable this to measure test coverage." "Enable this to measure test coverage."
OFF OFF
) )
OPTION ( ASSIMP_ERROR_MAX
"Enable all warnings."
OFF
)
OPTION ( ASSIMP_WERROR OPTION ( ASSIMP_WERROR
"Treat warnings as errors." "Treat warnings as errors."
OFF OFF
@ -253,7 +257,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 "${CMAKE_CXX_FLAGS_DEBUG} /D_DEBUG /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}")
@ -294,6 +298,16 @@ IF (ASSIMP_COVERALLS)
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage") SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage")
ENDIF() ENDIF()
IF (ASSIMP_ERROR_MAX)
MESSAGE(STATUS "Turning on all warnings")
IF (MSVC)
ADD_COMPILE_OPTIONS(/W4) # NB: there is a /Wall option, pedantic mode
ELSE()
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
ENDIF()
ENDIF()
IF (ASSIMP_WERROR) IF (ASSIMP_WERROR)
MESSAGE(STATUS "Treating warnings as errors") MESSAGE(STATUS "Treating warnings as errors")
IF (MSVC) IF (MSVC)
@ -391,6 +405,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

View File

@ -14,6 +14,7 @@ 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 - Visual Studio 2019
@ -29,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 "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2019" set CMAKE_GENERATOR_NAME=Visual Studio 16 2019 - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2019" set CMAKE_GENERATOR_NAME=Visual Studio 16 2019
- cmake %CMAKE_DEFINES% -G "%CMAKE_GENERATOR_NAME%" -A %platform% . - 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,10 +65,13 @@ 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(APPLE) if(WIN32)
set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@.@ASSIMP_VERSION_MAJOR@@CMAKE_SHARED_LIBRARY_SUFFIX@") # Handle MinGW compiler.
else(APPLE) set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@@CMAKE_SHARED_LIBRARY_SUFFIX@@CMAKE_STATIC_LIBRARY_SUFFIX@")
set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@@CMAKE_SHARED_LIBRARY_SUFFIX@.@ASSIMP_VERSION_MAJOR@") 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@")
endif() endif()
set_target_properties(assimp::assimp PROPERTIES set_target_properties(assimp::assimp PROPERTIES
IMPORTED_SONAME_DEBUG "${sharedLibraryName}" IMPORTED_SONAME_DEBUG "${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,13 +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(APPLE) if(WIN32)
set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}.@ASSIMP_VERSION_MAJOR@@CMAKE_SHARED_LIBRARY_SUFFIX@") # Handle MinGW compiler.
else(APPLE) set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@@CMAKE_SHARED_LIBRARY_SUFFIX@@CMAKE_STATIC_LIBRARY_SUFFIX@")
set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_SHARED_LIBRARY_SUFFIX@.@ASSIMP_VERSION_MAJOR@") 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@")
endif() 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

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

@ -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,53 +383,55 @@ 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] );
}
} }
} }
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void B3DImporter::ReadTRIS( int v0 ){ void B3DImporter::ReadTRIS(int v0) {
int matid=ReadInt(); int matid = ReadInt();
if( matid==-1 ){ if (matid == -1) {
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");
} }
std::unique_ptr<aiMesh> mesh(new aiMesh); std::unique_ptr<aiMesh> mesh(new aiMesh);
mesh->mMaterialIndex=matid; mesh->mMaterialIndex = matid;
mesh->mNumFaces=0; mesh->mNumFaces = 0;
mesh->mPrimitiveTypes=aiPrimitiveType_TRIANGLE; mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
int n_tris=ChunkSize()/12; int n_tris = ChunkSize() / 12;
aiFace *face=mesh->mFaces=new aiFace[n_tris]; aiFace *face = mesh->mFaces = new aiFace[n_tris];
for( int i=0;i<n_tris;++i ){ for (int i = 0; i < n_tris; ++i) {
int i0=ReadInt()+v0; int i0 = ReadInt() + v0;
int i1=ReadInt()+v0; int i1 = ReadInt() + 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;
} }
face->mNumIndices=3; face->mNumIndices = 3;
face->mIndices=new unsigned[3]; face->mIndices = new unsigned[3];
face->mIndices[0]=i0; face->mIndices[0] = i0;
face->mIndices[1]=i1; face->mIndices[1] = i1;
face->mIndices[2]=i2; face->mIndices[2] = i2;
++mesh->mNumFaces; ++mesh->mNumFaces;
++face; ++face;
} }
_meshes.emplace_back( std::move(mesh) ); _meshes.emplace_back(std::move(mesh));
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -453,29 +452,23 @@ void B3DImporter::ReadMESH(){
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void B3DImporter::ReadBONE( int id ){ void B3DImporter::ReadBONE(int id) {
while( ChunkSize() ){ while (ChunkSize()) {
int vertex=ReadInt(); int vertex = ReadInt();
float weight=ReadFloat(); float weight = ReadFloat();
if( vertex<0 || vertex>=(int)_vertices.size() ){ if (vertex < 0 || vertex >= (int)_vertices.size()) {
Fail( "Bad vertex index" ); Fail("Bad vertex index");
} }
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,11 +626,15 @@ 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 ){
aiNode *node=_nodes[i]; aiNode *node=_nodes[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

@ -407,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
@ -440,6 +454,16 @@ ADD_ASSIMP_IMPORTER( MDL
MDL/MDLLoader.cpp MDL/MDLLoader.cpp
MDL/MDLLoader.h MDL/MDLLoader.h
MDL/MDLMaterialLoader.cpp MDL/MDLMaterialLoader.cpp
MDL/HalfLife/HalfLifeMDLBaseHeader.h
MDL/HalfLife/HL1FileData.h
MDL/HalfLife/HL1MDLLoader.cpp
MDL/HalfLife/HL1MDLLoader.h
MDL/HalfLife/HL1ImportDefinitions.h
MDL/HalfLife/HL1ImportSettings.h
MDL/HalfLife/HL1MeshTrivert.h
MDL/HalfLife/LogFunctions.h
MDL/HalfLife/UniqueNameGenerator.cpp
MDL/HalfLife/UniqueNameGenerator.h
) )
SET( MaterialSystem_SRCS SET( MaterialSystem_SRCS
@ -670,6 +694,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
) )

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -107,7 +107,7 @@ LogStream* LogStream::createDefaultStream(aiDefaultLogStream streams,
return nullptr; return nullptr;
#endif #endif
// Platform-independent default streams // Platform-independent default streams
case aiDefaultLogStream_STDERR: case aiDefaultLogStream_STDERR:
return new StdOStreamLogStream(std::cerr); return new StdOStreamLogStream(std::cerr);
case aiDefaultLogStream_STDOUT: case aiDefaultLogStream_STDOUT:
@ -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 ExportSceneM3DA(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("m3da", "Model 3D (ascii)", "a3d", &ExportSceneM3DA, 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,24 +247,28 @@ Exporter :: Exporter()
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
Exporter::~Exporter() { Exporter::~Exporter() {
FreeBlob(); ai_assert(nullptr != pimpl);
FreeBlob();
delete pimpl; delete pimpl;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Exporter::SetIOHandler( IOSystem* pIOHandler) { void Exporter::SetIOHandler( IOSystem* pIOHandler) {
pimpl->mIsDefaultIOHandler = !pIOHandler; ai_assert(nullptr != pimpl);
pimpl->mIsDefaultIOHandler = !pIOHandler;
pimpl->mIOSystem.reset(pIOHandler); pimpl->mIOSystem.reset(pIOHandler);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
IOSystem* Exporter::GetIOHandler() const { IOSystem* Exporter::GetIOHandler() const {
return pimpl->mIOSystem.get(); ai_assert(nullptr != pimpl);
return pimpl->mIOSystem.get();
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
bool Exporter::IsDefaultIOHandler() const { bool Exporter::IsDefaultIOHandler() const {
return pimpl->mIsDefaultIOHandler; ai_assert(nullptr != pimpl);
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,30 +480,34 @@ void Exporter::FreeBlob() {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
const aiExportDataBlob* Exporter::GetBlob() const { const aiExportDataBlob* Exporter::GetBlob() const {
return pimpl->blob; ai_assert(nullptr != pimpl);
return pimpl->blob;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
const aiExportDataBlob* Exporter::GetOrphanedBlob() const { const aiExportDataBlob* Exporter::GetOrphanedBlob() const {
const aiExportDataBlob* tmp = pimpl->blob; ai_assert(nullptr != pimpl);
const aiExportDataBlob *tmp = pimpl->blob;
pimpl->blob = nullptr; pimpl->blob = nullptr;
return tmp; return tmp;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
size_t Exporter::GetExportFormatCount() const { size_t Exporter::GetExportFormatCount() const {
ai_assert(nullptr != pimpl);
return pimpl->mExporters.size(); return pimpl->mExporters.size();
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
const aiExportFormatDesc* Exporter::GetExportFormatDescription( size_t index ) const { const aiExportFormatDesc* Exporter::GetExportFormatDescription( size_t index ) const {
if (index >= GetExportFormatCount()) { ai_assert(nullptr != pimpl);
if (index >= GetExportFormatCount()) {
return nullptr; return nullptr;
} }
// Return from static storage if the requested index is built-in. // Return from static storage if the requested index is built-in.
if (index < sizeof(gExporters) / sizeof(gExporters[0])) { if (index < pimpl->mExporters.size()) {
return &gExporters[index].mDescription; return &pimpl->mExporters[index].mDescription;
} }
return &pimpl->mExporters[index].mDescription; return &pimpl->mExporters[index].mDescription;
@ -510,7 +515,8 @@ const aiExportFormatDesc* Exporter::GetExportFormatDescription( size_t index ) c
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
aiReturn Exporter::RegisterExporter(const ExportFormatEntry& desc) { aiReturn Exporter::RegisterExporter(const ExportFormatEntry& desc) {
for(const ExportFormatEntry& e : pimpl->mExporters) { ai_assert(nullptr != pimpl);
for (const ExportFormatEntry &e : pimpl->mExporters) {
if (!strcmp(e.mDescription.id,desc.mDescription.id)) { if (!strcmp(e.mDescription.id,desc.mDescription.id)) {
return aiReturn_FAILURE; return aiReturn_FAILURE;
} }
@ -522,7 +528,8 @@ aiReturn Exporter::RegisterExporter(const ExportFormatEntry& desc) {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Exporter::UnregisterExporter(const char* id) { void Exporter::UnregisterExporter(const char* id) {
for(std::vector<ExportFormatEntry>::iterator it = pimpl->mExporters.begin(); ai_assert(nullptr != pimpl);
for (std::vector<ExportFormatEntry>::iterator it = pimpl->mExporters.begin();
it != pimpl->mExporters.end(); ++it) { it != pimpl->mExporters.end(); ++it) {
if (!strcmp((*it).mDescription.id,id)) { if (!strcmp((*it).mDescription.id,id)) {
pimpl->mExporters.erase(it); pimpl->mExporters.erase(it);

View File

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

@ -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 = 5; #include "revision.h"
static const unsigned int MinorVersion = 0;
// -------------------------------------------------------------------------------- // --------------------------------------------------------------------------------
// 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);
} }
} }
@ -1229,18 +1263,18 @@ namespace Assimp {
return indices; return indices;
} }
unsigned int FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model, unsigned int FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model,
MatIndexArray::value_type index, MatIndexArray::value_type index,
const aiMatrix4x4& node_global_transform, aiNode *parent, aiNode *root_node,
aiNode& nd) const aiMatrix4x4 &absolute_transform)
{ {
aiMesh* const out_mesh = SetupEmptyMesh(mesh, nd); aiMesh* const out_mesh = SetupEmptyMesh(mesh, parent);
const MatIndexArray& mindices = mesh.GetMaterialIndices(); const MatIndexArray& mindices = mesh.GetMaterialIndices();
const std::vector<aiVector3D>& vertices = mesh.GetVertices(); const std::vector<aiVector3D>& vertices = mesh.GetVertices();
const std::vector<unsigned int>& faces = mesh.GetFaceIndexCounts(); const std::vector<unsigned int>& faces = mesh.GetFaceIndexCounts();
const bool process_weights = doc.Settings().readWeights && mesh.DeformerSkin() != NULL; const bool process_weights = doc.Settings().readWeights && mesh.DeformerSkin() != nullptr;
unsigned int count_faces = 0; unsigned int count_faces = 0;
unsigned int count_vertices = 0; unsigned int count_vertices = 0;
@ -1300,7 +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;
@ -1449,10 +1483,10 @@ namespace Assimp {
return static_cast<unsigned int>(meshes.size() - 1); return static_cast<unsigned int>(meshes.size() - 1);
} }
void FBXConverter::ConvertWeights(aiMesh* out, const Model& model, const MeshGeometry& geo, void FBXConverter::ConvertWeights(aiMesh *out, const Model &model, const MeshGeometry &geo,
const aiMatrix4x4& node_global_transform, const aiMatrix4x4 &absolute_transform,
unsigned int materialIndex, aiNode *parent, aiNode *root_node, unsigned int materialIndex,
std::vector<unsigned int>* outputVertStartIndices) std::vector<unsigned int> *outputVertStartIndices)
{ {
ai_assert(geo.DeformerSkin()); ai_assert(geo.DeformerSkin());
@ -1463,13 +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,64 +1558,102 @@ 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->mNumBones = static_cast<unsigned int>(bones.size());
std::swap_ranges(bones.begin(), bones.end(), out->mBones);
} }
out->mBones = new aiBone*[bones.size()]();
out->mNumBones = static_cast<unsigned int>(bones.size());
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)) {
bone->mOffsetMatrix.Inverse(); 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;
bone->mOffsetMatrix = bone->mOffsetMatrix * node_global_transform; // store local transform link for post processing
bone->mOffsetMatrix = cl->TransformLink();
bone->mOffsetMatrix.Inverse();
bone->mNumWeights = static_cast<unsigned int>(out_indices.size()); aiMatrix4x4 matrix = (aiMatrix4x4)absolute_transform;
aiVertexWeight* cursor = bone->mWeights = new aiVertexWeight[out_indices.size()];
const size_t no_index_sentinel = std::numeric_limits<size_t>::max(); bone->mOffsetMatrix = bone->mOffsetMatrix * matrix; // * mesh_offset
const WeightArray& weights = cl.GetWeights();
const size_t c = index_out_indices.size();
for (size_t i = 0; i < c; ++i) {
const size_t index_index = index_out_indices[i];
if (index_index == no_index_sentinel) { //
continue; // Now calculate the aiVertexWeights
//
aiVertexWeight *cursor = nullptr;
bone->mNumWeights = static_cast<unsigned int>(out_indices.size());
cursor = bone->mWeights = new aiVertexWeight[out_indices.size()];
const size_t no_index_sentinel = std::numeric_limits<size_t>::max();
const WeightArray& weights = cl->GetWeights();
const size_t c = index_out_indices.size();
for (size_t i = 0; i < c; ++i) {
const size_t index_index = index_out_indices[i];
if (index_index == no_index_sentinel) {
continue;
}
const size_t cc = count_out_indices[i];
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++;
out_weight.mVertexId = static_cast<unsigned int>(out_indices[index_index + j]);
out_weight.mWeight = weights[i];
}
} }
const size_t cc = count_out_indices[i]; bone_map.insert(std::pair<const std::string, aiBone *>(deformer_name, bone));
for (size_t j = 0; j < cc; ++j) {
aiVertexWeight& out_weight = *cursor++;
out_weight.mVertexId = static_cast<unsigned int>(out_indices[index_index + j]);
out_weight.mWeight = weights[i];
}
} }
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;
} }
} }
@ -2017,6 +2089,13 @@ namespace Assimp {
TrySetTextureProperties(out_mat, textures, "Maya|TEX_metallic_map|file", aiTextureType_METALNESS, mesh); TrySetTextureProperties(out_mat, textures, "Maya|TEX_metallic_map|file", aiTextureType_METALNESS, mesh);
TrySetTextureProperties(out_mat, textures, "Maya|TEX_roughness_map|file", aiTextureType_DIFFUSE_ROUGHNESS, mesh); TrySetTextureProperties(out_mat, textures, "Maya|TEX_roughness_map|file", aiTextureType_DIFFUSE_ROUGHNESS, mesh);
TrySetTextureProperties(out_mat, textures, "Maya|TEX_ao_map|file", aiTextureType_AMBIENT_OCCLUSION, mesh); TrySetTextureProperties(out_mat, textures, "Maya|TEX_ao_map|file", aiTextureType_AMBIENT_OCCLUSION, mesh);
// 3DSMax PBR
TrySetTextureProperties(out_mat, textures, "3dsMax|Parameters|base_color_map", aiTextureType_BASE_COLOR, mesh);
TrySetTextureProperties(out_mat, textures, "3dsMax|Parameters|bump_map", aiTextureType_NORMAL_CAMERA, mesh);
TrySetTextureProperties(out_mat, textures, "3dsMax|Parameters|emission_map", aiTextureType_EMISSION_COLOR, mesh);
TrySetTextureProperties(out_mat, textures, "3dsMax|Parameters|metalness_map", aiTextureType_METALNESS, mesh);
TrySetTextureProperties(out_mat, textures, "3dsMax|Parameters|roughness_map", aiTextureType_DIFFUSE_ROUGHNESS, mesh);
} }
void FBXConverter::SetTextureProperties(aiMaterial* out_mat, const LayeredTextureMap& layeredTextures, const MeshGeometry* const mesh) void FBXConverter::SetTextureProperties(aiMaterial* out_mat, const LayeredTextureMap& layeredTextures, const MeshGeometry* const mesh)
@ -2243,13 +2322,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 +2756,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 +2787,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 +3612,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 +3630,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 +3642,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 +3689,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.
@ -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,26 +48,26 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "FBXImporter.h" #include "FBXImporter.h"
#include "FBXTokenizer.h"
#include "FBXParser.h"
#include "FBXUtil.h"
#include "FBXDocument.h"
#include "FBXConverter.h" #include "FBXConverter.h"
#include "FBXDocument.h"
#include "FBXParser.h"
#include "FBXTokenizer.h"
#include "FBXUtil.h"
#include <assimp/StreamReader.h>
#include <assimp/MemoryIOWrapper.h> #include <assimp/MemoryIOWrapper.h>
#include <assimp/Importer.hpp> #include <assimp/StreamReader.h>
#include <assimp/importerdesc.h> #include <assimp/importerdesc.h>
#include <assimp/Importer.hpp>
namespace Assimp { namespace Assimp {
template<> template <>
const char* LogFunctions<FBXImporter>::Prefix() { const char *LogFunctions<FBXImporter>::Prefix() {
static auto prefix = "FBX: "; static auto prefix = "FBX: ";
return prefix; return prefix;
} }
} } // namespace Assimp
using namespace Assimp; using namespace Assimp;
using namespace Assimp::Formatter; using namespace Assimp::Formatter;
@ -76,131 +76,123 @@ using namespace Assimp::FBX;
namespace { namespace {
static const aiImporterDesc desc = { static const aiImporterDesc desc = {
"Autodesk FBX Importer", "Autodesk FBX Importer",
"", "",
"", "",
"", "",
aiImporterFlags_SupportTextFlavour, aiImporterFlags_SupportTextFlavour,
0, 0,
0, 0,
0, 0,
0, 0,
"fbx" "fbx"
}; };
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Constructor to be privately used by #Importer // Constructor to be privately used by #Importer
FBXImporter::FBXImporter() FBXImporter::FBXImporter() {
{
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Destructor, private as well // Destructor, private as well
FBXImporter::~FBXImporter() FBXImporter::~FBXImporter() {
{
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file. // Returns whether the class can handle the format of the given file.
bool FBXImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const bool FBXImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const {
{ const std::string &extension = GetExtension(pFile);
const std::string& extension = GetExtension(pFile); if (extension == std::string(desc.mFileExtensions)) {
if (extension == std::string( desc.mFileExtensions ) ) { return true;
return true; }
}
else if ((!extension.length() || checkSig) && pIOHandler) { else if ((!extension.length() || checkSig) && pIOHandler) {
// at least ASCII-FBX files usually have a 'FBX' somewhere in their head // at least ASCII-FBX files usually have a 'FBX' somewhere in their head
const char* tokens[] = {"fbx"}; const char *tokens[] = { "fbx" };
return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1); return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
} }
return false; return false;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// List all extensions handled by this loader // List all extensions handled by this loader
const aiImporterDesc* FBXImporter::GetInfo () const const aiImporterDesc *FBXImporter::GetInfo() const {
{ return &desc;
return &desc;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Setup configuration properties for the loader // Setup configuration properties for the loader
void FBXImporter::SetupProperties(const Importer* pImp) void FBXImporter::SetupProperties(const Importer *pImp) {
{ settings.readAllLayers = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_GEOMETRY_LAYERS, true);
settings.readAllLayers = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_GEOMETRY_LAYERS, true); settings.readAllMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_MATERIALS, false);
settings.readAllMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_MATERIALS, false); settings.readMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_MATERIALS, true);
settings.readMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_MATERIALS, true); settings.readTextures = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_TEXTURES, true);
settings.readTextures = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_TEXTURES, true); settings.readCameras = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_CAMERAS, true);
settings.readCameras = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_CAMERAS, true); settings.readLights = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_LIGHTS, true);
settings.readLights = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_LIGHTS, true); settings.readAnimations = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ANIMATIONS, true);
settings.readAnimations = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ANIMATIONS, true); settings.strictMode = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_STRICT_MODE, false);
settings.strictMode = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_STRICT_MODE, false); settings.preservePivots = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, true);
settings.preservePivots = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, true); settings.optimizeEmptyAnimationCurves = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES, true);
settings.optimizeEmptyAnimationCurves = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES, true); settings.useLegacyEmbeddedTextureNaming = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING, false);
settings.useLegacyEmbeddedTextureNaming = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING, false); settings.removeEmptyBones = pImp->GetPropertyBool(AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES, true);
settings.removeEmptyBones = pImp->GetPropertyBool(AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES, true); settings.convertToMeters = pImp->GetPropertyBool(AI_CONFIG_FBX_CONVERT_TO_M, false);
settings.convertToMeters = pImp->GetPropertyBool(AI_CONFIG_FBX_CONVERT_TO_M, false);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// 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"); }
}
// read entire file into memory - no streaming for this, fbx // read entire file into memory - no streaming for this, fbx
// files can grow large, but the assimp output data structure // files can grow large, but the assimp output data structure
// then becomes very large, too. Assimp doesn't support // then becomes very large, too. Assimp doesn't support
// streaming for its output data structures so the net win with // streaming for its output data structures so the net win with
// streaming input data would be very low. // streaming input data would be very low.
std::vector<char> contents; std::vector<char> contents;
contents.resize(stream->FileSize()+1); contents.resize(stream->FileSize() + 1);
stream->Read( &*contents.begin(), 1, contents.size()-1 ); stream->Read(&*contents.begin(), 1, contents.size() - 1);
contents[ contents.size() - 1 ] = 0; contents[contents.size() - 1] = 0;
const char* const begin = &*contents.begin(); const char *const begin = &*contents.begin();
// broadphase tokenizing pass in which we identify the core // broadphase tokenizing pass in which we identify the core
// syntax elements of FBX (brackets, commas, key:value mappings) // syntax elements of FBX (brackets, commas, key:value mappings)
TokenList tokens; TokenList tokens;
try { try {
bool is_binary = false; bool is_binary = false;
if (!strncmp(begin,"Kaydara FBX Binary",18)) { if (!strncmp(begin, "Kaydara FBX Binary", 18)) {
is_binary = true; is_binary = true;
TokenizeBinary(tokens,begin,contents.size()); TokenizeBinary(tokens, begin, contents.size());
} } else {
else { Tokenize(tokens, begin);
Tokenize(tokens,begin); }
}
// use this information to construct a very rudimentary // use this information to construct a very rudimentary
// parse-tree representing the FBX scope structure // parse-tree representing the FBX scope structure
Parser parser(tokens, is_binary); Parser parser(tokens, is_binary);
// take the raw parse-tree and convert it to a FBX DOM // take the raw parse-tree and convert it to a FBX DOM
Document doc(parser,settings); Document doc(parser, settings);
// convert the FBX DOM to aiScene // convert the FBX DOM to aiScene
ConvertToAssimpScene(pScene, doc, settings.removeEmptyBones); ConvertToAssimpScene(pScene, doc, settings.removeEmptyBones);
// size relative to cm // size relative to cm
float size_relative_to_cm = doc.GlobalSettings().UnitScaleFactor(); float size_relative_to_cm = doc.GlobalSettings().UnitScaleFactor();
// Set FBX file scale is relative to CM must be converted to M for // Set FBX file scale is relative to CM must be converted to M for
// assimp universal format (M) // assimp universal format (M)
SetFileScale( size_relative_to_cm * 0.01f); SetFileScale(size_relative_to_cm * 0.01f);
std::for_each(tokens.begin(),tokens.end(),Util::delete_fun<Token>()); std::for_each(tokens.begin(), tokens.end(), Util::delete_fun<Token>());
} } catch (std::exception &) {
catch(std::exception&) { std::for_each(tokens.begin(), tokens.end(), Util::delete_fun<Token>());
std::for_each(tokens.begin(),tokens.end(),Util::delete_fun<Token>()); throw;
throw; }
}
} }
#endif // !ASSIMP_BUILD_NO_FBX_IMPORTER #endif // !ASSIMP_BUILD_NO_FBX_IMPORTER

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

@ -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,437 @@
/*
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
#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 "M3DWrapper.h"
#include "M3DExporter.h"
#include "M3DMaterials.h"
// RESOURCES:
// https://gitlab.com/bztsrc/model3d/blob/master/docs/m3d_format.md
// https://gitlab.com/bztsrc/model3d/blob/master/docs/a3d_format.md
/*
* Currently supports static meshes, vertex colors, materials, textures
*
* For animation, it would require the following conversions:
* - aiNode (bones) -> m3d_t.bone (with parent id, position vector and oriantation quaternion)
* - aiMesh.aiBone -> m3d_t.skin (per vertex, with bone id, weight pairs)
* - aiAnimation -> m3d_action (frame with timestamp and list of bone id, position, orientation
* triplets, instead of per bone timestamp + lists)
*/
// ------------------------------------------------------------------------------------------------
// 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;
}
// ------------------------------------------------------------------------------------------------
// convert aiString to identifier safe C string. This is a duplication of _m3d_safestr
char *SafeStr(aiString str, bool isStrict)
{
char *s = (char *)&str.data;
char *d, *ret;
int i, len;
for(len = str.length + 1; *s && (*s == ' ' || *s == '\t'); s++, len--);
if(len > 255) len = 255;
ret = (char *)M3D_MALLOC(len + 1);
if (!ret) {
throw DeadlyExportError("memory allocation error");
}
for(i = 0, d = ret; i < len && *s && *s != '\r' && *s != '\n'; s++, d++, i++) {
*d = isStrict && (*s == ' ' || *s == '\t' || *s == '/' || *s == '\\') ? '_' : (*s == '\t' ? ' ' : *s);
}
for(; d > ret && (*(d-1) == ' ' || *(d-1) == '\t'); d--);
*d = 0;
return ret;
}
// ------------------------------------------------------------------------------------------------
// add a material to the output
M3D_INDEX addMaterial(const Assimp::M3DWrapper &m3d, const aiMaterial *mat) {
unsigned int mi = M3D_NOTDEFINED;
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 == M3D_NOTDEFINED) {
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 = SafeStr(name, true);
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 = SafeStr(name, true);
for (j = 0, i = M3D_NOTDEFINED; j < m3d->numtexture; j++)
if (!strcmp(fn, m3d->texture[j].name)) {
i = j;
free(fn);
break;
}
if (i == M3D_NOTDEFINED) {
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 ExportSceneM3DA(
const char *pFile,
IOSystem *pIOSystem,
const aiScene *pScene,
const ExportProperties *pProperties
) {
#ifdef M3D_ASCII
// initialize the exporter
M3DExporter exporter(pScene, pProperties);
// perform ascii export
exporter.doExport(pFile, pIOSystem, true);
#else
throw DeadlyExportError("Assimp configured without M3D_ASCII support");
#endif
}
// ------------------------------------------------------------------------------------------------
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 = SafeStr(mScene->mRootNode->mName, false);
// 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_NOTDEFINED;
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] = M3D_UNDEF;
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 = M3D_UNDEF;
// 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,770 @@
/*
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_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 "M3DWrapper.h"
#include "M3DImporter.h"
#include "M3DMaterials.h"
// RESOURCES:
// https://gitlab.com/bztsrc/model3d/blob/master/docs/m3d_format.md
// https://gitlab.com/bztsrc/model3d/blob/master/docs/a3d_format.md
/*
Unfortunately aiNode has bone structures and meshes too, yet we can't assign
the mesh to a bone aiNode as a skin may refer to several aiNodes. Therefore
I've decided to import into this structure:
aiScene->mRootNode
| |->mMeshes (all the meshes)
| \->children (empty if there's no skeleton imported, no meshes)
| \->skeleton root aiNode*
| |->bone aiNode
| | \->subbone aiNode
| |->bone aiNode
| | ...
| \->bone aiNode
\->mMeshes[]
\->aiBone, referencing mesh-less aiNodes from above
* - normally one, but if a model has several skeleton roots, then all of them
are listed in aiScene->mRootNode->children, but all without meshes
*/
static const aiImporterDesc desc = {
"Model 3D Importer",
"",
"",
"",
aiImporterFlags_SupportTextFlavour | aiImporterFlags_SupportBinaryFlavour,
0,
0,
0,
0,
#ifdef M3D_ASCII
"m3d a3d"
#else
"m3d"
#endif
};
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"
#ifdef M3D_ASCII
|| extension == "a3d"
#endif
)
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 */
#ifdef M3D_ASCII
|| !memcmp(data, "3dmo", 4) /* ASCII */
#endif
;
}
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 + ".");
}
// extra check for binary format's first 8 bytes. Not done for the ASCII variant
if(!memcmp(buffer.data(), "3DMO", 4) && memcmp(buffer.data() + 4, &fileSize, 4)) {
throw DeadlyImportError("Bad binary header in file " + file + ".");
}
#ifdef M3D_ASCII
// make sure there's a terminator zero character, as input must be ASCIIZ
if(!memcmp(buffer.data(), "3dmo", 4)) {
buffer.push_back(0);
}
#endif
// 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, M3D_NOTDEFINED, 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];
aiTexture *tx = new aiTexture;
tx->mFilename = aiString(std::string(t->name) + ".png");
if (!t->w || !t->h || !t->f || !t->d) {
/* without ASSIMP_USE_M3D_READFILECB, we only have the filename, but no texture data ever */
tx->mWidth = 0;
tx->mHeight = 0;
memcpy(tx->achFormatHint, "png\000", 4);
tx->pcData = nullptr;
} else {
/* if we have the texture loaded, set format hint and pcData too */
tx->mWidth = t->w;
tx->mHeight = t->h;
strcpy(tx->achFormatHint, formatHint[t->f - 1]);
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 = M3D_INDEXMAX;
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 != M3D_UNDEF && m3d->vertex[l].skinid != M3D_INDEXMAX && 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 != M3D_UNDEF) {
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 != M3D_UNDEF) {
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 != M3D_UNDEF && posid < m3d->numvertex);
ai_assert(orientid != M3D_UNDEF && 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 != M3D_UNDEF && s != M3D_INDEXMAX) {
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 != M3D_UNDEF && s != M3D_INDEXMAX) {
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(aiTextureType_AMBIENT_OCCLUSION,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_NORMALS(0) }, /* m3dp_map_N */
{ 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_REFLECTION,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>
#ifdef ASSIMP_USE_M3D_READFILECB
# if (__cplusplus >= 201103L) || !defined(_MSC_VER) || (_MSC_VER >= 1900) // C++11 and MSVC 2015 onwards
# define threadlocal thread_local
# else
# if defined(_MSC_VER) && (_MSC_VER >= 1800) // there's an alternative for MSVC 2013
# define threadlocal __declspec(thread)
# else
# define threadlocal
# endif
# endif
extern "C" {
// workaround: the M3D SDK expects a C callback, but we want to use Assimp::IOSystem to implement that
threadlocal 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 in a single-threaded scenario too 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;
}
}
#endif
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) {
#ifdef ASSIMP_USE_M3D_READFILECB
// pass this IOHandler to the C callback in a thread-local pointer
m3dimporter_pIOHandler = pIOHandler;
m3d_ = m3d_load(const_cast<unsigned char *>(buffer.data()), m3dimporter_readfile, free, nullptr);
// Clear the C callback
m3dimporter_pIOHandler = nullptr;
#else
m3d_ = m3d_load(const_cast<unsigned char *>(buffer.data()), nullptr, nullptr, nullptr);
#endif
}
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,101 @@
#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>
// Assimp specific M3D configuration. Comment out these defines to remove functionality
//#define ASSIMP_USE_M3D_READFILECB
//#define M3D_ASCII
#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

5612
code/M3D/m3d.h 100644

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,600 @@
/*
---------------------------------------------------------------------------
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.
---------------------------------------------------------------------------
*/
/** @file HL1FileData.h
* @brief Definition of in-memory structures for the
* Half-Life 1 MDL file format.
*/
#ifndef AI_HL1FILEDATA_INCLUDED
#define AI_HL1FILEDATA_INCLUDED
#include "HalfLifeMDLBaseHeader.h"
#include <assimp/Compiler/pushpack1.h>
#include <assimp/types.h>
namespace Assimp {
namespace MDL {
namespace HalfLife {
using vec3_t = float[3];
/** \struct Header_HL1
* \brief Data structure for the HL1 MDL file header.
*/
struct Header_HL1 : HalfLifeMDLBaseHeader {
//! The model name.
char name[64];
//! The total file size in bytes.
int32_t length;
//! Ideal eye position.
vec3_t eyeposition;
//! Ideal movement hull size.
vec3_t min;
vec3_t max;
//! Clipping bounding box.
vec3_t bbmin;
vec3_t bbmax;
//! Was "flags".
int32_t unused;
//! The number of bones.
int32_t numbones;
//! Offset to the first bone chunk.
int32_t boneindex;
//! The number of bone controllers.
int32_t numbonecontrollers;
//! Offset to the first bone controller chunk.
int32_t bonecontrollerindex;
//! The number of hitboxes.
int32_t numhitboxes;
//! Offset to the first hitbox chunk.
int32_t hitboxindex;
//! The number of sequences.
int32_t numseq;
//! Offset to the first sequence description chunk.
int32_t seqindex;
//! The number of sequence groups.
int32_t numseqgroups;
//! Offset to the first sequence group chunk.
int32_t seqgroupindex;
//! The number of textures.
int32_t numtextures;
//! Offset to the first texture chunk.
int32_t textureindex;
//! Offset to the first texture's image data.
int32_t texturedataindex;
//! The number of replaceable textures.
int32_t numskinref;
//! The number of skin families.
int32_t numskinfamilies;
//! Offset to the first replaceable texture.
int32_t skinindex;
//! The number of bodyparts.
int32_t numbodyparts;
//! Offset the the first bodypart.
int32_t bodypartindex;
//! The number of attachments.
int32_t numattachments;
//! Offset the the first attachment chunk.
int32_t attachmentindex;
//! Was "soundtable".
int32_t unused2;
//! Was "soundindex".
int32_t unused3;
//! Was "soundgroups".
int32_t unused4;
//! Was "soundgroupindex".
int32_t unused5;
//! The number of nodes in the sequence transition graph.
int32_t numtransitions;
//! Offset the the first sequence transition.
int32_t transitionindex;
} PACK_STRUCT;
/** \struct SequenceHeader_HL1
* \brief Data structure for the file header of a demand loaded
* HL1 MDL sequence group file.
*/
struct SequenceHeader_HL1 : HalfLifeMDLBaseHeader {
//! The sequence group file name.
char name[64];
//! The total file size in bytes.
int32_t length;
} PACK_STRUCT;
/** \struct Bone_HL1
* \brief Data structure for a bone in HL1 MDL files.
*/
struct Bone_HL1 {
//! The bone name.
char name[32];
//! The parent bone index. (-1) If it has no parent.
int32_t parent;
//! Was "flags".
int32_t unused;
//! Available bone controller per motion type.
//! (-1) if no controller is available.
int32_t bonecontroller[6];
/*! Default position and rotation values where
* scale[0] = position.X
* scale[1] = position.Y
* scale[2] = position.Z
* scale[3] = rotation.X
* scale[4] = rotation.Y
* scale[5] = rotation.Z
*/
float value[6];
/*! Compressed scale values where
* scale[0] = position.X scale
* scale[1] = position.Y scale
* scale[2] = position.Z scale
* scale[3] = rotation.X scale
* scale[4] = rotation.Y scale
* scale[5] = rotation.Z scale
*/
float scale[6];
} PACK_STRUCT;
/** \struct BoneController_HL1
* \brief Data structure for a bone controller in HL1 MDL files.
*/
struct BoneController_HL1 {
//! Bone affected by this controller.
int32_t bone;
//! The motion type.
int32_t type;
//! The minimum and maximum values.
float start;
float end;
// Was "rest".
int32_t unused;
// The bone controller channel.
int32_t index;
} PACK_STRUCT;
/** \struct Hitbox_HL1
* \brief Data structure for a hitbox in HL1 MDL files.
*/
struct Hitbox_HL1 {
//! The bone this hitbox follows.
int32_t bone;
//! The hit group.
int32_t group;
//! The hitbox minimum and maximum extents.
vec3_t bbmin;
vec3_t bbmax;
} PACK_STRUCT;
/** \struct SequenceGroup_HL1
* \brief Data structure for a sequence group in HL1 MDL files.
*/
struct SequenceGroup_HL1 {
//! A textual name for this sequence group.
char label[32];
//! The file name.
char name[64];
//! Was "cache".
int32_t unused;
//! Was "data".
int32_t unused2;
} PACK_STRUCT;
//! The type of blending for a sequence.
enum SequenceBlendMode_HL1 {
NoBlend = 1,
TwoWayBlending = 2,
FourWayBlending = 4,
};
/** \struct SequenceDesc_HL1
* \brief Data structure for a sequence description in HL1 MDL files.
*/
struct SequenceDesc_HL1 {
//! The sequence name.
char label[32];
//! Frames per second.
float fps;
//! looping/non-looping flags.
int32_t flags;
//! The sequence activity.
int32_t activity;
//! The sequence activity weight.
int32_t actweight;
//! The number of animation events.
int32_t numevents;
//! Offset the the first animation event chunk.
int32_t eventindex;
//! The number of frames in the sequence.
int32_t numframes;
//! Was "numpivots".
int32_t unused;
//! Was "pivotindex".
int32_t unused2;
//! Linear motion type.
int32_t motiontype;
//! Linear motion bone.
int32_t motionbone;
//! Linear motion.
vec3_t linearmovement;
//! Was "automoveposindex".
int32_t unused3;
//! Was "automoveangleindex".
int32_t unused4;
//! The sequence minimum and maximum extents.
vec3_t bbmin;
vec3_t bbmax;
//! The number of blend animations.
int32_t numblends;
//! Offset to first the AnimValueOffset_HL1 chunk.
//! This offset is relative to the SequenceHeader_HL1 of the file
//! that contains the animation data.
int32_t animindex;
//! The motion type of each blend controller.
int32_t blendtype[2];
//! The starting value of each blend controller.
float blendstart[2];
//! The ending value of each blend controller.
float blendend[2];
//! Was "blendparent".
int32_t unused5;
//! The sequence group.
int32_t seqgroup;
//! The node at entry in the sequence transition graph.
int32_t entrynode;
//! The node at exit in the sequence transition graph.
int32_t exitnode;
//! Transition rules.
int32_t nodeflags;
//! Was "nextseq"
int32_t unused6;
} PACK_STRUCT;
/** \struct AnimEvent_HL1
* \brief Data structure for an animation event in HL1 MDL files.
*/
struct AnimEvent_HL1 {
//! The frame at which this animation event occurs.
int32_t frame;
//! The script event type.
int32_t event;
//! was "type"
int32_t unused;
//! Options. Could be path to sound WAVE files.
char options[64];
} PACK_STRUCT;
/** \struct Attachment_HL1
* \brief Data structure for an attachment in HL1 MDL files.
*/
struct Attachment_HL1 {
//! Was "name".
char unused[32];
//! Was "type".
int32_t unused2;
//! The bone this attachment follows.
int32_t bone;
//! The attachment origin.
vec3_t org;
//! Was "vectors"
vec3_t unused3[3];
} PACK_STRUCT;
/** \struct AnimValueOffset_HL1
* \brief Data structure to hold offsets (one per motion type)
* to the first animation frame value for a single bone
* in HL1 MDL files.
*/
struct AnimValueOffset_HL1 {
unsigned short offset[6];
} PACK_STRUCT;
/** \struct AnimValue_HL1
* \brief Data structure for an animation frame in HL1 MDL files.
*/
union AnimValue_HL1 {
struct {
uint8_t valid;
uint8_t total;
} num;
short value;
} PACK_STRUCT;
/** \struct Bodypart_HL1
* \brief Data structure for a bodypart in HL1 MDL files.
*/
struct Bodypart_HL1 {
//! The bodypart name.
char name[64];
//! The number of available models for this bodypart.
int32_t nummodels;
//! Used to convert from a global model index
//! to a local bodypart model index.
int32_t base;
//! The offset to the first model chunk.
int32_t modelindex;
} PACK_STRUCT;
/** \struct Texture_HL1
* \brief Data structure for a texture in HL1 MDL files.
*/
struct Texture_HL1 {
//! Texture file name.
char name[64];
//! Texture flags.
int32_t flags;
//! Texture width in pixels.
int32_t width;
//! Texture height in pixels.
int32_t height;
//! Offset to the image data.
//! This offset is relative to the texture file header.
int32_t index;
} PACK_STRUCT;
/** \struct Model_HL1
* \brief Data structure for a model in HL1 MDL files.
*/
struct Model_HL1 {
//! Model name.
char name[64];
//! Was "type".
int32_t unused;
//! Was "boundingradius".
float unused2;
//! The number of meshes in the model.
int32_t nummesh;
//! Offset to the first mesh chunk.
int32_t meshindex;
//! The number of unique vertices.
int32_t numverts;
//! Offset to the vertex bone array.
int32_t vertinfoindex;
//! Offset to the vertex array.
int32_t vertindex;
//! The number of unique normals.
int32_t numnorms;
//! Offset to the normal bone array.
int32_t norminfoindex;
//! Offset to the normal array.
int32_t normindex;
//! Was "numgroups".
int32_t unused3;
//! Was "groupindex".
int32_t unused4;
} PACK_STRUCT;
/** \struct Mesh_HL1
* \brief Data structure for a mesh in HL1 MDL files.
*/
struct Mesh_HL1 {
//! Can be interpreted as the number of triangles in the mesh.
int32_t numtris;
//! Offset to the start of the tris sequence.
int32_t triindex;
//! The skin index.
int32_t skinref;
//! The number of normals in the mesh.
int32_t numnorms;
//! Was "normindex".
int32_t unused;
} PACK_STRUCT;
/** \struct Trivert
* \brief Data structure for a trivert in HL1 MDL files.
*/
struct Trivert {
//! Index into Model_HL1 vertex array.
short vertindex;
//! Index into Model_HL1 normal array.
short normindex;
//! Texture coordinates in absolute space (unnormalized).
short s, t;
} PACK_STRUCT;
#include <assimp/Compiler/poppack1.h>
#if (!defined AI_MDL_HL1_VERSION)
#define AI_MDL_HL1_VERSION 10
#endif
#if (!defined AI_MDL_HL1_MAX_TRIANGLES)
#define AI_MDL_HL1_MAX_TRIANGLES 20000
#endif
#if (!defined AI_MDL_HL1_MAX_VERTICES)
#define AI_MDL_HL1_MAX_VERTICES 2048
#endif
#if (!defined AI_MDL_HL1_MAX_SEQUENCES)
#define AI_MDL_HL1_MAX_SEQUENCES 2048
#endif
#if (!defined AI_MDL_HL1_MAX_SEQUENCE_GROUPS)
#define AI_MDL_HL1_MAX_SEQUENCE_GROUPS 32
#endif
#if (!defined AI_MDL_HL1_MAX_TEXTURES)
#define AI_MDL_HL1_MAX_TEXTURES 100
#endif
#if (!defined AI_MDL_HL1_MAX_SKIN_FAMILIES)
#define AI_MDL_HL1_MAX_SKIN_FAMILIES 100
#endif
#if (!defined AI_MDL_HL1_MAX_BONES)
#define AI_MDL_HL1_MAX_BONES 128
#endif
#if (!defined AI_MDL_HL1_MAX_BODYPARTS)
#define AI_MDL_HL1_MAX_BODYPARTS 32
#endif
#if (!defined AI_MDL_HL1_MAX_MODELS)
#define AI_MDL_HL1_MAX_MODELS 32
#endif
#if (!defined AI_MDL_HL1_MAX_MESHES)
#define AI_MDL_HL1_MAX_MESHES 256
#endif
#if (!defined AI_MDL_HL1_MAX_EVENTS)
#define AI_MDL_HL1_MAX_EVENTS 1024
#endif
#if (!defined AI_MDL_HL1_MAX_BONE_CONTROLLERS)
#define AI_MDL_HL1_MAX_BONE_CONTROLLERS 8
#endif
#if (!defined AI_MDL_HL1_MAX_ATTACHMENTS)
#define AI_MDL_HL1_MAX_ATTACHMENTS 512
#endif
// lighting options
#if (!defined AI_MDL_HL1_STUDIO_NF_FLATSHADE)
#define AI_MDL_HL1_STUDIO_NF_FLATSHADE 0x0001
#endif
#if (!defined AI_MDL_HL1_STUDIO_NF_CHROME)
#define AI_MDL_HL1_STUDIO_NF_CHROME 0x0002
#endif
#if (!defined AI_MDL_HL1_STUDIO_NF_ADDITIVE)
#define AI_MDL_HL1_STUDIO_NF_ADDITIVE 0x0020
#endif
#if (!defined AI_MDL_HL1_STUDIO_NF_MASKED)
#define AI_MDL_HL1_STUDIO_NF_MASKED 0x0040
#endif
} // namespace HalfLife
} // namespace MDL
} // namespace Assimp
#endif // AI_HL1FILEDATA_INCLUDED

View File

@ -0,0 +1,64 @@
/*
---------------------------------------------------------------------------
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.
---------------------------------------------------------------------------
*/
/** @file HL1ImportDefinitions.h
* @brief HL1 MDL loader specific definitions.
*/
#ifndef AI_MDL_HL1_IMPORT_DEFINITIONS_INCLUDED
#define AI_MDL_HL1_IMPORT_DEFINITIONS_INCLUDED
#define AI_MDL_HL1_NODE_ROOT "<MDL_root>"
#define AI_MDL_HL1_NODE_BODYPARTS "<MDL_bodyparts>"
#define AI_MDL_HL1_NODE_BONES "<MDL_bones>"
#define AI_MDL_HL1_NODE_BONE_CONTROLLERS "<MDL_bone_controllers>"
#define AI_MDL_HL1_NODE_SEQUENCE_INFOS "<MDL_sequence_infos>"
#define AI_MDL_HL1_NODE_SEQUENCE_GROUPS "<MDL_sequence_groups>"
#define AI_MDL_HL1_NODE_SEQUENCE_TRANSITION_GRAPH "<MDL_sequence_transition_graph>"
#define AI_MDL_HL1_NODE_ATTACHMENTS "<MDL_attachments>"
#define AI_MDL_HL1_NODE_HITBOXES "<MDL_hitboxes>"
#define AI_MDL_HL1_NODE_GLOBAL_INFO "<MDL_global_info>"
#define AI_MDL_HL1_NODE_ANIMATION_EVENTS "AnimationEvents"
#define AI_MDL_HL1_NODE_BLEND_CONTROLLERS "BlendControllers"
#define AI_MDL_HL1_MATKEY_CHROME(type, N) "$mat.HL1.chrome", type, N
#endif // AI_MDL_HL1_IMPORT_DEFINITIONS_INCLUDED

View File

@ -0,0 +1,85 @@
/*
---------------------------------------------------------------------------
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.
---------------------------------------------------------------------------
*/
/** @file HL1ImportSettings.h
* @brief Half-Life 1 MDL loader configuration settings.
*/
#ifndef AI_HL1IMPORTSETTINGS_INCLUDED
#define AI_HL1IMPORTSETTINGS_INCLUDED
#include <string>
namespace Assimp {
namespace MDL {
namespace HalfLife {
struct HL1ImportSettings {
HL1ImportSettings() :
read_animations(false),
read_animation_events(false),
read_blend_controllers(false),
read_sequence_groups_info(false),
read_sequence_transitions(false),
read_attachments(false),
read_bone_controllers(false),
read_hitboxes(false),
read_textures(false),
read_misc_global_info(false) {
}
bool read_animations;
bool read_animation_events;
bool read_blend_controllers;
bool read_sequence_groups_info;
bool read_sequence_transitions;
bool read_attachments;
bool read_bone_controllers;
bool read_hitboxes;
bool read_textures;
bool read_misc_global_info;
};
} // namespace HalfLife
} // namespace MDL
} // namespace Assimp
#endif // AI_HL1IMPORTSETTINGS_INCLUDED

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,241 @@
/*
---------------------------------------------------------------------------
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.
---------------------------------------------------------------------------
*/
/** @file HL1MDLLoader.h
* @brief Declaration of the Half-Life 1 MDL loader.
*/
#ifndef AI_HL1MDLLOADER_INCLUDED
#define AI_HL1MDLLOADER_INCLUDED
#include "HL1FileData.h"
#include "HL1ImportSettings.h"
#include "UniqueNameGenerator.h"
#include <memory>
#include <string>
#include <assimp/types.h>
#include <assimp/scene.h>
#include <assimp/texture.h>
#include <assimp/IOSystem.hpp>
#include <assimp/DefaultIOSystem.h>
#include <assimp/Exceptional.h>
namespace Assimp {
namespace MDL {
namespace HalfLife {
class HL1MDLLoader {
public:
HL1MDLLoader() = delete;
HL1MDLLoader(const HL1MDLLoader &) = delete;
/** See variables descriptions at the end for more details. */
HL1MDLLoader(
aiScene *scene,
IOSystem *io,
const unsigned char *buffer,
const std::string &file_path,
const HL1ImportSettings &import_settings);
~HL1MDLLoader();
void load_file();
protected:
/** \brief Validate the header data structure of a Half-Life 1 MDL file.
* \param[in] header Input header to be validated.
* \param[in] is_texture_header Whether or not we are reading an MDL
* texture file.
*/
void validate_header(const Header_HL1 *header, bool is_texture_header);
void load_texture_file();
void load_sequence_groups_files();
void read_textures();
void read_skins();
void read_bones();
void read_meshes();
void read_animations();
void read_sequence_groups_info();
void read_sequence_infos();
void read_sequence_transitions();
void read_attachments();
void read_hitboxes();
void read_bone_controllers();
void read_global_info();
private:
void release_resources();
/** \brief Load a file and copy it's content to a buffer.
* \param file_path The path to the file to be loaded.
* \param buffer A pointer to a buffer to receive the data.
*/
template <typename MDLFileHeader>
void load_file_into_buffer(const std::string &file_path, unsigned char *&buffer);
/** \brief Read an MDL texture.
* \param[in] ptexture A pointer to an MDL texture.
* \param[in] data A pointer to the data from \p ptexture.
* \param[in] pal A pointer to the texture palette from \p ptexture.
* \param[in,out] pResult A pointer to the output resulting Assimp texture.
* \param[in,out] last_palette_color The last color from the image palette.
*/
void read_texture(const Texture_HL1 *ptexture,
uint8_t *data, uint8_t *pal, aiTexture *pResult,
aiColor3D &last_palette_color);
/** \brief This method reads a compressed anim value.
* \param[in] panimvalue A pointer to the animation data.
* \param[in] frame The frame to look for.
* \param[in] bone_scale The current bone scale to apply to the compressed value.
* \param[in,out] value The decompressed anim value at \p frame.
*/
void extract_anim_value(const AnimValue_HL1 *panimvalue,
int frame, float bone_scale, float &value);
/**
* \brief Given the number of blend animations, determine the number of blend controllers.
*
* \param[in] num_blend_animations The number of blend animations.
* \param[out] num_blend_controllers The number of blend controllers.
* \return True if the number of blend controllers was determined. False otherwise.
*/
static bool get_num_blend_controllers(const int num_blend_animations, int &num_blend_controllers);
/** Output scene to be filled */
aiScene *scene_;
/** Output I/O handler. Required for additional IO operations. */
IOSystem *io_;
/** Buffer from MDLLoader class. */
const unsigned char *buffer_;
/** The full file path to the MDL file we are trying to load.
* Used to locate other MDL files since MDL may store resources
* in external MDL files. */
const std::string &file_path_;
/** Configuration for HL1 MDL */
const HL1ImportSettings &import_settings_;
/** Main MDL header. */
const Header_HL1 *header_;
/** External MDL texture header. */
const Header_HL1 *texture_header_;
/** External MDL animation headers.
* One for each loaded animation file. */
SequenceHeader_HL1 **anim_headers_;
/** Texture file data. */
unsigned char *texture_buffer_;
/** Animation files data. */
unsigned char **anim_buffers_;
/** The number of sequence groups. */
int num_sequence_groups_;
/** The list of children to be appended to the scene's root node. */
std::vector<aiNode *> rootnode_children_;
/** A unique name generator. Used to generate names for MDL values
* that may have empty/duplicate names. */
UniqueNameGenerator unique_name_generator_;
/** The list of unique sequence names. */
std::vector<std::string> unique_sequence_names_;
/** The list of unique sequence groups names. */
std::vector<std::string> unique_sequence_groups_names_;
/** Structure to store temporary bone information. */
struct TempBone {
TempBone() :
node(nullptr),
absolute_transform(),
offset_matrix() {}
aiNode *node;
aiMatrix4x4 absolute_transform;
aiMatrix4x4 offset_matrix;
};
std::vector<TempBone> temp_bones_;
/** The number of available bone controllers in the model. */
int num_blend_controllers_;
/** Self explanatory. */
int total_models_;
};
// ------------------------------------------------------------------------------------------------
template <typename MDLFileHeader>
void HL1MDLLoader::load_file_into_buffer(const std::string &file_path, unsigned char *&buffer) {
if (!io_->Exists(file_path))
throw DeadlyImportError("Missing file " + DefaultIOSystem::fileName(file_path) + ".");
std::unique_ptr<IOStream> file(io_->Open(file_path));
if (file.get() == NULL)
throw DeadlyImportError("Failed to open MDL file " + DefaultIOSystem::fileName(file_path) + ".");
const size_t file_size = file->FileSize();
if (file_size < sizeof(MDLFileHeader))
throw DeadlyImportError("MDL file is too small.");
buffer = new unsigned char[1 + file_size];
file->Read((void *)buffer, 1, file_size);
buffer[file_size] = '\0';
}
} // namespace HalfLife
} // namespace MDL
} // namespace Assimp
#endif // AI_HL1MDLLOADER_INCLUDED

View File

@ -0,0 +1,127 @@
/*
---------------------------------------------------------------------------
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.
---------------------------------------------------------------------------
*/
/** @file HL1MeshTrivert.h
* @brief This file contains the class declaration for the
* HL1 mesh trivert class.
*/
#ifndef AI_HL1MESHTRIVERT_INCLUDED
#define AI_HL1MESHTRIVERT_INCLUDED
#include "HL1FileData.h"
namespace Assimp {
namespace MDL {
namespace HalfLife {
/* A class to help map model triverts to mesh triverts. */
struct HL1MeshTrivert {
HL1MeshTrivert() :
vertindex(-1),
normindex(-1),
s(0),
t(0),
localindex(-1) {
}
HL1MeshTrivert(short vertindex, short normindex, short s, short t, short localindex) :
vertindex(vertindex),
normindex(normindex),
s(s),
t(t),
localindex() {
}
HL1MeshTrivert(const Trivert &a) :
vertindex(a.vertindex),
normindex(a.normindex),
s(a.s),
t(a.t),
localindex(-1) {
}
inline bool operator==(const Trivert &a) const {
return vertindex == a.vertindex &&
normindex == a.normindex &&
s == a.s &&
t == a.t;
}
inline bool operator!=(const Trivert &a) const {
return !(*this == a);
}
inline bool operator==(const HL1MeshTrivert &a) const {
return localindex == a.localindex &&
vertindex == a.vertindex &&
normindex == a.normindex &&
s == a.s &&
t == a.t;
}
inline bool operator!=(const HL1MeshTrivert &a) const {
return !(*this == a);
}
inline HL1MeshTrivert &operator=(const Trivert &other) {
vertindex = other.vertindex;
normindex = other.normindex;
s = other.s;
t = other.t;
return *this;
}
short vertindex;
short normindex;
short s, t;
short localindex;
};
struct HL1MeshFace {
short v0, v1, v2;
};
} // namespace HalfLife
} // namespace MDL
} // namespace Assimp
#endif // AI_HL1MESHTRIVERT_INCLUDED

View File

@ -0,0 +1,67 @@
/*
---------------------------------------------------------------------------
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.
---------------------------------------------------------------------------
*/
/** @file HalfLifeMDLBaseHeader.h */
#ifndef AI_HALFLIFEMDLBASEHEADER_INCLUDED
#define AI_HALFLIFEMDLBASEHEADER_INCLUDED
#include <assimp/types.h>
namespace Assimp {
namespace MDL {
namespace HalfLife {
/** Used to interface different Valve MDL formats. */
struct HalfLifeMDLBaseHeader
{
//! Magic number: "IDST"/"IDSQ"
char ident[4];
//! The file format version.
int32_t version;
};
}
}
}
#endif // AI_HALFLIFEMDLBASEHEADER_INCLUDED

View File

@ -0,0 +1,95 @@
/*
---------------------------------------------------------------------------
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.
---------------------------------------------------------------------------
*/
/** @file LogFunctions.h */
#ifndef AI_MDL_HALFLIFE_LOGFUNCTIONS_INCLUDED
#define AI_MDL_HALFLIFE_LOGFUNCTIONS_INCLUDED
#include <assimp/Logger.hpp>
#include <string>
namespace Assimp {
namespace MDL {
namespace HalfLife {
/**
* \brief A function to log precise messages regarding limits exceeded.
*
* \param[in] subject Subject.
* \param[in] current_amount Current amount.
* \param[in] direct_object Direct object.
* LIMIT Limit constant.
*
* Example: Model has 100 textures, which exceeds the limit (50)
*
* where \p subject is 'Model'
* \p current_amount is '100'
* \p direct_object is 'textures'
* LIMIT is '50'
*/
template <int LIMIT>
static inline void log_warning_limit_exceeded(
const std::string &subject, int current_amount,
const std::string &direct_object) {
ASSIMP_LOG_WARN(MDL_HALFLIFE_LOG_HEADER
+ subject
+ " has "
+ std::to_string(current_amount) + " "
+ direct_object
+ ", which exceeds the limit ("
+ std::to_string(LIMIT)
+ ")");
}
/** \brief Same as above, but uses 'Model' as the subject. */
template <int LIMIT>
static inline void log_warning_limit_exceeded(int current_amount,
const std::string &direct_object) {
log_warning_limit_exceeded<LIMIT>("Model", current_amount, direct_object);
}
} // namespace HalfLife
} // namespace MDL
} // namespace Assimp
#endif // AI_MDL_HALFLIFE_LOGFUNCTIONS_INCLUDED

View File

@ -0,0 +1,180 @@
/*
---------------------------------------------------------------------------
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.
---------------------------------------------------------------------------
*/
/** @file UniqueNameGenerator.cpp
* @brief Implementation for the unique name generator.
*/
#include "UniqueNameGenerator.h"
#include <algorithm>
#include <list>
#include <map>
#include <numeric>
namespace Assimp {
namespace MDL {
namespace HalfLife {
UniqueNameGenerator::UniqueNameGenerator() :
template_name_("unnamed"),
separator_("_") {
}
UniqueNameGenerator::UniqueNameGenerator(const char *template_name) :
template_name_(template_name),
separator_("_") {
}
UniqueNameGenerator::UniqueNameGenerator(const char *template_name, const char *separator) :
template_name_(template_name),
separator_(separator) {
}
UniqueNameGenerator::~UniqueNameGenerator() {
}
void UniqueNameGenerator::make_unique(std::vector<std::string> &names) {
struct DuplicateInfo {
DuplicateInfo() :
indices(),
next_id(0) {
}
std::list<size_t> indices;
size_t next_id;
};
std::vector<size_t> empty_names_indices;
std::vector<size_t> template_name_duplicates;
std::map<std::string, DuplicateInfo> names_to_duplicates;
const std::string template_name_with_separator(template_name_ + separator_);
auto format_name = [&](const std::string &base_name, size_t id) -> std::string {
return base_name + separator_ + std::to_string(id);
};
auto generate_unique_name = [&](const std::string &base_name) -> std::string {
auto *duplicate_info = &names_to_duplicates[base_name];
std::string new_name = "";
bool found_identical_name;
bool tried_with_base_name_only = false;
do {
// Assume that no identical name exists.
found_identical_name = false;
if (!tried_with_base_name_only) {
// First try with only the base name.
new_name = base_name;
} else {
// Create the name expected to be unique.
new_name = format_name(base_name, duplicate_info->next_id);
}
// Check in the list of duplicates for an identical name.
for (size_t i = 0;
i < names.size() &&
!found_identical_name;
++i) {
if (new_name == names[i])
found_identical_name = true;
}
if (tried_with_base_name_only)
++duplicate_info->next_id;
tried_with_base_name_only = true;
} while (found_identical_name);
return new_name;
};
for (size_t i = 0; i < names.size(); ++i) {
// Check for empty names.
if (names[i].find_first_not_of(' ') == std::string::npos) {
empty_names_indices.push_back(i);
continue;
}
/* Check for potential duplicate.
a) Either if this name is the same as the template name or
b) <template name><separator> is found at the beginning. */
if (names[i] == template_name_ ||
names[i].substr(0, template_name_with_separator.length()) == template_name_with_separator)
template_name_duplicates.push_back(i);
// Map each unique name to it's duplicate.
if (names_to_duplicates.count(names[i]) == 0)
names_to_duplicates.insert({ names[i], DuplicateInfo()});
else
names_to_duplicates[names[i]].indices.push_back(i);
}
// Make every non-empty name unique.
for (auto it = names_to_duplicates.begin();
it != names_to_duplicates.end(); ++it) {
for (auto it2 = it->second.indices.begin();
it2 != it->second.indices.end();
++it2)
names[*it2] = generate_unique_name(it->first);
}
// Generate a unique name for every empty string.
if (template_name_duplicates.size()) {
// At least one string ressembles to <template name>.
for (auto it = empty_names_indices.begin();
it != empty_names_indices.end(); ++it)
names[*it] = generate_unique_name(template_name_);
} else {
// No string alike <template name> exists.
size_t i = 0;
for (auto it = empty_names_indices.begin();
it != empty_names_indices.end(); ++it, ++i)
names[*it] = format_name(template_name_, i);
}
}
} // namespace HalfLife
} // namespace MDL
} // namespace Assimp

View File

@ -0,0 +1,81 @@
/*
---------------------------------------------------------------------------
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.
---------------------------------------------------------------------------
*/
/** @file UniqueNameGenerator.h
* @brief Declaration of the unique name generator.
*/
#ifndef AI_UNIQUENAMEGENERATOR_INCLUDED
#define AI_UNIQUENAMEGENERATOR_INCLUDED
#include <string>
#include <vector>
namespace Assimp {
namespace MDL {
namespace HalfLife {
class UniqueNameGenerator {
public:
UniqueNameGenerator();
UniqueNameGenerator(const char *template_name);
UniqueNameGenerator(const char *template_name, const char *separator);
~UniqueNameGenerator();
inline void set_template_name(const char *template_name) {
template_name_ = template_name;
}
inline void set_separator(const char *separator) {
separator_ = separator;
}
void make_unique(std::vector<std::string> &names);
private:
std::string template_name_;
std::string separator_;
};
} // namespace HalfLife
} // namespace MDL
} // namespace Assimp
#endif // AI_UNIQUENAMEGENERATOR_INCLUDED

View File

@ -51,6 +51,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "MDL/MDLLoader.h" #include "MDL/MDLLoader.h"
#include "MDL/MDLDefaultColorMap.h" #include "MDL/MDLDefaultColorMap.h"
#include "MD2/MD2FileData.h" #include "MD2/MD2FileData.h"
#include "MDL/HalfLife/HL1MDLLoader.h"
#include <assimp/qnan.h> #include <assimp/qnan.h>
#include <assimp/StringUtils.h> #include <assimp/StringUtils.h>
@ -142,6 +143,18 @@ void MDLImporter::SetupProperties(const Importer* pImp)
// AI_CONFIG_IMPORT_MDL_COLORMAP - palette file // AI_CONFIG_IMPORT_MDL_COLORMAP - palette file
configPalette = pImp->GetPropertyString(AI_CONFIG_IMPORT_MDL_COLORMAP,"colormap.lmp"); configPalette = pImp->GetPropertyString(AI_CONFIG_IMPORT_MDL_COLORMAP,"colormap.lmp");
// Read configuration specific to MDL (Half-Life 1).
mHL1ImportSettings.read_animations = pImp->GetPropertyBool(AI_CONFIG_IMPORT_MDL_HL1_READ_ANIMATIONS, true);
if (mHL1ImportSettings.read_animations) {
mHL1ImportSettings.read_animation_events = pImp->GetPropertyBool(AI_CONFIG_IMPORT_MDL_HL1_READ_ANIMATION_EVENTS, true);
mHL1ImportSettings.read_blend_controllers = pImp->GetPropertyBool(AI_CONFIG_IMPORT_MDL_HL1_READ_BLEND_CONTROLLERS, true);
mHL1ImportSettings.read_sequence_transitions = pImp->GetPropertyBool(AI_CONFIG_IMPORT_MDL_HL1_READ_SEQUENCE_TRANSITIONS, true);
}
mHL1ImportSettings.read_attachments = pImp->GetPropertyBool(AI_CONFIG_IMPORT_MDL_HL1_READ_ATTACHMENTS, true);
mHL1ImportSettings.read_bone_controllers = pImp->GetPropertyBool(AI_CONFIG_IMPORT_MDL_HL1_READ_BONE_CONTROLLERS, true);
mHL1ImportSettings.read_hitboxes = pImp->GetPropertyBool(AI_CONFIG_IMPORT_MDL_HL1_READ_HITBOXES, true);
mHL1ImportSettings.read_misc_global_info = pImp->GetPropertyBool(AI_CONFIG_IMPORT_MDL_HL1_READ_MISC_GLOBAL_INFO, true);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -224,9 +237,19 @@ void MDLImporter::InternReadFile( const std::string& pFile,
else if (AI_MDL_MAGIC_NUMBER_BE_HL2a == iMagicWord || AI_MDL_MAGIC_NUMBER_LE_HL2a == iMagicWord || else if (AI_MDL_MAGIC_NUMBER_BE_HL2a == iMagicWord || AI_MDL_MAGIC_NUMBER_LE_HL2a == iMagicWord ||
AI_MDL_MAGIC_NUMBER_BE_HL2b == iMagicWord || AI_MDL_MAGIC_NUMBER_LE_HL2b == iMagicWord) AI_MDL_MAGIC_NUMBER_BE_HL2b == iMagicWord || AI_MDL_MAGIC_NUMBER_LE_HL2b == iMagicWord)
{ {
ASSIMP_LOG_DEBUG("MDL subtype: Source(tm) Engine, magic word is IDST/IDSQ");
iGSFileVersion = 0; iGSFileVersion = 0;
InternReadFile_HL2();
HalfLife::HalfLifeMDLBaseHeader *pHeader = (HalfLife::HalfLifeMDLBaseHeader *)mBuffer;
if (pHeader->version == AI_MDL_HL1_VERSION)
{
ASSIMP_LOG_DEBUG("MDL subtype: Half-Life 1/Goldsrc Engine, magic word is IDST/IDSQ");
InternReadFile_HL1(pFile, iMagicWord);
}
else
{
ASSIMP_LOG_DEBUG("MDL subtype: Source(tm) Engine, magic word is IDST/IDSQ");
InternReadFile_HL2();
}
} }
else { else {
// print the magic word to the log file // print the magic word to the log file
@ -1955,6 +1978,23 @@ void MDLImporter::JoinSkins_3DGS_MDL7(
} }
} }
// ------------------------------------------------------------------------------------------------
// Read a Half-life 1 MDL
void MDLImporter::InternReadFile_HL1(const std::string& pFile, const uint32_t iMagicWord)
{
// We can't correctly load an MDL from a MDL "sequence" file.
if (iMagicWord == AI_MDL_MAGIC_NUMBER_BE_HL2b || iMagicWord == AI_MDL_MAGIC_NUMBER_LE_HL2b)
throw DeadlyImportError("Impossible to properly load a model from an MDL sequence file.");
// Read the MDL file.
HalfLife::HL1MDLLoader loader(
pScene,
pIOHandler,
mBuffer,
pFile,
mHL1ImportSettings);
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Read a half-life 2 MDL // Read a half-life 2 MDL
void MDLImporter::InternReadFile_HL2( ) void MDLImporter::InternReadFile_HL2( )

View File

@ -51,6 +51,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/BaseImporter.h> #include <assimp/BaseImporter.h>
#include "MDLFileData.h" #include "MDLFileData.h"
#include "HMP/HalfLifeFileData.h" #include "HMP/HalfLifeFileData.h"
#include "HalfLife/HL1ImportSettings.h"
struct aiNode; struct aiNode;
struct aiTexture; struct aiTexture;
@ -77,6 +78,7 @@ using namespace MDL;
* <li>3D Game Studio MDL3, MDL4</li> * <li>3D Game Studio MDL3, MDL4</li>
* <li>3D Game Studio MDL5</li> * <li>3D Game Studio MDL5</li>
* <li>3D Game Studio MDL7</li> * <li>3D Game Studio MDL7</li>
* <li>Halflife 1</li>
* <li>Halflife 2</li> * <li>Halflife 2</li>
* </ul> * </ul>
* These formats are partially identical and it would be possible to load * These formats are partially identical and it would be possible to load
@ -131,6 +133,11 @@ protected:
*/ */
void InternReadFile_3DGS_MDL7( ); void InternReadFile_3DGS_MDL7( );
// -------------------------------------------------------------------
/** Import a Half-Life 1 MDL file
*/
void InternReadFile_HL1(const std::string& pFile, const uint32_t iMagicWord);
// ------------------------------------------------------------------- // -------------------------------------------------------------------
/** Import a CS:S/HL2 MDL file (not fully implemented) /** Import a CS:S/HL2 MDL file (not fully implemented)
*/ */
@ -436,6 +443,9 @@ protected:
/** Size of the input file in bytes */ /** Size of the input file in bytes */
unsigned int iFileSize; unsigned int iFileSize;
/* Configuration for HL1 MDL */
HalfLife::HL1ImportSettings mHL1ImportSettings;
}; };
} // end of namespace Assimp } // end of namespace Assimp

View File

@ -273,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);
} }
@ -471,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

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

@ -137,8 +137,9 @@ public:
// ------------------------------------------------------------------- // -------------------------------------------------------------------
void Execute( aiScene* pScene); void Execute( aiScene* pScene);
protected: public:
void ProcessMesh( aiMesh* pMesh); /** Some other types of post-processing require winding order flips */
static void ProcessMesh( aiMesh* pMesh);
}; };
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------

View File

@ -43,13 +43,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* @brief Implementation of the aiProcess_OptimizGraph step * @brief Implementation of the aiProcess_OptimizGraph step
*/ */
#ifndef ASSIMP_BUILD_NO_OPTIMIZEGRAPH_PROCESS #ifndef ASSIMP_BUILD_NO_OPTIMIZEGRAPH_PROCESS
#include "OptimizeGraph.h" #include "OptimizeGraph.h"
#include "ProcessHelper.h" #include "ProcessHelper.h"
#include <assimp/SceneCombiner.h> #include "ConvertToLHProcess.h"
#include <assimp/Exceptional.h> #include <assimp/Exceptional.h>
#include <assimp/SceneCombiner.h>
#include <stdio.h> #include <stdio.h>
using namespace Assimp; using namespace Assimp;
@ -60,292 +60,299 @@ using namespace Assimp;
* The unhashed variant should be faster, except for *very* large data sets * The unhashed variant should be faster, except for *very* large data sets
*/ */
#ifdef AI_OG_USE_HASHING #ifdef AI_OG_USE_HASHING
// Use our standard hashing function to compute the hash // Use our standard hashing function to compute the hash
# define AI_OG_GETKEY(str) SuperFastHash(str.data,str.length) #define AI_OG_GETKEY(str) SuperFastHash(str.data, str.length)
#else #else
// Otherwise hope that std::string will utilize a static buffer // Otherwise hope that std::string will utilize a static buffer
// for shorter node names. This would avoid endless heap copying. // for shorter node names. This would avoid endless heap copying.
# define AI_OG_GETKEY(str) std::string(str.data) #define AI_OG_GETKEY(str) std::string(str.data)
#endif #endif
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer // Constructor to be privately used by Importer
OptimizeGraphProcess::OptimizeGraphProcess() OptimizeGraphProcess::OptimizeGraphProcess() :
: mScene() mScene(),
, nodes_in() nodes_in(),
, nodes_out() nodes_out(),
, count_merged() { count_merged() {
// empty // empty
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Destructor, private as well // Destructor, private as well
OptimizeGraphProcess::~OptimizeGraphProcess() { OptimizeGraphProcess::~OptimizeGraphProcess() {
// empty // empty
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field. // Returns whether the processing step is present in the given flag field.
bool OptimizeGraphProcess::IsActive( unsigned int pFlags) const { bool OptimizeGraphProcess::IsActive(unsigned int pFlags) const {
return (0 != (pFlags & aiProcess_OptimizeGraph)); return (0 != (pFlags & aiProcess_OptimizeGraph));
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Setup properties for the post-processing step // Setup properties for the post-processing step
void OptimizeGraphProcess::SetupProperties(const Importer* pImp) { void OptimizeGraphProcess::SetupProperties(const Importer *pImp) {
// Get value of AI_CONFIG_PP_OG_EXCLUDE_LIST // Get value of AI_CONFIG_PP_OG_EXCLUDE_LIST
std::string tmp = pImp->GetPropertyString(AI_CONFIG_PP_OG_EXCLUDE_LIST,""); std::string tmp = pImp->GetPropertyString(AI_CONFIG_PP_OG_EXCLUDE_LIST, "");
AddLockedNodeList(tmp); AddLockedNodeList(tmp);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Collect new children // Collect new children
void OptimizeGraphProcess::CollectNewChildren(aiNode* nd, std::list<aiNode*>& nodes) { void OptimizeGraphProcess::CollectNewChildren(aiNode *nd, std::list<aiNode *> &nodes) {
nodes_in += nd->mNumChildren; nodes_in += nd->mNumChildren;
// Process children // Process children
std::list<aiNode*> child_nodes; std::list<aiNode *> child_nodes;
for (unsigned int i = 0; i < nd->mNumChildren; ++i) { for (unsigned int i = 0; i < nd->mNumChildren; ++i) {
CollectNewChildren(nd->mChildren[i],child_nodes); CollectNewChildren(nd->mChildren[i], child_nodes);
nd->mChildren[i] = nullptr; nd->mChildren[i] = nullptr;
} }
// Check whether we need this node; if not we can replace it by our own children (warn, danger of incest). // Check whether we need this node; if not we can replace it by our own children (warn, danger of incest).
if (locked.find(AI_OG_GETKEY(nd->mName)) == locked.end() ) { if (locked.find(AI_OG_GETKEY(nd->mName)) == locked.end()) {
for (std::list<aiNode*>::iterator it = child_nodes.begin(); it != child_nodes.end();) { for (std::list<aiNode *>::iterator it = child_nodes.begin(); it != child_nodes.end();) {
if (locked.find(AI_OG_GETKEY((*it)->mName)) == locked.end()) { if (locked.find(AI_OG_GETKEY((*it)->mName)) == locked.end()) {
(*it)->mTransformation = nd->mTransformation * (*it)->mTransformation; (*it)->mTransformation = nd->mTransformation * (*it)->mTransformation;
nodes.push_back(*it); nodes.push_back(*it);
it = child_nodes.erase(it); it = child_nodes.erase(it);
continue; continue;
} }
++it; ++it;
} }
if (nd->mNumMeshes || !child_nodes.empty()) { if (nd->mNumMeshes || !child_nodes.empty()) {
nodes.push_back(nd); nodes.push_back(nd);
} else { } else {
delete nd; /* bye, node */ delete nd; /* bye, node */
return; return;
} }
} else { } else {
// Retain our current position in the hierarchy // Retain our current position in the hierarchy
nodes.push_back(nd); nodes.push_back(nd);
// Now check for possible optimizations in our list of child nodes. join as many as possible // Now check for possible optimizations in our list of child nodes. join as many as possible
aiNode* join_master = NULL; aiNode *join_master = nullptr;
aiMatrix4x4 inv; aiMatrix4x4 inv;
const LockedSetType::const_iterator end = locked.end(); const LockedSetType::const_iterator end = locked.end();
std::list<aiNode*> join; std::list<aiNode *> join;
for (std::list<aiNode*>::iterator it = child_nodes.begin(); it != child_nodes.end();) { for (std::list<aiNode *>::iterator it = child_nodes.begin(); it != child_nodes.end();) {
aiNode* child = *it; aiNode *child = *it;
if (child->mNumChildren == 0 && locked.find(AI_OG_GETKEY(child->mName)) == end) { if (child->mNumChildren == 0 && locked.find(AI_OG_GETKEY(child->mName)) == end) {
// There may be no instanced meshes // There may be no instanced meshes
unsigned int n = 0; unsigned int n = 0;
for (; n < child->mNumMeshes;++n) { for (; n < child->mNumMeshes; ++n) {
if (meshes[child->mMeshes[n]] > 1) { if (meshes[child->mMeshes[n]] > 1) {
break; break;
} }
} }
if (n == child->mNumMeshes) { if (n == child->mNumMeshes) {
if (!join_master) { if (!join_master) {
join_master = child; join_master = child;
inv = join_master->mTransformation; inv = join_master->mTransformation;
inv.Inverse(); inv.Inverse();
} else { } else {
child->mTransformation = inv * child->mTransformation ; child->mTransformation = inv * child->mTransformation;
join.push_back(child); join.push_back(child);
it = child_nodes.erase(it); it = child_nodes.erase(it);
continue; continue;
} }
} }
} }
++it; ++it;
} }
if (join_master && !join.empty()) { if (join_master && !join.empty()) {
join_master->mName.length = ::ai_snprintf(join_master->mName.data, MAXLEN, "$MergedNode_%i",count_merged++); join_master->mName.length = ::ai_snprintf(join_master->mName.data, MAXLEN, "$MergedNode_%i", count_merged++);
unsigned int out_meshes = 0; unsigned int out_meshes = 0;
for (std::list<aiNode*>::iterator it = join.begin(); it != join.end(); ++it) { for (std::list<aiNode *>::const_iterator it = join.cbegin(); it != join.cend(); ++it) {
out_meshes += (*it)->mNumMeshes; out_meshes += (*it)->mNumMeshes;
} }
// copy all mesh references in one array // copy all mesh references in one array
if (out_meshes) { if (out_meshes) {
unsigned int* meshes = new unsigned int[out_meshes+join_master->mNumMeshes], *tmp = meshes; unsigned int *meshes = new unsigned int[out_meshes + join_master->mNumMeshes], *tmp = meshes;
for (unsigned int n = 0; n < join_master->mNumMeshes;++n) { for (unsigned int n = 0; n < join_master->mNumMeshes; ++n) {
*tmp++ = join_master->mMeshes[n]; *tmp++ = join_master->mMeshes[n];
} }
for (std::list<aiNode*>::iterator it = join.begin(); it != join.end(); ++it) { for (const aiNode *join_node : join) {
for (unsigned int n = 0; n < (*it)->mNumMeshes; ++n) { for (unsigned int n = 0; n < join_node->mNumMeshes; ++n) {
*tmp = (*it)->mMeshes[n]; *tmp = join_node->mMeshes[n];
aiMesh* mesh = mScene->mMeshes[*tmp++]; aiMesh *mesh = mScene->mMeshes[*tmp++];
// manually move the mesh into the right coordinate system // Assume the transformation is affine
const aiMatrix3x3 IT = aiMatrix3x3( (*it)->mTransformation ).Inverse().Transpose(); // manually move the mesh into the right coordinate system
for (unsigned int a = 0; a < mesh->mNumVertices; ++a) {
mesh->mVertices[a] *= (*it)->mTransformation; // Check for odd negative scale (mirror)
if (join_node->mTransformation.Determinant() < 0) {
// Reverse the mesh face winding order
FlipWindingOrderProcess::ProcessMesh(mesh);
}
if (mesh->HasNormals()) // Update positions, normals and tangents
mesh->mNormals[a] *= IT; const aiMatrix3x3 IT = aiMatrix3x3(join_node->mTransformation).Inverse().Transpose();
for (unsigned int a = 0; a < mesh->mNumVertices; ++a) {
if (mesh->HasTangentsAndBitangents()) { mesh->mVertices[a] *= join_node->mTransformation;
mesh->mTangents[a] *= IT;
mesh->mBitangents[a] *= IT;
}
}
}
delete *it; // bye, node
}
delete[] join_master->mMeshes;
join_master->mMeshes = meshes;
join_master->mNumMeshes += out_meshes;
}
}
}
// reassign children if something changed
if (child_nodes.empty() || child_nodes.size() > nd->mNumChildren) {
delete[] nd->mChildren; if (mesh->HasNormals())
mesh->mNormals[a] *= IT;
if (!child_nodes.empty()) { if (mesh->HasTangentsAndBitangents()) {
nd->mChildren = new aiNode*[child_nodes.size()]; mesh->mTangents[a] *= IT;
} mesh->mBitangents[a] *= IT;
else nd->mChildren = nullptr; }
} }
}
delete join_node; // bye, node
}
delete[] join_master->mMeshes;
join_master->mMeshes = meshes;
join_master->mNumMeshes += out_meshes;
}
}
}
// reassign children if something changed
if (child_nodes.empty() || child_nodes.size() > nd->mNumChildren) {
nd->mNumChildren = static_cast<unsigned int>(child_nodes.size()); delete[] nd->mChildren;
if (nd->mChildren) { if (!child_nodes.empty()) {
aiNode** tmp = nd->mChildren; nd->mChildren = new aiNode *[child_nodes.size()];
for (std::list<aiNode*>::iterator it = child_nodes.begin(); it != child_nodes.end(); ++it) { } else
aiNode* node = *tmp++ = *it; nd->mChildren = nullptr;
node->mParent = nd; }
}
}
nodes_out += static_cast<unsigned int>(child_nodes.size()); nd->mNumChildren = static_cast<unsigned int>(child_nodes.size());
if (nd->mChildren) {
aiNode **tmp = nd->mChildren;
for (std::list<aiNode *>::iterator it = child_nodes.begin(); it != child_nodes.end(); ++it) {
aiNode *node = *tmp++ = *it;
node->mParent = nd;
}
}
nodes_out += static_cast<unsigned int>(child_nodes.size());
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Execute the post-processing step on the given scene // Execute the post-processing step on the given scene
void OptimizeGraphProcess::Execute( aiScene* pScene) { void OptimizeGraphProcess::Execute(aiScene *pScene) {
ASSIMP_LOG_DEBUG("OptimizeGraphProcess begin"); ASSIMP_LOG_DEBUG("OptimizeGraphProcess begin");
nodes_in = nodes_out = count_merged = 0; nodes_in = nodes_out = count_merged = 0;
mScene = pScene; mScene = pScene;
meshes.resize(pScene->mNumMeshes,0); meshes.resize(pScene->mNumMeshes, 0);
FindInstancedMeshes(pScene->mRootNode); FindInstancedMeshes(pScene->mRootNode);
// build a blacklist of identifiers. If the name of a node matches one of these, we won't touch it // build a blacklist of identifiers. If the name of a node matches one of these, we won't touch it
locked.clear(); locked.clear();
for (std::list<std::string>::const_iterator it = locked_nodes.begin(); it != locked_nodes.end(); ++it) { for (std::list<std::string>::const_iterator it = locked_nodes.begin(); it != locked_nodes.end(); ++it) {
#ifdef AI_OG_USE_HASHING #ifdef AI_OG_USE_HASHING
locked.insert(SuperFastHash((*it).c_str())); locked.insert(SuperFastHash((*it).c_str()));
#else #else
locked.insert(*it); locked.insert(*it);
#endif #endif
} }
for (unsigned int i = 0; i < pScene->mNumAnimations; ++i) { for (unsigned int i = 0; i < pScene->mNumAnimations; ++i) {
for (unsigned int a = 0; a < pScene->mAnimations[i]->mNumChannels; ++a) { for (unsigned int a = 0; a < pScene->mAnimations[i]->mNumChannels; ++a) {
aiNodeAnim* anim = pScene->mAnimations[i]->mChannels[a]; aiNodeAnim *anim = pScene->mAnimations[i]->mChannels[a];
locked.insert(AI_OG_GETKEY(anim->mNodeName)); locked.insert(AI_OG_GETKEY(anim->mNodeName));
} }
} }
for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) { for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
for (unsigned int a = 0; a < pScene->mMeshes[i]->mNumBones; ++a) { for (unsigned int a = 0; a < pScene->mMeshes[i]->mNumBones; ++a) {
aiBone* bone = pScene->mMeshes[i]->mBones[a]; aiBone *bone = pScene->mMeshes[i]->mBones[a];
locked.insert(AI_OG_GETKEY(bone->mName)); locked.insert(AI_OG_GETKEY(bone->mName));
// HACK: Meshes referencing bones may not be transformed; we need to look them. // HACK: Meshes referencing bones may not be transformed; we need to look them.
// The easiest way to do this is to increase their reference counters ... // The easiest way to do this is to increase their reference counters ...
meshes[i] += 2; meshes[i] += 2;
} }
} }
for (unsigned int i = 0; i < pScene->mNumCameras; ++i) { for (unsigned int i = 0; i < pScene->mNumCameras; ++i) {
aiCamera* cam = pScene->mCameras[i]; aiCamera *cam = pScene->mCameras[i];
locked.insert(AI_OG_GETKEY(cam->mName)); locked.insert(AI_OG_GETKEY(cam->mName));
} }
for (unsigned int i = 0; i < pScene->mNumLights; ++i) { for (unsigned int i = 0; i < pScene->mNumLights; ++i) {
aiLight* lgh = pScene->mLights[i]; aiLight *lgh = pScene->mLights[i];
locked.insert(AI_OG_GETKEY(lgh->mName)); locked.insert(AI_OG_GETKEY(lgh->mName));
} }
// Insert a dummy master node and make it read-only // Insert a dummy master node and make it read-only
aiNode* dummy_root = new aiNode(AI_RESERVED_NODE_NAME); aiNode *dummy_root = new aiNode(AI_RESERVED_NODE_NAME);
locked.insert(AI_OG_GETKEY(dummy_root->mName)); locked.insert(AI_OG_GETKEY(dummy_root->mName));
const aiString prev = pScene->mRootNode->mName; const aiString prev = pScene->mRootNode->mName;
pScene->mRootNode->mParent = dummy_root; pScene->mRootNode->mParent = dummy_root;
dummy_root->mChildren = new aiNode*[dummy_root->mNumChildren = 1]; dummy_root->mChildren = new aiNode *[dummy_root->mNumChildren = 1];
dummy_root->mChildren[0] = pScene->mRootNode; dummy_root->mChildren[0] = pScene->mRootNode;
// Do our recursive processing of scenegraph nodes. For each node collect // Do our recursive processing of scenegraph nodes. For each node collect
// a fully new list of children and allow their children to place themselves // a fully new list of children and allow their children to place themselves
// on the same hierarchy layer as their parents. // on the same hierarchy layer as their parents.
std::list<aiNode*> nodes; std::list<aiNode *> nodes;
CollectNewChildren (dummy_root,nodes); CollectNewChildren(dummy_root, nodes);
ai_assert(nodes.size() == 1); ai_assert(nodes.size() == 1);
if (dummy_root->mNumChildren == 0) { if (dummy_root->mNumChildren == 0) {
pScene->mRootNode = NULL; pScene->mRootNode = nullptr;
throw DeadlyImportError("After optimizing the scene graph, no data remains"); throw DeadlyImportError("After optimizing the scene graph, no data remains");
} }
if (dummy_root->mNumChildren > 1) { if (dummy_root->mNumChildren > 1) {
pScene->mRootNode = dummy_root; pScene->mRootNode = dummy_root;
// Keep the dummy node but assign the name of the old root node to it // Keep the dummy node but assign the name of the old root node to it
pScene->mRootNode->mName = prev; pScene->mRootNode->mName = prev;
} } else {
else {
// Remove the dummy root node again. // Remove the dummy root node again.
pScene->mRootNode = dummy_root->mChildren[0]; pScene->mRootNode = dummy_root->mChildren[0];
dummy_root->mChildren[0] = NULL; dummy_root->mChildren[0] = nullptr;
delete dummy_root; delete dummy_root;
} }
pScene->mRootNode->mParent = NULL; pScene->mRootNode->mParent = nullptr;
if (!DefaultLogger::isNullLogger()) { if (!DefaultLogger::isNullLogger()) {
if ( nodes_in != nodes_out) { if (nodes_in != nodes_out) {
ASSIMP_LOG_INFO_F("OptimizeGraphProcess finished; Input nodes: ", nodes_in, ", Output nodes: ", nodes_out); ASSIMP_LOG_INFO_F("OptimizeGraphProcess finished; Input nodes: ", nodes_in, ", Output nodes: ", nodes_out);
} else { } else {
ASSIMP_LOG_DEBUG("OptimizeGraphProcess finished"); ASSIMP_LOG_DEBUG("OptimizeGraphProcess finished");
} }
} }
meshes.clear(); meshes.clear();
locked.clear(); locked.clear();
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Build a LUT of all instanced meshes // Build a LUT of all instanced meshes
void OptimizeGraphProcess::FindInstancedMeshes (aiNode* pNode) void OptimizeGraphProcess::FindInstancedMeshes(aiNode *pNode) {
{ for (unsigned int i = 0; i < pNode->mNumMeshes; ++i) {
for (unsigned int i = 0; i < pNode->mNumMeshes;++i) { ++meshes[pNode->mMeshes[i]];
++meshes[pNode->mMeshes[i]]; }
}
for (unsigned int i = 0; i < pNode->mNumChildren; ++i) for (unsigned int i = 0; i < pNode->mNumChildren; ++i)
FindInstancedMeshes(pNode->mChildren[i]); FindInstancedMeshes(pNode->mChildren[i]);
} }
#endif // !! ASSIMP_BUILD_NO_OPTIMIZEGRAPH_PROCESS #endif // !! ASSIMP_BUILD_NO_OPTIMIZEGRAPH_PROCESS

View File

@ -75,13 +75,13 @@ public:
~OptimizeGraphProcess(); ~OptimizeGraphProcess();
// ------------------------------------------------------------------- // -------------------------------------------------------------------
bool IsActive( unsigned int pFlags) const; bool IsActive( unsigned int pFlags) const override;
// ------------------------------------------------------------------- // -------------------------------------------------------------------
void Execute( aiScene* pScene); void Execute( aiScene* pScene) override;
// ------------------------------------------------------------------- // -------------------------------------------------------------------
void SetupProperties(const Importer* pImp); void SetupProperties(const Importer* pImp) override;
// ------------------------------------------------------------------- // -------------------------------------------------------------------
/** @brief Add a list of node names to be locked and not modified. /** @brief Add a list of node names to be locked and not modified.

File diff suppressed because it is too large Load Diff

View File

@ -59,7 +59,7 @@ struct aiNode;
class PretransformVerticesTest; class PretransformVerticesTest;
namespace Assimp { namespace Assimp {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/** The PretransformVertices pre-transforms all vertices in the node tree /** The PretransformVertices pre-transforms all vertices in the node tree
@ -68,97 +68,97 @@ namespace Assimp {
*/ */
class ASSIMP_API PretransformVertices : public BaseProcess { class ASSIMP_API PretransformVertices : public BaseProcess {
public: public:
PretransformVertices (); PretransformVertices();
~PretransformVertices (); ~PretransformVertices();
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Check whether step is active // Check whether step is active
bool IsActive( unsigned int pFlags) const; bool IsActive(unsigned int pFlags) const override;
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Execute step on a given scene // Execute step on a given scene
void Execute( aiScene* pScene); void Execute(aiScene *pScene) override;
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Setup import settings // Setup import settings
void SetupProperties(const Importer* pImp); void SetupProperties(const Importer *pImp) override;
// ------------------------------------------------------------------- // -------------------------------------------------------------------
/** @brief Toggle the 'keep hierarchy' option /** @brief Toggle the 'keep hierarchy' option
* @param keep true for keep configuration. * @param keep true for keep configuration.
*/ */
void KeepHierarchy(bool keep) { void KeepHierarchy(bool keep) {
configKeepHierarchy = keep; configKeepHierarchy = keep;
} }
// ------------------------------------------------------------------- // -------------------------------------------------------------------
/** @brief Check whether 'keep hierarchy' is currently enabled. /** @brief Check whether 'keep hierarchy' is currently enabled.
* @return ... * @return ...
*/ */
bool IsHierarchyKept() const { bool IsHierarchyKept() const {
return configKeepHierarchy; return configKeepHierarchy;
} }
private: private:
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Count the number of nodes // Count the number of nodes
unsigned int CountNodes( aiNode* pcNode ); unsigned int CountNodes(const aiNode *pcNode) const;
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Get a bitwise combination identifying the vertex format of a mesh // Get a bitwise combination identifying the vertex format of a mesh
unsigned int GetMeshVFormat(aiMesh* pcMesh); unsigned int GetMeshVFormat(aiMesh *pcMesh) const;
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Count the number of vertices in the whole scene and a given // Count the number of vertices in the whole scene and a given
// material index // material index
void CountVerticesAndFaces( aiScene* pcScene, aiNode* pcNode, void CountVerticesAndFaces(const aiScene *pcScene, const aiNode *pcNode,
unsigned int iMat, unsigned int iMat,
unsigned int iVFormat, unsigned int iVFormat,
unsigned int* piFaces, unsigned int *piFaces,
unsigned int* piVertices); unsigned int *piVertices) const;
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Collect vertex/face data // Collect vertex/face data
void CollectData( aiScene* pcScene, aiNode* pcNode, void CollectData(const aiScene *pcScene, const aiNode *pcNode,
unsigned int iMat, unsigned int iMat,
unsigned int iVFormat, unsigned int iVFormat,
aiMesh* pcMeshOut, aiMesh *pcMeshOut,
unsigned int aiCurrent[2], unsigned int aiCurrent[2],
unsigned int* num_refs); unsigned int *num_refs) const;
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Get a list of all vertex formats that occur for a given material // Get a list of all vertex formats that occur for a given material
// The output list contains duplicate elements // The output list contains duplicate elements
void GetVFormatList( aiScene* pcScene, unsigned int iMat, void GetVFormatList(const aiScene *pcScene, unsigned int iMat,
std::list<unsigned int>& aiOut); std::list<unsigned int> &aiOut) const;
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Compute the absolute transformation matrices of each node // Compute the absolute transformation matrices of each node
void ComputeAbsoluteTransform( aiNode* pcNode ); void ComputeAbsoluteTransform(aiNode *pcNode);
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Simple routine to build meshes in worldspace, no further optimization // Simple routine to build meshes in worldspace, no further optimization
void BuildWCSMeshes(std::vector<aiMesh*>& out, aiMesh** in, void BuildWCSMeshes(std::vector<aiMesh *> &out, aiMesh **in,
unsigned int numIn, aiNode* node); unsigned int numIn, aiNode *node) const;
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Apply the node transformation to a mesh // Apply the node transformation to a mesh
void ApplyTransform(aiMesh* mesh, const aiMatrix4x4& mat); void ApplyTransform(aiMesh *mesh, const aiMatrix4x4 &mat) const;
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Reset transformation matrices to identity // Reset transformation matrices to identity
void MakeIdentityTransform(aiNode* nd); void MakeIdentityTransform(aiNode *nd) const;
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Build reference counters for all meshes // Build reference counters for all meshes
void BuildMeshRefCountArray(aiNode* nd, unsigned int * refs); void BuildMeshRefCountArray(const aiNode *nd, unsigned int *refs) const;
//! Configuration option: keep scene hierarchy as long as possible //! Configuration option: keep scene hierarchy as long as possible
bool configKeepHierarchy; bool configKeepHierarchy;
bool configNormalize; bool configNormalize;
bool configTransform; bool configTransform;
aiMatrix4x4 configTransformation; aiMatrix4x4 configTransformation;
bool mConfigPointCloud; bool mConfigPointCloud;
}; };
} // end of namespace Assimp } // end of namespace Assimp

View File

@ -603,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)
{ {
@ -634,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)

View File

@ -60,7 +60,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// internal headers // internal headers
#include "SMDLoader.h" #include "SMDLoader.h"
#ifndef _WIN32 #ifndef _MSC_VER
#define strtok_s strtok_r #define strtok_s strtok_r
#endif #endif

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();
@ -749,7 +749,7 @@ namespace glTF
/// \fn void Read(Value& pJSON_Object, Asset& pAsset_Root) /// \fn void Read(Value& pJSON_Object, Asset& pAsset_Root)
/// Get mesh data from JSON-object and place them to root asset. /// Get mesh data from JSON-object and place them to root asset.
/// \param [in] pJSON_Object - reference to pJSON-object from which data are read. /// \param [in] pJSON_Object - reference to pJSON-object from which data are read.
/// \param [out] pAsset_Root - reference to root assed where data will be stored. /// \param [out] pAsset_Root - reference to root asset where data will be stored.
void Read(Value& pJSON_Object, Asset& pAsset_Root); void Read(Value& pJSON_Object, Asset& pAsset_Root);
#ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC #ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC

View File

@ -325,7 +325,7 @@ inline void Buffer::Read(Value& obj, Asset& r)
} }
else { // Local file else { // Local file
if (byteLength > 0) { if (byteLength > 0) {
std::string dir = !r.mCurrentAssetDir.empty() ? (r.mCurrentAssetDir + "/") : ""; std::string dir = !r.mCurrentAssetDir.empty() ? (r.mCurrentAssetDir) : "";
IOStream* file = r.OpenFile(dir + uri, "rb"); IOStream* file = r.OpenFile(dir + uri, "rb");
if (file) { if (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;
obj.AddMember("max", MakeValue(vTmpMax, a.max, w.mAl), w.mAl); if (a.componentType == ComponentType_FLOAT) {
obj.AddMember("min", MakeValue(vTmpMin, a.min, 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);
} 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

@ -188,7 +188,7 @@ namespace glTFCommon {
size_t DecodeBase64(const char* in, size_t inLength, uint8_t*& out); size_t DecodeBase64(const char* in, size_t inLength, uint8_t*& out);
inline inline
size_t DecodeBase64(const char* in, uint8_t*& out) { size_t DecodeBase64(const char* in, uint8_t*& out) {
return DecodeBase64(in, strlen(in), out); return DecodeBase64(in, strlen(in), out);
} }
@ -221,25 +221,22 @@ namespace glTFCommon {
}; };
inline inline
char EncodeCharBase64(uint8_t b) { char EncodeCharBase64(uint8_t b) {
return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="[size_t(b)]; return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="[size_t(b)];
} }
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
@ -776,7 +783,7 @@ namespace glTF2
/// \fn void Read(Value& pJSON_Object, Asset& pAsset_Root) /// \fn void Read(Value& pJSON_Object, Asset& pAsset_Root)
/// Get mesh data from JSON-object and place them to root asset. /// Get mesh data from JSON-object and place them to root asset.
/// \param [in] pJSON_Object - reference to pJSON-object from which data are read. /// \param [in] pJSON_Object - reference to pJSON-object from which data are read.
/// \param [out] pAsset_Root - reference to root assed where data will be stored. /// \param [out] pAsset_Root - reference to root asset where data will be stored.
void Read(Value& pJSON_Object, Asset& pAsset_Root); void Read(Value& pJSON_Object, Asset& pAsset_Root);
}; };
@ -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>
@ -383,7 +384,7 @@ inline void Buffer::Read(Value& obj, Asset& r)
} }
else { // Local file else { // Local file
if (byteLength > 0) { if (byteLength > 0) {
std::string dir = !r.mCurrentAssetDir.empty() ? (r.mCurrentAssetDir + "/") : ""; std::string dir = !r.mCurrentAssetDir.empty() ? (r.mCurrentAssetDir) : "";
IOStream* file = r.OpenFile(dir + uri, "rb"); IOStream* file = r.OpenFile(dir + uri, "rb");
if (file) { if (file) {
@ -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;
obj.AddMember("max", MakeValue(vTmpMax, a.max, w.mAl), w.mAl); if (a.componentType == ComponentType_FLOAT) {
obj.AddMember("min", MakeValue(vTmpMin, a.min, 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);
} 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];
} }

File diff suppressed because it is too large Load Diff

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

@ -71,7 +71,7 @@ int arg = 2;
zip_extract("foo.zip", "/tmp", on_extract_entry, &arg); zip_extract("foo.zip", "/tmp", on_extract_entry, &arg);
``` ```
* Extract a zip entry into memory. * Extract a zip entry into memory.
```c ```c
void *buf = NULL; void *buf = NULL;
size_t bufsize; size_t bufsize;
@ -89,7 +89,7 @@ zip_close(zip);
free(buf); free(buf);
``` ```
* Extract a zip entry into memory (no internal allocation). * Extract a zip entry into memory (no internal allocation).
```c ```c
unsigned char *buf; unsigned char *buf;
size_t bufsize; size_t bufsize;
@ -110,7 +110,7 @@ zip_close(zip);
free(buf); free(buf);
``` ```
* Extract a zip entry into memory using callback. * Extract a zip entry into memory using callback.
```c ```c
struct buffer_t { struct buffer_t {
char *data; char *data;
@ -144,7 +144,7 @@ free(buf.data);
``` ```
* Extract a zip entry into a file. * Extract a zip entry into a file.
```c ```c
struct zip_t *zip = zip_open("foo.zip", 0, 'r'); struct zip_t *zip = zip_open("foo.zip", 0, 'r');
{ {
@ -157,7 +157,7 @@ struct zip_t *zip = zip_open("foo.zip", 0, 'r');
zip_close(zip); zip_close(zip);
``` ```
* List of all zip entries * List of all zip entries
```c ```c
struct zip_t *zip = zip_open("foo.zip", 0, 'r'); struct zip_t *zip = zip_open("foo.zip", 0, 'r');
int i, n = zip_total_entries(zip); int i, n = zip_total_entries(zip);
@ -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) {
break; mz_uint s = MZ_READ_LE32(pBuf + i);
if (s == record_sig) {
if ((pZip->m_archive_size - (cur_file_ofs + i)) >= record_size)
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);
if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS);
(decomp_size != comp_size)) || filename_size = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS);
(decomp_size && !comp_size) || (decomp_size == 0xFFFFFFFF) || ext_data_size = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS);
(comp_size == 0xFFFFFFFF))
return MZ_FALSE; 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)) &&
(decomp_size != comp_size)) ||
(decomp_size && !comp_size))
return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
}
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)))
if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK);
MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size)
return MZ_FALSE; if (comp_size != MZ_UINT32_MAX) {
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)
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,26 +77,34 @@ 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);
@ -660,7 +674,7 @@ ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf, size_t bufsize) {
} }
if (!mz_zip_reader_extract_to_mem_no_alloc(pzip, (mz_uint)zip->entry.index, if (!mz_zip_reader_extract_to_mem_no_alloc(pzip, (mz_uint)zip->entry.index,
buf, bufsize, 0, NULL, 0)) { buf, bufsize, 0, NULL, 0)) {
return -1; return -1;
} }
@ -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,241 +20,240 @@ 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
typedef long long ssize_t; /* byte count or error */ typedef long long ssize_t; /* byte count or error */
#else #else
typedef long ssize_t; /* byte count or error */ typedef long ssize_t; /* byte count or error */
#endif #endif
#define _SSIZE_T_DEFINED
#define _SSIZE_T_DEFINED_
#define __DEFINED_ssize_t
#define __ssize_t_defined
#define _SSIZE_T
#define _SSIZE_T_
#endif #endif
#ifndef MAX_PATH #ifndef MAX_PATH
#define MAX_PATH 32767 /* # chars in a path name including NULL */ #define MAX_PATH 32767 /* # chars in a path name including NULL */
#endif #endif
/**
* @mainpage
*
* Documenation for @ref zip.
*/
/**
* @addtogroup zip
* @{
*/
/**
* Default zip compression level.
*/
#define ZIP_DEFAULT_COMPRESSION_LEVEL 6 #define ZIP_DEFAULT_COMPRESSION_LEVEL 6
/* /**
This data structure is used throughout the library to represent zip archive * @struct zip_t
- forward declaration. *
*/ * This data structure is used throughout the library to represent zip archive -
* forward declaration.
*/
struct zip_t; struct zip_t;
/* /**
Opens zip archive with compression level using the given mode. * Opens zip archive with compression level using the given mode.
*
Args: * @param zipname zip archive file name.
zipname: zip archive file name. * @param level compression level (0-9 are the standard zlib-style levels).
level: compression level (0-9 are the standard zlib-style levels). * @param mode file access mode.
mode: file access mode. * - 'r': opens a file for reading/extracting (the file must exists).
'r': opens a file for reading/extracting (the file must exists). * - 'w': creates an empty file for writing.
'w': creates an empty file for writing. * - 'a': appends to an existing archive.
'a': appends to an existing archive. *
* @return the zip archive handler or NULL on error
Returns: */
The zip archive handler or NULL on error
*/
extern struct zip_t *zip_open(const char *zipname, int level, char mode); extern struct zip_t *zip_open(const char *zipname, int level, char mode);
/* /**
Closes the zip archive, releases resources - always finalize. * Closes the zip archive, releases resources - always finalize.
*
Args: * @param zip zip archive handler.
zip: zip archive handler. */
*/
extern void zip_close(struct zip_t *zip); extern void zip_close(struct zip_t *zip);
/* /**
Opens an entry by name in the zip archive. * Opens an entry by name in the zip archive.
For zip archive opened in 'w' or 'a' mode the function will append *
a new entry. In readonly mode the function tries to locate the entry * For zip archive opened in 'w' or 'a' mode the function will append
in global dictionary. * a new entry. In readonly mode the function tries to locate the entry
* in global dictionary.
Args: *
zip: zip archive handler. * @param zip zip archive handler.
entryname: an entry name in local dictionary. * @param entryname an entry name in local dictionary.
*
Returns: * @return the return code - 0 on success, negative number (< 0) on error.
The return code - 0 on success, negative number (< 0) on error. */
*/
extern int zip_entry_open(struct zip_t *zip, const char *entryname); extern int zip_entry_open(struct zip_t *zip, const char *entryname);
/* /**
Opens a new entry by index in the zip archive. * Opens a new entry by index in the zip archive.
This function is only valid if zip archive was opened in 'r' (readonly) mode. *
* This function is only valid if zip archive was opened in 'r' (readonly) mode.
Args: *
zip: zip archive handler. * @param zip zip archive handler.
index: index in local dictionary. * @param index index in local dictionary.
*
Returns: * @return the return code - 0 on success, negative number (< 0) on error.
The return code - 0 on success, negative number (< 0) on error. */
*/
extern int zip_entry_openbyindex(struct zip_t *zip, int index); extern int zip_entry_openbyindex(struct zip_t *zip, int index);
/* /**
Closes a zip entry, flushes buffer and releases resources. * Closes a zip entry, flushes buffer and releases resources.
*
Args: * @param zip zip archive handler.
zip: zip archive handler. *
* @return the return code - 0 on success, negative number (< 0) on error.
Returns: */
The return code - 0 on success, negative number (< 0) on error.
*/
extern int zip_entry_close(struct zip_t *zip); extern int zip_entry_close(struct zip_t *zip);
/* /**
Returns a local name of the current zip entry. * Returns a local name of the current zip entry.
The main difference between user's entry name and local entry name *
is optional relative path. * The main difference between user's entry name and local entry name
Following .ZIP File Format Specification - the path stored MUST not contain * is optional relative path.
a drive or device letter, or a leading slash. * Following .ZIP File Format Specification - the path stored MUST not contain
All slashes MUST be forward slashes '/' as opposed to backwards slashes '\' * a drive or device letter, or a leading slash.
for compatibility with Amiga and UNIX file systems etc. * All slashes MUST be forward slashes '/' as opposed to backwards slashes '\'
* for compatibility with Amiga and UNIX file systems etc.
Args: *
zip: zip archive handler. * @param zip: zip archive handler.
*
Returns: * @return the pointer to the current zip entry name, or NULL on error.
The pointer to the current zip entry name, or NULL on error. */
*/
extern const char *zip_entry_name(struct zip_t *zip); extern const char *zip_entry_name(struct zip_t *zip);
/* /**
Returns an index of the current zip entry. * Returns an index of the current zip entry.
*
Args: * @param zip zip archive handler.
zip: zip archive handler. *
* @return the index on success, negative number (< 0) on error.
Returns: */
The index on success, negative number (< 0) on error.
*/
extern int zip_entry_index(struct zip_t *zip); extern int zip_entry_index(struct zip_t *zip);
/* /**
Determines if the current zip entry is a directory entry. * Determines if the current zip entry is a directory entry.
*
Args: * @param zip zip archive handler.
zip: zip archive handler. *
* @return the return code - 1 (true), 0 (false), negative number (< 0) on
Returns: * error.
The return code - 1 (true), 0 (false), negative number (< 0) on error. */
*/
extern int zip_entry_isdir(struct zip_t *zip); extern int zip_entry_isdir(struct zip_t *zip);
/* /**
Returns an uncompressed size of the current zip entry. * Returns an uncompressed size of the current zip entry.
*
Args: * @param zip zip archive handler.
zip: zip archive handler. *
* @return the uncompressed size in bytes.
Returns: */
The uncompressed size in bytes.
*/
extern unsigned long long zip_entry_size(struct zip_t *zip); extern unsigned long long zip_entry_size(struct zip_t *zip);
/* /**
Returns CRC-32 checksum of the current zip entry. * Returns CRC-32 checksum of the current zip entry.
*
Args: * @param zip zip archive handler.
zip: zip archive handler. *
* @return the CRC-32 checksum.
Returns: */
The CRC-32 checksum.
*/
extern unsigned int zip_entry_crc32(struct zip_t *zip); extern unsigned int zip_entry_crc32(struct zip_t *zip);
/* /**
Compresses an input buffer for the current zip entry. * Compresses an input buffer for the current zip entry.
*
Args: * @param zip zip archive handler.
zip: zip archive handler. * @param buf input buffer.
buf: input buffer. * @param bufsize input buffer size (in bytes).
bufsize: input buffer size (in bytes). *
* @return the return code - 0 on success, negative number (< 0) on error.
Returns: */
The return code - 0 on success, negative number (< 0) on error.
*/
extern int zip_entry_write(struct zip_t *zip, const void *buf, size_t bufsize); extern int zip_entry_write(struct zip_t *zip, const void *buf, size_t bufsize);
/* /**
Compresses a file for the current zip entry. * Compresses a file for the current zip entry.
*
Args: * @param zip zip archive handler.
zip: zip archive handler. * @param filename input file.
filename: input file. *
* @return the return code - 0 on success, negative number (< 0) on error.
Returns: */
The return code - 0 on success, negative number (< 0) on error.
*/
extern int zip_entry_fwrite(struct zip_t *zip, const char *filename); extern int zip_entry_fwrite(struct zip_t *zip, const char *filename);
/* /**
Extracts the current zip entry into output buffer. * Extracts the current zip entry into output buffer.
The function allocates sufficient memory for a output buffer. *
* The function allocates sufficient memory for a output buffer.
Args: *
zip: zip archive handler. * @param zip zip archive handler.
buf: output buffer. * @param buf output buffer.
bufsize: output buffer size (in bytes). * @param bufsize output buffer size (in bytes).
*
Note: * @note remember to release memory allocated for a output buffer.
- remember to release memory allocated for a output buffer. * for large entries, please take a look at zip_entry_extract function.
- for large entries, please take a look at zip_entry_extract function. *
* @return the return code - the number of bytes actually read on success.
Returns: * Otherwise a -1 on error.
The return code - the number of bytes actually read on success. */
Otherwise a -1 on error.
*/
extern ssize_t zip_entry_read(struct zip_t *zip, void **buf, size_t *bufsize); extern ssize_t zip_entry_read(struct zip_t *zip, void **buf, size_t *bufsize);
/* /**
Extracts the current zip entry into a memory buffer using no memory * Extracts the current zip entry into a memory buffer using no memory
allocation. * allocation.
*
* @param zip zip archive handler.
* @param buf preallocated output buffer.
* @param bufsize output buffer size (in bytes).
*
* @note ensure supplied output buffer is large enough.
* zip_entry_size function (returns uncompressed size for the current
* entry) can be handy to estimate how big buffer is needed. for large
* entries, please take a look at zip_entry_extract function.
*
* @return the return code - the number of bytes actually read on success.
* Otherwise a -1 on error (e.g. bufsize is not large enough).
*/
extern ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf,
size_t bufsize);
Args: /**
zip: zip archive handler. * Extracts the current zip entry into output file.
buf: preallocated output buffer. *
bufsize: output buffer size (in bytes). * @param zip zip archive handler.
* @param filename output file.
Note: *
- ensure supplied output buffer is large enough. * @return the return code - 0 on success, negative number (< 0) on error.
- zip_entry_size function (returns uncompressed size for the current entry) */
can be handy to estimate how big buffer is needed.
- for large entries, please take a look at zip_entry_extract function.
Returns:
The return code - the number of bytes actually read on success.
Otherwise a -1 on error (e.g. bufsize is not large enough).
*/
extern ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf, size_t bufsize);
/*
Extracts the current zip entry into output file.
Args:
zip: zip archive handler.
filename: output file.
Returns:
The return code - 0 on success, negative number (< 0) on error.
*/
extern int zip_entry_fread(struct zip_t *zip, const char *filename); extern int zip_entry_fread(struct zip_t *zip, const char *filename);
/* /**
Extracts the current zip entry using a callback function (on_extract). * Extracts the current zip entry using a callback function (on_extract).
*
Args: * @param zip zip archive handler.
zip: zip archive handler. * @param on_extract callback function.
on_extract: callback function. * @param arg opaque pointer (optional argument, which you can pass to the
arg: opaque pointer (optional argument, * on_extract callback)
which you can pass to the on_extract callback) *
* @return the return code - 0 on success, negative number (< 0) on error.
Returns:
The return code - 0 on success, negative number (< 0) on error.
*/ */
extern int extern int
zip_entry_extract(struct zip_t *zip, zip_entry_extract(struct zip_t *zip,
@ -262,53 +261,49 @@ zip_entry_extract(struct zip_t *zip,
const void *data, size_t size), const void *data, size_t size),
void *arg); void *arg);
/* /**
Returns the number of all entries (files and directories) in the zip archive. * Returns the number of all entries (files and directories) in the zip archive.
*
Args: * @param zip zip archive handler.
zip: zip archive handler. *
* @return the return code - the number of entries on success, negative number
Returns: * (< 0) on error.
The return code - the number of entries on success, */
negative number (< 0) on error.
*/
extern int zip_total_entries(struct zip_t *zip); extern int zip_total_entries(struct zip_t *zip);
/* /**
Creates a new archive and puts files into a single zip archive. * Creates a new archive and puts files into a single zip archive.
*
Args: * @param zipname zip archive file.
zipname: zip archive file. * @param filenames input files.
filenames: input files. * @param len: number of input files.
len: number of input files. *
* @return the return code - 0 on success, negative number (< 0) on error.
Returns: */
The return code - 0 on success, negative number (< 0) on error.
*/
extern int zip_create(const char *zipname, const char *filenames[], size_t len); extern int zip_create(const char *zipname, const char *filenames[], size_t len);
/* /**
Extracts a zip archive file into directory. * Extracts a zip archive file into directory.
*
If on_extract_entry is not NULL, the callback will be called after * If on_extract_entry is not NULL, the callback will be called after
successfully extracted each zip entry. * successfully extracted each zip entry.
Returning a negative value from the callback will cause abort and return an * Returning a negative value from the callback will cause abort and return an
error. The last argument (void *arg) is optional, which you can use to pass * error. The last argument (void *arg) is optional, which you can use to pass
data to the on_extract_entry callback. * data to the on_extract_entry callback.
*
Args: * @param zipname zip archive file.
zipname: zip archive file. * @param dir output directory.
dir: output directory. * @param on_extract_entry on extract callback.
on_extract_entry: on extract callback. * @param arg opaque pointer.
arg: opaque pointer. *
* @return the return code - 0 on success, negative number (< 0) on error.
Returns: */
The return code - 0 on success, negative number (< 0) on error.
*/
extern int zip_extract(const char *zipname, const char *dir, extern int zip_extract(const char *zipname, const char *dir,
int (*on_extract_entry)(const char *filename, void *arg), int (*on_extract_entry)(const char *filename, void *arg),
void *arg); void *arg);
/** @} */
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

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

View File

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

View File

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

View File

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

@ -119,6 +119,16 @@ 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>()();\

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,

View File

@ -72,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
*/ */

View File

@ -0,0 +1,63 @@
/*
---------------------------------------------------------------------------
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.
---------------------------------------------------------------------------
*/
/** @file commonMetaData.h
* @brief Defines a set of common scene metadata keys.
*/
#pragma once
#ifndef AI_COMMONMETADATA_H_INC
#define AI_COMMONMETADATA_H_INC
/// Scene metadata holding the name of the importer which loaded the source asset.
/// This is always present if the scene was created from an imported asset.
#define AI_METADATA_SOURCE_FORMAT "SourceAsset_Format"
/// Scene metadata holding the version of the source asset as a string, if available.
/// Not all formats add this metadata.
#define AI_METADATA_SOURCE_FORMAT_VERSION "SourceAsset_FormatVersion"
/// Scene metadata holding the name of the software which generated the source asset, if available.
/// Not all formats add this metadata.
#define AI_METADATA_SOURCE_GENERATOR "SourceAsset_Generator"
#endif

View File

@ -695,6 +695,73 @@ enum aiComponent
#define AI_CONFIG_IMPORT_SMD_KEYFRAME "IMPORT_SMD_KEYFRAME" #define AI_CONFIG_IMPORT_SMD_KEYFRAME "IMPORT_SMD_KEYFRAME"
#define AI_CONFIG_IMPORT_UNREAL_KEYFRAME "IMPORT_UNREAL_KEYFRAME" #define AI_CONFIG_IMPORT_UNREAL_KEYFRAME "IMPORT_UNREAL_KEYFRAME"
// ---------------------------------------------------------------------------
/** @brief Set whether the MDL (HL1) importer will read animations.
*
* The default value is true (1)
* Property type: bool
*/
#define AI_CONFIG_IMPORT_MDL_HL1_READ_ANIMATIONS "IMPORT_MDL_HL1_READ_ANIMATIONS"
// ---------------------------------------------------------------------------
/** @brief Set whether the MDL (HL1) importer will read animation events.
* \note This property requires AI_CONFIG_IMPORT_MDL_HL1_READ_ANIMATIONS to be set to true.
*
* The default value is true (1)
* Property type: bool
*/
#define AI_CONFIG_IMPORT_MDL_HL1_READ_ANIMATION_EVENTS "IMPORT_MDL_HL1_READ_ANIMATION_EVENTS"
// ---------------------------------------------------------------------------
/** @brief Set whether the MDL (HL1) importer will read blend controllers.
* \note This property requires AI_CONFIG_IMPORT_MDL_HL1_READ_ANIMATIONS to be set to true.
*
* The default value is true (1)
* Property type: bool
*/
#define AI_CONFIG_IMPORT_MDL_HL1_READ_BLEND_CONTROLLERS "IMPORT_MDL_HL1_READ_BLEND_CONTROLLERS"
// ---------------------------------------------------------------------------
/** @brief Set whether the MDL (HL1) importer will read sequence transition graph.
* \note This property requires AI_CONFIG_IMPORT_MDL_HL1_READ_ANIMATIONS to be set to true.
*
* The default value is true (1)
* Property type: bool
*/
#define AI_CONFIG_IMPORT_MDL_HL1_READ_SEQUENCE_TRANSITIONS "IMPORT_MDL_HL1_READ_SEQUENCE_TRANSITIONS"
// ---------------------------------------------------------------------------
/** @brief Set whether the MDL (HL1) importer will read attachments info.
*
* The default value is true (1)
* Property type: bool
*/
#define AI_CONFIG_IMPORT_MDL_HL1_READ_ATTACHMENTS "IMPORT_MDL_HL1_READ_ATTACHMENTS"
// ---------------------------------------------------------------------------
/** @brief Set whether the MDL (HL1) importer will read bone controllers info.
*
* The default value is true (1)
* Property type: bool
*/
#define AI_CONFIG_IMPORT_MDL_HL1_READ_BONE_CONTROLLERS "IMPORT_MDL_HL1_READ_BONE_CONTROLLERS"
// ---------------------------------------------------------------------------
/** @brief Set whether the MDL (HL1) importer will read hitboxes info.
*
* The default value is true (1)
* Property type: bool
*/
#define AI_CONFIG_IMPORT_MDL_HL1_READ_HITBOXES "IMPORT_MDL_HL1_READ_HITBOXES"
// ---------------------------------------------------------------------------
/** @brief Set whether the MDL (HL1) importer will read miscellaneous global model info.
*
* The default value is true (1)
* Property type: bool
*/
#define AI_CONFIG_IMPORT_MDL_HL1_READ_MISC_GLOBAL_INFO "IMPORT_MDL_HL1_READ_MISC_GLOBAL_INFO"
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/** Smd load multiple animations /** Smd load multiple animations
* *

View File

@ -128,16 +128,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* GENBOUNDINGBOXES */ * GENBOUNDINGBOXES */
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
#ifdef _MSC_VER #ifdef _WIN32
# undef ASSIMP_API # undef ASSIMP_API
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
/* Define 'ASSIMP_BUILD_DLL_EXPORT' to build a DLL of the library */ /* Define 'ASSIMP_BUILD_DLL_EXPORT' to build a DLL of the library */
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
# ifdef ASSIMP_BUILD_DLL_EXPORT # ifdef ASSIMP_BUILD_DLL_EXPORT
# define ASSIMP_API __declspec(dllexport) # define ASSIMP_API __declspec(dllexport)
# define ASSIMP_API_WINONLY __declspec(dllexport) # define ASSIMP_API_WINONLY __declspec(dllexport)
# pragma warning (disable : 4251)
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
/* Define 'ASSIMP_DLL' before including Assimp to link to ASSIMP in /* Define 'ASSIMP_DLL' before including Assimp to link to ASSIMP in
@ -150,7 +148,19 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# define ASSIMP_API # define ASSIMP_API
# define ASSIMP_API_WINONLY # define ASSIMP_API_WINONLY
# endif # endif
#elif defined(SWIG)
/* Do nothing, the relevant defines are all in AssimpSwigPort.i */
#else
# define ASSIMP_API __attribute__ ((visibility("default")))
# define ASSIMP_API_WINONLY
#endif
#ifdef _MSC_VER
# ifdef ASSIMP_BUILD_DLL_EXPORT
# pragma warning (disable : 4251)
# endif
/* Force the compiler to inline a function, if possible /* Force the compiler to inline a function, if possible
*/ */
# define AI_FORCE_INLINE __forceinline # define AI_FORCE_INLINE __forceinline
@ -158,17 +168,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/* Tells the compiler that a function never returns. Used in code analysis /* Tells the compiler that a function never returns. Used in code analysis
* to skip dead paths (e.g. after an assertion evaluated to false). */ * to skip dead paths (e.g. after an assertion evaluated to false). */
# define AI_WONT_RETURN __declspec(noreturn) # define AI_WONT_RETURN __declspec(noreturn)
#elif defined(SWIG) #elif defined(SWIG)
/* Do nothing, the relevant defines are all in AssimpSwigPort.i */ /* Do nothing, the relevant defines are all in AssimpSwigPort.i */
#else #else
# define AI_WONT_RETURN # define AI_WONT_RETURN
# define ASSIMP_API __attribute__ ((visibility("default")))
# define ASSIMP_API_WINONLY
# define AI_FORCE_INLINE inline # define AI_FORCE_INLINE inline
#endif // (defined _MSC_VER) #endif // (defined _MSC_VER)
@ -301,7 +306,11 @@ static const ai_real ai_epsilon = (ai_real) 0.00001;
#define AI_MAX_ALLOC(type) ((256U * 1024 * 1024) / sizeof(type)) #define AI_MAX_ALLOC(type) ((256U * 1024 * 1024) / sizeof(type))
#ifndef _MSC_VER #ifndef _MSC_VER
# define AI_NO_EXCEPT noexcept # if __cplusplus >= 201103L // C++11
# define AI_NO_EXCEPT noexcept
# else
# define AI_NO_EXCEPT
# endif
#else #else
# if (_MSC_VER >= 1915 ) # if (_MSC_VER >= 1915 )
# define AI_NO_EXCEPT noexcept # define AI_NO_EXCEPT noexcept

View File

@ -252,6 +252,9 @@ struct aiVertexWeight {
}; };
// Forward declare aiNode (pointer use only)
struct aiNode;
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/** @brief A single bone of a mesh. /** @brief A single bone of a mesh.
* *
@ -268,6 +271,16 @@ struct aiBone {
//! The maximum value for this member is #AI_MAX_BONE_WEIGHTS. //! The maximum value for this member is #AI_MAX_BONE_WEIGHTS.
unsigned int mNumWeights; unsigned int mNumWeights;
#ifndef ASSIMP_BUILD_NO_ARMATUREPOPULATE_PROCESS
// The bone armature node - used for skeleton conversion
// you must enable aiProcess_PopulateArmatureData to populate this
C_STRUCT aiNode* mArmature;
// The bone node in the scene - used for skeleton conversion
// you must enable aiProcess_PopulateArmatureData to populate this
C_STRUCT aiNode* mNode;
#endif
//! The influence weights of this bone, by vertex index. //! The influence weights of this bone, by vertex index.
C_STRUCT aiVertexWeight* mWeights; C_STRUCT aiVertexWeight* mWeights;
@ -422,11 +435,11 @@ struct aiAnimMesh
/**Anim Mesh name */ /**Anim Mesh name */
C_STRUCT aiString mName; C_STRUCT aiString mName;
/** Replacement for aiMesh::mVertices. If this array is non-NULL, /** Replacement for aiMesh::mVertices. If this array is non-nullptr,
* it *must* contain mNumVertices entries. The corresponding * it *must* contain mNumVertices entries. The corresponding
* array in the host mesh must be non-NULL as well - animation * array in the host mesh must be non-nullptr as well - animation
* meshes may neither add or nor remove vertex components (if * meshes may neither add or nor remove vertex components (if
* a replacement array is NULL and the corresponding source * a replacement array is nullptr and the corresponding source
* array is not, the source data is taken instead)*/ * array is not, the source data is taken instead)*/
C_STRUCT aiVector3D* mVertices; C_STRUCT aiVector3D* mVertices;
@ -600,7 +613,7 @@ struct aiMesh
C_STRUCT aiVector3D* mVertices; C_STRUCT aiVector3D* mVertices;
/** Vertex normals. /** Vertex normals.
* The array contains normalized vectors, NULL if not present. * The array contains normalized vectors, nullptr if not present.
* The array is mNumVertices in size. Normals are undefined for * The array is mNumVertices in size. Normals are undefined for
* point and line primitives. A mesh consisting of points and * point and line primitives. A mesh consisting of points and
* lines only may not have normal vectors. Meshes with mixed * lines only may not have normal vectors. Meshes with mixed
@ -623,7 +636,7 @@ struct aiMesh
/** Vertex tangents. /** Vertex tangents.
* The tangent of a vertex points in the direction of the positive * The tangent of a vertex points in the direction of the positive
* X texture axis. The array contains normalized vectors, NULL if * X texture axis. The array contains normalized vectors, nullptr if
* not present. The array is mNumVertices in size. A mesh consisting * not present. The array is mNumVertices in size. A mesh consisting
* of points and lines only may not have normal vectors. Meshes with * of points and lines only may not have normal vectors. Meshes with
* mixed primitive types (i.e. lines and triangles) may have * mixed primitive types (i.e. lines and triangles) may have
@ -637,7 +650,7 @@ struct aiMesh
/** Vertex bitangents. /** Vertex bitangents.
* The bitangent of a vertex points in the direction of the positive * The bitangent of a vertex points in the direction of the positive
* Y texture axis. The array contains normalized vectors, NULL if not * Y texture axis. The array contains normalized vectors, nullptr if not
* present. The array is mNumVertices in size. * present. The array is mNumVertices in size.
* @note If the mesh contains tangents, it automatically also contains * @note If the mesh contains tangents, it automatically also contains
* bitangents. * bitangents.
@ -646,14 +659,14 @@ struct aiMesh
/** Vertex color sets. /** Vertex color sets.
* A mesh may contain 0 to #AI_MAX_NUMBER_OF_COLOR_SETS vertex * A mesh may contain 0 to #AI_MAX_NUMBER_OF_COLOR_SETS vertex
* colors per vertex. NULL if not present. Each array is * colors per vertex. nullptr if not present. Each array is
* mNumVertices in size if present. * mNumVertices in size if present.
*/ */
C_STRUCT aiColor4D* mColors[AI_MAX_NUMBER_OF_COLOR_SETS]; C_STRUCT aiColor4D* mColors[AI_MAX_NUMBER_OF_COLOR_SETS];
/** Vertex texture coords, also known as UV channels. /** Vertex texture coords, also known as UV channels.
* A mesh may contain 0 to AI_MAX_NUMBER_OF_TEXTURECOORDS per * A mesh may contain 0 to AI_MAX_NUMBER_OF_TEXTURECOORDS per
* vertex. NULL if not present. The array is mNumVertices in size. * vertex. nullptr if not present. The array is mNumVertices in size.
*/ */
C_STRUCT aiVector3D* mTextureCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS]; C_STRUCT aiVector3D* mTextureCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS];
@ -675,7 +688,7 @@ struct aiMesh
C_STRUCT aiFace* mFaces; C_STRUCT aiFace* mFaces;
/** The number of bones this mesh contains. /** The number of bones this mesh contains.
* Can be 0, in which case the mBones array is NULL. * Can be 0, in which case the mBones array is nullptr.
*/ */
unsigned int mNumBones; unsigned int mNumBones;
@ -773,7 +786,10 @@ struct aiMesh
// DO NOT REMOVE THIS ADDITIONAL CHECK // DO NOT REMOVE THIS ADDITIONAL CHECK
if (mNumBones && mBones) { if (mNumBones && mBones) {
for( unsigned int a = 0; a < mNumBones; a++) { for( unsigned int a = 0; a < mNumBones; a++) {
delete mBones[a]; if(mBones[a])
{
delete mBones[a];
}
} }
delete [] mBones; delete [] mBones;
} }

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