Assignment 6: Due Date: Oct 23
Develop a 3D graphics package based on primitives to
Specify Projection: set_projection_type() set_center_of_projection() set_direction_of_projection()
Specify Projection Plane: set_view_plane_normal() set_view_reference_point()
Specify Window and View Volume: set_view_up_vector() set_window(umin,umax,vmin,vmax) set_front_back_clipping_plane(F,B)
and providing standard move and draw primitives: move_3d() lineto_3d() line_3d(x1,y1,z1,x2,y2,z2) text_3d(string)
The usual screen oriented features such as set_viewport() and end__frame() should also be supported of course. You do not need to implement 3D clipping.
Demo for Assignment 6
The demo should show a rectangular (not cubic) box. It should be viewed from several viewpoints, both parallel and perspective.
The user can request a view on the fly and can specify whether it is to be parallel or perspective, and what the parameters should be.
The demo should use the X11 driver.
As usual, the exact names and and functionality is up to you. For example, as we will point out, the first three routines (for specifying the projection type) can easily be combined into one.
The assignment relies crucially on Assignment 5 which must be done first.
HINTS FOR ASSIGNMENT 6
It is important to distinguish between 2D and 3D primitives. By doing so carefully you can fully utilize your working 2D library. Here are a few suggestions.
0. ORGANIZATION:
Create a 3D library libd3d.a which contains all of the 3D calls, and which is distinct from the device indep 2d library lib2d.a and from the device dependent driver library libX11.a. A compilation will then have the form:
cc hw6.c lib3d.a lib2d.a libX11.a -lm -lX11 -o hw6
where hw6.c is a user program that utilizes the 3D library. Here hw6 will draw a rectangular box. The 3D library will contain no device dependencies. The libraries can of course be simple files: lib3d.o and lib2d.o.
lib3d.a will convert a view from a general viewing specification to a view in a normalized view volume - a pyramid or a 2x2x1 rectangular box as case maybe, followed by clipping (optional) and projection to a viewport on the screen (not optional).
Note that at the end of transformation to the canonical view volume, the window on the view plane will have been shrunk to a square window with dimensions (-w, -w, w,w). For the parallel case w = 1 always (i.e. it is a 2x2 square), but for the parallel case, w < 1 and needs to be determined - in fact w equals the distance from the projection plane to the origin since the pyramid has a 45 degree angle. You can now use your 2D package to complete the transformation to the screen by simply calling set_window_2d(-w,-w,w,w) and drawing 2D lines using the transformed and projected end points of the 3D lines (omitting the third coordinate of course).
1. VIEW SETUP COMMANDS:
init_graphics_3d()
set_projection_type() set_cop_or_dop() - or just set_projection(type,cop/dopx,cop/dopy,cop/dopz)
set_view_reference_point() set_view_plane_normal() set_view_up_vector() set_window_3d() set_front_back_clipping_plane()
These all need to be called in order to establish a view. They draw nothing of course but they allow a transformation to be computed. Basically you would compute two matrices: M1 = transform to the standard view volume M2 = projection from view volume to screen.
As soon as the above calls have occurred, the code should recognize that all data is available to compute M1 and M2 and should call
compute_transformations()
which computes M1, M2. A reasonable assumption is that after an endframe() the same view is still in effect, but the user is free to change some of the parameters, with the others being as in the previous frame. For example a viewer might change COP to zoom in on an object.
In addition to computing M1,M2, compute_transformation should also compute the bottom right and top left corners of the TRANSFORMED window on the view plane. For parallel these are -1,-1 and 1,1 but for perspective they are something else. In general we might call them utmin,vtmin and utmax,vtmax (the "t" denotes transformed).
We are now ready to initialize the 2D package, which should be done by compute_transformation or something called in conjunction with it.
init_graphics_2d(); /* your old init_graphics() if you had one */ set_window_2d(utmin,utmax,vtmin,vtmax); /* The USER (not you, the DEVELOPER) should never call set_window_2d. In fact he wont know there is a 2d package in use! Note that the 2D window will need to be reset in perspective cases every time the view information changes. For parallel case it stays fixed. */
2. DRAWING COMMANDS:
move_3d(x,y,z) line_to_3d(x,y,z) line_3d(x1,y1,z1,x2,y2,z2) text_3d(string) endframe_3d()
These call one or more of:
apply_transform(P), to computed transformed points Pt = M1*P clip_line(Pt1,Pt2,PCt1,PCt2), which clips any transformed line Pt1->Pt2 returning the 3D clipped endpoints PCt1, PCt2 of the piece to be drawn (if any). project_point(P,x,y), applies the transform M2*P and divides out the 4th coord to get x,y
move_2d(x,y), line_to_2d(x,y), text_2d(string), endframe_2d()
Note that text_3d(string) will draw the string horizontally on the screen with its first character at the current 3D point. It completely ignores issues of slanting the text, projecting text etc. However it does mean you can label faces of a cube or the like!
You may be able to use the 2D endframe() and text() directly, but I have added an _2d to their names for clarity.
3. LOCATION OF IMAGE ON FINAL SCREEN:
One call is shared by both 2D and 3D packages and is the only 2D routine the user will ever see. It is
set_viewport(Umin,Umax,Vmin,Vmax)
which says where on the FINAL screen the total window is to appear. The Umin etc here are NDC coordinates on the logical unit square final 2D screen - not to be confused with the other 3D screen - the window set in the set_window_3d command, or the final transformation of that window which is a 2x2 square in parallel or a smaller square in perspective cases (that square is the 2D window, not the viewport).
By default it will cover the whole screen, and the user therefore need never call it unless he wants smaller viewports.