回答編集履歴

6

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

2019/01/20 21:35

投稿

Bongo
Bongo

スコア10807

test CHANGED
@@ -116,11 +116,11 @@
116
116
 
117
117
  ? new Vector3(
118
118
 
119
- muscleIndexX >= 0 ? HumanTrait.GetMuscleDefaultMax(muscleIndexX) : limit.min.x,
119
+ muscleIndexX >= 0 ? HumanTrait.GetMuscleDefaultMax(muscleIndexX) : limit.max.x,
120
-
120
+
121
- muscleIndexY >= 0 ? HumanTrait.GetMuscleDefaultMax(muscleIndexY) : limit.min.y,
121
+ muscleIndexY >= 0 ? HumanTrait.GetMuscleDefaultMax(muscleIndexY) : limit.max.y,
122
-
122
+
123
- muscleIndexZ >= 0 ? HumanTrait.GetMuscleDefaultMax(muscleIndexZ) : limit.min.z)
123
+ muscleIndexZ >= 0 ? HumanTrait.GetMuscleDefaultMax(muscleIndexZ) : limit.max.z)
124
124
 
125
125
  : limit.max;
126
126
 

5

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

2019/01/20 21:35

投稿

Bongo
Bongo

スコア10807

test CHANGED
@@ -201,3 +201,201 @@
201
201
  エディタスクリプトでは何とかなりそうでしたが、実際の実行時に[Avatar](https://docs.unity3d.com/ja/current/ScriptReference/Avatar.html)からHumanDescriptionを取り出すのはやっかいそうです。探した限りではそれらしきクラス・メソッドは見つからず、ModelImporterがどのようにHumanDescriptionを作っているかの詳細も公開されていないようで、もっと詳しく調査・解析する必要がありそうです(もしかすると不可能かもしれません)。
202
202
 
203
203
  逆に、実行時にHumanDescriptionをもとにAvatarを作ることは[AvatarBuilder](https://docs.unity3d.com/ja/current/ScriptReference/AvatarBuilder.html)を使えば可能らしいんですがね...
204
+
205
+
206
+
207
+ #追記
208
+
209
+ UnityEditorに頼らず実行時に値を取得する方法はないものか調べてみたものの、結局AvatarからのHumanDescription取得は困難そうでしたのであきらめました。
210
+
211
+ 代案として、モデルのマッスルを最大・最小まで動かし、部位の回転を調べることでマッスル可動範囲の上限・下限を求められないかと思い、ちょっと試してみました。
212
+
213
+
214
+
215
+ モデルに対して特定のマッスル値を持つポーズを取らせるのは、[HumanPoseHandler](https://docs.unity3d.com/ja/current/ScriptReference/HumanPoseHandler.html)を使えば可能なようです。そこで、各マッスルを1つずつ動かして、回転が起こった部位の角度変化を見てみました。
216
+
217
+ 下記スクリプトをモデルにアタッチして実行したところ...
218
+
219
+ ```C#
220
+
221
+ using System.Linq;
222
+
223
+ using UnityEngine;
224
+
225
+
226
+
227
+ [RequireComponent(typeof(Animator))]
228
+
229
+ public class MuscleRangeInvestigator : MonoBehaviour
230
+
231
+ {
232
+
233
+ private void Start()
234
+
235
+ {
236
+
237
+ var animator = this.GetComponent<Animator>();
238
+
239
+ var avatar = animator.avatar;
240
+
241
+ Debug.Log($"Muscle ranges of {avatar.name}:");
242
+
243
+
244
+
245
+ // マッスル名一覧を取得
246
+
247
+ var muscleNames = HumanTrait.MuscleName;
248
+
249
+ var muscleCount = HumanTrait.MuscleCount;
250
+
251
+
252
+
253
+ // アバターと紐付いたポーズハンドラーを作成
254
+
255
+ var poseHandler = new HumanPoseHandler(avatar, this.transform);
256
+
257
+
258
+
259
+ // 現在のポーズを覚えておく
260
+
261
+ // 後で姿勢を復元する際に、リターゲッティング処理の都合上姿勢がずれる可能性が
262
+
263
+ // あるらしいので、ポーズを覚える前に親・位置・回転をそれぞれ覚えておき
264
+
265
+ // 親子関係を一旦解除、位置・回転もゼロにする
266
+
267
+ var initialLocalPosition = this.transform.localPosition;
268
+
269
+ var initialLocalRotation = this.transform.localRotation;
270
+
271
+ var initialParent = this.transform.parent;
272
+
273
+ this.transform.SetParent(null);
274
+
275
+ this.transform.localPosition = Vector3.zero;
276
+
277
+ this.transform.localRotation = Quaternion.identity;
278
+
279
+ var initialPose = new HumanPose();
280
+
281
+ poseHandler.GetHumanPose(ref initialPose);
282
+
283
+
284
+
285
+ // マッスル値をいじって実験するためのポーズを作る
286
+
287
+ var pose = new HumanPose
288
+
289
+ {
290
+
291
+ bodyPosition = Vector3.zero,
292
+
293
+ bodyRotation = Quaternion.identity,
294
+
295
+ muscles = new float[initialPose.muscles.Length]
296
+
297
+ };
298
+
299
+
300
+
301
+ // モデルルート以下の全Transformを配列化
302
+
303
+ var transforms = this.GetComponentsInChildren<Transform>();
304
+
305
+
306
+
307
+ // ゼロ姿勢(膝や腕を半曲げにした、あの姿勢)をセット
308
+
309
+ poseHandler.SetHumanPose(ref pose);
310
+
311
+
312
+
313
+ // ゼロ姿勢でのローカル回転を覚えておく
314
+
315
+ var zeroTransforms = transforms.Select(t => (Transform: t, Rotation: t.localRotation)).ToArray();
316
+
317
+
318
+
319
+ // 全マッスルについて...
320
+
321
+ for (var i = 0; i < muscleCount; i++)
322
+
323
+ {
324
+
325
+ // まずi番目マッスルを1にし、姿勢を適用
326
+
327
+ pose.muscles[i] = 1.0f;
328
+
329
+ poseHandler.SetHumanPose(ref pose);
330
+
331
+
332
+
333
+ // 回転が発生したTransformを抜き出し、おそらくそれらのうち
334
+
335
+ // 最大の角度変化量を可動角度上限として採用すればいいと思う...?
336
+
337
+ var max = zeroTransforms.Where(t => t.Rotation != t.Transform.localRotation)
338
+
339
+ .Select(t => Quaternion.Angle(t.Transform.localRotation, t.Rotation))
340
+
341
+ .OrderByDescending(a => a).FirstOrDefault();
342
+
343
+
344
+
345
+ // 引き続きi番目マッスルを-1にし、姿勢を適用
346
+
347
+ pose.muscles[i] = -1.0f;
348
+
349
+ poseHandler.SetHumanPose(ref pose);
350
+
351
+
352
+
353
+ // 同じく最大の角度変化量を求め、符号を反転して可動角度下限として採用...?
354
+
355
+ var min = -zeroTransforms.Where(t => t.Rotation != t.Transform.localRotation)
356
+
357
+ .Select(t => Quaternion.Angle(t.Transform.localRotation, t.Rotation))
358
+
359
+ .OrderByDescending(a => a).FirstOrDefault();
360
+
361
+
362
+
363
+ // コンソールに結果を表示
364
+
365
+ Debug.Log($"\t{muscleNames[i]}: {min:F1}~{max:F1}");
366
+
367
+
368
+
369
+ // i番目マッスルを0に戻す
370
+
371
+ pose.muscles[i] = 0.0f;
372
+
373
+ }
374
+
375
+
376
+
377
+ // 姿勢を元に戻して終わり
378
+
379
+ poseHandler.SetHumanPose(ref initialPose);
380
+
381
+ this.transform.SetParent(initialParent);
382
+
383
+ this.transform.localPosition = initialLocalPosition;
384
+
385
+ this.transform.localRotation = initialLocalRotation;
386
+
387
+ }
388
+
389
+ }
390
+
391
+ ```
392
+
393
+ 下図のように、どうやらそれらしい値が得られました。ですが少々ごまかしがありまして、表示される値を小数点以下第1位で丸めています。実際の値はインスペクタ上の値とぴったり一致はしませんでした。おそらく計算誤差によるものでしょう...
394
+
395
+ ![EthanAvatar](57b06e494b36de8fca8e01b46a19ca32.png)
396
+
397
+ ※おまけ
398
+
399
+ 各マッスルを動かしているときにそれぞれどのような姿勢になっているか見てみたところ、下図のようにEthanとユニティちゃんが体操する姿を撮影できました。ちょうどインスペクタ上の各マッスルのプレビュースライダーを操作したときと同様の動きをしていることがお分かりいただけるでしょうか?
400
+
401
+ ![体操](12a240f964e9e0721e4311ef200a48df.gif)

4

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

2019/01/18 17:37

投稿

Bongo
Bongo

スコア10807

test CHANGED
@@ -92,21 +92,37 @@
92
92
 
93
93
  var muscleIndexZ = HumanTrait.MuscleFromBone(boneIndex, 2);
94
94
 
95
+
96
+
97
+ // デフォルト値を使うよう設定されている場合は、limit内のmin・maxは無視するらしい?
98
+
95
99
  var limit = humanBone.limit;
96
100
 
97
-
98
-
99
- // デフォルト値を使うよう設定されている場合は、limit内のmin・maxは無視するらしい?
100
-
101
101
  var useDefault = limit.useDefaultValues;
102
102
 
103
+ var min = useDefault
104
+
105
+ ? new Vector3(
106
+
103
- var defaultMin = HumanTrait.GetMuscleDefaultMin(boneIndex);
107
+ muscleIndexX >= 0 ? HumanTrait.GetMuscleDefaultMin(muscleIndexX) : limit.min.x,
108
+
104
-
109
+ muscleIndexY >= 0 ? HumanTrait.GetMuscleDefaultMin(muscleIndexY) : limit.min.y,
110
+
111
+ muscleIndexZ >= 0 ? HumanTrait.GetMuscleDefaultMin(muscleIndexZ) : limit.min.z)
112
+
113
+ : limit.min;
114
+
115
+ var max = useDefault
116
+
117
+ ? new Vector3(
118
+
105
- var defaultMax = HumanTrait.GetMuscleDefaultMax(boneIndex);
119
+ muscleIndexX >= 0 ? HumanTrait.GetMuscleDefaultMax(muscleIndexX) : limit.min.x,
106
-
107
- var min = useDefault ? new Vector3(defaultMin, defaultMin, defaultMin) : limit.min;
120
+
108
-
109
- var max = useDefault ? new Vector3(defaultMax, defaultMax, defaultMax) : limit.max;
121
+ muscleIndexY >= 0 ? HumanTrait.GetMuscleDefaultMax(muscleIndexY) : limit.min.y,
122
+
123
+ muscleIndexZ >= 0 ? HumanTrait.GetMuscleDefaultMax(muscleIndexZ) : limit.min.z)
124
+
125
+ : limit.max;
110
126
 
111
127
 
112
128
 
@@ -116,13 +132,13 @@
116
132
 
117
133
  var minMaxColor = useDefault ? "<color=black>" : "<color=blue>";
118
134
 
119
-
120
-
121
135
  if (muscleIndexX >= 0)
122
136
 
123
137
  {
124
138
 
139
+ Debug.Log(
140
+
125
- Debug.Log($"\t\t{HumanTrait.MuscleName[muscleIndexX]}: {minMaxColor}{min.x}~{max.x}</color>");
141
+ $"\t\t{HumanTrait.MuscleName[muscleIndexX]}: {minMaxColor}{min.x}~{max.x}</color>");
126
142
 
127
143
  }
128
144
 
@@ -132,7 +148,9 @@
132
148
 
133
149
  {
134
150
 
151
+ Debug.Log(
152
+
135
- Debug.Log($"\t\t{HumanTrait.MuscleName[muscleIndexY]}: {minMaxColor}{min.y}~{max.y}</color>");
153
+ $"\t\t{HumanTrait.MuscleName[muscleIndexY]}: {minMaxColor}{min.y}~{max.y}</color>");
136
154
 
137
155
  }
138
156
 
@@ -142,7 +160,9 @@
142
160
 
143
161
  {
144
162
 
163
+ Debug.Log(
164
+
145
- Debug.Log($"\t\t{HumanTrait.MuscleName[muscleIndexZ]}: {minMaxColor}{min.z}~{max.z}</color>");
165
+ $"\t\t{HumanTrait.MuscleName[muscleIndexZ]}: {minMaxColor}{min.z}~{max.z}</color>");
146
166
 
147
167
  }
148
168
 
@@ -176,7 +196,7 @@
176
196
 
177
197
  プロジェクトビュー内でモデルファイルを選択し「Utility」→「Print HumanDescription」を実行すると、下図のような出力結果が得られました。
178
198
 
179
- ![Ethan](127dce58c4d9e702344e8bce71272afe.png)
199
+ ![Ethan](436cb658c8995acbc2c02aef90984845.png)
180
200
 
181
201
  エディタスクリプトでは何とかなりそうでしたが、実際の実行時に[Avatar](https://docs.unity3d.com/ja/current/ScriptReference/Avatar.html)からHumanDescriptionを取り出すのはやっかいそうです。探した限りではそれらしきクラス・メソッドは見つからず、ModelImporterがどのようにHumanDescriptionを作っているかの詳細も公開されていないようで、もっと詳しく調査・解析する必要がありそうです(もしかすると不可能かもしれません)。
182
202
 

3

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

2019/01/17 03:08

投稿

Bongo
Bongo

スコア10807

test CHANGED
@@ -65,6 +65,8 @@
65
65
  {
66
66
 
67
67
  boneIndex = j;
68
+
69
+ break;
68
70
 
69
71
  }
70
72
 

2

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

2019/01/16 22:34

投稿

Bongo
Bongo

スコア10807

test CHANGED
@@ -44,23 +44,63 @@
44
44
 
45
45
  Debug.Log("\tPer-Muscle Settings:");
46
46
 
47
- for (var i = 0; i < humanDescription.human.Length; i++)
47
+ foreach (var humanBone in humanDescription.human)
48
48
 
49
49
  {
50
50
 
51
- var humanBone = humanDescription.human[i];
51
+ var humanName = humanBone.humanName;
52
+
53
+
54
+
55
+ // ボーン名一覧を見て、対応するボーン番号を見つける
56
+
57
+ var boneIndex = -1;
58
+
59
+ for (var j = 0; j < HumanTrait.BoneCount; j++)
60
+
61
+ {
62
+
63
+ if (HumanTrait.BoneName[j] == humanName)
64
+
65
+ {
66
+
67
+ boneIndex = j;
68
+
69
+ }
70
+
71
+ }
72
+
73
+
74
+
75
+ if (boneIndex < 0)
76
+
77
+ {
78
+
79
+ continue;
80
+
81
+ }
82
+
83
+
84
+
85
+ // ボーンインデックスの各軸に対応するマッスル番号を見つける
86
+
87
+ var muscleIndexX = HumanTrait.MuscleFromBone(boneIndex, 0);
88
+
89
+ var muscleIndexY = HumanTrait.MuscleFromBone(boneIndex, 1);
90
+
91
+ var muscleIndexZ = HumanTrait.MuscleFromBone(boneIndex, 2);
52
92
 
53
93
  var limit = humanBone.limit;
54
94
 
55
-
95
+
56
96
 
57
97
  // デフォルト値を使うよう設定されている場合は、limit内のmin・maxは無視するらしい?
58
98
 
59
99
  var useDefault = limit.useDefaultValues;
60
100
 
61
- var defaultMin = HumanTrait.GetMuscleDefaultMin(i);
101
+ var defaultMin = HumanTrait.GetMuscleDefaultMin(boneIndex);
62
102
 
63
- var defaultMax = HumanTrait.GetMuscleDefaultMax(i);
103
+ var defaultMax = HumanTrait.GetMuscleDefaultMax(boneIndex);
64
104
 
65
105
  var min = useDefault ? new Vector3(defaultMin, defaultMin, defaultMin) : limit.min;
66
106
 
@@ -74,17 +114,35 @@
74
114
 
75
115
  var minMaxColor = useDefault ? "<color=black>" : "<color=blue>";
76
116
 
77
-
78
117
 
79
- // 多分xがTwist Left-Right、yがLeft-Right、zがFront-Backだと思いますが
80
118
 
81
- // 全項目についてちゃんと対応関係を確認したわけではないのでご注意ください
119
+ if (muscleIndexX >= 0)
82
120
 
83
- Debug.Log($"\t\t{humanBone.humanName} Front-Back: {minMaxColor}{min.z}~{max.z}</color>");
121
+ {
84
122
 
85
- Debug.Log($"\t\t{humanBone.humanName} Left-Right: {minMaxColor}{min.y}~{max.y}</color>");
123
+ Debug.Log($"\t\t{HumanTrait.MuscleName[muscleIndexX]}: {minMaxColor}{min.x}~{max.x}</color>");
86
124
 
125
+ }
126
+
127
+
128
+
129
+ if (muscleIndexY >= 0)
130
+
131
+ {
132
+
87
- Debug.Log($"\t\t{humanBone.humanName} Twist Left-Right: {minMaxColor}{min.x}~{max.x}</color>");
133
+ Debug.Log($"\t\t{HumanTrait.MuscleName[muscleIndexY]}: {minMaxColor}{min.y}~{max.y}</color>");
134
+
135
+ }
136
+
137
+
138
+
139
+ if (muscleIndexZ >= 0)
140
+
141
+ {
142
+
143
+ Debug.Log($"\t\t{HumanTrait.MuscleName[muscleIndexZ]}: {minMaxColor}{min.z}~{max.z}</color>");
144
+
145
+ }
88
146
 
89
147
  }
90
148
 
@@ -116,11 +174,7 @@
116
174
 
117
175
  プロジェクトビュー内でモデルファイルを選択し「Utility」→「Print HumanDescription」を実行すると、下図のような出力結果が得られました。
118
176
 
119
- インスペクタ上では、部位によって設定項目名が「Down-Up」「In-Out」だったり「Stretch」「Twist In-Out」だったりとバラバラですが、手抜きして全部「Front-Back」「Left-Right」「Twist Left-Right」としています。すみません...
120
-
121
- ちゃんと対応づけるには、部位ごとにどの値を動かすとHumanDescriptionのどの値が変化するかを確認してやる必要がありそうです。
122
-
123
- ![Ethan](5c28434208e8e21b723d527f258e5725.png)
177
+ ![Ethan](127dce58c4d9e702344e8bce71272afe.png)
124
178
 
125
179
  エディタスクリプトでは何とかなりそうでしたが、実際の実行時に[Avatar](https://docs.unity3d.com/ja/current/ScriptReference/Avatar.html)からHumanDescriptionを取り出すのはやっかいそうです。探した限りではそれらしきクラス・メソッドは見つからず、ModelImporterがどのようにHumanDescriptionを作っているかの詳細も公開されていないようで、もっと詳しく調査・解析する必要がありそうです(もしかすると不可能かもしれません)。
126
180
 

1

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

2019/01/16 22:31

投稿

Bongo
Bongo

スコア10807

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