| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825 |
- /*
- * 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/Interface/Interface.h>
- #include <AzCore/Console/IConsole.h>
- #include <AzCore/Math/PackedVector3.h>
- #include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentBus.h>
- #include <AtomLyIntegration/CommonFeatures/SkinnedMesh/SkinnedMeshOverrideBus.h>
- #include <Atom/RHI/RHIUtils.h>
- #include <NvCloth/IClothSystem.h>
- #include <NvCloth/IFabricCooker.h>
- #include <NvCloth/IClothConfigurator.h>
- #include <NvCloth/ITangentSpaceHelper.h>
- #include <Components/ClothComponentMesh/ActorClothColliders.h>
- #include <Components/ClothComponentMesh/ActorClothSkinning.h>
- #include <Components/ClothComponentMesh/ClothConstraints.h>
- #include <Components/ClothComponentMesh/ClothDebugDisplay.h>
- #include <Components/ClothComponentMesh/ClothComponentMesh.h>
- #include <AzFramework/Physics/PhysicsScene.h>
- #include <AzFramework/Physics/WindBus.h>
- #include <AzFramework/Physics/Common/PhysicsTypes.h>
- #include <Atom/RPI.Reflect/Buffer/BufferAssetView.h>
- #include <Atom/RPI.Reflect/Model/ModelAsset.h>
- #include <Atom/RPI.Reflect/Model/ModelLodAsset.h>
- namespace NvCloth
- {
- AZ_CVAR(float, cloth_DistanceToTeleport, 0.5f, nullptr, AZ::ConsoleFunctorFlags::Null,
- "The amount of meters the entity has to move in a frame to consider it a teleport for cloth.");
- AZ_CVAR(float, cloth_SecondsToDelaySimulationOnActorSpawned, 0.25f, nullptr, AZ::ConsoleFunctorFlags::Null,
- "The amount of time in seconds the cloth simulation will be delayed to avoid sudden impulses when actors are spawned.");
- // Helper class to map an RPI buffer from a buffer asset view.
- template<typename T>
- class MappedBuffer
- {
- public:
- MappedBuffer(
- const AZ::RPI::BufferAssetView* bufferAssetView,
- const size_t expectedElementCount,
- const AZ::RHI::Format expectedElementFormat);
- ~MappedBuffer();
- T* GetBuffer()
- {
- return m_buffer;
- }
- private:
- AZ::Data::Instance<AZ::RPI::Buffer> m_rpiBuffer;
- T* m_buffer = nullptr;
- };
- template<typename T>
- MappedBuffer<T>::MappedBuffer(
- const AZ::RPI::BufferAssetView* bufferAssetView,
- [[maybe_unused]] const size_t expectedElementCount,
- [[maybe_unused]] const AZ::RHI::Format expectedElementFormat)
- {
- if (!bufferAssetView)
- {
- return;
- }
- const AZ::RHI::BufferViewDescriptor& bufferViewDescriptor = bufferAssetView->GetBufferViewDescriptor();
- AZ_Assert(bufferViewDescriptor.m_elementCount == expectedElementCount,
- "Unexpected buffer size: expected is %d but descriptor's is %d", expectedElementCount, bufferViewDescriptor.m_elementCount);
- AZ_Assert(bufferViewDescriptor.m_elementSize == sizeof(T),
- "Unexpected buffer element size: expected is %d but descriptor's is %d", sizeof(T), bufferViewDescriptor.m_elementSize);
- AZ_Assert(bufferViewDescriptor.m_elementFormat == expectedElementFormat,
- "Unexpected buffer format: expected is %d but descriptor's is %d", expectedElementFormat, bufferViewDescriptor.m_elementFormat);
- const AZ::Data::Asset<AZ::RPI::BufferAsset>& bufferAsset = bufferAssetView->GetBufferAsset();
- m_rpiBuffer = AZ::RPI::Buffer::FindOrCreate(bufferAsset);
- if (m_rpiBuffer == nullptr)
- {
- AZ_Error("ClothComponentMesh", false,
- "Failed to find or create RPI buffer from buffer asset '%s'", bufferAsset.GetHint().c_str());
- return;
- }
- const uint64_t byteCount = aznumeric_cast<uint64_t>(bufferViewDescriptor.m_elementCount) * aznumeric_cast<uint64_t>(bufferViewDescriptor.m_elementSize);
- const uint64_t byteOffset = aznumeric_cast<uint64_t>(bufferViewDescriptor.m_elementOffset) * aznumeric_cast<uint64_t>(bufferViewDescriptor.m_elementSize);
- m_buffer = static_cast<T*>(m_rpiBuffer->Map(byteCount, byteOffset));
- }
- template<typename T>
- MappedBuffer<T>::~MappedBuffer()
- {
- if (m_buffer)
- {
- m_rpiBuffer->Unmap();
- }
- }
- ClothComponentMesh::ClothComponentMesh(AZ::EntityId entityId, const ClothConfiguration& config)
- : m_preSimulationEventHandler(
- [this](ClothId clothId, float deltaTime)
- {
- this->OnPreSimulation(clothId, deltaTime);
- })
- , m_postSimulationEventHandler(
- [this](ClothId clothId, float deltaTime, const AZStd::vector<SimParticleFormat>& updatedParticles)
- {
- this->OnPostSimulation(clothId, deltaTime, updatedParticles);
- })
- {
- Setup(entityId, config);
- }
- ClothComponentMesh::~ClothComponentMesh()
- {
- TearDown();
- }
- void ClothComponentMesh::UpdateConfiguration(AZ::EntityId entityId, const ClothConfiguration& config)
- {
- if (m_entityId != entityId ||
- m_config.m_meshNode != config.m_meshNode ||
- m_config.m_removeStaticTriangles != config.m_removeStaticTriangles)
- {
- Setup(entityId, config);
- }
- else if (m_cloth)
- {
- m_config = config;
- ApplyConfigurationToCloth();
- // Update the cloth constraints parameters
- m_clothConstraints->SetMotionConstraintMaxDistance(m_config.m_motionConstraintsMaxDistance);
- m_clothConstraints->SetBackstopMaxRadius(m_config.m_backstopRadius);
- m_clothConstraints->SetBackstopMaxOffsets(m_config.m_backstopBackOffset, m_config.m_backstopFrontOffset);
- UpdateSimulationConstraints();
- // Subscribe to WindNotificationsBus only if custom wind velocity flag is not set
- if (m_config.IsUsingWindBus())
- {
- Physics::WindNotificationsBus::Handler::BusConnect();
- }
- else
- {
- Physics::WindNotificationsBus::Handler::BusDisconnect();
- }
- }
- }
- void ClothComponentMesh::Setup(AZ::EntityId entityId, const ClothConfiguration& config)
- {
- TearDown();
- m_entityId = entityId;
- m_config = config;
- if (!CreateCloth())
- {
- TearDown();
- return;
- }
- // Initialize render data
- m_renderDataBufferIndex = 0;
- {
- auto& renderData = GetRenderData();
- renderData.m_particles = m_meshClothInfo.m_particles;
- renderData.m_tangents = m_meshClothInfo.m_tangents;
- renderData.m_bitangents = m_meshClothInfo.m_bitangents;
- renderData.m_normals = m_meshClothInfo.m_normals;
- }
- UpdateRenderData(m_cloth->GetParticles());
- // Copy the first initialized element to the rest of the buffer
- for (AZ::u32 i = 1; i < RenderDataBufferSize; ++i)
- {
- m_renderDataBuffer[i] = m_renderDataBuffer[0];
- }
- // It will return a valid instance if it's an actor with cloth colliders in it.
- m_actorClothColliders = ActorClothColliders::Create(m_entityId);
- // It will return a valid instance if it's an actor with skinning data.
- m_actorClothSkinning = ActorClothSkinning::Create(
- m_entityId,
- m_meshNodeInfo,
- m_meshClothInfo.m_particles,
- m_cloth->GetParticles().size(),
- m_meshRemappedVertices);
- m_timeClothSkinningUpdates = 0.0f;
- // Turn off GPU skinning for any sub-meshes simulated by the cloth component
- DisableSkinning();
- m_clothConstraints = ClothConstraints::Create(
- m_meshClothInfo.m_motionConstraints,
- m_config.m_motionConstraintsMaxDistance,
- m_meshClothInfo.m_backstopData,
- m_config.m_backstopRadius,
- m_config.m_backstopBackOffset,
- m_config.m_backstopFrontOffset,
- m_cloth->GetParticles(),
- m_cloth->GetInitialIndices(),
- m_meshRemappedVertices);
- AZ_Assert(m_clothConstraints, "Failed to create cloth constraints");
- UpdateSimulationConstraints();
- #ifndef RELEASE
- m_clothDebugDisplay = AZStd::make_unique<ClothDebugDisplay>(this);
- #endif
- AZ::TransformNotificationBus::Handler::BusConnect(m_entityId);
- AZ::TickBus::Handler::BusConnect();
- m_cloth->ConnectPreSimulationEventHandler(m_preSimulationEventHandler);
- m_cloth->ConnectPostSimulationEventHandler(m_postSimulationEventHandler);
- if (m_config.IsUsingWindBus())
- {
- Physics::WindNotificationsBus::Handler::BusConnect();
- }
- }
- void ClothComponentMesh::TearDown()
- {
- if (m_cloth)
- {
- Physics::WindNotificationsBus::Handler::BusDisconnect();
- AZ::TickBus::Handler::BusDisconnect();
- AZ::TransformNotificationBus::Handler::BusDisconnect();
- m_preSimulationEventHandler.Disconnect();
- m_postSimulationEventHandler.Disconnect();
- AZ::Interface<IClothSystem>::Get()->RemoveCloth(m_cloth);
- AZ::Interface<IClothSystem>::Get()->DestroyCloth(m_cloth);
- // Re-enable skinning for any sub-meshes that were previously skinned by the cloth component
- EnableSkinning();
- }
- m_entityId.SetInvalid();
- m_renderDataBuffer = {};
- m_meshRemappedVertices.clear();
- m_meshNodeInfo = {};
- m_meshClothInfo = {};
- m_actorClothColliders.reset();
- m_actorClothSkinning.reset();
- m_clothConstraints.reset();
- m_motionConstraints.clear();
- m_separationConstraints.clear();
- m_clothDebugDisplay.reset();
- }
- void ClothComponentMesh::OnPreSimulation(
- [[maybe_unused]] ClothId clothId,
- float deltaTime)
- {
- AZ_PROFILE_FUNCTION(Cloth);
- UpdateSimulationCollisions();
- if (m_actorClothSkinning)
- {
- UpdateSimulationSkinning(deltaTime);
- UpdateSimulationConstraints();
- }
- }
- void ClothComponentMesh::OnPostSimulation(
- [[maybe_unused]] ClothId clothId,
- [[maybe_unused]] float deltaTime,
- const AZStd::vector<SimParticleFormat>& updatedParticles)
- {
- AZ_PROFILE_FUNCTION(Cloth);
- // Next buffer index of the render data
- m_renderDataBufferIndex = (m_renderDataBufferIndex + 1) % RenderDataBufferSize;
- UpdateRenderData(updatedParticles);
- }
- void ClothComponentMesh::OnTransformChanged([[maybe_unused]] const AZ::Transform& local, const AZ::Transform& world)
- {
- // At the moment there is no way to distinguish "move" from "teleport".
- // As a workaround we will consider a teleport if the position has changed considerably.
- bool teleport = (m_worldPosition.GetDistance(world.GetTranslation()) >= cloth_DistanceToTeleport);
- if (teleport)
- {
- TeleportCloth(world);
- }
- else
- {
- MoveCloth(world);
- }
- }
- void ClothComponentMesh::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
- {
- CopyRenderDataToModel();
- }
- int ClothComponentMesh::GetTickOrder()
- {
- return AZ::TICK_PRE_RENDER;
- }
- void ClothComponentMesh::OnGlobalWindChanged()
- {
- m_cloth->GetClothConfigurator()->SetWindVelocity(GetWindBusVelocity());
- }
- void ClothComponentMesh::OnWindChanged([[maybe_unused]] const AZ::Aabb& aabb)
- {
- OnGlobalWindChanged();
- }
- ClothComponentMesh::RenderData& ClothComponentMesh::GetRenderData()
- {
- return const_cast<RenderData&>(
- static_cast<const ClothComponentMesh&>(*this).GetRenderData());
- }
- const ClothComponentMesh::RenderData& ClothComponentMesh::GetRenderData() const
- {
- return m_renderDataBuffer[m_renderDataBufferIndex];
- }
- void ClothComponentMesh::UpdateSimulationCollisions()
- {
- if (m_actorClothColliders)
- {
- AZ_PROFILE_FUNCTION(Cloth);
- m_actorClothColliders->Update();
- const auto& spheres = m_actorClothColliders->GetSpheres();
- m_cloth->GetClothConfigurator()->SetSphereColliders(spheres);
- const auto& capsuleIndices = m_actorClothColliders->GetCapsuleIndices();
- m_cloth->GetClothConfigurator()->SetCapsuleColliders(capsuleIndices);
- }
- }
- void ClothComponentMesh::UpdateSimulationSkinning(float deltaTime)
- {
- if (m_actorClothSkinning)
- {
- AZ_PROFILE_FUNCTION(Cloth);
- m_actorClothSkinning->UpdateSkinning();
- // Since component activation order is not trivial, the actor's pose might not be updated
- // immediately. Because of this cloth will receive a sudden impulse when changing from
- // T pose to animated pose. To avoid this undesired effect we will override cloth simulation during
- // a short amount of time.
- m_timeClothSkinningUpdates += deltaTime;
- // While the actor is not visible the skinned joints are not updated. Then when
- // it becomes visible the jump to the new skinned positions causes a sudden
- // impulse to cloth simulation. To avoid this undesired effect we will override cloth simulation during
- // a short amount of time.
- m_actorClothSkinning->UpdateActorVisibility();
- if (!m_actorClothSkinning->WasActorVisible() &&
- m_actorClothSkinning->IsActorVisible())
- {
- m_timeClothSkinningUpdates = 0.0f;
- }
- if (m_timeClothSkinningUpdates <= cloth_SecondsToDelaySimulationOnActorSpawned)
- {
- // Update skinning for all particles and apply it to cloth
- AZStd::vector<SimParticleFormat> particles = m_cloth->GetParticles();
- m_actorClothSkinning->ApplySkinning(m_cloth->GetInitialParticles(), particles);
- m_cloth->SetParticles(AZStd::move(particles));
- m_cloth->DiscardParticleDelta();
- }
- }
- }
- void ClothComponentMesh::UpdateSimulationConstraints()
- {
- AZ_PROFILE_FUNCTION(Cloth);
- m_motionConstraints = m_clothConstraints->GetMotionConstraints();
- m_separationConstraints = m_clothConstraints->GetSeparationConstraints();
- if (m_actorClothSkinning)
- {
- m_actorClothSkinning->ApplySkinning(m_clothConstraints->GetMotionConstraints(), m_motionConstraints);
- m_actorClothSkinning->ApplySkinning(m_clothConstraints->GetSeparationConstraints(), m_separationConstraints);
- }
- m_cloth->GetClothConfigurator()->SetMotionConstraints(m_motionConstraints);
- if (!m_separationConstraints.empty())
- {
- m_cloth->GetClothConfigurator()->SetSeparationConstraints(m_separationConstraints);
- }
- }
- void ClothComponentMesh::UpdateRenderData(const AZStd::vector<SimParticleFormat>& particles)
- {
- AZ_PROFILE_FUNCTION(Cloth);
- if (!m_cloth)
- {
- return;
- }
- auto& renderData = GetRenderData();
- if (m_actorClothSkinning)
- {
- // Apply skinning to the non-simulated part of the mesh.
- m_actorClothSkinning->ApplySkinningOnNonSimulatedVertices(m_meshClothInfo, renderData);
- }
- // Calculate normals of the cloth particles (simplified mesh).
- AZStd::vector<AZ::Vector3> normals;
- [[maybe_unused]] bool normalsCalculated =
- AZ::Interface<ITangentSpaceHelper>::Get()->CalculateNormals(particles, m_cloth->GetInitialIndices(), normals);
- AZ_Assert(normalsCalculated, "Cloth component mesh failed to calculate normals.");
- // Copy particles and normals to render data.
- // Since cloth's vertices were welded together,
- // the full mesh will result in smooth normals.
- for (size_t index = 0; index < m_meshRemappedVertices.size(); ++index)
- {
- const int remappedIndex = m_meshRemappedVertices[index];
- if (remappedIndex >= 0)
- {
- renderData.m_particles[index] = particles[remappedIndex];
- // For static particles only use the updated normal when indicated in the configuration.
- const bool useSimulatedClothParticleNormal =
- m_meshClothInfo.m_particles[index].GetW() != 0.0f ||
- m_config.m_updateNormalsOfStaticParticles;
- if (useSimulatedClothParticleNormal)
- {
- renderData.m_normals[index] = normals[remappedIndex];
- }
- }
- }
- // Calculate tangents and bitangents for the full mesh.
- [[maybe_unused]] bool tangentsAndBitangentsCalculated =
- AZ::Interface<ITangentSpaceHelper>::Get()->CalculateTangentsAndBitagents(
- renderData.m_particles, m_meshClothInfo.m_indices,
- m_meshClothInfo.m_uvs, renderData.m_normals,
- renderData.m_tangents, renderData.m_bitangents);
- AZ_Assert(tangentsAndBitangentsCalculated, "Cloth component mesh failed to calculate tangents and bitangents.");
- }
- void ClothComponentMesh::CopyRenderDataToModel()
- {
- AZ_PROFILE_FUNCTION(Cloth);
- // Previous buffer index of the render data
- const AZ::u32 previousBufferIndex = (m_renderDataBufferIndex + RenderDataBufferSize - 1) % RenderDataBufferSize;
- // Workaround to sync debug drawing with cloth rendering as
- // the Entity Debug Display Bus renders on the next frame.
- const bool isDebugDrawEnabled = m_clothDebugDisplay && m_clothDebugDisplay->IsDebugDrawEnabled();
- const RenderData& renderData = (isDebugDrawEnabled)
- ? m_renderDataBuffer[previousBufferIndex]
- : m_renderDataBuffer[m_renderDataBufferIndex];
- const auto& renderParticles = renderData.m_particles;
- const auto& renderNormals = renderData.m_normals;
- const auto& renderTangents = renderData.m_tangents;
- const auto& renderBitangents = renderData.m_bitangents;
- // Since Atom has a 1:1 relation with between ModelAsset buffers and Model buffers,
- // internally it created a new asset for the model instance. So it's important to
- // get the asset from the model when we want to write to them, instead of getting the
- // ModelAsset directly from the bus (which returns the original asset shared by all entities).
- AZ::Data::Instance<AZ::RPI::Model> model;
- AZ::Render::MeshComponentRequestBus::EventResult(model, m_entityId, &AZ::Render::MeshComponentRequestBus::Events::GetModel);
- if (!model)
- {
- return;
- }
- AZ::Data::Asset<AZ::RPI::ModelAsset> modelAsset = model->GetModelAsset();
- if (!modelAsset.IsReady())
- {
- return;
- }
- if (modelAsset->GetLodCount() < m_meshNodeInfo.m_lodLevel)
- {
- AZ_Error("ClothComponentMesh", false,
- "Unable to access lod %d from model asset '%s' as it only has %d lod levels.",
- m_meshNodeInfo.m_lodLevel,
- modelAsset.GetHint().c_str(),
- modelAsset->GetLodCount());
- return;
- }
- const auto modelLodAssets = modelAsset->GetLodAssets();
- const AZ::Data::Asset<AZ::RPI::ModelLodAsset> modelLodAsset = modelLodAssets[m_meshNodeInfo.m_lodLevel];
- if (!modelLodAsset.GetId().IsValid())
- {
- AZ_Error("ClothComponentMesh", false,
- "Model asset '%s' returns an invalid lod asset '%s' (lod level %d).",
- modelAsset.GetHint().c_str(),
- modelLodAsset.GetHint().c_str(),
- m_meshNodeInfo.m_lodLevel);
- return;
- }
- const AZ::Name positionSemantic("POSITION");
- const AZ::Name normalSemantic("NORMAL");
- const AZ::Name tangentSemantic("TANGENT");
- const AZ::Name bitangentSemantic("BITANGENT");
- // For each submesh...
- for (const auto& subMeshInfo : m_meshNodeInfo.m_subMeshes)
- {
- if (modelLodAsset->GetMeshes().size() < subMeshInfo.m_primitiveIndex)
- {
- AZ_Error("ClothComponentMesh", false,
- "Unable to access submesh %d from lod asset '%s' as it only has %d submeshes.",
- subMeshInfo.m_primitiveIndex,
- modelAsset.GetHint().c_str(),
- modelLodAsset->GetMeshes().size());
- continue;
- }
-
- const auto subMeshes = modelLodAsset->GetMeshes();
- const AZ::RPI::ModelLodAsset::Mesh& subMesh = subMeshes[subMeshInfo.m_primitiveIndex];
- const int numVertices = subMeshInfo.m_numVertices;
- const int firstVertex = subMeshInfo.m_verticesFirstIndex;
- if (subMesh.GetVertexCount() != static_cast<uint32_t>(numVertices))
- {
- AZ_Error("ClothComponentMesh", false,
- "Render mesh to be modified doesn't have the same number of vertices (%d) as the cloth's submesh (%d).",
- subMesh.GetVertexCount(),
- numVertices);
- continue;
- }
- AZ_Assert(firstVertex >= 0, "Invalid first vertex index %d", firstVertex);
- AZ_Assert((firstVertex + numVertices) <= static_cast<int>(renderParticles.size()),
- "Submesh number of vertices (%d) reaches outside the particles (%zu)", (firstVertex + numVertices), renderParticles.size());
- MappedBuffer<AZ::PackedVector3f> destVertices(subMesh.GetSemanticBufferAssetView(positionSemantic), numVertices, AZ::RHI::Format::R32G32B32_FLOAT);
- MappedBuffer<AZ::PackedVector3f> destNormals(subMesh.GetSemanticBufferAssetView(normalSemantic), numVertices, AZ::RHI::Format::R32G32B32_FLOAT);
- MappedBuffer<AZ::Vector4> destTangents(subMesh.GetSemanticBufferAssetView(tangentSemantic), numVertices, AZ::RHI::Format::R32G32B32A32_FLOAT);
- MappedBuffer<AZ::PackedVector3f> destBitangents(subMesh.GetSemanticBufferAssetView(bitangentSemantic), numVertices, AZ::RHI::Format::R32G32B32_FLOAT);
- auto* destVerticesBuffer = destVertices.GetBuffer();
- auto* destNormalsBuffer = destNormals.GetBuffer();
- auto* destTangentsBuffer = destTangents.GetBuffer();
- auto* destBitangentsBuffer = destBitangents.GetBuffer();
- if (!destVerticesBuffer)
- {
- AZ_Error("ClothComponentMesh", AZ::RHI::IsNullRHI(),
- "Invalid vertex position buffer obtained from the render mesh to be modified.");
- continue;
- }
- for (size_t index = 0; index < numVertices; ++index)
- {
- const int renderVertexIndex = static_cast<int>(firstVertex + index);
- const SimParticleFormat& renderParticle = renderParticles[renderVertexIndex];
- destVerticesBuffer[index].Set(
- renderParticle.GetX(),
- renderParticle.GetY(),
- renderParticle.GetZ());
- if (destNormalsBuffer)
- {
- const AZ::Vector3& renderNormal = renderNormals[renderVertexIndex];
- destNormalsBuffer[index].Set(
- renderNormal.GetX(),
- renderNormal.GetY(),
- renderNormal.GetZ());
- }
- if (destTangentsBuffer)
- {
- const AZ::Vector3& renderTangent = renderTangents[renderVertexIndex];
- destTangentsBuffer[index].Set(
- renderTangent,
- -1.0f); // Shader function ConstructTBN inverts w to change bitangent sign, but the bitangents passed are already corrected, so passing -1.0 to counteract.
- }
- if (destBitangentsBuffer)
- {
- const AZ::Vector3& renderBitangent = renderBitangents[renderVertexIndex];
- destBitangentsBuffer[index].Set(
- renderBitangent.GetX(),
- renderBitangent.GetY(),
- renderBitangent.GetZ());
- }
- }
- }
- }
- bool ClothComponentMesh::CreateCloth()
- {
- AZStd::unique_ptr<AssetHelper> assetHelper = AssetHelper::CreateAssetHelper(m_entityId);
- if (!assetHelper)
- {
- return false;
- }
- // Obtain cloth mesh info
- bool clothInfoObtained = assetHelper->ObtainClothMeshNodeInfo(m_config.m_meshNode,
- m_meshNodeInfo, m_meshClothInfo);
- if (!clothInfoObtained)
- {
- return false;
- }
- // Generate a simplified mesh for simulation
- AZStd::vector<SimParticleFormat> meshSimplifiedParticles;
- AZStd::vector<SimIndexType> meshSimplifiedIndices;
- AZ::Interface<IFabricCooker>::Get()->SimplifyMesh(
- m_meshClothInfo.m_particles, m_meshClothInfo.m_indices,
- meshSimplifiedParticles, meshSimplifiedIndices,
- m_meshRemappedVertices,
- m_config.m_removeStaticTriangles);
- if (meshSimplifiedParticles.empty() ||
- meshSimplifiedIndices.empty())
- {
- return false;
- }
- // Cook Fabric
- AZStd::optional<FabricCookedData> cookedData =
- AZ::Interface<IFabricCooker>::Get()->CookFabric(meshSimplifiedParticles, meshSimplifiedIndices);
- if (!cookedData)
- {
- return false;
- }
- // Create cloth instance
- m_cloth = AZ::Interface<IClothSystem>::Get()->CreateCloth(meshSimplifiedParticles, *cookedData);
- if (!m_cloth)
- {
- return false;
- }
- // Set initial Position and Rotation
- AZ::Transform transform = AZ::Transform::CreateIdentity();
- AZ::TransformBus::EventResult(transform, m_entityId, &AZ::TransformInterface::GetWorldTM);
- TeleportCloth(transform);
- ApplyConfigurationToCloth();
- // Add cloth to default solver to be simulated
- AZ::Interface<IClothSystem>::Get()->AddCloth(m_cloth);
- return true;
- }
- void ClothComponentMesh::ApplyConfigurationToCloth()
- {
- IClothConfigurator* clothConfig = m_cloth->GetClothConfigurator();
- // Mass
- clothConfig->SetMass(m_config.m_mass);
- // Gravity and scale
- if (m_config.IsUsingWorldBusGravity())
- {
- AZ::Vector3 gravity = AzPhysics::DefaultGravity;
- if (auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get())
- {
- AzPhysics::SceneHandle defaultScene = sceneInterface->GetSceneHandle(AzPhysics::DefaultPhysicsSceneName);
- if (defaultScene != AzPhysics::InvalidSceneHandle)
- {
- gravity = sceneInterface->GetGravity(defaultScene);
- }
- }
- clothConfig->SetGravity(gravity * m_config.m_gravityScale);
- }
- else
- {
- clothConfig->SetGravity(m_config.m_customGravity * m_config.m_gravityScale);
- }
- // Stiffness Frequency
- clothConfig->SetStiffnessFrequency(m_config.m_stiffnessFrequency);
- // Motion constraints parameters
- clothConfig->SetMotionConstraintsScale(m_config.m_motionConstraintsScale);
- clothConfig->SetMotionConstraintsBias(m_config.m_motionConstraintsBias);
- clothConfig->SetMotionConstraintsStiffness(m_config.m_motionConstraintsStiffness);
- // Damping parameters
- clothConfig->SetDamping(m_config.m_damping);
- clothConfig->SetDampingLinearDrag(m_config.m_linearDrag);
- clothConfig->SetDampingAngularDrag(m_config.m_angularDrag);
- // Inertia parameters
- clothConfig->SetLinearInertia(m_config.m_linearInteria);
- clothConfig->SetAngularInertia(m_config.m_angularInteria);
- clothConfig->SetCentrifugalInertia(m_config.m_centrifugalInertia);
- // Wind parameters
- if (m_config.IsUsingWindBus())
- {
- clothConfig->SetWindVelocity(GetWindBusVelocity());
- }
- else
- {
- clothConfig->SetWindVelocity(m_config.m_windVelocity);
- }
- clothConfig->SetWindDragCoefficient(m_config.m_airDragCoefficient);
- clothConfig->SetWindLiftCoefficient(m_config.m_airLiftCoefficient);
- clothConfig->SetWindFluidDensity(m_config.m_fluidDensity);
- // Collision parameters
- clothConfig->SetCollisionFriction(m_config.m_collisionFriction);
- clothConfig->SetCollisionMassScale(m_config.m_collisionMassScale);
- clothConfig->EnableContinuousCollision(m_config.m_continuousCollisionDetection);
- clothConfig->SetCollisionAffectsStaticParticles(m_config.m_collisionAffectsStaticParticles);
- // Self Collision parameters
- clothConfig->SetSelfCollisionDistance(m_config.m_selfCollisionDistance);
- clothConfig->SetSelfCollisionStiffness(m_config.m_selfCollisionStiffness);
- // Tether Constraints parameters
- clothConfig->SetTetherConstraintStiffness(m_config.m_tetherConstraintStiffness);
- clothConfig->SetTetherConstraintScale(m_config.m_tetherConstraintScale);
- // Quality parameters
- clothConfig->SetSolverFrequency(m_config.m_solverFrequency);
- clothConfig->SetAcceleationFilterWidth(m_config.m_accelerationFilterIterations);
- // Fabric Phases
- clothConfig->SetVerticalPhaseConfig(
- m_config.m_verticalStiffness,
- m_config.m_verticalStiffnessMultiplier,
- m_config.m_verticalCompressionLimit,
- m_config.m_verticalStretchLimit);
- clothConfig->SetHorizontalPhaseConfig(
- m_config.m_horizontalStiffness,
- m_config.m_horizontalStiffnessMultiplier,
- m_config.m_horizontalCompressionLimit,
- m_config.m_horizontalStretchLimit);
- clothConfig->SetBendingPhaseConfig(
- m_config.m_bendingStiffness,
- m_config.m_bendingStiffnessMultiplier,
- m_config.m_bendingCompressionLimit,
- m_config.m_bendingStretchLimit);
- clothConfig->SetShearingPhaseConfig(
- m_config.m_shearingStiffness,
- m_config.m_shearingStiffnessMultiplier,
- m_config.m_shearingCompressionLimit,
- m_config.m_shearingStretchLimit);
- }
- void ClothComponentMesh::MoveCloth(const AZ::Transform& worldTransform)
- {
- m_worldPosition = worldTransform.GetTranslation();
- m_cloth->GetClothConfigurator()->SetTransform(worldTransform);
- if (m_config.IsUsingWindBus())
- {
- // Wind velocity is affected by world position
- m_cloth->GetClothConfigurator()->SetWindVelocity(GetWindBusVelocity());
- }
- }
- void ClothComponentMesh::TeleportCloth(const AZ::Transform& worldTransform)
- {
- MoveCloth(worldTransform);
- // By clearing inertia the cloth won't be affected by the sudden translation caused when teleporting the entity.
- m_cloth->GetClothConfigurator()->ClearInertia();
- }
- AZ::Vector3 ClothComponentMesh::GetWindBusVelocity()
- {
- const Physics::WindRequests* windRequests = AZ::Interface<Physics::WindRequests>::Get();
- if (windRequests)
- {
- const AZ::Vector3 globalWind = windRequests->GetGlobalWind();
- const AZ::Vector3 localWind = windRequests->GetWind(m_worldPosition);
- return globalWind + localWind;
- }
- return AZ::Vector3::CreateZero();
- }
- void ClothComponentMesh::EnableSkinning() const
- {
- if (m_actorClothSkinning)
- {
- for (const auto& subMeshInfo : m_meshNodeInfo.m_subMeshes)
- {
- AZ::Render::SkinnedMeshOverrideRequestBus::Event(
- m_entityId,
- &AZ::Render::SkinnedMeshOverrideRequestBus::Events::EnableSkinning,
- aznumeric_cast<uint32_t>(m_meshNodeInfo.m_lodLevel), aznumeric_cast<uint32_t>(subMeshInfo.m_primitiveIndex));
- }
- }
- }
- void ClothComponentMesh::DisableSkinning() const
- {
- if (m_actorClothSkinning)
- {
- for (const auto& subMeshInfo : m_meshNodeInfo.m_subMeshes)
- {
- AZ::Render::SkinnedMeshOverrideRequestBus::Event(
- m_entityId,
- &AZ::Render::SkinnedMeshOverrideRequestBus::Events::DisableSkinning,
- aznumeric_cast<uint32_t>(m_meshNodeInfo.m_lodLevel), aznumeric_cast<uint32_t>(subMeshInfo.m_primitiveIndex));
- }
- }
- }
- } // namespace NvCloth
|