update assimp lib

This commit is contained in:
marauder2k7 2024-12-09 20:22:47 +00:00
parent 03a348deb7
commit d3f8fee74e
1725 changed files with 196314 additions and 62009 deletions

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -44,6 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* glTF Extensions Support:
* KHR_materials_pbrSpecularGlossiness full
* KHR_materials_specular full
* KHR_materials_unlit full
* KHR_lights_punctual full
* KHR_materials_sheen full
@ -51,6 +52,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* KHR_materials_transmission full
* KHR_materials_volume full
* KHR_materials_ior full
* KHR_materials_emissive_strength full
*/
#ifndef GLTF2ASSET_H_INC
#define GLTF2ASSET_H_INC
@ -364,20 +366,20 @@ struct CustomExtension {
~CustomExtension() = default;
CustomExtension(const CustomExtension &other) :
name(other.name),
mStringValue(other.mStringValue),
mDoubleValue(other.mDoubleValue),
mUint64Value(other.mUint64Value),
mInt64Value(other.mInt64Value),
mBoolValue(other.mBoolValue),
mValues(other.mValues) {
// empty
}
CustomExtension(const CustomExtension &other) = default;
CustomExtension& operator=(const CustomExtension&) = default;
};
//! Represents metadata in an glTF2 object
struct Extras {
std::vector<CustomExtension> mValues;
inline bool HasExtras() const {
return !mValues.empty();
}
};
//! Base class for all glTF top-level objects
struct Object {
int index; //!< The index of this object within its property container
@ -386,7 +388,7 @@ struct Object {
std::string name; //!< The user-defined name of this object
CustomExtension customExtensions;
CustomExtension extras;
Extras extras;
//! Objects marked as special are not exported (used to emulate the binary body buffer)
virtual bool IsSpecial() const { return false; }
@ -491,7 +493,7 @@ private:
public:
Buffer();
~Buffer();
~Buffer() override;
void Read(Value &obj, Asset &r);
@ -545,7 +547,7 @@ struct BufferView : public Object {
BufferViewTarget target; //! The target that the WebGL buffer should be bound to.
void Read(Value &obj, Asset &r);
uint8_t *GetPointer(size_t accOffset);
uint8_t *GetPointerAndTailSize(size_t accOffset, size_t& outTailSize);
};
//! A typed view into a BufferView. A BufferView contains raw binary data.
@ -573,7 +575,7 @@ struct Accessor : public Object {
inline size_t GetMaxByteSize();
template <class T>
void ExtractData(T *&outData);
size_t ExtractData(T *&outData, const std::vector<unsigned int> *remappingIndices = nullptr);
void WriteData(size_t count, const void *src_buffer, size_t src_stride);
void WriteSparseValues(size_t count, const void *src_data, size_t src_dataStride);
@ -627,7 +629,7 @@ struct Accessor : public Object {
std::vector<uint8_t> data; //!< Actual data, which may be defaulted to an array of zeros or the original data, with the sparse buffer view applied on top of it.
void PopulateData(size_t numBytes, uint8_t *bytes);
void PopulateData(size_t numBytes, const uint8_t *bytes);
void PatchData(unsigned int elementSize);
};
};
@ -718,6 +720,7 @@ const vec4 defaultBaseColor = { 1, 1, 1, 1 };
const vec3 defaultEmissiveFactor = { 0, 0, 0 };
const vec4 defaultDiffuseFactor = { 1, 1, 1, 1 };
const vec3 defaultSpecularFactor = { 1, 1, 1 };
const vec3 defaultSpecularColorFactor = { 1, 1, 1 };
const vec3 defaultSheenFactor = { 0, 0, 0 };
const vec3 defaultAttenuationColor = { 1, 1, 1 };
@ -761,6 +764,16 @@ struct PbrSpecularGlossiness {
void SetDefaults();
};
struct MaterialSpecular {
float specularFactor;
vec3 specularColorFactor;
TextureInfo specularTexture;
TextureInfo specularColorTexture;
MaterialSpecular() { SetDefaults(); }
void SetDefaults();
};
struct MaterialSheen {
vec3 sheenColorFactor;
float sheenRoughnessFactor;
@ -801,6 +814,13 @@ struct MaterialIOR {
void SetDefaults();
};
struct MaterialEmissiveStrength {
float emissiveStrength = 0.f;
MaterialEmissiveStrength() { SetDefaults(); }
void SetDefaults();
};
//! The material appearance of a primitive.
struct Material : public Object {
//PBR metallic roughness properties
@ -818,6 +838,9 @@ struct Material : public Object {
//extension: KHR_materials_pbrSpecularGlossiness
Nullable<PbrSpecularGlossiness> pbrSpecularGlossiness;
//extension: KHR_materials_specular
Nullable<MaterialSpecular> materialSpecular;
//extension: KHR_materials_sheen
Nullable<MaterialSheen> materialSheen;
@ -832,7 +855,10 @@ struct Material : public Object {
//extension: KHR_materials_ior
Nullable<MaterialIOR> materialIOR;
//extension: KHR_materials_emissive_strength
Nullable<MaterialEmissiveStrength> materialEmissiveStrength;
//extension: KHR_materials_unlit
bool unlit;
@ -1044,7 +1070,7 @@ class LazyDict : public LazyDictBase {
Ref<T> Add(T *obj);
public:
LazyDict(Asset &asset, const char *dictId, const char *extId = 0);
LazyDict(Asset &asset, const char *dictId, const char *extId = nullptr);
~LazyDict();
Ref<T> Retrieve(unsigned int i);
@ -1075,8 +1101,7 @@ struct AssetMetadata {
void Read(Document &doc);
AssetMetadata() :
version() {}
AssetMetadata() = default;
};
//
@ -1098,6 +1123,7 @@ 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;
@ -1106,12 +1132,14 @@ public:
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;
Extensions() :
KHR_materials_pbrSpecularGlossiness(false),
KHR_materials_specular(false),
KHR_materials_unlit(false),
KHR_lights_punctual(false),
KHR_texture_transform(false),
@ -1120,6 +1148,7 @@ public:
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) {

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -45,6 +45,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/StringUtils.h>
#include <assimp/DefaultLogger.hpp>
#include <assimp/Base64.hpp>
#include <rapidjson/document.h>
#include <rapidjson/schema.h>
#include <rapidjson/stringbuffer.h>
// clang-format off
#ifdef ASSIMP_ENABLE_DRACO
@ -139,6 +142,18 @@ inline CustomExtension ReadExtensions(const char *name, Value &obj) {
return ret;
}
inline Extras ReadExtras(Value &obj) {
Extras ret;
ret.mValues.reserve(obj.MemberCount());
for (auto it = obj.MemberBegin(); it != obj.MemberEnd(); ++it) {
auto &val = it->value;
ret.mValues.emplace_back(ReadExtensions(it->name.GetString(), val));
}
return ret;
}
inline void CopyData(size_t count, const uint8_t *src, size_t src_stride,
uint8_t *dst, size_t dst_stride) {
if (src_stride == dst_stride) {
@ -248,7 +263,7 @@ inline void Object::ReadExtensions(Value &val) {
inline void Object::ReadExtras(Value &val) {
if (Value *curExtras = FindObject(val, "extras")) {
this->extras = glTF2::ReadExtensions("extras", *curExtras);
this->extras = glTF2::ReadExtras(*curExtras);
}
}
@ -279,6 +294,8 @@ inline void SetDecodedIndexBuffer_Draco(const draco::Mesh &dracoMesh, Mesh::Prim
// Usually uint32_t but shouldn't assume
if (sizeof(dracoMesh.face(draco::FaceIndex(0))[0]) == componentBytes) {
memcpy(decodedIndexBuffer->GetPointer(), &dracoMesh.face(draco::FaceIndex(0))[0], decodedIndexBuffer->byteLength);
// Assign this alternate data buffer to the accessor
prim.indices->decodedBuffer.swap(decodedIndexBuffer);
return;
}
@ -371,7 +388,7 @@ template <class T>
inline LazyDict<T>::LazyDict(Asset &asset, const char *dictId, const char *extId) :
mDictId(dictId),
mExtId(extId),
mDict(0),
mDict(nullptr),
mAsset(asset) {
asset.mDicts.push_back(this); // register to the list of dictionaries
}
@ -770,12 +787,14 @@ inline void BufferView::Read(Value &obj, Asset &r) {
}
}
inline uint8_t *BufferView::GetPointer(size_t accOffset) {
inline uint8_t *BufferView::GetPointerAndTailSize(size_t accOffset, size_t& outTailSize) {
if (!buffer) {
outTailSize = 0;
return nullptr;
}
uint8_t *basePtr = buffer->GetPointer();
uint8_t * const basePtr = buffer->GetPointer();
if (!basePtr) {
outTailSize = 0;
return nullptr;
}
@ -784,17 +803,25 @@ inline uint8_t *BufferView::GetPointer(size_t accOffset) {
const size_t begin = buffer->EncodedRegion_Current->Offset;
const size_t end = begin + buffer->EncodedRegion_Current->DecodedData_Length;
if ((offset >= begin) && (offset < end)) {
outTailSize = end - offset;
return &buffer->EncodedRegion_Current->DecodedData[offset - begin];
}
}
if (offset >= buffer->byteLength)
{
outTailSize = 0;
return nullptr;
}
outTailSize = buffer->byteLength - offset;
return basePtr + offset;
}
//
// struct Accessor
//
inline void Accessor::Sparse::PopulateData(size_t numBytes, uint8_t *bytes) {
inline void Accessor::Sparse::PopulateData(size_t numBytes, const uint8_t *bytes) {
if (bytes) {
data.assign(bytes, bytes + numBytes);
} else {
@ -803,11 +830,21 @@ inline void Accessor::Sparse::PopulateData(size_t numBytes, uint8_t *bytes) {
}
inline void Accessor::Sparse::PatchData(unsigned int elementSize) {
uint8_t *pIndices = indices->GetPointer(indicesByteOffset);
size_t indicesTailDataSize;
uint8_t *pIndices = indices->GetPointerAndTailSize(indicesByteOffset, indicesTailDataSize);
const unsigned int indexSize = int(ComponentTypeSize(indicesType));
uint8_t *indicesEnd = pIndices + count * indexSize;
uint8_t *pValues = values->GetPointer(valuesByteOffset);
if ((uint64_t)indicesEnd > (uint64_t)pIndices + indicesTailDataSize) {
throw DeadlyImportError("Invalid sparse accessor. Indices outside allocated memory.");
}
size_t valuesTailDataSize;
uint8_t* pValues = values->GetPointerAndTailSize(valuesByteOffset, valuesTailDataSize);
if (elementSize * count > valuesTailDataSize) {
throw DeadlyImportError("Invalid sparse accessor. Indices outside allocated memory.");
}
while (pIndices != indicesEnd) {
size_t offset;
switch (indicesType) {
@ -879,6 +916,9 @@ inline void Accessor::Read(Value &obj, Asset &r) {
if (Value *indicesValue = FindObject(*sparseValue, "indices")) {
//indices bufferView
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));
@ -894,6 +934,9 @@ inline void Accessor::Read(Value &obj, Asset &r) {
if (Value *valuesValue = FindObject(*sparseValue, "values")) {
//value bufferView
Value *valueViewID = FindUInt(*valuesValue, "bufferView");
if (!valueViewID) {
throw DeadlyImportError("A bufferView value is required, when reading ", id.c_str(), name.empty() ? "" : " (" + name + ")");
}
sparse->values = r.bufferViews.Retrieve(valueViewID->GetUint());
//value byteOffset
sparse->valuesByteOffset = MemberOrDefault(*valuesValue, "byteOffset", size_t(0));
@ -903,8 +946,18 @@ inline void Accessor::Read(Value &obj, Asset &r) {
const unsigned int elementSize = GetElementSize();
const size_t dataSize = count * elementSize;
sparse->PopulateData(dataSize, bufferView ? bufferView->GetPointer(byteOffset) : 0);
sparse->PatchData(elementSize);
if (bufferView) {
size_t bufferViewTailSize;
const uint8_t* bufferViewPointer = bufferView->GetPointerAndTailSize(byteOffset, bufferViewTailSize);
if (dataSize > bufferViewTailSize) {
throw DeadlyImportError("Invalid buffer when reading ", id.c_str(), name.empty() ? "" : " (" + name + ")");
}
sparse->PopulateData(dataSize, bufferViewPointer);
}
else {
sparse->PopulateData(dataSize, nullptr);
}
sparse->PatchData(elementSize);
}
}
@ -962,14 +1015,15 @@ inline size_t Accessor::GetMaxByteSize() {
}
template <class T>
void Accessor::ExtractData(T *&outData) {
size_t Accessor::ExtractData(T *&outData, const std::vector<unsigned int> *remappingIndices) {
uint8_t *data = GetPointer();
if (!data) {
throw DeadlyImportError("GLTF2: data is null when extracting data from ", getContextForErrorMessages(id, name));
}
const size_t usedCount = (remappingIndices != nullptr) ? remappingIndices->size() : count;
const size_t elemSize = GetElementSize();
const size_t totalSize = elemSize * count;
const size_t totalSize = elemSize * usedCount;
const size_t stride = GetStride();
@ -980,18 +1034,31 @@ void Accessor::ExtractData(T *&outData) {
}
const size_t maxSize = GetMaxByteSize();
if (count * stride > maxSize) {
throw DeadlyImportError("GLTF: count*stride ", (count * stride), " > maxSize ", maxSize, " in ", getContextForErrorMessages(id, name));
}
outData = new T[count];
if (stride == elemSize && targetElemSize == elemSize) {
memcpy(outData, data, totalSize);
} else {
for (size_t i = 0; i < count; ++i) {
memcpy(outData + i, data + i * stride, elemSize);
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));
}
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 {
for (size_t i = 0; i < usedCount; ++i) {
memcpy(outData + i, data + i * stride, elemSize);
}
}
}
return usedCount;
}
inline void Accessor::WriteData(size_t _count, const void *src_buffer, size_t src_stride) {
@ -1249,6 +1316,19 @@ 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;
ReadMember(*curMatSpecular, "specularFactor", specular.specularFactor);
ReadTextureProperty(r, *curMatSpecular, "specularTexture", specular.specularTexture);
ReadMember(*curMatSpecular, "specularColorFactor", specular.specularColorFactor);
ReadTextureProperty(r, *curMatSpecular, "specularColorTexture", specular.specularColorTexture);
this->materialSpecular = Nullable<MaterialSpecular>(specular);
}
}
// Extension KHR_texture_transform is handled in ReadTextureProperty
@ -1313,6 +1393,16 @@ inline void Material::Read(Value &material, Asset &r) {
}
}
if (r.extensionsUsed.KHR_materials_emissive_strength) {
if (Value *curMaterialEmissiveStrength = FindObject(*extensions, "KHR_materials_emissive_strength")) {
MaterialEmissiveStrength emissiveStrength;
ReadMember(*curMaterialEmissiveStrength, "emissiveStrength", emissiveStrength.emissiveStrength);
this->materialEmissiveStrength = Nullable<MaterialEmissiveStrength>(emissiveStrength);
}
}
unlit = nullptr != FindObject(*extensions, "KHR_materials_unlit");
}
}
@ -1337,6 +1427,12 @@ inline void PbrSpecularGlossiness::SetDefaults() {
glossinessFactor = 1.0f;
}
inline void MaterialSpecular::SetDefaults() {
//KHR_materials_specular properties
SetVector(specularColorFactor, defaultSpecularColorFactor);
specularFactor = 1.f;
}
inline void MaterialSheen::SetDefaults() {
//KHR_materials_sheen properties
SetVector(sheenColorFactor, defaultSheenFactor);
@ -1346,7 +1442,7 @@ inline void MaterialSheen::SetDefaults() {
inline void MaterialVolume::SetDefaults() {
//KHR_materials_volume properties
thicknessFactor = 0.f;
attenuationDistance = INFINITY;
attenuationDistance = std::numeric_limits<float>::infinity();
SetVector(attenuationColor, defaultAttenuationColor);
}
@ -1355,6 +1451,11 @@ inline void MaterialIOR::SetDefaults() {
ior = 1.5f;
}
inline void MaterialEmissiveStrength::SetDefaults() {
//KHR_materials_emissive_strength properties
emissiveStrength = 0.f;
}
inline void Mesh::Read(Value &pJSON_Object, Asset &pAsset_Root) {
Value *curName = FindMember(pJSON_Object, "name");
if (nullptr != curName && curName->IsString()) {
@ -1489,6 +1590,22 @@ inline void Mesh::Read(Value &pJSON_Object, Asset &pAsset_Root) {
}
}
}
if(this->targetNames.empty())
{
Value *curExtras = FindObject(primitive, "extras");
if (nullptr != curExtras) {
if (Value *curTargetNames = FindArray(*curExtras, "targetNames")) {
this->targetNames.resize(curTargetNames->Size());
for (unsigned int j = 0; j < curTargetNames->Size(); ++j) {
Value &targetNameValue = (*curTargetNames)[j];
if (targetNameValue.IsString()) {
this->targetNames[j] = targetNameValue.GetString();
}
}
}
}
}
}
}
@ -1888,7 +2005,7 @@ inline void Asset::Load(const std::string &pFile, bool isBinary)
std::vector<char> sceneData;
rapidjson::Document doc = ReadDocument(*stream, isBinary, sceneData);
// If a schemaDocumentProvider is available, see if the glTF schema is present.
// If a schemaDocumentProvider is available, see if the glTF schema is present.
// If so, use it to validate the document.
if (mSchemaDocumentProvider) {
if (const rapidjson::SchemaDocument *gltfSchema = mSchemaDocumentProvider->GetRemoteDocument("glTF.schema.json", 16)) {
@ -2018,6 +2135,7 @@ inline void Asset::ReadExtensionsUsed(Document &doc) {
}
CHECK_EXT(KHR_materials_pbrSpecularGlossiness);
CHECK_EXT(KHR_materials_specular);
CHECK_EXT(KHR_materials_unlit);
CHECK_EXT(KHR_lights_punctual);
CHECK_EXT(KHR_texture_transform);
@ -2026,6 +2144,7 @@ inline void Asset::ReadExtensionsUsed(Document &doc) {
CHECK_EXT(KHR_materials_transmission);
CHECK_EXT(KHR_materials_volume);
CHECK_EXT(KHR_materials_ior);
CHECK_EXT(KHR_materials_emissive_strength);
CHECK_EXT(KHR_draco_mesh_compression);
CHECK_EXT(KHR_texture_basisu);

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -45,12 +45,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* glTF Extensions Support:
* KHR_materials_pbrSpecularGlossiness: full
* KHR_materials_specular: full
* KHR_materials_unlit: full
* KHR_materials_sheen: full
* KHR_materials_clearcoat: full
* KHR_materials_transmission: full
* KHR_materials_volume: full
* KHR_materials_ior: full
* KHR_materials_emissive_strength: full
*/
#ifndef GLTF2ASSETWRITER_H_INC
#define GLTF2ASSETWRITER_H_INC

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -418,6 +418,27 @@ namespace glTF2 {
exts.AddMember("KHR_materials_unlit", unlit, w.mAl);
}
if (m.materialSpecular.isPresent) {
Value materialSpecular(rapidjson::Type::kObjectType);
materialSpecular.SetObject();
MaterialSpecular &specular = m.materialSpecular.value;
if (specular.specularFactor != 0.0f) {
WriteFloat(materialSpecular, specular.specularFactor, "specularFactor", w.mAl);
}
if (specular.specularColorFactor[0] != defaultSpecularColorFactor[0] && specular.specularColorFactor[1] != defaultSpecularColorFactor[1] && specular.specularColorFactor[2] != defaultSpecularColorFactor[2]) {
WriteVec(materialSpecular, specular.specularColorFactor, "specularColorFactor", w.mAl);
}
WriteTex(materialSpecular, specular.specularTexture, "specularTexture", w.mAl);
WriteTex(materialSpecular, specular.specularColorTexture, "specularColorTexture", w.mAl);
if (!materialSpecular.ObjectEmpty()) {
exts.AddMember("KHR_materials_specular", materialSpecular, w.mAl);
}
}
if (m.materialSheen.isPresent) {
Value materialSheen(rapidjson::Type::kObjectType);
@ -486,7 +507,7 @@ namespace glTF2 {
WriteTex(materialVolume, volume.thicknessTexture, "thicknessTexture", w.mAl);
if (volume.attenuationDistance != INFINITY) {
if (volume.attenuationDistance != std::numeric_limits<float>::infinity()) {
WriteFloat(materialVolume, volume.attenuationDistance, "attenuationDistance", w.mAl);
}
@ -511,6 +532,20 @@ namespace glTF2 {
}
}
if (m.materialEmissiveStrength.isPresent) {
Value materialEmissiveStrength(rapidjson::Type::kObjectType);
MaterialEmissiveStrength &emissiveStrength = m.materialEmissiveStrength.value;
if (emissiveStrength.emissiveStrength != 0.f) {
WriteFloat(materialEmissiveStrength, emissiveStrength.emissiveStrength, "emissiveStrength", w.mAl);
}
if (!materialEmissiveStrength.ObjectEmpty()) {
exts.AddMember("KHR_materials_emissive_strength", materialEmissiveStrength, w.mAl);
}
}
if (!exts.ObjectEmpty()) {
obj.AddMember("extensions", exts, w.mAl);
}
@ -536,7 +571,7 @@ namespace glTF2 {
inline void Write(Value& obj, Mesh& m, AssetWriter& w)
{
/****************** Primitives *******************/
/****************** Primitives *******************/
Value primitives;
primitives.SetArray();
primitives.Reserve(unsigned(m.primitives.size()), w.mAl);
@ -620,6 +655,44 @@ namespace glTF2 {
}
}
inline void WriteExtrasValue(Value &parent, const CustomExtension &value, AssetWriter &w) {
Value valueNode;
if (value.mStringValue.isPresent) {
MakeValue(valueNode, value.mStringValue.value.c_str(), w.mAl);
} else if (value.mDoubleValue.isPresent) {
MakeValue(valueNode, value.mDoubleValue.value, w.mAl);
} else if (value.mUint64Value.isPresent) {
MakeValue(valueNode, value.mUint64Value.value, w.mAl);
} else if (value.mInt64Value.isPresent) {
MakeValue(valueNode, value.mInt64Value.value, w.mAl);
} else if (value.mBoolValue.isPresent) {
MakeValue(valueNode, value.mBoolValue.value, w.mAl);
} else if (value.mValues.isPresent) {
valueNode.SetObject();
for (auto const &subvalue : value.mValues.value) {
WriteExtrasValue(valueNode, subvalue, w);
}
}
parent.AddMember(StringRef(value.name), valueNode, w.mAl);
}
inline void WriteExtras(Value &obj, const Extras &extras, AssetWriter &w) {
if (!extras.HasExtras()) {
return;
}
Value extrasNode;
extrasNode.SetObject();
for (auto const &value : extras.mValues) {
WriteExtrasValue(extrasNode, value, w);
}
obj.AddMember("extras", extrasNode, w.mAl);
}
inline void Write(Value& obj, Node& n, AssetWriter& w)
{
if (n.matrix.isPresent) {
@ -655,6 +728,8 @@ namespace glTF2 {
if(n.skeletons.size()) {
AddRefsVector(obj, "skeletons", n.skeletons, w.mAl);
}
WriteExtras(obj, n.extras, w);
}
inline void Write(Value& /*obj*/, Program& /*b*/, AssetWriter& /*w*/)
@ -728,7 +803,6 @@ namespace glTF2 {
}
}
inline AssetWriter::AssetWriter(Asset& a)
: mDoc()
, mAsset(a)
@ -758,7 +832,7 @@ namespace glTF2 {
{
std::unique_ptr<IOStream> jsonOutFile(mAsset.OpenFile(path, "wt", true));
if (jsonOutFile == 0) {
if (jsonOutFile == nullptr) {
throw DeadlyExportError("Could not open output file: " + std::string(path));
}
@ -781,7 +855,7 @@ namespace glTF2 {
std::unique_ptr<IOStream> binOutFile(mAsset.OpenFile(binPath, "wb", true));
if (binOutFile == 0) {
if (binOutFile == nullptr) {
throw DeadlyExportError("Could not open output file: " + binPath);
}
@ -797,7 +871,7 @@ namespace glTF2 {
{
std::unique_ptr<IOStream> outfile(mAsset.OpenFile(path, "wb", true));
if (outfile == 0) {
if (outfile == nullptr) {
throw DeadlyExportError("Could not open output file: " + std::string(path));
}
@ -822,7 +896,7 @@ namespace glTF2 {
throw DeadlyExportError("Failed to write scene data!");
}
uint32_t jsonChunkLength = (docBuffer.GetSize() + 3) & ~3; // Round up to next multiple of 4
uint32_t jsonChunkLength = static_cast<uint32_t>((docBuffer.GetSize() + 3) & ~3); // Round up to next multiple of 4
auto paddingLength = jsonChunkLength - docBuffer.GetSize();
GLB_Chunk jsonChunk;
@ -848,7 +922,7 @@ namespace glTF2 {
int GLB_Chunk_count = 1;
uint32_t binaryChunkLength = 0;
if (bodyBuffer->byteLength > 0) {
binaryChunkLength = (bodyBuffer->byteLength + 3) & ~3; // Round up to next multiple of 4
binaryChunkLength = static_cast<uint32_t>((bodyBuffer->byteLength + 3) & ~3); // Round up to next multiple of 4
auto curPaddingLength = binaryChunkLength - bodyBuffer->byteLength;
++GLB_Chunk_count;
@ -866,7 +940,7 @@ namespace glTF2 {
if (outfile->Write(bodyBuffer->GetPointer(), 1, bodyBuffer->byteLength) != bodyBuffer->byteLength) {
throw DeadlyExportError("Failed to write body data!");
}
if (curPaddingLength && outfile->Write(&padding, 1, paddingLength) != paddingLength) {
if (curPaddingLength && outfile->Write(&padding, 1, curPaddingLength) != curPaddingLength) {
throw DeadlyExportError("Failed to write body data padding!");
}
}
@ -915,6 +989,10 @@ namespace glTF2 {
exts.PushBack(StringRef("KHR_materials_unlit"), mAl);
}
if (this->mAsset.extensionsUsed.KHR_materials_specular) {
exts.PushBack(StringRef("KHR_materials_specular"), mAl);
}
if (this->mAsset.extensionsUsed.KHR_materials_sheen) {
exts.PushBack(StringRef("KHR_materials_sheen"), mAl);
}
@ -935,6 +1013,10 @@ namespace glTF2 {
exts.PushBack(StringRef("KHR_materials_ior"), mAl);
}
if (this->mAsset.extensionsUsed.KHR_materials_emissive_strength) {
exts.PushBack(StringRef("KHR_materials_emissive_strength"), mAl);
}
if (this->mAsset.extensionsUsed.FB_ngon_encoding) {
exts.PushBack(StringRef("FB_ngon_encoding"), mAl);
}
@ -962,7 +1044,7 @@ namespace glTF2 {
if (d.mObjs.empty()) return;
Value* container = &mDoc;
const char* context = "Document";
const char* context = "Document";
if (d.mExtId) {
Value* exts = FindObject(mDoc, "extensions");

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -55,11 +55,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/version.h>
#include <assimp/Exporter.hpp>
#include <assimp/IOSystem.hpp>
#include <assimp/config.h>
// Header files, standard library.
#include <cinttypes>
#include <limits>
#include <memory>
#include <iostream>
using namespace rapidjson;
@ -90,6 +92,10 @@ glTF2Exporter::glTF2Exporter(const char *filename, IOSystem *pIOSystem, const ai
// Always on as our triangulation process is aware of this type of encoding
mAsset->extensionsUsed.FB_ngon_encoding = true;
configEpsilon = mProperties->GetPropertyFloat(
AI_CONFIG_CHECK_IDENTITY_MATRIX_EPSILON,
(ai_real)AI_CONFIG_CHECK_IDENTITY_MATRIX_EPSILON_DEFAULT);
if (isBinary) {
mAsset->SetAsBinary();
}
@ -172,22 +178,6 @@ static void IdentityMatrix4(mat4 &o) {
o[15] = 1;
}
static bool IsBoneWeightFitted(vec4 &weight) {
return weight[0] + weight[1] + weight[2] + weight[3] >= 1.f;
}
static int FitBoneWeight(vec4 &weight, float value) {
int i = 0;
for (; i < 4; ++i) {
if (weight[i] < value) {
weight[i] = value;
return i;
}
}
return -1;
}
template <typename T>
void SetAccessorRange(Ref<Accessor> acc, void *data, size_t count,
unsigned int numCompsIn, unsigned int numCompsOut) {
@ -263,7 +253,7 @@ size_t NZDiff(void *data, void *dataBase, size_t count, unsigned int numCompsIn,
for (short idx = 0; bufferData_ptr < bufferData_end; idx += 1, bufferData_ptr += numCompsIn) {
bool bNonZero = false;
//for the data, check any component Non Zero
// for the data, check any component Non Zero
for (unsigned int j = 0; j < numCompsOut; j++) {
double valueData = bufferData_ptr[j];
double valueBase = bufferBase_ptr ? bufferBase_ptr[j] : 0;
@ -273,11 +263,11 @@ size_t NZDiff(void *data, void *dataBase, size_t count, unsigned int numCompsIn,
}
}
//all zeros, continue
// all zeros, continue
if (!bNonZero)
continue;
//non zero, store the data
// non zero, store the data
for (unsigned int j = 0; j < numCompsOut; j++) {
T valueData = bufferData_ptr[j];
T valueBase = bufferBase_ptr ? bufferBase_ptr[j] : 0;
@ -286,14 +276,14 @@ size_t NZDiff(void *data, void *dataBase, size_t count, unsigned int numCompsIn,
vNZIdx.push_back(idx);
}
//avoid all-0, put 1 item
// avoid all-0, put 1 item
if (vNZDiff.size() == 0) {
for (unsigned int j = 0; j < numCompsOut; j++)
vNZDiff.push_back(0);
vNZIdx.push_back(0);
}
//process data
// process data
outputNZDiff = new T[vNZDiff.size()];
memcpy(outputNZDiff, vNZDiff.data(), vNZDiff.size() * sizeof(T));
@ -321,7 +311,7 @@ inline size_t NZDiff(ComponentType compType, void *data, void *dataBase, size_t
}
inline Ref<Accessor> ExportDataSparse(Asset &a, std::string &meshName, Ref<Buffer> &buffer,
size_t count, void *data, AttribType::Value typeIn, AttribType::Value typeOut, ComponentType compType, BufferViewTarget target = BufferViewTarget_NONE, void *dataBase = 0) {
size_t count, void *data, AttribType::Value typeIn, AttribType::Value typeOut, ComponentType compType, BufferViewTarget target = BufferViewTarget_NONE, void *dataBase = nullptr) {
if (!count || !data) {
return Ref<Accessor>();
}
@ -356,12 +346,12 @@ inline Ref<Accessor> ExportDataSparse(Asset &a, std::string &meshName, Ref<Buffe
acc->type = typeOut;
if (data) {
void *nzDiff = 0, *nzIdx = 0;
void *nzDiff = nullptr, *nzIdx = nullptr;
size_t nzCount = NZDiff(compType, data, dataBase, count, numCompsIn, numCompsOut, nzDiff, nzIdx);
acc->sparse.reset(new Accessor::Sparse);
acc->sparse->count = nzCount;
//indices
// indices
unsigned int bytesPerIdx = sizeof(unsigned short);
size_t indices_offset = buffer->byteLength;
size_t indices_padding = indices_offset % bytesPerIdx;
@ -379,7 +369,7 @@ inline Ref<Accessor> ExportDataSparse(Asset &a, std::string &meshName, Ref<Buffe
acc->sparse->indicesByteOffset = 0;
acc->WriteSparseIndices(nzCount, nzIdx, 1 * bytesPerIdx);
//values
// values
size_t values_offset = buffer->byteLength;
size_t values_padding = values_offset % bytesPerComp;
values_offset += values_padding;
@ -395,9 +385,9 @@ inline Ref<Accessor> ExportDataSparse(Asset &a, std::string &meshName, Ref<Buffe
acc->sparse->valuesByteOffset = 0;
acc->WriteSparseValues(nzCount, nzDiff, numCompsIn * bytesPerComp);
//clear
delete[](char *) nzDiff;
delete[](char *) nzIdx;
// clear
delete[] (char *)nzDiff;
delete[] (char *)nzIdx;
}
return acc;
}
@ -443,6 +433,61 @@ inline Ref<Accessor> ExportData(Asset &a, std::string &meshName, Ref<Buffer> &bu
return acc;
}
inline void ExportNodeExtras(const aiMetadataEntry &metadataEntry, aiString name, CustomExtension &value) {
value.name = name.C_Str();
switch (metadataEntry.mType) {
case AI_BOOL:
value.mBoolValue.value = *static_cast<bool *>(metadataEntry.mData);
value.mBoolValue.isPresent = true;
break;
case AI_INT32:
value.mInt64Value.value = *static_cast<int32_t *>(metadataEntry.mData);
value.mInt64Value.isPresent = true;
break;
case AI_UINT64:
value.mUint64Value.value = *static_cast<uint64_t *>(metadataEntry.mData);
value.mUint64Value.isPresent = true;
break;
case AI_FLOAT:
value.mDoubleValue.value = *static_cast<float *>(metadataEntry.mData);
value.mDoubleValue.isPresent = true;
break;
case AI_DOUBLE:
value.mDoubleValue.value = *static_cast<double *>(metadataEntry.mData);
value.mDoubleValue.isPresent = true;
break;
case AI_AISTRING:
value.mStringValue.value = static_cast<aiString *>(metadataEntry.mData)->C_Str();
value.mStringValue.isPresent = true;
break;
case AI_AIMETADATA: {
const aiMetadata *subMetadata = static_cast<aiMetadata *>(metadataEntry.mData);
value.mValues.value.resize(subMetadata->mNumProperties);
value.mValues.isPresent = true;
for (unsigned i = 0; i < subMetadata->mNumProperties; ++i) {
ExportNodeExtras(subMetadata->mValues[i], subMetadata->mKeys[i], value.mValues.value.at(i));
}
break;
}
default:
// AI_AIVECTOR3D not handled
break;
}
}
inline void ExportNodeExtras(const aiMetadata *metadata, Extras &extras) {
if (metadata == nullptr || metadata->mNumProperties == 0) {
return;
}
extras.mValues.resize(metadata->mNumProperties);
for (unsigned int i = 0; i < metadata->mNumProperties; ++i) {
ExportNodeExtras(metadata->mValues[i], metadata->mKeys[i], extras.mValues.at(i));
}
}
inline void SetSamplerWrap(SamplerWrap &wrap, aiTextureMapMode map) {
switch (map) {
case aiTextureMapMode_Clamp:
@ -516,11 +561,15 @@ void glTF2Exporter::GetMatTex(const aiMaterial &mat, Ref<Texture> &texture, unsi
if (mat.GetTextureCount(tt) == 0) {
return;
}
aiString tex;
// Read texcoord (UV map index)
mat.Get(AI_MATKEY_UVWSRC(tt, slot), texCoord);
// Note: must be an int to be successful.
int tmp = 0;
const auto ok = mat.Get(AI_MATKEY_UVWSRC(tt, slot), tmp);
if (ok == aiReturn_SUCCESS) texCoord = tmp;
if (mat.Get(AI_MATKEY_TEXTURE(tt, slot), tex) == AI_SUCCESS) {
std::string path = tex.C_Str();
@ -544,7 +593,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial &mat, Ref<Texture> &texture, unsi
if (curTex != nullptr) { // embedded
texture->source->name = curTex->mFilename.C_Str();
//basisu: embedded ktx2, bu
// basisu: embedded ktx2, bu
if (curTex->achFormatHint[0]) {
std::string mimeType = "image/";
if (memcmp(curTex->achFormatHint, "jpg", 3) == 0)
@ -564,7 +613,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial &mat, Ref<Texture> &texture, unsi
}
// The asset has its own buffer, see Image::SetData
//basisu: "image/ktx2", "image/basis" as is
// basisu: "image/ktx2", "image/basis" as is
texture->source->SetData(reinterpret_cast<uint8_t *>(curTex->pcData), curTex->mWidth, *mAsset);
} else {
texture->source->uri = path;
@ -574,7 +623,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial &mat, Ref<Texture> &texture, unsi
}
}
//basisu
// basisu
if (useBasisUniversal) {
mAsset->extensionsUsed.KHR_texture_basisu = true;
mAsset->extensionsRequired.KHR_texture_basisu = true;
@ -597,7 +646,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial &mat, NormalTextureInfo &prop, ai
GetMatTex(mat, texture, prop.texCoord, tt, slot);
if (texture) {
//GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot);
// GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot);
GetMatTexProp(mat, prop.scale, "scale", tt, slot);
}
}
@ -608,7 +657,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial &mat, OcclusionTextureInfo &prop,
GetMatTex(mat, texture, prop.texCoord, tt, slot);
if (texture) {
//GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot);
// GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot);
GetMatTexProp(mat, prop.strength, "strength", tt, slot);
}
}
@ -640,11 +689,10 @@ aiReturn glTF2Exporter::GetMatColor(const aiMaterial &mat, vec3 &prop, const cha
return result;
}
// This extension has been deprecated, only export with the specific flag enabled, defaults to false. Uses KHR_material_specular default.
bool glTF2Exporter::GetMatSpecGloss(const aiMaterial &mat, glTF2::PbrSpecularGlossiness &pbrSG) {
bool result = false;
// If has Glossiness, a Specular Color or Specular Texture, use the KHR_materials_pbrSpecularGlossiness extension
// NOTE: This extension is being considered for deprecation (Dec 2020), may be replaced by KHR_material_specular
if (mat.Get(AI_MATKEY_GLOSSINESS_FACTOR, pbrSG.glossinessFactor) == AI_SUCCESS) {
result = true;
} else {
@ -674,6 +722,25 @@ bool glTF2Exporter::GetMatSpecGloss(const aiMaterial &mat, glTF2::PbrSpecularGlo
return result;
}
bool glTF2Exporter::GetMatSpecular(const aiMaterial &mat, glTF2::MaterialSpecular &specular) {
// Specular requires either/or, default factors of zero disables specular, so do not export
if (GetMatColor(mat, specular.specularColorFactor, AI_MATKEY_COLOR_SPECULAR) != AI_SUCCESS && mat.Get(AI_MATKEY_SPECULAR_FACTOR, specular.specularFactor) != AI_SUCCESS) {
return false;
}
// The spec states that the default is 1.0 and [1.0, 1.0, 1.0]. We if both are 0, which should disable specular. Otherwise, if one is 0, set to 1.0
const bool colorFactorIsZero = specular.specularColorFactor[0] == defaultSpecularColorFactor[0] && specular.specularColorFactor[1] == defaultSpecularColorFactor[1] && specular.specularColorFactor[2] == defaultSpecularColorFactor[2];
if (specular.specularFactor == 0.0f && colorFactorIsZero) {
return false;
} else if (specular.specularFactor == 0.0f) {
specular.specularFactor = 1.0f;
} else if (colorFactorIsZero) {
specular.specularColorFactor[0] = specular.specularColorFactor[1] = specular.specularColorFactor[2] = 1.0f;
}
GetMatTex(mat, specular.specularTexture, aiTextureType_SPECULAR, 0);
GetMatTex(mat, specular.specularColorTexture, aiTextureType_SPECULAR, 1);
return true;
}
bool glTF2Exporter::GetMatSheen(const aiMaterial &mat, glTF2::MaterialSheen &sheen) {
// Return true if got any valid Sheen properties or textures
if (GetMatColor(mat, sheen.sheenColorFactor, AI_MATKEY_SHEEN_COLOR_FACTOR) != aiReturn_SUCCESS) {
@ -733,6 +800,10 @@ bool glTF2Exporter::GetMatIOR(const aiMaterial &mat, glTF2::MaterialIOR &ior) {
return mat.Get(AI_MATKEY_REFRACTI, ior.ior) == aiReturn_SUCCESS;
}
bool glTF2Exporter::GetMatEmissiveStrength(const aiMaterial &mat, glTF2::MaterialEmissiveStrength &emissiveStrength) {
return mat.Get(AI_MATKEY_EMISSIVE_INTENSITY, emissiveStrength.emissiveStrength) == aiReturn_SUCCESS;
}
void glTF2Exporter::ExportMaterials() {
aiString aiName;
for (unsigned int i = 0; i < mScene->mNumMaterials; ++i) {
@ -755,20 +826,30 @@ void glTF2Exporter::ExportMaterials() {
GetMatTex(mat, m->pbrMetallicRoughness.baseColorTexture, aiTextureType_BASE_COLOR);
if (!m->pbrMetallicRoughness.baseColorTexture.texture) {
//if there wasn't a baseColorTexture defined in the source, fallback to any diffuse texture
// if there wasn't a baseColorTexture defined in the source, fallback to any diffuse texture
GetMatTex(mat, m->pbrMetallicRoughness.baseColorTexture, aiTextureType_DIFFUSE);
}
GetMatTex(mat, m->pbrMetallicRoughness.metallicRoughnessTexture, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE);
GetMatTex(mat, m->pbrMetallicRoughness.metallicRoughnessTexture, aiTextureType_DIFFUSE_ROUGHNESS);
if (!m->pbrMetallicRoughness.metallicRoughnessTexture.texture) {
// if there wasn't a aiTextureType_DIFFUSE_ROUGHNESS defined in the source, fallback to aiTextureType_METALNESS
GetMatTex(mat, m->pbrMetallicRoughness.metallicRoughnessTexture, aiTextureType_METALNESS);
}
if (!m->pbrMetallicRoughness.metallicRoughnessTexture.texture) {
// if there still wasn't a aiTextureType_METALNESS defined in the source, fallback to AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE
GetMatTex(mat, m->pbrMetallicRoughness.metallicRoughnessTexture, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE);
}
if (GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_BASE_COLOR) != AI_SUCCESS) {
// if baseColorFactor wasn't defined, then the source is likely not a metallic roughness material.
//a fallback to any diffuse color should be used instead
// a fallback to any diffuse color should be used instead
GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_COLOR_DIFFUSE);
}
if (mat.Get(AI_MATKEY_METALLIC_FACTOR, m->pbrMetallicRoughness.metallicFactor) != AI_SUCCESS) {
//if metallicFactor wasn't defined, then the source is likely not a PBR file, and the metallicFactor should be 0
// if metallicFactor wasn't defined, then the source is likely not a PBR file, and the metallicFactor should be 0
m->pbrMetallicRoughness.metallicFactor = 0;
}
@ -781,10 +862,10 @@ void glTF2Exporter::ExportMaterials() {
if (mat.Get(AI_MATKEY_COLOR_SPECULAR, specularColor) == AI_SUCCESS && mat.Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS) {
// convert specular color to luminance
float specularIntensity = specularColor[0] * 0.2125f + specularColor[1] * 0.7154f + specularColor[2] * 0.0721f;
//normalize shininess (assuming max is 1000) with an inverse exponentional curve
// normalize shininess (assuming max is 1000) with an inverse exponentional curve
float normalizedShininess = std::sqrt(shininess / 1000);
//clamp the shininess value between 0 and 1
// clamp the shininess value between 0 and 1
normalizedShininess = std::min(std::max(normalizedShininess, 0.0f), 1.0f);
// low specular intensity values should produce a rough material even if shininess is high.
normalizedShininess = normalizedShininess * specularIntensity;
@ -814,9 +895,9 @@ void glTF2Exporter::ExportMaterials() {
m->alphaMode = alphaMode.C_Str();
}
{
// This extension has been deprecated, only export with the specific flag enabled, defaults to false. Uses KHR_material_specular default.
if (mProperties->GetPropertyBool(AI_CONFIG_USE_GLTF_PBR_SPECULAR_GLOSSINESS)) {
// KHR_materials_pbrSpecularGlossiness extension
// NOTE: This extension is being considered for deprecation (Dec 2020)
PbrSpecularGlossiness pbrSG;
if (GetMatSpecGloss(mat, pbrSG)) {
mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness = true;
@ -833,7 +914,13 @@ void glTF2Exporter::ExportMaterials() {
} else {
// These extensions are not compatible with KHR_materials_unlit or KHR_materials_pbrSpecularGlossiness
if (!m->pbrSpecularGlossiness.isPresent) {
// Sheen
MaterialSpecular specular;
if (GetMatSpecular(mat, specular)) {
mAsset->extensionsUsed.KHR_materials_specular = true;
m->materialSpecular = Nullable<MaterialSpecular>(specular);
GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_COLOR_DIFFUSE);
}
MaterialSheen sheen;
if (GetMatSheen(mat, sheen)) {
mAsset->extensionsUsed.KHR_materials_sheen = true;
@ -851,18 +938,24 @@ void glTF2Exporter::ExportMaterials() {
mAsset->extensionsUsed.KHR_materials_transmission = true;
m->materialTransmission = Nullable<MaterialTransmission>(transmission);
}
MaterialVolume volume;
if (GetMatVolume(mat, volume)) {
mAsset->extensionsUsed.KHR_materials_volume = true;
m->materialVolume = Nullable<MaterialVolume>(volume);
}
MaterialIOR ior;
if (GetMatIOR(mat, ior)) {
mAsset->extensionsUsed.KHR_materials_ior = true;
m->materialIOR = Nullable<MaterialIOR>(ior);
}
MaterialEmissiveStrength emissiveStrength;
if (GetMatEmissiveStrength(mat, emissiveStrength)) {
mAsset->extensionsUsed.KHR_materials_emissive_strength = true;
m->materialEmissiveStrength = Nullable<MaterialEmissiveStrength>(emissiveStrength);
}
}
}
}
@ -911,23 +1004,29 @@ Ref<Node> FindSkeletonRootJoint(Ref<Skin> &skinRef) {
return parentNodeRef;
}
void ExportSkin(Asset &mAsset, const aiMesh *aimesh, Ref<Mesh> &meshRef, Ref<Buffer> &bufferRef, Ref<Skin> &skinRef,
std::vector<aiMatrix4x4> &inverseBindMatricesData) {
struct boneIndexWeightPair {
unsigned int indexJoint;
float weight;
bool operator()(boneIndexWeightPair &a, boneIndexWeightPair &b) {
return a.weight > b.weight;
}
};
void ExportSkin(Asset &mAsset, const aiMesh *aimesh, Ref<Mesh> &meshRef, Ref<Buffer> &bufferRef, Ref<Skin> &skinRef,
std::vector<aiMatrix4x4> &inverseBindMatricesData, bool unlimitedBonesPerVertex) {
if (aimesh->mNumBones < 1) {
return;
}
// Store the vertex joint and weight data.
const size_t NumVerts(aimesh->mNumVertices);
vec4 *vertexJointData = new vec4[NumVerts];
vec4 *vertexWeightData = new vec4[NumVerts];
int *jointsPerVertex = new int[NumVerts];
std::vector<std::vector<boneIndexWeightPair>> allVerticesPairs;
int maxJointsPerVertex = 0;
for (size_t i = 0; i < NumVerts; ++i) {
jointsPerVertex[i] = 0;
for (size_t j = 0; j < 4; ++j) {
vertexJointData[i][j] = 0;
vertexWeightData[i][j] = 0;
}
std::vector<boneIndexWeightPair> vertexPair;
allVerticesPairs.push_back(vertexPair);
}
for (unsigned int idx_bone = 0; idx_bone < aimesh->mNumBones; ++idx_bone) {
@ -957,61 +1056,88 @@ void ExportSkin(Asset &mAsset, const aiMesh *aimesh, Ref<Mesh> &meshRef, Ref<Buf
jointNamesIndex = static_cast<unsigned int>(inverseBindMatricesData.size() - 1);
}
// aib->mWeights =====> vertexWeightData
for (unsigned int idx_weights = 0; idx_weights < aib->mNumWeights; ++idx_weights) {
// aib->mWeights =====> temp pairs data
for (unsigned int idx_weights = 0; idx_weights < aib->mNumWeights;
++idx_weights) {
unsigned int vertexId = aib->mWeights[idx_weights].mVertexId;
float vertWeight = aib->mWeights[idx_weights].mWeight;
// A vertex can only have at most four joint weights, which ideally sum up to 1
if (IsBoneWeightFitted(vertexWeightData[vertexId])) {
continue;
}
if (jointsPerVertex[vertexId] > 3) {
int boneIndexFitted = FitBoneWeight(vertexWeightData[vertexId], vertWeight);
if (boneIndexFitted != -1) {
vertexJointData[vertexId][boneIndexFitted] = static_cast<float>(jointNamesIndex);
}
}else {
vertexJointData[vertexId][jointsPerVertex[vertexId]] = static_cast<float>(jointNamesIndex);
vertexWeightData[vertexId][jointsPerVertex[vertexId]] = vertWeight;
jointsPerVertex[vertexId] += 1;
}
allVerticesPairs[vertexId].push_back({jointNamesIndex, vertWeight});
jointsPerVertex[vertexId] += 1;
maxJointsPerVertex =
std::max(maxJointsPerVertex, jointsPerVertex[vertexId]);
}
} // End: for-loop mNumMeshes
Mesh::Primitive &p = meshRef->primitives.back();
Ref<Accessor> vertexJointAccessor = ExportData(mAsset, skinRef->id, bufferRef, aimesh->mNumVertices,
vertexJointData, AttribType::VEC4, AttribType::VEC4, ComponentType_FLOAT);
if (vertexJointAccessor) {
size_t offset = vertexJointAccessor->bufferView->byteOffset;
size_t bytesLen = vertexJointAccessor->bufferView->byteLength;
unsigned int s_bytesPerComp = ComponentTypeSize(ComponentType_UNSIGNED_SHORT);
unsigned int bytesPerComp = ComponentTypeSize(vertexJointAccessor->componentType);
size_t s_bytesLen = bytesLen * s_bytesPerComp / bytesPerComp;
Ref<Buffer> buf = vertexJointAccessor->bufferView->buffer;
uint8_t *arrys = new uint8_t[bytesLen];
unsigned int i = 0;
for (unsigned int j = 0; j < bytesLen; j += bytesPerComp) {
size_t len_p = offset + j;
float f_value = *(float *)&buf->GetPointer()[len_p];
unsigned short c = static_cast<unsigned short>(f_value);
memcpy(&arrys[i * s_bytesPerComp], &c, s_bytesPerComp);
++i;
}
buf->ReplaceData_joint(offset, bytesLen, arrys, bytesLen);
vertexJointAccessor->componentType = ComponentType_UNSIGNED_SHORT;
vertexJointAccessor->bufferView->byteLength = s_bytesLen;
p.attributes.joint.push_back(vertexJointAccessor);
delete[] arrys;
if (!unlimitedBonesPerVertex){
// skinning limited only for 4 bones per vertex, default
maxJointsPerVertex = 4;
}
Ref<Accessor> vertexWeightAccessor = ExportData(mAsset, skinRef->id, bufferRef, aimesh->mNumVertices,
vertexWeightData, AttribType::VEC4, AttribType::VEC4, ComponentType_FLOAT);
if (vertexWeightAccessor) {
p.attributes.weight.push_back(vertexWeightAccessor);
// temp pairs data =====> vertexWeightData
size_t numGroups = (maxJointsPerVertex - 1) / 4 + 1;
vec4 *vertexJointData = new vec4[NumVerts * numGroups];
vec4 *vertexWeightData = new vec4[NumVerts * numGroups];
for (size_t indexVertex = 0; indexVertex < NumVerts; ++indexVertex) {
// order pairs by weight for each vertex
std::sort(allVerticesPairs[indexVertex].begin(),
allVerticesPairs[indexVertex].end(),
boneIndexWeightPair());
for (size_t indexGroup = 0; indexGroup < numGroups; ++indexGroup) {
for (size_t indexJoint = 0; indexJoint < 4; ++indexJoint) {
size_t indexBone = indexGroup * 4 + indexJoint;
size_t indexData = indexVertex + NumVerts * indexGroup;
if (indexBone >= allVerticesPairs[indexVertex].size()) {
vertexJointData[indexData][indexJoint] = 0.f;
vertexWeightData[indexData][indexJoint] = 0.f;
} else {
vertexJointData[indexData][indexJoint] =
static_cast<float>(
allVerticesPairs[indexVertex][indexBone].indexJoint);
vertexWeightData[indexData][indexJoint] =
allVerticesPairs[indexVertex][indexBone].weight;
}
}
}
}
for (size_t idx_group = 0; idx_group < numGroups; ++idx_group) {
Mesh::Primitive &p = meshRef->primitives.back();
Ref<Accessor> vertexJointAccessor = ExportData(
mAsset, skinRef->id, bufferRef, aimesh->mNumVertices,
vertexJointData + idx_group * NumVerts,
AttribType::VEC4, AttribType::VEC4, ComponentType_FLOAT);
if (vertexJointAccessor) {
size_t offset = vertexJointAccessor->bufferView->byteOffset;
size_t bytesLen = vertexJointAccessor->bufferView->byteLength;
unsigned int s_bytesPerComp =
ComponentTypeSize(ComponentType_UNSIGNED_SHORT);
unsigned int bytesPerComp =
ComponentTypeSize(vertexJointAccessor->componentType);
size_t s_bytesLen = bytesLen * s_bytesPerComp / bytesPerComp;
Ref<Buffer> buf = vertexJointAccessor->bufferView->buffer;
uint8_t *arrys = new uint8_t[bytesLen];
unsigned int i = 0;
for (unsigned int j = 0; j < bytesLen; j += bytesPerComp) {
size_t len_p = offset + j;
float f_value = *(float *)&buf->GetPointer()[len_p];
unsigned short c = static_cast<unsigned short>(f_value);
memcpy(&arrys[i * s_bytesPerComp], &c, s_bytesPerComp);
++i;
}
buf->ReplaceData_joint(offset, bytesLen, arrys, bytesLen);
vertexJointAccessor->componentType = ComponentType_UNSIGNED_SHORT;
vertexJointAccessor->bufferView->byteLength = s_bytesLen;
p.attributes.joint.push_back(vertexJointAccessor);
delete[] arrys;
}
Ref<Accessor> vertexWeightAccessor = ExportData(
mAsset, skinRef->id, bufferRef, aimesh->mNumVertices,
vertexWeightData + idx_group * NumVerts,
AttribType::VEC4, AttribType::VEC4, ComponentType_FLOAT);
if (vertexWeightAccessor) {
p.attributes.weight.push_back(vertexWeightAccessor);
}
}
delete[] jointsPerVertex;
delete[] vertexWeightData;
@ -1052,6 +1178,9 @@ void glTF2Exporter::ExportMeshes() {
for (unsigned int idx_mesh = 0; idx_mesh < mScene->mNumMeshes; ++idx_mesh) {
const aiMesh *aim = mScene->mMeshes[idx_mesh];
if (aim->mNumFaces == 0) {
continue;
}
std::string name = aim->mName.C_Str();
@ -1067,7 +1196,7 @@ void glTF2Exporter::ExportMeshes() {
/******************* Vertices ********************/
Ref<Accessor> v = ExportData(*mAsset, meshId, b, aim->mNumVertices, aim->mVertices, AttribType::VEC3,
AttribType::VEC3, ComponentType_FLOAT, BufferViewTarget_ARRAY_BUFFER);
AttribType::VEC3, ComponentType_FLOAT, BufferViewTarget_ARRAY_BUFFER);
if (v) {
p.attributes.position.push_back(v);
}
@ -1080,8 +1209,8 @@ void glTF2Exporter::ExportMeshes() {
}
}
Ref<Accessor> n = ExportData(*mAsset, meshId, b, aim->mNumVertices, aim->mNormals, AttribType::VEC3,
AttribType::VEC3, ComponentType_FLOAT, BufferViewTarget_ARRAY_BUFFER);
Ref<Accessor> n = ExportData(*mAsset, meshId, b, aim->mNumVertices, aim->mNormals, AttribType::VEC3,
AttribType::VEC3, ComponentType_FLOAT, BufferViewTarget_ARRAY_BUFFER);
if (n) {
p.attributes.normal.push_back(n);
}
@ -1102,8 +1231,8 @@ void glTF2Exporter::ExportMeshes() {
if (aim->mNumUVComponents[i] > 0) {
AttribType::Value type = (aim->mNumUVComponents[i] == 2) ? AttribType::VEC2 : AttribType::VEC3;
Ref<Accessor> tc = ExportData(*mAsset, meshId, b, aim->mNumVertices, aim->mTextureCoords[i],
AttribType::VEC3, type, ComponentType_FLOAT, BufferViewTarget_ARRAY_BUFFER);
Ref<Accessor> tc = ExportData(*mAsset, meshId, b, aim->mNumVertices, aim->mTextureCoords[i],
AttribType::VEC3, type, ComponentType_FLOAT, BufferViewTarget_ARRAY_BUFFER);
if (tc) {
p.attributes.texcoord.push_back(tc);
}
@ -1113,7 +1242,7 @@ void glTF2Exporter::ExportMeshes() {
/*************** Vertex colors ****************/
for (unsigned int indexColorChannel = 0; indexColorChannel < aim->GetNumColorChannels(); ++indexColorChannel) {
Ref<Accessor> c = ExportData(*mAsset, meshId, b, aim->mNumVertices, aim->mColors[indexColorChannel],
AttribType::VEC4, AttribType::VEC4, ComponentType_FLOAT, BufferViewTarget_ARRAY_BUFFER);
AttribType::VEC4, AttribType::VEC4, ComponentType_FLOAT, BufferViewTarget_ARRAY_BUFFER);
if (c) {
p.attributes.color.push_back(c);
}
@ -1130,8 +1259,8 @@ void glTF2Exporter::ExportMeshes() {
}
}
p.indices = ExportData(*mAsset, meshId, b, indices.size(), &indices[0], AttribType::SCALAR, AttribType::SCALAR,
ComponentType_UNSIGNED_INT, BufferViewTarget_ELEMENT_ARRAY_BUFFER);
p.indices = ExportData(*mAsset, meshId, b, indices.size(), &indices[0], AttribType::SCALAR, AttribType::SCALAR,
ComponentType_UNSIGNED_INT, BufferViewTarget_ELEMENT_ARRAY_BUFFER);
}
switch (aim->mPrimitiveTypes) {
@ -1149,9 +1278,19 @@ void glTF2Exporter::ExportMeshes() {
break;
}
// /*************** Skins ****************/
// if (aim->HasBones()) {
// ExportSkin(*mAsset, aim, m, b, skinRef, inverseBindMatricesData);
// }
/*************** Skins ****************/
if (aim->HasBones()) {
ExportSkin(*mAsset, aim, m, b, skinRef, inverseBindMatricesData);
bool unlimitedBonesPerVertex =
this->mProperties->HasPropertyBool(
AI_CONFIG_EXPORT_GLTF_UNLIMITED_SKINNING_BONES_PER_VERTEX) &&
this->mProperties->GetPropertyBool(
AI_CONFIG_EXPORT_GLTF_UNLIMITED_SKINNING_BONES_PER_VERTEX);
ExportSkin(*mAsset, aim, m, b, skinRef, inverseBindMatricesData,
unlimitedBonesPerVertex);
}
/*************** Targets for blendshapes ****************/
@ -1274,24 +1413,24 @@ void glTF2Exporter::MergeMeshes() {
unsigned int nMeshes = static_cast<unsigned int>(node->meshes.size());
//skip if it's 1 or less meshes per node
// skip if it's 1 or less meshes per node
if (nMeshes > 1) {
Ref<Mesh> firstMesh = node->meshes.at(0);
//loop backwards to allow easy removal of a mesh from a node once it's merged
// loop backwards to allow easy removal of a mesh from a node once it's merged
for (unsigned int m = nMeshes - 1; m >= 1; --m) {
Ref<Mesh> mesh = node->meshes.at(m);
//append this mesh's primitives to the first mesh's primitives
// append this mesh's primitives to the first mesh's primitives
firstMesh->primitives.insert(
firstMesh->primitives.end(),
mesh->primitives.begin(),
mesh->primitives.end());
//remove the mesh from the list of meshes
// remove the mesh from the list of meshes
unsigned int removedIndex = mAsset->meshes.Remove(mesh->id.c_str());
//find the presence of the removed mesh in other nodes
// find the presence of the removed mesh in other nodes
for (unsigned int nn = 0; nn < mAsset->nodes.Size(); ++nn) {
Ref<Node> curNode = mAsset->nodes.Get(nn);
@ -1310,7 +1449,7 @@ void glTF2Exporter::MergeMeshes() {
}
}
//since we were looping backwards, reverse the order of merged primitives to their original order
// since we were looping backwards, reverse the order of merged primitives to their original order
std::reverse(firstMesh->primitives.begin() + 1, firstMesh->primitives.end());
}
}
@ -1325,7 +1464,7 @@ unsigned int glTF2Exporter::ExportNodeHierarchy(const aiNode *n) {
node->name = n->mName.C_Str();
if (!n->mTransformation.IsIdentity()) {
if (!n->mTransformation.IsIdentity(configEpsilon)) {
node->matrix.isPresent = true;
CopyValue(n->mTransformation, node->matrix.value);
}
@ -1353,7 +1492,9 @@ unsigned int glTF2Exporter::ExportNode(const aiNode *n, Ref<Node> &parent) {
node->parent = parent;
node->name = name;
if (!n->mTransformation.IsIdentity()) {
ExportNodeExtras(n->mMetaData, node->extras);
if (!n->mTransformation.IsIdentity(configEpsilon)) {
if (mScene->mNumAnimations > 0 || (mProperties && mProperties->HasPropertyBool("GLTF2_NODE_IN_TRS"))) {
aiQuaternion quaternion;
n->mTransformation.Decompose(*reinterpret_cast<aiVector3D *>(&node->scale.value), quaternion, *reinterpret_cast<aiVector3D *>(&node->translation.value));
@ -1435,9 +1576,9 @@ inline void ExtractTranslationSampler(Asset &asset, std::string &animId, Ref<Buf
const aiVectorKey &key = nodeChannel->mPositionKeys[i];
// mTime is measured in ticks, but GLTF time is measured in seconds, so convert.
times[i] = static_cast<float>(key.mTime / ticksPerSecond);
values[(i * 3) + 0] = (ai_real) key.mValue.x;
values[(i * 3) + 1] = (ai_real) key.mValue.y;
values[(i * 3) + 2] = (ai_real) key.mValue.z;
values[(i * 3) + 0] = (ai_real)key.mValue.x;
values[(i * 3) + 1] = (ai_real)key.mValue.y;
values[(i * 3) + 2] = (ai_real)key.mValue.z;
}
sampler.input = GetSamplerInputRef(asset, animId, buffer, times);
@ -1454,9 +1595,9 @@ inline void ExtractScaleSampler(Asset &asset, std::string &animId, Ref<Buffer> &
const aiVectorKey &key = nodeChannel->mScalingKeys[i];
// mTime is measured in ticks, but GLTF time is measured in seconds, so convert.
times[i] = static_cast<float>(key.mTime / ticksPerSecond);
values[(i * 3) + 0] = (ai_real) key.mValue.x;
values[(i * 3) + 1] = (ai_real) key.mValue.y;
values[(i * 3) + 2] = (ai_real) key.mValue.z;
values[(i * 3) + 0] = (ai_real)key.mValue.x;
values[(i * 3) + 1] = (ai_real)key.mValue.y;
values[(i * 3) + 2] = (ai_real)key.mValue.z;
}
sampler.input = GetSamplerInputRef(asset, animId, buffer, times);
@ -1473,10 +1614,10 @@ inline void ExtractRotationSampler(Asset &asset, std::string &animId, Ref<Buffer
const aiQuatKey &key = nodeChannel->mRotationKeys[i];
// mTime is measured in ticks, but GLTF time is measured in seconds, so convert.
times[i] = static_cast<float>(key.mTime / ticksPerSecond);
values[(i * 4) + 0] = (ai_real) key.mValue.x;
values[(i * 4) + 1] = (ai_real) key.mValue.y;
values[(i * 4) + 2] = (ai_real) key.mValue.z;
values[(i * 4) + 3] = (ai_real) key.mValue.w;
values[(i * 4) + 0] = (ai_real)key.mValue.x;
values[(i * 4) + 1] = (ai_real)key.mValue.y;
values[(i * 4) + 2] = (ai_real)key.mValue.z;
values[(i * 4) + 3] = (ai_real)key.mValue.w;
}
sampler.input = GetSamplerInputRef(asset, animId, buffer, times);

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -49,6 +49,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/material.h>
#include <assimp/types.h>
#include <assimp/defs.h>
#include <map>
#include <memory>
@ -76,11 +77,13 @@ struct OcclusionTextureInfo;
struct Node;
struct Texture;
struct PbrSpecularGlossiness;
struct MaterialSpecular;
struct MaterialSheen;
struct MaterialClearcoat;
struct MaterialTransmission;
struct MaterialVolume;
struct MaterialIOR;
struct MaterialEmissiveStrength;
// Vec/matrix types, as raw float arrays
typedef float(vec2)[2];
@ -116,11 +119,13 @@ protected:
aiReturn GetMatColor(const aiMaterial &mat, glTF2::vec4 &prop, const char *propName, int type, int idx) const;
aiReturn GetMatColor(const aiMaterial &mat, glTF2::vec3 &prop, const char *propName, int type, int idx) const;
bool GetMatSpecGloss(const aiMaterial &mat, glTF2::PbrSpecularGlossiness &pbrSG);
bool GetMatSpecular(const aiMaterial &mat, glTF2::MaterialSpecular &specular);
bool GetMatSheen(const aiMaterial &mat, glTF2::MaterialSheen &sheen);
bool GetMatClearcoat(const aiMaterial &mat, glTF2::MaterialClearcoat &clearcoat);
bool GetMatTransmission(const aiMaterial &mat, glTF2::MaterialTransmission &transmission);
bool GetMatVolume(const aiMaterial &mat, glTF2::MaterialVolume &volume);
bool GetMatIOR(const aiMaterial &mat, glTF2::MaterialIOR &ior);
bool GetMatEmissiveStrength(const aiMaterial &mat, glTF2::MaterialEmissiveStrength &emissiveStrength);
void ExportMetadata();
void ExportMaterials();
void ExportMeshes();
@ -138,6 +143,7 @@ private:
std::map<std::string, unsigned int> mTexturesByPath;
std::shared_ptr<glTF2::Asset> mAsset;
std::vector<unsigned char> mBodyData;
ai_real configEpsilon;
};
} // namespace Assimp

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -82,7 +82,7 @@ struct Tangent {
// glTF2Importer
//
static const aiImporterDesc desc = {
static constexpr aiImporterDesc desc = {
"glTF2 Importer",
"",
"",
@ -92,32 +92,31 @@ static const aiImporterDesc desc = {
0,
0,
0,
"gltf glb"
"gltf glb vrm"
};
glTF2Importer::glTF2Importer() :
BaseImporter(),
meshOffsets(),
mEmbeddedTexIdxs(),
mScene(nullptr) {
// empty
}
glTF2Importer::~glTF2Importer() = default;
const aiImporterDesc *glTF2Importer::GetInfo() const {
return &desc;
}
bool glTF2Importer::CanRead(const std::string &filename, IOSystem *pIOHandler, bool checkSig) const {
const std::string extension = GetExtension(filename);
if (!checkSig && (extension != "gltf") && (extension != "glb")) {
if (!checkSig && (extension != "gltf") && (extension != "glb") && (extension != "vrm")) {
return false;
}
if (pIOHandler) {
glTF2::Asset asset(pIOHandler);
return asset.CanRead(filename, extension == "glb");
return asset.CanRead(
filename,
CheckMagicToken(
pIOHandler, filename, AI_GLB_MAGIC_NUMBER, 1, 0,
static_cast<unsigned int>(strlen(AI_GLB_MAGIC_NUMBER))));
}
return false;
@ -162,7 +161,7 @@ static void SetMaterialTextureProperty(std::vector<int> &embeddedTexIdxs, Asset
if (texIdx != -1) { // embedded
// setup texture reference string (copied from ColladaLoader::FindFilenameForEffectTexture)
uri.data[0] = '*';
uri.length = 1 + ASSIMP_itoa10(uri.data + 1, MAXLEN - 1, texIdx);
uri.length = 1 + ASSIMP_itoa10(uri.data + 1, AI_MAXLEN - 1, texIdx);
}
mat->AddProperty(&uri, AI_MATKEY_TEXTURE(texType, texSlot));
@ -185,7 +184,6 @@ static void SetMaterialTextureProperty(std::vector<int> &embeddedTexIdxs, Asset
const ai_real rsin(sin(-transform.mRotation));
transform.mTranslation.x = (static_cast<ai_real>(0.5) * transform.mScaling.x) * (-rcos + rsin + 1) + prop.TextureTransformExt_t.offset[0];
transform.mTranslation.y = ((static_cast<ai_real>(0.5) * transform.mScaling.y) * (rsin + rcos - 1)) + 1 - transform.mScaling.y - prop.TextureTransformExt_t.offset[1];
;
mat->AddProperty(&transform, 1, _AI_MATKEY_UVTRANSFORM_BASE, texType, texSlot);
}
@ -236,7 +234,8 @@ inline void SetMaterialTextureProperty(std::vector<int> &embeddedTexIdxs, Asset
SetMaterialTextureProperty(embeddedTexIdxs, r, (glTF2::TextureInfo)prop, mat, texType, texSlot);
if (prop.texture && prop.texture->source) {
mat->AddProperty(&prop.strength, 1, AI_MATKEY_GLTF_TEXTURE_STRENGTH(texType, texSlot));
std::string textureStrengthKey = std::string(_AI_MATKEY_TEXTURE_BASE) + "." + "strength";
mat->AddProperty(&prop.strength, 1, textureStrengthKey.c_str(), texType, texSlot);
}
}
@ -282,8 +281,19 @@ static aiMaterial *ImportMaterial(std::vector<int> &embeddedTexIdxs, Asset &r, M
aimat->AddProperty(&alphaMode, AI_MATKEY_GLTF_ALPHAMODE);
aimat->AddProperty(&mat.alphaCutoff, 1, AI_MATKEY_GLTF_ALPHACUTOFF);
// KHR_materials_specular
if (mat.materialSpecular.isPresent) {
MaterialSpecular &specular = mat.materialSpecular.value;
// Default values of zero disables Specular
if (std::memcmp(specular.specularColorFactor, defaultSpecularColorFactor, sizeof(glTFCommon::vec3)) != 0 || specular.specularFactor != 0.0f) {
SetMaterialColorProperty(r, specular.specularColorFactor, aimat, AI_MATKEY_COLOR_SPECULAR);
aimat->AddProperty(&specular.specularFactor, 1, AI_MATKEY_SPECULAR_FACTOR);
SetMaterialTextureProperty(embeddedTexIdxs, r, specular.specularTexture, aimat, aiTextureType_SPECULAR, 0);
SetMaterialTextureProperty(embeddedTexIdxs, r, specular.specularColorTexture, aimat, aiTextureType_SPECULAR, 1);
}
}
// pbrSpecularGlossiness
if (mat.pbrSpecularGlossiness.isPresent) {
else if (mat.pbrSpecularGlossiness.isPresent) {
PbrSpecularGlossiness &pbrSG = mat.pbrSpecularGlossiness.value;
SetMaterialColorProperty(r, pbrSG.diffuseFactor, aimat, AI_MATKEY_COLOR_DIFFUSE);
@ -357,6 +367,13 @@ static aiMaterial *ImportMaterial(std::vector<int> &embeddedTexIdxs, Asset &r, M
aimat->AddProperty(&ior.ior, 1, AI_MATKEY_REFRACTI);
}
// KHR_materials_emissive_strength
if (mat.materialEmissiveStrength.isPresent) {
MaterialEmissiveStrength &emissiveStrength = mat.materialEmissiveStrength.value;
aimat->AddProperty(&emissiveStrength.emissiveStrength, 1, AI_MATKEY_EMISSIVE_INTENSITY);
}
return aimat;
} catch (...) {
delete aimat;
@ -429,10 +446,10 @@ static inline bool CheckValidFacesIndices(aiFace *faces, unsigned nFaces, unsign
#endif // ASSIMP_BUILD_DEBUG
template <typename T>
aiColor4D *GetVertexColorsForType(Ref<Accessor> input) {
aiColor4D *GetVertexColorsForType(Ref<Accessor> input, std::vector<unsigned int> *vertexRemappingTable) {
constexpr float max = std::numeric_limits<T>::max();
aiColor4t<T> *colors;
input->ExtractData(colors);
input->ExtractData(colors, vertexRemappingTable);
auto output = new aiColor4D[input->count];
for (size_t i = 0; i < input->count; i++) {
output[i] = aiColor4D(
@ -447,18 +464,73 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
ASSIMP_LOG_DEBUG("Importing ", r.meshes.Size(), " meshes");
std::vector<std::unique_ptr<aiMesh>> meshes;
unsigned int k = 0;
meshOffsets.clear();
meshOffsets.reserve(r.meshes.Size() + 1);
mVertexRemappingTables.clear();
// Count the number of aiMeshes
unsigned int num_aiMeshes = 0;
for (unsigned int m = 0; m < r.meshes.Size(); ++m) {
meshOffsets.push_back(num_aiMeshes);
num_aiMeshes += unsigned(r.meshes[m].primitives.size());
}
meshOffsets.push_back(num_aiMeshes); // add a last element so we can always do meshOffsets[n+1] - meshOffsets[n]
std::vector<unsigned int> reverseMappingIndices;
std::vector<unsigned int> indexBuffer;
meshes.reserve(num_aiMeshes);
mVertexRemappingTables.resize(num_aiMeshes);
for (unsigned int m = 0; m < r.meshes.Size(); ++m) {
Mesh &mesh = r.meshes[m];
meshOffsets.push_back(k);
k += unsigned(mesh.primitives.size());
for (unsigned int p = 0; p < mesh.primitives.size(); ++p) {
Mesh::Primitive &prim = mesh.primitives[p];
Mesh::Primitive::Attributes &attr = prim.attributes;
// Find out the maximum number of vertices:
size_t numAllVertices = 0;
if (!attr.position.empty() && attr.position[0]) {
numAllVertices = attr.position[0]->count;
}
// Extract used vertices:
bool useIndexBuffer = prim.indices;
std::vector<unsigned int> *vertexRemappingTable = nullptr;
if (useIndexBuffer) {
size_t count = prim.indices->count;
indexBuffer.resize(count);
reverseMappingIndices.clear();
vertexRemappingTable = &mVertexRemappingTables[meshes.size()];
vertexRemappingTable->reserve(count / 3); // this is a very rough heuristic to reduce re-allocations
Accessor::Indexer data = prim.indices->GetIndexer();
if (!data.IsValid()) {
throw DeadlyImportError("GLTF: Invalid accessor without data in mesh ", getContextForErrorMessages(mesh.id, mesh.name));
}
// Build the vertex remapping table and the modified index buffer (used later instead of the original one)
// In case no index buffer is used, the original vertex arrays are being used so no remapping is required in the first place.
const unsigned int unusedIndex = ~0u;
for (unsigned int i = 0; i < count; ++i) {
unsigned int index = data.GetUInt(i);
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;
}
if (index >= reverseMappingIndices.size()) {
reverseMappingIndices.resize(index + 1, unusedIndex);
}
if (reverseMappingIndices[index] == unusedIndex) {
reverseMappingIndices[index] = static_cast<unsigned int>(vertexRemappingTable->size());
vertexRemappingTable->push_back(index);
}
indexBuffer[i] = reverseMappingIndices[index];
}
}
aiMesh *aim = new aiMesh();
meshes.push_back(std::unique_ptr<aiMesh>(aim));
@ -467,7 +539,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
if (mesh.primitives.size() > 1) {
ai_uint32 &len = aim->mName.length;
aim->mName.data[len] = '-';
len += 1 + ASSIMP_itoa10(aim->mName.data + len + 1, unsigned(MAXLEN - len - 1), p);
len += 1 + ASSIMP_itoa10(aim->mName.data + len + 1, unsigned(AI_MAXLEN - len - 1), p);
}
switch (prim.mode) {
@ -488,28 +560,25 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
break;
}
Mesh::Primitive::Attributes &attr = prim.attributes;
if (!attr.position.empty() && attr.position[0]) {
aim->mNumVertices = static_cast<unsigned int>(attr.position[0]->count);
attr.position[0]->ExtractData(aim->mVertices);
aim->mNumVertices = static_cast<unsigned int>(attr.position[0]->ExtractData(aim->mVertices, vertexRemappingTable));
}
if (!attr.normal.empty() && attr.normal[0]) {
if (attr.normal[0]->count != aim->mNumVertices) {
if (attr.normal[0]->count != numAllVertices) {
DefaultLogger::get()->warn("Normal count in mesh \"", mesh.name, "\" does not match the vertex count, normals ignored.");
} else {
attr.normal[0]->ExtractData(aim->mNormals);
attr.normal[0]->ExtractData(aim->mNormals, vertexRemappingTable);
// only extract tangents if normals are present
if (!attr.tangent.empty() && attr.tangent[0]) {
if (attr.tangent[0]->count != aim->mNumVertices) {
if (attr.tangent[0]->count != numAllVertices) {
DefaultLogger::get()->warn("Tangent count in mesh \"", mesh.name, "\" does not match the vertex count, tangents ignored.");
} else {
// generate bitangents from normals and tangents according to spec
Tangent *tangents = nullptr;
attr.tangent[0]->ExtractData(tangents);
attr.tangent[0]->ExtractData(tangents, vertexRemappingTable);
aim->mTangents = new aiVector3D[aim->mNumVertices];
aim->mBitangents = new aiVector3D[aim->mNumVertices];
@ -526,7 +595,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
}
for (size_t c = 0; c < attr.color.size() && c < AI_MAX_NUMBER_OF_COLOR_SETS; ++c) {
if (attr.color[c]->count != aim->mNumVertices) {
if (attr.color[c]->count != numAllVertices) {
DefaultLogger::get()->warn("Color stream size in mesh \"", mesh.name,
"\" does not match the vertex count");
continue;
@ -534,12 +603,12 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
auto componentType = attr.color[c]->componentType;
if (componentType == glTF2::ComponentType_FLOAT) {
attr.color[c]->ExtractData(aim->mColors[c]);
attr.color[c]->ExtractData(aim->mColors[c], vertexRemappingTable);
} else {
if (componentType == glTF2::ComponentType_UNSIGNED_BYTE) {
aim->mColors[c] = GetVertexColorsForType<unsigned char>(attr.color[c]);
aim->mColors[c] = GetVertexColorsForType<unsigned char>(attr.color[c], vertexRemappingTable);
} else if (componentType == glTF2::ComponentType_UNSIGNED_SHORT) {
aim->mColors[c] = GetVertexColorsForType<unsigned short>(attr.color[c]);
aim->mColors[c] = GetVertexColorsForType<unsigned short>(attr.color[c], vertexRemappingTable);
}
}
}
@ -549,13 +618,13 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
continue;
}
if (attr.texcoord[tc]->count != aim->mNumVertices) {
if (attr.texcoord[tc]->count != numAllVertices) {
DefaultLogger::get()->warn("Texcoord stream size in mesh \"", mesh.name,
"\" does not match the vertex count");
continue;
}
attr.texcoord[tc]->ExtractData(aim->mTextureCoords[tc]);
attr.texcoord[tc]->ExtractData(aim->mTextureCoords[tc], vertexRemappingTable);
aim->mNumUVComponents[tc] = attr.texcoord[tc]->GetNumComponents();
aiVector3D *values = aim->mTextureCoords[tc];
@ -580,11 +649,11 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
Mesh::Primitive::Target &target = targets[i];
if (needPositions) {
if (target.position[0]->count != aim->mNumVertices) {
if (target.position[0]->count != numAllVertices) {
ASSIMP_LOG_WARN("Positions of target ", i, " in mesh \"", mesh.name, "\" does not match the vertex count");
} else {
aiVector3D *positionDiff = nullptr;
target.position[0]->ExtractData(positionDiff);
target.position[0]->ExtractData(positionDiff, vertexRemappingTable);
for (unsigned int vertexId = 0; vertexId < aim->mNumVertices; vertexId++) {
aiAnimMesh.mVertices[vertexId] += positionDiff[vertexId];
}
@ -592,11 +661,11 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
}
}
if (needNormals) {
if (target.normal[0]->count != aim->mNumVertices) {
if (target.normal[0]->count != numAllVertices) {
ASSIMP_LOG_WARN("Normals of target ", i, " in mesh \"", mesh.name, "\" does not match the vertex count");
} else {
aiVector3D *normalDiff = nullptr;
target.normal[0]->ExtractData(normalDiff);
target.normal[0]->ExtractData(normalDiff, vertexRemappingTable);
for (unsigned int vertexId = 0; vertexId < aim->mNumVertices; vertexId++) {
aiAnimMesh.mNormals[vertexId] += normalDiff[vertexId];
}
@ -607,14 +676,14 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
if (!aiAnimMesh.HasNormals()) {
// prevent nullptr access to aiAnimMesh.mNormals below when no normals are available
ASSIMP_LOG_WARN("Bitangents of target ", i, " in mesh \"", mesh.name, "\" can't be computed, because mesh has no normals.");
} else if (target.tangent[0]->count != aim->mNumVertices) {
} else if (target.tangent[0]->count != numAllVertices) {
ASSIMP_LOG_WARN("Tangents of target ", i, " in mesh \"", mesh.name, "\" does not match the vertex count");
} else {
Tangent *tangent = nullptr;
attr.tangent[0]->ExtractData(tangent);
attr.tangent[0]->ExtractData(tangent, vertexRemappingTable);
aiVector3D *tangentDiff = nullptr;
target.tangent[0]->ExtractData(tangentDiff);
target.tangent[0]->ExtractData(tangentDiff, vertexRemappingTable);
for (unsigned int vertexId = 0; vertexId < aim->mNumVertices; ++vertexId) {
tangent[vertexId].xyz += tangentDiff[vertexId];
@ -638,20 +707,15 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
aiFace *facePtr = nullptr;
size_t nFaces = 0;
if (prim.indices) {
size_t count = prim.indices->count;
Accessor::Indexer data = prim.indices->GetIndexer();
if (!data.IsValid()) {
throw DeadlyImportError("GLTF: Invalid accessor without data in mesh ", getContextForErrorMessages(mesh.id, mesh.name));
}
if (useIndexBuffer) {
size_t count = indexBuffer.size();
switch (prim.mode) {
case PrimitiveMode_POINTS: {
nFaces = count;
facePtr = faces = new aiFace[nFaces];
for (unsigned int i = 0; i < count; ++i) {
SetFaceAndAdvance1(facePtr, aim->mNumVertices, data.GetUInt(i));
SetFaceAndAdvance1(facePtr, aim->mNumVertices, indexBuffer[i]);
}
break;
}
@ -664,7 +728,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
}
facePtr = faces = new aiFace[nFaces];
for (unsigned int i = 0; i < count; i += 2) {
SetFaceAndAdvance2(facePtr, aim->mNumVertices, data.GetUInt(i), data.GetUInt(i + 1));
SetFaceAndAdvance2(facePtr, aim->mNumVertices, indexBuffer[i], indexBuffer[i + 1]);
}
break;
}
@ -673,12 +737,12 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
case PrimitiveMode_LINE_STRIP: {
nFaces = count - ((prim.mode == PrimitiveMode_LINE_STRIP) ? 1 : 0);
facePtr = faces = new aiFace[nFaces];
SetFaceAndAdvance2(facePtr, aim->mNumVertices, data.GetUInt(0), data.GetUInt(1));
SetFaceAndAdvance2(facePtr, aim->mNumVertices, indexBuffer[0], indexBuffer[1]);
for (unsigned int i = 2; i < count; ++i) {
SetFaceAndAdvance2(facePtr, aim->mNumVertices, data.GetUInt(i - 1), data.GetUInt(i));
SetFaceAndAdvance2(facePtr, aim->mNumVertices, indexBuffer[i - 1], indexBuffer[i]);
}
if (prim.mode == PrimitiveMode_LINE_LOOP) { // close the loop
SetFaceAndAdvance2(facePtr, aim->mNumVertices, data.GetUInt(static_cast<int>(count) - 1), faces[0].mIndices[0]);
SetFaceAndAdvance2(facePtr, aim->mNumVertices, indexBuffer[static_cast<int>(count) - 1], faces[0].mIndices[0]);
}
break;
}
@ -691,7 +755,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
}
facePtr = faces = new aiFace[nFaces];
for (unsigned int i = 0; i < count; i += 3) {
SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(i), data.GetUInt(i + 1), data.GetUInt(i + 2));
SetFaceAndAdvance3(facePtr, aim->mNumVertices, indexBuffer[i], indexBuffer[i + 1], indexBuffer[i + 2]);
}
break;
}
@ -702,10 +766,10 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
// The ordering is to ensure that the triangles are all drawn with the same orientation
if ((i + 1) % 2 == 0) {
// For even n, vertices n + 1, n, and n + 2 define triangle n
SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(i + 1), data.GetUInt(i), data.GetUInt(i + 2));
SetFaceAndAdvance3(facePtr, aim->mNumVertices, indexBuffer[i + 1], indexBuffer[i], indexBuffer[i + 2]);
} else {
// For odd n, vertices n, n+1, and n+2 define triangle n
SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(i), data.GetUInt(i + 1), data.GetUInt(i + 2));
SetFaceAndAdvance3(facePtr, aim->mNumVertices, indexBuffer[i], indexBuffer[i + 1], indexBuffer[i + 2]);
}
}
break;
@ -713,9 +777,9 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
case PrimitiveMode_TRIANGLE_FAN:
nFaces = count - 2;
facePtr = faces = new aiFace[nFaces];
SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(0), data.GetUInt(1), data.GetUInt(2));
SetFaceAndAdvance3(facePtr, aim->mNumVertices, indexBuffer[0], indexBuffer[1], indexBuffer[2]);
for (unsigned int i = 1; i < nFaces; ++i) {
SetFaceAndAdvance3(facePtr, aim->mNumVertices, data.GetUInt(0), data.GetUInt(i + 1), data.GetUInt(i + 2));
SetFaceAndAdvance3(facePtr, aim->mNumVertices, indexBuffer[0], indexBuffer[i + 1], indexBuffer[i + 2]);
}
break;
}
@ -820,8 +884,6 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
}
}
meshOffsets.push_back(k);
CopyVector(meshes, mScene->mMeshes, mScene->mNumMeshes);
}
@ -846,7 +908,7 @@ void glTF2Importer::ImportCameras(glTF2::Asset &r) {
if (cam.type == Camera::Perspective) {
aicam->mAspect = cam.cameraProperties.perspective.aspectRatio;
aicam->mHorizontalFOV = cam.cameraProperties.perspective.yfov * ((aicam->mAspect == 0.f) ? 1.f : aicam->mAspect);
aicam->mHorizontalFOV = 2.0f * std::atan(std::tan(cam.cameraProperties.perspective.yfov * 0.5f) * ((aicam->mAspect == 0.f) ? 1.f : aicam->mAspect));
aicam->mClipPlaneFar = cam.cameraProperties.perspective.zfar;
aicam->mClipPlaneNear = cam.cameraProperties.perspective.znear;
} else {
@ -954,7 +1016,8 @@ static void GetNodeTransform(aiMatrix4x4 &matrix, const glTF2::Node &node) {
}
}
static void BuildVertexWeightMapping(Mesh::Primitive &primitive, std::vector<std::vector<aiVertexWeight>> &map) {
static void BuildVertexWeightMapping(Mesh::Primitive &primitive, std::vector<std::vector<aiVertexWeight>> &map, std::vector<unsigned int>* vertexRemappingTablePtr) {
Mesh::Primitive::Attributes &attr = primitive.attributes;
if (attr.weight.empty() || attr.joint.empty()) {
return;
@ -963,14 +1026,14 @@ static void BuildVertexWeightMapping(Mesh::Primitive &primitive, std::vector<std
return;
}
size_t num_vertices = attr.weight[0]->count;
size_t num_vertices = 0;
struct Weights {
float values[4];
};
Weights **weights = new Weights*[attr.weight.size()];
for (size_t w = 0; w < attr.weight.size(); ++w) {
attr.weight[w]->ExtractData(weights[w]);
num_vertices = attr.weight[w]->ExtractData(weights[w], vertexRemappingTablePtr);
}
struct Indices8 {
@ -984,12 +1047,12 @@ static void BuildVertexWeightMapping(Mesh::Primitive &primitive, std::vector<std
if (attr.joint[0]->GetElementSize() == 4) {
indices8 = new Indices8*[attr.joint.size()];
for (size_t j = 0; j < attr.joint.size(); ++j) {
attr.joint[j]->ExtractData(indices8[j]);
attr.joint[j]->ExtractData(indices8[j], vertexRemappingTablePtr);
}
} else {
indices16 = new Indices16 *[attr.joint.size()];
for (size_t j = 0; j < attr.joint.size(); ++j) {
attr.joint[j]->ExtractData(indices16[j]);
attr.joint[j]->ExtractData(indices16[j], vertexRemappingTablePtr);
}
}
//
@ -1048,15 +1111,13 @@ void ParseExtensions(aiMetadata *metadata, const CustomExtension &extension) {
}
}
void ParseExtras(aiMetadata *metadata, const CustomExtension &extension) {
if (extension.mValues.isPresent) {
for (auto const &subExtension : extension.mValues.value) {
ParseExtensions(metadata, subExtension);
}
void ParseExtras(aiMetadata* metadata, const Extras& extras) {
for (auto const &value : extras.mValues) {
ParseExtensions(metadata, value);
}
}
aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector<unsigned int> &meshOffsets, glTF2::Ref<glTF2::Node> &ptr) {
aiNode *glTF2Importer::ImportNode(glTF2::Asset &r, glTF2::Ref<glTF2::Node> &ptr) {
Node &node = *ptr;
aiNode *ainode = new aiNode(GetNodeName(node));
@ -1068,18 +1129,18 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector<unsigned int> &
std::fill(ainode->mChildren, ainode->mChildren + ainode->mNumChildren, nullptr);
for (unsigned int i = 0; i < ainode->mNumChildren; ++i) {
aiNode *child = ImportNode(pScene, r, meshOffsets, node.children[i]);
aiNode *child = ImportNode(r, node.children[i]);
child->mParent = ainode;
ainode->mChildren[i] = child;
}
}
if (node.customExtensions || node.extras) {
if (node.customExtensions || node.extras.HasExtras()) {
ainode->mMetaData = new aiMetadata;
if (node.customExtensions) {
ParseExtensions(ainode->mMetaData, node.customExtensions);
}
if (node.extras) {
if (node.extras.HasExtras()) {
ParseExtras(ainode->mMetaData, node.extras);
}
}
@ -1101,11 +1162,13 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector<unsigned int> &
if (node.skin) {
for (int primitiveNo = 0; primitiveNo < count; ++primitiveNo) {
aiMesh *mesh = pScene->mMeshes[meshOffsets[mesh_idx] + primitiveNo];
unsigned int aiMeshIdx = meshOffsets[mesh_idx] + primitiveNo;
aiMesh *mesh = mScene->mMeshes[aiMeshIdx];
unsigned int numBones = static_cast<unsigned int>(node.skin->jointNames.size());
std::vector<unsigned int> *vertexRemappingTablePtr = mVertexRemappingTables[aiMeshIdx].empty() ? nullptr : &mVertexRemappingTables[aiMeshIdx];
std::vector<std::vector<aiVertexWeight>> weighting(numBones);
BuildVertexWeightMapping(node.meshes[0]->primitives[primitiveNo], weighting);
BuildVertexWeightMapping(node.meshes[0]->primitives[primitiveNo], weighting, vertexRemappingTablePtr);
mesh->mNumBones = static_cast<unsigned int>(numBones);
mesh->mBones = new aiBone *[mesh->mNumBones];
@ -1122,7 +1185,7 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector<unsigned int> &
// mapping which makes things doubly-slow.
mat4 *pbindMatrices = nullptr;
node.skin->inverseBindMatrices->ExtractData(pbindMatrices);
node.skin->inverseBindMatrices->ExtractData(pbindMatrices, nullptr);
for (uint32_t i = 0; i < numBones; ++i) {
const std::vector<aiVertexWeight> &weights = weighting[i];
@ -1168,16 +1231,11 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector<unsigned int> &
}
if (node.camera) {
pScene->mCameras[node.camera.GetIndex()]->mName = ainode->mName;
if (node.translation.isPresent) {
aiVector3D trans;
CopyValue(node.translation.value, trans);
pScene->mCameras[node.camera.GetIndex()]->mPosition = trans;
}
mScene->mCameras[node.camera.GetIndex()]->mName = ainode->mName;
}
if (node.light) {
pScene->mLights[node.light.GetIndex()]->mName = ainode->mName;
mScene->mLights[node.light.GetIndex()]->mName = ainode->mName;
// range is optional - see https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual
// it is added to meta data of parent node, because there is no other place to put it
@ -1209,7 +1267,7 @@ void glTF2Importer::ImportNodes(glTF2::Asset &r) {
// The root nodes
unsigned int numRootNodes = unsigned(rootNodes.size());
if (numRootNodes == 1) { // a single root node: use it
mScene->mRootNode = ImportNode(mScene, r, meshOffsets, rootNodes[0]);
mScene->mRootNode = ImportNode(r, rootNodes[0]);
} else if (numRootNodes > 1) { // more than one root node: create a fake root
aiNode *root = mScene->mRootNode = new aiNode("ROOT");
@ -1217,7 +1275,7 @@ void glTF2Importer::ImportNodes(glTF2::Asset &r) {
std::fill(root->mChildren, root->mChildren + numRootNodes, nullptr);
for (unsigned int i = 0; i < numRootNodes; ++i) {
aiNode *node = ImportNode(mScene, r, meshOffsets, rootNodes[i]);
aiNode *node = ImportNode(r, rootNodes[i]);
node->mParent = root;
root->mChildren[root->mNumChildren++] = node;
}
@ -1572,7 +1630,7 @@ void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset &r) {
if (!img.mimeType.empty()) {
const char *ext = strchr(img.mimeType.c_str(), '/') + 1;
if (ext) {
if (strcmp(ext, "jpeg") == 0) {
if (strncmp(ext, "jpeg", 4) == 0) {
ext = "jpg";
} else if (strcmp(ext, "ktx2") == 0) { // basisu: ktx remains
ext = "kx2";
@ -1580,9 +1638,9 @@ void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset &r) {
ext = "bu";
}
size_t len = strlen(ext);
const size_t len = strlen(ext);
if (len <= 3) {
strcpy(tex->achFormatHint, ext);
strncpy(tex->achFormatHint, ext, len);
}
}
}
@ -1618,13 +1676,17 @@ void glTF2Importer::InternReadFile(const std::string &pFile, aiScene *pScene, IO
// clean all member arrays
meshOffsets.clear();
mVertexRemappingTables.clear();
mEmbeddedTexIdxs.clear();
this->mScene = pScene;
// read the asset file
glTF2::Asset asset(pIOHandler, static_cast<rapidjson::IRemoteSchemaDocumentProvider *>(mSchemaDocumentProvider));
asset.Load(pFile, GetExtension(pFile) == "glb");
asset.Load(pFile,
CheckMagicToken(
pIOHandler, pFile, AI_GLB_MAGIC_NUMBER, 1, 0,
static_cast<unsigned int>(strlen(AI_GLB_MAGIC_NUMBER))));
if (asset.scene) {
pScene->mName = asset.scene->name;
}

View file

@ -2,7 +2,7 @@
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
Copyright (c) 2006-2024, assimp team
All rights reserved.
@ -43,6 +43,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>
struct aiNode;
@ -59,13 +60,13 @@ namespace Assimp {
class glTF2Importer : public BaseImporter {
public:
glTF2Importer();
~glTF2Importer() override;
~glTF2Importer() override = default;
bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const override;
protected:
const aiImporterDesc *GetInfo() const override;
void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) override;
virtual void SetupProperties(const Importer *pImp) override;
void SetupProperties(const Importer *pImp) override;
private:
void ImportEmbeddedTextures(glTF2::Asset &a);
@ -76,10 +77,12 @@ private:
void ImportNodes(glTF2::Asset &a);
void ImportAnimations(glTF2::Asset &a);
void ImportCommonMetadata(glTF2::Asset &a);
aiNode *ImportNode(glTF2::Asset &r, glTF2::Ref<glTF2::Node> &ptr);
private:
std::vector<unsigned int> meshOffsets;
std::vector<int> mEmbeddedTexIdxs;
std::vector<std::vector<unsigned int>> mVertexRemappingTables; // for each converted aiMesh in the scene, it stores a list of vertices that are actually used
aiScene *mScene;
/// An instance of rapidjson::IRemoteSchemaDocumentProvider