From 9433779641a87f25885ba4e6cf6a8315e01c1deb Mon Sep 17 00:00:00 2001 From: NotAKidoS <37721153+NotAKidoS@users.noreply.github.com> Date: Fri, 6 Sep 2024 00:57:39 -0500 Subject: [PATCH] Stickers: added lazy placement preview --- Stickers/Main.cs | 22 +++- Stickers/Patches.cs | 2 + Stickers/Stickers/StickerData.cs | 104 ++++++++++++++++-- Stickers/Stickers/StickerSystem.Main.cs | 11 +- .../Stickers/StickerSystem.PlayerCallbacks.cs | 2 +- .../StickerSystem.StickerLifecycle.cs | 35 +++++- 6 files changed, 157 insertions(+), 19 deletions(-) diff --git a/Stickers/Main.cs b/Stickers/Main.cs index 8847dac..4b223f4 100644 --- a/Stickers/Main.cs +++ b/Stickers/Main.cs @@ -1,4 +1,6 @@ -using ABI_RC.Core.Player; +using ABI_RC.Core; +using ABI_RC.Core.Player; +using ABI_RC.Core.Savior; using ABI_RC.Systems.InputManagement; using MelonLoader; using NAK.Stickers.Integrations; @@ -35,11 +37,19 @@ public class StickerMod : MelonMod if (StickerSystem.Instance == null) return; - if (StickerSystem.Instance.IsInStickerMode - && Input.mouseScrollDelta.y != 0f - && Cursor.lockState == CursorLockMode.Locked // prevent scrolling while in menus - && !CVRInputManager.Instance.zoom) // prevent scrolling while using scroll zoom - StickerSystem.Instance.SelectedStickerSlot += (int)Input.mouseScrollDelta.y; + if (!MetaPort.Instance.isUsingVr + && StickerSystem.Instance.IsInStickerMode) + { + if (Input.mouseScrollDelta.y != 0f + && Cursor.lockState == CursorLockMode.Locked // prevent scrolling while in menus + && !CVRInputManager.Instance.zoom) // prevent scrolling while using scroll zoom + { + StickerSystem.Instance.SelectedStickerSlot += (int)Input.mouseScrollDelta.y; + } + StickerSystem.Instance.PlaceStickerFromControllerRay(PlayerSetup.Instance.activeCam.transform, CVRHand.Left, true); + } + + StickerSystem.Instance.UpdateStickerPreview(); // flashy flash if (!ModSettings.Entry_UsePlaceBinding.Value) return; diff --git a/Stickers/Patches.cs b/Stickers/Patches.cs index 717ebb2..4fc6b1b 100644 --- a/Stickers/Patches.cs +++ b/Stickers/Patches.cs @@ -26,6 +26,8 @@ internal static class ControllerRayPatches if (!StickerSystem.Instance.IsInStickerMode) return; + StickerSystem.Instance.PlaceStickerFromControllerRay(__instance.rayDirectionTransform, __instance.hand, true); // preview + if (__instance._gripDown) StickerSystem.Instance.IsInStickerMode = false; if (__instance._hitUIInternal || !__instance._interactDown) return; diff --git a/Stickers/Stickers/StickerData.cs b/Stickers/Stickers/StickerData.cs index da33888..4b4aaa9 100644 --- a/Stickers/Stickers/StickerData.cs +++ b/Stickers/Stickers/StickerData.cs @@ -16,7 +16,6 @@ namespace NAK.Stickers private Vector3 _lastPlacedPosition = Vector3.zero; - private readonly DecalType _decal; private readonly DecalSpawner[] _decalSpawners; private readonly Guid[] _textureHashes; @@ -27,7 +26,6 @@ namespace NAK.Stickers { PlayerId = playerId; - _decal = ScriptableObject.CreateInstance(); _decalSpawners = new DecalSpawner[decalSpawnersCount]; _materials = new Material[decalSpawnersCount]; _textureHashes = new Guid[decalSpawnersCount]; @@ -35,14 +33,14 @@ namespace NAK.Stickers for (int i = 0; i < decalSpawnersCount; i++) { _materials[i] = new Material(StickerMod.DecalSimpleShader); - _decal.decalSettings = new DecalSpawner.InitData + DecalSpawner.InitData decalSettings = new() { material = _materials[i], useShaderReplacement = false, inheritMaterialProperties = false, inheritMaterialPropertyBlock = false, }; - _decalSpawners[i] = DecalManager.GetSpawner(_decal.decalSettings, 4096, 1024); + _decalSpawners[i] = DecalManager.GetSpawner(decalSettings, 4096, 1024); } _audioSource = new GameObject("StickerAudioSource").AddComponent(); @@ -54,7 +52,21 @@ namespace NAK.Stickers _audioSource.maxDistance = 5f; _audioSource.minDistance = 1f; _audioSource.outputAudioMixerGroup = RootLogic.Instance.propSfx; // props are close enough to stickers - if (PlayerId == StickerSystem.PlayerLocalId) Object.DontDestroyOnLoad(_audioSource.gameObject); // keep audio source through world transitions + + // this is a hack so judge and fuck off lol + if (PlayerId == StickerSystem.PlayerLocalId) + { + Object.DontDestroyOnLoad(_audioSource.gameObject); // keep audio source through world transitions + + _previewMaterial = new Material(StickerMod.DecalSimpleShader); + _previewDecalSpawner = DecalManager.GetSpawner(new DecalSpawner.InitData + { + material = _previewMaterial, // default material + useShaderReplacement = false, + inheritMaterialProperties = false, + inheritMaterialPropertyBlock = false, + }, 4096, 1024); + } } public Guid GetTextureHash(int spawnerIndex = 0) @@ -97,9 +109,13 @@ namespace NAK.Stickers Material material = _materials[spawnerIndex]; - // Destroy the previous texture to avoid memory leaks + // destroy the previous texture to avoid memory leaks if (material.mainTexture != null) Object.Destroy(material.mainTexture); material.mainTexture = texture; + + // update the preview as well i guess cause lame + if (_previewMaterial != null && _previewSpawnerIndex != spawnerIndex) + _previewMaterial.mainTexture = texture; } public void Place(RaycastHit hit, Vector3 forwardDirection, Vector3 upDirection, int spawnerIndex = 0) @@ -166,8 +182,15 @@ namespace NAK.Stickers if (_materials[i].mainTexture != null) Object.Destroy(_materials[i].mainTexture); Object.Destroy(_materials[i]); } - - Object.Destroy(_decal); + + if (_audioSource != null) Object.Destroy(_audioSource.gameObject); + if (_previewDecalSpawner != null) // local player only + { + _previewDecalSpawner.Release(); + _previewDecalSpawner.staticGroups.Clear(); + _previewDecalSpawner.movableGroups.Clear(); + Object.Destroy(_previewMaterial); + } } public void PlayAudio() @@ -201,5 +224,70 @@ namespace NAK.Stickers material.color = color; } } + + #region Sticker Preview + + private const float FLASH_FREQUENCY = 2f; + private readonly DecalSpawner _previewDecalSpawner; + private readonly Material _previewMaterial; + private int _previewSpawnerIndex = -1; + private float _flashTime; + + public void PlacePreview(RaycastHit hit, Vector3 forwardDirection, Vector3 upDirection, int spawnerIndex = 0) + { + if (spawnerIndex < 0 || spawnerIndex >= _decalSpawners.Length) + { + StickerMod.Logger.Warning("Invalid spawner index for preview!"); + return; + } + + if (_previewDecalSpawner == null) + return; // uh fuck + + // clear previous + ClearPreview(); + + // place at hit pos + Transform rootObject = null; + GameObject hitGO = hit.transform.gameObject; + if (hitGO.scene.buildIndex == 4 || hitGO.TryGetComponent(out Animator _) || hitGO.GetComponentInParent() != null) + rootObject = hitGO.transform; + + Vector3 position = hit.point; + _previewDecalSpawner.AddDecal(position, + Quaternion.LookRotation(forwardDirection, upDirection), + hitGO, + DECAL_SIZE, DECAL_SIZE, 1f, 1f, 0f, + rootObject); + } + + public void UpdatePreview(int spawnerIndex) + { + if (_previewSpawnerIndex != spawnerIndex) + { + _previewSpawnerIndex = spawnerIndex; // update the preview image + _previewMaterial.mainTexture = _materials[spawnerIndex].mainTexture; + } + + _flashTime += Time.deltaTime; + float baseAlpha = (Mathf.Sin(_flashTime * 2f * Mathf.PI / FLASH_FREQUENCY) + 1f) / 2f; + float alpha = Mathf.Lerp(0.5f, 0.8f, baseAlpha); // 50% to 80% alpha + + Color color = _previewMaterial.color; + color.a = alpha; + _previewMaterial.color = color; + } + + public void ClearPreview() + { + if (_previewDecalSpawner == null) + return; + + _previewDecalSpawner.Release(); + _previewDecalSpawner.staticGroups.Clear(); + _previewDecalSpawner.movableGroups.Clear(); + } + + #endregion Sticker Preview } } \ No newline at end of file diff --git a/Stickers/Stickers/StickerSystem.Main.cs b/Stickers/Stickers/StickerSystem.Main.cs index a58e724..8bdc480 100644 --- a/Stickers/Stickers/StickerSystem.Main.cs +++ b/Stickers/Stickers/StickerSystem.Main.cs @@ -83,11 +83,18 @@ public partial class StickerSystem set { _isInStickerMode = value; - if (_isInStickerMode) CohtmlHud.Instance.SelectPropToSpawn( + if (_isInStickerMode) + { + CohtmlHud.Instance.SelectPropToSpawn( StickerCache.GetCohtmlResourcesPath(SelectedStickerName), Path.GetFileNameWithoutExtension(SelectedStickerName), "Sticker selected for stickering:"); - else CohtmlHud.Instance.ClearPropToSpawn(); + } + else + { + CohtmlHud.Instance.ClearPropToSpawn(); + ClearStickerPreview(); + } } } diff --git a/Stickers/Stickers/StickerSystem.PlayerCallbacks.cs b/Stickers/Stickers/StickerSystem.PlayerCallbacks.cs index 89394c9..e8ab380 100644 --- a/Stickers/Stickers/StickerSystem.PlayerCallbacks.cs +++ b/Stickers/Stickers/StickerSystem.PlayerCallbacks.cs @@ -31,7 +31,7 @@ public partial class StickerSystem for (int i = 0; i < _playerStickers.Values.Count; i++) { StickerData stickerData = _playerStickers.Values.ElementAt(i); - + if (stickerData.DeathTime > 0f) { if (currentTime < stickerData.DeathTime) diff --git a/Stickers/Stickers/StickerSystem.StickerLifecycle.cs b/Stickers/Stickers/StickerSystem.StickerLifecycle.cs index ee2d76e..2977dff 100644 --- a/Stickers/Stickers/StickerSystem.StickerLifecycle.cs +++ b/Stickers/Stickers/StickerSystem.StickerLifecycle.cs @@ -20,7 +20,7 @@ public partial class StickerSystem return stickerData; } - public void PlaceStickerFromControllerRay(Transform transform, CVRHand hand = CVRHand.Left) + public void PlaceStickerFromControllerRay(Transform transform, CVRHand hand = CVRHand.Left, bool isPreview = false) { Vector3 controllerForward = transform.forward; Vector3 controllerUp = transform.up; @@ -37,6 +37,12 @@ public partial class StickerSystem ? Vector3.Slerp(controllerUp, playerUp, 0.99f) : controllerUp; + if (isPreview) + { + PlaceStickerPreview(transform.position, controllerForward, targetUp); + return; + } + if (!PlaceStickerSelf(transform.position, transform.forward, targetUp)) return; @@ -53,7 +59,7 @@ public partial class StickerSystem return true; } - private bool AttemptPlaceSticker(string playerId, Vector3 position, Vector3 forward, Vector3 up, bool alignWithNormal = true, int stickerSlot = 0) + private bool AttemptPlaceSticker(string playerId, Vector3 position, Vector3 forward, Vector3 up, bool alignWithNormal = true, int stickerSlot = 0, bool isPreview = false) { StickerData stickerData = GetOrCreateStickerData(playerId); if (Time.time - stickerData.LastPlacedTime < StickerCooldown) @@ -69,6 +75,12 @@ public partial class StickerSystem if (hit.transform.gameObject.name.StartsWith("[NoSticker]")) return false; + if (isPreview) + { + stickerData.PlacePreview(hit, alignWithNormal ? -hit.normal : forward, up, stickerSlot); + return true; + } + stickerData.Place(hit, alignWithNormal ? -hit.normal : forward, up, stickerSlot); stickerData.PlayAudio(); return true; @@ -154,6 +166,25 @@ public partial class StickerSystem _playerStickers.Clear(); _playerStickers[PlayerLocalId] = localStickerData; } + + public void PlaceStickerPreview(Vector3 position, Vector3 forward, Vector3 up) + { + AttemptPlaceSticker(PlayerLocalId, position, forward, up, true, SelectedStickerSlot, true); + } + + public void UpdateStickerPreview() + { + if (!IsInStickerMode) return; + + StickerData localStickerData = GetOrCreateStickerData(PlayerLocalId); + localStickerData.UpdatePreview(SelectedStickerSlot); + } + + public void ClearStickerPreview() + { + StickerData localStickerData = GetOrCreateStickerData(PlayerLocalId); + localStickerData.ClearPreview(); + } #endregion Sticker Lifecycle }