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 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif // HAVE_CONFIG_H
36
37 #ifdef HAVE_NODEKITS
38
39 #include <ForeignFiles/SoSTLFileKit.h>
40 #include "coindefs.h"
41
42 #include <Inventor/SbBasic.h>
43 #include <Inventor/lists/SbList.h>
44 #include <Inventor/errors/SoDebugError.h>
45 #include <Inventor/actions/SoCallbackAction.h>
46 #include <Inventor/actions/SoReorganizeAction.h>
47 #include <Inventor/nodes/SoSeparator.h>
48 #include <Inventor/nodes/SoShapeHints.h>
49 #include <Inventor/nodes/SoTexture2.h>
50 #include <Inventor/nodes/SoNormal.h>
51 #include <Inventor/nodes/SoNormalBinding.h>
52 #include <Inventor/nodes/SoMaterial.h>
53 #include <Inventor/nodes/SoMaterialBinding.h>
54 #include <Inventor/nodes/SoCoordinate3.h>
55 #include <Inventor/nodes/SoIndexedFaceSet.h>
56 #include <Inventor/nodes/SoInfo.h>
57 #include <Inventor/SbBSPTree.h>
58 #include <Inventor/SoPrimitiveVertex.h>
59
60 #include "steel.h"
61 #include "nodekits/SoSubKitP.h"
62
63
64 #if 0
65 SoCallback "callbackList" SoBaseKit
66 SoSeparator "topSeparator" SoForeignFileKit
67 SoShapeHints "shapehints" SoSTLFileKit
68 SoTexture2 "texture" SoSTLFileKit
69 SoNormalBinding "normalbinding" SoSTLFileKit
70 SoNormal "normals" SoSTLFileKit
71 SoMaterialBinding "materialbinding" SoSTLFileKit
72 SoMaterial "material" SoSTLFileKit
73 SoCoordinate3 "coordinates" SoSTLFileKit
74 SoIndexedFaceSet "facets" SoSTLFileKit
75 #endif // 0
76
77 class SoSTLFileKitP {
78 public:
SoSTLFileKitP(SoSTLFileKit * pub)79 SoSTLFileKitP(SoSTLFileKit * pub)
80 : api(pub) {
81 this->data = new SbList<uint16_t>;
82 this->points = new SbBSPTree;
83 this->normals = new SbBSPTree;
84 }
~SoSTLFileKitP(void)85 ~SoSTLFileKitP(void) {
86 delete this->data;
87 delete this->points;
88 delete this->normals;
89 }
90
91 public:
92 SoSTLFileKit * const api;
93
94 SbList<uint16_t> * data;
95 SbBSPTree * points;
96 SbBSPTree * normals;
97
98 int numfacets;
99 int numvertices;
100 int numnormals;
101 int numsharedvertices;
102 int numsharednormals;
103 int numredundantfacets;
104 }; // SoSTLFileKitP
105
106 // *************************************************************************
107
108 /*!
109 \class SoSTLFileKit SoSTLFileKit.h ForeignFiles/SoSTLFileKit.h
110 \brief SoSTLFileKit is a class for using STL files with Coin.
111
112 Class for using STL files with Coin. You can use it to read and
113 write STL files, and convert back and forth between Open Inventor
114 scene graphs and SoSTLFileKits.
115
116 STL files are 3D models intended for 3D printers, and is a format
117 supported by a wide variety of computer-aided design programs. STL
118 models are, because of their intended purpose, always
119 representations of solid objects. STL is short for
120 Stereolithography, the process used for 3D printing.
121
122 Ordinary STL models do not contain color information. There are,
123 however, two extensions to the binary file format for specifying
124 color. Currently neither extension is supported. This is caused by
125 lack of sample models using the extensions and will be added as soon
126 as such models are found. We have the specs on the extensions, and
127 it should be pretty straight-forwards to implement, but we want to
128 get it right at once since we have write support (we don't want to
129 inadvertently create a third color extension ;).
130
131 When writing STL files, certain STL model criterias are not enforced
132 by SoSTLFileKit. These are:
133
134 - STL models should represent complete solids - it is the user's
135 responsibility to give models of solid data to readScene(), and
136 not readScene()'s responsibility to check the incoming data.
137
138 - STL models should have all triangles in counterclockwise order.
139 This is not enforced either.
140
141 - STL models should reside in the positive octant of the coordinate
142 space. This is also the user's responsibility to ensure, although
143 adding functionality for translating the model should be easy, so
144 it might get implemented.
145
146 Since the color extensions are not supported yet, color information
147 is not collected either when converting Open Inventor scene graphs to
148 SoSTLFileKits.
149
150 \relates foreignfileformats
151 \COIN_CLASS_EXTENSION
152 \since Coin 3.0
153 */
154
155 #define PRIVATE(obj) ((obj)->pimpl)
156
SO_KIT_SOURCE(SoSTLFileKit)157 SO_KIT_SOURCE(SoSTLFileKit)
158
159 /*!
160 Initializes class and registers file identification functions.
161 */
162
163 void
164 SoSTLFileKit::initClass(void)
165 {
166 SO_KIT_INIT_CLASS(SoSTLFileKit, SoForeignFileKit, SoForeignFileKit);
167
168 SoType type = SoSTLFileKit::getClassTypeId();
169 SoForeignFileKit::registerFileExtension(type, "stl", SoSTLFileKit::identify);
170 }
171
172 /*!
173 Returns wether or not \a filename is identified as an STL file.
174 */
175
176 SbBool
identify(const char * filename)177 SoSTLFileKit::identify(const char * filename)
178 {
179 assert(filename);
180 stl_reader * reader = stl_reader_create(filename);
181 if ( !reader ) {
182 return FALSE;
183 }
184 stl_reader_destroy(reader);
185 return TRUE;
186 }
187
188 /*!
189 Constructor.
190 */
191
SoSTLFileKit(void)192 SoSTLFileKit::SoSTLFileKit(void)
193 {
194 PRIVATE(this) = new SoSTLFileKitP(this);
195
196 SO_KIT_INTERNAL_CONSTRUCTOR(SoSTLFileKit);
197
198 SO_KIT_ADD_FIELD(info, (""));
199 SO_KIT_ADD_FIELD(binary, (FALSE));
200 SO_KIT_ADD_FIELD(colorization, (SoSTLFileKit::GREY));
201
202 SO_KIT_DEFINE_ENUM_VALUE(Colorization, GREY);
203 SO_KIT_DEFINE_ENUM_VALUE(Colorization, MATERIALISE);
204 SO_KIT_DEFINE_ENUM_VALUE(Colorization, TNO_VISICAM);
205
206 SO_KIT_SET_SF_ENUM_TYPE(colorization, Colorization);
207
208 SO_KIT_ADD_CATALOG_ENTRY(facets, SoIndexedFaceSet,
209 FALSE, topSeparator, \x0, FALSE);
210 SO_KIT_ADD_CATALOG_ENTRY(coordinates, SoCoordinate3,
211 FALSE, topSeparator, facets, FALSE);
212 SO_KIT_ADD_CATALOG_ENTRY(material, SoMaterial,
213 FALSE, topSeparator, coordinates, FALSE);
214 SO_KIT_ADD_CATALOG_ENTRY(materialbinding, SoMaterialBinding,
215 FALSE, topSeparator, material, FALSE);
216 SO_KIT_ADD_CATALOG_ENTRY(normals, SoNormal,
217 FALSE, topSeparator, materialbinding, FALSE);
218 SO_KIT_ADD_CATALOG_ENTRY(normalbinding, SoNormalBinding,
219 FALSE, topSeparator, normals, FALSE);
220 SO_KIT_ADD_CATALOG_ENTRY(texture, SoTexture2,
221 FALSE, topSeparator, normalbinding, FALSE);
222 SO_KIT_ADD_CATALOG_ENTRY(shapehints, SoShapeHints,
223 FALSE, topSeparator, texture, FALSE);
224
225 SO_KIT_INIT_INSTANCE();
226 }
227
228 /*!
229 Destructor.
230 */
231
~SoSTLFileKit(void)232 SoSTLFileKit::~SoSTLFileKit(void)
233 {
234 delete PRIVATE(this);
235 PRIVATE(this) = NULL;
236 }
237
238 // doc in inherited class
239 SbBool
canReadFile(const char * filename) const240 SoSTLFileKit::canReadFile(const char * filename) const
241 {
242 if ( !filename ) return TRUE; // we can read STL files, in general
243 return SoSTLFileKit::identify(filename);
244 }
245
246 /*!
247 Reads in an STL file. Both ascii and binary files are supported.
248 For binary files, the color extensions are not implemented yet.
249
250 Returns FALSE if \a filename could not be opened or parsed
251 correctly.
252
253 \sa canReadFile
254 */
255
256 SbBool
readFile(const char * filename)257 SoSTLFileKit::readFile(const char * filename)
258 {
259 assert(filename);
260
261 this->reset();
262
263 stl_reader * reader = stl_reader_create(filename);
264 if ( !reader ) {
265 SoDebugError::postInfo("SoSTLFileKit::readFile",
266 "unable to create STL reader for '%s'.",
267 filename);
268 return FALSE;
269 }
270
271 SbBool binary = (stl_reader_flags(reader) & STL_BINARY) ? TRUE : FALSE;
272
273 SoShapeHints * hints =
274 SO_GET_ANY_PART(this, "shapehints", SoShapeHints);
275 hints->vertexOrdering.setValue(SoShapeHints::UNKNOWN_ORDERING);
276 // what it should have been
277 // hints->vertexOrdering.setValue(SoShapeHints::COUNTERCLOCKWISE);
278 hints->shapeType.setValue(SoShapeHints::SOLID);
279 hints->faceType.setValue(SoShapeHints::UNKNOWN_FACE_TYPE);
280
281 SoNormalBinding * normalbinding =
282 SO_GET_ANY_PART(this, "normalbinding", SoNormalBinding);
283 normalbinding->value = SoNormalBinding::PER_FACE_INDEXED;
284
285 stl_facet * facet = stl_facet_create();
286 SbBool loop = TRUE, success = TRUE;
287 while ( loop ) {
288 const int peekval = stl_reader_peek(reader);
289 if ( peekval == STL_BEGIN ) {
290 } else if ( peekval == STL_INIT_INFO ) {
291 // FIXME: set info
292 } else if ( peekval == STL_EXIT_INFO ) {
293 } else if ( peekval == STL_END ) {
294 loop = FALSE;
295 } else if ( peekval == STL_FACET ) {
296 stl_real x, y, z;
297 stl_reader_fill_facet(reader, facet);
298 stl_facet_get_normal(facet, &x, &y, &z);
299 SbVec3f normal((float) x, (float) y, (float) z);
300 stl_facet_get_vertex1(facet, &x, &y, &z);
301 SbVec3f vertex1((float) x, (float) y, (float) z);
302 stl_facet_get_vertex2(facet, &x, &y, &z);
303 SbVec3f vertex2((float) x, (float) y, (float) z);
304 stl_facet_get_vertex3(facet, &x, &y, &z);
305 SbVec3f vertex3((float) x, (float) y, (float) z);
306 if ( normal.length() == 0.0f ) { // auto-calculate
307 SbVec3f v1(vertex2-vertex1);
308 SbVec3f v2(vertex3-vertex1);
309 normal = v1.cross(v2);
310 float len = normal.length();
311 if ( len > 0 ) normal /= len;
312 }
313 unsigned int data = stl_facet_get_padding(facet);
314
315 SbBool added = this->addFacet(vertex1, vertex2, vertex3, normal);
316
317 #if defined(COIN_EXTRA_DEBUG) || 1
318 if ( added && binary ) {
319 // binary contains padding, which might be colorization
320 // colorization is not implemented yet, so therefore some debug
321 // output comes here so colorized models can be detected.
322 PRIVATE(this)->data->append((uint16_t) data);
323 if ( data != 0 ) {
324 fprintf(stderr, "facet %5d - data: %04x\n", PRIVATE(this)->numfacets - 1, data);
325 }
326 }
327 #endif // COIN_EXTRA_DEBUG
328 } else if ( peekval == STL_ERROR ) {
329 SoDebugError::post("SoSTLFileKit::readFile",
330 "error '%s' after %d facets, line %d.",
331 stl_reader_get_error(reader),
332 PRIVATE(this)->numfacets,
333 stl_reader_get_line_number(reader));
334 loop = FALSE;
335 success = FALSE;
336 if (strcmp(stl_reader_get_error(reader), "premature end of file") == 0) {
337 // this one we will accept though - models with missing
338 // end-indicator have been found...
339 success = TRUE;
340 }
341 }
342 }
343
344 // done - no need for the BSP trees to contain data any more
345 PRIVATE(this)->points->clear();
346 PRIVATE(this)->normals->clear();
347
348 stl_facet_destroy(facet);
349 stl_reader_destroy(reader);
350
351 if ( !success ) {
352 this->reset();
353 } else {
354 this->organizeModel();
355 }
356 return success;
357 }
358
359 // doc in inherited class
360 SbBool
canReadScene(void) const361 SoSTLFileKit::canReadScene(void) const
362 {
363 return TRUE;
364 }
365
366 /*!
367 Converts a scene graph into an SoSTLFileKit. Useful for creating
368 STL files.
369
370 \sa canReadScene, canWriteFile, writeFile
371 */
372
373 SbBool
readScene(SoNode * scene)374 SoSTLFileKit::readScene(SoNode * scene)
375 {
376 this->reset();
377
378 scene->ref();
379 SoCallbackAction cba;
380 cba.addTriangleCallback(SoType::fromName("SoNode"), add_facet_cb, this);
381 cba.apply(scene);
382 scene->unrefNoDelete();
383
384 // no need for the BSP trees to contain data any more
385 PRIVATE(this)->points->clear();
386 PRIVATE(this)->normals->clear();
387
388 this->organizeModel();
389
390 return TRUE;
391 }
392
393 SoSeparator *
convert()394 SoSTLFileKit::convert()
395 {
396 SoSeparator * sceneroot = new SoSeparator;
397 sceneroot->ref();
398
399 SoInfo * info = new SoInfo;
400 info->string = "STL model data, created by Coin " COIN_VERSION ".";
401 sceneroot->addChild(info);
402
403 SoShapeHints * shapehints_orig =
404 SO_GET_ANY_PART(this, "shapehints", SoShapeHints);
405 SoShapeHints * shapehints_copy = new SoShapeHints;
406 shapehints_copy->copyContents(shapehints_orig, FALSE);
407 sceneroot->addChild(shapehints_copy);
408
409 SoTexture2 * texture_orig = SO_GET_ANY_PART(this, "texture", SoTexture2);
410 SoTexture2 * texture_copy = new SoTexture2;
411 texture_copy->copyContents(texture_orig, FALSE);
412 sceneroot->addChild(texture_copy);
413
414 SoNormalBinding * normalbinding_orig =
415 SO_GET_ANY_PART(this, "normalbinding", SoNormalBinding);
416 SoNormalBinding * normalbinding_copy = new SoNormalBinding;
417 normalbinding_copy->copyContents(normalbinding_orig, FALSE);
418 sceneroot->addChild(normalbinding_copy);
419
420 SoNormal * normals_orig = SO_GET_ANY_PART(this, "normals", SoNormal);
421 SoNormal * normals_copy = new SoNormal;
422 normals_copy->copyContents(normals_orig, FALSE);
423 sceneroot->addChild(normals_copy);
424
425 SoMaterialBinding * materialbinding_orig =
426 SO_GET_ANY_PART(this, "materialbinding", SoMaterialBinding);
427 SoMaterialBinding * materialbinding_copy = new SoMaterialBinding;
428 materialbinding_copy->copyContents(materialbinding_orig, FALSE);
429 sceneroot->addChild(materialbinding_copy);
430
431 SoMaterial * material_orig = SO_GET_ANY_PART(this, "material", SoMaterial);
432 SoMaterial * material_copy = new SoMaterial;
433 material_copy->copyContents(material_orig, FALSE);
434 sceneroot->addChild(material_copy);
435
436 SoCoordinate3 * coordinates_orig =
437 SO_GET_ANY_PART(this, "coordinates", SoCoordinate3);
438 SoCoordinate3 * coordinates_copy = new SoCoordinate3;
439 coordinates_copy->copyContents(coordinates_orig, FALSE);
440 sceneroot->addChild(coordinates_copy);
441
442 SoIndexedFaceSet * facets_orig =
443 SO_GET_ANY_PART(this, "facets", SoIndexedFaceSet);
444 SoIndexedFaceSet * facets_copy = new SoIndexedFaceSet;
445 facets_copy->copyContents(facets_orig, FALSE);
446 sceneroot->addChild(facets_copy);
447
448 // optimize/reorganize mesh
449 SoReorganizeAction ra;
450 ra.apply(sceneroot);
451
452 // FIXME: remove redundant scene graph nodes after scene reorganization
453
454 sceneroot->unrefNoDelete();
455 return sceneroot;
456 }
457
458 // doc in inherited class
459 SbBool
canWriteFile(const char * filename) const460 SoSTLFileKit::canWriteFile(const char * filename) const
461 {
462 return inherited::canWriteFile(filename);
463 }
464
465 /*!
466 Writes the STL model to an STL file.
467
468 \sa binary, info, canWriteFile, canReadScene
469 */
470
471 SbBool
writeFile(const char * filename)472 SoSTLFileKit::writeFile(const char * filename)
473 {
474 unsigned int flags = 0;
475 if ( this->binary.getValue() ) {
476 flags |= STL_BINARY;
477 // set up flags for colorization if wanted
478 }
479
480 stl_writer * writer = stl_writer_create(filename, flags);
481 if ( !writer ) {
482 return FALSE;
483 }
484
485 stl_facet * facet = stl_facet_create();
486 assert(facet);
487 stl_writer_set_facet(writer, facet);
488
489 SbString infostring = this->info.getValue();
490 if ( infostring.getLength() > 0 ) {
491 if ( stl_writer_set_info(writer, infostring.getString()) != STL_OK ) {
492 SoDebugError::post("SoSTLFileKit::writeFile",
493 "error: '%s'",
494 stl_writer_get_error(writer));
495 return FALSE;
496 }
497 }
498
499 this->ref();
500 SoCallbackAction cba;
501 cba.addTriangleCallback(SoNode::getClassTypeId(), put_facet_cb, writer);
502 cba.apply(this);
503 this->unrefNoDelete();
504
505 stl_writer_destroy(writer);
506
507 return TRUE;
508 }
509
510 // *************************************************************************
511
512 /*!
513 Resets the STL model so it contains nothing.
514 */
515
516 void
reset(void)517 SoSTLFileKit::reset(void)
518 {
519 PRIVATE(this)->numvertices = 0;
520 PRIVATE(this)->numfacets = 0;
521 PRIVATE(this)->numnormals = 0;
522 PRIVATE(this)->numsharedvertices = 0;
523 PRIVATE(this)->numsharednormals = 0;
524 PRIVATE(this)->numredundantfacets = 0;
525
526 PRIVATE(this)->data->truncate(0);
527 PRIVATE(this)->points->clear();
528 PRIVATE(this)->normals->clear();
529
530 this->setAnyPart("shapehints", new SoShapeHints);
531 this->setAnyPart("texture", new SoTexture2);
532 this->setAnyPart("normalbinding", new SoNormalBinding);
533 this->setAnyPart("normals", new SoNormal);
534 this->setAnyPart("materialbinding", new SoMaterialBinding);
535 this->setAnyPart("material", new SoMaterial);
536 this->setAnyPart("coordinates", new SoCoordinate3);
537 this->setAnyPart("facets", new SoIndexedFaceSet);
538
539 SoNormalBinding * normalbinding =
540 SO_GET_ANY_PART(this, "normalbinding", SoNormalBinding);
541 normalbinding->value = SoNormalBinding::PER_FACE_INDEXED;
542
543 SoShapeHints * shapehints =
544 SO_GET_ANY_PART(this, "shapehints", SoShapeHints);
545 shapehints->vertexOrdering = SoShapeHints::UNKNOWN_ORDERING;
546 // proper model is
547 // shapehints->vertexOrdering = SoShapeHints::COUNTERCLOCKWISE;
548 // but many models are not proper
549 shapehints->shapeType = SoShapeHints::SOLID;
550 }
551
552 /*!
553 Adds one triangle to the STL model.
554
555 \sa reset, organizeModel
556 */
557
558 SbBool
addFacet(const SbVec3f & v1,const SbVec3f & v2,const SbVec3f & v3,const SbVec3f & n)559 SoSTLFileKit::addFacet(const SbVec3f & v1, const SbVec3f & v2, const SbVec3f & v3, const SbVec3f & n)
560 {
561 SoNormal * normals =
562 SO_GET_ANY_PART(this, "normals", SoNormal);
563 SoCoordinate3 * coordinates =
564 SO_GET_ANY_PART(this, "coordinates", SoCoordinate3);
565 SoIndexedFaceSet * facets =
566 SO_GET_ANY_PART(this, "facets", SoIndexedFaceSet);
567
568 // find existing indexes if any
569 long v1idx = PRIVATE(this)->points->findPoint(v1), v1new = (v1idx == -1);
570 long v2idx = PRIVATE(this)->points->findPoint(v2), v2new = (v2idx == -1);
571 long v3idx = PRIVATE(this)->points->findPoint(v3), v3new = (v3idx == -1);
572 if (!v1new) { v1idx = (long) reinterpret_cast<intptr_t>(PRIVATE(this)->points->getUserData(v1idx)); }
573 if (!v2new) { v2idx = (long) reinterpret_cast<intptr_t>(PRIVATE(this)->points->getUserData(v2idx)); }
574 if (!v3new) { v3idx = (long) reinterpret_cast<intptr_t>(PRIVATE(this)->points->getUserData(v3idx)); }
575 long nidx = PRIVATE(this)->normals->findPoint(n);
576 if (nidx != -1) { nidx = (long) reinterpret_cast<intptr_t>(PRIVATE(this)->normals->getUserData(nidx)); }
577
578 // toss out invalid facets - facets where two or more points are in
579 // the same location. what are these - are they lines and points or
580 // something? selection? borders? creases?
581 if ((!v1new && !v2new && (v1idx == v2idx)) ||
582 (!v1new && !v3new && (v1idx == v3idx)) ||
583 (!v2new && !v3new && (v2idx == v3idx)) ||
584 (v1new && v2new && (v1 == v2)) ||
585 (v1new && v3new && (v1 == v3)) ||
586 (v2new && v3new && (v2 == v3))) {
587 // the above test is optimized for using vertex indexes if
588 // possible and avoid vec3f-comparisons when index-comparisons
589 // should have sufficed.
590 PRIVATE(this)->numredundantfacets += 1;
591 return FALSE;
592 }
593
594 #if 0 // disabled (O(n^2))
595 // toss out redundant facets, if any...
596 if (!v1new && !v2new && !v3new) {
597 int count = facets->coordIndex.getNum();
598 const int32_t * points = facets->coordIndex.getValues(0);
599 int i;
600 for (i = 0; i < count; i++) {
601 if (points[i] == v1idx) {
602 int beg = i - (i % 4);
603 if ( ((points[beg] == v1idx) && (points[beg+1] == v2idx) &&
604 (points[beg+2] == v3idx)) ||
605 ((points[beg] == v2idx) && (points[beg+1] == v3idx) &&
606 (points[beg+2] == v1idx)) ||
607 ((points[beg] == v3idx) && (points[beg+1] == v1idx) &&
608 (points[beg+2] == v2idx)) ) {
609 // same vertices, same vertex ordering (we drop comparing normal)
610 PRIVATE(this)->numredundantfacets += 1;
611 return FALSE;
612 }
613 }
614 }
615 }
616 #endif
617
618 // add facet (triangle) to faceset
619 if (v1new) {
620 v1idx = PRIVATE(this)->numvertices;
621 coordinates->point.set1Value(v1idx, v1);
622 PRIVATE(this)->points->addPoint(v1, (void *) v1idx);
623 PRIVATE(this)->numvertices++;
624 } else {
625 PRIVATE(this)->numsharedvertices++;
626 }
627 facets->coordIndex.set1Value(PRIVATE(this)->numfacets*4, v1idx);
628
629 if (v2new) {
630 v2idx = PRIVATE(this)->numvertices;
631 coordinates->point.set1Value(v2idx, v2);
632 PRIVATE(this)->points->addPoint(v2, (void *) v2idx);
633 PRIVATE(this)->numvertices++;
634 } else {
635 PRIVATE(this)->numsharedvertices++;
636 }
637 facets->coordIndex.set1Value(PRIVATE(this)->numfacets*4+1, v2idx);
638
639 if (v3new) {
640 v3idx = PRIVATE(this)->numvertices;
641 coordinates->point.set1Value(v3idx, v3);
642 PRIVATE(this)->points->addPoint(v3, (void *) v3idx);
643 PRIVATE(this)->numvertices++;
644 } else {
645 PRIVATE(this)->numsharedvertices++;
646 }
647 facets->coordIndex.set1Value(PRIVATE(this)->numfacets*4+2, v3idx);
648 facets->coordIndex.set1Value(PRIVATE(this)->numfacets*4+3, -1);
649
650 if (nidx == -1) {
651 nidx = PRIVATE(this)->numnormals;
652 normals->vector.set1Value(nidx, n);
653 PRIVATE(this)->normals->addPoint(n, (void *) nidx);
654 PRIVATE(this)->numnormals++;
655 } else {
656 PRIVATE(this)->numsharednormals++;
657 }
658 facets->normalIndex.set1Value(PRIVATE(this)->numfacets, nidx);
659
660 PRIVATE(this)->numfacets++;
661 return TRUE;
662 }
663
664 /*!
665 Should be called after the STL model is completely set up in the
666 SoSTLFileKit through import from a file or from a scene graph. The
667 model will then be optimized for fast rendering.
668
669 \sa addFacet, reset
670 */
671
672 void
organizeModel(void)673 SoSTLFileKit::organizeModel(void)
674 {
675 #if defined(COIN_EXTRA_DEBUG)
676 SoDebugError::postInfo("SoSTLFileKit::organizeModel",
677 "model data imported successfully. "
678 "%d unique vertices, %d reuses. "
679 "%d unique normals, %d reuses. "
680 "%d facets, %d redundant facets.",
681 PRIVATE(this)->numvertices,
682 PRIVATE(this)->numsharedvertices,
683 PRIVATE(this)->numnormals,
684 PRIVATE(this)->numsharednormals,
685 PRIVATE(this)->numfacets,
686 PRIVATE(this)->numredundantfacets);
687 #endif // COIN_EXTRA_DEBUG
688
689 SoIndexedFaceSet * facets =
690 SO_GET_ANY_PART(this, "facets", SoIndexedFaceSet);
691
692 assert(facets->coordIndex.getNum() == PRIVATE(this)->numfacets*4);
693 assert(facets->normalIndex.getNum() == (PRIVATE(this)->numfacets));
694
695 if ( PRIVATE(this)->numfacets > 300 ) {
696 // FIXME: at some number of facets, reorganization for faster
697 // rendering should really be performed.
698 }
699 }
700
701 /*!
702 Helper callback for readScene(), calling addFacet() for each
703 triangle in the provided scene graph.
704
705 \sa readScene
706 */
707
708 void
add_facet_cb(void * closure,SoCallbackAction * action,const SoPrimitiveVertex * v1,const SoPrimitiveVertex * v2,const SoPrimitiveVertex * v3)709 SoSTLFileKit::add_facet_cb(void * closure,
710 SoCallbackAction * action,
711 const SoPrimitiveVertex * v1,
712 const SoPrimitiveVertex * v2,
713 const SoPrimitiveVertex * v3)
714 {
715 assert(closure); assert(v1); assert(v2); assert(v3);
716 SoSTLFileKit * filekit = (SoSTLFileKit *) closure;
717
718 const SbMatrix & mm = action->getModelMatrix();
719
720 // move the points into world space
721 SbVec3f vertex1, vertex2, vertex3;
722 mm.multVecMatrix(v1->getPoint(), vertex1);
723 mm.multVecMatrix(v2->getPoint(), vertex2);
724 mm.multVecMatrix(v3->getPoint(), vertex3);
725
726 // flip ordering if the current shape is CW
727 if (action->getVertexOrdering() == SoShapeHints::CLOCKWISE) {
728 SbVec3f tmp = vertex2;
729 vertex2 = vertex3;
730 vertex3 = tmp;
731 }
732 SbVec3f vec1(vertex2-vertex1);
733 SbVec3f vec2(vertex3-vertex1);
734 SbVec3f normal(vec1.cross(vec2));
735 (void) normal.normalize();
736
737 filekit->addFacet(vertex1, vertex2, vertex3, normal);
738 }
739
740 /*!
741 Helper callback for writeFile(), writing each triangle in the STL
742 model to the STL file.
743
744 \sa writeFile
745 */
746
747 void
put_facet_cb(void * closure,SoCallbackAction * COIN_UNUSED_ARG (action),const SoPrimitiveVertex * v1,const SoPrimitiveVertex * v2,const SoPrimitiveVertex * v3)748 SoSTLFileKit::put_facet_cb(void * closure,
749 SoCallbackAction * COIN_UNUSED_ARG(action),
750 const SoPrimitiveVertex * v1,
751 const SoPrimitiveVertex * v2,
752 const SoPrimitiveVertex * v3)
753 {
754 assert(closure); assert(v1); assert(v2); assert(v3);
755 stl_writer * writer = (stl_writer *) closure;
756
757 SbVec3f vertex1(v1->getPoint());
758 SbVec3f vertex2(v2->getPoint());
759 SbVec3f vertex3(v3->getPoint());
760
761 SbVec3f vec1(vertex2-vertex1);
762 SbVec3f vec2(vertex3-vertex1);
763 SbVec3f normal(vec1.cross(vec2));
764 (void) normal.normalize();
765
766 stl_facet * facet = stl_writer_get_facet(writer);
767 assert(facet);
768 stl_facet_set_vertex1(facet, vertex1[0], vertex1[1], vertex1[2]);
769 stl_facet_set_vertex2(facet, vertex2[0], vertex2[1], vertex2[2]);
770 stl_facet_set_vertex3(facet, vertex3[0], vertex3[1], vertex3[2]);
771 stl_facet_set_normal(facet, normal[0], normal[1], normal[2]);
772 stl_facet_set_padding(facet, 0);
773
774 stl_writer_put_facet(writer, facet);
775 }
776
777 #undef PRIVATE
778 #endif // HAVE_NODEKITS
779