// @(#)root/gl:$Id: TGLViewerBase.cxx 24316 2008-06-17 19:48:30Z matevz $
// Author:  Matevz Tadel, Feb 2007

/*************************************************************************
 * Copyright (C) 1995-2004, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#include "TGLViewerBase.h"

#include "TGLSceneBase.h"
#include "TGLSceneInfo.h"

#include <TGLRnrCtx.h>
#include "TGLCamera.h"
#include <TGLOverlay.h>
#include <TGLSelectBuffer.h>
#include <TGLSelectRecord.h>
#include <TGLUtil.h>
#include "TGLContext.h"
#include "TGLIncludes.h"

#include <algorithm>
#include <stdexcept>

//______________________________________________________________________
// TGLViewerBase
//
// Base class for GL viewers. Provides a basic scene management and a
// small set of control variables (camera, LOD, style, clip) that are
// used by the scene classes. Renering wrappers are available but
// minimal.
//
// There is no concept of GL-context here ... we just draw
// into whatever is set from outside.
//
// Development notes:
//
// Each viewer automatically creates a TGLRnrCtx and passes it down
// all render functions.

ClassImp(TGLViewerBase)

//______________________________________________________________________
TGLViewerBase::TGLViewerBase() :
   fRnrCtx    (0),
   fCamera    (0),
   fClip      (0),
   fLOD       (TGLRnrCtx::kLODHigh),
   fStyle     (TGLRnrCtx::kFill),

   fResetSceneInfosOnRender (kFALSE),
   fChanged                 (kFALSE)
{
   // Constructor.

   fRnrCtx = new TGLRnrCtx(this);
}

//______________________________________________________________________
TGLViewerBase::~TGLViewerBase()
{
   // Destructor.

   for (SceneInfoList_i i=fScenes.begin(); i!=fScenes.end(); ++i)
   {
      (*i)->GetScene()->RemoveViewer(this);
      delete *i;
   }

   delete fRnrCtx;
}

//______________________________________________________________________________
const char* TGLViewerBase::LockIdStr() const
{
   // Name to print in locking output.

   return "TGLViewerBase";
}

/**************************************************************************/
// Scene & scene-info management
/**************************************************************************/

//______________________________________________________________________
TGLViewerBase::SceneInfoList_i
TGLViewerBase::FindScene(TGLSceneBase* scene)
{
   // Find scene-info corresponding to scene.

   SceneInfoList_i i = fScenes.begin();
   while (i != fScenes.end() && (*i)->GetScene() != scene) ++i;
   return i;
}

//______________________________________________________________________
TGLSceneInfo* TGLViewerBase::AddScene(TGLSceneBase* scene)
{
   // Add new scene, appropriate scene-info is created.

   SceneInfoList_i i = FindScene(scene);
   if (i == fScenes.end()) {
      TGLSceneInfo* sinfo = scene->CreateSceneInfo(this);
      fScenes.push_back(sinfo);
      scene->AddViewer(this);
      Changed();
      return sinfo;
   } else {
      Warning("TGLViewerBase::AddScene", "scene '%s' already in the list.",
              scene->GetName());
      return 0;
   }
}

//______________________________________________________________________
void TGLViewerBase::RemoveScene(TGLSceneBase* scene)
{
   // Remove scene from the viewer, its scene-info is deleted.

   SceneInfoList_i i = FindScene(scene);
   if (i != fScenes.end()) {
      delete *i;
      fScenes.erase(i);
      scene->RemoveViewer(this);
      Changed();
   } else {
      Warning("TGLViewerBase::RemoveScene", "scene '%s' not found.",
              scene->GetName());
   }
}

//______________________________________________________________________
void TGLViewerBase::RemoveAllScenes()
{
   // Remove all scenes from the viewer, their scene-infos are deleted.

   for (SceneInfoList_i i=fScenes.begin(); i!=fScenes.end(); ++i)
   {
      TGLSceneInfo * sinfo = *i;
      sinfo->GetScene()->RemoveViewer(this);
      delete sinfo;
   }
   fScenes.clear();
   Changed();
}

//______________________________________________________________________
void TGLViewerBase::SceneDestructing(TGLSceneBase* scene)
{
   // Remove scene, its scene-info is deleted.
   // Called from scene that is being destroyed while still holding
   // viewer references.

   SceneInfoList_i i = FindScene(scene);
   if (i != fScenes.end()) {
      delete *i;
      fScenes.erase(i);
      Changed();
   } else {
      Warning("TGLViewerBase::SceneDestructing", "scene not found.");
   }
}

//______________________________________________________________________
TGLSceneInfo* TGLViewerBase::GetSceneInfo(TGLSceneBase* scene)
{
   // Find scene-info corresponding to scene.

   SceneInfoList_i i = FindScene(scene);
   if (i != fScenes.end())
      return *i;
   else
      return 0;
}

//______________________________________________________________________
void TGLViewerBase::AddOverlayElement(TGLOverlayElement* el)
{
   // Add overlay element.

   fOverlay.push_back(el);
   Changed();
}

//______________________________________________________________________
void TGLViewerBase::RemoveOverlayElement(TGLOverlayElement* el)
{
   // Remove overlay element.
   std::vector<TGLOverlayElement*>::iterator it = std::find(fOverlay.begin(), fOverlay.end(), el);
   if(it != fOverlay.end())
      fOverlay.erase(it);
   Changed();
}

/**************************************************************************/
// SceneInfo update / check
/**************************************************************************/

void TGLViewerBase::ResetSceneInfos()
{
   // Force rebuild of view-dependent scene-info structures.
   //
   // This should be called before calling render (draw/select) if
   // something that affects camera interest has been changed.

   SceneInfoList_i i = fScenes.begin();
   while (i != fScenes.end())
   {
      (*i)->ResetSceneStamp();
      ++i;
   }
}

void TGLViewerBase::MergeSceneBBoxes(TGLBoundingBox& bbox)
{
   // Merge bounding-boxes of all active registered scenes.

   bbox.SetEmpty();
   for (SceneInfoList_i i=fScenes.begin(); i!=fScenes.end(); ++i)
   {
      TGLSceneInfo * sinfo = *i;
      if (sinfo->GetActive())
      {
         sinfo->SetupTransformsAndBBox(); // !!! transform not done yet, no camera
         bbox.MergeAligned(sinfo->GetTransformedBBox());
      }
   }
}

/**************************************************************************/
// Rendering / selection virtuals
/**************************************************************************/

//______________________________________________________________________
void TGLViewerBase::PreRender()
{
   // Initialize render-context, setup camera, GL, render-area.
   // Check and lock scenes, determine their visibility.

   TGLContextIdentity* cid = TGLContextIdentity::GetCurrent();
   if (cid == 0)
   {
      // Assume derived class set it up for us.
      // This happens due to very complex and involved implementation
      // of gl-in-pad that uses gGLManager.
      // In principle we should throw an exception:
      // throw std::runtime_error("Can not resolve GL context.");
   }
   else
   {
      if (cid != fRnrCtx->GetGLCtxIdentity())
      {
         if (fRnrCtx->GetGLCtxIdentity() != 0)
            Warning("TGLViewerBase::PreRender", "Switching to another GL context; maybe you should use context-sharing.");
         fRnrCtx->SetGLCtxIdentity(cid);
      }
   }

   fRnrCtx->SetCamera      (fCamera);
   fRnrCtx->SetViewerLOD   (fLOD);
   fRnrCtx->SetViewerStyle (fStyle);
   fRnrCtx->SetViewerClip  (fClip);

   if (fResetSceneInfosOnRender)
   {
      ResetSceneInfos();
      fResetSceneInfosOnRender = kFALSE;
   }

   fOverallBoundingBox.SetEmpty();
   SceneInfoList_t locked_scenes;
   for (SceneInfoList_i i=fScenes.begin(); i!=fScenes.end(); ++i)
   {
      TGLSceneInfo * sinfo = *i;
      if (sinfo->GetActive())
      {
         if ( ! sinfo->GetScene()->TakeLock(kDrawLock))
         {
            Warning("TGLViewerBase::PreRender", "locking of scene '%s' failed, skipping.",
                    sinfo->GetScene()->GetName());
            continue;
         }
         sinfo->SetupTransformsAndBBox(); // !!! transform not done yet
         fOverallBoundingBox.MergeAligned(sinfo->GetTransformedBBox());
         locked_scenes.push_back(sinfo);
      }
   }

   fCamera->Apply(fOverallBoundingBox, fRnrCtx->GetPickRectangle());

   // Make precursory selection of visible scenes.
   // Only scene bounding-box .vs. camera frustum check performed.
   fVisScenes.clear();
   for (SceneInfoList_i i=locked_scenes.begin(); i!=locked_scenes.end(); ++i)
   {
      TGLSceneInfo         * sinfo = *i;
      const TGLBoundingBox & bbox  = sinfo->GetTransformedBBox();
      Bool_t visp = (!bbox.IsEmpty() && fCamera->FrustumOverlap(bbox) != kOutside);
      sinfo->ViewCheck(visp);
      if (visp) {
         fRnrCtx->SetSceneInfo(sinfo);
         sinfo->GetScene()->PreDraw(*fRnrCtx);
         if (sinfo->IsVisible()) {
            fVisScenes.push_back(sinfo);
         } else {
            sinfo->GetScene()->PostDraw(*fRnrCtx);
            sinfo->GetScene()->ReleaseLock(kDrawLock);
         }
         fRnrCtx->SetSceneInfo(0);
      } else {
         sinfo->GetScene()->ReleaseLock(kDrawLock);
      }
   }
}

//______________________________________________________________________________
void TGLViewerBase::SubRenderScenes(SubRender_foo render_foo)
{
   // Call sub-rendering function render_foo on all currently visible
   // scenes.

   Int_t nScenes = fVisScenes.size();

   for (Int_t i = 0; i < nScenes; ++i)
   {
      TGLSceneInfo* sinfo = fVisScenes[i];
      TGLSceneBase* scene = sinfo->GetScene();
      fRnrCtx->SetSceneInfo(sinfo);
      glPushName(i);
      scene->PreRender(*fRnrCtx);
      (scene->*render_foo)(*fRnrCtx);
      scene->PostRender(*fRnrCtx);
      glPopName();
      fRnrCtx->SetSceneInfo(0);
   }
}

//______________________________________________________________________
void TGLViewerBase::Render()
{
   // Render all scenes. This is done in four passes:
   // - render opaque objects from all scenes
   // - render transparent objects from all scenes
   // - clear depth buffer
   // - render opaque selected objects from all scenes (with highlight)
   // - render transparent selected objects from all scenes (with highlight)

   RenderNonSelected();
   glClear(GL_DEPTH_BUFFER_BIT);
   RenderSelected();
}

//______________________________________________________________________
void TGLViewerBase::RenderNonSelected()
{
   // Render non-selected objects from all scenes.

   SubRenderScenes(&TGLSceneBase::RenderOpaque);

   glDepthMask(GL_FALSE);
   glEnable(GL_BLEND);
   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

   SubRenderScenes(&TGLSceneBase::RenderTransp);

   glDepthMask(GL_TRUE);
   glDisable(GL_BLEND);

   TGLUtil::CheckError("TGLViewerBase::RenderNonSelected - pre exit check");
}

//______________________________________________________________________
void TGLViewerBase::RenderSelected()
{
   // Render selected objects from all scenes.

   SubRenderScenes(&TGLSceneBase::RenderSelOpaque);

   glDepthMask(GL_FALSE);
   glEnable(GL_BLEND);
   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

   SubRenderScenes(&TGLSceneBase::RenderSelTransp);

   glDepthMask(GL_TRUE);
   glDisable(GL_BLEND);

   TGLUtil::CheckError("TGLViewerBase::RenderSelected - pre exit check");
}

//______________________________________________________________________
void TGLViewerBase::RenderOverlay()
{
   // Render overlay objects.

   Int_t nOvl = fOverlay.size();
   for (Int_t i = 0; i < nOvl; ++i)
   {
      TGLOverlayElement* el = fOverlay[i];
      glPushName(i);
      el->Render(*fRnrCtx);
      glPopName();
   }
}

//______________________________________________________________________
void TGLViewerBase::PostRender()
{
   // Function called after rendering is finished.
   // Here we just unlock the scenes.

   for (SceneInfoVec_i i = fVisScenes.begin(); i != fVisScenes.end(); ++i)
   {
      TGLSceneInfo* sinfo = *i;
      fRnrCtx->SetSceneInfo(sinfo);
      sinfo->GetScene()->PostDraw(*fRnrCtx);
      fRnrCtx->SetSceneInfo(0);
      sinfo->GetScene()->ReleaseLock(kDrawLock);
   }
   fChanged = kFALSE;
}

//______________________________________________________________________
void TGLViewerBase::PreRenderOverlaySelection()
{
   // Perform minimal initialization for overlay selection.
   // Here we assume that scene has already been drawn and that
   // camera and overall bounding box are ok.
   // Scenes are not locked.

   fCamera->Apply(fOverallBoundingBox, fRnrCtx->GetPickRectangle());
}

//______________________________________________________________________
void TGLViewerBase::PostRenderOverlaySelection()
{
   // Perform cleanup after overlay selection.

}

/**************************************************************************/
// High-level functions: drawing and picking.
/**************************************************************************/


//______________________________________________________________________
//void TGLViewerBase::Select(Int_t selX, Int_t selY, Int_t selRadius)
//{
   // Perform render-pass in selection mode.
   // Process the selection results.
   // For now only in derived classes.
//}

//______________________________________________________________________
Bool_t TGLViewerBase::ResolveSelectRecord(TGLSelectRecord& rec, Int_t recIdx)
{
   // Process selection record on buffer-position 'recIdx' and
   // fill the data into 'rec'.
   //
   // Returns TRUE if scene was demangled and an object identified.
   // When FALSE is returned it is still possible that scene has been
   // identified. Check for this if interested in scene-selection.
   //
   // The select-buffer is taken form fRnrCtx.

   TGLSelectBuffer* sb = fRnrCtx->GetSelectBuffer();
   if (recIdx >= sb->GetNRecords())
       return kFALSE;

   sb->SelectRecord(rec, recIdx);
   UInt_t sceneIdx = rec.GetItem(0);
   if (sceneIdx >= fVisScenes.size())
       return kFALSE;

   TGLSceneInfo* sinfo = fVisScenes[sceneIdx];
   rec.SetSceneInfo(sinfo);
   return sinfo->GetScene()->ResolveSelectRecord(rec, 1);
}

//______________________________________________________________________
Bool_t TGLViewerBase::FindClosestRecord(TGLSelectRecord& rec, Int_t& recIdx)
{
   // Find next select record that can be resolved, starting from
   // position 'recIdx'.
   // 'recIdx' is passed as reference and points to found record in the buffer.

   TGLSelectBuffer* sb = fRnrCtx->GetSelectBuffer();

   while (recIdx < sb->GetNRecords())
   {
      if (ResolveSelectRecord(rec, recIdx))
         return kTRUE;
      ++recIdx;
   }
   return kFALSE;
}

//______________________________________________________________________
Bool_t TGLViewerBase::FindClosestOpaqueRecord(TGLSelectRecord& rec, Int_t& recIdx)
{
   // Find next select record that can be resolved and whose result is
   // not transparent, starting from position 'recIdx'.
   // 'recIdx' is passed as reference and points to found record in the buffer.

   TGLSelectBuffer* sb = fRnrCtx->GetSelectBuffer();

   while (recIdx < sb->GetNRecords())
   {
      if (ResolveSelectRecord(rec, recIdx) && ! rec.GetTransparent())
         return kTRUE;
      ++recIdx;
   }
   return kFALSE;
}

//______________________________________________________________________
Bool_t TGLViewerBase::FindClosestOverlayRecord(TGLOvlSelectRecord& rec,
                                               Int_t             & recIdx)
{
   // Find next overlay-select record that can be resolved, starting from
   // position 'recIdx'.
   // 'recIdx' is passed as reference and points to found record in the buffer.

   TGLSelectBuffer* sb = fRnrCtx->GetSelectBuffer();

   while (recIdx < sb->GetNRecords())
   {
      sb->SelectRecord(rec, recIdx);
      if (rec.GetItem(0) < fOverlay.size())
      {
         rec.SetOvlElement(fOverlay[rec.GetItem(0)]);
         rec.NextPos();
         return kTRUE;
      }
      ++recIdx;
   }
   return kFALSE;
}

Last change: Wed Jun 25 08:41:34 2008
Last generated: 2008-06-25 08:41

This page has been automatically generated. If you have any comments or suggestions about the page layout send a mail to ROOT support, or contact the developers with any questions or problems regarding ROOT.