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.
This commit is contained in:
Zheng, Lei
2019-09-17 20:42:39 +08:00
committed by wwmayer
parent d6a1ef125e
commit bb3baefdb5
20 changed files with 488 additions and 262 deletions

View File

@@ -27,6 +27,9 @@
# include <qstring.h>
# include <Inventor/details/SoFaceDetail.h>
# include <Inventor/details/SoLineDetail.h>
# include <Inventor/nodes/SoCube.h>
# include <Inventor/actions/SoGetBoundingBoxAction.h>
# include <Inventor/nodes/SoCube.h>
#endif
#include <Inventor/elements/SoOverrideElement.h>
@@ -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<void*>()(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

View File

@@ -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;

View File

@@ -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<SoFullPath *>(PRIVATE(this)->searchaction->getPath());
if (path) {
SoFCUnifiedSelection * selection = static_cast<SoFCUnifiedSelection *>(path->getTail());
if (selection->getNumSelected()) {
PRIVATE(this)->basecolor->rgb.setValue(selection->colorSelection.getValue());
this->drawBoxes(path, selection->getList());
}
}
PRIVATE(this)->searchaction->reset();
}
}

View File

@@ -64,6 +64,7 @@
#include <Inventor/nodes/SoNormalBinding.h>
#include <Inventor/events/SoLocation2Event.h>
#include <Inventor/SoPickedPoint.h>
#include <Inventor/threads/SbStorage.h>
#ifdef FC_OS_MACOSX
# include <OpenGL/gl.h>
@@ -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<SoFCUnifiedSelection*>(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<ViewProviderDocumentObject*>(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<ViewProvider*>::iterator it = vps.begin(); it != vps.end(); ++it) {
ViewProviderDocumentObject* vpd = static_cast<ViewProviderDocumentObject*>(*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<PickedInfo> &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<ViewProviderGeometryObject*>(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<SoFCSelectionRoot*>(node);
for(stack.offset=0;stack.offset<stack.size();++stack.offset) {
auto it = map.find(stack);
auto ctx = it!=map.end()?it->second: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<bool,SoFCSelectionContextBasePtr*> 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<SelContext>(getNodeContext2(SelStack,this,SelContext::merge));
if(!ctx2 || !ctx2->hideAll) {
auto state = action->getState();
SelContextPtr ctx = getRenderContext<SelContext>(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<SelContext>(getNodeContext2(SelStack,this,SelContext::merge));
if(ctx2 && ctx2->hideAll)
return false;
auto state = action->getState();
SelContextPtr ctx = getRenderContext<SelContext>(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();i<count;++i) {
if(!path->getNode(i)->isOfType(SoFCSelectionRoot::getClassTypeId()))
continue;
auto node = static_cast<SoFCSelectionRoot*>(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);
}
}

View File

@@ -30,7 +30,6 @@
#include <Inventor/fields/SoSFEnum.h>
#include <Inventor/fields/SoSFString.h>
#include <Inventor/nodes/SoLightModel.h>
#include <Inventor/lists/SoPathList.h>
#include "View3DInventorViewer.h"
#include "SoFCSelectionContext.h"
#include <list>
@@ -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<class T>
static std::shared_ptr<T> getSecondaryActionContext(SoAction *action, SoNode *node) {
auto it = ActionStacks.find(action);
if(it == ActionStacks.end())
return std::shared_ptr<T>();
return std::dynamic_pointer_cast<T>(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<SoFCSelectionRoot*> {
public:
@@ -346,7 +345,6 @@ protected:
static Stack SelStack;
static std::unordered_map<SoAction*,Stack> ActionStacks;
struct StackComp {
bool operator()(const Stack &a, const Stack &b) const;
};

View File

@@ -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);

View File

@@ -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) \

View File

@@ -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();

View File

@@ -57,6 +57,7 @@
#include "TaskView/TaskAppearance.h"
#include "ViewProviderDocumentObject.h"
#include "ViewProviderExtension.h"
#include "SoFCUnifiedSelection.h"
#include "Tree.h"
#include <Gui/ViewProviderDocumentObjectPy.h>
@@ -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<SoFCSelectionRoot*>(getRoot())->selectionStyle = SelectionStyle.getValue()
?SoFCSelectionRoot::BOX:SoFCSelectionRoot::FULL;
}
}
if (pcDocument && !pcDocument->isModified() && testStatus(Gui::ViewStatus::TouchDocument)) {
if (prop)

View File

@@ -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 *);

View File

@@ -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);

View File

@@ -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()

View File

@@ -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) {

View File

@@ -164,6 +164,41 @@ void SoBrepEdgeSet::GLRenderBelowPath(SoGLRenderAction * action)
inherited::GLRenderBelowPath(action);
}
void SoBrepEdgeSet::getBoundingBox(SoGetBoundingBoxAction * action) {
SelContextPtr ctx2 = Gui::SoFCSelectionRoot::getSecondaryActionContext<SelContext>(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)
{

View File

@@ -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<SelContext> SelContextPtr;

View File

@@ -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<SelContext>(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<const SoGLCoordinateElement*>(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;j<id;j++)
start+=(int)pindices[j];
start *= 4;
auto viptr = &cindices[start];
auto viendptr = viptr + length;
while (viptr + 2 < viendptr) {
bbox.extendBy(coords3d[*viptr++]);
bbox.extendBy(coords3d[*viptr++]);
bbox.extendBy(coords3d[*viptr++]);
++viptr;
}
}
if(!bbox.isEmpty())
action->extendBy(bbox);
}
// this macro actually makes the code below more readable :-)
#define DO_VERTEX(idx) \
if (mbind == PER_VERTEX) { \

View File

@@ -98,6 +98,7 @@ protected:
const SoPrimitiveVertex * v3,
SoPickedPoint * pp);
virtual void generatePrimitives(SoAction * action);
virtual void getBoundingBox(SoGetBoundingBoxAction * action);
private:
enum Binding {

View File

@@ -154,6 +154,33 @@ void SoBrepPointSet::GLRenderBelowPath(SoGLRenderAction * action)
inherited::GLRenderBelowPath(action);
}
void SoBrepPointSet::getBoundingBox(SoGetBoundingBoxAction * action) {
SelContextPtr ctx2 = Gui::SoFCSelectionRoot::getSecondaryActionContext<SelContext>(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)

View File

@@ -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;

View File

@@ -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;