using System; using System.Collections.Generic; using System.Runtime.InteropServices; using Unity.Collections; using Unity.HLODSystem.Utils; using UnityEditor; using UnityEngine; using UnityEngine.Experimental.Rendering; using UnityEngine.Rendering; namespace Unity.HLODSystem { [Serializable] public class HLODData { [Serializable] public struct TextureCompressionData { [SerializeField] public TextureFormat PCTextureFormat; [SerializeField] public TextureFormat WebGLTextureFormat; [SerializeField] public TextureFormat AndroidTextureFormat; [SerializeField] public TextureFormat iOSTextureFormat; [SerializeField] public TextureFormat tvOSTextureFormat; } [Serializable] public struct SerializableMesh { [SerializeField] private string m_name; [SerializeField] private byte[] m_vertices; [SerializeField] private byte[] m_normals; [SerializeField] private byte[] m_tangents; [SerializeField] private byte[] m_uvs; [SerializeField] private byte[] m_uvs2; [SerializeField] private byte[] m_uvs3; [SerializeField] private byte[] m_uvs4; [SerializeField] private byte[] m_colors; [SerializeField] private List m_indices; public string Name { set { m_name = value; } get { return m_name; } } private static byte[] ArrayToBytes(T[] arr) where T : struct { int dataSize = Marshal.SizeOf(); byte[] buffer = new byte[dataSize * arr.Length]; IntPtr ptr = Marshal.AllocHGlobal(dataSize); for (int i = 0; i < arr.Length; ++i) { Marshal.StructureToPtr(arr[i], ptr, false); Marshal.Copy(ptr, buffer, i * dataSize, dataSize); } Marshal.FreeHGlobal(ptr); return buffer; } private T[] BytesToArray(Byte[] bytes) where T : struct { int dataSize = Marshal.SizeOf(); T[] array = new T[bytes.Length / dataSize]; IntPtr ptr = Marshal.AllocHGlobal(dataSize); for (int i = 0; i < array.Length; ++i) { Marshal.Copy(bytes, i * dataSize, ptr, dataSize); array[i] = Marshal.PtrToStructure(ptr); } Marshal.FreeHGlobal(ptr); return array; } public void From(WorkingMesh mesh) { m_name = mesh.name; m_vertices = ArrayToBytes(mesh.vertices); m_normals = ArrayToBytes(mesh.normals); m_tangents = ArrayToBytes(mesh.tangents); m_uvs = ArrayToBytes(mesh.uv); m_uvs2 = ArrayToBytes(mesh.uv2); m_uvs3 = ArrayToBytes(mesh.uv3); m_uvs4 = ArrayToBytes(mesh.uv4); m_colors = ArrayToBytes(mesh.colors); m_indices = new List(); for (int i = 0; i < mesh.subMeshCount; ++i) { m_indices.Add(mesh.GetTriangles(i)); } } public Mesh To() { Mesh mesh = new Mesh(); mesh.name = m_name; if (m_vertices.Length > 65535) mesh.indexFormat = IndexFormat.UInt32; mesh.vertices = BytesToArray(m_vertices); mesh.normals = BytesToArray(m_normals); mesh.tangents = BytesToArray(m_tangents); mesh.uv = BytesToArray(m_uvs); mesh.uv2 = BytesToArray(m_uvs2); mesh.uv3 = BytesToArray(m_uvs3); mesh.uv4 = BytesToArray(m_uvs4); mesh.colors = BytesToArray(m_colors); mesh.subMeshCount = m_indices.Count; for (int i = 0; i < m_indices.Count; ++i) { mesh.SetTriangles(m_indices[i], i); } return mesh; } public int GetSpaceUsage() { int usage = 0; usage += m_vertices.Length; usage += m_normals.Length; usage += m_tangents.Length; usage += m_uvs.Length; usage += m_uvs2.Length; usage += m_uvs3.Length; usage += m_uvs4.Length; usage += m_colors.Length; for ( int i = 0; i < m_indices.Count; ++i ) { usage += m_indices[i].Length * sizeof(int); } return usage; } } [Serializable] public struct SerializableTexture { [SerializeField] private string m_name; [SerializeField] private string m_textureName; [SerializeField] private GraphicsFormat m_format; [SerializeField] private TextureWrapMode m_wrapMode; [SerializeField] private int m_width; [SerializeField] private int m_height; [SerializeField] private byte[] m_bytes; public string Name { set { m_name = value; } get { return m_name; } } public string TextureName { get { return m_textureName; } } public int Height { get { return m_height; } } public int Width { get { return m_width; } } public GraphicsFormat GraphicsFormat { get { return m_format; } } public TextureWrapMode WrapMode { get { return m_wrapMode; } } public int BytesLength { get { if (m_bytes == null) return 0; return m_bytes.Length; } } public void From(Texture2D texture) { m_textureName = texture.name; m_format = texture.graphicsFormat; m_wrapMode = texture.wrapMode; m_width = texture.width; m_height = texture.height; m_bytes = texture.EncodeToPNG(); } public Texture2D To() { var textureFormat = GraphicsFormatUtility.GetTextureFormat(m_format); var srgb = GraphicsFormatUtility.IsSRGBFormat(m_format); Texture2D texture = new Texture2D(m_width, m_height, textureFormat, true, !srgb); texture.name = m_textureName; texture.LoadImage(m_bytes); texture.wrapMode = m_wrapMode; texture.Apply(); return texture; } } [Serializable] public class SerializableMaterial { [SerializeField] private string m_name; [SerializeField] private string m_id; [SerializeField] private string m_assetGuid; [SerializeField] private string m_jsonData; [SerializeField] private List m_textures; public string ID { get { return m_id; } } public void AddTexture(SerializableTexture texture) { if (m_textures == null) m_textures = new List(); m_textures.Add(texture); } public int GetTextureCount() { if (m_textures == null) return 0; return m_textures.Count; } public SerializableTexture GetTexture(int index) { return m_textures[index]; } public void From(WorkingMaterial material) { m_name = material.Name; bool needWrite = material.NeedWrite(); if (needWrite) { Material mat = material.ToMaterial(); m_jsonData = EditorJsonUtility.ToJson(mat); m_assetGuid = ""; } else { m_jsonData = ""; string path = AssetDatabase.GetAssetPath(material.InstanceID); m_assetGuid = AssetDatabase.AssetPathToGUID(path); } m_id = material.Guid; } public Material To() { if (string.IsNullOrEmpty(m_assetGuid)) { Material mat = new Material(Shader.Find("Standard")); EditorJsonUtility.FromJsonOverwrite(m_jsonData, mat); mat.name = m_name; return mat; } else { string path = AssetDatabase.GUIDToAssetPath(m_assetGuid); var objects = AssetDatabase.LoadAllAssetsAtPath(path); for (int i = 0; i < objects.Length; ++i) { Material mat = objects[i] as Material; if (mat == null) continue; if (mat.name == m_name) return mat; } return AssetDatabase.LoadAssetAtPath(path); } } } [Serializable] public class SerializableObject { [SerializeField] private string m_name; [SerializeField] private SerializableMesh m_mesh; [SerializeField] private List m_materialIds = new List(); [SerializeField] private List m_materialNames = new List(); [SerializeField] private LightProbeUsage m_lightProbeUsage; public string Name { set { m_name = value; } get { return m_name; } } public LightProbeUsage LightProbeUsage => m_lightProbeUsage; public SerializableMesh GetMesh() { return m_mesh; } public List GetMaterialIds() { return m_materialIds; } public List GetMaterialNames() { return m_materialNames; } public void From(WorkingObject obj) { Name = obj.Name; m_mesh.From(obj.Mesh); m_materialIds = new List(); m_materialNames = new List(); m_lightProbeUsage = obj.LightProbeUsage; for (int i = 0; i < obj.Materials.Count; ++i) { m_materialIds.Add(obj.Materials[i].Guid); m_materialNames.Add(obj.Materials[i].Name); } } } [Serializable] public struct SerializableVector3 { [SerializeField] public float X; [SerializeField] public float Y; [SerializeField] public float Z; public SerializableVector3(Vector3 vector3) { X = vector3.x; Y = vector3.y; Z = vector3.z; } public Vector3 To() { return new Vector3(X, Y, Z); } } [Serializable] public struct SerializableQuaternion { [SerializeField] public float X; [SerializeField] public float Y; [SerializeField] public float Z; [SerializeField] public float W; public SerializableQuaternion(Quaternion quaternion) { X = quaternion.x; Y = quaternion.y; Z = quaternion.z; W = quaternion.w; } public Quaternion To() { return new Quaternion(X, Y, Z, W); } } [Serializable] public class SerializableCollider { [SerializeField] string m_name; [SerializeField] string m_type; [SerializeField] SerializableVector3 m_position; [SerializeField] SerializableQuaternion m_rotation; [SerializeField] SerializableVector3 m_scale; [SerializeField] SerializableDynamicObject m_parameters; public string Name { get => m_name; set => m_name = value; } public void From(WorkingCollider collider) { m_type = collider.Type; m_position = new SerializableVector3(collider.Position); m_rotation = new SerializableQuaternion(collider.Rotation); m_scale = new SerializableVector3(collider.Scale); m_parameters = collider.Parameters; } public GameObject CreateGameObject() { if (m_type == typeof(BoxCollider).Name) { return CreateBoxCollider(); } if (m_type == typeof(MeshCollider).Name) { return CreateMeshCollider(); } if (m_type == typeof(SphereCollider).Name) { return CreateSphereCollider(); } if (m_type == typeof(CapsuleCollider).Name) { return CreateCapsuleCollider(); } return null; } private GameObject CreateBoxCollider() { dynamic param = m_parameters; GameObject go = new GameObject("Collider"); var col = go.AddComponent(); go.transform.position = m_position.To(); go.transform.rotation = m_rotation.To(); go.transform.localScale = m_scale.To(); Vector3 size; Vector3 center; size.x = param.SizeX; size.y = param.SizeY; size.z = param.SizeZ; center.x = param.CenterX; center.y = param.CenterY; center.z = param.CenterZ; col.size = size; col.center = center; return go; } private GameObject CreateMeshCollider() { dynamic param = m_parameters; string sharedMeshPath = param.SharedMeshPath; string mainAssetPath = ""; string subAssetName = ""; ObjectUtils.ParseObjectPath(sharedMeshPath, out mainAssetPath, out subAssetName); if (string.IsNullOrEmpty(mainAssetPath) == true) return null; GameObject go = new GameObject("Collider"); var col = go.AddComponent(); go.transform.position = m_position.To(); go.transform.rotation = m_rotation.To(); go.transform.localScale = m_scale.To(); if (string.IsNullOrEmpty(subAssetName) == true) { col.sharedMesh = AssetDatabase.LoadAssetAtPath(mainAssetPath); } else { UnityEngine.Object[] objects = AssetDatabase.LoadAllAssetsAtPath(mainAssetPath); for (int oi = 0; oi < objects.Length; ++oi) { if (objects[oi].name == subAssetName) { col.sharedMesh = objects[oi] as Mesh; if (col.sharedMesh != null) { break; } } } } col.convex = param.Convex; return go; } private GameObject CreateSphereCollider() { dynamic param = m_parameters; GameObject go = new GameObject("Collider"); var col = go.AddComponent(); go.transform.position = m_position.To(); go.transform.rotation = m_rotation.To(); go.transform.localScale = m_scale.To(); Vector3 center; center.x = param.CenterX; center.y = param.CenterY; center.z = param.CenterZ; col.center = center; col.radius = param.Radius; return go; } private GameObject CreateCapsuleCollider() { dynamic param = m_parameters; GameObject go = new GameObject("Collider"); var col = go.AddComponent(); go.transform.position = m_position.To(); go.transform.rotation = m_rotation.To(); go.transform.localScale = m_scale.To(); Vector3 center; center.x = param.CenterX; center.y = param.CenterY; center.z = param.CenterZ; col.center = center; col.radius = param.Radius; col.height = param.Height; col.direction = param.Direction; return go; } } public string Name { set { m_name = value; } get { return m_name; } } public TextureCompressionData CompressionData { set { m_compressionData = value; } get { return m_compressionData; } } [SerializeField] private string m_name; [SerializeField] private TextureCompressionData m_compressionData; [SerializeField] private List m_objects = new List(); [SerializeField] private List m_materials = new List(); [SerializeField] private List m_colliders = new List(); public void AddFromWokringObjects(string name, IList woList) { for (int i = 0; i < woList.Count; ++i) { WorkingObject wo = woList[i]; SerializableObject so = new SerializableObject(); so.From(wo); m_objects.Add(so); AddFromWorkingMaterials(wo.Materials); } } public void AddFromWorkingColliders(string name, IList wcList) { for (int i = 0; i < wcList.Count; ++i) { WorkingCollider wc = wcList[i]; SerializableCollider sc = new SerializableCollider(); sc.From(wc); sc.Name = name; m_colliders.Add(sc); } } public void AddFromGameObject(GameObject go) { using (WorkingObject wo = new WorkingObject(Allocator.Temp)) { var mr = go.GetComponent(); if (mr == null) return; wo.FromRenderer(mr); wo.Name = go.name; SerializableObject so = new SerializableObject(); so.From(wo); for (int mi = 0; mi < wo.Materials.Count; ++mi) { WorkingMaterial wm = wo.Materials[mi]; //Prevent duplication if (GetMaterial(wm.Guid) != null) continue; string[] textureNames = wm.GetTextureNames(); SerializableMaterial sm = new SerializableMaterial(); sm.From(wm); for (int ti = 0; ti < textureNames.Length; ++ti) { WorkingTexture tex = wm.GetTexture(textureNames[ti]); if (tex == null) continue; SerializableTexture st = new SerializableTexture(); st.From(tex.ToTexture()); st.Name = textureNames[ti]; sm.AddTexture(st); } m_materials.Add(sm); } m_objects.Add(so); } } private void AddFromWorkingMaterials(IList wmList) { for (int i = 0; i < wmList.Count; ++i) { WorkingMaterial wm = wmList[i]; //Prevent duplication if (GetMaterial(wm.Guid) != null) continue; string path = AssetDatabase.GUIDToAssetPath(wm.Guid); if (AssetDatabase.LoadAssetAtPath(path) != null) return; SerializableMaterial sm = new SerializableMaterial(); sm.From(wmList[i]); string[] textureNames = wm.GetTextureNames(); for (int ti = 0; ti < textureNames.Length; ++ti) { WorkingTexture wt = wm.GetTexture(textureNames[ti]); SerializableTexture st = new SerializableTexture(); st.From(wt.ToTexture()); st.Name = textureNames[ti]; sm.AddTexture(st); } m_materials.Add(sm); } } public List GetMaterials() { return m_materials; } public List GetObjects() { return m_objects; } public List GetColliders() { return m_colliders; } public int GetMaterialCount() { if (m_materials == null) return 0; return m_materials.Count; } private SerializableMaterial GetMaterial(string id) { for (int i = 0; i < m_materials.Count; ++i) { if (m_materials[i].ID == id) return m_materials[i]; } return null; } } }