122 lines
7.2 KiB
Markdown
122 lines
7.2 KiB
Markdown
### Entities {#Heading9}
|
|
|
|
So far, all we've drawn is the static, unchanging (apart from dynamic
|
|
lighting) world. That's an important foundation, but it's certainly not
|
|
a game; now we need to add moving objects. These objects fall into four
|
|
very different categories: BSP models, polygon models, sprites, and
|
|
particles.
|
|
|
|
#### BSP Models {#Heading10}
|
|
|
|
BSP models are just like the world, except that they can move. Examples
|
|
include doors, moving bridges, and health and ammo boxes. The way these
|
|
are rendered is by clipping their polygons into the world BSP tree, so
|
|
each polygon fragment is in only one leaf. Then these fragments are
|
|
added to the edge list, just like world polygons, and scanned out, along
|
|
with the rest of the world, when the edge list is processed. The only
|
|
trick here is front-to-back ordering. Each BSP model polygon fragment is
|
|
given the BSP sorting order of the leaf in which it resides, allowing it
|
|
to sort properly versus the world polygons. If two or more polygons from
|
|
different BSP models are in the same leaf, however, BSP ordering is no
|
|
longer useful, so we then sort those polygons by 1/z, calculated from
|
|
the polygons' plane equations.
|
|
|
|
Interesting note: We originally tried to sort all world polygons on 1/z
|
|
as well, the reason being that we could then avoid splitting polygons
|
|
except when they actually intersected, rather than having to split them
|
|
along the lines of parent nodes. This would result in fewer edges, and
|
|
faster edge list processing and rasterization. Unfortunately, we found
|
|
that precision errors and special cases such as seamlessly abutting
|
|
objects made it difficult to get global 1/z sorting to work completely
|
|
reliably, and the code that we had to add to work around these problems
|
|
slowed things up to the point where we were getting no extra performance
|
|
for all the extra code complexity. This is not to say that 1/z sorting
|
|
can't work (especially in something like a flight sim, where objects
|
|
never abut), but BSP sorting order can be a wonderful thing, partly
|
|
because it always works perfectly, and partly because it's simpler and
|
|
faster to sort on integer node and leaf orders than on floating-point
|
|
1/z values.
|
|
|
|
BSP models take some extra time because of the cost of clipping them
|
|
into the world BSP tree, but render just as fast as the rest of the
|
|
world, again with no overdraw, so closed doors, for example, block
|
|
drawing of whatever's on the other side (although it's still necessary
|
|
to transform, project, and add to the edge list the polygons the door
|
|
occludes, because they're still in the PVS—they're potentially visible
|
|
if the door opens). This makes BSP models most suitable for fairly
|
|
simple structures, such as boxes, which have relatively few polygons to
|
|
clip, and cause relatively few edges to be added to the edge list.
|
|
|
|
#### Polygon Models and Z-Buffering {#Heading11}
|
|
|
|
Polygon models, such as monsters, weapons, and projectiles, consist of a
|
|
triangle mesh with front and back skins stretched over the model. For
|
|
speed, the triangles are drawn with affine texture mapping; the
|
|
triangles are small enough, and the models are generally distant enough,
|
|
that affine distortion isn't visible. (However, it is visible on the
|
|
player's weapon; this caused a lot of extra work for the artists, and we
|
|
will probably implement a perspective-correct polygon-model rasterizer
|
|
in Quake 2 for this specific purpose.) The triangles are also Gouraud
|
|
shaded; interestingly, the light vector used to shade the models is
|
|
always from the same direction, and has no relation to any actual lights
|
|
in the world (although it does vary in intensity, along with the model's
|
|
ambient lighting, to match the brightness of the spot the player is
|
|
standing above in the world). Even this highly inaccurate lighting works
|
|
well, though; the Gouraud shading makes models look much more
|
|
three-dimensional, and varying the lighting in even so crude a way
|
|
allows hiding in shadows and illumination by explosions and muzzle
|
|
flashes.
|
|
|
|
One issue with polygon models was how to handle occlusion issues; that
|
|
is, what parts of models were visible, and what surfaces they were in
|
|
front of. We couldn't add models to the edge list, because the hundreds
|
|
of polygons per model would overwhelm the edge list. Our initial
|
|
occlusion solution was to sort polygon-model polygons into the world
|
|
BSP, drawing the portions in each leaf at the right points as we drew
|
|
the world in BSP order. That worked reasonably well with respect to the
|
|
world (not perfectly, though, because it would have been too expensive
|
|
to clip all the polygon-model polygons into the world, so there was some
|
|
occlusion error), but didn't handle the case of sorting polygon models
|
|
in the same leaf against each other, and also didn't help the polygons
|
|
in a given polygon model sort properly against each other.
|
|
|
|
The solution to this turned out to be z-buffering. After all the spans
|
|
in the world are drawn, the z-buffer is filled in for those spans. This
|
|
is a write-only operation, and involves no comparisons or overdraw
|
|
(remember, the spans cover every pixel on the screen exactly once), so
|
|
it's not that expensive—the performance cost is about 10%. Then polygon
|
|
models are drawn with z-buffering; this involves a z-compare at each
|
|
polygon-model pixel, but no complicated clipping or sorting—and
|
|
occlusion is exactly right in all respects. Polygon models tend to
|
|
occupy a small portion of the screen, so the cost of z-buffering is not
|
|
that high, anyway.
|
|
|
|
Opinions vary as to the desirability of z-buffers; some people who favor
|
|
more analytical approaches to hidden surface removal claim that John has
|
|
been seduced by the z-buffer. Maybe so, but there's a lot there to be
|
|
seduced by, and that will be all the more true as hardware rendering
|
|
becomes the norm. The addition of particles—thousands of tiny colored
|
|
rectangles—to Quake illustrated just how seductive the z-buffer can be;
|
|
it would have been very difficult to get all those rectangles to draw
|
|
properly using any other occlusion technique. Certainly z-buffering by
|
|
itself can't perform well enough to serve for all hidden surface
|
|
removal; that's why we have the PVS and the edge list (although for
|
|
hardware rendering the PVS would suffice), but z-buffering pretty much
|
|
means that if you can figure out how to draw an effect, you can readily
|
|
insert it into the world with proper occlusion, and that's a powerful
|
|
capability indeed.
|
|
|
|
Supporting scenes with a dozen or more models of 300 to 500 polygons
|
|
each was a major performance challenge in Quake, and the polygon-model
|
|
drawing code was being optimized right up until the last week before it
|
|
shipped. One help in allowing more models per scene was the PVS; we only
|
|
drew those models that were in the PVS, meaning that levels could have a
|
|
hundred or more models without requiring a lot of work to eliminate most
|
|
of those that were occluded. (Note that this is not unique to the PVS;
|
|
whatever high-level culling scheme we had ended up using for world
|
|
polygons would have provided the same benefit for polygon models.) Also,
|
|
model bounding boxes were used to trivially clip those that weren't in
|
|
the view pyramid, and to identify those that were unclipped, so they
|
|
could be sent through a special fast path. The biggest breakthrough,
|
|
though, was a very different sort of rasterizer that John came up with
|
|
for relatively distant models.
|