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);
// Apply unit-size scale calculation if (!ignoreUnitSize) {
// Apply unit-size scale calculation
pScene->mRootNode->mTransformation *= aiMatrix4x4(parser.mUnitSize, 0, 0, 0, pScene->mRootNode->mTransformation *= aiMatrix4x4(
0, parser.mUnitSize, 0, 0, parser.mUnitSize, 0, 0, 0,
0, 0, parser.mUnitSize, 0, 0, parser.mUnitSize, 0, 0,
0, 0, 0, 1); 0, 0, parser.mUnitSize, 0,
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

@ -71,7 +71,7 @@ static const aiColor4D AI_DXF_DEFAULT_COLOR(aiColor4D(0.6f, 0.6f, 0.6f, 0.6f));
// color indices for DXF - 16 are supported, the table is // color indices for DXF - 16 are supported, the table is
// taken directly from the DXF spec. // taken directly from the DXF spec.
static aiColor4D g_aclrDxfIndexColors[] = { static aiColor4D g_aclrDxfIndexColors[] = {
aiColor4D (0.6f, 0.6f, 0.6f, 1.0f), aiColor4D(0.6f, 0.6f, 0.6f, 1.0f),
aiColor4D (1.0f, 0.0f, 0.0f, 1.0f), // red aiColor4D (1.0f, 0.0f, 0.0f, 1.0f), // red
aiColor4D (0.0f, 1.0f, 0.0f, 1.0f), // green aiColor4D (0.0f, 1.0f, 0.0f, 1.0f), // green
aiColor4D (0.0f, 0.0f, 1.0f, 1.0f), // blue aiColor4D (0.0f, 0.0f, 1.0f, 1.0f), // blue
@ -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;
}
const int index = reader.ValueAsSignedInt();
if (index >= 0) {
indices[cnti++] = static_cast<unsigned int>(index);
} else {
indices[cnti++] = static_cast<unsigned int>(-index);
}
} }
indices[cnti++] = reader.ValueAsUnsignedInt();
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()
for(ObjectMap::value_type& v : objects) { {
delete v.second; // 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) {
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,8 +460,10 @@ void Document::ReadPropertyTemplates() {
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void Document::ReadConnections() { void Document::ReadConnections()
const Scope& sc = parser.GetRootScope(); {
StackAllocator &allocator = parser.GetAllocator();
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"];
if(!econns || !econns->Compound()) { if(!econns || !econns->Compound()) {
@ -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;
try { Assimp::StackAllocator tempAllocator;
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,10 +183,12 @@ 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
} catch (std::exception &) { std::for_each(tokens.begin(), tokens.end(), Util::destructor_fun<Token>());
std::for_each(tokens.begin(), tokens.end(), Util::delete_fun<Token>());
throw; } catch (std::exception &) {
std::for_each(tokens.begin(), tokens.end(), Util::destructor_fun<Token>());
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");
@ -190,7 +203,7 @@ Scope::Scope(Parser& parser,bool topLevel)
} }
auto *element = new_Element(*n, parser); auto *element = new_Element(*n, parser);
// Element() should stop at the next Key token (or right after a Close token) // Element() should stop at the next Key token (or right after a Close token)
n = parser.CurrentToken(); n = parser.CurrentToken();
if (n == nullptr) { if (n == nullptr) {
@ -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()
for(ElementMap::value_type& v : elements) { {
delete v.second; // This collection does not own the memory for the elements, but we need to call their d'tor:
for (ElementMap::value_type &v : elements) {
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);
} }
}; };

File diff suppressed because it is too large Load Diff

View File

@ -53,7 +53,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/StringUtils.h> #include <assimp/StringUtils.h>
#include <assimp/anim.h> #include <assimp/anim.h>
namespace Assimp { namespace Assimp {
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/** Irr importer class. /** Irr importer class.
@ -71,13 +71,13 @@ public:
/** Returns whether the class can handle the format of the given file. /** Returns whether the class can handle the format of the given file.
* See BaseImporter::CanRead() for details. * See BaseImporter::CanRead() for details.
*/ */
bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool CanRead(const std::string &pFile, IOSystem *pIOHandler,
bool checkSig) const override; bool checkSig) const override;
protected: protected:
const aiImporterDesc* GetInfo () const override; const aiImporterDesc *GetInfo() const override;
void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) override; void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) override;
void SetupProperties(const Importer* pImp) override; void SetupProperties(const Importer *pImp) override;
private: private:
/** Data structure for a scene-graph node animator /** Data structure for a scene-graph node animator
@ -85,27 +85,19 @@ private:
struct Animator { struct Animator {
// Type of the animator // Type of the animator
enum AT { enum AT {
UNKNOWN = 0x0, UNKNOWN = 0x0,
ROTATION = 0x1, ROTATION = 0x1,
FLY_CIRCLE = 0x2, FLY_CIRCLE = 0x2,
FLY_STRAIGHT = 0x3, FLY_STRAIGHT = 0x3,
FOLLOW_SPLINE = 0x4, FOLLOW_SPLINE = 0x4,
OTHER = 0x5 OTHER = 0x5
} 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,21 +134,20 @@ 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];
static int cnt; static int cnt;
ai_snprintf(buffer, 128, "IrrNode_%i",cnt++); ai_snprintf(buffer, 128, "IrrNode_%i", cnt++);
name = std::string(buffer); name = std::string(buffer);
// reserve space for up to 5 materials // reserve space for up to 5 materials
@ -175,10 +164,10 @@ private:
std::string name; std::string name;
// List of all child nodes // List of all child nodes
std::vector<Node*> children; std::vector<Node *> children;
// Parent node // Parent node
Node* parent; Node *parent;
// Animated meshes: frames per second // Animated meshes: frames per second
// 0.f if not specified // 0.f if not specified
@ -190,13 +179,13 @@ private:
// Meshes: List of materials to be assigned // Meshes: List of materials to be assigned
// along with their corresponding material flags // along with their corresponding material flags
std::vector< std::pair<aiMaterial*, unsigned int> > materials; std::vector<std::pair<aiMaterial *, unsigned int>> materials;
// Spheres: radius of the sphere to be generates // Spheres: radius of the sphere to be generates
ai_real sphereRadius; ai_real sphereRadius;
// Spheres: Number of polygons in the x,y direction // Spheres: Number of polygons in the x,y direction
unsigned int spherePolyCountX,spherePolyCountY; unsigned int spherePolyCountX, spherePolyCountY;
// List of all animators assigned to the node // List of all animators assigned to the node
std::list<Animator> animators; std::list<Animator> animators;
@ -204,40 +193,54 @@ 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
SkyboxVertex(ai_real px, ai_real py, ai_real pz, SkyboxVertex(ai_real px, ai_real py, ai_real pz,
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
void GenerateGraph(Node* root,aiNode* rootOut ,aiScene* scene, void GenerateGraph(Node *root, aiNode *rootOut, aiScene *scene,
BatchLoader& batch, BatchLoader &batch,
std::vector<aiMesh*>& meshes, std::vector<aiMesh *> &meshes,
std::vector<aiNodeAnim*>& anims, std::vector<aiNodeAnim *> &anims,
std::vector<AttachmentInfo>& attach, std::vector<AttachmentInfo> &attach,
std::vector<aiMaterial*>& materials, std::vector<aiMaterial *> &materials,
unsigned int& defaultMatIdx); unsigned int &defaultMatIdx);
// ------------------------------------------------------------------- // -------------------------------------------------------------------
/// Generate a mesh that consists of just a single quad /// Generate a mesh that consists of just a single quad
aiMesh* BuildSingleQuadMesh(const SkyboxVertex& v1, aiMesh *BuildSingleQuadMesh(const SkyboxVertex &v1,
const SkyboxVertex& v2, const SkyboxVertex &v2,
const SkyboxVertex& v3, const SkyboxVertex &v3,
const SkyboxVertex& v4); const SkyboxVertex &v4);
// ------------------------------------------------------------------- // -------------------------------------------------------------------
/// Build a sky-box /// Build a sky-box
@ -245,8 +248,8 @@ private:
/// @param meshes Receives 6 output meshes /// @param meshes Receives 6 output meshes
/// @param materials The last 6 materials are assigned to the newly /// @param materials The last 6 materials are assigned to the newly
/// created meshes. The names of the materials are adjusted. /// created meshes. The names of the materials are adjusted.
void BuildSkybox(std::vector<aiMesh*>& meshes, void BuildSkybox(std::vector<aiMesh *> &meshes,
std::vector<aiMaterial*> materials); std::vector<aiMaterial *> materials);
// ------------------------------------------------------------------- // -------------------------------------------------------------------
/** Copy a material for a mesh to the output material list /** Copy a material for a mesh to the output material list
@ -256,10 +259,10 @@ private:
* @param defMatIdx Default material index - UINT_MAX if not present * @param defMatIdx Default material index - UINT_MAX if not present
* @param mesh Mesh to work on * @param mesh Mesh to work on
*/ */
void CopyMaterial(std::vector<aiMaterial*>& materials, void CopyMaterial(std::vector<aiMaterial *> &materials,
std::vector< std::pair<aiMaterial*, unsigned int> >& inmaterials, std::vector<std::pair<aiMaterial *, unsigned int>> &inmaterials,
unsigned int& defMatIdx, unsigned int &defMatIdx,
aiMesh* mesh); aiMesh *mesh);
// ------------------------------------------------------------------- // -------------------------------------------------------------------
/** Compute animations for a specific node /** Compute animations for a specific node
@ -267,8 +270,8 @@ private:
* @param root Node to be processed * @param root Node to be processed
* @param anims The list of output animations * @param anims The list of output animations
*/ */
void ComputeAnimations(Node* root, aiNode* real, void ComputeAnimations(Node *root, aiNode *real,
std::vector<aiNodeAnim*>& anims); std::vector<aiNodeAnim *> &anims);
private: private:
/// Configuration option: desired output FPS /// Configuration option: desired output FPS
@ -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

@ -57,16 +57,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using namespace Assimp; using namespace Assimp;
static const aiImporterDesc desc = { static const aiImporterDesc desc = {
"Irrlicht Mesh Reader", "Irrlicht Mesh Reader",
"", "",
"", "",
"http://irrlicht.sourceforge.net/", "http://irrlicht.sourceforge.net/",
aiImporterFlags_SupportTextFlavour, aiImporterFlags_SupportTextFlavour,
0, 0,
0, 0,
0, 0,
0, 0,
"xml irrmesh" "xml irrmesh"
}; };
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -80,419 +80,443 @@ IRRMeshImporter::~IRRMeshImporter() = 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 IRRMeshImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const { bool IRRMeshImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const {
/* NOTE: A simple check for the file extension is not enough /* NOTE: A simple check for the file extension is not enough
* here. Irrmesh and irr are easy, but xml is too generic * here. Irrmesh and irr are easy, but xml is too generic
* and could be collada, too. So we need to open the file and * and could be collada, too. So we need to open the file and
* search for typical tokens. * search for typical tokens.
*/ */
static const char *tokens[] = { "irrmesh" }; static const char *tokens[] = { "irrmesh" };
return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens)); return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Get a list of all file extensions which are handled by this class // Get a list of all file extensions which are handled by this class
const aiImporterDesc *IRRMeshImporter::GetInfo() const { const aiImporterDesc *IRRMeshImporter::GetInfo() const {
return &desc; return &desc;
} }
static void releaseMaterial(aiMaterial **mat) { static void releaseMaterial(aiMaterial **mat) {
if (*mat != nullptr) { if (*mat != nullptr) {
delete *mat; delete *mat;
*mat = nullptr; *mat = nullptr;
} }
} }
static void releaseMesh(aiMesh **mesh) { static void releaseMesh(aiMesh **mesh) {
if (*mesh != nullptr) { if (*mesh != nullptr) {
delete *mesh; delete *mesh;
*mesh = nullptr; *mesh = nullptr;
} }
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Imports the given file into the given scene structure. // Imports the given file into the given scene structure.
void IRRMeshImporter::InternReadFile(const std::string &pFile, void IRRMeshImporter::InternReadFile(const std::string &pFile,
aiScene *pScene, IOSystem *pIOHandler) { aiScene *pScene, IOSystem *pIOHandler) {
std::unique_ptr<IOStream> file(pIOHandler->Open(pFile)); std::unique_ptr<IOStream> file(pIOHandler->Open(pFile));
// Check whether we can read from the file // Check whether we can read from the file
if (file == nullptr) if (file == nullptr)
throw DeadlyImportError("Failed to open IRRMESH file ", pFile); throw DeadlyImportError("Failed to open IRRMESH file ", pFile);
// Construct the irrXML parser // Construct the irrXML parser
XmlParser parser; XmlParser parser;
if (!parser.parse( file.get() )) { if (!parser.parse(file.get())) {
throw DeadlyImportError("XML parse error while loading IRRMESH file ", pFile); throw DeadlyImportError("XML parse error while loading IRRMESH file ", pFile);
} }
XmlNode root = parser.getRootNode(); XmlNode root = parser.getRootNode();
// final data // final data
std::vector<aiMaterial *> materials; std::vector<aiMaterial *> materials;
std::vector<aiMesh *> meshes; std::vector<aiMesh *> meshes;
materials.reserve(5); materials.reserve(5);
meshes.reserve(5); meshes.reserve(5);
// temporary data - current mesh buffer // temporary data - current mesh buffer
aiMaterial *curMat = nullptr; // TODO move all these to inside loop
aiMesh *curMesh = nullptr; aiMaterial *curMat = nullptr;
unsigned int curMatFlags = 0; aiMesh *curMesh = nullptr;
unsigned int curMatFlags = 0;
std::vector<aiVector3D> curVertices, curNormals, curTangents, curBitangents; std::vector<aiVector3D> curVertices, curNormals, curTangents, curBitangents;
std::vector<aiColor4D> curColors; std::vector<aiColor4D> curColors;
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
bool useColors = false; // int vertexFormat = 0; // 0 = normal; 1 = 2 tcoords, 2 = tangents
bool useColors = false;
// Parse the XML file /*
for (pugi::xml_node child : root.children()) { ** irrmesh files have a top level <mesh> owning multiple <buffer> nodes.
if (child.type() == pugi::node_element) { ** Each <buffer> contains <material>, <vertices>, and <indices>
if (!ASSIMP_stricmp(child.name(), "buffer") && (curMat || curMesh)) { ** <material> tags here directly owns the material data specs
// end of previous buffer. A material and a mesh should be there ** <vertices> are a vertex per line, contains position, UV1 coords, maybe UV2, normal, tangent, bitangent
if (!curMat || !curMesh) { ** <boundingbox> is ignored, I think assimp recalculates those?
ASSIMP_LOG_ERROR("IRRMESH: A buffer must contain a mesh and a material"); */
releaseMaterial(&curMat);
releaseMesh(&curMesh);
} else {
materials.push_back(curMat);
meshes.push_back(curMesh);
}
curMat = nullptr;
curMesh = nullptr;
curVertices.clear(); // Parse the XML file
curColors.clear(); pugi::xml_node const &meshNode = root.child("mesh");
curNormals.clear(); for (pugi::xml_node bufferNode : meshNode.children()) {
curUV2s.clear(); if (ASSIMP_stricmp(bufferNode.name(), "buffer")) {
curUVs.clear(); // Might be a useless warning
curTangents.clear(); ASSIMP_LOG_WARN("IRRMESH: Ignoring non buffer node <", bufferNode.name(), "> in mesh declaration");
curBitangents.clear(); continue;
} }
if (!ASSIMP_stricmp(child.name(), "material")) { curMat = nullptr;
if (curMat) { curMesh = nullptr;
ASSIMP_LOG_WARN("IRRMESH: Only one material description per buffer, please");
releaseMaterial(&curMat);
}
curMat = ParseMaterial(curMatFlags);
}
/* 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) { curVertices.clear();
// This is possible ... remove the mesh from the list and skip further reading curColors.clear();
ASSIMP_LOG_WARN("IRRMESH: Found mesh with zero vertices"); curNormals.clear();
curUV2s.clear();
curUVs.clear();
curTangents.clear();
curBitangents.clear();
releaseMaterial(&curMat); // TODO ensure all three nodes are present and populated
releaseMesh(&curMesh); // before allocating everything
textMeaning = 0;
continue;
}
curVertices.reserve(num); // Get first material node
curNormals.reserve(num); pugi::xml_node materialNode = bufferNode.child("material");
curColors.reserve(num); if (materialNode) {
curUVs.reserve(num); 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");
}
} else {
ASSIMP_LOG_ERROR("IRRMESH: Buffer must contain one material");
continue;
}
// Determine the file format // Get first vertices node
//const char *t = reader->getAttributeValueSafe("type"); pugi::xml_node verticesNode = bufferNode.child("vertices");
pugi::xml_attribute t = child.attribute("type"); if (verticesNode) {
if (!ASSIMP_stricmp("2tcoords", t.name())) { pugi::xml_attribute vertexCountAttrib = verticesNode.attribute("vertexCount");
curUV2s.reserve(num); int vertexCount = vertexCountAttrib.as_int();
vertexFormat = 1; 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
};
if (curMatFlags & AI_IRRMESH_EXTRA_2ND_TEXTURE) { curVertices.reserve(vertexCount);
// ********************************************************* curNormals.reserve(vertexCount);
// We have a second texture! So use this UV channel curColors.reserve(vertexCount);
// for it. The 2nd texture can be either a normal curUVs.reserve(vertexCount);
// texture (solid_2layer or lightmap_xxx) or a normal
// map (normal_..., parallax_...)
// *********************************************************
int idx = 1;
aiMaterial *mat = (aiMaterial *)curMat;
if (curMatFlags & AI_IRRMESH_MAT_lightmap) { VertexFormat vertexFormat;
mat->AddProperty(&idx, 1, AI_MATKEY_UVWSRC_LIGHTMAP(0)); // Determine the file format
} else if (curMatFlags & AI_IRRMESH_MAT_normalmap_solid) { pugi::xml_attribute typeAttrib = verticesNode.attribute("type");
mat->AddProperty(&idx, 1, AI_MATKEY_UVWSRC_NORMALS(0)); if (!ASSIMP_stricmp("2tcoords", typeAttrib.value())) {
} else if (curMatFlags & AI_IRRMESH_MAT_solid_2layer) { curUV2s.reserve(vertexCount);
mat->AddProperty(&idx, 1, AI_MATKEY_UVWSRC_DIFFUSE(1)); vertexFormat = VertexFormat::t2coord;
} if (curMatFlags & AI_IRRMESH_EXTRA_2ND_TEXTURE) {
} // *********************************************************
} else if (!ASSIMP_stricmp("tangents", t.name())) { // We have a second texture! So use this UV channel
curTangents.reserve(num); // for it. The 2nd texture can be either a normal
curBitangents.reserve(num); // texture (solid_2layer or lightmap_xxx) or a normal
vertexFormat = 2; // map (normal_..., parallax_...)
} else if (ASSIMP_stricmp("standard", t.name())) { // *********************************************************
releaseMaterial(&curMat); int idx = 1;
ASSIMP_LOG_WARN("IRRMESH: Unknown vertex format"); aiMaterial *mat = (aiMaterial *)curMat;
} else
vertexFormat = 0;
textMeaning = 1;
} else if (!ASSIMP_stricmp(child.name(), "indices")) {
if (curVertices.empty() && curMat) {
releaseMaterial(&curMat);
throw DeadlyImportError("IRRMESH: indices must come after vertices");
}
textMeaning = 2; if (curMatFlags & AI_IRRMESH_MAT_lightmap) {
mat->AddProperty(&idx, 1, AI_MATKEY_UVWSRC_LIGHTMAP(0));
} else if (curMatFlags & AI_IRRMESH_MAT_normalmap_solid) {
mat->AddProperty(&idx, 1, AI_MATKEY_UVWSRC_NORMALS(0));
} else if (curMatFlags & AI_IRRMESH_MAT_solid_2layer) {
mat->AddProperty(&idx, 1, AI_MATKEY_UVWSRC_DIFFUSE(1));
}
}
} else if (!ASSIMP_stricmp("tangents", typeAttrib.value())) {
curTangents.reserve(vertexCount);
curBitangents.reserve(vertexCount);
vertexFormat = VertexFormat::tangent;
} 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);
ASSIMP_LOG_ERROR("IRRMESH: Unknown vertex format");
continue; // Skip rest of buffer
};
// start a new mesh // We know what format buffer is, collect numbers
curMesh = new aiMesh(); ParseBufferVertices(verticesNode.text().get(), vertexFormat,
curVertices, curNormals,
curTangents, curBitangents,
curUVs, curUV2s, curColors, useColors);
}
// allocate storage for all faces // Get indices
pugi::xml_attribute attr = child.attribute("indexCount"); // At this point we have some vertices and a valid material
curMesh->mNumVertices = attr.as_int(); // Collect indices and create aiMesh at the same time
if (!curMesh->mNumVertices) { pugi::xml_node indicesNode = bufferNode.child("indices");
// This is possible ... remove the mesh from the list and skip further reading if (indicesNode) {
ASSIMP_LOG_WARN("IRRMESH: Found mesh with zero indices"); // start a new mesh
curMesh = new aiMesh();
// mesh - away // allocate storage for all faces
releaseMesh(&curMesh); pugi::xml_attribute attr = indicesNode.attribute("indexCount");
curMesh->mNumVertices = attr.as_int();
if (!curMesh->mNumVertices) {
// This is possible ... remove the mesh from the list and skip further reading
ASSIMP_LOG_WARN("IRRMESH: Found mesh with zero indices");
// material - away // mesh - away
releaseMaterial(&curMat); releaseMesh(&curMesh);
textMeaning = 0; // material - away
continue; releaseMaterial(&curMat);
} continue; // Go to next buffer
}
if (curMesh->mNumVertices % 3) { if (curMesh->mNumVertices % 3) {
ASSIMP_LOG_WARN("IRRMESH: Number if indices isn't divisible by 3"); ASSIMP_LOG_WARN("IRRMESH: Number if indices isn't divisible by 3");
} }
curMesh->mNumFaces = curMesh->mNumVertices / 3; curMesh->mNumFaces = curMesh->mNumVertices / 3;
curMesh->mFaces = new aiFace[curMesh->mNumFaces]; curMesh->mFaces = new aiFace[curMesh->mNumFaces];
// setup some members // setup some members
curMesh->mMaterialIndex = (unsigned int)materials.size(); curMesh->mMaterialIndex = (unsigned int)materials.size();
curMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; curMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
// allocate storage for all vertices // allocate storage for all vertices
curMesh->mVertices = new aiVector3D[curMesh->mNumVertices]; curMesh->mVertices = new aiVector3D[curMesh->mNumVertices];
if (curNormals.size() == curVertices.size()) { if (curNormals.size() == curVertices.size()) {
curMesh->mNormals = new aiVector3D[curMesh->mNumVertices]; curMesh->mNormals = new aiVector3D[curMesh->mNumVertices];
} }
if (curTangents.size() == curVertices.size()) { if (curTangents.size() == curVertices.size()) {
curMesh->mTangents = new aiVector3D[curMesh->mNumVertices]; curMesh->mTangents = new aiVector3D[curMesh->mNumVertices];
} }
if (curBitangents.size() == curVertices.size()) { if (curBitangents.size() == curVertices.size()) {
curMesh->mBitangents = new aiVector3D[curMesh->mNumVertices]; curMesh->mBitangents = new aiVector3D[curMesh->mNumVertices];
} }
if (curColors.size() == curVertices.size() && useColors) { if (curColors.size() == curVertices.size() && useColors) {
curMesh->mColors[0] = new aiColor4D[curMesh->mNumVertices]; curMesh->mColors[0] = new aiColor4D[curMesh->mNumVertices];
} }
if (curUVs.size() == curVertices.size()) { if (curUVs.size() == curVertices.size()) {
curMesh->mTextureCoords[0] = new aiVector3D[curMesh->mNumVertices]; curMesh->mTextureCoords[0] = new aiVector3D[curMesh->mNumVertices];
} }
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: { // read indices
const char *sz = child.child_value(); aiFace *curFace = curMesh->mFaces;
if (textMeaning == 1) { aiFace *const faceEnd = curMesh->mFaces + curMesh->mNumFaces;
textMeaning = 0;
// read vertices aiVector3D *pcV = curMesh->mVertices;
do { aiVector3D *pcN = curMesh->mNormals;
SkipSpacesAndLineEnd(&sz); aiVector3D *pcT = curMesh->mTangents;
aiVector3D temp; aiVector3D *pcB = curMesh->mBitangents;
aiColor4D c; aiColor4D *pcC0 = curMesh->mColors[0];
aiVector3D *pcT0 = curMesh->mTextureCoords[0];
aiVector3D *pcT1 = curMesh->mTextureCoords[1];
// Read the vertex position unsigned int curIdx = 0;
sz = fast_atoreal_move<float>(sz, (float &)temp.x); unsigned int total = 0;
SkipSpaces(&sz);
sz = fast_atoreal_move<float>(sz, (float &)temp.y); // NOTE this might explode for UTF-16 and wchars
SkipSpaces(&sz); const char *sz = indicesNode.text().get();
// For each index loop over aiMesh faces
while (SkipSpacesAndLineEnd(&sz)) {
if (curFace >= faceEnd) {
ASSIMP_LOG_ERROR("IRRMESH: Too many indices");
break;
}
// if new face
if (!curIdx) {
curFace->mNumIndices = 3;
curFace->mIndices = new unsigned int[3];
}
sz = fast_atoreal_move<float>(sz, (float &)temp.z); // Read index base 10
SkipSpaces(&sz); // function advances the pointer
curVertices.push_back(temp); unsigned int idx = strtoul10(sz, &sz);
if (idx >= curVertices.size()) {
ASSIMP_LOG_ERROR("IRRMESH: Index out of range");
idx = 0;
}
// Read the vertex normals // make up our own indices?
sz = fast_atoreal_move<float>(sz, (float &)temp.x); curFace->mIndices[curIdx] = total++;
SkipSpaces(&sz);
sz = fast_atoreal_move<float>(sz, (float &)temp.y); // Copy over data to aiMesh
SkipSpaces(&sz); *pcV++ = curVertices[idx];
if (pcN) *pcN++ = curNormals[idx];
if (pcT) *pcT++ = curTangents[idx];
if (pcB) *pcB++ = curBitangents[idx];
if (pcC0) *pcC0++ = curColors[idx];
if (pcT0) *pcT0++ = curUVs[idx];
if (pcT1) *pcT1++ = curUV2s[idx];
sz = fast_atoreal_move<float>(sz, (float &)temp.z); // start new face
SkipSpaces(&sz); if (++curIdx == 3) {
curNormals.push_back(temp); ++curFace;
curIdx = 0;
}
}
// We should be at the end of mFaces
if (curFace != faceEnd)
ASSIMP_LOG_ERROR("IRRMESH: Not enough indices");
}
// read the vertex colors // Finish processing the mesh - do some small material workarounds
uint32_t clr = strtoul16(sz, &sz); if (curMatFlags & AI_IRRMESH_MAT_trans_vertex_alpha && !useColors) {
ColorFromARGBPacked(clr, c); // Take the opacity value of the current material
// from the common vertex color alpha
aiMaterial *mat = (aiMaterial *)curMat;
mat->AddProperty(&curColors[0].a, 1, AI_MATKEY_OPACITY);
}
// textMeaning = 2;
if (!curColors.empty() && c != *(curColors.end() - 1)) // end of previous buffer. A material and a mesh should be there
useColors = true; if (!curMat || !curMesh) {
ASSIMP_LOG_ERROR("IRRMESH: A buffer must contain a mesh and a material");
releaseMaterial(&curMat);
releaseMesh(&curMesh);
} else {
materials.push_back(curMat);
meshes.push_back(curMesh);
}
}
curColors.push_back(c); // If one is empty then so is the other
SkipSpaces(&sz); if (materials.empty() || meshes.empty()) {
throw DeadlyImportError("IRRMESH: Unable to read a mesh from this file");
}
// read the first UV coordinate set // now generate the output scene
sz = fast_atoreal_move<float>(sz, (float &)temp.x); pScene->mNumMeshes = (unsigned int)meshes.size();
SkipSpaces(&sz); pScene->mMeshes = new aiMesh *[pScene->mNumMeshes];
for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
pScene->mMeshes[i] = meshes[i];
sz = fast_atoreal_move<float>(sz, (float &)temp.y); // clean this value ...
SkipSpaces(&sz); pScene->mMeshes[i]->mNumUVComponents[3] = 0;
temp.z = 0.f; }
temp.y = 1.f - temp.y; // DX to OGL
curUVs.push_back(temp);
// read the (optional) second UV coordinate set pScene->mNumMaterials = (unsigned int)materials.size();
if (vertexFormat == 1) { pScene->mMaterials = new aiMaterial *[pScene->mNumMaterials];
sz = fast_atoreal_move<float>(sz, (float &)temp.x); ::memcpy(pScene->mMaterials, &materials[0], sizeof(void *) * pScene->mNumMaterials);
SkipSpaces(&sz);
sz = fast_atoreal_move<float>(sz, (float &)temp.y); pScene->mRootNode = new aiNode();
temp.y = 1.f - temp.y; // DX to OGL pScene->mRootNode->mName.Set("<IRRMesh>");
curUV2s.push_back(temp); pScene->mRootNode->mNumMeshes = pScene->mNumMeshes;
} pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes];
// 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); for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
SkipSpaces(&sz); pScene->mRootNode->mMeshes[i] = i;
};
}
sz = fast_atoreal_move<float>(sz, (float &)temp.y); void IRRMeshImporter::ParseBufferVertices(const char *sz, VertexFormat vertexFormat,
SkipSpaces(&sz); std::vector<aiVector3D> &vertices, std::vector<aiVector3D> &normals,
temp.y *= -1.0f; std::vector<aiVector3D> &tangents, std::vector<aiVector3D> &bitangents,
curTangents.push_back(temp); std::vector<aiVector3D> &UVs, std::vector<aiVector3D> &UV2s,
std::vector<aiColor4D> &colors, bool &useColors) {
// read vertices
do {
SkipSpacesAndLineEnd(&sz);
aiVector3D temp;
aiColor4D c;
// bitangents // Read the vertex position
sz = fast_atoreal_move<float>(sz, (float &)temp.x); sz = fast_atoreal_move<float>(sz, (float &)temp.x);
SkipSpaces(&sz); SkipSpaces(&sz);
sz = fast_atoreal_move<float>(sz, (float &)temp.z); sz = fast_atoreal_move<float>(sz, (float &)temp.y);
SkipSpaces(&sz); SkipSpaces(&sz);
sz = fast_atoreal_move<float>(sz, (float &)temp.y); sz = fast_atoreal_move<float>(sz, (float &)temp.z);
SkipSpaces(&sz); SkipSpaces(&sz);
temp.y *= -1.0f; vertices.push_back(temp);
curBitangents.push_back(temp);
}
}
/* IMPORTANT: We assume that each vertex is specified in one // Read the vertex normals
line. So we can skip the rest of the line - unknown vertex sz = fast_atoreal_move<float>(sz, (float &)temp.x);
elements are ignored. SkipSpaces(&sz);
*/
while (SkipLine(&sz)); sz = fast_atoreal_move<float>(sz, (float &)temp.y);
} else if (textMeaning == 2) { SkipSpaces(&sz);
textMeaning = 0;
// read indices sz = fast_atoreal_move<float>(sz, (float &)temp.z);
aiFace *curFace = curMesh->mFaces; SkipSpaces(&sz);
aiFace *const faceEnd = curMesh->mFaces + curMesh->mNumFaces; normals.push_back(temp);
aiVector3D *pcV = curMesh->mVertices; // read the vertex colors
aiVector3D *pcN = curMesh->mNormals; uint32_t clr = strtoul16(sz, &sz);
aiVector3D *pcT = curMesh->mTangents; ColorFromARGBPacked(clr, c);
aiVector3D *pcB = curMesh->mBitangents;
aiColor4D *pcC0 = curMesh->mColors[0];
aiVector3D *pcT0 = curMesh->mTextureCoords[0];
aiVector3D *pcT1 = curMesh->mTextureCoords[1];
unsigned int curIdx = 0; // If we're pushing more than one distinct color
unsigned int total = 0; if (!colors.empty() && c != *(colors.end() - 1))
while (SkipSpacesAndLineEnd(&sz)) { useColors = true;
if (curFace >= faceEnd) {
ASSIMP_LOG_ERROR("IRRMESH: Too many indices");
break;
}
if (!curIdx) {
curFace->mNumIndices = 3;
curFace->mIndices = new unsigned int[3];
}
unsigned int idx = strtoul10(sz, &sz); colors.push_back(c);
if (idx >= curVertices.size()) { SkipSpaces(&sz);
ASSIMP_LOG_ERROR("IRRMESH: Index out of range");
idx = 0;
}
curFace->mIndices[curIdx] = total++; // read the first UV coordinate set
sz = fast_atoreal_move<float>(sz, (float &)temp.x);
SkipSpaces(&sz);
*pcV++ = curVertices[idx]; sz = fast_atoreal_move<float>(sz, (float &)temp.y);
if (pcN) *pcN++ = curNormals[idx]; SkipSpaces(&sz);
if (pcT) *pcT++ = curTangents[idx]; temp.z = 0.f;
if (pcB) *pcB++ = curBitangents[idx]; temp.y = 1.f - temp.y; // DX to OGL
if (pcC0) *pcC0++ = curColors[idx]; UVs.push_back(temp);
if (pcT0) *pcT0++ = curUVs[idx];
if (pcT1) *pcT1++ = curUV2s[idx];
if (++curIdx == 3) { // NOTE these correspond to specific S3DVertex* structs in irr sourcecode
++curFace; // So by definition, all buffers have either UV2 or tangents or neither
curIdx = 0; // read the (optional) second UV coordinate set
} if (vertexFormat == VertexFormat::t2coord) {
} sz = fast_atoreal_move<float>(sz, (float &)temp.x);
SkipSpaces(&sz);
if (curFace != faceEnd) sz = fast_atoreal_move<float>(sz, (float &)temp.y);
ASSIMP_LOG_ERROR("IRRMESH: Not enough indices"); 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);
// Finish processing the mesh - do some small material workarounds sz = fast_atoreal_move<float>(sz, (float &)temp.z);
if (curMatFlags & AI_IRRMESH_MAT_trans_vertex_alpha && !useColors) { SkipSpaces(&sz);
// Take the opacity value of the current material
// from the common vertex color alpha
aiMaterial *mat = (aiMaterial *)curMat;
mat->AddProperty(&curColors[0].a, 1, AI_MATKEY_OPACITY);
}
}
}
}
// End of the last buffer. A material and a mesh should be there sz = fast_atoreal_move<float>(sz, (float &)temp.y);
if (curMat || curMesh) { SkipSpaces(&sz);
if (!curMat || !curMesh) { temp.y *= -1.0f;
ASSIMP_LOG_ERROR("IRRMESH: A buffer must contain a mesh and a material"); tangents.push_back(temp);
releaseMaterial(&curMat);
releaseMesh(&curMesh);
} else {
materials.push_back(curMat);
meshes.push_back(curMesh);
}
}
if (materials.empty()) { // bitangents
throw DeadlyImportError("IRRMESH: Unable to read a mesh from this file"); sz = fast_atoreal_move<float>(sz, (float &)temp.x);
} SkipSpaces(&sz);
// now generate the output scene sz = fast_atoreal_move<float>(sz, (float &)temp.z);
pScene->mNumMeshes = (unsigned int)meshes.size(); SkipSpaces(&sz);
pScene->mMeshes = new aiMesh *[pScene->mNumMeshes];
for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
pScene->mMeshes[i] = meshes[i];
// clean this value ... sz = fast_atoreal_move<float>(sz, (float &)temp.y);
pScene->mMeshes[i]->mNumUVComponents[3] = 0; SkipSpaces(&sz);
} temp.y *= -1.0f;
bitangents.push_back(temp);
pScene->mNumMaterials = (unsigned int)materials.size(); }
pScene->mMaterials = new aiMaterial *[pScene->mNumMaterials]; } while (SkipLine(&sz));
::memcpy(pScene->mMaterials, &materials[0], sizeof(void *) * pScene->mNumMaterials); /* IMPORTANT: We assume that each vertex is specified in one
line. So we can skip the rest of the line - unknown vertex
pScene->mRootNode = new aiNode(); elements are ignored.
pScene->mRootNode->mName.Set("<IRRMesh>"); */
pScene->mRootNode->mNumMeshes = pScene->mNumMeshes;
pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes];
for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
pScene->mRootNode->mMeshes[i] = i;
}
} }
#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

@ -43,302 +43,302 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* @brief Shared utilities for the IRR and IRRMESH loaders * @brief Shared utilities for the IRR and IRRMESH loaders
*/ */
//This section should be excluded only if both the Irrlicht AND the Irrlicht Mesh importers were omitted. // This section should be excluded only if both the Irrlicht AND the Irrlicht Mesh importers were omitted.
#if !(defined(ASSIMP_BUILD_NO_IRR_IMPORTER) && defined(ASSIMP_BUILD_NO_IRRMESH_IMPORTER)) #if !(defined(ASSIMP_BUILD_NO_IRR_IMPORTER) && defined(ASSIMP_BUILD_NO_IRRMESH_IMPORTER))
#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;
// Transformation matrix to convert from Assimp to IRR space // Transformation matrix to convert from Assimp to IRR space
const aiMatrix4x4 Assimp::AI_TO_IRR_MATRIX = aiMatrix4x4 ( const aiMatrix4x4 Assimp::AI_TO_IRR_MATRIX = aiMatrix4x4(
1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f); 0.0f, 0.0f, 0.0f, 1.0f);
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// 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")) {
// simple copy the string // simple copy the string
out.value = std::string(attrib.value()); out.value = std::string(attrib.value());
} }
} }
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// 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")) {
// true or false, case insensitive // true or false, case insensitive
out.value = (ASSIMP_stricmp(attrib.value(), "true") ? false : true); out.value = (ASSIMP_stricmp(attrib.value(), "true") ? false : true);
} }
} }
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// 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")) {
// just parse the float // just parse the float
out.value = fast_atof(attrib.value()); out.value = fast_atof(attrib.value());
} }
} }
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// 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")) {
// three floats, separated with commas // three floats, separated with commas
const char *ptr = attrib.value(); const char *ptr = attrib.value();
SkipSpaces(&ptr); SkipSpaces(&ptr);
ptr = fast_atoreal_move<float>( ptr,(float&)out.value.x ); ptr = fast_atoreal_move<float>(ptr, (float &)out.value.x);
SkipSpaces(&ptr); SkipSpaces(&ptr);
if (',' != *ptr) { if (',' != *ptr) {
ASSIMP_LOG_ERROR("IRR(MESH): Expected comma in vector definition"); ASSIMP_LOG_ERROR("IRR(MESH): Expected comma in vector definition");
} else { } else {
SkipSpaces(ptr + 1, &ptr); SkipSpaces(ptr + 1, &ptr);
} }
ptr = fast_atoreal_move<float>( ptr,(float&)out.value.y ); ptr = fast_atoreal_move<float>(ptr, (float &)out.value.y);
SkipSpaces(&ptr); SkipSpaces(&ptr);
if (',' != *ptr) { if (',' != *ptr) {
ASSIMP_LOG_ERROR("IRR(MESH): Expected comma in vector definition"); ASSIMP_LOG_ERROR("IRR(MESH): Expected comma in vector definition");
} else { } else {
SkipSpaces(ptr + 1, &ptr); SkipSpaces(ptr + 1, &ptr);
} }
ptr = fast_atoreal_move<float>( ptr,(float&)out.value.z ); ptr = fast_atoreal_move<float>(ptr, (float &)out.value.z);
} }
} }
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Convert a string to a proper aiMappingMode // Convert a string to a proper aiMappingMode
int ConvertMappingMode(const std::string& mode) { int ConvertMappingMode(const std::string &mode) {
if (mode == "texture_clamp_repeat") { if (mode == "texture_clamp_repeat") {
return aiTextureMapMode_Wrap; return aiTextureMapMode_Wrap;
} else if (mode == "texture_clamp_mirror") { } else if (mode == "texture_clamp_mirror") {
return aiTextureMapMode_Mirror; return aiTextureMapMode_Mirror;
} }
return aiTextureMapMode_Clamp; return aiTextureMapMode_Clamp;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// 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;
matFlags = 0; // zero output flags matFlags = 0; // zero output flags
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);
} else if (prop.name == "Ambient") { } else if (prop.name == "Ambient") {
ColorFromARGBPacked(prop.value, clr); ColorFromARGBPacked(prop.value, clr);
mat->AddProperty(&clr, 1, AI_MATKEY_COLOR_AMBIENT); mat->AddProperty(&clr, 1, AI_MATKEY_COLOR_AMBIENT);
} else if (prop.name == "Specular") { } else if (prop.name == "Specular") {
ColorFromARGBPacked(prop.value, clr); ColorFromARGBPacked(prop.value, clr);
mat->AddProperty(&clr, 1, AI_MATKEY_COLOR_SPECULAR); mat->AddProperty(&clr, 1, AI_MATKEY_COLOR_SPECULAR);
} }
// NOTE: The 'emissive' property causes problems. It is // NOTE: The 'emissive' property causes problems. It is
// often != 0, even if there is obviously no light // often != 0, even if there is obviously no light
// emitted by the described surface. In fact I think // emitted by the described surface. In fact I think
// IRRLICHT ignores this property, too. // IRRLICHT ignores this property, too.
#if 0 #if 0
else if (prop.name == "Emissive") { else if (prop.name == "Emissive") {
ColorFromARGBPacked(prop.value,clr); ColorFromARGBPacked(prop.value,clr);
mat->AddProperty(&clr,1,AI_MATKEY_COLOR_EMISSIVE); mat->AddProperty(&clr,1,AI_MATKEY_COLOR_EMISSIVE);
} }
#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);
} else if (prop.name == "GouraudShading") { } else if (prop.name == "GouraudShading") {
int val = (prop.value ? aiShadingMode_Gouraud : aiShadingMode_NoShading); int val = (prop.value ? aiShadingMode_Gouraud : aiShadingMode_NoShading);
mat->AddProperty(&val, 1, AI_MATKEY_SHADING_MODEL); mat->AddProperty(&val, 1, AI_MATKEY_SHADING_MODEL);
} else if (prop.name == "BackfaceCulling") { } else if (prop.name == "BackfaceCulling") {
int val = (!prop.value); int val = (!prop.value);
mat->AddProperty(&val, 1, AI_MATKEY_TWOSIDED); mat->AddProperty(&val, 1, AI_MATKEY_TWOSIDED);
} }
} 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") {
if (prop.value == "solid") { if (prop.value == "solid") {
// default material ... // default material ...
} else if (prop.value == "trans_vertex_alpha") { } else if (prop.value == "trans_vertex_alpha") {
matFlags = AI_IRRMESH_MAT_trans_vertex_alpha; matFlags = AI_IRRMESH_MAT_trans_vertex_alpha;
} else if (prop.value == "lightmap") { } else if (prop.value == "lightmap") {
matFlags = AI_IRRMESH_MAT_lightmap; matFlags = AI_IRRMESH_MAT_lightmap;
} else if (prop.value == "solid_2layer") { } else if (prop.value == "solid_2layer") {
matFlags = AI_IRRMESH_MAT_solid_2layer; matFlags = AI_IRRMESH_MAT_solid_2layer;
} else if (prop.value == "lightmap_m2") { } else if (prop.value == "lightmap_m2") {
matFlags = AI_IRRMESH_MAT_lightmap_m2; matFlags = AI_IRRMESH_MAT_lightmap_m2;
} else if (prop.value == "lightmap_m4") { } else if (prop.value == "lightmap_m4") {
matFlags = AI_IRRMESH_MAT_lightmap_m4; matFlags = AI_IRRMESH_MAT_lightmap_m4;
} else if (prop.value == "lightmap_light") { } else if (prop.value == "lightmap_light") {
matFlags = AI_IRRMESH_MAT_lightmap_light; matFlags = AI_IRRMESH_MAT_lightmap_light;
} else if (prop.value == "lightmap_light_m2") { } else if (prop.value == "lightmap_light_m2") {
matFlags = AI_IRRMESH_MAT_lightmap_light_m2; matFlags = AI_IRRMESH_MAT_lightmap_light_m2;
} else if (prop.value == "lightmap_light_m4") { } else if (prop.value == "lightmap_light_m4") {
matFlags = AI_IRRMESH_MAT_lightmap_light_m4; matFlags = AI_IRRMESH_MAT_lightmap_light_m4;
} else if (prop.value == "lightmap_add") { } else if (prop.value == "lightmap_add") {
matFlags = AI_IRRMESH_MAT_lightmap_add; matFlags = AI_IRRMESH_MAT_lightmap_add;
} else if (prop.value == "normalmap_solid" || } else if (prop.value == "normalmap_solid" ||
prop.value == "parallaxmap_solid") { // Normal and parallax maps are treated equally prop.value == "parallaxmap_solid") { // Normal and parallax maps are treated equally
matFlags = AI_IRRMESH_MAT_normalmap_solid; matFlags = AI_IRRMESH_MAT_normalmap_solid;
} else if (prop.value == "normalmap_trans_vertex_alpha" || } else if (prop.value == "normalmap_trans_vertex_alpha" ||
prop.value == "parallaxmap_trans_vertex_alpha") { prop.value == "parallaxmap_trans_vertex_alpha") {
matFlags = AI_IRRMESH_MAT_normalmap_tva; matFlags = AI_IRRMESH_MAT_normalmap_tva;
} else if (prop.value == "normalmap_trans_add" || } else if (prop.value == "normalmap_trans_add" ||
prop.value == "parallaxmap_trans_add") { prop.value == "parallaxmap_trans_add") {
matFlags = AI_IRRMESH_MAT_normalmap_ta; matFlags = AI_IRRMESH_MAT_normalmap_ta;
} else { } else {
ASSIMP_LOG_WARN("IRRMat: Unrecognized material type: ", prop.value); ASSIMP_LOG_WARN("IRRMat: Unrecognized material type: ", prop.value);
} }
} }
// Up to 4 texture channels are supported // Up to 4 texture channels are supported
if (prop.name == "Texture1") { if (prop.name == "Texture1") {
// Always accept the primary texture channel // Always accept the primary texture channel
++cnt; ++cnt;
s.Set(prop.value); s.Set(prop.value);
mat->AddProperty(&s, AI_MATKEY_TEXTURE_DIFFUSE(0)); mat->AddProperty(&s, AI_MATKEY_TEXTURE_DIFFUSE(0));
} else if (prop.name == "Texture2" && cnt == 1) { } else if (prop.name == "Texture2" && cnt == 1) {
// 2-layer material lightmapped? // 2-layer material lightmapped?
if (matFlags & AI_IRRMESH_MAT_lightmap) { if (matFlags & AI_IRRMESH_MAT_lightmap) {
++cnt; ++cnt;
s.Set(prop.value); s.Set(prop.value);
mat->AddProperty(&s, AI_MATKEY_TEXTURE_LIGHTMAP(0)); mat->AddProperty(&s, AI_MATKEY_TEXTURE_LIGHTMAP(0));
// set the corresponding material flag // set the corresponding material flag
matFlags |= AI_IRRMESH_EXTRA_2ND_TEXTURE; matFlags |= AI_IRRMESH_EXTRA_2ND_TEXTURE;
} else if (matFlags & AI_IRRMESH_MAT_normalmap_solid) { // alternatively: normal or parallax mapping } else if (matFlags & AI_IRRMESH_MAT_normalmap_solid) { // alternatively: normal or parallax mapping
++cnt; ++cnt;
s.Set(prop.value); s.Set(prop.value);
mat->AddProperty(&s, AI_MATKEY_TEXTURE_NORMALS(0)); mat->AddProperty(&s, AI_MATKEY_TEXTURE_NORMALS(0));
// set the corresponding material flag // set the corresponding material flag
matFlags |= AI_IRRMESH_EXTRA_2ND_TEXTURE; matFlags |= AI_IRRMESH_EXTRA_2ND_TEXTURE;
} else if (matFlags & AI_IRRMESH_MAT_solid_2layer) { // or just as second diffuse texture } else if (matFlags & AI_IRRMESH_MAT_solid_2layer) { // or just as second diffuse texture
++cnt; ++cnt;
s.Set(prop.value); s.Set(prop.value);
mat->AddProperty(&s, AI_MATKEY_TEXTURE_DIFFUSE(1)); mat->AddProperty(&s, AI_MATKEY_TEXTURE_DIFFUSE(1));
++nd; ++nd;
// set the corresponding material flag // set the corresponding material flag
matFlags |= AI_IRRMESH_EXTRA_2ND_TEXTURE; matFlags |= AI_IRRMESH_EXTRA_2ND_TEXTURE;
} else { } else {
ASSIMP_LOG_WARN("IRRmat: Skipping second texture"); ASSIMP_LOG_WARN("IRRmat: Skipping second texture");
} }
} else if (prop.name == "Texture3" && cnt == 2) { } else if (prop.name == "Texture3" && cnt == 2) {
// Irrlicht does not seem to use these channels. // Irrlicht does not seem to use these channels.
++cnt; ++cnt;
s.Set(prop.value); s.Set(prop.value);
mat->AddProperty(&s, AI_MATKEY_TEXTURE_DIFFUSE(nd + 1)); mat->AddProperty(&s, AI_MATKEY_TEXTURE_DIFFUSE(nd + 1));
} else if (prop.name == "Texture4" && cnt == 3) { } else if (prop.name == "Texture4" && cnt == 3) {
// Irrlicht does not seem to use these channels. // Irrlicht does not seem to use these channels.
++cnt; ++cnt;
s.Set(prop.value); s.Set(prop.value);
mat->AddProperty(&s, AI_MATKEY_TEXTURE_DIFFUSE(nd + 2)); mat->AddProperty(&s, AI_MATKEY_TEXTURE_DIFFUSE(nd + 2));
} }
// Texture mapping options // Texture mapping options
if (prop.name == "TextureWrap1" && cnt >= 1) { if (prop.name == "TextureWrap1" && cnt >= 1) {
int map = ConvertMappingMode(prop.value); int map = ConvertMappingMode(prop.value);
mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_U_DIFFUSE(0)); mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_U_DIFFUSE(0));
mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_V_DIFFUSE(0)); mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_V_DIFFUSE(0));
} else if (prop.name == "TextureWrap2" && cnt >= 2) { } else if (prop.name == "TextureWrap2" && cnt >= 2) {
int map = ConvertMappingMode(prop.value); int map = ConvertMappingMode(prop.value);
if (matFlags & AI_IRRMESH_MAT_lightmap) { if (matFlags & AI_IRRMESH_MAT_lightmap) {
mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_U_LIGHTMAP(0)); mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_U_LIGHTMAP(0));
mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_V_LIGHTMAP(0)); mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_V_LIGHTMAP(0));
} else if (matFlags & (AI_IRRMESH_MAT_normalmap_solid)) { } else if (matFlags & (AI_IRRMESH_MAT_normalmap_solid)) {
mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_U_NORMALS(0)); mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_U_NORMALS(0));
mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_V_NORMALS(0)); mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_V_NORMALS(0));
} else if (matFlags & AI_IRRMESH_MAT_solid_2layer) { } else if (matFlags & AI_IRRMESH_MAT_solid_2layer) {
mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_U_DIFFUSE(1)); mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_U_DIFFUSE(1));
mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_V_DIFFUSE(1)); mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_V_DIFFUSE(1));
} }
} else if (prop.name == "TextureWrap3" && cnt >= 3) { } else if (prop.name == "TextureWrap3" && cnt >= 3) {
int map = ConvertMappingMode(prop.value); int map = ConvertMappingMode(prop.value);
mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_U_DIFFUSE(nd + 1)); mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_U_DIFFUSE(nd + 1));
mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_V_DIFFUSE(nd + 1)); mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_V_DIFFUSE(nd + 1));
} else if (prop.name == "TextureWrap4" && cnt >= 4) { } else if (prop.name == "TextureWrap4" && cnt >= 4) {
int map = ConvertMappingMode(prop.value); int map = ConvertMappingMode(prop.value);
mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_U_DIFFUSE(nd + 2)); mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_U_DIFFUSE(nd + 2));
mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_V_DIFFUSE(nd + 2)); mat->AddProperty(&map, 1, AI_MATKEY_MAPPINGMODE_V_DIFFUSE(nd + 2));
} }
} }
} }
//break; // break;
/*case EXN_ELEMENT_END: /*case EXN_ELEMENT_END:
// Assume there are no further nested nodes in <material> elements // Assume there are no further nested nodes in <material> elements
if ( !ASSIMP_stricmp(reader->getNodeName(),"material") || if ( !ASSIMP_stricmp(reader->getNodeName(),"material") ||
@ -378,8 +378,8 @@ aiMaterial* IrrlichtBase::ParseMaterial(unsigned int& matFlags) {
break; break;
} }
}*/ }*/
} }
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

@ -1,8 +1,8 @@
/** @file IRRShared.h /** @file IRRShared.h
* @brief Shared utilities for the IRR and IRRMESH loaders * @brief Shared utilities for the IRR and IRRMESH loaders
*/ */
#ifndef INCLUDED_AI_IRRSHARED_H #ifndef INCLUDED_AI_IRRSHARED_H
#define INCLUDED_AI_IRRSHARED_H #define INCLUDED_AI_IRRSHARED_H
@ -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,18 +632,17 @@ 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
nodes.back().channels.emplace_back();
LWO::Envelope &env = nodes.back().channels.back();
env.index = strtoul10(c);
// currently we can just interpret the standard channels 0...9
// (hack) assume that index-i yields the binary channel type from LWO
env.type = (LWO::EnvelopeType)(env.index + 1);
} }
// important: index of channel
nodes.back().channels.emplace_back();
LWO::Envelope &env = nodes.back().channels.back();
env.index = strtoul10(c);
// currently we can just interpret the standard channels 0...9
// (hack) assume that index-i yields the binary channel type from LWO
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;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -228,15 +255,20 @@ bool MD5Parser::ParseSection(Section &out) {
out.data[out.length] = '\0'; out.data[out.length] = '\0';
// 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; \
++sz; \ while ('\"' != *sz && '\0' != *sz) \
const char *szStart = ++sz; \ ++sz; \
while ('\"' != *sz) \ if ('\0' != *sz) { \
++sz; \ const char *szStart = ++sz; \
const char *szEnd = (sz++); \ while ('\"' != *sz && '\0' != *sz) \
out.length = (ai_uint32)(szEnd - szStart); \ ++sz; \
::memcpy(out.data, szStart, out.length); \ if ('\0' != *sz) { \
const char *szEnd = (sz++); \
out.length = (ai_uint32)(szEnd - szStart); \
::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;
ParseTextureColorData(szCurrent, iMasked, &iSkip, &tex);
try {
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,9 +252,9 @@ 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)
m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine); getFloatValue(m_pModel->mCurrentMaterial->anisotropy);
m_DataIt = skipLine<DataArrayIt>(m_DataIt, m_DataItEnd, m_uiLine);
} break; } break;
default: { default: {
@ -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;
@ -1113,13 +1138,14 @@ public:
bool KHR_texture_basisu; bool KHR_texture_basisu;
Extensions() : Extensions() :
KHR_materials_pbrSpecularGlossiness(false), KHR_materials_pbrSpecularGlossiness(false),
KHR_materials_unlit(false), KHR_materials_specular(false),
KHR_lights_punctual(false), KHR_materials_unlit(false),
KHR_texture_transform(false), KHR_lights_punctual(false),
KHR_materials_sheen(false), KHR_texture_transform(false),
KHR_materials_clearcoat(false), KHR_materials_sheen(false),
KHR_materials_transmission(false), KHR_materials_clearcoat(false),
KHR_materials_transmission(false),
KHR_materials_volume(false), KHR_materials_volume(false),
KHR_materials_ior(false), KHR_materials_ior(false),
KHR_materials_emissive_strength(false), KHR_materials_emissive_strength(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);
} }
} }
@ -1263,6 +1278,19 @@ inline void Material::Read(Value &material, Asset &r) {
this->pbrSpecularGlossiness = Nullable<PbrSpecularGlossiness>(pbrSG); this->pbrSpecularGlossiness = Nullable<PbrSpecularGlossiness>(pbrSG);
} }
} }
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
@ -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);
@ -550,7 +570,7 @@ namespace glTF2 {
inline void Write(Value& obj, Mesh& m, AssetWriter& w) inline void Write(Value& obj, Mesh& m, AssetWriter& w)
{ {
/****************** Primitives *******************/ /****************** Primitives *******************/
Value primitives; Value primitives;
primitives.SetArray(); primitives.SetArray();
primitives.Reserve(unsigned(m.primitives.size()), w.mAl); primitives.Reserve(unsigned(m.primitives.size()), w.mAl);
@ -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);
} }
@ -980,7 +1043,7 @@ namespace glTF2 {
if (d.mObjs.empty()) return; if (d.mObjs.empty()) return;
Value* container = &mDoc; Value* container = &mDoc;
const char* context = "Document"; const char* context = "Document";
if (d.mExtId) { if (d.mExtId) {
Value* exts = FindObject(mDoc, "extensions"); Value* exts = FindObject(mDoc, "extensions");

View File

@ -263,7 +263,7 @@ size_t NZDiff(void *data, void *dataBase, size_t count, unsigned int numCompsIn,
for (short idx = 0; bufferData_ptr < bufferData_end; idx += 1, bufferData_ptr += numCompsIn) { for (short idx = 0; bufferData_ptr < bufferData_end; idx += 1, bufferData_ptr += numCompsIn) {
bool bNonZero = false; bool bNonZero = false;
//for the data, check any component Non Zero // for the data, check any component Non Zero
for (unsigned int j = 0; j < numCompsOut; j++) { for (unsigned int j = 0; j < numCompsOut; j++) {
double valueData = bufferData_ptr[j]; double valueData = bufferData_ptr[j];
double valueBase = bufferBase_ptr ? bufferBase_ptr[j] : 0; double valueBase = bufferBase_ptr ? bufferBase_ptr[j] : 0;
@ -273,11 +273,11 @@ size_t NZDiff(void *data, void *dataBase, size_t count, unsigned int numCompsIn,
} }
} }
//all zeros, continue // all zeros, continue
if (!bNonZero) if (!bNonZero)
continue; continue;
//non zero, store the data // non zero, store the data
for (unsigned int j = 0; j < numCompsOut; j++) { for (unsigned int j = 0; j < numCompsOut; j++) {
T valueData = bufferData_ptr[j]; T valueData = bufferData_ptr[j];
T valueBase = bufferBase_ptr ? bufferBase_ptr[j] : 0; T valueBase = bufferBase_ptr ? bufferBase_ptr[j] : 0;
@ -286,14 +286,14 @@ size_t NZDiff(void *data, void *dataBase, size_t count, unsigned int numCompsIn,
vNZIdx.push_back(idx); vNZIdx.push_back(idx);
} }
//avoid all-0, put 1 item // avoid all-0, put 1 item
if (vNZDiff.size() == 0) { if (vNZDiff.size() == 0) {
for (unsigned int j = 0; j < numCompsOut; j++) for (unsigned int j = 0; j < numCompsOut; j++)
vNZDiff.push_back(0); vNZDiff.push_back(0);
vNZIdx.push_back(0); vNZIdx.push_back(0);
} }
//process data // process data
outputNZDiff = new T[vNZDiff.size()]; outputNZDiff = new T[vNZDiff.size()];
memcpy(outputNZDiff, vNZDiff.data(), vNZDiff.size() * sizeof(T)); memcpy(outputNZDiff, vNZDiff.data(), vNZDiff.size() * sizeof(T));
@ -361,7 +361,7 @@ inline Ref<Accessor> ExportDataSparse(Asset &a, std::string &meshName, Ref<Buffe
acc->sparse.reset(new Accessor::Sparse); acc->sparse.reset(new Accessor::Sparse);
acc->sparse->count = nzCount; acc->sparse->count = nzCount;
//indices // indices
unsigned int bytesPerIdx = sizeof(unsigned short); unsigned int bytesPerIdx = sizeof(unsigned short);
size_t indices_offset = buffer->byteLength; size_t indices_offset = buffer->byteLength;
size_t indices_padding = indices_offset % bytesPerIdx; size_t indices_padding = indices_offset % bytesPerIdx;
@ -379,7 +379,7 @@ inline Ref<Accessor> ExportDataSparse(Asset &a, std::string &meshName, Ref<Buffe
acc->sparse->indicesByteOffset = 0; acc->sparse->indicesByteOffset = 0;
acc->WriteSparseIndices(nzCount, nzIdx, 1 * bytesPerIdx); acc->WriteSparseIndices(nzCount, nzIdx, 1 * bytesPerIdx);
//values // values
size_t values_offset = buffer->byteLength; size_t values_offset = buffer->byteLength;
size_t values_padding = values_offset % bytesPerComp; size_t values_padding = values_offset % bytesPerComp;
values_offset += values_padding; values_offset += values_padding;
@ -395,9 +395,9 @@ inline Ref<Accessor> ExportDataSparse(Asset &a, std::string &meshName, Ref<Buffe
acc->sparse->valuesByteOffset = 0; acc->sparse->valuesByteOffset = 0;
acc->WriteSparseValues(nzCount, nzDiff, numCompsIn * bytesPerComp); acc->WriteSparseValues(nzCount, nzDiff, numCompsIn * bytesPerComp);
//clear // clear
delete[](char *) nzDiff; delete[] (char *)nzDiff;
delete[](char *) nzIdx; delete[] (char *)nzIdx;
} }
return acc; return acc;
} }
@ -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:
@ -544,7 +599,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial &mat, Ref<Texture> &texture, unsi
if (curTex != nullptr) { // embedded if (curTex != nullptr) { // embedded
texture->source->name = curTex->mFilename.C_Str(); texture->source->name = curTex->mFilename.C_Str();
//basisu: embedded ktx2, bu // basisu: embedded ktx2, bu
if (curTex->achFormatHint[0]) { if (curTex->achFormatHint[0]) {
std::string mimeType = "image/"; std::string mimeType = "image/";
if (memcmp(curTex->achFormatHint, "jpg", 3) == 0) if (memcmp(curTex->achFormatHint, "jpg", 3) == 0)
@ -564,7 +619,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial &mat, Ref<Texture> &texture, unsi
} }
// The asset has its own buffer, see Image::SetData // The asset has its own buffer, see Image::SetData
//basisu: "image/ktx2", "image/basis" as is // basisu: "image/ktx2", "image/basis" as is
texture->source->SetData(reinterpret_cast<uint8_t *>(curTex->pcData), curTex->mWidth, *mAsset); texture->source->SetData(reinterpret_cast<uint8_t *>(curTex->pcData), curTex->mWidth, *mAsset);
} else { } else {
texture->source->uri = path; texture->source->uri = path;
@ -574,7 +629,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial &mat, Ref<Texture> &texture, unsi
} }
} }
//basisu // basisu
if (useBasisUniversal) { if (useBasisUniversal) {
mAsset->extensionsUsed.KHR_texture_basisu = true; mAsset->extensionsUsed.KHR_texture_basisu = true;
mAsset->extensionsRequired.KHR_texture_basisu = true; mAsset->extensionsRequired.KHR_texture_basisu = true;
@ -597,7 +652,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial &mat, NormalTextureInfo &prop, ai
GetMatTex(mat, texture, prop.texCoord, tt, slot); GetMatTex(mat, texture, prop.texCoord, tt, slot);
if (texture) { if (texture) {
//GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); // GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot);
GetMatTexProp(mat, prop.scale, "scale", tt, slot); GetMatTexProp(mat, prop.scale, "scale", tt, slot);
} }
} }
@ -608,7 +663,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial &mat, OcclusionTextureInfo &prop,
GetMatTex(mat, texture, prop.texCoord, tt, slot); GetMatTex(mat, texture, prop.texCoord, tt, slot);
if (texture) { if (texture) {
//GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); // GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot);
GetMatTexProp(mat, prop.strength, "strength", tt, slot); GetMatTexProp(mat, prop.strength, "strength", tt, slot);
} }
} }
@ -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) {
@ -759,20 +832,30 @@ void glTF2Exporter::ExportMaterials() {
GetMatTex(mat, m->pbrMetallicRoughness.baseColorTexture, aiTextureType_BASE_COLOR); GetMatTex(mat, m->pbrMetallicRoughness.baseColorTexture, aiTextureType_BASE_COLOR);
if (!m->pbrMetallicRoughness.baseColorTexture.texture) { if (!m->pbrMetallicRoughness.baseColorTexture.texture) {
//if there wasn't a baseColorTexture defined in the source, fallback to any diffuse texture // if there wasn't a baseColorTexture defined in the source, fallback to any diffuse texture
GetMatTex(mat, m->pbrMetallicRoughness.baseColorTexture, aiTextureType_DIFFUSE); GetMatTex(mat, m->pbrMetallicRoughness.baseColorTexture, aiTextureType_DIFFUSE);
} }
GetMatTex(mat, m->pbrMetallicRoughness.metallicRoughnessTexture, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE); 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);
}
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.
//a fallback to any diffuse color should be used instead // a fallback to any diffuse color should be used instead
GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_COLOR_DIFFUSE); GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_COLOR_DIFFUSE);
} }
if (mat.Get(AI_MATKEY_METALLIC_FACTOR, m->pbrMetallicRoughness.metallicFactor) != AI_SUCCESS) { if (mat.Get(AI_MATKEY_METALLIC_FACTOR, m->pbrMetallicRoughness.metallicFactor) != AI_SUCCESS) {
//if metallicFactor wasn't defined, then the source is likely not a PBR file, and the metallicFactor should be 0 // if metallicFactor wasn't defined, then the source is likely not a PBR file, and the metallicFactor should be 0
m->pbrMetallicRoughness.metallicFactor = 0; m->pbrMetallicRoughness.metallicFactor = 0;
} }
@ -785,10 +868,10 @@ void glTF2Exporter::ExportMaterials() {
if (mat.Get(AI_MATKEY_COLOR_SPECULAR, specularColor) == AI_SUCCESS && mat.Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS) { if (mat.Get(AI_MATKEY_COLOR_SPECULAR, specularColor) == AI_SUCCESS && mat.Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS) {
// convert specular color to luminance // convert specular color to luminance
float specularIntensity = specularColor[0] * 0.2125f + specularColor[1] * 0.7154f + specularColor[2] * 0.0721f; float specularIntensity = specularColor[0] * 0.2125f + specularColor[1] * 0.7154f + specularColor[2] * 0.0721f;
//normalize shininess (assuming max is 1000) with an inverse exponentional curve // normalize shininess (assuming max is 1000) with an inverse exponentional curve
float normalizedShininess = std::sqrt(shininess / 1000); float normalizedShininess = std::sqrt(shininess / 1000);
//clamp the shininess value between 0 and 1 // clamp the shininess value between 0 and 1
normalizedShininess = std::min(std::max(normalizedShininess, 0.0f), 1.0f); normalizedShininess = std::min(std::max(normalizedShininess, 0.0f), 1.0f);
// low specular intensity values should produce a rough material even if shininess is high. // low specular intensity values should produce a rough material even if shininess is high.
normalizedShininess = normalizedShininess * specularIntensity; normalizedShininess = normalizedShininess * specularIntensity;
@ -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;
@ -981,7 +1069,7 @@ void ExportSkin(Asset &mAsset, const aiMesh *aimesh, Ref<Mesh> &meshRef, Ref<Buf
if (boneIndexFitted != -1) { if (boneIndexFitted != -1) {
vertexJointData[vertexId][boneIndexFitted] = static_cast<float>(jointNamesIndex); vertexJointData[vertexId][boneIndexFitted] = static_cast<float>(jointNamesIndex);
} }
}else { } else {
vertexJointData[vertexId][jointsPerVertex[vertexId]] = static_cast<float>(jointNamesIndex); vertexJointData[vertexId][jointsPerVertex[vertexId]] = static_cast<float>(jointNamesIndex);
vertexWeightData[vertexId][jointsPerVertex[vertexId]] = vertWeight; vertexWeightData[vertexId][jointsPerVertex[vertexId]] = vertWeight;
@ -993,7 +1081,7 @@ void ExportSkin(Asset &mAsset, const aiMesh *aimesh, Ref<Mesh> &meshRef, Ref<Buf
Mesh::Primitive &p = meshRef->primitives.back(); Mesh::Primitive &p = meshRef->primitives.back();
Ref<Accessor> vertexJointAccessor = ExportData(mAsset, skinRef->id, bufferRef, aimesh->mNumVertices, Ref<Accessor> vertexJointAccessor = ExportData(mAsset, skinRef->id, bufferRef, aimesh->mNumVertices,
vertexJointData, AttribType::VEC4, AttribType::VEC4, ComponentType_FLOAT); vertexJointData, AttribType::VEC4, AttribType::VEC4, ComponentType_FLOAT);
if (vertexJointAccessor) { if (vertexJointAccessor) {
size_t offset = vertexJointAccessor->bufferView->byteOffset; size_t offset = vertexJointAccessor->bufferView->byteOffset;
size_t bytesLen = vertexJointAccessor->bufferView->byteLength; size_t bytesLen = vertexJointAccessor->bufferView->byteLength;
@ -1077,7 +1165,7 @@ void glTF2Exporter::ExportMeshes() {
/******************* Vertices ********************/ /******************* Vertices ********************/
Ref<Accessor> v = ExportData(*mAsset, meshId, b, aim->mNumVertices, aim->mVertices, AttribType::VEC3, Ref<Accessor> v = ExportData(*mAsset, meshId, b, aim->mNumVertices, aim->mVertices, AttribType::VEC3,
AttribType::VEC3, ComponentType_FLOAT, BufferViewTarget_ARRAY_BUFFER); AttribType::VEC3, ComponentType_FLOAT, BufferViewTarget_ARRAY_BUFFER);
if (v) { if (v) {
p.attributes.position.push_back(v); p.attributes.position.push_back(v);
} }
@ -1091,7 +1179,7 @@ void glTF2Exporter::ExportMeshes() {
} }
Ref<Accessor> n = ExportData(*mAsset, meshId, b, aim->mNumVertices, aim->mNormals, AttribType::VEC3, Ref<Accessor> n = ExportData(*mAsset, meshId, b, aim->mNumVertices, aim->mNormals, AttribType::VEC3,
AttribType::VEC3, ComponentType_FLOAT, BufferViewTarget_ARRAY_BUFFER); AttribType::VEC3, ComponentType_FLOAT, BufferViewTarget_ARRAY_BUFFER);
if (n) { if (n) {
p.attributes.normal.push_back(n); p.attributes.normal.push_back(n);
} }
@ -1113,7 +1201,7 @@ void glTF2Exporter::ExportMeshes() {
AttribType::Value type = (aim->mNumUVComponents[i] == 2) ? AttribType::VEC2 : AttribType::VEC3; AttribType::Value type = (aim->mNumUVComponents[i] == 2) ? AttribType::VEC2 : AttribType::VEC3;
Ref<Accessor> tc = ExportData(*mAsset, meshId, b, aim->mNumVertices, aim->mTextureCoords[i], Ref<Accessor> tc = ExportData(*mAsset, meshId, b, aim->mNumVertices, aim->mTextureCoords[i],
AttribType::VEC3, type, ComponentType_FLOAT, BufferViewTarget_ARRAY_BUFFER); AttribType::VEC3, type, ComponentType_FLOAT, BufferViewTarget_ARRAY_BUFFER);
if (tc) { if (tc) {
p.attributes.texcoord.push_back(tc); p.attributes.texcoord.push_back(tc);
} }
@ -1123,7 +1211,7 @@ void glTF2Exporter::ExportMeshes() {
/*************** Vertex colors ****************/ /*************** Vertex colors ****************/
for (unsigned int indexColorChannel = 0; indexColorChannel < aim->GetNumColorChannels(); ++indexColorChannel) { for (unsigned int indexColorChannel = 0; indexColorChannel < aim->GetNumColorChannels(); ++indexColorChannel) {
Ref<Accessor> c = ExportData(*mAsset, meshId, b, aim->mNumVertices, aim->mColors[indexColorChannel], Ref<Accessor> c = ExportData(*mAsset, meshId, b, aim->mNumVertices, aim->mColors[indexColorChannel],
AttribType::VEC4, AttribType::VEC4, ComponentType_FLOAT, BufferViewTarget_ARRAY_BUFFER); AttribType::VEC4, AttribType::VEC4, ComponentType_FLOAT, BufferViewTarget_ARRAY_BUFFER);
if (c) { if (c) {
p.attributes.color.push_back(c); p.attributes.color.push_back(c);
} }
@ -1141,7 +1229,7 @@ void glTF2Exporter::ExportMeshes() {
} }
p.indices = ExportData(*mAsset, meshId, b, indices.size(), &indices[0], AttribType::SCALAR, AttribType::SCALAR, p.indices = ExportData(*mAsset, meshId, b, indices.size(), &indices[0], AttribType::SCALAR, AttribType::SCALAR,
ComponentType_UNSIGNED_INT, BufferViewTarget_ELEMENT_ARRAY_BUFFER); ComponentType_UNSIGNED_INT, BufferViewTarget_ELEMENT_ARRAY_BUFFER);
} }
switch (aim->mPrimitiveTypes) { switch (aim->mPrimitiveTypes) {
@ -1284,24 +1372,24 @@ void glTF2Exporter::MergeMeshes() {
unsigned int nMeshes = static_cast<unsigned int>(node->meshes.size()); unsigned int nMeshes = static_cast<unsigned int>(node->meshes.size());
//skip if it's 1 or less meshes per node // skip if it's 1 or less meshes per node
if (nMeshes > 1) { if (nMeshes > 1) {
Ref<Mesh> firstMesh = node->meshes.at(0); Ref<Mesh> firstMesh = node->meshes.at(0);
//loop backwards to allow easy removal of a mesh from a node once it's merged // loop backwards to allow easy removal of a mesh from a node once it's merged
for (unsigned int m = nMeshes - 1; m >= 1; --m) { for (unsigned int m = nMeshes - 1; m >= 1; --m) {
Ref<Mesh> mesh = node->meshes.at(m); Ref<Mesh> mesh = node->meshes.at(m);
//append this mesh's primitives to the first mesh's primitives // append this mesh's primitives to the first mesh's primitives
firstMesh->primitives.insert( firstMesh->primitives.insert(
firstMesh->primitives.end(), firstMesh->primitives.end(),
mesh->primitives.begin(), mesh->primitives.begin(),
mesh->primitives.end()); mesh->primitives.end());
//remove the mesh from the list of meshes // remove the mesh from the list of meshes
unsigned int removedIndex = mAsset->meshes.Remove(mesh->id.c_str()); unsigned int removedIndex = mAsset->meshes.Remove(mesh->id.c_str());
//find the presence of the removed mesh in other nodes // find the presence of the removed mesh in other nodes
for (unsigned int nn = 0; nn < mAsset->nodes.Size(); ++nn) { for (unsigned int nn = 0; nn < mAsset->nodes.Size(); ++nn) {
Ref<Node> curNode = mAsset->nodes.Get(nn); Ref<Node> curNode = mAsset->nodes.Get(nn);
@ -1320,7 +1408,7 @@ void glTF2Exporter::MergeMeshes() {
} }
} }
//since we were looping backwards, reverse the order of merged primitives to their original order // since we were looping backwards, reverse the order of merged primitives to their original order
std::reverse(firstMesh->primitives.begin() + 1, firstMesh->primitives.end()); std::reverse(firstMesh->primitives.begin() + 1, firstMesh->primitives.end());
} }
} }
@ -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;
@ -1445,9 +1535,9 @@ inline void ExtractTranslationSampler(Asset &asset, std::string &animId, Ref<Buf
const aiVectorKey &key = nodeChannel->mPositionKeys[i]; const aiVectorKey &key = nodeChannel->mPositionKeys[i];
// mTime is measured in ticks, but GLTF time is measured in seconds, so convert. // mTime is measured in ticks, but GLTF time is measured in seconds, so convert.
times[i] = static_cast<float>(key.mTime / ticksPerSecond); times[i] = static_cast<float>(key.mTime / ticksPerSecond);
values[(i * 3) + 0] = (ai_real) key.mValue.x; values[(i * 3) + 0] = (ai_real)key.mValue.x;
values[(i * 3) + 1] = (ai_real) key.mValue.y; values[(i * 3) + 1] = (ai_real)key.mValue.y;
values[(i * 3) + 2] = (ai_real) key.mValue.z; values[(i * 3) + 2] = (ai_real)key.mValue.z;
} }
sampler.input = GetSamplerInputRef(asset, animId, buffer, times); sampler.input = GetSamplerInputRef(asset, animId, buffer, times);
@ -1464,9 +1554,9 @@ inline void ExtractScaleSampler(Asset &asset, std::string &animId, Ref<Buffer> &
const aiVectorKey &key = nodeChannel->mScalingKeys[i]; const aiVectorKey &key = nodeChannel->mScalingKeys[i];
// mTime is measured in ticks, but GLTF time is measured in seconds, so convert. // mTime is measured in ticks, but GLTF time is measured in seconds, so convert.
times[i] = static_cast<float>(key.mTime / ticksPerSecond); times[i] = static_cast<float>(key.mTime / ticksPerSecond);
values[(i * 3) + 0] = (ai_real) key.mValue.x; values[(i * 3) + 0] = (ai_real)key.mValue.x;
values[(i * 3) + 1] = (ai_real) key.mValue.y; values[(i * 3) + 1] = (ai_real)key.mValue.y;
values[(i * 3) + 2] = (ai_real) key.mValue.z; values[(i * 3) + 2] = (ai_real)key.mValue.z;
} }
sampler.input = GetSamplerInputRef(asset, animId, buffer, times); sampler.input = GetSamplerInputRef(asset, animId, buffer, times);
@ -1483,10 +1573,10 @@ inline void ExtractRotationSampler(Asset &asset, std::string &animId, Ref<Buffer
const aiQuatKey &key = nodeChannel->mRotationKeys[i]; const aiQuatKey &key = nodeChannel->mRotationKeys[i];
// mTime is measured in ticks, but GLTF time is measured in seconds, so convert. // mTime is measured in ticks, but GLTF time is measured in seconds, so convert.
times[i] = static_cast<float>(key.mTime / ticksPerSecond); times[i] = static_cast<float>(key.mTime / ticksPerSecond);
values[(i * 4) + 0] = (ai_real) key.mValue.x; values[(i * 4) + 0] = (ai_real)key.mValue.x;
values[(i * 4) + 1] = (ai_real) key.mValue.y; values[(i * 4) + 1] = (ai_real)key.mValue.y;
values[(i * 4) + 2] = (ai_real) key.mValue.z; values[(i * 4) + 2] = (ai_real)key.mValue.z;
values[(i * 4) + 3] = (ai_real) key.mValue.w; values[(i * 4) + 3] = (ai_real)key.mValue.w;
} }
sampler.input = GetSamplerInputRef(asset, animId, buffer, times); sampler.input = GetSamplerInputRef(asset, animId, buffer, times);

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];
@ -477,14 +497,14 @@ 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,25 +1418,29 @@ 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
) )
install(FILES ${Assimp_BINARY_DIR}/code/RelWithDebInfo/assimp${LIBRARY_SUFFIX}.pdb install(FILES ${Assimp_BINARY_DIR}/code/RelWithDebInfo/assimp${LIBRARY_SUFFIX}.pdb
DESTINATION ${ASSIMP_LIB_INSTALL_DIR} DESTINATION ${ASSIMP_LIB_INSTALL_DIR}
CONFIGURATIONS RelWithDebInfo CONFIGURATIONS RelWithDebInfo
) )
ELSE()
install(FILES ${Assimp_BINARY_DIR}/code/assimp${LIBRARY_SUFFIX}${CMAKE_DEBUG_POSTFIX}.pdb
DESTINATION ${ASSIMP_LIB_INSTALL_DIR}
CONFIGURATIONS Debug
)
install(FILES ${Assimp_BINARY_DIR}/code/assimp${LIBRARY_SUFFIX}.pdb
DESTINATION ${ASSIMP_LIB_INSTALL_DIR}
CONFIGURATIONS RelWithDebInfo
)
ENDIF()
ELSE() ELSE()
install(FILES ${Assimp_BINARY_DIR}/code/assimp${LIBRARY_SUFFIX}${CMAKE_DEBUG_POSTFIX}.pdb install(FILES $<TARGET_PDB_FILE:assimp>
DESTINATION ${ASSIMP_LIB_INSTALL_DIR} DESTINATION ${ASSIMP_LIB_INSTALL_DIR}
CONFIGURATIONS Debug
)
install(FILES ${Assimp_BINARY_DIR}/code/assimp${LIBRARY_SUFFIX}.pdb
DESTINATION ${ASSIMP_LIB_INSTALL_DIR}
CONFIGURATIONS RelWithDebInfo
) )
ENDIF() ENDIF()
ENDIF () ENDIF ()

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
return true; /*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;
}
} }
// 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,24 +637,10 @@ 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 ImporterAndIndex candidate = { pimpl->mImporter[a], a };
// inside them, e.g. ogre.mesh.xml. Compare the entire end of the string. possibleImporters.push_back(candidate);
for (std::set<std::string>::const_iterator it = extensions.cbegin(); it != extensions.cend(); ++it) {
// Yay for C++<20 not having std::string::ends_with()
std::string extension = "." + *it;
if (extension.length() <= pFile.length()) {
// Possible optimization: Fetch the lowercase filename!
if (0 == ASSIMP_stricmp(pFile.c_str() + pFile.length() - extension.length(), extension.c_str())) {
ImporterAndIndex candidate = { pimpl->mImporter[a], a };
possibleImporters.push_back(candidate);
break;
}
}
} }
} }
// If 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.

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 :) // lcl transform grab - done in generate_nodes :)
// bone->mOffsetMatrix = bone_node->mTransformation;
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>
@ -51,39 +50,25 @@ using namespace Assimp;
namespace { namespace {
const static aiVector3D base_axis_y(0.0,1.0,0.0); const static aiVector3D base_axis_y(0.0, 1.0, 0.0);
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]) {
if (!mesh->mTextureCoords[m])return 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,22 +76,22 @@ 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.
const static ai_real LOWER_LIMIT = ai_real( 0.1 ); const static ai_real LOWER_LIMIT = ai_real(0.1);
const static ai_real UPPER_LIMIT = ai_real( 0.9 ); const static ai_real UPPER_LIMIT = ai_real(0.9);
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) {
if (face.mNumIndices < 3) continue; // triangles and polygons only, please 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,9 +137,8 @@ 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)
out[face.mIndices[n]].x = 1.0; out[face.mIndices[n]].x = 1.0;
@ -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);
@ -178,7 +157,7 @@ void ComputeUVMappingProcess::ComputeSphereMapping(aiMesh* mesh,const aiVector3D
// currently the mapping axis will always be one of x,y,z, except if the // currently the mapping axis will always be one of x,y,z, except if the
// PretransformVertices step is used (it transforms the meshes into worldspace, // PretransformVertices step is used (it transforms the meshes into worldspace,
// thus changing the mapping axis) // thus changing the mapping axis)
if (axis * base_axis_x >= angle_epsilon) { if (axis * base_axis_x >= angle_epsilon) {
// For each point get a normalized projection vector in the sphere, // For each point get a normalized projection vector in the sphere,
// get its longitude and latitude and map them to their respective // get its longitude and latitude and map them to their respective
@ -192,58 +171,54 @@ void ComputeUVMappingProcess::ComputeSphereMapping(aiMesh* mesh,const aiVector3D
// Thus we can derive: // Thus we can derive:
// lat = arcsin (z) // lat = arcsin (z)
// lon = arctan (y/x) // lon = arctan (y/x)
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.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();
out[pnt] = aiVector3D((std::atan2(diff.y, diff.x) + AI_MATH_PI_F ) / AI_MATH_TWO_PI_F, out[pnt] = aiVector3D((std::atan2(diff.y, diff.x) + AI_MATH_PI_F) / AI_MATH_TWO_PI_F,
(std::asin (diff.z) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0); (std::asin(diff.z) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0);
} }
} }
// 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);
// again the same, except we're applying a transformation now // again the same, except we're applying a transformation now
for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) { for (unsigned int pnt = 0; pnt < mesh->mNumVertices; ++pnt) {
const aiVector3D diff = ((mTrafo*mesh->mVertices[pnt])-center).Normalize(); const aiVector3D diff = ((mTrafo * mesh->mVertices[pnt]) - center).Normalize();
out[pnt] = aiVector3D((std::atan2(diff.y, diff.x) + AI_MATH_PI_F ) / AI_MATH_TWO_PI_F, out[pnt] = aiVector3D((std::atan2(diff.y, diff.x) + AI_MATH_PI_F) / AI_MATH_TWO_PI_F,
(std::asin(diff.z) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0); (std::asin(diff.z) + AI_MATH_HALF_PI_F) / AI_MATH_PI_F, 0.0);
} }
} }
// 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.
RemoveUVSeams(mesh,out); RemoveUVSeams(mesh, out);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
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 ...
// currently the mapping axis will always be one of x,y,z, except if the // currently the mapping axis will always be one of x,y,z, except if the
// PretransformVertices step is used (it transforms the meshes into worldspace, // PretransformVertices step is used (it transforms the meshes into worldspace,
// thus changing the mapping axis) // thus changing the mapping axis)
if (axis * base_axis_x >= angle_epsilon) { if (axis * base_axis_x >= angle_epsilon) {
FindMeshCenter(mesh, center, min, max); FindMeshCenter(mesh, center, min, max);
const ai_real diff = max.x - min.x; const ai_real diff = max.x - min.x;
@ -251,116 +226,110 @@ void ComputeUVMappingProcess::ComputeCylinderMapping(aiMesh* mesh,const aiVector
// directly to the texture V axis. The other axis is derived from // directly to the texture V axis. The other axis is derived from
// the angle between ( p.x - c.x, p.y - c.y ) and (1,0), where // the angle between ( p.x - c.x, p.y - c.y ) and (1,0), where
// 'c' is the center point of the mesh. // 'c' is the center point of the mesh.
for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) { for (unsigned int pnt = 0; pnt < mesh->mNumVertices; ++pnt) {
const aiVector3D& pos = mesh->mVertices[pnt]; const aiVector3D &pos = mesh->mVertices[pnt];
aiVector3D& uv = out[pnt]; aiVector3D &uv = out[pnt];
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;
// just the same ... // just the same ...
for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) { for (unsigned int pnt = 0; pnt < mesh->mNumVertices; ++pnt) {
const aiVector3D& pos = mesh->mVertices[pnt]; const aiVector3D &pos = mesh->mVertices[pnt];
aiVector3D& uv = out[pnt]; aiVector3D &uv = out[pnt];
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;
// just the same ... // just the same ...
for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) { for (unsigned int pnt = 0; pnt < mesh->mNumVertices; ++pnt) {
const aiVector3D& pos = mesh->mVertices[pnt]; const aiVector3D &pos = mesh->mVertices[pnt];
aiVector3D& uv = out[pnt]; aiVector3D &uv = out[pnt];
uv.y = (pos.z - min.z) / diff; uv.y = (pos.z - min.z) / diff;
uv.x = (std::atan2( pos.y - center.y, pos.x - center.x) +(ai_real)AI_MATH_PI ) / (ai_real)AI_MATH_TWO_PI; uv.x = (std::atan2(pos.y - center.y, pos.x - center.x) + (ai_real)AI_MATH_PI) / (ai_real)AI_MATH_TWO_PI;
} }
} }
// 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);
const ai_real diff = max.y - min.y; const ai_real diff = max.y - min.y;
// again the same, except we're applying a transformation now // again the same, except we're applying a transformation now
for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt){ for (unsigned int pnt = 0; pnt < mesh->mNumVertices; ++pnt) {
const aiVector3D pos = mTrafo* mesh->mVertices[pnt]; const aiVector3D pos = mTrafo * mesh->mVertices[pnt];
aiVector3D& uv = out[pnt]; aiVector3D &uv = out[pnt];
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;
} }
} }
// 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.
RemoveUVSeams(mesh,out); RemoveUVSeams(mesh, out);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
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;
// 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 ...
// currently the mapping axis will always be one of x,y,z, except if the // currently the mapping axis will always be one of x,y,z, except if the
// PretransformVertices step is used (it transforms the meshes into worldspace, // PretransformVertices step is used (it transforms the meshes into worldspace,
// thus changing the mapping axis) // thus changing the mapping axis)
if (axis * base_axis_x >= angle_epsilon) { if (axis * base_axis_x >= angle_epsilon) {
FindMeshCenter(mesh, center, min, max); FindMeshCenter(mesh, center, min, max);
diffu = max.z - min.z; diffu = max.z - min.z;
diffv = max.y - min.y; diffv = max.y - min.y;
for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) { for (unsigned int pnt = 0; pnt < mesh->mNumVertices; ++pnt) {
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;
for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) { for (unsigned int pnt = 0; pnt < mesh->mNumVertices; ++pnt) {
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;
for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) { for (unsigned int pnt = 0; pnt < mesh->mNumVertices; ++pnt) {
const aiVector3D& pos = mesh->mVertices[pnt]; const aiVector3D &pos = mesh->mVertices[pnt];
out[pnt].Set((pos.x - min.x) / diffu,(pos.y - min.y) / diffv,0.0); out[pnt].Set((pos.x - min.x) / diffu, (pos.y - min.y) / diffv, 0.0);
} }
} }
// 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);
diffu = max.x - min.x; diffu = max.x - min.x;
diffv = max.z - min.z; diffv = max.z - min.z;
// again the same, except we're applying a transformation now // again the same, except we're applying a transformation now
for (unsigned int pnt = 0; pnt < mesh->mNumVertices;++pnt) { for (unsigned int pnt = 0; pnt < mesh->mNumVertices; ++pnt) {
const aiVector3D pos = mTrafo * mesh->mVertices[pnt]; const aiVector3D pos = mTrafo * 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);
} }
} }
@ -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,23 +353,18 @@ 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);
{ if (aiTextureMapping_UV != mapping) {
aiTextureMapping& mapping = *((aiTextureMapping*)prop->mData); if (!DefaultLogger::isNullLogger()) {
if (aiTextureMapping_UV != mapping)
{
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));
ASSIMP_LOG_INFO(buffer); ASSIMP_LOG_INFO(buffer);
} }
@ -410,70 +372,62 @@ void ComputeUVMappingProcess::Execute( aiScene* pScene)
if (aiTextureMapping_OTHER == mapping) if (aiTextureMapping_OTHER == mapping)
continue; continue;
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;
if ( !::strcmp( prop2->mKey.data, "$tex.mapaxis")) { if (!::strcmp(prop2->mKey.data, "$tex.mapaxis")) {
info.axis = *((aiVector3D*)prop2->mData); info.axis = *((aiVector3D *)prop2->mData);
break; break;
} }
} }
unsigned int idx( 99999999 ); unsigned int idx(99999999);
// 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;
case aiTextureMapping_CYLINDER: case aiTextureMapping_CYLINDER:
ComputeCylinderMapping(mesh,info.axis,p); ComputeCylinderMapping(mesh, info.axis, p);
break; break;
case aiTextureMapping_PLANE: case aiTextureMapping_PLANE:
ComputePlaneMapping(mesh,info.axis,p); ComputePlaneMapping(mesh, info.axis, p);
break; break;
case aiTextureMapping_BOX: case aiTextureMapping_BOX:
ComputeBoxMapping(mesh,p); ComputeBoxMapping(mesh, p);
break; break;
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. ");
} }
idx = outIdx; idx = outIdx;
} }
@ -483,7 +437,7 @@ void ComputeUVMappingProcess::Execute( aiScene* pScene)
// Update the material property list // Update the material property list
mapping = aiTextureMapping_UV; mapping = aiTextureMapping_UV;
((aiMaterial*)mat)->AddProperty(&idx,1,AI_MATKEY_UVWSRC(prop->mSemantic,prop->mIndex)); ((aiMaterial *)mat)->AddProperty(&idx, 1, AI_MATKEY_UVWSRC(prop->mSemantic, prop->mIndex));
} }
} }
} }

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

@ -42,27 +42,26 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
/** @file Implementation of the post processing step to drop face /** @file Implementation of the post processing step to drop face
* 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;
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// 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 DropFaceNormalsProcess::IsActive( unsigned int pFlags) const { bool DropFaceNormalsProcess::IsActive(unsigned int pFlags) const {
return (pFlags & aiProcess_DropNormals) != 0; return (pFlags & aiProcess_DropNormals) != 0;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data. // Executes the post processing step on the given imported data.
void DropFaceNormalsProcess::Execute( aiScene* pScene) { void DropFaceNormalsProcess::Execute(aiScene *pScene) {
ASSIMP_LOG_DEBUG("DropFaceNormalsProcess begin"); ASSIMP_LOG_DEBUG("DropFaceNormalsProcess begin");
if (pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) { if (pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) {
@ -70,21 +69,21 @@ void DropFaceNormalsProcess::Execute( aiScene* pScene) {
} }
bool bHas = false; bool bHas = false;
for( unsigned int a = 0; a < pScene->mNumMeshes; a++) { for (unsigned int a = 0; a < pScene->mNumMeshes; a++) {
bHas |= this->DropMeshFaceNormals( pScene->mMeshes[a]); bHas |= this->DropMeshFaceNormals(pScene->mMeshes[a]);
} }
if (bHas) { if (bHas) {
ASSIMP_LOG_INFO("DropFaceNormalsProcess finished. " ASSIMP_LOG_INFO("DropFaceNormalsProcess finished. "
"Face normals have been removed"); "Face normals have been removed");
} else { } else {
ASSIMP_LOG_DEBUG("DropFaceNormalsProcess finished. " ASSIMP_LOG_DEBUG("DropFaceNormalsProcess finished. "
"No normals were present"); "No normals were present");
} }
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data. // Executes the post processing step on the given imported data.
bool DropFaceNormalsProcess::DropMeshFaceNormals (aiMesh* mesh) { bool DropFaceNormalsProcess::DropMeshFaceNormals(aiMesh *mesh) {
ai_assert(nullptr != mesh); ai_assert(nullptr != mesh);
if (nullptr == mesh->mNormals) { if (nullptr == mesh->mNormals) {

View File

@ -41,11 +41,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/** @file FindDegenerates.cpp /** @file FindDegenerates.cpp
* @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>
@ -54,35 +54,35 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using namespace Assimp; using namespace Assimp;
// Correct node indices to meshes and remove references to deleted mesh // Correct node indices to meshes and remove references to deleted mesh
static void updateSceneGraph(aiNode* pNode, const std::unordered_map<unsigned int, unsigned int>& meshMap); static void updateSceneGraph(aiNode *pNode, const std::unordered_map<unsigned int, unsigned int> &meshMap);
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer // Constructor to be privately used by Importer
FindDegeneratesProcess::FindDegeneratesProcess() : FindDegeneratesProcess::FindDegeneratesProcess() :
mConfigRemoveDegenerates( false ), mConfigRemoveDegenerates(false),
mConfigCheckAreaOfTriangle( false ){ mConfigCheckAreaOfTriangle(false) {
// empty // empty
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field. // Returns whether the processing step is present in the given flag field.
bool FindDegeneratesProcess::IsActive( unsigned int pFlags) const { bool FindDegeneratesProcess::IsActive(unsigned int pFlags) const {
return 0 != (pFlags & aiProcess_FindDegenerates); return 0 != (pFlags & aiProcess_FindDegenerates);
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Setup import configuration // Setup import configuration
void FindDegeneratesProcess::SetupProperties(const Importer* pImp) { void FindDegeneratesProcess::SetupProperties(const Importer *pImp) {
// Get the current value of AI_CONFIG_PP_FD_REMOVE // Get the current value of AI_CONFIG_PP_FD_REMOVE
mConfigRemoveDegenerates = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_REMOVE,0)); mConfigRemoveDegenerates = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_REMOVE, 0));
mConfigCheckAreaOfTriangle = ( 0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_CHECKAREA) ); mConfigCheckAreaOfTriangle = (0 != pImp->GetPropertyInteger(AI_CONFIG_PP_FD_CHECKAREA));
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data. // Executes the post processing step on the given imported data.
void FindDegeneratesProcess::Execute( aiScene* pScene) { void FindDegeneratesProcess::Execute(aiScene *pScene) {
ASSIMP_LOG_DEBUG("FindDegeneratesProcess begin"); ASSIMP_LOG_DEBUG("FindDegeneratesProcess begin");
if ( nullptr == pScene) { if (nullptr == pScene) {
return; return;
} }
@ -112,7 +112,7 @@ void FindDegeneratesProcess::Execute( aiScene* pScene) {
ASSIMP_LOG_DEBUG("FindDegeneratesProcess finished"); ASSIMP_LOG_DEBUG("FindDegeneratesProcess finished");
} }
static void updateSceneGraph(aiNode* pNode, const std::unordered_map<unsigned int, unsigned int>& meshMap) { static void updateSceneGraph(aiNode *pNode, const std::unordered_map<unsigned int, unsigned int> &meshMap) {
unsigned int targetIndex = 0; unsigned int targetIndex = 0;
for (unsigned i = 0; i < pNode->mNumMeshes; ++i) { for (unsigned i = 0; i < pNode->mNumMeshes; ++i) {
const unsigned int sourceMeshIndex = pNode->mMeshes[i]; const unsigned int sourceMeshIndex = pNode->mMeshes[i];
@ -123,7 +123,7 @@ static void updateSceneGraph(aiNode* pNode, const std::unordered_map<unsigned in
} }
} }
pNode->mNumMeshes = targetIndex; pNode->mNumMeshes = targetIndex;
//recurse to all children // recurse to all children
for (unsigned i = 0; i < pNode->mNumChildren; ++i) { for (unsigned i = 0; i < pNode->mNumChildren; ++i) {
updateSceneGraph(pNode->mChildren[i], meshMap); updateSceneGraph(pNode->mChildren[i], meshMap);
} }
@ -131,17 +131,17 @@ static void updateSceneGraph(aiNode* pNode, const std::unordered_map<unsigned in
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported mesh // Executes the post processing step on the given imported mesh
bool FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) { bool FindDegeneratesProcess::ExecuteOnMesh(aiMesh *mesh) {
mesh->mPrimitiveTypes = 0; mesh->mPrimitiveTypes = 0;
std::vector<bool> remove_me; std::vector<bool> remove_me;
if (mConfigRemoveDegenerates) { if (mConfigRemoveDegenerates) {
remove_me.resize( mesh->mNumFaces, false ); remove_me.resize(mesh->mNumFaces, false);
} }
unsigned int deg = 0, limit; unsigned int deg = 0, limit;
for ( unsigned int a = 0; a < mesh->mNumFaces; ++a ) { for (unsigned int a = 0; a < mesh->mNumFaces; ++a) {
aiFace& face = mesh->mFaces[a]; aiFace &face = mesh->mFaces[a];
bool first = true; bool first = true;
// check whether the face contains degenerated entries // check whether the face contains degenerated entries
@ -151,43 +151,43 @@ bool FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) {
// double points may not come directly after another. // double points may not come directly after another.
limit = face.mNumIndices; limit = face.mNumIndices;
if (face.mNumIndices > 4) { if (face.mNumIndices > 4) {
limit = std::min( limit, i+2 ); limit = std::min(limit, i + 2);
} }
for (unsigned int t = i+1; t < limit; ++t) { for (unsigned int t = i + 1; t < limit; ++t) {
if (mesh->mVertices[face.mIndices[ i ] ] == mesh->mVertices[ face.mIndices[ t ] ]) { if (mesh->mVertices[face.mIndices[i]] == mesh->mVertices[face.mIndices[t]]) {
// we have found a matching vertex position // we have found a matching vertex position
// remove the corresponding index from the array // remove the corresponding index from the array
--face.mNumIndices; --face.mNumIndices;
--limit; --limit;
for (unsigned int m = t; m < face.mNumIndices; ++m) { for (unsigned int m = t; m < face.mNumIndices; ++m) {
face.mIndices[ m ] = face.mIndices[ m+1 ]; face.mIndices[m] = face.mIndices[m + 1];
} }
--t; --t;
// NOTE: we set the removed vertex index to an unique value // NOTE: we set the removed vertex index to an unique value
// to make sure the developer gets notified when his // to make sure the developer gets notified when his
// application attempts to access this data. // application attempts to access this data.
face.mIndices[ face.mNumIndices ] = 0xdeadbeef; face.mIndices[face.mNumIndices] = 0xdeadbeef;
if(first) { if (first) {
++deg; ++deg;
first = false; first = false;
} }
if ( mConfigRemoveDegenerates ) { if (mConfigRemoveDegenerates) {
remove_me[ a ] = true; remove_me[a] = true;
goto evil_jump_outside; // hrhrhrh ... yeah, this rocks baby! goto evil_jump_outside; // hrhrhrh ... yeah, this rocks baby!
} }
} }
} }
if ( mConfigCheckAreaOfTriangle ) { if (mConfigCheckAreaOfTriangle) {
if ( face.mNumIndices == 3 ) { if (face.mNumIndices == 3) {
ai_real area = GeometryUtils::calculateAreaOfTriangle( face, mesh ); ai_real area = GeometryUtils::calculateAreaOfTriangle(face, mesh);
if (area < ai_epsilon) { if (area < ai_epsilon) {
if ( mConfigRemoveDegenerates ) { if (mConfigRemoveDegenerates) {
remove_me[ a ] = true; remove_me[a] = true;
++deg; ++deg;
goto evil_jump_outside; goto evil_jump_outside;
} }
@ -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;
@ -214,30 +213,28 @@ bool FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) {
mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON; mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON;
break; break;
}; };
evil_jump_outside: evil_jump_outside:
continue; continue;
} }
// 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++];
// Do a manual copy, keep the index array // Do a manual copy, keep the index array
face_dest.mNumIndices = face_src.mNumIndices; face_dest.mNumIndices = face_src.mNumIndices;
face_dest.mIndices = face_src.mIndices; face_dest.mIndices = face_src.mIndices;
if (&face_src != &face_dest) { if (&face_src != &face_dest) {
// clear source // clear source
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;
@ -247,15 +244,15 @@ evil_jump_outside:
// Just leave the rest of the array unreferenced, we don't care for now // Just leave the rest of the array unreferenced, we don't care for now
mesh->mNumFaces = n; mesh->mNumFaces = n;
if (!mesh->mNumFaces) { if (!mesh->mNumFaces) {
//The whole mesh consists of degenerated faces // The whole mesh consists of degenerated faces
//signal upward, that this mesh should be deleted. // signal upward, that this mesh should be deleted.
ASSIMP_LOG_VERBOSE_DEBUG("FindDegeneratesProcess removed a mesh full of degenerated primitives"); ASSIMP_LOG_VERBOSE_DEBUG("FindDegeneratesProcess removed a mesh full of degenerated primitives");
return true; return true;
} }
} }
if (deg && !DefaultLogger::isNullLogger()) { if (deg && !DefaultLogger::isNullLogger()) {
ASSIMP_LOG_WARN( "Found ", deg, " degenerated primitives"); ASSIMP_LOG_WARN("Found ", deg, " degenerated primitives");
} }
return false; return false;
} }

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
UpdateMeshReferences(pScene->mRootNode, meshMapping); try {
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,39 +290,44 @@ 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) {
// Reverse the mesh face winding order // Reverse the mesh face winding order
FlipWindingOrderProcess::ProcessMesh(mesh); FlipWindingOrderProcess::ProcessMesh(mesh);
}
// Update positions
if (mesh->HasPositions()) {
for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
mesh->mVertices[i] = mat * mesh->mVertices[i];
} }
}
// Update positions // Update normals and tangents
if (mesh->HasPositions()) { if (mesh->HasNormals() || mesh->HasTangentsAndBitangents()) {
const aiMatrix3x3 m = aiMatrix3x3(mat).Inverse().Transpose();
if (mesh->HasNormals()) {
normalizeVectorArray(mesh->mNormals, mesh->mNormals, mesh->mNumVertices);
}
if (mesh->HasTangentsAndBitangents()) {
for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
mesh->mVertices[i] = mat * mesh->mVertices[i]; mesh->mTangents[i] = (m * mesh->mTangents[i]).Normalize();
} mesh->mBitangents[i] = (m * mesh->mBitangents[i]).Normalize();
}
// Update normals and tangents
if (mesh->HasNormals() || mesh->HasTangentsAndBitangents()) {
const aiMatrix3x3 m = aiMatrix3x3(mat).Inverse().Transpose();
if (mesh->HasNormals()) {
for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
mesh->mNormals[i] = (m * mesh->mNormals[i]).Normalize();
}
}
if (mesh->HasTangentsAndBitangents()) {
for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
mesh->mTangents[i] = (m * mesh->mTangents[i]).Normalize();
mesh->mBitangents[i] = (m * mesh->mBitangents[i]).Normalize();
}
} }
} }
} }
@ -352,40 +350,41 @@ 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) {
aiMesh *ctz = out[n]; aiMesh *ctz = out[n];
if (ctz->mNumBones == node->mMeshes[i] && *reinterpret_cast<aiMatrix4x4 *>(ctz->mBones) == node->mTransformation) { if (ctz->mNumBones == node->mMeshes[i] && *reinterpret_cast<aiMatrix4x4 *>(ctz->mBones) == node->mTransformation) {
// ok, use this one. Update node mesh index // ok, use this one. Update node mesh index
node->mMeshes[i] = numIn + n; node->mMeshes[i] = numIn + n;
}
} }
if (node->mMeshes[i] < numIn) { }
// Worst case. Need to operate on a full copy of the mesh if (node->mMeshes[i] < numIn) {
ASSIMP_LOG_INFO("PretransformVertices: Copying mesh due to mismatching transforms"); // Worst case. Need to operate on a full copy of the mesh
aiMesh *ntz; ASSIMP_LOG_INFO("PretransformVertices: Copying mesh due to mismatching transforms");
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);
out.push_back(ntz); out.push_back(ntz);
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]);
}
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -394,8 +393,9 @@ 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]);
}
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -405,8 +405,27 @@ 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;
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -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++; }
++numFaces;
} }
iFaces = 0; numFaces = 0;
while ((*j) & (0x1000000 << iFaces)) while ((*j) & (0x1000000 << numFaces))
pcMesh->mColors[iFaces++] = new aiColor4D[iVertices]; 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,
@ -86,9 +85,9 @@ void ScaleProcess::Execute( aiScene* pScene ) {
return; // nothing to scale return; // nothing to scale
} }
ai_assert( mScale != 0 ); ai_assert(mScale != 0 );
ai_assert( nullptr != pScene ); ai_assert(nullptr != pScene );
ai_assert( nullptr != pScene->mRootNode ); ai_assert(nullptr != pScene->mRootNode );
if ( nullptr == pScene ) { if ( nullptr == pScene ) {
return; return;
@ -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,8 +99,9 @@ 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]);
}
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -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,22 +72,22 @@ 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) {
// it seems something has been split. rebuild the mesh list
delete[] pScene->mMeshes;
pScene->mNumMeshes = (unsigned int)avList.size();
pScene->mMeshes = new aiMesh*[avList.size()];
for (unsigned int i = 0; i < avList.size();++i) {
pScene->mMeshes[i] = avList[i].first;
}
// now we need to update all nodes
this->UpdateNode(pScene->mRootNode,avList);
ASSIMP_LOG_INFO("SplitLargeMeshesProcess_Triangle finished. Meshes have been split");
} else {
ASSIMP_LOG_DEBUG("SplitLargeMeshesProcess_Triangle finished. There was nothing to do"); ASSIMP_LOG_DEBUG("SplitLargeMeshesProcess_Triangle finished. There was nothing to do");
} }
// it seems something has been split. rebuild the mesh list
delete[] pScene->mMeshes;
pScene->mNumMeshes = (unsigned int)avList.size();
pScene->mMeshes = new aiMesh*[avList.size()];
for (unsigned int i = 0; i < avList.size();++i) {
pScene->mMeshes[i] = avList[i].first;
}
// now we need to update all nodes
this->UpdateNode(pScene->mRootNode,avList);
ASSIMP_LOG_INFO("SplitLargeMeshesProcess_Triangle finished. Meshes have been split");
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@ -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,18 +108,21 @@ 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) {
if (!parray) { return;
ReportError("aiScene::%s is nullptr (aiScene::%s is %i)", }
firstName, secondName, size);
} if (!parray) {
for (unsigned int i = 0; i < size; ++i) { ReportError("aiScene::%s is nullptr (aiScene::%s is %i)",
if (!parray[i]) { firstName, secondName, size);
ReportError("aiScene::%s[%i] is nullptr (aiScene::%s is %i)", }
firstName, i, secondName, size);
} for (unsigned int i = 0; i < size; ++i) {
Validate(parray[i]); if (!parray[i]) {
ReportError("aiScene::%s[%i] is nullptr (aiScene::%s is %i)",
firstName, i, secondName, size);
} }
Validate(parray[i]);
} }
} }
@ -130,25 +131,27 @@ 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) {
if (!parray) { return;
ReportError("aiScene::%s is nullptr (aiScene::%s is %i)", }
firstName, secondName, size);
if (!parray) {
ReportError("aiScene::%s is nullptr (aiScene::%s is %i)",
firstName, secondName, size);
}
for (unsigned int i = 0; i < size; ++i) {
if (!parray[i]) {
ReportError("aiScene::%s[%u] is nullptr (aiScene::%s is %u)",
firstName, i, secondName, size);
} }
for (unsigned int i = 0; i < size; ++i) { Validate(parray[i]);
if (!parray[i]) {
ReportError("aiScene::%s[%u] is nullptr (aiScene::%s is %u)",
firstName, i, secondName, size);
}
Validate(parray[i]);
// check whether there are duplicate names // check whether there are duplicate names
for (unsigned int a = i + 1; a < size; ++a) { for (unsigned int a = i + 1; a < size; ++a) {
if (parray[i]->mName == parray[a]->mName) { if (parray[i]->mName == parray[a]->mName) {
ReportError("aiScene::%s[%u] has the same name as " ReportError("aiScene::%s[%u] has the same name as "
"aiScene::%s[%u]", "aiScene::%s[%u]",
firstName, i, secondName, a); firstName, i, secondName, a);
}
} }
} }
} }
@ -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,9 +15,11 @@ 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 )
set ( build_shared_libs_default OFF ) message("Building shared lib.")
set ( build_shared_libs_default OFF )
else() else()
set ( build_shared_libs_default ON ) message("Building static lib.")
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} )
option( COVERALLS "Generate coveralls data" OFF ) option( COVERALLS "Generate coveralls data" OFF )
@ -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,11 +59,11 @@ 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 );
if( 0 == strncmp( FileOption, argv[ 1 ], strlen( FileOption ) ) ) { if( 0 == strncmp( FileOption, argv[1], strlen( FileOption ) ) ) {
filename = argv[ 2 ]; filename = argv[ 2 ];
} }
std::cout << "file to import: " << filename << std::endl; std::cout << "file to import: " << filename << std::endl;
@ -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,7 +30,10 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <sstream> #include <sstream>
#ifdef _WIN32 #ifdef _WIN32
#include <windows.h> # ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# include <windows.h>
#endif // _WIN32 #endif // _WIN32
BEGIN_ODDLPARSER_NS BEGIN_ODDLPARSER_NS
@ -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,20 +341,25 @@ char *OpenDDLParser::parseStructure(char *in, char *end) {
bool error(false); bool error(false);
in = lookForNextToken(in, end); in = lookForNextToken(in, end);
if (*in == *Grammar::OpenBracketToken) { if (in != end) {
// loop over all children ( data and nodes ) if (*in == *Grammar::OpenBracketToken) {
do { // loop over all children ( data and nodes )
in = parseStructureBody(in, end, error); do {
if (in == nullptr) { in = parseStructureBody(in, end, error);
return nullptr; if (in == nullptr) {
return nullptr;
}
} while (in != end &&
*in != *Grammar::CloseBracketToken);
if (in != end) {
++in;
} }
} while (*in != *Grammar::CloseBracketToken); } else {
++in; ++in;
} else { logInvalidTokenError(in, std::string(Grammar::OpenBracketToken), m_logCallback);
++in; error = true;
logInvalidTokenError(in, std::string(Grammar::OpenBracketToken), m_logCallback); return nullptr;
error = true; }
return nullptr;
} }
in = lookForNextToken(in, end); in = lookForNextToken(in, end);
@ -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,11 +980,12 @@ 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;
} }
} }
++in; if (in != end)
++in;
} }
return in; return 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