// @(#)root/gl:$Name:  $:$Id: TGLViewer.cxx,v 1.31 2005/12/12 15:28:32 brun Exp $
// Author:  Richard Maunder  25/05/2005

/*************************************************************************
 * Copyright (C) 1995-2000, 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 "TGLViewer.h"
#include "TGLIncludes.h"
#include "TGLStopwatch.h"
#include "TGLDisplayListCache.h"

#include "TGLLogicalShape.h"
#include "TGLPhysicalShape.h"
#include "TGLStopwatch.h"
#include "TGLSceneObject.h" // For TGLFaceSet
#include "TGLClip.h"
#include "TGLTransManip.h"
#include "TGLScaleManip.h"
#include "TGLRotateManip.h"

#include "TBuffer3D.h"
#include "TBuffer3DTypes.h"

#include "TVirtualPad.h" // Remove when pad removed - use signal

#include "TColor.h"
#include "TError.h"

// For event type translation ExecuteEvent
#include "Buttons.h"
#include "GuiTypes.h"

// Remove - replace with TGLManager
#include "TVirtualGL.h"
#include "TGLRenderArea.h"

#include "KeySymbols.h"
#include "TContextMenu.h"

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TGLViewer                                                            //
//                                                                      //
// Base GL viewer object - used by both standalone and embedded (in pad)//
// GL. Contains core viewer objects :                                   //
//                                                                      //
// GL scene (fScene) - collection of main drawn objects - see TGLScene  //
// Cameras (fXXXXCamera) - ortho and perspective cameras - see TGLCamera//
// Clipping (fClipXXXX) - collection of clip objects - see TGLClip      //
// Manipulators (fXXXXManip) - collection of manipulators - see TGLManip//
//                                                                      //
// It maintains the current active draw styles, clipping object,        //
// manipulator, camera etc.                                             //
//                                                                      //
// TGLViewer is 'GUI free' in that it does not derive from any ROOT GUI //
// TGFrame etc - see TGLSAViewer for this. However it contains GUI      //
// GUI style methods HandleButton() etc to which GUI events can be      //
// directed from standalone frame or embedding pad to perform           //
// interaction.                                                         //
//                                                                      //
// For embedded (pad) GL this viewer is created directly by plugin      //
// manager. For standalone the derived TGLSAViewer is.                  //
//////////////////////////////////////////////////////////////////////////
    
ClassImp(TGLViewer)
    
//______________________________________________________________________________
TGLViewer::TGLViewer(TVirtualPad * pad, Int_t x, Int_t y, 
                     UInt_t width, UInt_t height) :
   fPad(pad),
   fContextMenu(0),
   fPerspectiveCameraXOZ(TGLVector3(1.0, 0.0, 0.0), TGLVector3(0.0, 1.0, 0.0)), // XOZ floor
   fPerspectiveCameraYOZ(TGLVector3(0.0, 1.0, 0.0), TGLVector3(1.0, 0.0, 0.0)), // YOZ floor
   fPerspectiveCameraXOY(TGLVector3(1.0, 0.0, 0.0), TGLVector3(0.0, 0.0,-1.0)), // XOY floor
   fOrthoXOYCamera(TGLOrthoCamera::kXOY),
   fOrthoXOZCamera(TGLOrthoCamera::kXOZ),
   fOrthoZOYCamera(TGLOrthoCamera::kZOY),
   fCurrentCamera(&fPerspectiveCameraXOZ),
   fInternalRebuild(kFALSE), 
   fPostSceneBuildSetup(kTRUE),
   fAcceptedAllPhysicals(kTRUE),
   fForceAcceptAll(kFALSE),
   fInternalPIDs(kFALSE), 
   fNextInternalPID(1), // 0 reserved
   fComposite(0), fCSLevel(0),
   fAction(kNone), fStartPos(0,0), fLastPos(0,0), fActiveButtonID(0),
   fDrawStyle(kFill),
   fRedrawTimer(0),
   fNextSceneLOD(kHigh),
   fLightState(kLightMask), // All on
   fAxesType(kAxesNone),
   fReferencePos(0.0, 0.0, 0.0),
   fInitGL(kFALSE),
   fClipPlane(0), fClipBox(0), fCurrentClip(0), fClipEdit(kFALSE),
   fDebugMode(kFALSE),
   fAcceptedPhysicals(0), 
   fRejectedPhysicals(0),
   fIsPrinting(kFALSE),
   fGLWindow(0)
{
   // Construct the viewer object, with following arguments:
   //    'pad' - external pad viewer is bound to
   //    'x', 'y' - initial top left position
   //    'width', 'height' - initial width/height
   // Create timer
   fRedrawTimer = new TGLRedrawTimer(*this);

   // Create manipulators - not bound to any shape
   fTransManip = new TGLTransManip(*this, 0);
   fScaleManip = new TGLScaleManip(*this, 0);
   fRotateManip = new TGLRotateManip(*this, 0);
   fCurrentManip = fTransManip;

   SetViewport(x, y, width, height);
}

//______________________________________________________________________________
 TGLViewer::~TGLViewer()
{
   // Destroy viewer object
   delete fContextMenu;
   delete fRedrawTimer;
   delete fTransManip;
   delete fScaleManip;
   delete fRotateManip;

   // Delete clip objects
   ClearClips();

   fPad->ReleaseViewer3D();   
}

//______________________________________________________________________________
 Bool_t TGLViewer::PreferLocalFrame() const
{
   // Indicate if viewer prefers to receive logical shape descriptions
   // in local (kTRUE) or world frame (kFALSE). For GL viewer is kTRUE always
   // TVirtualViewer3D interface overload - see base/src/TVirtualViewer3D.cxx
   // for description of viewer architecture
   return kTRUE;
}

//______________________________________________________________________________
 void TGLViewer::BeginScene()
{
   // Start building of viewer scene
   // TVirtualViewer3D interface overload - see base/src/TVirtualViewer3D.cxx
   // for description of viewer architecture
   if (!fScene.TakeLock(TGLScene::kModifyLock)) {
      return;
   }

   UInt_t destroyedLogicals = 0;
   UInt_t destroyedPhysicals = 0;

   TGLStopwatch stopwatch;
   if (gDebug>2 || fDebugMode) {
      stopwatch.Start();
   }

   // External rebuild?
   if (!fInternalRebuild) 
   {
      // Potentially using external physical IDs
      fInternalPIDs = kFALSE;

      // Reset force acceptance of all
      fForceAcceptAll = kFALSE;

      // Reset camera interest to ensure we respond to
      // new scene range
      CurrentCamera().ResetInterest();

      // External rebuilds could potentially invalidate all logical and
      // physical shapes - including any modified physicals
      // Physicals must be removed first
      destroyedPhysicals = fScene.DestroyPhysicals(kTRUE); // include modified
      destroyedLogicals = fScene.DestroyLogicals();

      // Purge out the DL cache - not required once shapes do this themselves properly
      TGLDisplayListCache::Instance().Purge();
   } else {
      // Internal rebuilds - destroy all non-modified physicals no longer of
      // interest to camera - retain logicals
      destroyedPhysicals = fScene.DestroyPhysicals(kFALSE, &CurrentCamera()); // excluded modified
   }

   // Reset internal physical ID counter
   fNextInternalPID = 1;
   
   // Potentially accepting all physicals from external client
   fAcceptedAllPhysicals = kTRUE;

  // Reset tracing info
   fAcceptedPhysicals = 0;
   fRejectedPhysicals = 0;

   if (gDebug>2 || fDebugMode) {
      Info("TGLViewer::BeginScene", "destroyed %d physicals %d logicals in %f msec", 
            destroyedPhysicals, destroyedLogicals, stopwatch.End());
      fScene.Dump();
   }
}

//______________________________________________________________________________
 void TGLViewer::EndScene()
{
   // End building of viewer scene
   // TVirtualViewer3D interface overload - see base/src/TVirtualViewer3D.cxx
   // for description of viewer architecture
   fScene.ReleaseLock(TGLScene::kModifyLock);

   if (fPostSceneBuildSetup) {
      PostSceneBuildSetup();

      // We leave fPostSceneBuildSetup set true as we want
      // another full setup after first internal rebuild
      // when we have the full scene limits
   }

   // Externally triggered scene rebuild (first pass) completed
   if (!fInternalRebuild) {
      // Request intial draw - will result in an internal (full) rebuild
      // afterwards - when we have camera interest bootstrapped properly
      RequestDraw();
   } else {
      fInternalRebuild = kFALSE;

      // No more setup done after first internal scene rebuild
      fPostSceneBuildSetup = kFALSE;
   }      

   if (gDebug>2 || fDebugMode) {
      Info("TGLViewer::EndScene", "Added %d, rejected %d physicals, accepted all:%s", fAcceptedPhysicals, 
                                       fRejectedPhysicals, fAcceptedAllPhysicals ? "Yes":"No");
      fScene.Dump();
   }
}

//______________________________________________________________________________
 Bool_t TGLViewer::RebuildScene()
{
   // If we accepted all offered physicals into the scene no point in 
   // rebuilding it.
   if (fAcceptedAllPhysicals) {
      // For debug mode always force even if not required
      if (fDebugMode) {
         Info("TGLViewer::RebuildScene", "not required - all physicals previous accepted (FORCED anyway)");
      }
      else {
         if (gDebug>3) {
            Info("TGLViewer::RebuildScene", "not required - all physicals previous accepted");
         }
         return kFALSE;   
      }
   }
   // Update the camera interest (forced in debug mode) - if changed
   // scene should be rebuilt
   if (!CurrentCamera().UpdateInterest(fDebugMode)) {
      if (gDebug>3 || fDebugMode) {
         Info("TGLViewer::RebuildScene", "not required - no camera interest change");
      }
      return kFALSE;
   }
   
   // We are going to rebuild the scene - ensure any pending redraw timer cancelled now
   fRedrawTimer->Stop();

   if (gDebug>3 || fDebugMode) {
      Info("TGLViewer::RebuildScene", "required");
   }

   // Internally triggered scene rebuild
   fInternalRebuild = kTRUE;
   
   TGLStopwatch timer;
   if (gDebug>2 || fDebugMode) {
      timer.Start();
   }

   // Request a scene fill
   // TODO: Just marking modified doesn't seem to result in pad repaint - need to check on
   //fPad->Modified();
   fPad->Paint();

   if (gDebug>2 || fDebugMode) {
      Info("TGLViewer::RebuildScene", "rebuild complete in %f", timer.End());
   }

   // Need to invalidate/redraw via timer as under Win32 we are already inside the 
   // GUI(DoRedraw) thread - direct invalidation will be cleared when leaving
   fRedrawTimer->RequestDraw(20, kMed);

   return kTRUE;
}

//______________________________________________________________________________
 Int_t TGLViewer::AddObject(const TBuffer3D & buffer, Bool_t * addChildren)
{
   // Add an object to the viewer, using internal physical IDs
   // TVirtualViewer3D interface overload - see base/src/TVirtualViewer3D.cxx
   // for description of viewer architecture

   // If this is called we are generating internal physical IDs
   fInternalPIDs = kTRUE;
   Int_t sections = AddObject(fNextInternalPID, buffer, addChildren);   
   return sections;
}

//______________________________________________________________________________
// TODO: Cleanup addChildren to UInt_t flag for full termination - how returned?
 Int_t TGLViewer::AddObject(UInt_t physicalID, const TBuffer3D & buffer, Bool_t * addChildren)
{
   // Add an object to the viewer, using an external physical ID
   // TVirtualViewer3D interface overload - see base/src/TVirtualViewer3D.cxx
   // for description of viewer architecture

   // TODO: Break this up and make easier to understand. This is pretty convoluted
   // due to the large number of cases it has to deal with:
   // i) Exisiting physical and/or logical
   // ii) External provider can supply bounding box or not?
   // iii) Local/global reference frame
   // iv) Defered filling of some sections of the buffer
   // v) Internal or external physical IDs
   // vi) Composite components as special case
   //
   // The buffer filling means the function is re-entrant which adds to complication 

   if (physicalID == 0) {
      Error("TGLViewer::AddObject", "0 physical ID reserved");
      return TBuffer3D::kNone;
   }

   // Internal and external physical IDs cannot be mixed in a scene build
   if (fInternalPIDs && physicalID != fNextInternalPID) {
      Error("TGLViewer::AddObject", "invalid next physical ID - mix of internal + external IDs?");
      return TBuffer3D::kNone;
   }

   // Assume children are always sent initially
   if (addChildren) {
      *addChildren = kTRUE;
   }
   
   // Scene should be modify locked
   if (fScene.CurrentLock() != TGLScene::kModifyLock) {
      Error("TGLViewer::AddObject", "expected scene to be in mofifed locked");
      return TBuffer3D::kNone;
   }
   
   // Note that 'object' here is really a physical/logical pair described
   // in buffer + physical ID.

   // If adding component to a current partial composite do this now
   if (fComposite) {
      RootCsg::BaseMesh *newMesh = RootCsg::ConvertToMesh(buffer);
      // Solaris CC can't create stl pair with enumerate type
      fCSTokens.push_back(std::make_pair(static_cast<UInt_t>(TBuffer3D::kCSNoOp), newMesh));
      return TBuffer3D::kNone;
   }

   // TODO: Could be static and save possible double lookup?
   TGLPhysicalShape * physical = fScene.FindPhysical(physicalID);
   TGLLogicalShape * logical = 0;

   // If we have a valid (non-zero) ID in buffer see if the logical is already cached
   if (buffer.fID) {
      logical = fScene.FindLogical(reinterpret_cast<ULong_t>(buffer.fID));
   } else if (!fForceAcceptAll) {
      // If client is passing zero fID buffers we need to force accepting of all
      // so scene is never rebuilt (we can't detect cached items). Client
      // can't mix objects in scene with external or zero ids
      fForceAcceptAll = kTRUE;
      if (fNextInternalPID > 1) {
         Error("TGLViewer::AddObject", "zero fID objects can't be mixed with non-zero ones");
      }
   }

   // Function can be called twice if extra buffer filling for logical 
   // is required - record last physical ID to detect
   static UInt_t lastPID = 0;

   // First attempt to add this physical 
   if (physicalID != lastPID) {
      // Existing physical
      if (physical) {
         // If we have physical we should have logical cached too
         if (!logical) {
            Error("TGLViewer::AddObject", "cached physical with no assocaited cached logical");
         }

         // For external PIDs we check child interest as we may have reject children previously
         // with a different camera configuration
         if (addChildren && !fInternalPIDs) {
            *addChildren = kTRUE;
         }
         
         // Always increment the internal physical ID so they
         // match external object sequence
         if (fInternalPIDs) {
            fNextInternalPID++;
         }

         // We don't need anything more for this object
         return TBuffer3D::kNone; 
      }
      // New physical 
      else {
         if (!fForceAcceptAll) {
            // First test interest in camera - requires a bounding box
            TGLBoundingBox box;
            
            // If already have logical use it's BB
            if (logical) {
               box = logical->BoundingBox();
            }
            // else if bounding box in buffer valid use this
            else if (buffer.SectionsValid(TBuffer3D::kBoundingBox)) {
               box.Set(buffer.fBBVertex);

            // otherwise we need to use raw points to build a bounding box with
            // If raw sections not set it will be requested by ValidateObjectBuffer
            // below and we will re-enter here
            } else if (buffer.SectionsValid(TBuffer3D::kRaw)) {
               box.SetAligned(buffer.NbPnts(), buffer.fPnts);
            }
         
            // Box is valid?
            if (!box.IsEmpty()) {
               // Test transformed box with camera
               box.Transform(TGLMatrix(buffer.fLocalMaster));
               Bool_t ofInterest = CurrentCamera().OfInterest(box);
                  
               // For external PID request children if physical of interest
               if (addChildren &&!fInternalPIDs) {
                  *addChildren = ofInterest;
               }

               // Physical is of interest? If not record rejection
               if (!ofInterest) {
                  ++fRejectedPhysicals;
                  fAcceptedAllPhysicals = kFALSE;

                  // Always increment the internal physical ID so they
                  // match external object sequence
                  if (fInternalPIDs) {
                     fNextInternalPID++;
                  }
                  return TBuffer3D::kNone;
               } 
            }
         }
      }

      // Need any extra sections in buffer?
      // If we have logical already we don't need to check raw sections
      Int_t extraSections = ValidateObjectBuffer(buffer, 
                                                 logical == 0); // Check raw?
      if (extraSections != TBuffer3D::kNone) {         
         return extraSections;
      } else {
         lastPID = physicalID; // Will not to re-test interest
      }
   }

   if(lastPID != physicalID)
   {
      Error("TGLViewer::AddObject", "internal physical ID tracking error?");
   }
   // By now we should need to add a physical at least
   if (physical) {
      Error("TGLViewer::AddObject", "expecting to require physical");
      return TBuffer3D::kNone; 
   }

   // Create logical if required
   if (!logical) {
      logical = CreateNewLogical(buffer);
      if (!logical) { 
         Error("TGLViewer::AddObject", "failed to create logical");
         return TBuffer3D::kNone;
      }
      // Add logical to scene
      fScene.AdoptLogical(*logical);
   }

   // Finally create the physical, binding it to the logical, and add to scene
   physical = CreateNewPhysical(physicalID, buffer, *logical);

   if (physical) { 
      fScene.AdoptPhysical(*physical);
      ++fAcceptedPhysicals;
      if (gDebug>3 && fAcceptedPhysicals%1000 == 0) {
         Info("TGLViewer::AddObject", "added %d physicals", fAcceptedPhysicals);
      }
   } else {
      Error("TGLViewer::AddObject", "failed to create physical");
   }

   // Always increment the internal physical ID so they
   // match external object sequence
   if (fInternalPIDs) {
      fNextInternalPID++;
   }

   // Reset last physical ID so can detect new one
   lastPID = 0;
   return TBuffer3D::kNone;
}

//______________________________________________________________________________
 Bool_t TGLViewer::OpenComposite(const TBuffer3D & buffer, Bool_t * addChildren)
{
   // Open new composite container.
   // TVirtualViewer3D interface overload - see base/src/TVirtualViewer3D.cxx
   // for description of viewer architecture.
   if (fComposite) {
      Error("TGLViewer::OpenComposite", "composite already open");
      return kFALSE;
   }
   UInt_t extraSections = AddObject(buffer, addChildren);
   if (extraSections != TBuffer3D::kNone) {
      Error("TGLViewer::OpenComposite", "expected top level composite to not require extra buffer sections");
   }
   
   // If composite was created it is of interest - we want the rest of the
   // child components   
   if (fComposite) {
      return kTRUE;
   } else {
      return kFALSE;
   }
}

//______________________________________________________________________________
 void TGLViewer::CloseComposite()
{
   // Close composite container
   // TVirtualViewer3D interface overload - see base/src/TVirtualViewer3D.cxx
   // for description of viewer architecture
   
   // If we have a partially complete composite build it now
   if (fComposite) {
      // TODO: Why is this member and here - only used in BuildComposite()
      fCSLevel = 0;

      RootCsg::BaseMesh *resultMesh = BuildComposite();
      fComposite->SetFromMesh(resultMesh);
      delete resultMesh;
      for (UInt_t i = 0; i < fCSTokens.size(); ++i) delete fCSTokens[i].second;
      fCSTokens.clear();
      fComposite = 0;
   }
}

//______________________________________________________________________________
 void TGLViewer::AddCompositeOp(UInt_t operation)
{
   // Add composite operation used to combine objects added via AddObject
   // TVirtualViewer3D interface overload - see base/src/TVirtualViewer3D.cxx
   // for description of viewer architecture
   
   fCSTokens.push_back(std::make_pair(operation, (RootCsg::BaseMesh *)0));
}

//______________________________________________________________________________
 Int_t TGLViewer::ValidateObjectBuffer(const TBuffer3D & buffer, Bool_t includeRaw) const
{
   // Validate if the passed 'buffer' contains all sections we require to add object. 
   // Returns Int_t combination of TBuffer::ESection flags still required - or
   // TBuffer3D::kNone if buffer is valid. 
   // If 'includeRaw' is kTRUE check for kRaw/kRawSizes - skip otherwise.
   // See base/src/TVirtualViewer3D.cxx for description of viewer architecture
   
   // kCore: Should always be filled
   if (!buffer.SectionsValid(TBuffer3D::kCore)) {
      Error("TGLViewer::ValidateObjectBuffer", "kCore section of buffer should be filled always");
      return TBuffer3D::kNone;
   }

   // Need to check raw (kRaw/kRawSizes)?
   if (!includeRaw) {
      return TBuffer3D::kNone;
   }

   // kRawSizes / kRaw: These are on demand based on shape type
   Bool_t needRaw = kFALSE;

   // We need raw tesselation in these cases:
   //
   // 1. Shape type is NOT kSphere / kTube / kTubeSeg / kCutTube / kComposite
   if (buffer.Type() != TBuffer3DTypes::kSphere  &&
       buffer.Type() != TBuffer3DTypes::kTube    &&
       buffer.Type() != TBuffer3DTypes::kTubeSeg &&
       buffer.Type() != TBuffer3DTypes::kCutTube && 
       buffer.Type() != TBuffer3DTypes::kComposite) {
      needRaw = kTRUE;
   }
   // 2. Sphere type is kSPHE, but the sphere is hollow and/or cut - we
   //    do not support native drawing of these currently
   else if (buffer.Type() == TBuffer3DTypes::kSphere) {
      const TBuffer3DSphere * sphereBuffer = dynamic_cast<const TBuffer3DSphere *>(&buffer);
      if (sphereBuffer) {
         if (!sphereBuffer->IsSolidUncut()) {
            needRaw = kTRUE;
         }
      } else {
         Error("TGLViewer::ValidateObjectBuffer", "failed to cast buffer of type 'kSphere' to TBuffer3DSphere");
         return TBuffer3D::kNone;
      }
   }
   // 3. kBoundingBox is not filled - we generate a bounding box from 
   else if (!buffer.SectionsValid(TBuffer3D::kBoundingBox)) {
      needRaw = kTRUE;
   }
   // 3. kShapeSpecific is not filled - except in case of top level composite 
   else if (!buffer.SectionsValid(TBuffer3D::kShapeSpecific) && 
             buffer.Type() != TBuffer3DTypes::kComposite) {
      needRaw = kTRUE;
   }
   // 5. We are a component (not the top level) of a composite shape
   else if (fComposite) {
      needRaw = kTRUE;
   }

   if (needRaw && !buffer.SectionsValid(TBuffer3D::kRawSizes|TBuffer3D::kRaw)) {
      return TBuffer3D::kRawSizes|TBuffer3D::kRaw;
   } else {
      return TBuffer3D::kNone;
   }
}

//______________________________________________________________________________
 TGLLogicalShape * TGLViewer::CreateNewLogical(const TBuffer3D & buffer) const
{
   // Create and return a new TGLLogicalShape from the supplied buffer
   TGLLogicalShape * newLogical = 0;

   switch (buffer.Type()) {
   case TBuffer3DTypes::kLine:
      newLogical = new TGLPolyLine(buffer, buffer.fID);
      break;
   case TBuffer3DTypes::kMarker:
      newLogical = new TGLPolyMarker(buffer, buffer.fID);
      break;
   case TBuffer3DTypes::kSphere: {
      const TBuffer3DSphere * sphereBuffer = dynamic_cast<const TBuffer3DSphere *>(&buffer);
      if (sphereBuffer) {
         // We can only draw solid uncut spheres natively at present
         if (sphereBuffer->IsSolidUncut()) {
            newLogical = new TGLSphere(*sphereBuffer, sphereBuffer->fID);
         } else {
            newLogical = new TGLFaceSet(buffer, buffer.fID);
         }
      }
      else {
         Error("TGLViewer::CreateNewLogical", "failed to cast buffer of type 'kSphere' to TBuffer3DSphere");
      }
      break;
   }
   case TBuffer3DTypes::kTube:
   case TBuffer3DTypes::kTubeSeg:
   case TBuffer3DTypes::kCutTube: {
      const TBuffer3DTube * tubeBuffer = dynamic_cast<const TBuffer3DTube *>(&buffer);
      if (tubeBuffer)
      {
         newLogical = new TGLCylinder(*tubeBuffer, tubeBuffer->fID);
      }
      else {
         Error("TGLViewer::CreateNewLogical", "failed to cast buffer of type 'kTube/kTubeSeg/kCutTube' to TBuffer3DTube");
      }
      break;
   }
   case TBuffer3DTypes::kComposite: {
      // Create empty faceset and record partial complete composite object
      // Will be populated with mesh in CloseComposite()
      if (fComposite) {
         Error("TGLViewer::CreateNewLogical", "composite already open");
      }
      fComposite = new TGLFaceSet(buffer, buffer.fID);
      newLogical = fComposite;
      break;
   }
   default:
      newLogical = new TGLFaceSet(buffer, buffer.fID);
      break;
   }

   return newLogical;
}

//______________________________________________________________________________
 TGLPhysicalShape * TGLViewer::CreateNewPhysical(UInt_t ID, 
                                                    const TBuffer3D & buffer, 
                                                    const TGLLogicalShape & logical) const
{
   // Create and return a new TGLPhysicalShape with id 'ID', using 'buffer' placement
   // information (translation etc), and bound to suppled 'logical'
   
   // Extract indexed color from buffer
   // TODO: Still required? Better use proper color triplet in buffer?
   Int_t colorIndex = buffer.fColor;
   if (colorIndex <= 1) colorIndex = 42; //temporary
   Float_t rgba[4] = { 0.0 };
   TColor *rcol = gROOT->GetColor(colorIndex);

   if (rcol) {
      rcol->GetRGB(rgba[0], rgba[1], rgba[2]);
   }
   
   // Extract transparency component - convert to opacity (alpha)
   rgba[3] = 1.f - buffer.fTransparency / 100.f;

   TGLPhysicalShape * newPhysical = new TGLPhysicalShape(ID, logical, buffer.fLocalMaster, 
                                                         buffer.fReflection, rgba);
   return newPhysical;
}

//______________________________________________________________________________
 RootCsg::BaseMesh *TGLViewer::BuildComposite()
{
   // Build and return composite shape mesh
   const CSPart_t &currToken = fCSTokens[fCSLevel];
   UInt_t opCode = currToken.first;

   if (opCode != TBuffer3D::kCSNoOp) {
      ++fCSLevel;
      RootCsg::BaseMesh *left = BuildComposite();
      RootCsg::BaseMesh *right = BuildComposite();
      //RootCsg::BaseMesh *result = 0;
      switch (opCode) {
      case TBuffer3D::kCSUnion:
         return RootCsg::BuildUnion(left, right);
      case TBuffer3D::kCSIntersection:
         return RootCsg::BuildIntersection(left, right);
      case TBuffer3D::kCSDifference:
         return RootCsg::BuildDifference(left, right);
      default:
         Error("BuildComposite", "Wrong operation code %d\n", opCode);
         return 0;
      }
   } else return fCSTokens[fCSLevel++].second;
}

//______________________________________________________________________________
 void TGLViewer::InitGL()
{
   // Initialise GL state if not already done
   if (fInitGL) {
      Error("TGLViewer::InitGL", "GL already initialised");
   }

   // GL initialisation 
   glEnable(GL_LIGHTING);
   glEnable(GL_DEPTH_TEST);
   glEnable(GL_BLEND);
   glEnable(GL_CULL_FACE);
   glCullFace(GL_BACK);
   glClearColor(0.0, 0.0, 0.0, 0.0);
   glClearDepth(1.0);

   glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);
   Float_t lmodelAmb[] = {0.5f, 0.5f, 1.f, 1.f};
   glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodelAmb);
   
   TGLUtil::CheckError();
   fInitGL = kTRUE;
}

//______________________________________________________________________________
 void TGLViewer::PostSceneBuildSetup()
{
   // Perform post scene (re)build setup
   SetupCameras();
   SetupClips();

   // Set default reference to scene center
   fReferencePos.Set(fScene.BoundingBox().Center());
}

//______________________________________________________________________________
 void TGLViewer::SetupCameras()
{
   // Setup cameras for current scene bounding box
   if (fScene.IsLocked()) {
      Error("TGLViewer::SetupCameras", "expected kUnlocked, found %s", TGLScene::LockName(fScene.CurrentLock()));
      return;
   }

   // Setup cameras if scene box is not empty
   const TGLBoundingBox & box =  fScene.BoundingBox();
   if (!box.IsEmpty()) {
      fPerspectiveCameraYOZ.Setup(box);
      fPerspectiveCameraXOZ.Setup(box);
      fPerspectiveCameraXOY.Setup(box);
      fOrthoXOYCamera.Setup(box);
      fOrthoXOZCamera.Setup(box);
      fOrthoZOYCamera.Setup(box);
   }
}

//______________________________________________________________________________
 void TGLViewer::SetupLights()
{
   // Setup lights for current scene bounding box

   // Locate static light source positions
   const TGLBoundingBox & box = fScene.BoundingBox();
   if (!box.IsEmpty()) {
      // Calculate a sphere radius to arrange lights round
      Double_t lightRadius = box.Extents().Mag() * 2.9;
      Double_t sideLightsZ, frontLightZ;
      
      // Find Z depth (in eye coords) for front and side lights
      // Has to be handlded differently due to ortho camera infinite
      // viewpoint. TODO: Move into camera classes?
      TGLOrthoCamera * orthoCamera = dynamic_cast<TGLOrthoCamera *>(fCurrentCamera);
      if (orthoCamera) {
         // Find distance from near clip plane to furstum center - i.e. vector of half
         // clip depth. Ortho lights placed this distance from eye point
         sideLightsZ =
            fCurrentCamera->FrustumPlane(TGLCamera::kNear).DistanceTo(fCurrentCamera->FrustumCenter())*0.7;
         frontLightZ = sideLightsZ;
      } else {
         // Perspective camera

         // Extract vector from camera eye point to center
         // Camera must have been applied already
         TGLVector3 eyeVector = fCurrentCamera->EyePoint() - fCurrentCamera->FrustumCenter();

         // Pull forward slightly (0.85) to avoid to sharp a cutoff
         sideLightsZ = eyeVector.Mag() * -0.85;
         frontLightZ = 0.0;
      }

      // Reset the modelview so static lights are placed in fixed eye space
      // This will destroy camera application - so we re-apply it below
      glMatrixMode(GL_MODELVIEW);
      glLoadIdentity();

      // 0: Front
      // 1: Top   
      // 2: Bottom
      // 3: Left
      // 4: Right
      TGLVertex3 center = box.Center();
      Float_t pos0[] = { center.X() 				 , center.Y()					, frontLightZ, 1.0};
      Float_t pos1[] = { center.X() 				 , center.Y() + lightRadius, sideLightsZ, 1.0};
      Float_t pos2[] = { center.X() 				 , center.Y() - lightRadius, sideLightsZ, 1.0};
      Float_t pos3[] = { center.X() - lightRadius, center.Y()					, sideLightsZ, 1.0};
      Float_t pos4[] = { center.X() + lightRadius, center.Y()					, sideLightsZ, 1.0};

      Float_t frontLightColor[] = {0.35, 0.35, 0.35, 1.0};
      Float_t sideLightColor[] = {0.7, 0.7, 0.7, 1.0};
      glLightfv(GL_LIGHT0, GL_POSITION, pos0);
      glLightfv(GL_LIGHT0, GL_DIFFUSE, frontLightColor);
      glLightfv(GL_LIGHT1, GL_POSITION, pos1);
      glLightfv(GL_LIGHT1, GL_DIFFUSE, sideLightColor);
      glLightfv(GL_LIGHT2, GL_POSITION, pos2);
      glLightfv(GL_LIGHT2, GL_DIFFUSE, sideLightColor);
      glLightfv(GL_LIGHT3, GL_POSITION, pos3);
      glLightfv(GL_LIGHT3, GL_DIFFUSE, sideLightColor);
      glLightfv(GL_LIGHT4, GL_POSITION, pos4);
      glLightfv(GL_LIGHT4, GL_DIFFUSE, sideLightColor);
   }

   // Set light states everytime - must be defered until now when we know we
   // are in the correct thread for GL context
   // TODO: Could detect state change and only adjust if a change
   for (UInt_t light = 0; (1<<light) < kLightMask; light++) {
      if ((1<<light) & fLightState) {
         glEnable(GLenum(GL_LIGHT0 + light));

         // Debug mode - show active lights in yellow
         if (fDebugMode) {

            // Lighting itself needs to be disable so a single one can show...!
            glDisable(GL_LIGHTING);
            Float_t yellow[4] = { 1.0, 1.0, 0.0, 1.0 };
            Float_t position[4]; // Only float parameters for lights (no double)....
            glGetLightfv(GLenum(GL_LIGHT0 + light), GL_POSITION, position);
            Double_t size = fScene.BoundingBox().Extents().Mag() / 10.0;
            TGLVertex3 dPosition(position[0], position[1], position[2]);
            TGLUtil::DrawSphere(dPosition, size, yellow);
            glEnable(GL_LIGHTING);
         }
      } else {
         glDisable(GLenum(GL_LIGHT0 + light));
      }
   }

   // Restore camera which was applied before we were called, and is disturbed 
   // by static light positioning above. 
   fCurrentCamera->Apply(fScene.BoundingBox());
}

//______________________________________________________________________________
 void TGLViewer::SetupClips() 
{
   // Setup clipping objects for current scene bounding box
   
   // Clear out any previous clips
   ClearClips();

   const TGLBoundingBox & sceneBox = fScene.BoundingBox();
   fClipPlane = new TGLClipPlane(TGLPlane(0.0, -1.0, 0.0, 0.0), 
                                 sceneBox.Center(), 
                                 sceneBox.Extents().Mag()*5.0);

   TGLVector3 halfLengths = sceneBox.Extents() * 0.2501;
   TGLVertex3 center = sceneBox.Center() + halfLengths;
   fClipBox = new TGLClipBox(halfLengths, center);
}

//______________________________________________________________________________
 void TGLViewer::ClearClips()
{
   // Clear out exising clipping objects
   delete fClipPlane;
   delete fClipBox;
   fCurrentClip = 0;
}

//______________________________________________________________________________
 void TGLViewer::RequestDraw(UInt_t LOD)
{
   // Post request for redraw of viewer at level of detail 'LOD'
   // Request is directed via cross thread gVirtualGL object
   fNextSceneLOD = LOD;
   fRedrawTimer->Stop();
   
   // Ignore request if GL window or context not yet availible - we
   // will get redraw later
   if (!fGLWindow || !gVirtualGL) {
      return;
   }

   // Take scene draw lock - to be revisited
   if (!fScene.TakeLock(TGLScene::kDrawLock)) {
      // If taking drawlock fails the previous draw is still in progress
      // set timer to do this one later
      if (gDebug>3) {
         Info("TGLViewer::RequestDraw", "scene drawlocked - requesting another draw");
      }
      fRedrawTimer->RequestDraw(100, fNextSceneLOD);
      return;
   }
   
   gVirtualGL->DrawViewer(this);
}

//______________________________________________________________________________
 void TGLViewer::DoDraw()
{
   // Draw out the the current viewer/scene

   // Locking mainly for Win32 mutli thread safety - but no harm in all using it
   // During normal draws a draw lock is taken in other thread (Win32) in RequestDraw()
   // to ensure thread safety. For PrintObjects repeated Draw() calls are made.
   // If no draw lock taken get one now
   if (fScene.CurrentLock() != TGLScene::kDrawLock) {
      if (!fScene.TakeLock(TGLScene::kDrawLock)) {
         Error("TGLViewer::DoDraw", "scene is %s", TGLScene::LockName(fScene.CurrentLock()));
      }
   }

   fRedrawTimer->Stop();

   TGLStopwatch timer;
   if (gDebug>2) {
      timer.Start();
   }

   // GL pre draw setup
   if (!fIsPrinting) PreDraw();

   // Apply current camera projection - always do this even if scene is empty and we don't draw, 
   // as scene will likely be rebuilt, requiring camera interest and caching needs to be established
   fCurrentCamera->Apply(fScene.BoundingBox());

   // Something to draw?
   if (!fScene.BoundingBox().IsEmpty()) {
      // Setup total scene draw time 
      // Unlimted for high quality draws, 100 msec otherwise
      Double_t sceneDrawTime = fNextSceneLOD == kHigh ? 0.0 : 100.0;

      // Setup lighting
      SetupLights();

      // Draw the scene with clip object
      fScene.Draw(*fCurrentCamera, fDrawStyle, fNextSceneLOD, sceneDrawTime, fCurrentClip);

      // Draw guides (unclipped)
      fScene.DrawGuides(*fCurrentCamera, fAxesType, fReferenceOn ? &fReferencePos:0);

      // Draw edited clip object - TODO remove - the clip object should become 
      // the select object and will get drawn that way.
      if (fCurrentClip && fClipEdit) {
         fCurrentClip->Draw(fNextSceneLOD);
      }

      // Draw current manipulator - move to scene - only if selection?
      fCurrentManip->Draw(*fCurrentCamera);

      // Debug mode - draw some extra details
      if (fDebugMode) {
         glDisable(GL_LIGHTING);
         CurrentCamera().DrawDebugAids();

         // TODO: Scene + origin should probably be availible through GUI
         // along with axes - look when reworking
         // Green scene bounding box
         glColor3d(0.0, 1.0, 0.0);
         fScene.BoundingBox().Draw();

         // Scene bounding box center sphere (green) and 
         glDisable(GL_DEPTH_TEST);
         Double_t size = fScene.BoundingBox().Extents().Mag() / 200.0;
         static Float_t white[4] = {1.0, 1.0, 1.0, 1.0};
         TGLUtil::DrawSphere(TGLVertex3(0.0, 0.0, 0.0), size, white);
         static Float_t green[4] = {0.0, 1.0, 0.0, 1.0};
         const TGLVertex3 & center = fScene.BoundingBox().Center();
         TGLUtil::DrawSphere(center, size, green);
         glEnable(GL_DEPTH_TEST);

         glEnable(GL_LIGHTING);
         }
   }

   PostDraw();

   if (gDebug>2) {
      Info("TGLViewer::DoDraw()", "Took %f msec", timer.End());
      if (gDebug>3) {
         TGLDisplayListCache::Instance().Dump();
      }
   }

   // Release draw lock on scene
   fScene.ReleaseLock(TGLScene::kDrawLock);

   Bool_t redrawReq = kFALSE;

   // Debug mode have forced rebuilds only
   if (!fDebugMode) {
      // Final draw pass
      if (fNextSceneLOD == kHigh) {
         RebuildScene();
      } else {
         // Final draw pass required
         redrawReq = kTRUE;
      }
   } else {
      // Final draw pass required?
      redrawReq = fNextSceneLOD != kHigh;
   }

   // Request final pass high quality redraw via timer
   if (redrawReq) {
      fRedrawTimer->RequestDraw(100, kHigh);
   }
}

//______________________________________________________________________________
 void TGLViewer::PreDraw()
{
   // Perform GL work which must be done before each draw of scene
   MakeCurrent();

   // Initialise GL if not done
   if (!fInitGL) {
      InitGL();
   }

   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

   TGLUtil::CheckError();
}

//______________________________________________________________________________
 void TGLViewer::PostDraw()
{
   // Perform GL work which must be done after each draw of scene
   SwapBuffers();

   // Flush everything in case picking starts
   glFlush();

   TGLUtil::CheckError();
}

//______________________________________________________________________________
 void TGLViewer::MakeCurrent() const
{
   // Make GL context current
   fGLWindow->MakeCurrent();
   TGLUtil::CheckError();
}

//______________________________________________________________________________
 void TGLViewer::SwapBuffers() const
{
   // SWap GL buffers
   if (fScene.CurrentLock() != TGLScene::kDrawLock && 
      fScene.CurrentLock() != TGLScene::kSelectLock) {
      Error("TGLViewer::SwapBuffers", "scene is %s", TGLScene::LockName(fScene.CurrentLock()));   
   }
   fGLWindow->SwapBuffers();
}

//______________________________________________________________________________
 void TGLViewer::RequestSelect(UInt_t x, UInt_t y)
{
   // Post request for select draw of viewer, picking objects round the WINDOW
   // point (x,y).
   // Request is directed via cross thread gVirtualGL object
   
   // Take select lock on scene immediately we enter here - it is released
   // in the other (drawing) thread - see TGLViewer::Select()
   // Removed when gVirtualGL removed
   if (!fScene.TakeLock(TGLScene::kSelectLock)) {
      return;
   }

   // TODO: Check only the GUI thread ever enters here & DoSelect.
   // Then TVirtualGL and TGLKernel can be obsoleted.
   TGLRect selectRect(x, y, 3, 3); // TODO: Constant somewhere
   gVirtualGL->SelectViewer(this, &selectRect); 
}

//______________________________________________________________________________
 Bool_t TGLViewer::DoSelect(const TGLRect & rect)
{
   // Perform GL selection, picking objects overlapping WINDOW
   // area described by 'rect'. Return kTRUE if selection 
   // changed, kFALSE otherwise. Selection can be obtained
   // via fScene.GetSelected()
   // Select lock should already been taken in other thread in 
   // TGLViewer::DoSelect()
   if (fScene.CurrentLock() != TGLScene::kSelectLock) {
      Error("TGLViewer::Draw", "expected kSelectLock, found %s", TGLScene::LockName(fScene.CurrentLock()));
      return kFALSE;
   }

   MakeCurrent();

   TGLRect glRect(rect);
   fCurrentCamera->WindowToViewport(glRect);
   fCurrentCamera->Apply(fScene.BoundingBox(), &glRect);

   // Ask scene to do selection - this will result in a draw pass
   // Note we do not call DoDraw() here as there is lighting setup which 
   // will distrurb the camera picking rect - and are not needed for selection
   Bool_t changed = fScene.Select(*fCurrentCamera, fDrawStyle, fCurrentClip);

   // Release select lock on scene before invalidation
   fScene.ReleaseLock(TGLScene::kSelectLock);

   if (changed) {
      RequestDraw(kHigh);

      // Inform external client selection has been modified
      SelectionChanged();
   }

   return changed;
}

//______________________________________________________________________________
 void TGLViewer::RequestSelectManip(const TGLRect & rect)
{
   // Post request for select draw of current manipulator , picking objects round 
   // the WINDOW area described by 'rect'
   gVirtualGL->SelectViewerManip(this, &rect); 
}

//______________________________________________________________________________
 void TGLViewer::DoSelectManip(const TGLRect & rect)
{
   // Perform GL selection, picking objects overlapping WINDOW
   // area described by 'rect'
   MakeCurrent();
   TGLRect glRect(rect);
   fCurrentCamera->WindowToViewport(glRect);
   fCurrentCamera->Apply(fScene.BoundingBox(), &glRect);
   fCurrentManip->Select(*fCurrentCamera);   
}

//______________________________________________________________________________
 void TGLViewer::SetViewport(Int_t x, Int_t y, UInt_t width, UInt_t height)
{
   // Set viewer viewport (window area) with bottom/left at (x,y), with
   // dimensions 'width'/'height' 
   if (fScene.IsLocked()) {
      Error("TGLViewer::SetViewport", "expected kUnlocked, found %s", TGLScene::LockName(fScene.CurrentLock()));
      return;
   }
   // Only process if changed
   if (fViewport.X() == x && fViewport.Y() == y &&
       fViewport.Width() == width && fViewport.Height() == height) {
      return;
   }
       
   fViewport.Set(x, y, width, height);
   fCurrentCamera->SetViewport(fViewport);
   
   // Request redraw via timer as window resize can result in stream of calls
   fRedrawTimer->RequestDraw(20, kMed);
   if (gDebug>2) {
      Info("TGLViewer::SetViewport", "updated - corner %d,%d dimensions %d,%d", x, y, width, height);          
   }
}

//______________________________________________________________________________
 void TGLViewer::SetDrawStyle(EDrawStyle drawStyle)
{
   // Set the draw style - one of kFill, kWireframe, kOutline
   fDrawStyle = drawStyle;
   RequestDraw();
}

//______________________________________________________________________________
 void TGLViewer::SetCurrentCamera(ECameraType cameraType)
{
   // Set current active camera - 'cameraType' one of:
   // kCameraPerspX, kCameraPerspY, kCameraPerspZ
   // kCameraOrthoXOY, kCameraOrthoXOZ, kCameraOrthoZOY 
   if (fScene.IsLocked()) {
      Error("TGLViewer::SetCurrentCamera", "expected kUnlocked, found %s", TGLScene::LockName(fScene.CurrentLock()));
      return;
   }

   // TODO: Move these into a vector!
   switch(cameraType) {
      case(kCameraPerspXOZ): {
         fCurrentCamera = &fPerspectiveCameraXOZ;
         break;
      }
      case(kCameraPerspYOZ): {
         fCurrentCamera = &fPerspectiveCameraYOZ;
         break;
      }
      case(kCameraPerspXOY): {
         fCurrentCamera = &fPerspectiveCameraXOY;
         break;
      }
      case(kCameraOrthoXOY): {
         fCurrentCamera = &fOrthoXOYCamera;
         break;
      }
      case(kCameraOrthoXOZ): {
         fCurrentCamera = &fOrthoXOZCamera;
         break;
      }
      case(kCameraOrthoZOY): {
         fCurrentCamera = &fOrthoZOYCamera;
         break;
      }
      default: {
         Error("TGLViewer::SetCurrentCamera", "invalid camera type");
         break;
      }
   }

   // Ensure any viewport has been propigated to the current camera
   fCurrentCamera->SetViewport(fViewport);

   // And viewer is redrawn
   RequestDraw(kHigh);
}

//______________________________________________________________________________
 void TGLViewer::SetOrthoCamera(ECameraType camera, Double_t left, Double_t right, 
                               Double_t top, Double_t bottom)
{
   // Set an orthographic camera to supplied configuration - note this does not need
   // to be the current camera - though you will not see the effect if it is not.
   //
   // 'camera' defines the ortho camera - one of kCameraOrthoXOY, kCameraOrthoXOZ, kCameraOrthoZOY
   // 'left' / 'right' / 'top' / 'bottom' define the WORLD coordinates which
   // corresepond with the left/right/top/bottom positions on the GL viewer viewport
   // E.g. for kCameraOrthoXOY camera left/right are X world coords, 
   // top/bottom are Y world coords
   // As this is an orthographic camera the other axis (in eye direction) is 
   // no relevant. The near/far clip planes are set automatically based in scene
   // contents

   // TODO: Move these into a vector!
   switch(camera) {
      case(kCameraOrthoXOY): {
         fOrthoXOYCamera.Configure(left, right, top, bottom);
         if (fCurrentCamera == &fOrthoXOYCamera) {
            RequestDraw(kHigh);
         }
         break;
      }
      case(kCameraOrthoXOZ): {
         fOrthoXOZCamera.Configure(left, right, top, bottom);
         if (fCurrentCamera == &fOrthoXOZCamera) {
            RequestDraw(kHigh);
         }
         break;
      }
      case(kCameraOrthoZOY): {
         fOrthoZOYCamera.Configure(left, right, top, bottom);
         if (fCurrentCamera == &fOrthoZOYCamera) {
            RequestDraw(kHigh);
         }
         break;
      }
      default: {
         Error("TGLViewer::SetOrthoCamera", "invalid camera type");
         break;
      }
   }
}

//______________________________________________________________________________
 void TGLViewer::SetPerspectiveCamera(ECameraType camera, Double_t fov, Double_t dolly, 
                              Double_t center[3], Double_t hRotate, Double_t vRotate)
{
   // Set a perspective camera to supplied configuration - note this does not need
   // to be the current camera - though you will not see the effect if it is not.
   //
   // 'camera' defines the persp camera - one of kCameraPerspXOZ, kCameraPerspYOZ, kCameraPerspXOY
   // 'fov' - field of view (lens angle) in degrees (clamped to 0.1 - 170.0)
   // 'dolly' - distance from 'center'
   // 'center' - world position from which dolly/hRotate/vRotate are measured
   //             camera rotates round this, always facing in (in center of viewport)
   // 'hRotate' - horizontal rotation from initial configuration in degrees
   // 'hRotate' - vertical rotation from initial configuration in degrees

   // TODO: Move these into a vector!
   switch(camera) {
      case(kCameraPerspXOZ): {
         fPerspectiveCameraXOZ.Configure(fov, dolly, center, hRotate, vRotate);
         if (fCurrentCamera == &fPerspectiveCameraXOZ) {
            RequestDraw(kHigh);
         }
         break;
      }
      case(kCameraPerspYOZ): {
         fPerspectiveCameraYOZ.Configure(fov, dolly, center, hRotate, vRotate);
         if (fCurrentCamera == &fPerspectiveCameraYOZ) {
            RequestDraw(kHigh);
         }
         break;
      }
      case(kCameraPerspXOY): {
         fPerspectiveCameraXOY.Configure(fov, dolly, center, hRotate, vRotate);
         if (fCurrentCamera == &fPerspectiveCameraXOY) {
            RequestDraw(kHigh);
         }
         break;
      }
      default: {
         Error("TGLViewer::SetPerspectiveCamera", "invalid camera type");
         break;
      }
   }
}

//______________________________________________________________________________
 void TGLViewer::ToggleLight(ELight light)
{
   // Toggle light on/off - 'light' one of kFront, kTop, kBottom, kLeft, kRight

   // N.B. We can't directly call glEnable here as may not be in correct gl context
   // adjust mask and set when drawing
   if (light >= kLightMask) {
      Error("TGLViewer::ToggleLight", "invalid light type");
      return;
   }

   fLightState ^= light;
   RequestDraw();
}

//______________________________________________________________________________
 void TGLViewer::SetLight(ELight light, Bool_t on)
{
   // Set light on/off - 'light' one of kFront, kTop, kBottom, kLeft, kRight

   // N.B. We can't directly call glEnable here as may not be in correct gl context
   // adjust mask and set when drawing
   if (light >= kLightMask) {
      Error("TGLViewer::ToggleLight", "invalid light type");
      return;
   }

   if (on) {
      fLightState |= light;
   } else {
      fLightState &= ~light;
   }
   RequestDraw();
}

//______________________________________________________________________________
 void TGLViewer::GetGuideState(EAxesType & axesType, Bool_t & referenceOn, Double_t referencePos[3]) const
{
   // Fetch the state of guides (axes & reference markers) into arguments
   axesType = fAxesType;
   referenceOn = fReferenceOn;
   referencePos[0] = fReferencePos.X();
   referencePos[1] = fReferencePos.Y();
   referencePos[2] = fReferencePos.Z();
}

//______________________________________________________________________________
 void TGLViewer::SetGuideState(EAxesType axesType, Bool_t referenceOn, const Double_t referencePos[3])
{
   // Set the state of guides (axes & reference markers) from arguments
   fAxesType = axesType;
   fReferenceOn = referenceOn;
   fReferencePos.Set(referencePos[0], referencePos[1], referencePos[2]);
   RequestDraw();
}

//______________________________________________________________________________
 void TGLViewer::GetClipState(EClipType type, Double_t data[6]) const
{
   // Get state of clip object 'type' into data vector:
   //
   // 'type' requested        'data' contents returned
   // kClipPlane              4 components - A,B,C,D - of plane eq : Ax+By+CZ+D = 0
   // kBoxPlane               6 components - Box Center X/Y/Z - Box Extents X/Y/Z
   if (type == kClipPlane) {
      TGLPlaneSet_t planes;
      fClipPlane->PlaneSet(planes);
      data[0] = planes[0].A();
      data[1] = planes[0].B();
      data[2] = planes[0].C();
      data[3] = planes[0].D();
   } else if (type == kClipBox) {
      const TGLBoundingBox & box = fClipBox->BoundingBox();
      TGLVector3 ext = box.Extents();
      data[0] = box.Center().X();
      data[1] = box.Center().Y();
      data[2] = box.Center().Z();
      data[3] = box.Extents().X();
      data[4] = box.Extents().Y();
      data[5] = box.Extents().Z();
   } else {
      Error("TGLViewer::GetClipState", "invalid clip type");
   }
}

//______________________________________________________________________________
 void TGLViewer::SetClipState(EClipType type, const Double_t data[6])
{
   // Set state of clip object 'type' into data vector:
   //
   // 'type' specified        'data' contents interpretation
   // kClipNone               ignored
   // kClipPlane              4 components - A,B,C,D - of plane eq : Ax+By+CZ+D = 0
   // kBoxPlane               6 components - Box Center X/Y/Z - Box Extents X/Y/Z
   fCurrentManip->Attach(0);

   switch (type) {
      case(kClipNone): {
         break;
      }
      case(kClipPlane): {
         TGLPlane newPlane(data[0], data[1], data[2], data[3]);
         fClipPlane->Set(newPlane);
         break;
      }
      case(kClipBox): {
         //TODO: Pull these inside TGLPhysicalShape
         // Update clip box center
         const TGLBoundingBox & currentBox = fClipBox->BoundingBox();
         TGLVector3 shift(data[0] - currentBox.Center().X(),
                          data[1] - currentBox.Center().Y(),
                          data[2] - currentBox.Center().Z());
         fClipBox->Translate(shift);
         // Update clip box extents

         TGLVector3 currentScale = fClipBox->GetScale();
         TGLVector3 newScale(data[3] / currentBox.Extents().X() * currentScale.X(),
                             data[4] / currentBox.Extents().Y() * currentScale.Y(),
                             data[5] / currentBox.Extents().Z() * currentScale.Z());

         fClipBox->Scale(newScale);
         break;
      }
   }
}

//______________________________________________________________________________
TGLViewer::EClipType TGLViewer::GetCurrentClip() const
{
   // Get current type active in viewer - returns one of kClipNone
   // kClipPlane or kClipBox
   if (fCurrentClip == 0) {
      return TGLViewer::kClipNone;
   } else if (fCurrentClip == fClipPlane) {
      return TGLViewer::kClipPlane;
   } else if (fCurrentClip == fClipBox) {
      return TGLViewer::kClipBox;
   } else {
      Error("TGLViewer::GetCurrentClip" , "Unknown clip type");
      return TGLViewer::kClipNone;
   }
}

//______________________________________________________________________________
 void TGLViewer::SetCurrentClip(EClipType type, Bool_t edit)
{
   // Set current clip active in viewer - 'type' is one of kClipNone
   // kClipPlane or kClipBox. 'edit' indicates if clip object should
   // been shown/edited directly in viewer (current manipulator attached to it)
   // kTRUE if so (ignored for kClipNone), kFALSE otherwise
   fCurrentManip->Attach(0);
   switch (type) {
      case(kClipNone): {
         fCurrentClip = 0;
         break;
      }
      case(kClipPlane): {
         fCurrentClip = fClipPlane;
         break;
      }
      case(kClipBox): {
         fCurrentClip = fClipBox;
         break;
      }
      default: {
         Error("TGLViewer::SetCurrentClip" , "Unknown clip type");
         break;
      }
   }
    
   fClipEdit = edit;
   if (fClipEdit) {
      fCurrentManip->Attach(fCurrentClip);
   }

   RequestDraw();
}

//______________________________________________________________________________
 void TGLViewer::SetSelectedColor(const Float_t color[17])
{
   // Set full color attributes on current selected physical shape: 
   //
   // 0...3  - diffuse
   // 4...7  - ambient
   // 8...11 - specular
   // 12..15 - emission
   // 16     - shininess
   //
   // see OpenGL documentation for details of materials
   if (fScene.SetSelectedColor(color)) {
      RequestDraw();
   }
}

//______________________________________________________________________________
 void TGLViewer::SetColorOnSelectedFamily(const Float_t color[17])
{
   // Set full color attributes on all physical shapes sharing the same
   // logical shape as the selected physical
   //
   // 0...3  - diffuse
   // 4...7  - ambient
   // 8...11 - specular
   // 12..15 - emission
   // 16     - shininess
   //
   // see OpenGL documentation for details of materials
   if (fScene.SetColorOnSelectedFamily(color)) {
      RequestDraw();
   }
}

//______________________________________________________________________________
 void TGLViewer::SetSelectedGeom(const TGLVertex3 & trans, const TGLVector3 & scale)
{
   // Update geometry of the selected physical. 'trans' and 'scale' specify the
   // translation and scaling components of the physical shapes translation matrix
   // See TGLMatrix for more details
   if (fScene.SetSelectedGeom(trans, scale)) {
      RequestDraw();
   }
}

//______________________________________________________________________________
 void TGLViewer::SelectionChanged() 
{ 
   // Emit signal indicating selection has changed
   Emit("SelectionChanged()"); 
}

//______________________________________________________________________________
 void TGLViewer::ClipChanged() 
{ 
    // Emit signal indicating clip object has changed
   Emit("ClipChanged()"); 
}

//______________________________________________________________________________
 Int_t TGLViewer::DistancetoPrimitive(Int_t /*px*/, Int_t /*py*/)
{
   // Calcaulate and return pxiel distance to nearest viewer object from
   // window location px, py
   // This is provided for use when embedding GL viewer into pad
   
   // Can't track the indvidual objects in rollover. Just set the viewer as the
   // selected object, and return 0 (object identified) so we receive ExecuteEvent calls
   gPad->SetSelected(this);
   return 0;
}

//______________________________________________________________________________
 void TGLViewer::ExecuteEvent(Int_t event, Int_t px, Int_t py)
{
   // Process event of type 'event' - one of EEventType types,
   // occuring at window location px, py
   // This is provided for use when embedding GL viewer into pad
   
   /*enum EEventType {
   kNoEvent       =  0,
   kButton1Down   =  1, kButton2Down   =  2, kButton3Down   =  3, kKeyDown  =  4,
   kButton1Up     = 11, kButton2Up     = 12, kButton3Up     = 13, kKeyUp    = 14,
   kButton1Motion = 21, kButton2Motion = 22, kButton3Motion = 23, kKeyPress = 24,
   kButton1Locate = 41, kButton2Locate = 42, kButton3Locate = 43,
   kMouseMotion   = 51, kMouseEnter    = 52, kMouseLeave    = 53,
   kButton1Double = 61, kButton2Double = 62, kButton3Double = 63
   
   enum EGEventType {
   kGKeyPress, kKeyRelease, kButtonPress, kButtonRelease,
   kMotionNotify, kEnterNotify, kLeaveNotify, kFocusIn, kFocusOut,
   kExpose, kConfigureNotify, kMapNotify, kUnmapNotify, kDestroyNotify,
   kClientMessage, kSelectionClear, kSelectionRequest, kSelectionNotify,
   kColormapNotify, kButtonDoubleClick, kOtherEvent*/

   // Map our event EEventType (base/inc/Buttons.h) back to Event_t (base/inc/GuiTypes.h)
   // structure, and call appropriate HandleXXX() function 
   Event_t eventSt;
   eventSt.fX = px;
   eventSt.fY = py;

   switch (event) {
      case kButton1Down:
      case kButton1Up:
      {
         eventSt.fCode = kButton1;
         eventSt.fType = kButton1Down ? kButtonPress:kButtonRelease;
         HandleButton(&eventSt);
      }
      case kButton2Down:
      case kButton2Up:
      {
         eventSt.fCode = kButton2;
         eventSt.fType = kButton2Down ? kButtonPress:kButtonRelease;
         HandleButton(&eventSt);
      }
      case kButton3Down:
      case kButton3Up:
      {
         eventSt.fCode = kButton3;
         eventSt.fType = kButton3Down ? kButtonPress:kButtonRelease;
         HandleButton(&eventSt);
      }
      case kButton1Double:
      case kButton2Double:
      case kButton3Double:
      {
         eventSt.fCode = kButton1Double ? kButton1 : kButton2Double ? kButton2 : kButton3;
         eventSt.fType = kButtonDoubleClick;
         HandleDoubleClick(&eventSt);
      }
      case kButton1Motion:
      case kButton2Motion:
      case kButton3Motion:
      {
         eventSt.fCode = kButton1Motion ? kButton1 : kButton2Motion ? kButton2 : kButton3;
         eventSt.fType = kMotionNotify;
         HandleMotion(&eventSt);
      }
      case kKeyPress: // We only care about full key 'presses' not individual down/up
      {
         eventSt.fType = kKeyRelease;
         eventSt.fCode = px; // px contains key code - need modifiers from somewhere
         HandleKey(&eventSt);
      }
      default: 
      {
         Error("TGLViewer::ExecuteEvent", "invalid event type");
      }
   }
};

//______________________________________________________________________________
 Bool_t TGLViewer::HandleEvent(Event_t *event)
{
   // Handle generic Event_t type 'event' - provided to catch focus changes
   // and terminate any interaction in viewer
   if (event->fType == kFocusIn) {
      if (fAction != kNone) {
         Error("TGLViewer::HandleEvent", "active action at focus in");
      }
      fAction = kNone;
   }
   if (event->fType == kFocusOut) {
      fAction = kNone;
   }

   return kTRUE;
}

//______________________________________________________________________________
 Bool_t TGLViewer::HandleButton(Event_t *event)
{
   // Handle mouse button 'event'
   if (fScene.IsLocked()) {
      if (gDebug>2) {
         Info("TGLViewer::HandleButton", "ignored - scene is %s", TGLScene::LockName(fScene.CurrentLock()));
      }
      return kFALSE;
   }

   // If no current action give current manipulator first chance to process
   if (fAction == kNone) {
      if (fCurrentManip->HandleButton(event, *fCurrentCamera)) {
         ClipChanged(); // Clip may have changed
         RequestDraw();
         return kTRUE;
      }
   }

   // Only process one action/button down/up pairing - block others
   if (fAction != kNone) {
      if (event->fType == kButtonPress ||
          (event->fType == kButtonRelease && event->fCode != fActiveButtonID)) {
         return kFALSE;
      }
   }
   
   // Button DOWN
   if (event->fType == kButtonPress) {
      Bool_t grabPointer = kFALSE;

      // Record active button for release
      fActiveButtonID = event->fCode;

      // Record mouse start
      fStartPos.fX = fLastPos.fX = event->fX;
      fStartPos.fY = fLastPos.fY = event->fY;
      
      switch(event->fCode) {
         // LEFT mouse button
         case(kButton1): {
            if (event->fState & kKeyShiftMask) {
               RequestSelect(event->fX, event->fY);

               // TODO: If no selection start a box select
            } else {
               fAction = kRotate;
               grabPointer = kTRUE;
            }
            break;
         }
         // MID mouse button
         case(kButton2): {
            if (event->fState & kKeyShiftMask) {
               RequestSelect(event->fX, event->fY);
               // Start object drag
               if (fScene.GetSelected()) {
                  fAction = kDrag;
                  grabPointer = kTRUE;
               }
            } else {
               fAction = kTruck;
               grabPointer = kTRUE;
            }
            break;
         }
         // RIGHT mouse button
         case(kButton3): {
            // Shift + Right mouse - select+context menu
            if (event->fState & kKeyShiftMask) {
               RequestSelect(event->fX, event->fY);
               const TGLPhysicalShape * selected = fScene.GetSelected();
               if (selected) {
                  if (!fContextMenu) {
                     fContextMenu = new TContextMenu("glcm", "GL Viewer Context Menu");
                  }
                  selected->InvokeContextMenu(*fContextMenu, event->fX, event->fY);
               }
            } else {
               fAction = kDolly;
               grabPointer = kTRUE;
            }
            break;
         }
      }
   }
   // Button UP
   else if (event->fType == kButtonRelease) {
      // TODO: Check on Linux - on Win32 only see button release events
      // for mouse wheel
      switch(event->fCode) {
         // Buttons 4/5 are mouse wheel
         // Note: Modifiers (ctrl/shift) disabled as fState doesn't seem to
         // have correct modifier flags with mouse wheel under Windows..
         case(kButton4): {
            // Zoom out (adjust camera FOV)
            if (CurrentCamera().Zoom(+50, kFALSE, kFALSE)) { //TODO : val static const somewhere
               RequestDraw();
            }
            break;
         }
         case(kButton5): {
            // Zoom in (adjust camera FOV)
            if (CurrentCamera().Zoom(-50, kFALSE, kFALSE)) { //TODO : val static const somewhere
               RequestDraw();
            }
            break;
         }
      }
      fAction = kNone;
   }

   return kTRUE;
}

//______________________________________________________________________________
 Bool_t TGLViewer::HandleDoubleClick(Event_t *event)
{
   // Handle mouse double click 'event'
   if (fScene.IsLocked()) {
      if (gDebug>3) {
         Info("TGLViewer::HandleDoubleClick", "ignored - scene is %s", TGLScene::LockName(fScene.CurrentLock()));
      }
      return kFALSE;
   }

   // Reset interactive camera mode on button double
   // click (unless mouse wheel)
   if (event->fCode != kButton4 && event->fCode != kButton5) {
      CurrentCamera().Reset();
      fStartPos.fX = fLastPos.fX = event->fX;
      fStartPos.fY = fLastPos.fY = event->fY;
      RequestDraw();
   }
   return kTRUE;
}

//______________________________________________________________________________
 Bool_t TGLViewer::HandleConfigureNotify(Event_t *event)
{
   // Handle configure notify 'event' - a window resize/movement
   if (fScene.IsLocked()) {
      if (gDebug>3) {
         Info("TGLViewer::HandleConfigureNotify", "ignored - scene is %s", TGLScene::LockName(fScene.CurrentLock()));
      }
      return kFALSE;
   }

   if (event) {
      SetViewport(event->fX, event->fY, event->fWidth, event->fHeight);
   }
   return kTRUE;
}

//______________________________________________________________________________
 Bool_t TGLViewer::HandleKey(Event_t *event)
{
   // Handle keyboard 'event'
   if (fScene.IsLocked()) {
      if (gDebug>3) {
         Info("TGLViewer::HandleKey", "ignored - scene is %s", TGLScene::LockName(fScene.CurrentLock()));
      }
      return kFALSE;
   }

   char tmp[10] = {0};
   UInt_t keysym = 0;

   gVirtualX->LookupString(event, tmp, sizeof(tmp), keysym);
   
   Bool_t redraw = kFALSE;

   switch (keysym) {
   case kKey_Plus:
   case kKey_J:
   case kKey_j:
      redraw = CurrentCamera().Dolly(10, event->fState & kKeyControlMask, 
                                         event->fState & kKeyShiftMask); //TODO : val static const somewhere
      break;
   case kKey_Minus:
   case kKey_K:
   case kKey_k:
      redraw = CurrentCamera().Dolly(-10, event->fState & kKeyControlMask, 
                                          event->fState & kKeyShiftMask); //TODO : val static const somewhere
      break;
   case kKey_R:
   case kKey_r:
      fDrawStyle = kFill;
      redraw = kTRUE;
      break;
   case kKey_W:
   case kKey_w:
      fDrawStyle = kWireFrame;
      redraw = kTRUE;
      break;
   case kKey_T:
   case kKey_t:
      fDrawStyle = kOutline;
      redraw = kTRUE;
      break;
   case kKey_V:
   case kKey_v:
      // Install translation manipulator
      // Pass attached shape of manipulator across, then swap the manip
      fTransManip->Attach(fCurrentManip->GetAttached());
      fCurrentManip = fTransManip;
      redraw = kTRUE;
      break;
   case kKey_X:
   case kKey_x:
      // Install scale manipulator
      // Pass attached shape of manipulator across, then swap the manip
      fScaleManip->Attach(fCurrentManip->GetAttached());
      fCurrentManip = fScaleManip;
      redraw = kTRUE;
      break;
   case kKey_C:
   case kKey_c:
      // Install rotation manipulator
      // Pass attached shape of manipulator across, then swap the manip
      fRotateManip->Attach(fCurrentManip->GetAttached());
      fCurrentManip = fRotateManip;
      redraw = kTRUE;
      break;
   case kKey_Up:
      redraw = CurrentCamera().Truck(fViewport.CenterX(), fViewport.CenterY(), 0, 5);
      break;
   case kKey_Down:
      redraw = CurrentCamera().Truck(fViewport.CenterX(), fViewport.CenterY(), 0, -5);
      break;
   case kKey_Left:
      redraw = CurrentCamera().Truck(fViewport.CenterX(), fViewport.CenterY(), -5, 0);
      break;
   case kKey_Right:
      redraw = CurrentCamera().Truck(fViewport.CenterX(), fViewport.CenterY(), 5, 0);
      break;
   // Toggle debugging mode
   case kKey_D:
   case kKey_d:
      fDebugMode = !fDebugMode;
      redraw = kTRUE;
      Info("OpenGL viewer debug mode : ", fDebugMode ? "ON" : "OFF");
      break;
   // Forced rebuild for debugging mode
   case kKey_Space:
      if (fDebugMode) {
         Info("OpenGL viewer FORCED rebuild", "");
         RebuildScene();
      }
   }

   if (redraw) {
      RequestDraw();
   }
   
   return kTRUE;
}

//______________________________________________________________________________
 Bool_t TGLViewer::HandleMotion(Event_t *event)
{
   // Handle mouse motion 'event'
   if (fScene.IsLocked()) {
      if (gDebug>3) {
         Info("TGLViewer::HandleMotion", "ignored - scene is %s", TGLScene::LockName(fScene.CurrentLock()));
      }
      return kFALSE;
   }

   if (!event) {
      return kFALSE;
   }
   
   // Give current manipulator first chance to process
   if (fCurrentManip->HandleMotion(event, *fCurrentCamera)) {
      RequestDraw();
      return kTRUE;
   }

   Bool_t redraw = kFALSE;
   
   Int_t xDelta = event->fX - fLastPos.fX;
   Int_t yDelta = event->fY - fLastPos.fY;
   
   // Camera interface requires GL coords - Y inverted
   if (fAction == kRotate) {
      redraw = CurrentCamera().Rotate(xDelta, -yDelta);
   } else if (fAction == kTruck) {
      redraw = CurrentCamera().Truck(event->fX, fViewport.Y() - event->fY, xDelta, -yDelta);
   } else if (fAction == kDolly) {
      redraw = CurrentCamera().Dolly(xDelta, event->fState & kKeyControlMask, 
                                                 event->fState & kKeyShiftMask);
   } else if (fAction == kDrag) {
      const TGLPhysicalShape * selected = fScene.GetSelected();
      if (selected) {
         TGLVector3 shift = CurrentCamera().ViewportDeltaToWorld(selected->BoundingBox().Center(), 
                                                                 xDelta, -yDelta);

         // Don't modify selected directly as scene needs to invalidate bounding box
         // hence will only give us a const handle on selected
         redraw = fScene.ShiftSelected(shift);

         // Inform external client selection has been modified
         SelectionChanged();
      }
   }

   fLastPos.fX = event->fX;
   fLastPos.fY = event->fY;
   
   if (redraw) {
      RequestDraw();
   }
   
   return kTRUE;
}

//______________________________________________________________________________
 Bool_t TGLViewer::HandleExpose(Event_t *)
{
   // Handle window expose 'event' - show
   if (fScene.IsLocked()) {
      if (gDebug>3) {
         Info("TGLViewer::HandleExpose", "ignored - scene is %s", TGLScene::LockName(fScene.CurrentLock()));
      }
      return kFALSE;
   }

   RequestDraw(kHigh);
   return kTRUE;
}



ROOT page - Class index - Class Hierarchy - Top of the page

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.