netlib.narod.ru< Назад | Оглавление | Далее >

14.3. Примеры систем частиц: снег, фейерверк, след снаряда

Давайте теперь на основе класса PSystem реализуем несколько конкретных систем частиц. Эти системы будут достаточно просты, поскольку разработаны с целью иллюстрации, и не будут в полной мере использовать преимущества гибкости, предоставляемой классом PSystem. Мы реализуем системы снегопада, фейерверка и следа снаряда. Имена этих систем явно указывают на то, какие явления они моделируют. Система снегопада моделирует падение снежинок. Система фейерверка моделирует взрыв, похожий на залп салюта. Система следа снаряда при нажатии клавиши запускает частицы из точки расположения камеры в том направлении, куда камера направлена, создавая впечатление, что мы запустили ракету. Она может использоваться как основа для имитации стрельбы из оружия в игре.

ПРИМЕЧАНИЕ
Как обычно, полный код проектов иллюстрирующих эти системы частиц находится в сопроводительных файлах к этой главе.

14.3.1. Пример приложения: снег


Рис. 14.2. Окно программы Snow

Рис. 14.2. Окно программы Snow


Определение класса системы частиц Snow выглядит следующим образом:

class Snow : public PSystem
{
public:
     Snow(d3d::BoundingBox* boundingBox, int numParticles);
     void resetParticle(Attribute* attribute);
     void update(float timeDelta);
};

 

ПРИМЕЧАНИЕ
Обратите внимание насколько прост интерфейс класса системы частиц Snow. Это объясняется тем, что большую часть работы выполняет родительский класс. Фактически, все три системы частиц, которые мы рассматриваем в этом разделе, имеют достаточно простые интерфейсы, которые относительно легко реализовать.

Конструктор получает указатель на структуру данных ограничивающего параллелепипеда и количество частиц, которое должно быть в системе. Ограничивающий параллелепипед определяет объем пространства в котором будет падать снег. Если снежинки выходят за границы этого объема, они воскрешаются с новыми координатами. Таким образом система поддерживает постоянное количество активных частиц. Реализация конструктора выглядит следующим образом:

Snow::Snow(d3d::BoundingBox* boundingBox, int numParticles)
{
     _boundingBox = *boundingBox;
     _size = 0.8f;
     _vbSize = 2048;
     _vbOffset = 0;
     _vbBatchSize = 512;

     for(int i = 0; i < numParticles; i++)
          addParticle();
}

Обратите внимание, что мы задаем размер буфера вершин, размер партии частиц и начальное смещение.

Метод resetParticle создает снежинку внутри ограничивающего параллелепипеда со случайными значениями координат X и Z, а значение координаты Y делает равным координате верха ограничивающего объема. Затем вектор скорости снежинки устанавливается таким образом, чтобы она падала вниз и при этом слегка смещалась влево. Помимо этого, снежинка окрашивается в белый цвет:

void Snow::resetParticle(Attribute* attribute)
{
     attribute->_isAlive = true;

     // Получить случайные значения координат X и Z снежинки
     d3d::GetRandomVector(
              &attribute->_position,
              &_boundingBox._min,
              &_boundingBox._max);

     // Для высоты (координаты Y) случайное значение не нужно.
     // Падение снежинок всегда начинается с верха
     // ограничивающего параллелепипеда
     attribute->_position.y = _boundingBox._max.y;

     // Снежинка падает вниз и слегка смещается влево
     attribute->_velocity.x = d3d::GetRandomFloat(0.0f, 1.0f) * -3.0f;
     attribute->_velocity.y = d3d::GetRandomFloat(0.0f, 1.0f) * -10.0f;
     attribute->_velocity.z = 0.0f;

     // Все снежинки белые
     attribute->_color = d3d::WHITE;
}

Метод update обновляет местоположение частиц, а затем проверяет не вышли ли какие-нибудь частицы за пределы ограничивающего объема системы. Если частица вышла за пределы ограничивающего параллелепипеда, она заново инициализируется и снова падает с самого верха.

void Snow::update(float timeDelta)
{
     std::list<Attribute>::iterator i;
     for(i = _particles.begin(); i != _particles.end(); i++)
     {
          i->_position += i->_velocity * timeDelta;

          // Точка вне ограничивающего объема?
          if(_boundingBox.isPointInside(i->_position) == false)
          {
               // Вышедшие за пределы объема частицы не
               // уничтожаются, а снова используются и
               // воскрешаются с новыми координатами
               resetParticle(&(*i));
          }
     }
}

14.3.2. Пример приложения: фейерверк


Рис. 14.3. Окно программы Firework

Рис. 14.3. Окно программы Firework


Определение класса системы Firework выглядит следующим образом:

class Firework : public PSystem
{
public:
     Firework(D3DXVECTOR3* origin, int numParticles);
     void resetParticle(Attribute* attribute);
     void update(float timeDelta);
     void preRender();
     void postRender();
};

Конструктор получает указатель на базовую точку системы и количество частиц в системе. В данном случае базовая точка — это точка в которой взрывается фейерверк.

Метод resetParticle инициализирует частицу, помещая ее в базовую точку системы и назначает ей случайный вектор скорости в сфере. Каждой частице системы Firework назначается выбираемый случайным образом цвет. Помимо этого, мы указываем, что время жизни частицы равно двум секундам.

void Firework::resetParticle(Attribute* attribute)
{
     attribute->_isAlive = true;
     attribute->_position = _origin;

     D3DXVECTOR3 min = D3DXVECTOR3(-1.0f, -1.0f, -1.0f);
     D3DXVECTOR3 max = D3DXVECTOR3( 1.0f, 1.0f, 1.0f);

     d3d::GetRandomVector(
            &attribute->_velocity,
            &min,
            &max);

     // Нормализация для сферы
     D3DXVec3Normalize(
            &attribute->_velocity,
            &attribute->_velocity);

     attribute->_velocity *= 100.0f;

     attribute->_color = D3DXCOLOR(
            d3d::GetRandomFloat(0.0f, 1.0f),
            d3d::GetRandomFloat(0.0f, 1.0f),
            d3d::GetRandomFloat(0.0f, 1.0f),
            1.0f);

     attribute->_age = 0.0f;
     attribute->_lifeTime = 2.0f; // время жизни - 2 секунды
}

Метод update обновляет местоположение каждой частицы и уничтожает те из ник, которые просуществовали больше заданного времени. Обратите внимание, что система не удаляет мертвые частицы. Благодаря этому, если мы захотим создать новый фейерверк, нам надо будет просто выполнить инициализацию мертвой системы фейерверка. Такой подход позволяет избежать частого создания и уничтожения частиц.

void Firework::update(float timeDelta)
{
     std::list<Attribute>::iterator i;

     for(i = _particles.begin(); i != _particles.end(); i++)
     {
          // Обновляем только живые частицы
          if(i->_isAlive)
          {
               i->_position += i->_velocity * timeDelta;

               i->_age += timeDelta;

               if(i->_age > i->_lifeTime) // убиваем
                    i->_isAlive = false;
          }
     }
}

Система фейерверка при визуализации использует собственные коэффициенты смешивания. Кроме того, она еще и запрещает запись в буфер глубины. Мы можем легко изменить коэффициенты смешивания и запретить запись в буфер глубины путем переопределения методов PSystem::preRender и PSystem::postRender. Вот переопределенная реализация:

void Firework::preRender()
{
     PSystem::preRender();

     _device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
     _device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);

     // Не записываем частицы в z-буфер
     _device->SetRenderState(D3DRS_ZWRITEENABLE, false);
}

void Firework::postRender()
{
     PSystem::postRender();

     _device->SetRenderState(D3DRS_ZWRITEENABLE, true);
}

Обратите внимание, что оба метода вызывают одноименные методы родительского класса. Это позволяет использовать всю предлагаемую родительским классом функциональность, оставив в классе Firework только минимум кода, требуемый для данной конкретной системы частиц.

14.3.3. Пример приложения: след снаряда


Рис. 14.4. Окно программы Laser (Particle Gun)

Рис. 14.4. Окно программы Laser (Particle Gun)


Определение класса системы ParticleGun выглядит следующим образом:

class ParticleGun : public PSystem
{
public:
     ParticleGun(Camera* camera);
     void resetParticle(Attribute* attribute);
     void update(float timeDelta);

private:
     Camera* _camera;
};

Конструктор получает указатель на камеру. Это объясняется тем, что данной системе для создания частиц надо знать местоположение камеры и направление, в котором она смотрит.

Метод resetParticle устанавливает координаты частицы равными текущим координатам камеры и задает вектор скорости частицы равным умноженному на сто вектору взгляда камеры. В результате «снаряд» будет выстрелен в направлении взгляда. Изображающей снаряд частице мы назначаем зеленый цвет.

void ParticleGun::resetParticle(Attribute* attribute)
{
     attribute->_isAlive = true;

     D3DXVECTOR3 cameraPos;
     _camera->getPosition(&cameraPos);

     D3DXVECTOR3 cameraDir;
     _camera->getLook(&cameraDir);

     // Получаем местоположение камеры
     attribute->_position = cameraPos;
     attribute->_position.y -= 1.0f; // смещаем позицию вниз, чтобы
                                     // казалось, что мы держим оружие
                                     // в руках

     // Отправляем частицу в том направлении, куда смотрит камера
     attribute->_velocity = cameraDir * 100.0f;

     // Назначаем зеленый цвет
     attribute->_color = D3DXCOLOR(0.0f, 1.0f, 0.0f, 1.0f);

     attribute->_age = 0.0f;
     attribute->_lifeTime = 1.0f; // время жизни - 1 секунда
}

Метод update обновляет местоположение частиц и уничтожает частицы, которые прожили больше заданного времени. В самом конце функции мы просматриваем список частиц и удаляем из него все мертвые частицы.

void ParticleGun::update(float timeDelta)
{
     std::list<Attribute>::iterator i;

     for(i = _particles.begin(); i != _particles.end(); i++)
     {
          i->_position += i->_velocity * timeDelta;

          i->_age += timeDelta;

          if(i->_age > i->_lifeTime) // убиваем
               i->_isAlive = false;
     }
     removeDeadParticles();
}

netlib.narod.ru< Назад | Оглавление | Далее >

Сайт управляется системой uCoz