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(); } } }
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 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 Rectangle CalculateHeightDeltas( Rectangle rect ) { var clampedRect = new Rectangle( rect ); clampedRect.Left = Utility.Max( 0L, clampedRect.Left ); clampedRect.Top = Utility.Max( 0L, clampedRect.Top ); clampedRect.Right = Utility.Min( (long)this.mSize, clampedRect.Right ); clampedRect.Bottom = Utility.Min( (long)this.mSize, clampedRect.Bottom ); var finalRect = new Rectangle( clampedRect ); QuadTree.PreDeltaCalculation( clampedRect ); // Iterate over target levels, for ( var targetLevel = 1; targetLevel < NumLodLevels; ++targetLevel ) { var sourceLevel = targetLevel - 1; var step = 1 << targetLevel; // need to widen the dirty rectangle since change will affect surrounding // vertices at lower LOD var widendRect = rect; widendRect.Left = Utility.Max( 0L, widendRect.Left - step ); widendRect.Top = Utility.Max( 0L, widendRect.Top - step ); widendRect.Right = Utility.Min( (long)this.mSize, widendRect.Right + step ); widendRect.Bottom = Utility.Min( (long)this.mSize, widendRect.Bottom + step ); // keep a merge of the widest finalRect = finalRect.Merge( widendRect ); // now round the rectangle at this level so that it starts & ends on // the step boundaries var lodRect = new Rectangle( widendRect ); lodRect.Left -= lodRect.Left%step; lodRect.Top -= lodRect.Top%step; if ( lodRect.Right%step != 0 ) { lodRect.Right += step - ( lodRect.Right%step ); } if ( lodRect.Bottom%step != 0 ) { lodRect.Bottom += step - ( lodRect.Bottom%step ); } for ( var j = (int)lodRect.Top; j < lodRect.Bottom - step; j += step ) { for ( var i = (int)lodRect.Left; i < lodRect.Right - step; i += step ) { // Form planes relating to the lower detail tris to be produced // For even tri strip rows, they are this shape: // 2---3 // | / | // 0---1 // For odd tri strip rows, they are this shape: // 2---3 // | \ | // 0---1 var v0 = Vector3.Zero; var v1 = Vector3.Zero; var v2 = Vector3.Zero; var v3 = Vector3.Zero; GetPointAlign( i, j, Alignment.Align_X_Y, ref v0 ); GetPointAlign( i + step, j, Alignment.Align_X_Y, ref v1 ); GetPointAlign( i, j + step, Alignment.Align_X_Y, ref v2 ); GetPointAlign( i + step, j + step, Alignment.Align_X_Y, ref v3 ); var t1 = new Plane(); var t2 = new Plane(); var backwardTri = false; // Odd or even in terms of target level if ( ( j/step )%2 == 0 ) { t1.Redefine( v0, v1, v3 ); t2.Redefine( v0, v3, v2 ); } else { t1.Redefine( v1, v3, v2 ); t2.Redefine( v0, v1, v2 ); backwardTri = true; } //include the bottommost row of vertices if this is the last row var yubound = ( j == ( this.mSize - step ) ? step : step - 1 ); for ( var y = 0; y <= yubound; y++ ) { // include the rightmost col of vertices if this is the last col var xubound = ( i == ( this.mSize - step ) ? step : step - 1 ); for ( var x = 0; x <= xubound; x++ ) { var fulldetailx = i + x; var fulldetaily = j + y; if ( fulldetailx%step == 0 && fulldetaily%step == 0 ) { // Skip, this one is a vertex at this level continue; } var ypct = (Real)y/(Real)step; var xpct = (Real)x/(Real)step; //interpolated height var actualPos = Vector3.Zero; GetPointAlign( fulldetailx, fulldetaily, Alignment.Align_X_Y, ref actualPos ); Real interp_h = 0; // Determine which tri we're on if ( ( xpct > ypct && !backwardTri ) || ( xpct > ( 1 - ypct ) && backwardTri ) ) { // Solve for x/z interp_h = ( -t1.Normal.x*actualPos.x - t1.Normal.y*actualPos.y - t1.D )/t1.Normal.z; } else { // Second tri interp_h = ( -t2.Normal.x*actualPos.x - t2.Normal.y*actualPos.y - t2.D )/t2.Normal.z; } var actual_h = actualPos.z; var delta = interp_h - actual_h; // max(delta) is the worst case scenario at this LOD // compared to the original heightmap if ( delta == float.NaN ) { } // tell the quadtree about this QuadTree.NotifyDelta( (ushort)fulldetailx, (ushort)fulldetaily, (ushort)sourceLevel, delta ); // If this vertex is being removed at this LOD, // then save the height difference since that's the move // it will need to make. Vertices to be removed at this LOD // are halfway between the steps, but exclude those that // would have been eliminated at earlier levels int halfStep = step/2; if ( ( ( fulldetailx%step ) == halfStep && ( fulldetaily%halfStep ) == 0 ) || ( ( fulldetaily%step ) == halfStep && ( fulldetailx%halfStep ) == 0 ) ) { #if !AXIOM_SAFE_ONLY unsafe #endif { // Save height difference var pDest = GetDeltaData( fulldetailx, fulldetaily ).ToFloatPointer(); pDest[ 0 ] = delta; } } } //x } //y } } //j } //targetlevel QuadTree.PostDeltaCalculation( clampedRect ); return finalRect; }