/// <summary> /// Initializes a new instance of the <see cref="DynamicWaterMesh"/> class. /// </summary> /// <param name="resolution"> /// The number of grid nodes along the bigger side of the mesh. /// </param> /// <param name="size">The width and length of the mesh</param> /// <param name="settings"> /// DynamicWaterSettings instance representing the public settings properties of <see cref="DynamicWater"/> class. /// </param> /// <param name="fieldObstruction"> /// The array of <c>byte</c> indicating whether the water is obstructed by an object. \n /// <c>0</c> means the grid node is obstructed by an object, so the simulation is not updated; /// <c>255</c> means the grid node is not obstructed, and the simulation can proceed freely. /// Intermediate values represent the additional dampening value in that node. /// </param> public DynamicWaterMesh(int resolution, Vector2 size, IDynamicWaterSettings settings, byte[] fieldObstruction = null) { IsReady = false; _settings = settings; _size = size; GridMath.CalculateGrid(resolution, size, out _grid, out _nodeSize); // Some failsafe if (_size.x < Vector3.kEpsilon || _size.y < Vector3.kEpsilon) { _grid = new Vector2Int(1, 1); } _mesh = new Mesh(); _mesh.name = "DynamicWaterMesh"; #if !UNITY_3_5 _mesh.MarkDynamic(); #endif AllocateMeshArrays(); CreateMeshGrid(fieldObstruction); AssignMesh(); _mesh.RecalculateBounds(); IsReady = true; }
/// <summary> /// The initialization method. Automatically called by <see cref="DynamicWater"/>. /// </summary> /// <param name="gridSize"> /// Simulation grid resolution. /// </param> /// <seealso cref="DynamicWater"/> public override void Initialize(Vector2Int gridSize) { base.Initialize(gridSize); FieldSim = new float[_grid.x * _grid.y]; FieldSimNew = new float[_grid.x * _grid.y]; FieldSimSpeed = new float[_grid.x * _grid.y]; // Initial state Field = FieldSim; }
/// <summary> /// The initialization method. Called by <see cref="DynamicWater"/>. /// </summary> /// <param name="gridSize"> /// The simulation grid resolution. /// </param> /// <seealso cref="DynamicWater"/> public override void Initialize(Vector2Int gridSize) { base.Initialize(gridSize); _fieldSum = new float[_grid.x * _grid.y]; // Initial state _field = _fieldSum; _canInteract = !OnlyAmbient; }
/// <summary> /// Performs the wave simulation step. /// </summary> /// <param name="field"> /// Represents the current simulation state. /// </param> /// <param name="fieldNew"> /// Represents the updated simulation state. /// </param> /// <param name="fieldSpeed"> /// Represents the simulation state difference. /// </param> /// <param name="fieldObstruction"> /// Array of <c>bool</c> indicating whether the water is obstructed by an object. \n /// <c>true</c> means the grid node is obstructed by an object, so the simulation is not updated; /// <c>false</c> means the grid node is not obstructed, and the simulation can proceed freely. /// </param> /// <param name="gridSize"> /// Actual simulation grid resolution. /// </param> /// <param name="timeDelta"> /// Time delta in seconds. /// </param> /// <param name="damping"> /// Damping value. Must be clamped to the 0-1 range. /// </param> /// <param name="maxValue"> /// Value representing maximal absolute wave height. /// </param> public static void Solve(float[] field, float[] fieldNew, float[] fieldSpeed, byte[] fieldObstruction, Vector2Int gridSize, float timeDelta, float damping, out float maxValue) { maxValue = float.NegativeInfinity; bool isFieldObstructionNull = fieldObstruction == null; float obstructionValue = 1f; for (int j = 0; j < gridSize.y; j++) { int index = j * gridSize.x; for (int i = 0; i < gridSize.x; i++) { // Not updating borders and obstructions if (!(i <= 0 || j <= 0 || i >= gridSize.x - 1 || j >= gridSize.y - 1 || (!isFieldObstructionNull && fieldObstruction[index] == byte.MinValue))) { // Obstruction value (0-1) determined by obstruction geometry and obstruction mask if (!isFieldObstructionNull) { obstructionValue = fieldObstruction[index] * FastFunctions.InvertedByteMaxValue; } float laplasian = (field[index - 1] + field[index + 1] + field[index + gridSize.x] + field[index - gridSize.x]) * 0.25f - field[index]; fieldSpeed[index] += laplasian * timeDelta; fieldNew[index] = (field[index] + fieldSpeed[index]) * damping * obstructionValue; float valueAbs; if (fieldNew[index] > 0f) { valueAbs = fieldNew[index]; } else { valueAbs = -fieldNew[index]; } if (valueAbs > maxValue) { maxValue = valueAbs; } } index++; } } }
public static void CalculateGrid(int resolution, Vector2 size, out Vector2Int grid, out float nodeSize) { nodeSize = Mathf.Max(size.x, size.y) / resolution; grid.x = Mathf.FloorToInt(size.x / nodeSize + 1); grid.y = Mathf.FloorToInt(size.y / nodeSize + 1); }
/// <summary> /// The initialization method. Automatically called by <see cref="DynamicWater"/>. /// </summary> /// <param name="gridSize"> /// The size in nodes of simulation grid. /// </param> /// <seealso cref="DynamicWater"/> public virtual void Initialize(Vector2Int gridSize) { _isInitialized = true; _grid = gridSize; }
private void CreateSplashLine(Vector2Int start, Vector2Int end, float radius, float force) { int dx = Math.Abs(end.x - start.x); int dy = Math.Abs(end.y - start.y); int sx, sy; if (start.x < end.x) { sx = 1; } else { sx = -1; } if (start.y < end.y) { sy = 1; } else { sy = -1; } int err = dx - dy; bool splashMade = false; while (true) { if (start.x == end.x && start.y == end.y) { break; } Vector2 startf; startf.x = start.x; startf.y = start.y; CreateSplashNormalized(startf, radius, force); splashMade = true; int e2 = 2 * err; if (e2 > -dy) { err = err - dy; start.x = start.x + sx; } if (e2 < dx) { err = err + dx; start.y = start.y + sy; } } // Make sure we have made at least one splash if (!splashMade) { CreateSplashNormalized(new Vector2(start.x, start.y), radius, force); } }