Перейдем к моделированию движения нити, то есть цепочки частиц, связанных пружинами (см. 40-й урок NeHe). Для этого создадим класс Rope
:
const unsigned numberOfParticles = 20;
class Rope : public Application
{
Particle particle[numberOfParticles];
ForceRegistry registry;
const double stiffness;
const double normalLength;
Vector3 anchor;
AnchoredSpring anchoredSpring;
Spring spring[2*(numberOfParticles-1)];
void render();
public:
Rope();
virtual const char* getTitle() { return "Rope"; }
virtual void update();
virtual void display();
};
Частицы хранятся в статическом массиве particle
, пружины — в массиве spring
. Первая из частиц, составляющих трос, связана с точкой крепления при помощи объекта класса AnchoredSpring
. Кроме того, задаются координаты точки крепления anchor
, жесткость пружин stiffness
и длина нерастянутой нити (пружины) normalLength
.
Силы, действующие на частицы, регистрируется в реестре registry
.
Рассмотрим конструктор класса Rope
:
Rope::Rope()
: stiffness(20.0),
normalLength(10.0/numberOfParticles),
anchor(0.0, 20.0, 20.0),
anchoredSpring(&anchor, stiffness, normalLength)
{
for (unsigned i = 0; i < numberOfParticles; i++) {
particle[i].setPosition(0.0, 20.0, 20+(i+1)*normalLength);
particle[i].setVelocity(0.0, 0.0, 0.0);
particle[i].setMass(2.0/numberOfParticles);
particle[i].setDamping(0.9);
particle[i].setAcceleration(0.0, -10.0, 0.0);
particle[i].clearAccumulator();
}
for (unsigned i = 0, j = 0; i < numberOfParticles-1; i++, j+=2) {
spring[j] = Spring(&particle[i+1], stiffness, normalLength);
spring[j+1] = Spring(&particle[i], stiffness, normalLength);
}
registry.add(&particle[0], &anchoredSpring);
for (unsigned i = 0, j = 0; i < numberOfParticles-1; i++, j+=2) {
registry.add(&particle[i], &spring[j]);
registry.add(&particle[i+1], &spring[j+1]);
}
}
Заметим, что сила тяжести представлена не с помощью класса наследника от Force
, а через ускорение частицы, как в нашем первом примере. Поскольку в рассматриваемом случае тяжести постоянна и действует на протяжении всего периода моделирования, такой подход ведет к меньшим вычислительным затратам. Действительно, в этом случае в классе частиц используется дополнительное поле, тогда как иначе пришлось бы создавать для каждой частицы новый объект — силу Gravity
, — и затем добавлять ее в реестр.
Рисование нити выполняется функцией render()
. Нить представлена ломаной линией, вершины которой соответствуют частицам. Толщина линии определяется функцией glLineWidth()
.
void Rope::render()
{
glLineWidth(2);
glColor3f(0.0f, 0.0f, 0.0f);
glBegin(GL_LINES);
glVertex3f(anchor.x,
anchor.y,
anchor.z);
glVertex3f(particle[0].getPosition().x,
particle[0].getPosition().y,
particle[0].getPosition().z);
glEnd();
for (unsigned i = 0; i < numberOfParticles-1; i++) {
glBegin(GL_LINES);
glVertex3f(particle[i].getPosition().x,
particle[i].getPosition().y,
particle[i].getPosition().z);
glVertex3f(particle[i+1].getPosition().x,
particle[i+1].getPosition().y,
particle[i+1].getPosition().z);
glEnd();
}
}
Однако моделирование движения нити, выполненное с помощью рассмотренного выше кода, приведет к физически бессмысленным результатам. Вместо того, чтобы стабилизироваться возле положения устойчивого равновесия (прямой, проходящей через точку крепления параллельно оси y
), нить будет раскачиваться, все больше удаляясь от равновесия.
Причиной подобного поведения нити является некорректный выбор шага численного интегрирования.
Комментарии
comments powered by Disqus