File: render1.c

package info (click to toggle)
coolmail 1.3-15
  • links: PTS
  • area: main
  • in suites: forky, sid
  • size: 312 kB
  • sloc: ansic: 1,527; makefile: 69
file content (580 lines) | stat: -rw-r--r-- 18,202 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
/* render1.c
 *
 * By Byron C. Darrah
 * 4/28/94, Thursday
 *
 * render1 renders simple lists of polygons.  render1 keeps an aggregate
 * list of all polygons submitted to it, and also keeps an index to this
 * list keyed by object ID numbers, so that it knows which polygons
 * belong to which objects.
 */

/* Here is the implementation of the renderer called render1.  */

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <assert.h>
#include <string.h>

#include "render1.h"
#include "display_list.h"

typedef float rend_matrix_t[4][4];
rend_matrix_t camera_transformation;

float perspective,
      default_perspective = DEFAULT_PERSPECTIVE;

polygon_node_t ag_polygon_list;   /* A dummy node to be the head of list */

/* Here is the structure for the nodes of the object ID index to the
 * aggregate list.
 */
typedef struct obj_ki_struct
{
   struct obj_ki_struct  *next;
   int                    obj_id;
   polygon_node_t        *pnodes;    /* array of polygon nodes. */
   int                    npolygons; /* size of above array.    */
} obj_node_t;

obj_node_t obj_id_index;    /* This list also has a dummy node for a head */

/*---------------------------------------------------------------------------*/
/*---------------------- INTERNAL FUNCTION PROTOTYPES -----------------------*/
/*---------------------------------------------------------------------------*/

void        rend_agg_poly_insert(polygon_node_t *pnode);
obj_node_t *rend_obj_lookup(int obj_id);
void        mtx_mult_4x4(rend_matrix_t f1, rend_matrix_t f2,
                         rend_matrix_t product);
void        mtx_transform_pt(real_point_t *pos, rend_matrix_t m1,
                             real_point_t *new_pos);
void        mtx_transform_pg(polygon_node_t *polygon_node);

/*---------------------------------------------------------------------------*/
/*--------------------------- EXPORTED FUNCTIONS ----------------------------*/
/*---------------------------------------------------------------------------*/

/* This sets the camera matrix to a default value, initializes the
 * polygon lists, and the display.
 */

void rend_init(int *argc, char *argv[], float init_perspect)
{
   real_point_t c_position = {0.0,  0.0, -20.0}; /* Camera positon         */
   real_point_t c_focus    = {0.0,  0.0,   0.0}, /* Camera focus direction */
                c_up       = {0.0, 10.0,   0.0}; /* Camera up direction    */
   int i, color = 1;

   /* Scan for the -mono option in the argument list */
   for(i = 0; i < *argc; i++)
   {
      if(color)
      {
         if(!strcmp(argv[i], "-mono"))
         {
            color = 0;
            (*argc)--;
            if(i < *argc)
               argv[i] = argv[i+1];
         }
      }
      else
         argv[i] = argv[i+1];
   }
   

   /* Initialize the display */
   disp_init(argc, argv, color);

   ag_polygon_list.next = NULL;
   ag_polygon_list.prev = NULL;

   obj_id_index.next      = NULL;
   obj_id_index.pnodes    = NULL;
   obj_id_index.npolygons = 0;

   rend_set_camera(&c_position, &c_focus, &c_up);

   /* Initialize the perspective.  If no perspective value was given,
    * set it to the default perspective.
    */
   if (init_perspect != 0.0) default_perspective = init_perspect;

   /* Now work in a fudge-factor to cause points to be scaled according
    * to the display window size. */
   if (disp_x_scale < disp_y_scale)
      perspective = default_perspective * disp_x_scale;
   else
      perspective = default_perspective * disp_y_scale;

}

/*---------------------------------------------------------------------------*/

void rend_end(void)
{
   disp_end();
}

/*---------------------------------------------------------------------------*/

/*      Synchronize the scale of drawing operations with the size of the
 *      actual output window, and redraw the output frame.
 */

void rend_setsize(void)
{
   unsigned short width, height;

   disp_getsize(&width, &height);
   disp_setsize(width, height);

   /* Now work in a fudge-factor to cause points to be scaled according
    * to the display window size. */
   if (disp_x_scale < disp_y_scale)
      perspective = default_perspective * disp_x_scale;
   else
      perspective = default_perspective * disp_y_scale;

   /* Now that the drawing size has been adjusted, redraw the output */
   rend_transform_update_all();
   rend_frame();
}

/*---------------------------------------------------------------------------*/


/* This is used to set the camera matrix, which describes the position
 * and orientation of the POV.  Whenever the camera matrix is changed,
 * the entire list of polygons must be recomputed.
 *
 * The position and orientation of the camera determines what gets displayed
 * (obviously).  As a result, the camera's orientation is used to determine
 * a transformation which will be applied to all points to map them
 * to screen coordinates.  For more information, see "Faster 3-D Drawing"
 * by Brian Carmichael, in The AmigaWorld Tech Journal, pg 20, January/February
 * 1992, or any decent 3-D rendering text.
 *
 * Whenever the camera is moved, the aggregate polygon list must be
 * recomputed (because the mapping of sim-space to screen-space is
 * changed).  This can be done by calling rend_transform_update_all().
 * This is an time-expensive operation, so it is good if the calling
 * application can remove from the aggregate list any objects that are going
 * to resubmit their polygons before the next frame-render, so these objects
 * will not have to be redundantly transformed.
 */

void rend_set_camera(real_point_t *position,
                     real_point_t *focus,
                     real_point_t *up)
{
   rend_matrix_t m1, m2, m3;
   float cosAy, sinAy, cosAx, sinAx, cosAz, sinAz;
   float Dxz, Dyz, Dxy;
   real_point_t new_pos;


   /* let m1 be the transformation matrix for the translation, which moves
    * the camera's focus point to the origin.
    */

   m1[0][0] = 1; m1[0][1] =     m1[0][2] =    m1[0][3] = 0;
   m1[1][0] = 0; m1[1][1] =  1; m1[1][2] =    m1[1][3] = 0;
   m1[2][0] =    m1[2][1] =  0; m1[2][2] = 1; m1[2][3] = 0;
   m1[3][0] = -position->x;
   m1[3][1] = -position->y;
   m1[3][2] = -position->z;
   m1[3][3] = 1;

   /* Compute the camera focus relative to the position.
    */
   new_pos.x = focus->x - position->x;
   new_pos.y = focus->y - position->y;
   new_pos.z = focus->z - position->z;

   /* Now compute the cosine and sine of the y-rotation angle, Ay,
    * between the focus vector and the zy plane.
    */
   Dxz = sqrt((double)(new_pos.x * new_pos.x + new_pos.z * new_pos.z));
   if (Dxz != 0.0)
   {
      cosAy = new_pos.z / Dxz;
      sinAy = -new_pos.x / Dxz;
   }
   else  /* Uh-oh - handle indeterminate case */
   {
      cosAy = 1.0;
      sinAy = 0.0;
   }

   /* let m2 be the transformation matrix for rotating the focus point about
    * the y-axis onto the zy plane.
    */

   m2[0][0] = cosAy;  m2[0][1] = 0;  m2[0][2] = -sinAy;  m2[0][3] = 0;
   m2[1][0] = 0;      m2[1][1] = 1;  m2[1][2] = 0;       m2[1][3] = 0;
   m2[2][0] = sinAy;  m2[2][1] = 0;  m2[2][2] = cosAy;   m2[2][3] = 0;
   m2[3][0] = 0;      m2[3][1] = 0;  m2[3][2] = 0;       m2[3][3] = 1;

   /* let m3 be the product of m1 and m2 */
   mtx_mult_4x4(m1, m2, m3);

   /* Update the focus pos by multiplying it by m2. */
   new_pos.x = new_pos.x *  cosAy + new_pos.z * sinAy;
   new_pos.z = new_pos.x * -sinAy + new_pos.z * cosAy;

   /* Now compute the cosine and sine of the x-rotation angle, Ax */
   Dyz = sqrt((double)(new_pos.y * new_pos.y + new_pos.z * new_pos.z));
   cosAx = new_pos.z/Dyz;
   sinAx = new_pos.y/Dyz;

   /* let m2 be the transformation matrix for rotating the focus point about
    * the x-axis onto the +z-axis.
    */
   m2[0][0] = 1;  m2[0][1] = 0;       m2[0][2] = 0;      m2[0][3] = 0;
   m2[1][0] = 0;  m2[1][1] = cosAx;   m2[1][2] = sinAx;  m2[1][3] = 0;
   m2[2][0] = 0;  m2[2][1] = -sinAx;  m2[2][2] = cosAx;  m2[2][3] = 0;
   m2[3][0] = 0;  m2[3][1] = 0;       m2[3][2] = 0;      m2[3][3] = 1;

   /* let m1 be the product of m3 and m2 */
   mtx_mult_4x4(m3, m2, m1);

   /* Update the "up" point pos by multiplying it by m1 */
   mtx_transform_pt(up, m1, &new_pos);

   /* Now compute the cosine and sine of the z-axis rotation angle, Az */
   Dxy = sqrt((double)(new_pos.x * new_pos.x + new_pos.y * new_pos.y));
   cosAz = new_pos.y/Dxy;
   sinAz = new_pos.x/Dxy;

   /* let m2 be the transformation matrix for rotating the up point about
    * the z-axis onto the zy-plane.
    */

   m2[0][0] = cosAz;   m2[0][1] = sinAz;  m2[0][2] = 0;  m2[0][3] = 0;
   m2[1][0] = -sinAz;  m2[1][1] = cosAz;  m2[1][2] = 0;  m2[1][3] = 0;
   m2[2][0] = 0;       m2[2][1] = 0;      m2[2][2] = 1;  m2[2][3] = 0;
   m2[3][0] = 0;       m2[3][1] = 0;      m2[3][2] = 0;  m2[3][3] = 1;

   /* Now the final multiplication gives us the camera transformation: */
   mtx_mult_4x4(m1, m2, camera_transformation);


/* If you want to debug by printing out the camera matrix, uncomment this. */
/*   fprintf(stderr, "---Camera Matrix---\n");
   {
      int row, col;
      for(row = 0; row < 4; row++)
      {
         for(col = 0; col < 4; col++)
            fprintf(stderr, "%6.2f ", camera_transformation[row][col]);
         printf("\n");
      }
   }
   fprintf(stderr, "-------------------\n");
*/

}

/*---------------------------------------------------------------------------*/

/* rend_register_obj is called by objects that wish to use the display.
 * An object that calls this function is "registered", and may submit
 * lists of polygons for display.
 *
 */

void rend_register_obj(int obj_id, int npolygons)
{
   polygon_node_t *newlist;
   obj_node_t     *newobj;

   if ((newlist = (polygon_node_t *)malloc(sizeof(polygon_node_t) * npolygons))
       == NULL)
   {
      fprintf(stderr, "rend_register_obj: out of memory");
      assert(0);
   }

   if ((newobj = (obj_node_t *)malloc(sizeof(obj_node_t))) == NULL)
   {
      fprintf(stderr, "rend_register_obj: out of memory");
      assert(0);
   }

   /* Add the new object to the object ID index list */
   newobj->next = obj_id_index.next;
   obj_id_index.next = newobj;

   /* Now fill in the data for this object node */
   newobj->obj_id    = obj_id;
   newobj->pnodes    = newlist;
   newobj->npolygons = npolygons;
}

/*---------------------------------------------------------------------------*/

/* An object calls this function to submit an updatedlist of polygons
 * to the renderer.  Render1 knows how many polygons to expect, from
 * when the object was registered by rend_register_obj.  It transforms
 * the object's polygons to screen space, and adds them to the aggregate
 * list.  Note: An object must be sure that it has removed it's polygons
 * before resubmitting them, or else the aggregate list will get messed up.
 */

int rend_submit_plist(int obj_id, polygon_t *polygons)
{
   obj_node_t *obj_record;
   int i;
   polygon_node_t *pnode;

   if((obj_record = rend_obj_lookup(obj_id)) == NULL)
   {
      fprintf(stderr, "rend_submit_plist: unknown object %d.\n", obj_id);
      return (R1_OBJ_UNKNOWN);
   }

   /* Add the object's polygons */
   for(i = 0; i < obj_record->npolygons; i++)
   {
      pnode = obj_record->pnodes+i;
      pnode->polygon = polygons+i;
      /* transform the polygons and add them to the aggregate list */
      mtx_transform_pg(pnode);
      rend_agg_poly_insert(pnode);
   }

   return(R1_NORMAL);
}

/*---------------------------------------------------------------------------*/

/* remove an object's polygons from the aggregate display list */

int rend_rm_plist(int obj_id)
{
   obj_node_t *obj_node;
   polygon_node_t *poly_array;
   int i;

   if ((obj_node = rend_obj_lookup(obj_id)) == NULL)
      return(R1_OBJ_UNKNOWN);
   poly_array = obj_node->pnodes;

   /* Since the polygon list is a doubly-linked list, polygons can be
    * removed from it by simply removing the pointers to them in the
    * previous and next nodes.  The following loop does this.
    */
   for (i = 0; i < obj_node->npolygons; i++)
   {
      if (poly_array[i].next)  poly_array[i].next->prev = poly_array[i].prev;
      poly_array[i].prev->next = poly_array[i].next;
      poly_array[i].next = poly_array[i].prev = NULL;
   }
   return(R1_NORMAL);
}

/*---------------------------------------------------------------------------*/

/* Update the entire list of polygons --
 * perform transformations on all polygons and rebuild the aggregate
 * polygon display list in z-sorted order.
 */

void        rend_transform_update_all(void)
{
   polygon_node_t *templist;
   polygon_node_t *curnode;

   /* Make a copy of the aggregate polygon list, then clear it */
   templist = ag_polygon_list.next;
   ag_polygon_list.next = NULL;

   curnode = templist;
   while(curnode)
   {
      templist = curnode->next;
      mtx_transform_pg(curnode);     /* Transform polygon to screen space.   */
      rend_agg_poly_insert(curnode); /* Insert the node to the agg. list.    */
      curnode = templist;            /* Get the next node from the templist. */
   }
}

/*---------------------------------------------------------------------------*/

/* Render a display frame --
 * This outputs the aggregate polygon list to the display device.
 */

void rend_frame(void)
{
   disp_polygons(ag_polygon_list.next);
}

/*---------------------------------------------------------------------------*/

/* Ring the audio bell. */

void rend_bell(void)
{
   disp_bell();
}

/*---------------------------------------------------------------------------*/

/* Freeze the image as is.  Returns when either the specified interval has
 * expired, or a mouseclick has occurred in the image window.
 */

int  rend_freeze(unsigned long interval)
{
   int reason;
   while(1)
   {
      if ((reason = disp_freeze(interval)) == RESIZE)
         rend_setsize();
      else
         return(reason);
   }
         
}

/*---------------------------------------------------------------------------*/
/*--------------------------- INTERNAL FUNCTIONS ----------------------------*/
/*---------------------------------------------------------------------------*/

/* Given a pointer to a polygon_node_t, insert the node into the aggregate
 * polygon display list.
 */

void rend_agg_poly_insert(polygon_node_t *pnode)
{
   polygon_node_t *curnode = &ag_polygon_list;

   /* Find the node that pnode should be inserted after */
   while(curnode->next && (curnode->next->k > pnode->k))
      curnode = curnode->next;

   /* Insert the pnode into the aggregate list */
   if(curnode->next) curnode->next->prev = pnode;
   pnode->next = curnode->next;
   curnode->next = pnode;
   pnode->prev = curnode;
}

/*---------------------------------------------------------------------------*/

/* Given an object id, look up the object's entry in the object list.
 * NULL is returned if the end of the list is reached before the
 * object id is found.
 */

obj_node_t *rend_obj_lookup(int obj_id)
{
   obj_node_t *curnode = obj_id_index.next;

   for(;curnode && curnode->obj_id != obj_id; curnode = curnode->next)
      ;
   return(curnode);
}

/*---------------------------------------------------------------------------*/

/* mtx_mult_4x4 is a function which multiplies two 4x4 matrices and gives the
 * result.
 */

void mtx_mult_4x4(rend_matrix_t f1, rend_matrix_t f2, rend_matrix_t product)
{
   int i, row, col;
   float sum;

   for (row = 0; row < 4; row++)
      for (col = 0; col < 4; col++)
      {
         sum = f1[row][0] * f2[0][col];
         for(i = 1; i < 4; i++)
            sum += f1[row][i] * f2[i][col];
         product[row][col] = sum;
      }
}

/*---------------------------------------------------------------------------*/

/* mtx_transform_pt transforms a point by the specified matrix.  This is used
 *  by other functions to map a point in simulation space to screen space.
 *
 * This routine is optimized based on the assumption that a point is a 1x4
 * matrix with the first three elements equal to the x,y,z of the point, and
 * the last element always equal to one.
 */

void        mtx_transform_pt(real_point_t *pos, rend_matrix_t m1,
                             real_point_t *new_pos)
{
   new_pos->x =   pos->x * m1[0][0] + pos->y * m1[1][0] + pos->z * m1[2][0]
                + m1[3][0];
   new_pos->y =   pos->x * m1[0][1] + pos->y * m1[1][1] + pos->z * m1[2][1]
                + m1[3][1];
   new_pos->z =   pos->x * m1[0][2] + pos->y * m1[1][2] + pos->z * m1[2][2]
                + m1[3][2];
}

/*---------------------------------------------------------------------------*/

/* Given a polygon, transform it to screen space */

void mtx_transform_pg(polygon_node_t *polygon_node)
{
   int i;
   int clipped = 0;
   float scale;
   float d_sum = 0.0;
   polygon_t *polygon = polygon_node->polygon;

   for (i = polygon->npoints-1; i >= 0; i--)
   {
      real_point_t scrn_pt;
      int_point_t  *int_pt = polygon->int_points+i;

      /* Tanslate and rotate each point in the polygon into screen space */
      mtx_transform_pt(polygon->sim_points+i, camera_transformation,
                       &scrn_pt);

      /* Perform perspective scaling adjustment on each point */
      if (scrn_pt.z > 0.0)
      {
         scale = perspective / scrn_pt.z;
         int_pt->x = Gfx_X_Origin + (int)(scrn_pt.x * scale);
         int_pt->y = Gfx_Y_Origin - (int)(scrn_pt.y * scale);
      }

      /* Keep track of the z-sum */
      if (scrn_pt.z > 0.0)
      {
         d_sum += (float) sqrt((double)(scrn_pt.x*scrn_pt.x +
                              scrn_pt.y*scrn_pt.y +
                              scrn_pt.z*scrn_pt.z));
      }
      else  /* Uh-oh: this point is behind the focus -- clip it */
      {
         d_sum   = 0.0;
         clipped = 1;
         break;
      }
   }

   /* Store the z-center of the polygon, which is used to determine where
    * the polygon will fit into the aggregate list.
    */
   if (!clipped)
      polygon_node->k = d_sum/((float)polygon->npoints);
   else
      polygon_node->k = 0.0;
                                             
}

/*---------------------------------------------------------------------------*/