// Initialize GLUT & OpenSG and set up the scene
int main(int argc, char **argv)
{
    // OSG init
    OSG::osgInit(argc,argv);

    gScene = OSG::GroupNodeRefPtr::create();

    if (argc < 2)
    {
       std::cout << "Specify a directory to load models from." << std::endl;
       gScene = static_cast<OSG::Node *>(NULL);
       OSG::osgExit();
       exit(-1);
    }

    findModels(std::string(argv[1]));

    // Start the background loader.
    OSG::BackgroundLoader::the()->start();

    // GLUT init
    int winid = setupGLUT(&argc, argv);

    // the connection between GLUT and OpenSG
    OSG::GLUTWindowUnrecPtr gwin= OSG::GLUTWindow::create();
    gwin->setGlutId(winid);
    gwin->init();
    
    // create the SimpleSceneManager helper
    mgr = new OSG::SimpleSceneManager;

    // tell the manager what to manage
    mgr->setWindow(gwin );
    mgr->setRoot  (gScene.node());

    // show the whole scene
    mgr->showAll();

    // GLUT main loop
    glutMainLoop();

    OSG::BackgroundLoader::the()->stop(); 
 
   return 0;
}
void initPlaneSetup(void)
{
    // beacon for camera and light
    OSG::GroupNodeRefPtr  beacon = OSG::GroupNodeRefPtr::create();

    // transformation
    OSG::TransformNodeRefPtr cam_transPlane = 
        OSG::TransformNodeRefPtr::create();
    cam_transPlane.node()->addChild(beacon);

    // light
    OSG::DirectionalLightNodeRefPtr dl = 
        OSG::DirectionalLightNodeRefPtr::create();

    dl->setAmbient  (.3f, .3f, .3f, 1);
    dl->setDiffuse  ( 1,  1,  1, 1);
    dl->setDirection( 0,  0,  1   );
    dl->setBeacon   (beacon          );

    // planeRoot
    planeRoot = OSG::GroupNodeRefPtr::create();

    planeRoot.node()->addChild(cam_transPlane);
    planeRoot.node()->addChild(animRoot);
    planeRoot.node()->addChild(dl);

    // Create plane to project the staged render
    OSG::NodeUnrecPtr plane_node = OSG::makePlane(10, 10, 5, 5);

    // Setup the shared texture and texture environment
    // - Create an empty image so texture can allocate size and memory
    OSG::ImageUnrecPtr pImg = OSG::Image::create();
    pImg->set(OSG::Image::OSG_RGB_PF, 512, 512);

    tx1o->setImage    (pImg      );
    tx1o->setMinFilter(GL_LINEAR );
    tx1o->setMagFilter(GL_LINEAR );
    tx1o->setWrapS    (GL_REPEAT );
    tx1o->setWrapT    (GL_REPEAT );

    tx1e->setEnvMode (GL_REPLACE);

#ifdef USE_DEPTH_TEXTURE
    OSG::ImageUnrecPtr dImg = OSG::Image::create();
    dImg->set(Image::OSG_L_PF, 512, 512);

    txDepth->setImage (dImg);
    txDepth->setMinFilter(GL_NEAREST );
    txDepth->setMagFilter(GL_LINEAR );
    txDepth->setWrapS    (GL_CLAMP_TO_EDGE );
    txDepth->setWrapT    (GL_CLAMP_TO_EDGE );
    txDepth->setExternalFormat(GL_DEPTH_COMPONENT);
    txDepth->setInternalFormat(GL_DEPTH_COMPONENT32);
#endif


    // Material for plane
    // - Create a material that will reference the texture and render
    //     it on the plane
    OSG::SimpleMaterialUnrecPtr mat = OSG::SimpleMaterial::create();

    mat->setDiffuse(OSG::Color3f(1,1,1));
    mat->setLit    (false              );
#ifdef USE_DEPTH_TEXTURE
    mat->addChunk  (txDepth            );
#else
    mat->addChunk  (tx1o               );
#endif
    mat->addChunk  (tx1e               );

    OSG::GeometryUnrecPtr pGeo = 
        dynamic_cast<OSG::Geometry *>(plane_node->getCore());
    pGeo->setMaterial(mat);

    // Finish connecting graph
    OSG::TransformNodeRefPtr scene_trans = OSG::TransformNodeRefPtr::create();
    scene_trans.node()->addChild(plane_node );

    dl.node()->addChild(scene_trans);

}
void findModels(std::string dirname)
{
   fs::path dir_path(dirname);

   if (!fs::exists(dir_path))
   { 
      std::cerr << "ERROR: path does not exist: " << dirname << std::endl; 
      gScene = static_cast<OSG::Node *>(NULL);
      OSG::osgExit();
      exit(-1);
   }

   fs::directory_iterator end_itr;
   for(fs::directory_iterator itr(dir_path); itr != end_itr; ++itr)
   {
      if(!fs::is_directory(*itr))
      {
         if (fs::extension(*itr) == std::string(".osb") ||
             fs::extension(*itr) == std::string(".wrl"))
         {
            fs::path  complete_file = fs::complete(*itr);
            std::string filename(complete_file.string());
            std::cout << "Found file: " << filename << std::endl;
            OSG::ModelRequestPtr req = OSG::ModelRequest::create()->init(OSG::NodeRefPtr(gScene.node()), filename);
            OSG::BackgroundLoader::the()->addRequest(req);
         }
      }
   }

}
// Setup the part of the scene rooted at animRoot
// This includes a file to animate, a beacon for a light,
// and a staged core to render this subtree from the position
// of the light.
void initAnimSetup(int argc, char **argv)
{
    // beacon for light and stage camera
    OSG::GroupNodeRefPtr beacon = OSG::GroupNodeRefPtr::create();

    // transformation for beacon
    cam_transScene   = OSG::TransformNodeRefPtr::create();
    cam_transScene.node()->addChild(beacon);

    // light
    OSG::DirectionalLightNodeRefPtr dlight = 
        OSG::DirectionalLightNodeRefPtr::create();

    dlight->setAmbient  (.3f, .3f, .3f, 1);
    dlight->setDiffuse  ( 1,  1,  1, 1);
    dlight->setDirection( 0,  0,  1   );
    dlight->setBeacon   (beacon       );

    // animRoot
    animRoot = OSG::GroupNodeRefPtr::create();
    animRoot.node()->addChild(cam_transScene   );

    // Load the file and put it in the graph
    // under the sceneXform node.
    OSG::NodeUnrecPtr file = NULL;

    if(argc > 1)
    { file = OSG::SceneFileHandler::the()->read(argv[1]); }

    if(file == NULL)
    {
        std::cerr << "Couldn't load file, ignoring" << std::endl;
        file = OSG::makeTorus(.5, 2, 16, 16);
    }

    OSG::Vec3f min,max;
    OSG::commitChanges();
    file->updateVolume();
    file->dump();
    file->getVolume().getBounds(min, max);

    std::cout << "Volume: from " << min << " to " << max << std::endl;
    sceneTrans.setValues(min[0] + ((max[0] - min[0]) * 0.5),
                         min[1] + ((max[1] - min[1]) * 0.5),
                         max[2] + ( max[2] - min[2]) * 4.5 );


    sceneXform = OSG::TransformNodeRefPtr::create();
    sceneXform.node()->addChild(file);

    OSG::NodeUnrecPtr pBoxNode = OSG::makeBox(1, 1, 1, 5, 5, 5);

    pBoxNode->setTravMask(pBoxNode->getTravMask() & 0x0001);

    sceneXform.node()->addChild(pBoxNode);

    dlight.node()->addChild(sceneXform);


    // ---- STAGE RENDERING SETUP --- //
    // Camera: setup camera to point from beacon (light pos)
    //   with a 90deg FOV to render the scene
    OSG::PerspectiveCameraUnrecPtr stage_cam = OSG::PerspectiveCamera::create();

    stage_cam->setBeacon(beacon);
    stage_cam->setFov   (OSG::osgDegree2Rad(90));
    stage_cam->setNear  (0.1f);
    stage_cam->setFar   (100000);


    // Background
    OSG::SolidBackgroundUnrecPtr bkgnd = OSG::SolidBackground::create();
    bkgnd->setColor(OSG::Color3f(0,1,0));

    // FBO setup
    pFBO         = OSG::FrameBufferObject::create();
    pTexBuffer   = OSG::TextureBuffer::create();

#ifdef USE_DEPTH_TEXTURE
    OSG::TextureBufferUnrecPtr     pDepthBuffer = OSG::TextureBuffer::create();
    pDepthBuffer->setTexture(txDepth);
#else
    OSG::RenderBufferUnrecPtr      pDepthBuffer = OSG::RenderBuffer ::create();
    pDepthBuffer->setInternalFormat(GL_DEPTH_COMPONENT24   );
#endif

    
    pTexBuffer->setTexture (tx1o);
    pTexBuffer->setReadBack(true);

    pFBO->setSize(512, 512);
    pFBO->setColorAttachment(pTexBuffer, 0);
    pFBO->setDepthAttachment(pDepthBuffer );

    pFBO->editMFDrawBuffers()->clear();
    pFBO->editMFDrawBuffers()->push_back(GL_COLOR_ATTACHMENT0_EXT);

    // Stage core setup
    OSG::SimpleStageNodeRefPtr pStage    = OSG::SimpleStageNodeRefPtr::create();
    pStage->setRenderTarget(pFBO );
    pStage->setCamera      (stage_cam  );
    pStage->setBackground  (bkgnd);

    pStage->addPreRenderFunctor (&testPreRenderCB, "" );
    pStage->addPostRenderFunctor(&testPostRenderCB, "");

    // Setup sub-tree visitor
    // - This will setup a graph that will render a subtree during traversal
    pVisit = OSG::VisitSubTreeNodeRefPtr::create();
    pVisit->setSubTreeRoot(dlight);

    pStage.node()->addChild(pVisit);
    animRoot.node()->addChild(pStage);
    animRoot.node()->addChild(dlight);
}