Computer Graphics Workshop '97 Lecture Notes | 1/24/97 |
Most of the material in this lecture is derived from Chapter 13 of The Inventor Mentor.
We have already used one of these conversions; recall that all the inputs and outputs to the Compose and Decompose engines in the second problem set were MF fields, but that we were hooking up single-value, or SF, fields to them.
All outputs from engines are actually SoEngineOutputs, not fields. This means that you can not perform a getValue on the output of an engine. However, engine outputs do have types (which are indicated in parentheses in the manual); you can hook up engine outputs to fields of the appropriate type (subject to the field-to-field conversions listed above).
If you need to get the value of an engine's output and can not structure your scene graph so you can connect the engine's output to one of your nodes' fields, you can create a global field, hook up the engine to that field, and then perform a getValue. (You should NOT instantiate a new field outside of a node, by calling (for example) new-SoSFVec3f.) For example:
;; Example which creates a global field and hooks up ;; an engine output to it, then performs a getValue ;; on the field. Note that this is poor programming ;; style, and wherever possible you should avoid ;; having to perform a getValue on the end of an ;; engine network. ;; Create the global field. ;; Note that now we can use SoDB::getGlobalField ;; with argument (new-SbName "MyField") to get ;; a handle on this field at a later time, but ;; still need to cast it to an SoSFVec3f. (define my-vec3f-field (SoSFVec3f-cast (SoDB::createGlobalField (new-SbName "MyField") (SoSFVec3f::getClassTypeId)))) (define my-interp (new-SoInterpolateVec3f)) (send (send my-interp 'input0) 'setValue 0 0 0) (send (send my-interp 'input1) 'setValue 1 1 0) (send (send my-interp 'alpha) 'setValue 0.3) (send my-vec3f-field 'connectFrom (send my-interp 'output)) (format #t "Interpolator's output was ~s\n" (send (send my-vec3f-field 'getValue) 'getValue))
(define trans1 (new-SoTranslation)) (define trans2 (new-SoTranslation)) (send (send trans1 'translation) 'connectFrom (send trans2 'translation)) (send (send trans2 'translation) 'connectFrom (send trans1 'translation))Now whenever the translation field of either trans1 or trans2 changes, the other one will automatically update itself to hold the new value.
There are two advantages of using engines over sensors when possible:
In order to create animations, we first need a source of time. An SoElapsedTime engine starts counting time when it is created, and can be paused or reset. An SoOneShot adds a duration field which indicates how long the engine will run, as well as a ramp field which goes from 0.0 to 1.0 over the engine's cycle. An SoTimeCounter engine counts integer values over a specified range and frequency. All of these engines use the realTime global field as their source. See the Inventor Mentor examples 13.1.GlobalFlds, 13.2.ElapsedTime, and 13.3.TimeCounter for examples of using the realTime field and the SoElapsedTime and SoTimeCounter engines.
Interpolation engines are good for performing a smooth sweep of an object (or the camera) through space. All of the interpolation engine types take in two fields, input0 and input1, as well as a floating point interpolation factor, alpha. They perform a linear interpolation between their inputs, and send the result to the output output. Here's an example of animating the camera to a new position over a specifiable period of time whenever the mouse is clicked.
;; Interpolation of camera position and rotation on demand. ;; Define scene graph and viewer, and extract camera (define root (new-SoSeparator)) (send root 'ref) (define text (new-SoText3)) (send (send text 'string) 'set1Value 0 (new-SbString "Dramatic")) (send (send text 'string) 'set1Value 1 (new-SbString "Angles")) (send (send text 'parts) 'setValue SoText3::ALL) (send (send text 'justification) 'setValue SoText3::CENTER) (send root 'addChild text) (define v (examiner root)) (send v 'setViewing 0) (define cam (send v 'getCamera)) ;; Define interpolation engines with two fixed inputs (define xl-interp (new-SoInterpolateVec3f)) (define rot-interp (new-SoInterpolateRotation)) (send (send cam 'position) 'connectFrom (send xl-interp 'output)) (send (send cam 'orientation) 'connectFrom (send rot-interp 'output)) ;; These initial and final positions were obtained via ;; experimentation, and by extracting the values from ;; the camera's position and orientation fields. ;; Initial position (send (send xl-interp 'input0) 'setValue #(30.7657852172852 7.46131801605225 19.1452522277832)) ;; Initial orientation (send (send rot-interp 'input0) 'setValue #(-0.12331560254097 0.413395673036575 0.0722599551081657 0.8992640376091)) ;; Final position (send (send xl-interp 'input1) 'setValue #(-28.8112678527832 -2.25108289718628 16.6992588043213)) ;; Final orientation (send (send rot-interp 'input1) 'setValue #(0.0514174103736877 -0.380638420581818 -0.0924163833260536 0.918656527996063)) ;; OneShot engine for the animation. (define one-shot (new-SoOneShot)) ;; Three second animation (send (send one-shot 'duration) 'setValue (new-SbTime 3.0)) ;; Connect its ramp output to the interpolators' alphas (send (send xl-interp 'alpha) 'connectFrom (send one-shot 'ramp)) (send (send rot-interp 'alpha) 'connectFrom (send one-shot 'ramp)) ;; Tell it to hold its final output (send (send one-shot 'flags) 'setValue (+ SoOneShot::RETRIGGERABLE SoOneShot::HOLD_FINAL)) ;; Mouse button callback to start the animation (define (mouse-press-cb user-data event-cb) (let ((event (send event-cb 'getEvent))) (if (= 1 (SO_MOUSE_PRESS_EVENT event BUTTON1)) (begin (send (send one-shot 'trigger) 'setValue) (send event-cb 'setHandled))))) (define event-cb (new-SoEventCallback)) (send root 'addChild event-cb) (send event-cb 'addEventCallback (SoMouseButtonEvent::getClassTypeId) (get-scheme-event-callback-cb) (void-cast (callback-info mouse-press-cb)))Arithmetic engines like SoCalculator are good for computing paths for objects to follow (see 13.6.Calculator) and for other simple arithmetic operations (for example, assisting in the computation of the ship's path in Combat). The SoBoolOperation engine can be combined with another special engine, SoGate, to enable and disable engine animations. The Inventor Mentor examples 13.4.Gate and 13.5.Boolean demonstrate this.
There are also some nodes which Inventor provides that contain engines and perform automatic animation:
A perspective camera views the scene in the same fashion as the human eye; that is, objects farther away from the camera appear smaller. This type of viewing style is known as a perspective projection. An orthographic camera has no perspective; it uses a parallel projection so that objects far away from the camera look the same size as those near the camera. Each type of camera has associated fields whose values can be changed to alter the parameters of the view. An orthographic camera has a field, height, which specifies the height of the view volume; this, combined with the aspect ratio of the camera, specifies how much of the scene fits within the camera view. A perspective camera has a heightAngle field which performs a similar function. Both types of cameras have several fields inherited from the parent class, SoCamera, such as the position, orientation, and aspectRatio of the camera.
(define viewer (new-SoXtExaminerViewer)) ;;; ...Additional code for showing viewer, ;;; and setting up scene graph... ;;; This is a simple example: (define root (new-SoSeparator)) (-> root 'ref) (define draw-style (new-SoDrawStyle)) (-> (-> draw-style 'style) 'setValue SoDrawStyle::LINES) (-> root 'addChild draw-style) (-> root 'addChild (new-SoCube)) (-> viewer 'setSceneGraph root) ;;; Extract the camera from the viewer. (define camera (-> viewer 'getCamera)) (define other-camera '()) (if (= 1 (-> camera 'isOfType (SoPerspectiveCamera::getClassTypeId))) (set! other-camera (SoPerspectiveCamera-cast camera)) (set! other-camera (SoOrthographicCamera-cast camera)))We can also set the camera type of the viewer manually. Note, as described in the manual page, that the change does not take effect until the next time the viewer's scene graph is set:
(-> viewer 'setCameraType (SoOrthographicCamera::getClassTypeId)) (-> viewer 'setSceneGraph root)In the solutions from problem set 4, the "widget viewer" uses an orthographic camera to make the 3D slider widgets look two-dimensional.
Directional lights cause the fastest rendering of shapes, because they do not require shading over the surface of a polygon. Point lights are the next fastest, and spot lights are the slowest. You will probably find it best to use only directional lights, if needed, in your scenes.
Let's look at the difference between these three types of lights:
;;; Example of point, spot, and directional lights. ;;; Three viewers, one per light (define v1 (new-SoXtExaminerViewer)) (-> v1 'show) (-> v1 'setHeadlight 0) (define v2 (new-SoXtExaminerViewer)) (-> v2 'show) (-> v2 'setHeadlight 0) (define v3 (new-SoXtExaminerViewer)) (-> v3 'show) (-> v3 'setHeadlight 0) ;;; Three scene graph roots, one per type of light. (define point-root (new-SoSeparator)) (-> point-root 'ref) (define spot-root (new-SoSeparator)) (-> spot-root 'ref) (define dir-root (new-SoSeparator)) (-> dir-root 'ref) ;;; Group node for holding point light ;;; and associated transform. (define point-light-group (new-SoTransformSeparator)) (-> point-root 'addChild point-light-group) (define light-xform (new-SoTransform)) (-> (-> light-xform 'translation) 'setValue 0.5 -0.5 -0.5) (-> point-light-group 'addChild light-xform) (define light (new-SoPointLight)) (-> point-light-group 'addChild light) (-> (-> light 'intensity) 'setValue 1.0) (define sphere (new-SoSphere)) (-> point-light-group 'addChild sphere) (-> (-> sphere 'radius) 'setValue 0.1) ;;; Group node for holding spot light ;;; and associated transform. (define spot-light-group (new-SoTransformSeparator)) (-> spot-root 'addChild spot-light-group) (define light-xform (new-SoTransform)) (-> (-> light-xform 'rotation) 'setValue (new-SbVec3f -0.356368511915207 0.0632830709218979 -0.932199954986572) 1.46062397956848) (-> (-> light-xform 'translation) 'setValue 1.6 0.0 2.0) (-> spot-light-group 'addChild light-xform) (define light (new-SoSpotLight)) (-> spot-light-group 'addChild light) (-> (-> light 'intensity) 'setValue 1.0) (-> (-> light 'dropOffRate) 'setValue 0.1) ;;; Group node for holding directional light ;;; and associated transform. (define dir-light-group (new-SoTransformSeparator)) (-> dir-root 'addChild dir-light-group) (define light-xform (new-SoTransform)) (-> (-> light-xform 'translation) 'setValue 0.5 -0.5 -0.5) (-> dir-light-group 'addChild light-xform) (define light (new-SoDirectionalLight)) (-> dir-light-group 'addChild light) (-> (-> light 'intensity) 'setValue 1.0) (-> (-> light 'direction) 'setValue -1 -1.3 -3.0) ;;; Group node for holding the three cubes. ;;; Shared among the three scene graphs. (define cube-group (new-SoGroup)) ;; Complexity node improves the shading of the cubes ;; at the expense of rendering speed. (define complexity (new-SoComplexity)) (-> cube-group 'addChild complexity) (-> (-> complexity 'value) 'setValue 0.7) (define mat (new-SoMaterial)) (-> cube-group 'addChild mat) (-> (-> mat 'diffuseColor) 'setValue 0.2 0.2 0.9) (define xform0 (new-SoTransform)) (-> cube-group 'addChild xform0) (-> (-> xform0 'rotation) 'setValue (new-SbVec3f 0.551838874816895 -0.806114614009857 -0.213665723800659) 0.859030544757843) (define cube1 (new-SoCube)) (-> cube-group 'addChild cube1) (-> (-> cube1 'width) 'setValue 0.25) (define xform1 (new-SoTransform)) (-> cube-group 'addChild xform1) (-> (-> xform1 'translation) 'setValue 1.125 0 -1.125) (define cube2 (new-SoCube)) (-> cube-group 'addChild cube2) (-> (-> cube2 'depth) 'setValue 0.25) (define xform2 (new-SoTransform)) (-> cube-group 'addChild xform2) (-> (-> xform2 'translation) 'setValue 0 -1.125 1.125) (define cube3 (new-SoCube)) (-> cube-group 'addChild cube3) (-> (-> cube3 'height) 'setValue 0.25) (-> point-root 'addChild cube-group) (-> spot-root 'addChild cube-group) (-> dir-root 'addChild cube-group) (-> v1 'setSceneGraph point-root) (-> v2 'setSceneGraph spot-root) (-> v3 'setSceneGraph dir-root)
(-> viewer 'setHeadlight 0) ;; turns off headlight (-> viewer 'setHeadlight 1) ;; turns on headlight
$Id: index.html,v 1.7 1997/01/23 02:15:30 kbrussel Exp $