// @(#)root/gl:$Name: $:$Id: TViewerOpenGL.cxx,v 1.69 2005/07/14 19:13:04 brun Exp $
// Author: Timur Pocheptsov 03/08/2004
/*************************************************************************
* 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 "TViewerOpenGL.h"
#include "TPluginManager.h"
#include "TRootHelpDialog.h"
#include "TContextMenu.h"
#include "KeySymbols.h"
#include "TGShutter.h"
#include "TGLKernel.h"
#include "TVirtualGL.h"
#include "TGButton.h"
#include "TGClient.h"
#include "TGCanvas.h"
#include "HelpText.h"
#include "Buttons.h"
#include "TAtt3D.h"
#include "TGMenu.h"
#include "TColor.h"
#include "TMath.h"
#include "TSystem.h"
#include "TGLSceneObject.h"
#include "TGLRenderArea.h"
#include "TGLEditor.h"
#include "TGLCamera.h"
#include "TBuffer3D.h"
#include "TBuffer3DTypes.h"
#include "TVirtualPad.h"
#include "TGLLogicalShape.h"
#include "TGLPhysicalShape.h"
#include "TGLDisplayListCache.h"
#include "TGLStopwatch.h"
#include "TObject.h"
#include "gl2ps.h"
#include <assert.h>
const char gHelpViewerOpenGL[] = "\
PRESS \n\
\tw\t--- wireframe mode\n\
\tr\t--- filled polygons mode\n\
\tj\t--- zoom in\n\
\tk\t--- zoom out\n\n\
\tArrow Keys\tpan (truck) across scene\n\
You can ROTATE (ORBIT) the scene by holding the left \n\
mouse button and moving the mouse (pespective camera only).\n\
You can PAN (TRUCK) the camera using the middle mouse\n\
button or arrow keys.\n\
You can ZOOM (DOLLY) the camera by dragging side\n\
to side holding the right mouse button or using the\n\
mouse wheel.\n\
RESET the camera by double clicking any button\n\
SELECT an object with Shift+Left mouse button click.\n\
MOVE the object using Shift+Mid mouse drag.\n\
Invoked the CONTEXT menu with Shift+Right mouse click.\n\
PROJECTIONS\n\n\
You can select the different plane projections\n\
in \"Projections\" menu.\n\n\
COLOR\n\n\
After you selected an object or a light source,\n\
you can modify object's material and light\n\
source color.\n\n\
\tLIGHT SOURCES.\n\n\
\tThere are two pickable light sources in\n\
\tthe current implementation. They are shown as\n\
\tspheres. Each light source has three light\n\
\tcomponents : DIFFUSE, AMBIENT, SPECULAR.\n\
\tEach of this components is defined by the\n\
\tamounts of red, green and blue light it emits.\n\
\tYou can EDIT this parameters:\n\
\t1. Select light source sphere.\n" //hehe, too long string literal :)))
" \t2. Select light component you want to modify\n\
\t by pressing one of radio buttons.\n\
\t3. Change RGB by moving sliders\n\n\
\tMATERIAL\n\n\
\tObject's material is specified by the percentage\n\
\tof red, green, blue light it reflects. A surface can\n\
\treflect diffuse, ambient and specular light. \n\
\tA surface has two additional parameters: EMISSION\n\
\t- you can make surface self-luminous; SHININESS -\n\
\tmodifying this parameter you can change surface\n\
\thighlights.\n\
\tSometimes changes are not visible, or light\n\
\tsources seem not to work - you should understand\n\
\tthe meaning of diffuse, ambient etc. light and material\n\
\tcomponents. For example, if you define material, wich has\n\
\tdiffuse component (1., 0., 0.) and you have a light source\n\
\twith diffuse component (0., 1., 0.) - you surface does not\n\
\treflect diffuse light from this source. For another example\n\
\t- the color of highlight on the surface is specified by:\n\
\tlight's specular component, material specular component.\n\
\tAt the top of the color editor there is a small window\n\
\twith sphere. When you are editing surface material,\n\
\tyou can see this material applyed to sphere.\n\
\tWhen edit light source, you see this light reflected\n\
\tby sphere whith DIFFUSE and SPECULAR components\n\
\t(1., 1., 1.).\n\n\
OBJECT'S GEOMETRY\n\n\
You can edit object's location and stretch it by entering\n\
desired values in respective number entry controls.\n\n"
" SCENE PROPERTIES\n\n\
You can add clipping plane by clicking the checkbox and\n\
specifying the plane's equation A*x+B*y+C*z+D=0.";
enum EGLViewerCommands {
kGLHelpAbout,
kGLHelpOnViewer,
kGLXOY,
kGLXOZ,
kGLYOZ,
kGLPersp,
kGLPrintEPS_SIMPLE,
kGLPrintEPS_BSP,
kGLPrintPDF_SIMPLE,
kGLPrintPDF_BSP,
kGLExit
};
ClassImp(TViewerOpenGL)
const Int_t TViewerOpenGL::fgInitX = 0;
const Int_t TViewerOpenGL::fgInitY = 0;
const Int_t TViewerOpenGL::fgInitW = 780;
const Int_t TViewerOpenGL::fgInitH = 670;
int format = GL2PS_EPS;
int sortgl = GL2PS_BSP_SORT;
//______________________________________________________________________________
TViewerOpenGL::TViewerOpenGL(TVirtualPad * pad) :
TGMainFrame(gClient->GetDefaultRoot(), fgInitW, fgInitH),
fMainFrame(0), fV1(0), fV2(0), fShutter(0), fShutItem1(0), fShutItem2(0),
fShutItem3(0), fShutItem4(0), fL1(0), fL2(0), fL3(0), fL4(0),
fCanvasLayout(0), fMenuBar(0), fFileMenu(0), fViewMenu(0), fHelpMenu(0),
fMenuBarLayout(0), fMenuBarItemLayout(0), fMenuBarHelpLayout(0),
fContextMenu(0), fCanvasWindow(0), fCanvasContainer(0),
fColorEditor(0), fGeomEditor(0), fSceneEditor(0), fLightEditor(0),
fAction(kNone), fStartPos(0,0), fLastPos(0,0), fActiveButtonID(0),
fInternalRebuild(kFALSE), fAcceptedAllPhysicals(kTRUE),
fInternalPIDs(kFALSE), fNextInternalPID(1), // 0 reserved
fLightMask(0x1b), fPad(pad), fComposite(0), fCSLevel(0),
fAcceptedPhysicals(0), fRejectedPhysicals(0)
{
// Create OpenGL viewer.
static Bool_t init = kFALSE;
if (!init) {
TPluginHandler *h;
if ((h = gROOT->GetPluginManager()->FindHandler("TVirtualGLImp"))) {
if (h->LoadPlugin() == -1)
return;
TVirtualGLImp * imp = (TVirtualGLImp *) h->ExecPlugin(0);
new TGLKernel(imp);
}
init = kTRUE;
}
CreateViewer();
}
//______________________________________________________________________________
void TViewerOpenGL::CreateViewer()
{
// Menus creation
fFileMenu = new TGPopupMenu(fClient->GetRoot());
fFileMenu->AddEntry("&Print EPS", kGLPrintEPS_SIMPLE);
fFileMenu->AddEntry("&Print EPS (High quality)", kGLPrintEPS_BSP);
fFileMenu->AddEntry("&Print PDF", kGLPrintPDF_SIMPLE);
fFileMenu->AddEntry("&Print PDF (High quality)", kGLPrintPDF_BSP);
fFileMenu->AddEntry("&Exit", kGLExit);
fFileMenu->Associate(this);
fViewMenu = new TGPopupMenu(fClient->GetRoot());
fViewMenu->AddEntry("&XOY plane", kGLXOY);
fViewMenu->AddEntry("XO&Z plane", kGLXOZ);
fViewMenu->AddEntry("&YOZ plane", kGLYOZ);
fViewMenu->AddEntry("&Perspective view", kGLPersp);
fViewMenu->Associate(this);
fHelpMenu = new TGPopupMenu(fClient->GetRoot());
fHelpMenu->AddEntry("&About ROOT...", kGLHelpAbout);
fHelpMenu->AddSeparator();
fHelpMenu->AddEntry("Help on OpenGL Viewer...", kGLHelpOnViewer);
fHelpMenu->Associate(this);
// Create menubar layout hints
fMenuBarLayout = new TGLayoutHints(kLHintsTop | kLHintsLeft | kLHintsExpandX, 0, 0, 1, 1);
fMenuBarItemLayout = new TGLayoutHints(kLHintsTop | kLHintsLeft, 0, 4, 0, 0);
fMenuBarHelpLayout = new TGLayoutHints(kLHintsTop | kLHintsRight);
// Create menubar
fMenuBar = new TGMenuBar(this, 1, 1, kHorizontalFrame | kRaisedFrame);
fMenuBar->AddPopup("&File", fFileMenu, fMenuBarItemLayout);
fMenuBar->AddPopup("&Projections", fViewMenu, fMenuBarItemLayout);
fMenuBar->AddPopup("&Help", fHelpMenu, fMenuBarHelpLayout);
AddFrame(fMenuBar, fMenuBarLayout);
// Frames creation
fMainFrame = new TGCompositeFrame(this, 100, 100, kHorizontalFrame | kRaisedFrame);
fV1 = new TGVerticalFrame(fMainFrame, 150, 10, kSunkenFrame | kFixedWidth);
fShutter = new TGShutter(fV1, kSunkenFrame | kFixedWidth);
fShutItem1 = new TGShutterItem(fShutter, new TGHotString("Color"), 5001);
fShutItem2 = new TGShutterItem(fShutter, new TGHotString("Object's geometry"), 5002);
fShutItem3 = new TGShutterItem(fShutter, new TGHotString("Scene"), 5003);
fShutItem4 = new TGShutterItem(fShutter, new TGHotString("Lights"), 5004);
fShutter->AddItem(fShutItem1);
fShutter->AddItem(fShutItem2);
fShutter->AddItem(fShutItem3);
fShutter->AddItem(fShutItem4);
TGCompositeFrame *shutCont = (TGCompositeFrame *)fShutItem1->GetContainer();
fColorEditor = new TGLColorEditor(shutCont, this);
fL4 = new TGLayoutHints(kLHintsTop | kLHintsCenterX | kLHintsExpandX | kLHintsExpandY, 2, 5, 1, 2);
shutCont->AddFrame(fColorEditor, fL4);
fV1->AddFrame(fShutter, fL4);
fL1 = new TGLayoutHints(kLHintsLeft | kLHintsExpandY, 2, 0, 2, 2);
fMainFrame->AddFrame(fV1, fL1);
shutCont = (TGCompositeFrame *)fShutItem2->GetContainer();
fGeomEditor = new TGLGeometryEditor(shutCont, this);
shutCont->AddFrame(fGeomEditor, fL4);
shutCont = (TGCompositeFrame *)fShutItem3->GetContainer();
fSceneEditor = new TGLSceneEditor(shutCont, this);
shutCont->AddFrame(fSceneEditor, fL4);
shutCont = (TGCompositeFrame *)fShutItem4->GetContainer();
fLightEditor = new TGLLightEditor(shutCont, this);
shutCont->AddFrame(fLightEditor, fL4);
fV2 = new TGVerticalFrame(fMainFrame, 10, 10, kSunkenFrame);
fL3 = new TGLayoutHints(kLHintsRight | kLHintsExpandX | kLHintsExpandY,0,2,2,2);
fMainFrame->AddFrame(fV2, fL3);
fCanvasWindow = new TGCanvas(fV2, 10, 10, kSunkenFrame | kDoubleBorder);
fCanvasContainer = new TGLRenderArea(fCanvasWindow->GetViewPort()->GetId(), fCanvasWindow->GetViewPort());
TGLWindow * glWin = fCanvasContainer->GetGLWindow();
glWin->Connect("HandleButton(Event_t*)", "TViewerOpenGL", this, "HandleContainerButton(Event_t*)");
glWin->Connect("HandleDoubleClick(Event_t*)", "TViewerOpenGL", this, "HandleContainerDoubleClick(Event_t*)");
glWin->Connect("HandleKey(Event_t*)", "TViewerOpenGL", this, "HandleContainerKey(Event_t*)");
glWin->Connect("HandleMotion(Event_t*)", "TViewerOpenGL", this, "HandleContainerMotion(Event_t*)");
glWin->Connect("HandleExpose(Event_t*)", "TViewerOpenGL", this, "HandleContainerExpose(Event_t*)");
glWin->Connect("HandleConfigureNotify(Event_t*)", "TViewerOpenGL", this, "HandleContainerConfigure(Event_t*)");
fCanvasWindow->SetContainer(glWin);
fCanvasLayout = new TGLayoutHints(kLHintsExpandX | kLHintsExpandY);
fV2->AddFrame(fCanvasWindow, fCanvasLayout);
AddFrame(fMainFrame, fCanvasLayout);
SetWindowName("OpenGL experimental viewer");
SetClassHints("GLViewer", "GLViewer");
SetMWMHints(kMWMDecorAll, kMWMFuncAll, kMWMInputModeless);
MapSubwindows();
Resize(GetDefaultSize());
MoveResize(fgInitX, fgInitY, fgInitW, fgInitH);
SetWMPosition(fgInitX, fgInitY);
Show();
}
//______________________________________________________________________________
TViewerOpenGL::~TViewerOpenGL()
{
delete fFileMenu;
delete fViewMenu;
delete fHelpMenu;
delete fMenuBar;
delete fMenuBarLayout;
delete fMenuBarHelpLayout;
delete fMenuBarItemLayout;
delete fCanvasContainer;
delete fCanvasWindow;
delete fCanvasLayout;
delete fV1;
delete fV2;
delete fMainFrame;
delete fL1;
delete fL2;
delete fL3;
delete fL4;
delete fContextMenu;
delete fShutter;
delete fShutItem1;
delete fShutItem2;
delete fShutItem3;
delete fShutItem4;
}
//______________________________________________________________________________
void TViewerOpenGL::InitGL()
{
// Actual GL window/context creation should have already been done in CreateViewer()
assert(!fInitGL && fCanvasContainer && fCanvasContainer->GetGLWindow());
// 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);
// Calculate light source positions
// Arrange round an expanded sphere of scene BB
// TODO: These need to be positioned before each scene draw, after the camera MV translation so that they don't shift relative to objects.
TGLBoundingBox box = fScene.BoundingBox();
Double_t radius = box.Extents().Mag() * 4.0;
// 0: Front
// 1: Right
// 2: Bottom
// 3: Left
// 4: Top
Float_t pos0[] = {0.0, 0.0, 0.0, 1.0};
Float_t pos1[] = {box.Center().X() + radius, box.Center().Y() , -box.Center().Z() - radius, 1.0};
Float_t pos2[] = {box.Center().X() , box.Center().Y() - radius, -box.Center().Z() - radius, 1.0};
Float_t pos3[] = {box.Center().X() - radius, box.Center().Y() , -box.Center().Z() - radius, 1.0};
Float_t pos4[] = {box.Center().X() , box.Center().Y() + radius, -box.Center().Z() - radius, 1.0};
Float_t whiteCol[] = {0.7, 0.7, 0.7, 1.0};
gVirtualGL->GLLight(kLIGHT0, kPOSITION, pos0);
gVirtualGL->GLLight(kLIGHT0, kDIFFUSE, whiteCol);
gVirtualGL->GLLight(kLIGHT1, kPOSITION, pos1);
gVirtualGL->GLLight(kLIGHT1, kDIFFUSE, whiteCol);
gVirtualGL->GLLight(kLIGHT2, kPOSITION, pos2);
gVirtualGL->GLLight(kLIGHT2, kDIFFUSE, whiteCol);
gVirtualGL->GLLight(kLIGHT3, kPOSITION, pos3);
gVirtualGL->GLLight(kLIGHT3, kDIFFUSE, whiteCol);
gVirtualGL->GLLight(kLIGHT4, kPOSITION, pos4);
gVirtualGL->GLLight(kLIGHT4, kDIFFUSE, whiteCol);
if (fLightMask & 1) gVirtualGL->EnableGL(kLIGHT4);
if (fLightMask & 2) gVirtualGL->EnableGL(kLIGHT1);
if (fLightMask & 4) gVirtualGL->EnableGL(kLIGHT2);
if (fLightMask & 8) gVirtualGL->EnableGL(kLIGHT3);
if (fLightMask & 16) gVirtualGL->EnableGL(kLIGHT0);
TGLUtil::CheckError();
fInitGL = kTRUE;
}
//______________________________________________________________________________
void TViewerOpenGL::Invalidate(UInt_t redrawLOD)
{
if (fScene.IsLocked()) {
Error("TViewerOpenGL::Invalidate", "scene is %s", TGLScene::LockName(fScene.CurrentLock()));
return;
}
TGLViewer::Invalidate(redrawLOD);
// Mark the window as requiring a redraw - the GUI thread
// will call our DoRedraw() method
fClient->NeedRedraw(this);
if (gDebug>3) {
Info("TViewerOpenGL::Invalidate", "invalidated at %d LOD", fNextSceneLOD);
}
}
//______________________________________________________________________________
void TViewerOpenGL::MakeCurrent() const
{
fCanvasContainer->GetGLWindow()->MakeCurrent();
}
//______________________________________________________________________________
void TViewerOpenGL::SwapBuffers() const
{
if (fScene.CurrentLock() != TGLScene::kDrawLock &&
fScene.CurrentLock() != TGLScene::kSelectLock) {
Error("TViewerOpenGL::MakeCurrent", "scene is %s", TGLScene::LockName(fScene.CurrentLock()));
}
fCanvasContainer->GetGLWindow()->SwapBuffers();
}
//______________________________________________________________________________
Bool_t TViewerOpenGL::HandleContainerEvent(Event_t *event)
{
if (event->fType == kFocusIn) {
assert(fAction == kNone);
fAction = kNone;
}
if (event->fType == kFocusOut) {
fAction = kNone;
}
return kTRUE;
}
//______________________________________________________________________________
Bool_t TViewerOpenGL::HandleContainerButton(Event_t *event)
{
if (fScene.IsLocked()) {
if (gDebug>2) {
Info("TViewerOpenGL::HandleContainerButton", "ignored - scene is %s", TGLScene::LockName(fScene.CurrentLock()));
}
return kFALSE;
}
// 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) {
DoSelect(event, kFALSE); // without context menu
// TODO: If no selection start a box select
} else {
fAction = kRotate;
grabPointer = kTRUE;
}
break;
}
// MID mouse button
case(kButton2): {
if (event->fState & kKeyShiftMask) {
DoSelect(event, kFALSE); // without context menu
// 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) {
DoSelect(event, kTRUE); // with context menu
} 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
case(kButton4): {
// Zoom out (adjust camera FOV)
if (CurrentCamera().Zoom(-30, event->fState & kKeyControlMask,
event->fState & kKeyShiftMask)) { //TODO : val static const somewhere
Invalidate();
}
break;
}
case(kButton5): {
// Zoom in (adjust camera FOV)
if (CurrentCamera().Zoom(+30, event->fState & kKeyControlMask,
event->fState & kKeyShiftMask)) { //TODO : val static const somewhere
Invalidate();
}
break;
}
}
fAction = kNone;
}
return kTRUE;
}
//______________________________________________________________________________
Bool_t TViewerOpenGL::HandleContainerDoubleClick(Event_t *event)
{
if (fScene.IsLocked()) {
if (gDebug>3) {
Info("TViewerOpenGL::HandleContainerDoubleClick", "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;
Invalidate();
}
return kTRUE;
}
//______________________________________________________________________________
Bool_t TViewerOpenGL::HandleContainerConfigure(Event_t *event)
{
if (fScene.IsLocked()) {
if (gDebug>3) {
Info("TViewerOpenGL::HandleContainerConfigure", "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 TViewerOpenGL::HandleContainerKey(Event_t *event)
{
if (fScene.IsLocked()) {
if (gDebug>3) {
Info("TViewerOpenGL::HandleContainerKey", "ignored - scene is %s", TGLScene::LockName(fScene.CurrentLock()));
}
return kFALSE;
}
char tmp[10] = {0};
UInt_t keysym = 0;
Float_t black[] = {0.f, 0.f, 0.f, 1.f};
Float_t white[] = {1.f, 1.f, 1.f, 1.f};
gVirtualX->LookupString(event, tmp, sizeof(tmp), keysym);
Bool_t invalidate = kFALSE;
switch (keysym) {
case kKey_Plus:
case kKey_J:
case kKey_j:
invalidate = CurrentCamera().Dolly(10, event->fState & kKeyControlMask,
event->fState & kKeyShiftMask); //TODO : val static const somewhere
break;
case kKey_Minus:
case kKey_K:
case kKey_k:
invalidate = CurrentCamera().Dolly(-10, event->fState & kKeyControlMask,
event->fState & kKeyShiftMask); //TODO : val static const somewhere
break;
case kKey_R:
case kKey_r:
gVirtualGL->EnableGL(kLIGHTING);
gVirtualGL->EnableGL(kCULL_FACE);
gVirtualGL->PolygonGLMode(kFRONT, kFILL);
gVirtualGL->ClearGLColor(black[0], black[1], black[2], black[3]);
fScene.SetDrawMode(TGLScene::kFill);
invalidate = kTRUE;
break;
case kKey_W:
case kKey_w:
gVirtualGL->DisableGL(kCULL_FACE);
gVirtualGL->DisableGL(kLIGHTING);
gVirtualGL->PolygonGLMode(kFRONT_AND_BACK, kLINE);
gVirtualGL->ClearGLColor(black[0], black[1], black[2], black[3]);
fScene.SetDrawMode(TGLScene::kWireFrame);
invalidate = kTRUE;
break;
case kKey_T:
case kKey_t:
gVirtualGL->EnableGL(kLIGHTING);
gVirtualGL->EnableGL(kCULL_FACE);
gVirtualGL->PolygonGLMode(kFRONT, kFILL);
gVirtualGL->ClearGLColor(white[0], white[1], white[2], white[3]);
fScene.SetDrawMode(TGLScene::kOutline);
invalidate = kTRUE;
break;
case kKey_Up:
invalidate = CurrentCamera().Truck(fViewport.CenterX(), fViewport.CenterY(), 0, 5);
break;
case kKey_Down:
invalidate = CurrentCamera().Truck(fViewport.CenterX(), fViewport.CenterY(), 0, -5);
break;
case kKey_Left:
invalidate = CurrentCamera().Truck(fViewport.CenterX(), fViewport.CenterY(), -5, 0);
break;
case kKey_Right:
invalidate = CurrentCamera().Truck(fViewport.CenterX(), fViewport.CenterY(), 5, 0);
break;
// Toggle debugging mode
case kKey_D:
case kKey_d:
fDebugMode = !fDebugMode;
invalidate = 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 (invalidate) {
Invalidate();
}
return kTRUE;
}
//______________________________________________________________________________
Bool_t TViewerOpenGL::HandleContainerMotion(Event_t *event)
{
if (fScene.IsLocked()) {
if (gDebug>3) {
Info("TViewerOpenGL::HandleContainerMotion", "ignored - scene is %s", TGLScene::LockName(fScene.CurrentLock()));
}
return kFALSE;
}
if (!event) {
return kFALSE;
}
Bool_t invalidate = 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) {
invalidate = CurrentCamera().Rotate(xDelta, -yDelta);
} else if (fAction == kTruck) {
invalidate = CurrentCamera().Truck(event->fX, fViewport.Y() - event->fY, xDelta, -yDelta);
} else if (fAction == kDolly) {
invalidate = CurrentCamera().Dolly(xDelta, event->fState & kKeyControlMask,
event->fState & kKeyShiftMask);
} else if (fAction == kDrag) {
TGLPhysicalShape * selected = fScene.GetSelected();
if (selected) {
TGLVector3 shift = CurrentCamera().ProjectedShift(selected->BoundingBox().Center(), xDelta, -yDelta);
selected->Shift(shift);
fGeomEditor->SetCenter(selected->GetTranslation().CArr());
fScene.SelectedModified();
Invalidate();
}
}
fLastPos.fX = event->fX;
fLastPos.fY = event->fY;
if (invalidate) {
Invalidate();
}
return kTRUE;
}
//______________________________________________________________________________
Bool_t TViewerOpenGL::HandleContainerExpose(Event_t *)
{
if (fScene.IsLocked()) {
if (gDebug>3) {
Info("TViewerOpenGL::HandleContainerExpose", "ignored - scene is %s", TGLScene::LockName(fScene.CurrentLock()));
}
return kFALSE;
}
Invalidate(kHigh);
return kTRUE;
}
//______________________________________________________________________________
void TViewerOpenGL::DoSelect(Event_t *event, Bool_t invokeContext)
{
// 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(event->fX, event->fY, 3, 3); // TODO: Constant somewhere
gVirtualGL->SelectViewer(this, &selectRect);
// Do this regardless of whether selection actually changed - safe and
// the context menu may need to be invoked anyway
TGLPhysicalShape * selected = fScene.GetSelected();
if (selected) {
fColorEditor->SetRGBA(selected->GetColor());
fGeomEditor->SetCenter(selected->GetTranslation().CArr());
fGeomEditor->SetScale(selected->GetScale().CArr());
if (invokeContext) {
if (!fContextMenu) fContextMenu = new TContextMenu("glcm", "glcm");
// Defer creating the context menu to the actual object
selected->InvokeContextMenu(*fContextMenu, event->fXRoot, event->fYRoot);
}
} else { // No selection
fColorEditor->Disable();
fGeomEditor->Disable();
}
}
//______________________________________________________________________________
void TViewerOpenGL::Show()
{
if (fScene.IsLocked()) {
Error("TViewerOpenGL::Show", "scene is %s", TGLScene::LockName(fScene.CurrentLock()));
}
MapRaised();
// Must NOT Invalidate() here as for some reason it throws the win32
// GL kernel impl into a blank locked state? Poss related to having an
// empty viewer - nothing drawn. TODO: Investigate why....
}
//______________________________________________________________________________
void TViewerOpenGL::CloseWindow()
{
fPad->ReleaseViewer3D();
TTimer::SingleShot(50, IsA()->GetName(), this, "ReallyDelete()");
}
//______________________________________________________________________________
void TViewerOpenGL::DoRedraw()
{
// Take draw lock on scene immediately we enter here - it is released
// in the other (drawing) drawing thread - see TGLViewer::Draw()
// Removed when gVirtualGL removed
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("TViewerOpenGL::DoRedraw", "scene drawlocked - requesting another draw");
}
fRedrawTimer->RequestDraw(100, fNextSceneLOD);
return;
}
if (gDebug>3) {
Info("TViewerOpenGL::DoRedraw", "request draw at %d LOD on this = %d", fNextSceneLOD, this);
}
// TODO: Check only the GUI thread ever enters here, DoSelect() and PrintObjects().
// Then TVirtualGL and TGLKernel can be obsoleted and all GL context work done
// in GUI thread.
gVirtualGL->DrawViewer(this);
}
//______________________________________________________________________________
void TViewerOpenGL::PrintObjects()
{
if (fScene.IsLocked()) {
if (gDebug>3) {
Info("TViewerOpenGL::PrintObjects", "ignored - scene is %s", TGLScene::LockName(fScene.CurrentLock()));
}
return;
}
// Generates a PostScript or PDF output of the OpenGL scene. They are vector
// graphics files and can be huge and long to generate.
TGLBoundingBox sceneBox = fScene.BoundingBox();
gVirtualGL->PrintObjects(format, sortgl, this, fCanvasContainer->GetGLWindow(),
sceneBox.Extents().Mag(), sceneBox.Center().Y(), sceneBox.Center().Z());
}
//______________________________________________________________________________
Bool_t TViewerOpenGL::ProcessMessage(Long_t msg, Long_t parm1, Long_t)
{
if (fScene.IsLocked()) {
if (gDebug>3) {
Info("TViewerOpenGL::ProcessMessage", "ignored - scene is %s", TGLScene::LockName(fScene.CurrentLock()));
}
return kFALSE;
}
switch (GET_MSG(msg)) {
case kC_COMMAND:
switch (GET_SUBMSG(msg)) {
case kCM_BUTTON:
case kCM_MENU:
switch (parm1) {
case kGLHelpAbout: {
char str[32];
sprintf(str, "About ROOT %s...", gROOT->GetVersion());
TRootHelpDialog * hd = new TRootHelpDialog(this, str, 600, 400);
hd->SetText(gHelpAbout);
hd->Popup();
break;
}
case kGLHelpOnViewer: {
TRootHelpDialog * hd = new TRootHelpDialog(this, "Help on GL Viewer...", 600, 400);
hd->SetText(gHelpViewerOpenGL);
hd->Popup();
break;
}
case kGLPrintEPS_SIMPLE:
format = GL2PS_EPS;
sortgl = GL2PS_SIMPLE_SORT;
PrintObjects();
break;
case kGLPrintEPS_BSP:
format = GL2PS_EPS;
sortgl = GL2PS_BSP_SORT;
PrintObjects();
break;
case kGLPrintPDF_SIMPLE:
format = GL2PS_PDF;
sortgl = GL2PS_SIMPLE_SORT;
PrintObjects();
break;
case kGLPrintPDF_BSP:
format = GL2PS_PDF;
sortgl = GL2PS_BSP_SORT;
PrintObjects();
break;
case kGLXOY:
SetCurrentCamera(kXOY);
break;
case kGLXOZ:
SetCurrentCamera(kXOZ);
break;
case kGLYOZ:
SetCurrentCamera(kYOZ);
break;
case kGLPersp:
SetCurrentCamera(kPerspective);
break;
case kGLExit:
CloseWindow();
break;
default:
break;
}
default:
break;
}
default:
break;
}
return kTRUE;
}
//______________________________________________________________________________
void TViewerOpenGL::ModifyScene(Int_t wid)
{
if (!fScene.TakeLock(TGLScene::kModifyLock)) {
return;
}
MakeCurrent();
TGLPhysicalShape * selected = fScene.GetSelected();
switch (wid) {
case kTBa:
if (selected) {
selected->SetColor(fColorEditor->GetRGBA());
}
break;
case kTBaf:
if (selected) {
fScene.SetPhysicalsColorByLogical(selected->GetLogical().ID(),
fColorEditor->GetRGBA());
}
break;
case kTBa1:
{
if (selected) {
TGLVertex3 trans;
TGLVector3 scale;
fGeomEditor->GetObjectData(trans.Arr(), scale.Arr());
selected->SetTranslation(trans);
selected->SetScale(scale);
fScene.SelectedModified();
}
}
break;
case kTBda:
fDrawAxes = !fDrawAxes;
break;
case kTBcp:
fUseClipPlane = !fUseClipPlane;
case kTBcpm:
{
Double_t eqn[4] = {0.};
fSceneEditor->GetPlaneEqn(eqn);
fClipPlane.Set(eqn, kFALSE); // Don't normalise
break;
}
case kTBTop:
if ((fLightMask ^= 1) & 1) gVirtualGL->EnableGL(kLIGHT4);
else gVirtualGL->DisableGL(kLIGHT4);
break;
case kTBRight:
if ((fLightMask ^= 2) & 2) gVirtualGL->EnableGL(kLIGHT1);
else gVirtualGL->DisableGL(kLIGHT1);
break;
case kTBBottom:
if ((fLightMask ^= 4) & 4) gVirtualGL->EnableGL(kLIGHT2);
else gVirtualGL->DisableGL(kLIGHT2);
break;
case kTBLeft:
if ((fLightMask ^= 8) & 8) gVirtualGL->EnableGL(kLIGHT3);
else gVirtualGL->DisableGL(kLIGHT3);
break;
case kTBFront:
if ((fLightMask ^= 16) & 16) gVirtualGL->EnableGL(kLIGHT0);
else gVirtualGL->DisableGL(kLIGHT0);
break;
}
fScene.ReleaseLock(TGLScene::kModifyLock);
Invalidate();
}
//______________________________________________________________________________
Bool_t TViewerOpenGL::PreferLocalFrame() const
{
return kTRUE;
}
//______________________________________________________________________________
void TViewerOpenGL::BeginScene()
{
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 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("TViewerOpenGL::BeginScene", "destroyed %d physicals %d logicals in %f msec",
destroyedPhysicals, destroyedLogicals, stopwatch.End());
fScene.Dump();
}
}
//______________________________________________________________________________
void TViewerOpenGL::EndScene()
{
fScene.ReleaseLock(TGLScene::kModifyLock);
// External scene build
if (!fInternalRebuild) {
// Setup camera unless scene is empty
if (!fScene.BoundingBox().IsEmpty()) {
SetupCameras(fScene.BoundingBox());
}
Invalidate();
} else if (fInternalRebuild) {
fInternalRebuild = kFALSE;
}
if (gDebug>2 || fDebugMode) {
Info("TViewerOpenGL::EndScene", "Added %d, rejected %d physicals, accepted all:%s", fAcceptedPhysicals,
fRejectedPhysicals, fAcceptedAllPhysicals ? "Yes":"No");
fScene.Dump();
}
}
//______________________________________________________________________________
Bool_t TViewerOpenGL::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("TViewerOpenGL::RebuildScene", "not required - all physicals previous accepted (FORCED anyway)");
}
else {
if (gDebug>3) {
Info("TViewerOpenGL::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("TViewerOpenGL::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("TViewerOpenGL::RebuildScene", "required");
}
fInternalRebuild = kTRUE;
TGLStopwatch timer;
if (gDebug>2 || fDebugMode) {
timer.Start();
}
// 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("TViewerOpenGL::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 TViewerOpenGL::AddObject(const TBuffer3D & buffer, Bool_t * addChildren)
{
// Add an object to the viewer, using internal physical IDs
// 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 TViewerOpenGL::AddObject(UInt_t physicalID, const TBuffer3D & buffer, Bool_t * addChildren)
{
// Add an object to the viewer, using an external physical ID.
// 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("TViewerOpenGL::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("TViewerOpenGL::AddObject", "invalid next physical ID - mix of internal + external IDs?");
return TBuffer3D::kNone;
}
if (addChildren) {
*addChildren = kFALSE;
}
// Scene should be modify locked
if (fScene.CurrentLock() != TGLScene::kModifyLock) {
Error("TViewerOpenGL::AddObject", "expected scene to be in mofifed locked");
// TODO: For the moment live with this - DrawOverlap() problems to discuss with Andrei
// Just reject as pad will redraw anyway
// assert(kFALSE);
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?
TGLLogicalShape * logical = fScene.FindLogical(reinterpret_cast<ULong_t>(buffer.fID));
TGLPhysicalShape * physical = fScene.FindPhysical(physicalID);
// 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) {
assert(logical); // Have physical - should have logical
if (addChildren) {
// For internal PID we request all children even if we will reject them.
// This ensures PID always represent same external entity.
if (fInternalPIDs) {
*addChildren = kTRUE;
} else
// For external PIDs we check child interest as we may have reject children previously
// with a different camera configuration
{
*addChildren = CurrentCamera().OfInterest(physical->BoundingBox());
}
}
// 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 {
// First test interest in camera - requires a bounding box
TGLBoundingBox box;
// If already have logical use it's BB
if (logical) {
box = logical->BoundingBox();
//assert(!box.IsEmpty());
}
// else if bounding box in buffer valid use this
else if (buffer.SectionsValid(TBuffer3D::kBoundingBox)) {
box.Set(buffer.fBBVertex);
//assert(!box.IsEmpty());
// 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);
//assert(!box.IsEmpty());
}
// Box is valid?
if (!box.IsEmpty()) {
// Test transformed box with camera
box.Transform(TGLMatrix(buffer.fLocalMaster));
Bool_t ofInterest = CurrentCamera().OfInterest(box);
if (addChildren) {
// For internal PID we request all children even if we will reject them.
// This ensures PID always represent same external entity.
if (fInternalPIDs) {
*addChildren = kTRUE;
} else
// For external PID request children if physical of interest
{
*addChildren = ofInterest;
}
}
// Physical is of interest?
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?
Int_t extraSections = ValidateObjectBuffer(buffer,
logical == 0); // Need logical?
if (extraSections != TBuffer3D::kNone) {
return extraSections;
} else {
lastPID = physicalID; // Will not to re-test interest
}
}
if(lastPID != physicalID)
{
assert(kFALSE);
}
// By now we should need to add a physical at least
if (physical) {
assert(kFALSE);
return TBuffer3D::kNone;
}
// Create logical if required
if (!logical) {
assert(ValidateObjectBuffer(buffer,true) == TBuffer3D::kNone); // Buffer should be ready
logical = CreateNewLogical(buffer);
if (!logical) {
assert(kFALSE);
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("TViewerOpenGL::AddObject", "added %d physicals", fAcceptedPhysicals);
}
} else {
assert(kFALSE);
}
// 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;
}
//______________________________________________________________________________
Int_t TViewerOpenGL::ValidateObjectBuffer(const TBuffer3D & buffer, Bool_t logical) const
{
// kCore: Should always be filled
if (!buffer.SectionsValid(TBuffer3D::kCore)) {
assert(kFALSE);
return TBuffer3D::kNone;
}
// Currently all physical parts (kBoundingBox / kShapeSpecific) of buffer are
// filled automatically if producer can - no need to ask
if (!logical) {
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 {
assert(kFALSE);
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 * TViewerOpenGL::CreateNewLogical(const TBuffer3D & buffer) const
{
// Buffer should now be correctly filled
assert(ValidateObjectBuffer(buffer,true) == TBuffer3D::kNone);
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 {
assert(kFALSE);
}
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 {
assert(kFALSE);
}
break;
}
case TBuffer3DTypes::kComposite: {
// Create empty faceset and record partial complete composite object
// Will be populated with mesh in CloseComposite()
assert(!fComposite);
fComposite = new TGLFaceSet(buffer, buffer.fID);
newLogical = fComposite;
break;
}
default:
newLogical = new TGLFaceSet(buffer, buffer.fID);
break;
}
return newLogical;
}
//______________________________________________________________________________
TGLPhysicalShape * TViewerOpenGL::CreateNewPhysical(UInt_t ID,
const TBuffer3D & buffer,
const TGLLogicalShape & logical) const
{
// 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;
}
//______________________________________________________________________________
Bool_t TViewerOpenGL::OpenComposite(const TBuffer3D & buffer, Bool_t * addChildren)
{
assert(!fComposite);
UInt_t extraSections = AddObject(buffer, addChildren);
assert(extraSections == TBuffer3D::kNone);
// If composite was created it is of interest - we want the rest of the
// child components
if (fComposite) {
return kTRUE;
} else {
return kFALSE;
}
}
//______________________________________________________________________________
void TViewerOpenGL::CloseComposite()
{
// 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 TViewerOpenGL::AddCompositeOp(UInt_t operation)
{
fCSTokens.push_back(std::make_pair(operation, (RootCsg::BaseMesh *)0));
}
//______________________________________________________________________________
RootCsg::BaseMesh *TViewerOpenGL::BuildComposite()
{
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;
}
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.