# Lesson10.tcl
#
# Lionel Brits & NeHe's 3D World Tutorial
#
# This Code Was Created By Lionel Brits & Jeff Molofee 2000
# A HUGE Thanks To Fredric Echols For Cleaning Up
# And Optimizing This Code, Making It More Flexible!
# 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/01/25
# See www.tcl3d.org for the Tcl3D extension.
package require Img
package require tcl3d
# Font to be used in the Tk listbox.
set gDemo(listFont) {-family {Courier} -size 10}
# Determine the directory of this script.
set gDemo(scriptDir) [file dirname [info script]]
# Display mode.
set gDemo(fullScreen) false
# Window size.
set gDemo(winWidth) 640
set gDemo(winHeight) 480
set gDemo(blend) false ; # Blending ON/OFF
set gDemo(piover180) 0.0174532925
set gDemo(heading) 0.0
set gDemo(xpos) 0.0
set gDemo(zpos) 0.0
set gDemo(yrot) 0.0
set gDemo(walkbias) 0.0
set gDemo(walkbiasangle) 0.0
set gDemo(lookupdown) 0.0
set gDemo(z) 0.0
set filter -1 ; # Which Filter To Use
set filterList [list "Nearest" "Linear" "MipMapped"]
set texture [tcl3dVector GLuint 3] ; # Storage For 3 Textures
# Show errors occuring in the Togl callbacks.
proc bgerror { msg } {
tk_messageBox -icon error -type ok -message "Error: $msg\n\n$::errorInfo"
ExitProg
}
# 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 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 {} {
if { $::gDemo(fullScreen) } {
SetFullScreenMode .
set ::gDemo(fullScreen) false
} else {
SetWindowMode . $::gDemo(winWidth) $::gDemo(winHeight)
set ::gDemo(fullScreen) true
}
}
# Toggle blending on or off.
proc ToggleBlending { toglwin } {
set ::gDemo(blend) [expr ! $::gDemo(blend)]
if { $::gDemo(blend) } {
glEnable GL_BLEND ; # Turn Blending On
glDisable GL_DEPTH_FUNC ; # Turn Depth Testing Off
} else {
glDisable GL_BLEND ; # Turn Blending Off
glEnable GL_DEPTH_FUNC ; # Turn Depth Testing On
}
$toglwin postredisplay
}
# Toggle between the different texture filters.
proc ToggleFilter { toglwin } {
incr ::filter
if { $::filter > 2 } {
set ::filter 0
}
set filterName [lindex $::filterList $::filter]
PrintInfo [format "Filter %s\n%s" $filterName $::glInfo]
$toglwin postredisplay
}
proc SetBiasUp { toglwin } {
set ::gDemo(xpos) [expr {$::gDemo(xpos) - sin($::gDemo(heading)*$::gDemo(piover180)) * 0.05}]
set ::gDemo(zpos) [expr {$::gDemo(zpos) - cos($::gDemo(heading)*$::gDemo(piover180)) * 0.05}]
if {$::gDemo(walkbiasangle) >= 359.0} {
set ::gDemo(walkbiasangle) 0.0
} else {
set ::gDemo(walkbiasangle) [expr {$::gDemo(walkbiasangle) + 10}]
}
set ::gDemo(walkbias) [expr {sin($::gDemo(walkbiasangle) * $::gDemo(piover180))/20.0}]
$toglwin postredisplay
}
proc SetBiasDown { toglwin } {
set ::gDemo(xpos) [expr {$::gDemo(xpos) + sin($::gDemo(heading)*$::gDemo(piover180)) * 0.05}]
set ::gDemo(zpos) [expr {$::gDemo(zpos) + cos($::gDemo(heading)*$::gDemo(piover180)) * 0.05}]
if {$::gDemo(walkbiasangle) <= 1.0} {
set ::gDemo(walkbiasangle) 359.0
} else {
set ::gDemo(walkbiasangle) [expr {$::gDemo(walkbiasangle) - 10}]
}
set ::gDemo(walkbias) [expr {sin($::gDemo(walkbiasangle) * $::gDemo(piover180))/20.0}]
$toglwin postredisplay
}
proc SetHeading { toglwin val } {
set ::gDemo(heading) [expr {$::gDemo(heading) + $val}]
set ::gDemo(yrot) $::gDemo(heading)
$toglwin postredisplay
}
proc SetLook { toglwin val } {
set ::gDemo(lookupdown) [expr {$::gDemo(lookupdown) + $val}]
$toglwin postredisplay
}
proc SetDepth { toglwin val } {
set ::gDemo(z) [expr {$::gDemo(z) + $val}]
$toglwin postredisplay
}
proc readstr { fp } {
while { 1 } {
gets $fp line
# Empty line or comment: Read next line
if { ($line eq "") || ([string index $line 0] eq "/") } {
continue
} else {
break
}
}
return $line
}
proc SetupWorld {} {
set fileName [file join $::gDemo(scriptDir) "Data" "World.txt"]
set filein [open $fileName r] ; # File To Load World Data From
set oneline [readstr $filein]
scan $oneline "NUMPOLLIES %d" numtriangles
set ::sector1(numtriangles) $numtriangles
for { set loop 0 } { $loop < $numtriangles } { incr loop } {
for { set vert 0 } { $vert < 3 } { incr vert } {
set oneline [readstr $filein]
scan $oneline "%f %f %f %f %f" x y z u v
set ::sector1($loop,$vert,x) $x
set ::sector1($loop,$vert,y) $y
set ::sector1($loop,$vert,z) $z
set ::sector1($loop,$vert,u) $u
set ::sector1($loop,$vert,v) $v
}
}
close $filein
}
proc LoadGLTextures {} {
# Load texture image.
set texName [file join $::gDemo(scriptDir) "Data" "Mud.bmp"]
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
}
glGenTextures 3 $::texture ; # Create Three Textures
# Create Nearest Filtered Texture
glBindTexture GL_TEXTURE_2D [$::texture get 0]
glTexParameteri GL_TEXTURE_2D GL_TEXTURE_MAG_FILTER $::GL_NEAREST
glTexParameteri GL_TEXTURE_2D GL_TEXTURE_MIN_FILTER $::GL_NEAREST
glTexImage2D GL_TEXTURE_2D 0 $n $w $h 0 $type GL_UNSIGNED_BYTE $TextureImage
# Create Linear Filtered Texture
glBindTexture GL_TEXTURE_2D [$::texture get 1]
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 $n $w $h 0 $type GL_UNSIGNED_BYTE $TextureImage
# Create MipMapped Texture
glBindTexture GL_TEXTURE_2D [$::texture get 2]
glTexParameteri GL_TEXTURE_2D GL_TEXTURE_MAG_FILTER $::GL_LINEAR
glTexParameteri GL_TEXTURE_2D GL_TEXTURE_MIN_FILTER $::GL_LINEAR_MIPMAP_NEAREST
glTexImage2D GL_TEXTURE_2D 0 $n $w $h 0 $type GL_UNSIGNED_BYTE $TextureImage
gluBuild2DMipmaps GL_TEXTURE_2D $n $w $h $type GL_UNSIGNED_BYTE $TextureImage
$TextureImage delete ; # Free The Texture Image Memory
}
# 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
# Calculate The Aspect Ratio Of The Window
gluPerspective 45.0 [expr double($w)/double($h)] 0.1 100.0
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
glEnable GL_TEXTURE_2D ; # Enable Texture Mapping
glBlendFunc GL_SRC_ALPHA GL_ONE ; # Set The Blending Function For Translucency
glClearColor 0.0 0.0 0.0 0.0 ; # This Will Clear The Background Color To Black
glClearDepth 1.0 ; # Depth Buffer Setup
glEnable GL_DEPTH_TEST ; # Enables Depth Testing
glShadeModel GL_SMOOTH ; # Enable Smooth Shading
glDepthFunc GL_LEQUAL ; # The Type Of Depth Testing To Do
glHint GL_PERSPECTIVE_CORRECTION_HINT GL_NICEST ; # Really Nice Perspective Calculations
SetupWorld
}
# Here's Where We Do All The Drawing
proc DisplayCallback { toglwin } {
# 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]
glLoadIdentity ; # Reset The Current Modelview Matrix
set xtrans [expr {-1.0 * $::gDemo(xpos)}]
set ztrans [expr {-1.0 * $::gDemo(zpos)}]
set ytrans [expr {-1.0 * $::gDemo(walkbias)-0.25}]
set sceneroty [expr {360.0 - $::gDemo(yrot)}]
glRotatef $::gDemo(lookupdown) 1.0 0.0 0.0
glRotatef $sceneroty 0.0 1.0 0.0
glTranslatef $xtrans $ytrans $ztrans
glBindTexture GL_TEXTURE_2D [$::texture get $::filter]
set numtriangles $::sector1(numtriangles)
# Process Each Triangle
for { set loop_m 0 } { $loop_m < $numtriangles } { incr loop_m } {
glBegin GL_TRIANGLES
glNormal3f 0.0 0.0 1.0
set x_m $::sector1($loop_m,0,x)
set y_m $::sector1($loop_m,0,y)
set z_m $::sector1($loop_m,0,z)
set u_m $::sector1($loop_m,0,u)
set v_m $::sector1($loop_m,0,v)
glTexCoord2f $u_m $v_m ; glVertex3f $x_m $y_m $z_m
set x_m $::sector1($loop_m,1,x)
set y_m $::sector1($loop_m,1,y)
set z_m $::sector1($loop_m,1,z)
set u_m $::sector1($loop_m,1,u)
set v_m $::sector1($loop_m,1,v)
glTexCoord2f $u_m $v_m ; glVertex3f $x_m $y_m $z_m
set x_m $::sector1($loop_m,2,x)
set y_m $::sector1($loop_m,2,y)
set z_m $::sector1($loop_m,2,z)
set u_m $::sector1($loop_m,2,u)
set v_m $::sector1($loop_m,2,v)
glTexCoord2f $u_m $v_m ; glVertex3f $x_m $y_m $z_m
glEnd
}
$toglwin swapbuffers
}
# 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
# Create Our OpenGL Window
togl .fr.toglwin -width $::gDemo(winWidth) -height $::gDemo(winHeight) \
-double true -depth true \
-createcommand CreateCallback \
-reshapecommand ReshapeCallback \
-displaycommand DisplayCallback
listbox .fr.usage -font $::gDemo(listFont) -height 7
label .fr.info
grid .fr.toglwin -row 0 -column 0 -sticky news
grid .fr.usage -row 1 -column 0 -sticky news
grid .fr.info -row 2 -column 0 -sticky news
grid rowconfigure .fr 0 -weight 1
grid columnconfigure .fr 0 -weight 1
wm title . "Tcl3D demo: Lionel Brits & NeHe's 3D World Tutorial (Lesson 10)"
# Watch For ESC Key And Quit Messages
wm protocol . WM_DELETE_WINDOW "ExitProg"
bind . <Key-Escape> "ExitProg"
bind . <Key-F1> "ToggleWindowMode"
bind . <Key-b> "ToggleBlending .fr.toglwin"
bind . <Key-f> "ToggleFilter .fr.toglwin"
bind . <Key-Up> "SetBiasUp .fr.toglwin"
bind . <Key-Down> "SetBiasDown .fr.toglwin"
bind . <Key-Left> "SetHeading .fr.toglwin 1.0"
bind . <Key-Right> "SetHeading .fr.toglwin -1.0"
bind . <Key-Prior> "SetDepth .fr.toglwin -0.02 ; SetLook .fr.toglwin -1.0"
bind . <Key-Next> "SetDepth .fr.toglwin 0.02 ; SetLook .fr.toglwin 1.0"
.fr.usage insert end "Key-Escape Exit"
.fr.usage insert end "Key-F1 Toggle window mode"
.fr.usage insert end "Key-b Toggle blending"
.fr.usage insert end "Key-f Toggle filter"
.fr.usage insert end "Key-Up|Down Move forth|back"
.fr.usage insert end "Key-Left|Right Look left|right"
.fr.usage insert end "Key-PgUp|PgDn Look up|down"
.fr.usage configure -state disabled
}
CreateWindow
set glInfo [tcl3dOglGetInfoString]
ToggleFilter .fr.toglwin
|