[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Written by Jorrit Tyberghein, jorrit.tyberghein@uz.kuleuven.ac.be.
Warning: About half of the information in this document is out of date and no longer accurately reflects the state of the rendering engine in Crystal Space.
Here is a run-through of the main rendering loop. This document is not an explanation of portal technology. It just explains how the main rendering loop in Crystal Space works so that you can have a quick idea of where you have to go to see a particular part of the algorithm work. This is a rather technical document and not intended for people who are only interested in using Crystal Space in their own projects. It is intended for people who want to know how Crystal Space works internally.
To understand this you should know how portals are used in Crystal Space. You should also read the tutorial (see section 5.2 Simple Tutorial 1) as this explains the basics for using Crystal Space. This document is baseed upon the `simple' application (`CS/apps/simple/') because this discussion looks a lot like a tutorial.
First we start in `apps/simple/simple.cpp'. In the main()
function we initialize our engine. This is an instance of the class
`csEngine' which actually represents the engine. I will not explain how
all the initialization works. This is explained in the tutorial. But I will
go straight to csEngine::Draw()
which is called indirectly from within
Simple::NextFrame()
. It is called indirectly because first we call
csView::Draw()
which then calls csEngine::Draw()
.
csEngine::Draw()
)
The method csEngine::Draw()
is located in the file
`CS/libs/csengine/engine.cpp'.
csEngine::Draw()
first sets up the initial `csRenderView'
structure. This structure is defined in `CS/include/csengine/rview.h'
and is the main structure which is used throughout the entire rendering
process. It collects all data that is required for rendering the recursive
portal structure.
Basically it contains the following information:
view
g3d
g2d
clip_plane
callback
callback_data
Another important thing is that `csRenderView' is actually a subclass of `csCamera' (`CS/include/csengine/camera.h') so all camera functionality is present as well.
To set up the initial `csRenderView' structure csEngine::Draw()
creates a new instance based upon the given camera.
After this csEngine::Draw()
gets the current sector from the camera and
calls csSector::Draw()
(`CS/libs/csengine/sector.cpp'). This will
essentially draw the whole screen as discussed below.
After doing this (now that the screen is fully updated) we optionally draw halos. Halos are drawn on top of everything else since they are an effect in the eyes.
csSector::Draw()
)
This method csSector::Draw()
is located in
`CS/libs/csengine/sector.cpp'.
csSector::Draw()
is responsible for rendering everything in the current
sector. It will do this by first rendering the walls of the sector (using
Z-fill only). It is possible that there is a BSP tree attached to the
sector. In that case csSector::Draw()
will use that BSP tree to
sort the wall polygons back to front.
Before actually starting to render, it will first transform the sector to the
camera position so that (0,0,0) is the camera point, (1,0,0) is one unit right
of the camera, (0,1,0) is one unit above the camera, and (0,0,1) is one unit
above the camera. This is done in csPolygonSet::NewTransformation()
and csPolygonSet::TransformWorld2Cam()
(see
`CS/libs/csengine/basic/polyset.cpp'). The call to
NewTransformation()
is needed because it is possible that we render the
same sector multiple times in the same recursion tree. Because of this we
don't actually want to loose the transformation that occured the previous time
so NewTransformation()
takes care of potentially storing the old
transformation and setting a new one (mirrors and other space warping portals
make this feature essential).
After that actual drawing of the transformed polygons is performed by the
method csSector::DrawPolygons()
, which in turn calls
csPolygonSet::DrawPolygonArray()
. The `csPolygonSet' class is
implemented in `CS/libs/csengine/basic/polyset.cpp'), and
DrawPolygonArray()
is discussed further in the next section.
While drawing the polygons of the sector (which are the sector's walls) some
of them may be portals. In that case csPolygonSet::DrawPolygonArray()
will immediatelly perform recursion to the destination sector of the portal.
This means creating a new `csRenderView' with the new view and then
recursing into csSector::Draw()
of the new sector. It is important to
note that DrawPolygonArray()
may already enter recursion for a new
sector even though the first sector has not finished drawing yet.
So it is important to note that after drawing all sector walls we have in fact drawn the entire world visible from this sector (including things and sprites in other sectors). But we have not drawn things and sprites in this sector yet.
csSector::Draw()
will then continue to draw all things in the current
sector. There are some special cases here. It is possible that all
non-moving things have been collected into one thing which has an attached
BSP tree. This is the `static_thing'. If that exists we first
render that `static_thing' by using back to front ordering and using
Z-fill instead of Z-buffer. Note that all non-moving things which were
collected into the static thing are still in the list of things for the
sector. But you can recognize them because they IsMerged()
returns
true.
Now there are two important cases depending on wether or not there was a static thing in the previous case. If there was a static thing or if the sector itself had a BSP tree, then all remaining things have to be drawn using the Z-buffer (ignoring foggy things for the moment). In this case we can't use Z-fill anymore because there was a BSP tree used to render the previous sector or static thing.
If there was no static thing and there was no BSP tree for the sector then we can still use Z-fill to render all convex things provided we render them back to front. This part of the algorithm does not work correctly yet and that's the reason that it has been disabled.
In any case all foggy things are collected and then Z-sorted. This is also not correct but currently the only way. Here we need a better way to do Z-sorting.
Now we can proceed to drawing all sprites. Warning: The remainder of this paragraph is no longer true. Currently this algorithm is somewhat simple. It assumes that a sprite always lives in one sector. This is of course not always right and the sprite structure (`csSprite3D' in `CS/libs/csengine/objects/cssprite.h') already has provisions for the fact that a single sprite can live in several sectors at the same time. What we want to do here is to only draw a sprite if one of the below conditions is true:
In the first case we have to clip the sprite to the portal and render only the side on this portal. When returning from the recursion we will have to render the other side of the sprite.
In the second case we don't render the sprite but it will be rendered when we return from our recursion because the sprite is also part of the previous sector.
The next step in csSector::Draw()
is to queue all halos that were
encountered in this sector so that they can be drawn later. A Z-buffer
visibility test will be used later to make sure that the halo only appears
when the center is visible.
Finally we conclude by optionally fogging the current sector if this is needed. There are currently two ways of fogging:
Fixme: Is the next paragraph even accurate anymore?
The last method does not work correctly at this time. The first method is only implemented by the software renderer.
The method csSector::Draw()
ends by restoring the transformation by
calling csPolygonSet::RestoreTransformation()
.
csPolygonSet::DrawPolygonArray()
)
The implementation of csPolygonSet::DrawPolygonArray()
can be found in
the file `CS/libs/csengine/basic/polyset.cpp'.
Both `csSector' and `csThing' inherit from `csPolygonSet' so
they both basically use the same way of rendering polygons.
DrawPolygonArray()
is responsible for that. DrawPolygonArray()
is typically called on either the complete list of polygons for the
`csThing' or `csSector' or else a subset which was computed from a
BSP tree.
DrawPolygonArray()
will process every polygon in the array in turn. It
will basically call the following three functions for every polygon:
csPolygon3D::ClipToPlane()
(`CS/libs/csengine/polygon/polygon.cpp')
csPolygon3D::DoPerspective()
csPolygon2D::ClipAgainst()
ClipToPlane()
will do a quick test to see if all vertices of the
polygon are in front of the Z-plane. That will at least exclude polygons
quickly which are behind the viewer. Then it will perform backface culling.
Finally it will process the special `clip_plane' field in
`csRenderView' which was mentioned in the beginning of this document. In
other words, it will clip the polygon in 3D to that plane. This is rarely
needed.
If ClipToPlane()
fails then the polygon is not visible and we don't
need to continue with the other steps.
DoPerspective()
does perspective correction on the polygon and thus
transforms the polygon from 3D to 2D. The 2D polygon will be placed in a
special static variable of type `csPolygon2D' (called `clipped').
Finally it will also transform the plane of the polygon to camera space.
DoPerspective()
can also fail in which case the polygon is not visible
and processing can stop here.
Finally we do ClipAgainst()
. This will clip the polygon to the current
view. The clipping happens in place. The result will be in `clipper'.
If ClipAgainst()
fails the polygon was completely clipped away and we
don't need to show anything.
When all three steps above succeed we have a visible (part) of a polygon which has been perspective corrected. We can now render that polygon.
If the polygon is a portal then we initiate a recursive process by calling the
method csPortal::Draw()
(`CS/libs/csengine/polygon/portal.cpp')
which will in turn call csSector::Draw()
for the destination sector.
csPortal::Draw()
also takes care of possible space warping and also
creation of the `clip_plane' in `csRenderView' if this should be
needed.
If a portal has an alpha transparent texture superimposed then we will draw
that texture on top of the portal when csPortal::Draw()
returned.
However since `clipped' is a static variable we need to copy it locally
to be able to draw it again later. This happens in `keep_clipped' and
`keep_plane'.
If the polygon was not a portal then we just render it. This happens in
csPolygon2D::DrawFilled()
(from the file
`CS/libs/csengine/polygon/polygon.cpp') which is further explained in the
next section.
That's basically it for this function.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |