Rendering with P3DC is straightforward but unfortunately it is a bit time dependent (it is hard to keep a consistent frame rate without threads). The model is as follows:
If you were to program a game using this engine, it would have some additional layers:
These things will be present in my first game "Fantasy Castle."
At the top level the drawing functions are pretty straight forward, however it is more interesting when we look at the individual drawing functions. Each function has the same basic internal structure, it operates as follows.
p3dc_draw_xxxx(p3dc_VIEW *vp, p3dc_XFRM *view, int flags, p3dc_XXXX *thing_to_draw)
From this function call, the parameters are used as follows:
The simplest case is drawing points although all of the others are basically the same. The code for points is simply:
void p3dc_draw_point(p3dc_VIEW *vp, p3dc_XFRM *view, int flags, p3dc_POINT *p) { __p3dc_total_parts++; /* Step 1: Light the point */ if (flags & P3DC_DRAW_SHADED) { p3dc_light_point(e, p); } /* Step 2: Transform the point into view space */ p3dc_xfrm_vrtx(NULL, view, p->p0); /* Step 3: Clip the result. */ if ((flags & P3DC_DRAW_CLIPPED) && (p3dc_clip_point(p->p0))) return; __p3dc_drawn_parts++; /* Step 4: Draw the point. */ __hw_draw_point(e, p); }
In this code the variable p3dc_total_parts is used to give a number of "things" rendered in a given prepare/flip block. As you can see the flag P3DC_DRAW_SHADED is used to determine if the point should be lit according to a simple ambient light model. If set the point is lit using the function p3dc_light_point. The code in p3dc_light_point takes into account the origin point of the camera, the position of the lights (attached to the camera structure) and the position of the point to come up with an intensity modifier for the point's color.
No, not the 3D kind like 3Dfx Voodoo, the ones that let you render 3D scenes quickly and get on with things.
P3DC contains a number of techniques that are used to speed up the rendering pipeline, the most common technique is pre-caching results. There are three results that benefit greatly by being saved:
This section provides a catalog of the various functions in Project: 3D Craft.
p3dc_prepare_frame(p3dc_CLR *color)
Returns: void
This function prepares the frame for drawing. Generally it clears the entire screen to the color specified. Normally, you would want to specify whatever the default fog color is here. This is so that when fogging you can ignore things that are "holes" (nothing is rendered in front of them).
p3dc_flip_frame(p3dc_BOOL wait)
Returns: void
This function causes the last frame drawn to be "flipped" to the front. The current frame is then pushed into the stack of buffers available to draw on. Usually there will be two buffers the "shown" buffer and the "hidden" buffer. By double buffering like this we don't get nasty drawing artifacts if the screen changes while the monitor has displayed it.
p3dc_clear_region(p3dc_CLR *color, p3dc_FLOAT x, y, width, height)
Returns: void
This function clears a region of the screen to the indicated color. This is useful when you're not using the whole screen for rendering (say a cockpit window or rear view mirror).
p3dc_adjust_color(p3dc_CLR *color, p3dc_FLOAT factor, p3dc_CLR *result)
Returns: void
This function multiplies the RGB components of a color struct by a factor that is presumably less than 1.0. This is used in the lighting code to adjust the color of a vertex when a polygon is off angle from the light.
p3dc_draw_point(p3dc_VIEW *vp, p3dc_XFRM *view, int flags, p3dc_POINT *point)
Returns: void
This function renders a point on the screen. The point is clipped if it falls outside the visible screen boundary.
p3dc_xfrm_vrtx(p3dc_VRTX *result, p3dc_XFRM *transform, p3dc_VRTX *src)
Returns: void
This function applies the 3D transformation in transform to the vertex in src. Note that technically the vertex is a 4x1 column vector and the transform is a 4 x 4 matrix, however we take advantage of the fact that most of the time we're actually doing the transform in 3 space and don't bother multiplying the last row (0, 0, 0, 1) by the vector to get the value 1.0 in the w output.
Now there are a couple of things to note about this routine. First, the transform carries around with it an identifier called id. This identifier is changed any time the transform is changed. The vertex carries around both its original co-ordiantes and its transformed co-ordinates. The transformed co-ordinates include a value xid which is the identifier of the transform last used on the vertex's co-ordinates. Thus if we're trying to transform this vertex a second time with the same transform, nothing really happens and the function returns.
If the value result is non-NULL, then the transformed vertex co-ordinates are stored in the results "world" co-ordinates. This is how you move a vertex from one co-ordinate space into another co-ordinate space and it is what happens to models when they are transformed into world co-ordinates.
p3dc_clip_line(p3dc_LINE *line, float *delta1, float *delta2)
Returns: int -- The integer returned is a clip code, the defined codes are:
- CLIP_NONE - the line clipped off the screen so discard it.
- CLIP_A_B - the line is completely on screen so just draw it.
- CLIP_A_X - the first vertex on the line (a) was on screen but the second vertex (b) needed to be adjusted.
- CLIP_X_B - the second vertex on the line (b) was on screen but the first vertex (a) needed to be adjusted.
- CLIP_X_X - both vertices on the line needed to be adjusted.
- CLIP_ERROR - one or both of the vertices had not be transformed.
This function clips a line based on its transformed vertices. It uses the value w in the transformed version of the co-ordinates to clip the endpoints to values -w <= x <= w, -w <= y <= w, 0 <= z <= w. The results are stored in two p3dc_PNT4 structures c1 and c2 (clipped vertex 1 and clipped vertex 2). When the p3dc_draw_line function is called it checks the clip code and if it needs to use an adjusted version of the line structure it creates a temporary line structure and uses that.
p3dc_clip_point(p3dc_POINT *point)
Returns: int - The integer clip code for the point, only one of:
- CLIP_NONE - point is outside the visible screen, so drop it.
- CLIP_A_B - point is visible so plot it.
This function clips a point structure and returns a code to indicate if the point should actually be drawn. There is one accellerator here in that the vertex outcode is cached for a particular transform so that the clipping routine can do its thing.
$Id: p3dc.htm,v 1.1 1999-08-17 18:32:50-07 cmcmanis Exp $ $Log: p3dc.htm,v $ Revision 1.1 1999/08/18 01:32:50 cmcmanis Initial revision Revision 1.1 1999/07/26 03:11:46 cmcmanis Initial revision