Jak vyzrát na efekty 5.díl - Lepší částice - Builder.cz - Informacni server o programovani

Odběr fotomagazínu

Fotografický magazín "iZIN IDIF" každý týden ve Vašem e-mailu.
Co nového ve světě fotografie!

 

Zadejte Vaši e-mailovou adresu:

Kamarád fotí rád?

Přihlas ho k odběru fotomagazínu!

 

Zadejte e-mailovou adresu kamaráda:

Soutěž

Sponzorem soutěže je:

IDIF

 

Kde se koná výstava fotografií Luďka Vojtěchovského?

V dnešní soutěži hrajeme o:



C/C++

Jak vyzrát na efekty 5.díl - Lepší částice

effekt

11. listopadu 2002, 00.00 | Seriál byl sice ukončen, nicméně si dovolím se k tématu vrátit v tomto článku a čtenářům představit pár vylepšováků. Nejde o opravu závažných chyb, spíše o vylepšení po stránce grafické a programově efektivní.

{Změněné objekty}

Původně jsem chtěl tento seriál ukončit, ale postupem času jsem začal přicházet na lepší způsoby zpracování jednotlivých částí / článků. Nejde o opravu závažných chyb, spíše o vylepšení po stránce grafické a programově efektivní. Původní zpracování částic mělo několik much. Vyemitované částice se velmi těžko pohybovaly spolu s emitorem, částice bylo možno emitovat v dosti omezeném prostoru (pomocí úhlů alpha a beta). Změna směru částice působením např. gravitace nebyla taktéž možná. Postupem času jsem na tyto nedostatky začal narážet a snažil jsem se je odstranit. Nakonec jsem byl donucen přeprogramovat celý systém částicových emitorů, částic, atd. Rád bych vás tedy s těmito změnami seznámil.

Změněné objekty
Projdeme si tedy změny v jednotlivých objektech. Každá změna je zvýrazněna modrou barvou. Jako první bych zvolil objekt C_PARTICLE_EMITOR. Aby jsme mohli emitor svázat s objektem (včetně rotace), přidáme si proměnné init_x, init_y, init_z a rel_x, rel_y, rel_z. Proměnné, které začínají na init slouží k uložení výchozí pozice emitoru, ty začínající na rel slouží k uložení otočené pozice init..., pokud se emitor otáčí spolu s objektem.
class C_PARTICLE_EMITOR
{
public:

  int used;
  float x,y,z;
  float init_x,init_y,init_z;
  float rel_x,rel_y,rel_z; //relativne k objektu

  char had_owner;
  char emit_particles;
  char moved; //if the emitor is moved in this loop or not (in Refresh_Links we will se it to the FALSE)
  unsigned int emit_count;

  float alpha,beta;
  float wanted_beta;

  float start_size;
  float end_size;
  float life;

  float size_noise;
 
int life_noise;
  float color_noise;
  float start_speed;
  float end_speed;
  float differention_speed;

  float alpha_noise; 
  float beta_noise; 
  float alpha_start;
  float beta_start;


  float emit_radius;

  float size_dec;
  float speed_dec;

  int texture_ID;

  char lock_position_with_owner;

  T_COLOR_RGBA start_color;
  T_COLOR_RGBA end_color;
  T_COLOR_RGBA color_dec;

  C_FYZ_OBJECT * owner;

  C_PARTICLE_EMITOR();
  ~C_PARTICLE_EMITOR();

  void Reset();
  void Move();
  void New_Particle(int num);
  void Emit_Particle();
  void Link_To_Object(C_FYZ_OBJECT * to);
  void Lock_Position_With_Owner(int yes_or_no);
  void Bind_Texture(int ID);
  void Bind_Texture(char file[]);
  void Compute_Color_Dec(void);
  void Compute_Rotation_Matrix(void);
};

Nejjednodušší bude popsat funkce. Funkce Move() pohybuje s emitorem, tzn. přepočítává jeho pozici pokud je emitor svázán s objektem. Emit_Particles() se volá automaticky v každém cyklu a stará se o pravidelné emitování částic (počet je určen proměnnou emit_count) a probíhá pouze pokud je emit_particles roven 1 (true). Compute_Color_Dec() slouží k naplnění color_dec (vypočítá se color_dec = (color_end - color_start) / life). Jde tedy o linearni interpolaci barvy v závislosti na čase. V téže funkci se počítá i size_dec (změna velikosti), speed_dec (změna rychlosti). A poslední změněná fce. Compute_Rotation_Matrix() slouží k vypočítání rotace / rotační matice emitoru, zadané pomocí alpha, beta.
void C_PARTICLE_EMITOR::Emit_Particle()
{
  New_Particle(emit_count);
};

void C_PARTICLE_EMITOR::Move()
{
  C_VECTOR position(init_x,init_y,init_z);

  if(owner != NULL)  //pokud je vlastnik
  {
    position = owner->rotation_matrix * position; //pozice se otoci podle matice vlastnika

    x = position.vx + owner->x; //ulozi se pozice podle relativni pozice emitoru a pozice vlastnika
    y = position.vy + owner->y;
    z = position.vz + owner->z;

    rel_x = position.vx;  //pro pozdejsi potrebu se ulozi i relativni pozice vuci vlastnikovi
    rel_y = position.vy;
    rel_z = position.vz;

    rotation_matrix = initial_rotation_matrix * owner->rotation_matrix;  //otoci se rotacni matice podle vlastnika
  }
  else  //vlastnik neni
  {
    rel_x = x = init_x;
    rel_y = y = init_y;
    rel_z = z = init_z;

    rotation_matrix = initial_rotation_matrix;
  };

  moved = TRUE;  //uz jsme jednou pozici prepocitali
};

void C_PARTICLE_EMITOR::Compute_Color_Dec(void)
{
  if(life != 0.0f)
  {
    color_dec[0] = (float)(start_color[0] - end_color[0]) / (float)life;
    color_dec[1] = (float)(start_color[1] - end_color[1]) / (float)life;
    color_dec[2] = (float)(start_color[2] - end_color[2]) / (float)life;
    color_dec[3] = (float)(start_color[3] - end_color[3]) / (float)life;
    size_dec = (float)(start_size - end_size) / (float)life;
    speed_dec = (float)(start_speed - end_speed) / (float)life;
  };
};

void C_PARTICLE_EMITOR::Compute_Rotation_Matrix(void)
{
   initial_rotation_matrix = Get_Rotation_Matrix3x3(-beta,-alpha);
};


Ha, co je to Get_Rotation_Matrix3x3()? Jde o funkci, která vrátí rotační matici (otočení XY -> alpha = Y, beta = X,v programu). Ovšem záleží na orientaci souřadného systému. Já používám tento, tak ...
C_MATRIX3x3 Get_Rotation_Matrix3x3(float alpha,float gamma)
{
  C_MATRIX3x3 temp;

  double sinx,sinz,cosx,cosz;

  alpha = RAD(alpha);
  gamma = RAD(gamma);

  sinx = sin(alpha);
  sinz = sin(gamma);
  cosx = cos(alpha);
  cosz = cos(gamma);

  temp.m[0] = cosz;
  temp.m[1] = -sinz;
  temp.m[2] = 0;
  temp.m[3] = cosx * sinz;
  temp.m[4] = cosx * cosz;
  temp.m[5] = -sinx;
  temp.m[6] = sinx * sinz;
  temp.m[7] = sinx * cosz;
  temp.m[8] = cosx;

  return temp;
};

Jde pouze o úpravu násobení dvou rotačních matic. Díky předpočítání sinů a cosinů se vypočet o něco zrychlí. Nejprve vypočítám matici rotace okolo X, dále pak kolem Y. (v mém souřadném systému se kamera kouká ve směru osy Z, nahoru je osa Y, ...)

[-more-]{Objekt částice} Objekt částice
I objekt / třída částice prošla menšími úpravami. Jednak jsem přišel na lepší způsob vykreslování, jde o metodu tzv. billboardingu, přeci jen násobení matic je dost pomalé. V čem tato metoda spočívá? Získáme tři vektory, které zastupují otočené osy souřadného systému po provedené transformaci (myslím tím otočení a posunutí, které provedeme při umisťování kamery do prostoru). Kde vezmeme tyto vektory? Odpověď je také velmi jednoduchá. Pomocí příkazu glGetDoublev(GL_MODELVIEW_MATRIX,pole); získáme aktuální transformační matici, která se uloží do 16ti prvkového pole typu double (matice 4x4). Co je v takové matici uloženého?

K vykreslení použijeme místo GL_QUADS GL_TRIANGLE_STRIP, jednotlivé vrcholy napočítáme pomocí vektorů ze zobrazovací matice (OX... a OY...)

void C_PARTICLE::Draw()
{
  float temp_x,temp_y,temp_z;

  if(!used) return;  // castice se v poli nepouziva, tak ji nebudeme vykreslovat


  glColor4fv(color);  //nastavime barvu
  glBindTexture(GL_TEXTURE_2D,texture_ID);  //nastavime texturu

  engine.renderer.Draw_BillBoard(x + rel_x,y + rel_y,z + rel_z,camera,fabs(size));

  glBegin(GL_TRIANGLE_STRIP);

  //Pravy horni
    temp_x = ((OXx + OYx) * size) + x + rel_x;
    temp_y = ((OXy + OYy) * size) + y + rel_y;
    temp_z = ((OXz + OYz) * size) + z + rel_z;
    glTexCoord2f(1,1);
    glVertex3f( temp_x, temp_y,temp_z);
  //Levy horni
    temp_x = ((OYx - OXx) * size) + x + rel_x;
    temp_y = ((OYy - OXy) * size) + y + rel_y;
    temp_z = ((OYz - OXz) * size) + z + rel_z;
    glTexCoord2f(0,1);
    glVertex3f( temp_x, temp_y,temp_z);
  //Pravy dolni
    temp_x = ((OXx - OYx) * size) + x + rel_x;
    temp_y = ((OXy - OYy) * size) + y + rel_y;
    temp_z = ((OXz - OYz) * size) + z + rel_z;
    glTexCoord2f(1,0);
    glVertex3f( temp_x, temp_y,temp_z);
  //Levy dolni
    temp_x = ((OXx + OYx) * -size) + x + rel_x;
    temp_y = ((OXy + OYy) * -size) + y + rel_y;
    temp_z = ((OXz + OYz) * -size) + z + rel_z;
    glTexCoord2f(0,0);
    glVertex3f( temp_x, temp_y,temp_z);

  glEnd();
};


To jsem se ale trochu předběhl, podívejme se na objekt / třídu částice.
class C_PARTICLE
{
public:
  float x,y,z;  // pozice (vypocitana v kazdem kroku)
  float rel_x,rel_y,rel_z; //relativni pozice vuci emitoru

  T_COLOR_RGBA color;  // barvy (aktualni, na zacatku, ubytek barvy za ms)
  T_COLOR_RGBA color_start;
  T_COLOR_RGBA color_decrease; //color decrase per one tick

  C_VECTOR direction; // smer pohybu
  C_VECTOR wanted_direction; //pozadovany smer pohybu, viz. nize

  float size_start; // velikost (na zacatku, ubytek velikosti za ms, aktualni)
  float size_decrease;
  float size;

  float speed; // rychlost (aktualni, na zacatku, ubytek rychlosti za ms)
  float start_speed;
  float speed_dec;


  unsigned int life_end; // cas kdy castice zemre
  int life_length; // jak dlouho uz zije
  int life; //celkova delka zivota castice

  char position_locked_with_owner; //jestli je pozice prepocitavana podle emitoru
  char direction_can_change; //muze se smer castice menint v prubehu zivota (wanted_direction)
  char used; //jestli se castice v poli pouziva , ci ne
  int texture_ID; //ID textury

  C_PARTICLE_EMITOR * owner; //emitor, ktery vytvoril tuto castici

  C_PARTICLE();
  ~C_PARTICLE();

  void Move(int ticks);
  void Draw();  
};

To by byl začátek, funkce obsluhující pohyb si probereme až ke konci tohoto článku, omlouvám se za tuto roztrhanost, ale musíme si popsat spoustu věcí, aby ty další dávaly smysl.

[-more-]{Generování částice} Generování částice
Někomu z vás může být divné, proč tady vykládám, jak mám orientovaný souřadný systém. Je to kvůli určité představě jak celý systém funguje, neboť pak je velmi jednoduché si ho libovolně upravit. U Předešlých částicových emitorů jsme používali alpha_noise a beta_noise pro vypočítání odchylky od osy emitoru, která byla zadána pomocí alpha a beta. Toto bylo poněkud nevýhodné, proto jsem se rozhodl pro následující změnu. Jak jsem již řekl, pro mne je osa Y osou směřující nahoru, použijme tedy tuto osu jako provizorní osu emitoru (zapomeňme na nějakou rotaci). Pokud nyní použijeme dva úhly a,b, bude a vyjadřovat rotaci okolo osy emitoru a úhel b odchylku od této osy. V praxi to vypadá velice jednoduše :

Červená šipka je výsledný vektor vypuštění částice z emitoru,b a a jsou úhly, W je v vzdálenost paty kolmice (zpuštěné z bodu A do roviny XZ) a středu souřadného systému, Y je vzdálenost kolmice zpuštěné z bodu A k ose Y. My budeme počítat s jednotkovou kružnicí / koulí, tj. poloměr je roven 1. Body X a Y vypočítáme jako délky odvěsen v pravoúhlém trojúhelníku pomocí fcí. sin a cos.

X = sin(a);
Z = cos(a);

Nyní máme bod, který leží na kružnici v rovině XZ. Naším cílem je ale vypočítat bod A, který leží na kružnici v rovině dané osou Y a bodem [X,Z]. Určit Y bude velmi jednoduché, použijeme stejný vztah jako u předchozí kružnice, též si vypočítáme W.

Y = -sin(b + 90);
W = cos(b + 90);

Proč b + 90? B jako takové je odchylka od W, pokud chceme dostat odchylku od Y, přičteme 90. Nyní máme Y, ale ještě musíme upravit souřadnice X a Z. Jak jsem říkal na začátku, počítáme s jednotkovou kružnicí, z toho vyplývá, že vektor XZ má délku 1, pokud ho chceme upravit na délku W, není nic jednoduššího, než ho W vynásobit. Dostáváme tedy konečnou podobu výpočtu bodu A :

W = cos(b + 90);

X = sin(a) * W;
Y = -sin(b + 90);
Z = cos(a) * W;

K čemu tato změna?
Nyní můžeme generovat částice v rovině (pokud nastavíme b na 90), v prstenci (b například na 90, a beta_noise na 10), tím dostaneme prstenec o rozptylu 20°. Také můžeme generovat částice v určité vzdálenosti od emitoru (ve válci, atd.)


Pokud nastavíme beta_noise, má to samozřejmě vliv i na směr částice, která se generuje ve vzdálenosti emit_radius od emitoru (viz. obr. emit radius 2). Pokud chceme, aby byly částice generovány po celém obvodu kružnice, musíme nastavit alpha_noise na 180 (výchylka je vždy +- -> +180 a -180). Výpočet bodu vypuštění částice se počítá stejně jako v odstavci výše :

lx = sin(a) * emit_radius;
lz = cos(b) * emit_radius;
ly = 0;


A co teprve změna směru pohybu částice po vypuštění? Stačí si určit vektor požadovaného směru, kterým se má částice pohybovat a postupně při každém kroku připočítat tento vektor k původnímu vektoru pohybu a zarovnat na délku 1 (normalizovat) výsledný vektor. Tímto vektorem nahradíme původní vektor pohybu a takto to jde stále dokola, až se vektor vyrovná.


Jelikož je vektor vypuštění / vystřelení částice jednotkový a vektor požadovaného směru také, musíme vektor požadovaného směru  při sčítání zmenšit. Pokud by totiž oba měli délku 1, vektor by se upravil během prvních 10ti kroků (tj. při 60fps něco kolem 1/6s). Pokud vektor zmenšíme např. 100x, k úplnému vyrovnání dojde až za 16.6s, atd. Proto si zavedeme proměnnou differention_speed (jde pouze o číslo, kterým budeme násobit vektor pro požadovaný směr). Já jsem si ještě přidal jednu proměnnou, s názvem wanted_beta (beta úhel pro požadovaný směr). Pomocí ní vypočítám vektor pro požadovaný směr stejně jako vektor vypuštění (akorát se nahradí b za wanted_beta). Díky této úpravě velmi rychle docílíme efektu fontány, ohně, atd.

void C_PARTICLE_EMITOR::New_Particle(int num)
{
  float w,na,nb; //w, alpha a beta noise
  float nc,nl,ns; //color noise, life noise, size noise
  int i;
  int remain = num; //kolik musime jeste vytvorit castic 
  C_VECTOR fire; //vektor vypusteni / vystreleni castice
  float a,b; //uhly alpha a beta

  if(used == FALSE)  //pokud se emitor nepouziva (v poli), nebudeme nic generovat
  {
    fprintf(stderr,"Trying to access non-existing particle emitor [%p](owner is possibly dead)!\n",this);
    return;
  };

  if(moved == FALSE) Move();  // pokud se v tomot kole jeste nepohnul, tak s nim musime nejdrive pohnout (aby se upravila pozice a rotacni matice)

  for(i = 0; i < PARTICLE_NUMBER_MAX;i++) //projedeme pole castic a najdeme nejakou nepouzitou
  {
    if(engine3d_particle_array[i].used == FALSE)  //a mame ji
    {
      na = alpha_noise * Compute_Noise(1.0); //vypocitame noise (sum), Compute_Noise(1.0), vraci float v rozsahu +-1.0f
      nb = beta_noise * Compute_Noise(1.0);
      if(color_noise != 0.0f) nc = 1.0 + Compute_Noise(color_noise); else nc = 1.0f;  //color noise
      nl = (int)(life_noise * Compute_Noise(1.0));  //life noise
      ns = size_noise * Compute_Noise(1.0); //size noise

      a = na + alpha_start; // urcime alpha uhel
      b = nb + beta_start + 90; //urcime beta uhel

      if(wanted_beta != b && diferention_speed != 0.0f) // pokud je pozadovana beta (souvisi se zmenou castice za behu) jina nez beta vypocitana, urcime pozadovany vektor
      {
        w = cos(RAD(wanted_beta));
        fire.vx = (sin(RAD(a)) * w);
        fire.vz = (cos(RAD(a)) * w);
        fire.vy = -(sin(RAD(wanted_beta)));

        fire = rotation_matrix * fire;  //otocime vektor vypusteni podle rotace emitoru

        engine3d_particle_array[i].wanted_direction = fire * differention_speed; //upravime jeho delku
        engine3d_particle_array[i].direction_can_change = TRUE;  //rekneme castici, ze se jeji smer pohybu muze menit
      } else engine3d_particle_array[i].direction_can_change = FALSE; //uhly si odpovidaji, nebo nechceme aby se smer menil, tak to castici take oznamime

      w = cos(RAD(b));  //zname W, viz. vyse
      if(emit_radius == 0.0f)  //pokud chceme emitovat primo z emitoru
      {
        fire.vx = (sin(RAD(a)) * w);
        fire.vz = (cos(RAD(a)) * w);
        fire.vy = -(sin(RAD(b)));

        engine3d_particle_array[i].rel_x = 0;  //relativni pozice k emitoru je 0 = emitor
        engine3d_particle_array[i].rel_y = 0;
        engine3d_particle_array[i].rel_z = 0;
      }
      else
      {
        C_VECTOR pos;  //urcime si pozici v rovine XZ a vzdalenosti emit_radius
        fire.vx = sin(RAD(a));
        fire.vz = cos(RAD(a));
        fire.vy = -sin(RAD(b));

        pos.Set(emit_radius * fire.vx,0,emit_radius * fire.vz);
        pos = rotation_matrix * pos;

        engine3d_particle_array[i].rel_x = -pos.vx;  //ulozime relativni pozici castice
        engine3d_particle_array[i].rel_y = -pos.vy;
        engine3d_particle_array[i].rel_z = -pos.vz;

        fire.vx *= w; //upravime x a z vektoru vypusteni (viz. vyse) pro dalsi pouziti
        fire.vz *= w;
      };

      engine3d_particle_array[i].x = x; //ulozime pozici castice (rovna se pozici emitoru)
      engine3d_particle_array[i].y = y;
      engine3d_particle_array[i].z = z;

      engine3d_particle_array[i].used = TRUE; //castice se pouziva
      engine3d_particle_array[i].life_end = ticks + life + nl; //ulozime cas smrti castice
      engine3d_particle_array[i].life = life + nl; //ulozime delku zivota castice

      engine3d_particle_array[i].size_start = start_size + ns;  //velikost na zacatky
      engine3d_particle_array[i].size_decrease = size_dec; //ubytek velikosti

      engine3d_particle_array[i].start_speed = start_speed; //rychlost na zacatku
      engine3d_particle_array[i].speed_dec = speed_dec; //ubytek rychlosti

      memcpy(engine3d_particle_array[i].color_decrease,color_dec,sizeof(T_COLOR_RGBA)); //zkopirujeme ubytek barvy za ms (r,g,b,a = float[4] = T_COLOR_RGBA)

      engine3d_particle_array[i].direction = rotation_matrix * fire; //ulozime uhel vystreleni castice (otocime ho nejdrive podle rotacni matice / rotace emitoru)

      engine3d_particle_array[i].texture_ID = texture_ID; //ulozime ID textury emitoru

      engine3d_particle_array[i].owner = this; //rekneme, ze tento emitor je vlastnikem nove castice

      engine3d_particle_array[i].color_start[0] = start_color[0] * nc;  // vypocitame barvu (original * noise)
      engine3d_particle_array[i].color_start[1] = start_color[1] * nc;
      engine3d_particle_array[i].color_start[2] = start_color[2] * nc;
      engine3d_particle_array[i].color_start[3] = start_color[3] * nc;

      if(owner != NULL)  //pokud existuje vlastnik
      {
        if(lock_position_with_owner) //a chceme, aby byla pozice upravovana spolu s vlastnikem
        {
          engine3d_particle_array[i].position_locked_with_owner = TRUE; //oznamime to castici
        } else engine3d_particle_array[i].position_locked_with_owner = FALSE; //necheme upravovat pozici
      };

      num -= 1; // jedna z pozadovanych castic je hotova
    }
    if(num == 0) return; // vsechny jsou vygenerovany
  };
};


[-more-]{Závěrečné úpravy} Závěrečné úpravy
Emitování částic je hotové, zbývá nám pohyb částice. Částice se už logicky pohybuje pomocí přičítání vektoru pohybu k aktuální pozici, případně úpravou pozice podle emitoru (jsou-li spolu spojeny). Musíme si ale dát velký pozor na změnu vektoru pohybu částice.
void C_PARTICLE::Move(int ticks) // jako parametr zadavame cas od zacatku programu (dobu behu programu v ms)
{
  float speed_dif;  //upravena rychlost (interpolace rychlost start, rychlost konec)

  if(life_end < ticks || owner == NULL) // pokud uz nezije nebo vlastnik (emitor) neexistuje, zrusime ji
  {
    used = FALSE;
    return; //particle is dead
  }

  if(owner->used != TRUE)  //nebo pokud se vlastnik nepouziva (napr. byl vyrazen), castice se take vyradi. Toto muzete dat samozrejme pryc :-)
  {
    used = FALSE;
    return;
  };

  life_length = life - (life_end - ticks);  //vypocitame aktualni delku zivota

  speed_dif = (start_speed - speed_dec * life_length);  //vypocitame aktualni rychlost
  if(speed_dif < 0.0) speed_dif = 0.0f;  //pokud je rychlost zaporna, nastavime ji na 0

  if(direction_can_change) //pokud se muze menit smer
  {
    C_VECTOR new_direction; //docasny novy smer

    new_direction = direction + wanted_direction; //vektorovym souctem dostaneme novy vektor
    new_direction.Normalize();  //ten normalizujeme (delka = 1)
    direction = new_direction; //a ulozime jako novy smer
  };

  rel_x += direction.vx * speed_dif; // posuneme castici (relativni pozici)
  rel_y += direction.vy * speed_dif;
  rel_z += direction.vz * speed_dif;

  if(position_locked_with_owner) //opkud je castice spojena s emitorem, upravime jeji pozici podle emitoru
  {
    x = owner->owner->x + owner->rel_x; //particle = object + emitor
    y = owner->owner->y + owner->rel_y;
    z = owner->owner->z + owner->rel_z;
  }

//!!! pri vykreslovani totiz pocitame pozici jako X,Y,Z + rel_x, rel_y, rel_z; pokud X,Y,Z neupravime, zustane nastavena na pozici emitoru v dobe vytvoreni castice

  size = size_start - size_decrease * life_length;  //vypocitame castici

  color[0] = color_start[0] - color_decrease[0] * life_length; // take upravime barvu
  color[1] = color_start[1] - color_decrease[1] * life_length;
  color[2] = color_start[2] - color_decrease[2] * life_length;
  color[3] = color_start[3] - color_decrease[3] * life_length;
};


Doporučuji vytvořit metodu (funkci) s názvem například Move_Emitors(), kterou umístíme do objektu / třídy C_PARTICLE_SYSTEM. Bude se starat o to, aby se všechny emitory pohly a vyemitovaly částice (pokud mají). Tato metoda / funkce může vypadat například takto :

void C_PARTICLE_SYSTEM::Move_Emitors(void)
{
  int i;

  for(i = 0; i < PARTICLE_EMITOR_NUMBER_MAX; i++)  //projedeme pole casticovych emitoru
  {
    if(engine3d_particle_emitor_array[i].used == TRUE) //pokud se nejaky pouziva
    {
      engine3d_particle_emitor_array[i].Move(); //pohneme s nim
      if(engine3d_particle_emitor_array[i].emit_particles == TRUE) engine3d_particle_emitor_array[i].Emit_Particle(); //a pokud ma emitovat nejake castice, vyemitujeme je
    };
  };
};

Hodně to zjednoduší práci, hlavně v případě, že používáte emitory například k vytvoření kouře za výfukem. V editoru si nastavíte, že chcete, aby se každý krok vyemitovalo "xy" takových a makových částic, pak model nahrajete a o nic se už nestaráte. Samozřejmě nesmíte zapomenout tuto funkci zavolat, nejlépe před voláním funkce C_PARTICLE_SYSTEM::Draw().

A je hotovo
Toto jsou tedy emitory, které jsem vám chtěl presentovat. Ještě mám v zásobě pár obrázků, pokud se chcete pokochat. Dále mám rozpracovaný díl s názvem Světla jako od maminky, zabývající se, jak jinak než, vylepšením předchozího systému světel. Půjde spíše o stránku grafickou, ale zato důležitou. Těším se na vás u dalšího dílu tohoto seriálu. A ještě bych málem zapomněl, pokud budete mít někdo zájem o zdrojové kódy, ty budou zveřejněny asi tak za měsíc na stránkách sdroids3d.freehry.cz.
Tady je popisovaný ohníček(wanted_direction)
Fontána (wanted_direction)
Prstenec (beta_start, emit_radius)
Válec (emit_radius)
To si pojmenujte sami :-) (beta_start, start_size 0.6, end_size -3.0)

Obsah seriálu (více o seriálu):

Tématické zařazení:

 » Rubriky  » C/C++  

Poslat článek

Nyní máte možnost poslat odkaz článku svým přátelům:

Váš e-mail:

(Není povinný)

E-mail adresáta:

Odkaz článku:

Vzkaz:

Kontrola:

Do spodního pole opište z obrázku 5 znaků:

Kód pro ověření

 

 

 

 

Nejčtenější články
Nejlépe hodnocené články

 

Přihlášení k mému účtu

Uživatelské jméno:

Heslo: