From c9ba972d2638e85289e0ec9ba6ed63da84db3e7e Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Sun, 7 Jul 2019 16:08:38 +0800 Subject: [PATCH] Gui: add support of selection context The patch implements context-aware selection and rendering in 3D view. Please check [here](https://git.io/fjiY5) for more details, including the following 'Render Caching' section. The patch also includes modification of View3DInventorViewer to support always-on-top selection rendering using the secondary selection context and the new coin node SoFCPathAnnotation. Another small change in SoQtQuarterAdaptor for more responsive frame rate display. The original implementation reports skewed frame rate in the presence of long idle period. --- src/Gui/CMakeLists.txt | 2 + src/Gui/DlgSettings3DView.ui | 33 + src/Gui/DlgSettings3DViewImp.cpp | 6 + src/Gui/Quarter/SoQTQuarterAdaptor.cpp | 29 +- src/Gui/Quarter/SoQTQuarterAdaptor.h | 5 +- src/Gui/SceneInspector.cpp | 20 +- src/Gui/SoFCDB.cpp | 6 + src/Gui/SoFCInteractiveElement.cpp | 11 + src/Gui/SoFCSelection.cpp | 368 ++++--- src/Gui/SoFCSelection.h | 13 +- src/Gui/SoFCSelectionContext.cpp | 283 ++++++ src/Gui/SoFCSelectionContext.h | 135 +++ src/Gui/SoFCUnifiedSelection.cpp | 1258 +++++++++++++++++++----- src/Gui/SoFCUnifiedSelection.h | 279 +++++- src/Gui/View3DInventor.cpp | 4 + src/Gui/View3DInventorViewer.cpp | 444 +++++++-- src/Gui/View3DInventorViewer.h | 17 +- src/Gui/ViewProvider.cpp | 10 +- src/Gui/ViewProvider.h | 2 + src/Mod/Part/Gui/SoBrepEdgeSet.cpp | 361 ++++--- src/Mod/Part/Gui/SoBrepEdgeSet.h | 26 +- src/Mod/Part/Gui/SoBrepFaceSet.cpp | 768 +++++++++++---- src/Mod/Part/Gui/SoBrepFaceSet.h | 22 +- src/Mod/Part/Gui/SoBrepPointSet.cpp | 333 ++++--- src/Mod/Part/Gui/SoBrepPointSet.h | 22 +- 25 files changed, 3500 insertions(+), 957 deletions(-) create mode 100644 src/Gui/SoFCSelectionContext.cpp create mode 100644 src/Gui/SoFCSelectionContext.h diff --git a/src/Gui/CMakeLists.txt b/src/Gui/CMakeLists.txt index cc5a764517..54dab95773 100644 --- a/src/Gui/CMakeLists.txt +++ b/src/Gui/CMakeLists.txt @@ -1022,6 +1022,7 @@ SET(Inventor_CPP_SRCS SoFCOffscreenRenderer.cpp SoFCSelection.cpp SoFCUnifiedSelection.cpp + SoFCSelectionContext.cpp SoFCSelectionAction.cpp SoFCVectorizeSVGAction.cpp SoFCVectorizeU3DAction.cpp @@ -1047,6 +1048,7 @@ SET(Inventor_SRCS SoFCOffscreenRenderer.h SoFCSelection.h SoFCUnifiedSelection.h + SoFCSelectionContext.h SoFCSelectionAction.h SoFCVectorizeSVGAction.h SoFCVectorizeU3DAction.h diff --git a/src/Gui/DlgSettings3DView.ui b/src/Gui/DlgSettings3DView.ui index 5c7c276d52..48469bbdac 100644 --- a/src/Gui/DlgSettings3DView.ui +++ b/src/Gui/DlgSettings3DView.ui @@ -145,6 +145,39 @@ will be shown at the lower left in opened files + + + + + + Render cache + + + + + + + 0 + + + + Auto + + + + + Distributed + + + + + Centralized + + + + + + diff --git a/src/Gui/DlgSettings3DViewImp.cpp b/src/Gui/DlgSettings3DViewImp.cpp index 93c58eb16a..7b088b594d 100644 --- a/src/Gui/DlgSettings3DViewImp.cpp +++ b/src/Gui/DlgSettings3DViewImp.cpp @@ -88,6 +88,9 @@ void DlgSettings3DViewImp::saveSettings() index = this->naviCubeCorner->currentIndex(); hGrp->SetInt("CornerNaviCube", index); + index = this->renderCache->currentIndex(); + hGrp->SetInt("RenderCache", index); + QVariant const &vBoxMarkerSize = this->boxMarkerSize->itemData(this->boxMarkerSize->currentIndex()); hGrp->SetInt("MarkerSize", vBoxMarkerSize.toInt()); @@ -158,6 +161,9 @@ void DlgSettings3DViewImp::loadSettings() index = hGrp->GetInt("CornerNaviCube", 1); naviCubeCorner->setCurrentIndex(index); + index = hGrp->GetInt("RenderCache", 0); + renderCache->setCurrentIndex(index); + int const current = hGrp->GetInt("MarkerSize", 9L); this->boxMarkerSize->addItem(tr("5px"), QVariant(5)); this->boxMarkerSize->addItem(tr("7px"), QVariant(7)); diff --git a/src/Gui/Quarter/SoQTQuarterAdaptor.cpp b/src/Gui/Quarter/SoQTQuarterAdaptor.cpp index 47b4314cef..c26d1f0b48 100644 --- a/src/Gui/Quarter/SoQTQuarterAdaptor.cpp +++ b/src/Gui/Quarter/SoQTQuarterAdaptor.cpp @@ -714,32 +714,37 @@ bool SIM::Coin3D::Quarter::SoQTQuarterAdaptor::processSoEvent(const SoEvent* eve */ void SIM::Coin3D::Quarter::SoQTQuarterAdaptor::paintEvent(QPaintEvent* event) { + double start = SbTime::getTimeOfDay().getValue(); QuarterWidget::paintEvent(event); - - this->framesPerSecond = addFrametime(SbTime::getTimeOfDay().getValue()); + this->framesPerSecond = addFrametime(start); } void SIM::Coin3D::Quarter::SoQTQuarterAdaptor::resetFrameCounter(void) { this->framecount = 0; - this->frames.assign(100, 0.0f); - this->totaldraw = 0.0f; + this->frametime = 0.0f; + this->drawtime = 0.0f; this->starttime = SbTime::getTimeOfDay().getValue(); this->framesPerSecond = SbVec2f(0, 0); } -SbVec2f SIM::Coin3D::Quarter::SoQTQuarterAdaptor::addFrametime(double timeofday) +SbVec2f SIM::Coin3D::Quarter::SoQTQuarterAdaptor::addFrametime(double starttime) { - int framearray_size = 100; this->framecount++; - int arrayptr = (this->framecount - 1) % framearray_size; + double timeofday = SbTime::getTimeOfDay().getValue(); - double renderTime = timeofday - this->starttime; - this->totaldraw += (float(renderTime) - this->frames[arrayptr]); - float drawfps = this->totaldraw / std::min(this->framecount, framearray_size); + // draw time is the actual time spent on rendering + double drawtime = timeofday - starttime; +#define FPS_FACTOR 0.7 + this->drawtime = (drawtime*FPS_FACTOR) + this->drawtime*(1.0-FPS_FACTOR); + + // frame time is the time spent since the last frame. There could an + // indefinite pause between the last frame because the scene is not + // changing. So we limit the skew to 5 second. + double frametime = std::min(timeofday-this->starttime, std::max(drawtime,5000.0)); + this->frametime = (frametime*FPS_FACTOR) + this->frametime*(1.0-FPS_FACTOR); - this->frames[arrayptr] = static_cast(renderTime); this->starttime = timeofday; - return SbVec2f(1000 * drawfps, 1.0f / drawfps); + return SbVec2f(1000 * this->drawtime, 1.0f / this->frametime); } diff --git a/src/Gui/Quarter/SoQTQuarterAdaptor.h b/src/Gui/Quarter/SoQTQuarterAdaptor.h index 3f9cc9fe96..5131c15c07 100644 --- a/src/Gui/Quarter/SoQTQuarterAdaptor.h +++ b/src/Gui/Quarter/SoQTQuarterAdaptor.h @@ -117,9 +117,8 @@ private: SoCallbackList m_interactionStartCallback; SoCallbackList m_interactionEndCallback; - // Keep track of the frames-per-second counter. - std::vector frames; - float totaldraw; + double frametime; + double drawtime; double starttime; int framecount; diff --git a/src/Gui/SceneInspector.cpp b/src/Gui/SceneInspector.cpp index 87bbf42253..9d87cf62cc 100644 --- a/src/Gui/SceneInspector.cpp +++ b/src/Gui/SceneInspector.cpp @@ -25,6 +25,7 @@ #ifndef _PreComp_ # include # include +# include #endif #include "SceneInspector.h" @@ -102,12 +103,21 @@ void SceneModel::setNode(QModelIndex index, SoNode* node) setNode(this->index(i, 0, index), child); QHash::iterator it = nodeNames.find(child); - if (it != nodeNames.end()) { - this->setData(this->index(i, 1, index), QVariant(it.value())); - } - else { - this->setData(this->index(i, 1, index), QVariant(QString::fromLatin1(child->getName()))); + QString name; + QTextStream stream(&name); + stream << child << ", "; + if(child->isOfType(SoSwitch::getClassTypeId())) { + auto pcSwitch = static_cast(child); + stream << pcSwitch->whichChild.getValue() << ", "; + } else if (child->isOfType(SoSeparator::getClassTypeId())) { + auto pcSeparator = static_cast(child); + stream << pcSeparator->renderCaching.getValue() << ", "; } + if (it != nodeNames.end()) + stream << it.value(); + else + stream << child->getName(); + this->setData(this->index(i, 1, index), QVariant(name)); } } // insert icon diff --git a/src/Gui/SoFCDB.cpp b/src/Gui/SoFCDB.cpp index df2cafa485..3c9c7fd7be 100644 --- a/src/Gui/SoFCDB.cpp +++ b/src/Gui/SoFCDB.cpp @@ -122,6 +122,9 @@ void Gui::SoFCDB::init() MarkerBitmaps ::initClass(); SoFCCSysDragger ::initClass(); SmSwitchboard ::initClass(); + SoFCSeparator ::initClass(); + SoFCSelectionRoot ::initClass(); + SoFCPathAnnotation ::initClass(); PropertyItem ::init(); PropertySeparatorItem ::init(); @@ -203,6 +206,9 @@ void Gui::SoFCDB::finish() SoFCSelectionColorAction ::finish(); SoUpdateVBOAction ::finish(); SoFCHighlightColorAction ::finish(); + SoFCSeparator ::finish(); + SoFCSelectionRoot ::finish(); + SoFCPathAnnotation ::finish(); storage->unref(); storage = nullptr; diff --git a/src/Gui/SoFCInteractiveElement.cpp b/src/Gui/SoFCInteractiveElement.cpp index c5bb38fdb2..4b60fe1715 100644 --- a/src/Gui/SoFCInteractiveElement.cpp +++ b/src/Gui/SoFCInteractiveElement.cpp @@ -23,6 +23,7 @@ #include "PreCompiled.h" +#include #include "SoFCInteractiveElement.h" using namespace Gui; @@ -248,6 +249,16 @@ void SoGLVBOActivatedElement::get(SoState * state, SbBool& active) const SoGLVBOActivatedElement* self = static_cast (SoElement::getConstElement(state, classStackIndex)); active = self->active; + if(active) { + uint32_t flags = SoOverrideElement::getFlags(state); + if(flags & (SoOverrideElement::COLOR_INDEX| + SoOverrideElement::DIFFUSE_COLOR| + SoOverrideElement::MATERIAL_BINDING| + SoOverrideElement::TRANSPARENCY| + SoOverrideElement::NORMAL_VECTOR| + SoOverrideElement::NORMAL_BINDING)) + active = false; + } } void SoGLVBOActivatedElement::push(SoState * state) diff --git a/src/Gui/SoFCSelection.cpp b/src/Gui/SoFCSelection.cpp index 2eec373bbd..58c15aa728 100644 --- a/src/Gui/SoFCSelection.cpp +++ b/src/Gui/SoFCSelection.cpp @@ -54,13 +54,20 @@ #include "Selection.h" #include "SoFCSelectionAction.h" #include "SoFCInteractiveElement.h" +#include "SoFCUnifiedSelection.h" +#include "ViewParams.h" // For 64-bit system the method using the front buffer doesn't work at all for lines. // Thus, use the method which forces a redraw every time. This is a bit slower but at // least it works. -#if defined(_OCC64) // is set by configure or cmake +// +// Disable front buffer in all cases, in order to spare the repeating logic of +// handling selection contextn. SoFCSelection is not really used that much +// anyway. +// +// #if defined(_OCC64) // is set by configure or cmake # define NO_FRONTBUFFER -#endif +// #endif using namespace Gui; @@ -87,6 +94,7 @@ SoFCSelection::SoFCSelection() SO_NODE_ADD_FIELD(documentName, ("")); SO_NODE_ADD_FIELD(objectName, ("")); SO_NODE_ADD_FIELD(subElementName, ("")); + SO_NODE_ADD_FIELD(useNewSelection, (true)); SO_NODE_DEFINE_ENUM_VALUE(Styles, EMISSIVE); SO_NODE_DEFINE_ENUM_VALUE(Styles, EMISSIVE_DIFFUSE); @@ -111,6 +119,10 @@ SoFCSelection::SoFCSelection() bCtrl = false; selected = NOTSELECTED; + + useNewSelection = ViewParams::instance()->getUseNewSelection(); + selContext = std::make_shared(); + selContext2 = std::make_shared(); } /*! @@ -151,6 +163,46 @@ SoFCSelection::turnOffCurrentHighlight(SoGLRenderAction * action) void SoFCSelection::doAction(SoAction *action) { + if(useNewSelection.getValue() && action->getCurPathCode()!=SoAction::OFF_PATH) { + if (action->getTypeId() == Gui::SoHighlightElementAction::getClassTypeId()) { + Gui::SoHighlightElementAction* hlaction = static_cast(action); + if(!hlaction->isHighlighted()) { + auto ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext,false); + if(ctx->isHighlighted()) { + ctx->highlightIndex = -1; + touch(); + } + }else{ + auto ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext); + ctx->highlightColor = hlaction->getColor(); + if(!ctx->isHighlighted()) { + ctx->highlightIndex = 0; + touch(); + } + } + return; + } else if (action->getTypeId() == Gui::SoSelectionElementAction::getClassTypeId()) { + Gui::SoSelectionElementAction* selaction = static_cast(action); + if (selaction->getType() == Gui::SoSelectionElementAction::All || + selaction->getType() == Gui::SoSelectionElementAction::Append) { + SelContextPtr ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext); + ctx->selectionColor = selaction->getColor(); + if(!ctx->isSelectAll()) { + ctx->selectAll(); + this->touch(); + } + } else if (selaction->getType() == Gui::SoSelectionElementAction::None || + selaction->getType() == Gui::SoSelectionElementAction::Remove) { + SelContextPtr ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext,false); + if(ctx && ctx->isSelected()) { + ctx->selectionIndex.clear(); + this->touch(); + } + } + return; + } + } + if (action->getTypeId() == SoFCDocumentAction::getClassTypeId()) { SoFCDocumentAction *docaction = (SoFCDocumentAction*)action; this->documentName = docaction->documentName; @@ -164,84 +216,87 @@ void SoFCSelection::doAction(SoAction *action) objaction->setHandled(); } - if (action->getTypeId() == SoFCEnableHighlightAction::getClassTypeId()) { - SoFCEnableHighlightAction *preaction = (SoFCEnableHighlightAction*)action; - if (preaction->highlight) { - this->highlightMode = SoFCSelection::AUTO; - } - else { - this->highlightMode = SoFCSelection::OFF; - } - } + if(!useNewSelection.getValue()) { - if (action->getTypeId() == SoFCEnableSelectionAction::getClassTypeId()) { - SoFCEnableSelectionAction *selaction = (SoFCEnableSelectionAction*)action; - if (selaction->selection) { - this->selectionMode = SoFCSelection::SEL_ON; - } - else { - this->selectionMode = SoFCSelection::SEL_OFF; - if (selected.getValue() == SELECTED) { - this->selected = NOTSELECTED; + if (action->getTypeId() == SoFCEnableHighlightAction::getClassTypeId()) { + SoFCEnableHighlightAction *preaction = (SoFCEnableHighlightAction*)action; + if (preaction->highlight) { + this->highlightMode = SoFCSelection::AUTO; + } + else { + this->highlightMode = SoFCSelection::OFF; } } - } - if (action->getTypeId() == SoFCSelectionColorAction::getClassTypeId()) { - SoFCSelectionColorAction *colaction = (SoFCSelectionColorAction*)action; - this->colorSelection = colaction->selectionColor; - } + if (action->getTypeId() == SoFCEnableSelectionAction::getClassTypeId()) { + SoFCEnableSelectionAction *selaction = (SoFCEnableSelectionAction*)action; + if (selaction->selection) { + this->selectionMode = SoFCSelection::SEL_ON; + } + else { + this->selectionMode = SoFCSelection::SEL_OFF; + if (selected.getValue() == SELECTED) { + this->selected = NOTSELECTED; + } + } + } - if (action->getTypeId() == SoFCHighlightColorAction::getClassTypeId()) { - SoFCHighlightColorAction *colaction = (SoFCHighlightColorAction*)action; - this->colorHighlight = colaction->highlightColor; - } + if (action->getTypeId() == SoFCSelectionColorAction::getClassTypeId()) { + SoFCSelectionColorAction *colaction = (SoFCSelectionColorAction*)action; + this->colorSelection = colaction->selectionColor; + } - if (selectionMode.getValue() == SEL_ON && action->getTypeId() == SoFCSelectionAction::getClassTypeId()) { - SoFCSelectionAction *selaction = static_cast(action); + if (action->getTypeId() == SoFCHighlightColorAction::getClassTypeId()) { + SoFCHighlightColorAction *colaction = (SoFCHighlightColorAction*)action; + this->colorHighlight = colaction->highlightColor; + } - if (selaction->SelChange.Type == SelectionChanges::AddSelection || - selaction->SelChange.Type == SelectionChanges::RmvSelection) { - if (documentName.getValue() == selaction->SelChange.pDocName && - objectName.getValue() == selaction->SelChange.pObjectName && - (subElementName.getValue() == selaction->SelChange.pSubName || - *(selaction->SelChange.pSubName) == '\0') ) { - if (selaction->SelChange.Type == SelectionChanges::AddSelection) { - if(selected.getValue() == NOTSELECTED){ + if (selectionMode.getValue() == SEL_ON && action->getTypeId() == SoFCSelectionAction::getClassTypeId()) { + SoFCSelectionAction *selaction = static_cast(action); + + if (selaction->SelChange.Type == SelectionChanges::AddSelection || + selaction->SelChange.Type == SelectionChanges::RmvSelection) { + if (documentName.getValue() == selaction->SelChange.pDocName && + objectName.getValue() == selaction->SelChange.pObjectName && + (subElementName.getValue() == selaction->SelChange.pSubName || + *(selaction->SelChange.pSubName) == '\0') ) { + if (selaction->SelChange.Type == SelectionChanges::AddSelection) { + if(selected.getValue() == NOTSELECTED){ + selected = SELECTED; + } + } + else { + if(selected.getValue() == SELECTED){ + selected = NOTSELECTED; + } + } + return; + } + } + else if (selaction->SelChange.Type == SelectionChanges::ClrSelection) { + if (documentName.getValue() == selaction->SelChange.pDocName || + strcmp(selaction->SelChange.pDocName,"") == 0){ + if(selected.getValue() == SELECTED){ + selected = NOTSELECTED; + } + + } + } + else if (selaction->SelChange.Type == SelectionChanges::SetSelection) { + bool sel = Selection().isSelected( + documentName.getValue().getString(), + objectName.getValue().getString()/*, + subElementName.getValue().getString()*/); + if (sel) { + if (selected.getValue() == NOTSELECTED) { selected = SELECTED; } } else { - if(selected.getValue() == SELECTED){ + if (selected.getValue() == SELECTED) { selected = NOTSELECTED; } } - return; - } - } - else if (selaction->SelChange.Type == SelectionChanges::ClrSelection) { - if (documentName.getValue() == selaction->SelChange.pDocName || - strcmp(selaction->SelChange.pDocName,"") == 0){ - if(selected.getValue() == SELECTED){ - selected = NOTSELECTED; - } - - } - } - else if (selaction->SelChange.Type == SelectionChanges::SetSelection) { - bool sel = Selection().isSelected( - documentName.getValue().getString(), - objectName.getValue().getString()/*, - subElementName.getValue().getString()*/); - if (sel) { - if (selected.getValue() == NOTSELECTED) { - selected = SELECTED; - } - } - else { - if (selected.getValue() == SELECTED) { - selected = NOTSELECTED; - } } } } @@ -309,6 +364,11 @@ SoFCSelection::getPickedPoint(SoHandleEventAction* action) const void SoFCSelection::handleEvent(SoHandleEventAction * action) { + if(useNewSelection.getValue()) { + inherited::handleEvent( action ); + return; + } + static char buf[513]; HighlightModes mymode = (HighlightModes) this->highlightMode.getValue(); const SoEvent * event = action->getEvent(); @@ -337,12 +397,13 @@ SoFCSelection::handleEvent(SoHandleEventAction * action) } } - snprintf(buf,512,"Preselected: %s.%s.%s (%f,%f,%f)",documentName.getValue().getString() + const auto &pt = pp->getPoint(); + snprintf(buf,512,"Preselected: %s.%s.%s (%g, %g, %g)",documentName.getValue().getString() ,objectName.getValue().getString() ,subElementName.getValue().getString() - ,pp->getPoint()[0] - ,pp->getPoint()[1] - ,pp->getPoint()[2]); + ,fabs(pt[0])>1e-7?pt[0]:0.0 + ,fabs(pt[1])>1e-7?pt[1]:0.0 + ,fabs(pt[2])>1e-7?pt[2]:0.0); getMainWindow()->showMessage(QString::fromLatin1(buf)); } @@ -380,6 +441,7 @@ SoFCSelection::handleEvent(SoHandleEventAction * action) // Otherwise the tree signals that an object is preselected even though it is hidden. (Werner) const SoPickedPoint * pp = this->getPickedPoint(action); if (pp && pp->getPath()->containsPath(action->getCurPath())) { + const auto &pt = pp->getPoint(); if (bCtrl) { if (Gui::Selection().isSelected(documentName.getValue().getString() ,objectName.getValue().getString() @@ -391,17 +453,15 @@ SoFCSelection::handleEvent(SoHandleEventAction * action) Gui::Selection().addSelection(documentName.getValue().getString() ,objectName.getValue().getString() ,subElementName.getValue().getString() - ,pp->getPoint()[0] - ,pp->getPoint()[1] - ,pp->getPoint()[2]); + ,pt[0] ,pt[1] ,pt[2]); if (mymode == OFF) { - snprintf(buf,512,"Selected: %s.%s.%s (%f,%f,%f)",documentName.getValue().getString() + snprintf(buf,512,"Selected: %s.%s.%s (%g, %g, %g)",documentName.getValue().getString() ,objectName.getValue().getString() ,subElementName.getValue().getString() - ,pp->getPoint()[0] - ,pp->getPoint()[1] - ,pp->getPoint()[2]); + ,fabs(pt[0])>1e-7?pt[0]:0.0 + ,fabs(pt[1])>1e-7?pt[1]:0.0 + ,fabs(pt[2])>1e-7?pt[2]:0.0); getMainWindow()->showMessage(QString::fromLatin1(buf)); } @@ -415,27 +475,22 @@ SoFCSelection::handleEvent(SoHandleEventAction * action) Gui::Selection().addSelection(documentName.getValue().getString() ,objectName.getValue().getString() ,subElementName.getValue().getString() - ,pp->getPoint()[0] - ,pp->getPoint()[1] - ,pp->getPoint()[2]); + ,pt[0] ,pt[1] ,pt[2]); } else { Gui::Selection().clearSelection(documentName.getValue().getString()); Gui::Selection().addSelection(documentName.getValue().getString() ,objectName.getValue().getString() - ,0 - ,pp->getPoint()[0] - ,pp->getPoint()[1] - ,pp->getPoint()[2]); + ,0 ,pt[0] ,pt[1] ,pt[2]); } if (mymode == OFF) { - snprintf(buf,512,"Selected: %s.%s.%s (%f,%f,%f)",documentName.getValue().getString() + snprintf(buf,512,"Selected: %s.%s.%s (%g, %g, %g)",documentName.getValue().getString() ,objectName.getValue().getString() ,subElementName.getValue().getString() - ,pp->getPoint()[0] - ,pp->getPoint()[1] - ,pp->getPoint()[2]); + ,fabs(pt[0])>1e-7?pt[0]:0.0 + ,fabs(pt[1])>1e-7?pt[1]:0.0 + ,fabs(pt[2])>1e-7?pt[2]:0.0); getMainWindow()->showMessage(QString::fromLatin1(buf)); } @@ -544,6 +599,7 @@ SoFCSelection::handleEvent(SoHandleEventAction * action) // Otherwise the tree signals that an object is preselected even though it is hidden. (Werner) const SoPickedPoint * pp = this->getPickedPoint(action); if (pp && pp->getPath()->containsPath(action->getCurPath())) { + const auto &pt = pp->getPoint(); if (bCtrl) { if (Gui::Selection().isSelected(documentName.getValue().getString() ,objectName.getValue().getString() @@ -556,17 +612,15 @@ SoFCSelection::handleEvent(SoHandleEventAction * action) Gui::Selection().addSelection(documentName.getValue().getString() ,objectName.getValue().getString() ,subElementName.getValue().getString() - ,pp->getPoint()[0] - ,pp->getPoint()[1] - ,pp->getPoint()[2]); + ,pt[0] ,pt[1] ,pt[2]); if (mymode == OFF) { - snprintf(buf,512,"Selected: %s.%s.%s (%f,%f,%f)",documentName.getValue().getString() + snprintf(buf,512,"Selected: %s.%s.%s (%g, %g, %g)",documentName.getValue().getString() ,objectName.getValue().getString() ,subElementName.getValue().getString() - ,pp->getPoint()[0] - ,pp->getPoint()[1] - ,pp->getPoint()[2]); + ,fabs(pt[0])>1e-7?pt[0]:0.0 + ,fabs(pt[1])>1e-7?pt[1]:0.0 + ,fabs(pt[2])>1e-7?pt[2]:0.0); getMainWindow()->showMessage(QString::fromLatin1(buf)); } @@ -580,27 +634,22 @@ SoFCSelection::handleEvent(SoHandleEventAction * action) Gui::Selection().addSelection(documentName.getValue().getString() ,objectName.getValue().getString() ,subElementName.getValue().getString() - ,pp->getPoint()[0] - ,pp->getPoint()[1] - ,pp->getPoint()[2]); + ,pt[0] ,pt[1] ,pt[2]); } else { Gui::Selection().clearSelection(documentName.getValue().getString()); Gui::Selection().addSelection(documentName.getValue().getString() ,objectName.getValue().getString() - ,0 - ,pp->getPoint()[0] - ,pp->getPoint()[1] - ,pp->getPoint()[2]); + ,0 ,pt[0] ,pt[1] ,pt[2]); } if (mymode == OFF) { - snprintf(buf,512,"Selected: %s.%s.%s (%f,%f,%f)",documentName.getValue().getString() + snprintf(buf,512,"Selected: %s.%s.%s (%g, %g, %g)",documentName.getValue().getString() ,objectName.getValue().getString() ,subElementName.getValue().getString() - ,pp->getPoint()[0] - ,pp->getPoint()[1] - ,pp->getPoint()[2]); + ,fabs(pt[0])>1e-7?pt[0]:0.0 + ,fabs(pt[1])>1e-7?pt[1]:0.0 + ,fabs(pt[2])>1e-7?pt[2]:0.0); getMainWindow()->showMessage(QString::fromLatin1(buf)); } @@ -621,15 +670,24 @@ SoFCSelection::handleEvent(SoHandleEventAction * action) void SoFCSelection::GLRenderBelowPath(SoGLRenderAction * action) { + SoState * state = action->getState(); + SelContextPtr ctx = Gui::SoFCSelectionRoot::getRenderContext(this,selContext); + if(selContext2->checkGlobal(ctx)) + ctx = selContext2; + if(!useNewSelection.getValue() && selContext == ctx) { + ctx->selectionColor = this->colorSelection.getValue(); + ctx->highlightColor = this->colorHighlight.getValue(); + if(this->selected.getValue()==SELECTED) + ctx->selectAll(); + else + ctx->selectionIndex.clear(); + ctx->highlightIndex = this->highlighted?0:-1; + } + #ifdef NO_FRONTBUFFER // check if preselection is active - HighlightModes mymode = (HighlightModes) this->highlightMode.getValue(); - bool preselected = highlighted && mymode == AUTO; - SoState * state = action->getState(); state->push(); - if (preselected || this->highlightMode.getValue() == ON || this->selected.getValue() == SELECTED) { - this->setOverride(action); - } + this->setOverride(action,ctx); inherited::GLRenderBelowPath(action); state->pop(); #else @@ -652,22 +710,65 @@ SoFCSelection::GLRenderBelowPath(SoGLRenderAction * action) void SoFCSelection::GLRender(SoGLRenderAction * action) { + SoState * state = action->getState(); + SelContextPtr ctx = Gui::SoFCSelectionRoot::getRenderContext(this,selContext); + if(selContext2->checkGlobal(ctx)) + ctx = selContext2; + if(!useNewSelection.getValue() && selContext == ctx) { + ctx->selectionColor = this->colorSelection.getValue(); + ctx->highlightColor = this->colorHighlight.getValue(); + if(this->selected.getValue()==SELECTED) + ctx->selectAll(); + else + ctx->selectionIndex.clear(); + ctx->highlightIndex = this->highlighted?0:-1; + } + +#ifdef NO_FRONTBUFFER + // check if preselection is active + state->push(); + this->setOverride(action,ctx); inherited::GLRender(action); + state->pop(); +#else + // Set up state for locate highlighting (if necessary) + GLint oldDepthFunc; + SbBool drawHighlighted = preRender(action, oldDepthFunc); + + // now invoke the parent method + inherited::GLRender(action); + + // Restore old depth buffer model if needed + if (drawHighlighted || highlighted) + glDepthFunc((GLenum)oldDepthFunc); + + // Clean up state if needed + if (drawHighlighted) + action->getState()->pop(); +#endif } // doc from parent void SoFCSelection::GLRenderInPath(SoGLRenderAction * action) { + SelContextPtr ctx = Gui::SoFCSelectionRoot::getRenderContext(this,selContext); + if(selContext2->checkGlobal(ctx)) + ctx = selContext2; + if(!useNewSelection.getValue() && selContext == ctx) { + ctx->selectionColor = this->colorSelection.getValue(); + ctx->highlightColor = this->colorHighlight.getValue(); + if(this->selected.getValue()==SELECTED) + ctx->selectAll(); + else + ctx->selectionIndex.clear(); + ctx->highlightIndex = this->highlighted?0:-1; + } #ifdef NO_FRONTBUFFER // check if preselection is active - HighlightModes mymode = (HighlightModes) this->highlightMode.getValue(); - bool preselected = highlighted && mymode == AUTO; SoState * state = action->getState(); state->push(); - if (preselected || this->highlightMode.getValue() == ON || this->selected.getValue() == SELECTED) { - this->setOverride(action); - } + this->setOverride(action,ctx); inherited::GLRenderInPath(action); state->pop(); #else @@ -848,24 +949,41 @@ SoFCSelection::readInstance ( SoInput * in, unsigned short flags ) // update override state before rendering // void -SoFCSelection::setOverride(SoGLRenderAction * action) +SoFCSelection::setOverride(SoGLRenderAction * action, SelContextPtr ctx) { + HighlightModes mymode = (HighlightModes) this->highlightMode.getValue(); + bool preselected = ctx && ctx->isHighlighted() && (useNewSelection.getValue()||mymode == AUTO); + if (!preselected && mymode!=ON && (!ctx || !ctx->isSelected())) + return; + + // uniqueId is returned by SoNode::getNodeId(). It is used to notify change + // and for render cache update. In order to update cache on selection state + // change, We manually change the id here by using a combined hash of the + // original id and context pointer. + auto oldId = this->uniqueId; + this->uniqueId ^= std::hash()(ctx.get()) + 0x9e3779b9 + (oldId << 6) + (oldId >> 2); + //Base::Console().Log("SoFCSelection::setOverride() (%p)\n",this); SoState * state = action->getState(); - if(this->selected.getValue() == SELECTED) - SoLazyElement::setEmissive(state, &this->colorSelection.getValue()); - else - SoLazyElement::setEmissive(state, &this->colorHighlight.getValue()); + + SoMaterialBindingElement::set(state,SoMaterialBindingElement::OVERALL); + + if(!preselected) + SoLazyElement::setEmissive(state, &ctx->selectionColor); + else + SoLazyElement::setEmissive(state, &ctx->highlightColor); SoOverrideElement::setEmissiveColorOverride(state, this, true); Styles mystyle = (Styles) this->style.getValue(); if (mystyle == SoFCSelection::EMISSIVE_DIFFUSE) { - if(this->selected.getValue() == SELECTED) - SoLazyElement::setDiffuse(state, this,1, &this->colorSelection.getValue(),&colorpacker); + if(!preselected) + SoLazyElement::setDiffuse(state, this,1, &ctx->selectionColor,&colorpacker); else - SoLazyElement::setDiffuse(state, this,1, &this->colorHighlight.getValue(),&colorpacker); + SoLazyElement::setDiffuse(state, this,1, &ctx->highlightColor,&colorpacker); SoOverrideElement::setDiffuseColorOverride(state, this, true); } + + this->uniqueId = oldId; } // private convenience method diff --git a/src/Gui/SoFCSelection.h b/src/Gui/SoFCSelection.h index 61e54ffa7a..64a00cffc9 100644 --- a/src/Gui/SoFCSelection.h +++ b/src/Gui/SoFCSelection.h @@ -41,6 +41,9 @@ #include #include #include +#include +#include +#include "SoFCSelectionContext.h" class SoFullPath; class SoPickedPoint; @@ -94,6 +97,7 @@ public: SoSFString documentName; SoSFString objectName; SoSFString subElementName; + SoSFBool useNewSelection; virtual void doAction(SoAction *action); virtual void GLRender(SoGLRenderAction * action); @@ -105,13 +109,20 @@ public: protected: virtual ~SoFCSelection(); + + typedef SoFCSelectionContext SelContext; + typedef std::shared_ptr SelContextPtr; + SelContextPtr selContext; + SelContextPtr selContext2; + virtual void redrawHighlighted(SoAction * act, SbBool flag); + virtual SbBool readInstance(SoInput * in, unsigned short flags); private: static int getPriority(const SoPickedPoint*); static void turnoffcurrent(SoAction * action); - void setOverride(SoGLRenderAction * action); + void setOverride(SoGLRenderAction * action, SelContextPtr); SbBool isHighlighted(SoAction *action); SbBool preRender(SoGLRenderAction *act, GLint &oldDepthFunc); const SoPickedPoint* getPickedPoint(SoHandleEventAction*) const; diff --git a/src/Gui/SoFCSelectionContext.cpp b/src/Gui/SoFCSelectionContext.cpp new file mode 100644 index 0000000000..bd350cf5fe --- /dev/null +++ b/src/Gui/SoFCSelectionContext.cpp @@ -0,0 +1,283 @@ +/**************************************************************************** + * Copyright (c) 2018 Zheng, Lei (realthunder) * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ****************************************************************************/ +#include "PreCompiled.h" +#include +#include +#include +#include +#include "SoFCUnifiedSelection.h" +#include "Selection.h" + +using namespace Gui; + +///////////////////////////////////////////////////////////////////////////// + +SoFCSelectionContext::~SoFCSelectionContext() { + if(counter) + *counter -= 1; +} + +bool SoFCSelectionContext::checkGlobal(SoFCSelectionContextPtr ctx) { + bool sel = false; + bool hl = false; + SoFCSelectionRoot::checkSelection(sel,selectionColor,hl,highlightColor); + if(sel) + selectionIndex.insert(-1); + else if(ctx && hl) { + selectionColor = ctx->selectionColor; + selectionIndex = ctx->selectionIndex; + }else + selectionIndex.clear(); + if(hl) + highlightAll(); + else if(ctx && sel) { + highlightIndex = ctx->highlightIndex; + highlightColor = ctx->highlightColor; + }else + removeHighlight(); + return sel||hl; +} + +bool SoFCSelectionContext::removeIndex(int index) { + auto it = selectionIndex.find(index); + if(it != selectionIndex.end()) { + selectionIndex.erase(it); + return true; + } + return false; +} + +int SoFCSelectionContext::merge(int status, SoFCSelectionContextBasePtr &output, + SoFCSelectionContextBasePtr input, SoFCSelectionRoot *) +{ + auto ctx = std::dynamic_pointer_cast(input); + if(!ctx) + return status; + + if(ctx->selectionIndex.empty()) { + output = ctx; + return -1; + } + + auto ret = std::dynamic_pointer_cast(output); + if(!ret) { + output = ctx; + return 0; + } + + if(ctx->isSelectAll()) + return status; + + if(ret->isSelectAll()) { + if(!status) { + output = ret->copy(); + ret = std::dynamic_pointer_cast(ret); + assert(ret); + } + ret->selectionIndex = ctx->selectionIndex; + return status; + } + + std::vector remove; + for(auto idx : ret->selectionIndex) { + if(!ctx->selectionIndex.count(idx)) + remove.push_back(idx); + } + + for(auto idx : remove) { + if(!status) { + status = 1; + output = ret->copy(); + ret = std::dynamic_pointer_cast(ret); + assert(ret); + } + ret->selectionIndex.erase(idx); + if(ret->selectionIndex.empty()) + return -1; + } + return status; +} + +///////////////////////////////////////////////////////////////////////////////////// + +bool SoFCSelectionContextEx::setColors( + const std::map &colors, const std::string &element) { + std::map tmp; + auto it = colors.find(""); + if(it!=colors.end()) + tmp[-1] = it->second; + for(auto it=colors.lower_bound(element);it!=colors.end();++it) { + if(!boost::starts_with(it->first,element)) + break; + if(it->first.size()==element.size()) + tmp[-1] = it->second; + else { + int idx = std::atoi(it->first.c_str()+4); + if(idx>0) { + idx -= 1; + tmp[idx] = it->second; + } + } + } + if(tmp == this->colors) + return false; + this->colors.swap(tmp); + return true; +} + +uint32_t SoFCSelectionContextEx::packColor(const App::Color &c, bool &hasTransparency) { + float trans = std::max(trans0,c.a); + if(trans>0) + hasTransparency = true; + return SbColor(c.r,c.g,c.b).getPackedValue(trans); +} + +bool SoFCSelectionContextEx::applyColor(int idx, std::vector &packedColors, bool &hasTransparency) { + if(colors.empty()) + return false; + auto it = colors.find(idx); + if(it==colors.end()) { + if(colors.begin()->first >= 0) + return false; + it = colors.begin(); + } + packedColors.push_back(packColor(it->second,hasTransparency)); + return true; +} + +bool SoFCSelectionContextEx::isSingleColor(uint32_t &color, bool &hasTransparency) { + if(colors.size() && colors.begin()->first<0) { + color = packColor(colors.begin()->second,hasTransparency); + return colors.size()==1; + } + return false; +} + +int SoFCSelectionContextEx::merge(int status, SoFCSelectionContextBasePtr &output, + SoFCSelectionContextBasePtr input, SoFCSelectionRoot *node) +{ + auto ctx = std::dynamic_pointer_cast(input); + if(!ctx) { + if(node && node->hasColorOverride()) { + if(!status) + status = 2; + else if(status == 1) + status = 3; + } + return status; + } + + int status_copy = status; + if(status==2) + status_copy = 0; + else if(status==3) + status_copy = 1; + status_copy = SoFCSelectionContext::merge(status_copy,output,input,node); + if(status_copy < 0) + return status_copy; + + if(status>1) { + // When status>1 it means there is color override before us, all + // subsequent color override will be bypassed + if(status_copy==1) + status = 3; + else + status = 2; + return status; + } + + status = status_copy; + auto ret = std::dynamic_pointer_cast(output); + assert(ret); + for(auto &v : ctx->colors) { + if(ret->colors.count(v.first)) + continue; + if(!status) { + status = 1; + output = ret->copy(); + ret = std::dynamic_pointer_cast(output); + assert(ret); + } + ret->colors.insert(v); + } + + if(node && node->hasColorOverride()) { + if(!status) + status = 2; + else if(status == 1) + status = 3; + } + return status; +} + +/////////////////////////////////////////////////////////////////////// + +SoFCSelectionCounter::SoFCSelectionCounter() + :counter(std::make_shared(0)) + ,hasSelection(false) + ,hasPreselection(false) +{} + + +SoFCSelectionCounter::~SoFCSelectionCounter() +{} + + +bool SoFCSelectionCounter::checkRenderCache(SoState *state) { + if(*counter || + (hasSelection && Selection().hasSelection()) || + (hasPreselection && Selection().hasPreselection())) + { + if(SoFCSelectionRoot::getCacheMode()!=SoSeparator::OFF) + SoCacheElement::invalidate(state); + return false; + } + if(!Selection().hasPreselection()) + hasPreselection = false; + if(!Selection().hasSelection()) + hasSelection = false; + return true; +} + +void SoFCSelectionCounter::checkAction(SoHighlightElementAction *hlaction) { + if(hlaction->isHighlighted()) + hasPreselection = true; +} + +void SoFCSelectionCounter::checkAction(SoSelectionElementAction *selaction, SoFCSelectionContextPtr ctx) { + switch(selaction->getType()) { + case SoSelectionElementAction::None: + return; + case SoSelectionElementAction::All: + case SoSelectionElementAction::Append: + hasSelection = true; + break; + default: + break; + } + if(selaction->isSecondary()) { + if(ctx && !ctx->counter) { + *counter += 1; + ctx->counter = counter; + } + } +} diff --git a/src/Gui/SoFCSelectionContext.h b/src/Gui/SoFCSelectionContext.h new file mode 100644 index 0000000000..b87d013ffd --- /dev/null +++ b/src/Gui/SoFCSelectionContext.h @@ -0,0 +1,135 @@ +/**************************************************************************** + * Copyright (c) 2018 Zheng, Lei (realthunder) * + * * + * This file is part of the FreeCAD CAx development system. * + * * + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Library General Public * + * License as published by the Free Software Foundation; either * + * version 2 of the License, or (at your option) any later version. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Library General Public License for more details. * + * * + * You should have received a copy of the GNU Library General Public * + * License along with this library; see the file COPYING.LIB. If not, * + * write to the Free Software Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307, USA * + * * + ****************************************************************************/ + +#ifndef GUI_SOFCSELECTIONCONTEXT_H +#define GUI_SOFCSELECTIONCONTEXT_H + +#include +#include +#include +#include +#include +#include + +namespace Gui { + +class SoFCSelectionRoot; +struct SoFCSelectionContextBase; +typedef std::shared_ptr SoFCSelectionContextBasePtr; + +struct GuiExport SoFCSelectionContextBase { + virtual ~SoFCSelectionContextBase() {} + typedef int MergeFunc(int status, SoFCSelectionContextBasePtr &output, + SoFCSelectionContextBasePtr input, SoFCSelectionRoot *node); +}; + +struct SoFCSelectionContext; +typedef std::shared_ptr SoFCSelectionContextPtr; + +struct GuiExport SoFCSelectionContext : SoFCSelectionContextBase +{ + int highlightIndex = -1; + std::set selectionIndex; + SbColor selectionColor; + SbColor highlightColor; + std::shared_ptr counter; + + virtual ~SoFCSelectionContext(); + + bool isSelected() const { + return !selectionIndex.empty(); + } + + void selectAll() { + selectionIndex.clear(); + selectionIndex.insert(-1); + } + + bool isSelectAll() const{ + return selectionIndex.size() && *selectionIndex.begin()<0; + } + + bool isHighlighted() const { + return highlightIndex>=0; + } + + bool isHighlightAll() const{ + return highlightIndex==INT_MAX && (selectionIndex.empty() || isSelectAll()); + } + + void highlightAll() { + highlightIndex = INT_MAX; + } + + void removeHighlight() { + highlightIndex = -1; + } + + bool removeIndex(int index); + bool checkGlobal(SoFCSelectionContextPtr ctx); + + virtual SoFCSelectionContextBasePtr copy() { + return std::make_shared(*this); + } + + static MergeFunc merge; +}; + +struct SoFCSelectionContextEx; +typedef std::shared_ptr SoFCSelectionContextExPtr; + +struct GuiExport SoFCSelectionContextEx : SoFCSelectionContext +{ + std::map colors; + float trans0 = 0.0; + + bool setColors(const std::map &colors, const std::string &element); + uint32_t packColor(const App::Color &c, bool &hasTransparency); + bool applyColor(int idx, std::vector &packedColors, bool &hasTransparency); + bool isSingleColor(uint32_t &color, bool &hasTransparency); + + virtual SoFCSelectionContextBasePtr copy() { + return std::make_shared(*this); + } + + static MergeFunc merge; +}; + +class SoHighlightElementAction; +class SoSelectionElementAction; + +class GuiExport SoFCSelectionCounter { +public: + SoFCSelectionCounter(); + virtual ~SoFCSelectionCounter(); + bool checkRenderCache(SoState *state); + void checkAction(SoHighlightElementAction *hlaction); + void checkAction(SoSelectionElementAction *selaction, SoFCSelectionContextPtr ctx); +protected: + std::shared_ptr counter; + bool hasSelection; + bool hasPreselection; + static int cachingMode; +}; + +} +#endif //GUI_SOFCSELECTIONCONTEXT_H diff --git a/src/Gui/SoFCUnifiedSelection.cpp b/src/Gui/SoFCUnifiedSelection.cpp index ac4d1e3e45..28064c2da8 100644 --- a/src/Gui/SoFCUnifiedSelection.cpp +++ b/src/Gui/SoFCUnifiedSelection.cpp @@ -55,6 +55,7 @@ #include #include #include +#include #include #include #include @@ -76,10 +77,12 @@ #include #include +#include #include #include #include #include +#include #include "SoFCUnifiedSelection.h" #include "Application.h" @@ -90,12 +93,14 @@ #include "SoFCSelectionAction.h" #include "ViewProviderDocumentObject.h" #include "ViewProviderGeometryObject.h" +#include "ViewParams.h" + +FC_LOG_LEVEL_INIT("SoFCUnifiedSelection",false,true,true); using namespace Gui; SoFullPath * Gui::SoFCUnifiedSelection::currenthighlight = NULL; - // ************************************************************************* SO_NODE_SOURCE(SoFCUnifiedSelection); @@ -112,15 +117,19 @@ SoFCUnifiedSelection::SoFCUnifiedSelection() : pcDocument(0) SO_NODE_ADD_FIELD(highlightMode, (AUTO)); SO_NODE_ADD_FIELD(selectionMode, (ON)); SO_NODE_ADD_FIELD(selectionRole, (true)); + SO_NODE_ADD_FIELD(useNewSelection, (true)); SO_NODE_DEFINE_ENUM_VALUE(HighlightModes, AUTO); SO_NODE_DEFINE_ENUM_VALUE(HighlightModes, ON); SO_NODE_DEFINE_ENUM_VALUE(HighlightModes, OFF); SO_NODE_SET_SF_ENUM_TYPE (highlightMode, HighlightModes); - highlighted = false; + detailPath = static_cast(new SoPath(20)); + detailPath->ref(); + setPreSelection = false; preSelection = -1; + useNewSelection = ViewParams::instance()->getUseNewSelection(); } /*! @@ -134,6 +143,10 @@ SoFCUnifiedSelection::~SoFCUnifiedSelection() currenthighlight->unref(); currenthighlight = NULL; } + if (detailPath) { + detailPath->unref(); + detailPath = NULL; + } } // doc from parent @@ -148,6 +161,10 @@ void SoFCUnifiedSelection::finish() atexit_cleanup(); } +bool SoFCUnifiedSelection::hasHighlight() { + return currenthighlight != NULL; +} + void SoFCUnifiedSelection::applySettings() { float transparency; @@ -165,6 +182,7 @@ void SoFCUnifiedSelection::applySettings() highlightColor.setPackedValue((uint32_t)highlight, transparency); this->colorHighlight.setValue(highlightColor); } + if (!enableSel) { this->selectionMode = SoFCUnifiedSelection::OFF; } @@ -302,34 +320,78 @@ int SoFCUnifiedSelection::getPriority(const SoPickedPoint* p) return 0; } -const SoPickedPoint* -SoFCUnifiedSelection::getPickedPoint(SoHandleEventAction* action) const +std::vector +SoFCUnifiedSelection::getPickedList(SoHandleEventAction* action, bool singlePick) const { + ViewProvider *last_vp = 0; + std::vector ret; + const SoPickedPointList & points = action->getPickedPointList(); + for(int i=0,count=points.getLength();i(info.pp->getPath()); + if (this->pcDocument && path && path->containsPath(action->getCurPath())) { + vp = this->pcDocument->getViewProviderByPathFromHead(path); + if(singlePick && last_vp && last_vp!=vp) + return ret; + } + if(!vp || !vp->isDerivedFrom(ViewProviderDocumentObject::getClassTypeId())) { + if(!singlePick) continue; + if(ret.empty()) + ret.push_back(info); + break; + } + info.vpd = static_cast(vp); + if(!info.vpd->useNewSelectionModel() || !info.vpd->isSelectable()) { + if(!singlePick) continue; + if(ret.empty()) { + info.vpd = 0; + ret.push_back(info); + } + break; + } + if(!info.vpd->getElementPicked(info.pp,info.element)) + continue; + + if(singlePick) + last_vp = vp; + ret.push_back(info); + } + + if(ret.size()<=1) return ret; + // To identify the picking of lines in a concave area we have to // get all intersection points. If we have two or more intersection // points where the first is of a face and the second of a line with // almost similar coordinates we use the second point, instead. - const SoPickedPointList & points = action->getPickedPointList(); - if (points.getLength() == 0) - return 0; - else if (points.getLength() == 1) - return points[0]; - - const SoPickedPoint* picked = points[0]; - int picked_prio = getPriority(picked); - const SbVec3f& picked_pt = picked->getPoint(); - for (int i=1; igetPoint(); + int picked_prio = getPriority(ret[0].pp); + auto last_vpd = ret[0].vpd; + const SbVec3f& picked_pt = ret.front().pp->getPoint(); + auto itPicked = ret.begin(); + for(auto it=ret.begin()+1;it!=ret.end();++it) { + auto &info = *it; + if(last_vpd != info.vpd) + break; + + int cur_prio = getPriority(info.pp); + const SbVec3f& cur_pt = info.pp->getPoint(); if ((cur_prio > picked_prio) && picked_pt.equals(cur_pt, 0.01f)) { - picked = cur; + itPicked = it; picked_prio = cur_prio; } } - return picked; + + if(singlePick) { + std::vector sret(itPicked,itPicked+1); + return sret; + } + if(itPicked != ret.begin()) + std::swap(*itPicked, *ret.begin()); + return ret; } void SoFCUnifiedSelection::doAction(SoAction *action) @@ -397,6 +459,8 @@ void SoFCUnifiedSelection::doAction(SoAction *action) currenthighlight = static_cast(sa.getPath()->copy()); currenthighlight->ref(); } + if(useNewSelection.getValue()) + return; } if (selectionMode.getValue() == ON && action->getTypeId() == SoFCSelectionAction::getClassTypeId()) { @@ -408,32 +472,43 @@ void SoFCUnifiedSelection::doAction(SoAction *action) App::DocumentObject* obj = doc->getObject(selaction->SelChange.pObjectName); ViewProvider*vp = Application::Instance->getViewProvider(obj); if (vp && vp->useNewSelectionModel() && vp->isSelectable()) { - SoDetail* detail = vp->getDetail(selaction->SelChange.pSubName); - SoSelectionElementAction::Type type = SoSelectionElementAction::None; - if (selaction->SelChange.Type == SelectionChanges::AddSelection) { - if (detail) - type = SoSelectionElementAction::Append; - else - type = SoSelectionElementAction::All; - } - else { - if (detail) - type = SoSelectionElementAction::Remove; - else - type = SoSelectionElementAction::None; - } + SoDetail *detail = nullptr; + detailPath->truncate(0); + if(!selaction->SelChange.pSubName || !selaction->SelChange.pSubName[0] || + vp->getDetailPath(selaction->SelChange.pSubName,detailPath,true,detail)) + { + SoSelectionElementAction::Type type = SoSelectionElementAction::None; + if (selaction->SelChange.Type == SelectionChanges::AddSelection) { + if (detail) + type = SoSelectionElementAction::Append; + else + type = SoSelectionElementAction::All; + } + else { + if (detail) + type = SoSelectionElementAction::Remove; + else + type = SoSelectionElementAction::None; + } - if (checkSelectionStyle(type,vp)) { - SoSelectionElementAction action(type); - action.setColor(this->colorSelection.getValue()); - action.setElement(detail); - action.apply(vp->getRoot()); + if(checkSelectionStyle(type,vp)) { + SoSelectionElementAction action(type); + action.setColor(this->colorSelection.getValue()); + action.setElement(detail); + if(detailPath->getLength()) + action.apply(detailPath); + else + action.apply(vp->getRoot()); + } } + detailPath->truncate(0); delete detail; } - } - else if (selaction->SelChange.Type == SelectionChanges::ClrSelection || - selaction->SelChange.Type == SelectionChanges::SetSelection) { + }else if (selaction->SelChange.Type == SelectionChanges::ClrSelection) { + SoSelectionElementAction action(SoSelectionElementAction::None); + for(int i=0;igetNumChildren();++i) + action.apply(this->getChild(i)); + }else if(selaction->SelChange.Type == SelectionChanges::SetSelection) { std::vector vps; if (this->pcDocument) vps = this->pcDocument->getViewProvidersOfType(ViewProviderDocumentObject::getClassTypeId()); @@ -452,12 +527,269 @@ void SoFCUnifiedSelection::doAction(SoAction *action) } } } + } else if (selaction->SelChange.Type == SelectionChanges::SetPreselectSignal) { + // selection changes inside the 3d view are handled in handleEvent() + App::Document* doc = App::GetApplication().getDocument(selaction->SelChange.pDocName); + App::DocumentObject* obj = doc->getObject(selaction->SelChange.pObjectName); + ViewProvider*vp = Application::Instance->getViewProvider(obj); + if (vp && vp->isDerivedFrom(ViewProviderDocumentObject::getClassTypeId()) && + vp->useNewSelectionModel() && vp->isSelectable()) + { + detailPath->truncate(0); + SoDetail *det = 0; + if(vp->getDetailPath(selaction->SelChange.pSubName,detailPath,true,det)) { + setHighlight(detailPath,det,static_cast(vp), + selaction->SelChange.pSubName, + selaction->SelChange.x, + selaction->SelChange.y, + selaction->SelChange.z); + } + delete det; + } } + if(useNewSelection.getValue()) + return; } inherited::doAction( action ); } +bool SoFCUnifiedSelection::setHighlight(const PickedInfo &info) { + if(!info.pp) + return setHighlight(0,0,0,0,0.0,0.0,0.0); + const auto &pt = info.pp->getPoint(); + return setHighlight(static_cast(info.pp->getPath()), + info.pp->getDetail(), info.vpd, info.element.c_str(), pt[0],pt[1],pt[2]); +} + +bool SoFCUnifiedSelection::setHighlight(SoFullPath *path, const SoDetail *det, + ViewProviderDocumentObject *vpd, const char *element, float x, float y, float z) +{ + Base::FlagToggler flag(setPreSelection); + + bool highlighted = false; + if(path && path->getLength() && + vpd && vpd->getObject() && vpd->getObject()->getNameInDocument()) + { + const char *docname = vpd->getObject()->getDocument()->getName(); + const char *objname = vpd->getObject()->getNameInDocument(); + + this->preSelection = 1; + static char buf[513]; + snprintf(buf,512,"Preselected: %s.%s.%s (%g, %g, %g)" + ,docname,objname,element + ,fabs(x)>1e-7?x:0.0 + ,fabs(y)>1e-7?y:0.0 + ,fabs(z)>1e-7?z:0.0); + + getMainWindow()->showMessage(QString::fromLatin1(buf)); + + int ret = Gui::Selection().setPreselect(docname,objname,element,x,y,z); + if(ret<0 && currenthighlight) + return true; + + if(ret) { + if (currenthighlight) { + SoHighlightElementAction action; + action.setHighlighted(false); + action.apply(currenthighlight); + currenthighlight->unref(); + currenthighlight = 0; + } + currenthighlight = static_cast(path->copy()); + currenthighlight->ref(); + highlighted = true; + } + } + + if(currenthighlight) { + SoHighlightElementAction action; + action.setHighlighted(highlighted); + action.setColor(this->colorHighlight.getValue()); + action.setElement(det); + action.apply(currenthighlight); + if(!highlighted) { + currenthighlight->unref(); + currenthighlight = 0; + Selection().rmvPreselect(); + } + this->touch(); + } + return highlighted; +} + +bool SoFCUnifiedSelection::setSelection(const std::vector &infos, bool ctrlDown) { + if(infos.empty() || !infos[0].vpd) return false; + + std::vector sels; + if(infos.size()>1) { + for(auto &info : infos) { + if(!info.vpd) continue; + SelectionSingleton::SelObj sel; + sel.pObject = info.vpd->getObject(); + sel.pDoc = sel.pObject->getDocument(); + sel.DocName = sel.pDoc->getName(); + sel.FeatName = sel.pObject->getNameInDocument(); + sel.TypeName = sel.pObject->getTypeId().getName(); + sel.SubName = info.element.c_str(); + const auto &pt = info.pp->getPoint(); + sel.x = pt[0]; + sel.y = pt[1]; + sel.z = pt[2]; + sels.push_back(sel); + } + } + + const auto &info = infos[0]; + auto vpd = info.vpd; + if(!vpd) return false; + const char *objname = vpd->getObject()->getNameInDocument(); + if(!objname) return false; + const char *docname = vpd->getObject()->getDocument()->getName(); + + bool hasNext = false; + const SoPickedPoint * pp = info.pp; + const SoDetail *det = pp->getDetail(); + SoDetail *detNext = 0; + SoFullPath *pPath = static_cast(pp->getPath()); + const auto &pt = pp->getPoint(); + SoSelectionElementAction::Type type = SoSelectionElementAction::None; + HighlightModes mymode = (HighlightModes) this->highlightMode.getValue(); + static char buf[513]; + + if (ctrlDown) { + if(Gui::Selection().isSelected(docname,objname,info.element.c_str(),0)) + Gui::Selection().rmvSelection(docname,objname,info.element.c_str(),&sels); + else { + bool ok = Gui::Selection().addSelection(docname,objname, + info.element.c_str(), pt[0] ,pt[1] ,pt[2], &sels); + if (ok && mymode == OFF) { + snprintf(buf,512,"Selected: %s.%s.%s (%g, %g, %g)", + docname,objname,info.element.c_str() + ,fabs(pt[0])>1e-7?pt[0]:0.0 + ,fabs(pt[1])>1e-7?pt[1]:0.0 + ,fabs(pt[2])>1e-7?pt[2]:0.0); + + getMainWindow()->showMessage(QString::fromLatin1(buf)); + } + } + return true; + } + + // Hierarchy acending + // + // If the clicked subelement is already selected, check if there is an + // upper hierarchy, and select that hierarchy instead. + // + // For example, let's suppose PickedInfo above reports + // 'link.link2.box.Face1', and below Selection().getSelectedElement returns + // 'link.link2.box.', meaning that 'box' is the current selected hierarchy, + // and the user is clicking the box again. So we shall go up one level, + // and select 'link.link2.' + // + + std::string subName = info.element; + std::string objectName = objname; + + const char *subSelected = Gui::Selection().getSelectedElement( + vpd->getObject(),subName.c_str()); + + FC_TRACE("select " << (subSelected?subSelected:"'null'") << ", " << + objectName << ", " << subName); + std::string newElement; + if(subSelected) { + newElement = Data::ComplexGeoData::newElementName(subSelected); + subSelected = newElement.c_str(); + std::string nextsub; + const char *next = strrchr(subSelected,'.'); + if(next && next!=subSelected) { + if(next[1]==0) { + // The convention of dot separated SubName demands a mandatory + // ending dot for every object name reference inside SubName. + // The non-object sub-element, however, must not end with a dot. + // So, next[1]==0 here means current selection is a whole object + // selection (because no sub-element), so we shall search + // upwards for the second last dot, which is the end of the + // parent name of the current selected object + for(--next;next!=subSelected;--next) { + if(*next == '.') break; + } + } + if(*next == '.') + nextsub = std::string(subSelected,next-subSelected+1); + } + if(nextsub.length() || *subSelected!=0) { + hasNext = true; + subName = nextsub; + detailPath->truncate(0); + if(vpd->getDetailPath(subName.c_str(),detailPath,true,detNext) && + detailPath->getLength()) + { + pPath = detailPath; + det = detNext; + FC_TRACE("select next " << objectName << ", " << subName); + } + } + } + +#if 0 // ViewProviderDocumentObject now has default implementation of getElementPicked + + // If no next hierarchy is found, do another try on view provider hierarchies, + // which is used by geo feature group. + if(!hasNext) { + bool found = false; + auto vps = this->pcDocument->getViewProvidersByPath(pPath); + for(auto it=vps.begin();it!=vps.end();++it) { + auto vpdNext = it->first; + if(Gui::Selection().isSelected(vpdNext->getObject(),"")) { + found = true; + continue; + } + if(!found || !vpdNext->useNewSelectionModel() || !vpdNext->isSelectable()) + continue; + hasNext = true; + vpd = vpdNext; + det = 0; + pPath->truncate(it->second+1); + objectName = vpd->getObject()->getNameInDocument(); + subName = ""; + break; + } + } +#endif + + FC_TRACE("clearing selection"); + Gui::Selection().clearSelection(); + FC_TRACE("add selection"); + bool ok = Gui::Selection().addSelection(docname, objectName.c_str() ,subName.c_str(), + pt[0] ,pt[1] ,pt[2], &sels); + if (ok) + type = hasNext?SoSelectionElementAction::All:SoSelectionElementAction::Append; + + if (mymode == OFF) { + snprintf(buf,512,"Selected: %s.%s.%s (%g, %g, %g)", + docname, objectName.c_str() ,subName.c_str() + ,fabs(pt[0])>1e-7?pt[0]:0.0 + ,fabs(pt[1])>1e-7?pt[1]:0.0 + ,fabs(pt[2])>1e-7?pt[2]:0.0); + + getMainWindow()->showMessage(QString::fromLatin1(buf)); + } + + if (pPath && checkSelectionStyle(type,vpd)) { + FC_TRACE("applying action"); + SoSelectionElementAction action(type); + action.setColor(this->colorSelection.getValue()); + action.setElement(det); + action.apply(pPath); + FC_TRACE("applied action"); + this->touch(); + } + + if(detNext) delete detNext; + return true; +} + // doc from parent void SoFCUnifiedSelection::handleEvent(SoHandleEventAction * action) @@ -468,7 +800,6 @@ SoFCUnifiedSelection::handleEvent(SoHandleEventAction * action) return; } - static char buf[513]; HighlightModes mymode = (HighlightModes) this->highlightMode.getValue(); const SoEvent * event = action->getEvent(); @@ -482,7 +813,6 @@ SoFCUnifiedSelection::handleEvent(SoHandleEventAction * action) // return; //} - // // If this is a mouseMotion event, then check for locate highlighting // @@ -492,72 +822,11 @@ SoFCUnifiedSelection::handleEvent(SoHandleEventAction * action) // set has been selected. if (mymode == AUTO || mymode == ON) { // check to see if the mouse is over our geometry... - const SoPickedPoint * pp = this->getPickedPoint(action); - SoFullPath *pPath = (pp != NULL) ? (SoFullPath *) pp->getPath() : NULL; - ViewProvider *vp = 0; - ViewProviderDocumentObject* vpd = 0; - if (this->pcDocument && pPath && pPath->containsPath(action->getCurPath())) - vp = this->pcDocument->getViewProviderByPathFromTail(pPath); - if (vp && vp->isDerivedFrom(ViewProviderDocumentObject::getClassTypeId())) - vpd = static_cast(vp); - - //SbBool old_state = highlighted; - highlighted = false; - if (vpd && vpd->useNewSelectionModel() && vpd->isSelectable()) { - std::string documentName = vpd->getObject()->getDocument()->getName(); - std::string objectLabel = vpd->getObject()->Label.getStrValue(); - std::string objectName = vpd->getObject()->getNameInDocument(); - std::string subElementName = vpd->getElement(pp ? pp->getDetail() : 0); - - this->preSelection = 1; - static char buf[513]; - snprintf(buf,512,"Preselected: %s - %s.%s.%s (%g, %g, %g)" - ,objectLabel.c_str() - ,documentName.c_str() - ,objectName.c_str() - ,subElementName.c_str() - ,pp->getPoint()[0] - ,pp->getPoint()[1] - ,pp->getPoint()[2]); - - getMainWindow()->showMessage(QString::fromUtf8(buf)); - - setPreSelection = true; - if (Gui::Selection().setPreselect(documentName.c_str() - ,objectName.c_str() - ,subElementName.c_str() - ,pp->getPoint()[0] - ,pp->getPoint()[1] - ,pp->getPoint()[2])){ - - SoSearchAction sa; - sa.setNode(vp->getRoot()); - sa.apply(vp->getRoot()); - if (sa.getPath()) { - highlighted = true; - if (currenthighlight && currenthighlight->getTail() != sa.getPath()->getTail()) { - SoHighlightElementAction action; - action.setHighlighted(false); - action.apply(currenthighlight); - currenthighlight->unref(); - currenthighlight = 0; - //old_state = !highlighted; - } - else if (currenthighlight) { - // clean-up the highlight path before assigning a new path - currenthighlight->unref(); - currenthighlight = 0; - } - - currenthighlight = static_cast(sa.getPath()->copy()); - currenthighlight->ref(); - } - } - - setPreSelection = false; - } - // nothing picked - else if (!pp) { + auto infos = this->getPickedList(action,true); + if(infos.size()) + setHighlight(infos[0]); + else { + setHighlight(PickedInfo()); if (this->preSelection > 0) { this->preSelection = 0; // touch() makes sure to call GLRenderBelowPath so that the cursor can be updated @@ -565,19 +834,6 @@ SoFCUnifiedSelection::handleEvent(SoHandleEventAction * action) this->touch(); } } - - if (currenthighlight/* && old_state != highlighted*/) { - SoHighlightElementAction action; - action.setHighlighted(highlighted); - action.setColor(this->colorHighlight.getValue()); - action.setElement(pp ? pp->getDetail() : 0); - action.apply(currenthighlight); - if (!highlighted) { - currenthighlight->unref(); - currenthighlight = 0; - } - this->touch(); - } } } // mouse press events for (de)selection @@ -586,107 +842,9 @@ SoFCUnifiedSelection::handleEvent(SoHandleEventAction * action) const SoMouseButtonEvent* e = static_cast(event); if (SoMouseButtonEvent::isButtonReleaseEvent(e,SoMouseButtonEvent::BUTTON1)) { // check to see if the mouse is over a geometry... - const SoPickedPoint * pp = this->getPickedPoint(action); - SoFullPath *pPath = (pp != NULL) ? (SoFullPath *) pp->getPath() : NULL; - ViewProvider *vp = 0; - ViewProviderDocumentObject* vpd = 0; - if (this->pcDocument && pPath && pPath->containsPath(action->getCurPath())) - vp = this->pcDocument->getViewProviderByPathFromTail(pPath); - if (vp && vp->isDerivedFrom(ViewProviderDocumentObject::getClassTypeId())) - vpd = static_cast(vp); - if (vpd && vpd->useNewSelectionModel() && vpd->isSelectable()) { - SoSelectionElementAction::Type type = SoSelectionElementAction::None; - std::string documentName = vpd->getObject()->getDocument()->getName(); - std::string objectName = vpd->getObject()->getNameInDocument(); - std::string subElementName = vpd->getElement(pp ? pp->getDetail() : 0); - if (event->wasCtrlDown()) { - if (Gui::Selection().isSelected(documentName.c_str() - ,objectName.c_str() - ,subElementName.c_str())) { - Gui::Selection().rmvSelection(documentName.c_str() - ,objectName.c_str() - ,subElementName.c_str()); - type = SoSelectionElementAction::Remove; - } - else { - bool ok = Gui::Selection().addSelection(documentName.c_str() - ,objectName.c_str() - ,subElementName.c_str() - ,pp->getPoint()[0] - ,pp->getPoint()[1] - ,pp->getPoint()[2]); - if (ok) - type = SoSelectionElementAction::Append; - if (mymode == OFF) { - snprintf(buf,512,"Selected: %s.%s.%s (%g, %g, %g)" - ,documentName.c_str() - ,objectName.c_str() - ,subElementName.c_str() - ,pp->getPoint()[0] - ,pp->getPoint()[1] - ,pp->getPoint()[2]); - - getMainWindow()->showMessage(QString::fromLatin1(buf)); - } - } - } - else { // Ctrl - if (!Gui::Selection().isSelected(documentName.c_str() - ,objectName.c_str() - ,subElementName.c_str())) { - Gui::Selection().clearSelection(documentName.c_str()); - bool ok = Gui::Selection().addSelection(documentName.c_str() - ,objectName.c_str() - ,subElementName.c_str() - ,pp->getPoint()[0] - ,pp->getPoint()[1] - ,pp->getPoint()[2]); - if (ok) - type = SoSelectionElementAction::Append; - } - else { - Gui::Selection().clearSelection(documentName.c_str()); - bool ok = Gui::Selection().addSelection(documentName.c_str() - ,objectName.c_str() - ,0 - ,pp->getPoint()[0] - ,pp->getPoint()[1] - ,pp->getPoint()[2]); - if (ok) - type = SoSelectionElementAction::All; - } - - if (mymode == OFF) { - snprintf(buf,512,"Selected: %s.%s.%s (%g, %g, %g)" - ,documentName.c_str() - ,objectName.c_str() - ,subElementName.c_str() - ,pp->getPoint()[0] - ,pp->getPoint()[1] - ,pp->getPoint()[2]); - - getMainWindow()->showMessage(QString::fromLatin1(buf)); - } - } - - action->setHandled(); - if (currenthighlight && checkSelectionStyle(type, vpd)) { - SoSelectionElementAction action(type); - action.setColor(this->colorSelection.getValue()); - action.setElement(pp ? pp->getDetail() : 0); - action.apply(currenthighlight); - this->touch(); - } - } // picked point - else if (!pp) { - // user clicked onto empty space but in case Ctrl key was pressed - // then mark the action as handled to avoid that the navigation style - // processes the action and clears the selection - if (event->wasCtrlDown()) { - action->setHandled(); - } - - } + auto infos = this->getPickedList(action,!Selection().needPickedList()); + if(setSelection(infos,event->wasCtrlDown())) + action->setHandled(); } // mouse release } @@ -831,7 +989,8 @@ void SoSelectionElementAction::initClass() SO_ACTION_ADD_METHOD(SoPointSet,callDoAction); } -SoSelectionElementAction::SoSelectionElementAction (Type t) : _type(t), _det(0) +SoSelectionElementAction::SoSelectionElementAction (Type t, bool secondary) + : _type(t), _det(0), _secondary(secondary) { SO_ACTION_CONSTRUCTOR(SoSelectionElementAction); } @@ -942,3 +1101,654 @@ void SoVRMLAction::callDoAction(SoAction *action, SoNode *node) node->doAction(action); } + +// --------------------------------------------------------------------------------- +bool SoFCSelectionRoot::StackComp::operator()(const Stack &a, const Stack &b) const { + if(a.size()-a.offset < b.size()-b.offset) + return true; + if(a.size()-a.offset > b.size()-b.offset) + return false; + auto it1=a.rbegin(), end1=a.rend()-a.offset; + auto it2=b.rbegin(); + for(;it1!=end1;++it1,++it2) { + if(*it1 < *it2) + return true; + if(*it1 > *it2) + return false; + } + return false; +} +// --------------------------------------------------------------------------------- +SoSeparator::CacheEnabled SoFCSeparator::CacheMode = SoSeparator::AUTO; +SO_NODE_SOURCE(SoFCSeparator); + +SoFCSeparator::SoFCSeparator(bool trackCacheMode) + :trackCacheMode(trackCacheMode) +{ + SO_NODE_CONSTRUCTOR(SoFCSeparator); + if(!trackCacheMode) + renderCaching = SoSeparator::OFF; +} + +void SoFCSeparator::GLRenderBelowPath(SoGLRenderAction * action) { + if(trackCacheMode && renderCaching.getValue()!=CacheMode) + renderCaching = CacheMode; + inherited::GLRenderBelowPath(action); +} + +void SoFCSeparator::initClass(void) +{ + SO_NODE_INIT_CLASS(SoFCSeparator,SoSeparator,"FCSeparator"); +} + +void SoFCSeparator::finish() +{ + atexit_cleanup(); +} + + +// --------------------------------------------------------------------------------- + +SoFCSelectionRoot::Stack SoFCSelectionRoot::SelStack; +std::unordered_map SoFCSelectionRoot::ActionStacks; +SoFCSelectionRoot::ColorStack SoFCSelectionRoot::SelColorStack; +SoFCSelectionRoot::ColorStack SoFCSelectionRoot::HlColorStack; +SoFCSelectionRoot* SoFCSelectionRoot::ShapeColorNode; + +SO_NODE_SOURCE(SoFCSelectionRoot); + +SoFCSelectionRoot::SoFCSelectionRoot(bool trackCacheMode) + :SoFCSeparator(trackCacheMode) +{ + SO_NODE_CONSTRUCTOR(SoFCSelectionRoot); +} + +SoFCSelectionRoot::~SoFCSelectionRoot() +{ +} + +void SoFCSelectionRoot::initClass(void) +{ + SO_NODE_INIT_CLASS(SoFCSelectionRoot,SoFCSeparator,"FCSelectionRoot"); +} + +void SoFCSelectionRoot::finish() +{ + atexit_cleanup(); +} + +SoNode *SoFCSelectionRoot::getCurrentRoot(bool front, SoNode *def) { + if(SelStack.size()) + return front?SelStack.front():SelStack.back(); + return def; +} + +SoFCSelectionContextBasePtr SoFCSelectionRoot::getNodeContext( + Stack &stack, SoNode *node, SoFCSelectionContextBasePtr def) +{ + if(stack.empty()) + return def; + + SoFCSelectionRoot *front = stack.front(); + + // NOTE: _node is not necssary of type SoFCSelectionRoot, but it is safe + // here since we only use it as searching key, although it is probably not + // a best practice. + stack.front() = static_cast(node); + + auto it = front->contextMap.find(stack); + stack.front() = front; + if(it!=front->contextMap.end()) + return it->second; + return SoFCSelectionContextBasePtr(); +} + +SoFCSelectionContextBasePtr +SoFCSelectionRoot::getNodeContext2(Stack &stack, SoNode *node, SoFCSelectionContextBase::MergeFunc *merge) +{ + SoFCSelectionContextBasePtr ret; + if(stack.empty() || stack.back()->contextMap2.empty()) + return ret; + + int status = 0; + auto *back = stack.back(); + auto &map = back->contextMap2; + stack.back() = static_cast(node); + for(stack.offset=0;stack.offsetsecond:SoFCSelectionContextBasePtr(); + status = merge(status,ret,ctx,stack.offset==stack.size()-1?0:stack[stack.offset]); + if(status<0) + break; + } + stack.offset = 0; + stack.back() = back; + return ret; +} + +std::pair SoFCSelectionRoot::findActionContext( + SoAction *action, SoNode *_node, bool create, bool erase) +{ + std::pair res(false,0); + if(action->isOfType(SoSelectionElementAction::getClassTypeId())) + res.first = static_cast(action)->isSecondary(); + + auto it = ActionStacks.find(action); + if(it==ActionStacks.end() || it->second.empty()) + return res; + + auto &stack = it->second; + + auto node = static_cast(_node); + + if(res.first) { + auto back = stack.back(); + stack.back() = node; + if(create) + res.second = &back->contextMap2[stack]; + else { + auto it = back->contextMap2.find(stack); + if(it!=back->contextMap2.end()) { + res.second = &it->second; + if(erase) + back->contextMap2.erase(it); + } + } + stack.back() = back; + }else{ + auto front = stack.front(); + stack.front() = node; + if(create) + res.second = &front->contextMap[stack]; + else { + auto it = front->contextMap.find(stack); + if(it!=front->contextMap.end()) { + res.second = &it->second; + if(erase) + front->contextMap.erase(it); + } + } + stack.front() = front; + } + return res; +} + +static std::time_t _CyclicLastReported; + +void SoFCSelectionRoot::renderPrivate(SoGLRenderAction * action, bool inPath) { + if(ViewParams::instance()->getCoinCycleCheck() + && !SelStack.nodeSet.insert(this).second) + { + std::time_t t = std::time(0); + if(_CyclicLastReported < t) { + _CyclicLastReported = t+5; + FC_ERR("Cyclic scene graph: " << getName()); + } + return; + } + SelStack.push_back(this); + auto ctx2 = std::static_pointer_cast(getNodeContext2(SelStack,this,SelContext::merge)); + if(!ctx2 || !ctx2->hideAll) { + auto state = action->getState(); + SelContextPtr ctx = getRenderContext(this); + bool colorPushed = false; + if(!ShapeColorNode && overrideColor && + !SoOverrideElement::getDiffuseColorOverride(state) && + (!ctx || (!ctx->selAll && !ctx->hideAll))) + { + ShapeColorNode = this; + colorPushed = true; + state->push(); + auto &packer = ShapeColorNode->shapeColorPacker; + auto &trans = ShapeColorNode->transOverride; + auto &color = ShapeColorNode->colorOverride; + if(!SoOverrideElement::getTransparencyOverride(state) && trans) { + SoLazyElement::setTransparency(state, ShapeColorNode, 1, &trans, &packer); + SoOverrideElement::setTransparencyOverride(state,ShapeColorNode,true); + } + SoLazyElement::setDiffuse(state, ShapeColorNode, 1, &color, &packer); + SoOverrideElement::setDiffuseColorOverride(state,ShapeColorNode,true); + SoMaterialBindingElement::set(state, ShapeColorNode, SoMaterialBindingElement::OVERALL); + SoOverrideElement::setMaterialBindingOverride(state,ShapeColorNode,true); + + SoTextureEnabledElement::set(state,ShapeColorNode,false); + } + if(!ctx) { + if(inPath) + SoSeparator::GLRenderInPath(action); + else + SoSeparator::GLRenderBelowPath(action); + } else { + bool selPushed; + bool hlPushed; + if((selPushed = ctx->selAll)) { + SelColorStack.push_back(ctx->selColor); + state->push(); + auto &color = SelColorStack.back(); + SoLazyElement::setEmissive(state, &color); + SoOverrideElement::setEmissiveColorOverride(state,this,true); + if (SoLazyElement::getLightModel(state) == SoLazyElement::BASE_COLOR) { + auto &packer = shapeColorPacker; + SoLazyElement::setDiffuse(state, this, 1, &color, &packer); + SoOverrideElement::setDiffuseColorOverride(state,this,true); + SoMaterialBindingElement::set(state, this, SoMaterialBindingElement::OVERALL); + SoOverrideElement::setMaterialBindingOverride(state,this,true); + } + } + if((hlPushed = ctx->hlAll)) + HlColorStack.push_back(ctx->hlColor); + if(inPath) + SoSeparator::GLRenderInPath(action); + else + SoSeparator::GLRenderBelowPath(action); + if(selPushed) { + SelColorStack.pop_back(); + state->pop(); + } + if(hlPushed) + HlColorStack.pop_back(); + } + if(colorPushed) { + ShapeColorNode = 0; + state->pop(); + } + } + SelStack.pop_back(); + SelStack.nodeSet.erase(this); +} + +void SoFCSelectionRoot::GLRenderBelowPath(SoGLRenderAction * action) { + renderPrivate(action,false); +} + +void SoFCSelectionRoot::GLRenderInPath(SoGLRenderAction * action) { + if(action->getCurPathCode() == SoAction::BELOW_PATH) + return GLRenderBelowPath(action); + renderPrivate(action,true); +} + +bool SoFCSelectionRoot::checkColorOverride(SoState *state) { + if(ShapeColorNode) { + if(!SoOverrideElement::getDiffuseColorOverride(state)) { + state->push(); + auto &packer = ShapeColorNode->shapeColorPacker; + auto &trans = ShapeColorNode->transOverride; + auto &color = ShapeColorNode->colorOverride; + if(!SoOverrideElement::getTransparencyOverride(state) && trans) { + SoLazyElement::setTransparency(state, ShapeColorNode, 1, &trans, &packer); + SoOverrideElement::setTransparencyOverride(state,ShapeColorNode,true); + } + SoLazyElement::setDiffuse(state, ShapeColorNode, 1, &color, &packer); + SoOverrideElement::setDiffuseColorOverride(state,ShapeColorNode,true); + SoMaterialBindingElement::set(state, ShapeColorNode, SoMaterialBindingElement::OVERALL); + SoOverrideElement::setMaterialBindingOverride(state,ShapeColorNode,true); + + SoTextureEnabledElement::set(state,ShapeColorNode,false); + return true; + } + } + return false; +} + +void SoFCSelectionRoot::checkSelection(bool &sel, SbColor &selColor, bool &hl, SbColor &hlColor) { + sel = false; + hl = false; + if((sel = !SelColorStack.empty())) + selColor = SelColorStack.back(); + if((hl = !HlColorStack.empty())) + hlColor = HlColorStack.back(); +} + +void SoFCSelectionRoot::resetContext() { + contextMap.clear(); +} + + +#define BEGIN_ACTION \ + auto &stack = ActionStacks[action];\ + if(ViewParams::instance()->getCoinCycleCheck() \ + && !stack.nodeSet.insert(this).second) \ + {\ + std::time_t t = std::time(0);\ + if(_CyclicLastReported < t) {\ + _CyclicLastReported = t+5;\ + FC_ERR("Cyclic scene graph: " << getName());\ + }\ + return;\ + }\ + stack.push_back(this);\ + auto size = stack.size(); + +#define END_ACTION \ + if(stack.size()!=size || stack.back()!=this)\ + FC_ERR("action stack fault");\ + else {\ + stack.nodeSet.erase(this);\ + stack.pop_back();\ + if(stack.empty())\ + ActionStacks.erase(action);\ + } + +void SoFCSelectionRoot::pick(SoPickAction * action) { + BEGIN_ACTION; + if(doActionPrivate(stack,action)) + inherited::pick(action); + END_ACTION; +} + +void SoFCSelectionRoot::rayPick(SoRayPickAction * action) { + BEGIN_ACTION; + if(doActionPrivate(stack,action)) + inherited::rayPick(action); + END_ACTION; +} + +void SoFCSelectionRoot::handleEvent(SoHandleEventAction * action) { + BEGIN_ACTION; + inherited::handleEvent(action); + END_ACTION; +} + +void SoFCSelectionRoot::search(SoSearchAction * action) { + BEGIN_ACTION; + inherited::search(action); + END_ACTION; +} + +void SoFCSelectionRoot::getPrimitiveCount(SoGetPrimitiveCountAction * action) { + BEGIN_ACTION; + inherited::getPrimitiveCount(action); + END_ACTION; +} + +void SoFCSelectionRoot::getBoundingBox(SoGetBoundingBoxAction * action) +{ + BEGIN_ACTION; + if(doActionPrivate(stack,action)) + inherited::getBoundingBox(action); + END_ACTION; +} + +void SoFCSelectionRoot::getMatrix(SoGetMatrixAction * action) { + BEGIN_ACTION; + if(doActionPrivate(stack,action)) + inherited::getMatrix(action); + END_ACTION; +} + +void SoFCSelectionRoot::callback(SoCallbackAction *action) { + BEGIN_ACTION; + inherited::callback(action); + END_ACTION; +} + +void SoFCSelectionRoot::doAction(SoAction *action) { + BEGIN_ACTION + if(doActionPrivate(stack,action)) + inherited::doAction(action); + END_ACTION +} + +bool SoFCSelectionRoot::doActionPrivate(Stack &stack, SoAction *action) { + // Selection action short-circuit optimization. In case of whole object + // selection/pre-selection, we shall store a SelContext keyed by ourself. + // And the action traversal can be short-curcuited once the first targeted + // SoFCSelectionRoot is found here. New fuction checkSelection() is exposed + // to check for whole object selection. This greatly imporve performance on + // large group. + + SelContextPtr ctx2; + bool ctx2Searched = false; + bool isTail = false; + if(action->getCurPathCode()==SoAction::IN_PATH) { + auto path = action->getPathAppliedTo(); + if(path) { + isTail = path->getTail()==this || + (path->getLength()>1 + && path->getNodeFromTail(1)==this + && path->getTail()->isOfType(SoSwitch::getClassTypeId())); + } + + if(!action->isOfType(SoSelectionElementAction::getClassTypeId())) { + ctx2Searched = true; + ctx2 = std::static_pointer_cast(getNodeContext2(stack,this,SelContext::merge)); + if(ctx2 && ctx2->hideAll) + return false; + } + if(!isTail) + return true; + }else if(action->getWhatAppliedTo()!=SoAction::NODE && action->getCurPathCode()!=SoAction::BELOW_PATH) + return true; + + if(action->isOfType(SoSelectionElementAction::getClassTypeId())) { + auto selAction = static_cast(action); + if(selAction->isSecondary()) { + if(selAction->getType() == SoSelectionElementAction::Show || + (selAction->getType() == SoSelectionElementAction::Color && + selAction->getColors().empty() && + action->getWhatAppliedTo()==SoAction::NODE)) + { + auto ctx = getActionContext(action,this,SelContextPtr(),false); + if(ctx && ctx->hideAll) { + ctx->hideAll = false; + if(!ctx->hlAll && !ctx->selAll) + removeActionContext(action,this); + touch(); + } + // applied to a node means clear all visibility setting, so + // return true to propgate the action + return selAction->getType()==SoSelectionElementAction::Color || + action->getWhatAppliedTo()==SoAction::NODE; + + }else if(selAction->getType() == SoSelectionElementAction::Hide) { + if(action->getCurPathCode()==SoAction::BELOW_PATH || isTail) { + auto ctx = getActionContext(action,this,SelContextPtr()); + if(ctx && !ctx->hideAll) { + ctx->hideAll = true; + touch(); + } + return false; + } + } + return true; + } + + if(selAction->getType() == SoSelectionElementAction::None) { + if(action->getWhatAppliedTo() == SoAction::NODE) { + // Here the 'select none' action is applied to a node, and we + // are the first SoFCSelectionRoot encounted (which means all + // children stores selection context here, both whole object + // and element selection), then we can simply perform the + // action by clearing the selection context here, and save the + // time for traversing a potentially large amount of children + // nodes. + resetContext(); + touch(); + return false; + } + + auto ctx = getActionContext(action,this,SelContextPtr(),false); + if(ctx && ctx->selAll) { + ctx->selAll = false; + touch(); + return false; + } + } else if(selAction->getType() == SoSelectionElementAction::All) { + auto ctx = getActionContext(action,this,SelContextPtr()); + assert(ctx); + ctx->selAll = true; + ctx->selColor = selAction->getColor(); + touch(); + return false; + } + return true; + } + + if(action->isOfType(SoHighlightElementAction::getClassTypeId())) { + auto hlAction = static_cast(action); + if(hlAction->isHighlighted()) { + if(hlAction->getElement()) { + auto ctx = getActionContext(action,this,SelContextPtr(),false); + if(ctx && ctx->hlAll) { + ctx->hlAll = false; + touch(); + } + } else { + auto ctx = getActionContext(action,this,SelContextPtr()); + assert(ctx); + ctx->hlAll = true; + ctx->hlColor = hlAction->getColor(); + touch(); + return false; + } + } else { + auto ctx = getActionContext(action,this,SelContextPtr(),false); + if(ctx && ctx->hlAll) { + ctx->hlAll = false; + touch(); + return false; + } + } + return true; + } + + if(!ctx2Searched) { + ctx2 = std::static_pointer_cast(getNodeContext2(stack,this,SelContext::merge)); + if(ctx2 && ctx2->hideAll) + return false; + } + return true; +} + +int SoFCSelectionRoot::SelContext::merge(int status, SoFCSelectionContextBasePtr &output, + SoFCSelectionContextBasePtr input, SoFCSelectionRoot *) +{ + auto ctx = std::dynamic_pointer_cast(input); + if(ctx && ctx->hideAll) { + output = ctx; + return -1; + } + return status; +} + +///////////////////////////////////////////////////////////////////////////// + +SO_NODE_SOURCE(SoFCPathAnnotation); + +SoFCPathAnnotation::SoFCPathAnnotation() +{ + SO_NODE_CONSTRUCTOR(SoFCPathAnnotation); + path = 0; + tmpPath = 0; + det = 0; +} + +SoFCPathAnnotation::~SoFCPathAnnotation() +{ + if(path) path->unref(); + if(tmpPath) tmpPath->unref(); + delete det; +} + +void SoFCPathAnnotation::finish() +{ + atexit_cleanup(); +} + +void SoFCPathAnnotation::initClass(void) +{ + SO_NODE_INIT_CLASS(SoFCPathAnnotation,SoSeparator,"Separator"); +} + +void SoFCPathAnnotation::GLRender(SoGLRenderAction * action) +{ + switch (action->getCurPathCode()) { + case SoAction::NO_PATH: + case SoAction::BELOW_PATH: + this->GLRenderBelowPath(action); + break; + case SoAction::OFF_PATH: + break; + case SoAction::IN_PATH: + this->GLRenderInPath(action); + break; + } +} + +void SoFCPathAnnotation::GLRenderBelowPath(SoGLRenderAction * action) +{ + if(!path || !path->getLength() || !tmpPath || !tmpPath->getLength()) + return; + + if(path->getLength() != tmpPath->getLength()) { + // The auditing SoPath may be truncated due to harmless things such as + // fliping a SoSwitch sibling node. So we keep an unauditing SoTempPath + // around to try to restore the path. + for(int i=path->getLength()-1;igetLength()-1;++i) { + auto children = path->getNode(i)->getChildren(); + if(children) { + int idx = children->find(tmpPath->getNode(i+1)); + if(idx >= 0) { + path->append(idx); + continue; + } + } + tmpPath->unref(); + tmpPath = 0; + return; + } + } + + SoState * state = action->getState(); + SoGLCacheContextElement::shouldAutoCache(state, SoGLCacheContextElement::DONT_AUTO_CACHE); + + if (action->isRenderingDelayedPaths()) { + SbBool zbenabled = glIsEnabled(GL_DEPTH_TEST); + if (zbenabled) glDisable(GL_DEPTH_TEST); + inherited::GLRenderInPath(action); + if (zbenabled) glEnable(GL_DEPTH_TEST); + } + else { + SoCacheElement::invalidate(action->getState()); + auto curPath = action->getCurPath(); + SoPath *newPath = new SoPath(curPath->getLength()+path->getLength()); + newPath->append(curPath); + newPath->append(path); + action->addDelayedPath(newPath); + } +} + +void SoFCPathAnnotation::GLRenderInPath(SoGLRenderAction * action) +{ + GLRenderBelowPath(action); +} + +void SoFCPathAnnotation::setDetail(SoDetail *d) { + if(d!=det) { + delete det; + det = d; + } +} + +void SoFCPathAnnotation::setPath(SoPath *newPath) { + if(path) { + path->unref(); + coinRemoveAllChildren(this); + path = 0; + if(tmpPath) { + tmpPath->unref(); + tmpPath = 0; + } + } + if(!newPath || !newPath->getLength()) + return; + + tmpPath = new SoTempPath(newPath->getLength()); + tmpPath->ref(); + for(int i=0;igetLength();++i) + tmpPath->append(newPath->getNode(i)); + path = newPath->copy(); + path->ref(); + addChild(path->getNode(0)); +} + diff --git a/src/Gui/SoFCUnifiedSelection.h b/src/Gui/SoFCUnifiedSelection.h index 6e564cbdb7..2429167e77 100644 --- a/src/Gui/SoFCUnifiedSelection.h +++ b/src/Gui/SoFCUnifiedSelection.h @@ -32,7 +32,10 @@ #include #include #include "View3DInventorViewer.h" +#include "SoFCSelectionContext.h" #include +#include +#include class SoFullPath; class SoPickedPoint; @@ -42,6 +45,7 @@ class SoDetail; namespace Gui { class Document; +class ViewProviderDocumentObject; /** Unified Selection node * This is the new selection node for the 3D Viewer which will @@ -75,6 +79,7 @@ public: SoSFEnum highlightMode; SoSFEnum selectionMode; SoSFBool selectionRole; + SoSFBool useNewSelection; virtual void doAction(SoAction *action); //virtual void GLRender(SoGLRenderAction * action); @@ -86,6 +91,8 @@ public: bool checkSelectionStyle(int type, ViewProvider *vp); + static bool hasHighlight(); + friend class View3DInventorViewer; protected: @@ -114,19 +121,262 @@ private: //SbBool isHighlighted(SoAction *action); //SbBool preRender(SoGLRenderAction *act, GLint &oldDepthFunc); static int getPriority(const SoPickedPoint* p); - const SoPickedPoint* getPickedPoint(SoHandleEventAction*) const; + + struct PickedInfo { + const SoPickedPoint *pp; + ViewProviderDocumentObject *vpd; + std::string element; + PickedInfo():pp(0),vpd(0) + {} + }; + + bool setHighlight(const PickedInfo &); + bool setHighlight(SoFullPath *path, const SoDetail *det, + ViewProviderDocumentObject *vpd, const char *element, float x, float y, float z); + bool setSelection(const std::vector &, bool ctrlDown=false); + + std::vector getPickedList(SoHandleEventAction* action, bool singlePick) const; + Gui::Document *pcDocument; static SoFullPath * currenthighlight; + SoFullPath * detailPath; SoPathList selectionList; - SbBool highlighted; SbBool setPreSelection; + // -1 = not handled, 0 = not selected, 1 = selected int32_t preSelection; SoColorPacker colorpacker; }; +class GuiExport SoFCPathAnnotation : public SoSeparator { + typedef SoSeparator inherited; + + SO_NODE_HEADER(Gui::SoFCPathAnnotation); +public: + static void initClass(void); + static void finish(void); + SoFCPathAnnotation(); + + void setPath(SoPath *); + SoPath *getPath() {return path;} + void setDetail(SoDetail *d); + SoDetail *getDetail() {return det;} + + virtual void GLRenderBelowPath(SoGLRenderAction * action); + virtual void GLRender(SoGLRenderAction * action); + virtual void GLRenderInPath(SoGLRenderAction * action); + +protected: + virtual ~SoFCPathAnnotation(); + +protected: + SoPath *path; + SoTempPath *tmpPath; + SoDetail *det; +}; + +class GuiExport SoFCSeparator : public SoSeparator { + typedef SoSeparator inherited; + + SO_NODE_HEADER(Gui::SoFCSeparator); + +public: + static void initClass(void); + static void finish(void); + SoFCSeparator(bool trackCacheMode=true); + + virtual void GLRenderBelowPath(SoGLRenderAction * action); + + static void setCacheMode(CacheEnabled mode) { + CacheMode = mode; + } + static CacheEnabled getCacheMode() { + return CacheMode; + } + +private: + bool trackCacheMode; + static CacheEnabled CacheMode; +}; + +class GuiExport SoFCSelectionRoot : public SoFCSeparator { + typedef SoFCSeparator inherited; + + SO_NODE_HEADER(Gui::SoFCSelectionRoot); + +public: + static void initClass(void); + static void finish(void); + SoFCSelectionRoot(bool trackCacheMode=false); + + virtual void GLRenderBelowPath(SoGLRenderAction * action); + virtual void GLRenderInPath(SoGLRenderAction * action); + + virtual void doAction(SoAction *action); + virtual void pick(SoPickAction * action); + virtual void rayPick(SoRayPickAction * action); + virtual void handleEvent(SoHandleEventAction * action); + virtual void search(SoSearchAction * action); + virtual void getPrimitiveCount(SoGetPrimitiveCountAction * action); + virtual void getBoundingBox(SoGetBoundingBoxAction * action); + virtual void getMatrix(SoGetMatrixAction * action); + virtual void callback(SoCallbackAction *action); + + template + static std::shared_ptr getRenderContext(SoNode *node, std::shared_ptr def = std::shared_ptr()) { + return std::dynamic_pointer_cast(getNodeContext(SelStack,node,def)); + } + + /** Returns selection context for rendering. + * + * @param node: the querying node + * @param def: default context if none is found + * @param ctx2: secondary context output + * + * @return Returned the primary context for selection, and the context is + * always stored in the first encounted SoFCSelectionRoot in the path. It + * is keyed using the entires sequence of SoFCSelectionRoot along the path + * to \c node, replacing the first SoFCSelectionRoot with the given node. + * + * @return Secondary context returned in \c ctx2 is for customized + * highlighting, and is not affected by mouse event. The highlight is + * applied manually using SoSelectionElementAction. It is stored in the + * last encountered SoFCSelectionRoot, and is keyed using the querying + * \c node and (if there are more than one SoFCSelectionRoot along the + * path) the first SoFCSelectionRoot. The reason is so that any link to a + * node (new links means additional SoFCSelectionRoot added in front) with + * customized subelement highlight will also show the highlight. Secondary + * context can be chained, which why the secondary context type must provide + * an function called merge() for getRenderContext() to merge the context. + * See SoFCSelectionContext::merge() for an implementation of merging multiple + * context. + * + * @note For simplicity reason, currently secondary context is only freed + * when the storage SoFCSSelectionRoot node is freed. + */ + template + static std::shared_ptr getRenderContext(SoNode *node, std::shared_ptr def, std::shared_ptr &ctx2) + { + ctx2 = std::dynamic_pointer_cast(getNodeContext2(SelStack,node,T::merge)); + return std::dynamic_pointer_cast(getNodeContext(SelStack,node,def)); + } + + /** Get the selection context for an action. + * + * @param action: the action. SoSelectionElementAction has any option to + * query for secondary context. \sa getRenderContext for detail about + * secondary context + * @param node: the querying node + * @param def: default context if none is found, only used if querying + * non-secondary context + * @param create: create a new context if none is found + * + * @return If no SoFCSelectionRoot is found in the current path of action, + * \c def is returned. Otherwise a selection context returned. A new one + * will be created if none is found. + */ + template + static std::shared_ptr getActionContext( + SoAction *action, SoNode *node, std::shared_ptr def=std::shared_ptr(), bool create=true) + { + auto res = findActionContext(action,node,create,false); + if(!res.second) { + if(res.first) + return std::shared_ptr(); + // default context is only applicable for non-secondary context query + return def; + } + // make a new context if there is none + auto &ctx = *res.second; + if(ctx) { + auto ret = std::dynamic_pointer_cast(ctx); + if(!ret) + ctx.reset(); + } + if(!ctx && create) + ctx = std::make_shared(); + return std::static_pointer_cast(ctx); + } + + static bool removeActionContext(SoAction *action, SoNode *node) { + return findActionContext(action,node,false,true).second!=0; + } + + static void checkSelection(bool &sel, SbColor &selColor, bool &hl, SbColor &hlColor); + + static SoNode *getCurrentRoot(bool front, SoNode *def); + + void resetContext(); + + static bool checkColorOverride(SoState *state); + + bool hasColorOverride() const { + return overrideColor; + } + + void setColorOverride(App::Color c) { + overrideColor = true; + colorOverride = SbColor(c.r,c.g,c.b); + transOverride = c.a; + } + + void removeColorOverride() { + overrideColor = false; + } + +protected: + virtual ~SoFCSelectionRoot(); + + void renderPrivate(SoGLRenderAction *, bool inPath); + + class Stack : public std::vector { + public: + std::unordered_set nodeSet; + size_t offset = 0; + }; + + static SoFCSelectionContextBasePtr getNodeContext( + Stack &stack, SoNode *node, SoFCSelectionContextBasePtr def); + static SoFCSelectionContextBasePtr getNodeContext2( + Stack &stack, SoNode *node, SoFCSelectionContextBase::MergeFunc *merge); + static std::pair findActionContext( + SoAction *action, SoNode *node, bool create, bool erase); + + static Stack SelStack; + static std::unordered_map ActionStacks; + + struct StackComp { + bool operator()(const Stack &a, const Stack &b) const; + }; + + typedef std::map ContextMap; + ContextMap contextMap; + ContextMap contextMap2;//holding secondary context + + struct SelContext: SoFCSelectionContextBase { + public: + SbColor selColor; + SbColor hlColor; + bool selAll = false; + bool hlAll = false; + bool hideAll = false; + static MergeFunc merge; + }; + typedef std::shared_ptr SelContextPtr; + typedef std::vector ColorStack; + static ColorStack SelColorStack; + static ColorStack HlColorStack; + static SoFCSelectionRoot *ShapeColorNode; + bool overrideColor = false; + SbColor colorOverride; + float transOverride; + SoColorPacker shapeColorPacker; + + bool doActionPrivate(Stack &stack, SoAction *); +}; + /** * @author Werner Mayer */ @@ -167,17 +417,36 @@ class GuiExport SoSelectionElementAction : public SoAction SO_ACTION_HEADER(SoSelectionElementAction); public: - enum Type {None, Append, Remove, All}; + enum Type {None, Append, Remove, All, Color, Hide, Show}; - SoSelectionElementAction (Type); + SoSelectionElementAction (Type=None, bool secondary = false); ~SoSelectionElementAction(); Type getType() const; + void setType(Type type) { + _type = type; + } + void setColor(const SbColor&); const SbColor& getColor() const; void setElement(const SoDetail*); const SoDetail* getElement() const; + bool isSecondary() const {return _secondary;} + void setSecondary(bool enable) { + _secondary = enable; + } + + const std::map &getColors() const { + return _colors; + } + void setColors(const std::map &colors) { + _colors = colors; + } + void swapColors(std::map &colors) { + _colors.swap(colors); + } + static void initClass(); protected: @@ -190,6 +459,8 @@ private: Type _type; SbColor _color; const SoDetail* _det; + std::map _colors; + bool _secondary; }; /** diff --git a/src/Gui/View3DInventor.cpp b/src/Gui/View3DInventor.cpp index 1c78952eb5..5872137ec4 100644 --- a/src/Gui/View3DInventor.cpp +++ b/src/Gui/View3DInventor.cpp @@ -171,6 +171,7 @@ View3DInventor::View3DInventor(Gui::Document* pcDocument, QWidget* parent, OnChange(*hGrp,"ShowNaviCube"); OnChange(*hGrp,"CornerNaviCube"); OnChange(*hGrp,"UseVBO"); + OnChange(*hGrp,"RenderCache"); OnChange(*hGrp,"Orthographic"); OnChange(*hGrp,"HeadlightColor"); OnChange(*hGrp,"HeadlightDirection"); @@ -376,6 +377,9 @@ void View3DInventor::OnChange(ParameterGrp::SubjectType &rCaller,ParameterGrp::M else if (strcmp(Reason,"UseVBO") == 0) { _viewer->setEnabledVBO(rGrp.GetBool("UseVBO",false)); } + else if (strcmp(Reason,"RenderCache") == 0) { + _viewer->setRenderCache(rGrp.GetInt("RenderCache",0)); + } else if (strcmp(Reason,"Orthographic") == 0) { // check whether a perspective or orthogrphic camera should be set if (rGrp.GetBool("Orthographic", true)) diff --git a/src/Gui/View3DInventorViewer.cpp b/src/Gui/View3DInventorViewer.cpp index b28184e0cb..8a5df66d45 100644 --- a/src/Gui/View3DInventorViewer.cpp +++ b/src/Gui/View3DInventorViewer.cpp @@ -75,6 +75,8 @@ # include # include # include +# include +# include # include # include # include @@ -85,6 +87,8 @@ # include #endif +#include + #if !defined(FC_OS_MACOSX) # include # include @@ -100,6 +104,7 @@ #include #include #include +#include #include "View3DInventorViewer.h" #include "ViewProviderDocumentObject.h" @@ -142,6 +147,10 @@ #include "WinNativeGestureRecognizers.h" #include "Document.h" +#include "ViewProviderLink.h" + +FC_LOG_LEVEL_INIT("3DViewer",true,true); + //#define FC_LOGGING_CB using namespace Gui; @@ -344,7 +353,8 @@ public: // ************************************************************************* View3DInventorViewer::View3DInventorViewer(QWidget* parent, const QtGLWidget* sharewidget) - : Quarter::SoQTQuarterAdaptor(parent, sharewidget), editViewProvider(0), navigation(0), + : Quarter::SoQTQuarterAdaptor(parent, sharewidget), SelectionObserver(false,0), + editViewProvider(0), navigation(0), renderType(Native), framebuffer(0), axisCross(0), axisGroup(0), editing(false), redirected(false), allowredir(false), overrideMode("As Is"), _viewerPy(0) { @@ -352,7 +362,8 @@ View3DInventorViewer::View3DInventorViewer(QWidget* parent, const QtGLWidget* sh } View3DInventorViewer::View3DInventorViewer(const QtGLFormat& format, QWidget* parent, const QtGLWidget* sharewidget) - : Quarter::SoQTQuarterAdaptor(format, parent, sharewidget), editViewProvider(0), navigation(0), + : Quarter::SoQTQuarterAdaptor(format, parent, sharewidget), SelectionObserver(false,0), + editViewProvider(0), navigation(0), renderType(Native), framebuffer(0), axisCross(0), axisGroup(0), editing(false), redirected(false), allowredir(false), overrideMode("As Is"), _viewerPy(0) { @@ -361,11 +372,18 @@ View3DInventorViewer::View3DInventorViewer(const QtGLFormat& format, QWidget* pa void View3DInventorViewer::init() { + static bool _cacheModeInited; + if(!_cacheModeInited) { + _cacheModeInited = true; + pcViewProviderRoot = 0; + setRenderCache(-1); + } + shading = true; fpsEnabled = false; vboEnabled = false; - Gui::Selection().Attach(this); + attachSelection(); // Coin should not clear the pixel-buffer, so the background image // is not removed. @@ -471,6 +489,32 @@ void View3DInventorViewer::init() pcViewProviderRoot->addChild(cb); #endif + pcGroupOnTop = new SoSeparator; + pcGroupOnTop->ref(); + pcViewProviderRoot->addChild(pcGroupOnTop); + + auto pcGroupOnTopPickStyle = new SoPickStyle; + pcGroupOnTopPickStyle->style = SoPickStyle::UNPICKABLE; + // pcGroupOnTopPickStyle->style = SoPickStyle::SHAPE_ON_TOP; + pcGroupOnTopPickStyle->setOverride(true); + pcGroupOnTop->addChild(pcGroupOnTopPickStyle); + + coin_setenv("COIN_SEPARATE_DIFFUSE_TRANSPARENCY_OVERRIDE", "1", TRUE); + auto pcOnTopMaterial = new SoMaterial; + pcOnTopMaterial->transparency = 0.5; + pcOnTopMaterial->diffuseColor.setIgnored(true); + pcOnTopMaterial->setOverride(true); + pcGroupOnTop->addChild(pcOnTopMaterial); + + pcGroupOnTopSel = new SoFCSelectionRoot; + pcGroupOnTopSel->setName("GroupOnTopSel"); + pcGroupOnTopSel->ref(); + pcGroupOnTop->addChild(pcGroupOnTopSel); + pcGroupOnTopPreSel = new SoFCSelectionRoot; + pcGroupOnTopPreSel->setName("GroupOnTopPreSel"); + pcGroupOnTopPreSel->ref(); + pcGroupOnTop->addChild(pcGroupOnTopPreSel); + // Set our own render action which show a bounding box if // the SoFCSelection::BOX style is set // @@ -573,13 +617,23 @@ View3DInventorViewer::~View3DInventorViewer() this->backlight->unref(); this->backlight = 0; + this->pcGroupOnTop->unref(); + this->pcGroupOnTopPreSel->unref(); + this->pcGroupOnTopSel->unref(); + + this->pcEditingRoot->unref(); + this->pcEditingTransform->unref(); + + if(this->pcClipPlane) + this->pcClipPlane->unref(); + delete this->navigation; // Note: When closing the application the main window doesn't exist any more. if (getMainWindow()) getMainWindow()->setPaneText(2, QLatin1String("")); - Gui::Selection().Detach(this); + detachSelection(); removeEventFilter(viewerEventFilter); delete viewerEventFilter; @@ -615,6 +669,15 @@ void View3DInventorViewer::setDocument(Gui::Document* pcDocument) // write the document the viewer belongs to the selection node guiDocument = pcDocument; selectionRoot->pcDocument = pcDocument; + + if(pcDocument) { + const auto &sels = Selection().getSelection(pcDocument->getDocument()->getName(),0); + for(auto &sel : sels) { + SelectionChanges Chng(SelectionChanges::ShowSelection, + sel.DocName,sel.FeatName,sel.SubName); + onSelectionChanged(Chng); + } + } } Document* View3DInventorViewer::getDocument() { @@ -631,21 +694,232 @@ void View3DInventorViewer::initialize() this->axiscrossSize = 10; } -/// @cond DOXERR -void View3DInventorViewer::OnChange(Gui::SelectionSingleton::SubjectType& rCaller, - Gui::SelectionSingleton::MessageType Reason) -{ - Q_UNUSED(rCaller); - if (Reason.Type == SelectionChanges::AddSelection || - Reason.Type == SelectionChanges::RmvSelection || - Reason.Type == SelectionChanges::SetSelection || - Reason.Type == SelectionChanges::ClrSelection) { - SoFCSelectionAction cAct(Reason); - cAct.apply(pcViewProviderRoot); +void View3DInventorViewer::clearGroupOnTop() { + if(objectsOnTop.size() || objectsOnTopPreSel.size()) { + objectsOnTop.clear(); + objectsOnTopPreSel.clear(); + SoSelectionElementAction action(SoSelectionElementAction::None,true); + action.apply(pcGroupOnTopPreSel); + action.apply(pcGroupOnTopSel); + coinRemoveAllChildren(pcGroupOnTopSel); + coinRemoveAllChildren(pcGroupOnTopPreSel); + FC_LOG("clear annoation"); } - else if (Reason.Type == SelectionChanges::RmvPreselect || - Reason.Type == SelectionChanges::SetPreselect) { - SoFCHighlightAction cAct(Reason); +} + +void View3DInventorViewer::checkGroupOnTop(const SelectionChanges &Reason) { + if(Reason.Type == SelectionChanges::SetSelection || Reason.Type == SelectionChanges::ClrSelection) { + clearGroupOnTop(); + if(Reason.Type == SelectionChanges::ClrSelection) + return; + } + if(Reason.Type == SelectionChanges::RmvPreselect || + Reason.Type == SelectionChanges::RmvPreselectSignal) + { + SoSelectionElementAction action(SoSelectionElementAction::None,true); + action.apply(pcGroupOnTopPreSel); + coinRemoveAllChildren(pcGroupOnTopPreSel); + objectsOnTopPreSel.clear(); + return; + } + if(!getDocument() || !Reason.pDocName || !Reason.pDocName[0] || !Reason.pObjectName) + return; + auto obj = getDocument()->getDocument()->getObject(Reason.pObjectName); + if(!obj || !obj->getNameInDocument()) + return; + std::string key(obj->getNameInDocument()); + key += '.'; + auto subname = Reason.pSubName; + if(subname) + key += subname; + if(Reason.Type == SelectionChanges::RmvSelection) { + auto &objs = objectsOnTop; + auto pcGroup = pcGroupOnTopSel; + auto it = objs.find(key.c_str()); + if(it == objs.end()) + return; + int index = pcGroup->findChild(it->second); + if(index >= 0) { + auto node = static_cast(it->second); + SoSelectionElementAction action(node->getDetail()? + SoSelectionElementAction::Remove:SoSelectionElementAction::None,true); + auto path = node->getPath(); + SoTempPath tmpPath(2+path?path->getLength():0); + tmpPath.ref(); + tmpPath.append(pcGroup); + tmpPath.append(node); + tmpPath.append(node->getPath()); + action.setElement(node->getDetail()); + action.apply(&tmpPath); + tmpPath.unrefNoDelete(); + pcGroup->removeChild(index); + FC_LOG("remove annoation " << Reason.Type << " " << key); + }else + FC_LOG("remove annoation object " << Reason.Type << " " << key); + objs.erase(it); + return; + } + + auto &objs = Reason.Type==SelectionChanges::SetPreselect?objectsOnTopPreSel:objectsOnTop; + auto pcGroup = Reason.Type==SelectionChanges::SetPreselect?pcGroupOnTopPreSel:pcGroupOnTopSel; + + if(objs.find(key.c_str())!=objs.end()) + return; + auto vp = dynamic_cast( + Application::Instance->getViewProvider(obj)); + if(!vp || !vp->isSelectable() || !vp->isShow()) + return; + auto svp = vp; + if(subname && *subname) { + auto sobj = obj->getSubObject(subname); + if(!sobj || !sobj->getNameInDocument()) + return; + if(sobj!=obj) { + svp = dynamic_cast( + Application::Instance->getViewProvider(sobj)); + if(!svp || !svp->isSelectable()) + return; + } + } + int onTop; + // onTop==2 means on top only if whole object is selected, + // onTop==3 means on top only if some sub-element is selected + // onTop==1 means either + onTop = Gui::Selection().needPickedList() + || vp->OnTopWhenSelected.getValue() + || svp->OnTopWhenSelected.getValue(); + if(Reason.Type == SelectionChanges::SetPreselect) { + SoHighlightElementAction action; + action.setHighlighted(true); + action.setColor(selectionRoot->colorHighlight.getValue()); + action.apply(pcGroupOnTopPreSel); + if(!onTop) + onTop = 2; + }else { + if(!onTop) + return; + SoSelectionElementAction action(SoSelectionElementAction::All); + action.setColor(selectionRoot->colorSelection.getValue()); + action.apply(pcGroupOnTopSel); + } + if(onTop==2 || onTop==3) { + if(subname && *subname) { + size_t len = strlen(subname); + if(subname[len-1]=='.') { + // ending with '.' means whole object selection + if(onTop == 3) + return; + }else if(onTop==2) + return; + }else if(onTop==3) + return; + } + + std::vector groups; + auto grpVp = vp; + for(auto childVp=vp;;childVp=grpVp) { + auto grp = App::GeoFeatureGroupExtension::getGroupOfObject(childVp->getObject()); + if(!grp || !grp->getNameInDocument()) break; + grpVp = dynamic_cast( + Application::Instance->getViewProvider(grp)); + if(!grpVp) break; + auto childRoot = grpVp->getChildRoot(); + auto modeSwitch = grpVp->getModeSwitch(); + auto idx = modeSwitch->whichChild.getValue(); + if(idx<0 || idx>=modeSwitch->getNumChildren() || + modeSwitch->getChild(idx)!=childRoot) + { + FC_LOG("skip " << obj->getFullName() << '.' << (subname?subname:"") + << ", hidden inside geo group"); + return; + } + if(childRoot->findChild(childVp->getRoot())<0) { + FC_WARN("cannot find '" << childVp->getObject()->getFullName() + << "' in geo group '" << grp->getNameInDocument() << "'"); + break; + } + groups.push_back(grpVp); + } + + SoTempPath path(10); + path.ref(); + + for(auto it=groups.rbegin();it!=groups.rend();++it) { + auto grpVp = *it; + path.append(grpVp->getRoot()); + path.append(grpVp->getModeSwitch()); + path.append(grpVp->getChildRoot()); + } + + SoDetail *det = 0; + if(vp->getDetailPath(subname, &path,true,det) && path.getLength()) { + auto node = new SoFCPathAnnotation; + node->setPath(&path); + pcGroup->addChild(node); + if(det) { + SoSelectionElementAction action(SoSelectionElementAction::Append,true); + action.setElement(det); + SoTempPath tmpPath(path.getLength()+2); + tmpPath.ref(); + tmpPath.append(pcGroup); + tmpPath.append(node); + tmpPath.append(&path); + action.apply(&tmpPath); + tmpPath.unrefNoDelete(); + node->setDetail(det); + det = 0; + } + FC_LOG("add annoation " << Reason.Type << " " << key); + objs[key.c_str()] = node; + } + delete det; + path.unrefNoDelete(); +} + +/// @cond DOXERR +void View3DInventorViewer::onSelectionChanged(const SelectionChanges &_Reason) +{ + if(!getDocument()) + return; + + SelectionChanges Reason(_Reason); + + if(Reason.pDocName && *Reason.pDocName && + strcmp(getDocument()->getDocument()->getName(),Reason.pDocName)!=0) + return; + + switch(Reason.Type) { + case SelectionChanges::ShowSelection: + case SelectionChanges::HideSelection: + if(Reason.Type == SelectionChanges::ShowSelection) + Reason.Type = SelectionChanges::AddSelection; + else + Reason.Type = SelectionChanges::RmvSelection; + // fall through + case SelectionChanges::SetPreselect: + if(Reason.SubType!=2) // 2 means it is triggered from tree view + break; + case SelectionChanges::RmvPreselect: + case SelectionChanges::RmvPreselectSignal: + case SelectionChanges::SetSelection: + case SelectionChanges::AddSelection: + case SelectionChanges::RmvSelection: + case SelectionChanges::ClrSelection: + checkGroupOnTop(Reason); + break; + case SelectionChanges::SetPreselectSignal: + break; + default: + return; + } + + if(Reason.Type == SelectionChanges::RmvPreselect || + Reason.Type == SelectionChanges::RmvPreselectSignal) + { + SoFCHighlightAction cAct(SelectionChanges::RmvPreselect); + cAct.apply(pcViewProviderRoot); + } else { + SoFCSelectionAction cAct(Reason); cAct.apply(pcViewProviderRoot); } } @@ -672,7 +946,8 @@ void View3DInventorViewer::addViewProvider(ViewProvider* pcProvider) SoSeparator* root = pcProvider->getRoot(); if (root) { - pcViewProviderRoot->addChild(root); + if(pcProvider->canAddToSceneGraph()) + pcViewProviderRoot->addChild(root); _ViewProviderMap[root] = pcProvider; } @@ -695,8 +970,10 @@ void View3DInventorViewer::removeViewProvider(ViewProvider* pcProvider) SoSeparator* root = pcProvider->getRoot(); - if (root && (pcViewProviderRoot->findChild(root) != -1)) { - pcViewProviderRoot->removeChild(root); + if (root) { + int index = pcViewProviderRoot->findChild(root); + if(index>=0) + pcViewProviderRoot->removeChild(index); _ViewProviderMap.erase(root); } @@ -731,9 +1008,10 @@ SbBool View3DInventorViewer::setEditingViewProvider(Gui::ViewProvider* p, int Mo void View3DInventorViewer::resetEditingViewProvider() { if (this->editViewProvider) { + // In case the event action still has grabbed a node when leaving edit mode // force to release it now - SoEventManager* mgr = this->getSoEventManager(); + SoEventManager* mgr = getSoEventManager(); SoHandleEventAction* heaction = mgr->getHandleEventAction(); if (heaction && heaction->getGrabber()) heaction->releaseGrabber(); @@ -870,6 +1148,26 @@ bool View3DInventorViewer::isEnabledVBO() const return vboEnabled; } +void View3DInventorViewer::setRenderCache(int mode) +{ + if(mode<0) { + ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath + ("User parameter:BaseApp/Preferences/View"); + int setting = hGrp->GetInt("RenderCache",0); + if(mode==-2) { + if(pcViewProviderRoot && setting!=1) + pcViewProviderRoot->renderCaching = SoSeparator::ON; + mode = 2; + }else{ + if(pcViewProviderRoot) + pcViewProviderRoot->renderCaching = SoSeparator::AUTO; + mode = setting; + } + } + SoFCSeparator::setCacheMode( + mode==0?SoSeparator::AUTO:(mode==1?SoSeparator::ON:SoSeparator::OFF)); +} + void View3DInventorViewer::setEnabledNaviCube(bool on) { naviCubeEnabled = on; @@ -2433,54 +2731,51 @@ void View3DInventorViewer::viewAll(float factor) void View3DInventorViewer::viewSelection() { -#if 0 - // Search for all SoFCSelection nodes - SoSearchAction searchAction; - searchAction.setType(SoFCSelection::getClassTypeId()); - searchAction.setInterest(SoSearchAction::ALL); - searchAction.apply(pcViewProviderRoot); - - SoPathList& paths = searchAction.getPaths(); - int countPaths = paths.getLength(); - - SoGroup* root = new SoGroup(); - root->ref(); - - for (int i=0; igetTail(); - - if (!node || node->getTypeId() != SoFCSelection::getClassTypeId()) - continue; // should not happen - - SoFCSelection* select = static_cast(node); - - // Check only document and object name but not sub-element name - if (Selection().isSelected(select->documentName.getValue().getString(), - select->objectName.getValue().getString())) { - root->addChild(select); - } + Base::BoundBox3d bbox; + for(auto &sel : Selection().getSelection(0,0)) { + auto vp = Application::Instance->getViewProvider(sel.pObject); + if(!vp) + continue; + bbox.Add(vp->getBoundingBox(sel.SubName,true)); } -#else - SoGroup* root = new SoGroup(); - root->ref(); - - std::vector selection = Selection().getObjectsOfType(App::DocumentObject::getClassTypeId()); - for (std::vector::iterator it = selection.begin(); it != selection.end(); ++it) { - ViewProvider* vp = Application::Instance->getViewProvider(*it); - if (vp) { - root->addChild(vp->getRoot()); - } - } - -#endif - SoCamera* cam = this->getSoRenderManager()->getCamera(); - if (cam) - cam->viewAll(root, this->getSoRenderManager()->getViewportRegion()); - - root->unref(); + if (cam && bbox.IsValid()) { + SbBox3f box(bbox.MinX,bbox.MinY,bbox.MinZ,bbox.MaxX,bbox.MaxY,bbox.MaxZ); +#if (COIN_MAJOR_VERSION >= 4) + float aspectratio = getSoRenderManager()->getViewportRegion().getViewportAspectRatio(); + switch (cam->viewportMapping.getValue()) { + case SoCamera::CROP_VIEWPORT_FILL_FRAME: + case SoCamera::CROP_VIEWPORT_LINE_FRAME: + case SoCamera::CROP_VIEWPORT_NO_FRAME: + aspectratio = 1.0f; + break; + default: + break; + } + cam->viewBoundingBox(box,aspectratio,1.0); +#else + SoTempPath path(2); + path.ref(); + auto pcGroup = new SoGroup; + pcGroup->ref(); + auto pcTransform = new SoTransform; + pcGroup->addChild(pcTransform); + pcTransform->translation = box.getCenter(); + auto *pcCube = new SoCube; + pcGroup->addChild(pcCube); + float sizeX,sizeY,sizeZ; + box.getSize(sizeX,sizeY,sizeZ); + pcCube->width = sizeX; + pcCube->height = sizeY; + pcCube->depth = sizeZ; + path.append(pcGroup); + path.append(pcCube); + cam->viewAll(&path,getSoRenderManager()->getViewportRegion()); + path.unrefNoDelete(); + pcGroup->unref(); +#endif + } } /*! @@ -2958,16 +3253,16 @@ void View3DInventorViewer::removeEventCallback(SoType eventtype, SoEventCallback ViewProvider* View3DInventorViewer::getViewProviderByPath(SoPath* path) const { - // FIXME Use the viewprovider map introduced for the selection - for (std::set::const_iterator it = _ViewProviderSet.begin(); it != _ViewProviderSet.end(); ++it) { - for (int i = 0; igetLength(); i++) { - SoNode* node = path->getNode(i); - if ((*it)->getRoot() == node) { - return (*it); + for (int i = 0; i < path->getLength(); i++) { + SoNode* node = path->getNode(i); + + if (node->isOfType(SoSeparator::getClassTypeId())) { + auto it = _ViewProviderMap.find(static_cast(node)); + if (it != _ViewProviderMap.end()) { + return it->second; } } } - return 0; } @@ -3096,3 +3391,4 @@ void View3DInventorViewer::dragLeaveEvent(QDragLeaveEvent *e) { inherited::dragLeaveEvent(e); } + diff --git a/src/Gui/View3DInventorViewer.h b/src/Gui/View3DInventorViewer.h index fe53f68b5b..13b0c2b535 100644 --- a/src/Gui/View3DInventorViewer.h +++ b/src/Gui/View3DInventorViewer.h @@ -54,6 +54,7 @@ class SbBox2s; class SoVectorizeAction; class QImage; class SoGroup; +class SoPickStyle; class NaviCube; namespace Quarter = SIM::Coin3D::Quarter; @@ -65,7 +66,6 @@ class SoFCBackgroundGradient; class NavigationStyle; class SoFCUnifiedSelection; class Document; -class SoFCUnifiedSelection; class GLGraphicsItem; class SoShapeScale; class ViewerEventFilter; @@ -73,7 +73,7 @@ class ViewerEventFilter; /** GUI view into a 3D scene provided by View3DInventor * */ -class GuiExport View3DInventorViewer : public Quarter::SoQTQuarterAdaptor, public Gui::SelectionSingleton::ObserverType +class GuiExport View3DInventorViewer : public Quarter::SoQTQuarterAdaptor, public SelectionObserver { typedef Quarter::SoQTQuarterAdaptor inherited; @@ -133,8 +133,9 @@ public: void init(); /// Observer message from the Selection - virtual void OnChange(Gui::SelectionSingleton::SubjectType &rCaller, - Gui::SelectionSingleton::MessageType Reason); + virtual void onSelectionChanged(const SelectionChanges &Reason); + void checkGroupOnTop(const SelectionChanges &Reason); + void clearGroupOnTop(); SoDirectionalLight* getBacklight(void) const; void setBacklight(SbBool on); @@ -352,6 +353,7 @@ public: NaviCube* getNavigationCube() const; void setEnabledVBO(bool b); bool isEnabledVBO() const; + void setRenderCache(int); NavigationStyle* navigationStyle() const; @@ -415,6 +417,13 @@ private: SoDirectionalLight* backlight; SoSeparator * pcViewProviderRoot; + + SoGroup * pcGroupOnTop; + SoGroup * pcGroupOnTopSel; + SoGroup * pcGroupOnTopPreSel; + std::map objectsOnTop; + std::map objectsOnTopPreSel; + SoEventCallback* pEventCallback; NavigationStyle* navigation; SoFCUnifiedSelection* selectionRoot; diff --git a/src/Gui/ViewProvider.cpp b/src/Gui/ViewProvider.cpp index 4ba9a26f6b..80c8f07a0e 100644 --- a/src/Gui/ViewProvider.cpp +++ b/src/Gui/ViewProvider.cpp @@ -57,6 +57,7 @@ #include "View3DInventorViewer.h" #include "SoFCDB.h" #include "ViewProviderExtension.h" +#include "SoFCUnifiedSelection.h" #include "ViewParams.h" #include @@ -84,7 +85,7 @@ ViewProvider::ViewProvider() { setStatus(UpdateData, true); - pcRoot = new SoSeparator(); + pcRoot = new SoFCSeparator; pcRoot->ref(); pcModeSwitch = new SoSwitch(); pcModeSwitch->ref(); @@ -94,6 +95,8 @@ ViewProvider::ViewProvider() pcRoot->addChild(pcModeSwitch); sPixmap = "px"; pcModeSwitch->whichChild = _iActualMode; + + setRenderCacheMode(ViewParams::instance()->getRenderCache()); } ViewProvider::~ViewProvider() @@ -974,6 +977,11 @@ void ViewProvider::beforeDelete() { ext->extensionBeforeDelete(); } +void ViewProvider::setRenderCacheMode(int mode) { + pcRoot->renderCaching = + mode==0?SoSeparator::AUTO:(mode==1?SoSeparator::ON:SoSeparator::OFF); +} + Base::BoundBox3d ViewProvider::getBoundingBox(const char *subname, bool transform, MDIView *view) const { if(!pcRoot || !pcModeSwitch || pcRoot->findChild(pcModeSwitch)<0) return Base::BoundBox3d(); diff --git a/src/Gui/ViewProvider.h b/src/Gui/ViewProvider.h index 9cdcfb4147..93ba62a3a9 100644 --- a/src/Gui/ViewProvider.h +++ b/src/Gui/ViewProvider.h @@ -496,6 +496,8 @@ public: int getDefaultMode() const; //@} + virtual void setRenderCacheMode(int); + protected: /** Helper method to check that the node is valid, i.e. it must not cause * and infinite recursion. diff --git a/src/Mod/Part/Gui/SoBrepEdgeSet.cpp b/src/Mod/Part/Gui/SoBrepEdgeSet.cpp index 4c0f75c337..6352f53fb9 100644 --- a/src/Mod/Part/Gui/SoBrepEdgeSet.cpp +++ b/src/Mod/Part/Gui/SoBrepEdgeSet.cpp @@ -56,6 +56,7 @@ # include # include # include +# include #endif #include "SoBrepEdgeSet.h" @@ -64,36 +65,97 @@ using namespace PartGui; - SO_NODE_SOURCE(SoBrepEdgeSet); +struct SoBrepEdgeSet::SelContext: Gui::SoFCSelectionContext { + std::vector hl, sl; +}; + void SoBrepEdgeSet::initClass() { SO_NODE_INIT_CLASS(SoBrepEdgeSet, SoIndexedLineSet, "IndexedLineSet"); } SoBrepEdgeSet::SoBrepEdgeSet() + :selContext(std::make_shared()) + ,selContext2(std::make_shared()) { SO_NODE_CONSTRUCTOR(SoBrepEdgeSet); - SO_NODE_ADD_FIELD(highlightIndex, (-1)); - SO_NODE_ADD_FIELD(selectionIndex, (-1)); - selectionIndex.setNum(0); } void SoBrepEdgeSet::GLRender(SoGLRenderAction *action) { - if (this->selectionIndex.getNum() > 0) - renderSelection(action); - if (this->highlightIndex.getValue() >= 0) - renderHighlight(action); - inherited::GLRender(action); + auto state = action->getState(); + selCounter.checkRenderCache(state); + + SelContextPtr ctx2; + SelContextPtr ctx = Gui::SoFCSelectionRoot::getRenderContext(this,selContext,ctx2); + if(ctx2 && ctx2->selectionIndex.empty()) + return; + if(selContext2->checkGlobal(ctx)) { + if(selContext2->isSelectAll()) { + selContext2->sl.clear(); + selContext2->sl.push_back(-1); + }else if(ctx) + selContext2->sl = ctx->sl; + if(selContext2->highlightIndex==INT_MAX) { + selContext2->hl.clear(); + selContext2->hl.push_back(-1); + }else if(ctx) + selContext2->hl = ctx->hl; + ctx = selContext2; + } + + if(ctx && ctx->highlightIndex==INT_MAX) { + if(ctx->selectionIndex.empty() || ctx->isSelectAll()) { + if(ctx2) { + ctx2->selectionColor = ctx->highlightColor; + renderSelection(action,ctx2); + } else + renderHighlight(action,ctx); + }else{ + if(!action->isRenderingDelayedPaths()) + renderSelection(action,ctx); + if(ctx2) { + ctx2->selectionColor = ctx->highlightColor; + renderSelection(action,ctx2); + } else + renderHighlight(action,ctx); + if(action->isRenderingDelayedPaths()) + renderSelection(action,ctx); + } + return; + } + + if(!action->isRenderingDelayedPaths()) + renderHighlight(action,ctx); + if(ctx && ctx->selectionIndex.size()) { + if(ctx->isSelectAll()) { + if(ctx2) { + ctx2->selectionColor = ctx->selectionColor; + renderSelection(action,ctx2); + }else if(ctx->isSelectAll()) + renderSelection(action,ctx); + if(action->isRenderingDelayedPaths()) + renderHighlight(action,ctx); + return; + } + if(!action->isRenderingDelayedPaths()) + renderSelection(action,ctx); + } + if(ctx2 && ctx2->selectionIndex.size()) + renderSelection(action,ctx2,false); + else + inherited::GLRender(action); // Workaround for #0000433 //#if !defined(FC_OS_WIN32) - if (this->highlightIndex.getValue() >= 0) - renderHighlight(action); - if (this->selectionIndex.getNum() > 0) - renderSelection(action); + if(!action->isRenderingDelayedPaths()) + renderHighlight(action,ctx); + if(ctx && ctx->selectionIndex.size()) + renderSelection(action,ctx); + if(action->isRenderingDelayedPaths()) + renderHighlight(action,ctx); //#endif } @@ -125,17 +187,18 @@ void SoBrepEdgeSet::renderShape(const SoGLCoordinateElement * const coords, } } -void SoBrepEdgeSet::renderHighlight(SoGLRenderAction *action) +void SoBrepEdgeSet::renderHighlight(SoGLRenderAction *action, SelContextPtr ctx) { + if(!ctx || ctx->highlightIndex<0) + return; + SoState * state = action->getState(); state->push(); //SoLineWidthElement::set(state, this, 4.0f); - SoLazyElement::setEmissive(state, &this->highlightColor); - SoOverrideElement::setEmissiveColorOverride(state, this, true); - SoLazyElement::setDiffuse(state, this,1, &this->highlightColor,&this->colorpacker1); - SoOverrideElement::setDiffuseColorOverride(state, this, true); - SoLazyElement::setLightModel(state, SoLazyElement::BASE_COLOR); + SoLazyElement::setEmissive(state, &ctx->highlightColor); + packedColor = ctx->highlightColor.getPackedValue(0.0); + SoLazyElement::setPacked(state, this,1, &packedColor,false); const SoCoordinateElement * coords; const SbVec3f * normals; @@ -152,33 +215,35 @@ void SoBrepEdgeSet::renderHighlight(SoGLRenderAction *action) SoMaterialBundle mb(action); mb.sendFirst(); // make sure we have the correct material - int num = (int)this->hl.size(); + int num = (int)ctx->hl.size(); if (num > 0) { - const int32_t* id = &(this->hl[0]); - if (!validIndexes(coords, this->hl)) { - SoDebugError::postWarning("SoBrepEdgeSet::renderHighlight", "highlightIndex out of range"); + if (ctx->hl[0] < 0) { + renderShape(static_cast(coords), cindices, numcindices); } else { - renderShape(static_cast(coords), id, num); + const int32_t* id = &(ctx->hl[0]); + if (!validIndexes(coords, ctx->hl)) { + SoDebugError::postWarning("SoBrepEdgeSet::renderHighlight", "highlightIndex out of range"); + } + else { + renderShape(static_cast(coords), id, num); + } } } state->pop(); } -void SoBrepEdgeSet::renderSelection(SoGLRenderAction *action) +void SoBrepEdgeSet::renderSelection(SoGLRenderAction *action, SelContextPtr ctx, bool push) { - int numSelected = this->selectionIndex.getNum(); - if (numSelected == 0) return; - SoState * state = action->getState(); - state->push(); - //SoLineWidthElement::set(state, this, 4.0f); + if(push){ + state->push(); + //SoLineWidthElement::set(state, this, 4.0f); - SoLazyElement::setEmissive(state, &this->selectionColor); - SoOverrideElement::setEmissiveColorOverride(state, this, true); - SoLazyElement::setDiffuse(state, this,1, &this->selectionColor,&this->colorpacker2); - SoOverrideElement::setDiffuseColorOverride(state, this, true); - SoLazyElement::setLightModel(state, SoLazyElement::BASE_COLOR); + SoLazyElement::setEmissive(state, &ctx->selectionColor); + packedColor = ctx->selectionColor.getPackedValue(0.0); + SoLazyElement::setPacked(state, this,1, &packedColor,false); + } const SoCoordinateElement * coords; const SbVec3f * normals; @@ -195,15 +260,15 @@ void SoBrepEdgeSet::renderSelection(SoGLRenderAction *action) SoMaterialBundle mb(action); mb.sendFirst(); // make sure we have the correct material - int num = (int)this->sl.size(); + int num = (int)ctx->sl.size(); if (num > 0) { - if (this->sl[0] < 0) { + if (ctx->sl[0] < 0) { renderShape(static_cast(coords), cindices, numcindices); } else { - cindices = &(this->sl[0]); - numcindices = (int)this->sl.size(); - if (!validIndexes(coords, this->sl)) { + cindices = &(ctx->sl[0]); + numcindices = (int)ctx->sl.size(); + if (!validIndexes(coords, ctx->sl)) { SoDebugError::postWarning("SoBrepEdgeSet::renderSelection", "selectionIndex out of range"); } else { @@ -211,7 +276,7 @@ void SoBrepEdgeSet::renderSelection(SoGLRenderAction *action) } } } - state->pop(); + if(push) state->pop(); } bool SoBrepEdgeSet::validIndexes(const SoCoordinateElement* coords, const std::vector& pts) const @@ -224,128 +289,143 @@ bool SoBrepEdgeSet::validIndexes(const SoCoordinateElement* coords, const std::v return true; } -static void createIndexArray(const int32_t* segm, int numsegm, - const int32_t* cindices, int numcindices, - std::vector& out) -{ - std::vector v; - for (int j=0; j index) - break; - if (cindices[i] < 0) - section++; - } - - v.insert(v.end(), cindices+start, cindices+start+num); - } - - out.swap(v); -} - void SoBrepEdgeSet::doAction(SoAction* action) { if (action->getTypeId() == Gui::SoHighlightElementAction::getClassTypeId()) { Gui::SoHighlightElementAction* hlaction = static_cast(action); + selCounter.checkAction(hlaction); if (!hlaction->isHighlighted()) { - this->highlightIndex = -1; - this->hl.clear(); + SelContextPtr ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext,false); + if(ctx) { + ctx->highlightIndex = -1; + ctx->hl.clear(); + touch(); + } return; } const SoDetail* detail = hlaction->getElement(); - if (detail) { - if (!detail->isOfType(SoLineDetail::getClassTypeId())) { - this->highlightIndex = -1; - this->hl.clear(); - return; - } - - this->highlightColor = hlaction->getColor(); - int32_t index = static_cast(detail)->getLineIndex(); - const int32_t* cindices = this->coordIndex.getValues(0); - int numcindices = this->coordIndex.getNum(); - - createIndexArray(&index, 1, cindices, numcindices, this->hl); - this->highlightIndex.setValue(index); + if (!detail) { + SelContextPtr ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext); + ctx->highlightColor = hlaction->getColor(); + ctx->highlightIndex = INT_MAX; + ctx->hl.clear(); + ctx->hl.push_back(-1); + touch(); + return; } + + if (!detail->isOfType(SoLineDetail::getClassTypeId())) { + SelContextPtr ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext,false); + if(ctx) { + ctx->highlightIndex = -1; + ctx->hl.clear(); + touch(); + } + return; + } + + SelContextPtr ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext); + ctx->highlightColor = hlaction->getColor(); + int index = static_cast(detail)->getLineIndex(); + const int32_t* cindices = this->coordIndex.getValues(0); + int numcindices = this->coordIndex.getNum(); + + ctx->hl.clear(); + for(int section=0,i=0;i index) + break; + }else if(section == index) + ctx->hl.push_back(cindices[i]); + } + if(ctx->hl.size()) + ctx->highlightIndex = index; + else + ctx->highlightIndex = -1; + touch(); + return; } else if (action->getTypeId() == Gui::SoSelectionElementAction::getClassTypeId()) { Gui::SoSelectionElementAction* selaction = static_cast(action); - this->selectionColor = selaction->getColor(); - if (selaction->getType() == Gui::SoSelectionElementAction::All) { - //const int32_t* cindices = this->coordIndex.getValues(0); - //int numcindices = this->coordIndex.getNum(); - //unsigned int num = std::count_if(cindices, cindices+numcindices, - // std::bind2nd(std::equal_to(), -1)); - - //this->sl.clear(); - //this->selectionIndex.setNum(num); - //int32_t* v = this->selectionIndex.startEditing(); - //for (unsigned int i=0; iselectionIndex.finishEditing(); - - //int numsegm = this->selectionIndex.getNum(); - //if (numsegm > 0) { - // const int32_t* selsegm = this->selectionIndex.getValues(0); - // const int32_t* cindices = this->coordIndex.getValues(0); - // int numcindices = this->coordIndex.getNum(); - // createIndexArray(selsegm, numsegm, cindices, numcindices, this->sl); - //} - this->selectionIndex.setValue(-1); // all - this->sl.clear(); - this->sl.push_back(-1); + switch(selaction->getType()) { + case Gui::SoSelectionElementAction::None: { + if(selaction->isSecondary()) { + if(Gui::SoFCSelectionRoot::removeActionContext(action,this)) + touch(); + }else { + SelContextPtr ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext,false); + if(ctx) { + ctx->selectionIndex.clear(); + ctx->sl.clear(); + touch(); + } + } return; - } - else if (selaction->getType() == Gui::SoSelectionElementAction::None) { - this->selectionIndex.setNum(0); - this->sl.clear(); + } case Gui::SoSelectionElementAction::All: { + SelContextPtr ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext); + selCounter.checkAction(selaction,ctx); + ctx->selectionColor = selaction->getColor(); + ctx->selectionIndex.clear(); + ctx->selectionIndex.insert(-1); // all + ctx->sl.clear(); + ctx->sl.push_back(-1); + touch(); return; - } - - const SoDetail* detail = selaction->getElement(); - if (detail) { - if (!detail->isOfType(SoLineDetail::getClassTypeId())) { + } case Gui::SoSelectionElementAction::Append: + case Gui::SoSelectionElementAction::Remove: { + const SoDetail* detail = selaction->getElement(); + if (!detail || !detail->isOfType(SoLineDetail::getClassTypeId())) { + if(selaction->isSecondary()) { + // For secondary context, a detail of different type means + // the user may want to partial render only other type of + // geometry. So we call below to obtain a action context. + // If no secondary context exist, it will create an empty + // one, and an empty secondary context inhibites drawing + // here. + auto ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext); + selCounter.checkAction(selaction,ctx); + touch(); + } return; } - int index = static_cast(detail)->getLineIndex(); - switch (selaction->getType()) { - case Gui::SoSelectionElementAction::Append: - { - if (this->selectionIndex.find(index) < 0) { - int start = this->selectionIndex.getNum(); - this->selectionIndex.set1Value(start, index); - } - } - break; - case Gui::SoSelectionElementAction::Remove: - { - int start = this->selectionIndex.find(index); - if (start >= 0) - this->selectionIndex.deleteValues(start,1); - } - break; - default: - break; + SelContextPtr ctx; + if(selaction->getType() == Gui::SoSelectionElementAction::Append) { + ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext); + selCounter.checkAction(selaction,ctx); + ctx->selectionColor = selaction->getColor(); + if(ctx->isSelectAll()) + ctx->selectionIndex.clear(); + if(!ctx->selectionIndex.insert(index).second) + return; + }else{ + ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext,false); + if(!ctx || !ctx->removeIndex(index)) + return; } - - int numsegm = this->selectionIndex.getNum(); - if (numsegm > 0) { - const int32_t* selsegm = this->selectionIndex.getValues(0); + ctx->sl.clear(); + if(ctx->selectionIndex.size()) { const int32_t* cindices = this->coordIndex.getValues(0); int numcindices = this->coordIndex.getNum(); - createIndexArray(selsegm, numsegm, cindices, numcindices, this->sl); + auto it = ctx->selectionIndex.begin(); + for(int section=0,i=0;isl.push_back(cindices[i]); + if(cindices[i] < 0) { + if(++section > *it) { + if(++it == ctx->selectionIndex.end()) + break; + } + } + } } + touch(); + break; + } default : + break; } + return; } inherited::doAction(action); @@ -362,3 +442,4 @@ SoDetail * SoBrepEdgeSet::createLineSegmentDetail(SoRayPickAction * action, line_detail->setPartIndex(index); return detail; } + diff --git a/src/Mod/Part/Gui/SoBrepEdgeSet.h b/src/Mod/Part/Gui/SoBrepEdgeSet.h index b91732eaea..dfa9428111 100644 --- a/src/Mod/Part/Gui/SoBrepEdgeSet.h +++ b/src/Mod/Part/Gui/SoBrepEdgeSet.h @@ -32,6 +32,8 @@ #include #include #include +#include +#include class SoCoordinateElement; class SoGLCoordinateElement; @@ -48,9 +50,6 @@ public: static void initClass(); SoBrepEdgeSet(); - SoSFInt32 highlightIndex; - SoMFInt32 selectionIndex; - protected: virtual ~SoBrepEdgeSet() {}; virtual void GLRender(SoGLRenderAction *action); @@ -62,21 +61,20 @@ protected: const SoPrimitiveVertex *v2, SoPickedPoint *pp); private: + struct SelContext; + typedef std::shared_ptr SelContextPtr; + void renderShape(const SoGLCoordinateElement * const vertexlist, - const int32_t *vertexindices, - int num_vertexindices); - void renderHighlight(SoGLRenderAction *action); - void renderSelection(SoGLRenderAction *action); + const int32_t *vertexindices, int num_vertexindices); + void renderHighlight(SoGLRenderAction *action, SelContextPtr); + void renderSelection(SoGLRenderAction *action, SelContextPtr, bool push=true); bool validIndexes(const SoCoordinateElement*, const std::vector&) const; private: - std::vector hl, sl; - SbColor selectionColor; - SbColor highlightColor; - //#0000834: Minor preselection color bug - //To solve this we need a separate color packer for highlighting and selection - SoColorPacker colorpacker1; - SoColorPacker colorpacker2; + SelContextPtr selContext; + SelContextPtr selContext2; + Gui::SoFCSelectionCounter selCounter; + uint32_t packedColor; }; } // namespace PartGui diff --git a/src/Mod/Part/Gui/SoBrepFaceSet.cpp b/src/Mod/Part/Gui/SoBrepFaceSet.cpp index c74280b384..6d96cebe8f 100644 --- a/src/Mod/Part/Gui/SoBrepFaceSet.cpp +++ b/src/Mod/Part/Gui/SoBrepFaceSet.cpp @@ -55,6 +55,9 @@ # include # include # include +# include +# include +# include # ifdef FC_OS_WIN32 # include # include @@ -72,6 +75,7 @@ # include #endif +#include #include "SoBrepFaceSet.h" #include #include @@ -79,7 +83,6 @@ using namespace PartGui; - SO_NODE_SOURCE(SoBrepFaceSet); #define PRIVATE(p) ((p)->pimpl) @@ -173,9 +176,9 @@ SoBrepFaceSet::SoBrepFaceSet() { SO_NODE_CONSTRUCTOR(SoBrepFaceSet); SO_NODE_ADD_FIELD(partIndex, (-1)); - SO_NODE_ADD_FIELD(highlightIndex, (-1)); - SO_NODE_ADD_FIELD(selectionIndex, (-1)); - selectionIndex.setNum(0); + + selContext = std::make_shared(); + selContext2 = std::make_shared(); pimpl.reset(new VBO); } @@ -188,71 +191,124 @@ void SoBrepFaceSet::doAction(SoAction* action) { if (action->getTypeId() == Gui::SoHighlightElementAction::getClassTypeId()) { Gui::SoHighlightElementAction* hlaction = static_cast(action); + selCounter.checkAction(hlaction); if (!hlaction->isHighlighted()) { - this->highlightIndex = -1; + SelContextPtr ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext,false); + if(ctx) { + ctx->highlightIndex = -1; + touch(); + } return; } const SoDetail* detail = hlaction->getElement(); - if (detail) { - if (detail->isOfType(SoFaceDetail::getClassTypeId())) { + if (!detail) { + SelContextPtr ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext); + ctx->highlightIndex = INT_MAX; + ctx->highlightColor = hlaction->getColor(); + touch(); + }else { + if (!detail->isOfType(SoFaceDetail::getClassTypeId())) { + SelContextPtr ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext,false); + if(ctx) { + ctx->highlightIndex = -1; + touch(); + } + }else { int index = static_cast(detail)->getPartIndex(); - this->highlightIndex.setValue(index); - this->highlightColor = hlaction->getColor(); - } - else { - this->highlightIndex = -1; - return; + SelContextPtr ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext); + ctx->highlightIndex = index; + ctx->highlightColor = hlaction->getColor(); + touch(); } } + return; } else if (action->getTypeId() == Gui::SoSelectionElementAction::getClassTypeId()) { Gui::SoSelectionElementAction* selaction = static_cast(action); - this->selectionColor = selaction->getColor(); - if (selaction->getType() == Gui::SoSelectionElementAction::All) { - //int num = this->partIndex.getNum(); - //this->selectionIndex.setNum(num); - //int32_t* v = this->selectionIndex.startEditing(); - //for (int i=0; iselectionIndex.finishEditing(); - this->selectionIndex.setValue(-1); // all - PRIVATE(this)->updateVbo = true; + switch(selaction->getType()) { + case Gui::SoSelectionElementAction::All: { + SelContextPtr ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext); + selCounter.checkAction(selaction,ctx); + ctx->selectionColor = selaction->getColor(); + ctx->selectionIndex.clear(); + ctx->selectionIndex.insert(-1); + touch(); return; - } - else if (selaction->getType() == Gui::SoSelectionElementAction::None) { - this->selectionIndex.setNum(0); - PRIVATE(this)->updateVbo = true; + } case Gui::SoSelectionElementAction::None: + if(selaction->isSecondary()) { + if(Gui::SoFCSelectionRoot::removeActionContext(action,this)) + touch(); + }else { + SelContextPtr ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext,false); + if(ctx) { + ctx->selectionIndex.clear(); + ctx->colors.clear(); + touch(); + } + } return; - } - - const SoDetail* detail = selaction->getElement(); - if (detail) { - if (!detail->isOfType(SoFaceDetail::getClassTypeId())) { + case Gui::SoSelectionElementAction::Color: + if(selaction->isSecondary()) { + const auto &colors = selaction->getColors(); + auto ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext,false); + if(colors.empty()) { + if(ctx) { + ctx->colors.clear(); + if(ctx->isSelectAll()) + Gui::SoFCSelectionRoot::removeActionContext(action,this); + touch(); + } + return; + } + static std::string element("Face"); + if(colors.begin()->first.empty() || colors.lower_bound(element)!=colors.end()) { + if(!ctx) { + ctx = Gui::SoFCSelectionRoot::getActionContext(action,this); + selCounter.checkAction(selaction,ctx); + ctx->selectAll(); + } + if(ctx->setColors(selaction->getColors(),element)) + touch(); + } + } + return; + case Gui::SoSelectionElementAction::Remove: + case Gui::SoSelectionElementAction::Append: { + const SoDetail* detail = selaction->getElement(); + if (!detail || !detail->isOfType(SoFaceDetail::getClassTypeId())) { + if(selaction->isSecondary()) { + // For secondary context, a detail of different type means + // the user may want to partial render only other type of + // geometry. So we call below to obtain a action context. + // If no secondary context exist, it will create an empty + // one, and an empty secondary context inhibites drawing + // here. + auto ctx = Gui::SoFCSelectionRoot::getActionContext(action,this); + selCounter.checkAction(selaction,ctx); + touch(); + } return; } - int index = static_cast(detail)->getPartIndex(); - switch (selaction->getType()) { - case Gui::SoSelectionElementAction::Append: - { - if (this->selectionIndex.find(index) < 0) { - int start = this->selectionIndex.getNum(); - this->selectionIndex.set1Value(start, index); - } - } - break; - case Gui::SoSelectionElementAction::Remove: - { - int start = this->selectionIndex.find(index); - if (start >= 0) - this->selectionIndex.deleteValues(start,1); - } - break; - default: - break; + if (selaction->getType() == Gui::SoSelectionElementAction::Append) { + auto ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext); + selCounter.checkAction(selaction,ctx); + ctx->selectionColor = selaction->getColor(); + if(ctx->isSelectAll()) + ctx->selectionIndex.clear(); + if(ctx->selectionIndex.insert(index).second) + touch(); + }else{ + auto ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext,false); + if(ctx && ctx->removeIndex(index)) + touch(); } + break; + } default: + break; } + return; } else if (action->getTypeId() == Gui::SoVRMLAction::getClassTypeId()) { // update the materialIndex field to match with the number of triangles if needed @@ -298,6 +354,8 @@ void SoBrepFaceSet::doAction(SoAction* action) void SoBrepFaceSet::GLRender(SoGLRenderAction *action) { SoState * state = action->getState(); + // Disable caching for this node + SoGLCacheContextElement::shouldAutoCache(state, SoGLCacheContextElement::DONT_AUTO_CACHE); SoMaterialBundle mb(action); Binding mbind = this->findMaterialBinding(state); @@ -305,79 +363,92 @@ void SoBrepFaceSet::GLRender(SoGLRenderAction *action) SoTextureCoordinateBundle tb(action, true, false); SbBool doTextures = tb.needCoordinates(); - int32_t hl_idx = this->highlightIndex.getValue(); - int32_t num_selected = this->selectionIndex.getNum(); - - if (this->coordIndex.getNum() < 3) + if (ctx->coordIndex.getNum() < 3) return; - if (num_selected > 0) - renderSelection(action); - if (hl_idx >= 0) - renderHighlight(action); - // When setting transparency shouldGLRender() handles the rendering and returns false. - // Therefore generatePrimitives() needs to be re-implemented to handle the materials - // correctly. - if (!this->shouldGLRender(action)) + SelContextPtr ctx2; + SelContextPtr ctx = Gui::SoFCSelectionRoot::getRenderContext(this,selContext,ctx2); + if(ctx2 && ctx2->selectionIndex.empty()) return; + int32_t hl_idx = ctx?ctx->highlightIndex:-1; + int32_t num_selected = ctx?ctx->selectionIndex.size():0; + + renderHighlight(action,ctx); + if(ctx && ctx->selectionIndex.size()) { + if(ctx->isSelectAll()) { + if(ctx2 && ctx2->selectionIndex.size()) { + ctx2->selectionColor = ctx->selectionColor; + renderSelection(action,ctx2); + } else + renderSelection(action,ctx); + return; + } + renderSelection(action,ctx); + } + if(ctx2 && ctx2->selectionIndex.size()) { + renderSelection(action,ctx2,false); + }else{ + + // When setting transparency shouldGLRender() handles the rendering and returns false. + // Therefore generatePrimitives() needs to be re-implemented to handle the materials + // correctly. + if (!this->shouldGLRender(action)) + return; + #ifdef RENDER_GLARRAYS - if (!doTextures && index_array.size() && hl_idx < 0 && num_selected <= 0) { - if (mbind == 0) { - mb.sendFirst(); // only one material -> apply it! - renderSimpleArray(); - return; + if (!doTextures && index_array.size() && hl_idx < 0 && num_selected <= 0) { + if (mbind == 0) { + mb.sendFirst(); // only one material -> apply it! + renderSimpleArray(); + return; + } + else if (mbind == 1) { + renderColoredArray(&mb); + return; + } } - else if (mbind == 1) { - renderColoredArray(&mb); - return; - } - } #endif - Binding nbind = this->findNormalBinding(state); + Binding nbind = this->findNormalBinding(state); - const SoCoordinateElement * coords; - const SbVec3f * normals; - const int32_t * cindices; - int numindices; - const int32_t * nindices; - const int32_t * tindices; - const int32_t * mindices; - const int32_t * pindices; - int numparts; - SbBool normalCacheUsed; + const SoCoordinateElement * coords; + const SbVec3f * normals; + const int32_t * cindices; + int numindices; + const int32_t * nindices; + const int32_t * tindices; + const int32_t * mindices; + const int32_t * pindices; + int numparts; + SbBool normalCacheUsed; - SbBool sendNormals = !mb.isColorOnly() || tb.isFunction(); + SbBool sendNormals = !mb.isColorOnly() || tb.isFunction(); - this->getVertexData(state, coords, normals, cindices, - nindices, tindices, mindices, numindices, - sendNormals, normalCacheUsed); + this->getVertexData(state, coords, normals, cindices, + nindices, tindices, mindices, numindices, + sendNormals, normalCacheUsed); - mb.sendFirst(); // make sure we have the correct material + mb.sendFirst(); // make sure we have the correct material - // just in case someone forgot - if (!mindices) mindices = cindices; - if (!nindices) nindices = cindices; - pindices = this->partIndex.getValues(0); - numparts = this->partIndex.getNum(); + // just in case someone forgot + if (!mindices) mindices = cindices; + if (!nindices) nindices = cindices; + pindices = this->partIndex.getValues(0); + numparts = this->partIndex.getNum(); - renderShape(state, vboAvailable, static_cast(coords), cindices, numindices, - pindices, numparts, normals, nindices, &mb, mindices, &tb, tindices, nbind, mbind, doTextures?1:0); + renderShape(state, vboAvailable, static_cast(coords), cindices, numindices, + pindices, numparts, normals, nindices, &mb, mindices, &tb, tindices, nbind, mbind, doTextures?1:0); - // Disable caching for this node - SoGLCacheContextElement::shouldAutoCache(state, SoGLCacheContextElement::DONT_AUTO_CACHE); + if(normalCacheUsed) + this->readUnlockNormalCache(); + } // Workaround for #0000433 //#if !defined(FC_OS_WIN32) - if (hl_idx >= 0) - renderHighlight(action); - if (num_selected > 0) - renderSelection(action); + renderHighlight(action,ctx); + renderSelection(action,ctx); //#endif - - if(normalCacheUsed) - this->readUnlockNormalCache(); } //**************************************************************************** @@ -441,6 +512,7 @@ void SoBrepFaceSet::renderColoredArray(SoMaterialBundle *const materials) glDisableClientState(GL_NORMAL_ARRAY); } #else + void SoBrepFaceSet::GLRender(SoGLRenderAction *action) { //SoBase::staticDataLock(); @@ -454,85 +526,367 @@ void SoBrepFaceSet::GLRender(SoGLRenderAction *action) if (this->coordIndex.getNum() < 3) return; - if (this->selectionIndex.getNum() > 0) - renderSelection(action); - if (this->highlightIndex.getValue() >= 0) - renderHighlight(action); + + SelContextPtr ctx2; + std::vector ctxs; + SelContextPtr ctx = Gui::SoFCSelectionRoot::getRenderContext(this,selContext,ctx2); + if(ctx2 && ctx2->selectionIndex.empty()) + return; + if(selContext2->checkGlobal(ctx)) + ctx = selContext2; + if(ctx && (!ctx->selectionIndex.size() && ctx->highlightIndex<0)) + ctx.reset(); + + auto state = action->getState(); + selCounter.checkRenderCache(state); + + // override material binding to PER_PART_INDEX to achieve + // preselection/selection with transparency + bool pushed = overrideMaterialBinding(action,ctx,ctx2); + if(!pushed){ + // for non transparent cases, we still use the old selection rendering + // code, because it can override emission color, which gives a more + // distinguishable selection highlight. The above material binding + // override method can't, because Coin does not support per part + // emission color + + // There are a few factors affects the rendering order. + // + // 1) For normal case, the highlight (pre-selection) is the top layer. And since + // the depth buffer clipping is on here, we shall draw highlight first, then + // selection, then the rest part. + // + // 2) If action->isRenderingDelayedPaths() is true, it means we are rendering + // with depth buffer clipping turned off (always on top rendering), so we shall + // draw the top layer last, i.e. renderHighlight() last + // + // 3) If highlightIndex==INT_MAX, it means we are rendering full object highlight + // In order to not obscure selection layer, we shall draw highlight after selection + // if and only if it is not a full object selection. + // + // Transparency complicates stuff even more, but not here. It will be handled inside + // overrideMaterialBinding() + // + if(ctx && ctx->highlightIndex==INT_MAX) { + if(ctx->selectionIndex.empty() || ctx->isSelectAll()) { + if(ctx2) { + ctx2->selectionColor = ctx->highlightColor; + renderSelection(action,ctx2); + } else + renderHighlight(action,ctx); + }else{ + if(!action->isRenderingDelayedPaths()) + renderSelection(action,ctx); + if(ctx2) { + ctx2->selectionColor = ctx->highlightColor; + renderSelection(action,ctx2); + } else + renderHighlight(action,ctx); + if(action->isRenderingDelayedPaths()) + renderSelection(action,ctx); + } + return; + } + + if(!action->isRenderingDelayedPaths()) + renderHighlight(action,ctx); + if(ctx && ctx->selectionIndex.size()) { + if(ctx->isSelectAll()) { + if(ctx2) { + ctx2->selectionColor = ctx->selectionColor; + renderSelection(action,ctx2); + } else + renderSelection(action,ctx); + if(action->isRenderingDelayedPaths()) + renderHighlight(action,ctx); + return; + } + if(!action->isRenderingDelayedPaths()) + renderSelection(action,ctx); + } + if(ctx2) { + renderSelection(action,ctx2,false); + if(action->isRenderingDelayedPaths()) { + renderSelection(action,ctx); + renderHighlight(action,ctx); + } + return; + } + } + + SoMaterialBundle mb(action); + // It is important to send material before shouldGLRender(), otherwise + // material override with transparncy won't work. + mb.sendFirst(); + // When setting transparency shouldGLRender() handles the rendering and returns false. // Therefore generatePrimitives() needs to be re-implemented to handle the materials // correctly. - if (!this->shouldGLRender(action)) - return; + if(this->shouldGLRender(action)) { + Binding mbind = this->findMaterialBinding(state); + Binding nbind = this->findNormalBinding(state); - SbBool hasVBO = PRIVATE(this)->vboAvailable; - SoState * state = action->getState(); - if (hasVBO) { - // get the VBO status of the viewer - Gui::SoGLVBOActivatedElement::get(state, hasVBO); - } + const SoCoordinateElement * coords; + const SbVec3f * normals; + const int32_t * cindices; + int numindices; + const int32_t * nindices; + const int32_t * tindices; + const int32_t * mindices; + const int32_t * pindices; + int numparts; + SbBool doTextures; + SbBool normalCacheUsed; - Binding mbind = this->findMaterialBinding(state); - Binding nbind = this->findNormalBinding(state); + SoTextureCoordinateBundle tb(action, true, false); + doTextures = tb.needCoordinates(); + SbBool sendNormals = !mb.isColorOnly() || tb.isFunction(); - const SoCoordinateElement * coords; - const SbVec3f * normals; - const int32_t * cindices; - int numindices; - const int32_t * nindices; - const int32_t * tindices; - const int32_t * mindices; - const int32_t * pindices; - int numparts; - SbBool doTextures; - SbBool normalCacheUsed; + this->getVertexData(state, coords, normals, cindices, + nindices, tindices, mindices, numindices, + sendNormals, normalCacheUsed); - SoMaterialBundle mb(action); + // just in case someone forgot + if (!mindices) mindices = cindices; + if (!nindices) nindices = cindices; + pindices = this->partIndex.getValues(0); + numparts = this->partIndex.getNum(); - SoTextureCoordinateBundle tb(action, true, false); - doTextures = tb.needCoordinates(); - SbBool sendNormals = !mb.isColorOnly() || tb.isFunction(); + SbBool hasVBO = !ctx2 && PRIVATE(this)->vboAvailable; + if (hasVBO) { + // get the VBO status of the viewer + Gui::SoGLVBOActivatedElement::get(state, hasVBO); + // + //if (SoGLVBOElement::shouldCreateVBO(state, numindices)) { + // this->startVertexArray(action, coords, normals, false, false); + //} + } + renderShape(action, hasVBO, static_cast(coords), cindices, numindices, + pindices, numparts, normals, nindices, &mb, mindices, &tb, tindices, nbind, mbind, doTextures?1:0); - this->getVertexData(state, coords, normals, cindices, - nindices, tindices, mindices, numindices, - sendNormals, normalCacheUsed); - - mb.sendFirst(); // make sure we have the correct material - - // just in case someone forgot - if (!mindices) mindices = cindices; - if (!nindices) nindices = cindices; - pindices = this->partIndex.getValues(0); - numparts = this->partIndex.getNum(); - - if (hasVBO) { - // get the VBO status of the viewer - Gui::SoGLVBOActivatedElement::get(state, hasVBO); - // - //if (SoGLVBOElement::shouldCreateVBO(state, numindices)) { - // this->startVertexArray(action, coords, normals, false, false); - //} - } - renderShape(action, hasVBO, static_cast(coords), cindices, numindices, - pindices, numparts, normals, nindices, &mb, mindices, &tb, tindices, nbind, mbind, doTextures?1:0); - - if (!hasVBO) { - // Disable caching for this node - SoGLCacheContextElement::shouldAutoCache(state, SoGLCacheContextElement::DONT_AUTO_CACHE); + // if (!hasVBO) { + // // Disable caching for this node + // SoGLCacheContextElement::shouldAutoCache(state, SoGLCacheContextElement::DONT_AUTO_CACHE); + // }else + // SoGLCacheContextElement::setAutoCacheBits(state, SoGLCacheContextElement::DO_AUTO_CACHE); if (normalCacheUsed) this->readUnlockNormalCache(); } - // Workaround for #0000433 -//#if !defined(FC_OS_WIN32) - if (this->highlightIndex.getValue() >= 0) - renderHighlight(action); - if (this->selectionIndex.getNum() > 0) - renderSelection(action); -//#endif + if(pushed) { + SbBool notify = enableNotify(FALSE); + materialIndex.setNum(0); + if(notify) enableNotify(notify); + state->pop(); + }else if(action->isRenderingDelayedPaths()) { + renderSelection(action,ctx); + renderHighlight(action,ctx); + } } #endif +bool SoBrepFaceSet::overrideMaterialBinding(SoGLRenderAction *action, SelContextPtr ctx, SelContextPtr ctx2) { + if(!ctx && !ctx2) return false; + + auto state = action->getState(); + auto mb = SoMaterialBindingElement::get(state); + + auto element = SoLazyElement::getInstance(state); + const SbColor *diffuse = element->getDiffusePointer(); + if(!diffuse) return false; + int diffuse_size = element->getNumDiffuse(); + + const float *trans = element->getTransparencyPointer(); + int trans_size = element->getNumTransparencies(); + if(!trans || !trans_size) return false; + float trans0=0.0; + bool hasTransparency = false; + for(int i=0;i0.5?0.5:trans[i]; + break; + } + } + + // Override material binding to PER_PART_INDEXED so that we can reuse coin + // rendering for both selection, preselection and partial rendering. The + // main purpose is such that selection and preselection can have correct + // transparency, too. + // + // Criteria of using material binding override: + // 1) original material binding is either overall or per_part. We can + // support others, but ommitted here to simplify coding logic, and + // because it seems FC only uses these two. + // 2) either of the following : + // a) has highlight or selection and Selection().needPickPoint, so that + // any preselected/selected part automatically become transparent + // b) has transparency + // c) has color override in secondary context + + if((mb==SoMaterialBindingElement::OVERALL || + (mb==SoMaterialBindingElement::PER_PART && diffuse_size>=partIndex.getNum())) + && + ((ctx && Gui::Selection().needPickedList()) || + trans0!=0.0 || + (ctx2 && ctx2->colors.size()))) + { + state->push(); + + packedColors.clear(); + + if(ctx && Gui::Selection().needPickedList()) { + hasTransparency = true; + if(trans0 < 0.5) + trans0=0.5; + trans_size = 1; + if(ctx2) + ctx2->trans0 = trans0; + }else if(ctx2) + ctx2->trans0 = 0.0; + + uint32_t diffuseColor = diffuse[0].getPackedValue(trans0); + int singleColor = 0; + if(ctx && ctx->isHighlightAll()) { + singleColor = 1; + diffuseColor = ctx->highlightColor.getPackedValue(trans0); + }else if(ctx && ctx->isSelectAll()) { + diffuseColor = ctx->selectionColor.getPackedValue(trans0); + singleColor = ctx->isHighlighted()?-1:1; + } else if(ctx2 && ctx2->isSingleColor(diffuseColor,hasTransparency)) { + singleColor = ctx?-1:1; + } + + bool partialRender = ctx2 && !ctx2->isSelectAll(); + + if(singleColor>0 && !partialRender) { + //optimization for single color non-partial rendering + SoMaterialBindingElement::set(state,SoMaterialBindingElement::OVERALL); + SoOverrideElement::setMaterialBindingOverride(state, this, true); + packedColors.push_back(diffuseColor); + SoLazyElement::setPacked(state, this,1, &packedColors[0], hasTransparency); + SoTextureEnabledElement::set(state,this,false); + + if(hasTransparency && action->isRenderingDelayedPaths()) { + // rendering delayed paths means we are doing annotation (e.g. + // always on top rendering). To render transparency correctly in + // this case, we shall use openGL transparency blend. Override + // using SoLazyElement::setTransparencyType() doesn't seem to work + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthMask(false); + } + return true; + } + + matIndex.clear(); + matIndex.reserve(partIndex.getNum()); + + if(ctx && (ctx->isSelectAll() || ctx->isHighlightAll())) { + matIndex.resize(partIndex.getNum(),0); + if(!partialRender) + packedColors.push_back(diffuseColor); + else { + // default to full transparent + packedColors.push_back(SbColor(1.0,1.0,1.0).getPackedValue(1.0)); + packedColors.push_back(diffuseColor); + for(auto idx : ctx2->selectionIndex) { + if(idx>=0 && idxhighlightIndex>=0 && ctx->highlightIndexhighlightColor.getPackedValue(trans0)); + matIndex[ctx->highlightIndex] = packedColors.size()-1; + } + }else{ + if(partialRender) { + packedColors.push_back(SbColor(1.0,1.0,1.0).getPackedValue(1.0)); + matIndex.resize(partIndex.getNum(),0); + + if(mb == SoMaterialBindingElement::OVERALL || singleColor) { + packedColors.push_back(diffuseColor); + auto cidx = packedColors.size()-1; + for(auto idx : ctx2->selectionIndex) { + if(idx>=0 && idxapplyColor(idx,packedColors,hasTransparency)) + matIndex[idx] = packedColors.size()-1; + else + matIndex[idx] = cidx; + } + } + }else{ + assert(diffuse_size >= partIndex.getNum()); + for(auto idx : ctx2->selectionIndex) { + if(idx>=0 && idxapplyColor(idx,packedColors,hasTransparency)) { + auto t = idxcolors) { + int idx = v.first; + if(idx>=0 && idxpackColor(v.second,hasTransparency)); + matIndex[idx] = packedColors.size()-1; + } + } + } + }else{ + assert(diffuse_size >= partIndex.getNum()); + packedColors.reserve(diffuse_size+3); + for(int i=0;iapplyColor(i,packedColors,hasTransparency)) + packedColors.push_back(diffuse[i].getPackedValue(t)); + } + } + + if(ctx && ctx->selectionIndex.size()) { + packedColors.push_back(ctx->selectionColor.getPackedValue(trans0)); + for(auto idx : ctx->selectionIndex) { + if(idx>=0 && idxhighlightIndex>=0 && ctx->highlightIndexhighlightColor.getPackedValue(trans0)); + matIndex[ctx->highlightIndex] = packedColors.size()-1; + } + } + + SbBool notify = enableNotify(FALSE); + materialIndex.setValuesPointer(matIndex.size(),&matIndex[0]); + if(notify) enableNotify(notify); + + SoMaterialBindingElement::set(state, this, SoMaterialBindingElement::PER_PART_INDEXED); + SoLazyElement::setPacked(state, this, packedColors.size(), &packedColors[0], hasTransparency); + SoTextureEnabledElement::set(state,this,false); + + if(hasTransparency && action->isRenderingDelayedPaths()) { + // rendering delayed paths means we are doing annotation (e.g. + // always on top rendering). To render transparency correctly in + // this case, we shall use openGL transparency blend. Override + // using SoLazyElement::setTransparencyType() doesn't seem to work + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthMask(false); + } + return true; + } + return false; +} + void SoBrepFaceSet::GLRenderBelowPath(SoGLRenderAction * action) { inherited::GLRenderBelowPath(action); @@ -807,18 +1161,21 @@ void SoBrepFaceSet::generatePrimitives(SoAction * action) #undef DO_VERTEX -void SoBrepFaceSet::renderHighlight(SoGLRenderAction *action) +void SoBrepFaceSet::renderHighlight(SoGLRenderAction *action, SelContextPtr ctx) { + if(!ctx || ctx->highlightIndex < 0) + return; + SoState * state = action->getState(); state->push(); - SoLazyElement::setEmissive(state, &this->highlightColor); - SoOverrideElement::setEmissiveColorOverride(state, this, true); + SoLazyElement::setEmissive(state, &ctx->highlightColor); // if shading is disabled then set also the diffuse color if (SoLazyElement::getLightModel(state) == SoLazyElement::BASE_COLOR) { - SoLazyElement::setDiffuse(state, this,1, &this->highlightColor,&this->colorpacker); - SoOverrideElement::setDiffuseColorOverride(state, this, true); + packedColor = ctx->highlightColor.getPackedValue(0.0); + SoLazyElement::setPacked(state, this,1, &packedColor,false); } + SoTextureEnabledElement::set(state,this,false); Binding mbind = this->findMaterialBinding(state); Binding nbind = this->findNormalBinding(state); @@ -845,8 +1202,8 @@ void SoBrepFaceSet::renderHighlight(SoGLRenderAction *action) mb.sendFirst(); // make sure we have the correct material - int32_t id = this->highlightIndex.getValue(); - if (id >= this->partIndex.getNum()) { + int id = ctx->highlightIndex; + if (id!=INT_MAX && id >= this->partIndex.getNum()) { SoDebugError::postWarning("SoBrepFaceSet::renderHighlight", "highlightIndex out of range"); } else { @@ -856,11 +1213,17 @@ void SoBrepFaceSet::renderHighlight(SoGLRenderAction *action) pindices = this->partIndex.getValues(0); // coords - int length = (int)pindices[id]*4; int start=0; - for (int i=0;ireadUnlockNormalCache(); } -void SoBrepFaceSet::renderSelection(SoGLRenderAction *action) +void SoBrepFaceSet::renderSelection(SoGLRenderAction *action, SelContextPtr ctx, bool push) { - int numSelected = this->selectionIndex.getNum(); - const int32_t* selected = this->selectionIndex.getValues(0); - if (numSelected == 0) return; + if(!ctx || ctx->selectionIndex.empty()) + return; SoState * state = action->getState(); - state->push(); - SoLazyElement::setEmissive(state, &this->selectionColor); - SoOverrideElement::setEmissiveColorOverride(state, this, true); - // if shading is disabled then set also the diffuse color - if (SoLazyElement::getLightModel(state) == SoLazyElement::BASE_COLOR) { - SoLazyElement::setDiffuse(state, this,1, &this->selectionColor,&this->colorpacker); - SoOverrideElement::setDiffuseColorOverride(state, this, true); + if(push) { + state->push(); + + SoLazyElement::setEmissive(state, &ctx->selectionColor); + // if shading is disabled then set also the diffuse color + if (SoLazyElement::getLightModel(state) == SoLazyElement::BASE_COLOR) { + packedColor = ctx->selectionColor.getPackedValue(0.0); + SoLazyElement::setPacked(state, this,1, &packedColor,false); + } + SoTextureEnabledElement::set(state,this,false); } Binding mbind = this->findMaterialBinding(state); @@ -930,16 +1295,19 @@ void SoBrepFaceSet::renderSelection(SoGLRenderAction *action) if (!nindices) nindices = cindices; pindices = this->partIndex.getValues(0); - // materials - mbind = OVERALL; - doTextures = false; + if(push) { + // materials + mbind = OVERALL; + doTextures = false; + } - for (int i=0; iselectionIndex) { if (id >= this->partIndex.getNum()) { SoDebugError::postWarning("SoBrepFaceSet::renderSelection", "selectionIndex out of range"); break; } + if (id>=0 && id==ctx->highlightIndex) + continue; // coords int length=0; @@ -948,8 +1316,8 @@ void SoBrepFaceSet::renderSelection(SoGLRenderAction *action) // if < 0 then select everything if (id < 0) { length = numindices; - } - else { + id = 0; + } else { length = (int)pindices[id]*4; for (int j=0;j(coords), &(cindices[start]), length, &(pindices[id]), numparts, normals_s, nindices_s, &mb, mindices, &tb, tindices, nbind, mbind, doTextures?1:0); } - state->pop(); - + if(push) { + state->pop(); + // SoCacheElement::invalidate(state); + } + if (normalCacheUsed) this->readUnlockNormalCache(); } @@ -1033,6 +1404,7 @@ void SoBrepFaceSet::VBO::render(SoGLRenderAction * action, buf.vertex_array_size = 0; buf.index_array_size = 0; this->vbomap[contextId] = buf; + this->vboLoaded = false; } else { buf = it->second; diff --git a/src/Mod/Part/Gui/SoBrepFaceSet.h b/src/Mod/Part/Gui/SoBrepFaceSet.h index 590768a893..344655c46c 100644 --- a/src/Mod/Part/Gui/SoBrepFaceSet.h +++ b/src/Mod/Part/Gui/SoBrepFaceSet.h @@ -33,6 +33,7 @@ #include #include #include +#include class SoGLCoordinateElement; class SoTextureCoordinateBundle; @@ -84,8 +85,6 @@ public: SoBrepFaceSet(); SoMFInt32 partIndex; - SoSFInt32 highlightIndex; - SoMFInt32 selectionIndex; protected: virtual ~SoBrepFaceSet(); @@ -129,8 +128,14 @@ private: const int nbind, const int mbind, const int texture); - void renderHighlight(SoGLRenderAction *action); - void renderSelection(SoGLRenderAction *action); + + typedef Gui::SoFCSelectionContextEx SelContext; + typedef Gui::SoFCSelectionContextExPtr SelContextPtr; + + void renderHighlight(SoGLRenderAction *action, SelContextPtr); + void renderSelection(SoGLRenderAction *action, SelContextPtr, bool push=true); + + bool overrideMaterialBinding(SoGLRenderAction *action, SelContextPtr ctx, SelContextPtr ctx2); #ifdef RENDER_GLARRAYS void renderSimpleArray(); @@ -142,9 +147,12 @@ private: std::vector index_array; std::vector vertex_array; #endif - SbColor selectionColor; - SbColor highlightColor; - SoColorPacker colorpacker; + SelContextPtr selContext; + SelContextPtr selContext2; + std::vector matIndex; + std::vector packedColors; + uint32_t packedColor; + Gui::SoFCSelectionCounter selCounter; // Define some VBO pointer for the current mesh class VBO; diff --git a/src/Mod/Part/Gui/SoBrepPointSet.cpp b/src/Mod/Part/Gui/SoBrepPointSet.cpp index d404b2a059..f322cb45c3 100644 --- a/src/Mod/Part/Gui/SoBrepPointSet.cpp +++ b/src/Mod/Part/Gui/SoBrepPointSet.cpp @@ -64,7 +64,6 @@ using namespace PartGui; - SO_NODE_SOURCE(SoBrepPointSet); void SoBrepPointSet::initClass() @@ -73,33 +72,80 @@ void SoBrepPointSet::initClass() } SoBrepPointSet::SoBrepPointSet() + :selContext(std::make_shared()) + ,selContext2(std::make_shared()) { SO_NODE_CONSTRUCTOR(SoBrepPointSet); - SO_NODE_ADD_FIELD(highlightIndex, (-1)); - SO_NODE_ADD_FIELD(selectionIndex, (-1)); - selectionIndex.setNum(0); } void SoBrepPointSet::GLRender(SoGLRenderAction *action) { - const SoCoordinateElement* coords = SoCoordinateElement::getInstance(action->getState()); + auto state = action->getState(); + selCounter.checkRenderCache(state); + + const SoCoordinateElement* coords = SoCoordinateElement::getInstance(state); int num = coords->getNum() - this->startIndex.getValue(); if (num < 0) { // Fixes: #0000545: Undo revolve causes crash 'illegal storage' return; } - if (this->selectionIndex.getNum() > 0) - renderSelection(action); - if (this->highlightIndex.getValue() >= 0) - renderHighlight(action); - inherited::GLRender(action); + SelContextPtr ctx2; + SelContextPtr ctx = Gui::SoFCSelectionRoot::getRenderContext(this,selContext,ctx2); + if(ctx2 && ctx2->selectionIndex.empty()) + return; + if(selContext2->checkGlobal(ctx)) + ctx = selContext2; + + if(ctx && ctx->highlightIndex==INT_MAX) { + if(ctx->selectionIndex.empty() || ctx->isSelectAll()) { + if(ctx2) { + ctx2->selectionColor = ctx->highlightColor; + renderSelection(action,ctx2); + } else + renderHighlight(action,ctx); + }else{ + if(!action->isRenderingDelayedPaths()) + renderSelection(action,ctx); + if(ctx2) { + ctx2->selectionColor = ctx->highlightColor; + renderSelection(action,ctx2); + } else + renderHighlight(action,ctx); + if(action->isRenderingDelayedPaths()) + renderSelection(action,ctx); + } + return; + } + + if(!action->isRenderingDelayedPaths()) + renderHighlight(action,ctx); + if(ctx && ctx->selectionIndex.size()) { + if(ctx->isSelectAll()) { + if(ctx2 && ctx2->selectionIndex.size()) { + ctx2->selectionColor = ctx->selectionColor; + renderSelection(action,ctx2); + }else + renderSelection(action,ctx); + if(action->isRenderingDelayedPaths()) + renderHighlight(action,ctx); + return; + } + if(!action->isRenderingDelayedPaths()) + renderSelection(action,ctx); + } + if(ctx2 && ctx2->selectionIndex.size()) + renderSelection(action,ctx2,false); + else + inherited::GLRender(action); // Workaround for #0000433 //#if !defined(FC_OS_WIN32) - if (this->highlightIndex.getValue() >= 0) - renderHighlight(action); - if (this->selectionIndex.getNum() > 0) - renderSelection(action); + if(!action->isRenderingDelayedPaths()) + renderHighlight(action,ctx); + if(ctx && ctx->selectionIndex.size()) + renderSelection(action,ctx); + if(action->isRenderingDelayedPaths()) + renderHighlight(action,ctx); //#endif } @@ -108,35 +154,19 @@ void SoBrepPointSet::GLRenderBelowPath(SoGLRenderAction * action) inherited::GLRenderBelowPath(action); } -void SoBrepPointSet::renderShape(const SoGLCoordinateElement * const coords, - const int32_t *cindices, - int numindices) +void SoBrepPointSet::renderHighlight(SoGLRenderAction *action, SelContextPtr ctx) { - const SbVec3f * coords3d = coords->getArrayPtr3(); - if(coords3d == nullptr) + if(!ctx || ctx->highlightIndex<0) return; - int previ; - const int32_t *end = cindices + numindices; - glBegin(GL_POINTS); - while (cindices < end) { - previ = *cindices++; - glVertex3fv((const GLfloat*) (coords3d + previ)); - } - glEnd(); -} - -void SoBrepPointSet::renderHighlight(SoGLRenderAction *action) -{ SoState * state = action->getState(); state->push(); float ps = SoPointSizeElement::get(state); if (ps < 4.0f) SoPointSizeElement::set(state, this, 4.0f); - SoLazyElement::setEmissive(state, &this->highlightColor); - SoOverrideElement::setEmissiveColorOverride(state, this, true); - SoLazyElement::setDiffuse(state, this,1, &this->highlightColor,&this->colorpacker); - SoOverrideElement::setDiffuseColorOverride(state, this, true); + SoLazyElement::setEmissive(state, &ctx->highlightColor); + packedColor = ctx->highlightColor.getPackedValue(0.0); + SoLazyElement::setPacked(state, this,1, &packedColor,false); const SoCoordinateElement * coords; const SbVec3f * normals; @@ -146,128 +176,165 @@ void SoBrepPointSet::renderHighlight(SoGLRenderAction *action) SoMaterialBundle mb(action); mb.sendFirst(); // make sure we have the correct material - int32_t id = this->highlightIndex.getValue(); - if (id < this->startIndex.getValue() || id >= coords->getNum()) { - SoDebugError::postWarning("SoBrepPointSet::renderHighlight", "highlightIndex out of range"); - } - else { - renderShape(static_cast(coords), &id, 1); - } - state->pop(); -} - -void SoBrepPointSet::renderSelection(SoGLRenderAction *action) -{ - SoState * state = action->getState(); - state->push(); - float ps = SoPointSizeElement::get(state); - if (ps < 4.0f) SoPointSizeElement::set(state, this, 4.0f); - - SoLazyElement::setEmissive(state, &this->selectionColor); - SoOverrideElement::setEmissiveColorOverride(state, this, true); - SoLazyElement::setDiffuse(state, this,1, &this->selectionColor,&this->colorpacker); - SoOverrideElement::setDiffuseColorOverride(state, this, true); - - const SoCoordinateElement * coords; - const SbVec3f * normals; - const int32_t * cindices; - int numcindices; - - this->getVertexData(state, coords, normals, false); - - SoMaterialBundle mb(action); - mb.sendFirst(); // make sure we have the correct material - - cindices = this->selectionIndex.getValues(0); - numcindices = this->selectionIndex.getNum(); - - if (!validIndexes(coords, this->startIndex.getValue(), cindices, numcindices)) { - SoDebugError::postWarning("SoBrepPointSet::renderSelection", "selectionIndex out of range"); - } - else { - renderShape(static_cast(coords), cindices, numcindices); - } - state->pop(); -} - -bool SoBrepPointSet::validIndexes(const SoCoordinateElement* coords, int32_t startIndex, const int32_t * cindices, int numcindices) const -{ - for (int i=0; i= coords->getNum()) { - return false; + int id = ctx->highlightIndex; + const SbVec3f * coords3d = coords->getArrayPtr3(); + if(coords3d) { + if(id == INT_MAX) { + glBegin(GL_POINTS); + for(int idx=startIndex.getValue();idxgetNum();++idx) + glVertex3fv((const GLfloat*) (coords3d + idx)); + glEnd(); + }else if (id < this->startIndex.getValue() || id >= coords->getNum()) { + SoDebugError::postWarning("SoBrepPointSet::renderHighlight", "highlightIndex out of range"); + } + else { + glBegin(GL_POINTS); + glVertex3fv((const GLfloat*) (coords3d + id)); + glEnd(); } } - return true; + state->pop(); +} + +void SoBrepPointSet::renderSelection(SoGLRenderAction *action, SelContextPtr ctx, bool push) +{ + SoState * state = action->getState(); + if(push) { + state->push(); + float ps = SoPointSizeElement::get(state); + if (ps < 4.0f) SoPointSizeElement::set(state, this, 4.0f); + + SoLazyElement::setEmissive(state, &ctx->selectionColor); + packedColor = ctx->selectionColor.getPackedValue(0.0); + SoLazyElement::setPacked(state, this,1, &packedColor,false); + } + + const SoCoordinateElement * coords; + const SbVec3f * normals; + + this->getVertexData(state, coords, normals, false); + + SoMaterialBundle mb(action); + mb.sendFirst(); // make sure we have the correct material + + bool warn = false; + int startIndex = this->startIndex.getValue(); + const SbVec3f * coords3d = coords->getArrayPtr3(); + if(coords3d) { + glBegin(GL_POINTS); + if(ctx->isSelectAll()) { + for(int idx=startIndex;idxgetNum();++idx) + glVertex3fv((const GLfloat*) (coords3d + idx)); + }else{ + for(auto idx : ctx->selectionIndex) { + if(idx >= startIndex && idx < coords->getNum()) + glVertex3fv((const GLfloat*) (coords3d + idx)); + else + warn = true; + } + } + glEnd(); + } + if(warn) + SoDebugError::postWarning("SoBrepPointSet::renderSelection", "selectionIndex out of range"); + if(push) state->pop(); } void SoBrepPointSet::doAction(SoAction* action) { if (action->getTypeId() == Gui::SoHighlightElementAction::getClassTypeId()) { Gui::SoHighlightElementAction* hlaction = static_cast(action); + selCounter.checkAction(hlaction); if (!hlaction->isHighlighted()) { - this->highlightIndex = -1; + SelContextPtr ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext,false); + if(ctx) { + ctx->highlightIndex = -1; + touch(); + } return; } + SelContextPtr ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext); const SoDetail* detail = hlaction->getElement(); - if (detail) { - if (!detail->isOfType(SoPointDetail::getClassTypeId())) { - this->highlightIndex = -1; - return; - } - - int index = static_cast(detail)->getCoordinateIndex(); - this->highlightIndex.setValue(index); - this->highlightColor = hlaction->getColor(); + if (!detail) { + ctx->highlightIndex = INT_MAX; + ctx->highlightColor = hlaction->getColor(); + touch(); + return; + }else if (!detail->isOfType(SoPointDetail::getClassTypeId())) { + ctx->highlightIndex = -1; + touch(); + return; } + + int index = static_cast(detail)->getCoordinateIndex(); + if(index!=ctx->highlightIndex) { + ctx->highlightIndex = index; + ctx->highlightColor = hlaction->getColor(); + touch(); + } + return; } else if (action->getTypeId() == Gui::SoSelectionElementAction::getClassTypeId()) { Gui::SoSelectionElementAction* selaction = static_cast(action); - this->selectionColor = selaction->getColor(); - if (selaction->getType() == Gui::SoSelectionElementAction::All) { - const SoCoordinateElement* coords = SoCoordinateElement::getInstance(action->getState()); - int num = coords->getNum() - this->startIndex.getValue(); - this->selectionIndex.setNum(num); - int32_t* v = this->selectionIndex.startEditing(); - int32_t s = this->startIndex.getValue(); - for (int i=0; iselectionIndex.finishEditing(); + switch(selaction->getType()) { + case Gui::SoSelectionElementAction::All: { + SelContextPtr ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext); + selCounter.checkAction(selaction,ctx); + ctx->selectionColor = selaction->getColor(); + ctx->selectionIndex.clear(); + ctx->selectionIndex.insert(-1); + touch(); return; - } - else if (selaction->getType() == Gui::SoSelectionElementAction::None) { - this->selectionIndex.setNum(0); + } case Gui::SoSelectionElementAction::None: + if(selaction->isSecondary()) { + if(Gui::SoFCSelectionRoot::removeActionContext(action,this)) + touch(); + } else { + SelContextPtr ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext,false); + if(ctx) { + ctx->selectionIndex.clear(); + touch(); + } + } return; - } - - const SoDetail* detail = selaction->getElement(); - if (detail) { - if (!detail->isOfType(SoPointDetail::getClassTypeId())) { + case Gui::SoSelectionElementAction::Remove: + case Gui::SoSelectionElementAction::Append: { + const SoDetail* detail = selaction->getElement(); + if (!detail || !detail->isOfType(SoPointDetail::getClassTypeId())) { + if(selaction->isSecondary()) { + // For secondary context, a detail of different type means + // the user may want to partial render only other type of + // geometry. So we call below to obtain a action context. + // If no secondary context exist, it will create an empty + // one, and an empty secondary context inhibites drawing + // here. + auto ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext); + selCounter.checkAction(selaction,ctx); + touch(); + } return; } - int index = static_cast(detail)->getCoordinateIndex(); - switch (selaction->getType()) { - case Gui::SoSelectionElementAction::Append: - { - if (this->selectionIndex.find(index) < 0) { - int start = this->selectionIndex.getNum(); - this->selectionIndex.set1Value(start, index); - } - } - break; - case Gui::SoSelectionElementAction::Remove: - { - int start = this->selectionIndex.find(index); - if (start >= 0) - this->selectionIndex.deleteValues(start,1); - } - break; - default: - break; + if(selaction->getType() == Gui::SoSelectionElementAction::Append) { + SelContextPtr ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext); + selCounter.checkAction(selaction,ctx); + ctx->selectionColor = selaction->getColor(); + if(ctx->isSelectAll()) + ctx->selectionIndex.clear(); + if(ctx->selectionIndex.insert(index).second) + touch(); + } else { + SelContextPtr ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext,false); + if(ctx && ctx->removeIndex(index)) + touch(); } + break; + } default: + break; } + return; } inherited::doAction(action); } + diff --git a/src/Mod/Part/Gui/SoBrepPointSet.h b/src/Mod/Part/Gui/SoBrepPointSet.h index 91dbe233cb..42a724ecf3 100644 --- a/src/Mod/Part/Gui/SoBrepPointSet.h +++ b/src/Mod/Part/Gui/SoBrepPointSet.h @@ -32,6 +32,8 @@ #include #include #include +#include +#include class SoCoordinateElement; class SoGLCoordinateElement; @@ -48,9 +50,6 @@ public: static void initClass(); SoBrepPointSet(); - SoSFInt32 highlightIndex; - SoMFInt32 selectionIndex; - protected: virtual ~SoBrepPointSet() {}; virtual void GLRender(SoGLRenderAction *action); @@ -58,17 +57,16 @@ protected: virtual void doAction(SoAction* action); private: - void renderShape(const SoGLCoordinateElement * const vertexlist, - const int32_t *vertexindices, - int num_vertexindices); - void renderHighlight(SoGLRenderAction *action); - void renderSelection(SoGLRenderAction *action); - bool validIndexes(const SoCoordinateElement*, int32_t, const int32_t *, int) const; + typedef Gui::SoFCSelectionContext SelContext; + typedef Gui::SoFCSelectionContextPtr SelContextPtr; + void renderHighlight(SoGLRenderAction *action, SelContextPtr); + void renderSelection(SoGLRenderAction *action, SelContextPtr, bool push=true); private: - SbColor selectionColor; - SbColor highlightColor; - SoColorPacker colorpacker; + SelContextPtr selContext; + SelContextPtr selContext2; + Gui::SoFCSelectionCounter selCounter; + uint32_t packedColor; }; } // namespace PartGui