using System.Diagnostics;
using System.Threading.Tasks;
using Debug = UnityEngine.Debug;
namespace UnchartedLimbo.Core.Utilities.DataExport
public class DataExporter2Editor : EditorWindow
private string folderPath = "";
private int window_length;
private int window_overlap;
private MocapData_LiteHolder[] mocapDataContainers;
[MenuItem("Tools/LSTM - CSV Exporter Tool")]
public static void ShowWindow()
GetWindow<DataExporter2Editor>("Data Exporter Tool");
GUILayout.Label("Export Settings", EditorStyles.boldLabel);
if (GUILayout.Button("Select Mocap Data Folder"))
folderPath = EditorUtility.OpenFolderPanel("Select Folder Containing MocapData_LiteHolder", "", "");
if (!string.IsNullOrEmpty(folderPath))
LoadMocapDataHoldersFromFolder();
if (!string.IsNullOrEmpty(folderPath))
GUILayout.Label($"Selected Folder: {folderPath}", EditorStyles.wordWrappedLabel);
window_length = EditorGUILayout.IntField("Window Length (frames)", window_length);
window_overlap = EditorGUILayout.IntField("Window Overlap (frames)", window_overlap);
GUILayout.Label("CSV Settings", EditorStyles.boldLabel);
filename = EditorGUILayout.TextField("Filename", filename);
if (GUILayout.Button("Export Data"))
if (mocapDataContainers == null || mocapDataContainers.Length == 0)
Debug.LogError("No Mocap Data Containers found in the selected folder.");
private void LoadMocapDataHoldersFromFolder()
if (string.IsNullOrEmpty(folderPath)) return;
// Convert the system path to a Unity relative path
string relativeFolderPath = "Assets" + folderPath.Replace(Application.dataPath, "").Replace("\\", "/");
// Find all MocapData_LiteHolder assets in the folder
string[] guids = AssetDatabase.FindAssets("t:MocapData_LiteHolder", new[] { relativeFolderPath });
mocapDataContainers = new MocapData_LiteHolder[guids.Length];
for (int i = 0; i < guids.Length; i++)
string assetPath = AssetDatabase.GUIDToAssetPath(guids[i]);
mocapDataContainers[i] = AssetDatabase.LoadAssetAtPath<MocapData_LiteHolder>(assetPath);
Debug.Log($"{mocapDataContainers.Length} MocapData_LiteHolder(s) found in folder: {folderPath}");
private async void ExportDataAsync()
string full_name = $"{filename}_{DateTime.Today:yyyy-MM-dd}_W{window_length}_O{window_overlap}";
string path = Path.Combine(Application.streamingAssetsPath, full_name + ".csv");
string label_path = Path.Combine(Application.streamingAssetsPath, full_name + "_labels.csv");
Stopwatch masterStopwatch = new Stopwatch();
var data_string = new StringBuilder();
var label_string = new StringBuilder();
foreach (var mocapDataHolder in mocapDataContainers)
var (data, labels) = await BuildFileContent(mocapDataHolder, ref window_id, ref frame_counter);
data_string.Append(data);
label_string.Append(labels);
await WriteToFileAsync(path, data_string.ToString());
await WriteToFileAsync(label_path, label_string.ToString());
Debug.Log($">>> DURATION OF RUN: {masterStopwatch.Elapsed.TotalSeconds} seconds");
private async Task<(string, string)> BuildFileContent(MocapData_LiteHolder mocapDataHolder, ref int window_id, ref int frame_counter)
MocapData_Lite mocap = mocapDataHolder.mocapData;
AnimationClipList clipList = FindClipListForMocapData(mocapDataHolder);
Debug.LogError($"Could not find AnimationClipList for {mocapDataHolder.name}");
return (string.Empty, string.Empty);
var sw = new Stopwatch();
var headers = mocap.data_header;
var sequenceLengths = mocap.framesPerSequence;
var data_string = new StringBuilder();
var label_string = new StringBuilder();
var header_string = new StringBuilder();
header_string.Append("Sequence_ID,");
for (var i = 0; i < headers.Length; i++)
header_string.Append(headers[i] + "_Xpos,");
header_string.Append(headers[i] + "_Ypos,");
header_string.Append(headers[i] + "_Zpos,");
header_string.Append(headers[i] + "_Xrot,");
header_string.Append(headers[i] + "_Yrot,");
header_string.Append(headers[i] + "_Zrot,");
header_string.Append(headers[i] + "_Xdir,");
header_string.Append(headers[i] + "_Ydir,");
header_string.Append(headers[i] + "_Zdir,");
data_string.AppendLine(header_string.ToString());
label_string.AppendLine("Sequence_ID,Labels");
int window_start_index = 0;
int step = window_length - window_overlap;
await Task.Run(async () =>
for (var j = 0; j < mocap.sequenceCount; j++)
var seq_length = sequenceLengths[j];
while (window_index < seq_length - window_length)
for (int i = window_index; i < window_index + window_length; i++)
var index = window_start_index + i;
data_string.AppendLine($"{window_id},{d}");
label_string.AppendLine($"{window_id},{clipList.classes[d.label_id]}");
window_start_index += seq_length;
return (data_string.ToString(), label_string.ToString());
private AnimationClipGot it! If you'd like to select a folder directly from Unity's **Assets** folder rather than using the system file explorer, we can use Unity's `EditorUtility.OpenFolderPanel` to select a folder within the Unity project and then convert that folder path into a relative Unity path. Here's the updated script to allow folder selection directly from the **Assets** folder:
### Modified Code to Select Folder in Assets:
using System.Diagnostics;
using System.Threading.Tasks;
using Debug = UnityEngine.Debug;
namespace UnchartedLimbo.Core.Utilities.DataExport
public class DataExporter2Editor : EditorWindow
private string folderPath = "";
private int window_length;
private int window_overlap;
private MocapData_LiteHolder[] mocapDataContainers;
[MenuItem("Tools/LSTM - CSV Exporter Tool")]
public static void ShowWindow()
GetWindow<DataExporter2Editor>("Data Exporter Tool");
GUILayout.Label("Export Settings", EditorStyles.boldLabel);
if (GUILayout.Button("Select Mocap Data Folder in Assets"))
string selectedFolder = EditorUtility.OpenFolderPanel("Select Folder Containing MocapData_LiteHolder", "Assets", "");
if (!string.IsNullOrEmpty(selectedFolder))
folderPath = GetRelativeUnityPath(selectedFolder);
LoadMocapDataHoldersFromFolder();
if (!string.IsNullOrEmpty(folderPath))
GUILayout.Label($"Selected Folder: {folderPath}", EditorStyles.wordWrappedLabel);
window_length = EditorGUILayout.IntField("Window Length (frames)", window_length);
window_overlap = EditorGUILayout.IntField("Window Overlap (frames)", window_overlap);
GUILayout.Label("CSV Settings", EditorStyles.boldLabel);
filename = EditorGUILayout.TextField("Filename", filename);
if (GUILayout.Button("Export Data"))
if (mocapDataContainers == null || mocapDataContainers.Length == 0)
Debug.LogError("No Mocap Data Containers found in the selected folder.");
private void LoadMocapDataHoldersFromFolder()
if (string.IsNullOrEmpty(folderPath)) return;
// Find all MocapData_LiteHolder assets in the folder
string[] guids = AssetDatabase.FindAssets("t:MocapData_LiteHolder", new[] { folderPath });
mocapDataContainers = new MocapData_LiteHolder[guids.Length];
for (int i = 0; i < guids.Length; i++)
string assetPath = AssetDatabase.GUIDToAssetPath(guids[i]);
mocapDataContainers[i] = AssetDatabase.LoadAssetAtPath<MocapData_LiteHolder>(assetPath);
Debug.Log($"{mocapDataContainers.Length} MocapData_LiteHolder(s) found in folder: {folderPath}");
private async void ExportDataAsync()
string full_name = $"{filename}_{DateTime.Today:yyyy-MM-dd}_W{window_length}_O{window_overlap}";
string path = Path.Combine(Application.streamingAssetsPath, full_name + ".csv");
string label_path = Path.Combine(Application.streamingAssetsPath, full_name + "_labels.csv");
Stopwatch masterStopwatch = new Stopwatch();
var data_string = new StringBuilder();
var label_string = new StringBuilder();
foreach (var mocapDataHolder in mocapDataContainers)
var (data, labels) = await BuildFileContent(mocapDataHolder, ref window_id, ref frame_counter);
data_string.Append(data);
label_string.Append(labels);
await WriteToFileAsync(path, data_string.ToString());
await WriteToFileAsync(label_path, label_string.ToString());
Debug.Log($">>> DURATION OF RUN: {masterStopwatch.Elapsed.TotalSeconds} seconds");
private async Task<(string, string)> BuildFileContent(MocapData_LiteHolder mocapDataHolder, ref int window_id, ref int frame_counter)
MocapData_Lite mocap = mocapDataHolder.mocapData;
AnimationClipList clipList = FindClipListForMocapData(mocapDataHolder);
Debug.LogError($"Could not find AnimationClipList for {mocapDataHolder.name}");
return (string.Empty, string.Empty);
var sw = new Stopwatch();
var headers = mocap.data_header;
var sequenceLengths = mocap.framesPerSequence;
var data_string = new StringBuilder();
var label_string = new StringBuilder();
var header_string = new StringBuilder();
header_string.Append("Sequence_ID,");
for (var i = 0; i < headers.Length; i++)
header_string.Append(headers[i] + "_Xpos,");
header_string.Append(headers[i] + "_Ypos,");
header_string.Append(headers[i] + "_Zpos,");
header_string.Append(headers[i] + "_Xrot,");
header_string.Append(headers[i] + "_Yrot,");
header_string.Append(headers[i] + "_Zrot,");
header_string.Append(headers[i] + "_Xdir,");
header_string.Append(headers[i] + "_Ydir,");
header_string.Append(headers[i] + "_Zdir,");
data_string.AppendLine(header_string.ToString());
label_string.AppendLine("Sequence_ID,Labels");
int window_start_index = 0;
int step = window_length - window_overlap;
await Task.Run(async () =>
for (var j = 0; j < mocap.sequenceCount; j++)
var seq_length = sequenceLengths[j];
while (window_index < seq_length - window_length)
for (int i = window_index; i < window_index + window_length; i++)
var index = window_start_index + i;
data_string.AppendLine($"{window_id},{d}");
label_string.AppendLine($"{window_id},{clipList.classes[d.label_id]}");
window_start_index += seq_length;
return (data_string.ToString(), label_string.ToString());
private AnimationClipList FindClipListForMocapData(MocapData_LiteHolder mocapDataHolder)
string mocapName = mocapDataHolder.name;
string[] guids = AssetDatabase.FindAssets($"t:AnimationClipList {mocapName}");
foreach (string guid in guids)
string path = AssetDatabase.GUIDToAssetPath(guid);
AnimationClipList clipList = AssetDatabase.LoadAssetAtPath<AnimationClipList>(path);
if (clipList != null && clipList.name == mocapName)
Debug.LogError($"Could not find AnimationClipList named {mocapName} for MocapData_LiteHolder {mocapName}");
private async Task WriteToFileAsync(string filePath, string content)
Debug.Log($">>> Saving frames to: {filePath}");
await using var streamWriter = new StreamWriter(filePath, false);
await streamWriter.WriteAsync(content);
private string GetRelativeUnityPath(string fullPath)
if (fullPath.StartsWith(Application.dataPath))
return "Assets" + fullPath.Substring(Application.dataPath.Length);