From f206abcd434a8c143fa3a62d532023b8ab6d97b7 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Sun, 1 Aug 2004 10:36:02 +0000 Subject: [PATCH] From Geoff Michel, new osgtesselate example, and new features in osgUtil::Tesselator. --- VisualStudio/VisualStudio.dsw | 30 + .../examples/osgtesselate/osgtesselate.dsp | 101 +++ .../examples/osgtesselate/resources.rc | 1 + examples/osgtesselate/GNUmakefile | 18 + examples/osgtesselate/GNUmakefile.inst | 13 + examples/osgtesselate/osgtesselate.cpp | 774 ++++++++++++++++++ include/osgUtil/Tesselator | 94 ++- src/osgUtil/Tesselator.cpp | 691 +++++++++++----- 8 files changed, 1486 insertions(+), 236 deletions(-) create mode 100644 VisualStudio/examples/osgtesselate/osgtesselate.dsp create mode 100644 VisualStudio/examples/osgtesselate/resources.rc create mode 100644 examples/osgtesselate/GNUmakefile create mode 100644 examples/osgtesselate/GNUmakefile.inst create mode 100644 examples/osgtesselate/osgtesselate.cpp diff --git a/VisualStudio/VisualStudio.dsw b/VisualStudio/VisualStudio.dsw index 50ea4c6df..002874f33 100644 --- a/VisualStudio/VisualStudio.dsw +++ b/VisualStudio/VisualStudio.dsw @@ -1596,6 +1596,36 @@ Package=<4> ############################################################################### +Project: "Example osgtesselate"=.\examples\osgtesselate\osgtesselate.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name Core osg + End Project Dependency + Begin Project Dependency + Project_Dep_Name Core osgDB + End Project Dependency + Begin Project Dependency + Project_Dep_Name Core osgGA + End Project Dependency + Begin Project Dependency + Project_Dep_Name Core osgProducer + End Project Dependency + Begin Project Dependency + Project_Dep_Name Core osgUtil + End Project Dependency + Begin Project Dependency + Project_Dep_Name Core osgText + End Project Dependency +}}} + +############################################################################### + Project: "Example osgtext"=.\examples\osgtext\osgtextdemo.dsp - Package Owner=<4> Package=<5> diff --git a/VisualStudio/examples/osgtesselate/osgtesselate.dsp b/VisualStudio/examples/osgtesselate/osgtesselate.dsp new file mode 100644 index 000000000..473472e27 --- /dev/null +++ b/VisualStudio/examples/osgtesselate/osgtesselate.dsp @@ -0,0 +1,101 @@ +# Microsoft Developer Studio Project File - Name="Example osgtesselate" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=Example osgtesselate - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "osgtesselate.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 "osgtesselate.mak" CFG="Example osgtesselate - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "Example osgtesselate - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "Example osgtesselate - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "Example osgtesselate - 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 "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +MTL=midl.exe +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GR /GX /O2 /I "../../../include" /I "../../../../OpenThreads/include" /I "../../../../Producer/include" /I "../../../../3rdParty/include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /Zm200 /c +# 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 /subsystem:console /machine:I386 +# ADD LINK32 OpenThreadsWin32.lib opengl32.lib /nologo /subsystem:console /pdb:none /machine:I386 /out:"../../../bin/osgtesselate.exe" /libpath:"../../../lib" /libpath:"../../../../OpenThreads/lib/win32" /libpath:"../../../../Producer/lib" /libpath:"../../../../3rdParty/lib" + +!ELSEIF "$(CFG)" == "Example osgtesselate - 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 "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +MTL=midl.exe +# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MDd /W3 /Gm /vd0 /GR /GX /Zi /Od /I "../../../include" /I "../../../../OpenThreads/include" /I "../../../../Producer/include" /I "../../../../3rdParty/include" /D "_CONSOLE" /D "_MBCS" /D "FL_DLL" /D "WIN32" /D "_DEBUG" /FR /YX /FD /Zm200 /c +# 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 /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 OpenThreadsWin32d.lib opengl32.lib /nologo /subsystem:console /debug /machine:I386 /nodefaultlib:"libcmt" /out:"../../../bin/osgtesselated.exe" /pdbtype:sept /libpath:"../../../lib" /libpath:"../../../../OpenThreads/lib/win32" /libpath:"../../../../Producer/lib" /libpath:"../../../../3rdParty/lib" +# SUBTRACT LINK32 /incremental:no + +!ENDIF + +# Begin Target + +# Name "Example osgtesselate - Win32 Release" +# Name "Example osgtesselate - Win32 Debug" +# Begin Source File + +SOURCE=..\..\..\examples\osgtesselate\osgtesselate.cpp +# End Source File +# Begin Source File + +SOURCE=.\resources.rc +# End Source File +# End Target +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Project diff --git a/VisualStudio/examples/osgtesselate/resources.rc b/VisualStudio/examples/osgtesselate/resources.rc new file mode 100644 index 000000000..029ebe5a7 --- /dev/null +++ b/VisualStudio/examples/osgtesselate/resources.rc @@ -0,0 +1 @@ +PRODUCER_ICON ICON DISCARDABLE "../../icons/osg.ico" diff --git a/examples/osgtesselate/GNUmakefile b/examples/osgtesselate/GNUmakefile new file mode 100644 index 000000000..b38cd9091 --- /dev/null +++ b/examples/osgtesselate/GNUmakefile @@ -0,0 +1,18 @@ +TOPDIR = ../.. +include $(TOPDIR)/Make/makedefs + +CXXFILES =\ + osgtesselate.cpp\ + +LIBS += -losgProducer -lProducer -losgText -losgGA -losgDB -losgUtil -losg $(GL_LIBS) $(X_LIBS) $(OTHER_LIBS) + +INSTFILES = \ + $(CXXFILES)\ + GNUmakefile.inst=GNUmakefile + +EXEC = osgtesselate + +INC += $(X_INC) + +include $(TOPDIR)/Make/makerules + diff --git a/examples/osgtesselate/GNUmakefile.inst b/examples/osgtesselate/GNUmakefile.inst new file mode 100644 index 000000000..30dd4771d --- /dev/null +++ b/examples/osgtesselate/GNUmakefile.inst @@ -0,0 +1,13 @@ +TOPDIR = ../.. +include $(TOPDIR)/Make/makedefs + +CXXFILES =\ + osgtesselate.cpp\ + +LIBS += -losgProducer -lProducer -losgDB -losgText -losgUtil -losg $(GL_LIBS) $(X_LIBS) $(OTHER_LIBS) + +EXEC = osgtesselate + +INC += $(X_INC) + +include $(TOPDIR)/Make/makerules diff --git a/examples/osgtesselate/osgtesselate.cpp b/examples/osgtesselate/osgtesselate.cpp new file mode 100644 index 000000000..f89e29ec6 --- /dev/null +++ b/examples/osgtesselate/osgtesselate.cpp @@ -0,0 +1,774 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2003 Robert Osfield + * + * This application is open source and may be redistributed and/or modified + * freely and without restriction, both in commericial and non commericial applications, + * as long as this copyright notice is maintained. + * + * This application is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +*/ + +/* osgtesselator + * - this tesselator is an extension of the basic one - rather than tesselating + * individual polygons, we tesselate the entire geode with multiple contours. + * allows for overlapping contours etc. + * the tesselator has new member fuinctions + setTesselationType(osgUtil::Tesselator::TESS_TYPE_xxx); + tscx->setBoundaryOnly(bool); + tscx->setWindingType( osgUtil::Tesselator::TESS_WINDING_xxx); + * for winding rules: See the red book chap 13. + */ + +#include +#include +#include +#include +#include + +#include + +#include // to tesselate multiple contours + + +class tesselateDemoGeometry : public osg::Geometry, public osgUtil::Tesselator { + // We add the Tesselator to the geometry because we want to access the + // tesselatable contours again; you can apply a tesselator to a Geometry + // to produce exactly a required tesselation once only, and then + // the contours could be discarded since the geometry does not need to be retesselated. +public: + tesselateDemoGeometry() {}; + +protected: + virtual ~tesselateDemoGeometry() {}; +}; + +osg::Geometry *makePolsTwo (void) { + // an example of using current geometry contours to create next tesselation + // this polygon disappears once the contour rules make no polygons. + tesselateDemoGeometry *gtess= new tesselateDemoGeometry; + int i; + osg::Vec3Array *coords = new osg::Vec3Array; + osg::Vec3Array *nrms = new osg::Vec3Array; + osg::Vec2Array *tcs = new osg::Vec2Array; + osg::Vec3 nrm(0,-1,0); + static GLdouble quadstrip[8][3] = + {1900.0, 1130.0, 0.0, 2100.0, 1130.0, 0.0, + 1900.0, 1350.0, 0.0, 1950.0, 1350.0, 0.0, + 1900.0, 1550.0, 0.0, 2000.0, 1550.0, 0.0, + 1900.0, 1750.0, 0.0, 2400.0, 1750.0, 0.0}; + static GLdouble innerquadstrip[8][3] = + {2000.0, 1230.0, 0.0, 2050.0, 1230.0, 0.0, + 1920.0, 1350.0, 0.0, 1940.0, 1350.0, 0.0, + 1920.0, 1550.0, 0.0, 1980.0, 1550.0, 0.0, + 2000.0, 1650.0, 0.0, 2400.0, 1650.0, 0.0}; + // add one large quadstrip + for (i = 0; i < 8; i++) { + coords->push_back(osg::Vec3(quadstrip[i][0],quadstrip[i][2],quadstrip[i][1])); + tcs->push_back(osg::Vec2(quadstrip[i][0],quadstrip[i][1])/200.0); + nrms->push_back(nrm); + } + for (i = 0; i < 8; i++) { + coords->push_back(osg::Vec3(innerquadstrip[i][0],innerquadstrip[i][2],innerquadstrip[i][1])); + tcs->push_back(osg::Vec2(innerquadstrip[i][0],innerquadstrip[i][1])/200.0); + nrms->push_back(nrm); + } + gtess->setVertexArray(coords); + gtess->setNormalArray(nrms); + gtess->setTexCoordArray(0,tcs); + + // demonstrate that the tesselator makes textured tesselations + osg::StateSet* stateset = new osg::StateSet(); + + osg::Image* image = osgDB::readImageFile("Images/osg_posy.png"); + if (image) + { + osg::Texture2D* texture = new osg::Texture2D; + texture->setImage(image); + stateset->setTextureAttributeAndModes(0,texture,osg::StateAttribute::ON); + } + gtess->setStateSet( stateset ); + + int nstart=0; + // The derived class tesselateDemoGeometry retains the original contours for re-use. + gtess->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::TRIANGLE_STRIP,nstart,8));nstart+=8; + gtess->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::TRIANGLE_STRIP,nstart,8));nstart+=8; + + gtess->setTesselationType(osgUtil::Tesselator::TESS_TYPE_GEOMETRY); + gtess->setBoundaryOnly(true); + gtess->setWindingType( osgUtil::Tesselator::TESS_WINDING_ABS_GEQ_TWO); // so that first change in wind type makes the commonest tesselation - ODD. + + return gtess; +} + +osg::Geometry *makeSideWall (const float xpos) { + // demonstrate making a rectangular 'wall' with 2 holes in it. + osg::Geometry *gtess= new osg::Geometry; + int i; + osg::Vec3Array *coords = new osg::Vec3Array; + osg::Vec3Array *nrms = new osg::Vec3Array; + osg::Vec2Array *tcs = new osg::Vec2Array; + osg::Vec3 nrm(0,0,-1); + // front wall + static GLdouble wall[4][2] = + { 1130.0, 0.0, 1130.0, 300.0, + 1340.0,300.0, 1340.0,0.0}; + gtess->setVertexArray(coords); + gtess->setNormalArray(nrms); + gtess->setTexCoordArray(0,tcs); + + for (i = 0; i < 4; i++) { + coords->push_back(osg::Vec3(xpos,wall[i][1],wall[i][0])); + tcs->push_back(osg::Vec2(wall[i][1],wall[i][0])/100.0); + nrms->push_back(nrm); + } + int nstart=0; + + gtess->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,nstart,4));nstart+=4; + for (i = 0; i < 24; i++) { // make an ellipse hole + float y=150+50*cos(i*2*osg::PI/24.0); + float z=1300+30* sin(i*2*osg::PI/24.0); + coords->push_back(osg::Vec3(xpos,y,z)); + tcs->push_back(osg::Vec2(y,z)/100.0); + nrms->push_back(nrm); + } + gtess->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::POLYGON,nstart,24));nstart+=24; + for (i = 0; i < 5; i++) { // make a pentagonal hole + float y=150+50*cos(i*2*osg::PI/5.0); + float z=1200+40* sin(i*2*osg::PI/5.0); + coords->push_back(osg::Vec3(xpos,y,z)); + tcs->push_back(osg::Vec2(y,z)/100.0); + nrms->push_back(nrm); + } + gtess->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::POLYGON,nstart,5));nstart+=5; + + // demonstrate that the tesselator makes textured tesselations + osg::StateSet* stateset = new osg::StateSet(); + + osg::Image* image = osgDB::readImageFile("Images/osg_posx.png"); + if (image) + { + osg::Texture2D* texture = new osg::Texture2D; + texture->setImage(image); + stateset->setTextureAttributeAndModes(0,texture,osg::StateAttribute::ON); + } + gtess->setStateSet( stateset ); + + + osg::ref_ptr tscx=new osgUtil::Tesselator; // the v1.2 multi-contour tesselator. + // we use the geometry primitives to describe the contours which are tesselated. + // Winding odd means leave hole in surface where there are 2,4,6... contours circling the point. + tscx->setTesselationType(osgUtil::Tesselator::TESS_TYPE_GEOMETRY); + tscx->setBoundaryOnly(false); + tscx->setWindingType( osgUtil::Tesselator::TESS_WINDING_ODD); // so that first change in wind type makes the commonest tesselation - ODD. + + tscx->retesselatePolygons(*gtess); + + return gtess; +} + +osg::Geometry *makeFrontWall (const float zpos) { + // an example of using one tesselation to make a 'house' wall + // describe the wall as a pentagon, then door & 4 windows are further contours + // tesselate the set of contours to make a 'house wall' from the Boolean-like operations. + int nstart=0; // counts vertices used for the geometry primitives + + osg::Geometry *gtess= new osg::Geometry; + int i; + osg::Vec3Array *coords = new osg::Vec3Array; + osg::Vec3Array *nrms = new osg::Vec3Array; + osg::Vec2Array *tcs = new osg::Vec2Array; + osg::Vec3 nrm(0,0,-1); + // front wall + static GLdouble wall[5][2] = + {2200.0, 1130.0, 2600.0, 1130.0, + 2600.0, 1340.0, 2400.0, 1440.0, 2200.0, 1340.0}; + static GLdouble door[4][2] = + {2360.0, 1130.0, 2440.0, 1130.0, + 2440.0, 1230.0, 2360.0, 1230.0}; + static GLdouble windows[16][2] = + {2240.0, 1180.0, 2330.0, 1180.0, + 2330.0, 1220.0, 2240.0, 1220.0, + 2460.0, 1180.0, 2560.0, 1180.0, + 2560.0, 1220.0, 2460.0, 1220.0, + 2240.0, 1280.0, 2330.0, 1280.0, + 2330.0, 1320.0, 2240.0, 1320.0, + 2460.0, 1280.0, 2560.0, 1280.0, + 2560.0, 1320.0, 2460.0, 1320.0}; + + gtess->setVertexArray(coords); + gtess->setNormalArray(nrms); + gtess->setTexCoordArray(0,tcs); + + // add one large pentagon -the wall + for (i = 0; i < 5; i++) { + coords->push_back(osg::Vec3(wall[i][0],zpos,wall[i][1])); + tcs->push_back(osg::Vec2(wall[i][0],wall[i][1])/100.0); + nrms->push_back(nrm); + } + gtess->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::POLYGON,nstart,5));nstart+=5; + // add first hole, a door + for (i = 0; i < 4; i++) { + coords->push_back(osg::Vec3(door[i][0],zpos,door[i][1])); + tcs->push_back(osg::Vec2(door[i][0],door[i][1])/100.0); + nrms->push_back(nrm); + } + // and windows + for (i = 0; i < 16; i++) { + coords->push_back(osg::Vec3(windows[i][0],zpos,windows[i][1])); + tcs->push_back(osg::Vec2(windows[i][0],windows[i][1])/100.0); + nrms->push_back(nrm); + } + gtess->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,nstart,20));nstart+=20; + + // demonstrate that the tesselator makes textured tesselations + osg::StateSet* stateset = new osg::StateSet(); + + osg::Image* image = osgDB::readImageFile("Images/osg_posy.png"); + if (image) + { + osg::Texture2D* texture = new osg::Texture2D; + texture->setImage(image); + stateset->setTextureAttributeAndModes(0,texture,osg::StateAttribute::ON); + } + gtess->setStateSet( stateset ); + + // We use a Tesselator to produce the tesselation required once only + // and the contours are discarded. + osg::ref_ptr tscx=new osgUtil::Tesselator; // the v1.2 multi-contour tesselator. + tscx->setTesselationType(osgUtil::Tesselator::TESS_TYPE_GEOMETRY); + tscx->setBoundaryOnly(false); + tscx->setWindingType( osgUtil::Tesselator::TESS_WINDING_ODD); // so that first change in wind type makes the commonest tesselation - ODD. + + tscx->retesselatePolygons(*gtess); + + return gtess; +} +osg::Geode *makeHouse (void) { + osg::Geode *gd = new osg::Geode; + gd->addDrawable(makeFrontWall(0.0)); + gd->addDrawable(makeFrontWall(300.0)); + gd->addDrawable(makeSideWall(2200.0)); + gd->addDrawable(makeSideWall(2600.0)); + return gd; +} +osg::Geometry *makePols (void) { + tesselateDemoGeometry *gtess= new tesselateDemoGeometry; + int i; + osg::Vec3Array *coords = new osg::Vec3Array; + osg::Vec3Array *nrms = new osg::Vec3Array; + osg::Vec2Array *tcs = new osg::Vec2Array; + osg::Vec3 nrm(0,-1,0); + // coordinates from red book code but shifted by 1000 & 2000 for alternate tesselatory things. + static GLdouble rects[12][3] = + {50.0, 50.0, 0.0, 300.0, 50.0, 0.0, + 300.0, 300.0, 0.0, 50.0, 300.0, 0.0, + 100.0, 100.0, 0.0, 250.0, 100.0, 0.0, + 250.0, 250.0, 0.0, 100.0, 250.0, 0.0, + 150.0, 150.0, 0.0, 200.0, 150.0, 0.0, + 200.0, 200.0, 0.0, 150.0, 200.0, 0.0}; + static GLdouble rectsMidanti[12][3] = // the centre 2 contours are traversed opposite order to outer contour. + {1050.0, 50.0, 0.0, 1300.0, 50.0, 0.0, + 1300.0, 300.0, 0.0, 1050.0, 300.0, 0.0, + 1250.0, 100.0, 0.0, 1100.0, 100.0, 0.0, + 1100.0, 250.0, 0.0, 1250.0, 250.0, 0.0, + 1200.0, 150.0, 0.0, 1150.0, 150.0, 0.0, + 1150.0, 200.0, 0.0, 1200.0, 200.0, 0.0 }; + static GLdouble spiral[16][3] = // shift by 1000; nb the order of vertices is reversed from that of the red book + {3400.0, 250.0, 0.0, 3400.0, 50.0, 0.0, + 3050.0, 50.0, 0.0, 3050.0, 400.0, 0.0, + 3350.0, 400.0, 0.0, 3350.0, 100.0, 0.0, + 3100.0, 100.0, 0.0, 3100.0, 350.0, 0.0, + 3300.0, 350.0, 0.0, 3300.0, 150.0, 0.0, + 3150.0, 150.0, 0.0, 3150.0, 300.0, 0.0, + 3250.0, 300.0, 0.0, 3250.0, 200.0, 0.0, + 3200.0, 200.0, 0.0, 3200.0, 250.0, 0.0}; + static GLdouble quad1[4][3] = // shift by 2000 for next 3 things + {2050.0, 150.0, 0.0, 2350.0, 150.0, 0.0, + 2350.0, 200.0, 0.0, 2050.0, 200.0, 0.0}; + static GLdouble quad2[4][3] = + {2100.0, 100.0, 0.0, 2300.0, 100.0, 0.0, + 2300.0, 350.0, 0.0, 2100.0, 350.0, 0.0}; + static GLdouble tri[3][3] = + {2200.0, 50.0, 0.0, 2250.0, 300.0, 0.0, + 2150.0, 300.0, 0.0}; + static GLdouble quad3[4][3] = + {100.0, 1100.0, 0.0, 1300.0, 1100.0, 0.0, + 1300.0, 2350.0, 0.0, 100.0, 2350.0, 0.0}; + static GLdouble quadstrip[8][3] = + {900.0, 1130.0, 0.0, 1100.0, 1130.0, 0.0, + 900.0, 1350.0, 0.0, 950.0, 1350.0, 0.0, + 900.0, 1550.0, 0.0, 1000.0, 1550.0, 0.0, + 900.0, 1750.0, 0.0, 1400.0, 1750.0, 0.0}; + + for (i = 0; i < 12; i++) { + coords->push_back(osg::Vec3(rects[i][0],rects[i][2],rects[i][1])); + tcs->push_back(osg::Vec2(rects[i][0],rects[i][1])/200.0); + nrms->push_back(nrm); + } + for (i = 0; i < 12; i++) { + coords->push_back(osg::Vec3(rectsMidanti[i][0],rectsMidanti[i][2],rectsMidanti[i][1])); + tcs->push_back(osg::Vec2(rectsMidanti[i][0],rectsMidanti[i][1])/200.0); + nrms->push_back(nrm); + } + for (i = 0; i < 16; i++) { // and reverse spiral to make same as that of red book ch 11 + coords->push_back(osg::Vec3(spiral[15-i][0],spiral[15-i][2],spiral[15-i][1])); + tcs->push_back(osg::Vec2(spiral[15-i][0],spiral[15-i][1])/200.0); + nrms->push_back(nrm); + } + for (i = 0; i < 4; i++) { + coords->push_back(osg::Vec3(quad1[i][0],quad1[i][2],quad1[i][1])); + tcs->push_back(osg::Vec2(quad1[i][0],quad1[i][1])/200.0); + nrms->push_back(nrm); + } + for (i = 0; i < 4; i++) { + coords->push_back(osg::Vec3(quad2[i][0],quad2[i][2],quad2[i][1])); + tcs->push_back(osg::Vec2(quad2[i][0],quad2[i][1])/200.0); + nrms->push_back(nrm); + } + for (i = 0; i < 3; i++) { + coords->push_back(osg::Vec3(tri[i][0],tri[i][2],tri[i][1])); + tcs->push_back(osg::Vec2(tri[i][0],tri[i][1])/200.0); + nrms->push_back(nrm); + } + // add one large quad with multiple holes + for (i = 0; i < 4; i++) { + coords->push_back(osg::Vec3(quad3[i][0],quad3[i][2],quad3[i][1])); + tcs->push_back(osg::Vec2(quad3[i][0],quad3[i][1])/200.0); + nrms->push_back(nrm); + } + { + osg::Vec3 centre(300,0,1500); + for (i = 0; i < 18; i++) { + osg::Vec3 rim=centre+osg::Vec3(-cos(osg::DegreesToRadians((float)i*20.0)),0.0,sin(osg::DegreesToRadians((float)i*20.0)))*150.0; + coords->push_back(rim); + tcs->push_back(osg::Vec2(rim.x(),rim.z())/200.0); + nrms->push_back(nrm); + } + } + { + osg::Vec3 centre(400,0,1800); + for (i = 0; i < 18; i++) { + osg::Vec3 rim=centre+osg::Vec3(-cos(osg::DegreesToRadians((float)i*15.0)),0.0,sin(osg::DegreesToRadians((float)i*15.0)))*250.0; + coords->push_back(rim); + tcs->push_back(osg::Vec2(rim.x(),rim.z())/200.0); + nrms->push_back(nrm); + } + } + { + osg::Vec3 centre(600,0,1400); + for (i = 0; i < 18; i++) { + osg::Vec3 rim=centre+osg::Vec3(-cos(osg::DegreesToRadians((float)i*12.0)),0.0,sin(osg::DegreesToRadians((float)i*12.0)))*250.0; + coords->push_back(rim); + tcs->push_back(osg::Vec2(rim.x(),rim.z())/200.0); + nrms->push_back(nrm); + } + } + // add one large quadstrip + for (i = 0; i < 8; i++) { + coords->push_back(osg::Vec3(quadstrip[i][0],quadstrip[i][2],quadstrip[i][1])); + tcs->push_back(osg::Vec2(quadstrip[i][0],quadstrip[i][1])/200.0); + nrms->push_back(nrm); + } + gtess->setVertexArray(coords); + gtess->setNormalArray(nrms); + gtess->setTexCoordArray(0,tcs); + + // demonstrate that the tesselator makes textured tesselations + osg::StateSet* stateset = new osg::StateSet(); + + osg::Image* image = osgDB::readImageFile("Images/osg_posz.png"); + if (image) + { + osg::Texture2D* texture = new osg::Texture2D; + texture->setImage(image); + stateset->setTextureAttributeAndModes(0,texture,osg::StateAttribute::ON); + } + gtess->setStateSet( stateset ); + + int nstart=0; + // the contours accepoted are polygons; quads & tris. Trifans can bve added later. + gtess->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,nstart,12));nstart+=12; + gtess->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,nstart,12));nstart+=12; + gtess->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::POLYGON,nstart,16));nstart+=16; + gtess->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,nstart,4));nstart+=4; + gtess->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,nstart,4));nstart+=4; + gtess->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES,nstart,3));nstart+=3; + // A rectabngle with multiple holes + gtess->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINE_LOOP,nstart,4));nstart+=4; + gtess->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::TRIANGLE_FAN,nstart,18));nstart+=18; + gtess->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::POLYGON,nstart,18));nstart+=18; + gtess->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::POLYGON,nstart,18));nstart+=18; + // test for quad strip + gtess->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::TRIANGLE_STRIP,nstart,8));nstart+=8; + + // We need to access the tesselatable contours again to demonstrate all types of tesselation. + // I could add the Tesselator to the geometry as userdata, but here + // I use the derived tesselateDemoGeometry to hold both the drawable geode and the original contours. + + gtess->setTesselationType(osgUtil::Tesselator::TESS_TYPE_GEOMETRY); + gtess->setBoundaryOnly(true); + gtess->setWindingType( osgUtil::Tesselator::TESS_WINDING_ABS_GEQ_TWO); // so that first change in wind type makes the commonest tesselation - ODD. + + return gtess; +} +osg::Node* createHUD() +{ // add a string reporting the type of winding rule tesselation applied + osg::Geode* geode = new osg::Geode(); + + std::string timesFont("fonts/arial.ttf"); + + // turn lighting off for the text and disable depth test to ensure its always ontop. + osg::StateSet* stateset = geode->getOrCreateStateSet(); + stateset->setMode(GL_LIGHTING,osg::StateAttribute::OFF); + + // Disable depth test, and make sure that the hud is drawn after everything + // else so that it always appears ontop. + stateset->setMode(GL_DEPTH_TEST,osg::StateAttribute::OFF); + stateset->setRenderBinDetails(11,"RenderBin"); + + osg::Vec3 position(150.0f,900.0f,0.0f); + osg::Vec3 delta(0.0f,-30.0f,0.0f); + + { + osgText::Text* text = new osgText::Text; + geode->addDrawable( text ); + + text->setFont(timesFont); + text->setPosition(position); + text->setText("Tesselation example - no tesselation (use 'W' wireframe to visualise)"); + text->setColor(osg::Vec4(1.0,1.0,0.8,1.0)); + position += delta; + + } + { + osgText::Text* text = new osgText::Text; + geode->addDrawable( text ); + + text->setFont(timesFont); + text->setPosition(position); + text->setText("Press 'n' to use an alternative tesselation."); + + } + + // create the hud. + osg::MatrixTransform* modelview_abs = new osg::MatrixTransform; + modelview_abs->setReferenceFrame(osg::Transform::RELATIVE_TO_ABSOLUTE); + modelview_abs->setMatrix(osg::Matrix::identity()); + modelview_abs->addChild(geode); + + osg::Projection* projection = new osg::Projection; + projection->setMatrix(osg::Matrix::ortho2D(0,1280,0,1024)); + projection->addChild(modelview_abs); + + return projection; + +} + + +osg::Group *makeTesselateExample (void) { + osg::Group *grp=new osg::Group; + osg::Geode *gd=new osg::Geode; + gd->addDrawable(makePols()); + gd->addDrawable(makePolsTwo()); + grp->addChild(gd); + + grp->addChild(makeHouse()); + + return grp; +} + +class setTesselateVisitor : public osg::NodeVisitor +{ // searches a loaded model tree for tesselatable geometries. + // used with any database model which has a renderGroup (Geode) named 'tesselate' + // or you can force a type of tess with special names or a sub-class of Geode could have extra information + // of course you can use any name to detect what is to be tesselated! + // all the polygons within the specific node are deemed to be contours, so + // any tesselation can be requested. +public: + + setTesselateVisitor():osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) { + } + virtual void apply(osg::Geode& geode) { + if (geode.getName().compare(0,9,"tesselate")==0) { + for(unsigned int i=0;i(geode.getDrawable(i)); + if (geom) { + osg::ref_ptr tscx=new osgUtil::Tesselator(); + if (tscx.valid()) { + tscx->setTesselationType(osgUtil::Tesselator::TESS_TYPE_GEOMETRY); + if (geode.getName()== "tesselate") { + // add a tesselator so that this geom is retesselated when N is pressed + tscx->setBoundaryOnly(true); + tscx->setWindingType( osgUtil::Tesselator::TESS_WINDING_ABS_GEQ_TWO); // so that first change in wind type makes the commonest tesselation - ODD. + geom->setUserData(tscx.get()); + } else if (geode.getName()== "tesselate odd") { + // OR you can just apply the tesselator once only, using these different types + tscx->setWindingType( osgUtil::Tesselator::TESS_WINDING_ODD); // commonest tesselation - ODD. + tscx->retesselatePolygons(*geom); + } else if (geode.getName()== "tesselate odd bound") { + tscx->setBoundaryOnly(true); + tscx->setWindingType( osgUtil::Tesselator::TESS_WINDING_ODD); // tesselation - ODD, only show boundary. + tscx->retesselatePolygons(*geom); + } else if (geode.getName()== "tesselate positive") { + tscx->setWindingType( osgUtil::Tesselator::TESS_WINDING_POSITIVE); // tesselation - pos. + tscx->retesselatePolygons(*geom); + } else if (geode.getName()== "tesselate positive bound") { + tscx->setBoundaryOnly(true); + tscx->setWindingType( osgUtil::Tesselator::TESS_WINDING_POSITIVE); + tscx->retesselatePolygons(*geom); + } else if (geode.getName()== "tesselate negative") { + tscx->setWindingType( osgUtil::Tesselator::TESS_WINDING_NEGATIVE); + tscx->retesselatePolygons(*geom); + } else if (geode.getName()== "tesselate negative bound") { + tscx->setBoundaryOnly(true); + tscx->setWindingType( osgUtil::Tesselator::TESS_WINDING_NEGATIVE); + tscx->retesselatePolygons(*geom); + } else if (geode.getName()== "tesselate nonzero") { + tscx->setWindingType( osgUtil::Tesselator::TESS_WINDING_NONZERO); + tscx->retesselatePolygons(*geom); + } else if (geode.getName()== "tesselate nonzero bound") { + tscx->setBoundaryOnly(true); + tscx->setWindingType( osgUtil::Tesselator::TESS_WINDING_NONZERO); + tscx->retesselatePolygons(*geom); + } else if (geode.getName()== "tesselate geq2") { + tscx->setWindingType( osgUtil::Tesselator::TESS_WINDING_ABS_GEQ_TWO); + tscx->retesselatePolygons(*geom); + } else if (geode.getName()== "tesselate geq2 bound") { + tscx->setBoundaryOnly(true); + tscx->setWindingType( osgUtil::Tesselator::TESS_WINDING_ABS_GEQ_TWO); + tscx->retesselatePolygons(*geom); + } + } + } + } + } + } +}; + +class cxTesselateVisitor : public osg::NodeVisitor +{ // special to this demo, traverses SG and finds nodes which have been tesselated + // for test/demo purposes these nodes are of type tesselateDemoGeometry + // but you could store the Tesselator as UserData or however you like. + // the tesselator holds copies of the original contours used in the tesselation + // In this visitor, I reuse the contours to make a different type of tesselation. +public: + + cxTesselateVisitor():osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) { + } + virtual void apply(osg::Geode& geode) { + + for(unsigned int i=0;i(geode.getDrawable(i)); + if (geom) { + if (!geom->getBoundaryOnly()) { // turn on bounds only + // NB this shows only the true boundary of the curves, no internal edges + geom->setBoundaryOnly(true); + + } else { // change to next type of tesselation... + geom->setBoundaryOnly(false); + switch (geom->getWindingType()) { + case osgUtil::Tesselator::TESS_WINDING_ODD: + geom->setWindingType(osgUtil::Tesselator::TESS_WINDING_NONZERO); + break; + case osgUtil::Tesselator::TESS_WINDING_NONZERO: + geom->setWindingType( osgUtil::Tesselator::TESS_WINDING_POSITIVE); + break; + case osgUtil::Tesselator::TESS_WINDING_POSITIVE: + geom->setWindingType( osgUtil::Tesselator::TESS_WINDING_NEGATIVE); + break; + case osgUtil::Tesselator::TESS_WINDING_NEGATIVE: + geom->setWindingType( osgUtil::Tesselator::TESS_WINDING_ABS_GEQ_TWO); + break; + case osgUtil::Tesselator::TESS_WINDING_ABS_GEQ_TWO: + geom->setWindingType( osgUtil::Tesselator::TESS_WINDING_ODD); + break; + } + } + + switch (geom->getWindingType()) { // a text to be added to the scene. + case osgUtil::Tesselator::TESS_WINDING_ODD: + str="TESS_WINDING_ODD"; + break; + case osgUtil::Tesselator::TESS_WINDING_NONZERO: + str="TESS_WINDING_NONZERO"; + break; + case osgUtil::Tesselator::TESS_WINDING_POSITIVE: + str="TESS_WINDING_POSITIVE"; + break; + case osgUtil::Tesselator::TESS_WINDING_NEGATIVE: + str="TESS_WINDING_NEGATIVE"; + break; + case osgUtil::Tesselator::TESS_WINDING_ABS_GEQ_TWO: + str="TESS_WINDING_ABS_GEQ_TWO"; + break; + } + if (geom->getBoundaryOnly()) str += " Boundary"; + + geom->retesselatePolygons(*geom); + } + osgText::Text* txt = dynamic_cast(geode.getDrawable(i)); + if (txt) { + const osg::Vec4& ct=txt->getColor(); // pick the text to be changed by its color + if (ct.z()<0.9) { + txt->setText(str.c_str()); + } + } + } + traverse(geode); + } + + std::string str; // a label for on screen display +}; + +class KeyboardEventHandler : public osgGA::GUIEventHandler +{ // extra event handler traps 'n' key to re-tesselate any tesselated geodes. +public: + + KeyboardEventHandler(osg::Node *nd): + _scene(nd) {} + + virtual bool handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter&) + { + switch(ea.getEventType()) + { + case(osgGA::GUIEventAdapter::KEYDOWN): + { + if (_scene && ea.getKey()=='n') + { + // re-tesselate the scene graph. + // the same contours are re-tesselated using a new method. Old contours + // & tesselation type are held internally in the derived Geode class tesselateDemoGeometry. + cxTesselateVisitor tsv; + _scene->accept(tsv); + return true; + } + break; + } + default: + break; + } + return false; + } + + virtual void accept(osgGA::GUIEventHandlerVisitor& v) + { + v.visit(*this); + } + + osg::Node *_scene; + +}; + + +int main( int argc, char **argv ) +{ + + // use an ArgumentParser object to manage the program arguments. + osg::ArgumentParser arguments(&argc,argv); + + // set up the usage document, in case we need to print out how to use this program. + arguments.getApplicationUsage()->setApplicationName(arguments.getApplicationName()); + arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+" is the standard OpenSceneGraph example which loads and visualises 3d models."); + arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] filename ..."); + arguments.getApplicationUsage()->addCommandLineOption("-h or --help","Display this information"); + + + // construct the viewer. + osgProducer::Viewer viewer(arguments); + + // set up the value with sensible default event handlers. + viewer.setUpViewer(osgProducer::Viewer::STANDARD_SETTINGS); + + // get details on keyboard and mouse bindings used by the viewer. + viewer.getUsage(*arguments.getApplicationUsage()); + + // if user request help write it out to cout. + if (arguments.read("-h") || arguments.read("--help")) + { + arguments.getApplicationUsage()->write(std::cout); + return 1; + } + + // report any errors if they have occured when parsing the program aguments. + if (arguments.errors()) + { + arguments.writeErrorMessages(std::cout); + return 1; + } + + if (arguments.argc()<=1) + { + arguments.getApplicationUsage()->write(std::cout,osg::ApplicationUsage::COMMAND_LINE_OPTION); + /// return 1; + } + + osg::Timer timer; + osg::Timer_t start_tick = timer.tick(); + + // read the scene from the list of file specified commandline args. + osg::ref_ptr loadedModel = osgDB::readNodeFiles(arguments); + + // if no model has been successfully loaded report failure. + if (!loadedModel) + { + loadedModel=makeTesselateExample(); + } else { // if there is a loaded model: + // tesselate by searching for geode called tesselate & tesselate it + setTesselateVisitor tsv; + loadedModel->accept(tsv); + } + + // create the hud. + osg::Group *gload= dynamic_cast (loadedModel.get()); + gload->addChild(createHUD()); + + + // any option left unread are converted into errors to write out later. + arguments.reportRemainingOptionsAsUnrecognized(); + + // report any errors if they have occured when parsing the program aguments. + if (arguments.errors()) + { + arguments.writeErrorMessages(std::cout); + } + + osg::Timer_t end_tick = timer.tick(); + + std::cout << "Time to load = "< VertexPointList; struct Prim : public osg::Referenced @@ -71,12 +103,21 @@ class OSGUTIL_EXPORT Tesselator PrimList& getPrimList() { return _primList; } - void retesselatePolygons(osg::Geometry& geom); - void reset(); protected: + + /** remove unused parts of the array, eg for wehn retesselating + * tesselation can introduce extra vertices for concave or crossing boundaries, + * these will leak memory if not removed when retesselating. */ + void reduceArray(osg::Array * cold, const unsigned int nnu); + + void collectTesselation(osg::Geometry &cxgeom); + typedef std::map VertexPtrToIndexMap; + void addContour(osg::PrimitiveSet* primitive, osg::Vec3Array* vertices); + void handleNewVertices(osg::Geometry& geom,VertexPtrToIndexMap &vertexPtrToIndexMap); + void begin(GLenum mode); void vertex(osg::Vec3* vertex); void combine(osg::Vec3* vertex,void* vertex_data[4],GLfloat weight[4]); @@ -103,6 +144,7 @@ class OSGUTIL_EXPORT Tesselator { NewVertex(): + _vpos(0), _f1(0), _v1(0), _f2(0), @@ -113,6 +155,7 @@ class OSGUTIL_EXPORT Tesselator _v4(0) {} NewVertex(const NewVertex& nv): + _vpos(nv._vpos), _f1(nv._f1), _v1(nv._v1), _f2(nv._f2), @@ -122,10 +165,12 @@ class OSGUTIL_EXPORT Tesselator _f4(nv._f4), _v4(nv._v4) {} - NewVertex(float f1,osg::Vec3* v1, + NewVertex(osg::Vec3* vx, + float f1,osg::Vec3* v1, float f2,osg::Vec3* v2, float f3,osg::Vec3* v3, float f4,osg::Vec3* v4): + _vpos(vx), _f1(f1), _v1(v1), _f2(f2), @@ -134,6 +179,8 @@ class OSGUTIL_EXPORT Tesselator _v3(v3), _f4(f4), _v4(v4) {} + + osg::Vec3 *_vpos; // added gwm Jan 2004 the vertex coords s.t. NewVertex can be used in a std::vector float _f1; osg::Vec3* _v1; @@ -149,7 +196,10 @@ class OSGUTIL_EXPORT Tesselator }; - typedef std::map NewVertexList; + //change NewVertexList from std::map NewVertexList; + // because this has undefined order of insertion for new vertices. + // which occasionally corrupted the texture mapping. + typedef std::vector NewVertexList; typedef std::vector Vec3dList; GLUtesselator* _tobj; @@ -158,6 +208,22 @@ class OSGUTIL_EXPORT Tesselator NewVertexList _newVertexList; GLenum _errorCode; + /** winding rule, which parts will become solid */ + WindingType _wtype; + + /** tesselation rule, which parts will become solid */ + TesselationType _ttype; + + bool _boundaryOnly; // see gluTessProperty - if true: make the boundary edges only. + + /** number of vertices that are part of the 'original' set of contours */ + unsigned int _numberVerts; + + /** List of primitives that define the contours */ + osg::Geometry::PrimitiveSetList _Contours; + + /** count number of primitives in a geometry to get right no. of norms/colurs etc for per_primitive attributes. */ + unsigned int _index; }; } diff --git a/src/osgUtil/Tesselator.cpp b/src/osgUtil/Tesselator.cpp index 70012e850..390081c03 100644 --- a/src/osgUtil/Tesselator.cpp +++ b/src/osgUtil/Tesselator.cpp @@ -19,10 +19,15 @@ using namespace osg; using namespace osgUtil; -Tesselator::Tesselator() + +Tesselator::Tesselator() : + _wtype(TESS_WINDING_ODD), + _ttype(TESS_TYPE_POLYGONS), + _boundaryOnly(false), _numberVerts(0) { _tobj = 0; _errorCode = 0; + _index=0; } Tesselator::~Tesselator() @@ -145,11 +150,12 @@ class InsertNewVertices : public osg::ArrayVisitor virtual void apply(osg::Vec4Array& ba) { apply_imp(ba,Vec4()); } }; - -void Tesselator::retesselatePolygons(osg::Geometry& geom) +void Tesselator::retesselatePolygons(osg::Geometry &geom) { + // turn the contour list into primitives, a little like tesselator does but more generally osg::Vec3Array* vertices = dynamic_cast(geom.getVertexArray()); + if (!vertices || vertices->empty() || geom.getPrimitiveSetList().empty()) return; @@ -166,246 +172,285 @@ void Tesselator::retesselatePolygons(osg::Geometry& geom) if (geom.getTexCoordIndices(unit)) return; } - // process the primitives - int noPrimitiveAtStart = geom.getPrimitiveSetList().size(); - for(int primNo=0;primNogetMode()==osg::PrimitiveSet::POLYGON && primitive->getNumIndices()>3) - { - beginTesselation(); - beginContour(); + if (_ttype==TESS_TYPE_POLYGONS || _ttype==TESS_TYPE_DRAWABLE) _numberVerts=0; // 09.04.04 GWM reset tesselator + // the reset is needed by the flt loader which reuses a tesselator for triangulating polygons. + // as such it might be reset by other loaders/developers in future. + _index=0; // reset the counter for indexed vertices + if (!_numberVerts) { + _numberVerts=geom.getVertexArray()->getNumElements(); + // save the contours for complex (winding rule) tesselations + _Contours=geom.getPrimitiveSetList(); + } - switch(primitive->getType()) + // now cut out vertex attributes added on any previous tesselation + reduceArray(geom.getVertexArray(), _numberVerts); + reduceArray(geom.getColorArray(), _numberVerts); + reduceArray(geom.getNormalArray(), _numberVerts); + reduceArray(geom.getFogCoordArray(), _numberVerts); + for(unsigned int unit1=0;unit1 primitive = _Contours[primNo].get(); + if (_ttype==TESS_TYPE_POLYGONS || _ttype==TESS_TYPE_DRAWABLE) { + if (primitive->getMode()==osg::PrimitiveSet::POLYGON ||_ttype==TESS_TYPE_DRAWABLE) { + beginTesselation(); + addContour(primitive.get(), vertices); + endTesselation(); + + collectTesselation(geom); + } else { // copy the contour primitive as it is not being tesselated + geom.addPrimitiveSet(primitive.get()); + } + } else { + if (primitive->getMode()==osg::PrimitiveSet::POLYGON || + primitive->getMode()==osg::PrimitiveSet::QUADS || + primitive->getMode()==osg::PrimitiveSet::TRIANGLES || + primitive->getMode()==osg::PrimitiveSet::LINE_LOOP || + primitive->getMode()==osg::PrimitiveSet::QUAD_STRIP || + primitive->getMode()==osg::PrimitiveSet::TRIANGLE_FAN || + primitive->getMode()==osg::PrimitiveSet::TRIANGLE_STRIP) { - case(osg::PrimitiveSet::DrawArraysPrimitiveType): + addContour(primitive.get(), vertices); + } else { // copy the contour primitive as it is not being tesselated + // in this case points, lines or line_strip + geom.addPrimitiveSet(primitive.get()); + } + } + } + if (_ttype==TESS_TYPE_GEOMETRY) { + endTesselation(); + + collectTesselation(geom); + } +} + +void Tesselator::addContour(osg::PrimitiveSet* primitive, osg::Vec3Array* vertices) +{ + // adds a single primitive as a contour. + beginContour(); + unsigned int nperprim=0; // number of vertices per primitive + if (primitive->getMode()==osg::PrimitiveSet::QUADS) nperprim=4; + if (primitive->getMode()==osg::PrimitiveSet::TRIANGLES) nperprim=3; + unsigned int idx=0; + + switch(primitive->getType()) + { + case(osg::PrimitiveSet::DrawArraysPrimitiveType): + { + unsigned int i; + osg::DrawArrays* drawArray = static_cast(primitive); + unsigned int first = drawArray->getFirst(); + unsigned int last = first+drawArray->getCount(); + + switch (primitive->getMode()) { + case osg::PrimitiveSet::QUADS: + case osg::PrimitiveSet::TRIANGLES: + case osg::PrimitiveSet::POLYGON: + case osg::PrimitiveSet::LINE_LOOP: + case osg::PrimitiveSet::TRIANGLE_FAN: { - osg::DrawArrays* drawArray = static_cast(primitive); - unsigned int first = drawArray->getFirst(); - unsigned int last = first+drawArray->getCount(); - for(unsigned int i=first;i0 && i=first;i-=2) + { // ...5,3,1 + addVertex(&((*vertices)[i])); + } + } + break; + case osg::PrimitiveSet::TRIANGLE_STRIP: + { + for( i=first;ifirst&& i(primitive); - for(osg::DrawElementsUByte::iterator indexItr=drawElements->begin(); - indexItr!=drawElements->end(); - ++indexItr) + for(i=first;i0 && i(primitive); - for(osg::DrawElementsUShort::iterator indexItr=drawElements->begin(); - indexItr!=drawElements->end(); - ++indexItr) - { - addVertex(&((*vertices)[*indexItr])); - } - break; - } - case(osg::PrimitiveSet::DrawElementsUIntPrimitiveType): - { - osg::DrawElementsUInt* drawElements = static_cast(primitive); - for(osg::DrawElementsUInt::iterator indexItr=drawElements->begin(); - indexItr!=drawElements->end(); - ++indexItr) - { - addVertex(&((*vertices)[*indexItr])); - } - break; - } - default: - break; + break; } - - endContour(); - endTesselation(); - - typedef std::map VertexPtrToIndexMap; - VertexPtrToIndexMap vertexPtrToIndexMap; - - // populate the VertexPtrToIndexMap. - for(unsigned int vi=0;visize();++vi) + break; + } + case(osg::PrimitiveSet::DrawElementsUBytePrimitiveType): + { + osg::DrawElementsUByte* drawElements = static_cast(primitive); + for(osg::DrawElementsUByte::iterator indexItr=drawElements->begin(); + indexItr!=drawElements->end(); + ++indexItr, idx++) { - vertexPtrToIndexMap[&((*vertices)[vi])] = vi; - } - - if (!_newVertexList.empty()) - { - - osg::Vec3Array* normals = NULL; - if (geom.getNormalBinding()==osg::Geometry::BIND_PER_VERTEX) - { - normals = geom.getNormalArray(); + addVertex(&((*vertices)[*indexItr])); + if (nperprim>0 && indexItr!=drawElements->end() && idx%nperprim==nperprim-1) { + endContour(); + beginContour(); } - - typedef std::vector ArrayList; - ArrayList arrays; + } + break; + } + case(osg::PrimitiveSet::DrawElementsUShortPrimitiveType): + { + osg::DrawElementsUShort* drawElements = static_cast(primitive); + for(osg::DrawElementsUShort::iterator indexItr=drawElements->begin(); + indexItr!=drawElements->end(); + ++indexItr, idx++) + { + addVertex(&((*vertices)[*indexItr])); + if (nperprim>0 && indexItr!=drawElements->end() && idx%nperprim==nperprim-1) { + endContour(); + beginContour(); + } + } + break; + } + case(osg::PrimitiveSet::DrawElementsUIntPrimitiveType): + { + osg::DrawElementsUInt* drawElements = static_cast(primitive); + for(osg::DrawElementsUInt::iterator indexItr=drawElements->begin(); + indexItr!=drawElements->end(); + ++indexItr, idx++) + { + addVertex(&((*vertices)[*indexItr])); + if (nperprim>0 && indexItr!=drawElements->end() && idx%nperprim==nperprim-1) { + endContour(); + beginContour(); + } + } + break; + } + default: + break; + } - if (geom.getColorBinding()==osg::Geometry::BIND_PER_VERTEX) - { - arrays.push_back(geom.getColorArray()); - } - - if (geom.getSecondaryColorBinding()==osg::Geometry::BIND_PER_VERTEX) - { - arrays.push_back(geom.getSecondaryColorArray()); - } + endContour(); +} - if (geom.getFogCoordBinding()==osg::Geometry::BIND_PER_VERTEX) - { - arrays.push_back(geom.getFogCoordArray()); - } - - osg::Geometry::ArrayList& tcal = geom.getTexCoordArrayList(); - for(osg::Geometry::ArrayList::iterator tcalItr=tcal.begin(); - tcalItr!=tcal.end(); - ++tcalItr) - { - if (tcalItr->array.valid()) - { - arrays.push_back(tcalItr->array.get()); - } - } - - // now add any new vertices that are required. - for(NewVertexList::iterator itr=_newVertexList.begin(); - itr!=_newVertexList.end(); - ++itr) - { - osg::Vec3* vertex = itr->first; - NewVertex& newVertex = itr->second; - - // assign vertex. - vertexPtrToIndexMap[vertex]=vertices->size(); - vertices->push_back(*vertex); - - // assign normals - if (normals) - { - osg::Vec3 norm(0.0f,0.0f,0.0f); - if (newVertex._v1) norm += (*normals)[vertexPtrToIndexMap[newVertex._v1]] * newVertex._f1; - if (newVertex._v2) norm += (*normals)[vertexPtrToIndexMap[newVertex._v2]] * newVertex._f2; - if (newVertex._v3) norm += (*normals)[vertexPtrToIndexMap[newVertex._v3]] * newVertex._f3; - if (newVertex._v4) norm += (*normals)[vertexPtrToIndexMap[newVertex._v4]] * newVertex._f4; - norm.normalize(); - normals->push_back(norm); - } - - if (!arrays.empty()) - { - InsertNewVertices inv(newVertex._f1,vertexPtrToIndexMap[newVertex._v1], - newVertex._f2,vertexPtrToIndexMap[newVertex._v2], - newVertex._f3,vertexPtrToIndexMap[newVertex._v3], - newVertex._f4,vertexPtrToIndexMap[newVertex._v4]); - - // assign the rest of the attributes. - for(ArrayList::iterator aItr=arrays.begin(); - aItr!=arrays.end(); - ++aItr) - { - (*aItr)->accept(inv); - } - } - } - - } - - - // we don't properly handle per primitive and per primitive_set bindings yet - // will need to address this soon. Robert Oct 2002. +void Tesselator::handleNewVertices(osg::Geometry& geom,VertexPtrToIndexMap &vertexPtrToIndexMap) +{ + if (!_newVertexList.empty()) + { + + osg::Vec3Array* vertices = dynamic_cast(geom.getVertexArray()); + osg::Vec3Array* normals = NULL; + if (geom.getNormalBinding()==osg::Geometry::BIND_PER_VERTEX) + { + normals = geom.getNormalArray(); + } + + typedef std::vector ArrayList; + ArrayList arrays; + + if (geom.getColorBinding()==osg::Geometry::BIND_PER_VERTEX) + { + arrays.push_back(geom.getColorArray()); + } + + if (geom.getSecondaryColorBinding()==osg::Geometry::BIND_PER_VERTEX) + { + arrays.push_back(geom.getSecondaryColorArray()); + } + + if (geom.getFogCoordBinding()==osg::Geometry::BIND_PER_VERTEX) + { + arrays.push_back(geom.getFogCoordArray()); + } + + osg::Geometry::ArrayList& tcal = geom.getTexCoordArrayList(); + for(osg::Geometry::ArrayList::iterator tcalItr=tcal.begin(); + tcalItr!=tcal.end(); + ++tcalItr) + { + if (tcalItr->array.valid()) + { + arrays.push_back(tcalItr->array.get()); + } + } + + // now add any new vertices that are required. + for(NewVertexList::iterator itr=_newVertexList.begin(); + itr!=_newVertexList.end(); + ++itr) + { + NewVertex& newVertex = (*itr); + osg::Vec3* vertex = newVertex._vpos; + + // assign vertex. + vertexPtrToIndexMap[vertex]=vertices->size(); + vertices->push_back(*vertex); + + // assign normals + if (normals) { - osg::Vec3Array* normals = NULL; // GWM Sep 2002 - add normals for extra facets - int iprim=0; - if (geom.getNormalBinding()==osg::Geometry::BIND_PER_PRIMITIVE || - geom.getNormalBinding()==osg::Geometry::BIND_PER_PRIMITIVE_SET) - { - normals = geom.getNormalArray(); // GWM Sep 2002 - } - // GWM Dec 2003 - nneded to add colours for extra facets - osg::Vec4Array* cols4 = NULL; // GWM Dec 2003 colours are vec4 - osg::Vec3Array* cols3 = NULL; // GWM Dec 2003 colours are vec3 - if (geom.getColorBinding()==osg::Geometry::BIND_PER_PRIMITIVE/* || - geom.getColorBinding()==osg::Geometry::BIND_PER_PRIMITIVE_SET*/) - { - Array* colours = geom.getColorArray(); // GWM Dec 2003 - need to duplicate face colours - switch (colours->getType()) { - case osg::Array::Vec4ArrayType: - cols4=dynamic_cast (colours); - break; - case osg::Array::Vec3ArrayType: - cols3=dynamic_cast (colours); - break; - default: - // not handled cases - break; - } - - } - // GWM Dec 2003 - these holders need to go outside the loop to - // retain the flat shaded colour &/or normal for each tesselated polygon osg::Vec3 norm(0.0f,0.0f,0.0f); - osg::Vec4 primCol4(0.0f,0.0f,0.0f,1.0f); - osg::Vec3 primCol3(0.0f,0.0f,0.0f); - for(PrimList::iterator primItr=_primList.begin(); - primItr!=_primList.end(); - ++primItr) - { - Prim* prim = primItr->get(); - osg::Vec3 norm(0.0f,0.0f,0.0f); - - osg::DrawElementsUShort* elements = new osg::DrawElementsUShort(prim->_mode); - for(Prim::VecList::iterator vitr=prim->_vertices.begin(); - vitr!=prim->_vertices.end(); - ++vitr) - { - elements->push_back(vertexPtrToIndexMap[*vitr]); - } - - if (primItr==_primList.begin()) - { // first new primitive so overwrite the previous polygon & collect primitive normal & colour. - geom.getPrimitiveSetList()[primNo] = elements; - if (normals) { - norm=(*normals)[iprim]; // GWM Sep 2002 the flat shaded normal - } - if (cols4) { - primCol4=(*cols4)[iprim]; // GWM Dec 2003 the flat shaded rgba colour - } - if (cols3) { - primCol3=(*cols3)[iprim]; // GWM Dec 2003 flat shaded rgb colour - } - } - else - { - // subsequent primitives add to the back of the primitive list, and may have same colour as hte original facet. - geom.addPrimitiveSet(elements); - if (normals) normals->push_back(norm); // GWM Sep 2002 add flat shaded normal for new facet - if (cols4) cols4->push_back(primCol4); // GWM Dec 2003 add flat shaded colour for new facet - if (cols3) cols3->push_back(primCol3); // GWM Dec 2003 add flat shaded colour for new facet - if (prim->_mode==GL_TRIANGLES) { // also need one per triangle? - int ntris=elements->getNumIndices()/3; - for (int ii=1; iipush_back(norm); // GWM Sep 2002 add flat shaded normal for new facet - if (cols4) cols4->push_back(primCol4); - } - } - // osg::notify(osg::WARN)<<"Add: "<< iprim << std::endl; - } - iprim++; // GWM Sep 2002 count which normal we should use - } + if (newVertex._v1) norm += (*normals)[vertexPtrToIndexMap[newVertex._v1]] * newVertex._f1; + if (newVertex._v2) norm += (*normals)[vertexPtrToIndexMap[newVertex._v2]] * newVertex._f2; + if (newVertex._v3) norm += (*normals)[vertexPtrToIndexMap[newVertex._v3]] * newVertex._f3; + if (newVertex._v4) norm += (*normals)[vertexPtrToIndexMap[newVertex._v4]] * newVertex._f4; + norm.normalize(); + normals->push_back(norm); } + if (!arrays.empty()) + { + InsertNewVertices inv(newVertex._f1,vertexPtrToIndexMap[newVertex._v1], + newVertex._f2,vertexPtrToIndexMap[newVertex._v2], + newVertex._f3,vertexPtrToIndexMap[newVertex._v3], + newVertex._f4,vertexPtrToIndexMap[newVertex._v4]); + + // assign the rest of the attributes. + for(ArrayList::iterator aItr=arrays.begin(); + aItr!=arrays.end(); + ++aItr) + { + (*aItr)->accept(inv); + } + } } } + } void Tesselator::begin(GLenum mode) @@ -425,10 +470,11 @@ void Tesselator::vertex(osg::Vec3* vertex) void Tesselator::combine(osg::Vec3* vertex,void* vertex_data[4],GLfloat weight[4]) { - _newVertexList[vertex]=NewVertex(weight[0],(Vec3*)vertex_data[0], + _newVertexList.push_back(NewVertex(vertex, + weight[0],(Vec3*)vertex_data[0], weight[1],(Vec3*)vertex_data[1], weight[2],(Vec3*)vertex_data[2], - weight[3],(Vec3*)vertex_data[3]); + weight[3],(Vec3*)vertex_data[3])); } void Tesselator::end() @@ -469,3 +515,204 @@ void CALLBACK Tesselator::errorCallback(GLenum errorCode, void* userData) { ((Tesselator*)userData)->error(errorCode); } + +void Tesselator::reduceArray(osg::Array * cold, const unsigned int nnu) +{ // shrinks size of array to N + if (cold && cold->getNumElements()>nnu) { + osg::Vec2Array* v2arr = NULL; + osg::Vec3Array* v3arr = NULL; + osg::Vec4Array* v4arr = NULL; + switch (cold->getType()) { + case osg::Array::Vec2ArrayType: { + v2arr = dynamic_cast(cold); + osg::Vec2Array::iterator itr=v2arr->begin()+nnu; + (*v2arr).erase(itr, v2arr->end()); + } + break; + case osg::Array::Vec3ArrayType: { + v3arr = dynamic_cast(cold); + osg::Vec3Array::iterator itr=v3arr->begin()+nnu; + (*v3arr).erase(itr, v3arr->end()); + } + break; + case osg::Array::Vec4ArrayType: { + v4arr = dynamic_cast(cold); + osg::Vec4Array::iterator itr=v4arr->begin()+nnu; + (*v4arr).erase(itr, v4arr->end()); + } + break; + default: // should also handle:ArrayType' ByteArrayType' ShortArrayType' IntArrayType' + // `UShortArrayType' `UIntArrayType' `UByte4ArrayType' `FloatArrayType' + break; + } + } +} + +unsigned int _computeNumberOfPrimitives(const osg::Geometry& geom) +{ + + unsigned int totalNumberOfPrimitives = 0; + + for(Geometry::PrimitiveSetList::const_iterator itr=geom.getPrimitiveSetList().begin(); + itr!=geom.getPrimitiveSetList().end(); + ++itr) + { + const PrimitiveSet* primitiveset = itr->get(); + GLenum mode=primitiveset->getMode(); + + unsigned int primLength; + switch(mode) + { + case(GL_POINTS): primLength=1; break; + case(GL_LINES): primLength=2; break; + case(GL_TRIANGLES): primLength=3; break; + case(GL_QUADS): primLength=4; break; + default: primLength=0; break; // compute later when =0. + } + + // draw primtives by the more flexible "slow" path, + // sending OpenGL glBegin/glVertex.../glEnd(). + switch(primitiveset->getType()) + { + case(PrimitiveSet::DrawArrayLengthsPrimitiveType): + { + + const DrawArrayLengths* drawArrayLengths = static_cast(primitiveset); + for(DrawArrayLengths::const_iterator primItr=drawArrayLengths->begin(); + primItr!=drawArrayLengths->end(); + ++primItr) + { + if (primLength==0) totalNumberOfPrimitives += 1; + else totalNumberOfPrimitives += *primItr/primLength; // Dec 2003 - increment not set + } + break; + } + default: + { + if (primLength==0) totalNumberOfPrimitives += 1; + else totalNumberOfPrimitives += primitiveset->getNumIndices()/primLength; + } + } + } + + return totalNumberOfPrimitives; +} +// +void Tesselator::collectTesselation(osg::Geometry &geom) +{ + osg::Vec3Array* vertices = dynamic_cast(geom.getVertexArray()); + VertexPtrToIndexMap vertexPtrToIndexMap; + + // populate the VertexPtrToIndexMap. + for(unsigned int vi=0;visize();++vi) + { + vertexPtrToIndexMap[&((*vertices)[vi])] = vi; + } + + handleNewVertices(geom, vertexPtrToIndexMap); + + // we don't properly handle per primitive and per primitive_set bindings yet + // will need to address this soon. Robert Oct 2002. + { + osg::Vec3Array* normals = NULL; // GWM Sep 2002 - add normals for extra facets + int iprim=0; + if (geom.getNormalBinding()==osg::Geometry::BIND_PER_PRIMITIVE || + geom.getNormalBinding()==osg::Geometry::BIND_PER_PRIMITIVE_SET) + { + normals = geom.getNormalArray(); // GWM Sep 2002 + } + // GWM Dec 2003 - needed to add colours for extra facets + osg::Vec4Array* cols4 = NULL; // GWM Dec 2003 colours are vec4 + osg::Vec3Array* cols3 = NULL; // GWM Dec 2003 colours are vec3 + if (geom.getColorBinding()==osg::Geometry::BIND_PER_PRIMITIVE || + geom.getColorBinding()==osg::Geometry::BIND_PER_PRIMITIVE_SET) + { + Array* colours = geom.getColorArray(); // GWM Dec 2003 - need to duplicate face colours + switch (colours->getType()) { + case osg::Array::Vec4ArrayType: + cols4=dynamic_cast (colours); + break; + case osg::Array::Vec3ArrayType: + cols3=dynamic_cast (colours); + break; + default: + break; + } + + } + // GWM Dec 2003 - these holders need to go outside the loop to + // retain the flat shaded colour &/or normal for each tesselated polygon + osg::Vec3 norm(0.0f,0.0f,0.0f); + osg::Vec4 primCol4(0.0f,0.0f,0.0f,1.0f); + osg::Vec3 primCol3(0.0f,0.0f,0.0f); + + for(PrimList::iterator primItr=_primList.begin(); + primItr!=_primList.end(); + ++primItr, ++_index) + { + Prim* prim = primItr->get(); + + osg::DrawElementsUShort* elements = new osg::DrawElementsUShort(prim->_mode); + for(Prim::VecList::iterator vitr=prim->_vertices.begin(); + vitr!=prim->_vertices.end(); + ++vitr) + { + elements->push_back(vertexPtrToIndexMap[*vitr]); + } + + // add to the drawn primitive list. + geom.addPrimitiveSet(elements); + if (primItr==_primList.begin()) + { // first primitive so collect primitive normal & colour. + if (normals) { + norm=(*normals)[iprim]; // GWM Sep 2002 the flat shaded normal + } + if (cols4) { + primCol4=(*cols4)[iprim]; // GWM Dec 2003 the flat shaded rgba colour + if (_index>=cols4->size()) { + cols4->push_back(primCol4); // GWM Dec 2003 add flat shaded colour for new facet + } + } + if (cols3) { + primCol3=(*cols3)[iprim]; // GWM Dec 2003 flat shaded rgb colour + if (_index>=cols4->size()) { + cols3->push_back(primCol3); // GWM Dec 2003 add flat shaded colour for new facet + } + } + } + else + { // later primitives use same colour + if (normals) normals->push_back(norm); // GWM Sep 2002 add flat shaded normal for new facet + if (cols4 && _index>=cols4->size()) { + cols4->push_back(primCol4); // GWM Dec 2003 add flat shaded colour for new facet + } + if (cols3 && _index>=cols3->size()) { + if (cols3) cols3->push_back(primCol3); // GWM Dec 2003 add flat shaded colour for new facet + } + if (prim->_mode==GL_TRIANGLES) { + int ntris=elements->getNumIndices()/3; + if (geom.getNormalBinding()==osg::Geometry::BIND_PER_PRIMITIVE_SET || + geom.getNormalBinding()==osg::Geometry::BIND_PER_PRIMITIVE) { // need one per triangle? Not one per set. + for (int ii=1; iipush_back(norm); // GWM Sep 2002 add flat shaded normal for new facet + } + } + if (geom.getColorBinding()==osg::Geometry::BIND_PER_PRIMITIVE_SET || + geom.getColorBinding()==osg::Geometry::BIND_PER_PRIMITIVE) { // need one per triangle? Not one per set. + for (int ii=1; ii=cols3->size()) { + if (cols3) cols3->push_back(primCol3); + } + if (cols4 && _index>=cols4->size()) { + if (cols4) cols4->push_back(primCol4); + } + _index++; + } + } + } + // osg::notify(osg::WARN)<<"Add: "<< iprim << std::endl; + } + iprim++; // GWM Sep 2002 count which normal we should use + } + } +}