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

Revision 13133, 18.8 kB (checked in by robert, 6 minutes ago)

Added numTextureUnits parameter to the osg::State::resetVertexAttributeAlias(bool, unit) method, and set the default to 8.

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