[comp.sys.mac.programmer] Graf3D and What I Discovered Therein - part 3

mlab2@kuhub.cc.ukans.edu (12/01/90)

Graf3D and What I Discovered Therein - part3

Well, I must say I'm posting this all a piece at a time.  I don't have any of
this written all out in one place.  So if you want this, here it is, capture
it.  Also, I'll say again that I am not an expert with this at all.  I have
only experimented with it for a couple of months back when I was first learning
to program the Mac (and learning Pascal too).  So, feel free to post
corrections of any of my errors and take my 'tutorial' as a starting point
only.  I just saw all the confusion about Graf3D and thought, "Well, I could
explain a little bit of it."

Drawing in 3D-

As I may have hinted at before, all drawing is done 'wireframe' with Graf3D. 
You set up a group of 3D points and use Move3D, MoveTo3D, LineTo3D, etc. to
display your drawing.  I'm sure a guru could take the 2D equivalents of these
screen points and create regions and paint them etc.  For my own purposes, this
would clearly have been too slow.

So, how did I optimize Graf3D within the bounds allowed by Graf3D?

Setting up your 3D points-

Consider a simple cube.  Counting up the vertices gives us 8 points.  If we set
up an array [0..7] of Point3D, we can initialize all these points and then
repeatedly connect-the-dots.  Calls to Yaw, Pitch, etc, and moving the 'eye'
around in space will give us different views of this simple cube.

The scale you use for the cube is fairly arbitrary.  Lets use a cube with a
side of length 100 for simplicity.  In your initialization routine you caould
call this:

  var
    cubeVertices: array [0..7] of Point3D;

  begin
    SetPoint3D(cubeVertices[0], 0, 0, 0);
    SetPoint3D(cubevertices[1], 100, 0, 0);
    SetPoint3D(cubeVertices[2], 100, 100, 0);
    SetPoint3D(cubeVertices[3], 0, 100, 0);
    SetPoint3D(cubeVertices[4], 0, 0, 100);
    SetPoint3D(cubeVertices[5], 100, 0, 100);
    SetPoint3D(cubeVertices[6], 100, 100, 100);
    SetPoint3D(cubeVertices[7], 0, 100, 100);
  ...
  end;

An alternate (and perhaps quicker) way of doing this would be to have set up an
array of Fixed [0..7, 0..2].  This way, you never create the 3D points, but
assign the x, y, and z coordinates of each verticee.  MoveTo3D and LineTo3D
(etc) expect you to pass the fixed x, y, and z coordinates anyway.  Using
Point3D's is convenient, but you'll still have to "dissect" the corrdinates out
of these points when you call MoveTo3D, etc.

A 'convenient' way around this hassle would be to create your own procedures:
As an example:
  Long way:

    MoveTo3D(cubeVertices[i].x, cubeVertices[i].y, cubeVertices[i].z);

  A nice procedure:

    procedure MoveTo3DPt (thePoint: Point3D);
    begin
      MoveTo3D(thePoint.x, thePoint.y, thePoint.z);
    end;

    -now simply call-
    MoveTo3DPt(cubeVertices[i]);

I think I'll use this second way here.  (You could make a whole library of
routines that translate 3D points, etc.)
So, you've set up your 3D port (from part 2 of this little piece), and you've
initialized the 8 vertices of your cube, - here is a simple loop to use the
mouse to rotate the points from the perspective of the eye.

var
  mousePt:Point;     {A STANDARD QuickDraw point that is}

{------}

    procedure MoveTo3DPt (thePoint: Point3D);
    begin
      MoveTo3D(thePoint.x, thePoint.y, thePoint.z);
    end;

{------}

    procedure LineTo3DPt (thePoint: Point3D);
    begin
      LineTo3D(thePoint.x, thePoint.y, thePoint.z);
    end;

{------}

begin
 
  ...
 
  repeat
    GetMouse(mousePt);
    Roll((256 - mousePt.h) * 3276);  {3276 is about 1/10 of a degree, thus
                                      on a 512 Mac screen, +/- 25 degrees
                                      is your roll velocity}
    origin.z:=origin.z+mousePt.v;    {We'll use the vertical position of the
                                      mouse to change the z coordinate of
                                      the origin (see part 2 for origin}
    gPort3D.eye:=origin;             {Now we set the 'eye' of the 3D port
                                      to the new location of the var origin}
    FillRect(gPort3D.ViewRect, black);  {Erase other points drawn}
    MoveTo3DPt(cubeVertices[0]);
    LineTo3DPt(cubeVertices[1]);
    LineTo3DPt(cubeVertices[2]);
    LineTo3DPt(cubeVertices[3]);
    LineTo3DPt(cubeVertices[0]);
      {We have just drawn the bottom of the cube}
    MoveTo3DPt(cubeVertices[4]);
    LineTo3DPt(cubeVertices[5]);
    LineTo3DPt(cubeVertices[6]);
    LineTo3DPt(cubeVertices[7]);
    LineTo3DPt(cubeVertices[4]);
      {We have now drawn the top of the cube, now to connect the top & bot}
    MoveTo3DPt(cubeVertices[0]);
    LineTo3DPt(cubeVertices[4]);
    MoveTo3DPt(cubeVertices[1]);
    LineTo3DPt(cubeVertices[5]);
    MoveTo3DPt(cubeVertices[2]);
    LineTo3DPt(cubeVertices[6]);
    MoveTo3DPt(cubeVertices[3]);
    LineTo3DPt(cubeVertices[7]);
  until button;     {clicking the mouse button exits the program}
end.

So, try that one out.  It's bits from a little test program I wrote.  You'll
also have to have part 2 of my posts to do the port setting up stuff.  I'm
hoping to that this is enough to get most everyone started.

I could post more, but mostly program specific stuff.  I will say that my
biggest annoyance with Graf3D was that it is difficult (impossible?) to
actually rotate both the cube AND the eye at the same time.  As I recall,
rotation calls to Yaw and the like actually rotate space about the point
(0,0,0).  If you have, for example, two cubes located in differnt places in
space, calls to pitch will rotate BOTH cubes teeter-totter like about the point
(0,0,0) of space.  I could only imagine setting up SEPERATE 3D grafports for
each cube and copybitting the two (via XOr) to a single 2D grafport on the
screen.  Slow.

If anyone has any questions, post them to the net (or mail me I suppose).  I
can't reply to mail sent to me, but I could post more on a particular aspect of
Graf3D to the net.

One person mailed me about rotating 3D waveforms and displaying them.  That
would probably be fairly easy to do given the code I've posted.  Define the
waveform as a chain of 3D points stretching across the point (0,0,0).  Move the
eye out away from (0,0,0) but look TOWARD the center of space.  Yaws and Rolls
will gimble the waveform about.  Redraw (point to point) the waveform after
each transformation.  You can scale the waveform by simply moving the eye of
the 3D port toward and away from the center of space.

Cheerio.  I'll check the net for requests.

john calhoun

oster@well.sf.ca.us (David Phillip Oster) (12/08/90)

In article <27177.2756a557@kuhub.cc.ukans.edu> mlab2@kuhub.cc.ukans.edu writes:
_>I could post more, but mostly program specific stuff.  I will say that my
_>biggest annoyance with Graf3D was that it is difficult (impossible?) to
_>actually rotate both the cube AND the eye at the same time.  As I recall,
_>rotation calls to Yaw and the like actually rotate space about the point
_>(0,0,0).  If you have, for example, two cubes located in differnt places in
_>space, calls to pitch will rotate BOTH cubes teeter-totter like about the point
_>(0,0,0) of space.  I could only imagine setting up SEPERATE 3D grafports for
_>each cube and copybitting the two (via XOr) to a single 2D grafport on the
_>screen.  Slow.

There is nothing magic about what Graf3D is doing. It is just doing simple
matrix multiplication to do the transformation of the coordinates of the
3d wireframe cube to its final position. Any elementary book on computer
graphics gfoes into this process in excuricating detail. I like "Interactive
Computer Graphics" by Giloi. Just make sure to use the Fract trig routines
(inside Mac vol 4) and right your matrix mutiply routine to use Fixed 
numbers.

A cute trick: given a quadrilateral in 3-space, do your Line3To()s inside
BeginRegion(), EndRegion()s. Then, uyou can do a PaintRegion to actually
draw the face in "three space".  Regions work better than Polygons for this,
since pin a polygon, the pen hangs down, but in a region, the pen stays
within the region.
-- 
-- David Phillip Oster - At least the government doesn't make death worse.
-- oster@well.sf.ca.us = {backbone}!well!oster