1196 lines
47 KiB
C#
1196 lines
47 KiB
C#
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using System.IO;
|
|
using UnityEngine.Events;
|
|
using System.Collections;
|
|
using TriLibCore.SFB;
|
|
using XED.Util;
|
|
using System.Threading.Tasks;
|
|
using MessagePack;
|
|
using System;
|
|
using Newtonsoft.Json;
|
|
using System.Text;
|
|
using System.Linq;
|
|
using UnityEngine.EventSystems;
|
|
using MessagePack.Resolvers;
|
|
using UnityEngine.Rendering;
|
|
|
|
namespace XED.Asset
|
|
{
|
|
public class SaveLoadFBXData : MonoBehaviour
|
|
{
|
|
public string identifier;
|
|
public string password;
|
|
public UnityEvent<string, string, CustomAssetData> onLoadFbxFile;
|
|
public UnityEvent<string> onRemoveFbxFile;
|
|
public event System.Action onBeginLoadAsset;
|
|
private SaveData saveData;
|
|
private List<CustomAssetData> listAssets = new List<CustomAssetData>();
|
|
private Queue<string> loadFilePath = new Queue<string>();
|
|
private SharedMaterial sharedMaterial;
|
|
private MessagePackFileManager<SaveData> fileManager;
|
|
private bool isSaveTaskComplete = true;
|
|
private bool isLoadTaskComplete = true;
|
|
void Start()
|
|
{
|
|
saveData = new SaveData();
|
|
sharedMaterial = new SharedMaterial();
|
|
fileManager = new MessagePackFileManager<SaveData>();
|
|
fileManager.Initialize();
|
|
|
|
identifier = identifier.Length == 0 ? "defaultAssetData" : identifier;
|
|
StartCoroutine(CoroutineLoadLocalFiles());
|
|
string baseDataPath = Application.streamingAssetsPath + "/baseAssetData";
|
|
LoadLocalData(baseDataPath);
|
|
}
|
|
bool IsWebURL(string path)
|
|
{
|
|
return path.StartsWith("http://") || path.StartsWith("https://");
|
|
}
|
|
public void LoadLocalFBXFile()
|
|
{
|
|
TriLibCore.SFB.StandaloneFileBrowser.OpenFilePanelAsync("Load Local (File)", "C:\\Users", "fbx", false, OnLoadLocalFBXFile);
|
|
}
|
|
|
|
private void OnLoadLocalFBXFile(IList<ItemWithStream> list)
|
|
{
|
|
onBeginLoadAsset?.Invoke();
|
|
for (int i = 0; i < list.Count; i++)
|
|
{
|
|
if (!list[i].HasData)
|
|
continue;
|
|
var path = list[i].Name;
|
|
if (listAssets.Find((x) => x.localFBXPath == path) != null)
|
|
return;
|
|
loadFilePath.Enqueue(path);
|
|
}
|
|
}
|
|
|
|
public void LoadLocalFBXDirectory()
|
|
{
|
|
TriLibCore.SFB.StandaloneFileBrowser.OpenFolderPanelAsync("Load Local (Directory)", "C:\\Users", false, OnLoadLocalFBXDirectory);
|
|
}
|
|
|
|
private void OnLoadLocalFBXDirectory(IList<ItemWithStream> list)
|
|
{
|
|
for (int i = 0; i < list.Count; i++)
|
|
{
|
|
if (!list[i].HasData)
|
|
continue;
|
|
|
|
var path = list[i].Name;
|
|
Debug.Log("path " + path);
|
|
var files = Directory.GetFiles(path, "*.fbx", SearchOption.AllDirectories);
|
|
if (listAssets.Find((x) => x.localFBXPath == path) != null)
|
|
return;
|
|
foreach (var file in files)
|
|
{
|
|
loadFilePath.Enqueue(file);
|
|
}
|
|
}
|
|
}
|
|
public void SaveToLocalData(string path = "")
|
|
{
|
|
if (isSaveTaskComplete == false)
|
|
{
|
|
return;
|
|
}
|
|
StartCoroutine(CoroutineSaveToLocalData(null, path));
|
|
}
|
|
public void SaveToLocalData(List<CustomAssetData> assetDatas, string path = "")
|
|
{
|
|
if (isSaveTaskComplete == false)
|
|
{
|
|
return;
|
|
}
|
|
StartCoroutine(CoroutineSaveToLocalData(assetDatas, path));
|
|
}
|
|
public void LoadLocalData(string path = "")
|
|
{
|
|
if (isLoadTaskComplete == false)
|
|
{
|
|
return;
|
|
}
|
|
onBeginLoadAsset?.Invoke();
|
|
StartCoroutine(CoroutineLoadFromLocalData(path));
|
|
}
|
|
public void OnLoadLocalFBXFile(string[] paths)
|
|
{
|
|
if (paths != null && paths.Length > 0 && !string.IsNullOrEmpty(paths[0]))
|
|
{
|
|
if (listAssets.Find((x) => x.localFBXPath == paths[0]) != null)
|
|
return;
|
|
loadFilePath.Enqueue(paths[0]);
|
|
}
|
|
}
|
|
public void OnLoadLocalFBXDirectory(string[] paths)
|
|
{
|
|
if (paths != null && paths.Length > 0 && !string.IsNullOrEmpty(paths[0]))
|
|
{
|
|
string[] files = Directory.GetFiles(paths[0], "*.fbx", SearchOption.AllDirectories);
|
|
for (int i = 0; i < files.Length; i++)
|
|
{
|
|
string filePath = files[i];
|
|
if (listAssets.Find((x) => x.localFBXPath == filePath) != null)
|
|
continue;
|
|
loadFilePath.Enqueue(filePath);
|
|
}
|
|
}
|
|
}
|
|
public void Load(string path)
|
|
{
|
|
string name = System.IO.Path.GetFileNameWithoutExtension(path);
|
|
string directoryPath = Path.GetDirectoryName(path);
|
|
string lastFolderName = Path.GetFileName(directoryPath);
|
|
GameObject newObject = new GameObject(name);
|
|
newObject.transform.parent = transform;
|
|
CustomAssetData assetData = newObject.AddComponent<CustomAssetData>();
|
|
assetData.assetName = name;
|
|
assetData.folderName = lastFolderName;
|
|
assetData.LoadLocalFBX(path);
|
|
listAssets.Add(assetData);
|
|
onLoadFbxFile?.Invoke(name, lastFolderName, assetData);
|
|
}
|
|
public CustomAssetData GetCustomAssetData(string name)
|
|
{
|
|
return listAssets.FirstOrDefault(x => x.name == name);
|
|
}
|
|
public async Task SaveAsyncJson(string filename, SaveData data)
|
|
{
|
|
string filePath = Path.Combine(Application.persistentDataPath, filename);
|
|
string json = JsonConvert.SerializeObject(data);
|
|
using (StreamWriter writer = new StreamWriter(filePath, false, Encoding.UTF8))
|
|
{
|
|
await writer.WriteAsync(json);
|
|
}
|
|
}
|
|
public async Task<SaveData> LoadAsyncJson(string filename)
|
|
{
|
|
string filePath = Path.Combine(Application.persistentDataPath, filename);
|
|
|
|
if (!File.Exists(filePath)) return default;
|
|
|
|
try
|
|
{
|
|
using (StreamReader reader = new StreamReader(filePath, Encoding.UTF8))
|
|
{
|
|
string json = await reader.ReadToEndAsync();
|
|
return JsonConvert.DeserializeObject<SaveData>(json);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.LogError($"Load Error: {ex}");
|
|
return default;
|
|
}
|
|
}
|
|
IEnumerator CoroutineLoadLocalFiles()
|
|
{
|
|
while (true)
|
|
{
|
|
yield return new WaitUntil(() => loadFilePath.Count > 0);
|
|
string path = loadFilePath.Dequeue();
|
|
string name = System.IO.Path.GetFileNameWithoutExtension(path);
|
|
string directoryPath = Path.GetDirectoryName(path);
|
|
string lastFolderName = Path.GetFileName(directoryPath);
|
|
string createDate = File.GetCreationTime(path).ToShortDateString();
|
|
string lastRevisionDate = File.GetLastAccessTime(path).ToShortDateString();
|
|
string uploadDate = DateTime.Now.ToShortDateString();
|
|
|
|
if (listAssets.Find(x => x.name == name) != null)
|
|
{
|
|
yield return null;
|
|
continue;
|
|
}
|
|
GameObject newObject = new GameObject(name);
|
|
newObject.transform.parent = transform;
|
|
CustomAssetData assetData = newObject.AddComponent<CustomAssetData>();
|
|
assetData.assetName = name;
|
|
assetData.folderName = lastFolderName;
|
|
assetData.createDate = createDate;
|
|
assetData.LastRevisionDate = lastRevisionDate;
|
|
assetData.UploadDate = uploadDate;
|
|
assetData.Creator = "xr@uvc.co.kr";
|
|
assetData.Manager = "xr@uvc.co.kr";
|
|
assetData.LoadLocalFBX(path);
|
|
listAssets.Add(assetData);
|
|
onLoadFbxFile?.Invoke(name, lastFolderName, assetData);
|
|
yield return new WaitUntil(() => (assetData.isLoadComplete == true && assetData.progress == 1) || assetData.isLoadError == true);
|
|
if (assetData.isLoadError)
|
|
{
|
|
listAssets.Remove(assetData);
|
|
onRemoveFbxFile?.Invoke(name);
|
|
}
|
|
yield return null;
|
|
}
|
|
}
|
|
IEnumerator CoroutineSaveToLocalData(List<CustomAssetData> assetDatas, string path)
|
|
{
|
|
if (assetDatas == null || assetDatas.Count == 0)
|
|
{
|
|
assetDatas = listAssets;
|
|
}
|
|
if (assetDatas.Count == 0)
|
|
{
|
|
Debug.Log("No AssetDatas to Save");
|
|
isSaveTaskComplete = true;
|
|
yield break;
|
|
}
|
|
isSaveTaskComplete = false;
|
|
List<SavedModelData> savedModels = new List<SavedModelData>();
|
|
List<ThumbnailData> thumbnails = new List<ThumbnailData>();
|
|
RuntimePreviewGenerator.BackgroundColor = Color.clear;
|
|
RuntimePreviewGenerator.OrthographicMode = true;
|
|
|
|
for (int i = 0; i < assetDatas.Count; i++)
|
|
{
|
|
CustomAssetData assetData = assetDatas[i];
|
|
SavedModelData modelData = new SavedModelData();
|
|
modelData.SaveData(assetData.loadedObject, sharedMaterial);
|
|
//갯수 제한 없음 원하는 만큼 조절 가능. 테스트로 10개만 지정
|
|
modelData.attributes = new string[7][];
|
|
modelData.attributes[0] = new string[] { "assetName", assetData.assetName };
|
|
modelData.attributes[1] = new string[] { "folderName", assetData.folderName };
|
|
|
|
modelData.attributes[2] = new string[] { "createDate", assetData.createDate };
|
|
modelData.attributes[3] = new string[] { "LastRevisionDate", assetData.LastRevisionDate };
|
|
modelData.attributes[4] = new string[] { "UploadDate", assetData.UploadDate };
|
|
modelData.attributes[5] = new string[] { "Creator", assetData.createDate };
|
|
modelData.attributes[6] = new string[] { "Manager", assetData.Manager };
|
|
|
|
Texture2D thumbnail = RuntimePreviewGenerator.GenerateModelPreview(assetData.loadedObject.transform, 320, 200);
|
|
thumbnail = MakeReadableTexture(thumbnail);
|
|
thumbnails.Add(new ThumbnailData(thumbnail.EncodeToPNG()));
|
|
savedModels.Add(modelData);
|
|
}
|
|
saveData.modelDatas = savedModels.ToArray();
|
|
saveData.textureDatas = sharedMaterial.textureDatas.ToArray();
|
|
saveData.thumbnailDatas = thumbnails.ToArray();
|
|
yield return null;
|
|
|
|
//Task task = SaveAsync(identifier, saveData);
|
|
|
|
string filePath = string.IsNullOrEmpty(path) ? Path.Combine(Application.persistentDataPath, identifier) : path;
|
|
Task task = Task.Run(() => fileManager.SaveAsync(filePath, saveData));
|
|
|
|
yield return new WaitUntil(() => task.IsCompleted);
|
|
isSaveTaskComplete = true;
|
|
yield return null;
|
|
}
|
|
|
|
Texture2D MakeReadableTexture(Texture2D source)
|
|
{
|
|
RenderTexture renderTexture = RenderTexture.GetTemporary(
|
|
source.width,
|
|
source.height,
|
|
0,
|
|
RenderTextureFormat.Default,
|
|
RenderTextureReadWrite.Linear);
|
|
|
|
Graphics.Blit(source, renderTexture);
|
|
RenderTexture previous = RenderTexture.active;
|
|
RenderTexture.active = renderTexture;
|
|
Texture2D readableTexture = new Texture2D(source.width, source.height);
|
|
readableTexture.ReadPixels(new Rect(0, 0, renderTexture.width, renderTexture.height), 0, 0);
|
|
readableTexture.Apply();
|
|
RenderTexture.active = previous;
|
|
RenderTexture.ReleaseTemporary(renderTexture);
|
|
return readableTexture;
|
|
}
|
|
|
|
IEnumerator CoroutineLoadFromLocalData(string path)
|
|
{
|
|
isLoadTaskComplete = false;
|
|
float loadTime = 0;
|
|
|
|
//Task<SaveData> task = LoadAsync(identifier);
|
|
|
|
string filePath = string.IsNullOrEmpty(path) ? Path.Combine(Application.persistentDataPath, identifier) : path;
|
|
if (!System.IO.File.Exists(filePath))
|
|
{
|
|
Debug.Log("No File Found At : " + filePath);
|
|
isLoadTaskComplete = true;
|
|
yield break;
|
|
}
|
|
Task<SaveData> task = Task<SaveData>.Run(() => fileManager.LoadAsync(filePath));
|
|
yield return new WaitUntil(() => task.IsCompleted);
|
|
|
|
if (task.Result == null)
|
|
{
|
|
Debug.LogError("Error on loading local data.");
|
|
Debug.LogError("Error file location : " + filePath);
|
|
isLoadTaskComplete = true;
|
|
yield break;
|
|
}
|
|
saveData = task.Result;
|
|
for (int i = 0; i < saveData.textureDatas.Length; i++)
|
|
{
|
|
TextureData texData = saveData.textureDatas[i];
|
|
sharedMaterial.AddTextureData(texData);
|
|
loadTime += Time.deltaTime;
|
|
if (loadTime > 1.0f / 30.0f)
|
|
{
|
|
loadTime = 0;
|
|
yield return null;
|
|
}
|
|
}
|
|
for (int i = 0; i < saveData.modelDatas.Length; i++)
|
|
{
|
|
SavedModelData modelData = saveData.modelDatas[i];
|
|
string assetName = modelData.attributes.FirstOrDefault(x => x[0].Equals("assetName"))?[1];
|
|
string folderName = modelData.attributes.FirstOrDefault(x => x[0].Equals("folderName"))?[1];
|
|
string createDate = modelData.attributes.FirstOrDefault(x => x[0].Equals("createDate"))?[1];
|
|
string lastRevisionDate = modelData.attributes.FirstOrDefault(x => x[0].Equals("lastRevisionDate"))?[1];
|
|
string uploadDate = modelData.attributes.FirstOrDefault(x => x[0].Equals("uploadDate"))?[1];
|
|
string creator = modelData.attributes.FirstOrDefault(x => x[0].Equals("creator"))?[1];
|
|
string manager = modelData.attributes.FirstOrDefault(x => x[0].Equals("manager"))?[1];
|
|
Texture2D thumbnail = new Texture2D(1, 1);
|
|
thumbnail.LoadImage(saveData.thumbnailDatas[i].data);
|
|
|
|
if (listAssets.Find(x => x.name == assetName) != null)
|
|
{
|
|
continue;
|
|
}
|
|
GameObject newObject = new GameObject(assetName);
|
|
newObject.transform.parent = transform;
|
|
CustomAssetData assetData = newObject.AddComponent<CustomAssetData>();
|
|
assetData.assetName = assetName;
|
|
assetData.folderName = folderName;
|
|
assetData.createDate = createDate;
|
|
assetData.LastRevisionDate = lastRevisionDate;
|
|
assetData.UploadDate = uploadDate;
|
|
assetData.Creator = creator;
|
|
assetData.Manager = manager;
|
|
assetData.loadedObject = modelData.LoadModelData(sharedMaterial);
|
|
assetData.thumbnail = thumbnail;
|
|
assetData.OnLoadComplete();
|
|
listAssets.Add(assetData);
|
|
onLoadFbxFile?.Invoke(assetData.assetName, assetData.folderName, assetData);
|
|
loadTime += Time.deltaTime;
|
|
if (loadTime > 1.0f / 30.0f)
|
|
{
|
|
loadTime = 0;
|
|
yield return null;
|
|
}
|
|
}
|
|
isLoadTaskComplete = true;
|
|
yield return null;
|
|
}
|
|
}
|
|
public class LoadFBXData
|
|
{
|
|
public string path;
|
|
public GameObject loadedGB;
|
|
public bool isLoadComplete;
|
|
public bool isLoadError;
|
|
public List<Transform> drawTransforms;
|
|
public LoadFBXData(string path)
|
|
{
|
|
this.path = path;
|
|
loadedGB = null;
|
|
isLoadComplete = false;
|
|
isLoadError = false;
|
|
drawTransforms = new List<Transform>();
|
|
}
|
|
}
|
|
public class SharedMaterial
|
|
{
|
|
private enum RenderPipelineType
|
|
{
|
|
builtIn, URP, HDRP,
|
|
}
|
|
private Material sharedMaterial;
|
|
private Dictionary<MaterialPropertyData, Material> dicMaterials = new Dictionary<MaterialPropertyData, Material>();
|
|
private Dictionary<string, Texture2D> dicTextures = new Dictionary<string, Texture2D>();
|
|
private Dictionary<Texture2D, string> dicTextureIDs = new Dictionary<Texture2D, string>();
|
|
private RenderPipelineType renderPipelineType = RenderPipelineType.builtIn;
|
|
public List<TextureData> textureDatas = new List<TextureData>();
|
|
public SharedMaterial()
|
|
{
|
|
sharedMaterial = Resources.Load<Material>("Materials/Mat_TrilibBase");
|
|
if (UnityEngine.Rendering.GraphicsSettings.currentRenderPipeline == null)
|
|
{
|
|
renderPipelineType = RenderPipelineType.builtIn;
|
|
}
|
|
else if (UnityEngine.Rendering.GraphicsSettings.currentRenderPipeline.GetType().Name.Contains("UniversalRenderPipeline"))
|
|
{
|
|
renderPipelineType = RenderPipelineType.URP;
|
|
}
|
|
}
|
|
public Material Get(MaterialPropertyData mpd)
|
|
{
|
|
foreach (var kvp in dicMaterials)
|
|
{
|
|
if (AreMaterialSimilar(kvp.Key, mpd)) return kvp.Value;
|
|
}
|
|
Material mat = new Material(sharedMaterial);
|
|
mpd.ApplyToMaterial(mat);
|
|
ApplyTexture(mat, mpd);
|
|
dicMaterials.Add(mpd, mat);
|
|
return mat;
|
|
}
|
|
bool AreMaterialSimilar(MaterialPropertyData mpdA, MaterialPropertyData mpdB)
|
|
{
|
|
if (AreColorsSimilar(mpdA.baseColor, mpdB.baseColor) == false) return false;
|
|
if (AreColorsSimilar(mpdA.emissionColor, mpdB.emissionColor) == false) return false;
|
|
if (AreFloatsSimilar(mpdA.metallic, mpdB.metallic) == false) return false;
|
|
if (AreFloatsSimilar(mpdA.smoothness, mpdB.smoothness) == false) return false;
|
|
if (AreTexturesEqual(mpdA.texUIDs, mpdB.texUIDs) == false) return false;
|
|
return true;
|
|
}
|
|
bool AreFloatsSimilar(float a, float b, float tolerance = 0.001f)
|
|
{
|
|
return Mathf.Abs(a - b) < tolerance;
|
|
}
|
|
bool AreColorsSimilar(Color a, Color b, float tolerance = 0.01f)
|
|
{
|
|
return Mathf.Abs(a.r - b.r) < tolerance &&
|
|
Mathf.Abs(a.g - b.g) < tolerance &&
|
|
Mathf.Abs(a.b - b.b) < tolerance &&
|
|
Mathf.Abs(a.a - b.a) < tolerance;
|
|
}
|
|
bool AreColorsSimilar(float[] a, float[] b, float tolerance = 0.01f)
|
|
{
|
|
return Mathf.Abs(a[0] - b[0]) < tolerance &&
|
|
Mathf.Abs(a[1] - b[1]) < tolerance &&
|
|
Mathf.Abs(a[2] - b[2]) < tolerance &&
|
|
Mathf.Abs(a[3] - b[3]) < tolerance;
|
|
}
|
|
bool AreTexturesEqual(string[] savedData, string[] compareData)
|
|
{
|
|
if (savedData.Length != compareData.Length) return false;
|
|
for (int i = 0; i < compareData.Length; i++)
|
|
{
|
|
if (!savedData[i].Equals(compareData[i]))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
public void SaveTexture(Material material, MaterialPropertyData mpd)
|
|
{
|
|
List<string> listTextureTypes = new List<string>();
|
|
List<string> listTextureUIDs = new List<string>();
|
|
Texture2D albedo = null;
|
|
if (renderPipelineType == RenderPipelineType.builtIn && material.HasProperty("_MainTex"))
|
|
{
|
|
albedo = material.GetTexture("_MainTex") as Texture2D;
|
|
}
|
|
if (renderPipelineType == RenderPipelineType.URP && material.HasProperty("_BaseMap"))
|
|
{
|
|
albedo = material.GetTexture("_BaseMap") as Texture2D;
|
|
}
|
|
if (albedo != null)
|
|
{
|
|
listTextureTypes.Add("Albedo");
|
|
listTextureUIDs.Add(GetTexUID(albedo));
|
|
}
|
|
Texture2D specular = null;
|
|
if (material.HasProperty("_SpecGlossMap"))
|
|
{
|
|
specular = material.GetTexture("_SpecGlossMap") as Texture2D;
|
|
}
|
|
if (specular != null)
|
|
{
|
|
listTextureTypes.Add("Specular");
|
|
listTextureUIDs.Add(GetTexUID(specular));
|
|
}
|
|
Texture2D metallicMap = null;
|
|
if (material.HasProperty("_MetallicGlossMap"))
|
|
{
|
|
metallicMap = material.GetTexture("_MetallicGlossMap") as Texture2D;
|
|
}
|
|
if (metallicMap != null)
|
|
{
|
|
listTextureTypes.Add("Metallic");
|
|
listTextureUIDs.Add(GetTexUID(metallicMap));
|
|
}
|
|
Texture2D normalMap = null;
|
|
if (renderPipelineType == RenderPipelineType.builtIn && material.HasProperty("_BumpMap"))
|
|
{
|
|
normalMap = material.GetTexture("_BumpMap") as Texture2D;
|
|
}
|
|
if (renderPipelineType == RenderPipelineType.URP && material.HasProperty("_NormalMap"))
|
|
{
|
|
normalMap = material.GetTexture("_NormalMap") as Texture2D;
|
|
}
|
|
if (normalMap != null)
|
|
{
|
|
listTextureTypes.Add("Normal");
|
|
listTextureUIDs.Add(GetTexUID(normalMap));
|
|
}
|
|
Texture2D occlusionMap = null;
|
|
if (material.HasProperty("_OcclusionMap"))
|
|
{
|
|
occlusionMap = material.GetTexture("_OcclusionMap") as Texture2D;
|
|
}
|
|
if (occlusionMap != null)
|
|
{
|
|
listTextureTypes.Add("Occlusion");
|
|
listTextureUIDs.Add(GetTexUID(occlusionMap));
|
|
}
|
|
Texture2D emissionMap = null;
|
|
if (material.HasProperty("_EmissionMap"))
|
|
{
|
|
emissionMap = material.GetTexture("_EmissionMap") as Texture2D;
|
|
}
|
|
if (emissionMap != null)
|
|
{
|
|
listTextureTypes.Add("Emission");
|
|
listTextureUIDs.Add(GetTexUID(emissionMap));
|
|
}
|
|
mpd.texTypes = listTextureTypes.ToArray();
|
|
mpd.texUIDs = listTextureUIDs.ToArray();
|
|
}
|
|
void ApplyTexture(Material material, MaterialPropertyData mpd)
|
|
{
|
|
bool hasSpecular = false;
|
|
for (int i = 0; i < mpd.texTypes.Length; i++)
|
|
{
|
|
string type = mpd.texTypes[i];
|
|
string uid = mpd.texUIDs[i];
|
|
Texture2D tex = dicTextures[uid];
|
|
|
|
switch (type)
|
|
{
|
|
case "Albedo":
|
|
{
|
|
if (material.HasProperty("_BaseMap") && tex != null)
|
|
{
|
|
material.SetTexture("_BaseMap", tex);
|
|
}
|
|
else if (material.HasProperty("_MainTex") && tex != null)
|
|
{
|
|
material.SetTexture("_MainTex", tex);
|
|
}
|
|
}
|
|
break;
|
|
case "Specular":
|
|
{
|
|
if (material.HasProperty("_SpecGlossMap") && tex != null)
|
|
{
|
|
material.SetTexture("_SpecGlossMap", tex);
|
|
hasSpecular = true;
|
|
}
|
|
}
|
|
break;
|
|
case "Metallic":
|
|
{
|
|
if (material.HasProperty("_MetallicGlossMap") && tex != null)
|
|
{
|
|
material.SetTexture("_MetallicGlossMap", tex);
|
|
}
|
|
}
|
|
break;
|
|
case "Normal":
|
|
{
|
|
if (material.HasProperty("_NormalMap") && tex != null)
|
|
{
|
|
material.SetTexture("_NormalMap", tex);
|
|
}
|
|
else if (material.HasProperty("_BumpMap") && tex != null)
|
|
{
|
|
material.SetTexture("_BumpMap", tex);
|
|
}
|
|
if (material.HasProperty("_NormalScale"))
|
|
{
|
|
material.SetFloat("_NormalScale", mpd.normalScale);
|
|
}
|
|
else if (material.HasProperty("_BumpScale"))
|
|
{
|
|
material.SetFloat("_BumpScale", mpd.normalScale);
|
|
}
|
|
}
|
|
break;
|
|
case "Occlusion":
|
|
{
|
|
if (material.HasProperty("_OcclusionMap") && tex != null)
|
|
{
|
|
material.SetTexture("_OcclusionMap", tex);
|
|
}
|
|
if (material.HasProperty("_OcclusionStrength"))
|
|
{
|
|
material.SetFloat("_OcclusionStrength", mpd.occlusionStrength);
|
|
}
|
|
}
|
|
break;
|
|
case "Emission":
|
|
{
|
|
if (material.HasProperty("_EmissionMap") && tex != null)
|
|
{
|
|
material.SetTexture("_EmissionMap", tex);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (material.HasProperty("_WorkflowMode"))
|
|
{
|
|
material.SetFloat("_WorkflowMode", hasSpecular ? 1f : 0f);
|
|
}
|
|
}
|
|
public Texture2D GetReadableTexture(Texture texture)
|
|
{
|
|
RenderTexture renderTex = new RenderTexture(texture.width, texture.height, 0);
|
|
Graphics.Blit(texture, renderTex);
|
|
|
|
Texture2D readableTexture = new Texture2D(texture.width, texture.height, TextureFormat.RGBA32, false);
|
|
RenderTexture.active = renderTex;
|
|
readableTexture.ReadPixels(new Rect(0, 0, texture.width, texture.height), 0, 0);
|
|
readableTexture.Apply();
|
|
RenderTexture.active = null;
|
|
|
|
renderTex.Release();
|
|
return readableTexture;
|
|
}
|
|
public string GetTexUID(Texture2D tex)
|
|
{
|
|
string texUID = Guid.NewGuid().ToString();
|
|
if (dicTextureIDs.ContainsKey(tex))
|
|
{
|
|
texUID = dicTextureIDs[tex];
|
|
}
|
|
else
|
|
{
|
|
dicTextureIDs[tex] = texUID;
|
|
Texture2D readableTex = GetReadableTexture(tex);
|
|
//TextureData textureData = new TextureData()
|
|
//{
|
|
// name = tex.name,
|
|
// uid = texUID,
|
|
// data = readableTex.EncodeToPNG()
|
|
//};
|
|
//textureDatas.Add(textureData);
|
|
textureDatas.Add(new TextureData(tex.name, texUID, readableTex.EncodeToPNG()));
|
|
}
|
|
return texUID;
|
|
}
|
|
public void AddTextureData(TextureData texData)
|
|
{
|
|
if (dicTextures.ContainsKey(texData.uid))
|
|
{
|
|
//The odds are almost zero.
|
|
//It means save logic is wrong.
|
|
Debug.LogError("Duplicate Texture : " + texData.name + ", GUID : " + texData.uid);
|
|
}
|
|
else
|
|
{
|
|
Texture2D tex = new Texture2D(2, 2);
|
|
ImageConversion.LoadImage(tex, texData.data);
|
|
dicTextures[texData.uid] = tex;
|
|
}
|
|
}
|
|
}
|
|
[MessagePackObject]
|
|
public class SaveData
|
|
{
|
|
[Key(0)]
|
|
public SavedModelData[] modelDatas;
|
|
[Key(1)]
|
|
public TextureData[] textureDatas;
|
|
[Key(2)]
|
|
public ThumbnailData[] thumbnailDatas;
|
|
}
|
|
[MessagePackObject]
|
|
public class SavedModelData
|
|
{
|
|
[Key(0)]
|
|
public string[][] attributes;
|
|
//public string assetName;
|
|
//[Key(1)]
|
|
//public string folderName;
|
|
//[Key(2)]
|
|
[Key(1)]
|
|
public ModelData[] models;
|
|
public void SaveData(GameObject modelObject, SharedMaterial sharedMaterial)
|
|
{
|
|
MeshRenderer[] meshRenderers = modelObject.transform.GetComponentsInChildren<MeshRenderer>(true);
|
|
models = new ModelData[meshRenderers.Length];
|
|
for (int i = 0; i < meshRenderers.Length; i++)
|
|
{
|
|
MeshFilter meshFilter = meshRenderers[i].GetComponent<MeshFilter>();
|
|
models[i] = new ModelData();
|
|
models[i].transformData = new TransformData();
|
|
models[i].modelComponentName = meshFilter.gameObject.name;
|
|
models[i].transformData.SetData(meshRenderers[i].transform);
|
|
models[i].mesh = new SerializableMesh();
|
|
models[i].mesh.SetData(meshFilter.mesh);
|
|
List<MaterialPropertyData> listMaterialPropertyDatas = new List<MaterialPropertyData>();
|
|
for (int j = 0; j < meshRenderers[i].materials.Length; j++)
|
|
{
|
|
MaterialPropertyData materialPropertyData = new MaterialPropertyData();
|
|
materialPropertyData.SetData(meshRenderers[i].materials[j]);
|
|
sharedMaterial.SaveTexture(meshRenderers[i].materials[j], materialPropertyData);
|
|
listMaterialPropertyDatas.Add(materialPropertyData);
|
|
}
|
|
models[i].materialProperties = listMaterialPropertyDatas.ToArray();
|
|
}
|
|
}
|
|
public GameObject LoadModelData(SharedMaterial sharedMaterial)
|
|
{
|
|
string assetName = attributes.FirstOrDefault(x => x[0].Equals("assetName"))?[1];
|
|
GameObject modelObject = null;
|
|
Dictionary<int, GameObject> partList = new Dictionary<int, GameObject>();
|
|
|
|
foreach (ModelData model in models)
|
|
{
|
|
GameObject part = new GameObject(model.modelComponentName);
|
|
model.transformData.ApplyToTransform(part.transform);
|
|
if (model.mesh != null)
|
|
{
|
|
MeshFilter meshFilter = part.AddComponent<MeshFilter>();
|
|
MeshRenderer meshRenderer = part.AddComponent<MeshRenderer>();
|
|
meshFilter.mesh = model.mesh.ToMesh();
|
|
Material[] materials = new Material[model.materialProperties.Length];
|
|
for (int j = 0; j < materials.Length; j++)
|
|
{
|
|
materials[j] = new Material(sharedMaterial.Get(model.materialProperties[j]));
|
|
model.materialProperties[j].ApplyToMaterial(materials[j]);
|
|
}
|
|
meshRenderer.materials = materials;
|
|
}
|
|
|
|
partList[model.id] = part;
|
|
|
|
if (modelObject == null)
|
|
{
|
|
modelObject = part;
|
|
}
|
|
}
|
|
|
|
foreach (ModelData model in models)
|
|
{
|
|
if (model.parentId != -1 && partList.ContainsKey(model.parentId))
|
|
{
|
|
partList[model.id].transform.SetParent(partList[model.parentId].transform, false);
|
|
}
|
|
}
|
|
return modelObject;
|
|
}
|
|
}
|
|
[MessagePackObject]
|
|
public class ModelData
|
|
{
|
|
[Key(0)]
|
|
public int id;
|
|
[Key(1)]
|
|
public int parentId;
|
|
[Key(2)]
|
|
public string modelComponentName;
|
|
[Key(3)]
|
|
public TransformData transformData;
|
|
[Key(4)]
|
|
public SerializableMesh mesh;
|
|
[Key(5)]
|
|
public MaterialPropertyData[] materialProperties;
|
|
}
|
|
[MessagePackObject]
|
|
public class TransformData
|
|
{
|
|
[Key(0)]
|
|
public SerializableVector3 position;
|
|
[Key(1)]
|
|
public SerializableQuaternion rotation;
|
|
[Key(2)]
|
|
public SerializableVector3 scale;
|
|
public void SetData(Transform transform)
|
|
{
|
|
position = new SerializableVector3();
|
|
position.FromVector3(transform.position);
|
|
rotation = new SerializableQuaternion();
|
|
rotation.FromQuaternion(transform.rotation);
|
|
scale = new SerializableVector3();
|
|
scale.FromVector3(transform.lossyScale);
|
|
}
|
|
|
|
public void ApplyToTransform(Transform transform)
|
|
{
|
|
transform.position = position.ToVector3();
|
|
transform.rotation = rotation.ToQuaternion();
|
|
transform.localScale = scale.ToVector3();
|
|
}
|
|
}
|
|
[MessagePackObject]
|
|
public class SerializableMesh
|
|
{
|
|
[Key(0)]
|
|
public SerializableVector3[] vertices;
|
|
[Key(1)]
|
|
public SerializableVector3[] normals;
|
|
[Key(2)]
|
|
public SerializableVector4[] tangents;
|
|
[Key(3)]
|
|
public SerializableVector2[] uv;
|
|
//[Key("Triangles")]
|
|
//public int[] triangles;
|
|
[Key(4)]
|
|
public SubmeshData[] submeshes;
|
|
public void SetData(Mesh mesh)
|
|
{
|
|
vertices = SerializableVector3.FromVector3Array(mesh.vertices);
|
|
normals = SerializableVector3.FromVector3Array(mesh.normals);
|
|
tangents = SerializableVector4.FromVector4Array(mesh.tangents);
|
|
uv = SerializableVector2.FromVector2Array(mesh.uv);
|
|
//triangles = mesh.triangles;
|
|
List<SubmeshData> submeshList = new List<SubmeshData>();
|
|
for (int i = 0; i < mesh.subMeshCount; i++)
|
|
{
|
|
SubmeshData submesh = new SubmeshData
|
|
{
|
|
materialIndex = i,
|
|
triangles = mesh.GetTriangles(i)
|
|
};
|
|
submeshList.Add(submesh);
|
|
}
|
|
submeshes = submeshList.ToArray();
|
|
}
|
|
public Mesh ToMesh()
|
|
{
|
|
Mesh newMesh = new Mesh();
|
|
newMesh.indexFormat = vertices.Length > 65535 ? IndexFormat.UInt32 : IndexFormat.UInt16;
|
|
newMesh.vertices = SerializableVector3.ToVector3Array(vertices);
|
|
newMesh.normals = SerializableVector3.ToVector3Array(normals);
|
|
newMesh.tangents = SerializableVector4.ToVector4Array(tangents);
|
|
newMesh.uv = SerializableVector2.ToVector2Array(uv);
|
|
//newMesh.triangles = triangles;
|
|
newMesh.subMeshCount = submeshes.Length;
|
|
for (int i = 0; i < submeshes.Length; i++)
|
|
{
|
|
newMesh.SetTriangles(submeshes[i].triangles, i);
|
|
}
|
|
newMesh.RecalculateBounds();
|
|
return newMesh;
|
|
}
|
|
}
|
|
[MessagePackObject]
|
|
public class SubmeshData
|
|
{
|
|
[Key(0)]
|
|
public int materialIndex;
|
|
[Key(1)]
|
|
public int[] triangles;
|
|
}
|
|
[MessagePackObject]
|
|
public struct SerializableVector3
|
|
{
|
|
[Key(0)]
|
|
public float x;
|
|
[Key(1)]
|
|
public float y;
|
|
[Key(2)]
|
|
public float z;
|
|
|
|
[SerializationConstructor]
|
|
public SerializableVector3(float x, float y, float z)
|
|
{
|
|
this.x = x;
|
|
this.y = y;
|
|
this.z = z;
|
|
}
|
|
public void FromVector3(Vector3 v) { x = v.x; y = v.y; z = v.z; }
|
|
public Vector3 ToVector3() => new Vector3(x, y, z);
|
|
|
|
public static SerializableVector3[] FromVector3Array(Vector3[] array)
|
|
{
|
|
return array?.Select(v => new SerializableVector3(v.x, v.y, v.z)).ToArray();
|
|
}
|
|
|
|
public static Vector3[] ToVector3Array(SerializableVector3[] array)
|
|
{
|
|
return array?.Select(v => v.ToVector3()).ToArray();
|
|
}
|
|
}
|
|
[MessagePackObject]
|
|
public struct SerializableVector4
|
|
{
|
|
[Key(0)]
|
|
public float x;
|
|
[Key(1)]
|
|
public float y;
|
|
[Key(2)]
|
|
public float z;
|
|
[Key(3)]
|
|
public float w;
|
|
[SerializationConstructor]
|
|
public SerializableVector4(float x, float y, float z, float w)
|
|
{
|
|
this.x = x;
|
|
this.y = y;
|
|
this.z = z;
|
|
this.w = w;
|
|
}
|
|
public void FromVector4(Vector4 v) { x = v.x; y = v.y; z = v.z; w = v.w; }
|
|
public Vector4 ToVector4() => new Vector4(x, y, z, w);
|
|
|
|
public static SerializableVector4[] FromVector4Array(Vector4[] array)
|
|
{
|
|
return array?.Select(v => new SerializableVector4(v.x, v.y, v.z, v.w)).ToArray();
|
|
}
|
|
|
|
public static Vector4[] ToVector4Array(SerializableVector4[] array)
|
|
{
|
|
return array?.Select(v => v.ToVector4()).ToArray();
|
|
}
|
|
}
|
|
[MessagePackObject]
|
|
public struct SerializableVector2
|
|
{
|
|
[Key(0)]
|
|
public float x;
|
|
[Key(1)]
|
|
public float y;
|
|
|
|
[SerializationConstructor]
|
|
public SerializableVector2(float x, float y)
|
|
{
|
|
this.x = x;
|
|
this.y = y;
|
|
}
|
|
public void FromVector2(Vector2 v) { x = v.x; y = v.y; }
|
|
public Vector2 ToVector2() => new Vector2(x, y);
|
|
|
|
public static SerializableVector2[] FromVector2Array(Vector2[] array)
|
|
{
|
|
return array?.Select(v => new SerializableVector2(v.x, v.y)).ToArray();
|
|
}
|
|
|
|
public static Vector2[] ToVector2Array(SerializableVector2[] array)
|
|
{
|
|
return array?.Select(v => v.ToVector2()).ToArray();
|
|
}
|
|
}
|
|
[MessagePackObject]
|
|
public struct SerializableQuaternion
|
|
{
|
|
[Key(0)]
|
|
public float x;
|
|
[Key(1)]
|
|
public float y;
|
|
[Key(2)]
|
|
public float z;
|
|
[Key(3)]
|
|
public float w;
|
|
|
|
public void FromQuaternion(Quaternion q) { x = q.x; y = q.y; z = q.z; w = q.w; }
|
|
public Quaternion ToQuaternion() => new Quaternion(x, y, z, w);
|
|
}
|
|
[MessagePackObject]
|
|
public class MaterialPropertyData
|
|
{
|
|
[Key(0)]
|
|
public float[] baseColor;
|
|
[Key(1)]
|
|
public float metallic;
|
|
[Key(2)]
|
|
public float smoothness;
|
|
[Key(3)]
|
|
public float[] emissionColor;
|
|
[Key(4)]
|
|
public int renderMode;
|
|
[Key(5)]
|
|
public int surfaceType;
|
|
[Key(6)]
|
|
public int blendMode;
|
|
[Key(7)]
|
|
public float occlusionStrength;
|
|
[Key(8)]
|
|
public float normalScale;
|
|
[Key(9)]
|
|
public string[] texTypes;
|
|
[Key(10)]
|
|
public string[] texUIDs;
|
|
|
|
public void SetData(Material material)
|
|
{
|
|
Color bc = material.HasProperty("_BaseColor") ? material.GetColor("_BaseColor") :
|
|
material.HasProperty("_Color") ? material.GetColor("_Color") :
|
|
Color.white;
|
|
baseColor = new float[4] { bc.r, bc.g, bc.b, bc.a };
|
|
metallic = material.HasProperty("_Metallic") ? material.GetFloat("_Metallic") : 0;
|
|
smoothness = material.HasProperty("_Smoothness") ? material.GetFloat("_Smoothness") :
|
|
material.HasProperty("_Glossiness") ? material.GetFloat("_Glossiness") : 0;
|
|
Color ec = material.HasProperty("_EmissionColor") ? material.GetColor("_EmissionColor") : Color.white;
|
|
emissionColor = new float[4] { ec.r, ec.g, ec.b, ec.a };
|
|
renderMode = material.HasProperty("_Mode") ? (int)material.GetFloat("_Mode") : -1;
|
|
surfaceType = material.HasProperty("_Surface") ? (int)material.GetFloat("_Surface") : -1;
|
|
blendMode = material.HasProperty("_Blend") ? (int)material.GetFloat("_Blend") : -1;
|
|
occlusionStrength = material.HasProperty("_OcclusionStrength") ? material.GetFloat("_OcclusionStrength") : 0;
|
|
normalScale = material.HasProperty("_NormalScale") ? material.GetFloat("_NormalScale") :
|
|
material.HasProperty("_BumpScale") ? material.GetFloat("_BumpScale") : 0;
|
|
}
|
|
|
|
public void ApplyToMaterial(Material material)
|
|
{
|
|
bool isURP = material.shader.name.Contains("Universal Render Pipeline");
|
|
|
|
if (material.HasProperty("_BaseColor"))
|
|
{
|
|
material.SetColor("_BaseColor", new Color(baseColor[0], baseColor[1], baseColor[2], baseColor[3]));
|
|
}
|
|
else if (material.HasProperty("_Color"))
|
|
{
|
|
material.SetColor("_Color", new Color(baseColor[0], baseColor[1], baseColor[2], baseColor[3]));
|
|
}
|
|
if (material.HasProperty("_Metallic"))
|
|
{
|
|
material.SetFloat("_Metallic", metallic);
|
|
}
|
|
if (material.HasProperty("_Smoothness"))
|
|
{
|
|
material.SetFloat("_Smoothness", smoothness);
|
|
}
|
|
else if (material.HasProperty("_Glossiness"))
|
|
{
|
|
material.SetFloat("_Glossiness", smoothness);
|
|
}
|
|
if (material.HasProperty("_EmissionColor"))
|
|
{
|
|
material.SetColor("_EmissionColor", new Color(emissionColor[0], emissionColor[1], emissionColor[2], emissionColor[3]));
|
|
}
|
|
if (material.HasProperty("_Mode") && renderMode >= 0)
|
|
{
|
|
material.SetFloat("_Mode", renderMode);
|
|
}
|
|
if (material.HasProperty("_Surface") && surfaceType >= 0)
|
|
{
|
|
material.SetFloat("_Surface", surfaceType);
|
|
}
|
|
if (material.HasProperty("_Blend") && blendMode >= 0)
|
|
{
|
|
material.SetFloat("_Blend", blendMode);
|
|
}
|
|
|
|
if (isURP)
|
|
{
|
|
if (renderMode == 3)
|
|
{
|
|
material.SetFloat("_Surface", 1);
|
|
}
|
|
if (surfaceType == 1 || renderMode == 3)
|
|
{
|
|
material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent;
|
|
material.SetFloat("_ZWrite", 0);
|
|
material.SetFloat("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
|
|
material.SetFloat("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//Transparent
|
|
if (renderMode == 3 || surfaceType == 1)
|
|
{
|
|
material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent;
|
|
material.SetFloat("_ZWrite", 0);
|
|
material.SetFloat("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
|
|
material.SetFloat("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
|
|
material.EnableKeyword("_ALPHABLEND_ON");
|
|
material.DisableKeyword("_ALPHATEST_ON");
|
|
material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
|
|
}
|
|
//CutOut
|
|
else if (renderMode == 1)
|
|
{
|
|
material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.AlphaTest;
|
|
material.EnableKeyword("_ALPHATEST_ON");
|
|
material.DisableKeyword("_ALPHABLEND_ON");
|
|
material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
|
|
}
|
|
//Opaque
|
|
else
|
|
{
|
|
material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Geometry;
|
|
material.SetFloat("_ZWrite", 1);
|
|
material.SetFloat("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
|
|
material.SetFloat("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
|
|
material.DisableKeyword("_ALPHATEST_ON");
|
|
material.DisableKeyword("_ALPHABLEND_ON");
|
|
material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
[MessagePackObject]
|
|
public class TextureData
|
|
{
|
|
[Key(0)]
|
|
public string name;
|
|
[Key(1)]
|
|
public string uid;
|
|
[Key(2)]
|
|
public byte[] data;
|
|
|
|
[SerializationConstructor]
|
|
public TextureData(string name, string uid, byte[] data)
|
|
{
|
|
this.name = name;
|
|
this.uid = uid;
|
|
this.data = data;
|
|
}
|
|
}
|
|
[MessagePackObject]
|
|
public class ThumbnailData
|
|
{
|
|
[Key(0)]
|
|
public byte[] data;
|
|
|
|
[SerializationConstructor]
|
|
public ThumbnailData(byte[] data)
|
|
{
|
|
this.data = data;
|
|
}
|
|
}
|
|
}
|
|
public class MessagePackFileManager<T>
|
|
{
|
|
public void Initialize()
|
|
{
|
|
StaticCompositeResolver.Instance.Register(
|
|
GeneratedResolver.Instance,
|
|
StandardResolver.Instance
|
|
);
|
|
MessagePackSerializer.DefaultOptions = MessagePackSerializerOptions.Standard.WithResolver(
|
|
StaticCompositeResolver.Instance
|
|
);
|
|
}
|
|
public async Task<T> LoadAsync(string filePath)
|
|
{
|
|
var lz4Option = MessagePackSerializerOptions.Standard.
|
|
WithCompression(MessagePackCompression.Lz4Block).
|
|
WithResolver(StaticCompositeResolver.Instance);
|
|
byte[] readByte = null;
|
|
T deserailze = default(T);
|
|
try
|
|
{
|
|
readByte = await File.ReadAllBytesAsync(filePath);
|
|
deserailze = MessagePackSerializer.Deserialize<T>(readByte, lz4Option);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.LogError($"Task Error: {ex.Message}\n{ex.StackTrace}");
|
|
int byteLength = 0;
|
|
if (readByte != null)
|
|
{
|
|
byteLength = (int)readByte.Length;
|
|
}
|
|
Debug.LogError("Read Byte Size : " + byteLength.ToString());
|
|
}
|
|
return deserailze;
|
|
}
|
|
|
|
public async Task SaveAsync(string filePath, T data)
|
|
{
|
|
var lz4Option = MessagePackSerializerOptions.Standard.
|
|
WithCompression(MessagePackCompression.Lz4Block).
|
|
WithResolver(StaticCompositeResolver.Instance);
|
|
try
|
|
{
|
|
byte[] bytes = MessagePackSerializer.Serialize<T>(data, lz4Option);
|
|
await File.WriteAllBytesAsync(filePath, bytes);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.LogError($"Task Error: {ex.Message}\n{ex.StackTrace}");
|
|
}
|
|
}
|
|
} |