Merge branch 'master' into master

pull/4783/head
Kim Kulling 2023-09-02 10:52:22 +02:00 committed by GitHub
commit 27fcdc8c7b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
253 changed files with 24685 additions and 12265 deletions

View File

@ -46,7 +46,7 @@ jobs:
CC: clang CC: clang
- name: configure and build - name: configure and build
uses: lukka/run-cmake@v2 uses: lukka/run-cmake@v3
with: with:
cmakeListsOrSettingsJson: CMakeListsTxtAdvanced cmakeListsOrSettingsJson: CMakeListsTxtAdvanced
cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt' cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt'

View File

@ -49,8 +49,8 @@ option(ASSIMP_HUNTER_ENABLED "Enable Hunter package manager support" OFF)
IF(ASSIMP_HUNTER_ENABLED) IF(ASSIMP_HUNTER_ENABLED)
include("cmake-modules/HunterGate.cmake") include("cmake-modules/HunterGate.cmake")
HunterGate( HunterGate(
URL "https://github.com/cpp-pm/hunter/archive/v0.24.0.tar.gz" URL "https://github.com/cpp-pm/hunter/archive/v0.24.17.tar.gz"
SHA1 "a3d7f4372b1dcd52faa6ff4a3bd5358e1d0e5efd" SHA1 "e6396699e414120e32557fe92db097b7655b760b"
) )
add_definitions(-DASSIMP_USE_HUNTER) add_definitions(-DASSIMP_USE_HUNTER)
@ -268,6 +268,11 @@ IF ((CMAKE_C_COMPILER_ID MATCHES "GNU") AND NOT MINGW)
SET(CMAKE_CXX_STANDARD 17) SET(CMAKE_CXX_STANDARD 17)
SET(CMAKE_POSITION_INDEPENDENT_CODE ON) SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
ENDIF() ENDIF()
IF(CMAKE_CXX_COMPILER_VERSION GREATER_EQUAL 13)
MESSAGE(STATUS "GCC13 detected disabling \"-Wdangling-reference\" in Cpp files as it appears to be a false positive")
ADD_COMPILE_OPTIONS("$<$<COMPILE_LANGUAGE:CXX>:-Wno-dangling-reference>")
ENDIF()
# hide all not-exported symbols # hide all not-exported symbols
IF(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "mips64" ) IF(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "mips64" )
SET(CMAKE_CXX_FLAGS "-mxgot -fvisibility=hidden -fno-strict-aliasing -Wall ${CMAKE_CXX_FLAGS}") SET(CMAKE_CXX_FLAGS "-mxgot -fvisibility=hidden -fno-strict-aliasing -Wall ${CMAKE_CXX_FLAGS}")
@ -281,9 +286,9 @@ IF ((CMAKE_C_COMPILER_ID MATCHES "GNU") AND NOT MINGW)
ELSEIF(MSVC) ELSEIF(MSVC)
# enable multi-core compilation with MSVC # enable multi-core compilation with MSVC
IF(CMAKE_CXX_COMPILER_ID MATCHES "Clang" ) # clang-cl IF(CMAKE_CXX_COMPILER_ID MATCHES "Clang" ) # clang-cl
ADD_COMPILE_OPTIONS(/bigobj /W4 /WX ) ADD_COMPILE_OPTIONS(/bigobj)
ELSE() # msvc ELSE() # msvc
ADD_COMPILE_OPTIONS(/MP /bigobj /W4 /WX) ADD_COMPILE_OPTIONS(/MP /bigobj)
ENDIF() ENDIF()
# disable "elements of array '' will be default initialized" warning on MSVC2013 # disable "elements of array '' will be default initialized" warning on MSVC2013

View File

@ -1,14 +1,9 @@
FROM ubuntu:14.04 FROM ubuntu:22.04
RUN apt-get update && apt-get install -y \ RUN apt-get update && apt-get install -y ninja-build \
git cmake build-essential software-properties-common git cmake build-essential software-properties-common
RUN add-apt-repository ppa:ubuntu-toolchain-r/test && apt-get update && apt-get install -y gcc-4.9 g++-4.9 && \ RUN add-apt-repository ppa:ubuntu-toolchain-r/test && apt-get update
cd /usr/bin && \
rm gcc g++ cpp && \
ln -s gcc-4.9 gcc && \
ln -s g++-4.9 g++ && \
ln -s cpp-4.9 cpp
WORKDIR /opt WORKDIR /opt
@ -19,7 +14,8 @@ WORKDIR /opt/assimp
RUN git checkout master \ RUN git checkout master \
&& mkdir build && cd build && \ && mkdir build && cd build && \
cmake \ cmake -G 'Ninja' \
-DCMAKE_BUILD_TYPE=Release \ -DCMAKE_BUILD_TYPE=Release \
-DASSIMP_BUILD_ASSIMP_TOOLS=ON \
.. && \ .. && \
make && make install ninja -j4 && ninja install

View File

@ -397,10 +397,6 @@ struct Material {
Material(const Material &other) = default; Material(const Material &other) = default;
Material(Material &&other) AI_NO_EXCEPT = default;
Material &operator=(Material &&other) AI_NO_EXCEPT = default;
virtual ~Material() = default; virtual ~Material() = default;
//! Name of the material //! Name of the material

View File

@ -44,7 +44,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#ifndef ASSIMP_BUILD_NO_ASE_IMPORTER #ifndef ASSIMP_BUILD_NO_ASE_IMPORTER
#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER #ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
// internal headers // internal headers
@ -322,21 +321,6 @@ void ASEImporter::BuildAnimations(const std::vector<BaseNode *> &nodes) {
aiNodeAnim *nd = pcAnim->mChannels[iNum++] = new aiNodeAnim(); aiNodeAnim *nd = pcAnim->mChannels[iNum++] = new aiNodeAnim();
nd->mNodeName.Set(me->mName + ".Target"); nd->mNodeName.Set(me->mName + ".Target");
// If there is no input position channel we will need
// to supply the default position from the node's
// local transformation matrix.
/*TargetAnimationHelper helper;
if (me->mAnim.akeyPositions.empty())
{
aiMatrix4x4& mat = (*i)->mTransform;
helper.SetFixedMainAnimationChannel(aiVector3D(
mat.a4, mat.b4, mat.c4));
}
else helper.SetMainAnimationChannel (&me->mAnim.akeyPositions);
helper.SetTargetAnimationChannel (&me->mTargetAnim.akeyPositions);
helper.Process(&me->mTargetAnim.akeyPositions);*/
// Allocate the key array and fill it // Allocate the key array and fill it
nd->mNumPositionKeys = (unsigned int)me->mTargetAnim.akeyPositions.size(); nd->mNumPositionKeys = (unsigned int)me->mTargetAnim.akeyPositions.size();
nd->mPositionKeys = new aiVectorKey[nd->mNumPositionKeys]; nd->mPositionKeys = new aiVectorKey[nd->mNumPositionKeys];

View File

@ -479,6 +479,11 @@ void Parser::ParseLV1MaterialListBlock() {
if (TokenMatch(filePtr, "MATERIAL_COUNT", 14)) { if (TokenMatch(filePtr, "MATERIAL_COUNT", 14)) {
ParseLV4MeshLong(iMaterialCount); ParseLV4MeshLong(iMaterialCount);
if (UINT_MAX - iOldMaterialCount < iMaterialCount) {
LogWarning("Out of range: material index is too large");
return;
}
// now allocate enough storage to hold all materials // now allocate enough storage to hold all materials
m_vMaterials.resize(iOldMaterialCount + iMaterialCount, Material("INVALID")); m_vMaterials.resize(iOldMaterialCount + iMaterialCount, Material("INVALID"));
continue; continue;

View File

@ -115,15 +115,12 @@ BlenderImporter::~BlenderImporter() {
delete modifier_cache; delete modifier_cache;
} }
static const char * const Tokens[] = { "BLENDER" }; static const char Token[] = "BLENDER";
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file. // Returns whether the class can handle the format of the given file.
bool BlenderImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const { bool BlenderImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
// note: this won't catch compressed files return ParseMagicToken(pFile, pIOHandler).error.empty();
static const char *tokens[] = { "<BLENDER", "blender" };
return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -142,63 +139,21 @@ void BlenderImporter::SetupProperties(const Importer * /*pImp*/) {
// Imports the given file into the given scene structure. // Imports the given file into the given scene structure.
void BlenderImporter::InternReadFile(const std::string &pFile, void BlenderImporter::InternReadFile(const std::string &pFile,
aiScene *pScene, IOSystem *pIOHandler) { aiScene *pScene, IOSystem *pIOHandler) {
#ifndef ASSIMP_BUILD_NO_COMPRESSED_BLEND
std::vector<char> uncompressed;
#endif
FileDatabase file; FileDatabase file;
std::shared_ptr<IOStream> stream(pIOHandler->Open(pFile, "rb")); StreamOrError streamOrError = ParseMagicToken(pFile, pIOHandler);
if (!stream) { if (!streamOrError.error.empty()) {
ThrowException("Could not open file for reading"); ThrowException(streamOrError.error);
} }
std::shared_ptr<IOStream> stream = std::move(streamOrError.stream);
char magic[8] = { 0 }; char version[4] = { 0 };
stream->Read(magic, 7, 1); file.i64bit = (stream->Read(version, 1, 1), version[0] == '-');
if (strcmp(magic, Tokens[0])) { file.little = (stream->Read(version, 1, 1), version[0] == 'v');
// Check for presence of the gzip header. If yes, assume it is a
// compressed blend file and try uncompressing it, else fail. This is to
// avoid uncompressing random files which our loader might end up with.
#ifdef ASSIMP_BUILD_NO_COMPRESSED_BLEND
ThrowException("BLENDER magic bytes are missing, is this file compressed (Assimp was built without decompression support)?");
#else
if (magic[0] != 0x1f || static_cast<uint8_t>(magic[1]) != 0x8b) {
ThrowException("BLENDER magic bytes are missing, couldn't find GZIP header either");
}
LogDebug("Found no BLENDER magic word but a GZIP header, might be a compressed file"); stream->Read(version, 3, 1);
if (magic[2] != 8) { version[3] = '\0';
ThrowException("Unsupported GZIP compression method");
}
// http://www.gzip.org/zlib/rfc-gzip.html#header-trailer LogInfo("Blender version is ", version[0], ".", version + 1,
stream->Seek(0L, aiOrigin_SET);
std::shared_ptr<StreamReaderLE> reader = std::shared_ptr<StreamReaderLE>(new StreamReaderLE(stream));
size_t total = 0;
Compression compression;
if (compression.open(Compression::Format::Binary, Compression::FlushMode::NoFlush, 16 + Compression::MaxWBits)) {
total = compression.decompress((unsigned char *)reader->GetPtr(), reader->GetRemainingSize(), uncompressed);
compression.close();
}
// replace the input stream with a memory stream
stream = std::make_shared<MemoryIOStream>(reinterpret_cast<uint8_t *>(uncompressed.data()), total);
// .. and retry
stream->Read(magic, 7, 1);
if (strcmp(magic, "BLENDER")) {
ThrowException("Found no BLENDER magic word in decompressed GZIP file");
}
#endif
}
file.i64bit = (stream->Read(magic, 1, 1), magic[0] == '-');
file.little = (stream->Read(magic, 1, 1), magic[0] == 'v');
stream->Read(magic, 3, 1);
magic[3] = '\0';
LogInfo("Blender version is ", magic[0], ".", magic + 1,
" (64bit: ", file.i64bit ? "true" : "false", " (64bit: ", file.i64bit ? "true" : "false",
", little endian: ", file.little ? "true" : "false", ")"); ", little endian: ", file.little ? "true" : "false", ")");
@ -1338,4 +1293,55 @@ aiNode *BlenderImporter::ConvertNode(const Scene &in, const Object *obj, Convers
return node.release(); return node.release();
} }
BlenderImporter::StreamOrError BlenderImporter::ParseMagicToken(const std::string &pFile, IOSystem *pIOHandler) const {
std::shared_ptr<IOStream> stream(pIOHandler->Open(pFile, "rb"));
if (stream == nullptr) {
return {{}, {}, "Could not open file for reading"};
}
char magic[8] = { 0 };
stream->Read(magic, 7, 1);
if (strcmp(magic, Token) == 0) {
return {stream, {}, {}};
}
// Check for presence of the gzip header. If yes, assume it is a
// compressed blend file and try uncompressing it, else fail. This is to
// avoid uncompressing random files which our loader might end up with.
#ifdef ASSIMP_BUILD_NO_COMPRESSED_BLEND
return {{}, {}, "BLENDER magic bytes are missing, is this file compressed (Assimp was built without decompression support)?"};
#else
if (magic[0] != 0x1f || static_cast<uint8_t>(magic[1]) != 0x8b) {
return {{}, {}, "BLENDER magic bytes are missing, couldn't find GZIP header either"};
}
LogDebug("Found no BLENDER magic word but a GZIP header, might be a compressed file");
if (magic[2] != 8) {
return {{}, {}, "Unsupported GZIP compression method"};
}
// http://www.gzip.org/zlib/rfc-gzip.html#header-trailer
stream->Seek(0L, aiOrigin_SET);
std::shared_ptr<StreamReaderLE> reader = std::shared_ptr<StreamReaderLE>(new StreamReaderLE(stream));
size_t total = 0;
Compression compression;
auto uncompressed = std::make_shared<std::vector<char>>();
if (compression.open(Compression::Format::Binary, Compression::FlushMode::NoFlush, 16 + Compression::MaxWBits)) {
total = compression.decompress((unsigned char *)reader->GetPtr(), reader->GetRemainingSize(), *uncompressed);
compression.close();
}
// replace the input stream with a memory stream
stream = std::make_shared<MemoryIOStream>(reinterpret_cast<uint8_t *>(uncompressed->data()), total);
// .. and retry
stream->Read(magic, 7, 1);
if (strcmp(magic, Token) == 0) {
return {stream, uncompressed, {}};
}
return {{}, {}, "Found no BLENDER magic word in decompressed GZIP file"};
#endif
}
#endif // ASSIMP_BUILD_NO_BLEND_IMPORTER #endif // ASSIMP_BUILD_NO_BLEND_IMPORTER

View File

@ -180,6 +180,19 @@ private:
const Blender::MTex *tex, const Blender::MTex *tex,
Blender::ConversionData &conv_data); Blender::ConversionData &conv_data);
// TODO: Move to a std::variant, once c++17 is supported.
struct StreamOrError {
std::shared_ptr<IOStream> stream;
std::shared_ptr<std::vector<char>> input;
std::string error;
};
// Returns either a stream (and optional input data for the stream) or
// an error if it can't parse the magic token.
StreamOrError ParseMagicToken(
const std::string &pFile,
IOSystem *pIOHandler) const;
private: // static stuff, mostly logging and error reporting. private: // static stuff, mostly logging and error reporting.
// -------------------- // --------------------
static void CheckActualType(const Blender::ElemBase *dt, static void CheckActualType(const Blender::ElemBase *dt,

View File

@ -95,6 +95,7 @@ ColladaLoader::ColladaLoader() :
noSkeletonMesh(false), noSkeletonMesh(false),
removeEmptyBones(false), removeEmptyBones(false),
ignoreUpDirection(false), ignoreUpDirection(false),
ignoreUnitSize(false),
useColladaName(false), useColladaName(false),
mNodeNameCounter(0) { mNodeNameCounter(0) {
// empty // empty
@ -122,6 +123,7 @@ void ColladaLoader::SetupProperties(const Importer *pImp) {
noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES, 0) != 0; noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES, 0) != 0;
removeEmptyBones = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES, true) != 0; removeEmptyBones = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES, true) != 0;
ignoreUpDirection = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_COLLADA_IGNORE_UP_DIRECTION, 0) != 0; ignoreUpDirection = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_COLLADA_IGNORE_UP_DIRECTION, 0) != 0;
ignoreUnitSize = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_COLLADA_IGNORE_UNIT_SIZE, 0) != 0;
useColladaName = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_COLLADA_USE_COLLADA_NAMES, 0) != 0; useColladaName = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_COLLADA_USE_COLLADA_NAMES, 0) != 0;
} }
@ -170,12 +172,15 @@ void ColladaLoader::InternReadFile(const std::string &pFile, aiScene *pScene, IO
// ... then fill the materials with the now adjusted settings // ... then fill the materials with the now adjusted settings
FillMaterials(parser, pScene); FillMaterials(parser, pScene);
if (!ignoreUnitSize) {
// Apply unit-size scale calculation // Apply unit-size scale calculation
pScene->mRootNode->mTransformation *= aiMatrix4x4(
pScene->mRootNode->mTransformation *= aiMatrix4x4(parser.mUnitSize, 0, 0, 0, parser.mUnitSize, 0, 0, 0,
0, parser.mUnitSize, 0, 0, 0, parser.mUnitSize, 0, 0,
0, 0, parser.mUnitSize, 0, 0, 0, parser.mUnitSize, 0,
0, 0, 0, 1); 0, 0, 0, 1);
}
if (!ignoreUpDirection) { if (!ignoreUpDirection) {
// Convert to Y_UP, if different orientation // Convert to Y_UP, if different orientation
if (parser.mUpDirection == ColladaParser::UP_X) { if (parser.mUpDirection == ColladaParser::UP_X) {

View File

@ -239,6 +239,7 @@ protected:
bool noSkeletonMesh; bool noSkeletonMesh;
bool removeEmptyBones; bool removeEmptyBones;
bool ignoreUpDirection; bool ignoreUpDirection;
bool ignoreUnitSize;
bool useColladaName; bool useColladaName;
/** Used by FindNameForNode() to generate unique node names */ /** Used by FindNameForNode() to generate unique node names */

View File

@ -88,6 +88,7 @@ static aiColor4D g_aclrDxfIndexColors[] = {
aiColor4D (1.0f, 1.0f, 1.0f, 1.0f), // white aiColor4D (1.0f, 1.0f, 1.0f, 1.0f), // white
aiColor4D (0.6f, 0.0f, 1.0f, 1.0f) // violet aiColor4D (0.6f, 0.0f, 1.0f, 1.0f) // violet
}; };
#define AI_DXF_NUM_INDEX_COLORS (sizeof(g_aclrDxfIndexColors)/sizeof(g_aclrDxfIndexColors[0])) #define AI_DXF_NUM_INDEX_COLORS (sizeof(g_aclrDxfIndexColors)/sizeof(g_aclrDxfIndexColors[0]))
#define AI_DXF_ENTITIES_MAGIC_BLOCK "$ASSIMP_ENTITIES_MAGIC" #define AI_DXF_ENTITIES_MAGIC_BLOCK "$ASSIMP_ENTITIES_MAGIC"
@ -109,14 +110,6 @@ static const aiImporterDesc desc = {
"dxf" "dxf"
}; };
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
DXFImporter::DXFImporter() = default;
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
DXFImporter::~DXFImporter() = default;
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file. // Returns whether the class can handle the format of the given file.
bool DXFImporter::CanRead( const std::string& filename, IOSystem* pIOHandler, bool /*checkSig*/ ) const { bool DXFImporter::CanRead( const std::string& filename, IOSystem* pIOHandler, bool /*checkSig*/ ) const {
@ -229,7 +222,7 @@ void DXFImporter::ConvertMeshes(aiScene* pScene, DXF::FileData& output) {
ASSIMP_LOG_VERBOSE_DEBUG("DXF: Unexpanded polycount is ", icount, ", vertex count is ", vcount); ASSIMP_LOG_VERBOSE_DEBUG("DXF: Unexpanded polycount is ", icount, ", vertex count is ", vcount);
} }
if (! output.blocks.size() ) { if (output.blocks.empty()) {
throw DeadlyImportError("DXF: no data blocks loaded"); throw DeadlyImportError("DXF: no data blocks loaded");
} }
@ -587,10 +580,11 @@ void DXFImporter::ParseInsertion(DXF::LineReader& reader, DXF::FileData& output)
} }
} }
#define DXF_POLYLINE_FLAG_CLOSED 0x1 static constexpr unsigned int DXF_POLYLINE_FLAG_CLOSED = 0x1;
#define DXF_POLYLINE_FLAG_3D_POLYLINE 0x8 // Currently unused
#define DXF_POLYLINE_FLAG_3D_POLYMESH 0x10 //static constexpr unsigned int DXF_POLYLINE_FLAG_3D_POLYLINE = 0x8;
#define DXF_POLYLINE_FLAG_POLYFACEMESH 0x40 //static constexpr unsigned int DXF_POLYLINE_FLAG_3D_POLYMESH = 0x10;
static constexpr unsigned int DXF_POLYLINE_FLAG_POLYFACEMESH = 0x40;
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void DXFImporter::ParsePolyLine(DXF::LineReader& reader, DXF::FileData& output) { void DXFImporter::ParsePolyLine(DXF::LineReader& reader, DXF::FileData& output) {
@ -639,12 +633,6 @@ void DXFImporter::ParsePolyLine(DXF::LineReader& reader, DXF::FileData& output)
reader++; reader++;
} }
//if (!(line.flags & DXF_POLYLINE_FLAG_POLYFACEMESH)) {
// DefaultLogger::get()->warn((Formatter::format("DXF: polyline not currently supported: "),line.flags));
// output.blocks.back().lines.pop_back();
// return;
//}
if (vguess && line.positions.size() != vguess) { if (vguess && line.positions.size() != vguess) {
ASSIMP_LOG_WARN("DXF: unexpected vertex count in polymesh: ", ASSIMP_LOG_WARN("DXF: unexpected vertex count in polymesh: ",
line.positions.size(),", expected ", vguess ); line.positions.size(),", expected ", vguess );
@ -734,12 +722,18 @@ void DXFImporter::ParsePolyLineVertex(DXF::LineReader& reader, DXF::PolyLine& li
case 71: case 71:
case 72: case 72:
case 73: case 73:
case 74: case 74: {
if (cnti == 4) { if (cnti == 4) {
ASSIMP_LOG_WARN("DXF: more than 4 indices per face not supported; ignoring"); ASSIMP_LOG_WARN("DXF: more than 4 indices per face not supported; ignoring");
break; break;
} }
indices[cnti++] = reader.ValueAsUnsignedInt(); const int index = reader.ValueAsSignedInt();
if (index >= 0) {
indices[cnti++] = static_cast<unsigned int>(index);
} else {
indices[cnti++] = static_cast<unsigned int>(-index);
}
}
break; break;
// color // color
@ -777,8 +771,7 @@ void DXFImporter::ParsePolyLineVertex(DXF::LineReader& reader, DXF::PolyLine& li
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void DXFImporter::Parse3DFace(DXF::LineReader& reader, DXF::FileData& output) void DXFImporter::Parse3DFace(DXF::LineReader& reader, DXF::FileData& output) {
{
// (note) this is also used for for parsing line entities, so we // (note) this is also used for for parsing line entities, so we
// must handle the vertex_count == 2 case as well. // must handle the vertex_count == 2 case as well.
@ -795,8 +788,7 @@ void DXFImporter::Parse3DFace(DXF::LineReader& reader, DXF::FileData& output)
if (reader.GroupCode() == 0) { if (reader.GroupCode() == 0) {
break; break;
} }
switch (reader.GroupCode()) switch (reader.GroupCode()) {
{
// 8 specifies the layer // 8 specifies the layer
case 8: case 8:

View File

@ -68,8 +68,8 @@ namespace DXF {
*/ */
class DXFImporter : public BaseImporter { class DXFImporter : public BaseImporter {
public: public:
DXFImporter(); DXFImporter() = default;
~DXFImporter() override; ~DXFImporter() override = default;
// ------------------------------------------------------------------- // -------------------------------------------------------------------
/** Returns whether the class can handle the format of the given file. /** Returns whether the class can handle the format of the given file.

View File

@ -342,8 +342,7 @@ void ReadData(const char*& sbegin_out, const char*& send_out, const char* input,
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
bool ReadScope(TokenList& output_tokens, const char* input, const char*& cursor, const char* end, bool const is64bits) bool ReadScope(TokenList &output_tokens, StackAllocator &token_allocator, const char *input, const char *&cursor, const char *end, bool const is64bits) {
{
// the first word contains the offset at which this block ends // the first word contains the offset at which this block ends
const uint64_t end_offset = is64bits ? ReadDoubleWord(input, cursor, end) : ReadWord(input, cursor, end); const uint64_t end_offset = is64bits ? ReadDoubleWord(input, cursor, end) : ReadWord(input, cursor, end);
@ -409,7 +408,7 @@ bool ReadScope(TokenList& output_tokens, const char* input, const char*& cursor,
// XXX this is vulnerable to stack overflowing .. // XXX this is vulnerable to stack overflowing ..
while(Offset(input, cursor) < end_offset - sentinel_block_length) { while(Offset(input, cursor) < end_offset - sentinel_block_length) {
ReadScope(output_tokens, input, cursor, input + end_offset - sentinel_block_length, is64bits); ReadScope(output_tokens, token_allocator, input, cursor, input + end_offset - sentinel_block_length, is64bits);
} }
output_tokens.push_back(new_Token(cursor, cursor + 1, TokenType_CLOSE_BRACKET, Offset(input, cursor) )); output_tokens.push_back(new_Token(cursor, cursor + 1, TokenType_CLOSE_BRACKET, Offset(input, cursor) ));
@ -432,8 +431,7 @@ bool ReadScope(TokenList& output_tokens, const char* input, const char*& cursor,
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// TODO: Test FBX Binary files newer than the 7500 version to check if the 64 bits address behaviour is consistent // TODO: Test FBX Binary files newer than the 7500 version to check if the 64 bits address behaviour is consistent
void TokenizeBinary(TokenList& output_tokens, const char* input, size_t length) void TokenizeBinary(TokenList &output_tokens, const char *input, size_t length, StackAllocator &token_allocator) {
{
ai_assert(input); ai_assert(input);
ASSIMP_LOG_DEBUG("Tokenizing binary FBX file"); ASSIMP_LOG_DEBUG("Tokenizing binary FBX file");
@ -466,7 +464,7 @@ void TokenizeBinary(TokenList& output_tokens, const char* input, size_t length)
try try
{ {
while (cursor < end ) { while (cursor < end ) {
if (!ReadScope(output_tokens, input, cursor, input + length, is64bits)) { if (!ReadScope(output_tokens, token_allocator, input, cursor, input + length, is64bits)) {
break; break;
} }
} }

View File

@ -93,6 +93,8 @@ FBXConverter::FBXConverter(aiScene *out, const Document &doc, bool removeEmptyBo
mSceneOut(out), mSceneOut(out),
doc(doc), doc(doc),
mRemoveEmptyBones(removeEmptyBones) { mRemoveEmptyBones(removeEmptyBones) {
// animations need to be converted first since this will // animations need to be converted first since this will
// populate the node_anim_chain_bits map, which is needed // populate the node_anim_chain_bits map, which is needed
// to determine which nodes need to be generated. // to determine which nodes need to be generated.
@ -427,12 +429,26 @@ void FBXConverter::ConvertCamera(const Camera &cam, const std::string &orig_name
out_camera->mLookAt = aiVector3D(1.0f, 0.0f, 0.0f); out_camera->mLookAt = aiVector3D(1.0f, 0.0f, 0.0f);
out_camera->mUp = aiVector3D(0.0f, 1.0f, 0.0f); out_camera->mUp = aiVector3D(0.0f, 1.0f, 0.0f);
out_camera->mHorizontalFOV = AI_DEG_TO_RAD(cam.FieldOfView()); // NOTE: Some software (maya) does not put FieldOfView in FBX, so we compute
// mHorizontalFOV from FocalLength and FilmWidth with unit conversion.
out_camera->mClipPlaneNear = cam.NearPlane(); // TODO: This is not a complete solution for how FBX cameras can be stored.
out_camera->mClipPlaneFar = cam.FarPlane(); // TODO: Incorporate non-square pixel aspect ratio.
// TODO: FBX aperture mode might be storing vertical FOV in need of conversion with aspect ratio.
float fov_deg = cam.FieldOfView();
// If FOV not specified in file, compute using FilmWidth and FocalLength.
if (fov_deg == kFovUnknown) {
float film_width_inches = cam.FilmWidth();
float focal_length_mm = cam.FocalLength();
ASSIMP_LOG_VERBOSE_DEBUG("FBX FOV unspecified. Computing from FilmWidth (", film_width_inches, "inches) and FocalLength (", focal_length_mm, "mm).");
double half_fov_rad = std::atan2(film_width_inches * 25.4 * 0.5, focal_length_mm);
out_camera->mHorizontalFOV = static_cast<float>(half_fov_rad);
} else {
// FBX fov is full-view degrees. We want half-view radians.
out_camera->mHorizontalFOV = AI_DEG_TO_RAD(fov_deg) * 0.5f;
}
out_camera->mHorizontalFOV = AI_DEG_TO_RAD(cam.FieldOfView());
out_camera->mClipPlaneNear = cam.NearPlane(); out_camera->mClipPlaneNear = cam.NearPlane();
out_camera->mClipPlaneFar = cam.FarPlane(); out_camera->mClipPlaneFar = cam.FarPlane();
} }

View File

@ -243,7 +243,7 @@ FileGlobalSettings::FileGlobalSettings(const Document &doc, std::shared_ptr<cons
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
Document::Document(const Parser& parser, const ImportSettings& settings) : Document::Document(Parser& parser, const ImportSettings& settings) :
settings(settings), parser(parser) { settings(settings), parser(parser) {
ASSIMP_LOG_DEBUG("Creating FBX Document"); ASSIMP_LOG_DEBUG("Creating FBX Document");
@ -265,13 +265,17 @@ Document::Document(const Parser& parser, const ImportSettings& settings) :
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
Document::~Document() { Document::~Document()
{
// The document does not own the memory for the following objects, but we need to call their d'tor
// so they can properly free memory like string members:
for (ObjectMap::value_type &v : objects) { for (ObjectMap::value_type &v : objects) {
delete v.second; delete_LazyObject(v.second);
} }
for (ConnectionMap::value_type &v : src_connections) { for (ConnectionMap::value_type &v : src_connections) {
delete v.second; delete_Connection(v.second);
} }
// |dest_connections| contain the same Connection objects as the |src_connections| // |dest_connections| contain the same Connection objects as the |src_connections|
} }
@ -356,9 +360,11 @@ void Document::ReadObjects() {
DOMError("no Objects dictionary found"); DOMError("no Objects dictionary found");
} }
StackAllocator &allocator = parser.GetAllocator();
// add a dummy entry to represent the Model::RootNode object (id 0), // add a dummy entry to represent the Model::RootNode object (id 0),
// which is only indirectly defined in the input file // which is only indirectly defined in the input file
objects[0] = new LazyObject(0L, *eobjects, *this); objects[0] = new_LazyObject(0L, *eobjects, *this);
const Scope& sobjects = *eobjects->Compound(); const Scope& sobjects = *eobjects->Compound();
for(const ElementMap::value_type& el : sobjects.Elements()) { for(const ElementMap::value_type& el : sobjects.Elements()) {
@ -387,7 +393,7 @@ void Document::ReadObjects() {
delete foundObject->second; delete foundObject->second;
} }
objects[id] = new LazyObject(id, *el.second, *this); objects[id] = new_LazyObject(id, *el.second, *this);
// grab all animation stacks upfront since there is no listing of them // grab all animation stacks upfront since there is no listing of them
if(!strcmp(el.first.c_str(),"AnimationStack")) { if(!strcmp(el.first.c_str(),"AnimationStack")) {
@ -454,7 +460,9 @@ void Document::ReadPropertyTemplates() {
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Document::ReadConnections() { void Document::ReadConnections()
{
StackAllocator &allocator = parser.GetAllocator();
const Scope &sc = parser.GetRootScope(); const Scope &sc = parser.GetRootScope();
// read property templates from "Definitions" section // read property templates from "Definitions" section
const Element* const econns = sc["Connections"]; const Element* const econns = sc["Connections"];
@ -494,7 +502,7 @@ void Document::ReadConnections() {
} }
// add new connection // add new connection
const Connection* const c = new Connection(insertionOrder++,src,dest,prop,*this); const Connection* const c = new_Connection(insertionOrder++,src,dest,prop,*this);
src_connections.insert(ConnectionMap::value_type(src,c)); src_connections.insert(ConnectionMap::value_type(src,c));
dest_connections.insert(ConnectionMap::value_type(dest,c)); dest_connections.insert(ConnectionMap::value_type(dest,c));
} }

View File

@ -55,9 +55,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define _AI_CONCAT(a,b) a ## b #define _AI_CONCAT(a,b) a ## b
#define AI_CONCAT(a,b) _AI_CONCAT(a,b) #define AI_CONCAT(a,b) _AI_CONCAT(a,b)
namespace Assimp { namespace Assimp {
namespace FBX { namespace FBX {
// Use an 'illegal' default FOV value to detect if the FBX camera has set the FOV.
static const float kFovUnknown = -1.0f;
class Parser; class Parser;
class Object; class Object;
struct ImportSettings; struct ImportSettings;
@ -81,6 +86,10 @@ class BlendShape;
class Skin; class Skin;
class Cluster; class Cluster;
#define new_LazyObject new (allocator.Allocate(sizeof(LazyObject))) LazyObject
#define new_Connection new (allocator.Allocate(sizeof(Connection))) Connection
#define delete_LazyObject(_p) (_p)->~LazyObject()
#define delete_Connection(_p) (_p)->~Connection()
/** Represents a delay-parsed FBX objects. Many objects in the scene /** Represents a delay-parsed FBX objects. Many objects in the scene
* are not needed by assimp, so it makes no sense to parse them * are not needed by assimp, so it makes no sense to parse them
@ -243,7 +252,7 @@ public:
fbx_simple_property(FilmAspectRatio, float, 1.0f) fbx_simple_property(FilmAspectRatio, float, 1.0f)
fbx_simple_property(ApertureMode, int, 0) fbx_simple_property(ApertureMode, int, 0)
fbx_simple_property(FieldOfView, float, 1.0f) fbx_simple_property(FieldOfView, float, kFovUnknown)
fbx_simple_property(FocalLength, float, 1.0f) fbx_simple_property(FocalLength, float, 1.0f)
}; };
@ -1073,7 +1082,7 @@ private:
/** DOM root for a FBX file */ /** DOM root for a FBX file */
class Document { class Document {
public: public:
Document(const Parser& parser, const ImportSettings& settings); Document(Parser& parser, const ImportSettings& settings);
~Document(); ~Document();
@ -1157,7 +1166,7 @@ private:
const ImportSettings& settings; const ImportSettings& settings;
ObjectMap objects; ObjectMap objects;
const Parser& parser; Parser& parser;
PropertyTemplateMap templates; PropertyTemplateMap templates;
ConnectionMap src_connections; ConnectionMap src_connections;

View File

@ -152,19 +152,19 @@ void FBXImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
// broad-phase tokenized pass in which we identify the core // broad-phase tokenized pass in which we identify the core
// syntax elements of FBX (brackets, commas, key:value mappings) // syntax elements of FBX (brackets, commas, key:value mappings)
TokenList tokens; TokenList tokens;
Assimp::StackAllocator tempAllocator;
try { try {
bool is_binary = false; bool is_binary = false;
if (!strncmp(begin, "Kaydara FBX Binary", 18)) { if (!strncmp(begin, "Kaydara FBX Binary", 18)) {
is_binary = true; is_binary = true;
TokenizeBinary(tokens, begin, contents.size()); TokenizeBinary(tokens, begin, contents.size(), tempAllocator);
} else { } else {
Tokenize(tokens, begin); Tokenize(tokens, begin, tempAllocator);
} }
// use this information to construct a very rudimentary // use this information to construct a very rudimentary
// parse-tree representing the FBX scope structure // parse-tree representing the FBX scope structure
Parser parser(tokens, is_binary); Parser parser(tokens, tempAllocator, is_binary);
// take the raw parse-tree and convert it to a FBX DOM // take the raw parse-tree and convert it to a FBX DOM
Document doc(parser, mSettings); Document doc(parser, mSettings);
@ -183,9 +183,11 @@ void FBXImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
// assimp universal format (M) // assimp universal format (M)
SetFileScale(size_relative_to_cm * 0.01f); SetFileScale(size_relative_to_cm * 0.01f);
std::for_each(tokens.begin(), tokens.end(), Util::delete_fun<Token>()); // This collection does not own the memory for the tokens, but we need to call their d'tor
std::for_each(tokens.begin(), tokens.end(), Util::destructor_fun<Token>());
} catch (std::exception &) { } catch (std::exception &) {
std::for_each(tokens.begin(), tokens.end(), Util::delete_fun<Token>()); std::for_each(tokens.begin(), tokens.end(), Util::destructor_fun<Token>());
throw; throw;
} }
} }

View File

@ -116,8 +116,11 @@ namespace Assimp {
namespace FBX { namespace FBX {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
Element::Element(const Token& key_token, Parser& parser) : key_token(key_token) { Element::Element(const Token& key_token, Parser& parser) :
key_token(key_token), compound(nullptr)
{
TokenPtr n = nullptr; TokenPtr n = nullptr;
StackAllocator &allocator = parser.GetAllocator();
do { do {
n = parser.AdvanceToNextToken(); n = parser.AdvanceToNextToken();
if(!n) { if(!n) {
@ -146,7 +149,7 @@ Element::Element(const Token& key_token, Parser& parser) : key_token(key_token)
} }
if (n->Type() == TokenType_OPEN_BRACKET) { if (n->Type() == TokenType_OPEN_BRACKET) {
compound.reset(new Scope(parser)); compound = new_Scope(parser);
// current token should be a TOK_CLOSE_BRACKET // current token should be a TOK_CLOSE_BRACKET
n = parser.CurrentToken(); n = parser.CurrentToken();
@ -164,6 +167,15 @@ Element::Element(const Token& key_token, Parser& parser) : key_token(key_token)
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
Element::~Element()
{
if (compound) {
delete_Scope(compound);
}
// no need to delete tokens, they are owned by the parser
}
Scope::Scope(Parser& parser,bool topLevel) Scope::Scope(Parser& parser,bool topLevel)
{ {
if(!topLevel) { if(!topLevel) {
@ -173,6 +185,7 @@ Scope::Scope(Parser& parser,bool topLevel)
} }
} }
StackAllocator &allocator = parser.GetAllocator();
TokenPtr n = parser.AdvanceToNextToken(); TokenPtr n = parser.AdvanceToNextToken();
if (n == nullptr) { if (n == nullptr) {
ParseError("unexpected end of file"); ParseError("unexpected end of file");
@ -198,8 +211,8 @@ Scope::Scope(Parser& parser,bool topLevel)
elements.insert(ElementMap::value_type(str, element)); elements.insert(ElementMap::value_type(str, element));
return; return;
} }
delete_Element(element);
ParseError("unexpected end of file",parser.LastToken()); ParseError("unexpected end of file",parser.LastToken());
delete element;
} else { } else {
elements.insert(ElementMap::value_type(str, element)); elements.insert(ElementMap::value_type(str, element));
} }
@ -207,22 +220,27 @@ Scope::Scope(Parser& parser,bool topLevel)
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
Scope::~Scope() { Scope::~Scope()
{
// This collection does not own the memory for the elements, but we need to call their d'tor:
for (ElementMap::value_type &v : elements) { for (ElementMap::value_type &v : elements) {
delete v.second; delete_Element(v.second);
} }
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
Parser::Parser (const TokenList& tokens, bool is_binary) Parser::Parser(const TokenList &tokens, StackAllocator &allocator, bool is_binary) :
: tokens(tokens) tokens(tokens), allocator(allocator), last(), current(), cursor(tokens.begin()), is_binary(is_binary)
, last()
, current()
, cursor(tokens.begin())
, is_binary(is_binary)
{ {
ASSIMP_LOG_DEBUG("Parsing FBX tokens"); ASSIMP_LOG_DEBUG("Parsing FBX tokens");
root.reset(new Scope(*this,true)); root = new_Scope(*this, true);
}
// ------------------------------------------------------------------------------------------------
Parser::~Parser()
{
delete_Scope(root);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -52,6 +52,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/LogAux.h> #include <assimp/LogAux.h>
#include <assimp/fast_atof.h> #include <assimp/fast_atof.h>
#include "Common/StackAllocator.h"
#include "FBXCompileConfig.h" #include "FBXCompileConfig.h"
#include "FBXTokenizer.h" #include "FBXTokenizer.h"
@ -63,14 +64,14 @@ class Parser;
class Element; class Element;
// XXX should use C++11's unique_ptr - but assimp's need to keep working with 03 // XXX should use C++11's unique_ptr - but assimp's need to keep working with 03
typedef std::vector< Scope* > ScopeList; using ScopeList = std::vector<Scope*>;
typedef std::fbx_unordered_multimap< std::string, Element* > ElementMap; using ElementMap = std::fbx_unordered_multimap< std::string, Element*>;
using ElementCollection = std::pair<ElementMap::const_iterator,ElementMap::const_iterator>;
typedef std::pair<ElementMap::const_iterator,ElementMap::const_iterator> ElementCollection;
# define new_Scope new Scope
# define new_Element new Element
#define new_Scope new (allocator.Allocate(sizeof(Scope))) Scope
#define new_Element new (allocator.Allocate(sizeof(Element))) Element
#define delete_Scope(_p) (_p)->~Scope()
#define delete_Element(_p) (_p)->~Element()
/** FBX data entity that consists of a key:value tuple. /** FBX data entity that consists of a key:value tuple.
* *
@ -82,15 +83,16 @@ typedef std::pair<ElementMap::const_iterator,ElementMap::const_iterator> Element
* @endverbatim * @endverbatim
* *
* As can be seen in this sample, elements can contain nested #Scope * As can be seen in this sample, elements can contain nested #Scope
* as their trailing member. **/ * as their trailing member.
**/
class Element class Element
{ {
public: public:
Element(const Token& key_token, Parser& parser); Element(const Token& key_token, Parser& parser);
~Element() = default; ~Element();
const Scope* Compound() const { const Scope* Compound() const {
return compound.get(); return compound;
} }
const Token& KeyToken() const { const Token& KeyToken() const {
@ -104,7 +106,7 @@ public:
private: private:
const Token& key_token; const Token& key_token;
TokenList tokens; TokenList tokens;
std::unique_ptr<Scope> compound; Scope* compound;
}; };
/** FBX data entity that consists of a 'scope', a collection /** FBX data entity that consists of a 'scope', a collection
@ -159,8 +161,8 @@ class Parser
public: public:
/** Parse given a token list. Does not take ownership of the tokens - /** Parse given a token list. Does not take ownership of the tokens -
* the objects must persist during the entire parser lifetime */ * the objects must persist during the entire parser lifetime */
Parser (const TokenList& tokens,bool is_binary); Parser(const TokenList &tokens, StackAllocator &allocator, bool is_binary);
~Parser() = default; ~Parser();
const Scope& GetRootScope() const { const Scope& GetRootScope() const {
return *root; return *root;
@ -170,6 +172,10 @@ public:
return is_binary; return is_binary;
} }
StackAllocator &GetAllocator() {
return allocator;
}
private: private:
friend class Scope; friend class Scope;
friend class Element; friend class Element;
@ -180,10 +186,10 @@ private:
private: private:
const TokenList& tokens; const TokenList& tokens;
StackAllocator &allocator;
TokenPtr last, current; TokenPtr last, current;
TokenList::const_iterator cursor; TokenList::const_iterator cursor;
std::unique_ptr<Scope> root; Scope *root;
const bool is_binary; const bool is_binary;
}; };

View File

@ -94,7 +94,8 @@ AI_WONT_RETURN void TokenizeError(const std::string& message, unsigned int line,
// process a potential data token up to 'cur', adding it to 'output_tokens'. // process a potential data token up to 'cur', adding it to 'output_tokens'.
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void ProcessDataToken( TokenList& output_tokens, const char*& start, const char*& end, void ProcessDataToken(TokenList &output_tokens, StackAllocator &token_allocator,
const char*& start, const char*& end,
unsigned int line, unsigned int line,
unsigned int column, unsigned int column,
TokenType type = TokenType_DATA, TokenType type = TokenType_DATA,
@ -131,8 +132,7 @@ void ProcessDataToken( TokenList& output_tokens, const char*& start, const char*
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Tokenize(TokenList& output_tokens, const char* input) void Tokenize(TokenList &output_tokens, const char *input, StackAllocator &token_allocator) {
{
ai_assert(input); ai_assert(input);
ASSIMP_LOG_DEBUG("Tokenizing ASCII FBX file"); ASSIMP_LOG_DEBUG("Tokenizing ASCII FBX file");
@ -164,7 +164,7 @@ void Tokenize(TokenList& output_tokens, const char* input)
in_double_quotes = false; in_double_quotes = false;
token_end = cur; token_end = cur;
ProcessDataToken(output_tokens,token_begin,token_end,line,column); ProcessDataToken(output_tokens, token_allocator, token_begin, token_end, line, column);
pending_data_token = false; pending_data_token = false;
} }
continue; continue;
@ -181,30 +181,30 @@ void Tokenize(TokenList& output_tokens, const char* input)
continue; continue;
case ';': case ';':
ProcessDataToken(output_tokens,token_begin,token_end,line,column); ProcessDataToken(output_tokens, token_allocator, token_begin, token_end, line, column);
comment = true; comment = true;
continue; continue;
case '{': case '{':
ProcessDataToken(output_tokens,token_begin,token_end, line, column); ProcessDataToken(output_tokens, token_allocator, token_begin, token_end, line, column);
output_tokens.push_back(new_Token(cur,cur+1,TokenType_OPEN_BRACKET,line,column)); output_tokens.push_back(new_Token(cur,cur+1,TokenType_OPEN_BRACKET,line,column));
continue; continue;
case '}': case '}':
ProcessDataToken(output_tokens,token_begin,token_end,line,column); ProcessDataToken(output_tokens, token_allocator, token_begin, token_end, line, column);
output_tokens.push_back(new_Token(cur,cur+1,TokenType_CLOSE_BRACKET,line,column)); output_tokens.push_back(new_Token(cur,cur+1,TokenType_CLOSE_BRACKET,line,column));
continue; continue;
case ',': case ',':
if (pending_data_token) { if (pending_data_token) {
ProcessDataToken(output_tokens,token_begin,token_end,line,column,TokenType_DATA,true); ProcessDataToken(output_tokens, token_allocator, token_begin, token_end, line, column, TokenType_DATA, true);
} }
output_tokens.push_back(new_Token(cur,cur+1,TokenType_COMMA,line,column)); output_tokens.push_back(new_Token(cur,cur+1,TokenType_COMMA,line,column));
continue; continue;
case ':': case ':':
if (pending_data_token) { if (pending_data_token) {
ProcessDataToken(output_tokens,token_begin,token_end,line,column,TokenType_KEY,true); ProcessDataToken(output_tokens, token_allocator, token_begin, token_end, line, column, TokenType_KEY, true);
} }
else { else {
TokenizeError("unexpected colon", line, column); TokenizeError("unexpected colon", line, column);
@ -226,7 +226,7 @@ void Tokenize(TokenList& output_tokens, const char* input)
} }
} }
ProcessDataToken(output_tokens,token_begin,token_end,line,column,type); ProcessDataToken(output_tokens, token_allocator, token_begin, token_end, line, column, type);
} }
pending_data_token = false; pending_data_token = false;

View File

@ -47,6 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define INCLUDED_AI_FBX_TOKENIZER_H #define INCLUDED_AI_FBX_TOKENIZER_H
#include "FBXCompileConfig.h" #include "FBXCompileConfig.h"
#include "Common/StackAllocator.h"
#include <assimp/ai_assert.h> #include <assimp/ai_assert.h>
#include <assimp/defs.h> #include <assimp/defs.h>
#include <vector> #include <vector>
@ -157,7 +158,8 @@ private:
typedef const Token* TokenPtr; typedef const Token* TokenPtr;
typedef std::vector< TokenPtr > TokenList; typedef std::vector< TokenPtr > TokenList;
#define new_Token new Token #define new_Token new (token_allocator.Allocate(sizeof(Token))) Token
#define delete_Token(_p) (_p)->~Token()
/** Main FBX tokenizer function. Transform input buffer into a list of preprocessed tokens. /** Main FBX tokenizer function. Transform input buffer into a list of preprocessed tokens.
@ -167,7 +169,7 @@ typedef std::vector< TokenPtr > TokenList;
* @param output_tokens Receives a list of all tokens in the input data. * @param output_tokens Receives a list of all tokens in the input data.
* @param input_buffer Textual input buffer to be processed, 0-terminated. * @param input_buffer Textual input buffer to be processed, 0-terminated.
* @throw DeadlyImportError if something goes wrong */ * @throw DeadlyImportError if something goes wrong */
void Tokenize(TokenList& output_tokens, const char* input); void Tokenize(TokenList &output_tokens, const char *input, StackAllocator &tokenAllocator);
/** Tokenizer function for binary FBX files. /** Tokenizer function for binary FBX files.
@ -178,7 +180,7 @@ void Tokenize(TokenList& output_tokens, const char* input);
* @param input_buffer Binary input buffer to be processed. * @param input_buffer Binary input buffer to be processed.
* @param length Length of input buffer, in bytes. There is no 0-terminal. * @param length Length of input buffer, in bytes. There is no 0-terminal.
* @throw DeadlyImportError if something goes wrong */ * @throw DeadlyImportError if something goes wrong */
void TokenizeBinary(TokenList& output_tokens, const char* input, size_t length); void TokenizeBinary(TokenList &output_tokens, const char *input, size_t length, StackAllocator &tokenAllocator);
} // ! FBX } // ! FBX

View File

@ -66,6 +66,17 @@ struct delete_fun
} }
}; };
/** helper for std::for_each to call the destructor on all items in a container without freeing their heap*/
template <typename T>
struct destructor_fun {
void operator()(const volatile T* del) {
if (del) {
del->~T();
}
}
};
/** Get a string representation for a #TokenType. */ /** Get a string representation for a #TokenType. */
const char* TokenTypeString(TokenType t); const char* TokenTypeString(TokenType t);

View File

@ -115,7 +115,9 @@ void HMPImporter::InternReadFile(const std::string &pFile,
throw DeadlyImportError("HMP File is too small."); throw DeadlyImportError("HMP File is too small.");
// Allocate storage and copy the contents of the file to a memory buffer // Allocate storage and copy the contents of the file to a memory buffer
mBuffer = new uint8_t[fileSize]; auto deleter=[this](uint8_t* ptr){ delete[] ptr; mBuffer = nullptr; };
std::unique_ptr<uint8_t[], decltype(deleter)> buffer(new uint8_t[fileSize], deleter);
mBuffer = buffer.get();
file->Read((void *)mBuffer, 1, fileSize); file->Read((void *)mBuffer, 1, fileSize);
iFileSize = (unsigned int)fileSize; iFileSize = (unsigned int)fileSize;
@ -143,9 +145,6 @@ void HMPImporter::InternReadFile(const std::string &pFile,
// Print the magic word to the logger // Print the magic word to the logger
std::string szBuffer = ai_str_toprintable((const char *)&iMagic, sizeof(iMagic)); std::string szBuffer = ai_str_toprintable((const char *)&iMagic, sizeof(iMagic));
delete[] mBuffer;
mBuffer = nullptr;
// We're definitely unable to load this file // We're definitely unable to load this file
throw DeadlyImportError("Unknown HMP subformat ", pFile, throw DeadlyImportError("Unknown HMP subformat ", pFile,
". Magic word (", szBuffer, ") is not known"); ". Magic word (", szBuffer, ") is not known");
@ -153,9 +152,6 @@ void HMPImporter::InternReadFile(const std::string &pFile,
// Set the AI_SCENE_FLAGS_TERRAIN bit // Set the AI_SCENE_FLAGS_TERRAIN bit
pScene->mFlags |= AI_SCENE_FLAGS_TERRAIN; pScene->mFlags |= AI_SCENE_FLAGS_TERRAIN;
delete[] mBuffer;
mBuffer = nullptr;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -445,11 +441,11 @@ void HMPImporter::ReadFirstSkin(unsigned int iNumSkins, const unsigned char *szC
szCursor += sizeof(uint32_t); szCursor += sizeof(uint32_t);
// allocate an output material // allocate an output material
aiMaterial *pcMat = new aiMaterial(); std::unique_ptr<aiMaterial> pcMat(new aiMaterial());
// read the skin, this works exactly as for MDL7 // read the skin, this works exactly as for MDL7
ParseSkinLump_3DGS_MDL7(szCursor, &szCursor, ParseSkinLump_3DGS_MDL7(szCursor, &szCursor,
pcMat, iType, iWidth, iHeight); pcMat.get(), iType, iWidth, iHeight);
// now we need to skip any other skins ... // now we need to skip any other skins ...
for (unsigned int i = 1; i < iNumSkins; ++i) { for (unsigned int i = 1; i < iNumSkins; ++i) {
@ -468,7 +464,7 @@ void HMPImporter::ReadFirstSkin(unsigned int iNumSkins, const unsigned char *szC
// setup the material ... // setup the material ...
pScene->mNumMaterials = 1; pScene->mNumMaterials = 1;
pScene->mMaterials = new aiMaterial *[1]; pScene->mMaterials = new aiMaterial *[1];
pScene->mMaterials[0] = pcMat; pScene->mMaterials[0] = pcMat.release();
*szCursorOut = szCursor; *szCursorOut = szCursor;
} }

View File

@ -48,6 +48,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "AssetLib/IFC/IFCUtil.h" #include "AssetLib/IFC/IFCUtil.h"
#include "Common/PolyTools.h" #include "Common/PolyTools.h"
#include "Geometry/GeometryUtils.h"
#include "PostProcessing/ProcessHelper.h" #include "PostProcessing/ProcessHelper.h"
namespace Assimp { namespace Assimp {
@ -235,7 +236,7 @@ IfcVector3 TempMesh::ComputeLastPolygonNormal(bool normalize) const {
struct CompareVector { struct CompareVector {
bool operator () (const IfcVector3& a, const IfcVector3& b) const { bool operator () (const IfcVector3& a, const IfcVector3& b) const {
IfcVector3 d = a - b; IfcVector3 d = a - b;
IfcFloat eps = ai_epsilon; constexpr IfcFloat eps = ai_epsilon;
return d.x < -eps || (std::abs(d.x) < eps && d.y < -eps) || (std::abs(d.x) < eps && std::abs(d.y) < eps && d.z < -eps); return d.x < -eps || (std::abs(d.x) < eps && d.y < -eps) || (std::abs(d.x) < eps && std::abs(d.y) < eps && d.z < -eps);
} }
}; };

View File

@ -43,6 +43,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* @brief Implementation of the Irr importer class * @brief Implementation of the Irr importer class
*/ */
#include "assimp/Exceptional.h"
#include "assimp/StringComparison.h"
#ifndef ASSIMP_BUILD_NO_IRR_IMPORTER #ifndef ASSIMP_BUILD_NO_IRR_IMPORTER
#include "AssetLib/Irr/IRRLoader.h" #include "AssetLib/Irr/IRRLoader.h"
@ -62,8 +64,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/DefaultLogger.hpp> #include <assimp/DefaultLogger.hpp>
#include <assimp/IOSystem.hpp> #include <assimp/IOSystem.hpp>
#include <memory>
using namespace Assimp; using namespace Assimp;
static const aiImporterDesc desc = { static const aiImporterDesc desc = {
@ -380,7 +380,6 @@ void IRRImporter::ComputeAnimations(Node *root, aiNode *real, std::vector<aiNode
if (360 == lcm) if (360 == lcm)
break; break;
// find out how many time units we'll need for the finest // find out how many time units we'll need for the finest
// track (in seconds) - this defines the number of output // track (in seconds) - this defines the number of output
// keys (fps * seconds) // keys (fps * seconds)
@ -836,175 +835,164 @@ void IRRImporter::GenerateGraph(Node *root, aiNode *rootOut, aiScene *scene,
} }
} }
// ------------------------------------------------------------------------------------------------ void IRRImporter::ParseNodeAttributes(pugi::xml_node &attributesNode, IRRImporter::Node *nd, BatchLoader &batch) {
// Imports the given file into the given scene structure. ai_assert(!ASSIMP_stricmp(attributesNode.name(), "attributes")); // Node must be <attributes>
void IRRImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) { ai_assert(nd != nullptr); // dude
std::unique_ptr<IOStream> file(pIOHandler->Open(pFile));
// Check whether we can read from the file // Big switch statement that tests for various tags inside <attributes>
if (file == nullptr) { // and applies them to nd
throw DeadlyImportError("Failed to open IRR file ", pFile); // I don't believe nodes have boolean attributes
for (pugi::xml_node &attribute : attributesNode.children()) {
if (attribute.type() != pugi::node_element) continue;
if (!ASSIMP_stricmp(attribute.name(), "vector3d")) { // <vector3d />
VectorProperty prop;
ReadVectorProperty(prop, attribute);
if (prop.name == "Position") {
nd->position = prop.value;
} else if (prop.name == "Rotation") {
nd->rotation = prop.value;
} else if (prop.name == "Scale") {
nd->scaling = prop.value;
} else if (Node::CAMERA == nd->type) {
aiCamera *cam = cameras.back();
if (prop.name == "Target") {
cam->mLookAt = prop.value;
} else if (prop.name == "UpVector") {
cam->mUp = prop.value;
} }
// Construct the irrXML parser
XmlParser st;
if (!st.parse( file.get() )) {
throw DeadlyImportError("XML parse error while loading IRR file ", pFile);
} }
pugi::xml_node rootElement = st.getRootNode(); } else if (!ASSIMP_stricmp(attribute.name(), "float")) { // <float />
FloatProperty prop;
// The root node of the scene ReadFloatProperty(prop, attribute);
Node *root = new Node(Node::DUMMY); if (prop.name == "FramesPerSecond" && Node::ANIMMESH == nd->type) {
root->parent = nullptr; nd->framesPerSecond = prop.value;
root->name = "<IRRSceneRoot>"; } else if (Node::CAMERA == nd->type) {
/* This is the vertical, not the horizontal FOV.
// Current node parent * We need to compute the right FOV from the
Node *curParent = root; * screen aspect which we don't know yet.
// Scene-graph node we're currently working on
Node *curNode = nullptr;
// List of output cameras
std::vector<aiCamera *> cameras;
// List of output lights
std::vector<aiLight *> lights;
// Batch loader used to load external models
BatchLoader batch(pIOHandler);
//batch.SetBasePath(pFile);
cameras.reserve(5);
lights.reserve(5);
bool inMaterials = false, inAnimator = false;
unsigned int guessedAnimCnt = 0, guessedMeshCnt = 0, guessedMatCnt = 0;
// Parse the XML file
//while (reader->read()) {
for (pugi::xml_node child : rootElement.children())
switch (child.type()) {
case pugi::node_element:
if (!ASSIMP_stricmp(child.name(), "node")) {
// ***********************************************************************
/* What we're going to do with the node depends
* on its type:
*
* "mesh" - Load a mesh from an external file
* "cube" - Generate a cube
* "skybox" - Generate a skybox
* "light" - A light source
* "sphere" - Generate a sphere mesh
* "animatedMesh" - Load an animated mesh from an external file
* and join its animation channels with ours.
* "empty" - A dummy node
* "camera" - A camera
* "terrain" - a terrain node (data comes from a heightmap)
* "billboard", ""
*
* Each of these nodes can be animated and all can have multiple
* materials assigned (except lights, cameras and dummies, of course).
*/ */
// *********************************************************************** if (prop.name == "Fovy") {
//const char *sz = reader->getAttributeValueSafe("type"); cameras.back()->mHorizontalFOV = prop.value;
pugi::xml_attribute attrib = child.attribute("type"); } else if (prop.name == "Aspect") {
Node *nd; cameras.back()->mAspect = prop.value;
if (!ASSIMP_stricmp(attrib.name(), "mesh") || !ASSIMP_stricmp(attrib.name(), "octTree")) { } else if (prop.name == "ZNear") {
// OctTree's and meshes are treated equally cameras.back()->mClipPlaneNear = prop.value;
nd = new Node(Node::MESH); } else if (prop.name == "ZFar") {
} else if (!ASSIMP_stricmp(attrib.name(), "cube")) { cameras.back()->mClipPlaneFar = prop.value;
nd = new Node(Node::CUBE); }
++guessedMeshCnt; } else if (Node::LIGHT == nd->type) {
} else if (!ASSIMP_stricmp(attrib.name(), "skybox")) { /* Additional light information
nd = new Node(Node::SKYBOX); */
guessedMeshCnt += 6; if (prop.name == "Attenuation") {
} else if (!ASSIMP_stricmp(attrib.name(), "camera")) { lights.back()->mAttenuationLinear = prop.value;
nd = new Node(Node::CAMERA); } else if (prop.name == "OuterCone") {
lights.back()->mAngleOuterCone = AI_DEG_TO_RAD(prop.value);
} else if (prop.name == "InnerCone") {
lights.back()->mAngleInnerCone = AI_DEG_TO_RAD(prop.value);
}
}
// radius of the sphere to be generated -
// or alternatively, size of the cube
else if ((Node::SPHERE == nd->type && prop.name == "Radius") ||
(Node::CUBE == nd->type && prop.name == "Size")) {
nd->sphereRadius = prop.value;
}
} else if (!ASSIMP_stricmp(attribute.name(), "int")) { // <int />
// Only sphere nodes make use of integer attributes
if (Node::SPHERE == nd->type) {
IntProperty prop;
ReadIntProperty(prop, attribute);
if (prop.name == "PolyCountX") {
nd->spherePolyCountX = prop.value;
} else if (prop.name == "PolyCountY") {
nd->spherePolyCountY = prop.value;
}
}
} else if (!ASSIMP_stricmp(attribute.name(), "string") || !ASSIMP_stricmp(attribute.name(), "enum")) { // <string /> or < enum />
StringProperty prop;
ReadStringProperty(prop, attribute);
if (prop.value.length() == 0) continue; // skip empty strings
if (prop.name == "Name") {
nd->name = prop.value;
// Setup a temporary name for the camera /* If we're either a camera or a light source
aiCamera *cam = new aiCamera(); * we need to update the name in the aiLight/
cam->mName.Set(nd->name); * aiCamera structure, too.
cameras.push_back(cam); */
} else if (!ASSIMP_stricmp(attrib.name(), "light")) { if (Node::CAMERA == nd->type) {
nd = new Node(Node::LIGHT); cameras.back()->mName.Set(prop.value);
} else if (Node::LIGHT == nd->type) {
lights.back()->mName.Set(prop.value);
}
} else if (Node::LIGHT == nd->type && "LightType" == prop.name) {
if (prop.value == "Spot")
lights.back()->mType = aiLightSource_SPOT;
else if (prop.value == "Point")
lights.back()->mType = aiLightSource_POINT;
else if (prop.value == "Directional")
lights.back()->mType = aiLightSource_DIRECTIONAL;
else {
// We won't pass the validation with aiLightSourceType_UNDEFINED,
// so we remove the light and replace it with a silly dummy node
delete lights.back();
lights.pop_back();
nd->type = Node::DUMMY;
// Setup a temporary name for the light ASSIMP_LOG_ERROR("Ignoring light of unknown type: ", prop.value);
aiLight *cam = new aiLight(); }
cam->mName.Set(nd->name); } else if ((prop.name == "Mesh" && Node::MESH == nd->type) ||
lights.push_back(cam); Node::ANIMMESH == nd->type) {
} else if (!ASSIMP_stricmp(attrib.name(), "sphere")) { /* This is the file name of the mesh - either
nd = new Node(Node::SPHERE); * animated or not. We need to make sure we setup
++guessedMeshCnt; * the correct post-processing settings here.
} else if (!ASSIMP_stricmp(attrib.name(), "animatedMesh")) { */
nd = new Node(Node::ANIMMESH); unsigned int pp = 0;
} else if (!ASSIMP_stricmp(attrib.name(), "empty")) { BatchLoader::PropertyMap map;
nd = new Node(Node::DUMMY);
} else if (!ASSIMP_stricmp(attrib.name(), "terrain")) { /* If the mesh is a static one remove all animations from the impor data
nd = new Node(Node::TERRAIN); */
} else if (!ASSIMP_stricmp(attrib.name(), "billBoard")) { if (Node::ANIMMESH != nd->type) {
// We don't support billboards, so ignore them pp |= aiProcess_RemoveComponent;
ASSIMP_LOG_ERROR("IRR: Billboards are not supported by Assimp"); SetGenericProperty<int>(map.ints, AI_CONFIG_PP_RVC_FLAGS,
nd = new Node(Node::DUMMY); aiComponent_ANIMATIONS | aiComponent_BONEWEIGHTS);
}
/* TODO: maybe implement the protection against recursive
* loading calls directly in BatchLoader? The current
* implementation is not absolutely safe. A LWS and an IRR
* file referencing each other *could* cause the system to
* recurse forever.
*/
const std::string extension = GetExtension(prop.value);
if ("irr" == extension) {
ASSIMP_LOG_ERROR("IRR: Can't load another IRR file recursively");
} else { } else {
ASSIMP_LOG_WARN("IRR: Found unknown node: ", attrib.name()); nd->id = batch.AddLoadRequest(prop.value, pp, &map);
nd->meshPath = prop.value;
/* We skip the contents of nodes we don't know. }
* We parse the transformation and all animators }
* and skip the rest. }
*/ }
nd = new Node(Node::DUMMY);
}
/* Attach the newly created node to the scene-graph
*/
curNode = nd;
nd->parent = curParent;
curParent->children.push_back(nd);
} else if (!ASSIMP_stricmp(child.name(), "materials")) {
inMaterials = true;
} else if (!ASSIMP_stricmp(child.name(), "animators")) {
inAnimator = true;
} else if (!ASSIMP_stricmp(child.name(), "attributes")) {
// We should have a valid node here
// FIX: no ... the scene root node is also contained in an attributes block
if (!curNode) {
continue;
} }
void IRRImporter::ParseAnimators(pugi::xml_node &animatorNode, IRRImporter::Node *nd) {
Animator *curAnim = nullptr; Animator *curAnim = nullptr;
// Make empty animator
// Materials can occur for nearly any type of node nd->animators.emplace_back();
if (inMaterials && curNode->type != Node::DUMMY) { curAnim = &nd->animators.back(); // Push it back
// This is a material description - parse it! pugi::xml_node attributes = animatorNode.child("attributes");
curNode->materials.emplace_back(); if (!attributes) {
std::pair<aiMaterial *, unsigned int> &p = curNode->materials.back(); ASSIMP_LOG_WARN("Animator node does not contain attributes. ");
return;
p.first = ParseMaterial(p.second);
++guessedMatCnt;
continue;
} else if (inAnimator) {
// This is an animation path - add a new animator
// to the list.
curNode->animators.emplace_back();
curAnim = &curNode->animators.back();
++guessedAnimCnt;
} }
/* Parse all elements in the attributes block for (pugi::xml_node attrib : attributes.children()) {
* and process them. // XML may contain useless noes like CDATA
*/
// while (reader->read()) {
for (pugi::xml_node attrib : child.children()) {
if (attrib.type() == pugi::node_element) {
//if (reader->getNodeType() == EXN_ELEMENT) {
//if (!ASSIMP_stricmp(reader->getNodeName(), "vector3d")) {
if (!ASSIMP_stricmp(attrib.name(), "vector3d")) { if (!ASSIMP_stricmp(attrib.name(), "vector3d")) {
VectorProperty prop; VectorProperty prop;
ReadVectorProperty(prop); ReadVectorProperty(prop, attrib);
if (inAnimator) {
if (curAnim->type == Animator::ROTATION && prop.name == "Rotation") { if (curAnim->type == Animator::ROTATION && prop.name == "Rotation") {
// We store the rotation euler angles in 'direction' // We store the rotation euler angles in 'direction'
curAnim->direction = prop.value; curAnim->direction = prop.value;
@ -1041,36 +1029,20 @@ void IRRImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
curAnim->direction = prop.value; curAnim->direction = prop.value;
} }
} }
} else {
if (prop.name == "Position") {
curNode->position = prop.value;
} else if (prop.name == "Rotation") {
curNode->rotation = prop.value;
} else if (prop.name == "Scale") {
curNode->scaling = prop.value;
} else if (Node::CAMERA == curNode->type) {
aiCamera *cam = cameras.back();
if (prop.name == "Target") {
cam->mLookAt = prop.value;
} else if (prop.name == "UpVector") {
cam->mUp = prop.value;
}
}
}
//} else if (!ASSIMP_stricmp(reader->getNodeName(), "bool")) { //} else if (!ASSIMP_stricmp(reader->getNodeName(), "bool")) {
} else if (!ASSIMP_stricmp(attrib.name(), "bool")) { } else if (!ASSIMP_stricmp(attrib.name(), "bool")) {
BoolProperty prop; BoolProperty prop;
ReadBoolProperty(prop); ReadBoolProperty(prop, attrib);
if (inAnimator && curAnim->type == Animator::FLY_CIRCLE && prop.name == "Loop") { if (curAnim->type == Animator::FLY_CIRCLE && prop.name == "Loop") {
curAnim->loop = prop.value; curAnim->loop = prop.value;
} }
//} else if (!ASSIMP_stricmp(reader->getNodeName(), "float")) { //} else if (!ASSIMP_stricmp(reader->getNodeName(), "float")) {
} else if (!ASSIMP_stricmp(attrib.name(), "float")) { } else if (!ASSIMP_stricmp(attrib.name(), "float")) {
FloatProperty prop; FloatProperty prop;
ReadFloatProperty(prop); ReadFloatProperty(prop, attrib);
if (inAnimator) {
// The speed property exists for several animators // The speed property exists for several animators
if (prop.name == "Speed") { if (prop.name == "Speed") {
curAnim->speed = prop.value; curAnim->speed = prop.value;
@ -1079,126 +1051,20 @@ void IRRImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
} else if (curAnim->type == Animator::FOLLOW_SPLINE && prop.name == "Tightness") { } else if (curAnim->type == Animator::FOLLOW_SPLINE && prop.name == "Tightness") {
curAnim->tightness = prop.value; curAnim->tightness = prop.value;
} }
} else {
if (prop.name == "FramesPerSecond" && Node::ANIMMESH == curNode->type) {
curNode->framesPerSecond = prop.value;
} else if (Node::CAMERA == curNode->type) {
/* This is the vertical, not the horizontal FOV.
* We need to compute the right FOV from the
* screen aspect which we don't know yet.
*/
if (prop.name == "Fovy") {
cameras.back()->mHorizontalFOV = prop.value;
} else if (prop.name == "Aspect") {
cameras.back()->mAspect = prop.value;
} else if (prop.name == "ZNear") {
cameras.back()->mClipPlaneNear = prop.value;
} else if (prop.name == "ZFar") {
cameras.back()->mClipPlaneFar = prop.value;
}
} else if (Node::LIGHT == curNode->type) {
/* Additional light information
*/
if (prop.name == "Attenuation") {
lights.back()->mAttenuationLinear = prop.value;
} else if (prop.name == "OuterCone") {
lights.back()->mAngleOuterCone = AI_DEG_TO_RAD(prop.value);
} else if (prop.name == "InnerCone") {
lights.back()->mAngleInnerCone = AI_DEG_TO_RAD(prop.value);
}
}
// radius of the sphere to be generated -
// or alternatively, size of the cube
else if ((Node::SPHERE == curNode->type && prop.name == "Radius") || (Node::CUBE == curNode->type && prop.name == "Size")) {
curNode->sphereRadius = prop.value;
}
}
//} else if (!ASSIMP_stricmp(reader->getNodeName(), "int")) { //} else if (!ASSIMP_stricmp(reader->getNodeName(), "int")) {
} else if (!ASSIMP_stricmp(attrib.name(), "int")) { } else if (!ASSIMP_stricmp(attrib.name(), "int")) {
IntProperty prop; IntProperty prop;
ReadIntProperty(prop); ReadIntProperty(prop, attrib);
if (inAnimator) {
if (curAnim->type == Animator::FLY_STRAIGHT && prop.name == "TimeForWay") { if (curAnim->type == Animator::FLY_STRAIGHT && prop.name == "TimeForWay") {
curAnim->timeForWay = prop.value; curAnim->timeForWay = prop.value;
} }
} else {
// sphere polygon numbers in each direction
if (Node::SPHERE == curNode->type) {
if (prop.name == "PolyCountX") {
curNode->spherePolyCountX = prop.value;
} else if (prop.name == "PolyCountY") {
curNode->spherePolyCountY = prop.value;
}
}
}
//} else if (!ASSIMP_stricmp(reader->getNodeName(), "string") || !ASSIMP_stricmp(reader->getNodeName(), "enum")) { //} else if (!ASSIMP_stricmp(reader->getNodeName(), "string") || !ASSIMP_stricmp(reader->getNodeName(), "enum")) {
} else if (!ASSIMP_stricmp(attrib.name(), "string") || !ASSIMP_stricmp(attrib.name(), "enum")) { } else if (!ASSIMP_stricmp(attrib.name(), "string") || !ASSIMP_stricmp(attrib.name(), "enum")) {
StringProperty prop; StringProperty prop;
ReadStringProperty(prop); ReadStringProperty(prop, attrib);
if (prop.value.length()) {
if (prop.name == "Name") {
curNode->name = prop.value;
/* If we're either a camera or a light source if (prop.name == "Type") {
* we need to update the name in the aiLight/
* aiCamera structure, too.
*/
if (Node::CAMERA == curNode->type) {
cameras.back()->mName.Set(prop.value);
} else if (Node::LIGHT == curNode->type) {
lights.back()->mName.Set(prop.value);
}
} else if (Node::LIGHT == curNode->type && "LightType" == prop.name) {
if (prop.value == "Spot")
lights.back()->mType = aiLightSource_SPOT;
else if (prop.value == "Point")
lights.back()->mType = aiLightSource_POINT;
else if (prop.value == "Directional")
lights.back()->mType = aiLightSource_DIRECTIONAL;
else {
// We won't pass the validation with aiLightSourceType_UNDEFINED,
// so we remove the light and replace it with a silly dummy node
delete lights.back();
lights.pop_back();
curNode->type = Node::DUMMY;
ASSIMP_LOG_ERROR("Ignoring light of unknown type: ", prop.value);
}
} else if ((prop.name == "Mesh" && Node::MESH == curNode->type) ||
Node::ANIMMESH == curNode->type) {
/* This is the file name of the mesh - either
* animated or not. We need to make sure we setup
* the correct post-processing settings here.
*/
unsigned int pp = 0;
BatchLoader::PropertyMap map;
/* If the mesh is a static one remove all animations from the impor data
*/
if (Node::ANIMMESH != curNode->type) {
pp |= aiProcess_RemoveComponent;
SetGenericProperty<int>(map.ints, AI_CONFIG_PP_RVC_FLAGS,
aiComponent_ANIMATIONS | aiComponent_BONEWEIGHTS);
}
/* TODO: maybe implement the protection against recursive
* loading calls directly in BatchLoader? The current
* implementation is not absolutely safe. A LWS and an IRR
* file referencing each other *could* cause the system to
* recurse forever.
*/
const std::string extension = GetExtension(prop.value);
if ("irr" == extension) {
ASSIMP_LOG_ERROR("IRR: Can't load another IRR file recursively");
} else {
curNode->id = batch.AddLoadRequest(prop.value, pp, &map);
curNode->meshPath = prop.value;
}
} else if (inAnimator && prop.name == "Type") {
// type of the animator // type of the animator
if (prop.value == "rotation") { if (prop.value == "rotation") {
curAnim->type = Animator::ROTATION; curAnim->type = Animator::ROTATION;
@ -1216,42 +1082,168 @@ void IRRImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
} }
} }
} }
//} else if (reader->getNodeType() == EXN_ELEMENT_END && !ASSIMP_stricmp(reader->getNodeName(), "attributes")) {
} else if (attrib.type() == pugi::node_null && !ASSIMP_stricmp(attrib.name(), "attributes")) {
break;
} }
}
}
break;
/*case EXN_ELEMENT_END: IRRImporter::Node *IRRImporter::ParseNode(pugi::xml_node &node, BatchLoader &batch) {
// Parse <node> tags.
// <node> tags have various types
// <node> tags can contain <attribute>, <material>
// they can also contain other <node> tags, (and can reference other files as well?)
// ***********************************************************************
/* What we're going to do with the node depends
* on its type:
*
* "mesh" - Load a mesh from an external file
* "cube" - Generate a cube
* "skybox" - Generate a skybox
* "light" - A light source
* "sphere" - Generate a sphere mesh
* "animatedMesh" - Load an animated mesh from an external file
* and join its animation channels with ours.
* "empty" - A dummy node
* "camera" - A camera
* "terrain" - a terrain node (data comes from a heightmap)
* "billboard", ""
*
* Each of these nodes can be animated and all can have multiple
* materials assigned (except lights, cameras and dummies, of course).
* Said materials and animators are all collected at the bottom
*/
// ***********************************************************************
Node *nd;
pugi::xml_attribute nodeTypeAttrib = node.attribute("type");
if (!ASSIMP_stricmp(nodeTypeAttrib.value(), "mesh") || !ASSIMP_stricmp(nodeTypeAttrib.value(), "octTree")) {
// OctTree's and meshes are treated equally
nd = new Node(Node::MESH);
} else if (!ASSIMP_stricmp(nodeTypeAttrib.value(), "cube")) {
nd = new Node(Node::CUBE);
guessedMeshCnt += 1; // Cube is only one mesh
} else if (!ASSIMP_stricmp(nodeTypeAttrib.value(), "skybox")) {
nd = new Node(Node::SKYBOX);
guessedMeshCnt += 6; // Skybox is a box, with 6 meshes?
} else if (!ASSIMP_stricmp(nodeTypeAttrib.value(), "camera")) {
nd = new Node(Node::CAMERA);
// Setup a temporary name for the camera
aiCamera *cam = new aiCamera();
cam->mName.Set(nd->name);
cameras.push_back(cam);
} else if (!ASSIMP_stricmp(nodeTypeAttrib.value(), "light")) {
nd = new Node(Node::LIGHT);
// Setup a temporary name for the light
aiLight *cam = new aiLight();
cam->mName.Set(nd->name);
lights.push_back(cam);
} else if (!ASSIMP_stricmp(nodeTypeAttrib.value(), "sphere")) {
nd = new Node(Node::SPHERE);
guessedMeshCnt += 1;
} else if (!ASSIMP_stricmp(nodeTypeAttrib.value(), "animatedMesh")) {
nd = new Node(Node::ANIMMESH);
} else if (!ASSIMP_stricmp(nodeTypeAttrib.value(), "empty")) {
nd = new Node(Node::DUMMY);
} else if (!ASSIMP_stricmp(nodeTypeAttrib.value(), "terrain")) {
nd = new Node(Node::TERRAIN);
} else if (!ASSIMP_stricmp(nodeTypeAttrib.value(), "billBoard")) {
// We don't support billboards, so ignore them
ASSIMP_LOG_ERROR("IRR: Billboards are not supported by Assimp");
nd = new Node(Node::DUMMY);
} else {
ASSIMP_LOG_WARN("IRR: Found unknown node: ", nodeTypeAttrib.value());
// If we reached the end of a node, we need to continue processing its parent /* We skip the contents of nodes we don't know.
if (!ASSIMP_stricmp(reader->getNodeName(), "node")) { * We parse the transformation and all animators
if (!curNode) { * and skip the rest.
// currently is no node set. We need to go */
// back in the node hierarchy nd = new Node(Node::DUMMY);
if (!curParent) {
curParent = root;
ASSIMP_LOG_ERROR("IRR: Too many closing <node> elements");
} else
curParent = curParent->parent;
} else
curNode = nullptr;
} }
// clear all flags
else if (!ASSIMP_stricmp(reader->getNodeName(), "materials")) {
inMaterials = false;
} else if (!ASSIMP_stricmp(reader->getNodeName(), "animators")) {
inAnimator = false;
}
break;*/
default: // TODO: consolidate all into one loop
// GCC complains that not all enumeration values are handled for (pugi::xml_node subNode : node.children()) {
break; // Collect node attributes first
if (!ASSIMP_stricmp(subNode.name(), "attributes")) {
ParseNodeAttributes(subNode, nd, batch); // Parse attributes into this node
} else if (!ASSIMP_stricmp(subNode.name(), "animators")) {
// Then parse any animators
// All animators should contain an <attributes> tag
// This is an animation path - add a new animator
// to the list.
ParseAnimators(subNode, nd); // Function modifies nd's animator vector
guessedAnimCnt += 1;
}
// Then parse any materials
// Materials are available to almost all node types
if (nd->type != Node::DUMMY) {
if (!ASSIMP_stricmp(subNode.name(), "materials")) {
// Parse material description directly
// Each material should contain an <attributes> node
// with everything specified
nd->materials.emplace_back();
std::pair<aiMaterial *, unsigned int> &p = nd->materials.back();
p.first = ParseMaterial(subNode, p.second);
guessedMatCnt += 1;
}
}
}
// Then parse any child nodes
// Attach the newly created node to the scene-graph
for (pugi::xml_node child : node.children()) {
if (!ASSIMP_stricmp(child.name(), "node")) { // Is a child node
Node *childNd = ParseNode(child, batch); // Repeat this function for all children
nd->children.push_back(childNd);
};
}
// Return fully specified node
return nd;
}
// ------------------------------------------------------------------------------------------------
// Imports the given file into the given scene structure.
void IRRImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
std::unique_ptr<IOStream> file(pIOHandler->Open(pFile));
// Check whether we can read from the file
if (file == nullptr) {
throw DeadlyImportError("Failed to open IRR file ", pFile);
}
// Construct the irrXML parser
XmlParser st;
if (!st.parse(file.get())) {
throw DeadlyImportError("XML parse error while loading IRR file ", pFile);
}
pugi::xml_node documentRoot = st.getRootNode();
// The root node of the scene
Node *root = new Node(Node::DUMMY);
root->parent = nullptr;
root->name = "<IRRSceneRoot>";
// Batch loader used to load external models
BatchLoader batch(pIOHandler);
// batch.SetBasePath(pFile);
cameras.reserve(1); // Probably only one camera in entire scene
lights.reserve(5);
this->guessedAnimCnt = 0;
this->guessedMeshCnt = 0;
this->guessedMatCnt = 0;
// Parse the XML
// Find the scene root from document root.
const pugi::xml_node &sceneRoot = documentRoot.child("irr_scene");
if (!sceneRoot) throw new DeadlyImportError("IRR: <irr_scene> not found in file");
for (pugi::xml_node &child : sceneRoot.children()) {
// XML elements are either nodes, animators, attributes, or materials
if (!ASSIMP_stricmp(child.name(), "node")) {
// Recursive collect subtree children
Node *nd = ParseNode(child, batch);
// Attach to root
root->children.push_back(nd);
}
} }
//}
// Now iterate through all cameras and compute their final (horizontal) FOV // Now iterate through all cameras and compute their final (horizontal) FOV
for (aiCamera *cam : cameras) { for (aiCamera *cam : cameras) {
@ -1337,8 +1329,7 @@ void IRRImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
// Now merge all sub scenes and attach them to the correct // Now merge all sub scenes and attach them to the correct
// attachment points in the scenegraph. // attachment points in the scenegraph.
SceneCombiner::MergeScenes(&pScene, tempScene, attach, SceneCombiner::MergeScenes(&pScene, tempScene, attach,
AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES | (!configSpeedFlag ? ( AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES | (!configSpeedFlag ? (AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY | AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES) :
AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY | AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES) :
0)); 0));
// If we have no meshes | no materials now set the INCOMPLETE // If we have no meshes | no materials now set the INCOMPLETE

View File

@ -94,18 +94,10 @@ private:
} type; } type;
explicit Animator(AT t = UNKNOWN) explicit Animator(AT t = UNKNOWN) :
: type (t) type(t), speed(ai_real(0.001)), direction(ai_real(0.0), ai_real(1.0), ai_real(0.0)), circleRadius(ai_real(1.0)), tightness(ai_real(0.5)), loop(true), timeForWay(100) {
, speed ( ai_real( 0.001 ) )
, direction ( ai_real( 0.0 ), ai_real( 1.0 ), ai_real( 0.0 ) )
, circleRadius ( ai_real( 1.0) )
, tightness ( ai_real( 0.5 ) )
, loop (true)
, timeForWay (100)
{
} }
// common parameters // common parameters
ai_real speed; ai_real speed;
aiVector3D direction; aiVector3D direction;
@ -128,11 +120,9 @@ private:
/** Data structure for a scene-graph node in an IRR file /** Data structure for a scene-graph node in an IRR file
*/ */
struct Node struct Node {
{
// Type of the node // Type of the node
enum ET enum ET {
{
LIGHT, LIGHT,
CUBE, CUBE,
MESH, MESH,
@ -144,16 +134,15 @@ private:
ANIMMESH ANIMMESH
} type; } type;
explicit Node(ET t) explicit Node(ET t) :
: type (t) type(t), scaling(1.0, 1.0, 1.0) // assume uniform scaling by default
, scaling (1.0,1.0,1.0) // assume uniform scaling by default ,
, parent() parent(),
, framesPerSecond (0.0) framesPerSecond(0.0),
, id() id(),
, sphereRadius (1.0) sphereRadius(1.0),
, spherePolyCountX (100) spherePolyCountX(100),
, spherePolyCountY (100) spherePolyCountY(100) {
{
// Generate a default name for the node // Generate a default name for the node
char buffer[128]; char buffer[128];
@ -204,8 +193,7 @@ private:
/** Data structure for a vertex in an IRR skybox /** Data structure for a vertex in an IRR skybox
*/ */
struct SkyboxVertex struct SkyboxVertex {
{
SkyboxVertex() = default; SkyboxVertex() = default;
//! Construction from single vertex components //! Construction from single vertex components
@ -213,14 +201,29 @@ private:
ai_real nx, ai_real ny, ai_real nz, ai_real nx, ai_real ny, ai_real nz,
ai_real uvx, ai_real uvy) ai_real uvx, ai_real uvy)
: position (px,py,pz) :
, normal (nx,ny,nz) position(px, py, pz), normal(nx, ny, nz), uv(uvx, uvy, 0.0) {}
, uv (uvx,uvy,0.0)
{}
aiVector3D position, normal, uv; aiVector3D position, normal, uv;
}; };
// -------------------------------------------------------------------
// Parse <node> tag from XML file and extract child node
// @param node XML node
// @param guessedMeshesContained number of extra guessed meshes
IRRImporter::Node *ParseNode(pugi::xml_node &node, BatchLoader& batch);
// -------------------------------------------------------------------
// Parse <attributes> tags within <node> tags and apply to scene node
// @param attributeNode XML child node
// @param nd Attributed scene node
void ParseNodeAttributes(pugi::xml_node &attributeNode, IRRImporter::Node *nd, BatchLoader& batch);
// -------------------------------------------------------------------
// Parse an <animator> node and attach an animator to a node
// @param animatorNode XML animator node
// @param nd Animated scene node
void ParseAnimators(pugi::xml_node &animatorNode, IRRImporter::Node *nd);
// ------------------------------------------------------------------- // -------------------------------------------------------------------
/// Fill the scene-graph recursively /// Fill the scene-graph recursively
@ -276,6 +279,12 @@ private:
/// Configuration option: speed flag was set? /// Configuration option: speed flag was set?
bool configSpeedFlag; bool configSpeedFlag;
std::vector<aiCamera*> cameras;
std::vector<aiLight*> lights;
unsigned int guessedMeshCnt;
unsigned int guessedMatCnt;
unsigned int guessedAnimCnt;
}; };
} // end of namespace Assimp } // end of namespace Assimp

View File

@ -133,6 +133,7 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile,
meshes.reserve(5); meshes.reserve(5);
// temporary data - current mesh buffer // temporary data - current mesh buffer
// TODO move all these to inside loop
aiMaterial *curMat = nullptr; aiMaterial *curMat = nullptr;
aiMesh *curMesh = nullptr; aiMesh *curMesh = nullptr;
unsigned int curMatFlags = 0; unsigned int curMatFlags = 0;
@ -142,23 +143,28 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile,
std::vector<aiVector3D> curUVs, curUV2s; std::vector<aiVector3D> curUVs, curUV2s;
// some temporary variables // some temporary variables
int textMeaning = 0; // textMeaning is a 15 year old variable, that could've been an enum
int vertexFormat = 0; // 0 = normal; 1 = 2 tcoords, 2 = tangents // int textMeaning = 0; // 0=none? 1=vertices 2=indices
// int vertexFormat = 0; // 0 = normal; 1 = 2 tcoords, 2 = tangents
bool useColors = false; bool useColors = false;
/*
** irrmesh files have a top level <mesh> owning multiple <buffer> nodes.
** Each <buffer> contains <material>, <vertices>, and <indices>
** <material> tags here directly owns the material data specs
** <vertices> are a vertex per line, contains position, UV1 coords, maybe UV2, normal, tangent, bitangent
** <boundingbox> is ignored, I think assimp recalculates those?
*/
// Parse the XML file // Parse the XML file
for (pugi::xml_node child : root.children()) { pugi::xml_node const &meshNode = root.child("mesh");
if (child.type() == pugi::node_element) { for (pugi::xml_node bufferNode : meshNode.children()) {
if (!ASSIMP_stricmp(child.name(), "buffer") && (curMat || curMesh)) { if (ASSIMP_stricmp(bufferNode.name(), "buffer")) {
// end of previous buffer. A material and a mesh should be there // Might be a useless warning
if (!curMat || !curMesh) { ASSIMP_LOG_WARN("IRRMESH: Ignoring non buffer node <", bufferNode.name(), "> in mesh declaration");
ASSIMP_LOG_ERROR("IRRMESH: A buffer must contain a mesh and a material"); continue;
releaseMaterial(&curMat);
releaseMesh(&curMesh);
} else {
materials.push_back(curMat);
meshes.push_back(curMesh);
} }
curMat = nullptr; curMat = nullptr;
curMesh = nullptr; curMesh = nullptr;
@ -169,42 +175,47 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile,
curUVs.clear(); curUVs.clear();
curTangents.clear(); curTangents.clear();
curBitangents.clear(); curBitangents.clear();
}
if (!ASSIMP_stricmp(child.name(), "material")) { // TODO ensure all three nodes are present and populated
if (curMat) { // before allocating everything
// Get first material node
pugi::xml_node materialNode = bufferNode.child("material");
if (materialNode) {
curMat = ParseMaterial(materialNode, curMatFlags);
// Warn if there's more materials
if (materialNode.next_sibling("material")) {
ASSIMP_LOG_WARN("IRRMESH: Only one material description per buffer, please"); ASSIMP_LOG_WARN("IRRMESH: Only one material description per buffer, please");
releaseMaterial(&curMat);
} }
curMat = ParseMaterial(curMatFlags); } else {
} ASSIMP_LOG_ERROR("IRRMESH: Buffer must contain one material");
/* no else here! */ if (!ASSIMP_stricmp(child.name(), "vertices")) {
pugi::xml_attribute attr = child.attribute("vertexCount");
int num = attr.as_int();
//int num = reader->getAttributeValueAsInt("vertexCount");
if (!num) {
// This is possible ... remove the mesh from the list and skip further reading
ASSIMP_LOG_WARN("IRRMESH: Found mesh with zero vertices");
releaseMaterial(&curMat);
releaseMesh(&curMesh);
textMeaning = 0;
continue; continue;
} }
curVertices.reserve(num); // Get first vertices node
curNormals.reserve(num); pugi::xml_node verticesNode = bufferNode.child("vertices");
curColors.reserve(num); if (verticesNode) {
curUVs.reserve(num); pugi::xml_attribute vertexCountAttrib = verticesNode.attribute("vertexCount");
int vertexCount = vertexCountAttrib.as_int();
if (vertexCount == 0) {
// This is possible ... remove the mesh from the list and skip further reading
ASSIMP_LOG_WARN("IRRMESH: Found mesh with zero vertices");
releaseMaterial(&curMat);
// releaseMesh(&curMesh);
continue; // Bail out early
};
curVertices.reserve(vertexCount);
curNormals.reserve(vertexCount);
curColors.reserve(vertexCount);
curUVs.reserve(vertexCount);
VertexFormat vertexFormat;
// Determine the file format // Determine the file format
//const char *t = reader->getAttributeValueSafe("type"); pugi::xml_attribute typeAttrib = verticesNode.attribute("type");
pugi::xml_attribute t = child.attribute("type"); if (!ASSIMP_stricmp("2tcoords", typeAttrib.value())) {
if (!ASSIMP_stricmp("2tcoords", t.name())) { curUV2s.reserve(vertexCount);
curUV2s.reserve(num); vertexFormat = VertexFormat::t2coord;
vertexFormat = 1;
if (curMatFlags & AI_IRRMESH_EXTRA_2ND_TEXTURE) { if (curMatFlags & AI_IRRMESH_EXTRA_2ND_TEXTURE) {
// ********************************************************* // *********************************************************
// We have a second texture! So use this UV channel // We have a second texture! So use this UV channel
@ -223,29 +234,38 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile,
mat->AddProperty(&idx, 1, AI_MATKEY_UVWSRC_DIFFUSE(1)); mat->AddProperty(&idx, 1, AI_MATKEY_UVWSRC_DIFFUSE(1));
} }
} }
} else if (!ASSIMP_stricmp("tangents", t.name())) { } else if (!ASSIMP_stricmp("tangents", typeAttrib.value())) {
curTangents.reserve(num); curTangents.reserve(vertexCount);
curBitangents.reserve(num); curBitangents.reserve(vertexCount);
vertexFormat = 2; vertexFormat = VertexFormat::tangent;
} else if (ASSIMP_stricmp("standard", t.name())) { } else if (!ASSIMP_stricmp("standard", typeAttrib.value())) {
vertexFormat = VertexFormat::standard;
} else {
// Unsupported format, discard whole buffer/mesh
// Assuming we have a correct material, then release it
// We don't have a correct mesh for sure here
releaseMaterial(&curMat); releaseMaterial(&curMat);
ASSIMP_LOG_WARN("IRRMESH: Unknown vertex format"); ASSIMP_LOG_ERROR("IRRMESH: Unknown vertex format");
} else continue; // Skip rest of buffer
vertexFormat = 0; };
textMeaning = 1;
} else if (!ASSIMP_stricmp(child.name(), "indices")) { // We know what format buffer is, collect numbers
if (curVertices.empty() && curMat) { ParseBufferVertices(verticesNode.text().get(), vertexFormat,
releaseMaterial(&curMat); curVertices, curNormals,
throw DeadlyImportError("IRRMESH: indices must come after vertices"); curTangents, curBitangents,
curUVs, curUV2s, curColors, useColors);
} }
textMeaning = 2; // Get indices
// At this point we have some vertices and a valid material
// Collect indices and create aiMesh at the same time
pugi::xml_node indicesNode = bufferNode.child("indices");
if (indicesNode) {
// start a new mesh // start a new mesh
curMesh = new aiMesh(); curMesh = new aiMesh();
// allocate storage for all faces // allocate storage for all faces
pugi::xml_attribute attr = child.attribute("indexCount"); pugi::xml_attribute attr = indicesNode.attribute("indexCount");
curMesh->mNumVertices = attr.as_int(); curMesh->mNumVertices = attr.as_int();
if (!curMesh->mNumVertices) { if (!curMesh->mNumVertices) {
// This is possible ... remove the mesh from the list and skip further reading // This is possible ... remove the mesh from the list and skip further reading
@ -256,9 +276,7 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile,
// material - away // material - away
releaseMaterial(&curMat); releaseMaterial(&curMat);
continue; // Go to next buffer
textMeaning = 0;
continue;
} }
if (curMesh->mNumVertices % 3) { if (curMesh->mNumVertices % 3) {
@ -293,107 +311,6 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile,
if (curUV2s.size() == curVertices.size()) { if (curUV2s.size() == curVertices.size()) {
curMesh->mTextureCoords[1] = new aiVector3D[curMesh->mNumVertices]; curMesh->mTextureCoords[1] = new aiVector3D[curMesh->mNumVertices];
} }
}
//break;
//case EXN_TEXT: {
const char *sz = child.child_value();
if (textMeaning == 1) {
textMeaning = 0;
// read vertices
do {
SkipSpacesAndLineEnd(&sz);
aiVector3D temp;
aiColor4D c;
// Read the vertex position
sz = fast_atoreal_move<float>(sz, (float &)temp.x);
SkipSpaces(&sz);
sz = fast_atoreal_move<float>(sz, (float &)temp.y);
SkipSpaces(&sz);
sz = fast_atoreal_move<float>(sz, (float &)temp.z);
SkipSpaces(&sz);
curVertices.push_back(temp);
// Read the vertex normals
sz = fast_atoreal_move<float>(sz, (float &)temp.x);
SkipSpaces(&sz);
sz = fast_atoreal_move<float>(sz, (float &)temp.y);
SkipSpaces(&sz);
sz = fast_atoreal_move<float>(sz, (float &)temp.z);
SkipSpaces(&sz);
curNormals.push_back(temp);
// read the vertex colors
uint32_t clr = strtoul16(sz, &sz);
ColorFromARGBPacked(clr, c);
if (!curColors.empty() && c != *(curColors.end() - 1))
useColors = true;
curColors.push_back(c);
SkipSpaces(&sz);
// read the first UV coordinate set
sz = fast_atoreal_move<float>(sz, (float &)temp.x);
SkipSpaces(&sz);
sz = fast_atoreal_move<float>(sz, (float &)temp.y);
SkipSpaces(&sz);
temp.z = 0.f;
temp.y = 1.f - temp.y; // DX to OGL
curUVs.push_back(temp);
// read the (optional) second UV coordinate set
if (vertexFormat == 1) {
sz = fast_atoreal_move<float>(sz, (float &)temp.x);
SkipSpaces(&sz);
sz = fast_atoreal_move<float>(sz, (float &)temp.y);
temp.y = 1.f - temp.y; // DX to OGL
curUV2s.push_back(temp);
}
// read optional tangent and bitangent vectors
else if (vertexFormat == 2) {
// tangents
sz = fast_atoreal_move<float>(sz, (float &)temp.x);
SkipSpaces(&sz);
sz = fast_atoreal_move<float>(sz, (float &)temp.z);
SkipSpaces(&sz);
sz = fast_atoreal_move<float>(sz, (float &)temp.y);
SkipSpaces(&sz);
temp.y *= -1.0f;
curTangents.push_back(temp);
// bitangents
sz = fast_atoreal_move<float>(sz, (float &)temp.x);
SkipSpaces(&sz);
sz = fast_atoreal_move<float>(sz, (float &)temp.z);
SkipSpaces(&sz);
sz = fast_atoreal_move<float>(sz, (float &)temp.y);
SkipSpaces(&sz);
temp.y *= -1.0f;
curBitangents.push_back(temp);
}
}
/* IMPORTANT: We assume that each vertex is specified in one
line. So we can skip the rest of the line - unknown vertex
elements are ignored.
*/
while (SkipLine(&sz));
} else if (textMeaning == 2) {
textMeaning = 0;
// read indices // read indices
aiFace *curFace = curMesh->mFaces; aiFace *curFace = curMesh->mFaces;
@ -409,24 +326,33 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile,
unsigned int curIdx = 0; unsigned int curIdx = 0;
unsigned int total = 0; unsigned int total = 0;
// NOTE this might explode for UTF-16 and wchars
const char *sz = indicesNode.text().get();
// For each index loop over aiMesh faces
while (SkipSpacesAndLineEnd(&sz)) { while (SkipSpacesAndLineEnd(&sz)) {
if (curFace >= faceEnd) { if (curFace >= faceEnd) {
ASSIMP_LOG_ERROR("IRRMESH: Too many indices"); ASSIMP_LOG_ERROR("IRRMESH: Too many indices");
break; break;
} }
// if new face
if (!curIdx) { if (!curIdx) {
curFace->mNumIndices = 3; curFace->mNumIndices = 3;
curFace->mIndices = new unsigned int[3]; curFace->mIndices = new unsigned int[3];
} }
// Read index base 10
// function advances the pointer
unsigned int idx = strtoul10(sz, &sz); unsigned int idx = strtoul10(sz, &sz);
if (idx >= curVertices.size()) { if (idx >= curVertices.size()) {
ASSIMP_LOG_ERROR("IRRMESH: Index out of range"); ASSIMP_LOG_ERROR("IRRMESH: Index out of range");
idx = 0; idx = 0;
} }
// make up our own indices?
curFace->mIndices[curIdx] = total++; curFace->mIndices[curIdx] = total++;
// Copy over data to aiMesh
*pcV++ = curVertices[idx]; *pcV++ = curVertices[idx];
if (pcN) *pcN++ = curNormals[idx]; if (pcN) *pcN++ = curNormals[idx];
if (pcT) *pcT++ = curTangents[idx]; if (pcT) *pcT++ = curTangents[idx];
@ -435,14 +361,16 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile,
if (pcT0) *pcT0++ = curUVs[idx]; if (pcT0) *pcT0++ = curUVs[idx];
if (pcT1) *pcT1++ = curUV2s[idx]; if (pcT1) *pcT1++ = curUV2s[idx];
// start new face
if (++curIdx == 3) { if (++curIdx == 3) {
++curFace; ++curFace;
curIdx = 0; curIdx = 0;
} }
} }
// We should be at the end of mFaces
if (curFace != faceEnd) if (curFace != faceEnd)
ASSIMP_LOG_ERROR("IRRMESH: Not enough indices"); ASSIMP_LOG_ERROR("IRRMESH: Not enough indices");
}
// Finish processing the mesh - do some small material workarounds // Finish processing the mesh - do some small material workarounds
if (curMatFlags & AI_IRRMESH_MAT_trans_vertex_alpha && !useColors) { if (curMatFlags & AI_IRRMESH_MAT_trans_vertex_alpha && !useColors) {
@ -451,12 +379,9 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile,
aiMaterial *mat = (aiMaterial *)curMat; aiMaterial *mat = (aiMaterial *)curMat;
mat->AddProperty(&curColors[0].a, 1, AI_MATKEY_OPACITY); mat->AddProperty(&curColors[0].a, 1, AI_MATKEY_OPACITY);
} }
} // textMeaning = 2;
}
}
// End of the last buffer. A material and a mesh should be there // end of previous buffer. A material and a mesh should be there
if (curMat || curMesh) {
if (!curMat || !curMesh) { if (!curMat || !curMesh) {
ASSIMP_LOG_ERROR("IRRMESH: A buffer must contain a mesh and a material"); ASSIMP_LOG_ERROR("IRRMESH: A buffer must contain a mesh and a material");
releaseMaterial(&curMat); releaseMaterial(&curMat);
@ -467,7 +392,8 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile,
} }
} }
if (materials.empty()) { // If one is empty then so is the other
if (materials.empty() || meshes.empty()) {
throw DeadlyImportError("IRRMESH: Unable to read a mesh from this file"); throw DeadlyImportError("IRRMESH: Unable to read a mesh from this file");
} }
@ -492,7 +418,105 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile,
for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) { for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
pScene->mRootNode->mMeshes[i] = i; pScene->mRootNode->mMeshes[i] = i;
};
} }
void IRRMeshImporter::ParseBufferVertices(const char *sz, VertexFormat vertexFormat,
std::vector<aiVector3D> &vertices, std::vector<aiVector3D> &normals,
std::vector<aiVector3D> &tangents, std::vector<aiVector3D> &bitangents,
std::vector<aiVector3D> &UVs, std::vector<aiVector3D> &UV2s,
std::vector<aiColor4D> &colors, bool &useColors) {
// read vertices
do {
SkipSpacesAndLineEnd(&sz);
aiVector3D temp;
aiColor4D c;
// Read the vertex position
sz = fast_atoreal_move<float>(sz, (float &)temp.x);
SkipSpaces(&sz);
sz = fast_atoreal_move<float>(sz, (float &)temp.y);
SkipSpaces(&sz);
sz = fast_atoreal_move<float>(sz, (float &)temp.z);
SkipSpaces(&sz);
vertices.push_back(temp);
// Read the vertex normals
sz = fast_atoreal_move<float>(sz, (float &)temp.x);
SkipSpaces(&sz);
sz = fast_atoreal_move<float>(sz, (float &)temp.y);
SkipSpaces(&sz);
sz = fast_atoreal_move<float>(sz, (float &)temp.z);
SkipSpaces(&sz);
normals.push_back(temp);
// read the vertex colors
uint32_t clr = strtoul16(sz, &sz);
ColorFromARGBPacked(clr, c);
// If we're pushing more than one distinct color
if (!colors.empty() && c != *(colors.end() - 1))
useColors = true;
colors.push_back(c);
SkipSpaces(&sz);
// read the first UV coordinate set
sz = fast_atoreal_move<float>(sz, (float &)temp.x);
SkipSpaces(&sz);
sz = fast_atoreal_move<float>(sz, (float &)temp.y);
SkipSpaces(&sz);
temp.z = 0.f;
temp.y = 1.f - temp.y; // DX to OGL
UVs.push_back(temp);
// NOTE these correspond to specific S3DVertex* structs in irr sourcecode
// So by definition, all buffers have either UV2 or tangents or neither
// read the (optional) second UV coordinate set
if (vertexFormat == VertexFormat::t2coord) {
sz = fast_atoreal_move<float>(sz, (float &)temp.x);
SkipSpaces(&sz);
sz = fast_atoreal_move<float>(sz, (float &)temp.y);
temp.y = 1.f - temp.y; // DX to OGL
UV2s.push_back(temp);
}
// read optional tangent and bitangent vectors
else if (vertexFormat == VertexFormat::tangent) {
// tangents
sz = fast_atoreal_move<float>(sz, (float &)temp.x);
SkipSpaces(&sz);
sz = fast_atoreal_move<float>(sz, (float &)temp.z);
SkipSpaces(&sz);
sz = fast_atoreal_move<float>(sz, (float &)temp.y);
SkipSpaces(&sz);
temp.y *= -1.0f;
tangents.push_back(temp);
// bitangents
sz = fast_atoreal_move<float>(sz, (float &)temp.x);
SkipSpaces(&sz);
sz = fast_atoreal_move<float>(sz, (float &)temp.z);
SkipSpaces(&sz);
sz = fast_atoreal_move<float>(sz, (float &)temp.y);
SkipSpaces(&sz);
temp.y *= -1.0f;
bitangents.push_back(temp);
}
} while (SkipLine(&sz));
/* IMPORTANT: We assume that each vertex is specified in one
line. So we can skip the rest of the line - unknown vertex
elements are ignored.
*/
} }
#endif // !! ASSIMP_BUILD_NO_IRRMESH_IMPORTER #endif // !! ASSIMP_BUILD_NO_IRRMESH_IMPORTER

View File

@ -85,6 +85,19 @@ protected:
*/ */
void InternReadFile(const std::string &pFile, aiScene *pScene, void InternReadFile(const std::string &pFile, aiScene *pScene,
IOSystem *pIOHandler) override; IOSystem *pIOHandler) override;
private:
enum class VertexFormat {
standard = 0, // "standard" - also noted as 'normal' format elsewhere
t2coord = 1, // "2tcoord" - standard + 2 UV maps
tangent = 2, // "tangents" - standard + tangents and bitangents
};
void ParseBufferVertices(const char *sz, VertexFormat vertexFormat,
std::vector<aiVector3D> &vertices, std::vector<aiVector3D> &normals,
std::vector<aiVector3D> &tangents, std::vector<aiVector3D> &bitangents,
std::vector<aiVector3D> &UVs, std::vector<aiVector3D> &UV2s,
std::vector<aiColor4D> &colors, bool &useColors);
}; };
} // end of namespace Assimp } // end of namespace Assimp

View File

@ -49,8 +49,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "IRRShared.h" #include "IRRShared.h"
#include <assimp/ParsingUtils.h> #include <assimp/ParsingUtils.h>
#include <assimp/fast_atof.h> #include <assimp/fast_atof.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/material.h> #include <assimp/material.h>
#include <assimp/DefaultLogger.hpp>
using namespace Assimp; using namespace Assimp;
@ -63,34 +63,34 @@ const aiMatrix4x4 Assimp::AI_TO_IRR_MATRIX = aiMatrix4x4 (
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// read a property in hexadecimal format (i.e. ffffffff) // read a property in hexadecimal format (i.e. ffffffff)
void IrrlichtBase::ReadHexProperty(HexProperty &out ) { void IrrlichtBase::ReadHexProperty(HexProperty &out, pugi::xml_node& hexnode) {
for (pugi::xml_attribute attrib : mNode->attributes()) { for (pugi::xml_attribute attrib : hexnode.attributes()) {
if (!ASSIMP_stricmp(attrib.name(), "name")) { if (!ASSIMP_stricmp(attrib.name(), "name")) {
out.name = std::string(attrib.value()); out.name = std::string(attrib.value());
} else if (!ASSIMP_stricmp(attrib.name(), "value")) { } else if (!ASSIMP_stricmp(attrib.name(), "value")) {
// parse the hexadecimal value // parse the hexadecimal value
out.value = strtoul16(attrib.name()); out.value = strtoul16(attrib.value());
} }
} }
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// read a decimal property // read a decimal property
void IrrlichtBase::ReadIntProperty(IntProperty & out) { void IrrlichtBase::ReadIntProperty(IntProperty &out, pugi::xml_node& intnode) {
for (pugi::xml_attribute attrib : mNode->attributes()) { for (pugi::xml_attribute attrib : intnode.attributes()) {
if (!ASSIMP_stricmp(attrib.name(), "name")) { if (!ASSIMP_stricmp(attrib.name(), "name")) {
out.name = std::string(attrib.value()); out.name = std::string(attrib.value());
} else if (!ASSIMP_stricmp(attrib.value(),"value")) { } else if (!ASSIMP_stricmp(attrib.name(), "value")) {
// parse the int value // parse the int value
out.value = strtol10(attrib.name()); out.value = strtol10(attrib.value());
} }
} }
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// read a string property // read a string property
void IrrlichtBase::ReadStringProperty( StringProperty& out) { void IrrlichtBase::ReadStringProperty(StringProperty &out, pugi::xml_node& stringnode) {
for (pugi::xml_attribute attrib : mNode->attributes()) { for (pugi::xml_attribute attrib : stringnode.attributes()) {
if (!ASSIMP_stricmp(attrib.name(), "name")) { if (!ASSIMP_stricmp(attrib.name(), "name")) {
out.name = std::string(attrib.value()); out.name = std::string(attrib.value());
} else if (!ASSIMP_stricmp(attrib.name(), "value")) { } else if (!ASSIMP_stricmp(attrib.name(), "value")) {
@ -102,8 +102,8 @@ void IrrlichtBase::ReadStringProperty( StringProperty& out) {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// read a boolean property // read a boolean property
void IrrlichtBase::ReadBoolProperty(BoolProperty &out) { void IrrlichtBase::ReadBoolProperty(BoolProperty &out, pugi::xml_node& boolnode) {
for (pugi::xml_attribute attrib : mNode->attributes()) { for (pugi::xml_attribute attrib : boolnode.attributes()) {
if (!ASSIMP_stricmp(attrib.name(), "name")) { if (!ASSIMP_stricmp(attrib.name(), "name")) {
out.name = std::string(attrib.value()); out.name = std::string(attrib.value());
} else if (!ASSIMP_stricmp(attrib.name(), "value")) { } else if (!ASSIMP_stricmp(attrib.name(), "value")) {
@ -115,8 +115,8 @@ void IrrlichtBase::ReadBoolProperty(BoolProperty &out) {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// read a float property // read a float property
void IrrlichtBase::ReadFloatProperty(FloatProperty &out) { void IrrlichtBase::ReadFloatProperty(FloatProperty &out, pugi::xml_node &floatnode) {
for (pugi::xml_attribute attrib : mNode->attributes()) { for (pugi::xml_attribute attrib : floatnode.attributes()) {
if (!ASSIMP_stricmp(attrib.name(), "name")) { if (!ASSIMP_stricmp(attrib.name(), "name")) {
out.name = std::string(attrib.value()); out.name = std::string(attrib.value());
} else if (!ASSIMP_stricmp(attrib.name(), "value")) { } else if (!ASSIMP_stricmp(attrib.name(), "value")) {
@ -128,8 +128,8 @@ void IrrlichtBase::ReadFloatProperty(FloatProperty &out) {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// read a vector property // read a vector property
void IrrlichtBase::ReadVectorProperty( VectorProperty &out ) { void IrrlichtBase::ReadVectorProperty(VectorProperty &out, pugi::xml_node& vectornode) {
for (pugi::xml_attribute attrib : mNode->attributes()) { for (pugi::xml_attribute attrib : vectornode.attributes()) {
if (!ASSIMP_stricmp(attrib.name(), "name")) { if (!ASSIMP_stricmp(attrib.name(), "name")) {
out.name = std::string(attrib.value()); out.name = std::string(attrib.value());
} else if (!ASSIMP_stricmp(attrib.name(), "value")) { } else if (!ASSIMP_stricmp(attrib.name(), "value")) {
@ -170,7 +170,7 @@ int ConvertMappingMode(const std::string& mode) {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Parse a material from the XML file // Parse a material from the XML file
aiMaterial* IrrlichtBase::ParseMaterial(unsigned int& matFlags) { aiMaterial *IrrlichtBase::ParseMaterial(pugi::xml_node& materialNode, unsigned int &matFlags) {
aiMaterial *mat = new aiMaterial(); aiMaterial *mat = new aiMaterial();
aiColor4D clr; aiColor4D clr;
aiString s; aiString s;
@ -179,10 +179,10 @@ aiMaterial* IrrlichtBase::ParseMaterial(unsigned int& matFlags) {
int cnt = 0; // number of used texture channels int cnt = 0; // number of used texture channels
unsigned int nd = 0; unsigned int nd = 0;
for (pugi::xml_node child : mNode->children()) { for (pugi::xml_node child : materialNode.children()) {
if (!ASSIMP_stricmp(child.name(), "color")) { // Hex properties if (!ASSIMP_stricmp(child.name(), "color")) { // Hex properties
HexProperty prop; HexProperty prop;
ReadHexProperty(prop); ReadHexProperty(prop, child);
if (prop.name == "Diffuse") { if (prop.name == "Diffuse") {
ColorFromARGBPacked(prop.value, clr); ColorFromARGBPacked(prop.value, clr);
mat->AddProperty(&clr, 1, AI_MATKEY_COLOR_DIFFUSE); mat->AddProperty(&clr, 1, AI_MATKEY_COLOR_DIFFUSE);
@ -206,13 +206,13 @@ aiMaterial* IrrlichtBase::ParseMaterial(unsigned int& matFlags) {
#endif #endif
} else if (!ASSIMP_stricmp(child.name(), "float")) { // Float properties } else if (!ASSIMP_stricmp(child.name(), "float")) { // Float properties
FloatProperty prop; FloatProperty prop;
ReadFloatProperty(prop); ReadFloatProperty(prop, child);
if (prop.name == "Shininess") { if (prop.name == "Shininess") {
mat->AddProperty(&prop.value, 1, AI_MATKEY_SHININESS); mat->AddProperty(&prop.value, 1, AI_MATKEY_SHININESS);
} }
} else if (!ASSIMP_stricmp(child.name(), "bool")) { // Bool properties } else if (!ASSIMP_stricmp(child.name(), "bool")) { // Bool properties
BoolProperty prop; BoolProperty prop;
ReadBoolProperty(prop); ReadBoolProperty(prop, child);
if (prop.name == "Wireframe") { if (prop.name == "Wireframe") {
int val = (prop.value ? true : false); int val = (prop.value ? true : false);
mat->AddProperty(&val, 1, AI_MATKEY_ENABLE_WIREFRAME); mat->AddProperty(&val, 1, AI_MATKEY_ENABLE_WIREFRAME);
@ -226,7 +226,7 @@ aiMaterial* IrrlichtBase::ParseMaterial(unsigned int& matFlags) {
} else if (!ASSIMP_stricmp(child.name(), "texture") || } else if (!ASSIMP_stricmp(child.name(), "texture") ||
!ASSIMP_stricmp(child.name(), "enum")) { // String properties - textures and texture related properties !ASSIMP_stricmp(child.name(), "enum")) { // String properties - textures and texture related properties
StringProperty prop; StringProperty prop;
ReadStringProperty(prop); ReadStringProperty(prop, child);
if (prop.value.length()) { if (prop.value.length()) {
// material type (shader) // material type (shader)
if (prop.name == "Type") { if (prop.name == "Type") {
@ -379,7 +379,7 @@ aiMaterial* IrrlichtBase::ParseMaterial(unsigned int& matFlags) {
} }
}*/ }*/
} }
ASSIMP_LOG_ERROR("IRRMESH: Unexpected end of file. Material is not complete"); //ASSIMP_LOG_ERROR("IRRMESH: Unexpected end of file. Material is not complete");
return mat; return mat;
} }

View File

@ -58,8 +58,7 @@ extern const aiMatrix4x4 AI_TO_IRR_MATRIX;
*/ */
class IrrlichtBase { class IrrlichtBase {
protected: protected:
IrrlichtBase() : IrrlichtBase() {
mNode(nullptr) {
// empty // empty
} }
@ -82,25 +81,25 @@ protected:
/// XML reader instance /// XML reader instance
XmlParser mParser; XmlParser mParser;
pugi::xml_node *mNode;
// ------------------------------------------------------------------- // -------------------------------------------------------------------
/** Parse a material description from the XML /** Parse a material description from the XML
* @return The created material * @return The created material
* @param matFlags Receives AI_IRRMESH_MAT_XX flags * @param matFlags Receives AI_IRRMESH_MAT_XX flags
*/ */
aiMaterial *ParseMaterial(unsigned int &matFlags); aiMaterial *ParseMaterial(pugi::xml_node &materialNode, unsigned int &matFlags);
// ------------------------------------------------------------------- // -------------------------------------------------------------------
/** Read a property of the specified type from the current XML element. /** Read a property of the specified type from the current XML element.
* @param out Receives output data * @param out Receives output data
* @param node XML attribute element containing data
*/ */
void ReadHexProperty(HexProperty &out); void ReadHexProperty(HexProperty &out, pugi::xml_node& hexnode);
void ReadStringProperty(StringProperty &out); void ReadStringProperty(StringProperty &out, pugi::xml_node& stringnode);
void ReadBoolProperty(BoolProperty &out); void ReadBoolProperty(BoolProperty &out, pugi::xml_node& boolnode);
void ReadFloatProperty(FloatProperty &out); void ReadFloatProperty(FloatProperty &out, pugi::xml_node& floatnode);
void ReadVectorProperty(VectorProperty &out); void ReadVectorProperty(VectorProperty &out, pugi::xml_node& vectornode);
void ReadIntProperty(IntProperty &out); void ReadIntProperty(IntProperty &out, pugi::xml_node& intnode);
}; };
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2022, assimp team Copyright (c) 2006-2022, assimp team
All rights reserved. All rights reserved.
Redistribution and use of this software in source and binary forms, Redistribution and use of this software in source and binary forms,
@ -51,6 +49,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "AssetLib/LWO/LWOLoader.h" #include "AssetLib/LWO/LWOLoader.h"
#include "PostProcessing/ConvertToLHProcess.h" #include "PostProcessing/ConvertToLHProcess.h"
#include "PostProcessing/ProcessHelper.h" #include "PostProcessing/ProcessHelper.h"
#include "Geometry/GeometryUtils.h"
#include <assimp/ByteSwapper.h> #include <assimp/ByteSwapper.h>
#include <assimp/SGSpatialSort.h> #include <assimp/SGSpatialSort.h>
@ -528,7 +527,6 @@ void LWOImporter::ComputeNormals(aiMesh *mesh, const std::vector<unsigned int> &
continue; continue;
vNormals += v; vNormals += v;
} }
mesh->mNormals[idx] = vNormals.Normalize();
} }
} }
} }
@ -549,7 +547,6 @@ void LWOImporter::ComputeNormals(aiMesh *mesh, const std::vector<unsigned int> &
const aiVector3D &v = faceNormals[*a]; const aiVector3D &v = faceNormals[*a];
vNormals += v; vNormals += v;
} }
vNormals.Normalize();
for (std::vector<unsigned int>::const_iterator a = poResult.begin(); a != poResult.end(); ++a) { for (std::vector<unsigned int>::const_iterator a = poResult.begin(); a != poResult.end(); ++a) {
mesh->mNormals[*a] = vNormals; mesh->mNormals[*a] = vNormals;
vertexDone[*a] = true; vertexDone[*a] = true;
@ -557,6 +554,7 @@ void LWOImporter::ComputeNormals(aiMesh *mesh, const std::vector<unsigned int> &
} }
} }
} }
GeometryUtils::normalizeVectorArray(mesh->mNormals, mesh->mNormals, mesh->mNumVertices);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------

View File

@ -632,8 +632,7 @@ void LWSImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
nodes.push_back(d); nodes.push_back(d);
} }
ASSIMP_LOG_ERROR("LWS: Unexpected keyword: \'Channel\'"); ASSIMP_LOG_ERROR("LWS: Unexpected keyword: \'Channel\'");
} } else {
// important: index of channel // important: index of channel
nodes.back().channels.emplace_back(); nodes.back().channels.emplace_back();
LWO::Envelope &env = nodes.back().channels.back(); LWO::Envelope &env = nodes.back().channels.back();
@ -643,7 +642,7 @@ void LWSImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
// currently we can just interpret the standard channels 0...9 // currently we can just interpret the standard channels 0...9
// (hack) assume that index-i yields the binary channel type from LWO // (hack) assume that index-i yields the binary channel type from LWO
env.type = (LWO::EnvelopeType)(env.index + 1); env.type = (LWO::EnvelopeType)(env.index + 1);
}
} }
// 'Envelope': a single animation channel // 'Envelope': a single animation channel
else if ((*it).tokens[0] == "Envelope") { else if ((*it).tokens[0] == "Envelope") {

View File

@ -138,18 +138,31 @@ bool MD5Parser::ParseSection(Section &out) {
char *sz = buffer; char *sz = buffer;
while (!IsSpaceOrNewLine(*buffer)) { while (!IsSpaceOrNewLine(*buffer)) {
++buffer; ++buffer;
if (buffer == bufferEnd)
return false;
} }
out.mName = std::string(sz, (uintptr_t)(buffer - sz)); out.mName = std::string(sz, (uintptr_t)(buffer - sz));
SkipSpaces(); while (IsSpace(*buffer)) {
++buffer;
if (buffer == bufferEnd)
return false;
}
bool running = true; bool running = true;
while (running) { while (running) {
if ('{' == *buffer) { if ('{' == *buffer) {
// it is a normal section so read all lines // it is a normal section so read all lines
++buffer; ++buffer;
if (buffer == bufferEnd)
return false;
bool run = true; bool run = true;
while (run) { while (run) {
if (!SkipSpacesAndLineEnd()) { while (IsSpaceOrNewLine(*buffer)) {
++buffer;
if (buffer == bufferEnd)
return false;
}
if ('\0' == *buffer) {
return false; // seems this was the last section return false; // seems this was the last section
} }
if ('}' == *buffer) { if ('}' == *buffer) {
@ -164,25 +177,39 @@ bool MD5Parser::ParseSection(Section &out) {
elem.szStart = buffer; elem.szStart = buffer;
// terminate the line with zero // terminate the line with zero
while (!IsLineEnd(*buffer)) while (!IsLineEnd(*buffer)) {
++buffer; ++buffer;
if (buffer == bufferEnd)
return false;
}
if (*buffer) { if (*buffer) {
++lineNumber; ++lineNumber;
*buffer++ = '\0'; *buffer++ = '\0';
if (buffer == bufferEnd)
return false;
} }
} }
break; break;
} else if (!IsSpaceOrNewLine(*buffer)) { } else if (!IsSpaceOrNewLine(*buffer)) {
// it is an element at global scope. Parse its value and go on // it is an element at global scope. Parse its value and go on
sz = buffer; sz = buffer;
while (!IsSpaceOrNewLine(*buffer++)) while (!IsSpaceOrNewLine(*buffer++)) {
; if (buffer == bufferEnd)
return false;
}
out.mGlobalValue = std::string(sz, (uintptr_t)(buffer - sz)); out.mGlobalValue = std::string(sz, (uintptr_t)(buffer - sz));
continue; continue;
} }
break; break;
} }
return SkipSpacesAndLineEnd(); if (buffer == bufferEnd)
return false;
while (IsSpaceOrNewLine(*buffer)) {
++buffer;
if (buffer == bufferEnd)
return false;
}
return '\0' != *buffer;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -229,14 +256,19 @@ bool MD5Parser::ParseSection(Section &out) {
// parse a string, enclosed in quotation marks // parse a string, enclosed in quotation marks
#define AI_MD5_PARSE_STRING_IN_QUOTATION(out) \ #define AI_MD5_PARSE_STRING_IN_QUOTATION(out) \
while ('\"' != *sz) \ out.length = 0; \
while ('\"' != *sz && '\0' != *sz) \
++sz; \ ++sz; \
if ('\0' != *sz) { \
const char *szStart = ++sz; \ const char *szStart = ++sz; \
while ('\"' != *sz) \ while ('\"' != *sz && '\0' != *sz) \
++sz; \ ++sz; \
if ('\0' != *sz) { \
const char *szEnd = (sz++); \ const char *szEnd = (sz++); \
out.length = (ai_uint32)(szEnd - szStart); \ out.length = (ai_uint32)(szEnd - szStart); \
::memcpy(out.data, szStart, out.length); \ ::memcpy(out.data, szStart, out.length); \
} \
} \
out.data[out.length] = '\0'; out.data[out.length] = '\0';
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// .MD5MESH parsing function // .MD5MESH parsing function

View File

@ -271,10 +271,16 @@ void MDLImporter::InternReadFile(const std::string &pFile,
} }
} }
// ------------------------------------------------------------------------------------------------
// Check whether we're still inside the valid file range
bool MDLImporter::IsPosValid(const void *szPos) const {
return szPos && (const unsigned char *)szPos <= this->mBuffer + this->iFileSize && szPos >= this->mBuffer;
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Check whether we're still inside the valid file range // Check whether we're still inside the valid file range
void MDLImporter::SizeCheck(const void *szPos) { void MDLImporter::SizeCheck(const void *szPos) {
if (!szPos || (const unsigned char *)szPos > this->mBuffer + this->iFileSize) { if (!IsPosValid(szPos)) {
throw DeadlyImportError("Invalid MDL file. The file is too small " throw DeadlyImportError("Invalid MDL file. The file is too small "
"or contains invalid data."); "or contains invalid data.");
} }
@ -284,7 +290,7 @@ void MDLImporter::SizeCheck(const void *szPos) {
// Just for debugging purposes // Just for debugging purposes
void MDLImporter::SizeCheck(const void *szPos, const char *szFile, unsigned int iLine) { void MDLImporter::SizeCheck(const void *szPos, const char *szFile, unsigned int iLine) {
ai_assert(nullptr != szFile); ai_assert(nullptr != szFile);
if (!szPos || (const unsigned char *)szPos > mBuffer + iFileSize) { if (!IsPosValid(szPos)) {
// remove a directory if there is one // remove a directory if there is one
const char *szFilePtr = ::strrchr(szFile, '\\'); const char *szFilePtr = ::strrchr(szFile, '\\');
if (!szFilePtr) { if (!szFilePtr) {

View File

@ -150,6 +150,7 @@ protected:
*/ */
void SizeCheck(const void* szPos); void SizeCheck(const void* szPos);
void SizeCheck(const void* szPos, const char* szFile, unsigned int iLine); void SizeCheck(const void* szPos, const char* szFile, unsigned int iLine);
bool IsPosValid(const void* szPos) const;
// ------------------------------------------------------------------- // -------------------------------------------------------------------
/** Validate the header data structure of a game studio MDL7 file /** Validate the header data structure of a game studio MDL7 file

View File

@ -481,6 +481,8 @@ void MDLImporter::ParseSkinLump_3DGS_MDL7(
pcNew->achFormatHint[2] = 's'; pcNew->achFormatHint[2] = 's';
pcNew->achFormatHint[3] = '\0'; pcNew->achFormatHint[3] = '\0';
SizeCheck(szCurrent + pcNew->mWidth);
pcNew->pcData = (aiTexel *)new unsigned char[pcNew->mWidth]; pcNew->pcData = (aiTexel *)new unsigned char[pcNew->mWidth];
memcpy(pcNew->pcData, szCurrent, pcNew->mWidth); memcpy(pcNew->pcData, szCurrent, pcNew->mWidth);
szCurrent += iWidth; szCurrent += iWidth;
@ -493,12 +495,12 @@ void MDLImporter::ParseSkinLump_3DGS_MDL7(
aiString szFile; aiString szFile;
const size_t iLen = strlen((const char *)szCurrent); const size_t iLen = strlen((const char *)szCurrent);
size_t iLen2 = iLen + 1; size_t iLen2 = iLen > (MAXLEN - 1) ? (MAXLEN - 1) : iLen;
iLen2 = iLen2 > MAXLEN ? MAXLEN : iLen2;
memcpy(szFile.data, (const char *)szCurrent, iLen2); memcpy(szFile.data, (const char *)szCurrent, iLen2);
szFile.data[iLen2] = '\0';
szFile.length = static_cast<ai_uint32>(iLen2); szFile.length = static_cast<ai_uint32>(iLen2);
szCurrent += iLen2; szCurrent += iLen2 + 1;
// place this as diffuse texture // place this as diffuse texture
pcMatOut->AddProperty(&szFile, AI_MATKEY_TEXTURE_DIFFUSE(0)); pcMatOut->AddProperty(&szFile, AI_MATKEY_TEXTURE_DIFFUSE(0));
@ -703,7 +705,14 @@ void MDLImporter::SkipSkinLump_3DGS_MDL7(
tex.pcData = bad_texel; tex.pcData = bad_texel;
tex.mHeight = iHeight; tex.mHeight = iHeight;
tex.mWidth = iWidth; tex.mWidth = iWidth;
try {
ParseTextureColorData(szCurrent, iMasked, &iSkip, &tex); ParseTextureColorData(szCurrent, iMasked, &iSkip, &tex);
} catch (...) {
// FIX: Important, otherwise the destructor will crash
tex.pcData = nullptr;
throw;
}
// FIX: Important, otherwise the destructor will crash // FIX: Important, otherwise the destructor will crash
tex.pcData = nullptr; tex.pcData = nullptr;

View File

@ -52,6 +52,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/importerdesc.h> #include <assimp/importerdesc.h>
#include <assimp/StreamReader.h> #include <assimp/StreamReader.h>
#include <map> #include <map>
#include <limits>
using namespace Assimp; using namespace Assimp;
@ -160,6 +161,9 @@ void NDOImporter::InternReadFile( const std::string& pFile,
temp = file_format >= 12 ? reader.GetU4() : reader.GetU2(); temp = file_format >= 12 ? reader.GetU4() : reader.GetU2();
head = (const char*)reader.GetPtr(); head = (const char*)reader.GetPtr();
if (std::numeric_limits<unsigned int>::max() - 76 < temp) {
throw DeadlyImportError("Invalid name length");
}
reader.IncPtr(temp + 76); /* skip unknown stuff */ reader.IncPtr(temp + 76); /* skip unknown stuff */
obj.name = std::string(head, temp); obj.name = std::string(head, temp);

View File

@ -284,7 +284,7 @@ void OFFImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS
for (unsigned int i = 0; i < numFaces; ) { for (unsigned int i = 0; i < numFaces; ) {
if(!GetNextLine(buffer,line)) { if(!GetNextLine(buffer,line)) {
ASSIMP_LOG_ERROR("OFF: The number of faces in the header is incorrect"); ASSIMP_LOG_ERROR("OFF: The number of faces in the header is incorrect");
break; throw DeadlyImportError("OFF: The number of faces in the header is incorrect");
} }
unsigned int idx; unsigned int idx;
sz = line; SkipSpaces(&sz); sz = line; SkipSpaces(&sz);

View File

@ -239,8 +239,6 @@ struct Mesh {
unsigned int m_uiMaterialIndex; unsigned int m_uiMaterialIndex;
/// True, if normals are stored. /// True, if normals are stored.
bool m_hasNormals; bool m_hasNormals;
/// True, if vertex colors are stored.
bool m_hasVertexColors;
/// Constructor /// Constructor
explicit Mesh(const std::string &name) : explicit Mesh(const std::string &name) :

View File

@ -323,7 +323,7 @@ aiMesh *ObjFileImporter::createTopology(const ObjFile::Model *pModel, const ObjF
return nullptr; return nullptr;
} }
aiMesh *pMesh = new aiMesh; std::unique_ptr<aiMesh> pMesh(new aiMesh);
if (!pObjMesh->m_name.empty()) { if (!pObjMesh->m_name.empty()) {
pMesh->mName.Set(pObjMesh->m_name); pMesh->mName.Set(pObjMesh->m_name);
} }
@ -385,9 +385,9 @@ aiMesh *ObjFileImporter::createTopology(const ObjFile::Model *pModel, const ObjF
} }
// Create mesh vertices // Create mesh vertices
createVertexArray(pModel, pData, meshIndex, pMesh, uiIdxCount); createVertexArray(pModel, pData, meshIndex, pMesh.get(), uiIdxCount);
return pMesh; return pMesh.release();
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -498,6 +498,10 @@ void ObjFileImporter::createVertexArray(const ObjFile::Model *pModel,
if (vertexIndex) { if (vertexIndex) {
if (!last) { if (!last) {
if (pMesh->mNumVertices <= newIndex + 1) {
throw DeadlyImportError("OBJ: bad vertex index");
}
pMesh->mVertices[newIndex + 1] = pMesh->mVertices[newIndex]; pMesh->mVertices[newIndex + 1] = pMesh->mVertices[newIndex];
if (!sourceFace->m_normals.empty() && !pModel->mNormals.empty()) { if (!sourceFace->m_normals.empty() && !pModel->mNormals.empty()) {
pMesh->mNormals[newIndex + 1] = pMesh->mNormals[newIndex]; pMesh->mNormals[newIndex + 1] = pMesh->mNormals[newIndex];

View File

@ -252,8 +252,8 @@ void ObjFileMtlImporter::load() {
case 'a': // Anisotropy case 'a': // Anisotropy
{ {
++m_DataIt; ++m_DataIt;
getFloatValue(m_pModel->mCurrentMaterial->anisotropy);
if (m_pModel->mCurrentMaterial != nullptr) if (m_pModel->mCurrentMaterial != nullptr)
getFloatValue(m_pModel->mCurrentMaterial->anisotropy);
m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine); m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
} break; } break;
@ -371,6 +371,7 @@ void ObjFileMtlImporter::getTexture() {
if (m_pModel->mCurrentMaterial == nullptr) { if (m_pModel->mCurrentMaterial == nullptr) {
m_pModel->mCurrentMaterial = new ObjFile::Material(); m_pModel->mCurrentMaterial = new ObjFile::Material();
m_pModel->mCurrentMaterial->MaterialName.Set("Empty_Material"); m_pModel->mCurrentMaterial->MaterialName.Set("Empty_Material");
m_pModel->mMaterialMap["Empty_Material"] = m_pModel->mCurrentMaterial;
} }
const char *pPtr(&(*m_DataIt)); const char *pPtr(&(*m_DataIt));

View File

@ -156,9 +156,17 @@ void ObjFileParser::parseFile(IOStreamBuffer<char> &streamBuffer) {
// read in vertex definition (homogeneous coords) // read in vertex definition (homogeneous coords)
getHomogeneousVector3(m_pModel->mVertices); getHomogeneousVector3(m_pModel->mVertices);
} else if (numComponents == 6) { } else if (numComponents == 6) {
// fill previous omitted vertex-colors by default
if (m_pModel->mVertexColors.size() < m_pModel->mVertices.size()) {
m_pModel->mVertexColors.resize(m_pModel->mVertices.size(), aiVector3D(0, 0, 0));
}
// read vertex and vertex-color // read vertex and vertex-color
getTwoVectors3(m_pModel->mVertices, m_pModel->mVertexColors); getTwoVectors3(m_pModel->mVertices, m_pModel->mVertexColors);
} }
// append omitted vertex-colors as default for the end if any vertex-color exists
if (!m_pModel->mVertexColors.empty() && m_pModel->mVertexColors.size() < m_pModel->mVertices.size()) {
m_pModel->mVertexColors.resize(m_pModel->mVertices.size(), aiVector3D(0, 0, 0));
}
} else if (*m_DataIt == 't') { } else if (*m_DataIt == 't') {
// read in texture coordinate ( 2D or 3D ) // read in texture coordinate ( 2D or 3D )
++m_DataIt; ++m_DataIt;
@ -456,8 +464,19 @@ void ObjFileParser::getFace(aiPrimitiveType type) {
iPos = 0; iPos = 0;
} else { } else {
//OBJ USES 1 Base ARRAYS!!!! //OBJ USES 1 Base ARRAYS!!!!
const char *token = &(*m_DataIt); int iVal;
const int iVal = ::atoi(token); auto end = m_DataIt;
// find either the buffer end or the '\0'
while (end < m_DataItEnd && *end != '\0')
++end;
// avoid temporary string allocation if there is a zero
if (end != m_DataItEnd) {
iVal = ::atoi(&(*m_DataIt));
} else {
// otherwise make a zero terminated copy, which is safe to pass to atoi
std::string number(&(*m_DataIt), m_DataItEnd - m_DataIt);
iVal = ::atoi(number.c_str());
}
// increment iStep position based off of the sign and # of digits // increment iStep position based off of the sign and # of digits
int tmp = iVal; int tmp = iVal;

View File

@ -837,7 +837,10 @@ void SMDImporter::ParseNodeInfo(const char* szCurrent, const char** szCurrentOut
unsigned int iBone = 0; unsigned int iBone = 0;
SkipSpacesAndLineEnd(szCurrent,&szCurrent); SkipSpacesAndLineEnd(szCurrent,&szCurrent);
if ( !ParseUnsignedInt(szCurrent,&szCurrent,iBone) || !SkipSpaces(szCurrent,&szCurrent)) { if ( !ParseUnsignedInt(szCurrent,&szCurrent,iBone) || !SkipSpaces(szCurrent,&szCurrent)) {
LogErrorNoThrow("Unexpected EOF/EOL while parsing bone index"); throw DeadlyImportError("Unexpected EOF/EOL while parsing bone index");
}
if (iBone == UINT_MAX) {
LogErrorNoThrow("Invalid bone number while parsing bone index");
SMDI_PARSE_RETURN; SMDI_PARSE_RETURN;
} }
// add our bone to the list // add our bone to the list

View File

@ -578,7 +578,7 @@ void XFileImporter::ConvertMaterials( aiScene* pScene, std::vector<XFile::Materi
aiString name; aiString name;
pScene->mMaterials[b]->Get( AI_MATKEY_NAME, name); pScene->mMaterials[b]->Get( AI_MATKEY_NAME, name);
if( strcmp( name.C_Str(), oldMat.mName.data()) == 0 ) { if( strcmp( name.C_Str(), oldMat.mName.data()) == 0 ) {
oldMat.sceneIndex = a; oldMat.sceneIndex = b;
break; break;
} }
} }

View File

@ -58,8 +58,6 @@ class X3DExporter {
Value(value) { Value(value) {
// empty // empty
} }
SAttribute(SAttribute &&rhs) AI_NO_EXCEPT = default;
}; };
/***********************************************/ /***********************************************/

View File

@ -12,7 +12,6 @@ bool X3DXmlHelper::getColor3DAttribute(XmlNode &node, const char *attributeName,
tokenize<std::string>(val, values, " "); tokenize<std::string>(val, values, " ");
if (values.size() != 3) { if (values.size() != 3) {
Throw_ConvertFail_Str2ArrF(node.name(), attributeName); Throw_ConvertFail_Str2ArrF(node.name(), attributeName);
return false;
} }
auto it = values.begin(); auto it = values.begin();
color.r = stof(*it++); color.r = stof(*it++);
@ -30,7 +29,6 @@ bool X3DXmlHelper::getVector2DAttribute(XmlNode &node, const char *attributeName
tokenize<std::string>(val, values, " "); tokenize<std::string>(val, values, " ");
if (values.size() != 2) { if (values.size() != 2) {
Throw_ConvertFail_Str2ArrF(node.name(), attributeName); Throw_ConvertFail_Str2ArrF(node.name(), attributeName);
return false;
} }
auto it = values.begin(); auto it = values.begin();
color.x = stof(*it++); color.x = stof(*it++);
@ -47,7 +45,6 @@ bool X3DXmlHelper::getVector3DAttribute(XmlNode &node, const char *attributeName
tokenize<std::string>(val, values, " "); tokenize<std::string>(val, values, " ");
if (values.size() != 3) { if (values.size() != 3) {
Throw_ConvertFail_Str2ArrF(node.name(), attributeName); Throw_ConvertFail_Str2ArrF(node.name(), attributeName);
return false;
} }
auto it = values.begin(); auto it = values.begin();
color.x = stof(*it++); color.x = stof(*it++);

View File

@ -93,7 +93,10 @@ const aiImporterDesc *glTFImporter::GetInfo() const {
bool glTFImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /* checkSig */) const { bool glTFImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /* checkSig */) const {
glTF::Asset asset(pIOHandler); glTF::Asset asset(pIOHandler);
try { try {
asset.Load(pFile, GetExtension(pFile) == "glb"); asset.Load(pFile,
CheckMagicToken(
pIOHandler, pFile, AI_GLB_MAGIC_NUMBER, 1, 0,
static_cast<unsigned int>(strlen(AI_GLB_MAGIC_NUMBER))));
return asset.asset; return asset.asset;
} catch (...) { } catch (...) {
return false; return false;
@ -697,7 +700,10 @@ void glTFImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOS
// read the asset file // read the asset file
glTF::Asset asset(pIOHandler); glTF::Asset asset(pIOHandler);
asset.Load(pFile, GetExtension(pFile) == "glb"); asset.Load(pFile,
CheckMagicToken(
pIOHandler, pFile, AI_GLB_MAGIC_NUMBER, 1, 0,
static_cast<unsigned int>(strlen(AI_GLB_MAGIC_NUMBER))));
// //
// Copy the data out // Copy the data out

View File

@ -44,6 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
* glTF Extensions Support: * glTF Extensions Support:
* KHR_materials_pbrSpecularGlossiness full * KHR_materials_pbrSpecularGlossiness full
* KHR_materials_specular full
* KHR_materials_unlit full * KHR_materials_unlit full
* KHR_lights_punctual full * KHR_lights_punctual full
* KHR_materials_sheen full * KHR_materials_sheen full
@ -370,6 +371,15 @@ struct CustomExtension {
CustomExtension& operator=(const CustomExtension&) = default; CustomExtension& operator=(const CustomExtension&) = default;
}; };
//! Represents metadata in an glTF2 object
struct Extras {
std::vector<CustomExtension> mValues;
inline bool HasExtras() const {
return !mValues.empty();
}
};
//! Base class for all glTF top-level objects //! Base class for all glTF top-level objects
struct Object { struct Object {
int index; //!< The index of this object within its property container int index; //!< The index of this object within its property container
@ -378,7 +388,7 @@ struct Object {
std::string name; //!< The user-defined name of this object std::string name; //!< The user-defined name of this object
CustomExtension customExtensions; CustomExtension customExtensions;
CustomExtension extras; Extras extras;
//! Objects marked as special are not exported (used to emulate the binary body buffer) //! Objects marked as special are not exported (used to emulate the binary body buffer)
virtual bool IsSpecial() const { return false; } virtual bool IsSpecial() const { return false; }
@ -710,6 +720,7 @@ const vec4 defaultBaseColor = { 1, 1, 1, 1 };
const vec3 defaultEmissiveFactor = { 0, 0, 0 }; const vec3 defaultEmissiveFactor = { 0, 0, 0 };
const vec4 defaultDiffuseFactor = { 1, 1, 1, 1 }; const vec4 defaultDiffuseFactor = { 1, 1, 1, 1 };
const vec3 defaultSpecularFactor = { 1, 1, 1 }; const vec3 defaultSpecularFactor = { 1, 1, 1 };
const vec3 defaultSpecularColorFactor = { 0, 0, 0 };
const vec3 defaultSheenFactor = { 0, 0, 0 }; const vec3 defaultSheenFactor = { 0, 0, 0 };
const vec3 defaultAttenuationColor = { 1, 1, 1 }; const vec3 defaultAttenuationColor = { 1, 1, 1 };
@ -753,6 +764,16 @@ struct PbrSpecularGlossiness {
void SetDefaults(); void SetDefaults();
}; };
struct MaterialSpecular {
float specularFactor;
vec3 specularColorFactor;
TextureInfo specularTexture;
TextureInfo specularColorTexture;
MaterialSpecular() { SetDefaults(); }
void SetDefaults();
};
struct MaterialSheen { struct MaterialSheen {
vec3 sheenColorFactor; vec3 sheenColorFactor;
float sheenRoughnessFactor; float sheenRoughnessFactor;
@ -817,6 +838,9 @@ struct Material : public Object {
//extension: KHR_materials_pbrSpecularGlossiness //extension: KHR_materials_pbrSpecularGlossiness
Nullable<PbrSpecularGlossiness> pbrSpecularGlossiness; Nullable<PbrSpecularGlossiness> pbrSpecularGlossiness;
//extension: KHR_materials_specular
Nullable<MaterialSpecular> materialSpecular;
//extension: KHR_materials_sheen //extension: KHR_materials_sheen
Nullable<MaterialSheen> materialSheen; Nullable<MaterialSheen> materialSheen;
@ -1099,6 +1123,7 @@ public:
//! Keeps info about the enabled extensions //! Keeps info about the enabled extensions
struct Extensions { struct Extensions {
bool KHR_materials_pbrSpecularGlossiness; bool KHR_materials_pbrSpecularGlossiness;
bool KHR_materials_specular;
bool KHR_materials_unlit; bool KHR_materials_unlit;
bool KHR_lights_punctual; bool KHR_lights_punctual;
bool KHR_texture_transform; bool KHR_texture_transform;
@ -1114,6 +1139,7 @@ public:
Extensions() : Extensions() :
KHR_materials_pbrSpecularGlossiness(false), KHR_materials_pbrSpecularGlossiness(false),
KHR_materials_specular(false),
KHR_materials_unlit(false), KHR_materials_unlit(false),
KHR_lights_punctual(false), KHR_lights_punctual(false),
KHR_texture_transform(false), KHR_texture_transform(false),

View File

@ -45,6 +45,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/StringUtils.h> #include <assimp/StringUtils.h>
#include <assimp/DefaultLogger.hpp> #include <assimp/DefaultLogger.hpp>
#include <assimp/Base64.hpp> #include <assimp/Base64.hpp>
#include <rapidjson/document.h>
#include <rapidjson/schema.h>
#include <rapidjson/stringbuffer.h>
// clang-format off // clang-format off
#ifdef ASSIMP_ENABLE_DRACO #ifdef ASSIMP_ENABLE_DRACO
@ -139,6 +142,18 @@ inline CustomExtension ReadExtensions(const char *name, Value &obj) {
return ret; return ret;
} }
inline Extras ReadExtras(Value &obj) {
Extras ret;
ret.mValues.reserve(obj.MemberCount());
for (auto it = obj.MemberBegin(); it != obj.MemberEnd(); ++it) {
auto &val = it->value;
ret.mValues.emplace_back(ReadExtensions(it->name.GetString(), val));
}
return ret;
}
inline void CopyData(size_t count, const uint8_t *src, size_t src_stride, inline void CopyData(size_t count, const uint8_t *src, size_t src_stride,
uint8_t *dst, size_t dst_stride) { uint8_t *dst, size_t dst_stride) {
if (src_stride == dst_stride) { if (src_stride == dst_stride) {
@ -248,7 +263,7 @@ inline void Object::ReadExtensions(Value &val) {
inline void Object::ReadExtras(Value &val) { inline void Object::ReadExtras(Value &val) {
if (Value *curExtras = FindObject(val, "extras")) { if (Value *curExtras = FindObject(val, "extras")) {
this->extras = glTF2::ReadExtensions("extras", *curExtras); this->extras = glTF2::ReadExtras(*curExtras);
} }
} }
@ -1264,6 +1279,19 @@ inline void Material::Read(Value &material, Asset &r) {
} }
} }
if (r.extensionsUsed.KHR_materials_specular) {
if (Value *curMatSpecular = FindObject(*extensions, "KHR_materials_specular")) {
MaterialSpecular specular;
ReadMember(*curMatSpecular, "specularFactor", specular.specularFactor);
ReadTextureProperty(r, *curMatSpecular, "specularTexture", specular.specularTexture);
ReadMember(*curMatSpecular, "specularColorFactor", specular.specularColorFactor);
ReadTextureProperty(r, *curMatSpecular, "specularColorTexture", specular.specularColorTexture);
this->materialSpecular = Nullable<MaterialSpecular>(specular);
}
}
// Extension KHR_texture_transform is handled in ReadTextureProperty // Extension KHR_texture_transform is handled in ReadTextureProperty
if (r.extensionsUsed.KHR_materials_sheen) { if (r.extensionsUsed.KHR_materials_sheen) {
@ -1361,6 +1389,12 @@ inline void PbrSpecularGlossiness::SetDefaults() {
glossinessFactor = 1.0f; glossinessFactor = 1.0f;
} }
inline void MaterialSpecular::SetDefaults() {
//KHR_materials_specular properties
SetVector(specularColorFactor, defaultSpecularColorFactor);
specularFactor = 0.f;
}
inline void MaterialSheen::SetDefaults() { inline void MaterialSheen::SetDefaults() {
//KHR_materials_sheen properties //KHR_materials_sheen properties
SetVector(sheenColorFactor, defaultSheenFactor); SetVector(sheenColorFactor, defaultSheenFactor);
@ -2047,6 +2081,7 @@ inline void Asset::ReadExtensionsUsed(Document &doc) {
} }
CHECK_EXT(KHR_materials_pbrSpecularGlossiness); CHECK_EXT(KHR_materials_pbrSpecularGlossiness);
CHECK_EXT(KHR_materials_specular);
CHECK_EXT(KHR_materials_unlit); CHECK_EXT(KHR_materials_unlit);
CHECK_EXT(KHR_lights_punctual); CHECK_EXT(KHR_lights_punctual);
CHECK_EXT(KHR_texture_transform); CHECK_EXT(KHR_texture_transform);

View File

@ -45,6 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
* glTF Extensions Support: * glTF Extensions Support:
* KHR_materials_pbrSpecularGlossiness: full * KHR_materials_pbrSpecularGlossiness: full
* KHR_materials_specular: full
* KHR_materials_unlit: full * KHR_materials_unlit: full
* KHR_materials_sheen: full * KHR_materials_sheen: full
* KHR_materials_clearcoat: full * KHR_materials_clearcoat: full

View File

@ -418,6 +418,26 @@ namespace glTF2 {
exts.AddMember("KHR_materials_unlit", unlit, w.mAl); exts.AddMember("KHR_materials_unlit", unlit, w.mAl);
} }
if (m.materialSpecular.isPresent) {
Value materialSpecular(rapidjson::Type::kObjectType);
materialSpecular.SetObject();
MaterialSpecular &specular = m.materialSpecular.value;
if (specular.specularFactor != 0.0f) {
WriteFloat(materialSpecular, specular.specularFactor, "specularFactor", w.mAl);
WriteTex(materialSpecular, specular.specularTexture, "specularTexture", w.mAl);
}
if (specular.specularColorFactor[0] != defaultSpecularColorFactor[0] && specular.specularColorFactor[1] != defaultSpecularColorFactor[1] && specular.specularColorFactor[2] != defaultSpecularColorFactor[2]) {
WriteVec(materialSpecular, specular.specularColorFactor, "specularColorFactor", w.mAl);
WriteTex(materialSpecular, specular.specularColorTexture, "specularColorTexture", w.mAl);
}
if (!materialSpecular.ObjectEmpty()) {
exts.AddMember("KHR_materials_specular", materialSpecular, w.mAl);
}
}
if (m.materialSheen.isPresent) { if (m.materialSheen.isPresent) {
Value materialSheen(rapidjson::Type::kObjectType); Value materialSheen(rapidjson::Type::kObjectType);
@ -634,6 +654,44 @@ namespace glTF2 {
} }
} }
inline void WriteExtrasValue(Value &parent, const CustomExtension &value, AssetWriter &w) {
Value valueNode;
if (value.mStringValue.isPresent) {
MakeValue(valueNode, value.mStringValue.value.c_str(), w.mAl);
} else if (value.mDoubleValue.isPresent) {
MakeValue(valueNode, value.mDoubleValue.value, w.mAl);
} else if (value.mUint64Value.isPresent) {
MakeValue(valueNode, value.mUint64Value.value, w.mAl);
} else if (value.mInt64Value.isPresent) {
MakeValue(valueNode, value.mInt64Value.value, w.mAl);
} else if (value.mBoolValue.isPresent) {
MakeValue(valueNode, value.mBoolValue.value, w.mAl);
} else if (value.mValues.isPresent) {
valueNode.SetObject();
for (auto const &subvalue : value.mValues.value) {
WriteExtrasValue(valueNode, subvalue, w);
}
}
parent.AddMember(StringRef(value.name), valueNode, w.mAl);
}
inline void WriteExtras(Value &obj, const Extras &extras, AssetWriter &w) {
if (!extras.HasExtras()) {
return;
}
Value extrasNode;
extrasNode.SetObject();
for (auto const &value : extras.mValues) {
WriteExtrasValue(extrasNode, value, w);
}
obj.AddMember("extras", extrasNode, w.mAl);
}
inline void Write(Value& obj, Node& n, AssetWriter& w) inline void Write(Value& obj, Node& n, AssetWriter& w)
{ {
if (n.matrix.isPresent) { if (n.matrix.isPresent) {
@ -669,6 +727,8 @@ namespace glTF2 {
if(n.skeletons.size()) { if(n.skeletons.size()) {
AddRefsVector(obj, "skeletons", n.skeletons, w.mAl); AddRefsVector(obj, "skeletons", n.skeletons, w.mAl);
} }
WriteExtras(obj, n.extras, w);
} }
inline void Write(Value& /*obj*/, Program& /*b*/, AssetWriter& /*w*/) inline void Write(Value& /*obj*/, Program& /*b*/, AssetWriter& /*w*/)
@ -742,7 +802,6 @@ namespace glTF2 {
} }
} }
inline AssetWriter::AssetWriter(Asset& a) inline AssetWriter::AssetWriter(Asset& a)
: mDoc() : mDoc()
, mAsset(a) , mAsset(a)
@ -929,6 +988,10 @@ namespace glTF2 {
exts.PushBack(StringRef("KHR_materials_unlit"), mAl); exts.PushBack(StringRef("KHR_materials_unlit"), mAl);
} }
if (this->mAsset.extensionsUsed.KHR_materials_specular) {
exts.PushBack(StringRef("KHR_materials_specular"), mAl);
}
if (this->mAsset.extensionsUsed.KHR_materials_sheen) { if (this->mAsset.extensionsUsed.KHR_materials_sheen) {
exts.PushBack(StringRef("KHR_materials_sheen"), mAl); exts.PushBack(StringRef("KHR_materials_sheen"), mAl);
} }

View File

@ -443,6 +443,61 @@ inline Ref<Accessor> ExportData(Asset &a, std::string &meshName, Ref<Buffer> &bu
return acc; return acc;
} }
inline void ExportNodeExtras(const aiMetadataEntry &metadataEntry, aiString name, CustomExtension &value) {
value.name = name.C_Str();
switch (metadataEntry.mType) {
case AI_BOOL:
value.mBoolValue.value = *static_cast<bool *>(metadataEntry.mData);
value.mBoolValue.isPresent = true;
break;
case AI_INT32:
value.mInt64Value.value = *static_cast<int32_t *>(metadataEntry.mData);
value.mInt64Value.isPresent = true;
break;
case AI_UINT64:
value.mUint64Value.value = *static_cast<uint64_t *>(metadataEntry.mData);
value.mUint64Value.isPresent = true;
break;
case AI_FLOAT:
value.mDoubleValue.value = *static_cast<float *>(metadataEntry.mData);
value.mDoubleValue.isPresent = true;
break;
case AI_DOUBLE:
value.mDoubleValue.value = *static_cast<double *>(metadataEntry.mData);
value.mDoubleValue.isPresent = true;
break;
case AI_AISTRING:
value.mStringValue.value = static_cast<aiString *>(metadataEntry.mData)->C_Str();
value.mStringValue.isPresent = true;
break;
case AI_AIMETADATA: {
const aiMetadata *subMetadata = static_cast<aiMetadata *>(metadataEntry.mData);
value.mValues.value.resize(subMetadata->mNumProperties);
value.mValues.isPresent = true;
for (unsigned i = 0; i < subMetadata->mNumProperties; ++i) {
ExportNodeExtras(subMetadata->mValues[i], subMetadata->mKeys[i], value.mValues.value.at(i));
}
break;
}
default:
// AI_AIVECTOR3D not handled
break;
}
}
inline void ExportNodeExtras(const aiMetadata *metadata, Extras &extras) {
if (metadata == nullptr || metadata->mNumProperties == 0) {
return;
}
extras.mValues.resize(metadata->mNumProperties);
for (unsigned int i = 0; i < metadata->mNumProperties; ++i) {
ExportNodeExtras(metadata->mValues[i], metadata->mKeys[i], extras.mValues.at(i));
}
}
inline void SetSamplerWrap(SamplerWrap &wrap, aiTextureMapMode map) { inline void SetSamplerWrap(SamplerWrap &wrap, aiTextureMapMode map) {
switch (map) { switch (map) {
case aiTextureMapMode_Clamp: case aiTextureMapMode_Clamp:
@ -640,11 +695,10 @@ aiReturn glTF2Exporter::GetMatColor(const aiMaterial &mat, vec3 &prop, const cha
return result; return result;
} }
// This extension has been deprecated, only export with the specific flag enabled, defaults to false. Uses KHR_material_specular default.
bool glTF2Exporter::GetMatSpecGloss(const aiMaterial &mat, glTF2::PbrSpecularGlossiness &pbrSG) { bool glTF2Exporter::GetMatSpecGloss(const aiMaterial &mat, glTF2::PbrSpecularGlossiness &pbrSG) {
bool result = false; bool result = false;
// If has Glossiness, a Specular Color or Specular Texture, use the KHR_materials_pbrSpecularGlossiness extension // If has Glossiness, a Specular Color or Specular Texture, use the KHR_materials_pbrSpecularGlossiness extension
// NOTE: This extension is being considered for deprecation (Dec 2020), may be replaced by KHR_material_specular
if (mat.Get(AI_MATKEY_GLOSSINESS_FACTOR, pbrSG.glossinessFactor) == AI_SUCCESS) { if (mat.Get(AI_MATKEY_GLOSSINESS_FACTOR, pbrSG.glossinessFactor) == AI_SUCCESS) {
result = true; result = true;
} else { } else {
@ -674,6 +728,25 @@ bool glTF2Exporter::GetMatSpecGloss(const aiMaterial &mat, glTF2::PbrSpecularGlo
return result; return result;
} }
bool glTF2Exporter::GetMatSpecular(const aiMaterial &mat, glTF2::MaterialSpecular &specular) {
// Specular requires either/or, default factors of zero disables specular, so do not export
if (GetMatColor(mat, specular.specularColorFactor, AI_MATKEY_COLOR_SPECULAR) != AI_SUCCESS && mat.Get(AI_MATKEY_SPECULAR_FACTOR, specular.specularFactor) != AI_SUCCESS) {
return false;
}
// The spec states that the default is 1.0 and [1.0, 1.0, 1.0]. We if both are 0, which should disable specular. Otherwise, if one is 0, set to 1.0
const bool colorFactorIsZero = specular.specularColorFactor[0] == defaultSpecularColorFactor[0] && specular.specularColorFactor[1] == defaultSpecularColorFactor[1] && specular.specularColorFactor[2] == defaultSpecularColorFactor[2];
if (specular.specularFactor == 0.0f && colorFactorIsZero) {
return false;
} else if (specular.specularFactor == 0.0f) {
specular.specularFactor = 1.0f;
} else if (colorFactorIsZero) {
specular.specularColorFactor[0] = specular.specularColorFactor[1] = specular.specularColorFactor[2] = 1.0f;
}
GetMatTex(mat, specular.specularColorTexture, aiTextureType_SPECULAR);
GetMatTex(mat, specular.specularTexture, aiTextureType_SPECULAR);
return true;
}
bool glTF2Exporter::GetMatSheen(const aiMaterial &mat, glTF2::MaterialSheen &sheen) { bool glTF2Exporter::GetMatSheen(const aiMaterial &mat, glTF2::MaterialSheen &sheen) {
// Return true if got any valid Sheen properties or textures // Return true if got any valid Sheen properties or textures
if (GetMatColor(mat, sheen.sheenColorFactor, AI_MATKEY_SHEEN_COLOR_FACTOR) != aiReturn_SUCCESS) { if (GetMatColor(mat, sheen.sheenColorFactor, AI_MATKEY_SHEEN_COLOR_FACTOR) != aiReturn_SUCCESS) {
@ -763,7 +836,17 @@ void glTF2Exporter::ExportMaterials() {
GetMatTex(mat, m->pbrMetallicRoughness.baseColorTexture, aiTextureType_DIFFUSE); GetMatTex(mat, m->pbrMetallicRoughness.baseColorTexture, aiTextureType_DIFFUSE);
} }
GetMatTex(mat, m->pbrMetallicRoughness.metallicRoughnessTexture, aiTextureType_DIFFUSE_ROUGHNESS);
if (!m->pbrMetallicRoughness.metallicRoughnessTexture.texture) {
// if there wasn't a aiTextureType_DIFFUSE_ROUGHNESS defined in the source, fallback to aiTextureType_METALNESS
GetMatTex(mat, m->pbrMetallicRoughness.metallicRoughnessTexture, aiTextureType_METALNESS);
}
if (!m->pbrMetallicRoughness.metallicRoughnessTexture.texture) {
// if there still wasn't a aiTextureType_METALNESS defined in the source, fallback to AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE
GetMatTex(mat, m->pbrMetallicRoughness.metallicRoughnessTexture, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE); GetMatTex(mat, m->pbrMetallicRoughness.metallicRoughnessTexture, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE);
}
if (GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_BASE_COLOR) != AI_SUCCESS) { if (GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_BASE_COLOR) != AI_SUCCESS) {
// if baseColorFactor wasn't defined, then the source is likely not a metallic roughness material. // if baseColorFactor wasn't defined, then the source is likely not a metallic roughness material.
@ -818,9 +901,9 @@ void glTF2Exporter::ExportMaterials() {
m->alphaMode = alphaMode.C_Str(); m->alphaMode = alphaMode.C_Str();
} }
{ // This extension has been deprecated, only export with the specific flag enabled, defaults to false. Uses KHR_material_specular default.
if (mProperties->GetPropertyBool(AI_CONFIG_USE_GLTF_PBR_SPECULAR_GLOSSINESS)) {
// KHR_materials_pbrSpecularGlossiness extension // KHR_materials_pbrSpecularGlossiness extension
// NOTE: This extension is being considered for deprecation (Dec 2020)
PbrSpecularGlossiness pbrSG; PbrSpecularGlossiness pbrSG;
if (GetMatSpecGloss(mat, pbrSG)) { if (GetMatSpecGloss(mat, pbrSG)) {
mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness = true; mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness = true;
@ -837,7 +920,12 @@ void glTF2Exporter::ExportMaterials() {
} else { } else {
// These extensions are not compatible with KHR_materials_unlit or KHR_materials_pbrSpecularGlossiness // These extensions are not compatible with KHR_materials_unlit or KHR_materials_pbrSpecularGlossiness
if (!m->pbrSpecularGlossiness.isPresent) { if (!m->pbrSpecularGlossiness.isPresent) {
// Sheen MaterialSpecular specular;
if (GetMatSpecular(mat, specular)) {
mAsset->extensionsUsed.KHR_materials_specular = true;
m->materialSpecular = Nullable<MaterialSpecular>(specular);
}
MaterialSheen sheen; MaterialSheen sheen;
if (GetMatSheen(mat, sheen)) { if (GetMatSheen(mat, sheen)) {
mAsset->extensionsUsed.KHR_materials_sheen = true; mAsset->extensionsUsed.KHR_materials_sheen = true;
@ -1363,6 +1451,8 @@ unsigned int glTF2Exporter::ExportNode(const aiNode *n, Ref<Node> &parent) {
node->parent = parent; node->parent = parent;
node->name = name; node->name = name;
ExportNodeExtras(n->mMetaData, node->extras);
if (!n->mTransformation.IsIdentity()) { if (!n->mTransformation.IsIdentity()) {
if (mScene->mNumAnimations > 0 || (mProperties && mProperties->HasPropertyBool("GLTF2_NODE_IN_TRS"))) { if (mScene->mNumAnimations > 0 || (mProperties && mProperties->HasPropertyBool("GLTF2_NODE_IN_TRS"))) {
aiQuaternion quaternion; aiQuaternion quaternion;

View File

@ -76,6 +76,7 @@ struct OcclusionTextureInfo;
struct Node; struct Node;
struct Texture; struct Texture;
struct PbrSpecularGlossiness; struct PbrSpecularGlossiness;
struct MaterialSpecular;
struct MaterialSheen; struct MaterialSheen;
struct MaterialClearcoat; struct MaterialClearcoat;
struct MaterialTransmission; struct MaterialTransmission;
@ -117,6 +118,7 @@ protected:
aiReturn GetMatColor(const aiMaterial &mat, glTF2::vec4 &prop, const char *propName, int type, int idx) const; aiReturn GetMatColor(const aiMaterial &mat, glTF2::vec4 &prop, const char *propName, int type, int idx) const;
aiReturn GetMatColor(const aiMaterial &mat, glTF2::vec3 &prop, const char *propName, int type, int idx) const; aiReturn GetMatColor(const aiMaterial &mat, glTF2::vec3 &prop, const char *propName, int type, int idx) const;
bool GetMatSpecGloss(const aiMaterial &mat, glTF2::PbrSpecularGlossiness &pbrSG); bool GetMatSpecGloss(const aiMaterial &mat, glTF2::PbrSpecularGlossiness &pbrSG);
bool GetMatSpecular(const aiMaterial &mat, glTF2::MaterialSpecular &specular);
bool GetMatSheen(const aiMaterial &mat, glTF2::MaterialSheen &sheen); bool GetMatSheen(const aiMaterial &mat, glTF2::MaterialSheen &sheen);
bool GetMatClearcoat(const aiMaterial &mat, glTF2::MaterialClearcoat &clearcoat); bool GetMatClearcoat(const aiMaterial &mat, glTF2::MaterialClearcoat &clearcoat);
bool GetMatTransmission(const aiMaterial &mat, glTF2::MaterialTransmission &transmission); bool GetMatTransmission(const aiMaterial &mat, glTF2::MaterialTransmission &transmission);

View File

@ -100,8 +100,6 @@ glTF2Importer::glTF2Importer() :
// empty // empty
} }
glTF2Importer::~glTF2Importer() = default;
const aiImporterDesc *glTF2Importer::GetInfo() const { const aiImporterDesc *glTF2Importer::GetInfo() const {
return &desc; return &desc;
} }
@ -114,7 +112,11 @@ bool glTF2Importer::CanRead(const std::string &filename, IOSystem *pIOHandler, b
if (pIOHandler) { if (pIOHandler) {
glTF2::Asset asset(pIOHandler); glTF2::Asset asset(pIOHandler);
return asset.CanRead(filename, extension == "glb"); return asset.CanRead(
filename,
CheckMagicToken(
pIOHandler, filename, AI_GLB_MAGIC_NUMBER, 1, 0,
static_cast<unsigned int>(strlen(AI_GLB_MAGIC_NUMBER))));
} }
return false; return false;
@ -232,7 +234,8 @@ inline void SetMaterialTextureProperty(std::vector<int> &embeddedTexIdxs, Asset
SetMaterialTextureProperty(embeddedTexIdxs, r, (glTF2::TextureInfo)prop, mat, texType, texSlot); SetMaterialTextureProperty(embeddedTexIdxs, r, (glTF2::TextureInfo)prop, mat, texType, texSlot);
if (prop.texture && prop.texture->source) { if (prop.texture && prop.texture->source) {
mat->AddProperty(&prop.strength, 1, AI_MATKEY_GLTF_TEXTURE_STRENGTH(texType, texSlot)); std::string textureStrengthKey = std::string(_AI_MATKEY_TEXTURE_BASE) + "." + "strength";
mat->AddProperty(&prop.strength, 1, textureStrengthKey.c_str(), texType, texSlot);
} }
} }
@ -278,8 +281,19 @@ static aiMaterial *ImportMaterial(std::vector<int> &embeddedTexIdxs, Asset &r, M
aimat->AddProperty(&alphaMode, AI_MATKEY_GLTF_ALPHAMODE); aimat->AddProperty(&alphaMode, AI_MATKEY_GLTF_ALPHAMODE);
aimat->AddProperty(&mat.alphaCutoff, 1, AI_MATKEY_GLTF_ALPHACUTOFF); aimat->AddProperty(&mat.alphaCutoff, 1, AI_MATKEY_GLTF_ALPHACUTOFF);
// KHR_materials_specular
if (mat.materialSpecular.isPresent) {
MaterialSpecular &specular = mat.materialSpecular.value;
// Default values of zero disables Specular
if (std::memcmp(specular.specularColorFactor, defaultSpecularColorFactor, sizeof(glTFCommon::vec3)) != 0 || specular.specularFactor != 0.0f) {
SetMaterialColorProperty(r, specular.specularColorFactor, aimat, AI_MATKEY_COLOR_SPECULAR);
aimat->AddProperty(&specular.specularFactor, 1, AI_MATKEY_SPECULAR_FACTOR);
SetMaterialTextureProperty(embeddedTexIdxs, r, specular.specularTexture, aimat, aiTextureType_SPECULAR);
SetMaterialTextureProperty(embeddedTexIdxs, r, specular.specularColorTexture, aimat, aiTextureType_SPECULAR);
}
}
// pbrSpecularGlossiness // pbrSpecularGlossiness
if (mat.pbrSpecularGlossiness.isPresent) { else if (mat.pbrSpecularGlossiness.isPresent) {
PbrSpecularGlossiness &pbrSG = mat.pbrSpecularGlossiness.value; PbrSpecularGlossiness &pbrSG = mat.pbrSpecularGlossiness.value;
SetMaterialColorProperty(r, pbrSG.diffuseFactor, aimat, AI_MATKEY_COLOR_DIFFUSE); SetMaterialColorProperty(r, pbrSG.diffuseFactor, aimat, AI_MATKEY_COLOR_DIFFUSE);
@ -432,10 +446,10 @@ static inline bool CheckValidFacesIndices(aiFace *faces, unsigned nFaces, unsign
#endif // ASSIMP_BUILD_DEBUG #endif // ASSIMP_BUILD_DEBUG
template <typename T> template <typename T>
aiColor4D *GetVertexColorsForType(Ref<Accessor> input) { aiColor4D *GetVertexColorsForType(Ref<Accessor> input, std::vector<unsigned int> *vertexRemappingTable) {
constexpr float max = std::numeric_limits<T>::max(); constexpr float max = std::numeric_limits<T>::max();
aiColor4t<T> *colors; aiColor4t<T> *colors;
input->ExtractData(colors); input->ExtractData(colors, vertexRemappingTable);
auto output = new aiColor4D[input->count]; auto output = new aiColor4D[input->count];
for (size_t i = 0; i < input->count; i++) { for (size_t i = 0; i < input->count; i++) {
output[i] = aiColor4D( output[i] = aiColor4D(
@ -450,20 +464,26 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
ASSIMP_LOG_DEBUG("Importing ", r.meshes.Size(), " meshes"); ASSIMP_LOG_DEBUG("Importing ", r.meshes.Size(), " meshes");
std::vector<std::unique_ptr<aiMesh>> meshes; std::vector<std::unique_ptr<aiMesh>> meshes;
unsigned int k = 0;
meshOffsets.clear(); meshOffsets.clear();
meshOffsets.reserve(r.meshes.Size() + 1);
mVertexRemappingTables.clear();
// Count the number of aiMeshes
unsigned int num_aiMeshes = 0;
for (unsigned int m = 0; m < r.meshes.Size(); ++m) {
meshOffsets.push_back(num_aiMeshes);
num_aiMeshes += unsigned(r.meshes[m].primitives.size());
}
meshOffsets.push_back(num_aiMeshes); // add a last element so we can always do meshOffsets[n+1] - meshOffsets[n]
std::vector<unsigned int> usedVertexIndices;
std::vector<unsigned int> reverseMappingIndices; std::vector<unsigned int> reverseMappingIndices;
std::vector<unsigned int> indexBuffer; std::vector<unsigned int> indexBuffer;
meshes.reserve(num_aiMeshes);
mVertexRemappingTables.resize(num_aiMeshes);
for (unsigned int m = 0; m < r.meshes.Size(); ++m) { for (unsigned int m = 0; m < r.meshes.Size(); ++m) {
Mesh &mesh = r.meshes[m]; Mesh &mesh = r.meshes[m];
meshOffsets.push_back(k);
k += unsigned(mesh.primitives.size());
for (unsigned int p = 0; p < mesh.primitives.size(); ++p) { for (unsigned int p = 0; p < mesh.primitives.size(); ++p) {
Mesh::Primitive &prim = mesh.primitives[p]; Mesh::Primitive &prim = mesh.primitives[p];
@ -478,13 +498,13 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
// Extract used vertices: // Extract used vertices:
bool useIndexBuffer = prim.indices; bool useIndexBuffer = prim.indices;
std::vector<unsigned int> *vertexRemappingTable = nullptr; std::vector<unsigned int> *vertexRemappingTable = nullptr;
if (useIndexBuffer) { if (useIndexBuffer) {
size_t count = prim.indices->count; size_t count = prim.indices->count;
indexBuffer.resize(count); indexBuffer.resize(count);
usedVertexIndices.clear();
reverseMappingIndices.clear(); reverseMappingIndices.clear();
usedVertexIndices.reserve(count / 3); // this is a very rough heuristic to reduce re-allocations vertexRemappingTable = &mVertexRemappingTables[meshes.size()];
vertexRemappingTable = &usedVertexIndices; vertexRemappingTable->reserve(count / 3); // this is a very rough heuristic to reduce re-allocations
Accessor::Indexer data = prim.indices->GetIndexer(); Accessor::Indexer data = prim.indices->GetIndexer();
if (!data.IsValid()) { if (!data.IsValid()) {
throw DeadlyImportError("GLTF: Invalid accessor without data in mesh ", getContextForErrorMessages(mesh.id, mesh.name)); throw DeadlyImportError("GLTF: Invalid accessor without data in mesh ", getContextForErrorMessages(mesh.id, mesh.name));
@ -504,8 +524,8 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
reverseMappingIndices.resize(index + 1, unusedIndex); reverseMappingIndices.resize(index + 1, unusedIndex);
} }
if (reverseMappingIndices[index] == unusedIndex) { if (reverseMappingIndices[index] == unusedIndex) {
reverseMappingIndices[index] = static_cast<unsigned int>(usedVertexIndices.size()); reverseMappingIndices[index] = static_cast<unsigned int>(vertexRemappingTable->size());
usedVertexIndices.push_back(index); vertexRemappingTable->push_back(index);
} }
indexBuffer[i] = reverseMappingIndices[index]; indexBuffer[i] = reverseMappingIndices[index];
} }
@ -586,9 +606,9 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
attr.color[c]->ExtractData(aim->mColors[c], vertexRemappingTable); attr.color[c]->ExtractData(aim->mColors[c], vertexRemappingTable);
} else { } else {
if (componentType == glTF2::ComponentType_UNSIGNED_BYTE) { if (componentType == glTF2::ComponentType_UNSIGNED_BYTE) {
aim->mColors[c] = GetVertexColorsForType<unsigned char>(attr.color[c]); aim->mColors[c] = GetVertexColorsForType<unsigned char>(attr.color[c], vertexRemappingTable);
} else if (componentType == glTF2::ComponentType_UNSIGNED_SHORT) { } else if (componentType == glTF2::ComponentType_UNSIGNED_SHORT) {
aim->mColors[c] = GetVertexColorsForType<unsigned short>(attr.color[c]); aim->mColors[c] = GetVertexColorsForType<unsigned short>(attr.color[c], vertexRemappingTable);
} }
} }
} }
@ -864,8 +884,6 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
} }
} }
meshOffsets.push_back(k);
CopyVector(meshes, mScene->mMeshes, mScene->mNumMeshes); CopyVector(meshes, mScene->mMeshes, mScene->mNumMeshes);
} }
@ -998,7 +1016,8 @@ static void GetNodeTransform(aiMatrix4x4 &matrix, const glTF2::Node &node) {
} }
} }
static void BuildVertexWeightMapping(Mesh::Primitive &primitive, std::vector<std::vector<aiVertexWeight>> &map) { static void BuildVertexWeightMapping(Mesh::Primitive &primitive, std::vector<std::vector<aiVertexWeight>> &map, std::vector<unsigned int>* vertexRemappingTablePtr) {
Mesh::Primitive::Attributes &attr = primitive.attributes; Mesh::Primitive::Attributes &attr = primitive.attributes;
if (attr.weight.empty() || attr.joint.empty()) { if (attr.weight.empty() || attr.joint.empty()) {
return; return;
@ -1007,14 +1026,14 @@ static void BuildVertexWeightMapping(Mesh::Primitive &primitive, std::vector<std
return; return;
} }
size_t num_vertices = attr.weight[0]->count; size_t num_vertices = 0;
struct Weights { struct Weights {
float values[4]; float values[4];
}; };
Weights **weights = new Weights*[attr.weight.size()]; Weights **weights = new Weights*[attr.weight.size()];
for (size_t w = 0; w < attr.weight.size(); ++w) { for (size_t w = 0; w < attr.weight.size(); ++w) {
attr.weight[w]->ExtractData(weights[w]); num_vertices = attr.weight[w]->ExtractData(weights[w], vertexRemappingTablePtr);
} }
struct Indices8 { struct Indices8 {
@ -1028,12 +1047,12 @@ static void BuildVertexWeightMapping(Mesh::Primitive &primitive, std::vector<std
if (attr.joint[0]->GetElementSize() == 4) { if (attr.joint[0]->GetElementSize() == 4) {
indices8 = new Indices8*[attr.joint.size()]; indices8 = new Indices8*[attr.joint.size()];
for (size_t j = 0; j < attr.joint.size(); ++j) { for (size_t j = 0; j < attr.joint.size(); ++j) {
attr.joint[j]->ExtractData(indices8[j]); attr.joint[j]->ExtractData(indices8[j], vertexRemappingTablePtr);
} }
} else { } else {
indices16 = new Indices16 *[attr.joint.size()]; indices16 = new Indices16 *[attr.joint.size()];
for (size_t j = 0; j < attr.joint.size(); ++j) { for (size_t j = 0; j < attr.joint.size(); ++j) {
attr.joint[j]->ExtractData(indices16[j]); attr.joint[j]->ExtractData(indices16[j], vertexRemappingTablePtr);
} }
} }
// //
@ -1092,15 +1111,13 @@ void ParseExtensions(aiMetadata *metadata, const CustomExtension &extension) {
} }
} }
void ParseExtras(aiMetadata *metadata, const CustomExtension &extension) { void ParseExtras(aiMetadata* metadata, const Extras& extras) {
if (extension.mValues.isPresent) { for (auto const &value : extras.mValues) {
for (auto const &subExtension : extension.mValues.value) { ParseExtensions(metadata, value);
ParseExtensions(metadata, subExtension);
}
} }
} }
aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector<unsigned int> &meshOffsets, glTF2::Ref<glTF2::Node> &ptr) { aiNode *glTF2Importer::ImportNode(glTF2::Asset &r, glTF2::Ref<glTF2::Node> &ptr) {
Node &node = *ptr; Node &node = *ptr;
aiNode *ainode = new aiNode(GetNodeName(node)); aiNode *ainode = new aiNode(GetNodeName(node));
@ -1112,18 +1129,18 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector<unsigned int> &
std::fill(ainode->mChildren, ainode->mChildren + ainode->mNumChildren, nullptr); std::fill(ainode->mChildren, ainode->mChildren + ainode->mNumChildren, nullptr);
for (unsigned int i = 0; i < ainode->mNumChildren; ++i) { for (unsigned int i = 0; i < ainode->mNumChildren; ++i) {
aiNode *child = ImportNode(pScene, r, meshOffsets, node.children[i]); aiNode *child = ImportNode(r, node.children[i]);
child->mParent = ainode; child->mParent = ainode;
ainode->mChildren[i] = child; ainode->mChildren[i] = child;
} }
} }
if (node.customExtensions || node.extras) { if (node.customExtensions || node.extras.HasExtras()) {
ainode->mMetaData = new aiMetadata; ainode->mMetaData = new aiMetadata;
if (node.customExtensions) { if (node.customExtensions) {
ParseExtensions(ainode->mMetaData, node.customExtensions); ParseExtensions(ainode->mMetaData, node.customExtensions);
} }
if (node.extras) { if (node.extras.HasExtras()) {
ParseExtras(ainode->mMetaData, node.extras); ParseExtras(ainode->mMetaData, node.extras);
} }
} }
@ -1145,11 +1162,13 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector<unsigned int> &
if (node.skin) { if (node.skin) {
for (int primitiveNo = 0; primitiveNo < count; ++primitiveNo) { for (int primitiveNo = 0; primitiveNo < count; ++primitiveNo) {
aiMesh *mesh = pScene->mMeshes[meshOffsets[mesh_idx] + primitiveNo]; unsigned int aiMeshIdx = meshOffsets[mesh_idx] + primitiveNo;
aiMesh *mesh = mScene->mMeshes[aiMeshIdx];
unsigned int numBones = static_cast<unsigned int>(node.skin->jointNames.size()); unsigned int numBones = static_cast<unsigned int>(node.skin->jointNames.size());
std::vector<unsigned int> *vertexRemappingTablePtr = mVertexRemappingTables[aiMeshIdx].empty() ? nullptr : &mVertexRemappingTables[aiMeshIdx];
std::vector<std::vector<aiVertexWeight>> weighting(numBones); std::vector<std::vector<aiVertexWeight>> weighting(numBones);
BuildVertexWeightMapping(node.meshes[0]->primitives[primitiveNo], weighting); BuildVertexWeightMapping(node.meshes[0]->primitives[primitiveNo], weighting, vertexRemappingTablePtr);
mesh->mNumBones = static_cast<unsigned int>(numBones); mesh->mNumBones = static_cast<unsigned int>(numBones);
mesh->mBones = new aiBone *[mesh->mNumBones]; mesh->mBones = new aiBone *[mesh->mNumBones];
@ -1166,7 +1185,7 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector<unsigned int> &
// mapping which makes things doubly-slow. // mapping which makes things doubly-slow.
mat4 *pbindMatrices = nullptr; mat4 *pbindMatrices = nullptr;
node.skin->inverseBindMatrices->ExtractData(pbindMatrices); node.skin->inverseBindMatrices->ExtractData(pbindMatrices, nullptr);
for (uint32_t i = 0; i < numBones; ++i) { for (uint32_t i = 0; i < numBones; ++i) {
const std::vector<aiVertexWeight> &weights = weighting[i]; const std::vector<aiVertexWeight> &weights = weighting[i];
@ -1212,11 +1231,11 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector<unsigned int> &
} }
if (node.camera) { if (node.camera) {
pScene->mCameras[node.camera.GetIndex()]->mName = ainode->mName; mScene->mCameras[node.camera.GetIndex()]->mName = ainode->mName;
} }
if (node.light) { if (node.light) {
pScene->mLights[node.light.GetIndex()]->mName = ainode->mName; mScene->mLights[node.light.GetIndex()]->mName = ainode->mName;
// range is optional - see https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual // range is optional - see https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual
// it is added to meta data of parent node, because there is no other place to put it // it is added to meta data of parent node, because there is no other place to put it
@ -1248,7 +1267,7 @@ void glTF2Importer::ImportNodes(glTF2::Asset &r) {
// The root nodes // The root nodes
unsigned int numRootNodes = unsigned(rootNodes.size()); unsigned int numRootNodes = unsigned(rootNodes.size());
if (numRootNodes == 1) { // a single root node: use it if (numRootNodes == 1) { // a single root node: use it
mScene->mRootNode = ImportNode(mScene, r, meshOffsets, rootNodes[0]); mScene->mRootNode = ImportNode(r, rootNodes[0]);
} else if (numRootNodes > 1) { // more than one root node: create a fake root } else if (numRootNodes > 1) { // more than one root node: create a fake root
aiNode *root = mScene->mRootNode = new aiNode("ROOT"); aiNode *root = mScene->mRootNode = new aiNode("ROOT");
@ -1256,7 +1275,7 @@ void glTF2Importer::ImportNodes(glTF2::Asset &r) {
std::fill(root->mChildren, root->mChildren + numRootNodes, nullptr); std::fill(root->mChildren, root->mChildren + numRootNodes, nullptr);
for (unsigned int i = 0; i < numRootNodes; ++i) { for (unsigned int i = 0; i < numRootNodes; ++i) {
aiNode *node = ImportNode(mScene, r, meshOffsets, rootNodes[i]); aiNode *node = ImportNode(r, rootNodes[i]);
node->mParent = root; node->mParent = root;
root->mChildren[root->mNumChildren++] = node; root->mChildren[root->mNumChildren++] = node;
} }
@ -1657,13 +1676,17 @@ void glTF2Importer::InternReadFile(const std::string &pFile, aiScene *pScene, IO
// clean all member arrays // clean all member arrays
meshOffsets.clear(); meshOffsets.clear();
mVertexRemappingTables.clear();
mEmbeddedTexIdxs.clear(); mEmbeddedTexIdxs.clear();
this->mScene = pScene; this->mScene = pScene;
// read the asset file // read the asset file
glTF2::Asset asset(pIOHandler, static_cast<rapidjson::IRemoteSchemaDocumentProvider *>(mSchemaDocumentProvider)); glTF2::Asset asset(pIOHandler, static_cast<rapidjson::IRemoteSchemaDocumentProvider *>(mSchemaDocumentProvider));
asset.Load(pFile, GetExtension(pFile) == "glb"); asset.Load(pFile,
CheckMagicToken(
pIOHandler, pFile, AI_GLB_MAGIC_NUMBER, 1, 0,
static_cast<unsigned int>(strlen(AI_GLB_MAGIC_NUMBER))));
if (asset.scene) { if (asset.scene) {
pScene->mName = asset.scene->name; pScene->mName = asset.scene->name;
} }

View File

@ -43,6 +43,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define AI_GLTF2IMPORTER_H_INC #define AI_GLTF2IMPORTER_H_INC
#include <assimp/BaseImporter.h> #include <assimp/BaseImporter.h>
#include <AssetLib/glTF2/glTF2Asset.h>
struct aiNode; struct aiNode;
@ -59,7 +60,7 @@ namespace Assimp {
class glTF2Importer : public BaseImporter { class glTF2Importer : public BaseImporter {
public: public:
glTF2Importer(); glTF2Importer();
~glTF2Importer() override; ~glTF2Importer() override = default;
bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const override; bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const override;
protected: protected:
@ -76,10 +77,12 @@ private:
void ImportNodes(glTF2::Asset &a); void ImportNodes(glTF2::Asset &a);
void ImportAnimations(glTF2::Asset &a); void ImportAnimations(glTF2::Asset &a);
void ImportCommonMetadata(glTF2::Asset &a); void ImportCommonMetadata(glTF2::Asset &a);
aiNode *ImportNode(glTF2::Asset &r, glTF2::Ref<glTF2::Node> &ptr);
private: private:
std::vector<unsigned int> meshOffsets; std::vector<unsigned int> meshOffsets;
std::vector<int> mEmbeddedTexIdxs; std::vector<int> mEmbeddedTexIdxs;
std::vector<std::vector<unsigned int>> mVertexRemappingTables; // for each converted aiMesh in the scene, it stores a list of vertices that are actually used
aiScene *mScene; aiScene *mScene;
/// An instance of rapidjson::IRemoteSchemaDocumentProvider /// An instance of rapidjson::IRemoteSchemaDocumentProvider

View File

@ -194,6 +194,8 @@ SET( Common_SRCS
Common/ScenePreprocessor.cpp Common/ScenePreprocessor.cpp
Common/ScenePreprocessor.h Common/ScenePreprocessor.h
Common/SkeletonMeshBuilder.cpp Common/SkeletonMeshBuilder.cpp
Common/StackAllocator.h
Common/StackAllocator.inl
Common/StandardShapes.cpp Common/StandardShapes.cpp
Common/TargetAnimation.cpp Common/TargetAnimation.cpp
Common/TargetAnimation.h Common/TargetAnimation.h
@ -963,7 +965,6 @@ IF(ASSIMP_HUNTER_ENABLED)
find_package(minizip CONFIG REQUIRED) find_package(minizip CONFIG REQUIRED)
ELSE() ELSE()
SET( unzip_SRCS SET( unzip_SRCS
../contrib/unzip/crypt.c
../contrib/unzip/crypt.h ../contrib/unzip/crypt.h
../contrib/unzip/ioapi.c ../contrib/unzip/ioapi.c
../contrib/unzip/ioapi.h ../contrib/unzip/ioapi.h
@ -1198,7 +1199,6 @@ IF (ASSIMP_WARNINGS_AS_ERRORS)
IF(CMAKE_CXX_COMPILER_ID MATCHES "Clang" ) # clang-cl IF(CMAKE_CXX_COMPILER_ID MATCHES "Clang" ) # clang-cl
TARGET_COMPILE_OPTIONS(assimp PRIVATE -Wall -Werror TARGET_COMPILE_OPTIONS(assimp PRIVATE -Wall -Werror
-Wno-unused-function
-Wno-microsoft-enum-value -Wno-microsoft-enum-value
-Wno-switch-enum -Wno-switch-enum
-Wno-covered-switch-default -Wno-covered-switch-default
@ -1234,14 +1234,11 @@ IF (ASSIMP_WARNINGS_AS_ERRORS)
-Wno-header-hygiene -Wno-header-hygiene
-Wno-tautological-value-range-compare -Wno-tautological-value-range-compare
-Wno-tautological-type-limit-compare -Wno-tautological-type-limit-compare
-Wno-missing-noreturn
-Wno-missing-variable-declarations -Wno-missing-variable-declarations
-Wno-extra-semi -Wno-extra-semi
-Wno-nonportable-system-include-path -Wno-nonportable-system-include-path
-Wno-undefined-reinterpret-cast -Wno-undefined-reinterpret-cast
-Wno-shift-sign-overflow -Wno-shift-sign-overflow
-Wno-deprecated-copy-with-user-provided-dtor
-Wno-deprecated-copy-with-dtor
-Wno-deprecated -Wno-deprecated
-Wno-format-nonliteral -Wno-format-nonliteral
-Wno-comma -Wno-comma
@ -1391,7 +1388,7 @@ ENDIF()
# Add RT-extension library for glTF importer with Open3DGC-compression. # Add RT-extension library for glTF importer with Open3DGC-compression.
IF (RT_FOUND AND ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC) IF (RT_FOUND AND ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC)
TARGET_LINK_LIBRARIES(assimp ${RT_LIBRARY}) TARGET_LINK_LIBRARIES(assimp rt)
ENDIF () ENDIF ()
@ -1421,9 +1418,8 @@ if(MSVC AND ASSIMP_INSTALL_PDB)
COMPILE_PDB_NAME assimp${LIBRARY_SUFFIX} COMPILE_PDB_NAME assimp${LIBRARY_SUFFIX}
COMPILE_PDB_NAME_DEBUG assimp${LIBRARY_SUFFIX}${CMAKE_DEBUG_POSTFIX} COMPILE_PDB_NAME_DEBUG assimp${LIBRARY_SUFFIX}${CMAKE_DEBUG_POSTFIX}
) )
ENDIF()
IF(CMAKE_GENERATOR MATCHES "^Visual Studio") IF(GENERATOR_IS_MULTI_CONFIG)
install(FILES ${Assimp_BINARY_DIR}/code/Debug/assimp${LIBRARY_SUFFIX}${CMAKE_DEBUG_POSTFIX}.pdb install(FILES ${Assimp_BINARY_DIR}/code/Debug/assimp${LIBRARY_SUFFIX}${CMAKE_DEBUG_POSTFIX}.pdb
DESTINATION ${ASSIMP_LIB_INSTALL_DIR} DESTINATION ${ASSIMP_LIB_INSTALL_DIR}
CONFIGURATIONS Debug CONFIGURATIONS Debug
@ -1442,6 +1438,11 @@ if(MSVC AND ASSIMP_INSTALL_PDB)
CONFIGURATIONS RelWithDebInfo CONFIGURATIONS RelWithDebInfo
) )
ENDIF() ENDIF()
ELSE()
install(FILES $<TARGET_PDB_FILE:assimp>
DESTINATION ${ASSIMP_LIB_INSTALL_DIR}
)
ENDIF()
ENDIF () ENDIF ()
if (ASSIMP_COVERALLS) if (ASSIMP_COVERALLS)

View File

@ -59,6 +59,31 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <memory> #include <memory>
#include <sstream> #include <sstream>
namespace {
// Checks whether the passed string is a gcs version.
bool IsGcsVersion(const std::string &s) {
if (s.empty()) return false;
return std::all_of(s.cbegin(), s.cend(), [](const char c) {
// gcs only permits numeric characters.
return std::isdigit(static_cast<int>(c));
});
}
// Removes a possible version hash from a filename, as found for example in
// gcs uris (e.g. `gs://bucket/model.glb#1234`), see also
// https://github.com/GoogleCloudPlatform/gsutil/blob/c80f329bc3c4011236c78ce8910988773b2606cb/gslib/storage_url.py#L39.
std::string StripVersionHash(const std::string &filename) {
const std::string::size_type pos = filename.find_last_of('#');
// Only strip if the hash is behind a possible file extension and the part
// behind the hash is a version string.
if (pos != std::string::npos && pos > filename.find_last_of('.') &&
IsGcsVersion(filename.substr(pos + 1))) {
return filename.substr(0, pos);
}
return filename;
}
} // namespace
using namespace Assimp; using namespace Assimp;
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -158,7 +183,7 @@ void BaseImporter::GetExtensionList(std::set<std::string> &extensions) {
std::size_t numTokens, std::size_t numTokens,
unsigned int searchBytes /* = 200 */, unsigned int searchBytes /* = 200 */,
bool tokensSol /* false */, bool tokensSol /* false */,
bool noAlphaBeforeTokens /* false */) { bool noGraphBeforeTokens /* false */) {
ai_assert(nullptr != tokens); ai_assert(nullptr != tokens);
ai_assert(0 != numTokens); ai_assert(0 != numTokens);
ai_assert(0 != searchBytes); ai_assert(0 != searchBytes);
@ -207,8 +232,9 @@ void BaseImporter::GetExtensionList(std::set<std::string> &extensions) {
continue; continue;
} }
// We need to make sure that we didn't accidentally 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 " // e.g. in a previous version the "gltf " present in some gltf files was detected as "f ", or a
if (noAlphaBeforeTokens && (r != buffer && isalpha(static_cast<unsigned char>(r[-1])))) { // Blender-exported glb file containing "Khronos glTF Blender I/O " was detected as "o "
if (noGraphBeforeTokens && (r != buffer && isgraph(static_cast<unsigned char>(r[-1])))) {
continue; continue;
} }
// We got a match, either we don't care where it is, or it happens to // We got a match, either we don't care where it is, or it happens to
@ -229,33 +255,38 @@ void BaseImporter::GetExtensionList(std::set<std::string> &extensions) {
const char *ext0, const char *ext0,
const char *ext1, const char *ext1,
const char *ext2) { const char *ext2) {
std::string::size_type pos = pFile.find_last_of('.'); std::set<std::string> extensions;
for (const char* ext : {ext0, ext1, ext2}) {
// no file extension - can't read if (ext == nullptr) continue;
if (pos == std::string::npos) { extensions.emplace(ext);
return false; }
return HasExtension(pFile, extensions);
} }
const char *ext_real = &pFile[pos + 1]; // ------------------------------------------------------------------------------------------------
if (!ASSIMP_stricmp(ext_real, ext0)) { // Check for file extension
/*static*/ bool BaseImporter::HasExtension(const std::string &pFile, const std::set<std::string> &extensions) {
const std::string file = StripVersionHash(pFile);
// 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 (const std::string& ext : extensions) {
// Yay for C++<20 not having std::string::ends_with()
const std::string dotExt = "." + ext;
if (dotExt.length() > file.length()) continue;
// Possible optimization: Fetch the lowercase filename!
if (0 == ASSIMP_stricmp(file.c_str() + file.length() - dotExt.length(), dotExt.c_str())) {
return true; return true;
} }
// check for other, optional, file extensions
if (ext1 && !ASSIMP_stricmp(ext_real, ext1)) {
return true;
} }
if (ext2 && !ASSIMP_stricmp(ext_real, ext2)) {
return true;
}
return false; return false;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Get file extension from path // Get file extension from path
std::string BaseImporter::GetExtension(const std::string &file) { std::string BaseImporter::GetExtension(const std::string &pFile) {
const std::string file = StripVersionHash(pFile);
std::string::size_type pos = file.find_last_of('.'); std::string::size_type pos = file.find_last_of('.');
// no file extension at all // no file extension at all
@ -281,12 +312,7 @@ std::string BaseImporter::GetExtension(const std::string &file) {
if (!pIOHandler) { if (!pIOHandler) {
return false; return false;
} }
union { const char *magic = reinterpret_cast<const char *>(_magic);
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)); std::unique_ptr<IOStream> pStream(pIOHandler->Open(pFile));
if (pStream) { if (pStream) {
@ -308,15 +334,15 @@ std::string BaseImporter::GetExtension(const std::string &file) {
// that's just for convenience, the chance that we cause conflicts // that's just for convenience, the chance that we cause conflicts
// is quite low and it can save some lines and prevent nasty bugs // is quite low and it can save some lines and prevent nasty bugs
if (2 == size) { if (2 == size) {
uint16_t rev = *magic_u16; uint16_t magic_u16;
ByteSwap::Swap(&rev); memcpy(&magic_u16, magic, 2);
if (data_u16[0] == *magic_u16 || data_u16[0] == rev) { if (data_u16[0] == magic_u16 || data_u16[0] == ByteSwap::Swapped(magic_u16)) {
return true; return true;
} }
} else if (4 == size) { } else if (4 == size) {
uint32_t rev = *magic_u32; uint32_t magic_u32;
ByteSwap::Swap(&rev); memcpy(&magic_u32, magic, 4);
if (data_u32[0] == *magic_u32 || data_u32[0] == rev) { if (data_u32[0] == magic_u32 || data_u32[0] == ByteSwap::Swapped(magic_u32)) {
return true; return true;
} }
} else { } else {

View File

@ -225,7 +225,7 @@ static void setupExporterArray(std::vector<Exporter::ExportFormatEntry> &exporte
#endif #endif
#ifndef ASSIMP_BUILD_NO_PBRT_EXPORTER #ifndef ASSIMP_BUILD_NO_PBRT_EXPORTER
exporters.emplace_back("pbrt", "pbrt-v4 scene description file", "pbrt", &ExportScenePbrt, aiProcess_Triangulate | aiProcess_SortByPType); exporters.emplace_back("pbrt", "pbrt-v4 scene description file", "pbrt", &ExportScenePbrt, aiProcess_ConvertToLeftHanded | aiProcess_Triangulate | aiProcess_SortByPType);
#endif #endif
#ifndef ASSIMP_BUILD_NO_ASSJSON_EXPORTER #ifndef ASSIMP_BUILD_NO_ASSJSON_EXPORTER

View File

@ -297,7 +297,7 @@ private:
} }
const char separator = getOsSeparator(); const char separator = getOsSeparator();
for (it = in.begin(); it != in.end(); ++it) { for (it = in.begin(); it < in.end(); ++it) {
const size_t remaining = std::distance(in.end(), it); const size_t remaining = std::distance(in.end(), it);
// Exclude :// and \\, which remain untouched. // Exclude :// and \\, which remain untouched.
// https://sourceforge.net/tracker/?func=detail&aid=3031725&group_id=226462&atid=1067632 // https://sourceforge.net/tracker/?func=detail&aid=3031725&group_id=226462&atid=1067632

View File

@ -637,26 +637,12 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags) {
std::set<std::string> extensions; std::set<std::string> extensions;
pimpl->mImporter[a]->GetExtensionList(extensions); pimpl->mImporter[a]->GetExtensionList(extensions);
// CAUTION: Do not just search for the extension! if (BaseImporter::HasExtension(pFile, extensions)) {
// 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 }; ImporterAndIndex candidate = { pimpl->mImporter[a], a };
possibleImporters.push_back(candidate); possibleImporters.push_back(candidate);
break;
} }
} }
}
}
// If just one importer supports this extension, pick it and close the case. // If just one importer supports this extension, pick it and close the case.
BaseImporter* imp = nullptr; BaseImporter* imp = nullptr;
if (1 == possibleImporters.size()) { if (1 == possibleImporters.size()) {

View File

@ -64,8 +64,14 @@ inline double GetArea2D(const T& v1, const T& v2, const T& v3) {
* The function accepts an unconstrained template parameter for use with * The function accepts an unconstrained template parameter for use with
* both aiVector3D and aiVector2D, but generally ignores the third coordinate.*/ * both aiVector3D and aiVector2D, but generally ignores the third coordinate.*/
template <typename T> template <typename T>
inline bool OnLeftSideOfLine2D(const T& p0, const T& p1,const T& p2) { inline int OnLeftSideOfLine2D(const T& p0, const T& p1,const T& p2) {
return GetArea2D(p0,p2,p1) > 0; double area = GetArea2D(p0,p2,p1);
if(std::abs(area) < ai_epsilon)
return 0;
else if(area > 0)
return 1;
else
return -1;
} }
// ------------------------------------------------------------------------------- // -------------------------------------------------------------------------------
@ -75,7 +81,10 @@ inline bool OnLeftSideOfLine2D(const T& p0, const T& p1,const T& p2) {
template <typename T> 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) {
// pp should be left side of the three triangle side, by ccw arrow // pp should be left side of the three triangle side, by ccw arrow
return OnLeftSideOfLine2D(p0, p1, pp) && OnLeftSideOfLine2D(p1, p2, pp) && OnLeftSideOfLine2D(p2, p0, pp); int c1 = OnLeftSideOfLine2D(p0, p1, pp);
int c2 = OnLeftSideOfLine2D(p1, p2, pp);
int c3 = OnLeftSideOfLine2D(p2, p0, pp);
return (c1 >= 0) && (c2 >= 0) && (c3 >= 0);
} }
@ -110,7 +119,7 @@ inline bool IsCCW(T* in, size_t npoints) {
c = std::sqrt(cc); c = std::sqrt(cc);
theta = std::acos((bb + cc - aa) / (2 * b * c)); theta = std::acos((bb + cc - aa) / (2 * b * c));
if (OnLeftSideOfLine2D(in[i],in[i+2],in[i+1])) { if (OnLeftSideOfLine2D(in[i],in[i+2],in[i+1]) == 1) {
// if (convex(in[i].x, in[i].y, // if (convex(in[i].x, in[i].y,
// in[i+1].x, in[i+1].y, // in[i+1].x, in[i+1].y,
// in[i+2].x, in[i+2].y)) { // in[i+2].x, in[i+2].y)) {
@ -140,7 +149,7 @@ inline bool IsCCW(T* in, size_t npoints) {
//if (convex(in[npoints-2].x, in[npoints-2].y, //if (convex(in[npoints-2].x, in[npoints-2].y,
// in[0].x, in[0].y, // in[0].x, in[0].y,
// in[1].x, in[1].y)) { // in[1].x, in[1].y)) {
if (OnLeftSideOfLine2D(in[npoints-2],in[1],in[0])) { if (OnLeftSideOfLine2D(in[npoints-2],in[1],in[0]) == 1) {
convex_turn = AI_MATH_PI_F - theta; convex_turn = AI_MATH_PI_F - theta;
convex_sum += convex_turn; convex_sum += convex_turn;
} else { } else {

View File

@ -1349,6 +1349,9 @@ void SceneCombiner::Copy(aiMetadata **_dest, const aiMetadata *src) {
case AI_AIVECTOR3D: case AI_AIVECTOR3D:
out.mData = new aiVector3D(*static_cast<aiVector3D *>(in.mData)); out.mData = new aiVector3D(*static_cast<aiVector3D *>(in.mData));
break; break;
case AI_AIMETADATA:
out.mData = new aiMetadata(*static_cast<aiMetadata *>(in.mData));
break;
default: default:
ai_assert(false); ai_assert(false);
break; break;

View File

@ -0,0 +1,92 @@
/*
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 StackAllocator.h
* @brief A very bare-bone allocator class that is suitable when
* allocating many small objects, e.g. during parsing.
* Individual objects are not freed, instead only the whole memory
* can be deallocated.
*/
#ifndef AI_STACK_ALLOCATOR_H_INC
#define AI_STACK_ALLOCATOR_H_INC
#include <vector>
#include <stdint.h>
#include <stddef.h>
namespace Assimp {
/** @brief A very bare-bone allocator class that is suitable when
* allocating many small objects, e.g. during parsing.
* Individual objects are not freed, instead only the whole memory
* can be deallocated.
*/
class StackAllocator {
public:
/// @brief Constructs the allocator
inline StackAllocator();
/// @brief Destructs the allocator and frees all memory
inline ~StackAllocator();
// non copyable
StackAllocator(const StackAllocator &) = delete;
StackAllocator &operator=(const StackAllocator &) = delete;
/// @brief Returns a pointer to byteSize bytes of heap memory that persists
/// for the lifetime of the allocator (or until FreeAll is called).
inline void *Allocate(size_t byteSize);
/// @brief Releases all the memory owned by this allocator.
// Memory provided through function Allocate is not valid anymore after this function has been called.
inline void FreeAll();
private:
constexpr const static size_t g_maxBytesPerBlock = 64 * 1024 * 1024; // The maximum size (in bytes) of a block
constexpr const static size_t g_startBytesPerBlock = 16 * 1024; // Size of the first block. Next blocks will double in size until maximum size of g_maxBytesPerBlock
size_t m_blockAllocationSize = g_startBytesPerBlock; // Block size of the current block
size_t m_subIndex = g_maxBytesPerBlock; // The current byte offset in the current block
std::vector<uint8_t *> m_storageBlocks; // A list of blocks
};
} // namespace Assimp
#include "StackAllocator.inl"
#endif // include guard

View File

@ -0,0 +1,82 @@
/*
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 "StackAllocator.h"
#include <assimp/ai_assert.h>
using namespace Assimp;
inline StackAllocator::StackAllocator() {
}
inline StackAllocator::~StackAllocator() {
FreeAll();
}
inline void *StackAllocator::Allocate(size_t byteSize) {
if (m_subIndex + byteSize > m_blockAllocationSize) // start a new block
{
// double block size every time, up to maximum of g_maxBytesPerBlock.
// Block size must be at least as large as byteSize, but we want to use this for small allocations anyway.
m_blockAllocationSize = std::max(std::min(m_blockAllocationSize * 2, g_maxBytesPerBlock), byteSize);
uint8_t *data = new uint8_t[m_blockAllocationSize];
m_storageBlocks.emplace_back(data);
m_subIndex = byteSize;
return data;
}
uint8_t *data = m_storageBlocks.back();
data += m_subIndex;
m_subIndex += byteSize;
return data;
}
inline void StackAllocator::FreeAll() {
for (size_t i = 0; i < m_storageBlocks.size(); i++) {
delete [] m_storageBlocks[i];
}
std::vector<uint8_t *> empty;
m_storageBlocks.swap(empty);
// start over:
m_blockAllocationSize = g_startBytesPerBlock;
m_subIndex = g_maxBytesPerBlock;
}

View File

@ -48,6 +48,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma GCC diagnostic ignored "-Wunused-function" #pragma GCC diagnostic ignored "-Wunused-function"
#endif #endif
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-function"
#endif
#ifndef STB_USE_HUNTER #ifndef STB_USE_HUNTER
/* Use prefixed names for the symbols from stb_image as it is a very commonly embedded library. /* Use prefixed names for the symbols from stb_image as it is a very commonly embedded library.
Including vanilla stb_image symbols causes duplicate symbol problems if assimp is linked Including vanilla stb_image symbols causes duplicate symbol problems if assimp is linked
@ -114,3 +119,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
#endif #endif
#if defined(__clang__)
#pragma clang diagnostic pop
#endif

View File

@ -45,35 +45,59 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace Assimp { namespace Assimp {
// ------------------------------------------------------------------------------------------------
ai_real GeometryUtils::heron( ai_real a, ai_real b, ai_real c ) { ai_real GeometryUtils::heron( ai_real a, ai_real b, ai_real c ) {
ai_real s = (a + b + c) / 2; const ai_real s = (a + b + c) / 2;
ai_real area = pow((s * ( s - a ) * ( s - b ) * ( s - c ) ), (ai_real)0.5 ); const ai_real area = pow((s * ( s - a ) * ( s - b ) * ( s - c ) ), (ai_real)0.5 );
return area; return area;
} }
ai_real GeometryUtils::distance3D( const aiVector3D &vA, aiVector3D &vB ) { // ------------------------------------------------------------------------------------------------
ai_real GeometryUtils::distance3D( const aiVector3D &vA, const aiVector3D &vB ) {
const ai_real lx = ( vB.x - vA.x ); const ai_real lx = ( vB.x - vA.x );
const ai_real ly = ( vB.y - vA.y ); const ai_real ly = ( vB.y - vA.y );
const ai_real lz = ( vB.z - vA.z ); const ai_real lz = ( vB.z - vA.z );
ai_real a = lx*lx + ly*ly + lz*lz; const ai_real a = lx*lx + ly*ly + lz*lz;
ai_real d = pow( a, (ai_real)0.5 ); const ai_real d = pow( a, (ai_real)0.5 );
return d; return d;
} }
// ------------------------------------------------------------------------------------------------
ai_real GeometryUtils::calculateAreaOfTriangle( const aiFace& face, aiMesh* mesh ) { ai_real GeometryUtils::calculateAreaOfTriangle( const aiFace& face, aiMesh* mesh ) {
ai_real area = 0; ai_real area = 0;
aiVector3D vA( mesh->mVertices[ face.mIndices[ 0 ] ] ); const aiVector3D vA( mesh->mVertices[ face.mIndices[ 0 ] ] );
aiVector3D vB( mesh->mVertices[ face.mIndices[ 1 ] ] ); const aiVector3D vB( mesh->mVertices[ face.mIndices[ 1 ] ] );
aiVector3D vC( mesh->mVertices[ face.mIndices[ 2 ] ] ); const aiVector3D vC( mesh->mVertices[ face.mIndices[ 2 ] ] );
ai_real a( distance3D( vA, vB ) ); const ai_real a = distance3D( vA, vB );
ai_real b( distance3D( vB, vC ) ); const ai_real b = distance3D( vB, vC );
ai_real c( distance3D( vC, vA ) ); const ai_real c = distance3D( vC, vA );
area = heron( a, b, c ); area = heron( a, b, c );
return area; return area;
} }
// ------------------------------------------------------------------------------------------------
// Check whether a ray intersects a plane and find the intersection point
bool GeometryUtils::PlaneIntersect(const aiRay& ray, const aiVector3D& planePos,
const aiVector3D& planeNormal, aiVector3D& pos) {
const ai_real b = planeNormal * (planePos - ray.pos);
ai_real h = ray.dir * planeNormal;
if ((h < 10e-5 && h > -10e-5) || (h = b/h) < 0)
return false;
pos = ray.pos + (ray.dir * h);
return true;
}
// ------------------------------------------------------------------------------------------------
void GeometryUtils::normalizeVectorArray(aiVector3D *vectorArrayIn, aiVector3D *vectorArrayOut,
size_t numVectors) {
for (size_t i=0; i<numVectors; ++i) {
vectorArrayOut[i] = vectorArrayIn[i].Normalize();
}
}
} // namespace Assimp } // namespace Assimp

View File

@ -47,7 +47,7 @@ namespace Assimp {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/// @brief This helper class supports some basic geometry algorithms. /// @brief This helper class supports some basic geometry algorithms.
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
class GeometryUtils { class ASSIMP_API GeometryUtils {
public: public:
static ai_real heron( ai_real a, ai_real b, ai_real c ); static ai_real heron( ai_real a, ai_real b, ai_real c );
@ -55,13 +55,27 @@ public:
/// @param vA Vector a. /// @param vA Vector a.
/// @param vB Vector b. /// @param vB Vector b.
/// @return The distance. /// @return The distance.
static ai_real distance3D( const aiVector3D &vA, aiVector3D &vB ); static ai_real distance3D( const aiVector3D &vA, const aiVector3D &vB );
/// @brief Will calculate the area of a triangle described by a aiFace. /// @brief Will calculate the area of a triangle described by a aiFace.
/// @param face The face /// @param face The face
/// @param mesh The mesh containing the face /// @param mesh The mesh containing the face
/// @return The area. /// @return The area.
static ai_real calculateAreaOfTriangle( const aiFace& face, aiMesh* mesh ); static ai_real calculateAreaOfTriangle( const aiFace& face, aiMesh* mesh );
/// @brief Will calculate the intersection between a ray and a plane
/// @param ray The ray to test for
/// @param planePos A point on the plane
/// @param planeNormal The plane normal to describe its orientation
/// @param pos The position of the intersection.
/// @return true is an intersection was detected, false if not.
static bool PlaneIntersect(const aiRay& ray, const aiVector3D& planePos, const aiVector3D& planeNormal, aiVector3D& pos);
/// @brief Will normalize an array of vectors.
/// @param vectorArrayIn The incoming arra of vectors.
/// @param vectorArrayOut The normalized vectors.
/// @param numVectors The array size.
static void normalizeVectorArray(aiVector3D *vectorArrayIn, aiVector3D *vectorArrayOut, size_t numVectors);
}; };
} // namespace Assimp } // namespace Assimp

View File

@ -51,6 +51,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/material.h> #include <assimp/material.h>
#include <assimp/types.h> #include <assimp/types.h>
#include <assimp/DefaultLogger.hpp> #include <assimp/DefaultLogger.hpp>
#include <memory>
using namespace Assimp; using namespace Assimp;
@ -473,7 +474,7 @@ aiReturn aiMaterial::AddBinaryProperty(const void *pInput,
} }
// Allocate a new material property // Allocate a new material property
aiMaterialProperty *pcNew = new aiMaterialProperty(); std::unique_ptr<aiMaterialProperty> pcNew(new aiMaterialProperty());
// .. and fill it // .. and fill it
pcNew->mType = pType; pcNew->mType = pType;
@ -489,7 +490,7 @@ aiReturn aiMaterial::AddBinaryProperty(const void *pInput,
strcpy(pcNew->mKey.data, pKey); strcpy(pcNew->mKey.data, pKey);
if (UINT_MAX != iOutIndex) { if (UINT_MAX != iOutIndex) {
mProperties[iOutIndex] = pcNew; mProperties[iOutIndex] = pcNew.release();
return AI_SUCCESS; return AI_SUCCESS;
} }
@ -502,7 +503,6 @@ aiReturn aiMaterial::AddBinaryProperty(const void *pInput,
try { try {
ppTemp = new aiMaterialProperty *[mNumAllocated]; ppTemp = new aiMaterialProperty *[mNumAllocated];
} catch (std::bad_alloc &) { } catch (std::bad_alloc &) {
delete pcNew;
return AI_OUTOFMEMORY; return AI_OUTOFMEMORY;
} }
@ -513,7 +513,7 @@ aiReturn aiMaterial::AddBinaryProperty(const void *pInput,
mProperties = ppTemp; mProperties = ppTemp;
} }
// push back ... // push back ...
mProperties[mNumProperties++] = pcNew; mProperties[mNumProperties++] = pcNew.release();
return AI_SUCCESS; return AI_SUCCESS;
} }

View File

@ -111,7 +111,22 @@ PbrtExporter::PbrtExporter(
mScene(pScene), mScene(pScene),
mIOSystem(pIOSystem), mIOSystem(pIOSystem),
mPath(path), mPath(path),
mFile(file) { mFile(file),
mRootTransform(
// rotates the (already left-handed) CRS -90 degrees around the x axis in order to
// make +Z 'up' and +Y 'towards viewer', as in default in pbrt
1.f, 0.f, 0.f, 0.f, //
0.f, 0.f, -1.f, 0.f, //
0.f, 1.f, 0.f, 0.f, //
0.f, 0.f, 0.f, 1.f //
) {
mRootTransform = aiMatrix4x4(
-1.f, 0, 0.f, 0.f, //
0.0f, -1.f, 0.f, 0.f, //
0.f, 0.f, 1.f, 0.f, //
0.f, 0.f, 0.f, 1.f //
) * mRootTransform;
// Export embedded textures. // Export embedded textures.
if (mScene->mNumTextures > 0) if (mScene->mNumTextures > 0)
if (!mIOSystem->CreateDirectory("textures")) if (!mIOSystem->CreateDirectory("textures"))
@ -260,7 +275,7 @@ aiMatrix4x4 PbrtExporter::GetNodeTransform(const aiString &name) const {
node = node->mParent; node = node->mParent;
} }
} }
return m; return mRootTransform * m;
} }
std::string PbrtExporter::TransformAsString(const aiMatrix4x4 &m) { std::string PbrtExporter::TransformAsString(const aiMatrix4x4 &m) {
@ -309,7 +324,7 @@ void PbrtExporter::WriteCamera(int i) {
// Get camera fov // Get camera fov
float hfov = AI_RAD_TO_DEG(camera->mHorizontalFOV); float hfov = AI_RAD_TO_DEG(camera->mHorizontalFOV);
float fov = (aspect >= 1.0) ? hfov : (hfov * aspect); float fov = (aspect >= 1.0) ? hfov : (hfov / aspect);
if (fov < 5) { if (fov < 5) {
std::cerr << fov << ": suspiciously low field of view specified by camera. Setting to 45 degrees.\n"; std::cerr << fov << ": suspiciously low field of view specified by camera. Setting to 45 degrees.\n";
fov = 45; fov = 45;
@ -327,7 +342,7 @@ void PbrtExporter::WriteCamera(int i) {
if (!cameraActive) if (!cameraActive)
mOutput << "# "; mOutput << "# ";
mOutput << "Scale -1 1 1\n"; // right handed -> left handed mOutput << "Scale 1 1 1\n";
if (!cameraActive) if (!cameraActive)
mOutput << "# "; mOutput << "# ";
mOutput << "LookAt " mOutput << "LookAt "
@ -383,8 +398,8 @@ void PbrtExporter::WriteWorldDefinition() {
} }
mOutput << "# Geometry\n\n"; mOutput << "# Geometry\n\n";
aiMatrix4x4 worldFromObject;
WriteGeometricObjects(mScene->mRootNode, worldFromObject, meshUses); WriteGeometricObjects(mScene->mRootNode, mRootTransform, meshUses);
} }
void PbrtExporter::WriteTextures() { void PbrtExporter::WriteTextures() {

View File

@ -100,6 +100,9 @@ private:
// A private set to keep track of which textures have been declared // A private set to keep track of which textures have been declared
std::set<std::string> mTextureSet; std::set<std::string> mTextureSet;
// Transform to apply to the root node and all root objects such as cameras, lights, etc.
aiMatrix4x4 mRootTransform;
aiMatrix4x4 GetNodeTransform(const aiString& name) const; aiMatrix4x4 GetNodeTransform(const aiString& name) const;
static std::string TransformAsString(const aiMatrix4x4& m); static std::string TransformAsString(const aiMatrix4x4& m);

View File

@ -43,15 +43,18 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/DefaultLogger.hpp> #include <assimp/DefaultLogger.hpp>
#include <assimp/postprocess.h> #include <assimp/postprocess.h>
#include <assimp/scene.h> #include <assimp/scene.h>
#include <iostream>
namespace Assimp { namespace Assimp {
/// The default class constructor. static bool IsBoneNode(const aiString &bone_name, std::vector<aiBone *> &bones) {
ArmaturePopulate::ArmaturePopulate() = default; for (aiBone *bone : bones) {
if (bone->mName == bone_name) {
return true;
}
}
/// The class destructor. return false;
ArmaturePopulate::~ArmaturePopulate() = default; }
bool ArmaturePopulate::IsActive(unsigned int pFlags) const { bool ArmaturePopulate::IsActive(unsigned int pFlags) const {
return (pFlags & aiProcess_PopulateArmatureData) != 0; return (pFlags & aiProcess_PopulateArmatureData) != 0;
@ -70,7 +73,7 @@ void ArmaturePopulate::Execute(aiScene *out) {
BuildBoneList(out->mRootNode, out->mRootNode, out, bones); BuildBoneList(out->mRootNode, out->mRootNode, out, bones);
BuildNodeList(out->mRootNode, nodes); BuildNodeList(out->mRootNode, nodes);
BuildBoneStack(out->mRootNode, out->mRootNode, out, bones, bone_stack, nodes); BuildBoneStack(out->mRootNode, out, bones, bone_stack, nodes);
ASSIMP_LOG_DEBUG("Bone stack size: ", bone_stack.size()); ASSIMP_LOG_DEBUG("Bone stack size: ", bone_stack.size());
@ -78,9 +81,8 @@ void ArmaturePopulate::Execute(aiScene *out) {
aiBone *bone = kvp.first; aiBone *bone = kvp.first;
aiNode *bone_node = kvp.second; aiNode *bone_node = kvp.second;
ASSIMP_LOG_VERBOSE_DEBUG("active node lookup: ", bone->mName.C_Str()); ASSIMP_LOG_VERBOSE_DEBUG("active node lookup: ", bone->mName.C_Str());
// lcl transform grab - done in generate_nodes :)
// bone->mOffsetMatrix = bone_node->mTransformation; // lcl transform grab - done in generate_nodes :)
aiNode *armature = GetArmatureRoot(bone_node, bones); aiNode *armature = GetArmatureRoot(bone_node, bones);
ai_assert(armature); ai_assert(armature);
@ -159,8 +161,7 @@ void ArmaturePopulate::BuildNodeList(const aiNode *current_node,
// A bone stack allows us to have multiple armatures, with the same bone names // A bone stack allows us to have multiple armatures, with the same bone names
// A bone stack allows us also to retrieve bones true transform even with // A bone stack allows us also to retrieve bones true transform even with
// duplicate names :) // duplicate names :)
void ArmaturePopulate::BuildBoneStack(aiNode *, void ArmaturePopulate::BuildBoneStack(const aiNode *root_node,
const aiNode *root_node,
const aiScene*, const aiScene*,
const std::vector<aiBone *> &bones, const std::vector<aiBone *> &bones,
std::map<aiBone *, aiNode *> &bone_stack, std::map<aiBone *, aiNode *> &bone_stack,
@ -196,8 +197,7 @@ void ArmaturePopulate::BuildBoneStack(aiNode *,
// This is required to be detected for a bone initially, it will recurse up // This is required to be detected for a bone initially, it will recurse up
// until it cannot find another bone and return the node No known failure // until it cannot find another bone and return the node No known failure
// points. (yet) // points. (yet)
aiNode *ArmaturePopulate::GetArmatureRoot(aiNode *bone_node, aiNode *ArmaturePopulate::GetArmatureRoot(aiNode *bone_node, std::vector<aiBone *> &bone_list) {
std::vector<aiBone *> &bone_list) {
while (nullptr != bone_node) { while (nullptr != bone_node) {
if (!IsBoneNode(bone_node->mName, bone_list)) { if (!IsBoneNode(bone_node->mName, bone_list)) {
ASSIMP_LOG_VERBOSE_DEBUG("GetArmatureRoot() Found valid armature: ", bone_node->mName.C_Str()); ASSIMP_LOG_VERBOSE_DEBUG("GetArmatureRoot() Found valid armature: ", bone_node->mName.C_Str());
@ -212,18 +212,6 @@ aiNode *ArmaturePopulate::GetArmatureRoot(aiNode *bone_node,
return nullptr; return nullptr;
} }
// Simple IsBoneNode check if this could be a bone
bool ArmaturePopulate::IsBoneNode(const aiString &bone_name,
std::vector<aiBone *> &bones) {
for (aiBone *bone : bones) {
if (bone->mName == bone_name) {
return true;
}
}
return false;
}
// Pop this node by name from the stack if found // Pop this node by name from the stack if found
// Used in multiple armature situations with duplicate node / bone names // Used in multiple armature situations with duplicate node / bone names
// Known flaw: cannot have nodes with bone names, will be fixed in later release // Known flaw: cannot have nodes with bone names, will be fixed in later release

View File

@ -69,10 +69,10 @@ namespace Assimp {
class ASSIMP_API ArmaturePopulate : public BaseProcess { class ASSIMP_API ArmaturePopulate : public BaseProcess {
public: public:
/// The default class constructor. /// The default class constructor.
ArmaturePopulate(); ArmaturePopulate() = default;
/// The class destructor. /// The class destructor.
virtual ~ArmaturePopulate(); virtual ~ArmaturePopulate() = default;
/// Overwritten, @see BaseProcess /// Overwritten, @see BaseProcess
virtual bool IsActive( unsigned int pFlags ) const; virtual bool IsActive( unsigned int pFlags ) const;
@ -86,9 +86,6 @@ public:
static aiNode *GetArmatureRoot(aiNode *bone_node, static aiNode *GetArmatureRoot(aiNode *bone_node,
std::vector<aiBone *> &bone_list); std::vector<aiBone *> &bone_list);
static bool IsBoneNode(const aiString &bone_name,
std::vector<aiBone *> &bones);
static aiNode *GetNodeFromStack(const aiString &node_name, static aiNode *GetNodeFromStack(const aiString &node_name,
std::vector<aiNode *> &nodes); std::vector<aiNode *> &nodes);
@ -99,7 +96,7 @@ public:
const aiScene *scene, const aiScene *scene,
std::vector<aiBone *> &bones); std::vector<aiBone *> &bones);
static void BuildBoneStack(aiNode *current_node, const aiNode *root_node, static void BuildBoneStack(const aiNode *root_node,
const aiScene *scene, const aiScene *scene,
const std::vector<aiBone *> &bones, const std::vector<aiBone *> &bones,
std::map<aiBone *, aiNode *> &bone_stack, std::map<aiBone *, aiNode *> &bone_stack,
@ -108,5 +105,4 @@ public:
} // Namespace Assimp } // Namespace Assimp
#endif // SCALE_PROCESS_H_ #endif // SCALE_PROCESS_H_

View File

@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2022, assimp team Copyright (c) 2006-2022, assimp team
All rights reserved. All rights reserved.
Redistribution and use of this software in source and binary forms, Redistribution and use of this software in source and binary forms,
@ -42,8 +41,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/** @file GenUVCoords step */ /** @file GenUVCoords step */
#include "ComputeUVMappingProcess.h" #include "ComputeUVMappingProcess.h"
#include "Geometry/GeometryUtils.h"
#include "ProcessHelper.h" #include "ProcessHelper.h"
#include <assimp/Exceptional.h> #include <assimp/Exceptional.h>
@ -55,35 +54,21 @@ namespace {
const static aiVector3D base_axis_x(1.0, 0.0, 0.0); const static aiVector3D base_axis_x(1.0, 0.0, 0.0);
const static aiVector3D base_axis_z(0.0, 0.0, 1.0); const static aiVector3D base_axis_z(0.0, 0.0, 1.0);
const static ai_real angle_epsilon = ai_real(0.95); const static ai_real angle_epsilon = ai_real(0.95);
} } // namespace
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field. // Returns whether the processing step is present in the given flag field.
bool ComputeUVMappingProcess::IsActive( unsigned int pFlags) const bool ComputeUVMappingProcess::IsActive(unsigned int pFlags) const {
{
return (pFlags & aiProcess_GenUVCoords) != 0; return (pFlags & aiProcess_GenUVCoords) != 0;
} }
// ------------------------------------------------------------------------------------------------
// Check whether a ray intersects a plane and find the intersection point
inline bool PlaneIntersect(const aiRay& ray, const aiVector3D& planePos,
const aiVector3D& planeNormal, aiVector3D& pos)
{
const ai_real b = planeNormal * (planePos - ray.pos);
ai_real h = ray.dir * planeNormal;
if ((h < 10e-5 && h > -10e-5) || (h = b/h) < 0)
return false;
pos = ray.pos + (ray.dir * h);
return true;
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Find the first empty UV channel in a mesh // Find the first empty UV channel in a mesh
inline unsigned int FindEmptyUVChannel (aiMesh* mesh) inline unsigned int FindEmptyUVChannel(aiMesh *mesh) {
{
for (unsigned int m = 0; m < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++m) for (unsigned int m = 0; m < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++m)
if (!mesh->mTextureCoords[m])return m; if (!mesh->mTextureCoords[m]) {
return m;
}
ASSIMP_LOG_ERROR("Unable to compute UV coordinates, no free UV slot found"); ASSIMP_LOG_ERROR("Unable to compute UV coordinates, no free UV slot found");
return UINT_MAX; return UINT_MAX;
@ -91,8 +76,7 @@ inline unsigned int FindEmptyUVChannel (aiMesh* mesh)
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Try to remove UV seams // Try to remove UV seams
void RemoveUVSeams (aiMesh* mesh, aiVector3D* out) void RemoveUVSeams(aiMesh *mesh, aiVector3D *out) {
{
// TODO: just a very rough algorithm. I think it could be done // TODO: just a very rough algorithm. I think it could be done
// much easier, but I don't know how and am currently too tired to // much easier, but I don't know how and am currently too tired to
// to think about a better solution. // to think about a better solution.
@ -103,10 +87,11 @@ void RemoveUVSeams (aiMesh* mesh, aiVector3D* out)
const static ai_real LOWER_EPSILON = ai_real(10e-3); const static ai_real LOWER_EPSILON = ai_real(10e-3);
const static ai_real UPPER_EPSILON = ai_real(1.0 - 10e-3); const static ai_real UPPER_EPSILON = ai_real(1.0 - 10e-3);
for (unsigned int fidx = 0; fidx < mesh->mNumFaces;++fidx) for (unsigned int fidx = 0; fidx < mesh->mNumFaces; ++fidx) {
{
const aiFace &face = mesh->mFaces[fidx]; const aiFace &face = mesh->mFaces[fidx];
if (face.mNumIndices < 3) continue; // triangles and polygons only, please if (face.mNumIndices < 3) {
continue; // triangles and polygons only, please
}
unsigned int smallV = face.mNumIndices, large = smallV; unsigned int smallV = face.mNumIndices, large = smallV;
bool zero = false, one = false, round_to_zero = false; bool zero = false, one = false, round_to_zero = false;
@ -115,20 +100,18 @@ void RemoveUVSeams (aiMesh* mesh, aiVector3D* out)
// but the assumption that a face with at least one very small // but the assumption that a face with at least one very small
// on the one side and one very large U coord on the other side // on the one side and one very large U coord on the other side
// lies on a UV seam should work for most cases. // lies on a UV seam should work for most cases.
for (unsigned int n = 0; n < face.mNumIndices;++n) for (unsigned int n = 0; n < face.mNumIndices; ++n) {
{ if (out[face.mIndices[n]].x < LOWER_LIMIT) {
if (out[face.mIndices[n]].x < LOWER_LIMIT)
{
smallV = n; smallV = n;
// If we have a U value very close to 0 we can't // If we have a U value very close to 0 we can't
// round the others to 0, too. // round the others to 0, too.
if (out[face.mIndices[n]].x <= LOWER_EPSILON) if (out[face.mIndices[n]].x <= LOWER_EPSILON)
zero = true; zero = true;
else round_to_zero = true; else
round_to_zero = true;
} }
if (out[face.mIndices[n]].x > UPPER_LIMIT) if (out[face.mIndices[n]].x > UPPER_LIMIT) {
{
large = n; large = n;
// If we have a U value very close to 1 we can't // If we have a U value very close to 1 we can't
@ -137,10 +120,8 @@ void RemoveUVSeams (aiMesh* mesh, aiVector3D* out)
one = true; one = true;
} }
} }
if (smallV != face.mNumIndices && large != face.mNumIndices) if (smallV != face.mNumIndices && large != face.mNumIndices) {
{ for (unsigned int n = 0; n < face.mNumIndices; ++n) {
for (unsigned int n = 0; n < face.mNumIndices;++n)
{
// If the u value is over the upper limit and no other u // If the u value is over the upper limit and no other u
// value of that face is 0, round it to 0 // value of that face is 0, round it to 0
if (out[face.mIndices[n]].x > UPPER_LIMIT && !zero) if (out[face.mIndices[n]].x > UPPER_LIMIT && !zero)
@ -156,8 +137,7 @@ void RemoveUVSeams (aiMesh* mesh, aiVector3D* out)
// Due to numerical inaccuracies one U coord becomes 0, the // Due to numerical inaccuracies one U coord becomes 0, the
// other 1. But we do still have a third UV coord to determine // other 1. But we do still have a third UV coord to determine
// to which side we must round to. // to which side we must round to.
else if (one && zero) else if (one && zero) {
{
if (round_to_zero && out[face.mIndices[n]].x >= UPPER_EPSILON) if (round_to_zero && out[face.mIndices[n]].x >= UPPER_EPSILON)
out[face.mIndices[n]].x = 0.0; out[face.mIndices[n]].x = 0.0;
else if (!round_to_zero && out[face.mIndices[n]].x <= LOWER_EPSILON) else if (!round_to_zero && out[face.mIndices[n]].x <= LOWER_EPSILON)
@ -169,8 +149,7 @@ void RemoveUVSeams (aiMesh* mesh, aiVector3D* out)
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void ComputeUVMappingProcess::ComputeSphereMapping(aiMesh* mesh,const aiVector3D& axis, aiVector3D* out) void ComputeUVMappingProcess::ComputeSphereMapping(aiMesh *mesh, const aiVector3D &axis, aiVector3D *out) {
{
aiVector3D center, min, max; aiVector3D center, min, max;
FindMeshCenter(mesh, center, min, max); FindMeshCenter(mesh, center, min, max);
@ -197,16 +176,14 @@ void ComputeUVMappingProcess::ComputeSphereMapping(aiMesh* mesh,const aiVector3D
out[pnt] = aiVector3D((std::atan2(diff.z, diff.y) + AI_MATH_PI_F) / AI_MATH_TWO_PI_F, out[pnt] = aiVector3D((std::atan2(diff.z, diff.y) + AI_MATH_PI_F) / AI_MATH_TWO_PI_F,
(std::asin(diff.x) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0); (std::asin(diff.x) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0);
} }
} } else if (axis * base_axis_y >= angle_epsilon) {
else if (axis * base_axis_y >= angle_epsilon) {
// ... just the same again // ... just the same again
for (unsigned int pnt = 0; pnt < mesh->mNumVertices; ++pnt) { for (unsigned int pnt = 0; pnt < mesh->mNumVertices; ++pnt) {
const aiVector3D diff = (mesh->mVertices[pnt] - center).Normalize(); const aiVector3D diff = (mesh->mVertices[pnt] - center).Normalize();
out[pnt] = aiVector3D((std::atan2(diff.x, diff.z) + AI_MATH_PI_F) / AI_MATH_TWO_PI_F, out[pnt] = aiVector3D((std::atan2(diff.x, diff.z) + AI_MATH_PI_F) / AI_MATH_TWO_PI_F,
(std::asin(diff.y) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0); (std::asin(diff.y) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0);
} }
} } else if (axis * base_axis_z >= angle_epsilon) {
else if (axis * base_axis_z >= angle_epsilon) {
// ... just the same again // ... just the same again
for (unsigned int pnt = 0; pnt < mesh->mNumVertices; ++pnt) { for (unsigned int pnt = 0; pnt < mesh->mNumVertices; ++pnt) {
const aiVector3D diff = (mesh->mVertices[pnt] - center).Normalize(); const aiVector3D diff = (mesh->mVertices[pnt] - center).Normalize();
@ -227,7 +204,6 @@ void ComputeUVMappingProcess::ComputeSphereMapping(aiMesh* mesh,const aiVector3D
} }
} }
// Now find and remove UV seams. A seam occurs if a face has a tcoord // Now find and remove UV seams. A seam occurs if a face has a tcoord
// close to zero on the one side, and a tcoord close to one on the // close to zero on the one side, and a tcoord close to one on the
// other side. // other side.
@ -235,8 +211,7 @@ void ComputeUVMappingProcess::ComputeSphereMapping(aiMesh* mesh,const aiVector3D
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void ComputeUVMappingProcess::ComputeCylinderMapping(aiMesh* mesh,const aiVector3D& axis, aiVector3D* out) void ComputeUVMappingProcess::ComputeCylinderMapping(aiMesh *mesh, const aiVector3D &axis, aiVector3D *out) {
{
aiVector3D center, min, max; aiVector3D center, min, max;
// If the axis is one of x,y,z run a faster code path. It's worth the extra effort ... // If the axis is one of x,y,z run a faster code path. It's worth the extra effort ...
@ -258,8 +233,7 @@ void ComputeUVMappingProcess::ComputeCylinderMapping(aiMesh* mesh,const aiVector
uv.y = (pos.x - min.x) / diff; uv.y = (pos.x - min.x) / diff;
uv.x = (std::atan2(pos.z - center.z, pos.y - center.y) + (ai_real)AI_MATH_PI) / (ai_real)AI_MATH_TWO_PI; uv.x = (std::atan2(pos.z - center.z, pos.y - center.y) + (ai_real)AI_MATH_PI) / (ai_real)AI_MATH_TWO_PI;
} }
} } else if (axis * base_axis_y >= angle_epsilon) {
else if (axis * base_axis_y >= angle_epsilon) {
FindMeshCenter(mesh, center, min, max); FindMeshCenter(mesh, center, min, max);
const ai_real diff = max.y - min.y; const ai_real diff = max.y - min.y;
@ -271,8 +245,7 @@ void ComputeUVMappingProcess::ComputeCylinderMapping(aiMesh* mesh,const aiVector
uv.y = (pos.y - min.y) / diff; uv.y = (pos.y - min.y) / diff;
uv.x = (std::atan2(pos.x - center.x, pos.z - center.z) + (ai_real)AI_MATH_PI) / (ai_real)AI_MATH_TWO_PI; uv.x = (std::atan2(pos.x - center.x, pos.z - center.z) + (ai_real)AI_MATH_PI) / (ai_real)AI_MATH_TWO_PI;
} }
} } else if (axis * base_axis_z >= angle_epsilon) {
else if (axis * base_axis_z >= angle_epsilon) {
FindMeshCenter(mesh, center, min, max); FindMeshCenter(mesh, center, min, max);
const ai_real diff = max.z - min.z; const ai_real diff = max.z - min.z;
@ -309,8 +282,7 @@ void ComputeUVMappingProcess::ComputeCylinderMapping(aiMesh* mesh,const aiVector
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void ComputeUVMappingProcess::ComputePlaneMapping(aiMesh* mesh,const aiVector3D& axis, aiVector3D* out) void ComputeUVMappingProcess::ComputePlaneMapping(aiMesh *mesh, const aiVector3D &axis, aiVector3D *out) {
{
ai_real diffu, diffv; ai_real diffu, diffv;
aiVector3D center, min, max; aiVector3D center, min, max;
@ -327,8 +299,7 @@ void ComputeUVMappingProcess::ComputePlaneMapping(aiMesh* mesh,const aiVector3D&
const aiVector3D &pos = mesh->mVertices[pnt]; const aiVector3D &pos = mesh->mVertices[pnt];
out[pnt].Set((pos.z - min.z) / diffu, (pos.y - min.y) / diffv, 0.0); out[pnt].Set((pos.z - min.z) / diffu, (pos.y - min.y) / diffv, 0.0);
} }
} } else if (axis * base_axis_y >= angle_epsilon) {
else if (axis * base_axis_y >= angle_epsilon) {
FindMeshCenter(mesh, center, min, max); FindMeshCenter(mesh, center, min, max);
diffu = max.x - min.x; diffu = max.x - min.x;
diffv = max.z - min.z; diffv = max.z - min.z;
@ -337,8 +308,7 @@ void ComputeUVMappingProcess::ComputePlaneMapping(aiMesh* mesh,const aiVector3D&
const aiVector3D &pos = mesh->mVertices[pnt]; const aiVector3D &pos = mesh->mVertices[pnt];
out[pnt].Set((pos.x - min.x) / diffu, (pos.z - min.z) / diffv, 0.0); out[pnt].Set((pos.x - min.x) / diffu, (pos.z - min.z) / diffv, 0.0);
} }
} } else if (axis * base_axis_z >= angle_epsilon) {
else if (axis * base_axis_z >= angle_epsilon) {
FindMeshCenter(mesh, center, min, max); FindMeshCenter(mesh, center, min, max);
diffu = max.x - min.x; diffu = max.x - min.x;
diffv = max.y - min.y; diffv = max.y - min.y;
@ -349,8 +319,7 @@ void ComputeUVMappingProcess::ComputePlaneMapping(aiMesh* mesh,const aiVector3D&
} }
} }
// slower code path in case the mapping axis is not one of the coordinate system axes // slower code path in case the mapping axis is not one of the coordinate system axes
else else {
{
aiMatrix4x4 mTrafo; aiMatrix4x4 mTrafo;
aiMatrix4x4::FromToMatrix(axis, base_axis_y, mTrafo); aiMatrix4x4::FromToMatrix(axis, base_axis_y, mTrafo);
FindMeshCenterTransformed(mesh, center, min, max, mTrafo); FindMeshCenterTransformed(mesh, center, min, max, mTrafo);
@ -368,14 +337,12 @@ void ComputeUVMappingProcess::ComputePlaneMapping(aiMesh* mesh,const aiVector3D&
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void ComputeUVMappingProcess::ComputeBoxMapping( aiMesh*, aiVector3D* ) void ComputeUVMappingProcess::ComputeBoxMapping(aiMesh *, aiVector3D *) {
{
ASSIMP_LOG_ERROR("Mapping type currently not implemented"); ASSIMP_LOG_ERROR("Mapping type currently not implemented");
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void ComputeUVMappingProcess::Execute( aiScene* pScene) void ComputeUVMappingProcess::Execute(aiScene *pScene) {
{
ASSIMP_LOG_DEBUG("GenUVCoordsProcess begin"); ASSIMP_LOG_DEBUG("GenUVCoordsProcess begin");
char buffer[1024]; char buffer[1024];
@ -386,20 +353,15 @@ void ComputeUVMappingProcess::Execute( aiScene* pScene)
/* Iterate through all materials and search for non-UV mapped textures /* Iterate through all materials and search for non-UV mapped textures
*/ */
for (unsigned int i = 0; i < pScene->mNumMaterials;++i) for (unsigned int i = 0; i < pScene->mNumMaterials; ++i) {
{
mappingStack.clear(); mappingStack.clear();
aiMaterial *mat = pScene->mMaterials[i]; aiMaterial *mat = pScene->mMaterials[i];
for (unsigned int a = 0; a < mat->mNumProperties;++a) for (unsigned int a = 0; a < mat->mNumProperties; ++a) {
{
aiMaterialProperty *prop = mat->mProperties[a]; aiMaterialProperty *prop = mat->mProperties[a];
if (!::strcmp( prop->mKey.data, "$tex.mapping")) if (!::strcmp(prop->mKey.data, "$tex.mapping")) {
{
aiTextureMapping &mapping = *((aiTextureMapping *)prop->mData); aiTextureMapping &mapping = *((aiTextureMapping *)prop->mData);
if (aiTextureMapping_UV != mapping) if (aiTextureMapping_UV != mapping) {
{ if (!DefaultLogger::isNullLogger()) {
if (!DefaultLogger::isNullLogger())
{
ai_snprintf(buffer, 1024, "Found non-UV mapped texture (%s,%u). Mapping type: %s", ai_snprintf(buffer, 1024, "Found non-UV mapped texture (%s,%u). Mapping type: %s",
aiTextureTypeToString((aiTextureType)prop->mSemantic), prop->mIndex, aiTextureTypeToString((aiTextureType)prop->mSemantic), prop->mIndex,
MappingTypeToString(mapping)); MappingTypeToString(mapping));
@ -413,8 +375,7 @@ void ComputeUVMappingProcess::Execute( aiScene* pScene)
MappingInfo info(mapping); MappingInfo info(mapping);
// Get further properties - currently only the major axis // Get further properties - currently only the major axis
for (unsigned int a2 = 0; a2 < mat->mNumProperties;++a2) for (unsigned int a2 = 0; a2 < mat->mNumProperties; ++a2) {
{
aiMaterialProperty *prop2 = mat->mProperties[a2]; aiMaterialProperty *prop2 = mat->mProperties[a2];
if (prop2->mSemantic != prop->mSemantic || prop2->mIndex != prop->mIndex) if (prop2->mSemantic != prop->mSemantic || prop2->mIndex != prop->mIndex)
continue; continue;
@ -429,31 +390,25 @@ void ComputeUVMappingProcess::Execute( aiScene* pScene)
// Check whether we have this mapping mode already // Check whether we have this mapping mode already
std::list<MappingInfo>::iterator it = std::find(mappingStack.begin(), mappingStack.end(), info); std::list<MappingInfo>::iterator it = std::find(mappingStack.begin(), mappingStack.end(), info);
if (mappingStack.end() != it) if (mappingStack.end() != it) {
{
idx = (*it).uv; idx = (*it).uv;
} } else {
else
{
/* We have found a non-UV mapped texture. Now /* We have found a non-UV mapped texture. Now
* we need to find all meshes using this material * we need to find all meshes using this material
* that we can compute UV channels for them. * that we can compute UV channels for them.
*/ */
for (unsigned int m = 0; m < pScene->mNumMeshes;++m) for (unsigned int m = 0; m < pScene->mNumMeshes; ++m) {
{
aiMesh *mesh = pScene->mMeshes[m]; aiMesh *mesh = pScene->mMeshes[m];
unsigned int outIdx = 0; unsigned int outIdx = 0;
if (mesh->mMaterialIndex != i || (outIdx = FindEmptyUVChannel(mesh)) == UINT_MAX || if (mesh->mMaterialIndex != i || (outIdx = FindEmptyUVChannel(mesh)) == UINT_MAX ||
!mesh->mNumVertices) !mesh->mNumVertices) {
{
continue; continue;
} }
// Allocate output storage // Allocate output storage
aiVector3D *p = mesh->mTextureCoords[outIdx] = new aiVector3D[mesh->mNumVertices]; aiVector3D *p = mesh->mTextureCoords[outIdx] = new aiVector3D[mesh->mNumVertices];
switch (mapping) switch (mapping) {
{
case aiTextureMapping_SPHERE: case aiTextureMapping_SPHERE:
ComputeSphereMapping(mesh, info.axis, p); ComputeSphereMapping(mesh, info.axis, p);
break; break;
@ -469,8 +424,7 @@ void ComputeUVMappingProcess::Execute( aiScene* pScene)
default: default:
ai_assert(false); ai_assert(false);
} }
if (m && idx != outIdx) if (m && idx != outIdx) {
{
ASSIMP_LOG_WARN("UV index mismatch. Not all meshes assigned to " ASSIMP_LOG_WARN("UV index mismatch. Not all meshes assigned to "
"this material have equal numbers of UV channels. The UV index stored in " "this material have equal numbers of UV channels. The UV index stored in "
"the material structure does therefore not apply for all meshes. "); "the material structure does therefore not apply for all meshes. ");

View File

@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2022, assimp team Copyright (c) 2006-2022, assimp team
All rights reserved. All rights reserved.
Redistribution and use of this software in source and binary forms, Redistribution and use of this software in source and binary forms,
@ -225,13 +223,6 @@ void MakeLeftHandedProcess::ProcessAnimation(aiNodeAnim *pAnim) {
// rotation keys // rotation keys
for (unsigned int a = 0; a < pAnim->mNumRotationKeys; a++) { for (unsigned int a = 0; a < pAnim->mNumRotationKeys; a++) {
/* That's the safe version, but the float errors add up. So we try the short version instead
aiMatrix3x3 rotmat = pAnim->mRotationKeys[a].mValue.GetMatrix();
rotmat.a3 = -rotmat.a3; rotmat.b3 = -rotmat.b3;
rotmat.c1 = -rotmat.c1; rotmat.c2 = -rotmat.c2;
aiQuaternion rotquat( rotmat);
pAnim->mRotationKeys[a].mValue = rotquat;
*/
pAnim->mRotationKeys[a].mValue.x *= -1.0f; pAnim->mRotationKeys[a].mValue.x *= -1.0f;
pAnim->mRotationKeys[a].mValue.y *= -1.0f; pAnim->mRotationKeys[a].mValue.y *= -1.0f;
} }
@ -241,7 +232,7 @@ void MakeLeftHandedProcess::ProcessAnimation(aiNodeAnim *pAnim) {
// Converts a single camera to left handed coordinates. // Converts a single camera to left handed coordinates.
void MakeLeftHandedProcess::ProcessCamera( aiCamera* pCam) void MakeLeftHandedProcess::ProcessCamera( aiCamera* pCam)
{ {
pCam->mLookAt = 2.0f * pCam->mPosition - pCam->mLookAt; pCam->mLookAt = ai_real(2.0f) * pCam->mPosition - pCam->mLookAt;
} }
#endif // !! ASSIMP_BUILD_NO_MAKELEFTHANDED_PROCESS #endif // !! ASSIMP_BUILD_NO_MAKELEFTHANDED_PROCESS

View File

@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2022, assimp team Copyright (c) 2006-2022, assimp team
All rights reserved. All rights reserved.
Redistribution and use of this software in source and binary forms, Redistribution and use of this software in source and binary forms,
@ -87,7 +86,7 @@ void DeboneProcess::Execute( aiScene* pScene) {
if(!!mNumBonesCanDoWithout && (!mAllOrNone||mNumBonesCanDoWithout==mNumBones)) { if(!!mNumBonesCanDoWithout && (!mAllOrNone||mNumBonesCanDoWithout==mNumBones)) {
for(unsigned int a = 0; a < pScene->mNumMeshes; a++) { for(unsigned int a = 0; a < pScene->mNumMeshes; a++) {
if(splitList[a]) { if(splitList[a]) {
numSplits++; ++numSplits;
} }
} }
} }
@ -119,8 +118,8 @@ void DeboneProcess::Execute( aiScene* pScene) {
aiNode *theNode = find ? pScene->mRootNode->FindNode(*find) : nullptr; aiNode *theNode = find ? pScene->mRootNode->FindNode(*find) : nullptr;
std::pair<unsigned int,aiNode*> push_pair(static_cast<unsigned int>(meshes.size()),theNode); std::pair<unsigned int,aiNode*> push_pair(static_cast<unsigned int>(meshes.size()),theNode);
mSubMeshIndices[a].push_back(push_pair); mSubMeshIndices[a].emplace_back(push_pair);
meshes.push_back(newMeshes[b].first); meshes.emplace_back(newMeshes[b].first);
out+=newMeshes[b].first->mNumBones; out+=newMeshes[b].first->mNumBones;
} }
@ -360,9 +359,7 @@ void DeboneProcess::UpdateNode(aiNode* pNode) const {
unsigned int m = static_cast<unsigned int>(pNode->mNumMeshes), n = static_cast<unsigned int>(mSubMeshIndices.size()); unsigned int m = static_cast<unsigned int>(pNode->mNumMeshes), n = static_cast<unsigned int>(mSubMeshIndices.size());
// first pass, look for meshes which have not moved // first pass, look for meshes which have not moved
for(unsigned int a=0;a<m;a++) { for(unsigned int a=0;a<m;a++) {
unsigned int srcIndex = pNode->mMeshes[a]; unsigned int srcIndex = pNode->mMeshes[a];
const std::vector< std::pair< unsigned int,aiNode* > > &subMeshes = mSubMeshIndices[srcIndex]; const std::vector< std::pair< unsigned int,aiNode* > > &subMeshes = mSubMeshIndices[srcIndex];
unsigned int nSubmeshes = static_cast<unsigned int>(subMeshes.size()); unsigned int nSubmeshes = static_cast<unsigned int>(subMeshes.size());
@ -376,8 +373,7 @@ void DeboneProcess::UpdateNode(aiNode* pNode) const {
// second pass, collect deboned meshes // second pass, collect deboned meshes
for(unsigned int a=0;a<n;a++) for(unsigned int a=0;a<n;a++) {
{
const std::vector< std::pair< unsigned int,aiNode* > > &subMeshes = mSubMeshIndices[a]; const std::vector< std::pair< unsigned int,aiNode* > > &subMeshes = mSubMeshIndices[a];
unsigned int nSubmeshes = static_cast<unsigned int>(subMeshes.size()); unsigned int nSubmeshes = static_cast<unsigned int>(subMeshes.size());

View File

@ -45,12 +45,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* normals for all imported faces. * normals for all imported faces.
*/ */
#include "DropFaceNormalsProcess.h" #include "DropFaceNormalsProcess.h"
#include <assimp/Exceptional.h>
#include <assimp/postprocess.h> #include <assimp/postprocess.h>
#include <assimp/scene.h> #include <assimp/scene.h>
#include <assimp/DefaultLogger.hpp> #include <assimp/DefaultLogger.hpp>
#include <assimp/Exceptional.h>
using namespace Assimp; using namespace Assimp;

View File

@ -43,9 +43,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* @brief Implementation of the FindDegenerates post-process step. * @brief Implementation of the FindDegenerates post-process step.
*/ */
#include "ProcessHelper.h"
#include "FindDegenerates.h" #include "FindDegenerates.h"
#include "Geometry/GeometryUtils.h" #include "Geometry/GeometryUtils.h"
#include "ProcessHelper.h"
#include <assimp/Exceptional.h> #include <assimp/Exceptional.h>
@ -199,8 +199,7 @@ bool FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) {
} }
// We need to update the primitive flags array of the mesh. // We need to update the primitive flags array of the mesh.
switch (face.mNumIndices) switch (face.mNumIndices) {
{
case 1u: case 1u:
mesh->mPrimitiveTypes |= aiPrimitiveType_POINT; mesh->mPrimitiveTypes |= aiPrimitiveType_POINT;
break; break;
@ -221,8 +220,7 @@ evil_jump_outside:
// If AI_CONFIG_PP_FD_REMOVE is true, remove degenerated faces from the import // If AI_CONFIG_PP_FD_REMOVE is true, remove degenerated faces from the import
if (mConfigRemoveDegenerates && deg) { if (mConfigRemoveDegenerates && deg) {
unsigned int n = 0; unsigned int n = 0;
for (unsigned int a = 0; a < mesh->mNumFaces; ++a) for (unsigned int a = 0; a < mesh->mNumFaces; ++a) {
{
aiFace &face_src = mesh->mFaces[a]; aiFace &face_src = mesh->mFaces[a];
if (!remove_me[a]) { if (!remove_me[a]) {
aiFace &face_dest = mesh->mFaces[n++]; aiFace &face_dest = mesh->mFaces[n++];
@ -236,8 +234,7 @@ evil_jump_outside:
face_src.mNumIndices = 0; face_src.mNumIndices = 0;
face_src.mIndices = nullptr; face_src.mIndices = nullptr;
} }
} } else {
else {
// Otherwise delete it if we don't need this face // Otherwise delete it if we don't need this face
delete[] face_src.mIndices; delete[] face_src.mIndices;
face_src.mIndices = nullptr; face_src.mIndices = nullptr;

View File

@ -82,6 +82,9 @@ void UpdateMeshReferences(aiNode *node, const std::vector<unsigned int> &meshMap
for (unsigned int a = 0; a < node->mNumMeshes; ++a) { for (unsigned int a = 0; a < node->mNumMeshes; ++a) {
unsigned int ref = node->mMeshes[a]; unsigned int ref = node->mMeshes[a];
if (ref >= meshMapping.size())
throw DeadlyImportError("Invalid mesh ref");
if (UINT_MAX != (ref = meshMapping[ref])) { if (UINT_MAX != (ref = meshMapping[ref])) {
node->mMeshes[out++] = ref; node->mMeshes[out++] = ref;
} }
@ -143,7 +146,13 @@ void FindInvalidDataProcess::Execute(aiScene *pScene) {
// we need to remove some meshes. // we need to remove some meshes.
// therefore we'll also need to remove all references // therefore we'll also need to remove all references
// to them from the scenegraph // to them from the scenegraph
try {
UpdateMeshReferences(pScene->mRootNode, meshMapping); UpdateMeshReferences(pScene->mRootNode, meshMapping);
} catch (const std::exception&) {
// fix the real number of meshes otherwise we'll get double free in the scene destructor
pScene->mNumMeshes = real;
throw;
}
pScene->mNumMeshes = real; pScene->mNumMeshes = real;
} }

View File

@ -81,6 +81,7 @@ void LimitBoneWeightsProcess::Execute( aiScene* pScene) {
// Executes the post processing step on the given imported data. // Executes the post processing step on the given imported data.
void LimitBoneWeightsProcess::SetupProperties(const Importer* pImp) { void LimitBoneWeightsProcess::SetupProperties(const Importer* pImp) {
this->mMaxWeights = pImp->GetPropertyInteger(AI_CONFIG_PP_LBW_MAX_WEIGHTS,AI_LMW_MAX_WEIGHTS); this->mMaxWeights = pImp->GetPropertyInteger(AI_CONFIG_PP_LBW_MAX_WEIGHTS,AI_LMW_MAX_WEIGHTS);
this->mRemoveEmptyBones = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES, 1) != 0;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -172,9 +173,9 @@ void LimitBoneWeightsProcess::ProcessMesh(aiMesh* pMesh) {
} }
// remove empty bones // remove empty bones
#ifdef AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES if (mRemoveEmptyBones) {
pMesh->mNumBones = removeEmptyBones(pMesh); pMesh->mNumBones = removeEmptyBones(pMesh);
#endif // AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES }
if (!DefaultLogger::isNullLogger()) { if (!DefaultLogger::isNullLogger()) {
ASSIMP_LOG_INFO("Removed ", removed, " weights. Input bones: ", old_bones, ". Output bones: ", pMesh->mNumBones); ASSIMP_LOG_INFO("Removed ", removed, " weights. Input bones: ", old_bones, ". Output bones: ", pMesh->mNumBones);

View File

@ -133,6 +133,7 @@ public:
/** Maximum number of bones influencing any single vertex. */ /** Maximum number of bones influencing any single vertex. */
unsigned int mMaxWeights; unsigned int mMaxWeights;
bool mRemoveEmptyBones;
}; };
} // end of namespace Assimp } // end of namespace Assimp

View File

@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2022, assimp team Copyright (c) 2006-2022, assimp team
All rights reserved. All rights reserved.
Redistribution and use of this software in source and binary forms, Redistribution and use of this software in source and binary forms,
@ -41,9 +39,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------- ---------------------------------------------------------------------------
*/ */
/** @file PretransformVertices.cpp /// @file PretransformVertices.cpp
* @brief Implementation of the "PretransformVertices" post processing step /// @brief Implementation of the "PretransformVertices" post processing step
*/
#include "PretransformVertices.h" #include "PretransformVertices.h"
#include "ConvertToLHProcess.h" #include "ConvertToLHProcess.h"
@ -57,16 +54,44 @@ using namespace Assimp;
#define AI_PTVS_VERTEX 0x0 #define AI_PTVS_VERTEX 0x0
#define AI_PTVS_FACE 0x1 #define AI_PTVS_FACE 0x1
namespace {
// Get a bitwise combination identifying the vertex format of a mesh
static unsigned int GetMeshVFormat(aiMesh *pcMesh) {
// the vertex format is stored in aiMesh::mBones for later retrieval.
// there isn't a good reason to compute it a few hundred times
// from scratch. The pointer is unused as animations are lost
// during PretransformVertices.
if (pcMesh->mBones)
return (unsigned int)(uint64_t)pcMesh->mBones;
const unsigned int iRet = GetMeshVFormatUnique(pcMesh);
// store the value for later use
pcMesh->mBones = (aiBone **)(uint64_t)iRet;
return iRet;
}
// Get a list of all vertex formats that occur for a given material index
// The output list contains duplicate elements
static void GetVFormatList(const aiScene *pcScene, unsigned int iMat, std::list<unsigned int> &aiOut) {
for (unsigned int i = 0; i < pcScene->mNumMeshes; ++i) {
aiMesh *pcMesh = pcScene->mMeshes[i];
if (iMat == pcMesh->mMaterialIndex) {
aiOut.push_back(GetMeshVFormat(pcMesh));
}
}
}
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer // Constructor to be privately used by Importer
PretransformVertices::PretransformVertices() : PretransformVertices::PretransformVertices() :
configKeepHierarchy(false), mConfigKeepHierarchy(false),
configNormalize(false), mConfigNormalize(false),
configTransform(false), mConfigTransform(false),
configTransformation(), mConfigTransformation(),
mConfigPointCloud(false) { mConfigPointCloud(false) {}
// empty
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field. // Returns whether the processing step is present in the given flag field.
@ -79,11 +104,11 @@ bool PretransformVertices::IsActive(unsigned int pFlags) const {
void PretransformVertices::SetupProperties(const Importer *pImp) { void PretransformVertices::SetupProperties(const Importer *pImp) {
// Get the current value of AI_CONFIG_PP_PTV_KEEP_HIERARCHY, AI_CONFIG_PP_PTV_NORMALIZE, // Get the current value of AI_CONFIG_PP_PTV_KEEP_HIERARCHY, AI_CONFIG_PP_PTV_NORMALIZE,
// AI_CONFIG_PP_PTV_ADD_ROOT_TRANSFORMATION and AI_CONFIG_PP_PTV_ROOT_TRANSFORMATION // AI_CONFIG_PP_PTV_ADD_ROOT_TRANSFORMATION and AI_CONFIG_PP_PTV_ROOT_TRANSFORMATION
configKeepHierarchy = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_KEEP_HIERARCHY, 0)); mConfigKeepHierarchy = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_KEEP_HIERARCHY, 0));
configNormalize = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_NORMALIZE, 0)); mConfigNormalize = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_NORMALIZE, 0));
configTransform = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_ADD_ROOT_TRANSFORMATION, 0)); mConfigTransform = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_PTV_ADD_ROOT_TRANSFORMATION, 0));
configTransformation = pImp->GetPropertyMatrix(AI_CONFIG_PP_PTV_ROOT_TRANSFORMATION, aiMatrix4x4()); mConfigTransformation = pImp->GetPropertyMatrix(AI_CONFIG_PP_PTV_ROOT_TRANSFORMATION, aiMatrix4x4());
mConfigPointCloud = pImp->GetPropertyBool(AI_CONFIG_EXPORT_POINT_CLOUDS); mConfigPointCloud = pImp->GetPropertyBool(AI_CONFIG_EXPORT_POINT_CLOUDS);
} }
@ -99,25 +124,7 @@ unsigned int PretransformVertices::CountNodes(const aiNode *pcNode) const {
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Get a bitwise combination identifying the vertex format of a mesh // Count the number of vertices in the whole scene and a given material index
unsigned int PretransformVertices::GetMeshVFormat(aiMesh *pcMesh) const {
// the vertex format is stored in aiMesh::mBones for later retrieval.
// there isn't a good reason to compute it a few hundred times
// from scratch. The pointer is unused as animations are lost
// during PretransformVertices.
if (pcMesh->mBones)
return (unsigned int)(uint64_t)pcMesh->mBones;
const unsigned int iRet = GetMeshVFormatUnique(pcMesh);
// store the value for later use
pcMesh->mBones = (aiBone **)(uint64_t)iRet;
return iRet;
}
// ------------------------------------------------------------------------------------------------
// Count the number of vertices in the whole scene and a given
// material index
void PretransformVertices::CountVerticesAndFaces(const aiScene *pcScene, const aiNode *pcNode, unsigned int iMat, void PretransformVertices::CountVerticesAndFaces(const aiScene *pcScene, const aiNode *pcNode, unsigned int iMat,
unsigned int iVFormat, unsigned int *piFaces, unsigned int *piVertices) const { unsigned int iVFormat, unsigned int *piFaces, unsigned int *piVertices) const {
for (unsigned int i = 0; i < pcNode->mNumMeshes; ++i) { for (unsigned int i = 0; i < pcNode->mNumMeshes; ++i) {
@ -128,8 +135,7 @@ void PretransformVertices::CountVerticesAndFaces(const aiScene *pcScene, const a
} }
} }
for (unsigned int i = 0; i < pcNode->mNumChildren; ++i) { for (unsigned int i = 0; i < pcNode->mNumChildren; ++i) {
CountVerticesAndFaces(pcScene, pcNode->mChildren[i], iMat, CountVerticesAndFaces(pcScene, pcNode->mChildren[i], iMat, iVFormat, piFaces, piVertices);
iVFormat, piFaces, piVertices);
} }
} }
@ -272,19 +278,6 @@ void PretransformVertices::CollectData(const aiScene *pcScene, const aiNode *pcN
} }
} }
// ------------------------------------------------------------------------------------------------
// Get a list of all vertex formats that occur for a given material index
// The output list contains duplicate elements
void PretransformVertices::GetVFormatList(const aiScene *pcScene, unsigned int iMat,
std::list<unsigned int> &aiOut) const {
for (unsigned int i = 0; i < pcScene->mNumMeshes; ++i) {
aiMesh *pcMesh = pcScene->mMeshes[i];
if (iMat == pcMesh->mMaterialIndex) {
aiOut.push_back(GetMeshVFormat(pcMesh));
}
}
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Compute the absolute transformation matrices of each node // Compute the absolute transformation matrices of each node
void PretransformVertices::ComputeAbsoluteTransform(aiNode *pcNode) { void PretransformVertices::ComputeAbsoluteTransform(aiNode *pcNode) {
@ -297,11 +290,19 @@ void PretransformVertices::ComputeAbsoluteTransform(aiNode *pcNode) {
} }
} }
static void normalizeVectorArray(aiVector3D *vectorArrayIn, aiVector3D *vectorArrayOut, size_t numVectors) {
for (size_t i=0; i<numVectors; ++i) {
vectorArrayOut[i] = vectorArrayIn[i].Normalize();
}
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Apply the node transformation to a mesh // Apply the node transformation to a mesh
void PretransformVertices::ApplyTransform(aiMesh *mesh, const aiMatrix4x4 &mat) const { void PretransformVertices::ApplyTransform(aiMesh *mesh, const aiMatrix4x4 &mat) const {
// Check whether we need to transform the coordinates at all // Check whether we need to transform the coordinates at all
if (!mat.IsIdentity()) { if (mat.IsIdentity()) {
return;
}
// Check for odd negative scale (mirror) // Check for odd negative scale (mirror)
if (mesh->HasFaces() && mat.Determinant() < 0) { if (mesh->HasFaces() && mat.Determinant() < 0) {
@ -321,9 +322,7 @@ void PretransformVertices::ApplyTransform(aiMesh *mesh, const aiMatrix4x4 &mat)
const aiMatrix3x3 m = aiMatrix3x3(mat).Inverse().Transpose(); const aiMatrix3x3 m = aiMatrix3x3(mat).Inverse().Transpose();
if (mesh->HasNormals()) { if (mesh->HasNormals()) {
for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { normalizeVectorArray(mesh->mNormals, mesh->mNormals, mesh->mNumVertices);
mesh->mNormals[i] = (m * mesh->mNormals[i]).Normalize();
}
} }
if (mesh->HasTangentsAndBitangents()) { if (mesh->HasTangentsAndBitangents()) {
for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
@ -333,7 +332,6 @@ void PretransformVertices::ApplyTransform(aiMesh *mesh, const aiMatrix4x4 &mat)
} }
} }
} }
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Simple routine to build meshes in worldspace, no further optimization // Simple routine to build meshes in worldspace, no further optimization
@ -352,7 +350,8 @@ void PretransformVertices::BuildWCSMeshes(std::vector<aiMesh *> &out, aiMesh **i
// yes, we can. // yes, we can.
mesh->mBones = reinterpret_cast<aiBone **>(&node->mTransformation); mesh->mBones = reinterpret_cast<aiBone **>(&node->mTransformation);
mesh->mNumBones = UINT_MAX; mesh->mNumBones = UINT_MAX;
} else { continue;
}
// try to find us in the list of newly created meshes // try to find us in the list of newly created meshes
for (unsigned int n = 0; n < out.size(); ++n) { for (unsigned int n = 0; n < out.size(); ++n) {
@ -368,10 +367,10 @@ void PretransformVertices::BuildWCSMeshes(std::vector<aiMesh *> &out, aiMesh **i
ASSIMP_LOG_INFO("PretransformVertices: Copying mesh due to mismatching transforms"); ASSIMP_LOG_INFO("PretransformVertices: Copying mesh due to mismatching transforms");
aiMesh *ntz; aiMesh *ntz;
const unsigned int tmp = mesh->mNumBones; // const unsigned int cacheNumBones = mesh->mNumBones; //
mesh->mNumBones = 0; mesh->mNumBones = 0;
SceneCombiner::Copy(&ntz, mesh); SceneCombiner::Copy(&ntz, mesh);
mesh->mNumBones = tmp; mesh->mNumBones = cacheNumBones;
ntz->mNumBones = node->mMeshes[i]; ntz->mNumBones = node->mMeshes[i];
ntz->mBones = reinterpret_cast<aiBone **>(&node->mTransformation); ntz->mBones = reinterpret_cast<aiBone **>(&node->mTransformation);
@ -381,12 +380,12 @@ void PretransformVertices::BuildWCSMeshes(std::vector<aiMesh *> &out, aiMesh **i
node->mMeshes[i] = static_cast<unsigned int>(numIn + out.size() - 1); node->mMeshes[i] = static_cast<unsigned int>(numIn + out.size() - 1);
} }
} }
}
// call children // call children
for (unsigned int i = 0; i < node->mNumChildren; ++i) for (unsigned int i = 0; i < node->mNumChildren; ++i) {
BuildWCSMeshes(out, in, numIn, node->mChildren[i]); BuildWCSMeshes(out, in, numIn, node->mChildren[i]);
} }
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Reset transformation matrices to identity // Reset transformation matrices to identity
@ -394,9 +393,10 @@ void PretransformVertices::MakeIdentityTransform(aiNode *nd) const {
nd->mTransformation = aiMatrix4x4(); nd->mTransformation = aiMatrix4x4();
// call children // call children
for (unsigned int i = 0; i < nd->mNumChildren; ++i) for (unsigned int i = 0; i < nd->mNumChildren; ++i) {
MakeIdentityTransform(nd->mChildren[i]); MakeIdentityTransform(nd->mChildren[i]);
} }
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Build reference counters for all meshes // Build reference counters for all meshes
@ -405,9 +405,28 @@ void PretransformVertices::BuildMeshRefCountArray(const aiNode *nd, unsigned int
refs[nd->mMeshes[i]]++; refs[nd->mMeshes[i]]++;
// call children // call children
for (unsigned int i = 0; i < nd->mNumChildren; ++i) for (unsigned int i = 0; i < nd->mNumChildren; ++i) {
BuildMeshRefCountArray(nd->mChildren[i], refs); BuildMeshRefCountArray(nd->mChildren[i], refs);
} }
}
// ------------------------------------------------------------------------------------------------
static void appendNewMeshesToScene(aiScene *pScene, std::vector<aiMesh*> &apcOutMeshes) {
ai_assert(pScene != nullptr);
if (apcOutMeshes.empty()) {
return;
}
aiMesh **npp = new aiMesh *[pScene->mNumMeshes + apcOutMeshes.size()];
::memcpy(npp, pScene->mMeshes, sizeof(aiMesh *) * pScene->mNumMeshes);
::memcpy(npp + pScene->mNumMeshes, &apcOutMeshes[0], sizeof(aiMesh *) * apcOutMeshes.size());
pScene->mNumMeshes += static_cast<unsigned int>(apcOutMeshes.size());
delete[] pScene->mMeshes;
pScene->mMeshes = npp;
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data. // Executes the post processing step on the given imported data.
@ -418,12 +437,12 @@ void PretransformVertices::Execute(aiScene *pScene) {
if (!pScene->mNumMeshes) if (!pScene->mNumMeshes)
return; return;
const unsigned int iOldMeshes = pScene->mNumMeshes; const unsigned int oldMeshes = pScene->mNumMeshes;
const unsigned int iOldAnimationChannels = pScene->mNumAnimations; const unsigned int oldAnimationChannels = pScene->mNumAnimations;
const unsigned int iOldNodes = CountNodes(pScene->mRootNode); const unsigned int oldNodes = CountNodes(pScene->mRootNode);
if (configTransform) { if (mConfigTransform) {
pScene->mRootNode->mTransformation = configTransformation * pScene->mRootNode->mTransformation; pScene->mRootNode->mTransformation = mConfigTransformation * pScene->mRootNode->mTransformation;
} }
// first compute absolute transformation matrices for all nodes // first compute absolute transformation matrices for all nodes
@ -449,22 +468,13 @@ void PretransformVertices::Execute(aiScene *pScene) {
// we go on and transform all meshes, if one is referenced by nodes // we go on and transform all meshes, if one is referenced by nodes
// with different absolute transformations a depth copy of the mesh // with different absolute transformations a depth copy of the mesh
// is required. // is required.
if (configKeepHierarchy) { if (mConfigKeepHierarchy) {
// Hack: store the matrix we're transforming a mesh with in aiMesh::mBones // Hack: store the matrix we're transforming a mesh with in aiMesh::mBones
BuildWCSMeshes(apcOutMeshes, pScene->mMeshes, pScene->mNumMeshes, pScene->mRootNode); BuildWCSMeshes(apcOutMeshes, pScene->mMeshes, pScene->mNumMeshes, pScene->mRootNode);
// ... if new meshes have been generated, append them to the end of the scene // ... if new meshes have been generated, append them to the end of the scene
if (apcOutMeshes.size() > 0) { appendNewMeshesToScene(pScene, apcOutMeshes);
aiMesh **npp = new aiMesh *[pScene->mNumMeshes + apcOutMeshes.size()];
memcpy(npp, pScene->mMeshes, sizeof(aiMesh *) * pScene->mNumMeshes);
memcpy(npp + pScene->mNumMeshes, &apcOutMeshes[0], sizeof(aiMesh *) * apcOutMeshes.size());
pScene->mNumMeshes += static_cast<unsigned int>(apcOutMeshes.size());
delete[] pScene->mMeshes;
pScene->mMeshes = npp;
}
// now iterate through all meshes and transform them to world-space // now iterate through all meshes and transform them to world-space
for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) { for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
@ -488,34 +498,35 @@ void PretransformVertices::Execute(aiScene *pScene) {
aiVFormats.sort(); aiVFormats.sort();
aiVFormats.unique(); aiVFormats.unique();
for (std::list<unsigned int>::const_iterator j = aiVFormats.begin(); j != aiVFormats.end(); ++j) { for (std::list<unsigned int>::const_iterator j = aiVFormats.begin(); j != aiVFormats.end(); ++j) {
unsigned int iVertices = 0; unsigned int numVertices = 0u;
unsigned int iFaces = 0; unsigned int numFaces = 0u;
CountVerticesAndFaces(pScene, pScene->mRootNode, i, *j, &iFaces, &iVertices); CountVerticesAndFaces(pScene, pScene->mRootNode, i, *j, &numFaces, &numVertices);
if (0 != iFaces && 0 != iVertices) { if (0 != numFaces && 0 != numVertices) {
apcOutMeshes.push_back(new aiMesh()); apcOutMeshes.push_back(new aiMesh());
aiMesh *pcMesh = apcOutMeshes.back(); aiMesh *pcMesh = apcOutMeshes.back();
pcMesh->mNumFaces = iFaces; pcMesh->mNumFaces = numFaces;
pcMesh->mNumVertices = iVertices; pcMesh->mNumVertices = numVertices;
pcMesh->mFaces = new aiFace[iFaces]; pcMesh->mFaces = new aiFace[numFaces];
pcMesh->mVertices = new aiVector3D[iVertices]; pcMesh->mVertices = new aiVector3D[numVertices];
pcMesh->mMaterialIndex = i; pcMesh->mMaterialIndex = i;
if ((*j) & 0x2) pcMesh->mNormals = new aiVector3D[iVertices]; if ((*j) & 0x2) pcMesh->mNormals = new aiVector3D[numVertices];
if ((*j) & 0x4) { if ((*j) & 0x4) {
pcMesh->mTangents = new aiVector3D[iVertices]; pcMesh->mTangents = new aiVector3D[numVertices];
pcMesh->mBitangents = new aiVector3D[iVertices]; pcMesh->mBitangents = new aiVector3D[numVertices];
} }
iFaces = 0; numFaces = 0;
while ((*j) & (0x100 << iFaces)) { while ((*j) & (0x100 << numFaces)) {
pcMesh->mTextureCoords[iFaces] = new aiVector3D[iVertices]; pcMesh->mTextureCoords[numFaces] = new aiVector3D[numVertices];
if ((*j) & (0x10000 << iFaces)) if ((*j) & (0x10000 << numFaces)) {
pcMesh->mNumUVComponents[iFaces] = 3; pcMesh->mNumUVComponents[numFaces] = 3;
else } else {
pcMesh->mNumUVComponents[iFaces] = 2; pcMesh->mNumUVComponents[numFaces] = 2;
iFaces++;
} }
iFaces = 0; ++numFaces;
while ((*j) & (0x1000000 << iFaces)) }
pcMesh->mColors[iFaces++] = new aiColor4D[iVertices]; numFaces = 0;
while ((*j) & (0x1000000 << numFaces))
pcMesh->mColors[numFaces++] = new aiColor4D[numVertices];
// fill the mesh ... // fill the mesh ...
unsigned int aiTemp[2] = { 0, 0 }; unsigned int aiTemp[2] = { 0, 0 };
@ -593,7 +604,7 @@ void PretransformVertices::Execute(aiScene *pScene) {
l->mUp = aiMatrix3x3(nd->mTransformation) * l->mUp; l->mUp = aiMatrix3x3(nd->mTransformation) * l->mUp;
} }
if (!configKeepHierarchy) { if (!mConfigKeepHierarchy) {
// now delete all nodes in the scene and build a new // now delete all nodes in the scene and build a new
// flat node graph with a root node and some level 1 children // flat node graph with a root node and some level 1 children
@ -644,7 +655,7 @@ void PretransformVertices::Execute(aiScene *pScene) {
MakeIdentityTransform(pScene->mRootNode); MakeIdentityTransform(pScene->mRootNode);
} }
if (configNormalize) { if (mConfigNormalize) {
// compute the boundary of all meshes // compute the boundary of all meshes
aiVector3D min, max; aiVector3D min, max;
MinMaxChooser<aiVector3D>()(min, max); MinMaxChooser<aiVector3D>()(min, max);
@ -674,9 +685,9 @@ void PretransformVertices::Execute(aiScene *pScene) {
if (!DefaultLogger::isNullLogger()) { if (!DefaultLogger::isNullLogger()) {
ASSIMP_LOG_DEBUG("PretransformVerticesProcess finished"); ASSIMP_LOG_DEBUG("PretransformVerticesProcess finished");
ASSIMP_LOG_INFO("Removed ", iOldNodes, " nodes and ", iOldAnimationChannels, " animation channels (", ASSIMP_LOG_INFO("Removed ", oldNodes, " nodes and ", oldAnimationChannels, " animation channels (",
CountNodes(pScene->mRootNode), " output nodes)"); CountNodes(pScene->mRootNode), " output nodes)");
ASSIMP_LOG_INFO("Kept ", pScene->mNumLights, " lights and ", pScene->mNumCameras, " cameras."); ASSIMP_LOG_INFO("Kept ", pScene->mNumLights, " lights and ", pScene->mNumCameras, " cameras.");
ASSIMP_LOG_INFO("Moved ", iOldMeshes, " meshes to WCS (number of output meshes: ", pScene->mNumMeshes, ")"); ASSIMP_LOG_INFO("Moved ", oldMeshes, " meshes to WCS (number of output meshes: ", pScene->mNumMeshes, ")");
} }
} }

View File

@ -90,7 +90,7 @@ public:
* @param keep true for keep configuration. * @param keep true for keep configuration.
*/ */
void KeepHierarchy(bool keep) { void KeepHierarchy(bool keep) {
configKeepHierarchy = keep; mConfigKeepHierarchy = keep;
} }
// ------------------------------------------------------------------- // -------------------------------------------------------------------
@ -98,7 +98,7 @@ public:
* @return ... * @return ...
*/ */
bool IsHierarchyKept() const { bool IsHierarchyKept() const {
return configKeepHierarchy; return mConfigKeepHierarchy;
} }
private: private:
@ -108,7 +108,7 @@ private:
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Get a bitwise combination identifying the vertex format of a mesh // Get a bitwise combination identifying the vertex format of a mesh
unsigned int GetMeshVFormat(aiMesh *pcMesh) const; //unsigned int GetMeshVFormat(aiMesh *pcMesh) const;
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Count the number of vertices in the whole scene and a given // Count the number of vertices in the whole scene and a given
@ -131,8 +131,8 @@ private:
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Get a list of all vertex formats that occur for a given material // Get a list of all vertex formats that occur for a given material
// The output list contains duplicate elements // The output list contains duplicate elements
void GetVFormatList(const aiScene *pcScene, unsigned int iMat, /*void GetVFormatList(const aiScene *pcScene, unsigned int iMat,
std::list<unsigned int> &aiOut) const; std::list<unsigned int> &aiOut) const;*/
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Compute the absolute transformation matrices of each node // Compute the absolute transformation matrices of each node
@ -156,10 +156,10 @@ private:
void BuildMeshRefCountArray(const aiNode *nd, unsigned int *refs) const; void BuildMeshRefCountArray(const aiNode *nd, unsigned int *refs) const;
//! Configuration option: keep scene hierarchy as long as possible //! Configuration option: keep scene hierarchy as long as possible
bool configKeepHierarchy; bool mConfigKeepHierarchy;
bool configNormalize; bool mConfigNormalize;
bool configTransform; bool mConfigTransform;
aiMatrix4x4 configTransformation; aiMatrix4x4 mConfigTransformation;
bool mConfigPointCloud; bool mConfigPointCloud;
}; };

View File

@ -175,10 +175,9 @@ unsigned int GetMeshVFormatUnique(const aiMesh *pcMesh) {
// tangents and bitangents // tangents and bitangents
if (pcMesh->HasTangentsAndBitangents()) iRet |= 0x4; if (pcMesh->HasTangentsAndBitangents()) iRet |= 0x4;
#ifdef BOOST_STATIC_ASSERT
BOOST_STATIC_ASSERT(8 >= AI_MAX_NUMBER_OF_COLOR_SETS); static_assert(8 >= AI_MAX_NUMBER_OF_COLOR_SETS);
BOOST_STATIC_ASSERT(8 >= AI_MAX_NUMBER_OF_TEXTURECOORDS); static_assert(8 >= AI_MAX_NUMBER_OF_TEXTURECOORDS);
#endif
// texture coordinates // texture coordinates
unsigned int p = 0; unsigned int p = 0;

View File

@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2022, assimp team Copyright (c) 2006-2022, assimp team
All rights reserved. All rights reserved.
Redistribution and use of this software in source and binary forms, Redistribution and use of this software in source and binary forms,
@ -45,7 +43,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
// internal headers // internal headers
#include "RemoveRedundantMaterials.h" #include "RemoveRedundantMaterials.h"
#include <assimp/ParsingUtils.h> #include <assimp/ParsingUtils.h>
#include "ProcessHelper.h" #include "ProcessHelper.h"
@ -57,35 +54,28 @@ using namespace Assimp;
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer // Constructor to be privately used by Importer
RemoveRedundantMatsProcess::RemoveRedundantMatsProcess() RemoveRedundantMatsProcess::RemoveRedundantMatsProcess() : mConfigFixedMaterials() {}
: mConfigFixedMaterials() {
// nothing to do here
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field. // Returns whether the processing step is present in the given flag field.
bool RemoveRedundantMatsProcess::IsActive( unsigned int pFlags) const bool RemoveRedundantMatsProcess::IsActive( unsigned int pFlags) const {
{
return (pFlags & aiProcess_RemoveRedundantMaterials) != 0; return (pFlags & aiProcess_RemoveRedundantMaterials) != 0;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Setup import properties // Setup import properties
void RemoveRedundantMatsProcess::SetupProperties(const Importer* pImp) void RemoveRedundantMatsProcess::SetupProperties(const Importer* pImp) {
{
// Get value of AI_CONFIG_PP_RRM_EXCLUDE_LIST // Get value of AI_CONFIG_PP_RRM_EXCLUDE_LIST
mConfigFixedMaterials = pImp->GetPropertyString(AI_CONFIG_PP_RRM_EXCLUDE_LIST,""); mConfigFixedMaterials = pImp->GetPropertyString(AI_CONFIG_PP_RRM_EXCLUDE_LIST,"");
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data. // Executes the post processing step on the given imported data.
void RemoveRedundantMatsProcess::Execute( aiScene* pScene) void RemoveRedundantMatsProcess::Execute( aiScene* pScene) {
{
ASSIMP_LOG_DEBUG("RemoveRedundantMatsProcess begin"); ASSIMP_LOG_DEBUG("RemoveRedundantMatsProcess begin");
unsigned int redundantRemoved = 0, unreferencedRemoved = 0; unsigned int redundantRemoved = 0, unreferencedRemoved = 0;
if (pScene->mNumMaterials) if (pScene->mNumMaterials) {
{
// Find out which materials are referenced by meshes // Find out which materials are referenced by meshes
std::vector<bool> abReferenced(pScene->mNumMaterials,false); std::vector<bool> abReferenced(pScene->mNumMaterials,false);
for (unsigned int i = 0;i < pScene->mNumMeshes;++i) for (unsigned int i = 0;i < pScene->mNumMeshes;++i)
@ -134,8 +124,7 @@ void RemoveRedundantMatsProcess::Execute( aiScene* pScene)
// we do already have a specific hash. This allows us to // we do already have a specific hash. This allows us to
// determine which materials are identical. // determine which materials are identical.
uint32_t *aiHashes = new uint32_t[ pScene->mNumMaterials ];; uint32_t *aiHashes = new uint32_t[ pScene->mNumMaterials ];;
for (unsigned int i = 0; i < pScene->mNumMaterials;++i) for (unsigned int i = 0; i < pScene->mNumMaterials;++i) {
{
// No mesh is referencing this material, remove it. // No mesh is referencing this material, remove it.
if (!abReferenced[i]) { if (!abReferenced[i]) {
++unreferencedRemoved; ++unreferencedRemoved;
@ -147,8 +136,7 @@ void RemoveRedundantMatsProcess::Execute( aiScene* pScene)
// Check all previously mapped materials for a matching hash. // Check all previously mapped materials for a matching hash.
// On a match we can delete this material and just make it ref to the same index. // On a match we can delete this material and just make it ref to the same index.
uint32_t me = aiHashes[i] = ComputeMaterialHash(pScene->mMaterials[i]); uint32_t me = aiHashes[i] = ComputeMaterialHash(pScene->mMaterials[i]);
for (unsigned int a = 0; a < i;++a) for (unsigned int a = 0; a < i;++a) {
{
if (abReferenced[a] && me == aiHashes[a]) { if (abReferenced[a] && me == aiHashes[a]) {
++redundantRemoved; ++redundantRemoved;
me = 0; me = 0;
@ -205,12 +193,9 @@ void RemoveRedundantMatsProcess::Execute( aiScene* pScene)
delete[] aiHashes; delete[] aiHashes;
delete[] aiMappingTable; delete[] aiMappingTable;
} }
if (redundantRemoved == 0 && unreferencedRemoved == 0) if (redundantRemoved == 0 && unreferencedRemoved == 0) {
{
ASSIMP_LOG_DEBUG("RemoveRedundantMatsProcess finished "); ASSIMP_LOG_DEBUG("RemoveRedundantMatsProcess finished ");
} } else {
else
{
ASSIMP_LOG_INFO("RemoveRedundantMatsProcess finished. Removed ", redundantRemoved, " redundant and ", ASSIMP_LOG_INFO("RemoveRedundantMatsProcess finished. Removed ", redundantRemoved, " redundant and ",
unreferencedRemoved, " unused materials."); unreferencedRemoved, " unused materials.");
} }

View File

@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2022, assimp team Copyright (c) 2006-2022, assimp team
All rights reserved. All rights reserved.
Redistribution and use of this software in source and binary forms, Redistribution and use of this software in source and binary forms,
@ -74,63 +72,6 @@ inline void ArrayDelete(T **&in, unsigned int &num) {
num = 0; num = 0;
} }
#if 0
// ------------------------------------------------------------------------------------------------
// Updates the node graph - removes all nodes which have the "remove" flag set and the
// "don't remove" flag not set. Nodes with meshes are never deleted.
bool UpdateNodeGraph(aiNode* node,std::list<aiNode*>& childsOfParent,bool root)
{
bool b = false;
std::list<aiNode*> mine;
for (unsigned int i = 0; i < node->mNumChildren;++i)
{
if(UpdateNodeGraph(node->mChildren[i],mine,false))
b = true;
}
// somewhat tricky ... mNumMeshes must be originally 0 and MSB2 may not be set,
// so we can do a simple comparison against MSB here
if (!root && AI_RC_UINT_MSB == node->mNumMeshes )
{
// this node needs to be removed
if(node->mNumChildren)
{
childsOfParent.insert(childsOfParent.end(),mine.begin(),mine.end());
// set all children to nullptr to make sure they are not deleted when we delete ourself
for (unsigned int i = 0; i < node->mNumChildren;++i)
node->mChildren[i] = nullptr;
}
b = true;
delete node;
}
else
{
AI_RC_UNMASK(node->mNumMeshes);
childsOfParent.push_back(node);
if (b)
{
// reallocate the array of our children here
node->mNumChildren = (unsigned int)mine.size();
aiNode** const children = new aiNode*[mine.size()];
aiNode** ptr = children;
for (std::list<aiNode*>::iterator it = mine.begin(), end = mine.end();
it != end; ++it)
{
*ptr++ = *it;
}
delete[] node->mChildren;
node->mChildren = children;
return false;
}
}
return b;
}
#endif
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data. // Executes the post processing step on the given imported data.
void RemoveVCProcess::Execute(aiScene *pScene) { void RemoveVCProcess::Execute(aiScene *pScene) {

View File

@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2022, assimp team Copyright (c) 2006-2022, assimp team
All rights reserved. All rights reserved.
Redistribution and use of this software in source and binary forms, Redistribution and use of this software in source and binary forms,
@ -140,7 +139,7 @@ void ScaleProcess::Execute( aiScene* pScene ) {
aiMatrix4x4 scaling; aiMatrix4x4 scaling;
aiMatrix4x4::Scaling( aiVector3D(scale), scaling ); aiMatrix4x4::Scaling( aiVector3D(scale), scaling );
aiMatrix4x4 RotMatrix = aiMatrix4x4 (rotation.GetMatrix()); const aiMatrix4x4 RotMatrix = aiMatrix4x4(rotation.GetMatrix());
bone->mOffsetMatrix = translation * RotMatrix * scaling; bone->mOffsetMatrix = translation * RotMatrix * scaling;
} }

View File

@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2022, assimp team Copyright (c) 2006-2022, assimp team
All rights reserved. All rights reserved.
Redistribution and use of this software in source and binary forms, Redistribution and use of this software in source and binary forms,
@ -54,10 +52,7 @@ using namespace Assimp;
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer // Constructor to be privately used by Importer
SortByPTypeProcess::SortByPTypeProcess() : SortByPTypeProcess::SortByPTypeProcess() : mConfigRemoveMeshes(0) {}
mConfigRemoveMeshes(0) {
// empty
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field. // Returns whether the processing step is present in the given flag field.
@ -104,9 +99,10 @@ void UpdateNodes(const std::vector<unsigned int> &replaceMeshIndex, aiNode *node
} }
// call all subnodes recursively // call all subnodes recursively
for (unsigned int m = 0; m < node->mNumChildren; ++m) for (unsigned int m = 0; m < node->mNumChildren; ++m) {
UpdateNodes(replaceMeshIndex, node->mChildren[m]); UpdateNodes(replaceMeshIndex, node->mChildren[m]);
} }
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data. // Executes the post processing step on the given imported data.
@ -155,7 +151,7 @@ void SortByPTypeProcess::Execute(aiScene *pScene) {
if (1 == num) { if (1 == num) {
if (!(mConfigRemoveMeshes & mesh->mPrimitiveTypes)) { if (!(mConfigRemoveMeshes & mesh->mPrimitiveTypes)) {
*meshIdx = static_cast<unsigned int>(outMeshes.size()); *meshIdx = static_cast<unsigned int>(outMeshes.size());
outMeshes.push_back(mesh); outMeshes.emplace_back(mesh);
} else { } else {
delete mesh; delete mesh;
pScene->mMeshes[i] = nullptr; pScene->mMeshes[i] = nullptr;
@ -311,21 +307,23 @@ void SortByPTypeProcess::Execute(aiScene *pScene) {
if (vert) { if (vert) {
*vert++ = mesh->mVertices[idx]; *vert++ = mesh->mVertices[idx];
//mesh->mVertices[idx].x = get_qnan();
} }
if (nor) *nor++ = mesh->mNormals[idx]; if (nor)
*nor++ = mesh->mNormals[idx];
if (tan) { if (tan) {
*tan++ = mesh->mTangents[idx]; *tan++ = mesh->mTangents[idx];
*bit++ = mesh->mBitangents[idx]; *bit++ = mesh->mBitangents[idx];
} }
for (unsigned int pp = 0; pp < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++pp) { for (unsigned int pp = 0; pp < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++pp) {
if (!uv[pp]) break; if (!uv[pp])
break;
*uv[pp]++ = mesh->mTextureCoords[pp][idx]; *uv[pp]++ = mesh->mTextureCoords[pp][idx];
} }
for (unsigned int pp = 0; pp < AI_MAX_NUMBER_OF_COLOR_SETS; ++pp) { for (unsigned int pp = 0; pp < AI_MAX_NUMBER_OF_COLOR_SETS; ++pp) {
if (!cols[pp]) break; if (!cols[pp])
break;
*cols[pp]++ = mesh->mColors[pp][idx]; *cols[pp]++ = mesh->mColors[pp][idx];
} }
@ -351,7 +349,7 @@ void SortByPTypeProcess::Execute(aiScene *pScene) {
} }
} }
if (pp == mesh->mNumAnimMeshes) if (pp == mesh->mNumAnimMeshes)
amIdx++; ++amIdx;
in.mIndices[q] = outIdx++; in.mIndices[q] = outIdx++;
} }

View File

@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2022, assimp team Copyright (c) 2006-2022, assimp team
All rights reserved. All rights reserved.
Redistribution and use of this software in source and binary forms, Redistribution and use of this software in source and binary forms,
@ -58,9 +57,7 @@ using namespace Assimp::Formatter;
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Constructor // Constructor
SplitByBoneCountProcess::SplitByBoneCountProcess() : mMaxBoneCount(AI_SBBC_DEFAULT_MAX_BONES) { SplitByBoneCountProcess::SplitByBoneCountProcess() : mMaxBoneCount(AI_SBBC_DEFAULT_MAX_BONES) {}
// empty
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag. // Returns whether the processing step is present in the given flag.
@ -166,7 +163,7 @@ void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vector<aiMesh
unsigned int numBones = 0; unsigned int numBones = 0;
std::vector<bool> isBoneUsed( pMesh->mNumBones, false); std::vector<bool> isBoneUsed( pMesh->mNumBones, false);
// indices of the faces which are going to go into this submesh // indices of the faces which are going to go into this submesh
std::vector<unsigned int> subMeshFaces; IndexArray subMeshFaces;
subMeshFaces.reserve( pMesh->mNumFaces); subMeshFaces.reserve( pMesh->mNumFaces);
// accumulated vertex count of all the faces in this submesh // accumulated vertex count of all the faces in this submesh
unsigned int numSubMeshVertices = 0; unsigned int numSubMeshVertices = 0;
@ -202,7 +199,7 @@ void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vector<aiMesh
for (std::set<unsigned int>::iterator it = newBonesAtCurrentFace.begin(); it != newBonesAtCurrentFace.end(); ++it) { for (std::set<unsigned int>::iterator it = newBonesAtCurrentFace.begin(); it != newBonesAtCurrentFace.end(); ++it) {
if (!isBoneUsed[*it]) { if (!isBoneUsed[*it]) {
isBoneUsed[*it] = true; isBoneUsed[*it] = true;
numBones++; ++numBones;
} }
} }
@ -212,18 +209,17 @@ void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vector<aiMesh
// remember that this face is handled // remember that this face is handled
isFaceHandled[a] = true; isFaceHandled[a] = true;
numFacesHandled++; ++numFacesHandled;
} }
// create a new mesh to hold this subset of the source mesh // create a new mesh to hold this subset of the source mesh
aiMesh* newMesh = new aiMesh; aiMesh* newMesh = new aiMesh;
if( pMesh->mName.length > 0 ) if( pMesh->mName.length > 0 ) {
{
newMesh->mName.Set( format() << pMesh->mName.data << "_sub" << poNewMeshes.size()); newMesh->mName.Set( format() << pMesh->mName.data << "_sub" << poNewMeshes.size());
} }
newMesh->mMaterialIndex = pMesh->mMaterialIndex; newMesh->mMaterialIndex = pMesh->mMaterialIndex;
newMesh->mPrimitiveTypes = pMesh->mPrimitiveTypes; newMesh->mPrimitiveTypes = pMesh->mPrimitiveTypes;
poNewMeshes.push_back( newMesh); poNewMeshes.emplace_back( newMesh);
// create all the arrays for this mesh if the old mesh contained them // create all the arrays for this mesh if the old mesh contained them
newMesh->mNumVertices = numSubMeshVertices; newMesh->mNumVertices = numSubMeshVertices;
@ -251,7 +247,7 @@ void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vector<aiMesh
// and copy over the data, generating faces with linear indices along the way // and copy over the data, generating faces with linear indices along the way
newMesh->mFaces = new aiFace[subMeshFaces.size()]; newMesh->mFaces = new aiFace[subMeshFaces.size()];
unsigned int nvi = 0; // next vertex index 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 IndexArray 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 ) { for( unsigned int a = 0; a < subMeshFaces.size(); ++a ) {
const aiFace& srcFace = pMesh->mFaces[subMeshFaces[a]]; const aiFace& srcFace = pMesh->mFaces[subMeshFaces[a]];
aiFace& dstFace = newMesh->mFaces[a]; aiFace& dstFace = newMesh->mFaces[a];
@ -399,10 +395,10 @@ void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vector<aiMesh
void SplitByBoneCountProcess::UpdateNode( aiNode* pNode) const { void SplitByBoneCountProcess::UpdateNode( aiNode* pNode) const {
// rebuild the node's mesh index list // rebuild the node's mesh index list
if( pNode->mNumMeshes == 0 ) { if( pNode->mNumMeshes == 0 ) {
std::vector<unsigned int> newMeshList; IndexArray newMeshList;
for( unsigned int a = 0; a < pNode->mNumMeshes; ++a) { for( unsigned int a = 0; a < pNode->mNumMeshes; ++a) {
unsigned int srcIndex = pNode->mMeshes[a]; unsigned int srcIndex = pNode->mMeshes[a];
const std::vector<unsigned int>& replaceMeshes = mSubMeshIndices[srcIndex]; const IndexArray& replaceMeshes = mSubMeshIndices[srcIndex];
newMeshList.insert( newMeshList.end(), replaceMeshes.begin(), replaceMeshes.end()); newMeshList.insert( newMeshList.end(), replaceMeshes.begin(), replaceMeshes.end());
} }

View File

@ -76,6 +76,10 @@ public:
/// basing on the Importer's configuration property list. /// basing on the Importer's configuration property list.
virtual void SetupProperties(const Importer* pImp) override; virtual void SetupProperties(const Importer* pImp) override;
/// @brief Will return the maximal number of bones.
/// @return The maximal number of bones.
size_t getMaxNumberOfBones() const;
protected: protected:
/// Executes the post processing step on the given imported data. /// Executes the post processing step on the given imported data.
/// At the moment a process is not supposed to fail. /// At the moment a process is not supposed to fail.
@ -90,14 +94,19 @@ protected:
/// Recursively updates the node's mesh list to account for the changed mesh list /// Recursively updates the node's mesh list to account for the changed mesh list
void UpdateNode( aiNode* pNode) const; void UpdateNode( aiNode* pNode) const;
public: private:
/// Max bone count. Splitting occurs if a mesh has more than that number of bones. /// Max bone count. Splitting occurs if a mesh has more than that number of bones.
size_t mMaxBoneCount; size_t mMaxBoneCount;
/// Per mesh index: Array of indices of the new submeshes. /// Per mesh index: Array of indices of the new submeshes.
std::vector< std::vector<unsigned int> > mSubMeshIndices; using IndexArray = std::vector<unsigned int>;
std::vector<IndexArray> mSubMeshIndices;
}; };
inline size_t SplitByBoneCountProcess::getMaxNumberOfBones() const {
return mMaxBoneCount;
}
} // end of namespace Assimp } // end of namespace Assimp
#endif // !!AI_SPLITBYBONECOUNTPROCESS_H_INC #endif // !!AI_SPLITBYBONECOUNTPROCESS_H_INC

View File

@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2022, assimp team Copyright (c) 2006-2022, assimp team
All rights reserved. All rights reserved.
Redistribution and use of this software in source and binary forms, Redistribution and use of this software in source and binary forms,
@ -40,9 +39,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------- ----------------------------------------------------------------------
*/ */
/** /// @file Implementation of the SplitLargeMeshes postprocessing step
* @file Implementation of the SplitLargeMeshes postprocessing step
*/
// internal headers of the post-processing framework // internal headers of the post-processing framework
#include "SplitLargeMeshes.h" #include "SplitLargeMeshes.h"
@ -75,7 +72,10 @@ void SplitLargeMeshesProcess_Triangle::Execute( aiScene* pScene) {
this->SplitMesh(a, pScene->mMeshes[a],avList); this->SplitMesh(a, pScene->mMeshes[a],avList);
} }
if (avList.size() != pScene->mNumMeshes) { if (avList.size() == pScene->mNumMeshes) {
ASSIMP_LOG_DEBUG("SplitLargeMeshesProcess_Triangle finished. There was nothing to do");
}
// it seems something has been split. rebuild the mesh list // it seems something has been split. rebuild the mesh list
delete[] pScene->mMeshes; delete[] pScene->mMeshes;
pScene->mNumMeshes = (unsigned int)avList.size(); pScene->mNumMeshes = (unsigned int)avList.size();
@ -88,9 +88,6 @@ void SplitLargeMeshesProcess_Triangle::Execute( aiScene* pScene) {
// now we need to update all nodes // now we need to update all nodes
this->UpdateNode(pScene->mRootNode,avList); this->UpdateNode(pScene->mRootNode,avList);
ASSIMP_LOG_INFO("SplitLargeMeshesProcess_Triangle finished. Meshes have been split"); ASSIMP_LOG_INFO("SplitLargeMeshesProcess_Triangle finished. Meshes have been split");
} else {
ASSIMP_LOG_DEBUG("SplitLargeMeshesProcess_Triangle finished. There was nothing to do");
}
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -102,8 +99,7 @@ void SplitLargeMeshesProcess_Triangle::SetupProperties( const Importer* pImp) {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Update a node after some meshes have been split // Update a node after some meshes have been split
void SplitLargeMeshesProcess_Triangle::UpdateNode(aiNode* pcNode, void SplitLargeMeshesProcess_Triangle::UpdateNode(aiNode* pcNode, const std::vector<std::pair<aiMesh*, unsigned int> >& avList) {
const std::vector<std::pair<aiMesh*, unsigned int> >& avList) {
// for every index in out list build a new entry // for every index in out list build a new entry
std::vector<unsigned int> aiEntries; std::vector<unsigned int> aiEntries;
aiEntries.reserve(pcNode->mNumMeshes + 1); aiEntries.reserve(pcNode->mNumMeshes + 1);

View File

@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2022, assimp team Copyright (c) 2006-2022, assimp team
All rights reserved. All rights reserved.
Redistribution and use of this software in source and binary forms, Redistribution and use of this software in source and binary forms,
@ -42,8 +41,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/** @file A helper class that processes texture transformations */ /** @file A helper class that processes texture transformations */
#include <assimp/Importer.hpp> #include <assimp/Importer.hpp>
#include <assimp/postprocess.h> #include <assimp/postprocess.h>
#include <assimp/DefaultLogger.hpp> #include <assimp/DefaultLogger.hpp>
@ -494,8 +491,9 @@ void TextureTransformStep::Execute( aiScene* pScene) {
ai_assert(nullptr != src); ai_assert(nullptr != src);
// Copy the data to the destination array // Copy the data to the destination array
if (dest != src) if (dest != src) {
::memcpy(dest,src,sizeof(aiVector3D)*mesh->mNumVertices); ::memcpy(dest,src,sizeof(aiVector3D)*mesh->mNumVertices);
}
end = dest + mesh->mNumVertices; end = dest + mesh->mNumVertices;

View File

@ -158,15 +158,13 @@ namespace {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field. // Returns whether the processing step is present in the given flag field.
bool TriangulateProcess::IsActive( unsigned int pFlags) const bool TriangulateProcess::IsActive( unsigned int pFlags) const {
{
return (pFlags & aiProcess_Triangulate) != 0; return (pFlags & aiProcess_Triangulate) != 0;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data. // Executes the post processing step on the given imported data.
void TriangulateProcess::Execute( aiScene* pScene) void TriangulateProcess::Execute( aiScene* pScene) {
{
ASSIMP_LOG_DEBUG("TriangulateProcess begin"); ASSIMP_LOG_DEBUG("TriangulateProcess begin");
bool bHas = false; bool bHas = false;
@ -187,8 +185,7 @@ void TriangulateProcess::Execute( aiScene* pScene)
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Triangulates the given mesh. // Triangulates the given mesh.
bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh) bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh) {
{
// Now we have aiMesh::mPrimitiveTypes, so this is only here for test cases // Now we have aiMesh::mPrimitiveTypes, so this is only here for test cases
if (!pMesh->mPrimitiveTypes) { if (!pMesh->mPrimitiveTypes) {
bool bNeed = false; bool bNeed = false;
@ -218,8 +215,7 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
if( face.mNumIndices <= 3) { if( face.mNumIndices <= 3) {
numOut++; numOut++;
} } else {
else {
numOut += face.mNumIndices-2; numOut += face.mNumIndices-2;
max_out = std::max(max_out,face.mNumIndices); max_out = std::max(max_out,face.mNumIndices);
} }
@ -455,7 +451,7 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
*pnt2 = &temp_verts[next]; *pnt2 = &temp_verts[next];
// Must be a convex point. Assuming ccw winding, it must be on the right of the line between p-1 and p+1. // Must be a convex point. Assuming ccw winding, it must be on the right of the line between p-1 and p+1.
if (OnLeftSideOfLine2D(*pnt0,*pnt2,*pnt1)) { if (OnLeftSideOfLine2D(*pnt0,*pnt2,*pnt1) == 1) {
continue; continue;
} }
@ -511,22 +507,6 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
#endif #endif
num = 0; num = 0;
break; break;
/*curOut -= (max-num); // undo all previous work
for (tmp = 0; tmp < max-2; ++tmp) {
aiFace& nface = *curOut++;
nface.mNumIndices = 3;
if (!nface.mIndices)
nface.mIndices = new unsigned int[3];
nface.mIndices[0] = 0;
nface.mIndices[1] = tmp+1;
nface.mIndices[2] = tmp+2;
}
num = 0;
break;*/
} }
aiFace& nface = *curOut++; aiFace& nface = *curOut++;
@ -580,23 +560,6 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
for(aiFace* f = last_face; f != curOut; ) { for(aiFace* f = last_face; f != curOut; ) {
unsigned int* i = f->mIndices; unsigned int* i = f->mIndices;
// drop dumb 0-area triangles - deactivated for now:
//FindDegenerates post processing step can do the same thing
//if (std::fabs(GetArea2D(temp_verts[i[0]],temp_verts[i[1]],temp_verts[i[2]])) < 1e-5f) {
// ASSIMP_LOG_VERBOSE_DEBUG("Dropping triangle with area 0");
// --curOut;
// delete[] f->mIndices;
// f->mIndices = nullptr;
// for(aiFace* ff = f; ff != curOut; ++ff) {
// ff->mNumIndices = (ff+1)->mNumIndices;
// ff->mIndices = (ff+1)->mIndices;
// (ff+1)->mIndices = nullptr;
// }
// continue;
//}
i[0] = idx[i[0]]; i[0] = idx[i[0]];
i[1] = idx[i[1]]; i[1] = idx[i[1]];
i[2] = idx[i[2]]; i[2] = idx[i[2]];

View File

@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2022, assimp team Copyright (c) 2006-2022, assimp team
All rights reserved. All rights reserved.
Redistribution and use of this software in source and binary forms, Redistribution and use of this software in source and binary forms,
@ -110,11 +108,15 @@ inline int HasNameMatch(const aiString &in, aiNode *node) {
template <typename T> template <typename T>
inline void ValidateDSProcess::DoValidation(T **parray, unsigned int size, const char *firstName, const char *secondName) { inline void ValidateDSProcess::DoValidation(T **parray, unsigned int size, const char *firstName, const char *secondName) {
// validate all entries // validate all entries
if (size) { if (size == 0) {
return;
}
if (!parray) { if (!parray) {
ReportError("aiScene::%s is nullptr (aiScene::%s is %i)", ReportError("aiScene::%s is nullptr (aiScene::%s is %i)",
firstName, secondName, size); firstName, secondName, size);
} }
for (unsigned int i = 0; i < size; ++i) { for (unsigned int i = 0; i < size; ++i) {
if (!parray[i]) { if (!parray[i]) {
ReportError("aiScene::%s[%i] is nullptr (aiScene::%s is %i)", ReportError("aiScene::%s[%i] is nullptr (aiScene::%s is %i)",
@ -123,14 +125,16 @@ inline void ValidateDSProcess::DoValidation(T **parray, unsigned int size, const
Validate(parray[i]); Validate(parray[i]);
} }
} }
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
template <typename T> template <typename T>
inline void ValidateDSProcess::DoValidationEx(T **parray, unsigned int size, inline void ValidateDSProcess::DoValidationEx(T **parray, unsigned int size,
const char *firstName, const char *secondName) { const char *firstName, const char *secondName) {
// validate all entries // validate all entries
if (size) { if (size == 0) {
return;
}
if (!parray) { if (!parray) {
ReportError("aiScene::%s is nullptr (aiScene::%s is %i)", ReportError("aiScene::%s is nullptr (aiScene::%s is %i)",
firstName, secondName, size); firstName, secondName, size);
@ -152,7 +156,6 @@ inline void ValidateDSProcess::DoValidationEx(T **parray, unsigned int size,
} }
} }
} }
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
template <typename T> template <typename T>
@ -229,12 +232,6 @@ void ValidateDSProcess::Execute(aiScene *pScene) {
if (pScene->mNumMaterials) { if (pScene->mNumMaterials) {
DoValidation(pScene->mMaterials, pScene->mNumMaterials, "mMaterials", "mNumMaterials"); DoValidation(pScene->mMaterials, pScene->mNumMaterials, "mMaterials", "mNumMaterials");
} }
#if 0
// NOTE: ScenePreprocessor generates a default material if none is there
else if (!(mScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE)) {
ReportError("aiScene::mNumMaterials is 0. At least one material must be there");
}
#endif
else if (pScene->mMaterials) { else if (pScene->mMaterials) {
ReportError("aiScene::mMaterials is non-null although there are no materials"); ReportError("aiScene::mMaterials is non-null although there are no materials");
} }
@ -267,8 +264,7 @@ void ValidateDSProcess::Validate(const aiCamera *pCamera) {
if (pCamera->mClipPlaneFar <= pCamera->mClipPlaneNear) if (pCamera->mClipPlaneFar <= pCamera->mClipPlaneNear)
ReportError("aiCamera::mClipPlaneFar must be >= aiCamera::mClipPlaneNear"); ReportError("aiCamera::mClipPlaneFar must be >= aiCamera::mClipPlaneNear");
// FIX: there are many 3ds files with invalid FOVs. No reason to // There are many 3ds files with invalid FOVs. No reason to reject them at all ... a warning is appropriate.
// reject them at all ... a warning is appropriate.
if (!pCamera->mHorizontalFOV || pCamera->mHorizontalFOV >= (float)AI_MATH_PI) if (!pCamera->mHorizontalFOV || pCamera->mHorizontalFOV >= (float)AI_MATH_PI)
ReportWarning("%f is not a valid value for aiCamera::mHorizontalFOV", pCamera->mHorizontalFOV); ReportWarning("%f is not a valid value for aiCamera::mHorizontalFOV", pCamera->mHorizontalFOV);
} }
@ -361,15 +357,6 @@ void ValidateDSProcess::Validate(const aiMesh *pMesh) {
if (face.mIndices[a] >= pMesh->mNumVertices) { if (face.mIndices[a] >= pMesh->mNumVertices) {
ReportError("aiMesh::mFaces[%i]::mIndices[%i] is out of range", i, a); ReportError("aiMesh::mFaces[%i]::mIndices[%i] is out of range", i, a);
} }
// the MSB flag is temporarily used by the extra verbose
// mode to tell us that the JoinVerticesProcess might have
// been executed already.
/*if ( !(this->mScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT ) && !(this->mScene->mFlags & AI_SCENE_FLAGS_ALLOW_SHARED) &&
abRefList[face.mIndices[a]])
{
ReportError("aiMesh::mVertices[%i] is referenced twice - second "
"time by aiMesh::mFaces[%i]::mIndices[%i]",face.mIndices[a],i,a);
}*/
abRefList[face.mIndices[a]] = true; abRefList[face.mIndices[a]] = true;
} }
} }
@ -465,7 +452,7 @@ void ValidateDSProcess::Validate(const aiMesh *pMesh, const aiBone *pBone, float
this->Validate(&pBone->mName); this->Validate(&pBone->mName);
if (!pBone->mNumWeights) { if (!pBone->mNumWeights) {
//ReportError("aiBone::mNumWeights is zero"); ReportWarning("aiBone::mNumWeights is zero");
} }
// check whether all vertices affected by this bone are valid // check whether all vertices affected by this bone are valid

View File

@ -15,8 +15,10 @@ option( DDL_STATIC_LIBRARY "Deprecated, use BUILD_SHARED_LIBS instead."
# for backwards compatibility use DDL_STATIC_LIBRARY as initial value for cmake variable BUILD_SHARED_LIBS # for backwards compatibility use DDL_STATIC_LIBRARY as initial value for cmake variable BUILD_SHARED_LIBS
# https://cmake.org/cmake/help/latest/variable/BUILD_SHARED_LIBS.html # https://cmake.org/cmake/help/latest/variable/BUILD_SHARED_LIBS.html
if ( DDL_STATIC_LIBRARY ) if ( DDL_STATIC_LIBRARY )
message("Building shared lib.")
set ( build_shared_libs_default OFF ) set ( build_shared_libs_default OFF )
else() else()
message("Building static lib.")
set ( build_shared_libs_default ON ) set ( build_shared_libs_default ON )
endif() endif()
option( DDL_BUILD_SHARED_LIBS "Set to ON to build shared libary of OpenDDL Parser." ${build_shared_libs_default} ) option( DDL_BUILD_SHARED_LIBS "Set to ON to build shared libary of OpenDDL Parser." ${build_shared_libs_default} )
@ -36,6 +38,7 @@ endif()
add_definitions( -D_VARIADIC_MAX=10 ) add_definitions( -D_VARIADIC_MAX=10 )
add_definitions( -DGTEST_HAS_PTHREAD=0 ) add_definitions( -DGTEST_HAS_PTHREAD=0 )
if ( DDL_DEBUG_OUTPUT ) if ( DDL_DEBUG_OUTPUT )
message("Enable debug output.")
add_definitions( -DDDL_DEBUG_HEADER_NAME) add_definitions( -DDDL_DEBUG_HEADER_NAME)
endif() endif()
@ -62,10 +65,12 @@ if (COVERALLS)
include(Coveralls) include(Coveralls)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage")
message("Enable coveralls.")
endif() endif()
# Include the doc component. # Include the doc component.
if(DDL_DOCUMENTATION) if(DDL_DOCUMENTATION)
message("Generate doxygen documentation.")
find_package(Doxygen REQUIRED) find_package(Doxygen REQUIRED)
CONFIGURE_FILE( doc/openddlparser_doc.in doc/doxygenfile @ONLY ) CONFIGURE_FILE( doc/openddlparser_doc.in doc/doxygenfile @ONLY )
add_custom_target(doc ALL add_custom_target(doc ALL

View File

@ -5,13 +5,15 @@ The OpenDDL-Parser is a small and easy to use library for OpenDDL-file-format-pa
Build status Build status
============ ============
Linux build status: [![Build Status](https://travis-ci.org/kimkulling/openddl-parser.png)](https://travis-ci.org/kimkulling/openddl-parser)
Linux build status: [![Build Status](https://travis-ci.com/kimkulling/openddl-parser.svg?branch=master)](https://travis-ci.com/kimkulling/openddl-parser)
Current coverity check status: Current coverity check status:
<a href="https://scan.coverity.com/projects/5606"> <a href="https://scan.coverity.com/projects/5606">
<img alt="Coverity Scan Build Status" <img alt="Coverity Scan Build Status"
src="https://scan.coverity.com/projects/5606/badge.svg"/> src="https://scan.coverity.com/projects/5606/badge.svg"/>
</a> </a>
Current test coverage:[![Coverage Status](https://coveralls.io/repos/github/kimkulling/openddl-parser/badge.svg?branch=master)](https://coveralls.io/github/kimkulling/openddl-parser?branch=cpp_coveralls) Current test coverage:[![Coverage Status](https://coveralls.io/repos/github/kimkulling/openddl-parser/badge.svg?branch=master)](https://coveralls.io/github/kimkulling/openddl-parser?branch=cpp_coveralls)
Get the source code Get the source code
=================== ===================
You can get the code from our git repository, which is located at GitHub. You can clone the repository with the following command: You can get the code from our git repository, which is located at GitHub. You can clone the repository with the following command:
@ -57,7 +59,7 @@ USE_ODDLPARSER_NS;
int main( int argc, char *argv[] ) { int main( int argc, char *argv[] ) {
if( argc < 3 ) { if( argc < 3 ) {
return 1; return Error;
} }
char *filename( nullptr ); char *filename( nullptr );
@ -73,24 +75,27 @@ int main( int argc, char *argv[] ) {
FILE *fileStream = fopen( filename, "r+" ); FILE *fileStream = fopen( filename, "r+" );
if( NULL == filename ) { if( NULL == filename ) {
std::cerr << "Cannot open file " << filename << std::endl; std::cerr << "Cannot open file " << filename << std::endl;
return 1; return Error;
} }
// obtain file size: // obtain file size:
fseek( fileStream, 0, SEEK_END ); fseek( fileStream, 0, SEEK_END );
const size_t size( ftell( fileStream ) ); const size_t size = ftell( fileStream );
rewind( fileStream ); rewind( fileStream );
if( size > 0 ) { if( size > 0 ) {
char *buffer = new char[ size ]; char *buffer = new char[ size ];
const size_t readSize( fread( buffer, sizeof( char ), size, fileStream ) ); const size_t readSize = fread( buffer, sizeof( char ), size, fileStream );
assert( readSize == size ); assert( readSize == size );
// Set the memory buffer
OpenDDLParser theParser; OpenDDLParser theParser;
theParser.setBuffer( buffer, size ); theParser.setBuffer( buffer, size );
const bool result( theParser.parse() ); if( !theParser.parse() ) {
if( !result ) {
std::cerr << "Error while parsing file " << filename << "." << std::endl; std::cerr << "Error while parsing file " << filename << "." << std::endl;
return Error;
} }
} }
return 0; return 0;
} }
@ -106,9 +111,9 @@ theParser.setBuffer( buffer, size );
const bool result( theParser.parse() ); const bool result( theParser.parse() );
if ( result ) { if ( result ) {
DDLNode *root = theParser.getRoot(); DDLNode *root = theParser.getRoot();
DDLNode::DllNodeList childs = root->getChildNodeList(); DDLNode::DllNodeList children = root->getChildNodeList();
for ( size_t i=0; i<childs.size(); i++ ) { for ( size_t i=0; i<children.size(); i++ ) {
DDLNode *child = childs[ i ]; DDLNode *child = children[ i ];
Property *prop = child->getProperty(); // to get properties Property *prop = child->getProperty(); // to get properties
std::string type = child->getType(); // to get the node type std::string type = child->getType(); // to get the node type
Value *values = child->getValue(); // to get the data; Value *values = child->getValue(); // to get the data;

View File

@ -134,9 +134,10 @@ bool OpenDDLExport::writeToStream(const std::string &statement) {
} }
bool OpenDDLExport::writeNode(DDLNode *node, std::string &statement) { bool OpenDDLExport::writeNode(DDLNode *node, std::string &statement) {
bool success(true);
writeNodeHeader(node, statement); writeNodeHeader(node, statement);
if (node->hasProperties()) { if (node->hasProperties()) {
writeProperties(node, statement); success = writeProperties(node, statement);
} }
writeLineEnd(statement); writeLineEnd(statement);
@ -160,7 +161,7 @@ bool OpenDDLExport::writeNode(DDLNode *node, std::string &statement) {
writeToStream(statement); writeToStream(statement);
return true; return success;
} }
bool OpenDDLExport::writeNodeHeader(DDLNode *node, std::string &statement) { bool OpenDDLExport::writeNodeHeader(DDLNode *node, std::string &statement) {

View File

@ -30,6 +30,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <sstream> #include <sstream>
#ifdef _WIN32 #ifdef _WIN32
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# include <windows.h> # include <windows.h>
#endif // _WIN32 #endif // _WIN32
@ -71,7 +74,7 @@ const char *getTypeToken(Value::ValueType type) {
return Grammar::PrimitiveTypeToken[(size_t)type]; return Grammar::PrimitiveTypeToken[(size_t)type];
} }
static void logInvalidTokenError(char *in, const std::string &exp, OpenDDLParser::logCallback callback) { static void logInvalidTokenError(const char *in, const std::string &exp, OpenDDLParser::logCallback callback) {
if (callback) { if (callback) {
std::string full(in); std::string full(in);
std::string part(full.substr(0, 50)); std::string part(full.substr(0, 50));
@ -338,6 +341,7 @@ char *OpenDDLParser::parseStructure(char *in, char *end) {
bool error(false); bool error(false);
in = lookForNextToken(in, end); in = lookForNextToken(in, end);
if (in != end) {
if (*in == *Grammar::OpenBracketToken) { if (*in == *Grammar::OpenBracketToken) {
// loop over all children ( data and nodes ) // loop over all children ( data and nodes )
do { do {
@ -345,14 +349,18 @@ char *OpenDDLParser::parseStructure(char *in, char *end) {
if (in == nullptr) { if (in == nullptr) {
return nullptr; return nullptr;
} }
} while (*in != *Grammar::CloseBracketToken); } while (in != end &&
*in != *Grammar::CloseBracketToken);
if (in != end) {
++in; ++in;
}
} else { } else {
++in; ++in;
logInvalidTokenError(in, std::string(Grammar::OpenBracketToken), m_logCallback); logInvalidTokenError(in, std::string(Grammar::OpenBracketToken), m_logCallback);
error = true; error = true;
return nullptr; return nullptr;
} }
}
in = lookForNextToken(in, end); in = lookForNextToken(in, end);
// pop node from stack after successful parsing // pop node from stack after successful parsing
@ -418,8 +426,8 @@ char *OpenDDLParser::parseStructureBody(char *in, char *end, bool &error) {
} }
in = lookForNextToken(in, end); in = lookForNextToken(in, end);
if (*in != '}') { if (in == end || *in != '}') {
logInvalidTokenError(in, std::string(Grammar::CloseBracketToken), m_logCallback); logInvalidTokenError(in == end ? "" : in, std::string(Grammar::CloseBracketToken), m_logCallback);
return nullptr; return nullptr;
} else { } else {
//in++; //in++;
@ -455,7 +463,7 @@ DDLNode *OpenDDLParser::top() {
return nullptr; return nullptr;
} }
DDLNode *top(m_stack.back()); DDLNode *top = m_stack.back();
return top; return top;
} }
@ -647,12 +655,15 @@ char *OpenDDLParser::parseBooleanLiteral(char *in, char *end, Value **boolean) {
in = lookForNextToken(in, end); in = lookForNextToken(in, end);
char *start(in); char *start(in);
size_t len(0);
while (!isSeparator(*in) && in != end) { while (!isSeparator(*in) && in != end) {
++in; ++in;
++len;
} }
int res = ::strncmp(Grammar::BoolTrue, start, strlen(Grammar::BoolTrue)); int res = ::strncmp(Grammar::BoolTrue, start, len);
if (0 != res) { if (0 != res) {
res = ::strncmp(Grammar::BoolFalse, start, strlen(Grammar::BoolFalse)); res = ::strncmp(Grammar::BoolFalse, start, len);
if (0 != res) { if (0 != res) {
*boolean = nullptr; *boolean = nullptr;
return in; return in;
@ -733,7 +744,7 @@ char *OpenDDLParser::parseFloatingLiteral(char *in, char *end, Value **floating,
in = lookForNextToken(in, end); in = lookForNextToken(in, end);
char *start(in); char *start(in);
while (!isSeparator(*in) && in != end) { while (in != end && !isSeparator(*in)) {
++in; ++in;
} }
@ -838,6 +849,13 @@ char *OpenDDLParser::parseHexaLiteral(char *in, char *end, Value **data) {
int value(0); int value(0);
while (pos > 0) { while (pos > 0) {
int v = hex2Decimal(*start); int v = hex2Decimal(*start);
if (v < 0) {
while (isEndofLine(*in)) {
++in;
}
return in;
}
--pos; --pos;
value = (value << 4) | v; value = (value << 4) | v;
++start; ++start;
@ -901,10 +919,10 @@ char *OpenDDLParser::parseDataList(char *in, char *end, Value::ValueType type, V
} }
in = lookForNextToken(in, end); in = lookForNextToken(in, end);
if (*in == '{') { if (in != end && *in == '{') {
++in; ++in;
Value *current(nullptr), *prev(nullptr); Value *current(nullptr), *prev(nullptr);
while ('}' != *in) { while (in != end && '}' != *in) {
current = nullptr; current = nullptr;
in = lookForNextToken(in, end); in = lookForNextToken(in, end);
if (Value::ValueType::ddl_ref == type) { if (Value::ValueType::ddl_ref == type) {
@ -962,10 +980,11 @@ char *OpenDDLParser::parseDataList(char *in, char *end, Value::ValueType type, V
} }
in = getNextSeparator(in, end); in = getNextSeparator(in, end);
if (',' != *in && Grammar::CloseBracketToken[0] != *in && !isSpace(*in)) { if (in == end || (',' != *in && Grammar::CloseBracketToken[0] != *in && !isSpace(*in))) {
break; break;
} }
} }
if (in != end)
++in; ++in;
} }

View File

@ -26,8 +26,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <string> #include <string>
#include <vector> #include <vector>
#include <stdio.h> #include <cstdio>
#include <string.h> #include <cstring>
#ifndef _WIN32 #ifndef _WIN32
#include <inttypes.h> #include <inttypes.h>
#endif #endif

View File

@ -40,15 +40,6 @@ struct Identifier;
struct Reference; struct Reference;
struct Property; struct Property;
template <class T>
inline bool isEmbeddedCommentOpenTag(T *in, T *end) {
if (in == '/' && in + 1 == '*') {
return true;
}
return false;
}
/// @brief Utility function to search for the next token or the end of the buffer. /// @brief Utility function to search for the next token or the end of the buffer.
/// @param in [in] The start position in the buffer. /// @param in [in] The start position in the buffer.
/// @param end [in] The end position in the buffer. /// @param end [in] The end position in the buffer.

View File

@ -54,7 +54,9 @@ inline bool isSeparator(T in) {
return false; return false;
} }
static const unsigned char chartype_table[256] = { const size_t CharTableSize = 256;
static const unsigned char chartype_table[CharTableSize] = {
0, 0,
0, 0,
0, 0,
@ -318,6 +320,10 @@ static const unsigned char chartype_table[256] = {
template <class T> template <class T>
inline bool isNumeric(const T in) { inline bool isNumeric(const T in) {
if (static_cast<size_t>(in) >= CharTableSize) {
return '\0';
}
size_t idx = static_cast<size_t>(in); size_t idx = static_cast<size_t>(in);
return idx < sizeof(chartype_table) && (chartype_table[idx] == 1); return idx < sizeof(chartype_table) && (chartype_table[idx] == 1);
} }
@ -433,7 +439,7 @@ inline bool isEndofLine(const T in) {
template <class T> template <class T>
inline static T *getNextSeparator(T *in, T *end) { inline static T *getNextSeparator(T *in, T *end) {
while (!isSeparator(*in) || in == end) { while (in != end && !isSeparator(*in)) {
++in; ++in;
} }
return in; return in;

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