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

98 lines
5.5 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

### Raw Speed, Part II: Look it Up {#Heading4}
It's a funny thing about Turbo Profiler: Time spent in the Borland C++
80x87 emulator doesn't show up directly anywhere that I can see in the
timing results. The only way to detect it is by way of the line that
reports what percent of total time is represented by all the areas that
were profiled; if you're profiling all areas, whatever's not explicitly
accounted for seems to be the floating-point emulator time. This quirk
fooled me for a while, leading me to think sine and cosine weren't major
drags on performance, because the **sin()** and **cos()** functions
spend most of their time in the emulator, and that time doesn't show up
in Turbo Profiler's statistics on those functions. Once I figured out
what was going on, it turned out that not only were **sin()** and
**cos()** major drags, they were taking up over half the total execution
time by themselves.
The solution is a lookup table. Listing 53.1 contains a function called
**CosSin()** that calculates both the sine and cosine of an angle, via a
lookup table. The function accepts angles in tenths of degrees; I
decided to use tenths of degrees rather than radians because that way
it's always possible to look up the sine and cosine of the exact angle
requested, rather than approximating, as would be required with radians.
Tenths of degrees should be fine enough control for most purposes; if
not, it's easy to alter **CosSin()** for finer gradations yet. GENCOS.C,
the program used to generate the lookup table (COSTABLE.INC), included
in Listing 53.1, can be found in the XSHARP22 subdirectory on the
listings diskette. GENCOS.C can generate a cosine table with any
integral number of steps per degree.
FIXED.ASM (Listing 53.1) speeds X-Sharp up quite a bit, and it changes
the performance balance a great deal. When we started out with 3-D
animation, calculation time was the dragon we faced; more than 90
percent of the total time was spent doing matrix and projection math.
Additional optimizations in the area of math could still be made (using
32-bit multiplies in the backface-removal code, for example), but
fixed-point math, the sine and cosine lookup, and selective assembly
optimizations have done a pretty good job already. The bulk of the time
taken by X-Sharp is now spent drawing polygons, drawing rectangles (to
erase objects), and waiting for the page to flip. In other words, we've
slain the dragon of 3-D math, or at least wounded it grievously; now
we're back to the dragon of polygon filling. We'll address faster
polygon filling soon, but for the moment, we have more than enough
horsepower to have some fun with. First, though, we need one more
feature: hidden surfaces.
#### Hidden Surfaces {#Heading5}
So far, we've made a number of simplifying assumptions in order to get
the animation to look good; for example, all objects must currently be
convex polyhedrons. What's more, right now, objects can never pass
behind or in front of each other. What that means is that it's time to
have a look at hidden surfaces.
There are a passel of ways to do hidden surfaces. Way off at one end
(the slow end) of the spectrum is Z-buffering, whereby each pixel of
each polygon is checked as it's drawn to see whether it's the frontmost
version of the pixel at those coordinates. At the other end is the
technique of simply drawing the objects in back-to-front order, so that
nearer objects are drawn on top of farther objects. The latter approach,
depth sorting, is the one we'll take today. (Actually, true depth
sorting involves detecting and resolving possible ambiguities when
objects overlap in Z; in this chapter, we'll simply sort the objects on
Z and leave it at that.)
This limited version of depth sorting is fast but less than perfect. For
one thing, it doesn't address the issue of nonconvex objects, so we'll
have to stick with convex polyhedrons. For another, there's the question
of what part of each object to use as the sorting key; the nearest
point, the center, and the farthest point are all possibilities—and,
whichever point is used, depth sorting doesn't handle some overlap cases
properly. Figure 53.1 illustrates one case in which back-to-front
sorting doesn't work, regardless of what point is used as the sorting
key.
For photo-realistic rendering, these are serious problems. For fast
PC-based animation, however, they're manageable. Choose objects that
aren't too elongated; arrange their paths of travel so they don't
intersect in problematic ways; and, if they do overlap incorrectly,
trust that the glitch will be lost in the speed of the animation and the
complexity of the screen.
Listing 53.2 shows X-Sharp file OLIST.C, which includes the key routines
for depth sorting. Objects are now stored in a linked list. The initial,
empty list, created by **InitializeObjectList(),** consists of a
sentinel entry at either end, one at the farthest possible z coordinate,
and one at the nearest. New entries are inserted by **AddObject()** in
z-sorted order. Each time the objects are moved, before they're drawn at
their new locations, **SortObjects()** is called to Z-sort the object
list, so that drawing will proceed from back to front. The Z-sorting is
done on the basis of the objects' center points; a center-point field
has been added to the object structure to support this, and the center
point for each object is now transformed along with the vertices. That's
really all there is to depth sorting—and now we can have objects that
overlap in X and Y.
![](images/53-01.jpg)\
**Figure 53.1**  *Why back-to-front sorting doesn't always work
properly.*