Перейдем к моделированию движения нити, то есть цепочки частиц, связанных пружинами (см. 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