// David Eberly, Geometric Tools, Redmond WA 98052 // Copyright (c) 1998-2020 // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt // Version: 4.0.2019.08.13 #pragma once #include #include namespace WwiseGTE { template class MassSpringArbitrary : public ParticleSystem { public: // Construction and destruction. This class represents a set of M // masses that are connected by S springs with arbitrary topology. // The function SetSpring(...) should be called for each spring that // you want in the system. virtual ~MassSpringArbitrary() = default; MassSpringArbitrary(int numParticles, int numSprings, Real step) : ParticleSystem(numParticles, step), mSpring(numSprings, Spring()), mAdjacent(numParticles) { } struct Spring { Spring() : particle0(0), particle1(0), constant((Real)0), length((Real)0) { } int particle0, particle1; Real constant, length; }; // Member access. inline int GetNumSprings() const { return static_cast(mSpring.size()); } void SetSpring(int index, Spring const& spring) { mSpring[index] = spring; mAdjacent[spring.particle0].insert(index); mAdjacent[spring.particle1].insert(index); } inline Spring const& GetSpring(int index) const { return mSpring[index]; } // The default external force is zero. Derive a class from this one // to provide nonzero external forces such as gravity, wind, friction, // and so on. This function is called by Acceleration(...) to compute // the impulse F/m generated by the external force F. virtual Vector ExternalAcceleration(int, Real, std::vector> const&, std::vector> const&) { return Vector::Zero(); } protected: // Callback for acceleration (ODE solver uses x" = F/m) applied to // particle i. The positions and velocities are not necessarily // mPosition and mVelocity, because the ODE solver evaluates the // impulse function at intermediate positions. virtual Vector Acceleration(int i, Real time, std::vector> const& position, std::vector> const& velocity) { // Compute spring forces on position X[i]. The positions are not // necessarily mPosition, because the RK4 solver in ParticleSystem // evaluates the acceleration function at intermediate positions. Vector acceleration = ExternalAcceleration(i, time, position, velocity); for (auto adj : mAdjacent[i]) { // Process a spring connected to particle i. Spring const& spring = mSpring[adj]; Vector diff; if (i != spring.particle0) { diff = position[spring.particle0] - position[i]; } else { diff = position[spring.particle1] - position[i]; } Real ratio = spring.length / Length(diff); Vector force = spring.constant * ((Real)1 - ratio) * diff; acceleration += this->mInvMass[i] * force; } return acceleration; } std::vector mSpring; // Each particle has an associated array of spring indices for those // springs adjacent to the particle. The set elements are spring // indices, not indices of adjacent particles. std::vector> mAdjacent; }; }