Lighthouse3d.com

Dynamic Particle System Library

Sistema Dinâmico de Partículas
uma implementação para OpenGL

 

Licenciatura em Sistemas e Informática
Universidade do Minho
Portugal

José Miguel Araújo Ferreira [jmaferreira@hotmail.com]
Pedro Gabriel Dias Ferreira [pgabiferreira@portugalmail.pt]
Rui Manuel A.S.R. Guerra [ruiguerra@hotmail.com]

 


Downloads

 

Índice

  1. As Partículas e a Física de Newton
  2. O que é uma Partícula
  3. O que é uma Força
  4. O que é um Objecto
  5. Como utilizar o DPS
    1. Conjunto de Partículas
      1. Criar um Particula
    2. Conjunto de Forças
      1. Tipos de Forças
    3. Conjunto de Objectos
      1. Tipos de Objectos
      2. Acções de um Objecto
      3. Criar um Objecto
    4. Funções de manipulação dinâmica
  6. Demonstrações
  7. Apêndice A - Constantes e Tipos de Dados
    1. Constantes
    2. Tipos de Dados
  8. Apêndice B - Glossário de Funções
    1. Manipulação de Conjuntos
    2. Manipulação de Partículas
    3. Manipulação de Forças
    4. Manipulação de Objectos
    5. Manipulação Dinâmica

 

 

Introdução

Um sistema de partículas é uma colecção de pontos independentes que são estimulados por um conjunto de regras com a intensão de modelar de determinado efeito. Tem portanto como objectivo representar e simular alguns dos fenmenos que ocorrem no mundo real tais como a chama de um fogo, o choque de uma bola com uma parede ou a exploso de um objecto.

A necessidade de criar uma representao computacional de tais fenmenos fez com que diversas teorias sobre sistemas de partículas tenham sido criadas, cada uma focando um dominio especifico de aplicação. O modelo aqui apresentado é baseado na fsica Newtoniana, i.e., um modelo no qual os objectos obedecem a determinadas leis da fsica de Newton.

Constata-se ser trivial criar um modelo no qual grupos de objectos se movem no espao com um determinado comportamento bem definido. No entanto torna-se complexo quando temos um sistema em que os objectos colidem entre si, reagindo de uma maneira fisicamente correcta. Contorna-se este problema regendo o sistema atravs da combinao de algumas leis elementares da fsica com outras restries matemticas para assim controlar o comportamento dinmico dos objectos do sistema.

Esta aproximao ao problema feita para um conjunto de objectos, ou dito de outra forma, conjunto de partculas, uma vez que o termo partcula descreve de forma mais abrangente e exacta uma entidade sujeita a determinadas regras fsicas. Daqui surge ento a noo de sistema de partculas, que corresponde a nada mais que uma coleco de partculas, tipicamente pontos materiais com comportamento dinmico regido por um conjunto equaes pertencentes ao domínio da fsica e da matemtica Euclidiana.

Os sistemas de partculas tm sido utilizados para modelar uma vasta gama de fenmenos nos mais variados campos, como a chama de um fogo, o comportamento em de alguns bandos de pssaros, flocos de neve a cair, chuva num dia com vento e muitos outros. Todas estas entidades podem ser consideradas pontos de massa, i.e, objectos em que toda a sua massa est concentrada num nico ponto. Utilizando as equaes da fsica, que podemos resolver numericamente com relativa facilidade, conseguimos calcular o estado de cada partcula num dado momento. No final de cada srie de cálculos podemos desenhar um objecto grfico, em vez de um ponto.

 

 

As Partículas e a Física de Newton

A segunda lei de Newton diz-nos que uma partcula sujeita a uma dada fora f, com uma dada massa m fica sujeita a uma acelerao a, dada pela relao f = ma.

 

Nota: as letras a vermelho representam grandezas vectoriais, tipicamente definidas num espao tri-dimensional. As letras a azul representam conjuntos, enquanto as restantes representam grandezas escalares.

 

Uma das consequncias mais imediatas da fsica de Newton que, uma partcula de massa ideal, i.e, uma partcula cuja toda a sua massa est concentrada num nico ponto, tem o seu estado completamente definido pelo vector velocidade e pelo vector acelerao.

 

  • p = <x, y, z>
  • v = <vx, vy, vz>
  • a = <ax, ay, az>

Com as equaes do movimento uniformente acelerado:

  • r = r0 + vt
  • v= v0 + at,

e sabendo que v = r' e que a = r'', temos que a = f/m, conseguindo-se assim definir a qualquer momento a posio e a velocidade da partcula.

 

Nota: Alm do seu estado (posio e velocidade), cada partcula pode ainda ter associado um conjunto de atributos, como a massa, cor, idade, forma, etc.

 

Um força, ou um conjunto de foras F, determina o comportamento do sistema. Essas foras so baseadas no estado da partcula e podem variar ao longo do tempo.

Tipicamente podemos ter três tipos de foras a actuar sobre um conjunto de partículas:

  1. Foras partcula-partcula (p.e. repulso e atraco entre partículas)
  2. Foras aplicadas ao sistema (p.e. vento, gravidade, tornado)
  3. Foras partcula-objecto (p.e choque entre uma partícula e um objecto físico)

Finalmente pode dizer-se que o estado duma partícula obtido atravs de mtodos numricos, que permitem obter uma aproximao ao conjunto das funes e equaes diferenciais que regem o sistema. Assim cada iteraco permite obter um novo estado para o sistema de partculas.

 

 

As Partículas e a Geometria de Euclides

Para se definir algumas das caractersticas de um objecto foi necessrio recorrer a um conjunto de equaes da geometria a partir das quais se conseguem extrair os parmetros necessrios para a definio dum objecto, p.e., orientao do objecto ou os seus vectores ortonormais.

A equao mais frequentemente utilizada foi a de um plano (ax + by + cz + d) definido por um ponto e por um vector normal. Esta equao foi utilizada no s na definio do prprio objecto plano mas tambm no clculo de outros objectos planares como o rectngulo, o tringulo ou o disco.

Para a deteco da coliso de um partcula com um objecto foi necessrio calcular a distncia de um ponto p (posio actual da partcula) a um plano (no caso dos objectos com estrutura plana).
Utiliza-se a seguinte equao para o este efeito: dist = - (px.a + py.b + pz.c + d) / sqrt (a^2 + b^2 + c^2).

Existem na geometria cartesiana duas operaes que so a base para todo o trabalho sobre vectores. So elas o produto vectorial e o produto escalar.
O produto escalar entre dois vectores u e v definido da seguinte maneira: u . v = ux.vx + uy.uy + uz.Vz. O resultado um nmero real igual ao produto das normas dos vectores pelo co-seno do ngulo formado pelos dois vectores.
O produto vectorial entre dois vectores u e v dado pelo clculo do determinante de terceira ordem entre o conjunto de vectores directores do espao e pelos prprios vectores u e v. O resultado um vector cujas componentes em x, y e z so <uy.vz - Vy.Uz, Vx.Uz - ux.vz, ux.vy - uy.Vx>.

 

 

O que é uma Partícula

Como já foi referido anteriormente, uma partícula é um ponto material que possui determinados atributos sobre os quais forças poderão actuar:

 

Domínio
Atributo
Tipo
Descrição
Propriedades físicas duma partícula
position
vectorial
Posição no espaço onde a partícula se encotra
velocity
vectorial
Velocidade actual da partícula
mass
escalar
Massa da partícula
Propriedades que definem o aspecto gráfico duma partícula
age
escalar
Define a idade duma partícula. Tipicamente uma partícula nasce, tem um tempo de vida e morre.
color
vectorial
Permite definir a cor duma partícula.
mesh
função
Define a forma visual duma partícula.
roll
booleano
Permite dizer ao sistema se queremos ou nao que a forma definida por mesh se molde ao movimento da partícula.
Metodos que são invocados na presença de determinados eventos
collide
função
Quando uma partícula colide com um objecto, este método é invocado.
die
função
Quando uma partícula morre, este método é invocado.

 

 

O que é uma Força

Uma força é toda e qualquer entidade que permite alterar o estado duma partícula.
No DPS existem fundamentalmente dois tipos de forças, as baseadas na física e outras, às quais passaremos a chamar pseudo-forças. As forças baseadas na física são já nossas conhecidas, pois ocorrem frequentemente na natureza. Alguns exemplos destas forças são a gravidade, o vortex, vento, entre outras.

As pseudo-forças permitem-nos, basicamente, alterar determinadas propriedades duma partícula, porém não ocorrem tipicamente na natureza. Alguns exemplos destas forças especiais são: a eliminação de partículas, a alteração da sua cor ou da sua velocidade, entre outras.

 

O que é um Objecto

Permitir que as partculas mudem de estado atravs de um conjunto de equaes, notoriamente insuficiente para modelar determinados comportamentos do mundo real tais como colises, desvios de objectos ou at o desaparecimento ou fragmentao aquando de uma coliso com um objecto.

Aqui entram os objectos do sistema de partculas. Estes no passam de entidades geomtricas como esferas, rectngulos, discos entre outros, com uma aco bem definida e com um conjunto de parmetros que modelam o comportamento de uma partcula quando com estes interage.

No DPS existem quatro aces que cada objecto pode desempenhar (salvo algumas excepes):

 

Acção
Descrição
Collide
modela o choque de uma partcula com o objecto.
Avoid
Faz com que uma partcula se v desviando progressivamente de um objecto.
Kill
Mata uma partcula quando esta penetra no objecto.
Source
Faz de um objecto uma fonte de partculas.

 

Todas estas aces so partida suficientes para cobrir a generalidade dos casos a simular.

 

Nota: A cada objecto é associada apenas a uma aco, pelo que este desempenhar essa aco em todo o seu tempo de vida.

 

 

Como utilizar o DPS

Deixemos por agora a física e a matemática e vamos analisar o funcionamento do API.
O modo de funcionamento do API é bastante simples e intuitivo. A sintaxe utilizada é semelhante à utilizada pelas display lists do OpenGL, como veremos de seguida.

Uma vez compreendidos os conceitos inerentes às partículas, forças e objectos, passamos a explicar como todas estas entidades se relacionam.

No DPS (Dynamic Particle System, ou em português, Sistema Dinâmico de partículas) existem três dominios diferentes que interagem entre si: o dominio das partículas, das forças e dos objectos.

Para animar um sistema de partículas é necessário fornecer ao API estes três dominios, ainda que algum deles ou até todos, sejam vazios. É necessário portanto, definir um conjunto de partículas P, um conjunto de forças F e um conjunto de objectos O.

Numa segunda fase é necessário dizer ao sistema quais os conjuntos que interagem conjuntamente. Esta relação é conseguida através da função dpsIterateParticles, que calcula uma nova posição para cada uma das partículas do conjunto P, que estão sob a influência dum conjunto de forças F e um conjunto de objectos O. É necessário compreender que para uma iteração conjunta destes três conjuntos é necessário ter definido um incremento de tempo dt diferente de zero (ver dpsTimeIncrementf), senão a iteração não produz resultados visto que o tempo de simulação não foi alterado. É também necessário compreender que esta função deve ser chamada várias vezes durante o tempo de vida duma animação, pois apenas calcula uma e só uma iteração. Tipicamente esta função é chamada pela função glutIdleFunc pertencente à biblioteca GLUT.

A terceira e última fase operacional do DPS é desenhar. De nada serviria todo cálculo se não se monstrasse os resultados do processamento.

 

Nota: Este API tem como principal aplicação o desenvolvimento de efeitos visuais não interactivos. Embora seja utilizada física Newtoniana não foi nossa intenção fazer um ambiente de simulação físicamente perfeito.

 

Para isto basta utilizar a função dpsDrawParticleSystem, que recebe como argumento um conjunto de partículas e desenha-o no canvas aberto pelo OpenGL.

 

Resumo: Basicamente, para se ter um sistema de partículas a funcionar é necessário fazer três coisas:

  1. Definir os conjuntos de partículas, forças e objectos.
  2. Calcular uma iteração para um conjunto de partículas.
  3. Desenhar o conjunto das partículas.

 

 

Conjunto de Partículas

Este é o mais importante dos conjuntos a definir. Sem partículas as forças pura e simplesmente não actuam, ou seja, actuam sobre nada. É sempre necessário criar um conjunto de partículas, ainda que este possa ser vazio. Podem ser adicionadas partículas uma a uma, em posições específicas se desejarmos ter um sistema pequeno e controlado, ou então utilizar um objecto como gerador de partículas dinâmico (ver objecto geradores de partículas).

A definição dum ou mais conjuntos de partículas é assegurada pelas seguintes funções:

 

Protótipo
DPSshandle dpsGenParticleSystem(DPSsizei range);
Argumentos
range=numero de conjuntos a gerar
Retorno
Um handle para o primeiro conjunto gerado. Os seguintes são endereçaveis por handle + n. Este handle será utilizado para identificar um conjunto de partículas.
Descrição
Cria range numero de conjuntos de partículas.

 

Protótipo
void dpsNewParticleSystem(DPSshandle handle);
Argumentos
handle=identificador do conjunto de partículas
Retorno
Nada
Descrição
Indica ao DPS que passamos a trabalhar sobre o conjunto de partículas handle. A partir de agora podemos adicionar partículas a este conjunto. Não esquecer que, uma vez utilizado, devemos fechar o conjunto com dpsEndParticleSystem.

 

Protótipo
void dpsEndParticleSystem();
Argumentos
Nenhum
Retorno
Nada
Descrição
Fecha o conjunto actual de partículas.

 

Segue-se um pequeno exemplo de como utilizar estes três conceitos:

 

Criar um conjunto de partículas

01 DPSshandle ps_handle; 02 int number_of_sets = 1;
03 ps_handle = dpsGenParticleSystem(number_of_sets); 04 dpsNewParticleSystem(ps_handle); 05 dpsNewParticle(); 06 dpsEndParticleSystem();
Este pequeno excerto de código permite criar um conjunto de partículas e adicionar ao mesmo uma partícula.
 

No exemplo anterior não foram definidas quaisquer propriedades da partícula, portanto esta seria criada com os seus valores por defeito. Para definirmos os atributos das partículas são utilizadas variáveis de estado tal como no Open GL.

 

Definir alguns atributos para uma partícula

01 dpsNewParticleSystem(ps_handle); 02 dpsColor3f(0.5, 0.5, 0.5); 03 dpsPosition3f(0.0, 0.0, 0.0); 04 dpsVelocity(1.0, 0.0, 0.0); 05 dpsNewParticle(); 06 dpsEndParticleSystem();
Neste exemplo é colocada uma partícula cinzenta na origem do universo com uma velocidade de 1.0 na direcção do eixo dos xx.

 

 

Criar uma partícula

Para criar uma partícula é necessário definir uma série de propriedades relevantes que irão determinar o comportamento da mesma no seu instante inicial. Uma vez definidas essas propriedades basta invocar a função dpsNewParticle para que uma nova partícula seja adicionada ao conjunto.

Para definir as propriedades duma partícula são utilizadas diversas funções de estado que definem como todas as partículas vão ser geradas a partir da definição desse estado.

As funções de estado que definem os atributos das partículas a gerar (incluíndo as geradas dinamicamente usando objectos geradores) são:

 

Protótipo
void dpsColor4f(DPSfloat r, DPSfloat g, DPSfloat b, DPSfloat a);
Argumentos
r, g, b, a definidos em [0.0, 1.0]
r=Red, g=Green, b=Blue, a=Alpha
Retorno
Nada
Descrição
Define a cor das partículas através dos diferentes valores de Red, Green, Blue e Alpha

 

Protótipo
void dpsColor3f(DPSfloat r, DPSfloat g, DPSfloat b);
Argumentos
r, g, b definidos em [0.0, 1.0]
r=Red, g=Green, b=Blue
O valor de alpha é colocar a 1.0
Retorno
Nada
Descrição
Define a cor das partículas através dos diferentes valores de Red, Green e Blue.

 

Protótipo
void dpsRGBADev(DPSfloat rdev, DPSfloat gdev, DPSfloat bdev, DPSfloat adev);
Argumentos
rdev, gdev, bdev, adev definidos em [0.0, 1.0]
rdev=Desvio Red, gdev=Desvio Green, bdev=Desvio Blue
Retorno
Nada
Descrição
Define o desvio padrão para cada uma das componentes de cor.

 

Protótipo
void dpsPosition3f(DPSfloat rx, DPSfloat ry, DPSfloat rz);
Argumentos
rx=Posição no eixo dos xx, ry=Posição no eixo dos yy, rz=Posição no eixo dos zz
Retorno
Nada
Descrição
Define a posição das partículas.

 

Protótipo
void dpsVelocity3f(DPSfloat vx, DPSfloat vy, DPSfloat vz);
Argumentos
vx=Velocidade no eixo dos xx, vy=Velocidade no eixo dos yy, vz=Velocidade no eixo dos zz
Retorno
Nada
Descrição
Define a velocidade vectorial das partículas.

 

Protótipo
void dpsAgef(DPSfloat age);
Argumentos
age=idade inicial da partícula.
Tipicamente este valor é definido a zero.
Retorno
Nada
Descrição
Define a idade inicial das partículas.

 

Protótipo
void dpsMassf(DPSfloat mass);
Argumentos
mass=massa da partícula
Retorno
Nada
Descrição
Define a massa das partículas.

 

Protótipo
void dpsMeshFunc(DPSvoid (*mesh)(DPSulong));
Argumentos
mesh=função que desenha o objecto pretendido.
O argumento da função fornecida deve ser um DPSulong, que representa o número de vezes que a função foi invocada. A utilidade deste argumento é poder alterar a forma da partícula ao longo do tempo.
Retorno
Nada
Descrição
Define a forma das partículas. A função mesh é invocada sempre que a partícula vai ser desenhada.

 

Protótipo
void dpsMeshRoll(DPSboolean roll);
Argumentos
roll=DPS_TRUE ou DPS_FALSE
Retorno
Nada
Descrição

Define se a mesh deve rodar de forma a seguir o movimento da partícula. Caso esse seja o caso, é fundamental que a mesh seja desenhada na origem, de pé e virada para a frente.

dpsMeshRoll(DPS_FALSE);
dpsMeshRoll(DPS_TRUE);

 

Protótipo
void dpsDieFunc(DPSvoid (*die)(DPShandle, DPSshandle, DPSshandle, DPSshandle));
Argumentos

die=função que é chamada quando uma partícula morre.
Os argumentos da função fornecida são handles para a partícula que morreu, para o conjunto das partículas, para o conjunto das forças e para o conjunto dos objectos.
A utilidade deste mecanismo é termos controlo total sobre o que fazer quando uma determinada partícula morre. Como por exemplo, gerar umas quantas partículas no local do óbito e aplicar uma explosão, para dar um efeito do tipo foguete a rebentar.

Retorno
Nada
Descrição
Defne a função que é invocada sempre que uma partícula morre.

 

Protótipo
void dpsCollideFunc(DPSvoid (*collide)(DPShandle, DPSshandle, DPSshandle, DPSshandle));
Argumentos

collide=função que é chamada quando uma partícula colide com algum objecto.
Os argumentos da função fornecida são handles para a partícula que colidiu, para o conjunto das partículas, para o conjunto das forças e para o conjunto dos objectos.
A utilidade deste mecanismo é termos controlo total sobre o que fazer quando uma determinada partícula colide. Como por exemplo, gerar umas quantas partículas no local da colisão e aplicar uma explosão, para dar um efeito do tipo granada a bater no chão.

Retorno
Nada
Descrição
Defne a função que é invocada sempre que uma partícula colide com um objecto.

 

Protótipo
DPShandle dpsNewParticle();
Argumentos
Nenhum
Retorno
Um handle para a partícula. Permite que a partícula seja removida ou alterada mais tarde.
Descrição
Cria uma nova partícula no conjunto de partículas actual, com a propriedades actualmente definidas.

 

 

Conjunto de Forças

As foras so fundamentais num sistema de partículas. Com elas podemos simular efeitos como gravidade, vento, exploses, tornados, etc.
Tivemos uma enorme preocupao na optimizao do cdigo, pois funes como dpsNewGravitate exigem muito poder de cálculo.
Muitas das forças respeitam a relação: acelerao proporcional ao inverso do quadrado da distancia (ou raio de acção da força). O parâmetro epsilon sempre adicionado à distancia para evitar que a acelerao seja infinita quando as partculas esto muito prximas do núcleo (ou seja, para evitar buracos negros).

A definição dum ou mais conjuntos de forças é assegurada pelas seguintes funções:

 

Protótipo
DPSshandle dpsGenForceSystem(DPSsizei range);
Argumentos
range=numero de conjuntos a gerar
Retorno
Um handle para o primeiro conjunto gerado. Os seguintes são endereçaveis por handle + n. Este handle será utilizado para identificar um conjunto de partículas.
Descrição
Cria range numero de conjuntos de forças.

 

Protótipo
void dpsNewForceSystem(DPSshandle handle);
Argumentos
handle=identificador do conjunto de forças.
Retorno
Nada
Descrição
Indica ao DPS que passamos a trabalhar sobre o conjunto de forças handle. A partir de agora podemos adicionar forças a este conjunto. Não esquecer que, uma vez utilizado, devemos fechar o conjunto com dpsEndForceSystem.

 

Protótipo
void dpsEndForceSystem();
Argumentos
Nenhum
Retorno
Nada
Descrição
Fecha o conjunto actual de forças.

 

Tipos de Forças

De seguida são enumeradas todas as forças possiveis de adicionar ao conjunto de forças:

 

Protótipo
DPShandle dpsNewDamping(DPSfloat d0, DPSfloat d1, DPSfloat d2, DPSfloat vlowSqr, DPSfloat vhighSqr);
Argumentos
Se a velocidade de uma partícula não se encontra no intervalo [vlow, vhigh], ento cada componente da velocidade multiplicada pelas constantes de damping. Por norma damping_x, damping_y, damping_z so semelhantes. Como no existem limites para os valores de damping, se usarmos valores superior a 1.0 as partculas sero aceleradas.
Retorno
Hande para a força. Permite que a força seja removida ou alterada mais tarde.
Descrição
Simula a resistencia do ar, diminuindo a velocidade das partículas.

 

Protótipo
DPShandle dpsNewExplosion(DPSfloat x, DPSfloat y, DPSfloat z, DPSfloat velocity, DPSfloat magnitude, DPSfloat stdev, DPSfloat epsilon, DPSfloat age);
Argumentos
x, y, z = centro da explosão; velocity=velocidade com que as partículas são afastadas do centro; magnitude=intensidade da força; stdev=desvio padrão da velocidade; age=idade da força (quanto maior for a idade menor é a intensidade da explosão)
Retorno
Hande para a força. Permite que a força seja removida ou alterada mais tarde.
Descrição
Simula uma exploso. Afasta todas as partculas do centro, com uma fora proporcional magnitude.

 

Protótipo
DPShandle dpsNewFollow(DPSfloat magnitude, DPSfloat epsilon, DPSfloat max_radius);
Argumentos
magnitude=intensidade da força, max_radius=raio de acção da força
Retorno
Hande para a força. Permite que a força seja removida ou alterada mais tarde.
Descrição
Acelera a partcula na direco da seguinte partcula do conjunto.
Isto permite dispor as partculas em fila indiana. Cada partcula acelerada na direco da partcula seguinte do conjunto, com uma fora proporcional a magnitude.

 

Protótipo
DPShandle dpsNewGravity(DPSfloat x, DPSfloat y, DPSfloat z);
Argumentos
Os argumentos dados representam o vector acelerao de gravidade, que ser adicionado ao vector velocidade de cada partcula.
Retorno
Hande para a força. Permite que a força seja removida ou alterada mais tarde.
Descrição
Simula o efeito gravidade acelerando as partculas numa dada direco.

 

Protótipo
DPShandle dpsNewGravitate(DPSfloat magnitude, DPSfloat epsilon, DPSfloat max_radius);
Argumentos
magnitude=cada partcula acelerada em direco a todas as outras partculas com uma fora proporcional a magnitude; max_radius=define o raio mximo de influência desta aco. Assim partculas que distam mais do que max_radius no se influenciam.
Retorno
Hande para a força. Permite que a força seja removida ou alterada mais tarde.
Descrição
Simula foras de atraco entre as partculas.

 

Protótipo
DPShandle dpsNewSpeedlimit(DPSfloat min, DPSfloat max);
Argumentos
min, max=limites de velocidade
Retorno
Hande para a força. Permite que a força seja removida ou alterada mais tarde.
Descrição
Obriga a velocidade de cada partcula manter-se entre um dado valor mnimo e um mximo.

 

Protótipo
DPShandle dpsNewTargetColor(DPSfloat c1, DPSfloat c2, DPSfloat c3, DPSfloat alpha, DPSfloat scale);
Argumentos
c1=red, c2=green, c3=blue, alpha=alpha, scale=numero de iterações necessárias para atingir a cor destino.
Retorno
Hande para a força. Permite que a força seja removida ou alterada mais tarde.
Descrição
Faz com que a cor das partículas seja alterada para a cor especificada. Se o valor de scale for maior que zero esta força efectua uma transição suave entre a cor original e a cor destino.

 

Protótipo
DPShandle dpsNewVortex(DPSfloat p0, DPSfloat p1, DPSfloat p2, DPSfloat axis0, DPSfloat axis1, DPSfloat axis2, DPSfloat magnitude, DPSfloat epsilon, DPSfloat max_radius);
Argumentos
p0, p1, p2= ponto central; axis0, axis1, axis2= eixo; magnitude=intensidade da força; max_radius=raio de accção
Retorno
Hande para a força. Permite que a força seja removida ou alterada mais tarde.
Descrição
Movimenta as partículas em torno de um eixo.
O centro e o axis definem uma linha infinita, onde o centro representa a origem do vortex e o axis um vector ao longo dessa linha, cujo comprimento irrelevante. Usando-se um epsilon semelhante magnitude pode-se aumentar o raio de influencia do vortex. O raio_max define o campo de actuao desta fora.

 

Protótipo
DPShandle dpsNewKillOld(DPSfloat age_limit, DPSboolean kill_less_than);
Argumentos
age_limite=limite de idade, kill_less_than=elimina as partículas mais antigas ou as mais recentes que age_limit
Retorno
Hande para a força. Permite que a força seja removida ou alterada mais tarde.
Descrição
Elimina as partículas com idade superior a age_limit se o argumento Kill_less_than for falso. Se o argumento Kill_less_than for verdadeiro, elimina todas as partículas com idade inferior a age_limit.

 

 

Conjunto de Objectos

Este conjunto de entidades permite dar s partculas um comportamento idntico a determinados eventos do mundo real.

A interaco entre objectos e partculas pode ser dividida em duas fases: deteco e reaco. A deteco da partcula feita inserindo a sua posio no plano do objecto. A sua reaco depende do tipo de aco definida para o objecto, sendo no caso da coliso (ver DPS_COLLIDE) algo do tipo da reflexo da luz numa superfcie, no caso do evitar (ver DPS_AVOID) algo do gnero de uma partcula com carga negativa desviar-se de outra partcula com carga negativa. No caso da aco "morrer" (ver DPS_KILL) a partcula desaparece e no caso da fonte (ver DPS_SOURCE) so geradas partculas com um determinado débito. Note que no caso das aces colidir e "morrer" pode ser declarada uma aco a exercer sobre a partcula, por exemplo, uma partcula que colide pode morrer e serem geradas nessa posio e instante vrias partculas.

A definição dum ou mais conjuntos de objectos é assegurada pelas seguintes funções:

 

Protótipo
DPSshandle dpsGenObjectSystem(DPSsizei range);
Argumentos
range=numero de conjuntos a gerar
Retorno
Um handle para o primeiro conjunto gerado. Os seguintes são endereçaveis por handle + n. Este handle será utilizado para identificar um conjunto de partículas.
Descrição
Cria range numero de conjuntos de objectos.

 

Protótipo
void dpsNewObjectSystem(DPSshandle handle);
Argumentos
handle=identificador do conjunto de objectos.
Retorno
Nada
Descrição
Indica ao DPS que passamos a trabalhar sobre o conjunto de objectos handle. A partir de agora podemos adicionar forças a este conjunto. Não esquecer que, uma vez utilizado, devemos fechar o conjunto com dpsEndObjectSystem.

 

Protótipo
void dpsEndObjectSystem();
Argumentos
Nenhum
Retorno
Nada
Descrição
Fecha o conjunto actual de objectos.

 

 

Tipos de Objectos

Os objectos implementados neste sistema de partculas correspondem a figuras geomtricas comuns. So eles: esfera, rectngulo, tringulo, plano, cilindro, cone e cubo. Com estes podemos compor objectos mais complexos.

Na criao de um objecto, feita atravs das funes abaixo enumeradas (LINK), devemos ter a consciência da existência de dois dois grupos de argumentos.

  • O primeiro indica a localizao geogrfica do objecto, bem como todos os restantes parmetros necessrios para definir a estrutura do objecto, tais como vectores directores, vectores normais ou ainda raios internos e externos.
  • O segundo grupo de argumentos diz respeito ao tipo de aco associada ao objecto e os parmetros que modelam essa aco, sendo que estes ltimos so dependentes do tipo de aco definida, i.e., variam de aco para aco.

 

Acções de um Objecto

 

Action
DPS_COLLIDE
Descrição

A coliso evento exacto e instantneo, muito idntico ao fenmeno da luz a reflectir numa superfcie. sabido da fsica que existem dois tipos de colises: elsticas e inelsticas. Nas colises inelsticas, a partcula no perde enegia pelo que a magnitude da sua velocidade no alterada. No entanto aps a coliso a direco da partcula alterada sendo a direco dada por uma reflexo perfeita. Assim dada a normal no ponto de coliso p e a posio anterior da partcula podemos calcular a direco da reflexo perfeita. A distncia da partcula superfcie reflectora igual distncia que esta teria se tivesse penetrado o objecto no caso da ausncia da coliso.

No caso anterior, a componente tangencial da velocidade fica inalterada, sendo apenas a direco da componente normal invertida. Um clculo ligeiramente mais complexo feito para o caso da coliso elstica, onde a partcula perde alguma da sua energia quando colide com o objecto.

Var1
Frico, i.e., quando a velocidade da partcula superior velocidade de corte (var3), a componente tangencial multiplicada por (1- frico).
Var2
Resistncia, ou de outra forma coeficiente de restituio, i.e., fraco da componente normal da velocidade da partcula retida aps o choque.
Var3
Velocidade de corte. Este parmetro utilizado para lidar correctamente com foras de contacto. As foras de contacto podem ser explicadas com um exemplo: quando temos uma partcula sujeita a uma fora que a empurra ao longo de uma superfcie. A partcula no pode penetrar na superfcie, mas tambm no pode saltar, devido fora que est a ser aplicada nela. Assim variando este parmetro podemos definir a forma como a partcula desliza ao longo da superfcie.

 

Action
DPS_AVOID
Descrição

Esta aco permite que a partcula se vá desviando progressivamente quando detecta que vai colidir com o objecto.

 

Var1
Magnitude, indica com que intensidade a velocidade da partcula alterada para poder evitar o objecto.
Var2
Epsilon, um parmetro utilizado em aces cuja intensidade depende do inverso da distncia. Um exemplo deste tipo de aces so as foras atractivas entre partculas cuja intensidade inversamente proporcional ao quadrado da distncia. Quando as distncias so muito reduzidas temos um intensidade infinita. Assim o valor de epsilon adicionado ao raio, ou distncia para evitar esta situao. Note-se que quase sempre utilizada um valor constante (DPS_EPSILON) de epsilon para estes casos.
Var3
Lookahead, i.e., nmero de unidades de tempo que so utilizadas para detectar uma coliso. Isto permite que uma partcula se v desviando objecto de uma forma mais ou menos drstica. Assim se o valor do lookahead for grande permite um desvio mais suave do objecto. Valores mais pequenos deste parmetro conduz a um desvio mais acentuado.

 

Action
DPS_SOURCE
Descrição

Esta aco define o objecto como uma fonte de partculas.

 

Var1
Dbito de criação, i.e., nmero de partculas geradas por unidade de tempo.
Var2
Magnitude da velocidade, i.e., mdulo da velocidade da partcula gerada. O vector velocidade dependendo do objecto que gera a partcula, tm tipicamente o vector director do plano complanar ao objecto. Existe porém uma excepção. A esfera gera partículas com uma velocidade orientada em todas as direcções, tal como seria de esperar.
Var3
Desvio padro da velocidade, i.e., valor da norma da velocidade com o qual as partculas so geradas seguindo uma distribuio normal. Isto serve essencialmente para dar um efeito mais natural ao "nascimento" das partculas. Este valor aplicado velocidade da partcula gerada concentrando a maior parte da seus valores em torno de um valor mdio mas permitindo que as velocidades oscilem entre valor e uma gama de valores determinada por este desvio padro.

 

Action
DPS_KILL
Descrição

Nesta aco temos um caso simples em que a partcula ao colidir com o objecto simplesmente removida do conjunto de partculas a que pertence, ou seja deixa de existir para o sistema. Nesta aco os parmetros deixam de fazer sentido, estando presentes na funo apenas por questes de coerncia.

 

Var1
Don't care
Var2
Don't care
Var3
Don't care

 

 

Criar um Objecto

De seguida são apresentados os protótipos das funções que permitem adicionar objectos ao nosso sistema de partículas.

 

Protótipo
DPShandle dpsNewSphere(DPSfloat x, DPSfloat y, DPSfloat z, DPSfloat ri, DPSfloat ro, DPSenum action, DPSfloat var1, DPSfloat var2, DPSfloat var3);
Argumentos

x,y,z=centro da esfera; ri,ro = raio interior e exterior;
action, var1, var2, var3 = ver tipo de objectos;

 

Retorno
Um handle para o objecto. Permite que objecto seja removido ou alterado mais tarde.
Descrição
Cria uma nova esfera no conjunto de objectos actual.
A esfera pode ser maciça bastando ter o raio interior igual zero. Pode também ser oca se o raio interior for maior que zero;

 

Protótipo
DPShandle dpsNewPlane(DPSfloat x0, DPSfloat y0, DPSfloat z0, DPSfloat x1, DPSfloat y1, DPSfloat z1, DPSenum action, DPSfloat var1, DPSfloat var2, DPSfloat var3);
Argumentos

x0,y0,z0 = ponto do plano; x1,y1,z1 = vector normal ao plano
action, var1, var2, var3 = ver tipo de objectos;

 

Retorno
Um handle para o objecto. Permite que objecto seja removido ou alterado mais tarde.
Descrição
Cria um novo plano no conjunto de objectos actual.

 

Protótipo
DPShandle dpsNewRectangle(DPSfloat x0, DPSfloat y0, DPSfloat z0, DPSfloat x1, DPSfloat y1, DPSfloat z1, DPSfloat x2, DPSfloat y2, DPSfloat z2, DPSenum action, DPSfloat var1, DPSfloat var2, DPSfloat var3);
Argumentos

x0,y0,z0 = o =ponto base do rectângulo; x1,y1,z1 = u = vector director do rectângulo; x2,y2,z2 = v =vector director do rectângulo;
action, var1, var2, var3 = ver tipo de objectos;

 

Retorno
Um handle para o objecto. Permite que objecto seja removido ou alterado mais tarde.
Descrição
Cria um novo rectangulo no conjunto de objectos actual.
O resultado é um losângulo em que os seus cantos são : o, o+u, o+u+v, o+v;
Os vectores não devem ser paralelos mas não precisam de ser ortongonais nem normais;

 

Protótipo
DPShandle dpsNewTriangle(DPSfloat x0, DPSfloat y0, DPSfloat z0, DPSfloat x1, DPSfloat y1, DPSfloat z1, DPSfloat x2, DPSfloat y2, DPSfloat z2, DPSenum action, DPSfloat var1, DPSfloat var2, DPSfloat var3);
Argumentos

x0,y0,z0; x1,y1,z1; x2,y2,z2; definem os três vértices do triângulo, podendo este ter uma forma arbitrária;
action, var1, var2, var3 = ver tipo de objectos;

 

Retorno
Um handle para o objecto. Permite que objecto seja removido ou alterado mais tarde.
Descrição
Cria um novo triangulo no conjunto de objectos actual.

 

Protótipo
DPShandle dpsNewDisc(DPSfloat x0, DPSfloat y0, DPSfloat z0, DPSfloat x1, DPSfloat y1, DPSfloat z1, DPSfloat ri, DPSfloat ro, DPSenum action, DPSfloat var1, DPSfloat var2, DPSfloat var3);
Argumentos

x,y,z=centro do disco; x1,y1,z1 = vector normal ao disco;ri,ro = raio interior e exterior;
action, var1, var2, var3 = ver tipo de objectos;

 

Retorno
Um handle para o objecto. Permite que objecto seja removido ou alterado mais tarde.
Descrição
Cria um novo disco no conjunto de objectos actual. Um disco é como um circulo mas no espaço tri-dimensional, ou seja, não possui altura.

 

Protótipo
DPShandle dpsNewCone(DPSfloat x0, DPSfloat y0, DPSfloat z0, DPSfloat x1, DPSfloat y1, DPSfloat z1, DPSfloat ri, DPSfloat ro, DPSenum action, DPSfloat var1, DPSfloat var2, DPSfloat var3);
Argumentos

x0,y0,z0= centro da base; x1,y1,z1 = outra extremidade ;ri,ro = raio interior e exterior;
action, var1, var2, var3 = ver tipo de objectos;

 

Retorno
Um handle para o objecto. Permite que objecto seja removido ou alterado mais tarde.
Descrição
Cria um novo cone no conjunto de objectos actual.

 

Protótipo
DPShandle dpsNewCylinder(DPSfloat x0, DPSfloat y0, DPSfloat z0, DPSfloat x1, DPSfloat y1, DPSfloat z1, DPSfloat ri, DPSfloat ro, DPSenum action, DPSfloat var1, DPSfloat var2, DPSfloat var3);
Argumentos

x0,y0,z0= centro da base; x1,y1,z1 = outra extremidade; ri,ro = raio interior e exterior;
action, var1, var2, var3 = ver tipo de objectos;

 

Retorno
Um handle para o objecto. Permite que objecto seja removido ou alterado mais tarde.
Descrição
Cria um novo cilindro no conjunto de objectos actual.

 

Protótipo
DPShandle dpsNewCube(DPSfloat x0, DPSfloat y0, DPSfloat z0, DPSfloat x1, DPSfloat y1, DPSfloat z1, DPSenum action, DPSfloat var1, DPSfloat var2, DPSfloat var3);
Argumentos

x0,y0,z0= vértice do cubo; x1,y1,z1 = vértice do cubo;
action, var1, var2, var3 = ver tipo de objectos;

 

Retorno
Um handle para o objecto. Permite que objecto seja removido ou alterado mais tarde.
Descrição

Cria um novo cubo no conjunto de objectos actual.
O cubo gerado é centrado e alinhado nos eixos.

 

 

Funções de manipulação dinâmica

Para além das funções que permitem criar conjuntos e entidades pertencentes a esses conjuntos (nomeadamente, partículas, forças e objectos) existem ainda funções que permitem conhecer e alterar o estado actual do sistema de partículas. Existem três destas funções que são muito importantes e sempre necessárias em qualquer utilização do DPS: dpsTimeIncrementf, dpsIterateParticles, dpsDrawParticleSystem.
Todas estas funções encontram-se descritas de seguida:

 

Protótipo
void dpsTimeIncrementf(DPSfloat dt);
Argumentos
dt = Incremento de tempo.
Valores normais para este valor são oscilam entre 0.0 e 1.0.
Retorno
Nada
Descrição
Define o incremento de tempo dt que deve ser utilizado sempre que é calculada uma nova iteração. Este parametro é muito importante, pois se não for definido o sistema não anima. Pode ser redefinido ao longo do tempo de simulação de modo a acelerar ou diminuir a velocidade da mesma.

 

Protótipo
void dpsIterateParticles(DPSshandle particles, DPSshandle forces, DPSshandle objects);
Argumentos
particles=handle para o conjunto de partículas, forces=handle para o conjunto de forças, objects=handle para o conjunto de objectos.
Retorno
Nada
Descrição
Calcula uma iteração do sistema, ou seja, move as partículas sujeitas a forças para uma nova posição no espaço.

 

Protótipo
void dpsDrawParticleSystem(DPSbitfield mask, DPSshandle handle);
Argumentos

mask=tipo de visualização, handle=handle para o conjunto de partículas que queremos desenhar.

Mask é, tal como o nome indica, uma máscara de bits. Essa máscara de bits pode ser conseguida através duma disjunção dos seguintes valores possíveis:

DPS_POINTS
Desenha cada partícula como um ponto.
DPS_LINES
Desenha cada partícula como uma linha de comprimento directamente proporcional à norma da velocidade.
DPS_COLOR
Faz com que as partículas sejam desenhadas coloridas
DPS_MESHES
Utiliza a função fornecida ao sistema por dpsMeshFunc para desenhar as partículas. Neste contexto a propriedade DPS_COLOR é ignorada, pois a cor é fornecida pela função que desenha a mesh.


Retorno
Nada
Descrição
Desenha um conjunto de partículas usando o tipo de visualização definido por mask.

 

Protótipo
void dpsParticlePosition3fv(DPSvector3f position, DPShandle p);
Argumentos
position=vector de três posições passado por referência que retorna a posição da partícula p.
p=handle para a partícula.
Retorno
Nada
Descrição
Retorna a posição de uma dada partícula num dado momento.

 

Protótipo
void dpsParticleVelocity3fv(DPSvector3f velocity, DPShandle p);
Argumentos
velocity=vector de três posições passado por referência que retorna a velocidade da partícula p.
p=handle para a partícula.
Retorno
Nada
Descrição
Retorna a velocidade de uma dada partícula num dado momento.

 

Protótipo
void dpsParticleColor4fv(DPSvector4f color, DPShandle p);
Argumentos
color=vector de quatro posições passado por referência que retorna a velocidade da partícula p.
p=handle para a partícula.
Retorno
Nada
Descrição
Retorna a cor de uma dada partícula.

 

Protótipo
void dpsRemoveParticle(DPSshandle shandle, DPShandle phandle);
Argumentos
shandle=handle para o conjunto, phandle=handle para a partícula.
Retorno
Nada
Descrição
Remove uma partícula de um conjunto de partículas.

 

Protótipo
void dpsRemoveForce(DPSshandle shandle, DPShandle fhandle);
Argumentos
shandle=handle para o conjunto, phandle=handle para a força.
Retorno
Nada
Descrição
Remove uma força de um conjunto de forças.

 

Protótipo
void dpsRemoveObject(DPSshandle shandle, DPShandle ohandle);
Argumentos
shandle=handle para o conjunto, phandle=handle para o objecto.
Retorno
Nada
Descrição
Remove um objecto de um conjunto de objectos.

 

Protótipo
DPSulong dpsLiveParticles(DPSshandle handle);
Argumentos
handle=para o conjunto de partículas.
Retorno
Numero de partículas do conjunto.
Descrição
Permite saber o numero de partículas dum conjunto de partículas.

 

 

Demonstrações

Juntamente com o DPS são incluídas algumas demonstrações das suas potencialidades.

Vulcão
Screen Shots
Descrição
São colocados diversos objectos do tipo DPS_SPHERE debaixo dum gerador de partículas do tipo cone. A gravidade puxa as partículas para baixo e estas chocam com as esferas, morrendo num plano que delimita o mundo.
A cada 100 unidades de tempo ocorre uma explosão.

 

Foguetes
Screen Shots

Descrição
Temos um gerador do tipo cone, de modo a obter uma maior dispersão das partículas geradas. Possuímos também uma força da gravidade que acelera as partículas para baixo. Sempre que uma partícula morre é invocada uma função que coloca dezenas de partículas e uma explosão no local do óbito.

 

Voar pelo espaço
Screen Shots

Descrição
Um gerador em forma de circulo lança partículas de encontro à câmara. As partículas quando nascem são de cor preta e quando estão próximas da câmara são brancas de forma a garantir um maior realismo à simulação. Esta demonstração pretende exemplificar como é possivel utilizar a pseudo-força TargetColor para fazer variar a cor das partículas.

 

Naves espaciais
Screen Shots

Descrição
Esta demonstração pretende mostrar a capacidade de desenhar uma partícula com a forma que se desejar e capacidade de fazer o roll dos objectos, ou seja, permitir que o objecto rode de acordo com a direcção do seu movimento.

 

Cascata
Screen Shots

Descrição

É apenas, mais uma cascata. Nenhum sistema de partículas pode deixar de o exemplificar.
É de notar a forma como as partículas escorregam pelo plano.

 

Exponenciação
Screen Shots

Descrição

É colocada uma partícula no mundo. Quando esta colide com o plano que o delimita esta divide-se em duas. As novas partículas reagem da mesma forma. Assim, em poucos segundos, possuímos um mundo populado de forma exponencial.

 

 

Apêndice A - Contantes e Tipos de Dados


Constantes

 

Domínio
Constante DPS
Valor
Descrição
Valores boleanos
DPS_FALSE
0x00
Falso
DPS_TRUE
0x01
Verdadeiro
Apontadores
DPS_NULL
0x00
Nulo
Tipos de Objectos
DPS_AVOID
0x01
Tipo de objecto - Objecto a evitar
DPS_COLLIDE
0x03
Tipo de objecto - Objecto de colisão
DPS_KILL
0x05
Tipo de objecto - Eliminador de partículas
DPS_SOURCE
0x07
Tipo de objecto - Fonte de partículas
Argumentos
DPS_EPSILON
1e-3f
Aproximação ao infinito
Tipos visuais de partículas
DPS_COLOR
0x40
Desenhar as cores das partículas
DPS_LINES
0x04
Desenhar partículas como linhas
DPS_POINTS
0x01
Desenhar partículas como pontos
DPS_MESHES
0x02
Desenhar partículas como objectos

 

Tipos de Dados

 

Tipo DPS
Tipo C
Descrição
DPSfloat
float
Real
DPSvector3f
float[3] Vector de reais
DPSvector4f
float[4] Vector de reais
DPSenum
unsigned int Enumeração de valores constantes
DPSboolean
unsigned char Booleano
DPSvoid
void Void
DPSsizei
int Inteiro
DPSulong
unsigned long Contador
DPShandle
void* Handle para uma partícula, força ou objecto
DPSshandle
unsigned long Handle para um conjunto
DPSbitfield
unsigned int Maskara de bits

 

 

Apêndice B - Glossário de Funções

 

Manipulação de Conjuntos

 

Protótipo
DPSshandle dpsGenParticleSystem(DPSsizei range);
Argumentos
range=numero de conjuntos a gerar
Retorno
Um handle para o primeiro conjunto gerado. Os seguintes são endereçaveis por handle + n. Este handle será utilizado para identificar um conjunto de partículas.
Descrição
Cria range numero de conjuntos de partículas.

 

Protótipo
void dpsNewParticleSystem(DPSshandle handle);
Argumentos
handle=identificador do conjunto de partículas
Retorno
Nada
Descrição
Indica ao DPS que passamos a trabalhar sobre o conjunto de partículas handle. A partir de agora podemos adicionar partículas a este conjunto. Não esquecer que, uma vez utilizado, devemos fechar o conjunto com dpsEndParticleSystem.

 

Protótipo
void dpsEndParticleSystem();
Argumentos
Nenhum
Retorno
Nada
Descrição
Fecha o conjunto actual de partículas.

 

Protótipo
DPSshandle dpsGenForceSystem(DPSsizei range);
Argumentos
range=numero de conjuntos a gerar
Retorno
Um handle para o primeiro conjunto gerado. Os seguintes são endereçaveis por handle + n. Este handle será utilizado para identificar um conjunto de partículas.
Descrição
Cria range numero de conjuntos de forças.

 

Protótipo
void dpsNewForceSystem(DPSshandle handle);
Argumentos
handle=identificador do conjunto de forças.
Retorno
Nada
Descrição
Indica ao DPS que passamos a trabalhar sobre o conjunto de forças handle. A partir de agora podemos adicionar forças a este conjunto. Não esquecer que, uma vez utilizado, devemos fechar o conjunto com dpsEndForceSystem.

 

Protótipo
void dpsEndForceSystem();
Argumentos
Nenhum
Retorno
Nada
Descrição
Fecha o conjunto actual de forças.

 

Protótipo
DPSshandle dpsGenObjectSystem(DPSsizei range);
Argumentos
range=numero de conjuntos a gerar
Retorno
Um handle para o primeiro conjunto gerado. Os seguintes são endereçaveis por handle + n. Este handle será utilizado para identificar um conjunto de partículas.
Descrição
Cria range numero de conjuntos de objectos.

 

Protótipo
void dpsNewObjectSystem(DPSshandle handle);
Argumentos
handle=identificador do conjunto de objectos.
Retorno
Nada
Descrição
Indica ao DPS que passamos a trabalhar sobre o conjunto de objectos handle. A partir de agora podemos adicionar forças a este conjunto. Não esquecer que, uma vez utilizado, devemos fechar o conjunto com dpsEndObjectSystem.

 

Protótipo
void dpsEndObjectSystem();
Argumentos
Nenhum
Retorno
Nada
Descrição
Fecha o conjunto actual de objectos.

 

 

Manipulação de Partículas

 

Protótipo
void dpsColor4f(DPSfloat r, DPSfloat g, DPSfloat b, DPSfloat a);
Argumentos
r, g, b, a definidos em [0.0, 1.0]
r=Red, g=Green, b=Blue, a=Alpha
Retorno
Nada
Descrição
Define a cor das partículas através dos diferentes valores de Red, Green, Blue e Alpha

 

Protótipo
void dpsColor3f(DPSfloat r, DPSfloat g, DPSfloat b);
Argumentos
r, g, b definidos em [0.0, 1.0]
r=Red, g=Green, b=Blue
O valor de alpha é colocar a 1.0
Retorno
Nada
Descrição
Define a cor das partículas através dos diferentes valores de Red, Green e Blue.

 

Protótipo
void dpsRGBADev(DPSfloat rdev, DPSfloat gdev, DPSfloat bdev, DPSfloat adev);
Argumentos
rdev, gdev, bdev, adev definidos em [0.0, 1.0]
rdev=Desvio Red, gdev=Desvio Green, bdev=Desvio Blue
Retorno
Nada
Descrição
Define o desvio padrão para cada uma das componentes de cor.

 

Protótipo
void dpsPosition3f(DPSfloat rx, DPSfloat ry, DPSfloat rz);
Argumentos
rx=Posição no eixo dos xx, ry=Posição no eixo dos yy, rz=Posição no eixo dos zz
Retorno
Nada
Descrição
Define a posição das partículas.

 

Protótipo
void dpsVelocity3f(DPSfloat vx, DPSfloat vy, DPSfloat vz);
Argumentos
vx=Velocidade no eixo dos xx, vy=Velocidade no eixo dos yy, vz=Velocidade no eixo dos zz
Retorno
Nada
Descrição
Define a velocidade vectorial das partículas.

 

Protótipo
void dpsAgef(DPSfloat age);
Argumentos
age=idade inicial da partícula.
Tipicamente este valor é definido a zero.
Retorno
Nada
Descrição
Define a idade inicial das partículas.

 

Protótipo
void dpsMassf(DPSfloat mass);
Argumentos
mass=massa da partícula
Retorno
Nada
Descrição
Define a massa das partículas.

 

Protótipo
void dpsMeshFunc(DPSvoid (*mesh)(DPSulong));
Argumentos
mesh=função que desenha o objecto pretendido.
O argumento da função fornecida deve ser um DPSulong, que representa o número de vezes que a função foi invocada. A utilidade deste argumento é poder alterar a forma da partícula ao longo do tempo.
Retorno
Nada
Descrição
Define a forma das partículas. A função mesh é invocada sempre que a partícula vai ser desenhada.

 

Protótipo
void dpsMeshRoll(DPSboolean roll);
Argumentos
roll=DPS_TRUE ou DPS_FALSE
Retorno
Nada
Descrição

Define se a mesh deve rodar de forma a seguir o movimento da partícula. Caso esse seja o caso, é fundamental que a mesh seja desenhada na origem, de pé e virada para a frente.

dpsMeshRoll(DPS_FALSE);
dpsMeshRoll(DPS_TRUE);

 

Protótipo
void dpsDieFunc(DPSvoid (*die)(DPShandle, DPSshandle, DPSshandle, DPSshandle));
Argumentos

die=função que é chamada quando uma partícula morre.
Os argumentos da função fornecida são handles para a partícula que morreu, para o conjunto das partículas, para o conjunto das forças e para o conjunto dos objectos.
A utilidade deste mecanismo é termos controlo total sobre o que fazer quando uma determinada partícula morre. Como por exemplo, gerar umas quantas partículas no local do óbito e aplicar uma explosão, para dar um efeito do tipo foguete a rebentar.

Retorno
Nada
Descrição
Defne a função que é invocada sempre que uma partícula morre.

 

Protótipo
void dpsCollideFunc(DPSvoid (*collide)(DPShandle, DPSshandle, DPSshandle, DPSshandle));
Argumentos

collide=função que é chamada quando uma partícula colide com algum objecto.
Os argumentos da função fornecida são handles para a partícula que colidiu, para o conjunto das partículas, para o conjunto das forças e para o conjunto dos objectos.
A utilidade deste mecanismo é termos controlo total sobre o que fazer quando uma determinada partícula colide. Como por exemplo, gerar umas quantas partículas no local da colisão e aplicar uma explosão, para dar um efeito do tipo granada a bater no chão.

Retorno
Nada
Descrição
Defne a função que é invocada sempre que uma partícula colide com um objecto.

 

Protótipo
DPShandle dpsNewParticle();
Argumentos
Nenhum
Retorno
Um handle para a partícula. Permite que a partícula seja removida ou alterada mais tarde.
Descrição
Cria uma nova partícula no conjunto de partículas actual, com a propriedades actualmente definidas.

 

 

Manipulação de Forças

 

Protótipo
DPShandle dpsNewDamping(DPSfloat d0, DPSfloat d1, DPSfloat d2, DPSfloat vlowSqr, DPSfloat vhighSqr);
Argumentos
Se a velocidade de uma partícula não se encontra no intervalo [vlow, vhigh], ento cada componente da velocidade multiplicada pelas constantes de damping. Por norma damping_x, damping_y, damping_z so semelhantes. Como no existem limites para os valores de damping, se usarmos valores superior a 1.0 as partculas sero aceleradas.
Retorno
Hande para a força. Permite que a força seja removida ou alterada mais tarde.
Descrição
Simula a resistencia do ar, diminuindo a velocidade das partículas.

 

Protótipo
DPShandle dpsNewExplosion(DPSfloat x, DPSfloat y, DPSfloat z, DPSfloat velocity, DPSfloat magnitude, DPSfloat stdev, DPSfloat epsilon, DPSfloat age);
Argumentos
x, y, z = centro da explosão; velocity=velocidade com que as partículas são afastadas do centro; magnitude=intensidade da força; stdev=desvio padrão da velocidade; age=idade da força (quanto maior for a idade menor é a intensidade da explosão)
Retorno
Hande para a força. Permite que a força seja removida ou alterada mais tarde.
Descrição
Simula uma exploso. Afasta todas as partculas do centro, com uma fora proporcional magnitude.

 

Protótipo
DPShandle dpsNewFollow(DPSfloat magnitude, DPSfloat epsilon, DPSfloat max_radius);
Argumentos
magnitude=intensidade da força, max_radius=raio de acção da força
Retorno
Hande para a força. Permite que a força seja removida ou alterada mais tarde.
Descrição
Acelera a partcula na direco da seguinte partcula do conjunto.
Isto permite dispor as partculas em fila indiana. Cada partcula acelerada na direco da partcula seguinte do conjunto, com uma fora proporcional a magnitude.

 

Protótipo
DPShandle dpsNewGravity(DPSfloat x, DPSfloat y, DPSfloat z);
Argumentos
Os argumentos dados representam o vector acelerao de gravidade, que ser adicionado ao vector velocidade de cada partcula.
Retorno
Hande para a força. Permite que a força seja removida ou alterada mais tarde.
Descrição
Simula o efeito gravidade acelerando as partculas numa dada direco.

 

Protótipo
DPShandle dpsNewGravitate(DPSfloat magnitude, DPSfloat epsilon, DPSfloat max_radius);
Argumentos
magnitude=cada partcula acelerada em direco a todas as outras partculas com uma fora proporcional a magnitude; max_radius=define o raio mximo de influência desta aco. Assim partculas que distam mais do que max_radius no se influenciam.
Retorno
Hande para a força. Permite que a força seja removida ou alterada mais tarde.
Descrição
Simula foras de atraco entre as partculas.

 

Protótipo
DPShandle dpsNewSpeedlimit(DPSfloat min, DPSfloat max);
Argumentos
min, max=limites de velocidade
Retorno
Hande para a força. Permite que a força seja removida ou alterada mais tarde.
Descrição
Obriga a velocidade de cada partcula manter-se entre um dado valor mnimo e um mximo.

 

Protótipo
DPShandle dpsNewTargetColor(DPSfloat c1, DPSfloat c2, DPSfloat c3, DPSfloat alpha, DPSfloat scale);
Argumentos
c1=red, c2=green, c3=blue, alpha=alpha, scale=numero de iterações necessárias para atingir a cor destino.
Retorno
Hande para a força. Permite que a força seja removida ou alterada mais tarde.
Descrição
Faz com que a cor das partículas seja alterada para a cor especificada. Se o valor de scale for maior que zero esta força efectua uma transição suave entre a cor original e a cor destino.

 

Protótipo
DPShandle dpsNewVortex(DPSfloat p0, DPSfloat p1, DPSfloat p2, DPSfloat axis0, DPSfloat axis1, DPSfloat axis2, DPSfloat magnitude, DPSfloat epsilon, DPSfloat max_radius);
Argumentos
p0, p1, p2= ponto central; axis0, axis1, axis2= eixo; magnitude=intensidade da força; max_radius=raio de accção
Retorno
Hande para a força. Permite que a força seja removida ou alterada mais tarde.
Descrição
Movimenta as partículas em torno de um eixo.
O centro e o axis definem uma linha infinita, onde o centro representa a origem do vortex e o axis um vector ao longo dessa linha, cujo comprimento irrelevante. Usando-se um epsilon semelhante magnitude pode-se aumentar o raio de influencia do vortex. O raio_max define o campo de actuao desta fora.

 

Protótipo
DPShandle dpsNewKillOld(DPSfloat age_limit, DPSboolean kill_less_than);
Argumentos
age_limite=limite de idade, kill_less_than=elimina as partículas mais antigas ou as mais recentes que age_limit
Retorno
Hande para a força. Permite que a força seja removida ou alterada mais tarde.
Descrição
Elimina as partículas com idade superior a age_limit se o argumento Kill_less_than for falso. Se o argumento Kill_less_than for verdadeiro, elimina todas as partículas com idade inferior a age_limit.

 

 

Manipulação de Objectos

 

Protótipo
DPShandle dpsNewSphere(DPSfloat x, DPSfloat y, DPSfloat z, DPSfloat ri, DPSfloat ro, DPSenum action, DPSfloat var1, DPSfloat var2, DPSfloat var3);
Argumentos

x,y,z=centro da esfera; ri,ro = raio interior e exterior;
action, var1, var2, var3 = ver tipo de objectos;

 

Retorno
Um handle para o objecto. Permite que objecto seja removido ou alterado mais tarde.
Descrição
Cria uma nova esfera no conjunto de objectos actual.
A esfera pode ser maciça bastando ter o raio interior igual zero. Pode também ser oca se o raio interior for maior que zero;

 

Protótipo
DPShandle dpsNewPlane(DPSfloat x0, DPSfloat y0, DPSfloat z0, DPSfloat x1, DPSfloat y1, DPSfloat z1, DPSenum action, DPSfloat var1, DPSfloat var2, DPSfloat var3);
Argumentos

x0,y0,z0 = ponto do plano; x1,y1,z1 = vector normal ao plano
action, var1, var2, var3 = ver tipo de objectos;

 

Retorno
Um handle para o objecto. Permite que objecto seja removido ou alterado mais tarde.
Descrição
Cria um novo plano no conjunto de objectos actual.

 

Protótipo
DPShandle dpsNewRectangle(DPSfloat x0, DPSfloat y0, DPSfloat z0, DPSfloat x1, DPSfloat y1, DPSfloat z1, DPSfloat x2, DPSfloat y2, DPSfloat z2, DPSenum action, DPSfloat var1, DPSfloat var2, DPSfloat var3);
Argumentos

x0,y0,z0 = o =ponto base do rectângulo; x1,y1,z1 = u = vector director do rectângulo; x2,y2,z2 = v =vector director do rectângulo;
action, var1, var2, var3 = ver tipo de objectos;

 

Retorno
Um handle para o objecto. Permite que objecto seja removido ou alterado mais tarde.
Descrição
Cria um novo rectangulo no conjunto de objectos actual.
O resultado é um losângulo em que os seus cantos são : o, o+u, o+u+v, o+v;
Os vectores não devem ser paralelos mas não precisam de ser ortongonais nem normais;

 

Protótipo
DPShandle dpsNewTriangle(DPSfloat x0, DPSfloat y0, DPSfloat z0, DPSfloat x1, DPSfloat y1, DPSfloat z1, DPSfloat x2, DPSfloat y2, DPSfloat z2, DPSenum action, DPSfloat var1, DPSfloat var2, DPSfloat var3);
Argumentos

x0,y0,z0; x1,y1,z1; x2,y2,z2; definem os três vértices do triângulo, podendo este ter uma forma arbitrária;
action, var1, var2, var3 = ver tipo de objectos;

 

Retorno
Um handle para o objecto. Permite que objecto seja removido ou alterado mais tarde.
Descrição
Cria um novo triangulo no conjunto de objectos actual.

 

Protótipo
DPShandle dpsNewDisc(DPSfloat x0, DPSfloat y0, DPSfloat z0, DPSfloat x1, DPSfloat y1, DPSfloat z1, DPSfloat ri, DPSfloat ro, DPSenum action, DPSfloat var1, DPSfloat var2, DPSfloat var3);
Argumentos

x,y,z=centro do disco; x1,y1,z1 = vector normal ao disco;ri,ro = raio interior e exterior;
action, var1, var2, var3 = ver tipo de objectos;

 

Retorno
Um handle para o objecto. Permite que objecto seja removido ou alterado mais tarde.
Descrição
Cria um novo disco no conjunto de objectos actual. Um disco é como um circulo mas no espaço tri-dimensional, ou seja, não possui altura.

 

Protótipo
DPShandle dpsNewCone(DPSfloat x0, DPSfloat y0, DPSfloat z0, DPSfloat x1, DPSfloat y1, DPSfloat z1, DPSfloat ri, DPSfloat ro, DPSenum action, DPSfloat var1, DPSfloat var2, DPSfloat var3);
Argumentos

x0,y0,z0= centro da base; x1,y1,z1 = outra extremidade ;ri,ro = raio interior e exterior;
action, var1, var2, var3 = ver tipo de objectos;

 

Retorno
Um handle para o objecto. Permite que objecto seja removido ou alterado mais tarde.
Descrição
Cria um novo cone no conjunto de objectos actual.

 

Protótipo
DPShandle dpsNewCylinder(DPSfloat x0, DPSfloat y0, DPSfloat z0, DPSfloat x1, DPSfloat y1, DPSfloat z1, DPSfloat ri, DPSfloat ro, DPSenum action, DPSfloat var1, DPSfloat var2, DPSfloat var3);
Argumentos

x0,y0,z0= centro da base; x1,y1,z1 = outra extremidade; ri,ro = raio interior e exterior;
action, var1, var2, var3 = ver tipo de objectos;

 

Retorno
Um handle para o objecto. Permite que objecto seja removido ou alterado mais tarde.
Descrição
Cria um novo cilindro no conjunto de objectos actual.

 

Protótipo
DPShandle dpsNewCube(DPSfloat x0, DPSfloat y0, DPSfloat z0, DPSfloat x1, DPSfloat y1, DPSfloat z1, DPSenum action, DPSfloat var1, DPSfloat var2, DPSfloat var3);
Argumentos

x0,y0,z0= vértice do cubo; x1,y1,z1 = vértice do cubo;
action, var1, var2, var3 = ver tipo de objectos;

 

Retorno
Um handle para o objecto. Permite que objecto seja removido ou alterado mais tarde.
Descrição

Cria um novo cubo no conjunto de objectos actual.
O cubo gerado é centrado e alinhado nos eixos.

 

 

Manipulação Dinâmica

 

Protótipo
void dpsTimeIncrementf(DPSfloat dt);
Argumentos
dt = Incremento de tempo.
Valores normais para este valor são oscilam entre 0.0 e 1.0.
Retorno
Nada
Descrição
Define o incremento de tempo dt que deve ser utilizado sempre que é calculada uma nova iteração. Este parametro é muito importante, pois se não for definido o sistema não anima. Pode ser redefinido ao longo do tempo de simulação de modo a acelerar ou diminuir a velocidade da mesma.

 

Protótipo
void dpsIterateParticles(DPSshandle particles, DPSshandle forces, DPSshandle objects);
Argumentos
particles=handle para o conjunto de partículas, forces=handle para o conjunto de forças, objects=handle para o conjunto de objectos.
Retorno
Nada
Descrição
Calcula uma iteração do sistema, ou seja, move as partículas sujeitas a forças para uma nova posição no espaço.

 

Protótipo
void dpsDrawParticleSystem(DPSbitfield mask, DPSshandle handle);
Argumentos

mask=tipo de visualização, handle=handle para o conjunto de partículas que queremos desenhar.

Mask é, tal como o nome indica, uma máscara de bits. Essa máscara de bits pode ser conseguida através duma disjunção dos seguintes valores possíveis:

DPS_POINTS
Desenha cada partícula como um ponto.
DPS_LINES
Desenha cada partícula como uma linha de comprimento directamente proporcional à norma da velocidade.
DPS_COLOR
Faz com que as partículas sejam desenhadas coloridas
DPS_MESHES
Utiliza a função fornecida ao sistema por dpsMeshFunc para desenhar as partículas. Neste contexto a propriedade DPS_COLOR é ignorada, pois a cor é fornecida pela função que desenha a mesh.


Retorno
Nada
Descrição
Desenha um conjunto de partículas usando o tipo de visualização definido por mask.

 

Protótipo
void dpsParticlePosition3fv(DPSvector3f position, DPShandle p);
Argumentos
position=vector de três posições passado por referência que retorna a posição da partícula p.
p=handle para a partícula.
Retorno
Nada
Descrição
Retorna a posição de uma dada partícula num dado momento.

 

Protótipo
void dpsParticleVelocity3fv(DPSvector3f velocity, DPShandle p);
Argumentos
velocity=vector de três posições passado por referência que retorna a velocidade da partícula p.
p=handle para a partícula.
Retorno
Nada
Descrição
Retorna a velocidade de uma dada partícula num dado momento.

 

Protótipo
void dpsParticleColor4fv(DPSvector4f color, DPShandle p);
Argumentos
color=vector de quatro posições passado por referência que retorna a velocidade da partícula p.
p=handle para a partícula.
Retorno
Nada
Descrição
Retorna a cor de uma dada partícula.

 

Protótipo
void dpsRemoveParticle(DPSshandle shandle, DPShandle phandle);
Argumentos
shandle=handle para o conjunto, phandle=handle para a partícula.
Retorno
Nada
Descrição
Remove uma partícula de um conjunto de partículas.

 

Protótipo
void dpsRemoveForce(DPSshandle shandle, DPShandle fhandle);
Argumentos
shandle=handle para o conjunto, phandle=handle para a força.
Retorno
Nada
Descrição
Remove uma força de um conjunto de forças.

 

Protótipo
void dpsRemoveObject(DPSshandle shandle, DPShandle ohandle);
Argumentos
shandle=handle para o conjunto, phandle=handle para o objecto.
Retorno
Nada
Descrição
Remove um objecto de um conjunto de objectos.

 

Protótipo
DPSulong dpsLiveParticles(DPSshandle handle);
Argumentos
handle=para o conjunto de partículas.
Retorno
Numero de partículas do conjunto.
Descrição
Permite saber o numero de partículas dum conjunto de partículas.

 

Z6y ];CƼ.YN%s*[Tlש-Ϭ "AcBёd<)Hu)n4ݛ0cյ~\׷B [튂OX(<~X1C]Kl:/e}ݝ8y6K9fԏu{jS[@O4t<^UմGiׯiZ¹[_Dk- Wf8{PF{F/*T  b ,n[tfQҖñA$xs1 y̧w2B1o5WzN{Ubߦ(avώ\C I5x)&gA{lJċ&cd12h@S=\7K(sRQ`EF@IN ~Ur]mohV(ƷyRL }|dJ!lvKO܁d,^M ’&^z!K0lɆB0I0;crUuq)gYЁTh+Qb$m%鬩,80Ds m2cc=3ϜG㠍E3u8! (IӗOxzVng_(uNIA.D7Q@x'b U'[\?I^{͵у28R~L|qXwSEN#zs;hf=g"^yKϷ@QT.EH?`nVv m?s! U٤0j-BH?6^Y",Ry0C)sg,k;WWļgQ`]F-E_߿o-;܅dygZmc 0P50j3P IAY:h>`O!!鷬,$A SDkJ2.):)JyP%4 N6 擗E''vKgks6xd* F#eNeu[wK .ŕgq/dL^ou\R] |ss]׬ IۀU[wL߫ZN =.";sPdVwmű:bd@^ܵ`$cQz!1'JhBvӛ#H;ׅTNi;Օ<4hd֧$HbբqAk?rDe9_gbxmd%KDC`ŢB4b0܏G|D c%e3(,IdGbGL2 !_B%73w VMf=/JXo+<廉?ѩrZ?~)H)>4f#N<[&p2\-$w{E>KX(Uv^xR.%tWZRmW k+R8^ ,$Y0IM,E`Ueh ֪ jkeZWW uEW fvsqycOrin1R704XX_W_"Uhr=D^(/L!!% e: w7榐"%m"͝G}<? |LR8B " P`VI])U2%$`n >Uхt+[x G6G&ɞ `XfS:;' 58XP>dRL*8`R0V܅LYncKj|F*i^ εTקOrJbt7L٦DBIYnmwC'(҂ qJ&ᵀ?^Q^nNv5@K mK6fu`iT̂*tfԬγx+k^~Wlkܼ~МAbSα7.95.~Z.7>9]yF|*.vˇvrX/ ᕗo~z;C 9 '3$@,]xz;uT=+gjUgu6t <*aˈS"N85.\sNUˇo^&;~'qߴ6]U\Gz~p쮸뫆our۸Jڗ]E0[pE5` .BY i ʃe&}s&{3ryOgK|jy:8 ?v5|,bg[L[aks=&՘(y=^MoΗCD">l+7]$HH:i2h]%rB ǁ[ UZSTN,%@NsVgn axY'pԹ3J`,S198}d;NvTN>u}mw8KO7.lMVKdx:G',TWA{Vpbb RkZ6Sq6r.;Vz yb9cNziag='ɵֳzh>ʀݗKӼl0>MqoLo2Ke5~6?Y2D}b39qu'YKy<UcaN'IVwUk~:F |<[/I}\(ΌoKQ$it}K ʨ$L2tLK] &B$a`5. 5߳1%[T=71N?9^0>kJb+I ]0DG>7N\Bċ_兟0gNz17d?@PM]@X$6&^0=X3qEC?߆ˏ| c.,7qFҙtD؉DO@N"]{HQ͕A-W>/}K&Q0Fea`p㯠 ^6/c uH5lc{dSCX~8d.(E/zqJMЫ8!P@ ˛((eaټtOS؁P[|Yi{z&Dž.Xroh=<[DhQZh:[1Ӄ)2-0A)!KC\<1Rga[$0A(2uk8K ř aQ֢q a{.s[*Uy<-ר8t4)B ~OZT)ձ!|^;L+h튴[`GL6vAΥ&ؖo'jQUb^&'=ܨqlófFGZzWؔjU5\ِ \ؐb6PnFO |b"_`tˆW6JGIwgl 67d8oli&KiAAud]~U$?cndP4Zc\/͉RWo$@Ys8a";{ȒC;P5ʧ ƹEPgL^>9%?Hڟ{_δZޔ踯WhDr2W6\\ynAB IZ$xU}w2̐f(| nρd]d%ׂ'% g^ڲF1Utq&b^HQ {p[ZtP43~VBB]h](pOi;cT0sIRP>"s3/:Ɖ3d}*)Ka9g?Ϟ6TJ U$ӅP![-r!{󖏯Լi Ix}RF2uiX2aCgnlCC\u*bnrrx^w z>.#इ%"^q4րY,:oBI( |NԘ0/zr R>1PZV]N\d >a3{5P1)>I`u1(}OL XMiFD3t{ovSB ,mVus`g? h 0u(2 JXG%.ClyaBKX{2H$R8*k+t/4RPAjq}1hady/4J2ƗC)`5'SR%`62ze Z0" :>qc3vϴn#|j܄Jmǝ>]UpRb5̀0f;Qh쫁E_%vM9}fl16ٵ`g;8zu$Qd'I 7aa7mN!_ = S|oon\_ዂ vb0٥O=cz<RoƧ9NEi ڒ{O<\rN<~6WŸ氒˭e$)6حkt\X`!q.3YCaCc|_%KLn@!4PXnm+Tb/玜15>>XG1?o L 38IT8%}HU`?/r,Ż =bh7N-9JIVb~Dst%y|<:f5Hޗ2.zX #wRrc=*TKF-.A0Recbޠg!~JvJ?y_?\Hd^7d!fbǏYG#e1NAr=i{w# ($u *v"H$)~K:;%OGUX=ժ:ȫ*揦2+uH([dxIbijr˖Rec?3:܂ag ָ\$mnU&+4Qq`+R6x`FsoH %%jc{#$9c3EP CN7`l^ VɦE}mb0r0I~Idf1y vs fi~~1qmD?ǷhlaXAUW Lj¥@DwFRmk ㊊$ !؊Y#| h:u~o^I6&v'xU %9% .]r8>,OUI%%x+c'qhXH %μ0ϐn$A-2'O>nHq'.aDU' gfsU\v&r"T~)m/ZtԂa@7(ךޙ^*z!-|={E2dLeZ{aͨJcGiYϒb zc>)7Uk>mf?'P)p:b,x}e^0a:w; xpBN%ęu}>G 6}L Z/giyӽ|WEȑ̶mYOݻc| %7LEUlƃ7I37p@9%Z^͓+ Z?u`>u3@ԃ<<ǽh0h h*9d%KhZoےiUfS}#|6$Z ;!E|Yc VN2ZxGGCϥP@)=.km[w`7VݘRG` ..~ᦴy@4khFV Um[פoʈوnsKtfhDnpqH|]JVAx>Vg3Z aϛP0ha*KxH'խ O%<4Ա:jp*=x-,7XI\,a0ŀ”v="Uآ88M3R)5߫u9pn.94y-ꯉuJ2ԊծƲ%r&F*9h]8"{i.fc E"]ݗխA}ЏRC؁-[迀%]DD1NmF@H5d;auM|\8hQpHTڵ`1_CMhQH+#,g S$vRPul?$!ړ hRCsWXCJ1F PFYÿ \ph2  >ndp6kI&#ARH4Rաh*m[{qF;GJg-HQMf)[J99mi: ELFY_&KQWEk%hוO)ZjZln+-[PR4aDY4m#W+5˼ w&)G~@jZ݂y9~uX|m9&;g*ٱl ya9N,m!(ϮLBMNxeWNor^8>֦OJ Q>6BuO(Đ,&b7o<Q:ۢ+ L;M%X"/^;'X}=gȶ+[ f&1latl:ٯTF^4e!|h(4=-yPI9FfV~Kpq)$5c' )Ю3GSc-' # {sh-魯aRv0bC-.2).űL\d=-sG1{TEu3Yk*#L 1_٭SY)0= cuQk)L :Thêef(Ыe](8oq-{hҴDQ! 'O%usT=T]cD2 L,wg eP>9"h.x^2>$c ON[&_OP]6͒&d/7B|6_I-u~UT? '^#oi؅7[f'oN>nB&{H%&}괙,]kWN`=a1{L5֑IO~rݖ]P+> }G6$.]]{vUI"[L}紹g85 .:mn kg'(uO6$?+=YjLLeBsգ7 {`>}ɞ,] DM?Jģ$钏׽fS_z2Wܾ>߾6[H}[:_!;Y%:/L˶W}l8b,YЄ <+ ~ˡ} ]֎=cCmKFAq_G;UV7 mY| ;FJ|ty6Wp+z?lЩǶ}AQ)-Euycݕ.3~`q6x8T-fIS;G]koF .h%Gvc0CkdqCZ{̐CRv@8;g sS)KGCj`iwzci h+f* :dUqUęӤ?2MN)ni2zZ"`d7e& AeXt=t??A7.ko0]۵Uo -ciJھԬi3V3j$5&qgc2K(Yĉgv_{+!O3&`HFuHAB2:6STI9NB<-E|,N8WrZ[dZ? TM܏Aʥp o-ŅҼQ81*%;i$-8Y#jABOMɾ+Lc.){uyK^u6$. <ʸLOwix5, wo'o^+ qEzi?d{!4{X|13Ofx%NVwնʸLUgB<-v9R:Χiv'@l RLn"hG(|卯XT>6`s}J"9 6M9 TSjP5G>;ϔ\RK#C&R/ a Pʪ]eIqV]DTVzxhNyȽ,b̆NuRWFл7ř)>^'LQ44IK,C: v|0ʐ% Y%YAr;CWa.h|pa#uYB}ʰ[V2OdVGv/_w`)*(X[eMy,6mê=ʅWY(^So24Ս.QDmd'wwhk@oL)xPcFtюGhs>|G .f_'Óh p}s4SIEj*?|ENc{bL?Z'sGNQ:7˧mikviFQpG'K)gcO[>Po}vAWi^*ӗqSi)A&8G07ϟl1^