Files
create/src/Mod/Mesh/Gui/SoFCIndexedFaceSet.cpp

970 lines
31 KiB
C++

/***************************************************************************
* Copyright (c) 2009 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 "PreCompiled.h"
#ifndef _PreComp_
# include <algorithm>
# ifdef FC_OS_MACOSX
# include <OpenGL/gl.h>
# include <OpenGL/glu.h>
# include <OpenGL/glext.h>
# else
# include <GL/gl.h>
# include <GL/glu.h>
# include <GL/glext.h>
# endif
# include <Inventor/actions/SoGLRenderAction.h>
# include <Inventor/bundles/SoMaterialBundle.h>
# include <Inventor/elements/SoCoordinateElement.h>
# include <Inventor/elements/SoGLCoordinateElement.h>
# include <Inventor/elements/SoGLLazyElement.h>
# include <Inventor/elements/SoMaterialBindingElement.h>
# include <Inventor/elements/SoNormalBindingElement.h>
# include <Inventor/elements/SoProjectionMatrixElement.h>
# include <Inventor/elements/SoViewingMatrixElement.h>
# include <Inventor/errors/SoDebugError.h>
#endif
#include <Inventor/C/glue/gl.h>
#include <Gui/SoFCInteractiveElement.h>
#include <Gui/SoFCSelectionAction.h>
#include "SoFCIndexedFaceSet.h"
#include <QtOpenGL.h>
#ifdef HAVE_QT5_OPENGL
#define RENDER_GL_VAO
#endif
//#define RENDER_GLARRAYS
#ifdef RENDER_GL_VAO
#include <QOpenGLBuffer>
#include <QOpenGLFunctions>
#endif
using namespace MeshGui;
#if defined RENDER_GL_VAO
class MeshRenderer::Private : protected QOpenGLFunctions {
public:
QOpenGLBuffer vertices;
QOpenGLBuffer indices;
const SbColor * pcolors;
SoMaterialBindingElement::Binding matbinding;
Private();
void generateGLArrays(SoMaterialBindingElement::Binding matbind,
std::vector<float>& vertex, std::vector<int32_t>& index);
void renderFacesGLArray(SoGLRenderAction*);
void renderCoordsGLArray(SoGLRenderAction *action);
};
MeshRenderer::Private::Private()
: vertices(QOpenGLBuffer::VertexBuffer)
, indices(QOpenGLBuffer::IndexBuffer)
, pcolors(0)
, matbinding(SoMaterialBindingElement::OVERALL)
{
initializeOpenGLFunctions();
vertices.create();
indices.create();
vertices.setUsagePattern(QOpenGLBuffer::StaticDraw);
indices.setUsagePattern(QOpenGLBuffer::StaticDraw);
}
void MeshRenderer::Private::generateGLArrays(SoMaterialBindingElement::Binding matbind,
std::vector<float>& vertex, std::vector<int32_t>& index)
{
if (vertex.empty() || index.empty())
return;
vertices.bind();
vertices.allocate(&(vertex[0]),
vertex.size() * sizeof(float));
vertices.release();
indices.bind();
indices.allocate(&(index[0]),
index.size() * sizeof(int32_t));
indices.release();
this->matbinding = matbind;
}
void MeshRenderer::Private::renderFacesGLArray(SoGLRenderAction *)
{
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
vertices.bind();
indices.bind();
if (matbinding != SoMaterialBindingElement::OVERALL)
glInterleavedArrays(GL_C4F_N3F_V3F, 0, 0);
else
glInterleavedArrays(GL_N3F_V3F, 0, 0);
glDrawElements(GL_TRIANGLES, indices.size() / sizeof(uint32_t),
GL_UNSIGNED_INT, NULL);
vertices.release();
indices.release();
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
}
void MeshRenderer::Private::renderCoordsGLArray(SoGLRenderAction *)
{
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
vertices.bind();
indices.bind();
if (matbinding != SoMaterialBindingElement::OVERALL)
glInterleavedArrays(GL_C4F_N3F_V3F, 0, 0);
else
glInterleavedArrays(GL_N3F_V3F, 0, 0);
glDrawElements(GL_POINTS, indices.size() / sizeof(uint32_t),
GL_UNSIGNED_INT, NULL);
vertices.release();
indices.release();
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
}
#elif defined RENDER_GLARRAYS
class MeshRenderer::Private {
public:
std::vector<int32_t> index_array;
std::vector<float> vertex_array;
const SbColor * pcolors;
SoMaterialBindingElement::Binding matbinding;
Private()
: pcolors(0)
, matbinding(SoMaterialBindingElement::OVERALL)
{
}
void generateGLArrays(SoMaterialBindingElement::Binding matbind,
std::vector<float>& vertex, std::vector<int32_t>& index);
void renderFacesGLArray(SoGLRenderAction *action);
void renderCoordsGLArray(SoGLRenderAction *action);
};
void MeshRenderer::Private::generateGLArrays(SoMaterialBindingElement::Binding matbind,
std::vector<float>& vertex, std::vector<int32_t>& index)
{
if (vertex.empty() || index.empty())
return;
this->index_array.resize(0);
this->vertex_array.resize(0);
this->index_array.swap(index);
this->vertex_array.swap(vertex);
this->matbinding = matbind;
}
void MeshRenderer::Private::renderFacesGLArray(SoGLRenderAction *action)
{
#if 0 // use Inventor's coordIndex saves memory but the rendering is very slow then
const cc_glglue * glue = cc_glglue_instance(action->getCacheContext());
PFNGLPRIMITIVERESTARTINDEXPROC glPrimitiveRestartIndex = (PFNGLPRIMITIVERESTARTINDEXPROC)
cc_glglue_getprocaddress(glue, "glPrimitiveRestartIndex");
int cnt = coordIndex.getNum();
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
// https://www.opengl.org/discussion_boards/archive/index.php/t-180767.html
// https://www.khronos.org/opengl/wiki/Vertex_Rendering#Primitive_Restart
glPrimitiveRestartIndex(0xffffffff);
glEnable(GL_PRIMITIVE_RESTART);
//glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
glInterleavedArrays(GL_N3F_V3F, 0, &(vertex_array[0]));
glDrawElements(GL_TRIANGLES, cnt, GL_UNSIGNED_INT, coordIndex.getValues(0));
glDisable(GL_PRIMITIVE_RESTART);
//glDisable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
#else // Needs more memory but makes it very fast
(void)action;
int cnt = index_array.size();
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
if (matbinding != SoMaterialBindingElement::OVERALL)
glInterleavedArrays(GL_C4F_N3F_V3F, 0, &(vertex_array[0]));
else
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);
#endif
}
void MeshRenderer::Private::renderCoordsGLArray(SoGLRenderAction *)
{
int cnt = index_array.size();
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
if (matbinding != SoMaterialBindingElement::OVERALL)
glInterleavedArrays(GL_C4F_N3F_V3F, 0, &(vertex_array[0]));
else
glInterleavedArrays(GL_N3F_V3F, 0, &(vertex_array[0]));
glDrawElements(GL_POINTS, cnt, GL_UNSIGNED_INT, &(index_array[0]));
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
}
#else
class MeshRenderer::Private {
public:
const SbColor * pcolors;
SoMaterialBindingElement::Binding matbinding;
Private()
: pcolors(0)
, matbinding(SoMaterialBindingElement::OVERALL)
{
}
void generateGLArrays(SoMaterialBindingElement::Binding,
std::vector<float>&, std::vector<int32_t>&)
{
}
void renderFacesGLArray(SoGLRenderAction *)
{
}
void renderCoordsGLArray(SoGLRenderAction *)
{
}
};
#endif
MeshRenderer::MeshRenderer()
: p(new Private)
{
}
MeshRenderer::~MeshRenderer()
{
delete p;
}
void MeshRenderer::generateGLArrays(SoState* state, SoMaterialBindingElement::Binding matbind,
std::vector<float>& vertex, std::vector<int32_t>& index)
{
SoGLLazyElement* gl = SoGLLazyElement::getInstance(state);
if (gl) {
p->pcolors = gl->getDiffusePointer();
}
p->generateGLArrays(matbind, vertex, index);
}
// Implementation | FPS
// ================================================
// drawCoords (every 4th vertex) | 20.0
// renderCoordsGLArray (all vertexes) | 20.0
//
void MeshRenderer::renderCoordsGLArray(SoGLRenderAction *action)
{
p->renderCoordsGLArray(action);
}
//****************************************************************************
// renderFacesGLArray: normal and coord from vertex_array;
// no texture, color, highlight or selection but highest possible speed;
// all vertices written in one go!
//
// Benchmark tests with an 256 MB STL file:
//
// Implementation | FPS
// ================================================
// OpenInventor (SoIndexedFaceSet) | 3.0
// Custom OpenInventor (SoFCMeshObjectShape) | 8.5
// With GL_PRIMITIVE_RESTART | 0.9
// With GL_PRIMITIVE_RESTART_FIXED_INDEX | 0.9
// Without GL_PRIMITIVE_RESTART | 8.5
// Vertex-Array-Object (RENDER_GL_VAO) | 60.0
void MeshRenderer::renderFacesGLArray(SoGLRenderAction *action)
{
p->renderFacesGLArray(action);
}
bool MeshRenderer::matchMaterial(SoState* state) const
{
SoMaterialBindingElement::Binding matbind =
SoMaterialBindingElement::get(state);
const SbColor * pcolors = 0;
SoGLLazyElement* gl = SoGLLazyElement::getInstance(state);
if (gl) {
pcolors = gl->getDiffusePointer();
}
return p->matbinding == matbind && p->pcolors == pcolors;
}
bool MeshRenderer::shouldRenderDirectly(bool direct)
{
#ifdef RENDER_GL_VAO
Q_UNUSED(direct);
return false;
#else
return direct;
#endif
}
// ----------------------------------------------------------------------------
SO_ENGINE_SOURCE(SoFCMaterialEngine);
SoFCMaterialEngine::SoFCMaterialEngine()
{
SO_ENGINE_CONSTRUCTOR(SoFCMaterialEngine);
SO_ENGINE_ADD_INPUT(diffuseColor, (SbColor(0.0, 0.0, 0.0)));
SO_ENGINE_ADD_OUTPUT(trigger, SoSFBool);
}
SoFCMaterialEngine::~SoFCMaterialEngine()
{
}
void SoFCMaterialEngine::initClass()
{
SO_ENGINE_INIT_CLASS(SoFCMaterialEngine, SoEngine, "Engine");
}
void SoFCMaterialEngine::inputChanged(SoField *)
{
SO_ENGINE_OUTPUT(trigger, SoSFBool, setValue(true));
}
void SoFCMaterialEngine::evaluate()
{
// do nothing here
}
// ----------------------------------------------------------------------------
SO_NODE_SOURCE(SoFCIndexedFaceSet);
void SoFCIndexedFaceSet::initClass()
{
SO_NODE_INIT_CLASS(SoFCIndexedFaceSet, SoIndexedFaceSet, "IndexedFaceSet");
}
SoFCIndexedFaceSet::SoFCIndexedFaceSet()
: renderTriangleLimit(100000)
, selectBuf(0)
{
SO_NODE_CONSTRUCTOR(SoFCIndexedFaceSet);
SO_NODE_ADD_FIELD(updateGLArray, (false));
updateGLArray.setFieldType(SoField::EVENTOUT_FIELD);
setName(SoFCIndexedFaceSet::getClassTypeId().getName());
}
/**
* Either renders the complete mesh or only a subset of the points.
*/
void SoFCIndexedFaceSet::GLRender(SoGLRenderAction *action)
{
if (this->coordIndex.getNum() < 3)
return;
if (!this->shouldGLRender(action)) {
// Transparency is handled inside 'shouldGLRender' but the base class
// somehow misses to reset the blending mode. This causes SoGLLazyElement
// not to switch on and off GL_BLEND mode and thus transparency doesn't
// work as expected. Calling SoMaterialBundle::sendFirst seems to fix the
// problem.
SoMaterialBundle mb(action);
mb.sendFirst();
return;
}
SoState * state = action->getState();
SbBool mode = Gui::SoFCInteractiveElement::get(state);
#if defined(RENDER_GL_VAO)
if (mode == false || render.matchMaterial(state)) {
#else
unsigned int num = this->coordIndex.getNum()/4;
if (mode == false || num <= this->renderTriangleLimit) {
#endif
#if defined (RENDER_GLARRAYS) || defined(RENDER_GL_VAO)
#if 0
SoMaterialBindingElement::Binding matbind =
SoMaterialBindingElement::get(state);
if (matbind == SoMaterialBindingElement::OVERALL) {
SoMaterialBundle mb(action);
mb.sendFirst();
if (updateGLArray) {
updateGLArray = false;
generateGLArrays(state);
}
render.renderFacesGLArray(action);
}
else {
inherited::GLRender(action);
}
#else
if (updateGLArray.getValue()) {
updateGLArray = false;
generateGLArrays(state);
}
if (render.matchMaterial(state)) {
SoMaterialBundle mb(action);
mb.sendFirst();
render.renderFacesGLArray(action);
}
else {
inherited::GLRender(action);
}
#endif
#else
inherited::GLRender(action);
#endif
}
else {
#if 0 && defined (RENDER_GLARRAYS)
SoMaterialBundle mb(action);
mb.sendFirst();
render.renderCoordsGLArray(action);
#else
SoMaterialBindingElement::Binding matbind =
SoMaterialBindingElement::get(state);
int32_t binding = (int32_t)(matbind);
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 normalCacheUsed;
SoMaterialBundle mb(action);
SoTextureCoordinateBundle tb(action, true, false);
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
drawCoords(static_cast<const SoGLCoordinateElement*>(coords), cindices, numindices,
normals, nindices, &mb, mindices, binding, &tb, tindices);
// Disable caching for this node
SoGLCacheContextElement::shouldAutoCache(state, SoGLCacheContextElement::DONT_AUTO_CACHE);
#endif
}
}
void SoFCIndexedFaceSet::drawCoords(const SoGLCoordinateElement * const vertexlist,
const int32_t *vertexindices,
int numindices,
const SbVec3f *normals,
const int32_t *normalindices,
SoMaterialBundle *materials,
const int32_t * /*matindices*/,
const int32_t binding,
const SoTextureCoordinateBundle * const /*texcoords*/,
const int32_t * /*texindices*/)
{
const SbVec3f * coords3d = 0;
coords3d = vertexlist->getArrayPtr3();
int mod = numindices/(4*this->renderTriangleLimit)+1;
float size = std::min<float>((float)mod,3.0f);
glPointSize(size);
SbBool per_face = false;
SbBool per_vert = false;
switch (binding) {
case SoMaterialBindingElement::PER_FACE:
per_face = true;
break;
case SoMaterialBindingElement::PER_VERTEX:
per_vert = true;
break;
default:
break;
}
int ct=0;
const int32_t *viptr = vertexindices;
int32_t v1, v2, v3;
SbVec3f dummynormal(0,0,1);
const SbVec3f *currnormal = &dummynormal;
if (normals) currnormal = normals;
glBegin(GL_POINTS);
for (int index=0; index<numindices; ct++) {
if (ct%mod==0) {
if (per_face)
materials->send(ct, true);
v1 = *viptr++; index++;
if (per_vert)
materials->send(v1, true);
if (normals)
currnormal = &normals[*normalindices++];
glNormal3fv((const GLfloat*)currnormal);
glVertex3fv((const GLfloat*)(coords3d + v1));
v2 = *viptr++; index++;
if (per_vert)
materials->send(v2, true);
if (normals)
currnormal = &normals[*normalindices++];
glNormal3fv((const GLfloat*)currnormal);
glVertex3fv((const GLfloat*)(coords3d + v2));
v3 = *viptr++; index++;
if (per_vert)
materials->send(v3, true);
if (normals)
currnormal = &normals[*normalindices++];
glNormal3fv((const GLfloat*)currnormal);
glVertex3fv((const GLfloat*)(coords3d + v3));
}
else {
viptr++; index++; normalindices++;
viptr++; index++; normalindices++;
viptr++; index++; normalindices++;
}
viptr++; index++; normalindices++;
}
glEnd();
}
void SoFCIndexedFaceSet::invalidate()
{
updateGLArray = true;
}
void SoFCIndexedFaceSet::generateGLArrays(SoState * state)
{
const SoCoordinateElement * coords;
const SbVec3f * normals;
const int32_t * cindices;
const SbColor * pcolors = 0;
const float * transp = 0;
int numindices, numcolors = 0, numtransp = 0;
const int32_t * nindices;
const int32_t * tindices;
const int32_t * mindices;
SbBool normalCacheUsed;
SbBool sendNormals = true;
this->getVertexData(state, coords, normals, cindices,
nindices, tindices, mindices, numindices,
sendNormals, normalCacheUsed);
const SbVec3f * points = coords->getArrayPtr3();
SoMaterialBindingElement::Binding matbind =
SoMaterialBindingElement::get(state);
SoGLLazyElement* gl = SoGLLazyElement::getInstance(state);
if (gl) {
pcolors = gl->getDiffusePointer();
numcolors = gl->getNumDiffuse();
transp = gl->getTransparencyPointer();
numtransp = gl->getNumTransparencies();
Q_UNUSED(numtransp);
}
std::vector<float> face_vertices;
std::vector<int32_t> face_indices;
std::size_t numTria = numindices / 4;
if (!mindices && matbind == SoMaterialBindingElement::PER_VERTEX_INDEXED) {
mindices = cindices;
}
SoNormalBindingElement::Binding normbind = SoNormalBindingElement::get(state);
if (normbind == SoNormalBindingElement::PER_VERTEX_INDEXED) {
if (matbind == SoMaterialBindingElement::PER_FACE) {
face_vertices.reserve(3 * numTria * 10); // duplicate each vertex (rgba, normal, vertex)
face_indices.resize(3 * numTria);
if (numcolors != static_cast<int>(numTria)) {
SoDebugError::postWarning("SoFCIndexedFaceSet::generateGLArrays",
"The number of faces (%d) doesn't match with the number of colors (%d).", numTria, numcolors);
}
// the nindices must have the length of numindices
int32_t vertex = 0;
int index = 0;
float t = transp[0];
for (std::size_t i=0; i<numTria; i++) {
const SbColor& c = pcolors[i];
for (int j=0; j<3; j++) {
face_vertices.push_back(c[0]);
face_vertices.push_back(c[1]);
face_vertices.push_back(c[2]);
face_vertices.push_back(t);
const SbVec3f& n = normals[nindices[index]];
face_vertices.push_back(n[0]);
face_vertices.push_back(n[1]);
face_vertices.push_back(n[2]);
const SbVec3f& p = points[cindices[index]];
face_vertices.push_back(p[0]);
face_vertices.push_back(p[1]);
face_vertices.push_back(p[2]);
face_indices[vertex] = vertex;
vertex++;
index++;
}
index++;
}
}
else if (matbind == SoMaterialBindingElement::PER_VERTEX_INDEXED) {
face_vertices.reserve(3 * numTria * 10); // duplicate each vertex (rgba, normal, vertex)
face_indices.resize(3 * numTria);
if (numcolors != coords->getNum()) {
SoDebugError::postWarning("SoFCIndexedFaceSet::generateGLArrays",
"The number of points (%d) doesn't match with the number of colors (%d).", coords->getNum(), numcolors);
}
// the nindices must have the length of numindices
int32_t vertex = 0;
int index = 0;
float t = transp[0];
for (std::size_t i=0; i<numTria; i++) {
for (int j=0; j<3; j++) {
const SbColor& c = pcolors[mindices[index]];
face_vertices.push_back(c[0]);
face_vertices.push_back(c[1]);
face_vertices.push_back(c[2]);
face_vertices.push_back(t);
const SbVec3f& n = normals[nindices[index]];
face_vertices.push_back(n[0]);
face_vertices.push_back(n[1]);
face_vertices.push_back(n[2]);
const SbVec3f& p = points[cindices[index]];
face_vertices.push_back(p[0]);
face_vertices.push_back(p[1]);
face_vertices.push_back(p[2]);
face_indices[vertex] = vertex;
vertex++;
index++;
}
index++;
}
}
else {
// only an overall material
matbind = SoMaterialBindingElement::OVERALL;
face_vertices.reserve(3 * numTria * 6); // duplicate each vertex (normal, vertex)
face_indices.resize(3 * numTria);
// the nindices must have the length of numindices
int32_t vertex = 0;
int index = 0;
for (std::size_t i=0; i<numTria; i++) {
for (int j=0; j<3; j++) {
const SbVec3f& n = normals[nindices[index]];
face_vertices.push_back(n[0]);
face_vertices.push_back(n[1]);
face_vertices.push_back(n[2]);
const SbVec3f& p = points[cindices[index]];
face_vertices.push_back(p[0]);
face_vertices.push_back(p[1]);
face_vertices.push_back(p[2]);
face_indices[vertex] = vertex;
vertex++;
index++;
}
index++;
}
}
}
else if (normbind == SoNormalBindingElement::PER_VERTEX) {
// only an overall material
matbind = SoMaterialBindingElement::OVERALL;
std::size_t numPts = coords->getNum();
face_vertices.reserve(6 * numPts);
for (std::size_t i=0; i<numPts; i++) {
const SbVec3f& n = normals[i];
face_vertices.push_back(n[0]);
face_vertices.push_back(n[1]);
face_vertices.push_back(n[2]);
const SbVec3f& p = coords->get3(i);
face_vertices.push_back(p[0]);
face_vertices.push_back(p[1]);
face_vertices.push_back(p[2]);
}
face_indices.reserve(3 * numTria);
int index = 0;
for (std::size_t i=0; i<numTria; i++) {
for (int j=0; j<3; j++) {
face_indices.push_back(cindices[index]);
index++;
}
index++;
}
}
render.generateGLArrays(state, matbind, face_vertices, face_indices);
}
void SoFCIndexedFaceSet::doAction(SoAction * action)
{
if (action->getTypeId() == Gui::SoGLSelectAction::getClassTypeId()) {
SoNode* node = action->getNodeAppliedTo();
if (!node) return; // on no node applied
// The node we have is the parent of this node and the coordinate node
// thus we search there for it.
SoSearchAction sa;
sa.setInterest(SoSearchAction::FIRST);
sa.setSearchingAll(false);
sa.setType(SoCoordinate3::getClassTypeId(), 1);
sa.apply(node);
SoPath * path = sa.getPath();
if (!path) return;
// make sure we got the node we wanted
SoNode* coords = path->getNodeFromTail(0);
if (!(coords && coords->getTypeId().isDerivedFrom(SoCoordinate3::getClassTypeId())))
return;
startSelection(action);
renderSelectionGeometry(static_cast<SoCoordinate3*>(coords)->point.getValues(0));
stopSelection(action);
}
else if (action->getTypeId() == Gui::SoVisibleFaceAction::getClassTypeId()) {
SoNode* node = action->getNodeAppliedTo();
if (!node) return; // on no node applied
// The node we have is the parent of this node and the coordinate node
// thus we search there for it.
SoSearchAction sa;
sa.setInterest(SoSearchAction::FIRST);
sa.setSearchingAll(false);
sa.setType(SoCoordinate3::getClassTypeId(), 1);
sa.apply(node);
SoPath * path = sa.getPath();
if (!path) return;
// make sure we got the node we wanted
SoNode* coords = path->getNodeFromTail(0);
if (!(coords && coords->getTypeId().isDerivedFrom(SoCoordinate3::getClassTypeId())))
return;
startVisibility(action);
renderVisibleFaces(static_cast<SoCoordinate3*>(coords)->point.getValues(0));
stopVisibility(action);
}
inherited::doAction(action);
}
void SoFCIndexedFaceSet::startSelection(SoAction * action)
{
Gui::SoGLSelectAction *doaction = static_cast<Gui::SoGLSelectAction*>(action);
const SbViewportRegion& vp = doaction->getViewportRegion();
int x = vp.getViewportOriginPixels()[0];
int y = vp.getViewportOriginPixels()[1];
int w = vp.getViewportSizePixels()[0];
int h = vp.getViewportSizePixels()[1];
int bufSize = 5*(this->coordIndex.getNum()/4); // make the buffer big enough
this->selectBuf = new GLuint[bufSize];
SbMatrix view = SoViewingMatrixElement::get(action->getState());
SbMatrix proj = SoProjectionMatrixElement::get(action->getState());
glSelectBuffer(bufSize, selectBuf);
glRenderMode(GL_SELECT);
glInitNames();
glPushName(-1);
//double mp[16];
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT,viewport);
glMatrixMode(GL_PROJECTION);
//glGetDoublev(GL_PROJECTION_MATRIX ,mp);
glPushMatrix();
glLoadIdentity();
// See https://www.opengl.org/discussion_boards/showthread.php/184308-gluPickMatrix-Implementation?p=1259884&viewfull=1#post1259884
//gluPickMatrix(x, y, w, h, viewport);
if (w > 0 && h > 0) {
glTranslatef((viewport[2] - 2 * (x - viewport[0])) / w, (viewport[3] - 2 * (y - viewport[1])) / h, 0);
glScalef(viewport[2] / w, viewport[3] / h, 1.0);
}
glMultMatrixf(/*mp*/(float*)proj);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadMatrixf((float*)view);
}
void SoFCIndexedFaceSet::stopSelection(SoAction * action)
{
// restoring the original projection matrix
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glFlush();
// returning to normal rendering mode
GLint hits = glRenderMode(GL_RENDER);
int bufSize = 5*(this->coordIndex.getNum()/4);
std::vector< std::pair<double,unsigned int> > hit;
GLint index=0;
for (GLint ii=0;ii<hits && index<bufSize;ii++) {
GLint ct = (GLint)selectBuf[index];
hit.push_back(std::pair<double,unsigned int>
(selectBuf[index+1]/4294967295.0,selectBuf[index+3]));
index = index+ct+3;
}
delete [] selectBuf;
selectBuf = 0;
bool sorted = true;
if(sorted) std::sort(hit.begin(),hit.end());
Gui::SoGLSelectAction *doaction = static_cast<Gui::SoGLSelectAction*>(action);
doaction->indices.reserve(hit.size());
for (GLint ii=0;ii<hits;ii++) {
doaction->indices.push_back(hit[ii].second);
}
}
void SoFCIndexedFaceSet::renderSelectionGeometry(const SbVec3f * coords3d)
{
int numfaces = this->coordIndex.getNum()/4;
const int32_t * cindices = this->coordIndex.getValues(0);
int fcnt=0;
int32_t v1, v2, v3;
for (int index=0; index<numfaces;index++,cindices++) {
glLoadName(fcnt);
glBegin(GL_TRIANGLES);
v1 = *cindices++;
glVertex3fv((const GLfloat*)(coords3d + v1));
v2 = *cindices++;
glVertex3fv((const GLfloat*)(coords3d + v2));
v3 = *cindices++;
glVertex3fv((const GLfloat*)(coords3d + v3));
glEnd();
fcnt++;
}
}
void SoFCIndexedFaceSet::startVisibility(SoAction * action)
{
SbMatrix view = SoViewingMatrixElement::get(action->getState());
SbMatrix proj = SoProjectionMatrixElement::get(action->getState());
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glMultMatrixf((float*)proj);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadMatrixf((float*)view);
}
void SoFCIndexedFaceSet::stopVisibility(SoAction * /*action*/)
{
// restoring the original projection matrix
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glFlush();
}
void SoFCIndexedFaceSet::renderVisibleFaces(const SbVec3f * coords3d)
{
//GLint redBits, greenBits, blueBits;
//glGetIntegerv (GL_RED_BITS, &redBits);
//glGetIntegerv (GL_GREEN_BITS, &greenBits);
//glGetIntegerv (GL_BLUE_BITS, &blueBits);
glDisable (GL_BLEND);
glDisable (GL_DITHER);
glDisable (GL_FOG);
glDisable (GL_LIGHTING);
glDisable (GL_TEXTURE_1D);
glDisable (GL_TEXTURE_2D);
glShadeModel (GL_FLAT);
uint32_t numfaces = this->coordIndex.getNum()/4;
const int32_t * cindices = this->coordIndex.getValues(0);
int32_t v1, v2, v3;
for (uint32_t index=0; index<numfaces;index++,cindices++) {
glBegin(GL_TRIANGLES);
float t;
SbColor c;
c.setPackedValue(index<<8,t);
glColor3f(c[0],c[1],c[2]);
v1 = *cindices++;
glVertex3fv((const GLfloat*)(coords3d + v1));
v2 = *cindices++;
glVertex3fv((const GLfloat*)(coords3d + v2));
v3 = *cindices++;
glVertex3fv((const GLfloat*)(coords3d + v3));
glEnd();
}
}