Создадим класс, описывающий простейший механический объект — материальную точку (частицу). Назовем этот класс 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