You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
354 lines
18 KiB
C++
354 lines
18 KiB
C++
/*
|
|
* Copyright (c) Contributors to the Open 3D Engine Project.
|
|
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
*
|
|
*/
|
|
|
|
#include <AzCore/Memory/SystemAllocator.h>
|
|
#include <AzCore/RTTI/ReflectContext.h>
|
|
#include <AzCore/Serialization/EditContext.h>
|
|
|
|
#include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshData.h>
|
|
#include <SceneAPI/SceneCore/DataTypes/GraphData/IMeshVertexColorData.h>
|
|
|
|
#include <Pipeline/SceneAPIExt/ClothRule.h>
|
|
|
|
namespace NvCloth
|
|
{
|
|
namespace Pipeline
|
|
{
|
|
// It's necessary for the rule to specify the system allocator, otherwise
|
|
// the editor crashes when deleting the cloth modifier from Scene Settings.
|
|
AZ_CLASS_ALLOCATOR_IMPL(ClothRule, AZ::SystemAllocator, 0)
|
|
|
|
const char* const ClothRule::DefaultChooseNodeName = "Choose a node";
|
|
const char* const ClothRule::DefaultInverseMassesString = "Default: 1.0";
|
|
const char* const ClothRule::DefaultMotionConstraintsString = "Default: 1.0";
|
|
const char* const ClothRule::DefaultBackstopString = "None";
|
|
|
|
const AZStd::string& ClothRule::GetMeshNodeName() const
|
|
{
|
|
return m_meshNodeName;
|
|
}
|
|
|
|
AZStd::vector<AZ::Color> ClothRule::ExtractClothData(
|
|
const AZ::SceneAPI::Containers::SceneGraph& graph,
|
|
const size_t numVertices) const
|
|
{
|
|
const AZ::SceneAPI::Containers::SceneGraph::NodeIndex meshNodeIndex = [this, &graph]()
|
|
{
|
|
if (const auto index = graph.Find(GetMeshNodeName() + AZStd::string(AZ::SceneAPI::Utilities::OptimizedMeshSuffix)); index.IsValid())
|
|
{
|
|
return index;
|
|
}
|
|
return graph.Find(GetMeshNodeName());
|
|
}();
|
|
|
|
if (!meshNodeIndex.IsValid())
|
|
{
|
|
return {};
|
|
}
|
|
|
|
const float defaultInverseMass = 1.0f;
|
|
const float defaultMotionConstraint = 1.0f;
|
|
const float defaultBackstopOffset = 0.5f; // 0.5 means offset 0 once the range is converted from [0,1] -> [-1,1]
|
|
const float defaultBackstopRadius = 0.0f;
|
|
|
|
using GetterFunction = AZStd::function<float(size_t index)>;
|
|
|
|
GetterFunction getInverseMass = [defaultInverseMass]([[maybe_unused]] size_t index) { return defaultInverseMass; };
|
|
GetterFunction getMotionConstraint = [defaultMotionConstraint]([[maybe_unused]] size_t index) { return defaultMotionConstraint; };
|
|
GetterFunction getBackstopOffset = [defaultBackstopOffset]([[maybe_unused]] size_t index) { return defaultBackstopOffset; };
|
|
GetterFunction getBackstopRadius = [defaultBackstopRadius]([[maybe_unused]] size_t index) { return defaultBackstopRadius; };
|
|
|
|
auto getColorChannelSafe = [](
|
|
const AZ::SceneAPI::DataTypes::IMeshVertexColorData* data,
|
|
size_t index,
|
|
AZ::SceneAPI::DataTypes::ColorChannel channel)
|
|
{
|
|
const float colorChannel = data->GetColor(index).GetChannel(channel);
|
|
return AZ::GetClamp(colorChannel, 0.0f, 1.0f);
|
|
};
|
|
|
|
if (!IsInverseMassesStreamDisabled())
|
|
{
|
|
if (auto data = FindVertexColorData(graph, meshNodeIndex, m_inverseMassesStreamName, numVertices))
|
|
{
|
|
getInverseMass =
|
|
[&getColorChannelSafe, data, channel = m_inverseMassesChannel](size_t index)
|
|
{
|
|
return getColorChannelSafe(data.get(), index, channel);
|
|
};
|
|
}
|
|
}
|
|
|
|
if (!IsMotionConstraintsStreamDisabled())
|
|
{
|
|
if (auto data = FindVertexColorData(graph, meshNodeIndex, m_motionConstraintsStreamName, numVertices))
|
|
{
|
|
getMotionConstraint =
|
|
[&getColorChannelSafe, data, channel = m_motionConstraintsChannel](size_t index)
|
|
{
|
|
return getColorChannelSafe(data.get(), index, channel);
|
|
};
|
|
}
|
|
}
|
|
|
|
if (!IsBackstopStreamDisabled())
|
|
{
|
|
if (auto data = FindVertexColorData(graph, meshNodeIndex, m_backstopStreamName, numVertices))
|
|
{
|
|
getBackstopOffset =
|
|
[&getColorChannelSafe, data, channel = m_backstopOffsetChannel](size_t index)
|
|
{
|
|
return getColorChannelSafe(data.get(), index, channel);
|
|
};
|
|
getBackstopRadius =
|
|
[&getColorChannelSafe, data, channel = m_backstopRadiusChannel](size_t index)
|
|
{
|
|
return getColorChannelSafe(data.get(), index, channel);
|
|
};
|
|
}
|
|
}
|
|
|
|
AZStd::vector<AZ::Color> clothData;
|
|
clothData.resize_no_construct(numVertices);
|
|
|
|
// Compile all the data to the vertex color stream of the mesh.
|
|
for (size_t i = 0; i < numVertices; ++i)
|
|
{
|
|
clothData[i].Set(
|
|
getInverseMass(i), // Store inverse masses in red channel
|
|
getMotionConstraint(i), // Store motion constraints in green channel
|
|
getBackstopOffset(i), // Store backstop offsets in blue channel
|
|
getBackstopRadius(i)); // Store backstop radius in alpha channel
|
|
}
|
|
|
|
return clothData;
|
|
}
|
|
|
|
const AZStd::string& ClothRule::GetInverseMassesStreamName() const
|
|
{
|
|
return m_inverseMassesStreamName;
|
|
}
|
|
|
|
const AZStd::string& ClothRule::GetMotionConstraintsStreamName() const
|
|
{
|
|
return m_motionConstraintsStreamName;
|
|
}
|
|
|
|
const AZStd::string& ClothRule::GetBackstopStreamName() const
|
|
{
|
|
return m_backstopStreamName;
|
|
}
|
|
|
|
void ClothRule::SetMeshNodeName(const AZStd::string& name)
|
|
{
|
|
m_meshNodeName = name;
|
|
}
|
|
|
|
void ClothRule::SetInverseMassesStreamName(const AZStd::string& name)
|
|
{
|
|
m_inverseMassesStreamName = name;
|
|
}
|
|
|
|
void ClothRule::SetMotionConstraintsStreamName(const AZStd::string& name)
|
|
{
|
|
m_motionConstraintsStreamName = name;
|
|
}
|
|
|
|
void ClothRule::SetBackstopStreamName(const AZStd::string& name)
|
|
{
|
|
m_backstopStreamName = name;
|
|
}
|
|
|
|
bool ClothRule::IsInverseMassesStreamDisabled() const
|
|
{
|
|
return m_inverseMassesStreamName == DefaultInverseMassesString;
|
|
}
|
|
|
|
bool ClothRule::IsMotionConstraintsStreamDisabled() const
|
|
{
|
|
return m_motionConstraintsStreamName == DefaultMotionConstraintsString;
|
|
}
|
|
|
|
bool ClothRule::IsBackstopStreamDisabled() const
|
|
{
|
|
return m_backstopStreamName == DefaultBackstopString;
|
|
}
|
|
|
|
AZ::SceneAPI::DataTypes::ColorChannel ClothRule::GetInverseMassesStreamChannel() const
|
|
{
|
|
return m_inverseMassesChannel;
|
|
}
|
|
|
|
AZ::SceneAPI::DataTypes::ColorChannel ClothRule::GetMotionConstraintsStreamChannel() const
|
|
{
|
|
return m_motionConstraintsChannel;
|
|
}
|
|
|
|
AZ::SceneAPI::DataTypes::ColorChannel ClothRule::GetBackstopOffsetStreamChannel() const
|
|
{
|
|
return m_backstopOffsetChannel;
|
|
}
|
|
|
|
AZ::SceneAPI::DataTypes::ColorChannel ClothRule::GetBackstopRadiusStreamChannel() const
|
|
{
|
|
return m_backstopRadiusChannel;
|
|
}
|
|
|
|
void ClothRule::SetInverseMassesStreamChannel(AZ::SceneAPI::DataTypes::ColorChannel channel)
|
|
{
|
|
m_inverseMassesChannel = channel;
|
|
}
|
|
|
|
void ClothRule::SetMotionConstraintsStreamChannel(AZ::SceneAPI::DataTypes::ColorChannel channel)
|
|
{
|
|
m_motionConstraintsChannel = channel;
|
|
}
|
|
|
|
void ClothRule::SetBackstopOffsetStreamChannel(AZ::SceneAPI::DataTypes::ColorChannel channel)
|
|
{
|
|
m_backstopOffsetChannel = channel;
|
|
}
|
|
|
|
void ClothRule::SetBackstopRadiusStreamChannel(AZ::SceneAPI::DataTypes::ColorChannel channel)
|
|
{
|
|
m_backstopRadiusChannel = channel;
|
|
}
|
|
|
|
AZStd::shared_ptr<const AZ::SceneAPI::DataTypes::IMeshVertexColorData> ClothRule::FindVertexColorData(
|
|
const AZ::SceneAPI::Containers::SceneGraph& graph,
|
|
const AZ::SceneAPI::Containers::SceneGraph::NodeIndex& meshNodeIndex,
|
|
const AZStd::string& vertexColorName,
|
|
const size_t numVertices) const
|
|
{
|
|
if (vertexColorName.empty())
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
const auto vertexColorNodeIndex = graph.Find(meshNodeIndex, vertexColorName);
|
|
auto vertexColorData = azrtti_cast<const AZ::SceneAPI::DataTypes::IMeshVertexColorData*>(graph.GetNodeContent(vertexColorNodeIndex));
|
|
if (vertexColorData)
|
|
{
|
|
if (numVertices != vertexColorData->GetCount())
|
|
{
|
|
AZ_TracePrintf(AZ::SceneAPI::Utilities::WarningWindow,
|
|
"Number of vertices in the mesh node '%s' (%zu) doesn't match with the number of stored vertex color stream '%s' (%zu).",
|
|
GetMeshNodeName().c_str(), numVertices, vertexColorName.c_str(), vertexColorData->GetCount());
|
|
vertexColorData.reset();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AZ_TracePrintf(AZ::SceneAPI::Utilities::WarningWindow,
|
|
"Vertex color stream '%s' not found for mesh node '%s'.",
|
|
vertexColorName.c_str(),
|
|
GetMeshNodeName().c_str());
|
|
}
|
|
|
|
return vertexColorData;
|
|
}
|
|
|
|
void ClothRule::Reflect(AZ::ReflectContext* context)
|
|
{
|
|
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
|
|
if (!serializeContext)
|
|
{
|
|
return;
|
|
}
|
|
|
|
serializeContext->Class<AZ::SceneAPI::DataTypes::IClothRule, AZ::SceneAPI::DataTypes::IRule>()->Version(1);
|
|
|
|
serializeContext->Class<ClothRule, AZ::SceneAPI::DataTypes::IClothRule>()
|
|
->Version(2, &VersionConverter)
|
|
->Field("meshNodeName", &ClothRule::m_meshNodeName)
|
|
->Field("inverseMassesStreamName", &ClothRule::m_inverseMassesStreamName)
|
|
->Field("inverseMassesChannel", &ClothRule::m_inverseMassesChannel)
|
|
->Field("motionConstraintsStreamName", &ClothRule::m_motionConstraintsStreamName)
|
|
->Field("motionConstraintsChannel", &ClothRule::m_motionConstraintsChannel)
|
|
->Field("backstopStreamName", &ClothRule::m_backstopStreamName)
|
|
->Field("backstopOffsetChannel", &ClothRule::m_backstopOffsetChannel)
|
|
->Field("backstopRadiusChannel", &ClothRule::m_backstopRadiusChannel);
|
|
|
|
AZ::EditContext* editContext = serializeContext->GetEditContext();
|
|
if (editContext)
|
|
{
|
|
editContext->Class<ClothRule>("Cloth", "Adds cloth data to the exported CGF asset. The cloth data will be used to determine what meshes to use for cloth simulation.")
|
|
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
|
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
|
->Attribute(AZ::Edit::Attributes::NameLabelOverride, "")
|
|
->DataElement("NodeListSelection", &ClothRule::m_meshNodeName, "Select Cloth Mesh", "Mesh used for cloth simulation.")
|
|
->Attribute("ClassTypeIdFilter", AZ::SceneAPI::DataTypes::IMeshData::TYPEINFO_Uuid())
|
|
->Attribute("DisabledOption", DefaultChooseNodeName)
|
|
->DataElement("NodeListSelection", &ClothRule::m_inverseMassesStreamName, "Inverse Masses",
|
|
"Select the 'vertex color' stream that contains cloth inverse masses or 'Default: 1.0' to use mass 1.0 for all vertices.")
|
|
->Attribute("ClassTypeIdFilter", AZ::SceneAPI::DataTypes::IMeshVertexColorData::TYPEINFO_Uuid())
|
|
->Attribute("DisabledOption", DefaultInverseMassesString)
|
|
->Attribute("UseShortNames", true)
|
|
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
|
|
->DataElement(AZ::Edit::UIHandlers::ComboBox, &ClothRule::m_inverseMassesChannel, "Inverse Masses Channel",
|
|
"Select which color channel to obtain the inverse mass information from.")
|
|
->EnumAttribute(AZ::SceneAPI::DataTypes::ColorChannel::Red, "Red")
|
|
->EnumAttribute(AZ::SceneAPI::DataTypes::ColorChannel::Green, "Green")
|
|
->EnumAttribute(AZ::SceneAPI::DataTypes::ColorChannel::Blue, "Blue")
|
|
->EnumAttribute(AZ::SceneAPI::DataTypes::ColorChannel::Alpha, "Alpha")
|
|
->Attribute(AZ::Edit::Attributes::ReadOnly, &ClothRule::IsInverseMassesStreamDisabled)
|
|
->DataElement("NodeListSelection", &ClothRule::m_motionConstraintsStreamName, "Motion Constraints",
|
|
"Select the 'vertex color' stream that contains cloth motion constraints or 'Default: 1.0' to use 1.0 for all vertices.")
|
|
->Attribute("ClassTypeIdFilter", AZ::SceneAPI::DataTypes::IMeshVertexColorData::TYPEINFO_Uuid())
|
|
->Attribute("DisabledOption", DefaultMotionConstraintsString)
|
|
->Attribute("UseShortNames", true)
|
|
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
|
|
->DataElement(AZ::Edit::UIHandlers::ComboBox, &ClothRule::m_motionConstraintsChannel, "Motion Constraints Channel",
|
|
"Select which color channel to obtain the motion constraints information from.")
|
|
->EnumAttribute(AZ::SceneAPI::DataTypes::ColorChannel::Red, "Red")
|
|
->EnumAttribute(AZ::SceneAPI::DataTypes::ColorChannel::Green, "Green")
|
|
->EnumAttribute(AZ::SceneAPI::DataTypes::ColorChannel::Blue, "Blue")
|
|
->EnumAttribute(AZ::SceneAPI::DataTypes::ColorChannel::Alpha, "Alpha")
|
|
->Attribute(AZ::Edit::Attributes::ReadOnly, &ClothRule::IsMotionConstraintsStreamDisabled)
|
|
->DataElement("NodeListSelection", &ClothRule::m_backstopStreamName, "Backstop",
|
|
"Select the 'vertex color' stream that contains cloth backstop data.")
|
|
->Attribute("ClassTypeIdFilter", AZ::SceneAPI::DataTypes::IMeshVertexColorData::TYPEINFO_Uuid())
|
|
->Attribute("DisabledOption", DefaultBackstopString)
|
|
->Attribute("UseShortNames", true)
|
|
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::EntireTree)
|
|
->DataElement(AZ::Edit::UIHandlers::ComboBox, &ClothRule::m_backstopOffsetChannel, "Backstop Offset Channel",
|
|
"Select which color channel to obtain the backstop offset from.")
|
|
->EnumAttribute(AZ::SceneAPI::DataTypes::ColorChannel::Red, "Red")
|
|
->EnumAttribute(AZ::SceneAPI::DataTypes::ColorChannel::Green, "Green")
|
|
->EnumAttribute(AZ::SceneAPI::DataTypes::ColorChannel::Blue, "Blue")
|
|
->EnumAttribute(AZ::SceneAPI::DataTypes::ColorChannel::Alpha, "Alpha")
|
|
->Attribute(AZ::Edit::Attributes::ReadOnly, &ClothRule::IsBackstopStreamDisabled)
|
|
->DataElement(AZ::Edit::UIHandlers::ComboBox, &ClothRule::m_backstopRadiusChannel, "Backstop Radius Channel",
|
|
"Select which color channel to obtain the backstop radius from.")
|
|
->EnumAttribute(AZ::SceneAPI::DataTypes::ColorChannel::Red, "Red")
|
|
->EnumAttribute(AZ::SceneAPI::DataTypes::ColorChannel::Green, "Green")
|
|
->EnumAttribute(AZ::SceneAPI::DataTypes::ColorChannel::Blue, "Blue")
|
|
->EnumAttribute(AZ::SceneAPI::DataTypes::ColorChannel::Alpha, "Alpha")
|
|
->Attribute(AZ::Edit::Attributes::ReadOnly, &ClothRule::IsBackstopStreamDisabled);
|
|
}
|
|
}
|
|
|
|
bool ClothRule::VersionConverter(
|
|
AZ::SerializeContext& context,
|
|
AZ::SerializeContext::DataElementNode& classElement)
|
|
{
|
|
if (classElement.GetVersion() <= 1)
|
|
{
|
|
AZStd::string vertexColorStreamName;
|
|
classElement.FindSubElementAndGetData(AZ_CRC("vertexColorStreamName", 0xc5921188), vertexColorStreamName);
|
|
classElement.RemoveElementByName(AZ_CRC("vertexColorStreamName", 0xc5921188));
|
|
classElement.AddElementWithData(context, "inverseMassesStreamName", vertexColorStreamName.empty() ? AZStd::string(DefaultInverseMassesString) : vertexColorStreamName);
|
|
classElement.AddElementWithData(context, "motionConstraintsStreamName", AZStd::string(DefaultMotionConstraintsString));
|
|
classElement.AddElementWithData(context, "backstopStreamName", AZStd::string(DefaultBackstopString));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
} // namespace Pipeline
|
|
} // namespace NvCloth
|