Files
OpenSceneGraph/src/osgPlugins/dxf/DXFWriterNodeVisitor.h
Robert Osfield e1b7de4b3d 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!"
2009-06-08 14:06:58 +00:00

283 lines
8.0 KiB
C++

// -*-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