/**************************************************************************\
*
* This file is part of the Coin 3D visualization library.
* Copyright (C) 1998-2007 by Systems in Motion. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* ("GPL") version 2 as published by the Free Software Foundation.
* See the file LICENSE.GPL at the root directory of this source
* distribution for additional information about the GNU GPL.
*
* For using Coin with software that can not be combined with the GNU
* GPL, and for taking advantage of the additional benefits of our
* support services, please contact Systems in Motion about acquiring
* a Coin Professional Edition License.
*
* See http://www.coin3d.org/ for more information.
*
* Systems in Motion, Postboks 1283, Pirsenteret, 7462 Trondheim, NORWAY.
* http://www.sim.no/ sales@sim.no coin-support@coin3d.org
*
\**************************************************************************/
/*!
\class SoPointSet SoPointSet.h Inventor/nodes/SoPointSet.h
\brief The SoPointSet class is used to display a set of 3D points.
\ingroup nodes
This node either uses the coordinates currently on the state
(typically set up by a leading SoCoordinate3 node in the scenegraph)
or from a SoVertexProperty node attached to this node to render a
set of 3D points.
The SoPointSet::numPoints field specifies the number of points in
the coordinate set which should be rendered (or otherwise handled by
traversal actions).
Here's a simple usage example of SoPointSet in a scenegraph:
\verbatim
#Inventor V2.1 ascii
Separator {
Material {
diffuseColor [
1 0 0, 0 1 0, 0 0 1, 1 1 0, 1 0 1, 1 1 1, 1 0.8 0.6, 0.6 0.8 1
]
}
MaterialBinding { value PER_PART }
Coordinate3 {
point [
-1 1 0, -1 -1 0, 1 -1 0, 1 1 0, 0 2 -1, -2 0 -1, 0 -2 -1, 2 0 -1
]
}
DrawStyle { pointSize 3 }
PointSet { }
}
\endverbatim
FILE FORMAT/DEFAULTS:
\code
PointSet {
vertexProperty NULL
startIndex 0
numPoints -1
}
\endcode
*/
#include
#include
#include
#include
#include
#include
#ifdef HAVE_CONFIG_H
#include
#endif // HAVE_CONFIG_H
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "../misc/SoVBO.h"
#if COIN_DEBUG
#include
#endif // COIN_DEBUG
/*!
\var SoSFInt32 SoPointSet::numPoints
Used to specify number of points in the point set. Coordinates for
the points will be taken from the state stack's set of 3D
coordinates, typically set up by a leading SoCoordinate3 node.
If this field is equal to -1 (the default value) \e all coordinates
currently on the state will be rendered or otherwise handled by
traversal actions.
SoPointSet inherits the field SoNonIndexedShape::startIndex, which
specifies the start index for points from the current state set of
coordinates. Please note that this field has been obsoleted, but is
still provided for compatibility.
*/
SO_NODE_SOURCE(SoPointSet);
/*!
Constructor.
*/
SoPointSet::SoPointSet()
{
SO_NODE_INTERNAL_CONSTRUCTOR(SoPointSet);
SO_NODE_ADD_FIELD(numPoints, (-1));
}
/*!
Destructor.
*/
SoPointSet::~SoPointSet()
{
}
// doc from parent
void
SoPointSet::initClass(void)
{
SO_NODE_INTERNAL_INIT_CLASS(SoPointSet, SO_FROM_INVENTOR_1|SoNode::VRML1);
}
// doc from parent
void
SoPointSet::computeBBox(SoAction * action, SbBox3f & box, SbVec3f & center)
{
inherited::computeCoordBBox(action, this->numPoints.getValue(), box, center);
}
// Internal method which translates the current material binding
// found on the state to a material binding for this node.
// PER_PART, PER_FACE, PER_VERTEX and their indexed counterparts
// are translated to PER_VERTEX binding. OVERALL means overall
// binding for point set also, of course. The default material
// binding is OVERALL.
SoPointSet::Binding
SoPointSet::findMaterialBinding(SoState * const state) const
{
Binding binding = OVERALL;
if (SoMaterialBindingElement::get(state) !=
SoMaterialBindingElement::OVERALL) binding = PER_VERTEX;
return binding;
}
// Internal method which translates the current normal binding
// found on the state to a normal binding for this node.
// PER_PART, PER_FACE, PER_VERTEX and their indexed counterparts
// are translated to PER_VERTEX binding. OVERALL means overall
// binding for point set also, of course. The default normal
// binding is PER_VERTEX.
SoPointSet::Binding
SoPointSet::findNormalBinding(SoState * const state) const
{
Binding binding = PER_VERTEX;
if (SoNormalBindingElement::get(state) ==
SoNormalBindingElement::OVERALL) binding = OVERALL;
return binding;
}
// doc from parent
void
SoPointSet::GLRender(SoGLRenderAction * action)
{
int32_t numpts = this->numPoints.getValue();
if (numpts == 0) return;
if (!this->shouldGLRender(action)) return;
SoState * state = action->getState();
SbBool didpush = FALSE;
if (this->vertexProperty.getValue()) {
state->push();
didpush = TRUE;
this->vertexProperty.getValue()->GLRender(action);
}
const SoCoordinateElement * tmp;
const SbVec3f * normals;
SoTextureCoordinateBundle tb(action, TRUE, FALSE);
SbBool doTextures = tb.needCoordinates();
SoMaterialBundle mb(action);
SbBool needNormals = !mb.isColorOnly() || tb.isFunction();
SoVertexShape::getVertexData(state, tmp, normals,
needNormals);
if (normals == NULL && needNormals) {
needNormals = FALSE;
if (!didpush) {
state->push();
didpush = TRUE;
}
SoLazyElement::setLightModel(state, SoLazyElement::BASE_COLOR);
}
const SoGLCoordinateElement * coords = (SoGLCoordinateElement *)tmp;
Binding mbind = this->findMaterialBinding(action->getState());
Binding nbind = OVERALL;
if (needNormals) nbind = this->findNormalBinding(action->getState());
if (nbind == OVERALL && needNormals) {
if (normals) glNormal3fv((const GLfloat *)normals);
else glNormal3f(0.0f, 0.0f, 1.0f);
}
mb.sendFirst(); // make sure we have the correct material
int32_t idx = this->startIndex.getValue();
if (numpts < 0) numpts = coords->getNum() - idx;
#ifdef COIN_NEXT_MINOR
const cc_glglue * glue = sogl_glue_instance(state);
const uint32_t contextid = action->getCacheContext();
SbBool dova =
SoVBO::shouldRenderAsVertexArrays(contextid, numpts) &&
cc_glglue_has_vertex_array(glue);
if (dova && (mbind == PER_VERTEX)) {
const SoGLVBOElement * vboelem = SoGLVBOElement::getInstance(state);
if (vboelem->getColorVBO() == NULL) {
dova = FALSE;
// we might be able to do VA-rendering, but need to check the
// diffuse color type first.
SoGLLazyElement * lelem = (SoGLLazyElement*) SoLazyElement::getInstance(state);
if (!lelem->isPacked() && lelem->getNumTransparencies() <= 1) {
dova = TRUE;
}
}
}
if (dova) {
SbBool vbo = this->startVertexArray(action,
coords,
(needNormals && (nbind != OVERALL)) ? normals : NULL,
doTextures,
mbind == PER_VERTEX);
cc_glglue_glDrawArrays(glue, GL_POINTS, idx, numpts);
this->finishVertexArray(action, vbo,
(needNormals && (nbind != OVERALL)),
doTextures,
mbind == PER_VERTEX);
}
#else // COIN_NEXT_MINOR
if (0) {}
#endif // !COIN_NEXT_MINOR
else {
sogl_render_pointset(coords,
nbind != OVERALL ? normals : NULL,
mbind != OVERALL ? &mb : NULL,
doTextures ? &tb : NULL,
numpts, idx);
}
if (didpush)
state->pop();
// send approx number of points for autocache handling. Divide
// by three so that three points is the same as one triangle.
sogl_autocache_update(state, numpts/3);
}
// Documented in superclass.
SbBool
SoPointSet::generateDefaultNormals(SoState *, SoNormalCache * nc)
{
// Overridden to clear normal cache, as it's not possible to
// generate a normal for a point.
nc->set(0, NULL);
return TRUE;
}
// Documented in superclass.
SbBool
SoPointSet::generateDefaultNormals(SoState * state, SoNormalBundle * bundle)
{
// Overridden to avoid (faulty) compiler warnings with some version
// of g++.
return FALSE;
}
// doc from parent
void
SoPointSet::getBoundingBox(SoGetBoundingBoxAction *action)
{
inherited::getBoundingBox(action);
}
// doc from parent
void
SoPointSet::getPrimitiveCount(SoGetPrimitiveCountAction *action)
{
if (!this->shouldPrimitiveCount(action)) return;
int num = this->numPoints.getValue();
if (num < 0) {
SoNode *vpnode = this->vertexProperty.getValue();
SoVertexProperty *vp =
(vpnode && vpnode->isOfType(SoVertexProperty::getClassTypeId())) ?
(SoVertexProperty *)vpnode : NULL;
if (vp && vp->vertex.getNum()) {
num = vp->vertex.getNum() - this->startIndex.getValue();
}
else {
const SoCoordinateElement *coordelem =
SoCoordinateElement::getInstance(action->getState());
num = coordelem->getNum() - this->startIndex.getValue();
}
}
action->addNumPoints(num);
}
// doc from parent
void
SoPointSet::generatePrimitives(SoAction *action)
{
int32_t numpts = this->numPoints.getValue();
if (numpts == 0) return;
SoState * state = action->getState();
if (this->vertexProperty.getValue()) {
state->push();
this->vertexProperty.getValue()->doAction(action);
}
const SoCoordinateElement *coords;
const SbVec3f * normals;
SbBool doTextures;
SbBool needNormals = TRUE;
SoVertexShape::getVertexData(action->getState(), coords, normals,
needNormals);
if (normals == NULL) needNormals = FALSE;
SoTextureCoordinateBundle tb(action, FALSE, FALSE);
doTextures = tb.needCoordinates();
Binding mbind = this->findMaterialBinding(action->getState());
Binding nbind = this->findNormalBinding(action->getState());
if (!needNormals) nbind = OVERALL;
SoPrimitiveVertex vertex;
SoPointDetail pointDetail;
vertex.setDetail(&pointDetail);
SbVec3f dummynormal(0.0f, 0.0f, 1.0f);
const SbVec3f * currnormal = &dummynormal;
if (normals) currnormal = normals;
if (nbind == OVERALL && needNormals)
vertex.setNormal(*currnormal);
int32_t idx = this->startIndex.getValue();
if (numpts < 0) numpts = coords->getNum() - idx;
int matnr = 0;
int texnr = 0;
int normnr = 0;
this->beginShape(action, SoShape::POINTS);
for (int i = 0; i < numpts; i++) {
if (nbind == PER_VERTEX) {
pointDetail.setNormalIndex(normnr);
currnormal = &normals[normnr++];
vertex.setNormal(*currnormal);
}
if (mbind == PER_VERTEX) {
pointDetail.setMaterialIndex(matnr);
vertex.setMaterialIndex(matnr++);
}
if (doTextures) {
if (tb.isFunction()) {
vertex.setTextureCoords(tb.get(coords->get3(idx), *currnormal));
}
else {
pointDetail.setTextureCoordIndex(texnr);
vertex.setTextureCoords(tb.get(texnr++));
}
}
pointDetail.setCoordinateIndex(idx);
vertex.setPoint(coords->get3(idx++));
this->shapeVertex(&vertex);
}
this->endShape();
if (this->vertexProperty.getValue())
state->pop();
}