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:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user