More particles

Blitz3D Forums/Blitz3D Programming/More particles

JoshK(Posted 2004) [#1]
Here's another system. It's pretty straightforward, single-surface, very fast:


Const EMITTER_BUFFERSIZE=68
Const EMITTER_ENTITY=0
Const EMITTER_VELOCITYX=4
Const EMITTER_VELOCITYY=8
Const EMITTER_VELOCITYZ=12
Const EMITTER_DVELOCITY=16
Const EMITTER_SIZE=20
Const EMITTER_DSIZE=24
Const EMITTER_INTENSITY=28
Const EMITTER_LIFE=32
Const EMITTER_PARTICLEBUFFER=36
Const EMITTER_SPREADX=40
Const EMITTER_SPREADY=44
Const EMITTER_SPREADZ=48
Const EMITTER_COLOR=52
Const EMITTER_ACCELERATIONX=56
Const EMITTER_ACCELERATIONY=60
Const EMITTER_ACCELERATIONZ=64

Global EMITTER_BUFFER=CreateBank()

Function CountEmitters()
Return BankSize(emitter_BUFFER)/4
End Function

Function GetEmitter(n)
Return PeekInt(emitter_BUFFER,(n-1)*4)
End Function

Function UpdateEmitters(speed#=1)
For n=1 To countemitters()
updateemitter getemitter(n),speed#
Next
End Function

Function FindEmitter(entity)
For n=1 To countemitters()
buffer=getemitter(n)
If entity=PeekInt(buffer,EMITTER_ENTITY) Return buffer
Next
End Function

Function EmitterSize(entity,size#,dsize#=-1)
buffer=FindEmitter(entity)
PokeFloat buffer,EMITTER_SIZE,size
If dsize<>-1 PokeFloat buffer,EMITTER_DSIZE,dsize
End Function

Function EmitterVelocity(entity,vx#,vy#,vz#,ax#=43.63,ay#=43.63,az#=43.63,dv#=-1)
buffer=FindEmitter(entity)
PokeFloat buffer,EMITTER_VELOCITYX,vx
PokeFloat buffer,EMITTER_VELOCITYY,vy
PokeFloat buffer,EMITTER_VELOCITYZ,vz
If ax<>43.63 PokeFloat buffer,EMITTER_ACCELERATIONX,ax
If ay<>43.63 PokeFloat buffer,EMITTER_ACCELERATIONY,ay
If az<>43.63 PokeFloat buffer,EMITTER_ACCELERATIONZ,az
If dv<>-1.0 PokeFloat buffer,EMITTER_DVELOCITY,dv
End Function

Function EmitterSpread(entity,x#,y#,z#)
buffer=FindEmitter(entity)
PokeFloat buffer,EMITTER_SPREADX,x
PokeFloat buffer,EMITTER_SPREADY,y
PokeFloat buffer,EMITTER_SPREADZ,z
End Function

Function CreateEmitter(intensity=10,life=100)
buffer=CreateBank(EMITTER_BUFFERSIZE)
bsize=BankSize(EMITTER_BUFFER)
ResizeBank EMITTER_BUFFER,bsize+4
PokeInt EMITTER_BUFFER,bsize,buffer
entity=CreateMesh()
EntityBlend entity,3
EntityFX entity,1+2+8+32
PokeInt buffer,EMITTER_ENTITY,entity
PokeFloat buffer,EMITTER_VELOCITYX,0
PokeFloat buffer,EMITTER_VELOCITYY,1
PokeFloat buffer,EMITTER_VELOCITYZ,0
PokeFloat buffer,EMITTER_ACCELERATIONX,0
PokeFloat buffer,EMITTER_ACCELERATIONY,0
PokeFloat buffer,EMITTER_ACCELERATIONZ,0
PokeFloat buffer,EMITTER_DVELOCITY,0.01
PokeFloat buffer,EMITTER_SIZE,10
PokeFloat buffer,EMITTER_DSIZE,0.1
PokeFloat buffer,EMITTER_LIFE,life
PokeFloat buffer,EMITTER_SPREADX,5
PokeFloat buffer,EMITTER_SPREADY,5
PokeFloat buffer,EMITTER_SPREADZ,5
PokeInt buffer,EMITTER_COLOR,RGB(255,255,255)
PokeInt buffer,EMITTER_INTENSITY,intensity
surf=CreateSurface(entity)
For n=0 To intensity-1
angle#=Rnd(360)
p=CreatePivot()
RotateEntity p,0,0,angle
TFormVector 1,1,0,p,0
AddVertex surf,0,0,0,(TFormedX()+1.0)/2.0,(TFormedY()+1.0)/2.0
TFormVector 1,-1,0,p,0
AddVertex surf,0,0,0,(TFormedX()+1.0)/2.0,(TFormedY()+1.0)/2.0
TFormVector -1,-1,0,p,0
AddVertex surf,0,0,0,(TFormedX()+1.0)/2.0,(TFormedY()+1.0)/2.0
TFormVector -1,1,0,p,0
AddVertex surf,0,0,0,(TFormedX()+1.0)/2.0,(TFormedY()+1.0)/2.0
FreeEntity p
AddTriangle surf,n*4,n*4+2,n*4+1
AddTriangle surf,n*4+2,n*4+0,n*4+3
Next
pbuffer=CreateBank(4*intensity)
PokeInt buffer,EMITTER_PARTICLEBUFFER,pbuffer
offset#=Rnd(0,1)
For n=0 To intensity-1
bank=CreateBank(36)
PokeInt pbuffer,n*4,bank
PokeFloat bank,28,(offset+Float(n))/Float(intensity)+offset
PokeFloat bank,32,Rnd(360)
Next
Return entity
End Function

Function UpdateEmitter(buffer,speed#=1)
entity=PeekInt(buffer,EMITTER_ENTITY)
pbuffer=PeekInt(buffer,EMITTER_PARTICLEBUFFER)
life#=PeekFloat(buffer,EMITTER_LIFE)
size#=PeekFloat(buffer,EMITTER_SIZE)
dsize#=PeekFloat(buffer,EMITTER_DSIZE)
velx#=PeekFloat(buffer,EMITTER_VELOCITYX)
vely#=PeekFloat(buffer,EMITTER_VELOCITYY)
velz#=PeekFloat(buffer,EMITTER_VELOCITYZ)
ax#=PeekFloat(buffer,EMITTER_ACCELERATIONX)
ay#=PeekFloat(buffer,EMITTER_ACCELERATIONY)
az#=PeekFloat(buffer,EMITTER_ACCELERATIONZ)
dv#=PeekFloat(buffer,EMITTER_DVELOCITY)
surf=GetSurface(entity,1)
spreadx#=PeekFloat(buffer,EMITTER_SPREADX)
spready#=PeekFloat(buffer,EMITTER_SPREADY)
spreadz#=PeekFloat(buffer,EMITTER_SPREADZ)
hue=PeekInt(buffer,EMITTER_COLOR)
r=Red(hue)
g=Green(hue)
b=Blue(hue)

;Calculate particle orientation
TFormVector 1,1,0,cam,entity
x0#=TFormedX()
y0#=TFormedY()
z0#=TFormedZ()
TFormVector -1,1,0,cam,entity
x1#=TFormedX()
y1#=TFormedY()
z1#=TFormedZ()
TFormVector -1,-1,0,cam,entity
x2#=TFormedX()
y2#=TFormedY()
z2#=TFormedZ()
TFormVector 1,-1,0,cam,entity
x3#=TFormedX()
y3#=TFormedY()
z3#=TFormedZ()
surf=GetSurface(entity,1)

;Update particles
For n=0 To BankSize(pbuffer)/4-1

;Get info
bank=PeekInt(pbuffer,n*4)
x#=PeekFloat(bank,0)
y#=PeekFloat(bank,4)
z#=PeekFloat(bank,8)
vx#=PeekFloat(bank,12)
vy#=PeekFloat(bank,16)
vz#=PeekFloat(bank,20)
scale#=PeekFloat(bank,24)
alpha#=PeekFloat(bank,28)
angle#=PeekFloat(bank,32)

;Process info
alpha=alpha-1.0/life*speed
scale=scale+dsize*speed
vx=vx+Rnd(-dv,dv)*speed+ax
vy=vy+Rnd(-dv,dv)*speed+ay
vz=vz+Rnd(-dv,dv)*speed+az
x=x+vx
y=y+vy
z=z+vz
If alpha<0
alpha=1
x=Rnd(-spreadx,spreadx)
y=Rnd(-spready,spready)
z=Rnd(-spreadz,spreadz)
vx=velx
vy=vely
vz=velz
scale=size
EndIf

;Store info
PokeFloat bank,0,x
PokeFloat bank,4,y
PokeFloat bank,8,z
PokeFloat bank,12,vx
PokeFloat bank,16,vy
PokeFloat bank,20,vz
PokeFloat bank,24,scale
PokeFloat bank,28,alpha

;Update mesh
VertexCoords surf,n*4,x+x0*scale,y+y0*scale,z+z0*scale
VertexCoords surf,n*4+1,x+x1*scale,y+y1*scale,z+z1*scale
VertexCoords surf,n*4+2,x+x2*scale,y+y2*scale,z+z2*scale
VertexCoords surf,n*4+3,x+x3*scale,y+y3*scale,z+z3*scale
VertexColor surf,n*4,r,g,b,alpha
VertexColor surf,n*4+1,r,g,b,alpha
VertexColor surf,n*4+2,r,g,b,alpha
VertexColor surf,n*4+3,r,g,b,alpha

Next

End Function


Trixx(Posted 2004) [#2]
Halo, do you think that using banks and additional 2 peek/poke commands for each value ( in main loop ) is faster than using types for particles ?


poopla(Posted 2004) [#3]
It doesn't matter what you use tbh, so don't worry. The speed difference is tiny.


puki(Posted 2004) [#4]
It's a different option though - I like options.


Trixx(Posted 2004) [#5]
Err... I'm a perfectionist :) Even "1 FPS more" is "big deal" for me :)


Bot Builder(Posted 2004) [#6]
Halo, do you think that using banks and additional 2 peek/poke commands for each value ( in main loop ) is faster than using types for particles ?


I'm gonna bet it is - blitz should convert it directly to an add, possibly a comparisen(at least in debug mode), and finally a direct memory write. With the type system it probably has to parse through the blitz variable system, which might be a bit slower. You should do some tests.


Rob(Posted 2004) [#7]
various particle systems I've done use banks.


JoshK(Posted 2004) [#8]
Types are crap. Banks are the only dynamic data system that can be stored in a single four-byte integer, and accessed by other applications.

I don't know, I just got tired of "For particle.particle=each particle".


Trixx(Posted 2004) [#9]
I agree, banks are great, but in this case using types to iterate through particles will be much faster. Blitz's peek/poke commands are not too fast, as one could expected.


sswift(Posted 2004) [#10]
What I would be concerned with is the amount of memory being allocated and deallocated each frame, not the speed at which the types are being accessed. The speed at which they're accessed is surely insignificant considering how few particles one can have in the world.

But as I found with my shadow system, allocating and deallocating hundreds of polygons in a type each frame was a no-no. So you should do some speed tests and calculate how much time you think is going to be spent in that part of the code for an average number of particles being created and deleted each frame.

But it seems to me that the number of particles created and deleted each frame, even with an effect using hundreds of particles, is generally going to be fairly low. Let's say you had 200 snow particles falling gently down the screen. Only a few each frame would be deleted, and only a few would be created.

Fire and smoke on the other hand has faster moving particles, but still only a few should be created and deleted each frame.

Still, it's something to consider. With a type based system one could clearly not simulate every single particle of snow in a level at once if one is creatign and deleting types. However, if one were to create a pool of types, and re-use the allocated memory when possible... That would significantly cut down on the wasted reallocation.

Of course, one should not try to do snow in that way. That was just an example.


Picklesworth(Posted 2004) [#11]
Thanks for the particle system Halo! I will try it out soon...


Bot Builder(Posted 2004) [#12]
I agree, banks are great, but in this case using types to iterate through particles will be much faster. Blitz's peek/poke commands are not too fast, as one could expected.


Type Typ
 Field Flot#
End Type

Const n=100000*4

For a=1 To 100000
 t.typ=New typ
Next

bank=CreateBank(n)

For qwert=1 To 100
m=MilliSecs()
For a=0 To n-4 Step 4
 PokeFloat bank,a,Float(a/100.0)
Next
m2=m2+(MilliSecs()-m)
Next

Print Float(m2/100.0)+" millisecs for bank write" 

a=Rand(1,100)

For qwert=1 To 100
m=MilliSecs()
For t.typ = Each typ
 t\flot#=Float(a/100.0)
Next
m2=m2+(MilliSecs()-m)
Next

Print Float(m2/100.0)+" millisecs for type write" 

For qwert=1 To 100
m=MilliSecs()
For a=0 To n-4 Step 4
 moo#=PeekFloat#(bank,a)
Next
m2=m2+(MilliSecs()-m)
Next

Print ""

Print Float(m2/100.0)+" millisecs for bank read" 

a=Rand(1,100)

For qwert=1 To 100
m=MilliSecs()
For t.typ = Each typ
 moo#=t\flot#
Next
m2=m2+(MilliSecs()-m)
Next

Print Float(m2/100.0)+" millisecs for type read"


According to this comparisent that is incorrect.


JoshK(Posted 2004) [#13]
What I would be concerned with is the amount of memory being allocated and deallocated each frame.


This doesn't allocate any new memory. The particles are kept in a cycle, and their number is constant.


RetroBooster(Posted 2004) [#14]
SSwift, your idea that creation/deletion of types is slow is based on your impropper use of them, those polygons in your culling system contain tons of blitzarrays, adding blitzarrays to a type makes them mortaly slow to delete, (approx 1000-5000 times slower then a regular type, I'm NOT kidding about this) try deleting a 1000 of your polygon types and a 1000 types without blitz arrays, without debug on and time them both. You'll be shocked! Just gave away one of the reasons why Storms Shadows are prolly a bit faster then yours, but whatever I'm in a generous mood today. ;)


AntonyWells(Posted 2004) [#15]
Or maybe perhaps storm's is based on OpenGL and has access to lovely things like stencil buffers and nvidia specific shadow extensions etc ;)

I think the best is defined by what you're doing. My gl engine for example, for each mesh, there's a linked/tri version for the benefits of object based coding(I can quickly and easily construct new meshes)
there's then an optimization layer, that compiles the mesh
into a vertex array compatible format..
now that is a bank. A bank is better suited for this.

as for this lib, my god halo, learn how to format your code ffs. ;)


RetroBooster(Posted 2004) [#16]
Hehe, no otacon, I'm still talking about the non GL storm engine. The one based off blitz3d. Oh yes and I agree on halo's code... :P (halo, is your tab button broken? ;)


sswift(Posted 2004) [#17]
"Just gave away one of the reasons why Storms Shadows are prolly a bit faster then yours, but whatever I'm in a generous mood today. ;)"

1. I optimized all those type creations/deletions out of my system weeks ago before the new version which used them ever made it's way into any of the hands of those who use it. They were only ever in when I first added the clipping code, and it was too slow that way so I had top optimize it before release.

2. What's this Storm's Shadows you're talking about?

3. While my own tests did in fact show that deleting a type with an array in it was slower than deleting a regular type, I seem to recall my test showing that both were anything but lightning fast. But I won't deny that the difference may be great enough that you need not worry about it much if you avoid the use of arrays.


RetroBooster(Posted 2004) [#18]
1. I optimized all those type creations/deletions out of my system weeks ago before the new version which used them ever made it's way into any of the hands of those who use it. They were only ever in when I first added the clipping code, and it was too slow that way so I had top optimize it before release.


Good good, tho I wouldn't have used arrays in the polygon type in the first place, take a test, you'll see putting banks into types or even adding a manualy linked list to it is infact faster when being deleted then a type with blitzArrays, it's rediculous how slow they are... (the creation time is not slow however, just the removal)

2. What's this Storm's Shadows you're talking about?

Storm Engine, the shadows are just a tiny bit of it, it's a full development solution for blitz that myself and shattered are currently working on. It offers more functionality then all the seperate blitz products for sale currently offer together and in a much more flexible package.

3. While my own tests did in fact show that deleting a type with an array in it was slower than deleting a regular type, I seem to recall my test showing that both were anything but lightning fast. But I won't deny that the difference may be great enough that you need not worry about it much if you avoid the use of arrays.


Run this, it should demonstrate my point, overhere it takes 1 millisec to free the 10000 normal types and 2480 milliseconds to free the ones with blitzArrays give it a go on your comp! :)

Type tenVariables
Field a,b,c,d,e,f,g,h,i,j
End Type

Type blitzArray
Field dummy[9]
End Type

Const testAmmount = 10000

Delay 500

time = MilliSecs()

For i = 1 To testAmmount
	V.tenVariables = New tenVariables
Next

Print "Creation of tenVariables took: " + (MilliSecs() - time)

Delay 500

time = MilliSecs()

For j = 1 To testAmmount
	B.blitzArray = New blitzArray
Next

Print "Creation of blitzArray took: " + (MilliSecs() - time)

Delay 500

time = MilliSecs()

For V.tenVariables = Each tenVariables
	Delete V
Next

Print "Deleting of tenVariables took: " + (MilliSecs() - time)

Delay 500

time = MilliSecs()

For B.blitzArray = Each blitzArray
	Delete B
Next

Print "Deleting of blitzArray took: " + (MilliSecs() - time)

WaitKey()



AntonyWells(Posted 2004) [#19]
Yeah blitz arrays are horribly slow. Did a test on a fmc level in our engine back when I used types for everything all the time, and it took around 30 seconds to free around 30,000 types.


sswift(Posted 2004) [#20]
Hm, interesting... It takes 2 milliseconds to allocate the tenvars and 7 to allocate the blitz arrays, but just 2 ms again to deallocate the tenvars, but a whopping 5883 milliseconds to deallocate the blitzarrays.

Why in the world is is so slow to DEALLOCATE memory? I could understand if it took longer to allocate it... Doing so might mean that 0's have to be written to all addresses.

But deallocating it?

And why would an array be slower than a variable? Memory is memory. Technically, with either one, one would assume Blitz calculates the total size of the type, taking into account the size of each field, and then does a single memory alloction or deallocation for all.

So even if Blitz is doing some kind of memory management to defragment memory when freeing the type, because both operations should be IDENTICAL when allocating the memory, they should both taker exactly the same amount of time.

I don't get it. I mean even if Blitz is doing some funny set up internally for the array... still... Why would it affect deallocation? And why would it increase the time so much?

This doesn't make any sense. I'm gonna have to assume Mark messed up on this one. Hopefully he'll explain it to us. Inquiring minds want to know!

Well at least I know now that allocating the types isn't gonna slow stuff down. Just deallocating them.


sswift(Posted 2004) [#21]
Oh, and I want to know how you've supposedly made this shadow system faster than mine. Prove it! Compile that FPS demo I released with your shadow system. :-)

Even if you do though, there's one litlte problem. You can;t directly compare the systems. Your lights might have a different falloff. Or if your system were slower, it might be caused by corretly rendering the shadows rather than using the trick I did which prevents them from getting larger in the distance. (and thus covering a lot more polygons and looking all blocky)

Then again, perhaps you precalculate visibility information for a level. But does that come at the cost that the user can't then modify the level?

And of course there's the question of price. What are you gonna charge?


CyBeRGoth(Posted 2004) [#22]
There seems to be a problem with

PokeInt buffer,EMITTER_COLOR,RGB(255,255,255)

The function RGB is not found.


Bot Builder(Posted 2004) [#23]
Function RGB(r,g,b)
 Return (r Shr 16)+(g Shr 8)+b
End Function


I just punched this into the browser. Migt be wrong though. I don't use shr much.


Shambler(Posted 2004) [#24]
Isn't it Shl ?


Trixx(Posted 2004) [#25]
According to this comparisent that is incorrect

BotBuilder, I said "in this case" - and your example is far from halo's code ! Here is more accurate example, try it !
bank=CreateBank(4100)
Type p ; particle
	Field x#
	Field y#
	Field z#
	Field vx#
	Field vy#
	Field vz#
	Field scale#
	Field alpha#
	Field angle#
End Type

k1#=MilliSecs()

For i=1 To 1000
For n=1 To 1000 

mybank=PeekInt(bank,n*4)
x#=PeekFloat(bank,0) 
y#=PeekFloat(bank,4) 
z#=PeekFloat(bank,8) 
vx#=PeekFloat(bank,12) 
vy#=PeekFloat(bank,16) 
vz#=PeekFloat(bank,20) 
scale#=PeekFloat(bank,24) 
alpha#=PeekFloat(bank,28) 
angle#=PeekFloat(bank,32) 

x=x+1
y=y+1
z=z+1
vx=vx+1
vy=vy+1
vz=vz+1
scale=scale+1
alpha=alpha+1
angle=angle+1

PokeFloat bank,0,x 
PokeFloat bank,4,y 
PokeFloat bank,8,z 
PokeFloat bank,12,vx 
PokeFloat bank,16,vy 
PokeFloat bank,20,vz 
PokeFloat bank,24,scale 
PokeFloat bank,28,alpha 

Next
Next

k#=MilliSecs()-k1
Text 100,100,"using bank + peek +poke commands ="+k#

k1#=MilliSecs()
For a=1 To 1000
p.p=New P
Next
For i=1 To 1000
For p.p=Each p
	p\x=p\x+1
	p\y=p\y+1
	p\z=p\z+1
	p\vx=p\vx+1
	p\vy=p\vy+1
	p\vz=p\vz+1
	p\scale=p\scale+1
	p\alpha=p\alpha+1
	p\angle=p\angle+1
Next
Next

k#=MilliSecs()-k1
Text 100,120,"using types="+k#
WaitKey() 
End



AntonyWells(Posted 2004) [#26]

Function RGB(r,g,b)

Return (r Shr 16)+(g Shr 8)+b

End Function




I just punched this into the browser. Migt be wrong though. I don't use shr much.



Don't think that will work.

Try,
function rgb(r,g,b)
    return b or (g shl 8) or (r shl 16)
end function



RetroBooster(Posted 2004) [#27]
Oh, and I want to know how you've supposedly made this shadow system faster than mine. Prove it! Compile that FPS demo I released with your shadow system. :-)

Even if you do though, there's one litlte problem. You can;t directly compare the systems. Your lights might have a different falloff. Or if your system were slower, it might be caused by corretly rendering the shadows rather than using the trick I did which prevents them from getting larger in the distance. (and thus covering a lot more polygons and looking all blocky)

From what I've seen in your shadow system demo and read overhere I'm thinking our shadow systems aren't very different from eachother in the way they work, logic dictates that if we both coded it properly, their speeeds won't differ much... Ofcourse we don't need to compete with your shadow system directly, the shadows are just icing on the cake. :)

Then again, perhaps you precalculate visibility information for a level. But does that come at the cost that the user can't then modify the level?

Flexibility is our main objective, so all of our culling/visibility systems are either dynamic or optional.

And of course there's the question of price. What are you gonna charge?

That's a question we're leaving unanswered for the moment, we'll see how people respond to the techdemos, it's likely to be more expensive then your average blitz product. (which will be fairly obvious when you see it's feature set)


Picklesworth(Posted 2004) [#28]
That's a question we're leaving unanswered for the moment, we'll see how people respond to the techdemos, it's likely to be more expensive then your average blitz product. (which will be fairly obvious when you see it's feature set)


*whispers* we'd better pretend to hate it.


poopla(Posted 2004) [#29]
I have doubts about that.


AntonyWells(Posted 2004) [#30]
I plan to sell my lib for 5 quid cheaper than yours.

Fully expect a full on price war to commence, eventually resulting in us selling our libs for 50p each.

Everyone's a winner!

;)


RetroBooster(Posted 2004) [#31]
Not likely otacon, our lib is in a completely different league, yours is more of an extended rendering pipeline with some extra features like collision support, while ours is a complete and generic game engine, which just happens to use the blitz functionality as it's rendering base, you see, we could just as easily make the Storm Engine run on your OpenGL renderer or any other renderer we make ourselves, which might just be what happens when blitzMax is released. We'll simply port our remaining code to a C++ dll and plug in our own renderer... :) Essentialy creating a game engine accessible and usable from any language, not just blitz! :)


AntonyWells(Posted 2004) [#32]
You want a war? I'll give you a war. ;) (Sorry, just any excuse to quote scarface...)

Your right though, sounds like we're going to two very differant markets, mine is just a 2d/3d engine with lots of fx.


klasix(Posted 2004) [#33]
Hello halo,

Quote: "Banks are the only dynamic data system that can be stored in a single four-byte integer, and accessed by other applications."

The "accessed by other applications" .. can you explain that for me. As in 'how to do?' Have not run across a port on the matter.

Thank you,


klasix(Posted 2004) [#34]
Make that "post" ..