Files
2026-06-01 17:19:55 +02:00

1022 lines
29 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR;
namespace BlockSpace.Voxels
{
[DefaultExecutionOrder(-100)]
[DisallowMultipleComponent]
public sealed class VRGroupSelector : MonoBehaviour
{
private sealed class GrabbedGroupState
{
public VoxelGroup group;
public Vector3 startWorldPosition;
public byte startPackedRotation;
public bool colliderWasEnabled;
public float lastPreviewSentAt = float.NegativeInfinity;
}
private struct GroupMergeAnimationState
{
public VoxelGroup group;
public Vector3 startTransformPosition;
public Vector3 startBoundsCenter;
public Vector3 burstStartCenter;
public Vector3 burstPeakCenter;
}
private const string GroupTickClipResourcePath = "Click";
private const string GroupImpactClipResourcePath = "Thock";
[SerializeField]
private XRNode rightHand = XRNode.RightHand;
[SerializeField]
private XRNode leftHand = XRNode.LeftHand;
[SerializeField]
private Transform selectorOrigin;
[SerializeField]
private float selectionRadius = 0.1f;
[SerializeField]
private KeyCode keyboardSelectKey = KeyCode.G;
[SerializeField]
private KeyCode keyboardMergeKey = KeyCode.F;
[SerializeField]
private KeyCode keyboardDuplicateKey = KeyCode.D;
[SerializeField]
private KeyCode keyboardPaintSelectionKey = KeyCode.Q;
[SerializeField]
private KeyCode keyboardInventoryKey = KeyCode.I;
[SerializeField]
private KeyCode keyboardDeleteKey = KeyCode.Delete;
[SerializeField]
private float gripGrabThreshold = 0.85f;
[SerializeField]
private float deleteTriggerThreshold = 0.7f;
[SerializeField]
private float undoChordWindow = 0.12f;
[SerializeField]
private float grabPositionSnap = 0.125f;
[SerializeField]
private float grabRotationSnapDegrees = 90f;
[SerializeField]
private float movePreviewSendInterval = 0.05f;
[Header("Grouping Audio")]
[SerializeField]
private AudioClip groupingClickClip;
[SerializeField]
private AudioClip groupingThockClip;
[SerializeField]
private VoxelGroupManager groupManager;
private readonly HashSet<VoxelGroup> selectedGroups = new HashSet<VoxelGroup>();
private readonly List<VoxelGroup> selectionBuffer = new List<VoxelGroup>();
private readonly List<VoxelGroup> cleanupBuffer = new List<VoxelGroup>();
private readonly List<GrabbedGroupState> grabbedGroupStates = new List<GrabbedGroupState>();
private InputDevice rightHandDevice;
private InputDevice leftHandDevice;
private bool mergeButtonPrevious;
private bool duplicateButtonPrevious;
private bool thumbstickClickPrevious;
private bool inventoryButtonPrevious;
private bool selectionHeld;
private bool selectionInputHeld;
private bool undoChordActive;
private bool suppressRightHandActionsUntilNeutral;
private bool interactionSuppressed;
private bool selectModeLatchedThisHold;
private bool gripHeldPrevious;
private bool triggerHeldPrevious;
private float lastGripPressTime = float.NegativeInfinity;
private float lastTriggerPressTime = float.NegativeInfinity;
private Transform trackingOrigin;
private bool groupingAnimationActive;
private bool selectionGrabActive;
private Vector3 grabStartReferencePosition;
private Quaternion grabStartReferenceRotation;
private Vector3 grabStartSelectionCenter;
private Vector3 grabStartSelectionOffset;
public static VRGroupSelector Instance { get; private set; }
public bool IsSelecting => selectionHeld;
public bool HasSelection => selectedGroups.Count > 0;
public float SelectionRadius => selectionRadius;
public bool BlocksRightHandActions
{
get
{
if (!undoChordActive && !suppressRightHandActionsUntilNeutral && !groupingAnimationActive)
{
return selectionGrabActive;
}
return true;
}
}
public void SetInteractionSuppressed(bool suppressed)
{
if (interactionSuppressed == suppressed)
{
return;
}
interactionSuppressed = suppressed;
if (interactionSuppressed)
{
if (HasSelection)
{
ClearSelectionInternal(suppressUntilNeutral: false);
}
selectionHeld = false;
selectionInputHeld = false;
undoChordActive = false;
suppressRightHandActionsUntilNeutral = false;
selectModeLatchedThisHold = false;
EndSelectionGrab();
}
}
public void Configure(VoxelGroupManager manager, Transform origin, Transform handAnchor)
{
if (manager != null)
{
groupManager = manager;
}
trackingOrigin = origin;
if (handAnchor != null)
{
selectorOrigin = handAnchor;
}
}
public bool TryGetSelectionCenter(out Vector3 worldPosition)
{
return TryGetSelectorPosition(out worldPosition);
}
public void SetSelectionRadius(float radius)
{
selectionRadius = Mathf.Max(0.001f, radius);
}
public void ClearSelectionForToolUse()
{
if (HasSelection)
{
ClearSelectionInternal(suppressUntilNeutral: false);
}
}
private void Awake()
{
if (Instance != null && Instance != this)
{
Debug.LogError("Multiple VRGroupSelector instances found.", this);
return;
}
Instance = this;
if (groupManager == null)
{
groupManager = ((VoxelGroupManager.Instance != null) ? VoxelGroupManager.Instance : UnityEngine.Object.FindObjectOfType<VoxelGroupManager>());
}
selectionRadius = Mathf.Max(0.001f, selectionRadius);
gripGrabThreshold = Mathf.Clamp(gripGrabThreshold, 0.5f, 1f);
undoChordWindow = Mathf.Max(0.01f, undoChordWindow);
grabPositionSnap = Mathf.Max(0.001f, grabPositionSnap);
grabRotationSnapDegrees = Mathf.Max(1f, grabRotationSnapDegrees);
EnsureGroupingClipsAssigned();
}
private void OnEnable()
{
rightHandDevice = InputDevices.GetDeviceAtXRNode(rightHand);
leftHandDevice = InputDevices.GetDeviceAtXRNode(leftHand);
}
private void OnDestroy()
{
if (Instance == this)
{
Instance = null;
}
}
private void Update()
{
if (groupManager == null)
{
return;
}
if (!groupManager.CreatorMode || groupManager.EditingLocked)
{
if (HasSelection)
{
ClearSelectionInternal(suppressUntilNeutral: false);
}
selectionHeld = false;
selectionInputHeld = false;
undoChordActive = false;
suppressRightHandActionsUntilNeutral = false;
selectModeLatchedThisHold = false;
EndSelectionGrab();
return;
}
VRVoxelBrush instance = VRVoxelBrush.Instance;
bool flag = instance == null || instance.UsingXRInput;
if (!rightHandDevice.isValid)
{
rightHandDevice = InputDevices.GetDeviceAtXRNode(rightHand);
}
if (!leftHandDevice.isValid)
{
leftHandDevice = InputDevices.GetDeviceAtXRNode(leftHand);
}
if (groupingAnimationActive)
{
return;
}
if (interactionSuppressed)
{
bool flag2 = ((!flag) ? (Input.GetMouseButton(2) || Input.GetKey(keyboardSelectKey)) : (ReadBool(rightHandDevice, CommonUsages.primary2DAxisClick) || Input.GetKey(keyboardSelectKey)));
bool flag3 = (flag ? ReadGrabHeld(rightHandDevice) : Input.GetMouseButton(1));
bool flag4 = flag && ReadFloat(rightHandDevice, CommonUsages.trigger) >= deleteTriggerThreshold;
bool flag5 = flag && ReadBool(rightHandDevice, CommonUsages.primaryButton);
bool flag6 = flag && ReadBool(leftHandDevice, CommonUsages.primaryButton);
bool flag7 = flag && ReadBool(rightHandDevice, CommonUsages.secondaryButton);
selectionHeld = false;
selectionInputHeld = false;
thumbstickClickPrevious = flag2;
mergeButtonPrevious = flag5;
duplicateButtonPrevious = flag6;
inventoryButtonPrevious = flag7;
gripHeldPrevious = flag3;
triggerHeldPrevious = flag4;
if (!flag3 && !flag4)
{
undoChordActive = false;
suppressRightHandActionsUntilNeutral = false;
}
return;
}
CleanupDestroyedSelections();
Vector3 worldPosition;
bool num = TryGetSelectorPosition(out worldPosition);
selectionBuffer.Clear();
if (num)
{
selectionBuffer.AddRange(groupManager.GetGroupsInRadius(worldPosition, selectionRadius));
}
bool flag8 = ((!flag) ? (Input.GetMouseButton(2) || Input.GetKey(keyboardSelectKey)) : (ReadBool(rightHandDevice, CommonUsages.primary2DAxisClick) || Input.GetKey(keyboardSelectKey)));
bool flag9 = (flag ? ReadGrabHeld(rightHandDevice) : Input.GetMouseButton(1));
bool flag10 = flag && ReadFloat(rightHandDevice, CommonUsages.trigger) >= deleteTriggerThreshold;
bool flag11 = flag && ReadBool(rightHandDevice, CommonUsages.primaryButton);
bool flag12 = flag && ReadBool(leftHandDevice, CommonUsages.primaryButton);
bool flag13 = flag && ReadBool(rightHandDevice, CommonUsages.secondaryButton);
bool flag14 = Input.GetKeyDown(keyboardDuplicateKey) && (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl));
if (flag9 && !gripHeldPrevious)
{
lastGripPressTime = Time.unscaledTime;
}
if (flag10 && !triggerHeldPrevious)
{
lastTriggerPressTime = Time.unscaledTime;
}
if (flag8 && !thumbstickClickPrevious)
{
selectModeLatchedThisHold = false;
}
if (!flag8 && thumbstickClickPrevious)
{
selectModeLatchedThisHold = false;
}
if (flag8 && selectionBuffer.Count > 0)
{
selectModeLatchedThisHold = true;
}
selectionInputHeld = flag8 && selectModeLatchedThisHold;
if (!undoChordActive && flag9 && flag10)
{
float num2 = Mathf.Max(lastGripPressTime, lastTriggerPressTime);
if (num2 > float.NegativeInfinity && Mathf.Abs(lastGripPressTime - lastTriggerPressTime) <= undoChordWindow && Time.unscaledTime - num2 <= undoChordWindow)
{
undoChordActive = true;
selectionHeld = false;
EndSelectionGrab();
groupManager.UndoLastOperation();
}
}
if (!undoChordActive && !suppressRightHandActionsUntilNeutral && HasSelection && ((flag12 && !duplicateButtonPrevious) || flag14))
{
DuplicateSelectedGroups();
}
if (!undoChordActive && HasSelection && flag9 && !flag10)
{
if (!selectionGrabActive)
{
BeginSelectionGrab();
}
UpdateSelectionGrab();
}
else if (selectionGrabActive)
{
EndSelectionGrab();
}
if (!selectionInputHeld && !flag9 && !flag10 && !flag11 && !flag12 && !flag13)
{
undoChordActive = false;
suppressRightHandActionsUntilNeutral = false;
}
selectionHeld = selectionInputHeld && !undoChordActive && !suppressRightHandActionsUntilNeutral && !selectionGrabActive;
if (selectionHeld)
{
for (int i = 0; i < selectionBuffer.Count; i++)
{
VoxelGroup voxelGroup = selectionBuffer[i];
if (!(voxelGroup == null) && selectedGroups.Add(voxelGroup))
{
voxelGroup.SetSelected(selected: true);
VRHaptics.Pulse(rightHandDevice, 0.18f, 0.035f);
}
}
}
if (!undoChordActive && !suppressRightHandActionsUntilNeutral && ((flag11 && !mergeButtonPrevious) || Input.GetKeyDown(keyboardMergeKey)) && HasSelection)
{
BeginMergeSelectedGroups();
suppressRightHandActionsUntilNeutral = true;
}
if (!undoChordActive && HasSelection && flag13 && !inventoryButtonPrevious)
{
PaintSelectedGroups();
}
if (!undoChordActive && HasSelection && Input.GetKeyDown(keyboardPaintSelectionKey))
{
PaintSelectedGroups();
}
if (!undoChordActive && Input.GetKeyDown(keyboardInventoryKey))
{
SendSelectedGroupsToInventory();
}
if (!undoChordActive && Input.GetKeyDown(keyboardDeleteKey) && HasSelection)
{
DeleteSelectedGroups();
suppressRightHandActionsUntilNeutral = true;
}
mergeButtonPrevious = flag11;
duplicateButtonPrevious = flag12;
thumbstickClickPrevious = flag8;
inventoryButtonPrevious = flag13;
gripHeldPrevious = flag9;
triggerHeldPrevious = flag10;
}
public void ClearSelection()
{
ClearSelectionInternal(suppressUntilNeutral: true);
}
private void ClearSelectionInternal(bool suppressUntilNeutral)
{
cleanupBuffer.Clear();
cleanupBuffer.AddRange(selectedGroups);
for (int i = 0; i < cleanupBuffer.Count; i++)
{
VoxelGroup voxelGroup = cleanupBuffer[i];
if (voxelGroup != null)
{
voxelGroup.SetSelected(selected: false);
}
}
selectedGroups.Clear();
cleanupBuffer.Clear();
selectionHeld = false;
selectionInputHeld = false;
if (suppressUntilNeutral)
{
suppressRightHandActionsUntilNeutral = true;
}
}
private void BeginMergeSelectedGroups()
{
CleanupDestroyedSelections();
if (selectedGroups.Count >= 2)
{
selectionBuffer.Clear();
selectionBuffer.AddRange(selectedGroups);
StartCoroutine(AnimateAndMergeSelectedGroups(new List<VoxelGroup>(selectionBuffer)));
selectionBuffer.Clear();
}
}
private void DuplicateSelectedGroups()
{
CleanupDestroyedSelections();
if (selectedGroups.Count == 0)
{
return;
}
selectionBuffer.Clear();
selectionBuffer.AddRange(selectedGroups);
ClearSelectionInternal(suppressUntilNeutral: false);
for (int i = 0; i < selectionBuffer.Count; i++)
{
VoxelGroup voxelGroup = groupManager.DuplicateGroup(selectionBuffer[i], Vector3Int.zero);
if (!(voxelGroup == null))
{
voxelGroup.SetSelected(selected: true);
selectedGroups.Add(voxelGroup);
}
}
selectionBuffer.Clear();
VRHaptics.Pulse(leftHandDevice, 0.32f, 0.055f);
}
private void BeginSelectionGrab()
{
CleanupDestroyedSelections();
if (selectedGroups.Count == 0 || !TryGetGrabReferencePose(out grabStartReferencePosition, out grabStartReferenceRotation) || !TryGetSelectedBounds(out var bounds))
{
return;
}
grabbedGroupStates.Clear();
foreach (VoxelGroup selectedGroup in selectedGroups)
{
if (!(selectedGroup == null))
{
grabbedGroupStates.Add(new GrabbedGroupState
{
group = selectedGroup,
startWorldPosition = selectedGroup.transform.position,
startPackedRotation = selectedGroup.packedRotation,
colliderWasEnabled = (selectedGroup.meshCollider == null || selectedGroup.meshCollider.enabled)
});
groupManager.SetGroupColliderEnabled(selectedGroup, enabled: false);
groupManager.BroadcastMovePreview(selectedGroup);
}
}
if (grabbedGroupStates.Count != 0)
{
grabStartSelectionCenter = bounds.center;
grabStartSelectionOffset = grabStartSelectionCenter - grabStartReferencePosition;
selectionGrabActive = true;
selectionHeld = false;
selectionInputHeld = false;
}
}
private void UpdateSelectionGrab()
{
if (!selectionGrabActive || groupManager == null || !TryGetGrabReferencePose(out var worldPosition, out var worldRotation))
{
return;
}
float voxelSize = groupManager.VoxelSize;
float step = Mathf.Max(0.001f, grabPositionSnap);
Quaternion quaternion = SnapRotation(worldRotation * Quaternion.Inverse(grabStartReferenceRotation));
Vector3 vector = SnapVector(worldPosition + grabStartSelectionOffset, step);
for (int num = grabbedGroupStates.Count - 1; num >= 0; num--)
{
GrabbedGroupState grabbedGroupState = grabbedGroupStates[num];
if (grabbedGroupState == null || grabbedGroupState.group == null || !selectedGroups.Contains(grabbedGroupState.group))
{
if (grabbedGroupState != null && grabbedGroupState.group != null)
{
groupManager.SetGroupColliderEnabled(grabbedGroupState.group, grabbedGroupState.colliderWasEnabled);
groupManager.BroadcastMoveFinal(grabbedGroupState.group);
}
grabbedGroupStates.RemoveAt(num);
}
else
{
Vector3 vector2 = grabbedGroupState.startWorldPosition - grabStartSelectionCenter;
Vector3Int gridPosition = WorldToGrid(vector + quaternion * vector2, voxelSize);
byte rotation = PackSnappedRotation(quaternion * GridRotationUtility.ToQuaternion(grabbedGroupState.startPackedRotation));
if (groupManager.TransformGroup(grabbedGroupState.group, gridPosition, rotation, sendNetworkUpdate: false))
{
grabbedGroupState.group.SetSelected(selected: true);
if (Time.unscaledTime - grabbedGroupState.lastPreviewSentAt >= Mathf.Max(0.01f, movePreviewSendInterval))
{
groupManager.BroadcastMovePreview(grabbedGroupState.group);
grabbedGroupState.lastPreviewSentAt = Time.unscaledTime;
}
}
}
}
}
private void EndSelectionGrab()
{
if (!selectionGrabActive)
{
grabbedGroupStates.Clear();
return;
}
selectionGrabActive = false;
for (int i = 0; i < grabbedGroupStates.Count; i++)
{
GrabbedGroupState grabbedGroupState = grabbedGroupStates[i];
if (grabbedGroupState != null && !(grabbedGroupState.group == null))
{
groupManager.SetGroupColliderEnabled(grabbedGroupState.group, grabbedGroupState.colliderWasEnabled);
groupManager.BroadcastMoveFinal(grabbedGroupState.group);
}
}
grabbedGroupStates.Clear();
}
private void DeleteSelectedGroups()
{
CleanupDestroyedSelections();
if (selectedGroups.Count == 0)
{
return;
}
selectionBuffer.Clear();
selectionBuffer.AddRange(selectedGroups);
ClearSelectionInternal(suppressUntilNeutral: true);
for (int i = 0; i < selectionBuffer.Count; i++)
{
VoxelGroup voxelGroup = selectionBuffer[i];
if (voxelGroup != null)
{
groupManager.RemoveGroup(voxelGroup);
}
}
selectionBuffer.Clear();
}
private void SendSelectedGroupsToInventory()
{
CleanupDestroyedSelections();
if (selectedGroups.Count != 0)
{
SendSelectedGroupsToInventoryStub(selectedGroups.Count);
}
}
public List<VoxelGroup> GetSelectedGroupsSnapshot()
{
CleanupDestroyedSelections();
if (selectedGroups.Count == 0)
{
return new List<VoxelGroup>();
}
List<VoxelGroup> list = new List<VoxelGroup>(selectedGroups.Count);
foreach (VoxelGroup selectedGroup in selectedGroups)
{
if (selectedGroup != null)
{
list.Add(selectedGroup);
}
}
return list;
}
private void PaintSelectedGroups()
{
CleanupDestroyedSelections();
if (selectedGroups.Count == 0)
{
return;
}
VRVoxelBrush instance = VRVoxelBrush.Instance;
byte voxelValue = ((instance != null) ? instance.GetSelectedVoxelValue() : PackedVoxel.Create(16, VoxelMaterial.Lit));
selectionBuffer.Clear();
selectionBuffer.AddRange(selectedGroups);
for (int i = 0; i < selectionBuffer.Count; i++)
{
VoxelGroup voxelGroup = selectionBuffer[i];
if (voxelGroup != null && groupManager.PaintWholeGroup(voxelGroup, voxelValue))
{
VRHaptics.Pulse(rightHandDevice, 0.32f, 0.055f);
}
}
selectionBuffer.Clear();
}
private void CleanupDestroyedSelections()
{
cleanupBuffer.Clear();
foreach (VoxelGroup selectedGroup in selectedGroups)
{
if (selectedGroup == null)
{
cleanupBuffer.Add(selectedGroup);
}
}
for (int i = 0; i < cleanupBuffer.Count; i++)
{
selectedGroups.Remove(cleanupBuffer[i]);
}
cleanupBuffer.Clear();
}
private bool TryGetSelectorPosition(out Vector3 worldPosition)
{
VRVoxelBrush instance = VRVoxelBrush.Instance;
if (instance != null && !instance.UsingXRInput && instance.TryGetDesktopSelectionPoint(out worldPosition))
{
return true;
}
if (selectorOrigin != null)
{
worldPosition = selectorOrigin.position;
return true;
}
if (rightHandDevice.isValid && rightHandDevice.TryGetFeatureValue(CommonUsages.devicePosition, out var value) && rightHandDevice.TryGetFeatureValue(CommonUsages.deviceRotation, out var value2))
{
if (trackingOrigin != null)
{
worldPosition = trackingOrigin.TransformPoint(value);
_ = trackingOrigin.rotation * value2;
}
else
{
worldPosition = value;
}
return true;
}
worldPosition = base.transform.position;
return true;
}
private bool TryGetGrabReferencePose(out Vector3 worldPosition, out Quaternion worldRotation)
{
VRVoxelBrush instance = VRVoxelBrush.Instance;
if (instance != null && !instance.UsingXRInput)
{
Camera main = Camera.main;
if (main != null)
{
worldPosition = main.transform.position;
worldRotation = main.transform.rotation;
return true;
}
}
if (selectorOrigin != null)
{
worldPosition = selectorOrigin.position;
worldRotation = selectorOrigin.rotation;
return true;
}
if (rightHandDevice.isValid && rightHandDevice.TryGetFeatureValue(CommonUsages.devicePosition, out var value) && rightHandDevice.TryGetFeatureValue(CommonUsages.deviceRotation, out var value2))
{
if (trackingOrigin != null)
{
worldPosition = trackingOrigin.TransformPoint(value);
worldRotation = trackingOrigin.rotation * value2;
}
else
{
worldPosition = value;
worldRotation = value2;
}
return true;
}
worldPosition = base.transform.position;
worldRotation = base.transform.rotation;
return true;
}
private bool TryGetSelectedBounds(out Bounds bounds)
{
CleanupDestroyedSelections();
bounds = default(Bounds);
bool flag = false;
foreach (VoxelGroup selectedGroup in selectedGroups)
{
if (!(selectedGroup == null))
{
if (!flag)
{
bounds = selectedGroup.GetWorldBounds();
flag = true;
}
else
{
bounds.Encapsulate(selectedGroup.GetWorldBounds());
}
}
}
return flag;
}
private Vector3 SnapVector(Vector3 value, float step)
{
return new Vector3(Mathf.Round(value.x / step) * step, Mathf.Round(value.y / step) * step, Mathf.Round(value.z / step) * step);
}
private Quaternion SnapRotation(Quaternion rotation)
{
Vector3 eulerAngles = rotation.eulerAngles;
return Quaternion.Euler(SnapAngle(eulerAngles.x), SnapAngle(eulerAngles.y), SnapAngle(eulerAngles.z));
}
private float SnapAngle(float angle)
{
return Mathf.Round(NormalizeSignedAngle(angle) / grabRotationSnapDegrees) * grabRotationSnapDegrees;
}
private static float NormalizeSignedAngle(float angle)
{
angle %= 360f;
if (angle > 180f)
{
angle -= 360f;
}
else if (angle < -180f)
{
angle += 360f;
}
return angle;
}
private static Vector3Int WorldToGrid(Vector3 worldPosition, float voxelSize)
{
return new Vector3Int(Mathf.RoundToInt(worldPosition.x / voxelSize), Mathf.RoundToInt(worldPosition.y / voxelSize), Mathf.RoundToInt(worldPosition.z / voxelSize));
}
private static byte PackSnappedRotation(Quaternion rotation)
{
Vector3 eulerAngles = rotation.eulerAngles;
return GridRotationUtility.Pack(new Vector3Int(AngleToQuarterTurns(eulerAngles.x), AngleToQuarterTurns(eulerAngles.y), AngleToQuarterTurns(eulerAngles.z)));
}
private static int AngleToQuarterTurns(float angle)
{
int num = Mathf.RoundToInt(NormalizeSignedAngle(angle) / 90f) % 4;
if (num >= 0)
{
return num;
}
return num + 4;
}
private static bool ReadBool(InputDevice device, InputFeatureUsage<bool> usage)
{
bool value = default(bool);
return device.isValid && device.TryGetFeatureValue(usage, out value) && value;
}
private bool ReadGrabHeld(InputDevice device)
{
if (!device.isValid)
{
return false;
}
bool value;
bool num = device.TryGetFeatureValue(CommonUsages.gripButton, out value) && value;
bool flag = ReadFloat(device, CommonUsages.grip) >= gripGrabThreshold;
return num || flag;
}
private static float ReadFloat(InputDevice device, InputFeatureUsage<float> usage)
{
if (!device.isValid || !device.TryGetFeatureValue(usage, out var value))
{
return 0f;
}
return value;
}
private IEnumerator AnimateAndMergeSelectedGroups(List<VoxelGroup> groupsToMerge)
{
groupingAnimationActive = true;
float num = Mathf.Max(((groupManager != null) ? groupManager.VoxelSize : 0.125f) * 0.9f, 0.12f);
List<GroupMergeAnimationState> states = new List<GroupMergeAnimationState>(groupsToMerge.Count);
Vector3 mergeCenter = Vector3.zero;
int num2 = 0;
for (int i = 0; i < groupsToMerge.Count; i++)
{
VoxelGroup voxelGroup = groupsToMerge[i];
if (!(voxelGroup == null))
{
mergeCenter += voxelGroup.GetWorldBounds().center;
num2++;
}
}
if (num2 < 2)
{
groupingAnimationActive = false;
yield break;
}
mergeCenter /= (float)num2;
PlayGroupingClip(groupingClickClip, mergeCenter, 0.85f);
VRHaptics.Pulse(rightHandDevice, 0.18f, 0.035f);
VRHaptics.Pulse(rightHandDevice, 0.65f, 0.11f);
for (int j = 0; j < groupsToMerge.Count; j++)
{
VoxelGroup voxelGroup2 = groupsToMerge[j];
if (!(voxelGroup2 == null))
{
Vector3 center = voxelGroup2.GetWorldBounds().center;
Vector3 vector = center - mergeCenter;
if (vector.sqrMagnitude < 0.0001f)
{
float f = MathF.PI * 2f * (float)j / (float)Mathf.Max(1, num2);
vector = new Vector3(Mathf.Cos(f), 0f, Mathf.Sin(f));
}
vector.Normalize();
GroupMergeAnimationState item = new GroupMergeAnimationState
{
group = voxelGroup2,
startTransformPosition = voxelGroup2.transform.position,
startBoundsCenter = center,
burstStartCenter = center,
burstPeakCenter = center + vector * num
};
states.Add(item);
}
}
float elapsed = 0f;
while (elapsed < 0.18f)
{
elapsed += Time.unscaledDeltaTime;
float num3 = Mathf.Clamp01(elapsed / 0.18f);
for (int k = 0; k < states.Count; k++)
{
GroupMergeAnimationState state = states[k];
if (!(state.group == null))
{
float t = 1f - Mathf.Pow(1f - num3, 2f);
Vector3 animatedCenter = Vector3.LerpUnclamped(state.burstStartCenter, state.burstPeakCenter, t);
SetAnimatedGroupCenter(state, animatedCenter);
}
}
yield return null;
}
elapsed = 0f;
while (elapsed < 0.17f)
{
elapsed += Time.unscaledDeltaTime;
float num4 = Mathf.Clamp01(elapsed / 0.17f);
for (int l = 0; l < states.Count; l++)
{
GroupMergeAnimationState state2 = states[l];
if (!(state2.group == null))
{
float t2 = num4 * num4 * (3f - 2f * num4);
Vector3 animatedCenter2 = Vector3.LerpUnclamped(state2.burstPeakCenter, state2.startBoundsCenter, t2);
SetAnimatedGroupCenter(state2, animatedCenter2);
}
}
yield return null;
}
for (int m = 0; m < states.Count; m++)
{
GroupMergeAnimationState groupMergeAnimationState = states[m];
if (!(groupMergeAnimationState.group == null))
{
groupMergeAnimationState.group.transform.position = groupMergeAnimationState.startTransformPosition;
}
}
selectionBuffer.Clear();
for (int n = 0; n < states.Count; n++)
{
if (states[n].group != null)
{
selectionBuffer.Add(states[n].group);
}
}
ClearSelectionInternal(suppressUntilNeutral: true);
VoxelGroup mergedGroup = groupManager.MergeGroups(selectionBuffer);
selectionBuffer.Clear();
PlayGroupingClip(groupingThockClip, mergeCenter, 1f);
VRHaptics.Pulse(rightHandDevice, 0.65f, 0.11f);
if (mergedGroup != null)
{
float mergedVoxelSize = ((groupManager != null) ? groupManager.VoxelSize : 0.125f);
Vector3 mergedCenter = mergedGroup.GetWorldBounds().center;
Quaternion mergedRotation = mergedGroup.transform.rotation;
Vector3 mergedLocalBoundsCenter = new Vector3(mergedGroup.size.x, mergedGroup.size.y, mergedGroup.size.z) * (mergedVoxelSize * 0.5f);
float impactElapsed = 0f;
while (impactElapsed < 0.05f)
{
impactElapsed += Time.unscaledDeltaTime;
float num5 = Mathf.Clamp01(impactElapsed / 0.05f);
float uniformScale = ((!(num5 < 0.5f)) ? Mathf.LerpUnclamped(1.05f, 1f, (num5 - 0.5f) / 0.5f) : Mathf.LerpUnclamped(0.95f, 1.05f, num5 / 0.5f));
ApplyCenteredGroupScale(mergedGroup, mergedCenter, mergedRotation, mergedLocalBoundsCenter, uniformScale);
yield return null;
}
ResetMergedGroupTransform(mergedGroup, mergedVoxelSize);
}
groupingAnimationActive = false;
}
private void SetAnimatedGroupCenter(GroupMergeAnimationState state, Vector3 animatedCenter)
{
if (!(state.group == null))
{
Vector3 vector = animatedCenter - state.startBoundsCenter;
state.group.transform.position = state.startTransformPosition + vector;
}
}
private static void ApplyCenteredGroupScale(VoxelGroup group, Vector3 worldCenter, Quaternion rotation, Vector3 localBoundsCenter, float uniformScale)
{
if (!(group == null))
{
Vector3 vector = localBoundsCenter * uniformScale;
Vector3 position = worldCenter - rotation * vector;
group.transform.SetPositionAndRotation(position, rotation);
group.transform.localScale = Vector3.one * uniformScale;
}
}
private static void ResetMergedGroupTransform(VoxelGroup group, float voxelSize)
{
if (!(group == null))
{
group.transform.localScale = Vector3.one;
group.transform.SetPositionAndRotation((Vector3)group.position * voxelSize, GridRotationUtility.ToQuaternion(group.packedRotation));
}
}
private void PlayGroupingClip(AudioClip clip, Vector3 position, float volume)
{
if (!(clip == null))
{
AudioSource.PlayClipAtPoint(clip, position, volume);
}
}
private void EnsureGroupingClipsAssigned()
{
if (groupingClickClip == null)
{
groupingClickClip = Resources.Load<AudioClip>("Click");
}
if (groupingThockClip == null)
{
groupingThockClip = Resources.Load<AudioClip>("Thock");
}
}
private void SendSelectedGroupsToInventoryStub(int selectedGroupCount)
{
if (selectedGroupCount > 0)
{
Debug.Log($"BlockSpace inventory send stub for {selectedGroupCount} selected groups.", this);
}
}
}
}