[osg-users] Struggling trying to convert geometry coordinates in PagedLOD

203 views
Skip to first unread message

Olivier Tournaire

unread,
Mar 4, 2013, 2:48:35 PM3/4/13
to osg-...@lists.openscenegraph.org
 Hi all,

I am trying to convert geometry coordinates from one system to an other. The original geometries are in a (world) local coordinate system, unit meter and are centered around (0,0,0) and approximately bounded by (+-)10e+3. To integrate them in m'y software, I need to convert the original coordinates to à geographic projected system. In this target coordinate system, x values are around 10e+6, y values around 6.10e+6 and z are just a few tenth or hundred. Unit in this new target system are also in meters. I need to reach such a coordinates system since ll my other data are in this system (mainly a low resolution terrain generated from a georeferenced DTM with osgdem).

Now that the goal is stated, let me explain how my data are organized. I have a hierarchy of directory, each of them being a tile of a high resolution terrain. Each directory contains hundreds of files. They are organised in different LODs with 9 LODs and a there is also a "root" file. Let's say they are named file_<lod_x>_<id>.osgb, with <lod_x> going from 8 (lowest resolution) to 0 (highest resolution). For LODs 8, 7, 6 and 5, there is a single file. 4 files for LOD 4, 16 files for LOD 3, and so on ...
The osg structure of each file is as follow:
  1. file_lod_<8 to 6> have a PagedLOD root node which contains a single Geode, which itself contains a single Drawable
  2. file_lod_<5 to 1> have a Group root node. The group has several children PagedLOD, and each PagedLOD has a single Geode with a single Drawable
  3. file_lod_<0> have a single Group with a Geode containing only one Drawable.
Each PagedLOD is as follow:
  • RangeMode: PIXEL_SIZE_ON_SCREEN
  • Center: USER_DEFINED
  • Center: the mean coordinates of the children Geode

For now, I process each file sequentially and update the geometry vertices with some transformations based on PROJ4. To do so, I simply change the vertexArray with the new computed coordinates. To avoid high coordinates, I center them around (0,0,0) and add a translation MatrixTransform as a Parent of each Geode (thus, the MatrixTransform is now the child of each PagedLOD). the translation is simply the mean of coordinates on x, y and z after transformation. To center the coordinates, I simply substract the real coordinates with the mean coordinate. From this, I also need to update parent PagedLOD. I update the ranges according to the new dounding sphere radius of my Geode (only changes slightly), and set the center with the mean coordinates previously computed.

So, after conversion, the structure of each graph has been changed and "between" each PagedLOD and its Geode child, i have a translation MatrixTransform.

Here is the code, the "main" method being 'process':

*************************************************************************************

bool file_converter::transform_pagedlod( PagedLOD* pagedlod, Vec3d& new_center, double& new_radius, double& old_radius )
{
    NodeFinder<Geode> geode_finder;
    pagedlod->accept(geode_finder);

    if (!geode_finder.getNodeList().size())
    {
        _converter->_warning_callback( "PagedLOD does not contain any Geode" );
        return false;
    }

    ref_ptr<Geode> input_geode = geode_finder.getFirst();

    // TODO?
    if (geode_finder.getNodeList().size()>1)
        _converter->_warning_callback( "PagedLOD has more than 1 Geode (" + boost::lexical_cast<string>(geode_finder.getNodeList().size()) + " Geodes)! Only the FIRST one will be processed" );

    if(!transform_geode(input_geode, new_center, new_radius, old_radius))
        return false;

    ref_ptr<MatrixTransform> translation_from_center = _converter->create_translate_from_center_matrix(new_center);

    pagedlod->setDatabasePath( "" );
    pagedlod->setCenter(new_center);
    pagedlod->setRadius(new_radius);

    // Update PagedLOD ranges according to the following formula:
    // range = lod_threshold * radius / tolerance
    LOD::RangeList ranges = pagedlod->getRangeList();
    double r01 = ranges[0].second;
    double r10 = ranges[1].first;
    // 1) Compute tolerance...
    double tolerance01 = 2. * old_radius / r01;
    double tolerance10 = 2. * old_radius / r10;
    // 2) Update and set new ranges
    ranges[0].second = 2. * new_radius / tolerance01;
    ranges[1].first  = 2. * new_radius / tolerance10;
    pagedlod->setRangeList(ranges);

    // TODO
    ref_ptr<Node> firstChild = pagedlod->getChild(0);
    pagedlod->replaceChild(firstChild, translation_from_center);
    translation_from_center->addChild( firstChild );

    return true;
}

bool file_converter::transform_geode( Geode* geode, Vec3d& new_center, double& new_radius, double& old_radius )
{
    if (geode != NULL)
    {
        // TODO: Assume there is a single drawable and that it is a geometry ....
        if(geode->getNumDrawables()>0)
        {
            double x_mean, y_mean, z_mean;
            ref_ptr<Geometry> input_geometry = (Geometry *) geode->getDrawable(0);

            ref_ptr<Vec3Array> l93_vertex_vector = _converter->_geodesy->process_geometry(input_geometry, x_mean, y_mean, z_mean);
            if (!l93_vertex_vector.valid())
            {
                _converter->_last_error = "[Error propagated from geodesy]" + _converter->_geodesy->last_error();
                _converter->_error_callback( _converter->_last_error );
                return false;
            }

            // Here: stuffs on textures
            /* ... */

            old_radius = input_geometry->getBound().radius();
            input_geometry->setVertexArray( l93_vertex_vector );

            new_center = Vec3d(x_mean, y_mean, z_mean);
            new_radius = input_geometry->getBound().radius();
        }
        else
        {
            _converter->_last_error = "No drawable found!";
            return false;
        }
    }
    else
    {
        _converter->_last_error = "NULL geode!!!";
        return false;
    }
    return true;
}

bool file_converter::process(ref_ptr<Node>& input_node, string output)
{
    // Retrieve PagedLOD from imput_node
    NodeFinder<PagedLOD> pagedlod_finder;
    input_node->accept(pagedlod_finder);
    vector<PagedLOD*> pagedlods = pagedlod_finder.getNodeList();

    Vec3d new_center;
    double old_radius, new_radius;

    if ( pagedlods.size() )
    {
        NodeFinder<PagedLOD>::iterator it = pagedlods.begin(), ite = pagedlods.end();
        for(;it!=ite;++it)
        {
            if ( !transform_pagedlod(*it, new_center, new_radius, old_radius) )
            {
                _converter->_error_callback("Error processing (pagedlod) input ...");
                return false;
            }
        }

        write_output( input_node, output );

        return true;
    }
    else
    {
        NodeFinder<Geode> geode_finder;
        input_node->accept(geode_finder);
        ref_ptr<Geode> input_geode = geode_finder.getFirst();

        if (geode_finder.getNodeList().size()>1)
            _converter->_warning_callback( "PagedLOD has more than 1 geode (" + boost::lexical_cast<string>(geode_finder.getNodeList().size()) + " geodes)" );

        if ( !transform_geode(input_geode, new_center, new_radius, old_radius) )
        {
            _converter->_error_callback("Error processing (geode) input ...");
            return false;
        }

        ref_ptr<MatrixTransform> translation_from_center = _converter->create_translate_from_center_matrix(new_center);
        translation_from_center->addChild( input_node );

        write_output( translation_from_center, output );

        return true;
    }

    return true;
}

*************************************************************************************

And here is the code for the visitor:

*************************************************************************************

template <class NodeType>
class NodeFinder : public osg::NodeVisitor
{
public:
    NodeFinder() : NodeVisitor (osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {}
    void apply(NodeType &searchNode)
    {
        foundNodes.push_back ((NodeType*) &searchNode);
        traverse(searchNode);
    }

    NodeType* getFirst()
    {
        if (foundNodes.size() > 0)
            return foundNodes.front();
        else
            return NULL;
    }

    std::vector<NodeType*> getNodeList() { return foundNodes; }

    typedef typename std::vector<NodeType*>::const_iterator const_iterator;
    typedef typename std::vector<NodeType*>::iterator       iterator;

private:
    std::vector<NodeType*> foundNodes;
};

*************************************************************************************

At a first glance, it seems to work. However, when I zoom in and get close to the terrain, that is to say when I display high resolution LOD, sometimes, a small part disappear. If I move a few, the disappeared part comes back. See here: http://imageshack.us/photo/my-images/109/badtiles.png/

I also noticed that, my conversion seems to "add" geometries ... Do not understand why. I used this visitor to count the number of triangles in a PagedLOD:

*************************************************************************************

void InfoVisitor::apply(Geode& geode)
{
    for (unsigned int i=0; i<geode.getNumDrawables(); ++i)
    {
        ref_ptr<Drawable> drawable = geode.getDrawable(i);
        if (drawable.valid())
        {
            ref_ptr<Geometry> geometry = drawable->asGeometry();
            if (geometry.valid())
            {
                for (unsigned int j=0; j<geometry->getNumPrimitiveSets(); ++j)
                {
                    ref_ptr<PrimitiveSet> ps = geometry->getPrimitiveSet(j);

                    if (ps.valid())
                    {
                        switch (ps->getMode())
                        {
                        case PrimitiveSet::TRIANGLES:
                            _numTriangles += ps->getNumIndices() / 3;
                            break;
                        case PrimitiveSet::TRIANGLE_STRIP:
                            _numTriangles += ps->getNumIndices() - 2;
                            break;
                        case PrimitiveSet::TRIANGLE_FAN:
                            _numTriangles += ps->getNumIndices() - 2;
                            break;
                        case PrimitiveSet::QUADS:
                            _numTriangles += ps->getNumIndices() / 2;
                            break;
                        case PrimitiveSet::QUAD_STRIP:
                            _numTriangles += ps->getNumIndices() - 2;
                            break;
                        case PrimitiveSet::POLYGON:
                            _numTriangles += ps->getNumIndices() - 2;
                            break;
                        }
                    }
                }
            }
        }
    }
}

*************************************************************************************

When i use it on a root file before conversion, it return 52092 triangles. After conversion, on the same root file, it returns 53803 triangles...

I am completely lost on this issue and would be interested to have your opinion. I know that I could use a visitor for the conversion but, for now, I would prefer to first fix the problem. I am not able to locate and struggle with this tricky bug for a few days. Maybe something with the PagedLOD visitor?

Hope you could help and sorry for a so long message (just wanted to be clear enough).

Regards,

Olivier

Olivier Tournaire

unread,
Mar 13, 2013, 3:54:27 AM3/13/13
to osg-...@lists.openscenegraph.org
Hi all,

No one on this?

Regards,

Olivier

2013/3/4 Olivier Tournaire <oli...@gmail.com>

Robert Osfield

unread,
Mar 13, 2013, 6:44:16 AM3/13/13
to OpenSceneGraph Users
Hi Oliver,

On 13 March 2013 07:54, Olivier Tournaire <oli...@gmail.com> wrote:
> No one on this?

I suspect you lost a lot of readers with the volume of your post,
please remember that all members of the community are busy working and
only have a little time to read and keep up with posts and write
replies. Try distilling the issue you have down to what others might
be able to easily understand and provide an answer for.

Robert.
_______________________________________________
osg-users mailing list
osg-...@lists.openscenegraph.org
http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.org

Olivier Tournaire

unread,
Mar 13, 2013, 7:54:47 AM3/13/13
to OpenSceneGraph Users
Hi Robert,

So, I will try to summarize my post, without any code :) With my first post, I tried to be as complete as possible to clearly describe the issue, with all the related code. Sorry for this.

Thus, I am trying to convert geometry coordinates of Drawables contained in Geodes. My Geodes are children of PagedLOD, and there is a single Geode per PagedLOD. For a given part of my terrain, I have more than 1000 files, with 9 levels of details.

To convert the coordinates, I simply parse the coordinates in my original geometries, apply the transformation (code of my own and with proj4) and then use the setVertexArray method to replace the geometry coordinates in my drawables. I do this for each file. I center the coordinates around (0,0,0) because they are in 10e+06, and then add on top of my Geodes a translation MatrixTransform which value come from the barycenter of the points. If my Geode is contained in a PagedLOD, I also set the UserCenter to this value.

The problem is that when I view the transformed hierarchy in osgviewer, sometimes, when I am really close from the terrain, some tiles disappear. If I move a few, they appear again. It also seems that the "order" of the loaded tiles is incorrect. Both problems are illustrated by this images:
http://imageshack.us/photo/my-images/109/badtiles.png/
http://imageshack.us/photo/my-images/685/pagedlodsranges.jpg/



Hope you could help.

Best regards,

Olivier

2013/3/13 Robert Osfield <robert....@gmail.com>

Robert Osfield

unread,
Mar 13, 2013, 10:44:42 AM3/13/13
to OpenSceneGraph Users
Hi Olivier,

I principle what you are doing sounds reasonable. The only odd bit in
the description is that your data is already in OSG form before you
transform it into local coords, to me this seems like a missed
opportunity - as osg::Geometry stores coords with Vec3Array by default
so will be using float positions so open to precision issues,
subsequently moving these vertices into local coords won't fix the
precision problem as it's already backed into the data. Tools like
VirtualPlanetBuilder and osgEarth all convert from
geographic/geocentric coords that use doubles for position to local
coords with floats for the vertex data and doubles in the
MatrixTransform that decorate the local coordinate frames.

The problems with missing tiles/wrong tiles suggests to be an issue
with LOD range or centers. I guess it could also be something like
placing the transforms in the wrong place so that the LOD
centers/ranges aren't in the coordinate frame you think they are in.
Both VirtualPlanetBuilder and osgEarth place the MatrixTransform below
the PagedLOD and directly above the geometry.

Robert.

Olivier Tournaire

unread,
Mar 13, 2013, 2:44:00 PM3/13/13
to OpenSceneGraph Users
Hi Robert,

Thank you for your reply.

Regarding the precision issue, I take care to do all computation in double (which works because original coordinates are in local reference frame), and, once transformed, I can safely use float without loss of precision.

As VPB and osgEarth, the MatrixTransform is below the PagedLOD and directly above the Geode as you suggested.

However, having a detailed look at the original data, I saw that (A PagedLOD has always a single Geode with a single Drawable):
* PagedLOD center/radius is not the same as the Geode bound center/radius

In the current version of my code, I set the PagedLOD center as the new center computed from the coordinate transform, and set its radius according to the Geode bound radius after conversion. I tried, to update the PagedLOD raidus keeping the initial ratio, but the problem is still there.
However, this might also be related to the PagedLOD center. Do you have any idea on how to update its value, taking into account the new and old Geode centers, the old PagedLOD center?

Regards,

Robert Osfield

unread,
Mar 14, 2013, 5:32:20 AM3/14/13
to OpenSceneGraph Users
Hi Olivier,


On 13 March 2013 18:44, Olivier Tournaire <oli...@gmail.com> wrote:
> However, having a detailed look at the original data, I saw that (A PagedLOD
> has always a single Geode with a single Drawable):
> * PagedLOD center/radius is not the same as the Geode bound center/radius

The PageLOD's coordinate frame is in world coordinates while the leaf
nodes that sit below the MatrixTransform are in local coordinates so
one should expect the values of the bounding volume and center/radius
to be different.

> In the current version of my code, I set the PagedLOD center as the new
> center computed from the coordinate transform, and set its radius according
> to the Geode bound radius after conversion. I tried, to update the PagedLOD
> raidus keeping the initial ratio, but the problem is still there.
> However, this might also be related to the PagedLOD center. Do you have any
> idea on how to update its value, taking into account the new and old Geode
> centers, the old PagedLOD center?

The answer is above, the PagedLOD should be set up in world
coordinates not local coordinates.

Olivier Tournaire

unread,
Mar 14, 2013, 7:01:52 AM3/14/13
to OpenSceneGraph Users
Hi Robert,

2013/3/14 Robert Osfield <robert....@gmail.com>

Hi Olivier,


On 13 March 2013 18:44, Olivier Tournaire <oli...@gmail.com> wrote:
> However, having a detailed look at the original data, I saw that (A PagedLOD
> has always a single Geode with a single Drawable):
> * PagedLOD center/radius is not the same as the Geode bound center/radius

The PageLOD's coordinate frame is in world coordinates while the leaf
nodes that sit below the MatrixTransform are in local coordinates so
one should expect the values of the bounding volume and center/radius
to be different.

OK, but in the original data, there is no MatrixTransform between the PagedLOD and the Geode. So, do not understand why center/radius are different.
Reply all
Reply to author
Forward
0 new messages