Gui: add support of selection context

The patch implements context-aware selection and rendering in 3D view.

Please check [here](https://git.io/fjiY5) for more details, including
the following 'Render Caching' section.

The patch also includes modification of View3DInventorViewer to support
always-on-top selection rendering using the secondary selection context
and the new coin node SoFCPathAnnotation.

Another small change in SoQtQuarterAdaptor for more responsive frame
rate display. The original implementation reports skewed frame rate
in the presence of long idle period.
This commit is contained in:
Zheng, Lei
2019-07-07 16:08:38 +08:00
committed by wmayer
parent c88e1335d8
commit c9ba972d26
25 changed files with 3500 additions and 957 deletions

View File

@@ -55,6 +55,9 @@
# include <Inventor/details/SoLineDetail.h>
# include <Inventor/misc/SoState.h>
# include <Inventor/misc/SoContextHandler.h>
# include <Inventor/elements/SoShapeStyleElement.h>
# include <Inventor/elements/SoCacheElement.h>
# include <Inventor/elements/SoTextureEnabledElement.h>
# ifdef FC_OS_WIN32
# include <windows.h>
# include <GL/gl.h>
@@ -72,6 +75,7 @@
# include <Inventor/C/glue/gl.h>
#endif
#include <boost/algorithm/string/predicate.hpp>
#include "SoBrepFaceSet.h"
#include <Gui/SoFCUnifiedSelection.h>
#include <Gui/SoFCSelectionAction.h>
@@ -79,7 +83,6 @@
using namespace PartGui;
SO_NODE_SOURCE(SoBrepFaceSet);
#define PRIVATE(p) ((p)->pimpl)
@@ -173,9 +176,9 @@ SoBrepFaceSet::SoBrepFaceSet()
{
SO_NODE_CONSTRUCTOR(SoBrepFaceSet);
SO_NODE_ADD_FIELD(partIndex, (-1));
SO_NODE_ADD_FIELD(highlightIndex, (-1));
SO_NODE_ADD_FIELD(selectionIndex, (-1));
selectionIndex.setNum(0);
selContext = std::make_shared<SelContext>();
selContext2 = std::make_shared<SelContext>();
pimpl.reset(new VBO);
}
@@ -188,71 +191,124 @@ void SoBrepFaceSet::doAction(SoAction* action)
{
if (action->getTypeId() == Gui::SoHighlightElementAction::getClassTypeId()) {
Gui::SoHighlightElementAction* hlaction = static_cast<Gui::SoHighlightElementAction*>(action);
selCounter.checkAction(hlaction);
if (!hlaction->isHighlighted()) {
this->highlightIndex = -1;
SelContextPtr ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext,false);
if(ctx) {
ctx->highlightIndex = -1;
touch();
}
return;
}
const SoDetail* detail = hlaction->getElement();
if (detail) {
if (detail->isOfType(SoFaceDetail::getClassTypeId())) {
if (!detail) {
SelContextPtr ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext);
ctx->highlightIndex = INT_MAX;
ctx->highlightColor = hlaction->getColor();
touch();
}else {
if (!detail->isOfType(SoFaceDetail::getClassTypeId())) {
SelContextPtr ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext,false);
if(ctx) {
ctx->highlightIndex = -1;
touch();
}
}else {
int index = static_cast<const SoFaceDetail*>(detail)->getPartIndex();
this->highlightIndex.setValue(index);
this->highlightColor = hlaction->getColor();
}
else {
this->highlightIndex = -1;
return;
SelContextPtr ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext);
ctx->highlightIndex = index;
ctx->highlightColor = hlaction->getColor();
touch();
}
}
return;
}
else if (action->getTypeId() == Gui::SoSelectionElementAction::getClassTypeId()) {
Gui::SoSelectionElementAction* selaction = static_cast<Gui::SoSelectionElementAction*>(action);
this->selectionColor = selaction->getColor();
if (selaction->getType() == Gui::SoSelectionElementAction::All) {
//int num = this->partIndex.getNum();
//this->selectionIndex.setNum(num);
//int32_t* v = this->selectionIndex.startEditing();
//for (int i=0; i<num;i++)
// v[i] = i;
//this->selectionIndex.finishEditing();
this->selectionIndex.setValue(-1); // all
PRIVATE(this)->updateVbo = true;
switch(selaction->getType()) {
case Gui::SoSelectionElementAction::All: {
SelContextPtr ctx = Gui::SoFCSelectionRoot::getActionContext<SelContext>(action,this,selContext);
selCounter.checkAction(selaction,ctx);
ctx->selectionColor = selaction->getColor();
ctx->selectionIndex.clear();
ctx->selectionIndex.insert(-1);
touch();
return;
}
else if (selaction->getType() == Gui::SoSelectionElementAction::None) {
this->selectionIndex.setNum(0);
PRIVATE(this)->updateVbo = true;
} case Gui::SoSelectionElementAction::None:
if(selaction->isSecondary()) {
if(Gui::SoFCSelectionRoot::removeActionContext(action,this))
touch();
}else {
SelContextPtr ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext,false);
if(ctx) {
ctx->selectionIndex.clear();
ctx->colors.clear();
touch();
}
}
return;
}
const SoDetail* detail = selaction->getElement();
if (detail) {
if (!detail->isOfType(SoFaceDetail::getClassTypeId())) {
case Gui::SoSelectionElementAction::Color:
if(selaction->isSecondary()) {
const auto &colors = selaction->getColors();
auto ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext,false);
if(colors.empty()) {
if(ctx) {
ctx->colors.clear();
if(ctx->isSelectAll())
Gui::SoFCSelectionRoot::removeActionContext(action,this);
touch();
}
return;
}
static std::string element("Face");
if(colors.begin()->first.empty() || colors.lower_bound(element)!=colors.end()) {
if(!ctx) {
ctx = Gui::SoFCSelectionRoot::getActionContext<SelContext>(action,this);
selCounter.checkAction(selaction,ctx);
ctx->selectAll();
}
if(ctx->setColors(selaction->getColors(),element))
touch();
}
}
return;
case Gui::SoSelectionElementAction::Remove:
case Gui::SoSelectionElementAction::Append: {
const SoDetail* detail = selaction->getElement();
if (!detail || !detail->isOfType(SoFaceDetail::getClassTypeId())) {
if(selaction->isSecondary()) {
// For secondary context, a detail of different type means
// the user may want to partial render only other type of
// geometry. So we call below to obtain a action context.
// If no secondary context exist, it will create an empty
// one, and an empty secondary context inhibites drawing
// here.
auto ctx = Gui::SoFCSelectionRoot::getActionContext<SelContext>(action,this);
selCounter.checkAction(selaction,ctx);
touch();
}
return;
}
int index = static_cast<const SoFaceDetail*>(detail)->getPartIndex();
switch (selaction->getType()) {
case Gui::SoSelectionElementAction::Append:
{
if (this->selectionIndex.find(index) < 0) {
int start = this->selectionIndex.getNum();
this->selectionIndex.set1Value(start, index);
}
}
break;
case Gui::SoSelectionElementAction::Remove:
{
int start = this->selectionIndex.find(index);
if (start >= 0)
this->selectionIndex.deleteValues(start,1);
}
break;
default:
break;
if (selaction->getType() == Gui::SoSelectionElementAction::Append) {
auto ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext);
selCounter.checkAction(selaction,ctx);
ctx->selectionColor = selaction->getColor();
if(ctx->isSelectAll())
ctx->selectionIndex.clear();
if(ctx->selectionIndex.insert(index).second)
touch();
}else{
auto ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext,false);
if(ctx && ctx->removeIndex(index))
touch();
}
break;
} default:
break;
}
return;
}
else if (action->getTypeId() == Gui::SoVRMLAction::getClassTypeId()) {
// update the materialIndex field to match with the number of triangles if needed
@@ -298,6 +354,8 @@ void SoBrepFaceSet::doAction(SoAction* action)
void SoBrepFaceSet::GLRender(SoGLRenderAction *action)
{
SoState * state = action->getState();
// Disable caching for this node
SoGLCacheContextElement::shouldAutoCache(state, SoGLCacheContextElement::DONT_AUTO_CACHE);
SoMaterialBundle mb(action);
Binding mbind = this->findMaterialBinding(state);
@@ -305,79 +363,92 @@ void SoBrepFaceSet::GLRender(SoGLRenderAction *action)
SoTextureCoordinateBundle tb(action, true, false);
SbBool doTextures = tb.needCoordinates();
int32_t hl_idx = this->highlightIndex.getValue();
int32_t num_selected = this->selectionIndex.getNum();
if (this->coordIndex.getNum() < 3)
if (ctx->coordIndex.getNum() < 3)
return;
if (num_selected > 0)
renderSelection(action);
if (hl_idx >= 0)
renderHighlight(action);
// When setting transparency shouldGLRender() handles the rendering and returns false.
// Therefore generatePrimitives() needs to be re-implemented to handle the materials
// correctly.
if (!this->shouldGLRender(action))
SelContextPtr ctx2;
SelContextPtr ctx = Gui::SoFCSelectionRoot::getRenderContext<SelContext>(this,selContext,ctx2);
if(ctx2 && ctx2->selectionIndex.empty())
return;
int32_t hl_idx = ctx?ctx->highlightIndex:-1;
int32_t num_selected = ctx?ctx->selectionIndex.size():0;
renderHighlight(action,ctx);
if(ctx && ctx->selectionIndex.size()) {
if(ctx->isSelectAll()) {
if(ctx2 && ctx2->selectionIndex.size()) {
ctx2->selectionColor = ctx->selectionColor;
renderSelection(action,ctx2);
} else
renderSelection(action,ctx);
return;
}
renderSelection(action,ctx);
}
if(ctx2 && ctx2->selectionIndex.size()) {
renderSelection(action,ctx2,false);
}else{
// When setting transparency shouldGLRender() handles the rendering and returns false.
// Therefore generatePrimitives() needs to be re-implemented to handle the materials
// correctly.
if (!this->shouldGLRender(action))
return;
#ifdef RENDER_GLARRAYS
if (!doTextures && index_array.size() && hl_idx < 0 && num_selected <= 0) {
if (mbind == 0) {
mb.sendFirst(); // only one material -> apply it!
renderSimpleArray();
return;
if (!doTextures && index_array.size() && hl_idx < 0 && num_selected <= 0) {
if (mbind == 0) {
mb.sendFirst(); // only one material -> apply it!
renderSimpleArray();
return;
}
else if (mbind == 1) {
renderColoredArray(&mb);
return;
}
}
else if (mbind == 1) {
renderColoredArray(&mb);
return;
}
}
#endif
Binding nbind = this->findNormalBinding(state);
Binding nbind = this->findNormalBinding(state);
const SoCoordinateElement * coords;
const SbVec3f * normals;
const int32_t * cindices;
int numindices;
const int32_t * nindices;
const int32_t * tindices;
const int32_t * mindices;
const int32_t * pindices;
int numparts;
SbBool normalCacheUsed;
const SoCoordinateElement * coords;
const SbVec3f * normals;
const int32_t * cindices;
int numindices;
const int32_t * nindices;
const int32_t * tindices;
const int32_t * mindices;
const int32_t * pindices;
int numparts;
SbBool normalCacheUsed;
SbBool sendNormals = !mb.isColorOnly() || tb.isFunction();
SbBool sendNormals = !mb.isColorOnly() || tb.isFunction();
this->getVertexData(state, coords, normals, cindices,
nindices, tindices, mindices, numindices,
sendNormals, normalCacheUsed);
this->getVertexData(state, coords, normals, cindices,
nindices, tindices, mindices, numindices,
sendNormals, normalCacheUsed);
mb.sendFirst(); // make sure we have the correct material
mb.sendFirst(); // make sure we have the correct material
// just in case someone forgot
if (!mindices) mindices = cindices;
if (!nindices) nindices = cindices;
pindices = this->partIndex.getValues(0);
numparts = this->partIndex.getNum();
// just in case someone forgot
if (!mindices) mindices = cindices;
if (!nindices) nindices = cindices;
pindices = this->partIndex.getValues(0);
numparts = this->partIndex.getNum();
renderShape(state, vboAvailable, static_cast<const SoGLCoordinateElement*>(coords), cindices, numindices,
pindices, numparts, normals, nindices, &mb, mindices, &tb, tindices, nbind, mbind, doTextures?1:0);
renderShape(state, vboAvailable, static_cast<const SoGLCoordinateElement*>(coords), cindices, numindices,
pindices, numparts, normals, nindices, &mb, mindices, &tb, tindices, nbind, mbind, doTextures?1:0);
// Disable caching for this node
SoGLCacheContextElement::shouldAutoCache(state, SoGLCacheContextElement::DONT_AUTO_CACHE);
if(normalCacheUsed)
this->readUnlockNormalCache();
}
// Workaround for #0000433
//#if !defined(FC_OS_WIN32)
if (hl_idx >= 0)
renderHighlight(action);
if (num_selected > 0)
renderSelection(action);
renderHighlight(action,ctx);
renderSelection(action,ctx);
//#endif
if(normalCacheUsed)
this->readUnlockNormalCache();
}
//****************************************************************************
@@ -441,6 +512,7 @@ void SoBrepFaceSet::renderColoredArray(SoMaterialBundle *const materials)
glDisableClientState(GL_NORMAL_ARRAY);
}
#else
void SoBrepFaceSet::GLRender(SoGLRenderAction *action)
{
//SoBase::staticDataLock();
@@ -454,85 +526,367 @@ void SoBrepFaceSet::GLRender(SoGLRenderAction *action)
if (this->coordIndex.getNum() < 3)
return;
if (this->selectionIndex.getNum() > 0)
renderSelection(action);
if (this->highlightIndex.getValue() >= 0)
renderHighlight(action);
SelContextPtr ctx2;
std::vector<SelContextPtr> ctxs;
SelContextPtr ctx = Gui::SoFCSelectionRoot::getRenderContext(this,selContext,ctx2);
if(ctx2 && ctx2->selectionIndex.empty())
return;
if(selContext2->checkGlobal(ctx))
ctx = selContext2;
if(ctx && (!ctx->selectionIndex.size() && ctx->highlightIndex<0))
ctx.reset();
auto state = action->getState();
selCounter.checkRenderCache(state);
// override material binding to PER_PART_INDEX to achieve
// preselection/selection with transparency
bool pushed = overrideMaterialBinding(action,ctx,ctx2);
if(!pushed){
// for non transparent cases, we still use the old selection rendering
// code, because it can override emission color, which gives a more
// distinguishable selection highlight. The above material binding
// override method can't, because Coin does not support per part
// emission color
// There are a few factors affects the rendering order.
//
// 1) For normal case, the highlight (pre-selection) is the top layer. And since
// the depth buffer clipping is on here, we shall draw highlight first, then
// selection, then the rest part.
//
// 2) If action->isRenderingDelayedPaths() is true, it means we are rendering
// with depth buffer clipping turned off (always on top rendering), so we shall
// draw the top layer last, i.e. renderHighlight() last
//
// 3) If highlightIndex==INT_MAX, it means we are rendering full object highlight
// In order to not obscure selection layer, we shall draw highlight after selection
// if and only if it is not a full object selection.
//
// Transparency complicates stuff even more, but not here. It will be handled inside
// overrideMaterialBinding()
//
if(ctx && ctx->highlightIndex==INT_MAX) {
if(ctx->selectionIndex.empty() || ctx->isSelectAll()) {
if(ctx2) {
ctx2->selectionColor = ctx->highlightColor;
renderSelection(action,ctx2);
} else
renderHighlight(action,ctx);
}else{
if(!action->isRenderingDelayedPaths())
renderSelection(action,ctx);
if(ctx2) {
ctx2->selectionColor = ctx->highlightColor;
renderSelection(action,ctx2);
} else
renderHighlight(action,ctx);
if(action->isRenderingDelayedPaths())
renderSelection(action,ctx);
}
return;
}
if(!action->isRenderingDelayedPaths())
renderHighlight(action,ctx);
if(ctx && ctx->selectionIndex.size()) {
if(ctx->isSelectAll()) {
if(ctx2) {
ctx2->selectionColor = ctx->selectionColor;
renderSelection(action,ctx2);
} else
renderSelection(action,ctx);
if(action->isRenderingDelayedPaths())
renderHighlight(action,ctx);
return;
}
if(!action->isRenderingDelayedPaths())
renderSelection(action,ctx);
}
if(ctx2) {
renderSelection(action,ctx2,false);
if(action->isRenderingDelayedPaths()) {
renderSelection(action,ctx);
renderHighlight(action,ctx);
}
return;
}
}
SoMaterialBundle mb(action);
// It is important to send material before shouldGLRender(), otherwise
// material override with transparncy won't work.
mb.sendFirst();
// When setting transparency shouldGLRender() handles the rendering and returns false.
// Therefore generatePrimitives() needs to be re-implemented to handle the materials
// correctly.
if (!this->shouldGLRender(action))
return;
if(this->shouldGLRender(action)) {
Binding mbind = this->findMaterialBinding(state);
Binding nbind = this->findNormalBinding(state);
SbBool hasVBO = PRIVATE(this)->vboAvailable;
SoState * state = action->getState();
if (hasVBO) {
// get the VBO status of the viewer
Gui::SoGLVBOActivatedElement::get(state, hasVBO);
}
const SoCoordinateElement * coords;
const SbVec3f * normals;
const int32_t * cindices;
int numindices;
const int32_t * nindices;
const int32_t * tindices;
const int32_t * mindices;
const int32_t * pindices;
int numparts;
SbBool doTextures;
SbBool normalCacheUsed;
Binding mbind = this->findMaterialBinding(state);
Binding nbind = this->findNormalBinding(state);
SoTextureCoordinateBundle tb(action, true, false);
doTextures = tb.needCoordinates();
SbBool sendNormals = !mb.isColorOnly() || tb.isFunction();
const SoCoordinateElement * coords;
const SbVec3f * normals;
const int32_t * cindices;
int numindices;
const int32_t * nindices;
const int32_t * tindices;
const int32_t * mindices;
const int32_t * pindices;
int numparts;
SbBool doTextures;
SbBool normalCacheUsed;
this->getVertexData(state, coords, normals, cindices,
nindices, tindices, mindices, numindices,
sendNormals, normalCacheUsed);
SoMaterialBundle mb(action);
// just in case someone forgot
if (!mindices) mindices = cindices;
if (!nindices) nindices = cindices;
pindices = this->partIndex.getValues(0);
numparts = this->partIndex.getNum();
SoTextureCoordinateBundle tb(action, true, false);
doTextures = tb.needCoordinates();
SbBool sendNormals = !mb.isColorOnly() || tb.isFunction();
SbBool hasVBO = !ctx2 && PRIVATE(this)->vboAvailable;
if (hasVBO) {
// get the VBO status of the viewer
Gui::SoGLVBOActivatedElement::get(state, hasVBO);
//
//if (SoGLVBOElement::shouldCreateVBO(state, numindices)) {
// this->startVertexArray(action, coords, normals, false, false);
//}
}
renderShape(action, hasVBO, static_cast<const SoGLCoordinateElement*>(coords), cindices, numindices,
pindices, numparts, normals, nindices, &mb, mindices, &tb, tindices, nbind, mbind, doTextures?1:0);
this->getVertexData(state, coords, normals, cindices,
nindices, tindices, mindices, numindices,
sendNormals, normalCacheUsed);
mb.sendFirst(); // make sure we have the correct material
// just in case someone forgot
if (!mindices) mindices = cindices;
if (!nindices) nindices = cindices;
pindices = this->partIndex.getValues(0);
numparts = this->partIndex.getNum();
if (hasVBO) {
// get the VBO status of the viewer
Gui::SoGLVBOActivatedElement::get(state, hasVBO);
//
//if (SoGLVBOElement::shouldCreateVBO(state, numindices)) {
// this->startVertexArray(action, coords, normals, false, false);
//}
}
renderShape(action, hasVBO, static_cast<const SoGLCoordinateElement*>(coords), cindices, numindices,
pindices, numparts, normals, nindices, &mb, mindices, &tb, tindices, nbind, mbind, doTextures?1:0);
if (!hasVBO) {
// Disable caching for this node
SoGLCacheContextElement::shouldAutoCache(state, SoGLCacheContextElement::DONT_AUTO_CACHE);
// if (!hasVBO) {
// // Disable caching for this node
// SoGLCacheContextElement::shouldAutoCache(state, SoGLCacheContextElement::DONT_AUTO_CACHE);
// }else
// SoGLCacheContextElement::setAutoCacheBits(state, SoGLCacheContextElement::DO_AUTO_CACHE);
if (normalCacheUsed)
this->readUnlockNormalCache();
}
// Workaround for #0000433
//#if !defined(FC_OS_WIN32)
if (this->highlightIndex.getValue() >= 0)
renderHighlight(action);
if (this->selectionIndex.getNum() > 0)
renderSelection(action);
//#endif
if(pushed) {
SbBool notify = enableNotify(FALSE);
materialIndex.setNum(0);
if(notify) enableNotify(notify);
state->pop();
}else if(action->isRenderingDelayedPaths()) {
renderSelection(action,ctx);
renderHighlight(action,ctx);
}
}
#endif
bool SoBrepFaceSet::overrideMaterialBinding(SoGLRenderAction *action, SelContextPtr ctx, SelContextPtr ctx2) {
if(!ctx && !ctx2) return false;
auto state = action->getState();
auto mb = SoMaterialBindingElement::get(state);
auto element = SoLazyElement::getInstance(state);
const SbColor *diffuse = element->getDiffusePointer();
if(!diffuse) return false;
int diffuse_size = element->getNumDiffuse();
const float *trans = element->getTransparencyPointer();
int trans_size = element->getNumTransparencies();
if(!trans || !trans_size) return false;
float trans0=0.0;
bool hasTransparency = false;
for(int i=0;i<trans_size;++i) {
if(trans[i]!=0.0) {
hasTransparency = true;
trans0 = trans[i]>0.5?0.5:trans[i];
break;
}
}
// Override material binding to PER_PART_INDEXED so that we can reuse coin
// rendering for both selection, preselection and partial rendering. The
// main purpose is such that selection and preselection can have correct
// transparency, too.
//
// Criteria of using material binding override:
// 1) original material binding is either overall or per_part. We can
// support others, but ommitted here to simplify coding logic, and
// because it seems FC only uses these two.
// 2) either of the following :
// a) has highlight or selection and Selection().needPickPoint, so that
// any preselected/selected part automatically become transparent
// b) has transparency
// c) has color override in secondary context
if((mb==SoMaterialBindingElement::OVERALL ||
(mb==SoMaterialBindingElement::PER_PART && diffuse_size>=partIndex.getNum()))
&&
((ctx && Gui::Selection().needPickedList()) ||
trans0!=0.0 ||
(ctx2 && ctx2->colors.size())))
{
state->push();
packedColors.clear();
if(ctx && Gui::Selection().needPickedList()) {
hasTransparency = true;
if(trans0 < 0.5)
trans0=0.5;
trans_size = 1;
if(ctx2)
ctx2->trans0 = trans0;
}else if(ctx2)
ctx2->trans0 = 0.0;
uint32_t diffuseColor = diffuse[0].getPackedValue(trans0);
int singleColor = 0;
if(ctx && ctx->isHighlightAll()) {
singleColor = 1;
diffuseColor = ctx->highlightColor.getPackedValue(trans0);
}else if(ctx && ctx->isSelectAll()) {
diffuseColor = ctx->selectionColor.getPackedValue(trans0);
singleColor = ctx->isHighlighted()?-1:1;
} else if(ctx2 && ctx2->isSingleColor(diffuseColor,hasTransparency)) {
singleColor = ctx?-1:1;
}
bool partialRender = ctx2 && !ctx2->isSelectAll();
if(singleColor>0 && !partialRender) {
//optimization for single color non-partial rendering
SoMaterialBindingElement::set(state,SoMaterialBindingElement::OVERALL);
SoOverrideElement::setMaterialBindingOverride(state, this, true);
packedColors.push_back(diffuseColor);
SoLazyElement::setPacked(state, this,1, &packedColors[0], hasTransparency);
SoTextureEnabledElement::set(state,this,false);
if(hasTransparency && action->isRenderingDelayedPaths()) {
// rendering delayed paths means we are doing annotation (e.g.
// always on top rendering). To render transparency correctly in
// this case, we shall use openGL transparency blend. Override
// using SoLazyElement::setTransparencyType() doesn't seem to work
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDepthMask(false);
}
return true;
}
matIndex.clear();
matIndex.reserve(partIndex.getNum());
if(ctx && (ctx->isSelectAll() || ctx->isHighlightAll())) {
matIndex.resize(partIndex.getNum(),0);
if(!partialRender)
packedColors.push_back(diffuseColor);
else {
// default to full transparent
packedColors.push_back(SbColor(1.0,1.0,1.0).getPackedValue(1.0));
packedColors.push_back(diffuseColor);
for(auto idx : ctx2->selectionIndex) {
if(idx>=0 && idx<partIndex.getNum())
matIndex[idx] = packedColors.size()-1; // show only the selected
}
}
if(ctx->highlightIndex>=0 && ctx->highlightIndex<partIndex.getNum()) {
packedColors.push_back(ctx->highlightColor.getPackedValue(trans0));
matIndex[ctx->highlightIndex] = packedColors.size()-1;
}
}else{
if(partialRender) {
packedColors.push_back(SbColor(1.0,1.0,1.0).getPackedValue(1.0));
matIndex.resize(partIndex.getNum(),0);
if(mb == SoMaterialBindingElement::OVERALL || singleColor) {
packedColors.push_back(diffuseColor);
auto cidx = packedColors.size()-1;
for(auto idx : ctx2->selectionIndex) {
if(idx>=0 && idx<partIndex.getNum()) {
if(!singleColor && ctx2->applyColor(idx,packedColors,hasTransparency))
matIndex[idx] = packedColors.size()-1;
else
matIndex[idx] = cidx;
}
}
}else{
assert(diffuse_size >= partIndex.getNum());
for(auto idx : ctx2->selectionIndex) {
if(idx>=0 && idx<partIndex.getNum()) {
if(!ctx2->applyColor(idx,packedColors,hasTransparency)) {
auto t = idx<trans_size?trans[idx]:trans0;
packedColors.push_back(diffuse[idx].getPackedValue(t));
}
matIndex[idx] = packedColors.size()-1;
}
}
}
}else if(mb==SoMaterialBindingElement::OVERALL || singleColor) {
packedColors.push_back(diffuseColor);
matIndex.resize(partIndex.getNum(),0);
if(ctx2 && !singleColor) {
for(auto &v : ctx2->colors) {
int idx = v.first;
if(idx>=0 && idx<partIndex.getNum()) {
packedColors.push_back(ctx2->packColor(v.second,hasTransparency));
matIndex[idx] = packedColors.size()-1;
}
}
}
}else{
assert(diffuse_size >= partIndex.getNum());
packedColors.reserve(diffuse_size+3);
for(int i=0;i<diffuse_size;++i) {
auto t = i<trans_size?trans[i]:trans0;
matIndex.push_back(i);
if(!ctx2 || !ctx2->applyColor(i,packedColors,hasTransparency))
packedColors.push_back(diffuse[i].getPackedValue(t));
}
}
if(ctx && ctx->selectionIndex.size()) {
packedColors.push_back(ctx->selectionColor.getPackedValue(trans0));
for(auto idx : ctx->selectionIndex) {
if(idx>=0 && idx<partIndex.getNum())
matIndex[idx] = packedColors.size()-1;
}
}
if(ctx && ctx->highlightIndex>=0 && ctx->highlightIndex<partIndex.getNum()) {
packedColors.push_back(ctx->highlightColor.getPackedValue(trans0));
matIndex[ctx->highlightIndex] = packedColors.size()-1;
}
}
SbBool notify = enableNotify(FALSE);
materialIndex.setValuesPointer(matIndex.size(),&matIndex[0]);
if(notify) enableNotify(notify);
SoMaterialBindingElement::set(state, this, SoMaterialBindingElement::PER_PART_INDEXED);
SoLazyElement::setPacked(state, this, packedColors.size(), &packedColors[0], hasTransparency);
SoTextureEnabledElement::set(state,this,false);
if(hasTransparency && action->isRenderingDelayedPaths()) {
// rendering delayed paths means we are doing annotation (e.g.
// always on top rendering). To render transparency correctly in
// this case, we shall use openGL transparency blend. Override
// using SoLazyElement::setTransparencyType() doesn't seem to work
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDepthMask(false);
}
return true;
}
return false;
}
void SoBrepFaceSet::GLRenderBelowPath(SoGLRenderAction * action)
{
inherited::GLRenderBelowPath(action);
@@ -807,18 +1161,21 @@ void SoBrepFaceSet::generatePrimitives(SoAction * action)
#undef DO_VERTEX
void SoBrepFaceSet::renderHighlight(SoGLRenderAction *action)
void SoBrepFaceSet::renderHighlight(SoGLRenderAction *action, SelContextPtr ctx)
{
if(!ctx || ctx->highlightIndex < 0)
return;
SoState * state = action->getState();
state->push();
SoLazyElement::setEmissive(state, &this->highlightColor);
SoOverrideElement::setEmissiveColorOverride(state, this, true);
SoLazyElement::setEmissive(state, &ctx->highlightColor);
// if shading is disabled then set also the diffuse color
if (SoLazyElement::getLightModel(state) == SoLazyElement::BASE_COLOR) {
SoLazyElement::setDiffuse(state, this,1, &this->highlightColor,&this->colorpacker);
SoOverrideElement::setDiffuseColorOverride(state, this, true);
packedColor = ctx->highlightColor.getPackedValue(0.0);
SoLazyElement::setPacked(state, this,1, &packedColor,false);
}
SoTextureEnabledElement::set(state,this,false);
Binding mbind = this->findMaterialBinding(state);
Binding nbind = this->findNormalBinding(state);
@@ -845,8 +1202,8 @@ void SoBrepFaceSet::renderHighlight(SoGLRenderAction *action)
mb.sendFirst(); // make sure we have the correct material
int32_t id = this->highlightIndex.getValue();
if (id >= this->partIndex.getNum()) {
int id = ctx->highlightIndex;
if (id!=INT_MAX && id >= this->partIndex.getNum()) {
SoDebugError::postWarning("SoBrepFaceSet::renderHighlight", "highlightIndex out of range");
}
else {
@@ -856,11 +1213,17 @@ void SoBrepFaceSet::renderHighlight(SoGLRenderAction *action)
pindices = this->partIndex.getValues(0);
// coords
int length = (int)pindices[id]*4;
int start=0;
for (int i=0;i<id;i++)
start+=(int)pindices[i];
start *= 4;
int length;
if(id==INT_MAX) {
length = numindices;
id = 0;
} else {
length = (int)pindices[id]*4;
for (int i=0;i<id;i++)
start+=(int)pindices[i];
start *= 4;
}
// normals
if (nbind == PER_VERTEX_INDEXED)
@@ -883,21 +1246,23 @@ void SoBrepFaceSet::renderHighlight(SoGLRenderAction *action)
this->readUnlockNormalCache();
}
void SoBrepFaceSet::renderSelection(SoGLRenderAction *action)
void SoBrepFaceSet::renderSelection(SoGLRenderAction *action, SelContextPtr ctx, bool push)
{
int numSelected = this->selectionIndex.getNum();
const int32_t* selected = this->selectionIndex.getValues(0);
if (numSelected == 0) return;
if(!ctx || ctx->selectionIndex.empty())
return;
SoState * state = action->getState();
state->push();
SoLazyElement::setEmissive(state, &this->selectionColor);
SoOverrideElement::setEmissiveColorOverride(state, this, true);
// if shading is disabled then set also the diffuse color
if (SoLazyElement::getLightModel(state) == SoLazyElement::BASE_COLOR) {
SoLazyElement::setDiffuse(state, this,1, &this->selectionColor,&this->colorpacker);
SoOverrideElement::setDiffuseColorOverride(state, this, true);
if(push) {
state->push();
SoLazyElement::setEmissive(state, &ctx->selectionColor);
// if shading is disabled then set also the diffuse color
if (SoLazyElement::getLightModel(state) == SoLazyElement::BASE_COLOR) {
packedColor = ctx->selectionColor.getPackedValue(0.0);
SoLazyElement::setPacked(state, this,1, &packedColor,false);
}
SoTextureEnabledElement::set(state,this,false);
}
Binding mbind = this->findMaterialBinding(state);
@@ -930,16 +1295,19 @@ void SoBrepFaceSet::renderSelection(SoGLRenderAction *action)
if (!nindices) nindices = cindices;
pindices = this->partIndex.getValues(0);
// materials
mbind = OVERALL;
doTextures = false;
if(push) {
// materials
mbind = OVERALL;
doTextures = false;
}
for (int i=0; i<numSelected; i++) {
int id = selected[i];
for(auto id : ctx->selectionIndex) {
if (id >= this->partIndex.getNum()) {
SoDebugError::postWarning("SoBrepFaceSet::renderSelection", "selectionIndex out of range");
break;
}
if (id>=0 && id==ctx->highlightIndex)
continue;
// coords
int length=0;
@@ -948,8 +1316,8 @@ void SoBrepFaceSet::renderSelection(SoGLRenderAction *action)
// if < 0 then select everything
if (id < 0) {
length = numindices;
}
else {
id = 0;
} else {
length = (int)pindices[id]*4;
for (int j=0;j<id;j++)
start+=(int)pindices[j];
@@ -969,8 +1337,11 @@ void SoBrepFaceSet::renderSelection(SoGLRenderAction *action)
renderShape(action, false, static_cast<const SoGLCoordinateElement*>(coords), &(cindices[start]), length,
&(pindices[id]), numparts, normals_s, nindices_s, &mb, mindices, &tb, tindices, nbind, mbind, doTextures?1:0);
}
state->pop();
if(push) {
state->pop();
// SoCacheElement::invalidate(state);
}
if (normalCacheUsed)
this->readUnlockNormalCache();
}
@@ -1033,6 +1404,7 @@ void SoBrepFaceSet::VBO::render(SoGLRenderAction * action,
buf.vertex_array_size = 0;
buf.index_array_size = 0;
this->vbomap[contextId] = buf;
this->vboLoaded = false;
}
else {
buf = it->second;