Ogre::TerrainQuadTreeNode Class Reference
[Terrain]

A node in a quad tree used to store a patch of terrain. More...

#include <OgreTerrainQuadTreeNode.h>

Inheritance diagram for Ogre::TerrainQuadTreeNode:
Inheritance graph
[legend]

List of all members.

Classes

struct  LodLevel
class  Movable
 MovableObject implementation to provide the hook to the scene. More...
class  Rend
 Hook to the render queue. More...
struct  VertexDataRecord

Public Types

typedef vector< LodLevel * >::type LodLevelList

Public Member Functions

 TerrainQuadTreeNode (Terrain *terrain, TerrainQuadTreeNode *parent, uint16 xoff, uint16 yoff, uint16 size, uint16 lod, uint16 depth, uint16 quadrant)
 Constructor.
virtual ~TerrainQuadTreeNode ()
uint16 getXOffset () const
 Get the horizontal offset into the main terrain data of this node.
uint16 getYOffset () const
 Get the vertical offset into the main terrain data of this node.
bool isLeaf () const
 Is this a leaf node (no children).
uint16 getBaseLod () const
 Get the base LOD level this node starts at (the highest LOD it handles).
uint16 getLodCount () const
 Get the number of LOD levels this node can represent itself (only > 1 for leaf nodes).
TerrainQuadTreeNodegetChild (unsigned short child) const
 Get child node.
TerrainQuadTreeNodegetParent () const
 Get parent node.
TerraingetTerrain () const
 Get ultimate parent terrain.
void prepare ()
 Prepare node and children (perform CPU tasks, may be background thread).
void prepare (StreamSerialiser &stream)
 Prepare node from a stream.
void load ()
 Load node and children (perform GPU tasks, will be render thread).
void unload ()
 Unload node and children (perform GPU tasks, will be render thread).
void unprepare ()
 Unprepare node and children (perform CPU tasks, may be background thread).
void save (StreamSerialiser &stream)
 Save node to a stream.
const LodLevelgetLodLevel (uint16 lod)
 Get the LodLevel information for a given lod.
void preDeltaCalculation (const Rect &rect)
 Notify the node (and children) that deltas are going to be calculated for a given range.
void notifyDelta (uint16 x, uint16 y, uint16 lod, Real delta)
 Notify the node (and children) of a height delta value.
void postDeltaCalculation (const Rect &rect)
 Notify the node (and children) that deltas have finished being calculated.
void finaliseDeltaValues (const Rect &rect)
 Promote the delta values calculated to the runtime ones (this must be called in the main thread).
void assignVertexData (uint16 treeDepthStart, uint16 treeDepthEnd, uint16 resolution, uint sz)
 Assign vertex data to the tree, from a depth and at a given resolution.
void useAncestorVertexData (TerrainQuadTreeNode *owner, uint16 treeDepthEnd, uint16 resolution)
 Tell a node that it should use an anscestor's vertex data.
void updateVertexData (bool positions, bool deltas, const Rect &rect, bool cpuData)
 Tell the node to update its vertex data for a given region.
void mergeIntoBounds (long x, long y, const Vector3 &pos)
 Merge a point (relative to terrain node) into the local bounds, and that of children if applicable.
void resetBounds (const Rect &rect)
 Reset the bounds of this node and all its children for the region given.
bool rectIntersectsNode (const Rect &rect)
 Returns true if the given rectangle overlaps the terrain area that this node references.
bool rectContainsNode (const Rect &rect)
 Returns true if the given rectangle completely contains the terrain area that this node references.
bool pointIntersectsNode (long x, long y)
 Returns true if the given point is in the terrain area that this node references.
const AxisAlignedBoxgetAABB () const
 Get the AABB (local coords) of this node.
Real getBoundingRadius () const
 Get the bounding radius of this node.
const Vector3getLocalCentre () const
 Get the local centre of this node, relative to parent terrain centre.
Real getMinHeight () const
 Get the minimum height of the node.
Real getMaxHeight () const
 Get the maximum height of the node.
bool calculateCurrentLod (const Camera *cam, Real cFactor)
 Calculate appropriate LOD for this node and children.
int getCurrentLod () const
 Get the current LOD index (only valid after calculateCurrentLod).
bool isRenderedAtCurrentLod () const
 Returns whether this node is rendering itself at the current LOD level.
bool isSelfOrChildRenderedAtCurrentLod () const
 Returns whether this node or its children are being rendered at the current LOD level.
void setCurrentLod (int lod)
 Manually set the current LOD, intended for internal use only.
float getLodTransition () const
 Get the transition state between the current LOD and the next lower one (only valid after calculateCurrentLod).
void setLodTransition (float t)
 Manually set the current LOD transition state, intended for internal use only.
Renderable_getRenderable ()
 Returns the internal renderable object for this node.
void * operator new (size_t sz, const char *file, int line, const char *func)
 operator new, with debug line info
void * operator new (size_t sz)
void * operator new (size_t sz, void *ptr)
 placement operator new
void * operator new[] (size_t sz, const char *file, int line, const char *func)
 array operator new, with debug line info
void * operator new[] (size_t sz)
void operator delete (void *ptr)
void operator delete (void *ptr, void *)
void operator delete (void *ptr, const char *, int, const char *)
void operator delete[] (void *ptr)
void operator delete[] (void *ptr, const char *, int, const char *)

Static Public Attributes

static unsigned short POSITION_BUFFER
 Buffer binding used for holding positions.
static unsigned short DELTA_BUFFER
 Buffer binding used for holding delta values.

Protected Member Functions

void updateRenderQueue (RenderQueue *queue)
void visitRenderables (Renderable::Visitor *visitor, bool debugRenderables=false)
const MaterialPtrgetMaterial (void) const
TechniquegetTechnique (void) const
void getRenderOperation (RenderOperation &op)
void getWorldTransforms (Matrix4 *xform) const
Real getSquaredViewDepth (const Camera *cam) const
const LightListgetLights (void) const
bool getCastsShadows (void) const
const VertexDataRecordgetVertexDataRecord () const
void createCpuVertexData ()
void updateVertexBuffer (HardwareVertexBufferSharedPtr &posbuf, HardwareVertexBufferSharedPtr &deltabuf, const Rect &rect)
void destroyCpuVertexData ()
void createGpuVertexData ()
void destroyGpuVertexData ()
void updateGpuVertexData ()
void createGpuIndexData ()
void destroyGpuIndexData ()
void populateIndexData (uint16 batchSize, IndexData *destData)
void writePosVertex (bool compress, uint16 x, uint16 y, float height, const Vector3 &pos, float uvScale, float **ppPos)
void writeDeltaVertex (bool compress, uint16 x, uint16 y, float delta, float deltaThresh, float **ppDelta)
uint16 calcSkirtVertexIndex (uint16 mainIndex, bool isCol)

Protected Attributes

TerrainmTerrain
TerrainQuadTreeNodemParent
TerrainQuadTreeNodemChildren [4]
LodLevelList mLodLevels
uint16 mOffsetX
uint16 mOffsetY
uint16 mBoundaryX
uint16 mBoundaryY
uint16 mSize
 the number of vertices at the original terrain resolution this node encompasses
uint16 mBaseLod
uint16 mDepth
uint16 mQuadrant
Vector3 mLocalCentre
AxisAlignedBox mAABB
Real mBoundingRadius
int mCurrentLod
unsigned short mMaterialLodIndex
float mLodTransition
TerrainQuadTreeNodemChildWithMaxHeightDelta
 The child with the largest height delta.
bool mSelfOrChildRendered
TerrainQuadTreeNodemNodeWithVertexData
VertexDataRecordmVertexDataRecord
MovablemMovable
SceneNodemLocalNode
RendmRend

Friends

class Movable
class Rend

Detailed Description

A node in a quad tree used to store a patch of terrain.

Remarks:
Algorithm overview:
Our goal is to perform traditional chunked LOD with geomorphing. But, instead of just dividing the terrain into tiles, we will divide them into a hierarchy of tiles, a quadtree, where any level of the quadtree can be a rendered tile (to the exclusion of its children). The idea is to collect together children into a larger batch with their siblings as LOD decreases, to improve performance.
The minBatchSize and maxBatchSize parameters on Terrain a key to defining this behaviour. Both values are expressed in vertices down one axis. maxBatchSize determines the number of tiles on one side of the terrain, which is numTiles = (terrainSize-1) / (maxBatchSize-1). This in turn determines the depth of the quad tree, which is sqrt(numTiles). The minBatchSize determines the 'floor' of how low the number of vertices can go in a tile before it has to be grouped together with its siblings to drop any lower. We also do not group a tile with its siblings unless all of them are at this minimum batch size, rather than trying to group them when they all end up on the same 'middle' LOD; this is for several reasons; firstly, tiles hitting the same 'middle' LOD is less likely and more transient if they have different levels of 'roughness', and secondly since we're sharing a vertex / index pool between all tiles, only grouping at the min level means that the number of combinations of buffer sizes for any one tile is greatly simplified, making it easier to pool data. To be more specific, any tile / quadtree node can only have log2(maxBatchSize-1) - log2(minBatchSize-1) + 1 LOD levels (and if you set them to the same value, LOD can only change by going up/down the quadtree). The numbers of vertices / indices in each of these levels is constant for the same (relative) LOD index no matter where you are in the tree, therefore buffers can potentially be reused more easily.

Definition at line 85 of file OgreTerrainQuadTreeNode.h.


Member Typedef Documentation

Definition at line 149 of file OgreTerrainQuadTreeNode.h.


Constructor & Destructor Documentation

Ogre::TerrainQuadTreeNode::TerrainQuadTreeNode ( Terrain terrain,
TerrainQuadTreeNode parent,
uint16  xoff,
uint16  yoff,
uint16  size,
uint16  lod,
uint16  depth,
uint16  quadrant 
)

Constructor.

Parameters:
terrain The ultimate parent terrain
parent Optional parent node (in which case xoff, yoff are 0 and size must be entire terrain)
xoff,off Offsets from the start of the terrain data in 2D
size The size of the node in vertices at the highest LOD
lod The base LOD level
depth The depth that this node is at in the tree (or convenience)
quadrant The index of the quadrant (0, 1, 2, 3)
virtual Ogre::TerrainQuadTreeNode::~TerrainQuadTreeNode (  )  [virtual]

Member Function Documentation

Renderable* Ogre::TerrainQuadTreeNode::_getRenderable (  ) 

Returns the internal renderable object for this node.

void Ogre::TerrainQuadTreeNode::assignVertexData ( uint16  treeDepthStart,
uint16  treeDepthEnd,
uint16  resolution,
uint  sz 
)

Assign vertex data to the tree, from a depth and at a given resolution.

Parameters:
treeDepthStart The first depth of tree that should use this data, owns the data
treeDepthEnd The end of the depth that should use this data (exclusive)
resolution The resolution of the data to use (compared to full terrain)
sz The size of the data along one edge
uint16 Ogre::TerrainQuadTreeNode::calcSkirtVertexIndex ( uint16  mainIndex,
bool  isCol 
) [protected]
bool Ogre::TerrainQuadTreeNode::calculateCurrentLod ( const Camera cam,
Real  cFactor 
)

Calculate appropriate LOD for this node and children.

Parameters:
cam The camera to be used (this should already be the LOD camera)
cFactor The cFactor which incorporates the viewport size, max pixel error and lod bias
Returns:
true if this node or any of its children were selected for rendering
void Ogre::TerrainQuadTreeNode::createCpuVertexData (  )  [protected]
void Ogre::TerrainQuadTreeNode::createGpuIndexData (  )  [protected]
void Ogre::TerrainQuadTreeNode::createGpuVertexData (  )  [protected]
void Ogre::TerrainQuadTreeNode::destroyCpuVertexData (  )  [protected]
void Ogre::TerrainQuadTreeNode::destroyGpuIndexData (  )  [protected]
void Ogre::TerrainQuadTreeNode::destroyGpuVertexData (  )  [protected]
void Ogre::TerrainQuadTreeNode::finaliseDeltaValues ( const Rect rect  ) 

Promote the delta values calculated to the runtime ones (this must be called in the main thread).

const AxisAlignedBox& Ogre::TerrainQuadTreeNode::getAABB (  )  const

Get the AABB (local coords) of this node.

uint16 Ogre::TerrainQuadTreeNode::getBaseLod (  )  const

Get the base LOD level this node starts at (the highest LOD it handles).

Definition at line 108 of file OgreTerrainQuadTreeNode.h.

Real Ogre::TerrainQuadTreeNode::getBoundingRadius (  )  const

Get the bounding radius of this node.

bool Ogre::TerrainQuadTreeNode::getCastsShadows ( void   )  const [protected]
TerrainQuadTreeNode* Ogre::TerrainQuadTreeNode::getChild ( unsigned short  child  )  const

Get child node.

int Ogre::TerrainQuadTreeNode::getCurrentLod (  )  const

Get the current LOD index (only valid after calculateCurrentLod).

Definition at line 243 of file OgreTerrainQuadTreeNode.h.

const LightList& Ogre::TerrainQuadTreeNode::getLights ( void   )  const [protected]
const Vector3& Ogre::TerrainQuadTreeNode::getLocalCentre (  )  const

Get the local centre of this node, relative to parent terrain centre.

Definition at line 229 of file OgreTerrainQuadTreeNode.h.

uint16 Ogre::TerrainQuadTreeNode::getLodCount (  )  const

Get the number of LOD levels this node can represent itself (only > 1 for leaf nodes).

const LodLevel* Ogre::TerrainQuadTreeNode::getLodLevel ( uint16  lod  ) 

Get the LodLevel information for a given lod.

Parameters:
lod The lod level index relative to this classes own list; if you want to use a global lod level, subtract getBaseLod() first. Higher LOD levels are lower detail.
float Ogre::TerrainQuadTreeNode::getLodTransition (  )  const

Get the transition state between the current LOD and the next lower one (only valid after calculateCurrentLod).

Definition at line 251 of file OgreTerrainQuadTreeNode.h.

const MaterialPtr& Ogre::TerrainQuadTreeNode::getMaterial ( void   )  const [protected]
Real Ogre::TerrainQuadTreeNode::getMaxHeight (  )  const

Get the maximum height of the node.

Real Ogre::TerrainQuadTreeNode::getMinHeight (  )  const

Get the minimum height of the node.

TerrainQuadTreeNode* Ogre::TerrainQuadTreeNode::getParent (  )  const

Get parent node.

void Ogre::TerrainQuadTreeNode::getRenderOperation ( RenderOperation op  )  [protected]
Real Ogre::TerrainQuadTreeNode::getSquaredViewDepth ( const Camera cam  )  const [protected]
Technique* Ogre::TerrainQuadTreeNode::getTechnique ( void   )  const [protected]
Terrain* Ogre::TerrainQuadTreeNode::getTerrain (  )  const

Get ultimate parent terrain.

const VertexDataRecord* Ogre::TerrainQuadTreeNode::getVertexDataRecord (  )  const [protected]
void Ogre::TerrainQuadTreeNode::getWorldTransforms ( Matrix4 xform  )  const [protected]
uint16 Ogre::TerrainQuadTreeNode::getXOffset (  )  const

Get the horizontal offset into the main terrain data of this node.

Definition at line 102 of file OgreTerrainQuadTreeNode.h.

uint16 Ogre::TerrainQuadTreeNode::getYOffset (  )  const

Get the vertical offset into the main terrain data of this node.

Definition at line 104 of file OgreTerrainQuadTreeNode.h.

bool Ogre::TerrainQuadTreeNode::isLeaf (  )  const

Is this a leaf node (no children).

bool Ogre::TerrainQuadTreeNode::isRenderedAtCurrentLod (  )  const

Returns whether this node is rendering itself at the current LOD level.

bool Ogre::TerrainQuadTreeNode::isSelfOrChildRenderedAtCurrentLod (  )  const

Returns whether this node or its children are being rendered at the current LOD level.

void Ogre::TerrainQuadTreeNode::load (  ) 

Load node and children (perform GPU tasks, will be render thread).

void Ogre::TerrainQuadTreeNode::mergeIntoBounds ( long  x,
long  y,
const Vector3 pos 
)

Merge a point (relative to terrain node) into the local bounds, and that of children if applicable.

Parameters:
x,y The point on the terrain to which this position corresponds (affects which nodes update their bounds)
pos The position relative to the terrain centre
void Ogre::TerrainQuadTreeNode::notifyDelta ( uint16  x,
uint16  y,
uint16  lod,
Real  delta 
)

Notify the node (and children) of a height delta value.

template<class Alloc >
void Ogre::AllocatedObject< Alloc >::operator delete ( void *  ptr,
const char *  ,
int  ,
const char *   
) [inherited]

Definition at line 107 of file OgreMemoryAllocatedObject.h.

template<class Alloc >
void Ogre::AllocatedObject< Alloc >::operator delete ( void *  ptr,
void *   
) [inherited]

Definition at line 101 of file OgreMemoryAllocatedObject.h.

template<class Alloc >
void Ogre::AllocatedObject< Alloc >::operator delete ( void *  ptr  )  [inherited]

Definition at line 95 of file OgreMemoryAllocatedObject.h.

template<class Alloc >
void Ogre::AllocatedObject< Alloc >::operator delete[] ( void *  ptr,
const char *  ,
int  ,
const char *   
) [inherited]

Definition at line 118 of file OgreMemoryAllocatedObject.h.

template<class Alloc >
void Ogre::AllocatedObject< Alloc >::operator delete[] ( void *  ptr  )  [inherited]

Definition at line 112 of file OgreMemoryAllocatedObject.h.

template<class Alloc >
void* Ogre::AllocatedObject< Alloc >::operator new ( size_t  sz,
void *  ptr 
) [inherited]

placement operator new

Definition at line 78 of file OgreMemoryAllocatedObject.h.

template<class Alloc >
void* Ogre::AllocatedObject< Alloc >::operator new ( size_t  sz  )  [inherited]

Definition at line 72 of file OgreMemoryAllocatedObject.h.

template<class Alloc >
void* Ogre::AllocatedObject< Alloc >::operator new ( size_t  sz,
const char *  file,
int  line,
const char *  func 
) [inherited]

operator new, with debug line info

Definition at line 67 of file OgreMemoryAllocatedObject.h.

template<class Alloc >
void* Ogre::AllocatedObject< Alloc >::operator new[] ( size_t  sz  )  [inherited]

Definition at line 90 of file OgreMemoryAllocatedObject.h.

template<class Alloc >
void* Ogre::AllocatedObject< Alloc >::operator new[] ( size_t  sz,
const char *  file,
int  line,
const char *  func 
) [inherited]

array operator new, with debug line info

Definition at line 85 of file OgreMemoryAllocatedObject.h.

bool Ogre::TerrainQuadTreeNode::pointIntersectsNode ( long  x,
long  y 
)

Returns true if the given point is in the terrain area that this node references.

Parameters:
x,y The point in top-level terrain coords
void Ogre::TerrainQuadTreeNode::populateIndexData ( uint16  batchSize,
IndexData destData 
) [protected]
void Ogre::TerrainQuadTreeNode::postDeltaCalculation ( const Rect rect  ) 

Notify the node (and children) that deltas have finished being calculated.

void Ogre::TerrainQuadTreeNode::preDeltaCalculation ( const Rect rect  ) 

Notify the node (and children) that deltas are going to be calculated for a given range.

Remarks:
Based on this call, we can know whether or not to reset the max height.
void Ogre::TerrainQuadTreeNode::prepare ( StreamSerialiser stream  ) 

Prepare node from a stream.

void Ogre::TerrainQuadTreeNode::prepare (  ) 

Prepare node and children (perform CPU tasks, may be background thread).

bool Ogre::TerrainQuadTreeNode::rectContainsNode ( const Rect rect  ) 

Returns true if the given rectangle completely contains the terrain area that this node references.

Parameters:
rect The region in top-level terrain coords
bool Ogre::TerrainQuadTreeNode::rectIntersectsNode ( const Rect rect  ) 

Returns true if the given rectangle overlaps the terrain area that this node references.

Parameters:
rect The region in top-level terrain coords
void Ogre::TerrainQuadTreeNode::resetBounds ( const Rect rect  ) 

Reset the bounds of this node and all its children for the region given.

Parameters:
rect The region for which bounds should be reset, in top-level terrain coords
void Ogre::TerrainQuadTreeNode::save ( StreamSerialiser stream  ) 

Save node to a stream.

void Ogre::TerrainQuadTreeNode::setCurrentLod ( int  lod  ) 

Manually set the current LOD, intended for internal use only.

void Ogre::TerrainQuadTreeNode::setLodTransition ( float  t  ) 

Manually set the current LOD transition state, intended for internal use only.

void Ogre::TerrainQuadTreeNode::unload (  ) 

Unload node and children (perform GPU tasks, will be render thread).

void Ogre::TerrainQuadTreeNode::unprepare (  ) 

Unprepare node and children (perform CPU tasks, may be background thread).

void Ogre::TerrainQuadTreeNode::updateGpuVertexData (  )  [protected]
void Ogre::TerrainQuadTreeNode::updateRenderQueue ( RenderQueue queue  )  [protected]
void Ogre::TerrainQuadTreeNode::updateVertexBuffer ( HardwareVertexBufferSharedPtr posbuf,
HardwareVertexBufferSharedPtr deltabuf,
const Rect rect 
) [protected]
void Ogre::TerrainQuadTreeNode::updateVertexData ( bool  positions,
bool  deltas,
const Rect rect,
bool  cpuData 
)

Tell the node to update its vertex data for a given region.

void Ogre::TerrainQuadTreeNode::useAncestorVertexData ( TerrainQuadTreeNode owner,
uint16  treeDepthEnd,
uint16  resolution 
)

Tell a node that it should use an anscestor's vertex data.

Parameters:
treeDepthEnd The end of the depth that should use this data (exclusive)
resolution The resolution of the data to use
void Ogre::TerrainQuadTreeNode::visitRenderables ( Renderable::Visitor visitor,
bool  debugRenderables = false 
) [protected]
void Ogre::TerrainQuadTreeNode::writeDeltaVertex ( bool  compress,
uint16  x,
uint16  y,
float  delta,
float  deltaThresh,
float **  ppDelta 
) [protected]
void Ogre::TerrainQuadTreeNode::writePosVertex ( bool  compress,
uint16  x,
uint16  y,
float  height,
const Vector3 pos,
float  uvScale,
float **  ppPos 
) [protected]

Friends And Related Function Documentation

friend class Movable [friend]

Definition at line 350 of file OgreTerrainQuadTreeNode.h.

friend class Rend [friend]

Definition at line 372 of file OgreTerrainQuadTreeNode.h.


Member Data Documentation

unsigned short Ogre::TerrainQuadTreeNode::DELTA_BUFFER [static]

Buffer binding used for holding delta values.

Definition at line 258 of file OgreTerrainQuadTreeNode.h.

Definition at line 276 of file OgreTerrainQuadTreeNode.h.

Definition at line 272 of file OgreTerrainQuadTreeNode.h.

Definition at line 269 of file OgreTerrainQuadTreeNode.h.

Definition at line 269 of file OgreTerrainQuadTreeNode.h.

Definition at line 277 of file OgreTerrainQuadTreeNode.h.

Definition at line 265 of file OgreTerrainQuadTreeNode.h.

The child with the largest height delta.

Definition at line 282 of file OgreTerrainQuadTreeNode.h.

Definition at line 278 of file OgreTerrainQuadTreeNode.h.

Definition at line 273 of file OgreTerrainQuadTreeNode.h.

Definition at line 275 of file OgreTerrainQuadTreeNode.h.

Definition at line 351 of file OgreTerrainQuadTreeNode.h.

Definition at line 266 of file OgreTerrainQuadTreeNode.h.

Definition at line 280 of file OgreTerrainQuadTreeNode.h.

Definition at line 279 of file OgreTerrainQuadTreeNode.h.

Definition at line 349 of file OgreTerrainQuadTreeNode.h.

Definition at line 307 of file OgreTerrainQuadTreeNode.h.

Definition at line 268 of file OgreTerrainQuadTreeNode.h.

Definition at line 268 of file OgreTerrainQuadTreeNode.h.

Definition at line 264 of file OgreTerrainQuadTreeNode.h.

Definition at line 274 of file OgreTerrainQuadTreeNode.h.

Definition at line 371 of file OgreTerrainQuadTreeNode.h.

Definition at line 283 of file OgreTerrainQuadTreeNode.h.

the number of vertices at the original terrain resolution this node encompasses

Definition at line 271 of file OgreTerrainQuadTreeNode.h.

Definition at line 263 of file OgreTerrainQuadTreeNode.h.

Definition at line 308 of file OgreTerrainQuadTreeNode.h.

Buffer binding used for holding positions.

Definition at line 256 of file OgreTerrainQuadTreeNode.h.


The documentation for this class was generated from the following file:

Copyright © 2012 Torus Knot Software Ltd
Creative Commons License
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
Last modified Fri May 25 23:42:20 2012