From 2bf736e3513b82d4b7ddb46b7907460b08d70f6e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 22 Jul 2021 11:19:10 +0200 Subject: [PATCH] Add `MeshDataCache` utility for async mesh data caching --- Source/Engine/Utilities/MeshDataCache.cs | 138 +++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 Source/Engine/Utilities/MeshDataCache.cs diff --git a/Source/Engine/Utilities/MeshDataCache.cs b/Source/Engine/Utilities/MeshDataCache.cs new file mode 100644 index 000000000..0978bdc40 --- /dev/null +++ b/Source/Engine/Utilities/MeshDataCache.cs @@ -0,0 +1,138 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace FlaxEngine.Utilities +{ + /// + /// Helper utility for caching model meshes data. + /// + [HideInEditor] + public class MeshDataCache + { + /// + /// The single mesh data container. + /// + public struct MeshData + { + /// + /// The index buffer. + /// + public int[] IndexBuffer; + + /// + /// The vertex buffer. + /// + public Mesh.Vertex[] VertexBuffer; + } + + private Model _model; + private MeshData[][] _meshDatas; + private bool _meshDatasInProgress; + private bool _meshDatasCancel; + + /// + /// Gets the mesh datas (null if during downloading). + /// + public MeshData[][] MeshDatas => _meshDatasInProgress ? null : _meshDatas; + + /// + /// Requests the mesh data. + /// + /// The model to get it's data. + /// True if ahs valid data to access, otherwise false if it's during downloading. + public bool RequestMeshData(Model model) + { + if (model == null) + throw new ArgumentNullException(); + if (_model != model) + { + // Mode changes so release previous cache + Dispose(); + } + if (_meshDatasInProgress) + { + // Still downloading + return false; + } + if (_meshDatas != null) + { + // Has valid data + return true; + } + + // Start downloading + _model = model; + _meshDatasInProgress = true; + _meshDatasCancel = false; + Task.Run(new Action(DownloadMeshData)); + return false; + } + + /// + /// Releases cache. + /// + public void Dispose() + { + WaitForMeshDataRequestEnd(); + _meshDatas = null; + _meshDatasInProgress = false; + } + + /// + /// Waits for mesh data request to end. Does nothing if already has valid data or no valid request pending. + /// + public void WaitForMeshDataRequestEnd() + { + if (_meshDatasInProgress) + { + _meshDatasCancel = true; + for (int i = 0; i < 500 && _meshDatasInProgress; i++) + Thread.Sleep(10); + } + } + + private void DownloadMeshData() + { + try + { + if (!_model) + { + _meshDatasInProgress = false; + return; + } + var lods = _model.LODs; + _meshDatas = new MeshData[lods.Length][]; + + for (int lodIndex = 0; lodIndex < lods.Length && !_meshDatasCancel; lodIndex++) + { + var lod = lods[lodIndex]; + var meshes = lod.Meshes; + _meshDatas[lodIndex] = new MeshData[meshes.Length]; + + for (int meshIndex = 0; meshIndex < meshes.Length && !_meshDatasCancel; meshIndex++) + { + var mesh = meshes[meshIndex]; + _meshDatas[lodIndex][meshIndex] = new MeshData + { + IndexBuffer = mesh.DownloadIndexBuffer(), + VertexBuffer = mesh.DownloadVertexBuffer() + }; + } + } + } + catch (Exception ex) + { + Debug.Logger.LogHandler.LogWrite(LogType.Warning, "Failed to get mesh data."); + Debug.Logger.LogHandler.LogWrite(LogType.Warning, _model?.ToString() ?? string.Empty); + Debug.Logger.LogHandler.LogException(ex, null); + } + finally + { + _meshDatasInProgress = false; + } + } + } +}