mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-09-08 03:48:19 -05:00
ドキュメント整理
This commit is contained in:
36
docs/api/0_36_update.md
Normal file
36
docs/api/0_36_update.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# `v0.36` GltfUpdate
|
||||
|
||||
## テクスチャ名の格納位置の修正
|
||||
|
||||
GLTFの仕様に準拠しました。
|
||||
|
||||
* extraはextrasの間違い
|
||||
* imageはnameを持っていた
|
||||
|
||||
```js
|
||||
json.images[i].extra.name
|
||||
```
|
||||
|
||||
変更後
|
||||
|
||||
```js
|
||||
json.images[i].name
|
||||
```
|
||||
|
||||
## ブレンドシェイプ名の格納位置の修正
|
||||
|
||||
GLTFの仕様に準拠しました。
|
||||
|
||||
* extraはextrasの間違い
|
||||
* targetにextrasは不許可
|
||||
* https://github.com/KhronosGroup/glTF/issues/1036#issuecomment-314078356
|
||||
|
||||
```js
|
||||
json.meshes[i].primitives[j].targets[k].extra.name
|
||||
```
|
||||
|
||||
変更後
|
||||
|
||||
```js
|
||||
json.meshes[i].primitives[j].extras.targetNames[k]
|
||||
```
|
||||
163
docs/api/0_44_runtime_import.md
Normal file
163
docs/api/0_44_runtime_import.md
Normal file
@@ -0,0 +1,163 @@
|
||||
# `v0.44` RuntimeImport
|
||||
|
||||
## `Version 0.44~` LoadAsyncの例
|
||||
|
||||
```csharp
|
||||
var bytes = File.ReadAllBytes(path);
|
||||
// なんらかの方法でByte列を得る
|
||||
|
||||
var context = new VRMImporterContext();
|
||||
|
||||
context.ParseGlb(bytes);
|
||||
|
||||
// metaが必要な場合
|
||||
bool createThumbnail=true;
|
||||
var meta = context.ReadMeta(createThumbnail);
|
||||
var thumbnail = meta.Thumbnail;
|
||||
|
||||
// modelを構築
|
||||
context.LoadAsync(_ =>
|
||||
{
|
||||
context.ShowMeshes();
|
||||
var go = context.Root;
|
||||
// load完了
|
||||
},
|
||||
Debug.LogError);
|
||||
```
|
||||
|
||||
## LoadAsyncTaskを使う例
|
||||
|
||||
```csharp
|
||||
#if (NET_4_6 && UNITY_2017_1_OR_NEWER)
|
||||
async static Task<GameObject> LoadAsync(Byte[] bytes)
|
||||
{
|
||||
var context = new VRMImporterContext();
|
||||
|
||||
// GLB形式でJSONを取得しParseします
|
||||
context.ParseGlb(bytes);
|
||||
|
||||
try
|
||||
{
|
||||
// ParseしたJSONをシーンオブジェクトに変換していく
|
||||
await context.LoadAsyncTask();
|
||||
|
||||
// バウンディングボックスとカメラの位置関係で見切れるのを防止する
|
||||
// SkinnedMeshRenderer.updateWhenOffscreen = true
|
||||
context.EnableUpdateWhenOffscreen();
|
||||
|
||||
// T-Poseのモデルを表示したくない場合、ShowMeshesする前に準備する
|
||||
// ロード後に表示する
|
||||
context.ShowMeshes();
|
||||
|
||||
return context.Root;
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
Debug.LogError(ex);
|
||||
// 関連するリソースを破棄する
|
||||
context.Destroy(true);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
```
|
||||
|
||||
## 関連する記事など
|
||||
|
||||
こちらの記事がわかりやすいです。
|
||||
|
||||
* [UniVRMを使ってVRMモデルをランタイムロードする方法](https://qiita.com/sh_akira/items/8155e4b69107c2a7ede6)
|
||||
|
||||
|
||||
最新バージョンは[こちら]({{< relref "runtime_import.md" >}})をご覧ください。
|
||||
|
||||
Unityで実行時にモデルをインポートする方法です。
|
||||
|
||||
## ファイルパスからVRMを開く
|
||||
|
||||
```csharp
|
||||
var path="sample.vrm";
|
||||
var go=VRM.VRMImporter.LoadFromPath(path);
|
||||
Debug.LogFormat("loaded {0}", go.name);
|
||||
```
|
||||
|
||||
## ファイルパスから非同期にVRMを開く
|
||||
|
||||
```csharp
|
||||
var path="sample.vrm";
|
||||
VRMImporter.LoadVrmAsync(path, go => {
|
||||
Debug.LogFormat("loaded {0}", go.name);
|
||||
});
|
||||
```
|
||||
|
||||
## バイト列からVRM開く
|
||||
|
||||
```csharp
|
||||
var path="sample.vrm";
|
||||
var bytes = File.ReadAllBytes(path);
|
||||
var go=VRMImporter.LoadFromBytes(bytes);
|
||||
```
|
||||
|
||||
## バイト列から非同期にVRMを開く
|
||||
|
||||
```csharp
|
||||
VRMImporter.LoadVrmAsync(bytes, go => {
|
||||
Debug.LogFormat("loaded {0}", go.name);
|
||||
});
|
||||
```
|
||||
|
||||
## VRMから情報を取り出す
|
||||
|
||||
```csharp
|
||||
#if UNITY_STANDALONE_WIN
|
||||
var path = FileDialogForWindows.FileDialog("open VRM", ".vrm");
|
||||
#else
|
||||
var path = Application.dataPath + "/default.vrm";
|
||||
#endif
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Byte列を得る
|
||||
var bytes = File.ReadAllBytes(path);
|
||||
|
||||
var context = new VRMImporterContext();
|
||||
|
||||
// GLB形式をParseしてチャンクからJSONを取得しParseします
|
||||
context.ParseGlb(bytes);
|
||||
|
||||
// metaを取得
|
||||
var meta = context.ReadMeta();
|
||||
Debug.LogFormat("meta: title:{0}", meta.Title);
|
||||
|
||||
// もしくはこちらでパースされたGLTF全体にアクセスできます
|
||||
var vrm = context.GLTF;
|
||||
|
||||
// ParseしたJSONをもとにシーンを構築します
|
||||
if (m_loadAsync)
|
||||
{
|
||||
// 非同期に実行する
|
||||
var now = Time.time;
|
||||
VRMImporter.LoadVrmAsync(context, go=> {
|
||||
var delta = Time.time - now;
|
||||
Debug.LogFormat("LoadVrmAsync {0:0.0} seconds", delta);
|
||||
OnLoaded(go);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// 同期的に実行する
|
||||
VRMImporter.LoadFromBytes(context);
|
||||
OnLoaded(context.Root);
|
||||
}
|
||||
```
|
||||
|
||||
## Thumbnailを取得する(v0.37から)
|
||||
|
||||
ReadMetaに引数を渡すことでThumbnailテクスチャを作成できます。
|
||||
|
||||
```csharp
|
||||
var meta = context.ReadMeta(true); // Thumbnailテクスチャを作成する
|
||||
Texture2D thumbnail=meta.Thumbnail;
|
||||
```
|
||||
141
docs/api/0_58_blendshape.md
Normal file
141
docs/api/0_58_blendshape.md
Normal file
@@ -0,0 +1,141 @@
|
||||
# `v0.58` BlendShapeProxy
|
||||
|
||||
## BlendShapeKeyのインタフェースを厳格化、整理
|
||||
|
||||
BlendShapeKeyを作成する方法が不明瞭だったため、
|
||||
より明示的な API に置き換えました。
|
||||
|
||||
* BlendShapeClip.Key 追加
|
||||
|
||||
| arguments | before | after | 備考 |
|
||||
|--------------------------------------|--------------------------|--------------------------------|------------------------------------------------------------------------------------------------------------------------|
|
||||
| string name, BlendShapePreset preset | public constructor | private constructor | BlendShapePreset.Unknownのときの挙動が不明瞭だった。代わりに、CreateFromPreset または CreateUnknown を使用してください |
|
||||
| BlendShapeClip | BlendShapeKey.CreateFrom | BlendShapeKey.CreateFromClip | 他の関数に合わせて、名前を変更 |
|
||||
| BlendShapePreset | public constructor | BlendShapeKey.CreateFromPreset | オーバーロードをやめて明示的な関数に変更 |
|
||||
| string | public constructor | BlendShapeKey.CreateUnknown | オーバーロードをやめて明示的な関数に変更。 |
|
||||
|
||||
## 使用するメソッド
|
||||
|
||||
* [推奨] `SetValues`
|
||||
* [非推奨] `ImmediatelySetValue`
|
||||
* [上級者向け] `AccumulateValue`
|
||||
* [上級者向け] `Apply`
|
||||
|
||||
## スクリプトから BlendShape weight を適用する
|
||||
|
||||
`SetValues` 関数のみを使用します。
|
||||
そのフレームで必要な表情の weight 値をすべて集めてから `SetValues` を 1 回だけ呼んで設定します。
|
||||
|
||||
```csharp
|
||||
var proxy = GetComponent<VRMBlendShapeProxy>();
|
||||
|
||||
proxy.SetValues(new Dictionary<BlendShapeKey, float>
|
||||
{
|
||||
{BlendShapeKey.CreateFromPreset(BlendShapePreset.A), 1f}, // [0, 1] の範囲で Weight を指定
|
||||
{BlendShapeKey.CreateFromPreset(BlendShapePreset.Joy), 1f}, // システム定義の表情は enum で指定
|
||||
{BlendShapeKey.CreateUnknown("USER_DEFINED_FACIAL"), 1f}, // ユーザ定義の表情は string で指定
|
||||
});
|
||||
```
|
||||
|
||||
## 複数の BlendShape weight を適用する際の競合の問題について
|
||||
|
||||
この節では、なぜ `SetValues` を使わなければならないのかという疑問に回答します。
|
||||
|
||||
たとえば 2 つの VRMBlendShape `Blink_L` と `Blink_R` が
|
||||
|
||||
VRMBlendShape `Blink_L`
|
||||
|
||||
* Mesh `A` の Blendshape `eye_close_L` の weight 値 `100`
|
||||
* Mesh `A` の Blendshape `eye_close_R` の weight 値 `1`
|
||||
|
||||
VRMBlendShape `Blink_R`
|
||||
|
||||
* Mesh `A` の Blendshape `eye_close_L` の weight 値 `1`
|
||||
* Mesh `A` の Blendshape `eye_close_R` の weight 値 `100`
|
||||
|
||||
で定義されているとします。
|
||||
このとき両目を閉じたいモチベーションから、両方を有効にする意図で下記のように実行します。
|
||||
|
||||
```csharp
|
||||
proxy.ImmediatelySetValue(BlendShapeKey.CreateFromPreset(BlendShapePreset.Blink_L), 1.0f);
|
||||
proxy.ImmediatelySetValue(BlendShapeKey.CreateFromPreset(BlendShapePreset.Blink_R), 1.0f);
|
||||
```
|
||||
|
||||
すると、左目だけが開いてしまいます。
|
||||
これは後から `ImmediateSetValue` した `Blink_R` が `Blink_L` と競合して weight を上書きしてしまうからです。
|
||||
したがって VRM の表情制御においては下記の 2 通りのどちらかの方法で書くことが求められます。
|
||||
これらの方法はこの競合の問題を解決して表情を設定することができます。
|
||||
|
||||
```csharp
|
||||
proxy.SetValues(new Dictionary<BlendShapeKey, float>
|
||||
{
|
||||
{BlendShapeKey.CreateFromPreset(BlendShapePreset.Blink_L), 1.0f},
|
||||
{BlendShapeKey.CreateFromPreset(BlendShapePreset.Blink_R), 1.0f},
|
||||
});
|
||||
```
|
||||
|
||||
または
|
||||
|
||||
```csharp
|
||||
proxy.AccumulateValue(BlendShapeKey.CreateFromPreset(BlendShapePreset.Blink_L), 1.0f); // すぐに適用せずにたくわえる
|
||||
proxy.AccumulateValue(BlendShapeKey.CreateFromPreset(BlendShapePreset.Blink_R), 1.0f);
|
||||
proxy.Apply(); // 蓄積した値をまとめて適用する
|
||||
```
|
||||
|
||||
WIP
|
||||
|
||||
## 何故、複数のSetterがあるのか
|
||||
|
||||
* LipSync
|
||||
* 瞬き
|
||||
* 視線制御(BlendShapeで視線を動かすタイプのモデル)
|
||||
* プログラムによる喜怒哀楽
|
||||
|
||||
上記のような複数のBlendShapeが別々のコンポーネントから設定された場合に、
|
||||
BlendShape同士が競合することがわかりました。
|
||||
後で設定した値で上書きされて希望のBlendShapeが適用されないという状態になります。
|
||||
これを解決するために、一か所で中央集権的に制御する必要があります。
|
||||
|
||||
合成したり排他制御した、BlendShapeClipの集合のスナップショットをまとめて適用することを想定して `SetValues`
|
||||
|
||||
## ImmediatelySetValue
|
||||
|
||||
簡単なテストプログラムでの利用を想定しています。
|
||||
|
||||
例:
|
||||
|
||||
```csharp
|
||||
var proxy = GetComponent<VRMBlendShapeProxy>();
|
||||
|
||||
proxy.ImmediatelySetValue(BlendShapeKey.CreateFromPreset(BlendShapePreset.A), 1.0f);
|
||||
```
|
||||
|
||||
## AccumulateValue + Apply
|
||||
|
||||
例:
|
||||
|
||||
```csharp
|
||||
var proxy = GetComponent<VRMBlendShapeProxy>();
|
||||
|
||||
proxy.AccumulateValue(BlendShapeKey.CreateFromPreset(BlendShapePreset.Blink_L), 1.0f); // すぐに適用せずにたくわえる
|
||||
proxy.AccumulateValue(BlendShapeKey.CreateFromPreset(BlendShapePreset.Blink_R), 1.0f);
|
||||
proxy.Apply(); // 蓄積した値をまとめて適用する
|
||||
```
|
||||
|
||||
下記のSetValuesを推奨しています。
|
||||
|
||||
## SetValues
|
||||
|
||||
BlendShape合成器が必要に応じ呼び出すことを想定しています。
|
||||
|
||||
例:
|
||||
|
||||
```csharp
|
||||
var proxy = GetComponent<VRMBlendShapeProxy>();
|
||||
|
||||
proxy.SetValues(new Dictionary<BlendShapeKey, float>
|
||||
{
|
||||
{BlendShapeKey.CreateFromPreset(BlendShapePreset.Blink_L), 1.0f},
|
||||
{BlendShapeKey.CreateFromPreset(BlendShapePreset.Blink_R), 1.0f},
|
||||
});
|
||||
```
|
||||
163
docs/api/0_68_runtime_import.md
Normal file
163
docs/api/0_68_runtime_import.md
Normal file
@@ -0,0 +1,163 @@
|
||||
# `v0.68` RuntimeImport DisposeOnGameObjectDestroyed(obsolete)
|
||||
|
||||
## 過去バージョンからの仕様変更
|
||||
|
||||
`ImporterContext` の仕様を変更しました。
|
||||
|
||||
* ロード処理が Parse と Load の 2 ステップに分かれました。
|
||||
* Parse 処理をメインスレッド以外で処理することができます。
|
||||
* 非同期ロード関数 `ImporterContext.LoadAsync` の実装を `Task` に変更しました。
|
||||
* これまで明示的に破棄できなかった `UnityEngine.Object` リソースを破棄できるようになりました。
|
||||
* リソースのリークを防ぐことができます。
|
||||
* `ImporterContext.Dispose` を呼び出すべきタイミングを「ロード処理終了時」に変更しました。
|
||||
* 呼び出して破棄する前に、後述の `ImporterContext.DisposeOnGameObjectDestroyed` を呼び出してください。
|
||||
* 以前の仕様は「生成したモデルの破棄時」に呼び出すべき関数でした。
|
||||
* `ImporterContext.DisposeOnGameObjectDestroyed` 関数を追加しました。
|
||||
* VRM モデルが必要とするリソース (Texture, Material, Mesh, etc) を破棄する責務を GameObject に移譲できます。
|
||||
* VRM の GameObject の破棄タイミングでリソース (Texture, Material, Mesh, etc) を破棄します。
|
||||
|
||||
|
||||
## サンプルコード(同期的ロード)
|
||||
|
||||
```csharp
|
||||
using UniGLTF;
|
||||
using UnityEngine;
|
||||
using VRM;
|
||||
|
||||
namespace YourNameSpace
|
||||
{
|
||||
public sealed class LoadVrmSample : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private string _vrmFilePath;
|
||||
private GameObject _vrmGameObject;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
_vrmGameObject = LoadVrm(_vrmFilePath);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
DestroyVrm(_vrmGameObject);
|
||||
}
|
||||
|
||||
private GameObject LoadVrm(string vrmFilePath)
|
||||
{
|
||||
// 1. GltfParser を呼び出します。
|
||||
// GltfParser はファイルから JSON 情報とバイナリデータを読み出します。
|
||||
var parser = new GltfParser();
|
||||
parser.ParsePath(vrmFilePath);
|
||||
|
||||
// 2. GltfParser のインスタンスを引数にして VRMImporterContext を作成します。
|
||||
// VRMImporterContext は VRM のロードを実際に行うクラスです。
|
||||
using (var context = new VRMImporterContext(parser))
|
||||
{
|
||||
// 3. Load 関数を呼び出し、VRM の GameObject を生成します。
|
||||
context.Load();
|
||||
|
||||
// 4. (任意) SkinnedMeshRenderer の UpdateWhenOffscreen を有効にできる便利関数です。
|
||||
// https://docs.unity3d.com/2019.4/Documentation/ScriptReference/SkinnedMeshRenderer-updateWhenOffscreen.html
|
||||
context.EnableUpdateWhenOffscreen();
|
||||
|
||||
// 5. VRM モデルを表示します。
|
||||
context.ShowMeshes();
|
||||
|
||||
// 6. VRM の GameObject が実際に使用している UnityEngine.Object リソースの寿命を VRM の GameObject に紐付けます。
|
||||
// つまり VRM の GameObject の破棄時に、実際に使用しているリソース (Texture, Material, Mesh, etc) をまとめて破棄することができます。
|
||||
context.DisposeOnGameObjectDestroyed();
|
||||
|
||||
// 7. Root の GameObject を return します。
|
||||
// Root の GameObject とは VRMMeta コンポーネントが付与されている GameObject のことです。
|
||||
return context.Root;
|
||||
}
|
||||
// 8. using スコープを抜けて context が破棄されると、 VRMImporterContext が保持する UnityEngine.Object リソースが破棄されます。
|
||||
// このとき破棄されるリソースは、 glTF ファイルには含まれているが VRM の GameObject には割り当てられていないテクスチャなどです。
|
||||
// 手順 6. で VRM の GameObject に紐付けたリソースは、ここでは破棄されません。
|
||||
}
|
||||
|
||||
private void DestroyVrm(GameObject vrmGameObject)
|
||||
{
|
||||
// 9. 生成された VRM の GameObject を破棄します。
|
||||
// GameObject を破棄すれば、紐づくリソース (Texture, Material, Mesh, etc) も破棄されます。
|
||||
UnityEngine.Object.Destroy(vrmGameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## サンプルコード(非同期ロード)
|
||||
|
||||
```csharp
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using UniGLTF;
|
||||
using UnityEngine;
|
||||
using VRM;
|
||||
|
||||
namespace YourNameSpace
|
||||
{
|
||||
public sealed class LoadVrmAsyncSample : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private string _vrmFilePath;
|
||||
private GameObject _vrmGameObject;
|
||||
|
||||
private async void Start()
|
||||
{
|
||||
// 簡便のため、このサンプルではキャンセル処理などは考慮しません。
|
||||
_vrmGameObject = await LoadVrmAsync(_vrmFilePath);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
DestroyVrm(_vrmGameObject);
|
||||
}
|
||||
|
||||
private async Task<GameObject> LoadVrmAsync(string vrmFilePath)
|
||||
{
|
||||
// 1. GltfParser を呼び出します。
|
||||
// GltfParser はファイルから JSON 情報とバイナリデータを読み出します。
|
||||
// GltfParser は Unity のメインスレッド以外で実行できます。
|
||||
var parser = new GltfParser();
|
||||
await Task.Run(() =>
|
||||
{
|
||||
var file = File.ReadAllBytes(vrmFilePath);
|
||||
parser.ParseGlb(file);
|
||||
});
|
||||
|
||||
// 2. GltfParser のインスタンスを引数にして VRMImporterContext を作成します。
|
||||
// VRMImporterContext は VRM のロードを実際に行うクラスです。
|
||||
using (var context = new VRMImporterContext(parser))
|
||||
{
|
||||
// 3. Load 関数を呼び出し、VRM の GameObject を生成します。
|
||||
// Load 処理は数フレームの時間を要します。
|
||||
await context.LoadAsync();
|
||||
|
||||
// 4. (任意) SkinnedMeshRenderer の UpdateWhenOffscreen を有効にできる便利関数です。
|
||||
// https://docs.unity3d.com/2019.4/Documentation/ScriptReference/SkinnedMeshRenderer-updateWhenOffscreen.html
|
||||
context.EnableUpdateWhenOffscreen();
|
||||
|
||||
// 5. VRM モデルを表示します。
|
||||
context.ShowMeshes();
|
||||
|
||||
// 6. VRM の GameObject が実際に使用している UnityEngine.Object リソースの寿命を VRM の GameObject に紐付けます。
|
||||
// つまり VRM の GameObject の破棄時に、実際に使用しているリソース (Texture, Material, Mesh, etc) をまとめて破棄することができます。
|
||||
context.DisposeOnGameObjectDestroyed();
|
||||
|
||||
// 7. Root の GameObject を return します。
|
||||
// Root の GameObject とは VRMMeta コンポーネントが付与されている GameObject のことです。
|
||||
return context.Root;
|
||||
}
|
||||
// 8. using スコープを抜けて context が破棄されると、 VRMImporterContext が保持する UnityEngine.Object リソースが破棄されます。
|
||||
// このとき破棄されるリソースは、 glTF ファイルには含まれているが VRM の GameObject には割り当てられていないテクスチャなどです。
|
||||
// 手順 6. で VRM の GameObject に紐付けたリソースは、ここでは破棄されません。
|
||||
}
|
||||
|
||||
private void DestroyVrm(GameObject vrmGameObject)
|
||||
{
|
||||
// 9. 生成された VRM の GameObject を破棄します。
|
||||
// GameObject を破棄すれば、紐づくリソース (Texture, Material, Mesh, etc) も破棄されます。
|
||||
UnityEngine.Object.Destroy(vrmGameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
83
docs/api/0_77_runtime_import.md
Normal file
83
docs/api/0_77_runtime_import.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# `v0.77` RuntimeImport RuntimeGltfInstance
|
||||
|
||||
[DisposeOnGameObjectDestroyed](https://github.com/vrm-c/UniVRM/issues/1018)
|
||||
|
||||
`ImporterContext` の仕様を変更しました。
|
||||
|
||||
`Version 0.68` で導入した、 `ImporterContext.DisposeOnGameObjectDestroyed` が扱いづらかったためのでこれを取りやめ、
|
||||
`ImporterContext.Load` が `RuntimeGltfInstance` を返すようにしました。
|
||||
|
||||
`RuntimeGltfInstance` は、 `ImporterContext` の
|
||||
|
||||
* Root
|
||||
* EnableUpdateWhenOffscreen()
|
||||
* ShowMeshes()
|
||||
|
||||
を引き継ぎます。
|
||||
Load の呼び出し後の任意のタイミングで ImporterContext.Dispose で Importer を破棄してください。
|
||||
任意のタイミングで RuntimeGltfInstance を Destory することで紐づくリソース (Texture, Material, Mesh, etc) も破棄されます。
|
||||
|
||||
```csharp
|
||||
using UniGLTF;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
namespace VRM.Samples
|
||||
{
|
||||
public sealed class LoadVrmSample : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private string _vrmFilePath;
|
||||
private GameObject _vrmGameObject;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
_vrmGameObject = LoadVrm(_vrmFilePath);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
DestroyVrm(_vrmGameObject);
|
||||
}
|
||||
|
||||
private GameObject LoadVrm(string vrmFilePath)
|
||||
{
|
||||
// 1. GltfParser を呼び出します。
|
||||
// GltfParser はファイルから JSON 情報とバイナリデータを読み出します。
|
||||
var parser = new GltfParser();
|
||||
parser.ParsePath(vrmFilePath);
|
||||
|
||||
// 2. GltfParser のインスタンスを引数にして VRMImporterContext を作成します。
|
||||
// VRMImporterContext は VRM のロードを実際に行うクラスです。
|
||||
using (var context = new VRMImporterContext(parser))
|
||||
{
|
||||
// 3. Load 関数を呼び出し、VRM の GameObject を生成します。
|
||||
RuntimeGltfInstance instance = context.Load(); // <- `v0.77` でここが変わります。
|
||||
|
||||
// 非同期版 async 関数の中で下記のようにしてください
|
||||
// RuntimeGltfInstance instance = await context.LoadAsync();
|
||||
|
||||
// 4. (任意) SkinnedMeshRenderer の UpdateWhenOffscreen を有効にできる便利関数です。
|
||||
// https://docs.unity3d.com/2019.4/Documentation/ScriptReference/SkinnedMeshRenderer-updateWhenOffscreen.html
|
||||
instance.EnableUpdateWhenOffscreen(); // <- ImporterContext から RuntimeGltfInstance に移動しました。
|
||||
|
||||
// 5. VRM モデルを表示します。
|
||||
instance.ShowMeshes(); // <- ImporterContext から RuntimeGltfInstance に移動しました。
|
||||
|
||||
// 6. Root の GameObject を return します。
|
||||
// Root の GameObject とは VRMMeta コンポーネントが付与されている GameObject のことです。
|
||||
return instance.Root; // <- ImporterContext から RuntimeGltfInstance に移動しました。
|
||||
}
|
||||
// 7. using スコープを抜けて context が破棄されると、 VRMImporterContext が保持する UnityEngine.Object リソースが破棄されます。
|
||||
// このとき破棄されるリソースは、 glTF ファイルには含まれているが VRM の GameObject には割り当てられていないテクスチャなどです。
|
||||
// 手順 6. で VRM の GameObject に紐付けたリソースは、ここでは破棄されません。
|
||||
}
|
||||
|
||||
private void DestroyVrm(GameObject vrmGameObject)
|
||||
{
|
||||
// 8. 生成された VRM の GameObject を破棄します。
|
||||
// GameObject を破棄すれば、紐づくリソース (Texture, Material, Mesh, etc) も破棄されます。
|
||||
UnityEngine.Object.Destroy(vrmGameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
12
docs/api/0_79_runtime_import.md
Normal file
12
docs/api/0_79_runtime_import.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# `v0.79` RuntimeImport GltfData
|
||||
|
||||
`GltfParser` と `GltfData` の分割
|
||||
|
||||
```csharp
|
||||
var parser = new GltfParser();
|
||||
parser.ParsePath(path);
|
||||
```
|
||||
|
||||
```csharp
|
||||
GltfData data = new GlbFileParser(path).Parse();
|
||||
```
|
||||
113
docs/api/0_82_glb_import.md
Normal file
113
docs/api/0_82_glb_import.md
Normal file
@@ -0,0 +1,113 @@
|
||||
# `v0.82.1` GlbImport GltfData
|
||||
|
||||
以下のステップでロードします。
|
||||
|
||||
1. `GLB` / `GLTF` をパースして `GltfData` を得る。
|
||||
2. `GltfData` から `Unity Hierarchy` を ロード する。`RuntimeGltfInstance` を得る。 ローダーを破棄する。
|
||||
3. ロードした `RuntimeGltfInstance` 使う。`RuntimeGltfInstance` を破棄する。
|
||||
|
||||
## 1. パースする
|
||||
|
||||
### glb ファイルパスからパースする
|
||||
|
||||
* `vrm` もこの関数を使います。
|
||||
|
||||
```csharp
|
||||
GltfData Load(string path)
|
||||
{
|
||||
return new GlbFileParser(path).Parse();
|
||||
}
|
||||
```
|
||||
|
||||
### glb バイト列をパースする
|
||||
|
||||
* `vrm` もこの関数を使います。
|
||||
|
||||
```csharp
|
||||
GltfData Load(byte[] bytes)
|
||||
{
|
||||
return new GlbBinaryParser(bytes, "LOAD_NAME").parse();
|
||||
}
|
||||
```
|
||||
|
||||
### gltf ファイルパスからパースする
|
||||
|
||||
```csharp
|
||||
GltfData Load(string path)
|
||||
{
|
||||
return new GltfFileWithResourceFilesParser(path).Parse();
|
||||
}
|
||||
```
|
||||
|
||||
### zip アーカイブからパースする
|
||||
|
||||
gltf と関連するファイルを zip アーカイブしたファイルをパースできます(実験)。
|
||||
|
||||
```csharp
|
||||
GltfData Load(string path)
|
||||
{
|
||||
return new ZipArchivedGltfFileParser(path).Parse();
|
||||
}
|
||||
```
|
||||
|
||||
### ファイルパスの拡張子でパースする
|
||||
|
||||
サンプルの `SimpleViewer` を参考にしてください。
|
||||
|
||||
```csharp
|
||||
GltfData Load(string path)
|
||||
{
|
||||
// ファイル拡張子で自動判定します
|
||||
return new AutoGltfFileParser(path).Parse();
|
||||
}
|
||||
```
|
||||
|
||||
## 2. ロードする
|
||||
|
||||
### sync
|
||||
|
||||
```csharp
|
||||
RuntimeGltfInstance Load(GltfData data)
|
||||
{
|
||||
// ImporterContext は使用後に Dispose を呼び出してください。
|
||||
// using で自動的に呼び出すことができます。
|
||||
using(var loader = new UniGLTF.ImporterContext(data)
|
||||
{
|
||||
var instance = loader.Load();
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### async
|
||||
|
||||
```csharp
|
||||
async RuntimeGltfInstance Load(GltfData data)
|
||||
{
|
||||
// ImporterContext は使用後に Dispose を呼び出してください。
|
||||
// using で自動的に呼び出すことができます。
|
||||
using(var loader = new UniGLTF.ImporterContext(data)
|
||||
{
|
||||
var instance = await loader.LoadAsync();
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### materialGenerator で URP 用のマテリアルをロードする
|
||||
|
||||
{doc}`Import 時に生成される Material をカスタマイズする </gltf/how_to_customize_material_import>`
|
||||
|
||||
## 3. インスタンスを使用する
|
||||
|
||||
```csharp
|
||||
// SkinnedMeshRenderer に対する指示
|
||||
instance.EnableUpdateWhenOffscreen();
|
||||
// 準備ができたら表示する(デフォルトでは非表示)
|
||||
instance.ShowMeshes();
|
||||
```
|
||||
|
||||
使用後に以下のように破棄してください。関連する Asset(Texture, Material, Meshなど)も破棄されます。
|
||||
```csharp
|
||||
GameObject.Destroy(instance);
|
||||
```
|
||||
77
docs/api/0_82_runtime_import.md
Normal file
77
docs/api/0_82_runtime_import.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# `v0.82.1` RuntimeImport VRMData
|
||||
|
||||
以下の手順で import します。
|
||||
|
||||
1. VRMをパースして、`GltfData` を得る。
|
||||
1. `GltfData` から `VRMData` を得る。
|
||||
1. `VrmData` から `RuntimeGltfInstance` をロードする。
|
||||
1. `RuntimeGltfInstance` を使う。
|
||||
|
||||
サンプルの `Assets\VRM\Samples\SimpleViewer\ViewerUI.cs` も参照してください。
|
||||
|
||||
## 1. `GltfData` を得る
|
||||
|
||||
```csharp
|
||||
GltfData Load(string path)
|
||||
{
|
||||
return new GlbFileParser(path).Parse();
|
||||
}
|
||||
```
|
||||
|
||||
[GLB import](../gltf/0_82_glb_import.md) も参照してください。
|
||||
|
||||
## 2. `VRMData` を得る
|
||||
|
||||
```csharp
|
||||
VRMData vrm = new VRMData(data);
|
||||
```
|
||||
|
||||
## 3. Load する
|
||||
|
||||
```csharp
|
||||
async RuntimeGltfInstance Load(VRMData vrm)
|
||||
{
|
||||
// 使用後に Dispose で VRMImporterContext を破棄してください。
|
||||
using(var loader = new VRMImporterContext(vrm))
|
||||
{
|
||||
var instance = await loader.LoadAsync();
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### URP 向けに `materialGenerator` を指定する(実験)
|
||||
|
||||
`materialGenerator` 引き数(省略可能)を指定することで URP マテリアルを生成するようにカスタムできます。
|
||||
指定しない場合は `built-in` 向けのデフォルトが使用されます。
|
||||
|
||||
```csharp
|
||||
async RuntimeGltfInstance Load(VRMData vrm)
|
||||
{
|
||||
var materialGenerator = new VRMUrpMaterialDescriptorGenerator(vrm.VrmExtension);
|
||||
using(var loader = new VRM.VRMImporterContext(vrm, materialGenerator: materialGenerator))
|
||||
{
|
||||
var instance = await loader.LoadAsync();
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
* まだ URP 向け MToonShader が作成されていないので、`UniUnlit` にフォールバックします。
|
||||
|
||||
## 4. Instance
|
||||
|
||||
```csharp
|
||||
// SkinnedMeshRenderer に対する指示
|
||||
instance.EnableUpdateWhenOffscreen();
|
||||
// 準備ができたら表示する(デフォルトでは非表示)
|
||||
instance.ShowMeshes();
|
||||
```
|
||||
|
||||
使用後に以下のように破棄してください。関連する Asset(Texture, Material, Meshなど)も破棄されます。
|
||||
```csharp
|
||||
// GameObject.Destroy(instance);
|
||||
|
||||
// RuntimeGltfInstance ではなくて、その GameObject を Destroy します。
|
||||
GameObject.Destroy(instance.gameObject);
|
||||
```
|
||||
39
docs/api/0_87_runtime_import.md
Normal file
39
docs/api/0_87_runtime_import.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# `v0.87` RuntimeImport 非同期ロード
|
||||
|
||||
## awaitCaller 引き数
|
||||
|
||||
```csharp
|
||||
public virtual async Task<RuntimeGltfInstance> LoadAsync(IAwaitCaller awaitCaller = null, ...)
|
||||
```
|
||||
|
||||
awaitCaller によりロード時の挙動をカスタマイズできます。
|
||||
|
||||
```{literalinclude} ../../Assets/VRMShaders/GLTF/IO/Runtime/AwaitCaller/IAwaitCaller.cs
|
||||
:language: csharp
|
||||
:linenos:
|
||||
```
|
||||
|
||||
* NextFrame: 処理を中断して次のフレームで再開します。ロード処理が長い場合に長時間アプリケーションが固まることを防ぎます
|
||||
* Run: UnityEngine.Object にアクセスしない処理を別スレッドで実行します。タスクが終了したら await で Unity Script スレッドで続きを実行します。
|
||||
|
||||
## ImmediateCaller
|
||||
|
||||
デフォルトでは、`ImmediateCaller` が使われます。
|
||||
`ImmediateCaller` タスクを即時に実行するので、同期実行となります。
|
||||
|
||||
* Play(Editor Play, build), Editor(not play), UnitTest でデッドロックしないための実装です。
|
||||
|
||||
```{literalinclude} ../../Assets/VRMShaders/GLTF/IO/Runtime/AwaitCaller/ImmediateCaller.cs
|
||||
:language: csharp
|
||||
:linenos:
|
||||
```
|
||||
|
||||
## RuntimeOnlyAwaitCaller
|
||||
|
||||
* Play(Editor Play, build) 時に非同期実行する実装です
|
||||
* `VRM10_Samples/VRM10Viewer` に使用例があります。
|
||||
|
||||
```{literalinclude} ../../Assets/VRMShaders/GLTF/IO/Runtime/AwaitCaller/RuntimeOnlyAwaitCaller.cs
|
||||
:language: csharp
|
||||
:linenos:
|
||||
```
|
||||
40
docs/api/0_95_dispose.md
Normal file
40
docs/api/0_95_dispose.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# `v0.95` GltfData.Dispose
|
||||
|
||||
Importer の内部で `NativeArray` を使うようにしたため、
|
||||
終了時にこれを破棄する必要ができました。
|
||||
使い終わったら `Dispose` してください。
|
||||
|
||||
```cs
|
||||
class GltfData: IDisposable
|
||||
```
|
||||
|
||||
## 使用例
|
||||
|
||||
```cs
|
||||
// must dispose GltfData
|
||||
using (GltfData data = new AutoGltfFileParser(path).Parse())
|
||||
using (var loader = new UniGLTF.ImporterContext(data, materialGenerator: materialGenerator))
|
||||
{
|
||||
return await loader.LoadAsync(awaitCaller);
|
||||
}
|
||||
```
|
||||
|
||||
## Dispose しなかった場合
|
||||
|
||||
NativeArray が Dispose されずに GC に回収されたタイミングで、
|
||||
以下のエラーメッセージがコンソールに表示されます。
|
||||
|
||||
`A Native Collection has not been disposed`
|
||||
|
||||
このエラーがどこで起きたか分からない場合があります。
|
||||
`com.unity.jobs package` により詳細メッセージを得ることができます。
|
||||
|
||||
<https://forum.unity.com/threads/a-native-collection-has-not-been-disposed-enable-full-stack.1098973/>
|
||||
|
||||
を参考にしてください。
|
||||
|
||||
|
||||
## 関連
|
||||
|
||||
* <https://github.com/vrm-c/UniVRM/pull/1483>
|
||||
* <https://github.com/vrm-c/UniVRM/pull/1503>
|
||||
3
docs/api/0_95_highlevel.md
Normal file
3
docs/api/0_95_highlevel.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# `v0.95` 簡単API
|
||||
|
||||
API が複雑化してきたので、よくある用途を簡単にできる高レベル API を追加しました。
|
||||
47
docs/api/fast_spring_bone.md
Normal file
47
docs/api/fast_spring_bone.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# `v0.85` FastSpringBoneについて
|
||||
|
||||
## 概要
|
||||
UniVRMでは、DOTSを利用した高速なSpringBone実装である「FastSpringBone」を用意しています。
|
||||
|
||||
揺れ物の各房を並列処理・最適化することで1フレームあたりの処理時間を大幅に抑えます。
|
||||
|
||||
VRM0.x と VRM1.0 で、それぞれFastSpringBoneの立ち位置・実装が異なります。
|
||||
|
||||
## VRM 1.0 での FastSpringBone の概要
|
||||
VRM1.0ではFastSpringBoneが揺れものの標準実装です。
|
||||
|
||||
VRMのライフサイクルに紐付いて自動的に生成・破棄されます。
|
||||
|
||||
## VRM 0.x での FastSpringBone の概要
|
||||
VRM0.xでは後方互換性を保つため、デフォルトでは従来のDOTS実装でないSpringBoneで動作します。
|
||||
|
||||
VRM0.x向けのFastSpringBone実装は `VRM/Runtime/FastSpringBone` ディレクトリにあります。
|
||||
|
||||
## VRM 0.x での FastSpringBone の導入方法
|
||||
差し替えを行いたいタイミングで `FastSpringBoneReplacer.ReplaceAsync` を呼び出してください
|
||||
|
||||
これを明示的に呼ばなければ、従来のSpringBoneのまま動作します。
|
||||
|
||||
```csharp
|
||||
using (var loader = new UniGLTF.ImporterContext(data))
|
||||
{
|
||||
var instance = await loader.LoadAsync();
|
||||
SetModel(instance);
|
||||
}
|
||||
FastSpringBoneReplacer.ReplaceAsync(instance.Root)
|
||||
|
||||
instance.EnableUpdateWhenOffscreen();
|
||||
instance.ShowMeshes();
|
||||
```
|
||||
|
||||
## Burstの導入について
|
||||
UniVRM に加えて Burst を別途導入すると、 FastSpringBone が Burst によって高速化されます。
|
||||
|
||||
Burst の導入方法は [こちら](https://docs.unity3d.com/ja/2019.4/Manual/upm-ui-install.html) をご参照ください。
|
||||
|
||||
## FastSpringBoneServiceについて
|
||||
FastSpringBone が実行されると、`FastSpringBone Service` GameObject が `DontDestroyOnLoad` で生成されます。
|
||||
|
||||
これは全 VRM の FastSpringBone を集め、バッファの構築や、 FastSpringBone の実行タイミングの制御などを行う GameObject です。
|
||||
|
||||
明示的に破棄を行いたい場合は `FastSpringBoneService.Free` を呼んでください。
|
||||
73
docs/api/firstperson.md
Normal file
73
docs/api/firstperson.md
Normal file
@@ -0,0 +1,73 @@
|
||||
# VRMFirstPersonの使い方
|
||||
|
||||
{doc}`FirstPerson と Renderer の可視制御 </implementation/first_person>`
|
||||
|
||||
実行時に**VRMFirstPerson.Setup**を呼び出すことで、FirstPerson設定に応じた Renderer の可視制御を行うことができます。
|
||||
明示的に外部から呼び出してください。
|
||||
|
||||
## アプリケーションに追加の描画レイヤーを指定する
|
||||
|
||||
定数で以下のレイヤーを定義しています。
|
||||
|
||||
```csharp
|
||||
public class VRMFirstPerson : MonoBehaviour
|
||||
{
|
||||
public const int FIRSTPERSON_ONLY_LAYER = 9;
|
||||
public const int THIRDPERSON_ONLY_LAYER = 10;
|
||||
|
||||
// 省略
|
||||
}
|
||||
```
|
||||
|
||||
|{{< img src="images/vrm/layer_setting.png" >}}|
|
||||
|-----|
|
||||
|9番と10番にLayerを設定|
|
||||
|
||||
## 実行時にSetupを呼び出して、カメラにLayerMaskを設定する
|
||||
|
||||
* VRMFirstPerson.Setupの呼び出し
|
||||
* 一人称カメラとその他のカメラに対してLayerMask
|
||||
|
||||
```csharp
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using VRM;
|
||||
|
||||
public class SetupExample : MonoBehaviour
|
||||
{
|
||||
[SerializeField]
|
||||
Camera m_firstPersonCamera; // HMDのカメラ
|
||||
|
||||
[SerializeField]
|
||||
LayerMask m_firstPersonMask; // HMDのカメラにセットするマスク default | VRMFirstPersonOnly など
|
||||
|
||||
[SerializeField]
|
||||
LayerMask m_otherMask; // HMDのカメラにセットするマスク default | VRMThirdPersonOnly など
|
||||
|
||||
[SerializeField]
|
||||
VRMFirstPerson m_firstPerson;
|
||||
|
||||
void Reset()
|
||||
{
|
||||
m_firstPerson = GameObject.FindObjectOfType<VRMFirstPerson>();
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
foreach (var camera in GameObject.FindObjectsOfType<Camera>())
|
||||
{
|
||||
camera.cullingMask = (camera == m_firstPersonCamera)
|
||||
? m_firstPersonMask
|
||||
: m_otherMask
|
||||
;
|
||||
}
|
||||
|
||||
// VRMFirstPersonの初期化
|
||||
if (m_firstPerson != null)
|
||||
{
|
||||
m_firstPerson.Setup();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
25
docs/api/how_to_customize_material_import.md
Normal file
25
docs/api/how_to_customize_material_import.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# `v0.82` Import 時に生成される Material をカスタマイズする
|
||||
|
||||
`IMaterialDescriptorGenerator` を実装することで import 時に適用されるマテリアルを差し替えることができます。
|
||||
|
||||
## materialGenerator で URP 用のマテリアルをロードする
|
||||
|
||||
URP マテリアルを生成するようにカスタムする例です。
|
||||
|
||||
<https://github.com/vrm-c/UniVRM/issues/1214>
|
||||
|
||||
```csharp
|
||||
async RuntimeGltfInstance Load(GltfData data)
|
||||
{
|
||||
IMaterialDescriptorGenerator materialGenerator = new GltfUrpMaterialDescriptorGenerator();
|
||||
using(var loader = new UniGLTF.ImporterContext(data, materialGenerator: materialGenerator)
|
||||
{
|
||||
var instance = await loader.LoadAsync();
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 関連
|
||||
|
||||
* <https://github.com/vrm-c/UniVRM/pull/1177>
|
||||
441
docs/api/how_to_impl_extension.md
Normal file
441
docs/api/how_to_impl_extension.md
Normal file
@@ -0,0 +1,441 @@
|
||||
# `v0.63.2` glTF拡張の実装
|
||||
|
||||
`UniVRM-0.63.2` から `UniGLTF` の構成が変わって、 `extensions` / `extras` の実装方法が変わりました。
|
||||
|
||||
## GLTF 拡張とは
|
||||
|
||||
`glTF` は各所に `extensions`, `extras` が定義してありその中身を拡張できます。
|
||||
|
||||
* `extensions` (またはextras)
|
||||
* `asset.extensions` (またはextras)
|
||||
* `meshes[*].extensions` (またはextras)
|
||||
* `materials[*].extensions` (またはextras)
|
||||
|
||||
など。
|
||||
|
||||
`extensions` はオフィシャルに仕様を策定して `JsonSchema` として公開します。
|
||||
|
||||
* https://github.com/KhronosGroup/glTF/tree/master/extensions
|
||||
|
||||
`extensions` は、`{ベンダー名}_{拡張名}` という命名規則です。
|
||||
ベンダー名は、 https://github.com/KhronosGroup/glTF に申し込んで登録できます。
|
||||
|
||||
`extras` は登録せずにアプリケーション独自に拡張する場合に用います。仕組みは同じです。
|
||||
|
||||
> This enables glTF models to contain application-specific properties without creating a full glTF extension
|
||||
|
||||
## UniGLTF の extensions
|
||||
|
||||
`v0.63.0` 以前は、`GLTF 型` の `extensions` フィールドに、`GLTFExtensions` 型を定義して、`VRM` フィールドを定義するという方法をとっていました。
|
||||
|
||||
```csharp
|
||||
class VRM
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
class GLTFExtensions
|
||||
{
|
||||
public VRM VRM;
|
||||
}
|
||||
|
||||
class GLTF
|
||||
{
|
||||
// すべての拡張の型をコンパイル時に知っている必要がある。動的に拡張できない
|
||||
public GLTFExtensions extensions;
|
||||
}
|
||||
```
|
||||
|
||||
この設計だと GLTF と拡張を別ライブラリとして分離することができませんでした。
|
||||
|
||||
`v0.63.1` から設計を変更して、すべての `extensions/extras` に同じ型の入れ物を使うように変更しました。
|
||||
UniGLTF は `import/export` の具体的な内容を知らずに中間データの入れ物として扱います。
|
||||
|
||||
```csharp
|
||||
// extensions / extras の入れ物として使う型
|
||||
// 実行時は、 glTFExtensionImport / glTFExtensionExport を使う
|
||||
public abstract class glTFExtension
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
class GLTF
|
||||
{
|
||||
// UniGLTFは具体的な型を知らない。利用側が処理(serialize/deserialize)する
|
||||
public glTFExtension extensions;
|
||||
}
|
||||
```
|
||||
|
||||
## UniGLTF の拡張の書き方
|
||||
|
||||
拡張は、以下の部品要素から作れます。
|
||||
|
||||
* 名前(JsonPath)。例: `extensions.VRM`, `materials[*].extensions.KHR_materials_unlit`
|
||||
* 拡張の型。`T型`
|
||||
* デシリアライザー(import)。 `jsonバイト列 => T型`
|
||||
* シリアライザーexport)。`T型 => jsonバイト列`
|
||||
|
||||
### JSONPATH と 型を決める
|
||||
|
||||
```C#
|
||||
// 型
|
||||
class GoodMaterial
|
||||
{
|
||||
// `materials[*].extensions.CUSTOM_materials_good`
|
||||
public const string EXTENSION_NAME = "CUSTOM_materials_good";
|
||||
|
||||
public int GoodValue;
|
||||
}
|
||||
```
|
||||
|
||||
### import
|
||||
|
||||
```C#
|
||||
GoodMaterial DeserializeGoodMaterial(ListTreeNode<JsonValue> json)
|
||||
{
|
||||
// デシリアライズ。手で書くかコード生成する(後述)
|
||||
}
|
||||
|
||||
// ユーティリティ関数例
|
||||
bool TryGetExtension<T>(UniGLTF.glTFExtension extension, string key, Func<ListTreeNode<JsonValue>, T> deserializer, out T value)
|
||||
{
|
||||
if(material.extensions is UniGLTF.glTFExtensionsImport import)
|
||||
{
|
||||
// null check 完了
|
||||
foreach(var kv in import.ObjectItems())
|
||||
{
|
||||
if(kv.key.GetString()==key)
|
||||
{
|
||||
value = Deserialize(kv.Value);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
void ImportMaterial(UniGLTF.glTFMaterial material)
|
||||
{
|
||||
// material の処理に割り込んで
|
||||
if(TryGetExtension(material.extension, GoodMaterial.EXTENSION_NAME, DeserializeGoodMaterial, out GoodMaterial good))
|
||||
{
|
||||
// good material 独自の処理
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### export
|
||||
|
||||
```csharp
|
||||
void SerializeGoodMaterial(UniJSON.JsonFormatter f, GoodMaterial value)
|
||||
{
|
||||
// シリアライズ。手で書くかコード生成する(後述)
|
||||
}
|
||||
|
||||
// ユーティリティ関数例
|
||||
public ArraySegment<byte> SerializeExtension<T>(T value, Func<T, ArraySegment<byte>> serialize)
|
||||
{
|
||||
var f = new UniJSON.JsonFormatter();
|
||||
serialize(f, value);
|
||||
return f.GetStoreBytes();
|
||||
}
|
||||
|
||||
void ExportGoodMaterial(UniGLTF.glTFMaterial material, GoodMaterial good)
|
||||
{
|
||||
// material の処理に割り込んで
|
||||
if(!(material.extensions is UniGLTF.glTFExtensionsExport export))
|
||||
{
|
||||
// 無かった。新規作成
|
||||
export = new UniGLTF.glTFExtensionsExport();
|
||||
material.extensions = export;
|
||||
}
|
||||
|
||||
var bytes = SerializeExtension(good, SerializeGoodMaterial);
|
||||
export.Add(GoodMaterial.EXTENSION_NAME, bytes);
|
||||
}
|
||||
```
|
||||
|
||||
## 実装例
|
||||
|
||||
### GLTF: GLTF全体
|
||||
`C#の型からコード生成`
|
||||
|
||||
* `Assets\UniGLTF\Runtime\UniGLTF\Format\GltfSerializer.g.cs`
|
||||
* `Assets\UniGLTF\Runtime\UniGLTF\Format\GltfDeserializer.g.cs`
|
||||
|
||||
ジェネレーターの呼び出しコード
|
||||
|
||||
* `Assets\UniGLTF\Editor\UniGLTF\Serialization\SerializerGenerator.cs`
|
||||
* `Assets\UniGLTF\Editor\UniGLTF\Serialization\DeserializerGenerator.cs`
|
||||
|
||||
生成コードの呼び出し
|
||||
|
||||
### GLTF: `meshes[*].extras.targetNames`
|
||||
`コード生成せずに手書き`
|
||||
|
||||
* `Assets\UniGLTF\Runtime\UniGLTF\Format\ExtensionsAndExtras\gltf_mesh_extras_targetNames.cs`
|
||||
|
||||
生成コードの呼び出し
|
||||
|
||||
### GLTF: `materials[*].extensions.KHR_materials_unlit`
|
||||
`コード生成せずに手書き`
|
||||
|
||||
* `Assets\UniGLTF\Runtime\UniGLTF\Format\ExtensionsAndExtras\KHR_materials_unlit.cs`
|
||||
|
||||
生成コードの呼び出し
|
||||
|
||||
### GLTF: `materials[*].extensions.KHR_texture_transform`
|
||||
`コード生成せずに手書き`
|
||||
|
||||
* `Assets\UniGLTF\Runtime\UniGLTF\Format\ExtensionsAndExtras\KHR_texture_transform.cs`
|
||||
|
||||
生成コードの呼び出し
|
||||
|
||||
* https://github.com/vrm-c/UniVRM/blob/master/Assets/UniGLTF/Runtime/UniGLTF/IO/MaterialImporter.cs#L296
|
||||
* https://github.com/vrm-c/UniVRM/blob/master/Assets/UniGLTF/Runtime/UniGLTF/IO/MaterialExporter.cs#L193
|
||||
|
||||
### VRM0: `extensions.VRM`
|
||||
`C#の型からコード生成`
|
||||
|
||||
* `Assets\VRM\Runtime\Format\VRMSerializer.g.cs`
|
||||
* `Assets\VRM\Runtime\Format\VRMDeserializer.g.cs`
|
||||
|
||||
ジェネレーターの呼び出しコード
|
||||
|
||||
* `Assets\VRM\Editor\VRMSerializerGenerator.cs`
|
||||
* `Assets\VRM\Editor\VRMDeserializerGenerator.cs`
|
||||
|
||||
生成コードの呼び出し
|
||||
|
||||
* https://github.com/vrm-c/UniVRM/blob/master/Assets/VRM/Runtime/IO/VRMImporterContext.cs#L41
|
||||
* https://github.com/vrm-c/UniVRM/blob/master/Assets/VRM/Runtime/IO/VRMExporter.cs#L209
|
||||
|
||||
### VRM1: `extensions.VRMC_vrm` など
|
||||
`JsonSchemaからコード生成`
|
||||
|
||||
5つの Extensions に分かれたので個別に作成。
|
||||
ささる場所(JsonPath)が違うのに注意。
|
||||
|
||||
#### `extensions.VRMC_vrm`
|
||||
* `Assets\VRM10\Runtime\Format\VRM`
|
||||
|
||||
#### `materials[*].extensions.VRMC_materials_mtoon`
|
||||
* `Assets\VRM10\Runtime\Format\MaterialsMToon`
|
||||
|
||||
#### `nodes[*].extensions.VRMC_node_collider`
|
||||
* `Assets\VRM10\Runtime\Format\NodeCollider`
|
||||
|
||||
#### `extensions.VRMC_springBone`
|
||||
* `Assets\VRM10\Runtime\Format\SpringBone`
|
||||
|
||||
#### `extensions.VRMC_vrm_constraints`
|
||||
* `Assets\VRM10\Runtime\Format\Constraints`
|
||||
|
||||
#### ジェネレーターの呼び出しコード
|
||||
* `Assets\VRM10\Editor\GeneratorMenu.cs`
|
||||
|
||||
#### 生成コードの呼び出し
|
||||
|
||||
## コード生成
|
||||
JSON と C# の型との シリアライズ/デシリアライズは定型コードになるので、ジェネレーターがあります。
|
||||
C# の型から生成するものと、JsonSchema から C# の型とともに生成するものがあります。
|
||||
|
||||
### C# の型から生成
|
||||
|
||||
#### シリアライザー
|
||||
|
||||
ジェネレーターを呼び出すコードを作成します。
|
||||
|
||||
* 元になる型
|
||||
* 出力先
|
||||
|
||||
の2つを決めます。static関数を生成するので、namespace と static class で囲ってあげます。
|
||||
|
||||
例
|
||||
|
||||
* `Assets\UniGLTF\Editor\UniGLTF\Serialization\SerializerGenerator.cs`
|
||||
|
||||
```csharp
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using UniJSON;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
public static class SerializerGenerator
|
||||
{
|
||||
const BindingFlags FIELD_FLAGS = BindingFlags.Instance | BindingFlags.Public;
|
||||
|
||||
const string Begin = @"// Don't edit manually. This is generaged.
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UniJSON;
|
||||
|
||||
namespace UniGLTF {
|
||||
|
||||
static public class GltfSerializer
|
||||
{
|
||||
|
||||
";
|
||||
|
||||
const string End = @"
|
||||
} // class
|
||||
} // namespace
|
||||
";
|
||||
|
||||
static string OutPath
|
||||
{
|
||||
get
|
||||
{
|
||||
return Path.Combine(UnityEngine.Application.dataPath,
|
||||
"UniGLTF/UniGLTF/Scripts/IO/GltfSerializer.g.cs");
|
||||
}
|
||||
}
|
||||
|
||||
[MenuItem(UniGLTFVersion.MENU + "/GLTF: Generate Serializer")]
|
||||
static void GenerateSerializer()
|
||||
{
|
||||
var info = new ObjectSerialization(typeof(glTF), "gltf", "Serialize_");
|
||||
Debug.Log(info);
|
||||
|
||||
using (var s = File.Open(OutPath, FileMode.Create))
|
||||
using (var w = new StreamWriter(s, new UTF8Encoding(false)))
|
||||
{
|
||||
w.Write(Begin);
|
||||
info.GenerateSerializer(w, "Serialize");
|
||||
w.Write(End);
|
||||
}
|
||||
|
||||
Debug.LogFormat("write: {0}", OutPath);
|
||||
UnityPath.FromFullpath(OutPath).ImportAsset();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### デシリアライザー
|
||||
|
||||
ジェネレーターを呼び出すコードを作成します。
|
||||
|
||||
* 元になる型
|
||||
* 出力先
|
||||
|
||||
の2つを決めます。static関数を生成するので、namespace と static class で囲ってあげます。
|
||||
|
||||
例
|
||||
|
||||
* `Assets\UniGLTF\Editor\UniGLTF\Serialization\DeserializerGenerator.cs`
|
||||
|
||||
```csharp
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UniGLTF
|
||||
{
|
||||
/// <summary>
|
||||
/// Generate deserializer from ListTreeNode<JsonValue> to glTF using type reflection
|
||||
/// </summary>
|
||||
public static class DeserializerGenerator
|
||||
{
|
||||
public const BindingFlags FIELD_FLAGS = BindingFlags.Instance | BindingFlags.Public;
|
||||
|
||||
const string Begin = @"// Don't edit manually. This is generaged.
|
||||
using UniJSON;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UniGLTF {
|
||||
|
||||
public static class GltfDeserializer
|
||||
{
|
||||
|
||||
";
|
||||
|
||||
const string End = @"
|
||||
} // GltfDeserializer
|
||||
} // UniGLTF
|
||||
";
|
||||
|
||||
static string OutPath
|
||||
{
|
||||
get
|
||||
{
|
||||
return Path.Combine(UnityEngine.Application.dataPath,
|
||||
"UniGLTF/UniGLTF/Scripts/IO/GltfDeserializer.g.cs");
|
||||
}
|
||||
}
|
||||
|
||||
[MenuItem(UniGLTFVersion.MENU + "/GLTF: Generate Deserializer")]
|
||||
static void GenerateSerializer()
|
||||
{
|
||||
var info = new ObjectSerialization(typeof(glTF), "gltf", "Deserialize_");
|
||||
Debug.Log(info);
|
||||
|
||||
using (var s = File.Open(OutPath, FileMode.Create))
|
||||
using (var w = new StreamWriter(s, new UTF8Encoding(false)))
|
||||
{
|
||||
w.Write(Begin);
|
||||
info.GenerateDeserializer(w, "Deserialize");
|
||||
w.Write(End);
|
||||
}
|
||||
|
||||
Debug.LogFormat("write: {0}", OutPath);
|
||||
UnityPath.FromFullpath(OutPath).ImportAsset();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### キー出力の抑制
|
||||
|
||||
`index` に無効な値として `-1` を入れる場合に、JSONではキーを出力しないとしたいことがあります。
|
||||
|
||||
TODO: `int?` にするべきだった
|
||||
|
||||
```csharp
|
||||
[JsonSchema(Minimum = 0)]
|
||||
int index = -1;
|
||||
```
|
||||
|
||||
のようにすることで、キーの出力を抑制できます。
|
||||
|
||||
```csharp
|
||||
// 生成コードのキー出力例
|
||||
if(value.index>=0){
|
||||
```
|
||||
|
||||
何も付けないと
|
||||
|
||||
```csharp
|
||||
// 出力制御無し
|
||||
if(true){
|
||||
```
|
||||
|
||||
#### enum のエンコーディング
|
||||
|
||||
enumの値の名前を文字列で使う、enumの値の数値を使うの2種類がありえます。
|
||||
enumの場合はデフォルト値が無いので必須です。
|
||||
|
||||
```csharp
|
||||
[JsonSchema(EnumSerializationType = EnumSerializationType.AsInt)]
|
||||
public glBufferTarget target;
|
||||
|
||||
[JsonSchema(EnumSerializationType = EnumSerializationType.AsLowerString)]
|
||||
public ProjectionType type;
|
||||
```
|
||||
|
||||
### JsonSchemaから生成
|
||||
VRM-1.0 の実装
|
||||
|
||||
TODO:
|
||||
35
docs/api/index.md
Normal file
35
docs/api/index.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# API
|
||||
|
||||
## Update
|
||||
|
||||
```{toctree}
|
||||
:maxdepth: 1
|
||||
sample/index
|
||||
firstperson
|
||||
0_36_update
|
||||
0_44_runtime_import
|
||||
0_58_blendshape
|
||||
how_to_impl_extension
|
||||
0_68_runtime_import
|
||||
0_77_runtime_import
|
||||
0_79_runtime_import
|
||||
0_82_glb_import
|
||||
0_82_runtime_import
|
||||
how_to_customize_material_import
|
||||
fast_spring_bone
|
||||
0_87_runtime_import
|
||||
0_95_dispose
|
||||
0_95_highlevel
|
||||
```
|
||||
|
||||
## VRM-1.0(β)
|
||||
|
||||
```{toctree}
|
||||
:maxdepth: 2
|
||||
|
||||
vrm1_runtime_load
|
||||
vrm1_get_humanoid
|
||||
vrm1_expression
|
||||
vrm1_lookat
|
||||
vrm1_firstperson
|
||||
```
|
||||
3
docs/api/sample/AnimationBridgeSample.md
Normal file
3
docs/api/sample/AnimationBridgeSample.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# AnimationBridgeSample
|
||||
|
||||
AnimationClip に BlendShape の変換を記録して反映するサンプルです。
|
||||
18
docs/api/sample/FirstPersonSample.md
Normal file
18
docs/api/sample/FirstPersonSample.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# FirstPersonSample
|
||||
|
||||
VR アプリで FistPerson の設定に合わせて、可視設定を反映するサンプルです。
|
||||
|
||||
画面が4分割されて、
|
||||
|
||||
* HMDカメラ
|
||||
* その他のカメラ
|
||||
|
||||
の描画を例示します。
|
||||
|
||||
```{gitinclude} v0.87.0 Assets/VRM_Samples/FirstPersonSample/VRMRuntimeLoader.cs
|
||||
:language: csharp
|
||||
:linenos:
|
||||
:lines: 31-53
|
||||
:emphasize-lines: 15
|
||||
:caption:
|
||||
```
|
||||
10
docs/api/sample/RuntimeExporterSample.md
Normal file
10
docs/api/sample/RuntimeExporterSample.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# RuntimeExporter
|
||||
|
||||
VRM を Runtime Export するサンプルです。
|
||||
|
||||
```{gitinclude} v0.87.0 Assets/VRM_Samples/RuntimeExporterSample/VRMRuntimeExporter.cs
|
||||
:language: csharp
|
||||
:linenos:
|
||||
:lines: 106-127
|
||||
:caption:
|
||||
```
|
||||
10
docs/api/sample/SimpleViewer.md
Normal file
10
docs/api/sample/SimpleViewer.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# SimpleViewer
|
||||
|
||||
Runtime ローダーのサンプルです。
|
||||
|
||||
```{gitinclude} c89e Assets/VRM_Samples/SimpleViewer/ViewerUI.cs
|
||||
:language: csharp
|
||||
:linenos:
|
||||
:caption:
|
||||
:lines: 434-485
|
||||
```
|
||||
8
docs/api/sample/index.md
Normal file
8
docs/api/sample/index.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# Sample
|
||||
|
||||
```{toctree}
|
||||
SimpleViewer
|
||||
RuntimeExporterSample
|
||||
FirstPersonSample
|
||||
AnimationBridgeSample
|
||||
```
|
||||
23
docs/api/vrm1_expression.md
Normal file
23
docs/api/vrm1_expression.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# 🚧Expression
|
||||
|
||||
表情周りの操作方法。
|
||||
|
||||
> VRM-1.0 の `BlendShapeProxy` は、`Vrm10Instance.Expression` になります。
|
||||
|
||||
VRM-0.X の例
|
||||
|
||||
```csharp
|
||||
void SetExpression(GameObject root)
|
||||
{
|
||||
var controller = root.GetComponent<BlendShapeProxy>();
|
||||
}
|
||||
```
|
||||
|
||||
VRM-1.0 の例
|
||||
|
||||
```csharp
|
||||
void SetExpression(GameObject root)
|
||||
{
|
||||
var controller = root.GetComponent<Vrm10Instance>();
|
||||
}
|
||||
```
|
||||
66
docs/api/vrm1_firstperson.md
Normal file
66
docs/api/vrm1_firstperson.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# FirstPerson
|
||||
|
||||
{doc}`FirstPerson と Renderer の可視制御 </implementation/first_person>`
|
||||
|
||||
## Runtime に FirstPerson 機能を有効にする
|
||||
|
||||
VR向け FirstPerson 設定の初期化手順です。
|
||||
|
||||
1. Load する
|
||||
2. Vrm10Instance を取得する
|
||||
3. `controller.Vrm.FirstPerson.SetupAsync` を呼び出す
|
||||
4. ShowMeshes
|
||||
|
||||
```csharp
|
||||
async Task<RuntimeGltfInstance> LoadAsync(string path)
|
||||
{
|
||||
var data = new GlbFileParser(path).Parse();
|
||||
if (!Vrm10Data.TryParseOrMigrate(data, true, out Vrm10Data vrm))
|
||||
{
|
||||
throw new System.Exception("vrm parse error !");
|
||||
}
|
||||
using (var loader = new Vrm10Importer(vrm))
|
||||
{
|
||||
// 1.
|
||||
var instance = await loader.LoadAsync();
|
||||
|
||||
// 2.
|
||||
var controller = instance.GetComponent<Vrm10Instance>();
|
||||
|
||||
// 3. The headless model that created is added to instance
|
||||
await controller.Vrm.FirstPerson.SetupAsync(controller.gameObject);
|
||||
|
||||
// 4.
|
||||
instance.ShowMeshes();
|
||||
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## VRMの推奨する VR 向けのカメラ構成
|
||||
|
||||
ヘッドマウントディスプレイを表すカメラ と その他のカメラという2種類のカメラを想定ます。
|
||||
それぞれに対して、
|
||||
|
||||
* FIRSTPERSON_ONLY_LAYER(このレイヤーを指定した gameObject はその他のカメラから消えます)
|
||||
* THIRDPERSON_ONLY_LAYER(このレイヤーを指定した gameObject はヘッドマウントディスプレイから消えます)
|
||||
|
||||
を定義します。
|
||||
これにより、ヘッドマウント視点のアバターの描画を抑止しつつ、他者からは見えるようにします。
|
||||
|
||||
例: アバターの頭の描画を抑止して前が見えるようにする
|
||||
|
||||
VRMは、`VRMFirstPersonOnly` と `VRMThirdPersonOnly` という名前のレイヤーを予約しています。
|
||||
|
||||
`Project Settings` - `Tags and Layers` に `VRMFirstPersonOnly` と `VRMThirdPersonOnly` を
|
||||
設定してください。
|
||||
サンプルでは、それぞれに `9` と `10` を割り当ています。
|
||||
|
||||
## 初期化時に layer を明示する
|
||||
|
||||
追加の引数で指定できます。
|
||||
|
||||
```csharp
|
||||
var created = await controller.Vrm.FirstPerson.SetupAsync(controller.gameObject, firstPersonOnlyLayer: 9, thirdPersonOnlyLayer: 10);
|
||||
```
|
||||
7
docs/api/vrm1_get_humanoid.md
Normal file
7
docs/api/vrm1_get_humanoid.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# 🚧Humanoid
|
||||
|
||||
## Humanoid Bone の取得方法
|
||||
|
||||
### Humanoidコンポーネント(vrm-1.0)
|
||||
|
||||
### Animator経由(vrm-0.xとvrm-1.0共通)
|
||||
1
docs/api/vrm1_lookat.md
Normal file
1
docs/api/vrm1_lookat.md
Normal file
@@ -0,0 +1 @@
|
||||
# 🚧LookAt
|
||||
77
docs/api/vrm1_runtime_load.md
Normal file
77
docs/api/vrm1_runtime_load.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# RuntimeLoad
|
||||
|
||||
## Runtime Normalization (from `v0.92.0`)
|
||||
|
||||
`VRM-1.0` は、ノードの `回転・スケールの削除` をしません。
|
||||
後方互換性のため、 `RuntimeLoad` 時に動的に `回転・スケールの削除` する機能を提供します。
|
||||
|
||||
> `VRM-0.x` のエクスポート時の正規化に相当する処理です
|
||||
|
||||
以下のように doNormalize に true を付与してください。
|
||||
|
||||
```csharp
|
||||
using (var loader = new Vrm10Importer(vrm,
|
||||
// normalize option
|
||||
doNormalize: true))
|
||||
{
|
||||
RuntimeGltfInstance instance = await loader.LoadAsync();
|
||||
return instance;
|
||||
}
|
||||
```
|
||||
|
||||
## Migration(VRM-0 to VRM-1)
|
||||
|
||||
`VRM-1.0` は、 `VRM-0.x` もロードできます。
|
||||
その場合、あたらしい meta への変換が発生し互換性の無い部分はすべて `不許可` の値になります。
|
||||
このため、変換前のライセンスにアクセスする API を提供します。
|
||||
|
||||
サンプルの `Assets\VRM10\Samples\VRM10Viewer\VRM10ViewerUI.cs` も参照してください。
|
||||
|
||||
```csharp
|
||||
static IMaterialDescriptorGenerator GetVrmMaterialDescriptorGenerator(bool useUrp)
|
||||
{
|
||||
if (useUrp)
|
||||
{
|
||||
return new Vrm10UrpMaterialDescriptorGenerator();
|
||||
}
|
||||
else
|
||||
{
|
||||
return new Vrm10MaterialDescriptorGenerator();
|
||||
}
|
||||
}
|
||||
|
||||
async Task<RuntimeGltfInstance> LoadAsync(string path)
|
||||
{
|
||||
GltfData data = new AutoGltfFileParser(path).Parse();
|
||||
|
||||
// doMigrate: true で旧バージョンの vrm をロードできます。
|
||||
if (Vrm10Data.TryParseOrMigrate(data, doMigrate: true, out Vrm10Data vrm))
|
||||
{
|
||||
// vrm
|
||||
using (var loader = new Vrm10Importer(vrm,
|
||||
materialGenerator: GetVrmMaterialDescriptorGenerator(m_useUrpMaterial.isOn)))
|
||||
{
|
||||
// migrate しても thumbnail は同じ
|
||||
var thumbnail = await loader.LoadVrmThumbnailAsync();
|
||||
|
||||
if (vrm.OriginalMetaBeforeMigration != null)
|
||||
{
|
||||
// migrated from vrm-0.x. use OriginalMetaBeforeMigration
|
||||
UpdateMeta(vrm.OriginalMetaBeforeMigration, thumbnail);
|
||||
}
|
||||
else
|
||||
{
|
||||
// load vrm-1.0. use newMeta
|
||||
UpdateMeta(vrm.VrmExtension.Meta, thumbnail);
|
||||
}
|
||||
|
||||
// モデルをロード
|
||||
RuntimeGltfInstance instance = await loader.LoadAsync();
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
else{
|
||||
throw new Exception("not vrm");
|
||||
}
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user