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
|
#include <netpbm/pam.h>
#include <netpbm/shhopt.h>
#include <netpbm/mallocvar.h>
#include <stdbool.h>
#include <stdint.h>
#define MAX_MAXVAL 65535
#define MAX_NUM_ATTRIBS 20
#define MAX_Z ((1 << 30) - 1)
/*----------------------------------------------------------------------------
Struct definitions
----------------------------------------------------------------------------*/
typedef struct {
/*----------------------------------------------------------------------------
This struct and the functions that manipulate variables of this type act
as a substitute for floating point computations. Here, whenever we need a
value with a fractional component, we represent it using two parts: 1. An
integer part, called the "quotient", and 2. A fractional part, which is
itself composed of a "remainder" (or "numerator") and a "divisor" (or
"denominator"). The fract struct provides storage for the quotient and the
remainder, but the divisor must be given separately (because it often
happens in this program that whenever we are dealing with one variable of
type fract, we are dealing with more of them at the same time, and they
all have the same divisor).
To be more precise, the way we actually use variables of this type works
like this: We read integer values through standard input; When drawing
triangles, we need need to calculate differences between some pairs of
these input values and divide such differences by some other integer,
which is the above mentioned divisor. That result is then used to compute
successive interpolations between the two values for which we had
originally calculated the difference, and is therefore called the
"interpolation step". The values between which we wish to take successive
interpolations are called the "initial value" and the "final value". The
interpolation procedure works like this: First, we transform the initial
value into a fract variable by equating the quotient of that variable to
the initial value and assigning 0 to its remainder. Then, we successivelly
apply the interpolation step to that variable through successive calls to
step_up and/or multi_step_up until the quotient of the variable equals the
final value. Each application of step_up or multi_step_up yields a
particular linear interpolation between the initial and final values.
If and only if a particular fract variable represents an interpolation
step, the "negative_flag" field indicates whether the step is negative
(i. e. negative_flag == true) or not (negative_flag == false). This is
necessary in order to make sure that variables are "stepped up" in the
appropriate direction, so to speak, as the field which stores the
remainder in any fract variable, "r", is always equal to or above 0, and
the quotient of a step may be 0, so the actual sign of the step value is
not always discoverable through a simple examination of the sign of the
quotient. On the other hand, if the variable does not represent an
interpolation step, the negative_flag is meaningless.
-----------------------------------------------------------------------------*/
int32_t q; /* Quotient */
int32_t r: 31; /* Remainder */
bool negative_flag: 1;
} fract;
/* Each of the following structs has only one instance, which are created in
the main function.
*/
typedef struct {
/*----------------------------------------------------------------------------
Information about the frame buffer and PAM output
-----------------------------------------------------------------------------*/
/* These fields are initialized once by reading the command line
arguments. "maxval" and "num_attribs" may be modified later
through "realloc_image_buffer".
*/
int32_t width;
int32_t height;
int32_t maxval;
int32_t num_attribs;
/* The fields below must be initialized by "init_framebuffer" and
freed by "free_framebuffer", except for the tuple_type field in
"outpam" which is initialized once by reading the command line
arguments and may be modified later through "set_tupletype".
*/
struct {
uint16_t * buffer;
uint32_t bytes;
} img; /* Image buffer */
struct {
uint32_t * buffer;
uint32_t bytes;
} z; /* Z-buffer */
struct pam outpam;
tuple * pamrow;
} framebuffer_info;
typedef struct {
/*----------------------------------------------------------------------------
Information about visible triangle rows' boundaries. Also see the
"boundary buffer functions" below.
A "visible" triangle row is one which:
1. Corresponds to a frame buffer row whose index (from top to bottom) is
equal to or greater than 0 and smaller than the image height; and
2. Has at least some of its pixels between the frame buffer columns whose
index (from left to right) is equal to or greater than 0 and smaller
than the image width.
-----------------------------------------------------------------------------*/
int16_t start_scanline;
/* Index of the frame buffer scanline which contains the first visible
row of the current triangle, if there is any such row. If not, it
contains the value -1.
*/
int16_t num_upper_rows;
/* The number of visible rows in the upper part of the triangle. The
upper part of a triangle is composed of all the rows starting from
the top vertex down to the middle vertex, but not including this
last one.
*/
int16_t num_lower_rows;
/* The number of visible rows in the lower part of the triangle. The
lower part of a triangle is composed of all the rows from the
middle vertex to the bottom vertex -- all inclusive.
*/
int16_t * buffer;
/* This is the "boundary buffer": a pointer to an array of int16_t's
where each consecutive pair of values indicates, in this order, the
columns of the left and right boundary pixels for a particular
visible triangle row. Those boundaries are inclusive on both sides
and may be outside the limits of the frame buffer. This field is
initialized and freed by the functions "init_boundary_buffer" and
"free_boundary_buffer", respectively.
*/
} boundary_info;
typedef struct {
/*----------------------------------------------------------------------------
Information necessary for the "process_next_command" function. It must be
initialized through "init_input_processor" and freed by
"free_input_processor".
-----------------------------------------------------------------------------*/
char * buffer;
size_t length;
uint64_t number;
} input_info;
/*----------------------------------------------------------------------------
Utility functions
-----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------
Generate the interpolation steps for a collection of initial and final
values. "begin" points to an array of initial values, "end" points to the
array of corresponding final values; each interpolation step is stored in
the appropriate position in the array pointed by "out"; "elements" indicates
the number of elements in each of the previously mentioned arrays and
"divisor" is the common value by which we want to divide the difference
between each element in the array pointed to by "end" and the corresponding
element in the array pointed to by "begin". After an execution of this
function, for each out[i], with 0 <= i < elements, the following will hold:
1. If divisor > 1:
out[i].q = (end[i] - begin[i]) / divisor
out[i].r = abs((end[i] - begin[i]) % divisor)
2. If divisor == 1 || divisor == 0:
out[i].q = end[i] - begin[i]
out[i].r = 0
-----------------------------------------------------------------------------*/
void
gen_steps(const int32_t * begin,
const int32_t * end,
fract * out,
uint8_t elements,
int32_t divisor);
/*----------------------------------------------------------------------------
Apply interpolation steps (see above) to a collection of fract
variables (also see above) once. This is done by adding the
quotient of each step to the quotient of the corresponding variable
and the remainder of that step to the remainder of the variable. If the
remainder of the variable becomes equal to or larger than the
divisor, we increment the quotient of the variable if the negetive_flag
of the step is false, or decrement it if the negetive_flag is true, and
subtract the divisor from the remainder of the variable (in both cases).
It *is* safe to pass a 0 divisor to this function.
-----------------------------------------------------------------------------*/
void
step_up(fract * vars,
const fract * steps,
uint8_t elements,
int32_t divisor);
/*----------------------------------------------------------------------------
Similar to step_up, but apply the interpolation step an arbitrary number
of times, instead of just once.
It *is* also safe to pass a 0 divisor to this function.
-----------------------------------------------------------------------------*/
void
multi_step_up(fract * vars,
const fract * steps,
uint8_t elements,
int32_t times,
int32_t divisor);
void
fract_to_int32_array(const fract * in,
int32_t * out,
uint8_t elements);
void
int32_to_fract_array(const int32_t * in,
fract * out,
uint8_t elements);
/*----------------------------------------------------------------------------
Sort an index array of 3 elements. This function is used to sort vertices
with regard to relative row from top to bottom, but instead of sorting
an array of vertices with all their coordinates, we simply sort their
indices. Each element in the array pointed to by "index_array" should
contain one of the numbers 0, 1 or 2, and each one of them should be
different. "y_array" should point to an array containing the corresponding
Y coordinates (row) of each vertex and "x_array" should point to an array
containing the corresponding X coordinates (column) of each vertex.
If the Y coordinates are all equal, the indices are sorted with regard to
relative X coordinate from left to right. If only the top two vertex have
the same Y coordinate, the array is sorted normally with regard to relative
Y coordinate, but the first two indices are then sorted with regard to
relative X coordinate. Finally, If only the bottom two vertex have the same
Y coordinate, the array is sorted normally with regard to relative Y
coordinate, but the last two indices are then sorted with regard to relative
X coordinate.
-----------------------------------------------------------------------------*/
void
sort3(uint8_t * index_array,
const int32_t * y_array,
const int32_t * x_array);
/*----------------------------------------------------------------------------
Frame buffer functions
------------------------------------------------------------------------------
Every drawing operation is applied on an internal "frame buffer", which is
simply an "image buffer" which represents the picture currently being drawn,
along with a "Z-Buffer" which contains the depth values for every pixel in
the image buffer. Once all desired drawing operations for a particular
picture are effected, a function is provided to print the current contents
of the image buffer as a PAM image on standard output. Another function is
provided to clear the contents of the frame buffer (i. e. set all image
samples and Z-Buffer entries to 0), with the option of only clearing either
the image buffer or the Z-Buffer individually.
The Z-Buffer works as follows: Every pixel in the image buffer has a
corresponding entry in the Z-Buffer. Initially, every entry in the Z-Buffer
is set to 0. Every time we desire to plot a pixel at some particular
position in the frame buffer, the current value of the corresponding entry
in the Z-Buffer is compared against the the Z component of the incoming
pixel. If MAX_Z minus the value of the Z component of the incoming pixel is
equal to or greater than the current value of the corresponding entry in the
Z-Buffer, the frame buffer is changed as follows:
1. All the samples but the last of the corresponding position in the
image buffer are set to equal those of the incoming pixel.
2. The last sample, that is, the A-component of the corresponding position
in the image buffer is set to equal the maxval.
3. The corresponding entry in the Z-Buffer is set to equal MAX_Z minus the
value of the Z component of the incoming pixel.
Otherwise, no changes are made on the frame buffer.
-----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------
Set the tuple type for the output PAM images given a string ("str") of 255
characters or less. If the string has more than 255 characters, the function
returns 0. Otherwise, it returns 1. If NULL is given for the "str" argument,
the tuple type is set to a null string. This function is called during
program initialization and whenever a "r" command is executed. The second
argument must point to the tuple_type member of the "outpam" field in the
framebuffer_info struct.
-----------------------------------------------------------------------------*/
int
set_tupletype(const char * str,
char tupletype[256]);
int
init_framebuffer(framebuffer_info *);
void
free_framebuffer(framebuffer_info *);
void
print_framebuffer(framebuffer_info *);
void
clear_framebuffer(bool clear_image_buffer,
bool clear_z_buffer,
framebuffer_info *);
/*----------------------------------------------------------------------------
Reallocate the image buffer with a new maxval and depth, given the struct
with information about the framebuffer. The fields variables "maxval" and
"num_attribs".
From the point this function is called onwards, new PAM images printed on
standard output will have the new maxval for the maxval and num_attribs + 1
for the depth.
This function does *not* check whether the new maxval and num_attribs are
within the proper allowed limits. That is done inside the input processing
function "process_next_command", which is the only function that calls this
one.
If the function suceeds, the image buffer is left in cleared state. The
Z-Buffer, however, is not touched at all.
If the new depth is equal to the previous one, no actual reallocation is
performed: only the global variable "maxval" is changed. But the image
buffer is nonetheless left in cleared state regardless.
-----------------------------------------------------------------------------*/
int
realloc_image_buffer(int32_t new_maxval,
int32_t new_num_attribs,
framebuffer_info *);
/*----------------------------------------------------------------------------
Draw a horizontal span of "length" pixels into the frame buffer, performing
the appropriate depth tests. "base" must equal the row of the frame buffer
where one desires to draw the span *times* the image width, plus the column
of the first pixel in the span.
This function does not perform any kind of bounds checking.
-----------------------------------------------------------------------------*/
void draw_span(uint32_t base,
uint16_t length,
fract * attribs_start,
const fract * attribs_steps,
int32_t divisor,
framebuffer_info *);
/*----------------------------------------------------------------------------
Boundary buffer functions
------------------------------------------------------------------------------
New triangles are drawn one scanline at a time, and for every such scanline
we have left and right boundary columns within the frame buffer such that
the fraction of the triangle's area within that scanline is enclosed
between those two points (inclusive). Those coordinates may correspond to
columns outside the frame buffer's actual limits, in which case proper
post-processing should be made wherever such coordinates are used to
actually plot anything into the frame buffer.
-----------------------------------------------------------------------------*/
void
init_boundary_buffer(boundary_info * ,
int16_t height);
void
free_boundary_buffer(boundary_info *);
/*----------------------------------------------------------------------------
Generate an entry in the boundary buffer for the boundaries of every
VISIBLE row of a particular triangle. In case there is no such row,
start_row is accordingly set to -1. The argument is a 3-element array
of pairs of int16_t's representing the coordinates of the vertices of
a triangle. Those vertices MUST be already sorted in order from the
uppermost to the lowermost vertex (which is what draw_triangle, the
only function which uses this one, does with the help of sort3).
The return value indicates whether the middle vertex is to the left of the
line connecting the top vertex to the bottom vertex or not.
-----------------------------------------------------------------------------*/
bool
gen_triangle_boundaries(int32_t xy[3][2],
boundary_info *,
int16_t width,
int16_t height);
/*----------------------------------------------------------------------------
Return the left and right boundaries for a given VISIBLE triangle row (the
row index is relative to the first visible row). These values may be out of
the horizontal limits of the frame buffer, which is necessary in order to
compute correct attribute interpolations.
-----------------------------------------------------------------------------*/
void
get_triangle_boundaries(uint16_t row_index,
int32_t * left,
int32_t * right,
const boundary_info *);
/*----------------------------------------------------------------------------
Triangle functions
-----------------------------------------------------------------------------*/
void
draw_triangle(int32_t xy[3][2],
int32_t attribs[3][MAX_NUM_ATTRIBS + 1],
boundary_info *,
framebuffer_info *);
/*----------------------------------------------------------------------------
Input handling functions
-----------------------------------------------------------------------------*/
void
init_input_processor(input_info *);
void
free_input_processor(input_info *);
/*----------------------------------------------------------------------------
Doesn't necessarily process a command, just the next line of input, which
may be empty. Always returns 1, except when it cannot read any more lines of
input, an image buffer reallocation fails, or a "q" command is found in the
input -- in such cases it returns 0.
-----------------------------------------------------------------------------*/
int
process_next_command(input_info *,
boundary_info *,
framebuffer_info *);
|