5 using System.Collections.Generic;
9 using System.Runtime.InteropServices;
12 #if UNITY_WSA && UNITY_2017_2_OR_NEWER 19 using Windows.Foundation;
20 using Windows.Storage.Streams;
33 [Tooltip(
"This setting will be used to determine if the model, override or otherwise, should attempt to be animated based on the user's input.")]
34 public bool AnimateControllerModel =
true;
36 [Tooltip(
"This setting will be used to determine if the model should always be the left alternate. If false, the platform controller models will be preferred, only if they can't be loaded will the alternate be used. Otherwise, it will always use the alternate model.")]
37 public bool AlwaysUseAlternateLeftModel =
false;
38 [Tooltip(
"This setting will be used to determine if the model should always be the right alternate. If false, the platform controller models will be preferred, only if they can't be loaded will the alternate be used. Otherwise, it will always use the alternate model.")]
39 public bool AlwaysUseAlternateRightModel =
false;
41 [Tooltip(
"Use a model with the tip in the positive Z direction and the front face in the positive Y direction. To override the platform left controller model set AlwaysUseAlternateModel to true; otherwise this will be the default if the model can't be found.")]
44 [Tooltip(
"Use a model with the tip in the positive Z direction and the front face in the positive Y direction. To override the platform right controller model set AlwaysUseAlternateModel to true; otherwise this will be the default if the model can't be found.")]
47 [Tooltip(
"Use this to override the indicator used to show the user's touch location on the touchpad. Default is a sphere.")]
51 [Tooltip(
"This material will be used on the loaded glTF controller model. This does not affect the above overrides.")]
55 #if UNITY_WSA && UNITY_2017_2_OR_NEWER 57 private Dictionary<string, MotionControllerInfo> controllerDictionary =
new Dictionary<string, MotionControllerInfo>(0);
58 private List<string> loadingControllers =
new List<string>();
63 public event Action<MotionControllerInfo> OnControllerModelLoaded;
64 public event Action<MotionControllerInfo> OnControllerModelUnloaded;
68 [DllImport(
"EditorMotionController")]
69 private static extern bool TryGetMotionControllerModel([In] uint controllerId, [Out] out uint outputSize, [Out] out IntPtr outputBuffer);
76 #if UNITY_WSA && UNITY_2017_2_OR_NEWER 77 foreach (var sourceState
in InteractionManager.GetCurrentReading())
79 if (sourceState.source.kind == InteractionSourceKind.Controller)
81 StartTrackingController(sourceState.source);
85 Application.onBeforeRender += Application_onBeforeRender;
87 if (GLTFMaterial == null)
89 if (AlternateLeftController == null && AlternateRightController == null)
91 Debug.Log(
"If using glTF, please specify a material on " + name +
". Otherwise, please specify controller alternates.");
93 else if (AlternateLeftController == null || AlternateRightController == null)
95 Debug.Log(
"Only one alternate is specified, and no material is specified for the glTF model. Please set the material or the " + ((AlternateLeftController == null) ?
"left" :
"right") +
" controller alternate on " + name +
".");
99 InteractionManager.InteractionSourceDetected += InteractionManager_InteractionSourceDetected;
100 InteractionManager.InteractionSourceLost += InteractionManager_InteractionSourceLost;
104 private void Update()
108 UpdateControllerState();
115 #if UNITY_WSA && UNITY_2017_2_OR_NEWER 116 InteractionManager.InteractionSourceDetected -= InteractionManager_InteractionSourceDetected;
117 InteractionManager.InteractionSourceLost -= InteractionManager_InteractionSourceLost;
118 Application.onBeforeRender -= Application_onBeforeRender;
122 private void Application_onBeforeRender()
126 UpdateControllerState();
129 private void UpdateControllerState()
131 #if UNITY_WSA && UNITY_2017_2_OR_NEWER 132 foreach (var sourceState
in InteractionManager.GetCurrentReading())
135 if (sourceState.source.kind == InteractionSourceKind.Controller && controllerDictionary.TryGetValue(GenerateKey(sourceState.source), out currentController))
137 if (AnimateControllerModel)
139 currentController.
AnimateSelect(sourceState.selectPressedAmount);
141 if (sourceState.source.supportsGrasp)
143 currentController.AnimateGrasp(sourceState.grasped);
146 if (sourceState.source.supportsMenu)
148 currentController.AnimateMenu(sourceState.menuPressed);
151 if (sourceState.source.supportsThumbstick)
153 currentController.AnimateThumbstick(sourceState.thumbstickPressed, sourceState.thumbstickPosition);
156 if (sourceState.source.supportsTouchpad)
158 currentController.AnimateTouchpad(sourceState.touchpadPressed, sourceState.touchpadTouched, sourceState.touchpadPosition);
163 if (sourceState.sourcePose.TryGetPosition(out newPosition, InteractionSourceNode.Grip) && ValidPosition(newPosition))
165 currentController.ControllerParent.transform.localPosition = newPosition;
168 Quaternion newRotation;
169 if (sourceState.sourcePose.TryGetRotation(out newRotation, InteractionSourceNode.Grip) && ValidRotation(newRotation))
171 currentController.ControllerParent.transform.localRotation = newRotation;
178 private bool ValidRotation(Quaternion newRotation)
180 return !
float.IsNaN(newRotation.x) && !
float.IsNaN(newRotation.y) && !
float.IsNaN(newRotation.z) && !
float.IsNaN(newRotation.w) &&
181 !
float.IsInfinity(newRotation.x) && !
float.IsInfinity(newRotation.y) && !
float.IsInfinity(newRotation.z) && !
float.IsInfinity(newRotation.w);
184 private bool ValidPosition(Vector3 newPosition)
186 return !
float.IsNaN(newPosition.x) && !
float.IsNaN(newPosition.y) && !
float.IsNaN(newPosition.z) &&
187 !
float.IsInfinity(newPosition.x) && !
float.IsInfinity(newPosition.y) && !
float.IsInfinity(newPosition.z);
190 #if UNITY_WSA && UNITY_2017_2_OR_NEWER 191 private void InteractionManager_InteractionSourceDetected(InteractionSourceDetectedEventArgs obj)
193 StartTrackingController(obj.state.source);
196 private void StartTrackingController(InteractionSource source)
198 string key = GenerateKey(source);
201 if (source.kind == InteractionSourceKind.Controller)
203 if (!controllerDictionary.ContainsKey(key) && !loadingControllers.Contains(key))
205 StartCoroutine(LoadControllerModel(source));
207 else if (controllerDictionary.TryGetValue(key, out controllerInfo))
209 if (controllerInfo.Handedness == InteractionSourceHandedness.Left)
211 leftControllerModel = controllerInfo;
213 else if (controllerInfo.Handedness == InteractionSourceHandedness.Right)
215 rightControllerModel = controllerInfo;
220 if (OnControllerModelLoaded != null)
222 OnControllerModelLoaded(controllerInfo);
233 private void InteractionManager_InteractionSourceLost(InteractionSourceLostEventArgs obj)
235 InteractionSource source = obj.state.source;
236 if (source.kind == InteractionSourceKind.Controller)
239 if (controllerDictionary != null && controllerDictionary.TryGetValue(GenerateKey(source), out controllerInfo))
241 if (OnControllerModelUnloaded != null)
243 OnControllerModelUnloaded(controllerInfo);
246 if (controllerInfo.Handedness == InteractionSourceHandedness.Left)
248 leftControllerModel = null;
250 else if (controllerInfo.Handedness == InteractionSourceHandedness.Right)
252 rightControllerModel = null;
260 private IEnumerator LoadControllerModel(InteractionSource source)
262 loadingControllers.Add(GenerateKey(source));
264 if (AlwaysUseAlternateLeftModel && source.handedness == InteractionSourceHandedness.Left)
266 if (AlternateLeftController == null)
268 Debug.LogWarning(
"Always use the alternate left model is set on " + name +
", but the alternate left controller model was not specified.");
269 yield
return LoadSourceControllerModel(source);
273 LoadAlternateControllerModel(source);
276 else if (AlwaysUseAlternateRightModel && source.handedness == InteractionSourceHandedness.Right)
278 if (AlternateRightController == null)
280 Debug.LogWarning(
"Always use the alternate right model is set on " + name +
", but the alternate right controller model was not specified.");
281 yield
return LoadSourceControllerModel(source);
285 LoadAlternateControllerModel(source);
290 yield
return LoadSourceControllerModel(source);
294 private IEnumerator LoadSourceControllerModel(InteractionSource source)
297 GameObject controllerModelGameObject;
299 if (GLTFMaterial == null)
301 Debug.Log(
"If using glTF, please specify a material on " + name +
".");
307 IAsyncOperation<IRandomAccessStreamWithContentType> modelTask = source.TryGetRenderableModelAsync();
309 if (modelTask == null)
311 Debug.Log(
"Model task is null; loading alternate.");
312 LoadAlternateControllerModel(source);
316 while (modelTask.Status == AsyncStatus.Started)
321 IRandomAccessStreamWithContentType modelStream = modelTask.GetResults();
323 if (modelStream == null)
325 Debug.Log(
"Model stream is null; loading alternate.");
326 LoadAlternateControllerModel(source);
330 if (modelStream.Size == 0)
332 Debug.Log(
"Model stream is empty; loading alternate.");
333 LoadAlternateControllerModel(source);
337 fileBytes =
new byte[modelStream.Size];
339 using (DataReader reader =
new DataReader(modelStream))
341 DataReaderLoadOperation loadModelOp = reader.LoadAsync((uint)modelStream.Size);
343 while (loadModelOp.Status == AsyncStatus.Started)
348 reader.ReadBytes(fileBytes);
351 IntPtr controllerModel =
new IntPtr();
354 if (TryGetMotionControllerModel(source.id, out outputSize, out controllerModel))
356 fileBytes =
new byte[Convert.ToInt32(outputSize)];
358 Marshal.Copy(controllerModel, fileBytes, 0, Convert.ToInt32(outputSize));
362 Debug.Log(
"Unable to load controller models; loading alternate.");
363 LoadAlternateControllerModel(source);
368 controllerModelGameObject =
new GameObject { name =
"glTFController" };
369 controllerModelGameObject.transform.Rotate(0, 180, 0);
371 var sceneImporter =
new GLTFSceneImporter(
373 new MemoryStream(fileBytes, 0, fileBytes.Length,
false,
true),
374 controllerModelGameObject.transform
377 sceneImporter.SetShaderForMaterialType(GLTFSceneImporter.MaterialType.PbrMetallicRoughness, GLTFMaterial.shader);
378 sceneImporter.SetShaderForMaterialType(GLTFSceneImporter.MaterialType.KHR_materials_pbrSpecularGlossiness, GLTFMaterial.shader);
379 sceneImporter.SetShaderForMaterialType(GLTFSceneImporter.MaterialType.CommonConstant, GLTFMaterial.shader);
381 yield
return sceneImporter.Load();
383 FinishControllerSetup(controllerModelGameObject, source.handedness, GenerateKey(source));
386 private void LoadAlternateControllerModel(InteractionSource source)
388 GameObject controllerModelGameObject;
389 if (source.handedness == InteractionSourceHandedness.Left && AlternateLeftController != null)
391 controllerModelGameObject = Instantiate(AlternateLeftController);
393 else if (source.handedness == InteractionSourceHandedness.Right && AlternateRightController != null)
395 controllerModelGameObject = Instantiate(AlternateRightController);
399 loadingControllers.Remove(GenerateKey(source));
403 FinishControllerSetup(controllerModelGameObject, source.handedness, GenerateKey(source));
406 private string GenerateKey(InteractionSource source)
408 return source.vendorId +
"/" + source.productId +
"/" + source.productVersion +
"/" + source.handedness;
411 private void FinishControllerSetup(GameObject controllerModelGameObject, InteractionSourceHandedness handedness,
string dictionaryKey)
413 var parentGameObject =
new GameObject
415 name = handedness +
"Controller" 418 parentGameObject.transform.parent = transform;
419 controllerModelGameObject.transform.parent = parentGameObject.transform;
423 newControllerInfo.LoadInfo(controllerModelGameObject.GetComponentsInChildren<Transform>());
425 if (handedness == InteractionSourceHandedness.Left)
427 leftControllerModel = newControllerInfo;
429 else if (handedness == InteractionSourceHandedness.Right)
431 rightControllerModel = newControllerInfo;
434 if (OnControllerModelLoaded != null)
436 OnControllerModelLoaded(newControllerInfo);
439 loadingControllers.Remove(dictionaryKey);
440 controllerDictionary.Add(dictionaryKey, newControllerInfo);
443 public bool TryGetControllerModel(InteractionSourceHandedness handedness, out
MotionControllerInfo controller)
445 if (handedness == InteractionSourceHandedness.Left && leftControllerModel != null)
447 controller = leftControllerModel;
450 else if (handedness == InteractionSourceHandedness.Right && rightControllerModel != null)
452 controller = rightControllerModel;
465 GameObject touchVisualizer;
466 if (TouchpadTouchedOverride != null)
468 touchVisualizer = Instantiate(TouchpadTouchedOverride);
472 touchVisualizer = GameObject.CreatePrimitive(PrimitiveType.Sphere);
473 touchVisualizer.transform.localScale =
new Vector3(0.0025f, 0.0025f, 0.0025f);
474 touchVisualizer.GetComponent<Renderer>().sharedMaterial = GLTFMaterial;
477 Destroy(touchVisualizer.GetComponent<Collider>());
478 touchVisualizer.transform.parent = parentTransform;
479 touchVisualizer.transform.localPosition = Vector3.zero;
480 touchVisualizer.transform.localRotation = Quaternion.identity;
481 touchVisualizer.SetActive(
false);
482 return touchVisualizer;