Создадим класс, описывающий простейший механический объект — материальную точку (частицу). Назовем этот класс Particle
. Каждая частица характеризуется положением (position
), скоростью (velocity
) и ускорением (acceleration
) (подробнее, см. книгу Миллингтона или 39-ый урок NeHe).
class Particle
{
protected:
double inverseMass;
double damping;
Vector3 position;
Vector3 velocity;
Vector3 acceleration;
Vector3 forceAccum;
public:
void integrate(double duration);
...
void clearAccumulator();
};
Поля класса сделаны защищенными (protected
), поскольку в дальнейшем возможно развитие класса Particle
при помощи наследования.
Кроме того, частица имеет массу. Можно было бы, также как и выше, включить в класс поле для хранения массы, однако здесь есть некоторая проблема. Поскольку при вычислении ускорения используется сила, деленная на массу частицы, и, в принципе, возможна ситуация, когда масса окажется очень малой или нулевой, это может создать трудности при выполнении численных расчетов. Поэтому удобнее использовать не саму массу, а обратную ей величину (inverseMass
). Дополнительное удобство состоит в том, что частицы с бесконечной массой (неподвижные) имеют нулевую обратную массу и попросту исключаются из расчета движения.
Класс Particle
включает в себя метод integrate()
, реализующий численное интегрирование уравнений движения, и позволяющий вычислить положение и скорость частицы спустя заданный промежуток времени.
В данном случае нам не требуется высокая точность расчетов, и мы будем использовать для численного интегрирования метод Эйлера. Этот метод прост в реализации, однако из-за его погрешностей может возникнуть ситуация, когда скорость частицы неожиданно увеличивается — как будто она получила дополнительную энергию. Поэтому при выполнении интегрирования, мы будет искусственно уменьшать скорость, участвующую в вычислении очередного положения частицы. Для описания этого численного затухания введем поле damping
. Оно будет хранить долю скорости, используемую в расчетах положения частицы. Если damping = 1
, это означает, что вся скорость участвует в расчетах (и возможны проблемы численного интегрирования), damping = 0
вызовет остановку частицы. Предпочтительно выбрать значение damping
немного меньшим единицы, например, равным 0,999.
'-Численное интегрирование уравнений движения частицы — вычисление, на основе известных начального положения и скорости частицы, ее положения спустя заданный промежуток времени.-'
Наконец, в поле forceAccum
хранится вектор суммы всех сил, действующих на частицу, а метод clearAccumulator()
обнуляет этот вектор.
Кроме методов integrate()
и clearAccumulator()
, в классе содержатся обычные методы доступа к полям (геттеры) и изменения значений полей (сеттеры).
Полностью, интерфейс класса Particle
имеет вид:
class Particle
{
protected:
double inverseMass;
double damping;
Vector3 position;
Vector3 velocity;
Vector3 acceleration;
Vector3 forceAccum;
public:
void integrate(double duration);
void setMass(const double mass);
double getMass() const;
void setInverseMass(const double inverseMass);
double getInverseMass() const;
bool hasFiniteMass() const;
void setDamping(const double damping);
double getDamping() const;
void setPosition(const Vector3 &position);
void setPosition(const double x, const double y, const double z);
void getPosition(Vector3 *position) const;
Vector3 getPosition() const;
void setVelocity(const Vector3 &velocity);
void setVelocity(const double x, const double y, const double z);
void getVelocity(Vector3 *velocity) const;
Vector3 getVelocity() const;
void setAcceleration(const Vector3 &acceleration);
void setAcceleration(const double x, const double y, const double z);
void getAcceleration(Vector3 *acceleration) const;
Vector3 getAcceleration() const;
void clearAccumulator();
};
А вот как реализован метод integrator()
:
void Particle::integrate(double duration)
{
// Состояние частиц с бесконечной массой пересчитывать не нужно
if (inverseMass <= 0.0f) return;
assert(duration > 0.0);
// Вычисляем положение частицы
// position = position + velocity * duration
position.addScaledVector(velocity, duration);
// Находим ускорение с помощью силы:
// acceleration = force * inverseMass
Vector3 resultingAcc = acceleration;
resultingAcc.addScaledVector(forceAccum, inverseMass);
// Вычисляем скорость с помощью ускорения:
// velocity = velocity + acceleration * duration
velocity.addScaledVector(resultingAcc, duration);
// Добавляем "численное" затухание
velocity *= pow(damping, duration);
// Обнуляем действующие силы
clearAccumulator();
}
Комментарии
comments powered by Disqus