using System.Collections.Generic; using BlockSpace.Worlds; using Photon.VR; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.XR; using UnityEngine.XR.Interaction.Toolkit.Locomotion; using UnityEngine.XR.Interaction.Toolkit.Locomotion.Movement; namespace BlockSpace.Voxels { [DisallowMultipleComponent] public sealed class VRVoxelBrush : MonoBehaviour { private sealed class AvatarEditSnapshot { public VoxelGroup group; public Vector3Int position; public byte packedRotation; public Vector3Int size; public byte[] voxels; } private sealed class AvatarEditUndoRecord { public AvatarEditSnapshot before; public AvatarEditSnapshot after; } private const int PaletteColumns = 8; private const int PaletteRows = 8; private const float PaletteSize = 0.3f; private const float PaletteCubeSize = 0.03f; private const float PaletteSelectedCubeSize = 0.05f; private const float PaletteUpOffset = 0.2f; private const float PaletteForwardOffset = 0.14f; private const float PaletteCellLift = 0.01f; private const float PaletteCursorWidth = 0.0035f; private const float ColorPickRadius = 0.2f; private static readonly Color32 ColorPickIndicatorColor = new Color32(220, 120, byte.MaxValue, 140); private static readonly Color32 PaletteCursorLightColor = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); private static readonly Color32 PaletteCursorDarkColor = new Color32(12, 12, 12, byte.MaxValue); private static readonly Color32 IdleIndicatorColor = new Color32(64, 160, byte.MaxValue, 64); private static readonly Color32 DrawingIndicatorColor = new Color32(64, 160, byte.MaxValue, 0); private static readonly Color32 ErasingIndicatorColor = new Color32(byte.MaxValue, 72, 72, 64); private static readonly Color32 PaintingIndicatorColor = new Color32(byte.MaxValue, 220, 72, 96); private static readonly Color32 SelectingIndicatorColor = new Color32(72, byte.MaxValue, 96, 64); [SerializeField] private VoxelGroupManager groupManager; [SerializeField] private Transform trackingOrigin; [SerializeField] private Transform rightHandAnchor; [SerializeField] private Transform leftHandAnchor; [SerializeField] private float brushForwardOffset = 0.05f; [SerializeField] private float brushRadius = 0.125f; [SerializeField] private float minBrushRadius = 0.0625f; [SerializeField] private float maxBrushRadius = 0.625f; [SerializeField] private float brushRadiusStep = 0.0625f; [SerializeField] private float brushDistance = 1f; [SerializeField] private float minBrushDistance = 0.5f; [SerializeField] private float maxBrushDistance = 3f; [SerializeField] private float brushDistanceStep = 0.125f; [SerializeField] private float desktopSurfaceOffset = 0.01f; [SerializeField] private LayerMask desktopRaycastMask = -5; [SerializeField] private float triggerThreshold = 0.7f; [SerializeField] private float thumbstickDeadZone = 0.6f; [SerializeField] private float radiusRepeatDelay = 0.12f; [SerializeField] private float creatorToggleChordWindow = 0.12f; [SerializeField] private float avatarUndoChordWindow = 0.12f; [SerializeField] [Range(0f, 63f)] private byte selectedColorIndex = 16; [SerializeField] private VoxelMaterial selectedMaterial; private readonly HashSet strokeCells = new HashSet(); private readonly HashSet activeEraseGroups = new HashSet(); private readonly HashSet activePaintGroups = new HashSet(); private readonly Stack avatarUndoStack = new Stack(); private readonly List moveProviders = new List(); private readonly Dictionary moveProviderEnabledStates = new Dictionary(); private readonly List selectionLocomotionProviders = new List(); private readonly Dictionary selectionLocomotionStates = new Dictionary(); private readonly List paletteMoveObjects = new List(); private readonly Dictionary paletteMoveObjectStates = new Dictionary(); private InputDevice rightHand; private InputDevice leftHand; private bool useXRInput; private bool lastDesktopBrushWasHit; private bool lastDesktopBrushWasHitValid; private bool lastBrushPointHadHit; private bool lastErasePointHadHit; private bool lastPaintPointHadHit; private bool creatorMode = true; private bool drawingStroke; private bool erasing; private bool painting; private bool paletteOpen; private byte activeStrokeVoxelValue; private bool leftPrimaryPrevious; private bool leftThumbstickClickPrevious; private bool leftPaletteAxisLatched; private bool leftCreatorChordConsumed; private bool leftGripHeldPrevious; private bool leftTriggerHeldPrevious; private bool avatarUndoChordActive; private bool suppressAvatarUndoUntilNeutral; private float avatarClearChordStartTime = -1f; private bool suppressAvatarClearUntilNeutral; private float desktopAvatarClearStartTime = -1f; private bool rightGripHeldPrevious; private bool rightTriggerHeldPrevious; private bool avatarEditUndoSessionActive; private bool avatarEditUndoSessionChanged; private bool selectionLocomotionBlocked; private Vector3 lastBrushPoint; private Vector3 lastErasePoint; private Vector3 lastPaintPoint; private float lastLeftGripPressTime = float.NegativeInfinity; private float lastLeftTriggerPressTime = float.NegativeInfinity; private float lastRightGripPressTime = float.NegativeInfinity; private float lastRightTriggerPressTime = float.NegativeInfinity; private float nextRadiusChangeTime; private float nextToolHapticTime; private float lastEraseChangedTime = float.NegativeInfinity; private float lastPaintChangedTime = float.NegativeInfinity; private bool colorPickerHeldPrevious; private bool colorPickerActive; private VoxelGroup activeStrokeGroup; private bool activeStrokePreviewBroadcasting; private bool activeErasePreviewBroadcasting; private VoxelGroup avatarEditorTargetGroup; private AvatarEditSnapshot avatarEditUndoBeforeSnapshot; private Transform brushIndicator; private MeshRenderer brushIndicatorRenderer; private Transform colorPickIndicator; private MeshRenderer colorPickIndicatorRenderer; private Material brushIndicatorMaterial; private Material colorPickIndicatorMaterial; private Transform paletteRoot; private LineRenderer paletteCursorRenderer; private Material paletteCellMaterial; private Material paletteCursorMaterial; private readonly MeshRenderer[] paletteCellRenderers = new MeshRenderer[64]; private readonly MaterialPropertyBlock[] paletteCellBlocks = new MaterialPropertyBlock[64]; private int paletteColumn; private int paletteRow; public static VRVoxelBrush Instance { get; private set; } public bool IsPaletteOpen => paletteOpen; public bool UsingXRInput => useXRInput; public Transform RightHandAnchor => rightHandAnchor; public Transform LeftHandAnchor => leftHandAnchor; public Transform TrackingOrigin => trackingOrigin; public bool IsEditingAvatar => avatarEditorTargetGroup != null; private void Awake() { Instance = this; if (groupManager == null) { groupManager = Object.FindObjectOfType(); } if (groupManager != null) { creatorMode = groupManager.CreatorMode; } ApplyBrushRadiusLimits(); EnsureBrushIndicator(); EnsureColorPickIndicator(); EnsurePaletteUi(); EnsureGroupSelector(); EnsureNodeGraph(); EnsureDesktopRigController(); EnsureAvatarCustomizer(); RefreshMoveProviders(); RefreshSelectionLocomotionProviders(); SyncSelectionRadius(); SyncPaletteCursorToSelectedColor(); UpdatePaletteSelectionVisuals(); SetPaletteVisible(visible: false); } private void OnEnable() { AcquireDevices(); } private void OnDisable() { CancelStroke(); if (erasing) { EndErase(); } if (painting) { EndPaint(); } paletteOpen = false; ApplyPaletteLocomotionGate(disableMovement: false); ApplySelectionLocomotionGate(disableMovement: false); avatarUndoChordActive = false; suppressAvatarUndoUntilNeutral = false; SetPaletteVisible(visible: false); SetBrushIndicatorVisible(visible: false); SetColorPickIndicatorVisible(visible: false); } private void OnDestroy() { if (Instance == this) { Instance = null; } if (brushIndicatorMaterial != null) { Object.Destroy(brushIndicatorMaterial); } if (colorPickIndicatorMaterial != null) { Object.Destroy(colorPickIndicatorMaterial); } if (paletteCellMaterial != null) { Object.Destroy(paletteCellMaterial); } if (paletteCursorMaterial != null) { Object.Destroy(paletteCursorMaterial); } } public void SetAvatarEditorTarget(VoxelGroup targetGroup) { if (!(avatarEditorTargetGroup == targetGroup)) { FinishActiveAvatarEditSessions(); avatarEditorTargetGroup = targetGroup; } } public void ClearAvatarEditorTarget() { if (!(avatarEditorTargetGroup == null)) { FinishActiveAvatarEditSessions(); avatarEditorTargetGroup = null; } } public bool UndoAvatarEditorAction() { if (drawingStroke || erasing || painting) { return false; } if (avatarUndoStack.Count == 0) { return false; } AvatarEditUndoRecord avatarEditUndoRecord = avatarUndoStack.Pop(); if (avatarEditUndoRecord == null || avatarEditUndoRecord.before == null) { return false; } avatarEditUndoBeforeSnapshot = null; avatarEditUndoSessionActive = false; avatarEditUndoSessionChanged = false; RestoreAvatarEditSnapshot(avatarEditUndoRecord.before); return true; } public void FinalizeAvatarEditorSessions() { FinishActiveAvatarEditSessions(); } private void Update() { if (groupManager == null) { return; } creatorMode = groupManager.CreatorMode; EnsureDevices(); useXRInput = DetectXRInputActive(); HandleCreatorToggle(); HandlePaletteToggleAndNavigation(); HandleMaterialCycle(); ApplyBrushRadiusLimits(); HandleBrushRadius(); SyncSelectionRadius(); UpdatePaletteUi(); UpdateToolHaptics(); HandleColorPickerTool(); VRGroupSelector instance = VRGroupSelector.Instance; NodeGraphManager instance2 = NodeGraphManager.Instance; bool flag = instance != null && instance.HasSelection; bool flag2 = instance2 != null && instance2.BlocksRightHandActions; ApplySelectionLocomotionGate((instance != null && (flag || instance.BlocksRightHandActions)) || flag2); bool flag3 = instance != null && instance.IsSelecting; bool flag4 = flag3; bool flag5 = (instance != null && instance.BlocksRightHandActions) || flag2; Vector3 worldPosition; Quaternion worldRotation; bool flag6 = TryGetActiveBrushPose(out worldPosition, out worldRotation); bool currentHadHit = useXRInput || lastDesktopBrushWasHit; bool flag7 = (useXRInput ? (ReadFloat(rightHand, CommonUsages.trigger) >= triggerThreshold) : Input.GetMouseButton(0)); bool flag8 = (useXRInput ? ReadGripHeld(rightHand) : Input.GetMouseButton(1)); bool flag9 = (useXRInput ? ReadBool(rightHand, CommonUsages.secondaryButton) : (Input.GetKey(KeyCode.Q) && !flag)); bool flag10 = HandleAvatarEditUndo(flag7, flag8); bool flag11 = HandleDesktopUndoShortcut(); bool flag12 = HandleAvatarClearChord(); Vector3 vector = worldPosition; if (flag6 && avatarEditorTargetGroup != null && creatorMode && !flag3 && (flag7 || drawingStroke)) { vector = AdjustBrushPointForAvatarEditing(worldPosition); } if (!creatorMode) { if (paletteOpen) { paletteOpen = false; leftPaletteAxisLatched = false; ApplyPaletteLocomotionGate(disableMovement: false); SetPaletteVisible(visible: false); } ApplySelectionLocomotionGate(disableMovement: false); if (drawingStroke) { CancelStroke(); } if (erasing) { EndErase(); } if (painting) { EndPaint(); } UpdateBrushIndicator(flag6, drawingStroke ? vector : worldPosition); return; } if (flag12) { if (drawingStroke) { CancelStroke(); } if (erasing) { EndErase(); } if (painting) { EndPaint(); } UpdateBrushIndicator(flag6, drawingStroke ? vector : worldPosition); return; } if (flag10) { if (drawingStroke) { CancelStroke(); } if (erasing) { EndErase(); } if (painting) { EndPaint(); } UpdateBrushIndicator(flag6, drawingStroke ? vector : worldPosition); return; } if (flag11) { if (drawingStroke) { CancelStroke(); } if (erasing) { EndErase(); } if (painting) { EndPaint(); } UpdateBrushIndicator(flag6, drawingStroke ? vector : worldPosition); return; } if (flag5) { if (drawingStroke) { CancelStroke(); } if (erasing) { EndErase(); } if (painting) { EndPaint(); } UpdateBrushIndicator(flag6, drawingStroke ? vector : worldPosition); return; } if (flag6) { if (!flag3 && flag7) { if (painting) { EndPaint(); } if (erasing) { EndErase(); } if (!drawingStroke) { BeginStroke(vector); } else if (ShouldBreakContinuousEdit(lastBrushPoint, vector, lastBrushPointHadHit, currentHadHit)) { lastBrushPoint = vector; lastBrushPointHadHit = currentHadHit; RasterizeSegment(vector, vector, brushRadius); } else { RasterizeSegment(lastBrushPoint, vector, brushRadius); lastBrushPoint = vector; lastBrushPointHadHit = currentHadHit; } } else if (drawingStroke) { FinalizeStroke(); } if (flag3) { if (drawingStroke) { CancelStroke(); } if (erasing) { EndErase(); } if (painting) { EndPaint(); } if (instance2 != null && instance != null && instance.TryGetSelectionCenter(out var worldPosition2)) { instance2.SelectNodesWithinSphere(worldPosition2, instance.SelectionRadius, additive: true); } } else if (!drawingStroke) { if (flag8 && !flag4 && !flag) { if (painting) { EndPaint(); } if (!erasing) { BeginErase(worldPosition); } else if (ShouldBreakContinuousEdit(lastErasePoint, worldPosition, lastErasePointHadHit, currentHadHit)) { lastErasePoint = worldPosition; lastErasePointHadHit = currentHadHit; UpdateErase(worldPosition, worldPosition); } else { UpdateErase(lastErasePoint, worldPosition); lastErasePoint = worldPosition; lastErasePointHadHit = currentHadHit; } } else if (erasing) { EndErase(); } if (!erasing && !flag8 && !flag) { if (flag9) { if (!painting) { BeginPaint(worldPosition); } else if (ShouldBreakContinuousEdit(lastPaintPoint, worldPosition, lastPaintPointHadHit, currentHadHit)) { lastPaintPoint = worldPosition; lastPaintPointHadHit = currentHadHit; UpdatePaint(worldPosition, worldPosition); } else { UpdatePaint(lastPaintPoint, worldPosition); lastPaintPoint = worldPosition; lastPaintPointHadHit = currentHadHit; } } else if (painting) { EndPaint(); } } else if (painting) { EndPaint(); } } } else { if (!flag7 && drawingStroke) { FinalizeStroke(); } if (erasing) { EndErase(); } if (painting) { EndPaint(); } } UpdateBrushIndicator(flag6, drawingStroke ? vector : worldPosition); } private void BeginStroke(Vector3 startPoint) { VRGroupSelector instance = VRGroupSelector.Instance; if (instance != null && instance.HasSelection) { instance.ClearSelectionForToolUse(); } NodeGraphManager.Instance?.ClearSelectionForToolUse(); strokeCells.Clear(); drawingStroke = true; activeStrokeVoxelValue = GetCurrentBrushVoxelValue(); activeStrokeGroup = ((avatarEditorTargetGroup != null) ? avatarEditorTargetGroup : groupManager.BeginStrokeGroup(0)); activeStrokePreviewBroadcasting = false; if (activeStrokeGroup == avatarEditorTargetGroup) { BeginAvatarEditUndoSession(activeStrokeGroup); activeStrokeGroup.SetGreedyRenderMeshing(enabled: false); } else { BeginStrokePreviewBroadcast(startPoint); } lastBrushPoint = startPoint; lastBrushPointHadHit = useXRInput || lastDesktopBrushWasHit; RasterizeSegment(startPoint, startPoint, brushRadius); } private void FinalizeStroke() { drawingStroke = false; if (activeStrokeGroup == null) { EndStrokePreviewBroadcast(); strokeCells.Clear(); return; } if (activeStrokeGroup == avatarEditorTargetGroup) { FinalizeAvatarEditorGroup(activeStrokeGroup); } else if (strokeCells.Count == 0) { EndStrokePreviewBroadcast(); groupManager.CancelStrokeGroup(activeStrokeGroup); } else { EndStrokePreviewBroadcast(); groupManager.FinalizeStrokeGroup(activeStrokeGroup); } activeStrokeGroup = null; strokeCells.Clear(); } private void CancelStroke() { drawingStroke = false; if (activeStrokeGroup == avatarEditorTargetGroup) { FinalizeAvatarEditorGroup(activeStrokeGroup); activeStrokeGroup = null; } else if (activeStrokeGroup != null && groupManager != null) { EndStrokePreviewBroadcast(); groupManager.CancelStrokeGroup(activeStrokeGroup); activeStrokeGroup = null; } else { EndStrokePreviewBroadcast(); } strokeCells.Clear(); } private void BeginStrokePreviewBroadcast(Vector3 startPoint) { if (groupManager == null || activeStrokeGroup == null || activeStrokeGroup == avatarEditorTargetGroup) { activeStrokePreviewBroadcasting = false; return; } activeStrokePreviewBroadcasting = true; groupManager.BroadcastStrokePreviewBegin(activeStrokeVoxelValue, brushRadius, brushForwardOffset, startPoint); } private void UpdateStrokePreviewBroadcast(Vector3 point) { if (activeStrokePreviewBroadcasting && !(groupManager == null)) { groupManager.BroadcastStrokePreviewUpdate(activeStrokeVoxelValue, brushRadius, brushForwardOffset, point); } } private void EndStrokePreviewBroadcast() { if (activeStrokePreviewBroadcasting) { activeStrokePreviewBroadcasting = false; groupManager?.BroadcastStrokePreviewEnd(); } } private void BeginErase(Vector3 startPoint) { VRGroupSelector instance = VRGroupSelector.Instance; if (instance != null && instance.HasSelection) { instance.ClearSelectionForToolUse(); } erasing = true; activeEraseGroups.Clear(); activeErasePreviewBroadcasting = false; lastEraseChangedTime = float.NegativeInfinity; if (avatarEditorTargetGroup != null) { BeginAvatarEditUndoSession(avatarEditorTargetGroup); activeEraseGroups.Add(avatarEditorTargetGroup); avatarEditorTargetGroup.SetGreedyRenderMeshing(enabled: false); } else { groupManager.BeginEraseUndoSession(); BeginErasePreviewBroadcast(startPoint); } lastErasePoint = startPoint; lastErasePointHadHit = useXRInput || lastDesktopBrushWasHit; UpdateErase(startPoint, startPoint); } private void EndErase() { erasing = false; EndErasePreviewBroadcast(); if (avatarEditorTargetGroup != null) { foreach (VoxelGroup activeEraseGroup in activeEraseGroups) { if (activeEraseGroup != null) { FinalizeAvatarEditorGroup(activeEraseGroup); } } activeEraseGroups.Clear(); return; } if (groupManager == null) { activeEraseGroups.Clear(); return; } foreach (VoxelGroup activeEraseGroup2 in activeEraseGroups) { if (!(activeEraseGroup2 == null)) { groupManager.FinalizeLiveEditedGroup(activeEraseGroup2, sendNetworkUpdate: true, rebuildCollider: true, playDestroyAnimationIfEmpty: false); } } groupManager.CompleteEraseUndoSession(); activeEraseGroups.Clear(); } private void BeginPaint(Vector3 startPoint) { if (!(groupManager == null)) { VRGroupSelector instance = VRGroupSelector.Instance; if (instance != null && instance.HasSelection) { instance.ClearSelectionForToolUse(); } NodeGraphManager.Instance?.ClearSelectionForToolUse(); painting = true; activePaintGroups.Clear(); lastPaintChangedTime = float.NegativeInfinity; if (avatarEditorTargetGroup != null) { BeginAvatarEditUndoSession(avatarEditorTargetGroup); activePaintGroups.Add(avatarEditorTargetGroup); avatarEditorTargetGroup.SetGreedyRenderMeshing(enabled: false); } else { groupManager.BeginPaintUndoSession(); } lastPaintPoint = startPoint; lastPaintPointHadHit = useXRInput || lastDesktopBrushWasHit; UpdatePaint(startPoint, startPoint); } } private void EndPaint() { painting = false; if (avatarEditorTargetGroup != null) { foreach (VoxelGroup activePaintGroup in activePaintGroups) { if (activePaintGroup != null) { FinalizeAvatarEditorGroup(activePaintGroup); } } activePaintGroups.Clear(); return; } if (groupManager == null) { activePaintGroups.Clear(); return; } foreach (VoxelGroup activePaintGroup2 in activePaintGroups) { if (!(activePaintGroup2 == null)) { groupManager.FinalizeLiveEditedGroup(activePaintGroup2, sendNetworkUpdate: true, rebuildCollider: false); } } groupManager.CompletePaintUndoSession(); activePaintGroups.Clear(); } private void UpdatePaint(Vector3 start, Vector3 end) { if (groupManager == null) { return; } if (avatarEditorTargetGroup != null) { UpdateAvatarEditorPaint(start, end); return; } byte currentBrushVoxelValue = GetCurrentBrushVoxelValue(); float num = Mathf.Max(groupManager.VoxelSize * 0.5f, brushRadius * 0.5f); float num2 = Vector3.Distance(start, end); int num3 = Mathf.Max(1, Mathf.CeilToInt(num2 / num)); for (int i = 0; i <= num3; i++) { float t = ((num3 == 0) ? 0f : ((float)i / (float)num3)); Vector3 worldCenter = Vector3.Lerp(start, end, t); if (groupManager.PaintSphereRealtime(worldCenter, brushRadius, currentBrushVoxelValue, activePaintGroups)) { lastPaintChangedTime = Time.unscaledTime; } } } private void UpdateErase(Vector3 start, Vector3 end) { if (groupManager == null) { return; } if (avatarEditorTargetGroup != null) { UpdateAvatarEditorErase(start, end); return; } float voxelSize = groupManager.VoxelSize; float num = brushRadius; float num2 = Mathf.Max(voxelSize * 0.5f, num * 0.5f); float num3 = Vector3.Distance(start, end); int num4 = Mathf.Max(1, Mathf.CeilToInt(num3 / num2)); for (int i = 0; i <= num4; i++) { float t = ((num4 == 0) ? 0f : ((float)i / (float)num4)); Vector3 worldCenter = Vector3.Lerp(start, end, t); if (groupManager.EraseSphereRealtime(worldCenter, num, activeEraseGroups)) { lastEraseChangedTime = Time.unscaledTime; } NodeGraphManager.Instance?.DeleteNodesWithinSphere(worldCenter, num, broadcast: true); } UpdateErasePreviewBroadcast(end); } private void BeginErasePreviewBroadcast(Vector3 startPoint) { if (groupManager == null || avatarEditorTargetGroup != null) { activeErasePreviewBroadcasting = false; return; } activeErasePreviewBroadcasting = true; groupManager.BroadcastErasePreviewBegin(brushRadius, brushForwardOffset, startPoint); } private void UpdateErasePreviewBroadcast(Vector3 point) { if (activeErasePreviewBroadcasting && !(groupManager == null)) { groupManager.BroadcastErasePreviewUpdate(brushRadius, brushForwardOffset, point); } } private void EndErasePreviewBroadcast() { if (activeErasePreviewBroadcasting) { activeErasePreviewBroadcasting = false; groupManager?.BroadcastErasePreviewEnd(); } } private void UpdateToolHaptics() { if (useXRInput && !(Time.unscaledTime < nextToolHapticTime)) { if (drawingStroke) { VRHaptics.Pulse(rightHand, 0.32f, 0.055f); nextToolHapticTime = Time.unscaledTime + 0.05f; } else if (erasing && Time.unscaledTime - lastEraseChangedTime <= 0.2f) { VRHaptics.Pulse(rightHand, 0.32f, 0.055f); nextToolHapticTime = Time.unscaledTime + 0.05f; } else if (painting && Time.unscaledTime - lastPaintChangedTime <= 0.2f) { VRHaptics.Pulse(rightHand, 0.32f, 0.055f); nextToolHapticTime = Time.unscaledTime + 0.05f; } } } private void HandleColorPickerTool() { EnsureColorPickIndicator(); if (!creatorMode || groupManager == null) { colorPickerActive = false; SetColorPickIndicatorVisible(visible: false); colorPickerHeldPrevious = false; return; } bool flag = false; Vector3 worldPosition = Vector3.zero; bool flag2 = false; Quaternion worldRotation; if (useXRInput) { bool num = ReadGripHeld(leftHand); bool flag3 = ReadFloat(leftHand, CommonUsages.trigger) >= triggerThreshold; flag = num && !flag3; flag2 = TryGetBrushPose(leftHand, XRNode.LeftHand, leftHandAnchor, out worldPosition, out worldRotation); } else { flag = Input.GetKey(KeyCode.Z); flag2 = TryGetActiveBrushPose(out worldPosition, out worldRotation); } if (flag && flag2) { if (!colorPickerHeldPrevious) { NodeGraphManager.Instance?.ClearSelectionForToolUse(); } colorPickerActive = true; float b = ((avatarEditorTargetGroup != null) ? avatarEditorTargetGroup.CurrentVoxelSize : groupManager.VoxelSize); colorPickIndicator.position = worldPosition; colorPickIndicator.rotation = Quaternion.identity; colorPickIndicator.localScale = Vector3.one * Mathf.Max(0.001f, b); if (colorPickIndicatorMaterial != null) { ApplyIndicatorMaterialColor(colorPickIndicatorMaterial, ColorPickIndicatorColor); } SetColorPickIndicatorVisible(visible: true); } else { SetColorPickIndicatorVisible(visible: false); } if (!flag && colorPickerHeldPrevious && colorPickerActive && flag2) { if (TryPickColorFromWorld(worldPosition, out var pickedVoxelValue)) { ApplyPickedVoxelValueToPalette(pickedVoxelValue); if (useXRInput) { VRHaptics.Pulse(leftHand, 0.18f, 0.055f); } } colorPickerActive = false; } colorPickerHeldPrevious = flag; } private bool TryPickColorFromWorld(Vector3 worldPoint, out byte pickedVoxelValue) { pickedVoxelValue = 0; if (avatarEditorTargetGroup != null) { if (!avatarEditorTargetGroup.IntersectsSphere(worldPoint, 0.2f)) { return false; } Vector3 pickedWorldCenter; float pickedDistanceSquared; return avatarEditorTargetGroup.TryPickNearestVoxelValue(worldPoint, 0.2f, out pickedVoxelValue, out pickedWorldCenter, out pickedDistanceSquared); } if (groupManager != null) { return groupManager.TryPickNearestVoxelValue(worldPoint, 0.2f, out pickedVoxelValue); } return false; } private void ApplyPickedVoxelValueToPalette(byte voxelValue) { if (!PackedVoxel.IsAir(voxelValue)) { selectedColorIndex = PackedVoxel.GetColorIndex(voxelValue); selectedMaterial = PackedVoxel.GetMaterial(voxelValue); SyncPaletteCursorToSelectedColor(); UpdatePaletteSelectionVisuals(); } } private void EnsureBrushIndicator() { if (!(brushIndicator != null)) { GameObject gameObject = GameObject.CreatePrimitive(PrimitiveType.Sphere); gameObject.name = "BrushRadiusIndicator"; gameObject.transform.SetParent(base.transform, worldPositionStays: false); gameObject.layer = base.gameObject.layer; Collider component = gameObject.GetComponent(); if (component != null) { component.enabled = false; } brushIndicator = gameObject.transform; brushIndicatorRenderer = gameObject.GetComponent(); brushIndicatorRenderer.shadowCastingMode = ShadowCastingMode.Off; brushIndicatorRenderer.receiveShadows = false; brushIndicatorRenderer.sharedMaterial = GetOrCreateBrushIndicatorMaterial(); SetBrushIndicatorVisible(visible: false); } } private void EnsureColorPickIndicator() { if (!(colorPickIndicator != null)) { GameObject gameObject = GameObject.CreatePrimitive(PrimitiveType.Sphere); gameObject.name = "ColorPickIndicator"; gameObject.transform.SetParent(base.transform, worldPositionStays: false); gameObject.layer = base.gameObject.layer; Collider component = gameObject.GetComponent(); if (component != null) { component.enabled = false; } colorPickIndicator = gameObject.transform; colorPickIndicatorRenderer = gameObject.GetComponent(); colorPickIndicatorRenderer.shadowCastingMode = ShadowCastingMode.Off; colorPickIndicatorRenderer.receiveShadows = false; Material orCreateBrushIndicatorMaterial = GetOrCreateBrushIndicatorMaterial(); if (orCreateBrushIndicatorMaterial != null) { colorPickIndicatorMaterial = Object.Instantiate(orCreateBrushIndicatorMaterial); colorPickIndicatorMaterial.name = "ColorPickIndicatorMaterial"; colorPickIndicatorRenderer.sharedMaterial = colorPickIndicatorMaterial; } SetColorPickIndicatorVisible(visible: false); } } private void SetColorPickIndicatorVisible(bool visible) { if (colorPickIndicatorRenderer != null) { colorPickIndicatorRenderer.enabled = visible; } } private void EnsurePaletteUi() { if (paletteRoot != null) { return; } paletteRoot = new GameObject("LeftPaletteUI").transform; paletteRoot.SetParent(base.transform, worldPositionStays: false); for (int i = 0; i < 64; i++) { GameObject obj = GameObject.CreatePrimitive(PrimitiveType.Cube); obj.name = $"PaletteCell_{i:D2}"; obj.transform.SetParent(paletteRoot, worldPositionStays: false); obj.transform.localRotation = Quaternion.identity; obj.transform.localScale = Vector3.one * 0.03f; Collider component = obj.GetComponent(); if (component != null) { component.enabled = false; } MeshRenderer component2 = obj.GetComponent(); component2.shadowCastingMode = ShadowCastingMode.Off; component2.receiveShadows = false; component2.sharedMaterial = GetOrCreatePaletteCellMaterial(); paletteCellRenderers[i] = component2; paletteCellBlocks[i] = new MaterialPropertyBlock(); } GameObject gameObject = new GameObject("PaletteCursor"); gameObject.transform.SetParent(paletteRoot, worldPositionStays: false); paletteCursorRenderer = gameObject.AddComponent(); paletteCursorRenderer.useWorldSpace = false; paletteCursorRenderer.loop = true; paletteCursorRenderer.positionCount = 4; paletteCursorRenderer.alignment = LineAlignment.View; paletteCursorRenderer.widthMultiplier = 0.0035f; paletteCursorRenderer.numCapVertices = 0; paletteCursorRenderer.numCornerVertices = 0; paletteCursorRenderer.shadowCastingMode = ShadowCastingMode.Off; paletteCursorRenderer.receiveShadows = false; paletteCursorRenderer.sharedMaterial = GetOrCreatePaletteCursorMaterial(); paletteCursorRenderer.enabled = false; UpdatePaletteSelectionVisuals(); } private void UpdateBrushIndicator(bool hasBrushPose, Vector3 brushPoint) { EnsureBrushIndicator(); if (brushIndicator == null || brushIndicatorRenderer == null) { return; } if (!creatorMode) { SetBrushIndicatorVisible(visible: false); return; } VRGroupSelector instance = VRGroupSelector.Instance; Vector3 worldPosition = brushPoint; bool flag = instance != null && instance.IsSelecting && instance.TryGetSelectionCenter(out worldPosition); if (flag) { hasBrushPose = true; brushPoint = worldPosition; } if (!hasBrushPose) { SetBrushIndicatorVisible(visible: false); return; } brushIndicator.position = brushPoint; brushIndicator.rotation = Quaternion.identity; float num = (flag ? instance.SelectionRadius : brushRadius); float num2 = Mathf.Max((groupManager != null) ? groupManager.VoxelSize : 0.125f, num * 2f); brushIndicator.localScale = Vector3.one * num2; Color32 brushIndicatorColor = GetBrushIndicatorColor(); ApplyBrushIndicatorColor(brushIndicatorColor); SetBrushIndicatorVisible(brushIndicatorColor.a > 0); } private Color32 GetBrushIndicatorColor() { if (erasing) { return ErasingIndicatorColor; } if (painting) { return PaintingIndicatorColor; } if (drawingStroke) { return DrawingIndicatorColor; } VRGroupSelector instance = VRGroupSelector.Instance; if (instance != null && instance.IsSelecting) { return SelectingIndicatorColor; } return IdleIndicatorColor; } private Material GetOrCreateBrushIndicatorMaterial() { if (brushIndicatorMaterial != null) { return brushIndicatorMaterial; } Shader shader = Shader.Find("Universal Render Pipeline/Particles/Unlit") ?? Shader.Find("Universal Render Pipeline/Unlit") ?? Shader.Find("Sprites/Default") ?? Shader.Find("Standard"); brushIndicatorMaterial = new Material(shader) { name = "BlockSpace_BrushIndicator" }; if (brushIndicatorMaterial.HasProperty("_Surface")) { brushIndicatorMaterial.SetFloat("_Surface", 1f); } if (brushIndicatorMaterial.HasProperty("_Blend")) { brushIndicatorMaterial.SetFloat("_Blend", 0f); } if (brushIndicatorMaterial.HasProperty("_Cull")) { brushIndicatorMaterial.SetFloat("_Cull", 2f); } if (brushIndicatorMaterial.HasProperty("_SrcBlend")) { brushIndicatorMaterial.SetFloat("_SrcBlend", 5f); } if (brushIndicatorMaterial.HasProperty("_DstBlend")) { brushIndicatorMaterial.SetFloat("_DstBlend", 10f); } if (brushIndicatorMaterial.HasProperty("_SrcBlendAlpha")) { brushIndicatorMaterial.SetFloat("_SrcBlendAlpha", 1f); } if (brushIndicatorMaterial.HasProperty("_DstBlendAlpha")) { brushIndicatorMaterial.SetFloat("_DstBlendAlpha", 10f); } if (brushIndicatorMaterial.HasProperty("_ZWrite")) { brushIndicatorMaterial.SetFloat("_ZWrite", 0f); } brushIndicatorMaterial.renderQueue = 3000; ApplyBrushIndicatorColor(IdleIndicatorColor); return brushIndicatorMaterial; } private Material GetOrCreatePaletteCellMaterial() { if (paletteCellMaterial != null) { return paletteCellMaterial; } Shader paletteCellShader = GetPaletteCellShader(selectedMaterial == VoxelMaterial.Lit); paletteCellMaterial = new Material(paletteCellShader) { name = "BlockSpace_PaletteCell" }; if (paletteCellMaterial.HasProperty("_Surface")) { paletteCellMaterial.SetFloat("_Surface", 0f); } if (paletteCellMaterial.HasProperty("_Cull")) { paletteCellMaterial.SetFloat("_Cull", 0f); } if (paletteCellMaterial.HasProperty("_ZWrite")) { paletteCellMaterial.SetFloat("_ZWrite", 1f); } return paletteCellMaterial; } private void ConfigurePaletteCellMaterial(Material material) { if (!(material == null)) { Shader paletteCellShader = GetPaletteCellShader(selectedMaterial == VoxelMaterial.Lit); if (paletteCellShader != null && material.shader != paletteCellShader) { material.shader = paletteCellShader; } bool flag = selectedMaterial == VoxelMaterial.Transparent || selectedMaterial == VoxelMaterial.Invisible; if (material.HasProperty("_Surface")) { material.SetFloat("_Surface", flag ? 1f : 0f); } if (material.HasProperty("_Mode")) { material.SetFloat("_Mode", flag ? 3f : 0f); } if (material.HasProperty("_Blend")) { material.SetFloat("_Blend", 0f); } if (material.HasProperty("_Cull")) { material.SetFloat("_Cull", 2f); } if (material.HasProperty("_SrcBlend")) { material.SetFloat("_SrcBlend", flag ? 5f : 1f); } if (material.HasProperty("_DstBlend")) { material.SetFloat("_DstBlend", flag ? 10f : 0f); } if (material.HasProperty("_SrcBlendAlpha")) { material.SetFloat("_SrcBlendAlpha", 1f); } if (material.HasProperty("_DstBlendAlpha")) { material.SetFloat("_DstBlendAlpha", flag ? 10f : 0f); } if (material.HasProperty("_ZWrite")) { material.SetFloat("_ZWrite", flag ? 0f : 1f); } if (material.HasProperty("_Smoothness")) { material.SetFloat("_Smoothness", (selectedMaterial == VoxelMaterial.Transparent) ? 0.9f : 0.08f); } if (material.HasProperty("_Metallic")) { material.SetFloat("_Metallic", 0f); } material.renderQueue = (flag ? 3000 : 2000); material.SetOverrideTag("RenderType", flag ? "Transparent" : "Opaque"); } } private static Shader GetPaletteCellShader(bool useLitShader) { if (useLitShader) { return Shader.Find("Universal Render Pipeline/Particles/Lit") ?? Shader.Find("Universal Render Pipeline/Lit") ?? Shader.Find("Standard"); } return Shader.Find("Universal Render Pipeline/Particles/Unlit") ?? Shader.Find("Universal Render Pipeline/Unlit") ?? Shader.Find("Sprites/Default") ?? Shader.Find("Standard"); } private Color GetPalettePreviewColor(byte colorIndex) { return Palette.GetMaterialPreviewColor(colorIndex, selectedMaterial); } private Color GetPalettePreviewEmission(Color previewColor) { return Color.black; } private Material GetOrCreatePaletteCursorMaterial() { if (paletteCursorMaterial != null) { return paletteCursorMaterial; } Shader shader = Shader.Find("Universal Render Pipeline/Unlit") ?? Shader.Find("Universal Render Pipeline/Particles/Unlit") ?? Shader.Find("Sprites/Default") ?? Shader.Find("Standard"); paletteCursorMaterial = new Material(shader) { name = "BlockSpace_PaletteCursor" }; if (paletteCursorMaterial.HasProperty("_Surface")) { paletteCursorMaterial.SetFloat("_Surface", 0f); } if (paletteCursorMaterial.HasProperty("_Cull")) { paletteCursorMaterial.SetFloat("_Cull", 0f); } return paletteCursorMaterial; } private void EnsureGroupSelector() { VRGroupSelector vRGroupSelector = GetComponent(); if (vRGroupSelector == null) { vRGroupSelector = base.gameObject.AddComponent(); } vRGroupSelector.Configure(groupManager, trackingOrigin, rightHandAnchor); vRGroupSelector.SetSelectionRadius(brushRadius); } private void EnsureNodeGraph() { NodeGraphManager nodeGraphManager = GetComponent(); if (nodeGraphManager == null) { nodeGraphManager = base.gameObject.AddComponent(); } nodeGraphManager.Configure(groupManager, this, trackingOrigin, leftHandAnchor, rightHandAnchor); if (GetComponent() == null) { base.gameObject.AddComponent(); } } private void EnsureDesktopRigController() { GameObject gameObject = base.gameObject; DesktopRigController desktopRigController = gameObject.GetComponent(); if (desktopRigController == null) { desktopRigController = gameObject.AddComponent(); } desktopRigController.Configure(base.transform, (Camera.main != null) ? Camera.main.transform : null, leftHandAnchor, rightHandAnchor); } private void EnsureAvatarCustomizer() { GameObject gameObject = GameObject.Find("Avatar Customizer"); if (!(gameObject == null)) { AvatarCustomizerController avatarCustomizerController = gameObject.GetComponent(); if (avatarCustomizerController == null) { avatarCustomizerController = gameObject.AddComponent(); } avatarCustomizerController.Configure(groupManager, this); InventoryController inventoryController = gameObject.GetComponent(); if (inventoryController == null) { inventoryController = gameObject.AddComponent(); } inventoryController.Configure(groupManager, this); } } private void ApplyBrushIndicatorColor(Color32 color) { Material orCreateBrushIndicatorMaterial = GetOrCreateBrushIndicatorMaterial(); if (orCreateBrushIndicatorMaterial.HasProperty("_BaseColor")) { orCreateBrushIndicatorMaterial.SetColor("_BaseColor", color); } if (orCreateBrushIndicatorMaterial.HasProperty("_Color")) { orCreateBrushIndicatorMaterial.SetColor("_Color", color); } } private static void ApplyIndicatorMaterialColor(Material material, Color32 color) { if (!(material == null)) { if (material.HasProperty("_BaseColor")) { material.SetColor("_BaseColor", color); } if (material.HasProperty("_Color")) { material.SetColor("_Color", color); } } } private void SetBrushIndicatorVisible(bool visible) { if (brushIndicatorRenderer != null) { brushIndicatorRenderer.enabled = visible; } } private void SetPaletteVisible(bool visible) { if (paletteRoot != null) { paletteRoot.gameObject.SetActive(visible); } } private void HandleCreatorToggle() { if (WorldLoadingScreenController.Instance != null && WorldLoadingScreenController.Instance.IsLoading) { return; } if (!useXRInput) { if (!Input.GetKeyDown(KeyCode.LeftAlt)) { return; } creatorMode = !creatorMode; groupManager.SetCreatorMode(creatorMode); creatorMode = groupManager.CreatorMode; if (!creatorMode) { if (paletteOpen) { paletteOpen = false; leftPaletteAxisLatched = false; ApplyPaletteLocomotionGate(disableMovement: false); SetPaletteVisible(visible: false); } ApplySelectionLocomotionGate(disableMovement: false); CancelStroke(); if (erasing) { EndErase(); } if (painting) { EndPaint(); } VRGroupSelector instance = VRGroupSelector.Instance; if (instance != null && instance.HasSelection) { instance.ClearSelectionForToolUse(); } NodeGraphManager instance2 = NodeGraphManager.Instance; if (instance2 != null) { instance2.CancelEditingInteractions(clearSelection: true); } } return; } bool flag = ReadGripHeld(leftHand); bool flag2 = ReadFloat(leftHand, CommonUsages.trigger) >= triggerThreshold; if (flag && !leftGripHeldPrevious) { lastLeftGripPressTime = Time.unscaledTime; } if (flag2 && !leftTriggerHeldPrevious) { lastLeftTriggerPressTime = Time.unscaledTime; } if (!leftCreatorChordConsumed && flag && flag2) { float num = Mathf.Max(lastLeftGripPressTime, lastLeftTriggerPressTime); if (num > float.NegativeInfinity && Mathf.Abs(lastLeftGripPressTime - lastLeftTriggerPressTime) <= creatorToggleChordWindow && Time.unscaledTime - num <= creatorToggleChordWindow) { creatorMode = !creatorMode; groupManager.SetCreatorMode(creatorMode); creatorMode = groupManager.CreatorMode; if (!creatorMode) { if (paletteOpen) { paletteOpen = false; leftPaletteAxisLatched = false; ApplyPaletteLocomotionGate(disableMovement: false); SetPaletteVisible(visible: false); } ApplySelectionLocomotionGate(disableMovement: false); CancelStroke(); if (erasing) { EndErase(); } VRGroupSelector instance3 = VRGroupSelector.Instance; if (instance3 != null && instance3.HasSelection) { instance3.ClearSelectionForToolUse(); } NodeGraphManager instance4 = NodeGraphManager.Instance; if (instance4 != null) { instance4.CancelEditingInteractions(clearSelection: true); } } leftCreatorChordConsumed = true; } } if (!flag && !flag2) { leftCreatorChordConsumed = false; } leftGripHeldPrevious = flag; leftTriggerHeldPrevious = flag2; } private void HandlePaletteToggleAndNavigation() { bool flag = (useXRInput ? ReadBool(leftHand, CommonUsages.secondaryButton) : Input.GetKeyDown(KeyCode.Tab)); if (flag && !leftPrimaryPrevious) { paletteOpen = !paletteOpen; leftPaletteAxisLatched = false; ApplyPaletteLocomotionGate(paletteOpen); if (paletteOpen) { SyncPaletteCursorToSelectedColor(); UpdatePaletteSelectionVisuals(); } else { SetPaletteVisible(visible: false); } leftPrimaryPrevious = flag; return; } leftPrimaryPrevious = flag; if (!paletteOpen) { SetPaletteVisible(visible: false); return; } if (!useXRInput) { bool flag2 = false; if (Input.GetKeyDown(KeyCode.LeftArrow)) { paletteColumn = WrapPaletteIndex(paletteColumn - 1, 8); flag2 = true; } else if (Input.GetKeyDown(KeyCode.RightArrow)) { paletteColumn = WrapPaletteIndex(paletteColumn + 1, 8); flag2 = true; } else if (Input.GetKeyDown(KeyCode.UpArrow)) { paletteRow = WrapPaletteIndex(paletteRow - 1, 8); flag2 = true; } else if (Input.GetKeyDown(KeyCode.DownArrow)) { paletteRow = WrapPaletteIndex(paletteRow + 1, 8); flag2 = true; } if (flag2) { selectedColorIndex = (byte)(paletteRow * 8 + paletteColumn); UpdatePaletteSelectionVisuals(); } return; } Vector2 vector = ReadVector2(leftHand, CommonUsages.primary2DAxis); if (Mathf.Abs(vector.x) < thumbstickDeadZone && Mathf.Abs(vector.y) < thumbstickDeadZone) { leftPaletteAxisLatched = false; } else if (!leftPaletteAxisLatched) { leftPaletteAxisLatched = true; if (Mathf.Abs(vector.x) >= Mathf.Abs(vector.y)) { paletteColumn = WrapPaletteIndex(paletteColumn + ((!(vector.x > 0f)) ? 1 : (-1)), 8); } else { paletteRow = WrapPaletteIndex(paletteRow + ((!(vector.y > 0f)) ? 1 : (-1)), 8); } selectedColorIndex = (byte)(paletteRow * 8 + paletteColumn); UpdatePaletteSelectionVisuals(); } } private void HandleMaterialCycle() { bool flag = (useXRInput ? ReadBool(leftHand, CommonUsages.primary2DAxisClick) : Input.GetKeyDown(KeyCode.E)); if (flag && !leftThumbstickClickPrevious) { selectedMaterial = (selectedMaterial + 1) & VoxelMaterial.Invisible; UpdatePaletteSelectionVisuals(); } leftThumbstickClickPrevious = flag; } private void ApplyBrushRadiusLimits() { float num = ((groupManager != null) ? groupManager.VoxelSize : 0.125f); if (avatarEditorTargetGroup != null) { num *= 0.5f; } minBrushRadius = num * 0.5f; maxBrushRadius = num * 5f; brushRadiusStep = num * 0.5f; brushRadius = Mathf.Clamp(brushRadius, minBrushRadius, maxBrushRadius); brushDistance = Mathf.Clamp(brushDistance, minBrushDistance, maxBrushDistance); } private Vector3 AdjustBrushPointForAvatarEditing(Vector3 worldPoint) { if (avatarEditorTargetGroup == null) { return worldPoint; } Vector3 position = ((avatarEditorTargetGroup.transform.parent != null) ? avatarEditorTargetGroup.transform.parent : avatarEditorTargetGroup.transform).position; return position + (worldPoint - position) * 2f; } private void HandleBrushRadius() { if (!creatorMode || Time.unscaledTime < nextRadiusChangeTime) { return; } if (!useXRInput) { float y = Input.mouseScrollDelta.y; if (!(Mathf.Abs(y) < 0.01f)) { float num = Mathf.Sign(y); if (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl)) { brushDistance = Mathf.Clamp(brushDistance + num * brushDistanceStep, minBrushDistance, maxBrushDistance); } else { brushRadius = Mathf.Clamp(brushRadius + num * brushRadiusStep, minBrushRadius, maxBrushRadius); } nextRadiusChangeTime = Time.unscaledTime + radiusRepeatDelay; } } else { Vector2 vector = ReadVector2(rightHand, CommonUsages.primary2DAxis); if (vector.y >= thumbstickDeadZone) { brushRadius = Mathf.Min(maxBrushRadius, brushRadius + brushRadiusStep); nextRadiusChangeTime = Time.unscaledTime + radiusRepeatDelay; } else if (vector.y <= 0f - thumbstickDeadZone) { brushRadius = Mathf.Max(minBrushRadius, brushRadius - brushRadiusStep); nextRadiusChangeTime = Time.unscaledTime + radiusRepeatDelay; } } } private void SyncSelectionRadius() { VRGroupSelector instance = VRGroupSelector.Instance; if (instance != null) { instance.SetSelectionRadius(brushRadius); } } private void UpdatePaletteUi() { EnsurePaletteUi(); if (!paletteOpen) { SetPaletteVisible(visible: false); return; } if (!TryGetPalettePose(out var worldPosition, out var worldRotation)) { SetPaletteVisible(visible: false); return; } SetPaletteVisible(visible: true); paletteRoot.SetPositionAndRotation(worldPosition, worldRotation); } private void SyncPaletteCursorToSelectedColor() { int num = Mathf.Clamp(selectedColorIndex, 0, 63); paletteRow = num / 8; paletteColumn = num % 8; selectedColorIndex = (byte)num; } private void UpdatePaletteSelectionVisuals() { if (paletteRoot == null) { return; } Material orCreatePaletteCellMaterial = GetOrCreatePaletteCellMaterial(); ConfigurePaletteCellMaterial(orCreatePaletteCellMaterial); for (int i = 0; i < 64; i++) { MeshRenderer meshRenderer = paletteCellRenderers[i]; MaterialPropertyBlock materialPropertyBlock = paletteCellBlocks[i]; if (meshRenderer == null || materialPropertyBlock == null) { continue; } int num = i / 8; int num2 = i % 8; bool flag = num == paletteRow && num2 == paletteColumn; Transform obj = meshRenderer.transform; Vector3 paletteCellLocalPosition = GetPaletteCellLocalPosition(num, num2); paletteCellLocalPosition.z = (flag ? 0.01f : 0f); obj.localPosition = paletteCellLocalPosition; obj.localScale = Vector3.one * (flag ? 0.05f : 0.03f); materialPropertyBlock.Clear(); Color palettePreviewColor = GetPalettePreviewColor((byte)i); Color palettePreviewEmission = GetPalettePreviewEmission(palettePreviewColor); if (meshRenderer.sharedMaterial != null) { if (meshRenderer.sharedMaterial.HasProperty("_BaseColor")) { materialPropertyBlock.SetColor("_BaseColor", palettePreviewColor); } if (meshRenderer.sharedMaterial.HasProperty("_Color")) { materialPropertyBlock.SetColor("_Color", palettePreviewColor); } if (meshRenderer.sharedMaterial.HasProperty("_EmissionColor")) { materialPropertyBlock.SetColor("_EmissionColor", palettePreviewEmission); } if (meshRenderer.sharedMaterial.HasProperty("_EmissiveColor")) { materialPropertyBlock.SetColor("_EmissiveColor", palettePreviewEmission); } } meshRenderer.SetPropertyBlock(materialPropertyBlock); } UpdatePaletteCursorVisual(); } private void UpdatePaletteCursorVisual() { if (!(paletteCursorRenderer == null)) { paletteCursorRenderer.enabled = false; float num = 0.025f; Vector3 vector = GetPaletteCellLocalPosition(paletteRow, paletteColumn) + new Vector3(0f, 0f, 0.02f); paletteCursorRenderer.SetPosition(0, vector + new Vector3(0f - num, 0f - num, 0f)); paletteCursorRenderer.SetPosition(1, vector + new Vector3(0f - num, num, 0f)); paletteCursorRenderer.SetPosition(2, vector + new Vector3(num, num, 0f)); paletteCursorRenderer.SetPosition(3, vector + new Vector3(num, 0f - num, 0f)); Color32 color = Palette.GetColor(selectedColorIndex); Color32 color2 = (((0.2126f * (float)(int)color.r + 0.7152f * (float)(int)color.g + 0.0722f * (float)(int)color.b) / 255f > 0.55f) ? PaletteCursorDarkColor : PaletteCursorLightColor); paletteCursorRenderer.startColor = color2; paletteCursorRenderer.endColor = color2; } } private Vector3 GetPaletteCellLocalPosition(int row, int column) { float num = 0.03857143f; float x = -0.135f + (float)column * num; float y = 0.135f - (float)row * num; return new Vector3(x, y, 0f); } private float GetPaletteCellSize() { return 0.03f; } private void RasterizeSegment(Vector3 start, Vector3 end, float radius) { if (activeStrokeGroup == null) { return; } if (activeStrokeGroup == avatarEditorTargetGroup) { RasterizeAvatarEditorSegment(start, end, radius); return; } float voxelSize = groupManager.VoxelSize; float num = Mathf.Max(voxelSize * 0.5f, radius * 0.5f); float num2 = Vector3.Distance(start, end); int num3 = Mathf.Max(1, Mathf.CeilToInt(num2 / num)); bool flag = false; for (int i = 0; i <= num3; i++) { float t = ((num3 == 0) ? 0f : ((float)i / (float)num3)); Vector3 worldCenter = Vector3.Lerp(start, end, t); flag |= AddSphere(worldCenter, radius, voxelSize); } if (flag) { groupManager.RefreshStrokeGroup(activeStrokeGroup); UpdateStrokePreviewBroadcast(end); } } private bool AddSphere(Vector3 worldCenter, float radius, float voxelSize) { float num = radius * radius; int x = Mathf.RoundToInt(worldCenter.x / voxelSize - 0.5f); int y = Mathf.RoundToInt(worldCenter.y / voxelSize - 0.5f); int z = Mathf.RoundToInt(worldCenter.z / voxelSize - 0.5f); Vector3Int vector3Int = new Vector3Int(x, y, z); bool flag = false; if (strokeCells.Add(vector3Int)) { flag |= groupManager.AddVoxelToStrokeGroup(activeStrokeGroup, vector3Int, activeStrokeVoxelValue); } int num2 = Mathf.FloorToInt((worldCenter.x - radius) / voxelSize); int num3 = Mathf.FloorToInt((worldCenter.y - radius) / voxelSize); int num4 = Mathf.FloorToInt((worldCenter.z - radius) / voxelSize); int num5 = Mathf.FloorToInt((worldCenter.x + radius) / voxelSize); int num6 = Mathf.FloorToInt((worldCenter.y + radius) / voxelSize); int num7 = Mathf.FloorToInt((worldCenter.z + radius) / voxelSize); for (int i = num4; i <= num7; i++) { for (int j = num3; j <= num6; j++) { for (int k = num2; k <= num5; k++) { if (!((new Vector3(((float)k + 0.5f) * voxelSize, ((float)j + 0.5f) * voxelSize, ((float)i + 0.5f) * voxelSize) - worldCenter).sqrMagnitude > num)) { Vector3Int vector3Int2 = new Vector3Int(k, j, i); if (strokeCells.Add(vector3Int2)) { flag |= groupManager.AddVoxelToStrokeGroup(activeStrokeGroup, vector3Int2, activeStrokeVoxelValue); } } } } } return flag; } private void RasterizeAvatarEditorSegment(Vector3 start, Vector3 end, float radius) { if (!(avatarEditorTargetGroup == null) && !(groupManager == null)) { float voxelSize = groupManager.VoxelSize; float num = Mathf.Max(voxelSize * 0.5f, radius * 0.5f); float num2 = Vector3.Distance(start, end); int num3 = Mathf.Max(1, Mathf.CeilToInt(num2 / num)); bool flag = false; for (int i = 0; i <= num3; i++) { float t = ((num3 == 0) ? 0f : ((float)i / (float)num3)); Vector3 worldCenter = Vector3.Lerp(start, end, t); flag |= AddSphereToAvatarGroup(avatarEditorTargetGroup, worldCenter, radius, voxelSize, activeStrokeVoxelValue); } if (flag) { avatarEditUndoSessionChanged = true; avatarEditorTargetGroup.RebuildMesh(); DisableAvatarGroupCollider(avatarEditorTargetGroup); } } } private bool AddSphereToAvatarGroup(VoxelGroup group, Vector3 worldCenter, float radius, float voxelSize, byte voxelValue) { if (group == null) { return false; } Transform transform = ((group.UsesLocalGridTransform && group.transform.parent != null) ? group.transform.parent : null); Vector3 vector = ((transform != null) ? transform.InverseTransformPoint(worldCenter) : worldCenter) / voxelSize; float num = radius * radius; float num2 = radius / voxelSize; int x = Mathf.RoundToInt(vector.x - 0.5f); int y = Mathf.RoundToInt(vector.y - 0.5f); int z = Mathf.RoundToInt(vector.z - 0.5f); bool flag = group.SetStoredVoxel(new Vector3Int(x, y, z), voxelValue, markMeshDirty: false, markColliderDirty: false); int num3 = Mathf.FloorToInt(vector.x - num2); int num4 = Mathf.FloorToInt(vector.y - num2); int num5 = Mathf.FloorToInt(vector.z - num2); int num6 = Mathf.FloorToInt(vector.x + num2); int num7 = Mathf.FloorToInt(vector.y + num2); int num8 = Mathf.FloorToInt(vector.z + num2); for (int i = num5; i <= num8; i++) { for (int j = num4; j <= num7; j++) { for (int k = num3; k <= num6; k++) { Vector3 vector2 = new Vector3(((float)k + 0.5f) * voxelSize, ((float)j + 0.5f) * voxelSize, ((float)i + 0.5f) * voxelSize); if (!((((transform != null) ? transform.TransformPoint(vector2) : vector2) - worldCenter).sqrMagnitude > num)) { flag |= group.SetStoredVoxel(new Vector3Int(k, j, i), voxelValue, markMeshDirty: false, markColliderDirty: false); } } } } return flag; } private void UpdateAvatarEditorErase(Vector3 start, Vector3 end) { if (!(avatarEditorTargetGroup == null) && !(groupManager == null)) { float num = Mathf.Max(groupManager.VoxelSize * 0.5f, brushRadius * 0.5f); float num2 = Vector3.Distance(start, end); int num3 = Mathf.Max(1, Mathf.CeilToInt(num2 / num)); bool flag = false; for (int i = 0; i <= num3; i++) { float t = ((num3 == 0) ? 0f : ((float)i / (float)num3)); Vector3 worldCenter = Vector3.Lerp(start, end, t); flag |= avatarEditorTargetGroup.EraseSphere(worldCenter, brushRadius, markMeshDirty: false, markColliderDirty: false); } if (flag) { avatarEditUndoSessionChanged = true; avatarEditorTargetGroup.RebuildMesh(); DisableAvatarGroupCollider(avatarEditorTargetGroup); } } } private void UpdateAvatarEditorPaint(Vector3 start, Vector3 end) { if (!(avatarEditorTargetGroup == null) && !(groupManager == null)) { byte currentBrushVoxelValue = GetCurrentBrushVoxelValue(); float num = Mathf.Max(groupManager.VoxelSize * 0.5f, brushRadius * 0.5f); float num2 = Vector3.Distance(start, end); int num3 = Mathf.Max(1, Mathf.CeilToInt(num2 / num)); bool flag = false; for (int i = 0; i <= num3; i++) { float t = ((num3 == 0) ? 0f : ((float)i / (float)num3)); Vector3 worldCenter = Vector3.Lerp(start, end, t); flag |= avatarEditorTargetGroup.PaintSphere(worldCenter, brushRadius, currentBrushVoxelValue, markMeshDirty: false, markColliderDirty: false); } if (flag) { avatarEditUndoSessionChanged = true; avatarEditorTargetGroup.RebuildMesh(); DisableAvatarGroupCollider(avatarEditorTargetGroup); } } } private void FinishActiveAvatarEditSessions() { if (drawingStroke && activeStrokeGroup == avatarEditorTargetGroup) { FinalizeStroke(); } if (erasing) { EndErase(); } if (painting) { EndPaint(); } } private bool HandleAvatarEditUndo(bool triggerHeld, bool gripHeld) { avatarUndoChordWindow = Mathf.Max(0.01f, avatarUndoChordWindow); if (avatarEditorTargetGroup == null || !creatorMode) { if (!gripHeld && !triggerHeld) { avatarUndoChordActive = false; suppressAvatarUndoUntilNeutral = false; } rightGripHeldPrevious = gripHeld; rightTriggerHeldPrevious = triggerHeld; return false; } if (gripHeld && !rightGripHeldPrevious) { lastRightGripPressTime = Time.unscaledTime; } if (triggerHeld && !rightTriggerHeldPrevious) { lastRightTriggerPressTime = Time.unscaledTime; } if (!avatarUndoChordActive && !drawingStroke && !erasing && !painting && gripHeld && triggerHeld) { float num = Mathf.Max(lastRightGripPressTime, lastRightTriggerPressTime); if (num > float.NegativeInfinity && Mathf.Abs(lastRightGripPressTime - lastRightTriggerPressTime) <= avatarUndoChordWindow && Time.unscaledTime - num <= avatarUndoChordWindow) { avatarUndoChordActive = true; suppressAvatarUndoUntilNeutral = true; UndoAvatarEditorAction(); } } if (!gripHeld && !triggerHeld) { avatarUndoChordActive = false; suppressAvatarUndoUntilNeutral = false; } rightGripHeldPrevious = gripHeld; rightTriggerHeldPrevious = triggerHeld; if (!avatarUndoChordActive) { return suppressAvatarUndoUntilNeutral; } return true; } private bool HandleDesktopUndoShortcut() { if (useXRInput) { return false; } bool num = Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl); bool flag = Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift); if (!num) { return false; } NodeGraphManager instance = NodeGraphManager.Instance; if (instance != null) { if ((Input.GetKeyDown(KeyCode.Y) || (flag && Input.GetKeyDown(KeyCode.Z))) && instance.RedoLastCircuitEdit()) { return true; } if (Input.GetKeyDown(KeyCode.Z) && instance.UndoLastCircuitEdit()) { return true; } } if (!Input.GetKeyDown(KeyCode.Z)) { return false; } if (avatarEditorTargetGroup != null) { return UndoAvatarEditorAction(); } if (groupManager != null) { return groupManager.UndoLastOperation(); } return false; } private void BeginAvatarEditUndoSession(VoxelGroup group) { if (!(group == null) && (!avatarEditUndoSessionActive || avatarEditUndoBeforeSnapshot == null || !(avatarEditUndoBeforeSnapshot.group == group))) { avatarEditUndoBeforeSnapshot = CaptureAvatarEditSnapshot(group); avatarEditUndoSessionActive = avatarEditUndoBeforeSnapshot != null; avatarEditUndoSessionChanged = false; } } private void CompleteAvatarEditUndoSession(VoxelGroup group) { if (!avatarEditUndoSessionActive || avatarEditUndoBeforeSnapshot == null) { return; } AvatarEditSnapshot avatarEditSnapshot = avatarEditUndoBeforeSnapshot; avatarEditUndoBeforeSnapshot = null; avatarEditUndoSessionActive = false; if (!avatarEditUndoSessionChanged || avatarEditSnapshot.group != group) { avatarEditUndoSessionChanged = false; return; } AvatarEditSnapshot avatarEditSnapshot2 = CaptureAvatarEditSnapshot(group); avatarEditUndoSessionChanged = false; if (avatarEditSnapshot2 != null && !AvatarSnapshotsMatch(avatarEditSnapshot, avatarEditSnapshot2)) { avatarUndoStack.Push(new AvatarEditUndoRecord { before = avatarEditSnapshot, after = avatarEditSnapshot2 }); } } private static AvatarEditSnapshot CaptureAvatarEditSnapshot(VoxelGroup group) { if (group == null) { return null; } byte[] voxels = ((group.voxels != null && group.voxels.Length != 0) ? ((byte[])group.voxels.Clone()) : new byte[0]); return new AvatarEditSnapshot { group = group, position = group.position, packedRotation = group.packedRotation, size = group.size, voxels = voxels }; } private void RestoreAvatarEditSnapshot(AvatarEditSnapshot snapshot) { if (snapshot != null && !(snapshot.group == null)) { snapshot.group.OverwriteData(snapshot.position, snapshot.packedRotation, snapshot.size, (snapshot.voxels != null) ? ((byte[])snapshot.voxels.Clone()) : new byte[0], queueRebuild: false); snapshot.group.SetGreedyRenderMeshing(enabled: true); snapshot.group.RebuildMesh(); snapshot.group.RebuildCollider(); DisableAvatarGroupCollider(snapshot.group); } } private static bool AvatarSnapshotsMatch(AvatarEditSnapshot before, AvatarEditSnapshot after) { if (before == null || after == null) { return false; } if (before.group != after.group || before.position != after.position || before.packedRotation != after.packedRotation || before.size != after.size) { return false; } int num = ((before.voxels != null) ? before.voxels.Length : 0); int num2 = ((after.voxels != null) ? after.voxels.Length : 0); if (num != num2) { return false; } for (int i = 0; i < num; i++) { if (before.voxels[i] != after.voxels[i]) { return false; } } return true; } private void FinalizeAvatarEditorGroup(VoxelGroup group) { if (!(group == null)) { if (!group.TrimToContents()) { group.OverwriteData(Vector3Int.zero, 0, Vector3Int.zero, new byte[0], queueRebuild: false); } group.SetGreedyRenderMeshing(enabled: true); group.RebuildMesh(); group.RebuildCollider(); DisableAvatarGroupCollider(group); CompleteAvatarEditUndoSession(group); } } private static void DisableAvatarGroupCollider(VoxelGroup group) { if (!(group == null) && !(group.meshCollider == null)) { group.meshCollider.enabled = false; } } private void EnsureDevices() { if (!rightHand.isValid || !leftHand.isValid) { AcquireDevices(); } } private void AcquireDevices() { rightHand = InputDevices.GetDeviceAtXRNode(XRNode.RightHand); leftHand = InputDevices.GetDeviceAtXRNode(XRNode.LeftHand); } private void RefreshMoveProviders() { moveProviders.Clear(); moveProviderEnabledStates.Clear(); AddMoveProvidersFrom(base.gameObject.GetComponentsInParent(includeInactive: true)); AddMoveProvidersFrom(base.gameObject.GetComponentsInChildren(includeInactive: true)); AddMoveProvidersFrom(Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None)); } private void RefreshSelectionLocomotionProviders() { selectionLocomotionProviders.Clear(); selectionLocomotionStates.Clear(); AddSelectionLocomotionProvidersFrom(Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None)); } private void AddMoveProvidersFrom(ContinuousMoveProvider[] providers) { if (providers == null) { return; } foreach (ContinuousMoveProvider continuousMoveProvider in providers) { if (!(continuousMoveProvider == null) && !moveProviders.Contains(continuousMoveProvider)) { moveProviders.Add(continuousMoveProvider); } } } private void AddSelectionLocomotionProvidersFrom(LocomotionProvider[] providers) { if (providers == null) { return; } foreach (LocomotionProvider locomotionProvider in providers) { if (!(locomotionProvider == null) && !(locomotionProvider is ContinuousMoveProvider) && !selectionLocomotionProviders.Contains(locomotionProvider)) { selectionLocomotionProviders.Add(locomotionProvider); } } } private void ApplyPaletteLocomotionGate(bool disableMovement) { if (useXRInput || !disableMovement) { ApplyPaletteMoveObjectGate(disableMovement); } if (disableMovement) { RefreshMoveProviders(); } else if (moveProviders.Count == 0) { RefreshMoveProviders(); } for (int num = moveProviders.Count - 1; num >= 0; num--) { ContinuousMoveProvider continuousMoveProvider = moveProviders[num]; bool value; if (continuousMoveProvider == null) { moveProviders.RemoveAt(num); } else if (disableMovement) { if (!moveProviderEnabledStates.ContainsKey(continuousMoveProvider)) { moveProviderEnabledStates.Add(continuousMoveProvider, continuousMoveProvider.enabled); } continuousMoveProvider.enabled = false; } else if (moveProviderEnabledStates.TryGetValue(continuousMoveProvider, out value)) { continuousMoveProvider.enabled = value; } else { continuousMoveProvider.enabled = true; } } if (!disableMovement) { moveProviderEnabledStates.Clear(); } } private void ApplyPaletteMoveObjectGate(bool disableMovement) { if (disableMovement) { RefreshPaletteMoveObjects(); } for (int num = paletteMoveObjects.Count - 1; num >= 0; num--) { GameObject gameObject = paletteMoveObjects[num]; bool value; if (gameObject == null) { paletteMoveObjects.RemoveAt(num); } else if (disableMovement) { if (!paletteMoveObjectStates.ContainsKey(gameObject)) { paletteMoveObjectStates.Add(gameObject, gameObject.activeSelf); } gameObject.SetActive(value: false); } else if (paletteMoveObjectStates.TryGetValue(gameObject, out value)) { gameObject.SetActive(value); } } if (!disableMovement) { paletteMoveObjectStates.Clear(); } } private void RefreshPaletteMoveObjects() { paletteMoveObjects.Clear(); Transform transform = ResolveVrPlayerRoot(); if (transform != null) { AddPaletteMoveObjectFrom(transform); } Transform[] array = Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None); foreach (Transform transform2 in array) { if (!(transform2 == null) && !(transform2.name != "Move") && !(transform2.parent == null) && !(transform2.parent.name != "Locomotion") && !paletteMoveObjects.Contains(transform2.gameObject)) { paletteMoveObjects.Add(transform2.gameObject); } } } private void AddPaletteMoveObjectFrom(Transform root) { Transform transform = root.Find("Locomotion/Move"); if (transform != null && !paletteMoveObjects.Contains(transform.gameObject)) { paletteMoveObjects.Add(transform.gameObject); } } private Transform ResolveVrPlayerRoot() { if (PhotonVRManager.Manager != null) { if (PhotonVRManager.Manager.LocalPlayer != null) { Transform transform = PhotonVRManager.Manager.LocalPlayer.transform; Transform transform2 = FindAncestorNamed(transform, "VR Player"); if (!(transform2 != null)) { return transform.root; } return transform2; } if (PhotonVRManager.Manager.Head != null) { Transform head = PhotonVRManager.Manager.Head; Transform transform3 = FindAncestorNamed(head, "VR Player"); if (!(transform3 != null)) { return head.root; } return transform3; } } return null; } private static Transform FindAncestorNamed(Transform start, string name) { Transform transform = start; while (transform != null) { if (transform.name == name) { return transform; } transform = transform.parent; } return null; } private void ApplySelectionLocomotionGate(bool disableMovement) { if (disableMovement == selectionLocomotionBlocked) { return; } if (disableMovement) { RefreshSelectionLocomotionProviders(); } for (int num = selectionLocomotionProviders.Count - 1; num >= 0; num--) { LocomotionProvider locomotionProvider = selectionLocomotionProviders[num]; bool value; if (locomotionProvider == null) { selectionLocomotionProviders.RemoveAt(num); } else if (disableMovement) { if (!selectionLocomotionStates.ContainsKey(locomotionProvider)) { selectionLocomotionStates.Add(locomotionProvider, locomotionProvider.enabled); } locomotionProvider.enabled = false; } else if (selectionLocomotionStates.TryGetValue(locomotionProvider, out value)) { locomotionProvider.enabled = value; } else { locomotionProvider.enabled = true; } } selectionLocomotionBlocked = disableMovement; if (!disableMovement) { selectionLocomotionStates.Clear(); } } private byte GetCurrentBrushVoxelValue() { selectedColorIndex = (byte)GetPaletteCursorIndex(); return PackedVoxel.Create(selectedColorIndex, selectedMaterial); } public byte GetSelectedVoxelValue() { return GetCurrentBrushVoxelValue(); } public bool TryGetDesktopSelectionPoint(out Vector3 worldPosition) { worldPosition = Vector3.zero; if (useXRInput) { return false; } Quaternion worldRotation; return TryGetDesktopBrushPose(out worldPosition, out worldRotation); } public bool TryGetSpawnGridPosition(out Vector3Int gridPosition) { gridPosition = Vector3Int.zero; if (groupManager == null) { return false; } EnsureDevices(); if (!TryGetActiveBrushPose(out var worldPosition, out var _)) { return false; } float voxelSize = groupManager.VoxelSize; gridPosition = new Vector3Int(Mathf.FloorToInt(worldPosition.x / voxelSize), Mathf.FloorToInt(worldPosition.y / voxelSize), Mathf.FloorToInt(worldPosition.z / voxelSize)); return true; } private bool TryGetActiveBrushPose(out Vector3 worldPosition, out Quaternion worldRotation) { if (!useXRInput) { return TryGetDesktopBrushPose(out worldPosition, out worldRotation); } return TryGetBrushPose(rightHand, XRNode.RightHand, rightHandAnchor, out worldPosition, out worldRotation); } private int GetPaletteCursorIndex() { return Mathf.Clamp(paletteRow * 8 + paletteColumn, 0, 63); } private static bool ReadGripHeld(InputDevice device) { if (!device.isValid) { return false; } bool value; bool num = device.TryGetFeatureValue(CommonUsages.gripButton, out value) && value; float value2; bool flag = device.TryGetFeatureValue(CommonUsages.grip, out value2) && value2 >= 0.75f; return num || flag; } private bool TryGetBrushPose(InputDevice device, XRNode node, Transform anchor, out Vector3 worldPosition, out Quaternion worldRotation) { if (!TryGetControllerPose(device, node, anchor, out worldPosition, out worldRotation)) { worldPosition = Vector3.zero; worldRotation = Quaternion.identity; return false; } worldPosition += worldRotation * Vector3.forward * brushForwardOffset; return true; } private bool TryGetDesktopBrushPose(out Vector3 worldPosition, out Quaternion worldRotation) { if (!TryGetDesktopViewCamera(out var viewCamera)) { worldPosition = Vector3.zero; worldRotation = Quaternion.identity; return false; } Transform transform = viewCamera.transform; worldRotation = transform.rotation; if (Physics.Raycast(transform.position, transform.forward, out var hitInfo, brushDistance, GetDesktopRaycastMask(), QueryTriggerInteraction.Ignore)) { worldPosition = hitInfo.point + hitInfo.normal * desktopSurfaceOffset; lastDesktopBrushWasHit = true; } else { worldPosition = transform.position + transform.forward * brushDistance; lastDesktopBrushWasHit = false; } lastDesktopBrushWasHitValid = true; worldPosition = SnapToVoxelCenter(worldPosition); return true; } private bool HandleAvatarClearChord() { if (avatarEditorTargetGroup == null || !creatorMode) { avatarClearChordStartTime = -1f; suppressAvatarClearUntilNeutral = false; desktopAvatarClearStartTime = -1f; return false; } if (!useXRInput) { if (suppressAvatarClearUntilNeutral) { if (!Input.GetKey(KeyCode.O)) { suppressAvatarClearUntilNeutral = false; } return true; } if (!Input.GetKey(KeyCode.O)) { desktopAvatarClearStartTime = -1f; return false; } if (desktopAvatarClearStartTime < 0f) { desktopAvatarClearStartTime = Time.unscaledTime; return true; } if (Time.unscaledTime - desktopAvatarClearStartTime >= 5f) { ClearAvatarEditorGroupUndoable(); suppressAvatarClearUntilNeutral = true; desktopAvatarClearStartTime = -1f; } return true; } bool flag = ReadBool(leftHand, CommonUsages.primaryButton); bool flag2 = ReadBool(leftHand, CommonUsages.secondaryButton); bool flag3 = ReadBool(rightHand, CommonUsages.primaryButton); bool flag4 = ReadBool(rightHand, CommonUsages.secondaryButton); bool flag5 = flag && flag2 && flag3 && flag4; if (suppressAvatarClearUntilNeutral) { if (!flag && !flag2 && !flag3 && !flag4) { suppressAvatarClearUntilNeutral = false; } return true; } if (!flag5) { avatarClearChordStartTime = -1f; return false; } if (avatarClearChordStartTime < 0f) { avatarClearChordStartTime = Time.unscaledTime; return true; } if (Time.unscaledTime - avatarClearChordStartTime >= 5f) { ClearAvatarEditorGroupUndoable(); suppressAvatarClearUntilNeutral = true; avatarClearChordStartTime = -1f; } return true; } private void ClearAvatarEditorGroupUndoable() { if (avatarEditorTargetGroup == null) { return; } AvatarCustomizerController avatarCustomizerController = Object.FindObjectOfType(); if (avatarCustomizerController != null && avatarCustomizerController.TryGetEditorGroups(out var headBody, out var left, out var right)) { ClearSingleAvatarEditorGroupUndoable(headBody); ClearSingleAvatarEditorGroupUndoable(left); ClearSingleAvatarEditorGroupUndoable(right); AvatarVoxelPayload payload = AvatarVoxelStorage.CapturePayload(headBody, left, right); AvatarVoxelStorage.SaveLocal(payload); PhotonVoxelAvatar localOwnedInstance = PhotonVoxelAvatar.LocalOwnedInstance; if (localOwnedInstance != null) { localOwnedInstance.ApplyAndBroadcastAvatar(payload); } } else { ClearSingleAvatarEditorGroupUndoable(avatarEditorTargetGroup); } } private void ClearSingleAvatarEditorGroupUndoable(VoxelGroup group) { if (!(group == null)) { BeginAvatarEditUndoSession(group); group.OverwriteData(Vector3Int.zero, 0, Vector3Int.zero, new byte[0], queueRebuild: false); avatarEditUndoSessionChanged = true; FinalizeAvatarEditorGroup(group); } } private bool ShouldBreakContinuousEdit(Vector3 previous, Vector3 current, bool previousHadHit, bool currentHadHit) { if (previousHadHit != currentHadHit) { return true; } if (groupManager == null) { return false; } float num = Mathf.Max(groupManager.VoxelSize * 3f, brushRadius * 2f); return Vector3.Distance(previous, current) > num; } private int GetDesktopRaycastMask() { return desktopRaycastMask.value & -5; } private bool TryGetPalettePose(out Vector3 worldPosition, out Quaternion worldRotation) { if (!useXRInput) { if (!TryGetDesktopViewCamera(out var viewCamera)) { worldPosition = Vector3.zero; worldRotation = Quaternion.identity; return false; } Transform transform = viewCamera.transform; worldPosition = transform.position + transform.forward * 0.55f + -transform.right * 0.22f + transform.up * 0.08f; worldRotation = Quaternion.LookRotation(transform.forward, Vector3.up); return true; } if (!TryGetControllerPose(leftHand, XRNode.LeftHand, leftHandAnchor, out var worldPosition2, out var worldRotation2)) { worldPosition = Vector3.zero; worldRotation = Quaternion.identity; return false; } worldPosition = worldPosition2 + worldRotation2 * Vector3.up * 0.2f + worldRotation2 * Vector3.forward * 0.14f; Transform transform2 = ((Camera.main != null) ? Camera.main.transform : trackingOrigin); Vector3 vector = ((transform2 != null) ? (transform2.position - worldPosition) : (-(worldRotation2 * Vector3.forward))); if (vector.sqrMagnitude < 0.0001f) { vector = Vector3.back; } worldRotation = Quaternion.LookRotation(vector.normalized, Vector3.up); return true; } private bool TryGetControllerPose(InputDevice device, XRNode node, Transform anchor, out Vector3 worldPosition, out Quaternion worldRotation) { if (anchor != null) { worldPosition = anchor.position; worldRotation = anchor.rotation; return true; } if (!device.isValid) { device = InputDevices.GetDeviceAtXRNode(node); } if (device.isValid && device.TryGetFeatureValue(CommonUsages.devicePosition, out var value) && device.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 = Vector3.zero; worldRotation = Quaternion.identity; return false; } private bool TryGetDesktopViewCamera(out Camera viewCamera) { viewCamera = Camera.main; if (viewCamera != null) { return true; } Transform transform = ((PhotonVRManager.Manager != null) ? PhotonVRManager.Manager.Head : null); if (transform != null) { viewCamera = transform.GetComponent(); } return viewCamera != null; } private Vector3 SnapToVoxelCenter(Vector3 worldPoint) { float num = ((groupManager != null) ? groupManager.VoxelSize : 0.125f); if (avatarEditorTargetGroup != null) { num *= 0.5f; } return new Vector3((Mathf.Round(worldPoint.x / num - 0.5f) + 0.5f) * num, (Mathf.Round(worldPoint.y / num - 0.5f) + 0.5f) * num, (Mathf.Round(worldPoint.z / num - 0.5f) + 0.5f) * num); } private bool DetectXRInputActive() { if (!InputDevices.GetDeviceAtXRNode(XRNode.Head).isValid && !InputDevices.GetDeviceAtXRNode(XRNode.LeftHand).isValid) { return InputDevices.GetDeviceAtXRNode(XRNode.RightHand).isValid; } return true; } private static int WrapPaletteIndex(int value, int length) { if (length <= 0) { return 0; } value %= length; if (value >= 0) { return value; } return value + length; } private static bool ReadBool(InputDevice device, InputFeatureUsage usage) { bool value = default(bool); return device.isValid && device.TryGetFeatureValue(usage, out value) && value; } private static float ReadFloat(InputDevice device, InputFeatureUsage usage) { if (!device.isValid || !device.TryGetFeatureValue(usage, out var value)) { return 0f; } return value; } private static Vector2 ReadVector2(InputDevice device, InputFeatureUsage usage) { if (!device.isValid || !device.TryGetFeatureValue(usage, out var value)) { return Vector2.zero; } return value; } } }