abrash-black-book/53-04.md
2013-12-30 20:26:41 +11:00

138 lines
6.3 KiB
Markdown

**LISTING 53.2 OLIST.C**
/* Object list-related functions. */
#include <stdio.h>
#include "polygon.h"
/* Set up the empty object list, with sentinels at both ends to
terminate searches */
void InitializeObjectList()
{
ObjectListStart.NextObject = &ObjectListEnd;
ObjectListStart.PreviousObject = NULL;
ObjectListStart.CenterInView.Z = INT-TO-FIXED(-32768);
ObjectListEnd.NextObject = NULL;
ObjectListEnd.PreviousObject = &ObjectListStart;
ObjectListEnd.CenterInView.Z = 0x7FFFFFFFL;
NumObjects = 0;
}
/* Adds an object to the object list, sorted by center Z coord. */
void AddObject(Object *ObjectPtr)
{
Object *ObjectListPtr = ObjectListStart.NextObject;
/* Find the insertion point. Guaranteed to terminate because of
the end sentinel */
while (ObjectPtr->CenterInView.Z > ObjectListPtr->CenterInView.Z) {
ObjectListPtr = ObjectListPtr->NextObject;
}
/* Link in the new object */
ObjectListPtr->PreviousObject->NextObject = ObjectPtr;
ObjectPtr->NextObject = ObjectListPtr;
ObjectPtr->PreviousObject = ObjectListPtr->PreviousObject;
ObjectListPtr->PreviousObject = ObjectPtr;
NumObjects++;
}
/* Resorts the objects in order of ascending center Z coordinate in view space,
by moving each object in turn to the correct position in the object list. */
void SortObjects()
{
int i;
Object *ObjectPtr, *ObjectCmpPtr, *NextObjectPtr;
/* Start checking with the second object */
ObjectCmpPtr = ObjectListStart.NextObject;
ObjectPtr = ObjectCmpPtr->NextObject;
for (i=1; i<NumObjects; i++) {
/* See if we need to move backward through the list */
if (ObjectPtr->CenterInView.Z < ObjectCmpPtr->CenterInView.Z) {
/* Remember where to resume sorting with the next object */
NextObjectPtr = ObjectPtr->NextObject;
/* Yes, move backward until we find the proper insertion
point. Termination guaranteed because of start sentinel */
do {
ObjectCmpPtr = ObjectCmpPtr->PreviousObject;
} while (ObjectPtr->CenterInView.Z <
ObjectCmpPtr->CenterInView.Z);
/* Now move the object to its new location */
/* Unlink the object at the old location */
ObjectPtr->PreviousObject->NextObject =
ObjectPtr->NextObject;
ObjectPtr->NextObject->PreviousObject =
ObjectPtr->PreviousObject;
/* Link in the object at the new location */
ObjectCmpPtr->NextObject->PreviousObject = ObjectPtr;
ObjectPtr->PreviousObject = ObjectCmpPtr;
ObjectPtr->NextObject = ObjectCmpPtr->NextObject;
ObjectCmpPtr->NextObject = ObjectPtr;
/* Advance to the next object to sort */
ObjectCmpPtr = NextObjectPtr->PreviousObject;
ObjectPtr = NextObjectPtr;
} else {
/* Advance to the next object to sort */
ObjectCmpPtr = ObjectPtr;
ObjectPtr = ObjectPtr->NextObject;
}
}
}
#### Rounding {#Heading6}
FIXED.ASM contains the equate **ROUNDING-ON**. When this equate is 1,
the results of multiplications and divisions are rounded to the nearest
fixed-point values; when it's 0, the results are truncated. The
difference between the results produced by the two approaches is, at
most, 2^-16^; you wouldn't think that would make much difference, now,
would you? But it does. When the animation is run with rounding
disabled, the cubes start to distort visibly after a few minutes, and
after a few minutes more they look like they've been run over. In
contrast, I've never seen any significant distortion with rounding on,
even after a half-hour or so. I think the difference with rounding is
not that it's so much more accurate, but rather that the errors are
evenly distributed; with truncation, the errors are biased, and biased
errors become very visible when they're applied to right-angle objects.
Even with rounding, though, the errors will eventually creep in, and
reorthogonalization will become necessary at some point.
The performance cost of rounding is small, and the benefits are highly
visible. Still, truncation errors become significant only when they
accumulate over time, as, for example, when rotation matrices are
repeatedly concatenated over the course of many transformations. Some
time could be saved by rounding only in such cases. For example,
division is performed only in the course of projection, and the results
do not accumulate over time, so it would be reasonable to disable
rounding for division.
#### Having a Ball {#Heading7}
So far in our exploration of 3-D animation, we've had nothing to look at
but triangles and cubes. It's time for something a little more visually
appealing, so the demonstration program now features a 72-sided ball.
What's particularly interesting about this ball is that it's created by
the GENBALL.C program in the BALL subdirectory of X-Sharp, and both the
size of the ball and the number of bands of faces are programmable.
GENBALL.C spits out to a file all the arrays of vertices and faces
needed to create the ball, ready for inclusion in INITBALL.C. True, if
you change the number of bands, you must change the Colors array in
INITBALL.C to match, but that's a tiny detail; by and large, the process
of generating a ball-shaped object is now automated. In fact, we're not
limited to ball-shaped objects; substitute a different vertex and face
generation program for GENBALL.C, and you can make whatever convex
polyhedron you want; again, all you have to do is change the **Colors**
array correspondingly. You can easily create multiple versions of the
base object, too; INITCUBE.C is an example of this, creating 11
different cubes.
What we have here is the first glimmer of an object-editing system.
GENBALL.C is the prototype for object definition, and INITBALL.C is the
prototype for general-purpose object instantiation. Certainly, it would
be nice to someday have an interactive 3-D object editing tool and
resource management setup. We have our hands full with the drawing end
of things at the moment, though, and for now it's enough to be able to
create objects in a semiautomated way.