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;