質問するログイン新規登録

回答編集履歴

6

マッスル番号が見つからなかった場合の値が「max」であるべきところが「min」になっている...実際の動作では使われることがない値だが、一応修正

2019/01/20 21:35

投稿

Bongo
Bongo

スコア10816

answer CHANGED
@@ -57,9 +57,9 @@
57
57
  : limit.min;
58
58
  var max = useDefault
59
59
  ? new Vector3(
60
- muscleIndexX >= 0 ? HumanTrait.GetMuscleDefaultMax(muscleIndexX) : limit.min.x,
60
+ muscleIndexX >= 0 ? HumanTrait.GetMuscleDefaultMax(muscleIndexX) : limit.max.x,
61
- muscleIndexY >= 0 ? HumanTrait.GetMuscleDefaultMax(muscleIndexY) : limit.min.y,
61
+ muscleIndexY >= 0 ? HumanTrait.GetMuscleDefaultMax(muscleIndexY) : limit.max.y,
62
- muscleIndexZ >= 0 ? HumanTrait.GetMuscleDefaultMax(muscleIndexZ) : limit.min.z)
62
+ muscleIndexZ >= 0 ? HumanTrait.GetMuscleDefaultMax(muscleIndexZ) : limit.max.z)
63
63
  : limit.max;
64
64
 
65
65
  // カスタム値が使われている場合は青文字で表示することにしました

5

実行時のマッスル可動範囲取得の案を追記

2019/01/20 21:35

投稿

Bongo
Bongo

スコア10816

answer CHANGED
@@ -99,4 +99,103 @@
99
99
  プロジェクトビュー内でモデルファイルを選択し「Utility」→「Print HumanDescription」を実行すると、下図のような出力結果が得られました。
100
100
  ![Ethan](436cb658c8995acbc2c02aef90984845.png)
101
101
  エディタスクリプトでは何とかなりそうでしたが、実際の実行時に[Avatar](https://docs.unity3d.com/ja/current/ScriptReference/Avatar.html)からHumanDescriptionを取り出すのはやっかいそうです。探した限りではそれらしきクラス・メソッドは見つからず、ModelImporterがどのようにHumanDescriptionを作っているかの詳細も公開されていないようで、もっと詳しく調査・解析する必要がありそうです(もしかすると不可能かもしれません)。
102
- 逆に、実行時にHumanDescriptionをもとにAvatarを作ることは[AvatarBuilder](https://docs.unity3d.com/ja/current/ScriptReference/AvatarBuilder.html)を使えば可能らしいんですがね...
102
+ 逆に、実行時にHumanDescriptionをもとにAvatarを作ることは[AvatarBuilder](https://docs.unity3d.com/ja/current/ScriptReference/AvatarBuilder.html)を使えば可能らしいんですがね...
103
+
104
+ #追記
105
+ UnityEditorに頼らず実行時に値を取得する方法はないものか調べてみたものの、結局AvatarからのHumanDescription取得は困難そうでしたのであきらめました。
106
+ 代案として、モデルのマッスルを最大・最小まで動かし、部位の回転を調べることでマッスル可動範囲の上限・下限を求められないかと思い、ちょっと試してみました。
107
+
108
+ モデルに対して特定のマッスル値を持つポーズを取らせるのは、[HumanPoseHandler](https://docs.unity3d.com/ja/current/ScriptReference/HumanPoseHandler.html)を使えば可能なようです。そこで、各マッスルを1つずつ動かして、回転が起こった部位の角度変化を見てみました。
109
+ 下記スクリプトをモデルにアタッチして実行したところ...
110
+ ```C#
111
+ using System.Linq;
112
+ using UnityEngine;
113
+
114
+ [RequireComponent(typeof(Animator))]
115
+ public class MuscleRangeInvestigator : MonoBehaviour
116
+ {
117
+ private void Start()
118
+ {
119
+ var animator = this.GetComponent<Animator>();
120
+ var avatar = animator.avatar;
121
+ Debug.Log($"Muscle ranges of {avatar.name}:");
122
+
123
+ // マッスル名一覧を取得
124
+ var muscleNames = HumanTrait.MuscleName;
125
+ var muscleCount = HumanTrait.MuscleCount;
126
+
127
+ // アバターと紐付いたポーズハンドラーを作成
128
+ var poseHandler = new HumanPoseHandler(avatar, this.transform);
129
+
130
+ // 現在のポーズを覚えておく
131
+ // 後で姿勢を復元する際に、リターゲッティング処理の都合上姿勢がずれる可能性が
132
+ // あるらしいので、ポーズを覚える前に親・位置・回転をそれぞれ覚えておき
133
+ // 親子関係を一旦解除、位置・回転もゼロにする
134
+ var initialLocalPosition = this.transform.localPosition;
135
+ var initialLocalRotation = this.transform.localRotation;
136
+ var initialParent = this.transform.parent;
137
+ this.transform.SetParent(null);
138
+ this.transform.localPosition = Vector3.zero;
139
+ this.transform.localRotation = Quaternion.identity;
140
+ var initialPose = new HumanPose();
141
+ poseHandler.GetHumanPose(ref initialPose);
142
+
143
+ // マッスル値をいじって実験するためのポーズを作る
144
+ var pose = new HumanPose
145
+ {
146
+ bodyPosition = Vector3.zero,
147
+ bodyRotation = Quaternion.identity,
148
+ muscles = new float[initialPose.muscles.Length]
149
+ };
150
+
151
+ // モデルルート以下の全Transformを配列化
152
+ var transforms = this.GetComponentsInChildren<Transform>();
153
+
154
+ // ゼロ姿勢(膝や腕を半曲げにした、あの姿勢)をセット
155
+ poseHandler.SetHumanPose(ref pose);
156
+
157
+ // ゼロ姿勢でのローカル回転を覚えておく
158
+ var zeroTransforms = transforms.Select(t => (Transform: t, Rotation: t.localRotation)).ToArray();
159
+
160
+ // 全マッスルについて...
161
+ for (var i = 0; i < muscleCount; i++)
162
+ {
163
+ // まずi番目マッスルを1にし、姿勢を適用
164
+ pose.muscles[i] = 1.0f;
165
+ poseHandler.SetHumanPose(ref pose);
166
+
167
+ // 回転が発生したTransformを抜き出し、おそらくそれらのうち
168
+ // 最大の角度変化量を可動角度上限として採用すればいいと思う...?
169
+ var max = zeroTransforms.Where(t => t.Rotation != t.Transform.localRotation)
170
+ .Select(t => Quaternion.Angle(t.Transform.localRotation, t.Rotation))
171
+ .OrderByDescending(a => a).FirstOrDefault();
172
+
173
+ // 引き続きi番目マッスルを-1にし、姿勢を適用
174
+ pose.muscles[i] = -1.0f;
175
+ poseHandler.SetHumanPose(ref pose);
176
+
177
+ // 同じく最大の角度変化量を求め、符号を反転して可動角度下限として採用...?
178
+ var min = -zeroTransforms.Where(t => t.Rotation != t.Transform.localRotation)
179
+ .Select(t => Quaternion.Angle(t.Transform.localRotation, t.Rotation))
180
+ .OrderByDescending(a => a).FirstOrDefault();
181
+
182
+ // コンソールに結果を表示
183
+ Debug.Log($"\t{muscleNames[i]}: {min:F1}~{max:F1}");
184
+
185
+ // i番目マッスルを0に戻す
186
+ pose.muscles[i] = 0.0f;
187
+ }
188
+
189
+ // 姿勢を元に戻して終わり
190
+ poseHandler.SetHumanPose(ref initialPose);
191
+ this.transform.SetParent(initialParent);
192
+ this.transform.localPosition = initialLocalPosition;
193
+ this.transform.localRotation = initialLocalRotation;
194
+ }
195
+ }
196
+ ```
197
+ 下図のように、どうやらそれらしい値が得られました。ですが少々ごまかしがありまして、表示される値を小数点以下第1位で丸めています。実際の値はインスペクタ上の値とぴったり一致はしませんでした。おそらく計算誤差によるものでしょう...
198
+ ![EthanAvatar](57b06e494b36de8fca8e01b46a19ca32.png)
199
+ ※おまけ
200
+ 各マッスルを動かしているときにそれぞれどのような姿勢になっているか見てみたところ、下図のようにEthanとユニティちゃんが体操する姿を撮影できました。ちょうどインスペクタ上の各マッスルのプレビュースライダーを操作したときと同様の動きをしていることがお分かりいただけるでしょうか?
201
+ ![体操](12a240f964e9e0721e4311ef200a48df.gif)

4

明らかに顎や目のデフォルト値がおかしい...リファレンスのサンプルコードは誤っている?おそらく正しいと思われる形に変更、スクリーンショット差し替え

2019/01/18 17:37

投稿

Bongo
Bongo

スコア10816

answer CHANGED
@@ -45,32 +45,42 @@
45
45
  var muscleIndexX = HumanTrait.MuscleFromBone(boneIndex, 0);
46
46
  var muscleIndexY = HumanTrait.MuscleFromBone(boneIndex, 1);
47
47
  var muscleIndexZ = HumanTrait.MuscleFromBone(boneIndex, 2);
48
+
49
+ // デフォルト値を使うよう設定されている場合は、limit内のmin・maxは無視するらしい?
48
50
  var limit = humanBone.limit;
49
-
50
- // デフォルト値を使うよう設定されている場合は、limit内のmin・maxは無視するらしい?
51
51
  var useDefault = limit.useDefaultValues;
52
+ var min = useDefault
53
+ ? new Vector3(
52
- var defaultMin = HumanTrait.GetMuscleDefaultMin(boneIndex);
54
+ muscleIndexX >= 0 ? HumanTrait.GetMuscleDefaultMin(muscleIndexX) : limit.min.x,
55
+ muscleIndexY >= 0 ? HumanTrait.GetMuscleDefaultMin(muscleIndexY) : limit.min.y,
56
+ muscleIndexZ >= 0 ? HumanTrait.GetMuscleDefaultMin(muscleIndexZ) : limit.min.z)
57
+ : limit.min;
58
+ var max = useDefault
59
+ ? new Vector3(
53
- var defaultMax = HumanTrait.GetMuscleDefaultMax(boneIndex);
60
+ muscleIndexX >= 0 ? HumanTrait.GetMuscleDefaultMax(muscleIndexX) : limit.min.x,
54
- var min = useDefault ? new Vector3(defaultMin, defaultMin, defaultMin) : limit.min;
55
- var max = useDefault ? new Vector3(defaultMax, defaultMax, defaultMax) : limit.max;
61
+ muscleIndexY >= 0 ? HumanTrait.GetMuscleDefaultMax(muscleIndexY) : limit.min.y,
62
+ muscleIndexZ >= 0 ? HumanTrait.GetMuscleDefaultMax(muscleIndexZ) : limit.min.z)
63
+ : limit.max;
56
64
 
57
65
  // カスタム値が使われている場合は青文字で表示することにしました
58
66
  // インスペクタで範囲を個別に編集すると、その項目のuseDefaultValuesがfalseになるらしい?
59
67
  var minMaxColor = useDefault ? "<color=black>" : "<color=blue>";
60
-
61
68
  if (muscleIndexX >= 0)
62
69
  {
70
+ Debug.Log(
63
- Debug.Log($"\t\t{HumanTrait.MuscleName[muscleIndexX]}: {minMaxColor}{min.x}~{max.x}</color>");
71
+ $"\t\t{HumanTrait.MuscleName[muscleIndexX]}: {minMaxColor}{min.x}~{max.x}</color>");
64
72
  }
65
73
 
66
74
  if (muscleIndexY >= 0)
67
75
  {
76
+ Debug.Log(
68
- Debug.Log($"\t\t{HumanTrait.MuscleName[muscleIndexY]}: {minMaxColor}{min.y}~{max.y}</color>");
77
+ $"\t\t{HumanTrait.MuscleName[muscleIndexY]}: {minMaxColor}{min.y}~{max.y}</color>");
69
78
  }
70
79
 
71
80
  if (muscleIndexZ >= 0)
72
81
  {
82
+ Debug.Log(
73
- Debug.Log($"\t\t{HumanTrait.MuscleName[muscleIndexZ]}: {minMaxColor}{min.z}~{max.z}</color>");
83
+ $"\t\t{HumanTrait.MuscleName[muscleIndexZ]}: {minMaxColor}{min.z}~{max.z}</color>");
74
84
  }
75
85
  }
76
86
 
@@ -87,6 +97,6 @@
87
97
  }
88
98
  ```
89
99
  プロジェクトビュー内でモデルファイルを選択し「Utility」→「Print HumanDescription」を実行すると、下図のような出力結果が得られました。
90
- ![Ethan](127dce58c4d9e702344e8bce71272afe.png)
100
+ ![Ethan](436cb658c8995acbc2c02aef90984845.png)
91
101
  エディタスクリプトでは何とかなりそうでしたが、実際の実行時に[Avatar](https://docs.unity3d.com/ja/current/ScriptReference/Avatar.html)からHumanDescriptionを取り出すのはやっかいそうです。探した限りではそれらしきクラス・メソッドは見つからず、ModelImporterがどのようにHumanDescriptionを作っているかの詳細も公開されていないようで、もっと詳しく調査・解析する必要がありそうです(もしかすると不可能かもしれません)。
92
102
  逆に、実行時にHumanDescriptionをもとにAvatarを作ることは[AvatarBuilder](https://docs.unity3d.com/ja/current/ScriptReference/AvatarBuilder.html)を使えば可能らしいんですがね...

3

微修正、ボーンインデックス探索ループにbreak追加

2019/01/17 03:08

投稿

Bongo
Bongo

スコア10816

answer CHANGED
@@ -32,6 +32,7 @@
32
32
  if (HumanTrait.BoneName[j] == humanName)
33
33
  {
34
34
  boneIndex = j;
35
+ break;
35
36
  }
36
37
  }
37
38
 

2

デフォルト値取得の誤りを訂正、実行結果スクリーンショットを差し替え

2019/01/16 22:34

投稿

Bongo
Bongo

スコア10816

answer CHANGED
@@ -21,27 +21,56 @@
21
21
  var humanDescription = importer.humanDescription;
22
22
  Debug.Log($"HumanDescription for {model.name}:");
23
23
  Debug.Log("\tPer-Muscle Settings:");
24
- for (var i = 0; i < humanDescription.human.Length; i++)
24
+ foreach (var humanBone in humanDescription.human)
25
25
  {
26
- var humanBone = humanDescription.human[i];
26
+ var humanName = humanBone.humanName;
27
+
28
+ // ボーン名一覧を見て、対応するボーン番号を見つける
29
+ var boneIndex = -1;
30
+ for (var j = 0; j < HumanTrait.BoneCount; j++)
31
+ {
32
+ if (HumanTrait.BoneName[j] == humanName)
33
+ {
34
+ boneIndex = j;
35
+ }
36
+ }
37
+
38
+ if (boneIndex < 0)
39
+ {
40
+ continue;
41
+ }
42
+
43
+ // ボーンインデックスの各軸に対応するマッスル番号を見つける
44
+ var muscleIndexX = HumanTrait.MuscleFromBone(boneIndex, 0);
45
+ var muscleIndexY = HumanTrait.MuscleFromBone(boneIndex, 1);
46
+ var muscleIndexZ = HumanTrait.MuscleFromBone(boneIndex, 2);
27
47
  var limit = humanBone.limit;
28
-
48
+
29
49
  // デフォルト値を使うよう設定されている場合は、limit内のmin・maxは無視するらしい?
30
50
  var useDefault = limit.useDefaultValues;
31
- var defaultMin = HumanTrait.GetMuscleDefaultMin(i);
51
+ var defaultMin = HumanTrait.GetMuscleDefaultMin(boneIndex);
32
- var defaultMax = HumanTrait.GetMuscleDefaultMax(i);
52
+ var defaultMax = HumanTrait.GetMuscleDefaultMax(boneIndex);
33
53
  var min = useDefault ? new Vector3(defaultMin, defaultMin, defaultMin) : limit.min;
34
54
  var max = useDefault ? new Vector3(defaultMax, defaultMax, defaultMax) : limit.max;
35
55
 
36
56
  // カスタム値が使われている場合は青文字で表示することにしました
37
57
  // インスペクタで範囲を個別に編集すると、その項目のuseDefaultValuesがfalseになるらしい?
38
58
  var minMaxColor = useDefault ? "<color=black>" : "<color=blue>";
59
+
39
-
60
+ if (muscleIndexX >= 0)
61
+ {
40
- // 多分xがTwist Left-Right、yがLeft-Right、zがFront-Backだと思いますが
62
+ Debug.Log($"\t\t{HumanTrait.MuscleName[muscleIndexX]}: {minMaxColor}{min.x}~{max.x}</color>");
63
+ }
64
+
41
- // 全項目についてちゃんと対応関係を確認したわけではないのでご注意ください
65
+ if (muscleIndexY >= 0)
66
+ {
67
+ Debug.Log($"\t\t{HumanTrait.MuscleName[muscleIndexY]}: {minMaxColor}{min.y}~{max.y}</color>");
68
+ }
69
+
70
+ if (muscleIndexZ >= 0)
71
+ {
42
- Debug.Log($"\t\t{humanBone.humanName} Front-Back: {minMaxColor}{min.z}~{max.z}</color>");
72
+ Debug.Log($"\t\t{HumanTrait.MuscleName[muscleIndexZ]}: {minMaxColor}{min.z}~{max.z}</color>");
43
- Debug.Log($"\t\t{humanBone.humanName} Left-Right: {minMaxColor}{min.y}~{max.y}</color>");
73
+ }
44
- Debug.Log($"\t\t{humanBone.humanName} Twist Left-Right: {minMaxColor}{min.x}~{max.x}</color>");
45
74
  }
46
75
 
47
76
  Debug.Log("\tAdditional Settings:");
@@ -57,8 +86,6 @@
57
86
  }
58
87
  ```
59
88
  プロジェクトビュー内でモデルファイルを選択し「Utility」→「Print HumanDescription」を実行すると、下図のような出力結果が得られました。
60
- インスペクタ上では、部位によって設定項目名が「Down-Up」「In-Out」だったり「Stretch」「Twist In-Out」だったりとバラバラですが、手抜きして全部「Front-Back」「Left-Right」「Twist Left-Right」としています。すみません...
61
- ちゃんと対応づけるには、部位ごとにどの値を動かすとHumanDescriptionのどの値が変化するかを確認してやる必要がありそうです。
62
- ![Ethan](5c28434208e8e21b723d527f258e5725.png)
89
+ ![Ethan](127dce58c4d9e702344e8bce71272afe.png)
63
90
  エディタスクリプトでは何とかなりそうでしたが、実際の実行時に[Avatar](https://docs.unity3d.com/ja/current/ScriptReference/Avatar.html)からHumanDescriptionを取り出すのはやっかいそうです。探した限りではそれらしきクラス・メソッドは見つからず、ModelImporterがどのようにHumanDescriptionを作っているかの詳細も公開されていないようで、もっと詳しく調査・解析する必要がありそうです(もしかすると不可能かもしれません)。
64
91
  逆に、実行時にHumanDescriptionをもとにAvatarを作ることは[AvatarBuilder](https://docs.unity3d.com/ja/current/ScriptReference/AvatarBuilder.html)を使えば可能らしいんですがね...

1

一部項目名が違うことについて弁解

2019/01/16 22:31

投稿

Bongo
Bongo

スコア10816

answer CHANGED
@@ -57,6 +57,8 @@
57
57
  }
58
58
  ```
59
59
  プロジェクトビュー内でモデルファイルを選択し「Utility」→「Print HumanDescription」を実行すると、下図のような出力結果が得られました。
60
+ インスペクタ上では、部位によって設定項目名が「Down-Up」「In-Out」だったり「Stretch」「Twist In-Out」だったりとバラバラですが、手抜きして全部「Front-Back」「Left-Right」「Twist Left-Right」としています。すみません...
61
+ ちゃんと対応づけるには、部位ごとにどの値を動かすとHumanDescriptionのどの値が変化するかを確認してやる必要がありそうです。
60
62
  ![Ethan](5c28434208e8e21b723d527f258e5725.png)
61
- が、実際の実行時に[Avatar](https://docs.unity3d.com/ja/current/ScriptReference/Avatar.html)からHumanDescriptionを取り出すのはやっかいそうです。探した限りではそれらしきクラス・メソッドは見つからず、ModelImporterがどのようにHumanDescriptionを作っているかの詳細も公開されていないようで、もっと詳しく調査・解析する必要がありそうです(もしかすると不可能かもしれません)。
63
+ エディタスクリプトは何とかなりそうでしたが、実際の実行時に[Avatar](https://docs.unity3d.com/ja/current/ScriptReference/Avatar.html)からHumanDescriptionを取り出すのはやっかいそうです。探した限りではそれらしきクラス・メソッドは見つからず、ModelImporterがどのようにHumanDescriptionを作っているかの詳細も公開されていないようで、もっと詳しく調査・解析する必要がありそうです(もしかすると不可能かもしれません)。
62
64
  逆に、実行時にHumanDescriptionをもとにAvatarを作ることは[AvatarBuilder](https://docs.unity3d.com/ja/current/ScriptReference/AvatarBuilder.html)を使えば可能らしいんですがね...