t1 / TFDContents / Assets / KinectDemos / VisualizerDemo / Scripts / UserHandVisualizer.cs @ 3
이력 | 보기 | 이력해설 | 다운로드 (13.7 KB)
1 |
using UnityEngine; |
---|---|
2 |
using System.Collections; |
3 |
|
4 |
|
5 |
public class UserHandVisualizer : MonoBehaviour |
6 |
{ |
7 |
[Tooltip("Index of the player, tracked by this component. 0 - the 1st player, 1 - the 2nd player, 2 - the 3rd player, etc.")] |
8 |
public int playerIndex = 0; |
9 |
|
10 |
[Tooltip("Whether the mesh is facing the player or not.")] |
11 |
public bool mirroredMovement = true; |
12 |
|
13 |
[Tooltip("Kinect origin position.")] |
14 |
public Vector3 originPosition = Vector3.zero; |
15 |
|
16 |
[Tooltip("Whether the z-movement is inverted or not.")] |
17 |
public bool invertedZMovement = false; |
18 |
|
19 |
[Tooltip("Smooth factor used for the camera re-orientation.")] |
20 |
public float smoothFactor = 0f; |
21 |
|
22 |
[Tooltip("Camera that may be used to overlay the mesh over the color background.")] |
23 |
public Camera foregroundCamera; |
24 |
|
25 |
[Tooltip("Whether to update the mesh collider as well, when the user mesh changes.")] |
26 |
public bool updateMeshCollider = false; |
27 |
|
28 |
[Tooltip("Number of pixels per direction in a sample.")] |
29 |
private const int sampleSize = 1; |
30 |
|
31 |
|
32 |
private Mesh mesh; |
33 |
private Vector3[] vertices; |
34 |
private Vector2[] uvs; |
35 |
private int[] triangles; |
36 |
|
37 |
private KinectManager manager = null; |
38 |
|
39 |
private KinectInterop.SensorData sensorData = null; |
40 |
//private Vector3[] spaceCoords = null; |
41 |
private Matrix4x4 kinectToWorld = Matrix4x4.identity; |
42 |
|
43 |
private int depthWidth = 0; |
44 |
private int depthHeight = 0; |
45 |
|
46 |
private int sampledWidth = 0; |
47 |
private int sampledHeight = 0; |
48 |
|
49 |
private long userId = 0; |
50 |
private byte userBodyIndex = 255; |
51 |
private Vector3 userMeshPos = Vector3.zero; |
52 |
|
53 |
private Vector3 leftHandPos = Vector3.zero; |
54 |
private Vector3 rightHandPos = Vector3.zero; |
55 |
private Vector3 leftFingerPos = Vector3.zero; |
56 |
private Vector3 rightFingerPos = Vector3.zero; |
57 |
private Vector3 leftThumbPos = Vector3.zero; |
58 |
private Vector3 rightThumbPos = Vector3.zero; |
59 |
|
60 |
private byte[] vertexType; |
61 |
private int[] vertexIndex; |
62 |
|
63 |
|
64 |
void Start() |
65 |
{ |
66 |
manager = KinectManager.Instance; |
67 |
|
68 |
if (manager != null) |
69 |
{ |
70 |
sensorData = manager.GetSensorData(); |
71 |
|
72 |
depthWidth = manager.GetDepthImageWidth(); |
73 |
depthHeight = manager.GetDepthImageHeight(); |
74 |
|
75 |
sampledWidth = depthWidth / sampleSize; |
76 |
sampledHeight = depthHeight / sampleSize; |
77 |
|
78 |
//spaceCoords = new Vector3[depthWidth * depthHeight]; |
79 |
|
80 |
if(sensorData.depth2SpaceCoords == null) |
81 |
{ |
82 |
sensorData.depth2SpaceCoords = new Vector3[depthWidth * depthHeight]; |
83 |
} |
84 |
|
85 |
vertexType = new byte[sampledWidth * sampledHeight]; |
86 |
vertexIndex = new int[sampledWidth * sampledHeight]; |
87 |
|
88 |
CreateMesh(sampledWidth, sampledHeight); |
89 |
} |
90 |
} |
91 |
|
92 |
private void CreateMesh(int width, int height) |
93 |
{ |
94 |
mesh = new Mesh(); |
95 |
mesh.name = "UserMesh"; |
96 |
|
97 |
GetComponent<MeshFilter>().mesh = mesh; |
98 |
} |
99 |
|
100 |
void Update() |
101 |
{ |
102 |
if (manager == null || !manager.IsInitialized()) |
103 |
return; |
104 |
|
105 |
// get user texture |
106 |
Renderer renderer = GetComponent<Renderer>(); |
107 |
if(renderer && renderer.material && renderer.material.mainTexture == null) |
108 |
{ |
109 |
BackgroundRemovalManager backManager = BackgroundRemovalManager.Instance; |
110 |
renderer.material.mainTexture = backManager ? (Texture)sensorData.depth2ColorTexture : (Texture)manager.GetUsersLblTex(); |
111 |
} |
112 |
|
113 |
// get kinect-to-world matrix |
114 |
kinectToWorld = manager.GetKinectToWorldMatrix(); |
115 |
|
116 |
if(playerIndex >= 0) |
117 |
{ |
118 |
long lastUserId = userId; |
119 |
userId = manager.GetUserIdByIndex(playerIndex); |
120 |
|
121 |
userBodyIndex = (byte)manager.GetBodyIndexByUserId(userId); |
122 |
if(userBodyIndex == 255) |
123 |
userBodyIndex = 222; |
124 |
|
125 |
// hand positions |
126 |
leftHandPos = manager.GetJointKinectPosition(userId, (int)KinectInterop.JointType.HandLeft); |
127 |
rightHandPos = manager.GetJointKinectPosition(userId, (int)KinectInterop.JointType.HandRight); |
128 |
leftFingerPos = manager.GetJointKinectPosition(userId, (int)KinectInterop.JointType.HandTipLeft); |
129 |
rightFingerPos = manager.GetJointKinectPosition(userId, (int)KinectInterop.JointType.HandTipRight); |
130 |
leftThumbPos = manager.GetJointKinectPosition(userId, (int)KinectInterop.JointType.ThumbLeft); |
131 |
rightThumbPos = manager.GetJointKinectPosition(userId, (int)KinectInterop.JointType.ThumbRight); |
132 |
|
133 |
// check for color overlay |
134 |
if (foregroundCamera) |
135 |
{ |
136 |
// get the background rectangle (use the portrait background, if available) |
137 |
Rect backgroundRect = foregroundCamera.pixelRect; |
138 |
PortraitBackground portraitBack = PortraitBackground.Instance; |
139 |
|
140 |
if (portraitBack && portraitBack.enabled) |
141 |
{ |
142 |
backgroundRect = portraitBack.GetBackgroundRect (); |
143 |
} |
144 |
|
145 |
// get user position |
146 |
userMeshPos = manager.GetJointPosColorOverlay(userId, (int)KinectInterop.JointType.SpineBase, foregroundCamera, backgroundRect); |
147 |
} |
148 |
else |
149 |
{ |
150 |
// get user position |
151 |
userMeshPos = manager.GetJointKinectPosition(userId, (int)KinectInterop.JointType.SpineBase); |
152 |
} |
153 |
|
154 |
if(!mirroredMovement) |
155 |
{ |
156 |
userMeshPos.x = -userMeshPos.x; |
157 |
} |
158 |
|
159 |
if (foregroundCamera == null) |
160 |
{ |
161 |
// convert kinect pos to world coords, when there is no color overlay |
162 |
userMeshPos = kinectToWorld.MultiplyPoint3x4(userMeshPos); |
163 |
} |
164 |
|
165 |
// set transform position |
166 |
Vector3 newUserPos = userMeshPos + originPosition; // manager.GetJointPosition(userId, (int)KinectInterop.JointType.SpineBase) + originPosition; |
167 |
|
168 |
if(invertedZMovement) |
169 |
{ |
170 |
newUserPos.z = -newUserPos.z; |
171 |
} |
172 |
|
173 |
transform.position = lastUserId != 0 && smoothFactor != 0f ? Vector3.Lerp(transform.position, newUserPos, smoothFactor * Time.deltaTime) : newUserPos; |
174 |
} |
175 |
else |
176 |
{ |
177 |
userId = 0; |
178 |
userBodyIndex = 255; |
179 |
userMeshPos = Vector3.zero; |
180 |
|
181 |
leftHandPos = rightHandPos = Vector3.zero; |
182 |
leftFingerPos = rightFingerPos = Vector3.zero; |
183 |
leftThumbPos = rightThumbPos = Vector3.zero; |
184 |
} |
185 |
|
186 |
// update the mesh |
187 |
UpdateMesh(); |
188 |
} |
189 |
|
190 |
private void UpdateMesh() |
191 |
{ |
192 |
if(sensorData.depthImage != null && sensorData.bodyIndexImage != null && |
193 |
sensorData.depth2SpaceCoords != null && sensorData.spaceCoordsBufferReady) |
194 |
{ |
195 |
int vCount = 0, tCount = 0; |
196 |
EstimateUserVertices(out vCount, out tCount); |
197 |
|
198 |
vertices = new Vector3[vCount]; |
199 |
uvs = new Vector2[vCount]; |
200 |
triangles = new int[6 * tCount]; |
201 |
|
202 |
int index = 0, vIndex = 0, tIndex = 0, xyIndex = 0; |
203 |
for (int y = 0; y < depthHeight; y += sampleSize) |
204 |
{ |
205 |
int xyStartIndex = xyIndex; |
206 |
|
207 |
for (int x = 0; x < depthWidth; x += sampleSize) |
208 |
{ |
209 |
//Vector3 vSpacePos = spaceCoords[xyIndex]; |
210 |
Vector3 vSpacePos = sensorData.depth2SpaceCoords[xyIndex]; |
211 |
|
212 |
if(vertexType[index] != 0 && |
213 |
!float.IsInfinity(vSpacePos.x) && !float.IsInfinity(vSpacePos.y) && !float.IsInfinity(vSpacePos.z)) |
214 |
{ |
215 |
// check for color overlay |
216 |
if (foregroundCamera) |
217 |
{ |
218 |
// get the background rectangle (use the portrait background, if available) |
219 |
Rect backgroundRect = foregroundCamera.pixelRect; |
220 |
PortraitBackground portraitBack = PortraitBackground.Instance; |
221 |
|
222 |
if(portraitBack && portraitBack.enabled) |
223 |
{ |
224 |
backgroundRect = portraitBack.GetBackgroundRect(); |
225 |
} |
226 |
|
227 |
Vector2 vColorPos = sensorData.depth2ColorCoords[xyIndex]; |
228 |
ushort depthValue = sensorData.depthImage[xyIndex]; |
229 |
|
230 |
if(!float.IsInfinity(vColorPos.x) && !float.IsInfinity(vColorPos.y) && depthValue > 0) |
231 |
{ |
232 |
float xScaled = (float)vColorPos.x * backgroundRect.width / sensorData.colorImageWidth; |
233 |
float yScaled = (float)vColorPos.y * backgroundRect.height / sensorData.colorImageHeight; |
234 |
|
235 |
float xScreen = backgroundRect.x + xScaled; |
236 |
float yScreen = backgroundRect.y + backgroundRect.height - yScaled; |
237 |
float zDistance = (float)depthValue / 1000f; |
238 |
|
239 |
vSpacePos = foregroundCamera.ScreenToWorldPoint(new Vector3(xScreen, yScreen, zDistance)); |
240 |
} |
241 |
} |
242 |
|
243 |
if(!mirroredMovement) |
244 |
{ |
245 |
vSpacePos.x = -vSpacePos.x; |
246 |
} |
247 |
|
248 |
if(foregroundCamera == null) |
249 |
{ |
250 |
// convert space to world coords, when there is no color overlay |
251 |
vSpacePos = kinectToWorld.MultiplyPoint3x4(vSpacePos); |
252 |
} |
253 |
|
254 |
vertices[vIndex] = vSpacePos - userMeshPos; |
255 |
uvs[vIndex] = new Vector2((float)x / depthWidth, (float)y / depthHeight); |
256 |
vIndex++; |
257 |
|
258 |
if(vertexType[index] == 3) |
259 |
{ |
260 |
if(mirroredMovement) |
261 |
{ |
262 |
triangles[tIndex++] = vertexIndex[index]; // top left |
263 |
triangles[tIndex++] = vertexIndex[index + 1]; // top right |
264 |
triangles[tIndex++] = vertexIndex[index + sampledWidth]; // bottom left |
265 |
|
266 |
triangles[tIndex++] = vertexIndex[index + sampledWidth]; // bottom left |
267 |
triangles[tIndex++] = vertexIndex[index + 1]; // top right |
268 |
triangles[tIndex++] = vertexIndex[index + sampledWidth + 1]; // bottom right |
269 |
} |
270 |
else |
271 |
{ |
272 |
triangles[tIndex++] = vertexIndex[index + 1]; // top left |
273 |
triangles[tIndex++] = vertexIndex[index]; // top right |
274 |
triangles[tIndex++] = vertexIndex[index + sampledWidth + 1]; // bottom left |
275 |
|
276 |
triangles[tIndex++] = vertexIndex[index + sampledWidth + 1]; // bottom left |
277 |
triangles[tIndex++] = vertexIndex[index]; // top right |
278 |
triangles[tIndex++] = vertexIndex[index + sampledWidth]; // bottom right |
279 |
} |
280 |
} |
281 |
} |
282 |
|
283 |
index++; |
284 |
xyIndex += sampleSize; |
285 |
} |
286 |
|
287 |
xyIndex = xyStartIndex + sampleSize * depthWidth; |
288 |
} |
289 |
|
290 |
// buffer is released |
291 |
lock(sensorData.spaceCoordsBufferLock) |
292 |
{ |
293 |
sensorData.spaceCoordsBufferReady = false; |
294 |
} |
295 |
|
296 |
mesh.Clear(); |
297 |
mesh.vertices = vertices; |
298 |
mesh.uv = uvs; |
299 |
//mesh.normals = normals; |
300 |
mesh.triangles = triangles; |
301 |
mesh.RecalculateNormals(); |
302 |
mesh.RecalculateBounds(); |
303 |
|
304 |
if (updateMeshCollider) |
305 |
{ |
306 |
MeshCollider meshCollider = GetComponent<MeshCollider>(); |
307 |
|
308 |
if (meshCollider) |
309 |
{ |
310 |
meshCollider.sharedMesh = null; |
311 |
meshCollider.sharedMesh = mesh; |
312 |
} |
313 |
} |
314 |
} |
315 |
} |
316 |
|
317 |
// estimates which and how many sampled vertices are valid |
318 |
private void EstimateUserVertices(out int count1, out int count3) |
319 |
{ |
320 |
System.Array.Clear(vertexType, 0, vertexType.Length); |
321 |
|
322 |
Vector3[] vSpacePos = new Vector3[4]; |
323 |
int rowIndex = 0; |
324 |
|
325 |
for (int y = 0; y < sampledHeight - 1; y++) |
326 |
{ |
327 |
int pixIndex = rowIndex; |
328 |
|
329 |
for (int x = 0; x < sampledWidth - 1; x++) |
330 |
{ |
331 |
if(IsUserSampleValid(x, y, ref vSpacePos[0]) && IsUserSampleValid(x + 1, y, ref vSpacePos[1]) && |
332 |
IsUserSampleValid(x, y + 1, ref vSpacePos[2]) && IsUserSampleValid(x + 1, y + 1, ref vSpacePos[3])) |
333 |
{ |
334 |
if(IsSpacePointsCloseToEachOther(vSpacePos, 0.01f) && IsSpacePointsCloseToHands(vSpacePos, 0.01f)) |
335 |
{ |
336 |
vertexType[pixIndex] = 3; |
337 |
|
338 |
vertexType[pixIndex + 1] = 1; |
339 |
vertexType[pixIndex + sampledWidth] = 1; |
340 |
vertexType[pixIndex + sampledWidth + 1] = 1; |
341 |
} |
342 |
} |
343 |
|
344 |
pixIndex++; |
345 |
} |
346 |
|
347 |
rowIndex += sampledWidth; |
348 |
} |
349 |
|
350 |
// estimate counts |
351 |
count1 = 0; |
352 |
count3 = 0; |
353 |
|
354 |
for(int i = 0; i < vertexType.Length; i++) |
355 |
{ |
356 |
if(vertexType[i] != 0) |
357 |
{ |
358 |
vertexIndex[i] = count1; |
359 |
count1++; |
360 |
} |
361 |
else |
362 |
{ |
363 |
vertexIndex[i] = 0; |
364 |
} |
365 |
|
366 |
if(vertexType[i] == 3) |
367 |
{ |
368 |
count3++; |
369 |
} |
370 |
} |
371 |
} |
372 |
|
373 |
// checks if the space points are closer to each other than the minimum squared distance |
374 |
private bool IsSpacePointsCloseToEachOther(Vector3[] vSpacePos, float fMinDistSquared) |
375 |
{ |
376 |
int iPosLength = vSpacePos.Length; |
377 |
|
378 |
for(int i = 0; i < iPosLength; i++) |
379 |
{ |
380 |
for(int j = i + 1; j < iPosLength; j++) |
381 |
{ |
382 |
Vector3 vDist = vSpacePos[j] - vSpacePos[i]; |
383 |
if(vDist.sqrMagnitude > fMinDistSquared) |
384 |
return false; |
385 |
} |
386 |
} |
387 |
|
388 |
return true; |
389 |
} |
390 |
|
391 |
// checks if the space points are closer to hand, finger or thumb joints than the minimum squared distance |
392 |
private bool IsSpacePointsCloseToHands(Vector3[] vSpacePos, float fMinDistSquared) |
393 |
{ |
394 |
if(IsSpacePointsCloseToJoint(vSpacePos, leftHandPos, fMinDistSquared) || IsSpacePointsCloseToJoint(vSpacePos, rightHandPos, fMinDistSquared) || |
395 |
IsSpacePointsCloseToJoint(vSpacePos, leftFingerPos, fMinDistSquared) || IsSpacePointsCloseToJoint(vSpacePos, rightFingerPos, fMinDistSquared) || |
396 |
IsSpacePointsCloseToJoint(vSpacePos, leftThumbPos, fMinDistSquared) || IsSpacePointsCloseToJoint(vSpacePos, rightThumbPos, fMinDistSquared)) |
397 |
{ |
398 |
return true; |
399 |
} |
400 |
|
401 |
return false; |
402 |
} |
403 |
|
404 |
// checks if the space points are closer to joint position than the minimum squared distance |
405 |
private bool IsSpacePointsCloseToJoint(Vector3[] vSpacePos, Vector3 vJointPos, float fMinDistSquared) |
406 |
{ |
407 |
int iPosLength = vSpacePos.Length; |
408 |
|
409 |
for(int i = 0; i < iPosLength; i++) |
410 |
{ |
411 |
Vector3 vDist = vSpacePos[i] - vJointPos; |
412 |
if(vDist.sqrMagnitude > fMinDistSquared) |
413 |
return false; |
414 |
} |
415 |
|
416 |
return true; |
417 |
} |
418 |
|
419 |
// checks whether this sample block is valid for this user |
420 |
private bool IsUserSampleValid(int x, int y, ref Vector3 vSpacePos) |
421 |
{ |
422 |
int startIndex = y * sampleSize * depthWidth + x * sampleSize; |
423 |
|
424 |
//for (int y1 = 0; y1 < SampleSize; y1++) |
425 |
{ |
426 |
int pixelIndex = startIndex; |
427 |
//vSpacePos = spaceCoords[pixelIndex]; |
428 |
vSpacePos = sensorData.depth2SpaceCoords[pixelIndex]; |
429 |
|
430 |
//for (int x1 = 0; x1 < SampleSize; x1++) |
431 |
{ |
432 |
if(userBodyIndex != 255) |
433 |
{ |
434 |
if(sensorData.bodyIndexImage[pixelIndex] == userBodyIndex && |
435 |
!float.IsInfinity(vSpacePos.x) && !float.IsInfinity(vSpacePos.y) && !float.IsInfinity(vSpacePos.z)) |
436 |
{ |
437 |
return true; |
438 |
} |
439 |
} |
440 |
// else |
441 |
// { |
442 |
// if(sensorData.bodyIndexImage[pixelIndex] != 255 && |
443 |
// !float.IsInfinity(vSpacePos.x) && !float.IsInfinity(vSpacePos.y) && !float.IsInfinity(vSpacePos.z)) |
444 |
// { |
445 |
// return true; |
446 |
// } |
447 |
// } |
448 |
|
449 |
pixelIndex++; |
450 |
} |
451 |
|
452 |
startIndex += depthWidth; |
453 |
} |
454 |
|
455 |
return false; |
456 |
} |
457 |
|
458 |
} |