Files
o3de/Gems/NvCloth/Code/Source/Utils/ActorAssetHelper.cpp
T
2021-03-08 14:30:57 -08:00

230 lines
8.4 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Integration/ActorComponentBus.h>
// Needed to access the Mesh information inside Actor.
#include <EMotionFX/Source/Node.h>
#include <EMotionFX/Source/Mesh.h>
#include <EMotionFX/Source/SubMesh.h>
#include <EMotionFX/Source/ActorInstance.h>
#include <Utils/ActorAssetHelper.h>
namespace NvCloth
{
ActorAssetHelper::ActorAssetHelper(AZ::EntityId entityId)
: AssetHelper(entityId)
{
}
void ActorAssetHelper::GatherClothMeshNodes(MeshNodeList& meshNodes)
{
EMotionFX::ActorInstance* actorInstance = nullptr;
EMotionFX::Integration::ActorComponentRequestBus::EventResult(
actorInstance, m_entityId, &EMotionFX::Integration::ActorComponentRequestBus::Events::GetActorInstance);
if (!actorInstance)
{
return;
}
const EMotionFX::Actor* actor = actorInstance->GetActor();
if (!actor)
{
return;
}
const uint32 numNodes = actor->GetNumNodes();
const uint32 numLODs = actor->GetNumLODLevels();
for (uint32 lodLevel = 0; lodLevel < numLODs; ++lodLevel)
{
for (uint32 nodeIndex = 0; nodeIndex < numNodes; ++nodeIndex)
{
const EMotionFX::Mesh* mesh = actor->GetMesh(lodLevel, nodeIndex);
if (!mesh)
{
continue;
}
const bool hasClothData = (mesh->FindOriginalVertexData(EMotionFX::Mesh::ATTRIB_CLOTH_DATA) != nullptr);
if (hasClothData)
{
const EMotionFX::Node* node = actor->GetSkeleton()->GetNode(nodeIndex);
AZ_Assert(node, "Invalid node %u in actor '%s'", nodeIndex, actor->GetFileNameString().c_str());
meshNodes.push_back(node->GetNameString());
}
}
}
}
bool ActorAssetHelper::ObtainClothMeshNodeInfo(
const AZStd::string& meshNode,
MeshNodeInfo& meshNodeInfo,
MeshClothInfo& meshClothInfo)
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::Cloth);
EMotionFX::ActorInstance* actorInstance = nullptr;
EMotionFX::Integration::ActorComponentRequestBus::EventResult(
actorInstance, m_entityId, &EMotionFX::Integration::ActorComponentRequestBus::Events::GetActorInstance);
if (!actorInstance)
{
return false;
}
const EMotionFX::Actor* actor = actorInstance->GetActor();
if (!actor)
{
return false;
}
const uint32 numNodes = actor->GetNumNodes();
const uint32 numLODs = actor->GetNumLODLevels();
const EMotionFX::Mesh* emfxMesh = nullptr;
uint32 meshFirstPrimitiveIndex = 0;
// Find the render data of the mesh node
for (uint32 lodLevel = 0; lodLevel < numLODs; ++lodLevel)
{
meshFirstPrimitiveIndex = 0;
for (uint32 nodeIndex = 0; nodeIndex < numNodes; ++nodeIndex)
{
const EMotionFX::Mesh* mesh = actor->GetMesh(lodLevel, nodeIndex);
if (!mesh || mesh->GetIsCollisionMesh())
{
// Skip invalid and collision meshes.
continue;
}
const EMotionFX::Node* node = actor->GetSkeleton()->GetNode(nodeIndex);
if (meshNode != node->GetNameString())
{
// Skip. Increase the index of all primitives of the mesh we're skipping.
meshFirstPrimitiveIndex += mesh->GetNumSubMeshes();
continue;
}
// Mesh found, save the lod in mesh info
meshNodeInfo.m_lodLevel = lodLevel;
emfxMesh = mesh;
break;
}
if (emfxMesh)
{
break;
}
}
bool infoObtained = false;
if (emfxMesh)
{
bool dataCopied = CopyDataFromEMotionFXMesh(*emfxMesh, meshClothInfo);
if (dataCopied)
{
const uint32 numSubMeshes = emfxMesh->GetNumSubMeshes();
for (uint32 subMeshIndex = 0; subMeshIndex < numSubMeshes; ++subMeshIndex)
{
const EMotionFX::SubMesh* emfxSubMesh = emfxMesh->GetSubMesh(subMeshIndex);
MeshNodeInfo::SubMesh subMesh;
subMesh.m_primitiveIndex = static_cast<int>(meshFirstPrimitiveIndex + subMeshIndex);
subMesh.m_verticesFirstIndex = emfxSubMesh->GetStartVertex();
subMesh.m_numVertices = emfxSubMesh->GetNumVertices();
subMesh.m_indicesFirstIndex = emfxSubMesh->GetStartIndex();
subMesh.m_numIndices = emfxSubMesh->GetNumIndices();
meshNodeInfo.m_subMeshes.push_back(subMesh);
}
infoObtained = true;
}
else
{
AZ_Error("ActorAssetHelper", false, "Failed to extract data from node %s in actor %s",
meshNode.c_str(), actor->GetFileNameString().c_str());
}
}
return infoObtained;
}
bool ActorAssetHelper::CopyDataFromEMotionFXMesh(
const EMotionFX::Mesh& emfxMesh,
MeshClothInfo& meshClothInfo)
{
const int numVertices = emfxMesh.GetNumVertices();
const int numIndices = emfxMesh.GetNumIndices();
if (numVertices == 0 || numIndices == 0)
{
return false;
}
const uint32* sourceIndices = emfxMesh.GetIndices();
const AZ::Vector3* sourcePositions = static_cast<AZ::Vector3*>(emfxMesh.FindOriginalVertexData(EMotionFX::Mesh::ATTRIB_POSITIONS));
const AZ::u32* sourceClothData = static_cast<AZ::u32*>(emfxMesh.FindOriginalVertexData(EMotionFX::Mesh::ATTRIB_CLOTH_DATA));
const AZ::Vector2* sourceUVs = static_cast<AZ::Vector2*>(emfxMesh.FindOriginalVertexData(EMotionFX::Mesh::ATTRIB_UVCOORDS, 0)); // first UV set
if (!sourceIndices || !sourcePositions || !sourceClothData)
{
return false;
}
const SimUVType uvZero(0.0f, 0.0f);
meshClothInfo.m_particles.resize_no_construct(numVertices);
meshClothInfo.m_uvs.resize_no_construct(numVertices);
meshClothInfo.m_motionConstraints.resize_no_construct(numVertices);
meshClothInfo.m_backstopData.resize_no_construct(numVertices);
for (int index = 0; index < numVertices; ++index)
{
AZ::Color clothVertexData;
clothVertexData.FromU32(sourceClothData[index]);
const float inverseMass = clothVertexData.GetR();
const float motionConstraint = clothVertexData.GetG();
const float backstopRadius = clothVertexData.GetA();
const float backstopOffset = ConvertBackstopOffset(clothVertexData.GetB());
meshClothInfo.m_particles[index].Set(
sourcePositions[index],
inverseMass);
meshClothInfo.m_motionConstraints[index] = motionConstraint;
meshClothInfo.m_backstopData[index].Set(backstopOffset, backstopRadius);
meshClothInfo.m_uvs[index] = (sourceUVs) ? SimUVType(sourceUVs[index].GetX(), sourceUVs[index].GetY()) : uvZero;
}
meshClothInfo.m_indices.resize_no_construct(numIndices);
// Fast copy when SimIndexType is the same size as the EMFX indices type.
if constexpr (sizeof(SimIndexType) == sizeof(uint32))
{
memcpy(meshClothInfo.m_indices.data(), sourceIndices, numIndices * sizeof(SimIndexType));
}
else
{
for (int index = 0; index < numIndices; ++index)
{
meshClothInfo.m_indices[index] = static_cast<SimIndexType>(sourceIndices[index]);
}
}
return true;
}
} // namespace NvCloth