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 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 ); } } } }