Computer Graphics Workshop '96 PS5 Solutions | 1/22/96 |
;;; Problem 5-1 (require 'random) (define M_PI 3.14159265358979323846) ;; Parameters for shell of stars. (define star-resolution 10000) (define number-of-stars 1000) (define min-normalized-radius 0.0001) (define max-normalized-radius 1.0) (define r-min 30.0) (define r-max 50.0) ;; Definition of stars' transform and transformation matrix. (define xform (new-SoTransform)) (define xform-mat (new-SbMatrix)) (-> xform-mat 'makeIdentity) ;; Variables which get updated by the Location2Event callback ;; and used by the idle sensor callback. (define last-event #f) (define last-normalized-x-position 0.0) (define last-normalized-y-position 0.0) ;; Maximum angular change about an axis per time step (in radians) (define max-angular-velocity (/ M_PI 200.0)) ;; Procedure which attempts to get some random state into the ;; random number generator. (define (randomize-loop i) (if (> (modulo i 173) 0) (begin (display "Randomizing...\n") (random star-resolution) (randomize-loop (- i 1))))) ;; Returns a vector of x, y, z coordinates of a point inside ;; the thick shell of stars. (define (generate-star) ;; Pick random x, y, z between -1.0 and 1.0 (let ((x (- (* 2.0 (/ (random star-resolution) star-resolution)) 1.0)) (y (- (* 2.0 (/ (random star-resolution) star-resolution)) 1.0)) (z (- (* 2.0 (/ (random star-resolution) star-resolution)) 1.0))) ;; Calculate distance from origin (let ((r (sqrt (+ (* x x) (* y y) (* z z))))) ;; Constrain to be within the normalized shell (if (or (> r max-normalized-radius) (< r min-normalized-radius)) ;; If outside, try again (generate-star) ;; Otherwise, scale up to desired radius (let ((phi (asin (/ y r))) (theta (atan z x)) (new-r (+ r-min (* (- r min-normalized-radius) (- r-max r-min))))) ;; Return Cartesian coordinates of new point (vector (* new-r (cos phi) (cos theta)) (* new-r (sin phi)) (* new-r (cos phi) (sin theta)))))))) ;; Wrapper function for above -- returns ;; list of vectors of stars' coordinates (define (generate-stars num-stars) (if (> num-stars 0) (cons (generate-star) (generate-stars (- num-stars 1))) '())) ;; Simple function which returns stars' colors. ;; Designed to return various shades of blue. (define (generate-star-color) (let ((blueness (exact->inexact (/ (random star-resolution) star-resolution)))) (vector blueness blueness 1.0))) ;; Wrapper function for above which returns list of vectors of ;; stars' RGB colors. (define (generate-star-colors num-colors) (if (> num-colors 0) (cons (generate-star-color) (generate-star-colors (- num-colors 1))) '())) ;; Procedure which builds the shell of stars. ;; Returns root of this scene graph. (define (build-star-subgraph) (define root (new-SoSeparator)) ;; Note that we reference count the root for safety (-> root 'ref) (define bind (new-SoMaterialBinding)) (-> root 'addChild bind) ;; Each star gets its own color (-> (-> bind 'value) 'setValue SoMaterialBinding::PER_VERTEX) (define style (new-SoDrawStyle)) (-> root 'addChild style) ;; Change the size of the stars' points (-> (-> style 'pointSize) 'setValue 2.0) (define coords (new-SoCoordinate3)) (-> root 'addChild coords) ;; Set the coordinates inside the Coordinate3 node to the ;; result of the generate-stars procedure (set-mfield-values! (-> coords 'point) 0 (generate-stars number-of-stars)) (define mat (new-SoMaterial)) (-> root 'addChild mat) ;; Set the colors inside the Material node to the result of ;; generate-star-colors. (set-mfield-values! (-> mat 'diffuseColor) 0 (generate-star-colors number-of-stars)) (define ps (new-SoPointSet)) (-> root 'addChild ps) ;; Number-of-stars coordinates and colors to be rendered. (-> (-> ps 'numPoints) 'setValue number-of-stars) ;; Return root to its original reference count (without deleting it) (-> root 'unrefNoDelete) ;; Return root of the scene graph root) (define mouse-move-cb ;; Local variables. ;; Note that these are allocated only once for speed. (let ((xvec (new-SbVec3f 1 0 0)) (yvec (new-SbVec3f 0 1 0)) (xrot (new-SbRotation)) (yrot (new-SbRotation)) (xmat (new-SbMatrix)) (ymat (new-SbMatrix))) (lambda (user-data node) ;; Get event out of callback node (let* ((event (SoLocation2Event-cast (-> node 'getEvent))) ;; Get normalized position of where event occurred (loc (-> event 'getNormalizedPosition (-> viewer 'getViewportRegion))) ;; Change so (0,0) is in center of window (delta (-> loc 'operator- (new-SbVec2f 0.5 0.5)))) ;; Update last position where events occurred (set! last-normalized-x-position (-> delta 'operator-brackets 0)) (set! last-normalized-y-position (-> delta 'operator-brackets 1)) ;; Calculate incremental updates to x and y angles (let ((x-angle (* max-angular-velocity last-normalized-y-position)) (y-angle (* max-angular-velocity last-normalized-x-position))) ;; Update SbRotations with correct angle values (-> xrot 'setValue xvec x-angle) (-> yrot 'setValue yvec y-angle) ;; Update SbMatrix objects with rotations' new values (-> xmat 'operator= xrot) (-> ymat 'operator= yrot) ;; Update the transform matrix incrementally in two steps (-> xform-mat 'operator*= xmat) (-> xform-mat 'operator*= ymat) ;; Set the transform's matrix to be this new matrix (-> xform 'setMatrix xform-mat)))))) (define idle-cb (let ((xvec (new-SbVec3f 1 0 0)) (yvec (new-SbVec3f 0 1 0)) (xrot (new-SbRotation)) (yrot (new-SbRotation)) (xmat (new-SbMatrix)) (ymat (new-SbMatrix))) (lambda (user-data sensor) ;; Tell sensor to reschedule (-> sensor 'schedule) ;; Note that we make the x and y angles update faster inside ;; the idle callback. Otherwise, we we notice that the stars ;; rotate faster when the mouse is in motion. (let ((x-angle (* 3.0 max-angular-velocity last-normalized-y-position)) (y-angle (* 3.0 max-angular-velocity last-normalized-x-position))) ;; Exactly the same code as in the previous callback (-> xrot 'setValue xvec x-angle) (-> yrot 'setValue yvec y-angle) (-> xmat 'operator= xrot) (-> ymat 'operator= yrot) (-> xform-mat 'operator*= xmat) (-> xform-mat 'operator*= ymat) (-> xform 'setMatrix xform-mat))))) ;; Builds the entire scene graph, including stars' transform, ;; event callback node, and sphere of stars. (define (build-scene-graph) ;; Randomizing state of the random number generator (randomize-loop (get-universal-time)) (define root (new-SoSeparator)) (-> root 'ref) ;; xform is defined above; the global transform node for the stars. ;; Modified by both callbacks above. (-> root 'addChild xform) (define ev (new-SoEventCallback)) (-> root 'addChild ev) ;; Creating callback information node (same as for sensors). (define cb-info (new-SchemeSoCBInfo)) (-> cb-info 'ref) (-> (-> cb-info 'callbackName) 'setValue "mouse-move-cb") ;; Adding Location2Event callback to the event callback node. (-> ev 'addEventCallback (SoLocation2Event::getClassTypeId) (get-scheme-event-callback-cb) (void-cast cb-info)) ;; Add stars to the scene graph (-> root 'addChild (build-star-subgraph)) ;; Note that we do not unref this root node root) (define viewer (new-SoXtExaminerViewer)) (-> viewer 'show) (-> viewer 'setSceneGraph (build-scene-graph)) ;; Set the viewer's camera to be ;; at the center of the sphere of stars (-> (-> (-> viewer 'getCamera) 'position) 'setValue 0 0 0) ;; Select the arrow icon in the viewer (-> viewer 'setViewing 0) ;; Callback information node for the idle sensor's callback (define cb-info (new-SchemeSoCBInfo)) (-> cb-info 'ref) (-> (-> cb-info 'callbackName) 'setValue "idle-cb") ;; Creation and scheduling of the idle sensor (define idle-sensor (new-SoIdleSensor (get-scheme-sensor-cb) (void-cast cb-info))) (-> idle-sensor 'schedule)
;;; Problem 5-2. (define M_PI 3.14159265358979323846) (define viewer (new-SoXtExaminerViewer)) (-> viewer 'show) ;; Creation of root of scene graph (define root (new-SoSeparator)) (-> root 'ref) ;; Creation of camera kit. (define cam-kit (new-SoCameraKit)) (-> root 'addChild cam-kit) ;; Initialization code for camera kit. (define cam (SO_GET_PART cam-kit "camera" SoCamera)) (-> (-> cam 'nearDistance) 'setValue 1.0) (-> (-> cam 'farDistance) 'setValue 10000.0) (define transform (SO_GET_PART cam-kit "transform" SoTransform)) ;; Event callback node for mouse button and movement events. (define ev (new-SoEventCallback)) (-> root 'addChild ev) ;; Addition of the scene; something to look at. ;; "room.iv" must be in the current directory; you can ;; copy it from /mit/thingworld/Ivy/room.iv (-> root 'addChild (read-from-inventor-file "room.iv")) ;; Set scene graph and select arrow icon in viewer. (-> viewer 'setSceneGraph root) (-> viewer 'setViewing 0) ;; Global variables. (define *max-angular-velocity* (/ M_PI 200.0)) ;; in radians/sec ;; Matrix for translation of camera; initialize (define *translate-matrix* (new-SbMatrix)) (-> *translate-matrix* 'makeIdentity) ;; Matrix for rotation of camera about its origin (define *rotate-matrix* (new-SbMatrix)) ;; Initialize rotation matrix (-> *rotate-matrix* 'makeIdentity) ;; Global angles for rotations about x and y axes (define *x-angle* 0.0) (define *y-angle* 0.0) ;; Increment of speed depending on mouse button presses. ;; Note that this is negative, because the camera's negative z ;; axis goes into the screen. (define *forward-increment* -1) (define *forward-speed* (* 3 *forward-increment*)) ;; Has there been a mouse movement event yet? (define *last-event* #f) ;; Last positions of mouse within window (define *last-normalized-x* 0.0) (define *last-normalized-y* 0.0) ;; Callback for Location2Events. Very similar to problem 1. (define mouse-move-cb ;; Local variables; only allocated once for speed. (let ((xmat (new-SbMatrix)) (ymat (new-SbMatrix)) (zmat (new-SbMatrix)) (zvec (new-SbVec3f)) (xrot (new-SbRotation)) (xvec (new-SbVec3f 1 0 0)) (yrot (new-SbRotation)) (yvec (new-SbVec3f 0 1 0))) (lambda (user-data node) ;; Get event out of event callback node (let* ((event (SoLocation2Event-cast (-> node 'getEvent))) ;; Get normalized position within window of this event (loc (-> event 'getNormalizedPosition (-> viewer 'getViewportRegion))) ;; Change origin to be at center of window (delta (-> loc 'operator- (new-SbVec2f 0.5 0.5)))) ;; Update global angles depending on ;; mouse's position in window (set! *x-angle* (+ *x-angle* (* *max-angular-velocity* (-> delta 'operator-brackets 1)))) (set! *y-angle* (+ *y-angle* (* *max-angular-velocity* (-> delta 'operator-brackets 0)))) ;; Change SbRotations to use these new angles ;; Note that the angles are negated -- found this ;; through trial and error. (-> xrot 'setValue xvec (- *x-angle*)) (-> yrot 'setValue yvec (- *y-angle*)) ;; Set up the matrices using the computed SbRotations (-> xmat 'setRotate xrot) (-> ymat 'setRotate yrot) ;; Set the global rotation matrix equal to ;; the product of the x and y matrices in two steps. ;; Note that this is not incremental ;; updating as in the first problem. (-> *rotate-matrix* 'operator= xmat) (-> *rotate-matrix* 'operator*= ymat) ;; Creation of the world coordinate system vector ;; along which the camera will travel. ;; Create local coordinate system vector (-> zvec 'setValue 0 0 *forward-speed*) ;; "Magic" line to transform vector ;; into world coordinate system. ;; Found basically by trial and error. (-> (-> *rotate-matrix* 'inverse) 'multMatrixVec zvec zvec) ;; Set translation of final matrix to be this vector (-> zmat 'setTranslate zvec) ;; Update global translation matrix by this ;; vector through multiplication (-> *translate-matrix* 'operator= (-> zmat 'operator* *translate-matrix*)) ;; Set global transformation to be this matrix (-> transform 'setMatrix (-> *rotate-matrix* 'operator* *translate-matrix*)) ;; Update variables to allow idle callback to run (set! *last-event* #t) (set! *last-normalized-x* (-> delta 'operator-brackets 0)) (set! *last-normalized-y* (-> delta 'operator-brackets 1)))))) ;; MouseButtonEvent callback. Increases forward velocity of camera on ;; left mouse button press, decreases on middle button press. (define (mouse-press-cb user-data node) (let ((event (-> node 'getEvent))) (cond ((= 1 (SO_MOUSE_PRESS_EVENT event BUTTON1)) (begin (set! *forward-speed* (+ *forward-speed* *forward-increment*)) (-> node 'setHandled))) ((= 1 (SO_MOUSE_PRESS_EVENT event BUTTON2)) (begin (set! *forward-speed* (- *forward-speed* *forward-increment*)) (-> node 'setHandled)))))) ;; Callback for idle sensor. (define idle-cb (let ((xmat (new-SbMatrix)) (ymat (new-SbMatrix)) (zmat (new-SbMatrix)) (zvec (new-SbVec3f)) (xrot (new-SbRotation)) (xvec (new-SbVec3f 1 0 0)) (yrot (new-SbRotation)) (yvec (new-SbVec3f 0 1 0))) (lambda (user-data sensor) ;; Reschedule sensor (-> sensor 'schedule) (if *last-event* ;; Almost exactly the same code as in the mouse movement ;; callback for the case where the mouse has been present ;; in the window. (begin (set! *x-angle* (+ *x-angle* (* 2 *max-angular-velocity* *last-normalized-y*))) (set! *y-angle* (+ *y-angle* (* 2 *max-angular-velocity* *last-normalized-x*))) (-> xrot 'setValue xvec (- *x-angle*)) (-> yrot 'setValue yvec (- *y-angle*)) (-> xmat 'setRotate xrot) (-> ymat 'setRotate yrot) (-> *rotate-matrix* 'operator= xmat) (-> *rotate-matrix* 'operator*= ymat) ;; Change forward velocity to be faster in the idle ;; callback. This attempts to correct for the observed ;; effect that the camera moves faster when the ;; mouse is moving. (-> zvec 'setValue 0 0 (* 2 *forward-speed*)) (-> (-> *rotate-matrix* 'inverse) 'multMatrixVec zvec zvec) (-> zmat 'setTranslate zvec) (-> *translate-matrix* 'operator= (-> zmat 'operator* *translate-matrix*)) (-> transform 'setMatrix (-> *rotate-matrix* 'operator* *translate-matrix*))))))) ;; Set up callback for mouse movements (i.e. Location2Events). ;; Add callback to the EventCallback node. (define cb-info (new-SchemeSoCBInfo)) (-> cb-info 'ref) (-> (-> cb-info 'callbackName) 'setValue (new-SbString "mouse-move-cb")) (-> ev 'addEventCallback (SoLocation2Event::getClassTypeId) (get-scheme-event-callback-cb) (void-cast cb-info)) ;; Set up callback for mouse button events (i.e. MouseButtonEvents). ;; Add callback to the EventCallback node. (define cb-info (new-SchemeSoCBInfo)) (-> cb-info 'ref) (-> (-> cb-info 'callbackName) 'setValue (new-SbString "mouse-press-cb")) (-> ev 'addEventCallback (SoMouseButtonEvent::getClassTypeId) (get-scheme-event-callback-cb) (void-cast cb-info)) ;; Set up idle sensor callback. (define cb-info (new-SchemeSoCBInfo)) (-> cb-info 'ref) (-> (-> cb-info 'callbackName) 'setValue (new-SbString "idle-cb")) (define idle-sensor (new-SoIdleSensor (get-scheme-sensor-cb) (void-cast cb-info))) (-> idle-sensor 'schedule)
$Id: index.html,v 1.3 1996/01/19 01:34:12 kbrussel Exp $