· 4 years ago · Feb 23, 2021, 07:18 AM
1// *****************************************************************************
2//
3// Animated Cobra MK-III from Elite Classic using only Xlib
4//
5// Compile:
6// g++ -Wall -Os cobra.c -o cobra -lX11 -lXext -lm
7//
8// 2017 by hlt
9//
10// *****************************************************************************
11#include <X11/Xlib.h>
12#include <X11/Xutil.h>
13#include <X11/Xos.h>
14#include <X11/extensions/Xdbe.h>
15#include <X11/keysymdef.h>
16
17#include <stdio.h>
18#include <stdlib.h>
19#include <math.h>
20
21/*------------------------------------------------------------------------------
22TODO:
23--------------------------------------------------------------------------------
24V Hidden Line stuff (oh boy!)
25V Ships geometry not correct yet
26V remove unnecessary stuff from source (2d funcs etc)
27V small ship flickering inside big one while key pressed! why?
28V double buffering
29V animation loop without pressing keys
30V adjust parms: near, far, eye, d, w, h etc
31V what is the rotation algo of the ship in the elite classic start screen?
32V changing rotation axis resets all angles to orig vals. not nice
33V it seems the ship is slowly wobbeling while rotating => center of mass!!
34V add polygon table, remove edges table
35V seperate model data operations from presentation data operations
36 (MODEL, VERTICES_OUTPUT_BUFFER)
37v make all responsive to changed window size
38
39- add CLI args to control some aspects of the program (line width, fps, etc)
40- make it a xscreensaver
41- add zoom in/zoom out (nth) - when win resized(?!)
42- make auto-rotation random-ish, current auto-rotation is ok so far (nth)
43
44- atm viewport_transform() does only scale the model. no translation yet. if
45 model is translated to center of screen then look_at has to be changed too.
46 Best: dynamically change look_at to center_of_mass of the model
47V get rid of DX, DY, DZ stuff in vertices, use CenterOfMass instead
48V get rid of DIST_FACTOR
49V add func world_init() to setup the ship in the world initially
50V use keys to interact:
51 V SPACE : stop/start auto-rotate
52 V Left : yaw left by one step
53 V Right : yaw right by one step
54 V Up : pitch up by one step
55 V Down : pitch down by one step
56 V n : roll left by one step
57 V m : roll right by one step
58 V r : reset all angles to 0, stop auto-rotate
59- when using keys: rotate model about model axises, not world axises!
60------------------------------------------------------------------------------*/
61//#define DEBUG
62#define PI 3.1415926535897932f
63
64// *****************************************************************************
65// types
66// *****************************************************************************
67typedef struct {double x, y;} v2_t;
68typedef struct {double x, y, z;} v3_t;
69typedef struct {double x, y, z, w;} v4_t;
70typedef struct {double
71 x00, x01, x02, x03,
72 x10, x11, x12, x13,
73 x20, x21, x22, x23,
74 x30, x31, x32, x33;
75} m4_t;
76typedef struct {double x, y, z;} angles_t;
77
78
79// *****************************************************************************
80// globals
81// *****************************************************************************
82Display *DISPLAY;
83Window WINDOW;
84GC GCON;
85
86Visual *VISUAL = NULL;
87XdbeBackBuffer DBUFFER;
88bool IS_DBUFFER_ENABLED = false;
89
90ulong BACKGROUND_COLOR = 0;
91ulong FOREGROUND_COLOR = 0;
92int LINE_WIDTH = 1;
93
94int WIN_WIDTH = 640; // double the
95int WIN_HEIGHT = 400; // resolution of C=64
96
97int CENTER_X = WIN_WIDTH / 2;
98int CENTER_Y = WIN_HEIGHT / 2;
99
100double TOP = 0.0;
101double LEFT = 0.0;
102double BOTTOM = (double)WIN_HEIGHT;
103double RIGHT = (double)WIN_WIDTH;;
104
105double NEAR = 20.0;
106double FAR = 600.0;
107double FOV = 90.0 * PI / 180.0;
108
109double DZ = 300.0;
110
111v4_t EYE = {0.0, 0.0, 0.0, 1.0}; // camera position
112v4_t UP = {0.0, 1.0, 0.0, 0.0}; // camera up
113v4_t AT = {0.0, 0.0, DZ, 0.0}; // camera look at
114v4_t COM = {0.0, 0.0, 0.0, 0.0}; // center of mass
115
116angles_t MODEL_ANGLES = {0.0, 0.0, 0.0};
117
118// *****************************************************************************
119// vertices
120// *****************************************************************************
121#define VERTICES_COUNT 29
122static v4_t BASE_MODEL[VERTICES_COUNT] = {
123 // X Y Z W
124 // ship
125 {-123.0, -2.0, -40.0, 1.0}, // 0 A
126 {-123.0, -2.0, -15.0, 1.0}, // 1 B
127 { -33.0, -2.0, 72.0, 1.0}, // 2 C
128 { 33.0, -2.0, 72.0, 1.0}, // 3 D
129 { 123.0, -2.0, -15.0, 1.0}, // 4 E
130 { 123.0, -2.0, -40.0, 1.0}, // 5 F
131 { 103.0, 10.0, -40.0, 1.0}, // 6 G
132 { 0.0, 15.0, -40.0, 1.0}, // 7 H
133 {-103.0, 10.0, -40.0, 1.0}, // 8 I
134 { 0.0, 20.0, 30.0, 1.0}, // 9 J
135 { -33.0,-15.0, -40.0, 1.0}, // 10 K
136 { 33.0,-15.0, -40.0, 1.0}, // 11 L
137
138 // left main engine
139 { -5.0, 10.0, -40.0, 1.0}, // 12 M
140 { -5.0,-10.0, -40.0, 1.0}, // 13 N
141 { -34.0, -8.0, -40.0, 1.0}, // 14 O
142 { -34.0, 8.0, -40.0, 1.0}, // 15 P
143
144 // righ main engine
145 { 5.0, 10.0, -40.0, 1.0}, // 16 Q
146 { 5.0,-10.0, -40.0, 1.0}, // 17 R
147 { 34.0, -8.0, -40.0, 1.0}, // 18 S
148 { 34.0, 8.0, -40.0, 1.0}, // 19 T
149
150 // left small engine
151 { -97.0, 5.0, -40.0, 1.0}, // 20 U
152 {-106.0, 3.0, -40.0, 1.0}, // 21 V
153 { -97.0, -1.0, -40.0, 1.0}, // 22 W
154
155 // right small engine
156 { 97.0, 5.0, -40.0, 1.0}, // 23 X
157 { 106.0, 3.0, -40.0, 1.0}, // 24 Y
158 { 97.0, -1.0, -40.0, 1.0}, // 25 Z
159
160 // gun
161 { -0.1, -2.0, 72.0, 1.0}, // 26 Z1
162 { 0.0, -2.0, 92.0, 1.0}, // 27 Z2
163 { 0.1, -2.0, 72.0, 1.0} // 28 Z3
164};
165static v4_t MODEL[VERTICES_COUNT];
166static v4_t VERTICES_OUTPUT_BUFFER[VERTICES_COUNT];
167
168// *****************************************************************************
169// polygons
170// *****************************************************************************
171// ...a polygon is a list of indexes pointing to the vertices buffers
172#define MAX_VERTICES_PER_POLYGON 10
173typedef struct polygon_t {
174 int v[MAX_VERTICES_PER_POLYGON];
175} polygon_t;
176
177static polygon_t POLYGONS [] = {
178 {{0,1,8,0}}, // ABI 0 top left small backward triangle
179 {{1,2,8,1}}, // BCI 1 top left front - outer triangle
180 {{8,2,9,8}}, // ICJ 2 top left front - inner triangle
181 {{8,9,7,8}}, // IJH 3 top left main triangle
182 {{9,2,3,9}}, // JCD 4 top front center triangle
183 {{7,9,6,7}}, // HJG 5 top right main triangle
184 {{6,9,3,6}}, // GJD 6 top right front - inner triangle
185 {{6,3,4,6}}, // GDE 7 top right front - outer triangle
186 {{6,4,5,6}}, // GEF 8 top right small backward triangle
187 {{0,8,7,6,5,11,10,0}}, // AIHGFLK 9 ship's back plane
188 {{10,2,1,10}}, // KCB 10 bottom left
189 {{10,11,3,2,10}}, // KLDC 11 bottom center
190 {{11,4,3,11}}, // LED 12 bottom right
191 {{0,10,1,0}}, // ACB 13 bottom left backer
192 {{4,11,5,4}}, // DFE 14 bottom right backer
193 {{12,13,14,15,12}}, // MNOP 15 main engine left
194 {{19,18,17,16,19}}, // TSRQ 16 main engine right
195 {{22,21,20,22}}, // WVU 17 aux engine left
196 {{23,24,25,23}}, // XYZ 18 aux engine right
197// {{26,27,28,26}}, // Z1Z2Z3 19 gun
198 {{-1,0,0,0}} // EOL
199};
200
201// *****************************************************************************
202// get a random number. whatever
203double uniform()
204{
205 return (double)(rand() / (RAND_MAX + 1.0));
206}
207
208// *****************************************************************************
209// thats for debug
210/*
211static inline void show_vertices()
212{
213 for (int z = 0; z < VERTICES_COUNT; z++)
214 {
215 printf("%2d : (x=%9.2f, y=%9.2f, z=%9.2f, w=%9.2f)\n",
216 z,
217 MODEL[z].x,
218 MODEL[z].y,
219 MODEL[z].z,
220 MODEL[z].w);
221 }
222 printf('\n');
223}
224*/
225
226// *****************************************************************************
227// react if window size is changed
228static inline void adjust_to_new_window_dimensions()
229{
230 XWindowAttributes attr;
231
232 XGetWindowAttributes(DISPLAY, WINDOW, &attr);
233 if (WIN_WIDTH == attr.width && WIN_HEIGHT == attr.height)
234 return;
235
236 WIN_WIDTH = attr.width;
237 WIN_HEIGHT = attr.height;
238
239 CENTER_X = WIN_WIDTH / 2;
240 CENTER_Y = WIN_HEIGHT / 2;
241
242 RIGHT = (double)WIN_WIDTH;
243 BOTTOM = (double)WIN_HEIGHT;
244}
245
246// *****************************************************************************
247// init the xlib stuff
248static inline void x_init ()
249{
250 DISPLAY = XOpenDisplay(NULL);
251 if (DISPLAY == NULL)
252 {
253 fprintf(stderr, "Cannot open display\n");
254 exit(EXIT_FAILURE);
255 }
256
257 BACKGROUND_COLOR = BlackPixel (DISPLAY, DefaultScreen (DISPLAY));
258 FOREGROUND_COLOR = WhitePixel (DISPLAY, DefaultScreen (DISPLAY));
259
260 int major, minor;
261 if (XdbeQueryExtension(DISPLAY, &major, &minor))
262 {
263 int numScreens = 1;
264 Drawable screens[] = { DefaultRootWindow(DISPLAY) };
265 XdbeScreenVisualInfo *info = XdbeGetVisualInfo(DISPLAY, screens, &numScreens);
266 if (!info || numScreens < 1 || info->count < 1)
267 {
268 fprintf(stderr, "No visuals support Xdbe\n");
269 exit(EXIT_FAILURE);
270 }
271
272 // Choosing the first one, seems that they have all perflevel of 0,
273 // and the depth varies.
274 XVisualInfo xvisinfo_templ;
275
276 // We know there's at least one
277 xvisinfo_templ.visualid = info->visinfo[0].visual;
278
279 // As far as I know, screens are densely packed, so we can assume
280 // that if at least 1 exists, it's screen 0.
281 xvisinfo_templ.screen = 0;
282 xvisinfo_templ.depth = info->visinfo[0].depth;
283
284 int matches;
285 XVisualInfo *xvisinfo_match = XGetVisualInfo(DISPLAY,
286 VisualIDMask | VisualScreenMask | VisualDepthMask,
287 &xvisinfo_templ,
288 &matches);
289 if (!xvisinfo_match || matches < 1)
290 {
291 fprintf(stderr, "Couldn't match a Visual with double buffering\n");
292 exit(EXIT_FAILURE);
293 }
294
295 // We can use Visual from the match
296 VISUAL = xvisinfo_match->visual;
297
298#ifdef DEBUG
299 printf("Double buffering enabled\n");
300#endif
301 IS_DBUFFER_ENABLED = true;
302 }
303 else
304 {
305#ifdef DEBUG
306 printf("Double buffering not available\n");
307#endif
308 IS_DBUFFER_ENABLED = false;
309 }
310
311 if (IS_DBUFFER_ENABLED)
312 {
313 unsigned long xAttrMask = CWBackPixel;
314 XSetWindowAttributes xAttr;
315 xAttr.background_pixel = BACKGROUND_COLOR;
316 WINDOW = XCreateWindow(DISPLAY,
317 DefaultRootWindow(DISPLAY),
318 0, 0,
319 WIN_WIDTH, WIN_HEIGHT,
320 0,
321 CopyFromParent,
322 CopyFromParent,
323 VISUAL,
324 xAttrMask,
325 &xAttr);
326 }
327 else
328 {
329 WINDOW = XCreateSimpleWindow(DISPLAY, DefaultRootWindow(DISPLAY),
330 0, 0,
331 WIN_WIDTH, WIN_HEIGHT,
332 5,
333 FOREGROUND_COLOR,
334 BACKGROUND_COLOR);
335 }
336
337 if (!WINDOW)
338 {
339 fprintf(stderr, "Failed to create window\n");
340 exit(EXIT_FAILURE);
341 }
342
343 if (IS_DBUFFER_ENABLED)
344 {
345 DBUFFER = XdbeAllocateBackBufferName(DISPLAY, WINDOW, XdbeBackground);
346 GCON = XCreateGC (DISPLAY, DBUFFER, 0, 0);
347 }
348 else
349 {
350 GCON = XCreateGC (DISPLAY, WINDOW, 0, 0);
351 }
352
353 XSetStandardProperties (DISPLAY, WINDOW,
354 "xcobra - 'q' to quit",
355 "xcobra",
356 None,
357 NULL,
358 0,
359 NULL);
360
361 // adjust to changed window attributes
362 adjust_to_new_window_dimensions();
363
364 // event types watched
365 XSelectInput (DISPLAY, WINDOW, StructureNotifyMask | ExposureMask | KeyPressMask);
366 XMapWindow(DISPLAY, WINDOW);
367
368 // colors
369 XSetForeground (DISPLAY, GCON, FOREGROUND_COLOR);
370 XSetBackground (DISPLAY, GCON, BACKGROUND_COLOR);
371
372 XFlush(DISPLAY);
373}
374
375// *****************************************************************************
376// do a clean shutdown of xlib
377static inline void x_close ()
378{
379 XFreeGC (DISPLAY, GCON);
380 XDestroyWindow (DISPLAY, WINDOW);
381 XCloseDisplay (DISPLAY);
382 exit(EXIT_SUCCESS);
383}
384
385// *****************************************************************************
386// draw a line between two points using XDrawLine()
387static inline void x_line (int x1, int y1, int x2, int y2)
388{
389 // shift coords from top-left corner to center of window
390 int c_x1 = CENTER_X + x1;
391 int c_y1 = CENTER_Y - y1;
392 int c_x2 = CENTER_X + x2;
393 int c_y2 = CENTER_Y - y2;
394
395 // draw according to dbuffer setting
396 if(IS_DBUFFER_ENABLED)
397 XDrawLine(DISPLAY, DBUFFER, GCON, c_x1, c_y1, c_x2, c_y2);
398 else
399 XDrawLine(DISPLAY, WINDOW, GCON, c_x1, c_y1, c_x2, c_y2);
400#ifdef DEBUG
401 printf("(%d,%d) -> (%d,%d)\n", c_x1, c_y1, c_x2, c_y2);
402#endif
403}
404
405// *****************************************************************************
406// subtract vector4 b from vector4 a
407static inline v4_t sub_v4v4 (v4_t a, v4_t b)
408{
409 v4_t diff = {
410 a.x - b.x, // x
411 a.y - b.y, // y
412 a.z - b.z, // z
413 a.w - b.w // w
414 };
415 return diff;
416}
417
418// *****************************************************************************
419// multiply vector4 a by a scalar (scalar product)
420static inline v4_t scalar_mult_v4 (v4_t a, double scalar)
421{
422 v4_t sprod = {
423 a.x * scalar, // x
424 a.y * scalar, // y
425 a.z * scalar, // z
426 a.w * scalar, // w
427 };
428 return sprod;
429}
430
431// *****************************************************************************
432// multiply a matrix4 by a vector4
433static inline v4_t mult_m4v4 (m4_t M, v4_t a)
434{
435 v4_t Mxa = {
436 M.x00*a.x + M.x01*a.y + M.x02*a.z + M.x03*a.w, // x
437 M.x10*a.x + M.x11*a.y + M.x12*a.z + M.x13*a.w, // y
438 M.x20*a.x + M.x21*a.y + M.x22*a.z + M.x23*a.w, // z
439 M.x30*a.x + M.x31*a.y + M.x32*a.z + M.x33*a.w // w
440 };
441 return Mxa;
442}
443
444// *****************************************************************************
445// multiply matrix4 A by matrix4 B
446static inline m4_t mult_m4m4 (m4_t A, m4_t B)
447{
448 m4_t AxB = {
449 A.x00*B.x00 + A.x01*B.x10 + A.x02*B.x20 + A.x03*B.x30, // x00
450 A.x00*B.x01 + A.x01*B.x11 + A.x02*B.x21 + A.x03*B.x31, // x01
451 A.x00*B.x02 + A.x01*B.x12 + A.x02*B.x22 + A.x03*B.x32, // x02
452 A.x00*B.x03 + A.x01*B.x13 + A.x02*B.x23 + A.x03*B.x33, // x03
453
454 A.x10*B.x00 + A.x11*B.x10 + A.x12*B.x20 + A.x13*B.x30, // x10
455 A.x10*B.x01 + A.x11*B.x11 + A.x12*B.x21 + A.x13*B.x31, // x11
456 A.x10*B.x02 + A.x11*B.x12 + A.x12*B.x22 + A.x13*B.x32, // x12
457 A.x10*B.x03 + A.x11*B.x13 + A.x12*B.x23 + A.x13*B.x33, // x13
458
459 A.x20*B.x00 + A.x21*B.x10 + A.x22*B.x20 + A.x23*B.x30, // x20
460 A.x20*B.x01 + A.x21*B.x11 + A.x22*B.x21 + A.x23*B.x31, // x21
461 A.x20*B.x02 + A.x21*B.x12 + A.x22*B.x22 + A.x23*B.x32, // x22
462 A.x20*B.x03 + A.x21*B.x13 + A.x22*B.x23 + A.x23*B.x33, // x23
463
464 A.x30*B.x00 + A.x31*B.x10 + A.x32*B.x20 + A.x33*B.x30, // x30
465 A.x30*B.x01 + A.x31*B.x11 + A.x32*B.x21 + A.x33*B.x31, // x31
466 A.x30*B.x02 + A.x31*B.x12 + A.x32*B.x22 + A.x33*B.x32, // x32
467 A.x30*B.x03 + A.x31*B.x13 + A.x32*B.x23 + A.x33*B.x33 // x33
468 };
469 return AxB;
470}
471
472// *****************************************************************************
473// calc the dot product of vector4 a and vector4 b
474static inline double dot_v4v4 (v4_t a, v4_t b)
475{
476 return a.x*b.x + a.y*b.y + a.z*b.z;
477}
478
479// *****************************************************************************
480// calc the cross product of vector4 a and vector4 b
481static inline v4_t cross_v4v4 (v4_t a, v4_t b)
482{
483 v4_t axb = {
484 a.y*b.z - a.z*b.y, // x
485 a.z*b.x - a.x*b.z, // y
486 a.x*b.y - a.y*b.x, // z
487 1.0 // w
488 };
489 return axb;
490}
491
492// *****************************************************************************
493// calc the length of a vector4 a
494static inline double length_v4 (v4_t a)
495{
496 return sqrt(a.x*a.x + a.y*a.y + a.z*a.z);
497}
498
499// *****************************************************************************
500// normalize a vector4 a
501static inline v4_t norm_v4 (v4_t a)
502{
503 double len = length_v4(a);
504 v4_t norm = {
505 a.x / len, // x
506 a.y / len, // y
507 a.z / len, // z
508 1.0 // w
509 };
510 return norm;
511}
512
513// *****************************************************************************
514// calc the determinant of a vector4 a
515static inline double det_v4 (m4_t M)
516{
517 return -1.0;
518}
519
520// *****************************************************************************
521// calc the inverse matrix of a matrix4 M
522static inline m4_t inv_m4 (m4_t M)
523{
524 return M;
525}
526
527// *****************************************************************************
528// apply M to MODEL
529static inline void apply_to_model (m4_t M)
530{
531 for (int i = 0; i < VERTICES_COUNT; i++)
532 {
533 MODEL[i] = mult_m4v4 (M, MODEL[i]);
534 }
535}
536
537// *****************************************************************************
538// apply M to VERTICES_OUTPUT_BUFFER
539static inline void apply_to_vertices_output_buffer (m4_t M)
540{
541 for (int i = 0; i < VERTICES_COUNT; i++)
542 {
543 VERTICES_OUTPUT_BUFFER[i] = mult_m4v4 (M, VERTICES_OUTPUT_BUFFER[i]);
544 }
545}
546
547// *****************************************************************************
548// get identity matrix
549static inline m4_t identity_matrix ()
550{
551 m4_t M = {
552 1.0, 0.0, 0.0, 0.0,
553 0.0, 1.0, 0.0, 0.0,
554 0.0, 0.0, 1.0, 0.0,
555 0.0, 0.0, 0.0, 1.0
556 };
557 return M;
558}
559
560// *****************************************************************************
561// get rotation matrix for angle theta about x axis
562static inline m4_t R_x (double theta)
563{
564 double s = sin(theta * PI / 180.0);
565 double c = cos(theta * PI / 180.0);
566 m4_t M = {
567 1.0, 0.0, 0.0, 0.0,
568 0.0, c, s, 0.0,
569 0.0, -s, c, 0.0,
570 0.0, 0.0, 0.0, 1.0
571 };
572 return M;
573}
574
575// *****************************************************************************
576// get rotation matrix for angle theta about y axis
577static inline m4_t R_y (double theta)
578{
579 double s = sin(theta * PI / 180.0);
580 double c = cos(theta * PI / 180.0);
581 m4_t M = {
582 c, 0.0, -s, 0.0,
583 0.0, 1.0, 0.0, 0.0,
584 s, 0.0, c, 0.0,
585 0.0, 0.0, 0.0, 1.0
586 };
587 return M;
588}
589
590// *****************************************************************************
591// get rotation matrix for angle theta about y axis
592static inline m4_t R_z (double theta)
593{
594 double s = sin(theta * PI / 180.0);
595 double c = cos(theta * PI / 180.0);
596 m4_t M = {
597 c, s, 0.0, 0.0,
598 -s, c, 0.0, 0.0,
599 0.0, 0.0, 1.0, 0.0,
600 0.0, 0.0, 0.0, 1.0
601 };
602 return M;
603}
604
605// *****************************************************************************
606// get rotation matrix for angle theta about a vector4 v
607static inline m4_t R_v4 (v4_t v, double theta)
608{
609 v = norm_v4(v);
610 double s = sin(theta * PI / 180.0);
611 double c = cos(theta * PI / 180.0);
612 double o = 1.0 - c;
613 m4_t M = {
614 o*v.x*v.x+c, o*v.x*v.y-v.z*s, o*v.z*v.x+v.y*s, 0.0,
615 o*v.x*v.y+v.z*s, o*v.y*v.y+c, o*v.y*v.z-v.x*s, 0.0,
616 o*v.z*v.x-v.y*s, o*v.y*v.z+v.x*s, o*v.z*v.z+c, 0.0,
617 0.0, 0.0, 0.0, 1.0
618 };
619 return M;
620}
621
622// *****************************************************************************
623// get translation matrix for dx, dy and dz
624static inline m4_t T_xyz (double dx, double dy, double dz)
625{
626 m4_t M = {
627 1.0, 0.0, 0.0, dx,
628 0.0, 1.0, 0.0, dy,
629 0.0, 0.0, 1.0, dz,
630 0.0, 0.0, 0.0, 1.0
631 };
632 return M;
633}
634
635// *****************************************************************************
636// get scale matrix for factor fact along x axis
637static inline m4_t S_x (double fact)
638{
639 m4_t M = {
640 fact, 0.0, 0.0, 0.0,
641 0.0, 1.0, 0.0, 0.0,
642 0.0, 0.0, 1.0, 0.0,
643 0.0, 0.0, 0.0, 1.0
644 };
645 return M;
646}
647
648// *****************************************************************************
649// get scale matrix for factor fact along y axis
650static inline m4_t S_y (double fact)
651{
652 m4_t M = {
653 1.0, 0.0, 0.0, 0.0,
654 0.0, fact, 0.0, 0.0,
655 0.0, 0.0, 1.0, 0.0,
656 0.0, 0.0, 0.0, 1.0
657 };
658 return M;
659}
660
661// *****************************************************************************
662// get scale matrix for factor fact along z axis
663static inline m4_t S_z (double fact)
664{
665 m4_t M = {
666 1.0, 0.0, 0.0, 0.0,
667 0.0, 1.0, 0.0, 0.0,
668 0.0, 0.0, fact, 0.0,
669 0.0, 0.0, 0.0, 1.0
670 };
671 return M;
672}
673
674// *****************************************************************************
675// get scale matrix for factor fact along all axises
676static inline m4_t S_xyz (double fact)
677{
678 m4_t M = {
679 fact, 0.0, 0.0, 0.0,
680 0.0, fact, 0.0, 0.0,
681 0.0, 0.0, fact, 0.0,
682 0.0, 0.0, 0.0, 1.0
683 };
684 return M;
685}
686
687// *****************************************************************************
688// translate MODEL by dx, dy and dz
689static inline void translate_model (double dx = 0.0, double dy = 0.0, double dz = 0.0)
690{
691 apply_to_model(T_xyz(dx, dy, dz));
692}
693
694// *****************************************************************************
695// rotate MODEL about x by theta
696static inline void rotate_model_x (double theta)
697{
698 translate_model(0.0, 0.0, -DZ);
699
700// apply_to_model(R_x(-MODEL_ANGLES.x));
701// apply_to_model(R_y(-MODEL_ANGLES.y));
702// apply_to_model(R_z(-MODEL_ANGLES.z));
703
704 apply_to_model(R_x(theta));
705
706 MODEL_ANGLES.x += theta;
707 if (MODEL_ANGLES.x > 360.0) MODEL_ANGLES.x -= 360.0;
708 if (MODEL_ANGLES.x < 0.0) MODEL_ANGLES.x = 360.0 - MODEL_ANGLES.x;
709
710// apply_to_model(R_z(MODEL_ANGLES.z));
711// apply_to_model(R_y(MODEL_ANGLES.y));
712// apply_to_model(R_x(MODEL_ANGLES.x));
713
714 translate_model(0.0, 0.0, DZ);
715}
716
717// *****************************************************************************
718// rotate MODEL about y by theta
719static inline void rotate_model_y (double theta)
720{
721 translate_model(0.0, 0.0, -DZ);
722
723// apply_to_model(R_x(-MODEL_ANGLES.x));
724// apply_to_model(R_y(-MODEL_ANGLES.y));
725// apply_to_model(R_z(-MODEL_ANGLES.z));
726
727 apply_to_model(R_y(theta));
728
729 MODEL_ANGLES.y += theta;
730 if (MODEL_ANGLES.y > 360.0) MODEL_ANGLES.y -= 360.0;
731 if (MODEL_ANGLES.y < 0.0) MODEL_ANGLES.y = 360.0 - MODEL_ANGLES.y;
732
733// apply_to_model(R_z(MODEL_ANGLES.z));
734// apply_to_model(R_y(MODEL_ANGLES.y));
735// apply_to_model(R_x(MODEL_ANGLES.x));
736
737 translate_model(0.0, 0.0, DZ);
738}
739
740// *****************************************************************************
741// rotate MODEL about z by theta
742static inline void rotate_model_z (double theta)
743{
744 translate_model(0.0, 0.0, -DZ);
745
746// apply_to_model(R_x(-MODEL_ANGLES.x));
747// apply_to_model(R_y(-MODEL_ANGLES.y));
748// apply_to_model(R_z(-MODEL_ANGLES.z));
749
750 apply_to_model(R_z(theta));
751
752 MODEL_ANGLES.z += theta;
753 if (MODEL_ANGLES.z > 360.0) MODEL_ANGLES.z -= 360.0;
754 if (MODEL_ANGLES.z < 0.0) MODEL_ANGLES.z = 360.0 - MODEL_ANGLES.z;
755
756// apply_to_model(R_z(MODEL_ANGLES.z));
757// apply_to_model(R_y(MODEL_ANGLES.y));
758// apply_to_model(R_x(MODEL_ANGLES.x));
759
760 translate_model(0.0, 0.0, DZ);
761}
762
763// *****************************************************************************
764// scale MODEL along x by fact
765static inline void scale_model_x (double fact)
766{
767 translate_model(0.0, 0.0, -DZ);
768 apply_to_model(S_x(fact));
769 translate_model(0.0, 0.0, DZ);
770}
771
772// *****************************************************************************
773// scale MODEL along y by fact
774static inline void scale_model_y (double fact)
775{
776 translate_model(0.0, 0.0, -DZ);
777 apply_to_model(S_y(fact));
778 translate_model(0.0, 0.0, DZ);
779}
780
781// *****************************************************************************
782// scale MODEL along z by fact
783static inline void scale_model_z (double fact)
784{
785 translate_model(0.0, 0.0, -DZ);
786 apply_to_model(S_z(fact));
787 translate_model(0.0, 0.0, DZ);
788}
789
790// *****************************************************************************
791// scale MODEL along all axises by fact
792static inline void scale_model_xyz (double fact)
793{
794 translate_model(0.0, 0.0, -DZ);
795 apply_to_model(S_xyz(fact));
796 translate_model(0.0, 0.0, DZ);
797}
798
799
800// *****************************************************************************
801static inline m4_t calc_axises_and_position_of_model()
802{
803 v4_t com = {COM.x, COM.y, COM.z, COM.w};
804 v4_t x = {0.0, 0.0, 0.0, 0.0};
805 v4_t y = {0.0, 0.0, 0.0, 0.0};
806 v4_t z = {0.0, 0.0, 0.0, 0.0};
807
808 m4_t M = {
809 x.x, y.x, z.x, com.x,
810 x.y, y.y, z.y, com.y,
811 x.z, y.z, z.z, com.z,
812 x.w, y.w, z.w, com.w,
813 };
814 return M;
815}
816
817// *****************************************************************************
818// calc the center of mass of the model to get a rotation center
819static inline v4_t calc_center_of_mass ()
820{
821 double sum_x = 0.0;
822 double sum_y = 0.0;
823 double sum_z = 0.0;
824
825 for (int i = 0; i < VERTICES_COUNT; i++)
826 {
827 sum_x += MODEL[i].x;
828 sum_y += MODEL[i].y;
829 sum_z += MODEL[i].z;
830 }
831 COM.x = sum_x / VERTICES_COUNT;
832 COM.y = sum_y / VERTICES_COUNT;
833 COM.z = sum_z / VERTICES_COUNT;
834 COM.w = 1.0;
835 return COM;
836}
837
838// *****************************************************************************
839// apply camera transform to VERTICES_OUTPUT_BUFFER (!)
840static inline void camera_transform ()
841{
842 v4_t za = norm_v4(sub_v4v4(EYE, AT));
843 v4_t xa = norm_v4(cross_v4v4(UP, za));
844 v4_t ya = cross_v4v4(za, xa);
845
846 m4_t M = {
847 xa.x, ya.x, za.x, 0.0,
848 xa.y, ya.y, za.y, 0.0,
849 xa.z, ya.z, za.z, 0.0,
850 -dot_v4v4(xa,EYE), -dot_v4v4(ya,EYE), -dot_v4v4(za,EYE), 1.0
851 };
852 apply_to_vertices_output_buffer(M);
853}
854
855// *****************************************************************************
856// apply projection transform to VERTICES_OUTPUT_BUFFER (!)
857static inline void projection_transform ()
858{
859 // apply projection matrix
860 double W = 1.0 / tan(FOV * 0.5);
861 double H = 1.0 / tan(FOV * 0.5);
862
863 double A = FAR / (NEAR - FAR);
864 double B = (NEAR * FAR) / (NEAR - FAR);
865
866 m4_t M = {
867 W, 0.0, 0.0, 0.0,
868 0.0, H, 0.0, 0.0,
869 0.0, 0.0, A, -1.0,
870 0.0, 0.0, B, 0.0
871 };
872 apply_to_vertices_output_buffer(M);
873
874 // perspective divide
875 double w_len = 0.0;
876 for (int i = 0; i < VERTICES_COUNT; i++)
877 {
878 w_len = 1.0 / VERTICES_OUTPUT_BUFFER[i].w;
879 VERTICES_OUTPUT_BUFFER[i] = scalar_mult_v4(VERTICES_OUTPUT_BUFFER[i], w_len);
880 }
881}
882
883// *****************************************************************************
884// apply viewport transform to VERTICES_OUTPUT_BUFFER (!)
885static inline void viewport_transform ()
886{
887 m4_t M = {
888 20.0, 0.0, 0.0, 0.0,
889 0.0, 20.0, 0.0, 0.0,
890 0.0, 0.0, 1.0, 0.0,
891 0.0, 0.0, 0.0, 1.0
892 };
893 apply_to_vertices_output_buffer(M);
894}
895
896// *****************************************************************************
897// apply world transform to VERTICES_OUTPUT_BUFFER (!)
898static inline void world_transform ()
899{
900 apply_to_vertices_output_buffer(identity_matrix());
901}
902
903// *****************************************************************************
904// check if a polygon is visible to the viewer (backface culling)
905static inline bool is_polygon_visible (polygon_t polygon)
906{
907 // get first 3 vertices
908 v4_t vertice_A = VERTICES_OUTPUT_BUFFER[polygon.v[0]];
909 v4_t vertice_B = VERTICES_OUTPUT_BUFFER[polygon.v[1]];
910 v4_t vertice_C = VERTICES_OUTPUT_BUFFER[polygon.v[2]];
911
912 // Calc the vectors between the vertices AB and CD
913 // (A - B) (not B - A) to get the right orientation for vector_AB
914 v4_t vector_AB = sub_v4v4(vertice_A, vertice_B);
915
916 // (B - C) cause it is already correctly orientated
917 v4_t vector_BC = sub_v4v4(vertice_B, vertice_C);
918
919 // Calc cross prod of both vectors to get the normal vector of the polygon
920 v4_t cross = cross_v4v4(vector_AB, vector_BC);
921
922 // Calc dot product of normalized cross and eye - viewpoint
923 v4_t view_line = sub_v4v4(EYE, vertice_A);
924 double dot = dot_v4v4(norm_v4(cross), norm_v4(view_line));
925
926 // Case: dot > 0 => backfacing
927 // Case: dot = 0 => viewed on edge
928 // Case: dot < 0 => front facing
929 return (dot < 0.0) ? true : false;
930}
931
932// *****************************************************************************
933// draw a polygon in 2d space
934static inline void draw_2d_polygon(polygon_t polygon)
935{
936 // count vertices in polygon
937 int vertice_count = 0;
938 while (polygon.v[++vertice_count] != polygon.v[0]);
939
940 // project polygon's vertices from 3d -> 2d
941 double d = NEAR - EYE.z;
942 v2_t vertices_2d[MAX_VERTICES_PER_POLYGON];
943 v3_t vertice_3d;
944 for (int i = 0; i <= vertice_count; i++)
945 {
946 vertice_3d.x = VERTICES_OUTPUT_BUFFER[polygon.v[i]].x;
947 vertice_3d.y = VERTICES_OUTPUT_BUFFER[polygon.v[i]].y;
948 vertice_3d.z = VERTICES_OUTPUT_BUFFER[polygon.v[i]].z;
949
950 vertices_2d[i].x = (int) (d * (vertice_3d.x / vertice_3d.z));
951 vertices_2d[i].y = (int) (d * (vertice_3d.y / vertice_3d.z));
952 }
953
954 // draw all polygon vertices in order in 2d
955 for (int i = 0; i < vertice_count; i++)
956 {
957 x_line(vertices_2d[i ].x, vertices_2d[i ].y,
958 vertices_2d[i+1].x, vertices_2d[i+1].y);
959 }
960}
961
962// *****************************************************************************
963// draw all visible polygons in 2d space
964static inline void draw_2d_polygons()
965{
966 int i = 0;
967 while (POLYGONS[i].v[0] != -1)
968 {
969 if (is_polygon_visible(POLYGONS[i]))
970 draw_2d_polygon(POLYGONS[i]);
971 ++i;
972 }
973}
974
975// *****************************************************************************
976// redraw the whole scene
977static inline void draw_scene()
978{
979 if(!IS_DBUFFER_ENABLED)
980 XClearWindow(DISPLAY, WINDOW);
981
982 //copy model vertices to output buffer for transforms
983 for (int i = 0; i < VERTICES_COUNT; i++)
984 // ...from here on we work on VERTICES_OUTPUT_BUFFER
985 VERTICES_OUTPUT_BUFFER[i] = MODEL[i];
986
987 // do the 3d stuff
988 world_transform();
989 camera_transform();
990 projection_transform();
991 viewport_transform();
992
993 // draw stuff in 2d
994 XSetLineAttributes(DISPLAY, GCON, LINE_WIDTH, LineSolid, CapNotLast, JoinMiter);
995 draw_2d_polygons();
996
997 // Double buffering
998 if(IS_DBUFFER_ENABLED)
999 {
1000 XdbeSwapInfo swapInfo;
1001 swapInfo.swap_window = WINDOW;
1002 swapInfo.swap_action = XdbeBackground;
1003 XdbeSwapBuffers(DISPLAY, &swapInfo, 1);
1004 }
1005
1006#ifdef DEBUG
1007 printf ("model angles: X:%8.1f Y:%8.1f Z:%8.1f\n",
1008 MODEL_ANGLES.x, MODEL_ANGLES.y, MODEL_ANGLES.z);
1009#endif
1010
1011}
1012
1013// *****************************************************************************
1014// change angles of ship with each call
1015static inline void auto_angle(double &tx, double &ty, double &tz)
1016{
1017 static double dx = 0.0;
1018 const double amp = 1.1;
1019 const double wlen = 0.015;
1020
1021 tx = sin(dx * wlen) * amp + (amp * 0.75);
1022 ty = -cos(dx * wlen) * amp + (amp * 0.75);
1023 tz = -cos(dx * wlen) * amp + (amp * 0.75);
1024
1025 if (dx > 100000000.0f)
1026 dx = 0.0;
1027 dx += 1.0;
1028}
1029
1030// *****************************************************************************
1031// set inital position and angles of the ship
1032static inline void model_init()
1033{
1034 for (int i = 0; i < VERTICES_COUNT; i++)
1035 MODEL[i] = BASE_MODEL[i];
1036
1037 MODEL_ANGLES.x = 0.0;
1038 MODEL_ANGLES.y = 0.0;
1039 MODEL_ANGLES.z = 0.0;
1040
1041 v4_t com = calc_center_of_mass();
1042 translate_model(com.x, com.y, (DZ - com.z));
1043#ifdef DEBUG
1044 printf("center of mass: %3.6f, %3.6f, %3.6f \n", com.x, com.y, com.z);
1045#endif
1046}
1047
1048// *****************************************************************************
1049int main ()
1050{
1051
1052 srand(time(NULL));
1053 x_init();
1054 model_init();
1055
1056 double dt = 2; // stepping angle
1057 bool stepping = false; // stepping activated
1058
1059 int x11_fd = ConnectionNumber( DISPLAY );
1060 fd_set readfds;
1061 timeval timeout;
1062 XEvent event;
1063 for(;;)
1064 {
1065 FD_ZERO(&readfds);
1066 FD_SET(x11_fd, &readfds);
1067
1068 // consider timeout to be undefined after select()
1069 timeout.tv_sec = 0;
1070 timeout.tv_usec = 32768; // (1 / tv_usec) ~ fps
1071
1072 // select() returns 0 if an event was received
1073 // or !0 if the timeout fired. (thnx 2 AngryLlama [1])
1074 select((x11_fd + 1), &readfds, 0, 0, &timeout);
1075
1076 // we can now check for pending events before handling them
1077 while (XPending(DISPLAY))
1078 {
1079 XNextEvent(DISPLAY, &event);
1080
1081 // handle expose
1082 if (event.type == Expose && event.xexpose.count == 0)
1083 {
1084 adjust_to_new_window_dimensions();
1085 }
1086 // handle 'q'
1087 else if (event.type == KeyPress &&
1088 XLookupKeysym(&event.xkey, 0) == XK_q )
1089 {
1090 x_close();
1091 }
1092
1093
1094
1095 // handle 'Right' - rotate 1 dt step about y axis (nose right)
1096 else if (event.type == KeyPress &&
1097 XLookupKeysym(&event.xkey, 0) == XK_Right)
1098 {
1099 rotate_model_y(dt);
1100 stepping = true;
1101 }
1102 // handle 'Left' - rotate 1 dt step about y axis (nose left)
1103 else if (event.type == KeyPress &&
1104 XLookupKeysym(&event.xkey, 0) == XK_Left)
1105 {
1106 rotate_model_y(-dt);
1107 stepping = true;
1108 }
1109 // handle 'Up' - rotate 1 dt step about x axis (nose-up)
1110 else if (event.type == KeyPress &&
1111 XLookupKeysym(&event.xkey, 0) == XK_Up)
1112 {
1113 rotate_model_x(dt);
1114 stepping = true;
1115 }
1116 // handle 'Down' - rotate 1 dt step about x axis (nose down)
1117 else if (event.type == KeyPress &&
1118 XLookupKeysym(&event.xkey, 0) == XK_Down)
1119 {
1120 rotate_model_x(-dt);
1121 stepping = true;
1122 }
1123 // handle 'n' - rotate 1 dt step about z axis (roll left)
1124 else if (event.type == KeyPress &&
1125 XLookupKeysym(&event.xkey, 0) == XK_n)
1126 {
1127 rotate_model_z(dt);
1128 stepping = true;
1129 }
1130 // handle 'm' - rotate 1 dt step about z axis (roll right)
1131 else if (event.type == KeyPress &&
1132 XLookupKeysym(&event.xkey, 0) == XK_m)
1133 {
1134 rotate_model_z(-dt);
1135 stepping = true;
1136 }
1137
1138
1139
1140 // handle 'r' - reset all angles of model
1141 else if (event.type == KeyPress &&
1142 XLookupKeysym(&event.xkey, 0) == XK_r)
1143 {
1144 model_init();
1145 stepping = true;
1146 }
1147 // handle 'SPACE' - pause/resume auto-rotate
1148 else if (event.type == KeyPress &&
1149 XLookupKeysym(&event.xkey, 0) == XK_space)
1150 {
1151 stepping = !stepping;
1152 }
1153 else;
1154 }
1155 // auto-rotate
1156 if (!stepping)
1157 {
1158 // determine new auto-rotate angles and rotate model
1159 auto_angle(MODEL_ANGLES.x, MODEL_ANGLES.y, MODEL_ANGLES.z);
1160 rotate_model_x(MODEL_ANGLES.x);
1161 rotate_model_y(-MODEL_ANGLES.y);
1162 rotate_model_z(MODEL_ANGLES.z);
1163 }
1164 draw_scene();
1165 }
1166 return 0;
1167}
1168
1169
1170
1171// Refs:
1172// [1] http://www.linuxquestions.org/questions/programming-9/xnextevent-select-409355/#post2431345