Merging WrinkleMask support from 1.0 to main (#680)
Added a loop to the skin shader that will sample from wrinkle masks, multiply them by a weight, combine them, and use them instead of vertex colors for wrinkle map blending Added an array of masks, an array of weights, and a wrinkle mask count to the DefaultObjectSrg. -Will create a follow up task to handle this a better way. Removed motion vector (for now) from skin.materialtype since we're not using them, and removed depthtransparent since skin doesn't support transparency Added an interface to the MeshFeatureProcessor to get the object srg Wrapped srg->Compile in if(srg->IsQueuedForCompile()) to prevent compiling twice --This doesn't stop a race condition if both happen at the same time, but that is at least far less likely. It will need a better solution later. Added a function to the MorphTargetExporter that will check to see if a texture that matches the blend shape name exists in a particular folder, and adds a reference to that image to the MorphTargetMetaAsset --Only supports .tif, and doesn't automatically re-process the .fbx if the folder is updated. These can be improved in later iterations Added a null check in MaterialTypeSourceData.cpp to fix a crash I ran into Added a for loop in two places to look for the first submesh that has a morph target, instead of just using the first to check if a lod has morph targets or not. --I have a better fix for this, but it involves more areas of the code, so I'm saving that for another change. Modified AtomActorInstance to look for any morph targets that have a wrinkle mask reference Then each frame, for any morph targets with non-zero weights that also have wrinkle masks, it updates the mask array, weights, and count on the object srg.
This commit is contained in:
@@ -28,6 +28,7 @@
|
||||
#include <MCore/Source/AzCoreConversions.h>
|
||||
|
||||
#include <Atom/RPI.Public/Scene.h>
|
||||
#include <Atom/RPI.Public/Image/StreamingImage.h>
|
||||
|
||||
#include <AzCore/Casting/numeric_cast.h>
|
||||
#include <AzCore/Component/EntityId.h>
|
||||
@@ -39,6 +40,8 @@ namespace AZ
|
||||
{
|
||||
namespace Render
|
||||
{
|
||||
static constexpr uint32_t s_maxActiveWrinkleMasks = 16;
|
||||
|
||||
AZ_CLASS_ALLOCATOR_IMPL(AtomActorInstance, EMotionFX::Integration::EMotionFXAllocator, 0)
|
||||
|
||||
AtomActorInstance::AtomActorInstance(AZ::EntityId entityId,
|
||||
@@ -413,6 +416,10 @@ namespace AZ
|
||||
EMotionFX::MorphSetup* morphSetup = m_actorInstance->GetActor()->GetMorphSetup(lodIndex);
|
||||
if (morphSetup)
|
||||
{
|
||||
// Track all the masks/weights that are currently active
|
||||
m_wrinkleMasks.clear();
|
||||
m_wrinkleMaskWeights.clear();
|
||||
|
||||
uint32_t morphTargetCount = morphSetup->GetNumMorphTargets();
|
||||
m_morphTargetWeights.clear();
|
||||
for (uint32_t morphTargetIndex = 0; morphTargetIndex < morphTargetCount; ++morphTargetIndex)
|
||||
@@ -437,11 +444,28 @@ namespace AZ
|
||||
const EMotionFX::MorphTargetStandard::DeformData* deformData = morphTargetStandard->GetDeformData(deformDataIndex);
|
||||
if (deformData->mNumVerts > 0)
|
||||
{
|
||||
m_morphTargetWeights.push_back(morphTargetSetupInstance->GetWeight());
|
||||
float weight = morphTargetSetupInstance->GetWeight();
|
||||
m_morphTargetWeights.push_back(weight);
|
||||
|
||||
// If the morph target is active and it has a wrinkle mask
|
||||
auto wrinkleMaskIter = m_morphTargetWrinkleMaskMapsByLod[lodIndex].find(morphTargetStandard);
|
||||
if (weight > 0 && wrinkleMaskIter != m_morphTargetWrinkleMaskMapsByLod[lodIndex].end())
|
||||
{
|
||||
// Add the wrinkle mask and weight, to be set on the material
|
||||
m_wrinkleMasks.push_back(wrinkleMaskIter->second);
|
||||
m_wrinkleMaskWeights.push_back(weight);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
m_skinnedMeshRenderProxy->SetMorphTargetWeights(lodIndex, m_morphTargetWeights);
|
||||
|
||||
// Until EMotionFX and Atom lods are synchronized [ATOM-13564] we don't know which EMotionFX lod to pull the weights from
|
||||
// Until that is fixed, just use lod 0 [ATOM-15251]
|
||||
if (lodIndex == 0)
|
||||
{
|
||||
UpdateWrinkleMasks();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -453,6 +477,8 @@ namespace AZ
|
||||
MaterialComponentRequestBus::EventResult(materials, m_entityId, &MaterialComponentRequests::GetMaterialOverrides);
|
||||
CreateRenderProxy(materials);
|
||||
|
||||
InitWrinkleMasks();
|
||||
|
||||
TransformNotificationBus::Handler::BusConnect(m_entityId);
|
||||
MaterialComponentNotificationBus::Handler::BusConnect(m_entityId);
|
||||
MeshComponentRequestBus::Handler::BusConnect(m_entityId);
|
||||
@@ -573,5 +599,77 @@ namespace AZ
|
||||
{
|
||||
CreateSkinnedMeshInstance();
|
||||
}
|
||||
|
||||
void AtomActorInstance::InitWrinkleMasks()
|
||||
{
|
||||
EMotionFX::Actor* actor = m_actorAsset->GetActor();
|
||||
m_morphTargetWrinkleMaskMapsByLod.resize(m_skinnedMeshInputBuffers->GetLodCount());
|
||||
m_wrinkleMasks.reserve(s_maxActiveWrinkleMasks);
|
||||
m_wrinkleMaskWeights.reserve(s_maxActiveWrinkleMasks);
|
||||
|
||||
for (size_t lodIndex = 0; lodIndex < m_skinnedMeshInputBuffers->GetLodCount(); ++lodIndex)
|
||||
{
|
||||
EMotionFX::MorphSetup* morphSetup = actor->GetMorphSetup(lodIndex);
|
||||
if (morphSetup)
|
||||
{
|
||||
const AZStd::vector<AZ::RPI::MorphTargetMetaAsset::MorphTarget>& metaDatas = actor->GetMorphTargetMetaAsset()->GetMorphTargets();
|
||||
// Loop over all the EMotionFX morph targets
|
||||
uint32_t numMorphTargets = morphSetup->GetNumMorphTargets();
|
||||
for (uint32_t morphTargetIndex = 0; morphTargetIndex < numMorphTargets; ++morphTargetIndex)
|
||||
{
|
||||
EMotionFX::MorphTargetStandard* morphTarget = static_cast<EMotionFX::MorphTargetStandard*>(morphSetup->GetMorphTarget(morphTargetIndex));
|
||||
for (const RPI::MorphTargetMetaAsset::MorphTarget& metaData : metaDatas)
|
||||
{
|
||||
// Find the metaData associated with this morph target
|
||||
if (metaData.m_morphTargetName == morphTarget->GetNameString() && metaData.m_wrinkleMask && metaData.m_numVertices > 0)
|
||||
{
|
||||
// If the metaData has a wrinkle mask, add it to the map
|
||||
Data::Instance<RPI::StreamingImage> streamingImage = RPI::StreamingImage::FindOrCreate(metaData.m_wrinkleMask);
|
||||
if (streamingImage)
|
||||
{
|
||||
m_morphTargetWrinkleMaskMapsByLod[lodIndex][morphTarget] = streamingImage;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AtomActorInstance::UpdateWrinkleMasks()
|
||||
{
|
||||
if (m_meshHandle)
|
||||
{
|
||||
Data::Instance<RPI::ShaderResourceGroup> wrinkleMaskObjectSrg = m_meshFeatureProcessor->GetObjectSrg(*m_meshHandle);
|
||||
if (wrinkleMaskObjectSrg)
|
||||
{
|
||||
RHI::ShaderInputImageIndex wrinkleMasksIndex = wrinkleMaskObjectSrg->FindShaderInputImageIndex(Name{ "m_wrinkle_masks" });
|
||||
RHI::ShaderInputConstantIndex wrinkleMaskWeightsIndex = wrinkleMaskObjectSrg->FindShaderInputConstantIndex(Name{ "m_wrinkle_mask_weights" });
|
||||
RHI::ShaderInputConstantIndex wrinkleMaskCountIndex = wrinkleMaskObjectSrg->FindShaderInputConstantIndex(Name{ "m_wrinkle_mask_count" });
|
||||
if (wrinkleMasksIndex.IsValid() || wrinkleMaskWeightsIndex.IsValid() || wrinkleMaskCountIndex.IsValid())
|
||||
{
|
||||
AZ_Error("AtomActorInstance", wrinkleMasksIndex.IsValid(), "m_wrinkle_masks not found on the ObjectSrg, but m_wrinkle_mask_weights and/or m_wrinkle_mask_count are being used.");
|
||||
AZ_Error("AtomActorInstance", wrinkleMaskWeightsIndex.IsValid(), "m_wrinkle_mask_weights not found on the ObjectSrg, but m_wrinkle_masks and/or m_wrinkle_mask_count are being used.");
|
||||
AZ_Error("AtomActorInstance", wrinkleMaskCountIndex.IsValid(), "m_wrinkle_mask_count not found on the ObjectSrg, but m_wrinkle_mask_weights and/or m_wrinkle_masks are being used.");
|
||||
|
||||
if (m_wrinkleMasks.size())
|
||||
{
|
||||
wrinkleMaskObjectSrg->SetImageArray(wrinkleMasksIndex, AZStd::array_view<Data::Instance<RPI::Image>>(m_wrinkleMasks.data(), m_wrinkleMasks.size()));
|
||||
|
||||
// Set the weights for any active masks
|
||||
for (size_t i = 0; i < m_wrinkleMaskWeights.size(); ++i)
|
||||
{
|
||||
wrinkleMaskObjectSrg->SetConstant(wrinkleMaskWeightsIndex, m_wrinkleMaskWeights[i], i);
|
||||
}
|
||||
AZ_Error("AtomActorInstance", m_wrinkleMaskWeights.size() <= s_maxActiveWrinkleMasks, "The skinning shader supports no more than %d active morph targets with wrinkle masks.", s_maxActiveWrinkleMasks);
|
||||
}
|
||||
|
||||
wrinkleMaskObjectSrg->SetConstant(wrinkleMaskCountIndex, aznumeric_cast<uint32_t>(m_wrinkleMasks.size()));
|
||||
m_meshFeatureProcessor->QueueObjectSrgForCompile(*m_meshHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} //namespace Render
|
||||
} // namespace AZ
|
||||
|
||||
Reference in New Issue
Block a user