public TerrainQuadTreeNode( Terrain terrain, TerrainQuadTreeNode parent, ushort xOff, ushort yOff, ushort size, ushort lod, ushort depth, ushort quadrant ) : base() { this.mTerrain = terrain; this.mParent = parent; this.mOffsetX = xOff; this.mOffsetY = yOff; this.mBoundaryX = (ushort)( xOff + size ); this.mBoundaryY = (ushort)( yOff + size ); this.mSize = size; this.mBaseLod = lod; this.mDepth = depth; this.mQuadrant = quadrant; this.mBoundingRadius = 0; this.mCurrentLod = -1; this.mMaterialLodIndex = 0; this.mLodTransition = 0; this.mChildWithMaxHeightDelta = null; this.mSelfOrChildRendered = false; this.mNodeWithVertexData = null; this.mAABB = new AxisAlignedBox(); if ( this.mTerrain.MaxBatchSize < size ) { var childSize = (ushort)( ( ( size - 1 )*0.5f ) + 1 ); var childOff = (ushort)( childSize - 1 ); var childLod = (ushort)( lod - 1 ); // LOD levels decrease down the tree (higher detail) var childDepth = (ushort)( depth + 1 ); // create children this.mChildren[ 0 ] = new TerrainQuadTreeNode( this.mTerrain, this, xOff, yOff, childSize, childLod, childDepth, 0 ); this.mChildren[ 1 ] = new TerrainQuadTreeNode( this.mTerrain, this, (ushort)( xOff + childOff ), yOff, childSize, childLod, childDepth, 1 ); this.mChildren[ 2 ] = new TerrainQuadTreeNode( this.mTerrain, this, xOff, (ushort)( yOff + childOff ), childSize, childLod, childDepth, 2 ); this.mChildren[ 3 ] = new TerrainQuadTreeNode( this.mTerrain, this, (ushort)( xOff + childOff ), (ushort)( yOff + childOff ), childSize, childLod, childDepth, 3 ); var ll = new LodLevel(); // non-leaf nodes always render with minBatchSize vertices ll.BatchSize = this.mTerrain.MinBatchSize; ll.MaxHeightDelta = 0; ll.CalcMaxHeightDelta = 0; this.mLodLevels.Add( ll ); } else { //no children Array.Clear( this.mChildren, 0, this.mChildren.Length ); // this is a leaf node and may have internal LODs of its own var ownLod = this.mTerrain.NumLodLevelsPerLeaf; Debug.Assert( lod == ( ownLod - 1 ), "The lod passed in should reflect the number of lods in a leaf" ); // leaf nodes always have a base LOD of 0, because they're always handling // the highest level of detail this.mBaseLod = 0; var sz = this.mTerrain.MaxBatchSize; while ( ownLod-- != 0 ) { var ll = new LodLevel(); ll.BatchSize = sz; ll.MaxHeightDelta = 0; ll.CalcMaxHeightDelta = 0; this.mLodLevels.Add( ll ); if ( ownLod != 0 ) { sz = (ushort)( ( ( sz - 1 )*0.5 ) + 1 ); } } Debug.Assert( sz == this.mTerrain.MinBatchSize ); } // local centre calculation // because of pow2 +1 there is always a middle point var midoffset = (ushort)( ( size - 1 )/2 ); var midpointX = (ushort)( this.mOffsetX + midoffset ); var midpointY = (ushort)( this.mOffsetY + midoffset ); //derive the local centry, but give it a height if 0 //TODO: - what if we actually centred this at the terrain height at this point? //would this be better? this.mTerrain.GetPoint( midpointX, midpointY, 0, ref this.mLocalCentre ); this.mMovable = new Movable( this ); this.mRend = new Rend( this ); }
public void PostDeltaCalculation( Rectangle rect ) { if ( rect.Left <= this.mBoundaryX || rect.Right > this.mOffsetX || rect.Top <= this.mBoundaryY || rect.Bottom > this.mOffsetY ) { // relevant to this node (overlaps) // each non-leaf node should know which of its children transitions // to the lower LOD level last, because this is the one which controls // when the parent takes over if ( !IsLeaf ) { float maxChildDelta = -1; TerrainQuadTreeNode childWithMaxHeightDelta = null; for ( int i = 0; i < 4; i++ ) { TerrainQuadTreeNode child = this.mChildren[ i ]; child.PostDeltaCalculation( rect ); float childData = child.GetLodLevel( (ushort)( child.LodCount - 1 ) ).CalcMaxHeightDelta; if ( childData > maxChildDelta ) { childWithMaxHeightDelta = child; maxChildDelta = childData; } } // make sure that our highest delta value is greater than all children's // otherwise we could have some crossover problems // for a non-leaf, there is only one LOD level LodLevel tmp = this.mLodLevels[ 0 ]; tmp.CalcMaxHeightDelta = System.Math.Max( tmp.CalcMaxHeightDelta, maxChildDelta*(Real)1.05 ); this.mChildWithMaxHeightDelta = childWithMaxHeightDelta; } else { // make sure own LOD levels delta values ascend for ( int i = 0; i < this.mLodLevels.Count - 1; i++ ) { // the next LOD after this one should have a higher delta // otherwise it won't come into affect further back like it should! LodLevel tmp = this.mLodLevels[ i ]; LodLevel tmpPlus = this.mLodLevels[ i + 1 ]; tmpPlus.CalcMaxHeightDelta = System.Math.Max( tmpPlus.CalcMaxHeightDelta, tmp.CalcMaxHeightDelta*(Real)1.05 ); } } } }
public Movable( TerrainQuadTreeNode parent ) : base() { this.mParent = parent; }
public Rend( TerrainQuadTreeNode parent ) { this.mParent = parent; }
public void AssignVertexData( ushort treeDepthStart, ushort treeDepthEnd, ushort resolution, ushort sz ) { Debug.Assert( treeDepthStart >= this.mDepth, "Should not be calling this" ); if ( this.mDepth == treeDepthStart ) { //we own this vertex data this.mNodeWithVertexData = this; this.mVertexDataRecord = new VertexDataRecord( resolution, sz, (ushort)( treeDepthEnd - treeDepthStart ) ); CreateCpuVertexData(); //pass on to children if ( !IsLeaf && treeDepthEnd > ( this.mDepth + 1 ) ) // treeDepthEnd is exclusive, and this is children { for ( int i = 0; i < 4; ++i ) { this.mChildren[ i ].UseAncestorVertexData( this, treeDepthEnd, resolution ); } } } else { Debug.Assert( !IsLeaf, "No more levels below this!" ); for ( int i = 0; i < 4; ++i ) { this.mChildren[ i ].AssignVertexData( treeDepthStart, treeDepthEnd, resolution, sz ); } } }
public void UseAncestorVertexData( TerrainQuadTreeNode owner, int treeDepthEnd, int resolution ) { this.mNodeWithVertexData = owner; this.mVertexDataRecord = null; if ( !IsLeaf && treeDepthEnd > ( this.mDepth + 1 ) ) // treeDepthEnd is exclusive, and this is children { for ( int i = 0; i < 4; i++ ) { this.mChildren[ i ].UseAncestorVertexData( owner, treeDepthEnd, resolution ); } } }
public bool Prepare( ImportData importData ) { FreeTemporaryResources(); FreeCPUResources(); CopyGlobalOptions(); //validate if ( !( Bitwise.IsPow2( importData.TerrainSize - 1 ) && Bitwise.IsPow2( importData.MinBatchSize - 1 ) && Bitwise.IsPow2( importData.MaxBatchSize - 1 ) ) ) { throw new AxiomException( "terrainSize, minBatchSize and maxBatchSize must all be n^2 + 1. Terrain.Prepare" ); } if ( importData.MinBatchSize > importData.MaxBatchSize ) { throw new AxiomException( "MinBatchSize must be less then or equal to MaxBatchSize. Terrain.Prepare" ); } if ( importData.MaxBatchSize > TERRAIN_MAX_BATCH_SIZE ) { throw new AxiomException( "MaxBatchSize must be not larger then {0} . Terrain.Prepare", TERRAIN_MAX_BATCH_SIZE ); } Alignment = importData.TerrainAlign; this.mSize = importData.TerrainSize; this.mWorldSize = importData.WorldSize; this.mLayerDecl = importData.LayerDeclaration; CheckDeclaration(); this.mLayers = importData.LayerList; CheckLayers( false ); DeriveUVMultipliers(); this.mMaxBatchSize = importData.MaxBatchSize; this.mMinBatchSize = importData.MinBatchSize; this.mPos = importData.Pos; UpdateBaseScale(); DetermineLodLevels(); int numVertices = this.mSize*this.mSize; this.mHeightData = new float[numVertices]; if ( importData.InputFloat != null ) { if ( Utility.RealEqual( importData.InputBias, 0.0f ) && Utility.RealEqual( importData.InputScale, 1.0f ) ) { //straigt copy this.mHeightData = new float[numVertices]; Array.Copy( importData.InputFloat, this.mHeightData, this.mHeightData.Length ); } else { // scale & bias, lets do it unsafe, should be faster :) var src = importData.InputFloat; for ( var i = 0; i < numVertices; ++i ) { this.mHeightData[ i ] = ( src[ i ]*importData.InputScale ) + importData.InputBias; } } } else if ( importData.InputImage != null ) { var img = importData.InputImage; if ( img.Width != this.mSize || img.Height != this.mSize ) { img.Resize( this.mSize, this.mSize ); } // convert image data to floats // Do this on a row-by-row basis, because we describe the terrain in // a bottom-up fashion (ie ascending world coords), while Image is top-down var pSrcBaseF = BufferBase.Wrap( img.Data ); var pHeightDataF = BufferBase.Wrap( this.mHeightData, mHeightData.Length * sizeof(float) ); for ( var i = 0; i < this.mSize; ++i ) { var srcy = this.mSize - i - 1; using ( var pSrc = pSrcBaseF + srcy*img.RowSpan ) { using ( var pDest = pHeightDataF + i*this.mSize*sizeof ( float ) ) { PixelConverter.BulkPixelConversion( pSrc, img.Format, pDest, PixelFormat.FLOAT32_R, this.mSize ); } } } pSrcBaseF.Dispose(); pHeightDataF.Dispose(); if ( !Utility.RealEqual( importData.InputBias, 0.0f ) || !Utility.RealEqual( importData.InputScale, 1.0f ) ) { for ( int i = 0; i < numVertices; ++i ) { this.mHeightData[ i ] = ( this.mHeightData[ i ]*importData.InputScale ) + importData.InputBias; } } } else { // start with flat terrain this.mHeightData = new float[this.mSize*this.mSize]; } var deltaData = new float[numVertices]; this.mHeightDataPtr = BufferBase.Wrap( this.mHeightData,mHeightData.Length * sizeof(float) ); this.mDeltaDataPtr = BufferBase.Wrap( deltaData, deltaData.Length * sizeof(float) ); var numLevel = (ushort)(int)( NumLodLevels - 1 ); QuadTree = new TerrainQuadTreeNode( this, null, 0, 0, this.mSize, (ushort)( NumLodLevels - 1 ), 0, 0 ); QuadTree.Prepare(); //calculate entire terrain var rect = new Rectangle(); rect.Top = 0; rect.Bottom = this.mSize; rect.Left = 0; rect.Right = this.mSize; CalculateHeightDeltas( rect ); FinalizeHeightDeltas( rect, true ); DistributeVertexData(); // Imported data is treated as modified because it's not saved IsModified = true; IsHeightDataModified = true; return true; }
public bool Prepare( StreamSerializer stream ) { FreeTemporaryResources(); FreeCPUResources(); CopyGlobalOptions(); if ( stream.ReadChunkBegin( TERRAIN_CHUNK_ID, TERRAIN_CHUNK_VERSION ) == null ) { return false; } byte align; stream.Read( out align ); Alignment = (Alignment)align; stream.Read( out this.mSize ); stream.Read( out this.mWorldSize ); stream.Read( out this.mMaxBatchSize ); stream.Read( out this.mMinBatchSize ); stream.Read( out this.mPos ); RootSceneNode.Position = this.mPos; UpdateBaseScale(); DetermineLodLevels(); int numVertices = this.mSize*this.mSize; this.mHeightData = new float[numVertices]; stream.Read( out this.mHeightData ); // layer declaration if ( !ReadLayerDeclaration( ref stream, ref this.mLayerDecl ) ) { return false; } CheckDeclaration(); // Layers if ( !ReadLayerInstanceList( ref stream, this.mLayerDecl.Elements.Count, ref this.mLayers ) ) { return false; } DeriveUVMultipliers(); // Packed layer blend data var numLayers = (byte)this.mLayers.Count; stream.Read( out this.mLayerBlendMapSize ); this.mLayerBlendSizeActual = this.mLayerBlendMapSize; // for now, until we check //load packed CPU data var numBlendTex = GetBlendTextureCount( numLayers ); for ( var i = 0; i < numBlendTex; ++i ) { var fmt = GetBlendTextureFormat( (byte)i, numLayers ); var channels = PixelUtil.GetNumElemBytes( fmt ); var dataSz = channels*this.mLayerBlendMapSize*this.mLayerBlendMapSize; var data = new byte[dataSz]; stream.Read( out data ); this.mCpuBlendMapStorage.AddRange( data ); } //derived data while ( !stream.IsEndOfChunk( TERRAIN_CHUNK_ID ) && stream.NextChunkId == TERRAINDERIVEDDATA_CHUNK_ID ) { stream.ReadChunkBegin( TERRAINDERIVEDDATA_CHUNK_ID, TERRAINDERIVEDDATA_CHUNK_VERSION ); //name var name = string.Empty; stream.Read( out name ); ushort sz; stream.Read( out sz ); if ( name == "normalmap" ) { this.mNormalMapRequired = true; var data = new byte[sz*sz*3]; stream.Read( out data ); using ( var pDataF = BufferBase.Wrap( data ) ) { this.mCpuTerrainNormalMap = new PixelBox( sz, sz, 1, PixelFormat.BYTE_RGB, pDataF ); } } else if ( name == "colormap" ) { IsGlobalColorMapEnabled = true; GlobalColorMapSize = sz; this.mCpuColorMapStorage = new byte[sz*sz*3]; stream.Read( out this.mCpuColorMapStorage ); } else if ( name == "lightmap" ) { this.mLightMapRequired = true; LightMapSize = sz; this.mCpuLightmapStorage = new byte[sz*sz]; stream.Read( out this.mCpuLightmapStorage ); } else if ( name == "compositemap" ) { this.mCompositeMapRequired = true; this.mCompositeMapSize = sz; this.mCpuCompositeMapStorage = new byte[sz*sz*4]; stream.Read( out this.mCpuCompositeMapStorage ); } stream.ReadChunkEnd( TERRAINDERIVEDDATA_CHUNK_ID ); } //Load delta data var deltaData = new byte[ sizeof( float ) * numVertices ]; stream.Read( out deltaData ); this.mDeltaDataPtr = BufferBase.Wrap( deltaData ); //Create and load quadtree QuadTree = new TerrainQuadTreeNode( this, null, 0, 0, this.mSize, (ushort)( NumLodLevels - 1 ), 0, 0 ); QuadTree.Prepare(); stream.ReadChunkEnd( TERRAIN_CHUNK_ID ); DistributeVertexData(); IsModified = false; IsHeightDataModified = false; return true; }
/// <summary> /// Default constructor. /// </summary> /// <param name="terrain">The ultimate parent terrain</param> /// <param name="parent">ptional parent node (in which case xoff, yoff are 0 and size must be entire terrain)</param> /// <param name="xOff">Offsets from the start of the terrain data in 2D</param> /// <param name="yOff">Offsets from the start of the terrain data in 2D</param> /// <param name="size">The size of the node in vertices at the highest LOD</param> /// <param name="lod">The base LOD level</param> /// <param name="depth">The depth that this node is at in the tree (or convenience)</param> /// <param name="quadrant">The index of the quadrant (0, 1, 2, 3)</param> public TerrainQuadTreeNode(Terrain terrain, TerrainQuadTreeNode parent, ushort xOff, ushort yOff, ushort size, ushort lod, ushort depth, ushort quadrant) { mTerrain = terrain; mParent = parent; mOffsetX = xOff; mOffsetY = yOff; mBoundaryX = (ushort)(xOff + size); mBoundaryY = (ushort)(yOff + size); mSize = size; mBaseLod = lod; mDepth = depth; mQuadrant = quadrant; mBoundingRadius = 0; mCurrentLod = -1; mMaterialLodIndex = 0; mLodTransition = 0; mChildWithMaxHeightDelta = null; mSelfOrChildRendered = false; mNodeWithVertexData = null; // mMovable = null; mRend = null; mAABB = new AxisAlignedBox(); if (mTerrain.MaxBatchSize < size) { ushort childSize = (ushort)(((size - 1) * 0.5f) + 1); ushort childOff = (ushort)(childSize - 1); ushort childLod = (ushort)(lod - 1); ushort childDepth = (ushort)(depth + 1); mChildren[0] = new TerrainQuadTreeNode(mTerrain, this, xOff, yOff, childSize, childLod, childDepth, 0); mChildren[1] = new TerrainQuadTreeNode(mTerrain, this, (ushort)(xOff + childOff), yOff, childSize, childLod, childDepth, 1); mChildren[2] = new TerrainQuadTreeNode(mTerrain, this, xOff, (ushort)(yOff + childOff), childSize, childLod, childDepth, 2); mChildren[3] = new TerrainQuadTreeNode(mTerrain, this, (ushort)(xOff + childOff), (ushort)(yOff + childOff), childSize, childLod, childDepth, 3); LodLevel ll = new LodLevel(); // non-leaf nodes always render with minBatchSize vertices ll.BatchSize = mTerrain.MinBatchSize; ll.MaxHeightDelta = 0; ll.CalcMaxHeightDelta = 0; mLodLevels.Add(ll); } else { //no children Array.Clear(mChildren, 0, mChildren.Length); // this is a leaf node and may have internal LODs of its own ushort ownLod = mTerrain.LodLevelsPerLeafCount; Debug.Assert(lod == (ownLod - 1), "The lod passed in should reflect the number of lods in a leaf"); // leaf nodes always have a base LOD of 0, because they're always handling // the highest level of detail mBaseLod = 0; ushort sz = mTerrain.MaxBatchSize; while (ownLod-- != 0) { LodLevel ll = new LodLevel(); ll.BatchSize = sz; ll.MaxHeightDelta = 0; ll.CalcMaxHeightDelta = 0; mLodLevels.Add(ll); if(ownLod != 0) sz = (ushort)(((sz - 1) * 0.5) + 1); } Debug.Assert(sz == mTerrain.MinBatchSize); } // local centre calculation // because of pow2 +1 there is always a middle point ushort midoffset = (ushort)((size - 1) / 2); ushort midpointX = (ushort)(mOffsetX + midoffset); ushort midpointY = (ushort)(mOffsetY + midoffset); //derive the local centry, but give it a height if 0 //TODO: - what if we actually centred this at the terrain height at this point? //would this be better? mTerrain.GetPoint(midpointX, midpointY, 0, ref mLocalCentre); /*mRend = new Rend(this); mMovable = new Movable(this,mRend);*/ mRend= new TerrainRendable(this); SceneNode sn = mTerrain.RootSceneNode.CreateChildSceneNode(mLocalCentre); sn.AttachObject(mRend); // sn.AttachObject(mRend); }
public TerrainRendable(TerrainQuadTreeNode parent) { mParent = parent; this.MovableType = "AxiomTerrainNodeMovable"; }
/// <summary> /// /// </summary> /// <param name="parent"></param> public Movable(TerrainQuadTreeNode parent, Rend renderable) { mParent = parent; mRend = renderable; }