2909 lines
81 KiB
C#
2909 lines
81 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|