package require Tk
package require tcl3d
package require tile
package require tablelist

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

set g_WinWidth  400
set g_WinHeight 320

set TrackManip  0
set FlightManip 1
set DriveManip  2

set gOpts(showTreeview)     1
set gOpts(continuousUpdate) 0
set gOpts(lighting)         1
set gOpts(smoothshade)      1
set gOpts(texture)          1
set gOpts(displaylists)     1
set gOpts(polymode) $::osg::PolygonMode_FILL
set gOpts(clearcolor) [list 0.2 0.2 0.4]
set gOpts(manipMode) $TrackManip

set gOpts(lastDir) [pwd]
set gOpts(appName) "Tcl3D OSG Viewer"

# OPA TODO To be inserted into Tcl3D standard library.
proc tcl3dCreateScrolledTreeview { wid titleStr args } {
    return [eval {tcl3dCreateScrolledWidget ttk::treeview $wid $titleStr} $args ]
}

# 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 AddMenuCmd { menu label acc cmd args } {
    eval {$menu add command -label $label -accelerator $acc -command $cmd} $args
}

proc AddMenuCheck { menu label acc var cmd args } {
    eval {$menu add checkbutton -label $label -accelerator $acc \
                                -variable $var -command $cmd} $args
}

proc UpdateTitleString { addMsg } {
    set msg "Tcl3D OpenSceneGraph Viewer"
    append msg [format " (%s)" $addMsg]
    wm title . $msg
}

proc CreateWidgets {} {
    global gOpts gWidgets

    InitScene

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

    set osgwin [tcl3dOsgGetOsgWin]

    set panewin .fr.fr.pane
    set toglwin .fr.fr.pane.toglwin
    set gWidgets(toglwin) $toglwin

    ttk::frame .fr.fr
    ttk::panedwindow $panewin -orient horizontal
    pack $panewin -expand 1 -fill both -side left

    togl $toglwin -width $::g_WinWidth -height $::g_WinHeight \
                  -swapinterval 0 \
                  -double true -depth true \
                  -createproc tclCreateFunc \
                  -reshapeproc tclReshapeFunc \
                  -displayproc tclDisplayFunc 
    pack $toglwin -expand 1 -fill both -side left

    ttk::frame $panewin.hierfr
    pack $panewin.hierfr -expand 1 -fill both -side left

    $panewin add $panewin.hierfr
    $panewin add $toglwin

    ttk::frame .fr.btnfr
    ttk::label .fr.info -anchor center
    grid .fr.fr    -row 0 -column 0 -sticky news
    grid .fr.btnfr -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
    UpdateTitleString "No model loaded"
    wm minsize . 100 100

    ttk::labelframe .fr.fr.pane.hierfr.treefr -text "Hierachy"
    ttk::labelframe .fr.fr.pane.hierfr.listfr -text "Node properties"
    pack .fr.fr.pane.hierfr.treefr -side top -padx 2 -pady 3 -fill x -expand 0
    pack .fr.fr.pane.hierfr.listfr -side top -padx 2 -pady 3 -fill x -expand 0

    set treeView [tcl3dCreateScrolledTreeview .fr.fr.pane.hierfr.treefr "" -show tree]
    $treeView heading #0 -text "Tree"
    $treeView column #0 -width 60
    set gWidgets(treeView) $treeView

    set tableView [tcl3dCreateScrolledTablelist .fr.fr.pane.hierfr.listfr "" \
                -width 30 -height 10 -exportselection false \
                -columns {0 "Property" "left"
                          0 "Value"    "left" } \
                -setfocus 1 \
                -stripebackground #e0e8f0 \
                -selectmode extended \
                -labelcommand tablelist::sortByColumn \
                -showseparators yes]
    set gWidgets(tableView) $tableView

    bind $treeView <<TreeviewSelect>> "ShowNodeProperties $treeView $tableView"

    ttk::labelframe .fr.btnfr.pmode -text "Polygon Modes"
    pack .fr.btnfr.pmode -side top -padx 2 -pady 3 -fill x -expand 0

    ttk::radiobutton .fr.btnfr.pmode.fill \
                -text "FILL" -variable gOpts(polymode) \
                -command SetPolyMode -value $::osg::PolygonMode_FILL
    ttk::radiobutton .fr.btnfr.pmode.line \
                -text "LINE"  -variable gOpts(polymode) \
                -command SetPolyMode -value $::osg::PolygonMode_LINE
    ttk::radiobutton .fr.btnfr.pmode.point \
                -text "POINT" -variable gOpts(polymode) \
                -command SetPolyMode -value $::osg::PolygonMode_POINT
    pack .fr.btnfr.pmode.fill .fr.btnfr.pmode.line .fr.btnfr.pmode.point \
         -side top -expand 1 -fill x

    ttk::labelframe .fr.btnfr.bcol -text "Clear Color"
    pack .fr.btnfr.bcol -side top -padx 2 -pady 3 -fill x -expand 0

    ttk::radiobutton .fr.btnfr.bcol.black \
                -text "Black" -variable gOpts(clearcolor) \
                -command SetClearColor -value [list 0 0 0]
    ttk::radiobutton .fr.btnfr.bcol.blue \
                -text "Blue"  -variable gOpts(clearcolor) \
                -command SetClearColor -value [list 0.2 0.2 0.4]
    ttk::radiobutton .fr.btnfr.bcol.white \
                -text "White" -variable gOpts(clearcolor) \
                -command SetClearColor -value [list 1 1 1]
    pack .fr.btnfr.bcol.black .fr.btnfr.bcol.blue .fr.btnfr.bcol.white \
         -side top -expand 1 -fill x

    ttk::labelframe .fr.btnfr.bmanip -text "Manipulators"
    pack .fr.btnfr.bmanip -side top -padx 2 -pady 3 -fill x -expand 0

    ttk::radiobutton .fr.btnfr.bmanip.track \
                -text "Track" -variable gOpts(manipMode) \
                -command SetManipMode -value $::TrackManip
    ttk::radiobutton .fr.btnfr.bmanip.flight \
                -text "Flight" -variable gOpts(manipMode) \
                -command SetManipMode -value $::FlightManip
    ttk::radiobutton .fr.btnfr.bmanip.drive \
                -text "Drive" -variable gOpts(manipMode) \
                -command SetManipMode -value $::DriveManip
    pack .fr.btnfr.bmanip.track .fr.btnfr.bmanip.flight .fr.btnfr.bmanip.drive \
         -side top -expand 1 -fill x
    #bind . <Key-t> "SetManipMode 0"
    #bind . <Key-f> "SetManipMode 1"
    #bind . <Key-d> "SetManipMode 2"

    ttk::labelframe .fr.btnfr.toggles -text "Toggles"
    pack .fr.btnfr.toggles -side top -padx 2 -pady 3 -fill x -expand 0

    ttk::checkbutton .fr.btnfr.toggles.animation \
                -text "Continuous update" -variable gOpts(continuousUpdate) \
                -command ToggleContinuousUpdate
    ttk::checkbutton .fr.btnfr.toggles.lighting \
                -text "Lighting" -variable gOpts(lighting) \
                -command ToggleLighting
    ttk::checkbutton .fr.btnfr.toggles.shading \
                -text "Smooth shading" -variable gOpts(smoothshade) \
                -command ToggleShading
    ttk::checkbutton .fr.btnfr.toggles.texture \
                -text "Texturing" -variable gOpts(texture) \
                -command ToggleTexturing
    ttk::checkbutton .fr.btnfr.toggles.displaylists \
                -text "Display Lists" -variable gOpts(displaylists) \
                -command ToggleDisplayLists
    pack .fr.btnfr.toggles.animation \
         .fr.btnfr.toggles.lighting \
         .fr.btnfr.toggles.shading \
         .fr.btnfr.toggles.texture \
         .fr.btnfr.toggles.displaylists \
         -side top -expand 1 -fill x

    ttk::labelframe .fr.btnfr.cmds -text "Commands"
    pack .fr.btnfr.cmds -side top -padx 2 -pady 3 -fill x -expand 0

    ttk::button .fr.btnfr.cmds.smoothing \
           -text "SmoothingVisitor" -command CmdSmoothingVisitor
    ttk::button .fr.btnfr.cmds.tristriping \
           -text "TriStripVisitor" -command CmdTriStripVisitor

    pack .fr.btnfr.cmds.smoothing \
         .fr.btnfr.cmds.tristriping \
         -side top -expand 1 -fill x

    # Mouse bindings
    tcl3dOsgAddTrackballBindings $toglwin $osgwin

    bind . <KeyPress> "tcl3dOsgKeyPress $toglwin $osgwin %N"

    # Create menu pane
    set hMenu .menufr
    menu $hMenu
    $hMenu add cascade -menu $hMenu.file -label File      -underline 0
    $hMenu add cascade -menu $hMenu.view -label View      -underline 0
    $hMenu add cascade -menu $hMenu.help -label Help      -underline 0

    # Create menu "File"
    set fileMenu $hMenu.file
    menu $fileMenu -tearoff 0

    AddMenuCmd $fileMenu "Open ..."    "Ctrl+O" AskOpenMod
    AddMenuCmd $fileMenu "Save as ..." "Ctrl+S" AskSaveMod
    $fileMenu add separator
    AddMenuCmd $fileMenu "Quit"        "Ctrl+Q" ExitProg

    bind . <Control-o>  AskOpenMod
    bind . <Control-s>  AskSaveMod
    bind . <Control-q>  ExitProg
    bind . <Key-Escape> ExitProg
    if { $::tcl_platform(os) eq "windows" } {
        bind . <Alt-F4>     ExitProg
    }
    wm protocol . WM_DELETE_WINDOW "ExitProg"

    # Create menu "View"
    set viewMenu $hMenu.view
    menu $viewMenu -tearoff 0

    AddMenuCmd   $viewMenu "Reset transformations"   "Ctrl-R" ResetTfms
    bind . <Control-r>  ResetTfms

    # Create menu "Help"
    set helpMenu $hMenu.help
    menu $helpMenu -tearoff 0

    AddMenuCmd $helpMenu "About $gOpts(appName) ..."     ""   HelpProg

    . configure -menu $hMenu
}

proc HelpProg {} {
    tk_messageBox -message "Simple OSG model viewer powered by Tcl3D.\n\
                            Copyright 2009-2022 by Paul Obermeier.\n\n\
                            http://www.tcl3d.org" \
                           -type ok -icon info -title "$::gOpts(appName)"
    focus .
}

proc TreeviewVisitor { node args } {
    global gWidgets gTreeRootId gGraphParents

    set nodeName [osg::Object_getName $node]
    set nodeType [osg::Object_className $node]
    scan $node "_%lx" nodeAddr
    if { $nodeName eq "" } {
        set nodeName $nodeType
    }

    set n [osg::Node_getNumParents $node]
    if { $n == 0 || ! [info exists gTreeRootId] } {
        # This node does not have a parent or it is the first node encountered.
        set gTreeRootId [$gWidgets(treeView) insert {} end -id $nodeAddr \
            -text $nodeName -open true \
            -tags $node -values [list $nodeType]]
        $gWidgets(treeView) item $nodeAddr -image [tcl3dOsgGetBitmap $nodeType]
    } elseif { $n == 1 } {
        # This node has exactly 1 parent. Thus we can insert it into the tree easily.
        set parent [osg::Node_getParent $node 0]
        # puts "  Parent: [osg::Object_getName $parent]"
        scan $parent "_%lx" parentAddr
        $gWidgets(treeView) insert $parentAddr end -id $nodeAddr \
            -text $nodeName -open false \
            -tags $node -values [list $nodeType]
        $gWidgets(treeView) item $nodeAddr -image [tcl3dOsgGetBitmap $nodeType]
    } else {
        # We have a node with more than 1 parent, i.e. it is a graph and not a tree.
        set insertItem false
        # puts -nonewline "  Parents: "
        for { set i 0 } { $i < $n } { incr i } {
            set parent [osg::Node_getParent $node $i]
            # puts -nonewline "[osg::Object_getName $parent] "
            scan $parent "_%lx" parentAddr

            if { $insertItem == false } {
                set isReference false
                if { ! [info exists gGraphParents($nodeAddr)] } {
                    # First time this node is encountered.
                    set insertItem true
                    set nodeImg [tcl3dOsgGetBitmap $nodeType]
                    set gGraphParents($nodeAddr) 1
                    set gGraphParents($nodeAddr,$parentAddr) 1
                    set nodeId $nodeAddr
                } elseif { [info exists gGraphParents($nodeAddr,$parentAddr)] } {
                    # This node has already been inserted with the given parent.
                    set insertItem false
                } else {
                    # This node has already been inserted previously. Now a reference
                    # must be inserted.
                    set insertItem true
                    set nodeImg [tcl3dOsgGetBitmap $nodeType]
                    set gGraphParents($nodeAddr,$parentAddr) 1
                    set nodeId ${nodeAddr}_$i
                    set isReference true
                }

                if { $insertItem } {
                    $gWidgets(treeView) insert $parentAddr end -id $nodeId \
                        -text $nodeName -open true \
                        -tags [list $node $nodeId] -values [list $nodeType]
                    $gWidgets(treeView) item $nodeId -image $nodeImg
                    if { $isReference } {
                        $gWidgets(treeView) tag configure $nodeId \
                                            -background lightgreen
                    }
                }
            }
        }
        # puts ""
    }
}

proc ShowNodeProperties { treeId tableId } {
    global gOpts gMarker gWidgets
    
    if { $gOpts(continuousUpdate) } {
        StopAnimation
    }
    set selList [$treeId selection]
    set treeItem [lindex $selList 0]
    set node [$treeId item $treeItem -tags] 

    set nodeName [osg::Object_getName $node]
    if { $nodeName eq "" } {
        set nodeName "Name not set"
    }
    set nodeType     [osg::Object_className $node]
    set nodeMask     [osg::Node_getNodeMask $node]
    set nodeStateSet [osg::Node_getStateSet $node]

    $tableId delete 0 end
    $tableId insert end [list "Name" $nodeName]
    $tableId insert end [list "Type" $nodeType]
    $tableId insert end [list "Mask" [format "%0X" $nodeMask]]
    $tableId insert end [list "State Set" $nodeStateSet]

    set n [osg::Node_getNumDescriptions $node]
    for { set i 0 } { $i < $n } { incr i } {
        set desc [osg::Node_getDescription $node $i]
        $tableId insert end [list "Description-$i" $desc]
    }
    if { [info exists gMarker(nodeMarked)] } {
        osg::Geode_removeDrawable $gMarker(nodeMarked) gMarkShape
        gMarkShape -delete
        unset gMarker(nodeMarked)
    }
    if { $nodeType eq "Geode" } {
        $tableId insert end [list "Num Drawables" [osg::Geode_getNumDrawables $node]]
        set bbox [osg::Geode_getBoundingBox $node]
        set xMin [$bbox xMin]
        set xMax [$bbox xMax]
        set yMin [$bbox yMin]
        set yMax [$bbox yMax]
        set zMin [$bbox zMin]
        set zMax [$bbox zMax]
        $tableId insert end [list "BBox X" [format "%.2f %.2f" $xMin $xMax]]
        $tableId insert end [list "BBox Y" [format "%.2f %.2f" $yMin $yMax]]
        $tableId insert end [list "BBox Z" [format "%.2f %.2f" $zMin $zMax]]
        set bboxCenter [$bbox center]
        set bboxHalfs [osg::Vec3 half \
                        [expr ($xMax-$xMin)*0.5] \
                        [expr ($yMax-$yMin)*0.5] \
                        [expr ($zMax-$zMin)*0.5]]
        gMarkBox set $bboxCenter $bboxHalfs
        osg::ShapeDrawable gMarkShape gMarkBox
        osg::Geode_addDrawable $node gMarkShape
        set gMarker(nodeMarked) $node
        set pStateSet [gMarkShape getOrCreateStateSet]
        osg::PolygonMode pgonMode $::osg::PolygonMode_FRONT_AND_BACK \
                                  $::osg::PolygonMode_LINE
        $pStateSet setAttributeAndModes pgonMode \
              [expr $::osg::StateAttribute_PROTECTED|$::osg::StateAttribute_ON]
        $pStateSet setMode $::GL_LIGHTING \
              [expr $::osg::StateAttribute_PROTECTED|$::osg::StateAttribute_OFF]
        $pStateSet setMode $::GL_CULL_FACE \
              [expr $::osg::StateAttribute_PROTECTED|$::osg::StateAttribute_OFF]
    }
    update
    if { $gOpts(continuousUpdate) } {
        StartAnimation
    } else {
        $gWidgets(toglwin) postredisplay
    }
}

proc ReadModel { name } {
    global gOpts gTreeRootId gWidgets

    if { $gOpts(continuousUpdate) } {
        StopAnimation
    }
    ClearSceneGraph
    set modelNode [osgDB::readNodeFile $name]
    rootGroup addChild $modelNode
    viewer setSceneData rootGroup
    ResetTfms
    #$gWidgets(toglwin) postredisplay

    osg::tcl3dOsgNodeVisitor nv $::osg::NodeVisitor_TRAVERSE_ALL_CHILDREN
    nv setVisitorType $::osg::NodeVisitor_NODE_VISITOR
    catch { unset gTreeRootId }
    if { $gOpts(showTreeview) } {
        nv setVisitorProc TreeviewVisitor
        osg::Node_accept $modelNode nv
    }

    UpdateTitleString [file tail $name]
    update
    if { $gOpts(continuousUpdate) } {
        StartAnimation
    }
}

proc AskOpenMod {} {
    global gOpts

    set fileTypes {
        { "All files"            "*" }
        { "OpenSceneGraph files" "*.osg *.osgt" }
        { "OpenFlight files"     "*.flt" }
        { "Wavefront files"      "*.obj" }
    }
    set modName [tk_getOpenFile -filetypes $fileTypes \
                                -initialdir $gOpts(lastDir)]
    if { $modName != "" } {
        set gOpts(lastDir) [file dirname $modName]
        ReadModel $modName
    }
}

proc SaveModel { name } {
    set retVal [osgDB::writeNodeFile rootGroup $name]
    if { $retVal } {
        UpdateTitleString [file tail $name]
    } else {
        error "Could not save scene data in file $name"
    }
}

proc AskSaveMod {} {
    global gOpts

    set fileTypes {
        { "All files"            "*" }
        { "OpenSceneGraph files" "*.osg" }
        { "Collada DAE files"    "*.dae" }
    }
    set modName [tk_getSaveFile -filetypes $fileTypes \
                                -initialdir $gOpts(lastDir)]
    if { $modName != "" } {
        set gOpts(lastDir) [file dirname $modName]
        SaveModel $modName
    }
}

proc InitScene {} {
    osgViewer::ViewerRef viewer [osgViewer::Viewer]
    set osgwin [viewer setUpViewerAsEmbeddedInWindow 100 100 600 400]
    tcl3dOsgSetOsgWin $osgwin

    osgGA::TrackballManipulator trackManip
    osgGA::FlightManipulator    flightManip
    osgGA::DriveManipulator     driveManip

    osgGA::KeySwitchMatrixManipulator keyswitchManipulator
    keyswitchManipulator addMatrixManipulator [tcl3dOsgKeysym "t"] "Track"  trackManip
    keyswitchManipulator addMatrixManipulator [tcl3dOsgKeysym "f"] "Flight" flightManip
    keyswitchManipulator addMatrixManipulator [tcl3dOsgKeysym "d"] "Drive"  driveManip

    osgViewer::StatsHandler statsHandler
    viewer setCameraManipulator keyswitchManipulator
    viewer addEventHandler statsHandler
    viewer realize
    osg::Group rootGroup
}

proc ResetTfms {} {
    global gOpts gWidgets

    if { $gOpts(manipMode) == $::TrackManip } {
        trackManip home 0.0
    } elseif { $gOpts(manipMode) == $::DriveManip } {
        driveManip home 0.0
    } elseif { $gOpts(manipMode) == $::FlightManip } {
        flightManip home 0.0
    }
    $gWidgets(toglwin) postredisplay
}

proc ToggleLighting {} {
    global gOpts gWidgets

    if { $gOpts(lighting) } {
        set stateAttribute $::osg::StateAttribute_ON
    } else {
        set stateAttribute $::osg::StateAttribute_OFF
    }
    set stateSet [rootGroup getOrCreateStateSet]
    $stateSet setMode $::GL_LIGHTING \
                      [expr $::osg::StateAttribute_OVERRIDE|$stateAttribute]
    $gWidgets(toglwin) postredisplay
}

proc ToggleShading {} {
    global gOpts gWidgets

    set shademodel [osg::ShadeModel]
    set stateSet [rootGroup getOrCreateStateSet]
    $stateSet setAttribute $shademodel $::osg::StateAttribute_OVERRIDE
    if { $gOpts(smoothshade) } {
        $shademodel setMode $::osg::ShadeModel_SMOOTH
    } else {
        $shademodel setMode $::osg::ShadeModel_FLAT
    }
    $gWidgets(toglwin) postredisplay
}

proc ToggleTexturing {} {
    global gOpts gWidgets

    if { $gOpts(texture) } {
        set stateAttribute $::osg::StateAttribute_ON
    } else {
        set stateAttribute $::osg::StateAttribute_OFF
    }
    set stateSet [rootGroup getOrCreateStateSet]
    $stateSet setTextureMode 0 $::GL_TEXTURE_2D \
              [expr $::osg::StateAttribute_OVERRIDE|$stateAttribute]
    $gWidgets(toglwin) postredisplay
}

proc ToggleDisplayLists {} {
    global gOpts  gWidgets

    if { $gOpts(displaylists) } {
        set dlv [osgUtil::GLObjectsVisitor $::osgUtil::GLObjectsVisitor_SWITCH_ON_DISPLAY_LISTS]
    } else {
        set dlv [osgUtil::GLObjectsVisitor $::osgUtil::GLObjectsVisitor_SWITCH_OFF_DISPLAY_LISTS]
    }
    rootGroup accept $dlv
    $gWidgets(toglwin) postredisplay
}

proc CmdSmoothingVisitor {} {
    global gWidgets

    set sv [osgUtil::SmoothingVisitor]
    rootGroup accept $sv
    $gWidgets(toglwin) postredisplay
}

proc CmdTriStripVisitor {} {
    global gWidgets

    set tsv [osgUtil::TriStripVisitor]
    $tsv setMinStripSize 1
    rootGroup accept $tsv
    $gWidgets(toglwin) postredisplay
}

proc SetPolyMode {} {
    global gOpts gWidgets

    set polyModeObj [osg::PolygonMode]
    $polyModeObj setMode $::osg::PolygonMode_FRONT_AND_BACK $gOpts(polymode)
    set stateSet [rootGroup getOrCreateStateSet]
    $stateSet setAttributeAndModes $polyModeObj \
              [expr $::osg::StateAttribute_OVERRIDE|$::osg::StateAttribute_ON]
    $gWidgets(toglwin) postredisplay
}

proc SetClearColor {} {
    global gOpts gWidgets

    set v [osg::Vec4f]
    $v set [lindex $gOpts(clearcolor) 0] \
           [lindex $gOpts(clearcolor) 1] \
           [lindex $gOpts(clearcolor) 2] 1.0
    set cam [viewer getCamera]
    # OPA TODO Check for object command
    osg::Camera_setClearColor $cam $v
    $v -delete
    $gWidgets(toglwin) postredisplay
}

proc SetManipMode { { mode -1 } } {
    global gOpts gWidgets

    if { $mode >= 0 } {
        set gOpts(manipMode) $mode
    }
    keyswitchManipulator selectMatrixManipulator $gOpts(manipMode)
    $gWidgets(toglwin) postredisplay
}

proc ClearSceneGraph {} {
    global gWidgets gTreeRootId

    if { [info commands rootGroup] ne "" } {
        rootGroup removeChild 0 [rootGroup getNumChildren]
    }
    $gWidgets(tableView) delete 0 end
    catch { $gWidgets(treeView)  delete $gTreeRootId }
}

proc Animate {} {
    global gWidgets

    #puts "Animate [clock seconds]"
    $gWidgets(toglwin) postredisplay
    set ::animateId [after idle Animate]
}

proc StartAnimation {} {
    if { ! [info exists ::animateId] } {
        Animate
    }
}

proc StopAnimation {} {
    if { [info exists ::animateId] } {
        after cancel $::animateId 
        unset ::animateId
    }
}

proc ToggleContinuousUpdate {} {
    global gOpts

    if { $gOpts(continuousUpdate) } {
        StartAnimation
    } else {
        StopAnimation
    }
}

proc tclCreateFunc { toglwin } {
}

proc tclReshapeFunc { toglwin { w -1 } { h -1 } } {
    set w [$toglwin width]
    set h [$toglwin height]
    tcl3dOsgWindowResize $toglwin [tcl3dOsgGetOsgWin] $w $h
}

proc tclDisplayFunc { toglwin } {
    #puts "Events: [after info]"
    if { [viewer valid] } {
        viewer frame
    }
    $toglwin swapbuffers
}

proc ExitProg {} {
    exit
}

CreateWidgets

# Create a box shape used for marking geodes.
osg::Box gMarkBox
osg::ShapeDrawable gMarkShape gMarkBox

if { $argc != 0 } {
    set modName [lindex $argv 0]
    ReadModel $modName
} else {
    viewer setSceneData rootGroup
}

PrintInfo [format "Running on %s %s with a %s (OSG %s, Tcl/Tk %s %d-bit)" \
           $tcl_platform(os) $tcl_platform(osVersion) \
           [glGetString GL_RENDERER] \
           [tcl3dOsgGetVersion] [info patchlevel] \
           [expr $tcl_platform(pointerSize) == 4? 32: 64]]

update
if { $gOpts(continuousUpdate) } {
    StartAnimation
}
