# Lesson21.tcl
#
# NeHe's Line Tutorial
#
# This Code Was Created By Jeff Molofee 2000
# If You've Found This Code Useful, Please Let Me Know.
# Visit My Site At nehe.gamedev.net
#
# Modified for Tcl3D by Paul Obermeier 2006/03/14
# See www.tcl3d.org for the Tcl3D extension.
package require Img ; # Img extension for loading BMP files
package require tcl3d ; # Tcl3D extension for OpenGL graphics
# Optional: Snack extension for playing WAV sound files
set retVal [catch {package require sound} soundVersion]
set gDemo(haveSnack) [expr !$retVal]
# Determine the directory of this script.
set gDemo(scriptDir) [file dirname [info script]]
# Window size.
set gDemo(winWidth) 640
set gDemo(winHeight) 480
# Display mode.
set gDemo(fullScreen) 0
# Initialize administrative arrays.
for { set loop1 0 } { $loop1 < 11 } { incr loop1 } {
for { set loop2 0 } { $loop2 < 11 } { incr loop2 } {
set vline($loop1,$loop2) 0
set hline($loop1,$loop2) 0
}
}
for { set loop1 0 } { $loop1 < 9 } { incr loop1 } {
set enemy($loop1,x) 0
set enemy($loop1,y) 0
set enemy($loop1,fx) 0
set enemy($loop1,fy) 0
set enemy($loop1,spin) 0.0
}
set hourglass(x) 0
set hourglass(y) 0
set hourglass(fx) 0
set hourglass(fy) 0
set hourglass(spin) 0.0
set player(x) 0
set player(y) 0
set player(fx) 0
set player(fy) 0
set player(spin) 0.0
set keys(LEFT) 0
set keys(RIGHT) 0
set keys(UP) 0
set keys(DOWN) 0
set keys(SPACE) 0
set gDemo(filled) 0 ; # Done Filling In The Grid?
set gDemo(gameover) 0 ; # Is The Game Over?
set gDemo(anti) 1 ; # Antialiasing?
set gDemo(active) 1 ; # Window Active Flag Set To TRUE By Default
set gDemo(delay) 0 ; # Enemy Delay
set gDemo(adjust) 3 ; # Speed Adjustment For Really Slow Video Cards
set gDemo(lives) 5 ; # Player Lives
set gDemo(level) 1 ; # Internal Game Level
set gDemo(level2) $gDemo(level) ; # Displayed Game Level
set gDemo(stage) 1 ; # Game Stage
set gDemo(steps) { 1 2 4 5 10 20 } ; # Stepping Values For Slow Video Adjustment
set gDemo(texture) [tcl3dVector GLuint 5] ; # Font Texture Storage Space
# Show errors occuring in the Togl callbacks.
proc bgerror { msg } {
tk_messageBox -icon error -type ok -message "Error: $msg\n\n$::errorInfo"
ExitProg
}
proc SetFullScreenMode { win } {
set sh [winfo screenheight $win]
set sw [winfo screenwidth $win]
wm minsize $win $sw $sh
wm maxsize $win $sw $sh
set fmtStr [format "%dx%d+0+0" $sw $sh]
wm geometry $win $fmtStr
wm overrideredirect $win 1
focus -force $win
}
proc SetWindowMode { win w h } {
set sh [winfo screenheight $win]
set sw [winfo screenwidth $win]
wm minsize $win 10 10
wm maxsize $win $sw $sh
set fmtStr [format "%dx%d+0+25" $w $h]
wm geometry $win $fmtStr
wm overrideredirect $win 0
focus -force $win
}
# Toggle between windowing and fullscreen mode.
proc ToggleWindowMode {} {
global gWidList
if { $::gDemo(fullScreen) } {
SetFullScreenMode .
set ::gDemo(fullScreen) false
set ::slowdown 2.0
} else {
SetWindowMode . $::gDemo(winWidth) $::gDemo(winHeight)
set ::gDemo(fullScreen) true
set ::slowdown 1.0
}
}
proc LoadGLTextures {} {
# Load texture images.
set imgList { "Font.bmp" "Image.bmp" }
glGenTextures [llength $imgList] $::gDemo(texture) ; # Create N Textures
set imgInd 0
foreach imgName $imgList {
set texName [file join $::gDemo(scriptDir) "Data" $imgName]
set retVal [catch {set phImg [image create photo -file $texName]} err1]
if { $retVal != 0 } {
error "Error reading image $texName ($err1)"
} else {
set w [image width $phImg]
set h [image height $phImg]
set n [tcl3dPhotoChans $phImg]
set TextureImage [tcl3dVectorFromPhoto $phImg]
image delete $phImg
}
if { $n == 3 } {
set type $::GL_RGB
} else {
set type $::GL_RGBA
}
glBindTexture GL_TEXTURE_2D [$::gDemo(texture) get $imgInd]
glTexImage2D GL_TEXTURE_2D 0 $n $w $h 0 $type GL_UNSIGNED_BYTE $TextureImage
glTexParameteri GL_TEXTURE_2D GL_TEXTURE_MIN_FILTER $::GL_LINEAR
glTexParameteri GL_TEXTURE_2D GL_TEXTURE_MAG_FILTER $::GL_LINEAR
$TextureImage delete ; # Free The Texture Image Memory
incr imgInd
}
}
proc IntRand {} {
return [expr {int (rand() * 32767.0)}]
}
proc PlaySound { wavFile blockingFlag } {
global gDemo
if { ! $gDemo(haveSnack) } {
after 500
return
}
if { $wavFile eq "" } {
# OPA TODO Kill Sound
return
}
set fullName [file join $::gDemo(scriptDir) "Data" $wavFile]
set fullName [tcl3dGetExtFile $fullName]
set snd [snack::sound -load $fullName]
$snd play -blocking $blockingFlag
}
# Initialize Our Timer (Get It Ready)
proc TimerInit {} {
set ::timer [tcl3dNewSwatch]
tcl3dResetSwatch $::timer
tcl3dStartSwatch $::timer
}
# Get Time In Milliseconds
proc TimerGetTime {} {
return [expr {1000.0 * [tcl3dLookupSwatch $::timer]}]
}
# Reset Player And Enemies
proc ResetObjects {} {
set ::player(x) 0 ; # Reset Player X Position To Far Left Of The Screen
set ::player(y) 0 ; # Reset Player Y Position To The Top Of The Screen
set ::player(fx) 0 ; # Set Fine X Position To Match
set ::player(fy) 0 ; # Set Fine Y Position To Match
set num [expr $::gDemo(stage) * $::gDemo(level)]
for { set loop1 0 } { $loop1 < $num } { incr loop1 } {
set ::enemy($loop1,x) [expr 5+[IntRand]%6] ; # Select A Random X Position
set ::enemy($loop1,y) [expr [IntRand]%11] ; # Select A Random Y Position
set ::enemy($loop1,fx) [expr $::enemy($loop1,x)*60] ; # Set Fine X To Match
set ::enemy($loop1,fy) [expr $::enemy($loop1,y)*40] ; # Set Fine Y To Match
}
}
# Build Our Font Display List
proc BuildFont {} {
set ::base [glGenLists 256] ; # Creating 256 Display Lists
glBindTexture GL_TEXTURE_2D [$::gDemo(texture) get 0] ; # Select Our Font Texture
for { set loop1 0 } { $loop1 < 256 } { incr loop1 } {
set cx [expr double($loop1%16)/16.0] ; # X Position Of Current Character
set cy [expr double($loop1/16)/16.0] ; # Y Position Of Current Character
glNewList [expr $::base+$loop1] GL_COMPILE ; # Start Building A List
glBegin GL_QUADS ; # Use A Quad For Each Character
glTexCoord2f $cx [expr 1.0-$cy-0.0625] ; # Texture Coord (Bottom Left)
glVertex2d 0 16 ; # Vertex Coord (Bottom Left)
glTexCoord2f [expr $cx+0.0625] [expr 1.0-$cy-0.0625] ; # Texture Coord (Bottom Right)
glVertex2i 16 16 ; # Vertex Coord (Bottom Right)
glTexCoord2f [expr $cx+0.0625] [expr 1.0-$cy] ; # Texture Coord (Top Right)
glVertex2i 16 0 ; # Vertex Coord (Top Right)
glTexCoord2f $cx [expr 1.0-$cy] ; # Texture Coord (Top Left)
glVertex2i 0 0 ; # Vertex Coord (Top Left)
glEnd ; # Done Building Our Quad (Character)
glTranslated 15 0 0 ; # Move To The Right Of The Character
glEndList ; # Done Building The Display List
} ; # Loop Until All 256 Are Built
}
# Delete The Font From Memory
proc KillFont {} {
glDeleteLists $::base 256 ; # Delete All 256 Display Lists
}
# Where The Printing Happens
proc glPrint { x y cset fmt args } {
set text [format $fmt $args]
if { $cset > 1 } {
# Did User Choose An Invalid Character Set?
set cset 1 ; # If So, Select Set 1 (Italic)
}
glEnable GL_TEXTURE_2D ; # Enable Texture Mapping
glLoadIdentity ; # Reset The Modelview Matrix
glTranslated $x $y 0 ; # Position The Text (0,0 - Bottom Left)
glListBase [expr {$::base+(128*$cset)}] ; # Choose The Font Set (0 or 1)
if { $cset == 0 } {
# If Set 0 Is Being Used Enlarge Font
glScalef 1.5 2.0 1.0 ; # Enlarge Font Width And Height
}
set len [string length $text]
set sa [tcl3dVectorFromString GLubyte $text]
$sa addvec -32 0 $len ; # Subtract 32 (space)
glCallLists $len GL_UNSIGNED_BYTE $sa ; # Write The Text To The Screen
$sa delete
glDisable GL_TEXTURE_2D ; # Disable Texture Mapping
}
# Resize And Initialize The GL Window
proc ReshapeCallback { toglwin { w -1 } { h -1 } } {
set w [$toglwin width]
set h [$toglwin height]
glViewport 0 0 $w $h ; # Reset The Current Viewport
glMatrixMode GL_PROJECTION ; # Select The Projection Matrix
glLoadIdentity ; # Reset The Projection Matrix
glOrtho 0.0 $w $h 0.0 -1.0 1.0 ; # Create Ortho 640x480 View (0,0 At Top Left)
glMatrixMode GL_MODELVIEW ; # Select The Modelview Matrix
glLoadIdentity ; # Reset The Modelview Matrix
set ::gDemo(winWidth) $w
set ::gDemo(winHeight) $h
}
# All Setup For OpenGL Goes Here
proc CreateCallback { toglwin } {
LoadGLTextures ; # Jump To Texture Loading Routine
BuildFont ; # Build The Font
glShadeModel GL_SMOOTH ; # Enable Smooth Shading
glClearColor 0.0 0.0 0.0 0.5 ; # Black Background
glClearDepth 1.0 ; # Depth Buffer Setup
glHint GL_LINE_SMOOTH_HINT GL_NICEST ; # Set Line Antialiasing
glEnable GL_BLEND ; # Enable Blending
glBlendFunc GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA ; # Type Of Blending To Use
}
proc HandleLogic {} {
set step [lindex $::gDemo(steps) $::gDemo(adjust)]
set num [expr {$::gDemo(stage) * $::gDemo(level)}]
if { ! $::gDemo(gameover) && $::gDemo(active) } {
# If Game Isn't Over And Programs Active Move Objects
for { set loop1 0 } { $loop1 < $num } { incr loop1 } {
if { ($::enemy($loop1,x) < $::player(x)) && \
($::enemy($loop1,fy) == [expr {$::enemy($loop1,y)*40}]) } {
incr ::enemy($loop1,x) ; # Move The Enemy Right
}
if { ($::enemy($loop1,x) > $::player(x)) && \
($::enemy($loop1,fy) == [expr {$::enemy($loop1,y)*40}]) } {
incr ::enemy($loop1,x) -1 ; # Move The Enemy Left
}
if { ($::enemy($loop1,y) < $::player(y)) && \
($::enemy($loop1,fx) == [expr {$::enemy($loop1,x)*60}]) } {
incr ::enemy($loop1,y) ; # Move The Enemy Down
}
if { ($::enemy($loop1,y) > $::player(y)) && \
($::enemy($loop1,fx) == [expr {$::enemy($loop1,x)*60}]) } {
incr ::enemy($loop1,y) -1 ; # Move The Enemy Up
}
if { ($::gDemo(delay) > [expr {3-$::gDemo(level)}]) && ($::hourglass(fx) != 2) } {
# If Our Delay Is Done And Player Doesn't Have Hourglass
set ::gDemo(delay) 0 ; # Reset The Delay Counter Back To Zero
for { set loop2 0 } { $loop2 < $num } { incr loop2 } {
if { $::enemy($loop2,fx) < [expr {$::enemy($loop2,x)*60}] } {
# Is Fine Position On X Axis Lower Than Intended Position?
# If So, Increase Fine Position On X Axis
set ::enemy($loop2,fx) [expr {$::enemy($loop2,fx) + $step}]
# Spin Enemy Clockwise
set ::enemy($loop2,spin) [expr {$::enemy($loop2,spin) + $step}]
}
if { $::enemy($loop2,fx) > [expr {$::enemy($loop2,x)*60}] } {
# Is Fine Position On X Axis Higher Than Intended Position?
# If So, Decrease Fine Position On X Axis
set ::enemy($loop2,fx) [expr {$::enemy($loop2,fx) - $step}]
# Spin Enemy Counter Clockwise
set ::enemy($loop2,spin) [expr {$::enemy($loop2,spin) - $step}]
}
if { $::enemy($loop2,fy) < [expr {$::enemy($loop2,y)*40}] } {
# Is Fine Position On Y Axis Lower Than Intended Position?
# If So, Increase Fine Position On Y Axis
set ::enemy($loop2,fy) [expr {$::enemy($loop2,fy) + $step}]
# Spin Enemy Clockwise
set ::enemy($loop2,spin) [expr {$::enemy($loop2,spin) + $step}]
}
if { $::enemy($loop2,fy) > [expr {$::enemy($loop2,y)*40}] } {
# Is Fine Position On Y Axis Higher Than Intended Position?
# If So, Decrease Fine Position On Y Axis
set ::enemy($loop2,fy) [expr {$::enemy($loop2,fy) - $step}]
# Spin Enemy Counter Clockwise
set ::enemy($loop2,spin) [expr {$::enemy($loop2,spin) - $step}]
}
}
}
# Are Any Of The Enemies On Top Of The Player?
if { ($::enemy($loop1,fx) == $::player(fx)) && \
($::enemy($loop1,fy) == $::player(fy)) } {
incr ::gDemo(lives) -1 ; # If So, Player Loses A Life
if { $::gDemo(lives) == 0 } {
# Are We Out Of Lives?
set ::gDemo(gameover) 1 ; # If So, gameover Becomes TRUE
}
ResetObjects ; # Reset Player / Enemy Positions
PlaySound "Die.wav" true ; # Play The Death Sound
}
}
if { $::keys(RIGHT) && ($::player(x) < 10) && \
($::player(fx) == [expr {$::player(x)*60}]) && ($::player(fy) == [expr {$::player(y)*40}]) } {
set ::hline($::player(x),$::player(y)) 1 ; # Mark The Current Horizontal Border As Filled
incr ::player(x) ; # Move The Player Right
}
if { $::keys(LEFT) && ($::player(x) > 0) && \
($::player(fx) == [expr {$::player(x)*60}]) && ($::player(fy) == [expr {$::player(y)*40}]) } {
incr ::player(x) -1 ; # Move The Player Left
set ::hline($::player(x),$::player(y)) 1 ; # Mark The Current Horizontal Border As Filled
}
if { $::keys(DOWN) && ($::player(y) < 10) && \
($::player(fx) == [expr {$::player(x)*60}]) && ($::player(fy) == [expr {$::player(y)*40}]) } {
set ::vline($::player(x),$::player(y)) 1 ; # Mark The Current Vertical Border As Filled
incr ::player(y) ; # Move The Player Down
}
if { $::keys(UP) && ($::player(y) > 0) && \
($::player(fx) == [expr {$::player(x)*60}]) && ($::player(fy) == [expr {$::player(y)*40}]) } {
incr ::player(y) -1 ; # Move The Player Up
set ::vline($::player(x),$::player(y)) 1 ; # Mark The Current Verticle Border As Filled
}
# Is Fine Position On X Axis Lower Than Intended Position?
if { $::player(fx) < [expr {$::player(x)*60}] } {
# If So, Increase The Fine X Position
set ::player(fx) [expr {$::player(fx) + $step}]
}
# Is Fine Position On X Axis Greater Than Intended Position?
if { $::player(fx) > [expr {$::player(x)*60}] } {
# If So, Decrease The Fine X Position
set ::player(fx) [expr {$::player(fx) - $step}]
}
# Is Fine Position On Y Axis Lower Than Intended Position?
if { $::player(fy) < [expr {$::player(y)*40}] } {
# If So, Increase The Fine Y Position
set ::player(fy) [expr {$::player(fy) + $step}]
}
# Is Fine Position On Y Axis Greater Than Intended Position?
if { $::player(fy) > [expr {$::player(y)*40}] } {
# If So, Decrease The Fine Y Position
set ::player(fy) [expr {$::player(fy) - $step}]
}
} else {
if { $::keys(SPACE) } {
# If Spacebar Is Being Pressed
set ::gDemo(gameover) 0 ; # gameover Becomes FALSE
set ::gDemo(filled) 1 ; # filled Becomes TRUE
set ::gDemo(level) 1 ; # Starting Level Is Set Back To One
set ::gDemo(level2) 1 ; # Displayed Level Is Also Set To One
set ::gDemo(stage) 0 ; # Game Stage Is Set To Zero
set ::gDemo(lives) 5 ; # Lives Is Set To Five
}
}
if { $::gDemo(filled) } {
# Is The Grid Filled In?
PlaySound "Complete.wav" true ; # If So, Play The Level Complete Sound
incr ::gDemo(stage) ; # Increase The Stage
if { $::gDemo(stage) > 3 } {
set ::gDemo(stage) 1 ; # If So, Set The Stage To One
incr ::gDemo(level) ; # Increase The Level
incr ::gDemo(level2) ; # Increase The Displayed Level
if { $::gDemo(level) > 3 } {
set ::gDemo(level) 3 ; # If So, Set The Level To 3
incr ::gDemo(lives) ; # Give The Player A Free Life
if { $::gDemo(lives) > 5 } {
# Does The Player Have More Than 5 Lives?
set ::gDemo(lives) 5 ; # If So, Set Lives To Five
}
}
}
ResetObjects ; # Reset Player / Enemy Positions
for { set loop1 0 } { $loop1 < 11 } { incr loop1 } {
# Loop Through The Grid X Coordinates
for { set loop2 0 } { $loop2 < 11 } { incr loop2 } {
# Loop Through The Grid Y Coordinates
if { $loop1 < 10 } {
set ::hline($loop1,$loop2) 0 ; # Set The Current Horizontal Value To FALSE
}
if { $loop2 < 10 } {
set ::vline($loop1,$loop2) 0 ; # Set The Current Vertical Value To FALSE
}
}
}
}
# If The Player Hits The Hourglass While It's Being Displayed On The Screen
if { ($::player(fx) == [expr {$::hourglass(x)*60}]) && \
($::player(fy) == [expr {$::hourglass(y)*40}]) && ($::hourglass(fx) == 1) } {
# Play Freeze Enemy Sound
PlaySound "Freeze.wav" false ; # OPA TODO LOOP
set ::hourglass(fx) 2 ; # Set The hourglass fx Variable To Two
set ::hourglass(fy) 0 ; # Set The hourglass fy Variable To Zero
}
# Spin The Player Clockwise
set ::player(spin) [expr {$::player(spin) + 0.5 * $step}]
if { $::player(spin) > 360.0 } {
set ::player(spin) [expr {$::player(spin) - 360}]
}
# Spin The Hourglass Counter Clockwise
set ::hourglass(spin) [expr {$::hourglass(spin) - 0.25 * $step}]
if { $::hourglass(spin) < 0.0 } {
set ::hourglass(spin) [expr {$::hourglass(spin) + 360}]
}
set ::hourglass(fy) [expr {$::hourglass(fy) + $step}]
if { ($::hourglass(fx) == 0) && ($::hourglass(fy) > [expr {6000/$::gDemo(level)}]) } {
PlaySound "Hourglass.wav" false
set ::hourglass(x) [expr [IntRand]%10+1] ; # Give The Hourglass A Random X Value
set ::hourglass(y) [expr [IntRand]%11] ; # Give The Hourglass A Random Y Value
set ::hourglass(fx) 1 ; # Set hourglass fx Variable To One (Hourglass Stage)
set ::hourglass(fy) 0 ; # Set hourglass fy Variable To Zero (Counter)
}
if { ($::hourglass(fx) == 1) && ($::hourglass(fy) > [expr {6000/$::gDemo(level)}]) } {
set ::hourglass(fx) 0 ; # If So, Set fx To Zero (Hourglass Will Vanish)
set ::hourglass(fy) 0 ; # Set fy to Zero (Counter Is Reset)
}
if { ($::hourglass(fx) == 2) && ($::hourglass(fy) > [expr {500+(500*$::gDemo(level))}]) } {
PlaySound "" false ; # If So, Kill The Freeze Sound
set ::hourglass(fx) 0 ; # Set hourglass fx Variable To Zero
set ::hourglass(fy) 0 ; # Set hourglass fy Variable To Zero
}
incr ::gDemo(delay) ; # Increase The Enemy Delay Counter
}
# Here's Where We Do All The Drawing
proc DisplayCallback { toglwin } {
set start [TimerGetTime] ; # Grab Timer Value Before We Draw
set count 0
while {[TimerGetTime] < [expr {$start + double([lindex $::gDemo(steps) $::gDemo(adjust)])*2.0}] } {
incr count ; # Waste Cycles On Fast Systems
}
# Clear Screen And Depth Buffer
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]
glBindTexture GL_TEXTURE_2D [$::gDemo(texture) get 0] ; # Select Our Font Texture
glColor3f 1.0 0.5 1.0 ; # Set Color To Purple
glPrint 207 24 0 "GRID CRAZY" ; # Write GRID CRAZY On The Screen
glColor3f 1.0 1.0 0.0 ; # Set Color To Yellow
glPrint 20 20 1 "Level:%2i" $::gDemo(level2) ; # Write Actual Level Stats
glPrint 20 40 1 "Stage:%2i" $::gDemo(stage) ; # Write Stage Stats
if { $::gDemo(gameover) } {
# Pick A Random Color
glColor3ub [expr [IntRand]%255] [expr [IntRand]%255] [expr [IntRand]%255]
glPrint 472 20 1 "GAME OVER" ; # Write GAME OVER To The Screen
glPrint 456 40 1 "PRESS S" ; # Write PRESS S To The Screen
}
for { set loop1 0 } { $loop1 < [expr {$::gDemo(lives)-1}] } { incr loop1 } {
glLoadIdentity ; # Reset The View
glTranslatef [expr {490+($loop1*40.0)}] 40.0 0.0 ; # Move To The Right Of Our Title Text
glRotatef [expr {-1.0 * $::player(spin)}] 0.0 0.0 1.0 ; # Rotate Counter Clockwise
glColor3f 0.0 1.0 0.0 ; # Set Player Color To Light Green
glBegin GL_LINES ; # Start Drawing Our Player Using Lines
glVertex2d -5 -5 ; # Top Left Of Player
glVertex2d 5 5 ; # Bottom Right Of Player
glVertex2d 5 -5 ; # Top Right Of Player
glVertex2d -5 5 ; # Bottom Left Of Player
glEnd ; # Done Drawing The Player
glRotatef [expr {-1.0 * $::player(spin)*0.5}] 0.0 0.0 1.0 ; # Rotate Counter Clockwise
glColor3f 0.0 0.75 0.0 ; # Set Player Color To Dark Green
glBegin GL_LINES ; # Start Drawing Our Player Using Lines
glVertex2d -7 0 ; # Left Center Of Player
glVertex2d 7 0 ; # Right Center Of Player
glVertex2d 0 -7 ; # Top Center Of Player
glVertex2d 0 7 ; # Bottom Center Of Player
glEnd ; # Done Drawing The Player
}
set ::gDemo(filled) 1 ; # Set Filled To True Before Testing
glLineWidth 2.0 ; # Set Line Width For Cells To 2.0
glDisable GL_LINE_SMOOTH ; # Disable Antialiasing
glLoadIdentity ; # Reset The Current Modelview Matrix
for { set loop1 0 } { $loop1 < 11 } { incr loop1 } {
for { set loop2 0 } { $loop2 < 11 } { incr loop2 } {
set l1 [expr {$loop1*60}]
set l2 [expr {$loop2*40}]
# Loop From Top To Bottom
if { $::hline($loop1,$loop2) } {
# Has The Horizontal Line Been Traced
glColor3f 1.0 1.0 1.0 ; # If So, Set Line Color To White
} else {
glColor3f 0.0 0.5 1.0 ; # Set Line Color To Blue
}
if { $loop1 < 10 } {
# Dont Draw To Far Right
if { ! $::hline($loop1,$loop2) } {
# If A Horizontal Line Isn't Filled
set ::gDemo(filled) 0 ; # filled Becomes False
}
glBegin GL_LINES ; # Start Drawing Horizontal Cell Borders
glVertex2d [expr {20+$l1}] [expr {70+$l2}] ; # Left Side Of Horizontal Line
glVertex2d [expr {80+$l1}] [expr {70+$l2}] ; # Right Side Of Horizontal Line
glEnd ; # Done Drawing Horizontal Cell Borders
}
if { $::vline($loop1,$loop2) } {
# Has The Horizontal Line Been Traced
glColor3f 1.0 1.0 1.0 ; # If So, Set Line Color To White
} else {
glColor3f 0.0 0.5 1.0 ; # Set Line Color To Blue
}
if { $loop2 < 10 } {
# Dont Draw To Far Down
if { ! $::vline($loop1,$loop2) } {
# If A Verticle Line Isn't Filled
set ::gDemo(filled) 0 ; # filled Becomes False
}
glBegin GL_LINES ; # Start Drawing Verticle Cell Borders
glVertex2d [expr {20+$l1}] [expr { 70+$l2}] ; # Left Side Of Horizontal Line
glVertex2d [expr {20+$l1}] [expr {110+$l2}] ; # Right Side Of Horizontal Line
glEnd ; # Done Drawing Verticle Cell Borders
}
glEnable GL_TEXTURE_2D ; # Enable Texture Mapping
glColor3f 1.0 1.0 1.0 ; # Bright White Color
glBindTexture GL_TEXTURE_2D [$::gDemo(texture) get 1] ; # Select The Tile Image
if { ($loop1<10) && ($loop2<10) } {
# If In Bounds, Fill In Traced Boxes
# Are All Sides Of The Box Traced?
if { $::hline($loop1,$loop2) && $::hline($loop1,[expr {$loop2+1}]) && \
$::vline($loop1,$loop2) && $::vline([expr {$loop1+1}],$loop2) } {
set l1_10 [expr {double($loop1/10.0)}]
set l2_10 [expr {double($loop2/10.0)}]
glBegin GL_QUADS ; # Draw A Textured Quad
glTexCoord2f [expr {$l1_10+0.1}] [expr {1.0-$l2_10}]
glVertex2d [expr {20+$l1+59}] [expr {70+$l2+1}] ; # Top Right
glTexCoord2f $l1_10 [expr {1.0-$l2_10}]
glVertex2d [expr {20+$l1+1}] [expr {70+$l2+1}] ; # Top Left
glTexCoord2f $l1_10 [expr {1.0-($l2_10+0.1)}]
glVertex2d [expr {20+$l1+1}] [expr {70+$l2+39}] ; # Bottom Left
glTexCoord2f [expr {$l1_10+0.1}] [expr {1.0-($l2_10+0.1)}]
glVertex2d [expr {20+$l1+59}] [expr {70+$l2+39}] ; # Bottom Right
glEnd ; # Done Texturing The Box
}
}
glDisable GL_TEXTURE_2D ; # Disable Texture Mapping
}
}
glLineWidth 1.0 ; # Set The Line Width To 1.0
if { $::gDemo(anti) } {
# Is Anti TRUE?
glEnable GL_LINE_SMOOTH ; # If So, Enable Antialiasing
}
if { $::hourglass(fx) == 1 } {
# If fx=1 Draw The Hourglass
glLoadIdentity ; # Reset The Modelview Matrix
# Move To The Fine Hourglass Position
glTranslatef [expr {20.0+($::hourglass(x)*60)}] [expr {70.0+($::hourglass(y)*40)}] 0.0
glRotatef $::hourglass(spin) 0.0 0.0 1.0 ; # Rotate Clockwise
glColor3ub [expr [IntRand]%255] [expr [IntRand]%255] [expr [IntRand]%255]
glBegin GL_LINES ; # Start Drawing Our Hourglass Using Lines
glVertex2d -5 -5 ; # Top Left Of Hourglass
glVertex2d 5 5 ; # Bottom Right Of Hourglass
glVertex2d 5 -5 ; # Top Right Of Hourglass
glVertex2d -5 5 ; # Bottom Left Of Hourglass
glVertex2d -5 5 ; # Bottom Left Of Hourglass
glVertex2d 5 5 ; # Bottom Right Of Hourglass
glVertex2d -5 -5 ; # Top Left Of Hourglass
glVertex2d 5 -5 ; # Top Right Of Hourglass
glEnd ; # Done Drawing The Hourglass
}
glLoadIdentity ; # Reset The Modelview Matrix
# Move To The Fine Player Position
glTranslatef [expr {$::player(fx)+20.0}] [expr {$::player(fy)+70.0}] 0.0
glRotatef $::player(spin) 0.0 0.0 1.0 ; # Rotate Clockwise
glColor3f 0.0 1.0 0.0 ; # Set Player Color To Light Green
glBegin GL_LINES ; # Start Drawing Our Player Using Lines
glVertex2d -5 -5 ; # Top Left Of Player
glVertex2d 5 5 ; # Bottom Right Of Player
glVertex2d 5 -5 ; # Top Right Of Player
glVertex2d -5 5 ; # Bottom Left Of Player
glEnd ; # Done Drawing The Player
glRotatef [expr {$::player(spin)*0.5}] 0.0 0.0 1.0 ; # Rotate Clockwise
glColor3f 0.0 0.75 0.0 ; # Set Player Color To Dark Green
glBegin GL_LINES ; # Start Drawing Our Player Using Lines
glVertex2d -7 0 ; # Left Center Of Player
glVertex2d 7 0 ; # Right Center Of Player
glVertex2d 0 -7 ; # Top Center Of Player
glVertex2d 0 7 ; # Bottom Center Of Player
glEnd ; # Done Drawing The Player
set num [expr {$::gDemo(stage) * $::gDemo(level)}]
for { set loop1 0 } { $loop1 < $num } { incr loop1 } {
glLoadIdentity ; # Reset The Modelview Matrix
glTranslatef [expr {$::enemy($loop1,fx)+20.0}] [expr {$::enemy($loop1,fy)+70.0}] 0.0
glColor3f 1.0 0.5 0.5 ; # Make Enemy Body Pink
glBegin GL_LINES ; # Start Drawing Enemy
glVertex2d 0 -7 ; # Top Point Of Body
glVertex2d -7 0 ; # Left Point Of Body
glVertex2d -7 0 ; # Left Point Of Body
glVertex2d 0 7 ; # Bottom Point Of Body
glVertex2d 0 7 ; # Bottom Point Of Body
glVertex2d 7 0 ; # Right Point Of Body
glVertex2d 7 0 ; # Right Point Of Body
glVertex2d 0 -7 ; # Top Point Of Body
glEnd ; # Done Drawing Enemy Body
glRotatef $::enemy($loop1,spin) 0.0 0.0 1.0 ; # Rotate The Enemy Blade
glColor3f 1.0 0.0 0.0 ; # Make Enemy Blade Red
glBegin GL_LINES ; # Start Drawing Enemy Blade
glVertex2d -7 -7 ; # Top Left Of Enemy
glVertex2d 7 7 ; # Bottom Right Of Enemy
glVertex2d -7 7 ; # Bottom Left Of Enemy
glVertex2d 7 -7 ; # Top Right Of Enemy
glEnd ; # Done Drawing Enemy Blade
}
$toglwin swapbuffers
HandleLogic
}
proc Cleanup {} {
unset ::vline
unset ::hline
unset ::enemy
unset ::hourglass
unset ::player
unset ::keys
}
# Put all exit related code here.
proc ExitProg {} {
KillFont
exit
}
proc ToggleAnti {} {
set ::gDemo(anti) [expr 1 - $::gDemo(anti)]
}
proc SetKeys { type onOff } {
set ::keys($type) $onOff
}
proc Animate {} {
.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
}
}
# Create the OpenGL window and some Tk helper widgets.
proc CreateWindow {} {
frame .fr
pack .fr -expand 1 -fill both
# Create Our OpenGL Window
togl .fr.toglwin -width $::gDemo(winWidth) -height $::gDemo(winHeight) \
-swapinterval 1 \
-double true -depth true \
-createcommand CreateCallback \
-reshapecommand ReshapeCallback \
-displaycommand DisplayCallback
grid .fr.toglwin -row 0 -column 0 -sticky news
grid rowconfigure .fr 0 -weight 1
grid columnconfigure .fr 0 -weight 1
wm title . "Tcl3D demo: NeHe's Line Tutorial (Lesson 21)"
# Watch For ESC Key And Quit Messages
wm protocol . WM_DELETE_WINDOW "ExitProg"
bind . <Key-Escape> "ExitProg"
bind . <Key-F1> "ToggleWindowMode"
bind . <KeyPress-Left> "SetKeys LEFT 1"
bind . <KeyRelease-Left> "SetKeys LEFT 0"
bind . <KeyPress-Right> "SetKeys RIGHT 1"
bind . <KeyRelease-Right> "SetKeys RIGHT 0"
bind . <KeyPress-Up> "SetKeys UP 1"
bind . <KeyRelease-Up> "SetKeys UP 0"
bind . <KeyPress-Down> "SetKeys DOWN 1"
bind . <KeyRelease-Down> "SetKeys DOWN 0"
bind . <KeyPress-s> "SetKeys SPACE 1"
bind . <KeyRelease-s> "SetKeys SPACE 0"
bind . <KeyPress-a> "ToggleAnti"
bind .fr.toglwin <1> "StartAnimation"
bind .fr.toglwin <2> "StopAnimation"
bind .fr.toglwin <3> "StopAnimation"
bind .fr.toglwin <Control-Button-1> "StopAnimation"
}
CreateWindow
ResetObjects
TimerInit
if { [file tail [info script]] eq [file tail $::argv0] } {
# If started directly from tclsh or wish, then start animation.
update
StartAnimation
}
|