回答編集履歴
6
マッスル番号が見つからなかった場合の値が「max」であるべきところが「min」になっている...実際の動作では使われることがない値だが、一応修正
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.
|
60
|
+
muscleIndexX >= 0 ? HumanTrait.GetMuscleDefaultMax(muscleIndexX) : limit.max.x,
|
61
|
-
muscleIndexY >= 0 ? HumanTrait.GetMuscleDefaultMax(muscleIndexY) : limit.
|
61
|
+
muscleIndexY >= 0 ? HumanTrait.GetMuscleDefaultMax(muscleIndexY) : limit.max.y,
|
62
|
-
muscleIndexZ >= 0 ? HumanTrait.GetMuscleDefaultMax(muscleIndexZ) : limit.
|
62
|
+
muscleIndexZ >= 0 ? HumanTrait.GetMuscleDefaultMax(muscleIndexZ) : limit.max.z)
|
63
63
|
: limit.max;
|
64
64
|
|
65
65
|
// カスタム値が使われている場合は青文字で表示することにしました
|
5
実行時のマッスル可動範囲取得の案を追記
answer
CHANGED
@@ -99,4 +99,103 @@
|
|
99
99
|
プロジェクトビュー内でモデルファイルを選択し「Utility」→「Print HumanDescription」を実行すると、下図のような出力結果が得られました。
|
100
100
|

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

|
199
|
+
※おまけ
|
200
|
+
各マッスルを動かしているときにそれぞれどのような姿勢になっているか見てみたところ、下図のようにEthanとユニティちゃんが体操する姿を撮影できました。ちょうどインスペクタ上の各マッスルのプレビュースライダーを操作したときと同様の動きをしていることがお分かりいただけるでしょうか?
|
201
|
+

|
4
明らかに顎や目のデフォルト値がおかしい...リファレンスのサンプルコードは誤っている?おそらく正しいと思われる形に変更、スクリーンショット差し替え
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
|
-
|
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
|
-
|
60
|
+
muscleIndexX >= 0 ? HumanTrait.GetMuscleDefaultMax(muscleIndexX) : limit.min.x,
|
54
|
-
var min = useDefault ? new Vector3(defaultMin, defaultMin, defaultMin) : limit.min;
|
55
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-

|
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追加
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
デフォルト値取得の誤りを訂正、実行結果スクリーンショットを差し替え
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
|
-
|
24
|
+
foreach (var humanBone in humanDescription.human)
|
25
25
|
{
|
26
|
-
var
|
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(
|
51
|
+
var defaultMin = HumanTrait.GetMuscleDefaultMin(boneIndex);
|
32
|
-
var defaultMax = HumanTrait.GetMuscleDefaultMax(
|
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
|
-
|
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
|
-
|
72
|
+
Debug.Log($"\t\t{HumanTrait.MuscleName[muscleIndexZ]}: {minMaxColor}{min.z}~{max.z}</color>");
|
43
|
-
|
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
|
-

|
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
一部項目名が違うことについて弁解
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
|

|
61
|
-
で
|
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)を使えば可能らしいんですがね...
|