2018-05-18 19:39:22 -03:00

354 lines
7.2 KiB
C++

/*
* Copyright (c) 2016-2016 Irlan Robson http://www.irlan.net
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#ifndef B3_SPRING_CLOTH_H
#define B3_SPRING_CLOTH_H
#include <bounce/common/math/mat33.h>
#include <bounce/common/template/array.h>
// Maximum number of shapes per cloth.
#define B3_CLOTH_SHAPE_CAPACITY 32
class b3StackAllocator;
class b3Shape;
struct b3Mesh;
struct b3SpringClothDef
{
b3SpringClothDef()
{
allocator = nullptr;
mesh = nullptr;
density = 0.0f;
ks = 0.0f;
kb = 0.0f;
kd = 0.0f;
r = 0.05f;
gravity.SetZero();
}
// Stack allocator
b3StackAllocator* allocator;
// Cloth mesh
b3Mesh* mesh;
// Cloth density in kg/m^3
float32 density;
// Streching stiffness
float32 ks;
// Bending stiffness
float32 kb;
// Damping stiffness
float32 kd;
// Mass radius
// Typically this is value is small and is intended for correcting visual artifacts when
// the cloth is colliding against a solid.
float32 r;
// Acceleration due to gravity (m/s^2)
b3Vec3 gravity;
};
enum b3SpringType
{
e_strechSpring,
e_bendSpring
};
struct b3Spring
{
// Spring type
b3SpringType type;
// Mass 1
u32 i1;
// Mass 2
u32 i2;
// Rest length
float32 L0;
// Structural stiffness
float32 ks;
// Damping stiffness
float32 kd;
};
// Static masses have zero mass and velocity, and therefore they can't move.
// Kinematic masses are't moved by external and internal forces but can be moved by contact forces.
// Dynamic masses have non-zero mass and can move due to internal and external forces.
enum class b3MassType : u32
{
e_staticMass,
e_kinematicMass,
e_dynamicMass
};
//
struct b3MassContact
{
u32 j;
b3Vec3 n, t1, t2;
float32 Fn, Ft1, Ft2;
bool lockN, lockT1, lockT2;
};
// Time step statistics
struct b3SpringClothStep
{
u32 iterations;
};
// This class implements a cloth. It treats cloth as a collection
// of masses connected by springs.
// Large time steps can be taken.
// If accuracy and stability are required, not performance,
// you can use this class instead of using b3Cloth.
class b3SpringCloth
{
public:
b3SpringCloth();
~b3SpringCloth();
// Initialize this cloth from a definition.
void Initialize(const b3SpringClothDef& def);
// Return the cloth mesh used to initialize this cloth.
b3Mesh* GetMesh() const;
// Set the gravitational acceleration applied to this cloth.
// Units are m/s^2.
void SetGravity(const b3Vec3& gravity);
// Return the number of masses in this cloth.
u32 GetMassCount() const;
// Return the gravitational acceleration applied to this cloth.
const b3Vec3& GetGravity() const;
// Set the type of a given point mass.
void SetType(u32 i, b3MassType type);
// Return the type of a given point mass.
b3MassType GetType(u32 i) const;
// Set the position of a given point mass.
// This function will have effect on the position of the point mass
// after performing a time step.
void SetPosition(u32 i, const b3Vec3& position);
// Return the position of a given point mass.
const b3Vec3& GetPosition(u32 i) const;
// Set the velocity of a given point mass.
void SetVelocity(u32 i, const b3Vec3& velocity);
// Return the velocity of a given point mass.
const b3Vec3& GetVelocity(u32 i) const;
// Apply a force to a given point mass.
void ApplyForce(u32 i, const b3Vec3& force);
// Return the kinetic (or dynamic) energy in this system.
float32 GetEnergy() const;
// Return the tension forces (due to springs) of each point mass.
// Units are kg * m / s^2
void GetTension(b3Array<b3Vec3>& tensions) const;
// Add a shape to the list of shapes in this cloth.
// The cloth will be able to respond to collisions with each shape in the list of shapes.
void AddShape(b3Shape* shape);
// Return the number of shapes added to this cloth.
u32 GetShapeCount() const;
// Return the list of shapes added to this cloth.
b3Shape** GetShapes();
// Return the statistics of the last time step.
const b3SpringClothStep& GetStep() const;
// Perform a time step (marches time forward).
void Step(float32 dt);
// Set the positions of the mesh vertices to the positions of their associated point masses.
void Apply() const;
// Debug draw the cloth mesh.
void Draw() const;
protected:
friend class b3SpringSolver;
// Update contacts.
// This is where some contacts might be initiated or terminated.
void UpdateContacts();
b3StackAllocator* m_allocator;
b3Mesh* m_mesh;
float32 m_r;
b3Vec3 m_gravity;
b3Vec3* m_x;
b3Vec3* m_v;
b3Vec3* m_f;
float32* m_m;
float32* m_inv_m;
b3Vec3* m_y;
b3MassType* m_types;
u32 m_massCount;
b3MassContact* m_contacts;
b3Spring* m_springs;
u32 m_springCount;
b3Shape* m_shapes[B3_CLOTH_SHAPE_CAPACITY];
u32 m_shapeCount;
b3SpringClothStep m_step;
};
inline b3Mesh* b3SpringCloth::GetMesh() const
{
return m_mesh;
}
inline const b3Vec3& b3SpringCloth::GetGravity() const
{
return m_gravity;
}
inline void b3SpringCloth::SetGravity(const b3Vec3& gravity)
{
m_gravity = gravity;
}
inline u32 b3SpringCloth::GetMassCount() const
{
return m_massCount;
}
inline b3MassType b3SpringCloth::GetType(u32 i) const
{
B3_ASSERT(i < m_massCount);
return m_types[i];
}
inline void b3SpringCloth::SetType(u32 i, b3MassType type)
{
B3_ASSERT(i < m_massCount);
if (m_types[i] == type)
{
return;
}
m_types[i] = type;
m_f[i].SetZero();
if (type == b3MassType::e_staticMass)
{
m_v[i].SetZero();
m_y[i].SetZero();
m_contacts[i].lockN = false;
}
}
inline void b3SpringCloth::SetPosition(u32 i, const b3Vec3& position)
{
B3_ASSERT(i < m_massCount);
m_y[i] += position - m_x[i];
}
inline const b3Vec3& b3SpringCloth::GetPosition(u32 i) const
{
B3_ASSERT(i < m_massCount);
return m_x[i];
}
inline void b3SpringCloth::SetVelocity(u32 i, const b3Vec3& velocity)
{
B3_ASSERT(i < m_massCount);
if (m_types[i] == b3MassType::e_staticMass)
{
return;
}
m_v[i] = velocity;
}
inline const b3Vec3& b3SpringCloth::GetVelocity(u32 i) const
{
B3_ASSERT(i < m_massCount);
return m_v[i];
}
inline void b3SpringCloth::ApplyForce(u32 i, const b3Vec3& force)
{
B3_ASSERT(i < m_massCount);
if (m_types[i] != b3MassType::e_dynamicMass)
{
return;
}
m_f[i] += force;
}
inline float32 b3SpringCloth::GetEnergy() const
{
float32 E = 0.0f;
for (u32 i = 0; i < m_massCount; ++i)
{
E += m_m[i] * b3Dot(m_v[i], m_v[i]);
}
return 0.5f * E;
}
inline u32 b3SpringCloth::GetShapeCount() const
{
return m_shapeCount;
}
inline b3Shape** b3SpringCloth::GetShapes()
{
return m_shapes;
}
inline const b3SpringClothStep& b3SpringCloth::GetStep() const
{
return m_step;
}
#endif