Fix Cloth with models that use compressed vertex buffer

#4017
This commit is contained in:
Wojtek Figat
2026-03-24 22:59:21 +01:00
parent a63e05d444
commit 29abfbcdc9
9 changed files with 186 additions and 116 deletions

View File

@@ -39,51 +39,61 @@ public:
FORCE_INLINE int32 GetInt(int32 index) const
{
ASSERT_LOW_LAYER(index * _stride < _data.Length());
return (int32)_sampler.Read(_data.Get() + index * _stride).X;
}
FORCE_INLINE float GetFloat(int32 index) const
{
ASSERT_LOW_LAYER(index * _stride < _data.Length());
return _sampler.Read(_data.Get() + index * _stride).X;
}
FORCE_INLINE Float2 GetFloat2(int32 index) const
{
ASSERT_LOW_LAYER(index * _stride < _data.Length());
return Float2(_sampler.Read(_data.Get() + index * _stride));
}
FORCE_INLINE Float3 GetFloat3(int32 index) const
{
ASSERT_LOW_LAYER(index * _stride < _data.Length());
return Float3(_sampler.Read(_data.Get() + index * _stride));
}
FORCE_INLINE Float4 GetFloat4(int32 index) const
{
ASSERT_LOW_LAYER(index * _stride < _data.Length());
return _sampler.Read(_data.Get() + index * _stride);
}
FORCE_INLINE void SetInt(int32 index, const int32 value)
{
ASSERT_LOW_LAYER(index * _stride < _data.Length());
_sampler.Write(_data.Get() + index * _stride, Float4((float)value));
}
FORCE_INLINE void SetFloat(int32 index, const float value)
{
ASSERT_LOW_LAYER(index * _stride < _data.Length());
_sampler.Write(_data.Get() + index * _stride, Float4(value));
}
FORCE_INLINE void SetFloat2(int32 index, const Float2& value)
{
ASSERT_LOW_LAYER(index * _stride < _data.Length());
_sampler.Write(_data.Get() + index * _stride, Float4(value));
}
FORCE_INLINE void SetFloat3(int32 index, const Float3& value)
{
ASSERT_LOW_LAYER(index * _stride < _data.Length());
_sampler.Write(_data.Get() + index * _stride, Float4(value));
}
FORCE_INLINE void SetFloat4(int32 index, const Float4& value)
{
ASSERT_LOW_LAYER(index * _stride < _data.Length());
_sampler.Write(_data.Get() + index * _stride, value);
}
@@ -94,11 +104,22 @@ public:
void Set(Span<Float2> src);
void Set(Span<Float3> src);
void Set(Span<Color> src);
template<typename T>
void Set(const Array<T>& dst) const
{
Set(Span<T>(dst.Get(), dst.Count()));
}
// Copies the contents of this stream into a destination data span.
void CopyTo(Span<Float2> dst) const;
void CopyTo(Span<Float3> dst) const;
void CopyTo(Span<Color> dst) const;
template<typename T>
void CopyTo(Array<T>& dst) const
{
dst.Resize(GetCount());
CopyTo(Span<T>(dst.Get(), dst.Count()));
}
};
private:

View File

@@ -1397,6 +1397,16 @@ bool AnimatedModel::GetMeshData(const MeshReference& ref, MeshBufferType type, B
return mesh.DownloadDataCPU(type, result, count, layout);
}
MeshBase* AnimatedModel::GetMesh(const MeshReference& ref) const
{
const auto model = SkinnedModel.Get();
if (!model || model->WaitForLoaded())
return nullptr;
auto& lod = model->LODs[Math::Min(ref.LODIndex, model->LODs.Count() - 1)];
auto& mesh = lod.Meshes[Math::Min(ref.MeshIndex, lod.Meshes.Count() - 1)];
return &mesh;
}
MeshDeformation* AnimatedModel::GetMeshDeformation() const
{
if (!_deformation)

View File

@@ -479,6 +479,7 @@ public:
bool IntersectsEntry(int32 entryIndex, const Ray& ray, Real& distance, Vector3& normal) override;
bool IntersectsEntry(const Ray& ray, Real& distance, Vector3& normal, int32& entryIndex) override;
bool GetMeshData(const MeshReference& ref, MeshBufferType type, BytesContainer& result, int32& count, GPUVertexLayout** layout) const override;
MeshBase* GetMesh(const MeshReference& ref) const override;
void UpdateBounds() override;
MeshDeformation* GetMeshDeformation() const override;
void OnDeleteObject() override;

View File

@@ -14,6 +14,12 @@ String ModelInstanceActor::MeshReference::ToString() const
return String::Format(TEXT("Actor={},LOD={},Mesh={}"), Actor ? Actor->GetNamePath() : String::Empty, LODIndex, MeshIndex);
}
MeshBase* ModelInstanceActor::MeshReference::Get() const
{
auto actor = Actor.Get();
return actor ? actor->GetMesh(*this) : nullptr;
}
void ModelInstanceActor::SetEntries(const Array<ModelInstanceEntry>& value)
{
WaitForModelLoad();

View File

@@ -29,6 +29,7 @@ API_CLASS(Abstract) class FLAXENGINE_API ModelInstanceActor : public Actor
API_FIELD() int32 MeshIndex = 0;
String ToString() const;
MeshBase* Get() const;
};
protected:
@@ -113,6 +114,7 @@ public:
/// <summary>
/// Extracts mesh buffer data from CPU. Might be cached internally (eg. by Model/SkinnedModel).
/// [Deprecated in 1.12]
/// </summary>
/// <param name="ref">Mesh reference.</param>
/// <param name="type">Buffer type</param>
@@ -120,11 +122,22 @@ public:
/// <param name="count">The amount of items inside the result buffer.</param>
/// <param name="layout">The result layout of the result buffer (for vertex buffers). Optional, pass null to ignore it.</param>
/// <returns>True if failed, otherwise false.</returns>
DEPRECATED("Use GetMesh to resolve mesh reference and access mesh data with MeshAccessor.")
virtual bool GetMeshData(const MeshReference& ref, MeshBufferType type, BytesContainer& result, int32& count, GPUVertexLayout** layout = nullptr) const
{
return true;
}
/// <summary>
/// Resolves a given mesh reference.
/// </summary>
/// <param name="ref">Mesh reference.</param>
/// <returns>Mesh or null if invalid ref.</returns>
virtual MeshBase* GetMesh(const MeshReference& ref) const
{
return nullptr;
}
/// <summary>
/// Gets the mesh deformation utility for this model instance (optional).
/// </summary>

View File

@@ -665,6 +665,16 @@ bool StaticModel::GetMeshData(const MeshReference& ref, MeshBufferType type, Byt
return mesh.DownloadDataCPU(type, result, count, layout);
}
MeshBase* StaticModel::GetMesh(const MeshReference& ref) const
{
const auto model = Model.Get();
if (!model || model->WaitForLoaded())
return nullptr;
auto& lod = model->LODs[Math::Min(ref.LODIndex, model->LODs.Count() - 1)];
auto& mesh = lod.Meshes[Math::Min(ref.MeshIndex, lod.Meshes.Count() - 1)];
return &mesh;
}
MeshDeformation* StaticModel::GetMeshDeformation() const
{
if (!_deformation)

View File

@@ -181,6 +181,7 @@ public:
bool IntersectsEntry(int32 entryIndex, const Ray& ray, Real& distance, Vector3& normal) override;
bool IntersectsEntry(const Ray& ray, Real& distance, Vector3& normal, int32& entryIndex) override;
bool GetMeshData(const MeshReference& ref, MeshBufferType type, BytesContainer& result, int32& count, GPUVertexLayout** layout) const override;
MeshBase* GetMesh(const MeshReference& ref) const override;
MeshDeformation* GetMeshDeformation() const override;
void UpdateBounds() override;

View File

@@ -208,8 +208,12 @@ void Cloth::SetPaint(Span<const float> value)
if (_cloth)
{
// Update cloth particles
MeshAccessor accessor;
MeshBufferType bufferTypes[2] = { MeshBufferType::Index, MeshBufferType::Vertex0 };
if (accessor.LoadMesh(GetMesh().Get(), false, ToSpan(bufferTypes, 2)))
return;
Array<float> invMasses;
CalculateInvMasses(invMasses);
CalculateInvMasses(accessor, invMasses);
PhysicsBackend::LockClothParticles(_cloth);
PhysicsBackend::SetClothParticles(_cloth, Span<const Float4>(), Span<const Float3>(), ToSpan<float, const float>(invMasses));
PhysicsBackend::SetClothPaint(_cloth, value);
@@ -227,18 +231,20 @@ bool Cloth::IntersectsItself(const Ray& ray, Real& distance, Vector3& normal)
if (_cloth)
{
// Precise per-triangle intersection
const ModelInstanceActor::MeshReference mesh = GetMesh();
if (mesh.Actor == nullptr)
const ModelInstanceActor::MeshReference meshRef = GetMesh();
if (meshRef.Actor == nullptr)
return false;
BytesContainer indicesData;
int32 indicesCount;
if (mesh.Actor->GetMeshData(mesh, MeshBufferType::Index, indicesData, indicesCount))
MeshAccessor accessor;
MeshBufferType bufferTypes[1] = { MeshBufferType::Index };
if (accessor.LoadMesh(meshRef.Get(), false, ToSpan(bufferTypes, 1)))
return false;
auto indices = accessor.Index();
auto indicesData = indices.GetData();
PhysicsBackend::LockClothParticles(_cloth);
const Span<const Float4> particles = PhysicsBackend::GetClothParticles(_cloth);
const Transform transform = GetTransform();
const bool indices16bit = indicesData.Length() / indicesCount == sizeof(uint16);
const int32 trianglesCount = indicesCount / 3;
const bool indices16bit = indices.GetFormat() == PixelFormat::R16_UInt;
const int32 trianglesCount = indices.GetCount() / 3;
bool result = false;
distance = MAX_Real;
for (int32 triangleIndex = 0; triangleIndex < trianglesCount; triangleIndex++)
@@ -341,18 +347,20 @@ void Cloth::DrawPhysicsDebug(RenderView& view)
if (_cloth)
{
PROFILE_CPU();
const ModelInstanceActor::MeshReference mesh = GetMesh();
if (mesh.Actor == nullptr)
const ModelInstanceActor::MeshReference meshRef = GetMesh();
if (meshRef.Actor == nullptr)
return;
BytesContainer indicesData;
int32 indicesCount;
if (mesh.Actor->GetMeshData(mesh, MeshBufferType::Index, indicesData, indicesCount))
MeshAccessor accessor;
MeshBufferType bufferTypes[1] = { MeshBufferType::Index };
if (accessor.LoadMesh(meshRef.Get(), false, ToSpan(bufferTypes, 1)))
return;
auto indices = accessor.Index();
auto indicesData = indices.GetData();
PhysicsBackend::LockClothParticles(_cloth);
const Span<const Float4> particles = PhysicsBackend::GetClothParticles(_cloth);
const Transform transform = GetTransform();
const bool indices16bit = indicesData.Length() / indicesCount == sizeof(uint16);
const int32 trianglesCount = indicesCount / 3;
const bool indices16bit = indices.GetFormat() == PixelFormat::R16_UInt;
const int32 trianglesCount = indices.GetCount() / 3;
for (int32 triangleIndex = 0; triangleIndex < trianglesCount; triangleIndex++)
{
const int32 index = triangleIndex * 3;
@@ -390,18 +398,20 @@ void Cloth::OnDebugDrawSelected()
if (_cloth)
{
DEBUG_DRAW_WIRE_BOX(_box, Color::Violet.RGBMultiplied(0.8f), 0, true);
const ModelInstanceActor::MeshReference mesh = GetMesh();
if (mesh.Actor == nullptr)
const ModelInstanceActor::MeshReference meshRef = GetMesh();
if (meshRef.Actor == nullptr)
return;
BytesContainer indicesData;
int32 indicesCount;
if (mesh.Actor->GetMeshData(mesh, MeshBufferType::Index, indicesData, indicesCount))
MeshAccessor accessor;
MeshBufferType bufferTypes[1] = { MeshBufferType::Index };
if (accessor.LoadMesh(meshRef.Get(), false, ToSpan(bufferTypes, 1)))
return;
auto indices = accessor.Index();
auto indicesData = indices.GetData();
PhysicsBackend::LockClothParticles(_cloth);
const Span<const Float4> particles = PhysicsBackend::GetClothParticles(_cloth);
const Transform transform = GetTransform();
const bool indices16bit = indicesData.Length() / indicesCount == sizeof(uint16);
const int32 trianglesCount = indicesCount / 3;
const bool indices16bit = indices.GetFormat() == PixelFormat::R16_UInt;
const int32 trianglesCount = indices.GetCount() / 3;
for (int32 triangleIndex = 0; triangleIndex < trianglesCount; triangleIndex++)
{
const int32 index = triangleIndex * 3;
@@ -556,26 +566,36 @@ bool Cloth::CreateCloth()
// Get mesh data
// TODO: consider making it via async task so physics can wait on the cloth setup from mesh data just before next fixed update which gives more time when loading scene
const ModelInstanceActor::MeshReference mesh = GetMesh();
if (mesh.Actor == nullptr)
const ModelInstanceActor::MeshReference meshRef = GetMesh();
if (meshRef.Actor == nullptr)
return false;
PhysicsClothDesc desc;
desc.Actor = this;
BytesContainer data;
int32 count;
if (mesh.Actor->GetMeshData(mesh, MeshBufferType::Vertex0, data, count))
MeshAccessor accessor;
MeshBufferType bufferTypes[2] = { MeshBufferType::Index, MeshBufferType::Vertex0 };
if (accessor.LoadMesh(meshRef.Get(), false, ToSpan(bufferTypes, 2)))
return true;
// TODO: use MeshAccessor vertex data layout descriptor instead hardcoded position data at the beginning of VB0
desc.VerticesData = data.Get();
desc.VerticesCount = count;
desc.VerticesStride = data.Length() / count;
if (mesh.Actor->GetMeshData(mesh, MeshBufferType::Index, data, count))
return true;
desc.IndicesData = data.Get();
desc.IndicesCount = count;
desc.IndicesStride = data.Length() / count;
auto position = accessor.Position();
Array<Float3> tempPositions;
if (position.GetFormat() == PixelFormat::R32G32B32_Float)
{
desc.VerticesData = position.GetData().Get();
desc.VerticesCount = position.GetCount();
desc.VerticesStride = position.GetStride();
}
else
{
position.CopyTo(tempPositions);
desc.VerticesData = tempPositions.Get();
desc.VerticesCount = tempPositions.Count();
desc.VerticesStride = sizeof(Float3);
}
auto indices = accessor.Index();
desc.IndicesData = indices.GetData().Get();
desc.IndicesCount = indices.GetCount();
desc.IndicesStride = indices.GetStride();
Array<float> invMasses;
CalculateInvMasses(invMasses);
CalculateInvMasses(accessor, invMasses);
desc.InvMassesData = invMasses.Count() == desc.VerticesCount ? invMasses.Get() : nullptr;
desc.InvMassesStride = sizeof(float);
desc.MaxDistancesData = _paint.Count() == desc.VerticesCount ? _paint.Get() : nullptr;
@@ -595,13 +615,13 @@ bool Cloth::CreateCloth()
PhysicsBackend::ClearClothInertia(_cloth);
// Add cloth mesh deformer
if (auto* deformation = mesh.Actor->GetMeshDeformation())
if (auto* deformation = meshRef.Actor->GetMeshDeformation())
{
Function<void(const MeshBase*, MeshDeformationData&)> deformer;
deformer.Bind<Cloth, &Cloth::RunClothDeformer>(this);
deformation->AddDeformer(mesh.LODIndex, mesh.MeshIndex, MeshBufferType::Vertex0, deformer);
deformation->AddDeformer(meshRef.LODIndex, meshRef.MeshIndex, MeshBufferType::Vertex0, deformer);
if (_simulationSettings.ComputeNormals)
deformation->AddDeformer(mesh.LODIndex, mesh.MeshIndex, MeshBufferType::Vertex1, deformer);
deformation->AddDeformer(meshRef.LODIndex, meshRef.MeshIndex, MeshBufferType::Vertex1, deformer);
_meshDeformation = deformation;
}
@@ -631,7 +651,7 @@ void Cloth::DestroyCloth()
#endif
}
void Cloth::CalculateInvMasses(Array<float>& invMasses)
void Cloth::CalculateInvMasses(MeshAccessor& accessor, Array<float>& invMasses)
{
// Use per-particle max distance to evaluate which particles are immovable
#if WITH_CLOTH
@@ -641,29 +661,22 @@ void Cloth::CalculateInvMasses(Array<float>& invMasses)
PROFILE_MEM(Physics);
// Get mesh data
const ModelInstanceActor::MeshReference mesh = GetMesh();
if (mesh.Actor == nullptr)
return;
BytesContainer verticesData;
int32 verticesCount;
if (mesh.Actor->GetMeshData(mesh, MeshBufferType::Vertex0, verticesData, verticesCount))
return;
BytesContainer indicesData;
int32 indicesCount;
if (mesh.Actor->GetMeshData(mesh, MeshBufferType::Index, indicesData, indicesCount))
return;
auto positions = accessor.Position();
auto indices = accessor.Index();
CHECK(positions.IsValid() && indices.IsValid());
int32 verticesCount = positions.GetCount();
if (_paint.Count() != verticesCount)
{
// Fix incorrect paint data
LOG(Warning, "Incorrect cloth '{}' paint size {} for mesh '{}' that has {} vertices", GetNamePath(), _paint.Count(), mesh.ToString(), verticesCount);
LOG(Warning, "Incorrect cloth '{}' paint size {} for mesh '{}' that has {} vertices", GetNamePath(), _paint.Count(), GetMesh().ToString(), verticesCount);
int32 countBefore = _paint.Count();
_paint.Resize(verticesCount);
for (int32 i = countBefore; i < verticesCount; i++)
_paint.Get()[i] = 0.0f;
}
const int32 verticesStride = verticesData.Length() / verticesCount;
const bool indices16bit = indicesData.Length() / indicesCount == sizeof(uint16);
const int32 trianglesCount = indicesCount / 3;
const bool indices16bit = indices.GetFormat() == PixelFormat::R16_UInt;
const int32 trianglesCount = indices.GetCount() / 3;
auto indicesData = indices.GetData();
// Sum triangle area for each influenced particle
invMasses.Resize(verticesCount);
@@ -676,12 +689,9 @@ void Cloth::CalculateInvMasses(Array<float>& invMasses)
const int32 i0 = indicesData.Get<uint16>()[index];
const int32 i1 = indicesData.Get<uint16>()[index + 1];
const int32 i2 = indicesData.Get<uint16>()[index + 2];
// TODO: use MeshAccessor vertex data layout descriptor instead hardcoded position data at the beginning of VB0
#define GET_POS(i) *(Float3*)((byte*)verticesData.Get() + i * verticesStride)
const Float3 v0(GET_POS(i0));
const Float3 v1(GET_POS(i1));
const Float3 v2(GET_POS(i2));
#undef GET_POS
const Float3 v0(positions.GetFloat3(i0));
const Float3 v1(positions.GetFloat3(i1));
const Float3 v2(positions.GetFloat3(i2));
const float area = Float3::TriangleArea(v0, v1, v2);
invMasses.Get()[i0] += area;
invMasses.Get()[i1] += area;
@@ -696,12 +706,9 @@ void Cloth::CalculateInvMasses(Array<float>& invMasses)
const int32 i0 = indicesData.Get<uint32>()[index];
const int32 i1 = indicesData.Get<uint32>()[index + 1];
const int32 i2 = indicesData.Get<uint32>()[index + 2];
// TODO: use MeshAccessor vertex data layout descriptor instead hardcoded position data at the beginning of VB0
#define GET_POS(i) *(Float3*)((byte*)verticesData.Get() + i * verticesStride)
const Float3 v0(GET_POS(i0));
const Float3 v1(GET_POS(i1));
const Float3 v2(GET_POS(i2));
#undef GET_POS
const Float3 v0(positions.GetFloat3(i0));
const Float3 v1(positions.GetFloat3(i1));
const Float3 v2(positions.GetFloat3(i2));
const float area = Float3::TriangleArea(v0, v1, v2);
invMasses.Get()[i0] += area;
invMasses.Get()[i1] += area;
@@ -787,25 +794,22 @@ bool Cloth::OnPreUpdate()
{
if (animatedModel->GraphInstance.NodesPose.IsEmpty() || _paint.IsEmpty())
return false;
const ModelInstanceActor::MeshReference mesh = GetMesh();
if (mesh.Actor == nullptr)
return false;
BytesContainer verticesData;
int32 verticesCount;
GPUVertexLayout* layout;
if (mesh.Actor->GetMeshData(mesh, MeshBufferType::Vertex0, verticesData, verticesCount, &layout))
const ModelInstanceActor::MeshReference meshRef = GetMesh();
if (meshRef.Actor == nullptr)
return false;
MeshAccessor accessor;
if (accessor.LoadBuffer(MeshBufferType::Vertex0, verticesData, layout))
MeshBufferType bufferTypes[1] = { MeshBufferType::Vertex0 };
if (accessor.LoadMesh(meshRef.Get(), false, ToSpan(bufferTypes, 1)))
return false;
auto positionStream = accessor.Position();
auto blendIndicesStream = accessor.BlendIndices();
auto blendWeightsStream = accessor.BlendWeights();
if (!positionStream.IsValid() || !blendIndicesStream.IsValid() || !blendWeightsStream.IsValid())
return false;
const int32 verticesCount = positionStream.GetCount();
if (verticesCount != _paint.Count())
{
LOG(Warning, "Incorrect cloth '{}' paint size {} for mesh '{}' that has {} vertices", GetNamePath(), _paint.Count(), mesh.ToString(), verticesCount);
LOG(Warning, "Incorrect cloth '{}' paint size {} for mesh '{}' that has {} vertices", GetNamePath(), _paint.Count(), meshRef.ToString(), verticesCount);
return false;
}
PROFILE_CPU_NAMED("Skinned Pose");
@@ -934,49 +938,53 @@ void Cloth::RunClothDeformer(const MeshBase* mesh, MeshDeformationData& deformat
// Calculate normals
Array<Float3> normals;
const ModelInstanceActor::MeshReference meshRef = GetMesh();
BytesContainer indicesData;
int32 indicesCount;
if ((_simulationSettings.ComputeNormals || deformation.Type == MeshBufferType::Vertex1) &&
meshRef.Actor && !meshRef.Actor->GetMeshData(meshRef, MeshBufferType::Index, indicesData, indicesCount))
if ((_simulationSettings.ComputeNormals || deformation.Type == MeshBufferType::Vertex1) && meshRef.Actor)
{
PROFILE_CPU_NAMED("Normals");
// TODO: optimize memory allocs (eg. use shared allocator)
normals.Resize(vbCount);
Platform::MemoryClear(normals.Get(), vbCount * sizeof(Float3));
const bool indices16bit = indicesData.Length() / indicesCount == sizeof(uint16);
const int32 trianglesCount = indicesCount / 3;
if (indices16bit)
MeshAccessor accessor;
MeshBufferType bufferTypes[1] = { MeshBufferType::Index };
if (!accessor.LoadMesh(meshRef.Get(), false, ToSpan(bufferTypes, 1)))
{
for (int32 triangleIndex = 0; triangleIndex < trianglesCount; triangleIndex++)
PROFILE_CPU_NAMED("Normals");
auto indices = accessor.Index();
auto indicesData = indices.GetData();
// TODO: optimize memory allocs (eg. use shared allocator)
normals.Resize(vbCount);
Platform::MemoryClear(normals.Get(), vbCount * sizeof(Float3));
const bool indices16bit = indices.GetFormat() == PixelFormat::R16_UInt;
const int32 trianglesCount = indices.GetCount() / 3;
if (indices16bit)
{
const int32 index = triangleIndex * 3;
const int32 i0 = indicesData.Get<uint16>()[index];
const int32 i1 = indicesData.Get<uint16>()[index + 1];
const int32 i2 = indicesData.Get<uint16>()[index + 2];
const Float3 v0(particles.Get()[i0]);
const Float3 v1(particles.Get()[i1]);
const Float3 v2(particles.Get()[i2]);
const Float3 normal = Float3::Cross(v1 - v0, v2 - v0);
normals.Get()[i0] += normal;
normals.Get()[i1] += normal;
normals.Get()[i2] += normal;
for (int32 triangleIndex = 0; triangleIndex < trianglesCount; triangleIndex++)
{
const int32 index = triangleIndex * 3;
const int32 i0 = indicesData.Get<uint16>()[index];
const int32 i1 = indicesData.Get<uint16>()[index + 1];
const int32 i2 = indicesData.Get<uint16>()[index + 2];
const Float3 v0(particles.Get()[i0]);
const Float3 v1(particles.Get()[i1]);
const Float3 v2(particles.Get()[i2]);
const Float3 normal = Float3::Cross(v1 - v0, v2 - v0);
normals.Get()[i0] += normal;
normals.Get()[i1] += normal;
normals.Get()[i2] += normal;
}
}
}
else
{
for (int32 triangleIndex = 0; triangleIndex < trianglesCount; triangleIndex++)
else
{
const int32 index = triangleIndex * 3;
const int32 i0 = indicesData.Get<uint32>()[index];
const int32 i1 = indicesData.Get<uint32>()[index + 1];
const int32 i2 = indicesData.Get<uint32>()[index + 2];
const Float3 v0(particles.Get()[i0]);
const Float3 v1(particles.Get()[i1]);
const Float3 v2(particles.Get()[i2]);
const Float3 normal = Float3::Cross(v1 - v0, v2 - v0);
normals.Get()[i0] += normal;
normals.Get()[i1] += normal;
normals.Get()[i2] += normal;
for (int32 triangleIndex = 0; triangleIndex < trianglesCount; triangleIndex++)
{
const int32 index = triangleIndex * 3;
const int32 i0 = indicesData.Get<uint32>()[index];
const int32 i1 = indicesData.Get<uint32>()[index + 1];
const int32 i2 = indicesData.Get<uint32>()[index + 2];
const Float3 v0(particles.Get()[i0]);
const Float3 v1(particles.Get()[i1]);
const Float3 v2(particles.Get()[i2]);
const Float3 normal = Float3::Cross(v1 - v0, v2 - v0);
normals.Get()[i0] += normal;
normals.Get()[i1] += normal;
normals.Get()[i2] += normal;
}
}
}
}

View File

@@ -371,6 +371,6 @@ private:
ImplementPhysicsDebug;
bool CreateCloth();
void DestroyCloth();
void CalculateInvMasses(Array<float>& invMasses);
void CalculateInvMasses(class MeshAccessor& accessor, Array<float>& invMasses);
void RunClothDeformer(const MeshBase* mesh, struct MeshDeformationData& deformation);
};