Martin Beckett, "Here's a first attempt at a DXF writer plugin

At the moment it outputs DXF for whatever geometry is contained in the node it would be nice to draw the model as it is rendered (points/lines/surface)

If people could also test against other apps that need to read DXF, the format is a bit of a black art and not all importers support all features so it might need some options to tweak the output.

It has some rather clever colour lookup stuff to match real colours against the limited DXF palette. I cracked the code of the Autocad indexed colours!"
This commit is contained in:
Robert Osfield
2009-06-08 14:06:58 +00:00
parent 148bd9f232
commit e1b7de4b3d
4 changed files with 937 additions and 1 deletions

View File

@@ -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 )

View File

@@ -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 <osg/io_utils>
#include <iomanip>
#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"<<point.x()<<"\n 20\n"<<point.y()<<"\n 30\n"<<point.z()<<"\n";
}
private:
ValueVisitor& operator = (const ValueVisitor&) { return *this; }
std::ostream& _fout;
osg::Matrix _m;
const Layer _layer;
};
*/
/** writes all primitives of a primitive-set out to a stream, decomposes quads to triangles, line-strips to lines etc */
class PrimitiveIndexWriter : public osg::PrimitiveIndexFunctor {
public:
PrimitiveIndexWriter(std::ostream& fout,osg::Geometry* geo,const Layer &layer,AcadColor &acad,const osg::Matrix& m = osg::Matrix::identity()) :
osg::PrimitiveIndexFunctor(),
_fout(fout),
_geo(geo),
_layer(layer),
_acad(acad),
_m(m)
{
}
virtual void setVertexArray(unsigned int,const osg::Vec2*) {}
virtual void setVertexArray(unsigned int ,const osg::Vec3* ) {}
virtual void setVertexArray(unsigned int,const osg::Vec4* ) {}
virtual void setVertexArray(unsigned int,const osg::Vec2d*) {}
virtual void setVertexArray(unsigned int ,const osg::Vec3d* ) {}
virtual void setVertexArray(unsigned int,const osg::Vec4d* ) {}
void write(unsigned int i,int c)
{
const osg::Vec3 point = ((osg::Vec3Array *)_geo->getVertexArray())->at(i) * _m;
_fout <<c+10<<"\n "<<point.x()<<"\n"<<20+c<<"\n "<<point.y()<<"\n"<<30+c<<"\n "<<point.z()<<"\n";
}
// operator for facets - need to distinguish from triangles
void writeFace(unsigned int i1, unsigned int i2, unsigned int i3)
{
_fout << "0 \n3DFACE\n 8\n"<<_layer._name<<"\n";
if ( _layer._color ) {
_fout << "62\n"<<_layer._color<<"\n";
} else {
_fout << "62\n"<<_acad.findColor(DXFWriterNodeVisitor::getNodeRGB(_geo,i1))<<"\n";
// Acad2000 supports 24bit color but most dxf importers don't
//_fout << "420\n"<<DXFWriterNodeVisitor::getNodeRGB(_geo,i1)<<"\n";
}
write(i1,0);
write(i2,1);
write(i3,2);
write(i1,0); // yes you have to write the first point again
}
// operator for triangles
void writeTriangle(unsigned int i1, unsigned int i2, unsigned int i3)
{
_fout << "0 \nLINE\n 8\n"<<_layer._name<<"\n";
if ( _layer._color ) {
_fout << "62\n"<<_layer._color<<"\n";
} else {
_fout << "62\n"<<_acad.findColor(DXFWriterNodeVisitor::getNodeRGB(_geo,i1))<<"\n";
}
write(i1,0);
write(i2,1);
_fout << "0 \nLINE\n 8\n"<<_layer._name<<"\n";
if ( _layer._color ) {
_fout << "62\n"<<_layer._color<<"\n";
} else {
_fout << "62\n"<<_acad.findColor(DXFWriterNodeVisitor::getNodeRGB(_geo,i2))<<"\n";
}
write(i2,0);
write(i3,1);
_fout << "0 \nLINE\n 8\n"<<_layer._name<<"\n";
if ( _layer._color ) {
_fout << "62\n"<<_layer._color<<"\n";
} else {
_fout << "62\n"<<_acad.findColor(DXFWriterNodeVisitor::getNodeRGB(_geo,i3))<<"\n";
}
write(i3,0);
write(i1,1);
}
// operator for lines
void writeLine(unsigned int i1, unsigned int i2)
{
_fout << "0 \nLINE\n 8\n"<<_layer._name<<"\n";
if ( _layer._color ) {
_fout << "62\n"<<_layer._color<<"\n";
} else {
_fout << "62\n"<<_acad.findColor(DXFWriterNodeVisitor::getNodeRGB(_geo,i1))<<"\n";
}
write(i1,0);
write(i2,1);
}
// operator for points
void writePoint(unsigned int i1)
{
_fout << "0 \nPOINT\n 8\n"<<_layer._name<<"\n";
if ( _layer._color ) {
_fout << "62\n"<<_layer._color<<"\n";
} else {
_fout << "62\n"<<_acad.findColor(DXFWriterNodeVisitor::getNodeRGB(_geo,i1))<<"\n";
//_fout << "420\n"<<DXFWriterNodeVisitor::getNodeRGB(_geo,i1)<<"\n";
}
write(i1,0);
}
virtual void begin(GLenum mode)
{
_modeCache = mode;
_indexCache.clear();
}
virtual void vertex(unsigned int vert)
{
_indexCache.push_back(vert);
}
virtual void end()
{
if (!_indexCache.empty())
{
drawElements(_modeCache,_indexCache.size(),&_indexCache.front());
}
}
virtual void drawArrays(GLenum mode,GLint first,GLsizei count);
virtual void drawElements(GLenum mode,GLsizei count,const GLubyte* indices)
{
drawElementsImplementation<GLubyte>(mode, count, indices);
}
virtual void drawElements(GLenum mode,GLsizei count,const GLushort* indices)
{
drawElementsImplementation<GLushort>(mode, count, indices);
}
virtual void drawElements(GLenum mode,GLsizei count,const GLuint* indices)
{
drawElementsImplementation<GLuint>(mode, count, indices);
}
protected:
template<typename T>void 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<ilast;iptr+=3)
writeTriangle(*iptr,*(iptr+1),*(iptr+2));
break;
}
case(GL_TRIANGLE_STRIP):
{
IndexPointer iptr = indices;
for(GLsizei i=2;i<count;++i,++iptr)
{
if ((i%2)) writeTriangle(*(iptr),*(iptr+2),*(iptr+1));
else writeTriangle(*(iptr),*(iptr+1),*(iptr+2));
}
break;
}
case(GL_QUADS):
{
IndexPointer iptr = indices;
for(GLsizei i=3;i<count;i+=4,iptr+=4)
{
writeTriangle(*(iptr),*(iptr+1),*(iptr+2));
writeTriangle(*(iptr),*(iptr+2),*(iptr+3));
}
break;
}
case(GL_QUAD_STRIP):
{
IndexPointer iptr = indices;
for(GLsizei i=3;i<count;i+=2,iptr+=2)
{
writeTriangle(*(iptr),*(iptr+1),*(iptr+2));
writeTriangle(*(iptr+1),*(iptr+3),*(iptr+2));
}
break;
}
case(GL_POLYGON): // treat polygons as GL_TRIANGLE_FAN
case(GL_TRIANGLE_FAN):
{
IndexPointer iptr = indices;
unsigned int first = *iptr;
++iptr;
for(GLsizei i=2;i<count;++i,++iptr)
{
writeTriangle(first,*(iptr),*(iptr+1));
}
break;
}
case(GL_POINTS):
{
IndexPointer ilast = &indices[count];
for(IndexPointer iptr=indices;iptr<ilast;++iptr)
{
writePoint(*iptr);
}
break;
}
case(GL_LINES):
{
IndexPointer ilast = &indices[count];
for(IndexPointer iptr=indices;iptr<ilast;iptr+=2)
{
writeLine(*iptr, *(iptr+1));
}
break;
}
case(GL_LINE_STRIP):
{
IndexPointer ilast = &indices[count];
for(IndexPointer iptr=indices+1;iptr<ilast;iptr+=2)
{
writeLine(*(iptr-1), *iptr);
}
break;
}
case(GL_LINE_LOOP):
{
IndexPointer ilast = &indices[count];
for(IndexPointer iptr=indices+1;iptr<ilast;iptr+=2)
{
writeLine(*(iptr-1), *iptr);
}
writeLine(*ilast, *indices);
break;
}
default:
// uhm should never come to this point :)
break;
}
}
private:
PrimitiveIndexWriter& operator = (const PrimitiveIndexWriter&) { return *this; }
std::ostream& _fout;
GLenum _modeCache;
std::vector<GLuint> _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<count;i+=3,pos+=3)
{
writeTriangle(pos,pos+1,pos+2);
}
break;
}
case(GL_TRIANGLE_STRIP):
{
unsigned int pos=first;
for(GLsizei i=2;i<count;++i,++pos)
{
if ((i%2)) writeTriangle(pos,pos+2,pos+1);
else writeTriangle(pos,pos+1,pos+2);
}
break;
}
case(GL_QUADS):
{
unsigned int pos=first;
for(GLsizei i=3;i<count;i+=4,pos+=4)
{
writeTriangle(pos,pos+1,pos+2);
writeTriangle(pos,pos+2,pos+3);
}
break;
}
case(GL_QUAD_STRIP):
{
unsigned int pos=first;
for(GLsizei i=3;i<count;i+=2,pos+=2)
{
writeTriangle(pos,pos+1,pos+2);
writeTriangle(pos+1,pos+3,pos+2);
}
break;
}
case(GL_POLYGON): // treat polygons as GL_TRIANGLE_FAN
case(GL_TRIANGLE_FAN):
{
unsigned int pos=first+1;
for(GLsizei i=2;i<count;++i,++pos)
{
writeTriangle(first,pos,pos+1);
}
break;
}
case(GL_POINTS):
{
for(GLsizei i=0;i<count;++i)
{
writePoint(i);
}
break;
}
case(GL_LINES):
{
for(GLsizei i=0;i<count;i+=2)
{
writeLine(i, i+1);
}
break;
}
case(GL_LINE_STRIP):
{
for(GLsizei i=1;i<count;++i)
{
writeLine(i-1, i);
}
break;
}
case(GL_LINE_LOOP):
{
for(GLsizei i=1;i<count;++i)
{
writeLine(i-1, i);
}
writeLine(count-1, 0);
break;
}
default:
osg::notify(osg::WARN) << "DXFWriterNodeVisitor :: can't handle mode " << mode << std::endl;
break;
}
}
// TODO - illegal acad characters
std::string DXFWriterNodeVisitor::getLayerName(const std::string& defaultvalue)
{
std::string layerName=defaultvalue;
std::transform(layerName.begin(), layerName.end(), layerName.begin(), toupper);
// remove illegal ACAD characters
size_t found=0;
const std::string allowed("ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_-");
while ( (found=layerName.find_first_not_of(allowed)) != std::string::npos) {
layerName[found] = '-';
}
// TODO check that changed value isn't also a dupe
for (std::vector<Layer>::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 "<<layer._name<<"\n";
// osg::notify(osg::DEBUG_INFO) << "# " << array->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<osg::Vec3Array*>(geo->getVertexArray());
for (unsigned int ii=0;ii<data->getNumElements();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"<<point.x()<<"\n 20\n"<<point.y()<<"\n 30\n"<<point.z()<<"\n";
}
}
}
}
}
}
void DXFWriterNodeVisitor::apply( osg::Geode &node )
{
pushStateSet(node.getStateSet());
osg::Matrix m = osg::computeLocalToWorld(getNodePath());
unsigned int count = node.getNumDrawables();
for ( unsigned int i = 0; i < count; i++ )
{
osg::Geometry *g = node.getDrawable( i )->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"<<bound.center().x()-bound.radius()<<"\n20\n"<<bound.center().y()-bound.radius()<<"\n30\n"<<bound.center().z()-bound.radius()<<"\n";
_fout << "9\n$EXTMAX\n10\n"<<bound.center().x()+bound.radius()<<"\n20\n"<<bound.center().y()+bound.radius()<<"\n30\n"<<bound.center().z()+bound.radius()<<"\n";
_fout << "0\nENDSEC\n0\nSECTION\n2\nTABLES\n";
_fout << "0\nTABLE\n2\nLAYER\n";
for (std::vector<Layer>::iterator itr=_layers.begin();itr!=_layers.end();itr++) {
if ( itr->_color ) {
_fout<<"0\nLAYER\n2\n"<<itr->_name<<"\n70\n0\n62\n"<<itr->_color<<"\n6\nContinuous\n"; // color by layer
} else {
_fout<<"0\nLAYER\n2\n"<<itr->_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;
}

View File

@@ -0,0 +1,282 @@
// -*-c++-*-
/*
* Wavefront DXF loader for Open Scene Graph
*
* Copyright (C) 2001 Ulrich Hertlein <u.hertlein@web.de>
*
* 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 <string>
#include <stack>
#include <sstream>
#include <osg/Notify>
#include <osg/Node>
#include <osg/MatrixTransform>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/StateSet>
#include <osg/Material>
#include <osg/Texture2D>
#include <osg/TexGen>
#include <osg/TexMat>
#include <osgDB/Registry>
#include <osgDB/ReadFile>
#include <osgDB/FileUtils>
#include <osgDB/FileNameUtils>
#include <map>
#include <set>
#include <iostream>
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<const unsigned int, unsigned char> _indexColors; // maps RGB to autocad index colour
std::map<const unsigned int, unsigned char> _hueColors; // maps hue angle to autocad index colour
typedef std::pair <const unsigned int, unsigned char> ColorPair;
std::map<const unsigned int, unsigned char>::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<osg::Vec4Array*>(geo->getColorArray());
if ( data && index<data->size() ) {
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<osg::StateSet*>(_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<osg::StateSet>& ss1, const osg::ref_ptr<osg::StateSet>& 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<osg::ref_ptr<osg::StateSet> > StateSetStack;
std::ostream& _fout;
std::list<std::string> _nameStack;
StateSetStack _stateSetStack;
osg::ref_ptr<osg::StateSet> _currentStateSet;
unsigned int _count;
std::vector<Layer> _layers;
bool _firstPass;
Layer _layer;
AcadColor _acadColor;
};
#endif

View File

@@ -23,6 +23,7 @@
#include <string.h>
#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<const osg::Node*>(&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<const osg::Node*>(&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<osg::Node*>(&node))->accept(nv); // first pass is to get all node names and types -> layers
if ( nv.writeHeader(node.getBound()) ) {
(const_cast<osg::Node*>(&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<osg::Node*>(&node))->accept(nv); // first pass is to get all node names and types -> layers
if ( nv.writeHeader(node.getBound()) ) {
(const_cast<osg::Node*>(&node))->accept(nv); // second pass outputs data
nv.writeFooter();
}
return WriteResult(WriteResult::FILE_SAVED);
}
protected:
};