// react to keys
void keyboard(unsigned char k, int, int)
{
    switch(k)
    {
    case 27:    mgr             = NULL;
                gpcl_defaultmat = NULL;
                gpcl_surface    = NULL;
                exit(1);
    case 'y':   
    case 'z':
                glPolygonMode( GL_FRONT_AND_BACK, GL_POINT);
                std::cerr << "PolygonMode: Point." << std::endl;
                break;
    case 'x':   glPolygonMode( GL_FRONT_AND_BACK, GL_LINE);
                std::cerr << "PolygonMode: Line." << std::endl;
                break;
    case 'c':   glPolygonMode( GL_FRONT_AND_BACK, GL_FILL);
                std::cerr << "PolygonMode: Fill." << std::endl;
                break;
    case 'f':   g_error *= 2;
                std::cerr << "Error: " << g_error << std::endl;
                gpcl_surface->setError( g_error );
                break;
    case 'g':   g_error /= 2;
                std::cerr << "Error: " << g_error << std::endl;
                gpcl_surface->setError( g_error );
                break;
    default:    std::cerr << "y,z   = Polygon Point Mode\n"
                          << "x     = Polygon Line Mode\n"
                          << "c     = Polygon Fill Mode\n"
                          << "f/g   = +/- Error\n"
                          << std::endl;
                break;
    }
}
// react to keys
void keyboard(unsigned char k, int, int)
{
    switch(k)
    {
    case 27:    mgr             = NULL;
                gpcl_defaultmat = NULL;
                gpcl_surface    = NULL;
                exit(1);
    case 'y':   
    case 'z':
                glPolygonMode( GL_FRONT_AND_BACK, GL_POINT);
                std::cerr << "PolygonMode: Point." << std::endl;
                break;
    case 'x':   glPolygonMode( GL_FRONT_AND_BACK, GL_LINE);
                std::cerr << "PolygonMode: Line." << std::endl;
                break;
    case 'c':   glPolygonMode( GL_FRONT_AND_BACK, GL_FILL);
                std::cerr << "PolygonMode: Fill." << std::endl;
                break;
    case 'f':   g_error *= 2;
                std::cerr << "Error: " << g_error << std::endl;
                gpcl_surface->setError( g_error );
                // See comments about OSGSurface::forceTessellate() below

                if(useForceTesselate)
                    gpcl_surface->forceTessellate(); 
                break;
    case 'g':   g_error /= 2;
                std::cerr << "Error: " << g_error << std::endl;
                gpcl_surface->setError( g_error );

                // See comments about OSGSurface::forceTessellate() below
                if(useForceTesselate)
                    gpcl_surface->forceTessellate();                
                break;
    case 'd':   gpcl_surface->setIsDelaunay(!gpcl_surface->getIsDelaunay());
                std::cerr << "Delaunay: " << gpcl_surface->getIsDelaunay()
                          << std::endl;
                // See comments about OSGSurface::forceTessellate() below
                if(useForceTesselate)
                    gpcl_surface->forceTessellate();
                break;
    default:    std::cerr << "y,z   = Polygon Point Mode\n"
                          << "x     = Polygon Line Mode\n"
                          << "c     = Polygon Fill Mode\n"
                          << "f/g   = +/- Error\n"
                          << "d     = Delaunay on/off\n"
                          << std::endl;
                break;
    }
}
// react to keys
void keyboard(unsigned char k, int, int)
{
    switch(k)
    {
    case 27:
        delete mgr;
        gpcl_defaultmat = NULL;
        gpcl_surface1   = NULL;
        gpcl_surface2   = NULL;
        gpcl_fb_chunk   = NULL;
        exit(1);
    case 'y':
    case 'z':
        glPolygonMode( GL_FRONT_AND_BACK, GL_POINT);
        std::cerr << "PolygonMode: Point." << std::endl;
        break;
    case 'x':
        glPolygonMode( GL_FRONT_AND_BACK, GL_LINE);
        std::cerr << "PolygonMode: Line." << std::endl;
        break;
    case 'c':
        glPolygonMode( GL_FRONT_AND_BACK, GL_FILL);
        std::cerr << "PolygonMode: Fill." << std::endl;
        break;
    case 'f':
        g_error1 *= 2;
        if ( !gb_differenterrors )
        {
            std::cerr << "Error: " << g_error1 << std::endl;
        }
        else
        {
            std::cerr << "Error1: " << g_error1 << std::endl;
        }
        gpcl_surface1->setError( g_error1 );

        if ( !gb_differenterrors )
        {
            g_error2 = g_error1;
            gpcl_surface2->setError( g_error2 );
        }
        break;
    case 'g':
        g_error1 /= 2;
        if ( !gb_differenterrors )
        {
            std::cerr << "Error: " << g_error1 << std::endl;
        }
        else
        {
            std::cerr << "Error1: " << g_error1 << std::endl;
        }
        gpcl_surface1->setError( g_error1 );

        if ( !gb_differenterrors )
        {
            g_error2 = g_error1;
            gpcl_surface2->setError( g_error2 );
        }
        break;
    case 'h':
        g_error2 *= 2;
        if ( !gb_differenterrors )
        {
            std::cerr << "Error: " << g_error2 << std::endl;
        }
        else
        {
            std::cerr << "Error2: " << g_error2 << std::endl;
        }
        gpcl_surface2->setError( g_error2 );

        if ( !gb_differenterrors )
        {
            g_error1 = g_error2;
            gpcl_surface1->setError( g_error1 );
        }
        break;
    case 'j':
        g_error2 /= 2;
        if ( !gb_differenterrors )
        {
            std::cerr << "Error: " << g_error2 << std::endl;
        }
        else
        {
            std::cerr << "Error2: " << g_error2 << std::endl;
        }
        gpcl_surface2->setError( g_error2 );

        if ( !gb_differenterrors )
        {
            g_error1 = g_error2;
            gpcl_surface1->setError( g_error1 );
        }
        break;
    default:
        std::cerr << "y,z   = Polygon Point Mode\n"
                  << "x     = Polygon Line Mode\n"
                  << "c     = Polygon Fill Mode\n"
                  << "f/g   = +/- Error1\n"
                  << "h/j   = +/- Error2\n"
                  << std::endl;
        break;
    }
}
OSG::NodeTransitPtr makeScene( void )
{
    setupDefaultMaterial();

    OSG::NodeTransitPtr         root    = OSG::Node            ::create();
    OSG::SurfaceRefPtr          surface = OSG::Surface         ::create();
    OSG::GeoPnt3fPropertyRefPtr cps     = OSG::GeoPnt3fProperty::create();

    // control points should always be 3D for the time being,
    // rational support will be added later
    cps->clear();
    cps->push_back( OSG::Pnt3f(  1,  1,  2 ));
    cps->push_back( OSG::Pnt3f(  1,  0,  0 ));
    cps->push_back( OSG::Pnt3f(  1,  0,  1 ));
    cps->push_back( OSG::Pnt3f(  1, -1, -2 ));

    cps->push_back( OSG::Pnt3f(  0,  1,  0 ));
    cps->push_back( OSG::Pnt3f(  0,  0,  0 ));
    cps->push_back( OSG::Pnt3f(  0,  0,  1 ));
    cps->push_back( OSG::Pnt3f(  0, -1,  1 ));

    cps->push_back( OSG::Pnt3f(  0,  1,  1 ));
    cps->push_back( OSG::Pnt3f(  0,  0,  1 ));
    cps->push_back( OSG::Pnt3f(  0,  0,  0 ));
    cps->push_back( OSG::Pnt3f(  0, -1,  0 ));

    cps->push_back( OSG::Pnt3f( -1,  1,  1 ));
    cps->push_back( OSG::Pnt3f( -1,  0,  1 ));
    cps->push_back( OSG::Pnt3f( -1,  0,  0 ));
    cps->push_back( OSG::Pnt3f( -1, -1,  0 ));

    // let's clear the trimming
    surface->removeCurves();

    // set up dimensions and knot vectors:
    surface->setDimU( 3 );
    surface->setDimV( 3 );
    surface->editMFKnotsU()->clear();
    surface->editMFKnotsU()->push_back( 0 );
    surface->editMFKnotsU()->push_back( 0 );
    surface->editMFKnotsU()->push_back( 0 );
    surface->editMFKnotsU()->push_back( 0 );
    surface->editMFKnotsU()->push_back( 1 );
    surface->editMFKnotsU()->push_back( 1 );
    surface->editMFKnotsU()->push_back( 1 );
    surface->editMFKnotsU()->push_back( 1 );
    surface->editMFKnotsV()->clear();
    surface->editMFKnotsV()->push_back( 0 );
    surface->editMFKnotsV()->push_back( 0 );
    surface->editMFKnotsV()->push_back( 0 );
    surface->editMFKnotsV()->push_back( 0 );
    surface->editMFKnotsV()->push_back( 1 );
    surface->editMFKnotsV()->push_back( 1 );
    surface->editMFKnotsV()->push_back( 1 );
    surface->editMFKnotsV()->push_back( 1 );
    
    // set control points and texture control points
    surface->setControlPoints( cps );
    
    // set error
    surface->setError( g_error );
    
    // and finally set the material
    surface->setMaterial( gpcl_defaultmat );

    root->setCore( surface );

    gpcl_surface = surface;
    
    // Usually when the endEditCP() is called for an OSGSurface object
    // the need for (re)tessellation is flagged, and when the object
    // would be drawn on screen it gets tessellated. This might cause 
    // problems in a cluster environment as surfaces may get tessellated
    // more than once (e.g. on the server and on each client) which can
    // be _very_ slow and annoying.
    // 
    // The solution is "forcing" the tessellation by calling
    // OSGSurface::forceTessellate(). This will perform the tessellation
    // immediately, and setup flags so that automatic tessellation will
    // not be called again. This means that even if you change some or
    // all of the surface descriptiong FieldContainersin your OSGSurface
    // object it will _not_ be retessellated. You will have to "force"
    // tessellation again by calling OSGSurface::forceTessellate() whenever
    // you change something. See the calls in the keyboard() function
    // above.
    //
    // If you don't do clustering, you can probably just forget about all
    // this OSGSurface::forceTessellate() stuff (just like in the other 
    // Surface examples), it will just work automagically and tessellate 
    // your surface when needed (provided you don't forget the proper 
    // beginEditCP()/endEditCP() calls.
    if(useForceTesselate)
        gpcl_surface->forceTessellate();

    return root;
}
OSG::NodeTransitPtr makeScene( void )
{
    setupDefaultMaterial();

    OSG::NodeTransitPtr         root    = OSG::Node            ::create();
    OSG::SurfaceRefPtr          surface = OSG::Surface         ::create();
    OSG::GeoPnt3fPropertyRefPtr cps     = OSG::GeoPnt3fProperty::create();

    // control points should always be 3D for the time being,
    // rational support will be added later
    cps->clear();
    cps->push_back( OSG::Pnt3f(  1,  1,  0 ));
    cps->push_back( OSG::Pnt3f(  1,  0,  0 ));
    cps->push_back( OSG::Pnt3f(  1,  0,  1 ));
    cps->push_back( OSG::Pnt3f(  1, -1,  1 ));

    cps->push_back( OSG::Pnt3f(  0,  1,  0 ));
    cps->push_back( OSG::Pnt3f(  0,  0,  0 ));
    cps->push_back( OSG::Pnt3f(  0,  0,  1 ));
    cps->push_back( OSG::Pnt3f(  0, -1,  1 ));

    cps->push_back( OSG::Pnt3f(  0,  1,  1 ));
    cps->push_back( OSG::Pnt3f(  0,  0,  1 ));
    cps->push_back( OSG::Pnt3f(  0,  0,  0 ));
    cps->push_back( OSG::Pnt3f(  0, -1,  0 ));

    cps->push_back( OSG::Pnt3f( -1,  1,  1 ));
    cps->push_back( OSG::Pnt3f( -1,  0,  1 ));
    cps->push_back( OSG::Pnt3f( -1,  0,  0 ));
    cps->push_back( OSG::Pnt3f( -1, -1,  0 ));
    
    std::vector<OSG::Real64> knots1;
    std::vector<OSG::Real64> knots2;
    std::vector<OSG::Pnt2f> points;
    knots1.clear();
    knots1.push_back(0);
    knots1.push_back(0);
    knots1.push_back(1);
    knots1.push_back(1);
    knots2.clear();
    knots2.push_back(0);
    knots2.push_back(0);
    knots2.push_back(0);
    knots2.push_back(1);
    knots2.push_back(1);
    knots2.push_back(1);
        
    // first, let's clear the trimming
    surface->removeCurves();

    // add the outer trimming around the domain
    points.clear();
    points.push_back( OSG::Pnt2f(0,0) );
    points.push_back( OSG::Pnt2f(1,0) );
    surface->addCurve( 1, knots1, points, true );
    points.clear();
    points.push_back( OSG::Pnt2f(1,0) );
    points.push_back( OSG::Pnt2f(1,1) );
    surface->addCurve( 1, knots1, points, false );
    points.clear();
    points.push_back( OSG::Pnt2f(1,1) );
    points.push_back( OSG::Pnt2f(0,1) );
    surface->addCurve( 1, knots1, points, false );
    points.clear();
    points.push_back( OSG::Pnt2f(0,1) );
    points.push_back( OSG::Pnt2f(0,0) );
    surface->addCurve( 1, knots1, points, false );

    // add inner trimming loop 1
    points.clear();
    points.push_back( OSG::Pnt2f(0.3f,0.5f) );
    points.push_back( OSG::Pnt2f(0.1f,0.9f) );
    points.push_back( OSG::Pnt2f(0.5f,0.7f) );
    surface->addCurve( 2, knots2, points, true );
    points.clear();
    points.push_back( OSG::Pnt2f(0.5f,0.7f) );
    points.push_back( OSG::Pnt2f(0.9f,0.9f) );
    points.push_back( OSG::Pnt2f(0.7f,0.5f) );
    surface->addCurve( 2, knots2, points, false );
    points.clear();
    points.push_back( OSG::Pnt2f(0.7f,0.5f) );
    points.push_back( OSG::Pnt2f(0.9f,0.1f) );
    points.push_back( OSG::Pnt2f(0.5f,0.3f) );
    surface->addCurve( 2, knots2, points, false );
    points.clear();
    points.push_back( OSG::Pnt2f(0.5f,0.3f) );
    points.push_back( OSG::Pnt2f(0.1f,0.1f) );
    points.push_back( OSG::Pnt2f(0.3f,0.5f) );
    surface->addCurve( 2, knots2, points, false );
    
    // add inner trimming loop 2
    points.clear();
    points.push_back( OSG::Pnt2f(0.4f,0.5f) );
    points.push_back( OSG::Pnt2f(0.4f,0.4f) );
    points.push_back( OSG::Pnt2f(0.5f,0.4f) );
    surface->addCurve( 2, knots2, points, true );
    points.clear();
    points.push_back( OSG::Pnt2f(0.5f,0.4f) );
    points.push_back( OSG::Pnt2f(0.6f,0.4f) );
    points.push_back( OSG::Pnt2f(0.6f,0.5f) );
    surface->addCurve( 2, knots2, points, false );
    points.clear();
    points.push_back( OSG::Pnt2f(0.6f,0.5f) );
    points.push_back( OSG::Pnt2f(0.6f,0.6f) );
    points.push_back( OSG::Pnt2f(0.5f,0.6f) );
    surface->addCurve( 2, knots2, points, false );
    points.clear();
    points.push_back( OSG::Pnt2f(0.5f,0.6f) );
    points.push_back( OSG::Pnt2f(0.4f,0.6f) );
    points.push_back( OSG::Pnt2f(0.4f,0.5f) );
    surface->addCurve( 2, knots2, points, false );
    
    // set up dimensions and knot vectors:
    surface->setDimU( 3 );
    surface->setDimV( 3 );
    surface->editMFKnotsU()->clear();
    surface->editMFKnotsU()->push_back( 0 );
    surface->editMFKnotsU()->push_back( 0 );
    surface->editMFKnotsU()->push_back( 0 );
    surface->editMFKnotsU()->push_back( 0 );
    surface->editMFKnotsU()->push_back( 1 );
    surface->editMFKnotsU()->push_back( 1 );
    surface->editMFKnotsU()->push_back( 1 );
    surface->editMFKnotsU()->push_back( 1 );
    surface->editMFKnotsV()->clear();
    surface->editMFKnotsV()->push_back( 0 );
    surface->editMFKnotsV()->push_back( 0 );
    surface->editMFKnotsV()->push_back( 0 );
    surface->editMFKnotsV()->push_back( 0 );
    surface->editMFKnotsV()->push_back( 1 );
    surface->editMFKnotsV()->push_back( 1 );
    surface->editMFKnotsV()->push_back( 1 );
    surface->editMFKnotsV()->push_back( 1 );
    
    // set control points and texture control points
    surface->setControlPoints( cps );
    
    // set error
    surface->setError( g_error );
    
    // and finally set the material
    surface->setMaterial( gpcl_defaultmat );

    root->setCore( surface );

    gpcl_surface = surface;

    return root;
}