From e9b67cdb89b07ac14d193eb058280df824a26377 Mon Sep 17 00:00:00 2001 From: Yingying Wang Date: Mon, 11 May 2020 14:34:35 -0700 Subject: [PATCH 01/44] gltf2 import sparse accessor --- code/AssetLib/glTF2/glTF2Asset.h | 18 ++++++ code/AssetLib/glTF2/glTF2Asset.inl | 93 +++++++++++++++++++++++++++++- 2 files changed, 109 insertions(+), 2 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Asset.h b/code/AssetLib/glTF2/glTF2Asset.h index 48dab24a7..bae4c4eca 100644 --- a/code/AssetLib/glTF2/glTF2Asset.h +++ b/code/AssetLib/glTF2/glTF2Asset.h @@ -367,6 +367,7 @@ struct Object { //! An accessor provides a typed view into a BufferView or a subset of a BufferView //! similar to how WebGL's vertexAttribPointer() defines an attribute in a buffer. struct Accessor : public Object { + struct Sparse; Ref bufferView; //!< The ID of the bufferView. (required) size_t byteOffset; //!< The offset relative to the start of the bufferView in bytes. (required) ComponentType componentType; //!< The datatype of components in the attribute. (required) @@ -374,6 +375,7 @@ struct Accessor : public Object { AttribType::Value type; //!< Specifies if the attribute is a scalar, vector, or matrix. (required) std::vector max; //!< Maximum value of each component in this attribute. std::vector min; //!< Minimum value of each component in this attribute. + std::unique_ptr sparse; unsigned int GetNumComponents(); unsigned int GetBytesPerComponent(); @@ -423,6 +425,21 @@ struct Accessor : public Object { Accessor() {} void Read(Value &obj, Asset &r); + + //sparse + struct Sparse { + size_t count; + ComponentType indicesType; + Ref indices; + size_t indicesByteOffset; + Ref values; + size_t valuesByteOffset; + + std::vector data; //!< Actual data, which may be defaulted to an array of zeros or the original data, with the sparse buffer view applied on top of it. + + void PopulateData(size_t numBytes, uint8_t *bytes); + void PatchData(unsigned int elementSize); + }; }; //! A buffer points to binary geometry, animation, or skins. @@ -555,6 +572,7 @@ struct BufferView : public Object { BufferViewTarget target; //! The target that the WebGL buffer should be bound to. void Read(Value &obj, Asset &r); + uint8_t *GetPointer(size_t accOffset); }; struct Camera : public Object { diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index 881fd4efc..428001e6b 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -550,9 +550,62 @@ inline void BufferView::Read(Value &obj, Asset &r) { byteStride = MemberOrDefault(obj, "byteStride", 0u); } +inline uint8_t *BufferView::GetPointer(size_t accOffset) { + if (!buffer) return 0; + uint8_t *basePtr = buffer->GetPointer(); + if (!basePtr) return 0; + + size_t offset = accOffset + byteOffset; + if (buffer->EncodedRegion_Current != nullptr) { + const size_t begin = buffer->EncodedRegion_Current->Offset; + const size_t end = begin + buffer->EncodedRegion_Current->DecodedData_Length; + if ((offset >= begin) && (offset < end)) + return &buffer->EncodedRegion_Current->DecodedData[offset - begin]; + } + + return basePtr + offset; +} // // struct Accessor // +inline void Accessor::Sparse::PopulateData(size_t numBytes, uint8_t *bytes) { + if (bytes) { + data.assign(bytes, bytes + numBytes); + } else { + data.resize(numBytes, 0x00); + } +} + +inline void Accessor::Sparse::PatchData(unsigned int elementSize) { + uint8_t *pIndices = indices->GetPointer(indicesByteOffset); + const unsigned int indexSize = int(ComponentTypeSize(indicesType)); + uint8_t *indicesEnd = pIndices + count * indexSize; + + uint8_t *pValues = values->GetPointer(valuesByteOffset); + while (pIndices != indicesEnd) { + size_t offset; + switch (indicesType) { + case ComponentType_UNSIGNED_BYTE: + offset = *pIndices; + break; + case ComponentType_UNSIGNED_SHORT: + offset = *reinterpret_cast(pIndices); + break; + case ComponentType_UNSIGNED_INT: + offset = *reinterpret_cast(pIndices); + break; + default: + // have fun with float and negative values from signed types as indices. + throw DeadlyImportError("Unsupported component type in index."); + } + + offset *= elementSize; + std::memcpy(data.data() + offset, pValues, elementSize); + + pValues += elementSize; + pIndices += indexSize; + } +} inline void Accessor::Read(Value &obj, Asset &r) { @@ -566,6 +619,40 @@ inline void Accessor::Read(Value &obj, Asset &r) { const char *typestr; type = ReadMember(obj, "type", typestr) ? AttribType::FromString(typestr) : AttribType::SCALAR; + + if (Value *sparseValue = FindObject(obj, "sparse")) { + sparse.reset(new Sparse); + // count + ReadMember(*sparseValue, "count", sparse->count); + + // indices + if (Value *indicesValue = FindObject(*sparseValue, "indices")) { + //indices bufferView + Value *indiceViewID = FindUInt(*indicesValue, "bufferView"); + sparse->indices = r.bufferViews.Retrieve(indiceViewID->GetUint()); + //indices byteOffset + sparse->indicesByteOffset = MemberOrDefault(*indicesValue, "byteOffset", size_t(0)); + //indices componentType + sparse->indicesType = MemberOrDefault(*indicesValue, "componentType", ComponentType_BYTE); + } + + // value + if (Value *valuesValue = FindObject(*sparseValue, "values")) { + //value bufferView + Value *valueViewID = FindUInt(*valuesValue, "bufferView"); + sparse->values = r.bufferViews.Retrieve(valueViewID->GetUint()); + //value byteOffset + sparse->valuesByteOffset = MemberOrDefault(*valuesValue, "byteOffset", size_t(0)); + } + + // indicesType + sparse->indicesType = MemberOrDefault(*sparseValue, "componentType", ComponentType_UNSIGNED_SHORT); + + const unsigned int elementSize = GetElementSize(); + const size_t dataSize = count * elementSize; + sparse->PopulateData(dataSize, bufferView ? bufferView->GetPointer(byteOffset) : 0); + sparse->PatchData(elementSize); + } } inline unsigned int Accessor::GetNumComponents() { @@ -581,6 +668,9 @@ inline unsigned int Accessor::GetElementSize() { } inline uint8_t *Accessor::GetPointer() { + if (sparse) + return sparse->data.data(); + if (!bufferView || !bufferView->buffer) return 0; uint8_t *basePtr = bufferView->buffer->GetPointer(); if (!basePtr) return 0; @@ -634,8 +724,7 @@ void Accessor::ExtractData(T *&outData) const size_t targetElemSize = sizeof(T); ai_assert(elemSize <= targetElemSize); - - ai_assert(count * stride <= bufferView->byteLength); + ai_assert(count * stride <= (bufferView ? bufferView->byteLength : sparse->data.size())); outData = new T[count]; if (stride == elemSize && targetElemSize == elemSize) { From 0897c4c7be31880e36f6bac249698b1dc4fc5b2f Mon Sep 17 00:00:00 2001 From: Yingying Wang Date: Mon, 18 May 2020 11:31:44 -0700 Subject: [PATCH 02/44] merge --- code/AssetLib/glTF2/glTF2Asset.h | 10 ++++-- code/AssetLib/glTF2/glTF2Asset.inl | 55 ++++++++++++++++++++++++++++-- 2 files changed, 60 insertions(+), 5 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Asset.h b/code/AssetLib/glTF2/glTF2Asset.h index 9fc85c548..0d3b5d8b8 100644 --- a/code/AssetLib/glTF2/glTF2Asset.h +++ b/code/AssetLib/glTF2/glTF2Asset.h @@ -1,4 +1,4 @@ -/* +/* Open Asset Import Library (assimp) ---------------------------------------------------------------------- @@ -370,7 +370,8 @@ struct Object { //! An accessor provides a typed view into a BufferView or a subset of a BufferView //! similar to how WebGL's vertexAttribPointer() defines an attribute in a buffer. struct Accessor : public Object { - struct Sparse; + struct Sparse; //wangyi 0506 + Ref bufferView; //!< The ID of the bufferView. (required) size_t byteOffset; //!< The offset relative to the start of the bufferView in bytes. (required) ComponentType componentType; //!< The datatype of components in the attribute. (required) @@ -378,7 +379,7 @@ struct Accessor : public Object { AttribType::Value type; //!< Specifies if the attribute is a scalar, vector, or matrix. (required) std::vector max; //!< Maximum value of each component in this attribute. std::vector min; //!< Minimum value of each component in this attribute. - std::unique_ptr sparse; + std::unique_ptr sparse; //wangyi 0506 unsigned int GetNumComponents(); unsigned int GetBytesPerComponent(); @@ -390,6 +391,7 @@ struct Accessor : public Object { void ExtractData(T *&outData); void WriteData(size_t count, const void *src_buffer, size_t src_stride); + //wangyi 0506 void WriteSparseValues(size_t count, const void *src_data, size_t src_dataStride); void WriteSparseIndices(size_t count, const void *src_idx, size_t src_idxStride); @@ -430,6 +432,7 @@ struct Accessor : public Object { Accessor() {} void Read(Value &obj, Asset &r); + //wangyi 0506 //sparse struct Sparse { size_t count; @@ -580,6 +583,7 @@ struct BufferView : public Object { BufferViewTarget target; //! The target that the WebGL buffer should be bound to. void Read(Value &obj, Asset &r); + //wangyi 0506 uint8_t *GetPointer(size_t accOffset); }; diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index 75ff0b6fc..29d745234 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -1,22 +1,30 @@ /* Open Asset Import Library (assimp) ---------------------------------------------------------------------- + Copyright (c) 2006-2020, assimp team + + All rights reserved. + Redistribution and use of this software in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * Neither the name of the assimp team, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission of the assimp team. + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR @@ -28,6 +36,7 @@ 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. + ---------------------------------------------------------------------- */ @@ -547,6 +556,7 @@ inline void BufferView::Read(Value &obj, Asset &r) { byteStride = MemberOrDefault(obj, "byteStride", 0u); } +//wangyi 0506 inline uint8_t *BufferView::GetPointer(size_t accOffset) { if (!buffer) return 0; uint8_t *basePtr = buffer->GetPointer(); @@ -566,7 +576,7 @@ inline uint8_t *BufferView::GetPointer(size_t accOffset) { // // struct Accessor // - +//wangyi 0506 inline void Accessor::Sparse::PopulateData(size_t numBytes, uint8_t *bytes) { if (bytes) { data.assign(bytes, bytes + numBytes); @@ -605,7 +615,22 @@ inline void Accessor::Sparse::PatchData(unsigned int elementSize) { pIndices += indexSize; } } +/* old +inline void Accessor::Read(Value &obj, Asset &r) { + if (Value *bufferViewVal = FindUInt(obj, "bufferView")) { + bufferView = r.bufferViews.Retrieve(bufferViewVal->GetUint()); + } + + byteOffset = MemberOrDefault(obj, "byteOffset", size_t(0)); + componentType = MemberOrDefault(obj, "componentType", ComponentType_BYTE); + count = MemberOrDefault(obj, "count", size_t(0)); + + const char *typestr; + type = ReadMember(obj, "type", typestr) ? AttribType::FromString(typestr) : AttribType::SCALAR; +}*/ + +// wangyi 0506 inline void Accessor::Read(Value &obj, Asset &r) { if (Value *bufferViewVal = FindUInt(obj, "bufferView")) { @@ -619,6 +644,7 @@ inline void Accessor::Read(Value &obj, Asset &r) { const char *typestr; type = ReadMember(obj, "type", typestr) ? AttribType::FromString(typestr) : AttribType::SCALAR; + //wangyi 0506 if (Value *sparseValue = FindObject(obj, "sparse")) { sparse.reset(new Sparse); // count @@ -668,6 +694,27 @@ inline unsigned int Accessor::GetElementSize() { return GetNumComponents() * GetBytesPerComponent(); } +/* +inline uint8_t *Accessor::GetPointer() { + if (!bufferView || !bufferView->buffer) return 0; + uint8_t *basePtr = bufferView->buffer->GetPointer(); + if (!basePtr) return 0; + + size_t offset = byteOffset + bufferView->byteOffset; + + // Check if region is encoded. + if (bufferView->buffer->EncodedRegion_Current != nullptr) { + const size_t begin = bufferView->buffer->EncodedRegion_Current->Offset; + const size_t end = begin + bufferView->buffer->EncodedRegion_Current->DecodedData_Length; + + if ((offset >= begin) && (offset < end)) + return &bufferView->buffer->EncodedRegion_Current->DecodedData[offset - begin]; + } + + return basePtr + offset; +}*/ + +// wangyi 0506 inline uint8_t *Accessor::GetPointer() { if (sparse) return sparse->data.data(); @@ -724,6 +771,8 @@ void Accessor::ExtractData(T *&outData) { const size_t targetElemSize = sizeof(T); ai_assert(elemSize <= targetElemSize); + + //wangyi 0506 ai_assert(count * stride <= (bufferView ? bufferView->byteLength : sparse->data.size())); outData = new T[count]; @@ -749,6 +798,7 @@ inline void Accessor::WriteData(size_t _count, const void *src_buffer, size_t sr CopyData(_count, src, src_stride, dst, dst_stride); } +//wangyi 0506 inline void Accessor::WriteSparseValues(size_t _count, const void *src_data, size_t src_dataStride) { if (!sparse) return; @@ -763,6 +813,7 @@ inline void Accessor::WriteSparseValues(size_t _count, const void *src_data, siz CopyData(_count, value_src, src_dataStride, value_dst, value_dst_stride); } +//wangyi 0506 inline void Accessor::WriteSparseIndices(size_t _count, const void *src_idx, size_t src_idxStride) { if (!sparse) return; @@ -1673,4 +1724,4 @@ inline std::string Asset::FindUniqueID(const std::string &str, const char *suffi #pragma warning(pop) #endif // _WIN32 -} // namespace glTF2 \ No newline at end of file +} // namespace glTF2 From e39c6328097ce3cd8ff7c872f62611aac6c8009f Mon Sep 17 00:00:00 2001 From: Yingying Wang Date: Mon, 18 May 2020 11:50:51 -0700 Subject: [PATCH 03/44] cleanup --- code/AssetLib/glTF2/glTF2Asset.h | 9 +--- code/AssetLib/glTF2/glTF2Asset.inl | 70 ------------------------------ 2 files changed, 2 insertions(+), 77 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Asset.h b/code/AssetLib/glTF2/glTF2Asset.h index 0d3b5d8b8..99bcaf072 100644 --- a/code/AssetLib/glTF2/glTF2Asset.h +++ b/code/AssetLib/glTF2/glTF2Asset.h @@ -370,7 +370,7 @@ struct Object { //! An accessor provides a typed view into a BufferView or a subset of a BufferView //! similar to how WebGL's vertexAttribPointer() defines an attribute in a buffer. struct Accessor : public Object { - struct Sparse; //wangyi 0506 + struct Sparse; Ref bufferView; //!< The ID of the bufferView. (required) size_t byteOffset; //!< The offset relative to the start of the bufferView in bytes. (required) @@ -379,7 +379,7 @@ struct Accessor : public Object { AttribType::Value type; //!< Specifies if the attribute is a scalar, vector, or matrix. (required) std::vector max; //!< Maximum value of each component in this attribute. std::vector min; //!< Minimum value of each component in this attribute. - std::unique_ptr sparse; //wangyi 0506 + std::unique_ptr sparse; unsigned int GetNumComponents(); unsigned int GetBytesPerComponent(); @@ -391,9 +391,6 @@ struct Accessor : public Object { void ExtractData(T *&outData); void WriteData(size_t count, const void *src_buffer, size_t src_stride); - //wangyi 0506 - void WriteSparseValues(size_t count, const void *src_data, size_t src_dataStride); - void WriteSparseIndices(size_t count, const void *src_idx, size_t src_idxStride); //! Helper class to iterate the data class Indexer { @@ -432,7 +429,6 @@ struct Accessor : public Object { Accessor() {} void Read(Value &obj, Asset &r); - //wangyi 0506 //sparse struct Sparse { size_t count; @@ -583,7 +579,6 @@ struct BufferView : public Object { BufferViewTarget target; //! The target that the WebGL buffer should be bound to. void Read(Value &obj, Asset &r); - //wangyi 0506 uint8_t *GetPointer(size_t accOffset); }; diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index 29d745234..9f852ebf8 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -556,7 +556,6 @@ inline void BufferView::Read(Value &obj, Asset &r) { byteStride = MemberOrDefault(obj, "byteStride", 0u); } -//wangyi 0506 inline uint8_t *BufferView::GetPointer(size_t accOffset) { if (!buffer) return 0; uint8_t *basePtr = buffer->GetPointer(); @@ -576,7 +575,6 @@ inline uint8_t *BufferView::GetPointer(size_t accOffset) { // // struct Accessor // -//wangyi 0506 inline void Accessor::Sparse::PopulateData(size_t numBytes, uint8_t *bytes) { if (bytes) { data.assign(bytes, bytes + numBytes); @@ -615,22 +613,7 @@ inline void Accessor::Sparse::PatchData(unsigned int elementSize) { pIndices += indexSize; } } -/* old -inline void Accessor::Read(Value &obj, Asset &r) { - if (Value *bufferViewVal = FindUInt(obj, "bufferView")) { - bufferView = r.bufferViews.Retrieve(bufferViewVal->GetUint()); - } - - byteOffset = MemberOrDefault(obj, "byteOffset", size_t(0)); - componentType = MemberOrDefault(obj, "componentType", ComponentType_BYTE); - count = MemberOrDefault(obj, "count", size_t(0)); - - const char *typestr; - type = ReadMember(obj, "type", typestr) ? AttribType::FromString(typestr) : AttribType::SCALAR; -}*/ - -// wangyi 0506 inline void Accessor::Read(Value &obj, Asset &r) { if (Value *bufferViewVal = FindUInt(obj, "bufferView")) { @@ -644,7 +627,6 @@ inline void Accessor::Read(Value &obj, Asset &r) { const char *typestr; type = ReadMember(obj, "type", typestr) ? AttribType::FromString(typestr) : AttribType::SCALAR; - //wangyi 0506 if (Value *sparseValue = FindObject(obj, "sparse")) { sparse.reset(new Sparse); // count @@ -694,27 +676,6 @@ inline unsigned int Accessor::GetElementSize() { return GetNumComponents() * GetBytesPerComponent(); } -/* -inline uint8_t *Accessor::GetPointer() { - if (!bufferView || !bufferView->buffer) return 0; - uint8_t *basePtr = bufferView->buffer->GetPointer(); - if (!basePtr) return 0; - - size_t offset = byteOffset + bufferView->byteOffset; - - // Check if region is encoded. - if (bufferView->buffer->EncodedRegion_Current != nullptr) { - const size_t begin = bufferView->buffer->EncodedRegion_Current->Offset; - const size_t end = begin + bufferView->buffer->EncodedRegion_Current->DecodedData_Length; - - if ((offset >= begin) && (offset < end)) - return &bufferView->buffer->EncodedRegion_Current->DecodedData[offset - begin]; - } - - return basePtr + offset; -}*/ - -// wangyi 0506 inline uint8_t *Accessor::GetPointer() { if (sparse) return sparse->data.data(); @@ -771,8 +732,6 @@ void Accessor::ExtractData(T *&outData) { const size_t targetElemSize = sizeof(T); ai_assert(elemSize <= targetElemSize); - - //wangyi 0506 ai_assert(count * stride <= (bufferView ? bufferView->byteLength : sparse->data.size())); outData = new T[count]; @@ -798,35 +757,6 @@ inline void Accessor::WriteData(size_t _count, const void *src_buffer, size_t sr CopyData(_count, src, src_stride, dst, dst_stride); } -//wangyi 0506 -inline void Accessor::WriteSparseValues(size_t _count, const void *src_data, size_t src_dataStride) { - if (!sparse) - return; - - // values - uint8_t *value_buffer_ptr = sparse->values->buffer->GetPointer(); - size_t value_offset = sparse->valuesByteOffset + sparse->values->byteOffset; - size_t value_dst_stride = GetNumComponents() * GetBytesPerComponent(); - const uint8_t *value_src = reinterpret_cast(src_data); - uint8_t *value_dst = reinterpret_cast(value_buffer_ptr + value_offset); - ai_assert(value_dst + _count * value_dst_stride <= value_buffer_ptr + sparse->values->buffer->byteLength); - CopyData(_count, value_src, src_dataStride, value_dst, value_dst_stride); -} - -//wangyi 0506 -inline void Accessor::WriteSparseIndices(size_t _count, const void *src_idx, size_t src_idxStride) { - if (!sparse) - return; - - // indices - uint8_t *indices_buffer_ptr = sparse->indices->buffer->GetPointer(); - size_t indices_offset = sparse->indicesByteOffset + sparse->indices->byteOffset; - size_t indices_dst_stride = 1 * sizeof(unsigned short); - const uint8_t *indices_src = reinterpret_cast(src_idx); - uint8_t *indices_dst = reinterpret_cast(indices_buffer_ptr + indices_offset); - ai_assert(indices_dst + _count * indices_dst_stride <= indices_buffer_ptr + sparse->indices->buffer->byteLength); - CopyData(_count, indices_src, src_idxStride, indices_dst, indices_dst_stride); -} inline Accessor::Indexer::Indexer(Accessor &acc) : accessor(acc), data(acc.GetPointer()), From 193deb34411264227787774cf351ea73d03eafd6 Mon Sep 17 00:00:00 2001 From: Yingying Wang Date: Mon, 18 May 2020 12:01:09 -0700 Subject: [PATCH 04/44] cleanup --- code/AssetLib/glTF2/glTF2Asset.inl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index 9f852ebf8..328c57e38 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -1169,10 +1169,10 @@ inline void Camera::Read(Value &obj, Asset & /*r*/) { cameraProperties.perspective.zfar = MemberOrDefault(*it, "zfar", 100.f); cameraProperties.perspective.znear = MemberOrDefault(*it, "znear", 0.01f); } else { - cameraProperties.ortographic.xmag = MemberOrDefault(obj, "xmag", 1.f); - cameraProperties.ortographic.ymag = MemberOrDefault(obj, "ymag", 1.f); - cameraProperties.ortographic.zfar = MemberOrDefault(obj, "zfar", 100.f); - cameraProperties.ortographic.znear = MemberOrDefault(obj, "znear", 0.01f); + cameraProperties.ortographic.xmag = MemberOrDefault(*it, "xmag", 1.f); + cameraProperties.ortographic.ymag = MemberOrDefault(*it, "ymag", 1.f); + cameraProperties.ortographic.zfar = MemberOrDefault(*it, "zfar", 100.f); + cameraProperties.ortographic.znear = MemberOrDefault(*it, "znear", 0.01f); } } @@ -1248,7 +1248,7 @@ inline void Node::Read(Value &obj, Asset &r) { Value *curSkin = FindUInt(obj, "skin"); if (nullptr != curSkin) { - this->skin = r.skins.Retrieve(curSkin->GetUint()); + this->skin = r.skins.Get(curSkin->GetUint()); } Value *curCamera = FindUInt(obj, "camera"); @@ -1539,7 +1539,7 @@ inline void Asset::Load(const std::string &pFile, bool isBinary) { } } - // Force reading of skins since they're not always directly referenced + // Read skins after nodes have been loaded to avoid infinite recursion if (Value *skinsArray = FindArray(doc, "skins")) { for (unsigned int i = 0; i < skinsArray->Size(); ++i) { skins.Retrieve(i); From e33ed9e7a1b3307683a105b5821c6c8fd2e2252c Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 25 May 2020 20:30:18 +0200 Subject: [PATCH 05/44] File is duplicated closes https://github.com/assimp/assimp/issues/3238 --- code/Collada/ColladaExporter.cpp | 1704 ------------------------------ 1 file changed, 1704 deletions(-) delete mode 100644 code/Collada/ColladaExporter.cpp diff --git a/code/Collada/ColladaExporter.cpp b/code/Collada/ColladaExporter.cpp deleted file mode 100644 index 05df6fc94..000000000 --- a/code/Collada/ColladaExporter.cpp +++ /dev/null @@ -1,1704 +0,0 @@ -/* -Open Asset Import Library (assimp) ----------------------------------------------------------------------- - -Copyright (c) 2006-2020, assimp team - -All rights reserved. - -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the -following conditions are met: - -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the - following disclaimer. - -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the - following disclaimer in the documentation and/or other - materials provided with the distribution. - -* Neither the name of the assimp team, nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior - written permission of the assimp team. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------------------------------------------------------------------- -*/ - -#ifndef ASSIMP_BUILD_NO_EXPORT -#ifndef ASSIMP_BUILD_NO_COLLADA_EXPORTER - -#include "ColladaExporter.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -using namespace Assimp; - -namespace Assimp { - -// ------------------------------------------------------------------------------------------------ -// Worker function for exporting a scene to Collada. Prototyped and registered in Exporter.cpp -void ExportSceneCollada(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/) { - std::string path = DefaultIOSystem::absolutePath(std::string(pFile)); - std::string file = DefaultIOSystem::completeBaseName(std::string(pFile)); - - // invoke the exporter - ColladaExporter iDoTheExportThing( pScene, pIOSystem, path, file); - - if (iDoTheExportThing.mOutput.fail()) { - throw DeadlyExportError("output data creation failed. Most likely the file became too large: " + std::string(pFile)); - } - - // we're still here - export successfully completed. Write result to the given IOSYstem - std::unique_ptr outfile (pIOSystem->Open(pFile,"wt")); - if(outfile == NULL) { - throw DeadlyExportError("could not open output .dae file: " + std::string(pFile)); - } - - // XXX maybe use a small wrapper around IOStream that behaves like std::stringstream in order to avoid the extra copy. - outfile->Write( iDoTheExportThing.mOutput.str().c_str(), static_cast(iDoTheExportThing.mOutput.tellp()),1); -} - -} // end of namespace Assimp - -// ------------------------------------------------------------------------------------------------ -// Encodes a string into a valid XML ID using the xsd:ID schema qualifications. -static const std::string XMLIDEncode(const std::string& name) { - const char XML_ID_CHARS[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-."; - const unsigned int XML_ID_CHARS_COUNT = sizeof(XML_ID_CHARS) / sizeof(char); - - if (name.length() == 0) { - return name; - } - - std::stringstream idEncoded; - - // xsd:ID must start with letter or underscore - if (!((name[0] >= 'A' && name[0] <= 'z') || name[0] == '_')) { - idEncoded << '_'; - } - - for (std::string::const_iterator it = name.begin(); it != name.end(); ++it) { - // xsd:ID can only contain letters, digits, underscores, hyphens and periods - if (strchr(XML_ID_CHARS, *it) != nullptr) { - idEncoded << *it; - } else { - // Select placeholder character based on invalid character to prevent name collisions - idEncoded << XML_ID_CHARS[(*it) % XML_ID_CHARS_COUNT]; - } - } - - return idEncoded.str(); -} - -// ------------------------------------------------------------------------------------------------ -// Constructor for a specific scene to export -ColladaExporter::ColladaExporter( const aiScene* pScene, IOSystem* pIOSystem, const std::string& path, const std::string& file) -: mIOSystem(pIOSystem) -, mPath(path) -, mFile(file) { - // make sure that all formatting happens using the standard, C locale and not the user's current locale - mOutput.imbue( std::locale("C") ); - mOutput.precision(ASSIMP_AI_REAL_TEXT_PRECISION); - - mScene = pScene; - mSceneOwned = false; - - // set up strings - endstr = "\n"; - - // start writing the file - WriteFile(); -} - -// ------------------------------------------------------------------------------------------------ -// Destructor -ColladaExporter::~ColladaExporter() { - if ( mSceneOwned ) { - delete mScene; - } -} - -// ------------------------------------------------------------------------------------------------ -// Starts writing the contents -void ColladaExporter::WriteFile() { - // write the DTD - mOutput << "" << endstr; - // COLLADA element start - mOutput << "" << endstr; - PushTag(); - - WriteTextures(); - WriteHeader(); - - WriteCamerasLibrary(); - WriteLightsLibrary(); - WriteMaterials(); - WriteGeometryLibrary(); - WriteControllerLibrary(); - - WriteSceneLibrary(); - - // customized, Writes the animation library - WriteAnimationsLibrary(); - - // useless Collada fu at the end, just in case we haven't had enough indirections, yet. - mOutput << startstr << "" << endstr; - PushTag(); - mOutput << startstr << "mRootNode->mName.C_Str()) + "\" />" << endstr; - PopTag(); - mOutput << startstr << "" << endstr; - PopTag(); - mOutput << "" << endstr; -} - -// ------------------------------------------------------------------------------------------------ -// Writes the asset header -void ColladaExporter::WriteHeader() { - static const ai_real epsilon = Math::getEpsilon(); - static const aiQuaternion x_rot(aiMatrix3x3( - 0, -1, 0, - 1, 0, 0, - 0, 0, 1)); - static const aiQuaternion y_rot(aiMatrix3x3( - 1, 0, 0, - 0, 1, 0, - 0, 0, 1)); - static const aiQuaternion z_rot(aiMatrix3x3( - 1, 0, 0, - 0, 0, 1, - 0, -1, 0)); - - static const unsigned int date_nb_chars = 20; - char date_str[date_nb_chars]; - std::time_t date = std::time(NULL); - std::strftime(date_str, date_nb_chars, "%Y-%m-%dT%H:%M:%S", std::localtime(&date)); - - aiVector3D scaling; - aiQuaternion rotation; - aiVector3D position; - mScene->mRootNode->mTransformation.Decompose(scaling, rotation, position); - rotation.Normalize(); - - bool add_root_node = false; - - ai_real scale = 1.0; - if(std::abs(scaling.x - scaling.y) <= epsilon && std::abs(scaling.x - scaling.z) <= epsilon && std::abs(scaling.y - scaling.z) <= epsilon) { - scale = (ai_real) ((((double) scaling.x) + ((double) scaling.y) + ((double) scaling.z)) / 3.0); - } else { - add_root_node = true; - } - - std::string up_axis = "Y_UP"; - if(rotation.Equal(x_rot, epsilon)) { - up_axis = "X_UP"; - } else if(rotation.Equal(y_rot, epsilon)) { - up_axis = "Y_UP"; - } else if(rotation.Equal(z_rot, epsilon)) { - up_axis = "Z_UP"; - } else { - add_root_node = true; - } - - if(! position.Equal(aiVector3D(0, 0, 0))) { - add_root_node = true; - } - - if(mScene->mRootNode->mNumChildren == 0) { - add_root_node = true; - } - - if(add_root_node) { - aiScene* scene; - SceneCombiner::CopyScene(&scene, mScene); - - aiNode* root = new aiNode("Scene"); - - root->mNumChildren = 1; - root->mChildren = new aiNode*[root->mNumChildren]; - - root->mChildren[0] = scene->mRootNode; - scene->mRootNode->mParent = root; - scene->mRootNode = root; - - mScene = scene; - mSceneOwned = true; - - up_axis = "Y_UP"; - scale = 1.0; - } - - mOutput << startstr << "" << endstr; - PushTag(); - mOutput << startstr << "" << endstr; - PushTag(); - - // If no Scene metadata, use root node metadata - aiMetadata* meta = mScene->mMetaData; - if (nullptr == meta) { - meta = mScene->mRootNode->mMetaData; - } - - aiString value; - if (!meta || !meta->Get("Author", value)) { - mOutput << startstr << "" << "Assimp" << "" << endstr; - } else { - mOutput << startstr << "" << XMLEscape(value.C_Str()) << "" << endstr; - } - - if (nullptr == meta || !meta->Get(AI_METADATA_SOURCE_GENERATOR, value)) { - mOutput << startstr << "" << "Assimp Exporter" << "" << endstr; - } else { - mOutput << startstr << "" << XMLEscape(value.C_Str()) << "" << endstr; - } - - if (meta) { - if (meta->Get("Comments", value)) { - mOutput << startstr << "" << XMLEscape(value.C_Str()) << "" << endstr; - } - if (meta->Get(AI_METADATA_SOURCE_COPYRIGHT, value)) { - mOutput << startstr << "" << XMLEscape(value.C_Str()) << "" << endstr; - } - if (meta->Get("SourceData", value)) { - mOutput << startstr << "" << XMLEscape(value.C_Str()) << "" << endstr; - } - } - - PopTag(); - mOutput << startstr << "" << endstr; - - if (nullptr == meta || !meta->Get("Created", value)) { - mOutput << startstr << "" << date_str << "" << endstr; - } else { - mOutput << startstr << "" << XMLEscape(value.C_Str()) << "" << endstr; - } - - // Modified date is always the date saved - mOutput << startstr << "" << date_str << "" << endstr; - - if (meta) { - if (meta->Get("Keywords", value)) { - mOutput << startstr << "" << XMLEscape(value.C_Str()) << "" << endstr; - } - if (meta->Get("Revision", value)) { - mOutput << startstr << "" << XMLEscape(value.C_Str()) << "" << endstr; - } - if (meta->Get("Subject", value)) { - mOutput << startstr << "" << XMLEscape(value.C_Str()) << "" << endstr; - } - if (meta->Get("Title", value)) { - mOutput << startstr << "" << XMLEscape(value.C_Str()) << "" << endstr; - } - } - - mOutput << startstr << "" << endstr; - mOutput << startstr << "" << up_axis << "" << endstr; - PopTag(); - mOutput << startstr << "" << endstr; -} - -// ------------------------------------------------------------------------------------------------ -// Write the embedded textures -void ColladaExporter::WriteTextures() { - static const unsigned int buffer_size = 1024; - char str[buffer_size]; - - if (mScene->HasTextures()) { - for(unsigned int i = 0; i < mScene->mNumTextures; i++) { - // It would be great to be able to create a directory in portable standard C++, but it's not the case, - // so we just write the textures in the current directory. - - aiTexture* texture = mScene->mTextures[i]; - if ( nullptr == texture ) { - continue; - } - - ASSIMP_itoa10(str, buffer_size, i + 1); - - std::string name = mFile + "_texture_" + (i < 1000 ? "0" : "") + (i < 100 ? "0" : "") + (i < 10 ? "0" : "") + str + "." + ((const char*) texture->achFormatHint); - - std::unique_ptr outfile(mIOSystem->Open(mPath + mIOSystem->getOsSeparator() + name, "wb")); - if(outfile == NULL) { - throw DeadlyExportError("could not open output texture file: " + mPath + name); - } - - if(texture->mHeight == 0) { - outfile->Write((void*) texture->pcData, texture->mWidth, 1); - } else { - Bitmap::Save(texture, outfile.get()); - } - - outfile->Flush(); - - textures.insert(std::make_pair(i, name)); - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Write the embedded textures -void ColladaExporter::WriteCamerasLibrary() { - if(mScene->HasCameras()) { - - mOutput << startstr << "" << endstr; - PushTag(); - - for( size_t a = 0; a < mScene->mNumCameras; ++a) - WriteCamera( a); - - PopTag(); - mOutput << startstr << "" << endstr; - - } -} - -void ColladaExporter::WriteCamera(size_t pIndex){ - - const aiCamera *cam = mScene->mCameras[pIndex]; - const std::string cameraName = XMLEscape(cam->mName.C_Str()); - const std::string cameraId = XMLIDEncode(cam->mName.C_Str()); - - mOutput << startstr << "" << endstr; - PushTag(); - mOutput << startstr << "" << endstr; - PushTag(); - mOutput << startstr << "" << endstr; - PushTag(); - //assimp doesn't support the import of orthographic cameras! se we write - //always perspective - mOutput << startstr << "" << endstr; - PushTag(); - mOutput << startstr << ""<< - AI_RAD_TO_DEG(cam->mHorizontalFOV) - <<"" << endstr; - mOutput << startstr << "" - << cam->mAspect - << "" << endstr; - mOutput << startstr << "" - << cam->mClipPlaneNear - << "" << endstr; - mOutput << startstr << "" - << cam->mClipPlaneFar - << "" << endstr; - PopTag(); - mOutput << startstr << "" << endstr; - PopTag(); - mOutput << startstr << "" << endstr; - PopTag(); - mOutput << startstr << "" << endstr; - PopTag(); - mOutput << startstr << "" << endstr; - -} - - -// ------------------------------------------------------------------------------------------------ -// Write the embedded textures -void ColladaExporter::WriteLightsLibrary() { - if(mScene->HasLights()) { - - mOutput << startstr << "" << endstr; - PushTag(); - - for( size_t a = 0; a < mScene->mNumLights; ++a) - WriteLight( a); - - PopTag(); - mOutput << startstr << "" << endstr; - - } -} - -void ColladaExporter::WriteLight(size_t pIndex){ - - const aiLight *light = mScene->mLights[pIndex]; - const std::string lightName = XMLEscape(light->mName.C_Str()); - const std::string lightId = XMLIDEncode(light->mName.C_Str()); - - mOutput << startstr << "" << endstr; - PushTag(); - mOutput << startstr << "" << endstr; - PushTag(); - switch(light->mType){ - case aiLightSource_AMBIENT: - WriteAmbienttLight(light); - break; - case aiLightSource_DIRECTIONAL: - WriteDirectionalLight(light); - break; - case aiLightSource_POINT: - WritePointLight(light); - break; - case aiLightSource_SPOT: - WriteSpotLight(light); - break; - case aiLightSource_AREA: - case aiLightSource_UNDEFINED: - case _aiLightSource_Force32Bit: - break; - } - PopTag(); - mOutput << startstr << "" << endstr; - - PopTag(); - mOutput << startstr << "" << endstr; - -} - -void ColladaExporter::WritePointLight(const aiLight *const light){ - const aiColor3D &color= light->mColorDiffuse; - mOutput << startstr << "" << endstr; - PushTag(); - mOutput << startstr << "" - << color.r<<" "<" << endstr; - mOutput << startstr << "" - << light->mAttenuationConstant - <<"" << endstr; - mOutput << startstr << "" - << light->mAttenuationLinear - <<"" << endstr; - mOutput << startstr << "" - << light->mAttenuationQuadratic - <<"" << endstr; - - PopTag(); - mOutput << startstr << "" << endstr; - -} - -void ColladaExporter::WriteDirectionalLight(const aiLight *const light){ - const aiColor3D &color= light->mColorDiffuse; - mOutput << startstr << "" << endstr; - PushTag(); - mOutput << startstr << "" - << color.r<<" "<" << endstr; - - PopTag(); - mOutput << startstr << "" << endstr; - -} - -void ColladaExporter::WriteSpotLight(const aiLight *const light){ - - const aiColor3D &color= light->mColorDiffuse; - mOutput << startstr << "" << endstr; - PushTag(); - mOutput << startstr << "" - << color.r<<" "<" << endstr; - mOutput << startstr << "" - << light->mAttenuationConstant - <<"" << endstr; - mOutput << startstr << "" - << light->mAttenuationLinear - <<"" << endstr; - mOutput << startstr << "" - << light->mAttenuationQuadratic - <<"" << endstr; - - const ai_real fallOffAngle = AI_RAD_TO_DEG(light->mAngleInnerCone); - mOutput << startstr <<"" - << fallOffAngle - <<"" << endstr; - double temp = light->mAngleOuterCone-light->mAngleInnerCone; - - temp = std::cos(temp); - temp = std::log(temp)/std::log(0.1); - temp = 1/temp; - mOutput << startstr << "" - << temp - <<"" << endstr; - - - PopTag(); - mOutput << startstr << "" << endstr; - -} - -void ColladaExporter::WriteAmbienttLight(const aiLight *const light){ - - const aiColor3D &color= light->mColorAmbient; - mOutput << startstr << "" << endstr; - PushTag(); - mOutput << startstr << "" - << color.r<<" "<" << endstr; - - PopTag(); - mOutput << startstr << "" << endstr; -} - -// ------------------------------------------------------------------------------------------------ -// Reads a single surface entry from the given material keys -void ColladaExporter::ReadMaterialSurface( Surface& poSurface, const aiMaterial* pSrcMat, - aiTextureType pTexture, const char* pKey, size_t pType, size_t pIndex) { - if( pSrcMat->GetTextureCount( pTexture) > 0 ) { - aiString texfile; - unsigned int uvChannel = 0; - pSrcMat->GetTexture( pTexture, 0, &texfile, NULL, &uvChannel); - - std::string index_str(texfile.C_Str()); - - if(index_str.size() != 0 && index_str[0] == '*') { - unsigned int index; - - index_str = index_str.substr(1, std::string::npos); - - try { - index = (unsigned int) strtoul10_64(index_str.c_str()); - } catch(std::exception& error) { - throw DeadlyExportError(error.what()); - } - - std::map::const_iterator name = textures.find(index); - - if(name != textures.end()) { - poSurface.texture = name->second; - } else { - throw DeadlyExportError("could not find embedded texture at index " + index_str); - } - } else { - poSurface.texture = texfile.C_Str(); - } - - poSurface.channel = uvChannel; - poSurface.exist = true; - } else { - if( pKey ) - poSurface.exist = pSrcMat->Get( pKey, static_cast(pType), static_cast(pIndex), poSurface.color) == aiReturn_SUCCESS; - } -} - -static bool isalnum_C(char c) { - return ( nullptr != strchr("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",c) ); -} - -// ------------------------------------------------------------------------------------------------ -// Writes an image entry for the given surface -void ColladaExporter::WriteImageEntry( const Surface& pSurface, const std::string& pNameAdd) { - if( !pSurface.texture.empty() ) - { - mOutput << startstr << "" << endstr; - PushTag(); - mOutput << startstr << ""; - - // URL encode image file name first, then XML encode on top - std::stringstream imageUrlEncoded; - for( std::string::const_iterator it = pSurface.texture.begin(); it != pSurface.texture.end(); ++it ) - { - if( isalnum_C( (unsigned char) *it) || *it == ':' || *it == '_' || *it == '-' || *it == '.' || *it == '/' || *it == '\\' ) - imageUrlEncoded << *it; - else - imageUrlEncoded << '%' << std::hex << size_t( (unsigned char) *it) << std::dec; - } - mOutput << XMLEscape(imageUrlEncoded.str()); - mOutput << "" << endstr; - PopTag(); - mOutput << startstr << "" << endstr; - } -} - -// ------------------------------------------------------------------------------------------------ -// Writes a color-or-texture entry into an effect definition -void ColladaExporter::WriteTextureColorEntry( const Surface& pSurface, const std::string& pTypeName, const std::string& pImageName) -{ - if(pSurface.exist) { - mOutput << startstr << "<" << pTypeName << ">" << endstr; - PushTag(); - if( pSurface.texture.empty() ) - { - mOutput << startstr << "" << pSurface.color.r << " " << pSurface.color.g << " " << pSurface.color.b << " " << pSurface.color.a << "" << endstr; - } - else - { - mOutput << startstr << "" << endstr; - } - PopTag(); - mOutput << startstr << "" << endstr; - } -} - -// ------------------------------------------------------------------------------------------------ -// Writes the two parameters necessary for referencing a texture in an effect entry -void ColladaExporter::WriteTextureParamEntry( const Surface& pSurface, const std::string& pTypeName, const std::string& pMatName) -{ - // if surface is a texture, write out the sampler and the surface parameters necessary to reference the texture - if( !pSurface.texture.empty() ) - { - mOutput << startstr << "" << endstr; - PushTag(); - mOutput << startstr << "" << endstr; - PushTag(); - mOutput << startstr << "" << XMLIDEncode(pMatName) << "-" << pTypeName << "-image" << endstr; - PopTag(); - mOutput << startstr << "" << endstr; - PopTag(); - mOutput << startstr << "" << endstr; - - mOutput << startstr << "" << endstr; - PushTag(); - mOutput << startstr << "" << endstr; - PushTag(); - mOutput << startstr << "" << XMLIDEncode(pMatName) << "-" << pTypeName << "-surface" << endstr; - PopTag(); - mOutput << startstr << "" << endstr; - PopTag(); - mOutput << startstr << "" << endstr; - } -} - -// ------------------------------------------------------------------------------------------------ -// Writes a scalar property -void ColladaExporter::WriteFloatEntry( const Property& pProperty, const std::string& pTypeName) -{ - if(pProperty.exist) { - mOutput << startstr << "<" << pTypeName << ">" << endstr; - PushTag(); - mOutput << startstr << "" << pProperty.value << "" << endstr; - PopTag(); - mOutput << startstr << "" << endstr; - } -} - -// ------------------------------------------------------------------------------------------------ -// Writes the material setup -void ColladaExporter::WriteMaterials() -{ - materials.resize( mScene->mNumMaterials); - - /// collect all materials from the scene - size_t numTextures = 0; - for( size_t a = 0; a < mScene->mNumMaterials; ++a ) - { - const aiMaterial* mat = mScene->mMaterials[a]; - - aiString name; - if( mat->Get( AI_MATKEY_NAME, name) != aiReturn_SUCCESS ) { - name = "mat"; - materials[a].name = std::string( "m") + to_string(a) + name.C_Str(); - } else { - // try to use the material's name if no other material has already taken it, else append # - std::string testName = name.C_Str(); - size_t materialCountWithThisName = 0; - for( size_t i = 0; i < a; i ++ ) { - if( materials[i].name == testName ) { - materialCountWithThisName ++; - } - } - if( materialCountWithThisName == 0 ) { - materials[a].name = name.C_Str(); - } else { - materials[a].name = std::string(name.C_Str()) + to_string(materialCountWithThisName); - } - } - - aiShadingMode shading = aiShadingMode_Flat; - materials[a].shading_model = "phong"; - if(mat->Get( AI_MATKEY_SHADING_MODEL, shading) == aiReturn_SUCCESS) { - if(shading == aiShadingMode_Phong) { - materials[a].shading_model = "phong"; - } else if(shading == aiShadingMode_Blinn) { - materials[a].shading_model = "blinn"; - } else if(shading == aiShadingMode_NoShading) { - materials[a].shading_model = "constant"; - } else if(shading == aiShadingMode_Gouraud) { - materials[a].shading_model = "lambert"; - } - } - - ReadMaterialSurface( materials[a].ambient, mat, aiTextureType_AMBIENT, AI_MATKEY_COLOR_AMBIENT); - if( !materials[a].ambient.texture.empty() ) numTextures++; - ReadMaterialSurface( materials[a].diffuse, mat, aiTextureType_DIFFUSE, AI_MATKEY_COLOR_DIFFUSE); - if( !materials[a].diffuse.texture.empty() ) numTextures++; - ReadMaterialSurface( materials[a].specular, mat, aiTextureType_SPECULAR, AI_MATKEY_COLOR_SPECULAR); - if( !materials[a].specular.texture.empty() ) numTextures++; - ReadMaterialSurface( materials[a].emissive, mat, aiTextureType_EMISSIVE, AI_MATKEY_COLOR_EMISSIVE); - if( !materials[a].emissive.texture.empty() ) numTextures++; - ReadMaterialSurface( materials[a].reflective, mat, aiTextureType_REFLECTION, AI_MATKEY_COLOR_REFLECTIVE); - if( !materials[a].reflective.texture.empty() ) numTextures++; - ReadMaterialSurface( materials[a].transparent, mat, aiTextureType_OPACITY, AI_MATKEY_COLOR_TRANSPARENT); - if( !materials[a].transparent.texture.empty() ) numTextures++; - ReadMaterialSurface( materials[a].normal, mat, aiTextureType_NORMALS, NULL, 0, 0); - if( !materials[a].normal.texture.empty() ) numTextures++; - - materials[a].shininess.exist = mat->Get( AI_MATKEY_SHININESS, materials[a].shininess.value) == aiReturn_SUCCESS; - materials[a].transparency.exist = mat->Get( AI_MATKEY_OPACITY, materials[a].transparency.value) == aiReturn_SUCCESS; - materials[a].index_refraction.exist = mat->Get( AI_MATKEY_REFRACTI, materials[a].index_refraction.value) == aiReturn_SUCCESS; - } - - // output textures if present - if( numTextures > 0 ) - { - mOutput << startstr << "" << endstr; - PushTag(); - for( std::vector::const_iterator it = materials.begin(); it != materials.end(); ++it ) - { - const Material& mat = *it; - WriteImageEntry( mat.ambient, mat.name + "-ambient-image"); - WriteImageEntry( mat.diffuse, mat.name + "-diffuse-image"); - WriteImageEntry( mat.specular, mat.name + "-specular-image"); - WriteImageEntry( mat.emissive, mat.name + "-emission-image"); - WriteImageEntry( mat.reflective, mat.name + "-reflective-image"); - WriteImageEntry( mat.transparent, mat.name + "-transparent-image"); - WriteImageEntry( mat.normal, mat.name + "-normal-image"); - } - PopTag(); - mOutput << startstr << "" << endstr; - } - - // output effects - those are the actual carriers of information - if( !materials.empty() ) - { - mOutput << startstr << "" << endstr; - PushTag(); - for( std::vector::const_iterator it = materials.begin(); it != materials.end(); ++it ) - { - const Material& mat = *it; - // this is so ridiculous it must be right - mOutput << startstr << "" << endstr; - PushTag(); - mOutput << startstr << "" << endstr; - PushTag(); - - // write sampler- and surface params for the texture entries - WriteTextureParamEntry( mat.emissive, "emission", mat.name); - WriteTextureParamEntry( mat.ambient, "ambient", mat.name); - WriteTextureParamEntry( mat.diffuse, "diffuse", mat.name); - WriteTextureParamEntry( mat.specular, "specular", mat.name); - WriteTextureParamEntry( mat.reflective, "reflective", mat.name); - WriteTextureParamEntry( mat.transparent, "transparent", mat.name); - WriteTextureParamEntry( mat.normal, "normal", mat.name); - - mOutput << startstr << "" << endstr; - PushTag(); - mOutput << startstr << "<" << mat.shading_model << ">" << endstr; - PushTag(); - - WriteTextureColorEntry( mat.emissive, "emission", mat.name + "-emission-sampler"); - WriteTextureColorEntry( mat.ambient, "ambient", mat.name + "-ambient-sampler"); - WriteTextureColorEntry( mat.diffuse, "diffuse", mat.name + "-diffuse-sampler"); - WriteTextureColorEntry( mat.specular, "specular", mat.name + "-specular-sampler"); - WriteFloatEntry(mat.shininess, "shininess"); - WriteTextureColorEntry( mat.reflective, "reflective", mat.name + "-reflective-sampler"); - WriteTextureColorEntry( mat.transparent, "transparent", mat.name + "-transparent-sampler"); - WriteFloatEntry(mat.transparency, "transparency"); - WriteFloatEntry(mat.index_refraction, "index_of_refraction"); - - if(! mat.normal.texture.empty()) { - WriteTextureColorEntry( mat.normal, "bump", mat.name + "-normal-sampler"); - } - - PopTag(); - mOutput << startstr << "" << endstr; - PopTag(); - mOutput << startstr << "" << endstr; - PopTag(); - mOutput << startstr << "" << endstr; - PopTag(); - mOutput << startstr << "" << endstr; - } - PopTag(); - mOutput << startstr << "" << endstr; - - // write materials - they're just effect references - mOutput << startstr << "" << endstr; - PushTag(); - for( std::vector::const_iterator it = materials.begin(); it != materials.end(); ++it ) - { - const Material& mat = *it; - mOutput << startstr << "" << endstr; - PushTag(); - mOutput << startstr << "" << endstr; - PopTag(); - mOutput << startstr << "" << endstr; - } - PopTag(); - mOutput << startstr << "" << endstr; - } -} - -// ------------------------------------------------------------------------------------------------ -// Writes the controller library -void ColladaExporter::WriteControllerLibrary() -{ - mOutput << startstr << "" << endstr; - PushTag(); - - for( size_t a = 0; a < mScene->mNumMeshes; ++a) { - WriteController( a); - } - - PopTag(); - mOutput << startstr << "" << endstr; -} - -// ------------------------------------------------------------------------------------------------ -// Writes a skin controller of the given mesh -void ColladaExporter::WriteController( size_t pIndex) -{ - const aiMesh* mesh = mScene->mMeshes[pIndex]; - const std::string idstr = mesh->mName.length == 0 ? GetMeshId(pIndex) : mesh->mName.C_Str(); - const std::string idstrEscaped = XMLIDEncode(idstr); - - if ( mesh->mNumFaces == 0 || mesh->mNumVertices == 0 ) - return; - - if ( mesh->mNumBones == 0 ) - return; - - mOutput << startstr << ""<< endstr; - PushTag(); - - mOutput << startstr << "" << endstr; - PushTag(); - - // bind pose matrix - mOutput << startstr << "" << endstr; - PushTag(); - - // I think it is identity in general cases. - aiMatrix4x4 mat; - mOutput << startstr << mat.a1 << " " << mat.a2 << " " << mat.a3 << " " << mat.a4 << endstr; - mOutput << startstr << mat.b1 << " " << mat.b2 << " " << mat.b3 << " " << mat.b4 << endstr; - mOutput << startstr << mat.c1 << " " << mat.c2 << " " << mat.c3 << " " << mat.c4 << endstr; - mOutput << startstr << mat.d1 << " " << mat.d2 << " " << mat.d3 << " " << mat.d4 << endstr; - - PopTag(); - mOutput << startstr << "" << endstr; - - mOutput << startstr << "" << endstr; - PushTag(); - - mOutput << startstr << "mNumBones << "\">"; - - for( size_t i = 0; i < mesh->mNumBones; ++i ) - mOutput << XMLIDEncode(mesh->mBones[i]->mName.C_Str()) << " "; - - mOutput << "" << endstr; - - mOutput << startstr << "" << endstr; - PushTag(); - - mOutput << startstr << "mNumBones << "\" stride=\"" << 1 << "\">" << endstr; - PushTag(); - - mOutput << startstr << "" << endstr; - - PopTag(); - mOutput << startstr << "" << endstr; - - PopTag(); - mOutput << startstr << "" << endstr; - - PopTag(); - mOutput << startstr << "" << endstr; - - std::vector bind_poses; - bind_poses.reserve(mesh->mNumBones * 16); - for(unsigned int i = 0; i < mesh->mNumBones; ++i) - for( unsigned int j = 0; j < 4; ++j) - bind_poses.insert(bind_poses.end(), mesh->mBones[i]->mOffsetMatrix[j], mesh->mBones[i]->mOffsetMatrix[j] + 4); - - WriteFloatArray( idstr + "-skin-bind_poses", FloatType_Mat4x4, (const ai_real*) bind_poses.data(), bind_poses.size() / 16); - - bind_poses.clear(); - - std::vector skin_weights; - skin_weights.reserve(mesh->mNumVertices * mesh->mNumBones); - for( size_t i = 0; i < mesh->mNumBones; ++i) - for( size_t j = 0; j < mesh->mBones[i]->mNumWeights; ++j) - skin_weights.push_back(mesh->mBones[i]->mWeights[j].mWeight); - - WriteFloatArray( idstr + "-skin-weights", FloatType_Weight, (const ai_real*) skin_weights.data(), skin_weights.size()); - - skin_weights.clear(); - - mOutput << startstr << "" << endstr; - PushTag(); - - mOutput << startstr << "" << endstr; - mOutput << startstr << "" << endstr; - - PopTag(); - mOutput << startstr << "" << endstr; - - mOutput << startstr << "mNumVertices << "\">" << endstr; - PushTag(); - - mOutput << startstr << "" << endstr; - mOutput << startstr << "" << endstr; - - mOutput << startstr << ""; - - std::vector num_influences(mesh->mNumVertices, (ai_uint)0); - for( size_t i = 0; i < mesh->mNumBones; ++i) - for( size_t j = 0; j < mesh->mBones[i]->mNumWeights; ++j) - ++num_influences[mesh->mBones[i]->mWeights[j].mVertexId]; - - for( size_t i = 0; i < mesh->mNumVertices; ++i) - mOutput << num_influences[i] << " "; - - mOutput << "" << endstr; - - mOutput << startstr << ""; - - ai_uint joint_weight_indices_length = 0; - std::vector accum_influences; - accum_influences.reserve(num_influences.size()); - for( size_t i = 0; i < num_influences.size(); ++i) - { - accum_influences.push_back(joint_weight_indices_length); - joint_weight_indices_length += num_influences[i]; - } - - ai_uint weight_index = 0; - std::vector joint_weight_indices(2 * joint_weight_indices_length, (ai_int)-1); - for( unsigned int i = 0; i < mesh->mNumBones; ++i) - for( unsigned j = 0; j < mesh->mBones[i]->mNumWeights; ++j) - { - unsigned int vId = mesh->mBones[i]->mWeights[j].mVertexId; - for( ai_uint k = 0; k < num_influences[vId]; ++k) - { - if (joint_weight_indices[2 * (accum_influences[vId] + k)] == -1) - { - joint_weight_indices[2 * (accum_influences[vId] + k)] = i; - joint_weight_indices[2 * (accum_influences[vId] + k) + 1] = weight_index; - break; - } - } - ++weight_index; - } - - for( size_t i = 0; i < joint_weight_indices.size(); ++i) - mOutput << joint_weight_indices[i] << " "; - - num_influences.clear(); - accum_influences.clear(); - joint_weight_indices.clear(); - - mOutput << "" << endstr; - - PopTag(); - mOutput << startstr << "" << endstr; - - PopTag(); - mOutput << startstr << "" << endstr; - - PopTag(); - mOutput << startstr << "" << endstr; -} - -// ------------------------------------------------------------------------------------------------ -// Writes the geometry library -void ColladaExporter::WriteGeometryLibrary() -{ - mOutput << startstr << "" << endstr; - PushTag(); - - for( size_t a = 0; a < mScene->mNumMeshes; ++a) - WriteGeometry( a); - - PopTag(); - mOutput << startstr << "" << endstr; -} - -// ------------------------------------------------------------------------------------------------ -// Writes the given mesh -void ColladaExporter::WriteGeometry( size_t pIndex) -{ - const aiMesh* mesh = mScene->mMeshes[pIndex]; - const std::string idstr = mesh->mName.length == 0 ? GetMeshId(pIndex) : mesh->mName.C_Str(); - const std::string geometryName = XMLEscape(idstr); - const std::string geometryId = XMLIDEncode(idstr); - - if ( mesh->mNumFaces == 0 || mesh->mNumVertices == 0 ) - return; - - // opening tag - mOutput << startstr << "" << endstr; - PushTag(); - - mOutput << startstr << "" << endstr; - PushTag(); - - // Positions - WriteFloatArray( idstr + "-positions", FloatType_Vector, (ai_real*) mesh->mVertices, mesh->mNumVertices); - // Normals, if any - if( mesh->HasNormals() ) - WriteFloatArray( idstr + "-normals", FloatType_Vector, (ai_real*) mesh->mNormals, mesh->mNumVertices); - - // texture coords - for( size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) - { - if( mesh->HasTextureCoords(static_cast(a)) ) - { - WriteFloatArray( idstr + "-tex" + to_string(a), mesh->mNumUVComponents[a] == 3 ? FloatType_TexCoord3 : FloatType_TexCoord2, - (ai_real*) mesh->mTextureCoords[a], mesh->mNumVertices); - } - } - - // vertex colors - for( size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a) - { - if( mesh->HasVertexColors(static_cast(a)) ) - WriteFloatArray( idstr + "-color" + to_string(a), FloatType_Color, (ai_real*) mesh->mColors[a], mesh->mNumVertices); - } - - // assemble vertex structure - // Only write input for POSITION since we will write other as shared inputs in polygon definition - mOutput << startstr << "" << endstr; - PushTag(); - mOutput << startstr << "" << endstr; - PopTag(); - mOutput << startstr << "" << endstr; - - // count the number of lines, triangles and polygon meshes - int countLines = 0; - int countPoly = 0; - for( size_t a = 0; a < mesh->mNumFaces; ++a ) - { - if (mesh->mFaces[a].mNumIndices == 2) countLines++; - else if (mesh->mFaces[a].mNumIndices >= 3) countPoly++; - } - - // lines - if (countLines) - { - mOutput << startstr << "" << endstr; - PushTag(); - mOutput << startstr << "" << endstr; - if( mesh->HasNormals() ) - mOutput << startstr << "" << endstr; - for( size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a ) - { - if( mesh->HasTextureCoords(static_cast(a)) ) - mOutput << startstr << "" << endstr; - } - for( size_t a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a ) - { - if( mesh->HasVertexColors(static_cast(a) ) ) - mOutput << startstr << "" << endstr; - } - - mOutput << startstr << "

"; - for( size_t a = 0; a < mesh->mNumFaces; ++a ) - { - const aiFace& face = mesh->mFaces[a]; - if (face.mNumIndices != 2) continue; - for( size_t b = 0; b < face.mNumIndices; ++b ) - mOutput << face.mIndices[b] << " "; - } - mOutput << "

" << endstr; - PopTag(); - mOutput << startstr << "
" << endstr; - } - - // triangle - don't use it, because compatibility problems - - // polygons - if (countPoly) - { - mOutput << startstr << "" << endstr; - PushTag(); - mOutput << startstr << "" << endstr; - if( mesh->HasNormals() ) - mOutput << startstr << "" << endstr; - for( size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++a ) - { - if( mesh->HasTextureCoords(static_cast(a)) ) - mOutput << startstr << "" << endstr; - } - for( size_t a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; ++a ) - { - if( mesh->HasVertexColors(static_cast(a) ) ) - mOutput << startstr << "" << endstr; - } - - mOutput << startstr << ""; - for( size_t a = 0; a < mesh->mNumFaces; ++a ) - { - if (mesh->mFaces[a].mNumIndices < 3) continue; - mOutput << mesh->mFaces[a].mNumIndices << " "; - } - mOutput << "" << endstr; - - mOutput << startstr << "

"; - for( size_t a = 0; a < mesh->mNumFaces; ++a ) - { - const aiFace& face = mesh->mFaces[a]; - if (face.mNumIndices < 3) continue; - for( size_t b = 0; b < face.mNumIndices; ++b ) - mOutput << face.mIndices[b] << " "; - } - mOutput << "

" << endstr; - PopTag(); - mOutput << startstr << "
" << endstr; - } - - // closing tags - PopTag(); - mOutput << startstr << "
" << endstr; - PopTag(); - mOutput << startstr << "
" << endstr; -} - -// ------------------------------------------------------------------------------------------------ -// Writes a float array of the given type -void ColladaExporter::WriteFloatArray( const std::string& pIdString, FloatDataType pType, const ai_real* pData, size_t pElementCount) -{ - size_t floatsPerElement = 0; - switch( pType ) - { - case FloatType_Vector: floatsPerElement = 3; break; - case FloatType_TexCoord2: floatsPerElement = 2; break; - case FloatType_TexCoord3: floatsPerElement = 3; break; - case FloatType_Color: floatsPerElement = 3; break; - case FloatType_Mat4x4: floatsPerElement = 16; break; - case FloatType_Weight: floatsPerElement = 1; break; - case FloatType_Time: floatsPerElement = 1; break; - default: - return; - } - - std::string arrayId = XMLIDEncode(pIdString) + "-array"; - - mOutput << startstr << "" << endstr; - PushTag(); - - // source array - mOutput << startstr << " "; - PushTag(); - - if( pType == FloatType_TexCoord2 ) - { - for( size_t a = 0; a < pElementCount; ++a ) - { - mOutput << pData[a*3+0] << " "; - mOutput << pData[a*3+1] << " "; - } - } - else if( pType == FloatType_Color ) - { - for( size_t a = 0; a < pElementCount; ++a ) - { - mOutput << pData[a*4+0] << " "; - mOutput << pData[a*4+1] << " "; - mOutput << pData[a*4+2] << " "; - } - } - else - { - for( size_t a = 0; a < pElementCount * floatsPerElement; ++a ) - mOutput << pData[a] << " "; - } - mOutput << "" << endstr; - PopTag(); - - // the usual Collada fun. Let's bloat it even more! - mOutput << startstr << "" << endstr; - PushTag(); - mOutput << startstr << "" << endstr; - PushTag(); - - switch( pType ) - { - case FloatType_Vector: - mOutput << startstr << "" << endstr; - mOutput << startstr << "" << endstr; - mOutput << startstr << "" << endstr; - break; - - case FloatType_TexCoord2: - mOutput << startstr << "" << endstr; - mOutput << startstr << "" << endstr; - break; - - case FloatType_TexCoord3: - mOutput << startstr << "" << endstr; - mOutput << startstr << "" << endstr; - mOutput << startstr << "" << endstr; - break; - - case FloatType_Color: - mOutput << startstr << "" << endstr; - mOutput << startstr << "" << endstr; - mOutput << startstr << "" << endstr; - break; - - case FloatType_Mat4x4: - mOutput << startstr << "" << endstr; - break; - - case FloatType_Weight: - mOutput << startstr << "" << endstr; - break; - - // customized, add animation related - case FloatType_Time: - mOutput << startstr << "" << endstr; - break; - - } - - PopTag(); - mOutput << startstr << "" << endstr; - PopTag(); - mOutput << startstr << "" << endstr; - PopTag(); - mOutput << startstr << "" << endstr; -} - -// ------------------------------------------------------------------------------------------------ -// Writes the scene library -void ColladaExporter::WriteSceneLibrary() -{ - const std::string sceneName = XMLEscape(mScene->mRootNode->mName.C_Str()); - const std::string sceneId = XMLIDEncode(mScene->mRootNode->mName.C_Str()); - - mOutput << startstr << "" << endstr; - PushTag(); - mOutput << startstr << "" << endstr; - PushTag(); - - // start recursive write at the root node - for( size_t a = 0; a < mScene->mRootNode->mNumChildren; ++a ) - WriteNode( mScene, mScene->mRootNode->mChildren[a]); - - PopTag(); - mOutput << startstr << "" << endstr; - PopTag(); - mOutput << startstr << "" << endstr; -} -// ------------------------------------------------------------------------------------------------ -void ColladaExporter::WriteAnimationLibrary(size_t pIndex) -{ - static const float kSecondsFromMilliseconds = .001f; - - const aiAnimation * anim = mScene->mAnimations[pIndex]; - - if ( anim->mNumChannels == 0 && anim->mNumMeshChannels == 0 && anim->mNumMorphMeshChannels ==0 ) { - return; - } - - const std::string animation_name_escaped = XMLEscape( anim->mName.C_Str() ); - std::string idstr = anim->mName.C_Str(); - std::string ending = std::string( "AnimId" ) + to_string(pIndex); - if (idstr.length() >= ending.length()) { - if (0 != idstr.compare (idstr.length() - ending.length(), ending.length(), ending)) { - idstr = idstr + ending; - } - } else { - idstr = idstr + ending; - } - - const std::string idstrEscaped = XMLIDEncode(idstr); - - mOutput << startstr << "" << endstr; - PushTag(); - - std::string cur_node_idstr; - for (size_t a = 0; a < anim->mNumChannels; ++a) { - const aiNodeAnim * nodeAnim = anim->mChannels[a]; - - // sanity check - if (nodeAnim->mNumPositionKeys != nodeAnim->mNumScalingKeys || nodeAnim->mNumPositionKeys != nodeAnim->mNumRotationKeys) { - continue; - } - - { - cur_node_idstr.clear(); - cur_node_idstr += nodeAnim->mNodeName.data; - cur_node_idstr += std::string("_matrix-input"); - - std::vector frames; - for( size_t i = 0; i < nodeAnim->mNumPositionKeys; ++i) { - frames.push_back(static_cast(nodeAnim->mPositionKeys[i].mTime) * kSecondsFromMilliseconds); - } - - WriteFloatArray(cur_node_idstr, FloatType_Time, (const ai_real *)frames.data(), frames.size()); - frames.clear(); - } - - { - cur_node_idstr.clear(); - - cur_node_idstr += nodeAnim->mNodeName.data; - cur_node_idstr += std::string("_matrix-output"); - - std::vector keyframes; - keyframes.reserve(nodeAnim->mNumPositionKeys * 16); - for( size_t i = 0; i < nodeAnim->mNumPositionKeys; ++i) { - aiVector3D Scaling = nodeAnim->mScalingKeys[i].mValue; - aiMatrix4x4 ScalingM; // identity - ScalingM[0][0] = Scaling.x; ScalingM[1][1] = Scaling.y; ScalingM[2][2] = Scaling.z; - - aiQuaternion RotationQ = nodeAnim->mRotationKeys[i].mValue; - aiMatrix4x4 s = aiMatrix4x4( RotationQ.GetMatrix() ); - aiMatrix4x4 RotationM(s.a1, s.a2, s.a3, 0, s.b1, s.b2, s.b3, 0, s.c1, s.c2, s.c3, 0, 0, 0, 0, 1); - - aiVector3D Translation = nodeAnim->mPositionKeys[i].mValue; - aiMatrix4x4 TranslationM; // identity - TranslationM[0][3] = Translation.x; TranslationM[1][3] = Translation.y; TranslationM[2][3] = Translation.z; - - // Combine the above transformations - aiMatrix4x4 mat = TranslationM * RotationM * ScalingM; - - for( unsigned int j = 0; j < 4; ++j) { - keyframes.insert(keyframes.end(), mat[j], mat[j] + 4); - } - } - - WriteFloatArray(cur_node_idstr, FloatType_Mat4x4, (const ai_real *)keyframes.data(), keyframes.size() / 16); - } - - { - std::vector names; - for ( size_t i = 0; i < nodeAnim->mNumPositionKeys; ++i) { - if ( nodeAnim->mPreState == aiAnimBehaviour_DEFAULT - || nodeAnim->mPreState == aiAnimBehaviour_LINEAR - || nodeAnim->mPreState == aiAnimBehaviour_REPEAT - ) { - names.push_back( "LINEAR" ); - } else if (nodeAnim->mPostState == aiAnimBehaviour_CONSTANT) { - names.push_back( "STEP" ); - } - } - - const std::string cur_node_idstr2 = nodeAnim->mNodeName.data + std::string("_matrix-interpolation"); - std::string arrayId = XMLIDEncode(cur_node_idstr2) + "-array"; - - mOutput << startstr << "" << endstr; - PushTag(); - - // source array - mOutput << startstr << " "; - for( size_t aa = 0; aa < names.size(); ++aa ) { - mOutput << names[aa] << " "; - } - mOutput << "" << endstr; - - mOutput << startstr << "" << endstr; - PushTag(); - - mOutput << startstr << "" << endstr; - PushTag(); - - mOutput << startstr << "" << endstr; - - PopTag(); - mOutput << startstr << "" << endstr; - - PopTag(); - mOutput << startstr << "" << endstr; - - PopTag(); - mOutput << startstr << "" << endstr; - } - } - - for (size_t a = 0; a < anim->mNumChannels; ++a) { - const aiNodeAnim * nodeAnim = anim->mChannels[a]; - - { - // samplers - const std::string node_idstr = nodeAnim->mNodeName.data + std::string("_matrix-sampler"); - mOutput << startstr << "" << endstr; - PushTag(); - - mOutput << startstr << "mNodeName.data + std::string("_matrix-input") ) << "\"/>" << endstr; - mOutput << startstr << "mNodeName.data + std::string("_matrix-output") ) << "\"/>" << endstr; - mOutput << startstr << "mNodeName.data + std::string("_matrix-interpolation") ) << "\"/>" << endstr; - - PopTag(); - mOutput << startstr << "" << endstr; - } - } - - for (size_t a = 0; a < anim->mNumChannels; ++a) { - const aiNodeAnim * nodeAnim = anim->mChannels[a]; - - { - // channels - mOutput << startstr << "mNodeName.data + std::string("_matrix-sampler") ) << "\" target=\"" << XMLIDEncode(nodeAnim->mNodeName.data) << "/matrix\"/>" << endstr; - } - } - - PopTag(); - mOutput << startstr << "" << endstr; - -} -// ------------------------------------------------------------------------------------------------ -void ColladaExporter::WriteAnimationsLibrary() -{ - if ( mScene->mNumAnimations > 0 ) { - mOutput << startstr << "" << endstr; - PushTag(); - - // start recursive write at the root node - for( size_t a = 0; a < mScene->mNumAnimations; ++a) - WriteAnimationLibrary( a ); - - PopTag(); - mOutput << startstr << "" << endstr; - } -} -// ------------------------------------------------------------------------------------------------ -// Helper to find a bone by name in the scene -aiBone* findBone( const aiScene* scene, const char * name) { - for (size_t m=0; mmNumMeshes; m++) { - aiMesh * mesh = scene->mMeshes[m]; - for (size_t b=0; bmNumBones; b++) { - aiBone * bone = mesh->mBones[b]; - if (0 == strcmp(name, bone->mName.C_Str())) { - return bone; - } - } - } - return NULL; -} - -// ------------------------------------------------------------------------------------------------ -const aiNode * findBoneNode( const aiNode* aNode, const aiBone* bone) -{ - if ( aNode && bone && aNode->mName == bone->mName ) { - return aNode; - } - - if ( aNode && bone ) { - for (unsigned int i=0; i < aNode->mNumChildren; ++i) { - aiNode * aChild = aNode->mChildren[i]; - const aiNode * foundFromChild = 0; - if ( aChild ) { - foundFromChild = findBoneNode( aChild, bone ); - if ( foundFromChild ) return foundFromChild; - } - } - } - - return NULL; -} - -const aiNode * findSkeletonRootNode( const aiScene* scene, const aiMesh * mesh) -{ - std::set topParentBoneNodes; - if ( mesh && mesh->mNumBones > 0 ) { - for (unsigned int i=0; i < mesh->mNumBones; ++i) { - aiBone * bone = mesh->mBones[i]; - - const aiNode * node = findBoneNode( scene->mRootNode, bone); - if ( node ) { - while ( node->mParent && findBone(scene, node->mParent->mName.C_Str() ) != 0 ) { - node = node->mParent; - } - topParentBoneNodes.insert( node ); - } - } - } - - if ( !topParentBoneNodes.empty() ) { - const aiNode * parentBoneNode = *topParentBoneNodes.begin(); - if ( topParentBoneNodes.size() == 1 ) { - return parentBoneNode; - } else { - for (auto it : topParentBoneNodes) { - if ( it->mParent ) return it->mParent; - } - return parentBoneNode; - } - } - - return NULL; -} - -// ------------------------------------------------------------------------------------------------ -// Recursively writes the given node -void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode) -{ - // the node must have a name - if (pNode->mName.length == 0) - { - std::stringstream ss; - ss << "Node_" << pNode; - pNode->mName.Set(ss.str()); - } - - // If the node is associated with a bone, it is a joint node (JOINT) - // otherwise it is a normal node (NODE) - const char * node_type; - bool is_joint, is_skeleton_root = false; - if (nullptr == findBone(pScene, pNode->mName.C_Str())) { - node_type = "NODE"; - is_joint = false; - } else { - node_type = "JOINT"; - is_joint = true; - if (!pNode->mParent || nullptr == findBone(pScene, pNode->mParent->mName.C_Str())) { - is_skeleton_root = true; - } - } - - const std::string node_id = XMLIDEncode(pNode->mName.data); - const std::string node_name = XMLEscape(pNode->mName.data); - mOutput << startstr << "" << endstr; - PushTag(); - - // write transformation - we can directly put the matrix there - // TODO: (thom) decompose into scale - rot - quad to allow addressing it by animations afterwards - aiMatrix4x4 mat = pNode->mTransformation; - - // If this node is a Camera node, the camera coordinate system needs to be multiplied in. - // When importing from Collada, the mLookAt is set to 0, 0, -1, and the node transform is unchanged. - // When importing from a different format, mLookAt is set to 0, 0, 1. Therefore, the local camera - // coordinate system must be changed to matche the Collada specification. - for (size_t i = 0; imNumCameras; i++){ - if (mScene->mCameras[i]->mName == pNode->mName){ - aiMatrix4x4 sourceView; - mScene->mCameras[i]->GetCameraMatrix(sourceView); - - aiMatrix4x4 colladaView; - colladaView.a1 = colladaView.c3 = -1; // move into -z space. - mat *= (sourceView * colladaView); - break; - } - } - - // customized, sid should be 'matrix' to match with loader code. - //mOutput << startstr << ""; - mOutput << startstr << ""; - - mOutput << mat.a1 << " " << mat.a2 << " " << mat.a3 << " " << mat.a4 << " "; - mOutput << mat.b1 << " " << mat.b2 << " " << mat.b3 << " " << mat.b4 << " "; - mOutput << mat.c1 << " " << mat.c2 << " " << mat.c3 << " " << mat.c4 << " "; - mOutput << mat.d1 << " " << mat.d2 << " " << mat.d3 << " " << mat.d4; - mOutput << "" << endstr; - - if(pNode->mNumMeshes==0){ - //check if it is a camera node - for(size_t i=0; imNumCameras; i++){ - if(mScene->mCameras[i]->mName == pNode->mName){ - mOutput << startstr <<"" << endstr; - break; - } - } - //check if it is a light node - for(size_t i=0; imNumLights; i++){ - if(mScene->mLights[i]->mName == pNode->mName){ - mOutput << startstr <<"" << endstr; - break; - } - } - - }else - // instance every geometry - for( size_t a = 0; a < pNode->mNumMeshes; ++a ) - { - const aiMesh* mesh = mScene->mMeshes[pNode->mMeshes[a]]; - // do not instantiate mesh if empty. I wonder how this could happen - if( mesh->mNumFaces == 0 || mesh->mNumVertices == 0 ) - continue; - - const std::string meshName = mesh->mName.length == 0 ? GetMeshId(pNode->mMeshes[a]) : mesh->mName.C_Str(); - - if( mesh->mNumBones == 0 ) - { - mOutput << startstr << "" << endstr; - PushTag(); - } - else - { - mOutput << startstr - << "" - << endstr; - PushTag(); - - // note! this mFoundSkeletonRootNodeID some how affects animation, it makes the mesh attaches to armature skeleton root node. - // use the first bone to find skeleton root - const aiNode * skeletonRootBoneNode = findSkeletonRootNode( pScene, mesh ); - if ( skeletonRootBoneNode ) { - mFoundSkeletonRootNodeID = XMLIDEncode( skeletonRootBoneNode->mName.C_Str() ); - } - mOutput << startstr << "#" << mFoundSkeletonRootNodeID << "" << endstr; - } - mOutput << startstr << "" << endstr; - PushTag(); - mOutput << startstr << "" << endstr; - PushTag(); - mOutput << startstr << "mMaterialIndex].name) << "\">" << endstr; - PushTag(); - for( size_t aa = 0; aa < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++aa ) - { - if( mesh->HasTextureCoords( static_cast(aa) ) ) - // semantic as in - // input_semantic as in - // input_set as in - mOutput << startstr << "" << endstr; - } - PopTag(); - mOutput << startstr << "" << endstr; - PopTag(); - mOutput << startstr << "" << endstr; - PopTag(); - mOutput << startstr << "" << endstr; - - PopTag(); - if( mesh->mNumBones == 0) - mOutput << startstr << "" << endstr; - else - mOutput << startstr << "" << endstr; - } - - // recurse into subnodes - for( size_t a = 0; a < pNode->mNumChildren; ++a ) - WriteNode( pScene, pNode->mChildren[a]); - - PopTag(); - mOutput << startstr << "" << endstr; -} - -#endif -#endif From c82f8c0b8f39b26304a590f24e91b097849c5d1c Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 25 May 2020 20:38:12 +0200 Subject: [PATCH 06/44] Remove duplicated code closes https://github.com/assimp/assimp/issues/3183 --- tools/assimp_view/Display.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/assimp_view/Display.cpp b/tools/assimp_view/Display.cpp index 9fa157c41..1f6bb89eb 100644 --- a/tools/assimp_view/Display.cpp +++ b/tools/assimp_view/Display.cpp @@ -462,7 +462,7 @@ int CDisplay::AddTextureToDisplayList(unsigned int iType, TVINSERTSTRUCT sNew; tvi.pszText = chTemp; tvi.cchTextMax = (int)strlen(chTemp); - tvi.mask = TVIF_TEXT | TVIF_SELECTEDIMAGE | TVIF_IMAGE | TVIF_HANDLE | TVIF_HANDLE; + tvi.mask = TVIF_TEXT | TVIF_SELECTEDIMAGE | TVIF_IMAGE | TVIF_HANDLE; tvi.lParam = (LPARAM)20; // find out whether this is the default texture or not From c94ed5c7b1edda974bd0613f26af675e6f53b3ec Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 25 May 2020 20:40:19 +0200 Subject: [PATCH 07/44] Update utRemoveComponent.cpp closes https://github.com/assimp/assimp/issues/3183 --- test/unit/utRemoveComponent.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/test/unit/utRemoveComponent.cpp b/test/unit/utRemoveComponent.cpp index d3c102cfb..622a582d6 100644 --- a/test/unit/utRemoveComponent.cpp +++ b/test/unit/utRemoveComponent.cpp @@ -98,13 +98,6 @@ void RemoveVCProcessTest::SetUp() { pScene->mCameras = new aiCamera *[pScene->mNumCameras = 2]; pScene->mCameras[0] = new aiCamera(); pScene->mCameras[1] = new aiCamera(); - - // COMPILE TEST: aiMaterial may no add any extra members, - // so we don't need a virtual destructor - char check[sizeof(aiMaterial) == sizeof(aiMaterial) ? 10 : -1]; - check[0] = 0; - // to remove compiler warning - EXPECT_EQ(0, check[0]); } // ------------------------------------------------------------------------------------------------ From d40a3026dbacb24e1a1eab65ce0ea2b31dc8b2fd Mon Sep 17 00:00:00 2001 From: kimkulling Date: Thu, 28 May 2020 21:02:13 +0200 Subject: [PATCH 08/44] closes https://github.com/assimp/assimp/issues/3165: fix gcc build. --- code/AssetLib/STL/STLLoader.cpp | 14 ++++++++++++-- code/AssetLib/glTF/glTFAsset.h | 5 +++-- code/AssetLib/glTF2/glTF2Asset.h | 4 +++- code/Common/SceneCombiner.cpp | 5 ++++- code/Common/SpatialSort.cpp | 12 ++++-------- test/unit/utFindInvalidData.cpp | 8 ++++++-- 6 files changed, 32 insertions(+), 16 deletions(-) diff --git a/code/AssetLib/STL/STLLoader.cpp b/code/AssetLib/STL/STLLoader.cpp index 702f4fe5d..7d7fdb7c9 100644 --- a/code/AssetLib/STL/STLLoader.cpp +++ b/code/AssetLib/STL/STLLoader.cpp @@ -370,13 +370,23 @@ void STLImporter::LoadASCIIFile(aiNode *root) { pMesh->mNumFaces = static_cast(positionBuffer.size() / 3); pMesh->mNumVertices = static_cast(positionBuffer.size()); pMesh->mVertices = new aiVector3D[pMesh->mNumVertices]; - memcpy(pMesh->mVertices, &positionBuffer[0].x, pMesh->mNumVertices * sizeof(aiVector3D)); + for (size_t i=0; imNumVertices; ++i ) { + pMesh->mVertices[i].x = positionBuffer[i].x; + pMesh->mVertices[i].y = positionBuffer[i].y; + pMesh->mVertices[i].z = positionBuffer[i].z; + } + //memcpy(pMesh->mVertices, &positionBuffer[0].x, pMesh->mNumVertices * sizeof(aiVector3D)); positionBuffer.clear(); } // also only process normalBuffer when filled, else exception when accessing with index operator if (!normalBuffer.empty()) { pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; - memcpy(pMesh->mNormals, &normalBuffer[0].x, pMesh->mNumVertices * sizeof(aiVector3D)); + for (size_t i=0; imNumVertices; ++i ) { + pMesh->mNormals[i].x = normalBuffer[i].x; + pMesh->mNormals[i].y = normalBuffer[i].y; + pMesh->mNormals[i].z = normalBuffer[i].z; + } +// memcpy(pMesh->mNormals, &normalBuffer[0].x, pMesh->mNumVertices * sizeof(aiVector3D)); normalBuffer.clear(); } diff --git a/code/AssetLib/glTF/glTFAsset.h b/code/AssetLib/glTF/glTFAsset.h index 4c7aaa908..56cf18527 100644 --- a/code/AssetLib/glTF/glTFAsset.h +++ b/code/AssetLib/glTF/glTFAsset.h @@ -4,7 +4,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2020, assimp team - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -62,10 +61,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #define RAPIDJSON_HAS_STDSTRING 1 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wclass-memaccess" #include #include #include - +#pragma GCC diagnostic pop #ifdef ASSIMP_API # include # include diff --git a/code/AssetLib/glTF2/glTF2Asset.h b/code/AssetLib/glTF2/glTF2Asset.h index bfd4e4342..ed5f4318a 100644 --- a/code/AssetLib/glTF2/glTF2Asset.h +++ b/code/AssetLib/glTF2/glTF2Asset.h @@ -64,10 +64,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #define RAPIDJSON_HAS_STDSTRING 1 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wclass-memaccess" #include #include #include - +#pragma GCC diagnostic pop #ifdef ASSIMP_API #include #include diff --git a/code/Common/SceneCombiner.cpp b/code/Common/SceneCombiner.cpp index 4d2a411f2..2aa855989 100644 --- a/code/Common/SceneCombiner.cpp +++ b/code/Common/SceneCombiner.cpp @@ -978,7 +978,10 @@ void GetArrayCopy(Type*& dest, ai_uint num ) { Type* old = dest; dest = new Type[num]; - ::memcpy(dest, old, sizeof(Type) * num); + for ( size_t i=0; imNormals, 0, mMesh->mNumVertices * sizeof(aiVector3D)); - ::memset(mMesh->mBitangents, 0, mMesh->mNumVertices * sizeof(aiVector3D)); + for ( size_t i=0; imNumVertices; ++i ) { + mMesh->mNormals[i].x = mMesh->mNormals[i].y = mMesh->mNormals[i].z =0; + mMesh->mBitangents[i].x = mMesh->mBitangents[i].y = mMesh->mBitangents[i].z = 0; + } + //::memset(mMesh->mNormals, 0, mMesh->mNumVertices * sizeof(aiVector3D)); + //::memset(mMesh->mBitangents, 0, mMesh->mNumVertices * sizeof(aiVector3D)); mMesh->mTextureCoords[2][455] = aiVector3D(std::numeric_limits::quiet_NaN()); From d4ca91f408a3e8e10de029cfb8bc9bad7a72d155 Mon Sep 17 00:00:00 2001 From: Hanif Bin Ariffin Date: Thu, 28 May 2020 19:20:54 -0400 Subject: [PATCH 09/44] Evaluated the following expressions to either values. --- contrib/Open3DGC/o3dgcSC3DMCDecoder.inl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/contrib/Open3DGC/o3dgcSC3DMCDecoder.inl b/contrib/Open3DGC/o3dgcSC3DMCDecoder.inl index 326297426..c965fcd50 100644 --- a/contrib/Open3DGC/o3dgcSC3DMCDecoder.inl +++ b/contrib/Open3DGC/o3dgcSC3DMCDecoder.inl @@ -72,9 +72,12 @@ namespace o3dgc unsigned char mask = bstream.ReadUChar(m_iterator, m_streamType); ifs.SetCCW ((mask & 1) == 1); - ifs.SetSolid ((mask & 2) == 1); - ifs.SetConvex ((mask & 4) == 1); - ifs.SetIsTriangularMesh((mask & 8) == 1); + // (mask & 2) == 1 + ifs.SetSolid (false); + // (mask & 4) == 1 + ifs.SetConvex (false); + // (mask & 8) == 1 + ifs.SetIsTriangularMesh(false); //bool markerBit0 = (mask & 16 ) == 1; //bool markerBit1 = (mask & 32 ) == 1; //bool markerBit2 = (mask & 64 ) == 1; From eceb8aeed1b8fdef84cf98ddc2618dc24bdf0604 Mon Sep 17 00:00:00 2001 From: Hanif Bin Ariffin Date: Thu, 28 May 2020 19:22:06 -0400 Subject: [PATCH 10/44] Cleaned up implicit conversion and code. --- test/unit/utMatrix3x3.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/test/unit/utMatrix3x3.cpp b/test/unit/utMatrix3x3.cpp index 50fa66c50..12a84e0fa 100644 --- a/test/unit/utMatrix3x3.cpp +++ b/test/unit/utMatrix3x3.cpp @@ -73,9 +73,21 @@ TEST_F(utMatrix3x3Test, FromToMatrixTest) { aiVector3D from, to; + auto random_ratio = []() -> float { + return static_cast(rand() / RAND_MAX); + }; + for (int i = 0; i < NUM_SAMPLES; ++i) { - from = aiVector3D(1.f * rand() / RAND_MAX, 1.f * rand() / RAND_MAX, 1.f * rand() / RAND_MAX).Normalize(); - to = aiVector3D(1.f * rand() / RAND_MAX, 1.f * rand() / RAND_MAX, 1.f * rand() / RAND_MAX).Normalize(); + from = aiVector3D( + 1.f * random_ratio(), + 1.f * random_ratio(), + 1.f * random_ratio()) + .Normalize(); + to = aiVector3D( + 1.f * random_ratio(), + 1.f * random_ratio(), + 1.f * random_ratio()) + .Normalize(); aiMatrix3x3::FromToMatrix(from, to, trafo); res = trafo * from; From 7db6475592ef1c0d34227c2a7c682c3d4a63cac2 Mon Sep 17 00:00:00 2001 From: Hanif Bin Ariffin Date: Fri, 29 May 2020 11:59:28 -0400 Subject: [PATCH 11/44] Change from (int division -> cast) to (cast -> float division) --- test/unit/utMatrix3x3.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/utMatrix3x3.cpp b/test/unit/utMatrix3x3.cpp index 12a84e0fa..3905b1d35 100644 --- a/test/unit/utMatrix3x3.cpp +++ b/test/unit/utMatrix3x3.cpp @@ -74,7 +74,7 @@ TEST_F(utMatrix3x3Test, FromToMatrixTest) { aiVector3D from, to; auto random_ratio = []() -> float { - return static_cast(rand() / RAND_MAX); + return static_cast(rand()) / static_cast(RAND_MAX); }; for (int i = 0; i < NUM_SAMPLES; ++i) { From 4e34853ac717f2718eade04888b0cb8b9ada5f1e Mon Sep 17 00:00:00 2001 From: Yingying Wang Date: Fri, 5 Jun 2020 12:17:27 -0700 Subject: [PATCH 12/44] gltf2 export target names for blendshapes --- code/AssetLib/glTF2/glTF2AssetWriter.inl | 16 ++++++++++++++++ code/AssetLib/glTF2/glTF2Exporter.cpp | 5 +++++ 2 files changed, 21 insertions(+) diff --git a/code/AssetLib/glTF2/glTF2AssetWriter.inl b/code/AssetLib/glTF2/glTF2AssetWriter.inl index 361af40cd..d5b478e35 100644 --- a/code/AssetLib/glTF2/glTF2AssetWriter.inl +++ b/code/AssetLib/glTF2/glTF2AssetWriter.inl @@ -468,6 +468,22 @@ namespace glTF2 { } obj.AddMember("primitives", primitives, w.mAl); + // targetNames + if (m.targetNames.size() > 0) { + Value extras; + extras.SetObject(); + Value targetNames; + targetNames.SetArray(); + targetNames.Reserve(unsigned(m.targetNames.size()), w.mAl); + for (unsigned int n = 0; n < m.targetNames.size(); ++n) { + std::string name = m.targetNames[n]; + Value tname; + tname.SetString(name.c_str(), w.mAl); + targetNames.PushBack(tname, w.mAl); + } + extras.AddMember("targetNames", targetNames, w.mAl); + obj.AddMember("extras", extras, w.mAl); + } } inline void Write(Value& obj, Node& n, AssetWriter& w) diff --git a/code/AssetLib/glTF2/glTF2Exporter.cpp b/code/AssetLib/glTF2/glTF2Exporter.cpp index 566f95e80..2359851f1 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.cpp +++ b/code/AssetLib/glTF2/glTF2Exporter.cpp @@ -824,9 +824,14 @@ void glTF2Exporter::ExportMeshes() /*************** Targets for blendshapes ****************/ if (aim->mNumAnimMeshes > 0) { + bool bExportTargetNames = this->mProperties->HasPropertyBool("GLTF2_TARGETNAMES_EXP") && + this->mProperties->GetPropertyBool("GLTF2_TARGETNAMES_EXP"); + p.targets.resize(aim->mNumAnimMeshes); for (unsigned int am = 0; am < aim->mNumAnimMeshes; ++am) { aiAnimMesh *pAnimMesh = aim->mAnimMeshes[am]; + if (bExportTargetNames) + m->targetNames.push_back(pAnimMesh->mName.data); // position if (pAnimMesh->HasPositions()) { From 62273b63e5dff9c9b120e1f7345616ff82695811 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 6 Jun 2020 12:22:01 +0200 Subject: [PATCH 13/44] closes https://github.com/assimp/assimp/issues/3256: Remove redundand code --- code/AssetLib/M3D/m3d.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/AssetLib/M3D/m3d.h b/code/AssetLib/M3D/m3d.h index 75bcdfeb7..3d7a2564c 100644 --- a/code/AssetLib/M3D/m3d.h +++ b/code/AssetLib/M3D/m3d.h @@ -5071,7 +5071,7 @@ unsigned char *m3d_save(m3d_t *model, int quality, int flags, unsigned int *size ptr += sprintf(ptr, "\r\n"); } /* mathematical shapes face */ - if (model->numshape && model->numshape && !(flags & M3D_EXP_NOFACE)) { + if (model->numshape !(flags & M3D_EXP_NOFACE)) { for (j = 0; j < model->numshape; j++) { sn = _m3d_safestr(model->shape[j].name, 0); if (!sn) { From 2c0df39ef30621b330f988afc70f1e3ac85b536b Mon Sep 17 00:00:00 2001 From: Alec Date: Sat, 6 Jun 2020 14:33:41 +0200 Subject: [PATCH 14/44] Added rapidjson define to avoid warnings in c++17 --- code/AssetLib/glTF/glTFAsset.h | 1 + code/AssetLib/glTF/glTFCommon.h | 1 + code/AssetLib/glTF2/glTF2Asset.h | 1 + 3 files changed, 3 insertions(+) diff --git a/code/AssetLib/glTF/glTFAsset.h b/code/AssetLib/glTF/glTFAsset.h index 4c7aaa908..f0333ad0c 100644 --- a/code/AssetLib/glTF/glTFAsset.h +++ b/code/AssetLib/glTF/glTFAsset.h @@ -62,6 +62,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #define RAPIDJSON_HAS_STDSTRING 1 +#define RAPIDJSON_NOMEMBERITERATORCLASS #include #include #include diff --git a/code/AssetLib/glTF/glTFCommon.h b/code/AssetLib/glTF/glTFCommon.h index d161e8cd6..b151918b6 100644 --- a/code/AssetLib/glTF/glTFCommon.h +++ b/code/AssetLib/glTF/glTFCommon.h @@ -53,6 +53,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #define RAPIDJSON_HAS_STDSTRING 1 +#define RAPIDJSON_NOMEMBERITERATORCLASS #include #include #include diff --git a/code/AssetLib/glTF2/glTF2Asset.h b/code/AssetLib/glTF2/glTF2Asset.h index 99bcaf072..fc0fe1544 100644 --- a/code/AssetLib/glTF2/glTF2Asset.h +++ b/code/AssetLib/glTF2/glTF2Asset.h @@ -64,6 +64,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #define RAPIDJSON_HAS_STDSTRING 1 +#define RAPIDJSON_NOMEMBERITERATORCLASS #include #include #include From b37d42f6b548a42732abdee2fab84c322021443e Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sun, 7 Jun 2020 18:03:47 +0200 Subject: [PATCH 15/44] Perform sanity check only in debug closes https://github.com/assimp/assimp/issues/3255 --- code/AssetLib/Blender/BlenderDNA.inl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/code/AssetLib/Blender/BlenderDNA.inl b/code/AssetLib/Blender/BlenderDNA.inl index a41ceab7f..e59adab1d 100644 --- a/code/AssetLib/Blender/BlenderDNA.inl +++ b/code/AssetLib/Blender/BlenderDNA.inl @@ -4,7 +4,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2020, assimp team - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -239,11 +238,13 @@ bool Structure :: ReadFieldPtr(TOUT (&out)[N], const char* name, try { f = &(*this)[name]; +#ifdef _DEBUG // sanity check, should never happen if the genblenddna script is right if ((FieldFlag_Pointer|FieldFlag_Pointer) != (f->flags & (FieldFlag_Pointer|FieldFlag_Pointer))) { throw Error((Formatter::format(),"Field `",name,"` of structure `", this->name,"` ought to be a pointer AND an array")); } +#endif // _DEBUG db.reader->IncPtr(f->offset); From 26421aebc99fadf30334571f70b48f677296c77e Mon Sep 17 00:00:00 2001 From: Yingying Wang Date: Mon, 8 Jun 2020 13:33:16 -0700 Subject: [PATCH 16/44] support channel name in blendshape name --- code/AssetLib/FBX/FBXConverter.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/code/AssetLib/FBX/FBXConverter.cpp b/code/AssetLib/FBX/FBXConverter.cpp index eb995f429..261567e48 100644 --- a/code/AssetLib/FBX/FBXConverter.cpp +++ b/code/AssetLib/FBX/FBXConverter.cpp @@ -1163,7 +1163,8 @@ unsigned int FBXConverter::ConvertMeshSingleMaterial(const MeshGeometry &mesh, c const std::vector &curVertices = shapeGeometry->GetVertices(); const std::vector &curNormals = shapeGeometry->GetNormals(); const std::vector &curIndices = shapeGeometry->GetIndices(); - animMesh->mName.Set(FixAnimMeshName(shapeGeometry->Name())); + //losing channel name if using shapeGeometry->Name() + animMesh->mName.Set(FixAnimMeshName(blendShapeChannel->Name())); for (size_t j = 0; j < curIndices.size(); j++) { const unsigned int curIndex = curIndices.at(j); aiVector3D vertex = curVertices.at(j); From 9e46f9751fc81d75791aad02f00b6c1bf104261f Mon Sep 17 00:00:00 2001 From: Paul Arden Date: Tue, 9 Jun 2020 15:49:38 +1000 Subject: [PATCH 17/44] Check for invalid texture coordinate accessor. Fixes #3269. --- code/AssetLib/glTF2/glTF2Importer.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index 83f022e8f..a957d9d9b 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -416,6 +416,10 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { attr.color[c]->ExtractData(aim->mColors[c]); } for (size_t tc = 0; tc < attr.texcoord.size() && tc < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++tc) { + if (!attr.texcoord[tc]) { + throw DeadlyImportError("GLTF: Texture coordinate accessor not found or non-contiguous texture coordinate sets"); + } + if (attr.texcoord[tc]->count != aim->mNumVertices) { DefaultLogger::get()->warn("Texcoord stream size in mesh \"" + mesh.name + "\" does not match the vertex count"); From efbabf3b0daccb976639a3734fa9018c4b3e13fa Mon Sep 17 00:00:00 2001 From: David Date: Tue, 9 Jun 2020 08:32:59 +0200 Subject: [PATCH 18/44] Implement contextmanager for load --- port/PyAssimp/pyassimp/core.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/port/PyAssimp/pyassimp/core.py b/port/PyAssimp/pyassimp/core.py index 85cfe8233..37beac886 100644 --- a/port/PyAssimp/pyassimp/core.py +++ b/port/PyAssimp/pyassimp/core.py @@ -14,10 +14,13 @@ if sys.version_info >= (3,0): xrange = range -try: import numpy -except ImportError: numpy = None +try: + import numpy +except ImportError: + numpy = None import logging import ctypes +from contextlib import contextmanager logger = logging.getLogger("pyassimp") # attach default null handler to logger so it doesn't complain # even if you don't attach another handler to logger @@ -272,6 +275,13 @@ def recur_pythonize(node, scene): for c in node.children: recur_pythonize(c, scene) +def release(scene): + ''' + Release resources of a loaded scene. + ''' + _assimp_lib.release(ctypes.pointer(scene)) + +@contextmanager def load(filename, file_type = None, processing = postprocess.aiProcess_Triangulate): @@ -319,7 +329,10 @@ def load(filename, raise AssimpError('Could not import file!') scene = _init(model.contents) recur_pythonize(scene.rootnode, scene) - return scene + try: + yield scene + finally: + release(scene) def export(scene, filename, @@ -373,9 +386,6 @@ def export_blob(scene, raise AssimpError('Could not export scene to blob!') return exportBlobPtr -def release(scene): - _assimp_lib.release(ctypes.pointer(scene)) - def _finalize_texture(tex, target): setattr(target, "achformathint", tex.achFormatHint) if numpy: From b84dbb68ecf4d6e30c535c715ebaa88646dbe5db Mon Sep 17 00:00:00 2001 From: David Date: Tue, 9 Jun 2020 08:33:09 +0200 Subject: [PATCH 19/44] Update READMEs --- port/PyAssimp/README.md | 25 ++++++++++--------------- port/PyAssimp/README.rst | 23 ++++++++++------------- 2 files changed, 20 insertions(+), 28 deletions(-) diff --git a/port/PyAssimp/README.md b/port/PyAssimp/README.md index d64d72763..c9944f717 100644 --- a/port/PyAssimp/README.md +++ b/port/PyAssimp/README.md @@ -42,17 +42,14 @@ substituted by assertions ...): ```python -from pyassimp import * -scene = load('hello.3ds') +from pyassimp import load +with load('hello.3ds') as scene: -assert len(scene.meshes) -mesh = scene.meshes[0] + assert len(scene.meshes) + mesh = scene.meshes[0] -assert len(mesh.vertices) -print(mesh.vertices[0]) - -# don't forget this one, or you will leak! -release(scene) + assert len(mesh.vertices) + print(mesh.vertices[0]) ``` @@ -61,13 +58,11 @@ scene: ```python -from pyassimp import * -scene = load('hello.3ds') +from pyassimp import load +with load('hello.3ds') as scene: -for c in scene.rootnode.children: - print(str(c)) - -release(scene) + for c in scene.rootnode.children: + print(str(c)) ``` diff --git a/port/PyAssimp/README.rst b/port/PyAssimp/README.rst index f909e2cd0..03b7968f1 100644 --- a/port/PyAssimp/README.rst +++ b/port/PyAssimp/README.rst @@ -49,30 +49,27 @@ substituted by assertions ...): .. code:: python - from pyassimp import * - scene = load('hello.3ds') + from pyassimp import load + with load('hello.3ds') as scene: - assert len(scene.meshes) - mesh = scene.meshes[0] + assert len(scene.meshes) + mesh = scene.meshes[0] - assert len(mesh.vertices) - print(mesh.vertices[0]) + assert len(mesh.vertices) + print(mesh.vertices[0]) - # don't forget this one, or you will leak! - release(scene) Another example to list the 'top nodes' in a scene: .. code:: python - from pyassimp import * - scene = load('hello.3ds') + from pyassimp import load + with load('hello.3ds') as scene: - for c in scene.rootnode.children: - print(str(c)) + for c in scene.rootnode.children: + print(str(c)) - release(scene) INSTALL ------- From 5c7bed01f1133956d63419220f918577e49dbc03 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 9 Jun 2020 10:06:50 +0200 Subject: [PATCH 20/44] remove dead code. --- code/AssetLib/STL/STLLoader.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/code/AssetLib/STL/STLLoader.cpp b/code/AssetLib/STL/STLLoader.cpp index 7d7fdb7c9..00cfe4b4d 100644 --- a/code/AssetLib/STL/STLLoader.cpp +++ b/code/AssetLib/STL/STLLoader.cpp @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2020, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -101,11 +99,13 @@ static bool IsAsciiSTL(const char *buffer, unsigned int fileSize) { const char *bufferEnd = buffer + fileSize; - if (!SkipSpaces(&buffer)) + if (!SkipSpaces(&buffer)) { return false; + } - if (buffer + 5 >= bufferEnd) + if (buffer + 5 >= bufferEnd) { return false; + } bool isASCII(strncmp(buffer, "solid", 5) == 0); if (isASCII) { @@ -386,7 +386,6 @@ void STLImporter::LoadASCIIFile(aiNode *root) { pMesh->mNormals[i].y = normalBuffer[i].y; pMesh->mNormals[i].z = normalBuffer[i].z; } -// memcpy(pMesh->mNormals, &normalBuffer[0].x, pMesh->mNumVertices * sizeof(aiVector3D)); normalBuffer.clear(); } From 35fb4c93703790d19aa4407016297fe6c0f873eb Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 9 Jun 2020 10:07:25 +0200 Subject: [PATCH 21/44] Remove dead code --- code/AssetLib/STL/STLLoader.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/code/AssetLib/STL/STLLoader.cpp b/code/AssetLib/STL/STLLoader.cpp index 00cfe4b4d..2d710b084 100644 --- a/code/AssetLib/STL/STLLoader.cpp +++ b/code/AssetLib/STL/STLLoader.cpp @@ -375,7 +375,6 @@ void STLImporter::LoadASCIIFile(aiNode *root) { pMesh->mVertices[i].y = positionBuffer[i].y; pMesh->mVertices[i].z = positionBuffer[i].z; } - //memcpy(pMesh->mVertices, &positionBuffer[0].x, pMesh->mNumVertices * sizeof(aiVector3D)); positionBuffer.clear(); } // also only process normalBuffer when filled, else exception when accessing with index operator From 42afc40d168b0dfdd26794de4b076156e61ad081 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 9 Jun 2020 10:13:43 +0200 Subject: [PATCH 22/44] Disable warning only for gcc 8.0 or greater --- code/AssetLib/glTF/glTFAsset.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/code/AssetLib/glTF/glTFAsset.h b/code/AssetLib/glTF/glTFAsset.h index 56cf18527..50d977603 100644 --- a/code/AssetLib/glTF/glTFAsset.h +++ b/code/AssetLib/glTF/glTFAsset.h @@ -60,13 +60,23 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#ifndef RAPIDJSON_HAS_STDSTRING #define RAPIDJSON_HAS_STDSTRING 1 +#endif + +#if (__GNUC__ == 8 && __GNUC_MINOR__ >= 0) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wclass-memaccess" +#endif + #include #include #include + +#if (__GNUC__ == 8 && __GNUC_MINOR__ >= 0) #pragma GCC diagnostic pop +#endif + #ifdef ASSIMP_API # include # include From 620bebb999e16fcf9dfa2c561b12c261e579b204 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 9 Jun 2020 10:14:57 +0200 Subject: [PATCH 23/44] diable warning only for gcc 8.0 or greater --- code/AssetLib/glTF2/glTF2Asset.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/code/AssetLib/glTF2/glTF2Asset.h b/code/AssetLib/glTF2/glTF2Asset.h index 9c1938123..f70421ef2 100644 --- a/code/AssetLib/glTF2/glTF2Asset.h +++ b/code/AssetLib/glTF2/glTF2Asset.h @@ -4,7 +4,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2020, assimp team - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -63,13 +62,23 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#ifndef RAPIDJSON_HAS_STDSTRING #define RAPIDJSON_HAS_STDSTRING 1 +#endif + +#if (__GNUC__ == 8 && __GNUC_MINOR__ >= 0) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wclass-memaccess" +#endif + #include #include #include + +#if (__GNUC__ == 8 && __GNUC_MINOR__ >= 0) #pragma GCC diagnostic pop +#endif + #ifdef ASSIMP_API #include #include From 88c1509d69994a8433da53c337c2beeb0a28d232 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 9 Jun 2020 10:15:35 +0200 Subject: [PATCH 24/44] Remove dead code --- code/Common/SceneCombiner.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/code/Common/SceneCombiner.cpp b/code/Common/SceneCombiner.cpp index 2b3b701d5..4f262192b 100644 --- a/code/Common/SceneCombiner.cpp +++ b/code/Common/SceneCombiner.cpp @@ -981,7 +981,6 @@ void GetArrayCopy(Type*& dest, ai_uint num ) { for ( size_t i=0; i Date: Tue, 9 Jun 2020 10:16:05 +0200 Subject: [PATCH 25/44] Remove dead code. --- test/unit/utFindInvalidData.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/unit/utFindInvalidData.cpp b/test/unit/utFindInvalidData.cpp index a31479af7..17a9c0379 100644 --- a/test/unit/utFindInvalidData.cpp +++ b/test/unit/utFindInvalidData.cpp @@ -110,8 +110,6 @@ TEST_F(utFindInvalidDataProcess, testStepNegativeResult) { mMesh->mNormals[i].x = mMesh->mNormals[i].y = mMesh->mNormals[i].z =0; mMesh->mBitangents[i].x = mMesh->mBitangents[i].y = mMesh->mBitangents[i].z = 0; } - //::memset(mMesh->mNormals, 0, mMesh->mNumVertices * sizeof(aiVector3D)); - //::memset(mMesh->mBitangents, 0, mMesh->mNumVertices * sizeof(aiVector3D)); mMesh->mTextureCoords[2][455] = aiVector3D(std::numeric_limits::quiet_NaN()); From 1b0b4d8d1a33f46e9edfca0b9f1758e194c68d28 Mon Sep 17 00:00:00 2001 From: kimkulling Date: Fri, 12 Jun 2020 11:43:31 +0200 Subject: [PATCH 26/44] add a unittest. --- .../glTF2/issue_3269/texcoord_crash.gltf | 610 ++++++++++++++++++ test/unit/utglTF2ImportExport.cpp | 6 + 2 files changed, 616 insertions(+) create mode 100644 test/models/glTF2/issue_3269/texcoord_crash.gltf diff --git a/test/models/glTF2/issue_3269/texcoord_crash.gltf b/test/models/glTF2/issue_3269/texcoord_crash.gltf new file mode 100644 index 000000000..bc5d13f31 --- /dev/null +++ b/test/models/glTF2/issue_3269/texcoord_crash.gltf @@ -0,0 +1,610 @@ +{ + "accessors" : [ + { + "bufferView" : 0, + "componentType" : 5121, + "count" : 6, + "max" : [ + 3 + ], + "min" : [ + 0 + ], + "type" : "SCALAR" + }, + { + "bufferView" : 1, + "componentType" : 5126, + "count" : 4, + "max" : [ + 1.2000000476837158, + 1.2000001668930054, + -5.205485820169997e-08 + ], + "min" : [ + 0.19999980926513672, + 0.20000004768371582, + -1.5933926533762133e-07 + ], + "type" : "VEC3" + }, + { + "bufferView" : 2, + "componentType" : 5126, + "count" : 4, + "max" : [ + -2.1316282072803006e-14, + 1.0728441424134871e-07, + 1.0 + ], + "min" : [ + -2.1316282072803006e-14, + 1.0728441424134871e-07, + 1.0 + ], + "type" : "VEC3" + }, + { + "name": "TopRight_TEXCOORD_0", + "bufferView" : 3, + "componentType" : 5126, + "count" : 4, + "max" : [ + 1.0, + 0.3999999761581421 + ], + "min" : [ + 0.6000000238418579, + 0.0 + ], + "type" : "VEC2" + }, + { + "bufferView" : 4, + "componentType" : 5121, + "count" : 6, + "max" : [ + 3 + ], + "min" : [ + 0 + ], + "type" : "SCALAR" + }, + { + "bufferView" : 5, + "componentType" : 5126, + "count" : 4, + "max" : [ + -0.20000006258487701, + 1.2000000476837158, + 1.2601539367551595e-07 + ], + "min" : [ + -1.2000001668930054, + 0.19999974966049194, + -3.3740951721483725e-07 + ], + "type" : "VEC3" + }, + { + "bufferView" : 6, + "componentType" : 5126, + "count" : 4, + "max" : [ + 1.7807019503379706e-07, + 2.853547016457014e-07, + 1.0 + ], + "min" : [ + 1.7807019503379706e-07, + 2.853547016457014e-07, + 1.0 + ], + "type" : "VEC3" + }, + { + "name": "TopLeft_TEXCOORD_0", + "bufferView" : 7, + "componentType" : 5126, + "count" : 4, + "max" : [ + 0.3999999463558197, + 0.3999999761581421 + ], + "min" : [ + 7.915305388905836e-08, + 0.0 + ], + "type" : "VEC2" + }, + { + "bufferView" : 8, + "componentType" : 5121, + "count" : 6, + "max" : [ + 3 + ], + "min" : [ + 0 + ], + "type" : "SCALAR" + }, + { + "bufferView" : 9, + "componentType" : 5126, + "count" : 4, + "max" : [ + 1.2000001668930054, + -0.1999996304512024, + 5.255118367131217e-07 + ], + "min" : [ + 0.2000000923871994, + -1.2000000476837158, + 6.20869826661874e-08 + ], + "type" : "VEC3" + }, + { + "bufferView" : 10, + "componentType" : 5126, + "count" : 4, + "max" : [ + -1.7807025187721592e-07, + 2.853545879588637e-07, + 1.0 + ], + "min" : [ + -1.7807025187721592e-07, + 2.853545879588637e-07, + 1.0 + ], + "type" : "VEC3" + }, + { + "name": "BottomRight_TEXCOORD_0", + "bufferView" : 11, + "componentType" : 5126, + "count" : 4, + "max" : [ + 0.9999998807907104, + 1.0 + ], + "min" : [ + 0.6000000834465027, + 0.599999874830246 + ], + "type" : "VEC2" + }, + { + "bufferView" : 12, + "componentType" : 5121, + "count" : 6, + "max" : [ + 3 + ], + "min" : [ + 0 + ], + "type" : "SCALAR" + }, + { + "bufferView" : 13, + "componentType" : 5126, + "count" : 4, + "max" : [ + 1.0000001192092896, + 1.0000001192092896, + -0.052591003477573395 + ], + "min" : [ + -1.0000001192092896, + -1.0000001192092896, + -0.05259115248918533 + ], + "type" : "VEC3" + }, + { + "bufferView" : 14, + "componentType" : 5126, + "count" : 4, + "max" : [ + 1.0658142730467397e-14, + -7.450581307466564e-08, + -1.0 + ], + "min" : [ + 1.0658142730467397e-14, + -7.450581307466564e-08, + -1.0 + ], + "type" : "VEC3" + }, + { + "bufferView" : 15, + "componentType" : 5121, + "count" : 6, + "max" : [ + 3 + ], + "min" : [ + 0 + ], + "type" : "SCALAR" + }, + { + "bufferView" : 16, + "componentType" : 5126, + "count" : 4, + "max" : [ + -0.19999969005584717, + -0.2000000774860382, + 5.255118367131217e-07 + ], + "min" : [ + -1.2000000476837158, + -1.2000001668930054, + 6.208701108789683e-08 + ], + "type" : "VEC3" + }, + { + "bufferView" : 17, + "componentType" : 5126, + "count" : 4, + "max" : [ + -8.526512829121202e-14, + 4.6342486825778906e-07, + 1.0 + ], + "min" : [ + -8.526512829121202e-14, + 4.6342486825778906e-07, + 1.0 + ], + "type" : "VEC3" + }, + { + "name": "BottomLeft_TEXCOORD_0", + "bufferView" : 18, + "componentType" : 5126, + "count" : 4, + "max" : [ + 0.40000009536743164, + 0.9999999208469248 + ], + "min" : [ + 0.0, + 0.6000000536441803 + ], + "type" : "VEC2" + } + ], + "asset" : { + "copyright" : "Copyright 2017-2018 Analytical Graphics, Inc., CC-BY 4.0 https://creativecommons.org/licenses/by/4.0/ - Mesh and textures by Ed Mackey.", + "generator" : "Khronos Blender glTF 2.0 exporter, plus hand-edits", + "version" : "2.0" + }, + "bufferViews" : [ + { + "buffer" : 0, + "byteLength" : 6, + "byteOffset" : 0, + "target" : 34963 + }, + { + "buffer" : 0, + "byteLength" : 48, + "byteOffset" : 8, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 48, + "byteOffset" : 56, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 32, + "byteOffset" : 104, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 6, + "byteOffset" : 136, + "target" : 34963 + }, + { + "buffer" : 0, + "byteLength" : 48, + "byteOffset" : 144, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 48, + "byteOffset" : 192, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 32, + "byteOffset" : 240, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 6, + "byteOffset" : 272, + "target" : 34963 + }, + { + "buffer" : 0, + "byteLength" : 48, + "byteOffset" : 280, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 48, + "byteOffset" : 328, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 32, + "byteOffset" : 376, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 6, + "byteOffset" : 408, + "target" : 34963 + }, + { + "buffer" : 0, + "byteLength" : 48, + "byteOffset" : 416, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 48, + "byteOffset" : 464, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 6, + "byteOffset" : 512, + "target" : 34963 + }, + { + "buffer" : 0, + "byteLength" : 48, + "byteOffset" : 520, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 48, + "byteOffset" : 568, + "target" : 34962 + }, + { + "buffer" : 0, + "byteLength" : 32, + "byteOffset" : 616, + "target" : 34962 + } + ], + "buffers" : [ + { + "byteLength" : 648, + "uri" : "data:application/octet-stream;base64,AAECAwEAAACamZk/2sxMPuySX7PAzEw+mZmZP9gWK7TKzEw+0MxMPuySX7OYmZk/m5mZP9gWK7QAAMCoO2TmMwAAgD8AAMCoO2TmMwAAgD8AAMCoO2TmMwAAgD8AAMCoO2TmMwAAgD8AAIA/zMzMPpqZGT8AAAAAmpkZP8zMzD4AAIA/AAAAAAABAgMBAAAA0cxMvsnMTD7okl+zm5mZv5iZmT/aFiu0mZmZv7zMTD7ZTgc03sxMvpqZmT82JbW0kDM/NNoymTQAAIA/kDM/NNoymTQAAIA/kDM/NNoymTQAAIA/kDM/NNoymTQAAIA/y8zMPszMzD7a+qkzAAAAANr6qTPMzMw+y8zMPgAAAAAAAQIDAQAAAJuZmT+YmZm/5hANNdPMTD7HzEy+rlSFM+bMTD6amZm/Boi6NJmZmT+0zEy+8u6ANJQzP7TWMpk0AACAP5QzP7TWMpk0AACAP5QzP7TWMpk0AACAP5QzP7TWMpk0AACAP/3/fz8AAIA/m5kZP5iZGT+cmRk/AACAP/7/fz+YmRk/AAECAAMBAAABAIC//f9/P9JpV70BAIA//f9/v6ppV739/3+/AQCAv6ppV739/38/AQCAP9JpV70CAEAoAQCgswAAgL8CAEAoAQCgswAAgL8CAEAoAQCgswAAgL8CAEAoAQCgswAAgL8AAQIDAQAAALjMTL6ZmZm/5hANNZqZmb/izEy+uFSFM5iZmb+bmZm/5hANNcjMTL7SzEy+slSFMwAAwKmhzPg0AACAPwAAwKmhzPg0AACAPwAAwKmhzPg0AACAPwAAwKmhzPg0AACAP9DMzD7//38/AACAM5qZGT8AAAAA//9/P83MzD6amRk/" + } + ], + "images" : [ + { + "uri" : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4QYcDhkW4QlOQwAAHBNJREFUeNrt3XuMlNX9P/DP4u7OLkshVEGkgKyVWzTNsloTbYtGaIgmRGM1tRq1TbVSaqm2qdREwbSJoQlQGm9NiNSmCSyRRhNtm0YTTUyq1jWhtFDF2lKrqcKuCsJegfP74/vbDbC3mZ2Zvcy8Xslkk5lnn3l2zjPn8z7nuWxFSikFAFBWJvgIAEAAAAAEAABAAAAABAAAQAAAAAQAAEAAAAAEAABg9FRmu2BFRUVWy7mxIAAUXqHrcOVY/kOECQBKvWiPVq2rLPUPV4gAQH0pwQBQzAYWHgD08aWq0u5R2J1KaADQ7woADLrj2mkBfaO+UQCg3y+DLwKgP0MA4JQvlC8ToE9iuNwIqIS+hNleIwqgn0EAKPFU7ssK6EPot41TgedsCnmnokLcQCGf6ziHc/lILmepZnPyy2DH0Pqbeut5LqUUFRUVpuSAvPrznr6kv35msD4pm/4sl75xOP1uPn18IepLIWtdtusyA0BRdhZAH0LpcBKgLyyAfkUAwJcSQN8jAOCLBqC/EgDwZQHQ5wkA2MEB9KMCgJ0QAH21AGDHAEB9EAA0DgACxDgNAIW66xEAkFvdLHQNdidAAChDAgAACAAAgAAAAAgAAIAAAAAIAACAAAAAjFk53QgomxsMuFkQAORmNGqnGQAAKEMCAAAIAACAAAAACAAAgAAAAAgAAIAAAACMWZW5/sJQNytwkx8AyE0hbrSXa/01AwAAZUgAAAABAAAQAAAAAQAASklFRUVW/4lPAGDEHTt2LNatWxf19fWRyWRizpw5PhQg74KX7WO0ty+TycTnPve5uP7666O5uXlUC3iphYWKlON1A6N9GWCuH/5IX5bYs32Fet/169fHfffd1+dvKvT7AOUTAMZDH9qfqqqqeOaZZ+Lqq68uWJ+by++Pdr876pcBppQGfYy1NDnePfnkkxERsXXr1ujo6FDwgbz0129n89pobWdHR0f8/e9/j69//evR3d3d74CoVPrFoernUPU3VxPsvGPb/v37o6qqKr75zW9GJpPRewEjpru7OzZt2hSNjY1RV1cXdXV10djYGJs3b45jx471W7w+/PDDWLlyZcyYMSNqa2vjS1/6Urz22mvD3oZMJhMXXHBBPPLIIxERsW/fvn7f92Tt7e2xdu3aqK+vj6qqqpg7d2488MADMWnSpAEHou+++27ceuutMXXq1JgxY0bcdddd0dHR0e/ou2QGtqnAImLQRzEMtO6urq60cePGtHjx4jRx4sQ0ceLEtHjx4vSLX/widXd397uODz74IN15553p7LPPTjU1Nemyyy5Lr776at7bcrqdO3emZcuWpalTp6aqqqp07rnnptWrV6eWlpZBP8uhPmOAQvShXV1dadmyZQP2NcuXLz+lH+15fsqUKX2WraurS2+//XZefeg//vGPFBGpvr5+0OW7urrSV77ylaz7yZ7nzjzzzD7LrVu3LqvaNl7rZ8kGgLG2857sjjvuGHC75s+fnz755BMBABjVALBhw4YUEWn69OmpqakptbS0pJaWlrRt27Y0bdq0FBFp06ZNfdZx/fXXp1dffTW1t7en3bt3p8bGxhQR6dvf/vawtuXQoUPpxRdf7F3P2rVrB13+l7/8Ze92P/300+nw4cPp4MGDaefOnYMGgMsvvzzt2rUrHTp0KK1ZsyZFRJo3b96wBngCwCgHgLGy857uySefTBGRZs2alXbs2JEOHDiQ2traUnNzc7riiitSRKT77rtvyPUp+EAx+9CGhoYUEempp57qs3xTU1OKiLR48eIh+6SXX36535H7cGrIddddlzo6Ogbd9osvvjhFRNq+fXtWf2fPc8ePH+997sMPP0wRkTKZjAAwHgPAaO+8A7n00ktTRKTm5uY+r/33v/9NEZEWLVokAACjGgBqa2tTRKTW1tY+y7e0tKSISBMnThyyT/r0009TRKTq6uq8asj69euz2va6uroUEengwYM5BYCTdXd3Z73seK6fJXsfgLfeeisiIq688so+ry1btuyUZQbT0NAQERHvv/9+QbZr9+7dERFx8cUX9zmRZPbs2RER8e9//9vZR8CYOfE6m+cG0nMiXVVV1bBO+H700UcjImLz5s2xf//+IX+vq6srIiJqa2uH/TdXVlaWRdtOsPMWZ+cdyIkTJ7J+T4DRMn/+/IiIeP755/u89sILL0RExIIFC4Zcz0svvRQREYsWLRrWdqxatSruueee+OCDD+Kqq66Kjz76aNDlZ86cecpgqxg6OzvLNwCM5HWK433nPd3ChQsjIuKdd94pyOdUKjsiMLbccsstERGxevXqaGpqitbW1mhtbY2mpqZYvXr1Kcuc7Pe//3188sknceTIkXjuued6l73pppuGvS0bNmyIa6+9Nt5888245pprBu33emZ977777tizZ08cPXo0mpub47bbbsv7M+kJFw899FC0tbUVZcCahnmfneG+YUGPU4y1M1inTZuWtm/f3nsS4Pbt2wc9CfC5555LH3/8cfr000/Ts88+m84555w+y+ZzDsBjjz2WIiLNnj07PfHEE+k///lPam9vT21tbWnPnj3p0UcfTRdddNGQ65s5c2bvGbFHjx51EBMoaB/a1dWVli5dmvOVVP09LrvsstTZ2ZlXH3r06NHeE/xuuOGGdOLEiX6X37dvX5o0aVKfbaiurs7puH5/z69cuXJU616ha2/JBoDR3nkHehw/fjx961vfyvpkj4E+19HeEYHSDgA9/ejGjRtTQ0NDqq2tTbW1tamhoSFt2rRpwHuprFmzJn3+859PmUwmzZo1K/34xz9OR44cyXtbUkrpf//7X5ozZ06KiHT33XcPuPzrr7+elixZkmpqatLkyZPTihUr0htvvJEiItXU1Aw7ABw5ciTde++96bzzzkuZTCZNmTIlXXLJJeM2AOT8vwB6DgHkcsy92Aa6P3N3d3c8/PDD8dvf/rb3hL8FCxbErbfeGt///vdPOdGjZx1r1qyJnTt3xnvvvRfTpk2Lb3zjG7Fu3bqoq6vL+7M5eRv/+Mc/xhNPPBGvvfZaHDhwICoqKqK+vj6WLl0aN954Y3z5y18e9G87evRo/PSnP42dO3fG+++/HzU1NbFgwYK87rgFUOh+eKzYs2dPXHjhhTFv3rw+dxMcb59xoWpvSQSActh5AfShuevu7o69e/fGqlWr4s9//nPcdddd8fDDDwsAAoAAAFBqfehANWrGjBnxxhtv9J7MV+4BYILdFoBSsmLFipg9e3bU1tZGdXV1zJ07N+688854/fXXx23xL0qgSAWOa+N1dsAMAAClNsofTKWPe+yHEwAoNIcAAEAAAAAEAABAAAAABAAAYJwa9lUAA12q4Gx6ABiewWpooeuuGQAAKEMCAAAIAACAAAAACAAAQGkY9lUAA5116OoAABi+kaqjZgAAoAwJAAAgAAAAAgAAIAAAAKWhMp9f7u9MRWf7A8Dw5XKVXT411wwAAJQhAQAABAAAQAAAAEpSXicB9nfygRMDASA/I1FLzQAAQBkSAABAAAAABAAAoCRV5ruC009UcMIfAOQnm5Ps8623ZgAAoAwJAAAgAAAA5SDvcwBOPwbhnAAAyF+x66kZAAAoQwIAAAgAAEA5qCzESk4+TuGYPwDkb7Bz7ApRa80AAEAZEgAAoAwV5BDAyVMRDgcAQGEUs6aaAQCAMiQAAEAZqizUinqmKUz7A0Bh9HeIvVB11gwAAJgByD+lmAkAgMIpVl01AwAAZgDyTylG/gBQOCfPsBeyxk4o9Eae/u8LAYCxN8CeMNY3EADMAhR+gF2RVGwAKDtOAgQAAQAAEAAAAAEAABAAAAABAAAQAAAAAQAAEAAAAAEAABAAAAABAAAQAAAAAQAAEAAAAAEAABAAAAABAAAEAABAAAAABAAAQAAAAAQAAEAAAAAEAABAAAAABAAAQAAAAAQAAEAAAAAEAABAAAAABAAAQAAAAAQAABAAAAABAAAQAAAAAQAAEAAAAAEAABAAAAABAAAQAAAAAQAAEAAAAAEAABAAAAABAAAQAAAAAQAAEAAAQAAAAAQAAEAAAAAEAABAAAAABAAAQAAAAAQAAEAAAAAEAABAAAAAiqTSRwBDq6io6Pf5lJIPR7uAGQAolwKT6zJoFxAAYBwVmWxGkiklxUa7gAAApSLbApJtQUK7gAAARppoFxAAwEgT7QICABhpol1AAAAjTbQLCABgpKldtAsIAGCkqV20CwgAYKSpXbQLCABgpKldAAEAjDS1CyAAgJGmdgEBAMpg5Jjrz1xHmrn+RLuAAABFLjI9BSHXn6O5bu2iXaBo379kjgyyHqX2N8pEu4AAAOOoSIwV5foV1C4wuhwCAAABAAAQAAAAAQAAEAAAgHHKVQCQzRfF5WbaBcwAwPgtFMW4M1wx161dtAsIAJCH4d4RLpdbzhZj3dpFu0DRArhDADD4KHKw28MqGtoFzABAiRaZbEeaaBcQAKAE+Lez2gUEADDSNNLULiAAgJGmkaZ2AQEAjDSNNLULCABgpIl2AQEAjDTRLiAAgJEm2gUEADDSRLuAAABGmmgXEADASBPtAgIAGGmiXUAAgBEpNoVYBu0CY43/BggAZgAAAAEAABAAAAABAAAQAAAAAQAAEAAAAAEAABAAAAABAAAQAAAAAQAAEAAAAAEAABAAAAABAAAQAABAAAAABAAAQAAAAAQAAEAAAAAEAABAAAAABAAAQAAAAAQAAEAAAAAEAABAAAAABAAAQAAAAAQAAEAAAAABAAAQAAAAAQAAEAAAAAEAABAAAAABAAAQAAAAAQAAEAAAAAEAABAAAAABAAAQAAAAAQAAEAAAAAEAAAQAAEAAAAAEAABAAAAABAAAQAAAAAQAAEAAAAAEAABAAAAARkaljwCGVlFR0e/zKSUfjnYBMwBQLgUm12XQLiAAwDgqMtmMJFNKio12AQEASkW2BSTbgoR2AQEAjDTRLiAAgJEm2gUEADDSRLuAAABGmmgXEADASFO7aBcQAMBIU7toFxAAwEhTu2gXEADASFO7AAIAGGlqF0AAACNN7QICAJTByDHXn7mONHP9iXYBAQCKXGR6CkKuP0dz3dpFu0DRvn/JHBlkPUrtb5SJdgEBAMZRkRgryvUrqF1gdDkEAAACAAAgAAAAAgAAIAAAAOOUqwAgmy+Ky820C5gBgPFbKIpxZ7hirlu7aBcQACAPw70jXC63nC3GurWLdoGiBXCHAGDwUeRgt4dVNLQLmAGAEi0y2Y400S4gAEAJ8G9ntQsIAGCkaaSpXUAAACNNI03tAgIAGGkaaWoXEADASBPtAgIAGGmiXUAAACNNtAsIAGCkiXYBAQCMNNEuIACAkSbaBQQAMNJEu4AAACNSbAqxDNoFxhr/DRAAzAAAAAIAACAAAAACAAAgAAAAAgAAIAAAAAIAACAAAAACAAAgAAAAAgAAIAAAAAIAACAAAAACAAAgAACAAAAACAAAgAAAAAgAAIAAAAAIAACAAAAACAAAgAAAAAgAAIAAAAAIAACAAAAACAAAgAAAAAgAAIAAAAACAAAgAAAAAgAAIAAAAAIAACAAAAACAAAgAAAAAgAAIAAAAAIAACAAAAACAAAgAAAAAgAAIAAAAAIAAAgAAIAAAAAIAACAAAAACAAAgAAAAAgAAIAAAAAIAACAAAAACAAAgAAAAIzJAFBRUeETBYAiKHSNLWgASCkJAQBQhOKfUhrbMwCF3kAAKHfFGGBXFmsDBQEAKMzguhh11UmAAFCGCjYDYOQPAIXXU1cLXWfNAACAGYDCJRSzAQCQn2LWVDMAAFCGBAAAKEMFOQRg2h8ACu/kmlroWmsGAADKkAAAAGWoIIcABpqiOP01ACA7xa6nZgAAoAwJAAAgAAAA5SDvcwAc8weAwju9nha63poBAIAyJAAAgAAAAJSDvM8BGOoYRX/LAAADG4laagYAAMqQAAAAAgAAIAAAACUpr5MAnfAHAIXXXy0tdM01AwAAZUgAAAABAAAQAACAkpTXSYDZnqQw0LIAwOjUUTMAAFCGBAAAEAAAAAFgDKqoqDjlUVNTE+eff3786Ec/ikOHDuW93lxfA0AfPS7bKhX4rIJin7wwWCNfc8018cwzz+S13sFObHQiI4A+erQ+wzFzEuDpKa/nkVLq91FoKaU4ceJEfPrpp/H4449HRMSf/vQn3z6AMUAfnd9n199joLo74gFgrKSkSZMmxc033xwREdOnTz/l9e7u7ti0aVM0NjZGXV1d1NXVRWNjY2zevDmOHTvWb9oa7MPt7/ls3+P0dezevTuuvfbamDx5ckyePDluuumm+Pjjj+Oll16K5cuXx2c+85mYMWNGrFy5Mtrb230jAH20PrrgSWNYIqLfR7Gd/j6HDx9O999/f4qItGXLlt7nu7q60rJlywbczuXLl6fu7u5B/5ahHrm8x+nbP2HChD7Lz507t9/13H///QlgPNBHF/+zLVTdHbcB4PRHY2Njev/993uX27BhQ4qINH369NTU1JRaWlpSS0tL2rZtW5o2bVqKiLRp06YBd9rBdujhvsfJ61qyZEnatWtX6ujoSA8++GC/z69duzZFRDr//PP1KsC4CgD6aAFgxD6AiEhTp05Nu3btSiml1NDQkCIiPfXUU33W0dTUlCIiLV68OK+dK9f3OHldx48f733uwIEDKSJSVVXVKc9/+OGHKSJSJpPRqwDjOgDoowWAgk8vdXV1pf3796fbb7+9d0onpZRqa2tTRKTW1tY+62hpaUkRkSZOnJjXzpXrewy0rmPHjuX0PMBYDwD66LEfACbkce5ATmcpFusazaqqqjj33HNj48aNERHx6quvDnnZRCrwVQn5vscZZ5yR0/MA44U+OvcTJ0fqKruSuRPg4cOHT2nU+fPnR0TE888/32fZF154ISIiFixY0Oe1zs7OAd/j9NeG+x4A5UYfPfaM+wDQ1dUVu3fvjttvvz0iIpYsWRIREbfccktERKxevTqampqitbU1Wltbo6mpKVavXn3KMhERM2fOjIiIhx56KNra2k55j4Fey/U9AMqNPnoMG8kTQIq9/smTJ6e//e1vvcedli5dmvXlHytXrhxwmwd6Ldf3GOxYVa7PA4y3kwD10aNfQwtyEuBYCQBnnHFGOuecc9LNN9+c3n777VOW7erqShs3bkwNDQ2ptrY21dbWpoaGhrRp06Y+jX7kyJF07733pvPOOy9lMpk0ZcqUdMkllwz5Wi7vIQAA5RYA9NFjNwCM2P8COPnYDwAwujXUvwMGgDJUaZQPAGPDYDW00LXXDAAAlCEBAAAEAABAAAAABAAAQAAoCceOHYt169ZFfX19ZDKZmDNnjr0CoAwV8x/Xjcm/dyRvBBSR/2WCPesv1GavX78+7rvvvj7bWOj3ASC/elJdXR1nnXVWXHrppfGTn/wkLr744oLWh1x+vxg1otj1UwA4zcKFC+Ott96KrVu3xk033RSZTKZojQtAYepJVVVVPPPMM3H11VcLAALA8NTU1MSJEyeis7PzlG0XAADGRgDo6Yc7Ozvjn//8Z/zsZz+LHTt2xBe+8IX461//WrT3K/UAULLnAPzud7+Lr371q/HZz342qqurY+7cufGDH/wgWltbT/mwOzs7o7u7OyZMmND74Z8eBE5+ADA6MplMXHDBBfHII49ERMS+ffv6FNDT++n29vZYu3Zt1NfXR1VVVcydOzceeOCBmDRp0oB9+rvvvhu33nprTJ06NWbMmBF33XVXdHR09Fuox3ONKMkZgO985zuxZcuWfl+bP39+/OUvf4kpU6b0u60nH/8fiQQGQG79/ZtvvhmLFi2K+vr6+Ne//jXg8t3d3bF06dJ4+eWXs+rTe37/zDPPPGWwGBGxbt26ePDBB4esc/nUCDMAefrNb34TW7ZsiVmzZsWOHTviwIED0dbWFs3NzXHFFVfEvn374uc//3mfD/P//2vkAZ8/+XUARt7hw4fjpZdeiptvvjkiIm655ZZBl3/88cfj5ZdfjunTp8fTTz8dhw8fjoMHD8bOnTsH/b0LL7wwdu3aFYcOHYo1a9ZERMS2bdv6LcTjuUaU3AzAZZddFq+88ko0NzfHRRdddMpr7733XsyePTsWLVoUe/fuHXR9zgEAGBszAP257rrrYtu2bb0nbvfXb3/xi1+M5ubm2L59e9x4441D9vE9zx0/fjwmTPi/8fGBAwfi7LPPjkwm0+9hACcBjqEAMGnSpDh69Oig66ipqYn29nYBAGAcBoD169f3jswH67d76sHBgwfjrLPOyjoAnPzcsWPHoqqqKqtlx1sAmDCcDRzscfp0yEhPj5w4cWLIZU5OcQCMbT3149FHH42IiM2bN8f+/fuH/L2urq6IiKitrR32e1dWVo743znQY6j6W/QAMNYtXLgwIiLeeeedggSRzs5O3z6AMWDVqlVxzz33xAcffBBXXXVVfPTRR4MuP3PmzIiI2L17d9G2aTzXiJILAHfccUdERFxxxRWxdevWePfdd6OjoyPa29tj79698dhjj/W5e9RgO85DDz0UbW1tvnkAY8CGDRvi2muvjTfffDOuueaaQQvwlVdeGRERd999d+zZsyeOHj0azc3Ncdttt+W9HaVQI3I+B2Ckj1Hk+v7Hjx+P22+/PX79619ntZ0DHcf57ne/G7/61a9G/O8DIAbtn9va2uLyyy+P5ubmuOGGG2LHjh2nTIP3LP/2229HY2NjHDly5JTfr66u7j08kM1x/f6eH40aUej6W3IzABMmTIitW7fGH/7wh/ja174Ws2bNiurq6shkMrFw4cL43ve+N+g1oSenzHvvvTfOO++8yGQyMWXKlLjkkkt8IwFG2cSJE+PZZ5+NOXPmxFNPPRU//OEP+11u3rx58eKLL8aSJUuipqYmJk+eHCtWrIhXXnklIv7vhPB8ZiLGe40YdzMAAJCPPXv2xIUXXhjz5s3rczfB8TAjUqj6W2lXAKAcdHd3x969e2PVqlUREbF8+fKy/jzMAABQukVugJo1Y8aMeOONN3pP5ivHGYCczwHI9zpF/1AHgJGyYsWKmD17dtTW1vb+Y7g777wzXn/99TFV/LOpnYW+z86I3wnQLAEAjH7tnOBjB4DyIwAAgAAAAAgAAIAAAAAIAACAAAAAjBc53QrYNf4AUHjZ1M5C12AzAABQhgQAABAAAAABAAAQAAAAAQAAEAAAAAEAABizcroRUKFuVJDtugCg1I1W3awc7x+KIAGAGlViAWAkGkiAAFDAy7E+VNoFhrdzCA4A+moBgCF3RDshgH5UAGDAndwODujzEADo84XxZQH0VwgAvmy+aIC+h2FxI6AS/GJme00pgH5FAKDEUzqAPoSTVRZ6R8h2OqhQN1DI9zrO4V4+kutZqtme/DLUMbSBpt56nu95rqKiIlJKvT8Bci3+p/chp/czg/VJ2fRnufSNw+l38+njC1FfClnrilGHzQCUqJ4dQPEH9CHkNQPA+PiyAuhnEAB8AQH0SQgAvkwA+jMEAF8EAH2jAICdFkC/KwDYoQDQxwsAGhcA4UEA8MECwLiudRVJJQaAsuNOgAAgAAAAAgAAIAAAAAIAACAAAAACAAAgAAAAAgAAMIr+HyP6AtxN3EzKAAAAAElFTkSuQmCC" + } + ], + "materials" : [ + { + "name" : "BackPlaneMat", + "doubleSided": true, + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.16000001668930075, + 0.16000001668930075, + 0.16000001668930075, + 1.0 + ], + "metallicFactor" : 0.0 + } + }, + { + "name" : "BottomLeftMat", + "doubleSided": true, + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.0, + 0.16000000476837162, + 0.800000011920929, + 1.0 + ], + "baseColorTexture" : { + "index" : 0 + }, + "metallicFactor" : 0.0 + } + }, + { + "name" : "BottomRightMat", + "doubleSided": true, + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.0, + 0.800000011920929, + 0.0, + 1.0 + ], + "baseColorTexture" : { + "index" : 0 + }, + "metallicFactor" : 0.0 + } + }, + { + "name" : "TopLeftMat", + "doubleSided": true, + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.800000011920929, + 0.800000011920929, + 0.0, + 1.0 + ], + "baseColorTexture" : { + "index" : 0 + }, + "metallicFactor" : 0.0 + } + }, + { + "name" : "TopRightMat", + "doubleSided": true, + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.800000011920929, + 0.08000000238418581, + 0.0, + 1.0 + ], + "baseColorTexture" : { + "index" : 0 + }, + "metallicFactor" : 0.0 + } + } + ], + "meshes" : [ + { + "name" : "TopRightMesh", + "primitives" : [ + { + "attributes" : { + "NORMAL" : 2, + "POSITION" : 1, + "TEXCOORD_1" : 3 + }, + "indices" : 0, + "material" : 4 + } + ] + }, + { + "name" : "TopLeftMesh", + "primitives" : [ + { + "attributes" : { + "NORMAL" : 6, + "POSITION" : 5, + "TEXCOORD_0" : 7 + }, + "indices" : 4, + "material" : 3 + } + ] + }, + { + "name" : "BottomRightMesh", + "primitives" : [ + { + "attributes" : { + "NORMAL" : 10, + "POSITION" : 9, + "TEXCOORD_0" : 11 + }, + "indices" : 8, + "material" : 2 + } + ] + }, + { + "name" : "BackPlaneMesh", + "primitives" : [ + { + "attributes" : { + "NORMAL" : 14, + "POSITION" : 13 + }, + "indices" : 12, + "material" : 0 + } + ] + }, + { + "name" : "BottomLeftMesh", + "primitives" : [ + { + "attributes" : { + "NORMAL" : 17, + "POSITION" : 16, + "TEXCOORD_0" : 18 + }, + "indices" : 15, + "material" : 1 + } + ] + } + ], + "nodes" : [ + { + "mesh" : 3, + "name" : "BackPlane" + }, + { + "mesh" : 4, + "name" : "BottomLeftObj" + }, + { + "mesh" : 2, + "name" : "BottomRightObj" + }, + { + "mesh" : 1, + "name" : "TopLeftObj" + }, + { + "mesh" : 0, + "name" : "TopRightObj" + } + ], + "samplers" : [ + {} + ], + "scene" : 0, + "scenes" : [ + { + "name" : "Scene", + "nodes" : [ + 0, + 2, + 1, + 4, + 3 + ] + } + ], + "textures" : [ + { + "sampler" : 0, + "source" : 0 + } + ] +} diff --git a/test/unit/utglTF2ImportExport.cpp b/test/unit/utglTF2ImportExport.cpp index f0f18d503..8fa5e08ce 100644 --- a/test/unit/utglTF2ImportExport.cpp +++ b/test/unit/utglTF2ImportExport.cpp @@ -534,3 +534,9 @@ TEST_F(utglTF2ImportExport, norootnode_scenewithoutnodes) { ASSERT_NE(scene, nullptr); ASSERT_NE(scene->mRootNode, nullptr); } + +TEST_F(utglTF2ImportExport, norootnode_issue_3269) { + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/issue_3269/texcoord_crash.gltf", aiProcess_ValidateDataStructure); + ASSERT_NE(scene, nullptr); +} From ad62e6e863d7b144d0c6118701be42add4874cf6 Mon Sep 17 00:00:00 2001 From: kimkulling Date: Fri, 12 Jun 2020 12:29:51 +0200 Subject: [PATCH 27/44] fix unittest check - cannot work, just crash shall not occurr any more. --- code/AssetLib/glTF2/glTF2Asset.inl | 2 +- test/unit/utglTF2ImportExport.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index 80207a4be..5a99525b2 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -722,7 +722,7 @@ template void Accessor::ExtractData(T *&outData) { uint8_t *data = GetPointer(); if (!data) { - throw DeadlyImportError("GLTF: data is NULL"); + throw DeadlyImportError("GLTF2: data is nullptr."); } const size_t elemSize = GetElementSize(); diff --git a/test/unit/utglTF2ImportExport.cpp b/test/unit/utglTF2ImportExport.cpp index 8fa5e08ce..6791d5f89 100644 --- a/test/unit/utglTF2ImportExport.cpp +++ b/test/unit/utglTF2ImportExport.cpp @@ -535,8 +535,9 @@ TEST_F(utglTF2ImportExport, norootnode_scenewithoutnodes) { ASSERT_NE(scene->mRootNode, nullptr); } +// Shall not crash! TEST_F(utglTF2ImportExport, norootnode_issue_3269) { Assimp::Importer importer; const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/issue_3269/texcoord_crash.gltf", aiProcess_ValidateDataStructure); - ASSERT_NE(scene, nullptr); + ASSERT_EQ(scene, nullptr); } From f938a6b744bf9a951f60016dd982520fc3a41d4f Mon Sep 17 00:00:00 2001 From: kimkulling Date: Fri, 12 Jun 2020 14:52:02 +0200 Subject: [PATCH 28/44] fix leak: just ignore broken texture coordinates. --- code/AssetLib/glTF2/glTF2Importer.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index a957d9d9b..1c044e7ce 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -417,7 +417,8 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { } for (size_t tc = 0; tc < attr.texcoord.size() && tc < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++tc) { if (!attr.texcoord[tc]) { - throw DeadlyImportError("GLTF: Texture coordinate accessor not found or non-contiguous texture coordinate sets"); + DefaultLogger::get()->warn("Texture coordinate accessor not found or non-contiguous texture coordinate sets."); + continue; } if (attr.texcoord[tc]->count != aim->mNumVertices) { From f0f6612b81c1eec1ba2ebc8bcdfb227252223174 Mon Sep 17 00:00:00 2001 From: kimkulling Date: Mon, 15 Jun 2020 20:52:11 +0200 Subject: [PATCH 29/44] disable warning for radjson. --- contrib/rapidjson/include/rapidjson/document.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/contrib/rapidjson/include/rapidjson/document.h b/contrib/rapidjson/include/rapidjson/document.h index 93b091f64..473d846e3 100644 --- a/contrib/rapidjson/include/rapidjson/document.h +++ b/contrib/rapidjson/include/rapidjson/document.h @@ -17,6 +17,11 @@ /*! \file document.h */ +#if (__GNUC__ >= 8 && __GNUC_MINOR__ >= 0) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wclass-memaccess" +#endif + #include "reader.h" #include "internal/meta.h" #include "internal/strfunc.h" @@ -2610,4 +2615,8 @@ private: RAPIDJSON_NAMESPACE_END RAPIDJSON_DIAG_POP +#if (__GNUC__ == 8 && __GNUC_MINOR__ >= 0) +#pragma GCC diagnostic pop +#endif + #endif // RAPIDJSON_DOCUMENT_H_ From 524e2e4728a6b62d6fc6e33239031b30f7fc7a6c Mon Sep 17 00:00:00 2001 From: kimkulling Date: Mon, 15 Jun 2020 21:34:49 +0200 Subject: [PATCH 30/44] fix warning, hopefully ... --- code/Common/SceneCombiner.cpp | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/code/Common/SceneCombiner.cpp b/code/Common/SceneCombiner.cpp index 4f262192b..5d49baaf4 100644 --- a/code/Common/SceneCombiner.cpp +++ b/code/Common/SceneCombiner.cpp @@ -66,6 +66,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { +#if (__GNUC__ >= 8 && __GNUC_MINOR__ >= 0) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wclass-memaccess" +#endif + // ------------------------------------------------------------------------------------------------ // Add a prefix to a string inline @@ -75,7 +80,7 @@ void PrefixString(aiString& string,const char* prefix, unsigned int len) { return; if (len+string.length>=MAXLEN-1) { - ASSIMP_LOG_VERBOSE_DEBUG("Can't add an unique prefix because the string is too long"); + ASSIMP_LOG_VERBOSE_DEBUG("Can't add an unique prefix because the string is too long"); ai_assert(false); return; } @@ -98,8 +103,9 @@ void SceneCombiner::AddNodeHashes(aiNode* node, std::set& hashes) } // Process all children recursively - for (unsigned int i = 0; i < node->mNumChildren;++i) + for (unsigned int i = 0; i < node->mNumChildren;++i) { AddNodeHashes(node->mChildren[i],hashes); + } } // ------------------------------------------------------------------------------------------------ @@ -978,9 +984,7 @@ void GetArrayCopy(Type*& dest, ai_uint num ) { Type* old = dest; dest = new Type[num]; - for ( size_t i=0; imBitangents, dest->mNumVertices ); unsigned int n = 0; - while (dest->HasTextureCoords(n)) - GetArrayCopy( dest->mTextureCoords[n++], dest->mNumVertices ); + while (dest->HasTextureCoords(n)) { + GetArrayCopy( dest->mTextureCoords[n++], dest->mNumVertices ); + } n = 0; - while (dest->HasVertexColors(n)) - GetArrayCopy( dest->mColors[n++], dest->mNumVertices ); + while (dest->HasVertexColors(n)) { + GetArrayCopy( dest->mColors[n++], dest->mNumVertices ); + } // make a deep copy of all bones CopyPtrArray(dest->mBones,dest->mBones,dest->mNumBones); @@ -1343,5 +1349,9 @@ void SceneCombiner::Copy(aiMetadata** _dest, const aiMetadata* src) { } } +#if (__GNUC__ >= 8 && __GNUC_MINOR__ >= 0) +# pragma GCC diagnostic pop +#endif + } // Namespace Assimp From bb613df5431dfa4cbc0ccc5396d768e08a8ae076 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 16 Jun 2020 16:31:47 +0200 Subject: [PATCH 31/44] Update issue templates To make our life easier --- .github/ISSUE_TEMPLATE/bug_report.md | 38 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 20 ++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..dd84ea782 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..bbcbbe7d6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. From b3af5c5a14e4e54abf531c76a0f103eb57b1688d Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 17 Jun 2020 20:37:39 +0200 Subject: [PATCH 32/44] closes https://github.com/assimp/assimp/issues/3253 : remove useless code --- code/AssetLib/Obj/ObjFileParser.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/code/AssetLib/Obj/ObjFileParser.cpp b/code/AssetLib/Obj/ObjFileParser.cpp index a825b96ca..ecf51240b 100644 --- a/code/AssetLib/Obj/ObjFileParser.cpp +++ b/code/AssetLib/Obj/ObjFileParser.cpp @@ -253,7 +253,6 @@ static bool isDataDefinitionEnd(const char *tmp) { if (*tmp == '\\') { tmp++; if (IsLineEnd(*tmp)) { - tmp++; return true; } } From 6acaaee80f51fa43e62f55a8728bdfb26d364be2 Mon Sep 17 00:00:00 2001 From: Kota Iguchi Date: Sat, 20 Jun 2020 15:27:28 +0900 Subject: [PATCH 33/44] Check invalid vertex id for bone weight --- code/PostProcessing/LimitBoneWeightsProcess.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/code/PostProcessing/LimitBoneWeightsProcess.cpp b/code/PostProcessing/LimitBoneWeightsProcess.cpp index 5b8934159..b4f0b48d8 100644 --- a/code/PostProcessing/LimitBoneWeightsProcess.cpp +++ b/code/PostProcessing/LimitBoneWeightsProcess.cpp @@ -114,6 +114,10 @@ void LimitBoneWeightsProcess::ProcessMesh(aiMesh* pMesh) for (unsigned int w = 0; w < bone->mNumWeights; ++w) { const aiVertexWeight& vw = bone->mWeights[w]; + + if (vertexWeights.size() <= vw.mVertexId) + continue; + vertexWeights[vw.mVertexId].push_back(Weight(b, vw.mWeight)); maxVertexWeights = std::max(maxVertexWeights, vertexWeights[vw.mVertexId].size()); } From 6397bfbf904415d75b55c7389cb66a9958359a04 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sun, 21 Jun 2020 12:03:38 +0200 Subject: [PATCH 34/44] replace NULL by nullptr on loadFile. --- code/AMF/AMFImporter.cpp | 4 +++- code/BVH/BVHLoader.cpp | 10 ++++++---- code/CSM/CSMLoader.cpp | 2 +- code/HMP/HMPLoader.cpp | 5 +++-- code/Irr/IRRMeshLoader.cpp | 5 +++-- code/LWS/LWSLoader.cpp | 2 +- code/MD2/MD2Loader.cpp | 11 ++++++----- code/MD3/MD3Loader.cpp | 5 +++-- code/MD5/MD5Loader.cpp | 11 +++++------ code/MDL/HalfLife/HL1MDLLoader.h | 6 ++++-- code/Raw/RawLoader.cpp | 2 +- code/X/XFileImporter.cpp | 2 +- include/assimp/irrXMLWrapper.h | 2 +- 13 files changed, 38 insertions(+), 29 deletions(-) diff --git a/code/AMF/AMFImporter.cpp b/code/AMF/AMFImporter.cpp index df4324d4d..d93ca54cf 100644 --- a/code/AMF/AMFImporter.cpp +++ b/code/AMF/AMFImporter.cpp @@ -407,7 +407,9 @@ void AMFImporter::ParseFile(const std::string& pFile, IOSystem* pIOHandler) std::unique_ptr file(pIOHandler->Open(pFile, "rb")); // Check whether we can read from the file - if(file.get() == NULL) throw DeadlyImportError("Failed to open AMF file " + pFile + "."); + if (file.get() == nullptr) { + throw DeadlyImportError("Failed to open AMF file " + pFile + "."); + } // generate a XML reader for it std::unique_ptr mIOWrapper(new CIrrXML_IOStreamReader(file.get())); diff --git a/code/BVH/BVHLoader.cpp b/code/BVH/BVHLoader.cpp index 1110754c2..5d335d233 100644 --- a/code/BVH/BVHLoader.cpp +++ b/code/BVH/BVHLoader.cpp @@ -124,12 +124,14 @@ void BVHLoader::InternReadFile( const std::string& pFile, aiScene* pScene, IOSys // read file into memory std::unique_ptr file( pIOHandler->Open( pFile)); - if( file.get() == NULL) - throw DeadlyImportError( "Failed to open file " + pFile + "."); + if (file.get() == nullptr) { + throw DeadlyImportError("Failed to open file " + pFile + "."); + } size_t fileSize = file->FileSize(); - if( fileSize == 0) - throw DeadlyImportError( "File is too small."); + if (fileSize == 0) { + throw DeadlyImportError("File is too small."); + } mBuffer.resize( fileSize); file->Read( &mBuffer.front(), 1, fileSize); diff --git a/code/CSM/CSMLoader.cpp b/code/CSM/CSMLoader.cpp index fbabea12e..47563feb1 100644 --- a/code/CSM/CSMLoader.cpp +++ b/code/CSM/CSMLoader.cpp @@ -127,7 +127,7 @@ void CSMImporter::InternReadFile( const std::string& pFile, std::unique_ptr file( pIOHandler->Open( pFile, "rb")); // Check whether we can read from the file - if( file.get() == NULL) { + if( file.get() == nullptr) { throw DeadlyImportError( "Failed to open CSM file " + pFile + "."); } diff --git a/code/HMP/HMPLoader.cpp b/code/HMP/HMPLoader.cpp index 0dea09ee0..45f45d022 100644 --- a/code/HMP/HMPLoader.cpp +++ b/code/HMP/HMPLoader.cpp @@ -122,8 +122,9 @@ void HMPImporter::InternReadFile( const std::string& pFile, std::unique_ptr file(mIOHandler->Open(pFile)); // Check whether we can read from the file - if( file.get() == nullptr) - throw DeadlyImportError( "Failed to open HMP file " + pFile + "."); + if (file.get() == nullptr) { + throw DeadlyImportError("Failed to open HMP file " + pFile + "."); + } // Check whether the HMP file is large enough to contain // at least the file header diff --git a/code/Irr/IRRMeshLoader.cpp b/code/Irr/IRRMeshLoader.cpp index 13db70e91..d07ff87ea 100644 --- a/code/Irr/IRRMeshLoader.cpp +++ b/code/Irr/IRRMeshLoader.cpp @@ -139,8 +139,9 @@ void IRRMeshImporter::InternReadFile( const std::string& pFile, std::unique_ptr file( pIOHandler->Open( pFile)); // Check whether we can read from the file - if( file.get() == NULL) - throw DeadlyImportError( "Failed to open IRRMESH file " + pFile + ""); + if (file.get() == nullptr) { + throw DeadlyImportError("Failed to open IRRMESH file " + pFile + "."); + } // Construct the irrXML parser CIrrXML_IOStreamReader st(file.get()); diff --git a/code/LWS/LWSLoader.cpp b/code/LWS/LWSLoader.cpp index 952c3ccbc..d02d24ea3 100644 --- a/code/LWS/LWSLoader.cpp +++ b/code/LWS/LWSLoader.cpp @@ -501,7 +501,7 @@ void LWSImporter::InternReadFile(const std::string &pFile, aiScene *pScene, std::unique_ptr file(pIOHandler->Open(pFile, "rb")); // Check whether we can read from the file - if (file.get() == NULL) { + if (file.get() == nullptr) { throw DeadlyImportError("Failed to open LWS file " + pFile + "."); } diff --git a/code/MD2/MD2Loader.cpp b/code/MD2/MD2Loader.cpp index 473ab99bc..abc5f06ff 100644 --- a/code/MD2/MD2Loader.cpp +++ b/code/MD2/MD2Loader.cpp @@ -221,20 +221,21 @@ void MD2Importer::InternReadFile( const std::string& pFile, std::unique_ptr file( pIOHandler->Open( pFile)); // Check whether we can read from the file - if( file.get() == NULL) - throw DeadlyImportError( "Failed to open MD2 file " + pFile + ""); + if (file.get() == nullptr) { + throw DeadlyImportError("Failed to open MD2 file " + pFile + ""); + } // check whether the md3 file is large enough to contain // at least the file header fileSize = (unsigned int)file->FileSize(); - if( fileSize < sizeof(MD2::Header)) - throw DeadlyImportError( "MD2 File is too small"); + if (fileSize < sizeof(MD2::Header)) { + throw DeadlyImportError("MD2 File is too small"); + } std::vector mBuffer2(fileSize); file->Read(&mBuffer2[0], 1, fileSize); mBuffer = &mBuffer2[0]; - m_pcHeader = (BE_NCONST MD2::Header*)mBuffer; #ifdef AI_BUILD_BIG_ENDIAN diff --git a/code/MD3/MD3Loader.cpp b/code/MD3/MD3Loader.cpp index 2051ecdda..28799c0fc 100644 --- a/code/MD3/MD3Loader.cpp +++ b/code/MD3/MD3Loader.cpp @@ -747,8 +747,9 @@ void MD3Importer::InternReadFile( const std::string& pFile, aiScene* pScene, IOS std::unique_ptr file( pIOHandler->Open( pFile)); // Check whether we can read from the file - if( file.get() == NULL) - throw DeadlyImportError( "Failed to open MD3 file " + pFile + "."); + if (file.get() == nullptr) { + throw DeadlyImportError("Failed to open MD3 file " + pFile + "."); + } // Check whether the md3 file is large enough to contain the header fileSize = (unsigned int)file->FileSize(); diff --git a/code/MD5/MD5Loader.cpp b/code/MD5/MD5Loader.cpp index d428873df..2aa07df18 100644 --- a/code/MD5/MD5Loader.cpp +++ b/code/MD5/MD5Loader.cpp @@ -332,13 +332,12 @@ void MD5Importer::AttachChilds_Anim(int iParentID, aiNode *piParent, AnimBoneLis // ------------------------------------------------------------------------------------------------ // Load a MD5MESH file void MD5Importer::LoadMD5MeshFile() { - std::string pFile = mFile + "md5mesh"; - std::unique_ptr file(mIOHandler->Open(pFile, "rb")); + std::string filename = mFile + "md5mesh"; + std::unique_ptr file(mIOHandler->Open(filename, "rb")); // Check whether we can read from the file if (file.get() == nullptr || !file->FileSize()) { - ASSIMP_LOG_WARN("Failed to access MD5MESH file: " + pFile); - return; + throw DeadlyImportError("Failed to open MD5 file " + filename + "."); } bHadMD5Mesh = true; LoadFileIntoMemory(file.get()); @@ -552,9 +551,9 @@ void MD5Importer::LoadMD5AnimFile() { // Check whether we can read from the file if (!file.get() || !file->FileSize()) { - ASSIMP_LOG_WARN("Failed to read MD5ANIM file: " + pFile); - return; + throw DeadlyImportError("Failed to open MD3 file " + file + "."); } + LoadFileIntoMemory(file.get()); // parse the basic file structure diff --git a/code/MDL/HalfLife/HL1MDLLoader.h b/code/MDL/HalfLife/HL1MDLLoader.h index c4293259c..2891ddb1e 100644 --- a/code/MDL/HalfLife/HL1MDLLoader.h +++ b/code/MDL/HalfLife/HL1MDLLoader.h @@ -222,12 +222,14 @@ void HL1MDLLoader::load_file_into_buffer(const std::string &file_path, unsigned std::unique_ptr file(io_->Open(file_path)); - if (file.get() == NULL) + if (file.get() == nullptr) { throw DeadlyImportError("Failed to open MDL file " + DefaultIOSystem::fileName(file_path) + "."); + } const size_t file_size = file->FileSize(); - if (file_size < sizeof(MDLFileHeader)) + if (file_size < sizeof(MDLFileHeader)) { throw DeadlyImportError("MDL file is too small."); + } buffer = new unsigned char[1 + file_size]; file->Read((void *)buffer, 1, file_size); diff --git a/code/Raw/RawLoader.cpp b/code/Raw/RawLoader.cpp index 092323cc0..bb58026f0 100644 --- a/code/Raw/RawLoader.cpp +++ b/code/Raw/RawLoader.cpp @@ -104,7 +104,7 @@ void RAWImporter::InternReadFile( const std::string& pFile, std::unique_ptr file( pIOHandler->Open( pFile, "rb")); // Check whether we can read from the file - if( file.get() == NULL) { + if( file.get() == nullptr) { throw DeadlyImportError( "Failed to open RAW file " + pFile + "."); } diff --git a/code/X/XFileImporter.cpp b/code/X/XFileImporter.cpp index 846023b93..0b256b0bc 100644 --- a/code/X/XFileImporter.cpp +++ b/code/X/XFileImporter.cpp @@ -113,7 +113,7 @@ const aiImporterDesc* XFileImporter::GetInfo () const { void XFileImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) { // read file into memory std::unique_ptr file( pIOHandler->Open( pFile)); - if ( file.get() == NULL ) { + if ( file.get() == nullptr ) { throw DeadlyImportError( "Failed to open file " + pFile + "." ); } diff --git a/include/assimp/irrXMLWrapper.h b/include/assimp/irrXMLWrapper.h index 65a7be298..52c174791 100644 --- a/include/assimp/irrXMLWrapper.h +++ b/include/assimp/irrXMLWrapper.h @@ -63,7 +63,7 @@ namespace Assimp { * @code * // open the file * std::unique_ptr file( pIOHandler->Open( pFile)); - * if( file.get() == NULL) { + * if( file.get() == nullptr ) { * throw DeadlyImportError( "Failed to open file " + pFile + "."); * } * From 5717ea466f04584b157598b568c98615bea6f71f Mon Sep 17 00:00:00 2001 From: Ryan Styrczula Date: Mon, 22 Jun 2020 17:19:26 -0400 Subject: [PATCH 35/44] FindInvalidDataProcess: Mark removed meshes as "out" There's a code path where an input scene only has a single mesh. In this case, if ProcessMesh returns 2, we delete the mesh and move on with the postprocessing. UpdateMeshReferences is not called and so the deleted mesh is left dangling in nodes. In a later step (SplitMesh in my testing), it then tries to deference a null pointer and we explode. With `out = true`, we can now hit the DeadlyImportError instead of a segfault. --- code/PostProcessing/FindInvalidDataProcess.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/code/PostProcessing/FindInvalidDataProcess.cpp b/code/PostProcessing/FindInvalidDataProcess.cpp index 3ad2446b3..42c5a4ca9 100644 --- a/code/PostProcessing/FindInvalidDataProcess.cpp +++ b/code/PostProcessing/FindInvalidDataProcess.cpp @@ -127,6 +127,7 @@ void FindInvalidDataProcess::Execute(aiScene *pScene) { pScene->mMeshes[a] = NULL; meshMapping[a] = UINT_MAX; + out = true; continue; } From 9188f56839a29f2ef526e47c492681d0338cec72 Mon Sep 17 00:00:00 2001 From: Malcolm Tyrrell Date: Tue, 23 Jun 2020 11:55:51 +0100 Subject: [PATCH 36/44] Allow assert handling to be replaced. --- code/CMakeLists.txt | 1 + code/Common/AssertHandler.cpp | 72 +++++++++++++++++ code/Common/AssertHandler.h | 75 ++++++++++++++++++ include/assimp/ai_assert.h | 16 ++-- test/CMakeLists.txt | 1 + test/unit/Common/utAssertHandler.cpp | 111 +++++++++++++++++++++++++++ 6 files changed, 269 insertions(+), 7 deletions(-) create mode 100644 code/Common/AssertHandler.cpp create mode 100644 code/Common/AssertHandler.h create mode 100644 test/unit/Common/utAssertHandler.cpp diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 000ebad6c..9f8f519d9 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -199,6 +199,7 @@ SET( Common_SRCS Common/simd.h Common/simd.cpp Common/material.cpp + Common/AssertHandler.cpp ) SOURCE_GROUP(Common FILES ${Common_SRCS}) diff --git a/code/Common/AssertHandler.cpp b/code/Common/AssertHandler.cpp new file mode 100644 index 000000000..63b64f828 --- /dev/null +++ b/code/Common/AssertHandler.cpp @@ -0,0 +1,72 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2020, assimp team + + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file AssertHandler.cpp + * @brief Implementation of assert handling logic. + */ + +#include "AssertHandler.h" + +#include +#include + +void Assimp::defaultAiAssertHandler(const char* failedExpression, const char* file, int line) +{ + std::cerr << "ai_assert failure in " << file << "(" << line << "): " << failedExpression << std::endl; + std::abort(); +} + +namespace +{ + Assimp::AiAssertHandler s_handler = Assimp::defaultAiAssertHandler; +} + +void Assimp::setAiAssertHandler(AiAssertHandler handler) +{ + s_handler = handler; +} + +void Assimp::aiAssertViolation(const char* failedExpression, const char* file, int line) +{ + s_handler(failedExpression, file, line); +} diff --git a/code/Common/AssertHandler.h b/code/Common/AssertHandler.h new file mode 100644 index 000000000..2515f0bf2 --- /dev/null +++ b/code/Common/AssertHandler.h @@ -0,0 +1,75 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2020, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file Provides facilities to replace the default assert handler. */ + +#ifndef INCLUDED_AI_ASSERTHANDLER_H +#define INCLUDED_AI_ASSERTHANDLER_H + +#include +#include + +namespace Assimp +{ + // --------------------------------------------------------------------------- + /** Signature of functions which handle assert violations. + */ + using AiAssertHandler = void (*)(const char* failedExpression, const char* file, int line); + + // --------------------------------------------------------------------------- + /** Set the assert handler. + */ + ASSIMP_API void setAiAssertHandler(AiAssertHandler handler); + + // --------------------------------------------------------------------------- + /** The assert handler which is set by default. + * + * This issues a message to stderr and calls abort. + */ + ASSIMP_API void defaultAiAssertHandler(const char* failedExpression, const char* file, int line); + + // --------------------------------------------------------------------------- + /** Dispatches an assert violation to the assert handler. + */ + ASSIMP_API void aiAssertViolation(const char* failedExpression, const char* file, int line); +} // end of namespace Assimp + +#endif // INCLUDED_AI_ASSERTHANDLER_H \ No newline at end of file diff --git a/include/assimp/ai_assert.h b/include/assimp/ai_assert.h index f018163bf..6045a9801 100644 --- a/include/assimp/ai_assert.h +++ b/include/assimp/ai_assert.h @@ -42,14 +42,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef AI_ASSERT_H_INC #define AI_ASSERT_H_INC -#ifdef __GNUC__ -# pragma GCC system_header -#endif +#if defined(ASSIMP_BUILD_DEBUG) || defined(ASSIMP_BUILD_WITH_ASSERTS) + +namespace Assimp +{ + void aiAssertViolation(const char* failedExpression, const char* file, int line); +} + +# define ai_assert(expression) (void)((!!(expression)) || (Assimp::aiAssertViolation(#expression, __FILE__, __LINE__), 0)) +# define ai_assert_entry() ai_assert(false) -#ifdef ASSIMP_BUILD_DEBUG -# include -# define ai_assert(expression) assert( expression ) -# define ai_assert_entry() assert( false ) #else # define ai_assert(expression) # define ai_assert_entry() diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a5f8086e9..8e1746ce2 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -87,6 +87,7 @@ SET( COMMON unit/Common/uiScene.cpp unit/Common/utLineSplitter.cpp unit/Common/utSpatialSort.cpp + unit/Common/utAssertHandler.cpp ) SET( IMPORTERS diff --git a/test/unit/Common/utAssertHandler.cpp b/test/unit/Common/utAssertHandler.cpp new file mode 100644 index 000000000..0ac4c90af --- /dev/null +++ b/test/unit/Common/utAssertHandler.cpp @@ -0,0 +1,111 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2020, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +#include "UnitTestPCH.h" + +/// Ensure this test has asserts on, even if the build type doesn't have asserts by default. +#if !defined(ASSIMP_BUILD_WITH_ASSERTS) +#define ASSIMP_BUILD_WITH_ASSERTS +#endif + +#include +#include + +namespace +{ + /// An exception which is thrown by the testAssertHandler + struct TestAssertException : std::exception + { + TestAssertException(const char* failedExpression, const char* file, int line) + : std::exception("Test Assert Exception") + , m_failedExpression(failedExpression) + , m_file(file) + , m_line(line) + { + } + + std::string m_failedExpression; + std::string m_file; + int m_line; + }; + + /// Swap the default handler, which aborts, by one which throws. + void testAssertHandler(const char* failedExpression, const char* file, int line) + { + throw TestAssertException(failedExpression, file, line); + } + + /// Ensure that the default assert handler is restored after the test is finished. + struct ReplaceHandlerScope + { + ReplaceHandlerScope() + { + Assimp::setAiAssertHandler(testAssertHandler); + } + + ~ReplaceHandlerScope() + { + Assimp::setAiAssertHandler(Assimp::defaultAiAssertHandler); + } + }; +} + +TEST(utAssertHandler, replaceWithThrow) +{ + ReplaceHandlerScope scope; + + try + { + ai_assert((2 + 2 == 5) && "Sometimes people put messages here"); + EXPECT_TRUE(false); + } + catch(const TestAssertException& e) + { + EXPECT_STREQ(e.m_failedExpression.c_str(), "(2 + 2 == 5) && \"Sometimes people put messages here\""); + EXPECT_STREQ(e.m_file.c_str(), __FILE__); + EXPECT_GT(e.m_line, 0); + EXPECT_LT(e.m_line, __LINE__); + } + catch(...) + { + EXPECT_TRUE(false); + } +} From 297764335297053b86b273ad922d4459dda3e229 Mon Sep 17 00:00:00 2001 From: Malcolm Tyrrell Date: Tue, 23 Jun 2020 12:08:06 +0100 Subject: [PATCH 37/44] Fix. --- test/unit/Common/utAssertHandler.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/unit/Common/utAssertHandler.cpp b/test/unit/Common/utAssertHandler.cpp index 0ac4c90af..d04162bdc 100644 --- a/test/unit/Common/utAssertHandler.cpp +++ b/test/unit/Common/utAssertHandler.cpp @@ -52,11 +52,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace { /// An exception which is thrown by the testAssertHandler - struct TestAssertException : std::exception + struct TestAssertException { TestAssertException(const char* failedExpression, const char* file, int line) - : std::exception("Test Assert Exception") - , m_failedExpression(failedExpression) + : m_failedExpression(failedExpression) , m_file(file) , m_line(line) { From ae05dbb2527da54e791e3a115975c3d06504093d Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 23 Jun 2020 21:11:18 +0200 Subject: [PATCH 38/44] define CheckValidFacesIndices also in release builds. --- code/AssetLib/glTF/glTFImporter.cpp | 403 +++++++++++++--------------- 1 file changed, 186 insertions(+), 217 deletions(-) diff --git a/code/AssetLib/glTF/glTFImporter.cpp b/code/AssetLib/glTF/glTFImporter.cpp index 33288313a..b4ef9b06f 100644 --- a/code/AssetLib/glTF/glTFImporter.cpp +++ b/code/AssetLib/glTF/glTFImporter.cpp @@ -48,12 +48,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -#include -#include #include -#include -#include #include +#include +#include +#include +#include #include @@ -69,8 +69,7 @@ static const aiImporterDesc desc = { "", "", "", - aiImporterFlags_SupportTextFlavour | aiImporterFlags_SupportBinaryFlavour | aiImporterFlags_SupportCompressedFlavour - | aiImporterFlags_LimitedSupport | aiImporterFlags_Experimental, + aiImporterFlags_SupportTextFlavour | aiImporterFlags_SupportBinaryFlavour | aiImporterFlags_SupportCompressedFlavour | aiImporterFlags_LimitedSupport | aiImporterFlags_Experimental, 0, 0, 0, @@ -78,11 +77,8 @@ static const aiImporterDesc desc = { "gltf glb" }; -glTFImporter::glTFImporter() -: BaseImporter() -, meshOffsets() -, embeddedTexIdxs() -, mScene( nullptr ) { +glTFImporter::glTFImporter() : + BaseImporter(), meshOffsets(), embeddedTexIdxs(), mScene(nullptr) { // empty } @@ -90,11 +86,11 @@ glTFImporter::~glTFImporter() { // empty } -const aiImporterDesc* glTFImporter::GetInfo() const { +const aiImporterDesc *glTFImporter::GetInfo() const { return &desc; } -bool glTFImporter::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool /* checkSig */) const { +bool glTFImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /* checkSig */) const { const std::string &extension = GetExtension(pFile); if (extension != "gltf" && extension != "glb") { @@ -115,9 +111,8 @@ bool glTFImporter::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool return false; } -inline -void SetMaterialColorProperty(std::vector& embeddedTexIdxs, Asset& /*r*/, glTF::TexProperty prop, aiMaterial* mat, - aiTextureType texType, const char* pKey, unsigned int type, unsigned int idx) { +inline void SetMaterialColorProperty(std::vector &embeddedTexIdxs, Asset & /*r*/, glTF::TexProperty prop, aiMaterial *mat, + aiTextureType texType, const char *pKey, unsigned int type, unsigned int idx) { if (prop.texture) { if (prop.texture->source) { aiString uri(prop.texture->source->uri); @@ -138,22 +133,22 @@ void SetMaterialColorProperty(std::vector& embeddedTexIdxs, Asset& /*r*/, g } } -void glTFImporter::ImportMaterials(glTF::Asset& r) { +void glTFImporter::ImportMaterials(glTF::Asset &r) { mScene->mNumMaterials = unsigned(r.materials.Size()); - mScene->mMaterials = new aiMaterial*[mScene->mNumMaterials]; + mScene->mMaterials = new aiMaterial *[mScene->mNumMaterials]; for (unsigned int i = 0; i < mScene->mNumMaterials; ++i) { - aiMaterial* aimat = mScene->mMaterials[i] = new aiMaterial(); + aiMaterial *aimat = mScene->mMaterials[i] = new aiMaterial(); - Material& mat = r.materials[i]; + Material &mat = r.materials[i]; /*if (!mat.name.empty())*/ { aiString str(mat.id /*mat.name*/); aimat->AddProperty(&str, AI_MATKEY_NAME); } - SetMaterialColorProperty(embeddedTexIdxs, r, mat.ambient, aimat, aiTextureType_AMBIENT, AI_MATKEY_COLOR_AMBIENT ); - SetMaterialColorProperty(embeddedTexIdxs, r, mat.diffuse, aimat, aiTextureType_DIFFUSE, AI_MATKEY_COLOR_DIFFUSE ); + SetMaterialColorProperty(embeddedTexIdxs, r, mat.ambient, aimat, aiTextureType_AMBIENT, AI_MATKEY_COLOR_AMBIENT); + SetMaterialColorProperty(embeddedTexIdxs, r, mat.diffuse, aimat, aiTextureType_DIFFUSE, AI_MATKEY_COLOR_DIFFUSE); SetMaterialColorProperty(embeddedTexIdxs, r, mat.specular, aimat, aiTextureType_SPECULAR, AI_MATKEY_COLOR_SPECULAR); SetMaterialColorProperty(embeddedTexIdxs, r, mat.emission, aimat, aiTextureType_EMISSIVE, AI_MATKEY_COLOR_EMISSIVE); @@ -172,29 +167,25 @@ void glTFImporter::ImportMaterials(glTF::Asset& r) { mScene->mNumMaterials = 1; // Delete the array of length zero created above. delete[] mScene->mMaterials; - mScene->mMaterials = new aiMaterial*[1]; + mScene->mMaterials = new aiMaterial *[1]; mScene->mMaterials[0] = new aiMaterial(); } } - -static inline void SetFace(aiFace& face, int a) -{ +static inline void SetFace(aiFace &face, int a) { face.mNumIndices = 1; face.mIndices = new unsigned int[1]; face.mIndices[0] = a; } -static inline void SetFace(aiFace& face, int a, int b) -{ +static inline void SetFace(aiFace &face, int a, int b) { face.mNumIndices = 2; face.mIndices = new unsigned int[2]; face.mIndices[0] = a; face.mIndices[1] = b; } -static inline void SetFace(aiFace& face, int a, int b, int c) -{ +static inline void SetFace(aiFace &face, int a, int b, int c) { face.mNumIndices = 3; face.mIndices = new unsigned int[3]; face.mIndices[0] = a; @@ -202,9 +193,7 @@ static inline void SetFace(aiFace& face, int a, int b, int c) face.mIndices[2] = c; } -#ifdef ASSIMP_BUILD_DEBUG -static inline bool CheckValidFacesIndices(aiFace* faces, unsigned nFaces, unsigned nVerts) -{ +static inline bool CheckValidFacesIndices(aiFace *faces, unsigned nFaces, unsigned nVerts) { for (unsigned i = 0; i < nFaces; ++i) { for (unsigned j = 0; j < faces[i].mNumIndices; ++j) { unsigned idx = faces[i].mIndices[j]; @@ -214,105 +203,98 @@ static inline bool CheckValidFacesIndices(aiFace* faces, unsigned nFaces, unsign } return true; } -#endif // ASSIMP_BUILD_DEBUG -void glTFImporter::ImportMeshes(glTF::Asset& r) -{ - std::vector meshes; +void glTFImporter::ImportMeshes(glTF::Asset &r) { + std::vector meshes; unsigned int k = 0; meshOffsets.clear(); for (unsigned int m = 0; m < r.meshes.Size(); ++m) { - Mesh& mesh = r.meshes[m]; + Mesh &mesh = r.meshes[m]; - // Check if mesh extensions is used - if(mesh.Extension.size() > 0) - { - for(Mesh::SExtension* cur_ext : mesh.Extension) - { + // Check if mesh extensions is used + if (mesh.Extension.size() > 0) { + for (Mesh::SExtension *cur_ext : mesh.Extension) { #ifdef ASSIMP_IMPORTER_GLTF_USE_OPEN3DGC - if(cur_ext->Type == Mesh::SExtension::EType::Compression_Open3DGC) - { - // Limitations for meshes when using Open3DGC-compression. - // It's a current limitation of sp... Specification have not this part still - about mesh compression. Why only one primitive? - // Because glTF is very flexibly. But in fact it ugly flexible. Every primitive can has own set of accessors and accessors can - // point to a-a-a-a-any part of buffer (through bufferview of course) and even to another buffer. We know that "Open3DGC-compression" - // is applicable only to part of buffer. As we can't guaranty continuity of the data for decoder, we will limit quantity of primitives. - // Yes indices, coordinates etc. still can br stored in different buffers, but with current specification it's a exporter problem. - // Also primitive can has only one of "POSITION", "NORMAL" and less then "AI_MAX_NUMBER_OF_TEXTURECOORDS" of "TEXCOORD". All accessor - // of primitive must point to one continuous region of the buffer. - if(mesh.primitives.size() > 2) throw DeadlyImportError("GLTF: When using Open3DGC compression then only one primitive per mesh are allowed."); + if (cur_ext->Type == Mesh::SExtension::EType::Compression_Open3DGC) { + // Limitations for meshes when using Open3DGC-compression. + // It's a current limitation of sp... Specification have not this part still - about mesh compression. Why only one primitive? + // Because glTF is very flexibly. But in fact it ugly flexible. Every primitive can has own set of accessors and accessors can + // point to a-a-a-a-any part of buffer (through bufferview of course) and even to another buffer. We know that "Open3DGC-compression" + // is applicable only to part of buffer. As we can't guaranty continuity of the data for decoder, we will limit quantity of primitives. + // Yes indices, coordinates etc. still can br stored in different buffers, but with current specification it's a exporter problem. + // Also primitive can has only one of "POSITION", "NORMAL" and less then "AI_MAX_NUMBER_OF_TEXTURECOORDS" of "TEXCOORD". All accessor + // of primitive must point to one continuous region of the buffer. + if (mesh.primitives.size() > 2) throw DeadlyImportError("GLTF: When using Open3DGC compression then only one primitive per mesh are allowed."); - Mesh::SCompression_Open3DGC* o3dgc_ext = (Mesh::SCompression_Open3DGC*)cur_ext; - Ref buf = r.buffers.Get(o3dgc_ext->Buffer); + Mesh::SCompression_Open3DGC *o3dgc_ext = (Mesh::SCompression_Open3DGC *)cur_ext; + Ref buf = r.buffers.Get(o3dgc_ext->Buffer); - buf->EncodedRegion_SetCurrent(mesh.id); - } - else + buf->EncodedRegion_SetCurrent(mesh.id); + } else #endif - { - throw DeadlyImportError("GLTF: Can not import mesh: unknown mesh extension (code: \"" + to_string(cur_ext->Type) + - "\"), only Open3DGC is supported."); - } - } - }// if(mesh.Extension.size() > 0) + { + throw DeadlyImportError("GLTF: Can not import mesh: unknown mesh extension (code: \"" + to_string(cur_ext->Type) + + "\"), only Open3DGC is supported."); + } + } + } // if(mesh.Extension.size() > 0) - meshOffsets.push_back(k); + meshOffsets.push_back(k); k += unsigned(mesh.primitives.size()); for (unsigned int p = 0; p < mesh.primitives.size(); ++p) { - Mesh::Primitive& prim = mesh.primitives[p]; + Mesh::Primitive &prim = mesh.primitives[p]; - aiMesh* aim = new aiMesh(); + aiMesh *aim = new aiMesh(); meshes.push_back(aim); aim->mName = mesh.id; if (mesh.primitives.size() > 1) { - ai_uint32& len = aim->mName.length; + ai_uint32 &len = aim->mName.length; aim->mName.data[len] = '-'; len += 1 + ASSIMP_itoa10(aim->mName.data + len + 1, unsigned(MAXLEN - len - 1), p); } switch (prim.mode) { - case PrimitiveMode_POINTS: - aim->mPrimitiveTypes |= aiPrimitiveType_POINT; - break; + case PrimitiveMode_POINTS: + aim->mPrimitiveTypes |= aiPrimitiveType_POINT; + break; - case PrimitiveMode_LINES: - case PrimitiveMode_LINE_LOOP: - case PrimitiveMode_LINE_STRIP: - aim->mPrimitiveTypes |= aiPrimitiveType_LINE; - break; + case PrimitiveMode_LINES: + case PrimitiveMode_LINE_LOOP: + case PrimitiveMode_LINE_STRIP: + aim->mPrimitiveTypes |= aiPrimitiveType_LINE; + break; - case PrimitiveMode_TRIANGLES: - case PrimitiveMode_TRIANGLE_STRIP: - case PrimitiveMode_TRIANGLE_FAN: - aim->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; - break; + case PrimitiveMode_TRIANGLES: + case PrimitiveMode_TRIANGLE_STRIP: + case PrimitiveMode_TRIANGLE_FAN: + aim->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; + break; } - Mesh::Primitive::Attributes& attr = prim.attributes; + Mesh::Primitive::Attributes &attr = prim.attributes; - if (attr.position.size() > 0 && attr.position[0]) { + if (attr.position.size() > 0 && attr.position[0]) { aim->mNumVertices = attr.position[0]->count; attr.position[0]->ExtractData(aim->mVertices); - } + } - if (attr.normal.size() > 0 && attr.normal[0]) attr.normal[0]->ExtractData(aim->mNormals); + if (attr.normal.size() > 0 && attr.normal[0]) attr.normal[0]->ExtractData(aim->mNormals); for (size_t tc = 0; tc < attr.texcoord.size() && tc < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++tc) { attr.texcoord[tc]->ExtractData(aim->mTextureCoords[tc]); aim->mNumUVComponents[tc] = attr.texcoord[tc]->GetNumComponents(); - aiVector3D* values = aim->mTextureCoords[tc]; + aiVector3D *values = aim->mTextureCoords[tc]; for (unsigned int i = 0; i < aim->mNumVertices; ++i) { values[i].y = 1 - values[i].y; // Flip Y coords } } - - aiFace* faces = 0; + aiFace *faces = 0; unsigned int nFaces = 0; if (prim.indices) { @@ -322,76 +304,75 @@ void glTFImporter::ImportMeshes(glTF::Asset& r) ai_assert(data.IsValid()); switch (prim.mode) { - case PrimitiveMode_POINTS: { - nFaces = count; - faces = new aiFace[nFaces]; - for (unsigned int i = 0; i < count; ++i) { - SetFace(faces[i], data.GetUInt(i)); - } - break; + case PrimitiveMode_POINTS: { + nFaces = count; + faces = new aiFace[nFaces]; + for (unsigned int i = 0; i < count; ++i) { + SetFace(faces[i], data.GetUInt(i)); } - - case PrimitiveMode_LINES: { - nFaces = count / 2; - if (nFaces * 2 != count) { - ASSIMP_LOG_WARN("The number of vertices was not compatible with the LINES mode. Some vertices were dropped."); - count = nFaces * 2; - } - faces = new aiFace[nFaces]; - for (unsigned int i = 0; i < count; i += 2) { - SetFace(faces[i / 2], data.GetUInt(i), data.GetUInt(i + 1)); - } - break; - } - - case PrimitiveMode_LINE_LOOP: - case PrimitiveMode_LINE_STRIP: { - nFaces = count - ((prim.mode == PrimitiveMode_LINE_STRIP) ? 1 : 0); - faces = new aiFace[nFaces]; - SetFace(faces[0], data.GetUInt(0), data.GetUInt(1)); - for (unsigned int i = 2; i < count; ++i) { - SetFace(faces[i - 1], faces[i - 2].mIndices[1], data.GetUInt(i)); - } - if (prim.mode == PrimitiveMode_LINE_LOOP) { // close the loop - SetFace(faces[count - 1], faces[count - 2].mIndices[1], faces[0].mIndices[0]); - } - break; - } - - case PrimitiveMode_TRIANGLES: { - nFaces = count / 3; - if (nFaces * 3 != count) { - ASSIMP_LOG_WARN("The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped."); - count = nFaces * 3; - } - faces = new aiFace[nFaces]; - for (unsigned int i = 0; i < count; i += 3) { - SetFace(faces[i / 3], data.GetUInt(i), data.GetUInt(i + 1), data.GetUInt(i + 2)); - } - break; - } - case PrimitiveMode_TRIANGLE_STRIP: { - nFaces = count - 2; - faces = new aiFace[nFaces]; - SetFace(faces[0], data.GetUInt(0), data.GetUInt(1), data.GetUInt(2)); - for (unsigned int i = 3; i < count; ++i) { - SetFace(faces[i - 2], faces[i - 1].mIndices[1], faces[i - 1].mIndices[2], data.GetUInt(i)); - } - break; - } - case PrimitiveMode_TRIANGLE_FAN: - nFaces = count - 2; - faces = new aiFace[nFaces]; - SetFace(faces[0], data.GetUInt(0), data.GetUInt(1), data.GetUInt(2)); - for (unsigned int i = 3; i < count; ++i) { - SetFace(faces[i - 2], faces[0].mIndices[0], faces[i - 1].mIndices[2], data.GetUInt(i)); - } - break; + break; } - } - else { // no indices provided so directly generate from counts - // use the already determined count as it includes checks + case PrimitiveMode_LINES: { + nFaces = count / 2; + if (nFaces * 2 != count) { + ASSIMP_LOG_WARN("The number of vertices was not compatible with the LINES mode. Some vertices were dropped."); + count = nFaces * 2; + } + faces = new aiFace[nFaces]; + for (unsigned int i = 0; i < count; i += 2) { + SetFace(faces[i / 2], data.GetUInt(i), data.GetUInt(i + 1)); + } + break; + } + + case PrimitiveMode_LINE_LOOP: + case PrimitiveMode_LINE_STRIP: { + nFaces = count - ((prim.mode == PrimitiveMode_LINE_STRIP) ? 1 : 0); + faces = new aiFace[nFaces]; + SetFace(faces[0], data.GetUInt(0), data.GetUInt(1)); + for (unsigned int i = 2; i < count; ++i) { + SetFace(faces[i - 1], faces[i - 2].mIndices[1], data.GetUInt(i)); + } + if (prim.mode == PrimitiveMode_LINE_LOOP) { // close the loop + SetFace(faces[count - 1], faces[count - 2].mIndices[1], faces[0].mIndices[0]); + } + break; + } + + case PrimitiveMode_TRIANGLES: { + nFaces = count / 3; + if (nFaces * 3 != count) { + ASSIMP_LOG_WARN("The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped."); + count = nFaces * 3; + } + faces = new aiFace[nFaces]; + for (unsigned int i = 0; i < count; i += 3) { + SetFace(faces[i / 3], data.GetUInt(i), data.GetUInt(i + 1), data.GetUInt(i + 2)); + } + break; + } + case PrimitiveMode_TRIANGLE_STRIP: { + nFaces = count - 2; + faces = new aiFace[nFaces]; + SetFace(faces[0], data.GetUInt(0), data.GetUInt(1), data.GetUInt(2)); + for (unsigned int i = 3; i < count; ++i) { + SetFace(faces[i - 2], faces[i - 1].mIndices[1], faces[i - 1].mIndices[2], data.GetUInt(i)); + } + break; + } + case PrimitiveMode_TRIANGLE_FAN: + nFaces = count - 2; + faces = new aiFace[nFaces]; + SetFace(faces[0], data.GetUInt(0), data.GetUInt(1), data.GetUInt(2)); + for (unsigned int i = 3; i < count; ++i) { + SetFace(faces[i - 2], faces[0].mIndices[0], faces[i - 1].mIndices[2], data.GetUInt(i)); + } + break; + } + } else { // no indices provided so directly generate from counts + + // use the already determined count as it includes checks unsigned int count = aim->mNumVertices; switch (prim.mode) { @@ -484,22 +465,22 @@ void glTFImporter::ImportMeshes(glTF::Asset& r) CopyVector(meshes, mScene->mMeshes, mScene->mNumMeshes); } -void glTFImporter::ImportCameras(glTF::Asset& r) { +void glTFImporter::ImportCameras(glTF::Asset &r) { if (!r.cameras.Size()) { return; } mScene->mNumCameras = r.cameras.Size(); - mScene->mCameras = new aiCamera*[r.cameras.Size()]; + mScene->mCameras = new aiCamera *[r.cameras.Size()]; for (size_t i = 0; i < r.cameras.Size(); ++i) { - Camera& cam = r.cameras[i]; + Camera &cam = r.cameras[i]; - aiCamera* aicam = mScene->mCameras[i] = new aiCamera(); + aiCamera *aicam = mScene->mCameras[i] = new aiCamera(); if (cam.type == Camera::Perspective) { - aicam->mAspect = cam.perspective.aspectRatio; + aicam->mAspect = cam.perspective.aspectRatio; aicam->mHorizontalFOV = cam.perspective.yfov * ((aicam->mAspect == 0.f) ? 1.f : aicam->mAspect); - aicam->mClipPlaneFar = cam.perspective.zfar; + aicam->mClipPlaneFar = cam.perspective.zfar; aicam->mClipPlaneNear = cam.perspective.znear; } else { aicam->mClipPlaneFar = cam.ortographic.zfar; @@ -513,30 +494,33 @@ void glTFImporter::ImportCameras(glTF::Asset& r) { } } -void glTFImporter::ImportLights(glTF::Asset& r) -{ +void glTFImporter::ImportLights(glTF::Asset &r) { if (!r.lights.Size()) return; mScene->mNumLights = r.lights.Size(); - mScene->mLights = new aiLight*[r.lights.Size()]; + mScene->mLights = new aiLight *[r.lights.Size()]; for (size_t i = 0; i < r.lights.Size(); ++i) { - Light& l = r.lights[i]; + Light &l = r.lights[i]; - aiLight* ail = mScene->mLights[i] = new aiLight(); + aiLight *ail = mScene->mLights[i] = new aiLight(); switch (l.type) { - case Light::Type_directional: - ail->mType = aiLightSource_DIRECTIONAL; break; + case Light::Type_directional: + ail->mType = aiLightSource_DIRECTIONAL; + break; - case Light::Type_spot: - ail->mType = aiLightSource_SPOT; break; + case Light::Type_spot: + ail->mType = aiLightSource_SPOT; + break; - case Light::Type_ambient: - ail->mType = aiLightSource_AMBIENT; break; + case Light::Type_ambient: + ail->mType = aiLightSource_AMBIENT; + break; - default: // Light::Type_point - ail->mType = aiLightSource_POINT; break; + default: // Light::Type_point + ail->mType = aiLightSource_POINT; + break; } CopyValue(l.color, ail->mColorAmbient); @@ -546,35 +530,32 @@ void glTFImporter::ImportLights(glTF::Asset& r) ail->mAngleOuterCone = l.falloffAngle; ail->mAngleInnerCone = l.falloffExponent; // TODO fix this, it does not look right at all - ail->mAttenuationConstant = l.constantAttenuation; - ail->mAttenuationLinear = l.linearAttenuation; + ail->mAttenuationConstant = l.constantAttenuation; + ail->mAttenuationLinear = l.linearAttenuation; ail->mAttenuationQuadratic = l.quadraticAttenuation; } } +aiNode *ImportNode(aiScene *pScene, glTF::Asset &r, std::vector &meshOffsets, glTF::Ref &ptr) { + Node &node = *ptr; -aiNode* ImportNode(aiScene* pScene, glTF::Asset& r, std::vector& meshOffsets, glTF::Ref& ptr) -{ - Node& node = *ptr; - - aiNode* ainode = new aiNode(node.id); + aiNode *ainode = new aiNode(node.id); if (!node.children.empty()) { ainode->mNumChildren = unsigned(node.children.size()); - ainode->mChildren = new aiNode*[ainode->mNumChildren]; + ainode->mChildren = new aiNode *[ainode->mNumChildren]; for (unsigned int i = 0; i < ainode->mNumChildren; ++i) { - aiNode* child = ImportNode(pScene, r, meshOffsets, node.children[i]); + aiNode *child = ImportNode(pScene, r, meshOffsets, node.children[i]); child->mParent = ainode; ainode->mChildren[i] = child; } } - aiMatrix4x4& matrix = ainode->mTransformation; + aiMatrix4x4 &matrix = ainode->mTransformation; if (node.matrix.isPresent) { CopyValue(node.matrix.value, matrix); - } - else { + } else { if (node.translation.isPresent) { aiVector3D trans; CopyValue(node.translation.value, trans); @@ -591,7 +572,6 @@ aiNode* ImportNode(aiScene* pScene, glTF::Asset& r, std::vector& m matrix = s * matrix; } - if (node.rotation.isPresent) { aiQuaternion rot; CopyValue(node.rotation.value, rot); @@ -629,22 +609,20 @@ aiNode* ImportNode(aiScene* pScene, glTF::Asset& r, std::vector& m return ainode; } -void glTFImporter::ImportNodes(glTF::Asset& r) -{ +void glTFImporter::ImportNodes(glTF::Asset &r) { if (!r.scene) return; - std::vector< Ref > rootNodes = r.scene->nodes; + std::vector> rootNodes = r.scene->nodes; // The root nodes unsigned int numRootNodes = unsigned(rootNodes.size()); if (numRootNodes == 1) { // a single root node: use it mScene->mRootNode = ImportNode(mScene, r, meshOffsets, rootNodes[0]); - } - else if (numRootNodes > 1) { // more than one root node: create a fake root - aiNode* root = new aiNode("ROOT"); - root->mChildren = new aiNode*[numRootNodes]; + } else if (numRootNodes > 1) { // more than one root node: create a fake root + aiNode *root = new aiNode("ROOT"); + root->mChildren = new aiNode *[numRootNodes]; for (unsigned int i = 0; i < numRootNodes; ++i) { - aiNode* node = ImportNode(mScene, r, meshOffsets, rootNodes[i]); + aiNode *node = ImportNode(mScene, r, meshOffsets, rootNodes[i]); node->mParent = root; root->mChildren[root->mNumChildren++] = node; } @@ -656,8 +634,7 @@ void glTFImporter::ImportNodes(glTF::Asset& r) //} } -void glTFImporter::ImportEmbeddedTextures(glTF::Asset& r) -{ +void glTFImporter::ImportEmbeddedTextures(glTF::Asset &r) { embeddedTexIdxs.resize(r.images.Size(), -1); int numEmbeddedTexs = 0; @@ -669,7 +646,7 @@ void glTFImporter::ImportEmbeddedTextures(glTF::Asset& r) if (numEmbeddedTexs == 0) return; - mScene->mTextures = new aiTexture*[numEmbeddedTexs]; + mScene->mTextures = new aiTexture *[numEmbeddedTexs]; // Add the embedded textures for (size_t i = 0; i < r.images.Size(); ++i) { @@ -679,18 +656,18 @@ void glTFImporter::ImportEmbeddedTextures(glTF::Asset& r) int idx = mScene->mNumTextures++; embeddedTexIdxs[i] = idx; - aiTexture* tex = mScene->mTextures[idx] = new aiTexture(); + aiTexture *tex = mScene->mTextures[idx] = new aiTexture(); size_t length = img.GetDataLength(); - void* data = img.StealData(); + void *data = img.StealData(); tex->mFilename = img.name; tex->mWidth = static_cast(length); tex->mHeight = 0; - tex->pcData = reinterpret_cast(data); + tex->pcData = reinterpret_cast(data); if (!img.mimeType.empty()) { - const char* ext = strchr(img.mimeType.c_str(), '/') + 1; + const char *ext = strchr(img.mimeType.c_str(), '/') + 1; if (ext) { if (strcmp(ext, "jpeg") == 0) ext = "jpg"; @@ -703,32 +680,26 @@ void glTFImporter::ImportEmbeddedTextures(glTF::Asset& r) } } -void glTFImporter::ImportCommonMetadata(glTF::Asset& a) -{ +void glTFImporter::ImportCommonMetadata(glTF::Asset &a) { ai_assert(mScene->mMetaData == nullptr); const bool hasVersion = !a.asset.version.empty(); const bool hasGenerator = !a.asset.generator.empty(); const bool hasCopyright = !a.asset.copyright.empty(); - if (hasVersion || hasGenerator || hasCopyright) - { + if (hasVersion || hasGenerator || hasCopyright) { mScene->mMetaData = new aiMetadata; - if (hasVersion) - { + if (hasVersion) { mScene->mMetaData->Add(AI_METADATA_SOURCE_FORMAT_VERSION, aiString(a.asset.version)); } - if (hasGenerator) - { + if (hasGenerator) { mScene->mMetaData->Add(AI_METADATA_SOURCE_GENERATOR, aiString(a.asset.generator)); } - if (hasCopyright) - { + if (hasCopyright) { mScene->mMetaData->Add(AI_METADATA_SOURCE_COPYRIGHT, aiString(a.asset.copyright)); } } } -void glTFImporter::InternReadFile(const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) -{ +void glTFImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) { // clean all member arrays meshOffsets.clear(); embeddedTexIdxs.clear(); @@ -739,7 +710,6 @@ void glTFImporter::InternReadFile(const std::string& pFile, aiScene* pScene, IOS glTF::Asset asset(pIOHandler); asset.Load(pFile, GetExtension(pFile) == "glb"); - // // Copy the data out // @@ -761,4 +731,3 @@ void glTFImporter::InternReadFile(const std::string& pFile, aiScene* pScene, IOS } #endif // ASSIMP_BUILD_NO_GLTF_IMPORTER - From edaa8e4a802ed9943e04500a64046661b56e94ef Mon Sep 17 00:00:00 2001 From: Malcolm Tyrrell Date: Wed, 24 Jun 2020 11:37:43 +0100 Subject: [PATCH 39/44] Need to use ASSIMP_BUILD_DEBUG. --- include/assimp/ai_assert.h | 2 +- test/unit/Common/utAssertHandler.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/assimp/ai_assert.h b/include/assimp/ai_assert.h index 6045a9801..320104cfb 100644 --- a/include/assimp/ai_assert.h +++ b/include/assimp/ai_assert.h @@ -42,7 +42,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef AI_ASSERT_H_INC #define AI_ASSERT_H_INC -#if defined(ASSIMP_BUILD_DEBUG) || defined(ASSIMP_BUILD_WITH_ASSERTS) +#if defined(ASSIMP_BUILD_DEBUG) namespace Assimp { diff --git a/test/unit/Common/utAssertHandler.cpp b/test/unit/Common/utAssertHandler.cpp index d04162bdc..4a567c0c3 100644 --- a/test/unit/Common/utAssertHandler.cpp +++ b/test/unit/Common/utAssertHandler.cpp @@ -42,8 +42,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "UnitTestPCH.h" /// Ensure this test has asserts on, even if the build type doesn't have asserts by default. -#if !defined(ASSIMP_BUILD_WITH_ASSERTS) -#define ASSIMP_BUILD_WITH_ASSERTS +#if !defined(ASSIMP_BUILD_DEBUG) +#define ASSIMP_BUILD_DEBUG #endif #include From 170c00eee7120938ec6f2ef770b6e9a383d4dcda Mon Sep 17 00:00:00 2001 From: Malcolm Tyrrell Date: Wed, 24 Jun 2020 11:51:03 +0100 Subject: [PATCH 40/44] Useful comment. --- include/assimp/ai_assert.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/assimp/ai_assert.h b/include/assimp/ai_assert.h index 320104cfb..8b2b396f4 100644 --- a/include/assimp/ai_assert.h +++ b/include/assimp/ai_assert.h @@ -46,6 +46,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { + // Assert violation behavior can be customized: see AssertHandler.h. void aiAssertViolation(const char* failedExpression, const char* file, int line); } From 1466bbacf87ce6996a9c934668f8df3b08a7fef5 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 24 Jun 2020 20:30:02 +0200 Subject: [PATCH 41/44] fix unittests. --- code/AssetLib/MD5/MD5Loader.cpp | 145 ++++++++++++++++++-------------- code/AssetLib/MD5/MD5Loader.h | 15 ++-- 2 files changed, 87 insertions(+), 73 deletions(-) diff --git a/code/AssetLib/MD5/MD5Loader.cpp b/code/AssetLib/MD5/MD5Loader.cpp index 1790eac2a..5428a9c74 100644 --- a/code/AssetLib/MD5/MD5Loader.cpp +++ b/code/AssetLib/MD5/MD5Loader.cpp @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2020, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -82,7 +80,15 @@ static const aiImporterDesc desc = { // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer MD5Importer::MD5Importer() : - mIOHandler(nullptr), mBuffer(), fileSize(), iLineNumber(), pScene(), bHadMD5Mesh(), bHadMD5Anim(), bHadMD5Camera(), configNoAutoLoad(false) { + mIOHandler(nullptr), + mBuffer(), + mFileSize(), + mLineNumber(), + mScene(), + mHadMD5Mesh(), + mHadMD5Anim(), + mHadMD5Camera(), + mCconfigNoAutoLoad(false) { // empty } @@ -106,6 +112,7 @@ bool MD5Importer::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool c const char *tokens[] = { "MD5Version" }; return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1); } + return false; } @@ -119,16 +126,15 @@ const aiImporterDesc *MD5Importer::GetInfo() const { // Setup import properties void MD5Importer::SetupProperties(const Importer *pImp) { // AI_CONFIG_IMPORT_MD5_NO_ANIM_AUTOLOAD - configNoAutoLoad = (0 != pImp->GetPropertyInteger(AI_CONFIG_IMPORT_MD5_NO_ANIM_AUTOLOAD, 0)); + mCconfigNoAutoLoad = (0 != pImp->GetPropertyInteger(AI_CONFIG_IMPORT_MD5_NO_ANIM_AUTOLOAD, 0)); } // ------------------------------------------------------------------------------------------------ // Imports the given file into the given scene structure. -void MD5Importer::InternReadFile(const std::string &pFile, - aiScene *_pScene, IOSystem *pIOHandler) { +void MD5Importer::InternReadFile(const std::string &pFile, aiScene *_pScene, IOSystem *pIOHandler) { mIOHandler = pIOHandler; - pScene = _pScene; - bHadMD5Mesh = bHadMD5Anim = bHadMD5Camera = false; + mScene = _pScene; + mHadMD5Mesh = mHadMD5Anim = mHadMD5Camera = false; // remove the file extension const std::string::size_type pos = pFile.find_last_of('.'); @@ -138,7 +144,7 @@ void MD5Importer::InternReadFile(const std::string &pFile, try { if (extension == "md5camera") { LoadMD5CameraFile(); - } else if (configNoAutoLoad || extension == "md5anim") { + } else if (mCconfigNoAutoLoad || extension == "md5anim") { // determine file extension and process just *one* file if (extension.length() == 0) { throw DeadlyImportError("Failure, need file extension to determine MD5 part type"); @@ -158,17 +164,17 @@ void MD5Importer::InternReadFile(const std::string &pFile, } // make sure we have at least one file - if (!bHadMD5Mesh && !bHadMD5Anim && !bHadMD5Camera) { + if (!mHadMD5Mesh && !mHadMD5Anim && !mHadMD5Camera) { throw DeadlyImportError("Failed to read valid contents out of this MD5* file"); } // Now rotate the whole scene 90 degrees around the x axis to match our internal coordinate system - pScene->mRootNode->mTransformation = aiMatrix4x4(1.f, 0.f, 0.f, 0.f, + mScene->mRootNode->mTransformation = aiMatrix4x4(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); // the output scene wouldn't pass the validation without this flag - if (!bHadMD5Mesh) { - pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; + if (!mHadMD5Mesh) { + mScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; } // clean the instance -- the BaseImporter instance may be reused later. @@ -182,16 +188,16 @@ void MD5Importer::LoadFileIntoMemory(IOStream *file) { UnloadFileFromMemory(); ai_assert(nullptr != file); - fileSize = (unsigned int)file->FileSize(); - ai_assert(fileSize); + mFileSize = (unsigned int)file->FileSize(); + ai_assert(mFileSize); // allocate storage and copy the contents of the file to a memory buffer - mBuffer = new char[fileSize + 1]; - file->Read((void *)mBuffer, 1, fileSize); - iLineNumber = 1; + mBuffer = new char[mFileSize + 1]; + file->Read((void *)mBuffer, 1, mFileSize); + mLineNumber = 1; // append a terminal 0 - mBuffer[fileSize] = '\0'; + mBuffer[mFileSize] = '\0'; // now remove all line comments from the file CommentRemover::RemoveLineComments("//", mBuffer, ' '); @@ -203,7 +209,7 @@ void MD5Importer::UnloadFileFromMemory() { // delete the file buffer delete[] mBuffer; mBuffer = nullptr; - fileSize = 0; + mFileSize = 0; } // ------------------------------------------------------------------------------------------------ @@ -339,31 +345,32 @@ void MD5Importer::LoadMD5MeshFile() { // Check whether we can read from the file if (file.get() == nullptr || !file->FileSize()) { - throw DeadlyImportError("Failed to open MD5 file " + filename + "."); + ASSIMP_LOG_WARN("Failed to access MD5MESH file: " + filename); + return; } - bHadMD5Mesh = true; + mHadMD5Mesh = true; LoadFileIntoMemory(file.get()); // now construct a parser and parse the file - MD5::MD5Parser parser(mBuffer, fileSize); + MD5::MD5Parser parser(mBuffer, mFileSize); // load the mesh information from it MD5::MD5MeshParser meshParser(parser.mSections); // create the bone hierarchy - first the root node and dummy nodes for all meshes - pScene->mRootNode = new aiNode(""); - pScene->mRootNode->mNumChildren = 2; - pScene->mRootNode->mChildren = new aiNode *[2]; + mScene->mRootNode = new aiNode(""); + mScene->mRootNode->mNumChildren = 2; + mScene->mRootNode->mChildren = new aiNode *[2]; // build the hierarchy from the MD5MESH file - aiNode *pcNode = pScene->mRootNode->mChildren[1] = new aiNode(); + aiNode *pcNode = mScene->mRootNode->mChildren[1] = new aiNode(); pcNode->mName.Set(""); - pcNode->mParent = pScene->mRootNode; + pcNode->mParent = mScene->mRootNode; AttachChilds_Mesh(-1, pcNode, meshParser.mJoints); - pcNode = pScene->mRootNode->mChildren[0] = new aiNode(); + pcNode = mScene->mRootNode->mChildren[0] = new aiNode(); pcNode->mName.Set(""); - pcNode->mParent = pScene->mRootNode; + pcNode->mParent = mScene->mRootNode; #if 0 if (pScene->mRootNode->mChildren[1]->mNumChildren) /* start at the right hierarchy level */ @@ -372,28 +379,31 @@ void MD5Importer::LoadMD5MeshFile() { // FIX: MD5 files exported from Blender can have empty meshes for (std::vector::const_iterator it = meshParser.mMeshes.begin(), end = meshParser.mMeshes.end(); it != end; ++it) { - if (!(*it).mFaces.empty() && !(*it).mVertices.empty()) - ++pScene->mNumMaterials; + if (!(*it).mFaces.empty() && !(*it).mVertices.empty()) { + ++mScene->mNumMaterials; + } } // generate all meshes - pScene->mNumMeshes = pScene->mNumMaterials; - pScene->mMeshes = new aiMesh *[pScene->mNumMeshes]; - pScene->mMaterials = new aiMaterial *[pScene->mNumMeshes]; + mScene->mNumMeshes = mScene->mNumMaterials; + mScene->mMeshes = new aiMesh *[mScene->mNumMeshes]; + mScene->mMaterials = new aiMaterial *[mScene->mNumMeshes]; // storage for node mesh indices - pcNode->mNumMeshes = pScene->mNumMeshes; + pcNode->mNumMeshes = mScene->mNumMeshes; pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes]; - for (unsigned int m = 0; m < pcNode->mNumMeshes; ++m) + for (unsigned int m = 0; m < pcNode->mNumMeshes; ++m) { pcNode->mMeshes[m] = m; + } unsigned int n = 0; for (std::vector::iterator it = meshParser.mMeshes.begin(), end = meshParser.mMeshes.end(); it != end; ++it) { MD5::MeshDesc &meshSrc = *it; - if (meshSrc.mFaces.empty() || meshSrc.mVertices.empty()) + if (meshSrc.mFaces.empty() || meshSrc.mVertices.empty()) { continue; + } - aiMesh *mesh = pScene->mMeshes[n] = new aiMesh(); + aiMesh *mesh = mScene->mMeshes[n] = new aiMesh(); mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; // generate unique vertices in our internal verbose format @@ -423,17 +433,19 @@ void MD5Importer::LoadMD5MeshFile() { for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights; ++w) { MD5::WeightDesc &weightDesc = meshSrc.mWeights[w]; /* FIX for some invalid exporters */ - if (!(weightDesc.mWeight < AI_MD5_WEIGHT_EPSILON && weightDesc.mWeight >= -AI_MD5_WEIGHT_EPSILON)) + if (!(weightDesc.mWeight < AI_MD5_WEIGHT_EPSILON && weightDesc.mWeight >= -AI_MD5_WEIGHT_EPSILON)) { ++piCount[weightDesc.mBone]; + } } } // check how many we will need - for (unsigned int p = 0; p < meshParser.mJoints.size(); ++p) + for (unsigned int p = 0; p < meshParser.mJoints.size(); ++p) { if (piCount[p]) mesh->mNumBones++; + } - if (mesh->mNumBones) // just for safety - { + // just for safety + if (mesh->mNumBones) { mesh->mBones = new aiBone *[mesh->mNumBones]; for (unsigned int q = 0, h = 0; q < meshParser.mJoints.size(); ++q) { if (!piCount[q]) continue; @@ -458,8 +470,9 @@ void MD5Importer::LoadMD5MeshFile() { // there are models which have weights which don't sum to 1 ... ai_real fSum = 0.0; - for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights; ++w) + for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights; ++w) { fSum += meshSrc.mWeights[w].mWeight; + } if (!fSum) { ASSIMP_LOG_ERROR("MD5MESH: The sum of all vertex bone weights is 0"); continue; @@ -467,11 +480,12 @@ void MD5Importer::LoadMD5MeshFile() { // process bone weights for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights; ++w) { - if (w >= meshSrc.mWeights.size()) + if (w >= meshSrc.mWeights.size()) { throw DeadlyImportError("MD5MESH: Invalid weight index"); + } MD5::WeightDesc &weightDesc = meshSrc.mWeights[w]; - if (weightDesc.mWeight < AI_MD5_WEIGHT_EPSILON && weightDesc.mWeight >= -AI_MD5_WEIGHT_EPSILON) { + if (weightDesc.mWeight < AI_MD5_WEIGHT_EPSILON && weightDesc.mWeight >= -AI_MD5_WEIGHT_EPSILON) { continue; } @@ -510,7 +524,7 @@ void MD5Importer::LoadMD5MeshFile() { // generate a material for the mesh aiMaterial *mat = new aiMaterial(); - pScene->mMaterials[n] = mat; + mScene->mMaterials[n] = mat; // insert the typical doom3 textures: // nnn_local.tga - normal map @@ -553,13 +567,14 @@ void MD5Importer::LoadMD5AnimFile() { // Check whether we can read from the file if (!file.get() || !file->FileSize()) { - throw DeadlyImportError("Failed to open MD3 file " + pFile + "."); + ASSIMP_LOG_WARN("Failed to read MD5ANIM file: " + pFile); + return; } LoadFileIntoMemory(file.get()); // parse the basic file structure - MD5::MD5Parser parser(mBuffer, fileSize); + MD5::MD5Parser parser(mBuffer, mFileSize); // load the animation information from the parse tree MD5::MD5AnimParser animParser(parser.mSections); @@ -569,10 +584,10 @@ void MD5Importer::LoadMD5AnimFile() { animParser.mBaseFrames.size() != animParser.mAnimatedBones.size()) { ASSIMP_LOG_ERROR("MD5ANIM: No frames or animated bones loaded"); } else { - bHadMD5Anim = true; + mHadMD5Anim = true; - pScene->mAnimations = new aiAnimation *[pScene->mNumAnimations = 1]; - aiAnimation *anim = pScene->mAnimations[0] = new aiAnimation(); + mScene->mAnimations = new aiAnimation *[mScene->mNumAnimations = 1]; + aiAnimation *anim = mScene->mAnimations[0] = new aiAnimation(); anim->mNumChannels = (unsigned int)animParser.mAnimatedBones.size(); anim->mChannels = new aiNodeAnim *[anim->mNumChannels]; for (unsigned int i = 0; i < anim->mNumChannels; ++i) { @@ -638,15 +653,15 @@ void MD5Importer::LoadMD5AnimFile() { // If we didn't build the hierarchy yet (== we didn't load a MD5MESH), // construct it now from the data given in the MD5ANIM. - if (!pScene->mRootNode) { - pScene->mRootNode = new aiNode(); - pScene->mRootNode->mName.Set(""); + if (!mScene->mRootNode) { + mScene->mRootNode = new aiNode(); + mScene->mRootNode->mName.Set(""); - AttachChilds_Anim(-1, pScene->mRootNode, animParser.mAnimatedBones, (const aiNodeAnim **)anim->mChannels); + AttachChilds_Anim(-1, mScene->mRootNode, animParser.mAnimatedBones, (const aiNodeAnim **)anim->mChannels); // Call SkeletonMeshBuilder to construct a mesh to represent the shape - if (pScene->mRootNode->mNumChildren) { - SkeletonMeshBuilder skeleton_maker(pScene, pScene->mRootNode->mChildren[0]); + if (mScene->mRootNode->mNumChildren) { + SkeletonMeshBuilder skeleton_maker(mScene, mScene->mRootNode->mChildren[0]); } } } @@ -662,11 +677,11 @@ void MD5Importer::LoadMD5CameraFile() { if (!file.get() || !file->FileSize()) { throw DeadlyImportError("Failed to read MD5CAMERA file: " + pFile); } - bHadMD5Camera = true; + mHadMD5Camera = true; LoadFileIntoMemory(file.get()); // parse the basic file structure - MD5::MD5Parser parser(mBuffer, fileSize); + MD5::MD5Parser parser(mBuffer, mFileSize); // load the camera animation data from the parse tree MD5::MD5CameraParser cameraParser(parser.mSections); @@ -680,14 +695,14 @@ void MD5Importer::LoadMD5CameraFile() { // Construct output graph - a simple root with a dummy child. // The root node performs the coordinate system conversion - aiNode *root = pScene->mRootNode = new aiNode(""); + aiNode *root = mScene->mRootNode = new aiNode(""); root->mChildren = new aiNode *[root->mNumChildren = 1]; root->mChildren[0] = new aiNode(""); root->mChildren[0]->mParent = root; // ... but with one camera assigned to it - pScene->mCameras = new aiCamera *[pScene->mNumCameras = 1]; - aiCamera *cam = pScene->mCameras[0] = new aiCamera(); + mScene->mCameras = new aiCamera *[mScene->mNumCameras = 1]; + aiCamera *cam = mScene->mCameras[0] = new aiCamera(); cam->mName = ""; // FIXME: Fov is currently set to the first frame's value @@ -704,8 +719,8 @@ void MD5Importer::LoadMD5CameraFile() { cuts.push_back(static_cast(frames.size() - 1)); } - pScene->mNumAnimations = static_cast(cuts.size() - 1); - aiAnimation **tmp = pScene->mAnimations = new aiAnimation *[pScene->mNumAnimations]; + mScene->mNumAnimations = static_cast(cuts.size() - 1); + aiAnimation **tmp = mScene->mAnimations = new aiAnimation *[mScene->mNumAnimations]; for (std::vector::const_iterator it = cuts.begin(); it != cuts.end() - 1; ++it) { aiAnimation *anim = *tmp++ = new aiAnimation(); diff --git a/code/AssetLib/MD5/MD5Loader.h b/code/AssetLib/MD5/MD5Loader.h index cfdd9ed0e..f62a57e68 100644 --- a/code/AssetLib/MD5/MD5Loader.h +++ b/code/AssetLib/MD5/MD5Loader.h @@ -4,7 +4,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2020, assimp team - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -156,25 +155,25 @@ protected: char *mBuffer; /** Size of the file */ - unsigned int fileSize; + unsigned int mFileSize; /** Current line number. For debugging purposes */ - unsigned int iLineNumber; + unsigned int mLineNumber; /** Scene to be filled */ - aiScene *pScene; + aiScene *mScene; /** true if a MD5MESH file has already been parsed */ - bool bHadMD5Mesh; + bool mHadMD5Mesh; /** true if a MD5ANIM file has already been parsed */ - bool bHadMD5Anim; + bool mHadMD5Anim; /** true if a MD5CAMERA file has already been parsed */ - bool bHadMD5Camera; + bool mHadMD5Camera; /** configuration option: prevent anim autoload */ - bool configNoAutoLoad; + bool mCconfigNoAutoLoad; }; } // end of namespace Assimp From f6b4370f6ac1bf2db0ef9edeb0ff1ce8c7e61aab Mon Sep 17 00:00:00 2001 From: Evangel Date: Fri, 26 Jun 2020 14:26:45 +1000 Subject: [PATCH 42/44] Added arbitrary recursive metadata to allow for glTF2's extensions to be properly represented. Primary changes are to include/assimp/metadata.h, adding in the aiMetadata GetAiType function, adding the operator= to allow an aiMetadata type to be assigned, adding a check for the AI_AIMETADATA type flag as it can't be trivially memcpy'd. operator= is implemented with a by-value argument as then the copy is made by the copy constructor and we can just swap everything out and let the destructor handle the mess. Implemented parsing of the "extensions" flag on all glTF2 Nodes. Doesn't use the ReadValue helper function on numbers as it did not seem to fill out the Nullable structure properly. --- code/AssetLib/glTF2/glTF2Asset.h | 44 +++++++++++++++++++++++++++ code/AssetLib/glTF2/glTF2Asset.inl | 39 ++++++++++++++++++++++++ code/AssetLib/glTF2/glTF2Importer.cpp | 32 +++++++++++++++++-- include/assimp/metadata.h | 30 ++++++++++++++++-- 4 files changed, 140 insertions(+), 5 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Asset.h b/code/AssetLib/glTF2/glTF2Asset.h index ffecd3dbf..543bef95c 100644 --- a/code/AssetLib/glTF2/glTF2Asset.h +++ b/code/AssetLib/glTF2/glTF2Asset.h @@ -784,6 +784,48 @@ struct Mesh : public Object { void Read(Value &pJSON_Object, Asset &pAsset_Root); }; +struct CustomExtension : public Object { + // + // A struct containing custom extension data added to a glTF2 file + // Has to contain Object, Array, String, Double, Uint64, and Int64 at a minimum + // String, Double, Uint64, and Int64 are stored in the Nullables + // Object and Array are stored in the std::vector + // + + Nullable mStringValue; + Nullable mDoubleValue; + Nullable mUint64Value; + Nullable mInt64Value; + + // std::vector handles both Object and Array + Nullable> mValues; + + operator bool() const { + return Size(); + } + + size_t Size() const { + if (mValues.isPresent) { + return mValues.value.size(); + } else if (mStringValue.isPresent || mDoubleValue.isPresent || mUint64Value.isPresent || mInt64Value.isPresent) { + return 1; + } + return 0; + } + + CustomExtension() = default; + + CustomExtension(const CustomExtension& other) + : Object(other) + , mStringValue(other.mStringValue) + , mDoubleValue(other.mDoubleValue) + , mUint64Value(other.mUint64Value) + , mInt64Value(other.mInt64Value) + , mValues(other.mValues) + { + } +}; + struct Node : public Object { std::vector> children; std::vector> meshes; @@ -802,6 +844,8 @@ struct Node : public Object { Ref parent; //!< This is not part of the glTF specification. Used as a helper. + CustomExtension extensions; + Node() {} void Read(Value &obj, Asset &r); }; diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index 5a99525b2..71b164149 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -1207,6 +1207,43 @@ inline void Light::Read(Value &obj, Asset & /*r*/) { } } +inline CustomExtension ReadExtensions(const char *name, Value& obj) { + CustomExtension ret; + ret.name = name; + if (obj.IsObject()) { + ret.mValues.isPresent = true; + for (auto it = obj.MemberBegin(); it != obj.MemberEnd(); ++it) { + auto& val = it->value; + ret.mValues.value.push_back(ReadExtensions(it->name.GetString(), val)); + } + } + else if (obj.IsArray()) { + ret.mValues.value.reserve(obj.Size()); + ret.mValues.isPresent = true; + for (unsigned int i = 0; i < obj.Size(); ++i) + { + ret.mValues.value.push_back(ReadExtensions(name, obj[i])); + } + } + else if (obj.IsNumber()) { + if (obj.IsUint64()) { + ret.mUint64Value.value = obj.GetUint64(); + ret.mUint64Value.isPresent = true; + } else if (obj.IsInt64()) { + ret.mInt64Value.value = obj.GetInt64(); + ret.mInt64Value.isPresent = true; + } else if (obj.IsDouble()) { + ret.mDoubleValue.value = obj.GetDouble(); + ret.mDoubleValue.isPresent = true; + } + } + else if (obj.IsString()) { + ReadValue(obj, ret.mStringValue); + ret.mStringValue.isPresent = true; + } + return ret; +} + inline void Node::Read(Value &obj, Asset &r) { if (name.empty()) { name = id; @@ -1261,6 +1298,8 @@ inline void Node::Read(Value &obj, Asset &r) { Value *curExtensions = FindObject(obj, "extensions"); if (nullptr != curExtensions) { + this->extensions = ReadExtensions("extensions", *curExtensions); + if (r.extensionsUsed.KHR_lights_punctual) { if (Value *ext = FindObject(*curExtensions, "KHR_lights_punctual")) { Value *curLight = FindUInt(*ext, "light"); diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index 1c044e7ce..3a3c66e15 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -847,6 +847,24 @@ static std::string GetNodeName(const Node &node) { return node.name.empty() ? node.id : node.name; } +void ParseExtensions(aiMetadata *metadata, const CustomExtension &extension, unsigned int depth = 0) { + if (extension.mStringValue.isPresent) { + metadata->Add(extension.name.c_str(), aiString(extension.mStringValue.value)); + } else if (extension.mDoubleValue.isPresent) { + metadata->Add(extension.name.c_str(), extension.mDoubleValue.value); + } else if (extension.mUint64Value.isPresent) { + metadata->Add(extension.name.c_str(), extension.mUint64Value.value); + } else if (extension.mInt64Value.isPresent) { + metadata->Add(extension.name.c_str(), static_cast(extension.mInt64Value.value)); + } else if (extension.mValues.isPresent) { + aiMetadata val; + for (size_t i = 0; i < extension.mValues.value.size(); ++i) { + ParseExtensions(&val, extension.mValues.value[i], depth + 2); + } + metadata->Add(extension.name.c_str(), val); + } +} + aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector &meshOffsets, glTF2::Ref &ptr) { Node &node = *ptr; @@ -863,6 +881,11 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector & } } + if (node.extensions) { + ainode->mMetaData = new aiMetadata; + ParseExtensions(ainode->mMetaData, node.extensions); + } + GetNodeTransform(ainode->mTransformation, node); if (!node.meshes.empty()) { @@ -957,8 +980,13 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector & //range is optional - see https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual //it is added to meta data of parent node, because there is no other place to put it if (node.light->range.isPresent) { - ainode->mMetaData = aiMetadata::Alloc(1); - ainode->mMetaData->Set(0, "PBR_LightRange", node.light->range.value); + if (!ainode->mMetaData) { + ainode->mMetaData = aiMetadata::Alloc(1); + ainode->mMetaData->Set(0, "PBR_LightRange", node.light->range.value); + } + else { + ainode->mMetaData->Add("PBR_LightRange", node.light->range.value); + } } } diff --git a/include/assimp/metadata.h b/include/assimp/metadata.h index 684508a71..95d919584 100644 --- a/include/assimp/metadata.h +++ b/include/assimp/metadata.h @@ -69,7 +69,8 @@ typedef enum aiMetadataType { AI_DOUBLE = 4, AI_AISTRING = 5, AI_AIVECTOR3D = 6, - AI_META_MAX = 7, + AI_AIMETADATA = 7, + AI_META_MAX = 8, #ifndef SWIG FORCE_32BIT = INT_MAX @@ -100,6 +101,8 @@ struct aiMetadataEntry { #include +struct aiMetadata; + // ------------------------------------------------------------------------------- /** * Helper functions to get the aiType enum entry for a type @@ -127,6 +130,9 @@ inline aiMetadataType GetAiType(const aiString &) { inline aiMetadataType GetAiType(const aiVector3D &) { return AI_AIVECTOR3D; } +inline aiMetadataType GetAiType(const aiMetadata &) { + return AI_AIMETADATA; +} #endif // __cplusplus @@ -204,6 +210,11 @@ struct aiMetadata { rhs.Get(mKeys[i], v); mValues[i].mData = new aiVector3D(v); } break; + case AI_AIMETADATA: { + aiMetadata v; + rhs.Get(mKeys[i], v); + mValues[i].mData = new aiMetadata(v); + } break; #ifndef SWIG case FORCE_32BIT: #endif @@ -213,7 +224,15 @@ struct aiMetadata { } } - /** + aiMetadata &operator=(aiMetadata rhs) { + using std::swap; + swap(mNumProperties, rhs.mNumProperties); + swap(mKeys, rhs.mKeys); + swap(mValues, rhs.mValues); + return *this; + } + + /** * @brief The destructor. */ ~aiMetadata() { @@ -245,6 +264,9 @@ struct aiMetadata { case AI_AIVECTOR3D: delete static_cast(data); break; + case AI_AIMETADATA: + delete static_cast(data); + break; #ifndef SWIG case FORCE_32BIT: #endif @@ -323,8 +345,10 @@ struct aiMetadata { mValues[index].mType = GetAiType(value); // Copy the given value to the dynamic storage - if (nullptr != mValues[index].mData) { + if (nullptr != mValues[index].mData && AI_AIMETADATA != mValues[index].mType) { ::memcpy(mValues[index].mData, &value, sizeof(T)); + } else if (nullptr != mValues[index].mData && AI_AIMETADATA == mValues[index].mType) { + *static_cast(mValues[index].mData) = value; } else { mValues[index].mData = new T(value); } From d75d59a4fde93d070364cd3e7035eebc5428abf1 Mon Sep 17 00:00:00 2001 From: Evangel Date: Fri, 26 Jun 2020 15:07:42 +1000 Subject: [PATCH 43/44] Added operator== and operator!= to aiMetadata to allow for testing. Updated utMetadata.copy_test to reflect that there's now 8 metadata types. --- include/assimp/metadata.h | 83 +++++++++++++++++++++++++++++++++++++++ test/unit/utMetadata.cpp | 11 +++++- 2 files changed, 93 insertions(+), 1 deletion(-) diff --git a/include/assimp/metadata.h b/include/assimp/metadata.h index 95d919584..52121fbf1 100644 --- a/include/assimp/metadata.h +++ b/include/assimp/metadata.h @@ -442,6 +442,89 @@ struct aiMetadata { return false; } + friend bool CompareKeys(const aiMetadata &lhs, const aiMetadata &rhs) { + if (lhs.mNumProperties != rhs.mNumProperties) { + return false; + } + + for (unsigned int i = 0; i < lhs.mNumProperties; ++i) { + if (lhs.mKeys[i] != rhs.mKeys[i]) { + return false; + } + } + return true; + } + + friend bool CompareValues(const aiMetadata &lhs, const aiMetadata &rhs) { + if (lhs.mNumProperties != rhs.mNumProperties) { + return false; + } + + for (unsigned int i = 0; i < lhs.mNumProperties; ++i) { + if (lhs.mValues[i].mType != rhs.mValues[i].mType) { + return false; + } + + switch (lhs.mValues[i].mType) { + case AI_BOOL: { + if (*static_cast(lhs.mValues[i].mData) != *static_cast(rhs.mValues[i].mData)) { + return false; + } + } break; + case AI_INT32: { + if (*static_cast(lhs.mValues[i].mData) != *static_cast(rhs.mValues[i].mData)) { + return false; + } + } break; + case AI_UINT64: { + if (*static_cast(lhs.mValues[i].mData) != *static_cast(rhs.mValues[i].mData)) { + return false; + } + } break; + case AI_FLOAT: { + if (*static_cast(lhs.mValues[i].mData) != *static_cast(rhs.mValues[i].mData)) { + return false; + } + } break; + case AI_DOUBLE: { + if (*static_cast(lhs.mValues[i].mData) != *static_cast(rhs.mValues[i].mData)) { + return false; + } + } break; + case AI_AISTRING: { + if (*static_cast(lhs.mValues[i].mData) != *static_cast(rhs.mValues[i].mData)) { + return false; + } + } break; + case AI_AIVECTOR3D: { + if (*static_cast(lhs.mValues[i].mData) != *static_cast(rhs.mValues[i].mData)) { + return false; + } + } break; + case AI_AIMETADATA: { + if (*static_cast(lhs.mValues[i].mData) != *static_cast(rhs.mValues[i].mData)) { + return false; + } + } break; +#ifndef SWIG + case FORCE_32BIT: +#endif + default: + break; + } + } + + return true; + } + + friend bool operator==(const aiMetadata &lhs, const aiMetadata &rhs) { + return CompareKeys(lhs, rhs) && CompareValues(lhs, rhs); + } + + friend bool operator!=(const aiMetadata &lhs, const aiMetadata &rhs) { + return !(lhs == rhs); + } + #endif // __cplusplus }; diff --git a/test/unit/utMetadata.cpp b/test/unit/utMetadata.cpp index a605107db..81ab61435 100644 --- a/test/unit/utMetadata.cpp +++ b/test/unit/utMetadata.cpp @@ -197,9 +197,11 @@ TEST_F( utMetadata, copy_test ) { m_data->Set( 5, "aiString", strVal ); aiVector3D vecVal( 1, 2, 3 ); m_data->Set( 6, "aiVector3D", vecVal ); + aiMetadata metaVal; + m_data->Set( 7, "aiMetadata", metaVal ); aiMetadata copy( *m_data ); - EXPECT_EQ( 7u, copy.mNumProperties ); + EXPECT_EQ( 8u, copy.mNumProperties ); // bool test { @@ -251,4 +253,11 @@ TEST_F( utMetadata, copy_test ) { EXPECT_TRUE( copy.Get( "aiVector3D", v ) ); EXPECT_EQ( vecVal, v ); } + + // metadata test + { + aiMetadata v; + EXPECT_TRUE( copy.Get( "aiMetadata", v ) ); + EXPECT_EQ( metaVal, v ); + } } From 6d85280c8dcd936a19caedf07eb9d454e3e511a8 Mon Sep 17 00:00:00 2001 From: Evangel Date: Sat, 27 Jun 2020 12:53:26 +1000 Subject: [PATCH 44/44] Added bool, removed unused debug parameter --- code/AssetLib/glTF2/glTF2Asset.h | 4 +++- code/AssetLib/glTF2/glTF2Asset.inl | 4 ++++ code/AssetLib/glTF2/glTF2Importer.cpp | 6 ++++-- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Asset.h b/code/AssetLib/glTF2/glTF2Asset.h index 543bef95c..f79ddee87 100644 --- a/code/AssetLib/glTF2/glTF2Asset.h +++ b/code/AssetLib/glTF2/glTF2Asset.h @@ -796,6 +796,7 @@ struct CustomExtension : public Object { Nullable mDoubleValue; Nullable mUint64Value; Nullable mInt64Value; + Nullable mBoolValue; // std::vector handles both Object and Array Nullable> mValues; @@ -807,7 +808,7 @@ struct CustomExtension : public Object { size_t Size() const { if (mValues.isPresent) { return mValues.value.size(); - } else if (mStringValue.isPresent || mDoubleValue.isPresent || mUint64Value.isPresent || mInt64Value.isPresent) { + } else if (mStringValue.isPresent || mDoubleValue.isPresent || mUint64Value.isPresent || mInt64Value.isPresent || mBoolValue.isPresent) { return 1; } return 0; @@ -821,6 +822,7 @@ struct CustomExtension : public Object { , mDoubleValue(other.mDoubleValue) , mUint64Value(other.mUint64Value) , mInt64Value(other.mInt64Value) + , mBoolValue(other.mBoolValue) , mValues(other.mValues) { } diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index 71b164149..94f8d9ffa 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -1241,6 +1241,10 @@ inline CustomExtension ReadExtensions(const char *name, Value& obj) { ReadValue(obj, ret.mStringValue); ret.mStringValue.isPresent = true; } + else if (obj.IsBool()) { + ret.mBoolValue.value = obj.GetBool(); + ret.mBoolValue.isPresent = true; + } return ret; } diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index 3a3c66e15..4d740d8c1 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -847,7 +847,7 @@ static std::string GetNodeName(const Node &node) { return node.name.empty() ? node.id : node.name; } -void ParseExtensions(aiMetadata *metadata, const CustomExtension &extension, unsigned int depth = 0) { +void ParseExtensions(aiMetadata *metadata, const CustomExtension &extension) { if (extension.mStringValue.isPresent) { metadata->Add(extension.name.c_str(), aiString(extension.mStringValue.value)); } else if (extension.mDoubleValue.isPresent) { @@ -856,10 +856,12 @@ void ParseExtensions(aiMetadata *metadata, const CustomExtension &extension, uns metadata->Add(extension.name.c_str(), extension.mUint64Value.value); } else if (extension.mInt64Value.isPresent) { metadata->Add(extension.name.c_str(), static_cast(extension.mInt64Value.value)); + } else if (extension.mBoolValue.isPresent) { + metadata->Add(extension.name.c_str(), extension.mBoolValue.value); } else if (extension.mValues.isPresent) { aiMetadata val; for (size_t i = 0; i < extension.mValues.value.size(); ++i) { - ParseExtensions(&val, extension.mValues.value[i], depth + 2); + ParseExtensions(&val, extension.mValues.value[i]); } metadata->Add(extension.name.c_str(), val); }