diff --git a/CHANGELOG.md b/CHANGELOG.md index c043e80..4db6dbf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Change Log +## [2.5.7] - 2023-07-01 + +- Features + - VgoImporter.LoadAsync() method is added. + - If you are using `Unity 2023.1` or higher, you can declare `UNIVGO_USE_UNITY_AWAITABLE` script define symbol. + - If you are using `UniTask 2.0` or higher, you can declare `UNIVGO_USE_UNITASK` script define symbol. + - None of these may be used without declaration. + - Asynchronous methods load models more slowly than synchronous methods. + - This method is not provided in `WebGL`. +- Changes + - The class name of `ModelAsset` was changed to `VgoModelAsset`. + ## [2.5.6] - 2023-06-20 - Fixes diff --git a/Documentation~/UniVGO/Installation.ja.md b/Documentation~/UniVGO/Installation.ja.md index 0eafa3a..5517dea 100644 --- a/Documentation~/UniVGO/Installation.ja.md +++ b/Documentation~/UniVGO/Installation.ja.md @@ -44,7 +44,7 @@ Unity 2021.1 以下のバージョンを使用する場合 |LilToonShader.Utility|IzayoiJiichan|GitHub||1.4.0|2023年5月30日| |UniShaders|IzayoiJiichan|GitHub||1.4.0|2022年5月20日| |VgoSpringBone|IzayoiJiichan|GitHub||1.1.2|2022年8月24日| -|UniVGO2|IzayoiJiichan|GitHub|VGO 2.5|2.5.6|2023年6月20日| +|UniVGO2|IzayoiJiichan|GitHub|VGO 2.5|2.5.7|2023年7月1日| #### 追加パッケージ @@ -142,7 +142,7 @@ UniVGOを使用するために、以下の設定を追加してください。 "dependencies": { "com.izayoi.liltoon.shader.utility": "https://github.com/izayoijiichan/lilToonShaderUtility.git#v1.4.0", "com.izayoi.unishaders": "https://github.com/izayoijiichan/UniShaders.git#v1.4.0", - "com.izayoi.univgo": "https://github.com/izayoijiican/VGO.git#v2.5.6", + "com.izayoi.univgo": "https://github.com/izayoijiican/VGO.git#v2.5.7", "com.izayoi.vgospringbone": "https://github.com/izayoijiichan/VgoSpringBone.git#v1.1.2", "com.unity.nuget.newtonsoft-json": "3.2.1", "com.unity.ugui": "1.0.0", @@ -241,17 +241,17 @@ ___ |UniVRM|UniVGO|min Unity| |:---:|:---:|:---:| -|0.100.0|2.5.6|2020.3| -|0.101.0|2.5.6|2020.3| -|0.102.0|2.5.6|2020.3| -|0.103.2|2.5.6|2020.3| -|0.104.2|2.5.6|2020.3| -|0.105.0|2.5.6|2020.3| -|0.106.0|2.5.6|2020.3| -|0.107.2|2.5.6|2020.3| -|0.108.0|2.5.6|2020.3| -|0.109.0|2.5.6|2020.3| -|0.110.0|2.5.6|2020.3| +|0.100.0|2.5.7|2020.3| +|0.101.0|2.5.7|2020.3| +|0.102.0|2.5.7|2020.3| +|0.103.2|2.5.7|2020.3| +|0.104.2|2.5.7|2020.3| +|0.105.0|2.5.7|2020.3| +|0.106.0|2.5.7|2020.3| +|0.107.2|2.5.7|2020.3| +|0.108.0|2.5.7|2020.3| +|0.109.0|2.5.7|2020.3| +|0.110.0|2.5.7|2020.3| `/Packages/package.json` に以下の記述を行います。 @@ -282,7 +282,7 @@ https://github.com/izayoijiichan/VGO/wiki/How-to-use-UniVRM-and-UniVGO-together |2022.3.0f1|BRP|UniVGO + UniVRM|[Link](https://github.com/izayoijiichan/univgo2.sample.unity.project/tree/unity2022.3.brp.univrm)| ___ -最終更新日:2023年6月20日 +最終更新日:2023年7月1日 編集者:十六夜おじいちゃん *Copyright (C) 2020 Izayoi Jiichan. All Rights Reserved.* diff --git a/Documentation~/UniVGO/Installation.md b/Documentation~/UniVGO/Installation.md index d99b7ee..5db0ca1 100644 --- a/Documentation~/UniVGO/Installation.md +++ b/Documentation~/UniVGO/Installation.md @@ -44,7 +44,7 @@ This package is required for any Unity version. |LilToonShader.Utility|IzayoiJiichan|GitHub||1.4.0|30 May, 2023| |UniShaders|IzayoiJiichan|GitHub||1.4.0|20 May, 2022| |VgoSpringBone|IzayoiJiichan|GitHub||1.1.2|24 Aug, 2022| -|UniVGO2|IzayoiJiichan|GitHub|VGO 2.5|2.5.6|20 Jun, 2023| +|UniVGO2|IzayoiJiichan|GitHub|VGO 2.5|2.5.7|1 Jul, 2023| #### Additional Packages @@ -141,7 +141,7 @@ To use UniVGO, add the following settings. "dependencies": { "com.izayoi.liltoon.shader.utility": "https://github.com/izayoijiichan/lilToonShaderUtility.git#v1.4.0", "com.izayoi.unishaders": "https://github.com/izayoijiichan/UniShaders.git#v1.4.0", - "com.izayoi.univgo": "https://github.com/izayoijiican/VGO.git#v2.5.6", + "com.izayoi.univgo": "https://github.com/izayoijiican/VGO.git#v2.5.7", "com.izayoi.vgospringbone": "https://github.com/izayoijiichan/VgoSpringBone.git#v1.1.2", "com.unity.nuget.newtonsoft-json": "3.2.1", "com.unity.ugui": "1.0.0", @@ -239,17 +239,17 @@ The version combinations are as follows. |UniVRM|UniVGO|min Unity| |:---:|:---:|:---:| -|0.100.0|2.5.6|2020.3| -|0.101.0|2.5.6|2020.3| -|0.102.0|2.5.6|2020.3| -|0.103.2|2.5.6|2020.3| -|0.104.2|2.5.6|2020.3| -|0.105.0|2.5.6|2020.3| -|0.106.0|2.5.6|2020.3| -|0.107.2|2.5.6|2020.3| -|0.108.0|2.5.6|2020.3| -|0.109.0|2.5.6|2020.3| -|0.110.0|2.5.6|2020.3| +|0.100.0|2.5.7|2020.3| +|0.101.0|2.5.7|2020.3| +|0.102.0|2.5.7|2020.3| +|0.103.2|2.5.7|2020.3| +|0.104.2|2.5.7|2020.3| +|0.105.0|2.5.7|2020.3| +|0.106.0|2.5.7|2020.3| +|0.107.2|2.5.7|2020.3| +|0.108.0|2.5.7|2020.3| +|0.109.0|2.5.7|2020.3| +|0.110.0|2.5.7|2020.3| Write the following in ` /Packages/package.json`. @@ -279,7 +279,7 @@ https://github.com/izayoijiichan/VGO/wiki/How-to-use-UniVRM-and-UniVGO-together |2022.3.0f1|BRP|UniVGO + UniVRM|[Link](https://github.com/izayoijiichan/univgo2.sample.unity.project/tree/unity2022.3.brp.univrm)| ___ -Last updated: 20 Jun, 2023 +Last updated: 1 July, 2023 Editor: Izayoi Jiichan *Copyright (C) 2020 Izayoi Jiichan. All Rights Reserved.* diff --git a/Documentation~/UniVGO/Usage.ja.md b/Documentation~/UniVGO/Usage.ja.md index 1b41cdf..be143c2 100644 --- a/Documentation~/UniVGO/Usage.ja.md +++ b/Documentation~/UniVGO/Usage.ja.md @@ -13,7 +13,7 @@ ___ |No|項目|値| |:---:|:---|:---:| |1|Unity バージョン|2022.3| -|2|UniVGO バージョン|2.5.5| +|2|UniVGO バージョン|2.5.7| |3|VGO 仕様バージョン|2.5| ### 対応 Unity コンポーネント @@ -122,7 +122,7 @@ UniVGO サンプル プロジェクトを使用する場合は`ExportScene`を |No|項目|説明|値| |:---:|:---|:---|:---:| |1|Name|生成ツールの名前です。|UniVGO| -|2|Version|生成ツールのバージョンです。|2.5.5| +|2|Version|生成ツールのバージョンです。|2.5.7| ユーザーが設定可能な項目はありません。 ジェネレーター情報が古い場合にはコンポーネントを一度削除して、再度付与してください。 @@ -274,7 +274,7 @@ UniVGO サンプル プロジェクトを使用する場合は`ExportScene`を |No|項目|説明|値| |:---:|:---|:---|:---:| |1|Generator Name|生成ツールの名前です。|UniVGO| -|2|Generator Version|生成ツールのバージョンです。|2.5.5| +|2|Generator Version|生成ツールのバージョンです。|2.5.7| ユーザーが設定可能な項目はありません。 メタ情報が古い場合にはコンポーネントを一度削除して、再度付与してください。 @@ -489,22 +489,22 @@ VGOファイルが読み込まれることを確認します。 public class RuntimeLoadBehaviour : MonoBehaviour { - private IDisposable _ModelAssetDisposer; + private readonly VgoImporter _VgoImporter = new(); + + private IDisposable _VgoModelAssetDisposer; private void Start() { - VgoImporter importer = new(); - - ModelAsset modelAsset = importer.Load(filePath); + VgoModelAsset vgoModelAsset = _VgoImporter.Load(filePath); - importer.ReflectSkybox(Camera.main, modelAsset); + _VgoImporter.ReflectSkybox(Camera.main, vgoModelAsset); - _ModelAssetDisposer = modelAsset; + _VgoModelAssetDisposer = vgoModelAsset; } private void OnDestroy() { - _ModelAssetDisposer?.Dispose(); + _VgoModelAssetDisposer?.Dispose(); } } ~~~ @@ -524,7 +524,7 @@ VGOファイルをアップロードし管理することができるサービ https://vgohub.azurewebsites.net ___ -最終更新日:2023年6月20日 +最終更新日:2023年7月1日 編集者:十六夜おじいちゃん *Copyright (C) 2020 Izayoi Jiichan. All Rights Reserved.* diff --git a/Documentation~/UniVGO/Usage.md b/Documentation~/UniVGO/Usage.md index a446207..622b69e 100644 --- a/Documentation~/UniVGO/Usage.md +++ b/Documentation~/UniVGO/Usage.md @@ -13,7 +13,7 @@ The contents described in this manual are for the following versions. |No|item|value| |:---:|:---|:---:| |1|Unity version|2022.3| -|2|UniVGO version|2.5.5| +|2|UniVGO version|2.5.7| |3|VGO spec version|2.5| ### Supported Unity components @@ -122,7 +122,7 @@ The order of the components does not matter. |No|item|description|value| |:---:|:---|:---|:---:| |1|Name|The name of the generation tool.|UniVGO| -|2|Version|Version of the generation tool.|2.5.5| +|2|Version|Version of the generation tool.|2.5.7| There are no user-configurable items. If the meta information is old, delete the component once and attach it again. @@ -274,7 +274,7 @@ The order of the components does not matter. |No|item|description|value| |:---:|:---|:---|:---:| |1|Name|The name of the generation tool.|UniVGO| -|2|Version|Version of the generation tool.|2.5.5| +|2|Version|Version of the generation tool.|2.5.7| There are no user-configurable items. If the meta information is old, delete the component once and attach it again. @@ -485,22 +485,22 @@ If you write your own script, write as follows. public class RuntimeLoadBehaviour : MonoBehaviour { - private IDisposable _ModelAssetDisposer; + private readonly VgoImporter _VgoImporter = new(); + + private IDisposable _VgoModelAssetDisposer; private void Start() { - VgoImporter importer = new(); - - ModelAsset modelAsset = importer.Load(filePath); + VgoModelAsset vgoModelAsset = _VgoImporter.Load(filePath); - importer.ReflectSkybox(Camera.main, modelAsset); + _VgoImporter.ReflectSkybox(Camera.main, vgoModelAsset); - _ModelAssetDisposer = modelAsset; + _VgoModelAssetDisposer = vgoModelAsset; } private void OnDestroy() { - _ModelAssetDisposer?.Dispose(); + _VgoModelAssetDisposer?.Dispose(); } } ~~~ @@ -519,7 +519,7 @@ or the VGO (avatar or world) that others have set to be available in the linked https://vgohub.azurewebsites.net ___ -Last updated: 20 June, 2023 +Last updated: 1 July, 2023 Editor: Izayoi Jiichan *Copyright (C) 2020 Izayoi Jiichan. All Rights Reserved.* diff --git a/NewtonVgo/Runtime/Defines.meta b/NewtonVgo/Runtime/Constants.meta similarity index 100% rename from NewtonVgo/Runtime/Defines.meta rename to NewtonVgo/Runtime/Constants.meta diff --git a/NewtonVgo/Runtime/Defines/VertexKey.cs b/NewtonVgo/Runtime/Constants/VertexKey.cs similarity index 100% rename from NewtonVgo/Runtime/Defines/VertexKey.cs rename to NewtonVgo/Runtime/Constants/VertexKey.cs diff --git a/NewtonVgo/Runtime/Defines/VertexKey.cs.meta b/NewtonVgo/Runtime/Constants/VertexKey.cs.meta similarity index 100% rename from NewtonVgo/Runtime/Defines/VertexKey.cs.meta rename to NewtonVgo/Runtime/Constants/VertexKey.cs.meta diff --git a/NewtonVgo/Runtime/Defines/VgoCryptographyAlgorithms.cs b/NewtonVgo/Runtime/Constants/VgoCryptographyAlgorithms.cs similarity index 100% rename from NewtonVgo/Runtime/Defines/VgoCryptographyAlgorithms.cs rename to NewtonVgo/Runtime/Constants/VgoCryptographyAlgorithms.cs diff --git a/NewtonVgo/Runtime/Defines/VgoCryptographyAlgorithms.cs.meta b/NewtonVgo/Runtime/Constants/VgoCryptographyAlgorithms.cs.meta similarity index 100% rename from NewtonVgo/Runtime/Defines/VgoCryptographyAlgorithms.cs.meta rename to NewtonVgo/Runtime/Constants/VgoCryptographyAlgorithms.cs.meta diff --git a/NewtonVgo/Runtime/Storage/VgoStorage.Export.cs b/NewtonVgo/Runtime/Storage/VgoStorage.Export.cs new file mode 100644 index 0000000..0a5cc7c --- /dev/null +++ b/NewtonVgo/Runtime/Storage/VgoStorage.Export.cs @@ -0,0 +1,591 @@ +// ---------------------------------------------------------------------- +// @Namespace : NewtonVgo +// @Class : VgoStorage +// ---------------------------------------------------------------------- +#nullable enable +namespace NewtonVgo +{ + using Newtonsoft.Json; + using NewtonVgo.Buffers; + using NewtonVgo.Security.Cryptography; + using System; + using System.Collections.Generic; + using System.IO; + using System.Runtime.InteropServices; + using System.Text; + + /// + /// VGO Storage + /// + public partial class VgoStorage : IVgoStorage + { + #region Public Methods (Export) + + /// + /// Exports a VGO format file. + /// + /// The full path of the file. + /// A vgo export setting. + /// Returns true if the export was successful, false otherwise. + public virtual bool ExportVgoFile(string filePath, VgoExportSetting exportSetting) + { + if (filePath == null) + { + throw new ArgumentNullException(nameof(filePath)); + } + + if (AssetInfo == null) + { + throw new Exception(); + } + + if (Layout == null) + { + throw new Exception(); + } + + if (ResourceAccessors == null) + { + throw new Exception(); + } + + if (Resource == null) + { + throw new Exception(); + } + + if (exportSetting.Validate(out IReadOnlyList errorMessages) == false) + { + throw new Exception(string.Join("\n", errorMessages)); + } + + FileInfo fileInfo = new FileInfo(filePath); + + // Asset Info chunk + VgoChunk assetInfoChunk = CreateAssetInfoChunk(exportSetting.AssetInfoTypeId); + + // Layout chunk + VgoChunk layoutChunk = CreateLayoutChunk(exportSetting.LayoutTypeId); + + // Resource Accessor chunk + (VgoChunk resourceAccessorChunk, VgoChunk? resourceAccessorCryptChunk) = + CreateResourceAccessorChunk( + exportSetting.ResourceAccessorTypeId, + exportSetting.ResourceAccessorCryptTypeId, + exportSetting.ResourceAccessorCryptAlgorithm, + exportSetting.ResourceAccessorCryptKey + ); + + // Resource chunk + VgoChunk resourceChunk = CreateResourceChunk(exportSetting.ResourceTypeId, fileInfo, exportSetting.BinFileName, exportSetting.ResourceUri); + + // Composer chunk + VgoChunk composerChunk = CreateComposerChunk( + exportSetting.AssetInfoTypeId, + exportSetting.LayoutTypeId, + exportSetting.ResourceAccessorTypeId, + exportSetting.ResourceAccessorCryptTypeId, + exportSetting.ResourceTypeId + ); + + // Index chunk + List chunkList; + + if (resourceAccessorCryptChunk == null) + { + chunkList = new List(5) + { + composerChunk, + assetInfoChunk, + layoutChunk, + resourceAccessorChunk, + resourceChunk, + }; + } + else + { + chunkList = new List(6) + { + composerChunk, + assetInfoChunk, + layoutChunk, + resourceAccessorChunk, + resourceAccessorCryptChunk, + resourceChunk, + }; + } + + VgoChunk indexChunk = CreateIndexChunk(chunkList); + + // Header + if (exportSetting.ResourceAccessorCryptTypeId != VgoChunkTypeID.None) + { + Header.ResourceAccessorIsCrypted = 1; + + if (exportSetting.ResourceAccessorCryptKey != null) + { + Header.IsRequireResourceAccessorExternalCryptKey = 1; + } + } + + // Output (.vgo) + using (var stream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None)) + using (var writer = new BinaryWriter(stream)) + { + writer.Write(Header.ConvertToByteArray()); + writer.Write(indexChunk.ConvertToByteArray()); + writer.Write(composerChunk.ConvertToByteArray()); + writer.Write(assetInfoChunk.ConvertToByteArray()); + writer.Write(layoutChunk.ConvertToByteArray()); + writer.Write(resourceAccessorChunk.ConvertToByteArray()); + if (resourceAccessorCryptChunk != null) + { + writer.Write(resourceAccessorCryptChunk.ConvertToByteArray()); + } + writer.Write(resourceChunk.ConvertToByteArray()); + + writer.Flush(); + } + + if (exportSetting.ResourceAccessorCryptKey != null) + { + string keyFileName = fileInfo.Name.Substring(0, fileInfo.Name.Length - fileInfo.Extension.Length) + ".vgk"; + + string keyFullPath = Path.Combine(fileInfo.DirectoryName, keyFileName); + + // Output (.vgk) + using (var stream = new FileStream(keyFullPath, FileMode.Create, FileAccess.Write, FileShare.None)) + using (var writer = new BinaryWriter(stream)) + { + writer.Write(exportSetting.ResourceAccessorCryptKey); + + writer.Flush(); + } + } + + if (exportSetting.ResourceTypeId != VgoChunkTypeID.REPb) + { + string binFullPath = Path.Combine(fileInfo.DirectoryName, exportSetting.BinFileName); // @notice binFileName + + // Output (.bin) + using (var stream = new FileStream(binFullPath, FileMode.Create, FileAccess.Write, FileShare.None)) + using (var writer = new BinaryWriter(stream)) + { + writer.Write(Resource.ToArray()); + + writer.Flush(); + } + } + + return true; + } + + #endregion + + #region Protected Methods (Export) Asset Info chunk + + /// + /// Create an asset info chunk. + /// + /// The asset info chunk type ID. + /// An asset info chunk. + public virtual VgoChunk CreateAssetInfoChunk(VgoChunkTypeID assetInfoTypeId) + { + IByteBuffer assetInfoChunkData; + + try + { + if (assetInfoTypeId == VgoChunkTypeID.AIPJ) + { + // JSON + byte[] json = SerializeObject(AssetInfo, isBson: false); + + assetInfoChunkData = new ReadOnlyArraySegmentByteBuffer(new ArraySegment(json)); + } + else if (assetInfoTypeId == VgoChunkTypeID.AIPB) + { + // BSON + byte[] bson = SerializeObject(AssetInfo, isBson: true); + + assetInfoChunkData = new ReadOnlyArraySegmentByteBuffer(new ArraySegment(bson)); + } + else + { + throw new Exception($"{nameof(assetInfoTypeId)}: {assetInfoTypeId}"); + } + } + catch (JsonSerializationException) + { + throw; + } + catch + { + throw; + } + + VgoChunk assetInfoChunk = new VgoChunk(assetInfoTypeId, assetInfoChunkData); + + return assetInfoChunk; + } + + #endregion + + #region Protected Methods (Export) Layout chunk + + /// + /// Create a layout chunk. + /// + /// The layout chunk type ID. + /// A layout chunk. + protected virtual VgoChunk CreateLayoutChunk(VgoChunkTypeID layoutTypeId) + { + IByteBuffer layoutChunkData; + + try + { + if (layoutTypeId == VgoChunkTypeID.LAPJ) + { + // JSON + byte[] json = SerializeObject(Layout, isBson: false); + + layoutChunkData = new ReadOnlyArraySegmentByteBuffer(new ArraySegment(json)); + } + else if (layoutTypeId == VgoChunkTypeID.LAPB) + { + // BSON + byte[] bson = SerializeObject(Layout, isBson: true); + + layoutChunkData = new ReadOnlyArraySegmentByteBuffer(new ArraySegment(bson)); + } + else + { + throw new Exception($"{nameof(layoutTypeId)}: {layoutTypeId}"); + } + } + catch (JsonSerializationException) + { + throw; + } + catch + { + throw; + } + + VgoChunk layoutChunk = new VgoChunk(layoutTypeId, layoutChunkData); + + return layoutChunk; + } + + #endregion + + #region Protected Methods (Export) Resource Accessor chunk + + /// + /// Create a resource accessor chunk. + /// + /// The resource accessor chunk type ID. + /// The resource accessor crypt chunk type ID. + /// The crypt algorithm. + /// The crypt key. + /// A resource accessor chunk and a crypt chunk. + protected virtual (VgoChunk, VgoChunk?) CreateResourceAccessorChunk( + VgoChunkTypeID resourceAccessorTypeId, + VgoChunkTypeID resourceAccessorCryptTypeId, + string? cryptAlgorithm = null, + byte[]? cryptKey = null) + { + IByteBuffer resourceAccessorChunkData; + IByteBuffer? resourceAccessorCryptChunkData = null; + + try + { + byte[] plainResourceAccessorJsonOrBson; + + if ((resourceAccessorTypeId == VgoChunkTypeID.RAPJ) || + (resourceAccessorTypeId == VgoChunkTypeID.RACJ)) + { + // JSON + plainResourceAccessorJsonOrBson = SerializeObject(ResourceAccessors, isBson: false); + } + else if ( + (resourceAccessorTypeId == VgoChunkTypeID.RAPB) || + (resourceAccessorTypeId == VgoChunkTypeID.RACB)) + { + // BSON + plainResourceAccessorJsonOrBson = SerializeObject(ResourceAccessors, isBson: true); + } + else + { + throw new Exception($"{nameof(resourceAccessorTypeId)}: {resourceAccessorTypeId}"); + } + + if ((resourceAccessorTypeId == VgoChunkTypeID.RAPJ) || + (resourceAccessorTypeId == VgoChunkTypeID.RAPB)) + { + resourceAccessorChunkData = new ReadOnlyArraySegmentByteBuffer(new ArraySegment(plainResourceAccessorJsonOrBson)); + } + else if ( + (resourceAccessorTypeId == VgoChunkTypeID.RACJ) || + (resourceAccessorTypeId == VgoChunkTypeID.RACB)) + { + byte[] encryptedResourceAccessorJsonOrBson; + + VgoCryptV0 cryptInfo; + + if (cryptAlgorithm == VgoCryptographyAlgorithms.AES) + { + AesCrypter aesCrypter = new AesCrypter(); + + string? keyString; + string? ivString; + + // JSON or BSON (encrypt) + if (cryptKey == null) + { + encryptedResourceAccessorJsonOrBson = aesCrypter.Encrypt(plainResourceAccessorJsonOrBson, keySize: 128, blockSize: 128, out _, out keyString, out _, out ivString); + } + else + { + encryptedResourceAccessorJsonOrBson = aesCrypter.Encrypt(plainResourceAccessorJsonOrBson, cryptKey, blockSize: 128, out _, out ivString); + + keyString = null; // @notice Secret + } + + // CRAJ + cryptInfo = new VgoCryptV0 + { + algorithms = VgoCryptographyAlgorithms.AES, + key = keyString, + iv = ivString, + cipherMode = aesCrypter.CipherMode, + paddingMode = aesCrypter.PaddingMode, + }; + } + else if (cryptAlgorithm == VgoCryptographyAlgorithms.Base64) + { + // JSON or BSON (encrypt) + string base64String = Convert.ToBase64String(plainResourceAccessorJsonOrBson); + + encryptedResourceAccessorJsonOrBson = Encoding.UTF8.GetBytes(base64String); + + // CRAJ + cryptInfo = new VgoCryptV0 + { + algorithms = VgoCryptographyAlgorithms.Base64, + }; + } + else + { + throw new Exception($"resourceAccessorCryptAlgorithm: {cryptAlgorithm}"); + } + + byte[] cryptInfoJsonOrBson; + + if (resourceAccessorCryptTypeId == VgoChunkTypeID.CRAJ) + { + cryptInfoJsonOrBson = SerializeObject(cryptInfo, isBson: false); + } + else if (resourceAccessorCryptTypeId == VgoChunkTypeID.CRAB) + { + cryptInfoJsonOrBson = SerializeObject(cryptInfo, isBson: true); + } + else + { + throw new Exception($"{nameof(resourceAccessorCryptTypeId)}: {resourceAccessorCryptTypeId}"); + } + + resourceAccessorChunkData = new ReadOnlyArraySegmentByteBuffer(new ArraySegment(encryptedResourceAccessorJsonOrBson)); + resourceAccessorCryptChunkData = new ReadOnlyArraySegmentByteBuffer(new ArraySegment(cryptInfoJsonOrBson)); + } + else + { + throw new Exception($"{nameof(resourceAccessorTypeId)}: {resourceAccessorTypeId}"); + } + } + catch (JsonSerializationException) + { + throw; + } + catch + { + throw; + } + + VgoChunk resourceAccessorChunk = new VgoChunk(resourceAccessorTypeId, resourceAccessorChunkData); + + VgoChunk? resourceAccessorCryptChunk = null; + + if (resourceAccessorCryptChunkData != null) + { + resourceAccessorCryptChunk = new VgoChunk(resourceAccessorCryptTypeId, resourceAccessorCryptChunkData); + } + + return (resourceAccessorChunk, resourceAccessorCryptChunk); + } + + #endregion + + #region Protected Methods (Export) Resource chunk + + /// + /// Create a resource chunk. + /// + /// The resource chunk type ID. + /// The file info. + /// The resource binary file name. + /// The resource URI. + /// A resouce chunk. + protected virtual VgoChunk CreateResourceChunk(VgoChunkTypeID resourceTypeId, FileInfo fileInfo, string? binFileName = null, string? resourceUri = null) + { + if (binFileName == null) + { + binFileName = fileInfo.Name.Substring(0, fileInfo.Name.Length - fileInfo.Extension.Length) + ".bin"; + } + + if (resourceUri == null) + { + resourceUri = binFileName; + } + + if (Resource == null) + { + throw new Exception(); + } + + IByteBuffer resourceChunkData; + + try + { + var vgoResource = new VgoResource + { + uri = resourceUri, + byteLength = Resource.Length, + }; + + if (resourceTypeId == VgoChunkTypeID.REPb) + { + // binary + resourceChunkData = Resource; + } + else if (resourceTypeId == VgoChunkTypeID.REPJ) + { + // JSON + byte[] resourceJson = SerializeObject(vgoResource, isBson: false); + + resourceChunkData = new ReadOnlyArraySegmentByteBuffer(new ArraySegment(resourceJson)); + } + else if (resourceTypeId == VgoChunkTypeID.REPB) + { + // BSON + byte[] resourceBson = SerializeObject(vgoResource, isBson: true); + + resourceChunkData = new ReadOnlyArraySegmentByteBuffer(new ArraySegment(resourceBson)); + } + else + { + throw new Exception($"{nameof(resourceTypeId)}: {resourceTypeId}"); + } + } + catch (JsonSerializationException) + { + throw; + } + catch + { + throw; + } + + VgoChunk resourceChunk = new VgoChunk(resourceTypeId, resourceChunkData); + + return resourceChunk; + } + + #endregion + + #region Protected Methods (Export) Composer chunk + + /// + /// Create a composer chunk. + /// + /// The asset info chunk type ID. + /// The layout chunk type ID. + /// The resourse accessor chunk type ID. + /// The resourse accessor crypt chunk type ID. + /// The resourse chunk type ID. + /// A composer chunk. + public virtual VgoChunk CreateComposerChunk( + VgoChunkTypeID assetInfoTypeId, + VgoChunkTypeID layoutTypeId, + VgoChunkTypeID resourceAccessorTypeId, + VgoChunkTypeID resourceAccessorCryptTypeId, + VgoChunkTypeID resourceTypeId) + { + VgoComposerChunkData composerChunkData = new VgoComposerChunkData + { + AssetInfoChunkTypeId = assetInfoTypeId, + LayoutChunkTypeId = layoutTypeId, + ResourceAccessorChunkTypeId = resourceAccessorTypeId, + ResourceAccessorCryptChunkTypeId = resourceAccessorCryptTypeId, + ResourceCryptChunkTypeId = resourceAccessorCryptTypeId, + ResourceChunkTypeId = resourceTypeId, + }; + + byte[] compDataBytes = composerChunkData.ConvertToByteArray(); + + IByteBuffer composerCunkData = new ReadOnlyArraySegmentByteBuffer(new ArraySegment(compDataBytes)); + + VgoChunk composerChunk = new VgoChunk(VgoChunkTypeID.COMP, composerCunkData); + + return composerChunk; + } + + #endregion + + #region Protected Methods (Export) Index chunk + + /// + /// Create a index chunk. + /// + /// List of chunk. + /// A index chunk. + protected virtual VgoChunk CreateIndexChunk(List chunkList) + { + VgoIndexChunkDataElement[] indexChunkDataArray = new VgoIndexChunkDataElement[chunkList.Count]; + + int headerSize = Marshal.SizeOf(typeof(VgoHeader)); + + int indexChunkDataLength = Marshal.SizeOf(typeof(VgoIndexChunkDataElement)) * chunkList.Count; + + int indexChunkSize = 8 + indexChunkDataLength; + + uint byteOffset = (uint)(headerSize + indexChunkSize); + + for (int idx = 0; idx < indexChunkDataArray.Length; idx++) + { + VgoChunk curChunk = chunkList[idx]; + + indexChunkDataArray[idx] = new VgoIndexChunkDataElement + { + ChunkTypeId = curChunk.TypeId, + ByteOffset = byteOffset, + ByteLength = curChunk.AllLength, + BytePadding = (byte)curChunk.PaddingCount, + }; + + byteOffset += curChunk.AllLength; + } + + ChunkIndexMap = indexChunkDataArray; + + var indicatorBuffer = new ArraySegmentByteBuffer(indexChunkDataLength); + + indicatorBuffer.Append(new ArraySegment(indexChunkDataArray)); + + VgoChunk indexChunk = new VgoChunk(VgoChunkTypeID.Idx, indicatorBuffer); + + return indexChunk; + } + + #endregion + } +} diff --git a/NewtonVgo/Runtime/Storage/VgoStorage.Export.cs.meta b/NewtonVgo/Runtime/Storage/VgoStorage.Export.cs.meta new file mode 100644 index 0000000..ff2e879 --- /dev/null +++ b/NewtonVgo/Runtime/Storage/VgoStorage.Export.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9e0ed0fa3ba81bd4eb19cfb095697a40 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/NewtonVgo/Runtime/Storage/VgoStorage.Import.cs b/NewtonVgo/Runtime/Storage/VgoStorage.Import.cs new file mode 100644 index 0000000..3f3fd59 --- /dev/null +++ b/NewtonVgo/Runtime/Storage/VgoStorage.Import.cs @@ -0,0 +1,729 @@ +// ---------------------------------------------------------------------- +// @Namespace : NewtonVgo +// @Class : VgoStorage +// ---------------------------------------------------------------------- +#nullable enable +namespace NewtonVgo +{ + using Newtonsoft.Json; + using NewtonVgo.Buffers; + using NewtonVgo.Security.Cryptography; + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Net.Http; + using System.Runtime.InteropServices; + using System.Text; + + /// + /// VGO Storage + /// + public partial class VgoStorage : IVgoStorage + { + #region Protected Methods (Import) + + /// + /// Parse vgo. + /// + /// The vgo bytes. + /// The vgk bytes. + protected virtual void ParseVgo(byte[] vgoBytes, byte[]? vgkBytes, out VgoLayout layout) + { + if (vgoBytes == null) + { + throw new ArgumentNullException(nameof(vgoBytes)); + } + + if (vgoBytes.Any() == false) + { + throw new ArgumentOutOfRangeException(nameof(vgoBytes)); + } + + if (vgoBytes.Length < 16) + { + throw new ArgumentOutOfRangeException(nameof(vgoBytes)); + } + + ArraySegment allSegmentBytes = new ArraySegment(vgoBytes); + + // Header + Header = GetHeader(allSegmentBytes); + + if ((Header.IsRequireResourceAccessorExternalCryptKey == 1) && (vgkBytes == null)) + { + throw new Exception("cryptKey is required."); + } + + // Index chunk + ChunkIndexMap = GetChunkIndexMap(allSegmentBytes, indexByteOffset: 16); + + // Composer chunk + ArraySegment composerChunkDataBytes = ExtractChunkData(VgoChunkTypeID.COMP, ChunkIndexMap, allSegmentBytes); + + VgoComposerChunkData composerChunkData = composerChunkDataBytes + .ToArray() + .ConvertToStructure(); + + // Asset Info chunk + try + { + AssetInfo = ExtractAssetInfo(composerChunkData, ChunkIndexMap, allSegmentBytes); + } + catch + { + throw; + } + + // Layout chunk + try + { + layout = ExtractLayout(composerChunkData, ChunkIndexMap, allSegmentBytes); + } + catch + { + throw; + } + + // Resource Accessor chunk + try + { + ResourceAccessors = ExtractResourceAccessor(composerChunkData, ChunkIndexMap, allSegmentBytes, vgkBytes); + } + catch + { + throw; + } + + // Resource chunk + try + { + ArraySegment resourceBytes = ExtractResource(composerChunkData, ChunkIndexMap, allSegmentBytes); + + Resource = new ReadOnlyArraySegmentByteBuffer(resourceBytes); + } + catch + { + throw; + } + } + + #endregion + + #region Protected Methods (Import) Header chunk + + /// + /// Get header chunk. + /// + /// The all segment bytes. + /// The header chunk. + protected virtual VgoHeader GetHeader(ArraySegment allSegmentBytes) + { + try + { + int headerSize = Marshal.SizeOf(typeof(VgoHeader)); + + ArraySegment headerSegment = allSegmentBytes.Slice(offset: 0, count: headerSize); + + VgoHeader header = headerSegment.Array.ConvertToStructure(); + + if (header.Magic != (uint)VgoChunkTypeID.Vgo) + { + throw new FormatException($"Header.Magic: {header.Magic}"); + } + + if (header.DataLength != 8) + { + throw new FormatException($"Header.DataLength: {header.DataLength}"); + } + + if (header.MajorVersion == 0) + { + throw new FormatException($"Header.MajorVersion: {header.MajorVersion}"); + } + + if ((header.GeometryCoordinate != VgoGeometryCoordinate.RightHanded) && + (header.GeometryCoordinate != VgoGeometryCoordinate.LeftHanded)) + { + throw new FormatException($"Header.GeometryCoordinate: {header.GeometryCoordinate}"); + } + + if ((header.UVCoordinate != VgoUVCoordinate.TopLeft) && + (header.UVCoordinate != VgoUVCoordinate.BottomLeft)) + { + throw new FormatException($"Header.UVCoordinate: {header.UVCoordinate}"); + } + + return header; + } + catch + { + throw; + } + } + + #endregion + + #region Protected Methods (Import) Index chunk + + /// + /// Get chunk index map. + /// + /// The all segment bytes. + /// The start position of the index chunk. + /// The chunk index map. + /// + /// Index chunk (8 + 16 * n byte) + /// + protected virtual VgoIndexChunkDataElement[] GetChunkIndexMap(ArraySegment allSegmentBytes, int indexByteOffset) + { + try + { + uint indexChunkTypeId = BitConverter.ToUInt32(allSegmentBytes.Array, indexByteOffset); + + if (indexChunkTypeId != (uint)VgoChunkTypeID.Idx) + { + throw new FormatException($"[IDX] Chunk.ChunkTypeId: {indexChunkTypeId}"); + } + + uint indexChunkDataLength = BitConverter.ToUInt32(allSegmentBytes.Array, indexByteOffset + 4); + + if (indexChunkDataLength == 0) + { + throw new FormatException($"[IDX] Chunk.DataLength: {indexChunkDataLength}"); + } + + int elementSize = Marshal.SizeOf(typeof(VgoIndexChunkDataElement)); + + if ((indexChunkDataLength % elementSize) != 0) + { + throw new FormatException($"[IDX] Chunk.DataLength: {indexChunkDataLength}"); + } + + int elementCount = (int)indexChunkDataLength / elementSize; + + VgoIndexChunkDataElement[] chunkIndexMap = new VgoIndexChunkDataElement[elementCount]; + + allSegmentBytes + .Slice(offset: indexByteOffset + 8, count: elementSize * elementCount) + .MarshalCopyTo(chunkIndexMap); + + return chunkIndexMap; + } + catch + { + throw; + } + } + + /// + /// Get the index chunk data of the specified chunk type ID from the chunk index map. + /// + /// The chunk type ID. + /// The chunk index map. + /// + protected virtual VgoIndexChunkDataElement GetIndexChunkDataElement(VgoChunkTypeID chunkTypeId, VgoIndexChunkDataElement[] chunkIndexMap) + { + try + { + int count = chunkIndexMap.Where(x => x.ChunkTypeId == chunkTypeId).Count(); + + if (count == 0) + { + throw new FormatException($"{chunkTypeId} is not define in IDX chunk."); + } + + if (count >= 2) + { + throw new FormatException($"{chunkTypeId} is defined more than once in IDX chunk."); + } + + VgoIndexChunkDataElement chunkInfo = chunkIndexMap.Where(x => x.ChunkTypeId == chunkTypeId).First(); + + if (chunkInfo.ByteOffset == 0) + { + throw new FormatException($"[{chunkTypeId}] Chunk.ByteOffset: {chunkInfo.ByteOffset}"); + } + + if (chunkInfo.ByteLength == 0) + { + //throw new FormatException($"[{chunkTypeId}] Chunk.ByteLength: {chunkInfo.ByteLength}"); + } + + //if ((chunkInfo.ByteOffset + chunkInfo.ByteLength) > allBytes.Length) + //{ + // throw new FormatException($"[{chunkTypeId}] Chunk.ByteLength: {chunkInfo.ByteLength} is out of the range."); + //} + + return chunkInfo; + } + catch + { + throw; + } + } + + /// + /// Extract chunk data from asset info chunk. + /// + /// The chunk type ID. + /// The chunk index map. + /// The all segment bytes. + /// The chunk data bytes. + protected virtual ArraySegment ExtractChunkData(VgoChunkTypeID chunkTypeId, VgoIndexChunkDataElement[] chunkIndexMap, ArraySegment allSegmentBytes) + { + VgoIndexChunkDataElement chunkIndexInfo = GetIndexChunkDataElement(chunkTypeId, chunkIndexMap); + + uint chunkChunkTypeId = BitConverter.ToUInt32(allSegmentBytes.Array, (int)chunkIndexInfo.ByteOffset); + + if (chunkChunkTypeId != (uint)chunkIndexInfo.ChunkTypeId) + { + throw new FormatException($"[{chunkIndexInfo.ChunkTypeId}] Chunk.ChunkTypeId: {chunkChunkTypeId}"); + } + + uint chunkDataLength = BitConverter.ToUInt32(allSegmentBytes.Array, (int)chunkIndexInfo.ByteOffset + 4); + + if (chunkDataLength == 0) + { + throw new FormatException($"[{chunkIndexInfo.ChunkTypeId}] Chunk.DataLength: {chunkDataLength}"); + } + + ArraySegment chunkData = allSegmentBytes.Slice( + offset: (int)chunkIndexInfo.ByteOffset + 8, + count: (int)chunkDataLength - chunkIndexInfo.BytePadding + ); + + return chunkData; + } + + #endregion + + #region Protected Methods (Import) Asset Info chunk + + /// + /// Extract asset info from asset info chunk. + /// + /// The composer chunk data. + /// The chunk index map. + /// The all segment bytes. + /// The asset info. + protected virtual VgoAssetInfo? ExtractAssetInfo(VgoComposerChunkData composerChunkData, VgoIndexChunkDataElement[] chunkIndexMap, ArraySegment allSegmentBytes) + { + try + { + ArraySegment assetInfoChunkData = ExtractChunkData(composerChunkData.AssetInfoChunkTypeId, chunkIndexMap, allSegmentBytes); + + VgoAssetInfo? vgoAssetInfo; + + if (composerChunkData.AssetInfoChunkTypeId == VgoChunkTypeID.AIPJ) + { + // JSON + vgoAssetInfo = DeserializeObject(assetInfoChunkData.ToArray(), isBson: false); + } + else if (composerChunkData.AssetInfoChunkTypeId == VgoChunkTypeID.AIPB) + { + // BSON + vgoAssetInfo = DeserializeObject(assetInfoChunkData.ToArray(), isBson: true); + } + else + { + throw new FormatException($"[COMP] AssetInfoChunkTypeId: {composerChunkData.AssetInfoChunkTypeId}"); + } + + return vgoAssetInfo; + } + catch (JsonSerializationException) + { + throw; + } + catch + { + throw; + } + } + + #endregion + + #region Protected Methods (Import) Layout chunk + + /// + /// Extract layout from layout chunk. + /// + /// The composer chunk data. + /// The chunk index map. + /// The all segment bytes. + /// The layout. + protected virtual VgoLayout ExtractLayout(VgoComposerChunkData composerChunkData, VgoIndexChunkDataElement[] chunkIndexMap, ArraySegment allSegmentBytes) + { + try + { + ArraySegment layoutChunkData = ExtractChunkData(composerChunkData.LayoutChunkTypeId, chunkIndexMap, allSegmentBytes); + + VgoLayout? vgoLayout; + + if (composerChunkData.LayoutChunkTypeId == VgoChunkTypeID.LAPJ) + { + // JSON + vgoLayout = DeserializeObject(layoutChunkData.ToArray(), isBson: false); + } + else if (composerChunkData.LayoutChunkTypeId == VgoChunkTypeID.LAPB) + { + // BSON + vgoLayout = DeserializeObject(layoutChunkData.ToArray(), isBson: true); + } + else + { + throw new FormatException($"[COMP] LayoutChunkTypeId: {composerChunkData.LayoutChunkTypeId}"); + } + + if (vgoLayout is null) + { + throw new FormatException(); + } + + return vgoLayout; + } + catch (JsonSerializationException) + { + throw; + } + catch + { + throw; + } + } + + #endregion + + #region Protected Methods (Import) Resource Accessor chunk + + /// + /// Extract resource accessor from resource accessor chunk. + /// + /// The composer chunk data. + /// The chunk index map. + /// The all segment bytes. + /// The crypt key. + /// List of the resource accessor. + protected virtual List? ExtractResourceAccessor(VgoComposerChunkData composerChunkData, VgoIndexChunkDataElement[] chunkIndexMap, ArraySegment allSegmentBytes, byte[]? cryptKey = null) + { + VgoChunkTypeID raChunkTypeId = composerChunkData.ResourceAccessorChunkTypeId; + + switch (raChunkTypeId) + { + case VgoChunkTypeID.RAPJ: + case VgoChunkTypeID.RAPB: + case VgoChunkTypeID.RACJ: + case VgoChunkTypeID.RACB: + break; + default: + throw new FormatException($"[COMP] ResourceAccessorChunkTypeId: {raChunkTypeId}"); + } + + ArraySegment raChunkData = ExtractChunkData(raChunkTypeId, chunkIndexMap, allSegmentBytes); + + byte[]? plainJsonOrBson; + + if (raChunkTypeId == VgoChunkTypeID.RAPJ) + { + plainJsonOrBson = raChunkData.ToArray(); + } + else if (raChunkTypeId == VgoChunkTypeID.RAPB) + { + plainJsonOrBson = raChunkData.ToArray(); + } + else if ( + (raChunkTypeId == VgoChunkTypeID.RACJ) || + (raChunkTypeId == VgoChunkTypeID.RACB)) + { + VgoCryptV0? vgoCrypt = GetVgoCrypt(composerChunkData.ResourceAccessorCryptChunkTypeId, chunkIndexMap, allSegmentBytes); + + if (vgoCrypt is null) + { + throw new FormatException($" ResourceAccessorChunkTypeId: {raChunkTypeId}"); + } + + try + { + byte[] encryptedJsonOrBson = raChunkData.ToArray(); + + if (vgoCrypt.algorithms == VgoCryptographyAlgorithms.AES) + { + // AES + AesCrypter aesCrypter = new AesCrypter + { + CipherMode = vgoCrypt.cipherMode, + PaddingMode = vgoCrypt.paddingMode, + }; + + byte[] key; + + if (cryptKey == null) + { + if (string.IsNullOrEmpty(vgoCrypt.key)) + { + throw new Exception("crypt key is unknown."); + } + else + { + key = Convert.FromBase64String(vgoCrypt.key); + } + } + else + { + key = cryptKey; + } + + if (key == null) + { + throw new Exception("crypt key is unknown."); + } + + if (string.IsNullOrEmpty(vgoCrypt.iv)) + { + throw new Exception("iv is unknown."); + } + + byte[] iv = Convert.FromBase64String(vgoCrypt.iv); + + plainJsonOrBson = aesCrypter.Decrypt(encryptedJsonOrBson, key, iv); + } + else if (vgoCrypt.algorithms == VgoCryptographyAlgorithms.Base64) + { + // Base64 + string base64String = Encoding.UTF8.GetString(encryptedJsonOrBson); + + plainJsonOrBson = Convert.FromBase64String(base64String); + } + else + { + throw new NotSupportedException($"CryptographyAlgorithms: {vgoCrypt.algorithms}"); + } + + } + catch (JsonSerializationException) + { + throw; + } + catch + { + throw; + } + } + else + { + throw new FormatException($"[COMP] ResourceAccessorChunkTypeId: {raChunkTypeId}"); + } + + List? vgoResourceAccessors = null; + + try + { + if ((raChunkTypeId == VgoChunkTypeID.RAPJ) || + (raChunkTypeId == VgoChunkTypeID.RACJ)) + { + // JSON + vgoResourceAccessors = DeserializeObject>(plainJsonOrBson, isBson: false); + } + else if ( + (raChunkTypeId == VgoChunkTypeID.RAPB) || + (raChunkTypeId == VgoChunkTypeID.RACB)) + { + // BSON + vgoResourceAccessors = DeserializeObject>(plainJsonOrBson, isBson: true, rootValueAsArray: true); + } + } + catch (JsonSerializationException) + { + throw; + } + catch + { + throw; + } + + return vgoResourceAccessors; + } + + #endregion + + #region Protected Methods (Import) Resource chunk + + /// + /// Extract resouce bytes from resouce chunk. + /// + /// The composer chunk data. + /// The chunk index map. + /// The all segment bytes. + /// The resouce bytes. + protected virtual ArraySegment ExtractResource(VgoComposerChunkData composerChunkData, VgoIndexChunkDataElement[] chunkIndexMap, ArraySegment allSegmentBytes) + { + VgoChunkTypeID resourceChunkTypeId = composerChunkData.ResourceChunkTypeId; + + VgoResource? vgoResource; + + try + { + ArraySegment resouceChunkData = ExtractChunkData(resourceChunkTypeId, chunkIndexMap, allSegmentBytes); + + if (resourceChunkTypeId == VgoChunkTypeID.REPb) + { + return resouceChunkData; + } + else if (resourceChunkTypeId == VgoChunkTypeID.REPJ) + { + // JSON + vgoResource = DeserializeObject(resouceChunkData.ToArray(), isBson: false); + } + else if (resourceChunkTypeId == VgoChunkTypeID.REPB) + { + // BSON + vgoResource = DeserializeObject(resouceChunkData.ToArray(), isBson: true); + } + else + { + throw new FormatException($"[COMP] ResourceChunkTypeId: {resourceChunkTypeId}"); + } + + if (vgoResource is null) + { + throw new FormatException($"[{resourceChunkTypeId}] resource is null."); + } + } + catch (JsonSerializationException) + { + throw; + } + catch + { + throw; + } + + if (vgoResource.uri is null || vgoResource.uri == string.Empty) + { + throw new FormatException($"[{resourceChunkTypeId}] uri is null or empty."); + } + + if (vgoResource.byteLength == 0) + { + //throw new FormatException($"[{chunkTypeId}] byteLength: {vgoResource.byteLength}"); + } + + byte[] resourceBytes = GetUriData(vgoResource.uri); + + return new ArraySegment(resourceBytes); + } + + #endregion + + #region Protected Methods (Import) Crypt chunk + + /// + /// Get the vgo crypt. + /// + /// The crypt chunk type ID. + /// The chunk index map. + /// The all segment bytes. + /// The vgo crypt. + protected virtual VgoCryptV0? GetVgoCrypt(VgoChunkTypeID cryptChunkTypeId, VgoIndexChunkDataElement[] chunkIndexMap, ArraySegment allSegmentBytes) + { + try + { + ArraySegment cryptChunkData = ExtractChunkData(cryptChunkTypeId, chunkIndexMap, allSegmentBytes); + + VgoCryptV0? vgoCrypt; + + if (cryptChunkTypeId == VgoChunkTypeID.CRAJ) + { + // JSON + vgoCrypt = DeserializeObject(cryptChunkData.ToArray(), isBson: false); + } + else if (cryptChunkTypeId == VgoChunkTypeID.CRAB) + { + // BSON + vgoCrypt = DeserializeObject(cryptChunkData.ToArray(), isBson: true); + } + else + { + throw new Exception($"{nameof(cryptChunkTypeId)}: {cryptChunkTypeId}"); + } + + return vgoCrypt; + } + catch (JsonSerializationException) + { + throw; + } + catch + { + throw; + } + } + + #endregion + + #region Protected Methods (Import) Helper + + /// + /// Get URI data. + /// + /// The uri. + /// An byte array of uri data. + protected virtual byte[] GetUriData(string uri) + { + if (uri == null) + { + throw new ArgumentNullException(uri); + } + + if (uri.StartsWith("data:")) + { + throw new NotSupportedException(uri); + } + else if ( + uri.StartsWith("http://") || + uri.StartsWith("https://")) + { + using (HttpClient httpClient = new HttpClient() { Timeout = TimeSpan.FromSeconds(HttpTimeoutSeconds) }) + using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, uri)) + using (HttpResponseMessage response = httpClient.SendAsync(request).GetAwaiter().GetResult()) + { + if (response.IsSuccessStatusCode) + { + byte[] byteArray = response.Content.ReadAsByteArrayAsync().GetAwaiter().GetResult(); + + return byteArray; + } + else + { + throw new Exception(response.StatusCode.ToString()); + } + } + } + else if (uri.StartsWith("file://")) + { + throw new NotSupportedException(uri); + } + else + { + if (DirectoryPath == null) + { + throw new Exception(); + } + + string binFilePath = Path.Combine(DirectoryPath, uri); + + if (File.Exists(binFilePath) == false) + { + throw new FileNotFoundException(binFilePath); + } + + byte[] binData = File.ReadAllBytes(binFilePath); + + return binData; + } + } + + #endregion + } +} diff --git a/NewtonVgo/Runtime/Storage/VgoStorage.Import.cs.meta b/NewtonVgo/Runtime/Storage/VgoStorage.Import.cs.meta new file mode 100644 index 0000000..0250224 --- /dev/null +++ b/NewtonVgo/Runtime/Storage/VgoStorage.Import.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4f345e9f4b2e40840b54bb1a2ca8d573 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/NewtonVgo/Runtime/Storage/VgoStorage.cs b/NewtonVgo/Runtime/Storage/VgoStorage.cs index 8282a3b..07a5cb8 100644 --- a/NewtonVgo/Runtime/Storage/VgoStorage.cs +++ b/NewtonVgo/Runtime/Storage/VgoStorage.cs @@ -7,14 +7,10 @@ namespace NewtonVgo { using Newtonsoft.Json; using NewtonVgo.Buffers; - using NewtonVgo.Security.Cryptography; using NewtonVgo.Serialization; using System; using System.Collections.Generic; using System.IO; - using System.Linq; - using System.Net.Http; - using System.Runtime.InteropServices; using System.Text; /// @@ -103,9 +99,9 @@ public VgoStorage(string vgoFilePath, string? vgkFilePath = null) DirectoryPath = vgoFileInfo.DirectoryName; - byte[] allBytes = File.ReadAllBytes(vgoFilePath); + byte[] vgoBytes = File.ReadAllBytes(vgoFilePath); - byte[]? cryptKey = null; + byte[]? vgkBytes = null; if (vgkFilePath != null) { @@ -116,23 +112,23 @@ public VgoStorage(string vgoFilePath, string? vgkFilePath = null) throw new FileNotFoundException(vgkFilePath); } - cryptKey = File.ReadAllBytes(vgkFilePath); + vgkBytes = File.ReadAllBytes(vgkFilePath); } - ParseVgo(allBytes, cryptKey, out var layout); + ParseVgo(vgoBytes, vgkBytes, out var layout); Layout = layout; } /// - /// Create a new instance of VgoStorage with allBytes. + /// Create a new instance of VgoStorage with bytes. /// - /// All bytes of file. - /// The crypt key. + /// The vgo bytes. + /// The vgk bytes. /// for Import - public VgoStorage(byte[] allBytes, byte[]? cryptKey = null) + public VgoStorage(byte[] vgoBytes, byte[]? vgkBytes = null) { - ParseVgo(allBytes, cryptKey, out var layout); + ParseVgo(vgoBytes, vgkBytes, out var layout); Layout = layout; } @@ -162,1256 +158,6 @@ public VgoStorage(IByteBuffer resource, VgoGeometryCoordinate geometryCoordinate #endregion - #region Protected Methods (Import) - - /// - /// Parse vgo. - /// - /// All bytes of ".vgo" file. - /// The crypt key. - protected virtual void ParseVgo(byte[] allBytes, byte[]? cryptKey, out VgoLayout layout) - { - if (allBytes == null) - { - throw new ArgumentNullException(nameof(allBytes)); - } - - if (allBytes.Any() == false) - { - throw new ArgumentOutOfRangeException(nameof(allBytes)); - } - - if (allBytes.Length < 16) - { - throw new ArgumentOutOfRangeException(nameof(allBytes)); - } - - ArraySegment allSegmentBytes = new ArraySegment(allBytes); - - // Header - Header = GetHeader(allSegmentBytes); - - if ((Header.IsRequireResourceAccessorExternalCryptKey == 1) && (cryptKey == null)) - { - throw new Exception("cryptKey is required."); - } - - // Index chunk - ChunkIndexMap = GetChunkIndexMap(allSegmentBytes, indexByteOffset: 16); - - // Composer chunk - ArraySegment composerChunkDataBytes = ExtractChunkData(VgoChunkTypeID.COMP, ChunkIndexMap, allSegmentBytes); - - VgoComposerChunkData composerChunkData = composerChunkDataBytes - .ToArray() - .ConvertToStructure(); - - // Asset Info chunk - try - { - AssetInfo = ExtractAssetInfo(composerChunkData, ChunkIndexMap, allSegmentBytes); - } - catch - { - throw; - } - - // Layout chunk - try - { - layout = ExtractLayout(composerChunkData, ChunkIndexMap, allSegmentBytes); - } - catch - { - throw; - } - - // Resource Accessor chunk - try - { - ResourceAccessors = ExtractResourceAccessor(composerChunkData, ChunkIndexMap, allSegmentBytes, cryptKey); - } - catch - { - throw; - } - - // Resource chunk - try - { - ArraySegment resourceBytes = ExtractResource(composerChunkData, ChunkIndexMap, allSegmentBytes); - - Resource = new ReadOnlyArraySegmentByteBuffer(resourceBytes); - } - catch - { - throw; - } - } - - #endregion - - #region Protected Methods (Import) - - /// - /// Get header chunk. - /// - /// The all segment bytes. - /// The header chunk. - protected virtual VgoHeader GetHeader(ArraySegment allSegmentBytes) - { - try - { - int headerSize = Marshal.SizeOf(typeof(VgoHeader)); - - ArraySegment headerSegment = allSegmentBytes.Slice(offset: 0, count: headerSize); - - VgoHeader header = headerSegment.Array.ConvertToStructure(); - - if (header.Magic != (uint)VgoChunkTypeID.Vgo) - { - throw new FormatException($"Header.Magic: {header.Magic}"); - } - - if (header.DataLength != 8) - { - throw new FormatException($"Header.DataLength: {header.DataLength}"); - } - - if (header.MajorVersion == 0) - { - throw new FormatException($"Header.MajorVersion: {header.MajorVersion}"); - } - - if ((header.GeometryCoordinate != VgoGeometryCoordinate.RightHanded) && - (header.GeometryCoordinate != VgoGeometryCoordinate.LeftHanded)) - { - throw new FormatException($"Header.GeometryCoordinate: {header.GeometryCoordinate}"); - } - - if ((header.UVCoordinate != VgoUVCoordinate.TopLeft) && - (header.UVCoordinate != VgoUVCoordinate.BottomLeft)) - { - throw new FormatException($"Header.UVCoordinate: {header.UVCoordinate}"); - } - - return header; - } - catch - { - throw; - } - } - - /// - /// Get chunk index map. - /// - /// The all segment bytes. - /// The start position of the index chunk. - /// The chunk index map. - /// - /// Index chunk (8 + 16 * n byte) - /// - protected virtual VgoIndexChunkDataElement[] GetChunkIndexMap(ArraySegment allSegmentBytes, int indexByteOffset) - { - try - { - uint indexChunkTypeId = BitConverter.ToUInt32(allSegmentBytes.Array, indexByteOffset); - - if (indexChunkTypeId != (uint)VgoChunkTypeID.Idx) - { - throw new FormatException($"[IDX] Chunk.ChunkTypeId: {indexChunkTypeId}"); - } - - uint indexChunkDataLength = BitConverter.ToUInt32(allSegmentBytes.Array, indexByteOffset + 4); - - if (indexChunkDataLength == 0) - { - throw new FormatException($"[IDX] Chunk.DataLength: {indexChunkDataLength}"); - } - - int elementSize = Marshal.SizeOf(typeof(VgoIndexChunkDataElement)); - - if ((indexChunkDataLength % elementSize) != 0) - { - throw new FormatException($"[IDX] Chunk.DataLength: {indexChunkDataLength}"); - } - - int elementCount = (int)indexChunkDataLength / elementSize; - - VgoIndexChunkDataElement[] chunkIndexMap = new VgoIndexChunkDataElement[elementCount]; - - allSegmentBytes - .Slice(offset: indexByteOffset + 8, count: elementSize * elementCount) - .MarshalCopyTo(chunkIndexMap); - - return chunkIndexMap; - } - catch - { - throw; - } - } - - /// - /// Get the index chunk data of the specified chunk type ID from the chunk index map. - /// - /// The chunk type ID. - /// The chunk index map. - /// - protected virtual VgoIndexChunkDataElement GetIndexChunkDataElement(VgoChunkTypeID chunkTypeId, VgoIndexChunkDataElement[] chunkIndexMap) - { - try - { - int count = chunkIndexMap.Where(x => x.ChunkTypeId == chunkTypeId).Count(); - - if (count == 0) - { - throw new FormatException($"{chunkTypeId} is not define in IDX chunk."); - } - - if (count >= 2) - { - throw new FormatException($"{chunkTypeId} is defined more than once in IDX chunk."); - } - - VgoIndexChunkDataElement chunkInfo = chunkIndexMap.Where(x => x.ChunkTypeId == chunkTypeId).First(); - - if (chunkInfo.ByteOffset == 0) - { - throw new FormatException($"[{chunkTypeId}] Chunk.ByteOffset: {chunkInfo.ByteOffset}"); - } - - if (chunkInfo.ByteLength == 0) - { - //throw new FormatException($"[{chunkTypeId}] Chunk.ByteLength: {chunkInfo.ByteLength}"); - } - - //if ((chunkInfo.ByteOffset + chunkInfo.ByteLength) > allBytes.Length) - //{ - // throw new FormatException($"[{chunkTypeId}] Chunk.ByteLength: {chunkInfo.ByteLength} is out of the range."); - //} - - return chunkInfo; - } - catch - { - throw; - } - } - - /// - /// Extract chunk data from asset info chunk. - /// - /// The chunk type ID. - /// The chunk index map. - /// The all segment bytes. - /// The chunk data bytes. - protected virtual ArraySegment ExtractChunkData(VgoChunkTypeID chunkTypeId, VgoIndexChunkDataElement[] chunkIndexMap, ArraySegment allSegmentBytes) - { - VgoIndexChunkDataElement chunkIndexInfo = GetIndexChunkDataElement(chunkTypeId, chunkIndexMap); - - uint chunkChunkTypeId = BitConverter.ToUInt32(allSegmentBytes.Array, (int)chunkIndexInfo.ByteOffset); - - if (chunkChunkTypeId != (uint)chunkIndexInfo.ChunkTypeId) - { - throw new FormatException($"[{chunkIndexInfo.ChunkTypeId}] Chunk.ChunkTypeId: {chunkChunkTypeId}"); - } - - uint chunkDataLength = BitConverter.ToUInt32(allSegmentBytes.Array, (int)chunkIndexInfo.ByteOffset + 4); - - if (chunkDataLength == 0) - { - throw new FormatException($"[{chunkIndexInfo.ChunkTypeId}] Chunk.DataLength: {chunkDataLength}"); - } - - ArraySegment chunkData = allSegmentBytes.Slice( - offset: (int)chunkIndexInfo.ByteOffset + 8, - count: (int)chunkDataLength - chunkIndexInfo.BytePadding - ); - - return chunkData; - } - - #endregion - - #region Protected Methods (Import) Asset Info chunk - - /// - /// Extract asset info from asset info chunk. - /// - /// The composer chunk data. - /// The chunk index map. - /// The all segment bytes. - /// The asset info. - protected virtual VgoAssetInfo? ExtractAssetInfo(VgoComposerChunkData composerChunkData, VgoIndexChunkDataElement[] chunkIndexMap, ArraySegment allSegmentBytes) - { - try - { - ArraySegment assetInfoChunkData = ExtractChunkData(composerChunkData.AssetInfoChunkTypeId, chunkIndexMap, allSegmentBytes); - - VgoAssetInfo? vgoAssetInfo; - - if (composerChunkData.AssetInfoChunkTypeId == VgoChunkTypeID.AIPJ) - { - // JSON - vgoAssetInfo = DeserializeObject(assetInfoChunkData.ToArray(), isBson: false); - } - else if (composerChunkData.AssetInfoChunkTypeId == VgoChunkTypeID.AIPB) - { - // BSON - vgoAssetInfo = DeserializeObject(assetInfoChunkData.ToArray(), isBson: true); - } - else - { - throw new FormatException($"[COMP] AssetInfoChunkTypeId: {composerChunkData.AssetInfoChunkTypeId}"); - } - - return vgoAssetInfo; - } - catch (JsonSerializationException) - { - throw; - } - catch - { - throw; - } - } - - #endregion - - #region Protected Methods (Import) Layout chunk - - /// - /// Extract layout from layout chunk. - /// - /// The composer chunk data. - /// The chunk index map. - /// The all segment bytes. - /// The layout. - protected virtual VgoLayout ExtractLayout(VgoComposerChunkData composerChunkData, VgoIndexChunkDataElement[] chunkIndexMap, ArraySegment allSegmentBytes) - { - try - { - ArraySegment layoutChunkData = ExtractChunkData(composerChunkData.LayoutChunkTypeId, chunkIndexMap, allSegmentBytes); - - VgoLayout? vgoLayout; - - if (composerChunkData.LayoutChunkTypeId == VgoChunkTypeID.LAPJ) - { - // JSON - vgoLayout = DeserializeObject(layoutChunkData.ToArray(), isBson: false); - } - else if (composerChunkData.LayoutChunkTypeId == VgoChunkTypeID.LAPB) - { - // BSON - vgoLayout = DeserializeObject(layoutChunkData.ToArray(), isBson: true); - } - else - { - throw new FormatException($"[COMP] LayoutChunkTypeId: {composerChunkData.LayoutChunkTypeId}"); - } - - if (vgoLayout is null) - { - throw new FormatException(); - } - - return vgoLayout; - } - catch (JsonSerializationException) - { - throw; - } - catch - { - throw; - } - } - - #endregion - - #region Protected Methods (Import) Resource Accessor chunk - - /// - /// Extract resource accessor from resource accessor chunk. - /// - /// The composer chunk data. - /// The chunk index map. - /// The all segment bytes. - /// The crypt key. - /// List of the resource accessor. - protected virtual List? ExtractResourceAccessor(VgoComposerChunkData composerChunkData, VgoIndexChunkDataElement[] chunkIndexMap, ArraySegment allSegmentBytes, byte[]? cryptKey = null) - { - VgoChunkTypeID raChunkTypeId = composerChunkData.ResourceAccessorChunkTypeId; - - switch (raChunkTypeId) - { - case VgoChunkTypeID.RAPJ: - case VgoChunkTypeID.RAPB: - case VgoChunkTypeID.RACJ: - case VgoChunkTypeID.RACB: - break; - default: - throw new FormatException($"[COMP] ResourceAccessorChunkTypeId: {raChunkTypeId}"); - } - - ArraySegment raChunkData = ExtractChunkData(raChunkTypeId, chunkIndexMap, allSegmentBytes); - - byte[]? plainJsonOrBson; - - if (raChunkTypeId == VgoChunkTypeID.RAPJ) - { - plainJsonOrBson = raChunkData.ToArray(); - } - else if (raChunkTypeId == VgoChunkTypeID.RAPB) - { - plainJsonOrBson = raChunkData.ToArray(); - } - else if ( - (raChunkTypeId == VgoChunkTypeID.RACJ) || - (raChunkTypeId == VgoChunkTypeID.RACB)) - { - VgoCryptV0? vgoCrypt = GetVgoCrypt(composerChunkData.ResourceAccessorCryptChunkTypeId, chunkIndexMap, allSegmentBytes); - - if (vgoCrypt is null) - { - throw new FormatException($" ResourceAccessorChunkTypeId: {raChunkTypeId}"); - } - - try - { - byte[] encryptedJsonOrBson = raChunkData.ToArray(); - - if (vgoCrypt.algorithms == VgoCryptographyAlgorithms.AES) - { - // AES - AesCrypter aesCrypter = new AesCrypter - { - CipherMode = vgoCrypt.cipherMode, - PaddingMode = vgoCrypt.paddingMode, - }; - - byte[] key; - - if (cryptKey == null) - { - if (string.IsNullOrEmpty(vgoCrypt.key)) - { - throw new Exception("crypt key is unknown."); - } - else - { - key = Convert.FromBase64String(vgoCrypt.key); - } - } - else - { - key = cryptKey; - } - - if (key == null) - { - throw new Exception("crypt key is unknown."); - } - - if (string.IsNullOrEmpty(vgoCrypt.iv)) - { - throw new Exception("iv is unknown."); - } - - byte[] iv = Convert.FromBase64String(vgoCrypt.iv); - - plainJsonOrBson = aesCrypter.Decrypt(encryptedJsonOrBson, key, iv); - } - else if (vgoCrypt.algorithms == VgoCryptographyAlgorithms.Base64) - { - // Base64 - string base64String = Encoding.UTF8.GetString(encryptedJsonOrBson); - - plainJsonOrBson = Convert.FromBase64String(base64String); - } - else - { - throw new NotSupportedException($"CryptographyAlgorithms: {vgoCrypt.algorithms}"); - } - - } - catch (JsonSerializationException) - { - throw; - } - catch - { - throw; - } - } - else - { - throw new FormatException($"[COMP] ResourceAccessorChunkTypeId: {raChunkTypeId}"); - } - - List? vgoResourceAccessors = null; - - try - { - if ((raChunkTypeId == VgoChunkTypeID.RAPJ) || - (raChunkTypeId == VgoChunkTypeID.RACJ)) - { - // JSON - vgoResourceAccessors = DeserializeObject>(plainJsonOrBson, isBson: false); - } - else if ( - (raChunkTypeId == VgoChunkTypeID.RAPB) || - (raChunkTypeId == VgoChunkTypeID.RACB)) - { - // BSON - vgoResourceAccessors = DeserializeObject>(plainJsonOrBson, isBson: true, rootValueAsArray: true); - } - } - catch (JsonSerializationException) - { - throw; - } - catch - { - throw; - } - - return vgoResourceAccessors; - } - - #endregion - - #region Protected Methods (Import) Resource chunk - - /// - /// Extract resouce bytes from resouce chunk. - /// - /// The composer chunk data. - /// The chunk index map. - /// The all segment bytes. - /// The resouce bytes. - protected virtual ArraySegment ExtractResource(VgoComposerChunkData composerChunkData, VgoIndexChunkDataElement[] chunkIndexMap, ArraySegment allSegmentBytes) - { - VgoChunkTypeID resourceChunkTypeId = composerChunkData.ResourceChunkTypeId; - - VgoResource? vgoResource; - - try - { - ArraySegment resouceChunkData = ExtractChunkData(resourceChunkTypeId, chunkIndexMap, allSegmentBytes); - - if (resourceChunkTypeId == VgoChunkTypeID.REPb) - { - return resouceChunkData; - } - else if (resourceChunkTypeId == VgoChunkTypeID.REPJ) - { - // JSON - vgoResource = DeserializeObject(resouceChunkData.ToArray(), isBson: false); - } - else if (resourceChunkTypeId == VgoChunkTypeID.REPB) - { - // BSON - vgoResource = DeserializeObject(resouceChunkData.ToArray(), isBson: true); - } - else - { - throw new FormatException($"[COMP] ResourceChunkTypeId: {resourceChunkTypeId}"); - } - - if (vgoResource is null) - { - throw new FormatException($"[{resourceChunkTypeId}] resource is null."); - } - } - catch (JsonSerializationException) - { - throw; - } - catch - { - throw; - } - - if (vgoResource.uri is null || vgoResource.uri == string.Empty) - { - throw new FormatException($"[{resourceChunkTypeId}] uri is null or empty."); - } - - if (vgoResource.byteLength == 0) - { - //throw new FormatException($"[{chunkTypeId}] byteLength: {vgoResource.byteLength}"); - } - - byte[] resourceBytes = GetUriData(vgoResource.uri); - - return new ArraySegment(resourceBytes); - } - - #endregion - - #region Protected Methods (Import) Crypt chunk - - /// - /// Get the vgo crypt. - /// - /// The crypt chunk type ID. - /// The chunk index map. - /// The all segment bytes. - /// The vgo crypt. - protected virtual VgoCryptV0? GetVgoCrypt(VgoChunkTypeID cryptChunkTypeId, VgoIndexChunkDataElement[] chunkIndexMap, ArraySegment allSegmentBytes) - { - try - { - ArraySegment cryptChunkData = ExtractChunkData(cryptChunkTypeId, chunkIndexMap, allSegmentBytes); - - VgoCryptV0? vgoCrypt; - - if (cryptChunkTypeId == VgoChunkTypeID.CRAJ) - { - // JSON - vgoCrypt = DeserializeObject(cryptChunkData.ToArray(), isBson: false); - } - else if (cryptChunkTypeId == VgoChunkTypeID.CRAB) - { - // BSON - vgoCrypt = DeserializeObject(cryptChunkData.ToArray(), isBson: true); - } - else - { - throw new Exception($"{nameof(cryptChunkTypeId)}: {cryptChunkTypeId}"); - } - - return vgoCrypt; - } - catch (JsonSerializationException) - { - throw; - } - catch - { - throw; - } - } - - #endregion - - #region Protected Methods (Import) Helper - - /// - /// Get URI data. - /// - /// The uri. - /// An byte array of uri data. - protected virtual byte[] GetUriData(string uri) - { - if (uri == null) - { - throw new ArgumentNullException(uri); - } - - if (uri.StartsWith("data:")) - { - throw new NotSupportedException(uri); - } - else if ( - uri.StartsWith("http://") || - uri.StartsWith("https://")) - { - using (HttpClient httpClient = new HttpClient() { Timeout = TimeSpan.FromSeconds(HttpTimeoutSeconds) }) - using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, uri)) - using (HttpResponseMessage response = httpClient.SendAsync(request).GetAwaiter().GetResult()) - { - if (response.IsSuccessStatusCode) - { - byte[] byteArray = response.Content.ReadAsByteArrayAsync().GetAwaiter().GetResult(); - - return byteArray; - } - else - { - throw new Exception(response.StatusCode.ToString()); - } - } - } - else if (uri.StartsWith("file://")) - { - throw new NotSupportedException(uri); - } - else - { - if (DirectoryPath == null) - { - throw new Exception(); - } - - string binFilePath = Path.Combine(DirectoryPath, uri); - - if (File.Exists(binFilePath) == false) - { - throw new FileNotFoundException(binFilePath); - } - - byte[] binData = File.ReadAllBytes(binFilePath); - - return binData; - } - } - - #endregion - - #region Public Methods (Export) - - /// - /// Exports a VGO format file. - /// - /// The full path of the file. - /// A vgo export setting. - /// Returns true if the export was successful, false otherwise. - public virtual bool ExportVgoFile(string filePath, VgoExportSetting exportSetting) - { - if (filePath == null) - { - throw new ArgumentNullException(nameof(filePath)); - } - - if (AssetInfo == null) - { - throw new Exception(); - } - - if (Layout == null) - { - throw new Exception(); - } - - if (ResourceAccessors == null) - { - throw new Exception(); - } - - if (Resource == null) - { - throw new Exception(); - } - - if (exportSetting.Validate(out IReadOnlyList errorMessages) == false) - { - throw new Exception(string.Join("\n", errorMessages)); - } - - FileInfo fileInfo = new FileInfo(filePath); - - // Asset Info chunk - VgoChunk assetInfoChunk = CreateAssetInfoChunk(exportSetting.AssetInfoTypeId); - - // Layout chunk - VgoChunk layoutChunk = CreateLayoutChunk(exportSetting.LayoutTypeId); - - // Resource Accessor chunk - (VgoChunk resourceAccessorChunk, VgoChunk? resourceAccessorCryptChunk) = - CreateResourceAccessorChunk( - exportSetting.ResourceAccessorTypeId, - exportSetting.ResourceAccessorCryptTypeId, - exportSetting.ResourceAccessorCryptAlgorithm, - exportSetting.ResourceAccessorCryptKey - ); - - // Resource chunk - VgoChunk resourceChunk = CreateResourceChunk(exportSetting.ResourceTypeId, fileInfo, exportSetting.BinFileName, exportSetting.ResourceUri); - - // Composer chunk - VgoChunk composerChunk = CreateComposerChunk( - exportSetting.AssetInfoTypeId, - exportSetting.LayoutTypeId, - exportSetting.ResourceAccessorTypeId, - exportSetting.ResourceAccessorCryptTypeId, - exportSetting.ResourceTypeId - ); - - // Index chunk - List chunkList; - - if (resourceAccessorCryptChunk == null) - { - chunkList = new List(5) - { - composerChunk, - assetInfoChunk, - layoutChunk, - resourceAccessorChunk, - resourceChunk, - }; - } - else - { - chunkList = new List(6) - { - composerChunk, - assetInfoChunk, - layoutChunk, - resourceAccessorChunk, - resourceAccessorCryptChunk, - resourceChunk, - }; - } - - VgoChunk indexChunk = CreateIndexChunk(chunkList); - - // Header - if (exportSetting.ResourceAccessorCryptTypeId != VgoChunkTypeID.None) - { - Header.ResourceAccessorIsCrypted = 1; - - if (exportSetting.ResourceAccessorCryptKey != null) - { - Header.IsRequireResourceAccessorExternalCryptKey = 1; - } - } - - // Output (.vgo) - using (var stream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None)) - using (var writer = new BinaryWriter(stream)) - { - writer.Write(Header.ConvertToByteArray()); - writer.Write(indexChunk.ConvertToByteArray()); - writer.Write(composerChunk.ConvertToByteArray()); - writer.Write(assetInfoChunk.ConvertToByteArray()); - writer.Write(layoutChunk.ConvertToByteArray()); - writer.Write(resourceAccessorChunk.ConvertToByteArray()); - if (resourceAccessorCryptChunk != null) - { - writer.Write(resourceAccessorCryptChunk.ConvertToByteArray()); - } - writer.Write(resourceChunk.ConvertToByteArray()); - - writer.Flush(); - } - - if (exportSetting.ResourceAccessorCryptKey != null) - { - string keyFileName = fileInfo.Name.Substring(0, fileInfo.Name.Length - fileInfo.Extension.Length) + ".vgk"; - - string keyFullPath = Path.Combine(fileInfo.DirectoryName, keyFileName); - - // Output (.vgk) - using (var stream = new FileStream(keyFullPath, FileMode.Create, FileAccess.Write, FileShare.None)) - using (var writer = new BinaryWriter(stream)) - { - writer.Write(exportSetting.ResourceAccessorCryptKey); - - writer.Flush(); - } - } - - if (exportSetting.ResourceTypeId != VgoChunkTypeID.REPb) - { - string binFullPath = Path.Combine(fileInfo.DirectoryName, exportSetting.BinFileName); // @notice binFileName - - // Output (.bin) - using (var stream = new FileStream(binFullPath, FileMode.Create, FileAccess.Write, FileShare.None)) - using (var writer = new BinaryWriter(stream)) - { - writer.Write(Resource.ToArray()); - - writer.Flush(); - } - } - - return true; - } - - #endregion - - #region Protected Methods (Export) - - /// - /// Create an asset info chunk. - /// - /// The asset info chunk type ID. - /// An asset info chunk. - public virtual VgoChunk CreateAssetInfoChunk(VgoChunkTypeID assetInfoTypeId) - { - IByteBuffer assetInfoChunkData; - - try - { - if (assetInfoTypeId == VgoChunkTypeID.AIPJ) - { - // JSON - byte[] json = SerializeObject(AssetInfo, isBson: false); - - assetInfoChunkData = new ReadOnlyArraySegmentByteBuffer(new ArraySegment(json)); - } - else if (assetInfoTypeId == VgoChunkTypeID.AIPB) - { - // BSON - byte[] bson = SerializeObject(AssetInfo, isBson: true); - - assetInfoChunkData = new ReadOnlyArraySegmentByteBuffer(new ArraySegment(bson)); - } - else - { - throw new Exception($"{nameof(assetInfoTypeId)}: {assetInfoTypeId}"); - } - } - catch (JsonSerializationException) - { - throw; - } - catch - { - throw; - } - - VgoChunk assetInfoChunk = new VgoChunk(assetInfoTypeId, assetInfoChunkData); - - return assetInfoChunk; - } - - /// - /// Create a layout chunk. - /// - /// The layout chunk type ID. - /// A layout chunk. - protected virtual VgoChunk CreateLayoutChunk(VgoChunkTypeID layoutTypeId) - { - IByteBuffer layoutChunkData; - - try - { - if (layoutTypeId == VgoChunkTypeID.LAPJ) - { - // JSON - byte[] json = SerializeObject(Layout, isBson: false); - - layoutChunkData = new ReadOnlyArraySegmentByteBuffer(new ArraySegment(json)); - } - else if (layoutTypeId == VgoChunkTypeID.LAPB) - { - // BSON - byte[] bson = SerializeObject(Layout, isBson: true); - - layoutChunkData = new ReadOnlyArraySegmentByteBuffer(new ArraySegment(bson)); - } - else - { - throw new Exception($"{nameof(layoutTypeId)}: {layoutTypeId}"); - } - } - catch (JsonSerializationException) - { - throw; - } - catch - { - throw; - } - - VgoChunk layoutChunk = new VgoChunk(layoutTypeId, layoutChunkData); - - return layoutChunk; - } - - /// - /// Create a resource accessor chunk. - /// - /// The resource accessor chunk type ID. - /// The resource accessor crypt chunk type ID. - /// The crypt algorithm. - /// The crypt key. - /// A resource accessor chunk and a crypt chunk. - protected virtual (VgoChunk, VgoChunk?) CreateResourceAccessorChunk( - VgoChunkTypeID resourceAccessorTypeId, - VgoChunkTypeID resourceAccessorCryptTypeId, - string? cryptAlgorithm = null, - byte[]? cryptKey = null) - { - IByteBuffer resourceAccessorChunkData; - IByteBuffer? resourceAccessorCryptChunkData = null; - - try - { - byte[] plainResourceAccessorJsonOrBson; - - if ((resourceAccessorTypeId == VgoChunkTypeID.RAPJ) || - (resourceAccessorTypeId == VgoChunkTypeID.RACJ)) - { - // JSON - plainResourceAccessorJsonOrBson = SerializeObject(ResourceAccessors, isBson: false); - } - else if ( - (resourceAccessorTypeId == VgoChunkTypeID.RAPB) || - (resourceAccessorTypeId == VgoChunkTypeID.RACB)) - { - // BSON - plainResourceAccessorJsonOrBson = SerializeObject(ResourceAccessors, isBson: true); - } - else - { - throw new Exception($"{nameof(resourceAccessorTypeId)}: {resourceAccessorTypeId}"); - } - - if ((resourceAccessorTypeId == VgoChunkTypeID.RAPJ) || - (resourceAccessorTypeId == VgoChunkTypeID.RAPB)) - { - resourceAccessorChunkData = new ReadOnlyArraySegmentByteBuffer(new ArraySegment(plainResourceAccessorJsonOrBson)); - } - else if ( - (resourceAccessorTypeId == VgoChunkTypeID.RACJ) || - (resourceAccessorTypeId == VgoChunkTypeID.RACB)) - { - byte[] encryptedResourceAccessorJsonOrBson; - - VgoCryptV0 cryptInfo; - - if (cryptAlgorithm == VgoCryptographyAlgorithms.AES) - { - AesCrypter aesCrypter = new AesCrypter(); - - string? keyString; - string? ivString; - - // JSON or BSON (encrypt) - if (cryptKey == null) - { - encryptedResourceAccessorJsonOrBson = aesCrypter.Encrypt(plainResourceAccessorJsonOrBson, keySize: 128, blockSize: 128, out _, out keyString, out _, out ivString); - } - else - { - encryptedResourceAccessorJsonOrBson = aesCrypter.Encrypt(plainResourceAccessorJsonOrBson, cryptKey, blockSize: 128, out _, out ivString); - - keyString = null; // @notice Secret - } - - // CRAJ - cryptInfo = new VgoCryptV0 - { - algorithms = VgoCryptographyAlgorithms.AES, - key = keyString, - iv = ivString, - cipherMode = aesCrypter.CipherMode, - paddingMode = aesCrypter.PaddingMode, - }; - } - else if (cryptAlgorithm == VgoCryptographyAlgorithms.Base64) - { - // JSON or BSON (encrypt) - string base64String = Convert.ToBase64String(plainResourceAccessorJsonOrBson); - - encryptedResourceAccessorJsonOrBson = Encoding.UTF8.GetBytes(base64String); - - // CRAJ - cryptInfo = new VgoCryptV0 - { - algorithms = VgoCryptographyAlgorithms.Base64, - }; - } - else - { - throw new Exception($"resourceAccessorCryptAlgorithm: {cryptAlgorithm}"); - } - - byte[] cryptInfoJsonOrBson; - - if (resourceAccessorCryptTypeId == VgoChunkTypeID.CRAJ) - { - cryptInfoJsonOrBson = SerializeObject(cryptInfo, isBson: false); - } - else if (resourceAccessorCryptTypeId == VgoChunkTypeID.CRAB) - { - cryptInfoJsonOrBson = SerializeObject(cryptInfo, isBson: true); - } - else - { - throw new Exception($"{nameof(resourceAccessorCryptTypeId)}: {resourceAccessorCryptTypeId}"); - } - - resourceAccessorChunkData = new ReadOnlyArraySegmentByteBuffer(new ArraySegment(encryptedResourceAccessorJsonOrBson)); - resourceAccessorCryptChunkData = new ReadOnlyArraySegmentByteBuffer(new ArraySegment(cryptInfoJsonOrBson)); - } - else - { - throw new Exception($"{nameof(resourceAccessorTypeId)}: {resourceAccessorTypeId}"); - } - } - catch (JsonSerializationException) - { - throw; - } - catch - { - throw; - } - - VgoChunk resourceAccessorChunk = new VgoChunk(resourceAccessorTypeId, resourceAccessorChunkData); - - VgoChunk? resourceAccessorCryptChunk = null; - - if (resourceAccessorCryptChunkData != null) - { - resourceAccessorCryptChunk = new VgoChunk(resourceAccessorCryptTypeId, resourceAccessorCryptChunkData); - } - - return (resourceAccessorChunk, resourceAccessorCryptChunk); - } - - /// - /// Create a resource chunk. - /// - /// The resource chunk type ID. - /// The file info. - /// The resource binary file name. - /// The resource URI. - /// A resouce chunk. - protected virtual VgoChunk CreateResourceChunk(VgoChunkTypeID resourceTypeId, FileInfo fileInfo, string? binFileName = null, string? resourceUri = null) - { - if (binFileName == null) - { - binFileName = fileInfo.Name.Substring(0, fileInfo.Name.Length - fileInfo.Extension.Length) + ".bin"; - } - - if (resourceUri == null) - { - resourceUri = binFileName; - } - - if (Resource == null) - { - throw new Exception(); - } - - IByteBuffer resourceChunkData; - - try - { - var vgoResource = new VgoResource - { - uri = resourceUri, - byteLength = Resource.Length, - }; - - if (resourceTypeId == VgoChunkTypeID.REPb) - { - // binary - resourceChunkData = Resource; - } - else if (resourceTypeId == VgoChunkTypeID.REPJ) - { - // JSON - byte[] resourceJson = SerializeObject(vgoResource, isBson: false); - - resourceChunkData = new ReadOnlyArraySegmentByteBuffer(new ArraySegment(resourceJson)); - } - else if (resourceTypeId == VgoChunkTypeID.REPB) - { - // BSON - byte[] resourceBson = SerializeObject(vgoResource, isBson: true); - - resourceChunkData = new ReadOnlyArraySegmentByteBuffer(new ArraySegment(resourceBson)); - } - else - { - throw new Exception($"{nameof(resourceTypeId)}: {resourceTypeId}"); - } - } - catch (JsonSerializationException) - { - throw; - } - catch - { - throw; - } - - VgoChunk resourceChunk = new VgoChunk(resourceTypeId, resourceChunkData); - - return resourceChunk; - } - - /// - /// Create a composer chunk. - /// - /// The asset info chunk type ID. - /// The layout chunk type ID. - /// The resourse accessor chunk type ID. - /// The resourse accessor crypt chunk type ID. - /// The resourse chunk type ID. - /// A composer chunk. - public virtual VgoChunk CreateComposerChunk( - VgoChunkTypeID assetInfoTypeId, - VgoChunkTypeID layoutTypeId, - VgoChunkTypeID resourceAccessorTypeId, - VgoChunkTypeID resourceAccessorCryptTypeId, - VgoChunkTypeID resourceTypeId) - { - VgoComposerChunkData composerChunkData = new VgoComposerChunkData - { - AssetInfoChunkTypeId = assetInfoTypeId, - LayoutChunkTypeId = layoutTypeId, - ResourceAccessorChunkTypeId = resourceAccessorTypeId, - ResourceAccessorCryptChunkTypeId = resourceAccessorCryptTypeId, - ResourceCryptChunkTypeId = resourceAccessorCryptTypeId, - ResourceChunkTypeId = resourceTypeId, - }; - - byte[] compDataBytes = composerChunkData.ConvertToByteArray(); - - IByteBuffer composerCunkData = new ReadOnlyArraySegmentByteBuffer(new ArraySegment(compDataBytes)); - - VgoChunk composerChunk = new VgoChunk(VgoChunkTypeID.COMP, composerCunkData); - - return composerChunk; - } - - /// - /// Create a index chunk. - /// - /// List of chunk. - /// A index chunk. - protected virtual VgoChunk CreateIndexChunk(List chunkList) - { - VgoIndexChunkDataElement[] indexChunkDataArray = new VgoIndexChunkDataElement[chunkList.Count]; - - int headerSize = Marshal.SizeOf(typeof(VgoHeader)); - - int indexChunkDataLength = Marshal.SizeOf(typeof(VgoIndexChunkDataElement)) * chunkList.Count; - - int indexChunkSize = 8 + indexChunkDataLength; - - uint byteOffset = (uint)(headerSize + indexChunkSize); - - for (int idx = 0; idx < indexChunkDataArray.Length; idx++) - { - VgoChunk curChunk = chunkList[idx]; - - indexChunkDataArray[idx] = new VgoIndexChunkDataElement - { - ChunkTypeId = curChunk.TypeId, - ByteOffset = byteOffset, - ByteLength = curChunk.AllLength, - BytePadding = (byte)curChunk.PaddingCount, - }; - - byteOffset += curChunk.AllLength; - } - - ChunkIndexMap = indexChunkDataArray; - - var indicatorBuffer = new ArraySegmentByteBuffer(indexChunkDataLength); - - indicatorBuffer.Append(new ArraySegment(indexChunkDataArray)); - - VgoChunk indexChunk = new VgoChunk(VgoChunkTypeID.Idx, indicatorBuffer); - - return indexChunk; - } - - #endregion - #region Protected Methods (JSON or BSON) /// diff --git a/NewtonVgo/Runtime/Structs/Bounds.cs b/NewtonVgo/Runtime/Structs/Bounds.cs deleted file mode 100644 index 627aaae..0000000 --- a/NewtonVgo/Runtime/Structs/Bounds.cs +++ /dev/null @@ -1,41 +0,0 @@ -// ---------------------------------------------------------------------- -// @Namespace : NewtonVgo -// @Struct : Bounds -// ---------------------------------------------------------------------- -#nullable enable -namespace NewtonVgo -{ - using System; - using System.Numerics; - using System.Runtime.InteropServices; - - /// - /// Bounds - /// - [Serializable] - [StructLayout(LayoutKind.Sequential, Pack = 1)] - public struct Bounds - { - /// The center of the bounding box. - public Vector3 center; - - /// The total size of the box. - /// This is always twice as large as the extents. - public Vector3 size; - - ///// The extents of the Bounding Box. - ///// This is always half of the size of the Bounds. - //public Vector3 extents; - - /// - /// Creates a new Bounds with center and size. - /// - /// The location of the origin of the Bounds. - /// The dimensions of the Bounds. - public Bounds(Vector3 center, Vector3 size) - { - this.center = center; - this.size = size; - } - } -} diff --git a/NewtonVgo/Runtime/Structs/Color3.cs b/NewtonVgo/Runtime/Structs/Color3.cs deleted file mode 100644 index 11bc1a7..0000000 --- a/NewtonVgo/Runtime/Structs/Color3.cs +++ /dev/null @@ -1,46 +0,0 @@ -// ---------------------------------------------------------------------- -// @Namespace : NewtonVgo -// @Struct : Color3 -// ---------------------------------------------------------------------- -#nullable enable -namespace NewtonVgo -{ - using System; - using System.Runtime.InteropServices; - - /// - /// Color3 - /// - [Serializable] - [StructLayout(LayoutKind.Sequential, Pack = 1)] - public struct Color3 - { - /// Red - public float R; - - /// Green - public float G; - - /// Blue - public float B; - - /// - /// Create a new instance of Color3 with r, g, b. - /// - /// - /// - /// - public Color3(float r, float g, float b) - { - R = r; - G = g; - B = b; - } - - /// Black - public static Color3 Black => new Color3(0.0f, 0.0f, 0.0f); - - /// White - public static Color3 White => new Color3(1.0f, 1.0f, 1.0f); - } -} diff --git a/NewtonVgo/Runtime/Structs/Color4.cs b/NewtonVgo/Runtime/Structs/Color4.cs deleted file mode 100644 index d25b5de..0000000 --- a/NewtonVgo/Runtime/Structs/Color4.cs +++ /dev/null @@ -1,58 +0,0 @@ -// ---------------------------------------------------------------------- -// @Namespace : NewtonVgo -// @Struct : Color4 -// ---------------------------------------------------------------------- -#nullable enable -namespace NewtonVgo -{ - using System; - using System.Runtime.InteropServices; - - /// - /// Color4 - /// - [Serializable] - [StructLayout(LayoutKind.Sequential, Pack = 1)] - public struct Color4 - { - /// Red - public float R; - - /// Green - public float G; - - /// Blue - public float B; - - /// Alpha - public float A; - - /// - /// Create a new instance of Color4 with r, g, b. - /// - /// - /// - /// - public Color4(float r, float g, float b) : this(r, g, b, 1.0f) { } - - /// - /// Create a new instance of Color4 with r, g, b, a. - /// - /// - /// - /// - public Color4(float r, float g, float b, float a) - { - R = r; - G = g; - B = b; - A = a; - } - - /// Black - public static Color4 Black => new Color4(0.0f, 0.0f, 0.0f, 1.0f); - - /// White - public static Color4 White => new Color4(1.0f, 1.0f, 1.0f, 1.0f); - } -} diff --git a/NewtonVgo/Runtime/Structs/Vector4Ubyte.cs b/NewtonVgo/Runtime/Structs/Vector4Ubyte.cs deleted file mode 100644 index 02b2a02..0000000 --- a/NewtonVgo/Runtime/Structs/Vector4Ubyte.cs +++ /dev/null @@ -1,45 +0,0 @@ -// ---------------------------------------------------------------------- -// @Namespace : NewtonVgo -// @Struct : Vector4Byte -// ---------------------------------------------------------------------- -#nullable enable -namespace NewtonVgo -{ - using System; - using System.Runtime.InteropServices; - - /// - /// Vector4 (byte) - /// - [Serializable] - [StructLayout(LayoutKind.Sequential, Pack = 1)] - public struct Vector4Ubyte - { - /// - public byte X; - - /// - public byte Y; - - /// - public byte Z; - - /// - public byte W; - - /// - /// Create a new instance of Vector4Ubyte with x and y and z and w. - /// - /// - /// - /// - /// - public Vector4Ubyte(byte x, byte y, byte z, byte w) - { - X = x; - Y = y; - Z = z; - W = w; - } - } -} diff --git a/NewtonVgo/Runtime/Structs/Vector4Uint.cs b/NewtonVgo/Runtime/Structs/Vector4Uint.cs deleted file mode 100644 index 1510f68..0000000 --- a/NewtonVgo/Runtime/Structs/Vector4Uint.cs +++ /dev/null @@ -1,45 +0,0 @@ -// ---------------------------------------------------------------------- -// @Namespace : NewtonVgo -// @Struct : Vector4Uint -// ---------------------------------------------------------------------- -#nullable enable -namespace NewtonVgo -{ - using System; - using System.Runtime.InteropServices; - - /// - /// Vector4 (uint) - /// - [Serializable] - [StructLayout(LayoutKind.Sequential, Pack = 1)] - public struct Vector4Uint - { - /// - public uint X; - - /// - public uint Y; - - /// - public uint Z; - - /// - public uint W; - - /// - /// Create a new instance of Vector4Uint with x and y and z and w. - /// - /// - /// - /// - /// - public Vector4Uint(uint x, uint y, uint z, uint w) - { - X = x; - Y = y; - Z = z; - W = w; - } - } -} diff --git a/NewtonVgo/Runtime/Structs/Vector4Ushort.cs b/NewtonVgo/Runtime/Structs/Vector4Ushort.cs deleted file mode 100644 index cae179c..0000000 --- a/NewtonVgo/Runtime/Structs/Vector4Ushort.cs +++ /dev/null @@ -1,45 +0,0 @@ -// ---------------------------------------------------------------------- -// @Namespace : NewtonVgo -// @Struct : Vector4Ushort -// ---------------------------------------------------------------------- -#nullable enable -namespace NewtonVgo -{ - using System; - using System.Runtime.InteropServices; - - /// - /// Vector4 (ushort) - /// - [Serializable] - [StructLayout(LayoutKind.Sequential, Pack = 1)] - public struct Vector4Ushort - { - /// - public ushort X; - - /// - public ushort Y; - - /// - public ushort Z; - - /// - public ushort W; - - /// - /// Create a new instance of Vector4Ushort with x and y and z and w. - /// - /// - /// - /// - /// - public Vector4Ushort(ushort x, ushort y, ushort z, ushort w) - { - X = x; - Y = y; - Z = z; - W = w; - } - } -} diff --git a/NewtonVgo/Runtime/Structs.meta b/NewtonVgo/Runtime/Structures.meta similarity index 100% rename from NewtonVgo/Runtime/Structs.meta rename to NewtonVgo/Runtime/Structures.meta diff --git a/NewtonVgo/Runtime/Structures/Bounds.cs b/NewtonVgo/Runtime/Structures/Bounds.cs new file mode 100644 index 0000000..5d88201 --- /dev/null +++ b/NewtonVgo/Runtime/Structures/Bounds.cs @@ -0,0 +1,116 @@ +// ---------------------------------------------------------------------- +// @Namespace : NewtonVgo +// @Struct : Bounds +// ---------------------------------------------------------------------- +#nullable enable +namespace NewtonVgo +{ + using System; + using System.Numerics; + using System.Runtime.InteropServices; + + /// + /// Bounds + /// + [Serializable] + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct Bounds + { + #region Fields + + /// The center of the bounding box. + public Vector3 center; + + /// The total size of the box. + /// This is always twice as large as the extents. + public Vector3 size; + + ///// The extents of the Bounding Box. + ///// This is always half of the size of the Bounds. + //public Vector3 extents; + + #endregion + + #region Constructors + + /// + /// Creates a new Bounds with center and size. + /// + /// The location of the origin of the Bounds. + /// The dimensions of the Bounds. + public Bounds(Vector3 center, Vector3 size) + { + this.center = center; + this.size = size; + } + + #endregion + + #region Public Methods + + /// + /// Deconstruct Bounds to center and size; + /// + /// + /// + public void Deconstruct(out Vector3 center, out Vector3 size) + { + center = this.center; + size = this.size; + } + + /// + /// Determines whether the specified object is equal to the current object. + /// + /// + /// true if the specified object is equal to the current object; otherwise, false. + public bool Equals(Bounds other) + { + return + center.Equals(other.center) && + size.Equals(other.size); + } + + /// + /// Determines whether the specified object is equal to the current object. + /// + /// + /// true if the specified object is equal to the current object; otherwise, false. + public override bool Equals(object? other) + { + if (other is Bounds otherBounds) + { + return Equals(otherBounds); + } + + return false; + } + + /// + /// Serves as the default hash function. + /// + /// A hash code for the current object. + public override int GetHashCode() + { + return + center.GetHashCode() ^ + (size.GetHashCode() << 2); + } + + #endregion + + #region Operators + + public static bool operator ==(Bounds b1, Bounds b2) + { + return b1.Equals(b2); + } + + public static bool operator !=(Bounds b1, Bounds b2) + { + return b1.Equals(b2) == false; + } + + #endregion + } +} diff --git a/NewtonVgo/Runtime/Structs/Bounds.cs.meta b/NewtonVgo/Runtime/Structures/Bounds.cs.meta similarity index 100% rename from NewtonVgo/Runtime/Structs/Bounds.cs.meta rename to NewtonVgo/Runtime/Structures/Bounds.cs.meta diff --git a/NewtonVgo/Runtime/Structures/Color3.cs b/NewtonVgo/Runtime/Structures/Color3.cs new file mode 100644 index 0000000..b8c0779 --- /dev/null +++ b/NewtonVgo/Runtime/Structures/Color3.cs @@ -0,0 +1,208 @@ +// ---------------------------------------------------------------------- +// @Namespace : NewtonVgo +// @Struct : Color3 +// ---------------------------------------------------------------------- +#nullable enable +namespace NewtonVgo +{ + using System; + using System.Runtime.InteropServices; + + /// + /// Color3 + /// + [Serializable] + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct Color3 + { + #region Fields + + /// Red + public float R; + + /// Green + public float G; + + /// Blue + public float B; + + #endregion + + #region Constructors + + /// + /// Create a new instance of Color3 with r, g, b. + /// + /// + /// + /// + public Color3(float r, float g, float b) + { + R = r; + G = g; + B = b; + } + + /// + /// Create a new instance of Color3 with color4. + /// + /// + public Color3(Color4 color4) + { + R = color4.R; + G = color4.G; + B = color4.B; + } + + #endregion + + #region Propetties + + /// Black + public static Color3 Black => new Color3(0.0f, 0.0f, 0.0f); + + /// White + public static Color3 White => new Color3(1.0f, 1.0f, 1.0f); + + #endregion + + #region Indexers + + /// + /// + /// + /// + /// + /// + public float this[int index] + { + get + { + switch (index) + { + case 0: + return R; + case 1: + return G; + case 2: + return B; + default: + throw new IndexOutOfRangeException($"index: {index} is out of range."); + } + } + + set + { + switch (index) + { + case 0: + R = value; + break; + case 1: + G = value; + break; + case 2: + B = value; + break; + default: + throw new IndexOutOfRangeException($"index: {index} is out of range."); + } + } + } + + #endregion + + #region Public Methods + + /// + /// Deconstruct Color4 to r, g, and b; + /// + /// + /// + /// + public void Deconstruct(out float r, out float g, out float b) + { + r = R; + g = G; + b = B; + } + + /// + /// Determines whether the specified object is equal to the current object. + /// + /// + /// true if the specified object is equal to the current object; otherwise, false. + public bool Equals(Color3 other) + { + return + R.Equals(other.R) && + G.Equals(other.G) && + B.Equals(other.B); + } + + /// + /// Determines whether the specified object is equal to the current object. + /// + /// + /// true if the specified object is equal to the current object; otherwise, false. + public override bool Equals(object? other) + { + if (other is Color3 otherColor3) + { + return Equals(otherColor3); + } + + return false; + } + + /// + /// Serves as the default hash function. + /// + /// A hash code for the current object. + public override int GetHashCode() + { + return + R.GetHashCode() ^ + (G.GetHashCode() << 2) ^ + (B.GetHashCode() >> 2); + } + + /// + /// Set R, G and B components of an existing Color3. + /// + /// + /// + /// + public void Set(float newR, float newG, float newB) + { + R = newR; + G = newG; + B = newB; + } + + /// + /// Convert Color3 to Color4. + /// + /// + public Color4 ToColor4(float a = 1.0f) + { + return new Color4(this, a); + } + + #endregion + + #region Operators + + public static bool operator ==(Color3 c1, Color3 c2) + { + return c1.Equals(c2); + } + + public static bool operator !=(Color3 c1, Color3 c2) + { + return c1.Equals(c2) == false; + } + + #endregion + } +} diff --git a/NewtonVgo/Runtime/Structs/Color3.cs.meta b/NewtonVgo/Runtime/Structures/Color3.cs.meta similarity index 100% rename from NewtonVgo/Runtime/Structs/Color3.cs.meta rename to NewtonVgo/Runtime/Structures/Color3.cs.meta diff --git a/NewtonVgo/Runtime/Structures/Color4.cs b/NewtonVgo/Runtime/Structures/Color4.cs new file mode 100644 index 0000000..526a7f8 --- /dev/null +++ b/NewtonVgo/Runtime/Structures/Color4.cs @@ -0,0 +1,234 @@ +// ---------------------------------------------------------------------- +// @Namespace : NewtonVgo +// @Struct : Color4 +// ---------------------------------------------------------------------- +#nullable enable +namespace NewtonVgo +{ + using System; + using System.Runtime.InteropServices; + + /// + /// Color4 + /// + [Serializable] + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct Color4 + { + #region Fields + + /// Red + public float R; + + /// Green + public float G; + + /// Blue + public float B; + + /// Alpha + public float A; + + #endregion + + #region Constructors + + /// + /// Create a new instance of Color4 with r, g, b. + /// + /// + /// + /// + public Color4(float r, float g, float b) : this(r, g, b, 1.0f) { } + + /// + /// Create a new instance of Color4 with r, g, b, a. + /// + /// + /// + /// + /// + public Color4(float r, float g, float b, float a) + { + R = r; + G = g; + B = b; + A = a; + } + + /// + /// Create a new instance of Color4 with color3 and a. + /// + /// + /// + public Color4(Color3 color3, float a = 1.0f) + { + R = color3.R; + G = color3.G; + B = color3.B; + A = a; + } + + #endregion + + #region Propetties + + /// Black + public static Color4 Black => new Color4(0.0f, 0.0f, 0.0f, 1.0f); + + /// White + public static Color4 White => new Color4(1.0f, 1.0f, 1.0f, 1.0f); + + #endregion + + #region Indexers + + /// + /// + /// + /// + /// + /// + public float this[int index] + { + get + { + switch (index) + { + case 0: + return R; + case 1: + return G; + case 2: + return B; + case 3: + return A; + default: + throw new IndexOutOfRangeException($"index: {index} is out of range."); + } + } + + set + { + switch (index) + { + case 0: + R = value; + break; + case 1: + G = value; + break; + case 2: + B = value; + break; + case 3: + A = value; + break; + default: + throw new IndexOutOfRangeException($"index: {index} is out of range."); + } + } + } + + #endregion + + #region Public Methods + + /// + /// Deconstruct Color4 to r, g, b and a; + /// + /// + /// + /// + /// + public void Deconstruct(out float r, out float g, out float b, out float a) + { + r = R; + g = G; + b = B; + a = A; + } + + /// + /// Determines whether the specified object is equal to the current object. + /// + /// + /// true if the specified object is equal to the current object; otherwise, false. + public bool Equals(Color4 other) + { + return + R.Equals(other.R) && + G.Equals(other.G) && + B.Equals(other.B) && + A.Equals(other.A); + } + + /// + /// Determines whether the specified object is equal to the current object. + /// + /// + /// true if the specified object is equal to the current object; otherwise, false. + public override bool Equals(object? other) + { + if (other is Color4 otherColor4) + { + return Equals(otherColor4); + } + + return false; + } + + /// + /// Serves as the default hash function. + /// + /// A hash code for the current object. + public override int GetHashCode() + { + return + R.GetHashCode() ^ + (G.GetHashCode() << 2) ^ + (B.GetHashCode() >> 2) ^ + (A.GetHashCode() >> 1); + } + + /// + /// Set R, G, B and A components of an existing Color4. + /// + /// + /// + /// + /// + public void Set(float newR, float newG, float newB, float newA) + { + R = newR; + G = newG; + B = newB; + R = newA; + } + + /// + /// Convert Color4 to Color3. + /// + /// + public Color3 ToColor3() + { + return new Color3(this); + } + + #endregion + + #region Operators + + public static bool operator ==(Color4 c1, Color4 c2) + { + return c1.Equals(c2); + } + + public static bool operator !=(Color4 c1, Color4 c2) + { + return c1.Equals(c2) == false; + } + + #endregion + } +} diff --git a/NewtonVgo/Runtime/Structs/Color4.cs.meta b/NewtonVgo/Runtime/Structures/Color4.cs.meta similarity index 100% rename from NewtonVgo/Runtime/Structs/Color4.cs.meta rename to NewtonVgo/Runtime/Structures/Color4.cs.meta diff --git a/NewtonVgo/Runtime/Structs/Matrix2.cs b/NewtonVgo/Runtime/Structures/Matrix2.cs similarity index 100% rename from NewtonVgo/Runtime/Structs/Matrix2.cs rename to NewtonVgo/Runtime/Structures/Matrix2.cs diff --git a/NewtonVgo/Runtime/Structs/Matrix2.cs.meta b/NewtonVgo/Runtime/Structures/Matrix2.cs.meta similarity index 100% rename from NewtonVgo/Runtime/Structs/Matrix2.cs.meta rename to NewtonVgo/Runtime/Structures/Matrix2.cs.meta diff --git a/NewtonVgo/Runtime/Structs/Matrix3.cs b/NewtonVgo/Runtime/Structures/Matrix3.cs similarity index 100% rename from NewtonVgo/Runtime/Structs/Matrix3.cs rename to NewtonVgo/Runtime/Structures/Matrix3.cs diff --git a/NewtonVgo/Runtime/Structs/Matrix3.cs.meta b/NewtonVgo/Runtime/Structures/Matrix3.cs.meta similarity index 100% rename from NewtonVgo/Runtime/Structs/Matrix3.cs.meta rename to NewtonVgo/Runtime/Structures/Matrix3.cs.meta diff --git a/NewtonVgo/Runtime/Structures/Vector4Ubyte.cs b/NewtonVgo/Runtime/Structures/Vector4Ubyte.cs new file mode 100644 index 0000000..f6d4ff7 --- /dev/null +++ b/NewtonVgo/Runtime/Structures/Vector4Ubyte.cs @@ -0,0 +1,212 @@ +// ---------------------------------------------------------------------- +// @Namespace : NewtonVgo +// @Struct : Vector4Byte +// ---------------------------------------------------------------------- +#nullable enable +namespace NewtonVgo +{ + using System; + using System.Runtime.InteropServices; + + /// + /// Vector4 (byte) + /// + [Serializable] + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct Vector4Ubyte + { + #region Fields + + /// + public byte X; + + /// + public byte Y; + + /// + public byte Z; + + /// + public byte W; + + #endregion + + #region Constructors + + /// + /// Create a new instance of Vector4Ubyte with x and y and z and w. + /// + /// + /// + /// + /// + public Vector4Ubyte(byte x, byte y, byte z, byte w) + { + X = x; + Y = y; + Z = z; + W = w; + } + + #endregion + + #region Indexers + + /// + /// + /// + /// + /// + /// + public byte this[int index] + { + get + { + switch (index) + { + case 0: + return X; + case 1: + return Y; + case 2: + return Z; + case 3: + return W; + default: + throw new IndexOutOfRangeException($"index: {index} is out of range."); + } + } + + set + { + switch (index) + { + case 0: + X = value; + break; + case 1: + Y = value; + break; + case 2: + Z = value; + break; + case 3: + W = value; + break; + default: + throw new IndexOutOfRangeException($"index: {index} is out of range."); + } + } + } + + #endregion + + #region Public Methods + + /// + /// Deconstruct Vector4Ubyte to x, y, z and w; + /// + /// + /// + /// + /// + public void Deconstruct(out byte x, out byte y, out byte z, out byte w) + { + x = X; + y = Y; + z = Z; + w = W; + } + + /// + /// Determines whether the specified object is equal to the current object. + /// + /// + /// true if the specified object is equal to the current object; otherwise, false. + public bool Equals(Vector4Ubyte other) + { + return + X.Equals(other.X) && + Y.Equals(other.Y) && + Z.Equals(other.Z) && + W.Equals(other.W); + } + + /// + /// Determines whether the specified object is equal to the current object. + /// + /// + /// true if the specified object is equal to the current object; otherwise, false. + public override bool Equals(object? other) + { + if (other is Vector4Ubyte otherVector4) + { + return Equals(otherVector4); + } + + return false; + } + + /// + /// Serves as the default hash function. + /// + /// A hash code for the current object. + public override int GetHashCode() + { + return + X.GetHashCode() ^ + (Y.GetHashCode() << 2) ^ + (Z.GetHashCode() >> 2) ^ + (W.GetHashCode() >> 1); + } + + /// + /// Set X, Y, Z and W components of an existing Vector4Ubyte. + /// + /// + /// + /// + /// + public void Set(byte newX, byte newY, byte newZ, byte newW) + { + X = newX; + Y = newY; + Z = newZ; + W = newW; + } + + /// + /// Convert Vector4Ubyte to Vector4Uint. + /// + /// + public Vector4Uint ToVector4Uint() + { + return new Vector4Uint(this); + } + + /// + /// Convert Vector4Ubyte to Vector4Ushort. + /// + /// + public Vector4Ushort ToVector4Ushort() + { + return new Vector4Ushort(this); + } + + #endregion + + #region Operators + + public static bool operator ==(Vector4Ubyte v1, Vector4Ubyte v2) + { + return v1.Equals(v2); + } + + public static bool operator !=(Vector4Ubyte v1, Vector4Ubyte v2) + { + return v1.Equals(v2) == false; + } + + #endregion + } +} diff --git a/NewtonVgo/Runtime/Structs/Vector4Ubyte.cs.meta b/NewtonVgo/Runtime/Structures/Vector4Ubyte.cs.meta similarity index 100% rename from NewtonVgo/Runtime/Structs/Vector4Ubyte.cs.meta rename to NewtonVgo/Runtime/Structures/Vector4Ubyte.cs.meta diff --git a/NewtonVgo/Runtime/Structures/Vector4Uint.cs b/NewtonVgo/Runtime/Structures/Vector4Uint.cs new file mode 100644 index 0000000..4bf8cc3 --- /dev/null +++ b/NewtonVgo/Runtime/Structures/Vector4Uint.cs @@ -0,0 +1,270 @@ +// ---------------------------------------------------------------------- +// @Namespace : NewtonVgo +// @Struct : Vector4Uint +// ---------------------------------------------------------------------- +#nullable enable +namespace NewtonVgo +{ + using System; + using System.Runtime.InteropServices; + + /// + /// Vector4 (uint) + /// + [Serializable] + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct Vector4Uint + { + #region Fields + + /// + public uint X; + + /// + public uint Y; + + /// + public uint Z; + + /// + public uint W; + + #endregion + + #region Constructors + + /// + /// Create a new instance of Vector4Uint with x and y and z and w. + /// + /// + /// + /// + /// + public Vector4Uint(uint x, uint y, uint z, uint w) + { + X = x; + Y = y; + Z = z; + W = w; + } + + /// + /// Create a new instance of Vector4Uint with Vector4Ubyte. + /// + /// + public Vector4Uint(Vector4Ubyte source) + { + X = source.X; + Y = source.Y; + Z = source.Z; + W = source.W; + } + + /// + /// Create a new instance of Vector4Uint with Vector4Ushort. + /// + /// + public Vector4Uint(Vector4Ushort source) + { + X = source.X; + Y = source.Y; + Z = source.Z; + W = source.W; + } + + #endregion + + #region Indexers + + /// + /// + /// + /// + /// + /// + public uint this[int index] + { + get + { + switch (index) + { + case 0: + return X; + case 1: + return Y; + case 2: + return Z; + case 3: + return W; + default: + throw new IndexOutOfRangeException($"index: {index} is out of range."); + } + } + + set + { + switch (index) + { + case 0: + X = value; + break; + case 1: + Y = value; + break; + case 2: + Z = value; + break; + case 3: + W = value; + break; + default: + throw new IndexOutOfRangeException($"index: {index} is out of range."); + } + } + } + + #endregion + + #region Public Methods + + /// + /// Deconstruct Vector4Uint to x, y, z and w; + /// + /// + /// + /// + /// + public void Deconstruct(out uint x, out uint y, out uint z, out uint w) + { + x = X; + y = Y; + z = Z; + w = W; + } + + /// + /// Determines whether the specified object is equal to the current object. + /// + /// + /// true if the specified object is equal to the current object; otherwise, false. + public bool Equals(Vector4Uint other) + { + return + X.Equals(other.X) && + Y.Equals(other.Y) && + Z.Equals(other.Z) && + W.Equals(other.W); + } + + /// + /// Determines whether the specified object is equal to the current object. + /// + /// + /// true if the specified object is equal to the current object; otherwise, false. + public override bool Equals(object? other) + { + if (other is Vector4Uint otherVector4) + { + return Equals(otherVector4); + } + + return false; + } + + /// + /// Serves as the default hash function. + /// + /// A hash code for the current object. + public override int GetHashCode() + { + return + X.GetHashCode() ^ + (Y.GetHashCode() << 2) ^ + (Z.GetHashCode() >> 2) ^ + (W.GetHashCode() >> 1); + } + + /// + /// Set X, Y, Z and W components of an existing Vector4Uint. + /// + /// + /// + /// + /// + public void Set(uint newX, uint newY, uint newZ, uint newW) + { + X = newX; + Y = newY; + Z = newZ; + W = newW; + } + + /// + /// Try convert Vector4Uint to Vector4Ubyte. + /// + /// true if the operation is successful; otherwise, false. + public bool TryConvertTo(out Vector4Ubyte destination) + { + if ((X <= byte.MaxValue) && + (Y <= byte.MaxValue) && + (Z <= byte.MaxValue) && + (W <= byte.MaxValue)) + { + destination = new Vector4Ubyte( + (byte)X, + (byte)Y, + (byte)Z, + (byte)W + ); + + return true; + } + + destination = default; + + return false; + } + + /// + /// Try convert Vector4Uint to Vector4Ushort. + /// + /// true if the operation is successful; otherwise, false. + public bool TryConvertTo(out Vector4Ushort destination) + { + if ((X <= ushort.MaxValue) && + (Y <= ushort.MaxValue) && + (Z <= ushort.MaxValue) && + (W <= ushort.MaxValue)) + { + destination = new Vector4Ushort( + (ushort)X, + (ushort)Y, + (ushort)Z, + (ushort)W + ); + + return true; + } + + destination = default; + + return false; + } + + #endregion + + #region Operators + + public static bool operator ==(Vector4Uint v1, Vector4Uint v2) + { + return v1.Equals(v2); + } + + public static bool operator !=(Vector4Uint v1, Vector4Uint v2) + { + return v1.Equals(v2) == false; + } + + #endregion + } +} diff --git a/NewtonVgo/Runtime/Structs/Vector4Uint.cs.meta b/NewtonVgo/Runtime/Structures/Vector4Uint.cs.meta similarity index 100% rename from NewtonVgo/Runtime/Structs/Vector4Uint.cs.meta rename to NewtonVgo/Runtime/Structures/Vector4Uint.cs.meta diff --git a/NewtonVgo/Runtime/Structures/Vector4Ushort.cs b/NewtonVgo/Runtime/Structures/Vector4Ushort.cs new file mode 100644 index 0000000..05bf747 --- /dev/null +++ b/NewtonVgo/Runtime/Structures/Vector4Ushort.cs @@ -0,0 +1,241 @@ +// ---------------------------------------------------------------------- +// @Namespace : NewtonVgo +// @Struct : Vector4Ushort +// ---------------------------------------------------------------------- +#nullable enable +namespace NewtonVgo +{ + using System; + using System.Runtime.InteropServices; + + /// + /// Vector4 (ushort) + /// + [Serializable] + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct Vector4Ushort + { + #region Fields + + /// + public ushort X; + + /// + public ushort Y; + + /// + public ushort Z; + + /// + public ushort W; + + #endregion + + #region Constructors + + /// + /// Create a new instance of Vector4Ushort with x and y and z and w. + /// + /// + /// + /// + /// + public Vector4Ushort(ushort x, ushort y, ushort z, ushort w) + { + X = x; + Y = y; + Z = z; + W = w; + } + + /// + /// Create a new instance of Vector4Ushort with Vector4Ubyte. + /// + /// + public Vector4Ushort(Vector4Ubyte source) + { + X = source.X; + Y = source.Y; + Z = source.Z; + W = source.W; + } + + #endregion + + #region Indexers + + /// + /// + /// + /// + /// + /// + public ushort this[int index] + { + get + { + switch (index) + { + case 0: + return X; + case 1: + return Y; + case 2: + return Z; + case 3: + return W; + default: + throw new IndexOutOfRangeException($"index: {index} is out of range."); + } + } + + set + { + switch (index) + { + case 0: + X = value; + break; + case 1: + Y = value; + break; + case 2: + Z = value; + break; + case 3: + W = value; + break; + default: + throw new IndexOutOfRangeException($"index: {index} is out of range."); + } + } + } + + #endregion + + #region Public Methods + + /// + /// Deconstruct Vector4Ushort to x, y, z and w; + /// + /// + /// + /// + /// + public void Deconstruct(out ushort x, out ushort y, out ushort z, out ushort w) + { + x = X; + y = Y; + z = Z; + w = W; + } + + /// + /// Determines whether the specified object is equal to the current object. + /// + /// + /// true if the specified object is equal to the current object; otherwise, false. + public bool Equals(Vector4Ushort other) + { + return + X.Equals(other.X) && + Y.Equals(other.Y) && + Z.Equals(other.Z) && + W.Equals(other.W); + } + + /// + /// Determines whether the specified object is equal to the current object. + /// + /// + /// true if the specified object is equal to the current object; otherwise, false. + public override bool Equals(object? other) + { + if (other is Vector4Ushort otherVector4) + { + return Equals(otherVector4); + } + + return false; + } + + /// + /// Serves as the default hash function. + /// + /// A hash code for the current object. + public override int GetHashCode() + { + return + X.GetHashCode() ^ + (Y.GetHashCode() << 2) ^ + (Z.GetHashCode() >> 2) ^ + (W.GetHashCode() >> 1); + } + + /// + /// Set X, Y, Z and W components of an existing Vector4Ushort. + /// + /// + /// + /// + /// + public void Set(ushort newX, ushort newY, ushort newZ, ushort newW) + { + X = newX; + Y = newY; + Z = newZ; + W = newW; + } + + /// + /// Convert Vector4Ushort to Vector4Uint. + /// + /// + public Vector4Uint ToVector4Uint() + { + return new Vector4Uint(this); + } + + /// + /// Try convert Vector4Ushort to Vector4Ubyte. + /// + /// true if the operation is successful; otherwise, false. + public bool TryConvertTo(out Vector4Ubyte destination) + { + if ((X <= byte.MaxValue) && + (Y <= byte.MaxValue) && + (Z <= byte.MaxValue) && + (W <= byte.MaxValue)) + { + destination = new Vector4Ubyte( + (byte)X, + (byte)Y, + (byte)Z, + (byte)W + ); + + return true; + } + + destination = default; + + return false; + } + + #endregion + + #region Operators + + public static bool operator ==(Vector4Ushort v1, Vector4Ushort v2) + { + return v1.Equals(v2); + } + + public static bool operator !=(Vector4Ushort v1, Vector4Ushort v2) + { + return v1.Equals(v2) == false; + } + + #endregion + } +} diff --git a/NewtonVgo/Runtime/Structs/Vector4Ushort.cs.meta b/NewtonVgo/Runtime/Structures/Vector4Ushort.cs.meta similarity index 100% rename from NewtonVgo/Runtime/Structs/Vector4Ushort.cs.meta rename to NewtonVgo/Runtime/Structures/Vector4Ushort.cs.meta diff --git a/UniVgo2/Editor/ScriptedImporters/VgoScriptedImporter.cs b/UniVgo2/Editor/ScriptedImporters/VgoScriptedImporter.cs index 7585812..f1dacae 100644 --- a/UniVgo2/Editor/ScriptedImporters/VgoScriptedImporter.cs +++ b/UniVgo2/Editor/ScriptedImporters/VgoScriptedImporter.cs @@ -47,6 +47,9 @@ public class VgoScriptedImporter : ScriptedImporter /// The texture directory name. protected readonly string TextureDirectoryName = "Textures"; + /// The vgo importer. + protected readonly VgoImporter _VgoImporter = new VgoImporter(); + /// /// This method must by overriden by the derived class and is called by the Asset pipeline to import files. /// @@ -58,7 +61,7 @@ public override void OnImportAsset(AssetImportContext ctx) { try { - ModelAsset modelAsset = LoadModel(ctx.assetPath); + VgoModelAsset modelAsset = LoadModel(ctx.assetPath); { // Animation if (modelAsset.AnimationClipList != null) @@ -202,32 +205,28 @@ public override void OnImportAsset(AssetImportContext ctx) /// Load 3D model. /// /// The file path of the vgo. - /// A model asset. - protected virtual ModelAsset LoadModel(string vgoFilePath) + /// A vgo model asset. + protected virtual VgoModelAsset LoadModel(string vgoFilePath) { string? vgkFilePath = FindVgkFilePath(vgoFilePath); - var importer = new VgoImporter(); - - ModelAsset modelAsset = importer.Load(vgoFilePath, vgkFilePath); + VgoModelAsset vgoModelAsset = _VgoImporter.Load(vgoFilePath, vgkFilePath); - return modelAsset; + return vgoModelAsset; } /// /// Extract 3D model. /// /// The file path of the vgo. - /// A model asset. - protected virtual ModelAsset ExtractModel(string vgoFilePath) + /// A vgo model asset. + protected virtual VgoModelAsset ExtractModel(string vgoFilePath) { string? vgkFilePath = FindVgkFilePath(vgoFilePath); - var importer = new VgoImporter(); - - ModelAsset modelAsset = importer.Extract(vgoFilePath, vgkFilePath); + VgoModelAsset vgoModelAsset = _VgoImporter.Extract(vgoFilePath, vgkFilePath); - return modelAsset; + return vgoModelAsset; } /// @@ -364,7 +363,7 @@ public virtual void ExtractTextures() /// /// /// - public virtual void ExtractTextures(string dirName, Func ExtractModel, bool continueMaterial = false) + public virtual void ExtractTextures(string dirName, Func ExtractModel, bool continueMaterial = false) { if (string.IsNullOrEmpty(assetPath)) { @@ -384,7 +383,7 @@ public virtual void ExtractTextures(string dirName, Func Ext Dictionary targetPaths = new Dictionary(); // Reload Model - using (ModelAsset modelAsset = ExtractModel(assetPath)) + using (VgoModelAsset modelAsset = ExtractModel(assetPath)) { if (modelAsset.Layout != null && modelAsset.Layout.textures != null && diff --git a/UniVgo2/Runtime/Behaviours.meta b/UniVgo2/Runtime/Components.meta similarity index 100% rename from UniVgo2/Runtime/Behaviours.meta rename to UniVgo2/Runtime/Components.meta diff --git a/UniVgo2/Runtime/Behaviours/VgoBlendShape.cs b/UniVgo2/Runtime/Components/VgoBlendShape.cs similarity index 100% rename from UniVgo2/Runtime/Behaviours/VgoBlendShape.cs rename to UniVgo2/Runtime/Components/VgoBlendShape.cs diff --git a/UniVgo2/Runtime/Behaviours/VgoBlendShape.cs.meta b/UniVgo2/Runtime/Components/VgoBlendShape.cs.meta similarity index 100% rename from UniVgo2/Runtime/Behaviours/VgoBlendShape.cs.meta rename to UniVgo2/Runtime/Components/VgoBlendShape.cs.meta diff --git a/UniVgo2/Runtime/Behaviours/VgoGenerator.cs b/UniVgo2/Runtime/Components/VgoGenerator.cs similarity index 100% rename from UniVgo2/Runtime/Behaviours/VgoGenerator.cs rename to UniVgo2/Runtime/Components/VgoGenerator.cs diff --git a/UniVgo2/Runtime/Behaviours/VgoGenerator.cs.meta b/UniVgo2/Runtime/Components/VgoGenerator.cs.meta similarity index 100% rename from UniVgo2/Runtime/Behaviours/VgoGenerator.cs.meta rename to UniVgo2/Runtime/Components/VgoGenerator.cs.meta diff --git a/UniVgo2/Runtime/Behaviours/VgoRight.cs b/UniVgo2/Runtime/Components/VgoRight.cs similarity index 100% rename from UniVgo2/Runtime/Behaviours/VgoRight.cs rename to UniVgo2/Runtime/Components/VgoRight.cs diff --git a/UniVgo2/Runtime/Behaviours/VgoRight.cs.meta b/UniVgo2/Runtime/Components/VgoRight.cs.meta similarity index 100% rename from UniVgo2/Runtime/Behaviours/VgoRight.cs.meta rename to UniVgo2/Runtime/Components/VgoRight.cs.meta diff --git a/UniVgo2/Runtime/Models/ModelAsset.cs b/UniVgo2/Runtime/Models/VgoModelAsset.cs similarity index 95% rename from UniVgo2/Runtime/Models/ModelAsset.cs rename to UniVgo2/Runtime/Models/VgoModelAsset.cs index e9229e6..a5042ad 100644 --- a/UniVgo2/Runtime/Models/ModelAsset.cs +++ b/UniVgo2/Runtime/Models/VgoModelAsset.cs @@ -1,6 +1,6 @@ // ---------------------------------------------------------------------- // @Namespace : UniVgo2 -// @Class : ModelAsset +// @Class : VgoModelAsset // ---------------------------------------------------------------------- #nullable enable namespace UniVgo2 @@ -14,10 +14,10 @@ namespace UniVgo2 #endif /// - /// Model Asset + /// VGO Model Asset /// [Serializable] - public class ModelAsset : IDisposable + public class VgoModelAsset : IDisposable { #region Fields diff --git a/UniVgo2/Runtime/Models/ModelAsset.cs.meta b/UniVgo2/Runtime/Models/VgoModelAsset.cs.meta similarity index 100% rename from UniVgo2/Runtime/Models/ModelAsset.cs.meta rename to UniVgo2/Runtime/Models/VgoModelAsset.cs.meta diff --git a/UniVgo2/Runtime/Porters/Interfaces/IVgoMeshImporter.cs b/UniVgo2/Runtime/Porters/Interfaces/IVgoMeshImporter.cs index d8e7064..28eded5 100644 --- a/UniVgo2/Runtime/Porters/Interfaces/IVgoMeshImporter.cs +++ b/UniVgo2/Runtime/Porters/Interfaces/IVgoMeshImporter.cs @@ -5,8 +5,17 @@ #nullable enable namespace UniVgo2.Porters { +#if UNITY_2023_1_OR_NEWER && UNIVGO_USE_UNITY_AWAITABLE + // +#elif CYSHARP_UNITASK_2_OR_NEWER && UNIVGO_USE_UNITASK + using Cysharp.Threading.Tasks; +#else + using System.Threading.Tasks; +#endif + using NewtonVgo; using System.Collections.Generic; + using System.Threading; using UnityEngine; /// @@ -17,13 +26,68 @@ public interface IVgoMeshImporter #region Public Methods /// - /// Create a mesh asset. + /// Create mesh assets. + /// + /// A vgo storage. + /// List of unity material. + /// List of mesh asset. + List CreateMeshAssets(IVgoStorage vgoStorage, IList? materialList = null); + + /// + /// Create mesh assets. + /// + /// A vgo storage. + /// List of unity material. + /// + /// List of mesh asset. +#if UNITY_2023_1_OR_NEWER && UNIVGO_USE_UNITY_AWAITABLE + Awaitable> CreateMeshAssetsAsync(IVgoStorage vgoStorage, IList? materialList, CancellationToken cancellationToken); +#elif CYSHARP_UNITASK_2_OR_NEWER && UNIVGO_USE_UNITASK + UniTask> CreateMeshAssetsAsync(IVgoStorage vgoStorage, IList? materialList, CancellationToken cancellationToken); +#else + Task> CreateMeshAssetsAsync(IVgoStorage vgoStorage, IList? materialList, CancellationToken cancellationToken); +#endif + + /// + /// Create mesh assets. /// /// A vgo storage. - /// The index of vgo mesh. - /// List of unity material. - /// A mesh asset. - MeshAsset CreateMeshAsset(IVgoStorage vgoStorage, int meshIndex, IList? unityMaterialList = null); + /// List of unity material. + /// + /// List of mesh asset. +#if UNITY_2023_1_OR_NEWER && UNIVGO_USE_UNITY_AWAITABLE + Awaitable> CreateMeshAssetsParallelAsync(IVgoStorage vgoStorage, IList? materialList, CancellationToken cancellationToken); +#elif CYSHARP_UNITASK_2_OR_NEWER && UNIVGO_USE_UNITASK + UniTask> CreateMeshAssetsParallelAsync(IVgoStorage vgoStorage, IList? materialList, CancellationToken cancellationToken); +#else + Task> CreateMeshAssetsParallelAsync(IVgoStorage vgoStorage, IList? materialList, CancellationToken cancellationToken); +#endif + + ///// + ///// Create a mesh asset. + ///// + ///// A vgo storage. + ///// The index of vgo mesh. + ///// List of unity material. + ///// A mesh asset. + //MeshAsset CreateMeshAsset(IVgoStorage vgoStorage, int meshIndex, IList? unityMaterialList = null); + + ///// + ///// Create a mesh asset. + ///// + ///// A vgo storage. + ///// The index of vgo mesh. + ///// List of unity material. + ///// + ///// A mesh asset. +#if UNITY_2023_1_OR_NEWER && UNIVGO_USE_UNITY_AWAITABLE + //Awaitable CreateMeshAssetAsync(IVgoStorage vgoStorage, int meshIndex, IList? unityMaterialList, CancellationToken cancellationToken); +#elif CYSHARP_UNITASK_2_OR_NEWER && UNIVGO_USE_UNITASK + //UniTask CreateMeshAssetAsync(IVgoStorage vgoStorage, int meshIndex, IList? unityMaterialList, CancellationToken cancellationToken); +#else + //Task CreateMeshAssetAsync(IVgoStorage vgoStorage, int meshIndex, IList? unityMaterialList, CancellationToken cancellationToken); +#endif + #endregion } diff --git a/UniVgo2/Runtime/Porters/Interfaces/IVgoTextureImporter.cs b/UniVgo2/Runtime/Porters/Interfaces/IVgoTextureImporter.cs new file mode 100644 index 0000000..a8bf1c0 --- /dev/null +++ b/UniVgo2/Runtime/Porters/Interfaces/IVgoTextureImporter.cs @@ -0,0 +1,51 @@ +// ---------------------------------------------------------------------- +// @Namespace : UniVgo2.Porters +// @Class : IVgoMeshImporter +// ---------------------------------------------------------------------- +#nullable enable +namespace UniVgo2.Porters +{ +#if UNITY_2023_1_OR_NEWER && UNIVGO_USE_UNITY_AWAITABLE + // +#elif CYSHARP_UNITASK_2_OR_NEWER && UNIVGO_USE_UNITASK + using Cysharp.Threading.Tasks; +#else + using System.Threading.Tasks; +#endif + + using NewtonVgo; + using System.Collections.Generic; + using System.Threading; + using UnityEngine; + + /// + /// VGO Texture Importer Interface + /// + public interface IVgoTextureImporter + { + #region Public Methods + + /// + /// Create texture assets. + /// + /// A vgo storage. + /// List of unity texture2D. + List CreateTextureAssets(IVgoStorage vgoStorage); + + /// + /// Create texture assets. + /// + /// A vgo storage. + /// + /// List of unity texture2D. +#if UNITY_2023_1_OR_NEWER && UNIVGO_USE_UNITY_AWAITABLE + Awaitable> CreateTextureAssetsAsync(IVgoStorage vgoStorage, CancellationToken cancellationToken); +#elif CYSHARP_UNITASK_2_OR_NEWER && UNIVGO_USE_UNITASK + UniTask> CreateTextureAssetsAsync(IVgoStorage vgoStorage, CancellationToken cancellationToken); +#else + Task> CreateTextureAssetsAsync(IVgoStorage vgoStorage, CancellationToken cancellationToken); +#endif + + #endregion + } +} diff --git a/UniVgo2/Runtime/Porters/Interfaces/IVgoTextureImporter.cs.meta b/UniVgo2/Runtime/Porters/Interfaces/IVgoTextureImporter.cs.meta new file mode 100644 index 0000000..0a2069c --- /dev/null +++ b/UniVgo2/Runtime/Porters/Interfaces/IVgoTextureImporter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a2682330d6c85f444a571d924d62a171 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UniVgo2/Runtime/Porters/Meshes/VgoMeshImporter.cs b/UniVgo2/Runtime/Porters/Meshes/VgoMeshImporter.cs index cdc5e91..34a0530 100644 --- a/UniVgo2/Runtime/Porters/Meshes/VgoMeshImporter.cs +++ b/UniVgo2/Runtime/Porters/Meshes/VgoMeshImporter.cs @@ -5,10 +5,21 @@ #nullable enable namespace UniVgo2.Porters { +#if UNITY_2023_1_OR_NEWER && UNIVGO_USE_UNITY_AWAITABLE + // +#elif CYSHARP_UNITASK_2_OR_NEWER && UNIVGO_USE_UNITASK + using Cysharp.Threading.Tasks; +#else + //System.Threading.Tasks; +#endif + using NewtonVgo; using System; + using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; + using System.Threading; + using System.Threading.Tasks; using UnityEngine; /// @@ -43,6 +54,143 @@ public VgoMeshImporter(MeshImporterOption option) #region Public Methods + /// + /// Create mesh assets. + /// + /// A vgo storage. + /// List of unity material. + /// List of mesh asset. + public virtual List CreateMeshAssets(IVgoStorage vgoStorage, IList? materialList = null) + { + if ((vgoStorage.Layout.meshes == null) || (vgoStorage.Layout.meshes.Any() == false)) + { + return new List(0); + } + + var meshAssetList = new List(vgoStorage.Layout.meshes.Count); + + for (int meshIndex = 0; meshIndex < vgoStorage.Layout.meshes.Count; meshIndex++) + { + MeshAsset meshAsset = CreateMeshAsset(vgoStorage, meshIndex, materialList); + + if (meshAssetList.Where(x => x?.Mesh.name == meshAsset.Mesh.name).Any()) + { + meshAsset.Mesh.name = string.Format($"mesh_{meshIndex}"); + } + + meshAssetList.Add(meshAsset); + } + + return meshAssetList; + } + + /// + /// Create mesh assets. + /// + /// A vgo storage. + /// List of unity material. + /// + /// List of mesh asset. +#if UNITY_2023_1_OR_NEWER && UNIVGO_USE_UNITY_AWAITABLE + public virtual async Awaitable> CreateMeshAssetsAsync(IVgoStorage vgoStorage, IList? materialList, CancellationToken cancellationToken) +#elif CYSHARP_UNITASK_2_OR_NEWER && UNIVGO_USE_UNITASK + public virtual async UniTask> CreateMeshAssetsAsync(IVgoStorage vgoStorage, IList? materialList, CancellationToken cancellationToken) +#else + public virtual async Task> CreateMeshAssetsAsync(IVgoStorage vgoStorage, IList? materialList, CancellationToken cancellationToken) +#endif + { + if ((vgoStorage.Layout.meshes == null) || (vgoStorage.Layout.meshes.Any() == false)) + { + return new List(0); + } + + var meshAssetList = new List(vgoStorage.Layout.meshes.Count); + + for (int meshIndex = 0; meshIndex < vgoStorage.Layout.meshes.Count; meshIndex++) + { + MeshAsset meshAsset = await CreateMeshAssetAsync(vgoStorage, meshIndex, materialList, cancellationToken); + + if (meshAssetList.Where(x => x?.Mesh.name == meshAsset.Mesh.name).Any()) + { + meshAsset.Mesh.name = string.Format($"mesh_{meshIndex}"); + } + + meshAssetList.Add(meshAsset); + } + + return meshAssetList; + } + + /// + /// Create mesh assets. + /// + /// A vgo storage. + /// List of unity material. + /// + /// List of mesh asset. +#if UNITY_2023_1_OR_NEWER && UNIVGO_USE_UNITY_AWAITABLE + public virtual async Awaitable> CreateMeshAssetsParallelAsync(IVgoStorage vgoStorage, IList? materialList, CancellationToken cancellationToken) +#elif CYSHARP_UNITASK_2_OR_NEWER && UNIVGO_USE_UNITASK + public virtual async UniTask> CreateMeshAssetsParallelAsync(IVgoStorage vgoStorage, IList? materialList, CancellationToken cancellationToken) +#else + public virtual async Task> CreateMeshAssetsParallelAsync(IVgoStorage vgoStorage, IList? materialList, CancellationToken cancellationToken) +#endif + { + if ((vgoStorage.Layout.meshes == null) || (vgoStorage.Layout.meshes.Any() == false)) + { + return new List(0); + } + + var meshAssetList = new List(vgoStorage.Layout.meshes.Count); + +#if UNITY_2023_1_OR_NEWER && UNIVGO_USE_UNITY_AWAITABLE + await Awaitable.BackgroundThreadAsync(); +#elif CYSHARP_UNITASK_2_OR_NEWER && UNIVGO_USE_UNITASK + await UniTask.SwitchToThreadPool(); +#endif + //var sw = new System.Diagnostics.Stopwatch(); + + //sw.Start(); + + List meshContextList = await CreateMeshContextListParallelAsync(vgoStorage, cancellationToken); + + //sw.Stop(); + + //Debug.LogFormat("{0,2}: {1,5}ms", Thread.CurrentThread.ManagedThreadId, sw.ElapsedMilliseconds); + +#if UNITY_2023_1_OR_NEWER && UNIVGO_USE_UNITY_AWAITABLE + await Awaitable.MainThreadAsync(); +#elif CYSHARP_UNITASK_2_OR_NEWER && UNIVGO_USE_UNITASK + await UniTask.SwitchToMainThread(); +#endif + + for (int meshIndex = 0; meshIndex < vgoStorage.Layout.meshes.Count; meshIndex++) + { + MeshAsset meshAsset = CreateMeshAsset(vgoStorage, meshIndex, meshContextList, materialList); + + if (meshAssetList.Where(x => x?.Mesh.name == meshAsset.Mesh.name).Any()) + { + meshAsset.Mesh.name = string.Format($"mesh_{meshIndex}"); + } + + meshAssetList.Add(meshAsset); + } + +#if UNITY_2023_1_OR_NEWER && UNIVGO_USE_UNITY_AWAITABLE + //await Awaitable.NextFrameAsync(cancellationToken); + + return meshAssetList; +#elif CYSHARP_UNITASK_2_OR_NEWER && UNIVGO_USE_UNITASK + return await UniTask.FromResult(meshAssetList); +#else + return await Task.FromResult(meshAssetList); +#endif + } + + #endregion + + #region Protected Methods + /// /// Create a mesh asset. /// @@ -50,7 +198,7 @@ public VgoMeshImporter(MeshImporterOption option) /// The index of vgo mesh. /// List of unity material. /// A mesh asset. - public virtual MeshAsset CreateMeshAsset(IVgoStorage vgoStorage, int meshIndex, IList? unityMaterialList = null) + protected virtual MeshAsset CreateMeshAsset(IVgoStorage vgoStorage, int meshIndex, IList? unityMaterialList = null) { if (vgoStorage == null) { @@ -82,6 +230,196 @@ public virtual MeshAsset CreateMeshAsset(IVgoStorage vgoStorage, int meshIndex, return meshAsset; } + /// + /// Create a mesh asset. + /// + /// A vgo storage. + /// The index of vgo mesh. + /// List of mesh context. + /// List of unity material. + /// A mesh asset. + protected virtual MeshAsset CreateMeshAsset(IVgoStorage vgoStorage, int meshIndex, IList meshContextList, IList? unityMaterialList = null) + { + if (vgoStorage == null) + { + throw new ArgumentNullException(nameof(vgoStorage)); + } + + if (meshIndex.IsInRangeOf(meshContextList) == false) + { + throw new ArgumentOutOfRangeException(nameof(meshIndex)); + } + + MeshContext meshContext = meshContextList[meshIndex]; + + Mesh mesh = BuildMesh(meshContext); + + MeshAsset meshAsset = new MeshAsset(mesh); + + if (vgoStorage.IsSpecVersion_2_4_orLower) + { + if (unityMaterialList == null) + { + throw new ArgumentNullException(nameof(unityMaterialList)); + } + + meshAsset.Materials = meshContext.MaterialIndices.Select(x => unityMaterialList[x]).ToArray(); + } + + if ((meshContext.BlendShapesContext != null) && + (meshContext.BlendShapesContext.BlendShapeConfig != null)) + { + meshAsset.BlendShapeConfig = meshContext.BlendShapesContext.BlendShapeConfig; + } + + return meshAsset; + } + + /// + /// Create a mesh asset. + /// + /// A vgo storage. + /// The index of vgo mesh. + /// List of unity material. + /// + /// A mesh asset. +#if UNITY_2023_1_OR_NEWER && UNIVGO_USE_UNITY_AWAITABLE + protected virtual async Awaitable CreateMeshAssetAsync(IVgoStorage vgoStorage, int meshIndex, IList? unityMaterialList, CancellationToken cancellationToken) +#elif CYSHARP_UNITASK_2_OR_NEWER && UNIVGO_USE_UNITASK + protected virtual async UniTask CreateMeshAssetAsync(IVgoStorage vgoStorage, int meshIndex, IList? unityMaterialList, CancellationToken cancellationToken) +#else + protected virtual async Task CreateMeshAssetAsync(IVgoStorage vgoStorage, int meshIndex, IList? unityMaterialList, CancellationToken cancellationToken) +#endif + { + cancellationToken.ThrowIfCancellationRequested(); + + if (vgoStorage == null) + { + throw new ArgumentNullException(nameof(vgoStorage)); + } + +#if UNITY_2023_1_OR_NEWER && UNIVGO_USE_UNITY_AWAITABLE + await Awaitable.BackgroundThreadAsync(); + + //var sw = new System.Diagnostics.Stopwatch(); + + //sw.Start(); + + MeshContext meshContext = ReadMesh(vgoStorage, meshIndex); + + //sw.Stop(); + + //Debug.LogFormat("{0,2}: {1,5}ms", Thread.CurrentThread.ManagedThreadId, sw.ElapsedMilliseconds); + + await Awaitable.MainThreadAsync(); +#elif CYSHARP_UNITASK_2_OR_NEWER && UNIVGO_USE_UNITASK + await UniTask.SwitchToThreadPool(); + + //var sw = new System.Diagnostics.Stopwatch(); + + //sw.Start(); + + MeshContext meshContext = ReadMesh(vgoStorage, meshIndex); + + //sw.Stop(); + + //Debug.LogFormat("{0,2}: {1,5}ms", Thread.CurrentThread.ManagedThreadId, sw.ElapsedMilliseconds); + + await UniTask.SwitchToMainThread(); +#else + //var sw = new System.Diagnostics.Stopwatch(); + + //sw.Start(); + + MeshContext meshContext = ReadMesh(vgoStorage, meshIndex); + + //MeshContext meshContext = await Task.Run(() => + //{ + // //var swt = new System.Diagnostics.Stopwatch(); + + // //swt.Start(); + + // MeshContext context = ReadMesh(vgoStorage, meshIndex); + + // //swt.Stop(); + + // //Debug.LogFormat("{0,2}: {1,5}ms", Thread.CurrentThread.ManagedThreadId, swt.ElapsedMilliseconds); + + // return context; + //}); + + //sw.Stop(); + + //Debug.LogFormat("{0,2}: {1,5}ms", Thread.CurrentThread.ManagedThreadId, sw.ElapsedMilliseconds); +#endif + + Mesh mesh = BuildMesh(meshContext); + + MeshAsset meshAsset = new MeshAsset(mesh); + + if (vgoStorage.IsSpecVersion_2_4_orLower) + { + if (unityMaterialList == null) + { + throw new ArgumentNullException(nameof(unityMaterialList)); + } + + meshAsset.Materials = meshContext.MaterialIndices.Select(x => unityMaterialList[x]).ToArray(); + } + + if ((meshContext.BlendShapesContext != null) && + (meshContext.BlendShapesContext.BlendShapeConfig != null)) + { + meshAsset.BlendShapeConfig = meshContext.BlendShapesContext.BlendShapeConfig; + } + +#if UNITY_2023_1_OR_NEWER && UNIVGO_USE_UNITY_AWAITABLE + //await Awaitable.NextFrameAsync(cancellationToken); + + return meshAsset; +#elif CYSHARP_UNITASK_2_OR_NEWER && UNIVGO_USE_UNITASK + return await UniTask.FromResult(meshAsset); +#else + return await Task.FromResult(meshAsset); +#endif + } + + /// + /// + /// + /// A vgo storage. + /// + /// List of mesh context. + protected virtual async Task> CreateMeshContextListParallelAsync(IVgoStorage vgoStorage, CancellationToken cancellationToken) + { + if ((vgoStorage.Layout.meshes == null) || (vgoStorage.Layout.meshes.Any() == false)) + { + return new List(0); + } + + var meshContextDictionary = new ConcurrentDictionary(); + + var createMeshContextTasks = new List(vgoStorage.Layout.meshes.Count); + + for (int meshIndex = 0; meshIndex < vgoStorage.Layout.meshes.Count; meshIndex++) + { + int index = meshIndex; // @impotant + + Task createMeshContextTask = Task.Run(() => + { + MeshContext meshContext = ReadMesh(vgoStorage, index); + + meshContextDictionary.TryAdd(index, meshContext); + }); + + createMeshContextTasks.Add(createMeshContextTask); + } + + await Task.WhenAll(createMeshContextTasks); + + return meshContextDictionary.Values.ToList(); + } + #endregion #region ReadMesh diff --git a/UniVgo2/Runtime/Porters/Textures.meta b/UniVgo2/Runtime/Porters/Textures.meta new file mode 100644 index 0000000..fff8d0f --- /dev/null +++ b/UniVgo2/Runtime/Porters/Textures.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: db297f38dbb000544a25113c75cf2008 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UniVgo2/Runtime/Porters/Textures/VgoTextureImporter.cs b/UniVgo2/Runtime/Porters/Textures/VgoTextureImporter.cs new file mode 100644 index 0000000..fbd6e9f --- /dev/null +++ b/UniVgo2/Runtime/Porters/Textures/VgoTextureImporter.cs @@ -0,0 +1,329 @@ +// ---------------------------------------------------------------------- +// @Namespace : UniVgo2.Porters +// @Class : VgoTextureImporter +// ---------------------------------------------------------------------- +#nullable enable +namespace UniVgo2.Porters +{ +#if UNITY_2023_1_OR_NEWER && UNIVGO_USE_UNITY_AWAITABLE + // +#elif CYSHARP_UNITASK_2_OR_NEWER && UNIVGO_USE_UNITASK + using Cysharp.Threading.Tasks; +#else + using System.Threading.Tasks; +#endif + + using NewtonVgo; + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading; + using UnityEngine; + using UniVgo2.Converters; + + /// + /// VGO Texture Importer + /// + public partial class VgoTextureImporter : IVgoTextureImporter + { + #region Fields + + /// The texture converter. + protected readonly ITextureConverter _TextureConverter; + + #endregion + + #region Constructors + + /// + /// Create a new instance of TextureImporter. + /// + public VgoTextureImporter() + { + _TextureConverter = new TextureConverter(); + } + + #endregion + + #region Public Methods + + /// + /// Create texture assets. + /// + /// A vgo storage. + /// List of unity texture2D. + public virtual List CreateTextureAssets(IVgoStorage vgoStorage) + { + if ((vgoStorage.Layout.textures == null) || (vgoStorage.Layout.textures.Any() == false)) + { + return new List(0); + } + + var texture2dList = new List(vgoStorage.Layout.textures.Count); + + for (int textureIndex = 0; textureIndex < vgoStorage.Layout.textures.Count; textureIndex++) + { + VgoTexture? vgoTexture = vgoStorage.Layout.textures[textureIndex]; + + if (vgoTexture is null) + { + texture2dList.Add(null); + } + else + { + try + { + Texture2D? texture2d = CreateTexture2D(vgoTexture, vgoStorage); + + texture2dList.Add(texture2d); + } + catch (Exception ex) + { + Debug.LogException(ex); + + texture2dList.Add(null); + } + } + } + + return texture2dList; + } + + /// + /// Create texture assets. + /// + /// A vgo storage. + /// + /// List of unity texture2D. +#if UNITY_2023_1_OR_NEWER && UNIVGO_USE_UNITY_AWAITABLE + public virtual async Awaitable> CreateTextureAssetsAsync(IVgoStorage vgoStorage, CancellationToken cancellationToken) +#elif CYSHARP_UNITASK_2_OR_NEWER && UNIVGO_USE_UNITASK + public virtual async UniTask> CreateTextureAssetsAsync(IVgoStorage vgoStorage, CancellationToken cancellationToken) +#else + public virtual async Task> CreateTextureAssetsAsync(IVgoStorage vgoStorage, CancellationToken cancellationToken) +#endif + { + if ((vgoStorage.Layout.textures == null) || (vgoStorage.Layout.textures.Any() == false)) + { + return new List(0); + } + + var texture2dList = new List(vgoStorage.Layout.textures.Count); + + for (int textureIndex = 0; textureIndex < vgoStorage.Layout.textures.Count; textureIndex++) + { + cancellationToken.ThrowIfCancellationRequested(); + + VgoTexture? vgoTexture = vgoStorage.Layout.textures[textureIndex]; + + if (vgoTexture is null) + { + texture2dList.Add(null); + } + else + { + try + { + Texture2D? texture2d = await CreateTexture2DAsync(vgoTexture, vgoStorage, cancellationToken); + + texture2dList.Add(texture2d); + } + catch (OperationCanceledException) + { + throw; + } + catch (Exception ex) + { + Debug.LogException(ex); + + texture2dList.Add(null); + } + } + } + + return texture2dList; + } + + #endregion + + #region Protected Methods + + /// + /// Create a unity texture 2D. + /// + /// A vgo texture. + /// A vgo storage. + /// A unity texture 2D. + protected virtual Texture2D? CreateTexture2D(VgoTexture vgoTexture, IVgoStorage vgoStorage) + { + if (vgoStorage.ResourceAccessors is null) + { + throw new Exception(); + } + + if (vgoTexture.source.IsInRangeOf(vgoStorage.ResourceAccessors) == false) + { + Debug.LogError($"{nameof(VgoTexture)}.{nameof(vgoTexture.source)}: {vgoTexture.source}"); + + return null; + } + + if (vgoTexture.dimensionType != TextureDimension.Tex2D) + { + Debug.LogError($"{nameof(VgoTexture)}.{nameof(vgoTexture.dimensionType)}: {vgoTexture.dimensionType}"); + + return null; + } + + Texture2D srcTexture2d; + + { + byte[] imageBytes = vgoStorage.GetAccessorBytes(vgoTexture.source).ToArray(); + + srcTexture2d = new Texture2D(width: 2, height: 2, TextureFormat.ARGB32, mipChain: false, linear: vgoTexture.IsLinear) + { + name = vgoTexture.name + }; + + ImageConversion.LoadImage(srcTexture2d, imageBytes); + } + + Texture2D texture2D = _TextureConverter.GetImportTexture(srcTexture2d, vgoTexture.mapType, vgoTexture.metallicRoughness); + + texture2D.filterMode = (UnityEngine.FilterMode)vgoTexture.filterMode; + texture2D.wrapMode = (UnityEngine.TextureWrapMode)vgoTexture.wrapMode; + texture2D.wrapModeU = (UnityEngine.TextureWrapMode)vgoTexture.wrapModeU; + texture2D.wrapModeV = (UnityEngine.TextureWrapMode)vgoTexture.wrapModeV; + + texture2D.Apply(); + + return texture2D; + } + + /// + /// Create a unity texture 2D. + /// + /// A vgo texture. + /// A vgo storage. + /// + /// A unity texture 2D. +#if UNITY_2023_1_OR_NEWER && UNIVGO_USE_UNITY_AWAITABLE + protected virtual async Awaitable CreateTexture2DAsync(VgoTexture vgoTexture, IVgoStorage vgoStorage, CancellationToken cancellationToken) +#elif CYSHARP_UNITASK_2_OR_NEWER && UNIVGO_USE_UNITASK + protected virtual async UniTask CreateTexture2DAsync(VgoTexture vgoTexture, IVgoStorage vgoStorage, CancellationToken cancellationToken) +#else + protected virtual async Task CreateTexture2DAsync(VgoTexture vgoTexture, IVgoStorage vgoStorage, CancellationToken cancellationToken) +#endif + { + if (vgoStorage.ResourceAccessors is null) + { + throw new Exception(); + } + + if (vgoTexture.source.IsInRangeOf(vgoStorage.ResourceAccessors) == false) + { + Debug.LogError($"{nameof(VgoTexture)}.{nameof(vgoTexture.source)}: {vgoTexture.source}"); + + return null; + } + + if (vgoTexture.dimensionType != TextureDimension.Tex2D) + { + Debug.LogError($"{nameof(VgoTexture)}.{nameof(vgoTexture.dimensionType)}: {vgoTexture.dimensionType}"); + + return null; + } + + cancellationToken.ThrowIfCancellationRequested(); + + Texture2D srcTexture2d; + + { +#if UNITY_2023_1_OR_NEWER && UNIVGO_USE_UNITY_AWAITABLE + await Awaitable.BackgroundThreadAsync(); + + //var sw = new System.Diagnostics.Stopwatch(); + + //sw.Start(); + + byte[] imageBytes = vgoStorage.GetAccessorBytes(vgoTexture.source).ToArray(); + + //sw.Stop(); + + //Debug.LogFormat("{0,2}: {1,5}ms", Thread.CurrentThread.ManagedThreadId, sw.ElapsedMilliseconds); + + await Awaitable.MainThreadAsync(); +#elif CYSHARP_UNITASK_2_OR_NEWER && UNIVGO_USE_UNITASK + await UniTask.SwitchToThreadPool(); + + //var sw = new System.Diagnostics.Stopwatch(); + + //sw.Start(); + + byte[] imageBytes = vgoStorage.GetAccessorBytes(vgoTexture.source).ToArray(); + + //sw.Stop(); + + //Debug.LogFormat("{0,2}: {1,5}ms", Thread.CurrentThread.ManagedThreadId, sw.ElapsedMilliseconds); + + await UniTask.SwitchToMainThread(); +#else + //var sw = new System.Diagnostics.Stopwatch(); + + //sw.Start(); + + byte[] imageBytes = vgoStorage.GetAccessorBytes(vgoTexture.source).ToArray(); + + //byte[] imageBytes = await Task.Run(() => + //{ + // //var swt = new System.Diagnostics.Stopwatch(); + + // //swt.Start(); + + // var bytes = vgoStorage.GetAccessorBytes(vgoTexture.source).ToArray(); + + // //swt.Stop(); + + // //Debug.LogFormat("{0,2}: {1,5}ms", Thread.CurrentThread.ManagedThreadId, swt.ElapsedMilliseconds); + + // return bytes; + //}); + + //sw.Stop(); + + //Debug.LogFormat("{0,2}: {1,5}ms", Thread.CurrentThread.ManagedThreadId, sw.ElapsedMilliseconds); +#endif + + srcTexture2d = new Texture2D(width: 2, height: 2, TextureFormat.ARGB32, mipChain: false, linear: vgoTexture.IsLinear) + { + name = vgoTexture.name + }; + + ImageConversion.LoadImage(srcTexture2d, imageBytes); + } + + cancellationToken.ThrowIfCancellationRequested(); + + Texture2D texture2D = _TextureConverter.GetImportTexture(srcTexture2d, vgoTexture.mapType, vgoTexture.metallicRoughness); + + texture2D.filterMode = (UnityEngine.FilterMode)vgoTexture.filterMode; + texture2D.wrapMode = (UnityEngine.TextureWrapMode)vgoTexture.wrapMode; + texture2D.wrapModeU = (UnityEngine.TextureWrapMode)vgoTexture.wrapModeU; + texture2D.wrapModeV = (UnityEngine.TextureWrapMode)vgoTexture.wrapModeV; + + texture2D.Apply(); + +#if UNITY_2023_1_OR_NEWER && UNIVGO_USE_UNITY_AWAITABLE + //await Awaitable.NextFrameAsync(cancellationToken); + + return texture2D; +#elif CYSHARP_UNITASK_2_OR_NEWER && UNIVGO_USE_UNITASK + return await UniTask.FromResult(texture2D); +#else + return await Task.FromResult(texture2D); +#endif + } + + #endregion + } +} diff --git a/UniVgo2/Runtime/Porters/Textures/VgoTextureImporter.cs.meta b/UniVgo2/Runtime/Porters/Textures/VgoTextureImporter.cs.meta new file mode 100644 index 0000000..b61d54c --- /dev/null +++ b/UniVgo2/Runtime/Porters/Textures/VgoTextureImporter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cb78677ce967bb6418ff6d66d3f25115 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UniVgo2/Runtime/Porters/VgoImporter.Async.cs b/UniVgo2/Runtime/Porters/VgoImporter.Async.cs new file mode 100644 index 0000000..b8aa8e0 --- /dev/null +++ b/UniVgo2/Runtime/Porters/VgoImporter.Async.cs @@ -0,0 +1,219 @@ +// ---------------------------------------------------------------------- +// @Namespace : UniVgo2 +// @Class : VgoImporter +// ---------------------------------------------------------------------- +#nullable enable +namespace UniVgo2 +{ +#if UNITY_2023_1_OR_NEWER && UNIVGO_USE_UNITY_AWAITABLE + // +#elif CYSHARP_UNITASK_2_OR_NEWER && UNIVGO_USE_UNITASK + using Cysharp.Threading.Tasks; +#else + using System.Threading.Tasks; +#endif + + using NewtonVgo; + using System.Collections.Generic; + using System.Threading; + using UnityEngine; + + /// + /// VGO Importer + /// + public partial class VgoImporter + { +#if UNITY_WEBGL + // +#else + #region Public Methods + + /// + /// Load a 3D model from the specified file. + /// + /// The file path of the vgo. + /// + /// A vgo model asset. +#if UNITY_2023_1_OR_NEWER && UNIVGO_USE_UNITY_AWAITABLE + public virtual async Awaitable LoadAsync(string vgoFilePath, CancellationToken cancellationToken) +#elif CYSHARP_UNITASK_2_OR_NEWER && UNIVGO_USE_UNITASK + public virtual async UniTask LoadAsync(string vgoFilePath, CancellationToken cancellationToken) +#else + public virtual async Task LoadAsync(string vgoFilePath, CancellationToken cancellationToken) +#endif + { + var vgoStorage = new VgoStorage(vgoFilePath); + + return await LoadAsync(vgoStorage, cancellationToken); + } + + /// + /// Load a 3D model from the specified file. + /// + /// The file path of the vgo. + /// The file path of the crypt key. + /// + /// A vgo model asset. +#if UNITY_2023_1_OR_NEWER && UNIVGO_USE_UNITY_AWAITABLE + public virtual async Awaitable LoadAsync(string vgoFilePath, string vgkFilePath, CancellationToken cancellationToken) +#elif CYSHARP_UNITASK_2_OR_NEWER && UNIVGO_USE_UNITASK + public virtual async UniTask LoadAsync(string vgoFilePath, string vgkFilePath, CancellationToken cancellationToken) +#else + public virtual async Task LoadAsync(string vgoFilePath, string vgkFilePath, CancellationToken cancellationToken) +#endif + { + var vgoStorage = new VgoStorage(vgoFilePath, vgkFilePath); + + return await LoadAsync(vgoStorage, cancellationToken); + } + + /// + /// Load a 3D model from the specified bytes. + /// + /// The vgo bytes. + /// + /// A vgo model asset. +#if UNITY_2023_1_OR_NEWER && UNIVGO_USE_UNITY_AWAITABLE + public virtual async Awaitable LoadAsync(byte[] vgoBytes, CancellationToken cancellationToken) +#elif CYSHARP_UNITASK_2_OR_NEWER && UNIVGO_USE_UNITASK + public virtual async UniTask LoadAsync(byte[] vgoBytes, CancellationToken cancellationToken) +#else + public virtual async Task LoadAsync(byte[] vgoBytes, CancellationToken cancellationToken) +#endif + { + var vgoStorage = new VgoStorage(vgoBytes); + + return await LoadAsync(vgoStorage, cancellationToken); + } + + /// + /// Load a 3D model from the specified bytes. + /// + /// The vgo bytes. + /// The vgk bytes. + /// + /// A vgo model asset. +#if UNITY_2023_1_OR_NEWER && UNIVGO_USE_UNITY_AWAITABLE + public virtual async Awaitable LoadAsync(byte[] vgoBytes, byte[] vgkBytes, CancellationToken cancellationToken) +#elif CYSHARP_UNITASK_2_OR_NEWER && UNIVGO_USE_UNITASK + public virtual async UniTask LoadAsync(byte[] vgoBytes, byte[] vgkBytes, CancellationToken cancellationToken) +#else + public virtual async Task LoadAsync(byte[] vgoBytes, byte[] vgkBytes, CancellationToken cancellationToken) +#endif + { + var vgoStorage = new VgoStorage(vgoBytes, vgkBytes); + + return await LoadAsync(vgoStorage, cancellationToken); + } + + /// + /// Load a 3D model from the specified vgo storage. + /// + /// A vgo storage. + /// + /// A vgo model asset. +#if UNITY_2023_1_OR_NEWER && UNIVGO_USE_UNITY_AWAITABLE + public virtual async Awaitable LoadAsync(IVgoStorage vgoStorage, CancellationToken cancellationToken) +#elif CYSHARP_UNITASK_2_OR_NEWER && UNIVGO_USE_UNITASK + public virtual async UniTask LoadAsync(IVgoStorage vgoStorage, CancellationToken cancellationToken) +#else + public virtual async Task LoadAsync(IVgoStorage vgoStorage, CancellationToken cancellationToken) +#endif + { + var vgoModelAsset = new VgoModelAsset(); + + vgoModelAsset.Layout = vgoStorage.Layout; + + // UnityEngine.Texture2D + //vgoModelAsset.Texture2dList = _TextureImporter.CreateTextureAssets(vgoStorage); + vgoModelAsset.Texture2dList = await _TextureImporter.CreateTextureAssetsAsync(vgoStorage, cancellationToken); + + // UnityEngine.Material + vgoModelAsset.MaterialList = CreateMaterialAssets(vgoStorage, vgoModelAsset.Texture2dList); + + // UnityEngine.Mesh + if (vgoStorage.IsSpecVersion_2_4_orLower) + { + //vgoModelAsset.MeshAssetList = _MeshImporter.CreateMeshAssets(vgoStorage, modelAsset.MaterialList); + vgoModelAsset.MeshAssetList = await _MeshImporter.CreateMeshAssetsAsync(vgoStorage, vgoModelAsset.MaterialList, cancellationToken); + //vgoModelAsset.MeshAssetList = await _MeshImporter.CreateMeshAssetsParallelAsync(vgoStorage, modelAsset.MaterialList, cancellationToken); + } + else + { + //vgoModelAsset.MeshAssetList = _MeshImporter.CreateMeshAssets(vgoStorage); + vgoModelAsset.MeshAssetList = await _MeshImporter.CreateMeshAssetsAsync(vgoStorage, null, cancellationToken); + //vgoModelAsset.MeshAssetList = await _MeshImporter.CreateMeshAssetsParallelAsync(vgoStorage, null, cancellationToken); + } + + // UnityEngine.AnimationClip + vgoModelAsset.AnimationClipList = CreateAnimationClipAssets(vgoStorage.Layout, vgoStorage.GeometryCoordinate); + + // UnityEngine.VgoSpringBoneColliderGroup + vgoModelAsset.SpringBoneColliderGroupArray = CreateSpringBoneColliderGroupArray(vgoStorage.Layout); + + // UnityEngine.GameObejct + List nodes = CreateNodes(vgoStorage); + + CreateNodeHierarchy(nodes, vgoStorage.Layout); + + vgoModelAsset.Root = nodes[0].gameObject; + + if (vgoStorage.GeometryCoordinate == VgoGeometryCoordinate.RightHanded) + { + FixCoordinate(nodes); + } + + SetupNodes(nodes, vgoStorage, vgoModelAsset); + + SetupAssetInfo(vgoStorage, vgoModelAsset); + +#if UNITY_2023_1_OR_NEWER && UNIVGO_USE_UNITY_AWAITABLE + //await Awaitable.NextFrameAsync(cancellationToken); + + return vgoModelAsset; +#elif CYSHARP_UNITASK_2_OR_NEWER && UNIVGO_USE_UNITASK + return await UniTask.FromResult(vgoModelAsset); +#else + return await Task.FromResult(vgoModelAsset); +#endif + } + + #endregion +#endif // UNITY_WEBGL + + #region Public Methods + + ///// + ///// Extract a 3D model asset from the specified file. + ///// + ///// The file path of the vgo. + ///// The file path of the vgk. + ///// + ///// A vgo model asset. + ///// for ScriptedImporter +#if UNITY_2023_1_OR_NEWER && UNIVGO_USE_UNITY_AWAITABLE + //public virtual async Awaitable ExtractAsync(string vgoFilePath, string? vgkFilePath, CancellationToken cancellationToken) +#elif CYSHARP_UNITASK_2_OR_NEWER && UNIVGO_USE_UNITASK + //public virtual async UniTask ExtractAsync(string vgoFilePath, string? vgkFilePath, CancellationToken cancellationToken) +#else + //public virtual async Task ExtractAsync(string vgoFilePath, string? vgkFilePath, CancellationToken cancellationToken) +#endif + //{ + // var vgoStorage = new VgoStorage(vgoFilePath, vgkFilePath); + + // var vgoModelAsset = new VgoModelAsset(); + + // vgoModelAsset.Layout = vgoStorage.Layout; + + // // UnityEngine.Texture2D + // vgoModelAsset.Texture2dList = await _TextureImporter.CreateTextureAssetsAsync(vgoStorage, cancellationToken); + + // // UnityEngine.Material + // vgoModelAsset.MaterialList = CreateMaterialAssets(vgoStorage, modelAsset.Texture2dList); + + // return vgoModelAsset; + //} + + #endregion + } +} diff --git a/UniVgo2/Runtime/Porters/VgoImporter.Async.cs.meta b/UniVgo2/Runtime/Porters/VgoImporter.Async.cs.meta new file mode 100644 index 0000000..3c12380 --- /dev/null +++ b/UniVgo2/Runtime/Porters/VgoImporter.Async.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1bbdfb136f628ac41a3689aa46d69d49 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UniVgo2/Runtime/Porters/VgoImporter.cs b/UniVgo2/Runtime/Porters/VgoImporter.cs index ed00ee1..0c58ae6 100644 --- a/UniVgo2/Runtime/Porters/VgoImporter.cs +++ b/UniVgo2/Runtime/Porters/VgoImporter.cs @@ -16,7 +16,7 @@ namespace UniVgo2 /// /// VGO Importer /// - public class VgoImporter + public partial class VgoImporter { #region Fields @@ -32,8 +32,8 @@ public class VgoImporter /// The particle system importer. protected readonly IVgoParticleSystemImporter _ParticleSystemImporter; - /// The texture converter. - protected readonly ITextureConverter _TextureConverter; + /// The texture importer. + protected readonly IVgoTextureImporter _TextureImporter; #endregion @@ -62,7 +62,7 @@ public VgoImporter(VgoImporterOption option) _ParticleSystemImporter = new VgoParticleSystemImporter(); - _TextureConverter = new TextureConverter(); + _TextureImporter = new VgoTextureImporter(); } #endregion @@ -74,77 +74,77 @@ public VgoImporter(VgoImporterOption option) /// /// The file path of the vgo. /// The file path of the crypt key. - /// A model asset. - public virtual ModelAsset Load(string vgoFilePath, string? vgkFilePath = null) + /// A vgo model asset. + public virtual VgoModelAsset Load(string vgoFilePath, string? vgkFilePath = null) { - var storage = new VgoStorage(vgoFilePath, vgkFilePath); + var vgoStorage = new VgoStorage(vgoFilePath, vgkFilePath); - return Load(storage); + return Load(vgoStorage); } /// /// Load a 3D model from the specified bytes. /// - /// All bytes of file. - /// The crypt key. - /// A model asset. - public virtual ModelAsset Load(byte[] allBytes, byte[]? cryptKey = null) + /// The vgo bytes. + /// The vgk bytes. + /// A vgo model asset. + public virtual VgoModelAsset Load(byte[] vgoBytes, byte[]? vgkBytes = null) { - var storage = new VgoStorage(allBytes, cryptKey); + var vgoStorage = new VgoStorage(vgoBytes, vgkBytes); - return Load(storage); + return Load(vgoStorage); } /// /// Load a 3D model from the specified vgo storage. /// /// A vgo storage. - /// A model asset. - public virtual ModelAsset Load(IVgoStorage vgoStorage) + /// A vgo model asset. + public virtual VgoModelAsset Load(IVgoStorage vgoStorage) { - var modelAsset = new ModelAsset(); + var vgoModelAsset = new VgoModelAsset(); - modelAsset.Layout = vgoStorage.Layout; + vgoModelAsset.Layout = vgoStorage.Layout; // UnityEngine.Texture2D - modelAsset.Texture2dList = CreateTextureAssets(vgoStorage); + vgoModelAsset.Texture2dList = _TextureImporter.CreateTextureAssets(vgoStorage); // UnityEngine.Material - modelAsset.MaterialList = CreateMaterialAssets(vgoStorage, modelAsset.Texture2dList); + vgoModelAsset.MaterialList = CreateMaterialAssets(vgoStorage, vgoModelAsset.Texture2dList); // UnityEngine.Mesh if (vgoStorage.IsSpecVersion_2_4_orLower) { - modelAsset.MeshAssetList = CreateMeshAssets(vgoStorage, modelAsset.MaterialList); + vgoModelAsset.MeshAssetList = _MeshImporter.CreateMeshAssets(vgoStorage, vgoModelAsset.MaterialList); } else { - modelAsset.MeshAssetList = CreateMeshAssets(vgoStorage); + vgoModelAsset.MeshAssetList = _MeshImporter.CreateMeshAssets(vgoStorage); } // UnityEngine.AnimationClip - modelAsset.AnimationClipList = CreateAnimationClipAssets(vgoStorage.Layout, vgoStorage.GeometryCoordinate); + vgoModelAsset.AnimationClipList = CreateAnimationClipAssets(vgoStorage.Layout, vgoStorage.GeometryCoordinate); // UnityEngine.VgoSpringBoneColliderGroup - modelAsset.SpringBoneColliderGroupArray = CreateSpringBoneColliderGroupArray(vgoStorage.Layout); + vgoModelAsset.SpringBoneColliderGroupArray = CreateSpringBoneColliderGroupArray(vgoStorage.Layout); // UnityEngine.GameObejct List nodes = CreateNodes(vgoStorage); CreateNodeHierarchy(nodes, vgoStorage.Layout); - modelAsset.Root = nodes[0].gameObject; + vgoModelAsset.Root = nodes[0].gameObject; if (vgoStorage.GeometryCoordinate == VgoGeometryCoordinate.RightHanded) { FixCoordinate(nodes); } - SetupNodes(nodes, vgoStorage, modelAsset); + SetupNodes(nodes, vgoStorage, vgoModelAsset); - SetupAssetInfo(vgoStorage, modelAsset); + SetupAssetInfo(vgoStorage, vgoModelAsset); - return modelAsset; + return vgoModelAsset; } /// @@ -152,118 +152,23 @@ public virtual ModelAsset Load(IVgoStorage vgoStorage) /// /// The file path of the vgo. /// The file path of the vgk. - /// A model asset. + /// A vgo model asset. /// for ScriptedImporter - public virtual ModelAsset Extract(string vgoFilePath, string? vgkFilePath = null) + public virtual VgoModelAsset Extract(string vgoFilePath, string? vgkFilePath = null) { var vgoStorage = new VgoStorage(vgoFilePath, vgkFilePath); - var modelAsset = new ModelAsset(); + var vgoModelAsset = new VgoModelAsset(); - modelAsset.Layout = vgoStorage.Layout; + vgoModelAsset.Layout = vgoStorage.Layout; // UnityEngine.Texture2D - modelAsset.Texture2dList = CreateTextureAssets(vgoStorage); + vgoModelAsset.Texture2dList = _TextureImporter.CreateTextureAssets(vgoStorage); // UnityEngine.Material - modelAsset.MaterialList = CreateMaterialAssets(vgoStorage, modelAsset.Texture2dList); + vgoModelAsset.MaterialList = CreateMaterialAssets(vgoStorage, vgoModelAsset.Texture2dList); - return modelAsset; - } - - #endregion - - #region layout.textures - - /// - /// Create texture assets. - /// - /// A vgo storage. - /// List of unity texture2D. - /// - /// After UpdateTextureInfoList() - /// - protected virtual List CreateTextureAssets(IVgoStorage vgoStorage) - { - var texture2dList = new List(); - - if ((vgoStorage.Layout.textures == null) || (vgoStorage.Layout.textures.Any() == false)) - { - return texture2dList; - } - - for (int textureIndex = 0; textureIndex < vgoStorage.Layout.textures.Count; textureIndex++) - { - VgoTexture? vgoTexture = vgoStorage.Layout.textures[textureIndex]; - - if (vgoTexture is null) - { - texture2dList.Add(null); - } - else - { - Texture2D? texture2d = CreateTexture2D(vgoTexture, vgoStorage); - - texture2dList.Add(texture2d); - } - } - - return texture2dList; - } - - /// - /// Create a unity texture 2D. - /// - /// A vgo texture. - /// A vgo storage. - /// A unity texture 2D. - protected virtual Texture2D? CreateTexture2D(VgoTexture vgoTexture, IVgoStorage vgoStorage) - { - if (vgoStorage.ResourceAccessors is null) - { - throw new Exception(); - } - - if (vgoTexture.source.IsInRangeOf(vgoStorage.ResourceAccessors) == false) - { - Debug.LogError($"{nameof(VgoTexture)}.{nameof(vgoTexture.source)}: {vgoTexture.source}"); - - return null; - } - - if (vgoTexture.dimensionType != TextureDimension.Tex2D) - { - Debug.LogError($"{nameof(VgoTexture)}.{nameof(vgoTexture.dimensionType)}: {vgoTexture.dimensionType}"); - - return null; - } - - try - { - byte[] imageBytes = vgoStorage.GetAccessorBytes(vgoTexture.source).ToArray(); - - var srcTexture2d = new Texture2D(width: 2, height: 2, TextureFormat.ARGB32, mipChain: false, linear: vgoTexture.IsLinear) - { - name = vgoTexture.name - }; - - ImageConversion.LoadImage(srcTexture2d, imageBytes); - - Texture2D texture2D = _TextureConverter.GetImportTexture(srcTexture2d, vgoTexture.mapType, vgoTexture.metallicRoughness); - - texture2D.filterMode = (UnityEngine.FilterMode)vgoTexture.filterMode; - texture2D.wrapMode = (UnityEngine.TextureWrapMode)vgoTexture.wrapMode; - texture2D.wrapModeU = (UnityEngine.TextureWrapMode)vgoTexture.wrapModeU; - texture2D.wrapModeV = (UnityEngine.TextureWrapMode)vgoTexture.wrapModeV; - - return texture2D; - } - catch (Exception ex) - { - Debug.LogException(ex); - - return null; - } + return vgoModelAsset; } #endregion @@ -306,40 +211,6 @@ public virtual ModelAsset Extract(string vgoFilePath, string? vgkFilePath = null #endregion - #region layout.meshes - - /// - /// Create mesh assets. - /// - /// A vgo storage. - /// List of unity material. - /// List of mesh asset. - protected virtual List CreateMeshAssets(IVgoStorage vgoStorage, IList? materialList = null) - { - var meshAssetList = new List(); - - if ((vgoStorage.Layout.meshes == null) || (vgoStorage.Layout.meshes.Any() == false)) - { - return meshAssetList; - } - - for (int meshIndex = 0; meshIndex < vgoStorage.Layout.meshes.Count; meshIndex++) - { - MeshAsset meshAsset = _MeshImporter.CreateMeshAsset(vgoStorage, meshIndex, materialList); - - if (meshAssetList.Where(x => x?.Mesh.name == meshAsset.Mesh.name).Any()) - { - meshAsset.Mesh.name = string.Format($"mesh_{meshIndex}"); - } - - meshAssetList.Add(meshAsset); - } - - return meshAssetList; - } - - #endregion - #region layout.springBoneInfo /// @@ -485,8 +356,8 @@ protected virtual void FixCoordinate(List nodes) /// /// List of node. /// A vgo storage. - /// A model asset. - protected virtual void SetupNodes(List nodes, IVgoStorage vgoStorage, ModelAsset modelAsset) + /// A vgo model asset. + protected virtual void SetupNodes(List nodes, IVgoStorage vgoStorage, VgoModelAsset vgoModelAsset) { if (vgoStorage.Layout.nodes is null) { @@ -495,7 +366,7 @@ protected virtual void SetupNodes(List nodes, IVgoStorage vgoStorage, for (int nodeIndex = 0; nodeIndex < vgoStorage.Layout.nodes.Count; nodeIndex++) { - SetupNode(nodes, nodeIndex, vgoStorage.Layout, vgoStorage.GeometryCoordinate, modelAsset); + SetupNode(nodes, nodeIndex, vgoStorage.Layout, vgoStorage.GeometryCoordinate, vgoModelAsset); } // UnityEngine.Renderer @@ -512,30 +383,30 @@ protected virtual void SetupNodes(List nodes, IVgoStorage vgoStorage, { if (vgoNode.mesh >= 0) { - AttachMeshAndRenderer(nodes, nodeIndex, vgoStorage, modelAsset, _Option.ShowMesh, _Option.UpdateWhenOffscreen); + AttachMeshAndRenderer(nodes, nodeIndex, vgoStorage, vgoModelAsset, _Option.ShowMesh, _Option.UpdateWhenOffscreen); } } else { if (vgoNode.meshRenderer != null) { - AttachMeshAndRenderer(nodes, nodeIndex, vgoStorage, modelAsset, _Option.ShowMesh, _Option.UpdateWhenOffscreen); + AttachMeshAndRenderer(nodes, nodeIndex, vgoStorage, vgoModelAsset, _Option.ShowMesh, _Option.UpdateWhenOffscreen); } } } - modelAsset.ColliderList = CreateUnityColliderList(nodes); + vgoModelAsset.ColliderList = CreateUnityColliderList(nodes); // UnigyEngine.Cloth for (int nodeIndex = 0; nodeIndex < vgoStorage.Layout.nodes.Count; nodeIndex++) { - SetupNodeCloth(nodes, nodeIndex, vgoStorage, modelAsset.ColliderList); + SetupNodeCloth(nodes, nodeIndex, vgoStorage, vgoModelAsset.ColliderList); } // UnigyEngine.VgoSpringBone for (int nodeIndex = 0; nodeIndex < vgoStorage.Layout.nodes.Count; nodeIndex++) { - SetupNodeSpringBone(nodes, nodeIndex, vgoStorage.Layout, vgoStorage.GeometryCoordinate, modelAsset); + SetupNodeSpringBone(nodes, nodeIndex, vgoStorage.Layout, vgoStorage.GeometryCoordinate, vgoModelAsset); } } @@ -546,8 +417,8 @@ protected virtual void SetupNodes(List nodes, IVgoStorage vgoStorage, /// The index of layout.nodes. /// A vgo layout. /// - /// A model asset. - protected virtual void SetupNode(List nodes, int nodeIndex, VgoLayout vgoLayout, VgoGeometryCoordinate geometryCoordinate, ModelAsset modelAsset) + /// A vgo model asset. + protected virtual void SetupNode(List nodes, int nodeIndex, VgoLayout vgoLayout, VgoGeometryCoordinate geometryCoordinate, VgoModelAsset vgoModelAsset) { if (vgoLayout.nodes is null) { @@ -608,9 +479,9 @@ protected virtual void SetupNode(List nodes, int nodeIndex, VgoLayout AvatarConfiguration avatarConfiguration = CreateAvatarConfiguration(vgoNode.animator.humanAvatar); - modelAsset.Avatar = animator.avatar; + vgoModelAsset.Avatar = animator.avatar; - modelAsset.ScriptableObjectList.Add(avatarConfiguration); + vgoModelAsset.ScriptableObjectList.Add(avatarConfiguration); } } @@ -621,7 +492,7 @@ protected virtual void SetupNode(List nodes, int nodeIndex, VgoLayout try { - VgoAnimationConverter.SetComponentValue(animation, vgoNode.animation, modelAsset.AnimationClipList, geometryCoordinate); + VgoAnimationConverter.SetComponentValue(animation, vgoNode.animation, vgoModelAsset.AnimationClipList, geometryCoordinate); if (animation.enabled && animation.playAutomatically && @@ -711,9 +582,9 @@ protected virtual void SetupNode(List nodes, int nodeIndex, VgoLayout component.gizmoColor = layoutSpringBoneColliderGroup.gizmoColor.ToUnityColor(); - if (modelAsset.SpringBoneColliderGroupArray != null) + if (vgoModelAsset.SpringBoneColliderGroupArray != null) { - modelAsset.SpringBoneColliderGroupArray[vgoNode.springBoneColliderGroup] = component; + vgoModelAsset.SpringBoneColliderGroupArray[vgoNode.springBoneColliderGroup] = component; } } } @@ -748,7 +619,7 @@ protected virtual void SetupNode(List nodes, int nodeIndex, VgoLayout vgoLayout.particles.TryGetValue(vgoNode.particle, out var vgoParticleSystem) && vgoParticleSystem != null) { - _ParticleSystemImporter.AddComponent(go, vgoParticleSystem, geometryCoordinate, modelAsset.MaterialList, modelAsset.Texture2dList); + _ParticleSystemImporter.AddComponent(go, vgoParticleSystem, geometryCoordinate, vgoModelAsset.MaterialList, vgoModelAsset.Texture2dList); } } catch (Exception ex) @@ -763,8 +634,8 @@ protected virtual void SetupNode(List nodes, int nodeIndex, VgoLayout skybox.enabled = vgoNode.skybox.enabled; - if (modelAsset.MaterialList != null && - modelAsset.MaterialList.TryGetValue(vgoNode.skybox.materialIndex, out var skyboxMaterial) && + if (vgoModelAsset.MaterialList != null && + vgoModelAsset.MaterialList.TryGetValue(vgoNode.skybox.materialIndex, out var skyboxMaterial) && skyboxMaterial != null) { skybox.material = skyboxMaterial; @@ -794,8 +665,8 @@ protected virtual void SetupNode(List nodes, int nodeIndex, VgoLayout /// The index of layout.nodes. /// A vgo layout. /// - /// A model asset. - protected virtual void SetupNodeSpringBone(List nodes, int nodeIndex, VgoLayout vgoLayout, VgoGeometryCoordinate geometryCoordinate, ModelAsset modelAsset) + /// A vgo model asset. + protected virtual void SetupNodeSpringBone(List nodes, int nodeIndex, VgoLayout vgoLayout, VgoGeometryCoordinate geometryCoordinate, VgoModelAsset vgoModelAsset) { if (vgoLayout.nodes is null) { @@ -865,15 +736,15 @@ protected virtual void SetupNodeSpringBone(List nodes, int nodeIndex, // colliderGroups if (layoutSpringBoneGroup.colliderGroups != null && layoutSpringBoneGroup.colliderGroups.Any() && - modelAsset.SpringBoneColliderGroupArray != null) + vgoModelAsset.SpringBoneColliderGroupArray != null) { component.colliderGroups = new VgoSpringBone.VgoSpringBoneColliderGroup[layoutSpringBoneGroup.colliderGroups.Length]; for (int index = 0; index < layoutSpringBoneGroup.colliderGroups.Length; index++) { - if (layoutSpringBoneGroup.colliderGroups[index].IsInRangeOf(modelAsset.SpringBoneColliderGroupArray)) + if (layoutSpringBoneGroup.colliderGroups[index].IsInRangeOf(vgoModelAsset.SpringBoneColliderGroupArray)) { - var colliderGroup = modelAsset.SpringBoneColliderGroupArray[layoutSpringBoneGroup.colliderGroups[index]]; + var colliderGroup = vgoModelAsset.SpringBoneColliderGroupArray[layoutSpringBoneGroup.colliderGroups[index]]; component.colliderGroups[index] = colliderGroup; } @@ -979,16 +850,17 @@ protected virtual void SetupNodeCloth(List nodes, int nodeIndex, IVgo /// /// List of node. /// The index of layout.node. + /// A vgo model asset. /// Whether show mesh renderer. /// Whether update skinned mesh renderer when off screen. - protected virtual void AttachMeshAndRenderer(List nodes, int nodeIndex, IVgoStorage vgoStorage, ModelAsset modelAsset, bool showMesh = true, bool updateWhenOffscreen = false) + protected virtual void AttachMeshAndRenderer(List nodes, int nodeIndex, IVgoStorage vgoStorage, VgoModelAsset vgoModelAsset, bool showMesh = true, bool updateWhenOffscreen = false) { if (vgoStorage.Layout.nodes is null) { return; } - if (modelAsset.MeshAssetList is null) + if (vgoModelAsset.MeshAssetList is null) { return; } @@ -1006,7 +878,7 @@ protected virtual void AttachMeshAndRenderer(List nodes, int nodeInde if (vgoStorage.IsSpecVersion_2_4_orLower) { - MeshAsset meshAsset = modelAsset.MeshAssetList[vgoNode.mesh]; + MeshAsset meshAsset = vgoModelAsset.MeshAssetList[vgoNode.mesh]; Mesh mesh = meshAsset.Mesh; @@ -1065,7 +937,7 @@ protected virtual void AttachMeshAndRenderer(List nodes, int nodeInde vgoBlendShape.BlendShapeConfiguration = blendShapeConfiguration; - modelAsset.ScriptableObjectList.Add(blendShapeConfiguration); + vgoModelAsset.ScriptableObjectList.Add(blendShapeConfiguration); } } else @@ -1077,7 +949,7 @@ protected virtual void AttachMeshAndRenderer(List nodes, int nodeInde VgoMeshRenderer vgoMeshRenderer = vgoNode.meshRenderer; - MeshAsset meshAsset = modelAsset.MeshAssetList[vgoMeshRenderer.mesh]; + MeshAsset meshAsset = vgoModelAsset.MeshAssetList[vgoMeshRenderer.mesh]; Mesh mesh = meshAsset.Mesh; @@ -1085,11 +957,11 @@ protected virtual void AttachMeshAndRenderer(List nodes, int nodeInde if (vgoMeshRenderer.materials != null && vgoMeshRenderer.materials.Any() && - modelAsset.MaterialList != null && - modelAsset.MaterialList.Any()) + vgoModelAsset.MaterialList != null && + vgoModelAsset.MaterialList.Any()) { materials = vgoMeshRenderer.materials - .Select(materialIndex => modelAsset.MaterialList[materialIndex]) + .Select(materialIndex => vgoModelAsset.MaterialList[materialIndex]) .ToArray(); } @@ -1175,7 +1047,7 @@ protected virtual void AttachMeshAndRenderer(List nodes, int nodeInde vgoBlendShape.BlendShapeConfiguration = blendShapeConfiguration; - modelAsset.ScriptableObjectList.Add(blendShapeConfiguration); + vgoModelAsset.ScriptableObjectList.Add(blendShapeConfiguration); } } @@ -1338,15 +1210,15 @@ protected virtual void SetupSkin(SkinnedMeshRenderer skinnedMeshRenderer, int sk /// Setup a asset info to root GameObject. /// /// A vgo storage. - /// A model asset. - protected virtual void SetupAssetInfo(IVgoStorage vgoStorage, ModelAsset modelAsset) + /// A vgo model asset. + protected virtual void SetupAssetInfo(IVgoStorage vgoStorage, VgoModelAsset vgoModelAsset) { if (vgoStorage.AssetInfo == null) { return; } - if (modelAsset.Root == null) + if (vgoModelAsset.Root == null) { return; } @@ -1355,14 +1227,14 @@ protected virtual void SetupAssetInfo(IVgoStorage vgoStorage, ModelAsset modelAs if (vgoAssetInfo.right != null) { - VgoRight vgoRightComponent = modelAsset.Root.AddComponent(); + VgoRight vgoRightComponent = vgoModelAsset.Root.AddComponent(); vgoRightComponent.Right = new NewtonVgo.VgoRight(vgoAssetInfo.right); } if (vgoAssetInfo.generator != null) { - VgoGenerator vgoGeneratorComponent = modelAsset.Root.AddComponent(); + VgoGenerator vgoGeneratorComponent = vgoModelAsset.Root.AddComponent(); vgoGeneratorComponent.GeneratorInfo = new VgoGeneratorInfo(vgoAssetInfo.generator); } @@ -1376,15 +1248,15 @@ protected virtual void SetupAssetInfo(IVgoStorage vgoStorage, ModelAsset modelAs /// Reflect VGO skybox to Camera skybox. /// /// A scene main camera. - /// A model asset. - public virtual void ReflectSkybox(Camera camera, ModelAsset modelAsset) + /// A vgo model asset. + public virtual void ReflectSkybox(Camera camera, VgoModelAsset vgoModelAsset) { - if (modelAsset.Root == null) + if (vgoModelAsset.Root == null) { return; } - var vgoSkybox = modelAsset.Root.GetComponentInChildren(includeInactive: false); + var vgoSkybox = vgoModelAsset.Root.GetComponentInChildren(includeInactive: false); if (vgoSkybox != null) { diff --git a/UniVgo2/Runtime/UniVgo2.asmdef b/UniVgo2/Runtime/UniVgo2.asmdef index a948317..6fb6cd1 100644 --- a/UniVgo2/Runtime/UniVgo2.asmdef +++ b/UniVgo2/Runtime/UniVgo2.asmdef @@ -18,6 +18,7 @@ "MToon", "ShaderProperty.Runtime", "VRMShaders.GLTF.UniUnlit.Runtime", + "UniTask", "VgoSpringBone" ], "precompiledReferences": [ @@ -29,6 +30,11 @@ "optionalUnityReferences": [], "defineConstraints": [], "versionDefines": [ + { + "name": "com.cysharp.unitask", + "expression": "2.0.0", + "define": "CYSHARP_UNITASK_2_OR_NEWER" + }, { "name": "com.vrmc.vrmshaders", "expression": "[0.72.0,0.84.0]", diff --git a/UniVgo2/Runtime/VgoVersion.cs b/UniVgo2/Runtime/VgoVersion.cs index 79ceebe..600fb2e 100644 --- a/UniVgo2/Runtime/VgoVersion.cs +++ b/UniVgo2/Runtime/VgoVersion.cs @@ -17,9 +17,9 @@ public class VgoVersion public const int MINOR = 5; /// Patch - public const int PATCH = 6; + public const int PATCH = 7; /// Version - public const string VERSION = "2.5.6"; + public const string VERSION = "2.5.7"; } } diff --git a/package.json b/package.json index e5e7338..a6d3d2e 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "com.izayoi.univgo", "displayName": "UniVGO", "description": "UniVGO is a package that can handle VGO files in Unity.", - "version": "2.5.6", + "version": "2.5.7", "type": "tool", "category": "", "keywords": [ @@ -34,6 +34,7 @@ "com.izayoi.liltoon.shader.utility": "1.4.0", "com.izayoi.unishaders": "1.4.0", "com.izayoi.vgospringbone": "1.1.2", + "com.unity.modules.imageconversion": "1.0.0", "com.unity.nuget.newtonsoft-json": "3.0.0", "com.vrmc.vrmshaders": "0.72.0" }