991 lines
38 KiB
C#
991 lines
38 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;
|
|
|
|
namespace XED.Asset
|
|
{
|
|
public class SaveLoadFBXData : MonoBehaviour
|
|
{
|
|
public string identifier;
|
|
public string password;
|
|
public UnityEvent<string, string, CustomAssetData> onLoadFbxFile;
|
|
public UnityEvent<string> onRemoveFbxFile;
|
|
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>();
|
|
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)
|
|
{
|
|
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;
|
|
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;
|
|
}
|
|
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 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);
|
|
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.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>();
|
|
for (int i = 0; i < assetDatas.Count; i++)
|
|
{
|
|
CustomAssetData assetData = assetDatas[i];
|
|
SavedModelData modelData = new SavedModelData();
|
|
modelData.SaveData(assetData.loadedObject, sharedMaterial);
|
|
modelData.assetName = assetData.assetName;
|
|
modelData.folderName = assetData.folderName;
|
|
savedModels.Add(modelData);
|
|
}
|
|
saveData.modelDatas = savedModels.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;
|
|
}
|
|
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.");
|
|
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];
|
|
if (listAssets.Find(x => x.name == modelData.assetName) != null)
|
|
{
|
|
continue;
|
|
}
|
|
GameObject newObject = new GameObject(name);
|
|
newObject.transform.parent = transform;
|
|
CustomAssetData assetData = newObject.AddComponent<CustomAssetData>();
|
|
assetData.assetName = modelData.assetName;
|
|
assetData.folderName = modelData.folderName;
|
|
assetData.loadedObject = modelData.LoadModelData(sharedMaterial);
|
|
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 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>();
|
|
public List<TextureData> textureDatas = new List<TextureData>();
|
|
public SharedMaterial()
|
|
{
|
|
sharedMaterial = Resources.Load<Material>("Materials/Mat_TrilibBase");
|
|
}
|
|
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 (material.HasProperty("_MainTex"))
|
|
{
|
|
albedo = material.GetTexture("_MainTex") as Texture2D;
|
|
}
|
|
else if (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 (material.HasProperty("_BumpMap"))
|
|
{
|
|
normalMap = material.GetTexture("_BumpMap") as Texture2D;
|
|
}
|
|
else if (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);
|
|
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("ModelData")]
|
|
public SavedModelData[] modelDatas;
|
|
[Key("TextureData")]
|
|
public TextureData[] textureDatas;
|
|
}
|
|
[MessagePackObject]
|
|
public class SavedModelData
|
|
{
|
|
[Key("AssetName")]
|
|
public string assetName;
|
|
[Key("FolderName")]
|
|
public string folderName;
|
|
[Key("ModelArray")]
|
|
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].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)
|
|
{
|
|
GameObject modelObject = new GameObject(assetName);
|
|
for (int i = 0; i < models.Length; i++)
|
|
{
|
|
ModelData model = models[i];
|
|
GameObject part = new GameObject("part" + i);
|
|
part.transform.parent = modelObject.transform;
|
|
MeshFilter meshFilter = part.AddComponent<MeshFilter>();
|
|
MeshRenderer meshRenderer = part.AddComponent<MeshRenderer>();
|
|
model.transformData.ApplyToTransform(part.transform);
|
|
meshFilter.mesh = model.mesh.ToMesh();
|
|
Material[] materials = new Material[model.materialProperties.Length];
|
|
for (int j = 0; j < materials.Length; j++)
|
|
{
|
|
materials[j] = sharedMaterial.Get(model.materialProperties[j]);
|
|
//materials[j] = new Material(sharedMaterial.Get(model.materialProperties[j]));
|
|
//model.materialProperties[j].ApplyToMaterial(materials[j]);
|
|
}
|
|
meshRenderer.materials = materials;
|
|
}
|
|
return modelObject;
|
|
}
|
|
}
|
|
[MessagePackObject]
|
|
public class ModelData
|
|
{
|
|
[Key("TransformData")]
|
|
public TransformData transformData;
|
|
[Key("MeshData")]
|
|
public SerializableMesh mesh;
|
|
[Key("MaterialData")]
|
|
public MaterialPropertyData[] materialProperties;
|
|
}
|
|
[MessagePackObject]
|
|
public class TransformData
|
|
{
|
|
[Key("Position")]
|
|
public SerializableVector3 position;
|
|
[Key("Rotation")]
|
|
public SerializableQuaternion rotation;
|
|
[Key("Scale")]
|
|
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("Vertices")]
|
|
public SerializableVector3[] vertices;
|
|
[Key("Normals")]
|
|
public SerializableVector3[] normals;
|
|
[Key("UVs")]
|
|
public SerializableVector2[] uv;
|
|
[Key("Submeshes")]
|
|
public SubmeshData[] submeshes;
|
|
public void SetData(Mesh mesh)
|
|
{
|
|
vertices = SerializableVector3.FromVector3Array(mesh.vertices);
|
|
normals = SerializableVector3.FromVector3Array(mesh.normals);
|
|
uv = SerializableVector2.FromVector2Array(mesh.uv);
|
|
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.vertices = SerializableVector3.ToVector3Array(vertices);
|
|
newMesh.normals = SerializableVector3.ToVector3Array(normals);
|
|
newMesh.uv = SerializableVector2.ToVector2Array(uv);
|
|
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("MaterialIndex")]
|
|
public int materialIndex;
|
|
[Key("Triangles")]
|
|
public int[] triangles;
|
|
}
|
|
[MessagePackObject]
|
|
public struct SerializableVector3
|
|
{
|
|
[Key("V3x")]
|
|
public float x;
|
|
[Key("V3y")]
|
|
public float y;
|
|
[Key("V3z")]
|
|
public float z;
|
|
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 SerializableVector2
|
|
{
|
|
[Key("V2x")]
|
|
public float x;
|
|
[Key("V2y")]
|
|
public float y;
|
|
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("Qx")]
|
|
public float x;
|
|
[Key("Qy")]
|
|
public float y;
|
|
[Key("Qz")]
|
|
public float z;
|
|
[Key("Qw")]
|
|
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("BaseColor")]
|
|
public float[] baseColor;
|
|
[Key("Metallic")]
|
|
public float metallic;
|
|
[Key("Smoothness")]
|
|
public float smoothness;
|
|
[Key("Emission")]
|
|
public float[] emissionColor;
|
|
[Key("RenderMode")]
|
|
public int renderMode;
|
|
[Key("SurfaceType")]
|
|
public int surfaceType;
|
|
[Key("BlendMode")]
|
|
public int blendMode;
|
|
[Key("OcclusionStrength")]
|
|
public float occlusionStrength;
|
|
[Key("NormalScale")]
|
|
public float normalScale;
|
|
[Key("TextureTypes")]
|
|
public string[] texTypes;
|
|
[Key("TextureUIDs")]
|
|
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("TextureName")]
|
|
public string name;
|
|
[Key("TextureUID")]
|
|
public string uid;
|
|
[Key("TextureData")]
|
|
public byte[] data;
|
|
public TextureData(string name, string uid, byte[] data)
|
|
{
|
|
this.name = name;
|
|
this.uid = uid;
|
|
this.data = data;
|
|
}
|
|
}
|
|
}
|
|
public class MessagePackFileManager<T>
|
|
{
|
|
public async Task<T> LoadAsync(string filePath)
|
|
{
|
|
var lz4Option = MessagePackSerializerOptions.Standard.WithCompression(MessagePackCompression.Lz4Block);
|
|
byte[] readByte;
|
|
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}");
|
|
}
|
|
return deserailze;
|
|
}
|
|
|
|
public async Task SaveAsync(string filePath, T data)
|
|
{
|
|
var lz4Option = MessagePackSerializerOptions.Standard.WithCompression(MessagePackCompression.Lz4Block);
|
|
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}");
|
|
}
|
|
}
|
|
} |