1

I have a few objects that I want to combine into a scene graph.

  1. Street inherits from Geode and has a Geometry child drawable made up of a GL_LINE_STRIP.

  2. Pointer inherits from PositionAttitudeTransform and contains a Geode which contains two Geometry polygons.

When I add a bunch of Streets to a Group, it looks just fine. When I add only the Pointer to a Group, it also looks fine. But if I somehow have them both in the scene, the second one is screwed up. See the two pictures.

One view when the streets are first

In the above picture, the street network is as desired, and in the picture below, the pointer is as desired.

One view when the pointer is first

I'd appreciate any help! If you need to see the code, let me know and I'll update my question.

Update 1: Since nothing has happened so far, here is the minimal amount of code necessary to produce the phenomenon. I have put two pointers next to each other with no problem, so I'm starting to suspect that I made the streets wrong... next update will be some street generation code.

Update 2: The code now contains the street drawing code.

Update 3: The code now contains the pointer drawing code as well, and the street drawing code has been simplified.

// My libraries:
#include <asl/util/color.h>
using namespace asl;

#include <straph/point.h>
#include <straph/straph.h>
using namespace straph;

// Standard and OSG libraries:
#include <utility>
#include <boost/tuple/tuple.hpp> // tie
using namespace std;

#include <osg/ref_ptr>
#include <osg/Array>
#include <osg/Geometry>
#include <osg/Geode>
#include <osg/Group>
#include <osg/LineWidth>
using namespace osg;

#include <osgUtil/Tessellator>
#include <osgViewer/Viewer>
using namespace osgViewer;

/*
 * Just FYI: A Polyline looks like this:
 *
 *  typedef std::vector<Point> Polyline;
 *
 * And a Point basically is a simple struct:
 *
 *  struct Point {
 *      double x;
 *      double y;
 *  };
 */

inline osg::Vec3d toVec3d(const straph::Point& p, double elevation=0.0)
{
    return osg::Vec3d(p.x, p.y, elevation);
}

Geometry* createStreet(const straph::Polyline& path)
{
    ref_ptr<Vec3dArray> array (new Vec3dArray(path.size()));
    for (unsigned i = 0; i < path.size(); ++i) {
        (*array)[i] = toVec3d(path[i]);
    }
    Geometry* geom = new Geometry;
    geom->setVertexArray(array.get());
    geom->addPrimitiveSet(new osg::DrawArrays(GL_LINE_STRIP, 0, array->size()));
    return geom;
}

Geode* load_streets()
{
    unique_ptr<Straph> graph = read_shapefile("mexico/roads", 6);

    Geode* root = new Geode();
    boost::graph_traits<straph::Straph>::edge_iterator ei, ee;
    for (boost::tie(ei, ee) = edges(*graph); ei != ee; ++ei) {
        const straph::Segment& s = (*graph)[*ei];
        root->addDrawable(createStreet(s.polyline));
    }
    return root;
}

Geode* createPointer(double width, const Color& body_color, const Color& border_color)
{
    float f0 = 0.0f;
    float f3 = 3.0f;
    float f1 = 1.0f * width;
    float f2 = 2.0f * width;

    // Create vertex array
    ref_ptr<Vec3Array> vertices (new Vec3Array(4));
    (*vertices)[0].set(  f0   ,  f0    , f0 );
    (*vertices)[1].set( -f1/f3, -f1/f3 , f0 );
    (*vertices)[2].set(  f0   ,  f2/f3 , f0 );
    (*vertices)[3].set(  f1/f3, -f1/f3 , f0 );

    // Build the geometry object
    ref_ptr<Geometry> polygon (new Geometry);
    polygon->setVertexArray( vertices.get() );
    polygon->addPrimitiveSet( new DrawArrays(GL_POLYGON, 0, 4) );

    // Set the colors
    ref_ptr<Vec4Array> body_colors (new Vec4Array(1));
    (*body_colors)[0] = body_color.get();
    polygon->setColorArray( body_colors.get() );
    polygon->setColorBinding( Geometry::BIND_OVERALL );

    // Start the tesselation work
    osgUtil::Tessellator tess;
    tess.setTessellationType( osgUtil::Tessellator::TESS_TYPE_GEOMETRY );
    tess.setWindingType( osgUtil::Tessellator::TESS_WINDING_ODD );
    tess.retessellatePolygons( *polygon );

    // Create the border-lines
    ref_ptr<Geometry> border (new Geometry);
    border->setVertexArray( vertices.get() );
    border->addPrimitiveSet(new DrawArrays(GL_LINE_LOOP, 0, 4));
    border->getOrCreateStateSet()->setAttribute(new LineWidth(2.0f));

    ref_ptr<Vec4Array> border_colors (new Vec4Array(1));
    (*border_colors)[0] = border_color.get();
    border->setColorArray( border_colors.get() );
    border->setColorBinding( Geometry::BIND_OVERALL );

    // Create Geode object
    ref_ptr<Geode> geode (new Geode);
    geode->addDrawable( polygon.get() );
    geode->addDrawable( border.get() );

    return geode.release();
}

int main(int, char**)
{
    Group* root = new Group();

    Geode* str = load_streets();
    root->addChild(str);

    Geode* p = createPointer(6.0, TangoColor::Scarlet3, TangoColor::Black);
    root->addChild(p);

    Viewer viewer;
    viewer.setSceneData(root);
    viewer.getCamera()->setClearColor(Color(TangoColor::White).get());
    viewer.run();
}
cassava
  • 578
  • 6
  • 14

1 Answers1

1

In the functions createStreet I use a Vec3dArray for the vertex array, whereas in the createPointer function, I use a Vec3Array. In the library I guess it expects all nodes to be composed of floats or doubles, but not both. Changing these two functions solves the problem:

inline osg::Vec3 toVec3(const straph::Point& p, float elevation=0.0)
{
    return osg::Vec3(float(p.x), float(p.y), elevation);
}

Geometry* createStreet(const straph::Polyline& path)
{
    ref_ptr<Vec3Array> array (new Vec3Array(path.size()));
    for (unsigned i = 0; i < path.size(); ++i) {
        (*array)[i] = toVec3(path[i]);
    }
    Geometry* geom = new Geometry;
    geom->setVertexArray(array.get());
    geom->addPrimitiveSet(new osg::DrawArrays(GL_LINE_STRIP, 0, array->size()));
    return geom;
}

Here a comment by Robert Osfield:

I can only provide a guess, and that would be that the Intel OpenGL doesn't handle double vertex data correctly, so you are stumbling across a driver bug.

In general OpenGL hardware is based around floating point maths so the drivers normally convert any double data you pass it into floats before passing it to the GPU. Even if the driver does this correctly this conversion process slows performance down so it's best to keep osg::Geometry vertex/texcoord/normal etc. data all in float arrays such as Vec3Array.

You can retain precision by translating your data to a local origin prior to conversion to float then place a MatrixTransform above your data to place it in the correct 3D position. The OSG by default uses double for all internal matrices that that when it accumulates the modelvew matrix during the cull traversal double precision is maintain for as long as possible before passing the final modelview matrix to OpenGL. Using this technique the OSG can handle whole earth data without any jitter/precision problems.

Community
  • 1
  • 1
cassava
  • 578
  • 6
  • 14