Merge pull request #22029 from tetektoza/realthunder_multiselect_without_pie

Gui: Add a context menu to select obstructed items (from RT's fork Pick Geometry)
This commit is contained in:
Kacper Donat
2025-09-07 17:27:18 +02:00
committed by GitHub
22 changed files with 1092 additions and 45 deletions

View File

@@ -77,7 +77,7 @@ class ExportImportTest(unittest.TestCase):
sa.apply(feature.ViewObject.RootNode)
paths = sa.getPaths()
bind = paths.get(2).getTail()
bind = paths.get(1).getTail()
self.assertEqual(bind.value.getValue(), bind.PER_PART)
sa = coin.SoSearchAction()
@@ -87,5 +87,5 @@ class ExportImportTest(unittest.TestCase):
sa.apply(feature.ViewObject.RootNode)
paths = sa.getPaths()
mat = paths.get(2).getTail()
mat = paths.get(1).getTail()
self.assertEqual(mat.diffuseColor.getNum(), 6)

View File

@@ -44,10 +44,18 @@
# include <Inventor/elements/SoLineWidthElement.h>
# include <Inventor/errors/SoDebugError.h>
# include <Inventor/misc/SoState.h>
# include <Inventor/nodes/SoGroup.h>
# include <Inventor/actions/SoSearchAction.h>
#endif
#include <Gui/Selection/SoFCUnifiedSelection.h>
#include <Gui/Selection/Selection.h>
#include <Base/Console.h>
#include "SoBrepEdgeSet.h"
#include "SoBrepFaceSet.h"
#include "ViewProviderExt.h"
#include <Gui/Inventor/So3DAnnotation.h>
using namespace PartGui;
@@ -79,6 +87,26 @@ void SoBrepEdgeSet::GLRender(SoGLRenderAction *action)
SelContextPtr ctx = Gui::SoFCSelectionRoot::getRenderContext<SelContext>(this,selContext,ctx2);
if(ctx2 && ctx2->selectionIndex.empty())
return;
bool hasContextHighlight = ctx && !ctx->hl.empty();
bool hasFaceHighlight = viewProvider && viewProvider->isFaceHighlightActive();
bool hasAnyHighlight = hasContextHighlight || hasFaceHighlight;
if (Gui::Selection().isClarifySelectionActive()
&& !Gui::SoDelayedAnnotationsElement::isProcessingDelayedPaths
&& hasAnyHighlight) {
// if we are using clarifyselection - add this to delayed paths with priority
// as we want to get this rendered on top of everything
if (viewProvider) {
viewProvider->setFaceHighlightActive(true);
}
Gui::SoDelayedAnnotationsElement::addDelayedPath(action->getState(),
action->getCurPath()->copy(),
200);
return;
}
if(selContext2->checkGlobal(ctx)) {
if(selContext2->isSelectAll()) {
selContext2->sl.clear();
@@ -132,9 +160,22 @@ void SoBrepEdgeSet::GLRender(SoGLRenderAction *action)
}
if(ctx2 && !ctx2->selectionIndex.empty())
renderSelection(action,ctx2,false);
else
else if (Gui::Selection().isClarifySelectionActive()
&& !Gui::SoDelayedAnnotationsElement::isProcessingDelayedPaths && hasAnyHighlight) {
state->push();
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDepthMask(false);
glDisable(GL_DEPTH_TEST);
inherited::GLRender(action);
state->pop();
}
else {
inherited::GLRender(action);
}
// Workaround for #0000433
//#if !defined(FC_OS_WIN32)
if(!action->isRenderingDelayedPaths())

View File

@@ -36,6 +36,8 @@ class SoTextureCoordinateBundle;
namespace PartGui {
class ViewProviderPartExt;
class PartGuiExport SoBrepEdgeSet : public SoIndexedLineSet {
using inherited = SoIndexedLineSet;
@@ -44,6 +46,8 @@ class PartGuiExport SoBrepEdgeSet : public SoIndexedLineSet {
public:
static void initClass();
SoBrepEdgeSet();
void setViewProvider(ViewProviderPartExt* vp) { viewProvider = vp; }
protected:
~SoBrepEdgeSet() override = default;
@@ -68,11 +72,15 @@ private:
void renderSelection(SoGLRenderAction *action, SelContextPtr, bool push=true);
bool validIndexes(const SoCoordinateElement*, const std::vector<int32_t>&) const;
private:
SelContextPtr selContext;
SelContextPtr selContext2;
Gui::SoFCSelectionCounter selCounter;
uint32_t packedColor{0};
// backreference to viewprovider that owns this node
ViewProviderPartExt* viewProvider = nullptr;
};
} // namespace PartGui

View File

@@ -73,8 +73,12 @@
#include <Gui/SoFCInteractiveElement.h>
#include <Gui/Selection/SoFCSelectionAction.h>
#include <Gui/Selection/SoFCUnifiedSelection.h>
#include <Gui/Inventor/So3DAnnotation.h>
#include "SoBrepFaceSet.h"
#include "ViewProviderExt.h"
#include "SoBrepEdgeSet.h"
using namespace PartGui;
@@ -188,6 +192,9 @@ void SoBrepFaceSet::doAction(SoAction* action)
ctx->highlightIndex = -1;
touch();
}
if (viewProvider) {
viewProvider->setFaceHighlightActive(false);
}
return;
}
@@ -204,6 +211,9 @@ void SoBrepFaceSet::doAction(SoAction* action)
ctx->highlightIndex = -1;
touch();
}
if (viewProvider) {
viewProvider->setFaceHighlightActive(false);
}
}else {
int index = static_cast<const SoFaceDetail*>(detail)->getPartIndex();
SelContextPtr ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext);
@@ -521,6 +531,41 @@ void SoBrepFaceSet::GLRender(SoGLRenderAction *action)
auto state = action->getState();
selCounter.checkRenderCache(state);
bool hasContextHighlight = ctx && ctx->isHighlighted() && !ctx->isHighlightAll()
&& ctx->highlightIndex >= 0 && ctx->highlightIndex < partIndex.getNum();
// for the tool add this node to delayed paths as we want to render it on top of the scene
if (Gui::Selection().isClarifySelectionActive() && hasContextHighlight) {
if (!Gui::SoDelayedAnnotationsElement::isProcessingDelayedPaths) {
if (viewProvider) {
viewProvider->setFaceHighlightActive(true);
}
const SoPath* currentPath = action->getCurPath();
Gui::SoDelayedAnnotationsElement::addDelayedPath(action->getState(),
currentPath->copy(),
100);
return;
} else {
// during priority delayed paths processing:
// render base faces normally first, then render highlight on top
inherited::GLRender(action);
state->push();
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDepthMask(false);
glDisable(GL_DEPTH_TEST);
renderHighlight(action, ctx);
state->pop();
return;
}
}
// override material binding to PER_PART_INDEX to achieve
// preselection/selection with transparency
@@ -730,7 +775,7 @@ bool SoBrepFaceSet::overrideMaterialBinding(SoGLRenderAction *action, SelContext
singleColor = ctx?-1:1;
}
bool partialRender = ctx2 && !ctx2->isSelectAll();
bool partialRender = (ctx2 && !ctx2->isSelectAll());
if(singleColor>0 && !partialRender) {
//optimization for single color non-partial rendering
@@ -772,7 +817,8 @@ bool SoBrepFaceSet::overrideMaterialBinding(SoGLRenderAction *action, SelContext
packedColors.push_back(ctx->highlightColor.getPackedValue(trans0));
matIndex[ctx->highlightIndex] = packedColors.size()-1;
}
}else{
}
else{
if(partialRender) {
packedColors.push_back(SbColor(1.0,1.0,1.0).getPackedValue(1.0));
matIndex.resize(partIndex.getNum(),0);
@@ -848,7 +894,7 @@ bool SoBrepFaceSet::overrideMaterialBinding(SoGLRenderAction *action, SelContext
SoLazyElement::setPacked(state, this, packedColors.size(), packedColors.data(), hasTransparency);
SoTextureEnabledElement::set(state,this,false);
if(hasTransparency && action->isRenderingDelayedPaths()) {
if (hasTransparency && action->isRenderingDelayedPaths()) {
// rendering delayed paths means we are doing annotation (e.g.
// always on top rendering). To render transparency correctly in
// this case, we shall use openGL transparency blend. Override

View File

@@ -38,6 +38,8 @@ class SoTextureCoordinateBundle;
namespace PartGui {
class ViewProviderPartExt;
/**
* First some words to the history and the reason why we have this class:
* In older FreeCAD versions we had an own Inventor node for each sub-element of a shape with its own highlight node.
@@ -79,6 +81,8 @@ class PartGuiExport SoBrepFaceSet : public SoIndexedFaceSet {
public:
static void initClass();
SoBrepFaceSet();
void setViewProvider(ViewProviderPartExt* vp) { viewProvider = vp; }
SoMFInt32 partIndex;
@@ -154,6 +158,9 @@ private:
// Define some VBO pointer for the current mesh
class VBO;
std::unique_ptr<VBO> pimpl;
// backreference to viewprovider that owns this node
ViewProviderPartExt* viewProvider = nullptr;
};
} // namespace PartGui

View File

@@ -44,7 +44,9 @@
#endif
#include <Gui/Selection/SoFCUnifiedSelection.h>
#include <Gui/Inventor/So3DAnnotation.h>
#include "ViewProviderExt.h"
#include "SoBrepPointSet.h"
@@ -81,6 +83,22 @@ void SoBrepPointSet::GLRender(SoGLRenderAction *action)
return;
if(selContext2->checkGlobal(ctx))
ctx = selContext2;
bool hasContextHighlight =
ctx && ctx->isHighlighted() && !ctx->isHighlightAll() && ctx->highlightIndex >= 0;
// for clarifyselection, add this node to delayed path if it is highlighted and render it on
// top of everything else (highest priority)
if (Gui::Selection().isClarifySelectionActive() && hasContextHighlight
&& !Gui::SoDelayedAnnotationsElement::isProcessingDelayedPaths) {
if (viewProvider) {
viewProvider->setFaceHighlightActive(true);
}
Gui::SoDelayedAnnotationsElement::addDelayedPath(action->getState(),
action->getCurPath()->copy(),
300);
return;
}
if(ctx && ctx->highlightIndex == std::numeric_limits<int>::max()) {
if(ctx->selectionIndex.empty() || ctx->isSelectAll()) {
@@ -121,8 +139,15 @@ void SoBrepPointSet::GLRender(SoGLRenderAction *action)
}
if(ctx2 && !ctx2->selectionIndex.empty())
renderSelection(action,ctx2,false);
else
else if (Gui::SoDelayedAnnotationsElement::isProcessingDelayedPaths) {
glPushAttrib(GL_DEPTH_BUFFER_BIT);
glDepthFunc(GL_ALWAYS);
inherited::GLRender(action);
glPopAttrib();
}
else {
inherited::GLRender(action);
}
// Workaround for #0000433
//#if !defined(FC_OS_WIN32)

View File

@@ -36,6 +36,8 @@ class SoTextureCoordinateBundle;
namespace PartGui {
class ViewProviderPartExt;
class PartGuiExport SoBrepPointSet : public SoPointSet {
using inherited = SoPointSet;
@@ -44,6 +46,8 @@ class PartGuiExport SoBrepPointSet : public SoPointSet {
public:
static void initClass();
SoBrepPointSet();
void setViewProvider(ViewProviderPartExt* vp) { viewProvider = vp; }
protected:
~SoBrepPointSet() override = default;
@@ -64,6 +68,9 @@ private:
SelContextPtr selContext2;
Gui::SoFCSelectionCounter selCounter;
uint32_t packedColor{0};
// backreference to viewprovider that owns this node
ViewProviderPartExt* viewProvider = nullptr;
};
} // namespace PartGui

View File

@@ -191,6 +191,7 @@ ViewProviderPartExt::ViewProviderPartExt()
coords = new SoCoordinate3();
coords->ref();
faceset = new SoBrepFaceSet();
faceset->setViewProvider(this);
faceset->ref();
norm = new SoNormal;
norm->ref();
@@ -198,8 +199,10 @@ ViewProviderPartExt::ViewProviderPartExt()
normb->value = SoNormalBinding::PER_VERTEX_INDEXED;
normb->ref();
lineset = new SoBrepEdgeSet();
lineset->setViewProvider(this);
lineset->ref();
nodeset = new SoBrepPointSet();
nodeset->setViewProvider(this);
nodeset->ref();
pcFaceBind = new SoMaterialBinding();
@@ -447,9 +450,9 @@ void ViewProviderPartExt::attach(App::DocumentObject *pcFeat)
// normal viewing with edges and points
pcNormalRoot->addChild(pcPointsRoot);
pcNormalRoot->addChild(wireframe);
pcNormalRoot->addChild(offset);
pcNormalRoot->addChild(pcFlatRoot);
pcNormalRoot->addChild(wireframe);
// just faces with no edges or points
pcFlatRoot->addChild(pShapeHints);

View File

@@ -156,6 +156,9 @@ public:
bool allowOverride(const App::DocumentObject &) const override;
void setFaceHighlightActive(bool active) { faceHighlightActive = active; }
bool isFaceHighlightActive() const { return faceHighlightActive; }
/** @name Edit methods */
//@{
void setupContextMenu(QMenu*, QObject*, const char*) override;
@@ -213,6 +216,7 @@ protected:
bool VisualTouched;
bool NormalsFromUV;
bool faceHighlightActive = false;
private:
Gui::ViewProviderFaceTexture texture;

View File

@@ -49,7 +49,7 @@ class ColorPerFaceTest(unittest.TestCase):
sa.apply(box.ViewObject.RootNode)
paths = sa.getPaths()
mat = paths.get(2).getTail()
mat = paths.get(1).getTail()
self.assertEqual(mat.diffuseColor.getNum(), 6)
def testBoxAndLink(self):
@@ -83,7 +83,7 @@ class ColorPerFaceTest(unittest.TestCase):
sa.apply(box.ViewObject.RootNode)
paths = sa.getPaths()
mat = paths.get(2).getTail()
mat = paths.get(1).getTail()
self.assertEqual(mat.diffuseColor.getNum(), 6)
def testTransparency(self):
@@ -110,7 +110,7 @@ class ColorPerFaceTest(unittest.TestCase):
sa.apply(box.ViewObject.RootNode)
paths = sa.getPaths()
bind = paths.get(2).getTail()
bind = paths.get(1).getTail()
self.assertEqual(bind.value.getValue(), bind.PER_PART)
sa = coin.SoSearchAction()
@@ -120,7 +120,7 @@ class ColorPerFaceTest(unittest.TestCase):
sa.apply(box.ViewObject.RootNode)
paths = sa.getPaths()
mat = paths.get(2).getTail()
mat = paths.get(1).getTail()
self.assertEqual(mat.diffuseColor.getNum(), 6)
def testMultiFuse(self):
@@ -146,7 +146,7 @@ class ColorPerFaceTest(unittest.TestCase):
sa.apply(fuse.ViewObject.RootNode)
paths = sa.getPaths()
bind = paths.get(2).getTail()
bind = paths.get(1).getTail()
self.assertEqual(bind.value.getValue(), bind.PER_PART)
sa = coin.SoSearchAction()
@@ -156,7 +156,7 @@ class ColorPerFaceTest(unittest.TestCase):
sa.apply(fuse.ViewObject.RootNode)
paths = sa.getPaths()
mat = paths.get(2).getTail()
mat = paths.get(1).getTail()
self.assertEqual(mat.diffuseColor.getNum(), 11)
self.assertEqual(len(fuse.Shape.Faces), 11)
@@ -195,7 +195,7 @@ class ColorPerFaceTest(unittest.TestCase):
sa.apply(fuse.ViewObject.RootNode)
paths = sa.getPaths()
bind = paths.get(2).getTail()
bind = paths.get(1).getTail()
self.assertEqual(bind.value.getValue(), bind.PER_PART)
sa = coin.SoSearchAction()
@@ -205,5 +205,5 @@ class ColorPerFaceTest(unittest.TestCase):
sa.apply(fuse.ViewObject.RootNode)
paths = sa.getPaths()
mat = paths.get(2).getTail()
mat = paths.get(1).getTail()
self.assertEqual(mat.diffuseColor.getNum(), 11)