ドキュメント整理

This commit is contained in:
ousttrue
2022-02-04 19:20:42 +09:00
parent e67448c817
commit 89b7f81bd6
33 changed files with 109 additions and 90 deletions

36
docs/api/0_36_update.md Normal file
View 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]
```

View 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
View 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},
});
```

View 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);
}
}
}
```

View 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);
}
}
}
```

View 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
View 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);
```

View 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);
```

View 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
View 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>

View File

@@ -0,0 +1,3 @@
# `v0.95` 簡単API
API が複雑化してきたので、よくある用途を簡単にできる高レベル API を追加しました。

View 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
View 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番と番に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();
}
}
}
```

View 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>

View 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# の型から生成
#### シリアライザー
ジェネレーターを呼び出すコードを作成します。
* 元になる型
* 出力先
つを決めます。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();
}
}
}
```
#### デシリアライザー
ジェネレーターを呼び出すコードを作成します。
* 元になる型
* 出力先
つを決めます。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
View 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
```

View File

@@ -0,0 +1,3 @@
# AnimationBridgeSample
AnimationClip に BlendShape の変換を記録して反映するサンプルです。

View 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:
```

View 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:
```

View 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
View File

@@ -0,0 +1,8 @@
# Sample
```{toctree}
SimpleViewer
RuntimeExporterSample
FirstPersonSample
AnimationBridgeSample
```

View 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>();
}
```

View 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);
```

View 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
View File

@@ -0,0 +1 @@
# 🚧LookAt

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