EVERYTHING
This commit is contained in:
@@ -0,0 +1,2908 @@
|
||||
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<Vector3Int> strokeCells = new HashSet<Vector3Int>();
|
||||
|
||||
private readonly HashSet<VoxelGroup> activeEraseGroups = new HashSet<VoxelGroup>();
|
||||
|
||||
private readonly HashSet<VoxelGroup> activePaintGroups = new HashSet<VoxelGroup>();
|
||||
|
||||
private readonly Stack<AvatarEditUndoRecord> avatarUndoStack = new Stack<AvatarEditUndoRecord>();
|
||||
|
||||
private readonly List<ContinuousMoveProvider> moveProviders = new List<ContinuousMoveProvider>();
|
||||
|
||||
private readonly Dictionary<ContinuousMoveProvider, bool> moveProviderEnabledStates = new Dictionary<ContinuousMoveProvider, bool>();
|
||||
|
||||
private readonly List<LocomotionProvider> selectionLocomotionProviders = new List<LocomotionProvider>();
|
||||
|
||||
private readonly Dictionary<LocomotionProvider, bool> selectionLocomotionStates = new Dictionary<LocomotionProvider, bool>();
|
||||
|
||||
private readonly List<GameObject> paletteMoveObjects = new List<GameObject>();
|
||||
|
||||
private readonly Dictionary<GameObject, bool> paletteMoveObjectStates = new Dictionary<GameObject, bool>();
|
||||
|
||||
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<VoxelGroupManager>();
|
||||
}
|
||||
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<Collider>();
|
||||
if (component != null)
|
||||
{
|
||||
component.enabled = false;
|
||||
}
|
||||
brushIndicator = gameObject.transform;
|
||||
brushIndicatorRenderer = gameObject.GetComponent<MeshRenderer>();
|
||||
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<Collider>();
|
||||
if (component != null)
|
||||
{
|
||||
component.enabled = false;
|
||||
}
|
||||
colorPickIndicator = gameObject.transform;
|
||||
colorPickIndicatorRenderer = gameObject.GetComponent<MeshRenderer>();
|
||||
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<Collider>();
|
||||
if (component != null)
|
||||
{
|
||||
component.enabled = false;
|
||||
}
|
||||
MeshRenderer component2 = obj.GetComponent<MeshRenderer>();
|
||||
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<LineRenderer>();
|
||||
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<VRGroupSelector>();
|
||||
if (vRGroupSelector == null)
|
||||
{
|
||||
vRGroupSelector = base.gameObject.AddComponent<VRGroupSelector>();
|
||||
}
|
||||
vRGroupSelector.Configure(groupManager, trackingOrigin, rightHandAnchor);
|
||||
vRGroupSelector.SetSelectionRadius(brushRadius);
|
||||
}
|
||||
|
||||
private void EnsureNodeGraph()
|
||||
{
|
||||
NodeGraphManager nodeGraphManager = GetComponent<NodeGraphManager>();
|
||||
if (nodeGraphManager == null)
|
||||
{
|
||||
nodeGraphManager = base.gameObject.AddComponent<NodeGraphManager>();
|
||||
}
|
||||
nodeGraphManager.Configure(groupManager, this, trackingOrigin, leftHandAnchor, rightHandAnchor);
|
||||
if (GetComponent<NodeGraphPhotonSync>() == null)
|
||||
{
|
||||
base.gameObject.AddComponent<NodeGraphPhotonSync>();
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureDesktopRigController()
|
||||
{
|
||||
GameObject gameObject = base.gameObject;
|
||||
DesktopRigController desktopRigController = gameObject.GetComponent<DesktopRigController>();
|
||||
if (desktopRigController == null)
|
||||
{
|
||||
desktopRigController = gameObject.AddComponent<DesktopRigController>();
|
||||
}
|
||||
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<AvatarCustomizerController>();
|
||||
if (avatarCustomizerController == null)
|
||||
{
|
||||
avatarCustomizerController = gameObject.AddComponent<AvatarCustomizerController>();
|
||||
}
|
||||
avatarCustomizerController.Configure(groupManager, this);
|
||||
InventoryController inventoryController = gameObject.GetComponent<InventoryController>();
|
||||
if (inventoryController == null)
|
||||
{
|
||||
inventoryController = gameObject.AddComponent<InventoryController>();
|
||||
}
|
||||
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<ContinuousMoveProvider>(includeInactive: true));
|
||||
AddMoveProvidersFrom(base.gameObject.GetComponentsInChildren<ContinuousMoveProvider>(includeInactive: true));
|
||||
AddMoveProvidersFrom(Object.FindObjectsByType<ContinuousMoveProvider>(FindObjectsInactive.Include, FindObjectsSortMode.None));
|
||||
}
|
||||
|
||||
private void RefreshSelectionLocomotionProviders()
|
||||
{
|
||||
selectionLocomotionProviders.Clear();
|
||||
selectionLocomotionStates.Clear();
|
||||
AddSelectionLocomotionProvidersFrom(Object.FindObjectsByType<LocomotionProvider>(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<Transform>(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<AvatarCustomizerController>();
|
||||
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<Camera>();
|
||||
}
|
||||
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<bool> usage)
|
||||
{
|
||||
bool value = default(bool);
|
||||
return device.isValid && device.TryGetFeatureValue(usage, out value) && value;
|
||||
}
|
||||
|
||||
private static float ReadFloat(InputDevice device, InputFeatureUsage<float> usage)
|
||||
{
|
||||
if (!device.isValid || !device.TryGetFeatureValue(usage, out var value))
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private static Vector2 ReadVector2(InputDevice device, InputFeatureUsage<Vector2> usage)
|
||||
{
|
||||
if (!device.isValid || !device.TryGetFeatureValue(usage, out var value))
|
||||
{
|
||||
return Vector2.zero;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user