//this is faster on the cpu than the gpu so don't bother with the compute shader here //cityCount - no of cities to start with, cityMultiplier - how much cityCount increases by with each layer, cityDepth - how many layers //citySpread - radius of city, cityIntensity - max value of city, cityFalloff - how quickly cityIntensity drops public void generateCityNoise( int width, int height, int cityCount, int cityMultiplier, int cityDropoff, int cityDepth, float citySpread, float cityIntensity, float maxIntensity, float cityFalloff ) { _width = width; _height = height; _cityMaxIntensity = maxIntensity; _cityFalloff = cityFalloff; _diffuseStartIndexC = width * height * 8; int pixelCount = width * height; cityData = new float[ pixelCount ]; citySpread *= height * 0.01f; float startCitySpread = citySpread; float invCityDropoff = 1f / ( float ) cityDropoff; if( cityCount <= 0 ) return; //set up array for city positions int count = cityCount; for( int i = 0; i < cityDepth; ++i ) count *= cityMultiplier; Vector2[,] cityPos = new Vector2[ 2, count ]; Vector2[] startCityPos = new Vector2[ cityCount ]; int cityIndexOld = 0; int cityIndexNew = 1; cityPos[ cityIndexOld, 0 ] = new Vector2( Mathf.RoundToInt( width / 2f ), Mathf.RoundToInt( height / 2f ) ); _citySplatList = new List< CitySplatData >(); //set up offsets for positions int xOffset = width; int yOffset = height; float angle = 360f / ( float ) cityMultiplier; for( int d = 0; d < cityDepth; ++d ) { for( int i = 0; i < cityCount; ++i ) { int index = Mathf.FloorToInt( i / ( float ) cityMultiplier ); Vector2 pos = cityPos[ cityIndexOld, index ]; int x = ( _cityRnd.Next( xOffset ) - Mathf.RoundToInt( xOffset * 0.5f ) ) + Mathf.RoundToInt( pos.x ); int y = ( _cityRnd.Next( yOffset ) - Mathf.RoundToInt( yOffset * 0.5f ) ) + Mathf.RoundToInt( pos.y ); if( d > 0 ) { float a = Mathf.Deg2Rad * ( angle * i + ( _cityRnd.Next( 80 ) - 40 ) ); float diff = ( 0.5f - getSphericalDistortion( y ) ) * 2f; if( diff <= 0f ) diff = 1f; float xDiff = Mathf.Min( _cityRnd.Next( Mathf.RoundToInt( xOffset ) ) / diff, width ); float yDiff = _cityRnd.Next( Mathf.RoundToInt( yOffset ) ); x = Mathf.RoundToInt( Mathf.Cos( a ) * ( xOffset * 0.25f + xDiff ) + pos.x ); y = Mathf.RoundToInt( Mathf.Sin( a ) * ( yOffset * 0.25f + yDiff ) + pos.y ); } while( x < 0 || x >= _width ) x = ( x + _width ) % _width; while( y < 0 || y >= _height ) y = ( y + _height ) % _height; float spread = Mathf.Min( citySpread + _cityRnd.Next( cityDropoff ), startCitySpread ); CitySplatData citySplat = new CitySplatData(); citySplat.x = x; citySplat.y = y; citySplat.spread = spread; citySplat.intensity = cityIntensity; _citySplatList.Add( citySplat ); cityPos[ cityIndexNew, i ] = new Vector2( x, y ); if( d == 0 ) startCityPos[ i ] = new Vector2( x, y ); } if( d < cityDepth - 1 ) { cityCount *= cityMultiplier; citySpread *= invCityDropoff * 2f; cityIntensity = cityIntensity * invCityDropoff * 2f; cityIndexOld = ++cityIndexOld & 1; cityIndexNew = ++cityIndexNew & 1; xOffset = Mathf.RoundToInt( citySpread ) * cityMultiplier * 2; yOffset = xOffset; } } cityCount = startCityPos.Length * cityMultiplier; float startCityPosLength = startCityPos.Length / ( float ) cityCount; for( int i = 0; i < cityCount; ++i ) { int index = Mathf.FloorToInt( i * startCityPosLength ); Vector2 pos = startCityPos[ index ]; float a = Mathf.Deg2Rad * ( angle * i + ( _cityRnd.Next( 80 ) - 40 ) ); int x = Mathf.RoundToInt( Mathf.Cos( a ) * ( startCitySpread + _cityRnd.Next( Mathf.RoundToInt( startCitySpread * 0.25f ) ) ) + pos.x ); int y = Mathf.RoundToInt( Mathf.Sin( a ) * ( startCitySpread + _cityRnd.Next( Mathf.RoundToInt( startCitySpread * 0.25f ) ) ) + pos.y ); while( x < 0 || x >= _width ) x = ( x + _width ) % _width; while( y < 0 || y >= _height ) y = ( y + _height ) % _height; float spread = Mathf.Min( citySpread + _cityRnd.Next( Mathf.RoundToInt( cityDropoff * 2 ) ), startCitySpread ); CitySplatData citySplat = new CitySplatData(); citySplat.x = x; citySplat.y = y; citySplat.spread = spread; citySplat.intensity = cityIntensity; _citySplatList.Add( citySplat ); } int workerCount = SystemInfo.processorCount - 1; //-1 to account for main thread if( workerCount > 0 ) _worker = new Thread[ workerCount ]; //a bit inefficient to have more jobs than workers but with multiple threads //per core nowadays it makes sense, also gives an easy way to measure progress _tileCountX = ( workerCount + 1 ) * 2; //this gives us the no. of horizontal tiles _tileCountX = Mathf.Min( width / 16, _tileCountX ); //this means the tiles can't be smaller than 16 pixels wide _tileCountY = _tileCountX / 2; //this gives us square tiles _jobCount = _tileCountX * _tileCountY; _jobStartedCount = 0; _jobCompletedCount = 0; _jobList = new int[ _jobCount ]; for( int i = 0; i < _jobCount; ++i ) _jobList[ i ] = i; for( int i = 0; i < workerCount; ++i ) { _worker[ i ] = new Thread( () => { while( doCityWork() ); } ); _worker[ i ].Start(); } isDoingCityWork = true; }
private void cityNoiseWork( int jobId, int jobCount ) { int tileSize = ( _width / _tileCountX ); CityTileRect tileRect = new CityTileRect(); tileRect.minx = ( jobId % _tileCountX ) * tileSize; tileRect.maxx = tileRect.minx + tileSize; tileRect.miny = ( jobId / _tileCountX ) * tileSize; tileRect.maxy = tileRect.miny + tileSize; #if true for( int i = 0; i < _citySplatList.Count; ++i ) { CitySplatData citySplat = _citySplatList[ i ]; int intSpread = Mathf.RoundToInt( citySplat.spread ); float spreadTop = ( 0.5f - getSphericalDistortion( citySplat.y - intSpread ) ) * 2f; float spreadBottom = ( 0.5f - getSphericalDistortion( citySplat.y + intSpread ) ) * 2f; float spreadDistortion; if( spreadTop <= 0f || spreadBottom <= 0f ) spreadDistortion = _width * 0.5f; else spreadDistortion = Mathf.Min( citySplat.spread / Mathf.Min( spreadTop, spreadBottom ), _width * 0.5f ); int intSpreadDistortion = Mathf.RoundToInt( spreadDistortion ); float expFalloff = 1f / Mathf.Exp( _cityFalloff ); float invSpread = 1f / citySplat.spread; int minx = Mathf.Max( tileRect.minx, citySplat.x - intSpreadDistortion ); int maxx = Mathf.Min( tileRect.maxx, citySplat.x + intSpreadDistortion + 1 ); int miny = Mathf.Max( tileRect.miny, citySplat.y - intSpread ); int maxy = Mathf.Min( tileRect.maxy, citySplat.y + intSpread + 1 ); float referenceWidth = 8192f; float referenceHeight = referenceWidth * 0.5f; for( int a = minx; a < maxx; ++a ) { float iDistortion = ( ( citySplat.x - a ) / spreadDistortion ) * citySplat.spread + citySplat.x; float xi = ( citySplat.x - iDistortion ) * ( citySplat.x - iDistortion ); xi = ( xi / _width ) * referenceWidth; for( int b = miny; b < maxy; ++b ) { float xj = ( citySplat.y - b ) * ( citySplat.y - b ); xj = ( xj / _height ) * referenceHeight; float dist = Mathf.Max( citySplat.spread - Mathf.Sqrt( xi + xj ), 0f ) * invSpread; int index = getIndex( a, b ); float diffuseMultiplier = diffuseData[ _diffuseStartIndexC + index ] / 255f; float val = cityData[ index ] / diffuseMultiplier; val += ( ( citySplat.intensity * dist ) * expFalloff ) * dist; cityData[ index ] = Mathf.Min( val, _cityMaxIntensity ) * diffuseMultiplier; } } } #endif }