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