mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-09-07 19:38:13 -05:00
日本語ドキュメントを sphinx 化
This commit is contained in:
1
docs/.gitignore
vendored
Normal file
1
docs/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
_build
|
||||
55
docs/conf.py
Normal file
55
docs/conf.py
Normal file
@@ -0,0 +1,55 @@
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# This file only contains a selection of the most common options. For a full
|
||||
# list see the documentation:
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||
|
||||
# -- Path setup --------------------------------------------------------------
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#
|
||||
# import os
|
||||
# import sys
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
project = 'UniVRM Programming Document'
|
||||
copyright = '2021, VRM Consortium'
|
||||
author = 'VRM Consortium'
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = ['myst_parser']
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#
|
||||
# This is also used if you do content translation via gettext catalogs.
|
||||
# Usually you set "language" from the command line for these cases.
|
||||
language = 'ja'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This pattern also affects html_static_path and html_extra_path.
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
html_theme = 'classic'
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
36
docs/gltf/0_36_update.md
Normal file
36
docs/gltf/0_36_update.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# GltfUpdate(0.36)
|
||||
|
||||
## テクスチャ名の格納位置の修正
|
||||
|
||||
GLTFの仕様に準拠しました。
|
||||
|
||||
* extraはextrasの間違い
|
||||
* imageはnameを持っていた
|
||||
|
||||
```json
|
||||
json.images[i].extra.name
|
||||
```
|
||||
|
||||
変更後
|
||||
|
||||
```json
|
||||
json.images[i].name
|
||||
```
|
||||
|
||||
## ブレンドシェイプ名の格納位置の修正
|
||||
|
||||
GLTFの仕様に準拠しました。
|
||||
|
||||
* extraはextrasの間違い
|
||||
* targetにextrasは不許可
|
||||
* https://github.com/KhronosGroup/glTF/issues/1036#issuecomment-314078356
|
||||
|
||||
```json
|
||||
json.meshes[i].primitives[j].targets[k].extra.name
|
||||
```
|
||||
|
||||
変更後
|
||||
|
||||
```json
|
||||
json.meshes[i].primitives[j].extras.targetNames[k]
|
||||
```
|
||||
127
docs/gltf/0_82_glb_import.md
Normal file
127
docs/gltf/0_82_glb_import.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# GlbImport(0.82)
|
||||
`0.82.1` の API です。
|
||||
`0.82.0` の場合は更新をお願いします。
|
||||
|
||||
以下のステップでロードします。
|
||||
|
||||
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 用のマテリアルをロードする
|
||||
|
||||
`materialGenerator` 引き数(省略可能)を指定することで URP マテリアルを生成するようにカスタムできます。
|
||||
|
||||
```csharp
|
||||
async RuntimeGltfInstance Load(GltfData data)
|
||||
{
|
||||
var materialGenerator = new GltfUrpMaterialDescriptorGenerator();
|
||||
using(var loader = new UniGLTF.ImporterContext(data, materialGenerator: materialGenerator)
|
||||
{
|
||||
var instance = await loader.LoadAsync();
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 3. インスタンスを使用する
|
||||
|
||||
```csharp
|
||||
// SkinnedMeshRenderer に対する指示
|
||||
instance.EnableUpdateWhenOffscreen();
|
||||
// 準備ができたら表示する(デフォルトでは非表示)
|
||||
instance.ShowMeshes();
|
||||
```
|
||||
|
||||
使用後に以下のように破棄してください。関連する Asset(Texture, Material, Meshなど)も破棄されます。
|
||||
```csharp
|
||||
GameObject.Destroy(instance);
|
||||
```
|
||||
441
docs/gltf/how_to_impl_extension.md
Normal file
441
docs/gltf/how_to_impl_extension.md
Normal file
@@ -0,0 +1,441 @@
|
||||
# glTF拡張の実装(0.63.2)
|
||||
|
||||
`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:
|
||||
10
docs/history.md
Normal file
10
docs/history.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# Version
|
||||
|
||||
| version | |
|
||||
|---------|-------------------------------------------------|
|
||||
| v0.82.1 | `materialGenerator(URP対応)` 引き数と `VrmData` |
|
||||
| v0.79 | `GltfData` |
|
||||
| v0.77 | `RuntimeGltfInstance` |
|
||||
| v0.68 | `GltfParser` |
|
||||
| v0.63.2 | gltf の extension の実装方法を変更 |
|
||||
| v0.56 | BlendShapeKey の仕様変更 |
|
||||
67
docs/index.md
Normal file
67
docs/index.md
Normal file
@@ -0,0 +1,67 @@
|
||||
# UniVRM Programming Document
|
||||
|
||||
これは、開発者(UniVRMを使ったアプリケーションを作成する人)向けのドキュメントです。
|
||||
|
||||
UnityでVRMをエクスポートする方法などについては [manual](https://vrm.dev/docs/univrm/) を参照してください。
|
||||
|
||||
## 概要
|
||||
|
||||
```{toctree}
|
||||
:maxdepth: 1
|
||||
|
||||
package.md
|
||||
history.md
|
||||
```
|
||||
|
||||
## glTF
|
||||
|
||||
```{toctree}
|
||||
:maxdepth: 1
|
||||
|
||||
gltf/0_82_glb_import.md
|
||||
gltf/how_to_impl_extension.md
|
||||
gltf/0_36_update.md
|
||||
```
|
||||
|
||||
## VRM
|
||||
|
||||
```{toctree}
|
||||
:maxdepth: 1
|
||||
|
||||
vrm0/0_82_runtime_import.md
|
||||
vrm0/0_79_runtime_import.md
|
||||
vrm0/0_77_runtime_import.md
|
||||
vrm0/0_68_runtime_import.md
|
||||
vrm0/0_44_runtime_import.md
|
||||
vrm0/0_58_blendshape.md
|
||||
vrm0/firstperson.md
|
||||
```
|
||||
|
||||
### Samples
|
||||
|
||||
- SimpleViewer
|
||||
- RuntimeExporerSample
|
||||
- FirstPersonSample
|
||||
- AnimationBridgeSample
|
||||
|
||||
## VRM-1.0(β)
|
||||
|
||||
```{toctree}
|
||||
:maxdepth: 2
|
||||
|
||||
vrm1/vrm1_runtime_load.md
|
||||
vrm1/vrm1_get_humanoid.md
|
||||
vrm1/vrm1_expression.md
|
||||
vrm1/vrm1_lookat.md
|
||||
vrm1/vrm1_firstperson.md
|
||||
```
|
||||
|
||||
### Samples
|
||||
|
||||
- VRM10Viewer
|
||||
|
||||
# Indices and tables
|
||||
|
||||
* {ref}`genindex`
|
||||
* {ref}`modindex`
|
||||
* {ref}`search`
|
||||
34
docs/package.md
Normal file
34
docs/package.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# パッケージ構成
|
||||
|
||||
## パッケージ
|
||||
|
||||
| unitypackage | folder | contents |
|
||||
|--------------------|-----------------------------------|-----------------------|
|
||||
| VRMShaders_UniGLTF | Assets/VRMShaders, Assets/UniGLTF | VRMShaders と UniGLTF |
|
||||
| UniVRM | Assets/VRM | VRM-0.X |
|
||||
| VRM | Assets/VRM10 | VRM-1.0(β) |
|
||||
|
||||
```
|
||||
+----------++----------+
|
||||
|UniVRM-0.X||UniVRM-1.0|
|
||||
+----------++----------+
|
||||
+----------------------+
|
||||
| UniGLTF |
|
||||
+----------------------+
|
||||
| VRMShaders |
|
||||
+----------------------+
|
||||
```
|
||||
|
||||
## UPM
|
||||
|
||||
```json
|
||||
// manifest.json 抜粋
|
||||
{
|
||||
"dependencies": {
|
||||
"com.vrmc.vrmshaders": "https://github.com/vrm-c/UniVRM.git?path=/Assets/VRMShaders#v0.82.1",
|
||||
"com.vrmc.gltf": "https://github.com/vrm-c/UniVRM.git?path=/Assets/UniGLTF#v0.82.1", // rename unigltf to gltf
|
||||
"com.vrmc.univrm": "https://github.com/vrm-c/UniVRM.git?path=/Assets/VRM#v0.82.1",
|
||||
"com.vrmc.vrm": "https://github.com/vrm-c/UniVRM.git?path=/Assets/VRM10#v0.82.1", // rename univrm1 to vrm
|
||||
}
|
||||
}
|
||||
```
|
||||
163
docs/vrm0/0_44_runtime_import.md
Normal file
163
docs/vrm0/0_44_runtime_import.md
Normal file
@@ -0,0 +1,163 @@
|
||||
# RuntimeImport(0.44)
|
||||
|
||||
## `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を開く
|
||||
|
||||
{{< highlight cs >}}
|
||||
var path="sample.vrm";
|
||||
var go=VRM.VRMImporter.LoadFromPath(path);
|
||||
Debug.LogFormat("loaded {0}", go.name);
|
||||
{{< / highlight >}}
|
||||
|
||||
## ファイルパスから非同期にVRMを開く
|
||||
|
||||
{{< highlight cs >}}
|
||||
var path="sample.vrm";
|
||||
VRMImporter.LoadVrmAsync(path, go => {
|
||||
Debug.LogFormat("loaded {0}", go.name);
|
||||
});
|
||||
{{< / highlight >}}
|
||||
|
||||
## バイト列からVRM開く
|
||||
|
||||
{{< highlight cs >}}
|
||||
var path="sample.vrm";
|
||||
var bytes = File.ReadAllBytes(path);
|
||||
var go=VRMImporter.LoadFromBytes(bytes);
|
||||
{{< / highlight >}}
|
||||
|
||||
## バイト列から非同期にVRMを開く
|
||||
|
||||
{{< highlight cs >}}
|
||||
VRMImporter.LoadVrmAsync(bytes, go => {
|
||||
Debug.LogFormat("loaded {0}", go.name);
|
||||
});
|
||||
{{< / highlight >}}
|
||||
|
||||
## VRMから情報を取り出す
|
||||
|
||||
{{< highlight cs >}}
|
||||
#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);
|
||||
}
|
||||
{{< / highlight >}}
|
||||
|
||||
## Thumbnailを取得する(v0.37から)
|
||||
|
||||
ReadMetaに引数を渡すことでThumbnailテクスチャを作成できます。
|
||||
|
||||
{{< highlight cs >}}
|
||||
var meta = context.ReadMeta(true); // Thumbnailテクスチャを作成する
|
||||
Texture2D thumbnail=meta.Thumbnail;
|
||||
{{< / highlight >}}
|
||||
132
docs/vrm0/0_58_blendshape.md
Normal file
132
docs/vrm0/0_58_blendshape.md
Normal file
@@ -0,0 +1,132 @@
|
||||
# BlendShapeProxy(0.58)
|
||||
|
||||
## 環境
|
||||
UniVRM v0.58.0
|
||||
|
||||
[BlendShapeKeyのインタフェースを厳格化、整理](https://github.com/vrm-c/UniVRM/wiki/ReleaseNote-v0.56.0%28ja%29#blendshapekey%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%BF%E3%83%95%E3%82%A7%E3%83%BC%E3%82%B9%E3%82%92%E5%8E%B3%E6%A0%BC%E5%8C%96%E6%95%B4%E7%90%86)
|
||||
|
||||
## 使用するメソッド
|
||||
|
||||
* [推奨] `SetValues`
|
||||
* [非推奨] `ImmediatelySetValue`
|
||||
* [上級者向け] `AccumulateValue`
|
||||
* [上級者向け] `Apply`
|
||||
|
||||
## スクリプトから BlendShape weight を適用する
|
||||
|
||||
`SetValues` 関数のみを使用します。
|
||||
そのフレームで必要な表情の weight 値をすべて集めてから `SetValues` を 1 回だけ呼んで設定します。
|
||||
|
||||
{{< highlight cs >}}
|
||||
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 で指定
|
||||
});
|
||||
{{< / highlight >}}
|
||||
|
||||
## 複数の 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`
|
||||
|
||||
で定義されているとします。
|
||||
このとき両目を閉じたいモチベーションから、両方を有効にする意図で下記のように実行します。
|
||||
|
||||
{{< highlight cs >}}
|
||||
proxy.ImmediatelySetValue(BlendShapeKey.CreateFromPreset(BlendShapePreset.Blink_L), 1.0f);
|
||||
proxy.ImmediatelySetValue(BlendShapeKey.CreateFromPreset(BlendShapePreset.Blink_R), 1.0f);
|
||||
{{< / highlight >}}
|
||||
|
||||
すると、左目だけが開いてしまいます。
|
||||
これは後から `ImmediateSetValue` した `Blink_R` が `Blink_L` と競合して weight を上書きしてしまうからです。
|
||||
したがって VRM の表情制御においては下記の 2 通りのどちらかの方法で書くことが求められます。
|
||||
これらの方法はこの競合の問題を解決して表情を設定することができます。
|
||||
|
||||
{{< highlight cs >}}
|
||||
proxy.SetValues(new Dictionary<BlendShapeKey, float>
|
||||
{
|
||||
{BlendShapeKey.CreateFromPreset(BlendShapePreset.Blink_L), 1.0f},
|
||||
{BlendShapeKey.CreateFromPreset(BlendShapePreset.Blink_R), 1.0f},
|
||||
});
|
||||
{{< / highlight >}}
|
||||
|
||||
または
|
||||
|
||||
{{< highlight cs >}}
|
||||
proxy.AccumulateValue(BlendShapeKey.CreateFromPreset(BlendShapePreset.Blink_L), 1.0f); // すぐに適用せずにたくわえる
|
||||
proxy.AccumulateValue(BlendShapeKey.CreateFromPreset(BlendShapePreset.Blink_R), 1.0f);
|
||||
proxy.Apply(); // 蓄積した値をまとめて適用する
|
||||
{{< / highlight >}}
|
||||
|
||||
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/vrm0/0_68_runtime_import.md
Normal file
163
docs/vrm0/0_68_runtime_import.md
Normal file
@@ -0,0 +1,163 @@
|
||||
# RuntimeImport(0.68)
|
||||
|
||||
## 過去バージョンからの仕様変更
|
||||
|
||||
`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/vrm0/0_77_runtime_import.md
Normal file
83
docs/vrm0/0_77_runtime_import.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# RuntimeImport(0.77)
|
||||
|
||||
[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/vrm0/0_79_runtime_import.md
Normal file
12
docs/vrm0/0_79_runtime_import.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# RuntimeImport(0.79)
|
||||
|
||||
`GltfParser` と `GltfData` の分割
|
||||
|
||||
```csharp
|
||||
var parser = new GltfParser();
|
||||
parser.ParsePath(path);
|
||||
```
|
||||
|
||||
```csharp
|
||||
GltfData data = new GlbFileParser(path).Parse();
|
||||
```
|
||||
80
docs/vrm0/0_82_runtime_import.md
Normal file
80
docs/vrm0/0_82_runtime_import.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# RuntimeImport(0.82)
|
||||
|
||||
* `Version 0.82.0` は `0.82.1` 以降を使ってください。
|
||||
* `Version 0.82.1~`
|
||||
|
||||
以下の手順で 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);
|
||||
```
|
||||
80
docs/vrm0/firstperson.md
Normal file
80
docs/vrm0/firstperson.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# VRMFirstPersonの使い方
|
||||
|
||||
## VRMFirstPersonの設定
|
||||
[VRMFirstPerson]({{< relref "univrm_firstperson.md" >}})ではRendererに対して設定があります。
|
||||
|
||||
|FirstPersonFlag |レイヤー |備考 |
|
||||
|------------------------------|----------------------|--------------------------------------------|
|
||||
|Both |default |一人称と三人称で分ける必要のない部分に指定します|
|
||||
|ThirdPersonOnly |VRMThirdPersonOnly|一人称時に描画したくない部分に指定します |
|
||||
|FirstPersonOnly |VRMFirstPersonOnly|三人称時に描画したくない部分に指定します。自動作成した頭部無しモデルが使います|
|
||||
|Auto |VRMThirdPersonOnly|実行時に一人称用モデルを自動で作成し、それをFIRSTPERSON_ONLY_LAYERに設定します|
|
||||
|
||||
実行時に**VRMFirstPerson.Setup**を呼び出すことで、上記のレイヤー設定を行うことができます。明示的に外部から呼び出してください。
|
||||
|
||||
## アプリケーションに追加の描画レイヤーを指定する
|
||||
|
||||
定数で以下のレイヤーを定義しています。
|
||||
|
||||
{{< highlight cs >}}
|
||||
public class VRMFirstPerson : MonoBehaviour
|
||||
{
|
||||
public const int FIRSTPERSON_ONLY_LAYER = 9;
|
||||
public const int THIRDPERSON_ONLY_LAYER = 10;
|
||||
|
||||
// 省略
|
||||
}
|
||||
{{< / highlight >}}
|
||||
|
||||
|{{< img src="images/vrm/layer_setting.png" >}}|
|
||||
|-----|
|
||||
|9番と10番にLayerを設定|
|
||||
|
||||
## 実行時にSetupを呼び出して、カメラにLayerMaskを設定する
|
||||
|
||||
* VRMFirstPerson.Setupの呼び出し
|
||||
* 一人称カメラとその他のカメラに対してLayerMask
|
||||
|
||||
{{< highlight cs >}}
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
{{< / highlight >}}
|
||||
23
docs/vrm1/vrm1_expression.md
Normal file
23
docs/vrm1/vrm1_expression.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# 🚧Expression
|
||||
|
||||
表情周りの操作方法。
|
||||
|
||||
> VRM-1.0 の `BlendShapeProxy` は、`VRM10Controller.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<VRM10Controller>();
|
||||
}
|
||||
```
|
||||
68
docs/vrm1/vrm1_firstperson.md
Normal file
68
docs/vrm1/vrm1_firstperson.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# FirstPerson
|
||||
|
||||
## Runtime に FirstPerson 機能を有効にする
|
||||
|
||||
VR向け FirstPerson 設定の初期化手順です。
|
||||
|
||||
1. Load する
|
||||
2. VRM10Controller を取得する
|
||||
3. `controller.Vrm.FirstPerson.SetupAsync` を呼び出す
|
||||
4. `controller.Vrm.FirstPerson.SetupAsync` した結果新規に作成されたモデルを `RuntimeGltfInstance` に渡す
|
||||
5. 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<VRM10Controller>();
|
||||
|
||||
// 3.
|
||||
var created = await controller.Vrm.FirstPerson.SetupAsync(controller.gameObject);
|
||||
|
||||
// 4.
|
||||
instance.AddRenderers(created);
|
||||
|
||||
// 5.
|
||||
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/vrm1/vrm1_get_humanoid.md
Normal file
7
docs/vrm1/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/vrm1/vrm1_lookat.md
Normal file
1
docs/vrm1/vrm1_lookat.md
Normal file
@@ -0,0 +1 @@
|
||||
# 🚧LookAt
|
||||
56
docs/vrm1/vrm1_runtime_load.md
Normal file
56
docs/vrm1/vrm1_runtime_load.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# RuntimeLoad
|
||||
|
||||
`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