root/OpenSceneGraph/trunk/src/osgPlugins/stl/ReaderWriterSTL.cpp @ 10759

Revision 10759, 14.9 kB (checked in by robert, 4 years ago)

Replaced catch usage

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1// -*-c++-*-
2
3/*
4 * $Id$
5 *
6 * STL importer for OpenSceneGraph.
7 * Copyright (c)2004 Ulrich Hertlein <u.hertlein@sandbox.de>
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22 */
23
24#include <osg/Notify>
25#include <osg/Endian>
26
27#include <osgDB/Registry>
28#include <osgDB/ReadFile>
29#include <osgDB/FileNameUtils>
30#include <osgDB/FileUtils>
31
32#include <osgUtil/TriStripVisitor>
33#include <osgUtil/SmoothingVisitor>
34#include <osg/TriangleFunctor>
35
36#include <osg/Geode>
37#include <osg/Geometry>
38
39#include <stdio.h>
40#include <sys/types.h>
41#include <sys/stat.h>
42
43#include <string.h>
44
45/**
46 * STL importer for OpenSceneGraph.
47 */
48class ReaderWriterSTL : public osgDB::ReaderWriter
49{
50public:
51    ReaderWriterSTL()
52    {
53        supportsExtension("stl","STL binary format");
54        supportsExtension("sta","STL ASCII format");
55        supportsOption("smooth", "run SmoothingVisitor");
56        supportsOption("separateFiles", "Save every geode in a different file. Can be a Huge amount of Files!!!");
57    }
58
59    virtual const char* className() const {
60        return "STL Reader";
61    }
62
63    virtual ReadResult readNode(const std::string& fileName, const osgDB::ReaderWriter::Options*) const;
64    virtual WriteResult writeNode(const osg::Node& /*node*/,const std::string& /*fileName*/,const Options* =NULL) const ;
65private:
66
67    struct ReaderObject
68    {
69        ReaderObject():
70            _generateNormal(true),
71            _numFacets(0) {}
72
73        bool _generateNormal;
74        unsigned int _numFacets;
75
76        osg::ref_ptr<osg::Vec3Array> _vertex;
77        osg::ref_ptr<osg::Vec3Array> _normal;
78        osg::ref_ptr<osg::Vec4Array> _color;
79
80        bool readStlAscii(FILE* fp);
81        bool readStlBinary(FILE* fp);
82    };
83
84  class CreateStlVisitor : public osg::NodeVisitor {
85  public:
86
87    CreateStlVisitor( std::string const & fout, const osgDB::ReaderWriter::Options* options = 0): osg::NodeVisitor( osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN ), counter(0), m_fout(fout), m_options(options)
88    {
89      if (options && (options->getOptionString() == "separateFiles"))
90      {
91        osg::notify(osg::INFO) << "ReaderWriterSTL::writeNode: Files are seperated written" << std::endl;
92      } else {
93        m_f = new std::ofstream(m_fout.c_str());       
94        *m_f << "solid " << counter << std::endl;
95      }
96    };
97
98    std::string i2s( int i) {
99      char buf[16];  // -2^31 == -2147483648 needs 11 chars + \0  -> 12 (+4 for security ;-)
100      sprintf(buf,"%d",i);
101      return buf;
102    }
103
104    virtual void apply(  osg::Geode& node ){
105      osg::Matrix mat = osg::computeLocalToWorld( getNodePath() );
106     
107      if (m_options && (m_options->getOptionString() == "separateFiles")) {
108        std::string sepFile = m_fout + i2s(counter);
109        m_f = new std::ofstream(sepFile.c_str());
110        *m_f << "solid " << std::endl;
111      }
112     
113      for ( unsigned int i = 0; i < node.getNumDrawables(); ++i ) {
114        osg::TriangleFunctor<PushPoints> tf;
115        tf.m_stream = m_f;
116        tf.m_mat = mat;
117        node.getDrawable( i )->accept( tf );
118      }
119     
120      if (m_options && (m_options->getOptionString() == "separateFiles")) {
121        *m_f << "endsolid " << std::endl;
122        m_f->close();
123        delete m_f;
124      }
125     
126      ++counter;
127      traverse(node);
128     
129    }
130    //        nHandle->SetLocation( Frame( mat ) );
131    ~CreateStlVisitor() {
132      if (m_options && (m_options->getOptionString() == "separateFiles")) {
133        osg::notify(osg::INFO) << "ReaderWriterSTL::writeNode: " << counter-1 << "Files were written" << std::endl;
134      } else {
135        *m_f << "endsolid " << std::endl;
136        m_f->close();
137        delete m_f;
138      }
139    }
140   
141    const std::string& getErrorString() const { return m_ErrorString; }
142   
143  private:
144    int counter;
145    std::ofstream* m_f;
146    std::string m_fout;
147    osgDB::ReaderWriter::Options const * m_options;
148    std::string m_ErrorString;
149   
150   
151    struct PushPoints {
152      std::ofstream* m_stream;
153      osg::Matrix m_mat;
154      inline void operator () ( const osg::Vec3& _v1, const osg::Vec3& _v2, const osg::Vec3& _v3, bool treatVertexDataAsTemporary ) {
155        osg::Vec3 v1 = _v1 * m_mat;
156        osg::Vec3 v2 = _v2 * m_mat;
157        osg::Vec3 v3 = _v3 * m_mat;
158        osg::Vec3 vV1V2 = v2-v1;
159        osg::Vec3 vV1V3 = v3-v1;
160        osg::Vec3 vNormal = vV1V2.operator ^(vV1V3);
161        *m_stream << "facet normal " << vNormal[0] << " " << vNormal[1] << " " << vNormal[2] << std::endl;
162        *m_stream << "outer loop" << std::endl;
163        *m_stream << "vertex " << v1[0] << " " << v1[1] << " " << v1[2] << std::endl;
164        *m_stream << "vertex " << v2[0] << " " << v2[1] << " " << v2[2] << std::endl;
165        *m_stream << "vertex " << v3[0] << " " << v3[1] << " " << v3[2] << std::endl;
166        *m_stream << "endloop" << std::endl;
167        *m_stream << "endfacet " << std::endl;
168      }
169     
170    };
171   
172   
173  };
174 
175 
176       
177};
178
179
180// Register with Registry to instantiate the above reader/writer.
181REGISTER_OSGPLUGIN(stl, ReaderWriterSTL)
182
183
184/*
185 * STL
186 */
187struct StlHeader {
188    char text[80];
189    unsigned int numFacets;
190};
191const unsigned int sizeof_StlHeader = 84;
192
193struct StlVector {
194    float x,y,z;
195};
196struct StlFacet {
197    StlVector normal;
198    StlVector vertex[3];
199    unsigned short color;
200};
201const unsigned int sizeof_StlFacet = 50;
202
203const unsigned short StlHasColor = 0x8000;
204const unsigned short StlColorSize = 0x1f;        // 5 bit
205const float StlColorDepth = float(StlColorSize); // 2^5 - 1
206
207
208// Read node
209osgDB::ReaderWriter::ReadResult ReaderWriterSTL::readNode(const std::string& file, const osgDB::ReaderWriter::Options* options) const
210{
211    std::string ext = osgDB::getLowerCaseFileExtension(file);
212    if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
213
214    std::string fileName = osgDB::findDataFile( file, options );
215    if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;
216
217    osg::notify(osg::INFO) << "ReaderWriterSTL::readNode(" << fileName.c_str() << ")\n";
218
219    // determine ASCII vs. binary mode
220    FILE* fp = osgDB::fopen(fileName.c_str(), "rb");
221    if (!fp) {
222        return ReadResult::FILE_NOT_FOUND;
223    }
224
225    ReaderObject readerObject;
226
227    // assumes "unsigned int" is 4 bytes...
228    StlHeader header;
229    if (fread((void*) &header, sizeof(header), 1, fp) != 1) {
230        fclose(fp);
231        return ReadResult::ERROR_IN_READING_FILE;
232    }
233    bool isBinary = false;
234
235    // calculate expected file length from number of facets
236    unsigned int expectFacets = header.numFacets;
237    if (osg::getCpuByteOrder() == osg::BigEndian) {
238        osg::swapBytes4((char*) &expectFacets);
239    }
240    off_t expectLen = sizeof_StlHeader + expectFacets * sizeof_StlFacet;
241 
242    struct stat stb;
243    if (fstat(fileno(fp), &stb) < 0)
244    {
245        osg::notify(osg::FATAL) << "ReaderWriterSTL::readNode: Unable to stat '" << fileName << "'" << std::endl;
246        fclose(fp);
247        return ReadResult::ERROR_IN_READING_FILE;
248    }
249
250    if (stb.st_size == expectLen)
251    {
252        // assume binary
253        readerObject._numFacets = expectFacets;
254        isBinary = true;
255    }
256    else if (strstr(header.text, "solid") != 0)
257    {
258        // assume ASCII
259        isBinary = false;
260    }
261    else {
262        osg::notify(osg::FATAL) << "ReaderWriterSTL::readNode(" << fileName.c_str() << ") unable to determine file format" << std::endl;
263        fclose(fp);
264        return ReadResult::ERROR_IN_READING_FILE;
265    }
266
267    if (!isBinary)
268    {
269        fclose(fp);
270        fp = osgDB::fopen(fileName.c_str(), "r");
271    }
272
273    // read
274    rewind(fp);
275    bool ok = (isBinary ? readerObject.readStlBinary(fp) : readerObject.readStlAscii(fp));
276    fclose(fp);
277
278    if (!ok)
279    {
280        return ReadResult::FILE_NOT_HANDLED;
281    }
282   
283    osg::notify(osg::INFO) << "STL loader found " << readerObject._numFacets << " facets" << std::endl;
284
285    /*
286     * setup geometry
287     */
288    osg::Geometry* geom = new osg::Geometry;
289    geom->setVertexArray(readerObject._vertex.get());
290
291    geom->setNormalArray(readerObject._normal.get());
292    geom->setNormalBinding(osg::Geometry::BIND_PER_PRIMITIVE);
293
294    if (readerObject._color.valid()) {
295        osg::notify(osg::INFO) << "STL file with color" << std::endl;
296        geom->setColorArray(readerObject._color.get());
297        geom->setColorBinding(osg::Geometry::BIND_PER_PRIMITIVE);
298    }
299
300    geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES, 0, readerObject._numFacets*3));
301
302    osg::Geode* geode = new osg::Geode;
303    geode->addDrawable(geom);
304   
305    if (options && (options->getOptionString() == "smooth")) {
306        osgUtil::SmoothingVisitor smooter;
307        geode->accept(smooter);
308    }
309
310    osgUtil::TriStripVisitor tristripper;
311    tristripper.stripify(*geom);
312
313    return geode;
314}
315
316
317/**********************************************************************
318 *
319 * Private
320 *
321 **********************************************************************/
322
323bool ReaderWriterSTL::ReaderObject::readStlAscii(FILE* fp)
324{
325
326    unsigned int vertexCount = 0;
327    unsigned int facetIndex[] = { 0,0,0 };
328    unsigned int vertexIndex = 0;
329    unsigned int normalIndex = 0;
330
331    const int MaxLineSize = 256;
332    char buf[MaxLineSize];
333    char sx[MaxLineSize],sy[MaxLineSize],sz[MaxLineSize];
334
335    while (fgets(buf, sizeof(buf), fp)) {
336
337        // strip '\n' or '\r\n' and trailing whitespace
338        unsigned int len = strlen(buf)-1;
339        while (len && (buf[len] == '\n' || buf[len] == '\r' || isspace(buf[len]))) {
340            buf[len--] = '\0';
341        }
342        if (len == 0 || buf[0] == '\0') {
343            continue;
344        }
345
346        // strip leading whitespace
347        char* bp = buf;
348        while (isspace(*bp)) {
349            ++bp;
350        }
351
352        if (strncmp(bp, "vertex", 6) == 0)
353        {
354            if (sscanf(bp+6, "%s %s %s", sx,sy,sz) == 3) {
355                if (!_vertex.valid())
356                    _vertex = new osg::Vec3Array;
357
358                float vx = osg::asciiToFloat(sx);
359                float vy = osg::asciiToFloat(sy);
360                float vz = osg::asciiToFloat(sz);
361
362                vertexIndex = _vertex->size();
363                if (vertexCount < 3) {
364                    _vertex->push_back(osg::Vec3(vx,vy,vz));
365                    facetIndex[vertexCount++] = vertexIndex;
366                }
367                else {
368                    /*
369                     * There are some invalid ASCII files around (at least one ;-)
370                     * that have more than three vertices per facet - add an
371                     * additional triangle.
372                     */
373                    _normal->push_back((*_normal)[normalIndex]);
374                    _vertex->push_back((*_vertex)[facetIndex[0]]);
375                    _vertex->push_back((*_vertex)[facetIndex[2]]);
376                    _vertex->push_back(osg::Vec3(vx,vy,vz));
377                    facetIndex[1] = facetIndex[2];
378                    facetIndex[2] = vertexIndex;
379                    _numFacets++;
380                }
381            }
382        }
383        else if (strncmp(bp, "facet", 5) == 0)
384        {
385            if (sscanf(bp+5, "%*s %s %s %s", sx,sy,sz) == 3) {
386
387                float nx = osg::asciiToFloat(sx);
388                float ny = osg::asciiToFloat(sy);
389                float nz = osg::asciiToFloat(sz);
390
391                if (!_normal.valid())
392                    _normal = new osg::Vec3Array;
393
394                osg::Vec3 normal(nx,ny,nz);
395                normal.normalize();
396
397                normalIndex = _normal->size();
398                _normal->push_back(normal);
399
400                _numFacets++;
401                vertexCount = 0;
402            }
403        }
404        else if (strncmp(bp, "solid", 5) == 0) {
405            osg::notify(osg::INFO) << "STL loader parsing '" << bp + 6 << "'" << std::endl;
406        }
407    }
408
409    return true;
410}
411
412bool ReaderWriterSTL::ReaderObject::readStlBinary(FILE* fp)
413{
414    // seek to beginning of facets
415    ::fseek(fp, sizeof_StlHeader, SEEK_SET);
416
417    StlFacet facet;
418    for (unsigned int i = 0; i < _numFacets; ++i) {
419
420        if (::fread((void*) &facet, sizeof_StlFacet, 1, fp) != 1) {
421            osg::notify(osg::FATAL) << "ReaderWriterSTL::readStlBinary: Failed to read facet " << i << std::endl;
422            return false;
423        }
424
425        // vertices
426        if (!_vertex)
427            _vertex = new osg::Vec3Array;
428        osg::Vec3 v0(facet.vertex[0].x,facet.vertex[0].y,facet.vertex[0].z);
429        osg::Vec3 v1(facet.vertex[1].x,facet.vertex[1].y,facet.vertex[1].z);
430        osg::Vec3 v2(facet.vertex[2].x,facet.vertex[2].y,facet.vertex[2].z);
431        _vertex->push_back(v0);
432        _vertex->push_back(v1);
433        _vertex->push_back(v2);
434
435        // per-facet normal
436        osg::Vec3 normal;
437        if (_generateNormal) {
438            osg::Vec3 d01 = v1 - v0;
439            osg::Vec3 d02 = v2 - v0;
440            normal = d01 ^ d02;
441            normal.normalize();
442        }
443        else {
444            normal.set(facet.normal.x,facet.normal.y,facet.normal.z);
445        }
446        if (!_normal.valid())
447            _normal = new osg::Vec3Array;
448        _normal->push_back(normal);
449
450        /*
451         * color extension
452         * RGB555 with most-significat bit indicating if color is present
453         */
454        if (facet.color & StlHasColor) {
455            if (!_color) {
456                _color = new osg::Vec4Array;
457            }
458            float r = ((facet.color >> 10) & StlColorSize) / StlColorDepth;
459            float g = ((facet.color >> 5) & StlColorSize) / StlColorDepth;
460            float b = (facet.color & StlColorSize) / StlColorDepth;
461            _color->push_back(osg::Vec4(r,g,b,1.0f));
462        }
463    }
464
465    return true;
466}
467
468osgDB::ReaderWriter::WriteResult ReaderWriterSTL::writeNode(const osg::Node& node,const std::string& fileName, const Options* opts) const
469{
470    if (fileName.empty()) return WriteResult::FILE_NOT_HANDLED;
471
472    std::string ext = osgDB::getLowerCaseFileExtension(fileName);
473    if (ext != "stl" )
474    {
475        // sta - extension implies STL-Binary...
476        osg::notify(osg::INFO) << "ReaderWriterSTL::writeNode: Only STL-ASCII-files supported'" << std::endl;
477        return WriteResult::FILE_NOT_HANDLED;
478    }
479 
480    CreateStlVisitor createStlVisitor( fileName, opts );
481    const_cast<osg::Node&>(node).accept( createStlVisitor );
482
483    if (createStlVisitor.getErrorString().empty())
484    {
485        return WriteResult::FILE_SAVED;
486    }
487    else
488    {
489        osg::notify(osg::NOTICE)<<"Error: "<<createStlVisitor.getErrorString()<<std::endl;
490        return WriteResult::ERROR_IN_WRITING_FILE;
491    }
492}
Note: See TracBrowser for help on using the browser.