Files
create/src/Mod/Part/Gui/SoBrepFaceSet.cpp
Markus Reitböck 8fa48b25b4 Part: use CMake to generate precompiled headers on all platforms
"Professional CMake" book suggest the following:

"Targets should build successfully with or without compiler support for precompiled headers. It
 should be considered an optimization, not a requirement. In particular, do not explicitly include a
 precompile header (e.g. stdafx.h) in the source code, let CMake force-include an automatically
 generated precompile header on the compiler command line instead. This is more portable across
 the major compilers and is likely to be easier to maintain. It will also avoid warnings being
 generated from certain code checking tools like iwyu (include what you use)."

Therefore, removed the "#include <PreCompiled.h>" from sources, also
there is no need for the "#ifdef _PreComp_" anymore
2025-09-24 20:08:56 +02:00

2015 lines
73 KiB
C++

/***************************************************************************
* Copyright (c) 2011 Werner Mayer <wmayer[at]users.sourceforge.net> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#include <FCConfig.h>
#ifndef FC_OS_WIN32
# ifndef GL_GLEXT_PROTOTYPES
# define GL_GLEXT_PROTOTYPES 1
# endif
#endif
# include <algorithm>
# include <limits>
# include <map>
# include <Inventor/SoPickedPoint.h>
# include <Inventor/SoPrimitiveVertex.h>
# include <Inventor/actions/SoGetBoundingBoxAction.h>
# include <Inventor/actions/SoGLRenderAction.h>
# include <Inventor/bundles/SoMaterialBundle.h>
# include <Inventor/bundles/SoTextureCoordinateBundle.h>
# include <Inventor/elements/SoLazyElement.h>
# include <Inventor/elements/SoOverrideElement.h>
# include <Inventor/elements/SoCoordinateElement.h>
# include <Inventor/elements/SoGLCoordinateElement.h>
# include <Inventor/elements/SoGLCacheContextElement.h>
# include <Inventor/elements/SoGLVBOElement.h>
# include <Inventor/errors/SoDebugError.h>
# include <Inventor/details/SoFaceDetail.h>
# include <Inventor/misc/SoState.h>
# include <Inventor/misc/SoContextHandler.h>
# include <Inventor/elements/SoCacheElement.h>
# include <Inventor/elements/SoTextureEnabledElement.h>
# ifdef FC_OS_WIN32
# include <windows.h>
# include <GL/gl.h>
# include <GL/glext.h>
# else
# ifdef FC_OS_MACOSX
# include <OpenGL/gl.h>
# include <OpenGL/glext.h>
# else
# include <GL/gl.h>
# include <GL/glext.h>
# endif //FC_OS_MACOSX
# endif //FC_OS_WIN32
// Should come after glext.h to avoid warnings
# include <Inventor/C/glue/gl.h>
#include <Base/Profiler.h>
#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;
SO_NODE_SOURCE(SoBrepFaceSet)
#define PRIVATE(p) ((p)->pimpl)
class SoBrepFaceSet::VBO {
public:
struct Buffer {
uint32_t myvbo[2];
std::size_t vertex_array_size;
std::size_t index_array_size;
bool updateVbo;
bool vboLoaded;
};
static SbBool vboAvailable;
uint32_t indice_array;
std::map<uint32_t, Buffer> vbomap;
VBO()
{
SoContextHandler::addContextDestructionCallback(context_destruction_cb, this);
indice_array = 0;
}
~VBO()
{
SoContextHandler::removeContextDestructionCallback(context_destruction_cb, this);
// schedule delete for all allocated GL resources
std::map<uint32_t, Buffer>::iterator it;
for (it = vbomap.begin(); it != vbomap.end(); ++it) {
void * ptr0 = (void*) ((uintptr_t) it->second.myvbo[0]);
SoGLCacheContextElement::scheduleDeleteCallback(it->first, VBO::vbo_delete, ptr0);
void * ptr1 = (void*) ((uintptr_t) it->second.myvbo[1]);
SoGLCacheContextElement::scheduleDeleteCallback(it->first, VBO::vbo_delete, ptr1);
}
}
void render(SoGLRenderAction * action,
const SoGLCoordinateElement * const vertexlist,
const int32_t *vertexindices,
int num_vertexindices,
const int32_t *partindices,
int num_partindices,
const SbVec3f *normals,
const int32_t *normindices,
SoMaterialBundle *const materials,
const int32_t *matindices,
SoTextureCoordinateBundle * const texcoords,
const int32_t *texindices,
const int nbind,
const int mbind,
SbBool texture);
static void context_destruction_cb(uint32_t context, void * userdata)
{
VBO * self = static_cast<VBO*>(userdata);
std::map<uint32_t, Buffer>::iterator it = self->vbomap.find(context);
if (it != self->vbomap.end()) {
#ifdef FC_OS_WIN32
const cc_glglue * glue = cc_glglue_instance((int) context);
PFNGLDELETEBUFFERSARBPROC glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC)cc_glglue_getprocaddress(glue, "glDeleteBuffersARB");
#endif
auto &buffer = it->second;
glDeleteBuffersARB(2, buffer.myvbo);
self->vbomap.erase(it);
}
}
static void vbo_delete(void * closure, uint32_t contextid)
{
const cc_glglue * glue = cc_glglue_instance((int) contextid);
GLuint id = (GLuint) ((uintptr_t) closure);
cc_glglue_glDeleteBuffers(glue, 1, &id);
}
};
SbBool SoBrepFaceSet::VBO::vboAvailable = false;
void SoBrepFaceSet::initClass()
{
SO_NODE_INIT_CLASS(SoBrepFaceSet, SoIndexedFaceSet, "IndexedFaceSet");
}
SoBrepFaceSet::SoBrepFaceSet()
{
SO_NODE_CONSTRUCTOR(SoBrepFaceSet);
SO_NODE_ADD_FIELD(partIndex, (-1));
selContext = std::make_shared<SelContext>();
selContext2 = std::make_shared<SelContext>();
packedColor = 0;
pimpl = std::make_unique<VBO>();
}
SoBrepFaceSet::~SoBrepFaceSet() = default;
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()) {
SelContextPtr ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext,false);
if(ctx) {
ctx->highlightIndex = -1;
touch();
}
if (viewProvider) {
viewProvider->setFaceHighlightActive(false);
}
return;
}
const SoDetail* detail = hlaction->getElement();
if (!detail) {
SelContextPtr ctx = Gui::SoFCSelectionRoot::getActionContext(action,this,selContext);
ctx->highlightIndex = std::numeric_limits<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();
}
if (viewProvider) {
viewProvider->setFaceHighlightActive(false);
}
}else {
int index = static_cast<const SoFaceDetail*>(detail)->getPartIndex();
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);
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;
} 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;
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();
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
SoState * state = action->getState();
Binding mbind = this->findMaterialBinding(state);
if (mbind == PER_PART) {
const SoLazyElement* mat = SoLazyElement::getInstance(state);
int numColor = 1;
int numParts = partIndex.getNum();
if (mat) {
numColor = mat->getNumDiffuse();
if (numColor == numParts) {
int count = 0;
const int32_t * indices = this->partIndex.getValues(0);
for (int i=0; i<numParts; i++) {
count += indices[i];
}
this->materialIndex.setNum(count);
int32_t * matind = this->materialIndex.startEditing();
int32_t k = 0;
for (int i=0; i<numParts; i++) {
for (int j=0; j<indices[i]; j++) {
matind[k++] = i;
}
}
this->materialIndex.finishEditing();
}
}
}
}
// The recommended way to set 'updateVbo' is to reimplement the method 'notify'
// but the base class made this method private so that we can't override it.
// So, the alternative way is to write a custom SoAction class.
else if (action->getTypeId() == Gui::SoUpdateVBOAction::getClassTypeId()) {
for(auto &v : PRIVATE(this)->vbomap) {
v.second.updateVbo = true;
v.second.vboLoaded = false;
}
}
inherited::doAction(action);
}
#ifdef RENDER_GLARRAYS
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);
SoTextureCoordinateBundle tb(action, true, false);
SbBool doTextures = tb.needCoordinates();
if (ctx->coordIndex.getNum() < 3)
return;
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;
}
else if (mbind == 1) {
renderColoredArray(&mb);
return;
}
}
#endif
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;
SbBool sendNormals = !mb.isColorOnly() || tb.isFunction();
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();
renderShape(state, vboAvailable, static_cast<const SoGLCoordinateElement*>(coords), cindices, numindices,
pindices, numparts, normals, nindices, &mb, mindices, &tb, tindices, nbind, mbind, doTextures?1:0);
if(normalCacheUsed)
this->readUnlockNormalCache();
}
renderHighlight(action,ctx);
renderSelection(action,ctx);
}
//****************************************************************************
// renderSimpleArray: normal and coord from vertex_array;
// no texture, color, highlight or selection but highet possible speed;
// all vertices written in one go!
//
void SoBrepFaceSet::renderSimpleArray()
{
int cnt = index_array.size();
if (cnt == 0)
return;
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
glInterleavedArrays(GL_N3F_V3F, 0, &(vertex_array[0]));
glDrawElements(GL_TRIANGLES, cnt, GL_UNSIGNED_INT, &(index_array[0]));
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
}
//****************************************************************************
// renderColoredArray: normal and coord from vertex_array;
// no texture, highlight or selection but color / material array.
// needs to iterate over parts (i.e. geometry faces)
//
void SoBrepFaceSet::renderColoredArray(SoMaterialBundle *const materials)
{
int num_parts = partIndex.getNum();
int cnt = index_array.size();
if (cnt == 0)
return;
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
glInterleavedArrays(GL_N3F_V3F, 0, &(vertex_array[0]));
const int32_t* ptr = &(index_array[0]);
for (int part_id = 0; part_id < num_parts; part_id++) {
int tris = partIndex[part_id];
if (tris > 0) {
materials->send(part_id, true);
glDrawElements(GL_TRIANGLES, 3 * tris, GL_UNSIGNED_INT, ptr);
ptr += 3 * tris;
}
}
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
}
#else
void SoBrepFaceSet::GLRender(SoGLRenderAction *action)
{
ZoneScoped;
//SoBase::staticDataLock();
static bool init = false;
if (!init) {
std::string ext = (const char*)(glGetString(GL_EXTENSIONS));
PRIVATE(this)->vboAvailable = (ext.find("GL_ARB_vertex_buffer_object") != std::string::npos);
init = true;
}
//SoBase::staticDataUnlock();
if (this->coordIndex.getNum() < 3)
return;
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.empty() && ctx->highlightIndex<0))
ctx.reset();
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
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 (preselection) 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 == std::numeric_limits<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.empty()) {
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)) {
Binding mbind = this->findMaterialBinding(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 doTextures;
SbBool normalCacheUsed;
SoTextureCoordinateBundle tb(action, true, false);
doTextures = tb.needCoordinates();
SbBool sendNormals = !mb.isColorOnly() || tb.isFunction();
this->getVertexData(state, coords, normals, cindices,
nindices, tindices, mindices, numindices,
sendNormals, normalCacheUsed);
// just in case someone forgot
if (!mindices) mindices = cindices;
if (!nindices) nindices = cindices;
pindices = this->partIndex.getValues(0);
numparts = this->partIndex.getNum();
SbBool hasVBO = !ctx2 && PRIVATE(this)->vboAvailable;
if (hasVBO) {
// get the VBO status of the viewer
Gui::SoGLVBOActivatedElement::get(state, hasVBO);
}
renderShape(action, hasVBO, static_cast<const SoGLCoordinateElement*>(coords), cindices, numindices,
pindices, numparts, normals, nindices, &mb, mindices, &tb, tindices, nbind, mbind, doTextures?1:0);
if (normalCacheUsed)
this->readUnlockNormalCache();
}
if(pushed) {
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];
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 omitted 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.empty())))
{
state->push();
packedColors.clear();
if(ctx && Gui::Selection().needPickedList()) {
hasTransparency = true;
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.data(), 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.empty()) {
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;
}
}
size_t num = materialIndex.getNum();
if (num != matIndex.size() || materialIndex.getValues(0) != matIndex.data()) {
SbBool notify = enableNotify(FALSE);
materialIndex.setValuesPointer(matIndex.size(), matIndex.data());
if (notify) enableNotify(notify);
}
SoMaterialBindingElement::set(state, this, SoMaterialBindingElement::PER_PART_INDEXED);
SoLazyElement::setPacked(state, this, packedColors.size(), packedColors.data(), 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);
}
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) { \
pointDetail.setMaterialIndex(matnr); \
vertex.setMaterialIndex(matnr++); \
} \
else if (mbind == PER_VERTEX_INDEXED) { \
pointDetail.setMaterialIndex(*mindices); \
vertex.setMaterialIndex(*mindices++); \
} \
if (nbind == PER_VERTEX) { \
pointDetail.setNormalIndex(normnr); \
currnormal = &normals[normnr++]; \
vertex.setNormal(*currnormal); \
} \
else if (nbind == PER_VERTEX_INDEXED) { \
pointDetail.setNormalIndex(*nindices); \
currnormal = &normals[*nindices++]; \
vertex.setNormal(*currnormal); \
} \
if (tb.isFunction()) { \
vertex.setTextureCoords(tb.get(coords->get3(idx), *currnormal)); \
if (tb.needIndices()) pointDetail.setTextureCoordIndex(tindices ? *tindices++ : texidx++); \
} \
else if (tbind != NONE) { \
pointDetail.setTextureCoordIndex(tindices ? *tindices : texidx); \
vertex.setTextureCoords(tb.get(tindices ? *tindices++ : texidx++)); \
} \
vertex.setPoint(coords->get3(idx)); \
pointDetail.setCoordinateIndex(idx); \
this->shapeVertex(&vertex);
void SoBrepFaceSet::generatePrimitives(SoAction * action)
{
//This is highly experimental!!!
if (this->coordIndex.getNum() < 3)
return;
SoState * state = action->getState();
if (this->vertexProperty.getValue()) {
state->push();
this->vertexProperty.getValue()->doAction(action);
}
Binding mbind = this->findMaterialBinding(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;
SbBool doTextures;
SbBool sendNormals;
SbBool normalCacheUsed;
sendNormals = true; // always generate normals
this->getVertexData(state, coords, normals, cindices,
nindices, tindices, mindices, numindices,
sendNormals, normalCacheUsed);
SoTextureCoordinateBundle tb(action, false, false);
doTextures = tb.needCoordinates();
if (!sendNormals) nbind = OVERALL;
else if (normalCacheUsed && nbind == PER_VERTEX) {
nbind = PER_VERTEX_INDEXED;
}
else if (normalCacheUsed && nbind == PER_FACE_INDEXED) {
nbind = PER_FACE;
}
if (this->getNodeType() == SoNode::VRML1) {
// For VRML1, PER_VERTEX means per vertex in shape, not PER_VERTEX
// on the state.
if (mbind == PER_VERTEX) {
mbind = PER_VERTEX_INDEXED;
mindices = cindices;
}
if (nbind == PER_VERTEX) {
nbind = PER_VERTEX_INDEXED;
nindices = cindices;
}
}
Binding tbind = NONE;
if (doTextures) {
if (tb.isFunction() && !tb.needIndices()) {
tbind = NONE;
tindices = nullptr;
}
// FIXME: just call inherited::areTexCoordsIndexed() instead of
// the if-check? 20020110 mortene.
else if (SoTextureCoordinateBindingElement::get(state) ==
SoTextureCoordinateBindingElement::PER_VERTEX) {
tbind = PER_VERTEX;
tindices = nullptr;
}
else {
tbind = PER_VERTEX_INDEXED;
if (!tindices)
tindices = cindices;
}
}
if (nbind == PER_VERTEX_INDEXED && !nindices) {
nindices = cindices;
}
if (mbind == PER_VERTEX_INDEXED && !mindices) {
mindices = cindices;
}
int texidx = 0;
TriangleShape mode = POLYGON;
TriangleShape newmode;
const int32_t *viptr = cindices;
const int32_t *viendptr = viptr + numindices;
const int32_t *piptr = this->partIndex.getValues(0);
int num_partindices = this->partIndex.getNum();
const int32_t *piendptr = piptr + num_partindices;
int32_t v1, v2, v3, v4, v5 = 0, pi; // v5 init unnecessary, but kills a compiler warning.
SoPrimitiveVertex vertex;
SoPointDetail pointDetail;
SoFaceDetail faceDetail;
vertex.setDetail(&pointDetail);
SbVec3f dummynormal(0,0,1);
const SbVec3f *currnormal = &dummynormal;
if (normals) currnormal = normals;
vertex.setNormal(*currnormal);
int matnr = 0;
int normnr = 0;
int trinr = 0;
pi = piptr < piendptr ? *piptr++ : -1;
while (pi == 0) {
// It may happen that a part has no triangles
pi = piptr < piendptr ? *piptr++ : -1;
if (mbind == PER_PART)
matnr++;
else if (mbind == PER_PART_INDEXED)
mindices++;
}
while (viptr + 2 < viendptr) {
v1 = *viptr++;
v2 = *viptr++;
v3 = *viptr++;
if (v1 < 0 || v2 < 0 || v3 < 0) {
break;
}
v4 = viptr < viendptr ? *viptr++ : -1;
if (v4 < 0) newmode = TRIANGLES;
else {
v5 = viptr < viendptr ? *viptr++ : -1;
if (v5 < 0) newmode = QUADS;
else newmode = POLYGON;
}
if (newmode != mode) {
if (mode != POLYGON) this->endShape();
mode = newmode;
this->beginShape(action, mode, &faceDetail);
}
else if (mode == POLYGON) this->beginShape(action, POLYGON, &faceDetail);
// vertex 1 can't use DO_VERTEX
if (mbind == PER_PART) {
if (trinr == 0) {
pointDetail.setMaterialIndex(matnr);
vertex.setMaterialIndex(matnr++);
}
}
else if (mbind == PER_PART_INDEXED) {
if (trinr == 0) {
pointDetail.setMaterialIndex(*mindices);
vertex.setMaterialIndex(*mindices++);
}
}
else if (mbind == PER_VERTEX || mbind == PER_FACE) {
pointDetail.setMaterialIndex(matnr);
vertex.setMaterialIndex(matnr++);
}
else if (mbind == PER_VERTEX_INDEXED || mbind == PER_FACE_INDEXED) {
pointDetail.setMaterialIndex(*mindices);
vertex.setMaterialIndex(*mindices++);
}
if (nbind == PER_VERTEX || nbind == PER_FACE) {
pointDetail.setNormalIndex(normnr);
currnormal = &normals[normnr++];
vertex.setNormal(*currnormal);
}
else if (nbind == PER_FACE_INDEXED || nbind == PER_VERTEX_INDEXED) {
pointDetail.setNormalIndex(*nindices);
currnormal = &normals[*nindices++];
vertex.setNormal(*currnormal);
}
if (tb.isFunction()) {
vertex.setTextureCoords(tb.get(coords->get3(v1), *currnormal));
if (tb.needIndices()) pointDetail.setTextureCoordIndex(tindices ? *tindices++ : texidx++);
}
else if (tbind != NONE) {
pointDetail.setTextureCoordIndex(tindices ? *tindices : texidx);
vertex.setTextureCoords(tb.get(tindices ? *tindices++ : texidx++));
}
pointDetail.setCoordinateIndex(v1);
vertex.setPoint(coords->get3(v1));
this->shapeVertex(&vertex);
DO_VERTEX(v2);
DO_VERTEX(v3);
if (mode != TRIANGLES) {
DO_VERTEX(v4);
if (mode == POLYGON) {
DO_VERTEX(v5);
v1 = viptr < viendptr ? *viptr++ : -1;
while (v1 >= 0) {
DO_VERTEX(v1);
v1 = viptr < viendptr ? *viptr++ : -1;
}
this->endShape();
}
}
faceDetail.incFaceIndex();
if (mbind == PER_VERTEX_INDEXED) {
mindices++;
}
if (nbind == PER_VERTEX_INDEXED) {
nindices++;
}
if (tindices) tindices++;
trinr++;
if (pi == trinr) {
pi = piptr < piendptr ? *piptr++ : -1;
while (pi == 0) {
// It may happen that a part has no triangles
pi = piptr < piendptr ? *piptr++ : -1;
if (mbind == PER_PART)
matnr++;
else if (mbind == PER_PART_INDEXED)
mindices++;
}
trinr = 0;
}
}
if (mode != POLYGON) this->endShape();
if (normalCacheUsed) {
this->readUnlockNormalCache();
}
if (this->vertexProperty.getValue()) {
state->pop();
}
}
#undef DO_VERTEX
void SoBrepFaceSet::renderHighlight(SoGLRenderAction *action, SelContextPtr ctx)
{
if(!ctx || ctx->highlightIndex < 0)
return;
SoState * state = action->getState();
state->push();
SoLazyElement::setEmissive(state, &ctx->highlightColor);
// if shading is disabled then set also the diffuse color
if (SoLazyElement::getLightModel(state) == SoLazyElement::BASE_COLOR) {
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);
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;
SbBool doTextures;
SbBool normalCacheUsed;
SoMaterialBundle mb(action);
SoTextureCoordinateBundle tb(action, true, false);
doTextures = tb.needCoordinates();
SbBool sendNormals = !mb.isColorOnly() || tb.isFunction();
this->getVertexData(state, coords, normals, cindices,
nindices, tindices, mindices, numindices,
sendNormals, normalCacheUsed);
mb.sendFirst(); // make sure we have the correct material
int id = ctx->highlightIndex;
if (id != std::numeric_limits<int>::max() && id >= this->partIndex.getNum()) {
SoDebugError::postWarning("SoBrepFaceSet::renderHighlight", "highlightIndex out of range");
}
else {
// just in case someone forgot
if (!mindices) mindices = cindices;
if (!nindices) nindices = cindices;
pindices = this->partIndex.getValues(0);
// coords
int start=0;
int length;
if(id == std::numeric_limits<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)
nindices = &(nindices[start]);
else if (nbind == PER_VERTEX)
normals = &(normals[start]);
else
nbind = OVERALL;
// materials
mbind = OVERALL;
doTextures = false;
renderShape(action, false, static_cast<const SoGLCoordinateElement*>(coords), &(cindices[start]), length,
&(pindices[id]), 1, normals, nindices, &mb, mindices, &tb, tindices, nbind, mbind, doTextures);
}
state->pop();
if (normalCacheUsed)
this->readUnlockNormalCache();
}
void SoBrepFaceSet::renderSelection(SoGLRenderAction *action, SelContextPtr ctx, bool push)
{
if(!ctx || ctx->selectionIndex.empty())
return;
SoState * state = action->getState();
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);
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;
SbBool doTextures;
SbBool normalCacheUsed;
SoMaterialBundle mb(action);
SoTextureCoordinateBundle tb(action, true, false);
doTextures = tb.needCoordinates();
SbBool sendNormals = !mb.isColorOnly() || tb.isFunction();
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);
if(push) {
// materials
mbind = OVERALL;
doTextures = false;
}
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;
int start=0;
int numparts=1;
// if < 0 then select everything
if (id < 0) {
length = numindices;
id = 0;
} else {
length = (int)pindices[id]*4;
for (int j=0;j<id;j++)
start+=(int)pindices[j];
start *= 4;
}
// normals
const SbVec3f * normals_s = normals;
const int32_t * nindices_s = nindices;
if (nbind == PER_VERTEX_INDEXED)
nindices_s = &(nindices[start]);
else if (nbind == PER_VERTEX)
normals_s = &(normals[start]);
else
nbind = OVERALL;
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);
}
if (push) {
state->pop();
}
if (normalCacheUsed)
this->readUnlockNormalCache();
}
void SoBrepFaceSet::VBO::render(SoGLRenderAction * action,
const SoGLCoordinateElement * const vertexlist,
const int32_t *vertexindices,
int num_indices,
const int32_t *partindices,
int num_partindices,
const SbVec3f *normals,
const int32_t *normalindices,
SoMaterialBundle *const materials,
const int32_t *matindices,
SoTextureCoordinateBundle * const texcoords,
const int32_t *texindices,
const int nbind,
const int mbind,
SbBool texture)
{
(void)texcoords; (void)texindices; (void)texture;
const SbVec3f * coords3d = vertexlist->getArrayPtr3();
SbVec3f * cur_coords3d = const_cast<SbVec3f *>(coords3d);
const int32_t *viptr = vertexindices;
const int32_t *viendptr = viptr + num_indices;
const int32_t *piptr = partindices;
const int32_t *piendptr = piptr + num_partindices;
int32_t v1, v2, v3, v4, pi;
SbVec3f dummynormal(0,0,1);
int numverts = vertexlist->getNum();
const SbVec3f *currnormal = &dummynormal;
if (normals) currnormal = normals;
int matnr = 0;
int trinr = 0;
float * vertex_array = nullptr;
GLuint * index_array = nullptr;
SbColor mycolor1,mycolor2,mycolor3;
SbVec3f *mynormal1 = const_cast<SbVec3f *>(currnormal);
SbVec3f *mynormal2 = const_cast<SbVec3f *>(currnormal);
SbVec3f *mynormal3 = const_cast<SbVec3f *>(currnormal);
int indice=0;
uint32_t RGBA,R,G,B,A;
float Rf,Gf,Bf,Af;
uint32_t contextId = action->getCacheContext();
auto res = this->vbomap.insert(std::make_pair(contextId,VBO::Buffer()));
VBO::Buffer &buf = res.first->second;
if (res.second) {
#ifdef FC_OS_WIN32
const cc_glglue * glue = cc_glglue_instance(action->getCacheContext());
PFNGLGENBUFFERSPROC glGenBuffersARB = (PFNGLGENBUFFERSPROC)cc_glglue_getprocaddress(glue, "glGenBuffersARB");
#endif
glGenBuffersARB(2, buf.myvbo);
buf.vertex_array_size = 0;
buf.index_array_size = 0;
buf.vboLoaded = false;
}
if ((buf.vertex_array_size != (sizeof(float) * num_indices * 10)) ||
(buf.index_array_size != (sizeof(GLuint) * num_indices))) {
if ((buf.vertex_array_size != 0 ) && ( buf.index_array_size != 0))
buf.updateVbo = true;
}
// vbo loaded is defining if we must pre-load data into the VBO. When the variable is set to 0
// it means that the VBO has not been initialized
// updateVbo is tracking the need to update the content of the VBO which act as a buffer within
// the graphic card
// TODO FINISHING THE COLOR SUPPORT !
if (!buf.vboLoaded || buf.updateVbo) {
#ifdef FC_OS_WIN32
const cc_glglue * glue = cc_glglue_instance(action->getCacheContext());
PFNGLBINDBUFFERARBPROC glBindBufferARB = (PFNGLBINDBUFFERARBPROC) cc_glglue_getprocaddress(glue, "glBindBufferARB");
//PFNGLMAPBUFFERARBPROC glMapBufferARB = (PFNGLMAPBUFFERARBPROC) cc_glglue_getprocaddress(glue, "glMapBufferARB");
PFNGLGENBUFFERSPROC glGenBuffersARB = (PFNGLGENBUFFERSPROC)cc_glglue_getprocaddress(glue, "glGenBuffersARB");
PFNGLDELETEBUFFERSARBPROC glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC)cc_glglue_getprocaddress(glue, "glDeleteBuffersARB");
PFNGLBUFFERDATAARBPROC glBufferDataARB = (PFNGLBUFFERDATAARBPROC)cc_glglue_getprocaddress(glue, "glBufferDataARB");
#endif
// We must manage buffer size increase let's clear everything and re-init to test the
// clearing process
glDeleteBuffersARB(2, buf.myvbo);
glGenBuffersARB(2, buf.myvbo);
vertex_array = ( float * ) malloc ( sizeof(float) * num_indices * 10 );
index_array = ( GLuint *) malloc ( sizeof(GLuint) * num_indices );
buf.vertex_array_size = sizeof(float) * num_indices * 10;
buf.index_array_size = sizeof(GLuint) * num_indices;
this->vbomap[contextId] = buf;
this->indice_array = 0;
// Get the initial colors
SoState * state = action->getState();
mycolor1=SoLazyElement::getDiffuse(state,0);
mycolor2=SoLazyElement::getDiffuse(state,0);
mycolor3=SoLazyElement::getDiffuse(state,0);
pi = piptr < piendptr ? *piptr++ : -1;
while (pi == 0) {
// It may happen that a part has no triangles
pi = piptr < piendptr ? *piptr++ : -1;
if (mbind == PER_PART)
matnr++;
else if (mbind == PER_PART_INDEXED)
matindices++;
}
while (viptr + 2 < viendptr) {
v1 = *viptr++;
v2 = *viptr++;
v3 = *viptr++;
// This test is for robustness upon buggy data sets
if (v1 < 0 || v2 < 0 || v3 < 0 ||
v1 >= numverts || v2 >= numverts || v3 >= numverts) {
break;
}
v4 = viptr < viendptr ? *viptr++ : -1;
(void)v4;
if (mbind == PER_PART) {
if (trinr == 0) {
materials->send(matnr++, true);
mycolor1=SoLazyElement::getDiffuse(state,matnr-1);
mycolor2=mycolor1;
mycolor3=mycolor1;
}
}
else if (mbind == PER_PART_INDEXED) {
if (trinr == 0)
materials->send(*matindices++, true);
}
else if (mbind == PER_VERTEX || mbind == PER_FACE) {
materials->send(matnr++, true);
}
else if (mbind == PER_VERTEX_INDEXED || mbind == PER_FACE_INDEXED) {
materials->send(*matindices++, true);
}
if (normals) {
if (nbind == PER_VERTEX || nbind == PER_FACE) {
currnormal = normals++;
mynormal1 = const_cast<SbVec3f *>(currnormal);
}
else if (nbind == PER_VERTEX_INDEXED || nbind == PER_FACE_INDEXED) {
currnormal = &normals[*normalindices++];
mynormal1 = const_cast<SbVec3f *>(currnormal);
}
}
if (mbind == PER_VERTEX)
materials->send(matnr++, true);
else if (mbind == PER_VERTEX_INDEXED)
materials->send(*matindices++, true);
if (normals) {
if (nbind == PER_VERTEX) {
currnormal = normals++;
mynormal2 = const_cast<SbVec3f *>(currnormal);
}
else if (nbind == PER_VERTEX_INDEXED) {
currnormal = &normals[*normalindices++];
mynormal2 = const_cast<SbVec3f *>(currnormal);
}
}
if (mbind == PER_VERTEX)
materials->send(matnr++, true);
else if (mbind == PER_VERTEX_INDEXED)
materials->send(*matindices++, true);
if (normals) {
if (nbind == PER_VERTEX) {
currnormal = normals++;
mynormal3 =const_cast<SbVec3f *>(currnormal);
}
else if (nbind == PER_VERTEX_INDEXED) {
currnormal = &normals[*normalindices++];
mynormal3 = const_cast<SbVec3f *>(currnormal);
}
}
if (nbind == PER_VERTEX_INDEXED)
normalindices++;
/* We building the Vertex dataset there and push it to a VBO */
/* The Vertex array shall contain per element vertex_coordinates[3],
normal_coordinates[3], color_value[3] (RGBA format) */
index_array[this->indice_array] = this->indice_array;
index_array[this->indice_array+1] = this->indice_array + 1;
index_array[this->indice_array+2] = this->indice_array + 2;
this->indice_array += 3;
((SbVec3f *)(cur_coords3d+v1 ))->getValue(vertex_array[indice+0],
vertex_array[indice+1],
vertex_array[indice+2]);
((SbVec3f *)(mynormal1))->getValue(vertex_array[indice+3],
vertex_array[indice+4],
vertex_array[indice+5]);
/* We decode the Vertex1 color */
RGBA = mycolor1.getPackedValue();
R = ( RGBA & 0xFF000000 ) >> 24 ;
G = ( RGBA & 0xFF0000 ) >> 16;
B = ( RGBA & 0xFF00 ) >> 8;
A = ( RGBA & 0xFF );
Rf = (((float )R) / 255.0);
Gf = (((float )G) / 255.0);
Bf = (((float )B) / 255.0);
Af = (((float )A) / 255.0);
vertex_array[indice+6] = Rf;
vertex_array[indice+7] = Gf;
vertex_array[indice+8] = Bf;
vertex_array[indice+9] = Af;
indice+=10;
((SbVec3f *)(cur_coords3d+v2))->getValue(vertex_array[indice+0],
vertex_array[indice+1],
vertex_array[indice+2]);
((SbVec3f *)(mynormal2))->getValue(vertex_array[indice+3],
vertex_array[indice+4],
vertex_array[indice+5]);
RGBA = mycolor2.getPackedValue();
R = ( RGBA & 0xFF000000 ) >> 24 ;
G = ( RGBA & 0xFF0000 ) >> 16;
B = ( RGBA & 0xFF00 ) >> 8;
A = ( RGBA & 0xFF );
Rf = (((float )R) / 255.0);
Gf = (((float )G) / 255.0);
Bf = (((float )B) / 255.0);
Af = (((float )A) / 255.0);
vertex_array[indice+6] = Rf;
vertex_array[indice+7] = Gf;
vertex_array[indice+8] = Bf;
vertex_array[indice+9] = Af;
indice+=10;
((SbVec3f *)(cur_coords3d+v3))->getValue(vertex_array[indice+0],
vertex_array[indice+1],
vertex_array[indice+2]);
((SbVec3f *)(mynormal3))->getValue(vertex_array[indice+3],
vertex_array[indice+4],
vertex_array[indice+5]);
RGBA = mycolor3.getPackedValue();
R = ( RGBA & 0xFF000000 ) >> 24 ;
G = ( RGBA & 0xFF0000 ) >> 16;
B = ( RGBA & 0xFF00 ) >> 8;
A = ( RGBA & 0xFF );
Rf = (((float )R) / 255.0);
Gf = (((float )G) / 255.0);
Bf = (((float )B) / 255.0);
Af = (((float )A) / 255.0);
vertex_array[indice+6] = Rf;
vertex_array[indice+7] = Gf;
vertex_array[indice+8] = Bf;
vertex_array[indice+9] = Af;
indice+=10;
/* ============================================================ */
trinr++;
if (pi == trinr) {
pi = piptr < piendptr ? *piptr++ : -1;
while (pi == 0) {
// It may happen that a part has no triangles
pi = piptr < piendptr ? *piptr++ : -1;
if (mbind == PER_PART)
matnr++;
else if (mbind == PER_PART_INDEXED)
matindices++;
}
trinr = 0;
}
}
glBindBufferARB(GL_ARRAY_BUFFER_ARB, buf.myvbo[0]);
glBufferDataARB(GL_ARRAY_BUFFER_ARB, sizeof(float) * indice , vertex_array, GL_DYNAMIC_DRAW_ARB);
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, buf.myvbo[1]);
glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, sizeof(GLuint) * this->indice_array , &index_array[0], GL_DYNAMIC_DRAW_ARB);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
buf.vboLoaded = true;
buf.updateVbo = false;
free(vertex_array);
free(index_array);
}
// This is the VBO rendering code
#ifdef FC_OS_WIN32
const cc_glglue * glue = cc_glglue_instance(action->getCacheContext());
PFNGLBINDBUFFERARBPROC glBindBufferARB = (PFNGLBINDBUFFERARBPROC)cc_glglue_getprocaddress(glue, "glBindBufferARB");
#endif
if (!buf.updateVbo) {
glBindBufferARB(GL_ARRAY_BUFFER_ARB, buf.myvbo[0]);
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, buf.myvbo[1]);
}
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glVertexPointer(3,GL_FLOAT,10*sizeof(GLfloat),nullptr);
glNormalPointer(GL_FLOAT,10*sizeof(GLfloat),(GLvoid *)(3*sizeof(GLfloat)));
glColorPointer(4,GL_FLOAT,10*sizeof(GLfloat),(GLvoid *)(6*sizeof(GLfloat)));
glDrawElements(GL_TRIANGLES, this->indice_array, GL_UNSIGNED_INT, (void *)nullptr);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
buf.updateVbo = false;
// The data is within the VBO we can clear it at application level
}
void SoBrepFaceSet::renderShape(SoGLRenderAction * action,
SbBool hasVBO,
const SoGLCoordinateElement * const vertexlist,
const int32_t *vertexindices,
int num_indices,
const int32_t *partindices,
int num_partindices,
const SbVec3f *normals,
const int32_t *normalindices,
SoMaterialBundle *const materials,
const int32_t *matindices,
SoTextureCoordinateBundle * const texcoords,
const int32_t *texindices,
const int nbind,
const int mbind,
SbBool texture)
{
// Can we use vertex buffer objects?
if (hasVBO) {
int nbinding = nbind;
SoState* state = action->getState();
if (SoLazyElement::getLightModel(state) == SoLazyElement::BASE_COLOR) {
// if no shading is set then the normals are all equal
nbinding = static_cast<int>(OVERALL);
}
PRIVATE(this)->render(action, vertexlist, vertexindices, num_indices, partindices, num_partindices, normals,
normalindices, materials, matindices, texcoords, texindices, nbinding, mbind, texture);
return;
}
int texidx = 0;
const SbVec3f * coords3d = nullptr;
coords3d = vertexlist->getArrayPtr3();
const int32_t *viptr = vertexindices;
const int32_t *viendptr = viptr + num_indices;
const int32_t *piptr = partindices;
const int32_t *piendptr = piptr + num_partindices;
int32_t v1, v2, v3, v4, pi;
SbVec3f dummynormal(0,0,1);
int numverts = vertexlist->getNum();
const SbVec3f *currnormal = &dummynormal;
if (normals) currnormal = normals;
int matnr = 0;
int trinr = 0;
// Legacy code without VBO support
pi = piptr < piendptr ? *piptr++ : -1;
while (pi == 0) {
// It may happen that a part has no triangles
pi = piptr < piendptr ? *piptr++ : -1;
if (mbind == PER_PART)
matnr++;
else if (mbind == PER_PART_INDEXED)
matindices++;
}
glBegin(GL_TRIANGLES);
while (viptr + 2 < viendptr) {
v1 = *viptr++;
v2 = *viptr++;
v3 = *viptr++;
if (v1 < 0 || v2 < 0 || v3 < 0 ||
v1 >= numverts || v2 >= numverts || v3 >= numverts) {
break;
}
v4 = viptr < viendptr ? *viptr++ : -1;
(void)v4;
/* vertex 1 *********************************************************/
if (mbind == PER_PART) {
if (trinr == 0)
materials->send(matnr++, true);
}
else if (mbind == PER_PART_INDEXED) {
if (trinr == 0)
materials->send(*matindices++, true);
}
else if (mbind == PER_VERTEX || mbind == PER_FACE) {
materials->send(matnr++, true);
}
else if (mbind == PER_VERTEX_INDEXED || mbind == PER_FACE_INDEXED) {
materials->send(*matindices++, true);
}
if (normals) {
if (nbind == PER_VERTEX || nbind == PER_FACE) {
currnormal = normals++;
glNormal3fv((const GLfloat*)currnormal);
}
else if (nbind == PER_VERTEX_INDEXED || nbind == PER_FACE_INDEXED) {
currnormal = &normals[*normalindices++];
glNormal3fv((const GLfloat*)currnormal);
}
}
if (texture) {
texcoords->send(texindices ? *texindices++ : texidx++,
vertexlist->get3(v1),
*currnormal);
}
glVertex3fv((const GLfloat*) (coords3d + v1));
/* vertex 2 *********************************************************/
if (mbind == PER_VERTEX)
materials->send(matnr++, true);
else if (mbind == PER_VERTEX_INDEXED)
materials->send(*matindices++, true);
if (normals) {
if (nbind == PER_VERTEX) {
currnormal = normals++;
glNormal3fv((const GLfloat*)currnormal);
}
else if (nbind == PER_VERTEX_INDEXED) {
currnormal = &normals[*normalindices++];
glNormal3fv((const GLfloat*)currnormal);
}
}
if (texture) {
texcoords->send(texindices ? *texindices++ : texidx++,
vertexlist->get3(v2),
*currnormal);
}
glVertex3fv((const GLfloat*) (coords3d + v2));
/* vertex 3 *********************************************************/
if (mbind == PER_VERTEX)
materials->send(matnr++, true);
else if (mbind == PER_VERTEX_INDEXED)
materials->send(*matindices++, true);
if (normals) {
if (nbind == PER_VERTEX) {
currnormal = normals++;
glNormal3fv((const GLfloat*)currnormal);
}
else if (nbind == PER_VERTEX_INDEXED) {
currnormal = &normals[*normalindices++];
glNormal3fv((const GLfloat*)currnormal);
}
}
if (texture) {
texcoords->send(texindices ? *texindices++ : texidx++,
vertexlist->get3(v3),
*currnormal);
}
glVertex3fv((const GLfloat*) (coords3d + v3));
if (mbind == PER_VERTEX_INDEXED)
matindices++;
if (nbind == PER_VERTEX_INDEXED)
normalindices++;
if (texture && texindices) {
texindices++;
}
trinr++;
if (pi == trinr) {
pi = piptr < piendptr ? *piptr++ : -1;
while (pi == 0) {
// It may happen that a part has no triangles
pi = piptr < piendptr ? *piptr++ : -1;
if (mbind == PER_PART)
matnr++;
else if (mbind == PER_PART_INDEXED)
matindices++;
}
trinr = 0;
}
}
glEnd();
}
SoDetail * SoBrepFaceSet::createTriangleDetail(SoRayPickAction * action,
const SoPrimitiveVertex * v1,
const SoPrimitiveVertex * v2,
const SoPrimitiveVertex * v3,
SoPickedPoint * pp)
{
SoDetail* detail = inherited::createTriangleDetail(action, v1, v2, v3, pp);
const int32_t * indices = this->partIndex.getValues(0);
int num = this->partIndex.getNum();
if (indices) {
SoFaceDetail* face_detail = static_cast<SoFaceDetail*>(detail);
int index = face_detail->getFaceIndex();
int count = 0;
for (int i=0; i<num; i++) {
count += indices[i];
if (index < count) {
face_detail->setPartIndex(i);
break;
}
}
}
return detail;
}
SoBrepFaceSet::Binding
SoBrepFaceSet::findMaterialBinding(SoState * const state) const
{
Binding binding = OVERALL;
SoMaterialBindingElement::Binding matbind =
SoMaterialBindingElement::get(state);
switch (matbind) {
case SoMaterialBindingElement::OVERALL:
binding = OVERALL;
break;
case SoMaterialBindingElement::PER_VERTEX:
binding = PER_VERTEX;
break;
case SoMaterialBindingElement::PER_VERTEX_INDEXED:
binding = PER_VERTEX_INDEXED;
break;
case SoMaterialBindingElement::PER_PART:
binding = PER_PART;
break;
case SoMaterialBindingElement::PER_FACE:
binding = PER_FACE;
break;
case SoMaterialBindingElement::PER_PART_INDEXED:
binding = PER_PART_INDEXED;
break;
case SoMaterialBindingElement::PER_FACE_INDEXED:
binding = PER_FACE_INDEXED;
break;
default:
break;
}
return binding;
}
SoBrepFaceSet::Binding
SoBrepFaceSet::findNormalBinding(SoState * const state) const
{
Binding binding = PER_VERTEX_INDEXED;
SoNormalBindingElement::Binding normbind =
(SoNormalBindingElement::Binding) SoNormalBindingElement::get(state);
switch (normbind) {
case SoNormalBindingElement::OVERALL:
binding = OVERALL;
break;
case SoNormalBindingElement::PER_VERTEX:
binding = PER_VERTEX;
break;
case SoNormalBindingElement::PER_VERTEX_INDEXED:
binding = PER_VERTEX_INDEXED;
break;
case SoNormalBindingElement::PER_PART:
binding = PER_PART;
break;
case SoNormalBindingElement::PER_FACE:
binding = PER_FACE;
break;
case SoNormalBindingElement::PER_PART_INDEXED:
binding = PER_PART_INDEXED;
break;
case SoNormalBindingElement::PER_FACE_INDEXED:
binding = PER_FACE_INDEXED;
break;
default:
break;
}
return binding;
}
#undef PRIVATE