1 /**************************************************************************\
2  * Copyright (c) Kongsberg Oil & Gas Technologies AS
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * Redistributions of source code must retain the above copyright notice,
10  * this list of conditions and the following disclaimer.
11  *
12  * Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in the
14  * documentation and/or other materials provided with the distribution.
15  *
16  * Neither the name of the copyright holder nor the names of its
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 \**************************************************************************/
32 
33 /*!
34   \class SoPointSet SoPointSet.h Inventor/nodes/SoPointSet.h
35   \brief The SoPointSet class is used to display a set of 3D points.
36 
37   \ingroup nodes
38 
39   This node either uses the coordinates currently on the state
40   (typically set up by a leading SoCoordinate3 node in the scenegraph)
41   or from a SoVertexProperty node attached to this node to render a
42   set of 3D points.
43 
44   The SoPointSet::numPoints field specifies the number of points in
45   the coordinate set which should be rendered (or otherwise handled by
46   traversal actions).
47 
48   Here's a simple usage example of SoPointSet in a scenegraph:
49 
50   \verbatim
51   #Inventor V2.1 ascii
52 
53   Separator {
54      Material {
55         diffuseColor [
56          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
57         ]
58      }
59      MaterialBinding { value PER_PART }
60 
61      Coordinate3 {
62         point [
63          -1 1 0, -1 -1 0, 1 -1 0, 1 1 0, 0 2 -1, -2 0 -1, 0 -2 -1, 2 0 -1
64         ]
65      }
66 
67      DrawStyle { pointSize 3 }
68 
69      PointSet { }
70   }
71   \endverbatim
72 
73   <b>FILE FORMAT/DEFAULTS:</b>
74   \code
75     PointSet {
76         vertexProperty NULL
77         startIndex 0
78         numPoints -1
79     }
80   \endcode
81 
82 */
83 
84 #include <Inventor/nodes/SoPointSet.h>
85 
86 #ifdef HAVE_CONFIG_H
87 #include <config.h>
88 #endif // HAVE_CONFIG_H
89 
90 #include <Inventor/misc/SoState.h>
91 #include <Inventor/bundles/SoTextureCoordinateBundle.h>
92 #include <Inventor/SoPrimitiveVertex.h>
93 #include <Inventor/actions/SoGLRenderAction.h>
94 #include <Inventor/system/gl.h>
95 #include <Inventor/nodes/SoVertexProperty.h>
96 #include <Inventor/actions/SoGetPrimitiveCountAction.h>
97 #include <Inventor/elements/SoGLCoordinateElement.h>
98 #include <Inventor/elements/SoNormalBindingElement.h>
99 #include <Inventor/elements/SoMaterialBindingElement.h>
100 #include <Inventor/bundles/SoMaterialBundle.h>
101 #include <Inventor/elements/SoGLLazyElement.h>
102 #include <Inventor/elements/SoGLVBOElement.h>
103 #include <Inventor/caches/SoNormalCache.h>
104 #include <Inventor/details/SoPointDetail.h>
105 #include <Inventor/misc/SoGLDriverDatabase.h>
106 #if COIN_DEBUG
107 #include <Inventor/errors/SoDebugError.h>
108 #endif // COIN_DEBUG
109 
110 #include "nodes/SoSubNodeP.h"
111 #include "rendering/SoGL.h"
112 #include "rendering/SoVBO.h"
113 
114 /*!
115   \var SoSFInt32 SoPointSet::numPoints
116 
117   Used to specify number of points in the point set.  Coordinates for
118   the points will be taken from the state stack's set of 3D
119   coordinates, typically set up by a leading SoCoordinate3 node.
120 
121   If this field is equal to -1 (the default value) \e all coordinates
122   currently on the state will be rendered or otherwise handled by
123   traversal actions.
124 
125   SoPointSet inherits the field SoNonIndexedShape::startIndex, which
126   specifies the start index for points from the current state set of
127   coordinates. Please note that this field has been obsoleted, but is
128   still provided for compatibility.
129 */
130 
131 SO_NODE_SOURCE(SoPointSet);
132 
133 /*!
134   Constructor.
135 */
SoPointSet()136 SoPointSet::SoPointSet()
137 {
138   SO_NODE_INTERNAL_CONSTRUCTOR(SoPointSet);
139 
140   SO_NODE_ADD_FIELD(numPoints, (-1));
141 }
142 
143 /*!
144   Destructor.
145 */
~SoPointSet()146 SoPointSet::~SoPointSet()
147 {
148 }
149 
150 // doc from parent
151 void
initClass(void)152 SoPointSet::initClass(void)
153 {
154   SO_NODE_INTERNAL_INIT_CLASS(SoPointSet, SO_FROM_INVENTOR_1|SoNode::VRML1);
155 }
156 
157 // doc from parent
158 void
computeBBox(SoAction * action,SbBox3f & box,SbVec3f & center)159 SoPointSet::computeBBox(SoAction * action, SbBox3f & box, SbVec3f & center)
160 {
161   inherited::computeCoordBBox(action, this->numPoints.getValue(), box, center);
162 }
163 
164 // Internal method which translates the current material binding
165 // found on the state to a material binding for this node.
166 // PER_PART, PER_FACE, PER_VERTEX and their indexed counterparts
167 // are translated to PER_VERTEX binding. OVERALL means overall
168 // binding for point set also, of course. The default material
169 // binding is OVERALL.
170 SoPointSet::Binding
findMaterialBinding(SoState * const state) const171 SoPointSet::findMaterialBinding(SoState * const state) const
172 {
173   Binding binding = OVERALL;
174   if (SoMaterialBindingElement::get(state) !=
175       SoMaterialBindingElement::OVERALL) binding = PER_VERTEX;
176   return binding;
177 }
178 
179 //  Internal method which translates the current normal binding
180 //  found on the state to a normal binding for this node.
181 //  PER_PART, PER_FACE, PER_VERTEX and their indexed counterparts
182 //  are translated to PER_VERTEX binding. OVERALL means overall
183 //  binding for point set also, of course. The default normal
184 //  binding is PER_VERTEX.
185 SoPointSet::Binding
findNormalBinding(SoState * const state) const186 SoPointSet::findNormalBinding(SoState * const state) const
187 
188 {
189   Binding binding = PER_VERTEX;
190 
191   if (SoNormalBindingElement::get(state) ==
192       SoNormalBindingElement::OVERALL) binding = OVERALL;
193   return binding;
194 }
195 
196 // doc from parent
197 void
GLRender(SoGLRenderAction * action)198 SoPointSet::GLRender(SoGLRenderAction * action)
199 {
200   int32_t numpts = this->numPoints.getValue();
201   if (numpts == 0) return;
202   if (!this->shouldGLRender(action)) return;
203 
204   SoState * state = action->getState();
205 
206   SbBool didpush = FALSE;
207   if (this->vertexProperty.getValue()) {
208     state->push();
209     didpush = TRUE;
210     this->vertexProperty.getValue()->GLRender(action);
211   }
212 
213   const SoCoordinateElement * tmp;
214   const SbVec3f * normals;
215 
216   SoTextureCoordinateBundle tb(action, TRUE, FALSE);
217   SbBool doTextures = tb.needCoordinates();
218   SoMaterialBundle mb(action);
219 
220   SbBool needNormals = !mb.isColorOnly() || tb.isFunction();
221 
222   SoVertexShape::getVertexData(state, tmp, normals,
223                                needNormals);
224 
225   if (normals == NULL && needNormals) {
226     needNormals = FALSE;
227     if (!didpush) {
228       state->push();
229       didpush = TRUE;
230     }
231     SoLazyElement::setLightModel(state, SoLazyElement::BASE_COLOR);
232   }
233 
234   const SoGLCoordinateElement * coords = (SoGLCoordinateElement *)tmp;
235 
236   Binding mbind = this->findMaterialBinding(action->getState());
237 
238   Binding nbind = OVERALL;
239   if (needNormals) nbind = this->findNormalBinding(action->getState());
240 
241   if (nbind == OVERALL && needNormals) {
242     if (normals) glNormal3fv((const GLfloat *)normals);
243     else glNormal3f(0.0f, 0.0f, 1.0f);
244   }
245 
246   mb.sendFirst(); // make sure we have the correct material
247 
248   int32_t idx = this->startIndex.getValue();
249   if (numpts < 0) numpts = coords->getNum() - idx;
250 
251   const cc_glglue * glue = sogl_glue_instance(state);
252   const uint32_t contextid = action->getCacheContext();
253 
254   SbBool dova =
255     SoVBO::shouldRenderAsVertexArrays(state, contextid, numpts) &&
256     SoGLDriverDatabase::isSupported(glue, SO_GL_VERTEX_ARRAY);
257 
258   if (dova && (mbind == PER_VERTEX)) {
259     const SoGLVBOElement * vboelem = SoGLVBOElement::getInstance(state);
260     if (vboelem->getColorVBO() == NULL) {
261       dova = FALSE;
262       // we might be able to do VA-rendering, but need to check the
263       // diffuse color type first.
264       SoGLLazyElement * lelem = (SoGLLazyElement*) SoLazyElement::getInstance(state);
265       if (!lelem->isPacked() && lelem->getNumTransparencies() <= 1) {
266         dova = TRUE;
267       }
268     }
269   }
270   SbBool didrenderasvbo = FALSE;
271   if (dova) {
272     SbBool vbo = this->startVertexArray(action,
273                                         coords,
274                                         (needNormals && (nbind != OVERALL)) ? normals : NULL,
275                                         doTextures,
276                                         mbind == PER_VERTEX);
277     didrenderasvbo = vbo;
278     cc_glglue_glDrawArrays(glue, GL_POINTS, idx, numpts);
279     this->finishVertexArray(action, vbo,
280                             (needNormals && (nbind != OVERALL)),
281                             doTextures,
282                             mbind == PER_VERTEX);
283   }
284   else {
285     sogl_render_pointset(coords,
286                          nbind != OVERALL ? normals : NULL,
287                          mbind != OVERALL ? &mb : NULL,
288                          doTextures ? &tb : NULL,
289                          numpts, idx);
290   }
291   if (didpush)
292     state->pop();
293 
294   // send approx number of points for autocache handling. Divide
295   // by three so that three points is the same as one triangle.
296   sogl_autocache_update(state, numpts/3, didrenderasvbo);
297 
298 }
299 
300 // Documented in superclass.
301 SbBool
generateDefaultNormals(SoState *,SoNormalCache * nc)302 SoPointSet::generateDefaultNormals(SoState *, SoNormalCache * nc)
303 {
304   // Overridden to clear normal cache, as it's not possible to
305   // generate a normal for a point.
306   nc->set(0, NULL);
307   return TRUE;
308 }
309 
310 // Documented in superclass.
311 SbBool
generateDefaultNormals(SoState * COIN_UNUSED_ARG (state),SoNormalBundle * COIN_UNUSED_ARG (bundle))312 SoPointSet::generateDefaultNormals(SoState * COIN_UNUSED_ARG(state), SoNormalBundle * COIN_UNUSED_ARG(bundle))
313 {
314   // Overridden to avoid (faulty) compiler warnings with some version
315   // of g++.
316   return FALSE;
317 }
318 
319 // doc from parent
320 void
getBoundingBox(SoGetBoundingBoxAction * action)321 SoPointSet::getBoundingBox(SoGetBoundingBoxAction *action)
322 {
323   inherited::getBoundingBox(action);
324 }
325 
326 // doc from parent
327 void
getPrimitiveCount(SoGetPrimitiveCountAction * action)328 SoPointSet::getPrimitiveCount(SoGetPrimitiveCountAction *action)
329 {
330   if (!this->shouldPrimitiveCount(action)) return;
331   int num = this->numPoints.getValue();
332   if (num < 0) {
333     SoNode *vpnode = this->vertexProperty.getValue();
334     SoVertexProperty *vp =
335       (vpnode && vpnode->isOfType(SoVertexProperty::getClassTypeId())) ?
336       (SoVertexProperty *)vpnode : NULL;
337     if (vp && vp->vertex.getNum()) {
338       num = vp->vertex.getNum() - this->startIndex.getValue();
339     }
340     else {
341       const SoCoordinateElement *coordelem =
342         SoCoordinateElement::getInstance(action->getState());
343       num = coordelem->getNum() - this->startIndex.getValue();
344     }
345   }
346   action->addNumPoints(num);
347 }
348 
349 // doc from parent
350 void
generatePrimitives(SoAction * action)351 SoPointSet::generatePrimitives(SoAction *action)
352 {
353   int32_t numpts = this->numPoints.getValue();
354   if (numpts == 0) return;
355 
356   SoState * state = action->getState();
357 
358   if (this->vertexProperty.getValue()) {
359     state->push();
360     this->vertexProperty.getValue()->doAction(action);
361   }
362 
363   const SoCoordinateElement *coords;
364   const SbVec3f * normals;
365   SbBool doTextures;
366   SbBool needNormals = TRUE;
367 
368   SoVertexShape::getVertexData(action->getState(), coords, normals,
369                                needNormals);
370 
371   if (normals == NULL) needNormals = FALSE;
372 
373   SoTextureCoordinateBundle tb(action, FALSE, FALSE);
374   doTextures = tb.needCoordinates();
375 
376   Binding mbind = this->findMaterialBinding(action->getState());
377   Binding nbind = this->findNormalBinding(action->getState());
378 
379   if (!needNormals) nbind = OVERALL;
380 
381   SoPrimitiveVertex vertex;
382   SoPointDetail pointDetail;
383   vertex.setDetail(&pointDetail);
384 
385   SbVec3f dummynormal(0.0f, 0.0f, 1.0f);
386   const SbVec3f * currnormal = &dummynormal;
387   if (normals) currnormal = normals;
388   if (nbind == OVERALL && needNormals)
389     vertex.setNormal(*currnormal);
390 
391   int32_t idx = this->startIndex.getValue();
392   if (numpts < 0) numpts = coords->getNum() - idx;
393 
394   int matnr = 0;
395   int texnr = 0;
396   int normnr = 0;
397 
398   this->beginShape(action, SoShape::POINTS);
399   for (int i = 0; i < numpts; i++) {
400     if (nbind == PER_VERTEX) {
401       pointDetail.setNormalIndex(normnr);
402       currnormal = &normals[normnr++];
403       vertex.setNormal(*currnormal);
404     }
405     if (mbind == PER_VERTEX) {
406       pointDetail.setMaterialIndex(matnr);
407       vertex.setMaterialIndex(matnr++);
408     }
409     if (doTextures) {
410       if (tb.isFunction()) {
411         vertex.setTextureCoords(tb.get(coords->get3(idx), *currnormal));
412       }
413       else {
414         pointDetail.setTextureCoordIndex(texnr);
415         vertex.setTextureCoords(tb.get(texnr++));
416       }
417     }
418     pointDetail.setCoordinateIndex(idx);
419     vertex.setPoint(coords->get3(idx++));
420     this->shapeVertex(&vertex);
421   }
422   this->endShape();
423 
424   if (this->vertexProperty.getValue())
425     state->pop();
426 }
427