/// <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 ); } }
/// <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> /// 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> /// 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 ); }
public static Complex ToSysComplex(ComplexF d) { return(new Complex(d.x, d.y)); }
private static void mulComplex(ref ComplexF sourceA, ref ComplexF sourceB, ref ComplexF destination) { destination.Re = sourceA.Re * sourceB.Re; destination.Im = sourceA.Im * sourceB.Im; }
/// <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> /// 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]; } }
public CUBLASStatusv2 cublasCdotc(cublasHandle handle, int n, IntPtr x, int incx, IntPtr y, int incy, ref ComplexF result) { return(cublasCdotc_v2(handle, n, x, incx, y, incy, ref result)); }
public CUBLASStatusv2 cublasCrot(cublasHandle handle, int n, IntPtr x, int incx, IntPtr y, int incy, ref float c, ref ComplexF s) { return(cublasCrot_v2(handle, n, x, incx, y, incy, ref c, ref s)); }
private static extern CUBLASStatusv2 cublasCrotg_v2(cublasHandle handle, ref ComplexF a, ref ComplexF b, ref float c, ref ComplexF s);
public CUBLASStatusv2 cublasCaxpy(cublasHandle handle, int n, ref ComplexF alpha, IntPtr x, int incx, IntPtr y, int incy) { return(cublasCaxpy_v2(handle, n, ref alpha, x, incx, y, incy)); }
private static extern CUBLASStatusv2 cublasCrot_v2(cublasHandle handle, int n, IntPtr x, int incx, IntPtr y, int incy, ref float c, ref ComplexF s);
private static extern CUBLASStatusv2 cublasCdotc_v2(cublasHandle handle, int n, IntPtr x, int incx, IntPtr y, int incy, ref ComplexF result);
private static extern CUBLASStatusv2 cublasCaxpy_v2(cublasHandle handle, int n, ref ComplexF alpha, IntPtr x, int incx, IntPtr y, int incy);
/// <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; } }
public CUBLASStatusv2 cublasCrotg(cublasHandle handle, ref ComplexF a, ref ComplexF b, ref float c, ref ComplexF s) { return(cublasCrotg_v2(handle, ref a, ref b, ref c, ref s)); }
/// <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 ); }
private static void convertComplexToMagnitude(ref ComplexF source, ref Gray <float> destination) { destination.Intensity = source.Magnitude(); }
/// <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]; } }
static private void LockBufferCF( int length, ref ComplexF[] buffer ) { if( length != _bufferCF.Length ) { _bufferCF = new ComplexF[ length ]; } buffer = _bufferCF; }
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)); } } }
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> /// 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; }
private static void convertGrayToComplex(ref Gray <float> source, ref ComplexF destination) { destination.Re = source.Intensity; }
//------------------------------------------------------------------------------------- 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; } } }
static void Main(string[] args) { bool showUsage = true; if (args.Length >= 3) { try { // todo: generalize port parsing and throw new exceptions int inputPort = int.Parse(args[0]); int amplitudeOutputPort = int.Parse(args[1]); int phaseOutputPort = int.Parse(args[2]); if (inputPort > 0 && inputPort < 65536 && amplitudeOutputPort > 0 && amplitudeOutputPort < 65536 && phaseOutputPort > 0 && phaseOutputPort < 65536 ) { showUsage = false; String adrs = "127.0.0.1"; if (args.Length >= 4) { adrs = args[3]; } // todo: generalize address parsing and throw new exceptions Console.WriteLine("Sending on address " + adrs); Console.WriteLine("Receiving UDP packets on port " + inputPort); Console.WriteLine("Sending amplitude UDP packets on port " + amplitudeOutputPort); Console.WriteLine("Sending phase UDP packets on port " + phaseOutputPort); UdpClient inputUdpClient = new UdpClient(inputPort); UdpClient amplitudeUdpClient = new UdpClient(adrs, amplitudeOutputPort); UdpClient phaseUdpClient = new UdpClient(adrs, phaseOutputPort); try { while (true) { //IPEndPoint object will allow us to read datagrams sent from any source. IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0); // Blocks until a message returns on this socket from a remote host. Byte[] receiveBytes = inputUdpClient.Receive(ref RemoteIpEndPoint); //todo:Size handling!!! int size = 1024; // prepare dataset from real data List <ComplexF> cxList = new List <ComplexF>(size); for (int i = 0; i < size; i++) { // unsigned byte to R=[0..1],I=0 complex number //cxList.Add(new ComplexF((float)(receiveBytes[i]), 0)); float t = (float)(2 * Math.PI * size) / 16; // sampling rate is 16 samples/sec. float pt = (float)(1.1 + 1.0 * Math.Sin(t) + .5 * Math.Sin(1.5 * t)); cxList.Add(new ComplexF(pt, 0)); // * */ } // Transform in the complex array ComplexF[] cxArray = cxList.ToArray <ComplexF>(); Fourier.FFT(cxArray, FourierDirection.Forward); // find boundaries ComplexF maxCx = cxArray.Max <ComplexF>(); ComplexF minCx = cxArray.Min <ComplexF>(); // remap type List <byte> abList = new List <byte>(size); List <byte> pbList = new List <byte>(size); for (int i = 0; i < size; i++) { // also realign negative frequencies and flip amplitude int n = (i + size / 2) % size; abList.Add((byte)(255 - ((cxArray[n].GetModulus() * 255) / 1024))); // Amplitude [0..255] pbList.Add((byte)((cxArray[n].GetArgument() + Math.PI) / (2 * Math.PI) * 256)); // Phase [0..255] } byte[] amplitude = abList.ToArray <Byte>(); byte[] phase = pbList.ToArray <Byte>(); // and send! amplitudeUdpClient.Send(amplitude, amplitude.Length); phaseUdpClient.Send(phase, phase.Length); } } catch (Exception e) { Console.WriteLine(e.ToString()); } finally { inputUdpClient.Close(); amplitudeUdpClient.Close(); phaseUdpClient.Close(); } } } catch (Exception e) { // will show usage anyway... Console.WriteLine(e.ToString()); // todo: a friendly detailed explanation of the failure should be displayed when failing } } if (showUsage) { Console.WriteLine("Compute spectrum. using FFT"); Console.WriteLine("USAGE: UDPSpectrum inputPort amplitudeOutputPort phaseOutputPort [sendToAddress]\n"); Console.WriteLine("Warning: This kind of transformation is don on 2^x sets, som truncation and padding may occurs!"); // todo: make a strict flag in the arguments to optionnaly reject non-conform packets Console.WriteLine("Hit any key to exit"); Console.Read(); } }
static private void UnlockBufferCF( ref ComplexF[] buffer ) { buffer = null; }
/// <summary> /// Calculates the fast fourier transform of the image. /// </summary> /// <param name="image">The image to get calculate the FFT from.</param> /// <param name="shiftAxes">Whether to perform FFTShift on the Fourier image.</param> /// <param name="ignoreAlpha">Whether to ignore the alpha component of the image (if it is 32-bit).</param> /// <param name="disposeImage">Whether to dispose the image after use.</param> /// <param name="targetWidth">The height to resize the kernel to, or -1 to leave kernel same width. Can only be larger than the kernel width.</param> /// <param name="targetHeight">The height to resize the kernel to, or -1 to leave kernel same height. Can only be larger than the kernel height.</param> public FourierWorker(PixelWorker image, bool shiftAxes = true, bool ignoreAlpha = false, bool disposeImage = false, int targetWidth = -1, int targetHeight = -1) { if (image == null) { return; } int componentCount = image.ComponentCount, targetPixelCount = image.PixelCount; if (ignoreAlpha && componentCount == 4) { componentCount = 3; alphaReference = image; alphaReference.ShallowClone(); } values = new ComplexF[componentCount][]; ComponentCount = componentCount; TargetWidth = image.Width; TargetHeight = image.Height; TargetPixelCount = image.PixelCount; TargetPixelComponentCount = image.PixelComponentCount; TargetSize = new Size(TargetWidth, TargetHeight); FourierWidth = (int)Maths.CeilingPowerOfTwo((uint)(targetWidth <= TargetWidth ? TargetWidth : targetWidth)); FourierHeight = (int)Maths.CeilingPowerOfTwo((uint)(targetHeight <= TargetHeight ? TargetHeight : targetHeight)); FourierSize = new Size(FourierWidth, FourierHeight); FourierPixelCount = FourierWidth * FourierHeight; FourierPixelComponentCount = FourierPixelCount * ComponentCount; fourierWidthLog2 = Maths.Log2((uint)FourierWidth); fourierHeightLog2 = Maths.Log2((uint)FourierHeight); ComplexF[] current; byte comp; int c, x = 0, y = 0, fourierWidth = FourierWidth, fourierHeight = FourierHeight, tw = TargetWidth; int tcc = image.ComponentCount, txMinusOne = TargetWidth - 1, tyMinusOne = TargetHeight - 1; for (c = 0; c < componentCount; c++) { current = new ComplexF[FourierPixelCount]; values[c] = current; for (y = 0; y < fourierHeight; y++) { for (x = 0; x < fourierWidth; x++) { comp = image.GetPixelBgra((Math.Min(y, tyMinusOne) * tw + Math.Min(x, txMinusOne)) * tcc)[c]; if (comp > OriginalMax) { OriginalMax = comp; } current[y * fourierWidth + x] = new ComplexF() { Real = comp, }; } } } NormalizationCap = OriginalMax; FFT(true); if (shiftAxes) { ShiftAxes = true; FFTShift(); } if (disposeImage) { image.Dispose(); } }
//====================================================================================== /// <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; } } } }
public FourierWorker(float[][][] kernel, bool shiftAxes = true, int targetWidth = -1, int targetHeight = -1) { int cc = kernel == null ? 0 : kernel.Length; ComponentCount = cc; float[][] k = cc == 0 ? null : kernel[0]; if (targetWidth <= 0) { TargetWidth = k == null ? 0 : k.Length; FourierWidth = (int)Maths.CeilingPowerOfTwo((uint)TargetWidth); } else { TargetWidth = k == null ? 0 : k.Length; FourierWidth = (int)Maths.CeilingPowerOfTwo((uint)(targetWidth <= TargetWidth ? TargetWidth : targetWidth)); } if (targetHeight <= 0) { TargetHeight = (k == null || k.Length == 0) ? 0 : (k[0] == null ? 0 : k[0].Length); FourierHeight = (int)Maths.CeilingPowerOfTwo((uint)TargetHeight); } else { TargetHeight = (k == null || k.Length == 0) ? 0 : (k[0] == null ? 0 : k[0].Length); FourierHeight = (int)Maths.CeilingPowerOfTwo((uint)(targetHeight <= TargetHeight ? TargetHeight : targetHeight)); } OriginalMax = 255; NormalizationCap = 255; TargetPixelCount = TargetWidth * TargetHeight; TargetPixelComponentCount = TargetPixelCount * cc; FourierPixelCount = FourierWidth * FourierHeight; FourierPixelComponentCount = FourierPixelCount * cc; fourierWidthLog2 = Maths.Log2((uint)FourierWidth); fourierHeightLog2 = Maths.Log2((uint)FourierHeight); TargetSize = new Size(TargetWidth, TargetHeight); FourierSize = new Size(FourierWidth, FourierHeight); values = new ComplexF[cc][]; int c; for (c = 0; c < cc; c++) { values[c] = new ComplexF[FourierPixelCount]; } if (kernel == null) { return; } targetWidth = TargetWidth; targetHeight = TargetHeight; int fourierWidth = FourierWidth; float[] temp; ComplexF[] current; int x, y, xDiv, yDiv, xOffset, yOffset; for (c = 0; c < cc; c++) { current = values[c]; k = kernel[c]; x = 0; xDiv = (targetWidth & 1) == 0 ? -1 : targetWidth / 2; xOffset = (fourierWidth - targetWidth) / 2; while (x < targetWidth) { temp = k[x]; y = 0; yOffset = (FourierHeight - targetHeight) / 2; yDiv = (targetHeight & 1) == 0 ? -1 : targetHeight / 2; while (y < targetHeight) { current[(y + yOffset) * fourierWidth + x + xOffset] = new ComplexF() { Real = temp[y] }; if (y == yDiv) { yDiv = -1; yOffset++; } else { y++; } } if (x == xDiv) { xDiv = -1; xOffset++; } else { x++; } } } FFT(true); if (shiftAxes) { ShiftAxes = true; FFTShift(); } }
static private void UnlockWorkspaceF( ref ComplexF[] workspace ) { Debug.Assert( _workspaceF == workspace ); //Debug.Assert( _workspaceFLocked == true ); //_workspaceFLocked = false; workspace = null; }
/// <summary> /// Computes an in-place complex-to-complex FFT. /// </summary> private static void FFT(ComplexF[] values, long indexMultiplier, long indexOffset, int log2Length, bool forward) { long i, i1, k; double u1, u2, z; ComplexF t, temp; long nn = 1L << log2Length; long i2 = nn >> 1; long j = 0L; long nMinusOne = nn - 1L; long index1, index2; for (i = 0; i < nMinusOne; i++) { if (i < j) { index1 = i * indexMultiplier + indexOffset; index2 = j * indexMultiplier + indexOffset; t = values[index1]; values[index1] = values[index2]; values[index2] = t; } k = i2; while (k <= j) { j -= k; k >>= 1; } j += k; } double c1 = -1.0; double c2 = 0.0; long l1, l2 = 1L; double mult = forward ? -1.0 : 1.0; for (long l = 0L; l < log2Length; l++) { l1 = l2; l2 <<= 1; u1 = 1.0; u2 = 0.0; for (j = 0L; j < l1; j++) { for (i = j; i < nn; i += l2) { i1 = i + l1; index1 = i * indexMultiplier + indexOffset; index2 = i1 * indexMultiplier + indexOffset; t = values[index2]; t = new ComplexF((float)(u1 * t.Real - u2 * t.Imaginary), (float)(u1 * t.Imaginary + u2 * t.Real)); temp = values[index1]; values[index2] = temp - t; values[index1] = temp + t; } z = u1 * c1 - u2 * c2; u2 = u1 * c2 + u2 * c1; u1 = z; } c2 = Math.Sqrt((1.0 - c1) * 0.5) * mult; c1 = Math.Sqrt((1.0 + c1) * 0.5); } if (forward) { float multiplier = 1f / nn; for (i = 0L; i < nn; i++) { index1 = i * indexMultiplier + indexOffset; temp = values[index1]; values[index1] = new ComplexF(temp.Real * multiplier, temp.Imaginary * multiplier); } } }
/// <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 ); } }
// Use this for initialization void Start() { //oceansize= width * s * scale * ( max_LOD * 2 + 1 ); // normal map size n_width = 256; n_height = 256; textureA = new Texture2D(n_width, n_height); textureB = new Texture2D(n_width, n_height); textureA.filterMode = FilterMode.Bilinear; textureB.filterMode = FilterMode.Bilinear; if (!SetupOffscreenRendering()) { material_ocean.SetTexture("_BumpMap", textureA); material_ocean.SetTextureScale("_BumpMap", new Vector2(normal_scale, normal_scale)); material_ocean.SetTexture("_BumpMap2", textureB); material_ocean.SetTextureScale("_BumpMap2", new Vector2(normal_scale, normal_scale)); } pixelData = new Color[n_width * n_height]; // Init the water height matrix data = new ComplexF[width * height]; // lateral offset matrix to get the choppy waves data_x = new ComplexF[width * height]; // tangent t_x = new ComplexF[width * height]; t_y = new ComplexF[width * height]; n_x = new ComplexF[n_width * n_height]; n_y = new ComplexF[n_width * n_height]; // Geometry size g_height = height + 1; g_width = width + 1; tiles_LOD = new List <List <Mesh> >(); for (int LOD = 0; LOD < max_LOD * max_LOD; LOD++) { tiles_LOD.Add(new List <Mesh>()); } GameObject tile; int chDist; // Chebychev distance for (int y = 0; y < tiles_y; y++) { for (int x = 0; x < tiles_x; x++) { chDist = (int)Mathf.Max(Mathf.Abs(tiles_y / 2 - y), Mathf.Abs(tiles_x / 2 - x)); chDist = chDist > 0 ? chDist - 1 : 0; //Debug.Log(chDist); float cy = y - tiles_y / 2; float cx = x - tiles_x / 2; tile = new GameObject("Tile" + chDist); //~ tile.transform.position.x = ; //~ tile.transform.position.y = ; //~ tile.transform.position.z = ; tile.transform.position = new Vector3(Mathf.RoundToInt(cx * size.x * s), Mathf.RoundToInt(-2.0f * chDist), Mathf.RoundToInt(cy * size.z * s)); //~ tile.transform.localScale.x = s; //~ tile.transform.localScale.y = s; //~ tile.transform.localScale.z = s; tile.transform.localScale = new Vector3(s, s, s); MeshFilter filter = tile.AddComponent(typeof(MeshFilter)) as MeshFilter; tile.AddComponent("MeshRenderer"); //~ tile.AddComponent("MeshCollider"); //~ MeshCollider col = tile.AddComponent(typeof(MeshCollider)) as MeshCollider; //~ col.sharedMesh = filter.mesh; //~ col.smoothSphereCollisions = true; //~ col.convex = true; //~ tile.AddComponent(typeof(RecalculateBounds)); tile.renderer.material = material_ocean; //Make child of this object, so we don't clutter up the //scene hierarchy more than necessary. tile.transform.parent = transform; //Also we don't want these to be drawn while doing refraction/reflection passes, //so we'll add the to the water layer for easy filtering. tile.layer = LayerMask.NameToLayer("Water"); // Determine which LOD the tile belongs //~ tiles_LOD[chDist].Add ((tile.GetComponent(typeof(MeshFilter)) as MeshFilter).mesh); tiles_LOD[chDist].Add(filter.mesh); } } // Init wave spectra. One for vertex offset and another for normal map h0 = new ComplexF[width * height]; n0 = new ComplexF[n_width * n_height]; // Wind restricted to one direction, reduces calculations Vector2 wind = new Vector2(0.0f, windx); // Initialize wave generator for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { float yc = y < height / 2 ? y : -height + y; float xc = x < width / 2 ? 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(x, y), GaussianRnd(x, y)) * 0.707f * Mathf.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 / 2 ? y : -n_height + y; float xc = x < n_width / 2 ? 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(x, y), GaussianRnd(x, y)) * 0.707f * Mathf.Sqrt(P_spectrum(vec_k, wind)); } } GenerateHeightmap(); GenerateBumpmaps(); }
/// <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; } }
// Update is called once per frame void Update() { if (Input.GetKeyDown(KeyCode.P)) { Application.CaptureScreenshot("Screenshot.png"); } for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { int idx = width * y + x; float yc = y < height / 2 ? y : -height + y; float xc = x < width / 2 ? x : -width + x; Vector2 vec_k = new Vector2(2.0f * Mathf.PI * xc / size.x, 2.0f * Mathf.PI * yc / size.z); float iwkt = disp(vec_k) * Time.time; ComplexF coeffA = new ComplexF(Mathf.Cos(iwkt), Mathf.Sin(iwkt)); ComplexF coeffB = coeffA.GetConjugate(); 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 calcuations if (x + y > 0) { data[idx] += data[idx] * vec_k.x / vec_k.magnitude; } } } material_ocean.SetFloat("_BlendA", Mathf.Cos(Time.time) * 0.1f); material_ocean.SetFloat("_BlendB", Mathf.Sin(Time.time) * 0.5f); 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) { Mesh mesh = baseMesh; baseHeight = mesh.vertices; baseUV = mesh.uv; int itemCount = baseHeight.Length; uvs = new Vector2[itemCount]; vertices = new Vector3[itemCount]; normals = new Vector3[itemCount]; tangents = new Vector4[itemCount]; } //Vector3 vertex; Vector3 uv; //Vector3 normal; float n_scale = size.x / width / scale; float scaleA = choppy_scale / (width * height); float scaleB = scale / (width * height); float scaleBinv = 1.0f / scaleB; for (int i = 0; i < width * height; 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] = new Vector3(t_x[i].Re, scaleBinv, t_x[i].Im).normalized; uv = baseUV[iw]; uv.x = uv.x + Time.time * uv_speed; uvs[iw] = uv; if (!((i + 1) % width > 0)) { vertices[iw + 1] = baseHeight[iw + 1]; vertices[iw + 1].x += data[i + 1 - width].Im * scaleA; vertices[iw + 1].y = data[i + 1 - width].Re * scaleB; normals[iw + 1] = new Vector3(t_x[i + 1 - width].Re, scaleBinv, t_x[i + 1 - width].Im).normalized; uv = baseUV[iw + 1]; uv.x = uv.x + Time.time * uv_speed; uvs[iw + 1] = uv; } } int offset = g_width * (g_height - 1); for (int i = 0; i < g_width; i++) { vertices[i + offset] = baseHeight[i + offset]; vertices[i + offset].x += data[i % width].Im * scaleA; vertices[i + offset].y = data[i % width].Re * scaleB; normals[i + offset] = new Vector3(t_x[i % width].Re, scaleBinv, t_x[i % width].Im).normalized; uv = baseUV[i + offset]; uv.x = uv.x - Time.time * uv_speed; uvs[i + offset] = uv; } for (int i = 0; i < g_width * g_height - 1; i++) { //Need to preserve w in refraction/reflection mode if (!reflectionRefractionEnabled) { if (((i + 1) % g_width) == 0) { tangents[i] = (vertices[i - width + 1] + new Vector3(size.x, 0.0f, 0.0f) - vertices[i]).normalized; } else { tangents[i] = (vertices[i + 1] - vertices[i]).normalized; } tangents[i].w = 1.0f; } else { Vector3 tmp = Vector3.zero; if (((i + 1) % g_width) == 0) { tmp = (vertices[i - width + 1] + new Vector3(size.x, 0.0f, 0.0f) - vertices[i]).normalized; } else { tmp = (vertices[i + 1] - vertices[i]).normalized; } tangents[i] = new Vector4(tmp.x, tmp.y, tmp.z, tangents[i].w); } } //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++) { if (x + 1 >= g_width) { tangents[x + g_width * y].w = tangents[g_width * y].w; continue; } if (y + 1 >= g_height) { tangents[x + g_width * y].w = tangents[x].w; continue; } Vector3 right = vertices[(x + 1) + g_width * y] - vertices[x + g_width * y]; Vector3 back = vertices[x + g_width * y] - vertices[x + g_width * (y + 1)]; float foam = right.x / (size.x / g_width); if (foam < 0.0f) { tangents[x + g_width * y].w = 1; } else if (foam < 0.5f) { tangents[x + g_width * y].w += 2 * Time.deltaTime; } else { tangents[x + g_width * y].w -= 0.5f * Time.deltaTime; } tangents[x + g_width * y].w = Mathf.Clamp(tangents[x + g_width * y].w / foamTime, 0.0f, 2.0f); } } } tangents[g_width * g_height - 1] = (vertices[g_width * g_height - 1] + new Vector3(size.x, 0.0f, 0.0f) - vertices[1]).normalized; //~ LOD=0; for (int LOD = 0; LOD < max_LOD; LOD++) { int den = (int)Mathf.Pow(2, LOD); int itemcount = (height / den + 1) * (width / den + 1); Vector4[] tangentsLOD = new Vector4[itemcount]; Vector3[] verticesLOD = new Vector3[itemcount]; Vector3[] normalsLOD = new Vector3[itemcount]; Vector2[] uvLOD = new Vector2[(height / (int)Mathf.Pow(2, LOD) + 1) * (width / (int)Mathf.Pow(2, LOD) + 1)]; 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]; uvLOD[idx] = uvs[g_width * y + x]; tangentsLOD[idx] = tangents[idx2]; normalsLOD[idx++] = normals[idx2]; } } for (int k = 0; k < tiles_LOD[LOD].Count; k++) { Mesh meshLOD = tiles_LOD[LOD][k]; meshLOD.vertices = verticesLOD; meshLOD.normals = normalsLOD; meshLOD.uv = uvLOD; meshLOD.tangents = tangentsLOD; } } //oceansize=width*(max_LOD*2+1); float width_LOD2 = 175 * s; //transform.position.x=mycam.transform.localPosition.x; float tmpx = Mathf.RoundToInt(mycam.transform.position.x / width_LOD2); float tmpz = Mathf.RoundToInt(mycam.transform.position.z / width_LOD2); tmpx = tmpx * width_LOD2 - (width_LOD2 / 2); tmpz = tmpz * width_LOD2 - (width_LOD2 / 2); //tmpx-=Mathf.RoundToInt(width_LOD2); //tmpz-=Mathf.RoundToInt(width_LOD2); //~ transform.position.x=tmpx; //~ transform.position.z=tmpz; transform.position = new Vector3(tmpx, transform.position.y, tmpz); //transform.position.z=mycam.transform.localPosition.z*1000; }
/// <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; } }
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> /// 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]; } }
public float mag2db(ComplexF y) { return(20.0f * (float)Math.Log10(Math.Sqrt(y.Re * y.Re + y.Im * y.Im) / .02)); }
/// <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; } } }
static private void Swap( ref ComplexF a, ref ComplexF b ) { ComplexF temp = a; a = b; b = temp; }
/// <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 ) ) ); }
/// <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 LockWorkspaceF( int length, ref ComplexF[] workspace ) { //Debug.Assert( _workspaceFLocked == false ); //_workspaceFLocked = true; if( length >= _workspaceF.Length ) { _workspaceF = new ComplexF[ length ]; } workspace = _workspaceF; }
/// <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 ); }
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> /// Update is called once per frame. /// </summary> private void Update() { if (!simulationEnabled) { return; } // Calculate mesh vertices, uv and tangents. float halfWidth = tilePolygonWidth / 2.0f; float halfHeight = tilePolygonHeight / 2.0f; float time = Time.time; for (int y = 0; y < tilePolygonHeight; ++y) { for (int x = 0; x < tilePolygonWidth; ++x) { int idx = tilePolygonWidth * y + x; float yCopy = y < halfHeight ? y : y - tilePolygonHeight; float xCopy = x < halfWidth ? x : x - tilePolygonWidth; Vector2 vecK = new Vector2(2.0f * Mathf.PI * xCopy / oceanTileSize.x, 2.0f * Mathf.PI * yCopy / oceanTileSize.z); float sqrtMagnitude = (float)Math.Sqrt(Mathf.Pow(vecK.x, 2.0f) + Mathf.Pow(vecK.y, 2.0f)); float offset = Mathf.Sqrt(GRAVITY_ACCELERATION * sqrtMagnitude) * time * waveSpeed; ComplexF complexFA = new ComplexF(Mathf.Cos(offset), Mathf.Sin(offset)); ComplexF complexFB; complexFB.Re = complexFA.Re; complexFB.Im = -complexFA.Im; int nY = y > 0 ? tilePolygonHeight - y : 0; int nX = x > 0 ? tilePolygonWidth - x : 0; waterHeightData[idx] = vertexSpectra[idx] * complexFA + vertexSpectra[tilePolygonWidth * nY + nX].GetConjugate() * complexFB; tangentX[idx] = waterHeightData[idx] * new ComplexF(0.0f, vecK.x) - waterHeightData[idx] * vecK.y; // Choppy wave calculation. if (x + y > 0) { waterHeightData[idx] += waterHeightData[idx] * vecK.x / sqrtMagnitude; } } } Fourier.FFT2(waterHeightData, tilePolygonWidth, tilePolygonHeight, FourierDirection.Backward); Fourier.FFT2(tangentX, tilePolygonWidth, tilePolygonHeight, FourierDirection.Backward); // Get base values for vertices and uv coordinates. if (baseHeights == null) { baseHeights = baseMesh.vertices; vertices = new Vector3[baseHeights.Length]; normals = new Vector3[baseHeights.Length]; tangents = new Vector4[baseHeights.Length]; } int area = tilePolygonWidth * tilePolygonHeight; float scaleX = choppyScale / area; float scaleY = waveScaleRealTime / area; float scaleYReciprocal = MathUtility.GetReciprocal(scaleY); for (int i = 0; i < area; ++i) { int index = i + i / tilePolygonWidth; vertices[index] = baseHeights[index]; vertices[index].x += waterHeightData[i].Im * scaleX; vertices[index].y = waterHeightData[i].Re * scaleY; normals[index] = Vector3.Normalize(new Vector3(tangentX[i].Re, scaleYReciprocal, tangentX[i].Im)); if ((i + 1) % tilePolygonWidth == 0) { int indexPlus = index + 1; int iWidth = i + 1 - tilePolygonWidth; vertices[indexPlus] = baseHeights[indexPlus]; vertices[indexPlus].x += waterHeightData[iWidth].Im * scaleX; vertices[indexPlus].y = waterHeightData[iWidth].Re * scaleY; normals[indexPlus] = Vector3.Normalize(new Vector3(tangentX[iWidth].Re, scaleYReciprocal, tangentX[iWidth].Im)); } } int indexOffset = geometryWidth * (geometryHeight - 1); for (int i = 0; i < geometryWidth; ++i) { int index = i + indexOffset; int mod = i % tilePolygonWidth; vertices[index] = baseHeights[index]; vertices[index].x += waterHeightData[mod].Im * scaleX; vertices[index].y = waterHeightData[mod].Re * scaleY; normals[index] = Vector3.Normalize(new Vector3(tangentX[mod].Re, scaleYReciprocal, tangentX[mod].Im)); } int geometryArea = geometryWidth * geometryHeight - 1; for (int i = 0; i < geometryArea; ++i) { Vector3 tmp; if ((i + 1) % geometryWidth == 0) { tmp = Vector3.Normalize(vertices[i - tilePolygonWidth + 1] + new Vector3(oceanTileSize.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); } for (int y = 0; y < geometryHeight; ++y) { for (int x = 0; x < geometryWidth; ++x) { int index = x + geometryWidth * y; if (x + 1 >= geometryWidth) { tangents[index].w = tangents[geometryWidth * y].w; continue; } if (y + 1 >= geometryHeight) { tangents[index].w = tangents[x].w; continue; } float right = vertices[x + 1 + geometryWidth * y].x - vertices[index].x; float foam = right / (oceanTileSize.x / geometryWidth); if (foam < 0.0f) { tangents[index].w = 1.0f; } else if (foam < 0.5f) { tangents[index].w += 3.0f * Time.deltaTime; } else { tangents[index].w -= 0.4f * Time.deltaTime; } tangents[index].w = Mathf.Clamp(tangents[index].w, 0.0f, 2.0f); } } tangents[geometryArea] = Vector4.Normalize(vertices[geometryArea] + new Vector3(oceanTileSize.x, 0.0f, 0.0f) - vertices[1]); for (int level = 0; level < MAX_LOD; ++level) { int pow = (int)Math.Pow(2.0f, level); int length = (int)((tilePolygonHeight / pow + 1) * (tilePolygonWidth / pow + 1)); Vector4[] tangentsLOD = new Vector4[length]; Vector3[] verticesLOD = new Vector3[length]; Vector3[] normalsLOD = new Vector3[length]; int index = 0; for (int y = 0; y < geometryHeight; y += pow) { for (int x = 0; x < geometryWidth; x += pow) { int indexTemp = geometryWidth * y + x; verticesLOD[index] = vertices[indexTemp]; tangentsLOD[index] = tangents[indexTemp]; normalsLOD[index++] = normals[indexTemp]; } } for (int i = 0, count = tilesLOD[level].Count; i < count; ++i) { Mesh meshLOD = tilesLOD[level][i]; meshLOD.vertices = verticesLOD; meshLOD.normals = normalsLOD; meshLOD.tangents = tangentsLOD; } } if (reflectionEnabled) { RenderReflectionAndRefraction(); } }