# Tutorial OpenGL Transformation
# Original C++ code by Song Ho Ahn (song.ahn@gmail.com)
# See www.songho.ca/opengl/gl_transform.html for the original files
#
# Modified for Tcl3D by Paul Obermeier 2009/09/13
# See www.tcl3d.org for the Tcl3D extension.

package require Tk
package require tcl3d

tcl3dAddEvents

array set gState {
    fovY      60.0
    nearPlane  1.0
    farPlane 100.0
    windowWidth  200
    windowHeight 400
    drawModeChanged   false
    drawMode 0
    cameraAngleX   45.0
    cameraAngleY  -45.0
    cameraDistance 25.0
    view,pos,x    0
    view,pos,y    0
    view,pos,z    0
    view,angle,x  0
    view,angle,y  0
    view,angle,z  0
    model,pos,x   0
    model,pos,y   0
    model,pos,z   0
    model,angle,x 0
    model,angle,y 0
    model,angle,z 0
    bgColor  { 0.0 0.0 0.0 0.0 }
    textFont {-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
    }
}

# Set the camera position and rotation
proc SetViewMatrix { x y z pitch heading roll } {
    global gState

    set gState(view,pos,x) $x
    set gState(view,pos,y) $y
    set gState(view,pos,z) $z
    set gState(view,angle,x) $pitch
    set gState(view,angle,y) $heading
    set gState(view,angle,z) $roll
}

# Set the object position and rotation
proc SetModelMatrix { x y z rx ry rz } {
    global gState

    set gState(model,pos,x) $x
    set gState(model,pos,y) $y
    set gState(model,pos,z) $z
    set gState(model,angle,x) $rx
    set gState(model,angle,y) $ry
    set gState(model,angle,z) $rz
}

proc SetLeftMouse { x y } {
    global gState

    set gState(mouseX) $x
    set gState(mouseY) $y
}

proc RotateCamera { toglwin x y } {
    global gState

    set diffX [expr {$x - $gState(mouseX)}]
    set diffY [expr {$y - $gState(mouseY)}]
    set gState(cameraAngleY) [expr {$gState(cameraAngleY) + $diffX}]
    set gState(cameraAngleX) [expr {$gState(cameraAngleX) + $diffY}]
    set gState(mouseX) $x
    set gState(mouseY) $y
    $toglwin postredisplay
}

proc SetRightMouse { x y } {
    global gState

    set gState(mouseY) $y
}

proc ZoomCamera { toglwin x y } {
    global gState

    set diffY [expr {($y - $gState(mouseY)) * 0.05}]
    set gState(cameraDistance) [expr {$gState(cameraDistance) + $diffY}]
    set gState(mouseY) $y
    $toglwin postredisplay
}

proc ToggleDrawMode {} {
    global gState

    incr gState(drawMode)
    if { $gState(drawMode) > 2 } {
        set gState(drawMode) 0
    }
    set gState(drawModeChanged) true
    DisplayCallback .fr.toglwin
    DisplayCallback .fr.toglwin
}

# Configure projection and viewport of sub window
proc SetViewportSub { x y w h nearPlane farPlane } {
    global gState

    # set viewport
    glViewport $x $y $w $h
    glScissor  $x $y $w $h

    # set perspective viewing frustum
    glMatrixMode GL_PROJECTION
    glLoadIdentity
    # FOV, AspectRatio, NearClip, FarClip
    gluPerspective $gState(fovY) [expr {double($w)/double($h)}] \
                   $nearPlane $farPlane

    # switch to modelview matrix in order to set scene
    glMatrixMode GL_MODELVIEW
    glLoadIdentity
}

proc DrawCamera {} {
    set shininess 32.0
    set diffuseColor  {1.0 1.0 1.0}
    set specularColor {1.0 1.0 1.0 1.0}

    # set specular and shiniess using glMaterial
    glMaterialf  GL_FRONT_AND_BACK GL_SHININESS $shininess   ; # range 0 ~ 128
    glMaterialfv GL_FRONT_AND_BACK GL_SPECULAR $specularColor

    # set ambient and diffuse color using glColorMaterial (gold-yellow)
    glColorMaterial GL_FRONT_AND_BACK GL_AMBIENT_AND_DIFFUSE
    glColor3fv $diffuseColor

    tcl3dCameraModel
}

proc DrawTeapot {} {
    set shininess 15.0
    set diffuseColor  {0.929524 0.796542 0.178823 }
    set specularColor {1.00000  0.980392 0.549020 1.0 }

    # set specular and shiniess using glMaterial (gold-yellow)
    glMaterialf  GL_FRONT_AND_BACK GL_SHININESS $shininess ; # range 0 ~ 128
    glMaterialfv GL_FRONT_AND_BACK GL_SPECULAR  $specularColor

    # set ambient and diffuse color using glColorMaterial (gold-yellow)
    glColorMaterial GL_FRONT_AND_BACK GL_AMBIENT_AND_DIFFUSE
    glColor3fv $diffuseColor

    tcl3dTeapotModel
}

# Draw a grid on the xz plane
proc DrawGrid { size step } {
    glDisable GL_LIGHTING

    glBegin GL_LINES
        glColor3f 0.3 0.3 0.3
        for { set i $step } { $i <= $size } { set i [expr {$i + $step}] } {
            glVertex3f -$size 0  $i ;   # lines parallel to X-axis
            glVertex3f  $size 0  $i
            glVertex3f -$size 0 -$i ;   # lines parallel to X-axis
            glVertex3f  $size 0 -$i

            glVertex3f  $i 0 -$size ;   # lines parallel to Z-axis
            glVertex3f  $i 0  $size
            glVertex3f -$i 0 -$size ;   # lines parallel to Z-axis
            glVertex3f -$i 0  $size
        }

        # x-axis
        glColor3f 0.5 0 0
        glVertex3f -$size 0 0
        glVertex3f  $size 0 0

        # z-axis
        glColor3f 0 0 0.5
        glVertex3f 0 0 -$size
        glVertex3f 0 0  $size
    glEnd

    # enable lighting back
    glEnable GL_LIGHTING
}

# Draw the local axis of an object
proc DrawAxis { size } {
    glDepthFunc GL_ALWAYS    ; # to avoid visual artifacts with grid lines
    glDisable   GL_LIGHTING

    # draw axis
    glLineWidth 3
    glBegin GL_LINES
        glColor3f 1 0 0
        glVertex3f 0 0 0
        glVertex3f $size 0 0
        glColor3f 0 1 0
        glVertex3f 0 0 0
        glVertex3f 0 $size 0
        glColor3f 0 0 1
        glVertex3f 0 0 0
        glVertex3f 0 0 $size
    glEnd
    glLineWidth 1

    # draw arrows (actually big square dots)
    glPointSize 5
    glBegin GL_POINTS
        glColor3f  1 0 0
        glVertex3f $size 0 0
        glColor3f  0 1 0
        glVertex3f 0 $size 0
        glColor3f  0 0 1
        glVertex3f 0 0 $size
    glEnd
    glPointSize 1

    # restore default settings
    glEnable    GL_LIGHTING
    glDepthFunc GL_LEQUAL
}

# Draw frustum
proc DrawFrustum { fovY aspectRatio nearPlane farPlane } {
    set tangent    [expr {tan ([tcl3dDegToRad [expr {$fovY/2}]])}]
    set nearHeight [expr {$nearPlane * $tangent}]
    set nearWidth  [expr {$nearHeight * $aspectRatio}]
    set farHeight  [expr {$farPlane * $tangent}]
    set farWidth   [expr {$farHeight * $aspectRatio}]

    # compute 8 vertices of the frustum

    # near top right
    set vertices(0) [list  $nearWidth  $nearHeight -$nearPlane]
    # near top left
    set vertices(1) [list -$nearWidth  $nearHeight -$nearPlane]
    # near bottom left
    set vertices(2) [list -$nearWidth -$nearHeight -$nearPlane]
    # near bottom right
    set vertices(3) [list  $nearWidth -$nearHeight -$nearPlane]
    # far top right
    set vertices(4) [list  $farWidth   $farHeight  -$farPlane]
    # far top left
    set vertices(5) [list -$farWidth   $farHeight  -$farPlane]
    # far bottom left
    set vertices(6) [list -$farWidth  -$farHeight  -$farPlane]
    # far bottom right
    set vertices(7) [list  $farWidth  -$farHeight  -$farPlane]

    set colorLine1 { 0.7 0.7 0.7 0.7 }
    set colorLine2 { 0.2 0.2 0.2 0.7 }
    set colorPlane { 0.5 0.5 0.5 0.5 }

    glDisable GL_LIGHTING
    glDisable GL_CULL_FACE
    glBlendFunc GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA

    glBegin GL_LINES
        glColor4fv $colorLine2
        glVertex3f 0 0 0
        glColor4fv $colorLine1
        glVertex3fv $vertices(4)

        glColor4fv $colorLine2
        glVertex3f 0 0 0
        glColor4fv $colorLine1
        glVertex3fv $vertices(5)

        glColor4fv $colorLine2
        glVertex3f 0 0 0
        glColor4fv $colorLine1
        glVertex3fv $vertices(6)

        glColor4fv $colorLine2
        glVertex3f 0 0 0
        glColor4fv $colorLine1
        glVertex3fv $vertices(7)
    glEnd

    glColor4fv $colorLine1
    glBegin GL_LINE_LOOP
        glVertex3fv $vertices(4)
        glVertex3fv $vertices(5)
        glVertex3fv $vertices(6)
        glVertex3fv $vertices(7)
    glEnd

    glColor4fv $colorLine1
    glBegin GL_LINE_LOOP
        glVertex3fv $vertices(0)
        glVertex3fv $vertices(1)
        glVertex3fv $vertices(2)
        glVertex3fv $vertices(3)
    glEnd

    glColor4fv $colorPlane
    glBegin GL_QUADS
        glVertex3fv $vertices(0)
        glVertex3fv $vertices(1)
        glVertex3fv $vertices(2)
        glVertex3fv $vertices(3)
        glVertex3fv $vertices(4)
        glVertex3fv $vertices(5)
        glVertex3fv $vertices(6)
        glVertex3fv $vertices(7)
    glEnd

    glEnable GL_CULL_FACE
    glEnable GL_LIGHTING
}

# Draw upper window (view from the camera)
proc DrawSub1 {} {
    global gState

    # set upper viewport
    SetViewportSub 0 [expr {$gState(windowHeight)/2}] $gState(windowWidth) \
                     [expr {$gState(windowHeight)/2}] 1 10

    # clear buffer
    glClearColor 0.1 0.1 0.1 1
    glClear [expr $::GL_COLOR_BUFFER_BIT | \
                  $::GL_DEPTH_BUFFER_BIT | \
                  $::GL_STENCIL_BUFFER_BIT]

    # initialize ModelView matrix
    glPushMatrix
    glLoadIdentity

    # ModelView matrix is product of viewing matrix and modeling matrix
    # ModelView_M = View_M * Model_M
    # First, transform the camera (viewing matrix) from world space to eye space
    # Notice all values are negated, because we move the whole scene with the
    # inverse of camera transform
    glRotatef [expr {-1.0 * $gState(view,angle,x)}] 1 0 0 ; # pitch
    glRotatef [expr {-1.0 * $gState(view,angle,y)}] 0 1 0 ; # heading
    glRotatef [expr {-1.0 * $gState(view,angle,z)}] 0 0 1 ; # roll
    glTranslatef [expr {-1.0 * $gState(view,pos,x)}] \
                 [expr {-1.0 * $gState(view,pos,y)}] \
                 [expr {-1.0 * $gState(view,pos,z)}]

    # we have set viewing matrix up to this point. 
    # (Matrix from world space to eye space)
    # save the view matrix only
    set gState(matrixView) [tcl3dOglGetFloatState GL_MODELVIEW_MATRIX 16]

    # always draw the grid at the origin (before any modeling transform)
    DrawGrid 10 1

    # In order to get the modeling matrix only, reset GL_MODELVIEW matrix
    glLoadIdentity

    # transform the object
    # From now, all transform will be for modeling matrix only.
    # (transform from object space to world space)
    glTranslatef $gState(model,pos,x) $gState(model,pos,y) $gState(model,pos,z)
    glRotatef $gState(model,angle,x) 1 0 0
    glRotatef $gState(model,angle,y) 0 1 0
    glRotatef $gState(model,angle,z) 0 0 1

    # save modeling matrix
    set gState(matrixModel) [tcl3dOglGetFloatState GL_MODELVIEW_MATRIX 16]

    # restore GL_MODELVIEW matrix by multiplying matrixView and matrixModel
    # before drawing the object
    # ModelView_M = View_M * Model_M
    glLoadMatrixf $gState(matrixView)  ; # Mmv = Mv
    glMultMatrixf $gState(matrixModel) ; # Mmv *= Mm

    # save ModelView matrix
    set gState(matrixModelView) [tcl3dOglGetFloatState GL_MODELVIEW_MATRIX 16]

    # draw a teapot after ModelView transform
    # v' = Mmv * v
    DrawAxis 4
    DrawTeapot

    glPopMatrix
}

# Draw bottom window (3rd person view)
proc DrawSub2 {} {
    global gState

    # set bottom viewport
    SetViewportSub 0 0 $gState(windowWidth) [expr {$gState(windowHeight)/2}] \
                   $gState(nearPlane) $gState(farPlane)

    # clear buffer
    glClearColor [lindex $gState(bgColor) 0] [lindex $gState(bgColor) 1] \
                 [lindex $gState(bgColor) 2] [lindex $gState(bgColor) 3]
    glClear [expr $::GL_COLOR_BUFFER_BIT | \
                  $::GL_DEPTH_BUFFER_BIT | \
                  $::GL_STENCIL_BUFFER_BIT]

    glPushMatrix

    # First, transform the camera (viewing matrix) from world space to eye space
    glTranslatef 0 0 [expr {-1.0 * $gState(cameraDistance)}]
    glRotatef $gState(cameraAngleX) 1 0 0 ; # pitch
    glRotatef $gState(cameraAngleY) 0 1 0 ; # heading

    # draw grid
    DrawGrid 10 1

    # draw a teapot
    glPushMatrix
    glTranslatef $gState(model,pos,x) $gState(model,pos,y) $gState(model,pos,z)
    glRotatef $gState(model,angle,x) 1 0 0
    glRotatef $gState(model,angle,y) 0 1 0
    glRotatef $gState(model,angle,z) 0 0 1
    DrawAxis 4
    DrawTeapot
    glPopMatrix

    # draw the camera
    glPushMatrix
    glTranslatef $gState(view,pos,x) $gState(view,pos,y) $gState(view,pos,z)
    glRotatef $gState(view,angle,x) 1 0 0
    glRotatef $gState(view,angle,y) 0 1 0
    glRotatef $gState(view,angle,z) 0 0 1
    DrawCamera
    DrawFrustum $gState(fovY) 1 1 10
    glPopMatrix

    glPopMatrix
}

proc InitLights {} {
    # set up light colors (ambient, diffuse, specular)
    set lightKa {0.0 0.0 0.0 1.0} ; # ambient light
    set lightKd {0.9 0.9 0.9 1.0} ; # diffuse light
    set lightKs {1 1 1 1}         ; # specular light
    glLightfv GL_LIGHT0 GL_AMBIENT  $lightKa
    glLightfv GL_LIGHT0 GL_DIFFUSE  $lightKd
    glLightfv GL_LIGHT0 GL_SPECULAR $lightKs

    # position the light
    set lightPos {0 5 5 0}
    glLightfv GL_LIGHT0 GL_POSITION $lightPos

    glEnable GL_LIGHT0 ; # MUST enable each light source after configuration
}

# Initialize OpenGL states and scene
proc CreateCallback { toglwin } {
    global gState

    glShadeModel GL_SMOOTH              ; # shading mathod: GL_SMOOTH or GL_FLAT
    glPixelStorei GL_UNPACK_ALIGNMENT 4 ; # 4-byte pixel alignment

    # enable /disable features
    glHint GL_PERSPECTIVE_CORRECTION_HINT GL_NICEST
    glEnable GL_DEPTH_TEST
    glEnable GL_LIGHTING
    glEnable GL_TEXTURE_2D
    glEnable GL_CULL_FACE
    glEnable GL_BLEND
    glEnable GL_SCISSOR_TEST

    # track material ambient and diffuse from surface color,
    # call it before glEnable(GL_COLOR_MATERIAL)
    glColorMaterial GL_FRONT_AND_BACK GL_AMBIENT_AND_DIFFUSE
    glEnable GL_COLOR_MATERIAL

    glClearColor [lindex $gState(bgColor) 0] [lindex $gState(bgColor) 1] \
                 [lindex $gState(bgColor) 2] [lindex $gState(bgColor) 3]
    glClearStencil 0
    glClearDepth 1.0  ; # 0 is near, 1 is far
    glDepthFunc GL_LEQUAL

    InitLights

    SetViewMatrix 0 0 10 0 0 0
    set gState(matrixView)      [tcl3dOglGetFloatState GL_MODELVIEW_MATRIX 16]
    set gState(matrixModel)     [tcl3dOglGetFloatState GL_MODELVIEW_MATRIX 16]
    set gState(matrixModelView) [tcl3dOglGetFloatState GL_MODELVIEW_MATRIX 16]
}

# Set rendering window size
proc ReshapeCallback { toglwin { w -1 } { h -1 } } {
    global gState

    set w [$toglwin width]
    set h [$toglwin height]

    # assign the width/height of viewport
    set gState(windowWidth)  $w
    set gState(windowHeight) $h
}

# draw 2D/3D scene
proc DisplayCallback { toglwin } {
    global gState

    DrawSub1
    DrawSub2

    if { $gState(drawModeChanged) } {
        if { $gState(drawMode) == 0 } {
            # fill mode
            glPolygonMode GL_FRONT_AND_BACK GL_FILL
            glEnable GL_DEPTH_TEST
            glEnable GL_CULL_FACE
        } elseif { $gState(drawMode) == 1 } {
            # wireframe mode
            glPolygonMode GL_FRONT_AND_BACK GL_LINE
            glDisable GL_DEPTH_TEST
            glDisable GL_CULL_FACE
        } elseif { $gState(drawMode) == 2 } {
            # point mode
            glPolygonMode GL_FRONT_AND_BACK GL_POINT
            glDisable GL_DEPTH_TEST
            glDisable GL_CULL_FACE
        }
        set gState(drawModeChanged) false
    }
    glFinish
    $toglwin swapbuffers
}

# Transpose then return matrix values
proc GetViewMatrix {} {
    global gState

    for { set i 0 } { $i < 16 } { incr i } {
        lappend m 0.0
    }
    lset m 0  [lindex $gState(matrixView)  0]
    lset m 1  [lindex $gState(matrixView)  4]
    lset m 2  [lindex $gState(matrixView)  8]
    lset m 3  [lindex $gState(matrixView) 12]
    lset m 4  [lindex $gState(matrixView)  1]
    lset m 5  [lindex $gState(matrixView)  5]
    lset m 6  [lindex $gState(matrixView)  9]
    lset m 7  [lindex $gState(matrixView) 13]
    lset m 8  [lindex $gState(matrixView)  2]
    lset m 9  [lindex $gState(matrixView)  6]
    lset m 10 [lindex $gState(matrixView) 10]
    lset m 11 [lindex $gState(matrixView) 14]
    lset m 12 [lindex $gState(matrixView)  3]
    lset m 13 [lindex $gState(matrixView)  7]
    lset m 14 [lindex $gState(matrixView) 11]
    lset m 15 [lindex $gState(matrixView) 15]
    return $m
}

proc GetModelMatrix {} {
    global gState

    for { set i 0 } { $i < 16 } { incr i } {
        lappend m 0.0
    }
    lset m 0  [lindex $gState(matrixModel)  0]
    lset m 1  [lindex $gState(matrixModel)  4]
    lset m 2  [lindex $gState(matrixModel)  8]
    lset m 3  [lindex $gState(matrixModel) 12]
    lset m 4  [lindex $gState(matrixModel)  1]
    lset m 5  [lindex $gState(matrixModel)  5]
    lset m 6  [lindex $gState(matrixModel)  9]
    lset m 7  [lindex $gState(matrixModel) 13]
    lset m 8  [lindex $gState(matrixModel)  2]
    lset m 9  [lindex $gState(matrixModel)  6]
    lset m 10 [lindex $gState(matrixModel) 10]
    lset m 11 [lindex $gState(matrixModel) 14]
    lset m 12 [lindex $gState(matrixModel)  3]
    lset m 13 [lindex $gState(matrixModel)  7]
    lset m 14 [lindex $gState(matrixModel) 11]
    lset m 15 [lindex $gState(matrixModel) 15]
    return $m
}

proc GetModelViewMatrix {} {
    global gState

    for { set i 0 } { $i < 16 } { incr i } {
        lappend m 0.0
    }
    lset m 0  [lindex $gState(matrixModelView)  0]
    lset m 1  [lindex $gState(matrixModelView)  4]
    lset m 2  [lindex $gState(matrixModelView)  8]
    lset m 3  [lindex $gState(matrixModelView) 12]
    lset m 4  [lindex $gState(matrixModelView)  1]
    lset m 5  [lindex $gState(matrixModelView)  5]
    lset m 6  [lindex $gState(matrixModelView)  9]
    lset m 7  [lindex $gState(matrixModelView) 13]
    lset m 8  [lindex $gState(matrixModelView)  2]
    lset m 9  [lindex $gState(matrixModelView)  6]
    lset m 10 [lindex $gState(matrixModelView) 10]
    lset m 11 [lindex $gState(matrixModelView) 14]
    lset m 12 [lindex $gState(matrixModelView)  3]
    lset m 13 [lindex $gState(matrixModelView)  7]
    lset m 14 [lindex $gState(matrixModelView) 11]
    lset m 15 [lindex $gState(matrixModelView) 15]
    return $m
}

proc ResetMatrix { type } {
    if { $type eq "model" } {
        SetModelMatrix 0 0 0 0 0 0
    } else {
        SetViewMatrix 0 0 10 0 0 0
    }
    Update
}

#
# GUI specific part
#

proc CreateMatrixWidget { widget name { numRows 4 } { numCols 4 } } {
    labelframe $widget -text $name -labelanchor n -foreground blue \
                       -padx 1 -pady 3
    for { set row 0 } { $row < $numRows } { incr row } {
        for { set col 0 } { $col < $numCols } { incr col } {
            label $widget.l_${row}_${col} -text "$row.$col" -anchor e -width 5
            grid $widget.l_${row}_${col} -row $row -column $col -sticky nse
        }
    }
    return $widget
}

proc SetMatrixWidgetValue { widget row col value } {
    $widget.l_${row}_${col} configure -text [format "%4.2f" $value]
}

proc GetMatrixWidgetValue { widget row col } {
    return [$widget.l_${row}_${col} cget -text]
}

proc SetMatrixWidget { widget matList { numRows 4 } { numCols 4 } } {
    set count 0
    for { set row 0 } { $row < $numRows } { incr row } {
        for { set col 0 } { $col < $numCols } { incr col } {
            SetMatrixWidgetValue $widget $row $col [lindex $matList $count]
            incr count
        }
    }
}

proc CreateModelViewFrame { parent } {
    set matMV $parent.matMV
    set matV  $parent.matV
    set matM  $parent.matM

    set numRows 4
    set numCols 4

    frame $parent.frEqual
    label $parent.frEqual.l -text "="
    pack  $parent.frEqual.l

    frame $parent.frOp
    label $parent.frOp.l -text "X"
    pack  $parent.frOp.l

    CreateMatrixWidget $matMV "ModelView Matrix" $numRows $numCols
    CreateMatrixWidget $matV  "View Matrix"      $numRows $numCols
    CreateMatrixWidget $matM  "Model Matrix"     $numRows $numCols
    pack $matMV          -side left
    pack $parent.frEqual -side left
    pack $matV           -side left
    pack $parent.frOp    -side left
    pack $matM           -side left
    return [list $matMV $matV $matM]
}

proc UpdateGLCmds {} {
    global gState

    set widget $gState(viewGLTextWidget)
    $widget delete 1.0 end
    $widget insert end [format "glRotatef %4.0f 1 0 0\n" \
                                [expr {-1.0 * $gState(view,angle,x)}]]
    $widget insert end [format "glRotatef %4.0f 0 1 0\n" \
                                [expr {-1.0 * $gState(view,angle,y)}]]
    $widget insert end [format "glRotatef %4.0f 0 0 1\n" \
                                [expr {-1.0 * $gState(view,angle,z)}]]
    $widget insert end [format "glTranslatef %3.0f %3.0f %3.0f\n" \
                                [expr {-1.0 * $gState(view,pos,x)}] \
                                [expr {-1.0 * $gState(view,pos,y)}] \
                                [expr {-1.0 * $gState(view,pos,z)}]]

    set widget $gState(modelGLTextWidget)
    $widget delete 1.0 end
    $widget insert end [format "glTranslatef %3.0f %3.0f %3.0f\n" \
                                $gState(model,pos,x) \
                                $gState(model,pos,y) \
                                $gState(model,pos,z)]
    $widget insert end [format "glRotatef %4.0f 1 0 0\n" $gState(model,angle,x)]
    $widget insert end [format "glRotatef %4.0f 0 1 0\n" $gState(model,angle,y)]
    $widget insert end [format "glRotatef %4.0f 0 0 1\n" $gState(model,angle,z)]

    SetMatrixWidget $gState(matV)  [GetViewMatrix]
    SetMatrixWidget $gState(matM)  [GetModelMatrix]
    SetMatrixWidget $gState(matMV) [GetModelViewMatrix]
}

proc Update { args } {
    global gState

    DisplayCallback .fr.toglwin
    UpdateGLCmds
}

proc Cleanup {} {
    uplevel #0 unset gState
}

proc ExitProg {} {
    exit
}

frame .fr
pack  .fr -expand 1 -fill both

togl .fr.toglwin \
     -width $gState(windowWidth) -height $gState(windowHeight) \
     -double true -depth true -stencil true \
     -createcommand CreateCallback \
     -reshapecommand ReshapeCallback \
     -displaycommand DisplayCallback
frame .fr.guiFr
label .fr.info

grid .fr.toglwin -row 0 -column 0 -sticky news
grid .fr.guiFr   -row 0 -column 1 -sticky news
grid .fr.info    -row 1 -column 0 -sticky news -columnspan 2
grid rowconfigure .fr 0 -weight 1
grid columnconfigure .fr 0 -weight 1

frame .fr.guiFr.topFr
frame .fr.guiFr.frMatrix
pack .fr.guiFr.topFr .fr.guiFr.frMatrix -side top -padx 2

set viewFr  .fr.guiFr.topFr.frView
set modelFr .fr.guiFr.topFr.frModel
labelframe $viewFr  -text "View (Camera)" -foreground blue
labelframe $modelFr -text "Model"         -foreground blue
pack $viewFr $modelFr -side left -expand 1 -fill x

button $viewFr.reset -command "ResetMatrix view" -text "Reset"
grid $viewFr.reset -row 0 -column 0 -sticky news -columnspan 3
set row 1
foreach from { -10 -10 -10 -360 -360 -360 } \
        to   {  10  10  10  360  360  360 } \
        type {  pos,x  pos,y  pos,z  angle,x  angle,y  angle,z } \
        msg  { "Pos (X)" "Pos (Y)" "Pos (Z)" "Pitch (X)" "Heading (Y)" "Roll (Z)" } {
    label $viewFr.l_$row -text $msg
    label $viewFr.v_$row -textvariable gState(view,$type) -width 4 -anchor e
    scale $viewFr.s_$row -from $from -to $to -resolution 1 -orient horizontal \
          -variable gState(view,$type) -showvalue false -command Update
    grid $viewFr.l_$row -row $row -column 0 -sticky sw
    grid $viewFr.v_$row -row $row -column 1 -sticky sw
    grid $viewFr.s_$row -row $row -column 2 -sticky news
    incr row
}
text $viewFr.cmds -height 4 -width 25 -font $gState(textFont)
grid $viewFr.cmds -row $row -column 0 -sticky news -columnspan 3
set gState(viewGLTextWidget) $viewFr.cmds

button $modelFr.reset -command "ResetMatrix model" -text "Reset"
grid $modelFr.reset -row 0 -column 0 -sticky news -columnspan 3
set row 1
foreach from { -10 -10 -10 -360 -360 -360 } \
        to   {  10  10  10  360  360  360 } \
        type {  pos,x  pos,y  pos,z  angle,x  angle,y  angle,z } \
        msg  { "Pos (X)" "Pos (Y)" "Pos (Z)" "Rotation (X)" "Rotation (Y)" "Rotation (Z)" } {
    label $modelFr.l_$row -text $msg
    label $modelFr.v_$row -textvariable gState(model,$type) -width 4 -anchor e
    scale $modelFr.s_$row -from $from -to $to -resolution 1 -orient horizontal \
          -variable gState(model,$type) -showvalue false -command Update
    grid $modelFr.l_$row -row $row -column 0 -sticky sw
    grid $modelFr.v_$row -row $row -column 1 -sticky sw
    grid $modelFr.s_$row -row $row -column 2 -sticky news
    incr row
}
text $modelFr.cmds -height 4 -width 25 -font $gState(textFont)
grid $modelFr.cmds -row $row -column 0 -sticky news -columnspan 3
set gState(modelGLTextWidget) $modelFr.cmds

set widgetList [CreateModelViewFrame .fr.guiFr.frMatrix]
foreach { gState(matMV) gState(matV) gState(matM) } $widgetList break

set appTitle "Tcl3D demo: Song Ho Ahn's ModelView Matrix Tutorial"
wm title . $appTitle

# Watch For ESC Key And Quit Messages
wm protocol . WM_DELETE_WINDOW "ExitProg"
bind . <Key-Escape> "ExitProg"
bind . <Key-w>      "ToggleDrawMode"

bind .fr.toglwin <<LeftMousePress>>   "SetLeftMouse %x %y"
bind .fr.toglwin <<LeftMouseMotion>>  "RotateCamera .fr.toglwin %x %y"
bind .fr.toglwin <<RightMousePress>>  "SetRightMouse %x %y"
bind .fr.toglwin <<RightMouseMotion>> "ZoomCamera .fr.toglwin %x %y"

PrintInfo [tcl3dOglGetInfoString]
