#include "TGLScenePad.h"
#include "TGLViewer.h"
#include "TGLLogicalShape.h"
#include "TGLPhysicalShape.h"
#include "TGLObject.h"
#include "TGLStopwatch.h"
#include "TBuffer3D.h"
#include "TBuffer3DTypes.h"
#include "TGLFaceSet.h"
#include "TGLPolyLine.h"
#include "TGLPolyMarker.h"
#include "TGLCylinder.h"
#include "TGLSphere.h"
#include "TVirtualPad.h"
#include "TAtt3D.h"
#include "TClass.h"
#include "TList.h"
#include "TMath.h"
#include "TGLPlot3D.h"
ClassImp(TGLScenePad)
TGLScenePad::TGLScenePad(TVirtualPad* pad) :
TVirtualViewer3D(),
TGLScene(),
fPad (pad),
fInternalPIDs (kFALSE),
fNextInternalPID (1),
fLastPID (0),
fAcceptedPhysicals (0),
fComposite (0),
fCSLevel (0),
fSmartRefresh (kFALSE)
{
}
void TGLScenePad::AddHistoPhysical(TGLLogicalShape* log)
{
Double_t how = ((Double_t) gPad->GetWh()) / gPad->GetWw();
Double_t lw = gPad->GetAbsWNDC();
Double_t lh = gPad->GetAbsHNDC() * how;
Double_t lm = TMath::Min(lw, lh);
const TGLBoundingBox& bb = log->BoundingBox();
Double_t size = TMath::Sqrt(3) * (bb.XMax() - bb.XMin());
Double_t scale = lm / size;
TGLVector3 scaleVec(scale, scale, scale);
Double_t tx = gPad->GetAbsXlowNDC() + lw;
Double_t ty = gPad->GetAbsYlowNDC() * how + lh;
TGLVector3 transVec(0, ty, tx);
TGLMatrix mat;
mat.Scale(scaleVec);
mat.Translate(transVec);
mat.RotateLF(3, 2, TMath::PiOver2());
mat.RotateLF(1, 3, TMath::DegToRad()*gPad->GetTheta());
mat.RotateLF(1, 2, TMath::DegToRad()*(gPad->GetPhi() - 90));
Float_t rgba[4] = { 1, 1, 1, 1};
TGLPhysicalShape* phys = new TGLPhysicalShape
(fNextInternalPID++, *log, mat, false, rgba);
AdoptPhysical(*phys);
}
void TGLScenePad::SubPadPaint(TVirtualPad* pad)
{
TVirtualPad *padsav = gPad;
TVirtualViewer3D *vv3dsav = pad->GetViewer3D();
gPad = pad;
pad->SetViewer3D(this);
TList *prims = pad->GetListOfPrimitives();
TObjOptLink *lnk = (prims) ? (TObjOptLink*)prims->FirstLink() : 0;
while (lnk)
{
ObjectPaint(lnk->GetObject(), lnk->GetOption());
lnk = (TObjOptLink*)lnk->Next();
}
pad->SetViewer3D(vv3dsav);
gPad = padsav;
}
void TGLScenePad::ObjectPaint(TObject* obj, Option_t* opt)
{
TGLPlot3D* log = TGLPlot3D::CreatePlot(obj, opt, gPad);
if (log)
{
AdoptLogical(*log);
AddHistoPhysical(log);
}
else if (obj->InheritsFrom(TAtt3D::Class()))
{
obj->Paint(opt);
}
else if (obj->InheritsFrom(TVirtualPad::Class()))
{
SubPadPaint(dynamic_cast<TVirtualPad*>(obj));
}
else
{
obj->Paint(opt);
}
}
void TGLScenePad::PadPaintFromViewer(TGLViewer* viewer)
{
Bool_t sr = fSmartRefresh;
fSmartRefresh = viewer->GetSmartRefresh();
PadPaint(fPad);
fSmartRefresh = sr;
}
void TGLScenePad::PadPaint(TVirtualPad* pad)
{
if (pad != fPad)
{
Error("TGLScenePad::PadPaint", "Mismatch between pad argument and data-member!");
return;
}
BeginScene();
SubPadPaint(fPad);
EndScene();
}
void TGLScenePad::BeginScene()
{
if (gDebug>2) {
Info("TGLScenePad::BeginScene", "entering.");
}
if ( ! BeginUpdate()) {
Error("TGLScenePad::BeginScene", "could not take scene lock.");
return;
}
UInt_t destroyedLogicals = 0;
UInt_t destroyedPhysicals = 0;
TGLStopwatch stopwatch;
if (gDebug > 2) {
stopwatch.Start();
}
destroyedPhysicals = DestroyPhysicals(kTRUE);
if (fSmartRefresh) {
destroyedLogicals = BeginSmartRefresh();
} else {
destroyedLogicals = DestroyLogicals();
}
fInternalPIDs = kFALSE;
fNextInternalPID = 1;
fLastPID = 0;
fAcceptedPhysicals = 0;
if (gDebug > 2) {
Info("TGLScenePad::BeginScene", "destroyed %d physicals %d logicals in %f msec",
destroyedPhysicals, destroyedLogicals, stopwatch.End());
DumpMapSizes();
}
}
void TGLScenePad::EndScene()
{
if (fSmartRefresh) {
EndSmartRefresh();
}
EndUpdate();
if (gDebug > 2) {
Info("TGLScenePad::EndScene", "Accepted %d physicals", fAcceptedPhysicals);
DumpMapSizes();
}
}
Int_t TGLScenePad::AddObject(const TBuffer3D& buffer, Bool_t* addChildren)
{
fInternalPIDs = kTRUE;
Int_t sections = AddObject(fNextInternalPID, buffer, addChildren);
return sections;
}
Int_t TGLScenePad::AddObject(UInt_t physicalID, const TBuffer3D& buffer, Bool_t* addChildren)
{
if (physicalID == 0) {
Error("TGLScenePad::AddObject", "0 physical ID reserved");
return TBuffer3D::kNone;
}
if (fInternalPIDs && physicalID != fNextInternalPID) {
Error("TGLScenePad::AddObject", "invalid next physical ID - mix of internal + external IDs?");
return TBuffer3D::kNone;
}
if (addChildren)
*addChildren = kTRUE;
if (CurrentLock() != kModifyLock) {
Error("TGLScenePad::AddObject", "expected scene to be modify-locked.");
return TBuffer3D::kNone;
}
if (fComposite) {
RootCsg::TBaseMesh *newMesh = RootCsg::ConvertToMesh(buffer);
fCSTokens.push_back(std::make_pair(static_cast<UInt_t>(TBuffer3D::kCSNoOp), newMesh));
return TBuffer3D::kNone;
}
TGLPhysicalShape *physical = FindPhysical(physicalID);
TGLLogicalShape *logical = 0;
if (buffer.fID)
{
logical = FindLogical(buffer.fID);
if (!logical)
logical = AttemptDirectRenderer(buffer.fID);
}
if (physicalID != fLastPID)
{
if (physical)
{
if (!logical) {
Error("TGLScenePad::AddObject", "cached physical with no assocaited cached logical");
}
if (fInternalPIDs)
++fNextInternalPID;
return TBuffer3D::kNone;
}
Bool_t includeRaw = (logical == 0);
Int_t extraSections = ValidateObjectBuffer(buffer, includeRaw);
if (extraSections != TBuffer3D::kNone)
return extraSections;
fLastPID = physicalID;
}
if (fLastPID != physicalID) {
Error("TGLScenePad::AddObject", "internal physical ID tracking error?");
}
if (physical) {
Error("TGLScenePad::AddObject", "expecting to require physical");
return TBuffer3D::kNone;
}
if (!logical)
{
logical = CreateNewLogical(buffer);
if (!logical) {
Error("TGLScenePad::AddObject", "failed to create logical");
return TBuffer3D::kNone;
}
AdoptLogical(*logical);
}
physical = CreateNewPhysical(physicalID, buffer, *logical);
if (physical)
{
AdoptPhysical(*physical);
buffer.fPhysicalID = physicalID;
++fAcceptedPhysicals;
if (gDebug>3 && fAcceptedPhysicals%1000 == 0) {
Info("TGLScenePad::AddObject", "added %d physicals", fAcceptedPhysicals);
}
}
else
{
Error("TGLScenePad::AddObject", "failed to create physical");
}
if (fInternalPIDs)
fNextInternalPID++;
return TBuffer3D::kNone;
}
Bool_t TGLScenePad::OpenComposite(const TBuffer3D& buffer, Bool_t* addChildren)
{
if (fComposite) {
Error("TGLScenePad::OpenComposite", "composite already open");
return kFALSE;
}
UInt_t extraSections = AddObject(buffer, addChildren);
if (extraSections != TBuffer3D::kNone) {
Error("TGLScenePad::OpenComposite", "expected top level composite to not require extra buffer sections");
}
if (fComposite) {
return kTRUE;
} else {
return kFALSE;
}
}
void TGLScenePad::CloseComposite()
{
if (fComposite) {
fCSLevel = 0;
RootCsg::TBaseMesh *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 TGLScenePad::AddCompositeOp(UInt_t operation)
{
fCSTokens.push_back(std::make_pair(operation, (RootCsg::TBaseMesh *)0));
}
Int_t TGLScenePad::ValidateObjectBuffer(const TBuffer3D& buffer, Bool_t includeRaw) const
{
if (!buffer.SectionsValid(TBuffer3D::kCore)) {
Error("TGLScenePad::ValidateObjectBuffer", "kCore section of buffer should be filled always");
return TBuffer3D::kNone;
}
if (!includeRaw) {
return TBuffer3D::kNone;
}
Bool_t needRaw = kFALSE;
if (buffer.Type() != TBuffer3DTypes::kSphere &&
buffer.Type() != TBuffer3DTypes::kTube &&
buffer.Type() != TBuffer3DTypes::kTubeSeg &&
buffer.Type() != TBuffer3DTypes::kCutTube &&
buffer.Type() != TBuffer3DTypes::kComposite)
{
needRaw = kTRUE;
}
else if (buffer.Type() == TBuffer3DTypes::kSphere)
{
const TBuffer3DSphere * sphereBuffer = dynamic_cast<const TBuffer3DSphere *>(&buffer);
if (sphereBuffer) {
if (!sphereBuffer->IsSolidUncut()) {
needRaw = kTRUE;
}
} else {
Error("TGLScenePad::ValidateObjectBuffer", "failed to cast buffer of type 'kSphere' to TBuffer3DSphere");
return TBuffer3D::kNone;
}
}
else if (!buffer.SectionsValid(TBuffer3D::kBoundingBox))
{
needRaw = kTRUE;
}
else if (!buffer.SectionsValid(TBuffer3D::kShapeSpecific) &&
buffer.Type() != TBuffer3DTypes::kComposite)
{
needRaw = kTRUE;
}
else if (fComposite)
{
needRaw = kTRUE;
}
if (needRaw && !buffer.SectionsValid(TBuffer3D::kRawSizes|TBuffer3D::kRaw)) {
return TBuffer3D::kRawSizes|TBuffer3D::kRaw;
} else {
return TBuffer3D::kNone;
}
}
TGLLogicalShape* TGLScenePad::CreateNewLogical(const TBuffer3D& buffer) const
{
TGLLogicalShape * newLogical = 0;
if (buffer.fColor == 1)
const_cast<TBuffer3D&>(buffer).fColor = 42;
switch (buffer.Type())
{
case TBuffer3DTypes::kLine:
newLogical = new TGLPolyLine(buffer);
break;
case TBuffer3DTypes::kMarker:
newLogical = new TGLPolyMarker(buffer);
break;
case TBuffer3DTypes::kSphere:
{
const TBuffer3DSphere * sphereBuffer = dynamic_cast<const TBuffer3DSphere *>(&buffer);
if (sphereBuffer)
{
if (sphereBuffer->IsSolidUncut() && !buffer.SectionsValid(TBuffer3D::kRawSizes|TBuffer3D::kRaw))
{
newLogical = new TGLSphere(*sphereBuffer);
} else {
newLogical = new TGLFaceSet(buffer);
}
} else {
Error("TGLScenePad::CreateNewLogical", "failed to cast buffer of type 'kSphere' to TBuffer3DSphere");
}
break;
}
case TBuffer3DTypes::kTube:
case TBuffer3DTypes::kTubeSeg:
case TBuffer3DTypes::kCutTube:
{
const TBuffer3DTube * tubeBuffer = dynamic_cast<const TBuffer3DTube *>(&buffer);
if (tubeBuffer)
{
if (!buffer.SectionsValid(TBuffer3D::kRawSizes|TBuffer3D::kRaw)) {
newLogical = new TGLCylinder(*tubeBuffer);
} else {
newLogical = new TGLFaceSet(buffer);
}
} else {
Error("TGLScenePad::CreateNewLogical", "failed to cast buffer of type 'kTube/kTubeSeg/kCutTube' to TBuffer3DTube");
}
break;
}
case TBuffer3DTypes::kComposite:
{
if (fComposite)
{
Error("TGLScenePad::CreateNewLogical", "composite already open");
}
fComposite = new TGLFaceSet(buffer);
newLogical = fComposite;
break;
}
default:
newLogical = new TGLFaceSet(buffer);
break;
}
return newLogical;
}
TGLPhysicalShape*
TGLScenePad::CreateNewPhysical(UInt_t ID, const TBuffer3D& buffer,
const TGLLogicalShape& logical) const
{
Int_t colorIndex = buffer.fColor;
if (colorIndex < 0) colorIndex = 42;
Float_t rgba[4];
TGLScene::RGBAFromColorIdx(rgba, colorIndex, buffer.fTransparency);
return new TGLPhysicalShape(ID, logical, buffer.fLocalMaster,
buffer.fReflection, rgba);
}
RootCsg::TBaseMesh* TGLScenePad::BuildComposite()
{
const CSPart_t &currToken = fCSTokens[fCSLevel];
UInt_t opCode = currToken.first;
if (opCode != TBuffer3D::kCSNoOp) {
++fCSLevel;
RootCsg::TBaseMesh *left = BuildComposite();
RootCsg::TBaseMesh *right = BuildComposite();
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;
}
TGLLogicalShape* TGLScenePad::AttemptDirectRenderer(TObject* id)
{
TClass* cls = TGLObject::GetGLRenderer(id->IsA());
if (cls == 0)
return 0;
TGLObject* rnr = reinterpret_cast<TGLObject*>(cls->New());
if (rnr) {
if (rnr->SetModel(id) == kFALSE) {
Warning("TGLScenePad::AttemptDirectRenderer", "failed initializing direct rendering.");
delete rnr;
return 0;
}
rnr->SetBBox();
AdoptLogical(*rnr);
}
return rnr;
}