Merge branch 'master' into python-fixes

pull/2813/head
Kim Kulling 2019-12-08 12:58:50 +01:00 committed by GitHub
commit 6b5384afa6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
194 changed files with 14630 additions and 3034 deletions

127
.clang-format 100644
View File

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

5
.gitignore vendored
View File

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

View File

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

View File

@ -253,7 +253,7 @@ ELSEIF(MSVC)
IF(MSVC12)
ADD_COMPILE_OPTIONS(/wd4351)
ENDIF()
SET(CMAKE_CXX_FLAGS_DEBUG "/D_DEBUG /MDd /Ob2 /DEBUG:FULL /Zi")
SET(CMAKE_CXX_FLAGS_DEBUG "/D_DEBUG /MDd /Zi /Od")
ELSEIF ( "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" )
IF(NOT HUNTER_ENABLED)
SET(CMAKE_CXX_FLAGS "-fPIC -std=c++11 ${CMAKE_CXX_FLAGS}")
@ -391,6 +391,11 @@ IF(HUNTER_ENABLED)
)
ELSE(HUNTER_ENABLED)
# 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}/assimpTargets.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/assimpTargets.cmake" @ONLY IMMEDIATE)
IF (is_multi_config)

View File

@ -60,13 +60,19 @@ __Importers__:
- ENFF
- [FBX](https://en.wikipedia.org/wiki/FBX)
- [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
- IFC-STEP
- IRR / IRRMESH
- [LWO](https://en.wikipedia.org/wiki/LightWave_3D)
- LWS
- LXO
- [M3D](https://bztsrc.gitlab.io/model3d)
- MD2
- MD3
- MD5
@ -120,7 +126,7 @@ __Exporters__:
- FBX ( experimental )
### Building ###
Take a look into the https://github.com/assimp/assimp/blob/master/Build.md file. Our build system is CMake, if you used CMake before there is a good chance you know what to do.
Take a look into the https://github.com/assimp/assimp/blob/master/Build.md file. We are available in vcpkg, and our build system is CMake; if you used CMake before there is a good chance you know what to do.
### Ports ###
* [Android](port/AndroidJNI/README.md)

View File

@ -14,8 +14,10 @@ matrix:
fast_finish: true
image:
- Visual Studio 2013
- Visual Studio 2015
- Visual Studio 2017
- Visual Studio 2019
- MinGW
platform:
@ -28,11 +30,13 @@ install:
- set PATH=C:\Ruby24-x64\bin;%PATH%
- set CMAKE_DEFINES -DASSIMP_WERROR=ON
- 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 2017" set CMAKE_GENERATOR_NAME=Visual Studio 15 2017
- if "%platform%"=="x64" set CMAKE_GENERATOR_NAME=%CMAKE_GENERATOR_NAME% Win64
- cmake %CMAKE_DEFINES% -G "%CMAKE_GENERATOR_NAME%" .
# Rename sh.exe as sh.exe in PATH interferes with MinGW
- 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% .
# 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"
- 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

View File

@ -35,6 +35,8 @@ if(MSVC)
endif()
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)
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@")
@ -63,7 +65,14 @@ if(MSVC)
else()
set(ASSIMP_LIBRARY_SUFFIX "@ASSIMP_LIBRARY_SUFFIX@" CACHE STRING "the suffix for the assimp libraries" )
if(ASSIMP_BUILD_SHARED_LIBS)
if(WIN32)
# Handle MinGW compiler.
set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@@CMAKE_SHARED_LIBRARY_SUFFIX@@CMAKE_STATIC_LIBRARY_SUFFIX@")
elseif(APPLE)
set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@.@ASSIMP_VERSION_MAJOR@@CMAKE_SHARED_LIBRARY_SUFFIX@")
else()
set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@@CMAKE_SHARED_LIBRARY_SUFFIX@.@ASSIMP_VERSION_MAJOR@")
endif()
set_target_properties(assimp::assimp PROPERTIES
IMPORTED_SONAME_DEBUG "${sharedLibraryName}"
IMPORTED_LOCATION_DEBUG "${_IMPORT_PREFIX}/lib/${sharedLibraryName}"

View File

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

View File

@ -5,6 +5,9 @@ if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" LESS 2.5)
endif()
cmake_policy(PUSH)
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.
#----------------------------------------------------------------
@ -51,11 +54,7 @@ if(_IMPORT_PREFIX STREQUAL "/")
endif()
# Create imported target assimp::assimp
if(@BUILD_SHARED_LIBS@)
add_library(assimp::assimp SHARED IMPORTED)
else()
add_library(assimp::assimp STATIC IMPORTED)
endif()
add_library(assimp::assimp @BUILD_LIB_TYPE@ IMPORTED)
set_target_properties(assimp::assimp PROPERTIES
COMPATIBLE_INTERFACE_STRING "assimp_MAJOR_VERSION"

View File

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

View File

@ -72,7 +72,7 @@ void Discreet3DSImporter::ReplaceDefaultMaterial()
unsigned int idx( NotSet );
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 ) {
*it = static_cast< char >( ::tolower( *it ) );
}

View File

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

View File

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

View File

@ -137,7 +137,6 @@ SET( PUBLIC_HEADERS
${HEADER_PATH}/irrXMLWrapper.h
${HEADER_PATH}/BlobIOSystem.h
${HEADER_PATH}/MathFunctions.h
${HEADER_PATH}/Macros.h
${HEADER_PATH}/Exceptional.h
${HEADER_PATH}/ByteSwapper.h
)
@ -408,6 +407,20 @@ ADD_ASSIMP_IMPORTER( LWS
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
MD2/MD2FileData.h
MD2/MD2Loader.cpp
@ -671,6 +684,8 @@ SET( PostProcessing_SRCS
PostProcessing/MakeVerboseFormat.h
PostProcessing/ScaleProcess.cpp
PostProcessing/ScaleProcess.h
PostProcessing/ArmaturePopulate.cpp
PostProcessing/ArmaturePopulate.h
PostProcessing/GenBoundingBoxesProcess.cpp
PostProcessing/GenBoundingBoxesProcess.h
)
@ -1059,6 +1074,8 @@ MESSAGE(STATUS "Disabled importer formats:${ASSIMP_IMPORTERS_DISABLED}")
MESSAGE(STATUS "Enabled exporter formats:${ASSIMP_EXPORTERS_ENABLED}")
MESSAGE(STATUS "Disabled exporter formats:${ASSIMP_EXPORTERS_DISABLED}")
SOURCE_GROUP( include\\assimp FILES ${PUBLIC_HEADERS} )
SET( assimp_src
# Assimp Files
${Core_SRCS}

View File

@ -92,6 +92,36 @@ void ExportSceneCollada(const char* pFile, IOSystem* pIOSystem, const aiScene* p
} // 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
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.
mOutput << startstr << "<scene>" << endstr;
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();
mOutput << startstr << "</scene>" << endstr;
PopTag();
@ -356,9 +386,10 @@ void ColladaExporter::WriteCamerasLibrary() {
void ColladaExporter::WriteCamera(size_t 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();
mOutput << startstr << "<optics>" << endstr;
PushTag();
@ -412,10 +443,11 @@ void ColladaExporter::WriteLightsLibrary() {
void ColladaExporter::WriteLight(size_t 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=\""
<< idstrEscaped << "_name\" >" << endstr;
mOutput << startstr << "<light id=\"" << lightId << "-light\" name=\""
<< lightName << "\" >" << endstr;
PushTag();
mOutput << startstr << "<technique_common>" << endstr;
PushTag();
@ -586,7 +618,7 @@ static bool isalnum_C(char c) {
void ColladaExporter::WriteImageEntry( const Surface& pSurface, const std::string& pNameAdd) {
if( !pSurface.texture.empty() )
{
mOutput << startstr << "<image id=\"" << XMLEscape(pNameAdd) << "\">" << endstr;
mOutput << startstr << "<image id=\"" << XMLIDEncode(pNameAdd) << "\">" << endstr;
PushTag();
mOutput << startstr << "<init_from>";
@ -619,7 +651,7 @@ void ColladaExporter::WriteTextureColorEntry( const Surface& pSurface, const std
}
else
{
mOutput << startstr << "<texture texture=\"" << XMLEscape(pImageName) << "\" texcoord=\"CHANNEL" << pSurface.channel << "\" />" << endstr;
mOutput << startstr << "<texture texture=\"" << XMLIDEncode(pImageName) << "\" texcoord=\"CHANNEL" << pSurface.channel << "\" />" << endstr;
}
PopTag();
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( !pSurface.texture.empty() )
{
mOutput << startstr << "<newparam sid=\"" << XMLEscape(pMatName) << "-" << pTypeName << "-surface\">" << endstr;
mOutput << startstr << "<newparam sid=\"" << XMLIDEncode(pMatName) << "-" << pTypeName << "-surface\">" << endstr;
PushTag();
mOutput << startstr << "<surface type=\"2D\">" << endstr;
PushTag();
mOutput << startstr << "<init_from>" << XMLEscape(pMatName) << "-" << pTypeName << "-image</init_from>" << endstr;
mOutput << startstr << "<init_from>" << XMLIDEncode(pMatName) << "-" << pTypeName << "-image</init_from>" << endstr;
PopTag();
mOutput << startstr << "</surface>" << endstr;
PopTag();
mOutput << startstr << "</newparam>" << endstr;
mOutput << startstr << "<newparam sid=\"" << XMLEscape(pMatName) << "-" << pTypeName << "-sampler\">" << endstr;
mOutput << startstr << "<newparam sid=\"" << XMLIDEncode(pMatName) << "-" << pTypeName << "-sampler\">" << endstr;
PushTag();
mOutput << startstr << "<sampler2D>" << endstr;
PushTag();
mOutput << startstr << "<source>" << XMLEscape(pMatName) << "-" << pTypeName << "-surface</source>" << endstr;
mOutput << startstr << "<source>" << XMLIDEncode(pMatName) << "-" << pTypeName << "-surface</source>" << endstr;
PopTag();
mOutput << startstr << "</sampler2D>" << endstr;
PopTag();
@ -699,11 +731,6 @@ void ColladaExporter::WriteMaterials()
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;
materials[a].shading_model = "phong";
@ -768,7 +795,7 @@ void ColladaExporter::WriteMaterials()
{
const Material& mat = *it;
// 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();
mOutput << startstr << "<profile_COMMON>" << endstr;
PushTag();
@ -819,9 +846,9 @@ void ColladaExporter::WriteMaterials()
for( std::vector<Material>::const_iterator it = materials.begin(); it != materials.end(); ++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();
mOutput << startstr << "<instance_effect url=\"#" << XMLEscape(mat.name) << "-fx\"/>" << endstr;
mOutput << startstr << "<instance_effect url=\"#" << XMLIDEncode(mat.name) << "-fx\"/>" << endstr;
PopTag();
mOutput << startstr << "</material>" << endstr;
}
@ -850,8 +877,8 @@ void ColladaExporter::WriteControllerLibrary()
void ColladaExporter::WriteController( size_t pIndex)
{
const aiMesh* mesh = mScene->mMeshes[pIndex];
const std::string idstr = GetMeshId( pIndex);
const std::string idstrEscaped = XMLEscape(idstr);
const std::string idstr = mesh->mName.length == 0 ? GetMeshId(pIndex) : mesh->mName.C_Str();
const std::string idstrEscaped = XMLIDEncode(idstr);
if ( mesh->mNumFaces == 0 || mesh->mNumVertices == 0 )
return;
@ -886,7 +913,7 @@ void ColladaExporter::WriteController( size_t pIndex)
mOutput << startstr << "<Name_array id=\"" << idstrEscaped << "-skin-joints-array\" count=\"" << mesh->mNumBones << "\">";
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;
@ -1021,14 +1048,15 @@ void ColladaExporter::WriteGeometryLibrary()
void ColladaExporter::WriteGeometry( size_t pIndex)
{
const aiMesh* mesh = mScene->mMeshes[pIndex];
const std::string idstr = GetMeshId( pIndex);
const std::string idstrEscaped = XMLEscape(idstr);
const std::string idstr = mesh->mName.length == 0 ? GetMeshId(pIndex) : mesh->mName.C_Str();
const std::string geometryName = XMLEscape(idstr);
const std::string geometryId = XMLIDEncode(idstr);
if ( mesh->mNumFaces == 0 || mesh->mNumVertices == 0 )
return;
// opening tag
mOutput << startstr << "<geometry id=\"" << idstrEscaped << "\" name=\"" << idstrEscaped << "_name\" >" << endstr;
mOutput << startstr << "<geometry id=\"" << geometryId << "\" name=\"" << geometryName << "\" >" << endstr;
PushTag();
mOutput << startstr << "<mesh>" << endstr;
@ -1059,9 +1087,9 @@ void ColladaExporter::WriteGeometry( size_t pIndex)
// assemble vertex structure
// 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();
mOutput << startstr << "<input semantic=\"POSITION\" source=\"#" << idstrEscaped << "-positions\" />" << endstr;
mOutput << startstr << "<input semantic=\"POSITION\" source=\"#" << geometryId << "-positions\" />" << endstr;
PopTag();
mOutput << startstr << "</vertices>" << endstr;
@ -1079,18 +1107,18 @@ void ColladaExporter::WriteGeometry( size_t pIndex)
{
mOutput << startstr << "<lines count=\"" << countLines << "\" material=\"defaultMaterial\">" << endstr;
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() )
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 )
{
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 )
{
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>";
@ -1113,18 +1141,18 @@ void ColladaExporter::WriteGeometry( size_t pIndex)
{
mOutput << startstr << "<polylist count=\"" << countPoly << "\" material=\"defaultMaterial\">" << endstr;
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() )
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 )
{
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 )
{
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>";
@ -1173,13 +1201,13 @@ void ColladaExporter::WriteFloatArray( const std::string& pIdString, FloatDataTy
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();
// source array
mOutput << startstr << "<float_array id=\"" << XMLEscape(arrayId) << "\" count=\"" << pElementCount * floatsPerElement << "\"> ";
mOutput << startstr << "<float_array id=\"" << arrayId << "\" count=\"" << pElementCount * floatsPerElement << "\"> ";
PushTag();
if( pType == FloatType_TexCoord2 )
@ -1265,11 +1293,12 @@ void ColladaExporter::WriteFloatArray( const std::string& pIdString, FloatDataTy
// Writes the scene library
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;
PushTag();
mOutput << startstr << "<visual_scene id=\"" + scene_name_escaped + "\" name=\"" + scene_name_escaped + "\">" << endstr;
mOutput << startstr << "<visual_scene id=\"" + sceneId + "\" name=\"" + sceneName + "\">" << endstr;
PushTag();
// start recursive write at the root node
@ -1300,7 +1329,7 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex)
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;
PushTag();
@ -1372,13 +1401,13 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex)
}
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();
// 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 ) {
mOutput << names[a] << " ";
}
@ -1387,7 +1416,7 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex)
mOutput << startstr << "<technique_common>" << endstr;
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();
mOutput << startstr << "<param name=\"INTERPOLATION\" type=\"name\"></param>" << endstr;
@ -1409,12 +1438,12 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex)
{
// samplers
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();
mOutput << startstr << "<input semantic=\"INPUT\" source=\"#" << XMLEscape( 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=\"INTERPOLATION\" source=\"#" << XMLEscape( nodeAnim->mNodeName.data + std::string("_matrix-interpolation") ) << "\"/>" << endstr;
mOutput << startstr << "<input semantic=\"INPUT\" source=\"#" << XMLIDEncode( nodeAnim->mNodeName.data + std::string("_matrix-input") ) << "\"/>" << endstr;
mOutput << startstr << "<input semantic=\"OUTPUT\" source=\"#" << XMLIDEncode( nodeAnim->mNodeName.data + std::string("_matrix-output") ) << "\"/>" << endstr;
mOutput << startstr << "<input semantic=\"INTERPOLATION\" source=\"#" << XMLIDEncode( nodeAnim->mNodeName.data + std::string("_matrix-interpolation") ) << "\"/>" << endstr;
PopTag();
mOutput << startstr << "</sampler>" << endstr;
@ -1426,7 +1455,7 @@ void ColladaExporter::WriteAnimationLibrary(size_t pIndex)
{
// 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()
{
const std::string scene_name_escaped = XMLEscape(mScene->mRootNode->mName.C_Str());
if ( mScene->mNumAnimations > 0 ) {
mOutput << startstr << "<library_animations>" << endstr;
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 ";
if(is_skeleton_root) {
mOutput << "id=\"" << node_name_escaped << "\" " << (is_joint ? "sid=\"" + node_name_escaped +"\"" : "") ; // For now, only support one skeleton in a scene.
mFoundSkeletonRootNodeID = node_name_escaped;
mOutput << "id=\"" << node_id << "\" " << (is_joint ? "sid=\"" + node_id +"\"" : "") ; // For now, only support one skeleton in a scene.
mFoundSkeletonRootNodeID = node_id;
} 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
<< "\">" << endstr;
PushTag();
@ -1594,14 +1622,14 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode)
//check if it is a camera node
for(size_t i=0; i<mScene->mNumCameras; i++){
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;
}
}
//check if it is a light node
for(size_t i=0; i<mScene->mNumLights; i++){
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;
}
}
@ -1615,15 +1643,17 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode)
if( mesh->mNumFaces == 0 || mesh->mNumVertices == 0 )
continue;
const std::string meshName = mesh->mName.length == 0 ? GetMeshId(pNode->mMeshes[a]) : mesh->mName.C_Str();
if( mesh->mNumBones == 0 )
{
mOutput << startstr << "<instance_geometry url=\"#" << XMLEscape(GetMeshId( pNode->mMeshes[a])) << "\">" << endstr;
mOutput << startstr << "<instance_geometry url=\"#" << XMLIDEncode(meshName) << "\">" << endstr;
PushTag();
}
else
{
mOutput << startstr
<< "<instance_controller url=\"#" << XMLEscape(GetMeshId( pNode->mMeshes[a])) << "-skin\">"
<< "<instance_controller url=\"#" << XMLIDEncode(meshName) << "-skin\">"
<< endstr;
PushTag();
@ -1631,7 +1661,7 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode)
// use the first bone to find skeleton root
const aiNode * skeletonRootBoneNode = findSkeletonRootNode( pScene, mesh );
if ( skeletonRootBoneNode ) {
mFoundSkeletonRootNodeID = XMLEscape( skeletonRootBoneNode->mName.C_Str() );
mFoundSkeletonRootNodeID = XMLIDEncode( skeletonRootBoneNode->mName.C_Str() );
}
mOutput << startstr << "<skeleton>#" << mFoundSkeletonRootNodeID << "</skeleton>" << endstr;
}
@ -1639,7 +1669,7 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode)
PushTag();
mOutput << startstr << "<technique_common>" << endstr;
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();
for( size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a )
{

View File

@ -1735,6 +1735,7 @@ void ColladaLoader::BuildMaterials(ColladaParser& pParser, aiScene* /*pScene*/)
// ------------------------------------------------------------------------------------------------
// Resolves the texture name for the given effect texture entry
// and loads the texture data
aiString ColladaLoader::FindFilenameForEffectTexture(const ColladaParser& pParser,
const Collada::Effect& pEffect, const std::string& pName)
{
@ -1762,7 +1763,7 @@ aiString ColladaLoader::FindFilenameForEffectTexture(const ColladaParser& pParse
//set default texture file name
result.Set(name + ".jpg");
ConvertPath(result);
ColladaParser::UriDecodePath(result);
return result;
}
@ -1781,7 +1782,7 @@ aiString ColladaLoader::FindFilenameForEffectTexture(const ColladaParser& pParse
// 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");
}
strncpy(tex->achFormatHint, imIt->second.mEmbeddedFormat.c_str(), 3);
@ -1802,61 +1803,10 @@ aiString ColladaLoader::FindFilenameForEffectTexture(const ColladaParser& pParse
}
result.Set(imIt->second.mFileName);
ConvertPath(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.
ai_real ColladaLoader::ReadFloat(const Collada::Accessor& pAccessor, const Collada::Data& pData, size_t pIndex, size_t pOffset) const

View File

@ -184,9 +184,6 @@ protected:
aiString FindFilenameForEffectTexture( const ColladaParser& pParser,
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.
* @param pAccessor The accessor to use for reading
* @param pData The data array to read from

View File

@ -183,13 +183,67 @@ std::string ColladaParser::ReadZaeManifest(ZipArchiveIOSystem &zip_archive) {
if (filepath == nullptr)
return std::string();
return std::string(filepath);
aiString ai_str(filepath);
UriDecodePath(ai_str);
return std::string(ai_str.C_Str());
}
}
}
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
bool ColladaParser::ReadBoolFromTextContent()
@ -1120,7 +1174,12 @@ void ColladaParser::ReadImage(Collada::Image& pImage)
if (!mReader->isEmptyElement()) {
// element content is filename - hopefully
const char* sz = TestTextContent();
if (sz)pImage.mFileName = sz;
if (sz)
{
aiString filepath(sz);
UriDecodePath(filepath);
pImage.mFileName = filepath.C_Str();
}
TestClosing("init_from");
}
if (!pImage.mFileName.length()) {
@ -1153,7 +1212,12 @@ void ColladaParser::ReadImage(Collada::Image& pImage)
{
// element content is filename - hopefully
const char* sz = TestTextContent();
if (sz)pImage.mFileName = sz;
if (sz)
{
aiString filepath(sz);
UriDecodePath(filepath);
pImage.mFileName = filepath.C_Str();
}
TestClosing("ref");
}
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
for (ImageLibrary::iterator it = mImageLibrary.begin(); it != mImageLibrary.end(); ++it) {
@ -3170,13 +3234,12 @@ void ColladaParser::ReadScene()
// ------------------------------------------------------------------------------------------------
// 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);
}
void ColladaParser::ReportWarning(const char* msg, ...)
{
ai_assert(NULL != msg);
void ColladaParser::ReportWarning(const char* msg, ...) {
ai_assert(nullptr != msg);
va_list args;
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
void ColladaParser::SkipElement()
{
void ColladaParser::SkipElement() {
// nothing to skip if it's an <element />
if (mReader->isEmptyElement())
if (mReader->isEmptyElement()) {
return;
}
// reroute
SkipElement(mReader->getNodeName());
@ -3203,63 +3266,75 @@ void ColladaParser::SkipElement()
// ------------------------------------------------------------------------------------------------
// 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,
// which is going to change with the upcoming parsing
std::string element = pElement;
while (mReader->read())
{
if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
if (mReader->getNodeName() == element)
while (mReader->read()) {
if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
if (mReader->getNodeName() == element) {
break;
}
}
}
}
// ------------------------------------------------------------------------------------------------
// 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
if (!mReader->read())
if (!mReader->read()) {
ThrowException(format() << "Unexpected end of file while beginning of <" << pName << "> element.");
}
// whitespace in front is ok, just read again if found
if (mReader->getNodeType() == irr::io::EXN_TEXT)
if (!mReader->read())
if (mReader->getNodeType() == irr::io::EXN_TEXT) {
if (!mReader->read()) {
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.");
}
}
// ------------------------------------------------------------------------------------------------
// Tests for the closing tag of the given element, throws an exception if not found
void ColladaParser::TestClosing(const char* pName)
{
// 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)
void ColladaParser::TestClosing(const char* pName) {
// check if we have an empty (self-closing) element
if (mReader->isEmptyElement()) {
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 (!mReader->read())
if (!mReader->read()) {
ThrowException(format() << "Unexpected end of file while reading end of <" << pName << "> element.");
}
// whitespace in front is ok, just read again if found
if (mReader->getNodeType() == irr::io::EXN_TEXT)
if (!mReader->read())
if (mReader->getNodeType() == irr::io::EXN_TEXT) {
if (!mReader->read()) {
ThrowException(format() << "Unexpected end of file while reading end of <" << pName << "> element.");
}
}
// 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.");
}
}
// ------------------------------------------------------------------------------------------------
// 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);
if (index != -1)
if (index != -1) {
return index;
}
// attribute not found -> throw an exception
ThrowException(format() << "Expected attribute \"" << pAttr << "\" for element <" << mReader->getNodeName() << ">.");

View File

@ -66,6 +66,9 @@ namespace Assimp
{
friend class ColladaLoader;
/** Converts a path read from a collada file to the usual representation */
static void UriDecodePath(aiString& ss);
protected:
/** Map for generic metadata as aiString */
typedef std::map<std::string, aiString> StringMetaData;

View File

@ -67,7 +67,20 @@ using namespace Assimp;
// Constructor to be privately used by Importer
BaseImporter::BaseImporter() AI_NO_EXCEPT
: 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;
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()
{
@ -93,7 +122,7 @@ aiReturn DefaultIOStream::Seek(size_t pOffset,
aiOrigin_END == SEEK_END && aiOrigin_SET == SEEK_SET");
// 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) {
return 0;
}
return ::ftell(mFile);
return select_ftell<sizeof(void*)>(mFile);
}
// ----------------------------------------------------------------------------------

View File

@ -121,7 +121,7 @@ LogStream* LogStream::createDefaultStream(aiDefaultLogStream streams,
};
// For compilers without dead code path detection
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 ExportSceneFBXA(const char*, IOSystem*, const aiScene*, const ExportProperties*);
void ExportScene3MF( const char*, IOSystem*, const aiScene*, const ExportProperties* );
void ExportSceneM3D(const char*, IOSystem*, const aiScene*, const ExportProperties*);
void ExportSceneA3D(const char*, IOSystem*, const aiScene*, const ExportProperties*);
void ExportAssimp2Json(const char* , IOSystem*, const aiScene* , const Assimp::ExportProperties*);
// ------------------------------------------------------------------------------------------------
// global array of all export formats which Assimp supports in its current build
Exporter::ExportFormatEntry gExporters[] =
{
static void setupExporterArray(std::vector<Exporter::ExportFormatEntry> &exporters) {
#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
#ifndef ASSIMP_BUILD_NO_X_EXPORTER
Exporter::ExportFormatEntry( "x", "X Files", "x", &ExportSceneXFile,
aiProcess_MakeLeftHanded | aiProcess_FlipWindingOrder | aiProcess_FlipUVs ),
exporters.push_back(Exporter::ExportFormatEntry("x", "X Files", "x", &ExportSceneXFile,
aiProcess_MakeLeftHanded | aiProcess_FlipWindingOrder | aiProcess_FlipUVs));
#endif
#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
#ifndef ASSIMP_BUILD_NO_OBJ_EXPORTER
Exporter::ExportFormatEntry( "obj", "Wavefront OBJ format", "obj", &ExportSceneObj,
aiProcess_GenSmoothNormals /*| aiProcess_PreTransformVertices */ ),
Exporter::ExportFormatEntry( "objnomtl", "Wavefront OBJ format without material file", "obj", &ExportSceneObjNoMtl,
aiProcess_GenSmoothNormals /*| aiProcess_PreTransformVertices */ ),
exporters.push_back(Exporter::ExportFormatEntry("obj", "Wavefront OBJ format", "obj", &ExportSceneObj,
aiProcess_GenSmoothNormals /*| aiProcess_PreTransformVertices */));
exporters.push_back(Exporter::ExportFormatEntry("objnomtl", "Wavefront OBJ format without material file", "obj", &ExportSceneObjNoMtl,
aiProcess_GenSmoothNormals /*| aiProcess_PreTransformVertices */));
#endif
#ifndef ASSIMP_BUILD_NO_STL_EXPORTER
Exporter::ExportFormatEntry( "stl", "Stereolithography", "stl" , &ExportSceneSTL,
aiProcess_Triangulate | aiProcess_GenNormals | aiProcess_PreTransformVertices
),
Exporter::ExportFormatEntry( "stlb", "Stereolithography (binary)", "stl" , &ExportSceneSTLBinary,
aiProcess_Triangulate | aiProcess_GenNormals | aiProcess_PreTransformVertices
),
exporters.push_back(Exporter::ExportFormatEntry("stl", "Stereolithography", "stl", &ExportSceneSTL,
aiProcess_Triangulate | aiProcess_GenNormals | aiProcess_PreTransformVertices));
exporters.push_back(Exporter::ExportFormatEntry("stlb", "Stereolithography (binary)", "stl", &ExportSceneSTLBinary,
aiProcess_Triangulate | aiProcess_GenNormals | aiProcess_PreTransformVertices));
#endif
#ifndef ASSIMP_BUILD_NO_PLY_EXPORTER
Exporter::ExportFormatEntry( "ply", "Stanford Polygon Library", "ply" , &ExportScenePly,
aiProcess_PreTransformVertices
),
Exporter::ExportFormatEntry( "plyb", "Stanford Polygon Library (binary)", "ply", &ExportScenePlyBinary,
aiProcess_PreTransformVertices
),
exporters.push_back(Exporter::ExportFormatEntry("ply", "Stanford Polygon Library", "ply", &ExportScenePly,
aiProcess_PreTransformVertices));
exporters.push_back(Exporter::ExportFormatEntry("plyb", "Stanford Polygon Library (binary)", "ply", &ExportScenePlyBinary,
aiProcess_PreTransformVertices));
#endif
#ifndef ASSIMP_BUILD_NO_3DS_EXPORTER
Exporter::ExportFormatEntry( "3ds", "Autodesk 3DS (legacy)", "3ds" , &ExportScene3DS,
aiProcess_Triangulate | aiProcess_SortByPType | aiProcess_JoinIdenticalVertices ),
exporters.push_back(Exporter::ExportFormatEntry("3ds", "Autodesk 3DS (legacy)", "3ds", &ExportScene3DS,
aiProcess_Triangulate | aiProcess_SortByPType | aiProcess_JoinIdenticalVertices));
#endif
#ifndef ASSIMP_BUILD_NO_GLTF_EXPORTER
Exporter::ExportFormatEntry( "gltf2", "GL Transmission Format v. 2", "gltf", &ExportSceneGLTF2,
aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ),
Exporter::ExportFormatEntry( "glb2", "GL Transmission Format v. 2 (binary)", "glb", &ExportSceneGLB2,
aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ),
Exporter::ExportFormatEntry( "gltf", "GL Transmission Format", "gltf", &ExportSceneGLTF,
aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ),
Exporter::ExportFormatEntry( "glb", "GL Transmission Format (binary)", "glb", &ExportSceneGLB,
aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType ),
exporters.push_back(Exporter::ExportFormatEntry("gltf2", "GL Transmission Format v. 2", "gltf", &ExportSceneGLTF2,
aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType));
exporters.push_back(Exporter::ExportFormatEntry("glb2", "GL Transmission Format v. 2 (binary)", "glb", &ExportSceneGLB2,
aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType));
exporters.push_back(Exporter::ExportFormatEntry("gltf", "GL Transmission Format", "gltf", &ExportSceneGLTF,
aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType));
exporters.push_back(Exporter::ExportFormatEntry("glb", "GL Transmission Format (binary)", "glb", &ExportSceneGLB,
aiProcess_JoinIdenticalVertices | aiProcess_Triangulate | aiProcess_SortByPType));
#endif
#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
#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
#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
#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER
Exporter::ExportFormatEntry( "fbx", "Autodesk FBX (binary)", "fbx", &ExportSceneFBX, 0 ),
Exporter::ExportFormatEntry( "fbxa", "Autodesk FBX (ascii)", "fbx", &ExportSceneFBXA, 0 ),
exporters.push_back(Exporter::ExportFormatEntry("fbx", "Autodesk FBX (binary)", "fbx", &ExportSceneFBX, 0));
exporters.push_back(Exporter::ExportFormatEntry("fbxa", "Autodesk FBX (ascii)", "fbx", &ExportSceneFBXA, 0));
#endif
#ifndef ASSIMP_BUILD_NO_M3D_EXPORTER
exporters.push_back(Exporter::ExportFormatEntry("m3d", "Model 3D (binary)", "m3d", &ExportSceneM3D, 0));
exporters.push_back(Exporter::ExportFormatEntry("a3d", "Model 3D (ascii)", "m3d", &ExportSceneA3D, 0));
#endif
#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
#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
};
#define ASSIMP_NUM_EXPORTERS (sizeof(gExporters)/sizeof(gExporters[0]))
}
class ExporterPimpl {
public:
@ -205,10 +203,7 @@ public:
GetPostProcessingStepInstanceList(mPostProcessingSteps);
// grab all built-in exporters
if ( 0 != ( ASSIMP_NUM_EXPORTERS ) ) {
mExporters.resize( ASSIMP_NUM_EXPORTERS );
std::copy( gExporters, gExporters + ASSIMP_NUM_EXPORTERS, mExporters.begin() );
}
setupExporterArray(mExporters);
}
~ExporterPimpl() {
@ -252,23 +247,27 @@ Exporter :: Exporter()
// ------------------------------------------------------------------------------------------------
Exporter::~Exporter() {
ai_assert(nullptr != pimpl);
FreeBlob();
delete pimpl;
}
// ------------------------------------------------------------------------------------------------
void Exporter::SetIOHandler( IOSystem* pIOHandler) {
ai_assert(nullptr != pimpl);
pimpl->mIsDefaultIOHandler = !pIOHandler;
pimpl->mIOSystem.reset(pIOHandler);
}
// ------------------------------------------------------------------------------------------------
IOSystem* Exporter::GetIOHandler() const {
ai_assert(nullptr != pimpl);
return pimpl->mIOSystem.get();
}
// ------------------------------------------------------------------------------------------------
bool Exporter::IsDefaultIOHandler() const {
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,
unsigned int pPreprocessing, const ExportProperties* pProperties) {
ai_assert(nullptr != pimpl);
if (pimpl->blob) {
delete pimpl->blob;
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,
unsigned int pPreprocessing, const ExportProperties* pProperties) {
ASSIMP_BEGIN_EXCEPTION_REGION();
ai_assert(nullptr != pimpl);
// 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
// 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* pProp = pProperties ? (ExportProperties*)pProperties : &emptyProperties;
pProp->SetPropertyBool("bJoinIdenticalVertices", must_join_again);
exp.mExportFunction(pPath,pimpl->mIOSystem.get(),scenecopy.get(), pProp);
pProp->SetPropertyBool("bJoinIdenticalVertices", pp & aiProcess_JoinIdenticalVertices);
exp.mExportFunction(pPath,pimpl->mIOSystem.get(),scenecopy.get(), pProp);
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 {
ai_assert(nullptr != pimpl);
return pimpl->mError.c_str();
}
// ------------------------------------------------------------------------------------------------
void Exporter::FreeBlob() {
ai_assert(nullptr != pimpl);
delete pimpl->blob;
pimpl->blob = nullptr;
@ -479,11 +480,13 @@ void Exporter::FreeBlob() {
// ------------------------------------------------------------------------------------------------
const aiExportDataBlob* Exporter::GetBlob() const {
ai_assert(nullptr != pimpl);
return pimpl->blob;
}
// ------------------------------------------------------------------------------------------------
const aiExportDataBlob* Exporter::GetOrphanedBlob() const {
ai_assert(nullptr != pimpl);
const aiExportDataBlob *tmp = pimpl->blob;
pimpl->blob = nullptr;
return tmp;
@ -491,18 +494,20 @@ const aiExportDataBlob* Exporter::GetOrphanedBlob() const {
// ------------------------------------------------------------------------------------------------
size_t Exporter::GetExportFormatCount() const {
ai_assert(nullptr != pimpl);
return pimpl->mExporters.size();
}
// ------------------------------------------------------------------------------------------------
const aiExportFormatDesc* Exporter::GetExportFormatDescription( size_t index ) const {
ai_assert(nullptr != pimpl);
if (index >= GetExportFormatCount()) {
return nullptr;
}
// Return from static storage if the requested index is built-in.
if (index < sizeof(gExporters) / sizeof(gExporters[0])) {
return &gExporters[index].mDescription;
if (index < pimpl->mExporters.size()) {
return &pimpl->mExporters[index].mDescription;
}
return &pimpl->mExporters[index].mDescription;
@ -510,6 +515,7 @@ const aiExportFormatDesc* Exporter::GetExportFormatDescription( size_t index ) c
// ------------------------------------------------------------------------------------------------
aiReturn Exporter::RegisterExporter(const ExportFormatEntry& desc) {
ai_assert(nullptr != pimpl);
for (const ExportFormatEntry &e : pimpl->mExporters) {
if (!strcmp(e.mDescription.id,desc.mDescription.id)) {
return aiReturn_FAILURE;
@ -522,6 +528,7 @@ aiReturn Exporter::RegisterExporter(const ExportFormatEntry& desc) {
// ------------------------------------------------------------------------------------------------
void Exporter::UnregisterExporter(const char* id) {
ai_assert(nullptr != pimpl);
for (std::vector<ExportFormatEntry>::iterator it = pimpl->mExporters.begin();
it != pimpl->mExporters.end(); ++it) {
if (!strcmp((*it).mDescription.id,id)) {

View File

@ -5,8 +5,6 @@ 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,
@ -78,6 +76,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/TinyFormatter.h>
#include <assimp/Exceptional.h>
#include <assimp/Profiler.h>
#include <assimp/commonMetaData.h>
#include <set>
#include <memory>
#include <cctype>
@ -119,7 +119,7 @@ void* AllocateFromAssimpHeap::operator new ( size_t num_bytes, const std::nothro
return AllocateFromAssimpHeap::operator new( num_bytes );
}
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() {
try {
return AllocateFromAssimpHeap::operator new[]( num_bytes );
}
catch( ... ) {
return NULL;
} catch( ... ) {
return nullptr;
}
}
@ -148,7 +147,7 @@ void AllocateFromAssimpHeap::operator delete[] ( void* data) {
// Importer constructor.
Importer::Importer()
: pimpl( new ImporterPimpl ) {
pimpl->mScene = NULL;
pimpl->mScene = nullptr;
pimpl->mErrorString = "";
// Allocate a default IO handler
@ -174,14 +173,14 @@ Importer::Importer()
// ------------------------------------------------------------------------------------------------
// Destructor of Importer
Importer::~Importer()
{
Importer::~Importer() {
// Delete all import plugins
DeleteImporterInstanceList(pimpl->mImporter);
// 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 the assigned IO and progress handler
delete pimpl->mIOHandler;
@ -199,9 +198,9 @@ Importer::~Importer()
// ------------------------------------------------------------------------------------------------
// Register a custom post-processing step
aiReturn Importer::RegisterPPStep(BaseProcess* pImp)
{
ai_assert(NULL != pImp);
aiReturn Importer::RegisterPPStep(BaseProcess* pImp) {
ai_assert( nullptr != pImp );
ASSIMP_BEGIN_EXCEPTION_REGION();
pimpl->mPostProcessingSteps.push_back(pImp);
@ -213,9 +212,9 @@ aiReturn Importer::RegisterPPStep(BaseProcess* pImp)
// ------------------------------------------------------------------------------------------------
// Register a custom loader plugin
aiReturn Importer::RegisterLoader(BaseImporter* pImp)
{
ai_assert(NULL != pImp);
aiReturn Importer::RegisterLoader(BaseImporter* pImp) {
ai_assert(nullptr != pImp);
ASSIMP_BEGIN_EXCEPTION_REGION();
// --------------------------------------------------------------------
@ -242,13 +241,13 @@ aiReturn Importer::RegisterLoader(BaseImporter* pImp)
pimpl->mImporter.push_back(pImp);
ASSIMP_LOG_INFO_F("Registering custom importer for these file extensions: ", baked);
ASSIMP_END_EXCEPTION_REGION(aiReturn);
return AI_SUCCESS;
}
// ------------------------------------------------------------------------------------------------
// Unregister a custom loader plugin
aiReturn Importer::UnregisterLoader(BaseImporter* pImp)
{
aiReturn Importer::UnregisterLoader(BaseImporter* pImp) {
if(!pImp) {
// unregistering a NULL importer is no problem for us ... really!
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_END_EXCEPTION_REGION(aiReturn);
return AI_FAILURE;
}
// ------------------------------------------------------------------------------------------------
// Unregister a custom loader plugin
aiReturn Importer::UnregisterPPStep(BaseProcess* pImp)
{
aiReturn Importer::UnregisterPPStep(BaseProcess* pImp) {
if(!pImp) {
// unregistering a NULL ppstep is no problem for us ... really!
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_END_EXCEPTION_REGION(aiReturn);
return AI_FAILURE;
}
// ------------------------------------------------------------------------------------------------
// 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();
// If the new handler is zero, allocate a default IO implementation.
if (!pIOHandler)
{
if (!pIOHandler) {
// Release pointer in the possession of the caller
pimpl->mIOHandler = new DefaultIOSystem();
pimpl->mIsDefaultHandler = true;
}
// Otherwise register the custom handler
else if (pimpl->mIOHandler != pIOHandler)
{
} else if (pimpl->mIOHandler != pIOHandler) { // Otherwise register the custom handler
delete pimpl->mIOHandler;
pimpl->mIOHandler = pIOHandler;
pimpl->mIsDefaultHandler = false;
@ -316,29 +313,32 @@ void Importer::SetIOHandler( IOSystem* pIOHandler)
// ------------------------------------------------------------------------------------------------
// Get the currently set IO handler
IOSystem* Importer::GetIOHandler() const {
ai_assert(nullptr != pimpl);
return pimpl->mIOHandler;
}
// ------------------------------------------------------------------------------------------------
// Check whether a custom IO handler is currently set
bool Importer::IsDefaultIOHandler() const {
ai_assert(nullptr != pimpl);
return pimpl->mIsDefaultHandler;
}
// ------------------------------------------------------------------------------------------------
// Supplies a custom progress handler to get regular callbacks during importing
void Importer::SetProgressHandler ( ProgressHandler* pHandler ) {
ai_assert(nullptr != pimpl);
ASSIMP_BEGIN_EXCEPTION_REGION();
// If the new handler is zero, allocate a default implementation.
if (!pHandler)
{
if (!pHandler) {
// Release pointer in the possession of the caller
pimpl->mProgressHandler = new DefaultProgressHandler();
pimpl->mIsDefaultProgressHandler = true;
}
// Otherwise register the custom handler
else if (pimpl->mProgressHandler != pHandler)
{
} else if (pimpl->mProgressHandler != pHandler) { // Otherwise register the custom handler
delete pimpl->mProgressHandler;
pimpl->mProgressHandler = pHandler;
pimpl->mIsDefaultProgressHandler = false;
@ -349,19 +349,22 @@ void Importer::SetProgressHandler ( ProgressHandler* pHandler ) {
// ------------------------------------------------------------------------------------------------
// Get the currently set progress handler
ProgressHandler* Importer::GetProgressHandler() const {
ai_assert(nullptr != pimpl);
return pimpl->mProgressHandler;
}
// ------------------------------------------------------------------------------------------------
// Check whether a custom progress handler is currently set
bool Importer::IsDefaultProgressHandler() const {
ai_assert(nullptr != pimpl);
return pimpl->mIsDefaultProgressHandler;
}
// ------------------------------------------------------------------------------------------------
// Validate post process step flags
bool _ValidateFlags(unsigned int pFlags)
{
bool _ValidateFlags(unsigned int pFlags) {
if (pFlags & aiProcess_GenSmoothNormals && pFlags & aiProcess_GenNormals) {
ASSIMP_LOG_ERROR("#aiProcess_GenSmoothNormals and #aiProcess_GenNormals are incompatible");
return false;
@ -375,12 +378,13 @@ bool _ValidateFlags(unsigned int pFlags)
// ------------------------------------------------------------------------------------------------
// Free the current scene
void Importer::FreeScene( )
{
void Importer::FreeScene( ) {
ai_assert(nullptr != pimpl);
ASSIMP_BEGIN_EXCEPTION_REGION();
delete pimpl->mScene;
pimpl->mScene = NULL;
pimpl->mScene = nullptr;
pimpl->mErrorString = "";
ASSIMP_END_EXCEPTION_REGION(void);
@ -388,44 +392,48 @@ void Importer::FreeScene( )
// ------------------------------------------------------------------------------------------------
// Get the current error string, if any
const char* Importer::GetErrorString() const
{
/* Must remain valid as long as ReadFile() or FreeFile() are not called */
const char* Importer::GetErrorString() const {
ai_assert(nullptr != pimpl);
// Must remain valid as long as ReadFile() or FreeFile() are not called
return pimpl->mErrorString.c_str();
}
// ------------------------------------------------------------------------------------------------
// Enable extra-verbose mode
void Importer::SetExtraVerbose(bool bDo)
{
void Importer::SetExtraVerbose(bool bDo) {
ai_assert(nullptr != pimpl);
pimpl->bExtraVerbose = bDo;
}
// ------------------------------------------------------------------------------------------------
// Get the current scene
const aiScene* Importer::GetScene() const
{
const aiScene* Importer::GetScene() const {
ai_assert(nullptr != pimpl);
return pimpl->mScene;
}
// ------------------------------------------------------------------------------------------------
// Orphan the current scene and return it.
aiScene* Importer::GetOrphanedScene()
{
aiScene* Importer::GetOrphanedScene() {
ai_assert(nullptr != pimpl);
aiScene* s = pimpl->mScene;
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*);
return s;
}
// ------------------------------------------------------------------------------------------------
// Validate post-processing flags
bool Importer::ValidateFlags(unsigned int pFlags) const
{
bool Importer::ValidateFlags(unsigned int pFlags) const {
ASSIMP_BEGIN_EXCEPTION_REGION();
// run basic checks for mutually exclusive flags
if(!_ValidateFlags(pFlags)) {
@ -467,8 +475,9 @@ bool Importer::ValidateFlags(unsigned int pFlags) const
const aiScene* Importer::ReadFileFromMemory( const void* pBuffer,
size_t pLength,
unsigned int pFlags,
const char* pHint /*= ""*/)
{
const char* pHint /*= ""*/) {
ai_assert(nullptr != pimpl);
ASSIMP_BEGIN_EXCEPTION_REGION();
if (!pHint) {
pHint = "";
@ -476,12 +485,12 @@ const aiScene* Importer::ReadFileFromMemory( const void* pBuffer,
if (!pBuffer || !pLength || strlen(pHint) > MaxLenHint ) {
pimpl->mErrorString = "Invalid parameters passed to ReadFileFromMemory()";
return NULL;
return nullptr;
}
// prevent deletion of the previous IOHandler
IOSystem* io = pimpl->mIOHandler;
pimpl->mIOHandler = NULL;
pimpl->mIOHandler = nullptr;
SetIOHandler(new MemoryIOSystem((const uint8_t*)pBuffer,pLength,io));
@ -493,13 +502,13 @@ const aiScene* Importer::ReadFileFromMemory( const void* pBuffer,
ReadFile(fbuff,pFlags);
SetIOHandler(io);
ASSIMP_END_EXCEPTION_REGION(const aiScene*);
ASSIMP_END_EXCEPTION_REGION_WITH_ERROR_STRING(const aiScene*, pimpl->mErrorString);
return pimpl->mScene;
}
// ------------------------------------------------------------------------------------------------
void WriteLogOpening(const std::string& file)
{
void WriteLogOpening(const std::string& file) {
ASSIMP_LOG_INFO_F("Load ", file);
// 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.
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();
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 + "\".";
ASSIMP_LOG_ERROR(pimpl->mErrorString);
return NULL;
return nullptr;
}
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
BaseImporter* imp = NULL;
BaseImporter* imp = nullptr;
SetPropertyInteger("importerIndex", -1);
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) {
pimpl->mErrorString = "No suitable reader found for the file format of file \"" + pFile + "\".";
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
const aiImporterDesc *desc( imp->GetInfo() );
std::string ext( "unknown" );
if ( NULL != desc ) {
if ( nullptr != desc ) {
ext = desc->mName;
}
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( 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
// 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;
ds.ExecuteOnScene (this);
if (!pimpl->mScene) {
return NULL;
return nullptr;
}
}
#endif // no validation
@ -695,8 +710,7 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
}
}
#ifdef ASSIMP_CATCH_GLOBAL_EXCEPTIONS
catch (std::exception &e)
{
catch (std::exception &e) {
#if (defined _MSC_VER) && (defined _CPPRTTI)
// if we have RTTI get the full name of the exception that occurred
pimpl->mErrorString = std::string(typeid( e ).name()) + ": " + e.what();
@ -705,24 +719,26 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
#endif
ASSIMP_LOG_ERROR(pimpl->mErrorString);
delete pimpl->mScene; pimpl->mScene = NULL;
delete pimpl->mScene; pimpl->mScene = nullptr;
}
#endif // ! ASSIMP_CATCH_GLOBAL_EXCEPTIONS
// 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;
}
// ------------------------------------------------------------------------------------------------
// 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();
// Return immediately if no scene is active
if (!pimpl->mScene) {
return NULL;
return nullptr;
}
// 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
// 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.
if (pFlags & aiProcess_ValidateDataStructure)
{
if (pFlags & aiProcess_ValidateDataStructure) {
ValidateDSProcess ds;
ds.ExecuteOnScene (this);
if (!pimpl->mScene) {
return NULL;
return nullptr;
}
}
#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);
for( unsigned int a = 0; a < pimpl->mPostProcessingSteps.size(); a++) {
BaseProcess* process = pimpl->mPostProcessingSteps[a];
pimpl->mProgressHandler->UpdatePostProcess(static_cast<int>(a), static_cast<int>(pimpl->mPostProcessingSteps.size()) );
if( process->IsActive( pFlags)) {
if (profiler) {
profiler->BeginRegion("postprocess");
}
@ -803,24 +816,28 @@ const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags)
static_cast<int>(pimpl->mPostProcessingSteps.size()) );
// update private scene flags
if( pimpl->mScene )
if( pimpl->mScene ) {
ScenePriv(pimpl->mScene)->mPPStepsApplied |= pFlags;
}
// clear any data allocated by post-process steps
pimpl->mPPShared->Clean();
ASSIMP_LOG_INFO("Leaving post processing pipeline");
ASSIMP_END_EXCEPTION_REGION(const aiScene*);
return pimpl->mScene;
}
// ------------------------------------------------------------------------------------------------
const aiScene* Importer::ApplyCustomizedPostProcessing( BaseProcess *rootProcess, bool requestValidation ) {
ai_assert(nullptr != pimpl);
ASSIMP_BEGIN_EXCEPTION_REGION();
// Return immediately if no scene is active
if ( NULL == pimpl->mScene ) {
return NULL;
if ( nullptr == pimpl->mScene ) {
return nullptr;
}
// 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;
ds.ExecuteOnScene( this );
if ( !pimpl->mScene ) {
return NULL;
return nullptr;
}
}
#endif // no validation
@ -890,46 +907,50 @@ const aiScene* Importer::ApplyCustomizedPostProcessing( BaseProcess *rootProcess
// ------------------------------------------------------------------------------------------------
// 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);
}
// ------------------------------------------------------------------------------------------------
size_t Importer::GetImporterCount() const
{
size_t Importer::GetImporterCount() const {
ai_assert(nullptr != pimpl);
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()) {
return NULL;
return nullptr;
}
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()) {
return NULL;
return nullptr;
}
return pimpl->mImporter[index];
}
// ------------------------------------------------------------------------------------------------
// 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));
}
// ------------------------------------------------------------------------------------------------
// Find a loader plugin for a given file extension
size_t Importer::GetImporterIndex (const char* szExtension) const {
ai_assert(nullptr != pimpl);
ai_assert(nullptr != szExtension);
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
void Importer::GetExtensionList(aiString& szOut) const
{
void Importer::GetExtensionList(aiString& szOut) const {
ai_assert(nullptr != pimpl);
ASSIMP_BEGIN_EXCEPTION_REGION();
std::set<std::string> str;
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
bool Importer::SetPropertyInteger(const char* szName, int iValue)
{
bool Importer::SetPropertyInteger(const char* szName, int iValue) {
ai_assert(nullptr != pimpl);
bool existing;
ASSIMP_BEGIN_EXCEPTION_REGION();
existing = SetGenericProperty<int>(pimpl->mIntProperties, szName,iValue);
@ -996,8 +1019,9 @@ bool Importer::SetPropertyInteger(const char* szName, int iValue)
// ------------------------------------------------------------------------------------------------
// 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;
ASSIMP_BEGIN_EXCEPTION_REGION();
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
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;
ASSIMP_BEGIN_EXCEPTION_REGION();
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
bool Importer::SetPropertyMatrix(const char* szName, const aiMatrix4x4& value)
{
bool Importer::SetPropertyMatrix(const char* szName, const aiMatrix4x4& value) {
ai_assert(nullptr != pimpl);
bool existing;
ASSIMP_BEGIN_EXCEPTION_REGION();
existing = SetGenericProperty<aiMatrix4x4>(pimpl->mMatrixProperties, szName,value);
@ -1029,40 +1055,43 @@ bool Importer::SetPropertyMatrix(const char* szName, const aiMatrix4x4& value)
// ------------------------------------------------------------------------------------------------
// Get a configuration property
int Importer::GetPropertyInteger(const char* szName,
int iErrorReturn /*= 0xffffffff*/) const
{
int Importer::GetPropertyInteger(const char* szName, int iErrorReturn /*= 0xffffffff*/) const {
ai_assert(nullptr != pimpl);
return GetGenericProperty<int>(pimpl->mIntProperties,szName,iErrorReturn);
}
// ------------------------------------------------------------------------------------------------
// Get a configuration property
ai_real Importer::GetPropertyFloat(const char* szName,
ai_real iErrorReturn /*= 10e10*/) const
{
ai_real Importer::GetPropertyFloat(const char* szName, ai_real iErrorReturn /*= 10e10*/) const {
ai_assert(nullptr != pimpl);
return GetGenericProperty<ai_real>(pimpl->mFloatProperties,szName,iErrorReturn);
}
// ------------------------------------------------------------------------------------------------
// Get a configuration property
const std::string Importer::GetPropertyString(const char* szName,
const std::string& iErrorReturn /*= ""*/) const
{
const std::string Importer::GetPropertyString(const char* szName, const std::string& iErrorReturn /*= ""*/) const {
ai_assert(nullptr != pimpl);
return GetGenericProperty<std::string>(pimpl->mStringProperties,szName,iErrorReturn);
}
// ------------------------------------------------------------------------------------------------
// Get a configuration property
const aiMatrix4x4 Importer::GetPropertyMatrix(const char* szName,
const aiMatrix4x4& iErrorReturn /*= aiMatrix4x4()*/) const
{
const aiMatrix4x4 Importer::GetPropertyMatrix(const char* szName, const aiMatrix4x4& iErrorReturn /*= aiMatrix4x4()*/) const {
ai_assert(nullptr != pimpl);
return GetGenericProperty<aiMatrix4x4>(pimpl->mMatrixProperties,szName,iErrorReturn);
}
// ------------------------------------------------------------------------------------------------
// 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(unsigned int) * pcNode->mNumMeshes;
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
void Importer::GetMemoryRequirements(aiMemoryInfo& in) const
{
void Importer::GetMemoryRequirements(aiMemoryInfo& in) const {
ai_assert(nullptr != pimpl);
in = aiMemoryInfo();
aiScene* mScene = pimpl->mScene;
@ -1087,8 +1117,7 @@ void Importer::GetMemoryRequirements(aiMemoryInfo& in) const
in.total = sizeof(aiScene);
// 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);
if (mScene->mMeshes[i]->HasPositions()) {
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) {
if (mScene->mMeshes[i]->HasVertexColors(a)) {
in.meshes += sizeof(aiColor4D) * mScene->mMeshes[i]->mNumVertices;
} else {
break;
}
else break;
}
for (unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS;++a) {
if (mScene->mMeshes[i]->HasTextureCoords(a)) {
in.meshes += sizeof(aiVector3D) * mScene->mMeshes[i]->mNumVertices;
} else {
break;
}
else break;
}
if (mScene->mMeshes[i]->HasBones()) {
in.meshes += sizeof(void*) * mScene->mMeshes[i]->mNumBones;
@ -1131,8 +1162,9 @@ void Importer::GetMemoryRequirements(aiMemoryInfo& in) const
in.textures += sizeof(aiTexture);
if (pc->mHeight) {
in.textures += 4 * pc->mHeight * pc->mWidth;
} else {
in.textures += pc->mWidth;
}
else in.textures += pc->mWidth;
}
in.total += in.textures;

View File

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

View File

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

View File

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

View File

@ -46,8 +46,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/scene.h>
#include "ScenePrivate.h"
static const unsigned int MajorVersion = 4;
static const unsigned int MinorVersion = 1;
#include "revision.h"
// --------------------------------------------------------------------------------
// Legal information string - don't remove this.
@ -56,9 +55,9 @@ static const char* LEGAL_INFORMATION =
"Open Asset Import Library (Assimp).\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"
"http://assimp.sourceforge.net\n"
"http://assimp.org\n"
;
// ------------------------------------------------------------------------------------------------
@ -67,16 +66,22 @@ ASSIMP_API const char* aiGetLegalString () {
return LEGAL_INFORMATION;
}
// ------------------------------------------------------------------------------------------------
// Get Assimp patch version
ASSIMP_API unsigned int aiGetVersionPatch() {
return VER_PATCH;
}
// ------------------------------------------------------------------------------------------------
// Get Assimp minor version
ASSIMP_API unsigned int aiGetVersionMinor () {
return MinorVersion;
return VER_MINOR;
}
// ------------------------------------------------------------------------------------------------
// Get Assimp major version
ASSIMP_API unsigned int aiGetVersionMajor () {
return MajorVersion;
return VER_MAJOR;
}
// ------------------------------------------------------------------------------------------------
@ -104,9 +109,6 @@ ASSIMP_API unsigned int aiGetCompileFlags () {
return flags;
}
// include current build revision, which is even updated from time to time -- :-)
#include "revision.h"
// ------------------------------------------------------------------------------------------------
ASSIMP_API unsigned int aiGetVersionRevision() {
return GitVersion;

View File

@ -44,23 +44,23 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
aiNode::aiNode()
: mName("")
, mParent(NULL)
, mParent(nullptr)
, mNumChildren(0)
, mChildren(NULL)
, mChildren(nullptr)
, mNumMeshes(0)
, mMeshes(NULL)
, mMetaData(NULL) {
, mMeshes(nullptr)
, mMetaData(nullptr) {
// empty
}
aiNode::aiNode(const std::string& name)
: mName(name)
, mParent(NULL)
, mParent(nullptr)
, mNumChildren(0)
, mChildren(NULL)
, mChildren(nullptr)
, mNumMeshes(0)
, mMeshes(NULL)
, mMetaData(NULL) {
, mMeshes(nullptr)
, mMetaData(nullptr) {
// empty
}
@ -68,7 +68,7 @@ aiNode::aiNode(const std::string& name)
aiNode::~aiNode() {
// delete all children recursively
// 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++)
delete mChildren[a];

View File

@ -50,9 +50,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace Assimp {
namespace FBX
{
const std::string NULL_RECORD = { // 13 null bytes
'\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0','\0'
}; // who knows why
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'
}; // 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 MAGIC_NODE_TAG = "_$AssimpFbx$"; // from import
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
#include <map>
#include <set>
//
#if _MSC_VER > 1500 || (defined __GNUC___)
@ -54,16 +55,23 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# else
# define fbx_unordered_map map
# define fbx_unordered_multimap multimap
# define fbx_unordered_set set
# define fbx_unordered_multiset multiset
#endif
#ifdef ASSIMP_FBX_USE_UNORDERED_MULTIMAP
# include <unordered_map>
# include <unordered_set>
# if _MSC_VER > 1600
# define fbx_unordered_map unordered_map
# define fbx_unordered_multimap unordered_multimap
# define fbx_unordered_set unordered_set
# define fbx_unordered_multiset unordered_multiset
# else
# define fbx_unordered_map tr1::unordered_map
# define fbx_unordered_multimap tr1::unordered_multimap
# define fbx_unordered_set tr1::unordered_set
# define fbx_unordered_multiset tr1::unordered_multiset
# 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/CreateAnimMesh.h>
#include <assimp/commonMetaData.h>
#include <assimp/StringUtils.h>
#include <tuple>
#include <memory>
@ -68,7 +70,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <sstream>
#include <iomanip>
#include <cstdint>
#include <iostream>
#include <stdlib.h>
namespace Assimp {
namespace FBX {
@ -77,7 +80,7 @@ namespace Assimp {
#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 )
: defaultMaterialIndex()
@ -96,6 +99,14 @@ namespace Assimp {
// populate the node_anim_chain_bits map, which is needed
// to determine which nodes need to be generated.
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();
if (doc.Settings().readAllMaterials) {
@ -145,7 +156,7 @@ namespace Assimp {
out->mRootNode->mName.Set(unique_name);
// root has ID 0
ConvertNodes(0L, *out->mRootNode);
ConvertNodes(0L, out->mRootNode, out->mRootNode);
}
static std::string getAncestorBaseName(const aiNode* node)
@ -179,8 +190,11 @@ namespace Assimp {
GetUniqueName(original_name, unique_name);
return unique_name;
}
void FBXConverter::ConvertNodes(uint64_t id, aiNode& parent, const aiMatrix4x4& parent_transform) {
/// todo: pre-build node hierarchy
/// 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");
std::vector<aiNode*> nodes;
@ -191,62 +205,69 @@ namespace Assimp {
try {
for (const Connection* con : conns) {
// ignore object-property links
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();
if (nullptr == object) {
FBXImporter::LogWarn("failed to convert source object for Model link");
FBXImporter::LogError("failed to convert source object for Model link");
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);
if (nullptr != model) {
nodes_chain.clear();
post_nodes_chain.clear();
aiMatrix4x4 new_abs_transform = parent_transform;
std::string unique_name = MakeUniqueNodeName(model, parent);
aiMatrix4x4 new_abs_transform = parent->mTransformation;
std::string node_name = FixNodeName(model->Name());
// even though there is only a single input node, the design of
// assimp (or rather: the complicated transformation chain that
// is employed by fbx) means that we may need multiple aiNode's
// 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());
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
SetupNodeMetadata(*model, *nodes_chain.back());
// link all nodes in a row
aiNode* last_parent = &parent;
for (aiNode* prenode : nodes_chain) {
ai_assert(prenode);
aiNode* last_parent = parent;
for (aiNode* child : nodes_chain) {
ai_assert(child);
if (last_parent != &parent) {
if (last_parent != parent) {
last_parent->mNumChildren = 1;
last_parent->mChildren = new aiNode*[1];
last_parent->mChildren[0] = prenode;
last_parent->mChildren[0] = child;
}
prenode->mParent = last_parent;
last_parent = prenode;
child->mParent = last_parent;
last_parent = child;
new_abs_transform *= prenode->mTransformation;
new_abs_transform *= child->mTransformation;
}
// 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
const std::vector<const Connection*>& child_conns
@ -258,7 +279,7 @@ namespace Assimp {
for (aiNode* postnode : post_nodes_chain) {
ai_assert(postnode);
if (last_parent != &parent) {
if (last_parent != parent) {
last_parent->mNumChildren = 1;
last_parent->mChildren = new aiNode*[1];
last_parent->mChildren[0] = postnode;
@ -280,15 +301,15 @@ namespace Assimp {
);
}
// attach sub-nodes (if any)
ConvertNodes(model->ID(), *last_parent, new_abs_transform);
// recursion call - child nodes
ConvertNodes(model->ID(), last_parent, root_node);
if (doc.Settings().readLights) {
ConvertLights(*model, unique_name);
ConvertLights(*model, node_name);
}
if (doc.Settings().readCameras) {
ConvertCameras(*model, unique_name);
ConvertCameras(*model, node_name);
}
nodes.push_back(nodes_chain.front());
@ -297,11 +318,17 @@ namespace Assimp {
}
if (nodes.size()) {
parent.mChildren = new aiNode*[nodes.size()]();
parent.mNumChildren = static_cast<unsigned int>(nodes.size());
parent->mChildren = new aiNode*[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&) {
Util::delete_fun<aiNode> deleter;
@ -803,7 +830,7 @@ namespace Assimp {
// is_complex needs to be consistent with NeedsComplexTransformationChain()
// or the interplay between this code and the animation converter would
// 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,
// 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();
@ -917,11 +945,12 @@ namespace Assimp {
const MeshGeometry* const mesh = dynamic_cast<const MeshGeometry*>(geo);
const LineGeometry* const line = dynamic_cast<const LineGeometry*>(geo);
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));
}
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));
}
else {
@ -930,15 +959,16 @@ namespace Assimp {
}
if (meshes.size()) {
nd.mMeshes = new unsigned int[meshes.size()]();
nd.mNumMeshes = static_cast<unsigned int>(meshes.size());
parent->mMeshes = new 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,
const aiMatrix4x4& node_global_transform, aiNode& nd)
std::vector<unsigned int>
FBXConverter::ConvertMesh(const MeshGeometry &mesh, const Model &model, aiNode *parent, aiNode *root_node,
const aiMatrix4x4 &absolute_transform)
{
std::vector<unsigned int> temp;
@ -962,18 +992,18 @@ namespace Assimp {
const MatIndexArray::value_type base = mindices[0];
for (MatIndexArray::value_type index : mindices) {
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
temp.push_back(ConvertMeshSingleMaterial(mesh, model, node_global_transform, nd));
temp.push_back(ConvertMeshSingleMaterial(mesh, model, absolute_transform, parent, root_node));
return temp;
}
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;
@ -984,7 +1014,7 @@ namespace Assimp {
return temp;
}
aiMesh* const out_mesh = SetupEmptyMesh(line, nd);
aiMesh* const out_mesh = SetupEmptyMesh(line, root_node);
out_mesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
// copy vertices
@ -1019,7 +1049,7 @@ namespace Assimp {
return temp;
}
aiMesh* FBXConverter::SetupEmptyMesh(const Geometry& mesh, aiNode& nd)
aiMesh* FBXConverter::SetupEmptyMesh(const Geometry& mesh, aiNode *parent)
{
aiMesh* const out_mesh = new aiMesh();
meshes.push_back(out_mesh);
@ -1036,17 +1066,18 @@ namespace Assimp {
}
else
{
out_mesh->mName = nd.mName;
out_mesh->mName = parent->mName;
}
return out_mesh;
}
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();
aiMesh* const out_mesh = SetupEmptyMesh(mesh, nd);
aiMesh* const out_mesh = SetupEmptyMesh(mesh, parent);
const std::vector<aiVector3D>& vertices = mesh.GetVertices();
const std::vector<unsigned int>& faces = mesh.GetFaceIndexCounts();
@ -1113,7 +1144,7 @@ namespace Assimp {
binormals = &tempBinormals;
}
else {
binormals = NULL;
binormals = nullptr;
}
}
@ -1163,8 +1194,9 @@ namespace Assimp {
ConvertMaterialForMesh(out_mesh, model, mesh, mindices[0]);
}
if (doc.Settings().readWeights && mesh.DeformerSkin() != NULL) {
ConvertWeights(out_mesh, model, mesh, node_global_transform, NO_MATERIAL_SEPARATION);
if (doc.Settings().readWeights && mesh.DeformerSkin() != nullptr) {
ConvertWeights(out_mesh, model, mesh, absolute_transform, parent, root_node, NO_MATERIAL_SEPARATION,
nullptr);
}
std::vector<aiAnimMesh*> animMeshes;
@ -1209,8 +1241,10 @@ namespace Assimp {
return static_cast<unsigned int>(meshes.size() - 1);
}
std::vector<unsigned int> FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model,
const aiMatrix4x4& node_global_transform, aiNode& nd)
std::vector<unsigned int>
FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, aiNode *parent,
aiNode *root_node,
const aiMatrix4x4 &absolute_transform)
{
const MatIndexArray& mindices = mesh.GetMaterialIndices();
ai_assert(mindices.size());
@ -1221,7 +1255,7 @@ namespace Assimp {
for (MatIndexArray::value_type index : mindices) {
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);
}
}
@ -1231,16 +1265,16 @@ namespace Assimp {
unsigned int FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model,
MatIndexArray::value_type index,
const aiMatrix4x4& node_global_transform,
aiNode& nd)
aiNode *parent, aiNode *root_node,
const aiMatrix4x4 &absolute_transform)
{
aiMesh* const out_mesh = SetupEmptyMesh(mesh, nd);
aiMesh* const out_mesh = SetupEmptyMesh(mesh, parent);
const MatIndexArray& mindices = mesh.GetMaterialIndices();
const std::vector<aiVector3D>& vertices = mesh.GetVertices();
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_vertices = 0;
@ -1300,7 +1334,7 @@ namespace Assimp {
binormals = &tempBinormals;
}
else {
binormals = NULL;
binormals = nullptr;
}
}
@ -1399,7 +1433,7 @@ namespace Assimp {
ConvertMaterialForMesh(out_mesh, model, mesh, index);
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;
@ -1450,8 +1484,8 @@ namespace Assimp {
}
void FBXConverter::ConvertWeights(aiMesh *out, const Model &model, const MeshGeometry &geo,
const aiMatrix4x4& node_global_transform,
unsigned int materialIndex,
const aiMatrix4x4 &absolute_transform,
aiNode *parent, aiNode *root_node, unsigned int materialIndex,
std::vector<unsigned int> *outputVertStartIndices)
{
ai_assert(geo.DeformerSkin());
@ -1463,13 +1497,12 @@ namespace Assimp {
const Skin& sk = *geo.DeformerSkin();
std::vector<aiBone*> bones;
bones.reserve(sk.Clusters().size());
const bool no_mat_check = materialIndex == NO_MATERIAL_SEPARATION;
ai_assert(no_mat_check || outputVertStartIndices);
try {
// iterate over the sub deformers
for (const Cluster* cluster : sk.Clusters()) {
ai_assert(cluster);
@ -1483,15 +1516,16 @@ namespace Assimp {
index_out_indices.clear();
out_indices.clear();
// now check if *any* of these weights is contained in the output mesh,
// taking notes so we don't need to do it twice.
for (WeightIndexArray::value_type index : indices) {
unsigned int count = 0;
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
ai_assert(out_idx != NULL);
ai_assert(out_idx != nullptr);
index_out_indices.push_back(no_index_sentinel);
count_out_indices.push_back(0);
@ -1524,47 +1558,74 @@ namespace Assimp {
// if we found at least one, generate the output bones
// XXX this could be heavily simplified by collecting the bone
// data in a single step.
ConvertCluster(bones, model, *cluster, out_indices, index_out_indices,
count_out_indices, node_global_transform);
ConvertCluster(bones, cluster, out_indices, index_out_indices,
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>());
throw;
}
if (bones.empty()) {
out->mBones = nullptr;
out->mNumBones = 0;
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);
}
}
void FBXConverter::ConvertCluster(std::vector<aiBone*>& bones, const Model& /*model*/, const Cluster& cl,
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)
const aiNode* FBXConverter::GetNodeByName( const aiString& name, aiNode *current_node )
{
aiNode * iter = current_node;
//printf("Child count: %d", iter->mNumChildren);
return iter;
}
aiBone* const bone = new aiBone();
bones.push_back(bone);
void FBXConverter::ConvertCluster(std::vector<aiBone *> &local_mesh_bones, const Cluster *cl,
std::vector<size_t> &out_indices, std::vector<size_t> &index_out_indices,
std::vector<size_t> &count_out_indices, const aiMatrix4x4 &absolute_transform,
aiNode *parent, aiNode *root_node) {
ai_assert(cl); // make sure cluster valid
std::string deformer_name = cl->TargetNode()->Name();
aiString bone_name = aiString(FixNodeName(deformer_name));
bone->mName = FixNodeName(cl.TargetNode()->Name());
aiBone *bone = nullptr;
bone->mOffsetMatrix = cl.TransformLink();
if (bone_map.count(deformer_name)) {
ASSIMP_LOG_DEBUG_F("retrieved bone from lookup ", bone_name.C_Str(), ". Deformer:", deformer_name);
bone = bone_map[deformer_name];
} else {
ASSIMP_LOG_DEBUG_F("created new bone ", bone_name.C_Str(), ". Deformer: ", deformer_name);
bone = new aiBone();
bone->mName = bone_name;
// store local transform link for post processing
bone->mOffsetMatrix = cl->TransformLink();
bone->mOffsetMatrix.Inverse();
bone->mOffsetMatrix = bone->mOffsetMatrix * node_global_transform;
aiMatrix4x4 matrix = (aiMatrix4x4)absolute_transform;
bone->mOffsetMatrix = bone->mOffsetMatrix * matrix; // * mesh_offset
//
// Now calculate the aiVertexWeights
//
aiVertexWeight *cursor = nullptr;
bone->mNumWeights = static_cast<unsigned int>(out_indices.size());
aiVertexWeight* cursor = bone->mWeights = new aiVertexWeight[out_indices.size()];
cursor = bone->mWeights = new aiVertexWeight[out_indices.size()];
const size_t no_index_sentinel = std::numeric_limits<size_t>::max();
const WeightArray& weights = cl.GetWeights();
const WeightArray& weights = cl->GetWeights();
const size_t c = index_out_indices.size();
for (size_t i = 0; i < c; ++i) {
@ -1576,12 +1637,23 @@ namespace Assimp {
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];
}
}
bone_map.insert(std::pair<const std::string, aiBone *>(deformer_name, bone));
}
ASSIMP_LOG_DEBUG_F("bone research: Indicies size: ", out_indices.size());
// lookup must be populated in case something goes wrong
// this also allocates bones to mesh instance outside
local_mesh_bones.push_back(bone);
}
void FBXConverter::ConvertMaterialForMesh(aiMesh* out, const Model& model, const MeshGeometry& geo,
@ -1711,7 +1783,7 @@ namespace Assimp {
bool textureReady = false; //tells if our texture is ready (if it was loaded or if it was found)
unsigned int index;
VideoMap::const_iterator it = textures_converted.find(media);
VideoMap::const_iterator it = textures_converted.find(*media);
if (it != textures_converted.end()) {
index = (*it).second;
textureReady = true;
@ -1719,7 +1791,7 @@ namespace Assimp {
else {
if (media->ContentLength() > 0) {
index = ConvertVideo(*media);
textures_converted[media] = index;
textures_converted[*media] = index;
textureReady = true;
}
}
@ -2243,13 +2315,13 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa
if (media != nullptr && media->ContentLength() > 0) {
unsigned int index;
VideoMap::const_iterator it = textures_converted.find(media);
VideoMap::const_iterator it = textures_converted.find(*media);
if (it != textures_converted.end()) {
index = (*it).second;
}
else {
index = ConvertVideo(*media);
textures_converted[media] = index;
textures_converted[*media] = index;
}
// setup texture reference string (copied from ColladaLoader::FindFilenameForEffectTexture)
@ -2677,7 +2749,7 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa
// sanity check whether the input is ok
static void validateAnimCurveNodes(const std::vector<const AnimationCurveNode*>& curves,
bool strictMode) {
const Object* target(NULL);
const Object* target(nullptr);
for (const AnimationCurveNode* node : curves) {
if (!target) {
target = node->Target();
@ -2708,7 +2780,7 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa
#ifdef ASSIMP_BUILD_DEBUG
validateAnimCurveNodes(curves, doc.Settings().strictMode);
#endif
const AnimationCurveNode* curve_node = NULL;
const AnimationCurveNode* curve_node = nullptr;
for (const AnimationCurveNode* node : curves) {
ai_assert(node);
@ -3533,7 +3605,9 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa
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(1, "UpAxisSign", doc.GlobalSettings().UpAxisSign());
out->mMetaData->Set(2, "FrontAxis", doc.GlobalSettings().FrontAxis());
@ -3549,6 +3623,11 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa
out->mMetaData->Set(12, "TimeSpanStart", doc.GlobalSettings().TimeSpanStart());
out->mMetaData->Set(13, "TimeSpanStop", doc.GlobalSettings().TimeSpanStop());
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()
@ -3556,7 +3635,7 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa
ai_assert(!out->mMeshes);
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
// confusion why this code works.
@ -3603,6 +3682,47 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa
}
}
void FBXConverter::ConvertOrphantEmbeddedTextures()
{
// in C++14 it could be:
// for (auto&& [id, object] : objects)
for (auto&& id_and_object : doc.Objects())
{
auto&& id = std::get<0>(id_and_object);
auto&& object = std::get<1>(id_and_object);
// If an object doesn't have parent
if (doc.ConnectionsBySource().count(id) == 0)
{
const Texture* realTexture = nullptr;
try
{
const auto& element = object->GetElement();
const Token& key = element.KeyToken();
const char* obtype = key.begin();
const size_t length = static_cast<size_t>(key.end() - key.begin());
if (strncmp(obtype, "Texture", length) == 0)
{
const Texture* texture = static_cast<const Texture*>(object->Get());
if (texture->Media() && texture->Media()->ContentLength() > 0)
{
realTexture = texture;
}
}
}
catch (...)
{
// do nothing
}
if (realTexture)
{
const Video* media = realTexture->Media();
unsigned int index = ConvertVideo(*media);
textures_converted[*media] = index;
}
}
}
}
// ------------------------------------------------------------------------------------------------
void ConvertToAssimpScene(aiScene* out, const Document& doc, bool removeEmptyBones)
{

View File

@ -123,7 +123,7 @@ private:
// ------------------------------------------------------------------------------------------------
// 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 );
@ -179,32 +179,35 @@ private:
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
std::vector<unsigned int> ConvertMesh(const MeshGeometry& mesh, const Model& model,
const aiMatrix4x4& node_global_transform, aiNode& nd);
std::vector<unsigned int>
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,
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,
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,
const aiMatrix4x4& node_global_transform, aiNode& nd);
std::vector<unsigned int>
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,
MatIndexArray::value_type index,
const aiMatrix4x4& node_global_transform, aiNode& nd);
unsigned int ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, MatIndexArray::value_type index,
aiNode *parent, aiNode *root_node, const aiMatrix4x4 &absolute_transform);
// ------------------------------------------------------------------------------------------------
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
* each output vertex the DOM index it maps to.
*/
void ConvertWeights(aiMesh* out, const Model& model, const MeshGeometry& geo,
const aiMatrix4x4& node_global_transform = aiMatrix4x4(),
void ConvertWeights(aiMesh *out, const Model &model, const MeshGeometry &geo, const aiMatrix4x4 &absolute_transform,
aiNode *parent = NULL, aiNode *root_node = NULL,
unsigned int materialIndex = NO_MATERIAL_SEPARATION,
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,
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);
void ConvertCluster(std::vector<aiBone *> &local_mesh_bones, const Cluster *cl,
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);
// ------------------------------------------------------------------------------------------------
void ConvertMaterialForMesh(aiMesh* out, const Model& model, const MeshGeometry& geo,
@ -418,12 +421,18 @@ private:
double& minTime,
Model::RotOrder order);
// ------------------------------------------------------------------------------------------------
// Copy global geometric data and some information about the source asset into scene metadata.
void ConvertGlobalSettings();
// ------------------------------------------------------------------------------------------------
// copy generated meshes, animations, lights, cameras and textures to the output scene
void TransferDataToScene();
// ------------------------------------------------------------------------------------------------
// FBX file could have embedded textures not connected to anything
void ConvertOrphantEmbeddedTextures();
private:
// 0: not assigned yet, others: index is value - 1
unsigned int defaultMaterialIndex;
@ -435,27 +444,47 @@ private:
std::vector<aiCamera*> cameras;
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;
using VideoMap = std::map<const Video*, unsigned int>;
using VideoMap = std::fbx_unordered_map<const Video, unsigned int>;
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;
// 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;
// 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;
// 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;
aiScene* const out;
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;
}
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:
std::string type;
std::string relativeFileName;
@ -1005,10 +1019,10 @@ public:
// during their entire lifetime (Document). FBX files have
// up to many thousands of objects (most of which we never use),
// 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::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
* be accessed via Document.Globals(). */
@ -1177,4 +1191,25 @@ private:
} // Namespace FBX
} // 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

View File

@ -325,9 +325,9 @@ void FBX::Node::BeginBinary(Assimp::StreamWriterLE &s)
this->start_pos = s.Tell();
// placeholders for end pos and property section info
s.PutU4(0); // end pos
s.PutU4(0); // number of properties
s.PutU4(0); // total property section length
s.PutU8(0); // end pos
s.PutU8(0); // number of properties
s.PutU8(0); // total property section length
// 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();
ai_assert(pos > property_start);
size_t property_section_size = pos - property_start;
s.Seek(start_pos + 4);
s.PutU4(uint32_t(num_properties));
s.PutU4(uint32_t(property_section_size));
s.Seek(start_pos + 8); // 8 bytes of uint64_t of end_pos
s.PutU8(num_properties);
s.PutU8(property_section_size);
s.Seek(pos);
}
@ -375,7 +375,7 @@ void FBX::Node::EndBinary(
// now go back and write initial pos
this->end_pos = s.Tell();
s.Seek(start_pos);
s.PutU4(uint32_t(end_pos));
s.PutU8(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
namespace Assimp {
namespace FBX {
const std::string EXPORT_VERSION_STR = "7.4.0";
const uint32_t EXPORT_VERSION_INT = 7400; // 7.4 == 2014/2015
const std::string EXPORT_VERSION_STR = "7.5.0";
const uint32_t EXPORT_VERSION_INT = 7500; // 7.5 == 2016+
// 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.
// what we can do is set them to a known-working version.
@ -1289,7 +1289,7 @@ void FBXExporter::WriteObjects ()
for(unsigned int lr = 1; lr < m->GetNumUVChannels(); ++ lr)
{
FBX::Node layerExtra("Layer", int32_t(1));
FBX::Node layerExtra("Layer", int32_t(lr));
layerExtra.AddChild("Version", int32_t(100));
FBX::Node leExtra("LayerElement");
leExtra.AddChild("Type", "LayerElementUV");
@ -1860,6 +1860,7 @@ void FBXExporter::WriteObjects ()
sdnode.AddChild("Version", int32_t(100));
sdnode.AddChild("UserData", "", "");
std::set<int32_t> setWeightedVertex;
// add indices and weights, if any
if (b) {
std::vector<int32_t> subdef_indices;
@ -1867,7 +1868,8 @@ void FBXExporter::WriteObjects ()
int32_t last_index = -1;
for (size_t wi = 0; wi < b->mNumWeights; ++wi) {
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
// TODO, FIXME: this assumes identically-located vertices
// will always deform in the same way.
@ -1877,6 +1879,7 @@ void FBXExporter::WriteObjects ()
// identical vertex.
continue;
}
setWeightedVertex.insert(vi);
subdef_indices.push_back(vi);
subdef_weights.push_back(b->mWeights[wi].mWeight);
last_index = vi;

View File

@ -48,16 +48,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "FBXImporter.h"
#include "FBXTokenizer.h"
#include "FBXParser.h"
#include "FBXUtil.h"
#include "FBXDocument.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/Importer.hpp>
#include <assimp/StreamReader.h>
#include <assimp/importerdesc.h>
#include <assimp/Importer.hpp>
namespace Assimp {
@ -67,7 +67,7 @@ const char* LogFunctions<FBXImporter>::Prefix() {
return prefix;
}
}
} // namespace Assimp
using namespace Assimp;
using namespace Assimp::Formatter;
@ -91,20 +91,17 @@ static const aiImporterDesc desc = {
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by #Importer
FBXImporter::FBXImporter()
{
FBXImporter::FBXImporter() {
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
FBXImporter::~FBXImporter()
{
FBXImporter::~FBXImporter() {
}
// ------------------------------------------------------------------------------------------------
// 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);
if (extension == std::string(desc.mFileExtensions)) {
return true;
@ -120,15 +117,13 @@ bool FBXImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool
// ------------------------------------------------------------------------------------------------
// List all extensions handled by this loader
const aiImporterDesc* FBXImporter::GetInfo () const
{
const aiImporterDesc *FBXImporter::GetInfo() const {
return &desc;
}
// ------------------------------------------------------------------------------------------------
// 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.readAllMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_MATERIALS, false);
settings.readMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_MATERIALS, true);
@ -146,8 +141,7 @@ void FBXImporter::SetupProperties(const Importer* pImp)
// ------------------------------------------------------------------------------------------------
// Imports the given file into the given scene structure.
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"));
if (!stream) {
ThrowException("Could not open file for reading");
@ -173,8 +167,7 @@ void FBXImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS
if (!strncmp(begin, "Kaydara FBX Binary", 18)) {
is_binary = true;
TokenizeBinary(tokens, begin, contents.size());
}
else {
} else {
Tokenize(tokens, begin);
}
@ -196,8 +189,7 @@ void FBXImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS
SetFileScale(size_relative_to_cm * 0.01f);
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>());
throw;
}

View File

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

View File

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

View File

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

View File

@ -0,0 +1,412 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2019 bzt
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
#ifndef ASSIMP_BUILD_NO_EXPORT
#ifndef ASSIMP_BUILD_NO_M3D_EXPORTER
#define M3D_IMPLEMENTATION
#define M3D_NOIMPORTER
#define M3D_EXPORTER
#define M3D_ASCII
#ifndef ASSIMP_BUILD_NO_M3D_IMPORTER
#define M3D_NODUP
#endif
// Header files, standard library.
#include <memory> // shared_ptr
#include <string>
#include <vector>
#include <assimp/Exceptional.h> // DeadlyExportError
#include <assimp/StreamWriter.h> // StreamWriterLE
#include <assimp/material.h> // aiTextureType
#include <assimp/mesh.h>
#include <assimp/scene.h>
#include <assimp/version.h> // aiGetVersion
#include <assimp/DefaultLogger.hpp>
#include <assimp/Exporter.hpp>
#include <assimp/IOSystem.hpp>
#include "M3DExporter.h"
#include "M3DMaterials.h"
#include "M3DWrapper.h"
// RESOURCES:
// https://gitlab.com/bztsrc/model3d/blob/master/docs/m3d_format.md
// https://gitlab.com/bztsrc/model3d/blob/master/docs/a3d_format.md
/*
* Currently supports static meshes, vertex colors, materials, textures
*
* For animation, it would require the following conversions:
* - aiNode (bones) -> m3d_t.bone (with parent id, position vector and oriantation quaternion)
* - aiMesh.aiBone -> m3d_t.skin (per vertex, with bone id, weight pairs)
* - aiAnimation -> m3d_action (frame with timestamp and list of bone id, position, orientation
* triplets, instead of per bone timestamp + lists)
*/
// ------------------------------------------------------------------------------------------------
// Conversion functions
// ------------------------------------------------------------------------------------------------
// helper to add a vertex (private to NodeWalk)
m3dv_t *AddVrtx(m3dv_t *vrtx, uint32_t *numvrtx, m3dv_t *v, uint32_t *idx) {
if (v->x == (M3D_FLOAT)-0.0) v->x = (M3D_FLOAT)0.0;
if (v->y == (M3D_FLOAT)-0.0) v->y = (M3D_FLOAT)0.0;
if (v->z == (M3D_FLOAT)-0.0) v->z = (M3D_FLOAT)0.0;
if (v->w == (M3D_FLOAT)-0.0) v->w = (M3D_FLOAT)0.0;
vrtx = (m3dv_t *)M3D_REALLOC(vrtx, ((*numvrtx) + 1) * sizeof(m3dv_t));
memcpy(&vrtx[*numvrtx], v, sizeof(m3dv_t));
*idx = *numvrtx;
(*numvrtx)++;
return vrtx;
}
// ------------------------------------------------------------------------------------------------
// helper to add a tmap (private to NodeWalk)
m3dti_t *AddTmap(m3dti_t *tmap, uint32_t *numtmap, m3dti_t *ti, uint32_t *idx) {
tmap = (m3dti_t *)M3D_REALLOC(tmap, ((*numtmap) + 1) * sizeof(m3dti_t));
memcpy(&tmap[*numtmap], ti, sizeof(m3dti_t));
*idx = *numtmap;
(*numtmap)++;
return tmap;
}
// ------------------------------------------------------------------------------------------------
// convert aiColor4D into uint32_t
uint32_t mkColor(aiColor4D *c) {
return ((uint8_t)(c->a * 255) << 24L) |
((uint8_t)(c->b * 255) << 16L) |
((uint8_t)(c->g * 255) << 8L) |
((uint8_t)(c->r * 255) << 0L);
}
// ------------------------------------------------------------------------------------------------
// add a material property to the output
void addProp(m3dm_t *m, uint8_t type, uint32_t value) {
unsigned int i;
i = m->numprop++;
m->prop = (m3dp_t *)M3D_REALLOC(m->prop, m->numprop * sizeof(m3dp_t));
if (!m->prop) {
throw DeadlyExportError("memory allocation error");
}
m->prop[i].type = type;
m->prop[i].value.num = value;
}
// ------------------------------------------------------------------------------------------------
// add a material to the output
M3D_INDEX addMaterial(const Assimp::M3DWrapper &m3d, const aiMaterial *mat) {
unsigned int mi = -1U;
aiColor4D c;
aiString name;
ai_real f;
char *fn;
if (mat && mat->Get(AI_MATKEY_NAME, name) == AI_SUCCESS && name.length &&
strcmp((char *)&name.data, AI_DEFAULT_MATERIAL_NAME)) {
// check if we have saved a material by this name. This has to be done
// because only the referenced materials should be added to the output
for (unsigned int i = 0; i < m3d->nummaterial; i++)
if (!strcmp((char *)&name.data, m3d->material[i].name)) {
mi = i;
break;
}
// if not found, add the material to the output
if (mi == -1U) {
unsigned int k;
mi = m3d->nummaterial++;
m3d->material = (m3dm_t *)M3D_REALLOC(m3d->material, m3d->nummaterial * sizeof(m3dm_t));
if (!m3d->material) {
throw DeadlyExportError("memory allocation error");
}
m3d->material[mi].name = _m3d_safestr((char *)&name.data, 0);
m3d->material[mi].numprop = 0;
m3d->material[mi].prop = NULL;
// iterate through the material property table and see what we got
for (k = 0; k < 15; k++) {
unsigned int j;
if (m3d_propertytypes[k].format == m3dpf_map)
continue;
if (aiProps[k].pKey) {
switch (m3d_propertytypes[k].format) {
case m3dpf_color:
if (mat->Get(aiProps[k].pKey, aiProps[k].type,
aiProps[k].index, c) == AI_SUCCESS)
addProp(&m3d->material[mi],
m3d_propertytypes[k].id, mkColor(&c));
break;
case m3dpf_float:
if (mat->Get(aiProps[k].pKey, aiProps[k].type,
aiProps[k].index, f) == AI_SUCCESS)
addProp(&m3d->material[mi],
m3d_propertytypes[k].id,
/* not (uint32_t)f, because we don't want to convert
* it, we want to see it as 32 bits of memory */
*((uint32_t *)&f));
break;
case m3dpf_uint8:
if (mat->Get(aiProps[k].pKey, aiProps[k].type,
aiProps[k].index, j) == AI_SUCCESS) {
// special conversion for illumination model property
if (m3d_propertytypes[k].id == m3dp_il) {
switch (j) {
case aiShadingMode_NoShading: j = 0; break;
case aiShadingMode_Phong: j = 2; break;
default: j = 1; break;
}
}
addProp(&m3d->material[mi],
m3d_propertytypes[k].id, j);
}
break;
default:
if (mat->Get(aiProps[k].pKey, aiProps[k].type,
aiProps[k].index, j) == AI_SUCCESS)
addProp(&m3d->material[mi],
m3d_propertytypes[k].id, j);
break;
}
}
if (aiTxProps[k].pKey &&
mat->GetTexture((aiTextureType)aiTxProps[k].type,
aiTxProps[k].index, &name, NULL, NULL, NULL,
NULL, NULL) == AI_SUCCESS) {
unsigned int i;
for (j = name.length - 1; j > 0 && name.data[j] != '.'; j++)
;
if (j && name.data[j] == '.' &&
(name.data[j + 1] == 'p' || name.data[j + 1] == 'P') &&
(name.data[j + 1] == 'n' || name.data[j + 1] == 'N') &&
(name.data[j + 1] == 'g' || name.data[j + 1] == 'G'))
name.data[j] = 0;
// do we have this texture saved already?
fn = _m3d_safestr((char *)&name.data, 0);
for (j = 0, i = -1U; j < m3d->numtexture; j++)
if (!strcmp(fn, m3d->texture[j].name)) {
i = j;
free(fn);
break;
}
if (i == -1U) {
i = m3d->numtexture++;
m3d->texture = (m3dtx_t *)M3D_REALLOC(
m3d->texture,
m3d->numtexture * sizeof(m3dtx_t));
if (!m3d->texture) {
throw DeadlyExportError("memory allocation error");
}
// we don't need the texture itself, only its name
m3d->texture[i].name = fn;
m3d->texture[i].w = 0;
m3d->texture[i].h = 0;
m3d->texture[i].d = NULL;
}
addProp(&m3d->material[mi],
m3d_propertytypes[k].id + 128, i);
}
}
}
}
return mi;
}
namespace Assimp {
// ---------------------------------------------------------------------
// Worker function for exporting a scene to binary M3D.
// Prototyped and registered in Exporter.cpp
void ExportSceneM3D(
const char *pFile,
IOSystem *pIOSystem,
const aiScene *pScene,
const ExportProperties *pProperties) {
// initialize the exporter
M3DExporter exporter(pScene, pProperties);
// perform binary export
exporter.doExport(pFile, pIOSystem, false);
}
// ---------------------------------------------------------------------
// Worker function for exporting a scene to ASCII A3D.
// Prototyped and registered in Exporter.cpp
void ExportSceneA3D(
const char *pFile,
IOSystem *pIOSystem,
const aiScene *pScene,
const ExportProperties *pProperties
) {
// initialize the exporter
M3DExporter exporter(pScene, pProperties);
// perform ascii export
exporter.doExport(pFile, pIOSystem, true);
}
// ------------------------------------------------------------------------------------------------
M3DExporter::M3DExporter(const aiScene *pScene, const ExportProperties *pProperties) :
mScene(pScene),
mProperties(pProperties),
outfile() {}
// ------------------------------------------------------------------------------------------------
void M3DExporter::doExport(
const char *pFile,
IOSystem *pIOSystem,
bool toAscii) {
// TODO: convert mProperties into M3D_EXP_* flags
(void)mProperties;
// open the indicated file for writing (in binary / ASCII mode)
outfile.reset(pIOSystem->Open(pFile, toAscii ? "wt" : "wb"));
if (!outfile) {
throw DeadlyExportError("could not open output .m3d file: " + std::string(pFile));
}
M3DWrapper m3d;
if (!m3d) {
throw DeadlyExportError("memory allocation error");
}
m3d->name = _m3d_safestr((char *)&mScene->mRootNode->mName.data, 2);
// Create a model from assimp structures
aiMatrix4x4 m;
NodeWalk(m3d, mScene->mRootNode, m);
// serialize the structures
unsigned int size;
unsigned char *output = m3d.Save(M3D_EXP_FLOAT, M3D_EXP_EXTRA | (toAscii ? M3D_EXP_ASCII : 0), size);
if (!output || size < 8) {
throw DeadlyExportError("unable to serialize into Model 3D");
}
// Write out serialized model
outfile->Write(output, size, 1);
// explicitly release file pointer,
// so we don't have to rely on class destruction.
outfile.reset();
}
// ------------------------------------------------------------------------------------------------
// recursive node walker
void M3DExporter::NodeWalk(const M3DWrapper &m3d, const aiNode *pNode, aiMatrix4x4 m) {
aiMatrix4x4 nm = m * pNode->mTransformation;
for (unsigned int i = 0; i < pNode->mNumMeshes; i++) {
const aiMesh *mesh = mScene->mMeshes[pNode->mMeshes[i]];
unsigned int mi = (M3D_INDEX)-1U;
if (mScene->mMaterials) {
// get the material for this mesh
mi = addMaterial(m3d, mScene->mMaterials[mesh->mMaterialIndex]);
}
// iterate through the mesh faces
for (unsigned int j = 0; j < mesh->mNumFaces; j++) {
unsigned int n;
const aiFace *face = &(mesh->mFaces[j]);
// only triangle meshes supported for now
if (face->mNumIndices != 3) {
throw DeadlyExportError("use aiProcess_Triangulate before export");
}
// add triangle to the output
n = m3d->numface++;
m3d->face = (m3df_t *)M3D_REALLOC(m3d->face,
m3d->numface * sizeof(m3df_t));
if (!m3d->face) {
throw DeadlyExportError("memory allocation error");
}
/* set all index to -1 by default */
m3d->face[n].vertex[0] = m3d->face[n].vertex[1] = m3d->face[n].vertex[2] =
m3d->face[n].normal[0] = m3d->face[n].normal[1] = m3d->face[n].normal[2] =
m3d->face[n].texcoord[0] = m3d->face[n].texcoord[1] = m3d->face[n].texcoord[2] = -1U;
m3d->face[n].materialid = mi;
for (unsigned int k = 0; k < face->mNumIndices; k++) {
// get the vertex's index
unsigned int l = face->mIndices[k];
unsigned int idx;
m3dv_t vertex;
m3dti_t ti;
// multiply the position vector by the transformation matrix
aiVector3D v = mesh->mVertices[l];
v *= nm;
vertex.x = v.x;
vertex.y = v.y;
vertex.z = v.z;
vertex.w = 1.0;
vertex.color = 0;
vertex.skinid = -1U;
// add color if defined
if (mesh->HasVertexColors(0))
vertex.color = mkColor(&mesh->mColors[0][l]);
// save the vertex to the output
m3d->vertex = AddVrtx(m3d->vertex, &m3d->numvertex,
&vertex, &idx);
m3d->face[n].vertex[k] = (M3D_INDEX)idx;
// do we have texture coordinates?
if (mesh->HasTextureCoords(0)) {
ti.u = mesh->mTextureCoords[0][l].x;
ti.v = mesh->mTextureCoords[0][l].y;
m3d->tmap = AddTmap(m3d->tmap, &m3d->numtmap, &ti, &idx);
m3d->face[n].texcoord[k] = (M3D_INDEX)idx;
}
// do we have normal vectors?
if (mesh->HasNormals()) {
vertex.x = mesh->mNormals[l].x;
vertex.y = mesh->mNormals[l].y;
vertex.z = mesh->mNormals[l].z;
vertex.color = 0;
m3d->vertex = AddVrtx(m3d->vertex, &m3d->numvertex, &vertex, &idx);
m3d->face[n].normal[k] = (M3D_INDEX)idx;
}
}
}
}
// repeat for the children nodes
for (unsigned int i = 0; i < pNode->mNumChildren; i++) {
NodeWalk(m3d, pNode->mChildren[i], nm);
}
}
} // namespace Assimp
#endif // ASSIMP_BUILD_NO_M3D_EXPORTER
#endif // ASSIMP_BUILD_NO_EXPORT

View File

@ -0,0 +1,94 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2019 bzt
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** @file M3DExporter.h
* @brief Declares the exporter class to write a scene to a Model 3D file
*/
#ifndef AI_M3DEXPORTER_H_INC
#define AI_M3DEXPORTER_H_INC
#ifndef ASSIMP_BUILD_NO_M3D_EXPORTER
#include <assimp/types.h>
//#include <assimp/material.h>
#include <assimp/StreamWriter.h> // StreamWriterLE
#include <assimp/Exceptional.h> // DeadlyExportError
#include <memory> // shared_ptr
struct aiScene;
struct aiNode;
struct aiMaterial;
struct aiFace;
namespace Assimp
{
class IOSystem;
class IOStream;
class ExportProperties;
class M3DWrapper;
// ---------------------------------------------------------------------
/** Helper class to export a given scene to an M3D file. */
// ---------------------------------------------------------------------
class M3DExporter
{
public:
/// Constructor for a specific scene to export
M3DExporter(const aiScene* pScene, const ExportProperties* pProperties);
// call this to do the actual export
void doExport(const char* pFile, IOSystem* pIOSystem, bool toAscii);
private:
const aiScene* mScene; // the scene to export
const ExportProperties* mProperties; // currently unused
std::shared_ptr<IOStream> outfile; // file to write to
// helper to do the recursive walking
void NodeWalk(const M3DWrapper &m3d, const aiNode* pNode, aiMatrix4x4 m);
};
}
#endif // ASSIMP_BUILD_NO_M3D_EXPORTER
#endif // AI_M3DEXPORTER_H_INC

View File

@ -0,0 +1,742 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2019 bzt
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
#ifndef ASSIMP_BUILD_NO_M3D_IMPORTER
#define M3D_IMPLEMENTATION
#define M3D_ASCII
#define M3D_NONORMALS /* leave the post-processing to Assimp */
#define M3D_NOWEIGHTS
#define M3D_NOANIMATION
#include <assimp/DefaultIOSystem.h>
#include <assimp/IOStreamBuffer.h>
#include <assimp/ai_assert.h>
#include <assimp/importerdesc.h>
#include <assimp/scene.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/Importer.hpp>
#include <memory>
#include "M3DImporter.h"
#include "M3DMaterials.h"
#include "M3DWrapper.h"
// RESOURCES:
// https://gitlab.com/bztsrc/model3d/blob/master/docs/m3d_format.md
// https://gitlab.com/bztsrc/model3d/blob/master/docs/a3d_format.md
/*
Unfortunately aiNode has bone structures and meshes too, yet we can't assign
the mesh to a bone aiNode as a skin may refer to several aiNodes. Therefore
I've decided to import into this structure:
aiScene->mRootNode
| |->mMeshes (all the meshes)
| \->children (empty if there's no skeleton imported, no meshes)
| \->skeleton root aiNode*
| |->bone aiNode
| | \->subbone aiNode
| |->bone aiNode
| | ...
| \->bone aiNode
\->mMeshes[]
\->aiBone, referencing mesh-less aiNodes from above
* - normally one, but if a model has several skeleton roots, then all of them
are listed in aiScene->mRootNode->children, but all without meshes
*/
static const aiImporterDesc desc = {
"Model 3D Importer",
"",
"",
"",
aiImporterFlags_SupportTextFlavour | aiImporterFlags_SupportBinaryFlavour,
0,
0,
0,
0,
"m3d a3d"
};
namespace Assimp {
using namespace std;
// ------------------------------------------------------------------------------------------------
// Default constructor
M3DImporter::M3DImporter() :
mScene(nullptr) {}
// ------------------------------------------------------------------------------------------------
// Returns true, if file is a binary or ASCII Model 3D file.
bool M3DImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const {
const std::string extension = GetExtension(pFile);
if (extension == "m3d" || extension == "a3d")
return true;
else if (!extension.length() || checkSig) {
if (!pIOHandler) {
return true;
}
/*
* don't use CheckMagicToken because that checks with swapped bytes too, leading to false
* positives. This magic is not uint32_t, but char[4], so memcmp is the best way
const char* tokens[] = {"3DMO", "3dmo"};
return CheckMagicToken(pIOHandler,pFile,tokens,2,0,4);
*/
std::unique_ptr<IOStream> pStream(pIOHandler->Open(pFile, "rb"));
unsigned char data[4];
if (4 != pStream->Read(data, 1, 4)) {
return false;
}
return !memcmp(data, "3DMO", 4) /* bin */ || !memcmp(data, "3dmo", 4) /* ASCII */;
}
return false;
}
// ------------------------------------------------------------------------------------------------
const aiImporterDesc *M3DImporter::GetInfo() const {
return &desc;
}
// ------------------------------------------------------------------------------------------------
// Model 3D import implementation
void M3DImporter::InternReadFile(const std::string &file, aiScene *pScene, IOSystem *pIOHandler) {
// Read file into memory
std::unique_ptr<IOStream> pStream(pIOHandler->Open(file, "rb"));
if (!pStream.get()) {
throw DeadlyImportError("Failed to open file " + file + ".");
}
// Get the file-size and validate it, throwing an exception when fails
size_t fileSize = pStream->FileSize();
if (fileSize < 8) {
throw DeadlyImportError("M3D-file " + file + " is too small.");
}
std::vector<unsigned char> buffer(fileSize);
if (fileSize != pStream->Read(buffer.data(), 1, fileSize)) {
throw DeadlyImportError("Failed to read the file " + file + ".");
}
// Get the path for external assets
std::string folderName("./");
std::string::size_type pos = file.find_last_of("\\/");
if (pos != std::string::npos) {
folderName = file.substr(0, pos);
if (!folderName.empty()) {
pIOHandler->PushDirectory(folderName);
}
}
//DefaultLogger::create("/dev/stderr", Logger::VERBOSE);
ASSIMP_LOG_DEBUG_F("M3D: loading ", file);
// let the C SDK do the hard work for us
M3DWrapper m3d(pIOHandler, buffer);
if (!m3d) {
throw DeadlyImportError("Unable to parse " + file + " as M3D.");
}
// create the root node
pScene->mRootNode = new aiNode;
pScene->mRootNode->mName = aiString(m3d.Name());
pScene->mRootNode->mTransformation = aiMatrix4x4();
pScene->mRootNode->mNumChildren = 0;
mScene = pScene;
ASSIMP_LOG_DEBUG("M3D: root node " + m3d.Name());
// now we just have to fill up the Assimp structures in pScene
importMaterials(m3d);
importTextures(m3d);
importBones(m3d, -1U, pScene->mRootNode);
importMeshes(m3d);
importAnimations(m3d);
// Pop directory stack
if (pIOHandler->StackSize() > 0) {
pIOHandler->PopDirectory();
}
}
// ------------------------------------------------------------------------------------------------
// convert materials. properties are converted using a static table in M3DMaterials.h
void M3DImporter::importMaterials(const M3DWrapper &m3d_wrap) {
unsigned int i, j, k, l, n;
m3dm_t *m;
aiString name = aiString(AI_DEFAULT_MATERIAL_NAME);
aiColor4D c;
ai_real f;
ai_assert(mScene != nullptr);
ai_assert(m3d_wrap);
mScene->mNumMaterials = m3d_wrap->nummaterial + 1;
mScene->mMaterials = new aiMaterial *[mScene->mNumMaterials];
ASSIMP_LOG_DEBUG_F("M3D: importMaterials ", mScene->mNumMaterials);
// add a default material as first
aiMaterial *mat = new aiMaterial;
mat->AddProperty(&name, AI_MATKEY_NAME);
c.a = 1.0f;
c.b = c.g = c.r = 0.6f;
mat->AddProperty(&c, 1, AI_MATKEY_COLOR_DIFFUSE);
mScene->mMaterials[0] = mat;
for (i = 0; i < m3d_wrap->nummaterial; i++) {
m = &m3d_wrap->material[i];
aiMaterial *mat = new aiMaterial;
name.Set(std::string(m->name));
mat->AddProperty(&name, AI_MATKEY_NAME);
for (j = 0; j < m->numprop; j++) {
// look up property type
// 0 - 127 scalar values,
// 128 - 255 the same properties but for texture maps
k = 256;
for (l = 0; l < sizeof(m3d_propertytypes) / sizeof(m3d_propertytypes[0]); l++)
if (m->prop[j].type == m3d_propertytypes[l].id ||
m->prop[j].type == m3d_propertytypes[l].id + 128) {
k = l;
break;
}
// should never happen, but be safe than sorry
if (k == 256) continue;
// scalar properties
if (m->prop[j].type < 128 && aiProps[k].pKey) {
switch (m3d_propertytypes[k].format) {
case m3dpf_color:
c = mkColor(m->prop[j].value.color);
mat->AddProperty(&c, 1, aiProps[k].pKey, aiProps[k].type, aiProps[k].index);
break;
case m3dpf_float:
f = m->prop[j].value.fnum;
mat->AddProperty(&f, 1, aiProps[k].pKey, aiProps[k].type, aiProps[k].index);
break;
default:
n = m->prop[j].value.num;
if (m->prop[j].type == m3dp_il) {
switch (n) {
case 0: n = aiShadingMode_NoShading; break;
case 2: n = aiShadingMode_Phong; break;
default: n = aiShadingMode_Gouraud; break;
}
}
mat->AddProperty(&n, 1, aiProps[k].pKey, aiProps[k].type, aiProps[k].index);
break;
}
}
// texture map properties
if (m->prop[j].type >= 128 && aiTxProps[k].pKey &&
// extra check, should never happen, do we have the refered texture?
m->prop[j].value.textureid < m3d_wrap->numtexture &&
m3d_wrap->texture[m->prop[j].value.textureid].name) {
name.Set(std::string(std::string(m3d_wrap->texture[m->prop[j].value.textureid].name) + ".png"));
mat->AddProperty(&name, aiTxProps[k].pKey, aiTxProps[k].type, aiTxProps[k].index);
n = 0;
mat->AddProperty(&n, 1, _AI_MATKEY_UVWSRC_BASE, aiProps[k].type, aiProps[k].index);
}
}
mScene->mMaterials[i + 1] = mat;
}
}
// ------------------------------------------------------------------------------------------------
// import textures, this is the simplest of all
void M3DImporter::importTextures(const M3DWrapper &m3d) {
unsigned int i;
const char *formatHint[] = { "rgba0800", "rgba0808", "rgba8880", "rgba8888" };
m3dtx_t *t;
ai_assert(mScene != nullptr);
ai_assert(m3d);
mScene->mNumTextures = m3d->numtexture;
ASSIMP_LOG_DEBUG_F("M3D: importTextures ", mScene->mNumTextures);
if (!m3d->numtexture)
return;
mScene->mTextures = new aiTexture *[m3d->numtexture];
for (i = 0; i < m3d->numtexture; i++) {
unsigned int j, k;
t = &m3d->texture[i];
if (!t->w || !t->h || !t->f || !t->d) continue;
aiTexture *tx = new aiTexture;
strcpy(tx->achFormatHint, formatHint[t->f - 1]);
tx->mFilename = aiString(std::string(t->name) + ".png");
tx->mWidth = t->w;
tx->mHeight = t->h;
tx->pcData = new aiTexel[tx->mWidth * tx->mHeight];
for (j = k = 0; j < tx->mWidth * tx->mHeight; j++) {
switch (t->f) {
case 1: tx->pcData[j].g = t->d[k++]; break;
case 2:
tx->pcData[j].g = t->d[k++];
tx->pcData[j].a = t->d[k++];
break;
case 3:
tx->pcData[j].r = t->d[k++];
tx->pcData[j].g = t->d[k++];
tx->pcData[j].b = t->d[k++];
tx->pcData[j].a = 255;
break;
case 4:
tx->pcData[j].r = t->d[k++];
tx->pcData[j].g = t->d[k++];
tx->pcData[j].b = t->d[k++];
tx->pcData[j].a = t->d[k++];
break;
}
}
mScene->mTextures[i] = tx;
}
}
// ------------------------------------------------------------------------------------------------
// this is tricky. M3D has a global vertex and UV list, and faces are indexing them
// individually. In assimp there're per mesh vertex and UV lists, and they must be
// indexed simultaneously.
void M3DImporter::importMeshes(const M3DWrapper &m3d) {
unsigned int i, j, k, l, numpoly = 3, lastMat = -2U;
std::vector<aiMesh *> *meshes = new std::vector<aiMesh *>();
std::vector<aiFace> *faces = nullptr;
std::vector<aiVector3D> *vertices = nullptr;
std::vector<aiVector3D> *normals = nullptr;
std::vector<aiVector3D> *texcoords = nullptr;
std::vector<aiColor4D> *colors = nullptr;
std::vector<unsigned int> *vertexids = nullptr;
aiMesh *pMesh = nullptr;
ai_assert(mScene != nullptr);
ai_assert(m3d);
ai_assert(mScene->mRootNode != nullptr);
ASSIMP_LOG_DEBUG_F("M3D: importMeshes ", m3d->numface);
for (i = 0; i < m3d->numface; i++) {
// we must switch mesh if material changes
if (lastMat != m3d->face[i].materialid) {
lastMat = m3d->face[i].materialid;
if (pMesh && vertices && vertices->size() && faces && faces->size()) {
populateMesh(m3d, pMesh, faces, vertices, normals, texcoords, colors, vertexids);
meshes->push_back(pMesh);
delete faces;
delete vertices;
delete normals;
delete texcoords;
delete colors;
delete vertexids; // this is not stored in pMesh, just to collect bone vertices
}
pMesh = new aiMesh;
pMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
pMesh->mMaterialIndex = lastMat + 1;
faces = new std::vector<aiFace>();
vertices = new std::vector<aiVector3D>();
normals = new std::vector<aiVector3D>();
texcoords = new std::vector<aiVector3D>();
colors = new std::vector<aiColor4D>();
vertexids = new std::vector<unsigned int>();
}
// add a face to temporary vector
aiFace *pFace = new aiFace;
pFace->mNumIndices = numpoly;
pFace->mIndices = new unsigned int[numpoly];
for (j = 0; j < numpoly; j++) {
aiVector3D pos, uv, norm;
k = static_cast<unsigned int>(vertices->size());
pFace->mIndices[j] = k;
l = m3d->face[i].vertex[j];
pos.x = m3d->vertex[l].x;
pos.y = m3d->vertex[l].y;
pos.z = m3d->vertex[l].z;
vertices->push_back(pos);
colors->push_back(mkColor(m3d->vertex[l].color));
// add a bone to temporary vector
if (m3d->vertex[l].skinid != -1U && m3d->vertex[l].skinid != -2U && m3d->skin && m3d->bone) {
// this is complicated, because M3D stores a list of bone id / weight pairs per
// vertex but assimp uses lists of local vertex id/weight pairs per local bone list
vertexids->push_back(l);
}
l = m3d->face[i].texcoord[j];
if (l != -1U) {
uv.x = m3d->tmap[l].u;
uv.y = m3d->tmap[l].v;
uv.z = 0.0;
texcoords->push_back(uv);
}
l = m3d->face[i].normal[j];
if (l != -1U) {
norm.x = m3d->vertex[l].x;
norm.y = m3d->vertex[l].y;
norm.z = m3d->vertex[l].z;
normals->push_back(norm);
}
}
faces->push_back(*pFace);
delete pFace;
}
// if there's data left in the temporary vectors, flush them
if (pMesh && vertices->size() && faces->size()) {
populateMesh(m3d, pMesh, faces, vertices, normals, texcoords, colors, vertexids);
meshes->push_back(pMesh);
}
// create global mesh list in scene
mScene->mNumMeshes = static_cast<unsigned int>(meshes->size());
mScene->mMeshes = new aiMesh *[mScene->mNumMeshes];
std::copy(meshes->begin(), meshes->end(), mScene->mMeshes);
// create mesh indeces in root node
mScene->mRootNode->mNumMeshes = static_cast<unsigned int>(meshes->size());
mScene->mRootNode->mMeshes = new unsigned int[meshes->size()];
for (i = 0; i < meshes->size(); i++) {
mScene->mRootNode->mMeshes[i] = i;
}
delete meshes;
if (faces) delete faces;
if (vertices) delete vertices;
if (normals) delete normals;
if (texcoords) delete texcoords;
if (colors) delete colors;
if (vertexids) delete vertexids;
}
// ------------------------------------------------------------------------------------------------
// a reentrant node parser. Otherwise this is simple
void M3DImporter::importBones(const M3DWrapper &m3d, unsigned int parentid, aiNode *pParent) {
unsigned int i, n;
ai_assert(pParent != nullptr);
ai_assert(mScene != nullptr);
ai_assert(m3d);
ASSIMP_LOG_DEBUG_F("M3D: importBones ", m3d->numbone, " parentid ", (int)parentid);
for (n = 0, i = parentid + 1; i < m3d->numbone; i++)
if (m3d->bone[i].parent == parentid) n++;
pParent->mChildren = new aiNode *[n];
for (i = parentid + 1; i < m3d->numbone; i++) {
if (m3d->bone[i].parent == parentid) {
aiNode *pChild = new aiNode;
pChild->mParent = pParent;
pChild->mName = aiString(std::string(m3d->bone[i].name));
convertPose(m3d, &pChild->mTransformation, m3d->bone[i].pos, m3d->bone[i].ori);
pChild->mNumChildren = 0;
pParent->mChildren[pParent->mNumChildren] = pChild;
pParent->mNumChildren++;
importBones(m3d, i, pChild);
}
}
}
// ------------------------------------------------------------------------------------------------
// this is another headache. M3D stores list of changed bone id/position/orientation triplets and
// a timestamp per frame, but assimp needs timestamp and lists of position, orientation lists per
// bone, so we have to convert between the two conceptually different representation forms
void M3DImporter::importAnimations(const M3DWrapper &m3d) {
unsigned int i, j, k, l, pos, ori;
double t;
m3da_t *a;
ai_assert(mScene != nullptr);
ai_assert(m3d);
mScene->mNumAnimations = m3d->numaction;
ASSIMP_LOG_DEBUG_F("M3D: importAnimations ", mScene->mNumAnimations);
if (!m3d->numaction || !m3d->numbone)
return;
mScene->mAnimations = new aiAnimation *[m3d->numaction];
for (i = 0; i < m3d->numaction; i++) {
a = &m3d->action[i];
aiAnimation *pAnim = new aiAnimation;
pAnim->mName = aiString(std::string(a->name));
pAnim->mDuration = ((double)a->durationmsec) / 10;
pAnim->mTicksPerSecond = 100;
// now we know how many bones are referenced in this animation
pAnim->mNumChannels = m3d->numbone;
pAnim->mChannels = new aiNodeAnim *[pAnim->mNumChannels];
for (l = 0; l < m3d->numbone; l++) {
unsigned int n;
pAnim->mChannels[l] = new aiNodeAnim;
pAnim->mChannels[l]->mNodeName = aiString(std::string(m3d->bone[l].name));
// now n is the size of positions / orientations arrays
pAnim->mChannels[l]->mNumPositionKeys = pAnim->mChannels[l]->mNumRotationKeys = a->numframe;
pAnim->mChannels[l]->mPositionKeys = new aiVectorKey[a->numframe];
pAnim->mChannels[l]->mRotationKeys = new aiQuatKey[a->numframe];
pos = m3d->bone[l].pos;
ori = m3d->bone[l].ori;
for (j = n = 0; j < a->numframe; j++) {
t = ((double)a->frame[j].msec) / 10;
for (k = 0; k < a->frame[j].numtransform; k++) {
if (a->frame[j].transform[k].boneid == l) {
pos = a->frame[j].transform[k].pos;
ori = a->frame[j].transform[k].ori;
}
}
m3dv_t *v = &m3d->vertex[pos];
m3dv_t *q = &m3d->vertex[ori];
pAnim->mChannels[l]->mPositionKeys[j].mTime = t;
pAnim->mChannels[l]->mPositionKeys[j].mValue.x = v->x;
pAnim->mChannels[l]->mPositionKeys[j].mValue.y = v->y;
pAnim->mChannels[l]->mPositionKeys[j].mValue.z = v->z;
pAnim->mChannels[l]->mRotationKeys[j].mTime = t;
pAnim->mChannels[l]->mRotationKeys[j].mValue.w = q->w;
pAnim->mChannels[l]->mRotationKeys[j].mValue.x = q->x;
pAnim->mChannels[l]->mRotationKeys[j].mValue.y = q->y;
pAnim->mChannels[l]->mRotationKeys[j].mValue.z = q->z;
} // foreach frame
} // foreach bones
mScene->mAnimations[i] = pAnim;
}
}
// ------------------------------------------------------------------------------------------------
// convert uint32_t into aiColor4D
aiColor4D M3DImporter::mkColor(uint32_t c) {
aiColor4D color;
color.a = ((float)((c >> 24) & 0xff)) / 255;
color.b = ((float)((c >> 16) & 0xff)) / 255;
color.g = ((float)((c >> 8) & 0xff)) / 255;
color.r = ((float)((c >> 0) & 0xff)) / 255;
return color;
}
// ------------------------------------------------------------------------------------------------
// convert a position id and orientation id into a 4 x 4 transformation matrix
void M3DImporter::convertPose(const M3DWrapper &m3d, aiMatrix4x4 *m, unsigned int posid, unsigned int orientid) {
ai_assert(m != nullptr);
ai_assert(m3d);
ai_assert(posid != -1U && posid < m3d->numvertex);
ai_assert(orientid != -1U && orientid < m3d->numvertex);
m3dv_t *p = &m3d->vertex[posid];
m3dv_t *q = &m3d->vertex[orientid];
/* quaternion to matrix. Do NOT use aiQuaternion to aiMatrix3x3, gives bad results */
if (q->x == 0.0 && q->y == 0.0 && q->z >= 0.7071065 && q->z <= 0.7071075 && q->w == 0.0) {
m->a2 = m->a3 = m->b1 = m->b3 = m->c1 = m->c2 = 0.0;
m->a1 = m->b2 = m->c3 = -1.0;
} else {
m->a1 = 1 - 2 * (q->y * q->y + q->z * q->z);
if (m->a1 > -M3D_EPSILON && m->a1 < M3D_EPSILON) m->a1 = 0.0;
m->a2 = 2 * (q->x * q->y - q->z * q->w);
if (m->a2 > -M3D_EPSILON && m->a2 < M3D_EPSILON) m->a2 = 0.0;
m->a3 = 2 * (q->x * q->z + q->y * q->w);
if (m->a3 > -M3D_EPSILON && m->a3 < M3D_EPSILON) m->a3 = 0.0;
m->b1 = 2 * (q->x * q->y + q->z * q->w);
if (m->b1 > -M3D_EPSILON && m->b1 < M3D_EPSILON) m->b1 = 0.0;
m->b2 = 1 - 2 * (q->x * q->x + q->z * q->z);
if (m->b2 > -M3D_EPSILON && m->b2 < M3D_EPSILON) m->b2 = 0.0;
m->b3 = 2 * (q->y * q->z - q->x * q->w);
if (m->b3 > -M3D_EPSILON && m->b3 < M3D_EPSILON) m->b3 = 0.0;
m->c1 = 2 * (q->x * q->z - q->y * q->w);
if (m->c1 > -M3D_EPSILON && m->c1 < M3D_EPSILON) m->c1 = 0.0;
m->c2 = 2 * (q->y * q->z + q->x * q->w);
if (m->c2 > -M3D_EPSILON && m->c2 < M3D_EPSILON) m->c2 = 0.0;
m->c3 = 1 - 2 * (q->x * q->x + q->y * q->y);
if (m->c3 > -M3D_EPSILON && m->c3 < M3D_EPSILON) m->c3 = 0.0;
}
/* set translation */
m->a4 = p->x;
m->b4 = p->y;
m->c4 = p->z;
m->d1 = 0;
m->d2 = 0;
m->d3 = 0;
m->d4 = 1;
}
// ------------------------------------------------------------------------------------------------
// find a node by name
aiNode *M3DImporter::findNode(aiNode *pNode, aiString name) {
unsigned int i;
ai_assert(pNode != nullptr);
ai_assert(mScene != nullptr);
if (pNode->mName == name)
return pNode;
for (i = 0; i < pNode->mNumChildren; i++) {
aiNode *pChild = findNode(pNode->mChildren[i], name);
if (pChild) return pChild;
}
return nullptr;
}
// ------------------------------------------------------------------------------------------------
// fills up offsetmatrix in mBones
void M3DImporter::calculateOffsetMatrix(aiNode *pNode, aiMatrix4x4 *m) {
ai_assert(pNode != nullptr);
ai_assert(mScene != nullptr);
if (pNode->mParent) {
calculateOffsetMatrix(pNode->mParent, m);
*m *= pNode->mTransformation;
} else {
*m = pNode->mTransformation;
}
}
// ------------------------------------------------------------------------------------------------
// because M3D has a global mesh, global vertex ids and stores materialid on the face, we need
// temporary lists to collect data for an aiMesh, which requires local arrays and local indeces
// this function fills up an aiMesh with those temporary lists
void M3DImporter::populateMesh(const M3DWrapper &m3d, aiMesh *pMesh, std::vector<aiFace> *faces, std::vector<aiVector3D> *vertices,
std::vector<aiVector3D> *normals, std::vector<aiVector3D> *texcoords, std::vector<aiColor4D> *colors,
std::vector<unsigned int> *vertexids) {
ai_assert(pMesh != nullptr);
ai_assert(faces != nullptr);
ai_assert(vertices != nullptr);
ai_assert(normals != nullptr);
ai_assert(texcoords != nullptr);
ai_assert(colors != nullptr);
ai_assert(vertexids != nullptr);
ai_assert(m3d);
ASSIMP_LOG_DEBUG_F("M3D: populateMesh numvertices ", vertices->size(), " numfaces ", faces->size(),
" numnormals ", normals->size(), " numtexcoord ", texcoords->size(), " numbones ", m3d->numbone);
if (vertices->size() && faces->size()) {
pMesh->mNumFaces = static_cast<unsigned int>(faces->size());
pMesh->mFaces = new aiFace[pMesh->mNumFaces];
std::copy(faces->begin(), faces->end(), pMesh->mFaces);
pMesh->mNumVertices = static_cast<unsigned int>(vertices->size());
pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
std::copy(vertices->begin(), vertices->end(), pMesh->mVertices);
if (normals->size() == vertices->size()) {
pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
std::copy(normals->begin(), normals->end(), pMesh->mNormals);
}
if (texcoords->size() == vertices->size()) {
pMesh->mTextureCoords[0] = new aiVector3D[pMesh->mNumVertices];
std::copy(texcoords->begin(), texcoords->end(), pMesh->mTextureCoords[0]);
pMesh->mNumUVComponents[0] = 2;
}
if (colors->size() == vertices->size()) {
pMesh->mColors[0] = new aiColor4D[pMesh->mNumVertices];
std::copy(colors->begin(), colors->end(), pMesh->mColors[0]);
}
// this is complicated, because M3D stores a list of bone id / weight pairs per
// vertex but assimp uses lists of local vertex id/weight pairs per local bone list
pMesh->mNumBones = m3d->numbone;
/* we need aiBone with mOffsetMatrix for bones without weights as well */
if (pMesh->mNumBones) {
pMesh->mBones = new aiBone *[pMesh->mNumBones];
for (unsigned int i = 0; i < m3d->numbone; i++) {
aiNode *pNode;
pMesh->mBones[i] = new aiBone;
pMesh->mBones[i]->mName = aiString(std::string(m3d->bone[i].name));
pMesh->mBones[i]->mNumWeights = 0;
pNode = findNode(mScene->mRootNode, pMesh->mBones[i]->mName);
if (pNode) {
calculateOffsetMatrix(pNode, &pMesh->mBones[i]->mOffsetMatrix);
pMesh->mBones[i]->mOffsetMatrix.Inverse();
} else
pMesh->mBones[i]->mOffsetMatrix = aiMatrix4x4();
}
if (vertexids->size()) {
unsigned int i, j;
// first count how many vertices we have per bone
for (i = 0; i < vertexids->size(); i++) {
unsigned int s = m3d->vertex[vertexids->at(i)].skinid;
if (s != -1U && s != -2U) {
for (unsigned int k = 0; k < M3D_NUMBONE && m3d->skin[s].weight[k] > 0.0; k++) {
aiString name = aiString(std::string(m3d->bone[m3d->skin[s].boneid[k]].name));
for (j = 0; j < pMesh->mNumBones; j++) {
if (pMesh->mBones[j]->mName == name) {
pMesh->mBones[j]->mNumWeights++;
break;
}
}
}
}
}
// allocate mWeights
for (j = 0; j < pMesh->mNumBones; j++) {
aiBone *pBone = pMesh->mBones[j];
if (pBone->mNumWeights) {
pBone->mWeights = new aiVertexWeight[pBone->mNumWeights];
pBone->mNumWeights = 0;
}
}
// fill up with data
for (i = 0; i < vertexids->size(); i++) {
unsigned int s = m3d->vertex[vertexids->at(i)].skinid;
if (s != -1U && s != -2U) {
for (unsigned int k = 0; k < M3D_NUMBONE && m3d->skin[s].weight[k] > 0.0; k++) {
aiString name = aiString(std::string(m3d->bone[m3d->skin[s].boneid[k]].name));
for (j = 0; j < pMesh->mNumBones; j++) {
if (pMesh->mBones[j]->mName == name) {
aiBone *pBone = pMesh->mBones[j];
pBone->mWeights[pBone->mNumWeights].mVertexId = i;
pBone->mWeights[pBone->mNumWeights].mWeight = m3d->skin[s].weight[k];
pBone->mNumWeights++;
break;
}
}
} // foreach skin
}
} // foreach vertexids
}
}
}
}
// ------------------------------------------------------------------------------------------------
} // Namespace Assimp
#endif // !! ASSIMP_BUILD_NO_M3D_IMPORTER

View File

@ -0,0 +1,103 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2019 bzt
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** @file M3DImporter.h
* @brief Declares the importer class to read a scene from a Model 3D file
*/
#ifndef AI_M3DIMPORTER_H_INC
#define AI_M3DIMPORTER_H_INC
#ifndef ASSIMP_BUILD_NO_M3D_IMPORTER
#include <assimp/BaseImporter.h>
#include <assimp/material.h>
#include <vector>
struct aiMesh;
struct aiNode;
struct aiMaterial;
struct aiFace;
namespace Assimp {
class M3DWrapper;
class M3DImporter : public BaseImporter {
public:
/// \brief Default constructor
M3DImporter();
public:
/// \brief Returns whether the class can handle the format of the given file.
/// \remark See BaseImporter::CanRead() for details.
bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const;
private:
aiScene *mScene = nullptr; // the scene to import to
//! \brief Appends the supported extension.
const aiImporterDesc *GetInfo() const;
//! \brief File import implementation.
void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler);
void importMaterials(const M3DWrapper &m3d);
void importTextures(const M3DWrapper &m3d);
void importMeshes(const M3DWrapper &m3d);
void importBones(const M3DWrapper &m3d, unsigned int parentid, aiNode *pParent);
void importAnimations(const M3DWrapper &m3d);
// helper functions
aiColor4D mkColor(uint32_t c);
void convertPose(const M3DWrapper &m3d, aiMatrix4x4 *m, unsigned int posid, unsigned int orientid);
aiNode *findNode(aiNode *pNode, aiString name);
void calculateOffsetMatrix(aiNode *pNode, aiMatrix4x4 *m);
void populateMesh(const M3DWrapper &m3d, aiMesh *pMesh, std::vector<aiFace> *faces, std::vector<aiVector3D> *verteces,
std::vector<aiVector3D> *normals, std::vector<aiVector3D> *texcoords, std::vector<aiColor4D> *colors,
std::vector<unsigned int> *vertexids);
};
} // Namespace Assimp
#endif // ASSIMP_BUILD_NO_M3D_IMPORTER
#endif // AI_M3DIMPORTER_H_INC

View File

@ -0,0 +1,106 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2019 bzt
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** @file M3DMaterials.h
* @brief Declares the Assimp and Model 3D file material type relations
*/
#ifndef AI_M3DMATERIALS_H_INC
#define AI_M3DMATERIALS_H_INC
/*
* In the m3d.h header, there's a static array which defines the material
* properties, called m3d_propertytypes. These must have the same size, and
* list the matching Assimp materials for those properties. Used by both the
* M3DImporter and the M3DExporter, so you have to define these relations
* only once. D.R.Y. and K.I.S.S.
*/
typedef struct {
const char *pKey;
unsigned int type;
unsigned int index;
} aiMatProp;
/* --- Scalar Properties --- !!!!! must match m3d_propertytypes !!!!! */
static const aiMatProp aiProps[] = {
{ AI_MATKEY_COLOR_DIFFUSE }, /* m3dp_Kd */
{ AI_MATKEY_COLOR_AMBIENT }, /* m3dp_Ka */
{ AI_MATKEY_COLOR_SPECULAR }, /* m3dp_Ks */
{ AI_MATKEY_SHININESS }, /* m3dp_Ns */
{ AI_MATKEY_COLOR_EMISSIVE }, /* m3dp_Ke */
{ AI_MATKEY_COLOR_REFLECTIVE }, /* m3dp_Tf */
{ AI_MATKEY_BUMPSCALING }, /* m3dp_Km */
{ AI_MATKEY_OPACITY }, /* m3dp_d */
{ AI_MATKEY_SHADING_MODEL }, /* m3dp_il */
{ NULL, 0, 0 }, /* m3dp_Pr */
{ AI_MATKEY_REFLECTIVITY }, /* m3dp_Pm */
{ NULL, 0, 0 }, /* m3dp_Ps */
{ AI_MATKEY_REFRACTI }, /* m3dp_Ni */
{ NULL, 0, 0 }, /* m3dp_Nt */
{ NULL, 0, 0 },
{ NULL, 0, 0 },
{ NULL, 0, 0 }
};
/* --- Texture Map Properties --- !!!!! must match m3d_propertytypes !!!!! */
static const aiMatProp aiTxProps[] = {
{ AI_MATKEY_TEXTURE_DIFFUSE(0) }, /* m3dp_map_Kd */
{ AI_MATKEY_TEXTURE_AMBIENT(0) }, /* m3dp_map_Ka */
{ AI_MATKEY_TEXTURE_SPECULAR(0) }, /* m3dp_map_Ks */
{ AI_MATKEY_TEXTURE_SHININESS(0) }, /* m3dp_map_Ns */
{ AI_MATKEY_TEXTURE_EMISSIVE(0) }, /* m3dp_map_Ke */
{ NULL, 0, 0 }, /* m3dp_map_Tf */
{ AI_MATKEY_TEXTURE_HEIGHT(0) }, /* m3dp_bump */
{ AI_MATKEY_TEXTURE_OPACITY(0) }, /* m3dp_map_d */
{ AI_MATKEY_TEXTURE_REFLECTION(0) }, /* m3dp_refl */
{ AI_MATKEY_TEXTURE(aiTextureType_DIFFUSE_ROUGHNESS,0) },/* m3dp_map_Pr */
{ AI_MATKEY_TEXTURE(aiTextureType_METALNESS,0) }, /* m3dp_map_Pm */
{ NULL, 0, 0 }, /* m3dp_map_Ps */
{ AI_MATKEY_TEXTURE(aiTextureType_AMBIENT_OCCLUSION,0) },/* m3dp_map_Ni */
{ NULL, 0, 0 }, /* m3dp_map_Nt */
{ NULL, 0, 0 },
{ NULL, 0, 0 },
{ NULL, 0, 0 }
};
#endif // AI_M3DMATERIALS_H_INC

View File

@ -0,0 +1,142 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2019 bzt
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
#if !(ASSIMP_BUILD_NO_EXPORT || ASSIMP_BUILD_NO_M3D_EXPORTER) || !ASSIMP_BUILD_NO_M3D_IMPORTER
#include "M3DWrapper.h"
#include <assimp/DefaultIOSystem.h>
#include <assimp/IOStreamBuffer.h>
#include <assimp/ai_assert.h>
#ifndef AI_M3D_USE_STDMUTEX
#if (__cplusplus >= 201103L) || (_MSC_VER >= 1900) // C++11 and MSVC 2015 onwards
#define AI_M3D_USE_STDMUTEX 1
#else
#define AI_M3D_USE_STDMUTEX 0
#endif
#endif
#if AI_M3D_USE_STDMUTEX
#include <mutex>
std::mutex file_mutex;
#endif
// workaround: the M3D SDK expects a C callback, but we want to use Assimp::IOSystem to implement that
// This makes it non-rentrant so lock a mutex (requires C++11)
extern "C" {
void *m3dimporter_pIOHandler;
unsigned char *m3dimporter_readfile(char *fn, unsigned int *size) {
ai_assert(nullptr != fn);
ai_assert(nullptr != size);
std::string file(fn);
std::unique_ptr<Assimp::IOStream> pStream(
(reinterpret_cast<Assimp::IOSystem *>(m3dimporter_pIOHandler))->Open(file, "rb"));
size_t fileSize = 0;
unsigned char *data = NULL;
// sometimes pStream is nullptr for some reason (should be an empty object returning nothing I guess)
if (pStream) {
fileSize = pStream->FileSize();
// should be allocated with malloc(), because the library will call free() to deallocate
data = (unsigned char *)malloc(fileSize);
if (!data || !pStream.get() || !fileSize || fileSize != pStream->Read(data, 1, fileSize)) {
pStream.reset();
*size = 0;
// don't throw a deadly exception, it's not fatal if we can't read an external asset
return nullptr;
}
pStream.reset();
}
*size = (int)fileSize;
return data;
}
}
namespace Assimp {
M3DWrapper::M3DWrapper() {
// use malloc() here because m3d_free() will call free()
m3d_ = (m3d_t *)calloc(1, sizeof(m3d_t));
}
M3DWrapper::M3DWrapper(IOSystem *pIOHandler, const std::vector<unsigned char> &buffer) {
#if AI_M3D_USE_STDMUTEX
// M3D is NOT thread-safe, so lock the global mutex
const std::lock_guard<std::mutex> lock(file_mutex);
#endif
// pass this IOHandler to the C callback
m3dimporter_pIOHandler = pIOHandler;
m3d_ = m3d_load(const_cast<unsigned char *>(buffer.data()), m3dimporter_readfile, free, nullptr);
// Clear the C callback
m3dimporter_pIOHandler = nullptr;
}
M3DWrapper::~M3DWrapper() {
reset();
}
void M3DWrapper::reset() {
ClearSave();
if (m3d_)
m3d_free(m3d_);
m3d_ = nullptr;
}
unsigned char *M3DWrapper::Save(int quality, int flags, unsigned int &size) {
#if (!(ASSIMP_BUILD_NO_EXPORT || ASSIMP_BUILD_NO_M3D_EXPORTER))
ClearSave();
saved_output_ = m3d_save(m3d_, quality, flags, &size);
return saved_output_;
#else
return nullptr;
#endif
}
void M3DWrapper::ClearSave() {
if (saved_output_)
M3D_FREE(saved_output_);
saved_output_ = nullptr;
}
} // namespace Assimp
#endif

View File

@ -0,0 +1,97 @@
#pragma once
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2019 bzt
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** @file M3DWrapper.h
* @brief Declares a class to wrap the C m3d SDK
*/
#ifndef AI_M3DWRAPPER_H_INC
#define AI_M3DWRAPPER_H_INC
#if !(ASSIMP_BUILD_NO_EXPORT || ASSIMP_BUILD_NO_M3D_EXPORTER) || !ASSIMP_BUILD_NO_M3D_IMPORTER
#include <memory>
#include <vector>
#include <string>
#include "m3d.h"
namespace Assimp {
class IOSystem;
class M3DWrapper {
m3d_t *m3d_ = nullptr;
unsigned char *saved_output_ = nullptr;
public:
// Construct an empty M3D model
explicit M3DWrapper();
// Construct an M3D model from provided buffer
// NOTE: The m3d.h SDK function does not mark the data as const. Have assumed it does not write.
// BUG: SECURITY: The m3d.h SDK cannot be informed of the buffer size. BUFFER OVERFLOW IS CERTAIN
explicit M3DWrapper(IOSystem *pIOHandler, const std::vector<unsigned char> &buffer);
~M3DWrapper();
void reset();
// Name
inline std::string Name() const {
if (m3d_) return std::string(m3d_->name);
return std::string();
}
// Execute a save
unsigned char *Save(int quality, int flags, unsigned int &size);
void ClearSave();
inline explicit operator bool() const { return m3d_ != nullptr; }
// Allow direct access to M3D API
inline m3d_t *operator->() const { return m3d_; }
inline m3d_t *M3D() const { return m3d_; }
};
} // namespace Assimp
#endif
#endif // AI_M3DWRAPPER_H_INC

5599
code/M3D/m3d.h 100644

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -51,7 +51,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/types.h>
#include <assimp/material.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/Macros.h>
using namespace Assimp;
@ -274,14 +273,14 @@ aiReturn aiGetMaterialColor(const aiMaterial* pMat,
}
// ------------------------------------------------------------------------------------------------
// Get a aiUVTransform (4 floats) from the material
// Get a aiUVTransform (5 floats) from the material
aiReturn aiGetMaterialUVTransform(const aiMaterial* pMat,
const char* pKey,
unsigned int type,
unsigned int index,
aiUVTransform* pOut)
{
unsigned int iMax = 4;
unsigned int iMax = 5;
return aiGetMaterialFloatArray(pMat,pKey,type,index,(ai_real*)pOut,&iMax);
}
@ -472,12 +471,12 @@ aiReturn aiMaterial::AddBinaryProperty (const void* pInput,
aiPropertyTypeInfo pType
)
{
ai_assert( pInput != NULL );
ai_assert( pKey != NULL );
ai_assert( pInput != nullptr );
ai_assert(pKey != nullptr );
ai_assert( 0 != pSizeInBytes );
if ( 0 == pSizeInBytes ) {
return AI_FAILURE;
}
// first search the list whether there is already an entry with this key

View File

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

View File

@ -0,0 +1,268 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
#include "ArmaturePopulate.h"
#include <assimp/BaseImporter.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/postprocess.h>
#include <assimp/scene.h>
#include <iostream>
namespace Assimp {
/// The default class constructor.
ArmaturePopulate::ArmaturePopulate() : BaseProcess()
{}
/// The class destructor.
ArmaturePopulate::~ArmaturePopulate()
{}
bool ArmaturePopulate::IsActive(unsigned int pFlags) const {
return (pFlags & aiProcess_PopulateArmatureData) != 0;
}
void ArmaturePopulate::SetupProperties(const Importer *pImp) {
// do nothing
}
void ArmaturePopulate::Execute(aiScene *out) {
// Now convert all bone positions to the correct mOffsetMatrix
std::vector<aiBone *> bones;
std::vector<aiNode *> nodes;
std::map<aiBone *, aiNode *> bone_stack;
BuildBoneList(out->mRootNode, out->mRootNode, out, bones);
BuildNodeList(out->mRootNode, nodes);
BuildBoneStack(out->mRootNode, out->mRootNode, out, bones, bone_stack, nodes);
ASSIMP_LOG_DEBUG_F("Bone stack size: ", bone_stack.size());
for (std::pair<aiBone *, aiNode *> kvp : bone_stack) {
aiBone *bone = kvp.first;
aiNode *bone_node = kvp.second;
ASSIMP_LOG_DEBUG_F("active node lookup: ", bone->mName.C_Str());
// lcl transform grab - done in generate_nodes :)
// bone->mOffsetMatrix = bone_node->mTransformation;
aiNode *armature = GetArmatureRoot(bone_node, bones);
ai_assert(armature);
// set up bone armature id
bone->mArmature = armature;
// set this bone node to be referenced properly
ai_assert(bone_node);
bone->mNode = bone_node;
}
}
/* Reprocess all nodes to calculate bone transforms properly based on the REAL
* mOffsetMatrix not the local. */
/* Before this would use mesh transforms which is wrong for bone transforms */
/* Before this would work for simple character skeletons but not complex meshes
* with multiple origins */
/* Source: sketch fab log cutter fbx */
void ArmaturePopulate::BuildBoneList(aiNode *current_node,
const aiNode *root_node,
const aiScene *scene,
std::vector<aiBone *> &bones) {
ai_assert(scene);
for (unsigned int nodeId = 0; nodeId < current_node->mNumChildren; ++nodeId) {
aiNode *child = current_node->mChildren[nodeId];
ai_assert(child);
// check for bones
for (unsigned int meshId = 0; meshId < child->mNumMeshes; ++meshId) {
ai_assert(child->mMeshes);
unsigned int mesh_index = child->mMeshes[meshId];
aiMesh *mesh = scene->mMeshes[mesh_index];
ai_assert(mesh);
for (unsigned int boneId = 0; boneId < mesh->mNumBones; ++boneId) {
aiBone *bone = mesh->mBones[boneId];
ai_assert(bone);
// duplicate meshes exist with the same bones sometimes :)
// so this must be detected
if (std::find(bones.begin(), bones.end(), bone) == bones.end()) {
// add the element once
bones.push_back(bone);
}
}
// find mesh and get bones
// then do recursive lookup for bones in root node hierarchy
}
BuildBoneList(child, root_node, scene, bones);
}
}
/* Prepare flat node list which can be used for non recursive lookups later */
void ArmaturePopulate::BuildNodeList(const aiNode *current_node,
std::vector<aiNode *> &nodes) {
ai_assert(current_node);
for (unsigned int nodeId = 0; nodeId < current_node->mNumChildren; ++nodeId) {
aiNode *child = current_node->mChildren[nodeId];
ai_assert(child);
nodes.push_back(child);
BuildNodeList(child, nodes);
}
}
/* A bone stack allows us to have multiple armatures, with the same bone names
* A bone stack allows us also to retrieve bones true transform even with
* duplicate names :)
*/
void ArmaturePopulate::BuildBoneStack(aiNode *current_node,
const aiNode *root_node,
const aiScene *scene,
const std::vector<aiBone *> &bones,
std::map<aiBone *, aiNode *> &bone_stack,
std::vector<aiNode *> &node_stack) {
ai_assert(scene);
ai_assert(root_node);
ai_assert(!node_stack.empty());
for (aiBone *bone : bones) {
ai_assert(bone);
aiNode *node = GetNodeFromStack(bone->mName, node_stack);
if (node == nullptr) {
node_stack.clear();
BuildNodeList(root_node, node_stack);
ASSIMP_LOG_DEBUG_F("Resetting bone stack: nullptr element ", bone->mName.C_Str());
node = GetNodeFromStack(bone->mName, node_stack);
if (!node) {
ASSIMP_LOG_ERROR("serious import issue node for bone was not detected");
continue;
}
}
ASSIMP_LOG_DEBUG_F("Successfully added bone[", bone->mName.C_Str(), "] to stack and bone node is: ", node->mName.C_Str());
bone_stack.insert(std::pair<aiBone *, aiNode *>(bone, node));
}
}
/* Returns the armature root node */
/* This is required to be detected for a bone initially, it will recurse up
* until it cannot find another bone and return the node No known failure
* points. (yet)
*/
aiNode *ArmaturePopulate::GetArmatureRoot(aiNode *bone_node,
std::vector<aiBone *> &bone_list) {
while (bone_node) {
if (!IsBoneNode(bone_node->mName, bone_list)) {
ASSIMP_LOG_DEBUG_F("GetArmatureRoot() Found valid armature: ", bone_node->mName.C_Str());
return bone_node;
}
bone_node = bone_node->mParent;
}
ASSIMP_LOG_ERROR("GetArmatureRoot() can't find armature!");
return nullptr;
}
/* Simple IsBoneNode check if this could be a bone */
bool ArmaturePopulate::IsBoneNode(const aiString &bone_name,
std::vector<aiBone *> &bones) {
for (aiBone *bone : bones) {
if (bone->mName == bone_name) {
return true;
}
}
return false;
}
/* Pop this node by name from the stack if found */
/* Used in multiple armature situations with duplicate node / bone names */
/* Known flaw: cannot have nodes with bone names, will be fixed in later release
*/
/* (serious to be fixed) Known flaw: nodes which have more than one bone could
* be prematurely dropped from stack */
aiNode *ArmaturePopulate::GetNodeFromStack(const aiString &node_name,
std::vector<aiNode *> &nodes) {
std::vector<aiNode *>::iterator iter;
aiNode *found = nullptr;
for (iter = nodes.begin(); iter < nodes.end(); ++iter) {
aiNode *element = *iter;
ai_assert(element);
// node valid and node name matches
if (element->mName == node_name) {
found = element;
break;
}
}
if (found != nullptr) {
ASSIMP_LOG_INFO_F("Removed node from stack: ", found->mName.C_Str());
// now pop the element from the node list
nodes.erase(iter);
return found;
}
// unique names can cause this problem
ASSIMP_LOG_ERROR("[Serious] GetNodeFromStack() can't find node from stack!");
return nullptr;
}
} // Namespace Assimp

View File

@ -0,0 +1,112 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
#ifndef ARMATURE_POPULATE_H_
#define ARMATURE_POPULATE_H_
#include "Common/BaseProcess.h"
#include <assimp/BaseImporter.h>
#include <vector>
#include <map>
struct aiNode;
struct aiBone;
namespace Assimp {
// ---------------------------------------------------------------------------
/** Armature Populate: This is a post process designed
* To save you time when importing models into your game engines
* This was originally designed only for fbx but will work with other formats
* it is intended to auto populate aiBone data with armature and the aiNode
* This is very useful when dealing with skinned meshes
* or when dealing with many different skeletons
* It's off by default but recommend that you try it and use it
* It should reduce down any glue code you have in your
* importers
* You can contact RevoluPowered <gordon@gordonite.tech>
* For more info about this
*/
class ASSIMP_API ArmaturePopulate : public BaseProcess {
public:
/// The default class constructor.
ArmaturePopulate();
/// The class destructor.
virtual ~ArmaturePopulate();
/// Overwritten, @see BaseProcess
virtual bool IsActive( unsigned int pFlags ) const;
/// Overwritten, @see BaseProcess
virtual void SetupProperties( const Importer* pImp );
/// Overwritten, @see BaseProcess
virtual void Execute( aiScene* pScene );
static aiNode *GetArmatureRoot(aiNode *bone_node,
std::vector<aiBone *> &bone_list);
static bool IsBoneNode(const aiString &bone_name,
std::vector<aiBone *> &bones);
static aiNode *GetNodeFromStack(const aiString &node_name,
std::vector<aiNode *> &nodes);
static void BuildNodeList(const aiNode *current_node,
std::vector<aiNode *> &nodes);
static void BuildBoneList(aiNode *current_node, const aiNode *root_node,
const aiScene *scene,
std::vector<aiBone *> &bones);
static void BuildBoneStack(aiNode *current_node, const aiNode *root_node,
const aiScene *scene,
const std::vector<aiBone *> &bones,
std::map<aiBone *, aiNode *> &bone_stack,
std::vector<aiNode *> &node_stack);
};
} // Namespace Assimp
#endif // SCALE_PROCESS_H_

View File

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

View File

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

View File

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

View File

@ -362,8 +362,8 @@ namespace glTF
ComponentType componentType; //!< The datatype of components in the attribute. (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)
std::vector<float> max; //!< Maximum value of each component in this attribute.
std::vector<float> min; //!< Minimum value of each component in this attribute.
std::vector<double> max; //!< Maximum value of each component in this attribute.
std::vector<double> min; //!< Minimum value of each component in this attribute.
unsigned int GetNumComponents();
unsigned int GetBytesPerComponent();

View File

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

View File

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

View File

@ -228,18 +228,15 @@ namespace glTFCommon {
inline
uint8_t DecodeCharBase64(char c) {
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);
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.
#include <memory>
#include <limits>
#include <inttypes.h>
#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;
}
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,
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;
// calculate min and max values
{
// 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;
}
}
}
}
SetAccessorRange(compType, acc, data, count, numCompsIn, numCompsOut);
// copy the data
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/DefaultLogger.hpp>
#include <assimp/importerdesc.h>
#include <assimp/commonMetaData.h>
#include <memory>
@ -170,6 +171,8 @@ void glTFImporter::ImportMaterials(glTF::Asset& r) {
if (mScene->mNumMaterials == 0) {
mScene->mNumMaterials = 1;
// Delete the array of length zero created above.
delete[] mScene->mMaterials;
mScene->mMaterials = new aiMaterial*[1];
mScene->mMaterials[0] = new aiMaterial();
}
@ -330,6 +333,10 @@ void glTFImporter::ImportMeshes(glTF::Asset& r)
case PrimitiveMode_LINES: {
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];
for (unsigned int i = 0; i < count; i += 2) {
SetFace(faces[i / 2], data.GetUInt(i), data.GetUInt(i + 1));
@ -353,6 +360,10 @@ void glTFImporter::ImportMeshes(glTF::Asset& r)
case PrimitiveMode_TRIANGLES: {
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];
for (unsigned int i = 0; i < count; i += 3) {
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: {
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];
for (unsigned int i = 0; i < count; i += 2) {
SetFace(faces[i / 2], i, i + 1);
@ -418,6 +433,10 @@ void glTFImporter::ImportMeshes(glTF::Asset& r)
case PrimitiveMode_TRIANGLES: {
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];
for (unsigned int i = 0; i < count; i += 3) {
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)
{
// clean all member arrays
@ -705,7 +743,7 @@ void glTFImporter::InternReadFile(const std::string& pFile, aiScene* pScene, IOS
ImportLights(asset);
ImportNodes(asset);
ImportCommonMetadata(asset);
if (pScene->mNumMeshes == 0) {
pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;

View File

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

View File

@ -387,8 +387,8 @@ namespace glTF2
ComponentType componentType; //!< The datatype of components in the attribute. (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)
std::vector<float> max; //!< Maximum value of each component in this attribute.
std::vector<float> min; //!< Minimum value of each component in this attribute.
std::vector<double> max; //!< Maximum value of each component in this attribute.
std::vector<double> min; //!< Minimum value of each component in this attribute.
unsigned int GetNumComponents();
unsigned int GetBytesPerComponent();
@ -685,6 +685,13 @@ namespace glTF2
Ref<Texture> texture;
unsigned int index;
unsigned int texCoord = 0;
bool textureTransformSupported = false;
struct TextureTransformExt {
float offset[2];
float rotation;
float scale[2];
} TextureTransformExt_t;
};
struct NormalTextureInfo : TextureInfo
@ -1024,9 +1031,15 @@ namespace glTF2
bool KHR_materials_pbrSpecularGlossiness;
bool KHR_materials_unlit;
bool KHR_lights_punctual;
bool KHR_texture_transform;
} extensionsUsed;
//! Keeps info about the required extensions
struct RequiredExtensions
{
bool KHR_draco_mesh_compression;
} extensionsRequired;
AssetMetadata asset;
@ -1069,6 +1082,7 @@ namespace glTF2
, textures (*this, "textures")
{
memset(&extensionsUsed, 0, sizeof(extensionsUsed));
memset(&extensionsRequired, 0, sizeof(extensionsRequired));
}
//! Main function
@ -1087,6 +1101,7 @@ namespace glTF2
void ReadBinaryHeader(IOStream& stream, std::vector<char>& sceneData);
void ReadExtensionsUsed(Document& doc);
void ReadExtensionsRequired(Document& doc);
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");
}
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->oIndex = i;
ReadMember(obj, "name", inst->name);
inst->Read(obj, mAsset);
return Add(inst);
return Add(inst.release());
}
template<class T>
@ -800,8 +801,34 @@ inline void Texture::Read(Value& obj, Asset& r)
}
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")) {
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");
}
}
@ -1403,6 +1433,12 @@ inline void Asset::Load(const std::string& pFile, bool isBinary)
// Load the metadata
asset.Read(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
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)
{
@ -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_unlit);
CHECK_EXT(KHR_lights_punctual);
CHECK_EXT(KHR_texture_transform);
#undef CHECK_EXT
}

View File

@ -54,8 +54,8 @@ namespace glTF2 {
namespace {
template<size_t N>
inline Value& MakeValue(Value& val, float(&r)[N], MemoryPoolAllocator<>& al) {
template<typename T, size_t N>
inline Value& MakeValue(Value& val, T(&r)[N], MemoryPoolAllocator<>& al) {
val.SetArray();
val.Reserve(N, al);
for (decltype(N) i = 0; i < N; ++i) {
@ -64,7 +64,8 @@ namespace glTF2 {
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.Reserve(static_cast<rapidjson::SizeType>(r.size()), al);
for (unsigned int i = 0; i < r.size(); ++i) {
@ -73,8 +74,19 @@ namespace glTF2 {
return val;
}
inline Value& MakeValue(Value& val, float r, MemoryPoolAllocator<>& /*al*/) {
val.SetDouble(r);
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<typename T>
inline Value& MakeValue(Value& val, T r, MemoryPoolAllocator<>& /*al*/) {
val.Set(r);
return val;
}
@ -104,8 +116,13 @@ namespace glTF2 {
obj.AddMember("type", StringRef(AttribType::ToString(a.type)), w.mAl);
Value vTmpMax, vTmpMin;
if (a.componentType == ComponentType_FLOAT) {
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)
@ -358,7 +375,7 @@ namespace glTF2 {
WriteVec(pbrSpecularGlossiness, pbrSG.specularFactor, "specularFactor", defaultSpecularFactor, w.mAl);
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);

View File

@ -58,6 +58,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Header files, standard library.
#include <memory>
#include <limits>
#include <inttypes.h>
using namespace rapidjson;
@ -152,6 +153,62 @@ static void IdentityMatrix4(mat4& o) {
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,
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;
// calculate min and max values
{
// 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;
}
}
}
}
SetAccessorRange(compType, acc, data, count, numCompsIn, numCompsOut);
// copy the data
acc->WriteData(count, data, numCompsIn*bytesPerComp);
@ -320,7 +351,9 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref<Texture>& texture, aiTe
if (path[0] == '*') { // embedded
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);
if (tex->achFormatHint[0]) {

View File

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

View File

@ -43,18 +43,19 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef ASSIMP_BUILD_NO_GLTF_IMPORTER
#include "glTF2/glTF2Importer.h"
#include "PostProcessing/MakeVerboseFormat.h"
#include "glTF2/glTF2Asset.h"
#include "glTF2/glTF2AssetWriter.h"
#include "PostProcessing/MakeVerboseFormat.h"
#include <assimp/CreateAnimMesh.h>
#include <assimp/StringComparison.h>
#include <assimp/StringUtils.h>
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/ai_assert.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/importerdesc.h>
#include <assimp/CreateAnimMesh.h>
#include <assimp/scene.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/Importer.hpp>
#include <assimp/commonMetaData.h>
#include <memory>
#include <unordered_map>
@ -91,11 +92,11 @@ static const aiImporterDesc desc = {
"gltf glb"
};
glTF2Importer::glTF2Importer()
: BaseImporter()
, meshOffsets()
, embeddedTexIdxs()
, mScene( NULL ) {
glTF2Importer::glTF2Importer() :
BaseImporter(),
meshOffsets(),
embeddedTexIdxs(),
mScene(NULL) {
// empty
}
@ -103,13 +104,11 @@ glTF2Importer::~glTF2Importer() {
// empty
}
const aiImporterDesc* glTF2Importer::GetInfo() const
{
const aiImporterDesc *glTF2Importer::GetInfo() const {
return &desc;
}
bool glTF2Importer::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool /* checkSig */) const
{
bool glTF2Importer::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /* checkSig */) const {
const std::string &extension = GetExtension(pFile);
if (extension != "gltf" && extension != "glb")
@ -125,8 +124,7 @@ bool glTF2Importer::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool
return false;
}
static aiTextureMapMode ConvertWrappingMode(SamplerWrap gltfWrapMode)
{
static aiTextureMapMode ConvertWrappingMode(SamplerWrap gltfWrapMode) {
switch (gltfWrapMode) {
case SamplerWrap::Mirrored_Repeat:
return aiTextureMapMode_Mirror;
@ -180,22 +178,19 @@ static void CopyValue(const glTF2::vec4& v, aiQuaternion& out)
o.a4 = v[12]; o.b4 = v[13]; o.c4 = v[14]; o.d4 = v[15];
}*/
inline void SetMaterialColorProperty(Asset& /*r*/, vec4& prop, aiMaterial* mat, const char* pKey, unsigned int type, unsigned int idx)
{
inline void SetMaterialColorProperty(Asset & /*r*/, vec4 &prop, aiMaterial *mat, const char *pKey, unsigned int type, unsigned int idx) {
aiColor4D col;
CopyValue(prop, col);
mat->AddProperty(&col, 1, pKey, type, idx);
}
inline void SetMaterialColorProperty(Asset& /*r*/, vec3& prop, aiMaterial* mat, const char* pKey, unsigned int type, unsigned int idx)
{
inline void SetMaterialColorProperty(Asset & /*r*/, vec3 &prop, aiMaterial *mat, const char *pKey, unsigned int type, unsigned int idx) {
aiColor4D col;
glTFCommon::CopyValue(prop, col);
mat->AddProperty(&col, 1, pKey, type, idx);
}
inline void SetMaterialTextureProperty(std::vector<int>& embeddedTexIdxs, Asset& /*r*/, glTF2::TextureInfo prop, aiMaterial* mat, aiTextureType texType, unsigned int texSlot = 0)
{
inline void SetMaterialTextureProperty(std::vector<int> &embeddedTexIdxs, Asset & /*r*/, glTF2::TextureInfo prop, aiMaterial *mat, aiTextureType texType, unsigned int texSlot = 0) {
if (prop.texture && prop.texture->source) {
aiString uri(prop.texture->source->uri);
@ -207,7 +202,26 @@ inline void SetMaterialTextureProperty(std::vector<int>& embeddedTexIdxs, Asset&
}
mat->AddProperty(&uri, AI_MATKEY_TEXTURE(texType, texSlot));
mat->AddProperty(&prop.texCoord, 1, _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE, texType, texSlot);
if (prop.textureTransformSupported) {
aiUVTransform transform;
transform.mScaling.x = prop.TextureTransformExt_t.scale[0];
transform.mScaling.y = prop.TextureTransformExt_t.scale[1];
transform.mRotation = -prop.TextureTransformExt_t.rotation; // must be negated
// A change of coordinates is required to map glTF UV transformations into the space used by
// Assimp. In glTF all UV origins are at 0,1 (top left of texture) in Assimp space. In Assimp
// rotation occurs around the image center (0.5,0.5) where as in glTF rotation is around the
// texture origin. All three can be corrected for solely by a change of the translation since
// the transformations available are shape preserving. Note the importer already flips the V
// coordinate of the actual meshes during import.
const ai_real rcos(cos(-transform.mRotation));
const ai_real rsin(sin(-transform.mRotation));
transform.mTranslation.x = (0.5 * transform.mScaling.x) * (-rcos + rsin + 1) + prop.TextureTransformExt_t.offset[0];
transform.mTranslation.y = ((0.5 * transform.mScaling.y) * (rsin + rcos - 1)) + 1 - transform.mScaling.y - prop.TextureTransformExt_t.offset[1];;
mat->AddProperty(&transform, 1, _AI_MATKEY_UVTRANSFORM_BASE, texType, texSlot);
}
if (prop.texture->sampler) {
Ref<Sampler> sampler = prop.texture->sampler;
@ -234,8 +248,7 @@ inline void SetMaterialTextureProperty(std::vector<int>& embeddedTexIdxs, Asset&
}
}
inline void SetMaterialTextureProperty(std::vector<int>& embeddedTexIdxs, Asset& r, glTF2::NormalTextureInfo& prop, aiMaterial* mat, aiTextureType texType, unsigned int texSlot = 0)
{
inline void SetMaterialTextureProperty(std::vector<int> &embeddedTexIdxs, Asset &r, glTF2::NormalTextureInfo &prop, aiMaterial *mat, aiTextureType texType, unsigned int texSlot = 0) {
SetMaterialTextureProperty(embeddedTexIdxs, r, (glTF2::TextureInfo)prop, mat, texType, texSlot);
if (prop.texture && prop.texture->source) {
@ -243,8 +256,7 @@ inline void SetMaterialTextureProperty(std::vector<int>& embeddedTexIdxs, Asset&
}
}
inline void SetMaterialTextureProperty(std::vector<int>& embeddedTexIdxs, Asset& r, glTF2::OcclusionTextureInfo& prop, aiMaterial* mat, aiTextureType texType, unsigned int texSlot = 0)
{
inline void SetMaterialTextureProperty(std::vector<int> &embeddedTexIdxs, Asset &r, glTF2::OcclusionTextureInfo &prop, aiMaterial *mat, aiTextureType texType, unsigned int texSlot = 0) {
SetMaterialTextureProperty(embeddedTexIdxs, r, (glTF2::TextureInfo)prop, mat, texType, texSlot);
if (prop.texture && prop.texture->source) {
@ -252,8 +264,7 @@ inline void SetMaterialTextureProperty(std::vector<int>& embeddedTexIdxs, Asset&
}
}
static aiMaterial* ImportMaterial(std::vector<int>& embeddedTexIdxs, Asset& r, Material& mat)
{
static aiMaterial *ImportMaterial(std::vector<int> &embeddedTexIdxs, Asset &r, Material &mat) {
aiMaterial *aimat = new aiMaterial();
if (!mat.name.empty()) {
@ -311,8 +322,7 @@ static aiMaterial* ImportMaterial(std::vector<int>& embeddedTexIdxs, Asset& r, M
return aimat;
}
void glTF2Importer::ImportMaterials(glTF2::Asset& r)
{
void glTF2Importer::ImportMaterials(glTF2::Asset &r) {
const unsigned int numImportedMaterials = unsigned(r.materials.Size());
Material defaultMaterial;
@ -325,24 +335,20 @@ void glTF2Importer::ImportMaterials(glTF2::Asset& r)
}
}
static inline void SetFace(aiFace& face, int a)
{
static inline void SetFace(aiFace &face, int a) {
face.mNumIndices = 1;
face.mIndices = new unsigned int[1];
face.mIndices[0] = a;
}
static inline void SetFace(aiFace& face, int a, int b)
{
static inline void SetFace(aiFace &face, int a, int b) {
face.mNumIndices = 2;
face.mIndices = new unsigned int[2];
face.mIndices[0] = a;
face.mIndices[1] = b;
}
static inline void SetFace(aiFace& face, int a, int b, int c)
{
static inline void SetFace(aiFace &face, int a, int b, int c) {
face.mNumIndices = 3;
face.mIndices = new unsigned int[3];
face.mIndices[0] = a;
@ -351,8 +357,7 @@ static inline void SetFace(aiFace& face, int a, int b, int c)
}
#ifdef ASSIMP_BUILD_DEBUG
static inline bool CheckValidFacesIndices(aiFace* faces, unsigned nFaces, unsigned nVerts)
{
static inline bool CheckValidFacesIndices(aiFace *faces, unsigned nFaces, unsigned nVerts) {
for (unsigned i = 0; i < nFaces; ++i) {
for (unsigned j = 0; j < faces[i].mNumIndices; ++j) {
unsigned idx = faces[i].mIndices[j];
@ -364,8 +369,7 @@ static inline bool CheckValidFacesIndices(aiFace* faces, unsigned nFaces, unsign
}
#endif // ASSIMP_BUILD_DEBUG
void glTF2Importer::ImportMeshes(glTF2::Asset& r)
{
void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
std::vector<aiMesh *> meshes;
unsigned int k = 0;
@ -406,7 +410,6 @@ void glTF2Importer::ImportMeshes(glTF2::Asset& r)
case PrimitiveMode_TRIANGLE_FAN:
aim->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
break;
}
Mesh::Primitive::Attributes &attr = prim.attributes;
@ -508,7 +511,6 @@ void glTF2Importer::ImportMeshes(glTF2::Asset& r)
}
}
aiFace *faces = 0;
size_t nFaces = 0;
@ -530,6 +532,10 @@ void glTF2Importer::ImportMeshes(glTF2::Asset& r)
case PrimitiveMode_LINES: {
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];
for (unsigned int i = 0; i < count; i += 2) {
SetFace(faces[i / 2], data.GetUInt(i), data.GetUInt(i + 1));
@ -553,6 +559,10 @@ void glTF2Importer::ImportMeshes(glTF2::Asset& r)
case PrimitiveMode_TRIANGLES: {
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];
for (unsigned int i = 0; i < count; i += 3) {
SetFace(faces[i / 3], data.GetUInt(i), data.GetUInt(i + 1), data.GetUInt(i + 2));
@ -564,13 +574,10 @@ void glTF2Importer::ImportMeshes(glTF2::Asset& r)
faces = new aiFace[nFaces];
for (unsigned int i = 0; i < nFaces; ++i) {
//The ordering is to ensure that the triangles are all drawn with the same orientation
if ((i + 1) % 2 == 0)
{
if ((i + 1) % 2 == 0) {
//For even n, vertices n + 1, n, and n + 2 define triangle n
SetFace(faces[i], data.GetUInt(i + 1), data.GetUInt(i), data.GetUInt(i + 2));
}
else
{
} else {
//For odd n, vertices n, n+1, and n+2 define triangle n
SetFace(faces[i], data.GetUInt(i), data.GetUInt(i + 1), data.GetUInt(i + 2));
}
@ -586,8 +593,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset& r)
}
break;
}
}
else { // no indices provided so directly generate from counts
} else { // no indices provided so directly generate from counts
// use the already determined count as it includes checks
unsigned int count = aim->mNumVertices;
@ -604,6 +610,10 @@ void glTF2Importer::ImportMeshes(glTF2::Asset& r)
case PrimitiveMode_LINES: {
nFaces = count / 2;
if (nFaces * 2 != count) {
ASSIMP_LOG_WARN("The number of vertices was not compatible with the LINES mode. Some vertices were dropped.");
count = (unsigned int)nFaces * 2;
}
faces = new aiFace[nFaces];
for (unsigned int i = 0; i < count; i += 2) {
SetFace(faces[i / 2], i, i + 1);
@ -627,6 +637,10 @@ void glTF2Importer::ImportMeshes(glTF2::Asset& r)
case PrimitiveMode_TRIANGLES: {
nFaces = count / 3;
if (nFaces * 3 != count) {
ASSIMP_LOG_WARN("The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped.");
count = (unsigned int)nFaces * 3;
}
faces = new aiFace[nFaces];
for (unsigned int i = 0; i < count; i += 3) {
SetFace(faces[i / 3], i, i + 1, i + 2);
@ -638,13 +652,10 @@ void glTF2Importer::ImportMeshes(glTF2::Asset& r)
faces = new aiFace[nFaces];
for (unsigned int i = 0; i < nFaces; ++i) {
//The ordering is to ensure that the triangles are all drawn with the same orientation
if ((i+1) % 2 == 0)
{
if ((i + 1) % 2 == 0) {
//For even n, vertices n + 1, n, and n + 2 define triangle n
SetFace(faces[i], i + 1, i, i + 2);
}
else
{
} else {
//For odd n, vertices n, n+1, and n+2 define triangle n
SetFace(faces[i], i, i + 1, i + 2);
}
@ -670,11 +681,9 @@ void glTF2Importer::ImportMeshes(glTF2::Asset& r)
if (prim.material) {
aim->mMaterialIndex = prim.material.GetIndex();
}
else {
} else {
aim->mMaterialIndex = mScene->mNumMaterials - 1;
}
}
}
@ -683,8 +692,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset& r)
CopyVector(meshes, mScene->mMeshes, mScene->mNumMeshes);
}
void glTF2Importer::ImportCameras(glTF2::Asset& r)
{
void glTF2Importer::ImportCameras(glTF2::Asset &r) {
if (!r.cameras.Size()) return;
mScene->mNumCameras = r.cameras.Size();
@ -716,8 +724,7 @@ void glTF2Importer::ImportCameras(glTF2::Asset& r)
}
}
void glTF2Importer::ImportLights(glTF2::Asset& r)
{
void glTF2Importer::ImportLights(glTF2::Asset &r) {
if (!r.lights.Size())
return;
@ -729,18 +736,19 @@ void glTF2Importer::ImportLights(glTF2::Asset& r)
aiLight *ail = mScene->mLights[i] = new aiLight();
switch (light.type)
{
switch (light.type) {
case Light::Directional:
ail->mType = aiLightSource_DIRECTIONAL; break;
ail->mType = aiLightSource_DIRECTIONAL;
break;
case Light::Point:
ail->mType = aiLightSource_POINT; break;
ail->mType = aiLightSource_POINT;
break;
case Light::Spot:
ail->mType = aiLightSource_SPOT; break;
ail->mType = aiLightSource_SPOT;
break;
}
if (ail->mType != aiLightSource_POINT)
{
if (ail->mType != aiLightSource_POINT) {
ail->mDirection = aiVector3D(0.0f, 0.0f, -1.0f);
ail->mUp = aiVector3D(0.0f, 1.0f, 0.0f);
}
@ -750,14 +758,11 @@ void glTF2Importer::ImportLights(glTF2::Asset& r)
CopyValue(colorWithIntensity, ail->mColorDiffuse);
CopyValue(colorWithIntensity, ail->mColorSpecular);
if (ail->mType == aiLightSource_DIRECTIONAL)
{
if (ail->mType == aiLightSource_DIRECTIONAL) {
ail->mAttenuationConstant = 1.0;
ail->mAttenuationLinear = 0.0;
ail->mAttenuationQuadratic = 0.0;
}
else
{
} else {
//in PBR attenuation is calculated using inverse square law which can be expressed
//using assimps equation: 1/(att0 + att1 * d + att2 * d*d) with the following parameters
//this is correct equation for the case when range (see
@ -771,8 +776,7 @@ void glTF2Importer::ImportLights(glTF2::Asset& r)
ail->mAttenuationQuadratic = 1.0;
}
if (ail->mType == aiLightSource_SPOT)
{
if (ail->mType == aiLightSource_SPOT) {
ail->mAngleInnerCone = light.innerConeAngle;
ail->mAngleOuterCone = light.outerConeAngle;
}
@ -782,8 +786,7 @@ void glTF2Importer::ImportLights(glTF2::Asset& r)
static void GetNodeTransform(aiMatrix4x4 &matrix, const glTF2::Node &node) {
if (node.matrix.isPresent) {
CopyValue(node.matrix.value, matrix);
}
else {
} else {
if (node.translation.isPresent) {
aiVector3D trans;
CopyValue(node.translation.value, trans);
@ -808,8 +811,7 @@ static void GetNodeTransform(aiMatrix4x4& matrix, const glTF2::Node& node) {
}
}
static void BuildVertexWeightMapping(Mesh::Primitive& primitive, std::vector<std::vector<aiVertexWeight>>& map)
{
static void BuildVertexWeightMapping(Mesh::Primitive &primitive, std::vector<std::vector<aiVertexWeight>> &map) {
Mesh::Primitive::Attributes &attr = primitive.attributes;
if (attr.weight.empty() || attr.joint.empty()) {
return;
@ -820,12 +822,18 @@ static void BuildVertexWeightMapping(Mesh::Primitive& primitive, std::vector<std
size_t num_vertices = attr.weight[0]->count;
struct Weights { float values[4]; };
struct Weights {
float values[4];
};
Weights *weights = nullptr;
attr.weight[0]->ExtractData(weights);
struct Indices8 { uint8_t values[4]; };
struct Indices16 { uint16_t values[4]; };
struct Indices8 {
uint8_t values[4];
};
struct Indices16 {
uint16_t values[4];
};
Indices8 *indices8 = nullptr;
Indices16 *indices16 = nullptr;
if (attr.joint[0]->GetElementSize() == 4) {
@ -856,13 +864,11 @@ static void BuildVertexWeightMapping(Mesh::Primitive& primitive, std::vector<std
delete[] indices16;
}
static std::string GetNodeName(const Node& node)
{
static std::string GetNodeName(const Node &node) {
return node.name.empty() ? node.id : node.name;
}
aiNode* ImportNode(aiScene* pScene, glTF2::Asset& r, std::vector<unsigned int>& meshOffsets, glTF2::Ref<glTF2::Node>& ptr)
{
aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector<unsigned int> &meshOffsets, glTF2::Ref<glTF2::Node> &ptr) {
Node &node = *ptr;
aiNode *ainode = new aiNode(GetNodeName(node));
@ -964,8 +970,7 @@ aiNode* ImportNode(aiScene* pScene, glTF2::Asset& r, std::vector<unsigned int>&
//range is optional - see https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual
//it is added to meta data of parent node, because there is no other place to put it
if (node.light->range.isPresent)
{
if (node.light->range.isPresent) {
ainode->mMetaData = aiMetadata::Alloc(1);
ainode->mMetaData->Set(0, "PBR_LightRange", node.light->range.value);
}
@ -974,8 +979,7 @@ aiNode* ImportNode(aiScene* pScene, glTF2::Asset& r, std::vector<unsigned int>&
return ainode;
}
void glTF2Importer::ImportNodes(glTF2::Asset& r)
{
void glTF2Importer::ImportNodes(glTF2::Asset &r) {
if (!r.scene) return;
std::vector<Ref<Node>> rootNodes = r.scene->nodes;
@ -984,8 +988,7 @@ void glTF2Importer::ImportNodes(glTF2::Asset& r)
unsigned int numRootNodes = unsigned(rootNodes.size());
if (numRootNodes == 1) { // a single root node: use it
mScene->mRootNode = ImportNode(mScene, r, meshOffsets, rootNodes[0]);
}
else if (numRootNodes > 1) { // more than one root node: create a fake root
} else if (numRootNodes > 1) { // more than one root node: create a fake root
aiNode *root = new aiNode("ROOT");
root->mChildren = new aiNode *[numRootNodes];
for (unsigned int i = 0; i < numRootNodes; ++i) {
@ -995,27 +998,24 @@ void glTF2Importer::ImportNodes(glTF2::Asset& r)
}
mScene->mRootNode = root;
}
//if (!mScene->mRootNode) {
// mScene->mRootNode = new aiNode("EMPTY");
//}
}
struct AnimationSamplers {
AnimationSamplers()
: translation(nullptr)
, rotation(nullptr)
, scale(nullptr) {
AnimationSamplers() :
translation(nullptr),
rotation(nullptr),
scale(nullptr),
weight(nullptr) {
// empty
}
Animation::Sampler *translation;
Animation::Sampler *rotation;
Animation::Sampler *scale;
Animation::Sampler *weight;
};
aiNodeAnim* CreateNodeAnim(glTF2::Asset& r, Node& node, AnimationSamplers& samplers)
{
aiNodeAnim *CreateNodeAnim(glTF2::Asset &r, Node &node, AnimationSamplers &samplers) {
aiNodeAnim *anim = new aiNodeAnim();
anim->mNodeName = GetNodeName(node);
@ -1036,7 +1036,7 @@ aiNodeAnim* CreateNodeAnim(glTF2::Asset& r, Node& node, AnimationSamplers& sampl
delete[] values;
} else if (node.translation.isPresent) {
anim->mNumPositionKeys = 1;
anim->mPositionKeys = new aiVectorKey();
anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
anim->mPositionKeys->mTime = 0.f;
anim->mPositionKeys->mValue.x = node.translation.value[0];
anim->mPositionKeys->mValue.y = node.translation.value[1];
@ -1094,8 +1094,43 @@ aiNodeAnim* CreateNodeAnim(glTF2::Asset& r, Node& node, AnimationSamplers& sampl
return anim;
}
std::unordered_map<unsigned int, AnimationSamplers> GatherSamplers(Animation& anim)
{
aiMeshMorphAnim *CreateMeshMorphAnim(glTF2::Asset &r, Node &node, AnimationSamplers &samplers) {
aiMeshMorphAnim *anim = new aiMeshMorphAnim();
anim->mName = GetNodeName(node);
static const float kMillisecondsFromSeconds = 1000.f;
if (nullptr != samplers.weight) {
float *times = nullptr;
samplers.weight->input->ExtractData(times);
float *values = nullptr;
samplers.weight->output->ExtractData(values);
anim->mNumKeys = static_cast<uint32_t>(samplers.weight->input->count);
const unsigned int numMorphs = (unsigned int)samplers.weight->output->count / anim->mNumKeys;
anim->mKeys = new aiMeshMorphKey[anim->mNumKeys];
unsigned int k = 0u;
for (unsigned int i = 0u; i < anim->mNumKeys; ++i) {
anim->mKeys[i].mTime = times[i] * kMillisecondsFromSeconds;
anim->mKeys[i].mNumValuesAndWeights = numMorphs;
anim->mKeys[i].mValues = new unsigned int[numMorphs];
anim->mKeys[i].mWeights = new double[numMorphs];
for (unsigned int j = 0u; j < numMorphs; ++j, ++k) {
anim->mKeys[i].mValues[j] = j;
anim->mKeys[i].mWeights[j] = (0.f > values[k]) ? 0.f : values[k];
}
}
delete[] times;
delete[] values;
}
return anim;
}
std::unordered_map<unsigned int, AnimationSamplers> GatherSamplers(Animation &anim) {
std::unordered_map<unsigned int, AnimationSamplers> samplers;
for (unsigned int c = 0; c < anim.channels.size(); ++c) {
Animation::Channel &channel = anim.channels[c];
@ -1112,14 +1147,15 @@ std::unordered_map<unsigned int, AnimationSamplers> GatherSamplers(Animation& an
sampler.rotation = &anim.samplers[channel.sampler];
} else if (channel.target.path == AnimationPath_SCALE) {
sampler.scale = &anim.samplers[channel.sampler];
} else if (channel.target.path == AnimationPath_WEIGHTS) {
sampler.weight = &anim.samplers[channel.sampler];
}
}
return samplers;
}
void glTF2Importer::ImportAnimations(glTF2::Asset& r)
{
void glTF2Importer::ImportAnimations(glTF2::Asset &r) {
if (!r.scene) return;
mScene->mNumAnimations = r.animations.Size();
@ -1138,15 +1174,41 @@ void glTF2Importer::ImportAnimations(glTF2::Asset& r)
std::unordered_map<unsigned int, AnimationSamplers> samplers = GatherSamplers(anim);
ai_anim->mNumChannels = static_cast<uint32_t>(samplers.size());
uint32_t numChannels = 0u;
uint32_t numMorphMeshChannels = 0u;
for (auto &iter : samplers) {
if ((nullptr != iter.second.rotation) || (nullptr != iter.second.scale) || (nullptr != iter.second.translation)) {
++numChannels;
}
if (nullptr != iter.second.weight) {
++numMorphMeshChannels;
}
}
ai_anim->mNumChannels = numChannels;
if (ai_anim->mNumChannels > 0) {
ai_anim->mChannels = new aiNodeAnim *[ai_anim->mNumChannels];
int j = 0;
for (auto &iter : samplers) {
if ((nullptr != iter.second.rotation) || (nullptr != iter.second.scale) || (nullptr != iter.second.translation)) {
ai_anim->mChannels[j] = CreateNodeAnim(r, r.nodes[iter.first], iter.second);
++j;
}
}
}
ai_anim->mNumMorphMeshChannels = numMorphMeshChannels;
if (ai_anim->mNumMorphMeshChannels > 0) {
ai_anim->mMorphMeshChannels = new aiMeshMorphAnim *[ai_anim->mNumMorphMeshChannels];
int j = 0;
for (auto &iter : samplers) {
if (nullptr != iter.second.weight) {
ai_anim->mMorphMeshChannels[j] = CreateMeshMorphAnim(r, r.nodes[iter.first], iter.second);
++j;
}
}
}
// Use the latest keyframe for the duration of the animation
double maxDuration = 0;
@ -1175,15 +1237,27 @@ void glTF2Importer::ImportAnimations(glTF2::Asset& r)
maxNumberOfKeys = std::max(maxNumberOfKeys, chan->mNumScalingKeys);
}
}
for (unsigned int j = 0; j < ai_anim->mNumMorphMeshChannels; ++j) {
const auto *const chan = ai_anim->mMorphMeshChannels[j];
if (0u != chan->mNumKeys) {
const auto &lastKey = chan->mKeys[chan->mNumKeys - 1u];
if (lastKey.mTime > maxDuration) {
maxDuration = lastKey.mTime;
}
maxNumberOfKeys = std::max(maxNumberOfKeys, chan->mNumKeys);
}
}
ai_anim->mDuration = maxDuration;
ai_anim->mTicksPerSecond = (maxNumberOfKeys > 0 && maxDuration > 0) ? (maxNumberOfKeys / (maxDuration/1000)) : 30;
ai_anim->mTicksPerSecond = 1000.0;
mScene->mAnimations[i] = ai_anim;
}
}
void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset& r)
{
void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset &r) {
embeddedTexIdxs.resize(r.images.Size(), -1);
int numEmbeddedTexs = 0;
@ -1228,8 +1302,25 @@ void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset& r)
}
}
void glTF2Importer::InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
void glTF2Importer::ImportCommonMetadata(glTF2::Asset& a) {
ai_assert(mScene->mMetaData == nullptr);
const bool hasVersion = !a.asset.version.empty();
const bool hasGenerator = !a.asset.generator.empty();
if (hasVersion || hasGenerator)
{
mScene->mMetaData = new aiMetadata;
if (hasVersion)
{
mScene->mMetaData->Add(AI_METADATA_SOURCE_FORMAT_VERSION, aiString(a.asset.version));
}
if (hasGenerator)
{
mScene->mMetaData->Add(AI_METADATA_SOURCE_GENERATOR, aiString(a.asset.generator));
}
}
}
void glTF2Importer::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
// clean all member arrays
meshOffsets.clear();
embeddedTexIdxs.clear();
@ -1256,10 +1347,11 @@ void glTF2Importer::InternReadFile(const std::string& pFile, aiScene* pScene, IO
ImportAnimations(asset);
ImportCommonMetadata(asset);
if (pScene->mNumMeshes == 0) {
pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
}
}
#endif // ASSIMP_BUILD_NO_GLTF_IMPORTER

View File

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

View File

@ -15,6 +15,9 @@
#include <cstdint>
//using namespace Assimp;
// For locale independent number conversion
#include <sstream>
#include <locale>
#ifdef _DEBUG
#define IRR_DEBUGPRINT(x) printf((x));
@ -178,8 +181,11 @@ public:
return 0;
core::stringc c = attrvalue;
return static_cast<float>(atof(c.c_str()));
//return fast_atof(c.c_str());
std::istringstream sstr(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/
/test/build/
/xcodeproj/
.vscode/
# Object files
*.o
@ -54,3 +55,4 @@ zip.dir/
test/test.exe.vcxproj.filters
test/test.exe.vcxproj
test/test.exe.dir/

View File

@ -1,10 +1,14 @@
cmake_minimum_required(VERSION 2.8)
project(zip)
enable_language(C)
cmake_minimum_required(VERSION 3.0)
project(zip
LANGUAGES C
VERSION "0.1.15")
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
option(CMAKE_DISABLE_TESTING "Disable test creation" OFF)
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_COUNT=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 "AppleClang")
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)
# zip
set(SRC src/miniz.h src/zip.h src/zip.c)
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
if (NOT CMAKE_DISABLE_TESTING)
enable_testing()
add_subdirectory(test)
find_package(Sanitizers)
add_sanitizers(${PROJECT_NAME} test.exe)
add_sanitizers(${PROJECT_NAME} test_miniz.exe)
add_sanitizers(${PROJECT_NAME} ${test_out} ${test_miniz_out})
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}
EXPORT ${TARGETS_EXPORT_NAME}
RUNTIME DESTINATION bin
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
COMPONENT library)
install(FILES ${PROJECT_SOURCE_DIR}/src/zip.h DESTINATION include)
INCLUDES DESTINATION ${INCLUDE_INSTALL_DIR}
)
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)
if(NOT TARGET uninstall)
@ -45,3 +101,12 @@ if(NOT TARGET uninstall)
add_custom_target(uninstall
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake/cmake_uninstall.cmake)
endif()
find_package(Doxygen)
if(DOXYGEN_FOUND)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY)
add_custom_target(doc
${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Generating API documentation with Doxygen" VERBATIM)
endif()

View File

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

View File

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

View File

@ -221,6 +221,7 @@
#ifndef MINIZ_HEADER_INCLUDED
#define MINIZ_HEADER_INCLUDED
#include <stdint.h>
#include <stdlib.h>
// Defines to completely disable specific portions of miniz.c:
@ -284,7 +285,8 @@
/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES only if not set */
#if !defined(MINIZ_USE_UNALIGNED_LOADS_AND_STORES)
#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_UNALIGNED_USE_MEMCPY
#else
@ -354,6 +356,44 @@ enum {
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
#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);
typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs,
const void *pBuf, size_t n);
typedef mz_bool (*mz_file_needs_keepalive)(void *pOpaque);
struct mz_zip_internal_state_tag;
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;
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_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_free_func m_pFree;
@ -722,6 +777,7 @@ typedef struct mz_zip_archive_tag {
mz_file_read_func m_pRead;
mz_file_write_func m_pWrite;
mz_file_needs_keepalive m_pNeeds_keepalive;
void *m_pIO_opaque;
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);
#endif // #ifndef MINIZ_NO_ZLIB_APIS
#define MZ_UINT16_MAX (0xFFFFU)
#define MZ_UINT32_MAX (0xFFFFFFFFU)
#ifdef __cplusplus
}
#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))
#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
#define MZ_FORCEINLINE __forceinline
#elif defined(__GNUC__)
@ -4160,6 +4224,17 @@ enum {
MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30,
MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46,
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
MZ_ZIP_CDH_SIG_OFS = 0,
MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4,
@ -4199,6 +4274,31 @@ enum {
MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12,
MZ_ZIP_ECDH_CDIR_OFS_OFS = 16,
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 {
@ -4211,7 +4311,24 @@ struct mz_zip_internal_state_tag {
mz_zip_array m_central_dir;
mz_zip_array m_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_uint64 m_file_archive_start_ofs;
void *m_pMem;
size_t m_mem_size;
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_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,
mz_uint32 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,
mz_uint32 flags) {
mz_uint cdir_size, num_this_disk, cdir_disk_index;
mz_uint64 cdir_ofs;
static mz_bool mz_zip_reader_locate_header_sig(mz_zip_archive *pZip,
mz_uint32 record_sig,
mz_uint32 record_size,
mz_int64 *pOfs) {
mz_int64 cur_file_ofs;
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);
// 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)
/* Basic sanity checks - reject files which are too small */
if (pZip->m_archive_size < record_size)
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 =
MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0);
for (;;) {
int i,
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)
return MZ_FALSE;
for (i = n - 4; i >= 0; --i)
if (MZ_READ_LE32(pBuf + i) == MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG)
for (i = n - 4; i >= 0; --i) {
mz_uint s = MZ_READ_LE32(pBuf + i);
if (s == record_sig) {
if ((pZip->m_archive_size - (cur_file_ofs + i)) >= record_size)
break;
}
}
if (i >= 0) {
cur_file_ofs += i;
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) >=
(0xFFFF + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)))
(MZ_UINT16_MAX + record_size)))
return MZ_FALSE;
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,
MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) !=
MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)
return MZ_FALSE;
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;
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)
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);
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) &&
((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)) <
pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)
return MZ_FALSE;
if (cdir_size < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)
return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS);
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;
if (pZip->m_total_files) {
mz_uint i, n;
// Read the entire central directory into a heap block, and allocate another
// heap block to hold the unsorted central dir file record offsets, and
// another to hold the sorted indices.
/* Read the entire central directory into a heap block, and allocate another
* heap block to hold the unsorted central dir file record offsets, and
* possibly another to hold the sorted indices. */
if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size,
MZ_FALSE)) ||
(!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets,
pZip->m_total_files, MZ_FALSE)))
return MZ_FALSE;
return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
if (sort_central_dir) {
if (!mz_zip_array_resize(pZip,
&pZip->m_pState->m_sorted_central_dir_offsets,
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,
pZip->m_pState->m_central_dir.m_p,
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
// basic sanity checking on each record, and check for zip64 entries (which
// are not yet supported).
/* Now create an index into the central directory file records, do some
* basic sanity checking on each record */
p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p;
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) ||
(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,
i) =
(mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p);
if (sort_central_dir)
MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets,
mz_uint32, i) = i;
comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS);
decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS);
local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS);
filename_size = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS);
ext_data_size = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS);
if ((!pZip->m_pState->m_zip64_has_extended_info_fields) &&
(ext_data_size) &&
(MZ_MAX(MZ_MAX(comp_size, decomp_size), local_header_ofs) ==
MZ_UINT32_MAX)) {
/* Attempt to find zip64 extended information field in the entry's extra
* data */
mz_uint32 extra_size_remaining = ext_data_size;
if (extra_size_remaining) {
const mz_uint8 *pExtra_data;
void *buf = NULL;
if (MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + ext_data_size >
n) {
buf = MZ_MALLOC(ext_data_size);
if (buf == NULL)
return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
if (pZip->m_pRead(pZip->m_pIO_opaque,
cdir_ofs + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE +
filename_size,
buf, ext_data_size) != ext_data_size) {
MZ_FREE(buf);
return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
}
pExtra_data = (mz_uint8 *)buf;
} else {
pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size;
}
do {
mz_uint32 field_id;
mz_uint32 field_data_size;
if (extra_size_remaining < (sizeof(mz_uint16) * 2)) {
MZ_FREE(buf);
return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
}
field_id = MZ_READ_LE16(pExtra_data);
field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16));
if ((field_data_size + sizeof(mz_uint16) * 2) >
extra_size_remaining) {
MZ_FREE(buf);
return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
}
if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) {
/* Ok, the archive didn't have any zip64 headers but it uses a
* zip64 extended information field so mark it as zip64 anyway
* (this can occur with infozip's zip util when it reads
* compresses files from stdin). */
pZip->m_pState->m_zip64 = MZ_TRUE;
pZip->m_pState->m_zip64_has_extended_info_fields = MZ_TRUE;
break;
}
pExtra_data += sizeof(mz_uint16) * 2 + field_data_size;
extra_size_remaining =
extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size;
} while (extra_size_remaining);
MZ_FREE(buf);
}
}
/* I've seen archives that aren't marked as zip64 that uses zip64 ext
* data, argh */
if ((comp_size != MZ_UINT32_MAX) && (decomp_size != MZ_UINT32_MAX)) {
if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) &&
(decomp_size != comp_size)) ||
(decomp_size && !comp_size) || (decomp_size == 0xFFFFFFFF) ||
(comp_size == 0xFFFFFFFF))
return MZ_FALSE;
(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);
if ((disk_index != num_this_disk) && (disk_index != 1))
return MZ_FALSE;
if ((disk_index == MZ_UINT16_MAX) ||
((disk_index != num_this_disk) && (disk_index != 1)))
return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK);
if (comp_size != MZ_UINT32_MAX) {
if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) +
MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size)
return MZ_FALSE;
return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
}
bit_flags = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS);
if (bit_flags & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED)
return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION);
if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE +
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_COMMENT_LEN_OFS)) >
n)
return MZ_FALSE;
return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
n -= 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)[1] == ':')
#define FILESYSTEM_PREFIX_LEN(P) (HAS_DEVICE(P) ? 2 : 0)
#define ISSLASH(C) ((C) == '/' || (C) == '\\')
#else
@ -48,7 +47,7 @@ int symlink(const char *target, const char *linkpath); // needed on Linux
#endif
#ifndef ISSLASH
#define ISSLASH(C) ((C) == '/')
#define ISSLASH(C) ((C) == '/' || (C) == '\\')
#endif
#define CLEANUP(ptr) \
@ -78,27 +77,35 @@ static const char *base_name(const char *name) {
return base;
}
static int mkpath(const char *path) {
char const *p;
static int mkpath(char *path) {
char *p;
char npath[MAX_PATH + 1];
int len = 0;
int has_device = HAS_DEVICE(path);
memset(npath, 0, MAX_PATH + 1);
#ifdef _WIN32
// only on windows fix the path
if (has_device) {
// only on windows
npath[0] = path[0];
npath[1] = path[1];
len = 2;
#endif // _WIN32
}
for (p = path + len; *p && len < MAX_PATH; p++) {
if (ISSLASH(*p) && ((!has_device && len > 0) || (has_device && len > 2))) {
if (MKDIR(npath) == -1)
if (errno != EEXIST)
#if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) || \
defined(__MINGW32__)
#else
if ('\\' == *p) {
*p = '/';
}
#endif
if (MKDIR(npath) == -1) {
if (errno != EEXIST) {
return -1;
}
}
}
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;
memset(zip->entry.header, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE * sizeof(mz_uint8));
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;
#endif
num_alignment_padding_bytes =
mz_zip_writer_compute_padding_needed_for_file_alignment(pzip);
@ -670,10 +684,7 @@ ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf, size_t bufsize) {
int zip_entry_fread(struct zip_t *zip, const char *filename) {
mz_zip_archive *pzip = NULL;
mz_uint idx;
#if defined(_MSC_VER)
#else
mz_uint32 xattr = 0;
#endif
mz_zip_archive_file_stat info;
if (!zip) {
@ -875,12 +886,19 @@ int zip_extract(const char *zipname, const char *dir,
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)
&& info.m_external_attr & (0x20 << 24)) { // and has sym link attribute (0x80 is file, 0x40 is directory)
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)
&& 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) || \
defined(__MINGW32__)
#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;
}
symlink_to[info.m_uncomp_size] = '\0';

View File

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

View File

@ -1,19 +1,16 @@
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
include_directories(../src)
add_executable(test.exe test.c ../src/zip.c)
add_executable(test_miniz.exe test_miniz.c)
set(test_out test.out)
set(test_miniz_out test_miniz.out)
add_test(NAME test COMMAND test.exe)
add_test(NAME test_miniz COMMAND test_miniz.exe)
add_executable(${test_out} test.c)
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 XMODE 0100777
#define UNIXMODE 0100644
#define UNUSED(x) (void)x
static int total_entries = 0;
@ -102,6 +104,7 @@ static void test_read(void) {
assert(0 == zip_entry_close(zip));
free(buf);
buf = NULL;
bufsize = 0;
assert(0 == zip_entry_open(zip, "test/test-2.txt"));
assert(strlen(TESTDATA2) == zip_entry_size(zip));
@ -131,6 +134,7 @@ static void test_read(void) {
assert(0 == zip_entry_close(zip));
free(buf);
buf = NULL;
bufsize = 0;
buftmp = strlen(TESTDATA1);
buf = calloc(buftmp, sizeof(char));
@ -433,6 +437,35 @@ static void test_mtime(void) {
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[]) {
UNUSED(argc);
UNUSED(argv);
@ -453,6 +486,7 @@ int main(int argc, char *argv[]) {
test_write_permissions();
test_exe_permissions();
test_mtime();
test_unix_permissions();
remove(ZIPNAME);
return 0;

View File

@ -23,16 +23,39 @@ int main(int argc, char *argv[]) {
uint step = 0;
int cmp_status;
uLong src_len = (uLong)strlen(s_pStr);
uLong cmp_len = compressBound(src_len);
uLong uncomp_len = src_len;
uLong cmp_len;
uint8 *pCmp, *pUncomp;
size_t sz;
uint total_succeeded = 0;
(void)argc, (void)argv;
printf("miniz.c version: %s\n", MZ_VERSION);
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.
free(pCmp);
cmp_len = compressBound(src_len);
pCmp = (mz_uint8 *)malloc((size_t)cmp_len);
pUncomp = (mz_uint8 *)malloc((size_t)src_len);
if ((!pCmp) || (!pUncomp)) {

View File

@ -41,9 +41,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/** @file Definition of the base class for all importer worker classes. */
#pragma once
#ifndef INCLUDED_AI_BASEIMPORTER_H
#define INCLUDED_AI_BASEIMPORTER_H
#ifdef __GNUC__
# pragma GCC system_header
#endif
#include "Exceptional.h"
#include <vector>
@ -191,16 +196,13 @@ public:
/**
* 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: Valid options are initialised in the
* 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 = {
{ImporterUnits::M, 1},
{ImporterUnits::CM, 0.01},
{ImporterUnits::MM, 0.001},
{ImporterUnits::INCHES, 0.0254},
{ImporterUnits::FEET, 0.3048}
};
std::map<ImporterUnits, double> importerUnits;
virtual void SetApplicationUnits( const ImporterUnits& unit )
{

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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