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