#-----------------------------------------------------------------------------
#           Name: ogl_material.cpp
#         Author: Kevin Harris (kevin@codesampler.com)
#  Last Modified: 04/28/05
#    Description: This sample demonstrates how to use materials with lighting 
#                 to produce different surface effects.
#
#   Control Keys: Left Mouse Button - Spin the view
#-----------------------------------------------------------------------------
#
# Original C++ code by Kevin Harris (kevin@codesampler.com)
# See www.codesampler.com for the original files
# OpenGL samples page 5: Materials
# http://www.codesampler.com/oglsrc/oglsrc_5.htm#ogl_material
#
# Modified for Tcl3D by Paul Obermeier 2008/04/28
# See www.tcl3d.org for the Tcl3D extension.

package require Tk
package require tcl3d

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

set g_WinWidth  640
set g_WinHeight 480

set g_fSpinZ(1) 0.0
set g_fSpinY(1) 0.0

set g_EnableColorMaterial    0

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

proc ToggleColorMaterial {} {
    set ::g_EnableColorMaterial [expr ! $::g_EnableColorMaterial]
    .fr.toglwin postredisplay
}

proc SetMouseInput { btn x y } {
    set ::g_LastMousePosX($btn) $x
    set ::g_LastMousePosY($btn) $y
}

proc GetMouseInput { btn x y } {
    set nXDiff [expr ($x - $::g_LastMousePosX($btn))]
    set nYDiff [expr ($y - $::g_LastMousePosY($btn))]
        
    set ::g_fSpinZ($btn) [expr $::g_fSpinZ($btn) - $nXDiff]
    set ::g_fSpinY($btn) [expr $::g_fSpinY($btn) - $nYDiff]

    set ::g_LastMousePosX($btn) $x
    set ::g_LastMousePosY($btn) $y
    .fr.toglwin postredisplay
}

proc CreateCallback { toglwin } {
    glClearColor 0.0 0.0 0.0 1.0
    glEnable GL_DEPTH_TEST
    glEnable GL_LIGHTING

    glMatrixMode GL_PROJECTION
    glLoadIdentity
    gluPerspective 45.0 [expr double($::g_WinWidth)/double($::g_WinHeight)] \
                   0.1 1000.0

    set ::g_default_diffuse  { 0.8 0.8 0.8 1.0 }
    set ::g_default_ambient  { 0.2 0.2 0.2 1.0 }
    set ::g_default_emission { 0.0 0.0 0.0 1.0 }
    set ::g_default_specular { 0.0 0.0 0.0 1.0 }
    set ::g_default_shininess 0.0

    #-------------------------------------------------------------------------
    # Setup material - Used to create a reddish clay teapot
    #-------------------------------------------------------------------------

    # A matte clay look is easy. All we really have to do is set the color to 
    # look like clay pottery.
    set ::g_redClayMtrl_diffuse { 1.0 0.5 0.2 1.0 }

    #-------------------------------------------------------------------------
    # Setup material - Used to create a green plastic teapot
    #-------------------------------------------------------------------------

    # Set the material's main color to green.
    set ::g_greenPlasticMtrl_diffuse { 0.0 1.0 0.0 1.0 }

    # Lets favor the ambient's green over the other colors. Why? I don't know.
    # It just looks better to me. Using materials is some what more artistic
    # than scientific, so just play around till you get what you want.
    set ::g_greenPlasticMtrl_ambient { 0.5 1.0 0.5 1.0 }

    # Plastic can be shiny, but we don't want it too shiny are it will look 
    # more like glass or metal. We'll have the material reflect back more 
    # green than red and blue so the highlights won't be pure white.
    set ::g_greenPlasticMtrl_specular { 0.5 1.0 0.5 1.0 }

    # It seems backwards, but increasing the shininess value reduces the 
    # highlight's size
    set ::g_greenPlasticMtrl_shininess 40.0

    #-------------------------------------------------------------------------
    # Setup material - Used to create a silver metallic teapot
    #-------------------------------------------------------------------------

    # Set the material's main color to a silver-like gray color.
    set ::g_silverMetalMtrl_diffuse { 0.5 0.5 0.5 1.0 }

    # A silver metal would be very shiny, so we'll reflect back all ambient.
    set ::g_silverMetalMtrl_ambient { 1.0 1.0 1.0 1.0 }

    # We can make it seem extra shiny by having it actually emit some light 
    # of its own... but not too much are we'll wash the color out.
    set ::g_silverMetalMtrl_emission { 0.1 0.1 0.1 1.0 }

    # Polished silver can reflect back pure white highlights, so set the 
    # specular to pure white.
    set ::g_silverMetalMtrl_specular { 1.0 1.0 1.0 1.0 }

    # Set the Power value to a small number to make the highlight's size bigger.
    set ::g_silverMetalMtrl_shininess 5.0

    #-------------------------------------------------------------------------
    # Setup a simple directional light and some ambient...
    #-------------------------------------------------------------------------

    glEnable GL_LIGHT0

    set position_light0 { -1.0 0.0 1.0 0.0 }
    set diffuse_light0  {  1.0 1.0 1.0 1.0 }
    set specular_light0 {  1.0 1.0 1.0 1.0 }
    glLightfv GL_LIGHT0 GL_POSITION $position_light0
    glLightfv GL_LIGHT0 GL_DIFFUSE  $diffuse_light0
    glLightfv GL_LIGHT0 GL_SPECULAR $specular_light0

    # Be careful when setting up ambient lighting. You can very easily wash 
    # out the material's specular highlights.

    set ambient_lightModel { 0.1 0.1 0.1 1.0 }
    glLightModelfv GL_LIGHT_MODEL_AMBIENT $ambient_lightModel
}

proc ReshapeCallback { toglwin { w -1 } { h -1 } } {
    set w [$toglwin width]
    set h [$toglwin height]

    glViewport 0 0 $w $h
    glMatrixMode GL_PROJECTION
    glLoadIdentity
    gluPerspective 45.0 [expr double($w)/double($h)] 0.1 1000.0
}

proc DisplayCallback { toglwin } {
    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]

    glMatrixMode GL_MODELVIEW
    glLoadIdentity
    glTranslatef 0.0 0.0 -6.5

    # -------------------------------------------------------------------------

    # When using a material, its possible to have a color set by glColor3f 
    # override one or more material colors by enabling GL_COLOR_MATERIAL.
    # Once that's done, you can and ID the material color or colors to 
    # override using glColorMaterial.

    # Uncomment this to use the teapot's color, which is blue, as a 
    # replacement for one or more material colors.
    if { $::g_EnableColorMaterial } {
        glEnable GL_COLOR_MATERIAL
    } else {
        glDisable GL_COLOR_MATERIAL
    }

    # Uncomment one or more of these to ID the material color or colors you 
    # would like to override?
    # glColorMaterial GL_FRONT_AND_BACK GL_AMBIENT
    # glColorMaterial GL_FRONT_AND_BACK GL_AMBIENT_AND_DIFFUSE
    # glColorMaterial GL_FRONT_AND_BACK GL_DIFFUSE
    # glColorMaterial GL_FRONT_AND_BACK GL_SPECULAR

    # -------------------------------------------------------------------------

    # Render the first teapot using a red clay material

    glMaterialfv GL_FRONT_AND_BACK GL_DIFFUSE $::g_redClayMtrl_diffuse

    glPushMatrix
        glTranslatef 0.0 1.2 0.0
        glRotatef [expr -1.0 * $::g_fSpinY(1)] 1.0 0.0 0.0
        glRotatef [expr -1.0 * $::g_fSpinZ(1)] 0.0 0.0 1.0

        # This color won't get used unless we enable GL_COLOR_MATERIAL
        glColor3f 0.0 0.0 1.0
        glutSolidTeapot 1.0
    glPopMatrix

    # Render the second teapot using a green plastic material

    glMaterialfv GL_FRONT_AND_BACK GL_DIFFUSE   $::g_greenPlasticMtrl_diffuse
    glMaterialfv GL_FRONT_AND_BACK GL_AMBIENT   $::g_greenPlasticMtrl_ambient
    glMaterialfv GL_FRONT_AND_BACK GL_SPECULAR  $::g_greenPlasticMtrl_specular
    glMaterialf  GL_FRONT_AND_BACK GL_SHININESS $::g_greenPlasticMtrl_shininess

    glPushMatrix
        glTranslatef 1.7 -0.8 0.0
        glRotatef [expr -1.0 * $::g_fSpinY(1)] 1.0 0.0 0.0
        glRotatef [expr -1.0 * $::g_fSpinZ(1)] 0.0 0.0 1.0

        # This color won't get used unless we enable GL_COLOR_MATERIAL
        glColor3f 0.0 0.0 1.0
        glutSolidTeapot 1.0
    glPopMatrix

    # Render the third teapot using a silver metallic material

    glMaterialfv GL_FRONT_AND_BACK GL_DIFFUSE   $::g_silverMetalMtrl_diffuse
    glMaterialfv GL_FRONT_AND_BACK GL_AMBIENT   $::g_silverMetalMtrl_ambient
    glMaterialfv GL_FRONT_AND_BACK GL_EMISSION  $::g_silverMetalMtrl_emission
    glMaterialfv GL_FRONT_AND_BACK GL_SPECULAR  $::g_silverMetalMtrl_specular
    glMaterialf  GL_FRONT_AND_BACK GL_SHININESS $::g_silverMetalMtrl_shininess

    glPushMatrix
        glTranslatef -1.7 -0.8 0.0
        glRotatef [expr -1.0 * $::g_fSpinY(1)] 1.0 0.0 0.0
        glRotatef [expr -1.0 * $::g_fSpinZ(1)] 0.0 0.0 1.0

        # This color won't get used unless we enable GL_COLOR_MATERIAL
        glColor3f 0.0 0.0 1.0 
        glutSolidTeapot 1.0
    glPopMatrix

    # Set all the material properties back to their default values...

    glMaterialfv GL_FRONT_AND_BACK GL_DIFFUSE   $::g_default_diffuse
    glMaterialfv GL_FRONT_AND_BACK GL_AMBIENT   $::g_default_ambient
    glMaterialfv GL_FRONT_AND_BACK GL_EMISSION  $::g_default_emission
    glMaterialfv GL_FRONT_AND_BACK GL_SPECULAR  $::g_default_specular
    glMaterialf  GL_FRONT_AND_BACK GL_SHININESS $::g_default_shininess

    $toglwin swapbuffers
}

proc Cleanup {} {
    foreach var [info globals g_*] {
        uplevel #0 unset $var
    }
}

proc ExitProg {} {
    exit
}

frame .fr
pack .fr -expand 1 -fill both
togl .fr.toglwin -width $g_WinWidth -height $g_WinHeight \
                 -double true -depth true \
                 -createcommand CreateCallback \
                 -reshapecommand ReshapeCallback \
                 -displaycommand DisplayCallback 
listbox .fr.usage -font $::g_listFont -height 3
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: CodeSampler's Material Demo"

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

bind .fr.toglwin <1>         "SetMouseInput 1 %x %y"
bind .fr.toglwin <B1-Motion> "GetMouseInput 1 %x %y"

.fr.usage insert end "Key-Escape Exit"
.fr.usage insert end "Key-c      Toggle GL_COLOR_MATERIAL"
.fr.usage insert end "Mouse-L    Rotate teapots"
.fr.usage configure -state disabled

PrintInfo [tcl3dOglGetInfoString]
