[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [pygame] particles



I just worked up a somewhat less involved demo for OpenGLContext 2.0. Basically a little "fountain" that spews particles up into the air and lets them fall back down to "earth". BTW, don't think you meant tutorial 25, as that's about loading geometry from disk AFAICS.

Explanation of the included code:
Basically I keep 3 arrays, position (point in the code), velocities, and colours (first and last are actually stored in scenegraph nodes, for convenience in rendering). For each frame, I calculate the delta time fraction, and update velocities -> positions, and accellerations -> velocities (there's also a "color velocity" to make the particles look a little prettier). When a particle drops below y==0, then it's considered dead, and is moved to the emitter. 1/2 of the dead particles are reborn on each frame.

That's not proper simulation (it's neither particularly accurate nor reproducible), but it looks fine, and I hope is pretty easy to follow. Performance is not really stellar, about 9fps with 10,000 particles, 100fps with 1,000 on an Athlon 1GHz with a Radeon 7500. Any mouse interactions make the rate stutter as OpenGLContext renders the select-buffer-based pass, oh well, it's a demo/learning environment, not a production renderer :) .

Anyway, hope someone finds it useful. Enjoy yourselves,
Mike


"""Particle testing code
"""
from OpenGLContext import testingcontext
BaseContext, MainFunction = testingcontext.getInteractive()
from OpenGL.GL import *
from Numeric import *
from OpenGLContext.events.timer import Timer
import random

# need different imports for OpenGLContext 1.0
from OpenGLContext.scenegraph.basenodes import *

gravity = -9.8 #m/(s**2)
emitter = (0,0,0)
count = 1000
initialColor = (1,.9,.9)
pointSize = 3.0
colorVelocities = [-1,-.9,-.7]

class TestContext( BaseContext ):
"""Particle testing code context object
"""
initialPosition = (0,7,20)
lastFraction = 0.0
def OnInit( self ):
"""Do all of our setup functions..."""
self.points = PointSet(
coord = Coordinate(
point = [emitter]*count
),
color = Color(
color = [initialColor]*count
)
)
self.shape = Shape(
geometry = self.points,
)
self.velocities = array([ (0,0,0)]*count, 'd')
self.colorVelocities = array( colorVelocities, 'd')
self.addEventHandler( "keypress", name="s", function = self.OnSlower)
self.addEventHandler( "keypress", name="f", function = self.OnFaster)
self.time = Timer( duration = 1.0, repeating = 1 )
self.time.addEventHandler( "fraction", self.OnTimerFraction )
self.time.register (self)
self.time.start ()
glPointSize( pointSize )

### Timer callback
def OnTimerFraction( self, event ):
# item1, update all object's position with their last
# velocity, this isn't quite accurate, but should approximate
# nicely...
f = event.fraction()
if f < self.lastFraction:
f += 1.0
deltaFraction = (f-self.lastFraction)
self.lastFraction = event.fraction()
deltaV = self.velocities*deltaFraction
self.points.coord.point += deltaV
# item2, cycle colours from white, through blue, then yellow, then red
self.points.color.color += (self.colorVelocities*deltaFraction)
# item3, apply acceleration to all velocities...
self.velocities[:,1] += (gravity * deltaFraction)

# item3, start dead values again
# move all of the dead to (0,0,0), set colours to (1,1,1)
# for 1/4 of them, give them a new, random velocity
dead = nonzero(less_equal( self.points.coord.point[:,1], 0.0))
if len(dead):
print '%s dead'%len(dead)
def put( a, ind, b ):
for i in ind:
a[i] = b
put( self.points.coord.point, dead, emitter)
r = random.random
dead = dead[:len(dead)/2]
if len(dead):
print '%s spawning'%len(dead)
put( self.points.color.color, dead, initialColor)
velocities = [ ((r() - .5)*2, r()*20,(r()-.5)*2) for x in dead]
def copy( a, ind, b ):
for x in xrange(len(ind)):
i = ind[x]
a[i] = b[x]
copy( self.velocities, dead, velocities)

### Keyboard callbacks
def OnSlower( self, event ):
self.time.internal.multiplier = self.time.internal.multiplier /2.0
print "slower",self.time.internal.multiplier
def OnFaster( self, event ):
self.time.internal.multiplier = self.time.internal.multiplier * 2.0
print "faster",self.time.internal.multiplier
def Render( self, mode = 0):
"""Render scene geometry"""
BaseContext.Render( self, mode )
mode.visit( self.shape )

def OnIdle( self, ):
"""Request refresh of the context whenever idle"""
self.triggerRedraw(1)
return 1

if __name__ == "__main__":
MainFunction ( TestContext)


Urmas Eero wrote:

and this time with file attached :D

sammy


Urmas Eero wrote:

Hi,

the file attached is a pygame/pyopengl port of Nehes tutorial number 25 ( well, more like a quick hack ) from:

http://nehe.gamedev.net/tutorials/lesson.asp?l=25

the code is REALY not nice and has no comments but hey, nobody's perfect :)

just unpack, run ./compile.py ; ./main.py

a little about those files in the pack:

src.py - main script
lib/libparticles.py - particles library

compile.py - is a sort of compiler for python, that includes some librarys to main script so that we don't have to import too much :)
manual.py - 'compiler' greps all the functions from main(compiled script) and puts them to one file
main.py - the script you should run after compiling this thing

about speed:

with psyco about 400 fps
without psyco about .. well 20 fps :(



sammy



____________________________________
pygame mailing list
pygame-users@seul.org
http://pygame.seul.org

_______________________________________
 Mike C. Fletcher
 Designer, VR Plumber, Coder
 http://members.rogers.com/mcfletch/



____________________________________
pygame mailing list
pygame-users@seul.org
http://pygame.seul.org