Files
Studio/Assets/Scripts/Studio/AssetTool/SaveLoadFBXData.cs
geondo55 6c2c090329 복구
2025-03-20 16:58:32 +09:00

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}");
}
}
}