// @(#)root/gl:$Name:  $:$Id: TGLPhysicalShape.cxx,v 1.24 2006/08/23 14:39:40 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 "TGLPhysicalShape.h"
#include "TGLLogicalShape.h"
#include "TGLCamera.h"
#include "TGLDrawFlags.h"
#include "TGLIncludes.h"

// For debug tracing
#include "TClass.h"
#include "TError.h"

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TGLPhysicalShape                                                     //
//                                                                      //
// Concrete physical shape - a GL drawable. Physical shapes are the     //
// objects the user can actually see, select, move in the viewer. It is //
// a placement of the associated local frame TGLLogicaShape 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 cannot modify or be bound to another (or no)        //
// 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.          //
//////////////////////////////////////////////////////////////////////////

ClassImp(TGLPhysicalShape)

//______________________________________________________________________________
TGLPhysicalShape::TGLPhysicalShape(ULong_t ID, const TGLLogicalShape & logicalShape,
                                   const TGLMatrix & transform, Bool_t invertedWind,
                                   const Float_t rgba[4]) :
   TGLDrawable(ID, kFALSE), // Physical shapes NOT DL cached - see Draw()
   fLogicalShape(logicalShape),
   fTransform(transform),
   fSelected(kFALSE),
   fInvertedWind(invertedWind),
   fModified(kFALSE),
   fManip(kManipAll)
{
   // Construct a physical shape using arguments:
   //    ID             - unique drawable id.
   //    logicalShape   - bound logical shape
   //    transform      - transform for placement of logical drawing
   //    invertedWind   - use inverted face polygon winding?
   //    rgba           - basic four component (RGBA) diffuse color
   fLogicalShape.AddRef();
   UpdateBoundingBox();

   // Initialise color
   InitColor(rgba);
}

//______________________________________________________________________________
TGLPhysicalShape::TGLPhysicalShape(ULong_t ID, const TGLLogicalShape & logicalShape,
                                   const Double_t * transform, Bool_t invertedWind,
                                   const Float_t rgba[4]) :
   TGLDrawable(ID, kFALSE), // Physical shapes NOT DL cached - see Draw()
   fLogicalShape(logicalShape),
   fTransform(transform),
   fSelected(kFALSE),
   fInvertedWind(invertedWind),
   fModified(kFALSE),
   fManip(kManipAll)
{
   // Construct a physical shape using arguments:
   //    ID             - unique drawable id.
   //    logicalShape   - bound logical shape
   //    transform      - 16 Double_t component transform for placement of logical drawing
   //    invertedWind   - use inverted face polygon winding?
   //    rgba           - basic four component (RGBA) diffuse color

   fLogicalShape.AddRef();

   // Temporary hack - invert the 3x3 part of martix as TGeo sends this
   // in opp layout to shear/translation parts. Speak to Andrei about best place
   // to fix - probably when filling TBuffer3D - should always be OGL convention?
   fTransform.Transpose3x3();
   UpdateBoundingBox();

   // Initialise color
   InitColor(rgba);
}

//______________________________________________________________________________
TGLPhysicalShape::~TGLPhysicalShape()
{
   // Destroy the physical shape
   fLogicalShape.SubRef();
}

//______________________________________________________________________________
void TGLPhysicalShape::UpdateBoundingBox()
{
   // Update our internal bounding box (in global frame)
   fBoundingBox.Set(fLogicalShape.BoundingBox());
   fBoundingBox.Transform(fTransform);
}

//______________________________________________________________________________
void TGLPhysicalShape::InitColor(const Float_t rgba[4])
{
   // Initialise the colors, using basic RGBA diffuse material color supplied

   // TODO: Make a color class
   fColor[0] = rgba[0];
   fColor[1] = rgba[1];
   fColor[2] = rgba[2];
   fColor[3] = rgba[3];

   //ambient
   fColor[4] = fColor[5] = fColor[6] = 0.0f;
   //specular
   fColor[8] = fColor[9] = fColor[10] = 0.7f;
   //emission
   fColor[12] = fColor[13] = fColor[14] = 0.f;
   //alpha
   fColor[7] = fColor[11] = fColor[15] = 1.f;
   //shininess
   fColor[16] = 60.f;

   // Modified flag not set - just initialising
}

//______________________________________________________________________________
void TGLPhysicalShape::SetColor(const Float_t color[17])
{
   // Set full color attributes - see OpenGL material documentation
   // for full description
   //
   // 0...3  - diffuse
   // 4...7  - ambient
   // 8...11 - specular
   // 12..15 - emission
   // 16     - shininess

   // TODO: Make a color class
   for (UInt_t i = 0; i < 17; i++) {
      fColor[i] = color[i];
   }

   // DL cache is NOT affected by this (hence no Purge()) as colors set in Draw() -
   // not DirectDraw(). See Draw() for reasons

   fModified = kTRUE;
}

//______________________________________________________________________________
void TGLPhysicalShape::Draw(const TGLDrawFlags & flags) const
{
   // Draw physical shape, using LOD flags, potential from display list cache

   // Debug tracing
   if (gDebug > 4) {
      Info("TGLPhysicalShape::Draw", "this %d (class %s) LOD %d", this, IsA()->GetName(), flags.LOD());
   }

   // IMPORTANT: Per drawable DL cache purging does not work currently.
   // This means that DL caching cannot be enabled at TGLPhysicalShape level as
   // modifications (scale/rotate etc) will not result in the cache being
   // purged.

   // Setup current colors
   // NOTE: These *could* be done in DirectDraw(), and hence DL cached if/when enabled
   // at this level. This *might* be faster....
   // However this would mean the DL cache would need to create entries based on draw
   // style as well as LOD from 'flags' - resulting in DLs with different colors, but
   // identical geometry.
   //
   // TODO: Better solution - sorting of physicals - Min. state swap for attributes
   // Or maybe set color in logical then override if modified in physical????
   // as in normal cases physicals sharing same logical have same color.

   // Setup colors - avoid setting things not required
   // for current draw flags
   switch (flags.Style()) {
      case TGLDrawFlags::kWireFrame: {
         // Wireframe needs basic color only
         glColor4fv(fColor);
         break;
      }
      case TGLDrawFlags::kFill:
      case TGLDrawFlags::kOutline: {
         // Both need material colors

         // Set back diffuse only for clipping where inner (back) faces
         // are shown. Don't set shinneness or specular as we want
         // back face to appear as 'flat' as possible as crude visual
         // approximation to proper capped clipped solid
         glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, fColor);
         glMaterialfv(GL_FRONT, GL_AMBIENT, fColor + 4);
         glMaterialfv(GL_FRONT, GL_SPECULAR, fColor + 8);
         glMaterialfv(GL_FRONT, GL_EMISSION, fColor + 12);
         glMaterialf(GL_FRONT, GL_SHININESS, fColor[16]);

         // Outline also needs grey wireframe but respecting
         // transparency of main diffuse color
         if (flags.Style() == TGLDrawFlags::kOutline) {
            glColor4f(0.1, 0.1, 0.1, fColor[3]/2.0);
         } else {
            // Some objects use point/line graphics. Material mode disabled.
            glColor4fv(fColor);
         }

         // TODO: Scene draws outline style in two passes - for second
         // wireframe overlay one we don't need to set materials
         // But don't know the pass here.....
         // Also we only need to set back materials when clipping
         // - this might cause extra uneeded cost
         break;
      }
   }

   TGLDrawable::Draw(flags);
}

//______________________________________________________________________________
void TGLPhysicalShape::DirectDraw(const TGLDrawFlags & flags) const
{
   // Draw physical shape, using LOD flags - can be captured into display list cache
   //
   // Load gl name (for selection)
   // Load translation matrix - placement
   // Call associated fLogicalShape Draw() method

   // Debug tracing
   if (gDebug > 4) {
      Info("TGLPhysicalShape::DirectDraw", "this %d (class %s) LOD %d", this, IsA()->GetName(), flags.LOD());
   }

   glLoadName(ID());
   glPushMatrix();
   glMultMatrixd(fTransform.CArr());
   if (fInvertedWind) {
      glFrontFace(GL_CW);
   }
   // If LOD is pixel or less can draw pixel(point) directly, skipping
   // any logical call, caching etc
   if (flags.LOD() == TGLDrawFlags::kLODPixel) {
      glBegin(GL_POINTS);
      glVertex3d(0.0, 0.0, 0.0);
      glEnd();
   } else {
      fLogicalShape.Draw(flags);
   }
   if (fInvertedWind) {
      glFrontFace(GL_CCW);
   }
   glPopMatrix();
}

//______________________________________________________________________________
TGLDrawFlags TGLPhysicalShape::CalcDrawFlags(const TGLCamera & camera, const TGLDrawFlags & sceneFlags) const
{
   // Return draw flags for shape, suitible for use under projection defined by 'camera',
   // taking account of which local axes of the shape support LOD adjustment, and the
   // global 'sceneFlags' passed.
   //
   // sceneFlags.Style() - copied to shape draw style
   // sceneFlags.LOD() - factored into projection LOD
   //
   // Returned LOD() component is UInt 0 (kLODPixel - lowest quality) to
   // 100 (kLODHigh - highest quality) or special case kLODUnsupported if shape
   // does not support LOD at all

   std::vector <Double_t> boxViewportDiags;
   TGLDrawable::ELODAxes lodAxes = SupportedLODAxes();
   const TGLBoundingBox & box = BoundingBox();

   if (lodAxes == TGLDrawable::kLODAxesNone) {
      // Shape doesn't support LOD along any axes return special unsupported LOD draw/cache flag
      return TGLDrawFlags(sceneFlags.Style(), TGLDrawFlags::kLODUnsupported,
                          sceneFlags.Selection(), sceneFlags.SecSelection());
   }

   if (lodAxes == TGLDrawable::kLODAxesAll) {
      // Shape supports LOD along all axes - basis LOD hint on diagonal of viewport
      // projection rect round whole bounding box
      boxViewportDiags.push_back(camera.ViewportRect(box).Diagonal());
   } else if (lodAxes == (TGLDrawable::kLODAxesY | TGLDrawable::kLODAxesZ)) {
      // Shape supports LOD along Y/Z axes (not X). LOD hint based on longest
      // diagonal (largest rect) of either of the X axis end faces
      boxViewportDiags.push_back(camera.ViewportRect(box, TGLBoundingBox::kFaceLowX).Diagonal());
      boxViewportDiags.push_back(camera.ViewportRect(box, TGLBoundingBox::kFaceHighX).Diagonal());
   } else if (lodAxes == (TGLDrawable::kLODAxesX | TGLDrawable::kLODAxesZ)) {
      // Shape supports LOD along X/Z axes (not Y). See above for Y/Z
      boxViewportDiags.push_back(camera.ViewportRect(box, TGLBoundingBox::kFaceLowY).Diagonal());
      boxViewportDiags.push_back(camera.ViewportRect(box, TGLBoundingBox::kFaceHighY).Diagonal());
   } else if (lodAxes == (TGLDrawable::kLODAxesX | TGLDrawable::kLODAxesY)) {
      // Shape supports LOD along X/Y axes (not Z). See above for Y/Z
      boxViewportDiags.push_back(camera.ViewportRect(box, TGLBoundingBox::kFaceLowZ).Diagonal());
      boxViewportDiags.push_back(camera.ViewportRect(box, TGLBoundingBox::kFaceHighZ).Diagonal());
   }
   else {
      // Don't bother to implement LOD calc for shapes supporting LOD along single
      // axis only. Not needed at present + unlikely case - but could be done based
      // on longest of projection of 4 edges of BBox along LOD axis. However this would
      // probably be more costly than just using whole BB projection (as for all axes)
      Error("TGLScene::CalcPhysicalLOD", "LOD calculation for single axis not implemented presently");
      return TGLDrawFlags(sceneFlags.Style(), TGLDrawFlags::kLODMed,
                          sceneFlags.Selection(), sceneFlags.SecSelection());
   }

   // Find largest of the projected diagonals
   Double_t largestDiagonal = 0.0;
   for (UInt_t i = 0; i < boxViewportDiags.size(); i++) {
      if (boxViewportDiags[i] > largestDiagonal) {
         largestDiagonal = boxViewportDiags[i];
      }
   }

   // Pixel or less?
   if (largestDiagonal <= 1.0) {
      return TGLDrawFlags(sceneFlags.Style(), TGLDrawFlags::kLODPixel,
                          sceneFlags.Selection(), sceneFlags.SecSelection());
   }

   // TODO: Get real screen size - assuming 2000 pixel screen at present
   // Calculate a non-linear sizing hint for this shape based on diagonal.
   // Needs more experimenting with...
   UInt_t sizeLOD = static_cast<UInt_t>(pow(largestDiagonal,0.4) * 100.0 / pow(2000.0,0.4));

   // Factor in scene quality
   UInt_t shapeLOD = (sceneFlags.LOD() * sizeLOD) / 100;

   // Round LOD above 10 to nearest 10
   if (shapeLOD > 10) {
      Double_t quant = ((static_cast<Double_t>(shapeLOD)) + 0.5) / 10;
      shapeLOD = static_cast<UInt_t>(quant)*10;
   }
   // Round LOD below 10 to nearest 2
   else {
      Double_t quant = ((static_cast<Double_t>(shapeLOD)) + 0.5) / 2;
      shapeLOD = static_cast<UInt_t>(quant)*3;
   }

   if (shapeLOD > 100) {
      shapeLOD = 100;
   }

   return TGLDrawFlags(sceneFlags.Style(), shapeLOD,
                       sceneFlags.Selection(), sceneFlags.SecSelection());
}

//______________________________________________________________________________
void TGLPhysicalShape::InvokeContextMenu(TContextMenu & menu, UInt_t x, UInt_t y) const
{
   // Request creation of context menu on shape, attached to 'menu' at screen position
   // 'x' 'y'

   // Just defer to our logical at present
   fLogicalShape.InvokeContextMenu(menu, x, y);
}



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.