// @(#)root/gl:$Name:  $:$Id: TGLPhysicalShape.h,v 1.15 2006/02/08 10:49:26 couet Exp $
// Author:  Richard Maunder  25/05/2005
// Parts taken from original TGLSceneObject Timur Pocheptsov

/*************************************************************************
 * 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.             *
 *************************************************************************/

#ifndef ROOT_TGLPhysicalShape
#define ROOT_TGLPhysicalShape

#ifndef ROOT_TGLDrawable
#include "TGLDrawable.h"
#endif
#ifndef ROOT_TGLLogicalShape
#include "TGLLogicalShape.h"
#endif
#ifndef ROOT_TGLUtil
#include "TGLUtil.h" // For TGLMatrix
#endif

class TGLCamera;
class TContextMenu;

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TGLPhysicalShape                                                     //
//                                                                      //
// Concrete physical shape - a GL drawable. Physical shapes are the     //
// objects the user can actually see, select, move in the viewer. They  //
// placements of their associated local frame TGLLogicalShape into the  //
// world frame. The draw process is:                                    //
//                                                                      // 
// Load attributes - material colors etc                                // 
// Load translation matrix - placement                                  //
// Load gl name (for selection)                                         //
// Call our associated logical shape Draw() to draw placed shape        //
//                                                                      //
// The physical shape supports translation, scaling and rotation,       //
// selection, color changes, and permitted modification flags etc.      //
// A physical shape is always bound to a single, fixed logical shape    //
// - hence const & handle. It can perform mutable reference counting on //
// the logical to enable purging.                                       //
//                                                                      //
// See base/src/TVirtualViewer3D for description of common external 3D  //
// viewer architecture and how external viewer clients use it.          //
//////////////////////////////////////////////////////////////////////////

class TGLPhysicalShape : public TGLDrawable
{
public:
   // Flags for permitted manipulation of object
   enum EManip  { kTranslateX   = 1 << 0,
                  kTranslateY   = 1 << 1,
                  kTranslateZ   = 1 << 2,
                  kTranslateAll = kTranslateX | kTranslateY | kTranslateZ,
                  kScaleX       = 1 << 3,
                  kScaleY       = 1 << 4,
                  kScaleZ       = 1 << 5,
                  kScaleAll     = kScaleX | kScaleY | kScaleZ,
                  kRotateX      = 1 << 6,
                  kRotateY      = 1 << 7,
                  kRotateZ      = 1 << 8,
                  kRotateAll    = kRotateX | kRotateY | kRotateZ,
                  kManipAll     = kTranslateAll | kScaleAll | kRotateAll
                  };
private:
   // Fields
   const TGLLogicalShape & fLogicalShape; //! the associated logical shape
   TGLMatrix               fTransform;    //! transform (placement) of physical instance
   Float_t                 fColor[17];    //! GL color array
   Bool_t                  fSelected;     //! selected state
   Bool_t                  fInvertedWind; //! face winding TODO: can get directly from fTransform?
   Bool_t                  fModified;     //! has been modified - retain across scene rebuilds
   EManip                  fManip;        //! permitted manipulation bitflags - see EManip
   // TODO: Common UInt_t flags section (in TGLDrawable?) to avoid multiple bools

   // Methods
   void            UpdateBoundingBox(); 
   void            InitColor(const Float_t rgba[4]);

protected:
   // Methods
   virtual void DirectDraw(const TGLDrawFlags & flags) const;

public:
   TGLPhysicalShape(ULong_t ID, const TGLLogicalShape & logicalShape,
                    const TGLMatrix & transform, Bool_t invertedWind,
                    const Float_t rgba[4]);
   TGLPhysicalShape(ULong_t ID, const TGLLogicalShape & logicalShape,
                    const double * transform, Bool_t invertedWind,
                    const Float_t rgba[4]);
   virtual ~TGLPhysicalShape();

   TGLDrawFlags CalcDrawFlags(const TGLCamera & camera, const TGLDrawFlags & sceneFlags) const;

   // TGLDrawable overloads
   virtual ELODAxes SupportedLODAxes() const;
   virtual void     Draw(const TGLDrawFlags & flags) const;

   const TGLLogicalShape & GetLogical() const { return fLogicalShape; }
   void             InvokeContextMenu(TContextMenu & menu, UInt_t x, UInt_t y) const;

   // Modification and manipulation
   EManip           GetManip() const         { return fManip; }
   void             SetManip(EManip manip)   { fManip = manip; }
   // Selected treated as temporary modification
   Bool_t           IsModified() const       { return fModified || IsSelected(); }

   // Selection
   Bool_t           IsSelected() const       { return fSelected; }
   void             Select(Bool_t select)    { fSelected = select; }

   // Color
   const Float_t  * Color() const                      { return fColor; }
   Bool_t           IsTransparent() const              { return fColor[3] < 1.f; }
   Bool_t           IsInvisible() const                { return fColor[3] == 0.f; }
   void             SetColor(const Float_t rgba[17]);

   // Geometry
   TGLVector3       GetScale() const;
   TGLVertex3       GetTranslation() const;

   void             SetTransform(const TGLMatrix & transform);
   void             SetTranslation(const TGLVertex3 & translation);
   void             Translate(const TGLVector3 & vect);
   void             Scale(const TGLVector3 & scale);
   void             Rotate(const TGLVertex3 & pivot, const TGLVector3 & axis, Double_t angle);

   ClassDef(TGLPhysicalShape,0) // a physical (placed, global frame) drawable object
};

//______________________________________________________________________________
inline TGLPhysicalShape::ELODAxes TGLPhysicalShape::SupportedLODAxes() const
{
   // Defined by our logical shape
   return fLogicalShape.SupportedLODAxes();
}

//______________________________________________________________________________
inline TGLVector3 TGLPhysicalShape::GetScale() const
{ 
   return fTransform.GetScale(); 
}

//______________________________________________________________________________
inline TGLVertex3 TGLPhysicalShape::GetTranslation() const
{ 
   return fTransform.GetTranslation(); 
}

//______________________________________________________________________________
inline void TGLPhysicalShape::SetTransform(const TGLMatrix & transform)
{
   fTransform = transform;
   UpdateBoundingBox();
   fModified = kTRUE;

   // Any DL cache would be invalidate by this - NOTE does not work currently
   Purge();
}

//______________________________________________________________________________
inline void TGLPhysicalShape::SetTranslation(const TGLVertex3 & translation) 
{ 
   fTransform.SetTranslation(translation);
   UpdateBoundingBox();
   fModified = kTRUE;

   // Any DL cache would be invalidate by this - NOTE does not work currently
   Purge();
}

//______________________________________________________________________________
inline void TGLPhysicalShape::Translate(const TGLVector3 & vect)
{
   fTransform.Translate(vect);
   UpdateBoundingBox();
   fModified = kTRUE;

   // Any DL cache would be invalidate by this - NOTE does not work currently
   Purge();
}

//______________________________________________________________________________
inline void TGLPhysicalShape::Scale(const TGLVector3 & scale) 
{ 
   TGLVertex3 origCenter = fBoundingBox.Center();
   fTransform.Scale(scale); 
   UpdateBoundingBox();
   TGLVector3 shift = fBoundingBox.Center() - origCenter;
   Translate(-shift);
   UpdateBoundingBox();
   fModified = kTRUE;

   // Any DL cache would be invalidate by this - NOTE does not work currently
   Purge();
}

//______________________________________________________________________________
inline void TGLPhysicalShape::Rotate(const TGLVertex3 & pivot, const TGLVector3 & axis, Double_t angle)
{ 
   TGLVertex3 c = BoundingBox().Center();
   fTransform.Rotate(pivot, axis, angle);
   UpdateBoundingBox();
   fModified = kTRUE;

   // Any DL cache would be invalidate by this - NOTE does not work currently
   Purge();
}

#endif // ROOT_TGLPhysicalShape
