@c -*-texinfo-*-
@node Level of Detail, Progressive Level of Detail, Dynamic Worlds, Engine
@subsection Level of Detail
@cindex Level of Detail
@cindex static lod
@cindex progressive lod

@noindent
@emph{Written by Jorrit Tyberghein,
@email{jorrit.tyberghein@@gmail.com}. Last updated 13 August 2003.}

In a 3D engine there are typically two major ways that can be used to
optimize performance (aside from getting better hardware and making better
use of hardware). Those two ways are Visibility Culling
(@pxref{Visibility Culling}) and Level of Detail. In this section we will
talk about the latter.

Level of Detail (or @sc{lod}) is the technique of reducing high polygon
models with a simpler representation that renders faster. In contrast with
visibility culling, level of detail can improve the performances even if there
are a lot of objects visible at the same time. Level of Detail is practically
the only way to improve performance in situations where there is not much
culling opportunity (like in a space game).

Crystal Space supports several types of @sc{lod}:

@itemize @bullet
@item
Static @sc{lod}: With this kind of @sc{lod} you can let the engine
automatically select between different versions of the same model based
on the distance to the camera. This requires that you provide the different versions of
the same model, which is explained in this section.
@item
@ref{Progressive Level of Detail, Progressive @sc{lod}}: The major difference compared
with static @sc{lod} is that this kind of @sc{lod} doesn't use discrete @sc{lod} levels
but instead allows the engine to select as many triangles from the original model as
needed (while still preserving the original model as much as possible). Another advantage
is that the progressive @sc{lod}s are computed automatically. This @sc{lod} system is
currently only supported by Genmeshes. 
@item
Imposters: The mesh can be automatically replaced by a procedural image representing
the mesh from the current camera point of view. Displaying an image is much more cheap
than a 3D mesh. Here again, the images are computed automatically and do not need any
additional artistic work.
@item
Per-mesh @sc{lod}: Some meshes such as the @ref{MeshObject Terrain2, terrain} and the
@ref{Furmesh Mesh, fur} meshes have built-in @sc{lod} management.
@end itemize

@subsubheading Static @sc{lod} From Map File

Using static @sc{lod} from a map file is fairly easy. Here is an example:

@example
<meshobj name="loddy">
  <nullmesh>
    <min x="-1" y="0" z="-1" />
    <max x="1" y="2" z="1" />
  </nullmesh>
  <staticlod>
    <distance d0="100" d1="10" />
  </staticlod>
  <meshobj name="loddy_lowdetail">
    <lodlevel>0</lodlevel>
    @dots{}
  </meshobj>
  <meshobj name="loddy_mediumdetail">
    <lodlevel>1</lodlevel>
    @dots{}
  </meshobj>
  <meshobj name="loddy_highdetail_upper">
    <lodlevel>2</lodlevel>
    @dots{}
  </meshobj>
  <meshobj name="loddy_highdetail_lower">
    <lodlevel>2</lodlevel>
    @dots{}
  </meshobj>
  <move>
    @dots{}
  </move>
</meshobj>
@end example

Let's explain what this example does in more detail. First we
define a mesh object called @samp{loddy}. This is the parent mesh
which represents the actual model but on itself contains no geometry.
So that's why we use @samp{nullmesh}. The box given in the nullmesh
is important as it is used for visibility culling. So you should
make the box larger then any of the objects inside it. Also the center
of the box is used for calculating the distance between the box and
the camera.

Internally @sc{lod} uses a function to calculate a detail value betwen
0 and 1 (0 meaning lowest detail, 1 meaning highest):

@example
float lod = m * distance + a;
@end example

The result of this function is capped so it is always between 0 and 1.

Inside the @samp{staticlod} block you can directly specify the @samp{m} and
@samp{a} values but it is often easier to use a short-cut and specify @samp{d0}
and @samp{d1} instead. In the example above we set @samp{d0} equal to 100 which
means that at distance 100 and beyond we will go to lowest possible detail. We
also set @samp{d1} equal to 10 which means that at distance 10 or less the
object will use highest detail. Internally @samp{d0} and @samp{d1} are
transformed to @samp{m} and @samp{a} using the following equation:

@example
float m = 1.0 / (d1-d0);
float a = -m * d0;
@end example

After this we have four child meshes representing the different detail
levels. Using the @samp{lodlevel} keyword one can place a child mesh at
a @sc{lod} index. These indices always start at 0 for lowest detail and
can go as high as you want. The maximum index that is used will be
represented by detail level equal to 1.

Interesting in the example above is that there are two meshes for level
2. This means that at the highest detail level we actually use two sub-meshes
to represent our mesh. Using this trick one can also use static lod to replace
individual trees with collections of trees.

Note that it is also possible to define static @sc{lod} on a hierarchical
mesh factory using the same syntax as described above. In that case one can
use @samp{meshref} to make an instance of that hierarchical factory
(or use @code{iMeshFactoryWrapper->CreateMeshWrapper()}).

Note that it is also possible to use variables for @samp{m} and @samp{v}.
One can do that like this:
@example
<variables>
  <variable name="Lod M" value="-.1" />
  <variable name="Lod A" value=".5" />
</variables>
@dots{}
<staticlod>
  <distance varm="Lod M" vara="Lod A" />
</staticlod>
@end example
The advantage of this technique is that one can then use the same
@sc{lod} values for different objects and modify them easily. Also one
can modify the variables at runtime too with an immediate effect.

@subsubheading Static @sc{lod} From Code

Using static @sc{lod} from code is very easy. One basically has to create
a hierarchical mesh, followed by a static @sc{lod} object and then the
children which should be added to the right @sc{lod} level. Here follows
an example which roughly does the same as our map example in the previous
section:

@example
csRef<iMeshWrapper> loddy_mesh = Engine->CreateMeshWrapper (
  "crystalspace.mesh.object.null", "loddy");
csRef<iNullMeshState> nullmesh = scfQueryInterface<iNullMeshState> (
  loddy_mesh->GetMeshObject ());
nullmesh->SetBoundingBox (csBox3 (
  csVector3 (-1, 0, -1), csVector3 (1, 2, 1)));

iLODControl* lodctrl = loddy_mesh->CreateStaticLOD ();
float d0 = 100;
float d1 = 10;
float m = 1.0 / (d1-d0);
float a = -m * d0;
lodctrl->SetLOD (m, a);

csRef<iMeshWrapper> loddy_lowdetail = @dots{}
loddy_mesh->GetChildren ()->Add (loddy_lowdetail);
loddy_mesh->AddMeshToStaticLOD (0, loddy_lowdetail);
@dots{}
csRef<iMeshWrapper> loddy_meddetail = @dots{}
loddy_mesh->GetChildren ()->Add (loddy_meddetail);
loddy_mesh->AddMeshToStaticLOD (1, loddy_meddetail);
@dots{}
@end example

To-Do@dots{} @@@@@@
