I've accumulated some notes over time about how OSG and VPB databases works,
and I
thought I'd share them here for others' enlightenment, and also to ask for
corrections. I
don't guarantee this is correct, it just seems to be correct based on my
understanding.
Perhaps someday this might make it into some sort of OSG Terrain Guide or
something.
Structure of a typical VirtualPlanetBuilder/OSGDEM terrain database
Root OSG file looks like:
CoordinateSystemNode: Top-level, defines ellipsoid shape of globe
PagedLOD
Child0:TerrainTile (lowest LOD of terrain model)
Child1:Basename_root_L0_X0_Y0/Basename_L0_X0_Y0_subtile.osg
(file containing LODs that cover the same extent as
Child 0)
A VPB TerrainTile (for example, L0_X0_Y0) looks like:
TerrainTile (lowest LOD of terrain model)
Locator (defines Coordinate System and Transform of TerrainTile)
ElevationLayer
HeightFieldLayer
HeightField: UniqueID, Origin, X/Y Interval,
SkirtHeight, Heights [Array]
ColorLayer
ImageLayer
file Basename_L0_X0_Y0.dds
SubTile Basename_root_L0_X0_Y0/Basename_L0_X0_Y0_subtile.osg looks like:
Group
PagedLOD
Child0: TerrainTile
Child1: Basename_L1_X0_Y0_subtile.osg (Another subtile like
this one)
PagedLOD
Child0: TerrainTile
Child1: Basename_L1_X1_Y0_subtile.osg
...more, similar PagedLODs may exist here...
In operation, the Root OSG file is loaded by the caller. This file contains a
TerrainTile
with Color and Elevation data, so as soon as the initial root load is complete,
there is a
lo-resolution terrain surface visisble. OSG immediately begins cull traversal
of this
scene graph. During cull traversal, the PagedLOD node probably discovers that
the
currently visible child (child 0, with the TerrainTile) is insufficient LOD for
the
current view. The only alternate LOD is Child1, which is an external file.
PagedLOD
informs the DatabasePager to begin loading this external file and continues on
the cull.
The load operation has a priority level that hints to the DatabasePager how
important this
node is, allowing multiple loads to be queued according to how close each one
is to the
viewer.
After the first cull operation is complete, a number of cull/draw cycles may
execute while
the DatabasePager thread completes loading and possibly compiling the loaded
data for use
in the scenegraph. During each cull operation that the loaded external child is
not yet
available, the PagedLOD re-requests the child node to be loaded. This allows
the priority
of the node in the queue to adjust if the viewpoint moves prior to the node
getting loaded.
When ready, the DatabasePager inserts the loaded subgraph (see structure of
Basename_L0_X0_Y0_subtile.osg) into the scenegraph. At this point, the top
PagedLOD is
satisfied that it has an appropriate LOD, and subsequent cull operations will
traverse the
Child1 of the top PagedLOD Node.
Child1 itself contains a non-trivial subgraph. At the top of the subgraph are
several
PagedLOD nodes. Each of these contains its own Child0 LOD, which is not an
external file
but a TerrainTile complete with ElevationLayer and ColorLayer already loaded in
and
compiled as part of the Basename_L0_X0_Y0_subtile. So, when the Root-level
Child0
TerrainTile is replaced with the root-level Child1 external file, the
Basename_L0_X0_Y0_subtile's Child0s immediately cover the exact same visual
extent of
terrain without any more loading delay.
As soon as the Basename_L0_X0_Y0_subtile subgraph is merged into the scenegraph
and
traversed by cull, the PagedLODs in L0_X0_Y0 will evaluate their LOD criteria
(typically
expressed not as actual distance, but in realtive-apparent-size-on-screen) and
most likely
discover they too, are insufficient for the current view. At this point, each
of the
PagedLODs will decide to load their next LOD child (Child1). Because each of
these
children will be located at different locations with respect to the viewer,
they will each
individually determine a different priority, which the DatabasePager will
respect when
queuing the DatabaseRequest.
PagedLOD class has several conditions. The base LOD class and the Group class
beneath that
require that the container (a std::vector) that contains the actual LODs must be
contiguous – no empty slots are permitted in the container. The PagedLOD class
defines a
parallel container called PerRangeDataList, which is also a vector holding the
LOD ranges
and external filenames for each child that can be loaded. The contents of these
two
containers are associated only by the subscript of their contents. What this
means in
practice is that the LOD children in the Group must be loaded contiguously – no
higher LOD
can be loaded if a lower LOD is not already loaded.
PagedLOD will