// @(#)root/gl:$Id$
// Author:  Matevz Tadel, Feb 2007

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

#include "TGLLightSet.h"

#include "TGLBoundingBox.h"
#include "TGLOrthoCamera.h"

#include "TGLIncludes.h"

//______________________________________________________________________
// TGLLightSet
//
// Encapsulates a set of lights for OpenGL.
//

ClassImp(TGLLightSet)

TGLLightSet::TGLLightSet() :
   TObject(),

   fLightState(kLightMask), // All on
   fUseSpecular(kTRUE),

   fFrontPower(0.4), fSidePower(0.7), fSpecularPower(0.8)
{
   // Constructor.
}

//______________________________________________________________________________
void TGLLightSet::ToggleLight(ELight light)
{
   // Toggle light on/off.

   if (light == kLightSpecular) {
      fUseSpecular = !fUseSpecular;
   } else if (light >= kLightMask) {
      Error("TGLLightSet::ToggleLight", "invalid light type");
      return;
   } else {
      fLightState ^= light;
   }
}

//______________________________________________________________________________
void TGLLightSet::SetLight(ELight light, Bool_t on)
{
   // Set a light on/off.

   if (light == kLightSpecular) {
      fUseSpecular = on;
   } else if (light >= kLightMask) {
      Error("TGLViewer::ToggleLight", "invalid light type");
      return;
   }

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

//______________________________________________________________________________
void TGLLightSet::StdSetupLights(const TGLBoundingBox& bbox,
                                 const TGLCamera     & camera, Bool_t debug)
{
   // Setup lights for current given bounding box and camera.
   // This is called by standard GL viewer.
   // Expects matrix-mode to be model-view.

   glPushMatrix();

   if (!bbox.IsEmpty())
   {
      // Calculate a sphere radius to arrange lights round
      Double_t lightRadius = bbox.Extents().Mag() * 2.9;
      Double_t sideLightsZ, frontLightZ;

      const TGLOrthoCamera* orthoCamera = dynamic_cast<const TGLOrthoCamera*>(&camera);
      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 =
            camera.FrustumPlane(TGLCamera::kNear).DistanceTo(camera.FrustumCenter())*0.7;
         frontLightZ = sideLightsZ;
      } else {
         // Perspective camera
         // Extract vector from camera eye point to center.
         // Camera must have been applied already.
         TGLVector3 eyeVector = camera.EyePoint() - camera.FrustumCenter();

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

      // Reset the modelview so static lights are placed in fixed eye space
      // This will destroy camera application.
      glLoadIdentity();

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

      Float_t specular = fUseSpecular ? fSpecularPower : 0.0f;
      const Float_t frontLightColor[] = { fFrontPower, fFrontPower, fFrontPower, 1.0f };
      const Float_t sideLightColor[]  = { fSidePower,  fSidePower,  fSidePower,  1.0f };
      const Float_t specLightColor[]  = { specular,    specular,    specular,    1.0f };

      glLightfv(GL_LIGHT0, GL_POSITION, pos0);
      glLightfv(GL_LIGHT0, GL_DIFFUSE,  frontLightColor);
      glLightfv(GL_LIGHT0, GL_SPECULAR, specLightColor);

      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 (debug)
         {
            // Lighting itself needs to be disable so a single one can show...!
            glDisable(GL_LIGHTING);
            Float_t position[4]; // Only float parameters for lights (no double)....
            glGetLightfv(GLenum(GL_LIGHT0 + light), GL_POSITION, position);
            Double_t size = bbox.Extents().Mag() / 10.0;
            TGLVertex3 dPosition(position[0], position[1], position[2]);
            TGLUtil::DrawSphere(dPosition, size, TGLUtil::fgYellow);
            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.
   glPopMatrix();
}
 TGLLightSet.cxx:1
 TGLLightSet.cxx:2
 TGLLightSet.cxx:3
 TGLLightSet.cxx:4
 TGLLightSet.cxx:5
 TGLLightSet.cxx:6
 TGLLightSet.cxx:7
 TGLLightSet.cxx:8
 TGLLightSet.cxx:9
 TGLLightSet.cxx:10
 TGLLightSet.cxx:11
 TGLLightSet.cxx:12
 TGLLightSet.cxx:13
 TGLLightSet.cxx:14
 TGLLightSet.cxx:15
 TGLLightSet.cxx:16
 TGLLightSet.cxx:17
 TGLLightSet.cxx:18
 TGLLightSet.cxx:19
 TGLLightSet.cxx:20
 TGLLightSet.cxx:21
 TGLLightSet.cxx:22
 TGLLightSet.cxx:23
 TGLLightSet.cxx:24
 TGLLightSet.cxx:25
 TGLLightSet.cxx:26
 TGLLightSet.cxx:27
 TGLLightSet.cxx:28
 TGLLightSet.cxx:29
 TGLLightSet.cxx:30
 TGLLightSet.cxx:31
 TGLLightSet.cxx:32
 TGLLightSet.cxx:33
 TGLLightSet.cxx:34
 TGLLightSet.cxx:35
 TGLLightSet.cxx:36
 TGLLightSet.cxx:37
 TGLLightSet.cxx:38
 TGLLightSet.cxx:39
 TGLLightSet.cxx:40
 TGLLightSet.cxx:41
 TGLLightSet.cxx:42
 TGLLightSet.cxx:43
 TGLLightSet.cxx:44
 TGLLightSet.cxx:45
 TGLLightSet.cxx:46
 TGLLightSet.cxx:47
 TGLLightSet.cxx:48
 TGLLightSet.cxx:49
 TGLLightSet.cxx:50
 TGLLightSet.cxx:51
 TGLLightSet.cxx:52
 TGLLightSet.cxx:53
 TGLLightSet.cxx:54
 TGLLightSet.cxx:55
 TGLLightSet.cxx:56
 TGLLightSet.cxx:57
 TGLLightSet.cxx:58
 TGLLightSet.cxx:59
 TGLLightSet.cxx:60
 TGLLightSet.cxx:61
 TGLLightSet.cxx:62
 TGLLightSet.cxx:63
 TGLLightSet.cxx:64
 TGLLightSet.cxx:65
 TGLLightSet.cxx:66
 TGLLightSet.cxx:67
 TGLLightSet.cxx:68
 TGLLightSet.cxx:69
 TGLLightSet.cxx:70
 TGLLightSet.cxx:71
 TGLLightSet.cxx:72
 TGLLightSet.cxx:73
 TGLLightSet.cxx:74
 TGLLightSet.cxx:75
 TGLLightSet.cxx:76
 TGLLightSet.cxx:77
 TGLLightSet.cxx:78
 TGLLightSet.cxx:79
 TGLLightSet.cxx:80
 TGLLightSet.cxx:81
 TGLLightSet.cxx:82
 TGLLightSet.cxx:83
 TGLLightSet.cxx:84
 TGLLightSet.cxx:85
 TGLLightSet.cxx:86
 TGLLightSet.cxx:87
 TGLLightSet.cxx:88
 TGLLightSet.cxx:89
 TGLLightSet.cxx:90
 TGLLightSet.cxx:91
 TGLLightSet.cxx:92
 TGLLightSet.cxx:93
 TGLLightSet.cxx:94
 TGLLightSet.cxx:95
 TGLLightSet.cxx:96
 TGLLightSet.cxx:97
 TGLLightSet.cxx:98
 TGLLightSet.cxx:99
 TGLLightSet.cxx:100
 TGLLightSet.cxx:101
 TGLLightSet.cxx:102
 TGLLightSet.cxx:103
 TGLLightSet.cxx:104
 TGLLightSet.cxx:105
 TGLLightSet.cxx:106
 TGLLightSet.cxx:107
 TGLLightSet.cxx:108
 TGLLightSet.cxx:109
 TGLLightSet.cxx:110
 TGLLightSet.cxx:111
 TGLLightSet.cxx:112
 TGLLightSet.cxx:113
 TGLLightSet.cxx:114
 TGLLightSet.cxx:115
 TGLLightSet.cxx:116
 TGLLightSet.cxx:117
 TGLLightSet.cxx:118
 TGLLightSet.cxx:119
 TGLLightSet.cxx:120
 TGLLightSet.cxx:121
 TGLLightSet.cxx:122
 TGLLightSet.cxx:123
 TGLLightSet.cxx:124
 TGLLightSet.cxx:125
 TGLLightSet.cxx:126
 TGLLightSet.cxx:127
 TGLLightSet.cxx:128
 TGLLightSet.cxx:129
 TGLLightSet.cxx:130
 TGLLightSet.cxx:131
 TGLLightSet.cxx:132
 TGLLightSet.cxx:133
 TGLLightSet.cxx:134
 TGLLightSet.cxx:135
 TGLLightSet.cxx:136
 TGLLightSet.cxx:137
 TGLLightSet.cxx:138
 TGLLightSet.cxx:139
 TGLLightSet.cxx:140
 TGLLightSet.cxx:141
 TGLLightSet.cxx:142
 TGLLightSet.cxx:143
 TGLLightSet.cxx:144
 TGLLightSet.cxx:145
 TGLLightSet.cxx:146
 TGLLightSet.cxx:147
 TGLLightSet.cxx:148
 TGLLightSet.cxx:149
 TGLLightSet.cxx:150
 TGLLightSet.cxx:151
 TGLLightSet.cxx:152
 TGLLightSet.cxx:153
 TGLLightSet.cxx:154
 TGLLightSet.cxx:155
 TGLLightSet.cxx:156
 TGLLightSet.cxx:157
 TGLLightSet.cxx:158
 TGLLightSet.cxx:159
 TGLLightSet.cxx:160
 TGLLightSet.cxx:161
 TGLLightSet.cxx:162
 TGLLightSet.cxx:163
 TGLLightSet.cxx:164
 TGLLightSet.cxx:165
 TGLLightSet.cxx:166
 TGLLightSet.cxx:167
 TGLLightSet.cxx:168
 TGLLightSet.cxx:169
 TGLLightSet.cxx:170
 TGLLightSet.cxx:171