effekt
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 částiceI 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é úpravyEmitová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 hotovoToto 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) |
|