home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 December
/
simtel1292_SIMTEL_1292_Walnut_Creek.iso
/
msdos
/
ddjmag
/
ddj9002.arc
/
STROYAN.LST
< prev
Wrap
File List
|
1989-12-26
|
18KB
|
599 lines
_THREE-DIMENSIONAL GRAPHICS USING THE X-WINDOW SYSTEM_
by Michael Stroyan
[LISTING ONE]
/* example.c - An example of three dimensional graphics using Xlib. */
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <math.h>
#include <stdio.h>
#include "double_buffer.h"
typedef double Transform[4][4];
typedef double Point[3];
Display *display; /* display connection */
Window window; /* window identifier */
GC gc; /* graphics context */
XColor colors[4]; /* colors to draw with */
double_buffer_state *dbuf_state; /* state record for double buffer utilities */
double_buffer = 1; /* Whether to use double buffering */
unsigned int width, height; /* last known window size */
Transform model; /* transform from world to modelling coordinates */
Transform view; /* transform from modelling to VDC coordinates */
Transform device; /* transform from VDC to device coordinates */
Transform composite; /* transform from world to device coordinates */
Transform motion; /* transform for modelling motion */
identity(transform)
/* Set a Transform matrix to an identity matrix. */
Transform transform; /* transform to operate on */
{
register int i, j;
for (i = 0; i < 4; i++)
for (j = 0; j < 4; j++)
transform[i][j] = (i == j);
}
rotate_X(transform, angle)
/* Set a Transform matrix to a rotation around the X axis. */
Transform transform; /* transform to operate on */
double angle; /* angle in radians to rotate by */
{
identity(transform);
transform[1][1] = transform[2][2] = cos(angle);
transform[2][1] = -(transform[1][2] = sin(angle));
}
rotate_Y(transform, angle)
/* Set a Transform matrix to a rotation around the Y axis. */
Transform transform; /* transform to operate on */
double angle; /* angle in radians to rotate by */
{
identity(transform);
transform[0][0] = transform[2][2] = cos(angle);
transform[0][2] = -(transform[2][0] = sin(angle));
}
transform_point(transform, p, tp)
/* Apply a Transform matrix to a point. */
Transform transform; /* transform to apply to the point */
Point p; /* the point to transform */
Point tp; /* the returned point after transformation */
{
int i, j;
double homogeneous[4];
double sum;
for (i = 0; i < 4; i++) {
sum = 0.0;
for (j = 0; j < 3; j++)
sum += p[j] * transform[j][i];
homogeneous[i] = sum + transform[3][i];
}
for (i = 0; i < 3; i++)
tp[i] = homogeneous[i] / homogeneous[3];
}
cross_product(v1, v2, c)
/* Compute the cross product of two vectors. */
Point v1, v2; /* the vectors to take the cross product of */
Point c; /* the result */
{
c[0] = v1[1] * v2[2] - v1[2] * v2[1];
c[1] = v1[2] * v2[0] - v1[0] * v2[2];
c[2] = v1[0] * v2[1] - v1[1] * v2[0];
}
backface(p1, p2, p3)
/* Determine if a polygon is a back face of an object */
Point p1, p2, p3; /* the first three vertices of a polygon */
{
Point v1, v2, c;
/* This relies on the first three vertices of each face being clockwise
* around a convex angle or counter-clockwise around a concave
* angle as viewed from the front of the face. */
v1[0] = p2[0] - p1[0];
v1[1] = p2[1] - p1[1];
v1[2] = p2[2] - p1[2];
v2[0] = p2[0] - p3[0];
v2[1] = p2[1] - p3[1];
v2[2] = p2[2] - p3[2];
cross_product(v1, v2, c);
return(c[2] > 0);
}
concatenate_transforms(transform1, transform2, result)
/* Use matrix multiplication to combine two transforms into one. */
Transform transform1, transform2; /* the transforms to combine */
Transform result; /* the new combined transform */
{
register int i, j, k; /* index variables */
Transform temporary; /* a temporary result */
/* Using a temporary result allows a single transform to be passed in
* as both one of the original transforms and as the new result. */
for (i = 0; i < 4; i++) {
for (j = 0; j < 4; j++) {
temporary[i][j] = 0.0;
for (k = 0; k < 4; k++) {
temporary[i][j] += transform1[i][k] * transform2[k][j];
}
}
}
for (i = 0; i < 4; i++)
for (j = 0; j < 4; j++)
result[i][j] = temporary[i][j];
}
init_X(argc, argv)
/* Initialize the X window system */
int argc; /* the number of program arguments */
char *argv[]; /* an array of pointers to program arguments */
{
XEvent event; /* holds X server events */
static XSizeHints xsh = { /* Size hints for window manager */
(PPosition | PSize | PMinSize), /* flags */
300, /* height */
300, /* width */
200, /* minimum height */
200, /* minimum width */
5, /* x coordinate */
5 /* y coordinate */
};
static XWMHints xwmh = { /* More hints for window manager */
(InputHint | StateHint), /* flags */
False, /* input */
NormalState, /* initial_state */
0, /* icon pixmap */
0, /* icon window */
0, 0, /* icon location */
0, /* icon mask */
0, /* Window group */
};
static XClassHint xch = { /* Class hints for window manager */
"example", /* name */
"EXample" /* class */
};
XGCValues gcvalues;
if ((display = XOpenDisplay(NULL)) == NULL) {
fprintf(stderr, "Can't open %s\n", XDisplayName(NULL));
exit(1);
}
window = XCreateSimpleWindow(display,
DefaultRootWindow(display),
xsh.x, xsh.y, xsh.width, xsh.height, 2,
WhitePixel(display, DefaultScreen(display)),
BlackPixel(display, DefaultScreen(display)));
XSetStandardProperties(display, window, "Example", "Example",
None, argv, argc, &xsh);
XSetWMHints(display, window, &xwmh);
XSetClassHint(display, window, &xch);
XSelectInput(display, window,
StructureNotifyMask |
ExposureMask |
ButtonPressMask |
Button1MotionMask |
PointerMotionHintMask);
XMapWindow(display, window);
XFlush(display);
do {
XNextEvent(display, &event);
} while (event.type != MapNotify || event.xmap.window != window);
gc = XCreateGC(display, window, 0, &gcvalues);
XSetState(display, gc,
WhitePixel(display, DefaultScreen(display)),
BlackPixel(display, DefaultScreen(display)),
GXcopy, AllPlanes);
/* black */
colors[0].red = 0;
colors[0].green = 0;
colors[0].blue = 0;
/* white */
colors[1].red = 65535;
colors[1].green = 65535;
colors[1].blue = 65535;
/* green */
colors[2].red = 0;
colors[2].green = 40000;
colors[2].blue = 0;
/* yellow */
colors[3].red = 65535;
colors[3].green = 65535;
colors[3].blue = 0;
dbuf_state = start_double_buffer(display,
DefaultColormap(display, DefaultScreen(display)), 2, colors);
if (dbuf_state == NULL) {
fprintf(stderr, "Couldn't allocate resources for double buffering\n");
exit(1);
}
XSetPlaneMask(display, gc, dbuf_state->drawing_planes);
}
init_transforms()
/* Initialize transformations for modelling, viewing, and device mapping. */
{
Window root;
int x, y;
unsigned int b, d;
identity(model);
identity(view);
view[2][2] = 2.0;
view[2][3] = 1.0;
view[3][2] = 29.0;
view[3][3] = 15.0;
XGetGeometry(display, window, &root, &x, &y, &width, &height, &b, &d);
identity(device);
device[0][0] = device[3][0] = width / 2.0;
device[1][1] = device[3][1] = height / 2.0;
concatenate_transforms(model, view, composite);
concatenate_transforms(composite, device, composite);
}
#define POINTS 16 /* The total number of unique points */
#define POLYPOINTS 12 /* The maximum number of vertices in a polygon */
#define PARTIALS 6 /* The maximum number of partial polygons in a polygon */
#define END_POLYGON -2 /* designates the end of one polygon */
#define END_POLYGONS -3 /* designates the end of all polygons */
#define END_PARTIAL_POLYGON -1 /* designates the end of a partial polygon */
redraw()
/* Draw the 3d object using the current composite transform. */
{
static Point points[POINTS] = {
-4.0, -4.0, -4.0,
4.0, -4.0, -4.0,
-4.0, 4.0, -4.0,
4.0, 4.0, -4.0,
-4.0, -4.0, 4.0,
4.0, -4.0, 4.0,
-4.0, 4.0, 4.0,
4.0, 4.0, 4.0,
-2.0, -4.0, -2.0,
2.0, -4.0, -2.0,
-2.0, 0.0, -2.0,
2.0, 0.0, -2.0,
-2.0, -4.0, 2.0,
2.0, -4.0, 2.0,
-2.0, 0.0, 2.0,
2.0, 0.0, 2.0,
};
Point transformed_points[POINTS];
static int polygons[] = {
10, 11, 9, 8, 10, END_POLYGON,
12, 13, 15, 14, 12, END_POLYGON,
9, 11, 15, 13, 9, END_POLYGON,
11, 10, 14, 15, 11, END_POLYGON,
10, 8, 12, 14, 10, END_POLYGON,
0, 4, 5, 1, 0, END_PARTIAL_POLYGON,
8, 12, 13, 9, 8, END_POLYGON,
0, 1, 3, 2, 0, END_POLYGON,
6, 7, 5, 4, 6, END_POLYGON,
5, 7, 3, 1, 5, END_POLYGON,
7, 6, 2, 3, 7, END_POLYGON,
6, 4, 0, 2, 6, END_POLYGON,
END_POLYGONS,
};
XPoint buffer[POLYPOINTS]; /* a set of Xlib coordinate vertices */
int partials[PARTIALS]; /* starting points of partial polygons */
int num_partials; /* number of partial polygons in a polygon*/
int src; /* an index into buffer[] */
int dest; /* an index into polygons[] */
int i; /* an index into partials[] */
int at_start_of_polygon; /* flags the start of each polygon */
int skip_polygon; /* flags a backface polygon */
XSetForeground(display, gc, colors[0].pixel);
XFillRectangle(display, window, gc, 0, 0, width, height);
for (i = POINTS - 1; i >= 0; i--)
transform_point(composite, points[i], transformed_points[i]);
dest = 0;
at_start_of_polygon = True;
partials[0] = 0;
num_partials = 1;
for (src = 0; polygons[src] != END_POLYGONS; src++) {
if (at_start_of_polygon) {
skip_polygon = backface(
transformed_points[polygons[src]],
transformed_points[polygons[src+1]],
transformed_points[polygons[src+2]]);
at_start_of_polygon = False;
}
switch (polygons[src]) {
case END_POLYGON:
if (!skip_polygon) {
XSetForeground(display, gc, colors[2].pixel);
XFillPolygon(display, window, gc, &buffer[0], dest,
Complex, CoordModeOrigin);
XSetForeground(display, gc, colors[1].pixel);
partials[num_partials] = dest;
for (i=0; i<num_partials; i++)
XDrawLines(display, window, gc, &buffer[partials[i]],
partials[i+1] - partials[i], CoordModeOrigin);
}
dest = 0;
at_start_of_polygon = True;
break;
case END_PARTIAL_POLYGON:
partials[num_partials++] = dest;
break;
default:
buffer[dest].x = transformed_points[polygons[src]][0];
buffer[dest++].y = transformed_points[polygons[src]][1];
break;
}
}
if (double_buffer) {
double_buffer_switch(dbuf_state);
XSetPlaneMask(display, gc, dbuf_state->drawing_planes);
} else {
XSetPlaneMask(display, gc, AllPlanes);
}
XFlush(display);
}
main(argc, argv)
char *argv[];
{
XEvent event; /* holds X server events */
int x, y; /* the last X pointer position */
int new_x, new_y; /* a new X pointer position */
unsigned int mask; /* mask of button and modifier key state */
unsigned int dummy; /* placeholder for unwanted return values */
init_X(argc, argv);
init_transforms();
printf("Drag button 1 to rotate the object.\n");
printf("Press button 2 to toggle double buffering on and off.\n");
printf("Press button 3 to stop the program.\n");
for (; ; ) {
XNextEvent(display, &event);
switch (event.type) {
case DestroyNotify:
XCloseDisplay(display);
exit(0);
break;
case Expose:
if (event.xexpose.count == 0) {
redraw();
}
break;
case ConfigureNotify:
if ((event.xconfigure.width != width) ||
(event.xconfigure.height != height)) {
width = event.xconfigure.width;
height = event.xconfigure.height;
device[0][0] = device[3][0] = width / 2.0;
device[1][1] = device[3][1] = height / 2.0;
concatenate_transforms(model, view, composite);
concatenate_transforms(composite, device, composite);
redraw();
}
break;
case MotionNotify:
XQueryPointer(display, window,
&dummy, &dummy,
&dummy, &dummy,
&new_x, &new_y,
&mask);
if (!(mask & Button1Mask))
break;
rotate_X(motion, M_PI / 360.0 * (new_y - y));
concatenate_transforms(model, motion, model);
rotate_Y(motion, -M_PI / 360.0 * (new_x - x));
concatenate_transforms(model, motion, model);
x = new_x;
y = new_y;
concatenate_transforms(model, view, composite);
concatenate_transforms(composite, device, composite);
redraw();
break;
case ButtonPress:
x = event.xbutton.x;
y = event.xbutton.y;
if (event.xbutton.button == 2)
double_buffer ^= 1;
if (event.xbutton.button == 3)
exit(0);
break;
default:
break;
}
}
}
[LISTING TWO]
/* double_buffer.h - declarations for an Xlib double buffering utility. */
/* double buffering state record */
typedef struct {
Display *display;
Colormap cmap;
long drawing_planes; /* planes currently drawn to */
int buffer; /* which buffer to show, even or odd */
XColor *colormaps[2]; /* color maps for even and odd buffers */
int map_size; /* number of entries in color maps */
long masks[2]; /* write_enable masks for odd and even */
long *planes; /* individual planes */
long pixel; /* pixel base value of double buffering */
} double_buffer_state;
/* double buffering procedures */
extern double_buffer_state *start_double_buffer();
extern void double_buffer_switch();
extern void end_double_buffer();
[LISTING THREE]
/* double_buffer.c - an Xlib double buffering utility. */
#include <X11/Xlib.h>
#include <malloc.h>
#include <stdio.h>
#include "double_buffer.h"
static void release(state)
register double_buffer_state *state;
/* Release a possibly partially allocated double buffer state record. */
{
if (state != NULL) {
if (state->colormaps[0] != NULL) free(state->colormaps[0]);
if (state->colormaps[1] != NULL) free(state->colormaps[1]);
if (state->planes != NULL) free(state->planes);
free(state);
}
}
static long color(state, simple_color)
register double_buffer_state *state;
register long simple_color;
/* Map the supplied color into the equivalent color
* using the double buffered planes. */
{
register long i, plane, computed_color;
computed_color = state->pixel;
for (plane = 1, i = 0; simple_color != 0; plane <<= 1, i++) {
if (plane & simple_color) {
computed_color |= state->planes[i];
simple_color &= ~plane;
}
}
return(computed_color);
}
double_buffer_state *start_double_buffer(display, cmap, planes, colors)
Display *display;
Colormap cmap;
long planes; /* how many planes for each buffer */
XColor *colors; /* color settings for buffers */
/* Start double buffering in given number of planes per buffer.
* If resources can be allocated, then set color pixels in colors parameter
* and return the address of a double_buffer_state record.
* Otherwise, return NULL. */
{
register double_buffer_state *state;
register long i, high_mask, low_mask;
/* Allocate memory. */
state = (double_buffer_state *) malloc(sizeof(double_buffer_state));
if (state == NULL)
return (NULL);
state->map_size = 1 << (2 * planes);
state->colormaps[0] = (XColor *) malloc(state->map_size * sizeof(XColor));
state->colormaps[1] = (XColor *) malloc(state->map_size * sizeof(XColor));
state->planes = (long *) malloc((2 * planes) * sizeof(long));
if (state->colormaps[1] == NULL || state->colormaps[0] == NULL
|| state->planes == NULL) {
release(state);
return(NULL);
}
state->display = display;
state->cmap = cmap;
/* Get colors to double buffer with. */
if (XAllocColorCells(state->display, state->cmap, False,
state->planes, 2*planes, &state->pixel, 1) == 0) {
release(state);
return(NULL);
}
/* Prepare the write enable masks. */
state->masks[0] = AllPlanes;
state->masks[1] = AllPlanes;
/* Mask 0 won't write in the "low" planes. */
/* Mask 1 won't write in the "high" planes. */
for (i = 0; i < planes; i++) {
state->masks[0] &= ~state->planes[i];
state->masks[1] &= ~state->planes[planes + i];
}
/* Prepare the flags and pixel values for each color. */
for (i = 0; i < (1 << planes); i++) {
colors[i].pixel = color(state, i | (i << planes));
colors[i].flags = DoRed | DoGreen | DoBlue;
}
/* Prepare the two color map settings. */
/* Colormap 0 displays the "low" planes. */
/* Colormap 1 displays the "high" planes. */
low_mask = (1 << planes) - 1;
high_mask = low_mask << planes;
for (i = state->map_size - 1; i >= 0; i--) {
state->colormaps[0][i] = colors[i & low_mask];
state->colormaps[0][i].pixel = color(state, i);
state->colormaps[1][i] = colors[(i & high_mask) >> planes];
state->colormaps[1][i].pixel = color(state, i);
}
/* Set up initial color map and write_enable. */
state->buffer = 0;
state->drawing_planes = state->masks[state->buffer];
XStoreColors(state->display, state->cmap,
state->colormaps[state->buffer], state->map_size);
return(state);
}
void double_buffer_switch(state)
register double_buffer_state *state;
/* Change double buffering buffer.
* Return the new planes mask for double buffering. */
{
/* Toggle the buffers. */
state->buffer ^= 1;
/* Adjust the color map and write enable mask. */
XStoreColors(state->display, state->cmap,
state->colormaps[state->buffer], state->map_size);
state->drawing_planes = state->masks[state->buffer];
}
void end_double_buffer(state)
register double_buffer_state *state;
{
XFreeColors(state->display, state->cmap,
&state->pixel, 1, ~(state->masks[0] & state->masks[1]));
release(state);
}