public void SampleDisplacementVel(ref Vector3 i_worldPos, SamplingData i_samplingData, out Vector3 o_displacement, out bool o_displacementValid, out Vector3 o_displacementVel, out bool o_velValid) { var lodData = i_samplingData._tag as PerLodData; o_displacementValid = lodData._resultData.InterpolateARGB16(ref i_worldPos, out o_displacement); if (!o_displacementValid) { o_displacementVel = Vector3.zero; o_velValid = false; return; } // Check if this lod changed scales between result and previous result - if so can't compute vel. This should // probably go search for the results in the other LODs but returning 0 is easiest for now and should be ok-ish // for physics code. if (lodData._resultDataPrevFrame._renderData._texelWidth != lodData._resultData._renderData._texelWidth) { o_displacementVel = Vector3.zero; o_velValid = false; return; } Vector3 dispLast; o_velValid = lodData._resultDataPrevFrame.InterpolateARGB16(ref i_worldPos, out dispLast); if (!o_velValid) { o_displacementVel = Vector3.zero; return; } Debug.Assert(lodData._resultData.Valid && lodData._resultDataPrevFrame.Valid); o_displacementVel = (o_displacement - dispLast) / Mathf.Max(0.0001f, lodData._resultData._time - lodData._resultDataPrevFrame._time); }
public int Query(int i_ownerHash, SamplingData i_samplingData, Vector3[] i_queryPoints, Vector3[] o_resultDisps, Vector3[] o_resultNorms, Vector3[] o_resultVels) { if (o_resultDisps != null) { for (int i = 0; i < o_resultDisps.Length; i++) { SampleDisplacement(ref i_queryPoints[i], i_samplingData, out o_resultDisps[i]); } } if (o_resultNorms != null) { for (int i = 0; i < o_resultNorms.Length; i++) { Vector3 undispPos; if (ComputeUndisplacedPosition(ref i_queryPoints[i], i_samplingData, out undispPos)) { SampleNormal(ref undispPos, i_samplingData, out o_resultNorms[i]); } else { o_resultNorms[i] = Vector3.up; } } } return(0); }
public void SampleDisplacementVel(ref Vector3 i_worldPos, SamplingData i_samplingData, out Vector3 o_displacement, out bool o_displacementValid, out Vector3 o_displacementVel, out bool o_velValid) { o_displacement = Vector3.zero; o_displacementValid = true; o_displacementVel = Vector3.zero; o_velValid = true; }
public bool SampleHeight(ref Vector3 i_worldPos, SamplingData i_samplingData, out float o_height) { o_height = 0f; Vector3 posFlatland = i_worldPos; posFlatland.y = OceanRenderer.Instance.transform.position.y; Vector3 undisplacedPos; if (!ComputeUndisplacedPosition(ref posFlatland, i_samplingData, out undisplacedPos)) { return(false); } Vector3 disp; if (!SampleDisplacement(ref undisplacedPos, i_samplingData, out disp)) { return(false); } o_height = posFlatland.y + disp.y; return(true); }
public bool ComputeUndisplacedPosition(ref Vector3 i_worldPos, SamplingData i_samplingData, out Vector3 undisplacedWorldPos) { // Tag should not be null if the collision source is GPU readback. Debug.Assert(i_samplingData._tag != null, "Invalid sampling data - LOD to sample from was unspecified."); var lodData = i_samplingData._tag as PerLodData; // FPI - guess should converge to location that displaces to the target position Vector3 guess = i_worldPos; // 2 iterations was enough to get very close when chop = 1, added 2 more which should be // sufficient for most applications. for high chop values or really stormy conditions there may // be some error here. one could also terminate iteration based on the size of the error, this is // worth trying but is left as future work for now. Vector3 disp = Vector3.zero; for (int i = 0; i < 4 && lodData._resultData.InterpolateARGB16(ref guess, out disp); i++) { Vector3 error = guess + disp - i_worldPos; guess.x -= error.x; guess.z -= error.z; } undisplacedWorldPos = guess; undisplacedWorldPos.y = OceanRenderer.Instance.SeaLevel; return(true); }
public int Query(int i_ownerHash, SamplingData i_samplingData, Vector3[] i_queryPoints, float[] o_resultHeights, Vector3[] o_resultNorms, Vector3[] o_resultVels) { if (o_resultHeights != null) { for (int i = 0; i < o_resultHeights.Length; i++) { o_resultHeights[i] = 0f; } } if (o_resultNorms != null) { for (int i = 0; i < o_resultNorms.Length; i++) { o_resultNorms[i] = Vector3.up; } } if (o_resultVels != null) { for (int i = 0; i < o_resultVels.Length; i++) { o_resultVels[i] = Vector3.zero; } } return(0); }
public bool SampleNormal(ref Vector3 i_undisplacedWorldPos, SamplingData i_samplingData, out Vector3 o_normal) { var lodData = i_samplingData._tag as PerLodData; var gridSize = lodData._resultData._renderData._texelWidth; o_normal = Vector3.zero; var dispCenter = Vector3.zero; if (!lodData._resultData.InterpolateARGB16(ref i_undisplacedWorldPos, out dispCenter)) { return(false); } var undisplacedWorldPosX = i_undisplacedWorldPos + Vector3.right * gridSize; var dispX = Vector3.zero; if (!lodData._resultData.InterpolateARGB16(ref undisplacedWorldPosX, out dispX)) { return(false); } var undisplacedWorldPosZ = i_undisplacedWorldPos + Vector3.forward * gridSize; var dispZ = Vector3.zero; if (!lodData._resultData.InterpolateARGB16(ref undisplacedWorldPosZ, out dispZ)) { return(false); } o_normal = Vector3.Cross(dispZ + Vector3.forward * gridSize - dispCenter, dispX + Vector3.right * gridSize - dispCenter).normalized; return(true); }
public int Query(int i_ownerHash, SamplingData i_samplingData, Vector3[] i_queryPoints, float[] o_resultHeights, Vector3[] o_resultNorms, Vector3[] o_resultVels) { var status = 0; if (o_resultHeights != null) { for (int i = 0; i < o_resultHeights.Length; i++) { if (o_resultVels == null) { Vector3 disp; if (SampleDisplacement(ref i_queryPoints[i], i_samplingData, out disp)) { o_resultHeights[i] = OceanRenderer.Instance.SeaLevel + disp.y; } else { status = 1 | status; } } else { Vector3 disp; bool dispValid, velValid; SampleDisplacementVel(ref i_queryPoints[i], i_samplingData, out disp, out dispValid, out o_resultVels[i], out velValid); if (dispValid && velValid) { o_resultHeights[i] = OceanRenderer.Instance.SeaLevel + disp.y; } else { status = 1 | status; } } } } if (o_resultNorms != null) { for (int i = 0; i < o_resultNorms.Length; i++) { Vector3 undispPos; if (ComputeUndisplacedPosition(ref i_queryPoints[i], i_samplingData, out undispPos)) { SampleNormal(ref undispPos, i_samplingData, out o_resultNorms[i]); } else { o_resultNorms[i] = Vector3.up; status = 1 | status; } } } return(status); }
public bool SampleDisplacement(ref Vector3 i_worldPos, SamplingData i_data, out Vector3 o_displacement) { var lodData = i_data._tag as PerLodData; if (lodData == null) { o_displacement = Vector3.zero; return(false); } return(lodData._resultData.InterpolateARGB16(ref i_worldPos, out o_displacement)); }
// Compute normal to a surface with a parameterization - equation 14 here: http://mathworld.wolfram.com/NormalVector.html public bool SampleNormal(ref Vector3 i_undisplacedWorldPos, SamplingData i_samplingData, out Vector3 o_normal) { o_normal = Vector3.zero; if (_amplitudes == null) { return(false); } var pos = new Vector2(i_undisplacedWorldPos.x, i_undisplacedWorldPos.z); float mytime = OceanRenderer.Instance.CurrentTime; float windAngle = OceanRenderer.Instance._windDirectionAngle; float minWaveLength = i_samplingData._minSpatialLength / 2f; // base rate of change of our displacement function in x and z is unit var delfdelx = Vector3.right; var delfdelz = Vector3.forward; for (int j = 0; j < _amplitudes.Length; j++) { if (_amplitudes[j] <= 0.001f) { continue; } if (_wavelengths[j] < minWaveLength) { continue; } float C = ComputeWaveSpeed(_wavelengths[j]); // direction var D = new Vector2(Mathf.Cos((windAngle + _angleDegs[j]) * Mathf.Deg2Rad), Mathf.Sin((windAngle + _angleDegs[j]) * Mathf.Deg2Rad)); // wave number float k = 2f * Mathf.PI / _wavelengths[j]; float x = Vector2.Dot(D, pos); float t = k * (x + C * mytime) + _phases[j]; float disp = k * -_spectrum._chop * Mathf.Cos(t); float dispx = D.x * disp; float dispz = D.y * disp; float dispy = -k *Mathf.Sin(t); delfdelx += _amplitudes[j] * new Vector3(D.x * dispx, D.x * dispy, D.y * dispx); delfdelz += _amplitudes[j] * new Vector3(D.x * dispz, D.y * dispy, D.y * dispz); } o_normal = Vector3.Cross(delfdelz, delfdelx).normalized; return(true); }
public AvailabilityResult CheckAvailability(ref Vector3 i_worldPos, SamplingData i_samplingData) { Debug.Assert(i_samplingData._minSpatialLength >= 0f && i_samplingData._tag != null); var sampleAreaXZ = new Rect(i_worldPos.x, i_worldPos.z, 0f, 0f); bool oneWasInRect = false; bool wavelengthsLargeEnough = false; foreach (var gridSize_lodData in _perLodData) { if (!gridSize_lodData.Value._activelyBeingRendered || gridSize_lodData.Value._resultData._time == -1f) { continue; } // Check that the region of interest is covered by this data var wdcRect = gridSize_lodData.Value._resultData._renderData.RectXZ; // Shrink rect by 1 texel border - this is to make finite differences fit as well float texelWidth = gridSize_lodData.Key; wdcRect.x += texelWidth; wdcRect.y += texelWidth; wdcRect.width -= 2f * texelWidth; wdcRect.height -= 2f * texelWidth; if (!wdcRect.Contains(sampleAreaXZ.min) || !wdcRect.Contains(sampleAreaXZ.max)) { continue; } oneWasInRect = true; // The smallest wavelengths should repeat no more than twice across the smaller spatial length. Unless we're // in the last LOD - then this is the best we can do. float minWavelength = texelWidth * OceanRenderer.Instance.MinTexelsPerWave; if (i_samplingData._minSpatialLength / minWavelength > 2f) { continue; } wavelengthsLargeEnough = true; return(AvailabilityResult.DataAvailable); } if (!oneWasInRect) { return(AvailabilityResult.NoDataAtThisPosition); } if (!wavelengthsLargeEnough) { return(AvailabilityResult.NoLODsBigEnoughToFilterOutWavelengths); } // Should not get here. return(AvailabilityResult.ValidationFailed); }
public bool SampleFlow(ref Vector3 i_worldPos, SamplingData i_samplingData, out Vector2 flow) { var data = i_samplingData._tag as PerLodData; if (data == null) { if (i_samplingData._tag != null) { Debug.LogError("Wrong kind of SamplingData provided - sampling data for e.g. collision and flow are not interchangeable.", this); } flow = Vector2.zero; return(false); } return(data._resultData.SampleRG16(ref i_worldPos, out flow)); }
public int Query(int i_ownerHash, SamplingData i_samplingData, Vector3[] i_queryPoints, Vector3[] o_resultDisps, Vector3[] o_resultNorms, Vector3[] o_resultVels) { var status = 0; if (o_resultDisps != null) { for (int i = 0; i < o_resultDisps.Length; i++) { if (o_resultVels == null) { if (!SampleDisplacement(ref i_queryPoints[i], i_samplingData, out o_resultDisps[i])) { status = 1 | status; } } else { bool dispValid, velValid; SampleDisplacementVel(ref i_queryPoints[i], i_samplingData, out o_resultDisps[i], out dispValid, out o_resultVels[i], out velValid); if (!dispValid || !velValid) { status = 1 | status; } } } } if (o_resultNorms != null) { for (int i = 0; i < o_resultNorms.Length; i++) { Vector3 undispPos; if (ComputeUndisplacedPosition(ref i_queryPoints[i], i_samplingData, out undispPos)) { SampleNormal(ref undispPos, i_samplingData, out o_resultNorms[i]); } else { o_resultNorms[i] = Vector3.up; status = 1 | status; } } } return(status); }
public bool SampleHeight(ref Vector3 i_worldPos, SamplingData i_samplingData, out float height) { var posFlatland = i_worldPos; posFlatland.y = OceanRenderer.Instance.transform.position.y; Vector3 undisplacedPos; ComputeUndisplacedPosition(ref posFlatland, i_samplingData, out undisplacedPos); var disp = Vector3.zero; SampleDisplacement(ref undisplacedPos, i_samplingData, out disp); height = posFlatland.y + disp.y; return(true); }
public bool SampleDisplacement(ref Vector3 i_worldPos, SamplingData i_samplingData, out Vector3 o_displacement) { o_displacement = Vector3.zero; if (_amplitudes == null) { return(false); } Vector2 pos = new Vector2(i_worldPos.x, i_worldPos.z); float mytime = OceanRenderer.Instance.CurrentTime; float windAngle = _windDirectionAngle; float minWavelength = i_samplingData._minSpatialLength / 2f; for (int j = 0; j < _amplitudes.Length; j++) { if (_amplitudes[j] <= 0.001f) { continue; } if (_wavelengths[j] < minWavelength) { continue; } float C = ComputeWaveSpeed(_wavelengths[j]); // direction Vector2 D = new Vector2(Mathf.Cos((windAngle + _angleDegs[j]) * Mathf.Deg2Rad), Mathf.Sin((windAngle + _angleDegs[j]) * Mathf.Deg2Rad)); // wave number float k = 2f * Mathf.PI / _wavelengths[j]; float x = Vector2.Dot(D, pos); float t = k * (x + C * mytime) + _phases[j]; float disp = -_spectrum._chop * Mathf.Sin(t); // 変更 o_displacement += _amplitudes[j] * new Vector3( D.x * disp, Mathf.Cos(t), D.y * disp ); } return(true); }
/// <summary> /// Compute time derivative of the displacements by calculating difference from last query. More complicated than it would seem - results /// may not be available in one or both of the results, or the query locations in the array may change. /// </summary> protected int CalculateVelocities(int i_ownerHash, SamplingData i_samplingData, Vector3[] i_queryPositions, Vector3[] results) { // Need at least 2 returned results to do finite difference if (_queryResultsTime < 0f || _queryResultsTimeLast < 0f) { return(1); } Vector2Int segment; if (!_resultSegments.TryGetValue(i_ownerHash, out segment)) { return((int)QueryStatus.RetrieveFailed); } Vector2Int segmentLast; if (!_resultSegmentsLast.TryGetValue(i_ownerHash, out segmentLast)) { return((int)QueryStatus.NotEnoughDataForVels); } if ((segment.y - segment.x) != (segmentLast.y - segmentLast.x)) { // Number of queries changed - can't handle that return((int)QueryStatus.VelocityDataInvalidated); } var dt = _queryResultsTime - _queryResultsTimeLast; if (dt < 0.0001f) { return((int)QueryStatus.InvalidDtForVelocity); } var count = results.Length; for (var i = 0; i < count; i++) { results[i] = (_queryResults[i + segment.x] - _queryResultsLast[i + segmentLast.x]) / dt; } return(0); }
/// <summary> /// Height is the only thing that is cached right now. We could cache disps and normals too, but the height queries are heaviest. /// </summary> public bool SampleHeight(ref Vector3 i_worldPos, SamplingData i_samplingData, out float o_height) { var hash = CalcHash(ref i_worldPos); _cacheChecks++; if (_waterHeightCache.TryGetValue(hash, out o_height)) { // got it from the cache! _cacheHits++; return(true); } // compute the height bool success = _collProvider.SampleHeight(ref i_worldPos, i_samplingData, out o_height); // populate cache (regardless of success for now) _waterHeightCache.Add(hash, o_height); return(success); }
public int Query(int i_ownerHash, SamplingData i_samplingData, Vector3[] i_queryPoints, float[] o_resultHeights, Vector3[] o_resultNorms, Vector3[] o_resultVels) { var status = 0; if (o_resultHeights != null) { for (int i = 0; i < i_queryPoints.Length; i++) { status = status | (SampleHeight(ref i_queryPoints[i], i_samplingData, out o_resultHeights[i]) ? 0 : 1); } } if (o_resultNorms != null) { // No caching for normals - go straight to source for these status = status | _collProvider.Query(i_ownerHash, i_samplingData, i_queryPoints, (float[])null, o_resultNorms, null); } return(status); }
public int Query(int i_ownerHash, SamplingData i_samplingData, Vector3[] i_queryPoints, Vector3[] o_resultDisps, Vector3[] o_resultNorms, Vector3[] o_resultVels) { var result = (int)QueryStatus.OK; if (!UpdateQueryPoints(i_ownerHash, i_samplingData, i_queryPoints, o_resultNorms != null ? i_queryPoints : null)) { result |= (int)QueryStatus.PostFailed; } if (!RetrieveResults(i_ownerHash, o_resultDisps, null, o_resultNorms)) { result |= (int)QueryStatus.RetrieveFailed; } if (o_resultVels != null) { result |= CalculateVelocities(i_ownerHash, i_samplingData, i_queryPoints, o_resultVels); } return(result); }
public bool ComputeUndisplacedPosition(ref Vector3 i_worldPos, SamplingData i_samplingData, out Vector3 o_undisplacedWorldPos) { // FPI - guess should converge to location that displaces to the target position Vector3 guess = i_worldPos; // 2 iterations was enough to get very close when chop = 1, added 2 more which should be // sufficient for most applications. for high chop values or really stormy conditions there may // be some error here. one could also terminate iteration based on the size of the error, this is // worth trying but is left as future work for now. Vector3 disp; for (int i = 0; i < 4 && SampleDisplacement(ref guess, i_samplingData, out disp); i++) { Vector3 error = guess + disp - i_worldPos; guess.x -= error.x; guess.z -= error.z; } o_undisplacedWorldPos = guess; o_undisplacedWorldPos.y = OceanRenderer.Instance.SeaLevel; return(true); }
public void ReturnSamplingData(SamplingData i_data) { i_data._tag = null; }
public bool GetSamplingData(ref Rect i_displacedSamplingArea, float i_minSpatialLength, SamplingData o_samplingData) { o_samplingData._minSpatialLength = i_minSpatialLength; Rect undisplacedRect = new Rect( i_displacedSamplingArea.xMin - OceanRenderer.Instance.MaxHorizDisplacement, i_displacedSamplingArea.yMin - OceanRenderer.Instance.MaxHorizDisplacement, i_displacedSamplingArea.width + 2f * OceanRenderer.Instance.MaxHorizDisplacement, i_displacedSamplingArea.height + 2f * OceanRenderer.Instance.MaxHorizDisplacement ); o_samplingData._tag = GetData(undisplacedRect, i_minSpatialLength); return(o_samplingData._tag != null); }
public void SampleDisplacementVel(ref Vector3 i_worldPos, SamplingData i_samplingData, out Vector3 o_displacement, out bool o_displacementValid, out Vector3 o_displacementVel, out bool o_velValid) { o_displacementValid = SampleDisplacement(ref i_worldPos, i_samplingData, out o_displacement); o_velValid = GetSurfaceVelocity(ref i_worldPos, i_samplingData, out o_displacementVel); }
public bool ComputeUndisplacedPosition(ref Vector3 i_worldPos, SamplingData i_samplingData, out Vector3 undisplacedWorldPos) { return(_collProvider.ComputeUndisplacedPosition(ref i_worldPos, i_samplingData, out undisplacedWorldPos)); }
public AvailabilityResult CheckAvailability(ref Vector3 i_worldPos, SamplingData i_samplingData) { return(_amplitudes == null ? AvailabilityResult.NotInitialisedYet : AvailabilityResult.DataAvailable); }
public void ReturnSamplingData(SamplingData i_data) { i_data._minSpatialLength = -1f; }
public bool GetSamplingData(ref Rect i_displacedSamplingArea, float i_minSpatialLength, SamplingData o_samplingData) { // We're not bothered with areas as the waves are infinite, so just store the min wavelength. o_samplingData._minSpatialLength = i_minSpatialLength; return(true); }
public void SampleDisplacementVel(ref Vector3 i_worldPos, SamplingData i_samplingData, out Vector3 o_displacement, out bool o_displacementValid, out Vector3 o_displacementVel, out bool o_velValid) { _collProvider.SampleDisplacementVel(ref i_worldPos, i_samplingData, out o_displacement, out o_displacementValid, out o_displacementVel, out o_velValid); }
public bool SampleNormal(ref Vector3 i_undisplacedWorldPos, SamplingData i_samplingData, out Vector3 o_normal) { return(_collProvider.SampleNormal(ref i_undisplacedWorldPos, i_samplingData, out o_normal)); }
/// <summary> /// Takes a unique request ID and some world space XZ positions, and computes the displacement vector that lands at this position, /// to a good approximation. The world space height of the water at that position is then SeaLevel + displacement.y. /// </summary> protected bool UpdateQueryPoints(int i_ownerHash, SamplingData i_samplingData, Vector3[] queryPoints, Vector3[] queryNormals) { var segmentRetrieved = false; Vector2Int segment; // We'll send in 3 points to get normals var countPts = (queryPoints != null ? queryPoints.Length : 0); var countNorms = (queryNormals != null ? queryNormals.Length : 0); var countTotal = countPts + countNorms * 3; if (_segmentRegistrarRingBuffer.Current._segments.TryGetValue(i_ownerHash, out segment)) { var segmentSize = segment.y - segment.x + 1; if (segmentSize == countTotal) { segmentRetrieved = true; } else { _segmentRegistrarRingBuffer.Current._segments.Remove(i_ownerHash); } } if (countTotal == 0) { // No query data return(false); } if (!segmentRetrieved) { if (_segmentRegistrarRingBuffer.Current._segments.Count >= s_maxGuids) { Debug.LogError("Too many guids registered with CollProviderCompute. Increase s_maxGuids.", this); return(false); } segment.x = _segmentRegistrarRingBuffer.Current._numQueries; segment.y = segment.x + countTotal - 1; _segmentRegistrarRingBuffer.Current._segments.Add(i_ownerHash, segment); _segmentRegistrarRingBuffer.Current._numQueries += countTotal; //Debug.Log("Added points for " + guid); } // The smallest wavelengths should repeat no more than twice across the smaller spatial length. Unless we're // in the last LOD - then this is the best we can do. // i_samplingData._minSpatialLength float minWavelength = i_samplingData._minSpatialLength / 2f; float minGridSize = minWavelength / OceanRenderer.Instance.MinTexelsPerWave; for (int pointi = 0; pointi < countPts; pointi++) { _queryPosXZ_minGridSize[pointi + segment.x].x = queryPoints[pointi].x; _queryPosXZ_minGridSize[pointi + segment.x].y = queryPoints[pointi].z; _queryPosXZ_minGridSize[pointi + segment.x].z = minGridSize; } // To compute each normal, post 3 query points for (int normi = 0; normi < countNorms; normi++) { var arrIdx = segment.x + countPts + 3 * normi; _queryPosXZ_minGridSize[arrIdx + 0].x = queryNormals[normi].x; _queryPosXZ_minGridSize[arrIdx + 0].y = queryNormals[normi].z; _queryPosXZ_minGridSize[arrIdx + 0].z = minGridSize; _queryPosXZ_minGridSize[arrIdx + 1].x = queryNormals[normi].x + s_finiteDiffDx; _queryPosXZ_minGridSize[arrIdx + 1].y = queryNormals[normi].z; _queryPosXZ_minGridSize[arrIdx + 1].z = minGridSize; _queryPosXZ_minGridSize[arrIdx + 2].x = queryNormals[normi].x; _queryPosXZ_minGridSize[arrIdx + 2].y = queryNormals[normi].z + s_finiteDiffDx; _queryPosXZ_minGridSize[arrIdx + 2].z = minGridSize; } return(true); }