/// <summary> /// Calculate the square root of a complex number /// </summary> /// <param name="c"></param> /// <returns></returns> static public ComplexF Sqrt( ComplexF c ) { double x = c.Re; double y = c.Im; double modulus = Math.Sqrt( x*x + y*y ); int sign = ( y < 0 ) ? -1 : 1; c.Re = (float)( _halfOfRoot2 * Math.Sqrt( modulus + x ) ); c.Im = (float)( _halfOfRoot2 * sign * Math.Sqrt( modulus - x ) ); return c; }

//--------------------------------------------------------------------------------------------------- /// <summary> /// Calculate the power of a complex number /// </summary> /// <param name="c"></param> /// <param name="exponent"></param> /// <returns></returns> public static ComplexF Pow( ComplexF c, double exponent ) { double x = c.Re; double y = c.Im; double modulus = Math.Pow( x*x + y*y, exponent * 0.5 ); double argument = Math.Atan2( y, x ) * exponent; c.Re = (float)( modulus * System.Math.Cos( argument ) ); c.Im = (float)( modulus * System.Math.Sin( argument ) ); return c; }

static private ComplexF SumRecursion( ComplexF[] data, int start, int end ) { Debug.Assert( 0 <= start, "start = " + start ); Debug.Assert( start < end, "start = " + start + " and end = " + end ); Debug.Assert( end <= data.Length, "end = " + end + " and data.Length = " + data.Length ); if( ( end - start ) <= 1000 ) { ComplexF sum = ComplexF.Zero; for( int i = start; i < end; i ++ ) { sum += data[ i ]; } return sum; } else { int middle = ( start + end ) >> 1; return SumRecursion( data, start, middle ) + SumRecursion( data, middle, end ); } }

void InitWaveGenerator() { // Wind restricted to one direction, reduces calculations Vector2 wind = new Vector2 (windx, 0.0f); // Initialize wave generator for (int y=0; y<height; y++) { for (int x=0; x<width; x++) { float yc = y < height / 2f ? y : -height + y; float xc = x < width / 2f ? x : -width + x; Vector2 vec_k = new Vector2 (2.0f * Mathf.PI * xc / size.x, 2.0f * Mathf.PI * yc / size.z); h0 [width * y + x] = new ComplexF (GaussianRnd (), GaussianRnd ()) * 0.707f * (float)System.Math.Sqrt (P_spectrum (vec_k, wind)); } } for (int y=0; y<n_height; y++) { for (int x=0; x<n_width; x++) { float yc = y < n_height / 2f ? y : -n_height + y; float xc = x < n_width / 2f ? x : -n_width + x; Vector2 vec_k = new Vector2 (2.0f * Mathf.PI * xc / (size.x / normal_scale), 2.0f * Mathf.PI * yc / (size.z / normal_scale)); n0 [n_width * y + x] = new ComplexF (GaussianRnd (), GaussianRnd ()) * 0.707f * (float)System.Math.Sqrt (P_spectrum (vec_k, wind)); } } }

/// <summary> /// Invert each element in the array /// </summary> /// <param name="array"></param> static public void Invert( ComplexF[] array ) { for( int i = 0; i < array.Length; i ++ ) { array[i] = ((ComplexF) 1 ) / array[i]; } }

/// <summary> /// Copy an array /// </summary> /// <param name="dest"></param> /// <param name="source"></param> static public void Copy( ComplexF[] dest, ComplexF[] source ) { Debug.Assert( dest != null ); Debug.Assert( source != null ); Debug.Assert( dest.Length == source.Length ); for( int i = 0; i < dest.Length; i ++ ) { dest[i] = source[i]; } }

/// <summary> /// Divide each element in target array with corresponding element in rhs array /// </summary> /// <param name="target"></param> /// <param name="rhs"></param> static public void Divide( ComplexF[] target, ComplexF[] rhs ) { ComplexArray.Divide( target, rhs, target ); }

/// <summary> /// Multiply each element in target array with corresponding element in rhs array /// </summary> /// <param name="target"></param> /// <param name="rhs"></param> static public void Multiply( ComplexF[] target, ComplexF[] rhs ) { ComplexArray.Multiply( target, rhs, target ); }

/// <summary> /// Multiply each element in the array by a specific value /// </summary> /// <param name="array"></param> /// <param name="scale"></param> static public void Scale( ComplexF[] array, ComplexF scale ) { Debug.Assert( array != null ); int length = array.Length; for( int i = 0; i < length; i ++ ) { array[i] *= scale; } }

static private void LockBufferCF( int length, ref ComplexF[] buffer ) { if( length != _bufferCF.Length ) { _bufferCF = new ComplexF[ length ]; } buffer = _bufferCF; }

//------------------------------------------------------------------------------------- static private void ReorderArray( ComplexF[] data ) { int length = data.Length; int[] reversedBits = GetReversedBits( Log2( length ) ); for( int i = 0; i < length; i ++ ) { int swap = reversedBits[ i ]; if( swap > i ) { ComplexF temp = data[ i ]; data[ i ] = data[ swap ]; data[ swap ] = temp; } } }

/// <summary> /// Swap two complex numbers /// </summary> /// <param name="a"></param> /// <param name="b"></param> static public void Swap( ref ComplexF a, ref ComplexF b ) { ComplexF temp = a; a = b; b = temp; }

/// <summary> /// Compute a 1D fast Fourier transform of a dataset of complex numbers. /// </summary> /// <param name="data"></param> /// <param name="direction"></param> public static void FFT( ComplexF[] data, FourierDirection direction ) { if( data == null ) { throw new ArgumentNullException( "data" ); } Fourier.FFT( data, data.Length, direction ); }

/// <summary> /// Compute a 1D fast Fourier transform of a dataset of complex numbers. /// </summary> /// <param name="data"></param> /// <param name="length"></param> /// <param name="direction"></param> public static void FFT_Quick( ComplexF[] data, int length, FourierDirection direction ) { /*if( data == null ) { throw new ArgumentNullException( "data" ); } if( data.Length < length ) { throw new ArgumentOutOfRangeException( "length", length, "must be at least as large as 'data.Length' parameter" ); } if( Fourier.IsPowerOf2( length ) == false ) { throw new ArgumentOutOfRangeException( "length", length, "must be a power of 2" ); } Fourier.SyncLookupTableLength( length );*/ int ln = Fourier.Log2( length ); // reorder array Fourier.ReorderArray( data ); // successive doubling int N = 1; int signIndex = ( direction == FourierDirection.Forward ) ? 0 : 1; for( int level = 1; level <= ln; level ++ ) { int M = N; N <<= 1; float[] uRLookup = _uRLookupF[ level, signIndex ]; float[] uILookup = _uILookupF[ level, signIndex ]; for( int j = 0; j < M; j ++ ) { float uR = uRLookup[j]; float uI = uILookup[j]; for( int even = j; even < length; even += N ) { int odd = even + M; float r = data[ odd ].Re; float i = data[ odd ].Im; float odduR = r * uR - i * uI; float odduI = r * uI + i * uR; r = data[ even ].Re; i = data[ even ].Im; data[ even ].Re = r + odduR; data[ even ].Im = i + odduI; data[ odd ].Re = r - odduR; data[ odd ].Im = i - odduI; } } } }

static private void Swap( ref ComplexF a, ref ComplexF b ) { ComplexF temp = a; a = b; b = temp; }

/// <summary> /// Determine whether the elements in the two arrays are the same /// </summary> /// <param name="array1"></param> /// <param name="array2"></param> /// <param name="tolerance"></param> /// <returns></returns> static public bool IsEqual( ComplexF[] array1, ComplexF[] array2, float tolerance ) { if ( array1.Length != array2.Length ) { return false; } for( int i = 0; i < array1.Length; i ++ ) { if( ComplexF.IsEqual( array1[i], array2[i], tolerance ) == false ) { return false; } } return true; }

/// <summary> /// Add a specific value to each element in the array /// </summary> /// <param name="array"></param> /// <param name="offset"></param> static public void Offset( ComplexF[] array, ComplexF offset ) { int length = array.Length; for( int i = 0; i < length; i ++ ) { array[i] += offset; } }

static private void UnlockBufferCF( ref ComplexF[] buffer ) { buffer = null; }

/// <summary> /// Multiply each element in the array by a specific value /// </summary> /// <param name="array"></param> /// <param name="scale"></param> /// <param name="start"></param> /// <param name="length"></param> static public void Scale( ComplexF[] array, ComplexF scale, int start, int length ) { Debug.Assert( array != null ); Debug.Assert( start >= 0 ); Debug.Assert( length >= 0 ); Debug.Assert( ( start + length ) < array.Length ); for( int i = 0; i < length; i ++ ) { array[i + start] *= scale; } }

private static void LinearFFT_Quick( ComplexF[] data, int start, int inc, int length, FourierDirection direction ) { // copy to buffer ComplexF[] buffer = null; LockBufferCF( length, ref buffer ); int j = start; for( int i = 0; i < length; i ++ ) { buffer[ i ] = data[ j ]; j += inc; } FFT( buffer, length, direction ); // copy from buffer j = start; for( int i = 0; i < length; i ++ ) { data[ j ] = buffer[ i ]; j += inc; } UnlockBufferCF( ref buffer ); }

/// <summary> /// Multiply each element in lhs array with corresponding element in rhs array and /// put product in result array /// </summary> /// <param name="lhs"></param> /// <param name="rhs"></param> /// <param name="result"></param> static public void Multiply( ComplexF[] lhs, ComplexF[] rhs, ComplexF[] result ) { Debug.Assert( lhs != null ); Debug.Assert( rhs != null ); Debug.Assert( result != null ); Debug.Assert( lhs.Length == rhs.Length ); Debug.Assert( lhs.Length == result.Length ); int length = lhs.Length; for( int i = 0; i < length; i ++ ) { result[i] = lhs[i] * rhs[i]; } }

//====================================================================================== /// <summary> /// Compute a 1D fast Fourier transform of a dataset of complex numbers. /// </summary> /// <param name="data"></param> /// <param name="length"></param> /// <param name="direction"></param> public static void FFT( ComplexF[] data, int length, FourierDirection direction ) { SyncLookupTableLength( length ); int ln = Log2( length ); // reorder array ReorderArray( data ); // successive doubling int N = 1; int signIndex = ( direction == FourierDirection.Forward ) ? 0 : 1; for( int level = 1; level <= ln; level ++ ) { int M = N; N <<= 1; float[] uRLookup = _uRLookupF[ level, signIndex ]; float[] uILookup = _uILookupF[ level, signIndex ]; for( int j = 0; j < M; j ++ ) { float uR = uRLookup[j]; float uI = uILookup[j]; for( int even = j; even < length; even += N ) { int odd = even + M; float r = data[ odd ].Re; float i = data[ odd ].Im; float odduR = r * uR - i * uI; float odduI = r * uI + i * uR; r = data[ even ].Re; i = data[ even ].Im; data[ even ].Re = r + odduR; data[ even ].Im = i + odduI; data[ odd ].Re = r - odduR; data[ odd ].Im = i - odduI; } } } }

/// <summary> /// Divide each element in lhs array with corresponding element in rhs array and /// put product in result array /// </summary> /// <param name="lhs"></param> /// <param name="rhs"></param> /// <param name="result"></param> static public void Divide( ComplexF[] lhs, ComplexF[] rhs, ComplexF[] result ) { Debug.Assert( lhs != null ); Debug.Assert( rhs != null ); Debug.Assert( result != null ); Debug.Assert( lhs.Length == rhs.Length ); Debug.Assert( lhs.Length == result.Length ); ComplexF zero = ComplexF.Zero; int length = lhs.Length; for( int i = 0; i < length; i ++ ) { if( rhs[i] != zero ) { result[i] = lhs[i] / rhs[i]; } else { result[i] = zero; } } }

/// <summary> /// Compute a 2D fast fourier transform on a data set of complex numbers /// </summary> /// <param name="data"></param> /// <param name="xLength"></param> /// <param name="yLength"></param> /// <param name="direction"></param> public static void FFT2( ComplexF[] data, int xLength, int yLength, FourierDirection direction ) { int xInc = 1; int yInc = xLength; if( xLength > 1 ) { SyncLookupTableLength( xLength ); for( int y = 0; y < yLength; y ++ ) { int xStart = y * yInc; LinearFFT_Quick( data, xStart, xInc, xLength, direction ); } } if( yLength > 1 ) { SyncLookupTableLength( yLength ); for( int x = 0; x < xLength; x ++ ) { int yStart = x * xInc; LinearFFT_Quick( data, yStart, yInc, yLength, direction ); } } }

/// <summary> /// Scale and offset the elements in the array so that the /// overall range is [0, 1] /// </summary> /// <param name="array"></param> static public void Normalize( ComplexF[] array ) { float min = 0, max = 0; GetLengthRange( array, ref min, ref max ); Scale( array, ( 1 / ( max - min ) ) ); Offset( array, ( - min / ( max - min ) ) ); }

static private void UnlockWorkspaceF( ref ComplexF[] workspace ) { Debug.Assert( _workspaceF == workspace ); //Debug.Assert( _workspaceFLocked == true ); //_workspaceFLocked = false; workspace = null; }

static private void LockWorkspaceF( int length, ref ComplexF[] workspace ) { //Debug.Assert( _workspaceFLocked == false ); //_workspaceFLocked = true; if( length >= _workspaceF.Length ) { _workspaceF = new ComplexF[ length ]; } workspace = _workspaceF; }

/// <summary> /// Shift (offset) the elements in the array /// </summary> /// <param name="array"></param> /// <param name="offset"></param> static public void Shift( ComplexF[] array, int offset ) { Debug.Assert( array != null ); Debug.Assert( offset >= 0 ); Debug.Assert( offset < array.Length ); if( offset == 0 ) { return; } int length = array.Length; ComplexF[] workspace = null; ComplexArray.LockWorkspaceF( length, ref workspace ); for( int i = 0; i < length; i ++ ) { workspace[ ( i + offset ) % length ] = array[ i ]; } for( int i = 0; i < length; i ++ ) { array[ i ] = workspace[ i ]; } ComplexArray.UnlockWorkspaceF( ref workspace ); }

void Update() { //If player null, search for player by Player tag if(player == null) player = GameObject.FindGameObjectWithTag("Player").GetComponent<Transform>(); //Get sun reflection dir from sun object if(sun != null){ SunDir = sun.transform.forward; material.SetVector ("_SunDir", SunDir); } if (this.renderReflection) RenderObject (); if (followMainCamera) { Vector3 centerOffset; //centerOffset.x = Mathf.Floor (player.position.x / size.x) * size.x; //centerOffset.z = Mathf.Floor (player.position.z / size.z) * size.z; centerOffset.y = transform.position.y; //centerOffset.x = (((int)player.position.x + 64) & ~127); //centerOffset.z = (((int)player.position.z + 64) & ~127); //Optimized ocean tiles movement centerOffset.x = Mathf.Floor( (player.position.x + size.x * 0.5f) * sizeInv.x ) * size.x; centerOffset.z = Mathf.Floor( (player.position.z + size.z * 0.5f) * sizeInv.y ) * size.z; if(transform.position != centerOffset) transform.position = centerOffset; } float hhalf=height/2f; float whalf=width/2f; float time=Time.time; for (int y = 0; y<height; y++) { for (int x = 0; x<width; x++) { int idx = width * y + x; float yc = y < hhalf ? y : -height + y; float xc = x < whalf ? x : -width + x; Vector2 vec_k = new Vector2 (2.0f * Mathf.PI * xc / size.x, 2.0f * Mathf.PI * yc / size.z); float sqrtMagnitude=(float)System.Math.Sqrt((vec_k.x * vec_k.x) + (vec_k.y * vec_k.y)); float iwkt = (float)System.Math.Sqrt(9.81f * sqrtMagnitude) * time * speed; ComplexF coeffA = new ComplexF ((float)System.Math.Cos(iwkt), (float)System.Math.Sin(iwkt)); ComplexF coeffB; coeffB.Re = coeffA.Re; coeffB.Im = -coeffA.Im; int ny = y > 0 ? height - y : 0; int nx = x > 0 ? width - x : 0; data [idx] = h0 [idx] * coeffA + h0[width * ny + nx].GetConjugate() * coeffB; t_x [idx] = data [idx] * new ComplexF (0.0f, vec_k.x) - data [idx] * vec_k.y; // Choppy wave calculations if (x + y > 0) data [idx] += data [idx] * vec_k.x / sqrtMagnitude; } } Fourier.FFT2 (data, width, height, FourierDirection.Backward); Fourier.FFT2 (t_x, width, height, FourierDirection.Backward); // Get base values for vertices and uv coordinates. if (baseHeight == null) { baseHeight = baseMesh.vertices; vertices = new Vector3[baseHeight.Length]; normals = new Vector3[baseHeight.Length]; tangents = new Vector4[baseHeight.Length]; } int wh=width*height; float scaleA = choppy_scale / wh; float scaleB = scale / wh; float scaleBinv = 1.0f / scaleB; for (int i=0; i<wh; i++) { int iw = i + i / width; vertices [iw] = baseHeight [iw]; vertices [iw].x += data [i].Im * scaleA; vertices [iw].y = data [i].Re * scaleB; normals [iw] = Vector3.Normalize(new Vector3 (t_x [i].Re, scaleBinv, t_x [i].Im)); if (((i + 1) % width)==0) { int iwi=iw+1; int iwidth=i+1-width; vertices [iwi] = baseHeight [iwi]; vertices [iwi].x += data [iwidth].Im * scaleA; vertices [iwi].y = data [iwidth].Re * scaleB; normals [iwi] = Vector3.Normalize(new Vector3 (t_x [iwidth].Re, scaleBinv, t_x [iwidth].Im)); } } int offset = g_width * (g_height - 1); for (int i=0; i<g_width; i++) { int io=i+offset; int mod=i % width; vertices [io] = baseHeight [io]; vertices [io].x += data [mod].Im * scaleA; vertices [io].y = data [mod].Re * scaleB; normals [io] = Vector3.Normalize(new Vector3 (t_x [mod].Re, scaleBinv, t_x [mod].Im)); } int gwgh=g_width*g_height-1; for (int i=0; i<gwgh; i++) { //Need to preserve w in refraction/reflection mode if (!reflectionRefractionEnabled) { if (((i + 1) % g_width) == 0) { tangents [i] = Vector3.Normalize((vertices [i - width + 1] + new Vector3 (size.x, 0.0f, 0.0f) - vertices [i])); } else { tangents [i] = Vector3.Normalize((vertices [i + 1] - vertices [i])); } tangents [i].w = 1.0f; } else { Vector3 tmp;// = Vector3.zero; if (((i + 1) % g_width) == 0) { tmp = Vector3.Normalize(vertices[i - width + 1] + new Vector3 (size.x, 0.0f, 0.0f) - vertices [i]); } else { tmp = Vector3.Normalize(vertices [i + 1] - vertices [i]); } tangents [i] = new Vector4 (tmp.x, tmp.y, tmp.z, tangents [i].w); } } //Vector3 playerRelPos = player.position - transform.position; //In reflection mode, use tangent w for foam strength if (reflectionRefractionEnabled) { for (int y = 0; y < g_height; y++) { for (int x = 0; x < g_width; x++) { int item=x + g_width * y; if (x + 1 >= g_width) { tangents [item].w = tangents [g_width * y].w; continue; } if (y + 1 >= g_height) { tangents [item].w = tangents [x].w; continue; } float right = vertices[(x + 1) + g_width * y].x - vertices[item].x; float foam = right/(size.x / g_width); if (foam < 0.0f) tangents [item].w = 1f; else if (foam < 0.5f) tangents [item].w += 3.0f * Time.deltaTime; else tangents [item].w -= 0.4f * Time.deltaTime; if (player != null ) { Vector3 player2Vertex = (player.position - vertices[item] - transform.position); // foam around boat if (player2Vertex.x >= size.x) player2Vertex.x -= size.x; if (player2Vertex.x<= -size.x) player2Vertex.x += size.x; if (player2Vertex.z >= size.z) player2Vertex.z -= size.z; if (player2Vertex.z<= -size.z) player2Vertex.z += size.z; player2Vertex.y = 0; if (player2Vertex.sqrMagnitude < wakeDistance * wakeDistance) tangents[item].w += 3.0f * Time.deltaTime; } tangents [item].w = Mathf.Clamp (tangents[item].w, 0.0f, 2.0f); } } } tangents [gwgh] = Vector4.Normalize(vertices [gwgh] + new Vector3 (size.x, 0.0f, 0.0f) - vertices [1]); for (int L0D=0; L0D<max_LOD; L0D++) { int den = (int)System.Math.Pow (2f, L0D); int itemcount = (int)((height / den + 1) * (width / den + 1)); Vector4[] tangentsLOD = new Vector4[itemcount]; Vector3[] verticesLOD = new Vector3[itemcount]; Vector3[] normalsLOD = new Vector3[itemcount]; int idx = 0; for (int y=0; y<g_height; y+=den) { for (int x=0; x<g_width; x+=den) { int idx2 = g_width * y + x; verticesLOD [idx] = vertices [idx2]; tangentsLOD [idx] = tangents [idx2]; normalsLOD [idx++] = normals [idx2]; } } for (int k=0; k< tiles_LOD[L0D].Count; k++) { Mesh meshLOD = tiles_LOD [L0D][k]; meshLOD.vertices = verticesLOD; meshLOD.normals = normalsLOD; meshLOD.tangents = tangentsLOD; } } }

/// <summary> /// Get the range of element lengths /// </summary> /// <param name="array"></param> /// <param name="minimum"></param> /// <param name="maximum"></param> static public void GetLengthRange( ComplexF[] array, ref float minimum, ref float maximum ) { minimum = +float.MaxValue; maximum = -float.MaxValue; for( int i = 0; i < array.Length; i ++ ) { float temp = array[i].GetModulus(); minimum = Math.Min( temp, minimum ); maximum = Math.Max( temp, maximum ); } }