static void DrawSim <SimType>(LodDataMgr lodData, bool actualSize, ref bool doDraw, ref float offset, float bias = 0f, float scale = 1f) where SimType : LodDataMgr { if (lodData == null) { return; } var type = typeof(SimType); if (!s_simNames.ContainsKey(type)) { s_simNames.Add(type, type.Name.Substring(10)); } float togglesBegin = Screen.height - _bottomPanelHeight; float b = 7f; float h = actualSize ? lodData.DataTexture.height : togglesBegin / (float)lodData.DataTexture.volumeDepth; float w = h + b; float x = Screen.width - w * offset + b * (offset - 1f); if (doDraw) { // Background behind slices GUI.color = _guiColor; GUI.DrawTexture(new Rect(x, 0, offset == 1f ? w : w - b, Screen.height - _bottomPanelHeight), Texture2D.whiteTexture); GUI.color = Color.white; // Only use Graphics.DrawTexture in EventType.Repaint events if called in OnGUI if (Event.current.type.Equals(EventType.Repaint)) { for (int idx = 0; idx < lodData.DataTexture.volumeDepth; idx++) { float y = idx * h; if (offset == 1f) { w += b; } s_textureArrayMaterials.TryGetValue(lodData.DataTexture, out var material); if (material == null) { material = new Material(Shader.Find("Hidden/Crest/Debug/TextureArray")); s_textureArrayMaterials.Add(lodData.DataTexture, material); } // Render specific slice of 2D texture array material.SetInt("_Depth", idx); material.SetFloat("_Scale", scale); material.SetFloat("_Bias", bias); Graphics.DrawTexture(new Rect(x + b, y + b / 2f, h - b, h - b), lodData.DataTexture, material); } } } doDraw = GUI.Toggle(new Rect(x + b, togglesBegin, w - 2f * b, _bottomPanelHeight), doDraw, s_simNames[type]); offset++; }
static void DrawSims <SimType>(LodDataMgr lodData, bool showByDefault, ref float offset) where SimType : LodDataMgr { if (lodData == null) { return; } var type = typeof(SimType); if (!_drawTargets.ContainsKey(type)) { _drawTargets.Add(type, showByDefault); } if (!_simNames.ContainsKey(type)) { _simNames.Add(type, type.Name.Substring(10)); } float togglesBegin = Screen.height - _bottomPanelHeight; float b = 7f; float h = togglesBegin / (float)lodData.DataTexture.volumeDepth; float w = h + b; float x = Screen.width - w * offset + b * (offset - 1f); if (_drawTargets[type]) { GUI.color = _guiColor; GUI.DrawTexture(new Rect(x, 0, offset == 1f ? w : w - b, Screen.height - _bottomPanelHeight), Texture2D.whiteTexture); GUI.color = Color.white; // Only use Graphics.DrawTexture in EventType.Repaint events if called in OnGUI if (Event.current.type.Equals(EventType.Repaint)) { for (int idx = 0; idx < lodData.DataTexture.volumeDepth; idx++) { float y = idx * h; if (offset == 1f) { w += b; } // Render specific slice of 2D texture array textureArrayMaterial.SetInt("_Depth", idx); Graphics.DrawTexture(new Rect(x + b, y + b / 2f, h - b, h - b), lodData.DataTexture, textureArrayMaterial); } } } _drawTargets[type] = GUI.Toggle(new Rect(x + b, togglesBegin, w - 2f * b, _bottomPanelHeight), _drawTargets[type], _simNames[type]); offset++; }
/// <summary> /// Called when a compute buffer has been read back from the GPU to the CPU. /// </summary> void DataArrived(AsyncGPUReadbackRequest req) { // Can get callbacks after disable, so detect this. if (!_queryResults.IsCreated) { _requests.Clear(); return; } // Remove any error requests for (int i = _requests.Count - 1; i >= 0; --i) { if (_requests[i]._request.hasError) { _requests.RemoveAt(i); _segmentRegistrarRingBuffer.ReleaseLast(); } } // Find the last request that was completed var lastDoneIndex = _requests.Count - 1; while (lastDoneIndex >= 0 && !_requests[lastDoneIndex]._request.done) { --lastDoneIndex; } // If there is a completed request, process it if (lastDoneIndex >= 0) { // Update "last" results LodDataMgr.Swap(ref _queryResults, ref _queryResultsLast); _queryResultsTimeLast = _queryResultsTime; _resultSegmentsLast = _resultSegments; var data = _requests[lastDoneIndex]._request.GetData <Vector3>(); data.CopyTo(_queryResults); _queryResultsTime = _requests[lastDoneIndex]._dataTimestamp; _resultSegments = _requests[lastDoneIndex]._segments; } // Remove all the requests up to the last completed one for (int i = lastDoneIndex; i >= 0; --i) { _requests.RemoveAt(i); _segmentRegistrarRingBuffer.ReleaseLast(); } }
public static void GenerateMesh(OceanRenderer ocean, int lodDataResolution, int geoDownSampleFactor, int lodCount) { if (lodCount < 1) { Debug.LogError("Invalid LOD count: " + lodCount.ToString(), ocean); return; } #if UNITY_EDITOR if (!UnityEditor.EditorApplication.isPlaying) { Debug.LogError("Ocean mesh meant to be (re)generated in play mode", ocean); return; } #endif int oceanLayer = LayerMask.NameToLayer(ocean.LayerName); if (oceanLayer == -1) { Debug.LogError("Invalid ocean layer: " + ocean.LayerName + " please add this layer.", ocean); oceanLayer = 0; } #if PROFILE_CONSTRUCTION System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); sw.Start(); #endif // create mesh data Mesh[] meshInsts = new Mesh[(int)PatchType.Count]; // 4 tiles across a LOD, and support lowering density by a factor var tileResolution = Mathf.Round(0.25f * lodDataResolution / geoDownSampleFactor); for (int i = 0; i < (int)PatchType.Count; i++) { meshInsts[i] = BuildOceanPatch((PatchType)i, tileResolution); } ocean._lodTransform = ocean.gameObject.AddComponent <LodTransform>(); ocean._lodTransform.InitLODData(lodCount); // Create the LOD data managers ocean._lodDataAnimWaves = LodDataMgr.Create <LodDataMgrAnimWaves, SimSettingsAnimatedWaves>(ocean.gameObject, ref ocean._simSettingsAnimatedWaves); if (ocean.CreateDynamicWaveSim) { ocean._lodDataDynWaves = LodDataMgr.Create <LodDataMgrDynWaves, SimSettingsWave>(ocean.gameObject, ref ocean._simSettingsDynamicWaves); } if (ocean.CreateFlowSim) { ocean._lodDataFlow = LodDataMgr.Create <LodDataMgrFlow, SimSettingsFlow>(ocean.gameObject, ref ocean._simSettingsFlow); } if (ocean.CreateFoamSim) { ocean._lodDataFoam = LodDataMgr.Create <LodDataMgrFoam, SimSettingsFoam>(ocean.gameObject, ref ocean._simSettingsFoam); } if (ocean.CreateShadowData) { ocean._lodDataShadow = LodDataMgr.Create <LodDataMgrShadow, SimSettingsShadow>(ocean.gameObject, ref ocean._simSettingsShadow); } if (ocean.CreateSeaFloorDepthData) { ocean._lodDataSeaDepths = ocean.gameObject.AddComponent <LodDataMgrSeaFloorDepth>(); } if (ocean.CreateClipSurfaceData) { ocean._lodDataClipSurface = ocean.gameObject.AddComponent <LodDataMgrClipSurface>(); } // Add any required GPU readbacks { var ssaw = ocean._simSettingsAnimatedWaves; if (ssaw && ssaw.CollisionSource == SimSettingsAnimatedWaves.CollisionSources.ComputeShaderQueries) { ocean.gameObject.AddComponent <QueryDisplacements>(); } if (ocean.CreateFlowSim) { ocean.gameObject.AddComponent <QueryFlow>(); } } // Remove existing LODs for (int i = 0; i < ocean.transform.childCount; i++) { var child = ocean.transform.GetChild(i); if (child.name.StartsWith("Tile_L")) { child.parent = null; Object.Destroy(child.gameObject); i--; } } for (int i = 0; i < lodCount; i++) { CreateLOD(ocean, i, lodCount, meshInsts, lodDataResolution, geoDownSampleFactor, oceanLayer); } #if PROFILE_CONSTRUCTION sw.Stop(); Debug.Log("Finished generating " + lodCount.ToString() + " LODs, time: " + (1000.0 * sw.Elapsed.TotalSeconds).ToString(".000") + "ms"); #endif }
public static void GenerateMesh(OceanRenderer ocean, int lodDataResolution, int geoDownSampleFactor, int lodCount) { if (lodCount < 1) { Debug.LogError("Invalid LOD count: " + lodCount.ToString(), ocean); return; } #if UNITY_EDITOR if (!UnityEditor.EditorApplication.isPlaying) { Debug.LogError("Ocean mesh meant to be (re)generated in play mode", ocean); return; } #endif int oceanLayer = LayerMask.NameToLayer(ocean.LayerName); if (oceanLayer == -1) { Debug.LogError("Invalid ocean layer: " + ocean.LayerName + " please add this layer.", ocean); oceanLayer = 0; } #if PROFILE_CONSTRUCTION System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); sw.Start(); #endif // create mesh data Mesh[] meshInsts = new Mesh[(int)PatchType.Count]; // 4 tiles across a LOD, and support lowering density by a factor var tileResolution = Mathf.Round(0.25f * lodDataResolution / geoDownSampleFactor); for (int i = 0; i < (int)PatchType.Count; i++) { meshInsts[i] = BuildOceanPatch((PatchType)i, tileResolution); } ocean._lods = new LodTransform[lodCount]; // Create the LOD data managers ocean._lodDataAnimWaves = LodDataMgr.Create <LodDataMgrAnimWaves, SimSettingsAnimatedWaves>(ocean.gameObject, ref ocean._simSettingsAnimatedWaves); if (ocean.CreateDynamicWaveSim) { ocean._lodDataDynWaves = LodDataMgr.Create <LodDataMgrDynWaves, SimSettingsWave>(ocean.gameObject, ref ocean._simSettingsDynamicWaves); } if (ocean.CreateFlowSim) { ocean._lodDataFlow = LodDataMgr.Create <LodDataMgrFlow, SimSettingsFlow>(ocean.gameObject, ref ocean._simSettingsFlow); } if (ocean.CreateFoamSim) { ocean._lodDataFoam = LodDataMgr.Create <LodDataMgrFoam, SimSettingsFoam>(ocean.gameObject, ref ocean._simSettingsFoam); } if (ocean.CreateShadowData) { ocean._lodDataShadow = LodDataMgr.Create <LodDataMgrShadow, SimSettingsShadow>(ocean.gameObject, ref ocean._simSettingsShadow); } if (ocean.CreateSeaFloorDepthData) { ocean._lodDataSeaDepths = ocean.gameObject.AddComponent <LodDataMgrSeaFloorDepth>(); } // Add any required GPU readbacks { var ssaw = ocean._simSettingsAnimatedWaves; if (ssaw && ssaw.CollisionSource == SimSettingsAnimatedWaves.CollisionSources.OceanDisplacementTexturesGPU) { ocean.gameObject.AddComponent <GPUReadbackDisps>(); } if (ocean.CreateFlowSim) { var ssf = ocean._simSettingsFlow; if (ssf && ssf._readbackData) { ocean.gameObject.AddComponent <GPUReadbackFlow>(); } } } // Remove existing LODs for (int i = 0; i < ocean.transform.childCount; i++) { var child = ocean.transform.GetChild(i); if (child.name.StartsWith("LOD")) { child.parent = null; Object.Destroy(child.gameObject); i--; } } int startLevel = 0; for (int i = 0; i < lodCount; i++) { bool biggestLOD = i == lodCount - 1; GameObject nextLod = CreateLOD(ocean, i, lodCount, biggestLOD, meshInsts, lodDataResolution, geoDownSampleFactor, oceanLayer); nextLod.transform.parent = ocean.transform; // scale only horizontally, otherwise culling bounding box will be scaled up in y float horizScale = Mathf.Pow(2f, (float)(i + startLevel)); nextLod.transform.localScale = new Vector3(horizScale, 1f, horizScale); } #if PROFILE_CONSTRUCTION sw.Stop(); Debug.Log("Finished generating " + parms._lodCount.ToString() + " LODs, time: " + (1000.0 * sw.Elapsed.TotalSeconds).ToString(".000") + "ms"); #endif }
public void PopulateCache() { if (_type == OceanDepthCacheType.Baked) { return; } var layerMask = 0; var errorShown = false; foreach (var layer in _layerNames) { if (string.IsNullOrEmpty(layer)) { Debug.LogError("OceanDepthCache: An empty layer name was provided. Please provide a valid layer name. Click this message to highlight the cache in question.", this); errorShown = true; continue; } int layerIdx = LayerMask.NameToLayer(layer); if (layerIdx == -1) { Debug.LogError("OceanDepthCache: Invalid layer specified: \"" + layer + "\". Please add this layer to the project by putting the name in an empty layer slot in Edit/Project Settings/Tags and Layers. Click this message to highlight the cache in question.", this); errorShown = true; } else { layerMask = layerMask | (1 << layerIdx); } } if (layerMask == 0) { if (!errorShown) { Debug.LogError("No valid layers for populating depth cache, aborting. Click this message to highlight the cache in question.", this); } return; } #if UNITY_EDITOR if (_type == OceanDepthCacheType.Realtime && _checkTerrainDrawInstancedOption) { // This issue only affects the built-in render pipeline. Issue 158: https://github.com/crest-ocean/crest/issues/158 var terrains = FindObjectsOfType <Terrain>(); foreach (var terrain in terrains) { var mask = (int)Mathf.Pow(2f, terrain.gameObject.layer); if ((mask & layerMask) == 0) { continue; } if (terrain.drawInstanced) { Debug.LogError($"Terrain {terrain.gameObject.name} has 'Draw Instanced' enabled. This terrain will not populate into the depth cache and therefore will not contribute to shorelines and shallow water. This option must be disabled on the terrain when the depth cache is populated (but can be enabled afterwards).", terrain); } } } #endif if (_depthCacheTexture == null) { RenderTextureFormat fmt; if (_generateSDF) { fmt = RenderTextureFormat.RGHalf; } else { #if UNITY_EDITOR_WIN fmt = RenderTextureFormat.DefaultHDR; #else fmt = RenderTextureFormat.RHalf; #endif } Debug.Assert(SystemInfo.SupportsRenderTextureFormat(fmt), "The graphics device does not support the render texture format " + fmt.ToString()); _depthCacheTexture = new RenderTexture(_resolution, _resolution, 0); _depthCacheTexture.name = gameObject.name + "_oceanDepth"; _depthCacheTexture.format = fmt; _depthCacheTexture.useMipMap = false; _depthCacheTexture.anisoLevel = 0; _depthCacheTexture.enableRandomWrite = _generateSDF; _depthCacheTexture.Create(); } if (_depthCacheCamera == null) { _depthCacheCamera = GenerateCacheCamera( layerMask, _generateSDF ? "DepthSdfCam" : "DepthCacheCam", _cameraMaxTerrainHeight, transform, _depthCacheTexture, _hideDepthCacheCam ); } // Make sure this global is set - I found this was necessary to set it here. However this can cause glitchiness in editor // as it messes with this global vector, so only do it if not in edit mode #if UNITY_EDITOR if (EditorApplication.isPlaying) #endif { // Shader needs sea level to determine water depth var centerPoint = Vector3.zero; if (OceanRenderer.Instance != null) { centerPoint.y = OceanRenderer.Instance.Root.position.y; } else { centerPoint.y = transform.position.y; } Shader.SetGlobalVector("_OceanCenterPosWorld", centerPoint); } _depthCacheCamera.RenderWithShader(Shader.Find("Crest/Inputs/Depth/Ocean Depth From Geometry"), null); if (_generateSDF) { RenderTextureFormat fmt = RenderTextureFormat.RGHalf; RenderTexture voronoiPingPongTexture0 = new RenderTexture(_resolution, _resolution, 0); voronoiPingPongTexture0.name = gameObject.name + "_voronoiPingPong0"; voronoiPingPongTexture0.format = fmt; voronoiPingPongTexture0.useMipMap = false; voronoiPingPongTexture0.anisoLevel = 0; voronoiPingPongTexture0.enableRandomWrite = true; voronoiPingPongTexture0.Create(); RenderTexture voronoiPingPongTexture1 = new RenderTexture(_resolution, _resolution, 0); voronoiPingPongTexture1.name = gameObject.name + "_voronoiPingPong1"; voronoiPingPongTexture1.format = fmt; voronoiPingPongTexture1.useMipMap = false; voronoiPingPongTexture1.anisoLevel = 0; voronoiPingPongTexture1.enableRandomWrite = true; voronoiPingPongTexture1.Create(); using (CommandBuffer jumpFloodCommandBuffer = new CommandBuffer()) { var cameraToWorldMatrix = _depthCacheCamera.cameraToWorldMatrix; var projectionMatrix = _depthCacheCamera.projectionMatrix; var projectionToWorldMatrix = cameraToWorldMatrix * projectionMatrix.inverse; uint textureDimension = (uint)voronoiPingPongTexture0.width; { ComputeShader initJumpFloodShader = ComputeShaderHelpers.LoadShader("SdfInitJumpFlood"); int initJumpFloodKernel = initJumpFloodShader.FindKernel("SdfInitJumpFlood"); jumpFloodCommandBuffer.SetComputeTextureParam(initJumpFloodShader, initJumpFloodKernel, sp_FromTexture, _depthCacheTexture); jumpFloodCommandBuffer.SetComputeTextureParam(initJumpFloodShader, initJumpFloodKernel, sp_ToTexture, voronoiPingPongTexture0); jumpFloodCommandBuffer.SetComputeIntParam(initJumpFloodShader, sp_textureDimension, (int)textureDimension); jumpFloodCommandBuffer.SetComputeMatrixParam(initJumpFloodShader, sp_projectionToWorld, projectionToWorldMatrix); jumpFloodCommandBuffer.DispatchCompute( initJumpFloodShader, initJumpFloodKernel, _depthCacheTexture.width / 8, _depthCacheTexture.height / 8, 1 ); } ComputeShader jumpFloodShader = ComputeShaderHelpers.LoadShader("SdfJumpFlood"); int jumpFloodKernel = jumpFloodShader.FindKernel("SdfJumpFlood"); ComputeShader sdfGradientShader = ComputeShaderHelpers.LoadShader("SdfApply"); int sdfKernel = sdfGradientShader.FindKernel("SdfApply"); jumpFloodCommandBuffer.name = "Jump Flood"; for (uint jumpSize = (uint)textureDimension / 2; jumpSize > 0; jumpSize /= 2) { ApplyJumpFlood( jumpFloodCommandBuffer, jumpFloodShader, jumpFloodKernel, sp_jumpSize, jumpSize, sp_textureDimension, textureDimension, sp_projectionToWorld, projectionToWorldMatrix, sp_FromTexture, voronoiPingPongTexture0, sp_ToTexture, voronoiPingPongTexture1 ); LodDataMgr.Swap(ref voronoiPingPongTexture1, ref voronoiPingPongTexture0); } for (uint roundNum = 0; roundNum < _additionalJumpFloodRounds; roundNum++) { uint jumpSize = (uint)1 << (int)roundNum; ApplyJumpFlood( jumpFloodCommandBuffer, jumpFloodShader, jumpFloodKernel, sp_jumpSize, jumpSize, sp_textureDimension, textureDimension, sp_projectionToWorld, projectionToWorldMatrix, sp_FromTexture, voronoiPingPongTexture0, sp_ToTexture, voronoiPingPongTexture1 ); LodDataMgr.Swap(ref voronoiPingPongTexture1, ref voronoiPingPongTexture0); } jumpFloodCommandBuffer.SetComputeTextureParam(sdfGradientShader, sdfKernel, sp_FromTexture, voronoiPingPongTexture0); jumpFloodCommandBuffer.SetComputeTextureParam(sdfGradientShader, sdfKernel, sp_ToTexture, _depthCacheTexture); jumpFloodCommandBuffer.SetComputeIntParam(sdfGradientShader, sp_textureDimension, (int)textureDimension); jumpFloodCommandBuffer.SetComputeMatrixParam(sdfGradientShader, sp_projectionToWorld, projectionToWorldMatrix); jumpFloodCommandBuffer.DispatchCompute( sdfGradientShader, sdfKernel, voronoiPingPongTexture0.width / 8, voronoiPingPongTexture0.height / 8, 1 ); Graphics.ExecuteCommandBuffer(jumpFloodCommandBuffer); } DrawCacheQuad(ref _drawDepthCacheQuad, "SDFCache_", OceanDepthCacheType.Baked, _type == OceanDepthCacheType.Baked ? (Texture)_savedCache : _depthCacheTexture); voronoiPingPongTexture0.DiscardContents(); voronoiPingPongTexture1.DiscardContents(); } else { DrawCacheQuad(ref _drawDepthCacheQuad, "DepthCache_", _type, _type == OceanDepthCacheType.Baked ? (Texture)_savedCache : _depthCacheTexture); } }