Computer Graphics Workshop '96 Problem Set 3


Problem 1 - Compute a point on a torus

This problem set will lead you through the steps to making a torus. We have chosen a torus because it has several parameters which can be modified; if you have done something like this before, you may create some other type of shape (but not a sphere, cone, cube, or cylinder!). The fourth problem set presupposes the existence of some torus code, so keep this in mind.

The first step is to write the function compute-point-on-torus. The arguments to this function are:

This function should return a Scheme vector of the x, y, and z coordinates. To make this vector, use the vector function:

> (define x 2)
> (define y 3)
> (define z 4)
> (vector x y z)
#(2 3 4)
You may want to include the following definition in your program:
(define M_PI 3.14159265358979323846)
Problem 2 - Compute polygons on the surface of a torus

Next write the function compute-torus-polygons. This function takes at least the arguments that compute-point-on-torus does, plus more if you write the function in an iterative fashion. This function, when called, should return a list of vectors which are the coordinates of the quadrilaterals on the surface of the torus.

Problem 3 - Make a torus

Now we can put the torus together. We will need at least three nodes to do so:

You will probably want to add more nodes to the above scene graph (such as an SoMaterial node) to be able to change more parameters of the torus (like its color).

We want the torus to be an "object" that we can interact with in the same way we interact with Open Inventor objects from within Scheme. You should use this wrapper for the "->" function to allow Scheme and C++ objects to share the same syntax, in a similar fashion to the object system used in the adventure game in 6.001.

;;; SICP-ish simple object system for dealing similarly
;;; with Scheme and C++ objects using "send"

(define (send object message . args)
  (if (C++-object? object)
      (eval `(-> ,object ',message ,@args))
      (let ((method (get-method object message)))
	(if (not (no-method? method))
	    (apply method (cons object args))
	    (error "No method named" message)))))

(define (get-method object message)
  (object message))

(define (no-method method)

(define (no-method? method)
  (eq? method 'no-method))
Using this wrapper, and the structure for your Scheme objects described below, you can now use this "send" function to send messages to native Inventor objects and objects you have written in Scheme in the same way. For example:

> (define root (new-SoSeparator))
> (send root 'ref)
> (define torus (new-Torus 10 3 20 10)) ;; new-Torus 
                                        ;; defined below
> (send root 'addChild (send torus 'getGeometry))
Now define the "new-Torus" function, which is the constructor for a new Torus object:
(define (new-Torus major-radius 
  (define root (new-SoSeparator))
  (send root 'ref)

;;; ... Insert code here to build scene graph ...

  (lambda (message)

    ;;; Method to return pointer to root of scene graph

    (cond ((eq? message 'getGeometry)
	   (lambda (self)

	  ((eq? message 'generate)
	   (lambda (self)
	     (let* ((points (compute-torus-polygons major-radius
		    (num-points (length points)))
	       (set-mfield-values! (send coords 'point) 0 points)
	       (send (send coords 'point) 'setNum num-points)

	       ;; update the values in the face set: 
	       ;; (num-points / 4) polygons of four vertices.

	       (let loop
		   ((i 0))
		 (send (send face-set 'numVertices) 'set1Value i 4)
		 (if (< i (/ num-points 4))
		     (loop (1+ i))))
	       (send (send face-set 'numVertices) 'setNum
		     (/ num-points 4)))))
	  ;;; Insert code here for other methods which include 
	  ;;; setColor, setMajorRadius, setMinorRadius, 
	  ;;; setHorizResolution, and setVertResolution.

	  ;;; After all other methods' definitions,
	  ;;; if we haven't been able to find this method,
	  ;;; signal an error

	  (else (no-method message)))))
Each method takes the current object as its first argument (i.e. (lambda (self arg1 arg2 ...))). All of the "set" methods end with a call to
(send self 'generate)
This allows the torus object to be operated upon in a similar fashion to other Inventor objects; when a "field" in the torus is changed (i.e. the major radius) the scene graph is automatically redrawn to reflect that change.

Problem 4 - Make the torus look better

Once you have the torus on the screen, you may notice that it looks like it is made out of tiles; it is not smoothly shaded. This happens because Inventor does not know that it should smoothly interpolate the color between adjacent polygons.

Look at the man page for SoShapeHints. It has three fields: vertexOrdering, shapeType, faceType, and creaseAngle. The creaseAngle field specifies (in radians) the maximum angle between adjacent polygons before they stop being smoothly shaded together. In Inventor 2.1 the default crease angle is 0; in previous releases it was 0.5. If your torus is not smoothly shaded, add a shape hints node to your scene graph and increase its crease angle to 0.6 or 0.7.

To make the torus render faster, you can enable backface culling; this prevents polygons not facing the camera from rendering. It is only appropriate to enable this feature for solid shapes; but since normally you can not see the "inside" of the torus, we can use it here.

Add a shape hints node to your graph and set the shapeType field to be SOLID:

(-> (-> shape-hints 'shapeType) 'setValue SoShapeHints::SOLID)
Now, depending on whether you ordered the vertices in your polygons clockwise or counterclockwise with respect to the outside of the torus, set the vertexOrdering field to CLOCKWISE or COUNTERCLOCKWISE:
(-> (-> shape-hints 'vertexOrdering) 'setValue
;; or:
(-> (-> shape-hints 'vertexOrdering) 'setValue 
(If you have the wrong ordering, you will know: the inside of the torus will be visible on the screen, and will not look like a physically realizable object.) Switch back and forth between the SOLID and UNKNOWN_SHAPE_TYPE shape types. Note the difference in rendering speed.

Back to the CGW '96 home page

$Id: index.html,v 1.9 1996/01/17 22:39:40 kbrussel Exp $