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

Revision 13502, 19.8 kB (checked in by robert, 18 hours ago)

Removed GL header as it's already included via the ${OPENSCENEGRAPH_OPENGL_HEADER} entry.

  • 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 *
8 * Copyright (c) 2004 Ulrich Hertlein <u.hertlein@sandbox.de>
9 * Copyright (c) 2012 Piotr Domagalski <piotr@domagalski.com>
10 *
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
15 *
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19 * Lesser General Public License for more details.
20 *
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24 */
25
26#include <osg/Notify>
27#include <osg/Endian>
28
29#include <osgDB/Registry>
30#include <osgDB/ReadFile>
31#include <osgDB/FileNameUtils>
32#include <osgDB/FileUtils>
33
34#include <osgUtil/TriStripVisitor>
35#include <osgUtil/SmoothingVisitor>
36#include <osg/TriangleFunctor>
37
38#include <osg/Geode>
39#include <osg/Geometry>
40
41#include <stdio.h>
42#include <sys/types.h>
43#include <sys/stat.h>
44
45#include <string.h>
46
47#include <memory>
48
49/**
50 * STL importer for OpenSceneGraph.
51 */
52class ReaderWriterSTL : public osgDB::ReaderWriter
53{
54public:
55    ReaderWriterSTL()
56    {
57        supportsExtension("stl", "STL binary format");
58        supportsExtension("sta", "STL ASCII format");
59        supportsOption("smooth", "Run SmoothingVisitor");
60        supportsOption("separateFiles", "Save each geode in a different file. Can result in a huge amount of files!");
61        supportsOption("dontSaveNormals", "Set all normals to [0 0 0] when saving to a file.");
62    }
63
64    virtual const char* className() const
65    {
66        return "STL Reader";
67    }
68
69    virtual ReadResult readNode(const std::string& fileName, const osgDB::ReaderWriter::Options*) const;
70    virtual WriteResult writeNode(const osg::Node& node, const std::string& fileName, const Options* = NULL) const;
71
72private:
73    class ReaderObject
74    {
75    public:
76        ReaderObject(bool generateNormals = true):
77            _generateNormal(generateNormals),
78            _numFacets(0)
79        {
80        }
81
82        virtual ~ReaderObject()
83        {
84        }
85
86        enum ReadResult
87        {
88            ReadSuccess,
89            ReadError,
90            ReadEOF
91        };
92
93        virtual ReadResult read(FILE *fp) = 0;
94
95        osg::ref_ptr<osg::Geometry> asGeometry() const
96        {
97            osg::ref_ptr<osg::Geometry> geom = new osg::Geometry;
98
99            geom->setVertexArray(_vertex.get());
100           
101            if (_normal.valid())
102            {
103                // need to convert per triangle normals to per vertex
104                osg::ref_ptr<osg::Vec3Array> perVertexNormals = new osg::Vec3Array;
105                perVertexNormals->reserveArray(_normal->size() * 3);
106                for(osg::Vec3Array::iterator itr = _normal->begin();
107                    itr != _normal->end();
108                    ++itr)
109                {
110                    perVertexNormals->push_back(*itr);
111                }
112               
113                geom->setNormalArray(perVertexNormals.get());
114                geom->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
115            }
116           
117            if (_color.valid())
118            {
119                // need to convert per triangle colours to per vertex
120                OSG_INFO << "STL file with color" << std::endl;
121                osg::ref_ptr<osg::Vec4Array> perVertexColours = new osg::Vec4Array;
122                perVertexColours->reserveArray(_color->size() * 3);
123                for(osg::Vec4Array::iterator itr = _color->begin();
124                    itr != _color->end();
125                    ++itr)
126                {
127                    perVertexColours->push_back(*itr);
128                }
129                geom->setColorArray(perVertexColours.get());
130                geom->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
131            }
132
133            geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES, 0, _numFacets * 3));
134
135            osgUtil::TriStripVisitor tristripper;
136            tristripper.stripify(*geom);
137
138            return geom;
139        }
140
141        bool isEmpty()
142        {
143            return _numFacets == 0;
144        }
145
146        std::string& getName()
147        {
148            return _solidName;
149        }
150
151    protected:
152        bool _generateNormal;
153        unsigned int _numFacets;
154
155        std::string _solidName;
156        osg::ref_ptr<osg::Vec3Array> _vertex;
157        osg::ref_ptr<osg::Vec3Array> _normal;
158        osg::ref_ptr<osg::Vec4Array> _color;
159
160        void clear()
161        {
162            _solidName = "";
163            _numFacets = 0;
164            _vertex = osg::ref_ptr<osg::Vec3Array>();
165            _normal = osg::ref_ptr<osg::Vec3Array>();
166            _color = osg::ref_ptr<osg::Vec4Array>();
167        }
168    };
169
170    class AsciiReaderObject : public ReaderObject
171    {
172    public:
173        ReadResult read(FILE *fp);
174    };
175
176    class BinaryReaderObject : public ReaderObject
177    {
178    public:
179        BinaryReaderObject(unsigned int expectNumFacets, bool generateNormals = true)
180            : ReaderObject(generateNormals),
181            _expectNumFacets(expectNumFacets)
182        {
183        }
184
185        ReadResult read(FILE *fp);
186
187    protected:
188        unsigned int _expectNumFacets;
189    };
190
191    class CreateStlVisitor : public osg::NodeVisitor
192    {
193    public:
194        CreateStlVisitor(std::string const & fout, const osgDB::ReaderWriter::Options* options = 0):
195            osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN),
196            counter(0),
197            m_options(options),
198            m_dontSaveNormals(false)
199        {
200            if (options && (options->getOptionString() == "separateFiles"))
201            {
202                OSG_INFO << "ReaderWriterSTL::writeNode: Files are written separately" << std::endl;
203                m_fout_ext = osgDB::getLowerCaseFileExtension(fout);
204                m_fout = fout.substr(0, fout.rfind(m_fout_ext) - 1);
205            }
206            else
207            {
208                m_fout = fout;
209                m_f = new osgDB::ofstream(m_fout.c_str());
210            }
211
212            if (options && (options->getOptionString() == "dontSaveNormals"))
213            {
214                OSG_INFO << "ReaderWriterSTL::writeNode: Not saving normals" << std::endl;
215                m_dontSaveNormals = true;
216            }
217        }
218
219        std::string i2s(int i)
220        {
221            char buf[16];  // -2^31 == -2147483648 needs 11 chars + \0  -> 12 (+4 for security ;-)
222            sprintf(buf, "%d", i);
223            return buf;
224        }
225
226        virtual void apply(osg::Geode& node)
227        {
228            osg::Matrix mat = osg::computeLocalToWorld(getNodePath());
229
230            if (m_options && (m_options->getOptionString() == "separateFiles"))
231            {
232                std::string sepFile = m_fout + i2s(counter) + "." + m_fout_ext;
233                m_f = new osgDB::ofstream(sepFile.c_str());
234            }
235
236            if (node.getName().empty())
237                *m_f << "solid " << counter << std::endl;
238            else
239                *m_f << "solid " << node.getName() << std::endl;
240
241            for (unsigned int i = 0; i < node.getNumDrawables(); ++i)
242            {
243                osg::TriangleFunctor<PushPoints> tf;
244                tf.m_stream = m_f;
245                tf.m_mat = mat;
246                tf.m_dontSaveNormals = m_dontSaveNormals;
247                node.getDrawable(i)->accept(tf);
248            }
249
250            if (node.getName().empty())
251                *m_f << "endsolid " << counter << std::endl;
252            else
253                *m_f << "endsolid " << node.getName() << std::endl;
254
255            if (m_options && (m_options->getOptionString() == "separateFiles"))
256            {
257                m_f->close();
258                delete m_f;
259            }
260
261            ++counter;
262            traverse(node);
263        }
264
265        ~CreateStlVisitor()
266        {
267            if (m_options && (m_options->getOptionString() == "separateFiles"))
268            {
269                OSG_INFO << "ReaderWriterSTL::writeNode: " << counter - 1 << " files were written" << std::endl;
270            }
271            else
272            {
273                m_f->close();
274                delete m_f;
275            }
276        }
277
278        const std::string& getErrorString() const { return m_ErrorString; }
279
280    private:
281        int counter;
282        std::ofstream* m_f;
283        std::string m_fout;
284        std::string m_fout_ext;
285        osgDB::ReaderWriter::Options const * m_options;
286        std::string m_ErrorString;
287        bool m_dontSaveNormals;
288
289        struct PushPoints
290        {
291            std::ofstream* m_stream;
292            osg::Matrix m_mat;
293            bool m_dontSaveNormals;
294
295            inline void operator () (const osg::Vec3& _v1, const osg::Vec3& _v2, const osg::Vec3& _v3, bool treatVertexDataAsTemporary)
296            {
297                osg::Vec3 v1 = _v1 * m_mat;
298                osg::Vec3 v2 = _v2 * m_mat;
299                osg::Vec3 v3 = _v3 * m_mat;
300                osg::Vec3 vV1V2 = v2 - v1;
301                osg::Vec3 vV1V3 = v3 - v1;
302                osg::Vec3 vNormal = vV1V2.operator ^(vV1V3);
303                if (m_dontSaveNormals)
304                    *m_stream << "facet normal 0 0 0" << std::endl;
305                else
306                    *m_stream << "facet normal " << vNormal[0] << " " << vNormal[1] << " " << vNormal[2] << std::endl;
307                *m_stream << "outer loop" << std::endl;
308                *m_stream << "vertex " << v1[0] << " " << v1[1] << " " << v1[2] << std::endl;
309                *m_stream << "vertex " << v2[0] << " " << v2[1] << " " << v2[2] << std::endl;
310                *m_stream << "vertex " << v3[0] << " " << v3[1] << " " << v3[2] << std::endl;
311                *m_stream << "endloop" << std::endl;
312                *m_stream << "endfacet" << std::endl;
313            }
314        };
315    };
316};
317
318// Register with Registry to instantiate the above reader/writer.
319REGISTER_OSGPLUGIN(stl, ReaderWriterSTL)
320
321struct StlHeader
322{
323    char text[80];
324    unsigned int numFacets;
325};
326const unsigned int sizeof_StlHeader = 84;
327
328struct StlVector
329{
330    float x, y, z;
331};
332struct StlFacet
333{
334    StlVector normal;
335    StlVector vertex[3];
336    unsigned short color;
337};
338const unsigned int sizeof_StlFacet = 50;
339
340const unsigned short StlHasColor = 0x8000;
341const unsigned short StlColorSize = 0x1f;        // 5 bit
342const float StlColorDepth = float(StlColorSize); // 2^5 - 1
343
344osgDB::ReaderWriter::ReadResult ReaderWriterSTL::readNode(const std::string& file, const osgDB::ReaderWriter::Options* options) const
345{
346    std::string ext = osgDB::getLowerCaseFileExtension(file);
347    if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
348
349    std::string fileName = osgDB::findDataFile(file, options);
350    if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;
351
352    if (sizeof(unsigned int) != 4)
353    {
354        OSG_NOTICE<<"Waring: STL reading not supported as unsigned int is not 4 bytes on this system."<<std::endl;
355        return ReadResult::ERROR_IN_READING_FILE;
356    }
357
358    OSG_INFO << "ReaderWriterSTL::readNode(" << fileName.c_str() << ")" << std::endl;
359
360    // determine ASCII vs. binary mode
361    FILE* fp = osgDB::fopen(fileName.c_str(), "rb");
362    if (!fp)
363    {
364        return ReadResult::FILE_NOT_FOUND;
365    }
366
367    // assumes "unsigned int" is 4 bytes...
368    StlHeader header;
369    if (fread((void*) &header, sizeof(header), 1, fp) != 1)
370    {
371        fclose(fp);
372        return ReadResult::ERROR_IN_READING_FILE;
373    }
374    bool isBinary = false;
375
376    // calculate expected file length from number of facets
377    unsigned int expectFacets = header.numFacets;
378    if (osg::getCpuByteOrder() == osg::BigEndian)
379    {
380        osg::swapBytes4((char*) &expectFacets);
381    }
382    off_t expectLen = sizeof_StlHeader + expectFacets * sizeof_StlFacet;
383
384    struct stat stb;
385    if (fstat(fileno(fp), &stb) < 0)
386    {
387        OSG_FATAL << "ReaderWriterSTL::readNode: Unable to stat '" << fileName << "'" << std::endl;
388        fclose(fp);
389        return ReadResult::ERROR_IN_READING_FILE;
390    }
391
392    if (stb.st_size == expectLen)
393    {
394        isBinary = true;
395    }
396    else if (strstr(header.text, "solid") != 0)
397    {
398        isBinary = false;
399    }
400    else
401    {
402        OSG_FATAL << "ReaderWriterSTL::readNode(" << fileName.c_str() << ") unable to determine file format" << std::endl;
403        fclose(fp);
404        return ReadResult::ERROR_IN_READING_FILE;
405    }
406
407    if (!isBinary)
408    {
409        fclose(fp);
410        fp = osgDB::fopen(fileName.c_str(), "r");
411    }
412
413    osg::ref_ptr<osg::Group> group = new osg::Group;
414
415    // read
416    rewind(fp);
417
418    ReaderObject *readerObject;
419
420    if (isBinary)
421        readerObject = new BinaryReaderObject(expectFacets);
422    else
423        readerObject = new AsciiReaderObject();
424
425    std::auto_ptr<ReaderObject> readerPtr(readerObject);
426
427    while (1)
428    {
429        ReaderObject::ReadResult result;
430
431        if ((result = readerPtr->read(fp)) == ReaderObject::ReadError)
432        {
433            fclose(fp);
434            return ReadResult::FILE_NOT_HANDLED;
435        }
436
437        if (!readerPtr->isEmpty())
438        {
439            osg::ref_ptr<osg::Geometry> geom = readerPtr->asGeometry();
440            osg::ref_ptr<osg::Geode> geode = new osg::Geode;
441            geode->addDrawable(geom.get());
442            geode->setName(readerPtr->getName());
443            group->addChild(geode.get());
444        }
445
446        if (result == ReaderObject::ReadEOF)
447            break;
448    }
449
450    fclose(fp);
451
452    if (options && (options->getOptionString() == "smooth"))
453    {
454        osgUtil::SmoothingVisitor smoother;
455        group->accept(smoother);
456    }
457
458    return group.get();
459}
460
461/**********************************************************************
462 *
463 * Private
464 *
465 **********************************************************************/
466
467ReaderWriterSTL::ReaderObject::ReadResult ReaderWriterSTL::AsciiReaderObject::read(FILE* fp)
468{
469    unsigned int vertexCount = 0;
470    unsigned int facetIndex[] = { 0, 0, 0 };
471    unsigned int vertexIndex = 0;
472    unsigned int normalIndex = 0;
473
474    const int MaxLineSize = 256;
475    char buf[MaxLineSize];
476    char sx[MaxLineSize], sy[MaxLineSize], sz[MaxLineSize];
477
478    if (!isEmpty())
479    {
480        clear();
481    }
482
483    while (fgets(buf, sizeof(buf), fp))
484    {
485        // strip '\n' or '\r\n' and trailing whitespace
486        unsigned int len = strlen(buf) - 1;
487
488        while (len && (buf[len] == '\n' || buf[len] == '\r' || isspace(buf[len])))
489        {
490            buf[len--] = '\0';
491        }
492
493        if (len == 0 || buf[0] == '\0')
494        {
495            continue;
496        }
497
498        // strip leading whitespace
499        char* bp = buf;
500        while (isspace(*bp))
501        {
502            ++bp;
503        }
504
505        if (strncmp(bp, "vertex", 6) == 0)
506        {
507            if (sscanf(bp + 6, "%s %s %s", sx, sy, sz) == 3)
508            {
509                if (!_vertex.valid())
510                    _vertex = new osg::Vec3Array;
511
512                float vx = osg::asciiToFloat(sx);
513                float vy = osg::asciiToFloat(sy);
514                float vz = osg::asciiToFloat(sz);
515
516                vertexIndex = _vertex->size();
517                if (vertexCount < 3)
518                {
519                    _vertex->push_back(osg::Vec3(vx, vy, vz));
520                    facetIndex[vertexCount++] = vertexIndex;
521                }
522                else
523                {
524                    /*
525                     * There are some invalid ASCII files around (at least one ;-)
526                     * that have more than three vertices per facet - add an
527                     * additional triangle.
528                     */
529                    _normal->push_back((*_normal)[normalIndex]);
530                    _vertex->push_back((*_vertex)[facetIndex[0]]);
531                    _vertex->push_back((*_vertex)[facetIndex[2]]);
532                    _vertex->push_back(osg::Vec3(vx, vy, vz));
533                    facetIndex[1] = facetIndex[2];
534                    facetIndex[2] = vertexIndex;
535                    _numFacets++;
536                }
537            }
538        }
539        else if (strncmp(bp, "facet", 5) == 0)
540        {
541            if (sscanf(bp + 5, "%*s %s %s %s", sx, sy, sz) == 3)
542            {
543                float nx = osg::asciiToFloat(sx);
544                float ny = osg::asciiToFloat(sy);
545                float nz = osg::asciiToFloat(sz);
546
547                if (!_normal.valid())
548                    _normal = new osg::Vec3Array;
549
550                osg::Vec3 normal(nx, ny, nz);
551                normal.normalize();
552
553                normalIndex = _normal->size();
554                _normal->push_back(normal);
555
556                _numFacets++;
557                vertexCount = 0;
558            }
559        }
560        else if (strncmp(bp, "solid", 5) == 0)
561        {
562            OSG_INFO << "STL loader parsing '" << bp + 6 << "'" << std::endl;
563            _solidName = bp + 6;
564        }
565        else if (strncmp(bp, "endsolid", 8) == 0)
566        {
567            OSG_INFO << "STL loader done parsing '" << _solidName << "'" << std::endl;
568            return ReadSuccess;
569        }
570    }
571
572    return ReadEOF;
573}
574
575ReaderWriterSTL::ReaderObject::ReadResult ReaderWriterSTL::BinaryReaderObject::read(FILE* fp)
576{
577    if (isEmpty())
578    {
579        clear();
580    }
581
582    _numFacets = _expectNumFacets;
583
584    // seek to beginning of facets
585    ::fseek(fp, sizeof_StlHeader, SEEK_SET);
586
587    StlFacet facet;
588    for (unsigned int i = 0; i < _expectNumFacets; ++i)
589    {
590        if (::fread((void*) &facet, sizeof_StlFacet, 1, fp) != 1)
591        {
592            OSG_FATAL << "ReaderWriterSTL::readStlBinary: Failed to read facet " << i << std::endl;
593            return ReadError;
594        }
595
596        // vertices
597        if (!_vertex.valid())
598            _vertex = new osg::Vec3Array;
599
600        osg::Vec3 v0(facet.vertex[0].x, facet.vertex[0].y, facet.vertex[0].z);
601        osg::Vec3 v1(facet.vertex[1].x, facet.vertex[1].y, facet.vertex[1].z);
602        osg::Vec3 v2(facet.vertex[2].x, facet.vertex[2].y, facet.vertex[2].z);
603        _vertex->push_back(v0);
604        _vertex->push_back(v1);
605        _vertex->push_back(v2);
606
607        // per-facet normal
608        osg::Vec3 normal;
609        if (_generateNormal)
610        {
611            osg::Vec3 d01 = v1 - v0;
612            osg::Vec3 d02 = v2 - v0;
613            normal = d01 ^ d02;
614            normal.normalize();
615        }
616        else
617        {
618            normal.set(facet.normal.x, facet.normal.y, facet.normal.z);
619        }
620
621        if (!_normal.valid())
622            _normal = new osg::Vec3Array;
623        _normal->push_back(normal);
624
625        /*
626         * color extension
627         * RGB555 with most-significat bit indicating if color is present
628         */
629        if (facet.color & StlHasColor)
630        {
631            if (!_color.valid())
632            {
633                _color = new osg::Vec4Array;
634            }
635            float r = ((facet.color >> 10) & StlColorSize) / StlColorDepth;
636            float g = ((facet.color >> 5) & StlColorSize) / StlColorDepth;
637            float b = (facet.color & StlColorSize) / StlColorDepth;
638            _color->push_back(osg::Vec4(r, g, b, 1.0f));
639        }
640    }
641
642    return ReadEOF;
643}
644
645osgDB::ReaderWriter::WriteResult ReaderWriterSTL::writeNode(const osg::Node& node, const std::string& fileName, const Options* opts) const
646{
647    std::string ext = osgDB::getLowerCaseFileExtension(fileName);
648    if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED;
649
650    if (ext != "stl")
651    {
652        OSG_FATAL << "ReaderWriterSTL::writeNode: Only STL ASCII files supported" << std::endl;
653        return WriteResult::FILE_NOT_HANDLED;
654    }
655
656    CreateStlVisitor createStlVisitor(fileName, opts);
657    const_cast<osg::Node&>(node).accept(createStlVisitor);
658
659    if (createStlVisitor.getErrorString().empty())
660    {
661        return WriteResult::FILE_SAVED;
662    }
663    else
664    {
665        OSG_FATAL << "Error: " << createStlVisitor.getErrorString() << std::endl;
666        return WriteResult::ERROR_IN_WRITING_FILE;
667    }
668}
669
670/* vim: set ts=4 sw=4 expandtab: */
Note: See TracBrowser for help on using the browser.