update assimp to 5.2.3 Bugfix-Release

This commit is contained in:
AzaezelX 2022-04-26 11:56:24 -05:00
parent 3f796d2a06
commit f297476092
1150 changed files with 165834 additions and 112019 deletions

View file

@ -0,0 +1,72 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the following
conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
/** @file AssertHandler.cpp
* @brief Implementation of assert handling logic.
*/
#include "AssertHandler.h"
#include <iostream>
#include <cstdlib>
void Assimp::defaultAiAssertHandler(const char* failedExpression, const char* file, int line)
{
std::cerr << "ai_assert failure in " << file << "(" << line << "): " << failedExpression << std::endl;
std::abort();
}
namespace
{
Assimp::AiAssertHandler s_handler = Assimp::defaultAiAssertHandler;
}
void Assimp::setAiAssertHandler(AiAssertHandler handler)
{
s_handler = handler;
}
void Assimp::aiAssertViolation(const char* failedExpression, const char* file, int line)
{
s_handler(failedExpression, file, line);
}

View file

@ -0,0 +1,75 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2020, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** @file Provides facilities to replace the default assert handler. */
#ifndef INCLUDED_AI_ASSERTHANDLER_H
#define INCLUDED_AI_ASSERTHANDLER_H
#include <assimp/ai_assert.h>
#include <assimp/defs.h>
namespace Assimp
{
// ---------------------------------------------------------------------------
/** Signature of functions which handle assert violations.
*/
using AiAssertHandler = void (*)(const char* failedExpression, const char* file, int line);
// ---------------------------------------------------------------------------
/** Set the assert handler.
*/
ASSIMP_API void setAiAssertHandler(AiAssertHandler handler);
// ---------------------------------------------------------------------------
/** The assert handler which is set by default.
*
* This issues a message to stderr and calls abort.
*/
ASSIMP_API void defaultAiAssertHandler(const char* failedExpression, const char* file, int line);
// ---------------------------------------------------------------------------
/** Dispatches an assert violation to the assert handler.
*/
ASSIMP_API void aiAssertViolation(const char* failedExpression, const char* file, int line);
} // end of namespace Assimp
#endif // INCLUDED_AI_ASSERTHANDLER_H

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,179 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, 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 <assimp/Base64.hpp>
#include <assimp/Exceptional.h>
namespace Assimp {
namespace Base64 {
static const uint8_t tableDecodeBase64[128] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 64, 0, 0,
0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 0
};
static const char *tableEncodeBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
static inline char EncodeChar(uint8_t b) {
return tableEncodeBase64[size_t(b)];
}
inline uint8_t DecodeChar(char c) {
if (c & 0x80) {
throw DeadlyImportError("Invalid base64 char value: ", size_t(c));
}
return tableDecodeBase64[size_t(c & 0x7F)]; // TODO faster with lookup table or ifs?
}
void Encode(const uint8_t *in, size_t inLength, std::string &out) {
size_t outLength = ((inLength + 2) / 3) * 4;
size_t j = out.size();
out.resize(j + outLength);
for (size_t i = 0; i < inLength; i += 3) {
uint8_t b = (in[i] & 0xFC) >> 2;
out[j++] = EncodeChar(b);
b = (in[i] & 0x03) << 4;
if (i + 1 < inLength) {
b |= (in[i + 1] & 0xF0) >> 4;
out[j++] = EncodeChar(b);
b = (in[i + 1] & 0x0F) << 2;
if (i + 2 < inLength) {
b |= (in[i + 2] & 0xC0) >> 6;
out[j++] = EncodeChar(b);
b = in[i + 2] & 0x3F;
out[j++] = EncodeChar(b);
} else {
out[j++] = EncodeChar(b);
out[j++] = '=';
}
} else {
out[j++] = EncodeChar(b);
out[j++] = '=';
out[j++] = '=';
}
}
}
void Encode(const std::vector<uint8_t> &in, std::string &out) {
Encode(in.data(), in.size(), out);
}
std::string Encode(const std::vector<uint8_t> &in) {
std::string encoded;
Encode(in, encoded);
return encoded;
}
size_t Decode(const char *in, size_t inLength, uint8_t *&out) {
if (inLength % 4 != 0) {
throw DeadlyImportError("Invalid base64 encoded data: \"", std::string(in, std::min(size_t(32), inLength)), "\", length:", inLength);
}
if (inLength < 4) {
out = nullptr;
return 0;
}
int nEquals = int(in[inLength - 1] == '=') +
int(in[inLength - 2] == '=');
size_t outLength = (inLength * 3) / 4 - nEquals;
out = new uint8_t[outLength];
memset(out, 0, outLength);
size_t i, j = 0;
for (i = 0; i + 4 < inLength; i += 4) {
uint8_t b0 = DecodeChar(in[i]);
uint8_t b1 = DecodeChar(in[i + 1]);
uint8_t b2 = DecodeChar(in[i + 2]);
uint8_t b3 = DecodeChar(in[i + 3]);
out[j++] = (uint8_t)((b0 << 2) | (b1 >> 4));
out[j++] = (uint8_t)((b1 << 4) | (b2 >> 2));
out[j++] = (uint8_t)((b2 << 6) | b3);
}
{
uint8_t b0 = DecodeChar(in[i]);
uint8_t b1 = DecodeChar(in[i + 1]);
uint8_t b2 = DecodeChar(in[i + 2]);
uint8_t b3 = DecodeChar(in[i + 3]);
out[j++] = (uint8_t)((b0 << 2) | (b1 >> 4));
if (b2 < 64) out[j++] = (uint8_t)((b1 << 4) | (b2 >> 2));
if (b3 < 64) out[j++] = (uint8_t)((b2 << 6) | b3);
}
return outLength;
}
size_t Decode(const std::string &in, std::vector<uint8_t> &out) {
uint8_t *outPtr = nullptr;
size_t decodedSize = Decode(in.data(), in.size(), outPtr);
if (outPtr == nullptr) {
return 0;
}
out.assign(outPtr, outPtr + decodedSize);
delete[] outPtr;
return decodedSize;
}
std::vector<uint8_t> Decode(const std::string &in) {
std::vector<uint8_t> result;
Decode(in, result);
return result;
}
} // namespace Base64
} // namespace Assimp

View file

@ -3,9 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -45,29 +43,29 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* @brief Implementation of BaseImporter
*/
#include <assimp/BaseImporter.h>
#include <assimp/ParsingUtils.h>
#include "FileSystemFilter.h"
#include "Importer.h"
#include <assimp/BaseImporter.h>
#include <assimp/ByteSwapper.h>
#include <assimp/ParsingUtils.h>
#include <assimp/importerdesc.h>
#include <assimp/postprocess.h>
#include <assimp/scene.h>
#include <assimp/Importer.hpp>
#include <assimp/postprocess.h>
#include <assimp/importerdesc.h>
#include <cctype>
#include <ios>
#include <list>
#include <memory>
#include <sstream>
#include <cctype>
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
BaseImporter::BaseImporter() AI_NO_EXCEPT
: m_progress() {
// nothing to do here
: m_progress() {
// empty
}
// ------------------------------------------------------------------------------------------------
@ -76,8 +74,7 @@ BaseImporter::~BaseImporter() {
// nothing to do here
}
void BaseImporter::UpdateImporterScale( Importer* pImp )
{
void BaseImporter::UpdateImporterScale(Importer *pImp) {
ai_assert(pImp != nullptr);
ai_assert(importerScale != 0.0);
ai_assert(fileScale != 0.0);
@ -85,15 +82,14 @@ void BaseImporter::UpdateImporterScale( Importer* pImp )
double activeScale = importerScale * fileScale;
// Set active scaling
pImp->SetPropertyFloat( AI_CONFIG_APP_SCALE_KEY, static_cast<float>( activeScale) );
pImp->SetPropertyFloat(AI_CONFIG_APP_SCALE_KEY, static_cast<float>(activeScale));
ASSIMP_LOG_DEBUG_F("UpdateImporterScale scale set: %f", activeScale );
ASSIMP_LOG_DEBUG("UpdateImporterScale scale set: ", activeScale);
}
// ------------------------------------------------------------------------------------------------
// Imports the given file and returns the imported data.
aiScene* BaseImporter::ReadFile(Importer* pImp, const std::string& pFile, IOSystem* pIOHandler) {
aiScene *BaseImporter::ReadFile(Importer *pImp, const std::string &pFile, IOSystem *pIOHandler) {
m_progress = pImp->GetProgressHandler();
if (nullptr == m_progress) {
@ -103,28 +99,27 @@ aiScene* BaseImporter::ReadFile(Importer* pImp, const std::string& pFile, IOSyst
ai_assert(m_progress);
// Gather configuration properties for this run
SetupProperties( pImp );
SetupProperties(pImp);
// Construct a file system filter to improve our success ratio at reading external files
FileSystemFilter filter(pFile,pIOHandler);
FileSystemFilter filter(pFile, pIOHandler);
// create a scene object to hold the data
std::unique_ptr<aiScene> sc(new aiScene());
// dispatch importing
try
{
InternReadFile( pFile, sc.get(), &filter);
try {
InternReadFile(pFile, sc.get(), &filter);
// Calculate import scale hook - required because pImp not available anywhere else
// passes scale into ScaleProcess
UpdateImporterScale(pImp);
} catch( const std::exception& err ) {
} catch( const std::exception &err ) {
// extract error description
m_ErrorText = err.what();
ASSIMP_LOG_ERROR(m_ErrorText);
ASSIMP_LOG_ERROR(err.what());
m_Exception = std::current_exception();
return nullptr;
}
@ -133,69 +128,66 @@ aiScene* BaseImporter::ReadFile(Importer* pImp, const std::string& pFile, IOSyst
}
// ------------------------------------------------------------------------------------------------
void BaseImporter::SetupProperties(const Importer* pImp)
{
void BaseImporter::SetupProperties(const Importer *) {
// the default implementation does nothing
}
// ------------------------------------------------------------------------------------------------
void BaseImporter::GetExtensionList(std::set<std::string>& extensions) {
const aiImporterDesc* desc = GetInfo();
void BaseImporter::GetExtensionList(std::set<std::string> &extensions) {
const aiImporterDesc *desc = GetInfo();
ai_assert(desc != nullptr);
const char* ext = desc->mFileExtensions;
ai_assert(ext != nullptr );
const char *ext = desc->mFileExtensions;
ai_assert(ext != nullptr);
const char* last = ext;
const char *last = ext;
do {
if (!*ext || *ext == ' ') {
extensions.insert(std::string(last,ext-last));
ai_assert(ext-last > 0);
extensions.insert(std::string(last, ext - last));
ai_assert(ext - last > 0);
last = ext;
while(*last == ' ') {
while (*last == ' ') {
++last;
}
}
}
while(*ext++);
} while (*ext++);
}
// ------------------------------------------------------------------------------------------------
/*static*/ bool BaseImporter::SearchFileHeaderForToken( IOSystem* pIOHandler,
const std::string& pFile,
const char** tokens,
unsigned int numTokens,
unsigned int searchBytes /* = 200 */,
bool tokensSol /* false */,
bool noAlphaBeforeTokens /* false */)
{
ai_assert( nullptr != tokens );
ai_assert( 0 != numTokens );
ai_assert( 0 != searchBytes);
/*static*/ bool BaseImporter::SearchFileHeaderForToken(IOSystem *pIOHandler,
const std::string &pFile,
const char **tokens,
std::size_t numTokens,
unsigned int searchBytes /* = 200 */,
bool tokensSol /* false */,
bool noAlphaBeforeTokens /* false */) {
ai_assert(nullptr != tokens);
ai_assert(0 != numTokens);
ai_assert(0 != searchBytes);
if ( nullptr == pIOHandler ) {
if (nullptr == pIOHandler) {
return false;
}
std::unique_ptr<IOStream> pStream (pIOHandler->Open(pFile));
if (pStream.get() ) {
std::unique_ptr<IOStream> pStream(pIOHandler->Open(pFile));
if (pStream) {
// read 200 characters from the file
std::unique_ptr<char[]> _buffer (new char[searchBytes+1 /* for the '\0' */]);
char *buffer( _buffer.get() );
const size_t read( pStream->Read(buffer,1,searchBytes) );
if( 0 == read ) {
std::unique_ptr<char[]> _buffer(new char[searchBytes + 1 /* for the '\0' */]);
char *buffer(_buffer.get());
const size_t read(pStream->Read(buffer, 1, searchBytes));
if (0 == read) {
return false;
}
for( size_t i = 0; i < read; ++i ) {
buffer[ i ] = static_cast<char>( ::tolower( buffer[ i ] ) );
for (size_t i = 0; i < read; ++i) {
buffer[i] = static_cast<char>(::tolower((unsigned char)buffer[i]));
}
// It is not a proper handling of unicode files here ...
// ehm ... but it works in most cases.
char* cur = buffer,*cur2 = buffer,*end = &buffer[read];
while (cur != end) {
if( *cur ) {
char *cur = buffer, *cur2 = buffer, *end = &buffer[read];
while (cur != end) {
if (*cur) {
*cur2++ = *cur;
}
++cur;
@ -203,28 +195,28 @@ void BaseImporter::GetExtensionList(std::set<std::string>& extensions) {
*cur2 = '\0';
std::string token;
for (unsigned int i = 0; i < numTokens; ++i ) {
ai_assert( nullptr != tokens[i] );
const size_t len( strlen( tokens[ i ] ) );
for (unsigned int i = 0; i < numTokens; ++i) {
ai_assert(nullptr != tokens[i]);
const size_t len(strlen(tokens[i]));
token.clear();
const char *ptr( tokens[ i ] );
for ( size_t tokIdx = 0; tokIdx < len; ++tokIdx ) {
token.push_back( static_cast<char>( tolower( *ptr ) ) );
const char *ptr(tokens[i]);
for (size_t tokIdx = 0; tokIdx < len; ++tokIdx) {
token.push_back(static_cast<char>(tolower(static_cast<unsigned char>(*ptr))));
++ptr;
}
const char* r = strstr( buffer, token.c_str() );
if( !r ) {
const char *r = strstr(buffer, token.c_str());
if (!r) {
continue;
}
// We need to make sure that we didn't accidentially identify the end of another token as our token,
// We need to make sure that we didn't accidentally identify the end of another token as our token,
// e.g. in a previous version the "gltf " present in some gltf files was detected as "f "
if (noAlphaBeforeTokens && (r != buffer && isalpha(r[-1]))) {
if (noAlphaBeforeTokens && (r != buffer && isalpha(static_cast<unsigned char>(r[-1])))) {
continue;
}
// We got a match, either we don't care where it is, or it happens to
// be in the beginning of the file / line
if (!tokensSol || r == buffer || r[-1] == '\r' || r[-1] == '\n') {
ASSIMP_LOG_DEBUG_F( "Found positive match for header keyword: ", tokens[i] );
ASSIMP_LOG_DEBUG("Found positive match for header keyword: ", tokens[i]);
return true;
}
}
@ -235,26 +227,25 @@ void BaseImporter::GetExtensionList(std::set<std::string>& extensions) {
// ------------------------------------------------------------------------------------------------
// Simple check for file extension
/*static*/ bool BaseImporter::SimpleExtensionCheck (const std::string& pFile,
const char* ext0,
const char* ext1,
const char* ext2)
{
/*static*/ bool BaseImporter::SimpleExtensionCheck(const std::string &pFile,
const char *ext0,
const char *ext1,
const char *ext2) {
std::string::size_type pos = pFile.find_last_of('.');
// no file extension - can't read
if( pos == std::string::npos)
if (pos == std::string::npos)
return false;
const char* ext_real = & pFile[ pos+1 ];
if( !ASSIMP_stricmp(ext_real,ext0) )
const char *ext_real = &pFile[pos + 1];
if (!ASSIMP_stricmp(ext_real, ext0))
return true;
// check for other, optional, file extensions
if (ext1 && !ASSIMP_stricmp(ext_real,ext1))
if (ext1 && !ASSIMP_stricmp(ext_real, ext1))
return true;
if (ext2 && !ASSIMP_stricmp(ext_real,ext2))
if (ext2 && !ASSIMP_stricmp(ext_real, ext2))
return true;
return false;
@ -262,44 +253,43 @@ void BaseImporter::GetExtensionList(std::set<std::string>& extensions) {
// ------------------------------------------------------------------------------------------------
// Get file extension from path
std::string BaseImporter::GetExtension( const std::string& file ) {
std::string BaseImporter::GetExtension(const std::string &file) {
std::string::size_type pos = file.find_last_of('.');
// no file extension at all
if (pos == std::string::npos) {
return "";
return std::string();
}
// thanks to Andy Maloney for the hint
std::string ret = file.substr( pos + 1 );
std::transform( ret.begin(), ret.end(), ret.begin(), ToLower<char>);
std::string ret = file.substr(pos + 1);
ret = ai_tolower(ret);
return ret;
}
// ------------------------------------------------------------------------------------------------
// Check for magic bytes at the beginning of the file.
/* static */ bool BaseImporter::CheckMagicToken(IOSystem* pIOHandler, const std::string& pFile,
const void* _magic, unsigned int num, unsigned int offset, unsigned int size)
{
ai_assert( size <= 16 );
ai_assert( _magic );
/* static */ bool BaseImporter::CheckMagicToken(IOSystem *pIOHandler, const std::string &pFile,
const void *_magic, std::size_t num, unsigned int offset, unsigned int size) {
ai_assert(size <= 16);
ai_assert(_magic);
if (!pIOHandler) {
return false;
}
union {
const char* magic;
const uint16_t* magic_u16;
const uint32_t* magic_u32;
const char *magic;
const uint16_t *magic_u16;
const uint32_t *magic_u32;
};
magic = reinterpret_cast<const char*>(_magic);
std::unique_ptr<IOStream> pStream (pIOHandler->Open(pFile));
if (pStream.get() ) {
magic = reinterpret_cast<const char *>(_magic);
std::unique_ptr<IOStream> pStream(pIOHandler->Open(pFile));
if (pStream) {
// skip to offset
pStream->Seek(offset,aiOrigin_SET);
pStream->Seek(offset, aiOrigin_SET);
// read 'size' characters from the file
union {
@ -307,7 +297,7 @@ std::string BaseImporter::GetExtension( const std::string& file ) {
uint16_t data_u16[8];
uint32_t data_u32[4];
};
if(size != pStream->Read(data,1,size)) {
if (size != pStream->Read(data, 1, size)) {
return false;
}
@ -321,17 +311,15 @@ std::string BaseImporter::GetExtension( const std::string& file ) {
if (data_u16[0] == *magic_u16 || data_u16[0] == rev) {
return true;
}
}
else if (4 == size) {
} else if (4 == size) {
uint32_t rev = *magic_u32;
ByteSwap::Swap(&rev);
if (data_u32[0] == *magic_u32 || data_u32[0] == rev) {
return true;
}
}
else {
} else {
// any length ... just compare
if(!memcmp(magic,data,size)) {
if (!memcmp(magic, data, size)) {
return true;
}
}
@ -342,61 +330,62 @@ std::string BaseImporter::GetExtension( const std::string& file ) {
}
#ifdef ASSIMP_USE_HUNTER
# include <utf8/utf8.h>
#include <utf8.h>
#else
# include "../contrib/utf8cpp/source/utf8.h"
#include "../contrib/utf8cpp/source/utf8.h"
#endif
// ------------------------------------------------------------------------------------------------
// Convert to UTF8 data
void BaseImporter::ConvertToUTF8(std::vector<char>& data)
{
void BaseImporter::ConvertToUTF8(std::vector<char> &data) {
//ConversionResult result;
if(data.size() < 8) {
if (data.size() < 8) {
throw DeadlyImportError("File is too small");
}
// UTF 8 with BOM
if((uint8_t)data[0] == 0xEF && (uint8_t)data[1] == 0xBB && (uint8_t)data[2] == 0xBF) {
if ((uint8_t)data[0] == 0xEF && (uint8_t)data[1] == 0xBB && (uint8_t)data[2] == 0xBF) {
ASSIMP_LOG_DEBUG("Found UTF-8 BOM ...");
std::copy(data.begin()+3,data.end(),data.begin());
data.resize(data.size()-3);
std::copy(data.begin() + 3, data.end(), data.begin());
data.resize(data.size() - 3);
return;
}
// UTF 32 BE with BOM
if(*((uint32_t*)&data.front()) == 0xFFFE0000) {
if (*((uint32_t *)&data.front()) == 0xFFFE0000) {
// swap the endianness ..
for(uint32_t* p = (uint32_t*)&data.front(), *end = (uint32_t*)&data.back(); p <= end; ++p) {
for (uint32_t *p = (uint32_t *)&data.front(), *end = (uint32_t *)&data.back(); p <= end; ++p) {
AI_SWAP4P(p);
}
}
// UTF 32 LE with BOM
if(*((uint32_t*)&data.front()) == 0x0000FFFE) {
if (*((uint32_t *)&data.front()) == 0x0000FFFE) {
ASSIMP_LOG_DEBUG("Found UTF-32 BOM ...");
std::vector<char> output;
int *ptr = (int*)&data[ 0 ];
int *end = ptr + ( data.size() / sizeof(int) ) +1;
utf8::utf32to8( ptr, end, back_inserter(output));
int *ptr = (int *)&data[0];
int *end = ptr + (data.size() / sizeof(int)) + 1;
utf8::utf32to8(ptr, end, back_inserter(output));
return;
}
// UTF 16 BE with BOM
if(*((uint16_t*)&data.front()) == 0xFFFE) {
if (*((uint16_t *)&data.front()) == 0xFFFE) {
// Check to ensure no overflow can happen
if(data.size() % 2 != 0) {
return;
}
// swap the endianness ..
for(uint16_t* p = (uint16_t*)&data.front(), *end = (uint16_t*)&data.back(); p <= end; ++p) {
for (uint16_t *p = (uint16_t *)&data.front(), *end = (uint16_t *)&data.back(); p <= end; ++p) {
ByteSwap::Swap2(p);
}
}
// UTF 16 LE with BOM
if(*((uint16_t*)&data.front()) == 0xFEFF) {
if (*((uint16_t *)&data.front()) == 0xFEFF) {
ASSIMP_LOG_DEBUG("Found UTF-16 BOM ...");
std::vector<unsigned char> output;
@ -407,23 +396,22 @@ void BaseImporter::ConvertToUTF8(std::vector<char>& data)
// ------------------------------------------------------------------------------------------------
// Convert to UTF8 data to ISO-8859-1
void BaseImporter::ConvertUTF8toISO8859_1(std::string& data)
{
void BaseImporter::ConvertUTF8toISO8859_1(std::string &data) {
size_t size = data.size();
size_t i = 0, j = 0;
while(i < size) {
if ((unsigned char) data[i] < (size_t) 0x80) {
while (i < size) {
if ((unsigned char)data[i] < (size_t)0x80) {
data[j] = data[i];
} else if(i < size - 1) {
if((unsigned char) data[i] == 0xC2) {
} else if (i < size - 1) {
if ((unsigned char)data[i] == 0xC2) {
data[j] = data[++i];
} else if((unsigned char) data[i] == 0xC3) {
data[j] = ((unsigned char) data[++i] + 0x40);
} else if ((unsigned char)data[i] == 0xC3) {
data[j] = ((unsigned char)data[++i] + 0x40);
} else {
std::stringstream stream;
stream << "UTF8 code " << std::hex << data[i] << data[i + 1] << " can not be converted into ISA-8859-1.";
ASSIMP_LOG_ERROR( stream.str() );
ASSIMP_LOG_ERROR(stream.str());
data[j++] = data[i++];
data[j] = data[i];
@ -434,30 +422,30 @@ void BaseImporter::ConvertUTF8toISO8859_1(std::string& data)
data[j] = data[i];
}
i++; j++;
i++;
j++;
}
data.resize(j);
}
// ------------------------------------------------------------------------------------------------
void BaseImporter::TextFileToBuffer(IOStream* stream,
std::vector<char>& data,
TextFileMode mode)
{
void BaseImporter::TextFileToBuffer(IOStream *stream,
std::vector<char> &data,
TextFileMode mode) {
ai_assert(nullptr != stream);
const size_t fileSize = stream->FileSize();
if (mode == FORBID_EMPTY) {
if(!fileSize) {
if (!fileSize) {
throw DeadlyImportError("File is empty");
}
}
data.reserve(fileSize+1);
data.reserve(fileSize + 1);
data.resize(fileSize);
if(fileSize > 0) {
if(fileSize != stream->Read( &data[0], 1, fileSize)) {
if (fileSize > 0) {
if (fileSize != stream->Read(&data[0], 1, fileSize)) {
throw DeadlyImportError("File read error");
}
@ -470,58 +458,55 @@ void BaseImporter::TextFileToBuffer(IOStream* stream,
// ------------------------------------------------------------------------------------------------
namespace Assimp {
// Represents an import request
struct LoadRequest {
LoadRequest(const std::string& _file, unsigned int _flags,const BatchLoader::PropertyMap* _map, unsigned int _id)
: file(_file)
, flags(_flags)
, refCnt(1)
, scene(NULL)
, loaded(false)
, id(_id) {
if ( _map ) {
map = *_map;
}
// Represents an import request
struct LoadRequest {
LoadRequest(const std::string &_file, unsigned int _flags, const BatchLoader::PropertyMap *_map, unsigned int _id) :
file(_file),
flags(_flags),
refCnt(1),
scene(nullptr),
loaded(false),
id(_id) {
if (_map) {
map = *_map;
}
}
bool operator== ( const std::string& f ) const {
return file == f;
}
bool operator==(const std::string &f) const {
return file == f;
}
const std::string file;
unsigned int flags;
unsigned int refCnt;
aiScene *scene;
bool loaded;
BatchLoader::PropertyMap map;
unsigned int id;
};
}
const std::string file;
unsigned int flags;
unsigned int refCnt;
aiScene *scene;
bool loaded;
BatchLoader::PropertyMap map;
unsigned int id;
};
} // namespace Assimp
// ------------------------------------------------------------------------------------------------
// BatchLoader::pimpl data structure
struct Assimp::BatchData {
BatchData( IOSystem* pIO, bool validate )
: pIOSystem( pIO )
, pImporter( nullptr )
, next_id(0xffff)
, validate( validate ) {
ai_assert( nullptr != pIO );
BatchData(IOSystem *pIO, bool validate) :
pIOSystem(pIO), pImporter(nullptr), next_id(0xffff), validate(validate) {
ai_assert(nullptr != pIO);
pImporter = new Importer();
pImporter->SetIOHandler( pIO );
pImporter->SetIOHandler(pIO);
}
~BatchData() {
pImporter->SetIOHandler( nullptr ); /* get pointer back into our possession */
pImporter->SetIOHandler(nullptr); /* get pointer back into our possession */
delete pImporter;
}
// IO system to be used for all imports
IOSystem* pIOSystem;
IOSystem *pIOSystem;
// Importer used to load all meshes
Importer* pImporter;
Importer *pImporter;
// List of all imports
std::list<LoadRequest> requests;
@ -539,24 +524,23 @@ struct Assimp::BatchData {
typedef std::list<LoadRequest>::iterator LoadReqIt;
// ------------------------------------------------------------------------------------------------
BatchLoader::BatchLoader(IOSystem* pIO, bool validate ) {
BatchLoader::BatchLoader(IOSystem *pIO, bool validate) {
ai_assert(nullptr != pIO);
m_data = new BatchData( pIO, validate );
m_data = new BatchData(pIO, validate);
}
// ------------------------------------------------------------------------------------------------
BatchLoader::~BatchLoader()
{
BatchLoader::~BatchLoader() {
// delete all scenes what have not been polled by the user
for ( LoadReqIt it = m_data->requests.begin();it != m_data->requests.end(); ++it) {
for (LoadReqIt it = m_data->requests.begin(); it != m_data->requests.end(); ++it) {
delete (*it).scene;
}
delete m_data;
}
// ------------------------------------------------------------------------------------------------
void BatchLoader::setValidation( bool enabled ) {
void BatchLoader::setValidation(bool enabled) {
m_data->validate = enabled;
}
@ -566,21 +550,19 @@ bool BatchLoader::getValidation() const {
}
// ------------------------------------------------------------------------------------------------
unsigned int BatchLoader::AddLoadRequest(const std::string& file,
unsigned int steps /*= 0*/, const PropertyMap* map /*= NULL*/)
{
unsigned int BatchLoader::AddLoadRequest(const std::string &file,
unsigned int steps /*= 0*/, const PropertyMap *map /*= nullptr*/) {
ai_assert(!file.empty());
// check whether we have this loading request already
for ( LoadReqIt it = m_data->requests.begin();it != m_data->requests.end(); ++it) {
for (LoadReqIt it = m_data->requests.begin(); it != m_data->requests.end(); ++it) {
// Call IOSystem's path comparison function here
if ( m_data->pIOSystem->ComparePaths((*it).file,file)) {
if (m_data->pIOSystem->ComparePaths((*it).file, file)) {
if (map) {
if ( !( ( *it ).map == *map ) ) {
if (!((*it).map == *map)) {
continue;
}
}
else if ( !( *it ).map.empty() ) {
} else if (!(*it).map.empty()) {
continue;
}
@ -590,17 +572,16 @@ unsigned int BatchLoader::AddLoadRequest(const std::string& file,
}
// no, we don't have it. So add it to the queue ...
m_data->requests.push_back(LoadRequest(file,steps,map, m_data->next_id));
m_data->requests.emplace_back(file, steps, map, m_data->next_id);
return m_data->next_id++;
}
// ------------------------------------------------------------------------------------------------
aiScene* BatchLoader::GetImport( unsigned int which )
{
for ( LoadReqIt it = m_data->requests.begin();it != m_data->requests.end(); ++it) {
if ((*it).id == which && (*it).loaded) {
aiScene* sc = (*it).scene;
if (!(--(*it).refCnt)) {
aiScene *BatchLoader::GetImport(unsigned int which) {
for (LoadReqIt it = m_data->requests.begin(); it != m_data->requests.end(); ++it) {
if ((*it).id == which && (*it).loaded) {
aiScene *sc = (*it).scene;
if (!(--(*it).refCnt)) {
m_data->requests.erase(it);
}
return sc;
@ -609,32 +590,28 @@ aiScene* BatchLoader::GetImport( unsigned int which )
return nullptr;
}
// ------------------------------------------------------------------------------------------------
void BatchLoader::LoadAll()
{
void BatchLoader::LoadAll() {
// no threaded implementation for the moment
for ( LoadReqIt it = m_data->requests.begin();it != m_data->requests.end(); ++it) {
for (LoadReqIt it = m_data->requests.begin(); it != m_data->requests.end(); ++it) {
// force validation in debug builds
unsigned int pp = (*it).flags;
if ( m_data->validate ) {
if (m_data->validate) {
pp |= aiProcess_ValidateDataStructure;
}
// setup config properties if necessary
ImporterPimpl* pimpl = m_data->pImporter->Pimpl();
pimpl->mFloatProperties = (*it).map.floats;
pimpl->mIntProperties = (*it).map.ints;
ImporterPimpl *pimpl = m_data->pImporter->Pimpl();
pimpl->mFloatProperties = (*it).map.floats;
pimpl->mIntProperties = (*it).map.ints;
pimpl->mStringProperties = (*it).map.strings;
pimpl->mMatrixProperties = (*it).map.matrices;
if (!DefaultLogger::isNullLogger())
{
if (!DefaultLogger::isNullLogger()) {
ASSIMP_LOG_INFO("%%% BEGIN EXTERNAL FILE %%%");
ASSIMP_LOG_INFO_F("File: ", (*it).file);
ASSIMP_LOG_INFO("File: ", (*it).file);
}
m_data->pImporter->ReadFile((*it).file,pp);
m_data->pImporter->ReadFile((*it).file, pp);
(*it).scene = m_data->pImporter->GetOrphanedScene();
(*it).loaded = true;

View file

@ -3,9 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -43,45 +41,43 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/** @file Implementation of BaseProcess */
#include <assimp/BaseImporter.h>
#include "BaseProcess.h"
#include <assimp/DefaultLogger.hpp>
#include <assimp/scene.h>
#include "Importer.h"
#include <assimp/BaseImporter.h>
#include <assimp/scene.h>
#include <assimp/DefaultLogger.hpp>
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
BaseProcess::BaseProcess() AI_NO_EXCEPT
: shared()
, progress()
{
: shared(),
progress() {
// empty
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
BaseProcess::~BaseProcess()
{
BaseProcess::~BaseProcess() {
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
void BaseProcess::ExecuteOnScene( Importer* pImp)
{
ai_assert(NULL != pImp && NULL != pImp->Pimpl()->mScene);
void BaseProcess::ExecuteOnScene(Importer *pImp) {
ai_assert( nullptr != pImp );
ai_assert( nullptr != pImp->Pimpl()->mScene);
progress = pImp->GetProgressHandler();
ai_assert(progress);
ai_assert(nullptr != progress);
SetupProperties( pImp );
SetupProperties(pImp);
// catch exceptions thrown inside the PostProcess-Step
try
{
try {
Execute(pImp->Pimpl()->mScene);
} catch( const std::exception& err ) {
} catch (const std::exception &err) {
// extract error description
pImp->Pimpl()->mErrorString = err.what();
@ -94,14 +90,11 @@ void BaseProcess::ExecuteOnScene( Importer* pImp)
}
// ------------------------------------------------------------------------------------------------
void BaseProcess::SetupProperties(const Importer* /*pImp*/)
{
void BaseProcess::SetupProperties(const Importer * /*pImp*/) {
// the default implementation does nothing
}
// ------------------------------------------------------------------------------------------------
bool BaseProcess::RequireVerboseFormat() const
{
bool BaseProcess::RequireVerboseFormat() const {
return true;
}

View file

@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -44,12 +43,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef INCLUDED_AI_BASEPROCESS_H
#define INCLUDED_AI_BASEPROCESS_H
#include <map>
#include <assimp/GenericProperty.h>
#include <map>
struct aiScene;
namespace Assimp {
namespace Assimp {
class Importer;
@ -60,64 +60,50 @@ class Importer;
* to provide additional information to other steps. This is primarily
* intended for cross-step optimizations.
*/
class SharedPostProcessInfo
{
class SharedPostProcessInfo {
public:
struct Base
{
virtual ~Base()
{}
struct Base {
virtual ~Base() {}
};
//! Represents data that is allocated on the heap, thus needs to be deleted
template <typename T>
struct THeapData : public Base
{
explicit THeapData(T* in)
: data (in)
{}
struct THeapData : public Base {
explicit THeapData(T *in) :
data(in) {}
~THeapData()
{
~THeapData() {
delete data;
}
T* data;
T *data;
};
//! Represents static, by-value data not allocated on the heap
template <typename T>
struct TStaticData : public Base
{
explicit TStaticData(T in)
: data (in)
{}
struct TStaticData : public Base {
explicit TStaticData(T in) :
data(in) {}
~TStaticData()
{}
~TStaticData() {}
T data;
};
// some typedefs for cleaner code
typedef unsigned int KeyType;
typedef std::map<KeyType, Base*> PropertyMap;
typedef std::map<KeyType, Base *> PropertyMap;
public:
//! Destructor
~SharedPostProcessInfo()
{
~SharedPostProcessInfo() {
Clean();
}
//! Remove all stored properties from the table
void Clean()
{
void Clean() {
// invoke the virtual destructor for all stored properties
for (PropertyMap::iterator it = pmap.begin(), end = pmap.end();
it != end; ++it)
{
it != end; ++it) {
delete (*it).second;
}
pmap.clear();
@ -125,25 +111,22 @@ public:
//! Add a heap property to the list
template <typename T>
void AddProperty( const char* name, T* in ){
AddProperty(name,(Base*)new THeapData<T>(in));
void AddProperty(const char *name, T *in) {
AddProperty(name, (Base *)new THeapData<T>(in));
}
//! Add a static by-value property to the list
template <typename T>
void AddProperty( const char* name, T in ){
AddProperty(name,(Base*)new TStaticData<T>(in));
void AddProperty(const char *name, T in) {
AddProperty(name, (Base *)new TStaticData<T>(in));
}
//! Get a heap property
template <typename T>
bool GetProperty( const char* name, T*& out ) const
{
THeapData<T>* t = (THeapData<T>*)GetPropertyInternal(name);
if(!t)
{
out = NULL;
bool GetProperty(const char *name, T *&out) const {
THeapData<T> *t = (THeapData<T> *)GetPropertyInternal(name);
if (!t) {
out = nullptr;
return false;
}
out = t->data;
@ -152,53 +135,34 @@ public:
//! Get a static, by-value property
template <typename T>
bool GetProperty( const char* name, T& out ) const
{
TStaticData<T>* t = (TStaticData<T>*)GetPropertyInternal(name);
if(!t)return false;
bool GetProperty(const char *name, T &out) const {
TStaticData<T> *t = (TStaticData<T> *)GetPropertyInternal(name);
if ( nullptr == t) {
return false;
}
out = t->data;
return true;
}
//! Remove a property of a specific type
void RemoveProperty( const char* name) {
SetGenericPropertyPtr<Base>(pmap,name,NULL);
void RemoveProperty(const char *name) {
SetGenericPropertyPtr<Base>(pmap, name, nullptr );
}
private:
void AddProperty( const char* name, Base* data) {
SetGenericPropertyPtr<Base>(pmap,name,data);
void AddProperty(const char *name, Base *data) {
SetGenericPropertyPtr<Base>(pmap, name, data);
}
Base* GetPropertyInternal( const char* name) const {
return GetGenericProperty<Base*>(pmap,name,NULL);
Base *GetPropertyInternal(const char *name) const {
return GetGenericProperty<Base *>(pmap, name, nullptr );
}
private:
//! Map of all stored properties
PropertyMap pmap;
};
#if 0
// ---------------------------------------------------------------------------
/** @brief Represents a dependency table for a postprocessing steps.
*
* For future use.
*/
struct PPDependencyTable
{
unsigned int execute_me_before_these;
unsigned int execute_me_after_these;
unsigned int only_if_these_are_not_specified;
unsigned int mutually_exclusive_with;
};
#endif
#define AI_SPP_SPATIAL_SORT "$Spat"
// ---------------------------------------------------------------------------
@ -228,7 +192,7 @@ public:
* @return true if the process is present in this flag fields,
* false if not.
*/
virtual bool IsActive( unsigned int pFlags) const = 0;
virtual bool IsActive(unsigned int pFlags) const = 0;
// -------------------------------------------------------------------
/** Check whether this step expects its input vertex data to be
@ -238,17 +202,17 @@ public:
// -------------------------------------------------------------------
/** Executes the post processing step on the given imported data.
* The function deletes the scene if the postprocess step fails (
* the object pointer will be set to NULL).
* the object pointer will be set to nullptr).
* @param pImp Importer instance (pImp->mScene must be valid)
*/
void ExecuteOnScene( Importer* pImp);
void ExecuteOnScene(Importer *pImp);
// -------------------------------------------------------------------
/** Called prior to ExecuteOnScene().
* The function is a request to the process to update its configuration
* basing on the Importer's configuration property list.
*/
virtual void SetupProperties(const Importer* pImp);
virtual void SetupProperties(const Importer *pImp);
// -------------------------------------------------------------------
/** Executes the post processing step on the given imported data.
@ -256,35 +220,32 @@ public:
* This method must be implemented by deriving classes.
* @param pScene The imported data to work at.
*/
virtual void Execute( aiScene* pScene) = 0;
virtual void Execute(aiScene *pScene) = 0;
// -------------------------------------------------------------------
/** Assign a new SharedPostProcessInfo to the step. This object
* allows multiple postprocess steps to share data.
* @param sh May be NULL
* @param sh May be nullptr
*/
inline void SetSharedData(SharedPostProcessInfo* sh) {
inline void SetSharedData(SharedPostProcessInfo *sh) {
shared = sh;
}
// -------------------------------------------------------------------
/** Get the shared data that is assigned to the step.
*/
inline SharedPostProcessInfo* GetSharedData() {
inline SharedPostProcessInfo *GetSharedData() {
return shared;
}
protected:
/** See the doc of #SharedPostProcessInfo for more details */
SharedPostProcessInfo* shared;
SharedPostProcessInfo *shared;
/** Currently active progress handler */
ProgressHandler* progress;
ProgressHandler *progress;
};
} // end of namespace Assimp
#endif // AI_BASEPROCESS_H_INC

View file

@ -3,9 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -47,109 +45,112 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* Used for file formats which embed their textures into the model file.
*/
#include <assimp/Bitmap.h>
#include <assimp/ByteSwapper.h>
#include <assimp/texture.h>
#include <assimp/IOStream.hpp>
#include <assimp/ByteSwapper.h>
namespace Assimp {
void Bitmap::Save(aiTexture* texture, IOStream* file) {
if(file != NULL) {
Header header;
DIB dib;
dib.size = DIB::dib_size;
dib.width = texture->mWidth;
dib.height = texture->mHeight;
dib.planes = 1;
dib.bits_per_pixel = 8 * mBytesPerPixel;
dib.compression = 0;
dib.image_size = (((dib.width * mBytesPerPixel) + 3) & 0x0000FFFC) * dib.height;
dib.x_resolution = 0;
dib.y_resolution = 0;
dib.nb_colors = 0;
dib.nb_important_colors = 0;
header.type = 0x4D42; // 'BM'
header.offset = Header::header_size + DIB::dib_size;
header.size = header.offset + dib.image_size;
header.reserved1 = 0;
header.reserved2 = 0;
WriteHeader(header, file);
WriteDIB(dib, file);
WriteData(texture, file);
}
bool Bitmap::Save(aiTexture *texture, IOStream *file) {
if (file == nullptr) {
return false;
}
template<typename T>
inline
std::size_t Copy(uint8_t* data, const T &field) {
#ifdef AI_BUILD_BIG_ENDIAN
T field_swapped=AI_BE(field);
std::memcpy(data, &field_swapped, sizeof(field)); return sizeof(field);
#else
std::memcpy(data, &AI_BE(field), sizeof(field)); return sizeof(field);
#endif
}
Header header;
DIB dib;
dib.size = DIB::dib_size;
dib.width = texture->mWidth;
dib.height = texture->mHeight;
dib.planes = 1;
dib.bits_per_pixel = 8 * mBytesPerPixel;
dib.compression = 0;
dib.image_size = (((dib.width * mBytesPerPixel) + 3) & 0x0000FFFC) * dib.height;
dib.x_resolution = 0;
dib.y_resolution = 0;
dib.nb_colors = 0;
dib.nb_important_colors = 0;
void Bitmap::WriteHeader(Header& header, IOStream* file) {
uint8_t data[Header::header_size];
header.type = 0x4D42; // 'BM'
header.offset = Header::header_size + DIB::dib_size;
header.size = header.offset + dib.image_size;
header.reserved1 = 0;
header.reserved2 = 0;
std::size_t offset = 0;
offset += Copy(&data[offset], header.type);
offset += Copy(&data[offset], header.size);
offset += Copy(&data[offset], header.reserved1);
offset += Copy(&data[offset], header.reserved2);
Copy(&data[offset], header.offset);
file->Write(data, Header::header_size, 1);
}
void Bitmap::WriteDIB(DIB& dib, IOStream* file) {
uint8_t data[DIB::dib_size];
std::size_t offset = 0;
offset += Copy(&data[offset], dib.size);
offset += Copy(&data[offset], dib.width);
offset += Copy(&data[offset], dib.height);
offset += Copy(&data[offset], dib.planes);
offset += Copy(&data[offset], dib.bits_per_pixel);
offset += Copy(&data[offset], dib.compression);
offset += Copy(&data[offset], dib.image_size);
offset += Copy(&data[offset], dib.x_resolution);
offset += Copy(&data[offset], dib.y_resolution);
offset += Copy(&data[offset], dib.nb_colors);
Copy(&data[offset], dib.nb_important_colors);
file->Write(data, DIB::dib_size, 1);
}
void Bitmap::WriteData(aiTexture* texture, IOStream* file) {
static const std::size_t padding_offset = 4;
static const uint8_t padding_data[padding_offset] = {0x0, 0x0, 0x0, 0x0};
unsigned int padding = (padding_offset - ((mBytesPerPixel * texture->mWidth) % padding_offset)) % padding_offset;
uint8_t pixel[mBytesPerPixel];
for(std::size_t i = 0; i < texture->mHeight; ++i) {
for(std::size_t j = 0; j < texture->mWidth; ++j) {
const aiTexel& texel = texture->pcData[(texture->mHeight - i - 1) * texture->mWidth + j]; // Bitmap files are stored in bottom-up format
pixel[0] = texel.r;
pixel[1] = texel.g;
pixel[2] = texel.b;
pixel[3] = texel.a;
file->Write(pixel, mBytesPerPixel, 1);
}
file->Write(padding_data, padding, 1);
}
}
WriteHeader(header, file);
WriteDIB(dib, file);
WriteData(texture, file);
return true;
}
template <typename T>
inline std::size_t Copy(uint8_t *data, const T &field) {
#ifdef AI_BUILD_BIG_ENDIAN
T field_swapped = AI_BE(field);
std::memcpy(data, &field_swapped, sizeof(field));
return sizeof(field);
#else
std::memcpy(data, &AI_BE(field), sizeof(field));
return sizeof(field);
#endif
}
void Bitmap::WriteHeader(Header &header, IOStream *file) {
uint8_t data[Header::header_size];
std::size_t offset = 0;
offset += Copy(&data[offset], header.type);
offset += Copy(&data[offset], header.size);
offset += Copy(&data[offset], header.reserved1);
offset += Copy(&data[offset], header.reserved2);
Copy(&data[offset], header.offset);
file->Write(data, Header::header_size, 1);
}
void Bitmap::WriteDIB(DIB &dib, IOStream *file) {
uint8_t data[DIB::dib_size];
std::size_t offset = 0;
offset += Copy(&data[offset], dib.size);
offset += Copy(&data[offset], dib.width);
offset += Copy(&data[offset], dib.height);
offset += Copy(&data[offset], dib.planes);
offset += Copy(&data[offset], dib.bits_per_pixel);
offset += Copy(&data[offset], dib.compression);
offset += Copy(&data[offset], dib.image_size);
offset += Copy(&data[offset], dib.x_resolution);
offset += Copy(&data[offset], dib.y_resolution);
offset += Copy(&data[offset], dib.nb_colors);
Copy(&data[offset], dib.nb_important_colors);
file->Write(data, DIB::dib_size, 1);
}
void Bitmap::WriteData(aiTexture *texture, IOStream *file) {
static const std::size_t padding_offset = 4;
static const uint8_t padding_data[padding_offset] = { 0x0, 0x0, 0x0, 0x0 };
unsigned int padding = (padding_offset - ((mBytesPerPixel * texture->mWidth) % padding_offset)) % padding_offset;
uint8_t pixel[mBytesPerPixel];
for (std::size_t i = 0; i < texture->mHeight; ++i) {
for (std::size_t j = 0; j < texture->mWidth; ++j) {
const aiTexel &texel = texture->pcData[(texture->mHeight - i - 1) * texture->mWidth + j]; // Bitmap files are stored in bottom-up format
pixel[0] = texel.r;
pixel[1] = texel.g;
pixel[2] = texel.b;
pixel[3] = texel.a;
file->Write(pixel, mBytesPerPixel, 1);
}
file->Write(padding_data, padding, 1);
}
}
} // namespace Assimp

View file

@ -0,0 +1,214 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, 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 "Compression.h"
#include <assimp/ai_assert.h>
#include <assimp/Exceptional.h>
namespace Assimp {
struct Compression::impl {
bool mOpen;
z_stream mZSstream;
FlushMode mFlushMode;
impl() :
mOpen(false),
mZSstream(),
mFlushMode(Compression::FlushMode::NoFlush) {
// empty
}
};
Compression::Compression() :
mImpl(new impl) {
// empty
}
Compression::~Compression() {
ai_assert(mImpl != nullptr);
delete mImpl;
}
bool Compression::open(Format format, FlushMode flush, int windowBits) {
ai_assert(mImpl != nullptr);
if (mImpl->mOpen) {
return false;
}
// build a zlib stream
mImpl->mZSstream.opaque = Z_NULL;
mImpl->mZSstream.zalloc = Z_NULL;
mImpl->mZSstream.zfree = Z_NULL;
mImpl->mFlushMode = flush;
if (format == Format::Binary) {
mImpl->mZSstream.data_type = Z_BINARY;
} else {
mImpl->mZSstream.data_type = Z_ASCII;
}
// raw decompression without a zlib or gzip header
if (windowBits == 0) {
inflateInit(&mImpl->mZSstream);
} else {
inflateInit2(&mImpl->mZSstream, windowBits);
}
mImpl->mOpen = true;
return mImpl->mOpen;
}
static int getFlushMode(Compression::FlushMode flush) {
int z_flush = 0;
switch (flush) {
case Compression::FlushMode::NoFlush:
z_flush = Z_NO_FLUSH;
break;
case Compression::FlushMode::Block:
z_flush = Z_BLOCK;
break;
case Compression::FlushMode::Tree:
z_flush = Z_TREES;
break;
case Compression::FlushMode::SyncFlush:
z_flush = Z_SYNC_FLUSH;
break;
case Compression::FlushMode::Finish:
z_flush = Z_FINISH;
break;
default:
ai_assert(false);
break;
}
return z_flush;
}
constexpr size_t MYBLOCK = 32786;
size_t Compression::decompress(const void *data, size_t in, std::vector<char> &uncompressed) {
ai_assert(mImpl != nullptr);
if (data == nullptr || in == 0) {
return 0l;
}
mImpl->mZSstream.next_in = (Bytef*)(data);
mImpl->mZSstream.avail_in = (uInt)in;
int ret = 0;
size_t total = 0l;
const int flushMode = getFlushMode(mImpl->mFlushMode);
if (flushMode == Z_FINISH) {
mImpl->mZSstream.avail_out = static_cast<uInt>(uncompressed.size());
mImpl->mZSstream.next_out = reinterpret_cast<Bytef *>(&*uncompressed.begin());
ret = inflate(&mImpl->mZSstream, Z_FINISH);
if (ret != Z_STREAM_END && ret != Z_OK) {
throw DeadlyImportError("Compression", "Failure decompressing this file using gzip.");
}
total = mImpl->mZSstream.avail_out;
} else {
do {
Bytef block[MYBLOCK] = {};
mImpl->mZSstream.avail_out = MYBLOCK;
mImpl->mZSstream.next_out = block;
ret = inflate(&mImpl->mZSstream, flushMode);
if (ret != Z_STREAM_END && ret != Z_OK) {
throw DeadlyImportError("Compression", "Failure decompressing this file using gzip.");
}
const size_t have = MYBLOCK - mImpl->mZSstream.avail_out;
total += have;
uncompressed.resize(total);
::memcpy(uncompressed.data() + total - have, block, have);
} while (ret != Z_STREAM_END);
}
return total;
}
size_t Compression::decompressBlock(const void *data, size_t in, char *out, size_t availableOut) {
ai_assert(mImpl != nullptr);
if (data == nullptr || in == 0 || out == nullptr || availableOut == 0) {
return 0l;
}
// push data to the stream
mImpl->mZSstream.next_in = (Bytef *)data;
mImpl->mZSstream.avail_in = (uInt)in;
mImpl->mZSstream.next_out = (Bytef *)out;
mImpl->mZSstream.avail_out = (uInt)availableOut;
// and decompress the data ....
int ret = ::inflate(&mImpl->mZSstream, Z_SYNC_FLUSH);
if (ret != Z_OK && ret != Z_STREAM_END) {
throw DeadlyImportError("X: Failed to decompress MSZIP-compressed data");
}
::inflateReset(&mImpl->mZSstream);
::inflateSetDictionary(&mImpl->mZSstream, (const Bytef *)out, (uInt)availableOut - mImpl->mZSstream.avail_out);
return availableOut - (size_t)mImpl->mZSstream.avail_out;
}
bool Compression::isOpen() const {
ai_assert(mImpl != nullptr);
return mImpl->mOpen;
}
bool Compression::close() {
ai_assert(mImpl != nullptr);
if (!mImpl->mOpen) {
return false;
}
inflateEnd(&mImpl->mZSstream);
mImpl->mOpen = false;
return true;
}
} // namespace Assimp

View file

@ -0,0 +1,121 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, 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.
----------------------------------------------------------------------
*/
#pragma once
#ifdef ASSIMP_BUILD_NO_OWN_ZLIB
#include <zlib.h>
#else
#include "../contrib/zlib/zlib.h"
#endif
#include <vector>
#include <cstddef> // size_t
namespace Assimp {
/// @brief This class provides the decompression of zlib-compressed data.
class Compression {
public:
static const int MaxWBits = MAX_WBITS;
/// @brief Describes the format data type
enum class Format {
InvalidFormat = -1, ///< Invalid enum type.
Binary = 0, ///< Binary format.
ASCII, ///< ASCII format.
NumFormats ///< The number of supported formats.
};
/// @brief The supported flush mode, used for blocked access.
enum class FlushMode {
InvalidFormat = -1, ///< Invalid enum type.
NoFlush = 0, ///< No flush, will be done on inflate end.
Block, ///< Assists in combination of compress.
Tree, ///< Assists in combination of compress and returns if stream is finish.
SyncFlush, ///< Synced flush mode.
Finish, ///< Finish mode, all in once, no block access.
NumModes ///< The number of supported modes.
};
/// @brief The class constructor.
Compression();
/// @brief The class destructor.
~Compression();
/// @brief Will open the access to the compression.
/// @param[in] format The format type
/// @param[in] flush The flush mode.
/// @param[in] windowBits The windows history working size, shall be between 8 and 15.
/// @return true if close was successful, false if not.
bool open(Format format, FlushMode flush, int windowBits);
/// @brief Will return the open state.
/// @return true if the access is opened, false if not.
bool isOpen() const;
/// @brief Will close the decompress access.
/// @return true if close was successful, false if not.
bool close();
/// @brief Will decompress the data buffer in one step.
/// @param[in] data The data to decompress
/// @param[in] in The size of the data.
/// @param[out uncompressed A std::vector containing the decompressed data.
size_t decompress(const void *data, size_t in, std::vector<char> &uncompressed);
/// @brief Will decompress the data buffer block-wise.
/// @param[in] data The compressed data
/// @param[in] in The size of the data buffer
/// @param[out] out The output buffer
/// @param[out] availableOut The upper limit of the output buffer.
/// @return The size of the decompressed data buffer.
size_t decompressBlock(const void *data, size_t in, char *out, size_t availableOut);
private:
struct impl;
impl *mImpl;
};
} // namespace Assimp

View file

@ -4,7 +4,7 @@ Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (C) 2016 The Qt Company Ltd.
Copyright (c) 2006-2012, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -44,42 +44,46 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace Assimp {
aiAnimMesh *aiCreateAnimMesh(const aiMesh *mesh)
aiAnimMesh *aiCreateAnimMesh(const aiMesh *mesh, bool needPositions, bool needNormals, bool needTangents, bool needColors, bool needTexCoords)
{
aiAnimMesh *animesh = new aiAnimMesh;
animesh->mNumVertices = mesh->mNumVertices;
if (mesh->mVertices) {
if (needPositions && mesh->mVertices) {
animesh->mVertices = new aiVector3D[animesh->mNumVertices];
std::memcpy(animesh->mVertices, mesh->mVertices, mesh->mNumVertices * sizeof(aiVector3D));
}
if (mesh->mNormals) {
if (needNormals && mesh->mNormals) {
animesh->mNormals = new aiVector3D[animesh->mNumVertices];
std::memcpy(animesh->mNormals, mesh->mNormals, mesh->mNumVertices * sizeof(aiVector3D));
}
if (mesh->mTangents) {
if (needTangents && mesh->mTangents) {
animesh->mTangents = new aiVector3D[animesh->mNumVertices];
std::memcpy(animesh->mTangents, mesh->mTangents, mesh->mNumVertices * sizeof(aiVector3D));
}
if (mesh->mBitangents) {
if (needTangents && mesh->mBitangents) {
animesh->mBitangents = new aiVector3D[animesh->mNumVertices];
std::memcpy(animesh->mBitangents, mesh->mBitangents, mesh->mNumVertices * sizeof(aiVector3D));
}
for (int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS; ++i) {
if (mesh->mColors[i]) {
animesh->mColors[i] = new aiColor4D[animesh->mNumVertices];
std::memcpy(animesh->mColors[i], mesh->mColors[i], mesh->mNumVertices * sizeof(aiColor4D));
} else {
animesh->mColors[i] = NULL;
if (needColors) {
for (int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS; ++i) {
if (mesh->mColors[i]) {
animesh->mColors[i] = new aiColor4D[animesh->mNumVertices];
std::memcpy(animesh->mColors[i], mesh->mColors[i], mesh->mNumVertices * sizeof(aiColor4D));
} else {
animesh->mColors[i] = nullptr;
}
}
}
for (int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
if (mesh->mTextureCoords[i]) {
animesh->mTextureCoords[i] = new aiVector3D[animesh->mNumVertices];
std::memcpy(animesh->mTextureCoords[i], mesh->mTextureCoords[i], mesh->mNumVertices * sizeof(aiVector3D));
} else {
animesh->mTextureCoords[i] = NULL;
if (needTexCoords) {
for (int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
if (mesh->mTextureCoords[i]) {
animesh->mTextureCoords[i] = new aiVector3D[animesh->mNumVertices];
std::memcpy(animesh->mTextureCoords[i], mesh->mTextureCoords[i], mesh->mNumVertices * sizeof(aiVector3D));
} else {
animesh->mTextureCoords[i] = nullptr;
}
}
}
return animesh;

View file

@ -3,9 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -44,75 +42,104 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* @brief Default File I/O implementation for #Importer
*/
#include <assimp/ai_assert.h>
#include <assimp/DefaultIOStream.h>
#include <sys/types.h>
#include <assimp/ai_assert.h>
#include <sys/stat.h>
#include <sys/types.h>
using namespace Assimp;
namespace {
template <size_t sizeOfPointer>
inline size_t select_ftell(FILE *file) {
return ::ftell(file);
}
template <size_t sizeOfPointer>
inline 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 <>
inline size_t select_ftell<8>(FILE *file) {
return (size_t)::_ftelli64(file);
}
template <>
inline int select_fseek<8>(FILE *file, int64_t offset, int origin) {
return ::_fseeki64(file, offset, origin);
}
#endif // #if defined _WIN32 && (!defined __GNUC__ || __MSVCRT_VERSION__ >= 0x0601)
} // namespace
// ----------------------------------------------------------------------------------
DefaultIOStream::~DefaultIOStream()
{
DefaultIOStream::~DefaultIOStream() {
if (mFile) {
::fclose(mFile);
mFile = nullptr;
}
}
// ----------------------------------------------------------------------------------
size_t DefaultIOStream::Read(void* pvBuffer,
size_t pSize,
size_t pCount)
{
ai_assert(NULL != pvBuffer && 0 != pSize && 0 != pCount);
size_t DefaultIOStream::Read(void *pvBuffer,
size_t pSize,
size_t pCount) {
if (0 == pCount) {
return 0;
}
ai_assert(nullptr != pvBuffer);
ai_assert(0 != pSize);
return (mFile ? ::fread(pvBuffer, pSize, pCount, mFile) : 0);
}
// ----------------------------------------------------------------------------------
size_t DefaultIOStream::Write(const void* pvBuffer,
size_t pSize,
size_t pCount)
{
ai_assert(NULL != pvBuffer && 0 != pSize && 0 != pCount);
size_t DefaultIOStream::Write(const void *pvBuffer,
size_t pSize,
size_t pCount) {
ai_assert(nullptr != pvBuffer);
ai_assert(0 != pSize);
return (mFile ? ::fwrite(pvBuffer, pSize, pCount, mFile) : 0);
}
// ----------------------------------------------------------------------------------
aiReturn DefaultIOStream::Seek(size_t pOffset,
aiOrigin pOrigin)
{
aiOrigin pOrigin) {
if (!mFile) {
return AI_FAILURE;
}
// Just to check whether our enum maps one to one with the CRT constants
static_assert(aiOrigin_CUR == SEEK_CUR &&
aiOrigin_END == SEEK_END && aiOrigin_SET == SEEK_SET, "aiOrigin_CUR == SEEK_CUR && \
aiOrigin_END == SEEK_END && aiOrigin_SET == SEEK_SET,
"aiOrigin_CUR == SEEK_CUR && \
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);
}
// ----------------------------------------------------------------------------------
size_t DefaultIOStream::Tell() const
{
size_t DefaultIOStream::Tell() const {
if (!mFile) {
return 0;
}
return ::ftell(mFile);
return select_ftell<sizeof(void *)>(mFile);
}
// ----------------------------------------------------------------------------------
size_t DefaultIOStream::FileSize() const
{
if (! mFile || mFilename.empty()) {
size_t DefaultIOStream::FileSize() const {
if (!mFile || mFilename.empty()) {
return 0;
}
if (SIZE_MAX == mCachedSize ) {
if (SIZE_MAX == mCachedSize) {
// Although fseek/ftell would allow us to reuse the existing file handle here,
// it is generally unsafe because:
@ -125,27 +152,26 @@ size_t DefaultIOStream::FileSize() const
#if defined _WIN32 && (!defined __GNUC__ || __MSVCRT_VERSION__ >= 0x0601)
struct __stat64 fileStat;
//using fileno + fstat avoids having to handle the filename
int err = _fstat64( _fileno(mFile), &fileStat );
int err = _fstat64(_fileno(mFile), &fileStat);
if (0 != err)
return 0;
mCachedSize = (size_t) (fileStat.st_size);
mCachedSize = (size_t)(fileStat.st_size);
#elif defined __GNUC__ || defined __APPLE__ || defined __MACH__ || defined __FreeBSD__
struct stat fileStat;
int err = stat(mFilename.c_str(), &fileStat );
int err = stat(mFilename.c_str(), &fileStat);
if (0 != err)
return 0;
const unsigned long long cachedSize = fileStat.st_size;
mCachedSize = static_cast< size_t >( cachedSize );
mCachedSize = static_cast<size_t>(cachedSize);
#else
# error "Unknown platform"
#error "Unknown platform"
#endif
}
return mCachedSize;
}
// ----------------------------------------------------------------------------------
void DefaultIOStream::Flush()
{
void DefaultIOStream::Flush() {
if (mFile) {
::fflush(mFile);
}

View file

@ -3,9 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -44,91 +42,106 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/StringComparison.h>
#include <assimp/DefaultIOSystem.h>
#include <assimp/DefaultIOStream.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/DefaultIOSystem.h>
#include <assimp/ai_assert.h>
#include <stdlib.h>
#include <assimp/DefaultLogger.hpp>
#ifdef __unix__
#include <sys/param.h>
#include <stdlib.h>
# include <stdlib.h>
# include <sys/param.h>
#endif
#ifdef _WIN32
#include <windows.h>
# include <windows.h>
#endif
using namespace Assimp;
#ifdef _WIN32
static std::wstring Utf8ToWide(const char* in)
{
const std::wstring wdummy;
static std::wstring Utf8ToWide(const char *in) {
if (nullptr == in) {
return wdummy;
}
int size = MultiByteToWideChar(CP_UTF8, 0, in, -1, nullptr, 0);
// size includes terminating null; std::wstring adds null automatically
std::wstring out(static_cast<size_t>(size) - 1, L'\0');
MultiByteToWideChar(CP_UTF8, 0, in, -1, &out[0], size);
return out;
}
static std::string WideToUtf8(const wchar_t* in)
{
const std::string dummy;
static std::string WideToUtf8(const wchar_t *in) {
if (nullptr == in) {
return dummy;
}
int size = WideCharToMultiByte(CP_UTF8, 0, in, -1, nullptr, 0, nullptr, nullptr);
// size includes terminating null; std::string adds null automatically
std::string out(static_cast<size_t>(size) - 1, '\0');
WideCharToMultiByte(CP_UTF8, 0, in, -1, &out[0], size, nullptr, nullptr);
return out;
}
#endif
// ------------------------------------------------------------------------------------------------
// Tests for the existence of a file at the given path.
bool DefaultIOSystem::Exists(const char* pFile) const
{
bool DefaultIOSystem::Exists(const char *pFile) const {
#ifdef _WIN32
struct __stat64 filestat;
if (_wstat64(Utf8ToWide(pFile).c_str(), &filestat) != 0) {
return false;
}
#else
FILE* file = ::fopen(pFile, "rb");
if (!file)
FILE *file = ::fopen(pFile, "rb");
if (!file) {
return false;
}
::fclose(file);
#endif
return true;
}
// ------------------------------------------------------------------------------------------------
// Open a new file with a given path.
IOStream* DefaultIOSystem::Open(const char* strFile, const char* strMode)
{
IOStream *DefaultIOSystem::Open(const char *strFile, const char *strMode) {
ai_assert(strFile != nullptr);
ai_assert(strMode != nullptr);
FILE* file;
FILE *file;
#ifdef _WIN32
file = ::_wfopen(Utf8ToWide(strFile).c_str(), Utf8ToWide(strMode).c_str());
std::wstring name = Utf8ToWide(strFile);
if (name.empty()) {
return nullptr;
}
file = ::_wfopen(name.c_str(), Utf8ToWide(strMode).c_str());
#else
file = ::fopen(strFile, strMode);
#endif
if (!file)
if (!file) {
return nullptr;
}
return new DefaultIOStream(file, strFile);
}
// ------------------------------------------------------------------------------------------------
// Closes the given file and releases all resources associated with it.
void DefaultIOSystem::Close(IOStream* pFile)
{
void DefaultIOSystem::Close(IOStream *pFile) {
delete pFile;
}
// ------------------------------------------------------------------------------------------------
// Returns the operation specific directory separator
char DefaultIOSystem::getOsSeparator() const
{
char DefaultIOSystem::getOsSeparator() const {
#ifndef _WIN32
return '/';
#else
@ -138,34 +151,32 @@ char DefaultIOSystem::getOsSeparator() const
// ------------------------------------------------------------------------------------------------
// IOSystem default implementation (ComparePaths isn't a pure virtual function)
bool IOSystem::ComparePaths(const char* one, const char* second) const
{
bool IOSystem::ComparePaths(const char *one, const char *second) const {
return !ASSIMP_stricmp(one, second);
}
// ------------------------------------------------------------------------------------------------
// Convert a relative path into an absolute path
inline static std::string MakeAbsolutePath(const char* in)
{
inline static std::string MakeAbsolutePath(const char *in) {
ai_assert(in);
std::string out;
#ifdef _WIN32
wchar_t* ret = ::_wfullpath(nullptr, Utf8ToWide(in).c_str(), 0);
wchar_t *ret = ::_wfullpath(nullptr, Utf8ToWide(in).c_str(), 0);
if (ret) {
out = WideToUtf8(ret);
free(ret);
}
#else
char* ret = realpath(in, nullptr);
char *ret = realpath(in, nullptr);
if (ret) {
out = ret;
free(ret);
}
#endif
if (!ret) {
else {
// preserve the input path, maybe someone else is able to fix
// the path before it is accessed (e.g. our file system filter)
ASSIMP_LOG_WARN_F("Invalid path: ", std::string(in));
ASSIMP_LOG_WARN("Invalid path: ", std::string(in));
out = in;
}
return out;
@ -173,8 +184,7 @@ inline static std::string MakeAbsolutePath(const char* in)
// ------------------------------------------------------------------------------------------------
// DefaultIOSystem's more specialized implementation
bool DefaultIOSystem::ComparePaths(const char* one, const char* second) const
{
bool DefaultIOSystem::ComparePaths(const char *one, const char *second) const {
// chances are quite good both paths are formatted identically,
// so we can hopefully return here already
if (!ASSIMP_stricmp(one, second))
@ -187,8 +197,7 @@ bool DefaultIOSystem::ComparePaths(const char* one, const char* second) const
}
// ------------------------------------------------------------------------------------------------
std::string DefaultIOSystem::fileName(const std::string& path)
{
std::string DefaultIOSystem::fileName(const std::string &path) {
std::string ret = path;
std::size_t last = ret.find_last_of("\\/");
if (last != std::string::npos) ret = ret.substr(last + 1);
@ -196,8 +205,7 @@ std::string DefaultIOSystem::fileName(const std::string& path)
}
// ------------------------------------------------------------------------------------------------
std::string DefaultIOSystem::completeBaseName(const std::string& path)
{
std::string DefaultIOSystem::completeBaseName(const std::string &path) {
std::string ret = fileName(path);
std::size_t pos = ret.find_last_of('.');
if (pos != std::string::npos) ret = ret.substr(0, pos);
@ -205,8 +213,7 @@ std::string DefaultIOSystem::completeBaseName(const std::string& path)
}
// ------------------------------------------------------------------------------------------------
std::string DefaultIOSystem::absolutePath(const std::string& path)
{
std::string DefaultIOSystem::absolutePath(const std::string &path) {
std::string ret = path;
std::size_t last = ret.find_last_of("\\/");
if (last != std::string::npos) ret = ret.substr(0, last);

View file

@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
@ -46,25 +46,25 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// Default log streams
#include "Win32DebugLogStream.h"
#include "StdOStreamLogStream.h"
#include "FileLogStream.h"
#include "StdOStreamLogStream.h"
#include "Win32DebugLogStream.h"
#include <assimp/StringUtils.h>
#include <assimp/DefaultIOSystem.h>
#include <assimp/NullLogger.hpp>
#include <assimp/DefaultLogger.hpp>
#include <assimp/ai_assert.h>
#include <iostream>
#include <stdio.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/NullLogger.hpp>
#include <iostream>
#ifndef ASSIMP_BUILD_SINGLETHREADED
# include <thread>
# include <mutex>
std::mutex loggerMutex;
#include <mutex>
#include <thread>
std::mutex loggerMutex;
#endif
namespace Assimp {
namespace Assimp {
// ----------------------------------------------------------------------------------
NullLogger DefaultLogger::s_pNullLogger;
@ -75,13 +75,13 @@ static const unsigned int SeverityAll = Logger::Info | Logger::Err | Logger::War
// ----------------------------------------------------------------------------------
// Represents a log-stream + its error severity
struct LogStreamInfo {
unsigned int m_uiErrorSeverity;
LogStream *m_pStream;
unsigned int m_uiErrorSeverity;
LogStream *m_pStream;
// Constructor
LogStreamInfo( unsigned int uiErrorSev, LogStream *pStream ) :
m_uiErrorSeverity( uiErrorSev ),
m_pStream( pStream ) {
LogStreamInfo(unsigned int uiErrorSev, LogStream *pStream) :
m_uiErrorSeverity(uiErrorSev),
m_pStream(pStream) {
// empty
}
@ -93,12 +93,10 @@ struct LogStreamInfo {
// ----------------------------------------------------------------------------------
// Construct a default log stream
LogStream* LogStream::createDefaultStream(aiDefaultLogStream streams,
const char* name /*= "AssimpLog.txt"*/,
IOSystem* io /*= NULL*/)
{
switch (streams)
{
LogStream *LogStream::createDefaultStream(aiDefaultLogStream streams,
const char *name /*= "AssimpLog.txt"*/,
IOSystem *io /*= nullptr*/) {
switch (streams) {
// This is a platform-specific feature
case aiDefaultLogStream_DEBUGGER:
#ifdef WIN32
@ -107,116 +105,125 @@ LogStream* LogStream::createDefaultStream(aiDefaultLogStream streams,
return nullptr;
#endif
// Platform-independent default streams
// Platform-independent default streams
case aiDefaultLogStream_STDERR:
return new StdOStreamLogStream(std::cerr);
case aiDefaultLogStream_STDOUT:
return new StdOStreamLogStream(std::cout);
case aiDefaultLogStream_FILE:
return (name && *name ? new FileLogStream(name,io) : nullptr );
return (name && *name ? new FileLogStream(name, io) : nullptr);
default:
// We don't know this default log stream, so raise an assertion
ai_assert(false);
};
// For compilers without dead code path detection
return NULL;
return nullptr;
}
// ----------------------------------------------------------------------------------
// Creates the only singleton instance
Logger *DefaultLogger::create(const char* name /*= "AssimpLog.txt"*/,
LogSeverity severity /*= NORMAL*/,
unsigned int defStreams /*= aiDefaultLogStream_DEBUGGER | aiDefaultLogStream_FILE*/,
IOSystem* io /*= NULL*/) {
Logger *DefaultLogger::create(const char *name /*= "AssimpLog.txt"*/,
LogSeverity severity /*= NORMAL*/,
unsigned int defStreams /*= aiDefaultLogStream_DEBUGGER | aiDefaultLogStream_FILE*/,
IOSystem *io /*= nullptr*/) {
// enter the mutex here to avoid concurrency problems
#ifndef ASSIMP_BUILD_SINGLETHREADED
std::lock_guard<std::mutex> lock(loggerMutex);
#endif
if ( m_pLogger && !isNullLogger() ) {
if (m_pLogger && !isNullLogger()) {
delete m_pLogger;
}
m_pLogger = new DefaultLogger( severity );
m_pLogger = new DefaultLogger(severity);
// Attach default log streams
// Stream the log to the MSVC debugger?
if ( defStreams & aiDefaultLogStream_DEBUGGER ) {
m_pLogger->attachStream( LogStream::createDefaultStream( aiDefaultLogStream_DEBUGGER ) );
if (defStreams & aiDefaultLogStream_DEBUGGER) {
m_pLogger->attachStream(LogStream::createDefaultStream(aiDefaultLogStream_DEBUGGER));
}
// Stream the log to COUT?
if ( defStreams & aiDefaultLogStream_STDOUT ) {
m_pLogger->attachStream( LogStream::createDefaultStream( aiDefaultLogStream_STDOUT ) );
if (defStreams & aiDefaultLogStream_STDOUT) {
m_pLogger->attachStream(LogStream::createDefaultStream(aiDefaultLogStream_STDOUT));
}
// Stream the log to CERR?
if ( defStreams & aiDefaultLogStream_STDERR ) {
m_pLogger->attachStream( LogStream::createDefaultStream( aiDefaultLogStream_STDERR ) );
if (defStreams & aiDefaultLogStream_STDERR) {
m_pLogger->attachStream(LogStream::createDefaultStream(aiDefaultLogStream_STDERR));
}
// Stream the log to a file
if ( defStreams & aiDefaultLogStream_FILE && name && *name ) {
m_pLogger->attachStream( LogStream::createDefaultStream( aiDefaultLogStream_FILE, name, io ) );
if (defStreams & aiDefaultLogStream_FILE && name && *name) {
m_pLogger->attachStream(LogStream::createDefaultStream(aiDefaultLogStream_FILE, name, io));
}
return m_pLogger;
}
// ----------------------------------------------------------------------------------
void Logger::debug(const char* message) {
void Logger::debug(const char *message) {
// SECURITY FIX: otherwise it's easy to produce overruns since
// sometimes importers will include data from the input file
// (i.e. node names) in their messages.
if (strlen(message)>MAX_LOG_MESSAGE_LENGTH) {
return;
if (strlen(message) > MAX_LOG_MESSAGE_LENGTH) {
return OnDebug("<fixme: long message discarded>");
}
return OnDebug(message);
}
// ----------------------------------------------------------------------------------
void Logger::info(const char* message) {
void Logger::verboseDebug(const char *message) {
// SECURITY FIX: see above
if (strlen(message)>MAX_LOG_MESSAGE_LENGTH) {
return;
if (strlen(message) > MAX_LOG_MESSAGE_LENGTH) {
return OnVerboseDebug("<fixme: long message discarded>");
}
return OnVerboseDebug(message);
}
// ----------------------------------------------------------------------------------
void Logger::info(const char *message) {
// SECURITY FIX: see above
if (strlen(message) > MAX_LOG_MESSAGE_LENGTH) {
return OnInfo("<fixme: long message discarded>");
}
return OnInfo(message);
}
// ----------------------------------------------------------------------------------
void Logger::warn(const char* message) {
void Logger::warn(const char *message) {
// SECURITY FIX: see above
if (strlen(message)>MAX_LOG_MESSAGE_LENGTH) {
return;
if (strlen(message) > MAX_LOG_MESSAGE_LENGTH) {
return OnWarn("<fixme: long message discarded>");
}
return OnWarn(message);
}
// ----------------------------------------------------------------------------------
void Logger::error(const char* message) {
void Logger::error(const char *message) {
// SECURITY FIX: see above
if (strlen(message)>MAX_LOG_MESSAGE_LENGTH) {
return;
if (strlen(message) > MAX_LOG_MESSAGE_LENGTH) {
return OnError("<fixme: long message discarded>");
}
return OnError(message);
}
// ----------------------------------------------------------------------------------
void DefaultLogger::set( Logger *logger ) {
void DefaultLogger::set(Logger *logger) {
// enter the mutex here to avoid concurrency problems
#ifndef ASSIMP_BUILD_SINGLETHREADED
std::lock_guard<std::mutex> lock(loggerMutex);
#endif
if ( nullptr == logger ) {
if (nullptr == logger) {
logger = &s_pNullLogger;
}
if ( nullptr != m_pLogger && !isNullLogger() ) {
if (nullptr != m_pLogger && !isNullLogger()) {
delete m_pLogger;
}
@ -241,103 +248,115 @@ void DefaultLogger::kill() {
std::lock_guard<std::mutex> lock(loggerMutex);
#endif
if ( m_pLogger == &s_pNullLogger ) {
return;
}
if (m_pLogger == &s_pNullLogger) {
return;
}
delete m_pLogger;
m_pLogger = &s_pNullLogger;
}
// ----------------------------------------------------------------------------------
// Debug message
void DefaultLogger::OnDebug( const char* message ) {
if ( m_Severity == Logger::NORMAL ) {
void DefaultLogger::OnDebug(const char *message) {
if (m_Severity < Logger::DEBUGGING) {
return;
}
static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16;
char msg[Size];
ai_snprintf(msg, Size, "Debug, T%u: %s", GetThreadID(), message);
static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16;
char msg[Size];
ai_snprintf(msg, Size, "Debug, T%u: %s", GetThreadID(), message);
WriteToStreams( msg, Logger::Debugging );
WriteToStreams(msg, Logger::Debugging);
}
// Verbose debug message
void DefaultLogger::OnVerboseDebug(const char *message) {
if (m_Severity < Logger::VERBOSE) {
return;
}
static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16;
char msg[Size];
ai_snprintf(msg, Size, "Debug, T%u: %s", GetThreadID(), message);
WriteToStreams(msg, Logger::Debugging);
}
// ----------------------------------------------------------------------------------
// Logs an info
void DefaultLogger::OnInfo( const char* message ){
static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16;
char msg[Size];
ai_snprintf(msg, Size, "Info, T%u: %s", GetThreadID(), message );
void DefaultLogger::OnInfo(const char *message) {
static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16;
char msg[Size];
ai_snprintf(msg, Size, "Info, T%u: %s", GetThreadID(), message);
WriteToStreams( msg , Logger::Info );
WriteToStreams(msg, Logger::Info);
}
// ----------------------------------------------------------------------------------
// Logs a warning
void DefaultLogger::OnWarn( const char* message ) {
static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16;
char msg[Size];
ai_snprintf(msg, Size, "Warn, T%u: %s", GetThreadID(), message );
void DefaultLogger::OnWarn(const char *message) {
static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16;
char msg[Size];
ai_snprintf(msg, Size, "Warn, T%u: %s", GetThreadID(), message);
WriteToStreams( msg, Logger::Warn );
WriteToStreams(msg, Logger::Warn);
}
// ----------------------------------------------------------------------------------
// Logs an error
void DefaultLogger::OnError( const char* message ) {
static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16;
char msg[ Size ];
ai_snprintf(msg, Size, "Error, T%u: %s", GetThreadID(), message );
void DefaultLogger::OnError(const char *message) {
static const size_t Size = MAX_LOG_MESSAGE_LENGTH + 16;
char msg[Size];
ai_snprintf(msg, Size, "Error, T%u: %s", GetThreadID(), message);
WriteToStreams( msg, Logger::Err );
WriteToStreams(msg, Logger::Err);
}
// ----------------------------------------------------------------------------------
// Will attach a new stream
bool DefaultLogger::attachStream( LogStream *pStream, unsigned int severity ) {
if ( nullptr == pStream ) {
bool DefaultLogger::attachStream(LogStream *pStream, unsigned int severity) {
if (nullptr == pStream) {
return false;
}
if (0 == severity) {
if (0 == severity) {
severity = Logger::Info | Logger::Err | Logger::Warn | Logger::Debugging;
}
for ( StreamIt it = m_StreamArray.begin();
it != m_StreamArray.end();
++it )
{
if ( (*it)->m_pStream == pStream ) {
for (StreamIt it = m_StreamArray.begin();
it != m_StreamArray.end();
++it) {
if ((*it)->m_pStream == pStream) {
(*it)->m_uiErrorSeverity |= severity;
return true;
}
}
LogStreamInfo *pInfo = new LogStreamInfo( severity, pStream );
m_StreamArray.push_back( pInfo );
LogStreamInfo *pInfo = new LogStreamInfo(severity, pStream);
m_StreamArray.push_back(pInfo);
return true;
}
// ----------------------------------------------------------------------------------
// Detach a stream
bool DefaultLogger::detatchStream( LogStream *pStream, unsigned int severity ) {
if ( nullptr == pStream ) {
bool DefaultLogger::detachStream(LogStream *pStream, unsigned int severity) {
if (nullptr == pStream) {
return false;
}
if (0 == severity) {
if (0 == severity) {
severity = SeverityAll;
}
bool res( false );
for ( StreamIt it = m_StreamArray.begin(); it != m_StreamArray.end(); ++it ) {
if ( (*it)->m_pStream == pStream ) {
bool res(false);
for (StreamIt it = m_StreamArray.begin(); it != m_StreamArray.end(); ++it) {
if ((*it)->m_pStream == pStream) {
(*it)->m_uiErrorSeverity &= ~severity;
if ( (*it)->m_uiErrorSeverity == 0 ) {
if ((*it)->m_uiErrorSeverity == 0) {
// don't delete the underlying stream 'cause the caller gains ownership again
(**it).m_pStream = nullptr;
delete *it;
m_StreamArray.erase( it );
m_StreamArray.erase(it);
res = true;
break;
}
@ -349,17 +368,15 @@ bool DefaultLogger::detatchStream( LogStream *pStream, unsigned int severity ) {
// ----------------------------------------------------------------------------------
// Constructor
DefaultLogger::DefaultLogger(LogSeverity severity)
: Logger ( severity )
, noRepeatMsg (false)
, lastLen( 0 ) {
DefaultLogger::DefaultLogger(LogSeverity severity) :
Logger(severity), noRepeatMsg(false), lastLen(0) {
lastMsg[0] = '\0';
}
// ----------------------------------------------------------------------------------
// Destructor
DefaultLogger::~DefaultLogger() {
for ( StreamIt it = m_StreamArray.begin(); it != m_StreamArray.end(); ++it ) {
for (StreamIt it = m_StreamArray.begin(); it != m_StreamArray.end(); ++it) {
// also frees the underlying stream, we are its owner.
delete *it;
}
@ -367,43 +384,38 @@ DefaultLogger::~DefaultLogger() {
// ----------------------------------------------------------------------------------
// Writes message to stream
void DefaultLogger::WriteToStreams(const char *message, ErrorSeverity ErrorSev ) {
void DefaultLogger::WriteToStreams(const char *message, ErrorSeverity ErrorSev) {
ai_assert(nullptr != message);
// Check whether this is a repeated message
if (! ::strncmp( message,lastMsg, lastLen-1))
{
if (!noRepeatMsg)
{
auto thisLen = ::strlen(message);
if (thisLen == lastLen - 1 && !::strncmp(message, lastMsg, lastLen - 1)) {
if (!noRepeatMsg) {
noRepeatMsg = true;
message = "Skipping one or more lines with the same contents\n";
}
else return;
}
else
{
return;
} else {
// append a new-line character to the message to be printed
lastLen = ::strlen(message);
::memcpy(lastMsg,message,lastLen+1);
::strcat(lastMsg+lastLen,"\n");
lastLen = thisLen;
::memcpy(lastMsg, message, lastLen + 1);
::strcat(lastMsg + lastLen, "\n");
message = lastMsg;
noRepeatMsg = false;
++lastLen;
}
for ( ConstStreamIt it = m_StreamArray.begin();
it != m_StreamArray.end();
++it)
{
if ( ErrorSev & (*it)->m_uiErrorSeverity )
(*it)->m_pStream->write( message);
for (ConstStreamIt it = m_StreamArray.begin();
it != m_StreamArray.end();
++it) {
if (ErrorSev & (*it)->m_uiErrorSeverity)
(*it)->m_pStream->write(message);
}
}
// ----------------------------------------------------------------------------------
// Returns thread id, if not supported only a zero will be returned.
unsigned int DefaultLogger::GetThreadID()
{
unsigned int DefaultLogger::GetThreadID() {
// fixme: we can get this value via std::threads
// std::this_thread::get_id().hash() returns a (big) size_t, not sure if this is useful in this case.
#ifdef WIN32
@ -415,4 +427,4 @@ unsigned int DefaultLogger::GetThreadID()
// ----------------------------------------------------------------------------------
} // !namespace Assimp
} // namespace Assimp

View file

@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -48,18 +47,20 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/ProgressHandler.hpp>
namespace Assimp {
namespace Assimp {
// ------------------------------------------------------------------------------------
/** @brief Internal default implementation of the #ProgressHandler interface. */
/**
* @brief Internal default implementation of the #ProgressHandler interface.
*/
class DefaultProgressHandler : public ProgressHandler {
virtual bool Update(float /*percentage*/) {
public:
/// @brief Ignores the update callback.
bool Update(float) override {
return false;
}
};
}; // !class DefaultProgressHandler
} // Namespace Assimp
#endif
#endif // INCLUDED_AI_DEFAULTPROGRESSHANDLER_H

View file

@ -0,0 +1,52 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the following
conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
/** @file Exceptional.cpp
Implementations of the exception classes.
*/
#include <assimp/Exceptional.h>
#include <assimp/TinyFormatter.h>
DeadlyErrorBase::DeadlyErrorBase(Assimp::Formatter::format f) :
runtime_error(std::string(f)){}

View file

@ -3,9 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -76,120 +74,164 @@ Here we implement only the C++ interface (Assimp::Exporter).
namespace Assimp {
#ifdef _MSC_VER
# pragma warning( disable : 4800 )
#endif // _MSC_VER
// PostStepRegistry.cpp
void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out);
// ------------------------------------------------------------------------------------------------
// Exporter worker function prototypes. Should not be necessary to #ifndef them, it's just a prototype
// do not use const, because some exporter need to convert the scene temporary
// Exporter worker function prototypes. Do not use const, because some exporter need to convert
// the scene temporary
#ifndef ASSIMP_BUILD_NO_COLLADA_EXPORTER
void ExportSceneCollada(const char*,IOSystem*, const aiScene*, const ExportProperties*);
#endif
#ifndef ASSIMP_BUILD_NO_X_EXPORTER
void ExportSceneXFile(const char*,IOSystem*, const aiScene*, const ExportProperties*);
#endif
#ifndef ASSIMP_BUILD_NO_STEP_EXPORTER
void ExportSceneStep(const char*,IOSystem*, const aiScene*, const ExportProperties*);
#endif
#ifndef ASSIMP_BUILD_NO_OBJ_EXPORTER
void ExportSceneObj(const char*,IOSystem*, const aiScene*, const ExportProperties*);
void ExportSceneObjNoMtl(const char*,IOSystem*, const aiScene*, const ExportProperties*);
#endif
#ifndef ASSIMP_BUILD_NO_STL_EXPORTER
void ExportSceneSTL(const char*,IOSystem*, const aiScene*, const ExportProperties*);
void ExportSceneSTLBinary(const char*,IOSystem*, const aiScene*, const ExportProperties*);
#endif
#ifndef ASSIMP_BUILD_NO_PLY_EXPORTER
void ExportScenePly(const char*,IOSystem*, const aiScene*, const ExportProperties*);
void ExportScenePlyBinary(const char*, IOSystem*, const aiScene*, const ExportProperties*);
#endif
#ifndef ASSIMP_BUILD_NO_3DS_EXPORTER
void ExportScene3DS(const char*, IOSystem*, const aiScene*, const ExportProperties*);
#endif
#ifndef ASSIMP_BUILD_NO_GLTF_EXPORTER
void ExportSceneGLTF(const char*, IOSystem*, const aiScene*, const ExportProperties*);
void ExportSceneGLB(const char*, IOSystem*, const aiScene*, const ExportProperties*);
void ExportSceneGLTF2(const char*, IOSystem*, const aiScene*, const ExportProperties*);
void ExportSceneGLB2(const char*, IOSystem*, const aiScene*, const ExportProperties*);
#endif
#ifndef ASSIMP_BUILD_NO_ASSBIN_EXPORTER
void ExportSceneAssbin(const char*, IOSystem*, const aiScene*, const ExportProperties*);
#endif
#ifndef ASSIMP_BUILD_NO_ASSXML_EXPORTER
void ExportSceneAssxml(const char*, IOSystem*, const aiScene*, const ExportProperties*);
#endif
#ifndef ASSIMP_BUILD_NO_X3D_EXPORTER
void ExportSceneX3D(const char*, IOSystem*, const aiScene*, const ExportProperties*);
#endif
#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER
void ExportSceneFBX(const char*, IOSystem*, const aiScene*, const ExportProperties*);
void ExportSceneFBXA(const char*, IOSystem*, const aiScene*, const ExportProperties*);
#endif
#ifndef ASSIMP_BUILD_NO_3MF_EXPORTER
void ExportScene3MF( const char*, IOSystem*, const aiScene*, const ExportProperties* );
#endif
#ifndef ASSIMP_BUILD_NO_M3D_EXPORTER
void ExportSceneM3D(const char*, IOSystem*, const aiScene*, const ExportProperties*);
void ExportSceneM3DA(const char*, IOSystem*, const aiScene*, const ExportProperties*);
#endif
#ifndef ASSIMP_BUILD_NO_ASSJSON_EXPORTER
void ExportAssimp2Json(const char* , IOSystem*, const aiScene* , const Assimp::ExportProperties*);
#endif
#ifndef ASSIMP_BUILD_NO_PBRT_EXPORTER
void ExportScenePbrt(const char*, IOSystem*, const aiScene*, const ExportProperties*);
#endif
static void setupExporterArray(std::vector<Exporter::ExportFormatEntry> &exporters) {
(void)exporters;
// ------------------------------------------------------------------------------------------------
// global array of all export formats which Assimp supports in its current build
Exporter::ExportFormatEntry gExporters[] =
{
#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 ),
#if !defined(ASSIMP_BUILD_NO_GLTF_EXPORTER) && !defined(ASSIMP_BUILD_NO_GLTF2_EXPORTER)
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));
#endif
#if !defined(ASSIMP_BUILD_NO_GLTF_EXPORTER) && !defined(ASSIMP_BUILD_NO_GLTF1_EXPORTER)
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("m3da", "Model 3D (ascii)", "a3d", &ExportSceneM3DA, 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_PBRT_EXPORTER
exporters.push_back(Exporter::ExportFormatEntry("pbrt", "pbrt-v4 scene description file", "pbrt", &ExportScenePbrt, aiProcess_Triangulate | aiProcess_SortByPType));
#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 +247,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,24 +291,28 @@ Exporter :: Exporter()
// ------------------------------------------------------------------------------------------------
Exporter::~Exporter() {
FreeBlob();
ai_assert(nullptr != pimpl);
FreeBlob();
delete pimpl;
}
// ------------------------------------------------------------------------------------------------
void Exporter::SetIOHandler( IOSystem* pIOHandler) {
pimpl->mIsDefaultIOHandler = !pIOHandler;
ai_assert(nullptr != pimpl);
pimpl->mIsDefaultIOHandler = !pIOHandler;
pimpl->mIOSystem.reset(pIOHandler);
}
// ------------------------------------------------------------------------------------------------
IOSystem* Exporter::GetIOHandler() const {
return pimpl->mIOSystem.get();
ai_assert(nullptr != pimpl);
return pimpl->mIOSystem.get();
}
// ------------------------------------------------------------------------------------------------
bool Exporter::IsDefaultIOHandler() const {
return pimpl->mIsDefaultIOHandler;
ai_assert(nullptr != pimpl);
return pimpl->mIsDefaultIOHandler;
}
// ------------------------------------------------------------------------------------------------
@ -295,13 +338,16 @@ 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;
}
auto baseName = pProperties ? pProperties->GetPropertyString(AI_CONFIG_EXPORT_BLOB_NAME, AI_BLOBIO_MAGIC) : AI_BLOBIO_MAGIC;
std::shared_ptr<IOSystem> old = pimpl->mIOSystem;
BlobIOSystem* blobio = new BlobIOSystem();
BlobIOSystem *blobio = new BlobIOSystem(baseName);
pimpl->mIOSystem = std::shared_ptr<IOSystem>( blobio );
if (AI_SUCCESS != Export(pScene,pFormatId,blobio->GetMagicFileName(), pPreprocessing, pProperties)) {
@ -319,7 +365,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
@ -443,10 +489,9 @@ aiReturn Exporter::Export( const aiScene* pScene, const char* pFormatId, const c
proc.Execute(scenecopy.get());
}
ExportProperties emptyProperties; // Never pass NULL ExportProperties so Exporters don't have to worry.
ExportProperties emptyProperties; // Never pass nullptr 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 +511,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,30 +526,34 @@ void Exporter::FreeBlob() {
// ------------------------------------------------------------------------------------------------
const aiExportDataBlob* Exporter::GetBlob() const {
return pimpl->blob;
ai_assert(nullptr != pimpl);
return pimpl->blob;
}
// ------------------------------------------------------------------------------------------------
const aiExportDataBlob* Exporter::GetOrphanedBlob() const {
const aiExportDataBlob* tmp = pimpl->blob;
ai_assert(nullptr != pimpl);
const aiExportDataBlob *tmp = pimpl->blob;
pimpl->blob = nullptr;
return tmp;
}
// ------------------------------------------------------------------------------------------------
size_t Exporter::GetExportFormatCount() const {
ai_assert(nullptr != pimpl);
return pimpl->mExporters.size();
}
// ------------------------------------------------------------------------------------------------
const aiExportFormatDesc* Exporter::GetExportFormatDescription( size_t index ) const {
if (index >= GetExportFormatCount()) {
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,7 +561,8 @@ const aiExportFormatDesc* Exporter::GetExportFormatDescription( size_t index ) c
// ------------------------------------------------------------------------------------------------
aiReturn Exporter::RegisterExporter(const ExportFormatEntry& desc) {
for(const ExportFormatEntry& e : pimpl->mExporters) {
ai_assert(nullptr != pimpl);
for (const ExportFormatEntry &e : pimpl->mExporters) {
if (!strcmp(e.mDescription.id,desc.mDescription.id)) {
return aiReturn_FAILURE;
}
@ -522,7 +574,8 @@ aiReturn Exporter::RegisterExporter(const ExportFormatEntry& desc) {
// ------------------------------------------------------------------------------------------------
void Exporter::UnregisterExporter(const char* id) {
for(std::vector<ExportFormatEntry>::iterator it = pimpl->mExporters.begin();
ai_assert(nullptr != pimpl);
for (std::vector<ExportFormatEntry>::iterator it = pimpl->mExporters.begin();
it != pimpl->mExporters.end(); ++it) {
if (!strcmp((*it).mDescription.id,id)) {
pimpl->mExporters.erase(it);
@ -541,10 +594,23 @@ ExportProperties::ExportProperties(const ExportProperties &other)
: mIntProperties(other.mIntProperties)
, mFloatProperties(other.mFloatProperties)
, mStringProperties(other.mStringProperties)
, mMatrixProperties(other.mMatrixProperties) {
, mMatrixProperties(other.mMatrixProperties)
, mCallbackProperties(other.mCallbackProperties){
// empty
}
bool ExportProperties::SetPropertyCallback(const char *szName, const std::function<void *(void *)> &f) {
return SetGenericProperty<std::function<void *(void *)>>(mCallbackProperties, szName, f);
}
std::function<void *(void *)> ExportProperties::GetPropertyCallback(const char *szName) const {
return GetGenericProperty<std::function<void *(void *)>>(mCallbackProperties, szName, 0);
}
bool ExportProperties::HasPropertyCallback(const char *szName) const {
return HasGenericProperty<std::function<void *(void *)>>(mCallbackProperties, szName);
}
// ------------------------------------------------------------------------------------------------
// Set a configuration property
bool ExportProperties::SetPropertyInteger(const char* szName, int iValue) {

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -43,23 +43,21 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef ASSIMP_FILELOGSTREAM_H_INC
#define ASSIMP_FILELOGSTREAM_H_INC
#include <assimp/LogStream.hpp>
#include <assimp/IOStream.hpp>
#include <assimp/DefaultIOSystem.h>
#include <assimp/IOStream.hpp>
#include <assimp/LogStream.hpp>
namespace Assimp {
namespace Assimp {
// ----------------------------------------------------------------------------------
/** @class FileLogStream
* @brief Logstream to write into a file.
*/
class FileLogStream :
public LogStream
{
class FileLogStream : public LogStream {
public:
FileLogStream( const char* file, IOSystem* io = NULL );
FileLogStream(const char *file, IOSystem *io = nullptr);
~FileLogStream();
void write( const char* message );
void write(const char *message);
private:
IOStream *m_pStream;
@ -67,41 +65,36 @@ private:
// ----------------------------------------------------------------------------------
// Constructor
inline FileLogStream::FileLogStream( const char* file, IOSystem* io ) :
m_pStream(NULL)
{
if ( !file || 0 == *file )
inline FileLogStream::FileLogStream(const char *file, IOSystem *io) :
m_pStream(nullptr) {
if (!file || 0 == *file)
return;
// If no IOSystem is specified: take a default one
if (!io)
{
if (!io) {
DefaultIOSystem FileSystem;
m_pStream = FileSystem.Open( file, "wt");
}
else m_pStream = io->Open( file, "wt" );
m_pStream = FileSystem.Open(file, "wt");
} else
m_pStream = io->Open(file, "wt");
}
// ----------------------------------------------------------------------------------
// Destructor
inline FileLogStream::~FileLogStream()
{
inline FileLogStream::~FileLogStream() {
// The virtual d'tor should destroy the underlying file
delete m_pStream;
}
// ----------------------------------------------------------------------------------
// Write method
inline void FileLogStream::write( const char* message )
{
if (m_pStream != NULL)
{
inline void FileLogStream::write(const char *message) {
if (m_pStream != nullptr) {
m_pStream->Write(message, sizeof(char), ::strlen(message));
m_pStream->Flush();
}
}
// ----------------------------------------------------------------------------------
} // !Namespace Assimp
} // namespace Assimp
#endif // !! ASSIMP_FILELOGSTREAM_H_INC

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2008, assimp team
Copyright (c) 2006-2020, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
@ -76,7 +76,7 @@ public:
if (std::string::npos != (ss2 = mBase.find_last_of("\\/"))) {
mBase.erase(ss2,mBase.length()-ss2);
} else {
mBase = "";
mBase = std::string();
}
// make sure the directory is terminated properly
@ -89,7 +89,7 @@ public:
mBase += getOsSeparator();
}
DefaultLogger::get()->info("Import root directory is \'" + mBase + "\'");
DefaultLogger::get()->info("Import root directory is \'", mBase, "\'");
}
/** Destructor. */
@ -101,7 +101,7 @@ public:
/** Tests for the existence of a file at the given path. */
bool Exists( const char* pFile) const {
ai_assert( nullptr != mWrapped );
std::string tmp = pFile;
// Currently this IOSystem is also used to open THE ONE FILE.
@ -126,7 +126,7 @@ public:
if ( nullptr == pFile || nullptr == pMode ) {
return nullptr;
}
ai_assert( nullptr != pFile );
ai_assert( nullptr != pMode );
@ -300,13 +300,14 @@ private:
const char separator = getOsSeparator();
for (it = in.begin(); it != in.end(); ++it) {
int remaining = std::distance(in.end(), it);
// Exclude :// and \\, which remain untouched.
// https://sourceforge.net/tracker/?func=detail&aid=3031725&group_id=226462&atid=1067632
if ( !strncmp(&*it, "://", 3 )) {
if (remaining >= 3 && !strncmp(&*it, "://", 3 )) {
it += 3;
continue;
}
if (it == in.begin() && !strncmp(&*it, "\\\\", 2)) {
if (it == in.begin() && remaining >= 2 && !strncmp(&*it, "\\\\", 2)) {
it += 2;
continue;
}

View file

@ -35,6 +35,17 @@ struct SubChunkHeader
uint16_t length;
};
/////////////////////////////////////////////////////////////////////////////////
//! Describes an IFF form header
/////////////////////////////////////////////////////////////////////////////////
struct FormHeader
{
//! Length of the chunk data, in bytes
uint32_t length;
//! Type of the chunk header - FourCC
uint32_t type;
};
#define AI_IFF_FOURCC(a,b,c,d) ((uint32_t) (((uint8_t)a << 24u) | \
((uint8_t)b << 16u) | ((uint8_t)c << 8u) | ((uint8_t)d)))
@ -77,6 +88,24 @@ inline SubChunkHeader LoadSubChunk(uint8_t*& outFile)
return head;
}
/////////////////////////////////////////////////////////////////////////////////
//! Load a chunk header
//! @param outFile Pointer to the file data - points to the chunk data afterwards
//! @return Copy of the chunk header
/////////////////////////////////////////////////////////////////////////////////
inline ChunkHeader LoadForm(uint8_t*& outFile)
{
ChunkHeader head;
outFile += 4;
::memcpy(&head.length, outFile, 4);
outFile += 4;
::memcpy(&head.type, outFile, 4);
AI_LSWAP4(head.length);
AI_LSWAP4(head.type);
return head;
}
/////////////////////////////////////////////////////////////////////////////////
//! Read the file header and return the type of the file and its size
//! @param outFile Pointer to the file data. The buffer must at

View file

@ -0,0 +1,53 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the following
conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
/** @file Default implementation of IOSystem using the standard C file functions */
#include <assimp/IOSystem.hpp>
using namespace Assimp;
const std::string &IOSystem::CurrentDirectory() const {
if ( m_pathStack.empty() ) {
static const std::string Dummy = std::string();
return Dummy;
}
return m_pathStack[ m_pathStack.size()-1 ];
}

View file

@ -3,9 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -78,6 +76,9 @@ 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 <exception>
#include <set>
#include <memory>
#include <cctype>
@ -119,7 +120,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 +135,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,8 +148,8 @@ void AllocateFromAssimpHeap::operator delete[] ( void* data) {
// Importer constructor.
Importer::Importer()
: pimpl( new ImporterPimpl ) {
pimpl->mScene = NULL;
pimpl->mErrorString = "";
pimpl->mScene = nullptr;
pimpl->mErrorString = std::string();
// Allocate a default IO handler
pimpl->mIOHandler = new DefaultIOSystem;
@ -174,14 +174,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 +199,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 +213,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();
// --------------------------------------------------------------------
@ -232,7 +232,7 @@ aiReturn Importer::RegisterLoader(BaseImporter* pImp)
#ifdef ASSIMP_BUILD_DEBUG
if (IsExtensionSupported(*it)) {
ASSIMP_LOG_WARN_F("The file extension ", *it, " is already in use");
ASSIMP_LOG_WARN("The file extension ", *it, " is already in use");
}
#endif
baked += *it;
@ -240,17 +240,17 @@ aiReturn Importer::RegisterLoader(BaseImporter* pImp)
// add the loader
pimpl->mImporter.push_back(pImp);
ASSIMP_LOG_INFO_F("Registering custom importer for these file extensions: ", baked);
ASSIMP_LOG_INFO("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!
// unregistering a nullptr importer is no problem for us ... really!
return AI_SUCCESS;
}
@ -265,15 +265,15 @@ 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!
// unregistering a nullptr ppstep is no problem for us ... really!
return AI_SUCCESS;
}
@ -288,24 +288,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 +314,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 ) {
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 +350,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,57 +379,71 @@ 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 = "";
pimpl->mErrorString = std::string();
pimpl->mException = std::exception_ptr();
ASSIMP_END_EXCEPTION_REGION(void);
}
// ------------------------------------------------------------------------------------------------
// 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();
}
const std::exception_ptr& Importer::GetException() const {
ai_assert(nullptr != pimpl);
// Must remain valid as long as ReadFile() or FreeFile() are not called
return pimpl->mException;
}
// ------------------------------------------------------------------------------------------------
// 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 = std::string();
pimpl->mException = std::exception_ptr();
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 +485,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 +495,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,65 +512,75 @@ 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, pimpl->mException);
return pimpl->mScene;
}
// ------------------------------------------------------------------------------------------------
void WriteLogOpening(const std::string& file)
{
ASSIMP_LOG_INFO_F("Load ", file);
void WriteLogOpening(const std::string& file) {
ASSIMP_LOG_INFO("Load ", file);
// print a full version dump. This is nice because we don't
// need to ask the authors of incoming bug reports for
// the library version they're using - a log dump is
// sufficient.
const unsigned int flags( aiGetCompileFlags() );
const unsigned int flags = aiGetCompileFlags();
std::stringstream stream;
stream << "Assimp " << aiGetVersionMajor() << "." << aiGetVersionMinor() << "." << aiGetVersionRevision() << " "
#if defined(ASSIMP_BUILD_ARCHITECTURE)
<< ASSIMP_BUILD_ARCHITECTURE
<< ASSIMP_BUILD_ARCHITECTURE
#elif defined(_M_IX86) || defined(__x86_32__) || defined(__i386__)
<< "x86"
<< "x86"
#elif defined(_M_X64) || defined(__x86_64__)
<< "amd64"
<< "amd64"
#elif defined(_M_IA64) || defined(__ia64__)
<< "itanium"
<< "itanium"
#elif defined(__ppc__) || defined(__powerpc__)
<< "ppc32"
<< "ppc32"
#elif defined(__powerpc64__)
<< "ppc64"
<< "ppc64"
#elif defined(__arm__)
<< "arm"
<< "arm"
#else
<< "<unknown architecture>"
<< "<unknown architecture>"
#endif
<< " "
<< " "
#if defined(ASSIMP_BUILD_COMPILER)
<< ( ASSIMP_BUILD_COMPILER )
<< (ASSIMP_BUILD_COMPILER)
#elif defined(_MSC_VER)
<< "msvc"
<< "msvc"
#elif defined(__GNUC__)
<< "gcc"
<< "gcc"
#elif defined(__clang__)
<< "clang"
#elif defined(__EMSCRIPTEN__)
<< "emscripten"
#elif defined(__MINGW32__)
<< "MinGW-w64 32bit"
#elif defined(__MINGW64__)
<< "MinGW-w64 64bit"
#else
<< "<unknown compiler>"
<< "<unknown compiler>"
#endif
#ifdef ASSIMP_BUILD_DEBUG
<< " debug"
<< " debug"
#endif
<< (flags & ASSIMP_CFLAGS_NOBOOST ? " noboost" : "")
<< (flags & ASSIMP_CFLAGS_SHARED ? " shared" : "")
<< (flags & ASSIMP_CFLAGS_SINGLETHREADED ? " singlethreaded" : "");
<< (flags & ASSIMP_CFLAGS_NOBOOST ? " noboost" : "")
<< (flags & ASSIMP_CFLAGS_SHARED ? " shared" : "")
<< (flags & ASSIMP_CFLAGS_SINGLETHREADED ? " singlethreaded" : "")
<< (flags & ASSIMP_CFLAGS_DOUBLE_SUPPORT ? " double : " : "single : ");
ASSIMP_LOG_DEBUG(stream.str());
ASSIMP_LOG_DEBUG(stream.str());
}
// ------------------------------------------------------------------------------------------------
// 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,44 +609,86 @@ 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);
std::unique_ptr<Profiler> profiler(GetPropertyInteger(AI_CONFIG_GLOB_MEASURE_TIME, 0) ? new Profiler() : nullptr);
if (profiler) {
profiler->BeginRegion("total");
}
// Find an worker class which can handle the file
BaseImporter* imp = NULL;
// Find an worker class which can handle the file extension.
// Multiple importers may be able to handle the same extension (.xml!); gather them all.
SetPropertyInteger("importerIndex", -1);
for( unsigned int a = 0; a < pimpl->mImporter.size(); a++) {
struct ImporterAndIndex {
BaseImporter * importer;
unsigned int index;
};
std::vector<ImporterAndIndex> possibleImporters;
for (unsigned int a = 0; a < pimpl->mImporter.size(); a++) {
// Every importer has a list of supported extensions.
std::set<std::string> extensions;
pimpl->mImporter[a]->GetExtensionList(extensions);
// CAUTION: Do not just search for the extension!
// GetExtension() returns the part after the *last* dot, but some extensions have dots
// inside them, e.g. ogre.mesh.xml. Compare the entire end of the string.
for (std::set<std::string>::const_iterator it = extensions.cbegin(); it != extensions.cend(); ++it) {
// Yay for C++<20 not having std::string::ends_with()
std::string extension = "." + *it;
if (extension.length() <= pFile.length()) {
// Possible optimization: Fetch the lowercase filename!
if (0 == ASSIMP_stricmp(pFile.c_str() + pFile.length() - extension.length(), extension.c_str())) {
ImporterAndIndex candidate = { pimpl->mImporter[a], a };
possibleImporters.push_back(candidate);
break;
}
}
if( pimpl->mImporter[a]->CanRead( pFile, pimpl->mIOHandler, false)) {
imp = pimpl->mImporter[a];
SetPropertyInteger("importerIndex", a);
break;
}
}
// If just one importer supports this extension, pick it and close the case.
BaseImporter* imp = nullptr;
if (1 == possibleImporters.size()) {
imp = possibleImporters[0].importer;
SetPropertyInteger("importerIndex", possibleImporters[0].index);
}
// If multiple importers claim this file extension, ask them to look at the actual file data to decide.
// This can happen e.g. with XML (COLLADA vs. Irrlicht).
else {
for (std::vector<ImporterAndIndex>::const_iterator it = possibleImporters.begin(); it < possibleImporters.end(); ++it) {
BaseImporter & importer = *it->importer;
ASSIMP_LOG_INFO("Found a possible importer: " + std::string(importer.GetInfo()->mName) + "; trying signature-based detection");
if (importer.CanRead( pFile, pimpl->mIOHandler, true)) {
imp = &importer;
SetPropertyInteger("importerIndex", it->index);
break;
}
}
}
if (!imp) {
// not so bad yet ... try format auto detection.
const std::string::size_type s = pFile.find_last_of('.');
if (s != std::string::npos) {
ASSIMP_LOG_INFO("File extension not known, trying signature-based detection");
for( unsigned int a = 0; a < pimpl->mImporter.size(); a++) {
if( pimpl->mImporter[a]->CanRead( pFile, pimpl->mIOHandler, true)) {
imp = pimpl->mImporter[a];
SetPropertyInteger("importerIndex", a);
break;
}
ASSIMP_LOG_INFO("File extension not known, trying signature-based detection");
for( unsigned int a = 0; a < pimpl->mImporter.size(); a++) {
if( pimpl->mImporter[a]->CanRead( pFile, pimpl->mIOHandler, true)) {
imp = pimpl->mImporter[a];
SetPropertyInteger("importerIndex", a);
break;
}
}
// Put a proper error message if no suitable importer was found
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,10 +704,10 @@ 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 + "." );
ASSIMP_LOG_INFO("Found a matching importer for this file format: ", ext, "." );
pimpl->mProgressHandler->UpdateFileRead( 0, fileSize );
if (profiler) {
@ -654,15 +725,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
@ -685,6 +761,7 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags)
// if failed, extract the error string
else if( !pimpl->mScene) {
pimpl->mErrorString = imp->GetErrorText();
pimpl->mException = imp->GetException();
}
// clear any data allocated by post-process steps
@ -695,8 +772,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 +781,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, pimpl->mException);
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 +815,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
@ -760,13 +837,11 @@ const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags)
}
#endif // ! DEBUG
std::unique_ptr<Profiler> profiler(GetPropertyInteger(AI_CONFIG_GLOB_MEASURE_TIME,0)?new Profiler():NULL);
std::unique_ptr<Profiler> profiler(GetPropertyInteger(AI_CONFIG_GLOB_MEASURE_TIME, 0) ? new Profiler() : nullptr);
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");
}
@ -799,32 +874,36 @@ const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags)
}
#endif // ! DEBUG
}
pimpl->mProgressHandler->UpdatePostProcess( static_cast<int>(pimpl->mPostProcessingSteps.size()),
pimpl->mProgressHandler->UpdatePostProcess( static_cast<int>(pimpl->mPostProcessingSteps.size()),
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
if ( NULL == rootProcess ) {
if (nullptr == rootProcess) {
return pimpl->mScene;
}
@ -839,7 +918,7 @@ const aiScene* Importer::ApplyCustomizedPostProcessing( BaseProcess *rootProcess
ValidateDSProcess ds;
ds.ExecuteOnScene( this );
if ( !pimpl->mScene ) {
return NULL;
return nullptr;
}
}
#endif // no validation
@ -856,7 +935,7 @@ const aiScene* Importer::ApplyCustomizedPostProcessing( BaseProcess *rootProcess
}
#endif // ! DEBUG
std::unique_ptr<Profiler> profiler( GetPropertyInteger( AI_CONFIG_GLOB_MEASURE_TIME, 0 ) ? new Profiler() : NULL );
std::unique_ptr<Profiler> profiler(GetPropertyInteger(AI_CONFIG_GLOB_MEASURE_TIME, 0) ? new Profiler() : nullptr);
if ( profiler ) {
profiler->BeginRegion( "postprocess" );
@ -890,59 +969,62 @@ 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();
// skip over wildcard and dot characters at string head --
// skip over wild-card and dot characters at string head --
for ( ; *szExtension == '*' || *szExtension == '.'; ++szExtension );
std::string ext(szExtension);
if (ext.empty()) {
return static_cast<size_t>(-1);
}
std::transform( ext.begin(), ext.end(), ext.begin(), ToLower<char> );
ext = ai_tolower(ext);
std::set<std::string> str;
for (std::vector<BaseImporter*>::const_iterator i = pimpl->mImporter.begin();i != pimpl->mImporter.end();++i) {
str.clear();
@ -960,8 +1042,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 +1068,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 +1080,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 +1092,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 +1104,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);
@ -1027,42 +1114,65 @@ bool Importer::SetPropertyMatrix(const char* szName, const aiMatrix4x4& value)
return existing;
}
// ------------------------------------------------------------------------------------------------
// Set a configuration property
bool Importer::SetPropertyPointer(const char* szName, void* value) {
ai_assert(nullptr != pimpl);
bool existing;
ASSIMP_BEGIN_EXCEPTION_REGION();
existing = SetGenericProperty<void*>(pimpl->mPointerProperties, szName,value);
ASSIMP_END_EXCEPTION_REGION(bool);
return existing;
}
// ------------------------------------------------------------------------------------------------
// 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
{
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
{
aiMatrix4x4 Importer::GetPropertyMatrix(const char* szName, const aiMatrix4x4& iErrorReturn /*= aiMatrix4x4()*/) const {
ai_assert(nullptr != pimpl);
return GetGenericProperty<aiMatrix4x4>(pimpl->mMatrixProperties,szName,iErrorReturn);
}
// ------------------------------------------------------------------------------------------------
// Get a configuration property
void* Importer::GetPropertyPointer(const char* szName, void* iErrorReturn /*= nullptr*/) const {
ai_assert(nullptr != pimpl);
return GetGenericProperty<void*>(pimpl->mPointerProperties,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,21 +1184,20 @@ 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;
// return if we have no scene loaded
if (!pimpl->mScene)
if (!mScene)
return;
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 +1214,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 +1242,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;
@ -1143,7 +1255,7 @@ void Importer::GetMemoryRequirements(aiMemoryInfo& in) const
// add all bone anims
for (unsigned int a = 0; a < pc->mNumChannels; ++a) {
const aiNodeAnim* pc2 = pc->mChannels[i];
const aiNodeAnim* pc2 = pc->mChannels[a];
in.animations += sizeof(aiNodeAnim);
in.animations += pc2->mNumPositionKeys * sizeof(aiVectorKey);
in.animations += pc2->mNumScalingKeys * sizeof(aiVectorKey);
@ -1170,5 +1282,6 @@ void Importer::GetMemoryRequirements(aiMemoryInfo& in) const
in.materials += pc->mProperties[a]->mDataLength;
}
}
in.total += in.materials;
}

View file

@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -45,6 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef INCLUDED_AI_IMPORTER_H
#define INCLUDED_AI_IMPORTER_H
#include <exception>
#include <map>
#include <vector>
#include <string>
@ -73,12 +73,12 @@ public:
// Data type to store the key hash
typedef unsigned int KeyType;
// typedefs for our four configuration maps.
// We don't need more, so there is no need for a generic solution
// typedefs for our configuration maps.
typedef std::map<KeyType, int> IntPropertyMap;
typedef std::map<KeyType, ai_real> FloatPropertyMap;
typedef std::map<KeyType, std::string> StringPropertyMap;
typedef std::map<KeyType, aiMatrix4x4> MatrixPropertyMap;
typedef std::map<KeyType, void*> PointerPropertyMap;
/** IO handler to use for all file accesses. */
IOSystem* mIOHandler;
@ -94,12 +94,16 @@ public:
/** Post processing steps we can apply at the imported data. */
std::vector< BaseProcess* > mPostProcessingSteps;
/** The imported data, if ReadFile() was successful, NULL otherwise. */
/** The imported data, if ReadFile() was successful, nullptr otherwise. */
aiScene* mScene;
/** The error description, if there was one. */
/** The error description, if there was one. In the case of an exception,
* mException will carry the full details. */
std::string mErrorString;
/** Any exception which occurred */
std::exception_ptr mException;
/** List of integer properties */
IntPropertyMap mIntProperties;
@ -112,6 +116,9 @@ public:
/** List of Matrix properties */
MatrixPropertyMap mMatrixProperties;
/** List of pointer properties */
PointerPropertyMap mPointerProperties;
/** Used for testing - extra verbose mode causes the ValidateDataStructure-Step
* to be executed before and after every single post-process step */
bool bExtraVerbose;
@ -124,26 +131,27 @@ public:
};
inline
ImporterPimpl::ImporterPimpl() AI_NO_EXCEPT
: mIOHandler( nullptr )
, mIsDefaultHandler( false )
, mProgressHandler( nullptr )
, mIsDefaultProgressHandler( false )
, mImporter()
, mPostProcessingSteps()
, mScene( nullptr )
, mErrorString()
, mIntProperties()
, mFloatProperties()
, mStringProperties()
, mMatrixProperties()
, bExtraVerbose( false )
, mPPShared( nullptr ) {
ImporterPimpl::ImporterPimpl() AI_NO_EXCEPT :
mIOHandler( nullptr ),
mIsDefaultHandler( false ),
mProgressHandler( nullptr ),
mIsDefaultProgressHandler( false ),
mImporter(),
mPostProcessingSteps(),
mScene( nullptr ),
mErrorString(),
mException(),
mIntProperties(),
mFloatProperties(),
mStringProperties(),
mMatrixProperties(),
mPointerProperties(),
bExtraVerbose( false ),
mPPShared( nullptr ) {
// empty
}
//! @endcond
struct BatchData;
// ---------------------------------------------------------------------------
@ -154,17 +162,13 @@ struct BatchData;
* could, this has not yet been implemented at the moment).
*
* @note The class may not be used by more than one thread*/
class ASSIMP_API BatchLoader
{
// friend of Importer
class ASSIMP_API BatchLoader {
public:
//! @cond never
// -------------------------------------------------------------------
/** Wraps a full list of configuration properties for an importer.
* Properties can be set using SetGenericProperty */
struct PropertyMap
{
struct PropertyMap {
ImporterPimpl::IntPropertyMap ints;
ImporterPimpl::FloatPropertyMap floats;
ImporterPimpl::StringPropertyMap strings;
@ -181,10 +185,9 @@ public:
};
//! @endcond
public:
// -------------------------------------------------------------------
/** Construct a batch loader from a given IO system to be used
* to access external files
* to access external files
*/
explicit BatchLoader(IOSystem* pIO, bool validate = false );
@ -198,13 +201,13 @@ public:
* @param enable True for validation.
*/
void setValidation( bool enabled );
// -------------------------------------------------------------------
/** Returns the current validation step.
* @return The current validation step.
*/
bool getValidation() const;
// -------------------------------------------------------------------
/** Add a new file to the list of files to be loaded.
* @param file File to be loaded
@ -216,7 +219,7 @@ public:
unsigned int AddLoadRequest (
const std::string& file,
unsigned int steps = 0,
const PropertyMap* map = NULL
const PropertyMap *map = nullptr
);
// -------------------------------------------------------------------
@ -226,7 +229,7 @@ public:
* can be called several times, too.
*
* @param which LRWC returned by AddLoadRequest().
* @return NULL if there is no scene with this file name
* @return nullptr if there is no scene with this file name
* in the queue of the scene hasn't been loaded yet. */
aiScene* GetImport(
unsigned int which

View file

@ -3,9 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -48,324 +46,348 @@ directly (unless you are adding new loaders), instead use the
corresponding preprocessor flag to selectively disable formats.
*/
#include <vector>
#include <assimp/anim.h>
#include <assimp/BaseImporter.h>
#include <vector>
#include <cstdlib>
// ------------------------------------------------------------------------------------------------
// Importers
// (include_new_importers_here)
// ------------------------------------------------------------------------------------------------
#ifndef ASSIMP_BUILD_NO_X_IMPORTER
# include "X/XFileImporter.h"
#include "AssetLib/X/XFileImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_AMF_IMPORTER
# include "AMF/AMFImporter.hpp"
#include "AssetLib/AMF/AMFImporter.hpp"
#endif
#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
# include "3DS/3DSLoader.h"
#include "AssetLib/3DS/3DSLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_MD3_IMPORTER
# include "MD3/MD3Loader.h"
#include "AssetLib/MD3/MD3Loader.h"
#endif
#ifndef ASSIMP_BUILD_NO_MDL_IMPORTER
# include "MDL/MDLLoader.h"
#include "AssetLib/MDL/MDLLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_MD2_IMPORTER
# include "MD2/MD2Loader.h"
#include "AssetLib/MD2/MD2Loader.h"
#endif
#ifndef ASSIMP_BUILD_NO_PLY_IMPORTER
# include "Ply/PlyLoader.h"
#include "AssetLib/Ply/PlyLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_ASE_IMPORTER
# include "ASE/ASELoader.h"
#include "AssetLib/ASE/ASELoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_OBJ_IMPORTER
# include "Obj/ObjFileImporter.h"
#include "AssetLib/Obj/ObjFileImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_HMP_IMPORTER
# include "HMP/HMPLoader.h"
#include "AssetLib/HMP/HMPLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_SMD_IMPORTER
# include "SMD/SMDLoader.h"
#include "AssetLib/SMD/SMDLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_MDC_IMPORTER
# include "MDC/MDCLoader.h"
#include "AssetLib/MDC/MDCLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_MD5_IMPORTER
# include "MD5/MD5Loader.h"
#include "AssetLib/MD5/MD5Loader.h"
#endif
#ifndef ASSIMP_BUILD_NO_STL_IMPORTER
# include "STL/STLLoader.h"
#include "AssetLib/STL/STLLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_LWO_IMPORTER
# include "LWO/LWOLoader.h"
#include "AssetLib/LWO/LWOLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_DXF_IMPORTER
# include "DXF/DXFLoader.h"
#include "AssetLib/DXF/DXFLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_NFF_IMPORTER
# include "NFF/NFFLoader.h"
#include "AssetLib/NFF/NFFLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_RAW_IMPORTER
# include "Raw/RawLoader.h"
#include "AssetLib/Raw/RawLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_SIB_IMPORTER
# include "SIB/SIBImporter.h"
#include "AssetLib/SIB/SIBImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_OFF_IMPORTER
# include "OFF/OFFLoader.h"
#include "AssetLib/OFF/OFFLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_AC_IMPORTER
# include "AC/ACLoader.h"
#include "AssetLib/AC/ACLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_BVH_IMPORTER
# include "BVH/BVHLoader.h"
#include "AssetLib/BVH/BVHLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_IRRMESH_IMPORTER
# include "Irr/IRRMeshLoader.h"
#include "AssetLib/Irr/IRRMeshLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_IRR_IMPORTER
# include "Irr/IRRLoader.h"
#include "AssetLib/Irr/IRRLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_Q3D_IMPORTER
# include "Q3D/Q3DLoader.h"
#include "AssetLib/Q3D/Q3DLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_B3D_IMPORTER
# include "B3D/B3DImporter.h"
#include "AssetLib/B3D/B3DImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_COLLADA_IMPORTER
# include "Collada/ColladaLoader.h"
#include "AssetLib/Collada/ColladaLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_TERRAGEN_IMPORTER
# include "Terragen/TerragenLoader.h"
#include "AssetLib/Terragen/TerragenLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_CSM_IMPORTER
# include "CSM/CSMLoader.h"
#include "AssetLib/CSM/CSMLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_3D_IMPORTER
# include "Unreal/UnrealLoader.h"
#include "AssetLib/Unreal/UnrealLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_LWS_IMPORTER
# include "LWS/LWSLoader.h"
#include "AssetLib/LWS/LWSLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_OGRE_IMPORTER
# include "Ogre/OgreImporter.h"
#include "AssetLib/Ogre/OgreImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_OPENGEX_IMPORTER
# include "OpenGEX/OpenGEXImporter.h"
#include "AssetLib/OpenGEX/OpenGEXImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_MS3D_IMPORTER
# include "MS3D/MS3DLoader.h"
#include "AssetLib/MS3D/MS3DLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_COB_IMPORTER
# include "COB/COBLoader.h"
#include "AssetLib/COB/COBLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
# include "Blender/BlenderLoader.h"
#include "AssetLib/Blender/BlenderLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_Q3BSP_IMPORTER
# include "Q3BSP/Q3BSPFileImporter.h"
#include "AssetLib/Q3BSP/Q3BSPFileImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_NDO_IMPORTER
# include "NDO/NDOLoader.h"
#include "AssetLib/NDO/NDOLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_IFC_IMPORTER
# include "Importer/IFC/IFCLoader.h"
#include "AssetLib/IFC/IFCLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_XGL_IMPORTER
# include "XGL/XGLLoader.h"
#include "AssetLib/XGL/XGLLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
# include "FBX/FBXImporter.h"
#include "AssetLib/FBX/FBXImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_ASSBIN_IMPORTER
# include "Assbin/AssbinLoader.h"
#include "AssetLib/Assbin/AssbinLoader.h"
#endif
#ifndef ASSIMP_BUILD_NO_GLTF_IMPORTER
# include "glTF/glTFImporter.h"
# include "glTF2/glTF2Importer.h"
#if !defined(ASSIMP_BUILD_NO_GLTF_IMPORTER) && !defined(ASSIMP_BUILD_NO_GLTF1_IMPORTER)
#include "AssetLib/glTF/glTFImporter.h"
#endif
#if !defined(ASSIMP_BUILD_NO_GLTF_IMPORTER) && !defined(ASSIMP_BUILD_NO_GLTF2_IMPORTER)
#include "AssetLib/glTF2/glTF2Importer.h"
#endif
#ifndef ASSIMP_BUILD_NO_C4D_IMPORTER
# include "C4D/C4DImporter.h"
#include "AssetLib/C4D/C4DImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_3MF_IMPORTER
# include "3MF/D3MFImporter.h"
#include "AssetLib/3MF/D3MFImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
# include "X3D/X3DImporter.hpp"
#include "AssetLib/X3D/X3DImporter.hpp"
#endif
#ifndef ASSIMP_BUILD_NO_MMD_IMPORTER
# include "MMD/MMDImporter.h"
#include "AssetLib/MMD/MMDImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_STEP_IMPORTER
# include "Importer/StepFile/StepFileImporter.h"
#ifndef ASSIMP_BUILD_NO_M3D_IMPORTER
#include "AssetLib/M3D/M3DImporter.h"
#endif
#ifndef ASSIMP_BUILD_NO_IQM_IMPORTER
#include "AssetLib/IQM/IQMImporter.h"
#endif
namespace Assimp {
// ------------------------------------------------------------------------------------------------
void GetImporterInstanceList(std::vector< BaseImporter* >& out)
{
void GetImporterInstanceList(std::vector<BaseImporter *> &out) {
// Some importers may be unimplemented or otherwise unsuitable for general use
// in their current state. Devs can set ASSIMP_ENABLE_DEV_IMPORTERS in their
// local environment to enable them, otherwise they're left out of the registry.
const char *envStr = std::getenv("ASSIMP_ENABLE_DEV_IMPORTERS");
bool devImportersEnabled = envStr && strcmp(envStr, "0");
// Ensure no unused var warnings if all uses are #ifndef'd away below:
(void)devImportersEnabled;
// ----------------------------------------------------------------------------
// Add an instance of each worker class here
// (register_new_importers_here)
// ----------------------------------------------------------------------------
out.reserve(64);
#if (!defined ASSIMP_BUILD_NO_X_IMPORTER)
out.push_back( new XFileImporter());
out.push_back(new XFileImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_OBJ_IMPORTER)
out.push_back( new ObjFileImporter());
out.push_back(new ObjFileImporter());
#endif
#ifndef ASSIMP_BUILD_NO_AMF_IMPORTER
out.push_back( new AMFImporter() );
out.push_back(new AMFImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_3DS_IMPORTER)
out.push_back( new Discreet3DSImporter());
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());
out.push_back(new MD3Importer());
#endif
#if (!defined ASSIMP_BUILD_NO_MD2_IMPORTER)
out.push_back( new MD2Importer());
out.push_back(new MD2Importer());
#endif
#if (!defined ASSIMP_BUILD_NO_PLY_IMPORTER)
out.push_back( new PLYImporter());
out.push_back(new PLYImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_MDL_IMPORTER)
out.push_back( new MDLImporter());
out.push_back(new MDLImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_ASE_IMPORTER)
#if (!defined ASSIMP_BUILD_NO_3DS_IMPORTER)
out.push_back( new ASEImporter());
# endif
#if (!defined ASSIMP_BUILD_NO_3DS_IMPORTER)
out.push_back(new ASEImporter());
#endif
#endif
#if (!defined ASSIMP_BUILD_NO_HMP_IMPORTER)
out.push_back( new HMPImporter());
out.push_back(new HMPImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_SMD_IMPORTER)
out.push_back( new SMDImporter());
out.push_back(new SMDImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_MDC_IMPORTER)
out.push_back( new MDCImporter());
out.push_back(new MDCImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_MD5_IMPORTER)
out.push_back( new MD5Importer());
out.push_back(new MD5Importer());
#endif
#if (!defined ASSIMP_BUILD_NO_STL_IMPORTER)
out.push_back( new STLImporter());
out.push_back(new STLImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_LWO_IMPORTER)
out.push_back( new LWOImporter());
out.push_back(new LWOImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_DXF_IMPORTER)
out.push_back( new DXFImporter());
out.push_back(new DXFImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_NFF_IMPORTER)
out.push_back( new NFFImporter());
out.push_back(new NFFImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_RAW_IMPORTER)
out.push_back( new RAWImporter());
out.push_back(new RAWImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_SIB_IMPORTER)
out.push_back( new SIBImporter());
out.push_back(new SIBImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_OFF_IMPORTER)
out.push_back( new OFFImporter());
out.push_back(new OFFImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_AC_IMPORTER)
out.push_back( new AC3DImporter());
out.push_back(new AC3DImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_BVH_IMPORTER)
out.push_back( new BVHLoader());
out.push_back(new BVHLoader());
#endif
#if (!defined ASSIMP_BUILD_NO_IRRMESH_IMPORTER)
out.push_back( new IRRMeshImporter());
out.push_back(new IRRMeshImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_IRR_IMPORTER)
out.push_back( new IRRImporter());
out.push_back(new IRRImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_Q3D_IMPORTER)
out.push_back( new Q3DImporter());
out.push_back(new Q3DImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_B3D_IMPORTER)
out.push_back( new B3DImporter());
out.push_back(new B3DImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_COLLADA_IMPORTER)
out.push_back( new ColladaLoader());
out.push_back(new ColladaLoader());
#endif
#if (!defined ASSIMP_BUILD_NO_TERRAGEN_IMPORTER)
out.push_back( new TerragenImporter());
out.push_back(new TerragenImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_CSM_IMPORTER)
out.push_back( new CSMImporter());
out.push_back(new CSMImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_3D_IMPORTER)
out.push_back( new UnrealImporter());
out.push_back(new UnrealImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_LWS_IMPORTER)
out.push_back( new LWSImporter());
out.push_back(new LWSImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_OGRE_IMPORTER)
out.push_back( new Ogre::OgreImporter());
out.push_back(new Ogre::OgreImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_OPENGEX_IMPORTER )
out.push_back( new OpenGEX::OpenGEXImporter() );
#if (!defined ASSIMP_BUILD_NO_OPENGEX_IMPORTER)
out.push_back(new OpenGEX::OpenGEXImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_MS3D_IMPORTER)
out.push_back( new MS3DImporter());
out.push_back(new MS3DImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_COB_IMPORTER)
out.push_back( new COBImporter());
out.push_back(new COBImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_BLEND_IMPORTER)
out.push_back( new BlenderImporter());
out.push_back(new BlenderImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_Q3BSP_IMPORTER)
out.push_back( new Q3BSPFileImporter() );
out.push_back(new Q3BSPFileImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_NDO_IMPORTER)
out.push_back( new NDOImporter() );
out.push_back(new NDOImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_IFC_IMPORTER)
out.push_back( new IFCImporter() );
out.push_back(new IFCImporter());
#endif
#if ( !defined ASSIMP_BUILD_NO_XGL_IMPORTER )
out.push_back( new XGLImporter() );
#if (!defined ASSIMP_BUILD_NO_XGL_IMPORTER)
out.push_back(new XGLImporter());
#endif
#if ( !defined ASSIMP_BUILD_NO_FBX_IMPORTER )
out.push_back( new FBXImporter() );
#if (!defined ASSIMP_BUILD_NO_FBX_IMPORTER)
out.push_back(new FBXImporter());
#endif
#if ( !defined ASSIMP_BUILD_NO_ASSBIN_IMPORTER )
out.push_back( new AssbinImporter() );
#if (!defined ASSIMP_BUILD_NO_ASSBIN_IMPORTER)
out.push_back(new AssbinImporter());
#endif
#if ( !defined ASSIMP_BUILD_NO_GLTF_IMPORTER )
out.push_back( new glTFImporter() );
out.push_back( new glTF2Importer() );
#if (!defined ASSIMP_BUILD_NO_GLTF_IMPORTER && !defined ASSIMP_BUILD_NO_GLTF1_IMPORTER)
out.push_back(new glTFImporter());
#endif
#if ( !defined ASSIMP_BUILD_NO_C4D_IMPORTER )
out.push_back( new C4DImporter() );
#if (!defined ASSIMP_BUILD_NO_GLTF_IMPORTER && !defined ASSIMP_BUILD_NO_GLTF2_IMPORTER)
out.push_back(new glTF2Importer());
#endif
#if ( !defined ASSIMP_BUILD_NO_3MF_IMPORTER )
out.push_back( new D3MFImporter() );
#if (!defined ASSIMP_BUILD_NO_C4D_IMPORTER)
out.push_back(new C4DImporter());
#endif
#if (!defined ASSIMP_BUILD_NO_3MF_IMPORTER)
out.push_back(new D3MFImporter());
#endif
#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
out.push_back( new X3DImporter() );
out.push_back(new X3DImporter());
#endif
#ifndef ASSIMP_BUILD_NO_MMD_IMPORTER
out.push_back( new MMDImporter() );
out.push_back(new MMDImporter());
#endif
#ifndef ASSIMP_BUILD_NO_STEP_IMPORTER
out.push_back(new StepFile::StepFileImporter());
#ifndef ASSIMP_BUILD_NO_IQM_IMPORTER
out.push_back(new IQMImporter());
#endif
//#ifndef ASSIMP_BUILD_NO_STEP_IMPORTER
// out.push_back(new StepFile::StepFileImporter());
//#endif
}
/** will delete all registered importers. */
void DeleteImporterInstanceList(std::vector< BaseImporter* >& deleteList){
for(size_t i= 0; i<deleteList.size();++i){
delete deleteList[i];
deleteList[i]=nullptr;
}//for
void DeleteImporterInstanceList(std::vector<BaseImporter *> &deleteList) {
for (size_t i = 0; i < deleteList.size(); ++i) {
delete deleteList[i];
deleteList[i] = nullptr;
} //for
}
} // namespace Assimp

View file

@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -42,6 +41,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/** @file PolyTools.h, various utilities for our dealings with arbitrary polygons */
#pragma once
#ifndef AI_POLYTOOLS_H_INCLUDED
#define AI_POLYTOOLS_H_INCLUDED
@ -55,8 +55,7 @@ namespace Assimp {
* The function accepts an unconstrained template parameter for use with
* both aiVector3D and aiVector2D, but generally ignores the third coordinate.*/
template <typename T>
inline double GetArea2D(const T& v1, const T& v2, const T& v3)
{
inline double GetArea2D(const T& v1, const T& v2, const T& v3) {
return 0.5 * (v1.x * ((double)v3.y - v2.y) + v2.x * ((double)v1.y - v3.y) + v3.x * ((double)v2.y - v1.y));
}
@ -65,8 +64,7 @@ inline double GetArea2D(const T& v1, const T& v2, const T& v3)
* The function accepts an unconstrained template parameter for use with
* both aiVector3D and aiVector2D, but generally ignores the third coordinate.*/
template <typename T>
inline bool OnLeftSideOfLine2D(const T& p0, const T& p1,const T& p2)
{
inline bool OnLeftSideOfLine2D(const T& p0, const T& p1,const T& p2) {
return GetArea2D(p0,p2,p1) > 0;
}
@ -75,20 +73,23 @@ inline bool OnLeftSideOfLine2D(const T& p0, const T& p1,const T& p2)
* The function accepts an unconstrained template parameter for use with
* both aiVector3D and aiVector2D, but generally ignores the third coordinate.*/
template <typename T>
inline bool PointInTriangle2D(const T& p0, const T& p1,const T& p2, const T& pp)
{
inline bool PointInTriangle2D(const T& p0, const T& p1,const T& p2, const T& pp) {
// Point in triangle test using baryzentric coordinates
const aiVector2D v0 = p1 - p0;
const aiVector2D v1 = p2 - p0;
const aiVector2D v2 = pp - p0;
double dot00 = v0 * v0;
double dot01 = v0 * v1;
double dot02 = v0 * v2;
double dot11 = v1 * v1;
double dot12 = v1 * v2;
const double invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
const double dot01 = v0 * v1;
const double dot02 = v0 * v2;
const double dot12 = v1 * v2;
const double denom = dot00 * dot11 - dot01 * dot01;
if (denom == 0.0) {
return false;
}
const double invDenom = 1.0 / denom;
dot11 = (dot11 * dot02 - dot01 * dot12) * invDenom;
dot00 = (dot00 * dot12 - dot01 * dot02) * invDenom;
@ -133,8 +134,7 @@ inline bool IsCCW(T* in, size_t npoints) {
// in[i+2].x, in[i+2].y)) {
convex_turn = AI_MATH_PI_F - theta;
convex_sum += convex_turn;
}
else {
} else {
convex_sum -= AI_MATH_PI_F - theta;
}
}
@ -161,15 +161,13 @@ inline bool IsCCW(T* in, size_t npoints) {
if (OnLeftSideOfLine2D(in[npoints-2],in[1],in[0])) {
convex_turn = AI_MATH_PI_F - theta;
convex_sum += convex_turn;
}
else {
} else {
convex_sum -= AI_MATH_PI_F - theta;
}
return convex_sum >= (2 * AI_MATH_PI_F);
}
// -------------------------------------------------------------------------------
/** Compute the normal of an arbitrary polygon in R3.
*
@ -186,8 +184,7 @@ inline bool IsCCW(T* in, size_t npoints) {
* this method is much faster than the 'other' NewellNormal()
*/
template <int ofs_x, int ofs_y, int ofs_z, typename TReal>
inline void NewellNormal (aiVector3t<TReal>& out, int num, TReal* x, TReal* y, TReal* z)
{
inline void NewellNormal (aiVector3t<TReal>& out, int num, TReal* x, TReal* y, TReal* z) {
// Duplicate the first two vertices at the end
x[(num+0)*ofs_x] = x[0];
x[(num+1)*ofs_x] = x[ofs_x];
@ -224,6 +221,6 @@ inline void NewellNormal (aiVector3t<TReal>& out, int num, TReal* x, TReal* y, T
out = aiVector3t<TReal>(sum_yz,sum_zx,sum_xy);
}
} // ! Assimp
} // ! namespace Assimp
#endif
#endif // AI_POLYTOOLS_H_INCLUDED

View file

@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
@ -123,7 +123,7 @@ corresponding preprocessor flag to selectively disable steps.
# include "PostProcessing/OptimizeGraph.h"
#endif
#ifndef ASSIMP_BUILD_NO_SPLITBYBONECOUNT_PROCESS
# include "Common/SplitByBoneCountProcess.h"
# include "PostProcessing/SplitByBoneCountProcess.h"
#endif
#ifndef ASSIMP_BUILD_NO_DEBONE_PROCESS
# include "PostProcessing/DeboneProcess.h"
@ -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

@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -40,69 +39,78 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** @file RemoveComments.cpp
/**
* @file RemoveComments.cpp
* @brief Defines the CommentRemover utility class
*/
#include <assimp/RemoveComments.h>
#include <assimp/ParsingUtils.h>
namespace Assimp {
namespace Assimp {
// ------------------------------------------------------------------------------------------------
// Remove line comments from a file
void CommentRemover::RemoveLineComments(const char* szComment,
char* szBuffer, char chReplacement /* = ' ' */)
{
void CommentRemover::RemoveLineComments(const char* szComment, char* szBuffer, char chReplacement /* = ' ' */) {
// validate parameters
ai_assert(NULL != szComment && NULL != szBuffer && *szComment);
ai_assert(nullptr != szComment);
ai_assert(nullptr != szBuffer);
ai_assert(*szComment);
const size_t len = strlen(szComment);
while (*szBuffer) {
size_t len = strlen(szComment);
const size_t lenBuffer = strlen(szBuffer);
if (len > lenBuffer) {
len = lenBuffer;
}
for(size_t i = 0; i < lenBuffer; i++) {
// skip over quotes
if (*szBuffer == '\"' || *szBuffer == '\'')
while (*szBuffer++ && *szBuffer != '\"' && *szBuffer != '\'');
if (szBuffer[i] == '\"' || szBuffer[i] == '\'')
while (++i < lenBuffer && szBuffer[i] != '\"' && szBuffer[i] != '\'');
if (!strncmp(szBuffer,szComment,len)) {
while (!IsLineEnd(*szBuffer))
*szBuffer++ = chReplacement;
if(lenBuffer - i < len) {
break;
}
if (!*szBuffer) {
break;
if (!strncmp(szBuffer + i,szComment,len)) {
while (i < lenBuffer && !IsLineEnd(szBuffer[i])) {
szBuffer[i++] = chReplacement;
}
}
++szBuffer;
}
}
// ------------------------------------------------------------------------------------------------
// Remove multi-line comments from a file
void CommentRemover::RemoveMultiLineComments(const char* szCommentStart,
const char* szCommentEnd,char* szBuffer,
char chReplacement)
{
const char* szCommentEnd,char* szBuffer,
char chReplacement) {
// validate parameters
ai_assert(NULL != szCommentStart && NULL != szCommentEnd &&
NULL != szBuffer && *szCommentStart && *szCommentEnd);
ai_assert(nullptr != szCommentStart);
ai_assert(nullptr != szCommentEnd);
ai_assert(nullptr != szBuffer);
ai_assert(*szCommentStart);
ai_assert(*szCommentEnd);
const size_t len = strlen(szCommentEnd);
const size_t len2 = strlen(szCommentStart);
while (*szBuffer) {
// skip over quotes
if (*szBuffer == '\"' || *szBuffer == '\'')
if (*szBuffer == '\"' || *szBuffer == '\'') {
while (*szBuffer++ && *szBuffer != '\"' && *szBuffer != '\'');
}
if (!strncmp(szBuffer,szCommentStart,len2)) {
while (*szBuffer) {
if (!::strncmp(szBuffer,szCommentEnd,len)) {
for (unsigned int i = 0; i < len;++i)
for (unsigned int i = 0; i < len;++i) {
*szBuffer++ = chReplacement;
}
break;
}
*szBuffer++ = chReplacement;
*szBuffer++ = chReplacement;
}
continue;
}

View file

@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
@ -52,7 +52,7 @@ using namespace Assimp;
// ------------------------------------------------------------------------------------------------
SGSpatialSort::SGSpatialSort()
{
// define the reference plane. We choose some arbitrary vector away from all basic axises
// define the reference plane. We choose some arbitrary vector away from all basic axes
// in the hope that no model spreads all its vertices along this plane.
mPlaneNormal.Set( 0.8523f, 0.34321f, 0.5736f);
mPlaneNormal.Normalize();
@ -121,7 +121,7 @@ void SGSpatialSort::FindPositions( const aiVector3D& pPosition,
index++;
// Mow start iterating from there until the first position lays outside of the distance range.
// Add all positions inside the distance range within the given radius to the result aray
// Add all positions inside the distance range within the given radius to the result array
float squareEpsilon = pRadius * pRadius;
std::vector<Entry>::const_iterator it = mPositions.begin() + index;

File diff suppressed because it is too large Load diff

View file

@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -45,17 +44,19 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/scene.h>
#include <assimp/DefaultLogger.hpp>
using namespace Assimp;
// ---------------------------------------------------------------------------------------------
void ScenePreprocessor::ProcessScene ()
{
ai_assert(scene != NULL);
void ScenePreprocessor::ProcessScene() {
ai_assert(scene != nullptr);
// Process all meshes
for (unsigned int i = 0; i < scene->mNumMeshes;++i)
for (unsigned int i = 0; i < scene->mNumMeshes; ++i) {
if (nullptr == scene->mMeshes[i]) {
continue;
}
ProcessMesh(scene->mMeshes[i]);
}
// - nothing to do for nodes for the moment
// - nothing to do for textures for the moment
@ -63,27 +64,34 @@ void ScenePreprocessor::ProcessScene ()
// - nothing to do for cameras for the moment
// Process all animations
for (unsigned int i = 0; i < scene->mNumAnimations;++i)
for (unsigned int i = 0; i < scene->mNumAnimations; ++i) {
if (nullptr == scene->mAnimations[i]) {
continue;
}
ProcessAnimation(scene->mAnimations[i]);
}
// Generate a default material if none was specified
if (!scene->mNumMaterials && scene->mNumMeshes) {
scene->mMaterials = new aiMaterial*[2];
aiMaterial* helper;
scene->mMaterials = new aiMaterial *[2];
aiMaterial *helper;
aiString name;
scene->mMaterials[scene->mNumMaterials] = helper = new aiMaterial();
aiColor3D clr(0.6f,0.6f,0.6f);
helper->AddProperty(&clr,1,AI_MATKEY_COLOR_DIFFUSE);
aiColor3D clr(0.6f, 0.6f, 0.6f);
helper->AddProperty(&clr, 1, AI_MATKEY_COLOR_DIFFUSE);
// setup the default name to make this material identifiable
name.Set(AI_DEFAULT_MATERIAL_NAME);
helper->AddProperty(&name,AI_MATKEY_NAME);
helper->AddProperty(&name, AI_MATKEY_NAME);
ASSIMP_LOG_DEBUG("ScenePreprocessor: Adding default material \'" AI_DEFAULT_MATERIAL_NAME "\'");
ASSIMP_LOG_DEBUG("ScenePreprocessor: Adding default material \'" AI_DEFAULT_MATERIAL_NAME "\'");
for (unsigned int i = 0; i < scene->mNumMeshes;++i) {
for (unsigned int i = 0; i < scene->mNumMeshes; ++i) {
if (nullptr == scene->mMeshes[i]) {
continue;
}
scene->mMeshes[i]->mMaterialIndex = scene->mNumMaterials;
}
@ -92,34 +100,35 @@ void ScenePreprocessor::ProcessScene ()
}
// ---------------------------------------------------------------------------------------------
void ScenePreprocessor::ProcessMesh (aiMesh* mesh)
{
void ScenePreprocessor::ProcessMesh(aiMesh *mesh) {
// If aiMesh::mNumUVComponents is *not* set assign the default value of 2
for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
if (!mesh->mTextureCoords[i]) {
mesh->mNumUVComponents[i] = 0;
} else {
if (!mesh->mNumUVComponents[i])
if (!mesh->mNumUVComponents[i]) {
mesh->mNumUVComponents[i] = 2;
}
aiVector3D* p = mesh->mTextureCoords[i], *end = p+mesh->mNumVertices;
aiVector3D *p = mesh->mTextureCoords[i], *end = p + mesh->mNumVertices;
// Ensure unused components are zeroed. This will make 1D texture channels work
// as if they were 2D channels .. just in case an application doesn't handle
// this case
if (2 == mesh->mNumUVComponents[i]) {
for (; p != end; ++p)
for (; p != end; ++p) {
p->z = 0.f;
}
else if (1 == mesh->mNumUVComponents[i]) {
for (; p != end; ++p)
}
} else if (1 == mesh->mNumUVComponents[i]) {
for (; p != end; ++p) {
p->z = p->y = 0.f;
}
else if (3 == mesh->mNumUVComponents[i]) {
}
} else if (3 == mesh->mNumUVComponents[i]) {
// Really 3D coordinates? Check whether the third coordinate is != 0 for at least one element
for (; p != end; ++p) {
if (p->z != 0)
if (p->z != 0) {
break;
}
}
if (p == end) {
ASSIMP_LOG_WARN("ScenePreprocessor: UVs are declared to be 3D but they're obviously not. Reverting to 2D.");
@ -132,10 +141,9 @@ void ScenePreprocessor::ProcessMesh (aiMesh* mesh)
// If the information which primitive types are there in the
// mesh is currently not available, compute it.
if (!mesh->mPrimitiveTypes) {
for (unsigned int a = 0; a < mesh->mNumFaces; ++a) {
aiFace& face = mesh->mFaces[a];
switch (face.mNumIndices)
{
for (unsigned int a = 0; a < mesh->mNumFaces; ++a) {
aiFace &face = mesh->mFaces[a];
switch (face.mNumIndices) {
case 3u:
mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
break;
@ -156,106 +164,122 @@ void ScenePreprocessor::ProcessMesh (aiMesh* mesh)
}
// If tangents and normals are given but no bitangents compute them
if (mesh->mTangents && mesh->mNormals && !mesh->mBitangents) {
if (mesh->mTangents && mesh->mNormals && !mesh->mBitangents) {
mesh->mBitangents = new aiVector3D[mesh->mNumVertices];
for (unsigned int i = 0; i < mesh->mNumVertices;++i) {
for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
mesh->mBitangents[i] = mesh->mNormals[i] ^ mesh->mTangents[i];
}
}
}
// ---------------------------------------------------------------------------------------------
void ScenePreprocessor::ProcessAnimation (aiAnimation* anim)
{
void ScenePreprocessor::ProcessAnimation(aiAnimation *anim) {
double first = 10e10, last = -10e10;
for (unsigned int i = 0; i < anim->mNumChannels;++i) {
aiNodeAnim* channel = anim->mChannels[i];
for (unsigned int i = 0; i < anim->mNumChannels; ++i) {
aiNodeAnim *channel = anim->mChannels[i];
/* If the exact duration of the animation is not given
* compute it now.
*/
// If the exact duration of the animation is not given
// compute it now.
if (anim->mDuration == -1.) {
// Position keys
for (unsigned int j = 0; j < channel->mNumPositionKeys;++j) {
aiVectorKey& key = channel->mPositionKeys[j];
first = std::min (first, key.mTime);
last = std::max (last, key.mTime);
for (unsigned int j = 0; j < channel->mNumPositionKeys; ++j) {
aiVectorKey &key = channel->mPositionKeys[j];
first = std::min(first, key.mTime);
last = std::max(last, key.mTime);
}
// Scaling keys
for (unsigned int j = 0; j < channel->mNumScalingKeys;++j ) {
aiVectorKey& key = channel->mScalingKeys[j];
first = std::min (first, key.mTime);
last = std::max (last, key.mTime);
for (unsigned int j = 0; j < channel->mNumScalingKeys; ++j) {
aiVectorKey &key = channel->mScalingKeys[j];
first = std::min(first, key.mTime);
last = std::max(last, key.mTime);
}
// Rotation keys
for (unsigned int j = 0; j < channel->mNumRotationKeys;++j ) {
aiQuatKey& key = channel->mRotationKeys[ j ];
first = std::min (first, key.mTime);
last = std::max (last, key.mTime);
for (unsigned int j = 0; j < channel->mNumRotationKeys; ++j) {
aiQuatKey &key = channel->mRotationKeys[j];
first = std::min(first, key.mTime);
last = std::max(last, key.mTime);
}
}
/* Check whether the animation channel has no rotation
* or position tracks. In this case we generate a dummy
* track from the information we have in the transformation
* matrix of the corresponding node.
*/
if (!channel->mNumRotationKeys || !channel->mNumPositionKeys || !channel->mNumScalingKeys) {
// Check whether the animation channel has no rotation
// or position tracks. In this case we generate a dummy
// track from the information we have in the transformation
// matrix of the corresponding node.
if (!channel->mNumRotationKeys || !channel->mNumPositionKeys || !channel->mNumScalingKeys) {
// Find the node that belongs to this animation
aiNode* node = scene->mRootNode->FindNode(channel->mNodeName);
if (node) // ValidateDS will complain later if 'node' is NULL
aiNode *node = scene->mRootNode->FindNode(channel->mNodeName);
if (node) // ValidateDS will complain later if 'node' is nullptr
{
// Decompose the transformation matrix of the node
aiVector3D scaling, position;
aiQuaternion rotation;
node->mTransformation.Decompose(scaling, rotation,position);
node->mTransformation.Decompose(scaling, rotation, position);
// No rotation keys? Generate a dummy track
if (!channel->mNumRotationKeys) {
if (channel->mRotationKeys) {
delete[] channel->mRotationKeys;
channel->mRotationKeys = nullptr;
}
ai_assert(!channel->mRotationKeys);
channel->mNumRotationKeys = 1;
channel->mRotationKeys = new aiQuatKey[1];
aiQuatKey& q = channel->mRotationKeys[0];
aiQuatKey &q = channel->mRotationKeys[0];
q.mTime = 0.;
q.mTime = 0.;
q.mValue = rotation;
ASSIMP_LOG_DEBUG("ScenePreprocessor: Dummy rotation track has been generated");
ASSIMP_LOG_VERBOSE_DEBUG("ScenePreprocessor: Dummy rotation track has been generated");
} else {
ai_assert(channel->mRotationKeys);
}
// No scaling keys? Generate a dummy track
if (!channel->mNumScalingKeys) {
if (!channel->mNumScalingKeys) {
if (channel->mScalingKeys) {
delete[] channel->mScalingKeys;
channel->mScalingKeys = nullptr;
}
ai_assert(!channel->mScalingKeys);
channel->mNumScalingKeys = 1;
channel->mScalingKeys = new aiVectorKey[1];
aiVectorKey& q = channel->mScalingKeys[0];
aiVectorKey &q = channel->mScalingKeys[0];
q.mTime = 0.;
q.mTime = 0.;
q.mValue = scaling;
ASSIMP_LOG_DEBUG("ScenePreprocessor: Dummy scaling track has been generated");
ASSIMP_LOG_VERBOSE_DEBUG("ScenePreprocessor: Dummy scaling track has been generated");
} else {
ai_assert(channel->mScalingKeys);
}
// No position keys? Generate a dummy track
if (!channel->mNumPositionKeys) {
if (channel->mPositionKeys) {
delete[] channel->mPositionKeys;
channel->mPositionKeys = nullptr;
}
ai_assert(!channel->mPositionKeys);
channel->mNumPositionKeys = 1;
channel->mPositionKeys = new aiVectorKey[1];
aiVectorKey& q = channel->mPositionKeys[0];
aiVectorKey &q = channel->mPositionKeys[0];
q.mTime = 0.;
q.mTime = 0.;
q.mValue = position;
ASSIMP_LOG_DEBUG("ScenePreprocessor: Dummy position track has been generated");
ASSIMP_LOG_VERBOSE_DEBUG("ScenePreprocessor: Dummy position track has been generated");
} else {
ai_assert(channel->mPositionKeys);
}
}
}
}
if (anim->mDuration == -1.) {
ASSIMP_LOG_DEBUG("ScenePreprocessor: Setting animation duration");
anim->mDuration = last - std::min( first, 0. );
if (anim->mDuration == -1.) {
ASSIMP_LOG_VERBOSE_DEBUG("ScenePreprocessor: Setting animation duration");
anim->mDuration = last - std::min(first, 0.);
}
}

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -53,7 +53,7 @@ struct aiAnimation;
struct aiMesh;
class ScenePreprocessorTest;
namespace Assimp {
namespace Assimp {
// ----------------------------------------------------------------------------------
/** ScenePreprocessor: Preprocess a scene before any post-processing
@ -63,24 +63,21 @@ namespace Assimp {
* importer, such as aiMesh::mPrimitiveTypes.
*/
// ----------------------------------------------------------------------------------
class ASSIMP_API ScenePreprocessor
{
class ASSIMP_API ScenePreprocessor {
// Make ourselves a friend of the corresponding test unit.
friend class ::ScenePreprocessorTest;
public:
public:
// ----------------------------------------------------------------
/** Default c'tpr. Use SetScene() to assign a scene to the object.
*/
ScenePreprocessor()
: scene (NULL)
{}
ScenePreprocessor() :
scene(nullptr) {}
/** Constructs the object and assigns a specific scene to it
*/
ScenePreprocessor(aiScene* _scene)
: scene (_scene)
{}
ScenePreprocessor(aiScene *_scene) :
scene(_scene) {}
// ----------------------------------------------------------------
/** Assign a (new) scene to the object.
@ -89,37 +86,33 @@ public:
* Call ProcessScene to have the scene preprocessed.
* @param sc Scene to be processed.
*/
void SetScene (aiScene* sc) {
void SetScene(aiScene *sc) {
scene = sc;
}
// ----------------------------------------------------------------
/** Preprocess the current scene
*/
void ProcessScene ();
void ProcessScene();
protected:
// ----------------------------------------------------------------
/** Preprocess an animation in the scene
* @param anim Anim to be preprocessed.
*/
void ProcessAnimation (aiAnimation* anim);
void ProcessAnimation(aiAnimation *anim);
// ----------------------------------------------------------------
/** Preprocess a mesh in the scene
* @param mesh Mesh to be preprocessed.
*/
void ProcessMesh (aiMesh* mesh);
void ProcessMesh(aiMesh *mesh);
protected:
//! Scene we're currently working on
aiScene* scene;
aiScene *scene;
};
} // ! end namespace Assimp
} // namespace Assimp
#endif // include guard

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -44,30 +44,31 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* @brief Implementation of a little class to construct a dummy mesh for a skeleton
*/
#include <assimp/scene.h>
#include <assimp/SkeletonMeshBuilder.h>
#include <assimp/scene.h>
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// The constructor processes the given scene and adds a mesh there.
SkeletonMeshBuilder::SkeletonMeshBuilder( aiScene* pScene, aiNode* root, bool bKnobsOnly)
{
SkeletonMeshBuilder::SkeletonMeshBuilder(aiScene *pScene, aiNode *root, bool bKnobsOnly) {
// nothing to do if there's mesh data already present at the scene
if( pScene->mNumMeshes > 0 || pScene->mRootNode == NULL)
if (pScene->mNumMeshes > 0 || pScene->mRootNode == nullptr) {
return;
}
if (!root)
if (!root) {
root = pScene->mRootNode;
}
mKnobsOnly = bKnobsOnly;
// build some faces around each node
CreateGeometry( root );
CreateGeometry(root);
// create a mesh to hold all the generated faces
pScene->mNumMeshes = 1;
pScene->mMeshes = new aiMesh*[1];
pScene->mMeshes = new aiMesh *[1];
pScene->mMeshes[0] = CreateMesh();
// and install it at the root node
root->mNumMeshes = 1;
@ -75,154 +76,146 @@ SkeletonMeshBuilder::SkeletonMeshBuilder( aiScene* pScene, aiNode* root, bool bK
root->mMeshes[0] = 0;
// create a dummy material for the mesh
if(pScene->mNumMaterials==0){
pScene->mNumMaterials = 1;
pScene->mMaterials = new aiMaterial*[1];
pScene->mMaterials[0] = CreateMaterial();
if (pScene->mNumMaterials == 0) {
pScene->mNumMaterials = 1;
pScene->mMaterials = new aiMaterial *[1];
pScene->mMaterials[0] = CreateMaterial();
}
}
// ------------------------------------------------------------------------------------------------
// Recursively builds a simple mesh representation for the given node
void SkeletonMeshBuilder::CreateGeometry( const aiNode* pNode)
{
void SkeletonMeshBuilder::CreateGeometry(const aiNode *pNode) {
// add a joint entry for the node.
const unsigned int vertexStartIndex = static_cast<unsigned int>(mVertices.size());
// now build the geometry.
if( pNode->mNumChildren > 0 && !mKnobsOnly)
{
if (pNode->mNumChildren > 0 && !mKnobsOnly) {
// If the node has children, we build little pointers to each of them
for( unsigned int a = 0; a < pNode->mNumChildren; a++)
{
for (unsigned int a = 0; a < pNode->mNumChildren; a++) {
// find a suitable coordinate system
const aiMatrix4x4& childTransform = pNode->mChildren[a]->mTransformation;
aiVector3D childpos( childTransform.a4, childTransform.b4, childTransform.c4);
const aiMatrix4x4 &childTransform = pNode->mChildren[a]->mTransformation;
aiVector3D childpos(childTransform.a4, childTransform.b4, childTransform.c4);
ai_real distanceToChild = childpos.Length();
if( distanceToChild < 0.0001)
if (distanceToChild < 0.0001)
continue;
aiVector3D up = aiVector3D( childpos).Normalize();
aiVector3D up = aiVector3D(childpos).Normalize();
aiVector3D orth( 1.0, 0.0, 0.0);
if( std::fabs( orth * up) > 0.99)
orth.Set( 0.0, 1.0, 0.0);
aiVector3D orth(1.0, 0.0, 0.0);
if (std::fabs(orth * up) > 0.99)
orth.Set(0.0, 1.0, 0.0);
aiVector3D front = (up ^ orth).Normalize();
aiVector3D side = (front ^ up).Normalize();
unsigned int localVertexStart = static_cast<unsigned int>(mVertices.size());
mVertices.push_back( -front * distanceToChild * (ai_real)0.1);
mVertices.push_back( childpos);
mVertices.push_back( -side * distanceToChild * (ai_real)0.1);
mVertices.push_back( -side * distanceToChild * (ai_real)0.1);
mVertices.push_back( childpos);
mVertices.push_back( front * distanceToChild * (ai_real)0.1);
mVertices.push_back( front * distanceToChild * (ai_real)0.1);
mVertices.push_back( childpos);
mVertices.push_back( side * distanceToChild * (ai_real)0.1);
mVertices.push_back( side * distanceToChild * (ai_real)0.1);
mVertices.push_back( childpos);
mVertices.push_back( -front * distanceToChild * (ai_real)0.1);
mVertices.push_back(-front * distanceToChild * (ai_real)0.1);
mVertices.push_back(childpos);
mVertices.push_back(-side * distanceToChild * (ai_real)0.1);
mVertices.push_back(-side * distanceToChild * (ai_real)0.1);
mVertices.push_back(childpos);
mVertices.push_back(front * distanceToChild * (ai_real)0.1);
mVertices.push_back(front * distanceToChild * (ai_real)0.1);
mVertices.push_back(childpos);
mVertices.push_back(side * distanceToChild * (ai_real)0.1);
mVertices.push_back(side * distanceToChild * (ai_real)0.1);
mVertices.push_back(childpos);
mVertices.push_back(-front * distanceToChild * (ai_real)0.1);
mFaces.push_back( Face( localVertexStart + 0, localVertexStart + 1, localVertexStart + 2));
mFaces.push_back( Face( localVertexStart + 3, localVertexStart + 4, localVertexStart + 5));
mFaces.push_back( Face( localVertexStart + 6, localVertexStart + 7, localVertexStart + 8));
mFaces.push_back( Face( localVertexStart + 9, localVertexStart + 10, localVertexStart + 11));
mFaces.push_back(Face(localVertexStart + 0, localVertexStart + 1, localVertexStart + 2));
mFaces.push_back(Face(localVertexStart + 3, localVertexStart + 4, localVertexStart + 5));
mFaces.push_back(Face(localVertexStart + 6, localVertexStart + 7, localVertexStart + 8));
mFaces.push_back(Face(localVertexStart + 9, localVertexStart + 10, localVertexStart + 11));
}
}
else
{
} else {
// if the node has no children, it's an end node. Put a little knob there instead
aiVector3D ownpos( pNode->mTransformation.a4, pNode->mTransformation.b4, pNode->mTransformation.c4);
ai_real sizeEstimate = ownpos.Length() * ai_real( 0.18 );
aiVector3D ownpos(pNode->mTransformation.a4, pNode->mTransformation.b4, pNode->mTransformation.c4);
ai_real sizeEstimate = ownpos.Length() * ai_real(0.18);
mVertices.push_back( aiVector3D( -sizeEstimate, 0.0, 0.0));
mVertices.push_back( aiVector3D( 0.0, sizeEstimate, 0.0));
mVertices.push_back( aiVector3D( 0.0, 0.0, -sizeEstimate));
mVertices.push_back( aiVector3D( 0.0, sizeEstimate, 0.0));
mVertices.push_back( aiVector3D( sizeEstimate, 0.0, 0.0));
mVertices.push_back( aiVector3D( 0.0, 0.0, -sizeEstimate));
mVertices.push_back( aiVector3D( sizeEstimate, 0.0, 0.0));
mVertices.push_back( aiVector3D( 0.0, -sizeEstimate, 0.0));
mVertices.push_back( aiVector3D( 0.0, 0.0, -sizeEstimate));
mVertices.push_back( aiVector3D( 0.0, -sizeEstimate, 0.0));
mVertices.push_back( aiVector3D( -sizeEstimate, 0.0, 0.0));
mVertices.push_back( aiVector3D( 0.0, 0.0, -sizeEstimate));
mVertices.push_back(aiVector3D(-sizeEstimate, 0.0, 0.0));
mVertices.push_back(aiVector3D(0.0, sizeEstimate, 0.0));
mVertices.push_back(aiVector3D(0.0, 0.0, -sizeEstimate));
mVertices.push_back(aiVector3D(0.0, sizeEstimate, 0.0));
mVertices.push_back(aiVector3D(sizeEstimate, 0.0, 0.0));
mVertices.push_back(aiVector3D(0.0, 0.0, -sizeEstimate));
mVertices.push_back(aiVector3D(sizeEstimate, 0.0, 0.0));
mVertices.push_back(aiVector3D(0.0, -sizeEstimate, 0.0));
mVertices.push_back(aiVector3D(0.0, 0.0, -sizeEstimate));
mVertices.push_back(aiVector3D(0.0, -sizeEstimate, 0.0));
mVertices.push_back(aiVector3D(-sizeEstimate, 0.0, 0.0));
mVertices.push_back(aiVector3D(0.0, 0.0, -sizeEstimate));
mVertices.push_back( aiVector3D( -sizeEstimate, 0.0, 0.0));
mVertices.push_back( aiVector3D( 0.0, 0.0, sizeEstimate));
mVertices.push_back( aiVector3D( 0.0, sizeEstimate, 0.0));
mVertices.push_back( aiVector3D( 0.0, sizeEstimate, 0.0));
mVertices.push_back( aiVector3D( 0.0, 0.0, sizeEstimate));
mVertices.push_back( aiVector3D( sizeEstimate, 0.0, 0.0));
mVertices.push_back( aiVector3D( sizeEstimate, 0.0, 0.0));
mVertices.push_back( aiVector3D( 0.0, 0.0, sizeEstimate));
mVertices.push_back( aiVector3D( 0.0, -sizeEstimate, 0.0));
mVertices.push_back( aiVector3D( 0.0, -sizeEstimate, 0.0));
mVertices.push_back( aiVector3D( 0.0, 0.0, sizeEstimate));
mVertices.push_back( aiVector3D( -sizeEstimate, 0.0, 0.0));
mVertices.push_back(aiVector3D(-sizeEstimate, 0.0, 0.0));
mVertices.push_back(aiVector3D(0.0, 0.0, sizeEstimate));
mVertices.push_back(aiVector3D(0.0, sizeEstimate, 0.0));
mVertices.push_back(aiVector3D(0.0, sizeEstimate, 0.0));
mVertices.push_back(aiVector3D(0.0, 0.0, sizeEstimate));
mVertices.push_back(aiVector3D(sizeEstimate, 0.0, 0.0));
mVertices.push_back(aiVector3D(sizeEstimate, 0.0, 0.0));
mVertices.push_back(aiVector3D(0.0, 0.0, sizeEstimate));
mVertices.push_back(aiVector3D(0.0, -sizeEstimate, 0.0));
mVertices.push_back(aiVector3D(0.0, -sizeEstimate, 0.0));
mVertices.push_back(aiVector3D(0.0, 0.0, sizeEstimate));
mVertices.push_back(aiVector3D(-sizeEstimate, 0.0, 0.0));
mFaces.push_back( Face( vertexStartIndex + 0, vertexStartIndex + 1, vertexStartIndex + 2));
mFaces.push_back( Face( vertexStartIndex + 3, vertexStartIndex + 4, vertexStartIndex + 5));
mFaces.push_back( Face( vertexStartIndex + 6, vertexStartIndex + 7, vertexStartIndex + 8));
mFaces.push_back( Face( vertexStartIndex + 9, vertexStartIndex + 10, vertexStartIndex + 11));
mFaces.push_back( Face( vertexStartIndex + 12, vertexStartIndex + 13, vertexStartIndex + 14));
mFaces.push_back( Face( vertexStartIndex + 15, vertexStartIndex + 16, vertexStartIndex + 17));
mFaces.push_back( Face( vertexStartIndex + 18, vertexStartIndex + 19, vertexStartIndex + 20));
mFaces.push_back( Face( vertexStartIndex + 21, vertexStartIndex + 22, vertexStartIndex + 23));
mFaces.push_back(Face(vertexStartIndex + 0, vertexStartIndex + 1, vertexStartIndex + 2));
mFaces.push_back(Face(vertexStartIndex + 3, vertexStartIndex + 4, vertexStartIndex + 5));
mFaces.push_back(Face(vertexStartIndex + 6, vertexStartIndex + 7, vertexStartIndex + 8));
mFaces.push_back(Face(vertexStartIndex + 9, vertexStartIndex + 10, vertexStartIndex + 11));
mFaces.push_back(Face(vertexStartIndex + 12, vertexStartIndex + 13, vertexStartIndex + 14));
mFaces.push_back(Face(vertexStartIndex + 15, vertexStartIndex + 16, vertexStartIndex + 17));
mFaces.push_back(Face(vertexStartIndex + 18, vertexStartIndex + 19, vertexStartIndex + 20));
mFaces.push_back(Face(vertexStartIndex + 21, vertexStartIndex + 22, vertexStartIndex + 23));
}
unsigned int numVertices = static_cast<unsigned int>(mVertices.size() - vertexStartIndex);
if( numVertices > 0)
{
if (numVertices > 0) {
// create a bone affecting all the newly created vertices
aiBone* bone = new aiBone;
mBones.push_back( bone);
aiBone *bone = new aiBone;
mBones.push_back(bone);
bone->mName = pNode->mName;
// calculate the bone offset matrix by concatenating the inverse transformations of all parents
bone->mOffsetMatrix = aiMatrix4x4( pNode->mTransformation).Inverse();
for( aiNode* parent = pNode->mParent; parent != NULL; parent = parent->mParent)
bone->mOffsetMatrix = aiMatrix4x4( parent->mTransformation).Inverse() * bone->mOffsetMatrix;
bone->mOffsetMatrix = aiMatrix4x4(pNode->mTransformation).Inverse();
for (aiNode *parent = pNode->mParent; parent != nullptr; parent = parent->mParent)
bone->mOffsetMatrix = aiMatrix4x4(parent->mTransformation).Inverse() * bone->mOffsetMatrix;
// add all the vertices to the bone's influences
bone->mNumWeights = numVertices;
bone->mWeights = new aiVertexWeight[numVertices];
for( unsigned int a = 0; a < numVertices; a++)
bone->mWeights[a] = aiVertexWeight( vertexStartIndex + a, 1.0);
for (unsigned int a = 0; a < numVertices; a++)
bone->mWeights[a] = aiVertexWeight(vertexStartIndex + a, 1.0);
// HACK: (thom) transform all vertices to the bone's local space. Should be done before adding
// them to the array, but I'm tired now and I'm annoyed.
aiMatrix4x4 boneToMeshTransform = aiMatrix4x4( bone->mOffsetMatrix).Inverse();
for( unsigned int a = vertexStartIndex; a < mVertices.size(); a++)
aiMatrix4x4 boneToMeshTransform = aiMatrix4x4(bone->mOffsetMatrix).Inverse();
for (unsigned int a = vertexStartIndex; a < mVertices.size(); a++)
mVertices[a] = boneToMeshTransform * mVertices[a];
}
// and finally recurse into the children list
for( unsigned int a = 0; a < pNode->mNumChildren; a++)
CreateGeometry( pNode->mChildren[a]);
for (unsigned int a = 0; a < pNode->mNumChildren; a++)
CreateGeometry(pNode->mChildren[a]);
}
// ------------------------------------------------------------------------------------------------
// Creates the mesh from the internally accumulated stuff and returns it.
aiMesh* SkeletonMeshBuilder::CreateMesh()
{
aiMesh* mesh = new aiMesh();
aiMesh *SkeletonMeshBuilder::CreateMesh() {
aiMesh *mesh = new aiMesh();
// add points
mesh->mNumVertices = static_cast<unsigned int>(mVertices.size());
mesh->mVertices = new aiVector3D[mesh->mNumVertices];
std::copy( mVertices.begin(), mVertices.end(), mesh->mVertices);
std::copy(mVertices.begin(), mVertices.end(), mesh->mVertices);
mesh->mNormals = new aiVector3D[mesh->mNumVertices];
// add faces
mesh->mNumFaces = static_cast<unsigned int>(mFaces.size());
mesh->mFaces = new aiFace[mesh->mNumFaces];
for( unsigned int a = 0; a < mesh->mNumFaces; a++)
{
const Face& inface = mFaces[a];
aiFace& outface = mesh->mFaces[a];
for (unsigned int a = 0; a < mesh->mNumFaces; a++) {
const Face &inface = mFaces[a];
aiFace &outface = mesh->mFaces[a];
outface.mNumIndices = 3;
outface.mIndices = new unsigned int[3];
outface.mIndices[0] = inface.mIndices[0];
@ -232,10 +225,10 @@ aiMesh* SkeletonMeshBuilder::CreateMesh()
// Compute per-face normals ... we don't want the bones to be smoothed ... they're built to visualize
// the skeleton, so it's good if there's a visual difference to the rest of the geometry
aiVector3D nor = ((mVertices[inface.mIndices[2]] - mVertices[inface.mIndices[0]]) ^
(mVertices[inface.mIndices[1]] - mVertices[inface.mIndices[0]]));
(mVertices[inface.mIndices[1]] - mVertices[inface.mIndices[0]]));
if (nor.Length() < 1e-5) /* ensure that FindInvalidData won't remove us ...*/
nor = aiVector3D(1.0,0.0,0.0);
nor = aiVector3D(1.0, 0.0, 0.0);
for (unsigned int n = 0; n < 3; ++n)
mesh->mNormals[inface.mIndices[n]] = nor;
@ -243,8 +236,8 @@ aiMesh* SkeletonMeshBuilder::CreateMesh()
// add the bones
mesh->mNumBones = static_cast<unsigned int>(mBones.size());
mesh->mBones = new aiBone*[mesh->mNumBones];
std::copy( mBones.begin(), mBones.end(), mesh->mBones);
mesh->mBones = new aiBone *[mesh->mNumBones];
std::copy(mBones.begin(), mBones.end(), mesh->mBones);
// default
mesh->mMaterialIndex = 0;
@ -254,17 +247,16 @@ aiMesh* SkeletonMeshBuilder::CreateMesh()
// ------------------------------------------------------------------------------------------------
// Creates a dummy material and returns it.
aiMaterial* SkeletonMeshBuilder::CreateMaterial()
{
aiMaterial* matHelper = new aiMaterial;
aiMaterial *SkeletonMeshBuilder::CreateMaterial() {
aiMaterial *matHelper = new aiMaterial;
// Name
aiString matName( std::string( "SkeletonMaterial"));
matHelper->AddProperty( &matName, AI_MATKEY_NAME);
aiString matName(std::string("SkeletonMaterial"));
matHelper->AddProperty(&matName, AI_MATKEY_NAME);
// Prevent backface culling
const int no_cull = 1;
matHelper->AddProperty(&no_cull,1,AI_MATKEY_TWOSIDED);
matHelper->AddProperty(&no_cull, 1, AI_MATKEY_TWOSIDED);
return matHelper;
}

View file

@ -3,9 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -50,67 +48,75 @@ using namespace Assimp;
// CHAR_BIT seems to be defined under MVSC, but not under GCC. Pray that the correct value is 8.
#ifndef CHAR_BIT
# define CHAR_BIT 8
#define CHAR_BIT 8
#endif
const aiVector3D PlaneInit(0.8523f, 0.34321f, 0.5736f);
// ------------------------------------------------------------------------------------------------
// Constructs a spatially sorted representation from the given position array.
SpatialSort::SpatialSort( const aiVector3D* pPositions, unsigned int pNumPositions,
unsigned int pElementOffset)
// define the reference plane. We choose some arbitrary vector away from all basic axises
// in the hope that no model spreads all its vertices along this plane.
: mPlaneNormal(0.8523f, 0.34321f, 0.5736f)
{
// define the reference plane. We choose some arbitrary vector away from all basic axes
// in the hope that no model spreads all its vertices along this plane.
SpatialSort::SpatialSort(const aiVector3D *pPositions, unsigned int pNumPositions, unsigned int pElementOffset) :
mPlaneNormal(PlaneInit),
mFinalized(false) {
mPlaneNormal.Normalize();
Fill(pPositions,pNumPositions,pElementOffset);
Fill(pPositions, pNumPositions, pElementOffset);
}
// ------------------------------------------------------------------------------------------------
SpatialSort :: SpatialSort()
: mPlaneNormal(0.8523f, 0.34321f, 0.5736f)
{
SpatialSort::SpatialSort() :
mPlaneNormal(PlaneInit),
mFinalized(false) {
mPlaneNormal.Normalize();
}
// ------------------------------------------------------------------------------------------------
// Destructor
SpatialSort::~SpatialSort()
{
// nothing to do here, everything destructs automatically
SpatialSort::~SpatialSort() {
// empty
}
// ------------------------------------------------------------------------------------------------
void SpatialSort::Fill( const aiVector3D* pPositions, unsigned int pNumPositions,
unsigned int pElementOffset,
bool pFinalize /*= true */)
{
void SpatialSort::Fill(const aiVector3D *pPositions, unsigned int pNumPositions,
unsigned int pElementOffset,
bool pFinalize /*= true */) {
mPositions.clear();
Append(pPositions,pNumPositions,pElementOffset,pFinalize);
mFinalized = false;
Append(pPositions, pNumPositions, pElementOffset, pFinalize);
mFinalized = pFinalize;
}
// ------------------------------------------------------------------------------------------------
void SpatialSort :: Finalize()
{
std::sort( mPositions.begin(), mPositions.end());
ai_real SpatialSort::CalculateDistance(const aiVector3D &pPosition) const {
return (pPosition - mCentroid) * mPlaneNormal;
}
// ------------------------------------------------------------------------------------------------
void SpatialSort::Append( const aiVector3D* pPositions, unsigned int pNumPositions,
unsigned int pElementOffset,
bool pFinalize /*= true */)
{
void SpatialSort::Finalize() {
const ai_real scale = 1.0f / mPositions.size();
for (unsigned int i = 0; i < mPositions.size(); i++) {
mCentroid += scale * mPositions[i].mPosition;
}
for (unsigned int i = 0; i < mPositions.size(); i++) {
mPositions[i].mDistance = CalculateDistance(mPositions[i].mPosition);
}
std::sort(mPositions.begin(), mPositions.end());
mFinalized = true;
}
// ------------------------------------------------------------------------------------------------
void SpatialSort::Append(const aiVector3D *pPositions, unsigned int pNumPositions,
unsigned int pElementOffset,
bool pFinalize /*= true */) {
ai_assert(!mFinalized && "You cannot add positions to the SpatialSort object after it has been finalized.");
// store references to all given positions along with their distance to the reference plane
const size_t initial = mPositions.size();
mPositions.reserve(initial + (pFinalize?pNumPositions:pNumPositions*2));
for( unsigned int a = 0; a < pNumPositions; a++)
{
const char* tempPointer = reinterpret_cast<const char*> (pPositions);
const aiVector3D* vec = reinterpret_cast<const aiVector3D*> (tempPointer + a * pElementOffset);
// store position by index and distance
ai_real distance = *vec * mPlaneNormal;
mPositions.push_back( Entry( static_cast<unsigned int>(a+initial), *vec, distance));
mPositions.reserve(initial + pNumPositions);
for (unsigned int a = 0; a < pNumPositions; a++) {
const char *tempPointer = reinterpret_cast<const char *>(pPositions);
const aiVector3D *vec = reinterpret_cast<const aiVector3D *>(tempPointer + a * pElementOffset);
mPositions.push_back(Entry(static_cast<unsigned int>(a + initial), *vec));
}
if (pFinalize) {
@ -121,29 +127,28 @@ void SpatialSort::Append( const aiVector3D* pPositions, unsigned int pNumPositio
// ------------------------------------------------------------------------------------------------
// Returns an iterator for all positions close to the given position.
void SpatialSort::FindPositions( const aiVector3D& pPosition,
ai_real pRadius, std::vector<unsigned int>& poResults) const
{
const ai_real dist = pPosition * mPlaneNormal;
void SpatialSort::FindPositions(const aiVector3D &pPosition,
ai_real pRadius, std::vector<unsigned int> &poResults) const {
ai_assert(mFinalized && "The SpatialSort object must be finalized before FindPositions can be called.");
const ai_real dist = CalculateDistance(pPosition);
const ai_real minDist = dist - pRadius, maxDist = dist + pRadius;
// clear the array
poResults.clear();
// quick check for positions outside the range
if( mPositions.size() == 0)
if (mPositions.size() == 0)
return;
if( maxDist < mPositions.front().mDistance)
if (maxDist < mPositions.front().mDistance)
return;
if( minDist > mPositions.back().mDistance)
if (minDist > mPositions.back().mDistance)
return;
// do a binary search for the minimal distance to start the iteration there
unsigned int index = (unsigned int)mPositions.size() / 2;
unsigned int binaryStepSize = (unsigned int)mPositions.size() / 4;
while( binaryStepSize > 1)
{
if( mPositions[index].mDistance < minDist)
while (binaryStepSize > 1) {
if (mPositions[index].mDistance < minDist)
index += binaryStepSize;
else
index -= binaryStepSize;
@ -153,21 +158,20 @@ void SpatialSort::FindPositions( const aiVector3D& pPosition,
// depending on the direction of the last step we need to single step a bit back or forth
// to find the actual beginning element of the range
while( index > 0 && mPositions[index].mDistance > minDist)
while (index > 0 && mPositions[index].mDistance > minDist)
index--;
while( index < (mPositions.size() - 1) && mPositions[index].mDistance < minDist)
while (index < (mPositions.size() - 1) && mPositions[index].mDistance < minDist)
index++;
// Mow start iterating from there until the first position lays outside of the distance range.
// Add all positions inside the distance range within the given radius to the result aray
// Add all positions inside the distance range within the given radius to the result array
std::vector<Entry>::const_iterator it = mPositions.begin() + index;
const ai_real pSquared = pRadius*pRadius;
while( it->mDistance < maxDist)
{
if( (it->mPosition - pPosition).SquareLength() < pSquared)
poResults.push_back( it->mIndex);
const ai_real pSquared = pRadius * pRadius;
while (it->mDistance < maxDist) {
if ((it->mPosition - pPosition).SquareLength() < pSquared)
poResults.push_back(it->mIndex);
++it;
if( it == mPositions.end())
if (it == mPositions.end())
break;
}
@ -176,70 +180,72 @@ void SpatialSort::FindPositions( const aiVector3D& pPosition,
namespace {
// Binary, signed-integer representation of a single-precision floating-point value.
// IEEE 754 says: "If two floating-point numbers in the same format are ordered then they are
// ordered the same way when their bits are reinterpreted as sign-magnitude integers."
// This allows us to convert all floating-point numbers to signed integers of arbitrary size
// and then use them to work with ULPs (Units in the Last Place, for high-precision
// computations) or to compare them (integer comparisons are faster than floating-point
// comparisons on many platforms).
typedef ai_int BinFloat;
// Binary, signed-integer representation of a single-precision floating-point value.
// IEEE 754 says: "If two floating-point numbers in the same format are ordered then they are
// ordered the same way when their bits are reinterpreted as sign-magnitude integers."
// This allows us to convert all floating-point numbers to signed integers of arbitrary size
// and then use them to work with ULPs (Units in the Last Place, for high-precision
// computations) or to compare them (integer comparisons are faster than floating-point
// comparisons on many platforms).
typedef ai_int BinFloat;
// --------------------------------------------------------------------------------------------
// Converts the bit pattern of a floating-point number to its signed integer representation.
BinFloat ToBinary( const ai_real & pValue) {
// --------------------------------------------------------------------------------------------
// Converts the bit pattern of a floating-point number to its signed integer representation.
BinFloat ToBinary(const ai_real &pValue) {
// If this assertion fails, signed int is not big enough to store a float on your platform.
// Please correct the declaration of BinFloat a few lines above - but do it in a portable,
// #ifdef'd manner!
static_assert( sizeof(BinFloat) >= sizeof(ai_real), "sizeof(BinFloat) >= sizeof(ai_real)");
// If this assertion fails, signed int is not big enough to store a float on your platform.
// Please correct the declaration of BinFloat a few lines above - but do it in a portable,
// #ifdef'd manner!
static_assert(sizeof(BinFloat) >= sizeof(ai_real), "sizeof(BinFloat) >= sizeof(ai_real)");
#if defined( _MSC_VER)
// If this assertion fails, Visual C++ has finally moved to ILP64. This means that this
// code has just become legacy code! Find out the current value of _MSC_VER and modify
// the #if above so it evaluates false on the current and all upcoming VC versions (or
// on the current platform, if LP64 or LLP64 are still used on other platforms).
static_assert( sizeof(BinFloat) == sizeof(ai_real), "sizeof(BinFloat) == sizeof(ai_real)");
#if defined(_MSC_VER)
// If this assertion fails, Visual C++ has finally moved to ILP64. This means that this
// code has just become legacy code! Find out the current value of _MSC_VER and modify
// the #if above so it evaluates false on the current and all upcoming VC versions (or
// on the current platform, if LP64 or LLP64 are still used on other platforms).
static_assert(sizeof(BinFloat) == sizeof(ai_real), "sizeof(BinFloat) == sizeof(ai_real)");
// This works best on Visual C++, but other compilers have their problems with it.
const BinFloat binValue = reinterpret_cast<BinFloat const &>(pValue);
#else
// On many compilers, reinterpreting a float address as an integer causes aliasing
// problems. This is an ugly but more or less safe way of doing it.
union {
ai_real asFloat;
BinFloat asBin;
} conversion;
conversion.asBin = 0; // zero empty space in case sizeof(BinFloat) > sizeof(float)
conversion.asFloat = pValue;
const BinFloat binValue = conversion.asBin;
#endif
// This works best on Visual C++, but other compilers have their problems with it.
const BinFloat binValue = reinterpret_cast<BinFloat const &>(pValue);
//::memcpy(&binValue, &pValue, sizeof(pValue));
//return binValue;
#else
// On many compilers, reinterpreting a float address as an integer causes aliasing
// problems. This is an ugly but more or less safe way of doing it.
union {
ai_real asFloat;
BinFloat asBin;
} conversion;
conversion.asBin = 0; // zero empty space in case sizeof(BinFloat) > sizeof(float)
conversion.asFloat = pValue;
const BinFloat binValue = conversion.asBin;
#endif
// floating-point numbers are of sign-magnitude format, so find out what signed number
// representation we must convert negative values to.
// See http://en.wikipedia.org/wiki/Signed_number_representations.
// floating-point numbers are of sign-magnitude format, so find out what signed number
// representation we must convert negative values to.
// See http://en.wikipedia.org/wiki/Signed_number_representations.
const BinFloat mask = BinFloat(1) << (CHAR_BIT * sizeof(BinFloat) - 1);
// Two's complement?
if( (-42 == (~42 + 1)) && (binValue & 0x80000000))
return BinFloat(1 << (CHAR_BIT * sizeof(BinFloat) - 1)) - binValue;
// One's complement?
else if ( (-42 == ~42) && (binValue & 0x80000000))
return BinFloat(-0) - binValue;
// Sign-magnitude?
else if( (-42 == (42 | (-0))) && (binValue & 0x80000000)) // -0 = 1000... binary
return binValue;
else
return binValue;
}
// Two's complement?
const bool DefaultValue = ((-42 == (~42 + 1)) && (binValue & mask));
const bool OneComplement = ((-42 == ~42) && (binValue & mask));
if (DefaultValue)
return mask - binValue;
// One's complement?
else if (OneComplement)
return BinFloat(-0) - binValue;
// Sign-magnitude? -0 = 1000... binary
return binValue;
}
} // namespace
// ------------------------------------------------------------------------------------------------
// Fills an array with indices of all positions identical to the given position. In opposite to
// FindPositions(), not an epsilon is used but a (very low) tolerance of four floating-point units.
void SpatialSort::FindIdenticalPositions( const aiVector3D& pPosition,
std::vector<unsigned int>& poResults) const
{
void SpatialSort::FindIdenticalPositions(const aiVector3D &pPosition, std::vector<unsigned int> &poResults) const {
ai_assert(mFinalized && "The SpatialSort object must be finalized before FindIdenticalPositions can be called.");
// Epsilons have a huge disadvantage: they are of constant precision, while floating-point
// values are of log2 precision. If you apply e=0.01 to 100, the epsilon is rather small, but
// if you apply it to 0.001, it is enormous.
@ -265,20 +271,19 @@ void SpatialSort::FindIdenticalPositions( const aiVector3D& pPosition,
// Convert the plane distance to its signed integer representation so the ULPs tolerance can be
// applied. For some reason, VC won't optimize two calls of the bit pattern conversion.
const BinFloat minDistBinary = ToBinary( pPosition * mPlaneNormal) - distanceToleranceInULPs;
const BinFloat minDistBinary = ToBinary(CalculateDistance(pPosition)) - distanceToleranceInULPs;
const BinFloat maxDistBinary = minDistBinary + 2 * distanceToleranceInULPs;
// clear the array in this strange fashion because a simple clear() would also deallocate
// the array which we want to avoid
poResults.resize( 0 );
poResults.resize(0);
// do a binary search for the minimal distance to start the iteration there
unsigned int index = (unsigned int)mPositions.size() / 2;
unsigned int binaryStepSize = (unsigned int)mPositions.size() / 4;
while( binaryStepSize > 1)
{
while (binaryStepSize > 1) {
// Ugly, but conditional jumps are faster with integers than with floats
if( minDistBinary > ToBinary(mPositions[index].mDistance))
if (minDistBinary > ToBinary(mPositions[index].mDistance))
index += binaryStepSize;
else
index -= binaryStepSize;
@ -288,20 +293,19 @@ void SpatialSort::FindIdenticalPositions( const aiVector3D& pPosition,
// depending on the direction of the last step we need to single step a bit back or forth
// to find the actual beginning element of the range
while( index > 0 && minDistBinary < ToBinary(mPositions[index].mDistance) )
while (index > 0 && minDistBinary < ToBinary(mPositions[index].mDistance))
index--;
while( index < (mPositions.size() - 1) && minDistBinary > ToBinary(mPositions[index].mDistance))
while (index < (mPositions.size() - 1) && minDistBinary > ToBinary(mPositions[index].mDistance))
index++;
// Now start iterating from there until the first position lays outside of the distance range.
// Add all positions inside the distance range within the tolerance to the result array
std::vector<Entry>::const_iterator it = mPositions.begin() + index;
while( ToBinary(it->mDistance) < maxDistBinary)
{
if( distance3DToleranceInULPs >= ToBinary((it->mPosition - pPosition).SquareLength()))
while (ToBinary(it->mDistance) < maxDistBinary) {
if (distance3DToleranceInULPs >= ToBinary((it->mPosition - pPosition).SquareLength()))
poResults.push_back(it->mIndex);
++it;
if( it == mPositions.end())
if (it == mPositions.end())
break;
}
@ -309,22 +313,20 @@ void SpatialSort::FindIdenticalPositions( const aiVector3D& pPosition,
}
// ------------------------------------------------------------------------------------------------
unsigned int SpatialSort::GenerateMappingTable(std::vector<unsigned int>& fill, ai_real pRadius) const
{
fill.resize(mPositions.size(),UINT_MAX);
unsigned int SpatialSort::GenerateMappingTable(std::vector<unsigned int> &fill, ai_real pRadius) const {
ai_assert(mFinalized && "The SpatialSort object must be finalized before GenerateMappingTable can be called.");
fill.resize(mPositions.size(), UINT_MAX);
ai_real dist, maxDist;
unsigned int t=0;
const ai_real pSquared = pRadius*pRadius;
unsigned int t = 0;
const ai_real pSquared = pRadius * pRadius;
for (size_t i = 0; i < mPositions.size();) {
dist = mPositions[i].mPosition * mPlaneNormal;
dist = (mPositions[i].mPosition - mCentroid) * mPlaneNormal;
maxDist = dist + pRadius;
fill[mPositions[i].mIndex] = t;
const aiVector3D& oldpos = mPositions[i].mPosition;
for (++i; i < fill.size() && mPositions[i].mDistance < maxDist
&& (mPositions[i].mPosition - oldpos).SquareLength() < pSquared; ++i)
{
const aiVector3D &oldpos = mPositions[i].mPosition;
for (++i; i < fill.size() && mPositions[i].mDistance < maxDist && (mPositions[i].mPosition - oldpos).SquareLength() < pSquared; ++i) {
fill[mPositions[i].mIndex] = t;
}
++t;
@ -334,7 +336,7 @@ unsigned int SpatialSort::GenerateMappingTable(std::vector<unsigned int>& fill,
// debug invariant: mPositions[i].mIndex values must range from 0 to mPositions.size()-1
for (size_t i = 0; i < fill.size(); ++i) {
ai_assert(fill[i]<mPositions.size());
ai_assert(fill[i] < mPositions.size());
}
#endif

View file

@ -1,407 +0,0 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/// @file SplitByBoneCountProcess.cpp
/// Implementation of the SplitByBoneCount postprocessing step
// internal headers of the post-processing framework
#include "SplitByBoneCountProcess.h"
#include <assimp/postprocess.h>
#include <assimp/DefaultLogger.hpp>
#include <limits>
#include <assimp/TinyFormatter.h>
using namespace Assimp;
using namespace Assimp::Formatter;
// ------------------------------------------------------------------------------------------------
// Constructor
SplitByBoneCountProcess::SplitByBoneCountProcess()
{
// set default, might be overridden by importer config
mMaxBoneCount = AI_SBBC_DEFAULT_MAX_BONES;
}
// ------------------------------------------------------------------------------------------------
// Destructor
SplitByBoneCountProcess::~SplitByBoneCountProcess()
{
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag.
bool SplitByBoneCountProcess::IsActive( unsigned int pFlags) const
{
return !!(pFlags & aiProcess_SplitByBoneCount);
}
// ------------------------------------------------------------------------------------------------
// Updates internal properties
void SplitByBoneCountProcess::SetupProperties(const Importer* pImp)
{
mMaxBoneCount = pImp->GetPropertyInteger(AI_CONFIG_PP_SBBC_MAX_BONES,AI_SBBC_DEFAULT_MAX_BONES);
}
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void SplitByBoneCountProcess::Execute( aiScene* pScene)
{
ASSIMP_LOG_DEBUG("SplitByBoneCountProcess begin");
// early out
bool isNecessary = false;
for( unsigned int a = 0; a < pScene->mNumMeshes; ++a)
if( pScene->mMeshes[a]->mNumBones > mMaxBoneCount )
isNecessary = true;
if( !isNecessary )
{
ASSIMP_LOG_DEBUG( format() << "SplitByBoneCountProcess early-out: no meshes with more than " << mMaxBoneCount << " bones." );
return;
}
// we need to do something. Let's go.
mSubMeshIndices.clear();
mSubMeshIndices.resize( pScene->mNumMeshes);
// build a new array of meshes for the scene
std::vector<aiMesh*> meshes;
for( unsigned int a = 0; a < pScene->mNumMeshes; ++a)
{
aiMesh* srcMesh = pScene->mMeshes[a];
std::vector<aiMesh*> newMeshes;
SplitMesh( pScene->mMeshes[a], newMeshes);
// mesh was split
if( !newMeshes.empty() )
{
// store new meshes and indices of the new meshes
for( unsigned int b = 0; b < newMeshes.size(); ++b)
{
mSubMeshIndices[a].push_back( static_cast<unsigned int>(meshes.size()));
meshes.push_back( newMeshes[b]);
}
// and destroy the source mesh. It should be completely contained inside the new submeshes
delete srcMesh;
}
else
{
// Mesh is kept unchanged - store it's new place in the mesh array
mSubMeshIndices[a].push_back( static_cast<unsigned int>(meshes.size()));
meshes.push_back( srcMesh);
}
}
// rebuild the scene's mesh array
pScene->mNumMeshes = static_cast<unsigned int>(meshes.size());
delete [] pScene->mMeshes;
pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
std::copy( meshes.begin(), meshes.end(), pScene->mMeshes);
// recurse through all nodes and translate the node's mesh indices to fit the new mesh array
UpdateNode( pScene->mRootNode);
ASSIMP_LOG_DEBUG( format() << "SplitByBoneCountProcess end: split " << mSubMeshIndices.size() << " meshes into " << meshes.size() << " submeshes." );
}
// ------------------------------------------------------------------------------------------------
// Splits the given mesh by bone count.
void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vector<aiMesh*>& poNewMeshes) const
{
// skip if not necessary
if( pMesh->mNumBones <= mMaxBoneCount )
return;
// necessary optimisation: build a list of all affecting bones for each vertex
// TODO: (thom) maybe add a custom allocator here to avoid allocating tens of thousands of small arrays
typedef std::pair<unsigned int, float> BoneWeight;
std::vector< std::vector<BoneWeight> > vertexBones( pMesh->mNumVertices);
for( unsigned int a = 0; a < pMesh->mNumBones; ++a)
{
const aiBone* bone = pMesh->mBones[a];
for( unsigned int b = 0; b < bone->mNumWeights; ++b)
vertexBones[ bone->mWeights[b].mVertexId ].push_back( BoneWeight( a, bone->mWeights[b].mWeight));
}
unsigned int numFacesHandled = 0;
std::vector<bool> isFaceHandled( pMesh->mNumFaces, false);
while( numFacesHandled < pMesh->mNumFaces )
{
// which bones are used in the current submesh
unsigned int numBones = 0;
std::vector<bool> isBoneUsed( pMesh->mNumBones, false);
// indices of the faces which are going to go into this submesh
std::vector<unsigned int> subMeshFaces;
subMeshFaces.reserve( pMesh->mNumFaces);
// accumulated vertex count of all the faces in this submesh
unsigned int numSubMeshVertices = 0;
// a small local array of new bones for the current face. State of all used bones for that face
// can only be updated AFTER the face is completely analysed. Thanks to imre for the fix.
std::vector<unsigned int> newBonesAtCurrentFace;
// add faces to the new submesh as long as all bones affecting the faces' vertices fit in the limit
for( unsigned int a = 0; a < pMesh->mNumFaces; ++a)
{
// skip if the face is already stored in a submesh
if( isFaceHandled[a] )
continue;
const aiFace& face = pMesh->mFaces[a];
// check every vertex if its bones would still fit into the current submesh
for( unsigned int b = 0; b < face.mNumIndices; ++b )
{
const std::vector<BoneWeight>& vb = vertexBones[face.mIndices[b]];
for( unsigned int c = 0; c < vb.size(); ++c)
{
unsigned int boneIndex = vb[c].first;
// if the bone is already used in this submesh, it's ok
if( isBoneUsed[boneIndex] )
continue;
// if it's not used, yet, we would need to add it. Store its bone index
if( std::find( newBonesAtCurrentFace.begin(), newBonesAtCurrentFace.end(), boneIndex) == newBonesAtCurrentFace.end() )
newBonesAtCurrentFace.push_back( boneIndex);
}
}
// leave out the face if the new bones required for this face don't fit the bone count limit anymore
if( numBones + newBonesAtCurrentFace.size() > mMaxBoneCount )
continue;
// mark all new bones as necessary
while( !newBonesAtCurrentFace.empty() )
{
unsigned int newIndex = newBonesAtCurrentFace.back();
newBonesAtCurrentFace.pop_back(); // this also avoids the deallocation which comes with a clear()
if( isBoneUsed[newIndex] )
continue;
isBoneUsed[newIndex] = true;
numBones++;
}
// store the face index and the vertex count
subMeshFaces.push_back( a);
numSubMeshVertices += face.mNumIndices;
// remember that this face is handled
isFaceHandled[a] = true;
numFacesHandled++;
}
// create a new mesh to hold this subset of the source mesh
aiMesh* newMesh = new aiMesh;
if( pMesh->mName.length > 0 )
newMesh->mName.Set( format() << pMesh->mName.data << "_sub" << poNewMeshes.size());
newMesh->mMaterialIndex = pMesh->mMaterialIndex;
newMesh->mPrimitiveTypes = pMesh->mPrimitiveTypes;
poNewMeshes.push_back( newMesh);
// create all the arrays for this mesh if the old mesh contained them
newMesh->mNumVertices = numSubMeshVertices;
newMesh->mNumFaces = static_cast<unsigned int>(subMeshFaces.size());
newMesh->mVertices = new aiVector3D[newMesh->mNumVertices];
if( pMesh->HasNormals() )
newMesh->mNormals = new aiVector3D[newMesh->mNumVertices];
if( pMesh->HasTangentsAndBitangents() )
{
newMesh->mTangents = new aiVector3D[newMesh->mNumVertices];
newMesh->mBitangents = new aiVector3D[newMesh->mNumVertices];
}
for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a )
{
if( pMesh->HasTextureCoords( a) )
newMesh->mTextureCoords[a] = new aiVector3D[newMesh->mNumVertices];
newMesh->mNumUVComponents[a] = pMesh->mNumUVComponents[a];
}
for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a )
{
if( pMesh->HasVertexColors( a) )
newMesh->mColors[a] = new aiColor4D[newMesh->mNumVertices];
}
// and copy over the data, generating faces with linear indices along the way
newMesh->mFaces = new aiFace[subMeshFaces.size()];
unsigned int nvi = 0; // next vertex index
std::vector<unsigned int> previousVertexIndices( numSubMeshVertices, std::numeric_limits<unsigned int>::max()); // per new vertex: its index in the source mesh
for( unsigned int a = 0; a < subMeshFaces.size(); ++a )
{
const aiFace& srcFace = pMesh->mFaces[subMeshFaces[a]];
aiFace& dstFace = newMesh->mFaces[a];
dstFace.mNumIndices = srcFace.mNumIndices;
dstFace.mIndices = new unsigned int[dstFace.mNumIndices];
// accumulate linearly all the vertices of the source face
for( unsigned int b = 0; b < dstFace.mNumIndices; ++b )
{
unsigned int srcIndex = srcFace.mIndices[b];
dstFace.mIndices[b] = nvi;
previousVertexIndices[nvi] = srcIndex;
newMesh->mVertices[nvi] = pMesh->mVertices[srcIndex];
if( pMesh->HasNormals() )
newMesh->mNormals[nvi] = pMesh->mNormals[srcIndex];
if( pMesh->HasTangentsAndBitangents() )
{
newMesh->mTangents[nvi] = pMesh->mTangents[srcIndex];
newMesh->mBitangents[nvi] = pMesh->mBitangents[srcIndex];
}
for( unsigned int c = 0; c < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++c )
{
if( pMesh->HasTextureCoords( c) )
newMesh->mTextureCoords[c][nvi] = pMesh->mTextureCoords[c][srcIndex];
}
for( unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS; ++c )
{
if( pMesh->HasVertexColors( c) )
newMesh->mColors[c][nvi] = pMesh->mColors[c][srcIndex];
}
nvi++;
}
}
ai_assert( nvi == numSubMeshVertices );
// Create the bones for the new submesh: first create the bone array
newMesh->mNumBones = 0;
newMesh->mBones = new aiBone*[numBones];
std::vector<unsigned int> mappedBoneIndex( pMesh->mNumBones, std::numeric_limits<unsigned int>::max());
for( unsigned int a = 0; a < pMesh->mNumBones; ++a )
{
if( !isBoneUsed[a] )
continue;
// create the new bone
const aiBone* srcBone = pMesh->mBones[a];
aiBone* dstBone = new aiBone;
mappedBoneIndex[a] = newMesh->mNumBones;
newMesh->mBones[newMesh->mNumBones++] = dstBone;
dstBone->mName = srcBone->mName;
dstBone->mOffsetMatrix = srcBone->mOffsetMatrix;
dstBone->mNumWeights = 0;
}
ai_assert( newMesh->mNumBones == numBones );
// iterate over all new vertices and count which bones affected its old vertex in the source mesh
for( unsigned int a = 0; a < numSubMeshVertices; ++a )
{
unsigned int oldIndex = previousVertexIndices[a];
const std::vector<BoneWeight>& bonesOnThisVertex = vertexBones[oldIndex];
for( unsigned int b = 0; b < bonesOnThisVertex.size(); ++b )
{
unsigned int newBoneIndex = mappedBoneIndex[ bonesOnThisVertex[b].first ];
if( newBoneIndex != std::numeric_limits<unsigned int>::max() )
newMesh->mBones[newBoneIndex]->mNumWeights++;
}
}
// allocate all bone weight arrays accordingly
for( unsigned int a = 0; a < newMesh->mNumBones; ++a )
{
aiBone* bone = newMesh->mBones[a];
ai_assert( bone->mNumWeights > 0 );
bone->mWeights = new aiVertexWeight[bone->mNumWeights];
bone->mNumWeights = 0; // for counting up in the next step
}
// now copy all the bone vertex weights for all the vertices which made it into the new submesh
for( unsigned int a = 0; a < numSubMeshVertices; ++a)
{
// find the source vertex for it in the source mesh
unsigned int previousIndex = previousVertexIndices[a];
// these bones were affecting it
const std::vector<BoneWeight>& bonesOnThisVertex = vertexBones[previousIndex];
// all of the bones affecting it should be present in the new submesh, or else
// the face it comprises shouldn't be present
for( unsigned int b = 0; b < bonesOnThisVertex.size(); ++b)
{
unsigned int newBoneIndex = mappedBoneIndex[ bonesOnThisVertex[b].first ];
ai_assert( newBoneIndex != std::numeric_limits<unsigned int>::max() );
aiVertexWeight* dstWeight = newMesh->mBones[newBoneIndex]->mWeights + newMesh->mBones[newBoneIndex]->mNumWeights;
newMesh->mBones[newBoneIndex]->mNumWeights++;
dstWeight->mVertexId = a;
dstWeight->mWeight = bonesOnThisVertex[b].second;
}
}
// I have the strange feeling that this will break apart at some point in time...
}
}
// ------------------------------------------------------------------------------------------------
// Recursively updates the node's mesh list to account for the changed mesh list
void SplitByBoneCountProcess::UpdateNode( aiNode* pNode) const
{
// rebuild the node's mesh index list
if( pNode->mNumMeshes > 0 )
{
std::vector<unsigned int> newMeshList;
for( unsigned int a = 0; a < pNode->mNumMeshes; ++a)
{
unsigned int srcIndex = pNode->mMeshes[a];
const std::vector<unsigned int>& replaceMeshes = mSubMeshIndices[srcIndex];
newMeshList.insert( newMeshList.end(), replaceMeshes.begin(), replaceMeshes.end());
}
delete [] pNode->mMeshes;
pNode->mNumMeshes = static_cast<unsigned int>(newMeshList.size());
pNode->mMeshes = new unsigned int[pNode->mNumMeshes];
std::copy( newMeshList.begin(), newMeshList.end(), pNode->mMeshes);
}
// do that also recursively for all children
for( unsigned int a = 0; a < pNode->mNumChildren; ++a )
{
UpdateNode( pNode->mChildren[a]);
}
}

View file

@ -1,111 +0,0 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/// @file SplitByBoneCountProcess.h
/// Defines a post processing step to split meshes with many bones into submeshes
#ifndef AI_SPLITBYBONECOUNTPROCESS_H_INC
#define AI_SPLITBYBONECOUNTPROCESS_H_INC
#include <vector>
#include "BaseProcess.h"
#include <assimp/mesh.h>
#include <assimp/scene.h>
namespace Assimp
{
/** Postprocessing filter to split meshes with many bones into submeshes
* so that each submesh has a certain max bone count.
*
* Applied BEFORE the JoinVertices-Step occurs.
* Returns NON-UNIQUE vertices, splits by bone count.
*/
class SplitByBoneCountProcess : public BaseProcess
{
public:
SplitByBoneCountProcess();
~SplitByBoneCountProcess();
public:
/** Returns whether the processing step is present in the given flag.
* @param pFlags The processing flags the importer was called with. A
* bitwise combination of #aiPostProcessSteps.
* @return true if the process is present in this flag fields,
* false if not.
*/
bool IsActive( unsigned int pFlags) const;
/** Called prior to ExecuteOnScene().
* The function is a request to the process to update its configuration
* basing on the Importer's configuration property list.
*/
virtual void SetupProperties(const Importer* pImp);
protected:
/** Executes the post processing step on the given imported data.
* At the moment a process is not supposed to fail.
* @param pScene The imported data to work at.
*/
void Execute( aiScene* pScene);
/// Splits the given mesh by bone count.
/// @param pMesh the Mesh to split. Is not changed at all, but might be superfluous in case it was split.
/// @param poNewMeshes Array of submeshes created in the process. Empty if splitting was not necessary.
void SplitMesh( const aiMesh* pMesh, std::vector<aiMesh*>& poNewMeshes) const;
/// Recursively updates the node's mesh list to account for the changed mesh list
void UpdateNode( aiNode* pNode) const;
public:
/// Max bone count. Splitting occurs if a mesh has more than that number of bones.
size_t mMaxBoneCount;
/// Per mesh index: Array of indices of the new submeshes.
std::vector< std::vector<unsigned int> > mSubMeshIndices;
};
} // end of namespace Assimp
#endif // !!AI_SPLITBYBONECOUNTPROCESS_H_INC

View file

@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -49,69 +48,59 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/StandardShapes.h>
#include <assimp/StringComparison.h>
#include <stddef.h>
#include <assimp/Defines.h>
#include <assimp/mesh.h>
namespace Assimp {
namespace Assimp {
# define ADD_TRIANGLE(n0,n1,n2) \
positions.push_back(n0); \
positions.push_back(n1); \
#define ADD_TRIANGLE(n0, n1, n2) \
positions.push_back(n0); \
positions.push_back(n1); \
positions.push_back(n2);
# define ADD_PENTAGON(n0,n1,n2,n3,n4) \
if (polygons) \
{ \
#define ADD_PENTAGON(n0, n1, n2, n3, n4) \
if (polygons) { \
positions.push_back(n0); \
positions.push_back(n1); \
positions.push_back(n2); \
positions.push_back(n3); \
positions.push_back(n4); \
} else { \
ADD_TRIANGLE(n0, n1, n2) \
ADD_TRIANGLE(n0, n2, n3) \
ADD_TRIANGLE(n0, n3, n4) \
}
#define ADD_QUAD(n0, n1, n2, n3) \
if (polygons) { \
positions.push_back(n0); \
positions.push_back(n1); \
positions.push_back(n2); \
positions.push_back(n3); \
positions.push_back(n4); \
} \
else \
{ \
ADD_TRIANGLE(n0, n1, n2) \
ADD_TRIANGLE(n0, n2, n3) \
ADD_TRIANGLE(n0, n3, n4) \
}
# define ADD_QUAD(n0,n1,n2,n3) \
if (polygons) \
{ \
positions.push_back(n0); \
positions.push_back(n1); \
positions.push_back(n2); \
positions.push_back(n3); \
} \
else \
{ \
} else { \
ADD_TRIANGLE(n0, n1, n2) \
ADD_TRIANGLE(n0, n2, n3) \
}
// ------------------------------------------------------------------------------------------------
// Fast subdivision for a mesh whose verts have a magnitude of 1
void Subdivide(std::vector<aiVector3D>& positions)
{
void Subdivide(std::vector<aiVector3D> &positions) {
// assume this to be constant - (fixme: must be 1.0? I think so)
const ai_real fl1 = positions[0].Length();
unsigned int origSize = (unsigned int)positions.size();
for (unsigned int i = 0 ; i < origSize ; i+=3)
{
aiVector3D& tv0 = positions[i];
aiVector3D& tv1 = positions[i+1];
aiVector3D& tv2 = positions[i+2];
for (unsigned int i = 0; i < origSize; i += 3) {
aiVector3D &tv0 = positions[i];
aiVector3D &tv1 = positions[i + 1];
aiVector3D &tv2 = positions[i + 2];
aiVector3D a = tv0, b = tv1, c = tv2;
aiVector3D v1 = aiVector3D(a.x+b.x, a.y+b.y, a.z+b.z).Normalize()*fl1;
aiVector3D v2 = aiVector3D(a.x+c.x, a.y+c.y, a.z+c.z).Normalize()*fl1;
aiVector3D v3 = aiVector3D(b.x+c.x, b.y+c.y, b.z+c.z).Normalize()*fl1;
aiVector3D v1 = aiVector3D(a.x + b.x, a.y + b.y, a.z + b.z).Normalize() * fl1;
aiVector3D v2 = aiVector3D(a.x + c.x, a.y + c.y, a.z + c.z).Normalize() * fl1;
aiVector3D v3 = aiVector3D(b.x + c.x, b.y + c.y, b.z + c.z).Normalize() * fl1;
tv0 = v1; tv1 = v3; tv2 = v2; // overwrite the original
tv0 = v1;
tv1 = v3;
tv2 = v2; // overwrite the original
ADD_TRIANGLE(v1, v2, a);
ADD_TRIANGLE(v2, v3, c);
ADD_TRIANGLE(v3, v1, b);
@ -120,147 +109,143 @@ void Subdivide(std::vector<aiVector3D>& positions)
// ------------------------------------------------------------------------------------------------
// Construct a mesh from given vertex positions
aiMesh* StandardShapes::MakeMesh(const std::vector<aiVector3D>& positions,
unsigned int numIndices)
{
if (positions.empty() || !numIndices) return NULL;
aiMesh *StandardShapes::MakeMesh(const std::vector<aiVector3D> &positions,
unsigned int numIndices) {
if (positions.empty() || !numIndices) {
return nullptr;
}
// Determine which kinds of primitives the mesh consists of
aiMesh* out = new aiMesh();
aiMesh *out = new aiMesh();
switch (numIndices) {
case 1:
out->mPrimitiveTypes = aiPrimitiveType_POINT;
break;
case 2:
out->mPrimitiveTypes = aiPrimitiveType_LINE;
break;
case 3:
out->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
break;
default:
out->mPrimitiveTypes = aiPrimitiveType_POLYGON;
break;
case 1:
out->mPrimitiveTypes = aiPrimitiveType_POINT;
break;
case 2:
out->mPrimitiveTypes = aiPrimitiveType_LINE;
break;
case 3:
out->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
break;
default:
out->mPrimitiveTypes = aiPrimitiveType_POLYGON;
break;
};
out->mNumFaces = (unsigned int)positions.size() / numIndices;
out->mFaces = new aiFace[out->mNumFaces];
for (unsigned int i = 0, a = 0; i < out->mNumFaces;++i) {
aiFace& f = out->mFaces[i];
for (unsigned int i = 0, a = 0; i < out->mNumFaces; ++i) {
aiFace &f = out->mFaces[i];
f.mNumIndices = numIndices;
f.mIndices = new unsigned int[numIndices];
for (unsigned int j = 0; i < numIndices; ++i, ++a) {
for (unsigned int j = 0; j < numIndices; ++j, ++a) {
f.mIndices[j] = a;
}
}
out->mNumVertices = (unsigned int)positions.size();
out->mVertices = new aiVector3D[out->mNumVertices];
::memcpy(out->mVertices,&positions[0],out->mNumVertices*sizeof(aiVector3D));
::memcpy(out->mVertices, &positions[0], out->mNumVertices * sizeof(aiVector3D));
return out;
}
// ------------------------------------------------------------------------------------------------
// Construct a mesh with a specific shape (callback)
aiMesh* StandardShapes::MakeMesh ( unsigned int (*GenerateFunc)(
std::vector<aiVector3D>&))
{
aiMesh *StandardShapes::MakeMesh(unsigned int (*GenerateFunc)(
std::vector<aiVector3D> &)) {
std::vector<aiVector3D> temp;
unsigned num = (*GenerateFunc)(temp);
return MakeMesh(temp,num);
return MakeMesh(temp, num);
}
// ------------------------------------------------------------------------------------------------
// Construct a mesh with a specific shape (callback)
aiMesh* StandardShapes::MakeMesh ( unsigned int (*GenerateFunc)(
std::vector<aiVector3D>&, bool))
{
aiMesh *StandardShapes::MakeMesh(unsigned int (*GenerateFunc)(
std::vector<aiVector3D> &, bool)) {
std::vector<aiVector3D> temp;
unsigned num = (*GenerateFunc)(temp,true);
return MakeMesh(temp,num);
unsigned num = (*GenerateFunc)(temp, true);
return MakeMesh(temp, num);
}
// ------------------------------------------------------------------------------------------------
// Construct a mesh with a specific shape (callback)
aiMesh* StandardShapes::MakeMesh (unsigned int num, void (*GenerateFunc)(
unsigned int,std::vector<aiVector3D>&))
{
aiMesh *StandardShapes::MakeMesh(unsigned int num, void (*GenerateFunc)(
unsigned int, std::vector<aiVector3D> &)) {
std::vector<aiVector3D> temp;
(*GenerateFunc)(num,temp);
return MakeMesh(temp,3);
(*GenerateFunc)(num, temp);
return MakeMesh(temp, 3);
}
// ------------------------------------------------------------------------------------------------
// Build an incosahedron with points.magnitude == 1
unsigned int StandardShapes::MakeIcosahedron(std::vector<aiVector3D>& positions)
{
positions.reserve(positions.size()+60);
unsigned int StandardShapes::MakeIcosahedron(std::vector<aiVector3D> &positions) {
positions.reserve(positions.size() + 60);
const ai_real t = ( ai_real( 1.0 )+ ai_real( 2.236067977 ) ) / ai_real( 2.0 );
const ai_real s = std::sqrt(ai_real(1.0) + t*t);
const ai_real t = (ai_real(1.0) + ai_real(2.236067977)) / ai_real(2.0);
const ai_real s = std::sqrt(ai_real(1.0) + t * t);
const aiVector3D v0 = aiVector3D(t,1.0, 0.0)/s;
const aiVector3D v1 = aiVector3D(-t,1.0, 0.0)/s;
const aiVector3D v2 = aiVector3D(t,-1.0, 0.0)/s;
const aiVector3D v3 = aiVector3D(-t,-1.0, 0.0)/s;
const aiVector3D v4 = aiVector3D(1.0, 0.0, t)/s;
const aiVector3D v5 = aiVector3D(1.0, 0.0,-t)/s;
const aiVector3D v6 = aiVector3D(-1.0, 0.0,t)/s;
const aiVector3D v7 = aiVector3D(-1.0, 0.0,-t)/s;
const aiVector3D v8 = aiVector3D(0.0, t, 1.0)/s;
const aiVector3D v9 = aiVector3D(0.0,-t, 1.0)/s;
const aiVector3D v10 = aiVector3D(0.0, t,-1.0)/s;
const aiVector3D v11 = aiVector3D(0.0,-t,-1.0)/s;
const aiVector3D v0 = aiVector3D(t, 1.0, 0.0) / s;
const aiVector3D v1 = aiVector3D(-t, 1.0, 0.0) / s;
const aiVector3D v2 = aiVector3D(t, -1.0, 0.0) / s;
const aiVector3D v3 = aiVector3D(-t, -1.0, 0.0) / s;
const aiVector3D v4 = aiVector3D(1.0, 0.0, t) / s;
const aiVector3D v5 = aiVector3D(1.0, 0.0, -t) / s;
const aiVector3D v6 = aiVector3D(-1.0, 0.0, t) / s;
const aiVector3D v7 = aiVector3D(-1.0, 0.0, -t) / s;
const aiVector3D v8 = aiVector3D(0.0, t, 1.0) / s;
const aiVector3D v9 = aiVector3D(0.0, -t, 1.0) / s;
const aiVector3D v10 = aiVector3D(0.0, t, -1.0) / s;
const aiVector3D v11 = aiVector3D(0.0, -t, -1.0) / s;
ADD_TRIANGLE(v0,v8,v4);
ADD_TRIANGLE(v0,v5,v10);
ADD_TRIANGLE(v2,v4,v9);
ADD_TRIANGLE(v2,v11,v5);
ADD_TRIANGLE(v0, v8, v4);
ADD_TRIANGLE(v0, v5, v10);
ADD_TRIANGLE(v2, v4, v9);
ADD_TRIANGLE(v2, v11, v5);
ADD_TRIANGLE(v1,v6,v8);
ADD_TRIANGLE(v1,v10,v7);
ADD_TRIANGLE(v3,v9,v6);
ADD_TRIANGLE(v3,v7,v11);
ADD_TRIANGLE(v1, v6, v8);
ADD_TRIANGLE(v1, v10, v7);
ADD_TRIANGLE(v3, v9, v6);
ADD_TRIANGLE(v3, v7, v11);
ADD_TRIANGLE(v0,v10,v8);
ADD_TRIANGLE(v1,v8,v10);
ADD_TRIANGLE(v2,v9,v11);
ADD_TRIANGLE(v3,v11,v9);
ADD_TRIANGLE(v0, v10, v8);
ADD_TRIANGLE(v1, v8, v10);
ADD_TRIANGLE(v2, v9, v11);
ADD_TRIANGLE(v3, v11, v9);
ADD_TRIANGLE(v4,v2,v0);
ADD_TRIANGLE(v5,v0,v2);
ADD_TRIANGLE(v6,v1,v3);
ADD_TRIANGLE(v7,v3,v1);
ADD_TRIANGLE(v4, v2, v0);
ADD_TRIANGLE(v5, v0, v2);
ADD_TRIANGLE(v6, v1, v3);
ADD_TRIANGLE(v7, v3, v1);
ADD_TRIANGLE(v8,v6,v4);
ADD_TRIANGLE(v9,v4,v6);
ADD_TRIANGLE(v10,v5,v7);
ADD_TRIANGLE(v11,v7,v5);
ADD_TRIANGLE(v8, v6, v4);
ADD_TRIANGLE(v9, v4, v6);
ADD_TRIANGLE(v10, v5, v7);
ADD_TRIANGLE(v11, v7, v5);
return 3;
}
// ------------------------------------------------------------------------------------------------
// Build a dodecahedron with points.magnitude == 1
unsigned int StandardShapes::MakeDodecahedron(std::vector<aiVector3D>& positions,
bool polygons /*= false*/)
{
positions.reserve(positions.size()+108);
unsigned int StandardShapes::MakeDodecahedron(std::vector<aiVector3D> &positions,
bool polygons /*= false*/) {
positions.reserve(positions.size() + 108);
const ai_real a = ai_real( 1.0 ) / ai_real(1.7320508);
const ai_real b = std::sqrt(( ai_real( 3.0 )- ai_real( 2.23606797))/ ai_real( 6.0) );
const ai_real c = std::sqrt(( ai_real( 3.0 )+ ai_real( 2.23606797f))/ ai_real( 6.0) );
const ai_real a = ai_real(1.0) / ai_real(1.7320508);
const ai_real b = std::sqrt((ai_real(3.0) - ai_real(2.23606797)) / ai_real(6.0));
const ai_real c = std::sqrt((ai_real(3.0) + ai_real(2.23606797f)) / ai_real(6.0));
const aiVector3D v0 = aiVector3D(a,a,a);
const aiVector3D v1 = aiVector3D(a,a,-a);
const aiVector3D v2 = aiVector3D(a,-a,a);
const aiVector3D v3 = aiVector3D(a,-a,-a);
const aiVector3D v4 = aiVector3D(-a,a,a);
const aiVector3D v5 = aiVector3D(-a,a,-a);
const aiVector3D v6 = aiVector3D(-a,-a,a);
const aiVector3D v7 = aiVector3D(-a,-a,-a);
const aiVector3D v8 = aiVector3D(b,c,0.0);
const aiVector3D v9 = aiVector3D(-b,c,0.0);
const aiVector3D v10 = aiVector3D(b,-c,0.0);
const aiVector3D v11 = aiVector3D(-b,-c,0.0);
const aiVector3D v0 = aiVector3D(a, a, a);
const aiVector3D v1 = aiVector3D(a, a, -a);
const aiVector3D v2 = aiVector3D(a, -a, a);
const aiVector3D v3 = aiVector3D(a, -a, -a);
const aiVector3D v4 = aiVector3D(-a, a, a);
const aiVector3D v5 = aiVector3D(-a, a, -a);
const aiVector3D v6 = aiVector3D(-a, -a, a);
const aiVector3D v7 = aiVector3D(-a, -a, -a);
const aiVector3D v8 = aiVector3D(b, c, 0.0);
const aiVector3D v9 = aiVector3D(-b, c, 0.0);
const aiVector3D v10 = aiVector3D(b, -c, 0.0);
const aiVector3D v11 = aiVector3D(-b, -c, 0.0);
const aiVector3D v12 = aiVector3D(c, 0.0, b);
const aiVector3D v13 = aiVector3D(c, 0.0, -b);
const aiVector3D v14 = aiVector3D(-c, 0.0, b);
@ -288,74 +273,71 @@ unsigned int StandardShapes::MakeDodecahedron(std::vector<aiVector3D>& positions
// ------------------------------------------------------------------------------------------------
// Build an octahedron with points.magnitude == 1
unsigned int StandardShapes::MakeOctahedron(std::vector<aiVector3D>& positions)
{
positions.reserve(positions.size()+24);
unsigned int StandardShapes::MakeOctahedron(std::vector<aiVector3D> &positions) {
positions.reserve(positions.size() + 24);
const aiVector3D v0 = aiVector3D(1.0, 0.0, 0.0) ;
const aiVector3D v1 = aiVector3D(-1.0, 0.0, 0.0);
const aiVector3D v2 = aiVector3D(0.0, 1.0, 0.0);
const aiVector3D v3 = aiVector3D(0.0, -1.0, 0.0);
const aiVector3D v4 = aiVector3D(0.0, 0.0, 1.0);
const aiVector3D v5 = aiVector3D(0.0, 0.0, -1.0);
const aiVector3D v0 = aiVector3D(1.0, 0.0, 0.0);
const aiVector3D v1 = aiVector3D(-1.0, 0.0, 0.0);
const aiVector3D v2 = aiVector3D(0.0, 1.0, 0.0);
const aiVector3D v3 = aiVector3D(0.0, -1.0, 0.0);
const aiVector3D v4 = aiVector3D(0.0, 0.0, 1.0);
const aiVector3D v5 = aiVector3D(0.0, 0.0, -1.0);
ADD_TRIANGLE(v4,v0,v2);
ADD_TRIANGLE(v4,v2,v1);
ADD_TRIANGLE(v4,v1,v3);
ADD_TRIANGLE(v4,v3,v0);
ADD_TRIANGLE(v4, v0, v2);
ADD_TRIANGLE(v4, v2, v1);
ADD_TRIANGLE(v4, v1, v3);
ADD_TRIANGLE(v4, v3, v0);
ADD_TRIANGLE(v5,v2,v0);
ADD_TRIANGLE(v5,v1,v2);
ADD_TRIANGLE(v5,v3,v1);
ADD_TRIANGLE(v5,v0,v3);
ADD_TRIANGLE(v5, v2, v0);
ADD_TRIANGLE(v5, v1, v2);
ADD_TRIANGLE(v5, v3, v1);
ADD_TRIANGLE(v5, v0, v3);
return 3;
}
// ------------------------------------------------------------------------------------------------
// Build a tetrahedron with points.magnitude == 1
unsigned int StandardShapes::MakeTetrahedron(std::vector<aiVector3D>& positions)
{
positions.reserve(positions.size()+9);
unsigned int StandardShapes::MakeTetrahedron(std::vector<aiVector3D> &positions) {
positions.reserve(positions.size() + 9);
const ai_real invThree = ai_real( 1.0 ) / ai_real( 3.0 );
const ai_real a = ai_real( 1.41421 ) * invThree;
const ai_real b = ai_real( 2.4494 ) * invThree;
const ai_real invThree = ai_real(1.0) / ai_real(3.0);
const ai_real a = ai_real(1.41421) * invThree;
const ai_real b = ai_real(2.4494) * invThree;
const aiVector3D v0 = aiVector3D(0.0,0.0,1.0);
const aiVector3D v1 = aiVector3D(2*a,0,-invThree );
const aiVector3D v2 = aiVector3D(-a,b,-invThree );
const aiVector3D v3 = aiVector3D(-a,-b,-invThree );
const aiVector3D v0 = aiVector3D(0.0, 0.0, 1.0);
const aiVector3D v1 = aiVector3D(2 * a, 0, -invThree);
const aiVector3D v2 = aiVector3D(-a, b, -invThree);
const aiVector3D v3 = aiVector3D(-a, -b, -invThree);
ADD_TRIANGLE(v0,v1,v2);
ADD_TRIANGLE(v0,v2,v3);
ADD_TRIANGLE(v0,v3,v1);
ADD_TRIANGLE(v1,v3,v2);
ADD_TRIANGLE(v0, v1, v2);
ADD_TRIANGLE(v0, v2, v3);
ADD_TRIANGLE(v0, v3, v1);
ADD_TRIANGLE(v1, v3, v2);
return 3;
}
// ------------------------------------------------------------------------------------------------
// Build a hexahedron with points.magnitude == 1
unsigned int StandardShapes::MakeHexahedron(std::vector<aiVector3D>& positions,
bool polygons /*= false*/)
{
positions.reserve(positions.size()+36);
const ai_real length = ai_real(1.0)/ai_real(1.73205080);
unsigned int StandardShapes::MakeHexahedron(std::vector<aiVector3D> &positions,
bool polygons /*= false*/) {
positions.reserve(positions.size() + 36);
const ai_real length = ai_real(1.0) / ai_real(1.73205080);
const aiVector3D v0 = aiVector3D(-1.0,-1.0,-1.0)*length;
const aiVector3D v1 = aiVector3D(1.0,-1.0,-1.0)*length;
const aiVector3D v2 = aiVector3D(1.0,1.0,-1.0)*length;
const aiVector3D v3 = aiVector3D(-1.0,1.0,-1.0)*length;
const aiVector3D v4 = aiVector3D(-1.0,-1.0,1.0)*length;
const aiVector3D v5 = aiVector3D(1.0,-1.0,1.0)*length;
const aiVector3D v6 = aiVector3D(1.0,1.0,1.0)*length;
const aiVector3D v7 = aiVector3D(-1.0,1.0,1.0)*length;
const aiVector3D v0 = aiVector3D(-1.0, -1.0, -1.0) * length;
const aiVector3D v1 = aiVector3D(1.0, -1.0, -1.0) * length;
const aiVector3D v2 = aiVector3D(1.0, 1.0, -1.0) * length;
const aiVector3D v3 = aiVector3D(-1.0, 1.0, -1.0) * length;
const aiVector3D v4 = aiVector3D(-1.0, -1.0, 1.0) * length;
const aiVector3D v5 = aiVector3D(1.0, -1.0, 1.0) * length;
const aiVector3D v6 = aiVector3D(1.0, 1.0, 1.0) * length;
const aiVector3D v7 = aiVector3D(-1.0, 1.0, 1.0) * length;
ADD_QUAD(v0,v3,v2,v1);
ADD_QUAD(v0,v1,v5,v4);
ADD_QUAD(v0,v4,v7,v3);
ADD_QUAD(v6,v5,v1,v2);
ADD_QUAD(v6,v2,v3,v7);
ADD_QUAD(v6,v7,v4,v5);
ADD_QUAD(v0, v3, v2, v1);
ADD_QUAD(v0, v1, v5, v4);
ADD_QUAD(v0, v4, v7, v3);
ADD_QUAD(v6, v5, v1, v2);
ADD_QUAD(v6, v2, v3, v7);
ADD_QUAD(v6, v7, v4, v5);
return (polygons ? 4 : 3);
}
@ -366,28 +348,26 @@ unsigned int StandardShapes::MakeHexahedron(std::vector<aiVector3D>& positions,
// ------------------------------------------------------------------------------------------------
// Create a subdivision sphere
void StandardShapes::MakeSphere(unsigned int tess,
std::vector<aiVector3D>& positions)
{
void StandardShapes::MakeSphere(unsigned int tess,
std::vector<aiVector3D> &positions) {
// Reserve enough storage. Every subdivision
// splits each triangle in 4, the icosahedron consists of 60 verts
positions.reserve(positions.size()+60 * integer_pow(4, tess));
positions.reserve(positions.size() + 60 * integer_pow(4, tess));
// Construct an icosahedron to start with
MakeIcosahedron(positions);
// ... and subdivide it until the requested output
// tessellation is reached
for (unsigned int i = 0; i<tess;++i)
for (unsigned int i = 0; i < tess; ++i)
Subdivide(positions);
}
// ------------------------------------------------------------------------------------------------
// Build a cone
void StandardShapes::MakeCone(ai_real height,ai_real radius1,
ai_real radius2,unsigned int tess,
std::vector<aiVector3D>& positions,bool bOpen /*= false */)
{
void StandardShapes::MakeCone(ai_real height, ai_real radius1,
ai_real radius2, unsigned int tess,
std::vector<aiVector3D> &positions, bool bOpen /*= false */) {
// Sorry, a cone with less than 3 segments makes ABSOLUTELY NO SENSE
if (tess < 3 || !height)
return;
@ -401,39 +381,37 @@ void StandardShapes::MakeCone(ai_real height,ai_real radius1,
ai_real halfHeight = height / ai_real(2.0);
// radius1 is always the smaller one
if (radius2 > radius1)
{
std::swap(radius2,radius1);
if (radius2 > radius1) {
std::swap(radius2, radius1);
halfHeight = -halfHeight;
}
else old = SIZE_MAX;
} else
old = SIZE_MAX;
// Use a large epsilon to check whether the cone is pointy
if (radius1 < (radius2-radius1)*10e-3)radius1 = 0.0;
if (radius1 < (radius2 - radius1) * 10e-3) radius1 = 0.0;
// We will need 3*2 verts per segment + 3*2 verts per segment
// if the cone is closed
const unsigned int mem = tess*6 + (!bOpen ? tess*3 * (radius1 ? 2 : 1) : 0);
positions.reserve(positions.size () + mem);
const unsigned int mem = tess * 6 + (!bOpen ? tess * 3 * (radius1 ? 2 : 1) : 0);
positions.reserve(positions.size() + mem);
// Now construct all segments
const ai_real angle_delta = (ai_real)AI_MATH_TWO_PI / tess;
const ai_real angle_max = (ai_real)AI_MATH_TWO_PI;
const ai_real angle_max = (ai_real)AI_MATH_TWO_PI;
ai_real s = 1.0; // std::cos(angle == 0);
ai_real t = 0.0; // std::sin(angle == 0);
for (ai_real angle = 0.0; angle < angle_max; )
{
const aiVector3D v1 = aiVector3D (s * radius1, -halfHeight, t * radius1 );
const aiVector3D v2 = aiVector3D (s * radius2, halfHeight, t * radius2 );
for (ai_real angle = 0.0; angle < angle_max;) {
const aiVector3D v1 = aiVector3D(s * radius1, -halfHeight, t * radius1);
const aiVector3D v2 = aiVector3D(s * radius2, halfHeight, t * radius2);
const ai_real next = angle + angle_delta;
ai_real s2 = std::cos(next);
ai_real t2 = std::sin(next);
const aiVector3D v3 = aiVector3D (s2 * radius2, halfHeight, t2 * radius2 );
const aiVector3D v4 = aiVector3D (s2 * radius1, -halfHeight, t2 * radius1 );
const aiVector3D v3 = aiVector3D(s2 * radius2, halfHeight, t2 * radius2);
const aiVector3D v4 = aiVector3D(s2 * radius1, -halfHeight, t2 * radius1);
positions.push_back(v1);
positions.push_back(v2);
@ -442,21 +420,17 @@ void StandardShapes::MakeCone(ai_real height,ai_real radius1,
positions.push_back(v1);
positions.push_back(v3);
if (!bOpen)
{
if (!bOpen) {
// generate the end 'cap'
positions.push_back(aiVector3D(s * radius2, halfHeight, t * radius2 ));
positions.push_back(aiVector3D(s2 * radius2, halfHeight, t2 * radius2 ));
positions.push_back(aiVector3D(s * radius2, halfHeight, t * radius2));
positions.push_back(aiVector3D(s2 * radius2, halfHeight, t2 * radius2));
positions.push_back(aiVector3D(0.0, halfHeight, 0.0));
if (radius1)
{
if (radius1) {
// generate the other end 'cap'
positions.push_back(aiVector3D(s * radius1, -halfHeight, t * radius1 ));
positions.push_back(aiVector3D(s2 * radius1, -halfHeight, t2 * radius1 ));
positions.push_back(aiVector3D(s * radius1, -halfHeight, t * radius1));
positions.push_back(aiVector3D(s2 * radius1, -halfHeight, t2 * radius1));
positions.push_back(aiVector3D(0.0, -halfHeight, 0.0));
}
}
s = s2;
@ -465,9 +439,9 @@ void StandardShapes::MakeCone(ai_real height,ai_real radius1,
}
// Need to flip face order?
if ( SIZE_MAX != old ) {
for (size_t p = old; p < positions.size();p += 3) {
std::swap(positions[p],positions[p+1]);
if (SIZE_MAX != old) {
for (size_t p = old; p < positions.size(); p += 3) {
std::swap(positions[p], positions[p + 1]);
}
}
}
@ -475,8 +449,7 @@ void StandardShapes::MakeCone(ai_real height,ai_real radius1,
// ------------------------------------------------------------------------------------------------
// Build a circle
void StandardShapes::MakeCircle(ai_real radius, unsigned int tess,
std::vector<aiVector3D>& positions)
{
std::vector<aiVector3D> &positions) {
// Sorry, a circle with less than 3 segments makes ABSOLUTELY NO SENSE
if (tess < 3 || !radius)
return;
@ -484,24 +457,23 @@ void StandardShapes::MakeCircle(ai_real radius, unsigned int tess,
radius = std::fabs(radius);
// We will need 3 vertices per segment
positions.reserve(positions.size()+tess*3);
positions.reserve(positions.size() + tess * 3);
const ai_real angle_delta = (ai_real)AI_MATH_TWO_PI / tess;
const ai_real angle_max = (ai_real)AI_MATH_TWO_PI;
const ai_real angle_max = (ai_real)AI_MATH_TWO_PI;
ai_real s = 1.0; // std::cos(angle == 0);
ai_real t = 0.0; // std::sin(angle == 0);
for (ai_real angle = 0.0; angle < angle_max; )
{
positions.push_back(aiVector3D(s * radius,0.0,t * radius));
for (ai_real angle = 0.0; angle < angle_max;) {
positions.push_back(aiVector3D(s * radius, 0.0, t * radius));
angle += angle_delta;
s = std::cos(angle);
t = std::sin(angle);
positions.push_back(aiVector3D(s * radius,0.0,t * radius));
positions.push_back(aiVector3D(s * radius, 0.0, t * radius));
positions.push_back(aiVector3D(0.0,0.0,0.0));
positions.push_back(aiVector3D(0.0, 0.0, 0.0));
}
}
} // ! Assimp
} // namespace Assimp

View file

@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -53,6 +53,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using namespace Assimp;
void mydummy() {}
#ifdef _MSC_VER
#pragma warning(disable : 4709)
#endif // _MSC_VER
// ------------------------------------------------------------------------------------------------
/** Subdivider stub class to implement the Catmull-Clarke subdivision algorithm. The
* implementation is basing on recursive refinement. Directly evaluating the result is also
@ -60,24 +63,22 @@ void mydummy() {}
// ------------------------------------------------------------------------------------------------
class CatmullClarkSubdivider : public Subdivider {
public:
void Subdivide (aiMesh* mesh, aiMesh*& out, unsigned int num, bool discard_input);
void Subdivide (aiMesh** smesh, size_t nmesh,
aiMesh** out, unsigned int num, bool discard_input);
void Subdivide(aiMesh *mesh, aiMesh *&out, unsigned int num, bool discard_input);
void Subdivide(aiMesh **smesh, size_t nmesh,
aiMesh **out, unsigned int num, bool discard_input);
// ---------------------------------------------------------------------------
/** Intermediate description of an edge between two corners of a polygon*/
// ---------------------------------------------------------------------------
struct Edge
{
Edge()
: ref(0)
{}
struct Edge {
Edge() :
ref(0) {}
Vertex edge_point, midpoint;
unsigned int ref;
};
typedef std::vector<unsigned int> UIntVector;
typedef std::map<uint64_t,Edge> EdgeMap;
typedef std::map<uint64_t, Edge> EdgeMap;
// ---------------------------------------------------------------------------
// Hashing function to derive an index into an #EdgeMap from two given
@ -91,80 +92,72 @@ public:
// invariant id0<id1 to identify an edge - else two hashes would refer
// to the same edge.
// ---------------------------------------------------------------------------
#define MAKE_EDGE_HASH(id0,id1) (eh_tmp0__=id0,eh_tmp1__=id1,\
(eh_tmp0__<eh_tmp1__?std::swap(eh_tmp0__,eh_tmp1__):mydummy()),(uint64_t)eh_tmp0__^((uint64_t)eh_tmp1__<<32u))
#define MAKE_EDGE_HASH(id0, id1) (eh_tmp0__ = id0, eh_tmp1__ = id1, \
(eh_tmp0__ < eh_tmp1__ ? std::swap(eh_tmp0__, eh_tmp1__) : mydummy()), (uint64_t)eh_tmp0__ ^ ((uint64_t)eh_tmp1__ << 32u))
#define INIT_EDGE_HASH_TEMPORARIES()\
#define INIT_EDGE_HASH_TEMPORARIES() \
unsigned int eh_tmp0__, eh_tmp1__;
private:
void InternSubdivide (const aiMesh* const * smesh,
size_t nmesh,aiMesh** out, unsigned int num);
void InternSubdivide(const aiMesh *const *smesh,
size_t nmesh, aiMesh **out, unsigned int num);
};
// ------------------------------------------------------------------------------------------------
// Construct a subdivider of a specific type
Subdivider* Subdivider::Create (Algorithm algo)
{
switch (algo)
{
Subdivider *Subdivider::Create(Algorithm algo) {
switch (algo) {
case CATMULL_CLARKE:
return new CatmullClarkSubdivider();
};
ai_assert(false);
return NULL; // shouldn't happen
return nullptr; // shouldn't happen
}
// ------------------------------------------------------------------------------------------------
// Call the Catmull Clark subdivision algorithm for one mesh
void CatmullClarkSubdivider::Subdivide (
aiMesh* mesh,
aiMesh*& out,
unsigned int num,
bool discard_input
)
{
void CatmullClarkSubdivider::Subdivide(
aiMesh *mesh,
aiMesh *&out,
unsigned int num,
bool discard_input) {
ai_assert(mesh != out);
Subdivide(&mesh,1,&out,num,discard_input);
Subdivide(&mesh, 1, &out, num, discard_input);
}
// ------------------------------------------------------------------------------------------------
// Call the Catmull Clark subdivision algorithm for multiple meshes
void CatmullClarkSubdivider::Subdivide (
aiMesh** smesh,
size_t nmesh,
aiMesh** out,
unsigned int num,
bool discard_input
)
{
ai_assert( NULL != smesh );
ai_assert( NULL != out );
void CatmullClarkSubdivider::Subdivide(
aiMesh **smesh,
size_t nmesh,
aiMesh **out,
unsigned int num,
bool discard_input) {
ai_assert(nullptr != smesh);
ai_assert(nullptr != out);
// course, both regions may not overlap
ai_assert(smesh<out || smesh+nmesh>out+nmesh);
ai_assert(smesh < out || smesh + nmesh > out + nmesh);
if (!num) {
// No subdivision at all. Need to copy all the meshes .. argh.
if (discard_input) {
for (size_t s = 0; s < nmesh; ++s) {
out[s] = smesh[s];
smesh[s] = NULL;
smesh[s] = nullptr;
}
}
else {
} else {
for (size_t s = 0; s < nmesh; ++s) {
SceneCombiner::Copy(out+s,smesh[s]);
SceneCombiner::Copy(out + s, smesh[s]);
}
}
return;
}
std::vector<aiMesh*> inmeshes;
std::vector<aiMesh*> outmeshes;
std::vector<aiMesh *> inmeshes;
std::vector<aiMesh *> outmeshes;
std::vector<unsigned int> maptbl;
inmeshes.reserve(nmesh);
@ -175,34 +168,35 @@ void CatmullClarkSubdivider::Subdivide (
// number of edge cases the subdivider is forced to deal with. Line and
// point meshes are simply passed through.
for (size_t s = 0; s < nmesh; ++s) {
aiMesh* i = smesh[s];
aiMesh *i = smesh[s];
// FIX - mPrimitiveTypes might not yet be initialized
if (i->mPrimitiveTypes && (i->mPrimitiveTypes & (aiPrimitiveType_LINE|aiPrimitiveType_POINT))==i->mPrimitiveTypes) {
ASSIMP_LOG_DEBUG("Catmull-Clark Subdivider: Skipping pure line/point mesh");
if (i->mPrimitiveTypes && (i->mPrimitiveTypes & (aiPrimitiveType_LINE | aiPrimitiveType_POINT)) == i->mPrimitiveTypes) {
ASSIMP_LOG_VERBOSE_DEBUG("Catmull-Clark Subdivider: Skipping pure line/point mesh");
if (discard_input) {
out[s] = i;
smesh[s] = NULL;
}
else {
SceneCombiner::Copy(out+s,i);
smesh[s] = nullptr;
} else {
SceneCombiner::Copy(out + s, i);
}
continue;
}
outmeshes.push_back(NULL);inmeshes.push_back(i);
outmeshes.push_back(nullptr);
inmeshes.push_back(i);
maptbl.push_back(static_cast<unsigned int>(s));
}
// Do the actual subdivision on the preallocated storage. InternSubdivide
// *always* assumes that enough storage is available, it does not bother
// checking any ranges.
ai_assert(inmeshes.size()==outmeshes.size()&&inmeshes.size()==maptbl.size());
ai_assert(inmeshes.size() == outmeshes.size());
ai_assert(inmeshes.size() == maptbl.size());
if (inmeshes.empty()) {
ASSIMP_LOG_WARN("Catmull-Clark Subdivider: Pure point/line scene, I can't do anything");
return;
}
InternSubdivide(&inmeshes.front(),inmeshes.size(),&outmeshes.front(),num);
InternSubdivide(&inmeshes.front(), inmeshes.size(), &outmeshes.front(), num);
for (unsigned int i = 0; i < maptbl.size(); ++i) {
ai_assert(nullptr != outmeshes[i]);
out[maptbl[i]] = outmeshes[i];
@ -226,14 +220,14 @@ void CatmullClarkSubdivider::Subdivide (
// in-place unless 'smesh' and 'out' are equal (no strange overlaps or reorderings).
// Previous data is replaced/deleted then.
// ------------------------------------------------------------------------------------------------
void CatmullClarkSubdivider::InternSubdivide (
const aiMesh* const * smesh,
size_t nmesh,
aiMesh** out,
unsigned int num
)
{
ai_assert(NULL != smesh && NULL != out);
void CatmullClarkSubdivider::InternSubdivide(
const aiMesh *const *smesh,
size_t nmesh,
aiMesh **out,
unsigned int num) {
ai_assert(nullptr != smesh);
ai_assert(nullptr != out);
INIT_EDGE_HASH_TEMPORARIES();
// no subdivision requested or end of recursive refinement
@ -248,25 +242,24 @@ void CatmullClarkSubdivider::InternSubdivide (
// 0. Offset table to index all meshes continuously, generate a spatially
// sorted representation of all vertices in all meshes.
// ---------------------------------------------------------------------
typedef std::pair<unsigned int,unsigned int> IntPair;
typedef std::pair<unsigned int, unsigned int> IntPair;
std::vector<IntPair> moffsets(nmesh);
unsigned int totfaces = 0, totvert = 0;
for (size_t t = 0; t < nmesh; ++t) {
const aiMesh* mesh = smesh[t];
const aiMesh *mesh = smesh[t];
spatial.Append(mesh->mVertices,mesh->mNumVertices,sizeof(aiVector3D),false);
moffsets[t] = IntPair(totfaces,totvert);
spatial.Append(mesh->mVertices, mesh->mNumVertices, sizeof(aiVector3D), false);
moffsets[t] = IntPair(totfaces, totvert);
totfaces += mesh->mNumFaces;
totvert += mesh->mNumVertices;
totvert += mesh->mNumVertices;
}
spatial.Finalize();
const unsigned int num_unique = spatial.GenerateMappingTable(maptbl,ComputePositionEpsilon(smesh,nmesh));
const unsigned int num_unique = spatial.GenerateMappingTable(maptbl, ComputePositionEpsilon(smesh, nmesh));
#define FLATTEN_VERTEX_IDX(mesh_idx, vert_idx) (moffsets[mesh_idx].second+vert_idx)
#define FLATTEN_FACE_IDX(mesh_idx, face_idx) (moffsets[mesh_idx].first+face_idx)
#define FLATTEN_VERTEX_IDX(mesh_idx, vert_idx) (moffsets[mesh_idx].second + vert_idx)
#define FLATTEN_FACE_IDX(mesh_idx, face_idx) (moffsets[mesh_idx].first + face_idx)
// ---------------------------------------------------------------------
// 1. Compute the centroid point for all faces
@ -274,14 +267,13 @@ void CatmullClarkSubdivider::InternSubdivide (
std::vector<Vertex> centroids(totfaces);
unsigned int nfacesout = 0;
for (size_t t = 0, n = 0; t < nmesh; ++t) {
const aiMesh* mesh = smesh[t];
for (unsigned int i = 0; i < mesh->mNumFaces;++i,++n)
{
const aiFace& face = mesh->mFaces[i];
Vertex& c = centroids[n];
const aiMesh *mesh = smesh[t];
for (unsigned int i = 0; i < mesh->mNumFaces; ++i, ++n) {
const aiFace &face = mesh->mFaces[i];
Vertex &c = centroids[n];
for (unsigned int a = 0; a < face.mNumIndices;++a) {
c += Vertex(mesh,face.mIndices[a]);
for (unsigned int a = 0; a < face.mNumIndices; ++a) {
c += Vertex(mesh, face.mIndices[a]);
}
c /= static_cast<float>(face.mNumIndices);
@ -290,297 +282,297 @@ void CatmullClarkSubdivider::InternSubdivide (
}
{
// we want edges to go away before the recursive calls so begin a new scope
EdgeMap edges;
// we want edges to go away before the recursive calls so begin a new scope
EdgeMap edges;
// ---------------------------------------------------------------------
// 2. Set each edge point to be the average of all neighbouring
// face points and original points. Every edge exists twice
// if there is a neighboring face.
// ---------------------------------------------------------------------
for (size_t t = 0; t < nmesh; ++t) {
const aiMesh* mesh = smesh[t];
// ---------------------------------------------------------------------
// 2. Set each edge point to be the average of all neighbouring
// face points and original points. Every edge exists twice
// if there is a neighboring face.
// ---------------------------------------------------------------------
for (size_t t = 0; t < nmesh; ++t) {
const aiMesh *mesh = smesh[t];
for (unsigned int i = 0; i < mesh->mNumFaces;++i) {
const aiFace& face = mesh->mFaces[i];
for (unsigned int i = 0; i < mesh->mNumFaces; ++i) {
const aiFace &face = mesh->mFaces[i];
for (unsigned int p =0; p< face.mNumIndices; ++p) {
const unsigned int id[] = {
face.mIndices[p],
face.mIndices[p==face.mNumIndices-1?0:p+1]
};
const unsigned int mp[] = {
maptbl[FLATTEN_VERTEX_IDX(t,id[0])],
maptbl[FLATTEN_VERTEX_IDX(t,id[1])]
};
for (unsigned int p = 0; p < face.mNumIndices; ++p) {
const unsigned int id[] = {
face.mIndices[p],
face.mIndices[p == face.mNumIndices - 1 ? 0 : p + 1]
};
const unsigned int mp[] = {
maptbl[FLATTEN_VERTEX_IDX(t, id[0])],
maptbl[FLATTEN_VERTEX_IDX(t, id[1])]
};
Edge& e = edges[MAKE_EDGE_HASH(mp[0],mp[1])];
e.ref++;
if (e.ref<=2) {
if (e.ref==1) { // original points (end points) - add only once
e.edge_point = e.midpoint = Vertex(mesh,id[0])+Vertex(mesh,id[1]);
e.midpoint *= 0.5f;
Edge &e = edges[MAKE_EDGE_HASH(mp[0], mp[1])];
e.ref++;
if (e.ref <= 2) {
if (e.ref == 1) { // original points (end points) - add only once
e.edge_point = e.midpoint = Vertex(mesh, id[0]) + Vertex(mesh, id[1]);
e.midpoint *= 0.5f;
}
e.edge_point += centroids[FLATTEN_FACE_IDX(t, i)];
}
e.edge_point += centroids[FLATTEN_FACE_IDX(t,i)];
}
}
}
}
// ---------------------------------------------------------------------
// 3. Normalize edge points
// ---------------------------------------------------------------------
{unsigned int bad_cnt = 0;
for (EdgeMap::iterator it = edges.begin(); it != edges.end(); ++it) {
if ((*it).second.ref < 2) {
ai_assert((*it).second.ref);
++bad_cnt;
}
(*it).second.edge_point *= 1.f/((*it).second.ref+2.f);
}
// ---------------------------------------------------------------------
// 3. Normalize edge points
// ---------------------------------------------------------------------
{
unsigned int bad_cnt = 0;
for (EdgeMap::iterator it = edges.begin(); it != edges.end(); ++it) {
if ((*it).second.ref < 2) {
ai_assert((*it).second.ref);
++bad_cnt;
}
(*it).second.edge_point *= 1.f / ((*it).second.ref + 2.f);
}
if (bad_cnt) {
// Report the number of bad edges. bad edges are referenced by less than two
// faces in the mesh. They occur at outer model boundaries in non-closed
// shapes.
ASSIMP_LOG_DEBUG_F("Catmull-Clark Subdivider: got ", bad_cnt, " bad edges touching only one face (totally ",
static_cast<unsigned int>(edges.size()), " edges). ");
}}
// ---------------------------------------------------------------------
// 4. Compute a vertex-face adjacency table. We can't reuse the code
// from VertexTriangleAdjacency because we need the table for multiple
// meshes and out vertex indices need to be mapped to distinct values
// first.
// ---------------------------------------------------------------------
UIntVector faceadjac(nfacesout), cntadjfac(maptbl.size(),0), ofsadjvec(maptbl.size()+1,0); {
for (size_t t = 0; t < nmesh; ++t) {
const aiMesh* const minp = smesh[t];
for (unsigned int i = 0; i < minp->mNumFaces; ++i) {
const aiFace& f = minp->mFaces[i];
for (unsigned int n = 0; n < f.mNumIndices; ++n) {
++cntadjfac[maptbl[FLATTEN_VERTEX_IDX(t,f.mIndices[n])]];
if (bad_cnt) {
// Report the number of bad edges. bad edges are referenced by less than two
// faces in the mesh. They occur at outer model boundaries in non-closed
// shapes.
ASSIMP_LOG_VERBOSE_DEBUG("Catmull-Clark Subdivider: got ", bad_cnt, " bad edges touching only one face (totally ",
static_cast<unsigned int>(edges.size()), " edges). ");
}
}
}
unsigned int cur = 0;
for (size_t i = 0; i < cntadjfac.size(); ++i) {
ofsadjvec[i+1] = cur;
cur += cntadjfac[i];
}
for (size_t t = 0; t < nmesh; ++t) {
const aiMesh* const minp = smesh[t];
for (unsigned int i = 0; i < minp->mNumFaces; ++i) {
const aiFace& f = minp->mFaces[i];
for (unsigned int n = 0; n < f.mNumIndices; ++n) {
faceadjac[ofsadjvec[1+maptbl[FLATTEN_VERTEX_IDX(t,f.mIndices[n])]]++] = FLATTEN_FACE_IDX(t,i);
// ---------------------------------------------------------------------
// 4. Compute a vertex-face adjacency table. We can't reuse the code
// from VertexTriangleAdjacency because we need the table for multiple
// meshes and out vertex indices need to be mapped to distinct values
// first.
// ---------------------------------------------------------------------
UIntVector faceadjac(nfacesout), cntadjfac(maptbl.size(), 0), ofsadjvec(maptbl.size() + 1, 0);
{
for (size_t t = 0; t < nmesh; ++t) {
const aiMesh *const minp = smesh[t];
for (unsigned int i = 0; i < minp->mNumFaces; ++i) {
const aiFace &f = minp->mFaces[i];
for (unsigned int n = 0; n < f.mNumIndices; ++n) {
++cntadjfac[maptbl[FLATTEN_VERTEX_IDX(t, f.mIndices[n])]];
}
}
}
}
}
unsigned int cur = 0;
for (size_t i = 0; i < cntadjfac.size(); ++i) {
ofsadjvec[i + 1] = cur;
cur += cntadjfac[i];
}
for (size_t t = 0; t < nmesh; ++t) {
const aiMesh *const minp = smesh[t];
for (unsigned int i = 0; i < minp->mNumFaces; ++i) {
// check the other way round for consistency
const aiFace &f = minp->mFaces[i];
for (unsigned int n = 0; n < f.mNumIndices; ++n) {
faceadjac[ofsadjvec[1 + maptbl[FLATTEN_VERTEX_IDX(t, f.mIndices[n])]]++] = FLATTEN_FACE_IDX(t, i);
}
}
}
// check the other way round for consistency
#ifdef ASSIMP_BUILD_DEBUG
for (size_t t = 0; t < ofsadjvec.size()-1; ++t) {
for (unsigned int m = 0; m < cntadjfac[t]; ++m) {
const unsigned int fidx = faceadjac[ofsadjvec[t]+m];
ai_assert(fidx < totfaces);
for (size_t n = 1; n < nmesh; ++n) {
for (size_t t = 0; t < ofsadjvec.size() - 1; ++t) {
for (unsigned int m = 0; m < cntadjfac[t]; ++m) {
const unsigned int fidx = faceadjac[ofsadjvec[t] + m];
ai_assert(fidx < totfaces);
for (size_t n = 1; n < nmesh; ++n) {
if (moffsets[n].first > fidx) {
const aiMesh* msh = smesh[--n];
const aiFace& f = msh->mFaces[fidx-moffsets[n].first];
if (moffsets[n].first > fidx) {
const aiMesh *msh = smesh[--n];
const aiFace &f = msh->mFaces[fidx - moffsets[n].first];
bool haveit = false;
for (unsigned int i = 0; i < f.mNumIndices; ++i) {
if (maptbl[FLATTEN_VERTEX_IDX(n,f.mIndices[i])]==(unsigned int)t) {
haveit = true;
break;
}
}
ai_assert(haveit);
if (!haveit) {
ASSIMP_LOG_DEBUG("Catmull-Clark Subdivider: Index not used");
}
break;
}
}
}
}
#endif
}
#define GET_ADJACENT_FACES_AND_CNT(vidx,fstartout,numout) \
fstartout = &faceadjac[ofsadjvec[vidx]], numout = cntadjfac[vidx]
typedef std::pair<bool,Vertex> TouchedOVertex;
std::vector<TouchedOVertex > new_points(num_unique,TouchedOVertex(false,Vertex()));
// ---------------------------------------------------------------------
// 5. Spawn a quad from each face point to the corresponding edge points
// the original points being the fourth quad points.
// ---------------------------------------------------------------------
for (size_t t = 0; t < nmesh; ++t) {
const aiMesh* const minp = smesh[t];
aiMesh* const mout = out[t] = new aiMesh();
for (unsigned int a = 0; a < minp->mNumFaces; ++a) {
mout->mNumFaces += minp->mFaces[a].mNumIndices;
}
// We need random access to the old face buffer, so reuse is not possible.
mout->mFaces = new aiFace[mout->mNumFaces];
mout->mNumVertices = mout->mNumFaces*4;
mout->mVertices = new aiVector3D[mout->mNumVertices];
// quads only, keep material index
mout->mPrimitiveTypes = aiPrimitiveType_POLYGON;
mout->mMaterialIndex = minp->mMaterialIndex;
if (minp->HasNormals()) {
mout->mNormals = new aiVector3D[mout->mNumVertices];
}
if (minp->HasTangentsAndBitangents()) {
mout->mTangents = new aiVector3D[mout->mNumVertices];
mout->mBitangents = new aiVector3D[mout->mNumVertices];
}
for(unsigned int i = 0; minp->HasTextureCoords(i); ++i) {
mout->mTextureCoords[i] = new aiVector3D[mout->mNumVertices];
mout->mNumUVComponents[i] = minp->mNumUVComponents[i];
}
for(unsigned int i = 0; minp->HasVertexColors(i); ++i) {
mout->mColors[i] = new aiColor4D[mout->mNumVertices];
}
mout->mNumVertices = mout->mNumFaces<<2u;
for (unsigned int i = 0, v = 0, n = 0; i < minp->mNumFaces;++i) {
const aiFace& face = minp->mFaces[i];
for (unsigned int a = 0; a < face.mNumIndices;++a) {
// Get a clean new face.
aiFace& faceOut = mout->mFaces[n++];
faceOut.mIndices = new unsigned int [faceOut.mNumIndices = 4];
// Spawn a new quadrilateral (ccw winding) for this original point between:
// a) face centroid
centroids[FLATTEN_FACE_IDX(t,i)].SortBack(mout,faceOut.mIndices[0]=v++);
// b) adjacent edge on the left, seen from the centroid
const Edge& e0 = edges[MAKE_EDGE_HASH(maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[a])],
maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[a==face.mNumIndices-1?0:a+1])
])]; // fixme: replace with mod face.mNumIndices?
// c) adjacent edge on the right, seen from the centroid
const Edge& e1 = edges[MAKE_EDGE_HASH(maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[a])],
maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[!a?face.mNumIndices-1:a-1])
])]; // fixme: replace with mod face.mNumIndices?
e0.edge_point.SortBack(mout,faceOut.mIndices[3]=v++);
e1.edge_point.SortBack(mout,faceOut.mIndices[1]=v++);
// d= original point P with distinct index i
// F := 0
// R := 0
// n := 0
// for each face f containing i
// F := F+ centroid of f
// R := R+ midpoint of edge of f from i to i+1
// n := n+1
//
// (F+2R+(n-3)P)/n
const unsigned int org = maptbl[FLATTEN_VERTEX_IDX(t,face.mIndices[a])];
TouchedOVertex& ov = new_points[org];
if (!ov.first) {
ov.first = true;
const unsigned int* adj; unsigned int cnt;
GET_ADJACENT_FACES_AND_CNT(org,adj,cnt);
if (cnt < 3) {
ov.second = Vertex(minp,face.mIndices[a]);
}
else {
Vertex F,R;
for (unsigned int o = 0; o < cnt; ++o) {
ai_assert(adj[o] < totfaces);
F += centroids[adj[o]];
// adj[0] is a global face index - search the face in the mesh list
const aiMesh* mp = NULL;
size_t nidx;
if (adj[o] < moffsets[0].first) {
mp = smesh[nidx=0];
}
else {
for (nidx = 1; nidx<= nmesh; ++nidx) {
if (nidx == nmesh ||moffsets[nidx].first > adj[o]) {
mp = smesh[--nidx];
break;
}
}
}
ai_assert(adj[o]-moffsets[nidx].first < mp->mNumFaces);
const aiFace& f = mp->mFaces[adj[o]-moffsets[nidx].first];
bool haveit = false;
// find our original point in the face
for (unsigned int m = 0; m < f.mNumIndices; ++m) {
if (maptbl[FLATTEN_VERTEX_IDX(nidx,f.mIndices[m])] == org) {
// add *both* edges. this way, we can be sure that we add
// *all* adjacent edges to R. In a closed shape, every
// edge is added twice - so we simply leave out the
// factor 2.f in the amove formula and get the right
// result.
const Edge& c0 = edges[MAKE_EDGE_HASH(org,maptbl[FLATTEN_VERTEX_IDX(
nidx,f.mIndices[!m?f.mNumIndices-1:m-1])])];
// fixme: replace with mod face.mNumIndices?
const Edge& c1 = edges[MAKE_EDGE_HASH(org,maptbl[FLATTEN_VERTEX_IDX(
nidx,f.mIndices[m==f.mNumIndices-1?0:m+1])])];
// fixme: replace with mod face.mNumIndices?
R += c0.midpoint+c1.midpoint;
for (unsigned int i = 0; i < f.mNumIndices; ++i) {
if (maptbl[FLATTEN_VERTEX_IDX(n, f.mIndices[i])] == (unsigned int)t) {
haveit = true;
break;
}
}
// this invariant *must* hold if the vertex-to-face adjacency table is valid
ai_assert(haveit);
if ( !haveit ) {
ASSIMP_LOG_WARN( "OBJ: no name for material library specified." );
if (!haveit) {
ASSIMP_LOG_VERBOSE_DEBUG("Catmull-Clark Subdivider: Index not used");
}
break;
}
const float div = static_cast<float>(cnt), divsq = 1.f/(div*div);
ov.second = Vertex(minp,face.mIndices[a])*((div-3.f) / div) + R*divsq + F*divsq;
}
}
ov.second.SortBack(mout,faceOut.mIndices[2]=v++);
}
#endif
}
#define GET_ADJACENT_FACES_AND_CNT(vidx, fstartout, numout) \
fstartout = &faceadjac[ofsadjvec[vidx]], numout = cntadjfac[vidx]
typedef std::pair<bool, Vertex> TouchedOVertex;
std::vector<TouchedOVertex> new_points(num_unique, TouchedOVertex(false, Vertex()));
// ---------------------------------------------------------------------
// 5. Spawn a quad from each face point to the corresponding edge points
// the original points being the fourth quad points.
// ---------------------------------------------------------------------
for (size_t t = 0; t < nmesh; ++t) {
const aiMesh *const minp = smesh[t];
aiMesh *const mout = out[t] = new aiMesh();
for (unsigned int a = 0; a < minp->mNumFaces; ++a) {
mout->mNumFaces += minp->mFaces[a].mNumIndices;
}
// We need random access to the old face buffer, so reuse is not possible.
mout->mFaces = new aiFace[mout->mNumFaces];
mout->mNumVertices = mout->mNumFaces * 4;
mout->mVertices = new aiVector3D[mout->mNumVertices];
// quads only, keep material index
mout->mPrimitiveTypes = aiPrimitiveType_POLYGON;
mout->mMaterialIndex = minp->mMaterialIndex;
if (minp->HasNormals()) {
mout->mNormals = new aiVector3D[mout->mNumVertices];
}
if (minp->HasTangentsAndBitangents()) {
mout->mTangents = new aiVector3D[mout->mNumVertices];
mout->mBitangents = new aiVector3D[mout->mNumVertices];
}
for (unsigned int i = 0; minp->HasTextureCoords(i); ++i) {
mout->mTextureCoords[i] = new aiVector3D[mout->mNumVertices];
mout->mNumUVComponents[i] = minp->mNumUVComponents[i];
}
for (unsigned int i = 0; minp->HasVertexColors(i); ++i) {
mout->mColors[i] = new aiColor4D[mout->mNumVertices];
}
mout->mNumVertices = mout->mNumFaces << 2u;
for (unsigned int i = 0, v = 0, n = 0; i < minp->mNumFaces; ++i) {
const aiFace &face = minp->mFaces[i];
for (unsigned int a = 0; a < face.mNumIndices; ++a) {
// Get a clean new face.
aiFace &faceOut = mout->mFaces[n++];
faceOut.mIndices = new unsigned int[faceOut.mNumIndices = 4];
// Spawn a new quadrilateral (ccw winding) for this original point between:
// a) face centroid
centroids[FLATTEN_FACE_IDX(t, i)].SortBack(mout, faceOut.mIndices[0] = v++);
// b) adjacent edge on the left, seen from the centroid
const Edge &e0 = edges[MAKE_EDGE_HASH(maptbl[FLATTEN_VERTEX_IDX(t, face.mIndices[a])],
maptbl[FLATTEN_VERTEX_IDX(t, face.mIndices[a == face.mNumIndices - 1 ? 0 : a + 1])])]; // fixme: replace with mod face.mNumIndices?
// c) adjacent edge on the right, seen from the centroid
const Edge &e1 = edges[MAKE_EDGE_HASH(maptbl[FLATTEN_VERTEX_IDX(t, face.mIndices[a])],
maptbl[FLATTEN_VERTEX_IDX(t, face.mIndices[!a ? face.mNumIndices - 1 : a - 1])])]; // fixme: replace with mod face.mNumIndices?
e0.edge_point.SortBack(mout, faceOut.mIndices[3] = v++);
e1.edge_point.SortBack(mout, faceOut.mIndices[1] = v++);
// d= original point P with distinct index i
// F := 0
// R := 0
// n := 0
// for each face f containing i
// F := F+ centroid of f
// R := R+ midpoint of edge of f from i to i+1
// n := n+1
//
// (F+2R+(n-3)P)/n
const unsigned int org = maptbl[FLATTEN_VERTEX_IDX(t, face.mIndices[a])];
TouchedOVertex &ov = new_points[org];
if (!ov.first) {
ov.first = true;
const unsigned int *adj;
unsigned int cnt;
GET_ADJACENT_FACES_AND_CNT(org, adj, cnt);
if (cnt < 3) {
ov.second = Vertex(minp, face.mIndices[a]);
} else {
Vertex F, R;
for (unsigned int o = 0; o < cnt; ++o) {
ai_assert(adj[o] < totfaces);
F += centroids[adj[o]];
// adj[0] is a global face index - search the face in the mesh list
const aiMesh *mp = nullptr;
size_t nidx;
if (adj[o] < moffsets[0].first) {
mp = smesh[nidx = 0];
} else {
for (nidx = 1; nidx <= nmesh; ++nidx) {
if (nidx == nmesh || moffsets[nidx].first > adj[o]) {
mp = smesh[--nidx];
break;
}
}
}
ai_assert(adj[o] - moffsets[nidx].first < mp->mNumFaces);
const aiFace &f = mp->mFaces[adj[o] - moffsets[nidx].first];
bool haveit = false;
// find our original point in the face
for (unsigned int m = 0; m < f.mNumIndices; ++m) {
if (maptbl[FLATTEN_VERTEX_IDX(nidx, f.mIndices[m])] == org) {
// add *both* edges. this way, we can be sure that we add
// *all* adjacent edges to R. In a closed shape, every
// edge is added twice - so we simply leave out the
// factor 2.f in the amove formula and get the right
// result.
const Edge &c0 = edges[MAKE_EDGE_HASH(org, maptbl[FLATTEN_VERTEX_IDX(
nidx, f.mIndices[!m ? f.mNumIndices - 1 : m - 1])])];
// fixme: replace with mod face.mNumIndices?
const Edge &c1 = edges[MAKE_EDGE_HASH(org, maptbl[FLATTEN_VERTEX_IDX(
nidx, f.mIndices[m == f.mNumIndices - 1 ? 0 : m + 1])])];
// fixme: replace with mod face.mNumIndices?
R += c0.midpoint + c1.midpoint;
haveit = true;
break;
}
}
// this invariant *must* hold if the vertex-to-face adjacency table is valid
ai_assert(haveit);
if (!haveit) {
ASSIMP_LOG_WARN("OBJ: no name for material library specified.");
}
}
const float div = static_cast<float>(cnt), divsq = 1.f / (div * div);
ov.second = Vertex(minp, face.mIndices[a]) * ((div - 3.f) / div) + R * divsq + F * divsq;
}
}
ov.second.SortBack(mout, faceOut.mIndices[2] = v++);
}
}
}
}
} // end of scope for edges, freeing its memory
} // end of scope for edges, freeing its memory
// ---------------------------------------------------------------------
// 7. Apply the next subdivision step.
// ---------------------------------------------------------------------
if (num != 1) {
std::vector<aiMesh*> tmp(nmesh);
InternSubdivide (out,nmesh,&tmp.front(),num-1);
std::vector<aiMesh *> tmp(nmesh);
InternSubdivide(out, nmesh, &tmp.front(), num - 1);
for (size_t i = 0; i < nmesh; ++i) {
delete out[i];
out[i] = tmp[i];

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -41,58 +41,51 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "TargetAnimation.h"
#include <algorithm>
#include <assimp/ai_assert.h>
#include <algorithm>
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
KeyIterator::KeyIterator(const std::vector<aiVectorKey>* _objPos,
const std::vector<aiVectorKey>* _targetObjPos,
const aiVector3D* defaultObjectPos /*= NULL*/,
const aiVector3D* defaultTargetPos /*= NULL*/)
: reachedEnd (false)
, curTime (-1.)
, objPos (_objPos)
, targetObjPos (_targetObjPos)
, nextObjPos (0)
, nextTargetObjPos(0)
{
KeyIterator::KeyIterator(const std::vector<aiVectorKey> *_objPos,
const std::vector<aiVectorKey> *_targetObjPos,
const aiVector3D *defaultObjectPos /*= nullptr*/,
const aiVector3D *defaultTargetPos /*= nullptr*/) :
reachedEnd(false),
curTime(-1.),
objPos(_objPos),
targetObjPos(_targetObjPos),
nextObjPos(0),
nextTargetObjPos(0) {
// Generate default transformation tracks if necessary
if (!objPos || objPos->empty())
{
if (!objPos || objPos->empty()) {
defaultObjPos.resize(1);
defaultObjPos.front().mTime = 10e10;
defaultObjPos.front().mTime = 10e10;
if (defaultObjectPos)
defaultObjPos.front().mValue = *defaultObjectPos;
objPos = & defaultObjPos;
objPos = &defaultObjPos;
}
if (!targetObjPos || targetObjPos->empty())
{
if (!targetObjPos || targetObjPos->empty()) {
defaultTargetObjPos.resize(1);
defaultTargetObjPos.front().mTime = 10e10;
defaultTargetObjPos.front().mTime = 10e10;
if (defaultTargetPos)
defaultTargetObjPos.front().mValue = *defaultTargetPos;
targetObjPos = & defaultTargetObjPos;
targetObjPos = &defaultTargetObjPos;
}
}
// ------------------------------------------------------------------------------------------------
template <class T>
inline T Interpolate(const T& one, const T& two, ai_real val)
{
return one + (two-one)*val;
inline T Interpolate(const T &one, const T &two, ai_real val) {
return one + (two - one) * val;
}
// ------------------------------------------------------------------------------------------------
void KeyIterator::operator ++()
{
void KeyIterator::operator++() {
// If we are already at the end of all keyframes, return
if (reachedEnd) {
return;
@ -100,113 +93,102 @@ void KeyIterator::operator ++()
// Now search in all arrays for the time value closest
// to our current position on the time line
double d0,d1;
double d0, d1;
d0 = objPos->at ( std::min ( nextObjPos, static_cast<unsigned int>(objPos->size()-1)) ).mTime;
d1 = targetObjPos->at( std::min ( nextTargetObjPos, static_cast<unsigned int>(targetObjPos->size()-1)) ).mTime;
d0 = objPos->at(std::min(nextObjPos, static_cast<unsigned int>(objPos->size() - 1))).mTime;
d1 = targetObjPos->at(std::min(nextTargetObjPos, static_cast<unsigned int>(targetObjPos->size() - 1))).mTime;
// Easiest case - all are identical. In this
// case we don't need to interpolate so we can
// return earlier
if ( d0 == d1 )
{
if (d0 == d1) {
curTime = d0;
curPosition = objPos->at(nextObjPos).mValue;
curTargetPosition = targetObjPos->at(nextTargetObjPos).mValue;
// increment counters
if (objPos->size() != nextObjPos-1)
if (objPos->size() != nextObjPos - 1)
++nextObjPos;
if (targetObjPos->size() != nextTargetObjPos-1)
if (targetObjPos->size() != nextTargetObjPos - 1)
++nextTargetObjPos;
}
// An object position key is closest to us
else if (d0 < d1)
{
else if (d0 < d1) {
curTime = d0;
// interpolate the other
if (1 == targetObjPos->size() || !nextTargetObjPos) {
curTargetPosition = targetObjPos->at(0).mValue;
}
else
{
const aiVectorKey& last = targetObjPos->at(nextTargetObjPos);
const aiVectorKey& first = targetObjPos->at(nextTargetObjPos-1);
} else {
const aiVectorKey &last = targetObjPos->at(nextTargetObjPos);
const aiVectorKey &first = targetObjPos->at(nextTargetObjPos - 1);
curTargetPosition = Interpolate(first.mValue, last.mValue, (ai_real) (
(curTime-first.mTime) / (last.mTime-first.mTime) ));
curTargetPosition = Interpolate(first.mValue, last.mValue, (ai_real)((curTime - first.mTime) / (last.mTime - first.mTime)));
}
if (objPos->size() != nextObjPos-1)
if (objPos->size() != nextObjPos - 1)
++nextObjPos;
}
// A target position key is closest to us
else
{
else {
curTime = d1;
// interpolate the other
if (1 == objPos->size() || !nextObjPos) {
curPosition = objPos->at(0).mValue;
}
else
{
const aiVectorKey& last = objPos->at(nextObjPos);
const aiVectorKey& first = objPos->at(nextObjPos-1);
} else {
const aiVectorKey &last = objPos->at(nextObjPos);
const aiVectorKey &first = objPos->at(nextObjPos - 1);
curPosition = Interpolate(first.mValue, last.mValue, (ai_real) (
(curTime-first.mTime) / (last.mTime-first.mTime)));
curPosition = Interpolate(first.mValue, last.mValue, (ai_real)((curTime - first.mTime) / (last.mTime - first.mTime)));
}
if (targetObjPos->size() != nextTargetObjPos-1)
if (targetObjPos->size() != nextTargetObjPos - 1)
++nextTargetObjPos;
}
if (nextObjPos >= objPos->size()-1 &&
nextTargetObjPos >= targetObjPos->size()-1)
{
if (nextObjPos >= objPos->size() - 1 &&
nextTargetObjPos >= targetObjPos->size() - 1) {
// We reached the very last keyframe
reachedEnd = true;
}
}
// ------------------------------------------------------------------------------------------------
void TargetAnimationHelper::SetTargetAnimationChannel (
const std::vector<aiVectorKey>* _targetPositions)
{
ai_assert(NULL != _targetPositions);
void TargetAnimationHelper::SetTargetAnimationChannel(
const std::vector<aiVectorKey> *_targetPositions) {
ai_assert(nullptr != _targetPositions);
targetPositions = _targetPositions;
}
// ------------------------------------------------------------------------------------------------
void TargetAnimationHelper::SetMainAnimationChannel (
const std::vector<aiVectorKey>* _objectPositions)
{
ai_assert(NULL != _objectPositions);
void TargetAnimationHelper::SetMainAnimationChannel(
const std::vector<aiVectorKey> *_objectPositions) {
ai_assert(nullptr != _objectPositions);
objectPositions = _objectPositions;
}
// ------------------------------------------------------------------------------------------------
void TargetAnimationHelper::SetFixedMainAnimationChannel(
const aiVector3D& fixed)
{
objectPositions = NULL; // just to avoid confusion
const aiVector3D &fixed) {
objectPositions = nullptr; // just to avoid confusion
fixedMain = fixed;
}
// ------------------------------------------------------------------------------------------------
void TargetAnimationHelper::Process(std::vector<aiVectorKey>* distanceTrack)
{
ai_assert(NULL != targetPositions && NULL != distanceTrack);
void TargetAnimationHelper::Process(std::vector<aiVectorKey> *distanceTrack) {
ai_assert(nullptr != targetPositions);
ai_assert(nullptr != distanceTrack);
// TODO: in most cases we won't need the extra array
std::vector<aiVectorKey> real;
std::vector<aiVectorKey> real;
std::vector<aiVectorKey>* fill = (distanceTrack == objectPositions ? &real : distanceTrack);
fill->reserve(std::max( objectPositions->size(), targetPositions->size() ));
std::vector<aiVectorKey> *fill = (distanceTrack == objectPositions ? &real : distanceTrack);
fill->reserve(std::max(objectPositions->size(), targetPositions->size()));
// Iterate through all object keys and interpolate their values if necessary.
// Then get the corresponding target position, compute the difference
@ -214,28 +196,24 @@ void TargetAnimationHelper::Process(std::vector<aiVectorKey>* distanceTrack)
// that rotates the base vector of the object coordinate system at that time
// to match the diff vector.
KeyIterator iter(objectPositions,targetPositions,&fixedMain);
for (;!iter.Finished();++iter)
{
const aiVector3D& position = iter.GetCurPosition();
const aiVector3D& tposition = iter.GetCurTargetPosition();
KeyIterator iter(objectPositions, targetPositions, &fixedMain);
for (; !iter.Finished(); ++iter) {
const aiVector3D &position = iter.GetCurPosition();
const aiVector3D &tposition = iter.GetCurTargetPosition();
// diff vector
aiVector3D diff = tposition - position;
ai_real f = diff.Length();
// output distance vector
if (f)
{
if (f) {
fill->push_back(aiVectorKey());
aiVectorKey& v = fill->back();
v.mTime = iter.GetCurTime();
aiVectorKey &v = fill->back();
v.mTime = iter.GetCurTime();
v.mValue = diff;
diff /= f;
}
else
{
} else {
// FIXME: handle this
}

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -48,9 +48,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/anim.h>
#include <vector>
namespace Assimp {
namespace Assimp {
// ---------------------------------------------------------------------------
/** Helper class to iterate through all keys in an animation channel.
@ -58,55 +56,44 @@ namespace Assimp {
* Missing tracks are interpolated. This is a helper class for
* TargetAnimationHelper, but it can be freely used for other purposes.
*/
class KeyIterator
{
class KeyIterator {
public:
// ------------------------------------------------------------------
/** Constructs a new key iterator
*
* @param _objPos Object position track. May be NULL.
* @param _targetObjPos Target object position track. May be NULL.
* @param _objPos Object position track. May be nullptr.
* @param _targetObjPos Target object position track. May be nullptr.
* @param defaultObjectPos Default object position to be used if
* no animated track is available. May be NULL.
* no animated track is available. May be nullptr.
* @param defaultTargetPos Default target position to be used if
* no animated track is available. May be NULL.
* no animated track is available. May be nullptr.
*/
KeyIterator(const std::vector<aiVectorKey>* _objPos,
const std::vector<aiVectorKey>* _targetObjPos,
const aiVector3D* defaultObjectPos = NULL,
const aiVector3D* defaultTargetPos = NULL);
KeyIterator(const std::vector<aiVectorKey> *_objPos,
const std::vector<aiVectorKey> *_targetObjPos,
const aiVector3D *defaultObjectPos = nullptr,
const aiVector3D *defaultTargetPos = nullptr);
// ------------------------------------------------------------------
/** Returns true if all keys have been processed
*/
bool Finished() const
{return reachedEnd;}
bool Finished() const { return reachedEnd; }
// ------------------------------------------------------------------
/** Increment the iterator
*/
void operator++();
inline void operator++(int)
{return ++(*this);}
inline void operator++(int) { return ++(*this); }
// ------------------------------------------------------------------
/** Getters to retrieve the current state of the iterator
*/
inline const aiVector3D& GetCurPosition() const
{return curPosition;}
inline const aiVector3D &GetCurPosition() const { return curPosition; }
inline const aiVector3D& GetCurTargetPosition() const
{return curTargetPosition;}
inline const aiVector3D &GetCurTargetPosition() const { return curTargetPosition; }
inline double GetCurTime() const
{return curTime;}
inline double GetCurTime() const { return curTime; }
private:
//! Did we reach the end?
bool reachedEnd;
@ -116,10 +103,10 @@ private:
double curTime;
//! Input tracks and the next key to process
const std::vector<aiVectorKey>* objPos,*targetObjPos;
const std::vector<aiVectorKey> *objPos, *targetObjPos;
unsigned int nextObjPos, nextTargetObjPos;
std::vector<aiVectorKey> defaultObjPos,defaultTargetObjPos;
std::vector<aiVectorKey> defaultObjPos, defaultTargetObjPos;
};
// ---------------------------------------------------------------------------
@ -130,15 +117,13 @@ private:
* channel for the camera/spot light itself and a separate position
* animation channels specifying the position of the camera/spot light
* look-at target */
class TargetAnimationHelper
{
class TargetAnimationHelper {
public:
TargetAnimationHelper()
: targetPositions (NULL)
, objectPositions (NULL)
{}
TargetAnimationHelper() :
targetPositions(nullptr),
objectPositions(nullptr) {
// empty
}
// ------------------------------------------------------------------
/** Sets the target animation channel
@ -147,37 +132,30 @@ public:
* target at a specific position.
*
* @param targetPositions Translation channel*/
void SetTargetAnimationChannel (const
std::vector<aiVectorKey>* targetPositions);
void SetTargetAnimationChannel(const std::vector<aiVectorKey> *targetPositions);
// ------------------------------------------------------------------
/** Sets the main animation channel
*
* @param objectPositions Translation channel */
void SetMainAnimationChannel ( const
std::vector<aiVectorKey>* objectPositions);
void SetMainAnimationChannel(const std::vector<aiVectorKey> *objectPositions);
// ------------------------------------------------------------------
/** Sets the main animation channel to a fixed value
*
* @param fixed Fixed value for the main animation channel*/
void SetFixedMainAnimationChannel(const aiVector3D& fixed);
void SetFixedMainAnimationChannel(const aiVector3D &fixed);
// ------------------------------------------------------------------
/** Computes final animation channels
* @param distanceTrack Receive camera translation keys ... != NULL. */
void Process( std::vector<aiVectorKey>* distanceTrack );
* @param distanceTrack Receive camera translation keys ... != nullptr. */
void Process(std::vector<aiVectorKey> *distanceTrack);
private:
const std::vector<aiVectorKey>* targetPositions,*objectPositions;
const std::vector<aiVectorKey> *targetPositions, *objectPositions;
aiVector3D fixedMain;
};
} // ! end namespace Assimp
} // namespace Assimp
#endif // include guard

View file

@ -3,8 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -40,48 +39,50 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
// Actually just a dummy, used by the compiler to build the precompiled header.
// Actually just a dummy, used by the compiler to build the pre-compiled header.
#include <assimp/version.h>
#include <assimp/scene.h>
#include "ScenePrivate.h"
#include <assimp/scene.h>
#include <assimp/version.h>
static const unsigned int MajorVersion = 5;
static const unsigned int MinorVersion = 0;
#include "revision.h"
// --------------------------------------------------------------------------------
// Legal information string - don't remove this.
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-2020, assimp team\n"
"License under the terms and conditions of the 3-clause BSD license\n"
"https://github.com/assimp/assimp\n"
;
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) 2006-2022, Assimp team\n"
"License under the terms and conditions of the 3-clause BSD license\n"
"https://www.assimp.org\n";
// ------------------------------------------------------------------------------------------------
// Get legal string
ASSIMP_API const char* aiGetLegalString () {
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;
ASSIMP_API unsigned int aiGetVersionMinor() {
return VER_MINOR;
}
// ------------------------------------------------------------------------------------------------
// Get Assimp major version
ASSIMP_API unsigned int aiGetVersionMajor () {
return MajorVersion;
ASSIMP_API unsigned int aiGetVersionMajor() {
return VER_MAJOR;
}
// ------------------------------------------------------------------------------------------------
// Get flags used for compilation
ASSIMP_API unsigned int aiGetCompileFlags () {
ASSIMP_API unsigned int aiGetCompileFlags() {
unsigned int flags = 0;
@ -100,41 +101,42 @@ ASSIMP_API unsigned int aiGetCompileFlags () {
#ifdef _STLPORT_VERSION
flags |= ASSIMP_CFLAGS_STLPORT;
#endif
#ifdef ASSIMP_DOUBLE_PRECISION
flags |= ASSIMP_CFLAGS_DOUBLE_SUPPORT;
#endif
return flags;
}
// include current build revision, which is even updated from time to time -- :-)
#include "revision.h"
// ------------------------------------------------------------------------------------------------
ASSIMP_API unsigned int aiGetVersionRevision() {
return GitVersion;
}
// ------------------------------------------------------------------------------------------------
ASSIMP_API const char *aiGetBranchName() {
return GitBranch;
}
// ------------------------------------------------------------------------------------------------
ASSIMP_API aiScene::aiScene()
: mFlags(0)
, mRootNode(nullptr)
, mNumMeshes(0)
, mMeshes(nullptr)
, mNumMaterials(0)
, mMaterials(nullptr)
, mNumAnimations(0)
, mAnimations(nullptr)
, mNumTextures(0)
, mTextures(nullptr)
, mNumLights(0)
, mLights(nullptr)
, mNumCameras(0)
, mCameras(nullptr)
, mMetaData(nullptr)
, mPrivate(new Assimp::ScenePrivateData()) {
// empty
ASSIMP_API aiScene::aiScene() :
mFlags(0),
mRootNode(nullptr),
mNumMeshes(0),
mMeshes(nullptr),
mNumMaterials(0),
mMaterials(nullptr),
mNumAnimations(0),
mAnimations(nullptr),
mNumTextures(0),
mTextures(nullptr),
mNumLights(0),
mLights(nullptr),
mNumCameras(0),
mCameras(nullptr),
mMetaData(nullptr),
mPrivate(new Assimp::ScenePrivateData()) {
// empty
}
// ------------------------------------------------------------------------------------------------
@ -146,40 +148,39 @@ ASSIMP_API aiScene::~aiScene() {
// much better to check whether both mNumXXX and mXXX are
// valid instead of relying on just one of them.
if (mNumMeshes && mMeshes)
for( unsigned int a = 0; a < mNumMeshes; a++)
for (unsigned int a = 0; a < mNumMeshes; a++)
delete mMeshes[a];
delete [] mMeshes;
delete[] mMeshes;
if (mNumMaterials && mMaterials) {
for (unsigned int a = 0; a < mNumMaterials; ++a ) {
delete mMaterials[ a ];
for (unsigned int a = 0; a < mNumMaterials; ++a) {
delete mMaterials[a];
}
}
delete [] mMaterials;
delete[] mMaterials;
if (mNumAnimations && mAnimations)
for( unsigned int a = 0; a < mNumAnimations; a++)
for (unsigned int a = 0; a < mNumAnimations; a++)
delete mAnimations[a];
delete [] mAnimations;
delete[] mAnimations;
if (mNumTextures && mTextures)
for( unsigned int a = 0; a < mNumTextures; a++)
for (unsigned int a = 0; a < mNumTextures; a++)
delete mTextures[a];
delete [] mTextures;
delete[] mTextures;
if (mNumLights && mLights)
for( unsigned int a = 0; a < mNumLights; a++)
for (unsigned int a = 0; a < mNumLights; a++)
delete mLights[a];
delete [] mLights;
delete[] mLights;
if (mNumCameras && mCameras)
for( unsigned int a = 0; a < mNumCameras; a++)
for (unsigned int a = 0; a < mNumCameras; a++)
delete mCameras[a];
delete [] mCameras;
delete[] mCameras;
aiMetadata::Dealloc(mMetaData);
mMetaData = nullptr;
delete static_cast<Assimp::ScenePrivateData*>( mPrivate );
delete static_cast<Assimp::ScenePrivateData *>(mPrivate);
}

View file

@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
@ -52,46 +52,44 @@ using namespace Assimp;
// ------------------------------------------------------------------------------------------------
VertexTriangleAdjacency::VertexTriangleAdjacency(aiFace *pcFaces,
unsigned int iNumFaces,
unsigned int iNumVertices /*= 0*/,
bool bComputeNumTriangles /*= false*/)
{
unsigned int iNumFaces,
unsigned int iNumVertices /*= 0*/,
bool bComputeNumTriangles /*= false*/) {
// compute the number of referenced vertices if it wasn't specified by the caller
const aiFace* const pcFaceEnd = pcFaces + iNumFaces;
if (!iNumVertices) {
for (aiFace* pcFace = pcFaces; pcFace != pcFaceEnd; ++pcFace) {
ai_assert( nullptr != pcFace );
const aiFace *const pcFaceEnd = pcFaces + iNumFaces;
if (0 == iNumVertices) {
for (aiFace *pcFace = pcFaces; pcFace != pcFaceEnd; ++pcFace) {
ai_assert(nullptr != pcFace);
ai_assert(3 == pcFace->mNumIndices);
iNumVertices = std::max(iNumVertices,pcFace->mIndices[0]);
iNumVertices = std::max(iNumVertices,pcFace->mIndices[1]);
iNumVertices = std::max(iNumVertices,pcFace->mIndices[2]);
iNumVertices = std::max(iNumVertices, pcFace->mIndices[0]);
iNumVertices = std::max(iNumVertices, pcFace->mIndices[1]);
iNumVertices = std::max(iNumVertices, pcFace->mIndices[2]);
}
}
mNumVertices = iNumVertices;
mNumVertices = iNumVertices + 1;
unsigned int* pi;
unsigned int *pi;
// allocate storage
if (bComputeNumTriangles) {
pi = mLiveTriangles = new unsigned int[iNumVertices+1];
::memset(mLiveTriangles,0,sizeof(unsigned int)*(iNumVertices+1));
mOffsetTable = new unsigned int[iNumVertices+2]+1;
if (bComputeNumTriangles) {
pi = mLiveTriangles = new unsigned int[iNumVertices + 1];
::memset(mLiveTriangles, 0, sizeof(unsigned int) * (iNumVertices + 1));
mOffsetTable = new unsigned int[iNumVertices + 2] + 1;
} else {
pi = mOffsetTable = new unsigned int[iNumVertices+2]+1;
::memset(mOffsetTable,0,sizeof(unsigned int)*(iNumVertices+1));
mLiveTriangles = NULL; // important, otherwise the d'tor would crash
pi = mOffsetTable = new unsigned int[iNumVertices + 2] + 1;
::memset(mOffsetTable, 0, sizeof(unsigned int) * (iNumVertices + 1));
mLiveTriangles = nullptr; // important, otherwise the d'tor would crash
}
// get a pointer to the end of the buffer
unsigned int* piEnd = pi+iNumVertices;
unsigned int *piEnd = pi + iNumVertices;
*piEnd++ = 0u;
// first pass: compute the number of faces referencing each vertex
for (aiFace* pcFace = pcFaces; pcFace != pcFaceEnd; ++pcFace)
{
for (aiFace *pcFace = pcFaces; pcFace != pcFaceEnd; ++pcFace) {
unsigned nind = pcFace->mNumIndices;
unsigned * ind = pcFace->mIndices;
unsigned *ind = pcFace->mIndices;
if (nind > 0) pi[ind[0]]++;
if (nind > 1) pi[ind[1]]++;
if (nind > 2) pi[ind[2]]++;
@ -99,8 +97,8 @@ VertexTriangleAdjacency::VertexTriangleAdjacency(aiFace *pcFaces,
// second pass: compute the final offset table
unsigned int iSum = 0;
unsigned int* piCurOut = this->mOffsetTable;
for (unsigned int* piCur = pi; piCur != piEnd;++piCur,++piCurOut) {
unsigned int *piCurOut = this->mOffsetTable;
for (unsigned int *piCur = pi; piCur != piEnd; ++piCur, ++piCurOut) {
unsigned int iLastSum = iSum;
iSum += *piCur;
@ -111,9 +109,9 @@ VertexTriangleAdjacency::VertexTriangleAdjacency(aiFace *pcFaces,
// third pass: compute the final table
this->mAdjacencyTable = new unsigned int[iSum];
iSum = 0;
for (aiFace* pcFace = pcFaces; pcFace != pcFaceEnd; ++pcFace,++iSum) {
for (aiFace *pcFace = pcFaces; pcFace != pcFaceEnd; ++pcFace, ++iSum) {
unsigned nind = pcFace->mNumIndices;
unsigned * ind = pcFace->mIndices;
unsigned *ind = pcFace->mIndices;
if (nind > 0) mAdjacencyTable[pi[ind[0]]++] = iSum;
if (nind > 1) mAdjacencyTable[pi[ind[1]]++] = iSum;
@ -125,8 +123,7 @@ VertexTriangleAdjacency::VertexTriangleAdjacency(aiFace *pcFaces,
*mOffsetTable = 0u;
}
// ------------------------------------------------------------------------------------------------
VertexTriangleAdjacency::~VertexTriangleAdjacency()
{
VertexTriangleAdjacency::~VertexTriangleAdjacency() {
// delete allocated storage
delete[] mOffsetTable;
delete[] mAdjacencyTable;

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.

View file

@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
@ -71,19 +71,19 @@ public:
};
// ---------------------------------------------------------------------------
inline
Win32DebugLogStream::Win32DebugLogStream(){
inline
Win32DebugLogStream::Win32DebugLogStream(){
// empty
}
// ---------------------------------------------------------------------------
inline
inline
Win32DebugLogStream::~Win32DebugLogStream(){
// empty
}
// ---------------------------------------------------------------------------
inline
inline
void Win32DebugLogStream::write(const char* message) {
::OutputDebugStringA( message);
}

View file

@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -44,8 +43,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* @brief Zip File I/O implementation for #Importer
*/
#include <assimp/ZipArchiveIOSystem.h>
#include <assimp/BaseImporter.h>
#include <assimp/ZipArchiveIOSystem.h>
#include <assimp/ai_assert.h>
@ -53,70 +52,118 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <memory>
#ifdef ASSIMP_USE_HUNTER
# include <minizip/unzip.h>
# include <minizip/unzip.h>
#else
# include <unzip.h>
# include <unzip.h>
#endif
namespace Assimp {
// ----------------------------------------------------------------
// Wraps an existing Assimp::IOSystem for unzip
class IOSystem2Unzip {
public:
static voidpf open(voidpf opaque, const char* filename, int mode);
static uLong read(voidpf opaque, voidpf stream, void* buf, uLong size);
static uLong write(voidpf opaque, voidpf stream, const void* buf, uLong size);
static long tell(voidpf opaque, voidpf stream);
static long seek(voidpf opaque, voidpf stream, uLong offset, int origin);
static int close(voidpf opaque, voidpf stream);
static int testerror(voidpf opaque, voidpf stream);
static zlib_filefunc_def get(IOSystem* pIOHandler);
};
voidpf IOSystem2Unzip::open(voidpf opaque, const char* filename, int mode) {
IOSystem* io_system = reinterpret_cast<IOSystem*>(opaque);
// ----------------------------------------------------------------
// A read-only file inside a ZIP
const char* mode_fopen = nullptr;
if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_READ) {
mode_fopen = "rb";
}
else {
if (mode & ZLIB_FILEFUNC_MODE_EXISTING) {
mode_fopen = "r+b";
}
else {
if (mode & ZLIB_FILEFUNC_MODE_CREATE) {
mode_fopen = "wb";
}
class ZipFile : public IOStream {
friend class ZipFileInfo;
explicit ZipFile(std::string &filename, size_t size);
public:
std::string m_Filename;
virtual ~ZipFile();
// IOStream interface
size_t Read(void *pvBuffer, size_t pSize, size_t pCount) override;
size_t Write(const void * /*pvBuffer*/, size_t /*pSize*/, size_t /*pCount*/) override { return 0; }
size_t FileSize() const override;
aiReturn Seek(size_t pOffset, aiOrigin pOrigin) override;
size_t Tell() const override;
void Flush() override {}
private:
size_t m_Size = 0;
size_t m_SeekPtr = 0;
std::unique_ptr<uint8_t[]> m_Buffer;
};
// ----------------------------------------------------------------
// Wraps an existing Assimp::IOSystem for unzip
class IOSystem2Unzip {
public:
static voidpf open(voidpf opaque, const char *filename, int mode);
static voidpf opendisk(voidpf opaque, voidpf stream, uint32_t number_disk, int mode);
static uLong read(voidpf opaque, voidpf stream, void *buf, uLong size);
static uLong write(voidpf opaque, voidpf stream, const void *buf, uLong size);
static long tell(voidpf opaque, voidpf stream);
static long seek(voidpf opaque, voidpf stream, uLong offset, int origin);
static int close(voidpf opaque, voidpf stream);
static int testerror(voidpf opaque, voidpf stream);
static zlib_filefunc_def get(IOSystem *pIOHandler);
};
voidpf IOSystem2Unzip::open(voidpf opaque, const char *filename, int mode) {
IOSystem *io_system = reinterpret_cast<IOSystem *>(opaque);
const char *mode_fopen = nullptr;
if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_READ) {
mode_fopen = "rb";
} else {
if (mode & ZLIB_FILEFUNC_MODE_EXISTING) {
mode_fopen = "r+b";
} else {
if (mode & ZLIB_FILEFUNC_MODE_CREATE) {
mode_fopen = "wb";
}
}
return (voidpf)io_system->Open(filename, mode_fopen);
}
uLong IOSystem2Unzip::read(voidpf /*opaque*/, voidpf stream, void* buf, uLong size) {
IOStream* io_stream = (IOStream*)stream;
return (voidpf)io_system->Open(filename, mode_fopen);
}
return static_cast<uLong>(io_stream->Read(buf, 1, size));
voidpf IOSystem2Unzip::opendisk(voidpf opaque, voidpf stream, uint32_t number_disk, int mode) {
ZipFile *io_stream = (ZipFile *)stream;
voidpf ret = NULL;
int i;
char *disk_filename = (char*)malloc(io_stream->m_Filename.length() + 1);
strncpy(disk_filename, io_stream->m_Filename.c_str(), io_stream->m_Filename.length() + 1);
for (i = (int)io_stream->m_Filename.length() - 1; i >= 0; i -= 1)
{
if (disk_filename[i] != '.')
continue;
snprintf(&disk_filename[i], io_stream->m_Filename.length() - size_t(i), ".z%02u", number_disk + 1);
break;
}
uLong IOSystem2Unzip::write(voidpf /*opaque*/, voidpf stream, const void* buf, uLong size) {
IOStream* io_stream = (IOStream*)stream;
if (i >= 0)
ret = open(opaque, disk_filename, mode);
return static_cast<uLong>(io_stream->Write(buf, 1, size));
}
free(disk_filename);
return ret;
}
long IOSystem2Unzip::tell(voidpf /*opaque*/, voidpf stream) {
IOStream* io_stream = (IOStream*)stream;
uLong IOSystem2Unzip::read(voidpf /*opaque*/, voidpf stream, void *buf, uLong size) {
IOStream *io_stream = (IOStream *)stream;
return static_cast<long>(io_stream->Tell());
}
return static_cast<uLong>(io_stream->Read(buf, 1, size));
}
long IOSystem2Unzip::seek(voidpf /*opaque*/, voidpf stream, uLong offset, int origin) {
IOStream* io_stream = (IOStream*)stream;
uLong IOSystem2Unzip::write(voidpf /*opaque*/, voidpf stream, const void *buf, uLong size) {
IOStream *io_stream = (IOStream *)stream;
aiOrigin assimp_origin;
switch (origin) {
return static_cast<uLong>(io_stream->Write(buf, 1, size));
}
long IOSystem2Unzip::tell(voidpf /*opaque*/, voidpf stream) {
IOStream *io_stream = (IOStream *)stream;
return static_cast<long>(io_stream->Tell());
}
long IOSystem2Unzip::seek(voidpf /*opaque*/, voidpf stream, uLong offset, int origin) {
IOStream *io_stream = (IOStream *)stream;
aiOrigin assimp_origin;
switch (origin) {
default:
case ZLIB_FILEFUNC_SEEK_CUR:
assimp_origin = aiOrigin_CUR;
@ -127,157 +174,142 @@ namespace Assimp {
case ZLIB_FILEFUNC_SEEK_SET:
assimp_origin = aiOrigin_SET;
break;
}
return (io_stream->Seek(offset, assimp_origin) == aiReturn_SUCCESS ? 0 : -1);
}
int IOSystem2Unzip::close(voidpf opaque, voidpf stream) {
IOSystem *io_system = (IOSystem *)opaque;
IOStream *io_stream = (IOStream *)stream;
io_system->Close(io_stream);
return 0;
}
int IOSystem2Unzip::testerror(voidpf /*opaque*/, voidpf /*stream*/) {
return 0;
}
zlib_filefunc_def IOSystem2Unzip::get(IOSystem *pIOHandler) {
zlib_filefunc_def mapping;
mapping.zopen_file = (open_file_func)open;
mapping.zopendisk_file = (opendisk_file_func)opendisk;
mapping.zread_file = (read_file_func)read;
mapping.zwrite_file = (write_file_func)write;
mapping.ztell_file = (tell_file_func)tell;
mapping.zseek_file = (seek_file_func)seek;
mapping.zclose_file = (close_file_func)close;
mapping.zerror_file = testerror;
mapping.opaque = reinterpret_cast<voidpf>(pIOHandler);
return mapping;
}
// ----------------------------------------------------------------
// Info about a read-only file inside a ZIP
class ZipFileInfo {
public:
explicit ZipFileInfo(unzFile zip_handle, size_t size);
// Allocate and Extract data from the ZIP
ZipFile *Extract(std::string &filename, unzFile zip_handle) const;
private:
size_t m_Size = 0;
unz_file_pos_s m_ZipFilePos;
};
ZipFileInfo::ZipFileInfo(unzFile zip_handle, size_t size) :
m_Size(size) {
ai_assert(m_Size != 0);
// Workaround for MSVC 2013 - C2797
m_ZipFilePos.num_of_file = 0;
m_ZipFilePos.pos_in_zip_directory = 0;
unzGetFilePos(zip_handle, &(m_ZipFilePos));
}
ZipFile *ZipFileInfo::Extract(std::string &filename, unzFile zip_handle) const {
// Find in the ZIP. This cannot fail
unz_file_pos_s *filepos = const_cast<unz_file_pos_s *>(&(m_ZipFilePos));
if (unzGoToFilePos(zip_handle, filepos) != UNZ_OK)
return nullptr;
if (unzOpenCurrentFile(zip_handle) != UNZ_OK)
return nullptr;
ZipFile *zip_file = new ZipFile(filename, m_Size);
// Unzip has a limit of UINT16_MAX bytes buffer
uint16_t unzipBufferSize = zip_file->m_Size <= UINT16_MAX ? static_cast<uint16_t>(zip_file->m_Size) : UINT16_MAX;
std::unique_ptr<uint8_t[]> unzipBuffer = std::unique_ptr<uint8_t[]>(new uint8_t[unzipBufferSize]);
size_t readCount = 0;
while (readCount < zip_file->m_Size)
{
size_t bufferSize = zip_file->m_Size - readCount;
if (bufferSize > UINT16_MAX) {
bufferSize = UINT16_MAX;
}
return (io_stream->Seek(offset, assimp_origin) == aiReturn_SUCCESS ? 0 : -1);
}
int IOSystem2Unzip::close(voidpf opaque, voidpf stream) {
IOSystem* io_system = (IOSystem*)opaque;
IOStream* io_stream = (IOStream*)stream;
io_system->Close(io_stream);
return 0;
}
int IOSystem2Unzip::testerror(voidpf /*opaque*/, voidpf /*stream*/) {
return 0;
}
zlib_filefunc_def IOSystem2Unzip::get(IOSystem* pIOHandler) {
zlib_filefunc_def mapping;
#ifdef ASSIMP_USE_HUNTER
mapping.zopen_file = (open_file_func)open;
mapping.zread_file = (read_file_func)read;
mapping.zwrite_file = (write_file_func)write;
mapping.ztell_file = (tell_file_func)tell;
mapping.zseek_file = (seek_file_func)seek;
mapping.zclose_file = (close_file_func)close;
mapping.zerror_file = (error_file_func)testerror;
#else
mapping.zopen_file = open;
mapping.zread_file = read;
mapping.zwrite_file = write;
mapping.ztell_file = tell;
mapping.zseek_file = seek;
mapping.zclose_file = close;
mapping.zerror_file = testerror;
#endif
mapping.opaque = reinterpret_cast<voidpf>(pIOHandler);
return mapping;
}
// ----------------------------------------------------------------
// A read-only file inside a ZIP
class ZipFile : public IOStream {
friend class ZipFileInfo;
explicit ZipFile(size_t size);
public:
virtual ~ZipFile();
// IOStream interface
size_t Read(void* pvBuffer, size_t pSize, size_t pCount) override;
size_t Write(const void* /*pvBuffer*/, size_t /*pSize*/, size_t /*pCount*/) override { return 0; }
size_t FileSize() const override;
aiReturn Seek(size_t pOffset, aiOrigin pOrigin) override;
size_t Tell() const override;
void Flush() override {}
private:
size_t m_Size = 0;
size_t m_SeekPtr = 0;
std::unique_ptr<uint8_t[]> m_Buffer;
};
// ----------------------------------------------------------------
// Info about a read-only file inside a ZIP
class ZipFileInfo
{
public:
explicit ZipFileInfo(unzFile zip_handle, size_t size);
// Allocate and Extract data from the ZIP
ZipFile * Extract(unzFile zip_handle) const;
private:
size_t m_Size = 0;
unz_file_pos_s m_ZipFilePos;
};
ZipFileInfo::ZipFileInfo(unzFile zip_handle, size_t size)
: m_Size(size) {
ai_assert(m_Size != 0);
// Workaround for MSVC 2013 - C2797
m_ZipFilePos.num_of_file = 0;
m_ZipFilePos.pos_in_zip_directory = 0;
unzGetFilePos(zip_handle, &(m_ZipFilePos));
}
ZipFile * ZipFileInfo::Extract(unzFile zip_handle) const {
// Find in the ZIP. This cannot fail
unz_file_pos_s *filepos = const_cast<unz_file_pos_s*>(&(m_ZipFilePos));
if (unzGoToFilePos(zip_handle, filepos) != UNZ_OK)
return nullptr;
if (unzOpenCurrentFile(zip_handle) != UNZ_OK)
return nullptr;
ZipFile *zip_file = new ZipFile(m_Size);
if (unzReadCurrentFile(zip_handle, zip_file->m_Buffer.get(), static_cast<unsigned int>(m_Size)) != static_cast<int>(m_Size))
int ret = unzReadCurrentFile(zip_handle, unzipBuffer.get(), static_cast<unsigned int>(bufferSize));
if (ret != static_cast<int>(bufferSize))
{
// Failed, release the memory
delete zip_file;
zip_file = nullptr;
break;
}
ai_assert(unzCloseCurrentFile(zip_handle) == UNZ_OK);
return zip_file;
std::memcpy(zip_file->m_Buffer.get() + readCount, unzipBuffer.get(), ret);
readCount += ret;
}
ZipFile::ZipFile(size_t size)
: m_Size(size) {
ai_assert(m_Size != 0);
m_Buffer = std::unique_ptr<uint8_t[]>(new uint8_t[m_Size]);
}
ai_assert(unzCloseCurrentFile(zip_handle) == UNZ_OK);
return zip_file;
}
ZipFile::~ZipFile() {
}
ZipFile::ZipFile(std::string &filename, size_t size) :
m_Filename(filename), m_Size(size) {
ai_assert(m_Size != 0);
m_Buffer = std::unique_ptr<uint8_t[]>(new uint8_t[m_Size]);
}
size_t ZipFile::Read(void* pvBuffer, size_t pSize, size_t pCount) {
// Should be impossible
ai_assert(m_Buffer != nullptr);
ai_assert(NULL != pvBuffer && 0 != pSize && 0 != pCount);
ZipFile::~ZipFile() {
}
// Clip down to file size
size_t byteSize = pSize * pCount;
if ((byteSize + m_SeekPtr) > m_Size)
{
pCount = (m_Size - m_SeekPtr) / pSize;
byteSize = pSize * pCount;
if (byteSize == 0)
return 0;
size_t ZipFile::Read(void *pvBuffer, size_t pSize, size_t pCount) {
// Should be impossible
ai_assert(m_Buffer != nullptr);
ai_assert(nullptr != pvBuffer);
ai_assert(0 != pSize);
ai_assert(0 != pCount);
// Clip down to file size
size_t byteSize = pSize * pCount;
if ((byteSize + m_SeekPtr) > m_Size) {
pCount = (m_Size - m_SeekPtr) / pSize;
byteSize = pSize * pCount;
if (byteSize == 0) {
return 0;
}
std::memcpy(pvBuffer, m_Buffer.get() + m_SeekPtr, byteSize);
m_SeekPtr += byteSize;
return pCount;
}
size_t ZipFile::FileSize() const {
return m_Size;
}
std::memcpy(pvBuffer, m_Buffer.get() + m_SeekPtr, byteSize);
aiReturn ZipFile::Seek(size_t pOffset, aiOrigin pOrigin) {
switch (pOrigin)
{
m_SeekPtr += byteSize;
return pCount;
}
size_t ZipFile::FileSize() const {
return m_Size;
}
aiReturn ZipFile::Seek(size_t pOffset, aiOrigin pOrigin) {
switch (pOrigin) {
case aiOrigin_SET: {
if (pOffset > m_Size) return aiReturn_FAILURE;
m_SeekPtr = pOffset;
@ -296,244 +328,236 @@ namespace Assimp {
return aiReturn_SUCCESS;
}
default:;
}
return aiReturn_FAILURE;
}
size_t ZipFile::Tell() const {
return m_SeekPtr;
}
// ----------------------------------------------------------------
// pImpl of the Zip Archive IO
class ZipArchiveIOSystem::Implement {
public:
static const unsigned int FileNameSize = 256;
Implement(IOSystem* pIOHandler, const char* pFilename, const char* pMode);
~Implement();
bool isOpen() const;
void getFileList(std::vector<std::string>& rFileList);
void getFileListExtension(std::vector<std::string>& rFileList, const std::string& extension);
bool Exists(std::string& filename);
IOStream* OpenFile(std::string& filename);
static void SimplifyFilename(std::string& filename);
private:
void MapArchive();
private:
typedef std::map<std::string, ZipFileInfo> ZipFileInfoMap;
unzFile m_ZipFileHandle = nullptr;
ZipFileInfoMap m_ArchiveMap;
};
ZipArchiveIOSystem::Implement::Implement(IOSystem* pIOHandler, const char* pFilename, const char* pMode) {
ai_assert(strcmp(pMode, "r") == 0);
ai_assert(pFilename != nullptr);
if (pFilename[0] == 0)
return;
zlib_filefunc_def mapping = IOSystem2Unzip::get(pIOHandler);
m_ZipFileHandle = unzOpen2(pFilename, &mapping);
}
ZipArchiveIOSystem::Implement::~Implement() {
m_ArchiveMap.clear();
if (m_ZipFileHandle != nullptr) {
unzClose(m_ZipFileHandle);
m_ZipFileHandle = nullptr;
}
}
void ZipArchiveIOSystem::Implement::MapArchive() {
if (m_ZipFileHandle == nullptr)
return;
if (!m_ArchiveMap.empty())
return;
// At first ensure file is already open
if (unzGoToFirstFile(m_ZipFileHandle) != UNZ_OK)
return;
// Loop over all files
do {
char filename[FileNameSize];
unz_file_info fileInfo;
if (unzGetCurrentFileInfo(m_ZipFileHandle, &fileInfo, filename, FileNameSize, nullptr, 0, nullptr, 0) == UNZ_OK) {
if (fileInfo.uncompressed_size != 0) {
std::string filename_string(filename, fileInfo.size_filename);
SimplifyFilename(filename_string);
m_ArchiveMap.emplace(filename_string, ZipFileInfo(m_ZipFileHandle, fileInfo.uncompressed_size));
}
}
} while (unzGoToNextFile(m_ZipFileHandle) != UNZ_END_OF_LIST_OF_FILE);
}
bool ZipArchiveIOSystem::Implement::isOpen() const {
return (m_ZipFileHandle != nullptr);
}
void ZipArchiveIOSystem::Implement::getFileList(std::vector<std::string>& rFileList) {
MapArchive();
rFileList.clear();
for (const auto &file : m_ArchiveMap) {
rFileList.push_back(file.first);
}
}
void ZipArchiveIOSystem::Implement::getFileListExtension(std::vector<std::string>& rFileList, const std::string& extension) {
MapArchive();
rFileList.clear();
for (const auto &file : m_ArchiveMap) {
if (extension == BaseImporter::GetExtension(file.first))
rFileList.push_back(file.first);
}
}
bool ZipArchiveIOSystem::Implement::Exists(std::string& filename) {
MapArchive();
ZipFileInfoMap::const_iterator it = m_ArchiveMap.find(filename);
return (it != m_ArchiveMap.end());
}
IOStream * ZipArchiveIOSystem::Implement::OpenFile(std::string& filename) {
MapArchive();
SimplifyFilename(filename);
// Find in the map
ZipFileInfoMap::const_iterator zip_it = m_ArchiveMap.find(filename);
if (zip_it == m_ArchiveMap.cend())
return nullptr;
const ZipFileInfo &zip_file = (*zip_it).second;
return zip_file.Extract(m_ZipFileHandle);
}
inline void ReplaceAll(std::string& data, const std::string& before, const std::string& after) {
size_t pos = data.find(before);
while (pos != std::string::npos)
{
data.replace(pos, before.size(), after);
pos = data.find(before, pos + after.size());
}
}
inline void ReplaceAllChar(std::string& data, const char before, const char after) {
size_t pos = data.find(before);
while (pos != std::string::npos)
{
data[pos] = after;
pos = data.find(before, pos + 1);
}
}
void ZipArchiveIOSystem::Implement::SimplifyFilename(std::string& filename)
{
ReplaceAllChar(filename, '\\', '/');
// Remove all . and / from the beginning of the path
size_t pos = filename.find_first_not_of("./");
if (pos != 0)
filename.erase(0, pos);
// Simplify "my/folder/../file.png" constructions, if any
static const std::string relative("/../");
const size_t relsize = relative.size() - 1;
pos = filename.find(relative);
while (pos != std::string::npos)
{
// Previous slash
size_t prevpos = filename.rfind('/', pos - 1);
if (prevpos == pos)
filename.erase(0, pos + relative.size());
else
filename.erase(prevpos, pos + relsize - prevpos);
pos = filename.find(relative);
}
}
ZipArchiveIOSystem::ZipArchiveIOSystem(IOSystem* pIOHandler, const char* pFilename, const char* pMode)
: pImpl(new Implement(pIOHandler, pFilename, pMode)) {
}
// ----------------------------------------------------------------
// The ZipArchiveIO
ZipArchiveIOSystem::ZipArchiveIOSystem(IOSystem* pIOHandler, const std::string& rFilename, const char* pMode)
: pImpl(new Implement(pIOHandler, rFilename.c_str(), pMode))
{
}
ZipArchiveIOSystem::~ZipArchiveIOSystem() {
delete pImpl;
}
bool ZipArchiveIOSystem::Exists(const char* pFilename) const {
ai_assert(pFilename != nullptr);
if (pFilename == nullptr) {
return false;
}
std::string filename(pFilename);
return pImpl->Exists(filename);
}
// This is always '/' in a ZIP
char ZipArchiveIOSystem::getOsSeparator() const {
return '/';
}
// Only supports Reading
IOStream * ZipArchiveIOSystem::Open(const char* pFilename, const char* pMode) {
ai_assert(pFilename != nullptr);
for (size_t i = 0; pMode[i] != 0; ++i)
{
ai_assert(pMode[i] != 'w');
if (pMode[i] == 'w')
return nullptr;
}
std::string filename(pFilename);
return pImpl->OpenFile(filename);
}
void ZipArchiveIOSystem::Close(IOStream* pFile) {
delete pFile;
}
bool ZipArchiveIOSystem::isOpen() const {
return (pImpl->isOpen());
}
void ZipArchiveIOSystem::getFileList(std::vector<std::string>& rFileList) const {
return pImpl->getFileList(rFileList);
}
void ZipArchiveIOSystem::getFileListExtension(std::vector<std::string>& rFileList, const std::string& extension) const {
return pImpl->getFileListExtension(rFileList, extension);
}
bool ZipArchiveIOSystem::isZipArchive(IOSystem* pIOHandler, const char* pFilename) {
Implement tmp(pIOHandler, pFilename, "r");
return tmp.isOpen();
}
bool ZipArchiveIOSystem::isZipArchive(IOSystem* pIOHandler, const std::string& rFilename) {
return isZipArchive(pIOHandler, rFilename.c_str());
}
return aiReturn_FAILURE;
}
size_t ZipFile::Tell() const {
return m_SeekPtr;
}
// ----------------------------------------------------------------
// pImpl of the Zip Archive IO
class ZipArchiveIOSystem::Implement {
public:
static const unsigned int FileNameSize = 256;
Implement(IOSystem *pIOHandler, const char *pFilename, const char *pMode);
~Implement();
bool isOpen() const;
void getFileList(std::vector<std::string> &rFileList);
void getFileListExtension(std::vector<std::string> &rFileList, const std::string &extension);
bool Exists(std::string &filename);
IOStream *OpenFile(std::string &filename);
static void SimplifyFilename(std::string &filename);
private:
void MapArchive();
private:
typedef std::map<std::string, ZipFileInfo> ZipFileInfoMap;
unzFile m_ZipFileHandle = nullptr;
ZipFileInfoMap m_ArchiveMap;
};
ZipArchiveIOSystem::Implement::Implement(IOSystem *pIOHandler, const char *pFilename, const char *pMode) {
ai_assert(strcmp(pMode, "r") == 0);
ai_assert(pFilename != nullptr);
if (pFilename[0] == 0 || nullptr == pMode) {
return;
}
zlib_filefunc_def mapping = IOSystem2Unzip::get(pIOHandler);
m_ZipFileHandle = unzOpen2(pFilename, &mapping);
}
ZipArchiveIOSystem::Implement::~Implement() {
if (m_ZipFileHandle != nullptr) {
unzClose(m_ZipFileHandle);
}
}
void ZipArchiveIOSystem::Implement::MapArchive() {
if (m_ZipFileHandle == nullptr)
return;
if (!m_ArchiveMap.empty())
return;
// At first ensure file is already open
if (unzGoToFirstFile(m_ZipFileHandle) != UNZ_OK)
return;
// Loop over all files
do {
char filename[FileNameSize];
unz_file_info fileInfo;
if (unzGetCurrentFileInfo(m_ZipFileHandle, &fileInfo, filename, FileNameSize, nullptr, 0, nullptr, 0) == UNZ_OK) {
if (fileInfo.uncompressed_size != 0 && fileInfo.size_filename <= FileNameSize) {
std::string filename_string(filename, fileInfo.size_filename);
SimplifyFilename(filename_string);
m_ArchiveMap.emplace(filename_string, ZipFileInfo(m_ZipFileHandle, fileInfo.uncompressed_size));
}
}
} while (unzGoToNextFile(m_ZipFileHandle) != UNZ_END_OF_LIST_OF_FILE);
}
bool ZipArchiveIOSystem::Implement::isOpen() const {
return (m_ZipFileHandle != nullptr);
}
void ZipArchiveIOSystem::Implement::getFileList(std::vector<std::string> &rFileList) {
MapArchive();
rFileList.clear();
for (const auto &file : m_ArchiveMap) {
rFileList.push_back(file.first);
}
}
void ZipArchiveIOSystem::Implement::getFileListExtension(std::vector<std::string> &rFileList, const std::string &extension) {
MapArchive();
rFileList.clear();
for (const auto &file : m_ArchiveMap) {
if (extension == BaseImporter::GetExtension(file.first))
rFileList.push_back(file.first);
}
}
bool ZipArchiveIOSystem::Implement::Exists(std::string &filename) {
MapArchive();
ZipFileInfoMap::const_iterator it = m_ArchiveMap.find(filename);
return (it != m_ArchiveMap.end());
}
IOStream *ZipArchiveIOSystem::Implement::OpenFile(std::string &filename) {
MapArchive();
SimplifyFilename(filename);
// Find in the map
ZipFileInfoMap::const_iterator zip_it = m_ArchiveMap.find(filename);
if (zip_it == m_ArchiveMap.cend())
return nullptr;
const ZipFileInfo &zip_file = (*zip_it).second;
return zip_file.Extract(filename, m_ZipFileHandle);
}
inline void ReplaceAll(std::string &data, const std::string &before, const std::string &after) {
size_t pos = data.find(before);
while (pos != std::string::npos) {
data.replace(pos, before.size(), after);
pos = data.find(before, pos + after.size());
}
}
inline void ReplaceAllChar(std::string &data, const char before, const char after) {
size_t pos = data.find(before);
while (pos != std::string::npos) {
data[pos] = after;
pos = data.find(before, pos + 1);
}
}
void ZipArchiveIOSystem::Implement::SimplifyFilename(std::string &filename) {
ReplaceAllChar(filename, '\\', '/');
// Remove all . and / from the beginning of the path
size_t pos = filename.find_first_not_of("./");
if (pos != 0)
filename.erase(0, pos);
// Simplify "my/folder/../file.png" constructions, if any
static const std::string relative("/../");
const size_t relsize = relative.size() - 1;
pos = filename.find(relative);
while (pos != std::string::npos) {
// Previous slash
size_t prevpos = filename.rfind('/', pos - 1);
if (prevpos == pos)
filename.erase(0, pos + relative.size());
else
filename.erase(prevpos, pos + relsize - prevpos);
pos = filename.find(relative);
}
}
ZipArchiveIOSystem::ZipArchiveIOSystem(IOSystem *pIOHandler, const char *pFilename, const char *pMode) :
pImpl(new Implement(pIOHandler, pFilename, pMode)) {
}
// ----------------------------------------------------------------
// The ZipArchiveIO
ZipArchiveIOSystem::ZipArchiveIOSystem(IOSystem *pIOHandler, const std::string &rFilename, const char *pMode) :
pImpl(new Implement(pIOHandler, rFilename.c_str(), pMode)) {
}
ZipArchiveIOSystem::~ZipArchiveIOSystem() {
delete pImpl;
}
bool ZipArchiveIOSystem::Exists(const char *pFilename) const {
ai_assert(pFilename != nullptr);
if (pFilename == nullptr) {
return false;
}
std::string filename(pFilename);
return pImpl->Exists(filename);
}
// This is always '/' in a ZIP
char ZipArchiveIOSystem::getOsSeparator() const {
return '/';
}
// Only supports Reading
IOStream *ZipArchiveIOSystem::Open(const char *pFilename, const char *pMode) {
ai_assert(pFilename != nullptr);
for (size_t i = 0; pMode[i] != 0; ++i) {
ai_assert(pMode[i] != 'w');
if (pMode[i] == 'w')
return nullptr;
}
std::string filename(pFilename);
return pImpl->OpenFile(filename);
}
void ZipArchiveIOSystem::Close(IOStream *pFile) {
delete pFile;
}
bool ZipArchiveIOSystem::isOpen() const {
return (pImpl->isOpen());
}
void ZipArchiveIOSystem::getFileList(std::vector<std::string> &rFileList) const {
return pImpl->getFileList(rFileList);
}
void ZipArchiveIOSystem::getFileListExtension(std::vector<std::string> &rFileList, const std::string &extension) const {
return pImpl->getFileListExtension(rFileList, extension);
}
bool ZipArchiveIOSystem::isZipArchive(IOSystem *pIOHandler, const char *pFilename) {
Implement tmp(pIOHandler, pFilename, "r");
return tmp.isOpen();
}
bool ZipArchiveIOSystem::isZipArchive(IOSystem *pIOHandler, const std::string &rFilename) {
return isZipArchive(pIOHandler, rFilename.c_str());
}
} // namespace Assimp

View file

@ -37,7 +37,7 @@ The ASSBIN file format is composed of chunks to represent the hierarchical aiSce
This makes the format extensible and allows backward-compatibility with future data structure
versions. The <tt>&lt;root&gt;/code/assbin_chunks.h</tt> header contains some magic constants
for use by stand-alone ASSBIN loaders. Also, Assimp's own file writer can be found
in <tt>&lt;root&gt;/tools/assimp_cmd/WriteDumb.cpp</tt> (yes, the 'b' is no typo ...).
in <tt>&lt;root&gt;/tools/assimp_cmd/WriteDump.cpp</tt> (yes, the 'b' is no typo ...).
@verbatim

View file

@ -0,0 +1,100 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/// @file material.cpp
/** Implement common material related functions. */
#include <assimp/ai_assert.h>
#include <assimp/material.h>
// -------------------------------------------------------------------------------
const char *TextureTypeToString(aiTextureType in) {
switch (in) {
case aiTextureType_NONE:
return "n/a";
case aiTextureType_DIFFUSE:
return "Diffuse";
case aiTextureType_SPECULAR:
return "Specular";
case aiTextureType_AMBIENT:
return "Ambient";
case aiTextureType_EMISSIVE:
return "Emissive";
case aiTextureType_OPACITY:
return "Opacity";
case aiTextureType_NORMALS:
return "Normals";
case aiTextureType_HEIGHT:
return "Height";
case aiTextureType_SHININESS:
return "Shininess";
case aiTextureType_DISPLACEMENT:
return "Displacement";
case aiTextureType_LIGHTMAP:
return "Lightmap";
case aiTextureType_REFLECTION:
return "Reflection";
case aiTextureType_BASE_COLOR:
return "BaseColor";
case aiTextureType_NORMAL_CAMERA:
return "NormalCamera";
case aiTextureType_EMISSION_COLOR:
return "EmissionColor";
case aiTextureType_METALNESS:
return "Metalness";
case aiTextureType_DIFFUSE_ROUGHNESS:
return "DiffuseRoughness";
case aiTextureType_AMBIENT_OCCLUSION:
return "AmbientOcclusion";
case aiTextureType_SHEEN:
return "Sheen";
case aiTextureType_CLEARCOAT:
return "Clearcoat";
case aiTextureType_TRANSMISSION:
return "Transmission";
case aiTextureType_UNKNOWN:
return "Unknown";
default:
break;
}
ai_assert(false);
return "BUG";
}

View file

@ -3,9 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.
@ -42,25 +40,25 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <assimp/scene.h>
aiNode::aiNode()
: mName("")
, mParent(NULL)
, mNumChildren(0)
, mChildren(NULL)
, mNumMeshes(0)
, mMeshes(NULL)
, mMetaData(NULL) {
aiNode::aiNode() :
mName(""),
mParent(nullptr),
mNumChildren(0),
mChildren(nullptr),
mNumMeshes(0),
mMeshes(nullptr),
mMetaData(nullptr) {
// empty
}
aiNode::aiNode(const std::string& name)
: mName(name)
, mParent(NULL)
, mNumChildren(0)
, mChildren(NULL)
, mNumMeshes(0)
, mMeshes(NULL)
, mMetaData(NULL) {
aiNode::aiNode(const std::string &name) :
mName(name),
mParent(nullptr),
mNumChildren(0),
mChildren(nullptr),
mNumMeshes(0),
mMeshes(nullptr),
mMetaData(nullptr) {
// empty
}
@ -68,8 +66,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];
}
@ -78,7 +75,7 @@ aiNode::~aiNode() {
delete mMetaData;
}
const aiNode *aiNode::FindNode(const char* name) const {
const aiNode *aiNode::FindNode(const char *name) const {
if (nullptr == name) {
return nullptr;
}
@ -86,7 +83,7 @@ const aiNode *aiNode::FindNode(const char* name) const {
return this;
}
for (unsigned int i = 0; i < mNumChildren; ++i) {
const aiNode* const p = mChildren[i]->FindNode(name);
const aiNode *const p = mChildren[i]->FindNode(name);
if (p) {
return p;
}
@ -95,11 +92,10 @@ const aiNode *aiNode::FindNode(const char* name) const {
return nullptr;
}
aiNode *aiNode::FindNode(const char* name) {
if (!::strcmp(mName.data, name))return this;
for (unsigned int i = 0; i < mNumChildren; ++i)
{
aiNode* const p = mChildren[i]->FindNode(name);
aiNode *aiNode::FindNode(const char *name) {
if (!::strcmp(mName.data, name)) return this;
for (unsigned int i = 0; i < mNumChildren; ++i) {
aiNode *const p = mChildren[i]->FindNode(name);
if (p) {
return p;
}
@ -121,17 +117,16 @@ void aiNode::addChildren(unsigned int numChildren, aiNode **children) {
}
if (mNumChildren > 0) {
aiNode **tmp = new aiNode*[mNumChildren];
::memcpy(tmp, mChildren, sizeof(aiNode*) * mNumChildren);
aiNode **tmp = new aiNode *[mNumChildren];
::memcpy(tmp, mChildren, sizeof(aiNode *) * mNumChildren);
delete[] mChildren;
mChildren = new aiNode*[mNumChildren + numChildren];
::memcpy(mChildren, tmp, sizeof(aiNode*) * mNumChildren);
::memcpy(&mChildren[mNumChildren], children, sizeof(aiNode*)* numChildren);
mChildren = new aiNode *[mNumChildren + numChildren];
::memcpy(mChildren, tmp, sizeof(aiNode *) * mNumChildren);
::memcpy(&mChildren[mNumChildren], children, sizeof(aiNode *) * numChildren);
mNumChildren += numChildren;
delete[] tmp;
}
else {
mChildren = new aiNode*[numChildren];
} else {
mChildren = new aiNode *[numChildren];
for (unsigned int i = 0; i < numChildren; i++) {
mChildren[i] = children[i];
}

View file

@ -3,9 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team
All rights reserved.

View file

@ -3,7 +3,7 @@
Open Asset Import Library (assimp)
---------------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team
Copyright (c) 2006-2022, assimp team