update assimp to 6.0.5

This commit is contained in:
AzaezelX 2026-06-09 12:46:56 -05:00
parent 2d2eb57e2e
commit f5cf21cfeb
941 changed files with 22718 additions and 12240 deletions

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
Copyright (c) 2006-2026, assimp team
All rights reserved.
@ -53,17 +53,18 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* KHR_materials_volume full
* KHR_materials_ior full
* KHR_materials_emissive_strength full
* KHR_materials_anisotropy full
*/
#ifndef GLTF2ASSET_H_INC
#define GLTF2ASSET_H_INC
#if !defined(ASSIMP_BUILD_NO_GLTF_IMPORTER) && !defined(ASSIMP_BUILD_NO_GLTF2_IMPORTER)
//#if !defined(ASSIMP_BUILD_NO_GLTF_IMPORTER) && !defined(ASSIMP_BUILD_NO_GLTF2_IMPORTER)
#include <assimp/Exceptional.h>
#include <algorithm>
#include <list>
#include <map>
#include <unordered_map>
#include <set>
#include <stdexcept>
#include <string>
@ -94,7 +95,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# define ai_assert
#endif
#if _MSC_VER > 1500 || (defined __GNUC___)
#if _MSC_VER > 1500 || (defined __GNUC__)
# define ASSIMP_GLTF_USE_UNORDERED_MULTIMAP
#else
# define gltf_unordered_map map
@ -118,7 +119,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/material.h>
#include <assimp/GltfMaterial.h>
#include "AssetLib/glTF/glTFCommon.h"
#include "AssetLib/glTFCommon/glTFCommon.h"
namespace glTF2 {
@ -363,11 +364,8 @@ struct CustomExtension {
}
CustomExtension() = default;
~CustomExtension() = default;
CustomExtension(const CustomExtension &other) = default;
CustomExtension& operator=(const CustomExtension&) = default;
};
@ -422,7 +420,6 @@ public:
Type_text
};
/// \struct SEncodedRegion
/// Descriptor of encoded region in "bufferView".
struct SEncodedRegion {
const size_t Offset; ///< Offset from begin of "bufferView" to encoded region, in bytes.
@ -431,7 +428,6 @@ public:
const size_t DecodedData_Length; ///< Size of decoded region, in bytes.
const std::string ID; ///< ID of the region.
/// \fn SEncodedRegion(const size_t pOffset, const size_t pEncodedData_Length, uint8_t* pDecodedData, const size_t pDecodedData_Length, const std::string pID)
/// Constructor.
/// \param [in] pOffset - offset from begin of "bufferView" to encoded region, in bytes.
/// \param [in] pEncodedData_Length - size of encoded region, in bytes.
@ -445,7 +441,6 @@ public:
DecodedData_Length(pDecodedData_Length),
ID(pID) {}
/// \fn ~SEncodedRegion()
/// Destructor.
~SEncodedRegion() { delete[] DecodedData; }
};
@ -459,7 +454,6 @@ public:
Type type;
/// \var EncodedRegion_Current
/// Pointer to currently active encoded region.
/// Why not decoding all regions at once and not to set one buffer with decoded data?
/// Yes, why not? Even "accessor" point to decoded data. I mean that fields "byteOffset", "byteStride" and "count" has values which describes decoded
@ -499,7 +493,6 @@ public:
bool LoadFromStream(IOStream &stream, size_t length = 0, size_t baseOffset = 0);
/// \fn void EncodedRegion_Mark(const size_t pOffset, const size_t pEncodedData_Length, uint8_t* pDecodedData, const size_t pDecodedData_Length, const std::string& pID)
/// Mark region of "bufferView" as encoded. When data is request from such region then "bufferView" use decoded data.
/// \param [in] pOffset - offset from begin of "bufferView" to encoded region, in bytes.
/// \param [in] pEncodedData_Length - size of encoded region, in bytes.
@ -508,12 +501,10 @@ public:
/// \param [in] pID - ID of the region.
void EncodedRegion_Mark(const size_t pOffset, const size_t pEncodedData_Length, uint8_t *pDecodedData, const size_t pDecodedData_Length, const std::string &pID);
/// \fn void EncodedRegion_SetCurrent(const std::string& pID)
/// Select current encoded region by ID. \sa EncodedRegion_Current.
/// \param [in] pID - ID of the region.
void EncodedRegion_SetCurrent(const std::string &pID);
/// \fn bool ReplaceData(const size_t pBufferData_Offset, const size_t pBufferData_Count, const uint8_t* pReplace_Data, const size_t pReplace_Count)
/// Replace part of buffer data. Pay attention that function work with original array of data (\ref mData) not with encoded regions.
/// \param [in] pBufferData_Offset - index of first element in buffer from which new data will be placed.
/// \param [in] pBufferData_Count - count of bytes in buffer which will be replaced.
@ -821,6 +812,15 @@ struct MaterialEmissiveStrength {
void SetDefaults();
};
struct MaterialAnisotropy {
float anisotropyStrength = 0.f;
float anisotropyRotation = 0.f;
TextureInfo anisotropyTexture;
MaterialAnisotropy() { SetDefaults(); }
void SetDefaults();
};
//! The material appearance of a primitive.
struct Material : public Object {
//PBR metallic roughness properties
@ -859,6 +859,9 @@ struct Material : public Object {
//extension: KHR_materials_emissive_strength
Nullable<MaterialEmissiveStrength> materialEmissiveStrength;
//extension: KHR_materials_anisotropy
Nullable<MaterialAnisotropy> materialAnisotropy;
//extension: KHR_materials_unlit
bool unlit;
@ -989,27 +992,24 @@ struct Texture : public Object {
struct Animation : public Object {
struct Sampler {
Sampler() :
interpolation(Interpolation_LINEAR) {}
Sampler() = default;
Ref<Accessor> input; //!< Accessor reference to the buffer storing the key-frame times.
Ref<Accessor> output; //!< Accessor reference to the buffer storing the key-frame values.
Interpolation interpolation; //!< Type of interpolation algorithm to use between key-frames.
Interpolation interpolation{Interpolation_LINEAR}; //!< Type of interpolation algorithm to use between key-frames.
};
struct Target {
Target() :
path(AnimationPath_TRANSLATION) {}
Target() = default;
Ref<Node> node; //!< The node to animate.
AnimationPath path; //!< The property of the node to animate.
AnimationPath path{AnimationPath_TRANSLATION}; //!< The property of the node to animate.
};
struct Channel {
Channel() :
sampler(-1) {}
Channel() = default;
int sampler; //!< The sampler index containing the animation data.
int sampler{-1}; //!< The sampler index containing the animation data.
Target target; //!< The node and property to animate.
};
@ -1122,48 +1122,34 @@ class Asset {
public:
//! Keeps info about the enabled extensions
struct Extensions {
bool KHR_materials_pbrSpecularGlossiness;
bool KHR_materials_specular;
bool KHR_materials_unlit;
bool KHR_lights_punctual;
bool KHR_texture_transform;
bool KHR_materials_sheen;
bool KHR_materials_clearcoat;
bool KHR_materials_transmission;
bool KHR_materials_volume;
bool KHR_materials_ior;
bool KHR_materials_emissive_strength;
bool KHR_draco_mesh_compression;
bool FB_ngon_encoding;
bool KHR_texture_basisu;
bool KHR_materials_pbrSpecularGlossiness{false};
bool KHR_materials_specular{false};
bool KHR_materials_unlit{false};
bool KHR_lights_punctual{false};
bool KHR_texture_transform{false};
bool KHR_materials_sheen{false};
bool KHR_materials_clearcoat{false};
bool KHR_materials_transmission{false};
bool KHR_materials_volume{false};
bool KHR_materials_ior{false};
bool KHR_materials_emissive_strength{false};
bool KHR_materials_anisotropy{false};
bool KHR_draco_mesh_compression{false};
bool FB_ngon_encoding{false};
bool KHR_texture_basisu{false};
bool EXT_texture_webp{false};
Extensions() :
KHR_materials_pbrSpecularGlossiness(false),
KHR_materials_specular(false),
KHR_materials_unlit(false),
KHR_lights_punctual(false),
KHR_texture_transform(false),
KHR_materials_sheen(false),
KHR_materials_clearcoat(false),
KHR_materials_transmission(false),
KHR_materials_volume(false),
KHR_materials_ior(false),
KHR_materials_emissive_strength(false),
KHR_draco_mesh_compression(false),
FB_ngon_encoding(false),
KHR_texture_basisu(false) {
// empty
}
Extensions() = default;
~Extensions() = default;
} extensionsUsed;
//! Keeps info about the required extensions
struct RequiredExtensions {
bool KHR_draco_mesh_compression;
bool KHR_texture_basisu;
bool KHR_draco_mesh_compression{false};
bool KHR_texture_basisu{false};
bool EXT_texture_webp{false};
RequiredExtensions() : KHR_draco_mesh_compression(false), KHR_texture_basisu(false) {
// empty
}
RequiredExtensions() = default;
} extensionsRequired;
AssetMetadata asset;
@ -1251,8 +1237,8 @@ private:
size_t mSceneLength;
size_t mBodyOffset;
size_t mBodyLength;
IdMap mUsedIds;
Ref<Buffer> mBodyBuffer;
std::unordered_map<std::string, int> lastUsedID;
};
inline std::string getContextForErrorMessages(const std::string &id, const std::string &name) {
@ -1268,6 +1254,6 @@ inline std::string getContextForErrorMessages(const std::string &id, const std::
// Include the implementation of the methods
#include "glTF2Asset.inl"
#endif // ASSIMP_BUILD_NO_GLTF_IMPORTER
//#endif // ASSIMP_BUILD_NO_GLTF_IMPORTER
#endif // GLTF2ASSET_H_INC

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
Copyright (c) 2006-2026, assimp team
All rights reserved.
@ -39,7 +39,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
#include "AssetLib/glTF/glTFCommon.h"
#include "AssetLib/glTFCommon/glTFCommon.h"
#include <assimp/MemoryIOWrapper.h>
#include <assimp/StringUtils.h>
@ -87,6 +87,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using namespace Assimp;
namespace glTF2 {
using glTFCommon::FindStringInContext;
using glTFCommon::FindNumberInContext;
using glTFCommon::FindUIntInContext;
@ -437,7 +438,6 @@ unsigned int LazyDict<T>::Remove(const char *id) {
const unsigned int index = objIt->second;
mAsset.mUsedIds[id] = false;
mObjsById.erase(id);
mObjsByOIndex.erase(index);
delete mObjs[index];
@ -471,7 +471,6 @@ unsigned int LazyDict<T>::Remove(const char *id) {
template <class T>
Ref<T> LazyDict<T>::Retrieve(unsigned int i) {
typename Dict::iterator it = mObjsByOIndex.find(i);
if (it != mObjsByOIndex.end()) { // already created?
return Ref<T>(mObjs, it->second);
@ -539,16 +538,11 @@ Ref<T> LazyDict<T>::Add(T *obj) {
mObjs.push_back(obj);
mObjsByOIndex[obj->oIndex] = idx;
mObjsById[obj->id] = idx;
mAsset.mUsedIds[obj->id] = true;
return Ref<T>(mObjs, idx);
}
template <class T>
Ref<T> LazyDict<T>::Create(const char *id) {
Asset::IdMap::iterator it = mAsset.mUsedIds.find(id);
if (it != mAsset.mUsedIds.end()) {
throw DeadlyImportError("GLTF: two objects with the same ID exist");
}
T *inst = new T();
unsigned int idx = unsigned(mObjs.size());
inst->id = id;
@ -564,11 +558,12 @@ inline Buffer::Buffer() :
byteLength(0),
type(Type_arraybuffer),
EncodedRegion_Current(nullptr),
mIsSpecial(false) {}
mIsSpecial(false) {
// empty
}
inline Buffer::~Buffer() {
for (SEncodedRegion *reg : EncodedRegion_List)
delete reg;
for (SEncodedRegion *reg : EncodedRegion_List) delete reg;
}
inline const char *Buffer::TranslateId(Asset & /*r*/, const char *id) {
@ -692,7 +687,6 @@ inline void Buffer::EncodedRegion_SetCurrent(const std::string &pID) {
}
inline bool Buffer::ReplaceData(const size_t pBufferData_Offset, const size_t pBufferData_Count, const uint8_t *pReplace_Data, const size_t pReplace_Count) {
if ((pBufferData_Count == 0) || (pReplace_Count == 0) || (pReplace_Data == nullptr)) {
return false;
}
@ -808,12 +802,11 @@ inline uint8_t *BufferView::GetPointerAndTailSize(size_t accOffset, size_t& outT
}
}
if (offset >= buffer->byteLength)
{
if (offset >= buffer->byteLength) {
outTailSize = 0;
return nullptr;
}
outTailSize = buffer->byteLength - offset;
return basePtr + offset;
}
@ -895,7 +888,9 @@ inline void Accessor::Read(Value &obj, Asset &r) {
if (bufferView) {
// Check length
unsigned long long byteLength = (unsigned long long)GetBytesPerComponent() * (unsigned long long)count;
unsigned long long byteLength = count > 0
? (unsigned long long)GetStride() * (unsigned long long)(count - 1) + (unsigned long long)GetElementSize()
: 0;
// handle integer overflow
if (byteLength < count) {
@ -918,7 +913,7 @@ inline void Accessor::Read(Value &obj, Asset &r) {
Value *indiceViewID = FindUInt(*indicesValue, "bufferView");
if (!indiceViewID) {
throw DeadlyImportError("A bufferView value is required, when reading ", id.c_str(), name.empty() ? "" : " (" + name + ")");
}
}
sparse->indices = r.bufferViews.Retrieve(indiceViewID->GetUint());
//indices byteOffset
sparse->indicesByteOffset = MemberOrDefault(*indicesValue, "byteOffset", size_t(0));
@ -956,8 +951,8 @@ inline void Accessor::Read(Value &obj, Asset &r) {
}
else {
sparse->PopulateData(dataSize, nullptr);
}
sparse->PatchData(elementSize);
}
sparse->PatchData(elementSize);
}
}
@ -1011,7 +1006,15 @@ inline size_t Accessor::GetMaxByteSize() {
if (decodedBuffer)
return decodedBuffer->byteLength;
return (bufferView ? bufferView->byteLength : sparse->data.size());
if (sparse) {
return sparse->data.size();
}
if (bufferView) {
return byteOffset <= bufferView->byteLength ? bufferView->byteLength - byteOffset : 0;
}
return 0;
}
template <class T>
@ -1035,21 +1038,27 @@ size_t Accessor::ExtractData(T *&outData, const std::vector<unsigned int> *remap
const size_t maxSize = GetMaxByteSize();
if (elemSize > maxSize) {
throw DeadlyImportError("GLTF: elemSize ", elemSize, " > maxSize ", maxSize, " in ", getContextForErrorMessages(id, name));
}
const size_t maxCount = (maxSize - elemSize) / stride + 1;
if (count > maxCount) {
throw DeadlyImportError("GLTF: count ", count, " > maxCount ", maxCount, " in ", getContextForErrorMessages(id, name));
}
outData = new T[usedCount];
if (remappingIndices != nullptr) {
const unsigned int maxIndexCount = static_cast<unsigned int>(maxSize / stride);
for (size_t i = 0; i < usedCount; ++i) {
size_t srcIdx = (*remappingIndices)[i];
if (srcIdx >= maxIndexCount) {
throw DeadlyImportError("GLTF: index*stride ", (srcIdx * stride), " > maxSize ", maxSize, " in ", getContextForErrorMessages(id, name));
if (srcIdx >= count) {
throw DeadlyImportError("GLTF: index ", srcIdx, " >= count ", count, " in ", getContextForErrorMessages(id, name));
}
memcpy(outData + i, data + srcIdx * stride, elemSize);
}
} else { // non-indexed cases
if (usedCount * stride > maxSize) {
throw DeadlyImportError("GLTF: count*stride ", (usedCount * stride), " > maxSize ", maxSize, " in ", getContextForErrorMessages(id, name));
}
if (stride == elemSize && targetElemSize == elemSize) {
memcpy(outData, data, totalSize);
} else {
@ -1219,6 +1228,24 @@ inline void Texture::Read(Value &obj, Asset &r) {
if (Value *samplerVal = FindUInt(obj, "sampler")) {
sampler = r.samplers.Retrieve(samplerVal->GetUint());
}
if (Value *extensions = FindObject(obj, "extensions")) {
if (r.extensionsUsed.KHR_texture_basisu) {
if (Value *curBasisU = FindObject(*extensions, "KHR_texture_basisu")) {
if (Value *sourceVal = FindUInt(*curBasisU, "source")) {
source = r.images.Retrieve(sourceVal->GetUint());
}
}
} else if(r.extensionsUsed.EXT_texture_webp) {
if (Value *curBasisU = FindObject(*extensions, "EXT_texture_webp")) {
if (Value *sourceVal = FindUInt(*curBasisU, "source")) {
source = r.images.Retrieve(sourceVal->GetUint());
}
}
}
}
}
void Material::SetTextureProperties(Asset &r, Value *prop, TextureInfo &out) {
@ -1316,7 +1343,7 @@ inline void Material::Read(Value &material, Asset &r) {
this->pbrSpecularGlossiness = Nullable<PbrSpecularGlossiness>(pbrSG);
}
}
if (r.extensionsUsed.KHR_materials_specular) {
if (Value *curMatSpecular = FindObject(*extensions, "KHR_materials_specular")) {
MaterialSpecular specular;
@ -1403,6 +1430,18 @@ inline void Material::Read(Value &material, Asset &r) {
}
}
if (r.extensionsUsed.KHR_materials_anisotropy) {
if (Value *curMaterialAnisotropy = FindObject(*extensions, "KHR_materials_anisotropy")) {
MaterialAnisotropy anisotropy;
ReadMember(*curMaterialAnisotropy, "anisotropyStrength", anisotropy.anisotropyStrength);
ReadMember(*curMaterialAnisotropy, "anisotropyRotation", anisotropy.anisotropyRotation);
ReadTextureProperty(r, *curMaterialAnisotropy, "anisotropyTexture", anisotropy.anisotropyTexture);
this->materialAnisotropy = Nullable<MaterialAnisotropy>(anisotropy);
}
}
unlit = nullptr != FindObject(*extensions, "KHR_materials_unlit");
}
}
@ -1456,6 +1495,12 @@ inline void MaterialEmissiveStrength::SetDefaults() {
emissiveStrength = 0.f;
}
inline void MaterialAnisotropy::SetDefaults() {
//KHR_materials_anisotropy properties
anisotropyStrength = 0.f;
anisotropyRotation = 0.f;
}
inline void Mesh::Read(Value &pJSON_Object, Asset &pAsset_Root) {
Value *curName = FindMember(pJSON_Object, "name");
if (nullptr != curName && curName->IsString()) {
@ -2045,6 +2090,12 @@ inline void Asset::Load(const std::string &pFile, bool isBinary)
mDicts[i]->AttachToDocument(doc);
}
// Read the "extensions" property, then add it to each scene's metadata.
CustomExtension customExtensions;
if (Value *extensionsObject = FindObject(doc, "extensions")) {
customExtensions = glTF2::ReadExtensions("extensions", *extensionsObject);
}
// Read the "scene" property, which specifies which scene to load
// and recursively load everything referenced by it
unsigned int sceneIndex = 0;
@ -2056,6 +2107,8 @@ inline void Asset::Load(const std::string &pFile, bool isBinary)
if (Value *scenesArray = FindArray(doc, "scenes")) {
if (sceneIndex < scenesArray->Size()) {
this->scene = scenes.Retrieve(sceneIndex);
this->scene->customExtensions = customExtensions;
}
}
@ -2118,6 +2171,8 @@ inline void Asset::ReadExtensionsRequired(Document &doc) {
}
CHECK_REQUIRED_EXT(KHR_draco_mesh_compression);
CHECK_REQUIRED_EXT(KHR_texture_basisu);
CHECK_REQUIRED_EXT(EXT_texture_webp);
#undef CHECK_REQUIRED_EXT
}
@ -2145,8 +2200,10 @@ inline void Asset::ReadExtensionsUsed(Document &doc) {
CHECK_EXT(KHR_materials_volume);
CHECK_EXT(KHR_materials_ior);
CHECK_EXT(KHR_materials_emissive_strength);
CHECK_EXT(KHR_materials_anisotropy);
CHECK_EXT(KHR_draco_mesh_compression);
CHECK_EXT(KHR_texture_basisu);
CHECK_EXT(EXT_texture_webp);
#undef CHECK_EXT
}
@ -2166,31 +2223,27 @@ inline IOStream *Asset::OpenFile(const std::string &path, const char *mode, bool
inline std::string Asset::FindUniqueID(const std::string &str, const char *suffix) {
std::string id = str;
if (!id.empty()) {
if (mUsedIds.find(id) == mUsedIds.end())
int n = 1;
if(!id.empty()) {
n = lastUsedID[id];
if(!n) {
lastUsedID[id] = n+1;
return id;
}
id += "_";
}
id += suffix;
Asset::IdMap::iterator it = mUsedIds.find(id);
if (it == mUsedIds.end()) {
return id;
if(suffix) {
id += suffix;
n = lastUsedID[id];
if(!n) {
lastUsedID[id] = n+1;
return id;
}
}
std::vector<char> buffer;
buffer.resize(id.size() + 16);
int offset = ai_snprintf(buffer.data(), buffer.size(), "%s_", id.c_str());
for (int i = 0; it != mUsedIds.end(); ++i) {
ai_snprintf(buffer.data() + offset, buffer.size() - offset, "%d", i);
id = buffer.data();
it = mUsedIds.find(id);
}
return id;
lastUsedID[id] = n+1;
return id + "_" + std::to_string(n-1);
}
#if _MSC_VER

View file

@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
Copyright (c) 2006-2026, assimp team
All rights reserved.
@ -53,12 +52,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* KHR_materials_volume: full
* KHR_materials_ior: full
* KHR_materials_emissive_strength: full
* KHR_materials_anisotropy: full
*/
#ifndef GLTF2ASSETWRITER_H_INC
#define GLTF2ASSETWRITER_H_INC
#if !defined(ASSIMP_BUILD_NO_GLTF_IMPORTER) && !defined(ASSIMP_BUILD_NO_GLTF2_IMPORTER)
#include "glTF2Asset.h"
namespace glTF2
@ -98,6 +96,4 @@ public:
// Include the implementation of the methods
#include "glTF2AssetWriter.inl"
#endif // ASSIMP_BUILD_NO_GLTF_IMPORTER
#endif // GLTF2ASSETWRITER_H_INC

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
Copyright (c) 2006-2026, assimp team
All rights reserved.
@ -546,6 +546,26 @@ namespace glTF2 {
}
}
if (m.materialAnisotropy.isPresent) {
Value materialAnisotropy(rapidjson::Type::kObjectType);
MaterialAnisotropy &anisotropy = m.materialAnisotropy.value;
if (anisotropy.anisotropyStrength != 0.f) {
WriteFloat(materialAnisotropy, anisotropy.anisotropyStrength, "anisotropyStrength", w.mAl);
}
if (anisotropy.anisotropyRotation != 0.f) {
WriteFloat(materialAnisotropy, anisotropy.anisotropyRotation, "anisotropyRotation", w.mAl);
}
WriteTex(materialAnisotropy, anisotropy.anisotropyTexture, "anisotropyTexture", w.mAl);
if (!materialAnisotropy.ObjectEmpty()) {
exts.AddMember("KHR_materials_anisotropy", materialAnisotropy, w.mAl);
}
}
if (!exts.ObjectEmpty()) {
obj.AddMember("extensions", exts, w.mAl);
}
@ -608,6 +628,7 @@ namespace glTF2 {
{
WriteAttrs(w, attrs, p.attributes.position, "POSITION");
WriteAttrs(w, attrs, p.attributes.normal, "NORMAL");
WriteAttrs(w, attrs, p.attributes.tangent, "TANGENT");
WriteAttrs(w, attrs, p.attributes.texcoord, "TEXCOORD", true);
WriteAttrs(w, attrs, p.attributes.color, "COLOR", true);
WriteAttrs(w, attrs, p.attributes.joint, "JOINTS", true);
@ -689,7 +710,7 @@ namespace glTF2 {
for (auto const &value : extras.mValues) {
WriteExtrasValue(extrasNode, value, w);
}
obj.AddMember("extras", extrasNode, w.mAl);
}
@ -1017,6 +1038,10 @@ namespace glTF2 {
exts.PushBack(StringRef("KHR_materials_emissive_strength"), mAl);
}
if (this->mAsset.extensionsUsed.KHR_materials_anisotropy) {
exts.PushBack(StringRef("KHR_materials_anisotropy"), mAl);
}
if (this->mAsset.extensionsUsed.FB_ngon_encoding) {
exts.PushBack(StringRef("FB_ngon_encoding"), mAl);
}

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
Copyright (c) 2006-2026, assimp team
All rights reserved.
@ -59,10 +59,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Header files, standard library.
#include <cinttypes>
#include <cmath>
#include <limits>
#include <memory>
#include <iostream>
#include <rapidjson/rapidjson.h>
#include <rapidjson/document.h>
#include <rapidjson/error/en.h>
using namespace rapidjson;
using namespace Assimp;
@ -551,12 +556,6 @@ void glTF2Exporter::GetMatTexProp(const aiMaterial &mat, unsigned int &prop, con
mat.Get(textureKey.c_str(), tt, slot, prop);
}
void glTF2Exporter::GetMatTexProp(const aiMaterial &mat, float &prop, const char *propName, aiTextureType tt, unsigned int slot) {
std::string textureKey = std::string(_AI_MATKEY_TEXTURE_BASE) + "." + propName;
mat.Get(textureKey.c_str(), tt, slot, prop);
}
void glTF2Exporter::GetMatTex(const aiMaterial &mat, Ref<Texture> &texture, unsigned int &texCoord, aiTextureType tt, unsigned int slot = 0) {
if (mat.GetTextureCount(tt) == 0) {
return;
@ -647,7 +646,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial &mat, NormalTextureInfo &prop, ai
if (texture) {
// GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot);
GetMatTexProp(mat, prop.scale, "scale", tt, slot);
mat.Get(AI_MATKEY_GLTF_TEXTURE_SCALE(tt, slot), prop.scale);
}
}
@ -658,7 +657,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial &mat, OcclusionTextureInfo &prop,
if (texture) {
// GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot);
GetMatTexProp(mat, prop.strength, "strength", tt, slot);
mat.Get(AI_MATKEY_GLTF_TEXTURE_STRENGTH(tt, slot), prop.strength);
}
}
@ -804,6 +803,22 @@ bool glTF2Exporter::GetMatEmissiveStrength(const aiMaterial &mat, glTF2::Materia
return mat.Get(AI_MATKEY_EMISSIVE_INTENSITY, emissiveStrength.emissiveStrength) == aiReturn_SUCCESS;
}
bool glTF2Exporter::GetMatAnisotropy(const aiMaterial &mat, glTF2::MaterialAnisotropy &anisotropy) {
if (mat.Get(AI_MATKEY_ANISOTROPY_FACTOR, anisotropy.anisotropyStrength) != aiReturn_SUCCESS) {
return false;
}
// do not export anisotropy when strength is zero
if (anisotropy.anisotropyStrength == 0.0f) {
return false;
}
mat.Get(AI_MATKEY_ANISOTROPY_ROTATION, anisotropy.anisotropyRotation);
GetMatTex(mat, anisotropy.anisotropyTexture, AI_MATKEY_ANISOTROPY_TEXTURE);
return true;
}
void glTF2Exporter::ExportMaterials() {
aiString aiName;
for (unsigned int i = 0; i < mScene->mNumMaterials; ++i) {
@ -956,6 +971,12 @@ void glTF2Exporter::ExportMaterials() {
mAsset->extensionsUsed.KHR_materials_emissive_strength = true;
m->materialEmissiveStrength = Nullable<MaterialEmissiveStrength>(emissiveStrength);
}
MaterialAnisotropy anisotropy;
if (GetMatAnisotropy(mat, anisotropy)) {
mAsset->extensionsUsed.KHR_materials_anisotropy = true;
m->materialAnisotropy = Nullable<MaterialAnisotropy>(anisotropy);
}
}
}
}
@ -1215,6 +1236,32 @@ void glTF2Exporter::ExportMeshes() {
p.attributes.normal.push_back(n);
}
/******************** Tangents ********************/
if (nullptr != aim->mTangents && nullptr != aim->mBitangents) {
// Find the handedness by calculating the bitangent without the handedness factor,
// the use a dot product to find out if the original bitangent was inverted (multiplied
// by a factor of -1.0) or not (multiplied by 1.0)
std::vector<ai_real> tangentsWithHandedness(aim->mNumVertices * 4);
for (uint32_t i = 0; i < aim->mNumVertices; ++i) {
aiVector3D calculatedBitangent = aim->mNormals[i] ^ aim->mTangents[i];
ai_real bitangentDotProduct = calculatedBitangent * aim->mBitangents[i];
ai_real handedness = std::copysign(1.0, bitangentDotProduct);
aim->mTangents[i].NormalizeSafe();
tangentsWithHandedness[i * 4] = aim->mTangents[i][0];
tangentsWithHandedness[i * 4 + 1] = aim->mTangents[i][1];
tangentsWithHandedness[i * 4 + 2] = aim->mTangents[i][2];
tangentsWithHandedness[i * 4 + 3] = handedness;
}
Ref<Accessor> t = ExportData(
*mAsset, meshId, b, aim->mNumVertices, &tangentsWithHandedness[0], AttribType::VEC4,
AttribType::VEC4, ComponentType_FLOAT, BufferViewTarget_ARRAY_BUFFER
);
if (t) {
p.attributes.tangent.push_back(t);
}
}
/************** Texture coordinates **************/
for (int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
if (!aim->HasTextureCoords(i)) {
@ -1463,6 +1510,10 @@ unsigned int glTF2Exporter::ExportNodeHierarchy(const aiNode *n) {
Ref<Node> node = mAsset->nodes.Create(mAsset->FindUniqueID(n->mName.C_Str(), "node"));
node->name = n->mName.C_Str();
if(n->mNumChildren > 0)
node->children.reserve(n->mNumChildren);
if(n->mNumMeshes > 0)
node->meshes.reserve(n->mNumMeshes);
if (!n->mTransformation.IsIdentity(configEpsilon)) {
node->matrix.isPresent = true;
@ -1491,6 +1542,10 @@ unsigned int glTF2Exporter::ExportNode(const aiNode *n, Ref<Node> &parent) {
node->parent = parent;
node->name = name;
if(n->mNumChildren > 0)
node->children.reserve(n->mNumChildren);
if(n->mNumMeshes > 0)
node->meshes.reserve(n->mNumMeshes);
ExportNodeExtras(n->mMetaData, node->extras);

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
Copyright (c) 2006-2026, assimp team
All rights reserved.
@ -45,8 +45,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef AI_GLTF2EXPORTER_H_INC
#define AI_GLTF2EXPORTER_H_INC
#if !defined(ASSIMP_BUILD_NO_GLTF_IMPORTER) && !defined(ASSIMP_BUILD_NO_GLTF2_IMPORTER)
#include <assimp/material.h>
#include <assimp/types.h>
#include <assimp/defs.h>
@ -84,6 +82,7 @@ struct MaterialTransmission;
struct MaterialVolume;
struct MaterialIOR;
struct MaterialEmissiveStrength;
struct MaterialAnisotropy;
// Vec/matrix types, as raw float arrays
typedef float(vec2)[2];
@ -111,7 +110,6 @@ protected:
void WriteBinaryData(IOStream *outfile, std::size_t sceneLength);
void GetTexSampler(const aiMaterial &mat, glTFCommon::Ref<glTF2::Texture> texture, aiTextureType tt, unsigned int slot);
void GetMatTexProp(const aiMaterial &mat, unsigned int &prop, const char *propName, aiTextureType tt, unsigned int idx);
void GetMatTexProp(const aiMaterial &mat, float &prop, const char *propName, aiTextureType tt, unsigned int idx);
void GetMatTex(const aiMaterial &mat, glTFCommon::Ref<glTF2::Texture> &texture, unsigned int &texCoord, aiTextureType tt, unsigned int slot);
void GetMatTex(const aiMaterial &mat, glTF2::TextureInfo &prop, aiTextureType tt, unsigned int slot);
void GetMatTex(const aiMaterial &mat, glTF2::NormalTextureInfo &prop, aiTextureType tt, unsigned int slot);
@ -126,6 +124,7 @@ protected:
bool GetMatVolume(const aiMaterial &mat, glTF2::MaterialVolume &volume);
bool GetMatIOR(const aiMaterial &mat, glTF2::MaterialIOR &ior);
bool GetMatEmissiveStrength(const aiMaterial &mat, glTF2::MaterialEmissiveStrength &emissiveStrength);
bool GetMatAnisotropy(const aiMaterial &mat, glTF2::MaterialAnisotropy &anisotropy);
void ExportMetadata();
void ExportMaterials();
void ExportMeshes();
@ -148,6 +147,4 @@ private:
} // namespace Assimp
#endif // ASSIMP_BUILD_NO_GLTF_IMPORTER
#endif // AI_GLTF2EXPORTER_H_INC

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
Copyright (c) 2006-2026, assimp team
All rights reserved.
@ -41,12 +41,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#if !defined(ASSIMP_BUILD_NO_GLTF_IMPORTER) && !defined(ASSIMP_BUILD_NO_GLTF2_IMPORTER)
#include "AssetLib/glTF2/glTF2Importer.h"
#include "AssetLib/glTF2/glTF2Asset.h"
#include "glTF2Importer.h"
#include "glTF2Asset.h"
#include "PostProcessing/MakeVerboseFormat.h"
#if !defined(ASSIMP_BUILD_NO_EXPORT)
#include "AssetLib/glTF2/glTF2AssetWriter.h"
# include "AssetLib/glTF2/glTF2AssetWriter.h"
#endif
#include <assimp/CreateAnimMesh.h>
@ -65,12 +65,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <rapidjson/document.h>
#include <rapidjson/rapidjson.h>
#include <rapidjson/error/en.h>
using namespace Assimp;
using namespace glTF2;
using namespace glTFCommon;
namespace {
// generate bi-tangents from normals and tangents according to spec
struct Tangent {
aiVector3D xyz;
@ -111,7 +113,7 @@ bool glTF2Importer::CanRead(const std::string &filename, IOSystem *pIOHandler, b
}
if (pIOHandler) {
glTF2::Asset asset(pIOHandler);
Asset asset(pIOHandler);
return asset.CanRead(
filename,
CheckMagicToken(
@ -122,22 +124,23 @@ bool glTF2Importer::CanRead(const std::string &filename, IOSystem *pIOHandler, b
return false;
}
static inline aiTextureMapMode ConvertWrappingMode(SamplerWrap gltfWrapMode) {
static aiTextureMapMode ConvertWrappingMode(SamplerWrap gltfWrapMode) {
switch (gltfWrapMode) {
case SamplerWrap::Mirrored_Repeat:
return aiTextureMapMode_Mirror;
case SamplerWrap::Mirrored_Repeat:
return aiTextureMapMode_Mirror;
case SamplerWrap::Clamp_To_Edge:
return aiTextureMapMode_Clamp;
case SamplerWrap::Clamp_To_Edge:
return aiTextureMapMode_Clamp;
case SamplerWrap::UNSET:
case SamplerWrap::Repeat:
default:
return aiTextureMapMode_Wrap;
case SamplerWrap::UNSET:
case SamplerWrap::Repeat:
default:
break;
}
return aiTextureMapMode_Wrap;
}
static inline void SetMaterialColorProperty(Asset & /*r*/, vec4 &prop, aiMaterial *mat,
static void SetMaterialColorProperty(Asset & /*r*/, vec4 &prop, aiMaterial *mat,
const char *pKey, unsigned int type, unsigned int idx) {
aiColor4D col;
CopyValue(prop, col);
@ -147,12 +150,12 @@ static inline void SetMaterialColorProperty(Asset & /*r*/, vec4 &prop, aiMateria
static inline void SetMaterialColorProperty(Asset & /*r*/, vec3 &prop, aiMaterial *mat,
const char *pKey, unsigned int type, unsigned int idx) {
aiColor4D col;
glTFCommon::CopyValue(prop, col);
CopyValue(prop, col);
mat->AddProperty(&col, 1, pKey, type, idx);
}
static void SetMaterialTextureProperty(std::vector<int> &embeddedTexIdxs, Asset & /*r*/,
glTF2::TextureInfo prop, aiMaterial *mat, aiTextureType texType,
TextureInfo prop, aiMaterial *mat, aiTextureType texType,
unsigned int texSlot = 0) {
if (prop.texture && prop.texture->source) {
aiString uri(prop.texture->source->uri);
@ -218,24 +221,23 @@ static void SetMaterialTextureProperty(std::vector<int> &embeddedTexIdxs, Asset
}
}
inline void SetMaterialTextureProperty(std::vector<int> &embeddedTexIdxs, Asset &r,
static void SetMaterialTextureProperty(std::vector<int> &embeddedTexIdxs, Asset &r,
NormalTextureInfo &prop, aiMaterial *mat, aiTextureType texType,
unsigned int texSlot = 0) {
SetMaterialTextureProperty(embeddedTexIdxs, r, (glTF2::TextureInfo)prop, mat, texType, texSlot);
SetMaterialTextureProperty(embeddedTexIdxs, r, static_cast<TextureInfo>(prop), mat, texType, texSlot);
if (prop.texture && prop.texture->source) {
mat->AddProperty(&prop.scale, 1, AI_MATKEY_GLTF_TEXTURE_SCALE(texType, texSlot));
}
}
inline void SetMaterialTextureProperty(std::vector<int> &embeddedTexIdxs, Asset &r,
static void SetMaterialTextureProperty(std::vector<int> &embeddedTexIdxs, Asset &r,
OcclusionTextureInfo &prop, aiMaterial *mat, aiTextureType texType,
unsigned int texSlot = 0) {
SetMaterialTextureProperty(embeddedTexIdxs, r, (glTF2::TextureInfo)prop, mat, texType, texSlot);
SetMaterialTextureProperty(embeddedTexIdxs, r, static_cast<TextureInfo>(prop), mat, texType, texSlot);
if (prop.texture && prop.texture->source) {
std::string textureStrengthKey = std::string(_AI_MATKEY_TEXTURE_BASE) + "." + "strength";
mat->AddProperty(&prop.strength, 1, textureStrengthKey.c_str(), texType, texSlot);
mat->AddProperty(&prop.strength, 1, AI_MATKEY_GLTF_TEXTURE_STRENGTH(texType, texSlot));
}
}
@ -285,7 +287,7 @@ static aiMaterial *ImportMaterial(std::vector<int> &embeddedTexIdxs, Asset &r, M
if (mat.materialSpecular.isPresent) {
MaterialSpecular &specular = mat.materialSpecular.value;
// Default values of zero disables Specular
if (std::memcmp(specular.specularColorFactor, defaultSpecularColorFactor, sizeof(glTFCommon::vec3)) != 0 || specular.specularFactor != 0.0f) {
if (std::memcmp(specular.specularColorFactor, defaultSpecularColorFactor, sizeof(vec3)) != 0 || specular.specularFactor != 0.0f) {
SetMaterialColorProperty(r, specular.specularColorFactor, aimat, AI_MATKEY_COLOR_SPECULAR);
aimat->AddProperty(&specular.specularFactor, 1, AI_MATKEY_SPECULAR_FACTOR);
SetMaterialTextureProperty(embeddedTexIdxs, r, specular.specularTexture, aimat, aiTextureType_SPECULAR, 0);
@ -369,11 +371,21 @@ static aiMaterial *ImportMaterial(std::vector<int> &embeddedTexIdxs, Asset &r, M
// KHR_materials_emissive_strength
if (mat.materialEmissiveStrength.isPresent) {
MaterialEmissiveStrength &emissiveStrength = mat.materialEmissiveStrength.value;
const MaterialEmissiveStrength &emissiveStrength = mat.materialEmissiveStrength.value;
aimat->AddProperty(&emissiveStrength.emissiveStrength, 1, AI_MATKEY_EMISSIVE_INTENSITY);
}
// KHR_materials_anisotropy
if (mat.materialAnisotropy.isPresent) {
const MaterialAnisotropy &anisotropy = mat.materialAnisotropy.value;
aimat->AddProperty(&anisotropy.anisotropyStrength, 1, AI_MATKEY_ANISOTROPY_FACTOR);
aimat->AddProperty(&anisotropy.anisotropyRotation, 1, AI_MATKEY_ANISOTROPY_ROTATION);
SetMaterialTextureProperty(embeddedTexIdxs, r, anisotropy.anisotropyTexture, aimat, AI_MATKEY_ANISOTROPY_TEXTURE);
}
return aimat;
} catch (...) {
delete aimat;
@ -449,9 +461,9 @@ template <typename T>
aiColor4D *GetVertexColorsForType(Ref<Accessor> input, std::vector<unsigned int> *vertexRemappingTable) {
constexpr float max = std::numeric_limits<T>::max();
aiColor4t<T> *colors;
input->ExtractData(colors, vertexRemappingTable);
auto output = new aiColor4D[input->count];
for (size_t i = 0; i < input->count; i++) {
size_t count = input->ExtractData(colors, vertexRemappingTable);
auto output = new aiColor4D[count];
for (size_t i = 0; i < count; i++) {
output[i] = aiColor4D(
colors[i].r / max, colors[i].g / max,
colors[i].b / max, colors[i].a / max);
@ -498,7 +510,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
// Extract used vertices:
bool useIndexBuffer = prim.indices;
std::vector<unsigned int> *vertexRemappingTable = nullptr;
if (useIndexBuffer) {
size_t count = prim.indices->count;
indexBuffer.resize(count);
@ -518,7 +530,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
if (index >= numAllVertices) {
// Out-of-range indices will be filtered out when adding the faces and then lead to a warning. At this stage, we just keep them.
indexBuffer[i] = index;
continue;
continue;
}
if (index >= reverseMappingIndices.size()) {
reverseMappingIndices.resize(index + 1, unusedIndex);
@ -753,9 +765,12 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
ASSIMP_LOG_WARN("The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped.");
count = nFaces * 3;
}
facePtr = faces = new aiFace[nFaces];
for (unsigned int i = 0; i < count; i += 3) {
SetFaceAndAdvance3(facePtr, aim->mNumVertices, indexBuffer[i], indexBuffer[i + 1], indexBuffer[i + 2]);
// copycd:: than > 0
if (nFaces > 0) {
facePtr = faces = new aiFace[nFaces];
for (unsigned int i = 0; i < count; i += 3) {
SetFaceAndAdvance3(facePtr, aim->mNumVertices, indexBuffer[i], indexBuffer[i + 1], indexBuffer[i + 2]);
}
}
break;
}
@ -1055,7 +1070,8 @@ static void BuildVertexWeightMapping(Mesh::Primitive &primitive, std::vector<std
attr.joint[j]->ExtractData(indices16[j], vertexRemappingTablePtr);
}
}
//
// No indices are an invalid usecase
if (nullptr == indices8 && nullptr == indices16) {
// Something went completely wrong!
ai_assert(false);
@ -1299,6 +1315,18 @@ struct AnimationSamplers {
Animation::Sampler *weight;
};
struct vec4f {
float x, y, z, w;
};
static aiAnimInterpolation MapInterpolation(Interpolation interp) {
switch (interp) {
case Interpolation_STEP: return aiAnimInterpolation_Step;
case Interpolation_CUBICSPLINE: return aiAnimInterpolation_Cubic_Spline;
default: return aiAnimInterpolation_Linear;
}
}
aiNodeAnim *CreateNodeAnim(glTF2::Asset &, Node &node, AnimationSamplers &samplers) {
aiNodeAnim *anim = new aiNodeAnim();
@ -1310,15 +1338,41 @@ aiNodeAnim *CreateNodeAnim(glTF2::Asset &, Node &node, AnimationSamplers &sample
if (samplers.translation && samplers.translation->input && samplers.translation->output) {
float *times = nullptr;
samplers.translation->input->ExtractData(times);
aiVector3D *values = nullptr;
samplers.translation->output->ExtractData(values);
anim->mNumPositionKeys = static_cast<uint32_t>(samplers.translation->input->count);
anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
unsigned int ii = (samplers.translation->interpolation == Interpolation_CUBICSPLINE) ? 1 : 0;
for (unsigned int i = 0; i < anim->mNumPositionKeys; ++i) {
anim->mPositionKeys[i].mTime = times[i] * kMillisecondsFromSeconds;
anim->mPositionKeys[i].mValue = values[ii];
ii += (samplers.translation->interpolation == Interpolation_CUBICSPLINE) ? 3 : 1;
vec4f *tmp_values = nullptr;
size_t numItems = samplers.translation->output->ExtractData(tmp_values);
aiVector3D *values = new aiVector3D[numItems];
for (size_t i = 0; i < numItems; ++i) {
values[i].x = tmp_values[i].x;
values[i].y = tmp_values[i].y;
values[i].z = tmp_values[i].z;
}
delete[] tmp_values;
const bool isCubic = (samplers.translation->interpolation == Interpolation_CUBICSPLINE);
const aiAnimInterpolation interpType = MapInterpolation(samplers.translation->interpolation);
const unsigned int numLogicalKeys = static_cast<unsigned int>(samplers.translation->input->count);
if (isCubic) {
// Store as triplets: in-tangent, value, out-tangent per logical key
anim->mNumPositionKeys = numLogicalKeys * 3;
anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
for (unsigned int i = 0; i < numLogicalKeys; ++i) {
unsigned int srcBase = i * 3;
unsigned int dstBase = i * 3;
for (unsigned int t = 0; t < 3; ++t) {
anim->mPositionKeys[dstBase + t].mTime = times[i] * kMillisecondsFromSeconds;
anim->mPositionKeys[dstBase + t].mValue = values[srcBase + t];
anim->mPositionKeys[dstBase + t].mInterpolation = interpType;
}
}
} else {
anim->mNumPositionKeys = numLogicalKeys;
anim->mPositionKeys = new aiVectorKey[anim->mNumPositionKeys];
for (unsigned int i = 0; i < anim->mNumPositionKeys; ++i) {
anim->mPositionKeys[i].mTime = times[i] * kMillisecondsFromSeconds;
anim->mPositionKeys[i].mValue = values[i];
anim->mPositionKeys[i].mInterpolation = interpType;
}
}
delete[] times;
delete[] values;
@ -1336,16 +1390,36 @@ aiNodeAnim *CreateNodeAnim(glTF2::Asset &, Node &node, AnimationSamplers &sample
samplers.rotation->input->ExtractData(times);
aiQuaternion *values = nullptr;
samplers.rotation->output->ExtractData(values);
anim->mNumRotationKeys = static_cast<uint32_t>(samplers.rotation->input->count);
anim->mRotationKeys = new aiQuatKey[anim->mNumRotationKeys];
unsigned int ii = (samplers.rotation->interpolation == Interpolation_CUBICSPLINE) ? 1 : 0;
for (unsigned int i = 0; i < anim->mNumRotationKeys; ++i) {
anim->mRotationKeys[i].mTime = times[i] * kMillisecondsFromSeconds;
anim->mRotationKeys[i].mValue.x = values[ii].w;
anim->mRotationKeys[i].mValue.y = values[ii].x;
anim->mRotationKeys[i].mValue.z = values[ii].y;
anim->mRotationKeys[i].mValue.w = values[ii].z;
ii += (samplers.rotation->interpolation == Interpolation_CUBICSPLINE) ? 3 : 1;
const bool isCubic = (samplers.rotation->interpolation == Interpolation_CUBICSPLINE);
const aiAnimInterpolation interpType = MapInterpolation(samplers.rotation->interpolation);
const unsigned int numLogicalKeys = static_cast<unsigned int>(samplers.rotation->input->count);
if (isCubic) {
anim->mNumRotationKeys = numLogicalKeys * 3;
anim->mRotationKeys = new aiQuatKey[anim->mNumRotationKeys];
for (unsigned int i = 0; i < numLogicalKeys; ++i) {
unsigned int srcBase = i * 3;
unsigned int dstBase = i * 3;
for (unsigned int t = 0; t < 3; ++t) {
anim->mRotationKeys[dstBase + t].mTime = times[i] * kMillisecondsFromSeconds;
anim->mRotationKeys[dstBase + t].mValue.x = values[srcBase + t].w;
anim->mRotationKeys[dstBase + t].mValue.y = values[srcBase + t].x;
anim->mRotationKeys[dstBase + t].mValue.z = values[srcBase + t].y;
anim->mRotationKeys[dstBase + t].mValue.w = values[srcBase + t].z;
anim->mRotationKeys[dstBase + t].mInterpolation = interpType;
}
}
} else {
anim->mNumRotationKeys = numLogicalKeys;
anim->mRotationKeys = new aiQuatKey[anim->mNumRotationKeys];
for (unsigned int i = 0; i < anim->mNumRotationKeys; ++i) {
anim->mRotationKeys[i].mTime = times[i] * kMillisecondsFromSeconds;
anim->mRotationKeys[i].mValue.x = values[i].w;
anim->mRotationKeys[i].mValue.y = values[i].x;
anim->mRotationKeys[i].mValue.z = values[i].y;
anim->mRotationKeys[i].mValue.w = values[i].z;
anim->mRotationKeys[i].mInterpolation = interpType;
}
}
delete[] times;
delete[] values;
@ -1364,13 +1438,30 @@ aiNodeAnim *CreateNodeAnim(glTF2::Asset &, Node &node, AnimationSamplers &sample
samplers.scale->input->ExtractData(times);
aiVector3D *values = nullptr;
samplers.scale->output->ExtractData(values);
anim->mNumScalingKeys = static_cast<uint32_t>(samplers.scale->input->count);
anim->mScalingKeys = new aiVectorKey[anim->mNumScalingKeys];
unsigned int ii = (samplers.scale->interpolation == Interpolation_CUBICSPLINE) ? 1 : 0;
for (unsigned int i = 0; i < anim->mNumScalingKeys; ++i) {
anim->mScalingKeys[i].mTime = times[i] * kMillisecondsFromSeconds;
anim->mScalingKeys[i].mValue = values[ii];
ii += (samplers.scale->interpolation == Interpolation_CUBICSPLINE) ? 3 : 1;
const bool isCubic = (samplers.scale->interpolation == Interpolation_CUBICSPLINE);
const aiAnimInterpolation interpType = MapInterpolation(samplers.scale->interpolation);
const unsigned int numLogicalKeys = static_cast<unsigned int>(samplers.scale->input->count);
if (isCubic) {
anim->mNumScalingKeys = numLogicalKeys * 3;
anim->mScalingKeys = new aiVectorKey[anim->mNumScalingKeys];
for (unsigned int i = 0; i < numLogicalKeys; ++i) {
unsigned int srcBase = i * 3;
unsigned int dstBase = i * 3;
for (unsigned int t = 0; t < 3; ++t) {
anim->mScalingKeys[dstBase + t].mTime = times[i] * kMillisecondsFromSeconds;
anim->mScalingKeys[dstBase + t].mValue = values[srcBase + t];
anim->mScalingKeys[dstBase + t].mInterpolation = interpType;
}
}
} else {
anim->mNumScalingKeys = numLogicalKeys;
anim->mScalingKeys = new aiVectorKey[anim->mNumScalingKeys];
for (unsigned int i = 0; i < anim->mNumScalingKeys; ++i) {
anim->mScalingKeys[i].mTime = times[i] * kMillisecondsFromSeconds;
anim->mScalingKeys[i].mValue = values[i];
anim->mScalingKeys[i].mInterpolation = interpType;
}
}
delete[] times;
delete[] values;
@ -1456,7 +1547,8 @@ std::unordered_map<unsigned int, AnimationSamplers> GatherSamplers(Animation &an
}
if (animsampler.input->count > animsampler.output->count) {
ASSIMP_LOG_WARN("Animation ", anim.name, ": Number of keyframes in sampler input ", animsampler.input->count, " exceeds number of keyframes in sampler output ", animsampler.output->count);
ASSIMP_LOG_WARN("Animation ", anim.name, ": Number of keyframes in sampler input ", animsampler.input->count,
" exceeds number of keyframes in sampler output ", animsampler.output->count);
continue;
}
@ -1636,12 +1728,12 @@ void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset &r) {
ext = "kx2";
} else if (strcmp(ext, "basis") == 0) { // basisu
ext = "bu";
}
} // webp requires no transformation
const size_t len = strlen(ext);
if (len <= 3) {
strncpy(tex->achFormatHint, ext, len);
}
size_t len = strlen(ext);
if (len > 3) len = 3;
tex->achFormatHint[3] = '\0';
memcpy(tex->achFormatHint, ext, len);
}
}
}
@ -1654,7 +1746,8 @@ void glTF2Importer::ImportCommonMetadata(glTF2::Asset &a) {
const bool hasGenerator = !a.asset.generator.empty();
const bool hasCopyright = !a.asset.copyright.empty();
const bool hasSceneMetadata = a.scene->customExtensions;
if (hasVersion || hasGenerator || hasCopyright || hasSceneMetadata) {
const bool hasSceneExtras = a.scene->extras.HasExtras();
if (hasVersion || hasGenerator || hasCopyright || hasSceneMetadata || hasSceneExtras) {
mScene->mMetaData = new aiMetadata;
if (hasVersion) {
mScene->mMetaData->Add(AI_METADATA_SOURCE_FORMAT_VERSION, aiString(a.asset.version));
@ -1668,6 +1761,9 @@ void glTF2Importer::ImportCommonMetadata(glTF2::Asset &a) {
if (hasSceneMetadata) {
ParseExtensions(mScene->mMetaData, a.scene->customExtensions);
}
if (hasSceneExtras) {
ParseExtras(mScene->mMetaData, a.scene->extras);
}
}
}

View file

@ -2,8 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2024, assimp team
Copyright (c) 2006-2026, assimp team
All rights reserved.
@ -43,7 +42,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define AI_GLTF2IMPORTER_H_INC
#include <assimp/BaseImporter.h>
#include <AssetLib/glTF2/glTF2Asset.h>
#include "glTF2Asset.h"
struct aiNode;