A collection of fast implementations of mathematical functions.
    /// <summary>
    /// Executes the simulation step.
    /// </summary>
    /// <param name="speed">
    /// The overall simulation speed factor.
    /// </param>
    /// <param name="damping">
    /// The damping value (range 0-1).
    /// </param>
    public override void StepSimulation(float speed, float damping)
    {
        if (!OnlyAmbient)
        {
            base.StepSimulation(speed, damping);
        }

        _canInteract = !OnlyAmbient;

        bool isFieldObstructionNull = _fieldObstruction == null;

        // Ambient wave time
        _time += Time.deltaTime * AmbientWaveSpeed;

        // Caching to avoid many divisions
        Vector2 invGrid = new Vector2(1f / _grid.x, 1f / _grid.y);

        for (int i = 0; i < _grid.x; i++)
        {
            for (int j = 0; j < _grid.y; j++)
            {
                int index = j * _grid.x + i;
                if (!isFieldObstructionNull && _fieldObstruction[index] == byte.MinValue)
                {
                    continue;
                }
                // Obstruction value (0-1) determined by obstruction geometry and obstruction mask
                float obstructionValue = isFieldObstructionNull ? 1f : _fieldObstruction[index] * FastFunctions.InvertedByteMaxValue;

                // Normalizing the coordinates
                float normX = i * invGrid.x;
                float normY = j * invGrid.y;

                // Calculating new node value
                float val = (normX + normY) * FastFunctions.DoublePi;
                if (OnlyAmbient)
                {
                    _fieldSum[index] = FastFunctions.FastSin(val * AmbientWaveFrequency + _time) * AmbientWaveHeight * obstructionValue;
                }
                else
                {
                    _fieldSum[index] = FastFunctions.FastSin(val * AmbientWaveFrequency + _time) * AmbientWaveHeight * obstructionValue + FieldSimNew[index];
                }
            }
        }

        // Updating the field
        _field = _fieldSum;

        _isDirty = true;
    }
        /// <summary>
        /// Checks whether the point is inside a collider.
        /// </summary>
        /// <remarks>
        /// This method can check against all types of colliders,
        /// including <c>TerrainCollider</c> and concave <c>MeshCollider</c>.
        /// </remarks>
        /// <param name="collider">
        /// The collider to check against.
        /// </param>
        /// <param name="point">
        /// The point being checked.
        /// </param>
        /// <param name="specificTest">
        /// Defines a kind of specific collision test that must be done against <paramref name="collider"/>.
        /// </param>
        /// <returns>
        /// <c>true</c> if the <paramref name="point"/> is inside the <paramref name="collider"/>,
        /// <c>false</c> otherwise.
        /// </returns>
        public static bool IsPointInsideCollider(Collider collider, Vector3 point, ColliderSpecificTestEnum specificTest = ColliderSpecificTestEnum.None)
        {
            RaycastHit hit;

            if (specificTest == ColliderSpecificTestEnum.None)
            {
#if !UNITY_FLASH
                if (collider is TerrainCollider)
                {
                    if (!collider.Raycast(new Ray(point, Vector3.up), out hit, collider.bounds.size.y))
                    {
                        return(false);
                    }
                }
                else
#endif
                if (collider is MeshCollider && !((MeshCollider)collider).convex)
                {
                    if (!IsPointInsideMeshCollider(collider, point))
                    {
                        return(false);
                    }
                }
                else
                {
                    Vector3 direction          = collider.bounds.center - point;
                    float   directionMagnitude = direction.sqrMagnitude;
                    if (directionMagnitude > 0.0001f &&
                        collider.Raycast(new Ray(point, direction.normalized), out hit, FastFunctions.FastSqrt(directionMagnitude)))
                    {
                        return(false);
                    }
                }
            }
            else
            {
                if (specificTest == ColliderSpecificTestEnum.Terrain)
                {
                    if (!collider.Raycast(new Ray(point + Vector3.up * collider.bounds.size.y, Vector3.down), out hit, collider.bounds.size.y))
                    {
                        return(false);
                    }
                }
            }

            return(true);
        }
    /// <summary>
    /// Executes the simulation step.
    /// </summary>
    /// <param name="speed">
    /// The overall simulation speed factor.
    /// </param>
    /// <param name="damping">
    /// The damping value (range 0-1).
    /// </param>
    public override void StepSimulation(float speed, float damping)
    {
        if (!OnlyAmbient)
        {
            base.StepSimulation(speed, damping);
        }

        _canInteract = !OnlyAmbient;
        bool isFieldObstructionNull = _fieldObstruction == null;

        // Ambient wave time
        _time += Time.deltaTime;

        // Recalculating direction
        foreach (Wave wave in Waves)
        {
            wave.Direction   = new Vector2(FastFunctions.FastCos(wave.Angle * FastFunctions.Deg2Rad), FastFunctions.FastSin(wave.Angle * FastFunctions.Deg2Rad));
            wave.CircleShift = new Vector2(-Mathf.Clamp01(wave.Position.x) * FastFunctions.DoublePi, -Mathf.Clamp01(wave.Position.y) * FastFunctions.DoublePi);
        }

        // Caching to avoid many divisions
        Vector2 invGrid = new Vector2(1f / _grid.x, 1f / _grid.y);

        for (int i = 0; i < _grid.x; i++)
        {
            for (int j = 0; j < _grid.y; j++)
            {
                int index = j * _grid.x + i;

                if (!isFieldObstructionNull && _fieldObstruction[index] == byte.MinValue)
                {
                    continue;
                }
                // Obstruction value (0-1) determined by obstruction geometry and obstruction mask
                float obstructionValue = isFieldObstructionNull ? 1f : _fieldObstruction[index] * FastFunctions.InvertedByteMaxValue;

                // Normalizing the coordinates
                float normX = i * invGrid.x * FastFunctions.DoublePi;
                float normY = j * invGrid.y * FastFunctions.DoublePi;
                _fieldSum[index] = 0f;
                for (int k = 0; k < Waves.Length; k++)
                {
                    Wave wave = Waves[k];
                    // Calculating new node value
                    if (wave.Excluded)
                    {
                        continue;
                    }

                    float val;
                    if (wave.Circular)
                    {
                        normX += wave.CircleShift.x;
                        normY += wave.CircleShift.y;
                        /* Non-optimized version. Use it if you want */
                        //val = FastFunctions.FastSqrt(normX * normX + normY * normY) * wave.Frequency + _time * wave.Velocity;

                        val = normX * normX + normY * normY;

                        FastFunctions.FloatIntUnion u;
                        u.i = 0;
                        u.f = val;
                        float xhalf = 0.5f * val;
                        u.i = 0x5f375a86 - (u.i >> 1);
                        u.f = u.f * (1.5f - xhalf * u.f * u.f);

                        val = u.f * val * wave.Frequency + _time * wave.Velocity;
                    }
                    else
                    {
                        val = (wave.Direction.x * normX + wave.Direction.y * normY) * wave.Frequency + _time * wave.Velocity;
                    }

                    /* Non-optimized version. Use it if you want */
                    //_fieldSum[index] += FastFunctions.FastPow((FastFunctions.FastSin(val) + 1f) * 0.5f, wave.Steepness) * wave.Amplitude * obstructionValue;

                    /* Optimized version */
                    // FastFunctions.FastSin(val)
                    float tmpVal = (val + Mathf.PI) * FastFunctions.InvDoublePi;
                    int   floor  = tmpVal >= 0 ? (int)(tmpVal) : (int)((tmpVal) - 1);
                    val = val - FastFunctions.DoublePi * floor;

                    if (val < 0)
                    {
                        val = 1.27323954f * val + 0.405284735f * val * val;
                    }
                    else
                    {
                        val = 1.27323954f * val - 0.405284735f * val * val;
                    }

                    // FastFunctions.FastPowInt
                    tmpVal = (val + 1f) * 0.5f;
                    int steepness = wave.Steepness;

                    while (steepness > 0)
                    {
                        tmpVal *= val;
                        steepness--;
                    }

                    // Final calculations
                    val = tmpVal * wave.Amplitude * obstructionValue;
                    _fieldSum[index] += val;

                    /* End of optimized version */
                }

                if (!OnlyAmbient)
                {
                    _fieldSum[index] += FieldSimNew[index];
                }
            }
        }

        // Updating the field
        _field = _fieldSum;

        _isDirty = true;
    }
    /// <summary>
    /// Buoyancy calculation step.
    /// </summary>
    private void FixedUpdate()
    {
        // Happens on assembly reload
        if (_recompileMarker == null)
        {
            RecalculateVoxels();
            _water           = null;
            _recompileMarker = new RecompiledMarker();
        }

        // Failsafe
        if (_water == null || _density < 0.01f || !_isReady)
        {
            _rigidbody.drag        = _dragNonFluid;
            _rigidbody.angularDrag = _angularDragNonFluid;
            return;
        }

        float        invDoubleVoxelSize = 1f / (2f * _voxelSize);
        DynamicWater dynamicWater       = _water as DynamicWater;
        bool         solverCanInteract  = dynamicWater != null && dynamicWater.Solver.CanInteract;

        for (int i = 0; i < _voxelsLength; i++)
        {
            Vector3 wp         = _transform.TransformPoint(_buoyancyVoxels[i].Position);
            float   waterLevel = _water.GetWaterLevel(wp.x, wp.y, wp.z);

            // No force is applied to the points outside the fluid
            if (waterLevel != float.NegativeInfinity && (wp.y - _voxelSize / 1f < waterLevel))
            {
                Vector3 velocity = _rigidbody.GetPointVelocity(wp);

                // k == 1 when the voxel is fully submerged
                // k == 0 when the voxel is fully outside
                float k = (waterLevel - wp.y) * invDoubleVoxelSize + 0.5f;

                // Create the splash when the point has passed the water surface.
                if (_buoyancyVoxels[i].IsOnColliderEdge)
                {
                    if (!_buoyancyVoxels[i].HadPassedWater && (k <1f && k> 0f))
                    {
                        // Scaling and limiting the splash force
                        if (solverCanInteract)
                        {
                            float force = FastFunctions.FastVector3Magnitude(velocity) * _splashForceFactorNormalized;
                            if (force > _maxSplashForceNormalized)
                            {
                                force = _maxSplashForceNormalized;
                            }

                            if (force > 0.0075f)
                            {
                                _water.CreateSplash(wp, _voxelSize, force);
                            }
                        }

                        _buoyancyVoxels[i].HadPassedWater = true;
                    }
                    else
                    {
                        _buoyancyVoxels[i].HadPassedWater = false;
                    }
                }

                k = (k > 1f) ? 1f : (k < 0f) ? 0f : k;
                _subMergedVolume += k;

                // Calculating the actual force for this point depending oh how much
                // the point is submerged into the fluid
                Vector3 archimedesForce;
                archimedesForce.x = k * _voxelArchimedesForce.x;
                archimedesForce.y = k * _voxelArchimedesForce.y;
                archimedesForce.z = k * _voxelArchimedesForce.z;

                // Applying the local force
                _rigidbody.AddForceAtPosition(archimedesForce, wp, ForceMode.Impulse);
            }
        }

        // Normalizing the submerged volume
        // 0 - object is fully outside the water
        // 1 - object is fully submerged
        _subMergedVolume /= _voxelsLength;

        const float threshold = 0.01f;

        // Sending the message to other components
        if (_subMergedVolumePrev < threshold && _subMergedVolume >= threshold)
        {
            SendMessage("OnFluidVolumeEnter", _water, SendMessageOptions.DontRequireReceiver);
        }
        else if (_subMergedVolumePrev >= threshold && _subMergedVolume < threshold)
        {
            SendMessage("OnFluidVolumeExit", _water, SendMessageOptions.DontRequireReceiver);
        }

        _subMergedVolumePrev = _subMergedVolume;

        // Calculating the drag
        _rigidbody.drag        = Mathf.Lerp(_rigidbody.drag, _subMergedVolume > 0.0001f ? _dragNonFluid + _dragInFluid : _dragNonFluid, 8f * Time.deltaTime);
        _rigidbody.angularDrag = Mathf.Lerp(_rigidbody.angularDrag, _subMergedVolume > 0.0001f ? _angularDragNonFluid + _angularDragInFluid : _angularDragNonFluid, 8f * Time.deltaTime);
    }