using UnityEditor; using UnityEngine; namespace Passer.Humanoid { [CanEditMultipleObjects] [CustomEditor(typeof(HeadTarget), true)] public class HeadTarget_Editor : Editor { private HeadTarget headTarget; private HumanoidControl humanoid; private TargetProps[] allProps; #region Enable public void OnEnable() { headTarget = (HeadTarget)target; if (headTarget.humanoid == null) headTarget.humanoid = GetHumanoid(headTarget); humanoid = headTarget.humanoid; InitEditors(); headTarget.InitSensors(); InitSensors(); #if hFACE FaceTarget_Editor.OnEnable(serializedObject, headTarget); #endif } private void InitEditors() { allProps = new TargetProps[] { #if pUNITYXR new UnityXR_Editor.HeadTargetProps(serializedObject, headTarget), #endif #if hSTEAMVR && UNITY_STANDALONE_WIN #if hVIVETRACKER //new ViveTracker_Editor.HeadTargetProps(serializedObject, headTarget), #endif #endif #if hWINDOWSMR && UNITY_WSA_10_0 new WindowsMR_Editor.HeadTargetProps(serializedObject, headTarget), #endif #if hWAVEVR new WaveVR_Editor.HeadTargetProps(serializedObject, headTarget), #endif #if hVRTK new Vrtk_Editor.HeadTargetProps(serializedObject, headTarget), #endif #if hREALSENSE new Realsense_Editor.HeadTargetProps(serializedObject, headTarget), #endif #if hKINECT1 new Kinect1_Editor.HeadTargetProps(serializedObject, headTarget), #endif #if hKINECT2 new Kinect2_Editor.HeadTargetProps(serializedObject, headTarget), #endif #if hKINECT4 new Kinect4_Editor.HeadTargetProps(serializedObject, headTarget), #endif #if hORBBEC new Astra_Editor.HeadTargetProps(serializedObject, headTarget), #endif #if hOPTITRACK new Optitrack_Editor.HeadTargetProps(serializedObject, headTarget), #endif #if hNEURON new Neuron_Editor.HeadTargetProps(serializedObject, headTarget), #endif #if hTOBII new Tobii_Editor.HeadTargetProps(serializedObject, headTarget), #endif #if hARKIT new ArKit_Editor.HeadTargetProps(serializedObject, headTarget), #endif #if hDLIB new Dlib_Editor.HeadTargetProps(serializedObject, headTarget), #endif #if hPUPIL new Tracking.Pupil.Pupil_Editor.HeadTargetProps(serializedObject, headTarget), #endif #if hANTILATENCY new Antilatency_Editor.HeadTargetProps(serializedObject, headTarget), #endif #if hCUSTOM new Custom_Editor.HeadTargetProps(serializedObject, headTarget), #endif }; } #endregion #region Disable public void OnDisable() { if (humanoid == null) { // This target is not connected to a humanoid, so we delete it DestroyImmediate(headTarget, true); return; } if (!Application.isPlaying) { SetSensor2Target(); } #if hFACE FaceTarget_Editor.OnDisable(serializedObject, headTarget); #endif } private void SetSensor2Target() { if (allProps != null) foreach (TargetProps props in allProps) props.SetSensor2Target(); } #endregion #region Inspector public override void OnInspectorGUI() { if (headTarget == null || humanoid == null) return; serializedObject.Update(); SensorInspectors(headTarget); if (headTarget.humanoid != null) { #if hFACE SerializedProperty faceTargetProp = serializedObject.FindProperty("face"); FaceTarget_Editor.OnInspectorGUI(faceTargetProp, headTarget); #endif ConfigurationInspector(headTarget); #if hFACE FaceTarget_Editor.ExpressionsInspector(headTarget.face); #endif } PoseInspector(); SettingsInspector(headTarget); #if hFACE FaceTarget_Editor.FocusObjectInspector(headTarget.face); #endif EventsInspector(); GazeInteractionButton(headTarget); serializedObject.ApplyModifiedProperties(); } private static HumanoidControl GetHumanoid(HumanoidTarget target) { HumanoidControl foundHumanoid = target.transform.GetComponentInParent(); if (foundHumanoid != null) return foundHumanoid; HumanoidControl[] humanoids = GameObject.FindObjectsOfType(); for (int i = 0; i < humanoids.Length; i++) if (humanoids[i].headTarget.transform == target.transform) foundHumanoid = humanoids[i]; return foundHumanoid; } #region Sensors #if hFACE private SerializedProperty microphoneEnabledProp; #endif private void InitSensors() { #if hFACE microphoneEnabledProp = serializedObject.FindProperty("microphone.enabled"); #endif } private bool showControllers = true; private void SensorInspectors(HeadTarget headTarget) { showControllers = EditorGUILayout.Foldout(showControllers, "Controllers", true); if (showControllers) { EditorGUI.indentLevel++; FirstPersonCameraInspector(headTarget); ScreenInspector(headTarget); foreach (TargetProps props in allProps) props.Inspector(); AnimatorInspector(headTarget); EditorGUI.indentLevel--; } } private void FirstPersonCameraInspector(HeadTarget headTarget) { #if pUNITYXR || hLEGACYXR if (headTarget.unityXR == null) return; #endif #if hOPENVR && hVIVETRACKER && (UNITY_STANDALONE_WIN || UNITY_STANDALONE_OSX) EditorGUI.BeginDisabledGroup(headTarget.humanoid.openVR.enabled && headTarget.viveTracker.enabled); #endif #if pUNITYXR || hLEGACYXR bool wasEnabled = headTarget.unityXR.enabled; #endif EditorGUILayout.BeginHorizontal(); EditorGUI.BeginChangeCheck(); #if hLEGACYXR #if hOPENVR && hVIVETRACKER && (UNITY_STANDALONE_WIN || UNITY_STANDALONE_OSX) if (headTarget.humanoid.openVR.enabled && headTarget.viveTracker.enabled) headTarget.unity.enabled = false; #endif #endif #pragma warning disable 219 bool enabled #if pUNITYXR || hLEGACYXR = EditorGUILayout.ToggleLeft(headTarget.unityXR.name, headTarget.unityXR.enabled, GUILayout.MinWidth(80)); if (EditorGUI.EndChangeCheck()) { Undo.RecordObject(headTarget, enabled ? "Enabled " : "Disabled " + headTarget.unityXR.name); headTarget.unityXR.enabled = enabled; } #else = false; #endif #pragma warning restore 219 EditorGUILayout.EndHorizontal(); #if hFACE if (enabled) { // && microphoneEnabledProp != null) { EditorGUI.indentLevel++; microphoneEnabledProp.boolValue = EditorGUILayout.ToggleLeft("Microphone", microphoneEnabledProp.boolValue); EditorGUI.indentLevel--; } #endif #if pUNITYXR //if (!Application.isPlaying) { // //Passer.Tracking.UnityXRHmd.CheckCamera(headTarget); // if (!wasEnabled && headTarget.unity.enabled) { // Passer.Tracking.UnityXRHmd.AddCamera(headTarget.unity); // } // else if (wasEnabled && !headTarget.unity.enabled) { // Passer.Tracking.UnityXRHmd.RemoveCamera(headTarget.unity); // } //} #endif #if hOPENVR && hVIVETRACKER && (UNITY_STANDALONE_WIN || UNITY_STANDALONE_OSX) EditorGUI.EndDisabledGroup(); #endif } private void ScreenInspector(HeadTarget headTarget) { if (headTarget.virtual3d && headTarget.humanoid.showRealObjects) { if (headTarget.screenTransform == null) CreateScreen(headTarget); headTarget.screenTransform.gameObject.SetActive(true); } else if (headTarget.screenTransform != null) headTarget.screenTransform.gameObject.SetActive(false); } private void CreateScreen(HeadTarget headTarget) { GameObject realWorld = HumanoidControl.GetRealWorld(headTarget.humanoid.transform); headTarget.screenTransform = realWorld.transform.Find("Screen"); if (headTarget.screenTransform == null) { GameObject screenObj = GameObject.CreatePrimitive(PrimitiveType.Cube); //new GameObject("Screen"); screenObj.name = "Screen"; headTarget.screenTransform = screenObj.transform; headTarget.screenTransform.parent = realWorld.transform; headTarget.screenTransform.localPosition = headTarget.transform.position + headTarget.transform.forward; headTarget.screenTransform.rotation = headTarget.transform.rotation * Quaternion.AngleAxis(180, Vector3.up); headTarget.screenTransform.localScale = new Vector3(0.476F, 0.2677F, 0.02F); // 21.5 inch 16:9 screen size } } private void AnimatorInspector(HeadTarget headTarget) { if (headTarget.humanoid == null) return; SerializedProperty animatorProp = serializedObject.FindProperty(nameof(HeadTarget.headAnimator)+ "." +nameof(HeadTarget.headAnimator.enabled)); if (animatorProp != null && headTarget.humanoid.animatorEnabled) { GUIContent text = new GUIContent( "Procedural Animation", "Controls the head when no tracking is active" ); animatorProp.boolValue = EditorGUILayout.ToggleLeft("Procedural Animation", animatorProp.boolValue, GUILayout.MinWidth(80)); if (headTarget.headAnimator.enabled) { EditorGUI.indentLevel++; #if hFACE headTarget.face.behaviour.enabled = EditorGUILayout.ToggleLeft("Eye Behaviour", headTarget.face.behaviour.enabled); #endif EditorGUI.indentLevel--; } } } #endregion #region Configuration private bool showConfiguration; private bool showLeftEye; private bool showRightEye; private void ConfigurationInspector(HeadTarget headTarget) { if (headTarget.humanoid == null) return; showConfiguration = EditorGUILayout.Foldout(showConfiguration, "Configuration", true); if (showConfiguration) { EditorGUI.indentLevel++; #if hFACE SerializedProperty faceProp = serializedObject.FindProperty("face"); FaceTarget_Editor.ConfigurationInspector(faceProp, headTarget.face); #endif HeadConfigurationInspector(ref headTarget.head); NeckConfigurationInspector(ref headTarget.neck); EditorGUI.indentLevel--; } } private void HeadConfigurationInspector(ref HeadTarget.TargetedHeadBone head) { head.bone.transform = (Transform)EditorGUILayout.ObjectField("Head", head.bone.transform, typeof(Transform), true); if (head.bone.transform != null) { EditorGUI.indentLevel++; EditorGUILayout.BeginHorizontal(); head.bone.maxAngle = EditorGUILayout.Slider("Max Angle", head.bone.maxAngle, 0, 180); if (GUILayout.Button("R", GUILayout.Width(20))) { head.bone.maxAngle = HeadTarget.maxHeadAngle; } EditorGUILayout.EndHorizontal(); EditorGUI.indentLevel--; } } private void NeckConfigurationInspector(ref HeadTarget.TargetedNeckBone neck) { neck.bone.transform = (Transform)EditorGUILayout.ObjectField("Neck", neck.bone.transform, typeof(Transform), true); if (neck.bone.transform != null) { EditorGUI.indentLevel++; EditorGUILayout.BeginHorizontal(); neck.bone.maxAngle = EditorGUILayout.Slider("Max Angle", neck.bone.maxAngle, 0, 180); if (GUILayout.Button("R", GUILayout.Width(20))) { neck.bone.maxAngle = HeadTarget.maxNeckAngle; } EditorGUILayout.EndHorizontal(); EditorGUI.indentLevel--; } } #endregion #region Pose private void PoseInspector() { if (!Application.isPlaying && humanoid.pose != null && humanoid.editPose) humanoid.pose.UpdatePose(humanoid); } #endregion #region Settings private bool showSettings; private void SettingsInspector(HeadTarget headTarget) { showSettings = EditorGUILayout.Foldout(showSettings, "Settings", true); if (showSettings) { EditorGUI.indentLevel++; CollisionFaderInspector(); EditorGUI.indentLevel--; } } private void CollisionFaderInspector() { SerializedProperty collisionFaderProp = serializedObject.FindProperty("collisionFader"); collisionFaderProp.boolValue = EditorGUILayout.Toggle("Collision Fader", collisionFaderProp.boolValue); } #endregion #region Events protected int selectedEventSource = -1; protected int selectedEvent; protected bool showEvents; protected virtual void EventsInspector() { showEvents = EditorGUILayout.Foldout(showEvents, "Events", true); if (showEvents) { EditorGUI.indentLevel++; TrackingEventInspector(); AudioEventInspector(); #if hFACE FocusEventInspector(); BlinkEventInspector(); #endif InsideColliderInspector(); EditorGUI.indentLevel--; } } protected void TrackingEventInspector() { SerializedProperty trackingEventProp = serializedObject.FindProperty("trackingEvent"); BoolEvent_Editor.EventInspector(trackingEventProp, headTarget.trackingEvent, ref selectedEventSource, ref selectedEvent); } protected void AudioEventInspector() { SerializedProperty audioEventProp = serializedObject.FindProperty("audioEvent"); FloatEvent_Editor.EventInspector(audioEventProp, headTarget.audioEvent, ref selectedEventSource, ref selectedEvent); } #if hFACE protected void FocusEventInspector() { SerializedProperty focusEventProp = serializedObject.FindProperty("focusEvent"); GameObjectEvent_Editor.EventInspector(focusEventProp, headTarget.focusEvent, ref selectedEventSource, ref selectedEvent); } protected void BlinkEventInspector() { SerializedProperty blinkEventProp = serializedObject.FindProperty("blinkEvent"); BoolEvent_Editor.EventInspector(blinkEventProp, headTarget.blinkEvent, ref selectedEventSource, ref selectedEvent); } #endif protected void InsideColliderInspector() { SerializedProperty insideColliderEventProp = serializedObject.FindProperty("insideColliderEvent"); BoolEvent_Editor.EventInspector(insideColliderEventProp, headTarget.insideColliderEvent, ref selectedEventSource, ref selectedEvent); } // protected void EventDetails(int selectedEvent) { // switch (selectedEvent) { // case 0: // FloatEvent_Editor.DetailsInspector(headTarget.audioEvent, audioEventProp, "Audio"); // break; //#if hFACE // case 1: // GameObjectEvent_Editor.DetailsInspector(focusEventProp, "Focus"); // break; // case 2: // BoolEvent_Editor.DetailsInspector(blinkEventProp, "Blink"); // break; //#endif // } // } #endregion #region Buttons private void GazeInteractionButton(HeadTarget headTarget) { InteractionPointer interactionPointer = headTarget.transform.GetComponentInChildren(); if (interactionPointer != null) return; GUILayout.BeginHorizontal(); if (GUILayout.Button("Add Interaction Pointer")) AddInteractionPointer(); if (GUILayout.Button("Add Teleporter")) AddTeleporter(); GUILayout.EndHorizontal(); } private void AddInteractionPointer() { } private void AddTeleporter() { } #endregion #endregion #region Scene public void OnSceneGUI() { if (Application.isPlaying) return; if (headTarget == null || headTarget.humanoid == null) return; #if hFACE FaceTarget_Editor.UpdateScene(headTarget.face); #endif if (humanoid.pose != null) { if (humanoid.editPose) humanoid.pose.UpdatePose(humanoid); else { humanoid.pose.Show(humanoid); headTarget.CopyRigToTarget(); } } // update the target rig from the current head target headTarget.CopyTargetToRig(); // update the avatar bones from the target rig humanoid.UpdateMovements(); // match the target rig with the new avatar pose humanoid.MatchTargetsToAvatar(); // and update all targets to match the target rig humanoid.CopyRigToTargets(); // Update the sensors to match the updated targets humanoid.UpdateSensorsFromTargets(); } #endregion public abstract class TargetProps { public SerializedProperty enabledProp; public SerializedProperty sensorTransformProp; public SerializedProperty sensor2TargetPositionProp; public SerializedProperty sensor2TargetRotationProp; public HeadTarget headTarget; public HeadSensor sensor; public TargetProps(SerializedObject serializedObject, HeadSensor _sensor, HeadTarget _headTarget, string unitySensorName) { enabledProp = serializedObject.FindProperty(unitySensorName + ".enabled"); sensorTransformProp = serializedObject.FindProperty(unitySensorName + ".sensorTransform"); sensor2TargetPositionProp = serializedObject.FindProperty(unitySensorName + ".sensor2TargetPosition"); sensor2TargetRotationProp = serializedObject.FindProperty(unitySensorName + ".sensor2TargetRotation"); headTarget = _headTarget; sensor = _sensor; sensor.Init(headTarget); } public virtual void SetSensor2Target() { if (sensor.sensorTransform == null) return; sensor2TargetRotationProp.quaternionValue = Quaternion.Inverse(sensor.sensorTransform.rotation) * headTarget.head.target.transform.rotation; sensor2TargetPositionProp.vector3Value = -headTarget.head.target.transform.InverseTransformPoint(sensor.sensorTransform.position); } public abstract void Inspector(); } } }