Stickers: added lazy placement preview

This commit is contained in:
NotAKidoS 2024-09-06 00:57:39 -05:00
parent d409bf1743
commit 9433779641
6 changed files with 157 additions and 19 deletions

View file

@ -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 ABI_RC.Systems.InputManagement;
using MelonLoader; using MelonLoader;
using NAK.Stickers.Integrations; using NAK.Stickers.Integrations;
@ -35,11 +37,19 @@ public class StickerMod : MelonMod
if (StickerSystem.Instance == null) if (StickerSystem.Instance == null)
return; return;
if (StickerSystem.Instance.IsInStickerMode if (!MetaPort.Instance.isUsingVr
&& Input.mouseScrollDelta.y != 0f && StickerSystem.Instance.IsInStickerMode)
&& Cursor.lockState == CursorLockMode.Locked // prevent scrolling while in menus {
&& !CVRInputManager.Instance.zoom) // prevent scrolling while using scroll zoom if (Input.mouseScrollDelta.y != 0f
StickerSystem.Instance.SelectedStickerSlot += (int)Input.mouseScrollDelta.y; && 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) if (!ModSettings.Entry_UsePlaceBinding.Value)
return; return;

View file

@ -26,6 +26,8 @@ internal static class ControllerRayPatches
if (!StickerSystem.Instance.IsInStickerMode) if (!StickerSystem.Instance.IsInStickerMode)
return; return;
StickerSystem.Instance.PlaceStickerFromControllerRay(__instance.rayDirectionTransform, __instance.hand, true); // preview
if (__instance._gripDown) StickerSystem.Instance.IsInStickerMode = false; if (__instance._gripDown) StickerSystem.Instance.IsInStickerMode = false;
if (__instance._hitUIInternal || !__instance._interactDown) if (__instance._hitUIInternal || !__instance._interactDown)
return; return;

View file

@ -16,7 +16,6 @@ namespace NAK.Stickers
private Vector3 _lastPlacedPosition = Vector3.zero; private Vector3 _lastPlacedPosition = Vector3.zero;
private readonly DecalType _decal;
private readonly DecalSpawner[] _decalSpawners; private readonly DecalSpawner[] _decalSpawners;
private readonly Guid[] _textureHashes; private readonly Guid[] _textureHashes;
@ -27,7 +26,6 @@ namespace NAK.Stickers
{ {
PlayerId = playerId; PlayerId = playerId;
_decal = ScriptableObject.CreateInstance<DecalType>();
_decalSpawners = new DecalSpawner[decalSpawnersCount]; _decalSpawners = new DecalSpawner[decalSpawnersCount];
_materials = new Material[decalSpawnersCount]; _materials = new Material[decalSpawnersCount];
_textureHashes = new Guid[decalSpawnersCount]; _textureHashes = new Guid[decalSpawnersCount];
@ -35,14 +33,14 @@ namespace NAK.Stickers
for (int i = 0; i < decalSpawnersCount; i++) for (int i = 0; i < decalSpawnersCount; i++)
{ {
_materials[i] = new Material(StickerMod.DecalSimpleShader); _materials[i] = new Material(StickerMod.DecalSimpleShader);
_decal.decalSettings = new DecalSpawner.InitData DecalSpawner.InitData decalSettings = new()
{ {
material = _materials[i], material = _materials[i],
useShaderReplacement = false, useShaderReplacement = false,
inheritMaterialProperties = false, inheritMaterialProperties = false,
inheritMaterialPropertyBlock = false, inheritMaterialPropertyBlock = false,
}; };
_decalSpawners[i] = DecalManager.GetSpawner(_decal.decalSettings, 4096, 1024); _decalSpawners[i] = DecalManager.GetSpawner(decalSettings, 4096, 1024);
} }
_audioSource = new GameObject("StickerAudioSource").AddComponent<AudioSource>(); _audioSource = new GameObject("StickerAudioSource").AddComponent<AudioSource>();
@ -54,7 +52,21 @@ namespace NAK.Stickers
_audioSource.maxDistance = 5f; _audioSource.maxDistance = 5f;
_audioSource.minDistance = 1f; _audioSource.minDistance = 1f;
_audioSource.outputAudioMixerGroup = RootLogic.Instance.propSfx; // props are close enough to stickers _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) public Guid GetTextureHash(int spawnerIndex = 0)
@ -97,9 +109,13 @@ namespace NAK.Stickers
Material material = _materials[spawnerIndex]; 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); if (material.mainTexture != null) Object.Destroy(material.mainTexture);
material.mainTexture = texture; 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) 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); if (_materials[i].mainTexture != null) Object.Destroy(_materials[i].mainTexture);
Object.Destroy(_materials[i]); 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() public void PlayAudio()
@ -201,5 +224,70 @@ namespace NAK.Stickers
material.color = color; 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<Rigidbody>() != 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
} }
} }

View file

@ -83,11 +83,18 @@ public partial class StickerSystem
set set
{ {
_isInStickerMode = value; _isInStickerMode = value;
if (_isInStickerMode) CohtmlHud.Instance.SelectPropToSpawn( if (_isInStickerMode)
{
CohtmlHud.Instance.SelectPropToSpawn(
StickerCache.GetCohtmlResourcesPath(SelectedStickerName), StickerCache.GetCohtmlResourcesPath(SelectedStickerName),
Path.GetFileNameWithoutExtension(SelectedStickerName), Path.GetFileNameWithoutExtension(SelectedStickerName),
"Sticker selected for stickering:"); "Sticker selected for stickering:");
else CohtmlHud.Instance.ClearPropToSpawn(); }
else
{
CohtmlHud.Instance.ClearPropToSpawn();
ClearStickerPreview();
}
} }
} }

View file

@ -31,7 +31,7 @@ public partial class StickerSystem
for (int i = 0; i < _playerStickers.Values.Count; i++) for (int i = 0; i < _playerStickers.Values.Count; i++)
{ {
StickerData stickerData = _playerStickers.Values.ElementAt(i); StickerData stickerData = _playerStickers.Values.ElementAt(i);
if (stickerData.DeathTime > 0f) if (stickerData.DeathTime > 0f)
{ {
if (currentTime < stickerData.DeathTime) if (currentTime < stickerData.DeathTime)

View file

@ -20,7 +20,7 @@ public partial class StickerSystem
return stickerData; 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 controllerForward = transform.forward;
Vector3 controllerUp = transform.up; Vector3 controllerUp = transform.up;
@ -37,6 +37,12 @@ public partial class StickerSystem
? Vector3.Slerp(controllerUp, playerUp, 0.99f) ? Vector3.Slerp(controllerUp, playerUp, 0.99f)
: controllerUp; : controllerUp;
if (isPreview)
{
PlaceStickerPreview(transform.position, controllerForward, targetUp);
return;
}
if (!PlaceStickerSelf(transform.position, transform.forward, targetUp)) if (!PlaceStickerSelf(transform.position, transform.forward, targetUp))
return; return;
@ -53,7 +59,7 @@ public partial class StickerSystem
return true; 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); StickerData stickerData = GetOrCreateStickerData(playerId);
if (Time.time - stickerData.LastPlacedTime < StickerCooldown) if (Time.time - stickerData.LastPlacedTime < StickerCooldown)
@ -69,6 +75,12 @@ public partial class StickerSystem
if (hit.transform.gameObject.name.StartsWith("[NoSticker]")) if (hit.transform.gameObject.name.StartsWith("[NoSticker]"))
return false; 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.Place(hit, alignWithNormal ? -hit.normal : forward, up, stickerSlot);
stickerData.PlayAudio(); stickerData.PlayAudio();
return true; return true;
@ -154,6 +166,25 @@ public partial class StickerSystem
_playerStickers.Clear(); _playerStickers.Clear();
_playerStickers[PlayerLocalId] = localStickerData; _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 #endregion Sticker Lifecycle
} }