@c -*-texinfo-*-
@node 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 performance 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 currently supports three kinds of @sc{lod}:

@itemize
@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 distance.
@item
Progressive @sc{lod}. This kind of @sc{lod} is supported by 3D sprites and
is also based on distance. The major difference is that this kind of @sc{lod}
doesn't use discrete 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).
@item
Terrain @sc{lod}. The terrain engine has built-in @sc{lod}.
@end itemize

In future we will also support other kinds of @sc{lod} like imposters.

Also in future there will be a @sc{lod} manager. At this moment @sc{lod}
is purely distance based but for a given distance the level of detail will
not change. A @sc{lod} manager can decide to reduce the lod levels for
certain objects in order to improve overall performance.

@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

@subsubheading Progressive @sc{lod}

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