public Rectangle Intersect( Rectangle rhs ) { return Intersect( this, rhs ); }
internal static Rectangle Intersect( Rectangle lhs, Rectangle rhs ) { Rectangle r; r._left = lhs._left > rhs._left ? lhs._left : rhs._left; r._top = lhs._top > rhs._top ? lhs._top : rhs._top; r._right = lhs._right < rhs._right ? lhs._right : rhs._right; r._bottom = lhs._bottom < rhs._bottom ? lhs._bottom : rhs._bottom; return r; }
public void PreDeltaCalculation( Rectangle rect ) { if ( rect.Left <= this.mBoundaryX || rect.Right > this.mOffsetX || rect.Top <= this.mBoundaryY || rect.Bottom > this.mOffsetY ) { // relevant to this node (overlaps) // if the rect covers the whole node, reset the max height // this means that if you recalculate the deltas progressively, end up keeping // a max height that's no longer the case (ie more conservative lod), // but that's the price for not recaculating the whole node. If a // complete recalculation is required, just dirty the entire node. (or terrain) // Note we use the 'calc' field here to avoid interfering with any // ongoing LOD calculations (this can be in the background) if ( rect.Left <= this.mOffsetX && rect.Right > this.mBoundaryX && rect.Top <= this.mOffsetY && rect.Bottom > this.mBoundaryY ) { for ( int i = 0; i < this.mLodLevels.Count; i++ ) { LodLevel tmp = this.mLodLevels[ i ]; tmp.CalcMaxHeightDelta = 0.0f; } } //pass on to children if ( !IsLeaf ) { for ( int i = 0; i < 4; i++ ) { this.mChildren[ i ].PreDeltaCalculation( rect ); } } } }
public void FinaliseDeltaValues( Rectangle rect ) { if ( rect.Left <= this.mBoundaryX || rect.Right > this.mOffsetX || rect.Top <= this.mBoundaryY || rect.Bottom > this.mOffsetY ) { // relevant to this node (overlaps) // Children if ( !IsLeaf ) { for ( int i = 0; i < 4; i++ ) { TerrainQuadTreeNode child = this.mChildren[ i ]; child.FinaliseDeltaValues( rect ); } } // self LodLevel[] lvls = this.mLodLevels.ToArray(); for ( int i = 0; i < lvls.Length; i++ ) { LodLevel tmp = lvls[ i ]; // copy from 'calc' area to runtime value tmp.MaxHeightDelta = tmp.CalcMaxHeightDelta; // also trash stored cfactor tmp.LastCFactor = 0; } } }
public bool RectIntersectsNode( Rectangle rect ) { return ( rect.Right >= this.mOffsetX && rect.Left <= this.mBoundaryX && rect.Bottom >= this.mOffsetY && rect.Top <= this.mBoundaryY ); }
protected void CreateCpuVertexData() { if ( this.mVertexDataRecord != null ) { DestroyCpuVertexData(); // create vertex structure, not using GPU for now (these are CPU structures) var dcl = new VertexDeclaration(); var bufbind = new VertexBufferBinding(); this.mVertexDataRecord.CpuVertexData = new VertexData( dcl, bufbind ); // Vertex declaration // TODO: consider vertex compression int offset = 0; // POSITION // float3(x, y, z) offset += dcl.AddElement( POSITION_BUFFER, offset, VertexElementType.Float3, VertexElementSemantic.Position ).Size; // UV0 // float2(u, v) // TODO - only include this if needing fixed-function offset += dcl.AddElement( POSITION_BUFFER, offset, VertexElementType.Float2, VertexElementSemantic.TexCoords, 0 ).Size; // UV1 delta information // float2(delta, deltaLODthreshold) offset = 0; offset += dcl.AddElement( DELTA_BUFFER, offset, VertexElementType.Float2, VertexElementSemantic.TexCoords, 1 ).Size; // Calculate number of vertices // Base geometry size * size var baseNumVerts = (int)Utility.Sqr( this.mVertexDataRecord.Size ); var numVerts = baseNumVerts; // Now add space for skirts // Skirts will be rendered as copies of the edge vertices translated downwards // Some people use one big fan with only 3 vertices at the bottom, // but this requires creating them much bigger that necessary, meaning // more unnecessary overdraw, so we'll use more vertices // You need 2^levels + 1 rows of full resolution (max 129) vertex copies, plus // the same number of columns. There are common vertices at intersections var levels = this.mVertexDataRecord.TreeLevels; this.mVertexDataRecord.NumSkirtRowsCols = (ushort)( System.Math.Pow( 2, levels ) + 1 ); this.mVertexDataRecord.SkirtRowColSkip = (ushort)( ( this.mVertexDataRecord.Size - 1 )/( this.mVertexDataRecord.NumSkirtRowsCols - 1 ) ); numVerts += this.mVertexDataRecord.Size*this.mVertexDataRecord.NumSkirtRowsCols; numVerts += this.mVertexDataRecord.Size*this.mVertexDataRecord.NumSkirtRowsCols; //manually create CPU-side buffer var posBuf = HardwareBufferManager.Instance.CreateVertexBuffer( dcl.Clone( POSITION_BUFFER ), numVerts, BufferUsage.StaticWriteOnly, false ); var deltabuf = HardwareBufferManager.Instance.CreateVertexBuffer( dcl.Clone( DELTA_BUFFER ), numVerts, BufferUsage.StaticWriteOnly, false ); this.mVertexDataRecord.CpuVertexData.vertexStart = 0; this.mVertexDataRecord.CpuVertexData.vertexCount = numVerts; var updateRect = new Rectangle( this.mOffsetX, this.mOffsetY, this.mBoundaryX, this.mBoundaryY ); UpdateVertexBuffer( posBuf, deltabuf, updateRect ); this.mVertexDataRecord.IsGpuVertexDataDirty = true; bufbind.SetBinding( POSITION_BUFFER, posBuf ); bufbind.SetBinding( DELTA_BUFFER, deltabuf ); } }
public void NeighbourModified( NeighbourIndex index, Rectangle edgerect, Rectangle shadowrect ) { // We can safely assume that we would not have been contacted if it wasn't // important var neighbour = GetNeighbour( index ); if ( neighbour == null ) { return; // bogus request } var updateGeom = false; byte updateDerived = 0; if ( !edgerect.IsNull ) { // update edges; match heights first, then recalculate normals // reduce to just single line / corner var heightMatchRect = new Rectangle(); GetEdgeRect( index, 1, ref heightMatchRect ); heightMatchRect = heightMatchRect.Intersect( edgerect ); for ( var y = heightMatchRect.Top; y < heightMatchRect.Bottom; ++y ) { for ( var x = heightMatchRect.Left; x < heightMatchRect.Right; ++x ) { long nx = 0, ny = 0; GetNeighbourPoint( index, x, y, ref nx, ref ny ); var neighbourHeight = neighbour.GetHeightAtPoint( nx, ny ); if ( !Utility.RealEqual( neighbourHeight, GetHeightAtPoint( x, y ), 1e-3f ) ) { SetHeightAtPoint( x, y, neighbourHeight ); if ( !updateGeom ) { updateGeom = true; updateDerived |= DERIVED_DATA_ALL; } } } } // if we didn't need to update heights, we still need to update normals // because this was called only if neighbor changed if ( !updateGeom ) { // ideally we would deal with normal dirty rect separately (as we do with // lightmaps) because a dirty geom rectangle will actually grow by one // element in each direction for normals recalculation. However for // the sake of one row/column it's really not worth it. this.mDirtyDerivedDataRect.Merge( edgerect ); updateDerived |= DERIVED_DATA_NORMALS; } } if ( !shadowrect.IsNull ) { // update shadows // here we need to widen the rect passed in based on the min/max height // of the *neighbour* var lightVec = TerrainGlobalOptions.LightMapDirection; var widenedRect = new Rectangle(); WidenRectByVector( lightVec, shadowrect, neighbour.MinHeight, neighbour.MaxHeight, ref widenedRect ); // set the special-case lightmap dirty rectangle this.mDirtyLightmapFromNeighboursRect.Merge( widenedRect ); updateDerived |= DERIVED_DATA_LIGHTMAP; } if ( updateGeom ) { UpdateGeometry(); } if ( updateDerived != 0 ) { UpdateDerivedData( false, updateDerived ); } }
public void ResetBounds( Rectangle rect ) { if ( RectContainsNode( rect ) ) { this.mAABB.IsNull = true; this.mBoundingRadius = 0; if ( !IsLeaf ) { for ( int i = 0; i < 4; ++i ) { this.mChildren[ i ].ResetBounds( rect ); } } } }
public void SetNeighbour( NeighbourIndex index, Terrain neighbour, bool recalculate, bool notifyOther ) #endif { if ( this.mNeighbours[ (int)index ] != neighbour ) { System.Diagnostics.Debug.Assert( neighbour != this, "Can't set self as own neighbour!" ); // detach existing if ( this.mNeighbours[ (int)index ] != null && notifyOther ) { this.mNeighbours[ (int)index ].SetNeighbour( GetOppositeNeighbour( index ), null, false, false ); } this.mNeighbours[ (int)index ] = neighbour; if ( neighbour != null && notifyOther ) { this.mNeighbours[ (int)index ].SetNeighbour( GetOppositeNeighbour( index ), this, recalculate, false ); } if ( recalculate ) { //recalculate, pass OUR edge rect var edgerect = new Rectangle(); GetEdgeRect( index, 2, ref edgerect ); NeighbourModified( index, edgerect, edgerect ); } } }
public void NotifyNeighbours() { // There are 3 things that can need updating: // Height at edge - match to neighbour (first one to update 'loses' to other since read-only) // Normal at edge - use heights from across boundary too // Shadows across edge // The extent to which these can affect the current tile vary: // Height at edge - only affected by a change at the adjoining edge / corner // Normal at edge - only affected by a change to the 2 rows adjoining the edge / corner // Shadows across edge - possible effect extends based on the projection of the // neighbour AABB along the light direction (worst case scenario) if ( !this.mDirtyGeometryRectForNeighbours.IsNull ) { var dirtyRectForNeighbours = new Rectangle( this.mDirtyGeometryRectForNeighbours ); this.mDirtyGeometryRectForNeighbours.IsNull = true; // calculate light update rectangle var lightVec = TerrainGlobalOptions.LightMapDirection; var lightmapRect = new Rectangle(); WidenRectByVector( lightVec, dirtyRectForNeighbours, MinHeight, MaxHeight, ref lightmapRect ); for ( var i = 0; i < (int)NeighbourIndex.Count; ++i ) { var ni = (NeighbourIndex)( i ); var neighbour = GetNeighbour( ni ); if ( neighbour == null ) { continue; } // Intersect the incoming rectangles with the edge regions related to this neighbour var edgeRect = new Rectangle(); GetEdgeRect( ni, 2, ref edgeRect ); var heightEdgeRect = edgeRect.Intersect( dirtyRectForNeighbours ); var lightmapEdgeRect = edgeRect.Intersect( lightmapRect ); if ( !heightEdgeRect.IsNull || !lightmapRect.IsNull ) { // ok, we have something valid to pass on var neighbourHeightEdgeRect = new Rectangle(); var neighbourLightmapEdgeRect = new Rectangle(); if ( !heightEdgeRect.IsNull ) { GetNeighbourEdgeRect( ni, heightEdgeRect, ref neighbourHeightEdgeRect ); } if ( !lightmapRect.IsNull ) { GetNeighbourEdgeRect( ni, lightmapEdgeRect, ref neighbourLightmapEdgeRect ); } neighbour.NeighbourModified( GetOppositeNeighbour( ni ), neighbourHeightEdgeRect, neighbourLightmapEdgeRect ); } } } }
public void UpdateCompositeMap() { // All done in the render thread if ( this.mCompositeMapRequired && !this.mCompositeMapDirtyRect.IsNull ) { IsModified = true; CreateOrDestroyGPUCompositeMap(); if ( this.mCompositeMapDirtyRectLightmapUpdate && ( this.mCompositeMapDirtyRect.Width < this.mSize || this.mCompositeMapDirtyRect.Height < this.mSize ) ) { // widen the dirty rectangle since lighting makes it wider var widenedRect = new Rectangle(); WidenRectByVector( TerrainGlobalOptions.LightMapDirection, this.mCompositeMapDirtyRect, ref widenedRect ); // clamp widenedRect.Left = Utility.Max( widenedRect.Left, 0L ); widenedRect.Top = Utility.Max( widenedRect.Top, 0L ); widenedRect.Right = Utility.Min( widenedRect.Right, (long)this.mSize ); widenedRect.Bottom = Utility.Min( widenedRect.Bottom, (long)this.mSize ); this.mMaterialGenerator.UpdateCompositeMap( this, widenedRect ); } else { this.mMaterialGenerator.UpdateCompositeMap( this, this.mCompositeMapDirtyRect ); } this.mCompositeMapDirtyRectLightmapUpdate = false; this.mCompositeMapDirtyRect.IsNull = true; } }
public void FinalizeLightMap( Rectangle rect, PixelBox lightmapBox ) { CreateOrDestroyGPULightmap(); // deal with race condition where lm has been disabled while we were working! if ( LightMap != null ) { // blit the normals into the texture if ( rect.Left == 0 && rect.Top == 0 && rect.Bottom == this.mLightmapSizeActual && rect.Right == this.mLightmapSizeActual ) { LightMap.GetBuffer().BlitFromMemory( lightmapBox ); } else { // content of PixelBox is already inverted in Y, but rect is still // in terrain space for dealing with sub-rect, so invert var dstBox = new BasicBox(); dstBox.Left = (int)rect.Left; dstBox.Right = (int)rect.Right; dstBox.Top = (int)( this.mLightmapSizeActual - rect.Bottom ); dstBox.Bottom = (int)( this.mLightmapSizeActual - rect.Top ); LightMap.GetBuffer().BlitFromMemory( lightmapBox, dstBox ); } } // delete memory if ( lightmapBox.Data != null ) { lightmapBox.Data.Dispose(); lightmapBox.Data = null; } lightmapBox = null; }
public PixelBox CalculateLightMap( Rectangle rect, Rectangle extraTargetRect, ref Rectangle outFinalRect ) { // as well as calculating the lighting changes for the area that is // dirty, we also need to calculate the effect on casting shadow on // other areas. To do this, we project the dirt rect by the light direction // onto the minimum height var lightVec = TerrainGlobalOptions.LightMapDirection; var widenedRect = new Rectangle(); WidenRectByVector( lightVec, rect, ref widenedRect ); //merge in the extra area (e.g. from neighbours) widenedRect.Merge( extraTargetRect ); // widenedRect now contains terrain point space version of the area we // need to calculate. However, we need to calculate in lightmap image space var terrainToLightmapScale = (float)this.mLightmapSizeActual/(float)this.mSize; widenedRect.Left = (long)( widenedRect.Left*terrainToLightmapScale ); widenedRect.Right = (long)( widenedRect.Right*terrainToLightmapScale ); widenedRect.Top = (long)( widenedRect.Top*terrainToLightmapScale ); widenedRect.Bottom = (long)( widenedRect.Bottom*terrainToLightmapScale ); //clamp widenedRect.Left = Utility.Max( 0L, widenedRect.Left ); widenedRect.Top = Utility.Max( 0L, widenedRect.Top ); widenedRect.Right = Utility.Min( (long)this.mLightmapSizeActual, widenedRect.Right ); widenedRect.Bottom = Utility.Min( (long)this.mLightmapSizeActual, widenedRect.Bottom ); outFinalRect = widenedRect; // allocate memory (L8) var pData = new byte[widenedRect.Width*widenedRect.Height]; var pDataPtr = BufferBase.Wrap( pData ); var pixbox = new PixelBox( (int)widenedRect.Width, (int)widenedRect.Height, 1, PixelFormat.L8, pDataPtr ); var heightPad = ( MaxHeight - MinHeight )*1.0e-3f; for ( var y = widenedRect.Top; y < widenedRect.Bottom; ++y ) { for ( var x = widenedRect.Left; x < widenedRect.Right; ++x ) { var litVal = 1.0f; // convert to terrain space (not points, allow this to go between points) var Tx = (float)x/(float)( this.mLightmapSizeActual - 1 ); var Ty = (float)y/(float)( this.mLightmapSizeActual - 1 ); // get world space point // add a little height padding to stop shadowing self var wpos = Vector3.Zero; GetPosition( Tx, Ty, GetHeightAtTerrainPosition( Tx, Ty ) + heightPad, ref wpos ); wpos += Position; // build ray, cast backwards along light direction var ray = new Ray( wpos, -lightVec ); // Cascade into neighbours when casting, but don't travel further // than world size var rayHit = RayIntersects( ray, true, this.mWorldSize ); if ( rayHit.Key ) { litVal = 0.0f; } // encode as L8 // invert the Y to deal with image space var storeX = x - widenedRect.Left; var storeY = widenedRect.Bottom - y - 1; using ( var wrap = pDataPtr + ( ( storeY*widenedRect.Width ) + storeX ) ) { var pStore = (ITypePointer<byte>)wrap; pStore[ 0 ] = (byte)( litVal*255.0 ); } } } pDataPtr.Dispose(); return pixbox; }
public void WidenRectByVector( Vector3 vec, Rectangle inRect, Real minHeight, Real maxHeight, ref Rectangle outRect ) { outRect = inRect; var p = new Plane(); switch ( Alignment ) { case Alignment.Align_X_Y: p.Redefine( Vector3.UnitZ, new Vector3( 0, 0, vec.z < 0.0f ? minHeight : maxHeight ) ); break; case Alignment.Align_X_Z: p.Redefine( Vector3.UnitY, new Vector3( 0, vec.y < 0.0f ? minHeight : maxHeight, 0 ) ); break; case Alignment.Align_Y_Z: p.Redefine( Vector3.UnitX, new Vector3( vec.x < 0.0f ? minHeight : maxHeight, 0, 0 ) ); break; } var verticalVal = vec.Dot( p.Normal ); if ( Utility.RealEqual( verticalVal, 0.0f ) ) { return; } var corners = new Vector3[4]; var startHeight = verticalVal < 0.0f ? maxHeight : minHeight; GetPoint( inRect.Left, inRect.Top, startHeight, ref corners[ 0 ] ); GetPoint( inRect.Right - 1, inRect.Top, startHeight, ref corners[ 1 ] ); GetPoint( inRect.Left, inRect.Bottom - 1, startHeight, ref corners[ 2 ] ); GetPoint( inRect.Right - 1, inRect.Bottom - 1, startHeight, ref corners[ 3 ] ); for ( int i = 0; i < 4; ++i ) { var ray = new Ray( corners[ i ] + this.mPos, vec ); var rayHit = ray.Intersects( p ); if ( rayHit.Hit ) { var pt = ray.GetPoint( rayHit.Distance ); // convert back to terrain point var terrainHitPos = Vector3.Zero; GetTerrainPosition( pt, ref terrainHitPos ); // build rectangle which has rounded down & rounded up values // remember right & bottom are exclusive var mergeRect = new Rectangle( (long)terrainHitPos.x*( this.mSize - 1 ), (long)terrainHitPos.y*( this.mSize - 1 ), (long)( terrainHitPos.x*(float)( this.mSize - 1 ) + 0.5f ) + 1, (long)( terrainHitPos.y*(float)( this.mSize - 1 ) + 0.5f ) + 1 ); outRect.Merge( mergeRect ); } } }
public void GetEdgeRect( NeighbourIndex index, long range, ref Rectangle outRect ) { // We make the edge rectangle 2 rows / columns at the edge of the tile // 2 because this copes with normal changes and potentially filtered // shadows. // all right / bottom values are exclusive // terrain origin is bottom-left remember so north is highest value // set left/right switch ( index ) { case NeighbourIndex.East: case NeighbourIndex.NorthEast: case NeighbourIndex.SouthEast: outRect.Left = this.mSize - range; outRect.Right = this.mSize; break; case NeighbourIndex.West: case NeighbourIndex.NorthWest: case NeighbourIndex.SouthWest: outRect.Left = 0; outRect.Right = range; break; case NeighbourIndex.North: case NeighbourIndex.South: outRect.Left = 0; outRect.Right = this.mSize; break; case NeighbourIndex.Count: default: break; } ; // set top / bottom switch ( index ) { case NeighbourIndex.North: case NeighbourIndex.NorthEast: case NeighbourIndex.NorthWest: outRect.Top = this.mSize - range; outRect.Bottom = this.mSize; break; case NeighbourIndex.South: case NeighbourIndex.SouthWest: case NeighbourIndex.SouthEast: outRect.Top = 0; outRect.Bottom = range; break; case NeighbourIndex.East: case NeighbourIndex.West: outRect.Top = 0; outRect.Bottom = this.mSize; break; case NeighbourIndex.Count: default: break; } ; }
public void UpdateVertexData( bool positions, bool deltas, Rectangle rect, bool cpuData ) { if ( rect.Left <= this.mBoundaryX || rect.Right > this.mOffsetX || rect.Top <= this.mBoundaryY || rect.Bottom > this.mOffsetY ) { // Do we have vertex data? if ( this.mVertexDataRecord != null ) { // Trim to our bounds var updateRect = new Rectangle( this.mOffsetX, this.mOffsetY, this.mBoundaryX, this.mBoundaryY ); updateRect.Left = System.Math.Max( updateRect.Left, rect.Left ); updateRect.Right = System.Math.Min( updateRect.Right, rect.Right ); updateRect.Top = System.Math.Max( updateRect.Top, rect.Top ); updateRect.Bottom = System.Math.Min( updateRect.Bottom, rect.Bottom ); // update the GPU buffer directly #warning TODO: do we have no use for CPU vertex data after initial load? // if so, destroy it to free RAM, this should be fast enough to // to direct HardwareVertexBuffer posbuf = null, deltabuf = null; var targetData = cpuData ? this.mVertexDataRecord.CpuVertexData : this.mVertexDataRecord.GpuVertexData; if ( positions ) { posbuf = targetData.vertexBufferBinding.GetBuffer( POSITION_BUFFER ); } if ( deltas ) { deltabuf = targetData.vertexBufferBinding.GetBuffer( DELTA_BUFFER ); } UpdateVertexBuffer( posbuf, deltabuf, updateRect ); } // pass on to children if ( !IsLeaf ) { for ( int i = 0; i < 4; ++i ) { this.mChildren[ i ].UpdateVertexData( positions, deltas, rect, cpuData ); // merge bounds from children var childBox = new AxisAlignedBox( this.mChildren[ i ].AABB ); // this box is relative to child centre var boxoffset = this.mChildren[ i ].LocalCentre - LocalCentre; childBox.Minimum = childBox.Minimum + boxoffset; childBox.Maximum = childBox.Maximum + boxoffset; this.mAABB.Merge( childBox ); } } // Make sure node knows to update if ( this.mMovable != null && this.mMovable.IsAttached ) { this.mMovable.ParentSceneNode.NeedUpdate(); } } }
public void GetNeighbourEdgeRect( NeighbourIndex index, Rectangle inRect, ref Rectangle outRect ) { System.Diagnostics.Debug.Assert( this.mSize == GetNeighbour( index ).Size, "Neighbour has not the same size as this instance" ); // Basically just reflect the rect // remember index is neighbour relationship from OUR perspective so // arrangement is backwards to getEdgeRect // left/right switch ( index ) { case NeighbourIndex.East: case NeighbourIndex.NorthEast: case NeighbourIndex.SouthEast: case NeighbourIndex.West: case NeighbourIndex.NorthWest: case NeighbourIndex.SouthWest: outRect.Left = this.mSize - inRect.Right; outRect.Right = this.mSize - inRect.Left; break; default: outRect.Left = inRect.Left; outRect.Right = inRect.Right; break; } ; // top / bottom switch ( index ) { case NeighbourIndex.North: case NeighbourIndex.NorthEast: case NeighbourIndex.NorthWest: case NeighbourIndex.South: case NeighbourIndex.SouthWest: case NeighbourIndex.SouthEast: outRect.Top = this.mSize - inRect.Bottom; outRect.Bottom = this.mSize - inRect.Top; break; default: outRect.Top = inRect.Top; outRect.Bottom = inRect.Bottom; break; } ; }
public bool RectContainsNode( Rectangle rect ) { return ( rect.Left <= this.mOffsetX && rect.Right > this.mBoundaryX && rect.Top <= this.mOffsetY && rect.Bottom > this.mBoundaryY ); }
public void HandleResponse( WorkQueue.Response res, WorkQueue srcq ) { // Main thread var ddres = (DerivedDataResponse)res.Data; var ddreq = (DerivedDataRequest)res.Request.Data; // only deal with own requests if ( ddreq.Terrain != this ) { return; } if ( ( ( ddreq.TypeMask & DERIVED_DATA_DELTAS ) == ddreq.TypeMask ) && ( ( ddres.RemainingTypeMask & DERIVED_DATA_DELTAS ) != DERIVED_DATA_DELTAS ) ) { FinalizeHeightDeltas( ddres.DeltaUpdateRect, false ); } if ( ( ( ddreq.TypeMask & DERIVED_DATA_NORMALS ) == ddreq.TypeMask ) && ( ( ddres.RemainingTypeMask & DERIVED_DATA_NORMALS ) != DERIVED_DATA_NORMALS ) ) { FinalizeNormals( ddres.NormalUpdateRect, ddres.NormalMapBox ); this.mCompositeMapDirtyRect.Merge( ddreq.DirtyRect ); } if ( ( ( ddreq.TypeMask & DERIVED_DATA_LIGHTMAP ) == ddreq.TypeMask ) && ( ( ddres.RemainingTypeMask & DERIVED_DATA_LIGHTMAP ) != DERIVED_DATA_LIGHTMAP ) ) { FinalizeLightMap( ddres.LightMapUpdateRect, ddres.LightMapPixelBox ); this.mCompositeMapDirtyRect.Merge( ddreq.DirtyRect ); this.mCompositeMapDirtyRectLightmapUpdate = true; } IsDerivedDataUpdateInProgress = false; // Re-trigger another request if there are still things to do, or if // we had a new request since this one var newRect = new Rectangle( 0, 0, 0, 0 ); if ( ddres.RemainingTypeMask != 0 ) { newRect.Merge( ddreq.DirtyRect ); } if ( this.mDerivedUpdatePendingMask != 0 ) { newRect.Merge( this.mDirtyDerivedDataRect ); this.mDirtyDerivedDataRect.IsNull = true; } var newLightmapExtraRext = new Rectangle( 0, 0, 0, 0 ); if ( ddres.RemainingTypeMask != 0 ) { newLightmapExtraRext.Merge( ddreq.LightmapExtraDirtyRect ); } if ( this.mDerivedUpdatePendingMask != 0 ) { newLightmapExtraRext.Merge( this.mDirtyLightmapFromNeighboursRect ); this.mDirtyLightmapFromNeighboursRect.IsNull = true; } var newMask = (byte)( ddres.RemainingTypeMask | this.mDerivedUpdatePendingMask ); if ( newMask != 0 ) { // trigger again UpdateDerivedDataImpl( newRect, newLightmapExtraRext, false, newMask ); } else { // we've finished all the background processes // update the composite map if enabled if ( this.mCompositeMapRequired ) { UpdateCompositeMap(); } } }
protected void UpdateVertexBuffer( HardwareVertexBuffer posBuf, HardwareVertexBuffer deltaBuf, Rectangle rect ) { Debug.Assert( rect.Left >= this.mOffsetX && rect.Right <= this.mBoundaryX && rect.Top >= this.mOffsetY && rect.Bottom <= this.mBoundaryY ); // potentially reset our bounds depending on coverage of the update ResetBounds( rect ); //main data var inc = (ushort)( ( this.mTerrain.Size - 1 )/( this.mVertexDataRecord.Resolution - 1 ) ); long destOffsetX = rect.Left <= this.mOffsetX ? 0 : ( rect.Left - this.mOffsetX )/inc; long destOffsetY = rect.Top <= this.mOffsetY ? 0 : ( rect.Top - this.mOffsetY )/inc; // Fill the buffers BufferLocking lockmode; if ( destOffsetX != 0 || destOffsetY != 0 || rect.Width < this.mSize || rect.Height < this.mSize ) { lockmode = BufferLocking.Normal; } else { lockmode = BufferLocking.Discard; } Real uvScale = 1.0f/( this.mTerrain.Size - 1 ); var pBaseHeight = this.mTerrain.GetHeightData( rect.Left, rect.Top ); var pBaseDelta = this.mTerrain.GetDeltaData( rect.Left, rect.Top ); var rowskip = (ushort)( this.mTerrain.Size*inc ); ushort destPosRowSkip = 0, destDeltaRowSkip = 0; BufferBase pRootPosBuf = null; BufferBase pRootDeltaBuf = null; BufferBase pRowPosBuf = null; BufferBase pRowDeltaBuf = null; if ( posBuf != null ) { destPosRowSkip = (ushort)( this.mVertexDataRecord.Size*posBuf.VertexSize ); pRootPosBuf = posBuf.Lock( lockmode ); pRowPosBuf = pRootPosBuf; // skip dest buffer in by left/top pRowPosBuf += destOffsetY*destPosRowSkip + destOffsetX*posBuf.VertexSize; } if ( deltaBuf != null ) { destDeltaRowSkip = (ushort)( this.mVertexDataRecord.Size*deltaBuf.VertexSize ); pRootDeltaBuf = deltaBuf.Lock( lockmode ); pRowDeltaBuf = pRootDeltaBuf; // skip dest buffer in by left/top pRowDeltaBuf += destOffsetY*destDeltaRowSkip + destOffsetX*deltaBuf.VertexSize; } Vector3 pos = Vector3.Zero; for ( var y = (ushort)rect.Top; y < rect.Bottom; y += inc ) { #if !AXIOM_SAFE_ONLY unsafe #endif { var pHeight = pBaseHeight.ToFloatPointer(); var pHIdx = 0; var pDelta = pBaseDelta.ToFloatPointer(); var pDeltaIdx = 0; var pPosBuf = pRowPosBuf != null ? pRowPosBuf.ToFloatPointer() : null; var pPosBufIdx = 0; var pDeltaBuf = pRowDeltaBuf != null ? pRowDeltaBuf.ToFloatPointer() : null; var pDeltaBufIdx = 0; for ( var x = (ushort)rect.Left; x < rect.Right; x += inc ) { if ( pPosBuf != null ) { this.mTerrain.GetPoint( x, y, pHeight[ pHIdx ], ref pos ); // Update bounds *before* making relative MergeIntoBounds( x, y, pos ); // relative to local centre pos -= this.mLocalCentre; pHIdx += inc; pPosBuf[ pPosBufIdx++ ] = pos.x; pPosBuf[ pPosBufIdx++ ] = pos.y; pPosBuf[ pPosBufIdx++ ] = pos.z; // UVs - base UVs vary from 0 to 1, all other values // will be derived using scalings pPosBuf[ pPosBufIdx++ ] = x*uvScale; pPosBuf[ pPosBufIdx++ ] = 1.0f - ( y*uvScale ); } if ( pDeltaBuf != null ) { //delta pDeltaBuf[ pDeltaBufIdx++ ] = pDelta[ pDeltaIdx ]; pDeltaIdx += inc; // delta LOD threshold // we want delta to apply to LODs no higher than this value // at runtime this will be combined with a per-renderable parameter // to ensure we only apply morph to the correct LOD pDeltaBuf[ pDeltaBufIdx++ ] = (float)this.mTerrain.GetLODLevelWhenVertexEliminated( x, y ) - 1.0f; } } // end unsafe } //end for pBaseHeight += rowskip; pBaseDelta += rowskip; if ( pRowPosBuf != null ) { pRowPosBuf += destPosRowSkip; } if ( pRowDeltaBuf != null ) { pRowDeltaBuf += destDeltaRowSkip; } } //end for // Skirts now // skirt spacing based on top-level resolution (* inc to cope with resolution which is not the max) var skirtSpacing = (ushort)( this.mVertexDataRecord.SkirtRowColSkip*inc ); var skirtOffset = Vector3.Zero; this.mTerrain.GetVector( 0, 0, -this.mTerrain.SkirtSize, ref skirtOffset ); // skirt rows // clamp rows to skirt spacing (round up) var skirtStartX = rect.Left; var skirtStartY = rect.Top; // for rows, clamp Y to skirt frequency, X to inc (LOD resolution vs top) if ( skirtStartY%skirtSpacing != 0 ) { skirtStartY += skirtSpacing - ( skirtStartY%skirtSpacing ); } if ( skirtStartX%inc != 0 ) { skirtStartX += inc - ( skirtStartX%inc ); } skirtStartY = System.Math.Max( skirtStartY, (long)this.mOffsetY ); pBaseHeight = this.mTerrain.GetHeightData( skirtStartX, skirtStartY ); if ( posBuf != null ) { // position dest buffer just after the main vertex data pRowPosBuf = pRootPosBuf + posBuf.VertexSize*this.mVertexDataRecord.Size*this.mVertexDataRecord.Size; // move it onwards to skip the skirts we don't need to update pRowPosBuf += destPosRowSkip*( ( skirtStartY - this.mOffsetY )/skirtSpacing ); pRowPosBuf += posBuf.VertexSize*( skirtStartX - this.mOffsetX )/inc; } if ( deltaBuf != null ) { // position dest buffer just after the main vertex data pRowDeltaBuf = pRootDeltaBuf + deltaBuf.VertexSize*this.mVertexDataRecord.Size*this.mVertexDataRecord.Size; // move it onwards to skip the skirts we don't need to update pRowDeltaBuf += destDeltaRowSkip*( skirtStartY - this.mOffsetY )/skirtSpacing; pRowDeltaBuf += deltaBuf.VertexSize*( skirtStartX - this.mOffsetX )/inc; } for ( var y = (ushort)skirtStartY; y < (ushort)rect.Bottom; y += skirtSpacing ) { #if !AXIOM_SAFE_ONLY unsafe #endif { var pHeight = pBaseHeight.ToFloatPointer(); var pHIdx = 0; var pPosBuf = pRowPosBuf != null ? pRowPosBuf.ToFloatPointer() : null; var pPosIdx = 0; var pDeltaBuf = pRowDeltaBuf != null ? pRowDeltaBuf.ToFloatPointer() : null; var pDeltaIdx = 0; for ( var x = (ushort)skirtStartX; x < (ushort)rect.Right; x += inc ) { if ( pPosBuf != null ) { this.mTerrain.GetPoint( x, y, pHeight[ pHIdx ], ref pos ); // relative to local centre pos -= this.mLocalCentre; pHIdx += inc; pos += skirtOffset; pPosBuf[ pPosIdx++ ] = pos.x; pPosBuf[ pPosIdx++ ] = pos.y; pPosBuf[ pPosIdx++ ] = pos.z; // UVs - same as base pPosBuf[ pPosIdx++ ] = x*uvScale; pPosBuf[ pPosIdx++ ] = 1.0f - ( y*uvScale ); } if ( pDeltaBuf != null ) { // delta (none) pDeltaBuf[ pDeltaIdx++ ] = 0; // delta threshold (irrelevant) pDeltaBuf[ pDeltaIdx++ ] = 99; } } //end for pBaseHeight += this.mTerrain.Size*skirtSpacing; if ( pRowPosBuf != null ) { pRowPosBuf += destPosRowSkip; } if ( pRowDeltaBuf != null ) { pRowDeltaBuf += destDeltaRowSkip; } } // end unsafe } //end for // skirt cols // clamp cols to skirt spacing (round up) skirtStartX = rect.Left; if ( skirtStartX%skirtSpacing != 0 ) { skirtStartX += skirtSpacing - ( skirtStartX%skirtSpacing ); } // clamp Y to inc (LOD resolution vs top) skirtStartY = rect.Top; if ( skirtStartY%inc != 0 ) { skirtStartY += inc - ( skirtStartY%inc ); } skirtStartX = System.Math.Max( skirtStartX, (long)this.mOffsetX ); if ( posBuf != null ) { // position dest buffer just after the main vertex data and skirt rows pRowPosBuf = pRootPosBuf + posBuf.VertexSize*this.mVertexDataRecord.Size*this.mVertexDataRecord.Size; // skip the row skirts pRowPosBuf += this.mVertexDataRecord.NumSkirtRowsCols*this.mVertexDataRecord.Size*posBuf.VertexSize; // move it onwards to skip the skirts we don't need to update pRowPosBuf += destPosRowSkip*( skirtStartX - this.mOffsetX )/skirtSpacing; pRowPosBuf += posBuf.VertexSize*( skirtStartY - this.mOffsetY )/inc; } if ( deltaBuf != null ) { // Deltaition dest buffer just after the main vertex data and skirt rows pRowDeltaBuf = pRootDeltaBuf + deltaBuf.VertexSize*this.mVertexDataRecord.Size*this.mVertexDataRecord.Size; // skip the row skirts pRowDeltaBuf += this.mVertexDataRecord.NumSkirtRowsCols*this.mVertexDataRecord.Size*deltaBuf.VertexSize; // move it onwards to skip the skirts we don't need to update pRowDeltaBuf += destDeltaRowSkip*( skirtStartX - this.mOffsetX )/skirtSpacing; pRowDeltaBuf += deltaBuf.VertexSize*( skirtStartY - this.mOffsetY )/inc; } for ( var x = (ushort)skirtStartX; x < (ushort)rect.Right; x += skirtSpacing ) { #if !AXIOM_SAFE_ONLY unsafe #endif { var pPosBuf = pRowPosBuf != null ? pRowPosBuf.ToFloatPointer() : null; var pPosIdx = 0; var pDeltaBuf = pRowDeltaBuf != null ? pRowDeltaBuf.ToFloatPointer() : null; var pDeltaIdx = 0; for ( var y = (ushort)skirtStartY; y < (ushort)rect.Bottom; y += inc ) { if ( pPosBuf != null ) { this.mTerrain.GetPoint( x, y, this.mTerrain.GetHeightAtPoint( x, y ), ref pos ); // relative to local centre pos -= this.mLocalCentre; pos += skirtOffset; pPosBuf[ pPosIdx++ ] = pos.x; pPosBuf[ pPosIdx++ ] = pos.y; pPosBuf[ pPosIdx++ ] = pos.z; // UVs - same as base pPosBuf[ pPosIdx++ ] = x*uvScale; pPosBuf[ pPosIdx++ ] = 1.0f - ( y*uvScale ); } if ( pDeltaBuf != null ) { // delta (none) pDeltaBuf[ pDeltaIdx++ ] = 0; // delta threshold (irrelevant) pDeltaBuf[ pDeltaIdx++ ] = 99; } } //end for if ( pRowPosBuf != null ) { pRowPosBuf += destPosRowSkip; } if ( pRowDeltaBuf != null ) { pRowDeltaBuf += destDeltaRowSkip; } } // end unsafe } //end for if ( posBuf != null ) { posBuf.Unlock(); } if ( deltaBuf != null ) { deltaBuf.Unlock(); } }
public static System.Drawing.Rectangle ToRectangle( Rectangle rect ) { return new System.Drawing.Rectangle( new System.Drawing.Point( (int)rect.Left, (int)rect.Top ), new System.Drawing.Size( (int)rect.Width, (int)rect.Height ) ); }
public void Prepare( StreamSerializer stream ) { // load LOD data we need for ( int i = 0; i < this.mLodLevels.Count; ++i ) { var ll = this.mLodLevels[ i ]; // only read 'calc' and then copy to final (separation is only for // real-time calculation // Basically this is what finaliseHeightDeltas does in calc path stream.Read( out ll.CalcMaxHeightDelta ); ll.MaxHeightDelta = ll.CalcMaxHeightDelta; ll.LastCFactor = 0; } if ( !IsLeaf ) { for ( int i = 0; i < 4; ++i ) { this.mChildren[ i ].Prepare( stream ); } } // If this is the root, do the post delta calc to finish if ( this.mParent == null ) { var rect = new Rectangle(); rect.Top = this.mOffsetY; rect.Bottom = this.mBoundaryY; rect.Left = this.mOffsetX; rect.Right = this.mBoundaryX; PostDeltaCalculation( rect ); } }
/// <summary> /// Update the composite map for a terrain /// </summary> /// <param name="terrain"></param> /// <param name="rect"></param> public virtual void UpdateCompositeMap( Terrain terrain, Rectangle rect ) { // convert point-space rect into image space int compSize = terrain.CompositeMap.Width; var imgRect = new Rectangle(); Vector3 inVec = Vector3.Zero, outVec = Vector3.Zero; inVec.x = rect.Left; inVec.y = rect.Bottom - 1; // this is 'top' in image space, also make inclusive terrain.ConvertPosition( Space.PointSpace, inVec, Space.TerrainSpace, ref outVec ); float left = ( outVec.x*compSize ); float top = ( ( 1.0f - outVec.y )*compSize ); ; imgRect.Left = (long)left; imgRect.Top = (long)top; inVec.x = rect.Right - 1; inVec.y = rect.Top; // this is 'bottom' in image space terrain.ConvertPosition( Space.PointSpace, inVec, Space.TerrainSpace, ref outVec ); float right = ( outVec.x*(float)compSize + 1 ); imgRect.Right = (long)right; float bottom = ( ( 1.0f - outVec.y )*compSize + 1 ); imgRect.Bottom = (long)bottom; imgRect.Left = System.Math.Max( 0L, imgRect.Left ); imgRect.Top = System.Math.Max( 0L, imgRect.Top ); imgRect.Right = System.Math.Min( (long)compSize, imgRect.Right ); imgRect.Bottom = System.Math.Min( (long)compSize, imgRect.Bottom ); #warning enable rendercompositemap #if false mParent.RenderCompositeMap(compSize, imgRect, terrain.CompositeMapMaterial, terrain.CompositeMap); #endif update = true; }
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 ); } } } }
/// <summary> /// Update the composite map for a terrain. /// The composite map for a terrain must match what the terrain should look like /// at distance. This method will only be called in the render thread so the /// generator is free to render into a texture to support this, so long as /// the results are blitted into the Terrain's own composite map afterwards. /// </summary> /// <param name="terrain"></param> /// <param name="rect"></param> public virtual void UpdateCompositeMap( Terrain terrain, Rectangle rect ) { Profile p = ActiveProfile; if ( p == null ) { return; } else { p.UpdateCompositeMap( terrain, rect ); } }
public Rectangle( Rectangle copy ) { _left = copy._left; _top = copy._top; _right = copy._right; _bottom = copy._bottom; }
/// <summary> /// Helper method to render a composite map. /// </summary> /// <param name="size"> The requested composite map size</param> /// <param name="rect"> The region of the composite map to update, in image space</param> /// <param name="mat">The material to use to render the map</param> /// <param name="destCompositeMap"></param> public virtual void RenderCompositeMap( int size, Rectangle rect, Material mat, Texture destCompositeMap ) { //return; if ( this.mCompositeMapSM == null ) { //dedicated SceneManager this.mCompositeMapSM = Root.Instance.CreateSceneManager( SceneType.ExteriorClose, "TerrainMaterialGenerator_SceneManager" ); float camDist = 100; float halfCamDist = camDist*0.5f; this.mCompositeMapCam = this.mCompositeMapSM.CreateCamera( "TerrainMaterialGenerator_Camera" ); this.mCompositeMapCam.Position = new Vector3( 0, 0, camDist ); //mCompositeMapCam.LookAt(Vector3.Zero); this.mCompositeMapCam.ProjectionType = Projection.Orthographic; this.mCompositeMapCam.Near = 10; this.mCompositeMapCam.Far = 999999*3; //mCompositeMapCam.AspectRatio = camDist / camDist; this.mCompositeMapCam.SetOrthoWindow( camDist, camDist ); // Just in case material relies on light auto params this.mCompositeMapLight = this.mCompositeMapSM.CreateLight( "TerrainMaterialGenerator_Light" ); this.mCompositeMapLight.Type = LightType.Directional; RenderSystem rSys = Root.Instance.RenderSystem; float hOffset = rSys.HorizontalTexelOffset/(float)size; float vOffset = rSys.VerticalTexelOffset/(float)size; //setup scene this.mCompositeMapPlane = this.mCompositeMapSM.CreateManualObject( "TerrainMaterialGenerator_ManualObject" ); this.mCompositeMapPlane.Begin( mat.Name, OperationType.TriangleList ); this.mCompositeMapPlane.Position( -halfCamDist, halfCamDist, 0 ); this.mCompositeMapPlane.TextureCoord( 0 - hOffset, 0 - vOffset ); this.mCompositeMapPlane.Position( -halfCamDist, -halfCamDist, 0 ); this.mCompositeMapPlane.TextureCoord( 0 - hOffset, 1 - vOffset ); this.mCompositeMapPlane.Position( halfCamDist, -halfCamDist, 0 ); this.mCompositeMapPlane.TextureCoord( 1 - hOffset, 1 - vOffset ); this.mCompositeMapPlane.Position( halfCamDist, halfCamDist, 0 ); this.mCompositeMapPlane.TextureCoord( 1 - hOffset, 0 - vOffset ); this.mCompositeMapPlane.Quad( 0, 1, 2, 3 ); this.mCompositeMapPlane.End(); this.mCompositeMapSM.RootSceneNode.AttachObject( this.mCompositeMapPlane ); } //end if // update this.mCompositeMapPlane.SetMaterialName( 0, mat.Name ); this.mCompositeMapLight.Direction = TerrainGlobalOptions.LightMapDirection; this.mCompositeMapLight.Diffuse = TerrainGlobalOptions.CompositeMapDiffuse; this.mCompositeMapSM.AmbientLight = TerrainGlobalOptions.CompositeMapAmbient; //check for size change (allow smaller to be reused) if ( this.mCompositeMapRTT != null && size != this.mCompositeMapRTT.Width ) { TextureManager.Instance.Remove( this.mCompositeMapRTT ); this.mCompositeMapRTT = null; } if ( this.mCompositeMapRTT == null ) { this.mCompositeMapRTT = TextureManager.Instance.CreateManual( this.mCompositeMapSM.Name + "/compRTT", ResourceGroupManager.DefaultResourceGroupName, TextureType.TwoD, size, size, 0, PixelFormat.BYTE_RGBA, TextureUsage.RenderTarget ); RenderTarget rtt = this.mCompositeMapRTT.GetBuffer().GetRenderTarget(); // don't render all the time, only on demand rtt.IsAutoUpdated = false; Viewport vp = rtt.AddViewport( this.mCompositeMapCam ); // don't render overlays vp.ShowOverlays = false; } // calculate the area we need to update float vpleft = (float)rect.Left/(float)size; float vptop = (float)rect.Top/(float)size; float vpright = (float)rect.Right/(float)size; float vpbottom = (float)rect.Bottom/(float)size; float vpwidth = (float)rect.Width/(float)size; float vpheight = (float)rect.Height/(float)size; RenderTarget rtt2 = this.mCompositeMapRTT.GetBuffer().GetRenderTarget(); Viewport vp2 = rtt2.GetViewport( 0 ); this.mCompositeMapCam.SetWindow( vpleft, vptop, vpright, vpbottom ); rtt2.Update(); vp2.Update(); // We have an RTT, we want to copy the results into a regular texture // That's because in non-update scenarios we don't want to keep an RTT // around. We use a single RTT to serve all terrain pages which is more // efficient. var box = new BasicBox( (int)rect.Left, (int)rect.Top, (int)rect.Right, (int)rect.Bottom ); destCompositeMap.GetBuffer().Blit( this.mCompositeMapRTT.GetBuffer(), box, box ); }
public Rectangle Merge( Rectangle rhs ) { if ( Width == 0 ) { this = rhs; } else { Left = System.Math.Min( Left, rhs.Left ); Right = System.Math.Max( Right, rhs.Right ); Top = System.Math.Min( Top, rhs.Top ); Bottom = System.Math.Max( Bottom, rhs.Bottom ); } return this; }
public override void CopyContentsToMemory(PixelBox dst, FrameBuffer buffer) { if ((dst.Left < 0) || (dst.Right > Width) || (dst.Top < 0) || (dst.Bottom > Height) || (dst.Front != 0) || (dst.Back != 1)) { throw new Exception("Invalid box."); } var device = Driver.XnaDevice; //in 3.1, this was ResolveTexture2D, an actual RenderTarget provides the exact same //functionality, especially seeing as RenderTarget2D is a texture now. //the difference is surface is actually set on the device -DoubleA RenderTarget2D surface; var data = new byte[dst.ConsecutiveSize]; var pitch = 0; if (buffer == FrameBuffer.Auto) { buffer = FrameBuffer.Front; } #if SILVERLIGHT var mode = ((XnaRenderSystem)Root.Instance.RenderSystem).DisplayMode; surface = new RenderTarget2D(device, mode.Width, mode.Height, false, SurfaceFormat.Color, DepthFormat.Depth24Stencil8); #else var mode = device.DisplayMode; surface = new RenderTarget2D(device, mode.Width, mode.Height, false, SurfaceFormat.Rgba64, DepthFormat.Depth24Stencil8); #endif //ResolveTexture2D( device, mode.Width, mode.Height, 0, SurfaceFormat.Rgba32 ); #if !SILVERLIGHT if (buffer == FrameBuffer.Front) { // get the entire front buffer. This is SLOW!! device.SetRenderTarget(surface); if (IsFullScreen) { if ((dst.Left == 0) && (dst.Right == Width) && (dst.Top == 0) && (dst.Bottom == Height)) { surface.GetData(data); } else { var rect = new Rectangle(); rect.Left = dst.Left; rect.Right = dst.Right; rect.Top = dst.Top; rect.Bottom = dst.Bottom; surface.GetData(0, XnaHelper.ToRectangle(rect), data, 0, 255); } } #if !(XBOX || XBOX360 || WINDOWS_PHONE) else { var srcRect = new Rectangle(); srcRect.Left = dst.Left; srcRect.Right = dst.Right; srcRect.Top = dst.Top; srcRect.Bottom = dst.Bottom; // Adjust Rectangle for Window Menu and Chrome var point = new Point(); point.X = (int)srcRect.Left; point.Y = (int)srcRect.Top; var control = Control.FromHandle(_windowHandle); point = control.PointToScreen(point); srcRect.Top = (long)point.Y; srcRect.Left = (long)point.X; srcRect.Bottom += (long)point.Y; srcRect.Right += (long)point.X; surface.GetData(0, XnaHelper.ToRectangle(srcRect), data, 0, 255); } #endif } else { device.SetRenderTarget(surface); if ((dst.Left == 0) && (dst.Right == Width) && (dst.Top == 0) && (dst.Bottom == Height)) { surface.GetData(data); } else { var rect = new Rectangle(); rect.Left = dst.Left; rect.Right = dst.Right; rect.Top = dst.Top; rect.Bottom = dst.Bottom; surface.GetData(0, XnaHelper.ToRectangle(rect), data, 0, 255); } } #endif var format = XnaHelper.Convert(surface.Format); if (format == PixelFormat.Unknown) { throw new Exception("Unsupported format"); } var dataPtr = Memory.PinObject(data); var src = new PixelBox(dst.Width, dst.Height, 1, format, dataPtr); src.RowPitch = pitch / PixelUtil.GetNumElemBytes(format); src.SlicePitch = surface.Height * src.RowPitch; PixelConverter.BulkPixelConversion(src, dst); Memory.UnpinObject(data); surface.Dispose(); }
/// <summary> /// Updates this elements transform based on it's parent. /// </summary> public virtual void UpdateFromParent() { float parentLeft, parentTop, parentBottom, parentRight; parentLeft = parentTop = parentBottom = parentRight = 0; if ( parent != null ) { parentLeft = parent.DerivedLeft; parentTop = parent.DerivedTop; // derive right position if ( horzAlign == HorizontalAlignment.Center || horzAlign == HorizontalAlignment.Right ) { parentRight = parentLeft + parent.width; } // derive bottom position if ( vertAlign == VerticalAlignment.Center || vertAlign == VerticalAlignment.Bottom ) { parentBottom = parentTop + parent.height; } } else { // with no real parent, the "parent" is actually the full viewport size // parentLeft = parentTop = 0.0f; // parentRight = parentBottom = 1.0f; RenderSystem rSys = Root.Instance.RenderSystem; OverlayManager oMgr = OverlayManager.Instance; // Calculate offsets required for mapping texel origins to pixel origins in the // current rendersystem float hOffset = rSys.HorizontalTexelOffset / oMgr.ViewportWidth; float vOffset = rSys.VerticalTexelOffset / oMgr.ViewportHeight; parentLeft = 0.0f + hOffset; parentTop = 0.0f + vOffset; parentRight = 1.0f + hOffset; parentBottom = 1.0f + vOffset; } // sort out position based on alignment // all we do is derived the origin, we don't automatically sort out the position // This is more flexible than forcing absolute right & middle switch ( horzAlign ) { case HorizontalAlignment.Center: derivedLeft = ( ( parentLeft + parentRight ) * 0.5f ) + left; break; case HorizontalAlignment.Left: derivedLeft = parentLeft + left; break; case HorizontalAlignment.Right: derivedLeft = parentRight + left; break; } switch ( vertAlign ) { case VerticalAlignment.Center: derivedTop = ( ( parentTop + parentBottom ) * 0.5f ) + top; break; case VerticalAlignment.Top: derivedTop = parentTop + top; break; case VerticalAlignment.Bottom: derivedTop = parentBottom + top; break; } isDerivedOutOfDate = false; if ( parent != null ) { Rectangle parentRect; parentRect = parent.ClippingRegion; Rectangle childRect = new Rectangle( (long)derivedLeft, (long)derivedTop, (long)( derivedLeft + width ), (long)( derivedTop + height ) ); this.clippingRegion = Rectangle.Intersect( parentRect, childRect ); } else { clippingRegion = new Rectangle( (long)derivedLeft, (long)derivedTop, (long)( derivedLeft + width ), (long)( derivedTop + height ) ); } }
public void WidenRectByVector( Vector3 vec, Rectangle inRect, ref Rectangle outRect ) { WidenRectByVector( vec, inRect, MinHeight, MaxHeight, ref outRect ); }