mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-07-12 07:04:36 +00:00
update assimp to 6.0.5
This commit is contained in:
parent
2d2eb57e2e
commit
f5cf21cfeb
941 changed files with 22718 additions and 12240 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue