OUTPUT BUFFER:
# cgParticles.tcl # # Particle Effects using CG and OpenGL # # Original files from: http://www.shadertech.com/shaders/ParticleSystem-src.zip # # Original files are Copyright (c) 20002 Arkadiusz Waliszewski # This software is provided 'as-is', without any express or implied warranty. # In no event will the authors be held liable for any damages arising from the # use of this software. # # Permission is granted to anyone to use this software for any purpose, # including commercial applications, and to alter it and redistribute it # freely, subject to the following restrictions: # # 1. The origin of this software must not be misrepresented; # you must not claim that you wrote the original software. # If you use this software in a product, an acknowledgment # in the product documentation would be appreciated but is not required. # # 2. Altered source versions must be plainly marked as such, # and must not be misrepresented as being the original software. # # 3. This notice may not be removed or altered from any source distribution. # # Modified for Tcl3D by Paul Obermeier 2005/11/07 # See www.tcl3d.org for the Tcl3D extension. package require Tk package require Img package require tcl3d 0.5.0 if { [info procs tcl3dHaveCg] ne "" } { if { ![tcl3dHaveCg] } { tk_messageBox -icon error -type ok -title "Error" \ -message "You do not have Cg installed." exit } } # Particle source point set SOURCE_X 0 set SOURCE_Y 0 set SOURCE_Z 0 # Limits on starting velocity set VX_MAX 3 set VY_MAX 10 set VZ_MAX 3 # Acceleration components set WIND_X 0 set GRAVITY -5 set WIND_Z 0 set Y_FLOOR 0 set NUM_PARTICLES 100 set PARTICLE_SIZE 2 # Each particle will survive for PARTICLE_MAX_AGE seconds set PARTICLE_MAX_AGE 10 # No of particles/sec to keep a steady flow set PARTICLES_PER_SEC [expr $NUM_PARTICLES/$PARTICLE_MAX_AGE] set start_time 0 # Determine the directory of this script. set g_scriptDir [file dirname [info script]] # Show errors occuring in the Togl callbacks. proc bgerror { msg } { tk_messageBox -icon error -type ok -message "Error: $msg\n\n$::errorInfo" exit } # Print info message into widget a the bottom of the window. proc PrintInfo { msg } { if { [winfo exists .fr.info] } { .fr.info configure -text $msg } } proc GetBestSquare { w h } { if { $w > $h } { set val $w } else { set val $h } set sqrList { 1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 } foreach sqr $sqrList { if { $val <= $sqr } { return $sqr } } } proc ReadImg { imgName } { global gPo if { [info exists ::g_texture] } { $::g_texture delete } set fullName [file join $::g_scriptDir $imgName] set retVal [catch {set phImg [image create photo -file $fullName]} err1] if { $retVal != 0 } { error "Failure reading image $fullName" } else { set w [image width $phImg] set h [image height $phImg] set sqr [GetBestSquare $w $h] set sqrPhoto [image create photo -width $sqr -height $sqr] $sqrPhoto copy $phImg -from 0 0 $w $h -to 0 [expr $sqr -$h] set nChans [tcl3dPhotoChans $sqrPhoto] set ::g_texture [tcl3dVectorFromPhoto $sqrPhoto] image delete $phImg image delete $sqrPhoto glPixelStorei GL_UNPACK_ALIGNMENT 1 set ::g_textureId [tcl3dVector GLuint 1] glGenTextures 1 $::g_textureId glBindTexture GL_TEXTURE_2D [$::g_textureId get 0] glTexParameteri GL_TEXTURE_2D GL_TEXTURE_MAG_FILTER $::GL_LINEAR glTexParameteri GL_TEXTURE_2D GL_TEXTURE_MIN_FILTER $::GL_LINEAR glTexImage2D GL_TEXTURE_2D 0 4 \ $sqr $sqr \ 0 GL_RGBA GL_UNSIGNED_BYTE $::g_texture } } proc ReshapeCallback { toglwin { w -1 } { h -1 } } { set w [$toglwin width] set h [$toglwin height] glViewport 0 0 $w $h glMatrixMode GL_PROJECTION glLoadIdentity # Calculate the aspect ratio of the window */ gluPerspective 60.0 [expr double($w)/double($h)] 1.0 100.0 glMatrixMode GL_MODELVIEW glLoadIdentity # View from a nice position gluLookAt 0 5 35 0 0 0 0 1 0 } # Initialize CG and load the vertex program proc InitCgShader { filename } { tcl3dCgResetError set ::profile [tcl3dCgFindProfile $::CG_PROFILE_ARBVP1 $::CG_PROFILE_VP20] if { $::profile eq "" } { error "Could not find Cg profile ARBVP1 or VP20" } # Create a CG context set ::g_context [cgCreateContext] # Add the Vertex shader to the context set cgFile [tcl3dGetExtFile [file join $::g_scriptDir $filename]] set ::g_Program [cgCreateProgramFromFile $::g_context \ CG_SOURCE $cgFile \ $::profile "main" "NULL"] set retVal [tcl3dCgGetError $::g_context] if { $retVal ne "" } { error "cgCreateProgramFromFile: $retVal" } tcl3dCgPrintProgramInfo $::g_Program $filename # Load the program cgGLLoadProgram $::g_Program set retVal [tcl3dCgGetError $::g_context] if { $retVal ne "" } { error "cgGLLoadProgram: $retVal" } # Bind the uniform parameters set ::ModelViewProjBind [cgGetNamedParameter $::g_Program "ModelViewProj"] set ::AccelerationBind [cgGetNamedParameter $::g_Program "Acceleration"] set ::CurrentTimeBind [cgGetNamedParameter $::g_Program "CurrentTime"] # Bind the varying parameters set ::VelocityBind [cgGetNamedParameter $::g_Program "IN.velocity"] } proc GenerateParticle { ptcInd xo yo zo birth } { global g_particles # Set the origin of the particles set g_particles($ptcInd,x) $xo set g_particles($ptcInd,y) $yo set g_particles($ptcInd,z) $zo # Generate a random initial velocity */ set g_particles($ptcInd,vx) [expr rand() * 2 * $::VX_MAX - $::VX_MAX] set g_particles($ptcInd,vy) $::VY_MAX set g_particles($ptcInd,vz) [expr rand() * 2 * $::VZ_MAX - $::VZ_MAX] # Set birth time set g_particles($ptcInd,birth) $birth # Generate a random color set g_particles($ptcInd,r) [expr rand()] set g_particles($ptcInd,g) [expr rand()] set g_particles($ptcInd,b) [expr rand()] } proc InitParticles {} { for { set i 0 } { $i < $::NUM_PARTICLES } { incr i } { # Ensure constant stream of particles GenerateParticle $i $::SOURCE_X $::SOURCE_Y $::SOURCE_Z \ [expr double($i) / $::PARTICLES_PER_SEC] } } proc Animate {} { set ::rotAngle [expr $::rotAngle + 1.0] if { $::rotAngle > 360.0 } { set ::rotAngle [expr $::rotAngle - 360.0] } .fr.toglwin postredisplay set ::animateId [tcl3dAfterIdle Animate] } proc StartAnimation {} { if { ! [info exists ::animateId] } { Animate } } proc StopAnimation {} { if { [info exists ::animateId] } { after cancel $::animateId unset ::animateId } } # Initialize GL parameters */ proc CreateCallback { toglwin } { ReadImg "particle.bmp" glClearColor 0.0 0.0 0.0 1.0 glEnable GL_BLEND glBlendFunc GL_SRC_ALPHA GL_ONE glDisable GL_DEPTH_TEST glShadeModel GL_SMOOTH glHint GL_PERSPECTIVE_CORRECTION_HINT GL_NICEST InitCgShader "particle.cg" # Initialize particles InitParticles } proc DisplayCallback { toglwin } { global g_particles # Clear the frame and depth buffers glClear [expr $::GL_COLOR_BUFFER_BIT | $::GL_DEPTH_BUFFER_BIT] # Viewport command is not really needed, but has been inserted for # Mac OSX. Presentation framework (Tk) does not send a reshape event, # when switching from one demo to another. glViewport 0 0 [$toglwin width] [$toglwin height] # Save the View transformations glPushMatrix # Set the required transformation # Rotate a little bit every frame glRotatef $::rotAngle 0 1 0 # Bind various parameters cgGLBindProgram $::g_Program set retVal [tcl3dCgGetError $::g_context] if { $retVal ne "" } { error "cgGLBindProgram: $retVal" } # Bind Transformation matrix */ cgGLSetStateMatrixParameter $::ModelViewProjBind \ CG_GL_MODELVIEW_PROJECTION_MATRIX \ CG_GL_MATRIX_IDENTITY # Bind acceleration and floor height cgGLSetParameter4f $::AccelerationBind \ $::WIND_X $::GRAVITY $::WIND_Z $::Y_FLOOR # Bind current time if { $::start_time == 0 } { set ::start_time [clock clicks -milliseconds] } set t [expr ([clock clicks -milliseconds] - $::start_time)/1000.0] cgGLSetParameter4f $::CurrentTimeBind \ $t 0.0 0.0 0.0 glDisable GL_TEXTURE_2D # Draw the reference floor (Without the Vertex shader) glBegin GL_QUADS # Main large floor - Some large dimensions glColor3f 0.0 0.0 0.2 glVertex3f 1000 0 1000 glVertex3f 1000 0 -1000 glVertex3f -1000 0 -1000 glVertex3f -1000 0 1000 # A brighter reference patch - Makes the rotation perceivable glColor3f 0.0 0.0 0.3 glVertex3f 10 0 10 glVertex3f 10 0 -10 glVertex3f -10 0 -10 glVertex3f -10 0 10 glEnd # Enable our CG program cgGLEnableProfile $::profile # Enable Textures glEnable GL_TEXTURE_2D # Draw the particles glBegin GL_QUADS for { set i 0 } { $i < $::NUM_PARTICLES } { incr i } { # Do not even consider particles that aren't born # Creates an ugly blob at the source if { $t < $g_particles($i,birth) } { break } # Set the color for the particle glColor4f $g_particles($i,r) \ $g_particles($i,g) \ $g_particles($i,b) \ $::PARTICLE_SIZE # Set the velocity for this point cgGLSetParameter4f $::VelocityBind \ $g_particles($i,vx) \ $g_particles($i,vy) \ $g_particles($i,vz) \ $::PARTICLE_MAX_AGE # Repeat 4 times with tex co-ordinates for billboarding glTexCoord4f 1 1 0 1 glVertex4f $g_particles($i,x) \ $g_particles($i,y) \ $g_particles($i,z) \ $g_particles($i,birth) glTexCoord4f 0 1 0 1 glVertex4f $g_particles($i,x) \ $g_particles($i,y) \ $g_particles($i,z) \ $g_particles($i,birth) glTexCoord4f 0 0 0 1 glVertex4f $g_particles($i,x) \ $g_particles($i,y) \ $g_particles($i,z) \ $g_particles($i,birth) glTexCoord4f 1 0 0 1 glVertex4f $g_particles($i,x) \ $g_particles($i,y) \ $g_particles($i,z) \ $g_particles($i,birth) } # This cleans up any particles that's dead and regenerates them glEnd # Disable CG program while going out cgGLDisableProfile $::profile # Restore modelview transformation matrix glPopMatrix $toglwin swapbuffers } proc Cleanup {} { if { [info exists ::g_texture] } { $::g_texture delete } if { [info exists ::g_textureId] } { glDeleteTextures 1 [$::g_textureId get 0] $::g_textureId delete } cgDestroyContext $::g_context foreach var [info globals g_*] { uplevel #0 unset $var } } # Put all exit related code here. proc ExitProg {} { exit } # Create the OpenGL window and some Tk helper widgets. proc CreateWindow {} { frame .fr pack .fr -expand 1 -fill both togl .fr.toglwin -width 640 -height 480 \ -double true \ -swapinterval 1 \ -createproc CreateCallback \ -reshapeproc ReshapeCallback \ -displayproc DisplayCallback label .fr.info grid .fr.toglwin -row 0 -column 0 -sticky news grid .fr.info -row 1 -column 0 -sticky news grid rowconfigure .fr 0 -weight 1 grid columnconfigure .fr 0 -weight 1 wm title . "Tcl3D demo: Particles with Cg" bind .fr.toglwin <1> "StartAnimation" bind .fr.toglwin <2> "StopAnimation" bind .fr.toglwin <3> "StopAnimation" bind .fr.toglwin