Skip to content

Commit

Permalink
Add option to use fake mirror
Browse files Browse the repository at this point in the history
  • Loading branch information
nicoco007 committed Oct 28, 2024
1 parent 3e57169 commit c217ed5
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 3 deletions.
10 changes: 8 additions & 2 deletions Source/CustomAvatar/Avatar/AvatarSpawner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,7 @@ public SpawnedAvatar SpawnAvatar(AvatarPrefab avatar, IAvatarInput input, Transf
_logger.LogInformation($"Spawning avatar '{avatar.descriptor.name}'");
}

GameObject avatarInstance = Object.Instantiate(avatar, parent, false).gameObject;
Object.DestroyImmediate(avatarInstance.GetComponent<AvatarPrefab>());
GameObject avatarInstance = SpawnBareAvatar(avatar, parent);

var subContainer = new DiContainer(_container);
subContainer.Bind<AvatarPrefab>().FromInstance(avatar);
Expand All @@ -109,6 +108,13 @@ public SpawnedAvatar SpawnAvatar(AvatarPrefab avatar, IAvatarInput input, Transf
return spawnedAvatar;
}

internal GameObject SpawnBareAvatar(AvatarPrefab avatar, Transform parent = null)
{
GameObject avatarInstance = Object.Instantiate(avatar, parent, false).gameObject;
avatarInstance.GetComponent<AvatarPrefab>().enabled = false;
return avatarInstance;
}

private bool ShouldAddTransformTracking(AvatarPrefab avatarPrefab)
{
return avatarPrefab.head || avatarPrefab.leftHand || avatarPrefab.rightHand || avatarPrefab.pelvis || avatarPrefab.leftLeg || avatarPrefab.rightLeg;
Expand Down
1 change: 1 addition & 0 deletions Source/CustomAvatar/Configuration/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ public class Mirror
{
public ObservableValue<float> renderScale { get; } = new ObservableValue<float>(1);
public ObservableValue<int> antiAliasingLevel { get; } = new ObservableValue<int>(1);
public ObservableValue<bool> useFakeMirrorBeta { get; } = new ObservableValue<bool>(false);
public bool renderInExternalCameras { get; set; } = false;
}

Expand Down
6 changes: 6 additions & 0 deletions Source/CustomAvatar/UI/InterfaceSettingsHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ internal int antiAliasingLevel
set => _settings.mirror.antiAliasingLevel.value = value;
}

internal bool useFakeMirror
{
get => _settings.mirror.useFakeMirrorBeta;
set => _settings.mirror.useFakeMirrorBeta.value = value;
}

internal bool renderInExternalCameras
{
get => _settings.mirror.renderInExternalCameras;
Expand Down
99 changes: 98 additions & 1 deletion Source/CustomAvatar/UI/MirrorViewController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.

using System;
using System.Linq;
using BeatSaberMarkupLanguage.Attributes;
using BeatSaberMarkupLanguage.ViewControllers;
using BGLib.Polyglot;
Expand All @@ -23,6 +24,7 @@
using CustomAvatar.Player;
using CustomAvatar.Rendering;
using CustomAvatar.Tracking;
using CustomAvatar.Utilities;
using HMUI;
using JetBrains.Annotations;
using TMPro;
Expand All @@ -45,6 +47,7 @@ internal class MirrorViewController : BSMLAutomaticViewController
private PlayerAvatarManager _avatarManager;
private HierarchyManager _hierarchyManager;
private PlatformLeaderboardViewController _platformLeaderboardViewController;
private AvatarSpawner _avatarSpawner;
private TrackingRig _trackingRig;

private bool _isLoaderActive;
Expand All @@ -55,6 +58,8 @@ internal class MirrorViewController : BSMLAutomaticViewController
private TextMeshProUGUI _progressTitle;
private TextMeshProUGUI _progressText;

private GameObject _mirroredAvatarContainer;

protected bool isLoaderActive
{
get => _isLoaderActive;
Expand Down Expand Up @@ -98,6 +103,7 @@ internal void Construct(
PlayerAvatarManager avatarManager,
HierarchyManager hierarchyManager,
PlatformLeaderboardViewController platformLeaderboardViewController,
AvatarSpawner avatarSpawner,
TrackingRig trackingRig)
{
_container = container;
Expand All @@ -107,6 +113,7 @@ internal void Construct(
_avatarManager = avatarManager;
_hierarchyManager = hierarchyManager;
_platformLeaderboardViewController = platformLeaderboardViewController;
_avatarSpawner = avatarSpawner;
_trackingRig = trackingRig;
}

Expand All @@ -126,6 +133,7 @@ protected override void DidActivate(bool firstActivation, bool addedToHierarchy,

_settings.mirror.renderScale.changed += OnMirrorRenderScaleChanged;
_settings.mirror.antiAliasingLevel.changed += OnMirrorAntiAliasingLevelChanged;
_settings.mirror.useFakeMirrorBeta.changed += OnUseFakeMirrorChanged;

_trackingRig.activeCalibrationModeChanged += OnActiveCalibrationModeChanged;

Expand All @@ -136,7 +144,9 @@ protected override void DidActivate(bool firstActivation, bool addedToHierarchy,

if (!_mirror) return;

_container.InstantiateComponent<AutoResizeMirror>(_mirror.gameObject);
GameObject mirrorGameObject = _mirror.gameObject;
_container.InstantiateComponent<AutoResizeMirror>(mirrorGameObject);
mirrorGameObject.SetActive(!_settings.mirror.useFakeMirrorBeta);
}

if (firstActivation)
Expand Down Expand Up @@ -183,6 +193,7 @@ protected override void DidActivate(bool firstActivation, bool addedToHierarchy,
}

SetLoading(false);
ShowMirroredAvatar();
OnMirrorRenderScaleChanged(_settings.mirror.renderScale);
}

Expand All @@ -201,8 +212,11 @@ protected override void DidDeactivate(bool removedFromHierarchy, bool screenSyst

_settings.mirror.renderScale.changed -= OnMirrorRenderScaleChanged;
_settings.mirror.antiAliasingLevel.changed -= OnMirrorAntiAliasingLevelChanged;
_settings.mirror.useFakeMirrorBeta.changed -= OnUseFakeMirrorChanged;

_trackingRig.activeCalibrationModeChanged += OnActiveCalibrationModeChanged;

Destroy(_mirroredAvatarContainer);
}

#endregion
Expand All @@ -211,11 +225,57 @@ private void OnAvatarLoading(string filePath, string name)
{
_progressTitle.text = $"Loading {name}";
SetLoading(true);
DestroyMirroredAvatar();
}

private void OnAvatarChanged(SpawnedAvatar avatar)
{
SetLoading(false);
ShowMirroredAvatar();
}

private void ShowMirroredAvatar()
{
DestroyMirroredAvatar();

if (!_settings.mirror.useFakeMirrorBeta)
{
return;
}

SpawnedAvatar avatar = _avatarManager.currentlySpawnedAvatar;

if (avatar == null)
{
return;
}

_mirroredAvatarContainer = new GameObject("MirroredAvatarContainer");
_mirroredAvatarContainer.SetActive(false);

// mirrored at the line where the player platform usually ends (i.e. mirror plane is 0.75 m in front of the player)
Transform mirroredAvatarTransform = _mirroredAvatarContainer.transform;
mirroredAvatarTransform.SetPositionAndRotation(new Vector3(0, 0, 1.5f), Quaternion.identity);

GameObject gameObject = _avatarSpawner.SpawnBareAvatar(avatar.prefab, _mirroredAvatarContainer.transform);

TransformCopier transformCopier = _mirroredAvatarContainer.AddComponent<TransformCopier>();
transformCopier.root = avatar.transform.parent;
transformCopier.from = avatar.transform.GetComponentsInChildren<Transform>();
transformCopier.to = gameObject.transform.GetComponentsInChildren<Transform>();

foreach (Transform transform in transformCopier.to)
{
transform.gameObject.layer = AvatarLayers.kAlwaysVisible;
}

_mirroredAvatarContainer.SetActive(true);
gameObject.SetActive(true);
}

private void DestroyMirroredAvatar()
{
Destroy(_mirroredAvatarContainer);
}

private void OnAvatarLoadFailed(Exception exception)
Expand All @@ -235,6 +295,12 @@ private void OnMirrorAntiAliasingLevelChanged(int antiAliasingLevel)
UpdateMirrorRenderSettings(_settings.mirror.renderScale, antiAliasingLevel);
}

private void OnUseFakeMirrorChanged(bool value)
{
ShowMirroredAvatar();
_mirror.gameObject.SetActive(!value);
}

private void OnActiveCalibrationModeChanged(CalibrationMode calibrationMode)
{
NotifyPropertyChanged(nameof(isCalibrationMessageVisible));
Expand Down Expand Up @@ -274,5 +340,36 @@ protected override void UpdateOffset()
transform.localScale = new Vector3(width / 10, 1, height / 10);
}
}

// TODO: blend shapes and possibly other things
private class TransformCopier : MonoBehaviour
{
public Transform root;
public Transform[] from;
public Transform[] to;

protected void OnEnable()
{
Application.onBeforeRender += OnBeforeRender;
}

protected void OnDisable()
{
Application.onBeforeRender -= OnBeforeRender;
}

private void OnBeforeRender()
{
Vector3 scale = root.lossyScale;
transform.localScale = new Vector3(scale.x, scale.y, -scale.z); // mirrored across XY plane

foreach ((Transform from, Transform to) in from.Zip(to))
{
from.GetLocalPositionAndRotation(out Vector3 position, out Quaternion rotation);
to.SetLocalPositionAndRotation(position, rotation);
to.localScale = from.localScale;
}
}
}
}
}
1 change: 1 addition & 0 deletions Source/CustomAvatar/UI/Views/Settings.bsml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
</horizontal>
<increment-setting text="Render Scale" value="renderScale" bind-value="true" apply-on-change="true" min="0.5" max="2" increment="0.1" />
<list-setting text="Anti Aliasing" value="antiAliasingLevel" bind-value="true" apply-on-change="true" options="antiAliasingLevelOptions" formatter="AntiAliasingLevelFormatter" />
<toggle-setting text="Use Fake Mirror (beta)" value="useFakeMirror" bind-value="true" apply-on-change="true" hover-hint="Show a mirrored version of the current avatar rather than rendering a full mirror. Improves performance and allows the avatar to be closer to you, but may not accurately reflect all avatar features." />
<toggle-setting text="Show in non-VR cameras" value="renderInExternalCameras" bind-value="true" apply-on-change="true" hover-hint="Disable this setting to improve performance in this menu when multiple cameras are used (e.g. smooth camera or Camera2)." />
</settings-container>
</macro.as-host>
Expand Down
18 changes: 18 additions & 0 deletions Source/CustomAvatar/Utilities/EnumerableExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;

namespace CustomAvatar.Utilities
{
internal static class EnumerableExtensions
{
internal static IEnumerable<ValueTuple<TFirst, TSecond>> Zip<TFirst, TSecond>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second)
{
using IEnumerator<TFirst> e1 = first.GetEnumerator();
using IEnumerator<TSecond> e2 = second.GetEnumerator();
while (e1.MoveNext() && e2.MoveNext())
{
yield return (e1.Current, e2.Current);
}
}
}
}

0 comments on commit c217ed5

Please sign in to comment.