diff --git a/Make/makedirdefs b/Make/makedirdefs index 3a801e57f..bad1af3ce 100644 --- a/Make/makedirdefs +++ b/Make/makedirdefs @@ -61,6 +61,7 @@ PLUGIN_DIRS = \ ive\ rot\ scale\ + stl\ trans\ diff --git a/VisualStudio/VisualStudio.dsw b/VisualStudio/VisualStudio.dsw index b85438e07..c293d29d9 100644 --- a/VisualStudio/VisualStudio.dsw +++ b/VisualStudio/VisualStudio.dsw @@ -2358,6 +2358,27 @@ Package=<4> ############################################################################### +Project: "osgPlugin stl"=.\osgPlugins\stl\stl.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name Core osg + End Project Dependency + Begin Project Dependency + Project_Dep_Name Core osgDB + End Project Dependency + Begin Project Dependency + Project_Dep_Name Core osgUtil + End Project Dependency +}}} + +############################################################################### + Project: "osgPlugin osga"=.\osgPlugins\osga\osga.dsp - Package Owner=<4> Package=<5> diff --git a/VisualStudio/osgPlugins/stl/stl.dsp b/VisualStudio/osgPlugins/stl/stl.dsp new file mode 100644 index 000000000..2adc99a6a --- /dev/null +++ b/VisualStudio/osgPlugins/stl/stl.dsp @@ -0,0 +1,111 @@ +# Microsoft Developer Studio Project File - Name="osgPlugin stl" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=osgPlugin stl - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "stl.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "stl.mak" CFG="osgPlugin stl - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "osgPlugin stl - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "osgPlugin stl - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "osgPlugin stl - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "../../../lib" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +F90=df.exe +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GR /GX /O2 /I "../../../include" /I "../../../../OpenThreads/include" /I "../../../../Producer/include" /I "../../../../3rdParty/include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "NDEBUG" +# ADD RSC /l 0x809 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 OpenThreadsWin32.lib /nologo /dll /pdb:none /machine:I386 /nodefaultlib:"LIBC" /out:"../../../bin/osgdb_stl.dll" /libpath:"../../../lib" /libpath:"../../../../OpenThreads/lib/win32" /libpath:"../../../../Producer/lib" /libpath:"../../../../3rdParty/lib" +# SUBTRACT LINK32 /nodefaultlib + +!ELSEIF "$(CFG)" == "osgPlugin stl - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "../../../lib" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +F90=df.exe +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /vmg /vd0 /GR /GX /Zi /Od /I "../../../include" /I "../../../../OpenThreads/include" /I "../../../../Producer/include" /I "../../../../3rdParty/include" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "WIN32" /D "_DEBUG" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x809 /d "_DEBUG" +# ADD RSC /l 0x809 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 OpenThreadsWin32d.lib /nologo /dll /debug /machine:I386 /nodefaultlib:"LIBC" /out:"../../../bin/osgdb_stld.dll" /pdbtype:sept /libpath:"../../../lib" /libpath:"../../../../OpenThreads/lib/win32" /libpath:"../../../../Producer/lib" /libpath:"../../../../3rdParty/lib" +# SUBTRACT LINK32 /nodefaultlib + +!ENDIF + +# Begin Target + +# Name "osgPlugin stl - Win32 Release" +# Name "osgPlugin stl - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\..\src\osgPlugins\stl\ReaderWriterSTL.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/src/osgPlugins/stl/GNUmakefile b/src/osgPlugins/stl/GNUmakefile new file mode 100644 index 000000000..0b2ce4530 --- /dev/null +++ b/src/osgPlugins/stl/GNUmakefile @@ -0,0 +1,17 @@ +# +# $Id$ +# + +TOPDIR = ../../.. +include $(TOPDIR)/Make/makedefs + +CXXFILES =\ + ReaderWriterSTL.cpp + +LIBS += $(OSG_LIBS) $(OTHER_LIBS) + +TARGET_BASENAME = stl +include $(TOPDIR)/Make/cygwin_plugin_def +PLUGIN = $(PLUGIN_PREFIX)$(TARGET_BASENAME).$(PLUGIN_EXT) + +include $(TOPDIR)/Make/makerules diff --git a/src/osgPlugins/stl/ReaderWriterSTL.cpp b/src/osgPlugins/stl/ReaderWriterSTL.cpp new file mode 100644 index 000000000..20181dd32 --- /dev/null +++ b/src/osgPlugins/stl/ReaderWriterSTL.cpp @@ -0,0 +1,330 @@ +// -*-c++-*- + +/* + * $Id$ + * + * STL importer for OpenSceneGraph. + * Copyright (c)2004 Ulrich Hertlein + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + + +/** + * STL importer for OpenSceneGraph. + */ +class ReaderWriterSTL : public osgDB::ReaderWriter +{ +public: + ReaderWriterSTL() : _generateNormal(true), + _numFacets(0) { + } + + virtual const char* className() const { + return "STL Reader/Writer"; + } + + virtual bool acceptsExtension(const std::string& extension) { + return + osgDB::equalCaseInsensitive(extension,"stl") ? true : + osgDB::equalCaseInsensitive(extension,"sta") ? true : false; + } + + virtual ReadResult readNode(const std::string& fileName, + const osgDB::ReaderWriter::Options*); + +private: + bool _generateNormal; + unsigned int _numFacets; + + osg::ref_ptr _vertex; + osg::ref_ptr _normal; + osg::ref_ptr _color; + + bool readStlAscii(FILE* fp); + bool readStlBinary(FILE* fp); +}; + + +// Register with Registry to instantiate the above reader/writer. +osgDB::RegisterReaderWriterProxy g_readerWriter_STL_Proxy; + + +/* + * STL + */ +struct StlHeader { + char text[80]; + unsigned int numFacets; +}; +const unsigned int sizeof_StlHeader = 84; + +struct StlVector { + float x,y,z; +}; +struct StlFacet { + StlVector normal; + StlVector vertex[3]; + unsigned short color; +}; +const unsigned int sizeof_StlFacet = 50; + +const unsigned short StlHasColor = 0x8000; +const unsigned short StlColorSize = 0x1f; // 5 bit +const float StlColorDepth = float(StlColorSize); // 2^5 - 1 + + +// Read node +osgDB::ReaderWriter::ReadResult ReaderWriterSTL::readNode(const std::string& file, + const osgDB::ReaderWriter::Options*) +{ + std::string ext = osgDB::getLowerCaseFileExtension(file); + if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED; + + std::string fileName = osgDB::findDataFile( file ); + if (fileName.empty()) return ReadResult::FILE_NOT_FOUND; + + osg::notify(osg::INFO) << "ReaderWriterSTL::readNode(" << fileName.c_str() << ")\n"; + + // determine ASCII vs. binary mode + FILE* fp = fopen(fileName.c_str(), "r"); + if (!fp) { + return ReadResult::FILE_NOT_HANDLED; + } + + // assumes "unsigned int" is 4 bytes... + StlHeader header; + if (fread((void*) &header, sizeof(header), 1, fp) != 1) { + fclose(fp); + return ReadResult::FILE_NOT_HANDLED; + } + bool isBinary = false; + + // calculate expected file length from number of facets + unsigned int expectFacets = header.numFacets; + if (osg::getCpuByteOrder() == osg::BigEndian) { + osg::swapBytes4((char*) &expectFacets); + } + off_t expectLen = sizeof_StlHeader + expectFacets * sizeof_StlFacet; + + struct stat stb; + if (fstat(fileno(fp), &stb) < 0) { + osg::notify(osg::FATAL) << "ReaderWriterSTL::readNode: Unable to stat '" << fileName << "'" << std::endl; + fclose(fp); + return ReadResult::FILE_NOT_HANDLED; + } + if (stb.st_size == expectLen) { + // assume binary + _numFacets = expectFacets; + isBinary = true; + } + else if (strstr(header.text, "solid") != 0) { + // assume ASCII + isBinary = false; + } + else { + osg::notify(osg::FATAL) << "ReaderWriterSTL::readNode(" << fileName.c_str() << ") unable to determine file format" << std::endl; + fclose(fp); + return ReadResult::FILE_NOT_HANDLED; + } + + // read + rewind(fp); + bool ok = (isBinary ? readStlBinary(fp) : readStlAscii(fp)); + fclose(fp); + + if (!ok) { + return ReadResult::FILE_NOT_HANDLED; + } + osg::notify(osg::NOTICE) << "### found " << _numFacets << " facets" << std::endl; + + /* + * setup geometry + */ + osg::Geometry* geom = new osg::Geometry; + geom->setVertexArray(_vertex.get()); + + geom->setNormalArray(_normal.get()); + geom->setNormalBinding(osg::Geometry::BIND_PER_PRIMITIVE); + + if (_color.valid()) { + osg::notify(osg::NOTICE) << "### with color" << std::endl; + geom->setColorArray(_color.get()); + geom->setColorBinding(osg::Geometry::BIND_PER_PRIMITIVE); + } + + geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES, 0, _numFacets*3)); + + osg::Geode* geode = new osg::Geode; + geode->addDrawable(geom); + + return geode; +} + + +/********************************************************************** + * + * Private + * + **********************************************************************/ + +bool ReaderWriterSTL::readStlAscii(FILE* fp) +{ + unsigned int vertexCount = 0; + unsigned int facetIndex[] = { 0,0,0 }; + unsigned int vertexIndex = 0; + unsigned int normalIndex = 0; + + char buf[256]; + while (fgets(buf, sizeof(buf), fp)) { + + // strip '\n' or '\r\n' and trailing whitespace + unsigned int len = strlen(buf)-1; + while (len && (buf[len] == '\n' || buf[len] == '\r' || isspace(buf[len]))) { + buf[len--] = '\0'; + } + if (len == 0 || buf[0] == '\0') { + continue; + } + + // strip leading whitespace + char* bp = buf; + while (isspace(*bp)) { + ++bp; + } + + if (strncmp(bp, "vertex", 6) == 0) { + float vx,vy,vz; + if (sscanf(bp+6, "%f %f %f", &vx,&vy,&vz) == 3) { + if (!_vertex.valid()) + _vertex = new osg::Vec3Array; + + vertexIndex = _vertex->size(); + if (vertexCount < 3) { + _vertex->push_back(osg::Vec3(vx,vy,vz)); + facetIndex[vertexCount++] = vertexIndex; + } + else { + /* + * There are some invalid ASCII files around (at least one ;-) + * that have more than three vertices per facet - add an + * additional triangle. + */ + _normal->push_back((*_normal)[normalIndex]); + _vertex->push_back((*_vertex)[facetIndex[0]]); + _vertex->push_back((*_vertex)[facetIndex[2]]); + _vertex->push_back(osg::Vec3(vx,vy,vz)); + facetIndex[1] = facetIndex[2]; + facetIndex[2] = vertexIndex; + _numFacets++; + } + } + } + else if (strncmp(bp, "facet", 5) == 0) { + float nx,ny,nz; + if (sscanf(bp+5, "%*s %f %f %f", &nx,&ny,&nz) == 3) { + + if (!_normal.valid()) + _normal = new osg::Vec3Array; + + osg::Vec3 normal(nx,ny,nz); + normal.normalize(); + + normalIndex = _normal->size(); + _normal->push_back(normal); + + _numFacets++; + vertexCount = 0; + } + } + else if (strncmp(bp, "solid", 5) == 0) { + osg::notify(osg::NOTICE) << "### parsing '" << bp + 6 << "'" << std::endl; + } + } + + return true; +} + +bool ReaderWriterSTL::readStlBinary(FILE* fp) +{ + // seek to beginning of facets + ::fseek(fp, sizeof_StlHeader, SEEK_SET); + + StlFacet facet; + for (unsigned int i = 0; i < _numFacets; ++i) { + + if (::fread((void*) &facet, sizeof_StlFacet, 1, fp) != 1) { + osg::notify(osg::FATAL) << "ReaderWriterSTL::readStlBinary: Failed to read facet " << i << std::endl; + return false; + } + + // vertices + if (!_vertex) + _vertex = new osg::Vec3Array; + osg::Vec3 v0(facet.vertex[0].x,facet.vertex[0].y,facet.vertex[0].z); + osg::Vec3 v1(facet.vertex[1].x,facet.vertex[1].y,facet.vertex[1].z); + osg::Vec3 v2(facet.vertex[2].x,facet.vertex[2].y,facet.vertex[2].z); + _vertex->push_back(v0); + _vertex->push_back(v1); + _vertex->push_back(v2); + + // per-facet normal + osg::Vec3 normal; + if (_generateNormal) { + osg::Vec3 d01 = v1 - v0; + osg::Vec3 d02 = v2 - v0; + normal = d01 ^ d02; + normal.normalize(); + } + else { + normal.set(facet.normal.x,facet.normal.y,facet.normal.z); + } + if (!_normal.valid()) + _normal = new osg::Vec3Array; + _normal->push_back(normal); + + /* + * color extension + * RGB555 with most-significat bit indicating if color is present + */ + if (facet.color & StlHasColor) { + if (!_color) { + _color = new osg::Vec4Array; + } + float r = ((facet.color >> 10) & StlColorSize) / StlColorDepth; + float g = ((facet.color >> 5) & StlColorSize) / StlColorDepth; + float b = (facet.color & StlColorSize) / StlColorDepth; + _color->push_back(osg::Vec4(r,g,b,1.0f)); + } + } + + return true; +}