mirror of
https://github.com/NotAKidoS/NAK_CVR_Mods.git
synced 2025-09-02 14:29:25 +00:00
[AvatarScaleMod] Implement scaled components.
implemented scaling for specific components that Avatar Scale Tool would they conflict, if you ran your avatar through Avatar Scale Tool then the animations will add on top
This commit is contained in:
parent
44d5c7762b
commit
e8d3183bc3
7 changed files with 367 additions and 70 deletions
42
AvatarScale/AvatarScaleGesture.cs
Normal file
42
AvatarScale/AvatarScaleGesture.cs
Normal file
|
@ -0,0 +1,42 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace NAK.AvatarScaleMod;
|
||||
|
||||
public static class AvatarScaleGesture
|
||||
{
|
||||
public static float InitialModifier = 1f;
|
||||
public static float InitialTargetHeight = 1.8f;
|
||||
|
||||
public static void OnScaleStart(float modifier, Transform transform1, Transform transform2)
|
||||
{
|
||||
// AvatarScaleMod.Logger.Msg("OnScaleStart!");
|
||||
|
||||
if (AvatarScaleManager.LocalAvatar != null)
|
||||
{
|
||||
// store initial modifier
|
||||
InitialModifier = modifier;
|
||||
InitialTargetHeight = AvatarScaleManager.LocalAvatar.TargetHeight;
|
||||
}
|
||||
}
|
||||
|
||||
public static void OnScaleStay(float modifier, Transform transform1, Transform transform2)
|
||||
{
|
||||
// AvatarScaleMod.Logger.Msg("OnScaleStay!");
|
||||
|
||||
if (AvatarScaleManager.LocalAvatar != null)
|
||||
{
|
||||
float modifierRatio = modifier / InitialModifier;
|
||||
|
||||
// Determine the adjustment factor for the height, this will be >1 if scaling up, <1 if scaling down.
|
||||
float heightAdjustmentFactor = (modifierRatio > 1) ? 1 + (modifierRatio - 1) : 1 - (1 - modifierRatio);
|
||||
|
||||
// Apply the adjustment to the target height
|
||||
AvatarScaleManager.LocalAvatar.SetTargetHeight(InitialTargetHeight * heightAdjustmentFactor);
|
||||
}
|
||||
}
|
||||
|
||||
public static void OnScaleEnd(float modifier, Transform transform1, Transform transform2)
|
||||
{
|
||||
// AvatarScaleMod.Logger.Msg("OnScaleEnd!");
|
||||
}
|
||||
}
|
209
AvatarScale/AvatarScaleManager.cs
Normal file
209
AvatarScale/AvatarScaleManager.cs
Normal file
|
@ -0,0 +1,209 @@
|
|||
using ABI.CCK.Components;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Animations;
|
||||
|
||||
namespace NAK.AvatarScaleMod;
|
||||
|
||||
public class AvatarScaleManager : MonoBehaviour
|
||||
{
|
||||
public static AvatarScaleManager LocalAvatar { get; private set; }
|
||||
|
||||
// List of component types to be collected and scaled
|
||||
private static readonly System.Type[] scaleComponentTypes = new System.Type[]
|
||||
{
|
||||
typeof(Light),
|
||||
typeof(AudioSource),
|
||||
typeof(ParticleSystem),
|
||||
typeof(ParentConstraint),
|
||||
typeof(PositionConstraint),
|
||||
typeof(ScaleConstraint),
|
||||
};
|
||||
|
||||
public const float MinimumHeight = 0.25f;
|
||||
public const float MaximumHeight = 2.5f;
|
||||
|
||||
// Scalable Components
|
||||
private List<ScaledLight> _lights = new List<ScaledLight>();
|
||||
private List<ScaledAudioSource> _audioSources = new List<ScaledAudioSource>();
|
||||
//private List<ScaledComponent<ParticleSystem>> _particleSystems = new List<ScaledComponent<ParticleSystem>>();
|
||||
private List<ScaledParentConstraint> _parentConstraints = new List<ScaledParentConstraint>();
|
||||
private List<ScaledPositionConstraint> _positionConstraints = new List<ScaledPositionConstraint>();
|
||||
private List<ScaledScaleConstraint> _scaleConstraints = new List<ScaledScaleConstraint>();
|
||||
|
||||
public float TargetHeight { get; private set; }
|
||||
public float InitialHeight { get; private set; }
|
||||
public Vector3 InitialScale { get; private set; }
|
||||
public float ScaleFactor { get; private set; }
|
||||
|
||||
public void Initialize(float initialHeight, Vector3 initialScale)
|
||||
{
|
||||
// Check for zero height
|
||||
if (Math.Abs(initialHeight) < 1E-6)
|
||||
{
|
||||
AvatarScaleMod.Logger.Warning("Cannot initialize with a height of zero!");
|
||||
return;
|
||||
}
|
||||
|
||||
this.TargetHeight = 1f;
|
||||
this.InitialHeight = initialHeight;
|
||||
this.InitialScale = initialScale;
|
||||
UpdateScaleFactor();
|
||||
}
|
||||
|
||||
public void SetTargetHeight(float newHeight)
|
||||
{
|
||||
TargetHeight = Mathf.Clamp(newHeight, MinimumHeight, MaximumHeight);
|
||||
UpdateScaleFactor();
|
||||
}
|
||||
|
||||
public void UpdateScaleFactor()
|
||||
{
|
||||
// Check for zero
|
||||
if (Math.Abs(InitialHeight) < 1E-6)
|
||||
{
|
||||
AvatarScaleMod.Logger.Warning("InitialHeight is zero, cannot calculate ScaleFactor.");
|
||||
return;
|
||||
}
|
||||
|
||||
this.ScaleFactor = TargetHeight / InitialHeight;
|
||||
}
|
||||
|
||||
private Vector3 CalculateNewScale()
|
||||
{
|
||||
return InitialScale * ScaleFactor;
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
// why am i caching the avatar
|
||||
CVRAvatar avatar = GetComponent<CVRAvatar>();
|
||||
if (avatar == null)
|
||||
{
|
||||
AvatarScaleMod.Logger.Error("AvatarScaleManager should be attached to a GameObject with a CVRAvatar component.");
|
||||
return;
|
||||
}
|
||||
|
||||
// i cant believe i would stoop this low
|
||||
if (gameObject.layer == 8 && LocalAvatar == null)
|
||||
LocalAvatar = this;
|
||||
|
||||
FindComponentsOfType(scaleComponentTypes);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
_audioSources.Clear();
|
||||
_lights.Clear();
|
||||
//_particleSystems.Clear(); // fuck no
|
||||
_parentConstraints.Clear();
|
||||
_positionConstraints.Clear();
|
||||
_scaleConstraints.Clear();
|
||||
|
||||
// local player manager
|
||||
if (LocalAvatar == this)
|
||||
LocalAvatar = null;
|
||||
}
|
||||
|
||||
private void FindComponentsOfType(params System.Type[] types)
|
||||
{
|
||||
foreach (var type in types)
|
||||
{
|
||||
var components = gameObject.GetComponentsInChildren(type, true);
|
||||
foreach (var component in components)
|
||||
{
|
||||
switch (component)
|
||||
{
|
||||
case AudioSource audioSource:
|
||||
_audioSources.Add(new ScaledAudioSource(audioSource));
|
||||
break;
|
||||
case Light light:
|
||||
_lights.Add(new ScaledLight(light));
|
||||
break;
|
||||
case ParentConstraint parentConstraint:
|
||||
_parentConstraints.Add(new ScaledParentConstraint(parentConstraint));
|
||||
break;
|
||||
case PositionConstraint positionConstraint:
|
||||
_positionConstraints.Add(new ScaledPositionConstraint(positionConstraint));
|
||||
break;
|
||||
case ScaleConstraint scaleConstraint:
|
||||
_scaleConstraints.Add(new ScaledScaleConstraint(scaleConstraint));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
ApplyAvatarScaling();
|
||||
ApplyComponentScaling();
|
||||
}
|
||||
|
||||
void LateUpdate()
|
||||
{
|
||||
ApplyAvatarScaling();
|
||||
ApplyComponentScaling();
|
||||
}
|
||||
|
||||
private void ApplyAvatarScaling()
|
||||
{
|
||||
transform.localScale = CalculateNewScale();
|
||||
}
|
||||
|
||||
private void ApplyComponentScaling()
|
||||
{
|
||||
UpdateLightScales();
|
||||
UpdateAudioSourceScales();
|
||||
UpdateParentConstraintScales();
|
||||
UpdatePositionConstraintScales();
|
||||
UpdateScaleConstraintScales();
|
||||
}
|
||||
|
||||
private void UpdateLightScales()
|
||||
{
|
||||
foreach (var scaledLight in _lights)
|
||||
{
|
||||
scaledLight.Component.range = scaledLight.InitialRange * ScaleFactor;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateAudioSourceScales()
|
||||
{
|
||||
foreach (var scaledAudioSource in _audioSources)
|
||||
{
|
||||
scaledAudioSource.Component.minDistance = scaledAudioSource.InitialMinDistance * ScaleFactor;
|
||||
scaledAudioSource.Component.maxDistance = scaledAudioSource.InitialMaxDistance * ScaleFactor;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateParentConstraintScales()
|
||||
{
|
||||
foreach (var scaledParentConstraint in _parentConstraints)
|
||||
{
|
||||
scaledParentConstraint.Component.translationAtRest = scaledParentConstraint.InitialTranslationAtRest * ScaleFactor;
|
||||
|
||||
for (int i = 0; i < scaledParentConstraint.InitialTranslationOffsets.Count; i++)
|
||||
{
|
||||
scaledParentConstraint.Component.translationOffsets[i] = scaledParentConstraint.InitialTranslationOffsets[i] * ScaleFactor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdatePositionConstraintScales()
|
||||
{
|
||||
foreach (var scaledPositionConstraint in _positionConstraints)
|
||||
{
|
||||
scaledPositionConstraint.Component.translationAtRest = scaledPositionConstraint.InitialTranslationAtRest * ScaleFactor;
|
||||
scaledPositionConstraint.Component.translationOffset = scaledPositionConstraint.InitialTranslationOffset * ScaleFactor;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateScaleConstraintScales()
|
||||
{
|
||||
foreach (var scaledScaleConstraint in _scaleConstraints)
|
||||
{
|
||||
scaledScaleConstraint.Component.scaleAtRest = scaledScaleConstraint.InitialScaleAtRest * ScaleFactor;
|
||||
scaledScaleConstraint.Component.scaleOffset = scaledScaleConstraint.InitialScaleOffset * ScaleFactor;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,42 +1,13 @@
|
|||
using ABI_RC.Core.Player;
|
||||
using ABI_RC.Core.Savior;
|
||||
using HarmonyLib;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace NAK.AvatarScaleMod.HarmonyPatches;
|
||||
|
||||
class PlayerSetupPatches
|
||||
{
|
||||
/**
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(PlayerSetup), nameof(PlayerSetup.SetupAvatar))]
|
||||
static void Postfix_PlayerSetup_SetupAvatar(ref PlayerSetup __instance, ref float ____initialAvatarHeight)
|
||||
{
|
||||
if (!AvatarScaleMod.EntryEnabled.Value) return;
|
||||
|
||||
if (AvatarScaleMod.HiddenAvatarScale.Value > 0f)
|
||||
{
|
||||
__instance.changeAnimatorParam(AvatarScaleMod.ParameterName, AvatarScaleMod.HiddenAvatarScale.Value);
|
||||
return;
|
||||
}
|
||||
|
||||
// User has cleared MelonPrefs, store a default value.
|
||||
AvatarScaleMod.HiddenAvatarScale.Value = Utils.CalculateParameterValue(____initialAvatarHeight);
|
||||
}
|
||||
|
||||
[HarmonyPrefix]
|
||||
[HarmonyPatch(typeof(PlayerSetup), nameof(PlayerSetup.ClearAvatar))]
|
||||
static void Prefix_PlayerSetup_ClearAvatar(ref PlayerSetup __instance, ref float ____avatarHeight)
|
||||
{
|
||||
if (!AvatarScaleMod.EntryEnabled.Value) return;
|
||||
|
||||
if (!Utils.IsSupportedAvatar(__instance.animatorManager) && !AvatarScaleMod.EntryPersistAnyways.Value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AvatarScaleMod.HiddenAvatarScale.Value = Utils.CalculateParameterValue(____avatarHeight);
|
||||
}
|
||||
**/
|
||||
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(PlayerSetup), nameof(PlayerSetup.SetupAvatar))]
|
||||
static void Postfix_PlayerSetup_SetupAvatar(ref PlayerSetup __instance)
|
||||
|
@ -51,4 +22,39 @@ class PlayerSetupPatches
|
|||
AvatarScaleMod.Logger.Error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class GesturePlaneTestPatches
|
||||
{
|
||||
[HarmonyPostfix]
|
||||
[HarmonyPatch(typeof(GesturePlaneTest), nameof(GesturePlaneTest.Start))]
|
||||
static void Postfix_GesturePlaneTest_Start()
|
||||
{
|
||||
try
|
||||
{
|
||||
// nicked from Kafe >:))))
|
||||
var gesture = new CVRGesture
|
||||
{
|
||||
name = "avatarScale",
|
||||
type = CVRGesture.GestureType.Hold,
|
||||
};
|
||||
gesture.steps.Add(new CVRGestureStep
|
||||
{
|
||||
firstGesture = CVRGestureStep.Gesture.Fist,
|
||||
secondGesture = CVRGestureStep.Gesture.Fist,
|
||||
startDistance = 0.5f,
|
||||
endDistance = 0.4f,
|
||||
direction = CVRGestureStep.GestureDirection.MovingIn,
|
||||
});
|
||||
gesture.onStart.AddListener(new UnityAction<float, Transform, Transform>(AvatarScaleGesture.OnScaleStart));
|
||||
gesture.onStay.AddListener(new UnityAction<float, Transform, Transform>(AvatarScaleGesture.OnScaleStay));
|
||||
gesture.onEnd.AddListener(new UnityAction<float, Transform, Transform>(AvatarScaleGesture.OnScaleEnd));
|
||||
CVRGestureRecognizer.Instance.gestures.Add(gesture);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
AvatarScaleMod.Logger.Error($"Error during the patched method {nameof(Postfix_GesturePlaneTest_Start)}");
|
||||
AvatarScaleMod.Logger.Error(e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,28 +4,22 @@ namespace NAK.AvatarScaleMod;
|
|||
|
||||
public class AvatarScaleMod : MelonMod
|
||||
{
|
||||
internal const string ParameterName = "AvatarScale";
|
||||
internal const float MinimumHeight = 0.25f;
|
||||
internal const float MaximumHeight = 2.5f;
|
||||
|
||||
internal static MelonLogger.Instance Logger;
|
||||
|
||||
public static readonly MelonPreferences_Category Category =
|
||||
MelonPreferences.CreateCategory(nameof(AvatarScaleMod));
|
||||
|
||||
public static readonly MelonPreferences_Entry<bool> EntryEnabled =
|
||||
Category.CreateEntry("Enabled", true, description: "Should there be persistant avatar scaling? This only works properly across supported avatars.");
|
||||
Category.CreateEntry("Enabled", true, description: "Toggle AvatarScaleMod entirely.");
|
||||
|
||||
public static readonly MelonPreferences_Entry<bool> EntryPersistAnyways =
|
||||
Category.CreateEntry("Persist From Unsupported", true, description: "Should avatar scale persist even from unsupported avatars?");
|
||||
|
||||
public static readonly MelonPreferences_Entry<float> HiddenAvatarScale =
|
||||
Category.CreateEntry("Last Avatar Scale", -1f, is_hidden: true);
|
||||
public static readonly MelonPreferences_Entry<bool> EntryUseScaleGesture =
|
||||
Category.CreateEntry("Scale Gesture", true, description: "Use two fists to scale yourself easily.");
|
||||
|
||||
public override void OnInitializeMelon()
|
||||
{
|
||||
Logger = LoggerInstance;
|
||||
ApplyPatches(typeof(HarmonyPatches.PlayerSetupPatches));
|
||||
ApplyPatches(typeof(HarmonyPatches.GesturePlaneTestPatches));
|
||||
}
|
||||
|
||||
void ApplyPatches(Type type)
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
# AvatarScaleMod
|
||||
|
||||
Makes "AvatarScale" parameter persistant across avatars.
|
||||
Proof of concept mod to add Avatar Scaling to any avatar. This is local-only.
|
||||
|
||||
Combined with [AvatarScaleTool](https://github.com/NotAKidOnSteam/AvatarScaleTool), this allows for consistant scale when switching between avatars.
|
||||
Legit threw this together in three hours. ChilloutVR handles all the hard stuff already lmao.
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
Here is the block of text where I tell you this mod is not affiliated or endorsed by ABI.
|
||||
|
|
72
AvatarScale/ScaledComponents.cs
Normal file
72
AvatarScale/ScaledComponents.cs
Normal file
|
@ -0,0 +1,72 @@
|
|||
using UnityEngine;
|
||||
using UnityEngine.Animations;
|
||||
|
||||
namespace NAK.AvatarScaleMod;
|
||||
|
||||
public class ScaledAudioSource
|
||||
{
|
||||
public AudioSource Component { get; }
|
||||
public float InitialMinDistance { get; }
|
||||
public float InitialMaxDistance { get; }
|
||||
|
||||
public ScaledAudioSource(AudioSource component)
|
||||
{
|
||||
Component = component;
|
||||
InitialMinDistance = component.minDistance;
|
||||
InitialMaxDistance = component.maxDistance;
|
||||
}
|
||||
}
|
||||
|
||||
public class ScaledLight
|
||||
{
|
||||
public Light Component { get; }
|
||||
public float InitialRange { get; }
|
||||
|
||||
public ScaledLight(Light component)
|
||||
{
|
||||
Component = component;
|
||||
InitialRange = component.range;
|
||||
}
|
||||
}
|
||||
|
||||
public class ScaledPositionConstraint
|
||||
{
|
||||
public PositionConstraint Component { get; }
|
||||
public Vector3 InitialTranslationAtRest { get; }
|
||||
public Vector3 InitialTranslationOffset { get; }
|
||||
|
||||
public ScaledPositionConstraint(PositionConstraint component)
|
||||
{
|
||||
Component = component;
|
||||
InitialTranslationAtRest = component.translationAtRest;
|
||||
InitialTranslationOffset = component.translationOffset;
|
||||
}
|
||||
}
|
||||
|
||||
public class ScaledParentConstraint
|
||||
{
|
||||
public ParentConstraint Component { get; }
|
||||
public Vector3 InitialTranslationAtRest { get; }
|
||||
public List<Vector3> InitialTranslationOffsets { get; }
|
||||
|
||||
public ScaledParentConstraint(ParentConstraint component)
|
||||
{
|
||||
Component = component;
|
||||
InitialTranslationAtRest = component.translationAtRest;
|
||||
InitialTranslationOffsets = component.translationOffsets.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
public class ScaledScaleConstraint
|
||||
{
|
||||
public ScaleConstraint Component { get; }
|
||||
public Vector3 InitialScaleAtRest { get; }
|
||||
public Vector3 InitialScaleOffset { get; }
|
||||
|
||||
public ScaledScaleConstraint(ScaleConstraint component)
|
||||
{
|
||||
Component = component;
|
||||
InitialScaleAtRest = component.scaleAtRest;
|
||||
InitialScaleOffset = component.scaleOffset;
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
using ABI_RC.Core;
|
||||
|
||||
namespace NAK.AvatarScaleMod;
|
||||
|
||||
class Utils
|
||||
{
|
||||
public static bool IsSupportedAvatar(CVRAnimatorManager manager)
|
||||
{
|
||||
if (manager.animatorParameterFloatList.Contains(AvatarScaleMod.ParameterName) && manager._animator != null)
|
||||
{
|
||||
if (manager._advancedAvatarIndicesFloat.TryGetValue(AvatarScaleMod.ParameterName, out int index))
|
||||
{
|
||||
return index < manager._advancedAvatarCacheFloat.Count;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static float CalculateParameterValue(float lastAvatarHeight)
|
||||
{
|
||||
float t = (lastAvatarHeight - AvatarScaleMod.MinimumHeight) / (AvatarScaleMod.MaximumHeight - AvatarScaleMod.MinimumHeight);
|
||||
return t;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue