|
Engine
Documentation
Notes
|
Architecture
Lilith3D
Lilith3D is the main
class of the system. It manages the TerrainMesh, MOBs (mobile objects),
and particles. It has methods to draw, get access to the weather, camera, particle systems, and much more functionality.
- Get a pointer to the Lilith3D object: Instance()
- Drawing
- BeginDraw()
- Draw()
- EndDraw()
- usually followed by SDL_GL_SwapBuffers()
- Access other key control objects
- GetTerrainMesh()
- GetTerrainDecor()
- GetTimeClock()
- Add a sequence
- Query the weather
- Control rendering options
- RenderMode()
- RenderFoliage()
- RenderShadows()
- PerfDataDisplay()
- MapSize()
- SetInfoDisplay()
- ScreenCapture()
- Intersection*() functions
- Query particle systems
- Move the camera
Camera
With version 1.1.0, Lilith
has a very flexible quaternion camera. It's interface is specified
in yaw and pitch ("human" coordinates) but all
interpolation is done with full rotation matrices and quaternions.
The following basic design applies to the camera:
- Lilith uses a right
handed coordinate system for everything, including the camera
- The terrain is in
the XY plain, and height is positive Z
- The default camera
view is looking down the positive X axis. Left is the positive
Y axis, and up is the positive Z axis.
When positioning the
camera, human coordinates are used. Under the covers it's all quaternions,
and if you need to access them directly, it is straightforward.
- "Yaw" is
rotation around the Z axis. A positive yaw is to the left, a negative
to the right. Yaw is always measured around the Z axis even after
it has been transformed.
- "Pitch"
is rotation around the local Y axis. up & down rotation. A
positive pitch is forward (down) and a negative pitch is backward
(up).
TerrainMesh
The TerrainMesh is the
core of the engine. All the other classes assume the existence of
terrain.
The basic unit of measurement
of terrain is a "grid". The grid size is a power of 2
- in the demo, the grid size is 256. Image a giant chessboard of
256 by 256 squares, and that is the game world to lilith. The corner
of each square has a height.
At its heart then, the
terrain mesh is a height field. For a grid of 256x256, it uses 257x257
vertex heights. In order to cut down
on rendering time (and pathing time), the engine dynamically changes the level of detail
to meet a consistent quality criteria. Lilith will use as many polygons
as needed to hit the quality threshold; therefore, smoother terrains
use fewer polys that jaggy terrains do.
LOD
In version 1.5+
Lilith3D 1.5 uses a simple system for LOD. It groups 4 grids (2x2) into a "patch",
and can render a patch with 4-8 polygons, as needed. When RENDER_LOD
is on, the color now reflects the number of tris in a patch.

Why switch to a simpler
system from the previous system?
- Easier to animate.
It's hard to get smooth animation while changing up LOD drastically.
- Pathing. This was
really the death knell of the prior systems. Pathing over variably
sized patches is a nightmare to compute, and then you end up with
paths that don't look very good. Also, reserving areas for buildings
was complex.
- Code complexity. The
prior system was too hard to change, add features, and work with the pathing. I needed a more flexible
solution.
What's the performance
impact? None, anymore. The initial
Prior to 1.4
Lilith uses a level of
detail system to maintain a constant quality. The target level of
quality can be set in worlddefine.h.
 |
A view of the terrain.
This particular section is created to achieve an artificially
high LOD reduction in order to test the engine. |
 |
The exact same view,
this time colored to show the LOD. Error is consistent: red
takes the most polygons to achieve acceptable quality, yellow
less, green even less, and blue very few. |
 |
The exact same
view, this time showing the outlines of the triangles. Here
you can see the LOD, and the subdivision used to avoid cracks
in the mesh.
Note that neighboring
polygons can be at any level of detain with respect to one
another. |
Why not use ROAM? I started
with a ROAM algorithm, and it had problems, for this application,
that I just wasn't happy with:
- Popping. ROAM terrain
"pops" as the level of detain changes. This situation
is exasperated by Lilith's use of dynamic terrain (sand/snow/grass)
and lighting which made the popping more noticeable.
- What is the real height?
To accurately position models on the terrain, Lilith computes
the terrain height from the LOD mesh, not the height field. With
ROAM, modes (and trees and buildings) jumped up and down over
distance.
There's more that could
be done in Lilith to have more terrain in the far distance.
As terrain changes, in
response to terrain morphs, the polygons are changed to keep a consistent
level of detail. The engine updates the smallest area of the world
possible.
Terrain Textures
There are several approaches
to texturing the terrain. Lilith repeats small textures over the
terrain. (As opposed to the common approach of stretching a big
texture over the whole terrain.)
Shaders are just wonderful.
Lilith3D uses a GLSL shader program to generate the terrain, compute the light, and mix the textures. GLSL support is required for Lilith3D to run, and now is the exclusive terrain rendering option.
(In prior versions Lilith3D supported a multi-pass renderer on the fixed pipeline. It was an interesting experiment to sync up the two systems, but was just too much code to keep in sync.)
Pather
The pather is based on
an A* algorithm. A walking MOB moves across the world, and can move
across polygons that are above water. Objects move slower over snow and steep slopes, and the pather accounts for this.
As a MOB walks the terrain,
it speeds up or slows down based on how flat the terrain is, and whether there is snow.
 |
A typical
chunk of terrain. |
 |
A view of the
terrain that shows passable triangles (blue) and impassable
ones (red). Most triangle are impassible due to high slopes.
|
 |
A path - minimum
distance - between 2 points on the terrain. |
Archived Notes
Older version info:
Tree
Rendering
|