Skip to content

Commit e3f41d8

Browse files
GLTFLoader: add morph target data import
1 parent 2ddb298 commit e3f41d8

6 files changed

Lines changed: 617 additions & 37 deletions

File tree

AssetLoader/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ The following features are currently supported:
1515
* Draco Mesh Compression (automatically enabled when Draco is included into the project)
1616
* PBR Materials (Metallic-Roughness and Specular-Glossiness workflows)
1717
* Skinning
18+
* Morph targets and morph-weight animation data
1819
* Extensions:
1920
* [KHR_materials_anisotropy](https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_materials_anisotropy)
2021
* [KHR_materials_clearcoat](https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_materials_clearcoat)

AssetLoader/interface/GLTFBuilder.hpp

Lines changed: 218 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ class ModelBuilder
180180
int GltfLightIndex);
181181

182182
template <typename GltfModelType>
183-
bool LoadAnimationAndSkin(const GltfModelType& GltfModel);
183+
void LoadAnimationsAndSkins(const GltfModelType& GltfModel);
184184

185185
template <typename GltfModelType>
186186
void LoadSkins(const GltfModelType& GltfModel);
@@ -305,6 +305,198 @@ auto GetGltfDataInfo(const GltfModelType& GltfModel, int AccessorId)
305305
return GltfDataInfo{GltfAccessor, pSrcData, SrcCount, SrcByteStride};
306306
}
307307

308+
template <typename GltfModelType>
309+
bool AppendMorphTargetAttribute(const GltfModelType& GltfModel,
310+
int AccessorId,
311+
Uint32 VertexCount,
312+
const std::string& Semantic,
313+
MorphTarget& Target)
314+
{
315+
const auto Accessor = GltfModel.GetAccessor(AccessorId);
316+
if (Accessor.GetCount() != VertexCount)
317+
{
318+
LOG_ERROR_MESSAGE("Morph target attribute '", Semantic, "' contains ", Accessor.GetCount(),
319+
" values, but the primitive contains ", VertexCount, " vertices");
320+
return false;
321+
}
322+
323+
const auto NumComponents = Accessor.GetNumComponents();
324+
if (NumComponents <= 0)
325+
{
326+
LOG_ERROR_MESSAGE("Morph target attribute '", Semantic, "' has an invalid component count");
327+
return false;
328+
}
329+
330+
const size_t FirstValue = Target.Values.size();
331+
const size_t ValueCount = size_t{VertexCount} * static_cast<size_t>(NumComponents);
332+
if (FirstValue > std::numeric_limits<Uint32>::max() ||
333+
ValueCount > std::numeric_limits<size_t>::max() - FirstValue)
334+
{
335+
LOG_ERROR_MESSAGE("Morph target attribute '", Semantic, "' is too large");
336+
return false;
337+
}
338+
339+
Target.Values.resize(FirstValue + ValueCount, 0.f);
340+
float* const pDst = Target.Values.data() + FirstValue;
341+
342+
const auto WriteValues = [&](const void* pSrc, Uint32 SrcStride, float* pOutput, Uint32 Count) {
343+
return VertexDataConverter::Write({
344+
pSrc,
345+
Accessor.GetComponentType(),
346+
static_cast<Uint32>(NumComponents),
347+
SrcStride,
348+
pOutput,
349+
VT_FLOAT32,
350+
static_cast<Uint32>(NumComponents),
351+
static_cast<Uint32>(NumComponents) * sizeof(float),
352+
Count,
353+
Accessor.IsNormalized(),
354+
});
355+
};
356+
357+
if (Accessor.GetBufferViewId() >= 0)
358+
{
359+
const auto View = GltfModel.GetBufferView(Accessor.GetBufferViewId());
360+
const auto Buffer = GltfModel.GetBuffer(View.GetBufferId());
361+
const int ByteStride = Accessor.GetByteStride(View);
362+
const auto pSrc = Buffer.GetData(View.GetByteOffset() + Accessor.GetByteOffset());
363+
if (ByteStride <= 0 ||
364+
!WriteValues(pSrc, static_cast<Uint32>(ByteStride), pDst, VertexCount))
365+
{
366+
LOG_ERROR_MESSAGE("Failed to read morph target attribute '", Semantic, "'");
367+
Target.Values.resize(FirstValue);
368+
return false;
369+
}
370+
}
371+
else if (!Accessor.IsSparse())
372+
{
373+
LOG_ERROR_MESSAGE("Morph target attribute '", Semantic, "' has no data");
374+
Target.Values.resize(FirstValue);
375+
return false;
376+
}
377+
378+
if (Accessor.IsSparse())
379+
{
380+
const size_t SparseCount = Accessor.GetSparseCount();
381+
if (SparseCount > VertexCount)
382+
{
383+
LOG_ERROR_MESSAGE("Morph target attribute '", Semantic, "' contains too many sparse values");
384+
Target.Values.resize(FirstValue);
385+
return false;
386+
}
387+
388+
const auto IndicesView = GltfModel.GetBufferView(Accessor.GetSparseIndicesBufferViewId());
389+
const auto IndicesBuffer = GltfModel.GetBuffer(IndicesView.GetBufferId());
390+
const auto pIndices = IndicesBuffer.GetData(IndicesView.GetByteOffset() + Accessor.GetSparseIndicesByteOffset());
391+
const auto IndexType = Accessor.GetSparseIndicesComponentType();
392+
const auto IndexSize = GetValueSize(IndexType);
393+
394+
const auto ValuesView = GltfModel.GetBufferView(Accessor.GetSparseValuesBufferViewId());
395+
const auto ValuesBuffer = GltfModel.GetBuffer(ValuesView.GetBufferId());
396+
const auto pValues = ValuesBuffer.GetData(ValuesView.GetByteOffset() + Accessor.GetSparseValuesByteOffset());
397+
const auto ValueStride = GetValueSize(Accessor.GetComponentType()) * static_cast<Uint32>(NumComponents);
398+
399+
if (pIndices == nullptr || pValues == nullptr || IndexSize == 0 || ValueStride == 0)
400+
{
401+
LOG_ERROR_MESSAGE("Morph target attribute '", Semantic, "' has invalid sparse data");
402+
Target.Values.resize(FirstValue);
403+
return false;
404+
}
405+
406+
Uint32 PreviousIndex = 0;
407+
for (size_t SparseIndex = 0; SparseIndex < SparseCount; ++SparseIndex)
408+
{
409+
Uint32 VertexIndex = 0;
410+
const auto pIndex = static_cast<const Uint8*>(pIndices) + SparseIndex * IndexSize;
411+
switch (IndexType)
412+
{
413+
case VT_UINT8:
414+
{
415+
Uint8 Index;
416+
std::memcpy(&Index, pIndex, sizeof(Index));
417+
VertexIndex = Index;
418+
break;
419+
}
420+
421+
case VT_UINT16:
422+
{
423+
Uint16 Index;
424+
std::memcpy(&Index, pIndex, sizeof(Index));
425+
VertexIndex = Index;
426+
break;
427+
}
428+
429+
case VT_UINT32:
430+
std::memcpy(&VertexIndex, pIndex, sizeof(VertexIndex));
431+
break;
432+
433+
default:
434+
LOG_ERROR_MESSAGE("Morph target attribute '", Semantic, "' uses an invalid sparse index type");
435+
Target.Values.resize(FirstValue);
436+
return false;
437+
}
438+
439+
if (VertexIndex >= VertexCount || (SparseIndex != 0 && VertexIndex <= PreviousIndex))
440+
{
441+
LOG_ERROR_MESSAGE("Morph target attribute '", Semantic, "' contains invalid sparse indices");
442+
Target.Values.resize(FirstValue);
443+
return false;
444+
}
445+
PreviousIndex = VertexIndex;
446+
447+
const auto pSparseValue = static_cast<const Uint8*>(pValues) + SparseIndex * ValueStride;
448+
if (!WriteValues(pSparseValue,
449+
ValueStride,
450+
pDst + size_t{VertexIndex} * static_cast<size_t>(NumComponents),
451+
1))
452+
{
453+
LOG_ERROR_MESSAGE("Failed to read sparse morph target attribute '", Semantic, "'");
454+
Target.Values.resize(FirstValue);
455+
return false;
456+
}
457+
}
458+
}
459+
460+
Target.Attributes.push_back(MorphTargetAttribute{
461+
Semantic,
462+
static_cast<Uint32>(FirstValue),
463+
static_cast<Uint32>(NumComponents),
464+
});
465+
return true;
466+
}
467+
468+
template <typename GltfModelType, typename GltfPrimitiveType>
469+
bool LoadMorphTargets(const GltfModelType& GltfModel,
470+
const GltfPrimitiveType& GltfPrimitive,
471+
Uint32 VertexCount,
472+
Primitive& DstPrimitive)
473+
{
474+
const size_t TargetCount = GltfPrimitive.GetMorphTargetCount();
475+
DstPrimitive.MorphTargets.reserve(TargetCount);
476+
for (size_t TargetIndex = 0; TargetIndex < TargetCount; ++TargetIndex)
477+
{
478+
const auto& Attributes = GltfPrimitive.GetMorphTarget(TargetIndex);
479+
MorphTarget Target;
480+
Target.Attributes.reserve(Attributes.size());
481+
482+
for (const auto& Attribute : Attributes)
483+
{
484+
if (!AppendMorphTargetAttribute(GltfModel,
485+
Attribute.second,
486+
VertexCount,
487+
Attribute.first,
488+
Target))
489+
{
490+
DstPrimitive.MorphTargets.clear();
491+
return false;
492+
}
493+
}
494+
495+
DstPrimitive.MorphTargets.emplace_back(std::move(Target));
496+
}
497+
return true;
498+
}
499+
308500
template <typename GltfModelType>
309501
void ModelBuilder::LoadScenes(const GltfModelType& GltfModel, int SceneIndex)
310502
{
@@ -431,6 +623,16 @@ Mesh* ModelBuilder::LoadMesh(const GltfModelType& GltfModel,
431623
}
432624
m_LoadedMeshes.emplace(LoadedMeshId);
433625

626+
if (Mesh* pMesh = MeshLoader.GetLoadedMesh(LoadedMeshId))
627+
{
628+
const auto GltfMesh = GltfModel.GetMesh(GltfMeshIndex);
629+
pMesh->Name = GltfMesh.GetName();
630+
pMesh->Weights.reserve(GltfMesh.GetWeights().size());
631+
for (double Weight : GltfMesh.GetWeights())
632+
pMesh->Weights.push_back(static_cast<float>(Weight));
633+
pMesh->MorphTargetNames = GltfMesh.GetMorphTargetNames();
634+
}
635+
434636
return MeshLoader.LoadMesh(GltfModel, GltfMeshIndex, LoadedMeshId);
435637
}
436638

@@ -443,8 +645,6 @@ Mesh* MeshLoader::LoadMesh(const GltfModelType& GltfModel,
443645

444646
const auto& GltfMesh = GltfModel.GetMesh(GltfMeshIndex);
445647

446-
NewMesh.Name = GltfMesh.GetName();
447-
448648
const size_t PrimitiveCount = GltfMesh.GetPrimitiveCount();
449649
NewMesh.Primitives.reserve(PrimitiveCount);
450650
for (size_t prim = 0; prim < PrimitiveCount; ++prim)
@@ -525,7 +725,7 @@ Mesh* MeshLoader::LoadMesh(const GltfModelType& GltfModel,
525725
MaterialId = m_DefaultMaterialId;
526726
}
527727

528-
NewMesh.Primitives.emplace_back(
728+
Primitive& NewPrimitive = NewMesh.Primitives.emplace_back(
529729
IndexStart,
530730
IndexCount,
531731
VertexStart,
@@ -535,8 +735,11 @@ Mesh* MeshLoader::LoadMesh(const GltfModelType& GltfModel,
535735
PosMax //
536736
);
537737

738+
if (!LoadMorphTargets(GltfModel, GltfPrimitive, VertexCount, NewPrimitive))
739+
LOG_ERROR_MESSAGE("Failed to load morph targets for mesh '", NewMesh.Name, "' primitive ", prim);
740+
538741
if (m_CI.PrimitiveLoadCallback)
539-
m_CI.PrimitiveLoadCallback(&GltfModel.Get(), &GltfPrimitive.Get(), NewMesh.Primitives.back());
742+
m_CI.PrimitiveLoadCallback(&GltfModel.Get(), &GltfPrimitive.Get(), NewPrimitive);
540743
}
541744

542745
NewMesh.UpdateBoundingBox();
@@ -704,6 +907,10 @@ Node* ModelBuilder::LoadNode(const GltfModelType& GltfModel,
704907
NewNode.Matrix = float4x4::MakeMatrix(GltfNode.GetMatrix().data());
705908
}
706909

910+
NewNode.Weights.reserve(GltfNode.GetWeights().size());
911+
for (double Weight : GltfNode.GetWeights())
912+
NewNode.Weights.push_back(static_cast<float>(Weight));
913+
707914
// Load children first
708915
NewNode.Children.reserve(GltfNode.GetChildrenIds().size());
709916
for (const auto ChildNodeIdx : GltfNode.GetChildrenIds())
@@ -1032,13 +1239,6 @@ void ModelBuilder::LoadAnimations(const GltfModelType& GltfModel)
10321239
{
10331240
const auto& GltfChannel = GltfAnim.GetChannel(chnl);
10341241

1035-
const auto PathType = GltfChannel.GetPathType();
1036-
if (PathType == AnimationChannel::PATH_TYPE::WEIGHTS)
1037-
{
1038-
LOG_WARNING_MESSAGE("Weights are not yet supported, skipping channel");
1039-
continue;
1040-
}
1041-
10421242
const auto SamplerIndex = GltfChannel.GetSamplerId();
10431243
if (SamplerIndex < 0)
10441244
continue;
@@ -1051,31 +1251,19 @@ void ModelBuilder::LoadAnimations(const GltfModelType& GltfModel)
10511251
if (pNode == nullptr)
10521252
continue;
10531253

1054-
Anim.Channels.emplace_back(PathType, pNode, SamplerIndex);
1254+
Anim.Channels.emplace_back(GltfChannel.GetPathType(), pNode, SamplerIndex);
10551255
}
10561256
}
10571257
}
10581258

10591259
template <typename GltfModelType>
1060-
bool ModelBuilder::LoadAnimationAndSkin(const GltfModelType& GltfModel)
1260+
void ModelBuilder::LoadAnimationsAndSkins(const GltfModelType& GltfModel)
10611261
{
1062-
bool UsesAnimation = false;
1063-
for (size_t i = 0; i < m_Model.GetNumVertexAttributes(); ++i)
1064-
{
1065-
const auto& Attrib = m_Model.GetVertexAttribute(i);
1066-
1067-
if (strncmp(Attrib.Name, "WEIGHTS", 7) == 0 ||
1068-
strncmp(Attrib.Name, "JOINTS", 6) == 0)
1069-
{
1070-
UsesAnimation = true;
1071-
break;
1072-
}
1073-
}
1262+
LoadAnimations(GltfModel);
10741263

1075-
if (!UsesAnimation)
1076-
return false;
1264+
if (GltfModel.GetSkinCount() == 0)
1265+
return;
10771266

1078-
LoadAnimations(GltfModel);
10791267
LoadSkins(GltfModel);
10801268

10811269
// Assign skins
@@ -1098,8 +1286,6 @@ bool ModelBuilder::LoadAnimationAndSkin(const GltfModelType& GltfModel)
10981286
UNEXPECTED("Node ", i, " has no assigned skin id. This appears to be a bug.");
10991287
}
11001288
}
1101-
1102-
return true;
11031289
}
11041290

11051291
template <typename GltfModelType, typename MeshLoaderType>
@@ -1139,7 +1325,7 @@ void ModelBuilder::BuildModel(const GltfModelType& GltfModel,
11391325
VERIFY_EXPR(m_LoadedCameras.size() == m_Model.Cameras.size());
11401326
VERIFY_EXPR(m_LoadedLights.size() == m_Model.Lights.size());
11411327

1142-
LoadAnimationAndSkin(GltfModel);
1328+
LoadAnimationsAndSkins(GltfModel);
11431329
}
11441330

11451331
class MaterialBuilder

0 commit comments

Comments
 (0)