# Lesson22.tcl
#
# NeHe's GL_ARB_multitexture & Bump Mapping Tutorial
#
# This Code Was Created by Jens Schneider (WizardSoft) 2000 
# Lesson22 to the series of OpenGL tutorials by NeHe-Production
#
# This Code is loosely based upon Lesson06 by Jeff Molofee.
# contact me at: schneide@pool.informatik.rwth-aachen.de
#
# Basecode 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/08/16
# See www.tcl3d.org for the Tcl3D extension.

package require Img
package require tcl3d

set MAX_EMBOSS 0.008  ; # Maximum Emboss-Translate. Increase To Get Higher Immersion
                        # At A Cost Of Lower Quality (More Artifacts Will Occur!)

# Here Comes The ARB-Multitexture Support.
# There Are (Optimally) 6 New Commands To The OpenGL Set:
# glMultiTexCoordifARB i=1..4 : Sets Texture-Coordinates For Texel-Pipeline #i
# glActiveTextureARB          : Sets Active Texel-Pipeline
# glClientActiveTextureARB    : Sets Active Texel-Pipeline For The Pointer-Array-Commands
#
# There Are Even More For The Various Formats Of glMultiTexCoordi{f,fv,d,i},
# But We Don't Need Them.

# Font to be used in the Tk listbox.
set listFont {-family {Courier} -size 10}

# Determine the directory of this script.
set gDemo(scriptDir) [file dirname [info script]]

# Display mode.
set fullScreen false

# Window size.
set gDemo(winWidth)  640
set gDemo(winHeight) 480

set xrot   0.0  ; # X Rotation
set yrot   0.0  ; # Y Rotation
set xspeed 0.1  ; # X Rotation Speed
set yspeed 0.0  ; # Y Rotation Speed
set z     -5.0  ; # Depth Into The Screen

set LightAmbient  { 0.2 0.2 0.2 }               ; # Ambient Light is 20% white
set LightDiffuse  { 1.0 1.0 1.0 }               ; # Diffuse Light is white
set LightPosition { 0.0 0.0 2.0 }               ; # Position is somewhat in front of screen

set __ARB_ENABLE 1                              ; # Used To Disable ARB Extensions Entirely

set multitextureSupported 0                     ; # Flag Indicating Whether Multitexturing Is Supported
set useMultitexture 1                           ; # Use It If It Is Supported?
set maxTexelUnits 1                             ; # Number Of Texel-Pipelines. This Is At Least 1.

set emboss 0                                    ; # Emboss Only, No Basetexture?
set bumps  1                                    ; # Do Bumpmapping?

set filter 1                                    ; # Which Filter To Use
set texture   [tcl3dVector GLuint 3]            ; # Storage For 3 Textures
set bump      [tcl3dVector GLuint 3]            ; # Our Bumpmappings
set invbump   [tcl3dVector GLuint 3]            ; # Inverted Bumpmaps 
set glLogo    [tcl3dVector GLuint 1]            ; # Handle For OpenGL-Logo
set multiLogo [tcl3dVector GLuint 1]            ; # Handle For Multitexture-Enabled-Logo

set Gray { 0.5 0.5 0.5 1.0 }

# Data Contains The Faces For The Cube In Format 2xTexCoord, 3xVertex;
# Note That The Tesselation Of The Cube Is Only Absolute Minimum.
# Face ordering: Front, Back, Top, Bottom, Right, Left.
set data [list \
        0.0 0.0     -1.0 -1.0  1.0 \
        1.0 0.0      1.0 -1.0  1.0 \
        1.0 1.0      1.0  1.0  1.0 \
        0.0 1.0     -1.0  1.0  1.0 \
        1.0 0.0     -1.0 -1.0 -1.0 \
        1.0 1.0     -1.0  1.0 -1.0 \
        0.0 1.0      1.0  1.0 -1.0 \
        0.0 0.0      1.0 -1.0 -1.0 \
        0.0 1.0     -1.0  1.0 -1.0 \
        0.0 0.0     -1.0  1.0  1.0 \
        1.0 0.0      1.0  1.0  1.0 \
        1.0 1.0      1.0  1.0 -1.0 \
        1.0 1.0     -1.0 -1.0 -1.0 \
        0.0 1.0      1.0 -1.0 -1.0 \
        0.0 0.0      1.0 -1.0  1.0 \
        1.0 0.0     -1.0 -1.0  1.0 \
        1.0 0.0      1.0 -1.0 -1.0 \
        1.0 1.0      1.0  1.0 -1.0 \
        0.0 1.0      1.0  1.0  1.0 \
        0.0 0.0      1.0 -1.0  1.0 \
        0.0 0.0     -1.0 -1.0 -1.0 \
        1.0 0.0     -1.0 -1.0  1.0 \
        1.0 1.0     -1.0  1.0  1.0 \
        0.0 1.0     -1.0  1.0 -1.0 \
]

# 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 { $::fullScreen } {
        SetFullScreenMode .
        set ::fullScreen false
    } else {
        SetWindowMode . $::gDemo(winWidth) $::gDemo(winHeight)
        set ::fullScreen true
    }
}

proc ToggleEmboss {} {
    set ::emboss [expr 1 - $::emboss]
    .fr.toglwin postredisplay
}

proc ToggleMultitexture {} {
    set ::useMultitexture [expr (1 - $::useMultitexture) && $::multitextureSupported]
    .fr.toglwin postredisplay
}

proc ToggleBumps {} {
    set ::bumps [expr 1 - $::bumps]
    .fr.toglwin postredisplay
}

proc ToggleFilter {} {
    incr ::filter
    if { $::filter > 2 } {
        set ::filter 0
    }
    .fr.toglwin postredisplay
}

proc SetXSpeed { val } {
    set ::xspeed [expr $::xspeed + $val]
}

proc SetYSpeed { val } {
    set ::yspeed [expr $::yspeed + $val]
}

proc SetDepth { val } {
    set ::z [expr $::z + $val]
}

# initMultitexture Checks At Run-Time If Multitexturing Is Supported
proc initMultitexture { toglwin } {
    if { [tcl3dOglHaveExtension $toglwin "GL_ARB_multitexture"] && \
         $::__ARB_ENABLE && \
         [tcl3dOglHaveExtension $toglwin "GL_EXT_texture_env_combine"] } {
        set ::maxTexelUnits [tcl3dOglGetMaxTextureUnits]
        puts "GL_ARB_multitexture extension found ($::maxTexelUnits texel units)"
        return 1
    }
    puts "GL_ARB_multitexture extension not found"
    set ::useMultitexture 0             ; # We Can't Use It If It Isn't Supported!
    return 0
}

proc initLights {} {
    glLightfv GL_LIGHT1 GL_AMBIENT $::LightAmbient        ; # Load Light-Parameters Into GL_LIGHT1
    glLightfv GL_LIGHT1 GL_DIFFUSE $::LightDiffuse
    glLightfv GL_LIGHT1 GL_POSITION $::LightPosition

    glEnable GL_LIGHT1
}

proc LoadImage { imgName numChans } {
    if { $numChans != 3 && $numChans != 4 } {
        error "Error: Only 3 or 4 channels allowed ($numChans supplied)"
    }
    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 texImg [tcl3dVectorFromPhoto $phImg $numChans]
        image delete $phImg
    }
    return [list $texImg $w $h]
}

# Load Bitmaps And Convert To Textures
proc LoadGLTextures {} {

    # Load The Tile-Bitmap For Base-Texture
    set imgInfo [LoadImage "Base.bmp" 3]
    set imgData   [lindex $imgInfo 0]
    set imgWidth  [lindex $imgInfo 1]
    set imgHeight [lindex $imgInfo 2]

    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 $::GL_RGB8 $imgWidth $imgHeight 0 GL_RGB GL_UNSIGNED_BYTE $imgData

    # 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 $::GL_RGB8 $imgWidth $imgHeight 0 GL_RGB GL_UNSIGNED_BYTE $imgData

    # 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
    gluBuild2DMipmaps GL_TEXTURE_2D $::GL_RGB8 $imgWidth $imgHeight GL_RGB GL_UNSIGNED_BYTE $imgData

    # Delete the image data vector.
    $imgData delete
    
    # Load The Bumpmaps
    set imgInfo [LoadImage "Bump.bmp" 3]
    set imgData   [lindex $imgInfo 0]
    set imgWidth  [lindex $imgInfo 1]
    set imgHeight [lindex $imgInfo 2]

    glPixelTransferf GL_RED_SCALE   0.5         ; # Scale RGB By 50%, So That We Have Only           
    glPixelTransferf GL_GREEN_SCALE 0.5         ; # Half Intenstity
    glPixelTransferf GL_BLUE_SCALE  0.5

    glTexParameteri  GL_TEXTURE_2D GL_TEXTURE_WRAP_S $::GL_CLAMP       ; # No Wrapping, Please!
    glTexParameteri  GL_TEXTURE_2D GL_TEXTURE_WRAP_T $::GL_CLAMP
    glTexParameterfv GL_TEXTURE_2D GL_TEXTURE_BORDER_COLOR $::Gray

    glGenTextures 3 $::bump                     ; # Create Three Textures

    # Create Nearest Filtered Texture
    glBindTexture GL_TEXTURE_2D [$::bump 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 $::GL_RGB8 $imgWidth $imgHeight 0 GL_RGB GL_UNSIGNED_BYTE $imgData

    # Create Linear Filtered Texture
    glBindTexture GL_TEXTURE_2D [$::bump 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 $::GL_RGB8 $imgWidth $imgHeight 0 GL_RGB GL_UNSIGNED_BYTE $imgData

    # Create MipMapped Texture
    glBindTexture GL_TEXTURE_2D [$::bump get 2]
    glTexParameteri GL_TEXTURE_2D GL_TEXTURE_MAG_FILTER $::GL_LINEAR
    glTexParameteri GL_TEXTURE_2D GL_TEXTURE_MIN_FILTER $::GL_LINEAR_MIPMAP_NEAREST
    gluBuild2DMipmaps GL_TEXTURE_2D $::GL_RGB8 $imgWidth $imgHeight GL_RGB GL_UNSIGNED_BYTE $imgData
        
    tcl3dVectorManip $imgData $imgWidth $imgHeight 3 -1 255 ; # Invert The Bumpmap

    glGenTextures 3 $::invbump ; # Create Three Textures

    # Create Nearest Filtered Texture
    glBindTexture GL_TEXTURE_2D [$::invbump 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 $::GL_RGB8 $imgWidth $imgHeight 0 GL_RGB GL_UNSIGNED_BYTE $imgData
        
    # Create Linear Filtered Texture
    glBindTexture GL_TEXTURE_2D [$::invbump 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 $::GL_RGB8 $imgWidth $imgHeight 0 GL_RGB GL_UNSIGNED_BYTE $imgData

    # Create MipMapped Texture
    glBindTexture GL_TEXTURE_2D [$::invbump get 2]
    glTexParameteri GL_TEXTURE_2D GL_TEXTURE_MAG_FILTER $::GL_LINEAR
    glTexParameteri GL_TEXTURE_2D GL_TEXTURE_MIN_FILTER $::GL_LINEAR_MIPMAP_NEAREST
    gluBuild2DMipmaps GL_TEXTURE_2D $::GL_RGB8 $imgWidth $imgHeight GL_RGB GL_UNSIGNED_BYTE $imgData
    
    glPixelTransferf GL_RED_SCALE   1.0 ; # Scale RGB Back To 100% Again     
    glPixelTransferf GL_GREEN_SCALE 1.0          
    glPixelTransferf GL_BLUE_SCALE  1.0

    # Delete the image data vector.
    $imgData delete

    # Load The Logo-Bitmaps
    set imgInfo [LoadImage "OpenGL_Alpha.bmp" 3]
    set imgData   [lindex $imgInfo 0]
    set imgWidth  [lindex $imgInfo 1]
    set imgHeight [lindex $imgInfo 2]

    set alphaImg [tcl3dVector GLubyte [expr $imgWidth * $imgHeight * 4]] ; # Create Memory For RGBA8-Texture
    tcl3dVectorCopyChannel $imgData $alphaImg 0 3 $imgWidth $imgHeight 3 4 ; # Pick Only Red Value As Alpha
    $imgData delete

    set imgInfo [LoadImage "OpenGL.bmp" 3]
    set imgData [lindex $imgInfo 0]
    tcl3dVectorCopyChannel $imgData $alphaImg 0 0 $imgWidth $imgHeight 3 4 ; # Red
    tcl3dVectorCopyChannel $imgData $alphaImg 1 1 $imgWidth $imgHeight 3 4 ; # Green
    tcl3dVectorCopyChannel $imgData $alphaImg 2 2 $imgWidth $imgHeight 3 4 ; # Blue
    $imgData delete

    glGenTextures 1 $::glLogo ; # Create One Texture

    # Create Linear Filtered RGBA8-Texture
    glBindTexture GL_TEXTURE_2D [$::glLogo 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 $::GL_RGBA8 $imgWidth $imgHeight 0 GL_RGBA GL_UNSIGNED_BYTE $alphaImg
    $alphaImg delete
    
    # Load The "Extension Enabled"-Logo
    set imgInfo [LoadImage "Multi_On_Alpha.bmp" 3]
    set imgData   [lindex $imgInfo 0]
    set imgWidth  [lindex $imgInfo 1]
    set imgHeight [lindex $imgInfo 2]

    set alphaImg [tcl3dVector GLubyte [expr $imgWidth * $imgHeight * 4]]

    tcl3dVectorCopyChannel $imgData $alphaImg 0 3 $imgWidth $imgHeight 3 4 ; # Pick Only Red Value As Alpha
    $imgData delete

    set imgInfo [LoadImage "Multi_On.bmp" 3]
    set imgData [lindex $imgInfo 0]
    tcl3dVectorCopyChannel $imgData $alphaImg 0 0 $imgWidth $imgHeight 3 4 ; # Red
    tcl3dVectorCopyChannel $imgData $alphaImg 1 1 $imgWidth $imgHeight 3 4 ; # Green
    tcl3dVectorCopyChannel $imgData $alphaImg 2 2 $imgWidth $imgHeight 3 4 ; # Blue
    $imgData delete

    glGenTextures 1 $::multiLogo ; # Create One Texture

    # Create Linear Filtered RGBA8-Texture
    glBindTexture GL_TEXTURE_2D [$::multiLogo 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 $::GL_RGBA8 $imgWidth $imgHeight 0 GL_RGBA GL_UNSIGNED_BYTE $alphaImg
    $alphaImg delete
}

# 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
}

proc doCube {} {
    global data

    glBegin GL_QUADS
    # Front Face
    glNormal3f 0.0 0.0 1.0
    for { set i 0 } { $i < 4 } { incr i } {
        set ind [expr 5 * $i]
        glTexCoord2f [lindex $data $ind] [lindex $data [expr $ind+1]]
        glVertex3f [lindex $data [expr $ind+2]] [lindex $data [expr $ind+3]] [lindex $data [expr $ind+4]]
    }
    # Back Face
    glNormal3f 0.0 0.0 -1.0
    for { set i 4 } { $i < 8 } { incr i } {
        set ind [expr 5 * $i]
        glTexCoord2f [lindex $data $ind] [lindex $data [expr $ind+1]]
        glVertex3f [lindex $data [expr $ind+2]] [lindex $data [expr $ind+3]] [lindex $data [expr $ind+4]]
    }
    # Top Face
    glNormal3f 0.0 1.0 0.0
    for { set i 8 } { $i < 12 } { incr i } {
        set ind [expr 5 * $i]
        glTexCoord2f [lindex $data $ind] [lindex $data [expr $ind+1]]
        glVertex3f [lindex $data [expr $ind+2]] [lindex $data [expr $ind+3]] [lindex $data [expr $ind+4]]
    }
    # Bottom Face
    glNormal3f 0.0 -1.0 0.0
    for { set i 12 } { $i < 16 } { incr i } {
        set ind [expr 5 * $i]
        glTexCoord2f [lindex $data $ind] [lindex $data [expr $ind+1]]
        glVertex3f [lindex $data [expr $ind+2]] [lindex $data [expr $ind+3]] [lindex $data [expr $ind+4]]
    }
    # Right face
    glNormal3f 1.0 0.0 0.0
    for { set i 16 } { $i < 20 } { incr i } {
        set ind [expr 5 * $i]
        glTexCoord2f [lindex $data $ind] [lindex $data [expr $ind+1]]
        glVertex3f [lindex $data [expr $ind+2]] [lindex $data [expr $ind+3]] [lindex $data [expr $ind+4]]
    }
    # Left Face
    glNormal3f -1.0 0.0 0.0
    for { set i 20 } { $i < 24 } { incr i } {
        set ind [expr 5 * $i]
        glTexCoord2f [lindex $data $ind] [lindex $data [expr $ind+1]]
        glVertex3f [lindex $data [expr $ind+2]] [lindex $data [expr $ind+3]] [lindex $data [expr $ind+4]]
    }
    glEnd
}

# All Setup For OpenGL Goes Here
proc CreateCallback { toglwin } {
    set ::multitextureSupported [initMultitexture $toglwin]
    LoadGLTextures                          ; # Jump To Texture Loading Routine
    
    glEnable GL_TEXTURE_2D                  ; # Enable Texture Mapping
    glShadeModel GL_SMOOTH                  ; # Enable Smooth Shading
    glClearColor 0.0 0.0 0.0 0.5            ; # Black Background
    glClearDepth 1.0                        ; # Depth Buffer Setup
    glEnable GL_DEPTH_TEST                  ; # Enables Depth Testing
    glDepthFunc GL_LEQUAL                   ; # The Type Of Depth Testing To Do
    glHint GL_PERSPECTIVE_CORRECTION_HINT GL_NICEST ; # Really Nice Perspective Calculations

    initLights                              ; # Initialize OpenGL Light  
}

#  Okay, Here Comes The Important Stuff:
#  
#  On http://www.nvidia.com/marketing/Developer/DevRel.nsf/TechnicalDemosFrame?OpenPage
#  You Can Find A Demo Called GL_BUMP That Is A Little Bit More Complicated.
#  GL_BUMP:   Copyright Diego Tártara, 1999.           
#           -  diego_tartara@ciudad.com.ar  -
#
#  The Idea Behind GL_BUMP Is, That You Compute The Texture-Coordinate Offset As Follows:
#      0) All Coordinates Either In Object Or In World Space.
#      1) Calculate Vertex v From Actual Position (The Vertex You're At) To The Lightposition
#      2) Normalize v
#      3) Project This v Into Tangent Space.
#          Tangent Space Is The Plane "Touching" The Object In Our Current Position On It.
#          Typically, If You're Working With Flat Surfaces, This Is The Surface Itself.
#      4) Offset s,t-Texture-Coordinates By The Projected v's x And y-Component.
#
#  * This Would Be Called Once Per Vertex In Our Geometry, If Done Correctly.
#  * This Might Lead To Incoherencies In Our Texture Coordinates, But Is Ok As Long As You Did Not
#  * Wrap The Bumpmap.
#      
#  Basically, We Do It The Same Way With Some Exceptions:
#      ad 0) We'll Work In Object Space All Time. This Has The Advantage That We'll Only
#            Have To Transform The Lightposition From Frame To Frame. This Position Obviously
#            Has To Be Transformed Using The Inversion Of The Modelview Matrix. This Is, However,
#            A Considerable Drawback, If You Don't Know How Your Modelview Matrix Was Built, Since
#            Inverting A Matrix Is Costly And Complicated.
#      ad 1) Do It Exactly That Way.
#      ad 2) Do It Exactly That Way.
#      ad 3) To Project The Lightvector Into Tangent Space, We'll Support The Setup-Routine
#            With Two Directions: One Of Increasing s-Texture-Coordinate Axis, The Other In
#            Increasing t-Texture-Coordinate Axis. The Projection Simply Is (Assumed Both
#            texCoord Vectors And The Lightvector Are Normalized) The Dotproduct Between The
#            Respective texCoord Vector And The Lightvector. 
#      ad 4) The Offset Is Computed By Taking The Result Of Step 3 And Multiplying The Two
#            Numbers With MAX_EMBOSS, A Constant That Specifies How Much Quality We're Willing To
#            Trade For Stronger Bump-Effects. Just Temper A Little Bit With MAX_EMBOSS!
#
#  WHY THIS IS COOL:
#      * Have A Look!
#      * Very Cheap To Implement (About One Squareroot And A Couple Of MULs)!
#      * Can Even Be Further Optimized!
#      * SetUpBump Doesn't Disturb glBegin()/glEnd()
#      * THIS DOES ALWAYS WORK - Not Only With XY-Tangent Spaces!!
#
#  DRAWBACKS:
#      * Must Know "Structure" Of Modelview-Matrix Or Invert It. Possible To Do The Whole Thing
#      * In World Space, But This Involves One Transformation For Each Vertex!
#  
    
proc SetUpBumps { n c l s t } {
    set v [tcl3dVector GLfloat 3] ; # Vertex From Current Position To Light    
    set cv [tcl3dVectorFromList GLfloat $c]

    # Calculate v From Current Vector c To Lightposition And Normalize v   
    tcl3dVec3fSubtract $l $cv $v
    tcl3dVec3fNormalize $v
    # Project v Such That We Get Two Values Along Each Texture-Coordinat Axis.
    set retList $c
    lset retList 0 [expr ([lindex $s 0]*[$v get 0] + [lindex $s 1]*[$v get 1] + [lindex $s 2]*[$v get 2])*$::MAX_EMBOSS]
    lset retList 1 [expr ([lindex $t 0]*[$v get 0] + [lindex $t 1]*[$v get 1] + [lindex $t 2]*[$v get 2])*$::MAX_EMBOSS]
    return $retList
}

# MUST CALL THIS LAST!!!, Billboards The Two Logos.
proc doLogo {} {
    glDepthFunc GL_ALWAYS
    glBlendFunc GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA
    glEnable GL_BLEND
    glDisable GL_LIGHTING
    glLoadIdentity
    glBindTexture GL_TEXTURE_2D [$::glLogo get 0]
    glBegin GL_QUADS
        glTexCoord2f 0.0 0.0 ;  glVertex3f 0.23 -0.4  -1.0
        glTexCoord2f 1.0 0.0 ;  glVertex3f 0.53 -0.4  -1.0
        glTexCoord2f 1.0 1.0 ;  glVertex3f 0.53 -0.25 -1.0
        glTexCoord2f 0.0 1.0 ;  glVertex3f 0.23 -0.25 -1.0
    glEnd
    if { $::useMultitexture } {
        glBindTexture GL_TEXTURE_2D [$::multiLogo get 0]
        glBegin GL_QUADS
            glTexCoord2f 0.0 0.0 ;  glVertex3f -0.53 -0.4 -1.0
            glTexCoord2f 1.0 0.0 ;  glVertex3f -0.33 -0.4 -1.0
            glTexCoord2f 1.0 1.0 ;  glVertex3f -0.33 -0.3 -1.0
            glTexCoord2f 0.0 1.0 ;  glVertex3f -0.53 -0.3 -1.0
        glEnd
    }
    glDepthFunc GL_LEQUAL
}

proc doMesh1TexelUnits {} {
    global data

    #puts "doMesh1"
    set c {0.0 0.0 0.0} ; # Holds Current Vertex
    set n {0.0 0.0 0.0} ; # Normalized Normal Of Current Surface     
    set s {0.0 0.0 0.0} ; # s-Texture Coordinate Direction, Normalized
    set t {0.0 0.0 0.0} ; # t-Texture Coordinate Direction, Normalized
    set M    [tcl3dVector GLfloat 16] ; # Holds The Modelview Matrix.
    set Minv [tcl3dVector GLfloat 16] ; # Holds The Inverted Modelview Matrix To Do So.

    # Clear Screen And Depth Buffer
    glClear [expr $::GL_COLOR_BUFFER_BIT | $::GL_DEPTH_BUFFER_BIT]

    # Build Inverse Modelview Matrix First. This Substitutes One Push/Pop With One glLoadIdentity();
    # Simply Build It By Doing All Transformations Negated And In Reverse Order.
    glLoadIdentity
    glRotatef [expr {-1.0 * $::yrot}] 0.0 1.0 0.0
    glRotatef [expr {-1.0 * $::xrot}] 1.0 0.0 0.0
    glTranslatef 0.0 0.0 [expr {-1.0 * $::z}]

    glGetFloatv GL_MODELVIEW_MATRIX $M
    glLoadIdentity
    glTranslatef 0.0 0.0 $::z
    glRotatef $::xrot 1.0 0.0 0.0
    glRotatef $::yrot 0.0 1.0 0.0
    
    # Transform The Lightposition Into Object Coordinates:
    set l [tcl3dVectorFromList GLfloat $::LightPosition]
    tcl3dMatfTranspose $M $Minv
    tcl3dMatfTransformPoint $l $Minv $l
    
    # PASS#1: Use Texture "Bump"
    #         No Blend
    #         No Lighting
    #         No Offset Texture-Coordinates
    glBindTexture GL_TEXTURE_2D [$::bump get $::filter]
    glDisable GL_BLEND
    glDisable GL_LIGHTING
    doCube

    # PASS#2: Use Texture "Invbump"
    #         Blend GL_ONE To GL_ONE
    #         No Lighting
    #         Offset Texture Coordinates 
    glBindTexture GL_TEXTURE_2D [$::invbump get $::filter]
    glBlendFunc GL_ONE GL_ONE
    glDepthFunc GL_LEQUAL
    glEnable GL_BLEND

    glBegin GL_QUADS
        # Front Face
        set n { 0.0 0.0 1.0 }
        set s { 1.0 0.0 0.0 }
        set t { 0.0 1.0 0.0 }
        for { set i 0 } { $i < 4 } { incr i } {
            set ind [expr 5 * $i]
            lset c 0 [lindex $data [expr {$ind + 2}]]
            lset c 1 [lindex $data [expr {$ind + 3}]]
            lset c 2 [lindex $data [expr {$ind + 4}]]
            set c [SetUpBumps $n $c $l $s $t]
            glTexCoord2f [expr {[lindex $data $ind] + [lindex $c 0]}] \
                         [expr {[lindex $data [expr {$ind+1}]] + [lindex $c 1]}]
            glVertex3f [lindex $data [expr {$ind+2}]] \
                       [lindex $data [expr {$ind+3}]] \
                       [lindex $data [expr {$ind+4}]]
        }
        # Back Face    
        set n {  0.0 0.0 -1.0 }
        set s { -1.0 0.0  0.0 }
        set t {  0.0 1.0  0.0 }
        for { set i 4 } { $i < 8 } { incr i } {
            set ind [expr 5 * $i]
            lset c 0 [lindex $data [expr {$ind + 2}]]
            lset c 1 [lindex $data [expr {$ind + 3}]]
            lset c 2 [lindex $data [expr {$ind + 4}]]
            set c [SetUpBumps $n $c $l $s $t]
            glTexCoord2f [expr {[lindex $data $ind] + [lindex $c 0]}] \
                         [expr {[lindex $data [expr {$ind+1}]] + [lindex $c 1]}]
            glVertex3f [lindex $data [expr {$ind+2}]] \
                       [lindex $data [expr {$ind+3}]] \
                       [lindex $data [expr {$ind+4}]]
        }
        # Top Face 
        set n { 0.0 1.0  0.0 }
        set s { 1.0 0.0  0.0 }
        set t { 0.0 0.0 -1.0 }
        for { set i 8 } { $i < 12 } { incr i } {
            set ind [expr 5 * $i]
            lset c 0 [lindex $data [expr {$ind + 2}]]
            lset c 1 [lindex $data [expr {$ind + 3}]]
            lset c 2 [lindex $data [expr {$ind + 4}]]
            set c [SetUpBumps $n $c $l $s $t]
            glTexCoord2f [expr {[lindex $data $ind] + [lindex $c 0]}] \
                         [expr {[lindex $data [expr {$ind+1}]] + [lindex $c 1]}]
            glVertex3f [lindex $data [expr {$ind+2}]] \
                       [lindex $data [expr {$ind+3}]] \
                       [lindex $data [expr {$ind+4}]]
        }
        # Bottom Face
        set n {  0.0 -1.0  0.0 }
        set s { -1.0  0.0  0.0 }
        set t {  0.0  0.0 -1.0 }
        for { set i 12 } { $i < 16 } { incr i } {
            set ind [expr 5 * $i]
            lset c 0 [lindex $data [expr {$ind + 2}]]
            lset c 1 [lindex $data [expr {$ind + 3}]]
            lset c 2 [lindex $data [expr {$ind + 4}]]
            set c [SetUpBumps $n $c $l $s $t]
            glTexCoord2f [expr {[lindex $data $ind] + [lindex $c 0]}] \
                         [expr {[lindex $data [expr {$ind+1}]] + [lindex $c 1]}]
            glVertex3f [lindex $data [expr {$ind+2}]] \
                       [lindex $data [expr {$ind+3}]] \
                       [lindex $data [expr {$ind+4}]]
        }
        # Right Face   
        set n { 1.0 0.0  0.0 }
        set s { 0.0 0.0 -1.0 }
        set t { 0.0 1.0  0.0 }
        for { set i 16 } { $i < 20 } { incr i } {
            set ind [expr 5 * $i]
            lset c 0 [lindex $data [expr {$ind + 2}]]
            lset c 1 [lindex $data [expr {$ind + 3}]]
            lset c 2 [lindex $data [expr {$ind + 4}]]
            set c [SetUpBumps $n $c $l $s $t]
            glTexCoord2f [expr {[lindex $data $ind] + [lindex $c 0]}] \
                         [expr {[lindex $data [expr {$ind+1}]] + [lindex $c 1]}]
            glVertex3f [lindex $data [expr {$ind+2}]] \
                       [lindex $data [expr {$ind+3}]] \
                       [lindex $data [expr {$ind+4}]]
        }
        # Left Face
        set n { -1.0 0.0 0.0 }
        set s {  0.0 0.0 1.0 }
        set t {  0.0 1.0 0.0 }
        for { set i 20 } { $i < 24 } { incr i } {
            set ind [expr 5 * $i]
            lset c 0 [lindex $data [expr {$ind + 2}]]
            lset c 1 [lindex $data [expr {$ind + 3}]]
            lset c 2 [lindex $data [expr {$ind + 4}]]
            set c [SetUpBumps $n $c $l $s $t]
            glTexCoord2f [expr {[lindex $data $ind] + [lindex $c 0]}] \
                         [expr {[lindex $data [expr {$ind+1}]] + [lindex $c 1]}]
            glVertex3f [lindex $data [expr {$ind+2}]] \
                       [lindex $data [expr {$ind+3}]] \
                       [lindex $data [expr {$ind+4}]]
        }
    glEnd
    
    # PASS#3: Use Texture "Base"
    #         Blend GL_DST_COLOR To GL_SRC_COLOR (Multiplies By 2)
    #         Lighting Enabled
    #         No Offset Texture-Coordinates
    if { ! $::emboss } {
        glTexEnvf GL_TEXTURE_ENV GL_TEXTURE_ENV_MODE $::GL_MODULATE
        glBindTexture GL_TEXTURE_2D [$::texture get $::filter]
        glBlendFunc GL_DST_COLOR GL_SRC_COLOR
        glEnable GL_LIGHTING
        doCube
    }

    set ::xrot [expr {$::xrot + $::xspeed}]
    set ::yrot [expr {$::yrot + $::yspeed}]
    if { $::xrot > 360.0 } { set ::xrot [expr {$::xrot - 360.0}] }
    if { $::xrot <   0.0 } { set ::xrot [expr {$::xrot + 360.0}] }
    if { $::yrot > 360.0 } { set ::yrot [expr {$::yrot - 360.0}] }
    if { $::yrot <   0.0 } { set ::yrot [expr {$::yrot + 360.0}] }

    # LAST PASS: Do The Logos
    doLogo
}

proc doMesh2TexelUnits {} {
    global data

    #puts "doMesh2"
    set c {0.0 0.0 0.0} ; # Holds Current Vertex
    set n {0.0 0.0 0.0} ; # Normalized Normal Of Current Surface     
    set s {0.0 0.0 0.0} ; # s-Texture Coordinate Direction, Normalized
    set t {0.0 0.0 0.0} ; # t-Texture Coordinate Direction, Normalized
    set M    [tcl3dVector GLfloat 16] ; # Holds The Modelview Matrix.
    set Minv [tcl3dVector GLfloat 16] ; # Holds The Inverted Modelview Matrix To Do So.
    
    # Clear Screen And Depth Buffer
    glClear [expr $::GL_COLOR_BUFFER_BIT | $::GL_DEPTH_BUFFER_BIT]

    # Build Inverse Modelview Matrix First. This Substitutes One Push/Pop With One glLoadIdentity();
    # Simply Build It By Doing All Transformations Negated And In Reverse Order.
    glLoadIdentity
    glRotatef [expr {-1.0 * $::yrot}] 0.0 1.0 0.0
    glRotatef [expr {-1.0 * $::xrot}] 1.0 0.0 0.0
    glTranslatef 0.0 0.0 [expr {-1.0 * $::z}]

    glGetFloatv GL_MODELVIEW_MATRIX $M
    glLoadIdentity
    glTranslatef 0.0 0.0 $::z
    glRotatef $::xrot 1.0 0.0 0.0
    glRotatef $::yrot 0.0 1.0 0.0

    # Transform The Lightposition Into Object Coordinates:
    set l [tcl3dVectorFromList GLfloat $::LightPosition]
    tcl3dMatfTranspose $M $Minv
    tcl3dMatfTransformPoint $l $Minv $l 

    # PASS#1: Texel-Unit 0: Use Texture "Bump"
    #                       No Blend
    #                       No Lighting
    #                       No Offset Texture-Coordinates 
    #                       Texture-Operation "Replace"
    #         Texel-Unit 1: Use Texture "Invbump"
    #                       No Lighting
    #                       Offset Texture Coordinates 
    #                       Texture-Operation "Replace"

    # TEXTURE-UNIT #0      
    glActiveTextureARB GL_TEXTURE0_ARB
    glEnable GL_TEXTURE_2D
    glBindTexture GL_TEXTURE_2D [$::bump get $::filter]
    glTexEnvf GL_TEXTURE_ENV GL_TEXTURE_ENV_MODE $::GL_COMBINE_EXT
    glTexEnvf GL_TEXTURE_ENV GL_COMBINE_RGB_EXT $::GL_REPLACE
    # TEXTURE-UNIT #1:
    glActiveTextureARB GL_TEXTURE1_ARB
    glEnable GL_TEXTURE_2D
    glBindTexture GL_TEXTURE_2D [$::invbump get $::filter]
    glTexEnvf GL_TEXTURE_ENV GL_TEXTURE_ENV_MODE $::GL_COMBINE_EXT
    glTexEnvf GL_TEXTURE_ENV GL_COMBINE_RGB_EXT $::GL_ADD
    # General Switches:
    glDisable GL_BLEND
    glDisable GL_LIGHTING
    glBegin GL_QUADS
        # Front Face   
        set n { 0.0 0.0 1.0 }
        set s { 1.0 0.0 0.0 }
        set t { 0.0 1.0 0.0 }
        for { set i 0 } { $i < 4 } { incr i } {
            set ind [expr 5 * $i]
            lset c 0 [lindex $data [expr {$ind + 2}]]
            lset c 1 [lindex $data [expr {$ind + 3}]]
            lset c 2 [lindex $data [expr {$ind + 4}]]
            set c [SetUpBumps $n $c $l $s $t]
            glMultiTexCoord2fARB GL_TEXTURE0_ARB \
                                 [lindex $data $ind] [lindex $data [expr {$ind+1}]]
            glMultiTexCoord2fARB GL_TEXTURE1_ARB \
                                 [expr {[lindex $data $ind] + [lindex $c 0]}] \
                                 [expr {[lindex $data [expr {$ind+1}]] + [lindex $c 1]}]
            glVertex3f [lindex $data [expr {$ind+2}]] \
                       [lindex $data [expr {$ind+3}]] \
                       [lindex $data [expr {$ind+4}]]
        }
        # Back Face    
        set n {  0.0 0.0 -1.0 }
        set s { -1.0 0.0  0.0 }
        set t {  0.0 1.0  0.0 }
        for { set i 4 } { $i < 8 } { incr i } {
            set ind [expr 5 * $i]
            lset c 0 [lindex $data [expr {$ind + 2}]]
            lset c 1 [lindex $data [expr {$ind + 3}]]
            lset c 2 [lindex $data [expr {$ind + 4}]]
            set c [SetUpBumps $n $c $l $s $t]
            glMultiTexCoord2fARB GL_TEXTURE0_ARB \
                                 [lindex $data $ind] [lindex $data [expr {$ind+1}]]
            glMultiTexCoord2fARB GL_TEXTURE1_ARB \
                                 [expr {[lindex $data $ind] + [lindex $c 0]}] \
                                 [expr {[lindex $data [expr {$ind+1}]] + [lindex $c 1]}]
            glVertex3f [lindex $data [expr {$ind+2}]] \
                       [lindex $data [expr {$ind+3}]] \
                       [lindex $data [expr {$ind+4}]]
        }
        # Top Face 
        set n { 0.0 1.0  0.0 }
        set s { 1.0 0.0  0.0 }
        set t { 0.0 0.0 -1.0 }
        for { set i 8 } { $i < 12 } { incr i } {
            set ind [expr 5 * $i]
            lset c 0 [lindex $data [expr {$ind + 2}]]
            lset c 1 [lindex $data [expr {$ind + 3}]]
            lset c 2 [lindex $data [expr {$ind + 4}]]
            set c [SetUpBumps $n $c $l $s $t]
            glMultiTexCoord2fARB GL_TEXTURE0_ARB \
                                 [lindex $data $ind] [lindex $data [expr {$ind+1}]]
            glMultiTexCoord2fARB GL_TEXTURE1_ARB \
                                 [expr {[lindex $data $ind] + [lindex $c 0]}] \
                                 [expr {[lindex $data [expr {$ind+1}]] + [lindex $c 1]}]
            glVertex3f [lindex $data [expr {$ind+2}]] \
                       [lindex $data [expr {$ind+3}]] \
                       [lindex $data [expr {$ind+4}]]
        }
        # Bottom Face
        set n {  0.0 -1.0  0.0 }
        set s { -1.0  0.0  0.0 }
        set t {  0.0  0.0 -1.0 }
        for { set i 12 } { $i < 16 } { incr i } {
            set ind [expr 5 * $i]
            lset c 0 [lindex $data [expr {$ind + 2}]]
            lset c 1 [lindex $data [expr {$ind + 3}]]
            lset c 2 [lindex $data [expr {$ind + 4}]]
            set c [SetUpBumps $n $c $l $s $t]
            glMultiTexCoord2fARB GL_TEXTURE0_ARB \
                                 [lindex $data $ind] [lindex $data [expr {$ind+1}]]
            glMultiTexCoord2fARB GL_TEXTURE1_ARB \
                                 [expr {[lindex $data $ind] + [lindex $c 0]}] \
                                 [expr {[lindex $data [expr {$ind+1}]] + [lindex $c 1]}]
            glVertex3f [lindex $data [expr {$ind+2}]] \
                       [lindex $data [expr {$ind+3}]] \
                       [lindex $data [expr {$ind+4}]]
        }
        # Right Face   
        set n { 1.0 0.0  0.0 }
        set s { 0.0 0.0 -1.0 }
        set t { 0.0 1.0  0.0 }
        for { set i 16 } { $i < 20 } { incr i } {
            set ind [expr 5 * $i]
            lset c 0 [lindex $data [expr {$ind + 2}]]
            lset c 1 [lindex $data [expr {$ind + 3}]]
            lset c 2 [lindex $data [expr {$ind + 4}]]
            set c [SetUpBumps $n $c $l $s $t]
            glMultiTexCoord2fARB GL_TEXTURE0_ARB \
                                 [lindex $data $ind] [lindex $data [expr {$ind+1}]]
            glMultiTexCoord2fARB GL_TEXTURE1_ARB \
                                 [expr {[lindex $data $ind] + [lindex $c 0]}] \
                                 [expr {[lindex $data [expr {$ind+1}]] + [lindex $c 1]}]
            glVertex3f [lindex $data [expr {$ind+2}]] \
                       [lindex $data [expr {$ind+3}]] \
                       [lindex $data [expr {$ind+4}]]
        }
        # Left Face
        set n { -1.0 0.0 0.0 }
        set s {  0.0 0.0 1.0 }
        set t {  0.0 1.0 0.0 }
        for { set i 20 } { $i < 24 } { incr i } {
            set ind [expr 5 * $i]
            lset c 0 [lindex $data [expr {$ind + 2}]]
            lset c 1 [lindex $data [expr {$ind + 3}]]
            lset c 2 [lindex $data [expr {$ind + 4}]]
            set c [SetUpBumps $n $c $l $s $t]
            glMultiTexCoord2fARB GL_TEXTURE0_ARB \
                                 [lindex $data $ind] [lindex $data [expr {$ind+1}]]
            glMultiTexCoord2fARB GL_TEXTURE1_ARB \
                                 [expr {[lindex $data $ind] + [lindex $c 0]}] \
                                 [expr {[lindex $data [expr {$ind+1}]] + [lindex $c 1]}]
            glVertex3f [lindex $data [expr {$ind+2}]] \
                       [lindex $data [expr {$ind+3}]] \
                       [lindex $data [expr {$ind+4}]]
        }
    glEnd
    
    # PASS#2 Use Texture "Base"
    #        Blend GL_DST_COLOR To GL_SRC_COLOR (Multiplies By 2)
    #        Lighting Enabled
    #        No Offset Texture-Coordinates
    glActiveTextureARB GL_TEXTURE1_ARB
    glDisable GL_TEXTURE_2D
    glActiveTextureARB GL_TEXTURE0_ARB
    if { ! $::emboss } {
        glTexEnvf GL_TEXTURE_ENV GL_TEXTURE_ENV_MODE $::GL_MODULATE
        glBindTexture GL_TEXTURE_2D [$::texture get $::filter]
        glBlendFunc GL_DST_COLOR GL_SRC_COLOR
        glEnable GL_BLEND
        glEnable GL_LIGHTING
        doCube
    }

    set ::xrot [expr {$::xrot + $::xspeed}]
    set ::yrot [expr {$::yrot + $::yspeed}]
    if { $::xrot > 360.0 } { set ::xrot [expr {$::xrot - 360.0}] }
    if { $::xrot <   0.0 } { set ::xrot [expr {$::xrot + 360.0}] }
    if { $::yrot > 360.0 } { set ::yrot [expr {$::yrot - 360.0}] }
    if { $::yrot <   0.0 } { set ::yrot [expr {$::yrot + 360.0}] }

    # LAST PASS: Do The Logos
    doLogo
}

proc doMeshNoBumps {} {
    #puts "doMeshNoBumps"
    # Clear Screen And Depth Buffer
    glClear [expr $::GL_COLOR_BUFFER_BIT | $::GL_DEPTH_BUFFER_BIT]

    glLoadIdentity                      ; # Reset The Current Modelview Matrix
    glTranslatef 0.0 0.0 $::z
    glRotatef $::xrot 1.0 0.0 0.0
    glRotatef $::yrot 0.0 1.0 0.0

    if { $::useMultitexture } {
        glActiveTextureARB GL_TEXTURE1_ARB        
        glDisable GL_TEXTURE_2D
        glActiveTextureARB GL_TEXTURE0_ARB
    }
    glDisable GL_BLEND
    glBindTexture GL_TEXTURE_2D [$::texture get $::filter]
    glBlendFunc GL_DST_COLOR GL_SRC_COLOR
    glEnable GL_LIGHTING
    doCube
    
    set ::xrot [expr {$::xrot + $::xspeed}]
    set ::yrot [expr {$::yrot + $::yspeed}]
    if { $::xrot > 360.0 } { set ::xrot [expr {$::xrot - 360.0}] }
    if { $::xrot <   0.0 } { set ::xrot [expr {$::xrot + 360.0}] }
    if { $::yrot > 360.0 } { set ::yrot [expr {$::yrot - 360.0}] }
    if { $::yrot <   0.0 } { set ::yrot [expr {$::yrot + 360.0}] }

    # LAST PASS: Do The Logos
    doLogo
}

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
    }
}

# Here's Where We Do All The Drawing
proc DisplayCallback { toglwin } {

    # 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]

    if { $::bumps } {
        if { $::useMultitexture && ($::maxTexelUnits > 1) } {
            doMesh2TexelUnits
        } else {
            doMesh1TexelUnits
        }
    } else {
        doMeshNoBumps
    }
    $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 -alpha true \
                     -createcommand CreateCallback \
                     -reshapecommand ReshapeCallback \
                     -displaycommand DisplayCallback 
    listbox .fr.usage -font $::listFont -height 10
    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: NeHe's GL_ARB_multitexture & Bump Mapping Tutorial (Lesson 22)"

    # Watch For ESC Key And Quit Messages
    wm protocol . WM_DELETE_WINDOW "ExitProg"
    bind . <Key-Escape> "ExitProg"
    bind . <Key-F1>     "ToggleWindowMode"
    bind . <Key-e>      "ToggleEmboss"
    bind . <Key-m>      "ToggleMultitexture"
    bind . <Key-b>      "ToggleBumps"
    bind . <Key-f>      "ToggleFilter"
    bind . <Key-Up>     "SetXSpeed -0.01"
    bind . <Key-Down>   "SetXSpeed  0.01"
    bind . <Key-Left>   "SetYSpeed -0.01"
    bind . <Key-Right>  "SetYSpeed  0.01"
    bind . <Key-d>      "SetDepth  0.05"
    bind . <Key-i>      "SetDepth -0.05"

    bind .fr.toglwin <1> "StartAnimation"
    bind .fr.toglwin <2> "StopAnimation"
    bind .fr.toglwin <3> "StopAnimation"
    bind .fr.toglwin <Control-Button-1> "StopAnimation"

    .fr.usage insert end "Key-Escape     Exit"
    .fr.usage insert end "Key-F1         Toggle window mode"
    .fr.usage insert end "Key-e          Toggle emboss"
    .fr.usage insert end "Key-m          Toggle multitexturing"
    .fr.usage insert end "Key-b          Toggle bump maps"
    .fr.usage insert end "Key-f          Toggle filter"
    .fr.usage insert end "Key-Up|Down    Decrease|Increase x rotation speed"
    .fr.usage insert end "Key-Left|Right Decrease|Increase y rotation speed"
    .fr.usage insert end "Key-d|i        Decrease|Increase distance"
    .fr.usage insert end "Mouse-L|MR     Start|Stop animation"

    .fr.usage configure -state disabled
}

CreateWindow
PrintInfo [tcl3dOglGetInfoString]
if { [file tail [info script]] eq [file tail $::argv0] } {
    # If started directly from tclsh or wish, then start animation.
    update
    StartAnimation
}
