#include "TGLViewerBase.h"
#include "TGLSceneBase.h"
#include "TGLSceneInfo.h"
#include "TGLRnrCtx.h"
#include "TGLCamera.h"
#include "TGLClip.h"
#include "TGLOverlay.h"
#include "TGLSelectBuffer.h"
#include "TGLSelectRecord.h"
#include "TGLAnnotation.h"
#include "TGLUtil.h"
#include "TGLContext.h"
#include "TGLIncludes.h"
#include "TEnv.h"
#include <algorithm>
#include <stdexcept>
ClassImp(TGLViewerBase);
TGLViewerBase::TGLViewerBase() :
fRnrCtx (0),
fCamera (0),
fClip (0),
fLOD (TGLRnrCtx::kLODHigh),
fStyle (TGLRnrCtx::kFill),
fWFLineW (1),
fOLLineW (1),
fResetSceneInfosOnRender (kFALSE),
fChanged (kFALSE)
{
fRnrCtx = new TGLRnrCtx(this);
fWFLineW = gEnv->GetValue("OpenGL.WireframeLineScalingFactor", 1.0);
fOLLineW = gEnv->GetValue("OpenGL.OutlineLineScalingFactor", 1.0);
}
TGLViewerBase::~TGLViewerBase()
{
for (SceneInfoList_i i=fScenes.begin(); i!=fScenes.end(); ++i)
{
(*i)->GetScene()->RemoveViewer(this);
delete *i;
}
DeleteOverlayElements(TGLOverlayElement::kAll);
delete fRnrCtx;
}
const char* TGLViewerBase::LockIdStr() const
{
return "TGLViewerBase";
}
TGLViewerBase::SceneInfoList_i
TGLViewerBase::FindScene(TGLSceneBase* scene)
{
SceneInfoList_i i = fScenes.begin();
while (i != fScenes.end() && (*i)->GetScene() != scene) ++i;
return i;
}
TGLSceneInfo* TGLViewerBase::AddScene(TGLSceneBase* scene)
{
SceneInfoList_i i = FindScene(scene);
if (i == fScenes.end()) {
TGLSceneInfo* sinfo = scene->CreateSceneInfo(this);
fScenes.push_back(sinfo);
scene->AddViewer(this);
Changed();
return sinfo;
} else {
Warning("TGLViewerBase::AddScene", "scene '%s' already in the list.",
scene->GetName());
return 0;
}
}
void TGLViewerBase::RemoveScene(TGLSceneBase* scene)
{
SceneInfoList_i i = FindScene(scene);
if (i != fScenes.end()) {
delete *i;
fScenes.erase(i);
scene->RemoveViewer(this);
Changed();
} else {
Warning("TGLViewerBase::RemoveScene", "scene '%s' not found.",
scene->GetName());
}
}
void TGLViewerBase::RemoveAllScenes()
{
for (SceneInfoList_i i=fScenes.begin(); i!=fScenes.end(); ++i)
{
TGLSceneInfo * sinfo = *i;
sinfo->GetScene()->RemoveViewer(this);
delete sinfo;
}
fScenes.clear();
Changed();
}
void TGLViewerBase::SceneDestructing(TGLSceneBase* scene)
{
SceneInfoList_i i = FindScene(scene);
if (i != fScenes.end()) {
delete *i;
fScenes.erase(i);
Changed();
} else {
Warning("TGLViewerBase::SceneDestructing", "scene not found.");
}
}
TGLSceneInfo* TGLViewerBase::GetSceneInfo(TGLSceneBase* scene)
{
SceneInfoList_i i = FindScene(scene);
if (i != fScenes.end())
return *i;
else
return 0;
}
TGLLogicalShape* TGLViewerBase::FindLogicalInScenes(TObject* id)
{
for (SceneInfoList_i i=fScenes.begin(); i!=fScenes.end(); ++i)
{
TGLLogicalShape *lshp = (*i)->GetScene()->FindLogical(id);
if (lshp)
return lshp;
}
return 0;
}
void TGLViewerBase::AddOverlayElement(TGLOverlayElement* el)
{
fOverlay.push_back(el);
Changed();
}
void TGLViewerBase::RemoveOverlayElement(TGLOverlayElement* el)
{
OverlayElmVec_i it = std::find(fOverlay.begin(), fOverlay.end(), el);
if (it != fOverlay.end())
fOverlay.erase(it);
Changed();
}
void TGLViewerBase::DeleteOverlayAnnotations()
{
DeleteOverlayElements(TGLOverlayElement::kAnnotation);
}
void TGLViewerBase::DeleteOverlayElements(TGLOverlayElement::ERole role)
{
OverlayElmVec_t ovl;
fOverlay.swap(ovl);
for (OverlayElmVec_i i = ovl.begin(); i != ovl.end(); ++i)
{
if (role == TGLOverlayElement::kAll || (*i)->GetRole() == role)
delete *i;
else
fOverlay.push_back(*i);
}
Changed();
}
void TGLViewerBase::ResetSceneInfos()
{
SceneInfoList_i i = fScenes.begin();
while (i != fScenes.end())
{
(*i)->ResetSceneStamp();
++i;
}
}
void TGLViewerBase::MergeSceneBBoxes(TGLBoundingBox& bbox)
{
bbox.SetEmpty();
for (SceneInfoList_i i=fScenes.begin(); i!=fScenes.end(); ++i)
{
TGLSceneInfo * sinfo = *i;
if (sinfo->GetActive())
{
sinfo->SetupTransformsAndBBox();
bbox.MergeAligned(sinfo->GetTransformedBBox());
}
}
}
void TGLViewerBase::SetupClipObject()
{
if (fClip)
{
fClip->Setup(fOverallBoundingBox);
}
}
void TGLViewerBase::PreRender()
{
TGLContextIdentity* cid = TGLContextIdentity::GetCurrent();
if (cid == 0)
{
}
else
{
if (cid != fRnrCtx->GetGLCtxIdentity())
{
if (fRnrCtx->GetGLCtxIdentity() != 0)
Warning("TGLViewerBase::PreRender", "Switching to another GL context; maybe you should use context-sharing.");
fRnrCtx->SetGLCtxIdentity(cid);
}
}
fRnrCtx->SetCamera (fCamera);
fRnrCtx->SetViewerLOD (fLOD);
fRnrCtx->SetViewerStyle (fStyle);
fRnrCtx->SetViewerWFLineW (fWFLineW);
fRnrCtx->SetViewerOLLineW (fOLLineW);
fRnrCtx->SetViewerClip (fClip);
if (fResetSceneInfosOnRender)
{
ResetSceneInfos();
fResetSceneInfosOnRender = kFALSE;
}
fOverallBoundingBox.SetEmpty();
SceneInfoList_t locked_scenes;
for (SceneInfoList_i i=fScenes.begin(); i!=fScenes.end(); ++i)
{
TGLSceneInfo *sinfo = *i;
TGLSceneBase *scene = sinfo->GetScene();
if (sinfo->GetActive())
{
if ( ! fRnrCtx->Selection() || scene->GetSelectable())
{
if ( ! sinfo->GetScene()->TakeLock(kDrawLock))
{
Warning("TGLViewerBase::PreRender", "locking of scene '%s' failed, skipping.",
sinfo->GetScene()->GetName());
continue;
}
locked_scenes.push_back(sinfo);
}
sinfo->SetupTransformsAndBBox();
fOverallBoundingBox.MergeAligned(sinfo->GetTransformedBBox());
}
}
fCamera->Apply(fOverallBoundingBox, fRnrCtx->GetPickRectangle());
SetupClipObject();
fVisScenes.clear();
for (SceneInfoList_i i=locked_scenes.begin(); i!=locked_scenes.end(); ++i)
{
TGLSceneInfo * sinfo = *i;
const TGLBoundingBox & bbox = sinfo->GetTransformedBBox();
Bool_t visp = (!bbox.IsEmpty() && fCamera->FrustumOverlap(bbox) != Rgl::kOutside);
sinfo->ViewCheck(visp);
if (visp) {
fRnrCtx->SetSceneInfo(sinfo);
sinfo->GetScene()->PreDraw(*fRnrCtx);
if (sinfo->IsVisible()) {
fVisScenes.push_back(sinfo);
} else {
sinfo->GetScene()->PostDraw(*fRnrCtx);
sinfo->GetScene()->ReleaseLock(kDrawLock);
}
fRnrCtx->SetSceneInfo(0);
} else {
sinfo->GetScene()->ReleaseLock(kDrawLock);
}
}
}
void TGLViewerBase::SubRenderScenes(SubRender_foo render_foo)
{
Int_t nScenes = fVisScenes.size();
for (Int_t i = 0; i < nScenes; ++i)
{
TGLSceneInfo* sinfo = fVisScenes[i];
TGLSceneBase* scene = sinfo->GetScene();
fRnrCtx->SetSceneInfo(sinfo);
glPushName(i);
scene->PreRender(*fRnrCtx);
(scene->*render_foo)(*fRnrCtx);
scene->PostRender(*fRnrCtx);
glPopName();
fRnrCtx->SetSceneInfo(0);
}
}
void TGLViewerBase::Render()
{
RenderOpaque();
RenderTransparent();
}
void TGLViewerBase::RenderNonSelected()
{
SubRenderScenes(&TGLSceneBase::RenderOpaque);
TGLCapabilityEnabler blend(GL_BLEND, kTRUE);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDepthMask(GL_FALSE);
SubRenderScenes(&TGLSceneBase::RenderTransp);
glDepthMask(GL_TRUE);
TGLUtil::CheckError("TGLViewerBase::RenderNonSelected - pre exit check");
}
void TGLViewerBase::RenderSelected()
{
SubRenderScenes(&TGLSceneBase::RenderSelOpaque);
TGLCapabilityEnabler blend(GL_BLEND, kTRUE);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDepthMask(GL_FALSE);
SubRenderScenes(&TGLSceneBase::RenderSelTransp);
glDepthMask(GL_TRUE);
TGLUtil::CheckError("TGLViewerBase::RenderSelected - pre exit check");
}
void TGLViewerBase::RenderSelectedForHighlight()
{
fRnrCtx->SetHighlight(kTRUE);
SubRenderScenes(&TGLSceneBase::RenderSelOpaqueForHighlight);
TGLCapabilityEnabler blend(GL_BLEND, kTRUE);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDepthMask(GL_FALSE);
SubRenderScenes(&TGLSceneBase::RenderSelTranspForHighlight);
glDepthMask(GL_TRUE);
fRnrCtx->SetHighlight(kFALSE);
}
void TGLViewerBase::RenderOpaque(Bool_t rnr_non_selected, Bool_t rnr_selected)
{
if (rnr_non_selected)
{
SubRenderScenes(&TGLSceneBase::RenderOpaque);
}
if (rnr_selected)
{
SubRenderScenes(&TGLSceneBase::RenderSelOpaque);
}
TGLUtil::CheckError("TGLViewerBase::RenderOpaque - pre exit check");
}
void TGLViewerBase::RenderTransparent(Bool_t rnr_non_selected, Bool_t rnr_selected)
{
TGLCapabilityEnabler blend(GL_BLEND, kTRUE);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDepthMask(GL_FALSE);
if (rnr_non_selected)
{
SubRenderScenes(&TGLSceneBase::RenderTransp);
}
if (rnr_selected)
{
SubRenderScenes(&TGLSceneBase::RenderSelTransp);
}
glDepthMask(GL_TRUE);
TGLUtil::CheckError("TGLViewerBase::RenderTransparent - pre exit check");
}
void TGLViewerBase::RenderOverlay(Int_t state, Bool_t selection)
{
Int_t nOvl = fOverlay.size();
for (Int_t i = 0; i < nOvl; ++i)
{
TGLOverlayElement* el = fOverlay[i];
if (el->GetState() & state)
{
if (selection) glPushName(i);
el->Render(*fRnrCtx);
if (selection) glPopName();
}
}
}
void TGLViewerBase::PostRender()
{
for (SceneInfoVec_i i = fVisScenes.begin(); i != fVisScenes.end(); ++i)
{
TGLSceneInfo* sinfo = *i;
fRnrCtx->SetSceneInfo(sinfo);
sinfo->GetScene()->PostDraw(*fRnrCtx);
fRnrCtx->SetSceneInfo(0);
sinfo->GetScene()->ReleaseLock(kDrawLock);
}
fChanged = kFALSE;
}
void TGLViewerBase::PreRenderOverlaySelection()
{
fCamera->Apply(fOverallBoundingBox, fRnrCtx->GetPickRectangle());
}
void TGLViewerBase::PostRenderOverlaySelection()
{
}
Bool_t TGLViewerBase::ResolveSelectRecord(TGLSelectRecord& rec, Int_t recIdx)
{
TGLSelectBuffer* sb = fRnrCtx->GetSelectBuffer();
if (recIdx >= sb->GetNRecords())
return kFALSE;
if (sb->SelectRecord(rec, recIdx) < 1)
return kFALSE;
UInt_t sceneIdx = rec.GetItem(0);
if (sceneIdx >= fVisScenes.size())
return kFALSE;
TGLSceneInfo* sinfo = fVisScenes[sceneIdx];
rec.SetSceneInfo(sinfo);
return sinfo->GetScene()->ResolveSelectRecord(rec, 1);
}
Bool_t TGLViewerBase::FindClosestRecord(TGLSelectRecord& rec, Int_t& recIdx)
{
TGLSelectBuffer* sb = fRnrCtx->GetSelectBuffer();
while (recIdx < sb->GetNRecords())
{
if (ResolveSelectRecord(rec, recIdx))
return kTRUE;
++recIdx;
}
return kFALSE;
}
Bool_t TGLViewerBase::FindClosestOpaqueRecord(TGLSelectRecord& rec, Int_t& recIdx)
{
TGLSelectBuffer* sb = fRnrCtx->GetSelectBuffer();
while (recIdx < sb->GetNRecords())
{
if (ResolveSelectRecord(rec, recIdx) && ! rec.GetTransparent())
return kTRUE;
++recIdx;
}
return kFALSE;
}
Bool_t TGLViewerBase::FindClosestOverlayRecord(TGLOvlSelectRecord& rec,
Int_t & recIdx)
{
TGLSelectBuffer* sb = fRnrCtx->GetSelectBuffer();
while (recIdx < sb->GetNRecords())
{
sb->SelectRecord(rec, recIdx);
if (rec.GetItem(0) < fOverlay.size())
{
rec.SetOvlElement(fOverlay[rec.GetItem(0)]);
rec.NextPos();
return kTRUE;
}
++recIdx;
}
return kFALSE;
}