Author Topic: Particle Processing Routine  (Read 3955 times)

MrTAToad

  • Guest
Particle Processing Routine
« on: 2012-May-22 »
This is the code that displays particles :

Code: (glbasic) [Select]
// --------------------------------- //
// Project: TestParticleEmitter
// Start: Saturday, May 19, 2012
// IDE Version: 11.001

TYPE tParticle
m_fAge
m_fScale
m_fRotation
m_fVelocityX
m_fVelocityY
m_fRed
m_fBlue
m_fGreen
m_fAlpha
m_fX
m_fY
ENDTYPE

TYPE TParticleEmitter
m_freeParticles[] AS tParticle
m_liveParticles[] AS tParticle

m_strName$

m_baseParticle AS tParticle

m_fPointWeight = 1.0
m_fScaleFactor = - 0.2
m_fScaleMin = 0.5
m_fScaleMax = 1.0
m_fRotationFactor = 720.0
m_fVelocityMin = 9.0
m_fVelocityMax = 100.0
m_fRedFactor = 0.0
m_fBlueFactor = 0.0
m_fGreenFactor = 0.0
m_fAlphaFactor = -1.0

m_fMinDistance
m_fMaxDistance

m_fConeAngle = 360
m_fDirection

m_particleFile$

m_iParticlesPerBurst% = 20

m_Image% = -1
m_iWidth%;m_iHeight%
m_iXHandle%; m_iYHandle%
m_iAlphaBlend% = 1

m_lastBurstTime%

FUNCTION Initialise%:fileName$,ignoreImage%
LOCAL handle%, strLine$,key$,value$,val

self.Destroy()
DIM self.m_freeParticles[self.ReturnMaxParticles()]

IF fileName$<>""
handle%=GENFILE()
IF handle%<0 THEN RETURN FALSE

IF OPENFILE(handle%,fileName$,1)
READLINE handle%,strLine$
WHILE NOT(ENDOFFILE(handle%))
IF LEN(strLine$) > 0 AND LEFT$( strLine$, 1 ) <> "'"
key$ = LEFT$(strLine$,INSTR(strLine$,"="))
value$=RIGHT$(strLine$, LEN( strLine$ ) - INSTR( strLine$, "=" )-1)
val=value$
SELECT key$
CASE "point_weight"
self.m_fPointWeight = val
CASE "point_scalefactor"
self.m_fScaleFactor = val
CASE "point_scale"
self.m_fScaleMin = val
self.m_fScaleMax = val
CASE "point_scale_min"
self.m_fScaleMin = val
CASE "point_scale_max"
self.m_fScaleMax = val
CASE "point_rotationfactor"
self.m_fRotationFactor = val
CASE "point_coneangle"
self.m_fConeAngle = val
CASE "point_mindistance"
self.m_fMinDistance = val
CASE "point_maxdistance"
self.m_fMaxDistance = val
CASE "point_velocity_min"
self.m_fVelocityMin = val
CASE "point_velocity_max"
self.m_fVelocityMax = val
CASE "point_redfactor"
self.m_fRedFactor = val
CASE "point_red"
self.m_baseParticle.m_fRed = INTEGER(val)

CASE "point_greenfactor"
self.m_fGreenFactor = val
CASE "point_green"
self.m_baseParticle.m_fGreen = INTEGER(val)
CASE "point_bluefactor"
self.m_fBlueFactor = val
CASE "point_blue"
self.m_baseParticle.m_fBlue = INTEGER(val)
CASE "point_alphafactor"
self.m_fAlphaFactor = val
CASE "point_alpha"
self.m_baseParticle.m_fAlpha = val
CASE "emitter_particles_per_burst"
self.m_iParticlesPerBurst% = INTEGER(val)
CASE "image"
IF ignoreImage%=FALSE THEN self.LoadSpr(value$)
CASE "xhandle"
self.m_iXHandle% = INTEGER(val)
CASE "yhandle"
self.m_iYHandle% = INTEGER(val)
CASE "blendmode"
self.m_iAlphaBlend% = INTEGER(val)
ENDSELECT
ENDIF

READLINE handle%,strLine$
WEND

CLOSEFILE handle%

DEBUG "Loaded\n"
RETURN TRUE
ELSE
RETURN FALSE
ENDIF
ENDIF
RETURN FALSE
ENDFUNCTION

FUNCTION Destroy%:
DIM self.m_liveParticles[0]
DIM self.m_freeParticles[0]
ENDFUNCTION

FUNCTION Finish%:
self.UnloadSprite()
self.Destroy()
ENDFUNCTION

FUNCTION UnloadSprite%:
IF self.m_Image%>=0 THEN LOADSPRITE "",self.m_Image%
self.m_Image%=-1
ENDFUNCTION

FUNCTION LoadSpr%:fileName$
self.m_Image%=GENSPRITE()
IF self.m_Image%>=0
self.m_particleFile$=fileName$
LOADSPRITE self.m_particleFile$,self.m_Image%
GETSPRITESIZE self.m_Image%,self.m_iWidth%,self.m_iHeight%

IF self.m_iWidth%=0 OR self.m_iHeight%=0 THEN self.m_Image%=-1

DEBUG "Sprite size : "+self.m_iWidth%+" "+self.m_iWidth%+"\n"
ENDIF

IF self.m_Image%>=0
RETURN TRUE
ELSE
RETURN FALSE
ENDIF
ENDFUNCTION

FUNCTION Draw%:
LOCAL p AS tParticle

IF self.m_Image%=-1 THEN RETURN FALSE

FOREACH p IN self.m_liveParticles[]
ALPHAMODE ABS(p.m_fAlpha)*SGN(self.m_iAlphaBlend%-1.0)

self.z_rotozoomanim(self.m_Image%,p.m_fX,p.m_fY,self.m_iWidth%,self.m_iHeight%,p.m_fRotation,p.m_fScale, _
RGB(p.m_fRed*255.0,p.m_fGreen*255.0,p.m_fBlue*255.0))
NEXT

RETURN TRUE
ENDFUNCTION

FUNCTION Update%:fTime
LOCAL p AS tParticle

FOREACH p IN self.m_liveParticles[]
INC p.m_fAlpha,self.m_fAlphaFactor * fTime
INC p.m_fScale,self.m_fScaleFactor * fTime
IF p.m_fAlpha <= 0.0 OR p.m_fScale <= 0.0
DIMPUSH self.m_freeParticles[],p
DELETE p
ELSE
INC p.m_fAge,fTime
INC p.m_fRotation,self.m_fRotationFactor * fTime
DEC p.m_fVelocityX,self.m_fPointWeight * fTime
DEC p.m_fVelocityY,self.m_fPointWeight * fTime
INC p.m_fX,p.m_fVelocityX * fTime
INC p.m_fY,p.m_fVelocityY * fTime
INC p.m_fY,self.m_fPointWeight * (p.m_fAge * p.m_fAge)
INC p.m_fRed,self.m_fRedFactor * fTime
INC p.m_fBlue,self.m_fBlueFactor * fTime
INC p.m_fGreen,self.m_fGreenFactor * fTime
ENDIF
NEXT
//ASSERT ( m_freeParticles.Count() + m_liveParticles.Count() = MAX_PARTICLES_PER_EMITTER ) 'ensure particles don't exist IN both lists!!
ENDFUNCTION

FUNCTION SetDirection%:a
self.m_fDirection = a
DEBUG "Direction : "+self.m_fDirection+"\n"
ENDFUNCTION

FUNCTION DoBurst%:iX%,iY%,fpsSync%=TRUE
LOCAL count%
LOCAL p AS tParticle

IF fpsSync%
LOCAL timeNow%

timeNow=INTEGER(GETTIMERALL())
IF timeNow - self.m_lastBurstTime >= 20
self.m_lastBurstTime = timeNow
ELSE
RETURN FALSE
ENDIF
ENDIF

count%=self.m_iParticlesPerBurst%
WHILE BOUNDS(self.m_freeParticles[],0)<count%
DIMPUSH self.m_freeParticles[],self.m_liveParticles[0]
DIMDEL self.m_liveParticles[],0
WEND

FOREACH p IN self.m_freeParticles[]
LOCAL angle,velocity,distance

DEC count%
IF count%<0
RETURN TRUE
ENDIF

p=self.m_baseParticle

angle= (self.RndFloat()*self.m_fConeAngle)+(self.m_fDirection - ( self.m_fConeAngle / 2.0 ))
p.m_fScale = self.m_fScaleMin + (self.RndFloat()*(self.m_fScaleMax - self.m_fScaleMin ) )
velocity = self.m_fVelocityMin + (self.RndFloat()*(self.m_fVelocityMax - self.m_fVelocityMin ))
p.m_fVelocityX = COS( angle ) * velocity
p.m_fVelocityY = SIN( angle ) * velocity
distance = self.m_fMinDistance + (RndFloat()*(self.m_fMaxDistance - self.m_fMinDistance ))
p.m_fX = iX + (COS( angle ) * distance )
p.m_fY = iY + (SIN( angle ) * distance )

DIMPUSH self.m_liveParticles[],p
DELETE p
NEXT

RETURN TRUE
ENDFUNCTION

FUNCTION LiveCount%:
RETURN BOUNDS(self.m_liveParticles[],0)
ENDFUNCTION

FUNCTION RndFloat:
RETURN RND(100)/100.0
ENDFUNCTION

FUNCTION ReturnMaxParticles%:
RETURN 600
ENDFUNCTION

FUNCTION z_rotozoomanim: num%,_x,_y,w%,h%,_angle,_scale,_col%=0xFFFFFF
LOCAL sinp, cosp, spw, sph, dx, dy
LOCAL hx, hy, dxp, dxm, dyp, dym

    sinp = SIN(_angle)
    cosp = COS(_angle)

    spw = w%-0.5
    sph = h%-0.5

    dx=spw/2
    dy=sph/2


    hx = (dx - (w%-self.m_iXHandle%))
    hy = (dy - (h%-self.m_iYHandle%))

    dxp = (dx+hx) * _scale
    dxm = (dx-hx) * _scale
    dyp = (dy+hy) * _scale
    dym = (dy-hy) * _scale

    STARTPOLY num%
    POLYVECTOR _x - cosp * dxm - sinp * dym,   _y - cosp * dym + sinp * dxm,   0.5,   0.5,    _col
    POLYVECTOR _x - cosp * dxm + sinp * dyp,   _y + cosp * dyp + sinp * dxm,   0.5,   sph,  _col
    POLYVECTOR _x + cosp * dxp + sinp * dyp,   _y + cosp * dyp - sinp * dxp,   spw, sph,  _col
    POLYVECTOR _x + cosp * dxp - sinp * dym,   _y - cosp * dym - sinp * dxp,   spw, 0.5,    _col
    ENDPOLY
    ENDFUNCTION
ENDTYPE

Offline mentalthink

  • Prof. Inline
  • *****
  • Posts: 3366
  • Integrated Brain
    • View Profile
Re: Particle Processing Routine
« Reply #1 on: 2012-May-23 »
Thanks a lot for the code... I take a look, after  :nw: :nw: :nw:

Offline Slydog

  • Prof. Inline
  • *****
  • Posts: 930
  • KodeSource
    • View Profile
    • KodeSource
Re: Particle Processing Routine
« Reply #2 on: 2012-May-23 »
I second mentalthink's response.

A particle generator was on my to-do list for a while, but it seemed like too much to get into at any time.

I can't wait to plug this into my game and see how much more 'polish' it will add!
(ha, assuming I can create a nice looking particle configuration!)

Thanks again, great job!  :good:
My current project (WIP) :: TwistedMaze <<  [Updated: 2015-11-25]

MrTAToad

  • Guest
Re: Particle Processing Routine
« Reply #3 on: 2012-May-23 »
Glad you all like it!

Offline Hemlos

  • To boldy go where no pixel has gone before!
  • Global Moderator
  • Prof. Inline
  • *******
  • Posts: 1635
  • Particle Hawk
    • View Profile
Re: Particle Processing Routine
« Reply #4 on: 2012-Jun-11 »
pretty cool, pretty code too!

You guys have seen spritez?
i spent over a year working on that particle engine.
It has blackholes, fans, no fly zones, collision detection...and so much more.
It even can spawn particles based on an bmp image.

Unfortunetly i dimmed the memory blocks, instead of using types, but if you limit the particle count it will run fast.

edit:
ps i made my avatar using spritez, 1000 particles spawning on the bitmap spawnmap function(pheonix outline), background image(pheonix), quick lived particles(fireball), and a fan in the center to push them outwards.

pps. the sytem isnt perfect, it can be sped up quite a bit(Ocean once showed me a fast tweaked version of the engine, based on common C+ programming optimizations). I am not a professional programmer, optimizing really isnt my forte.
« Last Edit: 2012-Jun-11 by Hemlos »
Volume_of_Earth(km^3) = 4/3*3.14*POW(6371.392896,3)

MrTAToad

  • Guest
Re: Particle Processing Routine
« Reply #5 on: 2012-Jun-11 »
Yes, its very good!

Could do with being put in an extended type though :)

Offline Albert

  • Dr. Type
  • ****
  • Posts: 257
    • View Profile
    • Blog
Re: Particle Processing Routine
« Reply #6 on: 2012-Jun-13 »

You guys have seen spritez?
i spent over a year working on that particle engine.
It has blackholes, fans, no fly zones, collision detection...and so much more.
It even can spawn particles based on an bmp image.


Yeah, SpriteZ is a solid stuff, I've made an Android APK from that last year: https://dl.dropbox.com/u/292449/Android/apks/Particle.apk

Offline Albert

  • Dr. Type
  • ****
  • Posts: 257
    • View Profile
    • Blog
Re: Particle Processing Routine
« Reply #7 on: 2012-Jun-13 »
MrTAToad: Ok so what type of file it could be read? I remember that you had a particle editor, but can't find that forum thread anymore. Please put a little bit more info and link into the first post. Thank you!

MrTAToad

  • Guest
Re: Particle Processing Routine
« Reply #8 on: 2012-Jun-13 »
Will do soon!

Offline Hemlos

  • To boldy go where no pixel has gone before!
  • Global Moderator
  • Prof. Inline
  • *******
  • Posts: 1635
  • Particle Hawk
    • View Profile
Re: Particle Processing Routine
« Reply #9 on: 2012-Jun-13 »
Yeah, SpriteZ is a solid stuff, I've made an Android APK from that last year: https://dl.dropbox.com/u/292449/Android/apks/Particle.apk

not sure what this does.
Im glad the engine wasnt a complete waste of time heh.

@toad: i agree, it would be easier to tweak with TYPE, to be honest with you, im not adept enough with types to be able to convert it properly(my brain hurts when i mess with them, because im so used to using multiple dimension memory.)

PS@all: im going to do my best to clean up the api, and im going to try and finish spritez-3d this year...ive been burned out from this monster...i really could use some help with converting the memory. Spritez3d will be used in a 3d flight sim which uses a quaternion camera..thats how im going to fine tune the thing. I know youve heard all this before...im only one man, trying to move a mountain.




Volume_of_Earth(km^3) = 4/3*3.14*POW(6371.392896,3)

Offline kaotiklabs

  • Dr. Type
  • ****
  • Posts: 313
  • Spain is diferent
    • View Profile
Re: Particle Processing Routine
« Reply #10 on: 2012-Jun-14 »
@Hemlos, it wasn't a waste of time. I used it frequently in my stuff!  ;)
Vote Cthulhu! Because the stars are right!!!!
Ia Ia Cthulhu F' tang!

Offline Albert

  • Dr. Type
  • ****
  • Posts: 257
    • View Profile
    • Blog
Re: Particle Processing Routine
« Reply #11 on: 2012-Jun-14 »
Uh-ho: there are some bugs in the code:
Code: (glbasic) [Select]
CASE "point_red"
self.m_baseParticle.m_fRed = INTEGER(val)
CASE "point_greenfactor"
self.m_fGreenFactor = val
CASE "point_green"
self.m_baseParticle.m_fGreen = INTEGER(val)
CASE "point_bluefactor"
self.m_fBlueFactor = val
CASE "point_blue"
self.m_baseParticle.m_fBlue = INTEGER(val)

should be:

Code: (glbasic) [Select]
CASE "point_red"
self.m_baseParticle.m_fRed = val
CASE "point_greenfactor"
self.m_fGreenFactor = val
CASE "point_green"
self.m_baseParticle.m_fGreen = val
CASE "point_bluefactor"
self.m_fBlueFactor = val
CASE "point_blue"
self.m_baseParticle.m_fBlue = val

as you using this as:

Code: (glbasic) [Select]
self.z_rotozoomanim(self.m_Image%,p.m_fX,p.m_fY,self.m_iWidth%,self.m_iHeight%,p.m_fRotation,p.m_fScale, _
RGB(p.m_fRed*255.0,p.m_fGreen*255.0,p.m_fBlue*255.0))

Also I think this bug is present in the Particle Editor too.

I don't like when one of the color component overflows (254, 255, 0, 1, 2) and suddenly changes the color of the particle, so I'm using this in Update%(fTime):
Code: (glbasic) [Select]
p.m_fRed = MIN(1.0, MAX(0.0, p.m_fRed))
p.m_fBlue = MIN(1.0, MAX(0.0, p.m_fBlue))
p.m_fGreen = MIN(1.0, MAX(0.0, p.m_fGreen))

I write out the LiveParticles count on the screen, and I'm experienced, that particles live more longer than they disappeared (by reducing alpha) So i modified the expression in:
Code: (glbasic) [Select]
IF p.m_fAlpha <= 0.00 OR p.m_fScale <= 0.0
DIMPUSH self.m_freeParticles[],p
DELETE p
ELSE
to:
Code: (glbasic) [Select]
IF p.m_fAlpha <= 0.05 OR p.m_fScale <= 0.0
This produces a more satisfying particle lifecycle, and better framerate on Android.