Files
create/src/Mod/Mesh/Gui/SoFCMeshObject.cpp
2011-10-10 13:44:52 +00:00

1692 lines
55 KiB
C++

/***************************************************************************
* Copyright (c) 2006 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_WIN32
# include <windows.h>
# endif
# ifdef FC_OS_MACOSX
# include <OpenGL/gl.h>
# include <OpenGL/glu.h>
# else
# include <GL/gl.h>
# include <GL/glu.h>
# endif
# include <Inventor/actions/SoCallbackAction.h>
# include <Inventor/actions/SoGetBoundingBoxAction.h>
# include <Inventor/actions/SoGetPrimitiveCountAction.h>
# include <Inventor/actions/SoGLRenderAction.h>
# include <Inventor/actions/SoPickAction.h>
# include <Inventor/actions/SoWriteAction.h>
# include <Inventor/details/SoFaceDetail.h>
# include <Inventor/errors/SoReadError.h>
# include <Inventor/misc/SoState.h>
#endif
#include "SoFCMeshObject.h"
#include <Base/Console.h>
#include <Base/Exception.h>
#include <Gui/SoFCInteractiveElement.h>
#include <Gui/SoFCSelectionAction.h>
#include <Mod/Mesh/App/Core/Algorithm.h>
#include <Mod/Mesh/App/Core/MeshIO.h>
#include <Mod/Mesh/App/Core/MeshKernel.h>
#include <Mod/Mesh/App/Core/Elements.h>
#include <Mod/Mesh/App/Core/Grid.h>
using namespace MeshGui;
class SoOutputStreambuf : public std::streambuf
{
public:
SoOutputStreambuf(SoOutput* o) : out(o)
{
}
protected:
int overflow(int c = EOF)
{
if (c != EOF) {
char z = static_cast<char>(c);
out->write(z);
}
return c;
}
std::streamsize xsputn (const char* s, std::streamsize num)
{
out->write(s);
return num;
}
private:
SoOutput* out;
};
class SoOutputStream : public std::ostream
{
public:
SoOutputStream(SoOutput* o) : std::ostream(0), buf(o)
{
this->rdbuf(&buf);
}
private:
SoOutputStreambuf buf;
};
class SoInputStreambuf : public std::streambuf
{
public:
SoInputStreambuf(SoInput* o) : inp(o)
{
setg (buffer+pbSize,
buffer+pbSize,
buffer+pbSize);
}
protected:
int underflow()
{
if (gptr() < egptr()) {
return *gptr();
}
int numPutback;
numPutback = gptr() - eback();
if (numPutback > pbSize) {
numPutback = pbSize;
}
memcpy (buffer+(pbSize-numPutback), gptr()-numPutback, numPutback);
int num=0;
for (int i=0; i<bufSize; i++) {
char c;
SbBool ok = inp->get(c);
if (ok) {
num++;
buffer[pbSize+i] = c;
if (c == '\n')
break;
}
else if (num==0) {
return EOF;
}
}
setg (buffer+(pbSize-numPutback),
buffer+pbSize,
buffer+pbSize+num);
return *gptr();
}
private:
static const int pbSize = 4;
static const int bufSize = 1024;
char buffer[bufSize+pbSize];
SoInput* inp;
};
class SoInputStream : public std::istream
{
public:
SoInputStream(SoInput* o) : std::istream(0), buf(o)
{
this->rdbuf(&buf);
}
~SoInputStream()
{
}
private:
SoInputStreambuf buf;
};
// Defines all required member variables and functions for a
// single-value field
SO_SFIELD_SOURCE(SoSFMeshObject, const Mesh::MeshObject*, const Mesh::MeshObject*);
void SoSFMeshObject::initClass()
{
// This macro takes the name of the class and the name of the
// parent class
SO_SFIELD_INIT_CLASS(SoSFMeshObject, SoSField);
}
// This reads the value of a field from a file. It returns FALSE if the value could not be read
// successfully.
SbBool SoSFMeshObject::readValue(SoInput *in)
{
if (!in->isBinary()) {
SoInputStream str(in);
MeshCore::MeshKernel kernel;
MeshCore::MeshInput(kernel).LoadMeshNode(str);
value = new Mesh::MeshObject(kernel);
// We need to trigger the notification chain here, as this function
// can be used on a node in a scene graph in any state -- not only
// during initial scene graph import.
this->valueChanged();
return TRUE;
}
int32_t countPt;
in->read(countPt);
std::vector<float> verts(countPt);
in->readBinaryArray(&(verts[0]),countPt);
MeshCore::MeshPointArray rPoints;
rPoints.reserve(countPt/3);
for (std::vector<float>::iterator it = verts.begin();
it != verts.end();) {
Base::Vector3f p;
p.x = *it; it++;
p.y = *it; it++;
p.z = *it; it++;
rPoints.push_back(p);
}
int32_t countFt;
in->read(countFt);
std::vector<int32_t> faces(countFt);
in->readBinaryArray(&(faces[0]),countFt);
MeshCore::MeshFacetArray rFacets;
rFacets.reserve(countFt/3);
for (std::vector<int32_t>::iterator it = faces.begin();
it != faces.end();) {
MeshCore::MeshFacet f;
f._aulPoints[0] = *it; it++;
f._aulPoints[1] = *it; it++;
f._aulPoints[2] = *it; it++;
rFacets.push_back(f);
}
MeshCore::MeshKernel kernel;
kernel.Adopt(rPoints, rFacets, true);
value = new Mesh::MeshObject(kernel);
// We need to trigger the notification chain here, as this function
// can be used on a node in a scene graph in any state -- not only
// during initial scene graph import.
this->valueChanged();
return TRUE;
}
// This writes the value of a field to a file.
void SoSFMeshObject::writeValue(SoOutput *out) const
{
if (!out->isBinary()) {
SoOutputStream str(out);
MeshCore::MeshOutput(value->getKernel()).SaveMeshNode(str);
return;
}
if (!value) {
int32_t count = 0;
out->write(count);
out->write(count);
return;
}
const MeshCore::MeshPointArray& rPoints = value->getKernel().GetPoints();
std::vector<float> verts;
verts.reserve(3*rPoints.size());
for (MeshCore::MeshPointArray::_TConstIterator it = rPoints.begin();
it != rPoints.end(); ++it) {
verts.push_back(it->x);
verts.push_back(it->y);
verts.push_back(it->z);
}
int32_t countPt = (int32_t)verts.size();
out->write(countPt);
out->writeBinaryArray(&(verts[0]),countPt);
const MeshCore::MeshFacetArray& rFacets = value->getKernel().GetFacets();
std::vector<uint32_t> faces;
faces.reserve(3*rFacets.size());
for (MeshCore::MeshFacetArray::_TConstIterator it = rFacets.begin();
it != rFacets.end(); ++it) {
faces.push_back((int32_t)it->_aulPoints[0]);
faces.push_back((int32_t)it->_aulPoints[1]);
faces.push_back((int32_t)it->_aulPoints[2]);
}
int32_t countFt = (int32_t)faces.size();
out->write(countFt);
out->writeBinaryArray((const int32_t*)&(faces[0]),countFt);
}
// -------------------------------------------------------
SO_ELEMENT_SOURCE(SoFCMeshObjectElement);
void SoFCMeshObjectElement::initClass()
{
SO_ELEMENT_INIT_CLASS(SoFCMeshObjectElement, inherited);
}
void SoFCMeshObjectElement::init(SoState * state)
{
inherited::init(state);
this->mesh = 0;
}
SoFCMeshObjectElement::~SoFCMeshObjectElement()
{
}
void SoFCMeshObjectElement::set(SoState * const state, SoNode * const node, const Mesh::MeshObject * const mesh)
{
SoFCMeshObjectElement * elem = (SoFCMeshObjectElement *)
SoReplacedElement::getElement(state, classStackIndex, node);
if (elem) {
elem->mesh = mesh;
elem->nodeId = node->getNodeId();
}
}
const Mesh::MeshObject * SoFCMeshObjectElement::get(SoState * const state)
{
return SoFCMeshObjectElement::getInstance(state)->mesh;
}
const SoFCMeshObjectElement * SoFCMeshObjectElement::getInstance(SoState * state)
{
return (const SoFCMeshObjectElement *) SoElement::getConstElement(state, classStackIndex);
}
void SoFCMeshObjectElement::print(FILE * /* file */) const
{
}
// -------------------------------------------------------
SO_NODE_SOURCE(SoFCMeshPickNode);
/*!
Constructor.
*/
SoFCMeshPickNode::SoFCMeshPickNode(void) : meshGrid(0)
{
SO_NODE_CONSTRUCTOR(SoFCMeshPickNode);
SO_NODE_ADD_FIELD(mesh, (0));
}
/*!
Destructor.
*/
SoFCMeshPickNode::~SoFCMeshPickNode()
{
delete meshGrid;
}
// Doc from superclass.
void SoFCMeshPickNode::initClass(void)
{
SO_NODE_INIT_CLASS(SoFCMeshPickNode, SoNode, "Node");
}
void SoFCMeshPickNode::notify(SoNotList *list)
{
SoField *f = list->getLastField();
if (f == &mesh) {
const Mesh::MeshObject* meshObject = mesh.getValue();
if (meshObject) {
MeshCore::MeshAlgorithm alg(meshObject->getKernel());
float fAvgLen = alg.GetAverageEdgeLength();
delete meshGrid;
meshGrid = new MeshCore::MeshFacetGrid(meshObject->getKernel(), 5.0f * fAvgLen);
}
}
}
// Doc from superclass.
void SoFCMeshPickNode::rayPick(SoRayPickAction * action)
{
}
// Doc from superclass.
void SoFCMeshPickNode::pick(SoPickAction * action)
{
SoRayPickAction* raypick = static_cast<SoRayPickAction*>(action);
raypick->setObjectSpace();
const Mesh::MeshObject* meshObject = mesh.getValue();
MeshCore::MeshAlgorithm alg(meshObject->getKernel());
const SbLine& line = raypick->getLine();
const SbVec3f& pos = line.getPosition();
const SbVec3f& dir = line.getDirection();
Base::Vector3f pt(pos[0],pos[1],pos[2]);
Base::Vector3f dr(dir[0],dir[1],dir[2]);
unsigned long index;
if (alg.NearestFacetOnRay(pt, dr, *meshGrid, pt, index)) {
SoPickedPoint* pp = raypick->addIntersection(SbVec3f(pt.x,pt.y,pt.z));
if (pp) {
SoFaceDetail* det = new SoFaceDetail();
det->setFaceIndex(index);
pp->setDetail(det, this);
}
}
}
// -------------------------------------------------------
SO_NODE_SOURCE(SoFCMeshGridNode);
/*!
Constructor.
*/
SoFCMeshGridNode::SoFCMeshGridNode(void)
{
SO_NODE_CONSTRUCTOR(SoFCMeshGridNode);
SO_NODE_ADD_FIELD(minGrid, (SbVec3f(0,0,0)));
SO_NODE_ADD_FIELD(maxGrid, (SbVec3f(0,0,0)));
SO_NODE_ADD_FIELD(lenGrid, (SbVec3s(0,0,0)));
}
/*!
Destructor.
*/
SoFCMeshGridNode::~SoFCMeshGridNode()
{
}
// Doc from superclass.
void SoFCMeshGridNode::initClass(void)
{
SO_NODE_INIT_CLASS(SoFCMeshGridNode, SoNode, "Node");
}
void SoFCMeshGridNode::GLRender(SoGLRenderAction * action)
{
const SbVec3f& min = minGrid.getValue();
const SbVec3f& max = maxGrid.getValue();
const SbVec3s& len = lenGrid.getValue();
short u,v,w; len.getValue(u,v,w);
float minX, minY, minZ; min.getValue(minX, minY, minZ);
float maxX, maxY, maxZ; max.getValue(maxX, maxY, maxZ);
float dx = (maxX-minX)/(float)u;
float dy = (maxY-minY)/(float)v;
float dz = (maxZ-minZ)/(float)w;
glColor3f(0.0f,1.0f,0.0);
glBegin(GL_LINES);
for (short i=0; i<u+1; i++) {
for (short j=0; j<v+1; j++) {
float p[3];
p[0] = i * dx + minX;
p[1] = j * dy + minY;
p[2] = minZ;
glVertex3fv(p);
p[0] = i * dx + minX;
p[1] = j * dy + minY;
p[2] = maxZ;
glVertex3fv(p);
}
}
for (short i=0; i<u+1; i++) {
for (short j=0; j<w+1; j++) {
float p[3];
p[0] = i * dx + minX;
p[1] = minY;
p[2] = j * dz + minZ;
glVertex3fv(p);
p[0] = i * dx + minX;
p[1] = maxY;
p[2] = j * dz + minZ;
glVertex3fv(p);
}
}
for (short i=0; i<v+1; i++) {
for (short j=0; j<w+1; j++) {
float p[3];
p[0] = minX;
p[1] = i * dy + minY;
p[2] = j * dz + minZ;
glVertex3fv(p);
p[0] = maxX;
p[1] = i * dy + minY;
p[2] = j * dz + minZ;
glVertex3fv(p);
}
}
glEnd();
}
// -------------------------------------------------------
SO_NODE_SOURCE(SoFCMeshObjectNode);
/*!
Constructor.
*/
SoFCMeshObjectNode::SoFCMeshObjectNode(void)
{
SO_NODE_CONSTRUCTOR(SoFCMeshObjectNode);
SO_NODE_ADD_FIELD(mesh, (0));
}
/*!
Destructor.
*/
SoFCMeshObjectNode::~SoFCMeshObjectNode()
{
}
// Doc from superclass.
void SoFCMeshObjectNode::initClass(void)
{
SO_NODE_INIT_CLASS(SoFCMeshObjectNode, SoNode, "Node");
SO_ENABLE(SoGetBoundingBoxAction, SoFCMeshObjectElement);
SO_ENABLE(SoGLRenderAction, SoFCMeshObjectElement);
SO_ENABLE(SoPickAction, SoFCMeshObjectElement);
SO_ENABLE(SoCallbackAction, SoFCMeshObjectElement);
SO_ENABLE(SoGetPrimitiveCountAction, SoFCMeshObjectElement);
}
// Doc from superclass.
void SoFCMeshObjectNode::doAction(SoAction * action)
{
SoFCMeshObjectElement::set(action->getState(), this, mesh.getValue());
}
// Doc from superclass.
void SoFCMeshObjectNode::GLRender(SoGLRenderAction * action)
{
SoFCMeshObjectNode::doAction(action);
}
// Doc from superclass.
void SoFCMeshObjectNode::callback(SoCallbackAction * action)
{
SoFCMeshObjectNode::doAction(action);
}
// Doc from superclass.
void SoFCMeshObjectNode::pick(SoPickAction * action)
{
SoFCMeshObjectNode::doAction(action);
}
// Doc from superclass.
void SoFCMeshObjectNode::getBoundingBox(SoGetBoundingBoxAction * action)
{
SoFCMeshObjectNode::doAction(action);
}
// Doc from superclass.
void SoFCMeshObjectNode::getPrimitiveCount(SoGetPrimitiveCountAction * action)
{
SoFCMeshObjectNode::doAction(action);
}
// Helper functions: draw vertices
inline void glVertex(const MeshCore::MeshPoint& _v)
{
float v[3];
v[0]=_v.x; v[1]=_v.y;v[2]=_v.z;
glVertex3fv(v);
}
// Helper functions: draw normal
inline void glNormal(const Base::Vector3f& _n)
{
float n[3];
n[0]=_n.x; n[1]=_n.y;n[2]=_n.z;
glNormal3fv(n);
}
// Helper functions: draw normal
inline void glNormal(float* n)
{
glNormal3fv(n);
}
// Helper function: convert Vec to SbVec3f
inline SbVec3f sbvec3f(const Base::Vector3f& _v)
{
return SbVec3f(_v.x, _v.y, _v.z);
}
SO_NODE_SOURCE(SoFCMeshObjectShape);
void SoFCMeshObjectShape::initClass()
{
SO_NODE_INIT_CLASS(SoFCMeshObjectShape, SoShape, "Shape");
}
SoFCMeshObjectShape::SoFCMeshObjectShape() : renderTriangleLimit(100000), meshChanged(true)
{
SO_NODE_CONSTRUCTOR(SoFCMeshObjectShape);
setName(SoFCMeshObjectShape::getClassTypeId().getName());
}
void SoFCMeshObjectShape::notify(SoNotList * node)
{
inherited::notify(node);
meshChanged = true;
}
/**
* Either renders the complete mesh or only a subset of the points.
*/
void SoFCMeshObjectShape::GLRender(SoGLRenderAction *action)
{
if (shouldGLRender(action))
{
SoState* state = action->getState();
// Here we must save the model and projection matrices because
// we need them later for picking
glGetFloatv(GL_MODELVIEW_MATRIX, this->modelview);
glGetFloatv(GL_PROJECTION_MATRIX, this->projection);
SbBool mode = Gui::SoFCInteractiveElement::get(state);
const Mesh::MeshObject * mesh = SoFCMeshObjectElement::get(state);
if (!mesh || mesh->countPoints() == 0) return;
Binding mbind = this->findMaterialBinding(state);
SoMaterialBundle mb(action);
//SoTextureCoordinateBundle tb(action, true, false);
SbBool needNormals = !mb.isColorOnly()/* || tb.isFunction()*/;
mb.sendFirst(); // make sure we have the correct material
SbBool ccw = TRUE;
if (SoShapeHintsElement::getVertexOrdering(state) == SoShapeHintsElement::CLOCKWISE)
ccw = FALSE;
if (mode == false || mesh->countFacets() <= this->renderTriangleLimit) {
if (mbind != OVERALL)
drawFaces(mesh, &mb, mbind, needNormals, ccw);
else
drawFaces(mesh, 0, mbind, needNormals, ccw);
}
else {
drawPoints(mesh, needNormals, ccw);
}
// Disable caching for this node
//SoGLCacheContextElement::shouldAutoCache(state, SoGLCacheContextElement::DONT_AUTO_CACHE);
}
}
/**
* Translates current material binding into the internal Binding enum.
*/
SoFCMeshObjectShape::Binding SoFCMeshObjectShape::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_INDEXED;
break;
case SoMaterialBindingElement::PER_VERTEX_INDEXED:
binding = PER_VERTEX_INDEXED;
break;
case SoMaterialBindingElement::PER_PART:
case SoMaterialBindingElement::PER_FACE:
binding = PER_FACE_INDEXED;
break;
case SoMaterialBindingElement::PER_PART_INDEXED:
case SoMaterialBindingElement::PER_FACE_INDEXED:
binding = PER_FACE_INDEXED;
break;
default:
break;
}
return binding;
}
/**
* Renders the triangles of the complete mesh.
* FIXME: Do it the same way as Coin did to have only one implementation which is controled by defines
* FIXME: Implement using different values of transparency for each vertex or face
*/
void SoFCMeshObjectShape::drawFaces(const Mesh::MeshObject * mesh, SoMaterialBundle* mb,
Binding bind, SbBool needNormals, SbBool ccw) const
{
const MeshCore::MeshPointArray & rPoints = mesh->getKernel().GetPoints();
const MeshCore::MeshFacetArray & rFacets = mesh->getKernel().GetFacets();
bool perVertex = (mb && bind == PER_VERTEX_INDEXED);
bool perFace = (mb && bind == PER_FACE_INDEXED);
if (needNormals)
{
glBegin(GL_TRIANGLES);
if (ccw) {
// counterclockwise ordering
for (MeshCore::MeshFacetArray::_TConstIterator it = rFacets.begin(); it != rFacets.end(); ++it)
{
const MeshCore::MeshPoint& v0 = rPoints[it->_aulPoints[0]];
const MeshCore::MeshPoint& v1 = rPoints[it->_aulPoints[1]];
const MeshCore::MeshPoint& v2 = rPoints[it->_aulPoints[2]];
// Calculate the normal n = (v1-v0)x(v2-v0)
float n[3];
n[0] = (v1.y-v0.y)*(v2.z-v0.z)-(v1.z-v0.z)*(v2.y-v0.y);
n[1] = (v1.z-v0.z)*(v2.x-v0.x)-(v1.x-v0.x)*(v2.z-v0.z);
n[2] = (v1.x-v0.x)*(v2.y-v0.y)-(v1.y-v0.y)*(v2.x-v0.x);
if(perFace)
mb->send(it-rFacets.begin(), TRUE);
glNormal(n);
if(perVertex)
mb->send(it->_aulPoints[0], TRUE);
glVertex(v0);
if(perVertex)
mb->send(it->_aulPoints[1], TRUE);
glVertex(v1);
if(perVertex)
mb->send(it->_aulPoints[2], TRUE);
glVertex(v2);
}
}
else {
// clockwise ordering
for (MeshCore::MeshFacetArray::_TConstIterator it = rFacets.begin(); it != rFacets.end(); ++it)
{
const MeshCore::MeshPoint& v0 = rPoints[it->_aulPoints[0]];
const MeshCore::MeshPoint& v1 = rPoints[it->_aulPoints[1]];
const MeshCore::MeshPoint& v2 = rPoints[it->_aulPoints[2]];
// Calculate the normal n = -(v1-v0)x(v2-v0)
float n[3];
n[0] = -((v1.y-v0.y)*(v2.z-v0.z)-(v1.z-v0.z)*(v2.y-v0.y));
n[1] = -((v1.z-v0.z)*(v2.x-v0.x)-(v1.x-v0.x)*(v2.z-v0.z));
n[2] = -((v1.x-v0.x)*(v2.y-v0.y)-(v1.y-v0.y)*(v2.x-v0.x));
glNormal(n);
glVertex(v0);
glVertex(v1);
glVertex(v2);
}
}
glEnd();
}
else {
glBegin(GL_TRIANGLES);
for (MeshCore::MeshFacetArray::_TConstIterator it = rFacets.begin(); it != rFacets.end(); ++it)
{
glVertex(rPoints[it->_aulPoints[0]]);
glVertex(rPoints[it->_aulPoints[1]]);
glVertex(rPoints[it->_aulPoints[2]]);
}
glEnd();
}
}
/**
* Renders the gravity points of a subset of triangles.
*/
void SoFCMeshObjectShape::drawPoints(const Mesh::MeshObject * mesh, SbBool needNormals, SbBool ccw) const
{
const MeshCore::MeshPointArray & rPoints = mesh->getKernel().GetPoints();
const MeshCore::MeshFacetArray & rFacets = mesh->getKernel().GetFacets();
int mod = rFacets.size()/renderTriangleLimit+1;
float size = std::min<float>((float)mod,3.0f);
glPointSize(size);
if (needNormals)
{
glBegin(GL_POINTS);
int ct=0;
if (ccw) {
for (MeshCore::MeshFacetArray::_TConstIterator it = rFacets.begin(); it != rFacets.end(); ++it, ct++)
{
if (ct%mod==0) {
const MeshCore::MeshPoint& v0 = rPoints[it->_aulPoints[0]];
const MeshCore::MeshPoint& v1 = rPoints[it->_aulPoints[1]];
const MeshCore::MeshPoint& v2 = rPoints[it->_aulPoints[2]];
// Calculate the normal n = (v1-v0)x(v2-v0)
float n[3];
n[0] = (v1.y-v0.y)*(v2.z-v0.z)-(v1.z-v0.z)*(v2.y-v0.y);
n[1] = (v1.z-v0.z)*(v2.x-v0.x)-(v1.x-v0.x)*(v2.z-v0.z);
n[2] = (v1.x-v0.x)*(v2.y-v0.y)-(v1.y-v0.y)*(v2.x-v0.x);
// Calculate the center point p=(v0+v1+v2)/3
float p[3];
p[0] = (v0.x+v1.x+v2.x)/3.0f;
p[1] = (v0.y+v1.y+v2.y)/3.0f;
p[2] = (v0.z+v1.z+v2.z)/3.0f;
glNormal3fv(n);
glVertex3fv(p);
}
}
}
else {
for (MeshCore::MeshFacetArray::_TConstIterator it = rFacets.begin(); it != rFacets.end(); ++it, ct++)
{
if (ct%mod==0) {
const MeshCore::MeshPoint& v0 = rPoints[it->_aulPoints[0]];
const MeshCore::MeshPoint& v1 = rPoints[it->_aulPoints[1]];
const MeshCore::MeshPoint& v2 = rPoints[it->_aulPoints[2]];
// Calculate the normal n = -(v1-v0)x(v2-v0)
float n[3];
n[0] = -((v1.y-v0.y)*(v2.z-v0.z)-(v1.z-v0.z)*(v2.y-v0.y));
n[1] = -((v1.z-v0.z)*(v2.x-v0.x)-(v1.x-v0.x)*(v2.z-v0.z));
n[2] = -((v1.x-v0.x)*(v2.y-v0.y)-(v1.y-v0.y)*(v2.x-v0.x));
// Calculate the center point p=(v0+v1+v2)/3
float p[3];
p[0] = (v0.x+v1.x+v2.x)/3.0f;
p[1] = (v0.y+v1.y+v2.y)/3.0f;
p[2] = (v0.z+v1.z+v2.z)/3.0f;
glNormal3fv(n);
glVertex3fv(p);
}
}
}
glEnd();
}
else {
glBegin(GL_POINTS);
int ct=0;
for (MeshCore::MeshFacetArray::_TConstIterator it = rFacets.begin(); it != rFacets.end(); ++it, ct++)
{
if (ct%mod==0) {
const MeshCore::MeshPoint& v0 = rPoints[it->_aulPoints[0]];
const MeshCore::MeshPoint& v1 = rPoints[it->_aulPoints[1]];
const MeshCore::MeshPoint& v2 = rPoints[it->_aulPoints[2]];
// Calculate the center point p=(v0+v1+v2)/3
float p[3];
p[0] = (v0.x+v1.x+v2.x)/3.0f;
p[1] = (v0.y+v1.y+v2.y)/3.0f;
p[2] = (v0.z+v1.z+v2.z)/3.0f;
glVertex3fv(p);
}
}
glEnd();
}
}
void SoFCMeshObjectShape::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(SoFCMeshObjectNode::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(SoFCMeshObjectNode::getClassTypeId())))
return;
const Mesh::MeshObject* mesh = static_cast<SoFCMeshObjectNode*>(coords)->mesh.getValue();
startSelection(action, mesh);
renderSelectionGeometry(mesh);
stopSelection(action, mesh);
}
inherited::doAction(action);
}
void SoFCMeshObjectShape::startSelection(SoAction * action, const Mesh::MeshObject* mesh)
{
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];
unsigned int bufSize = 5*mesh->countFacets(); // make the buffer big enough
this->selectBuf = new GLuint[bufSize];
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();
gluPickMatrix(x, y, w, h, viewport);
glMultMatrixf(/*mp*/this->projection);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadMatrixf(this->modelview);
}
void SoFCMeshObjectShape::stopSelection(SoAction * action, const Mesh::MeshObject* mesh)
{
// restoring the original projection matrix
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glFlush();
// returning to normal rendering mode
GLint hits = glRenderMode(GL_RENDER);
unsigned int bufSize = 5*mesh->countFacets();
std::vector< std::pair<double,unsigned int> > hit;
GLuint 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 SoFCMeshObjectShape::renderSelectionGeometry(const Mesh::MeshObject* mesh)
{
int fcnt=0;
const MeshCore::MeshPointArray & rPoints = mesh->getKernel().GetPoints();
const MeshCore::MeshFacetArray & rFacets = mesh->getKernel().GetFacets();
MeshCore::MeshFacetArray::_TConstIterator it_end = rFacets.end();
for (MeshCore::MeshFacetArray::_TConstIterator it = rFacets.begin(); it != it_end; ++it) {
const MeshCore::MeshPoint& v0 = rPoints[it->_aulPoints[0]];
const MeshCore::MeshPoint& v1 = rPoints[it->_aulPoints[1]];
const MeshCore::MeshPoint& v2 = rPoints[it->_aulPoints[2]];
glLoadName(fcnt);
glBegin(GL_TRIANGLES);
glVertex(v0);
glVertex(v1);
glVertex(v2);
glEnd();
fcnt++;
}
}
// test bbox intersection
//static SbBool
//SoFCMeshObjectShape_ray_intersect(SoRayPickAction * action, const SbBox3f & box)
//{
// if (box.isEmpty()) return FALSE;
// return action->intersect(box, TRUE);
//}
/**
* Calculates picked point based on primitives generated by subclasses.
*/
void
SoFCMeshObjectShape::rayPick(SoRayPickAction * action)
{
//if (this->shouldRayPick(action)) {
// this->computeObjectSpaceRay(action);
// const SoBoundingBoxCache* bboxcache = getBoundingBoxCache();
// if (!bboxcache || !bboxcache->isValid(action->getState()) ||
// SoFCMeshObjectShape_ray_intersect(action, bboxcache->getProjectedBox())) {
// this->generatePrimitives(action);
// }
//}
inherited::rayPick(action);
}
/** Sets the point indices, the geometric points and the normal for each triangle.
* If the number of triangles exceeds \a renderTriangleLimit then only a triangulation of
* a rough model is filled in instead. This is due to performance issues.
* \see createTriangleDetail().
*/
void SoFCMeshObjectShape::generatePrimitives(SoAction* action)
{
SoState* state = action->getState();
const Mesh::MeshObject* mesh = SoFCMeshObjectElement::get(state);
if (!mesh)
return;
const MeshCore::MeshPointArray & rPoints = mesh->getKernel().GetPoints();
const MeshCore::MeshFacetArray & rFacets = mesh->getKernel().GetFacets();
if (rPoints.size() < 3)
return;
if (rFacets.size() < 1)
return;
// get material binding
Binding mbind = this->findMaterialBinding(state);
// Create the information when moving over or picking into the scene
SoPrimitiveVertex vertex;
SoPointDetail pointDetail;
SoFaceDetail faceDetail;
vertex.setDetail(&pointDetail);
beginShape(action, TRIANGLES, &faceDetail);
try
{
for (MeshCore::MeshFacetArray::_TConstIterator it = rFacets.begin(); it != rFacets.end(); ++it)
{
const MeshCore::MeshPoint& v0 = rPoints[it->_aulPoints[0]];
const MeshCore::MeshPoint& v1 = rPoints[it->_aulPoints[1]];
const MeshCore::MeshPoint& v2 = rPoints[it->_aulPoints[2]];
// Calculate the normal n = (v1-v0)x(v2-v0)
SbVec3f n;
n[0] = (v1.y-v0.y)*(v2.z-v0.z)-(v1.z-v0.z)*(v2.y-v0.y);
n[1] = (v1.z-v0.z)*(v2.x-v0.x)-(v1.x-v0.x)*(v2.z-v0.z);
n[2] = (v1.x-v0.x)*(v2.y-v0.y)-(v1.y-v0.y)*(v2.x-v0.x);
// Set the normal
vertex.setNormal(n);
// Vertex 0
if (mbind == PER_VERTEX_INDEXED || mbind == PER_FACE_INDEXED) {
pointDetail.setMaterialIndex(it->_aulPoints[0]);
vertex.setMaterialIndex(it->_aulPoints[0]);
}
pointDetail.setCoordinateIndex(it->_aulPoints[0]);
vertex.setPoint(sbvec3f(v0));
shapeVertex(&vertex);
// Vertex 1
if (mbind == PER_VERTEX_INDEXED || mbind == PER_FACE_INDEXED) {
pointDetail.setMaterialIndex(it->_aulPoints[1]);
vertex.setMaterialIndex(it->_aulPoints[1]);
}
pointDetail.setCoordinateIndex(it->_aulPoints[1]);
vertex.setPoint(sbvec3f(v1));
shapeVertex(&vertex);
// Vertex 2
if (mbind == PER_VERTEX_INDEXED || mbind == PER_FACE_INDEXED) {
pointDetail.setMaterialIndex(it->_aulPoints[2]);
vertex.setMaterialIndex(it->_aulPoints[2]);
}
pointDetail.setCoordinateIndex(it->_aulPoints[2]);
vertex.setPoint(sbvec3f(v2));
shapeVertex(&vertex);
// Increment for the next face
faceDetail.incFaceIndex();
}
}
catch (const Base::MemoryException&) {
Base::Console().Log("Not enough memory to generate primitives\n");
}
endShape();
}
/**
* If the number of triangles exceeds \a renderTriangleLimit 0 is returned.
* This means that the client programmer needs to implement itself to get the
* index of the picked triangle. If the number of triangles doesn't exceed
* \a renderTriangleLimit SoShape::createTriangleDetail() gets called.
* Against the default OpenInventor implementation which returns 0 as well
* Coin3d fills in the point and face indices.
*/
SoDetail * SoFCMeshObjectShape::createTriangleDetail(SoRayPickAction * action,
const SoPrimitiveVertex * v1,
const SoPrimitiveVertex * v2,
const SoPrimitiveVertex * v3,
SoPickedPoint * pp)
{
SoDetail* detail = inherited::createTriangleDetail(action, v1, v2, v3, pp);
return detail;
}
/**
* Sets the bounding box of the mesh to \a box and its center to \a center.
*/
void SoFCMeshObjectShape::computeBBox(SoAction *action, SbBox3f &box, SbVec3f &center)
{
SoState* state = action->getState();
const Mesh::MeshObject * mesh = SoFCMeshObjectElement::get(state);
if (mesh && mesh->countPoints() > 0) {
Base::BoundBox3f cBox = mesh->getKernel().GetBoundBox();
box.setBounds(SbVec3f(cBox.MinX,cBox.MinY,cBox.MinZ),
SbVec3f(cBox.MaxX,cBox.MaxY,cBox.MaxZ));
Base::Vector3f mid = cBox.CalcCenter();
center.setValue(mid.x,mid.y,mid.z);
}
else {
box.setBounds(SbVec3f(0,0,0), SbVec3f(0,0,0));
center.setValue(0.0f,0.0f,0.0f);
}
}
/**
* Adds the number of the triangles to the \a SoGetPrimitiveCountAction.
*/
void SoFCMeshObjectShape::getPrimitiveCount(SoGetPrimitiveCountAction * action)
{
if (!this->shouldPrimitiveCount(action)) return;
SoState* state = action->getState();
const Mesh::MeshObject * mesh = SoFCMeshObjectElement::get(state);
action->addNumTriangles(mesh->countFacets());
action->addNumPoints(mesh->countPoints());
}
/**
* Counts the number of triangles. If a mesh is not set yet it returns 0.
*/
unsigned int SoFCMeshObjectShape::countTriangles(SoAction * action) const
{
SoState* state = action->getState();
const Mesh::MeshObject * mesh = SoFCMeshObjectElement::get(state);
return (unsigned int)mesh->countFacets();
}
// -------------------------------------------------------
SO_NODE_SOURCE(SoFCMeshSegmentShape);
void SoFCMeshSegmentShape::initClass()
{
SO_NODE_INIT_CLASS(SoFCMeshSegmentShape, SoShape, "Shape");
}
SoFCMeshSegmentShape::SoFCMeshSegmentShape() : renderTriangleLimit(100000)
{
SO_NODE_CONSTRUCTOR(SoFCMeshSegmentShape);
SO_NODE_ADD_FIELD(index, (0));
}
/**
* Either renders the complete mesh or only a subset of the points.
*/
void SoFCMeshSegmentShape::GLRender(SoGLRenderAction *action)
{
if (shouldGLRender(action))
{
SoState* state = action->getState();
SbBool mode = Gui::SoFCInteractiveElement::get(state);
const Mesh::MeshObject * mesh = SoFCMeshObjectElement::get(state);
if (!mesh) return;
Binding mbind = this->findMaterialBinding(state);
SoMaterialBundle mb(action);
//SoTextureCoordinateBundle tb(action, true, false);
SbBool needNormals = !mb.isColorOnly()/* || tb.isFunction()*/;
mb.sendFirst(); // make sure we have the correct material
SbBool ccw = TRUE;
if (SoShapeHintsElement::getVertexOrdering(state) == SoShapeHintsElement::CLOCKWISE)
ccw = FALSE;
if (mode == false || mesh->countFacets() <= this->renderTriangleLimit) {
if (mbind != OVERALL)
drawFaces(mesh, &mb, mbind, needNormals, ccw);
else
drawFaces(mesh, 0, mbind, needNormals, ccw);
}
else {
drawPoints(mesh, needNormals, ccw);
}
// Disable caching for this node
//SoGLCacheContextElement::shouldAutoCache(state, SoGLCacheContextElement::DONT_AUTO_CACHE);
}
}
/**
* Translates current material binding into the internal Binding enum.
*/
SoFCMeshSegmentShape::Binding SoFCMeshSegmentShape::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_INDEXED;
break;
case SoMaterialBindingElement::PER_VERTEX_INDEXED:
binding = PER_VERTEX_INDEXED;
break;
case SoMaterialBindingElement::PER_PART:
case SoMaterialBindingElement::PER_FACE:
binding = PER_FACE_INDEXED;
break;
case SoMaterialBindingElement::PER_PART_INDEXED:
case SoMaterialBindingElement::PER_FACE_INDEXED:
binding = PER_FACE_INDEXED;
break;
default:
break;
}
return binding;
}
/**
* Renders the triangles of the complete mesh.
* FIXME: Do it the same way as Coin did to have only one implementation which is controled by defines
* FIXME: Implement using different values of transparency for each vertex or face
*/
void SoFCMeshSegmentShape::drawFaces(const Mesh::MeshObject * mesh, SoMaterialBundle* mb,
Binding bind, SbBool needNormals, SbBool ccw) const
{
const MeshCore::MeshPointArray & rPoints = mesh->getKernel().GetPoints();
const MeshCore::MeshFacetArray & rFacets = mesh->getKernel().GetFacets();
if (mesh->countSegments() <= this->index.getValue())
return;
const std::vector<unsigned long> rSegm = mesh->getSegment
(this->index.getValue()).getIndices();
bool perVertex = (mb && bind == PER_VERTEX_INDEXED);
bool perFace = (mb && bind == PER_FACE_INDEXED);
if (needNormals)
{
glBegin(GL_TRIANGLES);
if (ccw) {
// counterclockwise ordering
for (std::vector<unsigned long>::const_iterator it = rSegm.begin(); it != rSegm.end(); ++it)
{
const MeshCore::MeshFacet& f = rFacets[*it];
const MeshCore::MeshPoint& v0 = rPoints[f._aulPoints[0]];
const MeshCore::MeshPoint& v1 = rPoints[f._aulPoints[1]];
const MeshCore::MeshPoint& v2 = rPoints[f._aulPoints[2]];
// Calculate the normal n = (v1-v0)x(v2-v0)
float n[3];
n[0] = (v1.y-v0.y)*(v2.z-v0.z)-(v1.z-v0.z)*(v2.y-v0.y);
n[1] = (v1.z-v0.z)*(v2.x-v0.x)-(v1.x-v0.x)*(v2.z-v0.z);
n[2] = (v1.x-v0.x)*(v2.y-v0.y)-(v1.y-v0.y)*(v2.x-v0.x);
if(perFace)
mb->send(*it, TRUE);
glNormal(n);
if(perVertex)
mb->send(f._aulPoints[0], TRUE);
glVertex(v0);
if(perVertex)
mb->send(f._aulPoints[1], TRUE);
glVertex(v1);
if(perVertex)
mb->send(f._aulPoints[2], TRUE);
glVertex(v2);
}
}
else {
// clockwise ordering
for (std::vector<unsigned long>::const_iterator it = rSegm.begin(); it != rSegm.end(); ++it)
{
const MeshCore::MeshFacet& f = rFacets[*it];
const MeshCore::MeshPoint& v0 = rPoints[f._aulPoints[0]];
const MeshCore::MeshPoint& v1 = rPoints[f._aulPoints[1]];
const MeshCore::MeshPoint& v2 = rPoints[f._aulPoints[2]];
// Calculate the normal n = -(v1-v0)x(v2-v0)
float n[3];
n[0] = -((v1.y-v0.y)*(v2.z-v0.z)-(v1.z-v0.z)*(v2.y-v0.y));
n[1] = -((v1.z-v0.z)*(v2.x-v0.x)-(v1.x-v0.x)*(v2.z-v0.z));
n[2] = -((v1.x-v0.x)*(v2.y-v0.y)-(v1.y-v0.y)*(v2.x-v0.x));
glNormal(n);
glVertex(v0);
glVertex(v1);
glVertex(v2);
}
}
glEnd();
}
else {
glBegin(GL_TRIANGLES);
for (std::vector<unsigned long>::const_iterator it = rSegm.begin(); it != rSegm.end(); ++it)
{
const MeshCore::MeshFacet& f = rFacets[*it];
glVertex(rPoints[f._aulPoints[0]]);
glVertex(rPoints[f._aulPoints[1]]);
glVertex(rPoints[f._aulPoints[2]]);
}
glEnd();
}
}
/**
* Renders the gravity points of a subset of triangles.
*/
void SoFCMeshSegmentShape::drawPoints(const Mesh::MeshObject * mesh, SbBool needNormals, SbBool ccw) const
{
const MeshCore::MeshPointArray & rPoints = mesh->getKernel().GetPoints();
const MeshCore::MeshFacetArray & rFacets = mesh->getKernel().GetFacets();
if (mesh->countSegments() <= this->index.getValue())
return;
const std::vector<unsigned long> rSegm = mesh->getSegment
(this->index.getValue()).getIndices();
int mod = rSegm.size()/renderTriangleLimit+1;
float size = std::min<float>((float)mod,3.0f);
glPointSize(size);
if (needNormals)
{
glBegin(GL_POINTS);
int ct=0;
if (ccw) {
for (std::vector<unsigned long>::const_iterator it = rSegm.begin(); it != rSegm.end(); ++it, ct++)
{
if (ct%mod==0) {
const MeshCore::MeshFacet& f = rFacets[*it];
const MeshCore::MeshPoint& v0 = rPoints[f._aulPoints[0]];
const MeshCore::MeshPoint& v1 = rPoints[f._aulPoints[1]];
const MeshCore::MeshPoint& v2 = rPoints[f._aulPoints[2]];
// Calculate the normal n = (v1-v0)x(v2-v0)
float n[3];
n[0] = (v1.y-v0.y)*(v2.z-v0.z)-(v1.z-v0.z)*(v2.y-v0.y);
n[1] = (v1.z-v0.z)*(v2.x-v0.x)-(v1.x-v0.x)*(v2.z-v0.z);
n[2] = (v1.x-v0.x)*(v2.y-v0.y)-(v1.y-v0.y)*(v2.x-v0.x);
// Calculate the center point p=(v0+v1+v2)/3
float p[3];
p[0] = (v0.x+v1.x+v2.x)/3.0f;
p[1] = (v0.y+v1.y+v2.y)/3.0f;
p[2] = (v0.z+v1.z+v2.z)/3.0f;
glNormal3fv(n);
glVertex3fv(p);
}
}
}
else {
for (std::vector<unsigned long>::const_iterator it = rSegm.begin(); it != rSegm.end(); ++it, ct++)
{
if (ct%mod==0) {
const MeshCore::MeshFacet& f = rFacets[*it];
const MeshCore::MeshPoint& v0 = rPoints[f._aulPoints[0]];
const MeshCore::MeshPoint& v1 = rPoints[f._aulPoints[1]];
const MeshCore::MeshPoint& v2 = rPoints[f._aulPoints[2]];
// Calculate the normal n = -(v1-v0)x(v2-v0)
float n[3];
n[0] = -((v1.y-v0.y)*(v2.z-v0.z)-(v1.z-v0.z)*(v2.y-v0.y));
n[1] = -((v1.z-v0.z)*(v2.x-v0.x)-(v1.x-v0.x)*(v2.z-v0.z));
n[2] = -((v1.x-v0.x)*(v2.y-v0.y)-(v1.y-v0.y)*(v2.x-v0.x));
// Calculate the center point p=(v0+v1+v2)/3
float p[3];
p[0] = (v0.x+v1.x+v2.x)/3.0f;
p[1] = (v0.y+v1.y+v2.y)/3.0f;
p[2] = (v0.z+v1.z+v2.z)/3.0f;
glNormal3fv(n);
glVertex3fv(p);
}
}
}
glEnd();
}
else {
glBegin(GL_POINTS);
int ct=0;
for (std::vector<unsigned long>::const_iterator it = rSegm.begin(); it != rSegm.end(); ++it, ct++)
{
if (ct%mod==0) {
const MeshCore::MeshFacet& f = rFacets[*it];
const MeshCore::MeshPoint& v0 = rPoints[f._aulPoints[0]];
const MeshCore::MeshPoint& v1 = rPoints[f._aulPoints[1]];
const MeshCore::MeshPoint& v2 = rPoints[f._aulPoints[2]];
// Calculate the center point p=(v0+v1+v2)/3
float p[3];
p[0] = (v0.x+v1.x+v2.x)/3.0f;
p[1] = (v0.y+v1.y+v2.y)/3.0f;
p[2] = (v0.z+v1.z+v2.z)/3.0f;
glVertex3fv(p);
}
}
glEnd();
}
}
/** Sets the point indices, the geometric points and the normal for each triangle.
* If the number of triangles exceeds \a renderTriangleLimit then only a triangulation
* of a rough model is filled in instead. This is due to performance issues.
* \see createTriangleDetail().
*/
void SoFCMeshSegmentShape::generatePrimitives(SoAction* action)
{
SoState* state = action->getState();
const Mesh::MeshObject* mesh = SoFCMeshObjectElement::get(state);
if (!mesh)
return;
const MeshCore::MeshPointArray & rPoints = mesh->getKernel().GetPoints();
const MeshCore::MeshFacetArray & rFacets = mesh->getKernel().GetFacets();
if (rPoints.size() < 3)
return;
if (rFacets.size() < 1)
return;
if (mesh->countSegments() <= this->index.getValue())
return;
const std::vector<unsigned long> rSegm = mesh->getSegment
(this->index.getValue()).getIndices();
// get material binding
Binding mbind = this->findMaterialBinding(state);
// Create the information when moving over or picking into the scene
SoPrimitiveVertex vertex;
SoPointDetail pointDetail;
SoFaceDetail faceDetail;
vertex.setDetail(&pointDetail);
beginShape(action, TRIANGLES, &faceDetail);
try
{
for (std::vector<unsigned long>::const_iterator it = rSegm.begin(); it != rSegm.end(); ++it)
{
const MeshCore::MeshFacet& f = rFacets[*it];
const MeshCore::MeshPoint& v0 = rPoints[f._aulPoints[0]];
const MeshCore::MeshPoint& v1 = rPoints[f._aulPoints[1]];
const MeshCore::MeshPoint& v2 = rPoints[f._aulPoints[2]];
// Calculate the normal n = (v1-v0)x(v2-v0)
SbVec3f n;
n[0] = (v1.y-v0.y)*(v2.z-v0.z)-(v1.z-v0.z)*(v2.y-v0.y);
n[1] = (v1.z-v0.z)*(v2.x-v0.x)-(v1.x-v0.x)*(v2.z-v0.z);
n[2] = (v1.x-v0.x)*(v2.y-v0.y)-(v1.y-v0.y)*(v2.x-v0.x);
// Set the normal
vertex.setNormal(n);
// Vertex 0
if (mbind == PER_VERTEX_INDEXED || mbind == PER_FACE_INDEXED) {
pointDetail.setMaterialIndex(f._aulPoints[0]);
vertex.setMaterialIndex(f._aulPoints[0]);
}
pointDetail.setCoordinateIndex(f._aulPoints[0]);
vertex.setPoint(sbvec3f(v0));
shapeVertex(&vertex);
// Vertex 1
if (mbind == PER_VERTEX_INDEXED || mbind == PER_FACE_INDEXED) {
pointDetail.setMaterialIndex(f._aulPoints[1]);
vertex.setMaterialIndex(f._aulPoints[1]);
}
pointDetail.setCoordinateIndex(f._aulPoints[1]);
vertex.setPoint(sbvec3f(v1));
shapeVertex(&vertex);
// Vertex 2
if (mbind == PER_VERTEX_INDEXED || mbind == PER_FACE_INDEXED) {
pointDetail.setMaterialIndex(f._aulPoints[2]);
vertex.setMaterialIndex(f._aulPoints[2]);
}
pointDetail.setCoordinateIndex(f._aulPoints[2]);
vertex.setPoint(sbvec3f(v2));
shapeVertex(&vertex);
// Increment for the next face
faceDetail.incFaceIndex();
}
}
catch (const Base::MemoryException&) {
Base::Console().Log("Not enough memory to generate primitives\n");
}
endShape();
}
/**
* Sets the bounding box of the mesh to \a box and its center to \a center.
*/
void SoFCMeshSegmentShape::computeBBox(SoAction *action, SbBox3f &box, SbVec3f &center)
{
box.setBounds(SbVec3f(0,0,0), SbVec3f(0,0,0));
center.setValue(0.0f,0.0f,0.0f);
SoState* state = action->getState();
const Mesh::MeshObject * mesh = SoFCMeshObjectElement::get(state);
if (mesh && mesh->countSegments() > this->index.getValue()) {
const Mesh::Segment& segm = mesh->getSegment(this->index.getValue());
const std::vector<unsigned long>& indices = segm.getIndices();
Base::BoundBox3f cBox;
if (!indices.empty()) {
const MeshCore::MeshPointArray& rPoint = mesh->getKernel().GetPoints();
const MeshCore::MeshFacetArray& rFaces = mesh->getKernel().GetFacets();
for (std::vector<unsigned long>::const_iterator it = indices.begin();
it != indices.end(); ++it) {
const MeshCore::MeshFacet& face = rFaces[*it];
cBox &= rPoint[face._aulPoints[0]];
cBox &= rPoint[face._aulPoints[1]];
cBox &= rPoint[face._aulPoints[2]];
}
box.setBounds(SbVec3f(cBox.MinX,cBox.MinY,cBox.MinZ),
SbVec3f(cBox.MaxX,cBox.MaxY,cBox.MaxZ));
Base::Vector3f mid = cBox.CalcCenter();
center.setValue(mid.x,mid.y,mid.z);
}
}
}
/**
* Adds the number of the triangles to the \a SoGetPrimitiveCountAction.
*/
void SoFCMeshSegmentShape::getPrimitiveCount(SoGetPrimitiveCountAction * action)
{
if (!this->shouldPrimitiveCount(action)) return;
SoState* state = action->getState();
const Mesh::MeshObject * mesh = SoFCMeshObjectElement::get(state);
if (mesh && mesh->countSegments() > this->index.getValue()) {
const Mesh::Segment& segm = mesh->getSegment(this->index.getValue());
action->addNumTriangles(segm.getIndices().size());
}
}
// -------------------------------------------------------
SO_NODE_SOURCE(SoFCMeshObjectBoundary);
void SoFCMeshObjectBoundary::initClass()
{
SO_NODE_INIT_CLASS(SoFCMeshObjectBoundary, SoShape, "Shape");
}
SoFCMeshObjectBoundary::SoFCMeshObjectBoundary()
{
SO_NODE_CONSTRUCTOR(SoFCMeshObjectBoundary);
}
/**
* Renders the open edges only.
*/
void SoFCMeshObjectBoundary::GLRender(SoGLRenderAction *action)
{
if (shouldGLRender(action))
{
SoState* state = action->getState();
const Mesh::MeshObject * mesh = SoFCMeshObjectElement::get(state);
if (!mesh) return;
SoMaterialBundle mb(action);
SoTextureCoordinateBundle tb(action, TRUE, FALSE);
SoLazyElement::setLightModel(state, SoLazyElement::BASE_COLOR);
mb.sendFirst(); // make sure we have the correct material
drawLines(mesh);
// Disable caching for this node
//SoGLCacheContextElement::shouldAutoCache(state, SoGLCacheContextElement::DONT_AUTO_CACHE);
}
}
/**
* Renders the triangles of the complete mesh.
*/
void SoFCMeshObjectBoundary::drawLines(const Mesh::MeshObject * mesh) const
{
const MeshCore::MeshPointArray & rPoints = mesh->getKernel().GetPoints();
const MeshCore::MeshFacetArray & rFacets = mesh->getKernel().GetFacets();
// When rendering open edges use the given line width * 3
GLfloat lineWidth;
glGetFloatv(GL_LINE_WIDTH, &lineWidth);
glLineWidth(3.0f*lineWidth);
// Use the data structure directly and not through MeshFacetIterator as this
// class is quite slowly (at least for rendering)
glBegin(GL_LINES);
for (MeshCore::MeshFacetArray::_TConstIterator it = rFacets.begin(); it != rFacets.end(); ++it) {
for (int i=0; i<3; i++) {
if (it->_aulNeighbours[i] == ULONG_MAX) {
glVertex(rPoints[it->_aulPoints[i]]);
glVertex(rPoints[it->_aulPoints[(i+1)%3]]);
}
}
}
glEnd();
}
void SoFCMeshObjectBoundary::generatePrimitives(SoAction* action)
{
// do not create primitive information as an SoFCMeshObjectShape
// should already be used that delivers the information
SoState* state = action->getState();
const Mesh::MeshObject* mesh = SoFCMeshObjectElement::get(state);
if (!mesh)
return;
const MeshCore::MeshPointArray & rPoints = mesh->getKernel().GetPoints();
const MeshCore::MeshFacetArray & rFacets = mesh->getKernel().GetFacets();
// Create the information when moving over or picking into the scene
SoPrimitiveVertex vertex;
SoPointDetail pointDetail;
SoLineDetail lineDetail;
vertex.setDetail(&pointDetail);
beginShape(action, LINES, &lineDetail);
for (MeshCore::MeshFacetArray::_TConstIterator it = rFacets.begin(); it != rFacets.end(); ++it)
{
for (int i=0; i<3; i++) {
if (it->_aulNeighbours[i] == ULONG_MAX) {
const MeshCore::MeshPoint& v0 = rPoints[it->_aulPoints[i]];
const MeshCore::MeshPoint& v1 = rPoints[it->_aulPoints[(i+1)%3]];
// Vertex 0
pointDetail.setCoordinateIndex(it->_aulPoints[i]);
vertex.setPoint(sbvec3f(v0));
shapeVertex(&vertex);
// Vertex 1
pointDetail.setCoordinateIndex(it->_aulPoints[(i+1)%3]);
vertex.setPoint(sbvec3f(v1));
shapeVertex(&vertex);
// Increment for the next open edge
lineDetail.incLineIndex();
}
}
}
endShape();
}
/**
* Sets the bounding box of the mesh to \a box and its center to \a center.
*/
void SoFCMeshObjectBoundary::computeBBox(SoAction *action, SbBox3f &box, SbVec3f &center)
{
SoState* state = action->getState();
const Mesh::MeshObject * mesh = SoFCMeshObjectElement::get(state);
if (!mesh)
return;
const MeshCore::MeshPointArray & rPoints = mesh->getKernel().GetPoints();
if (rPoints.size() > 0) {
Base::BoundBox3f cBox;
for (MeshCore::MeshPointArray::_TConstIterator it = rPoints.begin(); it != rPoints.end(); ++it)
cBox &= (*it);
box.setBounds(SbVec3f(cBox.MinX,cBox.MinY,cBox.MinZ),
SbVec3f(cBox.MaxX,cBox.MaxY,cBox.MaxZ));
Base::Vector3f mid = cBox.CalcCenter();
center.setValue(mid.x,mid.y,mid.z);
}
else {
box.setBounds(SbVec3f(0,0,0), SbVec3f(0,0,0));
center.setValue(0.0f,0.0f,0.0f);
}
}
/**
* Adds the number of the triangles to the \a SoGetPrimitiveCountAction.
*/
void SoFCMeshObjectBoundary::getPrimitiveCount(SoGetPrimitiveCountAction * action)
{
if (!this->shouldPrimitiveCount(action)) return;
SoState* state = action->getState();
const Mesh::MeshObject * mesh = SoFCMeshObjectElement::get(state);
if (!mesh)
return;
const MeshCore::MeshFacetArray & rFaces = mesh->getKernel().GetFacets();
// Count number of open edges first
int ctEdges=0;
for (MeshCore::MeshFacetArray::_TConstIterator jt = rFaces.begin(); jt != rFaces.end(); ++jt) {
for (int i=0; i<3; i++) {
if (jt->_aulNeighbours[i] == ULONG_MAX) {
ctEdges++;
}
}
}
action->addNumLines(ctEdges);
}