/* -*-c++-*- */
/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
 * Copyright 2008-2010 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 */
#ifndef OSGEARTH_ENGINE_DROAM_MAPNODE_H
#define OSGEARTH_ENGINE_DROAM_MAPNODE_H 1

#include "MeshManager"
#include <osg/CoordinateSystemNode>
#include <osg/Geode>
#include <osg/NodeVisitor>
#include <osgEarth/Map>

/**
 * D-ROAM: A diamond-based continuous manifold terrain engine.
 *
 * Initially based on the "diamond structure" concept found in:
 * "Realtime Optimal Adaptation for Planetary Geometry and Texture: 4-8 Tile Hierarchies"
 * by Hwa, Duchaineau, and Joy. [1]
 * http://www.computer.org/portal/web/csdl/doi/10.1109/TVCG.2005.65
 *
 * The global mesh is comprised of Diamonds. The mesh is primed with a "cube manifold" as
 * described in [1]. The entire mesh shared a single vertex array and a single
 * corresponding normal array (in VBOs). Each Diamond referenced verts in this array, thus
 * ensuring a worldwide, continuous, deformable mesh with no cracks.
 *
 * The first version of this engine rendered each Diamond at each level with its own primitive
 * set. This worked well, but left us with a problem-- no regular gridded structure on which to
 * easily map textures. The second version addresses this by only rendering "quadtree ancestor"
 * diamonds, i.e, diamonds at every other level (odd-numbered levels). By doing this, the base
 * quads correspond to the faces of the base cube manifold, and all sub-quads fall within these
 * 6 faces. We can then use osgEarth's Cube SRS to map textures directly to these quadtree
 * ancestor geometries.
 *
 * The final problem was dealing with double precision. osgTerrain localizes each terrain tile
 * to solve this problem. Doing that would break our continuous mesh. So instead we will subdivide
 * the mesh into a fixed number of localized regions, and maintain a geometry per-diamond for each
 * region. We can optimize this when we know that a diamond will never render in a particular 
 * region. We select which region to render based on camera position.
 *
 * NOTE: OSG's stats will report outrageous numbers of verticies for this node. That is due to the
 * shared vertex array. Stats counts the vertex array once for each geometry in the mesh, resulting
 * in an exaggerated count. In fact, the real vertex count should be equal to the primitive count
 * (since there is one vertex per diamond).
 */
class DRoamNode : public osg::CoordinateSystemNode
{
public:
    DRoamNode( Map* map =0L );
    DRoamNode( const DRoamNode& rhs, const osg::CopyOp& op =osg::CopyOp::DEEP_COPY_ALL );
    META_Node(osgEarth,DRoamNode);

private:
    osg::ref_ptr<osg::Geode>          _geode;
    osg::ref_ptr<Manifold>            _manifold;
    osg::ref_ptr<MeshManager>         _mesh;
    osg::ref_ptr<osgEarth::Map>       _map;

private:
    void update( osg::NodeVisitor* nv );
    void cull( osg::NodeVisitor* nv );

    struct MyCullCallback : public osg::NodeCallback {
        void operator()( osg::Node* node, osg::NodeVisitor* nv ) {
            static_cast<DRoamNode*>(node)->cull( nv );
            traverse( node, nv );
        }
    };
    friend struct MyCullCallback;

    struct MyUpdateCallback : public osg::NodeCallback {
        void operator()( osg::Node* node, osg::NodeVisitor* nv ) {
            static_cast<DRoamNode*>(node)->update( nv );
            traverse( node, nv );
        }
    };
    friend struct MyUpdateCallback;
};

#endif // OSGEARTH_ENGINE_DROAM_MAPNODE_H
