From 4abfdb1ebdfeca08d280c47f92edd456ed245cdc Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Mon, 18 Feb 2002 20:46:47 +0000 Subject: [PATCH] Added OpenDX writer from Randall Hopper. --- AUTHORS | 1 + NEWS | 4 +- VisualStudio/VisualStudio.dsw | 18 + VisualStudio/osgPlugins/dx/dx.dsp | 138 + include/osgGLUT/GLUTEventAdapter | 3 + src/osgGLUT/GLUTEventAdapter.cpp | 40 +- src/osgPlugins/Makefile | 4 + src/osgPlugins/dx/AreaGeoSetTriangulator.cpp | 640 +++++ src/osgPlugins/dx/AreaGeoSetTriangulator.h | 12 + src/osgPlugins/dx/DXWriter.cpp | 2687 ++++++++++++++++++ src/osgPlugins/dx/DXWriter.h | 47 + src/osgPlugins/dx/Makefile | 23 + src/osgPlugins/dx/ReaderWriterDX.cpp | 79 + src/osgPlugins/dx/ReaderWriterDX.h | 47 + src/osgPlugins/dx/StateSetStr.cpp | 162 ++ src/osgPlugins/dx/StateSetStr.h | 18 + src/osgPlugins/osg/ReaderWriterOSG.cpp | 3 + 17 files changed, 3918 insertions(+), 8 deletions(-) create mode 100755 VisualStudio/osgPlugins/dx/dx.dsp create mode 100644 src/osgPlugins/dx/AreaGeoSetTriangulator.cpp create mode 100644 src/osgPlugins/dx/AreaGeoSetTriangulator.h create mode 100644 src/osgPlugins/dx/DXWriter.cpp create mode 100644 src/osgPlugins/dx/DXWriter.h create mode 100644 src/osgPlugins/dx/Makefile create mode 100644 src/osgPlugins/dx/ReaderWriterDX.cpp create mode 100644 src/osgPlugins/dx/ReaderWriterDX.h create mode 100644 src/osgPlugins/dx/StateSetStr.cpp create mode 100644 src/osgPlugins/dx/StateSetStr.h diff --git a/AUTHORS b/AUTHORS index 3f3e3288e..318ca4169 100644 --- a/AUTHORS +++ b/AUTHORS @@ -52,6 +52,7 @@ Byan Woods Randall Hopper - port to FreeBSD. - warning fixes to IRIX compilation. + - Open DX writer. Ulrich Hertlein - support for IBM Mirror Repeat extension in osg::Texture diff --git a/NEWS b/NEWS index 73c177927..2ba9b2f66 100644 --- a/NEWS +++ b/NEWS @@ -1,7 +1,7 @@ OSG News (most significant items from ChangeLog) ================================================ -10th February 2002 - osg-0.8.44.tar.gz +18th February 2002 - osg-0.8.44.tar.gz >>> Support added for Mac OSX, Cygwin and MinGW, and new TerraPage loader. @@ -28,6 +28,8 @@ OSG News (most significant items from ChangeLog) The Open Flight .flt loader has undergone a number of improvements including support for instancing and billboards. + New TerraPage (.txp) loader and OpenDX writer (.dx) plugins have been added. + Support for the clone operation on osg::Object's has been expanded to allow cloneType() - a simple clone an blank object of the same type, and clone(CopyOp&) where CopyOp is a functor which allows users to diff --git a/VisualStudio/VisualStudio.dsw b/VisualStudio/VisualStudio.dsw index 43ed264a4..443ed1f85 100644 --- a/VisualStudio/VisualStudio.dsw +++ b/VisualStudio/VisualStudio.dsw @@ -213,6 +213,24 @@ Package=<4> ############################################################################### +Project: "dx"=".\osgPlugins\dx\dx.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name osg + End Project Dependency + Begin Project Dependency + Project_Dep_Name osgDB + End Project Dependency +}}} + +############################################################################### + Project: "osg"=.\osg\osg.dsp - Package Owner=<4> Package=<5> diff --git a/VisualStudio/osgPlugins/dx/dx.dsp b/VisualStudio/osgPlugins/dx/dx.dsp new file mode 100755 index 000000000..9bc1de429 --- /dev/null +++ b/VisualStudio/osgPlugins/dx/dx.dsp @@ -0,0 +1,138 @@ +# Microsoft Developer Studio Project File - Name="dx" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=dx - Win32 Debug +!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 "dx.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 "dx.mak" CFG="dx - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "dx - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "dx - 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)" == "dx - 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 "" +# 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" /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 /nologo /dll /pdb:none /machine:I386 /nodefaultlib:"LIBC" /out:"../../../bin/osgdb_dx.dll" /libpath:"../../../lib" +# SUBTRACT LINK32 /nodefaultlib + +!ELSEIF "$(CFG)" == "dx - 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 "" +# 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" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /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 /nologo /dll /debug /machine:I386 /nodefaultlib:"LIBC" /out:"../../../bin/osgdb_dxd.dll" /pdbtype:sept /libpath:"../../../lib" +# SUBTRACT LINK32 /pdb:none /nodefaultlib + +!ENDIF + +# Begin Target + +# Name "dx - Win32 Release" +# Name "dx - 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\dx\AreaGeoSetTriangulator.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\src\osgPlugins\dx\DXWriter.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\src\osgPlugins\dx\ReaderWriterDX.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\..\src\osgPlugins\dx\StateSetStr.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\..\..\src\osgPlugins\dx\AreaGeoSetTriangulator.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\src\osgPlugins\dx\DXWriter.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\src\osgPlugins\dx\ReaderWriterDX.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\src\osgPlugins\dx\StateSetStr.h +# End Source File +# 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 + \ No newline at end of file diff --git a/include/osgGLUT/GLUTEventAdapter b/include/osgGLUT/GLUTEventAdapter index 4c803b907..2493dc32b 100644 --- a/include/osgGLUT/GLUTEventAdapter +++ b/include/osgGLUT/GLUTEventAdapter @@ -97,6 +97,9 @@ class OSGGLUT_EXPORT GLUTEventAdapter : public osgUtil::GUIEventAdapter // which required the mouse buttons state at the time of the event. static unsigned int _s_accumulatedButtonMask; + // used to store current button value + static int _s_button; + // used to store window min and max values. static int _s_Xmin; static int _s_Xmax; diff --git a/src/osgGLUT/GLUTEventAdapter.cpp b/src/osgGLUT/GLUTEventAdapter.cpp index 15e2c30f4..f5d572d65 100644 --- a/src/osgGLUT/GLUTEventAdapter.cpp +++ b/src/osgGLUT/GLUTEventAdapter.cpp @@ -6,6 +6,7 @@ using namespace osgGLUT; // default to no mouse buttons being pressed. unsigned int GLUTEventAdapter::_s_accumulatedButtonMask = 0; +int GLUTEventAdapter::_s_button = 0; int GLUTEventAdapter::_s_Xmin = 0; int GLUTEventAdapter::_s_Xmax = 1280; int GLUTEventAdapter::_s_Ymin = 0; @@ -31,6 +32,7 @@ GLUTEventAdapter::GLUTEventAdapter() void GLUTEventAdapter::copyStaticVariables() { _buttonMask = _s_accumulatedButtonMask; + _button = _s_button; _Xmin = _s_Xmin; _Xmax = _s_Xmax; _Ymin = _s_Ymin; @@ -95,13 +97,25 @@ void GLUTEventAdapter::adaptMouse(float time, int button, int state, int x, int { _eventType = PUSH; - _button = button; + _button = button; switch(button) { - case(GLUT_LEFT_BUTTON): _s_accumulatedButtonMask = _s_accumulatedButtonMask | LEFT_MOUSE_BUTTON; break; - case(GLUT_MIDDLE_BUTTON): _s_accumulatedButtonMask = _s_accumulatedButtonMask | MIDDLE_MOUSE_BUTTON; break; - case(GLUT_RIGHT_BUTTON): _s_accumulatedButtonMask = _s_accumulatedButtonMask | RIGHT_MOUSE_BUTTON; break; + case(GLUT_LEFT_BUTTON): + _s_accumulatedButtonMask = + _s_accumulatedButtonMask | LEFT_MOUSE_BUTTON; + _s_button = LEFT_MOUSE_BUTTON; + break; + case(GLUT_MIDDLE_BUTTON): + _s_accumulatedButtonMask = + _s_accumulatedButtonMask | MIDDLE_MOUSE_BUTTON; + _s_button = MIDDLE_MOUSE_BUTTON; + break; + case(GLUT_RIGHT_BUTTON): + _s_accumulatedButtonMask = + _s_accumulatedButtonMask | RIGHT_MOUSE_BUTTON; + _s_button = RIGHT_MOUSE_BUTTON; + break; } } @@ -113,9 +127,21 @@ void GLUTEventAdapter::adaptMouse(float time, int button, int state, int x, int switch(button) { - case(GLUT_LEFT_BUTTON): _s_accumulatedButtonMask = _s_accumulatedButtonMask & ~LEFT_MOUSE_BUTTON; break; - case(GLUT_MIDDLE_BUTTON): _s_accumulatedButtonMask = _s_accumulatedButtonMask & ~MIDDLE_MOUSE_BUTTON; break; - case(GLUT_RIGHT_BUTTON): _s_accumulatedButtonMask = _s_accumulatedButtonMask & ~RIGHT_MOUSE_BUTTON; break; + case(GLUT_LEFT_BUTTON): + _s_accumulatedButtonMask = + _s_accumulatedButtonMask & ~LEFT_MOUSE_BUTTON; + _s_button = LEFT_MOUSE_BUTTON; + break; + case(GLUT_MIDDLE_BUTTON): + _s_accumulatedButtonMask = + _s_accumulatedButtonMask & ~MIDDLE_MOUSE_BUTTON; + _s_button = MIDDLE_MOUSE_BUTTON; + break; + case(GLUT_RIGHT_BUTTON): + _s_accumulatedButtonMask = + _s_accumulatedButtonMask & ~RIGHT_MOUSE_BUTTON; + _s_button = RIGHT_MOUSE_BUTTON; + break; } } diff --git a/src/osgPlugins/Makefile b/src/osgPlugins/Makefile index c3983dc3c..e886f1125 100644 --- a/src/osgPlugins/Makefile +++ b/src/osgPlugins/Makefile @@ -6,6 +6,10 @@ DIRS = osg rgb lib3ds flt obj lwo txp dw bmp pic tga osgtgz tgz zip # comment in if you have Performer installed. # DIRS += pfb +# comment in if you want Open DX support, currently not in by default +# since we havn't yet checked compilation on all platforms yet. +# DIRS += dx + # comment in if you have Quicktime installed, i.e under Mac OS. # if in this case then its likely you'll want to comment out the below # png, jpeg, gif and tiff plugins. diff --git a/src/osgPlugins/dx/AreaGeoSetTriangulator.cpp b/src/osgPlugins/dx/AreaGeoSetTriangulator.cpp new file mode 100644 index 000000000..c9931e41e --- /dev/null +++ b/src/osgPlugins/dx/AreaGeoSetTriangulator.cpp @@ -0,0 +1,640 @@ +#include +#include +#include +#include "AreaGeoSetTriangulator.h" + +namespace dx { + +class AreaGeoSetTriangulator + // Used to convert area primitive GeoSets into TRIANGLE Geosets. + // Efficiently collects the various data arrays, which GeoSet can't do. +{ +protected: + std::vector< osg::uint > _cindex; + std::vector< osg::uint > _nindex; + std::vector< osg::uint > _colindex; + std::vector< osg::uint > _tindex; + const osg::GeoSet &_area_geoset; + +public: + AreaGeoSetTriangulator( const osg::GeoSet &area_geoset ); + + void operator() ( osg::uint v1, osg::uint v2, osg::uint v3, + osg::uint prim_index, osg::uint prim_tri_index, + osg::uint v1_geoset, osg::uint v2_geoset, + osg::uint v3_geoset ); + // Passed triangles as primitives are triangulated + + osg::GeoSet *BuildGeoSet(); +}; + +//--------------------------------------------------------------------------- +AreaGeoSetTriangulator::AreaGeoSetTriangulator( const osg::GeoSet &area_geoset) + : _area_geoset(area_geoset) +{ + // Check type + switch ( area_geoset.getPrimType() ) { + case osg::GeoSet::TRIANGLE_STRIP: + case osg::GeoSet::FLAT_TRIANGLE_STRIP: + case osg::GeoSet::TRIANGLES: + case osg::GeoSet::QUAD_STRIP: + case osg::GeoSet::QUADS: + case osg::GeoSet::TRIANGLE_FAN: + case osg::GeoSet::FLAT_TRIANGLE_FAN: + case osg::GeoSet::POLYGON: + break; + default: + fprintf( stderr, + "Invalid primitive type passed to AreaGeoSetTriangulator\n" ); + exit(1); + } + + // NOTE: _coords, _normals, _colors, and _tcoords are maintained + // from the original geoset. The new geoset will always have + // indexed coords/normals/colors/tcoords (to save space). +} + +//--------------------------------------------------------------------------- +void AreaGeoSetTriangulator::operator() ( + osg::uint v1, osg::uint v2, osg::uint v3, + osg::uint prim_index, osg::uint prim_tri_index, + osg::uint v1_geoset, osg::uint v2_geoset, + osg::uint v3_geoset ) + // Passed triangles as primitives are triangulated from the original + // GeoSet. Note that the v? params are indicies into the Coord array, + // whereas the v?_geoset params are vertex numbers relative to the + // entire "area GeoSet" (which are different when the coords are + // indexed). The latter is needed to look up colors/normals/tcoords). +{ + osg::GeoSet::BindingType binding; + osg::GeoSet::PrimitiveType primtype = _area_geoset.getPrimType(); + int area_is_flatprim = ( primtype == osg::GeoSet::FLAT_TRIANGLE_STRIP || + primtype == osg::GeoSet::FLAT_TRIANGLE_FAN ); + + // Store the triangle coord indicies + _cindex.push_back( v1 ); + _cindex.push_back( v2 ); + _cindex.push_back( v3 ); + + int index; + const osg::GeoSet::IndexPointer *ip; + + // Store normals (as needed) + if ( _area_geoset.getNumNormals() ) { + ip = &_area_geoset.getNormalIndices(); + + // Grrr... FLAT_ primitives lie about binding type... like Performer + binding = _area_geoset.getNormalBinding(); + if ( area_is_flatprim && binding == osg::GeoSet::BIND_PERVERTEX ) + binding = osg::GeoSet::BIND_PERPRIM; + + switch ( binding ) { + + case osg::GeoSet::BIND_OVERALL : // Only once + if ( prim_index == 0 && prim_tri_index == 0 ) { + index = ip->valid() ? (*ip)[0] : 0; + _nindex.push_back( index ); + break; + } + + case osg::GeoSet::BIND_PERPRIM : // Once per tri + index = ip->valid() ? (*ip)[prim_index] : prim_index; + _nindex.push_back( index ); + break; + + case osg::GeoSet::BIND_PERVERTEX : // Each vertex + index = ip->valid() ? (*ip)[v1_geoset] : v1_geoset; + _nindex.push_back( index ); + index = ip->valid() ? (*ip)[v2_geoset] : v2_geoset; + _nindex.push_back( index ); + index = ip->valid() ? (*ip)[v3_geoset] : v3_geoset; + _nindex.push_back( index ); + break; + + case osg::GeoSet::BIND_OFF: + case osg::GeoSet::BIND_DEFAULT: + // Nonsensical cases + break; + } + } + + // Store colors (as needed) + if ( _area_geoset.getNumColors() ) { + ip = &_area_geoset.getColorIndices(); + + // Grrr... FLAT_ primitives lie about binding type... like Performer + binding = _area_geoset.getColorBinding(); + if ( area_is_flatprim && binding == osg::GeoSet::BIND_PERVERTEX ) + binding = osg::GeoSet::BIND_PERPRIM; + + switch ( binding ) { + + case osg::GeoSet::BIND_OVERALL : // Only once + if ( prim_index == 0 && prim_tri_index == 0 ) { + index = ip->valid() ? (*ip)[0] : 0; + _colindex.push_back( index ); + break; + } + + case osg::GeoSet::BIND_PERPRIM : // Once per tri + index = ip->valid() ? (*ip)[prim_index] : prim_index; + _colindex.push_back( index ); + break; + + case osg::GeoSet::BIND_PERVERTEX : // Each vertex + index = ip->valid() ? (*ip)[v1_geoset] : v1_geoset; + _colindex.push_back( index ); + index = ip->valid() ? (*ip)[v2_geoset] : v2_geoset; + _colindex.push_back( index ); + index = ip->valid() ? (*ip)[v3_geoset] : v3_geoset; + _colindex.push_back( index ); + break; + + case osg::GeoSet::BIND_OFF: + case osg::GeoSet::BIND_DEFAULT: + // Nonsensical cases + break; + } + } + + // Store tcoords (as needed) + if ( _area_geoset.getNumTextureCoords() ) { + ip = &_area_geoset.getTextureIndices(); + + switch ( _area_geoset.getTextureBinding() ) { + + case osg::GeoSet::BIND_OVERALL : // Only once + if ( prim_index == 0 && prim_tri_index == 0 ) { + index = ip->valid() ? (*ip)[0] : 0; + _tindex.push_back( index ); + break; + } + + case osg::GeoSet::BIND_PERPRIM : // Once per tri + index = ip->valid() ? (*ip)[prim_index] : prim_index; + _tindex.push_back( index ); + break; + + case osg::GeoSet::BIND_PERVERTEX : // Each vertex + index = ip->valid() ? (*ip)[v1_geoset] : v1_geoset; + _tindex.push_back( index ); + index = ip->valid() ? (*ip)[v2_geoset] : v2_geoset; + _tindex.push_back( index ); + index = ip->valid() ? (*ip)[v3_geoset] : v3_geoset; + _tindex.push_back( index ); + break; + + case osg::GeoSet::BIND_OFF: + case osg::GeoSet::BIND_DEFAULT: + // Nonsensical cases + break; + } + } + +} + +//---------------------------------------------------------------------------- +// Shamelessly lifted from the GeoSet header and adapted for our uses +// This is the same except that: 1) it passes the coordinate indices +// to the op function insted of the Vec3s, and 2) it passes an original +// primitive index and "triangle-in-primitive" index to the op function, +// 3) it also passes the absolute vertex numbers in the primitive so they +// can be used to look up normals/colors/tcoords, and 4) fixes some +// problems with vertex ordering which caused FRONT/BACK confusion with +// normals and lighting. +// With this, we can construct a new TRIANGLE GeoSet from any area GeoSet. + +/** Template function for iterating through a GeoSet operating on triangles + with templated functor. Function automatically decomposes quads and polygons + into sub triangles which are passed onto functor.*/ + +template +void for_each_triangle2(const osg::GeoSet& gset,T& op) +{ + switch(gset.getPrimType()) + { + case(osg::GeoSet::TRIANGLE_STRIP): + case(osg::GeoSet::FLAT_TRIANGLE_STRIP): + { + if (gset.getCoordIndices().valid()) + { + if (gset.getCoordIndices()._is_ushort) + { + osg::ushort* base = gset.getCoordIndices()._ptr._ushort; + osg::ushort* iptr = base; + const int numPrim = gset.getNumPrims(); + for(int i=0; i0) + { + osg::ushort *start = iptr; + osg::ushort* iend = iptr+primLength; + ++iptr; + for(int j = 2; j < primLength; ++j ) + { + op(*start,*(iptr),*(iptr+1),i,tri++, + start-base, iptr-base,(iptr+1)-base); + ++iptr; + } + iptr=iend; + } + } + } + else + { + osg::uint* base = gset.getCoordIndices()._ptr._uint; + osg::uint* iptr = base; + const int numPrim = gset.getNumPrims(); + for(int i=0; i0) + { + osg::uint *start = iptr; + osg::uint* iend = iptr+primLength; + ++iptr; + for(int j = 2; j < primLength; ++j ) + { + op(*start,*(iptr),*(iptr+1),i,tri++, + start-base, iptr-base,(iptr+1)-base); + ++iptr; + } + iptr=iend; + } + } + } + } + else + { + osg::uint cindex = 0; + const int numPrim = gset.getNumPrims(); + for(int i=0; i0) + { + osg::uint cindex_start = cindex; + osg::uint cindex_end = cindex+primLength; + ++cindex; + for(int j = 2; j < primLength; ++j) + { + op(cindex_start,cindex,cindex+1,i,tri++, + cindex_start,cindex,cindex+1); + ++cindex; + } + cindex = cindex_end; + } + } + } + } + break; + default: + break; + } + +}; + + +//--------------------------------------------------------------------------- +osg::GeoSet *AreaGeoSetTriangulator::BuildGeoSet() + // After a triangulation pass, generate a valid TRIANGLE GeoSet containing + // the resulting triangles (w/ colors, normals, and tcoords). +{ + int num_tris = _cindex.size() / 3; + + osg::GeoSet *res = new osg::GeoSet; + res->setPrimType( osg::GeoSet::TRIANGLES ); + res->setNumPrims( num_tris ); + + // First, dup all of the data arrays -- they haven't changed + // NOTE: We generate fresh arrays for the new GeoSet to be compatible + // with the delete [] behavior of the default GeoSet + // AttributeDeleteFunctor, which is invoked in GeoSet::~GeoSet. + + osg::Vec3 *new_coords = 0; + osg::Vec3 *new_normals = 0; + osg::Vec4 *new_colors = 0; + osg::Vec2 *new_tcoords = 0; + + const osg::Vec3 *coords = _area_geoset.getCoords(); + const osg::Vec3 *normals = _area_geoset.getNormals(); + const osg::Vec4 *colors = _area_geoset.getColors(); + const osg::Vec2 *tcoords = _area_geoset.getTextureCoords(); + + if ( coords ) + new_coords = new osg::Vec3[ _area_geoset.getNumCoords() ]; + if ( normals ) + new_normals = new osg::Vec3[ _area_geoset.getNumNormals() ]; + if ( colors ) + new_colors = new osg::Vec4[ _area_geoset.getNumColors() ]; + if ( tcoords ) + new_tcoords = new osg::Vec2[ _area_geoset.getNumTextureCoords() ]; + + memcpy( new_coords, coords, + sizeof(osg::Vec3) * _area_geoset.getNumCoords() ); + memcpy( new_normals, normals, + sizeof(osg::Vec3) * _area_geoset.getNumNormals() ); + memcpy( new_colors, colors, + sizeof(osg::Vec4) * _area_geoset.getNumColors() ); + memcpy( new_tcoords, tcoords, + sizeof(osg::Vec2) * _area_geoset.getNumTextureCoords() ); + + // Now generate the index arrays + osg::uint *new_cindex = 0; + osg::uint *new_nindex = 0; + osg::uint *new_colindex = 0; + osg::uint *new_tindex = 0; + + if ( _cindex.size() ) new_cindex = new osg::uint[ _cindex.size() ]; + if ( _nindex.size() ) new_nindex = new osg::uint[ _nindex.size() ]; + if ( _colindex.size() ) new_colindex = new osg::uint[ _colindex.size() ]; + if ( _tindex.size() ) new_tindex = new osg::uint[ _tindex.size() ]; + + memcpy( new_cindex , &_cindex [0], sizeof(osg::uint) * _cindex.size() ); + memcpy( new_nindex , &_nindex [0], sizeof(osg::uint) * _nindex.size() ); + memcpy( new_colindex, &_colindex[0], sizeof(osg::uint) * _colindex.size() ); + memcpy( new_tindex , &_tindex [0], sizeof(osg::uint) * _tindex.size() ); + + res->setCoords ( new_coords , new_cindex ); + res->setNormals( new_normals, new_nindex ); + res->setColors ( new_colors , new_colindex ); + res->setTextureCoords( new_tcoords, new_tindex ); + + // And finally, set the normal/color/tcoord binding + // (Grrr... FLAT_ primitives lie about binding type... like Performer) + osg::GeoSet::BindingType nbinding = _area_geoset.getNormalBinding(); + osg::GeoSet::BindingType cbinding = _area_geoset.getColorBinding(); + if ( _area_geoset.getPrimType() == osg::GeoSet::FLAT_TRIANGLE_STRIP || + _area_geoset.getPrimType() == osg::GeoSet::FLAT_TRIANGLE_FAN ) { + if ( nbinding == osg::GeoSet::BIND_PERVERTEX ) + nbinding = osg::GeoSet::BIND_PERPRIM; + if ( cbinding == osg::GeoSet::BIND_PERVERTEX ) + cbinding = osg::GeoSet::BIND_PERPRIM; + } + + res->setNormalBinding ( nbinding ); + res->setColorBinding ( cbinding ); + res->setTextureBinding( _area_geoset.getTextureBinding() ); + + return res; +} + +//--------------------------------------------------------------------------- +osg::GeoSet *TriangulateAreaGeoSet( const osg::GeoSet &geoset ) + // The strategy here is to generate new coord/normal/color/tcoord index + // arrays as we bust up the primitives into triangles. The data arrays + // don't change between the old and new geoset. +{ + geoset.computeNumVerts(); // Update # coords/# normals from index arrays + + AreaGeoSetTriangulator triangulator( geoset ); + for_each_triangle2( geoset, triangulator ); + return triangulator.BuildGeoSet(); +} + +}; // namespace dx diff --git a/src/osgPlugins/dx/AreaGeoSetTriangulator.h b/src/osgPlugins/dx/AreaGeoSetTriangulator.h new file mode 100644 index 000000000..60f164b36 --- /dev/null +++ b/src/osgPlugins/dx/AreaGeoSetTriangulator.h @@ -0,0 +1,12 @@ +#ifndef __OSG_AREA_GEOSET_TRIANGULATOR_H +#define __OSG_AREA_GEOSET_TRIANGULATOR_H + +#include + +namespace dx { + +osg::GeoSet *TriangulateAreaGeoSet( const osg::GeoSet &geoset ); + +}; + +#endif diff --git a/src/osgPlugins/dx/DXWriter.cpp b/src/osgPlugins/dx/DXWriter.cpp new file mode 100644 index 000000000..14d61e7c6 --- /dev/null +++ b/src/osgPlugins/dx/DXWriter.cpp @@ -0,0 +1,2687 @@ +// This plugin writes an IBM Data Explorer (aka OpenDX) native file. +// (c) Randall Hopper, 2002. +// +// For details on the OpenDX visualization tool, its use, and its file format, +// refer to: +// +// http://www.opendx.org/ +// http://www.opendx.org/support.html#docs +// http://www.research.ibm.com/dx/ +// http://ftp.cs.umt.edu/DX/ +// +// SUPPORTED: +// +// - Objects without color are assigned a default opaque light gray color +// - All GeoSet primitive types: +// POINTS, LINES, TRIANGLES, QUADS, LINE_STRIP, FLAT_LINE_STRIP, +// LINE_LOOP, TRIANGLE_STRIP, FLAT_TRIANGLE_STRIP, TRIANGLE_FAN, +// FLAT_TRIANGLE_FAN, QUAD_STRIP, POLYGON +// NOTE: area strips, fans, polygons, and quads are mapped to triangles +// fields; line strips and loops are mapped to polylines fields +// - OSG Node Types: +// Geode, Group, Billboards (hack), LOD (hack), Switch (hack) +// NOTES: Billboards - written as a translation Transform parenting +// the Geode Fields, so they do not rotate in DX like they should; +// LODs - only the most detailed child is written; +// Switches - only the active child is written +// - Coords, colors, normals, tcoords; with all binding types +// (per vertex/primitive/overall) +// - MATERIAL attributes -- all except ColorMode +// - Single texturing (TEXTURE_0), with RGB, RGBA, LUMINANCE, LUMINANCE_ALPHA +// textures (note: you'll need a recent DX from CVS for this to work); +// note that LUMINANCE* textures are expanded to RGB* in the conversion +// - Texture function, texture min/mag filters +// - Cull face +// +// UNSUPPORTED: +// +// - OSG Scene Graph Node Types: +// LightSource, Transform, Imposter, EarthSky, +// Switch - only active child written +// LOD - only most detailed child written +// Billboard - only static translation transform parenting child written, +// and only one Geode per Billboard is supported; DX has +// no concept just like Performer/OSG billboards that rotate +// to face the camera but scale and translate with the scene +// +// - Interleaved GeoSet data (interleaved set, no coords set) +// +// - Report just the unhandled GL modes/attributes, not all of them +// +// - DX only supports UNSIGNED_BYTE GL_RGB (and now GL_RGBA in recent CVS) +// textures, so we convert LUMINANCE* textures to RGB* +// +// - Handle other attributes in StateSet +// +// - TEXTURE_[1-3], and rest of subattributes in TEXTURE_0 +// - MATERIAL - ColorMode (read but never processed); specifies that +// certain material attributes are set by glColor +// - ALPHAFUNC +// - ANTIALIAS +// - COLORTABLE +// - FOG +// - FRONTFACE +// - LIGHT, LIGHT_[0-7] +// - POINT +// - LINEWIDTH +// - POLYGONMODE +// - POLYGONOFFSET +// - TEXENV +// - TEXGEN +// - TEXMAT +// - TRANSPARENCY +// - STENCIL +// - COLORMASK +// - DEPTH +// - VIEWPORT +// - CLIPPLANE, CLIPPLANE_[0-5] +// - COLORMATRIX +// +// OTHER TO DO: +// +// - Use more meaningful names for nameless nodes (based on parent names) +// - Don't write a colormap when OSG colors aren't indexed unless num +// colors > 256 or something (see cane example) +// - Support ReaderWriter::Options for exporter switches +// - Support saving DX stream in both ASCII and binary +// - Handle normal/color/tcoord collisions (aliasing) when coords are indexed +// (See comment in DXArrayWriter::WritePerVertexNormals for details) +// Same applies for normals, colors, and tcoords +// - Handle accumulation of more than just Geodes in Groups (?) +// - Consider support for writing DX file with interleaved arrays, +// arrays at end, arrays in diff file in DX file +// - char [] -> std::string, globally +// +// DXWriter.h + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "DXWriter.h" +#include "AreaGeoSetTriangulator.h" +#include "StateSetStr.h" + +#if defined(__sgi) || defined(__FreeBSD__) + #include +#else + #include + #if defined(WIN32) || defined (macintosh) + #include + #endif +#endif + +namespace dx { + +//---------------------------------------------------------------------------- +// COMPILATION TWEAKS +//---------------------------------------------------------------------------- + +//#define SKIP_SINGLE_MEMBER_DXGROUPS + +//---------------------------------------------------------------------------- + +const osg::Vec3 INVALID_NORMAL ( -99,-99,-99 ); +const osg::Vec3 INVALID_COLOR ( 0,0,0 ); +const float INVALID_OPACITY = 1.0; +const float ALPHA_OPAQUE = 1.0; + +#define ARRAY_LEN(a) (sizeof(a)/sizeof((a)[0])) + +typedef osg::ubyte Vec4UB[4]; +typedef osg::ubyte Vec3UB[3]; + +//---------------------------------------------------------------------------- + +class DXNameManager + // Generates unique object names +{ + public: + std::string GetUnique( const std::string &suggestion ); + + protected: + std::map< std::string, int > dict; +}; + +std::string DXNameManager::GetUnique( const std::string &suggestion ) +{ + std::string name = suggestion; + int counter = 1; + char buf[30]; + + while ( dict.find( name ) != dict.end() ) { + sprintf( buf, " #%d", counter++ ); + name = ( suggestion.empty() ? "Object" : suggestion ) + buf; + } + dict[name] = 1; + return name; +} + +//---------------------------------------------------------------------------- + +class DXTextureManager + // Keeps track of what textures we've already written and their field names +{ + public: + void Register( const osg::Image *image, std::string dx_name ) + { dict[ image ] = dx_name; } + bool IsRegistered( const osg::Image *image ) + { return dict.find( image ) != dict.end(); } + std::string Lookup( const osg::Image *image ) + { return dict[ image ]; } + + protected: + std::map< const osg::Image *, std::string > dict; +}; + +//---------------------------------------------------------------------------- + +class MessageBin +{ + protected: + typedef std::vector< std::string > MessageList; + MessageList msg_list; + + public: + void Add( char fmt[], ... ) + { + char msg[1024]; + va_list args; + va_start(args, fmt); + int ret = vsnprintf(msg, sizeof(msg), fmt, args); + va_end(args); + + msg_list.push_back( std::string( msg ) ); + } + + std::string GetPending() + { + std::string result; + for ( unsigned i = 0; i < msg_list.size(); i++ ) { + if ( !result.empty() ) + result += '\n'; + result += msg_list[i]; + } + return result; + } + + void Clear() + { msg_list.clear(); } +}; + +//---------------------------------------------------------------------------- + +class DXField +{ + public: + struct Component { + std::string name; + std::string value; + Component( const char name[], const char value[] ) + : name(name), value(value) {} + }; + + std::vector< Component > comp; + std::string name; + + DXField( DXNameManager &name_mgr, const std::string *name_suggestion ) + { if ( name_suggestion && !name_suggestion->empty() ) + name = *name_suggestion; + else + name = "Field"; + name = name_mgr.GetUnique( name ); + } + + std::string &GetName() { return name; } + + void AddComponent( const char name[], const char value[] ) + { comp.push_back( Component( name, value ) ); } + + void Write( FILE *fp, const char *attr=0 ) + { + fprintf( fp, "object \"%s\" class field\n", name.c_str() ); + for ( unsigned i = 0; i < comp.size(); i++ ) + fprintf( fp, "component \"%s\" value \"%s\"\n", + comp[i].name.c_str(), comp[i].value.c_str() ); + if ( attr ) + fprintf( fp, "%s\n", attr ); + fprintf( fp, "#\n\n" ); + } +}; + +//---------------------------------------------------------------------------- + +class DXGroup +{ + public: + typedef std::vector< std::string > MemberList; + MemberList members; + std::string name; + + DXGroup( DXNameManager &name_mgr, + const std::string *name_suggestion = 0, + const std::string *fallback_suggestion = 0 ) + { if ( name_suggestion && !name_suggestion->empty() ) + name = *name_suggestion; + else if ( fallback_suggestion ) + name = fallback_suggestion->c_str(); + else + name = "Group"; + name = name_mgr.GetUnique( name ); + } + + void AddMember( const char member_name[] ) + { members.push_back( std::string( member_name ) ); } + + void RemoveMember( const char member_name[] ) + { + for(MemberList::iterator itr=members.begin(); + itr!=members.end(); + ++itr) + if ( *itr == member_name ) { + members.erase(itr); + break; + } + } + + int GetNumMembers() { return members.size(); } + + std::string &GetName() { return name; } + + void Write( FILE *fp, const char *attr=0 ) + { + fprintf( fp, "object \"%s\" class group\n", GetName().c_str() ); + for ( unsigned i = 0; i < members.size(); i++ ) + fprintf( fp, "member %d value \"%s\"\n", i, members[i].c_str() ); + if ( attr ) + fprintf( fp, "%s\n", attr ); + fprintf( fp, "#\n\n" ); + } +}; + +//---------------------------------------------------------------------------- + +class DXArrayWriter +{ + protected: + FILE *fp; + MessageBin *msg_bin; + + public: + void SetFP ( FILE *_fp ) { fp = _fp; } + void SetMsgBin( MessageBin *_msg_bin ) { msg_bin = _msg_bin; } + + void DeNanify( float &f, float subst ); + void DeNanify( osg::Vec3 &v, const osg::Vec3 &subst ); + void OSGColorToDX( const osg::Vec4 &osg_color, + osg::Vec3 &dx_color, float &dx_opacity ); + void OSGColorToDX( const Vec4UB osg_color, + Vec3UB dx_color, float &dx_opacity ); + int WriteMapsYN( osg::GeoSet::IndexPointer *ip, int num_colors ); + void WriteAttributes( const char *ref, const char *dep, const char *attr ); + void WriteFloatConstArray( float f, int len, const char name[], + const char *ref, const char *dep, const char *attr=0 ); + void WriteVec3ConstArray( const osg::Vec3 &v, int len, const char name[], + const char *ref, const char *dep, const char *attr=0 ); + void WriteFloatArray( const float *array, osg::GeoSet::IndexPointer *ip, + int len, const char name[], + const char *ref, const char *dep, const char *attr=0 ); + void WriteVec2Array( const osg::Vec2 *array, osg::GeoSet::IndexPointer *ip, + int len, const char name[], + const char *ref, const char *dep, const char *attr=0 ); + void WriteVec3Array( const osg::Vec3 *array, osg::GeoSet::IndexPointer *ip, + int len, const char name[], + const char *ref, const char *dep, const char *attr=0 ); + void WriteVec4Array( const osg::Vec4 *array, osg::GeoSet::IndexPointer *ip, + int len, const char name[], + const char *ref, const char *dep, const char *attr=0 ); + void WriteUByte3Array( + const Vec3UB *arrayp, osg::GeoSet::IndexPointer *ip, + int len, const char name[], + const char *ref, const char *dep, const char *attr=0 ); + + void WriteIndexArray( const osg::GeoSet::IndexPointer &ip, int len, + int rank, int shape, int ubyte, const char name[], + const char *ref, const char *dep, const char *attr=0 ); + void WriteColors( const osg::Vec4 *colors, int num_colors, + osg::GeoSet::IndexPointer *ip, int num_cindices, + const char colors_name[], const char colormap_name[], + const char opacities_name[], const char opacitymap_name[], + int do_opacities, const char *dep, int &wrote_maps ); + + void WriteColors( const Vec4UB colors[], int num_colors, + osg::GeoSet::IndexPointer *ip, int num_cindices, + const char colors_name[], const char colormap_name[], + const char opacities_name[], const char opacitymap_name[], + int do_opacities, const char *dep, int &wrote_maps ); + + void WritePerVertexNormals( const osg::GeoSet &geoset, + const char name[], const char *attr=0 ); + void WritePerVertexColors( const osg::GeoSet &geoset, + const char colors_name[], + const char colormap_name[], + const char opacities_name[], + const char opacitymap_name[], + int do_opacities, // FIXME + int &wrote_maps, + const char *attr=0 ); + void WritePerVertexTCoords( const osg::GeoSet &geoset, + const char name[], const char *attr=0 ); +}; + +//---------------------------------------------------------------------------- + +class MyStateSet +{ + protected: + MessageBin &msg_bin; + + public: + + std::map< osg::StateAttribute::Type, int > attr; + + // MATERIAL (osg::Material) + osg::Material::ColorMode colormode; + osg::Vec4 ambient_f, ambient_b; // Front and back colors + bool ambient_f_and_b; // Use front color for front and back + osg::Vec4 diffuse_f, diffuse_b; + bool diffuse_f_and_b; + osg::Vec4 specular_f, specular_b; + bool specular_f_and_b; + osg::Vec4 emission_f, emission_b; + bool emission_f_and_b; + float shininess_f, shininess_b; + bool shininess_f_and_b; + + // TEXTURE / TEXTURE_0 + const osg::Image *image; + osg::Texture::WrapMode wrap_s, wrap_t; + osg::Texture::FilterMode min_filter, mag_filter; + + // TEXENV + osg::TexEnv::Mode texture_func; + + MyStateSet( MessageBin &msg_bin ) : image(0), msg_bin(msg_bin) {} + + // CULLFACE + osg::CullFace::Mode cullface_mode; + + void Show( osg::StateSet &sset ); + void AddAttribute( osg::StateAttribute::Type type ) + { attr[ type ] = 1; } + bool HasAttribute( osg::StateAttribute::Type type ) + { return attr.find( type ) != attr.end(); } + void Query( const osg::StateSet &sset ); + void Write(); +}; + +//---------------------------------------------------------------------------- + +class StateSetCopy : public osg::StateSet +{ + public: + StateSetCopy() {} + ~StateSetCopy() {} + + StateSetCopy( const osg::StateSet &sset ) + { + // Can't use operator = because StateSet broke it + _modeList = sset.getModeList(); + _attributeList = sset.getAttributeList(); + _renderingHint = sset.getRenderingHint(); + _binMode = sset.getRenderBinMode(); + _binNum = sset.getBinNumber(); + _binName = sset.getBinName(); + } +}; + +//---------------------------------------------------------------------------- + +class DXWriter +{ + protected: + DXArrayWriter w; + + typedef std::map ModeList; + typedef std::map AttrList; + ModeList unsupported_modes; + AttrList unsupported_attrs; + + public: + FILE *fp; + WriterParms parms; + DXNameManager name_mgr; + DXTextureManager texture_mgr; + MessageBin msg_bin; + + void SetParms( const WriterParms &p ) + { memcpy( &parms, &p, sizeof(parms) ); } + + void Open(); + void Close(); + + std::string BuildStateSetAttributes( MyStateSet &sset, + int diffuse_is_color, + int diffuse_is_opacity ); + + std::string WriteImage( const osg::Image &image ); + + void WritePolylineConnections( const std::string &field_name, + osg::GeoSet &geoset, + DXField &field ); + std::string WriteGeoSetField( const std::string &field_name, + osg::GeoSet &geoset, + MyStateSet &sset ); + std::string WriteGeoSet( osg::GeoSet &geoset, + const osg::StateSet &active_sset, + std::string &field_name ); + std::string WriteGeode( osg::Geode &geode, + const osg::StateSet &active_sset ); + + void CollectUnhandledModesAndAttrs( osg::StateSet *sset ); + void ReportUnhandledModesAndAttrs(); +}; + +//---------------------------------------------------------------------------- + +inline int WARNING( char fmt[], ... ) +{ + va_list args; + va_start(args, fmt); + int ret = vfprintf(stderr, fmt, args); + va_end(args); + return ret; +} + +//---------------------------------------------------------------------------- + +void GetParms( int argc, char *argv[], + char infile [ PATH_MAX ], WriterParms &parms ) +{ + WriterParms defaults; + + /* Init defaults */ + parms = defaults; + + /* Parse user args */ + int c, i, errflg = 0; + char *arg; + + /* We'd use getopt(), but we have args with leading minus chars... */ + for ( i = 1; i < argc && !errflg; i++ ) { + if ( argv[i][0] == '\0' ) continue; + + if ( argv[i][0] != '-' ) + break; + + c = argv[i][1]; + if ( strchr( "c", c ) ) { + if ( i+1 >= argc ) { + WARNING( "Missing argument to -%c option.\n\n", c ); + errflg++; + break; + } + + arg = argv[++i]; + } + + switch ( c ) { + case 'c': parms.set_default_color = 1; + sscanf( arg, "%g,%g,%g,%g", + &parms.default_color[0], &parms.default_color[1], + &parms.default_color[2], &parms.default_color[3] ); + break; + case '?': + default : + errflg++; + } + } + + if ( i != argc - 2 ) + errflg++; + + if (errflg) { + WARNING( "\n" + "Converts 3D model files to IBM Data Explorer format.\n\n" + "Usage: osg2dx\n" + " [-c r,g,b,a] # Give uncolored objs this color\n" + " # Input model pathname\n" + " # Output DX pathname (- = stdout)\n" + "\n" ); + exit(2); + } + + infile[0] = parms.outfile[0] = '\0'; + strncat( infile , argv[ i ], PATH_MAX-1 ); + strncat( parms.outfile, argv[i+1], sizeof(parms.outfile)-1 ); +} + +//---------------------------------------------------------------------------- + +inline int IsNaNorInf( float f ) +{ +#if defined(__sgi) + int c = fpclass( double(f) ); + switch (c) + { + case FP_SNAN : + case FP_QNAN : + case FP_NINF : + case FP_PINF : return 1; + default : return 0; + } +#elif defined(__FreeBSD__) || defined(__linux) || defined(__CYGWIN__) + return isnanf(f) || isinf(f); +#elif defined(WIN32) + return _isnan(f) || _isinfinite(f); +#else +# error Teach me how to find non-numbers on this platform. +#endif +} + + +void DXArrayWriter::DeNanify( float &f, float subst ) +{ + if ( IsNaNorInf((double)f) ) { + msg_bin->Add( "WARNING: Denanifying double.\n" ); + f = subst; + } +} + +void DXArrayWriter::DeNanify( osg::Vec3 &v, const osg::Vec3 &subst ) +{ + // An old OSG bug generated NaN normals from 0,0,0 normals; + // consider removing this someday. + // Also, Performer town has a NaN in one of the colormaps. + if ( IsNaNorInf((double)v[0]) || IsNaNorInf((double)v[1]) || + IsNaNorInf((double)v[2]) ) { + msg_bin->Add( "WARNING: Denanifying 3D vector.\n" ); + v = subst; + } +} + +//---------------------------------------------------------------------------- + +void DXArrayWriter::OSGColorToDX( const osg::Vec4 &osg_color, + osg::Vec3 &dx_color, float &dx_opacity ) +{ + dx_color.set( osg_color[0], osg_color[1], osg_color[2] ); + dx_opacity = osg_color[3]; // alpha==1.0 -> opaque +} + +//---------------------------------------------------------------------------- + +void DXArrayWriter::OSGColorToDX( const Vec4UB osg_color, + Vec3UB dx_color, float &dx_opacity ) +{ + dx_color[0] = osg_color[0]; + dx_color[1] = osg_color[1]; + dx_color[2] = osg_color[2]; + dx_opacity = osg_color[3] / 255.0; // alpha==1.0 -> opaque +} + +//---------------------------------------------------------------------------- + +int DXArrayWriter::WriteMapsYN( osg::GeoSet::IndexPointer *ip, int num_colors ) +{ + // Yes, if we have indexed colors and the number of colors mapped to + // is <= 256. + return ( ip && ip->valid() && num_colors <= 256 ); +} + +//---------------------------------------------------------------------------- + +void DXArrayWriter::WriteAttributes( const char *ref, const char *dep, + const char *attr ) +{ + if ( attr && attr[0] ) + fprintf( fp, "%s\n", attr ); + if ( ref ) + fprintf( fp, "attribute \"ref\" string \"%s\"\n", ref ); + if ( dep ) + fprintf( fp, "attribute \"dep\" string \"%s\"\n", dep ); +} + +//---------------------------------------------------------------------------- + +void DXArrayWriter::WriteFloatConstArray( + float f, int len, const char name[], + const char *ref, const char *dep, const char *attr ) +{ + fprintf( fp, "object \"%s\" class constantarray type float" + " rank 0 items %d data follows\n", + name, len ); + fprintf( fp, " %g\n", f ); + WriteAttributes( ref, dep, attr ); + fprintf( fp, "#\n\n" ); +} + +//---------------------------------------------------------------------------- + +void DXArrayWriter::WriteVec3ConstArray( + const osg::Vec3 &v, int len, const char name[], + const char *ref, const char *dep, const char *attr ) +{ + fprintf( fp, "object \"%s\" class constantarray type float" + " rank 1 shape 3 items %d data follows\n", + name, len ); + fprintf( fp, " %g %g %g\n", v[0], v[1], v[2] ); + WriteAttributes( ref, dep, attr ); + fprintf( fp, "#\n\n" ); +} + +//---------------------------------------------------------------------------- + +void DXArrayWriter::WriteFloatArray( + const float *array, osg::GeoSet::IndexPointer *ip, + int len, const char name[], + const char *ref, const char *dep, const char *attr ) + // If ip && ip.valid(), use it to index; else index array directly +{ + int index; + + fprintf( fp, "object \"%s\" class array type float" + " rank 0 items %d data follows\n", + name, len ); + for ( int i = 0; i < len; i++ ) { + index = ( ip && ip->valid() ) ? (*ip)[i] : i; + + fprintf( fp, " %g\n", array[index] ); + } + WriteAttributes( ref, dep, attr ); + fprintf( fp, "#\n\n" ); +} + +//---------------------------------------------------------------------------- + +void DXArrayWriter::WriteVec2Array( + const osg::Vec2 *array, osg::GeoSet::IndexPointer *ip, + int len, const char name[], + const char *ref, const char *dep, const char *attr ) + // If ip && ip.valid(), use it to index; else index array directly +{ + int index; + + fprintf( fp, "object \"%s\" class array type float" + " rank 1 shape 2 items %d data follows\n", + name, len ); + for ( int i = 0; i < len; i++ ) { + index = ( ip && ip->valid() ) ? (*ip)[i] : i; + + fprintf( fp, " %g %g\n", array[index][0], array[index][1] ); + } + WriteAttributes( ref, dep, attr ); + fprintf( fp, "#\n\n" ); +} + +//---------------------------------------------------------------------------- + +void DXArrayWriter::WriteVec3Array( + const osg::Vec3 *array, osg::GeoSet::IndexPointer *ip, + int len, const char name[], + const char *ref, const char *dep, const char *attr ) + // If ip && ip.valid(), use it to index; else index array directly +{ + int index; + + fprintf( fp, "object \"%s\" class array type float" + " rank 1 shape 3 items %d data follows\n", + name, len ); + for ( int i = 0; i < len; i++ ) { + index = ( ip && ip->valid() ) ? (*ip)[i] : i; + + fprintf( fp, " %g %g %g\n", + array[index][0], array[index][1], array[index][2] ); + } + WriteAttributes( ref, dep, attr ); + fprintf( fp, "#\n\n" ); +} + +//---------------------------------------------------------------------------- + +void DXArrayWriter::WriteVec4Array( + const osg::Vec4 *array, osg::GeoSet::IndexPointer *ip, + int len, const char name[], + const char *ref, const char *dep, const char *attr ) + // If ip && ip.valid(), use it to index; else index array directly +{ + int index; + + fprintf( fp, "object \"%s\" class array type float" + " rank 1 shape 4 items %d data follows\n", + name, len ); + for ( int i = 0; i < len; i++ ) { + index = ( ip && ip->valid() ) ? (*ip)[i] : i; + + fprintf( fp, " %g %g %g %g\n", + array[index][0], array[index][1], + array[index][2], array[index][3] ); + } + WriteAttributes( ref, dep, attr ); + fprintf( fp, "#\n\n" ); +} + +//---------------------------------------------------------------------------- + +void DXArrayWriter::WriteIndexArray( + const osg::GeoSet::IndexPointer &ip, int len, + int rank, int shape, int ubyte, + const char name[], + const char *ref, const char *dep, const char *attr ) + // If ip.null(), write a sequential index (0,1,2,3,...) +{ + const char *type_str = ( ubyte ? "unsigned byte" : "int" ); + char shape_str[40]; + + assert( rank == 0 || rank == 1 ); + if ( rank == 0 ) + shape_str[0] = '\0'; + else + sprintf( shape_str, "shape %d ", shape ); + fprintf( fp, "object \"%s\" class array type %s" + " rank %d %sitems %d data follows\n", + name, type_str, rank, shape_str, len/shape ); + + for ( int i = 0; i < len; i++ ) { + if ( i % shape == 0 ) + fprintf( fp, " " ); + { + if ( ip.null() ) fprintf( fp, " %d", i ); + else fprintf( fp, " %d", ip[i] ); + } + if ( (i+1) % shape == 0 ) + fprintf( fp, "\n" ); + } + WriteAttributes( ref, dep, attr ); + fprintf( fp, "#\n\n" ); +} + +//---------------------------------------------------------------------------- + +void DXArrayWriter::WriteUByte3Array( + const Vec3UB *array, osg::GeoSet::IndexPointer *ip, + int len, const char name[], + const char *ref, const char *dep, const char *attr ) + // If ip && ip.valid(), use it to index; else index array directly +{ + int index; + + fprintf( fp, "object \"%s\" class array type unsigned byte" + " rank 1 shape 3 items %d data follows\n", + name, len ); + for ( int i = 0; i < len; i++ ) { + index = ( ip && ip->valid() ) ? (*ip)[i] : i; + + fprintf( fp, " %d %d %d\n", + array[index][0], array[index][1], array[index][2] ); + } + WriteAttributes( ref, dep, attr ); + fprintf( fp, "#\n\n" ); +} + +//---------------------------------------------------------------------------- + +void DXArrayWriter::WriteColors( + const osg::Vec4 *colors, int num_colors, + osg::GeoSet::IndexPointer *ip, int num_cindices, + const char colors_name[], const char colormap_name[], + const char opacities_name[], const char opacitymap_name[], + int do_opacities, const char *dep, int &wrote_maps ) +{ + int do_maps = WriteMapsYN( ip, num_colors ); + int i; + + // If should write color/opacity maps... + if ( do_maps ) { + + // Separate colors and opacities + osg::Vec3 *colors2 = new osg::Vec3[256]; + float *opacities2 = new float [256]; + for ( i = 0; i < num_colors; i++ ) { + OSGColorToDX( colors[i], colors2[i], opacities2[i] ); + DeNanify( colors2[i], INVALID_COLOR ); + DeNanify( opacities2[i], INVALID_OPACITY ); + } + + for ( ; i < 256; i++ ) + colors2[i].set( 0,0,0 ), opacities2[i] = 0; + + // Write color/opacity maps + WriteVec3Array( colors2, 0, 256, colormap_name, 0, 0 ); + if ( do_opacities ) + WriteFloatArray( opacities2, 0, 256, opacitymap_name, 0, 0); + + // Write colors/opacities + WriteIndexArray( *ip, num_cindices, 0, 1, 1, colors_name, + "color map", dep ); + + if ( do_opacities ) + WriteIndexArray( *ip, num_cindices, 0, 1, 1, opacities_name, + "opacity map", dep ); + + delete [] colors2; + delete [] opacities2; + } + + // Else write direct colors/opacities... + else { + int len = ip && ip->valid() ? num_cindices : num_colors; + + // Separate colors and opacities + osg::Vec3 *colors2 = new osg::Vec3[ len ]; + float *opacities2 = new float [ len ]; + for ( i = 0; i < len; i++ ) { + int index = ip && ip->valid() ? (*ip)[i] : i; + OSGColorToDX( colors[ index ], colors2[i], opacities2[i] ); + DeNanify( colors2[i], INVALID_COLOR ); + DeNanify( opacities2[i], INVALID_OPACITY ); + } + + WriteVec3Array( colors2, 0, len, colors_name, 0, dep ); + if ( do_opacities ) + WriteFloatArray( opacities2, 0, len, opacities_name, 0, dep ); + + delete [] colors2; + delete [] opacities2; + } + + wrote_maps = do_maps; +} + +//---------------------------------------------------------------------------- + +void DXArrayWriter::WriteColors( + const Vec4UB colors[], int num_colors, + osg::GeoSet::IndexPointer *ip, int num_cindices, + const char colors_name[], const char colormap_name[], + const char opacities_name[], const char opacitymap_name[], + int do_opacities, const char *dep, int &wrote_maps ) +{ + int do_maps = WriteMapsYN( ip, num_colors ); + int i; + + // If should write color/opacity maps... + if ( do_maps ) { + + // Separate colors and opacities + Vec3UB *colors2 = new Vec3UB[256]; + float *opacities2 = new float [256]; + for ( i = 0; i < num_colors; i++ ) + OSGColorToDX( colors[i], colors2[i], opacities2[i] ); + + for ( ; i < 256; i++ ) { + colors2[i][0] = colors2[i][1] = colors2[i][2] = 0; + opacities2[i] = 0.0; + } + + // Write color/opacity maps + WriteUByte3Array( colors2, 0, 256, colormap_name, 0, 0 ); + if ( do_opacities ) + WriteFloatArray( opacities2, 0, 256, opacitymap_name, 0, 0); + + // Write colors/opacities + WriteIndexArray( *ip, num_cindices, 0, 1, 1, colors_name, + "color map", dep ); + + if ( do_opacities ) + WriteIndexArray( *ip, num_cindices, 0, 1, 1, opacities_name, + "opacity map", dep ); + + delete [] colors2; + delete [] opacities2; + } + + // Else write direct colors/opacities... + else { + int len = ip && ip->valid() ? num_cindices : num_colors; + + // Separate colors and opacities + Vec3UB *colors2 = new Vec3UB[ len ]; + float *opacities2 = new float [ len ]; + for ( i = 0; i < len; i++ ) { + int index = ip && ip->valid() ? (*ip)[i] : i; + OSGColorToDX( colors[ index ], colors2[i], opacities2[i] ); + } + + WriteUByte3Array( colors2, 0, len, colors_name, 0, dep ); + if ( do_opacities ) + WriteFloatArray( opacities2, 0, len, opacities_name, 0, dep ); + + delete [] colors2; + delete [] opacities2; + } + + wrote_maps = do_maps; +} + +//---------------------------------------------------------------------------- + +void DXArrayWriter::WritePerVertexNormals( + const osg::GeoSet &geoset, + const char name[], const char *attr ) + // throws 1 +{ + // FIXME: + // NOTE: Strictly speaking this routine is not correct, but is + // probably good enough for now. + // + // THE ISSUE: + // In OSG, PER_VERTEX normals, colors, and texture coordinates are + // stored per vertex "instance", while in DX normals, colors, and + // tcoords dep positions are stored per vertex "definition" (DX has no + // vertex instances to which normals, colors, and tcoords can be + // separately attached). + // + // (So e.g. for normals:) in OSG/Performer when coordinates (positions) + // are indexed, C coords and CI coord indices are defined to draw the + // primitives. When normals are defined per-vertex, there are N + // normals (and optionally NI normal indices). + // + // Note that CI == N (or NI, when normals are indexed). C != N (or NI). + // So, while we have one normal instance per point "instance", we may + // potentially have multiple normal instances mapped to the + // same point "definition". + // + // THE CORRECT SOLUTION: + // The right way to approach this is to pre-scan the Geodes whose + // Coords are indexed and which have either normals, colors, or tcoords + // bound to vertices, and replicate points (positions) which have + // multiple "different" normals, colors, or texture coordinates mapped + // To them. + // + // WHAT IS IMPLEMENTED BELOW: + // Below we take a short cut and map normals for vertex instances to + // vertex definitions. If a collision occurs, we just report it + // so someone will know it's a problem. + // + + int num_points = geoset.getNumCoords(); + int num_pindices = geoset.getNumCoordIndices(); + int num_normals = geoset.getNumNormals(); + int num_nindices = geoset.getNumNormalIndices(); + //const osg::Vec3 *points = geoset.getCoords(); + const osg::Vec3 *normals = geoset.getNormals(); + const osg::GeoSet::IndexPointer &pindices = geoset.getCoordIndices(); + const osg::GeoSet::IndexPointer &nindices = geoset.getNormalIndices(); + + // Create a list of normals per vertex "definition" + osg::Vec3 *pt_normals = new osg::Vec3[ num_points ]; + int *set = new int[ num_points ]; + + try { + memset( set, '\0', num_points * sizeof(int) ); + + int num_pt_instances = num_pindices ? num_pindices : num_points; + int num_norm_instances = num_nindices ? num_nindices : num_normals; + if ( num_pt_instances != num_norm_instances ) { + msg_bin->Add( "ERROR: Incorrect number of normals found\n" ); + throw 1; + } + + for ( int i = 0; i < num_pt_instances; i++ ) + { + int pindex = pindices.valid() ? pindices[i] : i; + int nindex = nindices.valid() ? nindices[i] : i; + osg::Vec3 normal = normals[nindex]; + + DeNanify( normal, INVALID_NORMAL ); + + if ( set[pindex] && !( pt_normals[pindex] == normal ) ) { + msg_bin->Add("ERROR: Vertex normal aliasing!!! Ask somebody to expand\n" + " the implementation of DXArrayWriter::" + "WritePerVertexNormals().\n" ); + //printf( "%d: %g %g %g\n", + // nindex, normal[0], normal[1], normal[2] ); + //printf( " %g %g %g\n", + // pt_normals[pindex][0], + // pt_normals[pindex][1], + // pt_normals[pindex][2] ); + } + set[pindex] = 1; + pt_normals[pindex] = normal; + } + + // Now write the normals + WriteVec3Array( pt_normals, 0, num_points, name, 0, "positions" ); + } + catch (...) { + delete [] set; + delete [] pt_normals; + throw; + } + delete [] set; + delete [] pt_normals; +} + +//---------------------------------------------------------------------------- + +void DXArrayWriter::WritePerVertexColors( + const osg::GeoSet &geoset, + const char colors_name[], + const char colormap_name[], + const char opacities_name[], + const char opacitymap_name[], + int do_opacities, + int &wrote_maps, + const char *attr ) + // throws 1 +{ + // FIXME: + // NOTE: Strictly speaking this routine is not correct, but is + // probably good enough for now. + // + // THE ISSUE: + // (See the opening comment in DXArrayWriter::WritePerVertexNormals. The + // same applies here, namely that there may be multiple colors + // mapping to the same point definiton.) + + int num_points = geoset.getNumCoords(); + int num_pindices = geoset.getNumCoordIndices(); + int num_colors = geoset.getNumColors(); + int num_cindices = geoset.getNumColorIndices(); + //const osg::Vec3 *points = geoset.getCoords(); + const osg::Vec4 *colors = geoset.getColors(); + const osg::GeoSet::IndexPointer &pindices = geoset.getCoordIndices(); + const osg::GeoSet::IndexPointer &cindices = geoset.getColorIndices(); + + // Create a list of color indices per vertex "definition" + osg::uint *pt_cindices = new osg::uint[ num_points ]; + int *set = new int [ num_points ]; + + try { + memset( set, '\0', num_points * sizeof(int) ); + + osg::uint num_pt_instances = num_pindices ? num_pindices : num_points; + osg::uint num_color_instances = num_cindices ? num_cindices : num_colors; + if ( num_pt_instances != num_color_instances ) { + msg_bin->Add( "ERROR: Incorrect number of colors found\n" ); + throw 1; + } + + for ( osg::uint i = 0; i < num_pt_instances; i++ ) + { + osg::uint pindex = pindices.valid() ? pindices[i] : i; + osg::uint cindex = cindices.valid() ? cindices[i] : i; + + if ( set[pindex] && ( pt_cindices[pindex] != cindex ) ) + msg_bin->Add( "ERROR: Vertex color aliasing!!! Ask somebody to expand\n" + " the implementation of DXArrayWriter::" + "WritePerVertexColors()." ); + + set[pindex] = 1; + pt_cindices[pindex] = cindex; + } + + // Cook an IndexPointer to write colors/opacities via std methods + osg::GeoSet::IndexPointer ip; + ip.set( num_points, pt_cindices ); + + // Write colors (and opacities, and possibly associated maps) + WriteColors( colors, num_colors, &ip, num_points, + colors_name, colormap_name, opacities_name, opacitymap_name, + do_opacities, "positions", wrote_maps ); + + } + catch (...) { + delete [] set; + delete [] pt_cindices; + throw; + } + delete [] set; + delete [] pt_cindices; +} + +//---------------------------------------------------------------------------- + +void DXArrayWriter::WritePerVertexTCoords( + const osg::GeoSet &geoset, + const char name[], const char *attr ) + // throws 1 +{ + // FIXME: + // NOTE: Strictly speaking this routine is not correct, but is + // probably good enough for now. + // + // THE ISSUE: + // (See the opening comment in DXArrayWriter::WritePerVertexNormals. The + // same applies here, namely that there may be multiple tcoords + // mapping to the same point definiton.) + + int num_points = geoset.getNumCoords(); + int num_pindices = geoset.getNumCoordIndices(); + int num_tcoords = geoset.getNumTextureCoords(); + int num_tindices = geoset.getNumTextureIndices(); + //const osg::Vec3 *points = geoset.getCoords(); + const osg::Vec2 *tcoords = geoset.getTextureCoords(); + const osg::GeoSet::IndexPointer &pindices = geoset.getCoordIndices(); + const osg::GeoSet::IndexPointer &tindices = geoset.getTextureIndices(); + + // Create a list of tcoords per vertex "definition" + osg::Vec2 *pt_tcoords = new osg::Vec2[ num_points ]; + int *set = new int[ num_points ]; + + try { + memset( set, '\0', num_points * sizeof(int) ); + + int num_pt_instances = num_pindices ? num_pindices : num_points; + int num_tcoord_instances = num_tindices ? num_tindices : num_tcoords; + if ( num_pt_instances != num_tcoord_instances ) { + msg_bin->Add( "ERROR: Incorrect number of texture coordinates found\n" ); + throw 1; + } + + for ( int i = 0; i < num_pt_instances; i++ ) + { + int pindex = pindices.valid() ? pindices[i] : i; + int tindex = tindices.valid() ? tindices[i] : i; + + if ( set[pindex] && !( pt_tcoords[pindex] == tcoords[tindex] ) ) + msg_bin->Add( "ERROR: Vertex texture coordinate aliasing!!!\n" + " Ask somebody to expand the implementation of\n" + " DXArrayWriter::WritePerVertexTcoords().\n" ); + + set[pindex] = 1; + pt_tcoords[pindex] = tcoords[tindex]; + } + + // Now write the tcoords + WriteVec2Array( pt_tcoords, 0, num_points, name, 0, "positions" ); + + } + catch (...) { + delete [] set; + delete [] pt_tcoords; + throw; + } + delete [] set; + delete [] pt_tcoords; +} + +//---------------------------------------------------------------------------- + +inline float MAX( float x, float y ) + { return x > y ? x : y; } + +inline float MAX3( float x, float y, float z ) + { return MAX( MAX( x, y ), z ); } + +inline float MaxVecXYZ( const osg::Vec4 &v ) + { return MAX3( v[0], v[1], v[2] ); } + +inline float Luminance( const osg::Vec4 &v ) + { return 0.2122*v[0] + 0.7013*v[1] * 0.0865*v[2]; } + +inline char *Vec4AttributeString( char buf[], + char name[], + int front_and_back, + osg::Vec4 &front, + osg::Vec4 &back ) +{ + if ( front_and_back ) + sprintf( buf, "attribute \"osg %s\" string \"%g %g %g %g\"\n", + name, front[0], front[1], front[2], front[3] ); + else + sprintf( buf, "attribute \"osg front %s\" string \"%g %g %g %g\"\n" + "attribute \"osg back %s\" string \"%g %g %g %g\"\n", + name, front[0], front[1], front[2], front[3], + name, back[0], back[1], back[2], back[3] ); + return buf; +} + + +std::string DXWriter::BuildStateSetAttributes( MyStateSet &sset, + int diffuse_is_color, + int diffuse_is_opacity ) +{ + std::string str; + char buf[160]; + + // Material + if ( sset.HasAttribute( osg::StateAttribute::MATERIAL ) ) { + + // DX Lighting Model: + // I = KaAC+KdLC(n*l)+KsL(n*h)^shiny + // OpenGL: + // I = Ke+KaA+sum(stuff*KdA(n*l)+KsLs(n*h)^shiny + // NOTES: + // 1. DX specular doesn't depend on object color + // 2. DX doesn't have separate RGB color fields for specular, diffuse, + // ambient, emission + // 3. DX's material attributes are all scalars + // 4. DX lights don't separate specular and diffuse + // etc. + // Notice that the lighting models differ enough that it doesn't make + // much sense to set the DX material attributes (they're all scalars). + // So we'll just attach what the original attributes were to + // non-reserved attribute names and let the user fiddle with Shade() + // to set "shininess", "specular", "diffuse", "ambient" attributes to + // get the look they want. + + if ( diffuse_is_color ) { + sprintf( buf, "attribute \"diffuse is colors component\" number 1\n" ); + str += buf; + } + + if ( diffuse_is_opacity ) { + sprintf( buf, "attribute \"diffuse is opacities component\" number 1\n" ); + str += buf; + } + + if ( sset.shininess_f_and_b ) + sprintf( buf, "attribute \"osg shininess\" number %g\n", + sset.shininess_f ); + else + sprintf( buf, "attribute \"osg front shininess\" number %g\n" + "attribute \"osg back shininess\" number %g\n", + sset.shininess_f, sset.shininess_b ); + str += buf; + + str += Vec4AttributeString( buf, "emission", sset.emission_f_and_b, + sset.emission_f, sset.emission_b ); + str += Vec4AttributeString( buf, "specular", sset.specular_f_and_b, + sset.specular_f, sset.specular_b ); + str += Vec4AttributeString( buf, "diffuse", sset.diffuse_f_and_b, + sset.diffuse_f, sset.diffuse_b ); + str += Vec4AttributeString( buf, "ambient", sset.ambient_f_and_b, + sset.ambient_f, sset.ambient_b ); + + // NOTE: Didn't write "colormode" + } + + // Texture + if ( sset.HasAttribute( osg::StateAttribute::TEXTURE_0 ) ) { + sprintf( buf, "attribute \"texture wrap s\" string \"%s\"\n", + sset.wrap_s == GL_CLAMP ? "clamp" : "repeat" ); + str += buf; + sprintf( buf, "attribute \"texture wrap t\" string \"%s\"\n", + sset.wrap_t == GL_CLAMP ? "clamp" : "repeat" ); + str += buf; + + static struct + { GLenum val; char *str; } + filter_to_str[] = { + { GL_NEAREST , "nearest" }, + { GL_LINEAR , "linear" }, + { GL_NEAREST_MIPMAP_NEAREST, "nearest_mipmap_nearest" }, + { GL_NEAREST_MIPMAP_LINEAR , "nearest_mipmap_linear" }, + { GL_LINEAR_MIPMAP_NEAREST , "linear_mipmap_nearest" }, + { GL_LINEAR_MIPMAP_LINEAR , "linear_mipmap_linear" } }; + int i; + for ( i = 0; i < ARRAY_LEN(filter_to_str); i++ ) + if ( filter_to_str[i].val == sset.min_filter ) + break; + if ( i >= ARRAY_LEN(filter_to_str) ) + msg_bin.Add( "WARNING: Bad texture min filter: %d\n", sset.min_filter ); + else { + sprintf( buf, "attribute \"texture min filter\" string \"%s\"\n", + filter_to_str[i].str ); + str += buf; + } + + for ( i = 0; i < ARRAY_LEN(filter_to_str); i++ ) + if ( filter_to_str[i].val == sset.mag_filter ) + break; + if ( i >= ARRAY_LEN(filter_to_str) ) + msg_bin.Add( "WARNING: Bad texture mag filter: %d\n", sset.mag_filter ); + else { + sprintf( buf, "attribute \"texture mag filter\" string \"%s\"\n", + filter_to_str[i].str ); + str += buf; + } + } + + // TexEnv + if ( sset.HasAttribute( osg::StateAttribute::TEXENV ) ) { + char *func = 0; + switch ( sset.texture_func ) { + case osg::TexEnv::DECAL : func = "decal" ; break; + case osg::TexEnv::MODULATE : func = "modulate"; break; + case osg::TexEnv::BLEND : func = "blend" ; break; + case osg::TexEnv::REPLACE : func = "replace" ; break; + default: + msg_bin.Add( "WARNING: Bad texture function: %d\n", sset.texture_func ); + } + if ( func ) { + sprintf( buf, "attribute \"texture function\" string \"%s\"\n", func ); + str += buf; + } + } + + // CullFace + if ( sset.HasAttribute( osg::StateAttribute::CULLFACE ) ) { + sprintf( buf, "attribute \"cull face\" string \"%s\"\n", + sset.cullface_mode == osg::CullFace::FRONT ? "front" : + sset.cullface_mode == osg::CullFace::BACK ? "back" : + "front and back" ); + str += buf; + } + else { + sprintf( buf, "attribute \"cull face\" string \"off\"\n" ); + str += buf; + } + + return str; +} + +//---------------------------------------------------------------------------- + +std::string DXWriter::WriteImage( const osg::Image &image ) + // Ok, so this writes a field and not just an array. Clean this up later... + // throws 1 +{ + // If this image has already been written, don't write it again + if ( texture_mgr.IsRegistered( &image ) ) + return texture_mgr.Lookup( &image ); + + // Get the path to the image file, and build a uniquely named DX field + const std::string &path = image.getFileName(); + const char *base = strrchr( path.c_str(), '/' ); + if ( base ) + base++; + else + base = path.c_str(); + std::string basename( base ); + DXField field( name_mgr, &basename ); + std::string name = field.GetName(); + + // Write positions + std::string pos_name = name_mgr.GetUnique( name + " image positions" ); + fprintf( fp, "object \"%s\" class gridpositions counts %d %d\n" + " origin 0 0\n" + " delta 0 1\n" + " delta 1 0\n" + "attribute \"dep\" string \"positions\"\n" + "#\n", + pos_name.c_str(), image.t(), image.s() ); + field.AddComponent( "positions", pos_name.c_str() ); + + // Write connections + std::string conn_name = name_mgr.GetUnique( name + " image connections" ); + fprintf( fp, "object \"%s\" class gridconnections counts %d %d\n" + "attribute \"element type\" string \"quads\"\n" + "attribute \"dep\" string \"connections\"\n" + "attribute \"ref\" string \"positions\"\n" + "#\n", + conn_name.c_str(), image.t(), image.s() ); + field.AddComponent( "connections", conn_name.c_str() ); + + // Generate colors + unsigned int pixel_fmt = image.pixelFormat(); // A user-friendly int_fmt + unsigned int data_type = image.dataType(); // Sample data type + const unsigned char *data = image.data(); + int r_dim = image.r(); + + // FIXME: Support other image formats + if ( r_dim != 1 ) { + msg_bin.Add( "ERROR: Currently only 2D textures are supported.\n" ); + throw 1; + } + if ( data_type != GL_UNSIGNED_BYTE ) { + msg_bin.Add( "ERROR: Currently only ubyte textures are supported.\n" ); + throw 1; + } + + switch ( pixel_fmt ) { + case GL_RGB : + case GL_RGBA : + case GL_LUMINANCE : + case GL_LUMINANCE_ALPHA : + break; + default : + msg_bin.Add( "ERROR: Currently only RGB, RGBA, LUMINANCE, and \n" + " LUMINANCE_ALPHA textures are supported. Found %d.\n", + pixel_fmt ); + throw 1; + } + + unsigned num_pixels = image.s() * image.t(); + osg::ubyte opaque = osg::ubyte( ALPHA_OPAQUE * 255.0 ); + Vec4UB *colors = new Vec4UB[ num_pixels ]; + unsigned i; + + if ( pixel_fmt == GL_RGB || pixel_fmt == GL_RGBA ) + for ( i = 0; i < num_pixels; i++, data += (pixel_fmt==GL_RGB?3:4)) + colors[i][0] = data[0], + colors[i][1] = data[1], + colors[i][2] = data[2], + colors[i][3] = ( pixel_fmt == GL_RGB ? opaque : data[3] ); + else + for ( i = 0; i < num_pixels; + i++, data += (pixel_fmt==GL_LUMINANCE?1:2) ) + colors[i][0] = colors[i][1] = colors[i][2] = data[0], + colors[i][3] = ( pixel_fmt == GL_LUMINANCE ? opaque : data[1] ); + + // Write colors + int found_transparent = 0; + for ( unsigned i = 0; i < num_pixels && !found_transparent; i++ ) + found_transparent = (colors[i][3] != opaque); + + int do_opacities = ((pixel_fmt == GL_RGBA || + pixel_fmt == GL_LUMINANCE_ALPHA ) && + found_transparent); + + // NOTE: DX used to only support UNSIGNED_BYTE GL_RGB textures. Added + // Jan 2002: Added support to DX for RGBA textures w/ depth sorting. + // So we always write RGBA textures as image texture maps with opacities. + + std::string colors_name = name_mgr.GetUnique( name + " image colors"); + std::string colormap_name = name_mgr.GetUnique( name + " color map" ); + std::string opacities_name = name_mgr.GetUnique( name + " opacities" ); + std::string opacitymap_name = name_mgr.GetUnique( name + " opacity map" ); + int wrote_maps; + w.WriteColors( colors, num_pixels, 0, 0, + colors_name.c_str(), colormap_name.c_str(), + opacities_name.c_str(), opacitymap_name.c_str(), + do_opacities, "positions", wrote_maps ); + delete colors; + + field.AddComponent( "colors", colors_name.c_str() ); + if ( do_opacities ) + field.AddComponent( "opacities", opacities_name.c_str() ); + if ( wrote_maps ) + { + field.AddComponent( "color map", colormap_name.c_str() ); + if ( do_opacities ) + field.AddComponent( "opacity map", opacitymap_name.c_str() ); + } + + // Finally, write the image field + field.Write( fp, 0 ); + + // And keep track of this image to attach to future referring fields + texture_mgr.Register( &image, name ); + return name; +} + +//---------------------------------------------------------------------------- + +// FIXME: field_name never referenced +void DXWriter::WritePolylineConnections( const std::string &field_name, + osg::GeoSet &geoset, + DXField &field ) + // Writes the "edges" and "polylines" field components for LINE_STRIPs, + // FLAT_LINE_STRIPs, and LINE_LOOPs. +{ + assert( geoset.getPrimType() == osg::GeoSet::LINE_STRIP || + geoset.getPrimType() == osg::GeoSet::FLAT_LINE_STRIP || + geoset.getPrimType() == osg::GeoSet::LINE_LOOP ); + + int num_prims = geoset.getNumPrims(); + int num_points = geoset.getNumCoords(); // FIXME: Never referenced + int num_pindices = geoset.getNumCoordIndices(); // FIXME: Never referenced + const int *prim_lengths = geoset.getPrimLengths(); + int line_loops = geoset.getPrimType() == osg::GeoSet::LINE_LOOP; + int i,j; + const osg::GeoSet::IndexPointer &pindices = geoset.getCoordIndices(); + + // ---- Write "edges" components ---- + std::string edges_name = name_mgr.GetUnique( field.GetName() + " edges" ); + + // Count number of points in all polylines, and set up an index pointer + int count = 0; + for ( i = 0; i < num_prims; i++ ) + count += ( line_loops ? prim_lengths[i]+1 : prim_lengths[i] ); + osg::uint *indices = new osg::uint[ count ]; + osg::GeoSet::IndexPointer ip; + ip.set( count, indices ); + + // If no coordinate indices, then position indices are implicit; else explicit + int idx = 0; + for ( i = 0; i < num_prims; i++ ) { + int start_idx = idx; + for ( j = 0; j < prim_lengths[i]; j++, idx++ ) + indices[ idx ] = pindices.valid() ? pindices[ idx ] : idx; + if ( line_loops ) { + indices[ idx ] = pindices.valid() ? pindices[start_idx] : start_idx; + idx++; + } + } + + // Write component + w.WriteIndexArray( ip, count, 0, 1, 0, edges_name.c_str(), "positions", 0 ); + field.AddComponent( "edges", edges_name.c_str() ); + + // ---- Write "polylines" components ---- + std::string polylines_name = name_mgr.GetUnique( field.GetName() + + " polylines" ); + + idx = 0; + for ( i = 0; i < num_prims; i++ ) { + indices[i] = idx; + idx += ( line_loops ? prim_lengths[i]+1 : prim_lengths[i] ); + } + w.WriteIndexArray( ip, num_prims, 0, 1, 0, polylines_name.c_str(), + "edges", 0 ); + field.AddComponent( "polylines", polylines_name.c_str() ); + + delete indices; +} + +//---------------------------------------------------------------------------- + +std::string DXWriter::WriteGeoSetField( const std::string &field_name, + osg::GeoSet &geoset, + MyStateSet &sset ) + // throw 1 + + // Writes POINTS, LINES, TRIANGLES, LINE_STRIPs, FLAT_LINE_STRIPs, and + // LINE_LOOPs. + // + // QUADS NOTE: This routine almost supports QUADS. However, OSG/OpenGL + // quad vertices are ordered as a walk around the boundary of the polygon + // whereas DX swaps the last two vertices. So to support quads, + // we'd need to swap the 3rd and 4th coords (or coord indices) in + // each primitive, and for any normals/colors/tcoords, we'd also need + // to swap the 3rd and 4th data value (or data index) in each primitive. + // FIXME: For now, we'll just triangulate QUADS into TRIANGLES, and + // write those (see DXWriter::WriteGeoSet). Later consider swapping and + // writing quads connections if it doesn't obfuscate this routine too + // badly. +{ + // No QUADS -- see above comment + assert( geoset.getPrimType() != osg::GeoSet::QUADS ); + + geoset.computeNumVerts(); // Update # coords/# normals from index arrays + + int num_prims = geoset.getNumPrims(); + int num_points = geoset.getNumCoords(); + int num_pindices = geoset.getNumCoordIndices(); + int num_colors = geoset.getNumColors(); + int num_cindices = geoset.getNumColorIndices(); + int num_normals = geoset.getNumNormals(); + //int num_nindices = geoset.getNumNormalIndices(); + int num_tcoords = geoset.getNumTextureCoords(); + osg::GeoSet::BindingType cbinding = geoset.getColorBinding(); + osg::GeoSet::BindingType nbinding = geoset.getNormalBinding(); + osg::GeoSet::BindingType tbinding = geoset.getTextureBinding(); + int i, prim_length, polylines = 0; + char *connection_type = 0; + + // Give the file pointer to the DX array writer + w.SetFP( fp ); + w.SetMsgBin( &msg_bin ); + msg_bin.Clear(); + + // If texturing is on, write the texture as a DX image field, and give + // us back the name of field + int has_uv = num_tcoords > 0 && tbinding == osg::GeoSet::BIND_PERVERTEX; + int has_texture = sset.HasAttribute( osg::StateAttribute::TEXTURE_0 ); + int has_texgen = sset.HasAttribute( osg::StateAttribute::TEXGEN ); + + std::string texture_name; + if ( has_texture && has_uv ) + try { + texture_name = WriteImage( *sset.image ); + } catch (...) { throw; } + else if ( has_texture && !has_uv && has_texgen ) { + static int been_here = 0; + if ( !been_here ) { + msg_bin.Add( "WARNING: " + "Texture/uv not written; TEXGEN not supported yet\n" ); + been_here = 1; + } + } + + // Create a new DX field with a unique name + DXField field( name_mgr, &field_name ); + std::string name = field.GetName(); + + // Determine "element type" and primitive length of connections component + switch ( geoset.getPrimType() ) { + case osg::GeoSet::POINTS : + prim_length = 1, connection_type = 0 ; break; + case osg::GeoSet::LINES : + prim_length = 2, connection_type = "lines" ; break; + case osg::GeoSet::TRIANGLES : + prim_length = 3, connection_type = "triangles"; break; + case osg::GeoSet::QUADS : + prim_length = 4, connection_type = "quads" ; break; + case osg::GeoSet::LINE_STRIP : + case osg::GeoSet::FLAT_LINE_STRIP : + case osg::GeoSet::LINE_LOOP : + prim_length = 0, connection_type = "polylines"; + polylines = 1; + break; + + default: + msg_bin.Add( "ERROR: GeoSet passed to WriteGeoSetField is wrong type\n" ); + throw 1; + } + + // Ensure we "don't" have interleaved GeoSet data + // FIXME: Add support for this + if (( num_points == 0 ) && geoset.getInterleavedArray() != 0 ) { + msg_bin.Add( "ERROR: Interleaved Array GeoSets not yet supported\n" ); + throw 1; + } + + // Write "positions" component + std::string pos_name = name_mgr.GetUnique( name + " points" ); + w.WriteVec3Array( geoset.getCoords(), 0, num_points, pos_name.c_str(), + 0, "positions" ); + field.AddComponent( "positions", pos_name.c_str() ); + + // Write "connections" component (lines, triangles, or quads); pts have none + char *conn_dep_name = 0; + + if ( prim_length == 1 ) + // For points, there are no connections, so don't make colors or normals + // dep on them in the OVERALL or PERPRIM cases + conn_dep_name = "positions"; + else if ( polylines ) { + WritePolylineConnections( field_name, geoset, field ); + conn_dep_name = "polylines"; + } + else { + std::string conn_name = name_mgr.GetUnique( name + " connections" ); + int len = num_pindices ? num_pindices : num_points; + char attr[80]; + sprintf( attr, "attribute \"element type\" string \"%s\"", connection_type); + + w.WriteIndexArray( geoset.getCoordIndices(), len, 1, prim_length, 0, + conn_name.c_str(), "positions", "connections", attr ); + field.AddComponent( "connections", conn_name.c_str() ); + conn_dep_name = "connections"; + } + + // Write "normals" component + if ( num_normals > 0 ) { + std::string normals_name = name_mgr.GetUnique( name + " normals" ); + + // DX doesn't support a "normal map", so expand to Vec3 if normals indexed + // Also see notes in WritePerVertexNormals. + int index; + osg::GeoSet::IndexPointer *ip; + switch( nbinding ) { + + case osg::GeoSet::BIND_OVERALL : + ip = &geoset.getNormalIndices(); + index = ip->valid() ? (*ip)[0] : 0; + w.WriteVec3ConstArray( geoset.getNormals()[index], num_prims, + normals_name.c_str(), 0, conn_dep_name ); + break; + + case osg::GeoSet::BIND_PERPRIM : + w.WriteVec3Array( geoset.getNormals(), &geoset.getNormalIndices(), + num_prims, normals_name.c_str(), + 0, conn_dep_name ); + break; + + case osg::GeoSet::BIND_PERVERTEX : + try { + w.WritePerVertexNormals( geoset, normals_name.c_str() ); + } catch (...) { throw; } + break; + + case osg::GeoSet::BIND_OFF: + case osg::GeoSet::BIND_DEFAULT: + // Nonsensical cases + break; + } + field.AddComponent( "normals", normals_name.c_str() ); + } + + // Write "colors" (and possibly "opacities") + // If indexed colors and numcolors <= 256, create "color map"/"opacity map" + std::string colors_name = name_mgr.GetUnique( name + " colors" ); + std::string colormap_name = name_mgr.GetUnique( name + " color map" ); + std::string opacities_name = name_mgr.GetUnique( name + " opacities" ); + std::string opacitymap_name = name_mgr.GetUnique( name + " opacity map" ); + int has_maps = 0; + int diffuse_is_color = 0, diffuse_is_opacity = 0; + + // 4 cases: Choose/write colors/opacities in order of preference from: + // colors, diffuse material, default color. + // If none specified, write no colors/opacities components. + + if ( num_colors > 0 ) { + + osg::Vec4 *colors = geoset.getColors(); + osg::GeoSet::IndexPointer &ip = geoset.getColorIndices(); + int index, do_opacities, wrote_maps; + + // See if any non-opaque colors used + for ( i = 0; i < num_colors; i++ ) + if ( colors[i][3] != ALPHA_OPAQUE ) + break; + do_opacities = ( i < num_colors ); + + osg::Vec3 color; + float opacity; + + switch( cbinding ) { + + case osg::GeoSet::BIND_OVERALL : + index = ip.valid() ? ip[0] : 0; + w.OSGColorToDX( colors[index], color, opacity ); + w.WriteVec3ConstArray( color, num_prims, colors_name.c_str(), + 0, conn_dep_name ); + if ( do_opacities ) + w.WriteFloatConstArray( opacity, num_prims, opacities_name.c_str(), + 0, conn_dep_name ); + wrote_maps = 0; + break; + + case osg::GeoSet::BIND_PERPRIM : + w.WriteColors( colors, num_colors, + &geoset.getColorIndices(), num_cindices, + colors_name.c_str(), colormap_name.c_str(), + opacities_name.c_str(), opacitymap_name.c_str(), + do_opacities, conn_dep_name, wrote_maps ); + break; + + case osg::GeoSet::BIND_PERVERTEX : + try { + w.WritePerVertexColors( geoset, + colors_name.c_str() , colormap_name.c_str(), + opacities_name.c_str(), opacitymap_name.c_str(), + do_opacities, wrote_maps ); + } catch (...) { throw; } + break; + case osg::GeoSet::BIND_OFF: + case osg::GeoSet::BIND_DEFAULT: + // Nonsensical cases + break; + } + + // Register added components + field.AddComponent( "colors", colors_name.c_str() ); + if ( do_opacities ) + field.AddComponent( "opacities", opacities_name.c_str() ); + if ( wrote_maps ) + { + field.AddComponent( "color map", colormap_name.c_str() ); + if ( do_opacities ) + field.AddComponent( "opacity map", opacitymap_name.c_str() ); + } + } + + // Else no explicit colors specific. Fall back on diffuse Material + else if ( sset.HasAttribute( osg::StateAttribute::MATERIAL ) ) { + osg::Vec3 color; + float opacity; + w.OSGColorToDX( sset.diffuse_f, color, opacity ); + int do_opacities = opacity != ALPHA_OPAQUE; + + // Same as the BIND_OVERALL case above, except can have front/back colors + if ( do_opacities ) { + w.WriteFloatConstArray( opacity, num_prims, opacities_name.c_str(), + 0, conn_dep_name ); + field.AddComponent( "opacities", opacities_name.c_str() ); + } + + std::string front_colors_name = name_mgr.GetUnique( name +" front colors" ); + std::string back_colors_name = name_mgr.GetUnique( name +" back colors" ); + + if ( sset.diffuse_f_and_b ) { + w.WriteVec3ConstArray( color, num_prims, colors_name.c_str(), + 0, conn_dep_name ); + field.AddComponent( "colors", colors_name.c_str() ); + } + else { + w.WriteVec3ConstArray( color, num_prims, front_colors_name.c_str(), + 0, conn_dep_name ); + field.AddComponent( "colors", front_colors_name.c_str() ); + w.OSGColorToDX( sset.diffuse_b, color, opacity ); + w.WriteVec3ConstArray( color, num_prims, back_colors_name.c_str(), + 0, conn_dep_name ); + field.AddComponent( "colors", back_colors_name.c_str() ); + } + diffuse_is_color = 1; + diffuse_is_opacity = do_opacities; + } + + // No explicit colors or diffuse Material color. Give user provided default. + else if ( parms.set_default_color ) { + osg::Vec3 color; + float opacity; + w.OSGColorToDX( parms.default_color, color, opacity ); + int do_opacities = opacity != ALPHA_OPAQUE; + + // Same as the BIND_OVERALL case above, except can have front/back colors + w.WriteVec3ConstArray( color, num_prims, colors_name.c_str(), + 0, conn_dep_name ); + field.AddComponent( "colors", colors_name.c_str() ); + + if ( do_opacities ) { + w.WriteFloatConstArray( opacity, num_prims, opacities_name.c_str(), + 0, conn_dep_name ); + field.AddComponent( "opacities", opacities_name.c_str() ); + } + + diffuse_is_color = 1; + diffuse_is_opacity = do_opacities; + } + + // Write "uv" texture coordinates + std::string uv_name = name_mgr.GetUnique( name + " uv" ); + if ( num_tcoords > 0 && tbinding == osg::GeoSet::BIND_PERVERTEX ) { + try { + w.WritePerVertexTCoords( geoset, uv_name.c_str() ); + } catch (...) { throw; } + field.AddComponent( "uv", uv_name.c_str() ); + } + + // Generate field attributes + std::string attr( has_maps ? "attribute \"direct color map\" number 1\n" :""); + attr += BuildStateSetAttributes( sset, diffuse_is_color, diffuse_is_opacity ); + // If texturing is on, tack on a "texture" attribute with it's field name + if ( has_uv && has_texture ) { + char buf[160]; + //int size_ok = sset.image->s() >= 64 && sset.image->t() >= 64; + //char *attr_name = size_ok ? "texture" : "texture - too small"; + char *attr_name = "texture"; + + // FIXME: DX textures must be powers of two and >= 64 on a side. + // Now that we generate MIPmaps that's no longer true, and just remove + // the 64x64 min size check from DX. + // NOTE: FIXED in DX now that we mipmap everything + + //if ( !size_ok ) { + // // FIXME: When we generalize this, remove the static hack + // static int been_here = 0; + // if ( !been_here ) { + // msg_bin.Add( + // "WARNING: DX min texture size is 64x64. Renaming texture\n" + // " attribute to \"texture - too small\" for these" + // " small textures.\n" ); + // been_here = 1; + // } + //} + + sprintf( buf, "attribute \"%s\" value \"%s\"\n", + attr_name, texture_name.c_str() ); + attr += buf; + } + + if ( attr[ attr.length()-1 ] == '\n' ) + attr.assign( attr, 0, attr.length()-1 ); + + // Finally, write the field + field.Write( fp, attr.c_str() ); + return name; + + // FIXME: Need to handle "texture" image attribute, GeoState colors + // storing GeoState attributes on field object, etc. +} + +//---------------------------------------------------------------------------- + +void MyStateSet::Show( osg::StateSet &sset ) +{ + osg::StateSet::ModeList &mode_list = sset.getModeList(); + osg::StateSet::AttributeList &attr_list = sset.getAttributeList(); + //int rend_hint = sset.getRenderingHint(); + //bool use_rend_bin_details = sset.useRenderBinDetails(); + //int bin_num = sset.getBinNumber(); + //const std::string &bin_name = sset.getBinName(); + //osg::StateSet::RenderBinMode rendbin_mode = sset.getRenderBinMode(); + + // Iterate over all mode mappings (GLMode->GLModeValue) + for( osg::StateSet::ModeList::const_iterator mitr = mode_list.begin(); + mitr != mode_list.end(); + ++mitr ) + msg_bin.Add( " GLMode %d = GLValue %d\n", mitr->first, mitr->second ); + + // Iterate over all attribute mappings + // (Type -> pair< ref_ptr, OverrideValue >) + for( osg::StateSet::AttributeList::const_iterator aitr = attr_list.begin(); + aitr != attr_list.end(); + ++aitr ) + msg_bin.Add( " Attr Type %d (Attr Name \"%s\"), OverrideValue = %d\n", + aitr->first, aitr->second.first.get()->className(), + aitr->second.second ); + + // mgs_bin.Add( " Rendering Hint = %d\n", rend_hint ); + // mgs_bin.Add( " Use Rend Bin Details = %d\n", use_rend_bin_details ); + // mgs_bin.Add( " Bin Number = %d\n", bin_num ); + // mgs_bin.Add( " Bin Name = %s\n", bin_name.c_str() ); + // mgs_bin.Add( " Bin Mode = %d\n", rendbin_mode ); +} + +//---------------------------------------------------------------------------- + +void MyStateSet::Query( const osg::StateSet &sset ) +{ + const osg::StateAttribute *attr; + + //MyStateSet::Show( *drawable_state ); + + // FIXME: Ignore override flags for now + + // MATERIAL + attr = sset.getAttribute( osg::StateAttribute::MATERIAL ); + if ( attr ) { + AddAttribute( osg::StateAttribute::MATERIAL ); + + const osg::Material &mat = (const osg::Material &) (*attr); + colormode = mat.getColorMode(); // GL_COLOR_MATERIAL + + ambient_f = mat.getAmbient ( osg::Material::FRONT ); + ambient_b = mat.getAmbient ( osg::Material::BACK ); + diffuse_f = mat.getDiffuse ( osg::Material::FRONT ); + diffuse_b = mat.getDiffuse ( osg::Material::BACK ); + specular_f = mat.getSpecular ( osg::Material::FRONT ); + specular_b = mat.getSpecular ( osg::Material::BACK ); + emission_f = mat.getEmission ( osg::Material::FRONT ); + emission_b = mat.getEmission ( osg::Material::BACK ); + shininess_f = mat.getShininess( osg::Material::FRONT ); + shininess_b = mat.getShininess( osg::Material::BACK ); + + ambient_f_and_b = mat.getAmbientFrontAndBack (); + diffuse_f_and_b = mat.getDiffuseFrontAndBack (); + specular_f_and_b = mat.getSpecularFrontAndBack (); + emission_f_and_b = mat.getEmissionFrontAndBack (); + shininess_f_and_b = mat.getShininessFrontAndBack(); + } + + // TEXTURE / TEXTURE_0 + attr = sset.getAttribute( osg::StateAttribute::TEXTURE_0 ); + if ( attr && + ( sset.getMode( GL_TEXTURE_2D ) & osg::StateAttribute::ON )) { + + const osg::Texture &texture = (const osg::Texture &) (*attr); + + // NOTE: If OSG failed to load the texture, we'll get a NULL right here + image = texture.getImage(); + if ( image ) + AddAttribute( osg::StateAttribute::TEXTURE_0 ); + + // NOTE: DX limitations + // Traditional DX doesn't support any texture control besides specifying + // the texture ("texture" attribute) and the texture coordinates ("uv" + // component). DX was hard-coded to use these 2D texture settings: + // GL_TEXTURE_WRAP_S = GL_CLAMP + // GL_TEXTURE_WRAP_T = GL_CLAMP + // GL_TEXTURE_MIN_FILTER = GL_NEAREST + // GL_TEXTURE_MAG_FILTER = GL_NEAREST + // GL_TEXTURE_ENV_MODE = GL_MODULATE + // DX also registers a level 0 texture + // A DX patch I recently committed (1/02) creates mipmapped textures and + // adds the following DX textured field attributes: + // attribute "texture wrap s" string ["clamp"|"repeat"] + // attribute "texture wrap t" string ["clamp"|"repeat"] + // attribute "texture min filter" string ["nearest"|"linear"| + // "nearest_mipmap_nearest"|"nearest_mipmap_linear"| + // "linear_mipmap_nearest"|"linear_mipmap_linear"] + // attribute "texture mag filter" string ["nearest"|"linear"] + // attribute "texture function" string ["decal"|"replace"|"modulate" + // "blend"] + + wrap_s = texture.getWrap( osg::Texture::WRAP_S ); + wrap_t = texture.getWrap( osg::Texture::WRAP_T ); + min_filter = texture.getFilter( osg::Texture::MIN_FILTER ); + mag_filter = texture.getFilter( osg::Texture::MAG_FILTER ); + + if ( texture.getInternalFormatMode() != + osg::Texture::USE_IMAGE_DATA_FORMAT ) { + // FIXME: When we generalize this, remove the static hack + static int been_here = 0; + if ( !been_here ) { + msg_bin.Add( "WARNING: Only texture image data format supported.\n"); + been_here = 1; + } + } + + if ( texture.getSubloadMode() != osg::Texture::OFF ) { + // FIXME: When we generalize this, remove the static hack + static int been_here = 0; + if ( !been_here ) { + msg_bin.Add( "WARNING: Texture subloading not supported.\n" ); + been_here = 1; + } + } + } + + // TEXENV + attr = sset.getAttribute( osg::StateAttribute::TEXENV ); + if ( attr && + ( sset.getMode( GL_TEXTURE_2D ) & osg::StateAttribute::ON )) { + AddAttribute( osg::StateAttribute::TEXENV ); + + const osg::TexEnv &texenv = (const osg::TexEnv &) (*attr); + + texture_func = texenv.getMode(); + } + + // TEXGEN + attr = sset.getAttribute( osg::StateAttribute::TEXGEN ); + if ( attr && + ( sset.getMode( GL_TEXTURE_2D ) & osg::StateAttribute::ON )) { + + // FIXME: Just note that it is there for now. We don't actually support + // texture coordinate generation yet (i.e. generating the "uv" component + // for DX without explicit texture coordinates in the model) + AddAttribute( osg::StateAttribute::TEXGEN ); + } + + // CULLFACE + attr = sset.getAttribute( osg::StateAttribute::CULLFACE ); + if ( attr && + ( sset.getMode( GL_CULL_FACE ) & osg::StateAttribute::ON )) { + + AddAttribute( osg::StateAttribute::CULLFACE ); + const osg::CullFace &cullface = (const osg::CullFace &) (*attr); + cullface_mode = cullface.getMode(); + } + + // FIXME: Also consider supporting: + //exenv + // ALPHAFUNC, ANTIALIAS, COLORTABLE, FOG, FRONTFACE, + // LIGHTING, POINT, POLYGONMODE, POLYGONOFFSET, TEXGEN, TEXMAT, + // TEXTURE_1, TEXTURE_2, TEXTURE_3, TRANSPARENCY, + // STENCIL, COLORMASK, CLIPPLANE, CLIPPLANE_0, CLIPPLANE_1, + // CLIPPLANE_2, CLIPPLANE_3, CLIPPLANE_4, CLIPPLANE_5, DEPTH +} + +//---------------------------------------------------------------------------- + +void DXWriter::Open() +{ + if ( strcmp( parms.outfile, "-" ) == 0 ) + fp = stdout; + else + fp = fopen( parms.outfile, "wt" ); + + w.SetFP( fp ); + w.SetMsgBin( &msg_bin ); +} + +//---------------------------------------------------------------------------- + +void DXWriter::Close() +{ + if ( fp != stdout ) + fclose( fp ); + w.SetFP( NULL ); +} + +//---------------------------------------------------------------------------- + +std::string DXWriter::WriteGeoSet( osg::GeoSet &geoset, + const osg::StateSet &active_sset, + std::string &field_name ) + // throws 1 +{ + std::string dx_name; + MyStateSet my_sset(msg_bin); + + // Update the active state for this Drawable (GeoSet), if it has a StateSet + const osg::StateSet *sset; + StateSetCopy *new_sset = 0; + osg::StateSet *drawable_state = geoset.getStateSet(); + + if ( drawable_state ) { + new_sset = new StateSetCopy( active_sset ); + new_sset->merge( *drawable_state ); + sset = new_sset; + + // Collect unhandled modes/attributes in this geoset's stateset + CollectUnhandledModesAndAttrs( drawable_state ); + } + else + sset = &active_sset; + + my_sset.Query( *sset ); + delete new_sset; + + // Now write the GeoSet to a DX field + osg::GeoSet::PrimitiveType prim_type = geoset.getPrimType(); + osg::GeoSet *tri_geoset; + + switch( prim_type ) { + case osg::GeoSet::POINTS : + case osg::GeoSet::LINES : + case osg::GeoSet::TRIANGLES : + case osg::GeoSet::LINE_STRIP : + case osg::GeoSet::FLAT_LINE_STRIP : + case osg::GeoSet::LINE_LOOP : + + try { + dx_name = WriteGeoSetField( field_name, geoset, my_sset ); + } catch (...) { throw; } + break; + + case osg::GeoSet::QUADS : + case osg::GeoSet::TRIANGLE_STRIP : + case osg::GeoSet::FLAT_TRIANGLE_STRIP : + case osg::GeoSet::TRIANGLE_FAN : + case osg::GeoSet::FLAT_TRIANGLE_FAN : + case osg::GeoSet::QUAD_STRIP : + case osg::GeoSet::POLYGON : + + // For all of these cases, bust up the area primitive GeoSet into + // a TRIANGLES GeoSet, and then simply write that. DX doesn't + // support any of these primitive types except POLYGON and QUADS + // anyway, and its polygon support is a bit quirky (e.g. + // $DX_NESTED_LOOPS). + tri_geoset = TriangulateAreaGeoSet( geoset ); + try { + dx_name = WriteGeoSetField( field_name, *tri_geoset, my_sset ); + } catch (...) { + tri_geoset->unref(); + throw; + } + tri_geoset->unref(); + break; + + case osg::GeoSet::NO_TYPE : + // Nonsensical cases + break; + } + + // Return the name of the written field, or "" if not written + return dx_name; +} + +//---------------------------------------------------------------------------- + +std::string DXWriter::WriteGeode( osg::Geode &geode, + const osg::StateSet &active_sset ) + // throws 1 +{ + const std::string &name = geode.getName(); + std::string field_name; + DXGroup *group = 0; + std::string dx_name; + + // If there are multiple GeoSets in this Geode, then we will encase them + // in a DX group + if ( geode.getNumDrawables() > 1 ) + group = new DXGroup( name_mgr, &name ); + + // For all GeoSets in this Geode... + for ( int i = 0; i < geode.getNumDrawables(); i++ ) { + osg::GeoSet *geoset = dynamic_cast( geode.getDrawable(i) ); + + // If we have multiple GeoSets per Geode, we need to generate different + // DX field names for each + if ( geode.getNumDrawables() > 1 ) { + char prim_num_str[20]; + sprintf( prim_num_str, " %d", i+1 ); + field_name = name + prim_num_str; + } + else + field_name = name; + + try { + dx_name = WriteGeoSet( *geoset, active_sset, field_name ); + } catch (...) { throw; } + + if ( !dx_name.empty() && group ) + group->AddMember( dx_name.c_str() ); + } + + // If collecting fields in a Group, write the Group + if ( group ) { + // If no members were written successfully, don't write this group + // or include it in any parent groups + if ( group->GetNumMembers() == 0 ) + dx_name = ""; + else { + group->Write( fp ); + dx_name = group->GetName(); + } + delete group; + } + + return dx_name; +} + +//---------------------------------------------------------------------------- + +void DXWriter::CollectUnhandledModesAndAttrs( osg::StateSet *sset ) +{ + if ( !sset ) + return; + + // FIXME: For now we're collecting all of them, not just the + // unhandled ones + osg::StateSet::ModeList &modelist = sset->getModeList(); + osg::StateSet::AttributeList &attrlist = sset->getAttributeList(); + + // Loop through the mode settings + for( osg::StateSet::ModeList::const_iterator mitr = modelist.begin(); + mitr != modelist.end(); + ++mitr ) + unsupported_modes[ mitr->first ] = 1; + + // Loop through the attribute settings + for( osg::StateSet::AttributeList::const_iterator aitr = attrlist.begin(); + aitr != attrlist.end(); + ++aitr ) + unsupported_attrs[ aitr->first ] = 1; +} + +//---------------------------------------------------------------------------- + +void DXWriter::ReportUnhandledModesAndAttrs() +{ + char msg[1024]; + + msg_bin.Add( "\n" ); + + // Loop through the mode settings... + msg_bin.Add( "OpenGL Modes Encounted:\n " ); + msg[0] = '\0'; + for( DXWriter::ModeList::const_iterator mitr = unsupported_modes.begin(); + mitr != unsupported_modes.end(); + ++mitr ) { + const char *mode_str = GLModeToModeStr( mitr->first ); + if ( mode_str ) + snprintf( msg+strlen(msg), sizeof(msg)-strlen(msg), + " %s", mode_str ); + else + snprintf( msg+strlen(msg), sizeof(msg)-strlen(msg), + " %d", mitr->first ); + } + msg_bin.Add( msg ); + msg_bin.Add( "" ); + + // ...and the attribute settings + msg_bin.Add( "OpenSceneGraph Attributes Encountered:\n " ); + msg[0] = '\0'; + for( DXWriter::AttrList::const_iterator aitr = unsupported_attrs.begin(); + aitr != unsupported_attrs.end(); + ++aitr ) { + const char *attr_str = OSGAttrToAttrStr( aitr->first ); + if ( attr_str ) + snprintf( msg+strlen(msg), sizeof(msg)-strlen(msg), + " %s", attr_str ); + else + snprintf( msg+strlen(msg), sizeof(msg)-strlen(msg), + " %d", aitr->first ); + } + msg_bin.Add( msg ); + msg_bin.Add( "" ); +} + +//---------------------------------------------------------------------------- + +// NOTES ON TRAVERSAL: +// +// So what's up with the StateSetVisitor and StateSetActionVisitor +// pair? Well it's all to make sure that when the user's apply() +// method is called for a Node, we can just hand over the current state +// (attributes and modes) without the user having to go figure it out. +// +// So why are two passes needed? One to update the state. One to +// execute the user's apply method with that state. Basically, +// StateSetVisitor is the master traversal object, and we gosub into +// the StateSetActionVisitor just long enough to invoke the apply +// method stack for a Node, and then we gosub back out to continue +// traversal on the main StateSetVisitor. StateSetVisitor's handles +// keeping the active StateSet up-to-date. +// +// Here's how it works: traversal is invoked on a StateSetVisitor (a +// NodeVisitor). It's constructor accepts a reference to a +// StateSetActionVisitor, which has the user's apply methods. +// +// StateSetVisitor: In it's constructor, we give the +// StateSetActionVisitor we've been handed a handle to ourself. Then +// during traversal when we hit a Node, we push a new StateSet onto our +// StateSet stack which is a copy of the top of the stack merged with +// this Node's StateSet. This new StateSet is then given to the +// StateSetActionVisitor instance, and a new traversal is invoked on +// this Node with the StateSetActionVisitor instance as the visitor. +// When it returns, we pop the stack. +// +// StateSetActionVisitor: When StateSetVisitor initiates a traversal on +// a Node with us, we simply call the apply() method stack for the +// current Node. At the bottom in apply(Node&), we traverse back to +// the StateSetVisitor traversal using node.traverse, which continues +// traversal on the node's children. + +class StateSetVisitor; + +class StateSetActionVisitor : public osg::NodeVisitor + // Second pass for StateSetVisitor. Users should override apply() + // methods on this object, pass an instance of this object to + // the StateSetVisitor constructor, and initiate traversal on the + // resulting StateSetVisitor instance. + // + // NOTE: We need two passes per Node to update the state correctly so that + // users can still override accept() and get the current state, without + // the user having to call methods to keep it current. + // The first pass updates the state for the node; the second actually + // calls the user's accept methods. In those methods, they can + // call GetActiveStateSet() to get the current state inclusive of that + // node. + // The trick that allows this all to work is that this object's + // apply(Node&) method (at the bottom of the apply stack) transfers + // traversal back over to the StateSetVisitor starting with the children + // of the current node (_traversalMode is also set to TRAVERSE_NONE, + // not that that ever comes into play). So basically we gosub + // into StateSetActionVisitor just long enough to invoke the apply + // methods and then gosub right back out to the main traverser, + // alternating back and forth as we move down the scene graph. +{ + public: + friend class StateSetVisitor; + + StateSetActionVisitor() + : osg::NodeVisitor( osg::NodeVisitor::TRAVERSE_NONE ) {} + + // Give first pass traversal control back to StateSetVisitor + void apply(osg::Node& node); + + void apply(osg::Geode& node) { osg::NodeVisitor::apply(node); } + void apply(osg::Billboard& node) { osg::NodeVisitor::apply(node); } + void apply(osg::LightSource& node){ osg::NodeVisitor::apply(node); } + void apply(osg::Group& node) { osg::NodeVisitor::apply(node); } + void apply(osg::Transform& node) { osg::NodeVisitor::apply(node); } + void apply(osg::Switch& node) { osg::NodeVisitor::apply(node); } + void apply(osg::LOD& node) { osg::NodeVisitor::apply(node); } + void apply(osg::Impostor& node) { osg::NodeVisitor::apply(node); } + void apply(osg::EarthSky& node) { osg::NodeVisitor::apply(node); } + + + const osg::StateSet &GetActiveStateSet() + { return *_current_stateset; } + + protected: + void SetStateSetVisitor( StateSetVisitor *nv ) + { _ssvisitor = nv; } + void SetActiveStateSet( osg::StateSet *sset ) + { _current_stateset = sset; } + + StateSetVisitor *_ssvisitor; + osg::StateSet *_current_stateset; +}; + +//---------------------------------------------------------------------------- + +class StateSetVisitor : public osg::NodeVisitor + // First pass for StateSetVisitor. Users should override apply() + // methods on the StateSetActionVisitor object, pass an instance of it + // to this object's constructor, and initiate traversal on the + // resulting StateSetVisitor instance. + // + // See StateSetActionVisitor for more details. +{ + public: + + StateSetVisitor( StateSetActionVisitor *av ) + // ACTIVE_CHILDREN - Only traverse the most-detailed LOD and + // active children in a Switch + : osg::NodeVisitor( osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN ), + _actionTraversalVisitor(av) + { + _actionTraversalVisitor->SetStateSetVisitor( this ); + } + + void apply(osg::Node& node) + { + // In this first apply() pass, update state with node's StateSet. + osg::StateSet *node_state = node.getStateSet(); + osg::ref_ptr sset; + + if ( node_state ) { + sset = new StateSetCopy( *_state_stack.back() ); + sset->merge( *node_state ); + } + else + if ( !_state_stack.empty() ) + sset = _state_stack.back(); + else + sset = new StateSetCopy(); + + // Now push state, traverse using action visitor, and then pop state + _state_stack.push_back( sset ); + //traverse(node); + _actionTraversalVisitor->SetActiveStateSet( sset.get() ); + node.accept(*_actionTraversalVisitor); + _state_stack.pop_back(); + } + + protected: + StateSetActionVisitor *_actionTraversalVisitor; + std::vector< osg::ref_ptr > _state_stack; +}; + +//---------------------------------------------------------------------------- + +// Give first pass traversal control back to StateSetVisitor +void StateSetActionVisitor::apply(osg::Node& node) + { assert( _ssvisitor ); + // Flip back to StateSetVisitor and traverse node's kids + node.traverse( *_ssvisitor ); } + +//---------------------------------------------------------------------------- + +class DXWriteVisitor : public StateSetActionVisitor +{ + protected: + DXWriter &dx; + std::vector< DXGroup * > group_stack; + MessageBin &msg_bin; + + enum NodeTypes { LOD, BILLBOARD, LIGHTSOURCE, TRANSFORM, SWITCH, + IMPOSTER, EARTHSKY }; + + typedef std::map< NodeTypes, int > StringMap; + StringMap problem_nodes; + + public: + + DXWriteVisitor( DXWriter &dx ) : dx(dx), msg_bin(dx.msg_bin) {} + + void apply(osg::Node& node) + { + dx.CollectUnhandledModesAndAttrs( node.getStateSet() ); + StateSetActionVisitor::apply( node ); // Flip back to StateSetVisitor + } + + void apply(osg::Geode& node) + { + std::string name; + try { + name = dx.WriteGeode( node, GetActiveStateSet() ); + } catch (...) { throw; } + + // Accumulate fields (Geodes) in groups + if ( !name.empty() && group_stack.size() > 0 ) + group_stack[ group_stack.size()-1 ]->AddMember( name.c_str() ); + apply((osg::Node&)node); + } + + void apply(osg::Group& node) + { + DXGroup *group = 0; + + // Only start a new DX group if there are 2 or more kids +#ifdef SKIP_SINGLE_MEMBER_DXGROUPS + if ( node.getNumChildren() >= 2 ) { +#else + if ( 1 ) { +#endif + // New group + group = new DXGroup( dx.name_mgr, &node.getName() ); + group_stack.push_back( group ); + + // Add to any parent group + if ( group_stack.size() >= 2 ) + group_stack[ group_stack.size()-2 ]->AddMember( + group->GetName().c_str() ); + } + + // Process children + // (Children will be added to this DX group, or a parent DX group + // if we didn't start a new DX group above, or no DX group if + // none active right now). + apply((osg::Node&)node); + + // If we started a new DX group above + if ( group ) { + // If wrote some children, write group; else remove from parent group + if ( group_stack.back()->GetNumMembers() > 0 ) + group_stack.back()->Write( dx.fp ); + else if ( group_stack.size() >= 2 ) + group_stack[ group_stack.size()-2 ]->RemoveMember( + group->GetName().c_str() ); + + // Delete group + group_stack.pop_back(); + delete group; + } + } + + void apply(osg::LOD& node) + { problem_nodes[ LOD ]++; apply((osg::Node&)node); } + void apply(osg::Switch& node) + { problem_nodes[ SWITCH ]++; apply((osg::Node&)node); } + + // FIXME + void apply(osg::Billboard& node) + { + // FIXME: Right now we're just treating this like a transformed Geode + // ignoring the other Billboard properties + problem_nodes[ BILLBOARD ]++; + + DXGroup *group = 0; + + // New dummy group (to snap up our child Geode's DX object name) + std::string fallback_name( "Billboard" ); + group = new DXGroup( dx.name_mgr, &node.getName(), &fallback_name ); + group_stack.push_back( group ); + + // Add to any parent group + if ( group_stack.size() >= 2 ) + group_stack[ group_stack.size()-2 ]->AddMember( + group->GetName().c_str() ); + + // Process child + apply((osg::Geode&)node); + + // FIXME: Currently only support one GeoSet per Billboard + if ( group->GetNumMembers() > 1 ) + msg_bin.Add( + "WARNING: Currently only 1 GeoSet per Billboard is supported\n" + " Using same transform for all GeoSets.\n"); + + // Write billboard transform + fprintf( dx.fp, "object \"%s\" class transform of \"%s\"\n", + group->GetName().c_str(), + group->members[ group->members.size()-1 ].c_str() ); + fprintf( dx.fp, + " times 1 0 0\n" + " 0 1 0\n" + " 0 0 1\n" + " plus %g %g %g\n", + node.getPos(0).x(), node.getPos(0).y(), node.getPos(0).z() ); + fprintf( dx.fp, "#\n\n" ); + + // Delete group + group_stack.pop_back(); + delete group; + } + void apply(osg::LightSource& node) + { problem_nodes[ LIGHTSOURCE ]++; apply((osg::Node&)node); } + void apply(osg::Transform& node) + { problem_nodes[ TRANSFORM ]++; apply((osg::Group&)node); } + void apply(osg::Impostor& node) + { problem_nodes[ IMPOSTER ]++; apply((osg::LOD&)node); } + void apply(osg::EarthSky& node) + { problem_nodes[ EARTHSKY ]++; apply((osg::Group&)node); } + + void ReportProblems(); +}; + +//---------------------------------------------------------------------------- + +void DXWriteVisitor::ReportProblems() +{ + for( StringMap::const_iterator smitr = problem_nodes.begin(); + smitr != problem_nodes.end(); + ++smitr ) + switch ( smitr->first ) { + case LOD : msg_bin.Add( "WARNING: %d LOD(s) found ... " + "Traversed only the most detailed child of each.\n", + smitr->second ); + break; + case SWITCH : + msg_bin.Add( "WARNING: %d Switch(s) found ... " + "Traversed only the active child of each.\n", + smitr->second ); + break; + case BILLBOARD : + msg_bin.Add( "WARNING: %d Billboard(s) found ... " + "represented as simple Geodes with Transforms.\n", + smitr->second ); + break; + case LIGHTSOURCE : + msg_bin.Add( "WARNING: %d LightSource(s) found ... Skipped.\n", + smitr->second ); + break; + case TRANSFORM : + msg_bin.Add( "WARNING: %d Transform(s) found ... Skipped.\n", + smitr->second ); + break; + case IMPOSTER : + msg_bin.Add( "WARNING: %d Imposter(s) found ... Skipped.\n", + smitr->second ); + break; + case EARTHSKY : + msg_bin.Add( "WARNING: %d EarthSky(s) found ... Skipped.\n", + smitr->second ); + break; + } +} + +//---------------------------------------------------------------------------- + +bool WriteDX( const osg::Node &node, WriterParms &parms, std::string &messages ) +{ + messages = ""; + + DXWriter dx; + dx.SetParms( parms ); + dx.Open(); + + // Yes, this looks a bit weird. + // See the comment in the StateSetActionVisitor decl for more details. + DXWriteVisitor dxvisitor(dx); + StateSetVisitor ssv(&dxvisitor); + + bool success = false; + try { + // Arg. Node::accept isn't a const method. + ((osg::Node &)node).accept( ssv ); + success = true; + } + catch (...) { + unlink( parms.outfile ); + } + dx.Close(); + + dx.ReportUnhandledModesAndAttrs(); + dxvisitor.ReportProblems(); + messages = dx.msg_bin.GetPending(); + return success; + +} + +}; // namespace dx diff --git a/src/osgPlugins/dx/DXWriter.h b/src/osgPlugins/dx/DXWriter.h new file mode 100644 index 000000000..ca0fccb7b --- /dev/null +++ b/src/osgPlugins/dx/DXWriter.h @@ -0,0 +1,47 @@ +// This plugin writes an IBM Data Explorer (aka OpenDX) native file. +// (c) Randall Hopper, 2002. +// +// For details on the OpenDX visualization tool, its use, and its file format, +// refer to: +// +// http://www.opendx.org/ +// http://www.opendx.org/support.html#docs +// http://www.research.ibm.com/dx/ +// http://ftp.cs.umt.edu/DX/ +// +// SUPPORTED : Refer to DXWriter.cpp +// UNSUPPORTED: Refer to DXWriter.cpp +// + +// DXWriter.h + +#ifndef __OSG_DXWRITER_H +#define __OSG_DXWRITER_H + +#include +#include + +namespace dx { + +//---------------------------------------------------------------------------- + +struct WriterParms { + bool set_default_color; // Give color to uncolored objects + osg::Vec4 default_color; // Color to assign to uncolored objects + char outfile[ PATH_MAX ]; // Output pathname + //bool binary_mode; // Write DX arrays in binary format + + WriterParms() + { set_default_color = 0, outfile[0] = '\0'; } +}; + +//---------------------------------------------------------------------------- + +bool WriteDX( const osg::Node &node, WriterParms &parms, + std::string &messages ); + +}; // namespace dx + +//---------------------------------------------------------------------------- + +#endif // __OSG_DXWRITER_H diff --git a/src/osgPlugins/dx/Makefile b/src/osgPlugins/dx/Makefile new file mode 100644 index 000000000..b0c0dee99 --- /dev/null +++ b/src/osgPlugins/dx/Makefile @@ -0,0 +1,23 @@ +#!smake +include $(OSGHOME)/Make/makedefs + +C++FILES = ReaderWriterDX.cpp \ + DXWriter.cpp \ + AreaGeoSetTriangulator.cpp \ + StateSetStr.cpp + +TARGET_BASENAME = osgdb_dx + +LOADABLE = $(OSGHOME)/lib/osgPlugins/$(TARGET_BASENAME).$(DL_EXT) +LIB= + +TARGET_LOADER_FILES = osgPlugins/$(TARGET_BASENAME).$(DL_EXT) + +LIBS = $(GL_LIBS) -losg -losgDB +MACOSXLIBS = -L$(OSGHOME)/lib -losg -losgDB -lstdc++ + +C++FLAGS += -I. -I$(OSGHOME)/include +LDFLAGS += -L$(OSGHOME)/lib + +include $(OSGHOME)/Make/makerules + diff --git a/src/osgPlugins/dx/ReaderWriterDX.cpp b/src/osgPlugins/dx/ReaderWriterDX.cpp new file mode 100644 index 000000000..94354c15b --- /dev/null +++ b/src/osgPlugins/dx/ReaderWriterDX.cpp @@ -0,0 +1,79 @@ +// This plugin writes an IBM Data Explorer (aka OpenDX) native file. +// (c) Randall Hopper, 2002. +// +// For details on the OpenDX visualization tool, its use, and its file format, +// refer to: +// +// http://www.opendx.org/ +// http://www.opendx.org/support.html#docs +// http://www.research.ibm.com/dx/ +// http://ftp.cs.umt.edu/DX/ +// +// SUPPORTED : Refer to DXWriter.cpp +// UNSUPPORTED: Refer to DXWriter.cpp +// + +// ReaderWriterDX.cpp + +#include +#include +#include +#include "ReaderWriterDX.h" +#include "DXWriter.h" + +// FIXME: Support options +osgDB::ReaderWriter::WriteResult ReaderWriterDX::writeObject( + const osg::Object &obj, + const std::string &filename, + const Options *options ) +{ + const osg::Node *node = dynamic_cast(&obj); + + if ( node != 0 ) + return writeNode( *node, filename, options ); + else + // FIXME: This should probably be "OBJECT_NOT_HANDLED" + return osgDB::ReaderWriter::WriteResult::FILE_NOT_HANDLED; +} + + +osgDB::ReaderWriter::WriteResult ReaderWriterDX::writeNode( + const osg::Node &node, + const std::string &filename, + const Options *options ) +{ + dx::WriterParms parms; + std::string messages; + bool ret; + osgDB::ReaderWriter::WriteResult result; + + std::string ext = osgDB::getFileExtension(filename); + if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED; + + parms.outfile[0] = '\0'; + strncat( parms.outfile, filename.c_str(), sizeof(parms.outfile)-1 ); + + // FIXME: Allow specification via Options + parms.set_default_color = 1; + parms.default_color[0] = + parms.default_color[1] = + parms.default_color[2] = 0.75; + parms.default_color[3] = 1.0; // opaque light gray + + ret = dx::WriteDX( node, parms, messages ); + + if ( ret ) + result = osgDB::ReaderWriter::WriteResult( + osgDB::ReaderWriter::WriteResult::FILE_SAVED ); + else + result = osgDB::ReaderWriter::WriteResult( messages ); + + osg::notify( osg::DEBUG_INFO ) << messages; + + return result; +} + + +// now register with Registry to instantiate the above +// reader/writer. +osgDB::RegisterReaderWriterProxy g_dxReaderWriterProxy; diff --git a/src/osgPlugins/dx/ReaderWriterDX.h b/src/osgPlugins/dx/ReaderWriterDX.h new file mode 100644 index 000000000..bb8921c44 --- /dev/null +++ b/src/osgPlugins/dx/ReaderWriterDX.h @@ -0,0 +1,47 @@ +// This plugin writes an IBM Data Explorer (aka OpenDX) native file. +// (c) Randall Hopper, 2002. +// +// For details on the OpenDX visualization tool, its use, and its file format, +// refer to: +// +// http://www.opendx.org/ +// http://www.opendx.org/support.html#docs +// http://www.research.ibm.com/dx/ +// http://ftp.cs.umt.edu/DX/ +// +// SUPPORTED : Refer to DXWriter.cpp +// UNSUPPORTED: Refer to DXWriter.cpp +// + +// ReaderWriterDX.h + +#ifndef __OSG_READER_WRITER_DX_H +#define __OSG_READER_WRITER_DX_H + +#include +#include +#include +#include +#include +#include + + +class ReaderWriterDX : public osgDB::ReaderWriter +{ + public: + + virtual const char* className() { return "OpenDX Writer"; } + virtual bool acceptsExtension( const std::string &extension ) + { return osgDB::equalCaseInsensitive( extension, "dx" ); } + + virtual WriteResult writeObject( const osg::Object &obj, + const std::string &filename, + const Options *options = NULL ); + + virtual WriteResult writeNode ( const osg::Node &node, + const std::string &filename, + const Options *options = NULL ); + +}; + +#endif // __READER_WRITER_DX_H diff --git a/src/osgPlugins/dx/StateSetStr.cpp b/src/osgPlugins/dx/StateSetStr.cpp new file mode 100644 index 000000000..dc5498b50 --- /dev/null +++ b/src/osgPlugins/dx/StateSetStr.cpp @@ -0,0 +1,162 @@ +#include +#include +#include +#include + +namespace dx { + +typedef std::map ModeMap; + +static ModeMap S_mode_map; + +#define ADD_NAME(str,mode) (S_mode_map[(mode)] = (str)); + +// This is a copy of some of the code in src/osgPlugins/osg/StateSet.cpp. +// We need to translate GLModes and StateSet attributes into strings, +// and that's not available in an OSG public API (yet). + +static void initGLNames() +{ + static bool first_time = true; + if (!first_time) return; + + ADD_NAME("GL_ALPHA_TEST",GL_ALPHA_TEST) + ADD_NAME("GL_BLEND",GL_BLEND) + ADD_NAME("GL_COLOR_MATERIAL",GL_COLOR_MATERIAL) + ADD_NAME("GL_CULL_FACE",GL_CULL_FACE) + ADD_NAME("GL_DEPTH_TEST",GL_DEPTH_TEST) + ADD_NAME("GL_FOG",GL_FOG) + ADD_NAME("GL_LIGHTING",GL_LIGHTING) + ADD_NAME("GL_POINT_SMOOTH",GL_POINT_SMOOTH) + ADD_NAME("GL_POLYGON_OFFSET_FILL",GL_POLYGON_OFFSET_FILL) + ADD_NAME("GL_POLYGON_OFFSET_LINE",GL_POLYGON_OFFSET_LINE) + ADD_NAME("GL_POLYGON_OFFSET_POINT",GL_POLYGON_OFFSET_POINT) + + ADD_NAME("GL_TEXTURE_2D",GL_TEXTURE_2D) + ADD_NAME("GL_TEXTURE_GEN_Q",GL_TEXTURE_GEN_Q) + ADD_NAME("GL_TEXTURE_GEN_R",GL_TEXTURE_GEN_R) + ADD_NAME("GL_TEXTURE_GEN_S",GL_TEXTURE_GEN_S) + ADD_NAME("GL_TEXTURE_GEN_T",GL_TEXTURE_GEN_T) + + ADD_NAME("GL_STENCIL_TEST",GL_STENCIL_TEST) + + ADD_NAME("GL_CLIP_PLANE0",GL_CLIP_PLANE0); + ADD_NAME("GL_CLIP_PLANE1",GL_CLIP_PLANE1); + ADD_NAME("GL_CLIP_PLANE2",GL_CLIP_PLANE2); + ADD_NAME("GL_CLIP_PLANE3",GL_CLIP_PLANE3); + ADD_NAME("GL_CLIP_PLANE4",GL_CLIP_PLANE4); + ADD_NAME("GL_CLIP_PLANE5",GL_CLIP_PLANE5); + + ADD_NAME("GL_LIGHT0",GL_LIGHT0); + ADD_NAME("GL_LIGHT1",GL_LIGHT1); + ADD_NAME("GL_LIGHT2",GL_LIGHT2); + ADD_NAME("GL_LIGHT3",GL_LIGHT3); + ADD_NAME("GL_LIGHT4",GL_LIGHT4); + ADD_NAME("GL_LIGHT5",GL_LIGHT5); + ADD_NAME("GL_LIGHT6",GL_LIGHT6); + ADD_NAME("GL_LIGHT7",GL_LIGHT7); + + first_time = false; +} + +const char *GLModeToModeStr( osg::StateAttribute::GLMode mode ) +{ + initGLNames(); + ModeMap::const_iterator mitr = S_mode_map.find(mode); + if ( mitr != S_mode_map.end() ) + return mitr->second.c_str(); + else + return 0; +} + +osg::StateAttribute::GLMode GLModeStrToMode( const char mode_str[] ) +{ + initGLNames(); + for( ModeMap::const_iterator mitr = S_mode_map.begin(); + mitr != S_mode_map.end(); + ++mitr ) + if ( strcmp( mode_str, mitr->second.c_str() ) == 0 ) + return mitr->first; + return (osg::StateAttribute::GLMode) -1; +} + +//=========================================================================== + +typedef std::map AttrMap; + +static AttrMap S_attr_map; + +#define ADD_ATTR(attr,str) (S_attr_map[(attr)] = (str)); + +static void initOSGAttrNames() +{ + static bool first_time = true; + if (!first_time) return; + + ADD_ATTR( osg::StateAttribute::TEXTURE , "TEXTURE" ); + ADD_ATTR( osg::StateAttribute::TEXTURE_0 , "TEXTURE_0" ); + ADD_ATTR( osg::StateAttribute::TEXTURE_1 , "TEXTURE_1" ); + ADD_ATTR( osg::StateAttribute::TEXTURE_2 , "TEXTURE_2" ); + ADD_ATTR( osg::StateAttribute::TEXTURE_3 , "TEXTURE_3" ); + ADD_ATTR( osg::StateAttribute::MATERIAL , "MATERIAL" ); + ADD_ATTR( osg::StateAttribute::ALPHAFUNC , "ALPHAFUNC" ); + ADD_ATTR( osg::StateAttribute::ANTIALIAS , "ANTIALIAS" ); + ADD_ATTR( osg::StateAttribute::COLORTABLE , "COLORTABLE" ); + ADD_ATTR( osg::StateAttribute::CULLFACE , "CULLFACE" ); + ADD_ATTR( osg::StateAttribute::FOG , "FOG" ); + ADD_ATTR( osg::StateAttribute::FRONTFACE , "FRONTFACE" ); + ADD_ATTR( osg::StateAttribute::LIGHT , "LIGHT" ); + ADD_ATTR( osg::StateAttribute::LIGHT_0 , "LIGHT_0" ); + ADD_ATTR( osg::StateAttribute::LIGHT_1 , "LIGHT_1" ); + ADD_ATTR( osg::StateAttribute::LIGHT_2 , "LIGHT_2" ); + ADD_ATTR( osg::StateAttribute::LIGHT_3 , "LIGHT_3" ); + ADD_ATTR( osg::StateAttribute::LIGHT_4 , "LIGHT_4" ); + ADD_ATTR( osg::StateAttribute::LIGHT_5 , "LIGHT_5" ); + ADD_ATTR( osg::StateAttribute::LIGHT_6 , "LIGHT_6" ); + ADD_ATTR( osg::StateAttribute::LIGHT_7 , "LIGHT_7" ); + ADD_ATTR( osg::StateAttribute::POINT , "POINT" ); + ADD_ATTR( osg::StateAttribute::LINEWIDTH , "LINEWIDTH" ); + ADD_ATTR( osg::StateAttribute::POLYGONMODE , "POLYGONMODE" ); + ADD_ATTR( osg::StateAttribute::POLYGONOFFSET , "POLYGONOFFSET"); + ADD_ATTR( osg::StateAttribute::TEXENV , "TEXENV" ); + ADD_ATTR( osg::StateAttribute::TEXGEN , "TEXGEN" ); + ADD_ATTR( osg::StateAttribute::TEXMAT , "TEXMAT" ); + ADD_ATTR( osg::StateAttribute::TRANSPARENCY , "TRANSPARENCY" ); + ADD_ATTR( osg::StateAttribute::STENCIL , "STENCIL" ); + ADD_ATTR( osg::StateAttribute::COLORMASK , "COLORMASK" ); + ADD_ATTR( osg::StateAttribute::DEPTH , "DEPTH" ); + ADD_ATTR( osg::StateAttribute::VIEWPORT , "VIEWPORT" ); + ADD_ATTR( osg::StateAttribute::CLIPPLANE , "CLIPPLANE" ); + ADD_ATTR( osg::StateAttribute::CLIPPLANE_0 , "CLIPPLANE_0" ); + ADD_ATTR( osg::StateAttribute::CLIPPLANE_1 , "CLIPPLANE_1" ); + ADD_ATTR( osg::StateAttribute::CLIPPLANE_2 , "CLIPPLANE_2" ); + ADD_ATTR( osg::StateAttribute::CLIPPLANE_3 , "CLIPPLANE_3" ); + ADD_ATTR( osg::StateAttribute::CLIPPLANE_4 , "CLIPPLANE_4" ); + ADD_ATTR( osg::StateAttribute::CLIPPLANE_5 , "CLIPPLANE_5" ); + ADD_ATTR( osg::StateAttribute::COLORMATRIX , "COLORMATRIX" ); + + first_time = false; +} + +const char *OSGAttrToAttrStr( osg::StateAttribute::Type attr ) +{ + initOSGAttrNames(); + AttrMap::const_iterator aitr = S_attr_map.find(attr); + if ( aitr != S_attr_map.end() ) + return aitr->second.c_str(); + else + return 0; +} + +osg::StateAttribute::Type OSGAttrStrToAttr( const char attr_str[] ) +{ + initGLNames(); + for( AttrMap::const_iterator aitr = S_attr_map.begin(); + aitr != S_attr_map.end(); + ++aitr ) + if ( strcmp( attr_str, aitr->second.c_str() ) == 0 ) + return aitr->first; + return (osg::StateAttribute::Type) -1; +} + +}; // namespace dx diff --git a/src/osgPlugins/dx/StateSetStr.h b/src/osgPlugins/dx/StateSetStr.h new file mode 100644 index 000000000..7c8bca66e --- /dev/null +++ b/src/osgPlugins/dx/StateSetStr.h @@ -0,0 +1,18 @@ +#ifndef __OSG_STATESETSTR_H +#define __OSG_STATESETSTR_H + +#include + +namespace dx { + +const char *GLModeToModeStr( osg::StateAttribute::GLMode mode ); + +osg::StateAttribute::GLMode GLModeStrToMode( const char mode_str[] ); + +const char *OSGAttrToAttrStr( osg::StateAttribute::Type attr ); + +osg::StateAttribute::Type OSGAttrStrToAttr( const char attr_str[] ); + +}; + +#endif diff --git a/src/osgPlugins/osg/ReaderWriterOSG.cpp b/src/osgPlugins/osg/ReaderWriterOSG.cpp index 2667eb5eb..efd31baa9 100644 --- a/src/osgPlugins/osg/ReaderWriterOSG.cpp +++ b/src/osgPlugins/osg/ReaderWriterOSG.cpp @@ -85,6 +85,9 @@ class OSGReaderWriter : public ReaderWriter virtual WriteResult writeNode(const Node& node,const std::string& fileName, const osgDB::ReaderWriter::Options*) { + std::string ext = getFileExtension(fileName); + if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED; + Output fout; fout.open(fileName.c_str()); if (fout)