mirror of
https://github.com/vrm-c/UniVRM.git
synced 2026-04-24 06:57:49 -05:00
Articles
This commit is contained in:
parent
58b44f68f5
commit
1c44a545c2
21
docfx/.vscode/tasks.json
vendored
21
docfx/.vscode/tasks.json
vendored
|
|
@ -16,10 +16,11 @@
|
|||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "meta-build",
|
||||
"label": "watch-build",
|
||||
"type": "shell",
|
||||
"command": "invoke meta-build",
|
||||
"problemMatcher": []
|
||||
"command": "python watch_build.py",
|
||||
"problemMatcher": [],
|
||||
"isBackground": true
|
||||
},
|
||||
{
|
||||
"label": "serve",
|
||||
|
|
@ -27,6 +28,18 @@
|
|||
"command": "docfx --serve",
|
||||
"problemMatcher": [],
|
||||
"isBackground": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "meta-build",
|
||||
"type": "shell",
|
||||
"command": "invoke meta-build",
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "lang-diff",
|
||||
"type": "shell",
|
||||
"command": "invoke lang-diff",
|
||||
"problemMatcher": [],
|
||||
},
|
||||
]
|
||||
}
|
||||
9
docfx/articles/en/gltf/0_82_glb_import.md
Normal file
9
docfx/articles/en/gltf/0_82_glb_import.md
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
|
||||
## GLB
|
||||
|
||||
Set `materialGenerator` argument, Material is replaced by URP version.
|
||||
|
||||
```cs
|
||||
var data = new GlbFileParser(path).Parse();
|
||||
var loader = new UniGLTF.ImporterContext(data, materialGenerator: new GltfUrpMaterialDescriptorGenerator());
|
||||
```
|
||||
440
docfx/articles/en/gltf/how_to_impl_extension.md
Normal file
440
docfx/articles/en/gltf/how_to_impl_extension.md
Normal file
|
|
@ -0,0 +1,440 @@
|
|||
From `UniVRM-0.63.2` , `UniGLTF` structure is changed, cause `extensions` / `extras` implementations is updated.
|
||||
|
||||
## About GLTF Extensions
|
||||
|
||||
`glTF` has each `extensions`, `extras` points.
|
||||
|
||||
* `extensions` (or extras)
|
||||
* `asset.extensions` (or はextras)
|
||||
* `meshes[*].extensions` (or はextras)
|
||||
* `materials[*].extensions` (or はextras)
|
||||
|
||||
etc...
|
||||
|
||||
`extensions` is defined specification officially, and publish `JsonSchema`.
|
||||
|
||||
* https://github.com/KhronosGroup/glTF/tree/master/extensions
|
||||
|
||||
`extensions` naming convention is `{VENDOR_NAME}_{EXTENSION_NAME}`.
|
||||
VENDOR_NAME can registered on https://github.com/KhronosGroup/glTF.
|
||||
|
||||
`extras` is defined in application local.
|
||||
|
||||
> This enables glTF models to contain application-specific properties without creating a full glTF extension
|
||||
|
||||
## UniGLTF の extensions
|
||||
|
||||
Before `v0.63.0`, `GLTF type` 's `extensions` field is statically extended as `GLTFExtensions Type`, and had `VRM type` field.
|
||||
|
||||
```cs
|
||||
class VRM
|
||||
{
|
||||
}
|
||||
|
||||
class GLTFExtensions
|
||||
{
|
||||
public VRM VRM;
|
||||
}
|
||||
|
||||
class GLTF
|
||||
{
|
||||
// all types require in compile time. cannot extend dynamically.
|
||||
public GLTFExtensions extensions;
|
||||
}
|
||||
```
|
||||
|
||||
TODO:
|
||||
|
||||
この設計だと GLTF と拡張を別ライブラリとして分離することができませんでした。
|
||||
|
||||
`v0.63.1` から設計を変更して、すべての `extensions/extras` に同じ型の入れ物を使うように変更しました。
|
||||
UniGLTF は `import/export` の具体的な内容を知らずに中間データの入れ物として扱います。
|
||||
|
||||
```cs
|
||||
// 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
|
||||
|
||||
```cs
|
||||
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`
|
||||
|
||||
```cs
|
||||
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`
|
||||
|
||||
```cs
|
||||
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?` にするべきだった
|
||||
|
||||
```cs
|
||||
[JsonSchema(Minimum = 0)]
|
||||
int index = -1;
|
||||
```
|
||||
|
||||
のようにすることで、キーの出力を抑制できます。
|
||||
|
||||
```cs
|
||||
// 生成コードのキー出力例
|
||||
if(value.index>=0){
|
||||
```
|
||||
|
||||
何も付けないと
|
||||
|
||||
```cs
|
||||
// 出力制御無し
|
||||
if(true){
|
||||
```
|
||||
|
||||
#### enum のエンコーディング
|
||||
|
||||
enumの値の名前を文字列で使う、enumの値の数値を使うの2種類がありえます。
|
||||
enumの場合はデフォルト値が無いので必須です。
|
||||
|
||||
```cs
|
||||
[JsonSchema(EnumSerializationType = EnumSerializationType.AsInt)]
|
||||
public glBufferTarget target;
|
||||
|
||||
[JsonSchema(EnumSerializationType = EnumSerializationType.AsLowerString)]
|
||||
public ProjectionType type;
|
||||
```
|
||||
|
||||
### JsonSchemaから生成
|
||||
VRM-1.0 の実装
|
||||
|
||||
TODO:
|
||||
45
docfx/articles/en/history.md
Normal file
45
docfx/articles/en/history.md
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
# Version
|
||||
|
||||
| version | |
|
||||
|---------|---------------------------------------|
|
||||
| v0.82.1 | Material replacement when(for URP) |
|
||||
| v0.82.0 | Material replacement when(for URP) |
|
||||
| v0.77 | Reorganize ImporterContext |
|
||||
| v0.68 | Reorganize ImporterContext |
|
||||
| v0.63.2 | Update gltf extensions implementation |
|
||||
| v0.56 | Update BlendShapeKey spec |
|
||||
|
||||
[Rework BlendShapeKey's Interface](https://github.com/vrm-c/UniVRM/wiki/ReleaseNote-v0.56.0%28en%29#reworks-blendshapekeys-interface)
|
||||
|
||||
## v0.36
|
||||
|
||||
### Changed Storage Position of Texture Name
|
||||
|
||||
Conforming to the GLTF specification.
|
||||
|
||||
```json
|
||||
json.images[i].extra.name
|
||||
```
|
||||
|
||||
After the change
|
||||
|
||||
```json
|
||||
json.images[i].name
|
||||
```
|
||||
|
||||
### Changed Storage Position BlendShape Name
|
||||
|
||||
Conforming to the GLTF specification.
|
||||
|
||||
* `extras` is not allowed in target
|
||||
* https://github.com/KhronosGroup/glTF/issues/1036#issuecomment-314078356
|
||||
|
||||
```json
|
||||
json.meshes[i].primitives[j].targets[k].extra.name
|
||||
```
|
||||
|
||||
After the change
|
||||
|
||||
```json
|
||||
json.meshes[i].primitives[j].extras.targetNames[k]
|
||||
```
|
||||
3
docfx/articles/en/index.md
Normal file
3
docfx/articles/en/index.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
This is a document for developer that using UniVRM.
|
||||
|
||||
Please see [manual](https://vrm.dev/en/docs//univrm/) about how to export VRM.
|
||||
32
docfx/articles/en/package.md
Normal file
32
docfx/articles/en/package.md
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
# Package Structure
|
||||
|
||||
| unitypackage | folder | contents |
|
||||
|--------------------|-----------------------------------|------------------------|
|
||||
| VRMShaders_UniGLTF | Assets/VRMShaders, Assets/UniGLTF | VRMShaders and 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
|
||||
}
|
||||
}
|
||||
```
|
||||
49
docfx/articles/en/toc.yml
Normal file
49
docfx/articles/en/toc.yml
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
- name: Overview
|
||||
href: index.md
|
||||
items:
|
||||
- name: PackageStructure
|
||||
href: package.md
|
||||
- name: Version
|
||||
href: history.md
|
||||
|
||||
- name: glTF
|
||||
items:
|
||||
- name: GlbImport(0.82)
|
||||
href: gltf/0_82_glb_import.md
|
||||
- name: glTF extensions implementation(0.63.2)
|
||||
href: gltf/how_to_impl_extension.md
|
||||
|
||||
- name: VRM
|
||||
items:
|
||||
- name: 🚧Samples/SimpleViewer
|
||||
- name: 🚧Samples/RuntimeExporerSample
|
||||
- name: 🚧Samples/FirstPersonSample
|
||||
- name: 🚧Samples/AnimationBridgeSample
|
||||
- name: RuntimeImport(0.82)
|
||||
href: vrm0/0_82_runtime_import.md
|
||||
- name: RuntimeImport(0.79)
|
||||
href: vrm0/0_79_runtime_import.md
|
||||
- name: RuntimeImport(0.77)
|
||||
href: vrm0/0_77_runtime_import.md
|
||||
- name: RuntimeImport(0.68)
|
||||
href: vrm0/0_68_runtime_import.md
|
||||
- name: RuntimeImport(0.44)
|
||||
href: vrm0/0_44_runtime_import.md
|
||||
- name: BlendShapeProxy(0.58)
|
||||
href: vrm0/0_58_blendshape.md
|
||||
- name: FirstPerson
|
||||
href: vrm0/firstperson.md
|
||||
|
||||
- name: VRM-1.0(β)
|
||||
items:
|
||||
- name: 🚧Samples/VRM10Viewer
|
||||
- name: 🚧RuntimeImport
|
||||
href: vrm1/vrm1_runtime_load.md
|
||||
- name: 🚧Humanoid
|
||||
href: vrm1/vrm1_get_humanoid.md
|
||||
- name: 🚧Expression
|
||||
href: vrm1/vrm1_expression.md
|
||||
- name: 🚧LookAt
|
||||
href: vrm1/vrm1_lookat.md
|
||||
- name: 🚧FirstPerson
|
||||
href: vrm1/vrm1_firstperson.md
|
||||
162
docfx/articles/en/vrm0/0_44_runtime_import.md
Normal file
162
docfx/articles/en/vrm0/0_44_runtime_import.md
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
## `Version 0.44~` LoadAsync Example
|
||||
|
||||
```csharp
|
||||
// Get the byte array
|
||||
var bytes = File.ReadAllBytes(path);
|
||||
|
||||
var context = new VRMImporterContext();
|
||||
context.ParseGlb(bytes);
|
||||
|
||||
// When meta is needed
|
||||
bool createThumbnail=true;
|
||||
var meta = context.ReadMeta(createThumbnail);
|
||||
var thumbnail = meta.Thumbnail;
|
||||
|
||||
// Construct a model
|
||||
context.LoadAsync(_ =>
|
||||
{
|
||||
context.ShowMeshes();
|
||||
var go = context.Root;
|
||||
// Load completed
|
||||
},
|
||||
Debug.LogError);
|
||||
```
|
||||
|
||||
## LoadAsyncTask Example
|
||||
|
||||
```csharp
|
||||
#if (NET_4_6 && UNITY_2017_1_OR_NEWER)
|
||||
async static Task<GameObject> LoadAsync(Byte[] bytes)
|
||||
{
|
||||
var context = new VRMImporterContext();
|
||||
|
||||
// Get JSON in GLB format and parse it
|
||||
context.ParseGlb(bytes);
|
||||
|
||||
try
|
||||
{
|
||||
// Convert the parsed JSON to the scene object
|
||||
await context.LoadAsyncTask();
|
||||
|
||||
// Prevent the model's surface from being penetrated by
|
||||
// the positional relation between the bounding box and the camera
|
||||
// SkinnedMeshRenderer.updateWhenOffscreen = true
|
||||
context.EnableUpdateWhenOffscreen();
|
||||
|
||||
// If you do not want the program displaying the model's T-Pose,
|
||||
// prepare it before ShowMeshes
|
||||
// Display the model when the loading is finished
|
||||
context.ShowMeshes();
|
||||
|
||||
return context.Root;
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
Debug.LogError(ex);
|
||||
// Destroy related resources
|
||||
context.Destroy(true);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
```
|
||||
|
||||
## Related-Article
|
||||
|
||||
More details can be found in the link below (written in Japanese):
|
||||
|
||||
* [UniVRMを使ってVRMモデルをランタイムロードする方法](https://qiita.com/sh_akira/items/8155e4b69107c2a7ede6)
|
||||
|
||||
|
||||
Examples of importing the VRM model with the latest version [can be found here]({{< relref "runtime_import.md" >}}).
|
||||
|
||||
The followings are the methods to import a VRM model at runtime in Unity:
|
||||
|
||||
## Open VRM from a file path
|
||||
|
||||
{{< highlight cs >}}
|
||||
var path="sample.vrm";
|
||||
var go=VRM.VRMImporter.LoadFromPath(path);
|
||||
Debug.LogFormat("loaded {0}", go.name);
|
||||
{{< / highlight >}}
|
||||
|
||||
## Open VRM asynchronously from a file path
|
||||
|
||||
{{< highlight cs >}}
|
||||
var path="sample.vrm";
|
||||
VRMImporter.LoadVrmAsync(path, go => {
|
||||
Debug.LogFormat("loaded {0}", go.name);
|
||||
});
|
||||
{{< / highlight >}}
|
||||
|
||||
## Open VRM from a byte array
|
||||
|
||||
{{< highlight cs >}}
|
||||
var path="sample.vrm";
|
||||
var bytes = File.ReadAllBytes(path);
|
||||
var go=VRMImporter.LoadFromBytes(bytes);
|
||||
{{< / highlight >}}
|
||||
|
||||
## Open VRM asynchronously from a byte array
|
||||
|
||||
{{< highlight cs >}}
|
||||
VRMImporter.LoadVrmAsync(bytes, go => {
|
||||
Debug.LogFormat("loaded {0}", go.name);
|
||||
});
|
||||
{{< / highlight >}}
|
||||
|
||||
## Get the information form 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;
|
||||
}
|
||||
|
||||
// Get the byte array
|
||||
var bytes = File.ReadAllBytes(path);
|
||||
|
||||
var context = new VRMImporterContext();
|
||||
|
||||
// Get JSON in GLB format and parse it
|
||||
context.ParseGlb(bytes);
|
||||
|
||||
// Get the meta
|
||||
var meta = context.ReadMeta();
|
||||
Debug.LogFormat("meta: title:{0}", meta.Title);
|
||||
|
||||
// You can access the entire parsed GLTF here
|
||||
var vrm = context.GLTF;
|
||||
|
||||
// Convert the parsed JSON to the Scene Object
|
||||
if (m_loadAsync)
|
||||
{
|
||||
// Run asynchronously
|
||||
var now = Time.time;
|
||||
VRMImporter.LoadVrmAsync(context, go=> {
|
||||
var delta = Time.time - now;
|
||||
Debug.LogFormat("LoadVrmAsync {0:0.0} seconds", delta);
|
||||
OnLoaded(go);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// Run synchronously
|
||||
VRMImporter.LoadFromBytes(context);
|
||||
OnLoaded(context.Root);
|
||||
}
|
||||
{{< / highlight >}}
|
||||
|
||||
## Get the thumbnail (From v0.37)
|
||||
|
||||
A thumbnail texture can be created by passing arguments to ReadMeta.
|
||||
|
||||
{{< highlight cs >}}
|
||||
var meta = context.ReadMeta(true); // Make a thumbnail texture
|
||||
Texture2D thumbnail=meta.Thumbnail;
|
||||
{{< / highlight >}}
|
||||
126
docfx/articles/en/vrm0/0_58_blendshape.md
Normal file
126
docfx/articles/en/vrm0/0_58_blendshape.md
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
---
|
||||
title: "How to Use BlendShapeProxy"
|
||||
date: 2018-04-16T16:30:00+09:00
|
||||
aliases: ["/en/dev/univrm-0.xx/programming/univrm_use_blendshape/",
|
||||
"/en/docs/univrm/programming/how_to_use_blendshapeproxy/"]
|
||||
weight: 3
|
||||
tags: ["api"]
|
||||
---
|
||||
|
||||
## Environment
|
||||
UniVRM v0.58.0
|
||||
|
||||
## Methods
|
||||
|
||||
* [Recommended] `SetValues`
|
||||
* [Not Recommended] `ImmediatelySetValue`
|
||||
* [For Advanced Users] `AccumulateValue`, `Apply`
|
||||
|
||||
## Apply BlendShape weight from script
|
||||
|
||||
Call `SetValues` function once to create the specific expression (merged by multiple BlendShapes) in a frame:
|
||||
|
||||
{{< highlight cs >}}
|
||||
var proxy = GetComponent<VRMBlendShapeProxy>();
|
||||
|
||||
proxy.SetValues(new Dictionary<BlendShapeKey, float>
|
||||
{
|
||||
{BlendShapeKey.CreateFromPreset(BlendShapePreset.A), 1f}, // Assign the Weight of a BlendShape clip between 0 and 1
|
||||
{BlendShapeKey.CreateFromPreset(BlendShapePreset.Joy), 1f}, // Specify a system-defined BlendShape clip by enum
|
||||
{BlendShapeKey.CreateUnknown("USER_DEFINED_FACIAL"), 1f}, // Specify a user-defined BlendShape clip by string
|
||||
});
|
||||
{{< / highlight >}}
|
||||
|
||||
## Why use `SetValues` for synthesizing multiple BlendShapes?
|
||||
|
||||
We found that multiple BlendShapes compete with each other when the following expressions are specified:
|
||||
|
||||
* LipSync
|
||||
* Eye Blink
|
||||
* Eye Gaze control (if eye gaze movements are controlled by BlendShape)
|
||||
* Emotions
|
||||
|
||||
A BlendShape set first may be overwritten with followed BlendShapes so it turns out that the specified expression is not actually shown.
|
||||
|
||||
In order to address this issue, we can use `SetValues` function to merge multiple BlendShapes into a specified expression while the BlendShape overwriting can be avoided.
|
||||
|
||||
Blink example:
|
||||
|
||||
For Blink_L
|
||||
|
||||
* The weight value for BlendShape `eye_L` of `Mesh_A` is 100
|
||||
* The weight value for BlendShape `eye_R` of `Mesh_A` is 1
|
||||
|
||||
For Blink_R
|
||||
|
||||
* The weight value for BlendShape `eye_L` of `Mesh_A` is 1
|
||||
* The weight value for BlendShape `eye_R` of `Mesh_A` is 100
|
||||
|
||||
If we use `ImmediatelySetValue` function for eye blinking,
|
||||
|
||||
{{< highlight cs >}}
|
||||
proxy.ImmediatelySetValue(BlendShapeKey.CreateFromPreset(BlendShapePreset.Blink_L), 1.0f);
|
||||
proxy.ImmediatelySetValue(BlendShapeKey.CreateFromPreset(BlendShapePreset.Blink_R), 1.0f);
|
||||
{{< / highlight >}}
|
||||
|
||||
The weight values set for Blink_L will be overwritten by Blink_R. To resolve this issue, we use `SetValues` or `AccumulateValue` to correctly manipulate specified BlendShapes:
|
||||
|
||||
{{< 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);
|
||||
// Apply all the specified BlendShapes at once
|
||||
proxy.Apply();
|
||||
{{< / highlight >}}
|
||||
|
||||
More details are described below:
|
||||
|
||||
## ImmediatelySetValue
|
||||
|
||||
Assumed to be used for a simple test program.
|
||||
|
||||
Example:
|
||||
|
||||
```cs
|
||||
var proxy = GetComponent<VRMBlendShapeProxy>();
|
||||
|
||||
proxy.ImmediatelySetValue(BlendShapeKey.CreateFromPreset(BlendShapePreset.A), 1.0f);
|
||||
```
|
||||
|
||||
## AccumulateValue + Apply
|
||||
|
||||
Example:
|
||||
|
||||
```cs
|
||||
var proxy = GetComponent<VRMBlendShapeProxy>();
|
||||
|
||||
proxy.AccumulateValue(BlendShapeKey.CreateFromPreset(BlendShapePreset.Blink_L), 1.0f);
|
||||
proxy.AccumulateValue(BlendShapeKey.CreateFromPreset(BlendShapePreset.Blink_R), 1.0f);
|
||||
// Apply all the specified BlendShapes at once
|
||||
proxy.Apply();
|
||||
```
|
||||
|
||||
We recommend `SetValues` (below) to handle the case of applying multiple BlendShapes.
|
||||
|
||||
## SetValues
|
||||
|
||||
Call `SetValues` to combine multiple BlendShapes.
|
||||
|
||||
Example:
|
||||
|
||||
```cs
|
||||
var proxy = GetComponent<VRMBlendShapeProxy>();
|
||||
|
||||
proxy.SetValues(new Dictionary<BlendShapeKey, float>
|
||||
{
|
||||
{BlendShapeKey.CreateFromPreset(BlendShapePreset.Blink_L), 1.0f},
|
||||
{BlendShapeKey.CreateFromPreset(BlendShapePreset.Blink_R), 1.0f},
|
||||
});
|
||||
```
|
||||
161
docfx/articles/en/vrm0/0_68_runtime_import.md
Normal file
161
docfx/articles/en/vrm0/0_68_runtime_import.md
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
## `Version 0.68~`
|
||||
|
||||
### API Changes
|
||||
|
||||
ImporterContext has been reworked.
|
||||
|
||||
* Loading processing has been divided into two steps: `Parse` and `Load`
|
||||
* `Parse` processing can be processed by other than the main thread
|
||||
* The implementation of asynchronous loading function `ImporterContext.LoadAsync` has changed to `Task`
|
||||
* The method of explicitly destroying `UnityEngine.Object` resources is now available. As such, resource leaks can be prevented
|
||||
* The timing of calling `ImporterContext.Dispose` has been changed to when the loading process ends
|
||||
* Call `ImporterContext.DisposeOnGameObjectDestroyed` function (described below) before `ImporterContext.Dispose` function is called
|
||||
* In the previous versions, `ImporterContext.Dispose` is called when the generated VRM model is destroyed
|
||||
* Added `ImporterContext.DisposeOnGameObjectDestroyed` function
|
||||
* The duty of destroying VRM resources (Texture, Material, Mesh, etc) has been transferred to GameObject
|
||||
* The resources (Texture, Material, Mesh, etc) will be destroyed when VRM's GameObject is destroyed
|
||||
|
||||
|
||||
### Sample Codes (Synchronous Loading)
|
||||
|
||||
```cs
|
||||
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. Call GltfParser function (it has been separated from ImporterContext)
|
||||
// We use GltfParser to obtain JSON information and binary data from the VRM file
|
||||
var parser = new GltfParser();
|
||||
parser.ParsePath(vrmFilePath);
|
||||
|
||||
// 2. Initialize a new VRMImporterContext object and pass `parser` as an argument to it
|
||||
// VRMImporterContext is the class for loading VRM
|
||||
using (var context = new VRMImporterContext(parser))
|
||||
{
|
||||
// 3. Call Load function to create a VRM GameObject
|
||||
context.Load();
|
||||
|
||||
// 4. Enable UpdateWhenOffscreen
|
||||
// https://docs.unity3d.com/2019.4/Documentation/ScriptReference/SkinnedMeshRenderer-updateWhenOffscreen.html
|
||||
context.EnableUpdateWhenOffscreen();
|
||||
|
||||
// 5. Display the model
|
||||
context.ShowMeshes();
|
||||
|
||||
// 6. By calling this function, unity resources such as Texture, Material, Mesh, etc. used by VRM GameObject can be associated
|
||||
// In other words, when the VRM GameObject is destroyed, resources (Texture, Material, Mesh, etc) that are actually used by the VRM GameObject can be destroyed
|
||||
context.DisposeOnGameObjectDestroyed();
|
||||
|
||||
// 7. Return Root GameObject (VRM model)
|
||||
// Root GameObject is where VRMMeta component is attached
|
||||
return context.Root;
|
||||
}
|
||||
// 8. When using statement ends, UnityEngine.Object resources held by VRMImporterContext are destroyed
|
||||
// As mentioned in step 6, the resources associated with the VRM GameObject will not be destroyed
|
||||
// The unused resources (not used by the VRM GameObject), i.e. unassigned textures, will be destroyed
|
||||
}
|
||||
|
||||
private void DestroyVrm(GameObject vrmGameObject)
|
||||
{
|
||||
// 9. Destroy the generated VRM GameObject
|
||||
// If the VRM GameObject is destroyed, the associated unity resources (Texture, Material, Mesh, etc) will be destroyed, too
|
||||
UnityEngine.Object.Destroy(vrmGameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Sample Codes (Asynchronous Loading)
|
||||
|
||||
```cs
|
||||
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. Call GltfParser function (it has been separated from ImporterContext)
|
||||
// We use GltfParser to obtain JSON information and binary data from the VRM file
|
||||
// GltfParser can be run by other than the Unity's main thread
|
||||
var parser = new GltfParser();
|
||||
await Task.Run(() =>
|
||||
{
|
||||
var file = File.ReadAllBytes(vrmFilePath);
|
||||
parser.ParseGlb(file);
|
||||
});
|
||||
|
||||
// 2. Initialize a new VRMImporterContext object and pass `parser` as an argument to it
|
||||
// VRMImporterContext is the class for loading VRM
|
||||
using (var context = new VRMImporterContext(parser))
|
||||
{
|
||||
// 3. Call LoadAsync function to create a VRM GameObject
|
||||
// For loading process it will take several frames
|
||||
await context.LoadAsync();
|
||||
|
||||
// 4. Enable UpdateWhenOffscreen
|
||||
// https://docs.unity3d.com/2019.4/Documentation/ScriptReference/SkinnedMeshRenderer-updateWhenOffscreen.html
|
||||
context.EnableUpdateWhenOffscreen();
|
||||
|
||||
// 5. Display the model
|
||||
context.ShowMeshes();
|
||||
|
||||
// 6. By calling this function, unity resources such as Texture, Material, Mesh, etc. used by VRM GameObject can be associated
|
||||
// In other words, when the VRM GameObject is destroyed, resources (Texture, Material, Mesh, etc) that are actually used by the VRM GameObject can be destroyed
|
||||
context.DisposeOnGameObjectDestroyed();
|
||||
|
||||
// 7. Return Root GameObject (VRM model)
|
||||
// Root GameObject is where VRMMeta component is attached
|
||||
return context.Root;
|
||||
}
|
||||
// 8. When using statement ends, UnityEngine.Object resources held by VRMImporterContext are destroyed
|
||||
// As mentioned in step 6, the resources associated with the VRM GameObject will not be destroyed
|
||||
// The unused resources (not used by the VRM GameObject), i.e. unassigned textures, will be destroyed
|
||||
}
|
||||
|
||||
private void DestroyVrm(GameObject vrmGameObject)
|
||||
{
|
||||
// 9. Destroy the generated VRM GameObject
|
||||
// If the VRM GameObject is destroyed, the associated unity resources (Texture, Material, Mesh, etc) will be destroyed, too
|
||||
UnityEngine.Object.Destroy(vrmGameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
81
docfx/articles/en/vrm0/0_77_runtime_import.md
Normal file
81
docfx/articles/en/vrm0/0_77_runtime_import.md
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
## `Version 0.77~`
|
||||
|
||||
[DisposeOnGameObjectDestroyed](https://github.com/vrm-c/UniVRM/issues/1018)
|
||||
|
||||
`ImporterContext` API has been modified.
|
||||
|
||||
The function `ImporterContext.DisposeOnGameObjectDestroyed` introduced in `Version 0.68` has been discarded.
|
||||
Instead, `ImporterContext.Load` and `RuntimeGltfInstance` are used in `v0.77`.
|
||||
|
||||
In addition, `ImporterContext`'s members have been moved to `RuntimeGltfInstance`:
|
||||
|
||||
* Root
|
||||
* EnableUpdateWhenOffscreen()
|
||||
* ShowMeshes()
|
||||
|
||||
To destroy the Importer, use `ImporterContext.Dispose` after `Load` is called.
|
||||
By destroying RuntimeGltfInstance, the resources associated with the RuntimeGltfInstance (Texture, Material, Mesh, etc) will be destroyed.
|
||||
|
||||
```cs
|
||||
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. Call GltfParser function (it has been separated from ImporterContext)
|
||||
// We use GltfParser to obtain JSON information and binary data from the VRM file
|
||||
var parser = new GltfParser();
|
||||
parser.ParsePath(vrmFilePath);
|
||||
|
||||
// 2. Initialize a new VRMImporterContext object and pass `parser` as an argument to it
|
||||
// The class for loading VRM is VRMImporterContext
|
||||
using (var context = new VRMImporterContext(parser))
|
||||
{
|
||||
// 3. Call Load function to create a VRM GameObject
|
||||
RuntimeGltfInstance instance = context.Load(); // <- `v0.77`
|
||||
|
||||
// For asynchronous loading, use the following line instead:
|
||||
// RuntimeGltfInstance instance = await context.LoadAsync();
|
||||
|
||||
// 4. Enable UpdateWhenOffscreen
|
||||
// https://docs.unity3d.com/2019.4/Documentation/ScriptReference/SkinnedMeshRenderer-updateWhenOffscreen.html
|
||||
instance.EnableUpdateWhenOffscreen(); // This function has been moved from ImporterContext to RuntimeGltfInstance in `v0.77`
|
||||
|
||||
// 5. Display the model
|
||||
instance.ShowMeshes(); // This function has been moved from ImporterContext to RuntimeGltfInstance `v0.77`
|
||||
|
||||
// 6. Return Root GameObject (VRM model)
|
||||
// Root GameObject is where VRMMeta component is attached
|
||||
return instance.Root; // <- changed from ImporterContext to RuntimeGltfInstance in `v0.77`
|
||||
}
|
||||
// 7. When using statement ends, UnityEngine.Object resources held by VRMImporterContext are destroyed
|
||||
// In step 6, the resources associated with the VRM GameObject will not be destroyed
|
||||
// The unused resources included in the glTF file (not used by the VRM GameObject), i.e. unassigned textures, will be destroyed
|
||||
}
|
||||
|
||||
private void DestroyVrm(GameObject vrmGameObject)
|
||||
{
|
||||
// 8. Destroy the generated VRM GameObject
|
||||
// If the VRM GameObject is destroyed, the associated unity resources (Texture, Material, Mesh, etc) will also be destroyed
|
||||
UnityEngine.Object.Destroy(vrmGameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
12
docfx/articles/en/vrm0/0_79_runtime_import.md
Normal file
12
docfx/articles/en/vrm0/0_79_runtime_import.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
## `Version 0.79`
|
||||
|
||||
Separate `GltfData` from `GltfParser`
|
||||
|
||||
```cs
|
||||
var parser = new GltfParser();
|
||||
parser.ParsePath(path);
|
||||
```
|
||||
|
||||
```cs
|
||||
GltfData data = new GlbFileParser(path).Parse();
|
||||
```
|
||||
56
docfx/articles/en/vrm0/0_82_runtime_import.md
Normal file
56
docfx/articles/en/vrm0/0_82_runtime_import.md
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
* `Version 0.82.0`: Please use `0.82.1` or later
|
||||
* `Version 0.82.1~`
|
||||
|
||||
Material replacement for URP when Import is implemented .
|
||||
|
||||
Below step is needed.
|
||||
|
||||
1. Parse VRM, and Get VrmData.
|
||||
1. Create VrmImporter, and load Unity hierarchy.
|
||||
1. Use loaded instance(ShowMeshes)
|
||||
|
||||
# 1. Parse
|
||||
|
||||
`v0.82.1` VrmData is introduced.
|
||||
|
||||
```cs
|
||||
var data = new UniGLTF.GlbFileParser(path).Parse();
|
||||
VrmData vrm = new VRM.VRMData(data);
|
||||
```
|
||||
|
||||
# 2. Load
|
||||
|
||||
`v0.82.1` VrmData is introduced.
|
||||
|
||||
```cs
|
||||
var loader = new VRM.VRMImporterContext(vrm);
|
||||
RuntimeGltfInstance instance = context.Load();
|
||||
```
|
||||
|
||||
## Set `materialGenerator` argument for URP
|
||||
|
||||
Set `materialGenerator` Argument, material generation is could customized.
|
||||
If omitted, default `materialGenerator` for `built-in` pipeline is used.
|
||||
|
||||
```cs
|
||||
var loader = new VRM.VRMImporterContext(vrm, materialGenerator: new VRMUrpMaterialDescriptorGenerator(vrm.VrmExtension));
|
||||
```
|
||||
|
||||
* `MToonShader for URP` has not been implemented yet, fallback to `UniUnlit`.
|
||||
|
||||
# 3. Instance
|
||||
## ShowMeshes
|
||||
|
||||
`v0.77` ShowMeshes method is move to RuntimeGltfInstance from Importer.
|
||||
|
||||
```cs
|
||||
instance.ShowMeshes();
|
||||
```
|
||||
|
||||
## Destroy
|
||||
|
||||
`v0.77` When destroy, related assets(meshes, materials and textures etc...) is destroyed.
|
||||
|
||||
```cs
|
||||
GameObject.Destroy(instance);
|
||||
```
|
||||
87
docfx/articles/en/vrm0/firstperson.md
Normal file
87
docfx/articles/en/vrm0/firstperson.md
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
---
|
||||
title: "How to Use VRMFirstPerson"
|
||||
linkTitle: "How to use first-person mode "
|
||||
date: 2018-05-29T10:00:00+09:00
|
||||
aliases: ["/en/dev/univrm-0.xx/programming/univrm_use_firstperson/"]
|
||||
weight: 5
|
||||
tags: ["api"]
|
||||
---
|
||||
|
||||
# VRMFirstPerson Settings
|
||||
[VRMFirstPerson]({{< relref "univrm_firstperson.md" >}}) has the following settings for Renderer:
|
||||
|
||||
|FirstPersonFlag |Layer |Note |
|
||||
|------------------------------|----------------------|--------------------------------------------|
|
||||
|Both |default |Specify parts that are not necessarily separated between first-person view and third-person view.|
|
||||
|ThirdPersonOnly |VRMThirdPersonOnly|Specify parts that are not rendered in first-person view.|
|
||||
|FirstPersonOnly |VRMFirstPersonOnly|Specify parts that are not rendered in third-person view. The auto-created headless model is used.|
|
||||
|Auto |VRMThirdPersonOnly|Automatically create the model in first-person view at runtime and set it to FIRSTPERSON_ONLY_LAYER.|
|
||||
|
||||
By calling **VRMFirstPerson.Setup** at runtime, the layer settings described above can be performed. Please call the function explicitly from outside.
|
||||
|
||||
# Specify the additional render layers for the application
|
||||
|
||||
The following layers are defined as constant:
|
||||
|
||||
{{< highlight cs >}}
|
||||
public class VRMFirstPerson : MonoBehaviour
|
||||
{
|
||||
public const int FIRSTPERSON_ONLY_LAYER = 9;
|
||||
public const int THIRDPERSON_ONLY_LAYER = 10;
|
||||
|
||||
// The following parts are omitted
|
||||
}
|
||||
{{< / highlight >}}
|
||||
|
||||
|{{< img src="images/vrm/layer_setting.png" >}}|
|
||||
|-----|
|
||||
|Set Layer in #9 and #10|
|
||||
|
||||
# Call Setup function at runtime and set LayerMask in Camera
|
||||
|
||||
* Call VRMFirstPerson.Setup
|
||||
* Set LayerMask for first-person camera view and other camera views
|
||||
|
||||
{{< highlight cs >}}
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using VRM;
|
||||
|
||||
public class SetupExample : MonoBehaviour
|
||||
{
|
||||
[SerializeField]
|
||||
Camera m_firstPersonCamera; // HMD camera
|
||||
|
||||
[SerializeField]
|
||||
LayerMask m_firstPersonMask; // Set a first-person mask (default | VRMFirstPersonOnly, etc.) in HMD camera
|
||||
|
||||
[SerializeField]
|
||||
LayerMask m_otherMask; // Set other masks (default | VRMThirdPersonOnly, etc.) in HMD camera
|
||||
|
||||
[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 initialization
|
||||
if (m_firstPerson != null)
|
||||
{
|
||||
m_firstPerson.Setup();
|
||||
}
|
||||
}
|
||||
}
|
||||
{{< / highlight >}}
|
||||
26
docfx/articles/en/vrm1/vrm1_expression.md
Normal file
26
docfx/articles/en/vrm1/vrm1_expression.md
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
title: 🚧Expression
|
||||
weight: 30
|
||||
---
|
||||
|
||||
表情周りの操作方法。
|
||||
|
||||
> VRM-1.0 の `BlendShapeProxy` は、`VRM10Controller.Expression` になります。
|
||||
|
||||
VRM-0.X の例
|
||||
|
||||
```cs
|
||||
void SetExpression(GameObject root)
|
||||
{
|
||||
var controller = root.GetComponent<BlendShapeProxy>();
|
||||
}
|
||||
```
|
||||
|
||||
VRM-1.0 の例
|
||||
|
||||
```cs
|
||||
void SetExpression(GameObject root)
|
||||
{
|
||||
var controller = root.GetComponent<VRM10Controller>();
|
||||
}
|
||||
```
|
||||
5
docfx/articles/en/vrm1/vrm1_firstperson.md
Normal file
5
docfx/articles/en/vrm1/vrm1_firstperson.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: 🚧FirstPerson
|
||||
weight: 50
|
||||
---
|
||||
|
||||
10
docfx/articles/en/vrm1/vrm1_get_humanoid.md
Normal file
10
docfx/articles/en/vrm1/vrm1_get_humanoid.md
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
title: 🚧Humanoid
|
||||
weight: 20
|
||||
---
|
||||
|
||||
## Humanoid Bone の取得方法
|
||||
|
||||
### Humanoidコンポーネント(vrm-1.0)
|
||||
|
||||
### Animator経由(vrm-0.xとvrm-1.0共通)
|
||||
5
docfx/articles/en/vrm1/vrm1_lookat.md
Normal file
5
docfx/articles/en/vrm1/vrm1_lookat.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: 🚧LookAt
|
||||
weight: 40
|
||||
---
|
||||
|
||||
19
docfx/articles/en/vrm1/vrm1_runtime_load.md
Normal file
19
docfx/articles/en/vrm1/vrm1_runtime_load.md
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
title: 🚧Runtime ロード
|
||||
weight: 10
|
||||
---
|
||||
|
||||
VRM-1.0 を使うアプリケーションは、 `VRM-0.x` の資産もロードしたいはずです。
|
||||
|
||||
`VRM-1.0` と `VRM-0.X` の meta の非互換に [Metaの自動的なマイグレーションは禁止する方針](https://github.com/vrm-c/vrm-specification/issues/181) となりました。
|
||||
|
||||
## VRM-0.X を VRM-1.0 コンポーネントに対してロードする
|
||||
|
||||
`UniVRM-1.0` では、
|
||||
`VRM-0.X` のライセンスを保持したまま、`VRM-1.0` のコンポーネントにロードする機能を提供します(予定)。
|
||||
|
||||
アプリケーションは、この方法でロードしたモデルは、`VRM-0.X` ライセンスとして扱ってください。
|
||||
|
||||
## RuntimeLoad 例
|
||||
|
||||
`vrm-0.x` と `vrm-1.0` 両方をロードしてライセンスを取得する例。
|
||||
3
docfx/articles/index.md
Normal file
3
docfx/articles/index.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
* [日本語](./ja/)
|
||||
* [English](./en/)
|
||||
|
|
@ -1 +0,0 @@
|
|||
# Add your introductions here!
|
||||
9
docfx/articles/ja/gltf/0_82_glb_import.md
Normal file
9
docfx/articles/ja/gltf/0_82_glb_import.md
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
|
||||
## GLB
|
||||
|
||||
`materialGenerator` 引き数(省略可能)を指定することで URP マテリアルを生成するようにカスタムできます。
|
||||
|
||||
```cs
|
||||
var data = new GlbFileParser(path).Parse();
|
||||
var loader = new UniGLTF.ImporterContext(data, materialGenerator: new GltfUrpMaterialDescriptorGenerator());
|
||||
```
|
||||
439
docfx/articles/ja/gltf/how_to_impl_extension.md
Normal file
439
docfx/articles/ja/gltf/how_to_impl_extension.md
Normal file
|
|
@ -0,0 +1,439 @@
|
|||
`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` フィールドを定義するという方法をとっていました。
|
||||
|
||||
```cs
|
||||
class VRM
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
class GLTFExtensions
|
||||
{
|
||||
public VRM VRM;
|
||||
}
|
||||
|
||||
class GLTF
|
||||
{
|
||||
// すべての拡張の型をコンパイル時に知っている必要がある。動的に拡張できない
|
||||
public GLTFExtensions extensions;
|
||||
}
|
||||
```
|
||||
|
||||
この設計だと GLTF と拡張を別ライブラリとして分離することができませんでした。
|
||||
|
||||
`v0.63.1` から設計を変更して、すべての `extensions/extras` に同じ型の入れ物を使うように変更しました。
|
||||
UniGLTF は `import/export` の具体的な内容を知らずに中間データの入れ物として扱います。
|
||||
|
||||
```cs
|
||||
// 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
|
||||
|
||||
```cs
|
||||
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`
|
||||
|
||||
```cs
|
||||
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`
|
||||
|
||||
```cs
|
||||
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?` にするべきだった
|
||||
|
||||
```cs
|
||||
[JsonSchema(Minimum = 0)]
|
||||
int index = -1;
|
||||
```
|
||||
|
||||
のようにすることで、キーの出力を抑制できます。
|
||||
|
||||
```cs
|
||||
// 生成コードのキー出力例
|
||||
if(value.index>=0){
|
||||
```
|
||||
|
||||
何も付けないと
|
||||
|
||||
```cs
|
||||
// 出力制御無し
|
||||
if(true){
|
||||
```
|
||||
|
||||
#### enum のエンコーディング
|
||||
|
||||
enumの値の名前を文字列で使う、enumの値の数値を使うの2種類がありえます。
|
||||
enumの場合はデフォルト値が無いので必須です。
|
||||
|
||||
```cs
|
||||
[JsonSchema(EnumSerializationType = EnumSerializationType.AsInt)]
|
||||
public glBufferTarget target;
|
||||
|
||||
[JsonSchema(EnumSerializationType = EnumSerializationType.AsLowerString)]
|
||||
public ProjectionType type;
|
||||
```
|
||||
|
||||
### JsonSchemaから生成
|
||||
VRM-1.0 の実装
|
||||
|
||||
TODO:
|
||||
49
docfx/articles/ja/history.md
Normal file
49
docfx/articles/ja/history.md
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
# Version
|
||||
|
||||
| version | |
|
||||
|---------|-------------------------------------|
|
||||
| v0.82.1 | Import時のMaterial差し替え(URP対応) |
|
||||
| v0.82.0 | Import時のMaterial差し替え(URP対応) |
|
||||
| v0.77 | ImporterContext の整理 |
|
||||
| v0.68 | ImporterContext の整理 |
|
||||
| v0.63.2 | gltf の extension の実装方法を変更 |
|
||||
| v0.56 | BlendShapeKey の仕様変更 |
|
||||
|
||||
[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)
|
||||
|
||||
## v0.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]
|
||||
```
|
||||
3
docfx/articles/ja/index.md
Normal file
3
docfx/articles/ja/index.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
これは、開発者(UniVRMを使ったアプリケーションを作成する人)向けのドキュメントです。
|
||||
|
||||
UnityでVRMをエクスポートする方法などについては [manual](https://vrm.dev/docs/univrm/) を参照してください。
|
||||
32
docfx/articles/ja/package.md
Normal file
32
docfx/articles/ja/package.md
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
# パッケージ構成
|
||||
|
||||
| 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
|
||||
}
|
||||
}
|
||||
```
|
||||
49
docfx/articles/ja/toc.yml
Normal file
49
docfx/articles/ja/toc.yml
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
- name: 概要
|
||||
href: index.md
|
||||
items:
|
||||
- name: パッケージ構成
|
||||
href: package.md
|
||||
- name: Version
|
||||
href: history.md
|
||||
|
||||
- name: glTF
|
||||
items:
|
||||
- name: GlbImport(0.82)
|
||||
href: gltf/0_82_glb_import.md
|
||||
- name: glTF拡張の実装(0.63.2)
|
||||
href: gltf/how_to_impl_extension.md
|
||||
|
||||
- name: VRM
|
||||
items:
|
||||
- name: 🚧Samples/SimpleViewer
|
||||
- name: 🚧Samples/RuntimeExporerSample
|
||||
- name: 🚧Samples/FirstPersonSample
|
||||
- name: 🚧Samples/AnimationBridgeSample
|
||||
- name: RuntimeImport(0.82)
|
||||
href: vrm0/0_82_runtime_import.md
|
||||
- name: RuntimeImport(0.79)
|
||||
href: vrm0/0_79_runtime_import.md
|
||||
- name: RuntimeImport(0.77)
|
||||
href: vrm0/0_77_runtime_import.md
|
||||
- name: RuntimeImport(0.68)
|
||||
href: vrm0/0_68_runtime_import.md
|
||||
- name: RuntimeImport(0.44)
|
||||
href: vrm0/0_44_runtime_import.md
|
||||
- name: BlendShapeProxy(0.58)
|
||||
href: vrm0/0_58_blendshape.md
|
||||
- name: FirstPerson
|
||||
href: vrm0/firstperson.md
|
||||
|
||||
- name: VRM-1.0(β)
|
||||
items:
|
||||
- name: 🚧Samples/VRM10Viewer
|
||||
- name: 🚧RuntimeImport
|
||||
href: vrm1/vrm1_runtime_load.md
|
||||
- name: 🚧Humanoid
|
||||
href: vrm1/vrm1_get_humanoid.md
|
||||
- name: 🚧Expression
|
||||
href: vrm1/vrm1_expression.md
|
||||
- name: 🚧LookAt
|
||||
href: vrm1/vrm1_lookat.md
|
||||
- name: 🚧FirstPerson
|
||||
href: vrm1/vrm1_firstperson.md
|
||||
161
docfx/articles/ja/vrm0/0_44_runtime_import.md
Normal file
161
docfx/articles/ja/vrm0/0_44_runtime_import.md
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
## `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 >}}
|
||||
138
docfx/articles/ja/vrm0/0_58_blendshape.md
Normal file
138
docfx/articles/ja/vrm0/0_58_blendshape.md
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
---
|
||||
title: BlendShapeProxyの使い方
|
||||
date: 2018-04-16T16:30:00+09:00
|
||||
aliases: [
|
||||
"/dev/univrm-0.xx/programming/univrm_use_blendshape/",
|
||||
"/univrm/programming/how_to_use_blendshapeproxy/",
|
||||
]
|
||||
weight: 3
|
||||
tags: ["api"]
|
||||
---
|
||||
## 環境
|
||||
UniVRM v0.58.0
|
||||
|
||||
## 使用するメソッド
|
||||
|
||||
* [推奨] `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
|
||||
|
||||
簡単なテストプログラムでの利用を想定しています。
|
||||
|
||||
例:
|
||||
|
||||
```cs
|
||||
var proxy = GetComponent<VRMBlendShapeProxy>();
|
||||
|
||||
proxy.ImmediatelySetValue(BlendShapeKey.CreateFromPreset(BlendShapePreset.A), 1.0f);
|
||||
```
|
||||
|
||||
## AccumulateValue + Apply
|
||||
|
||||
例:
|
||||
|
||||
```cs
|
||||
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合成器が必要に応じ呼び出すことを想定しています。
|
||||
|
||||
例:
|
||||
|
||||
```cs
|
||||
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
docfx/articles/ja/vrm0/0_68_runtime_import.md
Normal file
163
docfx/articles/ja/vrm0/0_68_runtime_import.md
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
## `Version 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) を破棄します。
|
||||
|
||||
|
||||
### サンプルコード(同期的ロード)
|
||||
|
||||
```cs
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### サンプルコード(非同期ロード)
|
||||
|
||||
```cs
|
||||
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
docfx/articles/ja/vrm0/0_77_runtime_import.md
Normal file
83
docfx/articles/ja/vrm0/0_77_runtime_import.md
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
## `Version 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) も破棄されます。
|
||||
|
||||
```cs
|
||||
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
docfx/articles/ja/vrm0/0_79_runtime_import.md
Normal file
12
docfx/articles/ja/vrm0/0_79_runtime_import.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
## `Version 0.79`
|
||||
|
||||
`GltfParser` と `GltfData` の分割
|
||||
|
||||
```cs
|
||||
var parser = new GltfParser();
|
||||
parser.ParsePath(path);
|
||||
```
|
||||
|
||||
```cs
|
||||
GltfData data = new GlbFileParser(path).Parse();
|
||||
```
|
||||
56
docfx/articles/ja/vrm0/0_82_runtime_import.md
Normal file
56
docfx/articles/ja/vrm0/0_82_runtime_import.md
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
* `Version 0.82.0` `0.82.1` 以降を使ってください。
|
||||
* `Version 0.82.1~`
|
||||
|
||||
Import時の Material 差し替え機能(URP対応)が実装されました。
|
||||
|
||||
以下の手順で import します。
|
||||
|
||||
1. VRMをパースして、VrmDataを得る
|
||||
1. VrmImporter 作成して、 Unity のヒエラルキーをロードする
|
||||
1. ロードされたインスタンスを使う(ShowMeshes)
|
||||
|
||||
# 1. Parse
|
||||
|
||||
`v0.82.1` VrmData が導入されます。
|
||||
|
||||
```cs
|
||||
var data = new UniGLTF.GlbFileParser(path).Parse();
|
||||
VrmData vrm = new VRM.VRMData(data);
|
||||
```
|
||||
|
||||
# 2. Load
|
||||
|
||||
`v0.82.1` VrmData が導入されます。
|
||||
|
||||
```cs
|
||||
var loader = new VRM.VRMImporterContext(vrm);
|
||||
RuntimeGltfInstance instance = context.Load();
|
||||
```
|
||||
|
||||
## URP 向けに `materialGenerator` を指定する
|
||||
|
||||
`materialGenerator` 引き数(省略可能)を指定することで URP マテリアルを生成するようにカスタムできます。
|
||||
指定しない場合は `built-in` 向けのデフォルトが使用されます。
|
||||
|
||||
```cs
|
||||
var loader = new VRM.VRMImporterContext(vrm, materialGenerator: new VRMUrpMaterialDescriptorGenerator(vrm.VrmExtension));
|
||||
```
|
||||
|
||||
* まだ URP 向け MToonShader が作成されていないので、URP 向け `UniUnlit` にフォールバックします。
|
||||
|
||||
# 3. Instance
|
||||
## ShowMeshes
|
||||
|
||||
`v0.77` ShowMeshes が loader から instance に移動しました。
|
||||
|
||||
```cs
|
||||
instance.ShowMeshes();
|
||||
```
|
||||
|
||||
## Destroy
|
||||
|
||||
`v0.77` 破棄時に関連する Asset を破棄するようになりました。
|
||||
|
||||
```cs
|
||||
GameObject.Destroy(instance);
|
||||
```
|
||||
87
docfx/articles/ja/vrm0/firstperson.md
Normal file
87
docfx/articles/ja/vrm0/firstperson.md
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
---
|
||||
title: "VRMFirstPersonの使い方"
|
||||
linkTitle: "一人称モードの使い方"
|
||||
date: 2018-05-29T10:00:00+09:00
|
||||
aliases: ["/dev/univrm-0.xx/programming/univrm_use_firstperson/"]
|
||||
weight: 5
|
||||
tags: ["api"]
|
||||
---
|
||||
|
||||
# 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 >}}
|
||||
26
docfx/articles/ja/vrm1/vrm1_expression.md
Normal file
26
docfx/articles/ja/vrm1/vrm1_expression.md
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
title: 🚧Expression
|
||||
weight: 30
|
||||
---
|
||||
|
||||
表情周りの操作方法。
|
||||
|
||||
> VRM-1.0 の `BlendShapeProxy` は、`VRM10Controller.Expression` になります。
|
||||
|
||||
VRM-0.X の例
|
||||
|
||||
```cs
|
||||
void SetExpression(GameObject root)
|
||||
{
|
||||
var controller = root.GetComponent<BlendShapeProxy>();
|
||||
}
|
||||
```
|
||||
|
||||
VRM-1.0 の例
|
||||
|
||||
```cs
|
||||
void SetExpression(GameObject root)
|
||||
{
|
||||
var controller = root.GetComponent<VRM10Controller>();
|
||||
}
|
||||
```
|
||||
5
docfx/articles/ja/vrm1/vrm1_firstperson.md
Normal file
5
docfx/articles/ja/vrm1/vrm1_firstperson.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: 🚧FirstPerson
|
||||
weight: 50
|
||||
---
|
||||
|
||||
10
docfx/articles/ja/vrm1/vrm1_get_humanoid.md
Normal file
10
docfx/articles/ja/vrm1/vrm1_get_humanoid.md
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
title: 🚧Humanoid
|
||||
weight: 20
|
||||
---
|
||||
|
||||
## Humanoid Bone の取得方法
|
||||
|
||||
### Humanoidコンポーネント(vrm-1.0)
|
||||
|
||||
### Animator経由(vrm-0.xとvrm-1.0共通)
|
||||
5
docfx/articles/ja/vrm1/vrm1_lookat.md
Normal file
5
docfx/articles/ja/vrm1/vrm1_lookat.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: 🚧LookAt
|
||||
weight: 40
|
||||
---
|
||||
|
||||
19
docfx/articles/ja/vrm1/vrm1_runtime_load.md
Normal file
19
docfx/articles/ja/vrm1/vrm1_runtime_load.md
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
title: 🚧Runtime ロード
|
||||
weight: 10
|
||||
---
|
||||
|
||||
VRM-1.0 を使うアプリケーションは、 `VRM-0.x` の資産もロードしたいはずです。
|
||||
|
||||
`VRM-1.0` と `VRM-0.X` の meta の非互換に [Metaの自動的なマイグレーションは禁止する方針](https://github.com/vrm-c/vrm-specification/issues/181) となりました。
|
||||
|
||||
## VRM-0.X を VRM-1.0 コンポーネントに対してロードする
|
||||
|
||||
`UniVRM-1.0` では、
|
||||
`VRM-0.X` のライセンスを保持したまま、`VRM-1.0` のコンポーネントにロードする機能を提供します(予定)。
|
||||
|
||||
アプリケーションは、この方法でロードしたモデルは、`VRM-0.X` ライセンスとして扱ってください。
|
||||
|
||||
## RuntimeLoad 例
|
||||
|
||||
`vrm-0.x` と `vrm-1.0` 両方をロードしてライセンスを取得する例。
|
||||
|
|
@ -1,2 +1,7 @@
|
|||
- name: Introduction
|
||||
href: intro.md
|
||||
- name: Index
|
||||
href: index.md
|
||||
items:
|
||||
- name: English
|
||||
href: en
|
||||
- name: Japanese
|
||||
href: ja
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
# UniVRM Programming Document
|
||||
|
||||
index
|
||||
* [日本語](./articles/ja/)
|
||||
* [English](./articles/en/)
|
||||
|
|
|
|||
|
|
@ -48,3 +48,28 @@ def meta_build(c):
|
|||
'''
|
||||
c.run("docfx metadata")
|
||||
c.run("docfx build")
|
||||
|
||||
|
||||
def diff_dir(l_root: pathlib.Path, lhs: pathlib.Path, r_root: pathlib.Path,
|
||||
rhs: pathlib.Path) -> None:
|
||||
right = [x.relative_to(r_root) for x in rhs.iterdir()]
|
||||
for l in lhs.iterdir():
|
||||
relative = l.relative_to(l_root)
|
||||
if relative in right:
|
||||
right.remove(relative)
|
||||
|
||||
if l.is_dir():
|
||||
diff_dir(l_root, l, r_root, r_root / relative)
|
||||
else:
|
||||
# only left
|
||||
print(f'<only(ja) {l}')
|
||||
# only right
|
||||
for r in right:
|
||||
print(f'>only(en) {r}')
|
||||
|
||||
|
||||
@task
|
||||
def lang_diff(c):
|
||||
ja = HERE / 'articles/ja'
|
||||
en = HERE / 'articles/en'
|
||||
diff_dir(ja, ja, en, en)
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ class MyHandler(PatternMatchingEventHandler):
|
|||
def watch():
|
||||
event_handler = MyHandler()
|
||||
observer = Observer()
|
||||
observer.schedule(event_handler, HERE, recursive=True)
|
||||
observer.schedule(event_handler, HERE / 'articles', recursive=True)
|
||||
observer.start()
|
||||
try:
|
||||
while True:
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user