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;
+ }
+ }
+ }
+}