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