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