update CCK to 3.10, fixing unity 2021 crash :)
This commit is contained in:
parent
48a978fa2a
commit
d11e0fb3a9
492 changed files with 2165204 additions and 437687 deletions
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 77bf502e56374ff8ba1488a070dc2435
|
||||
timeCreated: 1709423065
|
|
@ -0,0 +1,82 @@
|
|||
#if UNITY_EDITOR
|
||||
using ABI.CCK.Scripts;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using AnimatorController = UnityEditor.Animations.AnimatorController;
|
||||
|
||||
namespace ABI.CCK.Components
|
||||
{
|
||||
public partial class CCK_CVRAvatarEditor
|
||||
{
|
||||
// store so we can also know when an undo/redo happened
|
||||
private RuntimeAnimatorController _lastBaseController;
|
||||
private RuntimeAnimatorController _lastOverrideController;
|
||||
|
||||
private void DrawAutogenControllerFields()
|
||||
{
|
||||
RuntimeAnimatorController tempBaseController = EditorGUILayout.ObjectField(
|
||||
"Base Controller",
|
||||
m_baseControllerProp.objectReferenceValue,
|
||||
typeof(RuntimeAnimatorController),
|
||||
false) as RuntimeAnimatorController;
|
||||
|
||||
if (tempBaseController is not AnimatorOverrideController
|
||||
&& tempBaseController != m_baseControllerProp.objectReferenceValue)
|
||||
m_baseControllerProp.objectReferenceValue = tempBaseController;
|
||||
|
||||
EditorGUILayout.HelpBox(
|
||||
"This is the Base Controller that is extended for the creation of your Advanced Avatar Settings. " +
|
||||
"If you do not want to extend a specific Animator Controller, make sure that the Default Avatar Animator " +
|
||||
"From the Directory 'ABI.CCK/Animations' is used here.", MessageType.Info);
|
||||
|
||||
RuntimeAnimatorController tempOverrideController = EditorGUILayout.ObjectField(
|
||||
"Override Controller",
|
||||
m_baseOverrideControllerProp.objectReferenceValue,
|
||||
typeof(RuntimeAnimatorController),
|
||||
false) as RuntimeAnimatorController;
|
||||
|
||||
if (tempOverrideController is not AnimatorController
|
||||
&& tempOverrideController != m_baseOverrideControllerProp.objectReferenceValue)
|
||||
m_baseOverrideControllerProp.objectReferenceValue = tempOverrideController;
|
||||
|
||||
EditorGUILayout.HelpBox(
|
||||
"You can put your previous Override Controller here in order to put your overrides in " +
|
||||
"the newly created Override Controller.", MessageType.Info);
|
||||
|
||||
if (_lastBaseController == m_baseControllerProp.objectReferenceValue
|
||||
&& _lastOverrideController == m_baseOverrideControllerProp.objectReferenceValue)
|
||||
return;
|
||||
|
||||
// change detected (undo/redo, inspector change, etc.)
|
||||
_lastBaseController = m_baseControllerProp.objectReferenceValue as RuntimeAnimatorController;
|
||||
_lastOverrideController = m_baseOverrideControllerProp.objectReferenceValue as RuntimeAnimatorController;
|
||||
|
||||
serializedObject.ApplyModifiedProperties(); // update backing fields
|
||||
UpdateSyncUsageAndBaseParameters(); // update the sync usage and base parameters
|
||||
}
|
||||
|
||||
private void DrawCreateControllerButton()
|
||||
{
|
||||
if (_syncedBitsTuple.Item2 <= CVRCommon.AVATAR_BIT_LIMIT)
|
||||
{
|
||||
if (GUILayout.Button("Create Controller"))
|
||||
CreateAASController();
|
||||
}
|
||||
else
|
||||
{
|
||||
using (new EditorGUI.DisabledScope(true))
|
||||
GUILayout.Button("Create Controller");
|
||||
EditorGUILayout.HelpBox(
|
||||
"Cannot create controller. You are over the Synced Bit Limit!", MessageType.Warning);
|
||||
GUILayout.Space(5);
|
||||
}
|
||||
|
||||
if (_avatar.avatarSettings.overrides == null ||
|
||||
_avatar.avatarSettings.overrides == _avatar.overrides) return;
|
||||
|
||||
if (GUILayout.Button("Attach created Override to Avatar"))
|
||||
m_OverridesProp.objectReferenceValue = _avatar.avatarSettings.overrides;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d6aa02cac0694cab9ba81de900bdf4c6
|
||||
timeCreated: 1714267882
|
|
@ -0,0 +1,318 @@
|
|||
#if UNITY_EDITOR
|
||||
using System.Text.RegularExpressions;
|
||||
using ABI.CCK.Scripts;
|
||||
using ABI.CCK.Scripts.Editor;
|
||||
using UnityEditor;
|
||||
using UnityEditorInternal;
|
||||
using UnityEngine;
|
||||
using AnimatorController = UnityEditor.Animations.AnimatorController;
|
||||
|
||||
namespace ABI.CCK.Components
|
||||
{
|
||||
public partial class CCK_CVRAvatarEditor
|
||||
{
|
||||
#region ReorderableListDrawing AAS
|
||||
|
||||
private void OnMouseUpAAS(ReorderableList list)
|
||||
{
|
||||
if (list.index != _selectedAdvSetting)
|
||||
{
|
||||
_selectedAdvSetting = list.index;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (list.index == -1)
|
||||
return;
|
||||
|
||||
list.Deselect(_selectedAdvSetting);
|
||||
list.index = _selectedAdvSetting = -1;
|
||||
Repaint();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnChangedAAS(ReorderableList list)
|
||||
{
|
||||
EditorUtility.SetDirty(_avatar);
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
private void OnReorderedAAS(ReorderableList list)
|
||||
{
|
||||
// this is a bandaid fix for the reorderable list not updating the nested reorderable list
|
||||
foreach (CVRAdvancedSettingsEntry aasEntry in _advSettingsList.list)
|
||||
{
|
||||
CVRAdvancesAvatarSettingBase setting = aasEntry.setting;
|
||||
switch (setting)
|
||||
{
|
||||
case CVRAdvancesAvatarSettingGameObjectToggle toggle:
|
||||
toggle.reorderableList = null; // needs recreation
|
||||
break;
|
||||
case CVRAdvancedAvatarSettingMaterialColor color:
|
||||
color.reorderableList = null; // needs recreation
|
||||
break;
|
||||
case CVRAdvancesAvatarSettingSlider slider:
|
||||
slider.reorderableList = null; // needs recreation
|
||||
break;
|
||||
case CVRAdvancesAvatarSettingGameObjectDropdown dropdown:
|
||||
dropdown.reorderableList = null; // needs recreation
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDrawHeaderAAS(Rect rect)
|
||||
{
|
||||
Rect labelRect = new(rect.x, rect.y, rect.width - 35, EditorGUIUtility.singleLineHeight);
|
||||
GUI.Label(labelRect, $"Inputs ({_advSettingsList.count})");
|
||||
EditorGUIExtensions.UtilityMenu(rect, _advSettingsList, m_settingEntriesProp, AppendUtilityMenu);
|
||||
}
|
||||
|
||||
private float OnHeightElementAAS(int index)
|
||||
{
|
||||
float height = EditorGUIUtility.singleLineHeight * 1.25f;
|
||||
if (index < 0 || index >= _avatar.avatarSettings.settings.Count)
|
||||
return height;
|
||||
|
||||
CVRAdvancedSettingsEntry advSettingEntry = _avatar.avatarSettings.settings[index];
|
||||
if (advSettingEntry.isCollapsed)
|
||||
return height;
|
||||
|
||||
float lines = 5f; // 4 lines for the basic settings + 1 for the autogen foldout
|
||||
|
||||
switch (advSettingEntry.setting)
|
||||
{
|
||||
case CVRAdvancesAvatarSettingGameObjectToggle toggle:
|
||||
if (advSettingEntry.isAutogenCollapsed) break;
|
||||
if (toggle.useAnimationClip)
|
||||
{
|
||||
lines += 3f;
|
||||
break;
|
||||
}
|
||||
lines += 3.5f; // reorderable list header & footer + space after
|
||||
lines += toggle.gameObjectTargets.Count == 0 ? 1f : toggle.gameObjectTargets.Count * 1.09f;
|
||||
break;
|
||||
case CVRAdvancedAvatarSettingMaterialColor color:
|
||||
if (advSettingEntry.isAutogenCollapsed) break;
|
||||
lines += 2.5f; // reorderable list header & footer + space after
|
||||
lines += color.materialColorTargets.Count == 0 ? 1f : color.materialColorTargets.Count * 1.09f;
|
||||
break;
|
||||
case CVRAdvancesAvatarSettingSlider slider:
|
||||
if (advSettingEntry.isAutogenCollapsed) break;
|
||||
if (slider.useAnimationClip)
|
||||
{
|
||||
lines += 3f;
|
||||
break;
|
||||
}
|
||||
lines += 3.5f; // reorderable list header & footer + space after
|
||||
lines += slider.materialPropertyTargets.Count == 0 ? 2f : slider.materialPropertyTargets.Count * 2.09f;
|
||||
break;
|
||||
case CVRAdvancesAvatarSettingJoystick2D or CVRAdvancesAvatarSettingJoystick3D:
|
||||
lines += 3.5f; // no fancy autogen stuff but has min/max
|
||||
break;
|
||||
case CVRAdvancesAvatarSettingInputSingle or CVRAdvancesAvatarSettingInputVector2 or CVRAdvancesAvatarSettingInputVector3:
|
||||
lines += 1.5f; // no fancy autogen stuff
|
||||
break;
|
||||
case CVRAdvancesAvatarSettingGameObjectDropdown dropdown:
|
||||
lines += 2f; // reorderable list header & footer, but one less due to autogen foldout
|
||||
lines += dropdown.options.Count == 0 ? 1f : dropdown.options.Count * 1.09f;
|
||||
|
||||
foreach (CVRAdvancedSettingsDropDownEntry dropDownEntry in dropdown.options)
|
||||
{
|
||||
if (dropDownEntry.isAutogenCollapsed)
|
||||
continue;
|
||||
|
||||
lines += 1f; // use animation toggle
|
||||
if (dropDownEntry.useAnimationClip)
|
||||
{
|
||||
lines += 1f; // one animation field
|
||||
continue;
|
||||
}
|
||||
lines += 2.5f; // reorderable list header & footer + space after
|
||||
lines += dropDownEntry.gameObjectTargets.Count == 0 ? 1f : dropDownEntry.gameObjectTargets.Count * 1.09f;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return lines * height;
|
||||
}
|
||||
|
||||
private void OnDrawElementAAS(Rect rect, int index, bool isActive, bool isFocused)
|
||||
{
|
||||
if (index >= _advSettingsList.count) return;
|
||||
CVRAdvancedSettingsEntry advSettingEntry = (CVRAdvancedSettingsEntry)_advSettingsList.list[index];
|
||||
SerializedProperty advSettingEntryProp = m_settingEntriesProp.GetArrayElementAtIndex(index);
|
||||
if (advSettingEntryProp == null || index >= m_settingEntriesProp.arraySize)
|
||||
return;
|
||||
|
||||
DrawAdvSettingEntry(rect, advSettingEntry, advSettingEntryProp);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Drawing Methods
|
||||
|
||||
private void DrawAdvSettingEntry(Rect rect, CVRAdvancedSettingsEntry advSettingEntry, SerializedProperty advSettingEntryProp)
|
||||
{
|
||||
// boilerplate
|
||||
rect = new Rect(rect.x, rect.y + 2, rect.width, EditorGUIUtility.singleLineHeight);
|
||||
float spacing = EditorGUIUtility.singleLineHeight * 1.25f;
|
||||
float originalLabelWidth = EditorGUIUtility.labelWidth;
|
||||
EditorGUIUtility.labelWidth = 120;
|
||||
|
||||
SerializedProperty nameProp = advSettingEntryProp.FindPropertyRelative(nameof(CVRAdvancedSettingsEntry.name));
|
||||
SerializedProperty machineNameProp = advSettingEntryProp.FindPropertyRelative(nameof(CVRAdvancedSettingsEntry.machineName));
|
||||
SerializedProperty entryType = advSettingEntryProp.FindPropertyRelative(nameof(CVRAdvancedSettingsEntry.type));
|
||||
SerializedProperty isCollapsed = advSettingEntryProp.FindPropertyRelative(nameof(CVRAdvancedSettingsEntry.isCollapsed));
|
||||
SerializedProperty unlinkNameFromMachine = advSettingEntryProp.FindPropertyRelative(nameof(CVRAdvancedSettingsEntry.unlinkNameFromMachineName));
|
||||
|
||||
Texture2D autoIcon = (unlinkNameFromMachine.boolValue
|
||||
? EditorGUIUtility.Load("d_Linked")
|
||||
: EditorGUIUtility.Load("d_Unlinked")) as Texture2D;
|
||||
GUIContent autoButtonContent = new(autoIcon, "Unlink/Link Name to Machine");
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
Rect positionFull = rect;
|
||||
var remainingWidth = positionFull.width - EditorGUIUtility.labelWidth - 20f;
|
||||
isCollapsed.boolValue = !EditorGUI.Foldout(
|
||||
new Rect(positionFull.x + 10, positionFull.y, EditorGUIUtility.labelWidth - 20f, positionFull.height),
|
||||
!isCollapsed.boolValue, "Name", true);
|
||||
|
||||
if (GUI.Button(new Rect(positionFull.x + EditorGUIUtility.labelWidth + 2f, positionFull.y, 20f,
|
||||
positionFull.height), autoButtonContent, GUIStyle.none))
|
||||
unlinkNameFromMachine.boolValue = !unlinkNameFromMachine.boolValue;
|
||||
|
||||
string oldName = nameProp.stringValue;
|
||||
EditorGUI.PropertyField(
|
||||
new Rect(positionFull.x + EditorGUIUtility.labelWidth + 24f, positionFull.y, remainingWidth,
|
||||
positionFull.height), nameProp, GUIContent.none);
|
||||
|
||||
if (oldName != nameProp.stringValue &&
|
||||
!unlinkNameFromMachine.boolValue) // update machine name if not unlinked
|
||||
machineNameProp.stringValue = Regex.Replace(nameProp.stringValue, @"[^a-zA-Z0-9/\-_#]", "");
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
if (isCollapsed.boolValue)
|
||||
{
|
||||
// TODO: Draw collapsed version
|
||||
EditorGUIUtility.labelWidth = originalLabelWidth;
|
||||
return;
|
||||
}
|
||||
|
||||
rect.y += spacing;
|
||||
|
||||
if (unlinkNameFromMachine.boolValue)
|
||||
{
|
||||
// search dropdown for machine name
|
||||
machineNameProp.stringValue = EditorGUIExtensions.AdvancedDropdownInput(
|
||||
rect,
|
||||
machineNameProp.stringValue,
|
||||
_baseControllerParams,
|
||||
"Parameter",
|
||||
"No Parameters"
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
//EditorGUI.LabelField(rect, "Parameter", machineNameProp.stringValue);
|
||||
GUI.enabled = false;
|
||||
EditorGUI.TextField(rect, "Parameter", machineNameProp.stringValue); // i like the border
|
||||
GUI.enabled = true;
|
||||
}
|
||||
|
||||
rect.y += spacing;
|
||||
|
||||
// setting type dropdowns
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
Rect position = rect;
|
||||
float halfWidth = (position.width - EditorGUIUtility.labelWidth) / 2 - 2;
|
||||
|
||||
// Setting Type
|
||||
var oldType = entryType.enumValueIndex;
|
||||
EditorGUI.PropertyField(
|
||||
new Rect(position.x, position.y, EditorGUIUtility.labelWidth + halfWidth, position.height), entryType,
|
||||
new GUIContent("Setting Type"));
|
||||
|
||||
// get the property for the specific setting type
|
||||
SerializedProperty advSettingProp = entryType.enumValueIndex switch
|
||||
{
|
||||
(int)CVRAdvancedSettingsEntry.SettingsType.Color => advSettingEntryProp.FindPropertyRelative(
|
||||
nameof(CVRAdvancedSettingsEntry.materialColorSettings)),
|
||||
(int)CVRAdvancedSettingsEntry.SettingsType.Dropdown => advSettingEntryProp.FindPropertyRelative(
|
||||
nameof(CVRAdvancedSettingsEntry.dropDownSettings)),
|
||||
(int)CVRAdvancedSettingsEntry.SettingsType.Slider => advSettingEntryProp.FindPropertyRelative(
|
||||
nameof(CVRAdvancedSettingsEntry.sliderSettings)),
|
||||
(int)CVRAdvancedSettingsEntry.SettingsType.Joystick2D => advSettingEntryProp.FindPropertyRelative(
|
||||
nameof(CVRAdvancedSettingsEntry.joystick2DSetting)),
|
||||
(int)CVRAdvancedSettingsEntry.SettingsType.Joystick3D => advSettingEntryProp.FindPropertyRelative(
|
||||
nameof(CVRAdvancedSettingsEntry.joystick3DSetting)),
|
||||
(int)CVRAdvancedSettingsEntry.SettingsType.InputSingle => advSettingEntryProp.FindPropertyRelative(
|
||||
nameof(CVRAdvancedSettingsEntry.inputSingleSettings)),
|
||||
(int)CVRAdvancedSettingsEntry.SettingsType.InputVector2 => advSettingEntryProp.FindPropertyRelative(
|
||||
nameof(CVRAdvancedSettingsEntry.inputVector2Settings)),
|
||||
(int)CVRAdvancedSettingsEntry.SettingsType.InputVector3 => advSettingEntryProp.FindPropertyRelative(
|
||||
nameof(CVRAdvancedSettingsEntry.inputVector3Settings)),
|
||||
_ => advSettingEntryProp.FindPropertyRelative(nameof(CVRAdvancedSettingsEntry.toggleSettings))
|
||||
};
|
||||
|
||||
// part of the setting base for whatever reason, so gotta get it after above
|
||||
SerializedProperty usedType = advSettingProp.FindPropertyRelative(nameof(CVRAdvancesAvatarSettingBase.usedType));
|
||||
|
||||
if (oldType != entryType.enumValueIndex)
|
||||
{
|
||||
usedType.intValue = (int)CVRAdvancedAvatarSettings.GetDefaultType((CVRAdvancedSettingsEntry.SettingsType)entryType.enumValueIndex);
|
||||
serializedObject.ApplyModifiedProperties(); // apply the change
|
||||
}
|
||||
|
||||
// used-type dropdown (filtered by setting type)
|
||||
usedType.intValue = (int)EnumFilter.FilteredEnumPopup(
|
||||
new Rect(position.x + EditorGUIUtility.labelWidth + halfWidth + 4, position.y, halfWidth,
|
||||
position.height),
|
||||
(CVRAdvancesAvatarSettingBase.ParameterType)usedType.intValue,
|
||||
CVRAdvancedAvatarSettings.GetSupportedTypes(
|
||||
(CVRAdvancedSettingsEntry.SettingsType)entryType.enumValueIndex)
|
||||
);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
// draw default value by type
|
||||
rect.y += spacing;
|
||||
switch ((CVRAdvancedSettingsEntry.SettingsType)entryType.enumValueIndex)
|
||||
{
|
||||
default:
|
||||
case CVRAdvancedSettingsEntry.SettingsType.Toggle:
|
||||
DrawToggleInternal(rect, spacing, advSettingEntry, advSettingProp);
|
||||
break;
|
||||
case CVRAdvancedSettingsEntry.SettingsType.Dropdown:
|
||||
DrawDropdownInternal(rect, spacing, advSettingEntry, advSettingProp);
|
||||
break;
|
||||
case CVRAdvancedSettingsEntry.SettingsType.Color:
|
||||
DrawColorInternal(rect, spacing, advSettingEntry, advSettingProp);
|
||||
break;
|
||||
case CVRAdvancedSettingsEntry.SettingsType.Slider:
|
||||
DrawSliderInternal(rect, spacing, advSettingEntry, advSettingProp);
|
||||
break;
|
||||
case CVRAdvancedSettingsEntry.SettingsType.Joystick2D:
|
||||
DrawJoystick2DInternal(rect, spacing, advSettingEntry, advSettingProp);
|
||||
break;
|
||||
case CVRAdvancedSettingsEntry.SettingsType.Joystick3D:
|
||||
DrawJoystick3DInternal(rect, spacing, advSettingEntry, advSettingProp);
|
||||
break;
|
||||
case CVRAdvancedSettingsEntry.SettingsType.InputSingle:
|
||||
DrawInputSingleInternal(rect, spacing, advSettingEntry, advSettingProp);
|
||||
break;
|
||||
case CVRAdvancedSettingsEntry.SettingsType.InputVector2:
|
||||
DrawInputVector2Internal(rect, spacing, advSettingEntry, advSettingProp);
|
||||
break;
|
||||
case CVRAdvancedSettingsEntry.SettingsType.InputVector3:
|
||||
DrawInputVector3Internal(rect, spacing, advSettingEntry, advSettingProp);
|
||||
break;
|
||||
}
|
||||
|
||||
EditorGUIUtility.labelWidth = originalLabelWidth;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6118c371302943b6bb8a740c62dd0a97
|
||||
timeCreated: 1714268283
|
|
@ -0,0 +1,245 @@
|
|||
#if UNITY_EDITOR
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ABI.CCK.Scripts;
|
||||
using ABI.CCK.Scripts.Editor;
|
||||
using UnityEditor;
|
||||
using UnityEditorInternal;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using static ABI.CCK.Scripts.Editor.SharedComponentGUI;
|
||||
|
||||
namespace ABI.CCK.Components
|
||||
{
|
||||
public partial class CCK_CVRAvatarEditor
|
||||
{
|
||||
private void DrawColorInternal(
|
||||
Rect rect,
|
||||
float spacing,
|
||||
CVRAdvancedSettingsEntry advSettingEntry,
|
||||
SerializedProperty advSettingProp)
|
||||
{
|
||||
CVRAdvancedAvatarSettingMaterialColor colorSetting = (CVRAdvancedAvatarSettingMaterialColor)advSettingEntry.setting;
|
||||
|
||||
SerializedProperty defaultValueProp = advSettingProp.FindPropertyRelative(nameof(CVRAdvancedAvatarSettingMaterialColor.defaultValue));
|
||||
EditorGUI.PropertyField(rect, defaultValueProp, new GUIContent("Default Color"));
|
||||
rect.y += spacing;
|
||||
|
||||
// foldout
|
||||
advSettingEntry.isAutogenCollapsed = !EditorGUI.Foldout(rect, !advSettingEntry.isAutogenCollapsed, "Autogeneration Options", true, s_BoldFoldoutStyle);
|
||||
if (advSettingEntry.isAutogenCollapsed)
|
||||
return;
|
||||
|
||||
// autogen stuff
|
||||
rect.y += spacing;
|
||||
|
||||
SerializedProperty materialColorTargets = advSettingProp.FindPropertyRelative(nameof(CVRAdvancedAvatarSettingMaterialColor.materialColorTargets));
|
||||
|
||||
// Material Color Targets List
|
||||
if (colorSetting.reorderableList == null // recreate list if null or stale
|
||||
|| colorSetting.reorderableList.serializedProperty.serializedObject != serializedObject)
|
||||
{
|
||||
|
||||
colorSetting.reorderableList = new ReorderableList(serializedObject, materialColorTargets,
|
||||
true, true, true, true)
|
||||
{
|
||||
list = colorSetting.materialColorTargets,
|
||||
};
|
||||
|
||||
colorSetting.reorderableList.drawElementCallback = (innerRect, innerIndex, _, _) =>
|
||||
{
|
||||
innerRect = new Rect(innerRect.x, innerRect.y + 2, innerRect.width, EditorGUIUtility.singleLineHeight);
|
||||
SerializedProperty colorEntry = colorSetting.reorderableList.serializedProperty.GetArrayElementAtIndex(innerIndex);
|
||||
|
||||
const float elementSpacing = 5;
|
||||
const float infoIconWidth = 20;
|
||||
float innerRectHalfWidth = innerRect.width * 0.5f;
|
||||
|
||||
// GameObject Field
|
||||
innerRect.width = innerRectHalfWidth;
|
||||
SerializedProperty materialColorProp = colorEntry.FindPropertyRelative(nameof(CVRAdvancedSettingsTargetEntryMaterialColor.gameObject));
|
||||
EditorGUI.PropertyField(innerRect, materialColorProp, GUIContent.none);
|
||||
|
||||
string tooltipPath = "No Target";
|
||||
var propertyList = new Dictionary<string, string>();
|
||||
if (materialColorProp.objectReferenceValue != null)
|
||||
{
|
||||
Transform avatarTransform = _avatar.transform;
|
||||
Transform targetTransform = ((GameObject)materialColorProp.objectReferenceValue).transform;
|
||||
if (!targetTransform.IsChildOf(avatarTransform))
|
||||
{
|
||||
materialColorProp.objectReferenceValue = null; // invalid
|
||||
}
|
||||
else
|
||||
{
|
||||
MeshRenderer meshRenderer = targetTransform.GetComponent<MeshRenderer>();
|
||||
SkinnedMeshRenderer skinnedMeshRenderer = targetTransform.GetComponent<SkinnedMeshRenderer>();
|
||||
ParticleSystemRenderer particleRenderer = targetTransform.GetComponent<ParticleSystemRenderer>();
|
||||
LineRenderer lineRenderer =targetTransform.GetComponent<LineRenderer>();
|
||||
TrailRenderer trailRenderer = targetTransform.GetComponent<TrailRenderer>();
|
||||
|
||||
bool rendererFound = meshRenderer || skinnedMeshRenderer || particleRenderer ||
|
||||
lineRenderer || trailRenderer;
|
||||
|
||||
if (rendererFound)
|
||||
{
|
||||
#region Populate Property List
|
||||
|
||||
if (meshRenderer != null)
|
||||
{
|
||||
foreach (Material material in meshRenderer.sharedMaterials)
|
||||
{
|
||||
if (material == null) continue;
|
||||
Shader shader = material.shader;
|
||||
for (var j = 0; j < shader.GetPropertyCount(); j++)
|
||||
{
|
||||
if (shader.GetPropertyType(j) != ShaderPropertyType.Color) continue;
|
||||
var propertyKey = "MeshRenderer: " + shader.GetPropertyDescription(j) +
|
||||
"(" + shader.GetPropertyName(j) + ")";
|
||||
if (!propertyList.ContainsKey(propertyKey))
|
||||
propertyList.Add(propertyKey, "MSR:" + shader.GetPropertyName(j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (skinnedMeshRenderer != null)
|
||||
{
|
||||
foreach (Material material in skinnedMeshRenderer.sharedMaterials)
|
||||
{
|
||||
if (material == null) continue;
|
||||
Shader shader = material.shader;
|
||||
for (var j = 0; j < shader.GetPropertyCount(); j++)
|
||||
{
|
||||
if (shader.GetPropertyType(j) != ShaderPropertyType.Color) continue;
|
||||
var propertyKey = "SkinnedMeshRenderer: " +
|
||||
shader.GetPropertyDescription(j) + "(" +
|
||||
shader.GetPropertyName(j) + ")";
|
||||
if (!propertyList.ContainsKey(propertyKey))
|
||||
propertyList.Add(propertyKey, "SMR:" + shader.GetPropertyName(j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (particleRenderer != null)
|
||||
{
|
||||
foreach (Material material in particleRenderer.sharedMaterials)
|
||||
{
|
||||
if (material == null) continue;
|
||||
Shader shader = material.shader;
|
||||
for (var j = 0; j < shader.GetPropertyCount(); j++)
|
||||
{
|
||||
if (shader.GetPropertyType(j) != ShaderPropertyType.Color) continue;
|
||||
var propertyKey = "ParticleRenderer: " + shader.GetPropertyDescription(j) +
|
||||
"(" + shader.GetPropertyName(j) + ")";
|
||||
if (!propertyList.ContainsKey(propertyKey))
|
||||
propertyList.Add(propertyKey, "PTR:" + shader.GetPropertyName(j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lineRenderer != null)
|
||||
{
|
||||
foreach (Material material in lineRenderer.sharedMaterials)
|
||||
{
|
||||
if (material == null) continue;
|
||||
Shader shader = material.shader;
|
||||
for (var j = 0; j < shader.GetPropertyCount(); j++)
|
||||
{
|
||||
if (shader.GetPropertyType(j) != ShaderPropertyType.Color) continue;
|
||||
var propertyKey = "LineRenderer: " + shader.GetPropertyDescription(j) +
|
||||
"(" + shader.GetPropertyName(j) + ")";
|
||||
if (!propertyList.ContainsKey(propertyKey))
|
||||
propertyList.Add(propertyKey, "LNR:" + shader.GetPropertyName(j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (trailRenderer != null)
|
||||
{
|
||||
foreach (Material material in trailRenderer.sharedMaterials)
|
||||
{
|
||||
if (material == null) continue;
|
||||
Shader shader = material.shader;
|
||||
for (var j = 0; j < shader.GetPropertyCount(); j++)
|
||||
{
|
||||
if (shader.GetPropertyType(j) != ShaderPropertyType.Color) continue;
|
||||
var propertyKey = "TrailRenderer: " + shader.GetPropertyDescription(j) +
|
||||
"(" + shader.GetPropertyName(j) + ")";
|
||||
if (!propertyList.ContainsKey(propertyKey))
|
||||
propertyList.Add(propertyKey, "TLR:" + shader.GetPropertyName(j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
else
|
||||
{
|
||||
materialColorProp.objectReferenceValue = null; // invalid
|
||||
}
|
||||
|
||||
SerializedProperty materialColorPathProp = colorEntry.FindPropertyRelative(nameof(CVRAdvancedSettingsTargetEntryMaterialColor.treePath));
|
||||
materialColorPathProp.stringValue = AnimationUtility.CalculateTransformPath(targetTransform, avatarTransform);
|
||||
tooltipPath = $"Path: {avatarTransform.name}/{materialColorPathProp.stringValue}";
|
||||
}
|
||||
}
|
||||
|
||||
// Property Dropdown
|
||||
innerRect.x += innerRectHalfWidth + elementSpacing;
|
||||
innerRect.width -= 2*infoIconWidth - 2*elementSpacing; // left/right padding
|
||||
SerializedProperty propNameProp = colorEntry.FindPropertyRelative(nameof(CVRAdvancedSettingsTargetEntryMaterialColor.propertyName));
|
||||
SerializedProperty propTypeIdProp = colorEntry.FindPropertyRelative(nameof(CVRAdvancedSettingsTargetEntryMaterialColor.propertyTypeIdentifier));
|
||||
|
||||
// not valid as a serialized property?
|
||||
//SerializedProperty propTypeProp = toggleEntry.FindPropertyRelative(nameof(CVRAdvancedSettingsTargetEntryMaterialColor.propertyType));
|
||||
|
||||
//var propNames = propertyList.Keys.ToArray();
|
||||
var propDescs = propertyList.Values.ToArray();
|
||||
string currentItem = propNameProp.stringValue.Length > 0
|
||||
? propTypeIdProp.stringValue + ":" + propNameProp.stringValue
|
||||
: CVRCommon.NONE_OR_EMPTY;
|
||||
|
||||
// search dropdown for material properties
|
||||
int selected = EditorGUIExtensions.CustomPopup(
|
||||
innerRect,
|
||||
Array.IndexOf(propDescs, currentItem),
|
||||
propDescs,
|
||||
"No Properties"
|
||||
);
|
||||
|
||||
// name: _Color
|
||||
// typeid: SMR
|
||||
// type: SkinnedMeshRenderer
|
||||
|
||||
if (selected >= 0)
|
||||
{
|
||||
currentItem = propDescs[selected];
|
||||
propNameProp.stringValue = currentItem[4..];
|
||||
propTypeIdProp.stringValue = currentItem[..3];
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
// propType is not a valid serialized property
|
||||
// so autogen will need to handle this
|
||||
}
|
||||
|
||||
// Path Info
|
||||
innerRect.x += innerRect.width + elementSpacing;
|
||||
innerRect.width = infoIconWidth;
|
||||
EditorGUI.LabelField(innerRect, new GUIContent(EditorGUIUtility.IconContent("d_animationanimated").image, tooltipPath));
|
||||
};
|
||||
|
||||
colorSetting.reorderableList.elementHeight = EditorGUIUtility.singleLineHeight + 4f;
|
||||
colorSetting.reorderableList.drawHeaderCallback = (headerRect) =>
|
||||
{
|
||||
Rect labelRect = new(headerRect.x, headerRect.y, headerRect.width - 35, EditorGUIUtility.singleLineHeight);
|
||||
EditorGUI.LabelField(labelRect, $"Color Targets ({materialColorTargets.arraySize})");
|
||||
labelRect.x += 35; // not sure why this is needed, nested lists are weird
|
||||
EditorGUIExtensions.UtilityMenu(labelRect, colorSetting.reorderableList, materialColorTargets);
|
||||
};
|
||||
}
|
||||
|
||||
colorSetting.reorderableList.DoList(rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d0297c5beb0a41ea8108dcbda5dd6bc0
|
||||
timeCreated: 1709424213
|
|
@ -0,0 +1,202 @@
|
|||
#if UNITY_EDITOR
|
||||
using ABI.CCK.Scripts;
|
||||
using ABI.CCK.Scripts.Editor;
|
||||
using UnityEditor;
|
||||
using UnityEditorInternal;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ABI.CCK.Components
|
||||
{
|
||||
public partial class CCK_CVRAvatarEditor
|
||||
{
|
||||
void DrawDropdownInternal(
|
||||
Rect rect,
|
||||
float spacing,
|
||||
CVRAdvancedSettingsEntry advSettingEntry,
|
||||
SerializedProperty advSettingProp)
|
||||
{
|
||||
CVRAdvancesAvatarSettingGameObjectDropdown dropdownSetting = (CVRAdvancesAvatarSettingGameObjectDropdown)advSettingEntry.setting;
|
||||
|
||||
SerializedProperty defaultValueProp =
|
||||
advSettingProp.FindPropertyRelative(nameof(CVRAdvancesAvatarSettingGameObjectDropdown
|
||||
.defaultValue));
|
||||
|
||||
int selectedValue = defaultValueProp.intValue; // default value is index of the dropdown options
|
||||
selectedValue = EditorGUI.Popup(rect, "Default Value", selectedValue, dropdownSetting.optionNames);
|
||||
defaultValueProp.intValue = selectedValue;
|
||||
rect.y += spacing;
|
||||
|
||||
// foldout
|
||||
// advSettingEntry.isAutogenCollapsed = !EditorGUI.Foldout(rect, !advSettingEntry.isAutogenCollapsed,
|
||||
// "Autogeneration Options", true, s_BoldFoldoutStyle);
|
||||
// if (advSettingEntry.isAutogenCollapsed)
|
||||
// return;
|
||||
|
||||
// autogen stuff
|
||||
//rect.y += spacing;
|
||||
SerializedProperty dropdownOptionsProp = advSettingProp.FindPropertyRelative(nameof(CVRAdvancesAvatarSettingGameObjectDropdown.options));
|
||||
|
||||
// GameObject Targets List
|
||||
if (dropdownSetting.reorderableList == null // recreate list if null or stale
|
||||
|| dropdownSetting.reorderableList.serializedProperty.serializedObject != serializedObject)
|
||||
{
|
||||
dropdownSetting.reorderableList = new ReorderableList(serializedObject, dropdownOptionsProp,
|
||||
true, true, true, true)
|
||||
{
|
||||
list = dropdownSetting.options,
|
||||
};
|
||||
|
||||
dropdownSetting.reorderableList.drawElementCallback = (innerRect, innerIndex, _, _) =>
|
||||
{
|
||||
if (innerIndex < 0 || innerIndex >= dropdownSetting.options.Count)
|
||||
return;
|
||||
|
||||
innerRect = new Rect(innerRect.x, innerRect.y + 2, innerRect.width, EditorGUIUtility.singleLineHeight);
|
||||
SerializedProperty dropdownEntryProp = dropdownSetting.reorderableList.serializedProperty.GetArrayElementAtIndex(innerIndex);
|
||||
CVRAdvancedSettingsDropDownEntry dropDownEntry = dropdownSetting.options[innerIndex];
|
||||
|
||||
const float elementSpacing = 10;
|
||||
float innerRectHalfWidth = innerRect.width * 0.5f;
|
||||
Rect splitRect = new(innerRect);
|
||||
|
||||
// Autogen Dropdown (i have no fucking idea how to make this look good... im sorry)
|
||||
splitRect.x += elementSpacing;
|
||||
splitRect.width = innerRectHalfWidth;
|
||||
dropDownEntry.isAutogenCollapsed = !EditorGUI.Foldout(splitRect,
|
||||
!dropDownEntry.isAutogenCollapsed, $"Element Name ({innerIndex})", true/*, s_BoldFoldoutStyle*/);
|
||||
|
||||
// Name Field
|
||||
splitRect.x += innerRectHalfWidth;
|
||||
splitRect.width -= elementSpacing; // left/right padding
|
||||
SerializedProperty nameProp = dropdownEntryProp.FindPropertyRelative(nameof(CVRAdvancedSettingsDropDownEntry.name));
|
||||
EditorGUI.PropertyField(splitRect, nameProp, GUIContent.none);
|
||||
|
||||
if (dropDownEntry.isAutogenCollapsed)
|
||||
return;
|
||||
|
||||
// autogen stuff
|
||||
innerRect.y += spacing;
|
||||
|
||||
#region Internal Drawing - Dropdown Inner-Autogen
|
||||
|
||||
SerializedProperty useAnimationProp = dropdownEntryProp.FindPropertyRelative(nameof(CVRAdvancedSettingsDropDownEntry.useAnimationClip));
|
||||
SerializedProperty animationClipProp = dropdownEntryProp.FindPropertyRelative(nameof(CVRAdvancedSettingsDropDownEntry.animationClip));
|
||||
|
||||
useAnimationProp.boolValue = EditorGUI.Toggle(innerRect, "Use Animation Clip", useAnimationProp.boolValue);
|
||||
innerRect.y += spacing;
|
||||
|
||||
// Animation Clip Field
|
||||
if (useAnimationProp.boolValue)
|
||||
{
|
||||
EditorGUI.PropertyField(new Rect(innerRect.x, innerRect.y, innerRect.width, EditorGUIUtility.singleLineHeight), animationClipProp);
|
||||
return;
|
||||
}
|
||||
|
||||
#region Internal Drawing - Dropdown Inner-Autogen-GameObjectTargets
|
||||
|
||||
SerializedProperty gameObjectTargetsProp = dropdownEntryProp.FindPropertyRelative(nameof(CVRAdvancedSettingsDropDownEntry.gameObjectTargets));
|
||||
|
||||
// GameObject Targets List
|
||||
if (dropDownEntry.reorderableList == null // recreate list if null or stale
|
||||
|| dropDownEntry.reorderableList.serializedProperty.serializedObject != serializedObject
|
||||
|| dropDownEntry.reorderableList.serializedProperty != gameObjectTargetsProp)
|
||||
{
|
||||
dropDownEntry.reorderableList = new ReorderableList(serializedObject, gameObjectTargetsProp,
|
||||
true, true, true, true)
|
||||
{
|
||||
list = dropDownEntry.gameObjectTargets,
|
||||
};
|
||||
|
||||
dropDownEntry.reorderableList.drawElementCallback = (innerInnerRect, innerInnerIndex, _, _) =>
|
||||
{
|
||||
innerInnerRect = new Rect(innerInnerRect.x, innerInnerRect.y + 2, innerInnerRect.width, EditorGUIUtility.singleLineHeight);
|
||||
SerializedProperty toggleEntryProp = dropDownEntry.reorderableList.serializedProperty.GetArrayElementAtIndex(innerInnerIndex);
|
||||
|
||||
const float innerElementSpacing = 5;
|
||||
const float infoIconWidth = 20;
|
||||
|
||||
// GameObject Field
|
||||
innerInnerRect.width = innerInnerRect.width * 0.5f;
|
||||
SerializedProperty gameObjectProp = toggleEntryProp.FindPropertyRelative(nameof(CVRAdvancedSettingsTargetEntryGameObject.gameObject));
|
||||
EditorGUI.PropertyField(innerInnerRect, gameObjectProp, GUIContent.none);
|
||||
|
||||
string tooltipPath = "No Target";
|
||||
if (gameObjectProp.objectReferenceValue != null)
|
||||
{
|
||||
Transform avatarTransform = _avatar.transform;
|
||||
Transform targetTransform = ((GameObject)gameObjectProp.objectReferenceValue).transform;
|
||||
if (!targetTransform.IsChildOf(avatarTransform))
|
||||
{
|
||||
gameObjectProp.objectReferenceValue = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
SerializedProperty gameObjectPathProp = toggleEntryProp.FindPropertyRelative(nameof(CVRAdvancedSettingsTargetEntryGameObject.treePath));
|
||||
gameObjectPathProp.stringValue = AnimationUtility.CalculateTransformPath(targetTransform, avatarTransform);
|
||||
tooltipPath = $"Path: {avatarTransform.name}/{gameObjectPathProp.stringValue}";
|
||||
}
|
||||
}
|
||||
|
||||
// OnState Field
|
||||
innerInnerRect.x += innerRectHalfWidth + innerElementSpacing;
|
||||
innerInnerRect.width -= 2*infoIconWidth - 2*innerElementSpacing; // left/right padding
|
||||
SerializedProperty onStateProp = toggleEntryProp.FindPropertyRelative(nameof(CVRAdvancedSettingsTargetEntryGameObject.onState));
|
||||
onStateProp.boolValue = EditorGUI.Popup(innerInnerRect, onStateProp.boolValue ? 1 : 0, BoolAsStringDisplayOptions) == 1;
|
||||
|
||||
// Path Info
|
||||
innerInnerRect.x += innerInnerRect.width + innerElementSpacing;
|
||||
innerInnerRect.width = infoIconWidth;
|
||||
EditorGUI.LabelField(innerInnerRect, new GUIContent(EditorGUIUtility.IconContent("d_animationanimated").image, tooltipPath));
|
||||
};
|
||||
|
||||
dropDownEntry.reorderableList.elementHeight = EditorGUIUtility.singleLineHeight + 4f;
|
||||
dropDownEntry.reorderableList.drawHeaderCallback = (headerRect) =>
|
||||
{
|
||||
Rect labelRect = new(headerRect.x, headerRect.y, headerRect.width - 35, EditorGUIUtility.singleLineHeight);
|
||||
EditorGUI.LabelField(labelRect, $"GameObject Targets ({gameObjectTargetsProp.arraySize})");
|
||||
labelRect.x += 35; // not sure why this is needed, nested lists are weird
|
||||
EditorGUIExtensions.UtilityMenu(labelRect, dropDownEntry.reorderableList, gameObjectTargetsProp);
|
||||
};
|
||||
}
|
||||
|
||||
dropDownEntry.reorderableList.DoList(innerRect);
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
};
|
||||
dropdownSetting.reorderableList.elementHeight = EditorGUIUtility.singleLineHeight + 4f;
|
||||
dropdownSetting.reorderableList.elementHeightCallback = (innerIndex) =>
|
||||
{
|
||||
float height = EditorGUIUtility.singleLineHeight * 1.25f;
|
||||
if (innerIndex < 0 || innerIndex >= dropdownSetting.options.Count)
|
||||
return height;
|
||||
|
||||
CVRAdvancedSettingsDropDownEntry dropdownEntry = dropdownSetting.options[innerIndex];
|
||||
if (dropdownEntry.isAutogenCollapsed) return height;
|
||||
|
||||
float lines = 1f; // use animation toggle
|
||||
if (dropdownEntry.useAnimationClip)
|
||||
{
|
||||
lines += 2f; // both animation fields
|
||||
return lines * height;
|
||||
}
|
||||
|
||||
lines += 3.5f; // reorderable list header & footer + space after
|
||||
lines += dropdownEntry.gameObjectTargets.Count == 0 ? 1f : dropdownEntry.gameObjectTargets.Count * 1.09f;
|
||||
return lines * height;
|
||||
};
|
||||
dropdownSetting.reorderableList.drawHeaderCallback = (headerRect) =>
|
||||
{
|
||||
Rect labelRect = new(headerRect.x, headerRect.y, headerRect.width - 35, EditorGUIUtility.singleLineHeight);
|
||||
EditorGUI.LabelField(labelRect, $"Dropdown Options ({dropdownOptionsProp.arraySize})");
|
||||
labelRect.x += 35; // not sure why this is needed, nested lists are weird
|
||||
EditorGUIExtensions.UtilityMenu(labelRect, dropdownSetting.reorderableList, dropdownOptionsProp);
|
||||
};
|
||||
}
|
||||
|
||||
dropdownSetting.reorderableList.DoList(rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4e1f511a5d874fd9b5bc9c8a1728066e
|
||||
timeCreated: 1709424050
|
|
@ -0,0 +1,62 @@
|
|||
#if UNITY_EDITOR
|
||||
using ABI.CCK.Scripts;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ABI.CCK.Components
|
||||
{
|
||||
public partial class CCK_CVRAvatarEditor
|
||||
{
|
||||
void DrawInputSingleInternal(
|
||||
Rect rect,
|
||||
float spacing,
|
||||
CVRAdvancedSettingsEntry advSettingEntry,
|
||||
SerializedProperty advSettingProp)
|
||||
{
|
||||
SerializedProperty defaultValueProp = advSettingProp.FindPropertyRelative(nameof(CVRAdvancesAvatarSettingInputSingle.defaultValue));
|
||||
EditorGUI.PropertyField(rect, defaultValueProp, new GUIContent("Default Value"));
|
||||
rect.y += spacing;
|
||||
|
||||
rect.height *= 2.5f;
|
||||
EditorGUI.HelpBox(rect, "This Settings does not provide a Setup Utility. " +
|
||||
"But it will create the necessary Animator Layers, Parameters and Animations. " +
|
||||
"So you can edit them to your liking after the controller is created.",
|
||||
MessageType.Info);
|
||||
}
|
||||
|
||||
void DrawInputVector2Internal(
|
||||
Rect rect,
|
||||
float spacing,
|
||||
CVRAdvancedSettingsEntry advSettingEntry,
|
||||
SerializedProperty advSettingProp)
|
||||
{
|
||||
SerializedProperty defaultValueProp = advSettingProp.FindPropertyRelative(nameof(CVRAdvancesAvatarSettingInputVector2.defaultValue));
|
||||
EditorGUI.PropertyField(rect, defaultValueProp, new GUIContent("Default Value"));
|
||||
rect.y += spacing;
|
||||
|
||||
rect.height *= 2.5f;
|
||||
EditorGUI.HelpBox(rect, "This Settings does not provide a Setup Utility. " +
|
||||
"But it will create the necessary Animator Layers, Parameters and Animations. " +
|
||||
"So you can edit them to your liking after the controller is created.",
|
||||
MessageType.Info);
|
||||
}
|
||||
|
||||
void DrawInputVector3Internal(
|
||||
Rect rect,
|
||||
float spacing,
|
||||
CVRAdvancedSettingsEntry advSettingEntry,
|
||||
SerializedProperty advSettingProp)
|
||||
{
|
||||
SerializedProperty defaultValueProp = advSettingProp.FindPropertyRelative(nameof(CVRAdvancesAvatarSettingInputVector3.defaultValue));
|
||||
EditorGUI.PropertyField(rect, defaultValueProp, new GUIContent("Default Value"));
|
||||
rect.y += spacing;
|
||||
|
||||
rect.height *= 2.5f;
|
||||
EditorGUI.HelpBox(rect, "This Settings does not provide a Setup Utility. " +
|
||||
"But it will create the necessary Animator Layers, Parameters and Animations. " +
|
||||
"So you can edit them to your liking after the controller is created.",
|
||||
MessageType.Info);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: fd5428143ca147de8f8649f82edbce3b
|
||||
timeCreated: 1709424338
|
|
@ -0,0 +1,61 @@
|
|||
#if UNITY_EDITOR
|
||||
using ABI.CCK.Scripts;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ABI.CCK.Components
|
||||
{
|
||||
public partial class CCK_CVRAvatarEditor
|
||||
{
|
||||
private void DrawJoystick2DInternal(
|
||||
Rect rect,
|
||||
float spacing,
|
||||
CVRAdvancedSettingsEntry advSettingEntry,
|
||||
SerializedProperty advSettingProp)
|
||||
{
|
||||
SerializedProperty defaultValueProp = advSettingProp.FindPropertyRelative(nameof(CVRAdvancesAvatarSettingJoystick2D.defaultValue));
|
||||
EditorGUI.PropertyField(rect, defaultValueProp, new GUIContent("Default Value"));
|
||||
rect.y += spacing;
|
||||
|
||||
// min/max
|
||||
SerializedProperty minProp = advSettingProp.FindPropertyRelative(nameof(CVRAdvancesAvatarSettingJoystick2D.rangeMin));
|
||||
SerializedProperty maxProp = advSettingProp.FindPropertyRelative(nameof(CVRAdvancesAvatarSettingJoystick2D.rangeMax));
|
||||
EditorGUI.PropertyField(rect, minProp, new GUIContent("Min Value"));
|
||||
rect.y += spacing;
|
||||
EditorGUI.PropertyField(rect, maxProp, new GUIContent("Max Value"));
|
||||
rect.y += spacing;
|
||||
|
||||
rect.height *= 2.5f;
|
||||
EditorGUI.HelpBox(rect, "This Settings does not provide a Setup Utility. " +
|
||||
"But it will create the necessary Animator Layers, Parameters and Animations. " +
|
||||
"So you can edit them to your liking after the controller is created.",
|
||||
MessageType.Info);
|
||||
}
|
||||
|
||||
private void DrawJoystick3DInternal(
|
||||
Rect rect,
|
||||
float spacing,
|
||||
CVRAdvancedSettingsEntry advSettingEntry,
|
||||
SerializedProperty advSettingProp)
|
||||
{
|
||||
SerializedProperty defaultValueProp = advSettingProp.FindPropertyRelative(nameof(CVRAdvancesAvatarSettingJoystick3D.defaultValue));
|
||||
EditorGUI.PropertyField(rect, defaultValueProp, new GUIContent("Default Value"));
|
||||
rect.y += spacing;
|
||||
|
||||
// min/max
|
||||
SerializedProperty minProp = advSettingProp.FindPropertyRelative(nameof(CVRAdvancesAvatarSettingJoystick3D.rangeMin));
|
||||
SerializedProperty maxProp = advSettingProp.FindPropertyRelative(nameof(CVRAdvancesAvatarSettingJoystick3D.rangeMax));
|
||||
EditorGUI.PropertyField(rect, minProp, new GUIContent("Min Value"));
|
||||
rect.y += spacing;
|
||||
EditorGUI.PropertyField(rect, maxProp, new GUIContent("Max Value"));
|
||||
rect.y += spacing;
|
||||
|
||||
rect.height *= 2.5f;
|
||||
EditorGUI.HelpBox(rect, "This Settings does not provide a Setup Utility. " +
|
||||
"But it will create the necessary Animator Layers, Parameters and Animations. " +
|
||||
"So you can edit them to your liking after the controller is created.",
|
||||
MessageType.Info);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 877f1862df6d493fb7547361ef50ce65
|
||||
timeCreated: 1709424273
|
|
@ -0,0 +1,275 @@
|
|||
#if UNITY_EDITOR
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ABI.CCK.Scripts;
|
||||
using ABI.CCK.Scripts.Editor;
|
||||
using UnityEditor;
|
||||
using UnityEditorInternal;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using static ABI.CCK.Scripts.Editor.SharedComponentGUI;
|
||||
|
||||
namespace ABI.CCK.Components
|
||||
{
|
||||
public partial class CCK_CVRAvatarEditor
|
||||
{
|
||||
private void DrawSliderInternal(
|
||||
Rect rect,
|
||||
float spacing,
|
||||
CVRAdvancedSettingsEntry advSettingEntry,
|
||||
SerializedProperty advSettingProp)
|
||||
{
|
||||
CVRAdvancesAvatarSettingSlider sliderSetting = (CVRAdvancesAvatarSettingSlider)advSettingEntry.setting;
|
||||
|
||||
// min/max is not yet exposed, but is in the client
|
||||
SerializedProperty defaultValueProp = advSettingProp.FindPropertyRelative(nameof(CVRAdvancesAvatarSettingSlider.defaultValue));
|
||||
defaultValueProp.floatValue = EditorGUI.Slider(rect, "Default Value", defaultValueProp.floatValue, 0f, 1f);
|
||||
rect.y += spacing;
|
||||
|
||||
// foldout
|
||||
advSettingEntry.isAutogenCollapsed = !EditorGUI.Foldout(rect, !advSettingEntry.isAutogenCollapsed, "Autogeneration Options", true, s_BoldFoldoutStyle);
|
||||
if (advSettingEntry.isAutogenCollapsed)
|
||||
return;
|
||||
|
||||
// autogen stuff
|
||||
rect.y += spacing;
|
||||
|
||||
SerializedProperty useAnimationProp = advSettingProp.FindPropertyRelative(nameof(CVRAdvancesAvatarSettingSlider.useAnimationClip));
|
||||
SerializedProperty minAnimationClipProp = advSettingProp.FindPropertyRelative(nameof(CVRAdvancesAvatarSettingSlider.minAnimationClip));
|
||||
SerializedProperty maxAnimationClipProp = advSettingProp.FindPropertyRelative(nameof(CVRAdvancesAvatarSettingSlider.maxAnimationClip));
|
||||
|
||||
useAnimationProp.boolValue = EditorGUI.Toggle(rect, "Use Animation Clip", useAnimationProp.boolValue);
|
||||
rect.y += spacing;
|
||||
|
||||
// Animation Clip Fields
|
||||
if (useAnimationProp.boolValue)
|
||||
{
|
||||
EditorGUI.PropertyField(new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight), minAnimationClipProp);
|
||||
rect.y += spacing;
|
||||
EditorGUI.PropertyField(new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight), maxAnimationClipProp);
|
||||
return;
|
||||
}
|
||||
|
||||
SerializedProperty materialPropertyTargets = advSettingProp.FindPropertyRelative(nameof(CVRAdvancesAvatarSettingSlider.materialPropertyTargets));
|
||||
|
||||
// Slider Float/Range Targets List
|
||||
if (sliderSetting.reorderableList == null // recreate list if null or stale
|
||||
|| sliderSetting.reorderableList.serializedProperty.serializedObject != serializedObject)
|
||||
{
|
||||
sliderSetting.reorderableList = new ReorderableList(serializedObject, materialPropertyTargets,
|
||||
true, true, true, true)
|
||||
{
|
||||
list = sliderSetting.materialPropertyTargets,
|
||||
};
|
||||
|
||||
sliderSetting.reorderableList.drawElementCallback = (innerRect, innerIndex, _, _) =>
|
||||
{
|
||||
innerRect = new Rect(innerRect.x, innerRect.y + 2, innerRect.width, EditorGUIUtility.singleLineHeight);
|
||||
Rect innerRectButBelow = new Rect(innerRect.x, innerRect.y + 2, innerRect.width, EditorGUIUtility.singleLineHeight);
|
||||
|
||||
SerializedProperty colorEntry = sliderSetting.reorderableList.serializedProperty.GetArrayElementAtIndex(innerIndex);
|
||||
|
||||
const float elementSpacing = 5;
|
||||
const float infoIconWidth = 20;
|
||||
float innerRectHalfWidth = innerRect.width * 0.5f;
|
||||
|
||||
// GameObject Field
|
||||
innerRect.width = innerRectHalfWidth;
|
||||
SerializedProperty materialColorProp = colorEntry.FindPropertyRelative(nameof(CVRAdvancedSettingsTargetEntryMaterialProperty.gameObject));
|
||||
EditorGUI.PropertyField(innerRect, materialColorProp, GUIContent.none);
|
||||
|
||||
string tooltipPath = "No Target";
|
||||
var propertyList = new Dictionary<string, string>();
|
||||
if (materialColorProp.objectReferenceValue != null)
|
||||
{
|
||||
Transform avatarTransform = _avatar.transform;
|
||||
Transform targetTransform = ((GameObject)materialColorProp.objectReferenceValue).transform;
|
||||
if (!targetTransform.IsChildOf(avatarTransform))
|
||||
{
|
||||
materialColorProp.objectReferenceValue = null; // invalid
|
||||
}
|
||||
else
|
||||
{
|
||||
MeshRenderer meshRenderer = targetTransform.GetComponent<MeshRenderer>();
|
||||
SkinnedMeshRenderer skinnedMeshRenderer = targetTransform.GetComponent<SkinnedMeshRenderer>();
|
||||
ParticleSystemRenderer particleRenderer = targetTransform.GetComponent<ParticleSystemRenderer>();
|
||||
LineRenderer lineRenderer =targetTransform.GetComponent<LineRenderer>();
|
||||
TrailRenderer trailRenderer = targetTransform.GetComponent<TrailRenderer>();
|
||||
|
||||
bool rendererFound = meshRenderer || skinnedMeshRenderer || particleRenderer ||
|
||||
lineRenderer || trailRenderer;
|
||||
|
||||
if (rendererFound)
|
||||
{
|
||||
#region Populate Property List
|
||||
|
||||
if (meshRenderer != null)
|
||||
{
|
||||
foreach (Material material in meshRenderer.sharedMaterials)
|
||||
{
|
||||
if (material == null) continue;
|
||||
Shader shader = material.shader;
|
||||
for (var j = 0; j < shader.GetPropertyCount(); j++)
|
||||
{
|
||||
if (shader.GetPropertyType(j) != ShaderPropertyType.Float
|
||||
&& shader.GetPropertyType(j) != ShaderPropertyType.Range) continue;
|
||||
var propertyKey = "MeshRenderer: " + shader.GetPropertyDescription(j) +
|
||||
"(" + shader.GetPropertyName(j) + ")";
|
||||
if (!propertyList.ContainsKey(propertyKey))
|
||||
propertyList.Add(propertyKey, "MSR:" + shader.GetPropertyName(j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (skinnedMeshRenderer != null)
|
||||
{
|
||||
foreach (Material material in skinnedMeshRenderer.sharedMaterials)
|
||||
{
|
||||
if (material == null) continue;
|
||||
Shader shader = material.shader;
|
||||
for (var j = 0; j < shader.GetPropertyCount(); j++)
|
||||
{
|
||||
if (shader.GetPropertyType(j) != ShaderPropertyType.Float
|
||||
&& shader.GetPropertyType(j) != ShaderPropertyType.Range) continue;
|
||||
var propertyKey = "SkinnedMeshRenderer: " +
|
||||
shader.GetPropertyDescription(j) + "(" +
|
||||
shader.GetPropertyName(j) + ")";
|
||||
if (!propertyList.ContainsKey(propertyKey))
|
||||
propertyList.Add(propertyKey, "SMR:" + shader.GetPropertyName(j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (particleRenderer != null)
|
||||
{
|
||||
foreach (Material material in particleRenderer.sharedMaterials)
|
||||
{
|
||||
if (material == null) continue;
|
||||
Shader shader = material.shader;
|
||||
for (var j = 0; j < shader.GetPropertyCount(); j++)
|
||||
{
|
||||
if (shader.GetPropertyType(j) != ShaderPropertyType.Float
|
||||
&& shader.GetPropertyType(j) != ShaderPropertyType.Range) continue;
|
||||
var propertyKey = "ParticleRenderer: " + shader.GetPropertyDescription(j) +
|
||||
"(" + shader.GetPropertyName(j) + ")";
|
||||
if (!propertyList.ContainsKey(propertyKey))
|
||||
propertyList.Add(propertyKey, "PTR:" + shader.GetPropertyName(j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lineRenderer != null)
|
||||
{
|
||||
foreach (Material material in lineRenderer.sharedMaterials)
|
||||
{
|
||||
if (material == null) continue;
|
||||
Shader shader = material.shader;
|
||||
for (var j = 0; j < shader.GetPropertyCount(); j++)
|
||||
{
|
||||
if (shader.GetPropertyType(j) != ShaderPropertyType.Float
|
||||
&& shader.GetPropertyType(j) != ShaderPropertyType.Range) continue;
|
||||
var propertyKey = "LineRenderer: " + shader.GetPropertyDescription(j) +
|
||||
"(" + shader.GetPropertyName(j) + ")";
|
||||
if (!propertyList.ContainsKey(propertyKey))
|
||||
propertyList.Add(propertyKey, "LNR:" + shader.GetPropertyName(j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (trailRenderer != null)
|
||||
{
|
||||
foreach (Material material in trailRenderer.sharedMaterials)
|
||||
{
|
||||
if (material == null) continue;
|
||||
Shader shader = material.shader;
|
||||
for (var j = 0; j < shader.GetPropertyCount(); j++)
|
||||
{
|
||||
if (shader.GetPropertyType(j) != ShaderPropertyType.Float
|
||||
&& shader.GetPropertyType(j) != ShaderPropertyType.Range) continue;
|
||||
var propertyKey = "TrailRenderer: " + shader.GetPropertyDescription(j) +
|
||||
"(" + shader.GetPropertyName(j) + ")";
|
||||
if (!propertyList.ContainsKey(propertyKey))
|
||||
propertyList.Add(propertyKey, "TLR:" + shader.GetPropertyName(j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
else
|
||||
{
|
||||
materialColorProp.objectReferenceValue = null; // invalid
|
||||
}
|
||||
|
||||
SerializedProperty materialColorPathProp = colorEntry.FindPropertyRelative(nameof(CVRAdvancedSettingsTargetEntryMaterialProperty.treePath));
|
||||
materialColorPathProp.stringValue = AnimationUtility.CalculateTransformPath(targetTransform, avatarTransform);
|
||||
tooltipPath = $"Path: {avatarTransform.name}/{materialColorPathProp.stringValue}";
|
||||
}
|
||||
}
|
||||
|
||||
// Property Dropdown
|
||||
innerRect.x += innerRectHalfWidth + elementSpacing;
|
||||
innerRect.width -= 2*infoIconWidth - 2*elementSpacing; // left/right padding
|
||||
SerializedProperty propNameProp = colorEntry.FindPropertyRelative(nameof(CVRAdvancedSettingsTargetEntryMaterialProperty.propertyName));
|
||||
SerializedProperty propTypeIdProp = colorEntry.FindPropertyRelative(nameof(CVRAdvancedSettingsTargetEntryMaterialProperty.propertyTypeIdentifier));
|
||||
SerializedProperty minValueProp = colorEntry.FindPropertyRelative(nameof(CVRAdvancedSettingsTargetEntryMaterialProperty.minValue));
|
||||
SerializedProperty maxValueProp = colorEntry.FindPropertyRelative(nameof(CVRAdvancedSettingsTargetEntryMaterialProperty.maxValue));
|
||||
|
||||
// not valid as a serialized property?
|
||||
//SerializedProperty propTypeProp = toggleEntry.FindPropertyRelative(nameof(CVRAdvancedSettingsTargetEntryMaterialColor.propertyType));
|
||||
|
||||
//var propNames = propertyList.Keys.ToArray();
|
||||
var propDescs = propertyList.Values.ToArray();
|
||||
string currentItem = propNameProp.stringValue.Length > 0
|
||||
? propTypeIdProp.stringValue + ":" + propNameProp.stringValue
|
||||
: CVRCommon.NONE_OR_EMPTY;
|
||||
|
||||
// search dropdown for material properties
|
||||
int selected = EditorGUIExtensions.CustomPopup(
|
||||
innerRect,
|
||||
Array.IndexOf(propDescs, currentItem),
|
||||
propDescs,
|
||||
"No Properties"
|
||||
);
|
||||
|
||||
// name: _Color
|
||||
// typeid: SMR
|
||||
// type: SkinnedMeshRenderer
|
||||
|
||||
if (selected >= 0)
|
||||
{
|
||||
currentItem = propDescs[selected];
|
||||
propNameProp.stringValue = currentItem[4..];
|
||||
propTypeIdProp.stringValue = currentItem[..3];
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
// propType is not a valid serialized property
|
||||
// so autogen will need to handle this
|
||||
}
|
||||
|
||||
// Path Info
|
||||
innerRect.x += innerRect.width + elementSpacing;
|
||||
innerRect.width = infoIconWidth;
|
||||
EditorGUI.LabelField(innerRect, new GUIContent(EditorGUIUtility.IconContent("d_animationanimated").image, tooltipPath));
|
||||
innerRectButBelow.y += EditorGUIUtility.singleLineHeight + 4f;
|
||||
innerRectButBelow.width = innerRectHalfWidth;
|
||||
minValueProp.floatValue = EditorGUI.FloatField(innerRectButBelow, "Min Value", minValueProp.floatValue);
|
||||
innerRectButBelow.x += innerRectHalfWidth + elementSpacing;
|
||||
maxValueProp.floatValue = EditorGUI.FloatField(innerRectButBelow, "Max Value", maxValueProp.floatValue);
|
||||
};
|
||||
|
||||
sliderSetting.reorderableList.elementHeight = (EditorGUIUtility.singleLineHeight + 4f) * 2f;
|
||||
sliderSetting.reorderableList.drawHeaderCallback = (headerRect) =>
|
||||
{
|
||||
Rect labelRect = new(headerRect.x, headerRect.y, headerRect.width - 35, EditorGUIUtility.singleLineHeight);
|
||||
EditorGUI.LabelField(labelRect, $"Slider Targets ({materialPropertyTargets.arraySize})");
|
||||
labelRect.x += 35; // not sure why this is needed, nested lists are weird
|
||||
EditorGUIExtensions.UtilityMenu(labelRect, sliderSetting.reorderableList, materialPropertyTargets);
|
||||
};
|
||||
}
|
||||
|
||||
sliderSetting.reorderableList.DoList(rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d42afa81de944f8b96dd614d64b9e485
|
||||
timeCreated: 1709424234
|
|
@ -0,0 +1,118 @@
|
|||
#if UNITY_EDITOR
|
||||
using ABI.CCK.Scripts;
|
||||
using ABI.CCK.Scripts.Editor;
|
||||
using UnityEditor;
|
||||
using UnityEditorInternal;
|
||||
using UnityEngine;
|
||||
using static ABI.CCK.Scripts.Editor.SharedComponentGUI;
|
||||
|
||||
namespace ABI.CCK.Components
|
||||
{
|
||||
public partial class CCK_CVRAvatarEditor
|
||||
{
|
||||
private void DrawToggleInternal(
|
||||
Rect rect,
|
||||
float spacing,
|
||||
CVRAdvancedSettingsEntry advSettingEntry,
|
||||
SerializedProperty advSettingProp)
|
||||
{
|
||||
CVRAdvancesAvatarSettingGameObjectToggle toggleSetting = advSettingEntry.setting as CVRAdvancesAvatarSettingGameObjectToggle;
|
||||
|
||||
SerializedProperty defaultValueProp = advSettingProp.FindPropertyRelative(nameof(CVRAdvancesAvatarSettingGameObjectToggle.defaultValue));
|
||||
defaultValueProp.boolValue = EditorGUI.Popup(rect, "Default Value", defaultValueProp.boolValue ? 1 : 0, BoolAsStringDisplayOptions) == 1;
|
||||
rect.y += spacing;
|
||||
|
||||
// foldout
|
||||
advSettingEntry.isAutogenCollapsed = !EditorGUI.Foldout(rect, !advSettingEntry.isAutogenCollapsed, "Autogeneration Options", true, s_BoldFoldoutStyle);
|
||||
if (advSettingEntry.isAutogenCollapsed)
|
||||
return;
|
||||
|
||||
// autogen stuff
|
||||
rect.y += spacing;
|
||||
|
||||
SerializedProperty useAnimationProp = advSettingProp.FindPropertyRelative(nameof(CVRAdvancesAvatarSettingGameObjectToggle.useAnimationClip));
|
||||
SerializedProperty animationClipProp = advSettingProp.FindPropertyRelative(nameof(CVRAdvancesAvatarSettingGameObjectToggle.animationClip));
|
||||
SerializedProperty animationNameProp = advSettingProp.FindPropertyRelative(nameof(CVRAdvancesAvatarSettingGameObjectToggle.offAnimationClip));
|
||||
|
||||
useAnimationProp.boolValue = EditorGUI.Toggle(rect, "Use Animation Clip", useAnimationProp.boolValue);
|
||||
rect.y += spacing;
|
||||
|
||||
// Animation Clip Fields
|
||||
if (useAnimationProp.boolValue)
|
||||
{
|
||||
EditorGUI.PropertyField(new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight), animationClipProp);
|
||||
rect.y += spacing;
|
||||
EditorGUI.PropertyField(new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight), animationNameProp);
|
||||
return;
|
||||
}
|
||||
|
||||
SerializedProperty gameObjectTargetsProp = advSettingProp.FindPropertyRelative(nameof(CVRAdvancesAvatarSettingGameObjectToggle.gameObjectTargets));
|
||||
|
||||
// GameObject Targets List
|
||||
if (toggleSetting!.reorderableList == null // recreate list if null
|
||||
|| toggleSetting.reorderableList.serializedProperty.serializedObject != serializedObject) // stale
|
||||
{
|
||||
toggleSetting.reorderableList = new ReorderableList(serializedObject, gameObjectTargetsProp,
|
||||
true, true, true, true)
|
||||
{
|
||||
list = toggleSetting.gameObjectTargets,
|
||||
};
|
||||
|
||||
toggleSetting.reorderableList.drawElementCallback = (innerRect, innerIndex, _, _) =>
|
||||
{
|
||||
innerRect = new Rect(innerRect.x, innerRect.y + 2, innerRect.width, EditorGUIUtility.singleLineHeight);
|
||||
SerializedProperty toggleEntryProp = toggleSetting.reorderableList.serializedProperty.GetArrayElementAtIndex(innerIndex);
|
||||
|
||||
const float elementSpacing = 5;
|
||||
const float infoIconWidth = 20;
|
||||
float innerRectHalfWidth = innerRect.width * 0.5f;
|
||||
|
||||
// GameObject Field
|
||||
innerRect.width = innerRectHalfWidth;
|
||||
SerializedProperty gameObjectProp = toggleEntryProp.FindPropertyRelative(nameof(CVRAdvancedSettingsTargetEntryGameObject.gameObject));
|
||||
EditorGUI.PropertyField(innerRect, gameObjectProp, GUIContent.none);
|
||||
|
||||
string tooltipPath = "No Target";
|
||||
if (gameObjectProp.objectReferenceValue != null)
|
||||
{
|
||||
Transform avatarTransform = _avatar.transform;
|
||||
Transform targetTransform = ((GameObject)gameObjectProp.objectReferenceValue).transform;
|
||||
if (!targetTransform.IsChildOf(avatarTransform))
|
||||
{
|
||||
gameObjectProp.objectReferenceValue = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
SerializedProperty gameObjectPathProp = toggleEntryProp.FindPropertyRelative(nameof(CVRAdvancedSettingsTargetEntryGameObject.treePath));
|
||||
gameObjectPathProp.stringValue = AnimationUtility.CalculateTransformPath(targetTransform, avatarTransform);
|
||||
tooltipPath = $"Path: {avatarTransform.name}/{gameObjectPathProp.stringValue}";
|
||||
}
|
||||
}
|
||||
|
||||
// OnState Field
|
||||
innerRect.x += innerRectHalfWidth + elementSpacing;
|
||||
innerRect.width -= 2*infoIconWidth - 2*elementSpacing; // left/right padding
|
||||
SerializedProperty onStateProp = toggleEntryProp.FindPropertyRelative(nameof(CVRAdvancedSettingsTargetEntryGameObject.onState));
|
||||
onStateProp.boolValue = EditorGUI.Popup(innerRect, onStateProp.boolValue ? 1 : 0, BoolAsStringDisplayOptions) == 1;
|
||||
|
||||
// Path Info
|
||||
innerRect.x += innerRect.width + elementSpacing;
|
||||
innerRect.width = infoIconWidth;
|
||||
EditorGUI.LabelField(innerRect, new GUIContent(EditorGUIUtility.IconContent("d_animationanimated").image, tooltipPath));
|
||||
};
|
||||
|
||||
toggleSetting.reorderableList.elementHeight = EditorGUIUtility.singleLineHeight + 4f;
|
||||
toggleSetting.reorderableList.drawHeaderCallback = (headerRect) =>
|
||||
{
|
||||
Rect labelRect = new(headerRect.x, headerRect.y, headerRect.width - 35, EditorGUIUtility.singleLineHeight);
|
||||
EditorGUI.LabelField(labelRect, $"GameObject Targets ({gameObjectTargetsProp.arraySize})");
|
||||
labelRect.x += 35; // not sure why this is needed, nested lists are weird
|
||||
EditorGUIExtensions.UtilityMenu(labelRect, toggleSetting.reorderableList, gameObjectTargetsProp);
|
||||
};
|
||||
}
|
||||
|
||||
toggleSetting.reorderableList.DoList(rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: df6671fcd41141df877371502a7eb664
|
||||
timeCreated: 1709423125
|
490
Assets/ABI.CCK/Scripts/Editor/CCK_CVRAvatarEditor/CCK_CVRAvatarEditor.cs
Executable file
490
Assets/ABI.CCK/Scripts/Editor/CCK_CVRAvatarEditor/CCK_CVRAvatarEditor.cs
Executable file
|
@ -0,0 +1,490 @@
|
|||
#if UNITY_EDITOR
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ABI.CCK.Scripts;
|
||||
using ABI.CCK.Scripts.Editor;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ABI.CCK.Components
|
||||
{
|
||||
[CanEditMultipleObjects]
|
||||
[CustomEditor(typeof(CVRAvatar))]
|
||||
public partial class CCK_CVRAvatarEditor : Editor
|
||||
{
|
||||
#region Editor GUI Foldouts
|
||||
|
||||
private static bool _guiAvatarSettingsFoldout = true;
|
||||
private static bool _guiAvatarCustomizationFoldout = true;
|
||||
private static bool _guiEyeLookSettingsFoldout;
|
||||
private static bool _guiEyeLookMuscleInfoFoldout;
|
||||
private static bool _guiEyeBlinkSettingsFoldout;
|
||||
private static bool _guiLipSyncSettingsFoldout;
|
||||
private static bool _guiFirstPersonRenderSettingsFoldout;
|
||||
private static bool _guiAdvancedTaggingFoldout;
|
||||
private static bool _guiAdvancedSettingsFoldout;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Variables
|
||||
|
||||
// Common
|
||||
private static readonly string[] _visemeNames =
|
||||
{ "sil", "PP", "FF", "TH", "DD", "kk", "CH", "SS", "nn", "RR", "aa", "E", "ih", "oh", "ou" };
|
||||
|
||||
// Avatar
|
||||
private CVRAvatar _avatar;
|
||||
private Animator _animator;
|
||||
|
||||
// Avatar Info
|
||||
private bool _isHumanoid;
|
||||
private List<string> _blendShapeNames;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Serialized Properties
|
||||
|
||||
private SerializedProperty m_ViewPositionProp;
|
||||
private SerializedProperty m_VoiceParentProp;
|
||||
private SerializedProperty m_VoicePositionProp;
|
||||
private SerializedProperty m_OverridesProp;
|
||||
private SerializedProperty m_BodyMeshProp;
|
||||
|
||||
// Eye Movement Settings
|
||||
private SerializedProperty m_UseEyeMovementProp;
|
||||
private SerializedProperty m_EyeMovementIntervalProp;
|
||||
private SerializedProperty m_EyeMovementInfoProp;
|
||||
private UnityEditorInternal.ReorderableList m_EyeMovementInfoEyesList;
|
||||
|
||||
// Eye Blinking Settings
|
||||
private SerializedProperty m_UseBlinkBlendshapesProp;
|
||||
private SerializedProperty m_BlinkGapProp;
|
||||
private SerializedProperty m_BlinkDurationProp;
|
||||
private SerializedProperty m_BlinkBlendshapeProp;
|
||||
|
||||
// Lipsync Settings
|
||||
private SerializedProperty m_UseVisemeLipsyncProp;
|
||||
private SerializedProperty m_VisemeModeProp;
|
||||
private SerializedProperty m_VisemeSmoothingProp;
|
||||
private SerializedProperty m_VisemeBlendshapesProp;
|
||||
|
||||
// First Person Render Settings
|
||||
private SerializedProperty m_fprSettingsListProp;
|
||||
|
||||
// Avatar Advanced Tagging
|
||||
private SerializedProperty m_advTaggingListProp;
|
||||
|
||||
// Avatar Advanced Settings
|
||||
private SerializedProperty m_avatarSettingsProp;
|
||||
private SerializedProperty m_settingEntriesProp;
|
||||
private SerializedProperty m_baseControllerProp;
|
||||
private SerializedProperty m_baseOverrideControllerProp;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity Events
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (target == null) return;
|
||||
_avatar = (CVRAvatar)target;
|
||||
_avatar.Reset();
|
||||
|
||||
m_ViewPositionProp = serializedObject.FindProperty(nameof(CVRAvatar.viewPosition));
|
||||
m_VoiceParentProp = serializedObject.FindProperty(nameof(CVRAvatar.voiceParent));
|
||||
m_VoicePositionProp = serializedObject.FindProperty(nameof(CVRAvatar.voicePosition));
|
||||
m_OverridesProp = serializedObject.FindProperty(nameof(CVRAvatar.overrides));
|
||||
m_BodyMeshProp = serializedObject.FindProperty(nameof(CVRAvatar.bodyMesh));
|
||||
|
||||
// Eye Movement Settings
|
||||
m_UseEyeMovementProp = serializedObject.FindProperty(nameof(CVRAvatar.useEyeMovement));
|
||||
m_EyeMovementIntervalProp = serializedObject.FindProperty(nameof(CVRAvatar.eyeMovementInterval));
|
||||
m_EyeMovementInfoProp = serializedObject.FindProperty(nameof(CVRAvatar.eyeMovementInfo));
|
||||
m_EyeMovementInfoEyesList = InitializeEyesReorderableList(m_EyeMovementInfoProp);
|
||||
|
||||
// Eye Blinking Settings
|
||||
m_UseBlinkBlendshapesProp = serializedObject.FindProperty(nameof(CVRAvatar.useBlinkBlendshapes));
|
||||
m_BlinkGapProp = serializedObject.FindProperty(nameof(CVRAvatar.blinkGap));
|
||||
m_BlinkDurationProp = serializedObject.FindProperty(nameof(CVRAvatar.blinkDuration));
|
||||
m_BlinkBlendshapeProp = serializedObject.FindProperty(nameof(CVRAvatar.blinkBlendshape));
|
||||
|
||||
// Viseme Settings
|
||||
m_UseVisemeLipsyncProp = serializedObject.FindProperty(nameof(CVRAvatar.useVisemeLipsync));
|
||||
m_VisemeModeProp = serializedObject.FindProperty(nameof(CVRAvatar.visemeMode));
|
||||
m_VisemeSmoothingProp = serializedObject.FindProperty(nameof(CVRAvatar.visemeSmoothing));
|
||||
m_VisemeBlendshapesProp = serializedObject.FindProperty(nameof(CVRAvatar.visemeBlendshapes));
|
||||
|
||||
// First Person Render Settings
|
||||
m_fprSettingsListProp = serializedObject.FindProperty(nameof(CVRAvatar.fprSettingsList));
|
||||
|
||||
// Avatar Advanced Tagging
|
||||
m_advTaggingListProp = serializedObject.FindProperty(nameof(CVRAvatar.advancedTaggingList));
|
||||
|
||||
// Avatar Advanced Settings
|
||||
m_avatarSettingsProp = serializedObject.FindProperty(nameof(CVRAvatar.avatarSettings));
|
||||
|
||||
m_baseControllerProp = m_avatarSettingsProp.FindPropertyRelative(nameof(CVRAdvancedAvatarSettings.baseController));
|
||||
m_baseOverrideControllerProp = m_avatarSettingsProp.FindPropertyRelative(nameof(CVRAdvancedAvatarSettings.baseOverrideController));
|
||||
m_settingEntriesProp = m_avatarSettingsProp.FindPropertyRelative(nameof(CVRAdvancedAvatarSettings.settings));
|
||||
|
||||
GetBlendshapeNames();
|
||||
|
||||
_animator = _avatar.GetComponent<Animator>();
|
||||
if (_animator != null && _animator.isHuman)
|
||||
{
|
||||
_isHumanoid = true;
|
||||
GetHumanoidEyeMuscleLimits();
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
if (_avatar == null)
|
||||
return;
|
||||
|
||||
serializedObject.Update();
|
||||
|
||||
Draw_GeneralAvatarSettings();
|
||||
Draw_AvatarCustomization();
|
||||
Draw_EyeLookSettings();
|
||||
Draw_EyeBlinkSettings();
|
||||
Draw_LipSyncSettings();
|
||||
|
||||
Draw_FPRSettings();
|
||||
Draw_AdvancedTagging();
|
||||
Draw_AdvancedSettings();
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
public void OnSceneGUI()
|
||||
{
|
||||
if (_avatar == null)
|
||||
return;
|
||||
|
||||
Transform avatarTransform = _avatar.transform;
|
||||
Vector3 scale = avatarTransform.localScale;
|
||||
Vector3 inverseScale = new Vector3(1 / scale.x, 1 / scale.y, 1 / scale.z);
|
||||
|
||||
//View Position
|
||||
GUIStyle style = new GUIStyle();
|
||||
style.normal.textColor = Color.green;
|
||||
style.fontSize = 20;
|
||||
Handles.BeginGUI();
|
||||
Vector3 pos = avatarTransform.TransformPoint(Vector3.Scale(_avatar.viewPosition, inverseScale));
|
||||
Vector2 pos2D = HandleUtility.WorldToGUIPoint(pos);
|
||||
GUI.Label(new Rect(pos2D.x + 20, pos2D.y - 10, 100, 20), "View Position", style);
|
||||
Handles.EndGUI();
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
Vector3 viewPos = Handles.PositionHandle(pos, avatarTransform.rotation);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
Undo.RecordObject(_avatar, "CVR View Position Change");
|
||||
_avatar.viewPosition = Vector3.Scale(avatarTransform.InverseTransformPoint(viewPos), scale);
|
||||
}
|
||||
|
||||
//Voice Position
|
||||
style.normal.textColor = Color.red;
|
||||
Handles.BeginGUI();
|
||||
pos = avatarTransform.TransformPoint(Vector3.Scale(_avatar.voicePosition, inverseScale));
|
||||
pos2D = HandleUtility.WorldToGUIPoint(pos);
|
||||
GUI.Label(new Rect(pos2D.x + 20, pos2D.y - 10, 100, 20), "Voice Position", style);
|
||||
Handles.EndGUI();
|
||||
|
||||
EditorGUI.BeginChangeCheck();
|
||||
Vector3 voicePos = Handles.PositionHandle(pos, avatarTransform.rotation);
|
||||
if (EditorGUI.EndChangeCheck())
|
||||
{
|
||||
Undo.RecordObject(_avatar, "CVR Voice Position Change");
|
||||
_avatar.voicePosition = Vector3.Scale(avatarTransform.InverseTransformPoint(voicePos), scale);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region BlendshapeDropdowns
|
||||
|
||||
private void DrawBlendshape(string label, ref string blendshape)
|
||||
{
|
||||
if (_avatar.bodyMesh != null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(blendshape))
|
||||
blendshape = "-none-";
|
||||
|
||||
blendshape = EditorGUIExtensions.CustomPopup(
|
||||
GUILayoutUtility.GetRect(new GUIContent(label), EditorStyles.popup),
|
||||
label,
|
||||
blendshape,
|
||||
_blendShapeNames.ToArray(),
|
||||
blendshape
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.HelpBox("Avatar does not have a Face Mesh specified.", MessageType.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawBlendshape(string label, SerializedProperty blendshapesProp)
|
||||
{
|
||||
if (_avatar.bodyMesh != null)
|
||||
{
|
||||
if (!blendshapesProp.isArray)
|
||||
return;
|
||||
|
||||
if (blendshapesProp.arraySize != 1)
|
||||
blendshapesProp.arraySize = 1;
|
||||
|
||||
SerializedProperty blendshapeElementProp = blendshapesProp.GetArrayElementAtIndex(0);
|
||||
|
||||
string currentValue = blendshapeElementProp.stringValue;
|
||||
if (string.IsNullOrEmpty(currentValue))
|
||||
currentValue = "-none-";
|
||||
|
||||
string newValue = EditorGUIExtensions.CustomPopup(
|
||||
GUILayoutUtility.GetRect(new GUIContent(label), EditorStyles.popup),
|
||||
label,
|
||||
currentValue,
|
||||
_blendShapeNames.ToArray(),
|
||||
currentValue
|
||||
);
|
||||
|
||||
if (newValue != currentValue)
|
||||
blendshapeElementProp.stringValue = newValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.HelpBox("Avatar does not have a Face Mesh specified.",
|
||||
MessageType.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawBlendshapes(string label, ref string[] blendshapes, IReadOnlyList<string> names = null)
|
||||
{
|
||||
if (_avatar.bodyMesh != null)
|
||||
{
|
||||
for (var i = 0; i < blendshapes.Length; i++)
|
||||
{
|
||||
var currentLabel = names == null ? label + " " + (i + 1) : label + names[i];
|
||||
if (string.IsNullOrEmpty(blendshapes[i]))
|
||||
blendshapes[i] = "-none-";
|
||||
|
||||
blendshapes[i] = EditorGUIExtensions.CustomPopup(
|
||||
GUILayoutUtility.GetRect(new GUIContent(currentLabel), EditorStyles.popup),
|
||||
currentLabel,
|
||||
blendshapes[i],
|
||||
_blendShapeNames.ToArray(),
|
||||
blendshapes[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.HelpBox("Avatar does not have a Face Mesh specified.",
|
||||
MessageType.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawBlendshapes(string label, SerializedProperty blendshapesProp, IReadOnlyList<string> names = null)
|
||||
{
|
||||
if (_avatar.bodyMesh != null)
|
||||
{
|
||||
if (!blendshapesProp.isArray)
|
||||
return;
|
||||
|
||||
if (names != null && blendshapesProp.arraySize != names.Count)
|
||||
blendshapesProp.arraySize = names.Count;
|
||||
|
||||
for (int i = 0; i < blendshapesProp.arraySize; i++)
|
||||
{
|
||||
SerializedProperty blendshapeElementProp = blendshapesProp.GetArrayElementAtIndex(i);
|
||||
var currentLabel = (names == null) ? label + " " + (i + 1) : label + names[i];
|
||||
|
||||
string currentValue = blendshapeElementProp.stringValue;
|
||||
if (string.IsNullOrEmpty(currentValue))
|
||||
{
|
||||
currentValue = "-none-";
|
||||
}
|
||||
|
||||
string newValue = EditorGUIExtensions.CustomPopup(
|
||||
GUILayoutUtility.GetRect(new GUIContent(currentLabel), EditorStyles.popup),
|
||||
currentLabel,
|
||||
currentValue,
|
||||
_blendShapeNames.ToArray(),
|
||||
currentValue);
|
||||
|
||||
if (newValue != currentValue)
|
||||
blendshapeElementProp.stringValue = newValue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.HelpBox("Avatar does not have a Face Mesh specified.",
|
||||
MessageType.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
// this is probably overkill
|
||||
private void AutoSetViewPosition()
|
||||
{
|
||||
if (_animator == null || !_isHumanoid)
|
||||
return;
|
||||
|
||||
Transform leftEye = _animator.GetBoneTransform(HumanBodyBones.LeftEye);
|
||||
Transform rightEye = _animator.GetBoneTransform(HumanBodyBones.RightEye);
|
||||
Transform head = _animator.GetBoneTransform(HumanBodyBones.Head);
|
||||
|
||||
if (leftEye && rightEye)
|
||||
{
|
||||
Undo.RecordObject(_avatar, "CVR View Position Change");
|
||||
_avatar.viewPosition = RoundNearZero((leftEye.position + rightEye.position) / 2);
|
||||
return;
|
||||
}
|
||||
|
||||
if (leftEye || rightEye)
|
||||
{
|
||||
Undo.RecordObject(_avatar, "CVR View Position Change");
|
||||
_avatar.viewPosition = ProjectSingleEyePosition(leftEye ? leftEye : rightEye);
|
||||
return;
|
||||
}
|
||||
|
||||
if (head)
|
||||
{
|
||||
string[] leftEyeNames = { "LeftEye", "Left_Eye", "EyeLeft", "Eye_Left" };
|
||||
string[] rightEyeNames = { "RightEye", "Right_Eye", "EyeRight", "Eye_Right" };
|
||||
leftEye = FindChildByNameVariants(head, leftEyeNames);
|
||||
rightEye = FindChildByNameVariants(head, rightEyeNames);
|
||||
|
||||
if (leftEye && rightEye)
|
||||
{
|
||||
Undo.RecordObject(_avatar, "CVR View Position Change");
|
||||
_avatar.viewPosition = RoundNearZero((leftEye.position + rightEye.position) / 2);
|
||||
return;
|
||||
}
|
||||
|
||||
if (leftEye || rightEye)
|
||||
{
|
||||
Undo.RecordObject(_avatar, "CVR View Position Change");
|
||||
_avatar.viewPosition = ProjectSingleEyePosition(leftEye ? leftEye : rightEye);
|
||||
return;
|
||||
}
|
||||
|
||||
Transform root = _animator.GetBoneTransform(HumanBodyBones.Hips);
|
||||
float headBoneHeight = Vector3.Distance(root.position, head.position);
|
||||
Vector3 localOffset = new Vector3(0f, -0.1f * headBoneHeight, 0.1f * headBoneHeight);
|
||||
Undo.RecordObject(_avatar, "CVR View Position Change");
|
||||
_avatar.viewPosition = RoundNearZero(head.TransformPoint(localOffset));
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.LogWarning("Could not find suitable bone for view position. We really tried...");
|
||||
}
|
||||
|
||||
private Vector3 ProjectSingleEyePosition(Transform singleEye)
|
||||
{
|
||||
Vector3 eyePosition = singleEye.position;
|
||||
Transform avatarRoot = _animator.transform;
|
||||
Vector3 toEyeDirection = (eyePosition - avatarRoot.position).normalized;
|
||||
|
||||
float dotForward = Vector3.Dot(toEyeDirection, avatarRoot.forward);
|
||||
float dotUp = Vector3.Dot(toEyeDirection, avatarRoot.up);
|
||||
float dotRight = Vector3.Dot(toEyeDirection, avatarRoot.right);
|
||||
|
||||
if (Mathf.Abs(dotForward) > Mathf.Abs(dotUp) && Mathf.Abs(dotForward) > Mathf.Abs(dotRight))
|
||||
return RoundNearZero(eyePosition - Vector3.Project(eyePosition - _animator.transform.position, avatarRoot.forward));
|
||||
|
||||
return Mathf.Abs(dotUp) > Mathf.Abs(dotRight) ? RoundNearZero(singleEye.position - Vector3.Project(eyePosition - avatarRoot.position, avatarRoot.up)) : RoundNearZero(singleEye.position - Vector3.Project(eyePosition - avatarRoot.position, avatarRoot.right));
|
||||
}
|
||||
|
||||
private void AutoSetVoicePosition()
|
||||
{
|
||||
Transform jaw = _animator.GetBoneTransform(HumanBodyBones.Jaw);
|
||||
Transform head = _animator.GetBoneTransform(HumanBodyBones.Head);
|
||||
|
||||
if (jaw)
|
||||
{
|
||||
Undo.RecordObject(_avatar, "CVR Voice Position Change");
|
||||
_avatar.voicePosition = jaw.position;
|
||||
}
|
||||
else if (head)
|
||||
{
|
||||
Vector3 localOffset = new Vector3(0f, 0.005f, 0.06f);
|
||||
Undo.RecordObject(_avatar, "CVR Voice Position Change");
|
||||
_avatar.voicePosition = head.TransformPoint(localOffset);
|
||||
}
|
||||
}
|
||||
|
||||
private static Transform FindChildByNameVariants(Transform parent, string[] nameVariants)
|
||||
{
|
||||
foreach (string potentialName in nameVariants)
|
||||
{
|
||||
Transform child = parent.Find(potentialName);
|
||||
if (child) return child;
|
||||
|
||||
child = parent.Cast<Transform>().FirstOrDefault(t => string.Equals(t.name, potentialName, StringComparison.OrdinalIgnoreCase));
|
||||
if (child) return child;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Vector3 RoundNearZero(Vector3 position)
|
||||
{
|
||||
const float tolerance = 0.01f;
|
||||
return new Vector3(
|
||||
Mathf.Abs(position.x) < tolerance ? 0 : position.x,
|
||||
Mathf.Abs(position.y) < tolerance ? 0 : position.y,
|
||||
Mathf.Abs(position.z) < tolerance ? 0 : position.z
|
||||
);
|
||||
}
|
||||
|
||||
private void GetBlendshapeNames()
|
||||
{
|
||||
_blendShapeNames = new List<string> { "-none-" };
|
||||
if (_avatar.bodyMesh == null) return;
|
||||
|
||||
for (var i = 0; i < _avatar.bodyMesh.sharedMesh.blendShapeCount; i++)
|
||||
_blendShapeNames.Add(_avatar.bodyMesh.sharedMesh.GetBlendShapeName(i));
|
||||
}
|
||||
|
||||
private void AutoSelectVisemeBlendshapes()
|
||||
{
|
||||
Undo.RecordObject(_avatar, "CVR Auto Select Visemes");
|
||||
for (var i = 0; i < _visemeNames.Length; i++)
|
||||
{
|
||||
var vPrefix = "v_" + _visemeNames[i];
|
||||
var visemePrefix = "viseme_" + _visemeNames[i];
|
||||
|
||||
foreach (var blendShapeName in _blendShapeNames)
|
||||
{
|
||||
if (blendShapeName.IndexOf(vPrefix, StringComparison.OrdinalIgnoreCase) < 0 &&
|
||||
blendShapeName.IndexOf(visemePrefix, StringComparison.OrdinalIgnoreCase) < 0)
|
||||
continue;
|
||||
_avatar.visemeBlendshapes[i] = blendShapeName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Failed... Reset is called on component
|
||||
// but editor doesnt exist yet...
|
||||
// private void AutoSetup(CVRAvatar avatar)
|
||||
// {
|
||||
// if (_avatar != avatar)
|
||||
// return;
|
||||
//
|
||||
// // find face mesh first
|
||||
// AutoSetViewPosition();
|
||||
// AutoSetVoicePosition();
|
||||
// AutoSelectVisemeBlendshapes();
|
||||
// }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f674ceaf9f32b8b49a43756c6b8c534f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,265 @@
|
|||
#if UNITY_EDITOR
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
using ABI.CCK.Scripts;
|
||||
using ABI.CCK.Scripts.Editor;
|
||||
using UnityEditor;
|
||||
using UnityEditorInternal;
|
||||
using UnityEngine;
|
||||
using AnimatorController = UnityEditor.Animations.AnimatorController;
|
||||
using AnimatorControllerParameter = UnityEngine.AnimatorControllerParameter;
|
||||
using AnimatorControllerParameterType = UnityEngine.AnimatorControllerParameterType;
|
||||
using static ABI.CCK.Scripts.Editor.SharedComponentGUI;
|
||||
|
||||
namespace ABI.CCK.Components
|
||||
{
|
||||
public partial class CCK_CVRAvatarEditor
|
||||
{
|
||||
private static readonly string[] BoolAsStringDisplayOptions = {"False", "True"};
|
||||
|
||||
private ReorderableList _advSettingsList;
|
||||
private int _selectedAdvSetting = -1;
|
||||
|
||||
private (int, int) _syncedBitsTuple; // (Current-Overrides, New-AutoGen)
|
||||
|
||||
private int _syncedValues;
|
||||
private int _syncedBooleans;
|
||||
private List<string> _baseControllerParams = new();
|
||||
|
||||
private void InitializeSettingsListIfNeeded()
|
||||
{
|
||||
if (_advSettingsList != null)
|
||||
return;
|
||||
|
||||
if (_avatar.avatarSettings is not { initialized: true })
|
||||
CreateAvatarSettings(_avatar);
|
||||
|
||||
if (_avatar.avatarSettings == null)
|
||||
return;
|
||||
|
||||
UpdateSyncUsageAndBaseParameters();
|
||||
|
||||
_advSettingsList = new ReorderableList(serializedObject, m_settingEntriesProp,
|
||||
true, true, true, true)
|
||||
{
|
||||
drawHeaderCallback = OnDrawHeaderAAS,
|
||||
onMouseUpCallback = OnMouseUpAAS,
|
||||
drawElementCallback = OnDrawElementAAS,
|
||||
elementHeightCallback = OnHeightElementAAS,
|
||||
onChangedCallback = OnChangedAAS,
|
||||
onReorderCallback = OnReorderedAAS,
|
||||
list = _avatar.avatarSettings.settings,
|
||||
index = -1
|
||||
};
|
||||
}
|
||||
|
||||
private void Draw_AdvancedSettings()
|
||||
{
|
||||
using (new ToggleFoldoutScope(ref _guiAdvancedSettingsFoldout, ref _avatar.avatarUsesAdvancedSettings, "Advanced Settings"))
|
||||
{
|
||||
if (!_guiAdvancedSettingsFoldout) return;
|
||||
|
||||
InitializeSettingsListIfNeeded();
|
||||
using (new EditorGUI.DisabledGroupScope(!_avatar.avatarUsesAdvancedSettings))
|
||||
DrawAvatarAdvancedSettings();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Cleanup all of this...
|
||||
// Decouple AutoGen from GUI
|
||||
// Add simplified menu entry mode (no AutoGen)
|
||||
|
||||
private void DrawAvatarAdvancedSettings()
|
||||
{
|
||||
if (_avatar.avatarSettings == null)
|
||||
return;
|
||||
|
||||
DrawAutogenControllerFields();
|
||||
|
||||
// display current usage compared to new usage prior to creating the controller
|
||||
EditorGUIExtensions.MultiProgressBar(
|
||||
EditorGUILayout.GetControlRect(false, EditorGUIUtility.singleLineHeight),
|
||||
_syncedBitsTuple.Item1 / CVRCommon.AVATAR_BIT_LIMIT,
|
||||
(_syncedBitsTuple.Item2) / CVRCommon.AVATAR_BIT_LIMIT,
|
||||
$"({_syncedBitsTuple.Item1}, {_syncedBitsTuple.Item2}) of 3200 Synced Bits used"
|
||||
);
|
||||
|
||||
_advSettingsList.DoLayoutList();
|
||||
|
||||
DrawCreateControllerButton();
|
||||
}
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private void UpdateSyncUsageAndBaseParameters()
|
||||
{
|
||||
// get the current usage of synced bits / preview usage of autogen
|
||||
_syncedBitsTuple = _avatar.GetParameterSyncUsage();
|
||||
|
||||
// populate the base controller parameters for existing-generation warning
|
||||
_baseControllerParams = CVRCommon.GetParametersFromControllerAsString(
|
||||
_avatar.avatarSettings.baseController, CVRCommon.NonCoreFilter, CVRCommon.NonNullFilter);
|
||||
}
|
||||
|
||||
private void CreateAASController()
|
||||
{
|
||||
// ensure everything is up to date
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
|
||||
if (_avatar.avatarSettings.baseController == null)
|
||||
{
|
||||
EditorUtility.DisplayDialog("Animator Error",
|
||||
"The Base Animator was not selected. No new Animator Controller was created.", "OK");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_avatar.avatarSettings.animator != null)
|
||||
{
|
||||
if (!EditorUtility.DisplayDialog("Animator already created",
|
||||
"There is Animator already created for this avatar.", "Override", "Cancel"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
string pathToCurrentFolder = "Assets/AdvancedSettings.Generated";
|
||||
if (!AssetDatabase.IsValidFolder(pathToCurrentFolder))
|
||||
AssetDatabase.CreateFolder("Assets", "AdvancedSettings.Generated");
|
||||
|
||||
string folderPath = pathToCurrentFolder + "/" + _avatar.name + "_AAS";
|
||||
if (!AssetDatabase.IsValidFolder(folderPath))
|
||||
AssetDatabase.CreateFolder(pathToCurrentFolder, _avatar.name + "_AAS");
|
||||
string animatorPath = pathToCurrentFolder + "/" + _avatar.name + "_AAS/" + _avatar.name + "_aas.controller";
|
||||
AssetDatabase.CopyAsset(AssetDatabase.GetAssetPath(_avatar.avatarSettings.baseController.GetInstanceID()),
|
||||
animatorPath);
|
||||
|
||||
_avatar.avatarSettings.animator = AssetDatabase.LoadAssetAtPath<AnimatorController>(animatorPath);
|
||||
|
||||
_baseControllerParams.Clear();
|
||||
|
||||
if (_avatar.avatarSettings.baseController != null)
|
||||
{
|
||||
AnimatorController animator = (AnimatorController)_avatar.avatarSettings.baseController;
|
||||
|
||||
foreach (AnimatorControllerParameter parameter in animator.parameters)
|
||||
{
|
||||
if (parameter.type == AnimatorControllerParameterType.Float && parameter.name.Length > 0 &&
|
||||
!CVRCommon.CoreParameters.Contains(parameter.name) && parameter.name.Substring(0, 1) != "#")
|
||||
{
|
||||
_baseControllerParams.Add(parameter.name);
|
||||
}
|
||||
|
||||
if (parameter.type == AnimatorControllerParameterType.Int && parameter.name.Length > 0 &&
|
||||
!CVRCommon.CoreParameters.Contains(parameter.name) && parameter.name.Substring(0, 1) != "#")
|
||||
{
|
||||
_baseControllerParams.Add(parameter.name);
|
||||
}
|
||||
|
||||
if (parameter.type == AnimatorControllerParameterType.Bool && parameter.name.Length > 0 &&
|
||||
!CVRCommon.CoreParameters.Contains(parameter.name) && parameter.name.Substring(0, 1) != "#")
|
||||
{
|
||||
_baseControllerParams.Add(parameter.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (CVRAdvancedSettingsEntry entry in _avatar.avatarSettings.settings)
|
||||
{
|
||||
switch (entry.type)
|
||||
{
|
||||
default:
|
||||
if (_baseControllerParams.Contains(entry.machineName)) continue;
|
||||
break;
|
||||
case CVRAdvancedSettingsEntry.SettingsType.Color:
|
||||
if (_baseControllerParams.Contains(entry.machineName + "-r") ||
|
||||
_baseControllerParams.Contains(entry.machineName + "-g") ||
|
||||
_baseControllerParams.Contains(entry.machineName + "-b")) continue;
|
||||
break;
|
||||
case CVRAdvancedSettingsEntry.SettingsType.Joystick2D:
|
||||
case CVRAdvancedSettingsEntry.SettingsType.InputVector2:
|
||||
if (_baseControllerParams.Contains(entry.machineName + "-x") ||
|
||||
_baseControllerParams.Contains(entry.machineName + "-y")) continue;
|
||||
break;
|
||||
case CVRAdvancedSettingsEntry.SettingsType.Joystick3D:
|
||||
case CVRAdvancedSettingsEntry.SettingsType.InputVector3:
|
||||
if (_baseControllerParams.Contains(entry.machineName + "-x") ||
|
||||
_baseControllerParams.Contains(entry.machineName + "-y") ||
|
||||
_baseControllerParams.Contains(entry.machineName + "-z")) continue;
|
||||
break;
|
||||
}
|
||||
|
||||
string fileName = Regex.Replace(entry.machineName, "[^a-zA-Z0-9_]+", "", RegexOptions.Compiled);
|
||||
entry.setting.SetupAnimator(ref _avatar.avatarSettings.animator, entry.machineName, folderPath, fileName);
|
||||
}
|
||||
|
||||
if (_avatar.avatarSettings.baseOverrideController != null)
|
||||
{
|
||||
string overridePath = pathToCurrentFolder + "/" + _avatar.name + "_AAS/" + _avatar.name +
|
||||
"_aas_overrides.overrideController";
|
||||
AssetDatabase.CopyAsset(
|
||||
AssetDatabase.GetAssetPath(_avatar.avatarSettings.baseOverrideController.GetInstanceID()),
|
||||
overridePath);
|
||||
_avatar.avatarSettings.overrides = AssetDatabase.LoadAssetAtPath<AnimatorOverrideController>(overridePath);
|
||||
_avatar.avatarSettings.overrides.runtimeAnimatorController = _avatar.avatarSettings.animator;
|
||||
}
|
||||
else
|
||||
{
|
||||
_avatar.avatarSettings.overrides = new AnimatorOverrideController(_avatar.avatarSettings.animator);
|
||||
AssetDatabase.CreateAsset(_avatar.avatarSettings.overrides,
|
||||
pathToCurrentFolder + "/" + _avatar.name + "_AAS/" + _avatar.name +
|
||||
"_aas_overrides.overrideController");
|
||||
}
|
||||
|
||||
AssetDatabase.SaveAssets();
|
||||
}
|
||||
|
||||
private static void CreateAvatarSettings(CVRAvatar avatar)
|
||||
{
|
||||
string[] guids = AssetDatabase.FindAssets("AvatarAnimator t:animatorController", null);
|
||||
|
||||
if (guids.Length < 1)
|
||||
{
|
||||
Debug.LogError(
|
||||
"No Animator controller with the name \"AvatarAnimator\" was found. Please make sure that you CCK is installed properly.");
|
||||
return;
|
||||
}
|
||||
|
||||
Type projectWindowUtilType = typeof(ProjectWindowUtil);
|
||||
MethodInfo getActiveFolderPath =
|
||||
projectWindowUtilType.GetMethod("GetActiveFolderPath", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
if (getActiveFolderPath != null) getActiveFolderPath.Invoke(null, Array.Empty<object>());
|
||||
|
||||
avatar.avatarSettings = new CVRAdvancedAvatarSettings
|
||||
{
|
||||
baseController = AssetDatabase.LoadAssetAtPath<AnimatorController>(AssetDatabase.GUIDToAssetPath(guids[0])),
|
||||
settings = new List<CVRAdvancedSettingsEntry>(),
|
||||
initialized = true
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utility
|
||||
|
||||
private void AppendUtilityMenu(GenericMenuBuilder genericMenuBuilder, ReorderableList list)
|
||||
{
|
||||
bool hasSettings = _avatar != null &&
|
||||
_avatar.avatarSettings is { settings: not null };
|
||||
|
||||
genericMenuBuilder.AddMenuItem("Collapse All", hasSettings,
|
||||
() => CollapseAllEntries(_avatar.avatarSettings.settings));
|
||||
}
|
||||
|
||||
private static void CollapseAllEntries(List<CVRAdvancedSettingsEntry> settingsEntries)
|
||||
{
|
||||
foreach (CVRAdvancedSettingsEntry advSettingEntry in settingsEntries)
|
||||
advSettingEntry.isCollapsed = advSettingEntry.isAutogenCollapsed = true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a1a0903b37ddaac4cadea63af36bb2f0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,103 @@
|
|||
#if UNITY_EDITOR
|
||||
using ABI.CCK.Scripts.Editor;
|
||||
using UnityEditor;
|
||||
using UnityEditorInternal;
|
||||
using UnityEngine;
|
||||
using static ABI.CCK.Scripts.Editor.SharedComponentGUI;
|
||||
|
||||
namespace ABI.CCK.Components
|
||||
{
|
||||
public partial class CCK_CVRAvatarEditor
|
||||
{
|
||||
private ReorderableList _advTaggingList;
|
||||
|
||||
private void Draw_AdvancedTagging()
|
||||
{
|
||||
using (new ToggleFoldoutScope(ref _guiAdvancedTaggingFoldout, ref _avatar.enableAdvancedTagging, "Advanced Tagging"))
|
||||
{
|
||||
if (!_guiAdvancedTaggingFoldout) return;
|
||||
|
||||
InitializeTaggingList();
|
||||
|
||||
EditorGUILayout.HelpBox("If you are using the Advanced Tagging System, you do not need to Tag your Avatar appropriately if you mark all affected GameObjects here.", MessageType.Info);
|
||||
using (new EditorGUI.DisabledGroupScope(!_avatar.enableAdvancedTagging))
|
||||
_advTaggingList.DoLayoutList();
|
||||
}
|
||||
}
|
||||
|
||||
#region AvatarAdvancedTagging ReorderableList
|
||||
|
||||
private void InitializeTaggingList()
|
||||
{
|
||||
_advTaggingList ??= new ReorderableList(serializedObject, m_advTaggingListProp, true, true, true, true)
|
||||
{
|
||||
drawHeaderCallback = OnDrawHeaderTagging,
|
||||
drawElementCallback = OnDrawElementTagging,
|
||||
elementHeightCallback = OnHeightElementTagging,
|
||||
onChangedCallback = OnChangedTagging,
|
||||
list = _avatar.advancedTaggingList // This is a hack to make the Utility Menu work
|
||||
};
|
||||
}
|
||||
|
||||
private float OnHeightElementTagging(int index)
|
||||
{
|
||||
if (index > _avatar.advancedTaggingList.Count) return EditorGUIUtility.singleLineHeight * 2.5f;
|
||||
|
||||
return EditorGUIUtility.singleLineHeight *
|
||||
((_avatar.advancedTaggingList[index].fallbackGameObject != null &&
|
||||
_avatar.advancedTaggingList[index].fallbackGameObject.activeSelf) ? 5f : 3.75f);
|
||||
}
|
||||
|
||||
private void OnDrawHeaderTagging(Rect rect)
|
||||
{
|
||||
Rect _rect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight);
|
||||
GUI.Label(_rect, "Tagged GameObjects");
|
||||
EditorGUIExtensions.UtilityMenu(_rect, _advTaggingList);
|
||||
}
|
||||
|
||||
private void OnDrawElementTagging(Rect rect, int index, bool isActive, bool isFocused)
|
||||
{
|
||||
if (index >= _advTaggingList.count) return;
|
||||
SerializedProperty advTaggingProp = _advTaggingList.serializedProperty.GetArrayElementAtIndex(index);
|
||||
if (advTaggingProp == null || index >= _advTaggingList.serializedProperty.arraySize)
|
||||
return;
|
||||
|
||||
Rect _rect = new (rect.x, rect.y + 2, rect.width, EditorGUIUtility.singleLineHeight);
|
||||
float spacing = EditorGUIUtility.singleLineHeight * 1.25f;
|
||||
float originalLabelWidth = EditorGUIUtility.labelWidth;
|
||||
EditorGUIUtility.labelWidth = 100;
|
||||
|
||||
SerializedProperty tagsProp = advTaggingProp.FindPropertyRelative(nameof(CVRAvatarAdvancedTaggingEntry.tags));
|
||||
EditorGUI.PropertyField(_rect, tagsProp, new GUIContent("Tags"));
|
||||
_rect.y += spacing;
|
||||
|
||||
SerializedProperty gameObjectProp = advTaggingProp.FindPropertyRelative(nameof(CVRAvatarAdvancedTaggingEntry.gameObject));
|
||||
EditorGUI.PropertyField(_rect, gameObjectProp, new GUIContent("GameObject"));
|
||||
_rect.y += spacing;
|
||||
|
||||
SerializedProperty fallbackGameObjectProp = advTaggingProp.FindPropertyRelative(nameof(CVRAvatarAdvancedTaggingEntry.fallbackGameObject));
|
||||
EditorGUI.PropertyField(_rect, fallbackGameObjectProp, new GUIContent("Fallback GO"));
|
||||
_rect.y += spacing;
|
||||
|
||||
if (fallbackGameObjectProp.objectReferenceValue != null)
|
||||
{
|
||||
GameObject fallbackGO = (GameObject)fallbackGameObjectProp.objectReferenceValue;
|
||||
if (fallbackGO.activeSelf)
|
||||
{
|
||||
EditorGUI.HelpBox(_rect, "The Fallback GameObject needs to be disabled by default!", MessageType.Error);
|
||||
_rect.y += spacing;
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUIUtility.labelWidth = originalLabelWidth;
|
||||
}
|
||||
|
||||
private void OnChangedTagging(ReorderableList list)
|
||||
{
|
||||
EditorUtility.SetDirty(target);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 90200356b5f9ddb4da77d79cc62408e0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,37 @@
|
|||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using static ABI.CCK.Scripts.Editor.SharedComponentGUI;
|
||||
|
||||
namespace ABI.CCK.Components
|
||||
{
|
||||
public partial class CCK_CVRAvatarEditor
|
||||
{
|
||||
private void Draw_AvatarCustomization()
|
||||
{
|
||||
using (new FoldoutScope(ref _guiAvatarCustomizationFoldout, "Avatar Customization"))
|
||||
{
|
||||
if (!_guiAvatarCustomizationFoldout) return;
|
||||
using (new EditorGUI.IndentLevelScope())
|
||||
DrawAvatarCustomization();
|
||||
}
|
||||
}
|
||||
|
||||
#region Drawing Methods
|
||||
|
||||
private void DrawAvatarCustomization()
|
||||
{
|
||||
EditorGUILayout.PropertyField(m_OverridesProp, new GUIContent("Animation Overrides"));
|
||||
|
||||
EditorGUILayout.HelpBox(CCKLocalizationProvider.GetLocalizedText("ABI_UI_AVATAR_INFO_OVERRIDE_CONTROLLER"), MessageType.Info);
|
||||
|
||||
Object previousBodyMesh = m_BodyMeshProp.objectReferenceValue;
|
||||
EditorGUILayout.PropertyField(m_BodyMeshProp, new GUIContent("Face Mesh"));
|
||||
if (m_BodyMeshProp.objectReferenceValue != previousBodyMesh)
|
||||
GetBlendshapeNames();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0b03a9525ffb3cf45b0da9f97cddb018
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,42 @@
|
|||
#if UNITY_EDITOR
|
||||
using ABI.CCK.Scripts.Editor;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using static ABI.CCK.Scripts.Editor.SharedComponentGUI;
|
||||
|
||||
namespace ABI.CCK.Components
|
||||
{
|
||||
public partial class CCK_CVRAvatarEditor
|
||||
{
|
||||
private void Draw_EyeBlinkSettings()
|
||||
{
|
||||
using (new FoldoutScope(ref _guiEyeBlinkSettingsFoldout, "Eye Blink Settings"))
|
||||
{
|
||||
if (!_guiEyeBlinkSettingsFoldout) return;
|
||||
using (new EditorGUI.IndentLevelScope())
|
||||
DrawEyeBlinkSettings();
|
||||
}
|
||||
}
|
||||
|
||||
#region Drawing Methods
|
||||
|
||||
private void DrawEyeBlinkSettings()
|
||||
{
|
||||
|
||||
EditorGUILayout.PropertyField(m_UseBlinkBlendshapesProp, new GUIContent("Use Blink Blendshapes"));
|
||||
|
||||
Separator();
|
||||
|
||||
EditorGUIExtensions.LimitSliderSidedProp("Blink Gap (Seconds)", m_BlinkGapProp, CVRAvatar.BlinkMinGapLimit, CVRAvatar.BlinkMaxGapLimit);
|
||||
|
||||
EditorGUIExtensions.LimitSliderSidedProp("Blink Duration (Seconds)", m_BlinkDurationProp, CVRAvatar.BlinkMinDurationLimit, CVRAvatar.BlinkMaxDurationLimit);
|
||||
|
||||
Separator();
|
||||
|
||||
DrawBlendshapes("Blink", m_BlinkBlendshapeProp);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d00535e95d2832841a9af39be34acf6a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
391
Assets/ABI.CCK/Scripts/Editor/CCK_CVRAvatarEditor/CCK_CVRAvatarEditorEyeLook.cs
Executable file
391
Assets/ABI.CCK/Scripts/Editor/CCK_CVRAvatarEditor/CCK_CVRAvatarEditorEyeLook.cs
Executable file
|
@ -0,0 +1,391 @@
|
|||
#if UNITY_EDITOR
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ABI.CCK.Scripts;
|
||||
using ABI.CCK.Scripts.Editor;
|
||||
using UnityEditor;
|
||||
using UnityEditorInternal;
|
||||
using UnityEngine;
|
||||
using static ABI.CCK.Scripts.Editor.SharedComponentGUI;
|
||||
|
||||
namespace ABI.CCK.Components
|
||||
{
|
||||
public partial class CCK_CVRAvatarEditor
|
||||
{
|
||||
// Muscle Limits
|
||||
private Vector2 _leftEyeUpDown, _leftEyeInOut, _rightEyeUpDown, _rightEyeInOut;
|
||||
|
||||
private void Draw_EyeLookSettings()
|
||||
{
|
||||
using (new FoldoutScope(ref _guiEyeLookSettingsFoldout, "Eye Look Settings"))
|
||||
{
|
||||
if (!_guiEyeLookSettingsFoldout) return;
|
||||
using (new EditorGUI.IndentLevelScope())
|
||||
DrawEyeLookSettings();
|
||||
}
|
||||
}
|
||||
|
||||
#region Drawing Methods
|
||||
|
||||
private void DrawEyeLookSettings()
|
||||
{
|
||||
EditorGUILayout.PropertyField(m_UseEyeMovementProp, new GUIContent("Use Eye Movement"));
|
||||
|
||||
Separator();
|
||||
|
||||
EditorGUIExtensions.LimitSliderSidedProp("Switch target interval (Seconds)", m_EyeMovementIntervalProp, CVRAvatar.EyeMovementMinIntervalLimit, CVRAvatar.EyeMovementMaxIntervalLimit);
|
||||
|
||||
// Eye movement mode
|
||||
SerializedProperty typeProp = m_EyeMovementInfoProp.FindPropertyRelative(nameof(CVRAvatar.EyeMovementInfo.type));
|
||||
EditorGUILayout.PropertyField(typeProp, new GUIContent("Eye Look Mode"));
|
||||
|
||||
Separator();
|
||||
|
||||
switch (_avatar.eyeMovementInfo.type)
|
||||
{
|
||||
default: // legacy avatars likely use Muscle
|
||||
case CVRAvatar.CVRAvatarEyeLookMode.Muscle:
|
||||
DrawMuscleEyeSettings();
|
||||
break;
|
||||
case CVRAvatar.CVRAvatarEyeLookMode.None:
|
||||
EditorGUILayout.HelpBox("Eye movement will be completely disabled.", MessageType.Info);
|
||||
break;
|
||||
case CVRAvatar.CVRAvatarEyeLookMode.Transform:
|
||||
m_EyeMovementInfoEyesList.DoLayoutList();
|
||||
EditorGUILayout.HelpBox("Is Left will dictate the Eye In/Out direction, and used for the " +
|
||||
"parameter stream (it will pick the first left and right eye available.", MessageType.Info);
|
||||
break;
|
||||
case CVRAvatar.CVRAvatarEyeLookMode.Blendshape:
|
||||
m_EyeMovementInfoEyesList.DoLayoutList();
|
||||
|
||||
EditorGUILayout.HelpBox("Is Left will dictate the Eye In/Out direction, and used for the " +
|
||||
"parameter stream (it will pick the first left and right eye available.", MessageType.Info);
|
||||
|
||||
EditorGUILayout.HelpBox("These angle limit values will be used to decide when a blendshape takes the value 100. " +
|
||||
"For example if you set the eye up limit to 25 degrees, we will set the blendshape to 100 " +
|
||||
"whenever the eye angle is 25 degrees up (or more).", MessageType.Info);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawMuscleEyeSettings()
|
||||
{
|
||||
if (!_isHumanoid)
|
||||
{
|
||||
EditorGUILayout.HelpBox("Avatar is not configured as humanoid or lacks an Animator component. Muscle eye look mode is not supported!",
|
||||
MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
EditorGUILayout.HelpBox("Using the eye transforms and muscle limits set in the humanoid configuration.",
|
||||
MessageType.Info);
|
||||
|
||||
if (!InnerFoldout(ref _guiEyeLookMuscleInfoFoldout, "Eye Limits"))
|
||||
return;
|
||||
|
||||
EditorGUI.BeginDisabledGroup(true);
|
||||
|
||||
EditorGUILayout.ObjectField("Left Eye", _animator.GetBoneTransform(HumanBodyBones.LeftEye),
|
||||
typeof(Transform), true);
|
||||
EditorGUILayout.ObjectField("Right Eye", _animator.GetBoneTransform(HumanBodyBones.RightEye),
|
||||
typeof(Transform), true);
|
||||
|
||||
EditorGUILayout.LabelField("Left Eye Limits (in degrees)", EditorStyles.boldLabel);
|
||||
EditorGUIExtensions.LimitSliderSided("Eye Down/Up", ref _leftEyeUpDown, -180f, 180f);
|
||||
EditorGUIExtensions.LimitSliderSided("Eye In/Out", ref _leftEyeInOut, -180f, 180f);
|
||||
|
||||
EditorGUILayout.LabelField("Right Eye Limits (in degrees)", EditorStyles.boldLabel);
|
||||
EditorGUIExtensions.LimitSliderSided("Eye Down/Up", ref _rightEyeUpDown, -180f, 180f);
|
||||
EditorGUIExtensions.LimitSliderSided("Eye In/Out", ref _rightEyeInOut, -180f, 180f);
|
||||
|
||||
EditorGUI.EndDisabledGroup();
|
||||
}
|
||||
|
||||
private ReorderableList InitializeEyesReorderableList(SerializedProperty eyeMovementInfoProp) {
|
||||
SerializedProperty eyesListProp = eyeMovementInfoProp.FindPropertyRelative(nameof(CVRAvatar.EyeMovementInfo.eyes));
|
||||
return new ReorderableList(eyeMovementInfoProp.serializedObject, eyesListProp, true, true, true, true) {
|
||||
drawHeaderCallback = OnDrawHeaderEyeSettings,
|
||||
drawElementCallback = OnDrawElementEyeSettings,
|
||||
elementHeightCallback = OnHeightElementEyeSettings,
|
||||
onChangedCallback = OnChangedEyeSettings,
|
||||
onAddCallback = OnAddEyeElement,
|
||||
onRemoveCallback = OnRemoveEyeElement,
|
||||
};
|
||||
}
|
||||
|
||||
private void OnAddEyeElement(ReorderableList list)
|
||||
{
|
||||
Undo.RecordObject(target, "Add Avatar Custom Eye Element");
|
||||
serializedObject.Update();
|
||||
|
||||
var tempList = _avatar.eyeMovementInfo.eyes == null
|
||||
? new List<CVRAvatar.EyeMovementInfoEye>()
|
||||
: new List<CVRAvatar.EyeMovementInfoEye>(_avatar.eyeMovementInfo.eyes);
|
||||
|
||||
// Attempt to auto-populate the eye transforms
|
||||
bool containsOneRightEye = tempList.Exists(e => e is { isLeft: false });
|
||||
Transform eyeInitialTransform = null;
|
||||
if (_animator != null && _animator.isHuman)
|
||||
eyeInitialTransform = _animator.GetBoneTransform(containsOneRightEye ? HumanBodyBones.LeftEye : HumanBodyBones.RightEye);
|
||||
|
||||
switch (_avatar.eyeMovementInfo.type)
|
||||
{
|
||||
case CVRAvatar.CVRAvatarEyeLookMode.Transform:
|
||||
tempList.Add(new CVRAvatar.EyeMovementInfoEye() {
|
||||
isLeft = containsOneRightEye,
|
||||
eyeTransform = eyeInitialTransform,
|
||||
eyeSkinnedMeshRenderer = _avatar.bodyMesh,
|
||||
eyeAngleLimitDown = CVRAvatar.DefaultEyeAngleLimitDown,
|
||||
eyeAngleLimitUp = CVRAvatar.DefaultEyeAngleLimitUp,
|
||||
eyeAngleLimitIn = CVRAvatar.DefaultEyeAngleLimitIn,
|
||||
eyeAngleLimitOut = CVRAvatar.DefaultEyeAngleLimitOut,
|
||||
});
|
||||
break;
|
||||
|
||||
case CVRAvatar.CVRAvatarEyeLookMode.Blendshape:
|
||||
tempList.Add(new CVRAvatar.EyeMovementInfoEye() {
|
||||
isLeft = containsOneRightEye,
|
||||
eyeTransform = eyeInitialTransform,
|
||||
eyeSkinnedMeshRenderer = _avatar.bodyMesh,
|
||||
eyeAngleLimitDown = -CVRAvatar.DefaultUniformAngleLimit,
|
||||
eyeAngleLimitUp = CVRAvatar.DefaultUniformAngleLimit,
|
||||
eyeAngleLimitIn = -CVRAvatar.DefaultUniformAngleLimit,
|
||||
eyeAngleLimitOut = CVRAvatar.DefaultUniformAngleLimit,
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
_avatar.eyeMovementInfo.eyes = tempList.ToArray();
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
EditorUtility.SetDirty(target);
|
||||
}
|
||||
|
||||
private void OnRemoveEyeElement(ReorderableList list)
|
||||
{
|
||||
var index = list.index;
|
||||
var eyesProperty = list.serializedProperty;
|
||||
if (index < 0 || index >= eyesProperty.arraySize) return;
|
||||
eyesProperty.DeleteArrayElementAtIndex(index);
|
||||
eyesProperty.serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
private void OnDrawHeaderEyeSettings(Rect rect)
|
||||
{
|
||||
Rect labelRect = new Rect(rect.x, rect.y, rect.width - 35, EditorGUIUtility.singleLineHeight);
|
||||
GUI.Label(labelRect, "Eyes");
|
||||
}
|
||||
|
||||
private void OnChangedEyeSettings(ReorderableList list)
|
||||
{
|
||||
EditorUtility.SetDirty(target);
|
||||
}
|
||||
|
||||
private float OnHeightElementEyeSettings(int index)
|
||||
{
|
||||
SerializedProperty eyesListProp = m_EyeMovementInfoProp.FindPropertyRelative(nameof(CVRAvatar.EyeMovementInfo.eyes));
|
||||
float spacing = EditorGUIUtility.singleLineHeight * 1.25f;
|
||||
|
||||
if (index >= eyesListProp.arraySize) return spacing;
|
||||
|
||||
float heightElement = spacing * 3;
|
||||
|
||||
SerializedProperty eyeProp = eyesListProp.GetArrayElementAtIndex(index);
|
||||
|
||||
// Handle the missing/wrong transform height
|
||||
bool skip = false;
|
||||
SerializedProperty eyeTransformProp = eyeProp.FindPropertyRelative(nameof(CVRAvatar.EyeMovementInfoEye.eyeTransform));
|
||||
if (eyeTransformProp.objectReferenceValue == null || !((Transform)eyeTransformProp.objectReferenceValue).IsChildOf(_avatar.transform)) {
|
||||
heightElement += spacing * 1;
|
||||
skip = true;
|
||||
}
|
||||
|
||||
if (skip) return heightElement;
|
||||
|
||||
switch (_avatar.eyeMovementInfo.type) {
|
||||
|
||||
case CVRAvatar.CVRAvatarEyeLookMode.Transform:
|
||||
heightElement += spacing * 5;
|
||||
break;
|
||||
|
||||
case CVRAvatar.CVRAvatarEyeLookMode.Blendshape:
|
||||
|
||||
// Handle the missing/wrong mesh renderer height
|
||||
SerializedProperty eyeSkinnedMeshRendererProp = eyeProp.FindPropertyRelative(nameof(CVRAvatar.EyeMovementInfoEye.eyeSkinnedMeshRenderer));
|
||||
if (eyeSkinnedMeshRendererProp.objectReferenceValue == null || !((SkinnedMeshRenderer)eyeSkinnedMeshRendererProp.objectReferenceValue).transform.IsChildOf(_avatar.transform)) {
|
||||
heightElement += spacing * 2;
|
||||
break;
|
||||
}
|
||||
|
||||
heightElement += spacing * 12;
|
||||
break;
|
||||
}
|
||||
|
||||
return heightElement;
|
||||
}
|
||||
|
||||
private void OnDrawElementEyeSettings(Rect rect, int index, bool isActive, bool isFocused) {
|
||||
|
||||
SerializedProperty eyesListProp = m_EyeMovementInfoProp.FindPropertyRelative(nameof(CVRAvatar.EyeMovementInfo.eyes));
|
||||
if (index >= eyesListProp.arraySize) return;
|
||||
|
||||
rect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight);
|
||||
|
||||
float spacing = EditorGUIUtility.singleLineHeight * 1.25f;
|
||||
|
||||
SerializedProperty eyeProp = eyesListProp.GetArrayElementAtIndex(index);
|
||||
EditorGUI.LabelField(rect, $"Eye #{index+1}", EditorStyles.boldLabel);
|
||||
rect.y += spacing;
|
||||
|
||||
SerializedProperty isLeftProp = eyeProp.FindPropertyRelative(nameof(CVRAvatar.EyeMovementInfoEye.isLeft));
|
||||
EditorGUI.PropertyField(rect, isLeftProp);
|
||||
rect.y += spacing;
|
||||
|
||||
switch (_avatar.eyeMovementInfo.type) {
|
||||
case CVRAvatar.CVRAvatarEyeLookMode.Transform:
|
||||
DrawTransformEyeSettingsEye(ref rect, spacing, eyeProp);
|
||||
break;
|
||||
case CVRAvatar.CVRAvatarEyeLookMode.Blendshape:
|
||||
DrawBlendshapeEyeSettingsEye(ref rect, spacing, eyeProp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawTransformEyeSettingsEye(ref Rect rect, float spacing, SerializedProperty eyeProp) {
|
||||
|
||||
SerializedProperty eyeTransformProp = eyeProp.FindPropertyRelative(nameof(CVRAvatar.EyeMovementInfoEye.eyeTransform));
|
||||
|
||||
// Display ObjectFields for Transform
|
||||
eyeTransformProp.objectReferenceValue = EditorGUI.ObjectField(rect, "Eye Transform", eyeTransformProp.objectReferenceValue, typeof(Transform), true);
|
||||
rect.y += spacing;
|
||||
if (eyeTransformProp.objectReferenceValue == null) {
|
||||
EditorGUI.HelpBox(rect, "A Transform is required for Transform and Blenshapes based eye movement!", MessageType.Error);
|
||||
rect.y += spacing;
|
||||
return;
|
||||
}
|
||||
if (!((Transform)eyeTransformProp.objectReferenceValue).IsChildOf(_avatar.transform)) {
|
||||
EditorGUI.HelpBox(rect, "The Transform is not inside of the current avatar's Hierarchy!", MessageType.Error);
|
||||
rect.y += spacing;
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the angle values
|
||||
rect.y += spacing;
|
||||
EditorGUI.LabelField(rect, "Angle Limits (in degrees)", EditorStyles.boldLabel);
|
||||
rect.y += spacing;
|
||||
EditorGUIExtensions.LimitSliderSidedFloats(ref rect, spacing, "Eye Down/Up",
|
||||
eyeProp.FindPropertyRelative(nameof(CVRAvatar.EyeMovementInfoEye.eyeAngleLimitDown)),
|
||||
eyeProp.FindPropertyRelative(nameof(CVRAvatar.EyeMovementInfoEye.eyeAngleLimitUp)), -180f, 180f);
|
||||
|
||||
EditorGUIExtensions.LimitSliderSidedFloats(ref rect, spacing, "Eye In/Out",
|
||||
eyeProp.FindPropertyRelative(nameof(CVRAvatar.EyeMovementInfoEye.eyeAngleLimitIn)),
|
||||
eyeProp.FindPropertyRelative(nameof(CVRAvatar.EyeMovementInfoEye.eyeAngleLimitOut)), -180f, 180f);
|
||||
|
||||
rect.y += spacing;
|
||||
}
|
||||
|
||||
private void DrawBlendshapeEyeSettingsEye(ref Rect rect, float spacing, SerializedProperty eyeProp) {
|
||||
|
||||
SerializedProperty eyeTransformProp = eyeProp.FindPropertyRelative(nameof(CVRAvatar.EyeMovementInfoEye.eyeTransform));
|
||||
SerializedProperty eyeSkinnedMeshRendererProp = eyeProp.FindPropertyRelative(nameof(CVRAvatar.EyeMovementInfoEye.eyeSkinnedMeshRenderer));
|
||||
|
||||
// Display ObjectFields for Transform and SkinnedMeshRenderer
|
||||
eyeTransformProp.objectReferenceValue = EditorGUI.ObjectField(rect, "Eye Position Transform", eyeTransformProp.objectReferenceValue, typeof(Transform), true);
|
||||
rect.y += spacing;
|
||||
if (eyeTransformProp.objectReferenceValue == null) {
|
||||
EditorGUI.HelpBox(rect, "A Transform is required for Transform and Blenshapes based eye movement!", MessageType.Error);
|
||||
rect.y += spacing;
|
||||
return;
|
||||
}
|
||||
if (!((Transform)eyeTransformProp.objectReferenceValue).IsChildOf(_avatar.transform)) {
|
||||
EditorGUI.HelpBox(rect, "The Transform is not inside of the current avatar's Hierarchy!", MessageType.Error);
|
||||
rect.y += spacing;
|
||||
return;
|
||||
}
|
||||
|
||||
eyeSkinnedMeshRendererProp.objectReferenceValue = EditorGUI.ObjectField(rect, "Eye Skinned Mesh Renderer", eyeSkinnedMeshRendererProp.objectReferenceValue, typeof(SkinnedMeshRenderer), true);
|
||||
rect.y += spacing;
|
||||
if (eyeSkinnedMeshRendererProp.objectReferenceValue == null) {
|
||||
EditorGUI.HelpBox(rect, "A Skinned Mesh Renderer is required for Blenshapes based eye movement!", MessageType.Error);
|
||||
rect.y += spacing;
|
||||
return;
|
||||
}
|
||||
if (!((SkinnedMeshRenderer)eyeSkinnedMeshRendererProp.objectReferenceValue).transform.IsChildOf(_avatar.transform)) {
|
||||
EditorGUI.HelpBox(rect, "The Skinned Mesh Renderer is not inside of the current avatar's Hierarchy!", MessageType.Error);
|
||||
rect.y += spacing;
|
||||
return;
|
||||
}
|
||||
|
||||
// Display blendshape selection for each side
|
||||
rect.y += spacing;
|
||||
EditorGUI.LabelField(rect, "Blendshapes", EditorStyles.boldLabel);
|
||||
rect.y += spacing;
|
||||
DrawBlendShapeSelector(ref rect, spacing, "Look Up", eyeProp.FindPropertyRelative(nameof(CVRAvatar.EyeMovementInfoEye.eyeBlendShapeUp)), eyeSkinnedMeshRendererProp.objectReferenceValue as SkinnedMeshRenderer);
|
||||
DrawBlendShapeSelector(ref rect, spacing, "Look Down", eyeProp.FindPropertyRelative(nameof(CVRAvatar.EyeMovementInfoEye.eyeBlendShapeDown)), eyeSkinnedMeshRendererProp.objectReferenceValue as SkinnedMeshRenderer);
|
||||
DrawBlendShapeSelector(ref rect, spacing, "Look In", eyeProp.FindPropertyRelative(nameof(CVRAvatar.EyeMovementInfoEye.eyeBlendShapeIn)), eyeSkinnedMeshRendererProp.objectReferenceValue as SkinnedMeshRenderer);
|
||||
DrawBlendShapeSelector(ref rect, spacing, "Look Out", eyeProp.FindPropertyRelative(nameof(CVRAvatar.EyeMovementInfoEye.eyeBlendShapeOut)), eyeSkinnedMeshRendererProp.objectReferenceValue as SkinnedMeshRenderer);
|
||||
|
||||
// Set the angle values
|
||||
rect.y += spacing;
|
||||
EditorGUI.LabelField(rect, "Angle Limits (in degrees)", EditorStyles.boldLabel);
|
||||
rect.y += spacing;
|
||||
EditorGUIExtensions.LimitSliderSidedFloats(ref rect, spacing, "Eye Down/Up",
|
||||
eyeProp.FindPropertyRelative(nameof(CVRAvatar.EyeMovementInfoEye.eyeAngleLimitDown)),
|
||||
eyeProp.FindPropertyRelative(nameof(CVRAvatar.EyeMovementInfoEye.eyeAngleLimitUp)), -180f, 180f);
|
||||
|
||||
EditorGUIExtensions.LimitSliderSidedFloats(ref rect, spacing, "Eye In/Out",
|
||||
eyeProp.FindPropertyRelative(nameof(CVRAvatar.EyeMovementInfoEye.eyeAngleLimitIn)),
|
||||
eyeProp.FindPropertyRelative(nameof(CVRAvatar.EyeMovementInfoEye.eyeAngleLimitOut)), -180f, 180f);
|
||||
|
||||
rect.y += spacing;
|
||||
}
|
||||
|
||||
private void DrawBlendShapeSelector(ref Rect rect, float spacing, string label, SerializedProperty blendShapeProp, SkinnedMeshRenderer skinnedMeshRenderer) {
|
||||
|
||||
var blendShapes = new List<string> {"-none-"};
|
||||
for (int i = 0; i < skinnedMeshRenderer.sharedMesh.blendShapeCount; i++) {
|
||||
blendShapes.Add(skinnedMeshRenderer.sharedMesh.GetBlendShapeName(i));
|
||||
}
|
||||
|
||||
int currentIndex = blendShapes.IndexOf(blendShapeProp.stringValue);
|
||||
if (currentIndex == -1) currentIndex = 0; // Default to "-none-"
|
||||
|
||||
int selectedIndex = EditorGUI.Popup(rect, label, currentIndex, blendShapes.ToArray());
|
||||
rect.y += spacing;
|
||||
blendShapeProp.stringValue = selectedIndex > 0 ? blendShapes[selectedIndex] : "";
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private void GetHumanoidEyeMuscleLimits()
|
||||
{
|
||||
Vector2 defaultUpDown = new Vector2(-10f, 15f);
|
||||
Vector2 defaultInOut = new Vector2(-20f, 20f);
|
||||
|
||||
foreach (HumanBone humanBone in _animator.avatar.humanDescription.human)
|
||||
if (humanBone.humanName == HumanBodyBones.LeftEye.ToString())
|
||||
{
|
||||
_leftEyeUpDown = humanBone.limit.useDefaultValues
|
||||
? defaultUpDown
|
||||
: new Vector2(humanBone.limit.min.z, humanBone.limit.max.z);
|
||||
_leftEyeInOut = humanBone.limit.useDefaultValues
|
||||
? defaultInOut
|
||||
: new Vector2(-humanBone.limit.max.y, -humanBone.limit.min.y);
|
||||
}
|
||||
else if (humanBone.humanName == HumanBodyBones.RightEye.ToString())
|
||||
{
|
||||
_rightEyeUpDown = humanBone.limit.useDefaultValues
|
||||
? defaultUpDown
|
||||
: new Vector2(humanBone.limit.min.z, humanBone.limit.max.z);
|
||||
_rightEyeInOut = humanBone.limit.useDefaultValues
|
||||
? defaultInOut
|
||||
: new Vector2(humanBone.limit.min.y, humanBone.limit.max.y);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0b75520004bec7345b51aa98559b122c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,161 @@
|
|||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using UnityEditorInternal;
|
||||
using UnityEngine;
|
||||
using static ABI.CCK.Scripts.Editor.SharedComponentGUI;
|
||||
|
||||
namespace ABI.CCK.Components
|
||||
{
|
||||
public partial class CCK_CVRAvatarEditor
|
||||
{
|
||||
private ReorderableList _fprSettingsList;
|
||||
|
||||
private void InitializeFirstPersonRenderList()
|
||||
{
|
||||
if (_fprSettingsList != null)
|
||||
return;
|
||||
|
||||
_fprSettingsList ??= new ReorderableList(serializedObject, m_fprSettingsListProp, true, true, true, true)
|
||||
{
|
||||
drawHeaderCallback = OnDrawHeaderFPR,
|
||||
drawElementCallback = OnDrawElementFPR,
|
||||
elementHeightCallback = OnHeightElementFPR,
|
||||
onChangedCallback = OnChangedFPR,
|
||||
//list = _avatar.fprSettingsList
|
||||
};
|
||||
}
|
||||
|
||||
private void Draw_FPRSettings()
|
||||
{
|
||||
if (!shouldDrawFPRMigrationInfo)
|
||||
return;
|
||||
|
||||
using (new ToggleFoldoutScope(ref _guiFirstPersonRenderSettingsFoldout, ref _avatar.enableCustomFPR, "First Person Render (Deprecated)"))
|
||||
{
|
||||
if (!_guiFirstPersonRenderSettingsFoldout) return;
|
||||
|
||||
Draw_MigrationInfo();
|
||||
|
||||
EditorGUI.BeginDisabledGroup(true);
|
||||
InitializeFirstPersonRenderList();
|
||||
_fprSettingsList.DoLayoutList();
|
||||
EditorGUI.EndDisabledGroup();
|
||||
}
|
||||
}
|
||||
|
||||
#region ReorderableListDrawing FPR
|
||||
|
||||
private void OnDrawHeaderFPR(Rect rect)
|
||||
{
|
||||
Rect labelRect = new Rect(rect.x, rect.y, rect.width - 35, EditorGUIUtility.singleLineHeight);
|
||||
GUI.Label(labelRect, $"FPR Exclusions ({_fprSettingsList.count})");
|
||||
//EditorGUIExtensions.UtilityMenu(rect, _fprSettingsList);
|
||||
}
|
||||
|
||||
private void OnChangedFPR(ReorderableList list)
|
||||
{
|
||||
EditorUtility.SetDirty(target);
|
||||
}
|
||||
|
||||
private float OnHeightElementFPR(int index)
|
||||
{
|
||||
return EditorGUIUtility.singleLineHeight * 1.25f;
|
||||
}
|
||||
|
||||
private void OnDrawElementFPR(Rect rect, int index, bool isActive, bool isFocused)
|
||||
{
|
||||
if (index >= _fprSettingsList.count) return;
|
||||
SerializedProperty fprSettingsEntry = _fprSettingsList.serializedProperty.GetArrayElementAtIndex(index);
|
||||
if (fprSettingsEntry == null || index >= _fprSettingsList.serializedProperty.arraySize)
|
||||
return;
|
||||
|
||||
rect = new Rect(rect.x, rect.y + 2, rect.width, EditorGUIUtility.singleLineHeight);
|
||||
|
||||
SerializedProperty visibilityProp = fprSettingsEntry.FindPropertyRelative(nameof(CVRAvatarFPREntry.visibility));
|
||||
SerializedProperty transformProp = fprSettingsEntry.FindPropertyRelative(nameof(CVRAvatarFPREntry.transform));
|
||||
|
||||
EditorGUI.PropertyField(new Rect(rect.x, rect.y, rect.width - 60, rect.height), transformProp, GUIContent.none);
|
||||
|
||||
rect.x += rect.width - 55;
|
||||
rect.width = 55;
|
||||
|
||||
string[] popupOptions = { "Hide", "Show" };
|
||||
int visibilityIndex = visibilityProp.intValue;
|
||||
|
||||
visibilityIndex = EditorGUI.Popup(rect, visibilityIndex, popupOptions);
|
||||
|
||||
if (visibilityIndex != visibilityProp.intValue)
|
||||
visibilityProp.intValue = visibilityIndex;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region FPRExclusion Migration
|
||||
|
||||
private bool shouldDrawFPRMigrationInfo => _avatar.fprSettingsList is { Count: > 0 };
|
||||
|
||||
private void Draw_MigrationInfo()
|
||||
{
|
||||
EditorGUILayout.HelpBox("This feature has been reworked and moved to the new FPRExclusion component.", MessageType.Warning);
|
||||
EditorGUILayout.HelpBox("You may migrate your old settings to the new system by clicking the button below. Note: You may need to manually tweak things afterward.", MessageType.Info);
|
||||
if (GUILayout.Button("Migrate FPR Settings")) MigrateFPRSettings();
|
||||
if (GUILayout.Button("Remove FPR Settings")) RemoveFPRSettings();
|
||||
}
|
||||
|
||||
private void MigrateFPRSettings()
|
||||
{
|
||||
// prompt user
|
||||
if (!EditorUtility.DisplayDialog("Migrate FPR Settings",
|
||||
"This process will autogenerate a GameObject named CCK.FPRExclusions on the root of your avatar containing the new components. This cannot be undone.", "Do it", "No thanks"))
|
||||
return;
|
||||
|
||||
// convert FPREntries into FPRExclusion components
|
||||
|
||||
var oldFPRSettings = _avatar.fprSettingsList;
|
||||
|
||||
// add gameobject to root of avatar
|
||||
GameObject fprExclusionRoot = new ("CCK.FPRExclusions");
|
||||
fprExclusionRoot.transform.SetParent(_avatar.transform);
|
||||
fprExclusionRoot.transform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity);
|
||||
fprExclusionRoot.transform.localScale = Vector3.one;
|
||||
|
||||
foreach (CVRAvatarFPREntry entry in oldFPRSettings)
|
||||
{
|
||||
if (entry.transform == null) continue; // skip null entries
|
||||
|
||||
// create new child object with FPRExclusion component
|
||||
GameObject newExclusion = new ("[FPR] " + entry.transform.name, typeof(FPRExclusion));
|
||||
newExclusion.transform.SetParent(fprExclusionRoot.transform);
|
||||
newExclusion.transform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity);
|
||||
newExclusion.transform.localScale = Vector3.one;
|
||||
|
||||
// set target
|
||||
FPRExclusion fprExclusion = newExclusion.GetComponent<FPRExclusion>();
|
||||
fprExclusion.target = entry.transform;
|
||||
fprExclusion.isShown = entry.visibility;
|
||||
}
|
||||
|
||||
// remove old settings
|
||||
_avatar.fprSettingsList.Clear();
|
||||
_avatar.fprSettingsList = null;
|
||||
|
||||
// ping new object in hierarchy
|
||||
EditorGUIUtility.PingObject(fprExclusionRoot);
|
||||
}
|
||||
|
||||
private void RemoveFPRSettings()
|
||||
{
|
||||
// prompt user
|
||||
if (!EditorUtility.DisplayDialog("Remove FPR Settings",
|
||||
"This process will remove all depricated FPR settings from this avatar. This cannot be undone.", "Do it", "No thanks"))
|
||||
return;
|
||||
|
||||
// remove old settings
|
||||
_avatar.fprSettingsList.Clear();
|
||||
_avatar.fprSettingsList = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d7c23f289e852114f8795da96ecaae1c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,43 @@
|
|||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using static ABI.CCK.Scripts.Editor.SharedComponentGUI;
|
||||
|
||||
namespace ABI.CCK.Components
|
||||
{
|
||||
public partial class CCK_CVRAvatarEditor
|
||||
{
|
||||
private void Draw_GeneralAvatarSettings()
|
||||
{
|
||||
using (new FoldoutScope(ref _guiAvatarSettingsFoldout, "General Avatar Settings"))
|
||||
{
|
||||
if (!_guiAvatarSettingsFoldout) return;
|
||||
using (new EditorGUI.IndentLevelScope())
|
||||
DrawGeneralSettings();
|
||||
}
|
||||
}
|
||||
|
||||
#region Drawing Methods
|
||||
|
||||
private void DrawGeneralSettings()
|
||||
{
|
||||
using (new EditorGUILayout.HorizontalScope())
|
||||
{
|
||||
EditorGUILayout.PropertyField(m_ViewPositionProp, new GUIContent("View Position"));
|
||||
if (_isHumanoid && GUILayout.Button("Auto", GUILayout.Width(40))) AutoSetViewPosition();
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
if (_isHumanoid) EditorGUILayout.PropertyField(m_VoiceParentProp, new GUIContent("Voice Parent"));
|
||||
|
||||
using (new EditorGUILayout.HorizontalScope())
|
||||
{
|
||||
EditorGUILayout.PropertyField(m_VoicePositionProp, new GUIContent("Voice Position"));
|
||||
if (_isHumanoid && GUILayout.Button("Auto", GUILayout.Width(40))) AutoSetVoicePosition();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f1fc531404a36bc428e4f04d465d6595
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,80 @@
|
|||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using static ABI.CCK.Scripts.Editor.SharedComponentGUI;
|
||||
|
||||
namespace ABI.CCK.Components
|
||||
{
|
||||
public partial class CCK_CVRAvatarEditor
|
||||
{
|
||||
private void Draw_LipSyncSettings()
|
||||
{
|
||||
using (new FoldoutScope(ref _guiLipSyncSettingsFoldout, "Lip Sync Settings"))
|
||||
{
|
||||
if (!_guiLipSyncSettingsFoldout) return;
|
||||
using (new EditorGUI.IndentLevelScope())
|
||||
DrawLipSyncSettings();
|
||||
}
|
||||
}
|
||||
|
||||
#region Drawing Methods
|
||||
|
||||
private void DrawLipSyncSettings()
|
||||
{
|
||||
EditorGUILayout.PropertyField(m_UseVisemeLipsyncProp, new GUIContent("Use Lip Sync"));
|
||||
EditorGUILayout.PropertyField(m_VisemeModeProp, new GUIContent("Lip Sync Mode"));
|
||||
|
||||
Separator();
|
||||
|
||||
switch (_avatar.visemeMode)
|
||||
{
|
||||
default: // not really sure what the default is?
|
||||
case CVRAvatar.CVRAvatarVisemeMode.Visemes:
|
||||
{
|
||||
DrawLipSyncVisemeMode();
|
||||
break;
|
||||
}
|
||||
case CVRAvatar.CVRAvatarVisemeMode.SingleBlendshape:
|
||||
DrawBlendshape("Single Blendshape: ", m_VisemeBlendshapesProp);
|
||||
break;
|
||||
case CVRAvatar.CVRAvatarVisemeMode.JawBone:
|
||||
DrawLipSyncJawBoneMode();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawLipSyncVisemeMode()
|
||||
{
|
||||
EditorGUILayout.PropertyField(m_VisemeSmoothingProp, new GUIContent("Viseme Smoothing"), GUILayout.MinWidth(10));
|
||||
DrawBlendshapes("Viseme: ", m_VisemeBlendshapesProp, _visemeNames);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
if (_avatar.bodyMesh != null && GUILayout.Button("Auto Select Visemes"))
|
||||
{
|
||||
m_UseVisemeLipsyncProp.boolValue = true;
|
||||
AutoSelectVisemeBlendshapes();
|
||||
}
|
||||
|
||||
//EditorGUILayout.HelpBox(CCKLocalizationProvider.GetLocalizedText("ABI_UI_AVATAR_INFO_EYE_VISEMES"), MessageType.Info);
|
||||
}
|
||||
|
||||
private void DrawLipSyncJawBoneMode()
|
||||
{
|
||||
if (!_isHumanoid)
|
||||
{
|
||||
EditorGUILayout.HelpBox("Avatar is not configured as humanoid or lacks an Animator component. Jaw bone lipsync mode is not supported!",
|
||||
MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
EditorGUILayout.HelpBox("Using the jaw transform set in the humanoid configuration.", MessageType.Info);
|
||||
|
||||
using (new EditorGUI.DisabledGroupScope(true))
|
||||
EditorGUILayout.ObjectField("Jaw", _animator.GetBoneTransform(HumanBodyBones.Jaw), typeof(Transform), true);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a2a799dec0d33114199d9bba008748fa
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Loading…
Add table
Add a link
Reference in a new issue