Компоненты системы частиц
Системой частиц называется набор частиц и код, отвечающий за управление этими частицами и их отображение. Система частиц отслеживает глобальные свойства, влияющие на все частицы, такие как размер частиц, место из которого появляются частицы, накладываемая на частицы текстура ит.д. С точки зрения функциональности система частиц отвечает за обновление, отображение, уничнтожение и создание частиц.
Хотя различные системы частиц ведут себя по-разному, мы можем выполнить обобщение и выделить некоторые базовые свойства, которые используются всеми системами частиц. Мы поместим эти общие свойства в абстрактный базовый класс PSystem, который будет родителем для классов конкретных систем частиц. Давайте теперь взглянем на класс PSystem:
class PSystem { public: PSystem(); virtual ~PSystem();
virtual bool init(IDirect3DDevice9* device, char* texFileName); virtual void reset(); virtual void resetParticle(Attribute* attribute) = 0; virtual void addParticle(); virtual void update(float timeDelta) = 0;
virtual void preRender(); virtual void render(); virtual void postRender();
bool isEmpty(); bool isDead(); protected: virtual void removeDeadParticles();
protected: IDirect3DDevice9* _device; D3DXVECTOR3 _origin; d3d::BoundingBox _boundingBox; float _emitRate; float _size; IDirect3DTexture9* _tex; IDirect3DVertexBuffer9* _vb; std::list<Attribute> _particles; int _maxParticles;
DWORD _vbSize; DWORD _vbOffset; DWORD _vbBatchSize; };
Начнем с членов данных:
_origin — Базовая точка системы. Это то место, откуда появляются частицы системы.
_boundingBox — Ограничивающий параллелепипед используется в тех системах частиц, где надо ограничить объем пространства в котором могут находиться частицы. Например, мы хотим, чтобы снег падал только в пространстве, окружающем высокую вершину горы; для этого следует создать ограничивающий параллелепипед, охватывающий желаемый объем и частицы, вышедшие за границы этого объема будут уничтожаться.
_emitRate — Частота добавления новых частиц к системе.
Обычно измеряется в частицах в секунду.
_size — Размер всех частиц системы.
_particles — Список, содержащий атрибуты частиц системы. Мы работаем с этим списком при создании, уничножении и обновлении частиц. Когда мы готовы к рисованию частиц, мы копируем часть узлов списка в буфер вершин и рисуем частицы. Затем мы копируем следующий блок и рисуем частицы. Эти действия повторяются до тех пор, пока не будут нарисованы все частицы. Это крайне упрощенное описание; подробно процесс рисования будет рассмотрен в разделе 14.2.1.
_maxParticles — Максимальное количество частиц, которое может быть в системе одновременно. Если, к примеру, частицы создаются быстрее чем удаляются, может получиться, что у вас будет огромное количество частиц, что приведет к неработоспособности программы. Данный член позволяет избежать такого развития событий.
_vbSize — Количество частиц, которое может быть помещено в буфер вершин одновременно. Это значение не зависит от количества частиц в конкретной системе частиц.
ПРИМЕЧАНИЕ
Методы класса:
PSystem/~PSystem — Конструктор инициализирует значения по умолчанию, а деструктор освобождает интерфейсы устройства (буфер вершин, текстуры).
init — Этот метод выполняет зависящую от устройства Direct3D инициализацию, такую как создание буфера вершин для хранения точечных спрайтов и создание текстуры. При создании буфера вершин указываются несколько флагов о которых мы говорили, но до настоящего момента еще ни разу не использовали:
hr = device->CreateVertexBuffer( _vbSize * sizeof(Particle), D3DUSAGE_DYNAMIC | D3DUSAGE_POINTS | D3DUSAGE_WRITEONLY, Particle::FVF, D3DPOOL_DEFAULT, &_vb, 0);
Обратите внимание, что мы используем динамический буфер вершин. Это вызвано тем, что мы должны в каждом кадре обновлять данные частиц, что требует доступа к памяти буфера вершин.
Вспомните, что доступ к статическому буферу вершин осуществляется очень медленно; поэтому мы используем динамический буфер вершин.
Взгляните на используемый флаг D3DUSAGE_POINTS, который сообщает, что в буфере вершин будут храниться точечные спрайты.
Обратите внимание, что размер буфера вершин задан переменной _vbSize и не имеет ничего общего с количеством частиц в системе. То есть, значение _vbSize очень редко равно количеству частиц в системе. Это вызвано тем, что мы визуализируем систему частиц по частям, а не всю сразу. Процесс визуализации мы исследуем в разделе 14.2.1.
Мы используем пул памяти по умолчанию, а не обычный управляемый пул памяти потому что динамический буфер вершин не может быть размещен в управляемом пуле памяти.
reset — Этот метод сбрасывает значения атрибутов у каждой частицы системы:
void PSystem::reset() { std::list<Attribute>::iterator i; for(i = _particles.begin(); i != _particles.end(); i++) { resetParticle(&(*i)); } }
resetParticle — Этот метод сбрасывает значения атрибутов одной частицы. То, как именно должен выполняться сброс атрибутов, зависит от параметров конкретной системы частиц. Следовательно, мы делаем этот метод абстрактным и он должен быть реализован в производном классе.
addParticle — Этот метод добавляет частицу к системе. Он использует метод resetParticle для инициализации частицы перед ее добавлением к списку:
void PSystem::addParticle() { Attribute attribute;
resetParticle(&attribute);
_particles.push_back(attribute); }
update — Этот метод обновляет данные всех частиц системы. Поскольку реализация такого метода зависит от спецификаций конкретной системы частиц, мы объявляем этот метод абстрактным и должны реализовать его в производном классе.
render — Данный метод отображает все частицы системы. Его реализация достаточно сложна и мы отложим ее обсуждение до раздела 14.2.1.
preRender — Применяется для установки начальных режимов визуализации, которые должны быть заданы перед визуализацией.
Поскольку они могут меняться в зависимости от конкретной системы частиц, мы делаем этот метод виртуальным. Предлагаемая по умолчанию реализация выглядит так:
void PSystem::preRender() { _device->SetRenderState(D3DRS_LIGHTING, false); _device->SetRenderState(D3DRS_POINTSPRITEENABLE, true); _device->SetRenderState(D3DRS_POINTSCALEENABLE, true); _device->SetRenderState(D3DRS_POINTSIZE, d3d::FtoDw(_size)); _device->SetRenderState(D3DRS_POINTSIZE_MIN, d3d::FtoDw(0.0f));
// Управление изменением размера частицы // в зависимости от расстояния до нее _device->SetRenderState(D3DRS_POINTSCALE_A, d3d::FtoDw(0.0f)); _device->SetRenderState(D3DRS_POINTSCALE_B, d3d::FtoDw(0.0f)); _device->SetRenderState(D3DRS_POINTSCALE_C, d3d::FtoDw(1.0f));
// Для текстуры используется альфа-смешивание _device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); _device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); _device->SetRenderState(D3DRS_ALPHABLENDENABLE, true); _device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); _device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); }
Обратите внимание, что мы разрешили альфа-смешивание, чтобы альфа-канал установленной в данный момент текстуры определял прозрачность ее участков. Прозрачность используется для множества эффектов; один из них — реализация частиц непрямоугольной формы. Предположим, вам нужны круглые, похожие на хлопья снега, частицы. В этом случае мы используем текстуру в виде белого квадрата с альфа-каналом в виде черного квадрата с белым кругом. В результате на экране будет отображен похожий на снежинку белый круг, а не квадратная текстура.
postRender — Используется для восстановления режимов визуализации, которые изменила данная система частиц. Поскольку режимы меняются в зависимости от конкретной системы частиц, этот метод виртуальный. По умолчанию используется следующая реализация:
void PSystem::postRender() { _device->SetRenderState(D3DRS_LIGHTING, true); _device->SetRenderState(D3DRS_POINTSPRITEENABLE, false); _device->SetRenderState(D3DRS_POINTSCALEENABLE, false); _device->SetRenderState(D3DRS_ALPHABLENDENABLE, false); }
isEmpty — Метод возвращает true, если в системе нет ни одной частицы и false в ином случае.
isDead — Метод возвращает true если все частицы в системе мертвы и false, если хотя бы одна частица жива. Обратите внимание что если все частицы мертвы, это не значит, что система частиц пуста. В пустой системе нет ни мертвых ни живых частиц. Если система мертвая, это значит, что в ней есть частицы, но все они помечены как мертвые.
removeDeadParticles — Метод перебирает элементы списка атрибутов _particle и удаляет из него все частицы, которые отмечены как мертвые:
void PSystem::removeDeadParticles() { std::list::iterator i; i = _particles.begin(); while( i != _particles.end() ) { if( i->_isAlive == false ) { // стирание возвращает номер следующего элемента, // поэтому самостоятельно увеличивать счетчик не надо i = _particles.erase(i); } else { i++; // следующий элемент списка } } }
ПРИМЕЧАНИЕ