# select.tcl
#
# An example of the OpenGL red book modified to work with Tcl3D.
# The original C sources are Copyright (c) 1993-2003, Silicon Graphics, Inc.
# The Tcl3D sources are Copyright (c) 2005-2025, Paul Obermeier.
# See file LICENSE for complete license information.
#
# This is an illustration of the selection mode and 
# name stack, which detects whether objects which collide 
# with a viewing volume.  First, four triangles and a 
# rectangular box representing a viewing volume are drawn 
# (drawScene routine).  The green triangle and yellow 
# triangles appear to lie within the viewing volume, but 
# the red triangle appears to lie outside it.  Then the 
# selection mode is entered (selectObjects routine).  
# Drawing to the screen ceases.  To see if any collisions 
# occur, the four triangles are called.  In this example, 
# the green triangle causes one hit with the name 1, and 
# the yellow triangles cause one hit with the name 3.

package require tcl3d

tcl3dConsoleCreate .tcl3dOutputConsole "# " "Select Output"

set BUFSIZE 512
set selectBuffer [tcl3dVector GLuint $::BUFSIZE]

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

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

# draw a triangle with vertices at (x1, y1), (x2, y2) 
# and (x3, y3) at z units away from the origin.

proc drawTriangle { x1 y1 x2 y2 x3 y3 z } {
    glBegin GL_TRIANGLES
    glVertex3f $x1 $y1 $z
    glVertex3f $x2 $y2 $z
    glVertex3f $x3 $y3 $z
    glEnd
}

# draw a rectangular box with these outer x, y, and z values
proc drawViewVolume { x1 x2 y1 y2 z1 z2 } {
    set negz1 [expr -1.0 * $z1]
    set negz2 [expr -1.0 * $z2]

    glColor3f 1.0 1.0 1.0
    glBegin GL_LINE_LOOP
    glVertex3f $x1 $y1 $negz1
    glVertex3f $x2 $y1 $negz1
    glVertex3f $x2 $y2 $negz1
    glVertex3f $x1 $y2 $negz1
    glEnd

    glBegin GL_LINE_LOOP
    glVertex3f $x1 $y1 $negz2
    glVertex3f $x2 $y1 $negz2
    glVertex3f $x2 $y2 $negz2
    glVertex3f $x1 $y2 $negz2
    glEnd

    glBegin GL_LINES ; # 4 lines
    glVertex3f $x1 $y1 $negz1
    glVertex3f $x1 $y1 $negz2
    glVertex3f $x1 $y2 $negz1
    glVertex3f $x1 $y2 $negz2
    glVertex3f $x2 $y1 $negz1
    glVertex3f $x2 $y1 $negz2
    glVertex3f $x2 $y2 $negz1
    glVertex3f $x2 $y2 $negz2
    glEnd
}

# drawScene draws 4 triangles and a wire frame
# which represents the viewing volume.
#/
proc drawScene {} {
    glMatrixMode GL_PROJECTION
    glLoadIdentity
    gluPerspective 40.0 [expr 4.0/3.0] 1.0 100.0

    glMatrixMode GL_MODELVIEW
    glLoadIdentity
    gluLookAt 7.5 7.5 12.5 2.5 2.5 -5.0 0.0 1.0 0.0
    glColor3f 0.0 1.0 0.0 ;     # green triangle
    drawTriangle 2.0 2.0 3.0 2.0 2.5 3.0 -5.0
    glColor3f 1.0 0.0 0.0 ;     # red triangle
    drawTriangle 2.0 7.0 3.0 7.0 2.5 8.0 -5.0
    glColor3f 1.0 1.0 0.0 ;     # yellow triangles
    drawTriangle 2.0 2.0 3.0 2.0 2.5 3.0 0.0
    drawTriangle 2.0 2.0 3.0 2.0 2.5 3.0 -10.0
    drawViewVolume 0.0 5.0 0.0 5.0 0.0 10.0
}

# processHits prints out the contents of the selection array
#
proc processHits { hits } {
    set count 0
    puts  "hits = $hits"
    for { set i 0 } { $i < $hits } { incr i } {
        set names [$::selectBuffer get $count]
        puts " number of names for hit = $names"
        incr count
        puts -nonewline [format "  z1 is %g;"  \
               [expr double ([$::selectBuffer get $count]) / 0x7fffffff]]
        incr count
        puts [format " z2 is %g" \
               [expr double ([$::selectBuffer get $count]) / 0x7fffffff]]
        incr count
        puts -nonewline "   the name is "
        for { set j 0 } { $j < $names } { incr j } {
            puts -nonewline [format "%d " [$::selectBuffer get $count]]
            incr count
        }
        puts ""
    }
}

# selectObjects "draws" the triangles in selection mode, 
# assigning names for the triangles.  Note that the third
# and fourth triangles share one name, so that if either 
# or both triangles intersects the viewing/clipping volume, 
# only one hit will be registered.
#/
proc selectObjects {} {
    glSelectBuffer $::BUFSIZE $::selectBuffer
    glRenderMode GL_SELECT

    glInitNames
    glPushName 0

    glPushMatrix
    glMatrixMode GL_PROJECTION
    glLoadIdentity
    glOrtho 0.0 5.0 0.0 5.0 0.0 10.0
    glMatrixMode GL_MODELVIEW
    glLoadIdentity
    glLoadName 1
    drawTriangle 2.0 2.0 3.0 2.0 2.5 3.0 -5.0
    glLoadName 2
    drawTriangle 2.0 7.0 3.0 7.0 2.5 8.0 -5.0
    glLoadName 3
    drawTriangle 2.0 2.0 3.0 2.0 2.5 3.0 0.0
    drawTriangle 2.0 2.0 3.0 2.0 2.5 3.0 -10.0
    glPopMatrix
    glFlush
 
    set hits [glRenderMode GL_RENDER]
    processHits $hits
} 

proc CreateCallback { toglwin } {
    glEnable GL_DEPTH_TEST
    glShadeModel GL_FLAT
}


proc ReshapeCallback { toglwin { w -1 } { h -1 } } {
}

proc DisplayCallback { toglwin } {
    glClearColor 0.0 0.0 0.0 0.0
    glClear [expr $::GL_COLOR_BUFFER_BIT | $::GL_DEPTH_BUFFER_BIT]
    drawScene
    selectObjects
    glFlush
    $toglwin swapbuffers
}

frame .fr
pack .fr -expand 1 -fill both
togl .fr.toglwin -width 400 -height 400 -double true -depth true \
                 -createcommand CreateCallback \
                 -reshapecommand ReshapeCallback \
                 -displaycommand DisplayCallback 
listbox .fr.usage -font $::listFont -height 1
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: OpenGL Red Book example select"

bind . <Key-Escape> "exit"

.fr.usage insert end "Key-Escape Exit"
.fr.usage configure -state disabled

PrintInfo [tcl3dOglGetInfoString]
