diff --git a/src/osgPlugins/dxf/CMakeLists.txt b/src/osgPlugins/dxf/CMakeLists.txt index b41db5ce9..860e78cb5 100644 --- a/src/osgPlugins/dxf/CMakeLists.txt +++ b/src/osgPlugins/dxf/CMakeLists.txt @@ -1,5 +1,6 @@ SET(TARGET_SRC ReaderWriterDXF.cpp + DXFWriterNodeVisitor.cpp aci.cpp dxfBlock.cpp dxfEntity.cpp @@ -22,6 +23,7 @@ SET(TARGET_H dxfSectionBase.h dxfTable.h scene.h + DXFWriterNodeVisitor.h ) SET(TARGET_ADDED_LIBRARIES osgText ) diff --git a/src/osgPlugins/dxf/DXFWriterNodeVisitor.cpp b/src/osgPlugins/dxf/DXFWriterNodeVisitor.cpp new file mode 100644 index 000000000..fce2de4d0 --- /dev/null +++ b/src/osgPlugins/dxf/DXFWriterNodeVisitor.cpp @@ -0,0 +1,592 @@ +// -*-c++-*- + +/* + * Autcad DXF writer for Open Scene Graph + * + * Copyright (C) 2009 Martin Beckett mgb@mgbeckett.com + * + * Based on OBJ writer plugin by Ulrich Hertlein + * + * The Open Scene Graph (OSG) is a cross platform C++/OpenGL library for + * real-time rendering of large 3D photo-realistic models. + * The OSG homepage is http://www.openscenegraph.org/ + */ + + +#include +#include + +#include "DXFWriterNodeVisitor.h" + +// ROBERT - is there any need for a value visitor like this or is it just overkill? + +/** writes all values of an array out to a stream, applies a matrix beforehand if necessary */ + +// I think this is a bit over the top for just a simple vertex array - but if anyone knwos different? +/* +class ValueVisitor : public osg::ValueVisitor { + public: + ValueVisitor(std::ostream& fout, const Layer &layer,const osg::Matrix& m = osg::Matrix::identity()) : + osg::ValueVisitor(), + _fout(fout), + _layer(layer), + _m(m) + { + //_applyMatrix = (_m != osg::Matrix::identity()); + } + + virtual void apply(osg::Vec3 & inv) + { + osg::Vec3 point(inv) ; + point = point * _m; + _fout << "0 \nVERTEX\n 8\n"<<_layer._name<<"\n"; + if ( _layer._color ) { + _fout << "62\n"<<_layer._color<<"\n"; + } + + _fout <<" 10\n"<getVertexArray())->at(i) * _m; + _fout <(mode, count, indices); + } + virtual void drawElements(GLenum mode,GLsizei count,const GLushort* indices) + { + drawElementsImplementation(mode, count, indices); + } + + virtual void drawElements(GLenum mode,GLsizei count,const GLuint* indices) + { + drawElementsImplementation(mode, count, indices); + } + + protected: + + templatevoid drawElementsImplementation(GLenum mode, GLsizei count, const T* indices) + { + if (indices==0 || count==0) return; + + typedef const T* IndexPointer; + + switch(mode) + { + case(GL_TRIANGLES): + { + IndexPointer ilast = &indices[count]; + for(IndexPointer iptr=indices;iptr _indexCache; + osg::Geometry* _geo; + + Layer _layer; + AcadColor _acad; // needed to lookup new colors + osg::Matrix _m; +}; + + +void PrimitiveIndexWriter::drawArrays(GLenum mode,GLint first,GLsizei count) +{ + switch(mode) + { + case(GL_TRIANGLES): + { + unsigned int pos=first; + for(GLsizei i=2;i::iterator itr=_layers.begin();itr!=_layers.end();itr++) { + if (itr->_name == layerName ) { + std::stringstream ss; + ss << defaultvalue<< "_" << _layers.size(); + layerName = ss.str(); + break; + } + } + + return layerName; + +} + +// Now deal with VertexArray directly +//void DXFWriterNodeVisitor::processArray(osg::Array* array, const Layer &layer,const osg::Matrix& m) +//{ +// if (array == NULL) +// return; +// +// ValueVisitor vv(_fout, layer,m); +// for(unsigned int i = 0; i < array->getNumElements(); ++i) { +// array->accept(i, vv); +// } +// +// osg::notify(osg::DEBUG_INFO) << "processArray "<getNumElements() << " elements written" << std::endl; +// +//} + +void DXFWriterNodeVisitor::processStateSet(osg::StateSet* ss) +{ + // anything to do if no material/texture? + // could detect polygon mode and output in that form? +} + +void DXFWriterNodeVisitor::processGeometry(osg::Geometry* geo, osg::Matrix& m) +{ + + + // We only want to create a new layer for geometry with something to draw + if (geo->getVertexArray() && geo->getVertexArray()->getNumElements() ) { + + processStateSet(_currentStateSet.get()); + + if ( _firstPass ) { + // Must have unique layer names + _layer._name = getLayerName( geo->getName().empty() ? geo->getParent(0)->getName() : geo->getName() ); + osg::notify(osg::DEBUG_INFO) << "adding Layer " << _layer._name << std::endl; + + // if single colour include in header + if ( osg::Geometry::BIND_OVERALL == geo->getColorBinding() ) { + _layer._color = _acadColor.findColor(getNodeRGB(geo)); // per layer color + } else if ( osg::Geometry::BIND_OFF== geo->getColorBinding() ) { + _layer._color = 0xff; // use white - or can we easily lookup in texture? + } else { + _layer._color = 0; // per point color + } + _layers.push_back(_layer); + + } else { + _layer = _layers[_count++]; + osg::notify(osg::DEBUG_INFO) << "writing Layer " << _layer._name << std::endl; + if ( geo->getNumPrimitiveSets() ) { + for(unsigned int i = 0; i < geo->getNumPrimitiveSets(); ++i) + { + osg::PrimitiveSet* ps = geo->getPrimitiveSet(i); + PrimitiveIndexWriter pif(_fout, geo,_layer,_acadColor,m); + ps->accept(pif); + } + } else { + // Is array visitor necessary for only dealing with vertex arrays? + //processArray(geo->getVertexArray(), _layer,m); + if ( geo->getVertexArray() ) { + osg::Vec3Array* data=static_cast(geo->getVertexArray()); + for (unsigned int ii=0;iigetNumElements();ii++) + { + osg::Vec3 point = data->at(ii) * m; + _fout << "0 \nVERTEX\n 8\n"<<_layer._name<<"\n"; + if ( _layer._color ) { + _fout << "62\n"<<_layer._color<<"\n"; + } else { + _fout << "62\n"<<_acadColor.findColor(getNodeRGB(geo,ii))<<"\n"; + } + _fout<<" 10\n"<asGeometry(); + if ( g != NULL ) + { + pushStateSet(g->getStateSet()); + processGeometry(g,m); + popStateSet(g->getStateSet()); + } + } + + + popStateSet(node.getStateSet()); +} + + +bool DXFWriterNodeVisitor::writeHeader(const osg::BoundingSphere &bound) +{ + if ( _layers.empty() ) { + return false; + } + _fout << "999\n written by OpenSceneGraph" << std::endl; + + _fout << "0\nSECTION\n2\nHEADER\n"; + _fout << "9\n$ACADVER\n1\nAC1006\n"; // specify minimum autocad version AC1006=R10 + + _fout << "9\n$EXTMIN\n10\n"<::iterator itr=_layers.begin();itr!=_layers.end();itr++) { + if ( itr->_color ) { + _fout<<"0\nLAYER\n2\n"<_name<<"\n70\n0\n62\n"<_color<<"\n6\nContinuous\n"; // color by layer + } else { + _fout<<"0\nLAYER\n2\n"<_name<<"\n70\n0\n62\n255\n6\nContinuous\n"; // most apps won't read 24bit color without a color value in header + } + } + + _fout << "0\nENDTAB\n0\nENDSEC\n"; + + _fout << "0\nSECTION\n2\nENTITIES\n"; + _firstPass=false; + _count=0; + + return true; +} + +void DXFWriterNodeVisitor::writeFooter() +{ + _fout << "0\nENDSEC\n0\nEOF"; + _fout << std::endl; +} + diff --git a/src/osgPlugins/dxf/DXFWriterNodeVisitor.h b/src/osgPlugins/dxf/DXFWriterNodeVisitor.h new file mode 100644 index 000000000..4768ab3d4 --- /dev/null +++ b/src/osgPlugins/dxf/DXFWriterNodeVisitor.h @@ -0,0 +1,282 @@ +// -*-c++-*- + +/* + * Wavefront DXF loader for Open Scene Graph + * + * Copyright (C) 2001 Ulrich Hertlein + * + * Modified by Robert Osfield to support per Drawable coord, normal and + * texture coord arrays, bug fixes, and support for texture mapping. + * + * Writing support added 2007 by Stephan Huber, http://digitalmind.de, + * some ideas taken from the dae-plugin + * + * The Open Scene Graph (OSG) is a cross platform C++/OpenGL library for + * real-time rendering of large 3D photo-realistic models. + * The OSG homepage is http://www.openscenegraph.org/ + */ + + #ifndef DXF_WRITER_NODE_VISITOR_HEADER__ + #define DXF_WRITER_NODE_VISITOR_HEADER__ + + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +#include +#include +#include +struct Layer +{ +public: + Layer(const std::string name="",unsigned int color=7) : _name(name),_color(color) { } + std::string _name; + unsigned int _color; +}; + +// reuse aci class for autocad colors, see http://bitsy.sub-atomic.com/~moses/acadcolors.html for samples +#include "aci.h" +class AcadColor +{ +public: + AcadColor() + { + int index=10; + for (int ii=10*3;ii<256*3; ) { + // find RGB for each Autocad index colour + unsigned int red = (int)floor(aci::table[ii++]*255.0f); + unsigned int green = (int)floor(aci::table[ii++]*255.0f); + unsigned int blue = (int)floor(aci::table[ii++]*255.0f); + unsigned int rgb = (red<<16) + (green<<8) + blue; + _indexColors[rgb]=index++; + } + // + } + + // returns Autocad index color for supplied RGB. + // if no exact match is found returns nearest color based on hue + // also adds match to cache for future lookups. + int findColor(unsigned int rgb) + { + int aci = 255; + itr = _indexColors.find(rgb); + if (itr != _indexColors.end() ) { + aci = itr->second; + } else { + // not found - match based on hue + aci = nearestColor(rgb); + + // add matching colour to list to cache + _indexColors[rgb]=aci; + } + return aci; + } + +protected: + // returns hue as an angle in range 0-360, saturation and value as 0-1 + void hsv(unsigned int rgb,float &hue,float &sat,float &value) + { + int red = rgb>>16; + int green = (0x0000ff00&rgb)>>8; + int blue = 0x000000ff&rgb; + int H=std::max(std::max(red,green),blue); + int L=std::min(std::min(red,green),blue); + + value = (float)H/255.0f; // note hsv and hsl define v differently! + sat=(float)(H-L)/(float)H; + + if (H==L) { + hue=0.0; + }else if (H==red) { + hue=360.0 + (60.0 * (float)(green-blue)/(float)(H-L)); + if ( hue > 360 ) { hue-=360; } + } else if (H==green) { + hue=120.0 + (60.0 * (float)(blue-red)/(float)(H-L)); + } else if (H==blue) { + hue=240.0 + (60.0 * (float)(red-green)/(float)(H-L)); + } else { + hue = 0.0; + } + } + + int nearestColor(unsigned int rgb) + { + //- match based on hue + float h; + float s; + float v; + hsv(rgb,h,s,v); + + // aci index format is + // last digit odd = 50% sat, even=100% + // last digit 0,1 = 100% value, 2,3=80%, 4,5=60% 6,7=50%, 8,9=30% + // first two sigits are hue angle /1.5 but count starts at 10, first 9 values are dummy named colours + int aci=10 + (int)(h/1.5); + aci -= (aci%10); // ensure last digit is zero + + if ( v < 0.3 ) { + aci += 9; + } else if ( v < 0.5 ) { + aci += 6; + } else if ( v < 0.6 ) { + aci += 4; + } else if ( v < 0.8 ) { + aci += 2; + } else { + // last digit=0; + } + + if ( s<0.5 ) { + aci += 1; + } + + return aci; + } + + + +protected: + + std::map _indexColors; // maps RGB to autocad index colour + std::map _hueColors; // maps hue angle to autocad index colour + + typedef std::pair ColorPair; + std::map::iterator itr; +}; + +class DXFWriterNodeVisitor: public osg::NodeVisitor { + + public: + DXFWriterNodeVisitor(std::ostream& fout) : + osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN), + _fout(fout), + _currentStateSet(new osg::StateSet()), + _firstPass(true) + { + + + } + + static unsigned int getNodeRGB(osg::Geometry *geo,unsigned int index=0) + { + osg::Vec4Array* data=static_cast(geo->getColorArray()); + if ( data && indexsize() ) { + return (data->at(index).asABGR())>>8; + } + return 0; + } + + + bool writeHeader(const osg::BoundingSphere &bound);// call after first pass to trigger draw pass + void writeFooter(); + + void buildColorMap(); + + virtual void apply(osg::Geode &node); + + virtual void apply(osg::Group &node) + { + osg::NodeVisitor::traverse( node ); + + } + + void traverse (osg::Node &node) + { + pushStateSet(node.getStateSet()); + + osg::NodeVisitor::traverse( node ); + + popStateSet(node.getStateSet()); + } + + void pushStateSet(osg::StateSet* ss) + { + if (NULL!=ss) { + // Save our current stateset + _stateSetStack.push(_currentStateSet.get()); + + // merge with node stateset + _currentStateSet = static_cast(_currentStateSet->clone(osg::CopyOp::SHALLOW_COPY)); + _currentStateSet->merge(*ss); + } + } + + + void popStateSet(osg::StateSet* ss) + { + if (NULL!=ss) { + // restore the previous stateset + _currentStateSet = _stateSetStack.top(); + _stateSetStack.pop(); + } + } + + int getNodeAcadColor(osg::Geometry *geo,int index=0) { return 0;} + + protected: + struct CompareStateSet + { + bool operator()(const osg::ref_ptr& ss1, const osg::ref_ptr& ss2) const + { + return ss1->compare(*ss2, true) < 0; + } + }; + + + private: + + DXFWriterNodeVisitor& operator = (const DXFWriterNodeVisitor&) { return *this; } + + // first pass get layer names and draw types + void makeGeometryLayer(osg::Geometry* geo); + + // second pass - output data + void processGeometry(osg::Geometry* geo, osg::Matrix& m); + + + void processArray(osg::Array* array, const Layer& layer,const osg::Matrix& m = osg::Matrix::identity()); + + void processStateSet(osg::StateSet* stateset); + + std::string getLayerName(const std::string& defaultValue = ""); + + typedef std::stack > StateSetStack; + + + + + std::ostream& _fout; + std::list _nameStack; + StateSetStack _stateSetStack; + osg::ref_ptr _currentStateSet; + + unsigned int _count; + std::vector _layers; + bool _firstPass; + Layer _layer; + + AcadColor _acadColor; + + +}; + +#endif diff --git a/src/osgPlugins/dxf/ReaderWriterDXF.cpp b/src/osgPlugins/dxf/ReaderWriterDXF.cpp index d7c5801dc..098de8d49 100644 --- a/src/osgPlugins/dxf/ReaderWriterDXF.cpp +++ b/src/osgPlugins/dxf/ReaderWriterDXF.cpp @@ -23,6 +23,7 @@ #include #include "dxfFile.h" +#include "DXFWriterNodeVisitor.h" using namespace osg; using namespace osgDB; @@ -37,8 +38,67 @@ public: supportsExtension("dxf","Autodesk DXF format"); } - virtual const char* className() { return "Autodesk DXF Reader"; } + virtual const char* className() { return "Autodesk DXF Reader/Writer"; } virtual ReadResult readNode(const std::string& fileName, const osgDB::ReaderWriter::Options*) const; + + + virtual WriteResult writeObject(const osg::Object& obj,const std::string& fileName,const Options* options=NULL) const + { + const osg::Node* node = dynamic_cast(&obj); + if (node) + return writeNode(*node, fileName, options); + else + return WriteResult(WriteResult::FILE_NOT_HANDLED); + } + + + virtual WriteResult writeObject(const osg::Object& obj,std::ostream& fout,const Options* options=NULL) const + { + const osg::Node* node = dynamic_cast(&obj); + if (node) + return writeNode(*node, fout, options); + else + return WriteResult(WriteResult::FILE_NOT_HANDLED); + } + + virtual WriteResult writeNode(const osg::Node& node,std::ostream& fout,const Options* =NULL) const + { + + + DXFWriterNodeVisitor nv(fout); + + (const_cast(&node))->accept(nv); // first pass is to get all node names and types -> layers + + if ( nv.writeHeader(node.getBound()) ) { + (const_cast(&node))->accept(nv); // second pass outputs data + nv.writeFooter(); + } + + return WriteResult(WriteResult::FILE_SAVED); + } + + virtual WriteResult writeNode(const osg::Node& node,const std::string& fileName,const Options* options =NULL) const + { + if (!acceptsExtension(osgDB::getFileExtension(fileName))) + return WriteResult(WriteResult::FILE_NOT_HANDLED); + + osgDB::ofstream f(fileName.c_str()); + + if (!f.is_open() ) { + return WriteResult(WriteResult::ERROR_IN_WRITING_FILE); + } + DXFWriterNodeVisitor nv(f); + + (const_cast(&node))->accept(nv); // first pass is to get all node names and types -> layers + + if ( nv.writeHeader(node.getBound()) ) { + (const_cast(&node))->accept(nv); // second pass outputs data + nv.writeFooter(); + } + + return WriteResult(WriteResult::FILE_SAVED); + } + protected: };