BASIC

Author Topic: Particle Processing Routine  (Read 3832 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.