Demo 1 of 9 in category NPS-Tutorials
 |
# NPS02_GeometryTest.tcl
#
# Original C++ code by Joseph Sullivan.
# See http://www.openscenegraph.org/projects/osg/wiki/Support/Tutorials
# for the original files.
#
# Modified for Tcl3D by Paul Obermeier 2009/03/20.
# See www.tcl3d.org for the Tcl3D extension.
package require tcl3d
if { ! [tcl3dHaveOsg] } {
tk_messageBox -icon error -type ok -title "Missing Tcl3D module" \
-message "Demo needs the tcl3dOSG module."
proc Cleanup {} {}
exit 1
return
}
# Font to be used in the Tk listbox.
set gDemo(listFont) {-family {Courier} -size 10}
# Window size.
set gDemo(winWidth) 640
set gDemo(winHeight) 480
# Determine the directory and filename of this script.
set gDemo(scriptFile) [info script]
set gDemo(scriptDir) [file dirname $gDemo(scriptFile)]
# Show errors occuring in the Togl callbacks.
proc bgerror { msg } {
puts "Error: $msg\n\n$::errorInfo"
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
}
}
# Idle callback to redisplay the scene.
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
}
}
proc CreateCallback { toglwin } {
}
proc ReshapeCallback { toglwin { w -1 } { h -1 } } {
set w [$toglwin width]
set h [$toglwin height]
# Propagate resize event to embedded OSG window.
tcl3dOsgWindowResize $toglwin [tcl3dOsgGetOsgWin] $w $h
}
proc DisplayCallback { toglwin } {
if { [viewer valid] } {
viewer frame
}
$toglwin swapbuffers
}
proc Cleanup {} {
uplevel #0 unset gDemo
viewer -delete
}
proc ExitProg {} {
exit
}
proc SaveOsgToFile {} {
global gDemo
set osgRoot [viewer getSceneData]
set outFile [format "%s.osgt" [file rootname $gDemo(scriptFile)]]
# Create a name on the file system, if running from within a Starpack.
set outFile [tcl3dGenExtName $outFile]
puts "Saving scenegraph to file $outFile"
if { ! [osgDB::writeNodeFile $osgRoot $outFile] } {
puts "Failed to write scenegraph to file $outFile"
}
}
proc CreateWidgets { osgwin } {
global gDemo
frame .fr
pack .fr -expand 1 -fill both
set toglwin .fr.toglwin
togl $toglwin -width $gDemo(winWidth) -height $gDemo(winHeight) \
-double true -depth true -alpha true \
-createcommand CreateCallback \
-reshapecommand ReshapeCallback \
-displaycommand DisplayCallback
listbox .fr.usage -font $::gDemo(listFont) -height 3
label .fr.info
grid $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: Sullivan's OSG tutorial #2 (GeometryTest)"
wm protocol . WM_DELETE_WINDOW "ExitProg"
bind . <Key-Escape> "ExitProg"
bind . <Key-f> "SaveOsgToFile"
# Propagate key and mouse events to embedded OSG window.
bind . <KeyPress> "tcl3dOsgKeyPress $toglwin $osgwin %N"
tcl3dOsgAddTrackballBindings $toglwin $osgwin
.fr.usage insert end "Key-Escape Exit"
.fr.usage insert end "Key-f Save SceneGraph to file"
.fr.usage insert end "Mouse Trackball"
.fr.usage configure -state disabled
}
#
# Start of tutorial specific code.
#
osg::Group root
osg::Geode pyramidGeode
osg::Geometry pyramidGeometry
osg::Geode crossGeode
osg::Geometry crossGeometry
# Associate the pyramid geometry with the pyramid geode
# Add the pyramid geode to the root node of the scene graph.
pyramidGeode addDrawable pyramidGeometry
root addChild pyramidGeode
crossGeode addDrawable crossGeometry
root addChild crossGeode
# Declare an array of vertices. Each vertex will be represented by
# a triple -- an instances of the vec3 class. An instance of
# osg::Vec3Array can be used to store these triples. Since
# osg::Vec3Array is derived from the STL vector class, we can use the
# push_back method to add array elements. Push back adds elements to
# the end of the vector, thus the index of first element entered is
# zero, the second entries index is 1, etc.
# Using a right-handed coordinate system with 'z' up, array
# elements zero..four below represent the 5 points required to create
# a simple pyramid.
osg::Vec3Array pyramidVertices
pyramidVertices push [osg::Vec3 p1 0 0 0] ; # front left
pyramidVertices push [osg::Vec3 p2 10 0 0] ; # front right
pyramidVertices push [osg::Vec3 p3 10 10 0] ; # back right
pyramidVertices push [osg::Vec3 p4 0 10 0] ; # back left
pyramidVertices push [osg::Vec3 p5 5 5 10] ; # peak
set pclen 12.0
set mclen -12.0
osg::Vec3Array crossVertices
crossVertices push [osg::Vec3 c1 $mclen 0.0 0.0]
crossVertices push [osg::Vec3 c2 $pclen 0.0 0.0]
crossVertices push [osg::Vec3 c3 0.0 0.0 $mclen]
crossVertices push [osg::Vec3 c4 0.0 0.0 $pclen]
# Associate this set of vertices with the geometry associated with the
# geode we added to the scene.
pyramidGeometry setVertexArray pyramidVertices
crossGeometry setVertexArray crossVertices
# Next, create a primitive set and add it to the pyramid geometry.
# Use the first four points of the pyramid to define the base using an
# instance of the DrawElementsUint class. Again this class is derived
# from the STL vector, so the push_back method will add elements in
# sequential order. To ensure proper backface cullling, vertices
# should be specified in counterclockwise order. The arguments for the
# constructor are the enumerated type for the primitive
# (same as the OpenGL primitive enumerated types), and the index in
# the vertex array to start from.
osg::DrawElementsUInt pyramidBase $::osg::PrimitiveSet_QUADS 0
pyramidBase push 3
pyramidBase push 2
pyramidBase push 1
pyramidBase push 0
pyramidGeometry addPrimitiveSet pyramidBase
osg::DrawElementsUInt cross $::osg::PrimitiveSet_LINES 0
cross push 3
cross push 2
cross push 1
cross push 0
crossGeometry addPrimitiveSet cross
# Repeat the same for each of the four sides. Again, vertices are
# specified in counter-clockwise order.
osg::DrawElementsUInt pyramidFaceOne $::osg::PrimitiveSet_TRIANGLES 0
pyramidFaceOne push 0
pyramidFaceOne push 1
pyramidFaceOne push 4
pyramidGeometry addPrimitiveSet pyramidFaceOne
osg::DrawElementsUInt pyramidFaceTwo $::osg::PrimitiveSet_TRIANGLES 0
pyramidFaceTwo push 1
pyramidFaceTwo push 2
pyramidFaceTwo push 4
pyramidGeometry addPrimitiveSet pyramidFaceTwo
osg::DrawElementsUInt pyramidFaceThree $::osg::PrimitiveSet_TRIANGLES 0
pyramidFaceThree push 2
pyramidFaceThree push 3
pyramidFaceThree push 4
pyramidGeometry addPrimitiveSet pyramidFaceThree
osg::DrawElementsUInt pyramidFaceFour $::osg::PrimitiveSet_TRIANGLES 0
pyramidFaceFour push 3
pyramidFaceFour push 0
pyramidFaceFour push 4
pyramidGeometry addPrimitiveSet pyramidFaceFour
# Declare and load an array of Vec4 elements to store colors.
osg::Vec4Array colors
colors push [osg::Vec4f col1 1.0 0.0 0.0 1.0] ; # index 0 red
colors push [osg::Vec4f col2 0.0 1.0 0.0 1.0] ; # index 1 green
colors push [osg::Vec4f col3 0.0 0.0 1.0 1.0] ; # index 2 blue
colors push [osg::Vec4f col4 1.0 1.0 1.0 1.0] ; # index 3 white
colors setBinding $::osg::Geometry_BIND_PER_VERTEX
# The next step is to associate the array of colors with the geometry,
# assign the color indices created above to the geometry and set the
# binding mode to _PER_VERTEX.
pyramidGeometry setColorArray colors
crossGeometry setColorArray colors
# Now that we have created a geometry node and added it to the scene
# we can reuse this geometry. For example, if we wanted to put a
# second pyramid 15 units to the right of the first one, we could add
# this geode as the child of a transform node in our scene graph.
# Declare and initialize a transform node.
osg::PositionAttitudeTransform pyramidTwoXForm
# Use the 'addChild' method of the osg::Group class to
# add the transform as a child of the root node and the
# pyramid node as a child of the transform.
root addChild pyramidTwoXForm
pyramidTwoXForm addChild pyramidGeode
# Declare and initialize a Vec3 instance to change the
# position of the model in the scene
osg::Vec3d pyramidTwoPosition 15 0 0
pyramidTwoXForm setPosition pyramidTwoPosition
# We have the geometry ready. Now create the viewer and the Tk widgets.
osgViewer::ViewerRef viewer [osgViewer::Viewer]
viewer setSceneData root
viewer setCameraManipulator [osgGA::TrackballManipulator]
if { $argc >= 1 && [lindex $argv 0] eq "-viewer" } {
# Only use the standard OSG viewer window without any Tk widgets.
viewer setUpViewInWindow 50 50 500 400
viewer run
exit 0
}
# Use the OSG viewer inside a Togl widget.
set osgwin [viewer setUpViewerAsEmbeddedInWindow 50 50 500 400]
tcl3dOsgSetOsgWin $osgwin
viewer realize
CreateWidgets $osgwin
PrintInfo [tcl3dOsgGetInfoString]
if { [file tail [info script]] eq [file tail $::argv0] } {
# If started directly from tclsh or wish, then start animation.
update
StartAnimation
}
|
