The 3D library


The 3D gamelib component is for displaying wirefrae 3D objects. It doesn't do a hidden surface removal, but it does Z-buffering when displaying. The world coordinates use a right-hand coordinate system for objects, with the XY plane being the base plane and Z indicating height. The camera coordinate system is also a right-hand coordinate system, using the X axis as left/right, the Y axis as front/back and Z as up/down.

Starting with version 0.3, there are two cameras available. The "old" camera, capable of moving in X, Y and Z, always turning in the XY plane and the "new" (or 3D) camera, having 6 degrees of freedom.

Defining volumes

All game objects seem are defined as volumes, using the DEFVOLUME macro.

DEFVOLUME takes three required parameters, one optional parameter and a list of extra slots to define.

(DEFVOLUME class name vertex count (surface ...) [visible surfaces] [extra slot]...)

The surface spec is as follows:
(type vertex 1 vertex 2 vertex 3 ...)

Vertex specs should be in clock-wise order around a normal seen from outside the volume. Declaring the surfaces is, ideally, done from low to high, though surfaces that will never be seen can be placed last and stopped from ever displaying by using the "visible surfaces" limiter.

The extra slots are user-defined slots for the shape class (ammunition count or what-have-you).

Base and transform designators

Starting from version 0.3, there is a "fully 3D" camera available, necessitating some new concepts and accessors (hopefully seamlessly integrated into the whole). The most developer-visible of these are "base designators" and "transform designators".

A "base designator" is either 3-element vector, array or list.

A "transform designator" is a 3-element sequence of "base designators", specifying the (world coordinate) vectors from the camera origo to [ 1, 0, 0 ], [ 0, 1, 0 ] and [ 0, 0, 1 ].

What will be shown and how?

All game objects that will be displayed should be added to the *ALL-SHAPES* list. All game objects that have independent movement should be added to the *MOVING-OBJECTS* list.

In DRAW-ALL-SHAPES, all objects are sorted in falling average y-coordinate in camera space (effectively, that is depth). This ensures that as we traverse the list, we can stop trying to display items once we hit a negative y-average.

After that, the DRAW-SHAPE gf is called. For most shapes, this will cause an update of the shape's cam-vertexes and then a list of all the sides to be displayed (the lowest of the shape's to-show limit or all defined sides) is generated, culled by a visibility test and sorted by y-average. After that, DRAW-SHAPE is called for each flat surface of the shape.

When applied to a flat, DRAW-SHAPE starts by checking if all vertexes of the flat are visible. If all vertexes are visible, we convert each vertex to an XY coordinate in screen-space. If not, we find a vertex that is visible, then traverse the flat vertex by vertex, inserting extra vertexes on the screen-edge where necessary and culling vertexes outside the display, then return a list of XY coordinates for each vertex-to-display. After all that, we call XLIB:DRAW-LINES twice on the resulting coordinates, once filling the outline with white, the second just generating the black outline.

Spheres is handled slightly differently. Looking at the sphere code, I am suddenly not entirely sure that we are doing it correctly.

Exported symbols

Global variable that contains the "player camera".
Global variable that contains the X display.
List of fonts, in falling preference order. Only consulted on initial connection and window creation.
Separation, in world-space units, for a "grid on the floor".
Tabulation of scores to object type. Used by score-from-object.
Constant for the height and width of the X pixmap that all 3d-rendering is made on. Also guides the size of the X window it will be displayed in.
(3d-camera x y z &optional transform)
Return a newly constructed 3d-camera object, at position [ X, Y, Z ]. If an optional transform designator is passed it, the camera will use this as its transform, otherwise it will use a unit transform.
Shape class. 8 corners, 6 sides.
(camera-to-screen coord cam &optional clip)
Convert from coord (in camera-space) to screen coordinates, return these as a list (X Y). If clip is non-nil and the coordinates are outside the screen, return NIL.
(collide-p object-1 object-2)
Generic function, determine if two objects are touching. The default methods check for camera colliding with anything and anything colliding with anything. The camera-collision is effectively "is the camera inside a bounding box, infinitely high, of the second object's footprint". The generic collision compares the bounding boxes of the footprints and returns T if there is an overlap.
(collide-action object-1 object-2)
Generic function, determine what happens when two objects collide. The default method prints some diagnosting on *STANDARD-OUTPUT*.
(defvolume name vertexes sides &optional show-limit &rest extra-slots)
Helper macro. Define a shape-class. See defining volumes
Draw all shapes in *ALL-SHAPES*.
(draw-grid camera)
Draw a grid with lines parallel to the X and Y world axis.
(draw-shape shape)
Generic function.
Shape class, superclass for all flat surfaces.
(full-move camera displacement)
Displace the camera by displacement, a 3D coordinate (using the camera's position as origo, expressed in the camera coordinate system). The displacement will be transformed into world coordinates before being applied.
(get-centre shape &optional camera-p)
Get the geometric centre of a shape. If camera-p is non-nil, return results in camera-space coordinates, otherwise return coordinates in world-space.
(move object &optional distance)
Generic function. Move an object.
Shape class
(score-from-object object) Helper function, checks *score-table* for an entry for the class of object and returns that (or 0).
Shape super-class.
Shape class
Shape class (flat)
Shape class (flat)
(turn object angle &optional angle)
Generic function. Turn object through angle. If the object is "the standard camera" it will be turned around the Z axis. A 3D camera will default to turn around teh Z axis unless otherwise specified. Axes are designated by the :x :y or :z keywords. The angle is specified in radians.
Shape class, superclass for all volumes.
(world-to-camera world-coord camera &optional result)
Compute camera-sapce coordinates for a given coordinate and return that. If result is provided, also side-effect-update this and return that object as the result (lessens object-creation slightly).
(world-to-screen world-coord camera &optional clip)
Converts a world coordinate to a screen XY. Clips in te same manner as camera-to-screen.