From 45fd865fc0388746e2c53adfd9c0ae4d8917e994 Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Tue, 17 Sep 2019 20:42:39 +0800 Subject: [PATCH] Gui: refactor bounding box selection style Previously, box style selection is rendered using customized SoBoxSelectionRenderAction, which does not support selection context, i.e. it does not work with Link. This patch implements context aware bound box rendering inside SoFCSelectionRoot, SoFCSelection and SoFCPathAnnotation (for always on top rendering). The box rendering in SoBoxSelectionRenderAction is disabled on construction. Box style selection can be enabled for individual object through property SelectionStyle (moved from ViewProviderGeometryObject to ViewProviderDocumentObject), or globally through Parameter BaseApp/Preferences/View/ShowSelectionBoundingBox. In addition, the parameter BaseApp/Preferences/View/UseNewSelection is used to override selection model reported from ViewProvider::useNewSelectionModel(). The reason being that, the same parameter is already used to toggle selection model inside SoFCSelection. This avoids inconsistency of selection model choice between view provider and the SoFCSelection node inside. Note that if the parameter 'UseNewSelection' is set to false, those view providers that choose old selection model will not work with Link. --- src/Gui/SoFCSelection.cpp | 49 ++- src/Gui/SoFCSelection.h | 2 +- src/Gui/SoFCSelectionAction.cpp | 17 +- src/Gui/SoFCUnifiedSelection.cpp | 463 ++++++++++++++----------- src/Gui/SoFCUnifiedSelection.h | 42 ++- src/Gui/View3DInventorViewer.cpp | 17 +- src/Gui/ViewParams.h | 6 +- src/Gui/ViewProvider.cpp | 10 +- src/Gui/ViewProviderDocumentObject.cpp | 11 + src/Gui/ViewProviderDocumentObject.h | 1 + src/Gui/ViewProviderGeometryObject.cpp | 4 - src/Gui/ViewProviderLink.cpp | 1 + src/Mod/Mesh/Gui/ViewProvider.cpp | 6 +- src/Mod/Part/Gui/SoBrepEdgeSet.cpp | 35 ++ src/Mod/Part/Gui/SoBrepEdgeSet.h | 3 + src/Mod/Part/Gui/SoBrepFaceSet.cpp | 47 +++ src/Mod/Part/Gui/SoBrepFaceSet.h | 1 + src/Mod/Part/Gui/SoBrepPointSet.cpp | 27 ++ src/Mod/Part/Gui/SoBrepPointSet.h | 2 + src/Mod/Part/Gui/ViewProviderExt.cpp | 6 + 20 files changed, 488 insertions(+), 262 deletions(-) diff --git a/src/Gui/SoFCSelection.cpp b/src/Gui/SoFCSelection.cpp index 7dc4c9f9fc..83a7acd28c 100644 --- a/src/Gui/SoFCSelection.cpp +++ b/src/Gui/SoFCSelection.cpp @@ -27,6 +27,9 @@ # include # include # include +# include +# include +# include #endif #include @@ -686,10 +689,11 @@ SoFCSelection::GLRenderBelowPath(SoGLRenderAction * action) #ifdef NO_FRONTBUFFER // check if preselection is active - state->push(); - this->setOverride(action,ctx); - inherited::GLRenderBelowPath(action); - state->pop(); + if(this->setOverride(action,ctx)) { + inherited::GLRenderBelowPath(action); + state->pop(); + } else + inherited::GLRenderBelowPath(action); #else // Set up state for locate highlighting (if necessary) GLint oldDepthFunc; @@ -726,10 +730,11 @@ void SoFCSelection::GLRender(SoGLRenderAction * action) #ifdef NO_FRONTBUFFER // check if preselection is active - state->push(); - this->setOverride(action,ctx); - inherited::GLRender(action); - state->pop(); + if(this->setOverride(action,ctx)) { + inherited::GLRender(action); + state->pop(); + } else + inherited::GLRender(action); #else // Set up state for locate highlighting (if necessary) GLint oldDepthFunc; @@ -767,10 +772,11 @@ SoFCSelection::GLRenderInPath(SoGLRenderAction * action) #ifdef NO_FRONTBUFFER // check if preselection is active SoState * state = action->getState(); - state->push(); - this->setOverride(action,ctx); - inherited::GLRenderInPath(action); - state->pop(); + if(this->setOverride(action,ctx)) { + inherited::GLRenderInPath(action); + state->pop(); + } else + inherited::GLRenderInPath(action); #else // Set up state for locate highlighting (if necessary) GLint oldDepthFunc; @@ -948,13 +954,13 @@ SoFCSelection::readInstance ( SoInput * in, unsigned short flags ) // // update override state before rendering // -void +bool 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; + return false; // 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 @@ -963,10 +969,21 @@ SoFCSelection::setOverride(SoGLRenderAction * action, SelContextPtr ctx) auto oldId = this->uniqueId; this->uniqueId ^= std::hash()(ctx.get()) + 0x9e3779b9 + (oldId << 6) + (oldId >> 2); + Styles mystyle = (Styles) this->style.getValue(); + + if(mystyle == SoFCSelection::BOX) { + SoFCSelectionRoot::renderBBox( + action,this,preselected?ctx->highlightColor:ctx->selectionColor); + this->uniqueId = oldId; + return false; + } + //Base::Console().Log("SoFCSelection::setOverride() (%p)\n",this); SoState * state = action->getState(); + state->push(); SoMaterialBindingElement::set(state,SoMaterialBindingElement::OVERALL); + SoOverrideElement::setMaterialBindingOverride(state,this,true); if(!preselected) SoLazyElement::setEmissive(state, &ctx->selectionColor); @@ -974,8 +991,7 @@ SoFCSelection::setOverride(SoGLRenderAction * action, SelContextPtr ctx) SoLazyElement::setEmissive(state, &ctx->highlightColor); SoOverrideElement::setEmissiveColorOverride(state, this, true); - Styles mystyle = (Styles) this->style.getValue(); - if (mystyle == SoFCSelection::EMISSIVE_DIFFUSE) { + if(mystyle == SoFCSelection::EMISSIVE_DIFFUSE) { if(!preselected) SoLazyElement::setDiffuse(state, this,1, &ctx->selectionColor,&colorpacker); else @@ -984,6 +1000,7 @@ SoFCSelection::setOverride(SoGLRenderAction * action, SelContextPtr ctx) } this->uniqueId = oldId; + return true; } // private convenience method diff --git a/src/Gui/SoFCSelection.h b/src/Gui/SoFCSelection.h index 64a00cffc9..216b371a0a 100644 --- a/src/Gui/SoFCSelection.h +++ b/src/Gui/SoFCSelection.h @@ -122,7 +122,7 @@ protected: private: static int getPriority(const SoPickedPoint*); static void turnoffcurrent(SoAction * action); - void setOverride(SoGLRenderAction * action, SelContextPtr); + bool setOverride(SoGLRenderAction * action, SelContextPtr); SbBool isHighlighted(SoAction *action); SbBool preRender(SoGLRenderAction *act, GLint &oldDepthFunc); const SoPickedPoint* getPickedPoint(SoHandleEventAction*) const; diff --git a/src/Gui/SoFCSelectionAction.cpp b/src/Gui/SoFCSelectionAction.cpp index 6b702110a8..68dc73fdf1 100644 --- a/src/Gui/SoFCSelectionAction.cpp +++ b/src/Gui/SoFCSelectionAction.cpp @@ -1142,7 +1142,8 @@ SoBoxSelectionRenderAction::constructorCommon(void) // Initialize local variables PRIVATE(this)->initBoxGraph(); - this->hlVisible = true; + // this->hlVisible = true; + this->hlVisible = false; PRIVATE(this)->basecolor->rgb.setValue(1.0f, 0.0f, 0.0f); PRIVATE(this)->drawstyle->linePattern = 0xffff; @@ -1236,20 +1237,6 @@ SoBoxSelectionRenderAction::apply(SoNode * node) } } PRIVATE(this)->searchaction->reset(); - - // Search for selections of SoFCUnifiedSelection - PRIVATE(this)->searchaction->setType(SoFCUnifiedSelection::getClassTypeId()); - PRIVATE(this)->searchaction->setInterest(SoSearchAction::FIRST); - PRIVATE(this)->searchaction->apply(node); - SoFullPath * path = static_cast(PRIVATE(this)->searchaction->getPath()); - if (path) { - SoFCUnifiedSelection * selection = static_cast(path->getTail()); - if (selection->getNumSelected()) { - PRIVATE(this)->basecolor->rgb.setValue(selection->colorSelection.getValue()); - this->drawBoxes(path, selection->getList()); - } - } - PRIVATE(this)->searchaction->reset(); } } diff --git a/src/Gui/SoFCUnifiedSelection.cpp b/src/Gui/SoFCUnifiedSelection.cpp index 13536ada93..12f2750849 100644 --- a/src/Gui/SoFCUnifiedSelection.cpp +++ b/src/Gui/SoFCUnifiedSelection.cpp @@ -64,6 +64,7 @@ #include #include #include +#include #ifdef FC_OS_MACOSX # include @@ -215,101 +216,6 @@ void SoFCUnifiedSelection::write(SoWriteAction * action) } } -int SoFCUnifiedSelection::getNumSelected(void) const -{ - return this->selectionList.getLength(); -} - -const SoPathList* SoFCUnifiedSelection::getList(void) const -{ - return &this->selectionList; -} - -void SoFCUnifiedSelection::addPath(SoPath * path) -{ - this->selectionList.append(path); -} - -void SoFCUnifiedSelection::removePath(const int which) -{ - SoPath * path = this->selectionList[which]; - path->ref(); - this->selectionList.remove(which); - path->unref(); -} - -SoPath * SoFCUnifiedSelection::copyFromThis(const SoPath * path) const -{ - SoPath * newpath = NULL; - path->ref(); - int i = path->findNode(this); - if (i >= 0) { - newpath = path->copy(i); - } - path->unrefNoDelete(); - return newpath; -} - -int SoFCUnifiedSelection::findPath(const SoPath * path) const -{ - int idx = -1; - - // make copy only if necessary - if (path->getHead() != this) { - SoPath * newpath = this->copyFromThis(path); - if (newpath) { - newpath->ref(); - idx = this->selectionList.findPath(*newpath); - newpath->unref(); - } - else { - idx = -1; - } - } - else { - idx = this->selectionList.findPath(*path); - } - return idx; -} - -SoPath * SoFCUnifiedSelection::searchNode(SoNode * node) const -{ - SoSearchAction sa; - sa.setNode(node); - sa.apply(const_cast(this)); - SoPath * path = sa.getPath(); - if (path) - path->ref(); - return path; -} - -void SoFCUnifiedSelection::select(SoNode * node) -{ - SoPath * path = this->searchNode(node); - if (path) { - // don't ref() the path. searchNode() will ref it before returning - if (this->findPath(path) < 0) - this->addPath(path); - path->unref(); - } -} - -void SoFCUnifiedSelection::deselect(const SoPath * path) -{ - int idx = this->findPath(path); - if (idx >= 0) this->removePath(idx); -} - -void SoFCUnifiedSelection::deselect(SoNode * node) -{ - SoPath * path = this->searchNode(node); - if (path) { - // don't ref() the path. searchNode() will ref it before returning - this->deselect(path); - path->unref(); - } -} - int SoFCUnifiedSelection::getPriority(const SoPickedPoint* p) { const SoDetail* detail = p->getDetail(); @@ -344,7 +250,7 @@ SoFCUnifiedSelection::getPickedList(SoHandleEventAction* action, bool singlePick break; } info.vpd = static_cast(vp); - if(!info.vpd->useNewSelectionModel() || !info.vpd->isSelectable()) { + if(!(useNewSelection.getValue()||info.vpd->useNewSelectionModel()) || !info.vpd->isSelectable()) { if(!singlePick) continue; if(ret.empty()) { info.vpd = 0; @@ -474,7 +380,7 @@ void SoFCUnifiedSelection::doAction(SoAction *action) 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->useNewSelectionModel() && vp->isSelectable()) { + if (vp && (useNewSelection.getValue()||vp->useNewSelectionModel()) && vp->isSelectable()) { SoDetail *detail = nullptr; detailPath->truncate(0); if(!selaction->SelChange.pSubName || !selaction->SelChange.pSubName[0] || @@ -494,15 +400,13 @@ void SoFCUnifiedSelection::doAction(SoAction *action) type = SoSelectionElementAction::None; } - 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()); - } + 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; @@ -518,17 +422,15 @@ void SoFCUnifiedSelection::doAction(SoAction *action) vps = this->pcDocument->getViewProvidersOfType(ViewProviderDocumentObject::getClassTypeId()); for (std::vector::iterator it = vps.begin(); it != vps.end(); ++it) { ViewProviderDocumentObject* vpd = static_cast(*it); - if (vpd->useNewSelectionModel()) { + if (useNewSelection.getValue() || vpd->useNewSelectionModel()) { SoSelectionElementAction::Type type; if(Selection().isSelected(vpd->getObject()) && vpd->isSelectable()) type = SoSelectionElementAction::All; else type = SoSelectionElementAction::None; - if (checkSelectionStyle(type, vpd)) { - SoSelectionElementAction action(type); - action.setColor(this->colorSelection.getValue()); - action.apply(vpd->getRoot()); - } + SoSelectionElementAction action(type); + action.setColor(this->colorSelection.getValue()); + action.apply(vpd->getRoot()); } } } else if (selaction->SelChange.Type == SelectionChanges::SetPreselectSignal) { @@ -537,7 +439,7 @@ void SoFCUnifiedSelection::doAction(SoAction *action) App::DocumentObject* obj = doc->getObject(selaction->SelChange.pObjectName); ViewProvider*vp = Application::Instance->getViewProvider(obj); if (vp && vp->isDerivedFrom(ViewProviderDocumentObject::getClassTypeId()) && - vp->useNewSelectionModel() && vp->isSelectable()) + (useNewSelection.getValue()||vp->useNewSelectionModel()) && vp->isSelectable()) { detailPath->truncate(0); SoDetail *det = 0; @@ -780,7 +682,7 @@ bool SoFCUnifiedSelection::setSelection(const std::vector &infos, bo getMainWindow()->showMessage(QString::fromLatin1(buf)); } - if (pPath && checkSelectionStyle(type,vpd)) { + if (pPath) { FC_TRACE("applying action"); SoSelectionElementAction action(type); action.setColor(this->colorSelection.getValue()); @@ -855,31 +757,6 @@ SoFCUnifiedSelection::handleEvent(SoHandleEventAction * action) inherited::handleEvent(action); } -bool SoFCUnifiedSelection::checkSelectionStyle(int type, ViewProvider *vp) -{ - if ((type == SoSelectionElementAction::All || type == SoSelectionElementAction::None) && - vp->isDerivedFrom(ViewProviderGeometryObject::getClassTypeId()) && - static_cast(vp)->SelectionStyle.getValue() == 1) - { - bool selected = (type == SoSelectionElementAction::All); - int numSelected = getNumSelected(); - if (selected) { - select(vp->getRoot()); - } - else { - deselect(vp->getRoot()); - } - - if (numSelected != getNumSelected()) - this->touch(); - - if (selected) { - return false; - } - } - return true; -} - void SoFCUnifiedSelection::GLRenderBelowPath(SoGLRenderAction * action) { inherited::GLRenderBelowPath(action); @@ -1107,6 +984,7 @@ void SoVRMLAction::callDoAction(SoAction *action, SoNode *node) } // --------------------------------------------------------------------------------- + bool SoFCSelectionRoot::StackComp::operator()(const Stack &a, const Stack &b) const { if(a.size()-a.offset < b.size()-b.offset) return true; @@ -1130,13 +1008,17 @@ SoFCSeparator::SoFCSeparator(bool trackCacheMode) :trackCacheMode(trackCacheMode) { SO_NODE_CONSTRUCTOR(SoFCSeparator); - if(!trackCacheMode) + if(!trackCacheMode) { renderCaching = SoSeparator::OFF; + boundingBoxCaching = SoSeparator::OFF; + } } void SoFCSeparator::GLRenderBelowPath(SoGLRenderAction * action) { - if(trackCacheMode && renderCaching.getValue()!=CacheMode) + if(trackCacheMode && renderCaching.getValue()!=CacheMode) { renderCaching = CacheMode; + boundingBoxCaching = CacheMode; + } inherited::GLRenderBelowPath(action); } @@ -1150,6 +1032,40 @@ void SoFCSeparator::finish() atexit_cleanup(); } +// --------------------------------------------------------------------------------- +// Thread local data for bounding box rendering +// +// The code is inspred by Coin SoLevelOfDetails.cpp. +typedef struct { + SoGetBoundingBoxAction * bboxaction; + SoCube *cube; + SoColorPacker *packer; +} SoFCBBoxRenderInfo; + +static void so_bbox_construct_data(void * closure) +{ + SoFCBBoxRenderInfo * data = (SoFCBBoxRenderInfo*) closure; + data->bboxaction = NULL; + data->cube = NULL; + data->packer = NULL; +} + +static void so_bbox_destruct_data(void * closure) +{ + SoFCBBoxRenderInfo * data = (SoFCBBoxRenderInfo*) closure; + delete data->bboxaction; + if(data->cube) + data->cube->unref(); + delete data->packer; +} + +static SbStorage * so_bbox_storage = NULL; + +// called from atexit +static void so_bbox_cleanup(void) +{ + delete so_bbox_storage; +} // --------------------------------------------------------------------------------- @@ -1165,6 +1081,11 @@ SoFCSelectionRoot::SoFCSelectionRoot(bool trackCacheMode) :SoFCSeparator(trackCacheMode) { SO_NODE_CONSTRUCTOR(SoFCSelectionRoot); + SO_NODE_ADD_FIELD(selectionStyle,(FULL)); + SO_NODE_DEFINE_ENUM_VALUE(SelectStyles, FULL); + SO_NODE_DEFINE_ENUM_VALUE(SelectStyles, BOX); + SO_NODE_DEFINE_ENUM_VALUE(SelectStyles, PASSTHROUGH); + SO_NODE_SET_SF_ENUM_TYPE(selectionStyle, SelectStyles); } SoFCSelectionRoot::~SoFCSelectionRoot() @@ -1174,10 +1095,16 @@ SoFCSelectionRoot::~SoFCSelectionRoot() void SoFCSelectionRoot::initClass(void) { SO_NODE_INIT_CLASS(SoFCSelectionRoot,SoFCSeparator,"FCSelectionRoot"); + + so_bbox_storage = new SbStorage(sizeof(SoFCBBoxRenderInfo), + so_bbox_construct_data, so_bbox_destruct_data); + + // cc_coin_atexit((coin_atexit_f*) so_bbox_cleanup); } void SoFCSelectionRoot::finish() { + so_bbox_cleanup(); atexit_cleanup(); } @@ -1220,7 +1147,9 @@ SoFCSelectionRoot::getNodeContext2(Stack &stack, SoNode *node, SoFCSelectionCont stack.back() = static_cast(node); for(stack.offset=0;stack.offsetsecond:SoFCSelectionContextBasePtr(); + SoFCSelectionContextBasePtr ctx; + if(it!=map.end()) + ctx = it->second; status = merge(status,ret,ctx,stack.offset==stack.size()-1?0:stack[stack.offset]); if(status<0) break; @@ -1277,6 +1206,54 @@ std::pair SoFCSelectionRoot::findActionContex return res; } +bool SoFCSelectionRoot::renderBBox(SoGLRenderAction *action, SoNode *node, SbColor color) +{ + auto data = (SoFCBBoxRenderInfo*) so_bbox_storage->get(); + if (data->bboxaction == NULL) { + // The viewport region will be replaced every time the action is + // used, so we can just feed it a dummy here. + data->bboxaction = new SoGetBoundingBoxAction(SbViewportRegion()); + data->cube = new SoCube; + data->cube->ref(); + data->packer = new SoColorPacker; + } + + SbBox3f bbox; + data->bboxaction->setViewportRegion(action->getViewportRegion()); + data->bboxaction->apply(node); + bbox = data->bboxaction->getBoundingBox(); + if(bbox.isEmpty()) + return false; + + auto state = action->getState(); + + state->push(); + + SoMaterialBindingElement::set(state,SoMaterialBindingElement::OVERALL); + SoLazyElement::setEmissive(state, &color); + SoLazyElement::setDiffuse(state, node,1, &color,data->packer); + SoDrawStyleElement::set(state,node,SoDrawStyleElement::LINES); + + const static float trans = 0.0; + SoLazyElement::setTransparency(state, node, 1, &trans, data->packer); + + float x, y, z; + bbox.getSize(x, y, z); + data->cube->width = x+0.001; + data->cube->height = y+0.001; + data->cube->depth = z+0.001; + + SoModelMatrixElement::translateBy(state,node,bbox.getCenter()); + + SoMaterialBundle mb(action); + mb.sendFirst(); + + data->cube->GLRender(action); + + state->pop(); + return true; +} + static std::time_t _CyclicLastReported; void SoFCSelectionRoot::renderPrivate(SoGLRenderAction * action, bool inPath) { @@ -1291,42 +1268,78 @@ void SoFCSelectionRoot::renderPrivate(SoGLRenderAction * action, bool inPath) { 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); + if(_renderPrivate(action,inPath)) { + if(inPath) + SoSeparator::GLRenderInPath(action); + else + SoSeparator::GLRenderBelowPath(action); + } + SelStack.pop_back(); + SelStack.nodeSet.erase(this); +} - SoTextureEnabledElement::set(state,ShapeColorNode,false); +bool SoFCSelectionRoot::_renderPrivate(SoGLRenderAction * action, bool inPath) { + auto ctx2 = std::static_pointer_cast(getNodeContext2(SelStack,this,SelContext::merge)); + if(ctx2 && ctx2->hideAll) + return false; + + auto state = action->getState(); + SelContextPtr ctx = getRenderContext(this); + int style = selectionStyle.getValue(); + if((style==SoFCSelectionRoot::BOX || ViewParams::instance()->getShowSelectionBoundingBox()) + && ctx && !ctx->hideAll && (ctx->selAll || ctx->hlAll)) + { + if(style==SoFCSelectionRoot::PASSTHROUGH) + style = SoFCSelectionRoot::BOX; + else { + renderBBox(action,this,ctx->hlAll?ctx->hlColor:ctx->selColor); + return true; } - 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); + } + + // Here, we are not setting (pre)selection color override here. + // Instead, we are checking and setting up for any secondary context + // color override. + // + // When the current selection style is full highlight, we should ignore any + // secondary override. If the style is bounding box, however, we should + // honour the secondary color override. + + bool colorPushed = false; + if(!ShapeColorNode && overrideColor && + !SoOverrideElement::getDiffuseColorOverride(state) && + (style==SoFCSelectionRoot::BOX || !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); + + if(style != SoFCSelectionRoot::BOX) { state->push(); auto &color = SelColorStack.back(); SoLazyElement::setEmissive(state, &color); @@ -1339,26 +1352,32 @@ void SoFCSelectionRoot::renderPrivate(SoGLRenderAction * action, bool inPath) { 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(); + } + + if((hlPushed = ctx->hlAll)) + HlColorStack.push_back(ctx->hlColor); + + if(inPath) + SoSeparator::GLRenderInPath(action); + else + SoSeparator::GLRenderBelowPath(action); + + if(selPushed) { + SelColorStack.pop_back(); + + if(style != SoFCSelectionRoot::BOX) state->pop(); - } - if(hlPushed) - HlColorStack.pop_back(); - } - if(colorPushed) { - ShapeColorNode = 0; - state->pop(); } + if(hlPushed) + HlColorStack.pop_back(); } - SelStack.pop_back(); - SelStack.nodeSet.erase(this); + + if(colorPushed) { + ShapeColorNode = 0; + state->pop(); + } + + return false; } void SoFCSelectionRoot::GLRenderBelowPath(SoGLRenderAction * action) { @@ -1407,6 +1426,16 @@ void SoFCSelectionRoot::resetContext() { contextMap.clear(); } +void SoFCSelectionRoot::moveActionStack(SoAction *from, SoAction *to, bool erase) { + auto it = ActionStacks.find(from); + if(it == ActionStacks.end()) + return; + auto &stack = ActionStacks[to]; + assert(stack.empty()); + stack.swap(it->second); + if(erase) + ActionStacks.erase(it); +} #define BEGIN_ACTION \ auto &stack = ActionStacks[action];\ @@ -1709,10 +1738,39 @@ void SoFCPathAnnotation::GLRenderBelowPath(SoGLRenderAction * action) if (action->isRenderingDelayedPaths()) { SbBool zbenabled = glIsEnabled(GL_DEPTH_TEST); if (zbenabled) glDisable(GL_DEPTH_TEST); - inherited::GLRenderInPath(action); + + if(det) + inherited::GLRenderInPath(action); + else { + bool bbox = ViewParams::instance()->getShowSelectionBoundingBox(); + if(!bbox) { + for(int i=0,count=path->getLength();igetNode(i)->isOfType(SoFCSelectionRoot::getClassTypeId())) + continue; + auto node = static_cast(path->getNode(i)); + if(node->selectionStyle.getValue()==SoFCSelectionRoot::BOX) { + bbox = true; + break; + } + } + } + if(!bbox) + inherited::GLRenderInPath(action); + else { + bool sel = false; + bool hl = false; + SbColor selColor,hlColor; + SoFCSelectionRoot::checkSelection(sel,selColor,hl,hlColor); + if(sel || hl) + SoFCSelectionRoot::renderBBox(action,this,hl?hlColor:selColor); + else + inherited::GLRenderInPath(action); + } + } + if (zbenabled) glEnable(GL_DEPTH_TEST); - } - else { + + } else { SoCacheElement::invalidate(action->getState()); auto curPath = action->getCurPath(); SoPath *newPath = new SoPath(curPath->getLength()+path->getLength()); @@ -1755,3 +1813,16 @@ void SoFCPathAnnotation::setPath(SoPath *newPath) { path->ref(); addChild(path->getNode(0)); } + +void SoFCPathAnnotation::getBoundingBox(SoGetBoundingBoxAction * action) +{ + if(path) { + SoGetBoundingBoxAction bboxAction(action->getViewportRegion()); + SoFCSelectionRoot::moveActionStack(action,&bboxAction,false); + bboxAction.apply(path); + SoFCSelectionRoot::moveActionStack(&bboxAction,action,true); + auto bbox = bboxAction.getBoundingBox(); + if(!bbox.isEmpty()) + action->extendBy(bbox); + } +} diff --git a/src/Gui/SoFCUnifiedSelection.h b/src/Gui/SoFCUnifiedSelection.h index 43db9756c2..53bcb21194 100644 --- a/src/Gui/SoFCUnifiedSelection.h +++ b/src/Gui/SoFCUnifiedSelection.h @@ -30,7 +30,6 @@ #include #include #include -#include #include "View3DInventorViewer.h" #include "SoFCSelectionContext.h" #include @@ -71,8 +70,6 @@ public: const char* getFileFormatName(void) const; void write(SoWriteAction * action); - int getNumSelected(void) const; - const SoPathList* getList(void) const; SoSFColor colorHighlight; SoSFColor colorSelection; @@ -89,8 +86,6 @@ public: //virtual void GLRenderInPath(SoGLRenderAction * action); //static void turnOffCurrentHighlight(SoGLRenderAction * action); - bool checkSelectionStyle(int type, ViewProvider *vp); - static bool hasHighlight(); friend class View3DInventorViewer; @@ -100,21 +95,6 @@ protected: //virtual void redrawHighlighted(SoAction * act, SbBool flag); //virtual SbBool readInstance(SoInput * in, unsigned short flags); - /** @name Nodes selection. - * The SoBoxSelectionRenderAction uses these nodes to draw a - * bounding box. - */ - //@{ - void addPath(SoPath * path); - void removePath(const int which); - SoPath * copyFromThis(const SoPath * path) const; - SoPath * searchNode(SoNode * node) const; - int findPath(const SoPath * path) const; - void select(SoNode * node); - void deselect(const SoPath * path); - void deselect(SoNode * node); - //@} - private: //static void turnoffcurrent(SoAction * action); //void setOverride(SoGLRenderAction * action); @@ -141,7 +121,6 @@ private: static SoFullPath * currenthighlight; SoFullPath * detailPath; - SoPathList selectionList; SbBool setPreSelection; @@ -168,6 +147,8 @@ public: virtual void GLRender(SoGLRenderAction * action); virtual void GLRenderInPath(SoGLRenderAction * action); + virtual void getBoundingBox(SoGetBoundingBoxAction * action); + protected: virtual ~SoFCPathAnnotation(); @@ -304,8 +285,18 @@ public: return findActionContext(action,node,false,true).second!=0; } + template + static std::shared_ptr getSecondaryActionContext(SoAction *action, SoNode *node) { + auto it = ActionStacks.find(action); + if(it == ActionStacks.end()) + return std::shared_ptr(); + return std::dynamic_pointer_cast(getNodeContext2(it->second,node,T::merge)); + } + static void checkSelection(bool &sel, SbColor &selColor, bool &hl, SbColor &hlColor); + static void moveActionStack(SoAction *from, SoAction *to, bool erase); + static SoNode *getCurrentRoot(bool front, SoNode *def); void resetContext(); @@ -326,10 +317,18 @@ public: overrideColor = false; } + enum SelectStyles { + FULL, BOX, PASSTHROUGH + }; + SoSFEnum selectionStyle; + + static bool renderBBox(SoGLRenderAction *action, SoNode *node, SbColor color); + protected: virtual ~SoFCSelectionRoot(); void renderPrivate(SoGLRenderAction *, bool inPath); + bool _renderPrivate(SoGLRenderAction *, bool inPath); class Stack : public std::vector { public: @@ -346,7 +345,6 @@ protected: static Stack SelStack; static std::unordered_map ActionStacks; - struct StackComp { bool operator()(const Stack &a, const Stack &b) const; }; diff --git a/src/Gui/View3DInventorViewer.cpp b/src/Gui/View3DInventorViewer.cpp index d52a081b25..82fccfdbf3 100644 --- a/src/Gui/View3DInventorViewer.cpp +++ b/src/Gui/View3DInventorViewer.cpp @@ -506,11 +506,15 @@ void View3DInventorViewer::init() pcOnTopMaterial->setOverride(true); pcGroupOnTop->addChild(pcOnTopMaterial); - pcGroupOnTopSel = new SoFCSelectionRoot; + auto selRoot = new SoFCSelectionRoot; + selRoot->selectionStyle = SoFCSelectionRoot::PASSTHROUGH; + pcGroupOnTopSel = selRoot; pcGroupOnTopSel->setName("GroupOnTopSel"); pcGroupOnTopSel->ref(); pcGroupOnTop->addChild(pcGroupOnTopSel); - pcGroupOnTopPreSel = new SoFCSelectionRoot; + selRoot = new SoFCSelectionRoot; + selRoot->selectionStyle = SoFCSelectionRoot::PASSTHROUGH; + pcGroupOnTopPreSel = selRoot; pcGroupOnTopPreSel->setName("GroupOnTopPreSel"); pcGroupOnTopPreSel->ref(); pcGroupOnTop->addChild(pcGroupOnTopPreSel); @@ -805,9 +809,12 @@ void View3DInventorViewer::checkGroupOnTop(const SelectionChanges &Reason) { // 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(Gui::Selection().needPickedList()) + onTop = 1; + else if(vp->OnTopWhenSelected.getValue()) + onTop = vp->OnTopWhenSelected.getValue(); + else + onTop = svp->OnTopWhenSelected.getValue(); if(Reason.Type == SelectionChanges::SetPreselect) { SoHighlightElementAction action; action.setHighlighted(true); diff --git a/src/Gui/ViewParams.h b/src/Gui/ViewParams.h index e750ed90c4..9a94004658 100644 --- a/src/Gui/ViewParams.h +++ b/src/Gui/ViewParams.h @@ -28,7 +28,10 @@ namespace Gui { -/** Convenient class to obtain view provider related parameters */ +/** Convenient class to obtain view provider related parameters + * + * The parameters are under group "User parameter:BaseApp/Preferences/View" + */ class GuiExport ViewParams: public ParameterGrp::ObserverType { public: ViewParams(); @@ -55,6 +58,7 @@ public: FC_VIEW_PARAM(DefaultShapeLineWidth,int,Int,2) \ FC_VIEW_PARAM(CoinCycleCheck,bool,Bool,true) \ FC_VIEW_PARAM(EnablePropertyViewForInactiveDocument,bool,Bool,true) \ + FC_VIEW_PARAM(ShowSelectionBoundingBox,bool,Bool,false) \ #undef FC_VIEW_PARAM #define FC_VIEW_PARAM(_name,_ctype,_type,_def) \ diff --git a/src/Gui/ViewProvider.cpp b/src/Gui/ViewProvider.cpp index b3d0d6d5e1..2c363b6afc 100644 --- a/src/Gui/ViewProvider.cpp +++ b/src/Gui/ViewProvider.cpp @@ -104,7 +104,15 @@ ViewProvider::ViewProvider() { setStatus(UpdateData, true); - pcRoot = new SoFCSeparator; + + // SoFCSeparater and SoFCSelectionRoot can both track render cache setting. + // We change to SoFCSelectionRoot so that we can dynamically change full + // selection mode (full highlight vs. boundbox). Note that comparing to + // SoFCSeparater, there are some small overhead with SoFCSelectionRoot for + // selection context tracking. + // + // pcRoot = new SoFCSeparator(true); + pcRoot = new SoFCSelectionRoot(true); pcRoot->ref(); pcModeSwitch = new SoSwitch(); pcModeSwitch->ref(); diff --git a/src/Gui/ViewProviderDocumentObject.cpp b/src/Gui/ViewProviderDocumentObject.cpp index 050f445850..1e622fce35 100644 --- a/src/Gui/ViewProviderDocumentObject.cpp +++ b/src/Gui/ViewProviderDocumentObject.cpp @@ -57,6 +57,7 @@ #include "TaskView/TaskAppearance.h" #include "ViewProviderDocumentObject.h" #include "ViewProviderExtension.h" +#include "SoFCUnifiedSelection.h" #include "Tree.h" #include @@ -75,6 +76,10 @@ ViewProviderDocumentObject::ViewProviderDocumentObject() ADD_PROPERTY(Visibility,(true)); ADD_PROPERTY(ShowInTree,(true)); + ADD_PROPERTY(SelectionStyle,((long)0)); + static const char *SelectionStyleEnum[] = {"Shape","BoundBox",0}; + SelectionStyle.setEnums(SelectionStyleEnum); + static const char* OnTopEnum[]= {"Disabled","Enabled","Object","Element",NULL}; ADD_PROPERTY(OnTopWhenSelected,((long int)0)); ADD_PROPERTY_TYPE(OnTopWhenSelected,((long int)0), "Base", App::Prop_None, @@ -181,6 +186,12 @@ void ViewProviderDocumentObject::onChanged(const App::Property* prop) if(getObject() && getObject()->Visibility.getValue()!=Visibility.getValue()) getObject()->Visibility.setValue(Visibility.getValue()); } + else if (prop == &SelectionStyle) { + if(getRoot()->isOfType(SoFCSelectionRoot::getClassTypeId())) { + static_cast(getRoot())->selectionStyle = SelectionStyle.getValue() + ?SoFCSelectionRoot::BOX:SoFCSelectionRoot::FULL; + } + } if (pcDocument && !pcDocument->isModified() && testStatus(Gui::ViewStatus::TouchDocument)) { if (prop) diff --git a/src/Gui/ViewProviderDocumentObject.h b/src/Gui/ViewProviderDocumentObject.h index 79bda407c1..b6fba4d81b 100644 --- a/src/Gui/ViewProviderDocumentObject.h +++ b/src/Gui/ViewProviderDocumentObject.h @@ -62,6 +62,7 @@ public: App::PropertyBool Visibility; App::PropertyBool ShowInTree; App::PropertyEnumeration OnTopWhenSelected; + App::PropertyEnumeration SelectionStyle; virtual void attach(App::DocumentObject *pcObject); virtual void reattach(App::DocumentObject *); diff --git a/src/Gui/ViewProviderGeometryObject.cpp b/src/Gui/ViewProviderGeometryObject.cpp index 23e7937950..d0129fb229 100644 --- a/src/Gui/ViewProviderGeometryObject.cpp +++ b/src/Gui/ViewProviderGeometryObject.cpp @@ -103,10 +103,6 @@ ViewProviderGeometryObject::ViewProviderGeometryObject() ADD_PROPERTY(BoundingBox,(false)); ADD_PROPERTY(Selectable,(true)); - ADD_PROPERTY(SelectionStyle,((long)0)); - static const char *SelectionStyleEnum[] = {"Shape","BoundBox",0}; - SelectionStyle.setEnums(SelectionStyleEnum); - bool enableSel = hGrp->GetBool("EnableSelection", true); Selectable.setValue(enableSel); diff --git a/src/Gui/ViewProviderLink.cpp b/src/Gui/ViewProviderLink.cpp index 4b34ecb77b..a9fa99e5f3 100644 --- a/src/Gui/ViewProviderLink.cpp +++ b/src/Gui/ViewProviderLink.cpp @@ -363,6 +363,7 @@ public: pcSnapshot = new SoFCSelectionRoot; else pcSnapshot = new SoSeparator; + pcSnapshot->boundingBoxCaching = SoSeparator::OFF; pcSnapshot->renderCaching = SoSeparator::OFF; std::ostringstream ss; ss << pcLinked->getObject()->getNameInDocument() diff --git a/src/Mod/Mesh/Gui/ViewProvider.cpp b/src/Mod/Mesh/Gui/ViewProvider.cpp index 0f92a4186e..431c0b64d0 100644 --- a/src/Mod/Mesh/Gui/ViewProvider.cpp +++ b/src/Mod/Mesh/Gui/ViewProvider.cpp @@ -315,7 +315,7 @@ ViewProviderMesh::ViewProviderMesh() : pcOpenEdge(0) } if (hGrp->GetBool("ShowBoundingBox", false)) - pcHighlight->style = Gui::SoFCSelection::BOX; + SelectionStyle.setValue(1); Coloring.setStatus(App::Property::Hidden, true); } @@ -370,6 +370,10 @@ void ViewProviderMesh::onChanged(const App::Property* prop) else if (prop == &Coloring) { tryColorPerVertexOrFace(Coloring.getValue()); } + else if (prop == &SelectionStyle) { + pcHighlight->style = SelectionStyle.getValue() + ?Gui::SoFCSelection::BOX:Gui::SoFCSelection::EMISSIVE; + } else { // Set the inverse color for open edges if (prop == &ShapeColor) { diff --git a/src/Mod/Part/Gui/SoBrepEdgeSet.cpp b/src/Mod/Part/Gui/SoBrepEdgeSet.cpp index b57aedccd4..3b9875f211 100644 --- a/src/Mod/Part/Gui/SoBrepEdgeSet.cpp +++ b/src/Mod/Part/Gui/SoBrepEdgeSet.cpp @@ -164,6 +164,41 @@ void SoBrepEdgeSet::GLRenderBelowPath(SoGLRenderAction * action) inherited::GLRenderBelowPath(action); } +void SoBrepEdgeSet::getBoundingBox(SoGetBoundingBoxAction * action) { + + SelContextPtr ctx2 = Gui::SoFCSelectionRoot::getSecondaryActionContext(action,this); + if(!ctx2 || (ctx2->sl.size()==1 && ctx2->sl[0]<0)) { + inherited::getBoundingBox(action); + return; + } + + if(ctx2->sl.empty()) + return; + + auto state = action->getState(); + auto coords = SoCoordinateElement::getInstance(state); + const SbVec3f *coords3d = coords->getArrayPtr3(); + + if(!validIndexes(coords,ctx2->sl)) + return; + + SbBox3f bbox; + + int32_t i; + const int32_t *cindices = &ctx2->sl[0]; + const int32_t *end = cindices + ctx2->sl.size(); + while (cindices < end) { + bbox.extendBy(coords3d[*cindices++]); + i = (cindices < end) ? *cindices++ : -1; + while (i >= 0) { + bbox.extendBy(coords3d[i]); + i = cindices < end ? *cindices++ : -1; + } + } + if(!bbox.isEmpty()) + action->extendBy(bbox); +} + void SoBrepEdgeSet::renderShape(const SoGLCoordinateElement * const coords, const int32_t *cindices, int numindices) { diff --git a/src/Mod/Part/Gui/SoBrepEdgeSet.h b/src/Mod/Part/Gui/SoBrepEdgeSet.h index dfa9428111..ca1c65fd2b 100644 --- a/src/Mod/Part/Gui/SoBrepEdgeSet.h +++ b/src/Mod/Part/Gui/SoBrepEdgeSet.h @@ -60,6 +60,9 @@ protected: const SoPrimitiveVertex *v1, const SoPrimitiveVertex *v2, SoPickedPoint *pp); + + virtual void getBoundingBox(SoGetBoundingBoxAction * action); + private: struct SelContext; typedef std::shared_ptr SelContextPtr; diff --git a/src/Mod/Part/Gui/SoBrepFaceSet.cpp b/src/Mod/Part/Gui/SoBrepFaceSet.cpp index 6e192e03c9..d15a37fb32 100644 --- a/src/Mod/Part/Gui/SoBrepFaceSet.cpp +++ b/src/Mod/Part/Gui/SoBrepFaceSet.cpp @@ -890,6 +890,53 @@ void SoBrepFaceSet::GLRenderBelowPath(SoGLRenderAction * action) inherited::GLRenderBelowPath(action); } +void SoBrepFaceSet::getBoundingBox(SoGetBoundingBoxAction * action) { + + if (this->coordIndex.getNum() < 3) + return; + + SelContextPtr ctx2 = Gui::SoFCSelectionRoot::getSecondaryActionContext(action,this); + if(!ctx2 || ctx2->isSelectAll()) { + inherited::getBoundingBox(action); + return; + } + + if(ctx2->selectionIndex.empty()) + return; + + auto state = action->getState(); + auto coords = SoCoordinateElement::getInstance(state); + const SbVec3f *coords3d = static_cast(coords)->getArrayPtr3(); + const int32_t *cindices = this->coordIndex.getValues(0); + const int32_t *pindices = this->partIndex.getValues(0); + int numparts = this->partIndex.getNum(); + + SbBox3f bbox; + for(auto id : ctx2->selectionIndex) { + if (id<0 || id >= numparts) + break; + // coords + int length=0; + int start=0; + length = (int)pindices[id]*4; + for (int j=0;jextendBy(bbox); +} + // this macro actually makes the code below more readable :-) #define DO_VERTEX(idx) \ if (mbind == PER_VERTEX) { \ diff --git a/src/Mod/Part/Gui/SoBrepFaceSet.h b/src/Mod/Part/Gui/SoBrepFaceSet.h index 344655c46c..fab4b94cb7 100644 --- a/src/Mod/Part/Gui/SoBrepFaceSet.h +++ b/src/Mod/Part/Gui/SoBrepFaceSet.h @@ -98,6 +98,7 @@ protected: const SoPrimitiveVertex * v3, SoPickedPoint * pp); virtual void generatePrimitives(SoAction * action); + virtual void getBoundingBox(SoGetBoundingBoxAction * action); private: enum Binding { diff --git a/src/Mod/Part/Gui/SoBrepPointSet.cpp b/src/Mod/Part/Gui/SoBrepPointSet.cpp index 268084490d..ff5ee35875 100644 --- a/src/Mod/Part/Gui/SoBrepPointSet.cpp +++ b/src/Mod/Part/Gui/SoBrepPointSet.cpp @@ -154,6 +154,33 @@ void SoBrepPointSet::GLRenderBelowPath(SoGLRenderAction * action) inherited::GLRenderBelowPath(action); } +void SoBrepPointSet::getBoundingBox(SoGetBoundingBoxAction * action) { + + SelContextPtr ctx2 = Gui::SoFCSelectionRoot::getSecondaryActionContext(action,this); + if(!ctx2 || ctx2->isSelectAll()) { + inherited::getBoundingBox(action); + return; + } + + if(ctx2->selectionIndex.empty()) + return; + + auto state = action->getState(); + auto coords = SoCoordinateElement::getInstance(state); + const SbVec3f *coords3d = coords->getArrayPtr3(); + int numverts = coords->getNum(); + int startIndex = this->startIndex.getValue(); + + SbBox3f bbox; + for(auto idx : ctx2->selectionIndex) { + if(idx >= startIndex && idx < numverts) + bbox.extendBy(coords3d[idx]); + } + + if(!bbox.isEmpty()) + action->extendBy(bbox); +} + void SoBrepPointSet::renderHighlight(SoGLRenderAction *action, SelContextPtr ctx) { if(!ctx || ctx->highlightIndex<0) diff --git a/src/Mod/Part/Gui/SoBrepPointSet.h b/src/Mod/Part/Gui/SoBrepPointSet.h index 42a724ecf3..cdfa1bdb1d 100644 --- a/src/Mod/Part/Gui/SoBrepPointSet.h +++ b/src/Mod/Part/Gui/SoBrepPointSet.h @@ -56,6 +56,8 @@ protected: virtual void GLRenderBelowPath(SoGLRenderAction * action); virtual void doAction(SoAction* action); + virtual void getBoundingBox(SoGetBoundingBoxAction * action); + private: typedef Gui::SoFCSelectionContext SelContext; typedef Gui::SoFCSelectionContextPtr SelContextPtr; diff --git a/src/Mod/Part/Gui/ViewProviderExt.cpp b/src/Mod/Part/Gui/ViewProviderExt.cpp index 39896d656c..db15911363 100644 --- a/src/Mod/Part/Gui/ViewProviderExt.cpp +++ b/src/Mod/Part/Gui/ViewProviderExt.cpp @@ -493,6 +493,12 @@ void ViewProviderPartExt::attach(App::DocumentObject *pcFeat) pcPointsRoot->renderCaching = wireframe->renderCaching = SoSeparator::OFF; + pcNormalRoot->boundingBoxCaching = + pcFlatRoot->boundingBoxCaching = + pcWireframeRoot->boundingBoxCaching = + pcPointsRoot->boundingBoxCaching = + wireframe->boundingBoxCaching = SoSeparator::OFF; + // enable two-side rendering pShapeHints->vertexOrdering = SoShapeHints::COUNTERCLOCKWISE; pShapeHints->shapeType = SoShapeHints::UNKNOWN_SHAPE_TYPE;