Add support for importing skeleton-only as Skinned Model (eg. from animation file to have skeleton for retargeting)
This commit is contained in:
@@ -292,21 +292,29 @@ SkinnedModel::SkeletonMapping SkinnedModel::GetSkeletonMapping(Asset* source)
|
||||
|
||||
bool SkinnedModel::Intersects(const Ray& ray, const Matrix& world, Real& distance, Vector3& normal, SkinnedMesh** mesh, int32 lodIndex)
|
||||
{
|
||||
if (LODs.Count() == 0)
|
||||
return false;
|
||||
return LODs[lodIndex].Intersects(ray, world, distance, normal, mesh);
|
||||
}
|
||||
|
||||
bool SkinnedModel::Intersects(const Ray& ray, const Transform& transform, Real& distance, Vector3& normal, SkinnedMesh** mesh, int32 lodIndex)
|
||||
{
|
||||
if (LODs.Count() == 0)
|
||||
return false;
|
||||
return LODs[lodIndex].Intersects(ray, transform, distance, normal, mesh);
|
||||
}
|
||||
|
||||
BoundingBox SkinnedModel::GetBox(const Matrix& world, int32 lodIndex) const
|
||||
{
|
||||
if (LODs.Count() == 0)
|
||||
return BoundingBox::Zero;
|
||||
return LODs[lodIndex].GetBox(world);
|
||||
}
|
||||
|
||||
BoundingBox SkinnedModel::GetBox(int32 lodIndex) const
|
||||
{
|
||||
if (LODs.Count() == 0)
|
||||
return BoundingBox::Zero;
|
||||
return LODs[lodIndex].GetBox();
|
||||
}
|
||||
|
||||
@@ -837,6 +845,7 @@ bool SkinnedModel::Init(const Span<int32>& meshesCountPerLod)
|
||||
for (int32 lodIndex = 0; lodIndex < LODs.Count(); lodIndex++)
|
||||
LODs[lodIndex].Dispose();
|
||||
LODs.Resize(meshesCountPerLod.Length());
|
||||
_initialized = true;
|
||||
|
||||
// Setup meshes
|
||||
for (int32 lodIndex = 0; lodIndex < meshesCountPerLod.Length(); lodIndex++)
|
||||
@@ -1069,7 +1078,7 @@ Asset::LoadResult SkinnedModel::load()
|
||||
// Amount of material slots
|
||||
int32 materialSlotsCount;
|
||||
stream->ReadInt32(&materialSlotsCount);
|
||||
if (materialSlotsCount <= 0 || materialSlotsCount > 4096)
|
||||
if (materialSlotsCount < 0 || materialSlotsCount > 4096)
|
||||
return LoadResult::InvalidData;
|
||||
MaterialSlots.Resize(materialSlotsCount, false);
|
||||
|
||||
@@ -1093,9 +1102,10 @@ Asset::LoadResult SkinnedModel::load()
|
||||
// Amount of LODs
|
||||
byte lods;
|
||||
stream->ReadByte(&lods);
|
||||
if (lods == 0 || lods > MODEL_MAX_LODS)
|
||||
if (lods > MODEL_MAX_LODS)
|
||||
return LoadResult::InvalidData;
|
||||
LODs.Resize(lods);
|
||||
_initialized = true;
|
||||
|
||||
// For each LOD
|
||||
for (int32 lodIndex = 0; lodIndex < lods; lodIndex++)
|
||||
@@ -1220,6 +1230,7 @@ void SkinnedModel::unload(bool isReloading)
|
||||
LODs[i].Dispose();
|
||||
LODs.Clear();
|
||||
Skeleton.Dispose();
|
||||
_initialized = false;
|
||||
_loadedLODs = 0;
|
||||
_skeletonRetargets.Clear();
|
||||
ClearSkeletonMapping();
|
||||
|
||||
@@ -36,6 +36,7 @@ private:
|
||||
Span<int32> NodesMapping;
|
||||
};
|
||||
|
||||
bool _initialized = false;
|
||||
int32 _loadedLODs = 0;
|
||||
StreamSkinnedModelLODTask* _streamingTask = nullptr;
|
||||
Dictionary<Asset*, SkeletonMappingData> _skeletonMappingCache;
|
||||
@@ -63,7 +64,7 @@ public:
|
||||
/// </summary>
|
||||
FORCE_INLINE bool IsInitialized() const
|
||||
{
|
||||
return LODs.HasItems();
|
||||
return _initialized;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -766,21 +766,11 @@ bool ModelData::Pack2SkinnedModelHeader(WriteStream* stream) const
|
||||
return true;
|
||||
}
|
||||
const int32 lodCount = GetLODsCount();
|
||||
if (lodCount == 0 || lodCount > MODEL_MAX_LODS)
|
||||
if (lodCount > MODEL_MAX_LODS)
|
||||
{
|
||||
Log::ArgumentOutOfRangeException();
|
||||
return true;
|
||||
}
|
||||
if (Materials.IsEmpty())
|
||||
{
|
||||
Log::ArgumentOutOfRangeException(TEXT("MaterialSlots"), TEXT("Material slots collection cannot be empty."));
|
||||
return true;
|
||||
}
|
||||
if (!HasSkeleton())
|
||||
{
|
||||
Log::InvalidOperationException(TEXT("Missing skeleton."));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Version
|
||||
stream->WriteByte(1);
|
||||
|
||||
@@ -673,6 +673,15 @@ bool ModelTool::ImportModel(const String& path, ModelData& meshData, Options& op
|
||||
}
|
||||
case ModelType::SkinnedModel:
|
||||
{
|
||||
// Add single node if imported skeleton is empty
|
||||
if (data.Skeleton.Nodes.IsEmpty())
|
||||
{
|
||||
data.Skeleton.Nodes.Resize(1);
|
||||
data.Skeleton.Nodes[0].Name = TEXT("Root");
|
||||
data.Skeleton.Nodes[0].LocalTransform = Transform::Identity;
|
||||
data.Skeleton.Nodes[0].ParentIndex = -1;
|
||||
}
|
||||
|
||||
// Special case if imported model has no bones but has valid skeleton and meshes.
|
||||
// We assume that every mesh uses a single bone. Copy nodes to bones.
|
||||
if (data.Skeleton.Bones.IsEmpty() && Math::IsInRange(data.Skeleton.Nodes.Count(), 1, MAX_BONES_PER_MODEL))
|
||||
@@ -700,16 +709,6 @@ bool ModelTool::ImportModel(const String& path, ModelData& meshData, Options& op
|
||||
}
|
||||
|
||||
// Validate
|
||||
if (data.LODs.IsEmpty() || data.LODs[0].Meshes.IsEmpty())
|
||||
{
|
||||
errorMsg = TEXT("Imported model has no valid geometry.");
|
||||
return true;
|
||||
}
|
||||
if (data.Skeleton.Nodes.IsEmpty() || data.Skeleton.Bones.IsEmpty())
|
||||
{
|
||||
errorMsg = TEXT("Imported model has no skeleton.");
|
||||
return true;
|
||||
}
|
||||
if (data.Skeleton.Bones.Count() > MAX_BONES_PER_MODEL)
|
||||
{
|
||||
errorMsg = String::Format(TEXT("Imported model skeleton has too many bones. Imported: {0}, maximum supported: {1}. Please optimize your asset."), data.Skeleton.Bones.Count(), MAX_BONES_PER_MODEL);
|
||||
@@ -720,7 +719,8 @@ bool ModelTool::ImportModel(const String& path, ModelData& meshData, Options& op
|
||||
LOG(Warning, "Imported skinned model has more than one LOD. Removing the lower LODs. Only single one is supported.");
|
||||
data.LODs.Resize(1);
|
||||
}
|
||||
for (int32 i = 0; i < data.LODs[0].Meshes.Count(); i++)
|
||||
const int32 meshesCount = data.LODs.Count() != 0 ? data.LODs[0].Meshes.Count() : 0;
|
||||
for (int32 i = 0; i < meshesCount; i++)
|
||||
{
|
||||
const auto mesh = data.LODs[0].Meshes[i];
|
||||
if (mesh->BlendIndices.IsEmpty() || mesh->BlendWeights.IsEmpty())
|
||||
@@ -771,7 +771,7 @@ bool ModelTool::ImportModel(const String& path, ModelData& meshData, Options& op
|
||||
#endif
|
||||
}
|
||||
|
||||
LOG(Info, "Imported skeleton has {0} bones, {3} nodes, {1} meshes and {2} material", data.Skeleton.Bones.Count(), data.LODs[0].Meshes.Count(), data.Materials.Count(), data.Nodes.Count());
|
||||
LOG(Info, "Imported skeleton has {0} bones, {3} nodes, {1} meshes and {2} material", data.Skeleton.Bones.Count(), meshesCount, data.Materials.Count(), data.Nodes.Count());
|
||||
break;
|
||||
}
|
||||
case ModelType::Animation:
|
||||
|
||||
Reference in New Issue
Block a user