Index: /anuga_core/source/anuga_viewer/INSTALL.txt
===================================================================
--- /anuga_core/source/anuga_viewer/INSTALL.txt	(revision 3887)
+++ /anuga_core/source/anuga_viewer/INSTALL.txt	(revision 3887)
@@ -0,0 +1,132 @@
+
+
+BUILDING FROM SOURCE
+
+1) The pyVolution Visualizer has several dependencies. 
+
+   OpenThreads, Producer, OpenSceneGraph:
+            - http://openscenegraph.sourceforge.net
+            - available as a bundle, OSG_OP_OT-0.9.8, that contains
+              OpenThreads, (Open)Producer and OpenSceneGraph as 
+              subdirectories.  
+            - OpenSceneGraph has 3rd party dependencies, these are
+              available as a downloadable .zip for Windows.  Extracted
+              .zip directory (3rdParty) lives alongside other three
+              subdirectories.
+            - Build "release" versions in above order
+            - ensure you can run the examples that come with OSG
+              prior to building the viewer (these live in the 
+              OpenSceneGraph/bin directory on Windows).
+
+   NetCDF   - a self-describing binary file format I/O library
+            - http://my.unidata.ucar.edu/content/software/netcdf/index.html
+            - require the header file netcdf.h and the library proper to
+              link against
+
+   cppUnit  - a unit testing framework for C++
+            - http://cppunit.sourceforge.net
+            - this is optional
+
+
+2) Set environment variables:
+
+   OSG_ROOT :  root of your OpenSceneGraph installation
+               conforming to following layout
+
+               OSG_ROOT
+                 /OpenThreads
+                    /lib
+                       /win32 (for VisualStudio builds)
+                    /include
+                 /Producer
+                    /lib
+                    /include
+                 /OpenSceneGraph
+                    /lib
+                    /include
+                 /3rdParty (Windows only)
+
+   NETCDF_DIR :  root of NetCDF binary package with needed subdirs 
+                 /lib and /include
+
+   CPPUNIT :  root of c++ unit testing framework with needed subdirs 
+              /lib and /include (optional) 
+
+
+3) Compilation
+
+
+   WINDOWS
+
+      (i) Within VisualStudio, Open the solution in Swollen visualstudio subdirectory
+     (ii) Open Menu Tools|Options|Projects|VC++Directories and add a
+          path to $(NETCDF_DIR)/bin and $(OSG_ROOT)/bin.
+     (iii) Build the solution
+
+
+   Mac OSX
+
+          Run make in top-level OSG_ROOT directory.
+
+
+   Linux
+
+          Makefiles not yet written
+
+
+
+4) The binary swollen (swollen.exe on Windows) lives in the distribution's
+   bin directory.  Test the build with any of the sample datasets in the
+   distribution's data subdirectory, e.g., 
+         "cd bin; ./swollen ../data/laminar.sww"
+	 
+
+=========================================	 	 
+RUNNING SWOLLEN
+
+Fix for swollen not locating sky and water texture images when binary
+found using PATH environment variable. The fix is to set an
+environment variable SWOLLEN_BINDIR pointing to your distro
+directory. This is not an optimal approach, but it works.
+
+
+
+=========================================
+STEREO
+
+Invoke through the commandline parameter --stereo
+e.g. 
+  swollen --stereo lwru2_variable_mesh.sww 
+or  
+  swollen --stereo HORIZONTAL_SPLIT lwru2_variable_mesh.sww 
+
+See 
+  swollen --help for more info  
+  
+  
+Minor tweaks to re-enable stereo functionality. I have verified that this works on the vizlab active stereo system. For the GA passive stereo system, youll need to set the following environment variables.
+
+Environment Variable Name Value Description 
+OSG_STEREO_MODE HORIZONTAL_SPLIT Use horizontal split stereo mode when in stereo 
+OSG_SCREEN_DISTANCE 0.50 Set the distance the viewer is from screen in metres (default shown) 
+OSG_SCREEN_HEIGHT 0.26 Set the height of image on the screen in metres (default shown) 
+OSG_SCREEN_WIDTH 0.325 Set the width of image on the screen in metres (default shown) 
+OSG_EYE_SEPARATION 0.06 Set the eye separation  interoccular distance (default shown.) 
+OSG_SPLIT_STEREO_HORIZONTAL_SEPARATION 42 Set the number of pixels between the left and right viewports (default shown) 
+
+The default physical dimensions are for a computer monitor. These need to be adjusted for the larger setup. Typical values might be 2m viewing distance from screen, screen dimensions of 2m x 1.5m. Eye separation obviously stays the same. Then just start swollen with the dash dash stereo flag.
+
+The GA edge:
+OSG_STEREO_MODE QUAD_BUFFER
+OSG_SCREEN_DISTANCE 3.0 
+OSG_SCREEN_HEIGHT 2.0
+OSG_SCREEN_WIDTH 2.66
+OSG_EYE_SEPARATION 0.06 
+OSG_SPLIT_STEREO_HORIZONTAL_SEPARATION 0
+
+	 
+===========DEBUG
+
+set OSG_NOTIFY_LEVEL=DEBUG
+
+
Index: /anuga_core/source/anuga_viewer/Makefile
===================================================================
--- /anuga_core/source/anuga_viewer/Makefile	(revision 3887)
+++ /anuga_core/source/anuga_viewer/Makefile	(revision 3887)
@@ -0,0 +1,12 @@
+
+
+# top-level makefile
+
+
+all:
+	cd swwreader; make
+	cd swollen; make
+
+clean:
+	cd swwreader; make clean
+	cd swollen; make clean
Index: /anuga_core/source/anuga_viewer/README.txt
===================================================================
--- /anuga_core/source/anuga_viewer/README.txt	(revision 3887)
+++ /anuga_core/source/anuga_viewer/README.txt	(revision 3887)
@@ -0,0 +1,1 @@
+
Index: /anuga_core/source/anuga_viewer/distros/windistro.py
===================================================================
--- /anuga_core/source/anuga_viewer/distros/windistro.py	(revision 3887)
+++ /anuga_core/source/anuga_viewer/distros/windistro.py	(revision 3887)
@@ -0,0 +1,96 @@
+#
+# Python script to create a windows distribution,
+# currently a gzipped tar file.  I had a
+# fancy installer but (i) it required a separate
+# 23Mb download for the .NET framework and (ii) it
+# probably wouldn't have run at GA anyway.
+#
+
+
+# convenience function for building file list
+import os
+def DLLFiles( directory, list ):
+    return [ os.sep.join( [directory, name+'.dll'] ) for name in list ]
+
+
+#---- NAME ------------------------------------------------------------
+
+from datetime import datetime
+distro = datetime.today().strftime('distro%Y%m%d.tgz')
+
+
+  
+#---- LOCAL DEPENDENCIES ----------------------------------------------
+
+# local directories
+localrootdir = '..'
+localbindir = localrootdir + os.sep + 'bin'
+
+# local files
+localdlls = DLLFiles( localbindir, ['swwreader'] )
+localfiles = [ os.sep.join( [localbindir, f] ) for f in \
+               ['swollen.exe',     # the executable
+                'sky_small.jpg',   # sky texture
+                'envmap.jpg',      # water surface environment map
+                'bedslope.jpg']]   # surface texture
+
+# sample sww file for testing
+localtestdir = localrootdir + os.sep + 'tests'
+#localfiles.append(localtestdir + os.sep + 'cylinders.sww')
+#localfiles.append(localtestdir + os.sep + 'cylinders.tif')
+
+
+#----- NETCDF DEPENDENCIES --------------------------------------------
+
+# netcdf directory
+netcdfdir = os.environ['NETCDF_DIR']
+
+# netcdf DLL
+netcdfdll = os.sep.join( [netcdfdir, 'bin', 'netcdf.dll'] )
+
+
+
+#----- OSG DEPENDENCIES -----------------------------------------------
+
+# OSG directories
+osgrootdir = os.environ['OSG_ROOT']
+osgdir = os.sep.join( [osgrootdir,'OpenSceneGraph','bin'] )
+producerdir = os.sep.join( [osgrootdir,'Producer','bin'] )
+openthreadsdir = os.sep.join( [osgrootdir,'OpenThreads','bin','win32'] )
+
+# OSG/Producer/OpenThreads DLLs
+producerdll = DLLFiles( producerdir, ['Producer'] )
+openthreadsdll = DLLFiles( openthreadsdir, ['OpenThreadsWin32'] )
+osgdlls = DLLFiles( osgdir, ['osg', 'osgdb', 'osgGA', 'osgProducer', 'osgText', 'osgUtil'] )
+
+# supported database loaders (osgdb_XXX.dll)
+osgimgformats = [ 'osgdb_'+f for f in ['jpeg', 'png', 'tiff', 'freetype'] ]
+osgimgdlls = DLLFiles( osgdir, osgimgformats )
+
+# third party dlls
+thirdpartydir = os.sep.join( [osgrootdir,'3rdparty','bin'] )
+thirdpartydlls = DLLFiles( thirdpartydir, ['gdal12', 'glut32', 'msvcp71', 'msvcr71'] )
+
+# All OSG DLLs
+osgdlls = producerdll + openthreadsdll + osgdlls + osgimgdlls + thirdpartydlls
+
+
+
+#------ ARCHIVE -------------------------------------------------------
+
+# all files above
+files = localfiles + localdlls + osgdlls + [netcdfdll]
+files.sort()
+
+import tarfile
+import os.path
+print "creating tgz archive %s" % distro
+t = tarfile.open( distro, 'w:gz' )
+for f in files:
+    basename = os.path.basename(f)
+    print "\tadding %s" % basename
+    t.add( f, basename )
+t.close()
+
+cmd = "scp %s dee900@anusf.anu.edu.au:public_html/Projects/Swollen/Releases" % distro
+os.system(cmd)
Index: /anuga_core/source/anuga_viewer/doc/contract2_hours.txt
===================================================================
--- /anuga_core/source/anuga_viewer/doc/contract2_hours.txt	(revision 3887)
+++ /anuga_core/source/anuga_viewer/doc/contract2_hours.txt	(revision 3887)
@@ -0,0 +1,228 @@
+
+2nd Contract Hours
+------------------
+
+11 July, Monday (10 hours)
+- saving images with -movie <dirname> option now functioning
+
+
+total = 196 hours (5.2 weeks)
+subtotal = 15 hours
+
+9-10 July, Sunday (7 hours)
+- more work on playback of .swm data
+
+8 July, Friday (3 hours)
+- georeferencing of flagstaff dataset
+
+7 July, Thursday (5 hours)
+- towards playback of .swm data
+
+
+total = 181 hours (4.8 weeks)
+subtotal = 18 hours
+
+26 June, Sunday (8 hours)
+- implemented state saving and playback
+
+25 June, Saturday (6 hours)
+- refactored to allow getting and setting of current state
+
+... I know I worked on Swollen in here, but forgot to log it ;-)
+
+14 June, Tuesday (4 hours)
+- jumbled vertex problem on "fairy" dataset fixed
+- bedslope texture path prepended with unneeded "/" bug fixed
+- y>x aspect ratio geometry offset bug fixed
+
+
+
+total = 163 hours (4.3 weeks)
+subtotal = 9 hours
+
+10 June, Friday (4 hours)
+- sww file "texture" attribute image now found in same location as .sww
+- steep culling reintroduced
+- built and released distro20050610 for windows
+
+8 June, Wednesday (3 hours)
+- use of xllcorner,yllcorner in sww file
+- bug that "texture" image is looking in rundir
+
+6 June, Monday (2 hours)
+- added command line specification of bedslope spotlight position (-lightpos option)
+- changed to "texture" global attribute usage
+
+
+
+total = 154 hours (4.1 weeks)
+subtotal = 11 hours
+
+3 June, Friday (3 hours)
+- now looking for bedslope_image global attribute in sww file
+- cylinders georeference test passes
+- built and released distro20050603 for windows
+
+2 June, Thursday (2 hours)
+- bedslope texture only used when specified
+- almost finished image query of .sww global attributes
+
+31 May, Tuesday (3 hours)
+- installation and compile of OSG 0.9.9 on Mac and Windows
+- built Swollen against OSG 0.9.9
+
+30 May 2005, Monday (3 hours)
+- created Photoshop image for cylinder texture test
+- used gdal_translate utility to add georeferences
+
+
+
+total = 143 hours (3.8 weeks)
+subtotal = 8 hours
+
+
+23 December, Thursday (4 hours)
+- bedslope lighting
+
+22 December, Wednesday (4 hours)
+- orientable directional light
+
+
+
+total = 135 hours (3.6 weeks)
+subtotal = 22 hours
+
+
+16 December, Thursday (8 hours)
+- terrain manipulator added
+- -version flag
+- toggling between terrain manipulator and trackball for lighting
+- experiments with cairns ubd map
+
+15 December, Wednesday (4 hours)
+- changed Visual Studio solution to compile new GDAL code
+- work on understanding lack of bedslope lighting
+
+14 December, Tuesday (4 hours)
+- added -texture <filename> option for bedslope image
+- other code cleanups related to handling georeferenced data
+- small amount of work on background thread for data read
+
+15 December, Monday (6 hours)
+- visit to GA, swollen now compiling on-site under Windows/Visual Studio .NET
+- added command line params for tweaking transparency (see "swollen -help")
+- added -nosky option to omit surrounding sky map
+
+
+
+total = 113 hours (3.0 weeks)
+subtotal = 8 hours
+
+
+12 December, Saturday (4 hours)
+- based on Robert's suggestions, removed culling from swwreader in favour
+  of per-vertex transparency and OpenGL per-fragment alpha cull
+- switched to DrawElements OpenGL primitive for much improved speed
+- modified input view to be slightly above plane
+- surrounding sky sphere now maintained at fixed distance
+
+7 December, Wednesday (4 hours)
+- new distro released
+
+
+
+total = 105 hours (2.8 weeks)
+subtotal = 7 hours
+
+
+2 November, Tuesday (4 hours)
+- re "popping" triangles in cairns sww file, shifted dataset scaling
+  into swwreader, now scaling actual vertex values on loading rather than
+  depending on an OSG transform node to shrink to unit cube
+- watersurface now has a method for offsetting surface from bedslope, this
+  and the above seems to have fixed above problem
+- culling toggle 'c' now loops through 4 modes: CULLNONE, CULLNEARZERO,
+  CULLSTEEPANGLE, CALLALL (previous two settings combined)
+
+1 November, Monday (3 hours)
+- reorganized code and moved to Stephen Roberts' subversion repository
+- searching for "popping" triangles in cairns sww file
+- gdal installation and recompilation (support for georef tiff)
+
+
+
+total = 98 hours (2.6 weeks)
+subtotal = 19 hours
+
+
+28 October, Friday (3 hours)
+- distro released
+
+27 October, Thursday (4 hours)
+- revamped windows distro
+
+26 October, Wednesday (4 hours)
+- bedslope texture working
+
+25 October, Tuesday (4 hours)
+- bedslope texture started
+
+24 October, Monday (4 hours)
+- windows installer woes
+- make sure all distributed DLLs are executable!
+
+
+
+total = 79 hours (2 weeks)
+subtotal = 33 hours
+
+
+23 October, Sunday (6 hours)
+- environment mapped water
+
+21 October, Thursday (6 hours)
+- orientable light
+
+20 October, Wednesday (6 hours)
+- orientable light
+
+19 October, Tuesday (6 hours)
+- culling revisited
+
+18 October, Monday (6 hours)
+- memory leaks, ref_ptr
+
+15 October, Friday (3 hours)
+- memory leaks
+
+
+
+total = 46 hours
+
+
+14 October, Thursday (3 hours)
+- sky sphere
+- hud text always "on top"
+
+13 October, Wednesday (6 hours)
+- windows installer
+- sky factored out
+
+12 October, Tuesday (6 hours)
+- view initialization
+
+11 October, Monday (7 hours)
+- surrounding sky
+
+8 October, Friday (6 hours)
+- factored out SpotLight
+
+7 October, Thursday (6 hours)
+
+6 October, Wednesday (6 hours)
+- factored out keyboardeventhandler and watersurface
+- swwreader builds under mac osx
+
+5 October, Tuesday (6 hours)
+- removed -f switch from command line
+- factored out HeadsUpDisplay
Index: /anuga_core/source/anuga_viewer/doc/contract2_priorities.txt
===================================================================
--- /anuga_core/source/anuga_viewer/doc/contract2_priorities.txt	(revision 3887)
+++ /anuga_core/source/anuga_viewer/doc/contract2_priorities.txt	(revision 3887)
@@ -0,0 +1,124 @@
+
+PRIORITIES FOR SECOND CONTRACT
+
+Priorities are in the range [0,5] with 5 being the highest and 0 the lowest.
+
+
+
+ACCEPTED:
+
+- Wireframe toggle for water surface only, 'w' key [4]
+- Reference horizon (possibly sky) as visual aid to orient scene [5]
+- Use per-vertex normals for reflection of light [4]
+- Correct scaling and origin for datasets not centered at (0,0)? [3]
+- Texture mapped topography (currently fixed as bedslope.jpg) [4]
+- Movement restricted to within bounding sphere (sky now stays at fixed distance 
+  from camera) [1]
+- Ability to remove light source (it's in the way sometimes) (removed totally) [2]  
+- Duncan and Ole able to compile Swollen from source under Windows/Visual Studio .NET [3]
+- z-scaling to emphasize vertical details (via -scale command line parameter) [1] 
+- added -nosky command-line option to appease those who want a plain background [?]
+- add -version command line flag synced with Subversion revision number (useful for debugging) [?]
+- Ability to automatically produce distro (win32) [4]
+- Culling of triangles ('c' key to cycle through culling states) [5] (Still there 20/1/5?)
+- Ability to have shading on bed-surface in the absence of texture
+- add back steep culling ability [4]
+- Ability to handle scenes with a resolution of 500,000+ triangles [3]
+
+
+ACCEPTED IN PRINCIPLE (STILL NEED TO BE VERIFIED):
+
+- Compilation under Mac [1]
+- Ability to compile from arbitrary location (e.g. using environment variables) [4]
+- Texture mapped water surface [2] (Not the same as the reflection?)
+- use of geotiff in sww file [5]
+  Question, does swollen read georeferencing from within tif?  
+
+
+COMPLETED:
+
+- Optional height-dependent semi-transparent water surface [0]
+- Interactive positioning of light(s) [0]
+- fix popping on shallow triangles [5]
+- Output coordinates defining camera position [?]
+- Start up with a view sight from above rather than from the side [2]
+- Command line parameters for culling, speed [1]
+- Ability to output frames in a common image format for making standalone animation [5]
+
+
+  
+UNDER DEVELOPMENT:
+
+
+
+BUG LIST (status of these?)
+
+- steepcull dataset reveals steep angle cull problem
+- Problems with Duncan's, Chris' and Stephen Roberts' machines at GA: Swollen won't render semi 
+  transparency. Swwviewer works fine. (This appeared to work on Dunca's computer 13/12/4?).
+  (It seems to be a problem with older cards - don't worry) 
+- with -scale, rotation seems unnatural (and initial position shifted vertically?)
+
+
+
+BUGS FIXED:
+
+- --help not working, -h giving an "abort trap"
+- culling
+- toggle wireframe not working on Mac
+- can no longer make model disappear by pushing back through sky [2004-12-15]
+- Swollen starts up showing first time step, not zeroth (even though clock shows zero). Stepping one 
+  forward and one back, bring things into sync. [2004-12-01]
+- nonuniform scaling into unit cube should be uniform [2004-12-01]
+- onscreen text no longer obscured by zoomed model
+- removed lighting from onscreen text, now constant colour
+- memory leak occurring when loading subsequent stages fixed by use 
+  of ref_ptrs in SWWReader
+- dragging sww onto swollen (all in one directory) does not produce the same results as 
+  calling from command line
+
+
+  
+  
+
+--------------------------------------------------
+
+APPEARANCE:
+
+- Scene augmentation with OSG models [2]	
+- Optional contour lines of water surface [1]
+
+	
+FUNCTIONALITY:
+
+- Optional colour coding of water surface based on arbitrary fields 
+  (e.g. stage, speed, momentum, ..) [4]
+- Ability to read from file as it is being produced (like tail -f) [2]
+- Input camera position to give desired view [3]
+- key binding 'h' for home camera position (return to home view) [2]
+- key binding 't' for top camera position (go to orthographic view) [1]
+- Ability to show earthquakes [0]
+- Concept of 'buildings' and shaking them [0]
+- user feedback should differentiate between "sww file not found" and
+  "sww file couldn't read" [1]
+- prune -help documentation removing irrelevant options [?]
+
+
+OPTIMIZATION:
+
+- Scene optimization via geometry and texture level-of-detail (LOD) [3]
+- Separate thread for loading a ring buffer of stage data [1]
+
+
+COMPILATION:
+
+- Ability to automatically produce distro (linux) [4]
+- Compilation under Linux [3]
+- Ability to compile using gnu gcc/g++ [3]
+
+
+OTHER:
+
+- Brief documentation of interface [4]
+- Revisit use of culling steep epsilon [3]	
+- SWW netcdf meta information [0]
Index: /anuga_core/source/anuga_viewer/doc/notes.txt
===================================================================
--- /anuga_core/source/anuga_viewer/doc/notes.txt	(revision 3887)
+++ /anuga_core/source/anuga_viewer/doc/notes.txt	(revision 3887)
@@ -0,0 +1,25 @@
+
+
+3 November, Wednesday
+
+- state machine for current continuous modes, plus-minus to
+  adjust level
+- colour coding from either none, stage, speed, xmomentum, ...
+- colour bar for colour coding?
+- scaling should be uniform
+
+
+2 November, Tuesday
+
+- need to encapsulate state for serializing current view
+    - camera position
+    - trackball orientation
+    - sww timestep, cullsettings
+    - wireframe or not
+    - zscale factor
+    - zoffset
+- ideally, objects now how to represent themselves (reflection)
+
+
+- need an interface, or at least a means of tweaking settings
+- really should consider wxWindows or similar ...
Index: /anuga_core/source/anuga_viewer/include/project.h
===================================================================
--- /anuga_core/source/anuga_viewer/include/project.h	(revision 3887)
+++ /anuga_core/source/anuga_viewer/include/project.h	(revision 3887)
@@ -0,0 +1,33 @@
+
+/*
+    Project-wide includes and adjustable defaults
+
+    An OpenSceneGraph viewer for pyVolution SWW files.
+    copyright (C) 2004 Geoscience Australia
+*/
+
+
+#ifndef PROJECT_H
+#define PROJECT_H
+
+
+// copied from OSG, must be Visual Studio idiosyncrocies ...
+#if defined(WIN32) && !(defined(__CYGWIN__) || defined(__MINGW32__))
+    #pragma warning( disable : 4244 )
+    #pragma warning( disable : 4251 )
+    #pragma warning( disable : 4267 )
+    #pragma warning( disable : 4275 )
+    #pragma warning( disable : 4290 )
+    #pragma warning( disable : 4786 )
+    #pragma warning( disable : 4305 )
+#endif
+
+
+// Compile-time defaults
+#define DEF_PAUSED_START        true
+#define DEF_BACKGROUND_COLOUR   0.5, 0.5, 0.5, 1.0    // R, G, B, Alpha (grey)
+#define DEF_TPS                 10.0                  // sww timesteps per second
+
+
+
+#endif PROJECT_H
Index: /anuga_core/source/anuga_viewer/include/swwreader.h
===================================================================
--- /anuga_core/source/anuga_viewer/include/swwreader.h	(revision 3887)
+++ /anuga_core/source/anuga_viewer/include/swwreader.h	(revision 3887)
@@ -0,0 +1,198 @@
+/*
+  SWWReader
+
+    A C++ class for reading PyVolution SWW files from within
+    OpenSceneGraph.
+
+    An SWW file is the visualization output of the pyVolution
+    Shallow Water Wave equation solver.  SWW files contain bedslope
+    geometry and a sequence of stages at known timesteps.  The
+    internal format is NetCDF:
+
+    netcdf demo.sww {
+        dimensions:
+            number_of_volumes = <integer> ;
+            number_of_vertices = 3 ;
+            number_of_points = <integer> ;
+            number_of_timesteps = UNLIMITED ;
+        variables:
+            float x(number_of_points) ;
+            float y(number_of_points) ;
+            float z(number_of_points) ;
+            int volumes(number_of_volumes, number_of_vertices) ;
+            float time(number_of_timesteps) ;
+            float stage(number_of_timesteps, number_of_points) ;
+
+
+    Authored under contract to Geoscience Australia by:
+
+            Darran Edmundson
+            ANU Supercomputer Facility Vizlab
+            Australian National University
+            Canberra, ACT 2600
+
+    copyright (C) 2004 Geoscience Australia
+*/
+
+#ifndef SWWREADER_H
+#define SWWREADER_H
+
+#include <string>
+#include <project.h>
+#include <iostream>
+#include <osg/Geometry>
+
+
+// needed to create a .lib file under win32/Visual Studio
+#if defined(_MSC_VER)
+    #define SWWREADER_EXPORT   __declspec(dllexport)
+#else
+    #define SWWREADER_EXPORT
+#endif
+
+
+typedef std::vector<unsigned int> triangle_list;
+
+
+class SWWREADER_EXPORT SWWReader
+{
+
+public:
+
+    SWWReader(const std::string& filename);
+
+    virtual bool isValid() {return _valid;}
+
+    // bedslope
+    virtual osg::ref_ptr<osg::Vec3Array> getBedslopeVertexArray() {return _bedslopevertices;}
+    virtual osg::ref_ptr<osg::Vec3Array> getBedslopeNormalArray() {return _bedslopenormals;}
+    virtual osg::ref_ptr<osg::DrawElementsUInt> getBedslopeIndexArray() {return _bedslopeindices;}
+    virtual osg::ref_ptr<osg::Vec3Array> getBedslopeCentroidArray() {return _bedslopecentroids;}
+    virtual osg::ref_ptr<osg::Vec2Array> getBedslopeTextureCoords();
+
+    virtual bool hasBedslopeTexture() {return (_state.bedslopetexturefilename != NULL);}
+    virtual void setBedslopeTexture( std::string filename );
+    virtual osg::Image* getBedslopeTexture();
+
+
+    // stage
+    virtual osg::ref_ptr<osg::Vec3Array> getStageVertexArray(unsigned int index);
+    virtual osg::ref_ptr<osg::Vec3Array> getStageVertexNormalArray() {return _stagevertexnormals;}
+    virtual osg::ref_ptr<osg::Vec3Array> getStageCentroidsArray() {return _stagecentroids;}
+    virtual osg::ref_ptr<osg::Vec4Array> getStageColorArray() {return _stagecolors;}
+
+
+    virtual float getTime(unsigned int index) {return *(_ptime+index);}
+    virtual size_t getNumberOfVertices() {return _npoints;}
+    virtual unsigned int getNumberOfTimesteps() {return _ntimesteps;}
+
+    virtual float getAlphaMin() {return _state.alphamin;}
+    virtual float getAlphaMax() {return _state.alphamax;}
+    virtual float getHeightMin() {return _state.heightmin;}
+    virtual float getHeightMax() {return _state.heightmax;}
+    virtual float getCullAngle() {return _state.cullangle;}
+
+    virtual void setAlphaMin( float value ) {_state.alphamin = value;}
+    virtual void setAlphaMax( float value ) {_state.alphamax = value;}
+    virtual void setHeightMin( float value ) {_state.heightmin = value;}
+    virtual void setHeightMax( float value ) {_state.heightmax = value;}
+
+    virtual void setCullAngle( float value ) {_state.cullangle = value;}
+    virtual void toggleCulling() {_state.culling = _state.culling ? false : true;}
+    virtual bool getCulling() {return _state.culling;}
+    virtual void setCulling(bool value) {_state.culling = value;}
+    
+    virtual triangle_list getConnectivity(unsigned int index) {return _connectivity.at(index);}
+
+    const std::string getSwollenDir() {return *(_state.swollendirectory);}
+    virtual void setSwollenDir(const std::string path) {_state.swollendirectory = new std::string(path);}
+
+
+protected:
+
+    virtual ~SWWReader();
+
+
+    // state contains all the info needed to serialize
+    struct
+    {
+          float alphamax;  // define alpha (transparency) function
+          float alphamin;
+          float heightmax;
+          float heightmin;
+
+          float cullangle;  // cull triangles with steepness angle above this value
+          bool culling;   // culling is on or off
+
+          std::string* swwfilename;
+          std::string* bedslopetexturefilename;
+          std::string* swollendirectory;
+
+    } _state;
+
+
+    // constructor determines SWW validity (netcdf + proper structure)
+    bool _valid;
+
+    // netCDF file id
+    int _ncid;
+
+    // netcdf dimension ids
+    int _nvolumesid, _nverticesid, _npointsid, _ntimestepsid;
+
+    // netcdf dimension values
+    size_t _nvolumes, _nvertices, _npoints, _ntimesteps;
+
+    // netcdf variable ids
+    int _xid, _yid, _zid, _volumesid, _timeid, _stageid;
+
+    // netcdf variable values (allocated in constructor)
+    float *_px, *_py, *_pz, *_ptime, *_pstage;
+    unsigned int *_pvolumes;
+
+    // fixed geometry
+    osg::ref_ptr<osg::Vec3Array> _bedslopevertices;
+    osg::ref_ptr<osg::Vec3Array> _bedslopenormals;
+    osg::ref_ptr<osg::DrawElementsUInt> _bedslopeindices;
+    osg::ref_ptr<osg::Vec3Array> _bedslopecentroids;
+    osg::ref_ptr<osg::Vec2Array> _bedslopetexcoords;
+
+    // geometry that changes per timestep
+    osg::ref_ptr<osg::Vec3Array> _stagevertices;
+    osg::ref_ptr<osg::Vec3Array> _stageprimitivenormals;
+    osg::ref_ptr<osg::Vec3Array> _stagevertexnormals;
+    osg::ref_ptr<osg::Vec3Array> _stagecentroids;
+    osg::ref_ptr<osg::Vec4Array> _stagecolors;
+
+    // optional geodata for bedslope texture map
+    struct
+    {
+        bool hasData;
+        int xresolution, yresolution;
+        float xorigin, yorigin;
+        float xpixel, ypixel;
+        float rotation;
+    } _bedslopegeodata;
+
+
+    // bedslope vertex scale and shift factors (for normalizing to unit cube)
+    float _xscale, _yscale, _zscale, _scale;
+    float _xoffset, _yoffset, _zoffset;
+    float _xcenter, _ycenter, _zcenter;
+
+    // sww file can contain optional global offset attributes
+    float _xllcorner, _yllcorner;
+
+    // stack of return values from netcdf function calls
+    std::vector<int> _status;
+
+    // error checker (iterates through _status stack)
+    bool _statusHasError();
+
+    // triangle connectivity, list (indexed by vertex number) of 
+    // lists (indices of triangles sharing this vertex)
+    std::vector<triangle_list> _connectivity;
+
+};
+
+#endif  // SWWREADER_H
Index: /anuga_core/source/anuga_viewer/swollen/Makefile
===================================================================
--- /anuga_core/source/anuga_viewer/swollen/Makefile	(revision 3887)
+++ /anuga_core/source/anuga_viewer/swollen/Makefile	(revision 3887)
@@ -0,0 +1,57 @@
+
+#
+#  Mac OS X Makefile
+#
+
+TOPDIR           =  ..
+
+#OPTIMIZATION     =  -O2
+OPTIMIZATION     =  -g
+
+OSG_INCLUDE      =  ${OSGHOME}/include
+LOCAL_INCLUDE    =  $(TOPDIR)/include
+INCLUDES         =  -I . -I $(LOCAL_INCLUDE) -I $(OSG_INCLUDE) -I/usr/X11R6/include
+
+CARBON_LIB       =  -framework Carbon
+GL_LIBS          =  -framework OpenGL
+X_LIBS           =  -lX11
+OTHER_LIBS       =  -lm -lstdc++
+LIBS            +=  -losg -losgText -losgProducer -losgDB -losgUtil -losgGA \
+                    -lOpenThreads -lProducer -lSWWReader \
+                    $(GL_LIBS) $(X_LIBS) $(OTHER_LIBS) $(CARBON_LIB)
+LIBDIRS          =  -L/usr/lib -L/usr/X11R6/lib -L/sw/lib -L/usr/local/lib -L$(TOPDIR)/bin
+
+NAME             =  swollen
+TARGETDIR        =  $(TOPDIR)/bin
+TARGET           =  $(TARGETDIR)/$(NAME)
+
+COMPILER         =  g++
+OBJ              =  customviewer.o hud.o keyboardeventhandler.o watersurface.o main.o version.o \
+                    bedslope.o createSky.o customtrackball.o customterrainmanipulator.o spotlight.o \
+                    directionallight.o state.o customviewereventhandler.o customargumentparser.o \
+                    animation.o
+CPPFLAGS         =  -F/System/Library/Frameworks -Wall -DDARWIN_QUICKTIME \
+                    $(OPTIMIZATION)
+
+
+
+%.o : %.cpp
+	$(COMPILER) -c $(INCLUDES) $(CPPFLAGS) $< -o $@
+
+
+$(TARGET) : $(OBJ)
+	g++ $(CPPFLAGS) $(LIBDIRS) $(OBJ) $(LIBS) -o $(TARGET)
+
+
+.PHONY : version.cpp
+
+# on every build, recreate version() string based on SVN revision number
+version.cpp:
+	echo -n 'const char* version() { const char* s = "Revision: ' > version.cpp
+	svnversion -n . >> version.cpp
+	echo '"; return s; }' >> version.cpp
+
+
+clean :
+	rm -f *.o *~ $(TARGET)
+
Index: /anuga_core/source/anuga_viewer/swollen/animation.cpp
===================================================================
--- /anuga_core/source/anuga_viewer/swollen/animation.cpp	(revision 3887)
+++ /anuga_core/source/anuga_viewer/swollen/animation.cpp	(revision 3887)
@@ -0,0 +1,61 @@
+/*
+  SWWViewer
+
+  An OpenSceneGraph viewer for pyVolution SWW files.
+  copyright (C) 2004-2005 Geoscience Australia
+*/
+
+
+#include <iostream>
+#include <string>
+#include <osg/Image>
+#include <osgDB/WriteFile>
+#include <osgDB/FileUtils>
+//#include <osgDB/FileNameUtils>
+#include <animation.h>
+
+
+Animation::Animation()
+{
+   _valid = false;
+   _image = new osg::Image;
+
+}
+
+
+
+void Animation::setDirectory( std::string dirname )
+{
+   osgDB::makeDirectory( dirname );
+   _dirname = dirname;
+   _valid = true;
+}
+
+
+
+void Animation::setViewer( CustomViewer viewer )
+{
+   viewer.getCamera(0)->getProjectionRectangle( _x, _y, _width, _height );
+}
+
+
+
+
+bool Animation::saveImage( unsigned int playback_index )
+{
+   _image->readPixels( _x, _y, _width, _height, GL_RGB, GL_UNSIGNED_BYTE);
+
+   // restricted to 9999 frames (famous last words but that should be enough ;-)
+   char nstr[5];
+   sprintf(nstr, "%04d", playback_index);
+   
+   std::string filename = std::string( _dirname + "/frame" + nstr + ".jpg" );
+   if ( osgDB::writeImageFile(*_image, filename) )
+   {
+      std::cout << "Saved " << filename << std::endl;
+      return true;
+   }
+
+   return false;
+
+}
Index: /anuga_core/source/anuga_viewer/swollen/animation.h
===================================================================
--- /anuga_core/source/anuga_viewer/swollen/animation.h	(revision 3887)
+++ /anuga_core/source/anuga_viewer/swollen/animation.h	(revision 3887)
@@ -0,0 +1,39 @@
+/*
+    Animation Class
+
+    An OpenSceneGraph viewer for pyVolution .sww files.
+    copyright (C) 2004-2005 Geoscience Australia
+*/
+
+
+#ifndef ANIMATION_H
+#define ANIMATION_H
+
+
+#include <osg/Image>
+#include <customviewer.h>
+#include <string>
+
+
+class Animation
+{
+
+public:
+
+   Animation();
+   void setDirectory( std::string dirname );
+   void setViewer( CustomViewer viewer );
+   bool saveImage( unsigned int index );
+
+
+protected:
+
+   std::string _dirname;
+   int _x, _y;
+   unsigned int _width, _height;
+   osg::ref_ptr<osg::Image> _image;
+   bool _valid;
+};
+
+
+#endif  // ANIMATION_H
Index: /anuga_core/source/anuga_viewer/swollen/bedslope.cpp
===================================================================
--- /anuga_core/source/anuga_viewer/swollen/bedslope.cpp	(revision 3887)
+++ /anuga_core/source/anuga_viewer/swollen/bedslope.cpp	(revision 3887)
@@ -0,0 +1,78 @@
+
+
+#include <bedslope.h>
+
+#include <osg/Texture>
+#include <osg/Texture2D>
+
+#include <osgUtil/SmoothingVisitor>
+
+
+#define DEF_BEDSLOPE_COLOUR     145, 122, 59, 255     // R, G, B, Alpha (brown)
+
+
+// constructor
+BedSlope::BedSlope(SWWReader* sww)
+{
+    // persistent
+    _sww = sww;
+
+    // bedslope geometry as triangles with shared vertices
+    _geom = new osg::Geometry;
+
+    // geometry from sww file
+    osg::Vec3Array* vertices = _sww->getBedslopeVertexArray().get();
+
+    _geom->setUseDisplayList( true );
+    _geom->setVertexArray( vertices );
+    _geom->addPrimitiveSet( _sww->getBedslopeIndexArray().get() );
+
+    osg::UByte4Array* color = new osg::UByte4Array(1);
+    (*color)[0] = osg::UByte4( DEF_BEDSLOPE_COLOUR );
+    _geom->setColorArray( color );
+    _geom->setColorBinding( osg::Geometry::BIND_OVERALL );
+
+    // Calculate per-vertex normals
+    osgUtil::SmoothingVisitor* visitor = new osgUtil::SmoothingVisitor();
+    visitor->smooth( *_geom );
+
+	_node = new osg::Geode;
+    _node->setName("bedslope");
+    _node->addDrawable(_geom);
+
+    // bedslope texture
+	_texture = false;
+	if( sww->hasBedslopeTexture() )
+	{
+	    _texture = true;
+		osg::Texture2D* texture = new osg::Texture2D;
+		texture->setDataVariance( osg::Object::DYNAMIC );
+		texture->setWrap( osg::Texture::WRAP_S, osg::Texture::CLAMP );
+		texture->setWrap( osg::Texture::WRAP_T, osg::Texture::CLAMP );
+		texture->setImage( _sww->getBedslopeTexture() );
+
+		_geom->setTexCoordArray( 0, _sww->getBedslopeTextureCoords().get() );
+
+		// material
+		_material = new osg::Material();
+		_material->setAmbient( osg::Material::FRONT_AND_BACK, osg::Vec4(0.2, 0.15, 0.06, 1.0) );
+		_material->setDiffuse( osg::Material::FRONT_AND_BACK, osg::Vec4(0.6, 0.5, 0.2, 1.0) );
+		_material->setSpecular( osg::Material::FRONT_AND_BACK, osg::Vec4(0.0, 0.0, 0.0, 1.0) );
+		_material->setEmission( osg::Material::FRONT_AND_BACK, osg::Vec4(0.0, 0.0, 0.0, 1.0) );
+
+		_material->setAmbient( osg::Material::FRONT_AND_BACK, osg::Vec4(0.0, 0.0, 0.0, 1.0) );
+		_material->setDiffuse( osg::Material::FRONT_AND_BACK, osg::Vec4(1.0, 1.0, 1.0, 1.0) );
+		_material->setSpecular( osg::Material::FRONT_AND_BACK, osg::Vec4(0.0, 0.0, 0.0, 1.0) );
+		_material->setEmission( osg::Material::FRONT_AND_BACK, osg::Vec4(0.0, 0.0, 0.0, 1.0) );
+
+		// state
+		_stateset = new osg::StateSet;
+		_stateset->setAttributeAndModes( _material, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE );
+		_stateset->setMode( GL_BLEND, osg::StateAttribute::ON );
+		_stateset->setMode( GL_LIGHTING, osg::StateAttribute::ON );
+		_stateset->setTextureAttributeAndModes( 0, texture, osg::StateAttribute::ON );
+
+	    _node->setStateSet(_stateset);
+	}
+
+}
Index: /anuga_core/source/anuga_viewer/swollen/bedslope.h
===================================================================
--- /anuga_core/source/anuga_viewer/swollen/bedslope.h	(revision 3887)
+++ /anuga_core/source/anuga_viewer/swollen/bedslope.h	(revision 3887)
@@ -0,0 +1,47 @@
+
+
+/*
+    BedSlope class
+
+    An OpenSceneGraph viewer for pyVolution .sww files.
+    copyright (C) 2004-2005 Geoscience Australia
+*/
+
+
+#ifndef BEDSLOPE_H
+#define BEDSLOPE_H
+
+
+#include <project.h>
+#include <SWWReader.h>
+#include <osg/Geode>
+#include <osg/Material>
+#include <osg/StateAttribute>
+
+
+
+class BedSlope
+{
+
+public:
+
+    BedSlope(SWWReader *sww);
+    virtual osg::Geode* get(){ return _node; }
+    virtual osg::BoundingBox getBound(){ return _geom->getBound(); }
+    virtual void update(){}
+
+
+protected:
+
+    SWWReader* _sww;
+    osg::Geode* _node;
+    osg::Geometry* _geom;
+    osg::StateSet* _stateset;
+    osg::Material* _material;
+    virtual ~BedSlope(){;}
+    bool _texture;
+
+};
+
+
+#endif  // BEDSLOPE_H
Index: /anuga_core/source/anuga_viewer/swollen/createSky.cpp
===================================================================
--- /anuga_core/source/anuga_viewer/swollen/createSky.cpp	(revision 3887)
+++ /anuga_core/source/anuga_viewer/swollen/createSky.cpp	(revision 3887)
@@ -0,0 +1,78 @@
+
+#include <osg/Depth>
+#include <osg/Geode>
+#include <osg/Geometry>
+#include <osg/ShapeDrawable>
+#include <osg/Texture2D>
+#include <osg/Transform>
+#include <osg/PolygonOffset>
+#include <osgDB/Registry>
+#include <osgDB/ReadFile>
+#include <osgUtil/CullVisitor>
+
+
+class MoveHorizonWithEyePointTransform : public osg::Transform
+{
+
+public:
+
+    // Get the transformation matrix which moves from local coords to world coords
+    virtual bool computeLocalToWorldMatrix(osg::Matrix& matrix,osg::NodeVisitor* nv) const 
+    {
+        osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>(nv);
+        if (cv)
+        {
+            osg::Vec3 eyePointLocal = cv->getEyeLocal();
+            matrix.preMult(osg::Matrix::translate(eyePointLocal));
+        }
+        return true;
+    }
+
+    // Get transformation matrix which moves from world coords to local coords
+    virtual bool computeWorldToLocalMatrix(osg::Matrix& matrix,osg::NodeVisitor* nv) const
+    {
+        osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>(nv);
+        if (cv)
+        {
+            osg::Vec3 eyePointLocal = cv->getEyeLocal();
+            matrix.postMult(osg::Matrix::translate(-eyePointLocal));
+        }
+        return true;
+    }
+};
+
+
+
+osg::Transform* createSky(float radius, const std::string filename)
+{
+    osg::Geode* geode = new osg::Geode;
+    osg::ShapeDrawable* geom = new osg::ShapeDrawable( new osg::Sphere(osg::Vec3(0,0,-radius/4.0),radius) );
+    
+    // set up the texture state.    
+    osg::Texture2D* texture = new osg::Texture2D;
+    texture->setDataVariance(osg::Object::DYNAMIC);  // protect from being optimized away as static state.
+    texture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
+    texture->setImage(osgDB::readImageFile(filename.c_str()));
+    
+    osg::StateSet* stateset = new osg::StateSet;
+    stateset->setTextureAttributeAndModes( 0, texture, osg::StateAttribute::ON );
+    stateset->setMode( GL_LIGHTING, osg::StateAttribute::OFF );
+
+    // to prevent being able to push the model back through the sky, we
+    // do two things (i) ensure sky is drawn first, and (ii) skip writing
+    // to the depth buffer
+    stateset->setRenderBinDetails( -1, "RenderBin" );
+    osg::Depth* depth = new osg::Depth();
+    depth->setWriteMask( false );
+    stateset->setAttributeAndModes( depth, osg::StateAttribute::ON );
+
+    geom->setStateSet( stateset );
+    geom->setUseDisplayList( true );
+    geode->addDrawable(geom);
+
+    osg::Transform* transform = new MoveHorizonWithEyePointTransform;
+    transform->setCullingActive( false );
+    transform->addChild( geode );
+
+    return transform;
+}
Index: /anuga_core/source/anuga_viewer/swollen/customargumentparser.cpp
===================================================================
--- /anuga_core/source/anuga_viewer/swollen/customargumentparser.cpp	(revision 3887)
+++ /anuga_core/source/anuga_viewer/swollen/customargumentparser.cpp	(revision 3887)
@@ -0,0 +1,98 @@
+/*
+  SWWViewer
+
+  An OpenSceneGraph viewer for pyVolution SWW files.
+  copyright (C) 2004-2005 Geoscience Australia
+*/
+
+#include "customargumentparser.h"
+#include <fstream>
+#include <iostream>
+#include <osgDB/FileNameUtils>
+
+
+#define MAX_LINE_LENGTH 200
+
+using namespace osg;
+
+
+CustomArgumentParser::CustomArgumentParser(int* argc, char **argv) : ArgumentParser(argc, argv)
+{
+
+   // last argument is either .sww or .swm filename
+   _filename = new std::string(argv[*argc-1]);
+
+   // macro file?
+   if( osgDB::getLowerCaseFileExtension(*_filename) == std::string("swm") )
+   {
+      _isswm = true;
+
+      // attempt to open the macro file ...
+      std::fstream f;
+      f.open( _filename->c_str(), std::fstream::in );
+      if( f.is_open() )
+      {
+         char str[MAX_LINE_LENGTH];
+
+         // FIXME: verify this really is the SWM header ...
+         f.getline(str,MAX_LINE_LENGTH);
+
+         // read the stored argc count
+         f.getline(str,MAX_LINE_LENGTH);
+         sscanf( str, "# SWM CLP %d", &_nargs );
+
+         // we will modify the argv array to include the original 
+         // argv[1] ... argv[argc-1] (ie., ignore the final argument which is the swm 
+         // filename) extended by the stored arguments contained in the macro file.
+         char** newargv = new char*[*argc-1+_nargs];
+
+         // current command line params (sans last entry, the .swm file)
+         for( int i=0; i<*argc-1; i++ )
+            newargv[i] = argv[i];
+
+         // now fill in the stored argv[] from the macro file
+         for( int i=0; i<_nargs; i++ )
+         {
+            f.getline(str,MAX_LINE_LENGTH);
+            char *s = new char[strlen(str)+1];
+            strcpy(s,str);
+            newargv[*argc-1+i] = s;
+         }
+
+         // change baseclass private members to the extended argc/argv
+         *_argc = *argc - 1 + _nargs;
+         _argv = newargv;
+
+
+         std::cout << "Modified argument list based on SWM contents" << std::endl;
+         for( int i=0; i<*_argc; i++ )
+            std::cout << "    " << i << ":  " << _argv[i] << std::endl;
+
+      }
+      f.close();
+
+   }
+
+   else
+
+   {
+      _isswm = false;
+
+      // local copies of current argc, argv[] as we may have to save them on '3' save.
+      // note that we don't save argv[0] which is the program name.
+      _nargs = *argc - 1;
+      for( int i=1; i<*argc; i++ )
+         _vargs.push_back( std::string(argv[i]) );
+   } 
+
+}
+
+
+void CustomArgumentParser::write(std::ostream& s)
+{
+   s << "# SWM CLP " << _nargs << std::endl;
+
+   for( int i=0; i < _nargs; i++ )
+      s << _vargs[i] << std::endl;
+
+}
Index: /anuga_core/source/anuga_viewer/swollen/customargumentparser.h
===================================================================
--- /anuga_core/source/anuga_viewer/swollen/customargumentparser.h	(revision 3887)
+++ /anuga_core/source/anuga_viewer/swollen/customargumentparser.h	(revision 3887)
@@ -0,0 +1,32 @@
+/*
+    CustomArgementParser class
+
+    An OpenSceneGraph viewer for pyVolution .sww files.
+    copyright (C) 2004-2005 Geoscience Australia
+*/
+
+#include <osg/ArgumentParser>
+#include <istream>
+#include <vector>
+#include <string>
+
+
+class CustomArgumentParser : public osg::ArgumentParser
+{
+
+public:
+
+    CustomArgumentParser(int *argc, char **argv);
+    void write(std::ostream& s);
+    bool isSWM(){ return _isswm; };
+    std::string getFilename(){ return *_filename; }
+
+
+protected:
+
+    int _nargs;
+    std::vector<std::string> _vargs;
+    std::string* _filename;
+    bool _isswm;
+};
+
Index: /anuga_core/source/anuga_viewer/swollen/customterrainmanipulator.cpp
===================================================================
--- /anuga_core/source/anuga_viewer/swollen/customterrainmanipulator.cpp	(revision 3887)
+++ /anuga_core/source/anuga_viewer/swollen/customterrainmanipulator.cpp	(revision 3887)
@@ -0,0 +1,28 @@
+/*
+    CustomTerrainManipulator class
+
+    An OpenSceneGraph viewer for pyVolution .sww files.
+    copyright (C) 2004 Geoscience Australia
+*/
+
+#include <iostream>
+#include "customterrainmanipulator.h"
+
+
+void CustomTerrainManipulator::moveToHome()
+{
+    computePosition( _homeEye, _homeCenter, _homeUp );
+}
+
+
+bool CustomTerrainManipulator::handle( const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &us )
+{
+    if( _disabled )
+	return false;
+    else
+    {
+	// std::cout << "customterrainmanipulator active" << std::endl;
+	return osgGA::TerrainManipulator::handle( ea, us );
+    }
+}
+
Index: /anuga_core/source/anuga_viewer/swollen/customterrainmanipulator.h
===================================================================
--- /anuga_core/source/anuga_viewer/swollen/customterrainmanipulator.h	(revision 3887)
+++ /anuga_core/source/anuga_viewer/swollen/customterrainmanipulator.h	(revision 3887)
@@ -0,0 +1,30 @@
+
+#ifndef CUSTOMTERRAINMANIPULATOR_H
+#define CUSTOMTERRAINMANIPULATOR_H
+
+
+#include <project.h>
+
+#include <osgGA/GUIEventAdapter>
+#include <osgGA/GUIActionAdapter>
+#include <osgGA/TerrainManipulator>
+
+
+class CustomTerrainManipulator : public osgGA::TerrainManipulator
+{
+
+public:
+
+    void moveToHome();
+    virtual bool handle( const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &us );
+    void disable(){ _disabled = true ;}
+    void enable(){ _disabled = false ;}
+
+
+protected:
+
+    bool _disabled;
+
+};
+
+#endif  // CUSTOMTERRAINMANIPULATOR_H
Index: /anuga_core/source/anuga_viewer/swollen/customtrackball.cpp
===================================================================
--- /anuga_core/source/anuga_viewer/swollen/customtrackball.cpp	(revision 3887)
+++ /anuga_core/source/anuga_viewer/swollen/customtrackball.cpp	(revision 3887)
@@ -0,0 +1,28 @@
+/*
+    CustomTrackBall class
+
+    An OpenSceneGraph viewer for pyVolution .sww files.
+    copyright (C) 2004 Geoscience Australia
+*/
+
+#include <iostream>
+#include "customtrackball.h"
+
+
+void CustomTrackballManipulator::moveToHome()
+{
+    computePosition( _homeEye, _homeCenter, _homeUp );
+}
+
+
+bool CustomTrackballManipulator::handle( const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &us )
+{
+    if( _disabled )
+	return false;
+    else
+    {
+	// std::cout << "customtrackballmanipulator active" << std::endl;
+	return osgGA::TrackballManipulator::handle( ea, us );
+    }
+}
+
Index: /anuga_core/source/anuga_viewer/swollen/customtrackball.h
===================================================================
--- /anuga_core/source/anuga_viewer/swollen/customtrackball.h	(revision 3887)
+++ /anuga_core/source/anuga_viewer/swollen/customtrackball.h	(revision 3887)
@@ -0,0 +1,31 @@
+
+
+#ifndef CUSTOMTRACKBALL_H
+#define CUSTOMTRACKBALL_H
+
+
+#include <project.h>
+#include <osgGA/TrackballManipulator>
+#include <osgGA/GUIActionAdapter>
+#include <osgGA/GUIEventAdapter>
+
+
+class CustomTrackballManipulator : public osgGA::TrackballManipulator
+{
+
+public:
+
+    void moveToHome();
+    virtual bool handle( const osgGA::GUIEventAdapter &ea, osgGA::GUIActionAdapter &us );
+    void disable(){ _disabled = true ;}
+    void enable(){ _disabled = false ;}
+    bool isEnabled(){ return !_disabled ;}
+
+
+protected:
+
+    bool _disabled;
+
+};
+
+#endif  // CUSTOMTRACKBALL_H
Index: /anuga_core/source/anuga_viewer/swollen/customviewer.cpp
===================================================================
--- /anuga_core/source/anuga_viewer/swollen/customviewer.cpp	(revision 3887)
+++ /anuga_core/source/anuga_viewer/swollen/customviewer.cpp	(revision 3887)
@@ -0,0 +1,82 @@
+
+#include <osg/LightSource>
+#include <osg/AlphaFunc>
+#include <osgUtil/UpdateVisitor>
+#include <osgGA/StateSetManipulator>
+#include <osgProducer/OsgCameraGroup>
+#include "customviewer.h"
+#include "customviewereventhandler.h"
+
+using namespace osg;
+
+
+//Viewer(arguments)
+CustomViewer::CustomViewer(osg::ArgumentParser& arguments) : osgProducer::Viewer()
+{
+   osg::DisplaySettings::instance()->readCommandLine(arguments);
+}
+
+
+
+void CustomViewer::setUpViewer(unsigned int options)
+{
+    // set up the keyboard and mouse handling.
+    Producer::InputArea *ia = getCameraConfig()->getInputArea();
+    
+    if (!_kbm)
+    {
+        _kbm = ia ?
+                   (new Producer::KeyboardMouse(ia)) : 
+                   (new Producer::KeyboardMouse(getCamera(0)->getRenderSurface()));                   
+    }
+    
+    // set the keyboard mouse callback to catch the events from the windows.
+    if (!_kbmcb)
+        _kbmcb = new osgProducer::KeyboardMouseCallback( _kbm.get(), _done, (options & ESCAPE_SETS_DONE)!=0 );
+        
+    _kbmcb->setStartTick(_start_tick);
+    
+    // register the callback with the keyboard mouse manager.
+    _kbm->setCallback( _kbmcb.get() );
+
+    // set the global state    
+    osg::ref_ptr<osg::StateSet> globalStateSet = new osg::StateSet;
+    setGlobalStateSet(globalStateSet.get());
+    {
+        globalStateSet->setGlobalDefaults();
+
+        // enable depth testing by default.
+        globalStateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::ON);
+
+        // enable lighting by default
+        globalStateSet->setMode(GL_LIGHTING, osg::StateAttribute::ON);
+
+        // set up an alphafunc by default to speed up blending operations.
+        osg::AlphaFunc* alphafunc = new osg::AlphaFunc;
+        alphafunc->setFunction(osg::AlphaFunc::GREATER,0.0f);
+        globalStateSet->setAttributeAndModes(alphafunc, osg::StateAttribute::ON);
+    }
+    
+    
+    if (!_updateVisitor) _updateVisitor = new osgUtil::UpdateVisitor;
+    _updateVisitor->setFrameStamp(_frameStamp.get());
+
+
+    _terrainmanipulator = new CustomTerrainManipulator;
+    if( options&TERRAIN_MANIPULATOR ) addCameraManipulator( _terrainmanipulator );
+
+    
+    if( options&STATE_MANIPULATOR )
+    {
+        osg::ref_ptr<osgGA::StateSetManipulator> statesetManipulator = new osgGA::StateSetManipulator;
+        statesetManipulator->setStateSet( getGlobalStateSet() );
+        _eventHandlerList.push_back( statesetManipulator.get() );
+    }
+        
+    if( options&VIEWER_MANIPULATOR )
+    {
+       osg::ref_ptr<CustomViewerEventHandler> viewereventhandler = new CustomViewerEventHandler(this);
+       getEventHandlerList().push_back( viewereventhandler.get() );
+    }
+    
+}
Index: /anuga_core/source/anuga_viewer/swollen/customviewer.h
===================================================================
--- /anuga_core/source/anuga_viewer/swollen/customviewer.h	(revision 3887)
+++ /anuga_core/source/anuga_viewer/swollen/customviewer.h	(revision 3887)
@@ -0,0 +1,38 @@
+/*
+    CustomViewer Class
+
+    An OpenSceneGraph viewer for pyVolution .sww files.
+    copyright (C) 2004-2005 Geoscience Australia
+*/
+
+
+#ifndef CUSTOMVIEWER_H
+#define CUSTOMVIEWER_H
+
+
+#include <project.h>
+#include <osgProducer/Viewer>
+#include "customtrackball.h"
+#include "customterrainmanipulator.h"
+
+
+class CustomViewer : public osgProducer::Viewer
+{
+
+public:
+    CustomViewer(osg::ArgumentParser& arguments);
+    void setUpViewer(unsigned int options=STANDARD_SETTINGS);
+    virtual CustomTrackballManipulator* getTrackball(){return _trackball;}
+    virtual CustomTerrainManipulator* getTerrainManipulator(){return _terrainmanipulator;}
+    osg::Vec3d getPosition(){return osg::Vec3d(_position[0],_position[1],_position[2]);}
+    osg::Quat getOrientation(){return _orientation;}
+    //virtual void setPosition( osg::Vec3 position );
+    //virtual void setOrientation( osg::Quat orientation );
+
+protected:
+    CustomTrackballManipulator* _trackball;
+    CustomTerrainManipulator* _terrainmanipulator;
+
+};
+
+#endif  // CUSTOMVIEWER_H
Index: /anuga_core/source/anuga_viewer/swollen/customviewereventhandler.cpp
===================================================================
--- /anuga_core/source/anuga_viewer/swollen/customviewereventhandler.cpp	(revision 3887)
+++ /anuga_core/source/anuga_viewer/swollen/customviewereventhandler.cpp	(revision 3887)
@@ -0,0 +1,133 @@
+
+#include <osgDB/WriteFile>
+#include <osgDB/FileNameUtils>
+#include "customviewereventhandler.h"
+
+using namespace osg;
+
+CustomViewerEventHandler::CustomViewerEventHandler(osgProducer::OsgCameraGroup* cg) : 
+   osgProducer::ViewerEventHandler(cg)
+{
+}
+
+
+void CustomViewerEventHandler::getUsage(osg::ApplicationUsage& usage) const
+{
+    usage.addKeyboardMouseBinding("f","Toggle fullscreen");
+    usage.addKeyboardMouseBinding("h","Display help");
+    usage.addKeyboardMouseBinding("O/PrintScreen","Screen capture to \"saved_image*.jpg\"");
+    usage.addKeyboardMouseBinding("Space","Toggle surface data time stepping");
+    usage.addKeyboardMouseBinding("c","Toggle steep surface triangle culling");
+    usage.addKeyboardMouseBinding("w","Toggle wireframe mode for surface triangles");
+    usage.addKeyboardMouseBinding("1","Toggle recording.");
+    usage.addKeyboardMouseBinding("2","Toggle playback of recorded information");
+    usage.addKeyboardMouseBinding("3","Save recorded macro to \"movie.swm\"");
+}
+
+
+bool CustomViewerEventHandler::handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
+{
+    if(!_cg) return false;
+
+    if(ea.getEventType()==osgGA::GUIEventAdapter::KEYDOWN)
+    {
+
+        switch( ea.getKey() )
+        {
+            case 's' :
+            {
+                FrameStatsMode newFrameStatsMode = (FrameStatsMode)((_frameStatsMode+1)%3);
+                setFrameStatsMode(newFrameStatsMode);
+                return true;
+            }
+
+            case 'f' :
+            {
+                Producer::CameraConfig* cfg = _cg->getCameraConfig();
+
+                bool shouldBeFullScreen = false;
+                bool flag = true;
+                for( unsigned int i = 0; i < cfg->getNumberOfCameras(); ++i )
+                {
+                    Producer::Camera *cam = cfg->getCamera(i);
+                    Producer::RenderSurface* rs = cam->getRenderSurface();
+                    if( rs->getDrawableType() != Producer::RenderSurface::DrawableType_Window )
+                        continue;
+                  
+                    if( flag )
+                    {
+                        shouldBeFullScreen =! rs->isFullScreen();    // Remember the initial state of the first render surface
+                        flag = false;
+                    }
+
+                    if ( shouldBeFullScreen!=rs->isFullScreen() )        // If the current render surface hasn't been modified already
+                    {
+                        if (_firstTimeTogglingFullScreen && rs->isFullScreen())
+                        {
+                            unsigned int screenWidth;
+                            unsigned int screenHeight;
+                            unsigned int windowWidth;
+                            unsigned int windowHeight;
+                            rs->getScreenSize( screenWidth, screenHeight );
+                            if( screenHeight > screenWidth )
+                            {
+                                windowWidth  = (unsigned int)((float)screenWidth * 0.625);
+                                windowHeight = (unsigned int)((float)windowWidth * 0.75);
+                            }
+                            else
+                            {
+                                windowHeight = (unsigned int)((float)screenHeight * 0.625);
+                                windowWidth  = (unsigned int)((float)windowHeight * 1.334);
+                            }
+                            int x = (screenWidth - windowWidth) >> 1;
+                            int y = (screenHeight - windowHeight) >> 1;
+                        #ifndef WIN32                    
+                            rs->useBorder(true);
+                        #else                        
+                            rs->fullScreen(false);
+                        #endif
+                            rs->setWindowRectangle( x, y, windowWidth, windowHeight );
+                        }
+                        else
+                        {
+                            rs->fullScreen(!rs->isFullScreen());
+                        }
+                    }
+                }
+                _firstTimeTogglingFullScreen = false;
+
+                return true;
+            }
+
+            case osgGA::GUIEventAdapter::KEY_Print :
+            case 'O' :
+            {
+                setWriteImageOnNextFrame(true);                
+                return true;
+            }
+            case '+' :
+            {
+                //_camera->setFusionDistanceRatio(_camera->getFusionDistanceRatio()*1.25f);
+                return true;
+            }
+            case '-' :
+            {
+                //_camera->setFusionDistanceRatio(_camera->getFusionDistanceRatio()/1.25f);
+                return true;
+            }
+
+            case osgGA::GUIEventAdapter::KEY_Help :
+            case 'h' :
+            {
+                setDisplayHelp(!getDisplayHelp());
+                return true;
+            }
+
+        default:
+            break;
+
+        }
+    }
+    return false;
+
+}
Index: /anuga_core/source/anuga_viewer/swollen/customviewereventhandler.h
===================================================================
--- /anuga_core/source/anuga_viewer/swollen/customviewereventhandler.h	(revision 3887)
+++ /anuga_core/source/anuga_viewer/swollen/customviewereventhandler.h	(revision 3887)
@@ -0,0 +1,32 @@
+/*
+  Customized event handler
+
+  An OpenSceneGraph viewer for pyVolution SWW files.
+  copyright (C) 2004-2005 Geoscience Australia
+*/
+
+#include <project.h>
+#include <osgProducer/ViewerEventHandler>
+#include <osgProducer/OsgCameraGroup>
+#include <osgGA/GUIEventHandler>
+
+
+class CustomViewerEventHandler : public osgProducer::ViewerEventHandler
+{
+   public:
+
+      CustomViewerEventHandler(osgProducer::OsgCameraGroup* cg);
+
+      // Get the keyboard and mouse usage of this viewer
+      virtual void getUsage(osg::ApplicationUsage& usage) const;
+
+      // core code handling key events
+      virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa);
+
+
+   protected:
+
+};
+
+
+
Index: /anuga_core/source/anuga_viewer/swollen/directionallight.cpp
===================================================================
--- /anuga_core/source/anuga_viewer/swollen/directionallight.cpp	(revision 3887)
+++ /anuga_core/source/anuga_viewer/swollen/directionallight.cpp	(revision 3887)
@@ -0,0 +1,66 @@
+
+#include <math.h>
+#include <directionallight.h>
+#include <osg/Texture2D>
+// #include <osg/ShapeDrawable>
+// #include <osgDB/ReadFile>
+
+#include <iostream>
+
+// #define DEF_DEFAULT_CYLINDER_RADIUS 0.25
+// #define DEF_DEFAULT_CYLINDER_HEIGHT 0.50
+
+
+
+DirectionalLight::DirectionalLight(osg::StateSet* rootStateSet, int num)
+{
+    // positioning container
+    _transform = new osg::MatrixTransform;
+    _transform->setCullingActive(false);
+
+    // OpenGL light
+    _light = new osg::Light;
+    _light->setLightNum(num);
+
+    // homogeneous coordinates (x,y,z,w), w=0 indicates position at infinity
+    _light->setPosition( osg::Vec4( 0, 0, 1, 0 ) );
+
+    _light->setAmbient( osg::Vec4( 0.7, 0.7, 0.7, 1.0 ) );
+    _light->setDiffuse( osg::Vec4( 1.0, 1.0, 1.0, 1.0 ) );
+    _light->setSpecular( osg::Vec4( 1.0, 1.0, 1.0, 1.0) );
+
+    // Scenegraph node
+    _source = new osg::LightSource;
+    _source->setReferenceFrame( osg::LightSource::RELATIVE_RF );
+    _source->setLight( _light );
+    _source->setLocalStateSetModes( osg::StateAttribute::ON ); 
+    _source->setStateSetModes( *rootStateSet, osg::StateAttribute::ON );
+    _transform->addChild( _source );
+
+    // marker geometry, texture and state
+    //_marker = osgDB::readNodeFile( "light.osg" );
+    //_transform->addChild( _marker );
+
+    setPosition( osg::Vec3(0,0,1) );  // default overhead
+}
+
+
+
+void DirectionalLight::setPosition(osg::Vec3 v)
+{
+
+    //std::cout << "[DirectionalLight::setPosition] vector = " << v << std::endl;
+    osg::Vec3 origindir;
+    osg::Quat quat;
+    origindir.set( -v );
+    origindir.normalize();
+    quat.makeRotate( osg::Vec3(0,0,-1), origindir );
+
+    _transform->setMatrix( osg::Matrix::rotate( quat ) * osg::Matrix::translate(v) );
+}
+
+
+
+DirectionalLight::~DirectionalLight()
+{
+}
Index: /anuga_core/source/anuga_viewer/swollen/directionallight.h
===================================================================
--- /anuga_core/source/anuga_viewer/swollen/directionallight.h	(revision 3887)
+++ /anuga_core/source/anuga_viewer/swollen/directionallight.h	(revision 3887)
@@ -0,0 +1,62 @@
+
+/*
+    DirectionalLight class
+
+    An OpenSceneGraph viewer for pyVolution .sww files.
+    copyright (C) 2004 Geoscience Australia
+*/
+
+
+#ifndef DIRECTIONALLIGHT_H
+#define DIRECTIONALLIGHT_H
+
+
+#include <project.h>
+#include <osg/Geode>
+#include <osg/Geometry>
+#include <osg/Group>
+#include <osg/Light>
+#include <osg/LightSource>
+#include <osg/MatrixTransform>
+#include <osg/StateAttribute>
+#include <osg/StateSet>
+#include <osg/Vec3>
+
+
+
+class DirectionalLight
+{
+
+public:
+
+    DirectionalLight(osg::StateSet* rootStateSet, int num=0);
+    virtual osg::Group* get(){ return _transform; }
+    virtual void setPosition(osg::Vec3 v);
+    virtual osg::Vec3f* getPosition(){ return _position; }
+    void setMatrix( osg::Matrixf matrix ){ _transform->setMatrix( matrix ); }
+    void setAmbient( osg::Vec3f v ){ _light->setAmbient(osg::Vec4(v,1)); }
+    void setDiffuse( osg::Vec3f v ){ _light->setDiffuse(osg::Vec4(v,1)); }
+    const osg::Matrix getMatrix(){ return _transform->getMatrix(); }
+    // void show(){ _marker->setNodeMask(1); }
+    // void hide(){ _marker->setNodeMask(0); }
+
+
+protected:
+
+    osg::MatrixTransform* _transform;
+    osg::Vec3* _position;
+    osg::Vec3* _target;
+    osg::Light* _light;
+    osg::LightSource* _source;
+
+    // osg::Node* _marker;
+    // osg::Cylinder* _shape;
+    // osg::Geometry* _geom;
+    // osg::StateSet* _stateset;
+
+    virtual ~DirectionalLight();
+
+};
+
+
+#endif  // DIRECTIONALLIGHT_H
Index: /anuga_core/source/anuga_viewer/swollen/hud.cpp
===================================================================
--- /anuga_core/source/anuga_viewer/swollen/hud.cpp	(revision 3887)
+++ /anuga_core/source/anuga_viewer/swollen/hud.cpp	(revision 3887)
@@ -0,0 +1,150 @@
+
+/*
+  HeadsUpDisplay class for orthographic foreground display of text
+
+  An OpenSceneGraph viewer for pyVolution SWW files.
+  copyright (C) 2004 Geoscience Australia
+*/
+
+
+#include <osg/Geode>
+#include <osg/Geometry>
+#include <osg/Group>
+#include <osg/MatrixTransform>
+#include <osg/StateSet>
+#include <hud.h>
+
+#define ORTHO2D_WIDTH 1280
+#define ORTHO2D_HEIGHT 1024
+#define DEF_HUD_COLOUR   0.8, 0.8, 0.8, 1.0
+
+
+// constructor
+HeadsUpDisplay::HeadsUpDisplay()
+{
+   // a heads-up display requires an orthographic projection
+   _projection = new osg::Projection;
+   _projection->setMatrix( osg::Matrix::ortho2D(0,ORTHO2D_WIDTH,0,ORTHO2D_HEIGHT) );
+
+   // font
+   osgText::Font* font = osgText::readFontFile("fonts/arial.ttf");
+
+   // title text
+   _titletext = new osgText::Text;
+   _titletext->setFont(font);
+   _titletext->setColor(osg::Vec4(DEF_HUD_COLOUR) );
+   _titletext->setCharacterSize(20);
+   _titletext->setPosition(osg::Vec3(20,20,0));
+   _titletext->setFontResolution(40,40);
+   _dirtytime = false;
+
+   // timer text
+   _timetext = new osgText::Text;
+   _timetext->setFont(font);
+   _timetext->setColor(osg::Vec4(DEF_HUD_COLOUR) );
+   _timetext->setCharacterSize(30);
+   _timetext->setPosition(osg::Vec3(1100,20,0));
+   _timetext->setFontResolution(40,40);
+   _timevalue = 0.0;
+   _dirtytime = true;
+
+   // recording mode text
+   _modetext = new osgText::Text;
+   _modetext->setFont(font);
+   _modetext->setColor(osg::Vec4(DEF_HUD_COLOUR) );
+   _modetext->setCharacterSize(30);
+   _modetext->setPosition(osg::Vec3(600,20,0));
+   _modetext->setFontResolution(40,40);
+   _modestring = std::string("");
+   _dirtymode = true;
+
+
+   // state
+   osg::StateSet *state = _projection->getOrCreateStateSet();
+   state->setRenderBinDetails(12, "RenderBin");
+   state->setMode(GL_DEPTH_TEST,osg::StateAttribute::OFF);
+   state->setMode( GL_LIGHTING, osg::StateAttribute::OFF );
+
+   // scenegraph
+   osg::MatrixTransform* xfm = new osg::MatrixTransform;
+   xfm->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
+   xfm->setMatrix(osg::Matrix::identity());
+   osg::Geode* textnode = new osg::Geode;
+   textnode->addDrawable( _titletext );
+   textnode->addDrawable( _timetext );
+   textnode->addDrawable( _modetext );
+   xfm->addChild( textnode );
+   _projection->addChild( xfm );
+}
+
+
+HeadsUpDisplay::~HeadsUpDisplay()
+{
+}
+
+
+
+void HeadsUpDisplay::setTime( float t )
+{
+   if( t != _timevalue )
+   {
+      _timevalue = t;
+      _dirtytime = true;
+   }
+}
+
+
+void HeadsUpDisplay::setTitle( char* s )
+{
+   std::string tmp = std::string(s);
+   if( tmp != _titlestring )
+   {
+      _titlestring = tmp;
+      _dirtytitle = true;
+   }
+}
+
+
+void HeadsUpDisplay::setMode( char* s )
+{
+   std::string tmp = std::string(s);
+   if( tmp != _modestring )
+   {
+      _modestring = tmp;
+      _dirtymode = true;
+   }
+}
+
+
+
+void HeadsUpDisplay::update()
+{
+   
+   if( _dirtytime )
+   {
+      char timestr[20];
+      sprintf(timestr, "t = %-7.2f", _timevalue);
+      _timetext->setText(timestr);
+
+      // hud time now updated ...
+      _dirtytime = false;
+   }
+
+
+   if( _dirtymode )
+   {
+      _modetext->setText(_modestring.c_str());
+
+      // hud "recording/playback" text now updated ...
+      _dirtymode = false;
+   }
+
+
+   if( _dirtytitle )
+   {
+      _titletext->setText(_titlestring.c_str());
+
+      // hud title text now updated ...
+      _dirtytitle = false;
+   }
+}
Index: /anuga_core/source/anuga_viewer/swollen/hud.h
===================================================================
--- /anuga_core/source/anuga_viewer/swollen/hud.h	(revision 3887)
+++ /anuga_core/source/anuga_viewer/swollen/hud.h	(revision 3887)
@@ -0,0 +1,45 @@
+
+/*
+    Head's up viewer class
+
+    An OpenSceneGraph viewer for pyVolution SWW files.
+    copyright (C) 2004 Geoscience Australia
+*/
+
+
+#ifndef HUD_H
+#define HUD_H
+
+#include <project.h>
+#include <osg/Projection>
+#include <osgText/Font>
+#include <osgText/Text>
+
+
+class HeadsUpDisplay
+{
+
+public:
+
+    HeadsUpDisplay();
+    virtual void setTime(float t);
+    virtual void setTitle(char* s);
+    virtual void setMode(char* s);
+    virtual void update();
+    virtual osg::Projection* get(){ return _projection; }
+
+protected:
+
+    osg::Projection* _projection;
+    osgText::Text* _titletext;
+    osgText::Text* _timetext;
+    osgText::Text* _modetext;
+    float _timevalue;
+    std::string _modestring;
+    std::string _titlestring;
+    bool _dirtytime, _dirtymode, _dirtytitle;
+    virtual ~HeadsUpDisplay();
+
+};
+
+#endif  // HUD_H
Index: /anuga_core/source/anuga_viewer/swollen/keyboardeventhandler.cpp
===================================================================
--- /anuga_core/source/anuga_viewer/swollen/keyboardeventhandler.cpp	(revision 3887)
+++ /anuga_core/source/anuga_viewer/swollen/keyboardeventhandler.cpp	(revision 3887)
@@ -0,0 +1,191 @@
+
+#include <keyboardeventhandler.h>
+#include <iostream>
+
+
+KeyboardEventHandler::KeyboardEventHandler( int nTimesteps, float tps)
+{
+   _paused = DEF_PAUSED_START;
+   _direction = 1;
+   _ntimesteps = nTimesteps;
+   _tps = tps;
+   _tpsorig = tps;
+   _timestepchanged = false;
+   _timestep = 0;
+   _prevtime = 0;
+   _togglewireframe = false;
+   _toggleculling = false;
+   _togglerecording = false;
+   _toggleplayback = false;
+   _togglesave = false;
+}
+
+
+bool KeyboardEventHandler::handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter&)
+{
+   bool handled = false;
+
+   if( ea.getEventType() == osgGA::GUIEventAdapter::KEYDOWN )
+   {
+      switch( ea.getKey() )
+      {
+	    case osgGA::GUIEventAdapter::KEY_Space:
+            _paused = _paused ? false : true;
+            handled = true;
+            break;
+
+	    case osgGA::GUIEventAdapter::KEY_Up:
+            if( !_paused ) _tps *= 1.5;
+            handled = true;
+            break;
+
+	    case osgGA::GUIEventAdapter::KEY_Down:
+            if( !_paused ) _tps /= 1.5;
+            handled = true;
+            break;
+
+	    case 'r':
+            _paused = DEF_PAUSED_START;
+            _tps = _tpsorig;
+            _timestep = 0;
+            _timestepchanged = true;
+            handled = true;
+            break;
+
+	    case osgGA::GUIEventAdapter::KEY_Right:
+            if( _paused )
+            {
+               _timestep = (_timestep+1) % _ntimesteps;
+               _timestepchanged = true;
+            }
+            else
+               _direction = +1;
+            handled = true;
+            break;
+
+	    case osgGA::GUIEventAdapter::KEY_Left:
+            if( _paused ) 
+            {
+               _timestep = _timestep-1;
+               if( _timestep < 0 ) _timestep = _ntimesteps-1;
+               _timestepchanged = true;
+            }
+            else
+               _direction = -1;
+            handled = true;
+            break;
+
+	    case 'w':
+            _togglewireframe = true;
+            handled = true;
+            break;
+
+	    case 'c':
+            _toggleculling = true;
+            handled = true;
+            break;
+
+
+	    case '1':
+            _togglerecording = true;
+            handled = true;
+            break;
+
+	    case '2':
+            _toggleplayback = true;
+            handled = true;
+            break;
+
+	    case '3':
+            _togglesave = true;
+            handled = true;
+            break;
+
+      }
+
+   }
+   return handled;
+}
+
+
+void KeyboardEventHandler::setTime( float time)
+{
+   //std::cout << "setTime: " << time << std::endl;
+   //std::cout << "setTime: " << _tps << std::endl;
+   if( !isPaused()  &&  time - _prevtime > 1.0/_tps )
+   {
+      _prevtime = time;
+      _timestep = _timestep + _direction;
+      if( _timestep == _ntimesteps ) 
+	    _timestep = 0;
+      else if( _timestep < 0 ) 
+	    _timestep = _ntimesteps-1;
+      _timestepchanged = true;
+   }
+}
+
+
+bool KeyboardEventHandler::timestepChanged()
+{
+   if( _timestepchanged )
+   {
+      _timestepchanged = false;
+      return true;
+   }
+   return false;
+}
+
+
+bool KeyboardEventHandler::toggleWireframe()
+{
+   if( _togglewireframe )
+   {
+      _togglewireframe = false;
+      return true;
+   }
+   return false;
+}
+
+
+bool KeyboardEventHandler::toggleCulling()
+{
+   if( _toggleculling )
+   {
+      _toggleculling = false;
+      return true;
+   }
+   return false;
+}
+
+
+bool KeyboardEventHandler::toggleRecording()
+{
+   if( _togglerecording )
+   {
+      _togglerecording = false;
+      return true;
+   }
+   return false;
+}
+
+
+bool KeyboardEventHandler::togglePlayback()
+{
+   if( _toggleplayback )
+   {
+      _toggleplayback = false;
+      return true;
+   }
+   return false;
+}
+
+
+bool KeyboardEventHandler::toggleSave()
+{
+   if( _togglesave )
+   {
+      _togglesave = false;
+      return true;
+   }
+   return false;
+}
Index: /anuga_core/source/anuga_viewer/swollen/keyboardeventhandler.h
===================================================================
--- /anuga_core/source/anuga_viewer/swollen/keyboardeventhandler.h	(revision 3887)
+++ /anuga_core/source/anuga_viewer/swollen/keyboardeventhandler.h	(revision 3887)
@@ -0,0 +1,40 @@
+/*
+    SWWViewer
+
+    An OpenSceneGraph viewer for pyVolution SWW files.
+    copyright (C) 2004 Geoscience Australia
+*/
+
+#ifndef KEYBOARDEVENTHANDLER_H
+#define KEYBOARDEVENTHANDLER_H
+
+#include <project.h>
+#include <osgGA/GUIEventHandler>
+
+
+class KeyboardEventHandler : public osgGA::GUIEventHandler 
+{
+
+public:
+    KeyboardEventHandler( int nTimesteps, float tps);
+    virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter&);
+    virtual void accept(osgGA::GUIEventHandlerVisitor&) {}
+    virtual bool isPaused(){return _paused;}
+    virtual void setPaused(bool value){_paused = value;}
+    virtual bool timestepChanged();
+    virtual bool toggleWireframe();
+    virtual bool toggleCulling();
+    virtual bool toggleRecording();
+    virtual bool togglePlayback();
+    virtual bool toggleSave();
+    virtual int getTimestep(){return (unsigned int) _timestep;}
+    virtual void setTime(float time);
+
+private:
+    int _direction, _timestep, _ntimesteps;
+    float _tps, _prevtime, _tpsorig;
+    bool _paused, _timestepchanged, _togglewireframe, _toggleculling;
+    bool _togglerecording, _toggleplayback, _togglesave;
+};
+
+#endif  // KEYBOARDEVENTHANDLER_H
Index: /anuga_core/source/anuga_viewer/swollen/main.cpp
===================================================================
--- /anuga_core/source/anuga_viewer/swollen/main.cpp	(revision 3887)
+++ /anuga_core/source/anuga_viewer/swollen/main.cpp	(revision 3887)
@@ -0,0 +1,395 @@
+/*
+  SWWViewer
+
+  An OpenSceneGraph viewer for pyVolution SWW files.
+  copyright (C) 2004-2005 Geoscience Australia
+*/
+
+#include <iostream>
+#include <fstream>
+
+#include <osg/Group>
+#include <osg/Material>
+#include <osg/MatrixTransform>
+#include <osg/Notify>
+#include <osg/PositionAttitudeTransform>
+#include <osg/StateAttribute>
+#include <osgDB/FileNameUtils>
+#include <osgDB/FileUtils>
+
+#include <animation.h>
+#include <project.h>
+#include <SWWReader.h>
+#include <bedslope.h>
+#include <hud.h>
+#include <keyboardeventhandler.h>
+#include <directionallight.h>
+#include <state.h>
+#include <watersurface.h>
+#include <customviewer.h>
+#include <customargumentparser.h>
+
+// prototypes
+osg::Transform* createSky(float radius, const std::string filename);
+extern const char* version();
+
+
+int main( int argc, char **argv )
+{
+
+   // use an ArgumentParser object to manage the program arguments.
+   // this custom version detects if the last argument is a macro file
+   // and modifies the argument list accordingly so the following code works ...
+   CustomArgumentParser arguments( &argc, argv );
+
+
+   // set up the usage document
+   std::string appname = arguments.getApplicationName();
+   arguments.getApplicationUsage()->setDescription( appname );
+   arguments.getApplicationUsage()->setCommandLineUsage("swollen [options] swwfile ...");
+   arguments.getApplicationUsage()->addCommandLineOption("-help", "Display this information");
+   arguments.getApplicationUsage()->addCommandLineOption("-scale <float>", "Vertical scale factor");
+   arguments.getApplicationUsage()->addCommandLineOption("-tps <rate>", "Timesteps per second");
+   arguments.getApplicationUsage()->addCommandLineOption("-hmin <float>", "Height below which transparency is set to zero");
+   arguments.getApplicationUsage()->addCommandLineOption("-hmax <float>", "Height above which transparency is set to alphamax");
+   arguments.getApplicationUsage()->addCommandLineOption("-alphamin <float 0-1>", "Transparency value at hmin");
+   arguments.getApplicationUsage()->addCommandLineOption("-alphamax <float 0-1>", "Maximum transparency clamp value");
+   arguments.getApplicationUsage()->addCommandLineOption("-lightpos <float>,<float>,<float>", "x,y,z of bedslope directional light (z is up, default is overhead)");
+   arguments.getApplicationUsage()->addCommandLineOption("-movie <dirname>", "Save numbered images to named directory and quit");
+   arguments.getApplicationUsage()->addCommandLineOption("-loop", "Repeated (looped) playback of .swm files");
+   arguments.getApplicationUsage()->addCommandLineOption("-nosky", "Omit background sky");
+   arguments.getApplicationUsage()->addCommandLineOption("-cullangle <float angle 0-90>", "Cull triangles steeper than this value");
+   arguments.getApplicationUsage()->addCommandLineOption("-texture <file>", "Image to use for bedslope topography");
+   arguments.getApplicationUsage()->addCommandLineOption("-version", "Revision number and creation (not compile) date");
+
+
+   // construct the viewer.
+   CustomViewer viewer(arguments);
+
+
+   // set up with sensible default event handlers
+   viewer.setUpViewer( osgProducer::Viewer::STANDARD_SETTINGS );
+   viewer.setClearColor( osg::Vec4(DEF_BACKGROUND_COLOUR) );
+   //viewer.getCamera(0)->getRenderSurface()->setWindowRectangle(200,300,400,300);
+   viewer.getCullSettings().setComputeNearFarMode( osg::CullSettings::COMPUTE_NEAR_FAR_USING_PRIMITIVES );
+
+
+   // animation
+   StateList statelist;
+   bool playbackmode;
+   bool recordingmode = false;
+   bool savemovie = false;
+   bool loop = false;
+   unsigned int playback_index = 0;
+   std::string moviedir;
+   Animation animation;
+
+   if( arguments.isSWM() )
+   {
+      playbackmode = true;
+      statelist.read( arguments.getFilename() );
+      if( arguments.read("-movie",moviedir) ) 
+      {
+         savemovie = true;  // flag to store frames and quit ...
+         animation.setDirectory( moviedir );
+         animation.setViewer( viewer );
+      }
+      if( arguments.read("-loop") ) 
+         loop = true;
+   }
+   else
+   {
+      playbackmode = false;
+      savemovie = false;
+      loop = true;  // playback in none macro mode should loop (otherwise you don't get a chance to save)
+   }
+
+
+   // get details on keyboard and mouse bindings used by the viewer
+   viewer.getUsage(*arguments.getApplicationUsage());
+
+   // if user requested help, write it out to cout
+   if( arguments.read("-help") || arguments.read("--help") || arguments.read("-h") )
+   {
+      arguments.getApplicationUsage()->write(std::cout);
+      return 1;
+   }
+
+
+   // same for version info
+   if( arguments.read("-version") )
+   {
+      std::cout << version() << std::endl;
+      return 1;
+   }
+
+
+   // last argument is either an sww file or an swm macro recording
+   int lastarg = arguments.argc()-1;
+   std::string swwfile = arguments.argv()[lastarg];
+   arguments.remove(lastarg);
+   if( osgDB::getLowerCaseFileExtension(swwfile) != std::string("sww") )
+   {
+      std::cout << "Require last argument be an .sww/.swm file ... quitting" << std::endl;
+      return 1; 
+   }
+   SWWReader *sww = new SWWReader(swwfile);
+   if (sww->isValid() == false)
+   {
+      std::cout << "Unable to load " << swwfile << " ... is this really an .sww file?" << std::endl;
+      return 1;
+   }
+
+
+   // need swollen binary directory which contains resource images
+   char *ptr;
+   if( (ptr = getenv( "SWOLLEN_BINDIR" )) )
+      sww->setSwollenDir( std::string(ptr) );
+   else if( osgDB::getFilePath(argv[0]) == "" )
+      sww->setSwollenDir( std::string(".") );
+   else
+      sww->setSwollenDir( osgDB::getFilePath(argv[0]) );
+   std::cout << sww->getSwollenDir() << std::endl;
+
+
+   // default arguments and command line parameters
+   float tmpfloat, tps, vscale;
+   if( !arguments.read("-tps", tps) || tps <= 0.0 ) tps = DEF_TPS;
+   if( !arguments.read("-scale", vscale) ) vscale = 1.0;
+   if( arguments.read("-hmin",tmpfloat) ) sww->setHeightMin( tmpfloat );  
+   if( arguments.read("-hmax",tmpfloat) ) sww->setHeightMax( tmpfloat );      
+   if( arguments.read("-alphamin",tmpfloat) ) sww->setAlphaMin( tmpfloat );   
+   if( arguments.read("-alphamax",tmpfloat) ) sww->setAlphaMax( tmpfloat );
+   if( arguments.read("-cullangle",tmpfloat) ) sww->setCullAngle( tmpfloat );
+
+   std::string bedslopetexture;
+   if( arguments.read("-texture",bedslopetexture) ) sww->setBedslopeTexture( bedslopetexture );
+
+   // root node
+   osg::Group* rootnode = new osg::Group;
+
+   // transform
+   osg::PositionAttitudeTransform* model = new osg::PositionAttitudeTransform;
+   model->setName("position_attitude_transform");
+
+   // enscapsulates OpenGL state
+   osg::StateSet* rootStateSet = new osg::StateSet;
+
+   // Bedslope geometry
+   BedSlope* bedslope = new BedSlope(sww);
+
+   // Water geometry
+   WaterSurface* water = new WaterSurface(sww);
+
+   // Heads Up Display (text overlay)
+   HeadsUpDisplay* hud = new HeadsUpDisplay();
+   hud->setTitle("pyVolution SWW Viewer");
+   if( arguments.isSWM() )
+      hud->setMode("playback");
+
+
+   // Lighting
+   DirectionalLight* light = new DirectionalLight(rootStateSet);
+   light->setPosition( osg::Vec3(1,1,1) );  // z is up
+
+   std::string lightposstr;
+   while (arguments.read("-lightpos",lightposstr))
+   {
+      float x, y, z;
+      int count = sscanf( lightposstr.c_str(), "%f,%f,%f", &x, &y, &z );
+      if( count == 3 ) light->setPosition( osg::Vec3(x,y,z) );  // z is up
+      else osg::notify(osg::WARN) << "Invalid bedslope light position \"" << lightposstr << "\"" << std::endl;
+   }
+
+
+
+   // scenegraph hierarchy
+   rootnode->setStateSet(rootStateSet);
+   rootnode->addChild( hud->get() );
+   rootnode->addChild( light->get() );
+   rootnode->addChild(model);
+   model->addChild( bedslope->get() );
+   model->addChild( water->get() );
+
+
+   // allow vertical scaling from command line parameter
+   model->setScale( osg::Vec3(1.0, 1.0, vscale) );
+
+   // surrounding sky sphere
+   if( !arguments.read("-nosky") )
+      rootnode->addChild( createSky(10.0, sww->getSwollenDir() + std::string("/sky_small.jpg") ));
+
+
+   // add model to viewer.
+   viewer.setSceneData(rootnode);
+ 
+   // 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);
+      return 1;
+   }
+
+
+   // register additional event handler
+   KeyboardEventHandler* event_handler = new KeyboardEventHandler(sww->getNumberOfTimesteps(), tps);
+   viewer.getEventHandlerList().push_front(event_handler);
+
+ 
+   // create the windows and run the threads.
+   viewer.realize();
+
+
+   // initial camera position
+   CustomTerrainManipulator* terrainmanipulator = viewer.getTerrainManipulator();
+   terrainmanipulator->setNode( model );
+   terrainmanipulator->setAutoComputeHomePosition( false );
+   terrainmanipulator->setHomePosition(
+      osg::Vec3d(0,-3,3),    // camera location
+      osg::Vec3d(0,0,0),     // camera target
+      osg::Vec3d(0,1,1) );   // camera up vector
+   terrainmanipulator->moveToHome();
+   terrainmanipulator->enable();
+
+ 
+   unsigned int timestep = 0;
+   bool done = false;
+   while( !done )
+   {
+    
+      // wait for all cull and draw threads to complete.
+      viewer.sync();
+
+      // user hits 'escape' to exit
+      if( viewer.done() )
+         done = true;
+
+
+      if( !playbackmode )
+      {
+
+         // current time
+         double time = viewer.getFrameStamp()->getReferenceTime();
+
+         event_handler->setTime( time );
+         timestep = event_handler->getTimestep();
+         water->setTimeStep(timestep);
+         hud->setTime( sww->getTime(timestep) );
+
+
+         // events
+         if( event_handler->toggleWireframe() )
+            water->toggleWireframe();
+         if( event_handler->toggleCulling() )
+            water->toggleCulling();
+
+         // '1' key starts/stops recording of view/position/setting info
+         if( event_handler->toggleRecording() )
+         {
+            switch( recordingmode )
+            {
+               case false : 
+                  recordingmode = true; 
+                  hud->setMode("recording");
+                  break;
+               case true : 
+                  recordingmode = false; 
+                  hud->setMode("");
+                  break;
+            }
+         }
+
+
+         // '2' key starts playback of recorded frames
+         if( event_handler->togglePlayback() && statelist.size() > 0 )
+         {
+            recordingmode = false;
+            playbackmode = true; 
+            hud->setMode("playback");
+            event_handler->setPaused( true );
+            playback_index = 0;
+         }
+
+         if( recordingmode )
+         {
+            State state = State();
+            state.setTimestep( event_handler->getTimestep() );
+            state.setCulling( sww->getCulling() );
+            state.setWireframe( water->getWireframe() );
+            state.setMatrix( viewer.getViewMatrix() );
+            statelist.push_back( state );
+         }
+      }
+
+      else
+
+      {
+         // might need to save image from previous pass ...
+         if( savemovie )
+            animation.saveImage( playback_index );
+
+         // in playback mode
+         State state = statelist.at( playback_index );
+         water->setTimeStep( state.getTimestep() );
+         water->setWireframe( state.getWireframe() );
+         water->setCulling( state.getCulling() );
+         hud->setTime( sww->getTime(state.getTimestep()) );
+
+         // loop playback
+         playback_index ++;
+         if( playback_index == statelist.size() )
+         {
+            if( loop )
+               playback_index = 0;
+            else
+               done = true;
+         }
+
+         // '2' key stops playback of recorded frames
+         if( event_handler->togglePlayback() )
+         {
+            playbackmode = false; 
+            hud->setMode("");
+            event_handler->setPaused( true );
+         }
+
+         viewer.setView( state.getMatrix() );
+      }
+
+
+      // '3' key causes compiled animation to be saved to disk
+      if( event_handler->toggleSave() )
+      {
+         std::fstream f;
+         f.open( "movie.swm", std::fstream::out );
+         if( f.is_open() )
+         {
+            f << "# SWM 1.0 START" << std::endl;
+            arguments.write( f );
+            statelist.write( f );
+            f << "# SWM END" << std::endl;
+            f.close();
+         }
+         std::cout << "Wrote macro file movie.swm" << std::endl;
+      }
+
+      // scene-graph updates
+      bedslope->update();
+      hud->update();
+      water->update();
+
+      // update the scene by traversing with the update visitor 
+      viewer.update();
+
+      // fire off the cull and draw traversals of the scene.
+      viewer.frame();
+   }
+    
+   // wait for all cull and draw threads to complete before exit.
+   viewer.sync();
+
+   return 0;
+}
Index: /anuga_core/source/anuga_viewer/swollen/main.vcproj
===================================================================
--- /anuga_core/source/anuga_viewer/swollen/main.vcproj	(revision 3887)
+++ /anuga_core/source/anuga_viewer/swollen/main.vcproj	(revision 3887)
@@ -0,0 +1,229 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="7.10"
+	Name="Swollen"
+	ProjectGUID="{CAC216DA-D220-440D-B72D-BAD4CB15D45F}"
+	RootNamespace="SWWViewer"
+	Keyword="Win32Proj">
+	<Platforms>
+		<Platform
+			Name="Win32"/>
+	</Platforms>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="Debug"
+			IntermediateDirectory="Debug"
+			ConfigurationType="1"
+			CharacterSet="2">
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="..\include;.\;&quot;$(OSG_ROOT)\OpenSceneGraph\include&quot;;&quot;$(OSG_ROOT)\Producer\include&quot;;&quot;$(OSG_ROOT)\OpenThreads\include&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+				MinimalRebuild="TRUE"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="TRUE"
+				UsePrecompiledHeader="2"
+				WarningLevel="3"
+				Detect64BitPortabilityProblems="TRUE"
+				DebugInformationFormat="4"/>
+			<Tool
+				Name="VCCustomBuildTool"/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="swwreaderd.lib osgd.lib osgdbd.lib osgtextd.lib osggad.lib osgutild.lib osgproducerd.lib Producerd.lib OpenThreadsWin32d.lib"
+				OutputFile="../bin/swollend.exe"
+				LinkIncremental="2"
+				AdditionalLibraryDirectories="..\lib;&quot;$(OSG_ROOT)\OpenSceneGraph\lib&quot;;&quot;$(OSG_ROOT)/Producer/lib&quot;;&quot;$(OSG_ROOT)/OpenThreads/lib/win32&quot;"
+				GenerateDebugInformation="TRUE"
+				ProgramDatabaseFile="$(OutDir)/SWWViewer.pdb"
+				SubSystem="1"
+				TargetMachine="1"/>
+			<Tool
+				Name="VCMIDLTool"/>
+			<Tool
+				Name="VCPostBuildEventTool"/>
+			<Tool
+				Name="VCPreBuildEventTool"/>
+			<Tool
+				Name="VCPreLinkEventTool"/>
+			<Tool
+				Name="VCResourceCompilerTool"/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"/>
+			<Tool
+				Name="VCWebDeploymentTool"/>
+			<Tool
+				Name="VCManagedWrapperGeneratorTool"/>
+			<Tool
+				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="Release"
+			IntermediateDirectory="Release"
+			ConfigurationType="1"
+			CharacterSet="2">
+			<Tool
+				Name="VCCLCompilerTool"
+				AdditionalIncludeDirectories="./;../include;&quot;$(OSG_ROOT)/OpenSceneGraph/include&quot;;&quot;$(OSG_ROOT)/Producer/include&quot;;&quot;$(OSG_ROOT)/OpenThreads/include&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+				RuntimeLibrary="2"
+				RuntimeTypeInfo="TRUE"
+				UsePrecompiledHeader="2"
+				WarningLevel="3"
+				Detect64BitPortabilityProblems="TRUE"
+				DebugInformationFormat="3"/>
+			<Tool
+				Name="VCCustomBuildTool"/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="swwreader.lib osg.lib osgdb.lib osgtext.lib osgga.lib osgutil.lib osgproducer.lib Producer.lib OpenThreadsWin32.lib"
+				OutputFile="../bin/swollen.exe"
+				LinkIncremental="1"
+				AdditionalLibraryDirectories="../lib;&quot;$(OSG_ROOT)/OpenSceneGraph/lib&quot;;&quot;$(OSG_ROOT)/Producer/lib&quot;;&quot;$(OSG_ROOT)/OpenThreads/lib/win32&quot;"
+				GenerateDebugInformation="TRUE"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="1"/>
+			<Tool
+				Name="VCMIDLTool"/>
+			<Tool
+				Name="VCPostBuildEventTool"/>
+			<Tool
+				Name="VCPreBuildEventTool"/>
+			<Tool
+				Name="VCPreLinkEventTool"/>
+			<Tool
+				Name="VCResourceCompilerTool"/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"/>
+			<Tool
+				Name="VCWebDeploymentTool"/>
+			<Tool
+				Name="VCManagedWrapperGeneratorTool"/>
+			<Tool
+				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="Source Files"
+			Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
+			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}">
+			<File
+				RelativePath=".\animation.cpp">
+			</File>
+			<File
+				RelativePath=".\bedslope.cpp">
+			</File>
+			<File
+				RelativePath=".\createSky.cpp">
+			</File>
+			<File
+				RelativePath=".\customargumentparser.cpp">
+			</File>
+			<File
+				RelativePath=".\customterrainmanipulator.cpp">
+			</File>
+			<File
+				RelativePath=".\customtrackball.cpp">
+			</File>
+			<File
+				RelativePath=".\customviewer.cpp">
+			</File>
+			<File
+				RelativePath=".\customviewereventhandler.cpp">
+			</File>
+			<File
+				RelativePath=".\directionallight.cpp">
+			</File>
+			<File
+				RelativePath=".\hud.cpp">
+			</File>
+			<File
+				RelativePath=".\keyboardeventhandler.cpp">
+			</File>
+			<File
+				RelativePath=".\main.cpp">
+			</File>
+			<File
+				RelativePath=".\spotlight.cpp">
+			</File>
+			<File
+				RelativePath=".\state.cpp">
+			</File>
+			<File
+				RelativePath=".\surface.cpp">
+			</File>
+			<File
+				RelativePath=".\version.cpp">
+			</File>
+			<File
+				RelativePath=".\watersurface.cpp">
+			</File>
+		</Filter>
+		<Filter
+			Name="Header Files"
+			Filter="h;hpp;hxx;hm;inl;inc;xsd"
+			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}">
+			<File
+				RelativePath=".\animation.h">
+			</File>
+			<File
+				RelativePath=".\bedslope.h">
+			</File>
+			<File
+				RelativePath=".\customargumentparser.h">
+			</File>
+			<File
+				RelativePath=".\customterrainmanipulator.h">
+			</File>
+			<File
+				RelativePath=".\customtrackball.h">
+			</File>
+			<File
+				RelativePath=".\customviewer.h">
+			</File>
+			<File
+				RelativePath=".\customviewereventhandler.h">
+			</File>
+			<File
+				RelativePath=".\directionallight.h">
+			</File>
+			<File
+				RelativePath=".\hud.h">
+			</File>
+			<File
+				RelativePath=".\keyboardeventhandler.h">
+			</File>
+			<File
+				RelativePath="..\include\project.h">
+			</File>
+			<File
+				RelativePath=".\spotlight.h">
+			</File>
+			<File
+				RelativePath=".\state.h">
+			</File>
+			<File
+				RelativePath=".\surface.h">
+			</File>
+			<File
+				RelativePath=".\watersurface.h">
+			</File>
+		</Filter>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
Index: /anuga_core/source/anuga_viewer/swollen/saveImage.cpp
===================================================================
--- /anuga_core/source/anuga_viewer/swollen/saveImage.cpp	(revision 3887)
+++ /anuga_core/source/anuga_viewer/swollen/saveImage.cpp	(revision 3887)
@@ -0,0 +1,38 @@
+/*
+  SWWViewer
+
+  An OpenSceneGraph viewer for pyVolution SWW files.
+  copyright (C) 2004-2005 Geoscience Australia
+*/
+
+#include <iostream>
+#include <osg/Image>
+#include <osgDB/WriteFile>
+#include <osgDB/FileNameUtils>
+#include <customviewer.h>
+
+
+bool saveImage(unsigned int playback_index, CustomViewer viewer, std::string moviedir)
+{
+
+   int x, y;
+   unsigned int width, height;
+
+   viewer.getCamera(0)->getProjectionRectangle(x, y, width, height);
+
+   osg::ref_ptr<osg::Image> image = new osg::Image;
+
+   image->readPixels(x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE);
+
+
+
+
+   if ( osgDB::writeImageFile(*image, "image.jpg") )
+   {
+      std::cout << "Saved image " << playback_index << std::endl;
+      return true;
+   }
+
+   return false;
+
+}
Index: /anuga_core/source/anuga_viewer/swollen/spotlight.cpp
===================================================================
--- /anuga_core/source/anuga_viewer/swollen/spotlight.cpp	(revision 3887)
+++ /anuga_core/source/anuga_viewer/swollen/spotlight.cpp	(revision 3887)
@@ -0,0 +1,76 @@
+
+#include <math.h>
+#include <spotlight.h>
+#include <osg/ShapeDrawable>
+#include <iostream>
+
+#define DEF_DEFAULT_ANGLE 45
+#define DEF_DEFAULT_EXPONENT 10
+#define DEF_DEFAULT_CONE_RADIUS 0.125
+#define DEF_DEFAULT_CONE_HEIGHT 0.25
+
+
+SpotLight::SpotLight(osg::StateSet* rootStateSet, int num)
+{
+    // positioning container
+    _transform = new osg::MatrixTransform;
+    _transform->setCullingActive(false);
+
+    // OpenGL light
+    _light = new osg::Light;
+    _light->setLightNum(num);
+    _light->setPosition(osg::Vec4(0,0,0,1));
+    _light->setAmbient(osg::Vec4(0.7,0.7,0.7,1));
+    _light->setDiffuse(osg::Vec4(1,1,1,1));
+    _light->setSpecular(osg::Vec4(1,1,1,1));
+    _light->setSpotCutoff(DEF_DEFAULT_ANGLE);
+    _light->setSpotExponent(DEF_DEFAULT_EXPONENT);
+
+    // Scenegraph node
+    _source = new osg::LightSource;
+    _source->setReferenceFrame( osg::LightSource::RELATIVE_RF );
+    _source->setLight( _light );
+    _source->setLocalStateSetModes( osg::StateAttribute::ON ); 
+    _source->setStateSetModes( *rootStateSet, osg::StateAttribute::ON );
+    _transform->addChild( _source );
+
+    // create cone marker for spotlight and rotate towards origin
+    _marker = new osg::Geode;
+    _shape = new osg::Cone;
+    _shape->setRadius(DEF_DEFAULT_CONE_RADIUS);
+    _shape->setHeight(DEF_DEFAULT_CONE_HEIGHT);
+    _shape->setCenter( osg::Vec3(0,0,0) );
+    _marker->addDrawable( new osg::ShapeDrawable(_shape) );
+
+    // omit marker cone for now (FIX ME)
+    _transform->addChild(_marker);
+
+    setPosition( osg::Vec3(0,0,1) );  // default overhead
+}
+
+
+void SpotLight::setSpotAngle( float degrees )
+{
+    _light->setSpotCutoff( degrees );
+    float radians = degrees * osg::PI / 180.0;
+    float cone_radius = tan( radians/2.0 ) * _shape->getHeight();
+    _shape->setRadius( cone_radius );
+}
+
+
+void SpotLight::setPosition(osg::Vec3 v)
+{
+    osg::Vec3 origindir;
+    osg::Quat quat;
+    origindir.set( -v );
+    origindir.normalize();
+    quat.makeRotate( osg::Vec3(0,0,-1), origindir );
+
+    _transform->setMatrix( osg::Matrix::rotate( quat ) * osg::Matrix::translate(v) );
+}
+
+
+
+SpotLight::~SpotLight()
+{
+}
Index: /anuga_core/source/anuga_viewer/swollen/spotlight.h
===================================================================
--- /anuga_core/source/anuga_viewer/swollen/spotlight.h	(revision 3887)
+++ /anuga_core/source/anuga_viewer/swollen/spotlight.h	(revision 3887)
@@ -0,0 +1,61 @@
+
+
+/*
+    SpotLight class
+
+    An OpenSceneGraph viewer for pyVolution .sww files.
+    copyright (C) 2004 Geoscience Australia
+*/
+
+
+#ifndef SPOTLIGHT_H
+#define SPOTLIGHT_H
+
+
+#include <project.h>
+#include <osg/Geode>
+#include <osg/Geometry>
+#include <osg/Group>
+#include <osg/Light>
+#include <osg/LightSource>
+#include <osg/MatrixTransform>
+#include <osg/StateAttribute>
+#include <osg/StateSet>
+#include <osg/Vec3>
+
+
+
+class SpotLight
+{
+
+public:
+
+    SpotLight(osg::StateSet* rootStateSet, int num=0);
+    virtual osg::Group* get(){ return _transform; }
+    virtual void setPosition(osg::Vec3 v);
+    virtual osg::Vec3f* getPosition(){ return _position; }
+    void setSpotAngle( float degrees );
+    void setMatrix( osg::Matrixf matrix ){ _transform->setMatrix( matrix ); }
+    void setAmbient( osg::Vec3f v ){ _light->setAmbient(osg::Vec4(v,1)); }
+    void setDiffuse( osg::Vec3f v ){ _light->setDiffuse(osg::Vec4(v,1)); }
+    const osg::Matrix getMatrix(){ return _transform->getMatrix(); }
+
+
+protected:
+
+    osg::MatrixTransform* _transform;
+    osg::Vec3* _position;
+    osg::Vec3* _target;
+    osg::Light* _light;
+    osg::LightSource* _source;
+
+    osg::Geode* _marker;
+    osg::Cone* _shape;
+    osg::Geometry* _geom;
+    osg::StateSet* _stateset;
+    virtual ~SpotLight();
+
+};
+
+
+#endif  // SPOTLIGHT_H
Index: /anuga_core/source/anuga_viewer/swollen/state.cpp
===================================================================
--- /anuga_core/source/anuga_viewer/swollen/state.cpp	(revision 3887)
+++ /anuga_core/source/anuga_viewer/swollen/state.cpp	(revision 3887)
@@ -0,0 +1,137 @@
+/*
+    State class
+
+    An OpenSceneGraph viewer for pyVolution .sww files.
+    copyright (C) 2004-2005 Geoscience Australia
+*/
+
+
+#define MAX_LINE_LENGTH 200
+
+#include <state.h>
+
+
+// constructor
+State::State()
+{
+
+}
+
+
+State::~State()
+{
+}
+
+
+// serialize to named stream
+void State::write(std::ostream& s)
+{
+    s << _timestep << " ";
+    s << _position.x() << " " << _position.y() << " " << _position.z() << " ";
+    s << _orientation.x() << " " << _orientation.y() << " " << _orientation.z() << " " << _orientation.w() << " ";
+    s << _culling << " ";
+    s << _wireframe << std::endl;
+}
+
+
+// reconstitute self from named stream
+bool State::read(std::istream& s)
+{
+   s >> _timestep
+     >>  _position.x() >> _position.y() >> _position.z()
+     >>  _orientation.x() >> _orientation.y() >> _orientation.z() >> _orientation.w()
+     >> _culling
+     >> _wireframe;
+
+   return s.fail() ? false : true;
+}
+
+
+void State::setMatrix(osg::Matrix m)
+{
+   m.get(_orientation);
+   _position = m.getTrans();
+
+}
+
+osg::Matrix State::getMatrix()
+{
+   osg::Matrix m;
+   m.set(_orientation);
+   m.setTrans(_position);
+   return m;
+}
+
+
+
+
+StateList::StateList() : std::vector<State>()
+{
+}
+
+
+bool StateList::write(std::ostream& s)
+{
+   if( this->size() == 0 )
+      return false;
+
+   s << "# SWM DATA" << std::endl;
+   StateList::iterator iter = this->begin();
+   for( ; iter < this->end(); iter++ )
+      (*iter).write( s );
+
+   return true;
+}
+
+
+
+
+
+bool StateList::read(std::string filename)
+{
+
+   // attempt to open the macro file ...
+   std::fstream f;
+   f.open( filename.c_str(), std::fstream::in );
+   if( f.is_open() )
+   {
+      char str[MAX_LINE_LENGTH];
+      char *p;
+      int index = 0;
+
+      // search for data section
+      // FIXME: not very safe, what if data section missing?
+      do 
+      {
+         f.getline(str, MAX_LINE_LENGTH);
+         p = strstr(str, "# SWM DATA");
+         index++;
+
+      } while( p != str );
+
+
+      bool valid;
+      index = 0;
+      do
+      {
+         State* s = new State();
+         valid = s->read(f);
+         if( valid )
+         {
+            this->push_back( *s );
+            index++;
+         }
+      } while( valid );
+
+      std::cout << "Read " << index << " frames of animation for playback ..." << std::endl;
+
+      f.close();
+
+      // success only on non-empty animation
+      if( index )
+         return true;
+   }
+
+   return false;
+
+}
Index: /anuga_core/source/anuga_viewer/swollen/state.h
===================================================================
--- /anuga_core/source/anuga_viewer/swollen/state.h	(revision 3887)
+++ /anuga_core/source/anuga_viewer/swollen/state.h	(revision 3887)
@@ -0,0 +1,102 @@
+
+/*
+  state.h, encapsulate current camera view and animation settings
+
+  An OpenSceneGraph viewer for pyVolution SWW files.
+  copyright (C) 2004-2005 Geoscience Australia
+*/
+
+
+/*
+  Finite State Machine for animation
+      
+  0 1 1 begin storing view/orientation/cull settings
+  0 2 2 if exists, store current settings, commence playback of saved animation path
+  0 3 0 save animation to disk
+
+  1 1 0 stop storing settings
+  1 2 2 stop storing settings and commence playback
+  1 3 1 save animation, keep recording
+
+  2 2 0 stop playback, return to saved settings
+  2 1 2 no effect
+  2 3 2 save animation, continue playing back
+
+  header comprised of original command line arguments
+
+  state comprised of:
+  frame
+  elapsedtime
+  modelview matrix
+  timestep
+  cullsteep
+  wireframe
+  
+*/
+
+#ifndef STATE_H
+#define STATE_H
+
+#include <project.h>
+#include <osg/Quat>
+#include <osg/Vec3>
+#include <osg/Matrix>
+#include <istream>
+#include <vector>
+#include <fstream>
+#include <iostream>
+
+
+class State
+{
+
+public:
+
+    State();
+    virtual void setTimestep(unsigned int value){ _timestep = value; }
+    virtual void setCulling(bool value){ _culling = value; }
+    virtual void setWireframe(bool value){ _wireframe = value; }
+    virtual void setMatrix(osg::Matrix value);
+
+    virtual unsigned int getTimestep(){ return _timestep; }
+    virtual osg::Matrix getMatrix();
+    virtual bool getCulling(){ return _culling; }
+    virtual bool getWireframe(){ return _wireframe; }
+
+    virtual void write(std::ostream &s);
+    virtual bool read(std::istream& s);
+
+    virtual ~State();
+
+
+protected:
+
+    unsigned int _timestep;
+    osg::Vec3d _position;
+    osg::Quat _orientation;
+    //osg::Matrix _matrix;
+    bool _culling;
+    bool _wireframe;
+ 
+};
+
+
+
+
+class StateList : public std::vector<State>
+{
+
+public:
+
+   StateList();
+   bool write(std::ostream& s);
+   bool read(std::string filename);
+
+   //void ~StateList();
+
+protected:
+ 
+};
+
+
+#endif  // STATE_H
Index: /anuga_core/source/anuga_viewer/swollen/surface.cpp
===================================================================
--- /anuga_core/source/anuga_viewer/swollen/surface.cpp	(revision 3887)
+++ /anuga_core/source/anuga_viewer/swollen/surface.cpp	(revision 3887)
@@ -0,0 +1,2 @@
+
+#include <surface.h>
Index: /anuga_core/source/anuga_viewer/swollen/surface.h
===================================================================
--- /anuga_core/source/anuga_viewer/swollen/surface.h	(revision 3887)
+++ /anuga_core/source/anuga_viewer/swollen/surface.h	(revision 3887)
@@ -0,0 +1,39 @@
+/*
+    Surface class
+
+    An OpenSceneGraph viewer for pyVolution .sww files.
+    copyright (C) 2004 Geoscience Australia
+*/
+
+
+#ifndef SURFACE_H
+#define SURFACE_H
+
+
+#include <project.h>
+#include <osg/Geode>
+#include <osg/Material>
+#include <osg/StateAttribute>
+
+
+
+class Surface
+{
+
+public:
+
+    Surface();
+    virtual osg::Geode* get(){ return _node; }
+
+protected:
+
+    osg::Geode* _node;
+    osg::Geometry* _geom;
+    osg::StateSet* _stateset;
+    osg::Material* _material;
+    virtual ~Surface();
+
+};
+
+
+#endif  // SURFACE_H
Index: /anuga_core/source/anuga_viewer/swollen/version.cpp
===================================================================
--- /anuga_core/source/anuga_viewer/swollen/version.cpp	(revision 3887)
+++ /anuga_core/source/anuga_viewer/swollen/version.cpp	(revision 3887)
@@ -0,0 +1,1 @@
+const char* version() { const char* s = "Revision: 1992M"; return s; }
Index: /anuga_core/source/anuga_viewer/swollen/watersurface.cpp
===================================================================
--- /anuga_core/source/anuga_viewer/swollen/watersurface.cpp	(revision 3887)
+++ /anuga_core/source/anuga_viewer/swollen/watersurface.cpp	(revision 3887)
@@ -0,0 +1,196 @@
+/*
+  WaterSurface class
+
+  An OpenSceneGraph viewer for pyVolution .sww files.
+  copyright (C) 2004 Geoscience Australia
+*/
+
+
+#include <watersurface.h>
+#include <osg/AlphaFunc>
+#include <osg/BlendFunc>
+#include <osg/PolygonMode>
+#include <osg/ShapeDrawable>
+#include <osg/Texture2D>
+#include <osg/TexEnv>
+#include <osg/TexGen>
+
+#include <osgDB/ReadFile>
+//#include <osgUtil/TriStripVisitor>
+
+#define DEF_ALPHA_THRESHOLD 0.05
+
+
+// constructor
+WaterSurface::WaterSurface(SWWReader* sww)
+{
+   // persistent
+   _sww = sww;
+   _node = new osg::Geode;
+   _geom = new osg::Geometry;
+   _stateset = new osg::StateSet;
+
+   // construct local scenegraph hierarchy
+   _node->setName("watersurface");
+   _node->addDrawable(_geom);
+   _node->setStateSet(_stateset);
+
+   _geom->setUseDisplayList( false );
+
+
+   // surface starts on first timestep
+   _timestep = 0;
+   _dirtydata = true;  // will force load of watersurface
+
+   // default is filled watersurface
+   _wireframe = false;
+   _dirtywireframe = true;  // will force wireframe refresh
+
+
+   // default is steep culling on
+   _culling = true;
+   _dirtyculling = true;  // will force culling ...
+
+
+   // environment map
+   osg::Texture2D* texture = new osg::Texture2D;
+   texture->setDataVariance(osg::Object::DYNAMIC);
+   texture->setBorderColor(osg::Vec4(1.0f,1.0f,1.0f,0.5f));
+   texture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
+   std::string* envmap = new std::string( _sww->getSwollenDir() + std::string("/") + std::string("envmap.jpg") );
+   texture->setImage(osgDB::readImageFile( envmap->c_str() ));
+   _stateset->setTextureAttributeAndModes( 1, texture, osg::StateAttribute::ON );
+   _stateset->setMode( GL_LIGHTING, osg::StateAttribute::ON );
+
+   // surface transparency
+   osg::BlendFunc* osgBlendFunc = new osg::BlendFunc();
+   osgBlendFunc->setFunction(osg::BlendFunc::SRC_ALPHA, osg::BlendFunc::ONE_MINUS_SRC_ALPHA);
+   _stateset->setAttribute(osgBlendFunc);
+   _stateset->setMode(GL_BLEND, osg::StateAttribute::ON);
+   _stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
+
+   // discard pixels with an alpha value below threshold
+   osg::AlphaFunc* alphaFunc = new osg::AlphaFunc;
+   alphaFunc->setFunction( osg::AlphaFunc::GREATER, DEF_ALPHA_THRESHOLD );
+   _stateset->setAttributeAndModes( alphaFunc, osg::StateAttribute::ON );
+
+   // automatically generate texture coords
+   osg::TexGen* texgen = new osg::TexGen;
+   texgen->setMode( osg::TexGen::SPHERE_MAP );
+
+   osg::TexEnv* texenv = new osg::TexEnv;
+   texenv->setMode( osg::TexEnv::DECAL );
+   //texenv->setMode( osg::TexEnv::BLEND );
+   texenv->setColor( osg::Vec4(0.6f,0.6f,0.6f,0.2f) );
+   _stateset->setTextureAttributeAndModes( 1, texgen, osg::StateAttribute::ON );
+   _stateset->setTextureAttribute( 1, texenv );
+
+}
+
+
+
+WaterSurface::~WaterSurface()
+{
+}
+
+
+
+void WaterSurface::setTimeStep(unsigned int ts)
+{
+   if( ts != _timestep )
+   {
+      _timestep = ts;
+      _dirtydata = true;
+   }
+}
+
+
+void WaterSurface::setWireframe(bool value)
+{
+   if( value != _wireframe )
+   {
+      _wireframe = value;
+      _dirtywireframe = true;
+   }
+}
+
+void WaterSurface::setCulling(bool value)
+{
+   if( value != _culling )
+   {
+      _culling = value;
+      _dirtyculling = true;
+      _dirtydata = true;
+   }
+}
+
+
+void WaterSurface::update()
+{
+
+   if( _dirtyculling )
+   {
+      _sww->setCulling( _culling );
+      _dirtyculling = false;
+   }
+
+   if( _dirtydata )
+   {
+      // delete if exists
+      if( _geom->getNumPrimitiveSets() )
+         _geom->removePrimitiveSet(0);  // reference counting does actual delete
+
+      // local reference to raw height field data
+      osg::ref_ptr<osg::Vec3Array> vertices = _sww->getStageVertexArray(_timestep);
+      osg::ref_ptr<osg::Vec3Array> vertexnormals = _sww->getStageVertexNormalArray();
+      osg::ref_ptr<osg::Vec4Array> colors = _sww->getStageColorArray();
+
+      // geometry
+      _geom->setVertexArray( vertices.get() );
+      _geom->addPrimitiveSet( _sww->getBedslopeIndexArray().get() );
+
+      // per vertex colors (we only modulate the alpha for transparency)
+      _geom->setColorArray( colors.get() );
+      _geom->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
+
+      // normals
+      _geom->setNormalArray( vertexnormals.get() );
+      _geom->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
+
+      // osgUtil::TriStripVisitor* optimize = new osgUtil::TriStripVisitor();
+      // optimize->stripify( *_geom );
+
+      // water surface corresponding to _timestep is now (re)loaded ...
+      _dirtydata = false;
+   }
+
+
+   if( _dirtywireframe )
+   {
+      osg::PolygonMode* polyModeObj = 
+         dynamic_cast<osg::PolygonMode*>(_stateset->getAttribute(osg::StateAttribute::POLYGONMODE));
+
+      if (!polyModeObj) 
+      {
+         polyModeObj = new osg::PolygonMode;
+         _stateset->setAttribute(polyModeObj);
+      }
+
+      switch( _wireframe )
+      {
+         case true :
+            polyModeObj->setMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE);
+            break;
+
+         case false :
+            polyModeObj->setMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::FILL);
+            break;
+      }
+
+      // desired wireframe mode now loaded ...
+      _dirtywireframe = false;
+
+   }
+
+
+}
Index: /anuga_core/source/anuga_viewer/swollen/watersurface.h
===================================================================
--- /anuga_core/source/anuga_viewer/swollen/watersurface.h	(revision 3887)
+++ /anuga_core/source/anuga_viewer/swollen/watersurface.h	(revision 3887)
@@ -0,0 +1,53 @@
+
+/*
+    WaterSurface class
+
+    An OpenSceneGraph viewer for pyVolution .sww files.
+    copyright (C) 2004 Geoscience Australia
+*/
+
+
+#ifndef WATERSURFACE_H
+#define WATERSURFACE_H
+
+
+#include <project.h>
+#include <SWWReader.h>
+#include <osg/Geode>
+#include <osg/StateAttribute>
+
+
+class WaterSurface
+{
+
+public:
+
+    WaterSurface(SWWReader *sww);
+    virtual osg::Geode* get(){ return _node; }
+    virtual void setTimeStep( unsigned int ts );
+
+    virtual void setWireframe(bool value);
+    virtual bool getWireframe(){ return _wireframe; };
+    virtual void toggleWireframe(){ setWireframe( _wireframe ? false : true ); };
+
+    virtual void setCulling(bool value);
+    virtual bool getCulling(){ return _culling; };
+    virtual void toggleCulling(){ setCulling( _culling ? false : true ); };
+
+    virtual void update();
+
+protected:
+
+    SWWReader* _sww;
+    osg::Geode* _node;
+    osg::Geometry* _geom;
+    osg::StateSet* _stateset;
+    unsigned int _timestep;
+    virtual ~WaterSurface();
+    bool _wireframe, _dirtywireframe;
+    bool _culling, _dirtyculling;
+    bool _dirtydata;
+};
+
+
+#endif  // WATERSURFACE_H
Index: /anuga_core/source/anuga_viewer/swwreader/Makefile
===================================================================
--- /anuga_core/source/anuga_viewer/swwreader/Makefile	(revision 3887)
+++ /anuga_core/source/anuga_viewer/swwreader/Makefile	(revision 3887)
@@ -0,0 +1,40 @@
+
+#
+#  Mac OS X Makefile
+#
+
+TOPDIR           =  ..
+
+OPTIMIZATION     =  -O2
+#OPTIMIZATION     =  -g
+
+NETCDF_INCLUDE   =  /sw/include
+OSG_INCLUDE      =  ${OSGHOME}/include
+LOCAL_INCLUDE    =  $(TOPDIR)/include
+GDAL_INCLUDE     =  `gdal-config --cflags`
+INCLUDES         =  -I $(LOCAL_INCLUDE) -I $(NETCDF_INCLUDE) -I $(OSG_INCLUDE) $(GDAL_INCLUDE)
+
+CARBON_LIB       =  -framework Carbon
+GL_LIBS          =  -framework OpenGL
+NETCDF_LIBS      =  -lnetcdf
+X_LIBS           =  -lX11
+GDAL_LIBS        =  `gdal-config --libs`
+OTHER_LIBS       =  -lm -lstdc++ -lobjc
+LIBS            +=  -losg -losgDB -lOpenThreads $(NETCDF_LIBS) $(GL_LIBS) $(X_LIBS) $(OTHER_LIBS) $(CARBON_LIB) $(GDAL_LIBS)
+LIBDIRS          =  -L/usr/lib -L/usr/X11R6/lib -L/sw/lib -L/usr/local/lib
+
+NAME             =  swwreader
+TARGET           =  $(TOPDIR)/bin/lib$(NAME).dylib
+
+
+$(TARGET) : $(NAME).o
+	g++ $(OPTIMIZATION) -dynamiclib -F/System/Library/Frameworks $(LIBDIRS) $(NAME).o $(LIBS) -o $(TARGET)
+
+
+$(NAME).o : $(NAME).cpp $(LOCAL_INCLUDE)/$(NAME).h
+	g++ $(OPTIMIZATION) $(INCLUDES) -c $(NAME).cpp
+
+
+clean :
+	rm -f *.o *~ $(TARGET)
+
Index: /anuga_core/source/anuga_viewer/swwreader/swwreader.cpp
===================================================================
--- /anuga_core/source/anuga_viewer/swwreader/swwreader.cpp	(revision 3887)
+++ /anuga_core/source/anuga_viewer/swwreader/swwreader.cpp	(revision 3887)
@@ -0,0 +1,513 @@
+/*
+  SWWReader
+
+  Reader of SWW files from within OpenSceneGraph.
+
+  copyright (C) 2004-2005 Geoscience Australia
+*/
+
+#include <SWWReader.h>
+#include <cstdlib>
+#include <string>
+#include <fstream>
+#include <netcdf.h>
+#include <osg/Notify>
+
+#include <osgDB/Registry>
+#include <osgDB/ReadFile>
+#include <osgDB/FileNameUtils>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <gdal_priv.h>
+
+
+// compile time defaults
+#define DEFAULT_CULLANGLE 85.0
+#define DEFAULT_ALPHAMIN 0.8
+#define DEFAULT_ALPHAMAX 1.0
+#define DEFAULT_HEIGHTMIN 0.0
+#define DEFAULT_HEIGHTMAX 1.0
+#define DEFAULT_BEDSLOPEOFFSET 0.0
+#define DEFAULT_CULLONSTART false
+
+
+// only constructor, requires netcdf file
+SWWReader::SWWReader(const std::string& filename)
+{
+   // assume failure until end of constructor
+   _valid = false;
+
+   // state initialization
+   _state.bedslopetexturefilename = NULL;
+
+   // netcdf filename
+   _state.swwfilename = new std::string(filename);
+
+   // netcdf open
+   _status.push_back( nc_open(filename.c_str(), NC_NOWRITE, &_ncid) );
+   if (this->_statusHasError()) return;
+
+   // dimension ids
+   _status.push_back( nc_inq_dimid(_ncid, "number_of_volumes", &_nvolumesid) );
+   _status.push_back( nc_inq_dimid(_ncid, "number_of_vertices", &_nverticesid) );
+   _status.push_back( nc_inq_dimid(_ncid, "number_of_points", &_npointsid) );
+   _status.push_back( nc_inq_dimid(_ncid, "number_of_timesteps", &_ntimestepsid) );
+   if (this->_statusHasError()) return;
+
+   // dimension values
+   _status.push_back( nc_inq_dimlen(_ncid, _nvolumesid, &_nvolumes) );
+   _status.push_back( nc_inq_dimlen(_ncid, _nverticesid, &_nvertices) );
+   _status.push_back( nc_inq_dimlen(_ncid, _npointsid, &_npoints) );
+   _status.push_back( nc_inq_dimlen(_ncid, _ntimestepsid, &_ntimesteps) );
+   if (this->_statusHasError()) return;
+
+   // variable ids
+   _status.push_back( nc_inq_varid (_ncid, "x", &_xid) );
+   _status.push_back( nc_inq_varid (_ncid, "y", &_yid) );
+   _status.push_back( nc_inq_varid (_ncid, "z", &_zid) );
+   _status.push_back( nc_inq_varid (_ncid, "volumes", &_volumesid) );
+   _status.push_back( nc_inq_varid (_ncid, "time", &_timeid) );
+   _status.push_back( nc_inq_varid (_ncid, "stage", &_stageid) );
+   if (this->_statusHasError()) return;
+
+   // allocation of variable arrays, destructor responsible for cleanup
+   _px = new float[_npoints];
+   _py = new float[_npoints];
+   _pz = new float[_npoints];
+   _ptime = new float[_ntimesteps];
+   _pvolumes = new unsigned int[_nvertices * _nvolumes];
+   _pstage = new float[_npoints];
+
+   // loading variables from netcdf file
+   _status.push_back( nc_get_var_float (_ncid, _xid, _px) );  // x vertices
+   _status.push_back( nc_get_var_float (_ncid, _yid, _py) );  // y vertices
+   _status.push_back( nc_get_var_float (_ncid, _zid, _pz) );  // bedslope heights
+   _status.push_back( nc_get_var_float (_ncid, _timeid, _ptime) );  // time array
+   _status.push_back( nc_get_var_int (_ncid, _volumesid, (int *) _pvolumes) );  // triangle indices
+   if (this->_statusHasError()) return;
+
+
+   osg::notify(osg::INFO) << "[SWWReader] number of volumes: " << _nvolumes <<  std::endl;
+   osg::notify(osg::INFO) << "[SWWReader] number of vertices: " << _nvertices <<  std::endl;
+   osg::notify(osg::INFO) << "[SWWReader] number of points: " << _npoints <<  std::endl;
+   osg::notify(osg::INFO) << "[SWWReader] number of timesteps: " << _ntimesteps <<  std::endl;
+
+
+   // sww file can optionally contain bedslope texture image filename
+   size_t attlen; // length of text attribute (if it exists)
+   if( nc_inq_attlen(_ncid, NC_GLOBAL, "texture", &attlen) != NC_ENOTATT )
+   {
+      char* texfilename;
+      if( (texfilename = (char*) malloc(attlen+1)) != NULL){
+         int status;
+         status = nc_get_att_text(_ncid, NC_GLOBAL, "texture", texfilename);
+         if( status == NC_NOERR )
+         {
+            texfilename[attlen] = '\0';  // ensure string is terminated, not a requirement for netcdf attributes
+            osg::notify(osg::INFO) << "[SWWReader] embedded image filename: " << texfilename <<  std::endl;
+
+            // if sww isn't in current directory, need to prepend sww path to the bedslope texture
+            if( osgDB::getFilePath(filename) == "" )
+               setBedslopeTexture( std::string(texfilename) );
+            else
+               setBedslopeTexture( osgDB::getFilePath(filename) + std::string("/") + std::string(texfilename) );
+         }
+         free( texfilename );
+      }
+   }
+
+   // sww file can optionally contain georeference offset
+   int status1 = nc_get_att_float(_ncid, NC_GLOBAL, "xllcorner", &_xllcorner);
+   int status2 = nc_get_att_float(_ncid, NC_GLOBAL, "yllcorner", &_yllcorner);
+   if( status1 == NC_NOERR && status2 == NC_NOERR)
+   {
+      osg::notify(osg::INFO) << "[SWWReader] xllcorner: " << _xllcorner <<  std::endl;
+      osg::notify(osg::INFO) << "[SWWReader] yllcorner: " << _yllcorner <<  std::endl;
+   }
+   else
+   {
+      _xllcorner = 0.0;  // default value
+      _yllcorner = 0.0;
+   }
+
+
+   // alpha-scaling defaults, can be overridden after construction by command line parameters
+   _state.alphamin = DEFAULT_ALPHAMIN;
+   _state.alphamax = DEFAULT_ALPHAMAX;
+   _state.heightmin = DEFAULT_HEIGHTMIN;
+   _state.heightmax = DEFAULT_HEIGHTMAX;
+
+   // steepness culling default, can be overridden after construction by command line parameter
+   _state.cullangle = DEFAULT_CULLANGLE;
+   _state.culling = DEFAULT_CULLONSTART;
+
+   // loop index
+   size_t iv;
+
+   // vertex indices
+   unsigned int v1index, v2index, v3index;
+
+   // bedslope range and resultant scale factors (note, these don't take into
+   // account any xllcorner, yllcorner offsets - used during texture coord assignment)
+   float xmin, xmax, xrange;
+   float ymin, ymax, yrange;
+   float zmin, zmax, zrange;
+   float aspect_ratio;
+   xmin = _px[0]; 
+   xmax = _px[0];
+   ymin = _py[0]; 
+   ymax = _py[0];
+   zmin = _pz[0]; 
+   zmax = _pz[0];
+   for( iv=1; iv < _npoints; iv++ )
+   {
+      if( _px[iv] < xmin ) xmin = _px[iv];
+      if( _px[iv] > xmax ) xmax = _px[iv];
+      if( _py[iv] < ymin ) ymin = _py[iv];
+      if( _py[iv] > ymax ) ymax = _py[iv];
+      if( _pz[iv] < zmin ) zmin = _pz[iv];
+      if( _pz[iv] > zmax ) zmax = _pz[iv];
+   }
+
+   xrange = xmax - xmin;
+   yrange = ymax - ymin;
+   zrange = zmax - zmin;
+   aspect_ratio = xrange/yrange;
+   _xscale = 1.0 / xrange;
+   _yscale = 1.0 / yrange;
+    
+   // handle case of a flat bed that doesn't necessarily pass through z=0
+   _zscale = (zrange == 0.0) ? 1.0 : 1.0/zrange;
+
+   _xoffset = xmin;
+   _yoffset = ymin;
+   _zoffset = zmin;
+   _xcenter = 0.5;
+   _ycenter = 0.5;
+   _zcenter = 0.0;
+
+   if( aspect_ratio > 1.0 )
+   {
+      _scale = 1.0/xrange;
+      _ycenter /= aspect_ratio;
+   }
+   else
+   {
+      _scale = 1.0/yrange;
+      _xcenter *= aspect_ratio;
+   }
+
+   osg::notify(osg::INFO) << "[SWWReader] xmin: " << xmin <<  std::endl;
+   osg::notify(osg::INFO) << "[SWWReader] xmax: " << xmax <<  std::endl;
+   osg::notify(osg::INFO) << "[SWWReader] xrange: " << xrange <<  std::endl;
+   osg::notify(osg::INFO) << "[SWWReader] xscale: " << _xscale <<  std::endl;
+   osg::notify(osg::INFO) << "[SWWReader] xcenter: " << _xcenter <<  std::endl;
+   osg::notify(osg::INFO) <<  std::endl;
+
+   osg::notify(osg::INFO) << "[SWWReader] ymin: " << ymin <<  std::endl;
+   osg::notify(osg::INFO) << "[SWWReader] ymax: " << ymax <<  std::endl;
+   osg::notify(osg::INFO) << "[SWWReader] yrange: " << yrange <<  std::endl;
+   osg::notify(osg::INFO) << "[SWWReader] yscale: " << _yscale <<  std::endl;
+   osg::notify(osg::INFO) << "[SWWReader] ycenter: " << _ycenter <<  std::endl;
+   osg::notify(osg::INFO) <<  std::endl;
+
+   osg::notify(osg::INFO) << "[SWWReader] zmin: " << zmin <<  std::endl;
+   osg::notify(osg::INFO) << "[SWWReader] zmax: " << zmax <<  std::endl;
+   osg::notify(osg::INFO) << "[SWWReader] zrange: " << zrange <<  std::endl;
+   osg::notify(osg::INFO) << "[SWWReader] zscale: " << _zscale <<  std::endl;
+   osg::notify(osg::INFO) << "[SWWReader] zcenter: " << _zcenter <<  std::endl;
+   osg::notify(osg::INFO) <<  std::endl;
+
+   // compute triangle connectivity, a list (indexed by vertex number) 
+   // of lists (indices of triangles sharing this vertex)
+   _connectivity = std::vector<triangle_list>(_npoints);
+   for (iv=0; iv < _nvolumes; iv++)
+   {
+      v1index = _pvolumes[3*iv+0];
+      v2index = _pvolumes[3*iv+1];
+      v3index = _pvolumes[3*iv+2];
+      _connectivity.at(v1index).push_back(iv);
+      _connectivity.at(v2index).push_back(iv);
+      _connectivity.at(v3index).push_back(iv);
+   }
+
+   // bedslope vertex array, shifting and scaling vertices to unit cube
+   // centred about the origin
+   _bedslopevertices = new osg::Vec3Array;
+   for (iv=0; iv < _npoints; iv++)
+      _bedslopevertices->push_back( osg::Vec3( (_px[iv]-_xoffset)*_scale - _xcenter, 
+                                               (_py[iv]-_yoffset)*_scale - _ycenter, 
+                                               (_pz[iv]-_zoffset)*_scale - _zcenter - DEFAULT_BEDSLOPEOFFSET) );
+
+   // bedslope index array, pvolumes array indexes into x, y and z
+   _bedslopeindices = new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLES, _nvolumes*_nvertices);
+   for (iv=0; iv < _nvolumes*_nvertices; iv++)
+      (*_bedslopeindices)[iv] = _pvolumes[iv];
+
+
+   // calculate bedslope primitive normal and centroid arrays
+   _bedslopenormals = new osg::Vec3Array;
+   _bedslopecentroids = new osg::Vec3Array;
+   osg::Vec3 v1, v2, v3, side1, side2, nrm;
+   for (iv=0; iv < _nvolumes; iv++)
+   {
+      v1index = _pvolumes[3*iv+0];
+      v2index = _pvolumes[3*iv+1];
+      v3index = _pvolumes[3*iv+2];
+
+      v1 = _bedslopevertices->at(v1index);
+      v2 = _bedslopevertices->at(v2index);
+      v3 = _bedslopevertices->at(v3index);
+
+      side1 = v2 - v1;
+      side2 = v3 - v2;
+      nrm = side1^side2;
+      nrm.normalize();
+
+      _bedslopenormals->push_back( nrm );
+      _bedslopecentroids->push_back( (v1+v2+v3)/3.0 );
+   }
+
+   // success!
+   _valid = true;
+
+}
+
+
+
+SWWReader::~SWWReader()
+{
+   _status.push_back( nc_close(_ncid) );
+   delete[]( _px, _py, _pz, _ptime, _pvolumes, _pstage );
+}
+
+
+
+void SWWReader::setBedslopeTexture( std::string filename )
+{
+   osg::notify(osg::INFO) << "[SWWReader::setBedslopetexture] filename: " << filename <<  std::endl;
+   _state.bedslopetexturefilename = new std::string(filename);
+   _bedslopegeodata.hasData = false;
+
+   // GDAL Geospatial Image Library
+   GDALDataset *pGDAL;
+   GDALAllRegister();
+   pGDAL = (GDALDataset *) GDALOpen( filename.c_str(), GA_ReadOnly );
+   if( pGDAL == NULL )
+      osg::notify(osg::INFO) << "[SWWReader::setBedslopetexture] GDAL unable to open file: " << filename <<  std::endl;
+   else
+   {
+      osg::notify(osg::INFO) << "[SWWReader::setBedslopetexture] GDAL Driver: "
+			     << pGDAL->GetDriver()->GetDescription() << "/" 
+			     << pGDAL->GetDriver()->GetMetadataItem( GDAL_DMD_LONGNAME ) 
+			     << std::endl;
+
+      _bedslopegeodata.xresolution = pGDAL->GetRasterXSize();
+      _bedslopegeodata.yresolution = pGDAL->GetRasterYSize();
+      osg::notify(osg::INFO) << "[SWWReader::setBedslopetexture] xresolution: " << _bedslopegeodata.xresolution <<  std::endl;
+      osg::notify(osg::INFO) << "[SWWReader::setBedslopetexture] yresolution: " << _bedslopegeodata.yresolution <<  std::endl;
+
+      // check if image contains geodata
+      double geodata[6];
+      if( pGDAL->GetGeoTransform( geodata ) == CE_None )
+      {
+         _bedslopegeodata.xorigin = geodata[0];
+         _bedslopegeodata.yorigin = geodata[3];
+         _bedslopegeodata.rotation = geodata[2];
+         _bedslopegeodata.xpixel = geodata[1];
+         _bedslopegeodata.ypixel = geodata[5];
+         _bedslopegeodata.hasData = true;
+
+         osg::notify(osg::INFO) << "[SWWReader::setBedslopetexture] xorigin: " << _bedslopegeodata.xorigin <<  std::endl;
+         osg::notify(osg::INFO) << "[SWWReader::setBedslopetexture] yorigin: " << _bedslopegeodata.yorigin <<  std::endl;
+         osg::notify(osg::INFO) << "[SWWReader::setBedslopetexture] xpixel: " << _bedslopegeodata.xpixel <<  std::endl;
+         osg::notify(osg::INFO) << "[SWWReader::setBedslopetexture] ypixel: " << _bedslopegeodata.ypixel <<  std::endl;
+         osg::notify(osg::INFO) << "[SWWReader::setBedslopetexture] rotation: " << _bedslopegeodata.rotation <<  std::endl;
+      }
+   }
+}
+
+
+
+osg::Image* SWWReader::getBedslopeTexture()
+{
+   return osgDB::readImageFile( _state.bedslopetexturefilename->c_str() );
+}
+
+
+
+osg::ref_ptr<osg::Vec2Array> SWWReader::getBedslopeTextureCoords()
+{
+   _bedslopetexcoords = new osg::Vec2Array;
+   if( _bedslopegeodata.hasData )  // georeferenced bedslope texture
+   {
+      float xorigin = _bedslopegeodata.xorigin;
+      float yorigin = _bedslopegeodata.yorigin;
+      float xrange = _bedslopegeodata.xpixel * _bedslopegeodata.xresolution;
+      float yrange = _bedslopegeodata.ypixel * _bedslopegeodata.yresolution;
+      for( unsigned int iv=0; iv < _npoints; iv++ )
+         _bedslopetexcoords->push_back( osg::Vec2( (_px[iv] + _xllcorner - xorigin)/xrange, 
+                                                   1.0 - (_py[iv] + _yllcorner - yorigin)/yrange ) );
+   }
+   else
+   {
+      // decaled using (x,y) locations scaled by extents into range [0,1]
+      for( unsigned int iv=0; iv < _npoints; iv++ )
+         _bedslopetexcoords->push_back( osg::Vec2( (_px[iv]-_xoffset)*_xscale, (_py[iv]-_yoffset)*_yscale ) );
+   }
+
+   return _bedslopetexcoords;
+}
+
+
+
+osg::ref_ptr<osg::Vec3Array> SWWReader::getStageVertexArray(unsigned int index)
+{
+   size_t start[2], count[2], iv;
+   const ptrdiff_t stride[2] = {1,1};
+   start[0] = index;
+   start[1] = 0;
+   count[0] = 1;
+   count[1] = _npoints;
+
+   // empty array for storing list of steep triangles
+   osg::ref_ptr<osg::IntArray> steeptri = new osg::IntArray;
+
+   // stage heights from netcdf file (x and y are same as bedslope)
+   int status = nc_get_vars_float (_ncid, _stageid, start, count, stride, _pstage);
+
+   // load stage vertex array, scaling and shifting vertices to lie in the unit cube
+   _stagevertices = new osg::Vec3Array;
+   for (iv=0; iv < _npoints; iv++)
+      _stagevertices->push_back( osg::Vec3( (_px[iv]-_xoffset)*_scale - _xcenter, 
+                                            (_py[iv]-_yoffset)*_scale - _ycenter, 
+                                            (_pstage[iv]-_zoffset)*_scale - _zcenter) );
+
+   // stage index, per primitive normal and centroid arrays
+   _stageprimitivenormals = new osg::Vec3Array;
+   _stagecentroids = new osg::Vec3Array;
+   osg::Vec3 v1b, v2b, v3b;
+   osg::Vec3 v1s, v2s, v3s;
+   osg::Vec3 side1, side2, nrm;
+   unsigned int v1index, v2index, v3index;
+
+   // cullangle given in degrees, test is against dot product
+   float cullthreshold = cos(osg::DegreesToRadians(_state.cullangle));
+
+   // over all stage triangles
+   for (iv=0; iv < _nvolumes; iv++)
+   {
+      v1index = _pvolumes[3*iv+0];
+      v2index = _pvolumes[3*iv+1];
+      v3index = _pvolumes[3*iv+2];
+
+      v1s = _stagevertices->at(v1index);
+      v2s = _stagevertices->at(v2index);
+      v3s = _stagevertices->at(v3index);
+      v1b = _bedslopevertices->at(v1index);
+      v2b = _bedslopevertices->at(v2index);
+      v3b = _bedslopevertices->at(v3index);
+
+      // current triangle primitive normal
+      side1 = v2s - v1s;
+      side2 = v3s - v2s;
+      nrm = side1^side2;
+      nrm.normalize();
+
+      // store centroid and primitive normal
+      _stagecentroids->push_back( (v1s+v2s+v3s)/3.0 );
+      _stageprimitivenormals->push_back( nrm );
+
+      // identify steep triangles, store index
+      if( fabs(nrm * osg::Vec3f(0,0,1)) < cullthreshold )
+         steeptri->push_back( iv );
+   }
+
+   // stage height above bedslope mapped as alpha value
+   //      alpha = min( a(h-hmin) + alphamin, alphamax),  h >= hmin
+   //      alpha = 0,                                     h < hmin
+   // where a = (alphamax-alphamin)/(hmax-hmin)
+   float alpha, height, alphascale;
+   alphascale = (_state.alphamax - _state.alphamin) / (_state.heightmax - _state.heightmin);
+   _stagecolors = new osg::Vec4Array;
+   for (iv=0; iv < _npoints; iv++)
+   {
+      // water height above corresponding bedslope
+      height = _stagevertices->at(iv).z() - _bedslopevertices->at(iv).z();
+	
+      if (height < _state.heightmin)
+         alpha = 0.0;
+      else 
+      {
+         alpha = alphascale * (height - _state.heightmin) + _state.alphamin;
+         if( alpha > _state.alphamax ) 
+            alpha = _state.alphamax;
+      }
+
+      _stagecolors->push_back( osg::Vec4( 1.0, 1.0, 1.0, alpha ) );
+   }
+
+   // steep triangle vertices should have alpha=0, overwrite such vertex colours
+   if( _state.culling )
+   {
+      //std::cout << "culling on step: " << index << "  steeptris: " << steeptri->size() << std::endl;
+      for (iv=0; iv < steeptri->size() ; iv++)
+      {
+         int tri = steeptri->at(iv);
+         v1index = _pvolumes[3*tri+0];
+         v2index = _pvolumes[3*tri+1];
+         v3index = _pvolumes[3*tri+2];
+
+         _stagecolors->at(v1index) = osg::Vec4( 1, 1, 1, 0 );
+         _stagecolors->at(v2index) = osg::Vec4( 1, 1, 1, 0 );
+         _stagecolors->at(v3index) = osg::Vec4( 1, 1, 1, 0 );
+      }
+   }
+
+   // per-vertex normals calculated as average of primitive normals
+   // from contributing triangles
+   _stagevertexnormals = new osg::Vec3Array;
+   int num_shared_triangles, triangle_index;
+   for (iv=0; iv < _npoints; iv++)
+   {
+      nrm.set(0,0,0);
+      num_shared_triangles = _connectivity.at(iv).size();
+      for (int i=0; i<num_shared_triangles; i++ )
+      {
+         triangle_index = _connectivity.at(iv).at(i);
+         nrm += _stageprimitivenormals->at(triangle_index);
+      }
+
+      nrm = nrm / num_shared_triangles;  // average
+      nrm.normalize();
+      _stagevertexnormals->push_back(nrm);
+   }
+
+   return _stagevertices;
+}
+
+
+
+
+bool SWWReader::_statusHasError()
+{
+   bool haserror = false;  // assume success, trap failure
+
+   std::vector<int>::iterator iter;
+   for (iter=_status.begin(); iter != _status.end(); iter++)
+   {
+      if (*iter != NC_NOERR)
+      {
+         osg::notify(osg::WARN) << nc_strerror(*iter) <<  std::endl;
+         haserror = true;
+         nc_close(_ncid);
+         break;
+      }
+   }
+
+   // on return start gathering result values afresh
+   _status.clear();
+
+   return haserror;
+}
Index: /anuga_core/source/anuga_viewer/swwreader/swwreader.vcproj
===================================================================
--- /anuga_core/source/anuga_viewer/swwreader/swwreader.vcproj	(revision 3887)
+++ /anuga_core/source/anuga_viewer/swwreader/swwreader.vcproj	(revision 3887)
@@ -0,0 +1,143 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="7.10"
+	Name="SWWReader"
+	ProjectGUID="{05573643-DA30-4B8D-883A-F86D5D7B1FED}"
+	Keyword="Win32Proj">
+	<Platforms>
+		<Platform
+			Name="Win32"/>
+	</Platforms>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="Debug"
+			IntermediateDirectory="Debug"
+			ConfigurationType="2"
+			CharacterSet="2">
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="./;&quot;$(OSG_ROOT)/Producer/include&quot;;&quot;$(OSG_ROOT)/OpenThreads/include&quot;;&quot;$(OSG_ROOT)/OpenSceneGraph/include&quot;;&quot;$(NETCDF_DIR)/include&quot;;../include;&quot;$(OSG_ROOT)/3rdParty/include&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+				MinimalRebuild="TRUE"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="TRUE"
+				UsePrecompiledHeader="2"
+				PrecompiledHeaderFile="$(IntDir)/$(TargetName).pch"
+				ProgramDataBaseFileName="$(IntDir)/vc70.pdb"
+				WarningLevel="3"
+				Detect64BitPortabilityProblems="TRUE"
+				DebugInformationFormat="4"/>
+			<Tool
+				Name="VCCustomBuildTool"/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="osgd.lib osgdbd.lib netcdf.lib gdal_i.lib"
+				OutputFile="../bin/swwreaderd.dll"
+				LinkIncremental="2"
+				SuppressStartupBanner="TRUE"
+				AdditionalLibraryDirectories="&quot;$(OSG_ROOT)/Producer/lib&quot;;&quot;$(OSG_ROOT)/OpenThreads/lib/win32&quot;;&quot;$(OSG_ROOT)/OpenSceneGraph/lib&quot;;&quot;$(NETCDF_DIR)/lib&quot;;../lib;&quot;$(OSG_ROOT)/3rdParty/lib&quot;"
+				GenerateDebugInformation="TRUE"
+				ProgramDatabaseFile="$(OutDir)/SWWReader.pdb"
+				SubSystem="1"
+				ImportLibrary="../lib/$(TargetName).lib"
+				TargetMachine="1"/>
+			<Tool
+				Name="VCMIDLTool"/>
+			<Tool
+				Name="VCPostBuildEventTool"/>
+			<Tool
+				Name="VCPreBuildEventTool"/>
+			<Tool
+				Name="VCPreLinkEventTool"/>
+			<Tool
+				Name="VCResourceCompilerTool"/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"/>
+			<Tool
+				Name="VCWebDeploymentTool"/>
+			<Tool
+				Name="VCManagedWrapperGeneratorTool"/>
+			<Tool
+				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="Release"
+			IntermediateDirectory="Release"
+			ConfigurationType="2"
+			CharacterSet="2">
+			<Tool
+				Name="VCCLCompilerTool"
+				AdditionalIncludeDirectories="&quot;$(OSG_ROOT)/Producer/include&quot;;&quot;$(OSG_ROOT)/OpenThreads/include&quot;;&quot;$(OSG_ROOT)/OpenSceneGraph/include&quot;;&quot;$(NETCDF_DIR)/include&quot;;../include;./;&quot;$(OSG_ROOT)/3rdParty/include&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+				RuntimeLibrary="2"
+				RuntimeTypeInfo="TRUE"
+				UsePrecompiledHeader="2"
+				WarningLevel="3"
+				Detect64BitPortabilityProblems="TRUE"
+				DebugInformationFormat="3"/>
+			<Tool
+				Name="VCCustomBuildTool"/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="osg.lib osgdb.lib netcdf.lib gdal_i.lib"
+				OutputFile="../bin/swwreader.dll"
+				LinkIncremental="1"
+				AdditionalLibraryDirectories="../lib;&quot;$(OSG_ROOT)/Producer/lib&quot;;&quot;$(OSG_ROOT)/OpenThreads/lib/win32&quot;;&quot;$(OSG_ROOT)/OpenSceneGraph/lib&quot;;&quot;$(NETCDF_DIR)/lib&quot;;&quot;$(OSG_ROOT)/3rdParty/lib&quot;"
+				GenerateDebugInformation="TRUE"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				ImportLibrary="../lib/$(TargetName).lib"
+				TargetMachine="1"/>
+			<Tool
+				Name="VCMIDLTool"/>
+			<Tool
+				Name="VCPostBuildEventTool"/>
+			<Tool
+				Name="VCPreBuildEventTool"/>
+			<Tool
+				Name="VCPreLinkEventTool"/>
+			<Tool
+				Name="VCResourceCompilerTool"/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"/>
+			<Tool
+				Name="VCWebDeploymentTool"/>
+			<Tool
+				Name="VCManagedWrapperGeneratorTool"/>
+			<Tool
+				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="Source Files"
+			Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
+			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}">
+			<File
+				RelativePath=".\SWWReader.cpp">
+			</File>
+		</Filter>
+		<Filter
+			Name="Header Files"
+			Filter="h;hpp;hxx;hm;inl;inc;xsd"
+			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}">
+			<File
+				RelativePath="..\include\SWWReader.h">
+			</File>
+		</Filter>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
Index: /anuga_core/source/anuga_viewer/swwreadertest/swwreadertest.cpp
===================================================================
--- /anuga_core/source/anuga_viewer/swwreadertest/swwreadertest.cpp	(revision 3887)
+++ /anuga_core/source/anuga_viewer/swwreadertest/swwreadertest.cpp	(revision 3887)
@@ -0,0 +1,166 @@
+
+#include <cppunit/CompilerOutputter.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <cppunit/ui/text/TestRunner.h>
+#include "SWWReaderTest.h"
+
+
+// Registers the fixture
+CPPUNIT_TEST_SUITE_REGISTRATION( SWWReaderTest );
+
+
+
+void SWWReaderTest::setUp()
+{
+    _sww = new SWWReader("../SWWReaderTest/test.sww");
+}
+
+
+void SWWReaderTest::testValid()
+{
+    CPPUNIT_ASSERT( _sww->isValid() );
+}
+
+
+
+void SWWReaderTest::testNumberOfVertices()
+{
+    const size_t nvertices = 20;
+    CPPUNIT_ASSERT_EQUAL( _sww->getNumberOfVertices(), nvertices );
+}
+
+
+
+void SWWReaderTest::testTime()
+{
+    const unsigned int ntimesteps = 3;
+    CPPUNIT_ASSERT_EQUAL( _sww->getNumberOfTimesteps(), ntimesteps );
+
+    // hard-coded values from sww file
+    const float time_array[3] = {0.0, 0.5, 1.0};
+    for (unsigned int i=0; i<ntimesteps; i++)
+        CPPUNIT_ASSERT_EQUAL( _sww->getTime(i), time_array[i] );
+}
+
+
+
+void SWWReaderTest::testBedslopeVertexArray()
+{
+    osg::ref_ptr<osg::Vec3Array> actual = _sww->getBedslopeVertexArray();
+
+    // expected number of bedslope vertices
+    const size_t nvertices = 20;
+    CPPUNIT_ASSERT_EQUAL( actual->size(), nvertices );
+
+    // hard-coded values extracted from sww file
+    using osg::Vec3;
+    using osg::Vec3Array;
+    Vec3Array *expected = new Vec3Array(20);
+    (*expected)[ 0] = Vec3( 0.000000, 0.000000, 0.000000 );
+    (*expected)[ 1] = Vec3( 0.000000, 0.666667, 0.222222 );
+    (*expected)[ 2] = Vec3( 0.000000, 1.333333, 0.444444 );
+    (*expected)[ 3] = Vec3( 0.000000, 2.000000, 0.666667 );
+    (*expected)[ 4] = Vec3( 0.500000, 0.000000, -0.166667 );
+    (*expected)[ 5] = Vec3( 0.500000, 0.666667, 0.055556 );
+    (*expected)[ 6] = Vec3( 0.500000, 1.333333, 0.277778 );
+    (*expected)[ 7] = Vec3( 0.500000, 2.000000, 0.500000 );
+    (*expected)[ 8] = Vec3( 1.000000, 0.000000, -0.333333 );
+    (*expected)[ 9] = Vec3( 1.000000, 0.666667, -0.111111 );
+    (*expected)[10] = Vec3( 1.000000, 1.333333, 0.111111 );
+    (*expected)[11] = Vec3( 1.000000, 2.000000, 0.333333 );
+    (*expected)[12] = Vec3( 1.500000, 0.000000, -0.500000 );
+    (*expected)[13] = Vec3( 1.500000, 0.666667, -0.277778 );
+    (*expected)[14] = Vec3( 1.500000, 1.333333, -0.055556 );
+    (*expected)[15] = Vec3( 1.500000, 2.000000, 0.166667 );
+    (*expected)[16] = Vec3( 2.000000, 0.000000, -0.666667 );
+    (*expected)[17] = Vec3( 2.000000, 0.666667, -0.444444 );
+    (*expected)[18] = Vec3( 2.000000, 1.333333, -0.222222 );
+    (*expected)[19] = Vec3( 2.000000, 2.000000, 0.000000 );
+
+    for (size_t i=0; i<nvertices; i++)
+        CPPUNIT_ASSERT_VEC3_EQUAL( actual->at(i), expected->at(i) );
+}
+
+
+
+void SWWReaderTest::testBedslopeIndexArray()
+{
+    osg::ref_ptr<osg::UIntArray> actual = _sww->getBedslopeIndexArray();
+
+    // expected number of bedslope indices
+    const size_t nindices = 24*3;
+    CPPUNIT_ASSERT_EQUAL( actual->size(), nindices );
+
+    // hard-coded values extracted from sww file
+    const unsigned int expected[72] = { 4, 5, 0,  1, 0, 5,  5, 6, 1, 
+        2, 1, 6,  6, 7, 2,  3, 2, 7,  8, 9, 4,  5, 4, 9,  9, 10, 5, 
+        6, 5, 10,  10, 11, 6,  7, 6, 11,  12, 13, 8,  9, 8, 13,
+        13, 14, 9,  10, 9, 14,  14, 15, 10,  11, 10, 15,  16, 17, 12, 
+        13, 12, 17,  17, 18, 13,  14, 13, 18,  18, 19, 14,  15, 14, 19 };
+
+    for (size_t i=0; i<nindices; i++)
+        CPPUNIT_ASSERT_EQUAL( actual->at(i), expected[i] );
+}
+
+
+
+void SWWReaderTest::testConnectivity()
+{
+    // shared vertices for triangle 17
+    triangle_list actual = _sww->getConnectivity(17);
+
+    const size_t nshared = 3;
+    CPPUNIT_ASSERT_EQUAL( actual.size(), nshared );
+    const unsigned int expected[nshared] = { 18, 19, 20 };
+    for (size_t i=0; i<nshared; i++)
+        CPPUNIT_ASSERT_EQUAL( actual.at(i), expected[i] );
+}
+
+
+
+void SWWReaderTest::testBedslopeNormalArray()
+{
+    osg::ref_ptr<osg::Vec3Array> actual = _sww->getBedslopeNormalArray();
+
+    // expected number of bedslope normals
+    const size_t nvertices = 24;
+    CPPUNIT_ASSERT_EQUAL( actual->size(), nvertices );
+
+    // hard-coded values (bedslope is flat plane)
+    using osg::Vec3;
+    using osg::Vec3Array;
+    Vec3Array *expected = new Vec3Array; 
+    expected->assign(24, Vec3( 0.301511, -0.301511, 0.904534 ) );
+
+    for (size_t i=0; i<nvertices; i++)
+        CPPUNIT_ASSERT_VEC3_EQUAL( actual->at(i), expected->at(i) );
+}
+
+
+
+
+void SWWReaderTest::tearDown()
+{
+}
+
+
+
+
+int main(int argc, char* argv[])
+{
+  // Get the top level suite from the registry
+  CppUnit::Test *suite = CppUnit::TestFactoryRegistry::getRegistry().makeTest();
+
+  // Add test
+  CppUnit::TextUi::TestRunner runner;
+  runner.addTest( suite );
+
+  // Change default outputter to a compiler error format outputter
+  runner.setOutputter( new CppUnit::CompilerOutputter( &runner.result(), std::cerr ) );
+
+  // Run tests
+  bool wasSucessful = runner.run();
+
+  // Error code 1 if any tests failed
+  return wasSucessful ? 0 : 1;
+}
Index: /anuga_core/source/anuga_viewer/swwreadertest/swwreadertest.h
===================================================================
--- /anuga_core/source/anuga_viewer/swwreadertest/swwreadertest.h	(revision 3887)
+++ /anuga_core/source/anuga_viewer/swwreadertest/swwreadertest.h	(revision 3887)
@@ -0,0 +1,66 @@
+
+#ifndef SWWREADERTEST_H
+#define SWWREADERTEST_H
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/SourceLine.h>
+#include <cppunit/TestAssert.h>
+#include <SWWReader.h>
+#include <osg/Geometry>
+#include <stdio.h>
+
+// allowable distance between two Vectors to be considered equal
+#define TOLERANCE 0.001
+
+
+// custom assertion testing equivalence of two OSG Vec3
+void checkVec3Equal( osg::Vec3 expected, osg::Vec3 actual, CppUnit::SourceLine sourceLine )
+{  
+    float distance = (expected-actual).length();
+    if (distance < TOLERANCE)
+        return;
+
+    // test failed
+    char s_expected[20], s_actual[20];
+    sprintf(s_expected, "%10.3f", expected);
+    sprintf(s_actual, "%10.3f", actual);
+    ::CppUnit::Asserter::failNotEqual( s_expected, s_actual, sourceLine );
+}
+
+// macro shortcut to above assertion
+#define CPPUNIT_ASSERT_VEC3_EQUAL( expected, actual ) \
+    checkVec3Equal( expected, actual, CPPUNIT_SOURCELINE() )
+
+
+
+
+class SWWReaderTest : public CppUnit::TestFixture
+{
+  CPPUNIT_TEST_SUITE( SWWReaderTest );
+  CPPUNIT_TEST( testValid );
+  CPPUNIT_TEST( testNumberOfVertices );
+  CPPUNIT_TEST( testTime );
+  CPPUNIT_TEST( testBedslopeVertexArray );
+  CPPUNIT_TEST( testBedslopeIndexArray );
+  CPPUNIT_TEST( testBedslopeNormalArray );
+  CPPUNIT_TEST( testConnectivity );
+  CPPUNIT_TEST_SUITE_END();
+
+public:
+  void setUp();
+  void tearDown();
+
+  void testValid();
+  void testNumberOfVertices();
+  void testTime();
+  void testBedslopeVertexArray();
+  void testBedslopeIndexArray();
+  void testBedslopeNormalArray();
+  void testConnectivity();
+
+
+private:
+    SWWReader* _sww;
+};
+
+#endif
Index: /anuga_core/source/anuga_viewer/swwreadertest/swwreadertest.vcproj
===================================================================
--- /anuga_core/source/anuga_viewer/swwreadertest/swwreadertest.vcproj	(revision 3887)
+++ /anuga_core/source/anuga_viewer/swwreadertest/swwreadertest.vcproj	(revision 3887)
@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="7.10"
+	Name="SWWReaderTest"
+	ProjectGUID="{42DADC3F-E085-4547-B7D7-B2894A0B0915}"
+	Keyword="Win32Proj">
+	<Platforms>
+		<Platform
+			Name="Win32"/>
+	</Platforms>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="Debug"
+			IntermediateDirectory="Debug"
+			ConfigurationType="1"
+			CharacterSet="2">
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="$(CPPUNIT)\include;$(OSG_ROOT)\OpenSceneGraph\include;..\include"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+				MinimalRebuild="FALSE"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="TRUE"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				Detect64BitPortabilityProblems="FALSE"
+				DebugInformationFormat="4"/>
+			<Tool
+				Name="VCCustomBuildTool"/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="odbc32.lib odbccp32.lib cppunitd.lib osgd.lib swwreaderd.lib"
+				OutputFile="../bin/swwreadertestd.exe"
+				LinkIncremental="2"
+				SuppressStartupBanner="TRUE"
+				AdditionalLibraryDirectories="$(CPPUNIT)\lib;..\lib;$(OSG_ROOT)\OpenSceneGraph\lib"
+				IgnoreAllDefaultLibraries="FALSE"
+				GenerateDebugInformation="TRUE"
+				ProgramDatabaseFile="$(OutDir)/SWWReaderTest.pdb"
+				SubSystem="1"
+				TargetMachine="1"/>
+			<Tool
+				Name="VCMIDLTool"/>
+			<Tool
+				Name="VCPostBuildEventTool"
+				Description="Unit Testing"
+				CommandLine="$(TargetPath)"
+				ExcludedFromBuild="FALSE"/>
+			<Tool
+				Name="VCPreBuildEventTool"/>
+			<Tool
+				Name="VCPreLinkEventTool"/>
+			<Tool
+				Name="VCResourceCompilerTool"/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"/>
+			<Tool
+				Name="VCWebDeploymentTool"/>
+			<Tool
+				Name="VCManagedWrapperGeneratorTool"/>
+			<Tool
+				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="Release"
+			IntermediateDirectory="Release"
+			ConfigurationType="1"
+			CharacterSet="2">
+			<Tool
+				Name="VCCLCompilerTool"
+				AdditionalIncludeDirectories="$(OSG_ROOT)/OpenSceneGraph/include;$(CPPUNIT)\include;../include"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+				RuntimeLibrary="2"
+				RuntimeTypeInfo="TRUE"
+				UsePrecompiledHeader="2"
+				WarningLevel="3"
+				Detect64BitPortabilityProblems="TRUE"
+				DebugInformationFormat="3"/>
+			<Tool
+				Name="VCCustomBuildTool"/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalDependencies="cppunit.lib osg.lib swwreader.lib"
+				OutputFile="../bin/SWWReaderTest.exe"
+				LinkIncremental="1"
+				AdditionalLibraryDirectories="$(CPPUNIT)\lib;..\lib;$(OSG_ROOT)\OpenSceneGraph\lib"
+				GenerateDebugInformation="TRUE"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="1"/>
+			<Tool
+				Name="VCMIDLTool"/>
+			<Tool
+				Name="VCPostBuildEventTool"
+				CommandLine="$(TARGETPATH)"/>
+			<Tool
+				Name="VCPreBuildEventTool"
+				ExcludedFromBuild="TRUE"/>
+			<Tool
+				Name="VCPreLinkEventTool"/>
+			<Tool
+				Name="VCResourceCompilerTool"/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"/>
+			<Tool
+				Name="VCWebDeploymentTool"/>
+			<Tool
+				Name="VCManagedWrapperGeneratorTool"/>
+			<Tool
+				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="Source Files"
+			Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
+			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}">
+			<File
+				RelativePath=".\SWWReaderTest.cpp">
+			</File>
+		</Filter>
+		<Filter
+			Name="Header Files"
+			Filter="">
+			<File
+				RelativePath=".\SWWReaderTest.h">
+			</File>
+		</Filter>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
Index: /anuga_core/source/anuga_viewer/tests/geotiff.wld
===================================================================
--- /anuga_core/source/anuga_viewer/tests/geotiff.wld	(revision 3887)
+++ /anuga_core/source/anuga_viewer/tests/geotiff.wld	(revision 3887)
@@ -0,0 +1,6 @@
+0.1
+0.0
+0.0
+0.1
+0.05
+0.05
Index: /anuga_core/source/anuga_viewer/visualstudio/sww.sln
===================================================================
--- /anuga_core/source/anuga_viewer/visualstudio/sww.sln	(revision 3887)
+++ /anuga_core/source/anuga_viewer/visualstudio/sww.sln	(revision 3887)
@@ -0,0 +1,37 @@
+Microsoft Visual Studio Solution File, Format Version 8.00
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SWWReader", "..\swwreader\swwreader.vcproj", "{05573643-DA30-4B8D-883A-F86D5D7B1FED}"
+	ProjectSection(ProjectDependencies) = postProject
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Swollen", "..\swollen\main.vcproj", "{CAC216DA-D220-440D-B72D-BAD4CB15D45F}"
+	ProjectSection(ProjectDependencies) = postProject
+		{05573643-DA30-4B8D-883A-F86D5D7B1FED} = {05573643-DA30-4B8D-883A-F86D5D7B1FED}
+	EndProjectSection
+EndProject
+Global
+	GlobalSection(SolutionConfiguration) = preSolution
+		Debug = Debug
+		Release = Release
+	EndGlobalSection
+	GlobalSection(ProjectConfiguration) = postSolution
+		{05573643-DA30-4B8D-883A-F86D5D7B1FED}.Debug.ActiveCfg = Debug|Win32
+		{05573643-DA30-4B8D-883A-F86D5D7B1FED}.Debug.Build.0 = Debug|Win32
+		{05573643-DA30-4B8D-883A-F86D5D7B1FED}.Release.ActiveCfg = Release|Win32
+		{05573643-DA30-4B8D-883A-F86D5D7B1FED}.Release.Build.0 = Release|Win32
+		{CAC216DA-D220-440D-B72D-BAD4CB15D45F}.Debug.ActiveCfg = Debug|Win32
+		{CAC216DA-D220-440D-B72D-BAD4CB15D45F}.Debug.Build.0 = Debug|Win32
+		{CAC216DA-D220-440D-B72D-BAD4CB15D45F}.Release.ActiveCfg = Release|Win32
+		{CAC216DA-D220-440D-B72D-BAD4CB15D45F}.Release.Build.0 = Release|Win32
+	EndGlobalSection
+	GlobalSection(SolutionItems) = postSolution
+		..\doc\contract2_hours.txt = ..\doc\contract2_hours.txt
+		..\doc\contract2_priorities.txt = ..\doc\contract2_priorities.txt
+		INSTALL = INSTALL
+		..\doc\notes.txt = ..\doc\notes.txt
+		README = README
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+	EndGlobalSection
+	GlobalSection(ExtensibilityAddIns) = postSolution
+	EndGlobalSection
+EndGlobal
