update CCK to 3.10, fixing unity 2021 crash :)

This commit is contained in:
Crispy 2024-08-03 22:24:42 +02:00
parent 48a978fa2a
commit d11e0fb3a9
492 changed files with 2165204 additions and 437687 deletions

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 77bf502e56374ff8ba1488a070dc2435
timeCreated: 1709423065

View file

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

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d6aa02cac0694cab9ba81de900bdf4c6
timeCreated: 1714267882

View file

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

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6118c371302943b6bb8a740c62dd0a97
timeCreated: 1714268283

View file

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

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d0297c5beb0a41ea8108dcbda5dd6bc0
timeCreated: 1709424213

View file

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

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4e1f511a5d874fd9b5bc9c8a1728066e
timeCreated: 1709424050

View file

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

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: fd5428143ca147de8f8649f82edbce3b
timeCreated: 1709424338

View file

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

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 877f1862df6d493fb7547361ef50ce65
timeCreated: 1709424273

View file

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

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d42afa81de944f8b96dd614d64b9e485
timeCreated: 1709424234

View file

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

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: df6671fcd41141df877371502a7eb664
timeCreated: 1709423125

View 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

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f674ceaf9f32b8b49a43756c6b8c534f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

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

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a1a0903b37ddaac4cadea63af36bb2f0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

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

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 90200356b5f9ddb4da77d79cc62408e0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

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

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0b03a9525ffb3cf45b0da9f97cddb018
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

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

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d00535e95d2832841a9af39be34acf6a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View 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

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0b75520004bec7345b51aa98559b122c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

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

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d7c23f289e852114f8795da96ecaae1c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

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

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f1fc531404a36bc428e4f04d465d6595
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

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

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a2a799dec0d33114199d9bba008748fa
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: