EVERYTHING
This commit is contained in:
@@ -0,0 +1,1907 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using BlockSpace.Gamemodes;
|
||||
using Photon.Pun;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BlockSpace.Voxels
|
||||
{
|
||||
[DisallowMultipleComponent]
|
||||
public sealed class VoxelGroupManager : MonoBehaviour
|
||||
{
|
||||
private enum UndoOperationType : byte
|
||||
{
|
||||
Stroke = 0,
|
||||
Erase = 1,
|
||||
Merge = 2
|
||||
}
|
||||
|
||||
private sealed class GroupSnapshot
|
||||
{
|
||||
public ushort id;
|
||||
|
||||
public Vector3Int position;
|
||||
|
||||
public byte packedRotation;
|
||||
|
||||
public Vector3Int size;
|
||||
|
||||
public byte[] voxels;
|
||||
}
|
||||
|
||||
public struct SavedGroupData
|
||||
{
|
||||
public Vector3Int position;
|
||||
|
||||
public byte packedRotation;
|
||||
|
||||
public Vector3Int size;
|
||||
|
||||
public byte[] voxels;
|
||||
|
||||
public bool IsValid
|
||||
{
|
||||
get
|
||||
{
|
||||
if (voxels != null && voxels.Length != 0 && size.x > 0 && size.y > 0)
|
||||
{
|
||||
return size.z > 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class UndoRecord
|
||||
{
|
||||
public UndoOperationType type;
|
||||
|
||||
public readonly List<GroupSnapshot> before = new List<GroupSnapshot>();
|
||||
|
||||
public readonly List<GroupSnapshot> after = new List<GroupSnapshot>();
|
||||
}
|
||||
|
||||
private const string SpawnClipPath = "Assets/Spawn.mp3";
|
||||
|
||||
private const string DeleteClipPath = "Assets/Delete.mp3";
|
||||
|
||||
private const string SpawnDestroyParticlePrefabPath = "Assets/Spawn_Destroy Particle.prefab";
|
||||
|
||||
public const float DefaultVoxelSize = 0.125f;
|
||||
|
||||
[SerializeField]
|
||||
private float voxelSize = 0.125f;
|
||||
|
||||
[SerializeField]
|
||||
private int maxRebuildsPerFrame = 2;
|
||||
|
||||
[SerializeField]
|
||||
private bool creatorMode = true;
|
||||
|
||||
[SerializeField]
|
||||
private Material opaqueMaterial;
|
||||
|
||||
[SerializeField]
|
||||
private Material unlitMaterial;
|
||||
|
||||
[SerializeField]
|
||||
private Material transparentMaterial;
|
||||
|
||||
[SerializeField]
|
||||
private Material invisibleMaterial;
|
||||
|
||||
[SerializeField]
|
||||
private AudioClip spawnClip;
|
||||
|
||||
[SerializeField]
|
||||
private AudioClip deleteClip;
|
||||
|
||||
[SerializeField]
|
||||
private GameObject spawnDestroyParticlePrefab;
|
||||
|
||||
private readonly Dictionary<ushort, VoxelGroup> groups = new Dictionary<ushort, VoxelGroup>();
|
||||
|
||||
private readonly Queue<VoxelGroup> rebuildQueue = new Queue<VoxelGroup>();
|
||||
|
||||
private readonly HashSet<ushort> queuedIds = new HashSet<ushort>();
|
||||
|
||||
private readonly List<ushort> deleteBuffer = new List<ushort>();
|
||||
|
||||
private readonly List<VoxelGroup> groupBuffer = new List<VoxelGroup>();
|
||||
|
||||
private readonly Stack<UndoRecord> undoStack = new Stack<UndoRecord>();
|
||||
|
||||
private readonly Dictionary<ushort, GroupSnapshot> activeEraseBeforeSnapshots = new Dictionary<ushort, GroupSnapshot>();
|
||||
|
||||
private readonly HashSet<ushort> activeEraseAffectedIds = new HashSet<ushort>();
|
||||
|
||||
private ushort nextId = 1;
|
||||
|
||||
private Material[] sharedMaterials;
|
||||
|
||||
private bool eraseUndoSessionActive;
|
||||
|
||||
private bool isUndoing;
|
||||
|
||||
private bool suppressNetworkBroadcast;
|
||||
|
||||
private bool editingLocked;
|
||||
|
||||
private VoxelGroupPhotonSync networkSync;
|
||||
|
||||
public const ushort AvatarGroupIdMin = 60000;
|
||||
|
||||
public static VoxelGroupManager Instance { get; private set; }
|
||||
|
||||
public float VoxelSize => voxelSize;
|
||||
|
||||
public bool CreatorMode => creatorMode;
|
||||
|
||||
public bool EditingLocked => editingLocked;
|
||||
|
||||
internal Material[] SharedMaterials => sharedMaterials;
|
||||
|
||||
internal void RegisterNetworkSync(VoxelGroupPhotonSync sync)
|
||||
{
|
||||
networkSync = sync;
|
||||
}
|
||||
|
||||
public VoxelGroup CreateGroupFromStroke(ICollection<Vector3Int> worldCells, byte voxelValue, byte rotation = 0)
|
||||
{
|
||||
if (worldCells == null || worldCells.Count == 0 || PackedVoxel.IsAir(voxelValue))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
Vector3Int vector3Int = new Vector3Int(int.MaxValue, int.MaxValue, int.MaxValue);
|
||||
Vector3Int vector3Int2 = new Vector3Int(int.MinValue, int.MinValue, int.MinValue);
|
||||
foreach (Vector3Int worldCell in worldCells)
|
||||
{
|
||||
if (worldCell.x < vector3Int.x)
|
||||
{
|
||||
vector3Int.x = worldCell.x;
|
||||
}
|
||||
if (worldCell.y < vector3Int.y)
|
||||
{
|
||||
vector3Int.y = worldCell.y;
|
||||
}
|
||||
if (worldCell.z < vector3Int.z)
|
||||
{
|
||||
vector3Int.z = worldCell.z;
|
||||
}
|
||||
if (worldCell.x > vector3Int2.x)
|
||||
{
|
||||
vector3Int2.x = worldCell.x;
|
||||
}
|
||||
if (worldCell.y > vector3Int2.y)
|
||||
{
|
||||
vector3Int2.y = worldCell.y;
|
||||
}
|
||||
if (worldCell.z > vector3Int2.z)
|
||||
{
|
||||
vector3Int2.z = worldCell.z;
|
||||
}
|
||||
}
|
||||
Vector3Int size = new Vector3Int(vector3Int2.x - vector3Int.x + 1, vector3Int2.y - vector3Int.y + 1, vector3Int2.z - vector3Int.z + 1);
|
||||
byte[] array = new byte[size.x * size.y * size.z];
|
||||
foreach (Vector3Int worldCell2 in worldCells)
|
||||
{
|
||||
Vector3Int vector3Int3 = worldCell2 - vector3Int;
|
||||
int num = vector3Int3.x + vector3Int3.y * size.x + vector3Int3.z * size.x * size.y;
|
||||
array[num] = voxelValue;
|
||||
}
|
||||
VoxelGroup voxelGroup = CreateGroup(vector3Int, rotation, size, array, queueInitialRebuild: true, playSpawnAnimation: true, playSpawnParticles: true);
|
||||
if (voxelGroup != null)
|
||||
{
|
||||
voxelGroup.zoneID = GetCurrentZoneIDForNewGroups();
|
||||
}
|
||||
return voxelGroup;
|
||||
}
|
||||
|
||||
public VoxelGroup BeginStrokeGroup(byte rotation = 0)
|
||||
{
|
||||
VoxelGroup voxelGroup = CreateGroup(Vector3Int.zero, rotation, Vector3Int.zero, new byte[0], queueInitialRebuild: false, playSpawnAnimation: false, playSpawnParticles: false);
|
||||
voxelGroup.zoneID = GetCurrentZoneIDForNewGroups();
|
||||
voxelGroup.SetGreedyRenderMeshing(enabled: false);
|
||||
return voxelGroup;
|
||||
}
|
||||
|
||||
public bool AddVoxelToStrokeGroup(VoxelGroup group, Vector3Int worldCell, byte voxelValue)
|
||||
{
|
||||
if (!ContainsGroup(group) || PackedVoxel.IsAir(voxelValue))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return group.SetWorldVoxel(worldCell, voxelValue, markMeshDirty: false, markColliderDirty: false);
|
||||
}
|
||||
|
||||
public void RefreshStrokeGroup(VoxelGroup group)
|
||||
{
|
||||
if (ContainsGroup(group))
|
||||
{
|
||||
group.RebuildMesh();
|
||||
}
|
||||
}
|
||||
|
||||
internal bool RasterizeStrokeSegment(VoxelGroup group, Vector3 start, Vector3 end, float radius, byte voxelValue, ISet<Vector3Int> trackedCells = null, bool refreshMesh = true)
|
||||
{
|
||||
if (!ContainsGroup(group) || PackedVoxel.IsAir(voxelValue))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
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 |= AddSphereToStrokeGroup(group, worldCenter, radius, voxelValue, trackedCells);
|
||||
}
|
||||
if (flag && refreshMesh)
|
||||
{
|
||||
RefreshStrokeGroup(group);
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
internal void DiscardTransientStrokeGroup(VoxelGroup group)
|
||||
{
|
||||
if (ContainsGroup(group))
|
||||
{
|
||||
RemoveGroupInternal(group, sendRemoveHook: false, playDestroyAnimation: false, playDestroyParticles: false);
|
||||
}
|
||||
}
|
||||
|
||||
internal void BroadcastStrokePreviewBegin(byte voxelValue, float radius, float forwardOffset, Vector3 startPoint)
|
||||
{
|
||||
if (!suppressNetworkBroadcast)
|
||||
{
|
||||
ResolveNetworkSync()?.BroadcastStrokePreviewBegin(voxelValue, radius, forwardOffset, startPoint);
|
||||
}
|
||||
}
|
||||
|
||||
internal void BroadcastStrokePreviewUpdate(byte voxelValue, float radius, float forwardOffset, Vector3 point)
|
||||
{
|
||||
if (!suppressNetworkBroadcast)
|
||||
{
|
||||
ResolveNetworkSync()?.BroadcastStrokePreviewUpdate(voxelValue, radius, forwardOffset, point);
|
||||
}
|
||||
}
|
||||
|
||||
internal void BroadcastStrokePreviewEnd()
|
||||
{
|
||||
if (!suppressNetworkBroadcast)
|
||||
{
|
||||
ResolveNetworkSync()?.BroadcastStrokePreviewEnd();
|
||||
}
|
||||
}
|
||||
|
||||
internal void BroadcastErasePreviewBegin(float radius, float forwardOffset, Vector3 startPoint)
|
||||
{
|
||||
if (!suppressNetworkBroadcast)
|
||||
{
|
||||
ResolveNetworkSync()?.BroadcastErasePreviewBegin(radius, forwardOffset, startPoint);
|
||||
}
|
||||
}
|
||||
|
||||
internal void BroadcastErasePreviewUpdate(float radius, float forwardOffset, Vector3 point)
|
||||
{
|
||||
if (!suppressNetworkBroadcast)
|
||||
{
|
||||
ResolveNetworkSync()?.BroadcastErasePreviewUpdate(radius, forwardOffset, point);
|
||||
}
|
||||
}
|
||||
|
||||
internal void BroadcastErasePreviewEnd()
|
||||
{
|
||||
if (!suppressNetworkBroadcast)
|
||||
{
|
||||
ResolveNetworkSync()?.BroadcastErasePreviewEnd();
|
||||
}
|
||||
}
|
||||
|
||||
internal bool TryRestoreTransientGroup(ushort id, SavedGroupData data)
|
||||
{
|
||||
if (!data.IsValid)
|
||||
{
|
||||
ApplyNetworkDestroy(id, playDestroyAnimation: false, playDestroyParticles: false);
|
||||
return true;
|
||||
}
|
||||
ApplyNetworkCreateOrReplace(id, data.position, data.packedRotation, data.size, data.voxels, playSpawnAnimation: false, playSpawnParticles: false);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void FinalizeStrokeGroup(VoxelGroup group)
|
||||
{
|
||||
if (ContainsGroup(group))
|
||||
{
|
||||
FinalizeLiveEditedGroup(group, sendNetworkUpdate: false);
|
||||
RecordStrokeUndo(group, playSpawnAnimationOnRemote: true);
|
||||
}
|
||||
}
|
||||
|
||||
public void CancelStrokeGroup(VoxelGroup group)
|
||||
{
|
||||
if (ContainsGroup(group))
|
||||
{
|
||||
DeleteGroup(group);
|
||||
}
|
||||
}
|
||||
|
||||
private bool AddSphereToStrokeGroup(VoxelGroup group, Vector3 worldCenter, float radius, byte voxelValue, ISet<Vector3Int> trackedCells)
|
||||
{
|
||||
float num = radius * radius;
|
||||
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);
|
||||
bool flag = false;
|
||||
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 vector3Int = new Vector3Int(k, j, i);
|
||||
if (trackedCells == null || trackedCells.Add(vector3Int))
|
||||
{
|
||||
flag |= AddVoxelToStrokeGroup(group, vector3Int, voxelValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
public void DeleteGroup(ushort id)
|
||||
{
|
||||
if (groups.TryGetValue(id, out var value))
|
||||
{
|
||||
RemoveGroup(value);
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteGroup(VoxelGroup group)
|
||||
{
|
||||
if (!(group == null))
|
||||
{
|
||||
RemoveGroup(group);
|
||||
}
|
||||
}
|
||||
|
||||
public VoxelGroup MergeGroups(IReadOnlyCollection<VoxelGroup> groupsToMerge)
|
||||
{
|
||||
if (groupsToMerge == null || groupsToMerge.Count < 2)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
List<VoxelGroup> list = new List<VoxelGroup>(groupsToMerge.Count);
|
||||
foreach (VoxelGroup item in groupsToMerge)
|
||||
{
|
||||
if (ContainsGroup(item) && item.size.x > 0 && item.size.y > 0 && item.size.z > 0)
|
||||
{
|
||||
list.Add(item);
|
||||
}
|
||||
}
|
||||
if (list.Count < 2)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
UndoRecord undoRecord = (isUndoing ? null : new UndoRecord
|
||||
{
|
||||
type = UndoOperationType.Merge
|
||||
});
|
||||
list.Sort((VoxelGroup a, VoxelGroup b) => a.id.CompareTo(b.id));
|
||||
Vector3Int vector3Int = new Vector3Int(int.MaxValue, int.MaxValue, int.MaxValue);
|
||||
Vector3Int vector3Int2 = new Vector3Int(int.MinValue, int.MinValue, int.MinValue);
|
||||
bool flag = false;
|
||||
for (int num = 0; num < list.Count; num++)
|
||||
{
|
||||
VoxelGroup voxelGroup = list[num];
|
||||
for (int num2 = 0; num2 < voxelGroup.size.z; num2++)
|
||||
{
|
||||
for (int num3 = 0; num3 < voxelGroup.size.y; num3++)
|
||||
{
|
||||
for (int num4 = 0; num4 < voxelGroup.size.x; num4++)
|
||||
{
|
||||
if (!PackedVoxel.IsAir(voxelGroup.GetVoxel(num4, num3, num2)))
|
||||
{
|
||||
Vector3Int vector3Int3 = voxelGroup.position + GridRotationUtility.Rotate(new Vector3Int(num4, num3, num2), voxelGroup.packedRotation);
|
||||
if (vector3Int3.x < vector3Int.x)
|
||||
{
|
||||
vector3Int.x = vector3Int3.x;
|
||||
}
|
||||
if (vector3Int3.y < vector3Int.y)
|
||||
{
|
||||
vector3Int.y = vector3Int3.y;
|
||||
}
|
||||
if (vector3Int3.z < vector3Int.z)
|
||||
{
|
||||
vector3Int.z = vector3Int3.z;
|
||||
}
|
||||
if (vector3Int3.x > vector3Int2.x)
|
||||
{
|
||||
vector3Int2.x = vector3Int3.x;
|
||||
}
|
||||
if (vector3Int3.y > vector3Int2.y)
|
||||
{
|
||||
vector3Int2.y = vector3Int3.y;
|
||||
}
|
||||
if (vector3Int3.z > vector3Int2.z)
|
||||
{
|
||||
vector3Int2.z = vector3Int3.z;
|
||||
}
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!flag)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
Vector3Int groupSize = new Vector3Int(vector3Int2.x - vector3Int.x + 1, vector3Int2.y - vector3Int.y + 1, vector3Int2.z - vector3Int.z + 1);
|
||||
byte[] array = new byte[groupSize.x * groupSize.y * groupSize.z];
|
||||
for (int num5 = 0; num5 < list.Count; num5++)
|
||||
{
|
||||
VoxelGroup voxelGroup2 = list[num5];
|
||||
for (int num6 = 0; num6 < voxelGroup2.size.z; num6++)
|
||||
{
|
||||
for (int num7 = 0; num7 < voxelGroup2.size.y; num7++)
|
||||
{
|
||||
for (int num8 = 0; num8 < voxelGroup2.size.x; num8++)
|
||||
{
|
||||
byte voxel = voxelGroup2.GetVoxel(num8, num7, num6);
|
||||
if (!PackedVoxel.IsAir(voxel))
|
||||
{
|
||||
Vector3Int vector3Int4 = voxelGroup2.position + GridRotationUtility.Rotate(new Vector3Int(num8, num7, num6), voxelGroup2.packedRotation) - vector3Int;
|
||||
int num9 = vector3Int4.x + vector3Int4.y * groupSize.x + vector3Int4.z * groupSize.x * groupSize.y;
|
||||
array[num9] = voxel;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
VoxelGroup voxelGroup3 = list[list.Count - 1];
|
||||
if (undoRecord != null)
|
||||
{
|
||||
for (int num10 = 0; num10 < list.Count; num10++)
|
||||
{
|
||||
GroupSnapshot groupSnapshot = CaptureSnapshot(list[num10]);
|
||||
if (groupSnapshot != null)
|
||||
{
|
||||
undoRecord.before.Add(groupSnapshot);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int num11 = 0; num11 < list.Count; num11++)
|
||||
{
|
||||
VoxelGroup voxelGroup4 = list[num11];
|
||||
if (!(voxelGroup4 == voxelGroup3))
|
||||
{
|
||||
RemoveGroupInternal(voxelGroup4, !suppressNetworkBroadcast, playDestroyAnimation: false, playDestroyParticles: false);
|
||||
}
|
||||
}
|
||||
voxelGroup3.OverwriteData(vector3Int, 0, groupSize, array);
|
||||
if (undoRecord != null)
|
||||
{
|
||||
GroupSnapshot groupSnapshot2 = CaptureSnapshot(voxelGroup3);
|
||||
if (groupSnapshot2 != null)
|
||||
{
|
||||
undoRecord.after.Add(groupSnapshot2);
|
||||
PushUndoRecord(undoRecord);
|
||||
}
|
||||
}
|
||||
SendUpdateGroupAfterMergeStub(voxelGroup3);
|
||||
return voxelGroup3;
|
||||
}
|
||||
|
||||
public bool TryCaptureGroupData(VoxelGroup group, out SavedGroupData data)
|
||||
{
|
||||
data = default(SavedGroupData);
|
||||
if (!ContainsGroup(group) || group.voxels == null || group.voxels.Length == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
data = new SavedGroupData
|
||||
{
|
||||
position = group.position,
|
||||
packedRotation = group.packedRotation,
|
||||
size = group.size,
|
||||
voxels = (byte[])group.voxels.Clone()
|
||||
};
|
||||
return data.IsValid;
|
||||
}
|
||||
|
||||
public bool TryBuildCombinedGroupData(IReadOnlyCollection<VoxelGroup> groupsToCombine, out SavedGroupData data)
|
||||
{
|
||||
data = default(SavedGroupData);
|
||||
if (groupsToCombine == null || groupsToCombine.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
List<VoxelGroup> list = new List<VoxelGroup>(groupsToCombine.Count);
|
||||
foreach (VoxelGroup item in groupsToCombine)
|
||||
{
|
||||
if (ContainsGroup(item) && item.size.x > 0 && item.size.y > 0 && item.size.z > 0)
|
||||
{
|
||||
list.Add(item);
|
||||
}
|
||||
}
|
||||
if (list.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (list.Count == 1)
|
||||
{
|
||||
return TryCaptureGroupData(list[0], out data);
|
||||
}
|
||||
list.Sort((VoxelGroup a, VoxelGroup b) => a.id.CompareTo(b.id));
|
||||
Vector3Int vector3Int = new Vector3Int(int.MaxValue, int.MaxValue, int.MaxValue);
|
||||
Vector3Int vector3Int2 = new Vector3Int(int.MinValue, int.MinValue, int.MinValue);
|
||||
bool flag = false;
|
||||
for (int num = 0; num < list.Count; num++)
|
||||
{
|
||||
VoxelGroup voxelGroup = list[num];
|
||||
for (int num2 = 0; num2 < voxelGroup.size.z; num2++)
|
||||
{
|
||||
for (int num3 = 0; num3 < voxelGroup.size.y; num3++)
|
||||
{
|
||||
for (int num4 = 0; num4 < voxelGroup.size.x; num4++)
|
||||
{
|
||||
if (!PackedVoxel.IsAir(voxelGroup.GetVoxel(num4, num3, num2)))
|
||||
{
|
||||
Vector3Int vector3Int3 = voxelGroup.position + GridRotationUtility.Rotate(new Vector3Int(num4, num3, num2), voxelGroup.packedRotation);
|
||||
if (vector3Int3.x < vector3Int.x)
|
||||
{
|
||||
vector3Int.x = vector3Int3.x;
|
||||
}
|
||||
if (vector3Int3.y < vector3Int.y)
|
||||
{
|
||||
vector3Int.y = vector3Int3.y;
|
||||
}
|
||||
if (vector3Int3.z < vector3Int.z)
|
||||
{
|
||||
vector3Int.z = vector3Int3.z;
|
||||
}
|
||||
if (vector3Int3.x > vector3Int2.x)
|
||||
{
|
||||
vector3Int2.x = vector3Int3.x;
|
||||
}
|
||||
if (vector3Int3.y > vector3Int2.y)
|
||||
{
|
||||
vector3Int2.y = vector3Int3.y;
|
||||
}
|
||||
if (vector3Int3.z > vector3Int2.z)
|
||||
{
|
||||
vector3Int2.z = vector3Int3.z;
|
||||
}
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!flag)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Vector3Int size = new Vector3Int(vector3Int2.x - vector3Int.x + 1, vector3Int2.y - vector3Int.y + 1, vector3Int2.z - vector3Int.z + 1);
|
||||
byte[] array = new byte[size.x * size.y * size.z];
|
||||
for (int num5 = 0; num5 < list.Count; num5++)
|
||||
{
|
||||
VoxelGroup voxelGroup2 = list[num5];
|
||||
for (int num6 = 0; num6 < voxelGroup2.size.z; num6++)
|
||||
{
|
||||
for (int num7 = 0; num7 < voxelGroup2.size.y; num7++)
|
||||
{
|
||||
for (int num8 = 0; num8 < voxelGroup2.size.x; num8++)
|
||||
{
|
||||
byte voxel = voxelGroup2.GetVoxel(num8, num7, num6);
|
||||
if (!PackedVoxel.IsAir(voxel))
|
||||
{
|
||||
Vector3Int vector3Int4 = voxelGroup2.position + GridRotationUtility.Rotate(new Vector3Int(num8, num7, num6), voxelGroup2.packedRotation) - vector3Int;
|
||||
int num9 = vector3Int4.x + vector3Int4.y * size.x + vector3Int4.z * size.x * size.y;
|
||||
array[num9] = voxel;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
data = new SavedGroupData
|
||||
{
|
||||
position = vector3Int,
|
||||
packedRotation = 0,
|
||||
size = size,
|
||||
voxels = array
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
public VoxelGroup SpawnSavedGroup(SavedGroupData data, Vector3Int gridPosition)
|
||||
{
|
||||
if (!data.IsValid)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
VoxelGroup voxelGroup = CreateGroup(gridPosition, data.packedRotation, data.size, data.voxels, queueInitialRebuild: true, playSpawnAnimation: true, playSpawnParticles: true);
|
||||
if (voxelGroup != null)
|
||||
{
|
||||
voxelGroup.zoneID = GetCurrentZoneIDForNewGroups();
|
||||
}
|
||||
RecordStrokeUndo(voxelGroup, playSpawnAnimationOnRemote: false, playSpawnParticlesOnRemote: false);
|
||||
return voxelGroup;
|
||||
}
|
||||
|
||||
public VoxelGroup DuplicateGroup(VoxelGroup source, Vector3Int gridOffset)
|
||||
{
|
||||
if (!ContainsGroup(source) || source.voxels == null || source.voxels.Length == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
VoxelGroup voxelGroup = CreateGroup(source.position + gridOffset, source.packedRotation, source.size, (byte[])source.voxels.Clone(), queueInitialRebuild: true, playSpawnAnimation: true, playSpawnParticles: true);
|
||||
if (voxelGroup != null)
|
||||
{
|
||||
voxelGroup.zoneID = source.zoneID;
|
||||
RecordStrokeUndo(voxelGroup, playSpawnAnimationOnRemote: false, playSpawnParticlesOnRemote: false);
|
||||
}
|
||||
return voxelGroup;
|
||||
}
|
||||
|
||||
public bool TransformGroup(VoxelGroup group, Vector3Int gridPosition, byte rotation, bool sendNetworkUpdate = true)
|
||||
{
|
||||
if (!ContainsGroup(group))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (group.position == gridPosition && group.packedRotation == rotation)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
group.SetGridPose(gridPosition, rotation);
|
||||
if (sendNetworkUpdate)
|
||||
{
|
||||
SendUpdateGroupStub(group);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void SetGroupColliderEnabled(VoxelGroup group, bool enabled)
|
||||
{
|
||||
if (ContainsGroup(group) && !(group.meshCollider == null))
|
||||
{
|
||||
group.meshCollider.enabled = enabled;
|
||||
if (!suppressNetworkBroadcast)
|
||||
{
|
||||
ResolveNetworkSync()?.BroadcastSetColliderEnabled(group, enabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void BroadcastMovePreview(VoxelGroup group)
|
||||
{
|
||||
if (ContainsGroup(group) && !suppressNetworkBroadcast)
|
||||
{
|
||||
ResolveNetworkSync()?.BroadcastMovePreview(group);
|
||||
}
|
||||
}
|
||||
|
||||
public void BroadcastMoveFinal(VoxelGroup group)
|
||||
{
|
||||
if (ContainsGroup(group) && !suppressNetworkBroadcast)
|
||||
{
|
||||
ResolveNetworkSync()?.BroadcastMoveFinal(group);
|
||||
}
|
||||
}
|
||||
|
||||
public bool PaintWholeGroup(VoxelGroup group, byte voxelValue)
|
||||
{
|
||||
if (!ContainsGroup(group) || PackedVoxel.IsAir(voxelValue))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!group.RepaintAll(voxelValue))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
SendUpdateGroupStub(group);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void BeginEraseUndoSession()
|
||||
{
|
||||
if (!isUndoing)
|
||||
{
|
||||
activeEraseBeforeSnapshots.Clear();
|
||||
activeEraseAffectedIds.Clear();
|
||||
eraseUndoSessionActive = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void BeginPaintUndoSession()
|
||||
{
|
||||
BeginEraseUndoSession();
|
||||
}
|
||||
|
||||
public void CompleteEraseUndoSession()
|
||||
{
|
||||
if (!eraseUndoSessionActive)
|
||||
{
|
||||
return;
|
||||
}
|
||||
eraseUndoSessionActive = false;
|
||||
if (activeEraseBeforeSnapshots.Count == 0)
|
||||
{
|
||||
activeEraseBeforeSnapshots.Clear();
|
||||
activeEraseAffectedIds.Clear();
|
||||
return;
|
||||
}
|
||||
UndoRecord undoRecord = new UndoRecord
|
||||
{
|
||||
type = UndoOperationType.Erase
|
||||
};
|
||||
foreach (KeyValuePair<ushort, GroupSnapshot> activeEraseBeforeSnapshot in activeEraseBeforeSnapshots)
|
||||
{
|
||||
undoRecord.before.Add(activeEraseBeforeSnapshot.Value);
|
||||
}
|
||||
foreach (ushort activeEraseAffectedId in activeEraseAffectedIds)
|
||||
{
|
||||
if (groups.TryGetValue(activeEraseAffectedId, out var value))
|
||||
{
|
||||
GroupSnapshot groupSnapshot = CaptureSnapshot(value);
|
||||
if (groupSnapshot != null)
|
||||
{
|
||||
undoRecord.after.Add(groupSnapshot);
|
||||
}
|
||||
}
|
||||
}
|
||||
PushUndoRecord(undoRecord);
|
||||
activeEraseBeforeSnapshots.Clear();
|
||||
activeEraseAffectedIds.Clear();
|
||||
}
|
||||
|
||||
public void CompletePaintUndoSession()
|
||||
{
|
||||
CompleteEraseUndoSession();
|
||||
}
|
||||
|
||||
public bool UndoLastOperation()
|
||||
{
|
||||
if (undoStack.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
UndoRecord undoRecord = undoStack.Pop();
|
||||
isUndoing = true;
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < undoRecord.after.Count; i++)
|
||||
{
|
||||
GroupSnapshot groupSnapshot = undoRecord.after[i];
|
||||
if (groups.TryGetValue(groupSnapshot.id, out var value))
|
||||
{
|
||||
RemoveGroupInternal(value, !suppressNetworkBroadcast, playDestroyAnimation: true, playDestroyParticles: false);
|
||||
}
|
||||
}
|
||||
for (int j = 0; j < undoRecord.before.Count; j++)
|
||||
{
|
||||
RestoreSnapshot(undoRecord.before[j]);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
isUndoing = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void EraseSphere(Vector3 worldCenter, float radius)
|
||||
{
|
||||
if (radius <= 0f || groups.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
groupBuffer.Clear();
|
||||
foreach (VoxelGroup value2 in groups.Values)
|
||||
{
|
||||
groupBuffer.Add(value2);
|
||||
}
|
||||
deleteBuffer.Clear();
|
||||
for (int i = 0; i < groupBuffer.Count; i++)
|
||||
{
|
||||
VoxelGroup voxelGroup = groupBuffer[i];
|
||||
if (!(voxelGroup == null) && voxelGroup.IntersectsSphere(worldCenter, radius) && voxelGroup.EraseSphere(worldCenter, radius))
|
||||
{
|
||||
if (!voxelGroup.TrimToContents())
|
||||
{
|
||||
deleteBuffer.Add(voxelGroup.id);
|
||||
continue;
|
||||
}
|
||||
QueueRebuild(voxelGroup);
|
||||
SendUpdateGroupStub(voxelGroup);
|
||||
}
|
||||
}
|
||||
for (int j = 0; j < deleteBuffer.Count; j++)
|
||||
{
|
||||
if (groups.TryGetValue(deleteBuffer[j], out var value))
|
||||
{
|
||||
RemoveGroupInternal(value, !suppressNetworkBroadcast, playDestroyAnimation: false, playDestroyParticles: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool EraseSphereRealtime(Vector3 worldCenter, float radius, ISet<VoxelGroup> editedGroups)
|
||||
{
|
||||
if (radius <= 0f || groups.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
groupBuffer.Clear();
|
||||
foreach (VoxelGroup value2 in groups.Values)
|
||||
{
|
||||
groupBuffer.Add(value2);
|
||||
}
|
||||
deleteBuffer.Clear();
|
||||
bool result = false;
|
||||
for (int i = 0; i < groupBuffer.Count; i++)
|
||||
{
|
||||
VoxelGroup voxelGroup = groupBuffer[i];
|
||||
if (voxelGroup == null || !voxelGroup.IntersectsSphere(worldCenter, radius))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
GroupSnapshot groupSnapshot = null;
|
||||
if (eraseUndoSessionActive && !activeEraseBeforeSnapshots.ContainsKey(voxelGroup.id))
|
||||
{
|
||||
groupSnapshot = CaptureSnapshot(voxelGroup);
|
||||
}
|
||||
if (voxelGroup.EraseSphere(worldCenter, radius, markMeshDirty: false, markColliderDirty: false))
|
||||
{
|
||||
result = true;
|
||||
if (groupSnapshot != null)
|
||||
{
|
||||
activeEraseBeforeSnapshots[voxelGroup.id] = groupSnapshot;
|
||||
}
|
||||
if (eraseUndoSessionActive)
|
||||
{
|
||||
activeEraseAffectedIds.Add(voxelGroup.id);
|
||||
}
|
||||
if (!voxelGroup.TrimToContents())
|
||||
{
|
||||
deleteBuffer.Add(voxelGroup.id);
|
||||
editedGroups?.Remove(voxelGroup);
|
||||
}
|
||||
else
|
||||
{
|
||||
voxelGroup.SetGreedyRenderMeshing(enabled: false);
|
||||
voxelGroup.RebuildMesh();
|
||||
editedGroups?.Add(voxelGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int j = 0; j < deleteBuffer.Count; j++)
|
||||
{
|
||||
if (groups.TryGetValue(deleteBuffer[j], out var value))
|
||||
{
|
||||
RemoveGroupInternal(value, !suppressNetworkBroadcast, playDestroyAnimation: false, playDestroyParticles: false);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public bool PaintSphereRealtime(Vector3 worldCenter, float radius, byte voxelValue, ISet<VoxelGroup> editedGroups)
|
||||
{
|
||||
if (radius <= 0f || groups.Count == 0 || PackedVoxel.IsAir(voxelValue))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
groupBuffer.Clear();
|
||||
foreach (VoxelGroup value in groups.Values)
|
||||
{
|
||||
groupBuffer.Add(value);
|
||||
}
|
||||
bool result = false;
|
||||
for (int i = 0; i < groupBuffer.Count; i++)
|
||||
{
|
||||
VoxelGroup voxelGroup = groupBuffer[i];
|
||||
if (voxelGroup == null || !voxelGroup.IntersectsSphere(worldCenter, radius))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
GroupSnapshot groupSnapshot = null;
|
||||
if (eraseUndoSessionActive && !activeEraseBeforeSnapshots.ContainsKey(voxelGroup.id))
|
||||
{
|
||||
groupSnapshot = CaptureSnapshot(voxelGroup);
|
||||
}
|
||||
if (voxelGroup.PaintSphere(worldCenter, radius, voxelValue, markMeshDirty: false, markColliderDirty: false))
|
||||
{
|
||||
result = true;
|
||||
if (groupSnapshot != null)
|
||||
{
|
||||
activeEraseBeforeSnapshots[voxelGroup.id] = groupSnapshot;
|
||||
}
|
||||
if (eraseUndoSessionActive)
|
||||
{
|
||||
activeEraseAffectedIds.Add(voxelGroup.id);
|
||||
}
|
||||
voxelGroup.SetGreedyRenderMeshing(enabled: false);
|
||||
voxelGroup.RebuildMesh();
|
||||
editedGroups?.Add(voxelGroup);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal bool EraseSpherePreview(Vector3 worldCenter, float radius, ISet<VoxelGroup> editedGroups)
|
||||
{
|
||||
bool flag = isUndoing;
|
||||
bool flag2 = suppressNetworkBroadcast;
|
||||
isUndoing = true;
|
||||
suppressNetworkBroadcast = true;
|
||||
try
|
||||
{
|
||||
return EraseSphereRealtime(worldCenter, radius, editedGroups);
|
||||
}
|
||||
finally
|
||||
{
|
||||
isUndoing = flag;
|
||||
suppressNetworkBroadcast = flag2;
|
||||
}
|
||||
}
|
||||
|
||||
public void FinalizeLiveEditedGroup(VoxelGroup group, bool sendNetworkUpdate = true, bool rebuildCollider = true, bool playDestroyAnimationIfEmpty = true)
|
||||
{
|
||||
if (!ContainsGroup(group))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!group.TrimToContents())
|
||||
{
|
||||
RemoveGroupInternal(group, !suppressNetworkBroadcast, playDestroyAnimationIfEmpty, playDestroyAnimationIfEmpty);
|
||||
return;
|
||||
}
|
||||
group.SetGreedyRenderMeshing(enabled: true);
|
||||
group.RebuildMesh();
|
||||
if (rebuildCollider)
|
||||
{
|
||||
group.RebuildCollider();
|
||||
}
|
||||
else
|
||||
{
|
||||
group.dirtyCollider = false;
|
||||
}
|
||||
if (sendNetworkUpdate)
|
||||
{
|
||||
SendUpdateGroupStub(group);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetCreatorMode(bool enabled)
|
||||
{
|
||||
if (enabled && editingLocked)
|
||||
{
|
||||
enabled = false;
|
||||
}
|
||||
if (creatorMode == enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
creatorMode = enabled;
|
||||
foreach (VoxelGroup value in groups.Values)
|
||||
{
|
||||
if (!(value == null))
|
||||
{
|
||||
value.SetCreatorMode(enabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetEditingLocked(bool locked)
|
||||
{
|
||||
if (editingLocked != locked)
|
||||
{
|
||||
editingLocked = locked;
|
||||
if (editingLocked && creatorMode)
|
||||
{
|
||||
SetCreatorMode(enabled: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal int GetCurrentZoneIDForNewGroups()
|
||||
{
|
||||
GamemodeZone gamemodeZone = ((GamemodeZoneManager.Instance != null) ? GamemodeZoneManager.Instance.CurrentZone : null);
|
||||
if (!(gamemodeZone != null))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return gamemodeZone.zoneID;
|
||||
}
|
||||
|
||||
public void RemoveGroupsByZoneID(int zoneID)
|
||||
{
|
||||
if (groups.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
groupBuffer.Clear();
|
||||
foreach (VoxelGroup value in groups.Values)
|
||||
{
|
||||
if (value != null && value.zoneID == zoneID)
|
||||
{
|
||||
groupBuffer.Add(value);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < groupBuffer.Count; i++)
|
||||
{
|
||||
RemoveGroup(groupBuffer[i]);
|
||||
}
|
||||
groupBuffer.Clear();
|
||||
}
|
||||
|
||||
public int RemoveGroupsForZone(GamemodeZone zone)
|
||||
{
|
||||
if (zone == null || groups.Count == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
groupBuffer.Clear();
|
||||
foreach (VoxelGroup value in groups.Values)
|
||||
{
|
||||
if (!(value == null) && (value.zoneID == zone.zoneID || zone.ContainsGroup(value)))
|
||||
{
|
||||
groupBuffer.Add(value);
|
||||
}
|
||||
}
|
||||
int count = groupBuffer.Count;
|
||||
for (int i = 0; i < groupBuffer.Count; i++)
|
||||
{
|
||||
RemoveGroup(groupBuffer[i]);
|
||||
}
|
||||
groupBuffer.Clear();
|
||||
return count;
|
||||
}
|
||||
|
||||
public bool TryGetGroup(ushort id, out VoxelGroup group)
|
||||
{
|
||||
return groups.TryGetValue(id, out group);
|
||||
}
|
||||
|
||||
public bool TryPickNearestVoxelValue(Vector3 worldPoint, float maxRadius, out byte voxelValue)
|
||||
{
|
||||
voxelValue = 0;
|
||||
if (groups.Count == 0 || maxRadius <= 0f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
float num = maxRadius * maxRadius;
|
||||
bool result = false;
|
||||
groupBuffer.Clear();
|
||||
foreach (VoxelGroup value in groups.Values)
|
||||
{
|
||||
groupBuffer.Add(value);
|
||||
}
|
||||
for (int i = 0; i < groupBuffer.Count; i++)
|
||||
{
|
||||
VoxelGroup voxelGroup = groupBuffer[i];
|
||||
if (!(voxelGroup == null) && voxelGroup.IntersectsSphere(worldPoint, maxRadius) && voxelGroup.TryPickNearestVoxelValue(worldPoint, maxRadius, out var voxelValue2, out var _, out var pickedDistanceSquared) && !(pickedDistanceSquared > num))
|
||||
{
|
||||
result = true;
|
||||
num = pickedDistanceSquared;
|
||||
voxelValue = voxelValue2;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<VoxelGroup> GetGroupsInRadius(Vector3 worldPos, float radius)
|
||||
{
|
||||
List<VoxelGroup> list = new List<VoxelGroup>();
|
||||
float num = radius * radius;
|
||||
foreach (VoxelGroup value in groups.Values)
|
||||
{
|
||||
if (!(value == null) && value.size.x > 0 && value.size.y > 0 && value.size.z > 0 && value.GetWorldBounds().SqrDistance(worldPos) <= num)
|
||||
{
|
||||
list.Add(value);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public List<VoxelGroup> GetAllGroups()
|
||||
{
|
||||
List<VoxelGroup> result = new List<VoxelGroup>(groups.Count);
|
||||
GetAllGroups(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void GetAllGroups(List<VoxelGroup> result)
|
||||
{
|
||||
if (result == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
result.Clear();
|
||||
foreach (VoxelGroup value in groups.Values)
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
result.Add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearAllGroupsForNetworkSync()
|
||||
{
|
||||
bool flag = isUndoing;
|
||||
bool flag2 = suppressNetworkBroadcast;
|
||||
isUndoing = true;
|
||||
suppressNetworkBroadcast = true;
|
||||
try
|
||||
{
|
||||
undoStack.Clear();
|
||||
activeEraseBeforeSnapshots.Clear();
|
||||
activeEraseAffectedIds.Clear();
|
||||
eraseUndoSessionActive = false;
|
||||
groupBuffer.Clear();
|
||||
foreach (VoxelGroup value in groups.Values)
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
groupBuffer.Add(value);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < groupBuffer.Count; i++)
|
||||
{
|
||||
if (!(groupBuffer[i] != null) || groupBuffer[i].id < 60000)
|
||||
{
|
||||
RemoveGroupInternal(groupBuffer[i], sendRemoveHook: false, playDestroyAnimation: false, playDestroyParticles: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
groupBuffer.Clear();
|
||||
isUndoing = flag;
|
||||
suppressNetworkBroadcast = flag2;
|
||||
}
|
||||
}
|
||||
|
||||
public VoxelGroup ApplyNetworkCreateOrReplace(ushort id, Vector3Int position, byte rotation, Vector3Int size, byte[] voxelData, bool playSpawnAnimation, bool playSpawnParticles)
|
||||
{
|
||||
return ApplyNetworkCreateOrReplace(id, position, rotation, size, voxelData, -1, playSpawnAnimation, playSpawnParticles);
|
||||
}
|
||||
|
||||
public VoxelGroup ApplyNetworkCreateOrReplace(ushort id, Vector3Int position, byte rotation, Vector3Int size, byte[] voxelData, int zoneID, bool playSpawnAnimation, bool playSpawnParticles)
|
||||
{
|
||||
bool flag = isUndoing;
|
||||
bool flag2 = suppressNetworkBroadcast;
|
||||
isUndoing = true;
|
||||
suppressNetworkBroadcast = true;
|
||||
try
|
||||
{
|
||||
VoxelGroup voxelGroup = UpsertGroup(id, position, rotation, size, voxelData, queueInitialRebuild: true, playSpawnAnimation, playSpawnParticles);
|
||||
if (voxelGroup != null)
|
||||
{
|
||||
voxelGroup.zoneID = zoneID;
|
||||
}
|
||||
return voxelGroup;
|
||||
}
|
||||
finally
|
||||
{
|
||||
isUndoing = flag;
|
||||
suppressNetworkBroadcast = flag2;
|
||||
}
|
||||
}
|
||||
|
||||
public void ApplyNetworkDestroy(ushort id, bool playDestroyAnimation, bool playDestroyParticles)
|
||||
{
|
||||
bool flag = isUndoing;
|
||||
bool flag2 = suppressNetworkBroadcast;
|
||||
isUndoing = true;
|
||||
suppressNetworkBroadcast = true;
|
||||
try
|
||||
{
|
||||
if (groups.TryGetValue(id, out var value))
|
||||
{
|
||||
RemoveGroupInternal(value, sendRemoveHook: false, playDestroyAnimation, playDestroyParticles);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
isUndoing = flag;
|
||||
suppressNetworkBroadcast = flag2;
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveGroup(VoxelGroup group)
|
||||
{
|
||||
RemoveGroupInternal(group, !suppressNetworkBroadcast, playDestroyAnimation: true, playDestroyParticles: true);
|
||||
}
|
||||
|
||||
public void RemoveGroupWithoutAnimation(VoxelGroup group)
|
||||
{
|
||||
RemoveGroupInternal(group, !suppressNetworkBroadcast, playDestroyAnimation: false, playDestroyParticles: false);
|
||||
}
|
||||
|
||||
private void RemoveGroupInternal(VoxelGroup group, bool sendRemoveHook, bool playDestroyAnimation, bool playDestroyParticles)
|
||||
{
|
||||
if (!(group == null))
|
||||
{
|
||||
if (groups.Remove(group.id))
|
||||
{
|
||||
queuedIds.Remove(group.id);
|
||||
}
|
||||
group.SetSelected(selected: false);
|
||||
if (sendRemoveHook)
|
||||
{
|
||||
SendRemoveGroupStub(group.id, playDestroyAnimation, playDestroyParticles);
|
||||
}
|
||||
if (playDestroyAnimation)
|
||||
{
|
||||
StartCoroutine(AnimateAndDestroyGroup(group, playDestroyParticles));
|
||||
}
|
||||
else
|
||||
{
|
||||
UnityEngine.Object.Destroy(group.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ushort GetNextGroupId()
|
||||
{
|
||||
return AllocateId();
|
||||
}
|
||||
|
||||
public void AddGroup(VoxelGroup group)
|
||||
{
|
||||
if (group == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (group.id == 0)
|
||||
{
|
||||
group.id = GetNextGroupId();
|
||||
}
|
||||
if (!groups.ContainsKey(group.id))
|
||||
{
|
||||
group.transform.SetParent(base.transform, worldPositionStays: false);
|
||||
group.SetOwner(this);
|
||||
groups.Add(group.id, group);
|
||||
AdvanceNextIdPast(group.id);
|
||||
if (group.dirtyMesh || group.dirtyCollider)
|
||||
{
|
||||
QueueRebuild(group);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void QueueRebuild(VoxelGroup group)
|
||||
{
|
||||
if (!(group == null) && queuedIds.Add(group.id))
|
||||
{
|
||||
rebuildQueue.Enqueue(group);
|
||||
}
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (Instance != null && Instance != this)
|
||||
{
|
||||
Debug.LogError("Multiple VoxelGroupManager instances found.", this);
|
||||
return;
|
||||
}
|
||||
Instance = this;
|
||||
voxelSize = Mathf.Max(0.001f, voxelSize);
|
||||
maxRebuildsPerFrame = Mathf.Max(1, maxRebuildsPerFrame);
|
||||
EnsureEffectAssetsAssigned();
|
||||
BuildSharedMaterials();
|
||||
RebindExistingGroups();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
int num = 0;
|
||||
while (num < maxRebuildsPerFrame && rebuildQueue.Count > 0)
|
||||
{
|
||||
VoxelGroup voxelGroup = rebuildQueue.Dequeue();
|
||||
if (voxelGroup == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
queuedIds.Remove(voxelGroup.id);
|
||||
if (groups.ContainsKey(voxelGroup.id))
|
||||
{
|
||||
if (voxelGroup.dirtyMesh)
|
||||
{
|
||||
voxelGroup.RebuildMesh();
|
||||
}
|
||||
if (voxelGroup.dirtyCollider)
|
||||
{
|
||||
voxelGroup.RebuildCollider();
|
||||
}
|
||||
num++;
|
||||
if (voxelGroup.dirtyMesh || voxelGroup.dirtyCollider)
|
||||
{
|
||||
QueueRebuild(voxelGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (Instance == this)
|
||||
{
|
||||
Instance = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnValidate()
|
||||
{
|
||||
EnsureEffectAssetsAssigned();
|
||||
}
|
||||
|
||||
private ushort AllocateId()
|
||||
{
|
||||
if (PhotonNetwork.InRoom && PhotonNetwork.LocalPlayer != null)
|
||||
{
|
||||
ushort num = (ushort)((byte)Mathf.Clamp(PhotonNetwork.LocalPlayer.ActorNumber, 1, 255) << 8);
|
||||
for (ushort num2 = 1; num2 < 255; num2++)
|
||||
{
|
||||
ushort num3 = (ushort)(num | num2);
|
||||
if (!groups.ContainsKey(num3))
|
||||
{
|
||||
return num3;
|
||||
}
|
||||
}
|
||||
throw new InvalidOperationException("No free voxel group ids remain for the local network actor.");
|
||||
}
|
||||
ushort num4 = nextId;
|
||||
for (int i = 0; i < 65535; i++)
|
||||
{
|
||||
if (num4 == 0)
|
||||
{
|
||||
num4 = 1;
|
||||
}
|
||||
if (!groups.ContainsKey(num4))
|
||||
{
|
||||
nextId = (ushort)(num4 + 1);
|
||||
if (nextId == 0)
|
||||
{
|
||||
nextId = 1;
|
||||
}
|
||||
return num4;
|
||||
}
|
||||
num4++;
|
||||
}
|
||||
throw new InvalidOperationException("No free voxel group ids remain.");
|
||||
}
|
||||
|
||||
private VoxelGroup CreateGroup(Vector3Int gridPosition, byte rotation, Vector3Int size, byte[] voxels, bool queueInitialRebuild, bool playSpawnAnimation, bool playSpawnParticles)
|
||||
{
|
||||
ushort nextGroupId = GetNextGroupId();
|
||||
return UpsertGroup(nextGroupId, gridPosition, rotation, size, voxels, queueInitialRebuild, playSpawnAnimation, playSpawnParticles);
|
||||
}
|
||||
|
||||
private bool ContainsGroup(VoxelGroup group)
|
||||
{
|
||||
if (group != null && groups.TryGetValue(group.id, out var value))
|
||||
{
|
||||
return value == group;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void RebindExistingGroups()
|
||||
{
|
||||
groups.Clear();
|
||||
queuedIds.Clear();
|
||||
rebuildQueue.Clear();
|
||||
VoxelGroup[] componentsInChildren = GetComponentsInChildren<VoxelGroup>(includeInactive: true);
|
||||
ushort num = 0;
|
||||
foreach (VoxelGroup voxelGroup in componentsInChildren)
|
||||
{
|
||||
if (!(voxelGroup == null))
|
||||
{
|
||||
if (voxelGroup.id == 0 || groups.ContainsKey(voxelGroup.id))
|
||||
{
|
||||
voxelGroup.id = AllocateSceneId(groups);
|
||||
}
|
||||
num = ((voxelGroup.id > num) ? voxelGroup.id : num);
|
||||
groups[voxelGroup.id] = voxelGroup;
|
||||
voxelGroup.SetOwner(this);
|
||||
QueueRebuild(voxelGroup);
|
||||
}
|
||||
}
|
||||
nextId = (ushort)(num + 1);
|
||||
if (nextId == 0)
|
||||
{
|
||||
nextId = 1;
|
||||
}
|
||||
}
|
||||
|
||||
private ushort AllocateSceneId(Dictionary<ushort, VoxelGroup> existing)
|
||||
{
|
||||
for (ushort num = 1; num != 0; num++)
|
||||
{
|
||||
if (!existing.ContainsKey(num))
|
||||
{
|
||||
return num;
|
||||
}
|
||||
}
|
||||
throw new InvalidOperationException("No free voxel group ids remain.");
|
||||
}
|
||||
|
||||
private void BuildSharedMaterials()
|
||||
{
|
||||
opaqueMaterial = EnsureCompatibleMaterial(opaqueMaterial, "BlockSpace_Opaque", transparent: false, useLitShader: true);
|
||||
unlitMaterial = EnsureCompatibleMaterial(unlitMaterial, "BlockSpace_Unlit", transparent: false, useLitShader: false);
|
||||
transparentMaterial = EnsureCompatibleMaterial(transparentMaterial, "BlockSpace_Transparent", transparent: true, useLitShader: false);
|
||||
invisibleMaterial = EnsureCompatibleMaterial(invisibleMaterial, "BlockSpace_Invisible", transparent: true, useLitShader: false);
|
||||
sharedMaterials = new Material[4] { opaqueMaterial, unlitMaterial, transparentMaterial, invisibleMaterial };
|
||||
}
|
||||
|
||||
private static Material CreateDefaultMaterial(string materialName, bool transparent, bool useLitShader)
|
||||
{
|
||||
Shader shader = Shader.Find(GetBlockSpaceVertexColorShaderName(transparent, useLitShader));
|
||||
if (shader != null)
|
||||
{
|
||||
Material obj = new Material(shader)
|
||||
{
|
||||
name = materialName
|
||||
};
|
||||
ConfigureDefaultMaterial(obj, transparent);
|
||||
return obj;
|
||||
}
|
||||
Shader shader2 = (useLitShader ? (Shader.Find("Universal Render Pipeline/Particles/Lit") ?? Shader.Find("Universal Render Pipeline/Lit") ?? Shader.Find("Standard")) : (Shader.Find("Universal Render Pipeline/Particles/Unlit") ?? Shader.Find("Universal Render Pipeline/Unlit") ?? Shader.Find("Sprites/Default") ?? Shader.Find("Standard")));
|
||||
if (shader2 == null)
|
||||
{
|
||||
Debug.LogError("BlockSpace could not find a fallback shader for voxel materials.");
|
||||
return null;
|
||||
}
|
||||
Material obj2 = new Material(shader2)
|
||||
{
|
||||
name = materialName
|
||||
};
|
||||
ConfigureDefaultMaterial(obj2, transparent);
|
||||
return obj2;
|
||||
}
|
||||
|
||||
private static Material EnsureCompatibleMaterial(Material material, string materialName, bool transparent, bool useLitShader)
|
||||
{
|
||||
bool flag = UsesBlockSpaceVertexColorShader(material);
|
||||
bool flag2 = UsesBlockSpaceVertexColorShader(material, transparent, useLitShader);
|
||||
if (material == null || !SupportsVertexColorParticles(material) || (flag && !flag2) || (!flag && useLitShader && !UsesLitShader(material)) || (!flag && !useLitShader && UsesLitShader(material)))
|
||||
{
|
||||
return CreateDefaultMaterial(materialName, transparent, useLitShader);
|
||||
}
|
||||
ConfigureDefaultMaterial(material, transparent);
|
||||
return material;
|
||||
}
|
||||
|
||||
private static void ConfigureDefaultMaterial(Material material, bool transparent)
|
||||
{
|
||||
if (!(material == null))
|
||||
{
|
||||
if (material.HasProperty("_BaseColor"))
|
||||
{
|
||||
material.SetColor("_BaseColor", Color.white);
|
||||
}
|
||||
if (material.HasProperty("_Color"))
|
||||
{
|
||||
material.SetColor("_Color", Color.white);
|
||||
}
|
||||
if (material.HasProperty("_Smoothness"))
|
||||
{
|
||||
material.SetFloat("_Smoothness", transparent ? 0.05f : 0.15f);
|
||||
}
|
||||
if (material.HasProperty("_Metallic"))
|
||||
{
|
||||
material.SetFloat("_Metallic", 0f);
|
||||
}
|
||||
if (material.HasProperty("_BaseMap"))
|
||||
{
|
||||
material.SetTexture("_BaseMap", Texture2D.whiteTexture);
|
||||
}
|
||||
if (material.HasProperty("_ColorMode"))
|
||||
{
|
||||
material.SetFloat("_ColorMode", 0f);
|
||||
}
|
||||
if (material.HasProperty("_BaseColorAddSubDiff"))
|
||||
{
|
||||
material.SetVector("_BaseColorAddSubDiff", Vector4.zero);
|
||||
}
|
||||
if (material.HasProperty("_Surface"))
|
||||
{
|
||||
material.SetFloat("_Surface", transparent ? 1f : 0f);
|
||||
}
|
||||
if (material.HasProperty("_Blend"))
|
||||
{
|
||||
material.SetFloat("_Blend", 0f);
|
||||
}
|
||||
if (material.HasProperty("_Cull"))
|
||||
{
|
||||
material.SetFloat("_Cull", 2f);
|
||||
}
|
||||
if (material.HasProperty("_SrcBlend"))
|
||||
{
|
||||
material.SetFloat("_SrcBlend", transparent ? 5f : 1f);
|
||||
}
|
||||
if (material.HasProperty("_DstBlend"))
|
||||
{
|
||||
material.SetFloat("_DstBlend", transparent ? 10f : 0f);
|
||||
}
|
||||
if (material.HasProperty("_SrcBlendAlpha"))
|
||||
{
|
||||
material.SetFloat("_SrcBlendAlpha", 1f);
|
||||
}
|
||||
if (material.HasProperty("_DstBlendAlpha"))
|
||||
{
|
||||
material.SetFloat("_DstBlendAlpha", transparent ? 10f : 0f);
|
||||
}
|
||||
if (material.HasProperty("_ZWrite"))
|
||||
{
|
||||
material.SetFloat("_ZWrite", transparent ? 0f : 1f);
|
||||
}
|
||||
if (material.HasProperty("_AlphaClip"))
|
||||
{
|
||||
material.SetFloat("_AlphaClip", 0f);
|
||||
}
|
||||
material.renderQueue = (transparent ? 3000 : 2000);
|
||||
material.enableInstancing = false;
|
||||
if (transparent)
|
||||
{
|
||||
material.SetOverrideTag("RenderType", "Transparent");
|
||||
material.EnableKeyword("_SURFACE_TYPE_TRANSPARENT");
|
||||
}
|
||||
else
|
||||
{
|
||||
material.SetOverrideTag("RenderType", "Opaque");
|
||||
material.DisableKeyword("_SURFACE_TYPE_TRANSPARENT");
|
||||
}
|
||||
material.DisableKeyword("_COLOROVERLAY_ON");
|
||||
material.DisableKeyword("_COLORCOLOR_ON");
|
||||
material.DisableKeyword("_COLORADDSUBDIFF_ON");
|
||||
}
|
||||
}
|
||||
|
||||
private static bool SupportsVertexColorParticles(Material material)
|
||||
{
|
||||
if (material == null || material.shader == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!material.HasProperty("_ColorMode") && !material.HasProperty("_BaseColorAddSubDiff") && !material.shader.name.Contains("Particles"))
|
||||
{
|
||||
return UsesBlockSpaceVertexColorShader(material);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool UsesBlockSpaceVertexColorShader(Material material)
|
||||
{
|
||||
if (material == null || material.shader == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
string text = material.shader.name;
|
||||
if (!(text == "BlockSpace/VertexColorURP") && !(text == "BlockSpace/VertexColorURP_Unlit"))
|
||||
{
|
||||
return text == "BlockSpace/VertexColorURP_Transparent";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool UsesBlockSpaceVertexColorShader(Material material, bool transparent, bool useLitShader)
|
||||
{
|
||||
if (material != null && material.shader != null)
|
||||
{
|
||||
return material.shader.name == GetBlockSpaceVertexColorShaderName(transparent, useLitShader);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static string GetBlockSpaceVertexColorShaderName(bool transparent, bool useLitShader)
|
||||
{
|
||||
if (transparent)
|
||||
{
|
||||
return "BlockSpace/VertexColorURP_Transparent";
|
||||
}
|
||||
if (!useLitShader)
|
||||
{
|
||||
return "BlockSpace/VertexColorURP_Unlit";
|
||||
}
|
||||
return "BlockSpace/VertexColorURP";
|
||||
}
|
||||
|
||||
private static bool UsesLitShader(Material material)
|
||||
{
|
||||
if (material == null || material.shader == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
string text = material.shader.name;
|
||||
if (text.Contains("Lit"))
|
||||
{
|
||||
return !text.Contains("Unlit");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void SendUpdateGroupAfterMergeStub(VoxelGroup newGroup)
|
||||
{
|
||||
if (!(newGroup == null) && !suppressNetworkBroadcast)
|
||||
{
|
||||
ResolveNetworkSync()?.BroadcastModify(newGroup);
|
||||
}
|
||||
}
|
||||
|
||||
private void SendUpdateGroupStub(VoxelGroup group)
|
||||
{
|
||||
if (!(group == null) && !suppressNetworkBroadcast)
|
||||
{
|
||||
ResolveNetworkSync()?.BroadcastModify(group);
|
||||
}
|
||||
}
|
||||
|
||||
public void BroadcastGroupModify(VoxelGroup group)
|
||||
{
|
||||
SendUpdateGroupStub(group);
|
||||
}
|
||||
|
||||
private void SendRemoveGroupStub(ushort groupId, bool playDestroyAnimation, bool playDestroyParticles)
|
||||
{
|
||||
if (groupId != 0 && !suppressNetworkBroadcast)
|
||||
{
|
||||
ResolveNetworkSync()?.BroadcastDestroy(groupId, playDestroyAnimation, playDestroyParticles);
|
||||
}
|
||||
}
|
||||
|
||||
private void RecordStrokeUndo(VoxelGroup group, bool playSpawnAnimationOnRemote, bool playSpawnParticlesOnRemote = true)
|
||||
{
|
||||
if (!isUndoing)
|
||||
{
|
||||
GroupSnapshot groupSnapshot = CaptureSnapshot(group);
|
||||
if (groupSnapshot != null)
|
||||
{
|
||||
UndoRecord undoRecord = new UndoRecord
|
||||
{
|
||||
type = UndoOperationType.Stroke
|
||||
};
|
||||
undoRecord.after.Add(groupSnapshot);
|
||||
PushUndoRecord(undoRecord);
|
||||
ResolveNetworkSync()?.BroadcastCreate(group, playSpawnAnimationOnRemote, playSpawnParticlesOnRemote);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private GroupSnapshot CaptureSnapshot(VoxelGroup group)
|
||||
{
|
||||
if (group == null || group.voxels == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return new GroupSnapshot
|
||||
{
|
||||
id = group.id,
|
||||
position = group.position,
|
||||
packedRotation = group.packedRotation,
|
||||
size = group.size,
|
||||
voxels = (byte[])group.voxels.Clone()
|
||||
};
|
||||
}
|
||||
|
||||
private void RestoreSnapshot(GroupSnapshot snapshot)
|
||||
{
|
||||
if (snapshot == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
bool flag = groups.ContainsKey(snapshot.id);
|
||||
VoxelGroup voxelGroup = UpsertGroup(snapshot.id, snapshot.position, snapshot.packedRotation, snapshot.size, (byte[])snapshot.voxels.Clone(), queueInitialRebuild: true, !flag, playSpawnParticlesForNewGroup: false);
|
||||
if (!(voxelGroup == null))
|
||||
{
|
||||
if (flag)
|
||||
{
|
||||
SendUpdateGroupStub(voxelGroup);
|
||||
}
|
||||
else
|
||||
{
|
||||
ResolveNetworkSync()?.BroadcastCreate(voxelGroup, playSpawnAnimation: true, playSpawnParticles: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void PushUndoRecord(UndoRecord undoRecord)
|
||||
{
|
||||
if (!isUndoing && undoRecord != null && (undoRecord.before.Count != 0 || undoRecord.after.Count != 0))
|
||||
{
|
||||
undoStack.Push(undoRecord);
|
||||
}
|
||||
}
|
||||
|
||||
private VoxelGroup UpsertGroup(ushort id, Vector3Int position, byte rotation, Vector3Int size, byte[] voxels, bool queueInitialRebuild, bool playSpawnAnimationForNewGroup, bool playSpawnParticlesForNewGroup)
|
||||
{
|
||||
byte[] voxelData = ((voxels != null) ? ((byte[])voxels.Clone()) : new byte[0]);
|
||||
if (groups.TryGetValue(id, out var value))
|
||||
{
|
||||
value.OverwriteData(position, rotation, size, voxelData, queueInitialRebuild);
|
||||
AdvanceNextIdPast(id);
|
||||
return value;
|
||||
}
|
||||
GameObject obj = new GameObject($"VoxelGroup_{id:D5}");
|
||||
obj.transform.SetParent(base.transform, worldPositionStays: false);
|
||||
VoxelGroup voxelGroup = obj.AddComponent<VoxelGroup>();
|
||||
voxelGroup.Initialize(this, id, position, rotation, size, voxelData, queueInitialRebuild);
|
||||
AddGroup(voxelGroup);
|
||||
if (playSpawnAnimationForNewGroup)
|
||||
{
|
||||
StartCoroutine(AnimateSpawnedGroup(voxelGroup, playSpawnParticlesForNewGroup));
|
||||
}
|
||||
return voxelGroup;
|
||||
}
|
||||
|
||||
private void AdvanceNextIdPast(ushort id)
|
||||
{
|
||||
if (!PhotonNetwork.InRoom && id >= nextId)
|
||||
{
|
||||
nextId = (ushort)(id + 1);
|
||||
if (nextId == 0)
|
||||
{
|
||||
nextId = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureEffectAssetsAssigned()
|
||||
{
|
||||
}
|
||||
|
||||
private IEnumerator AnimateSpawnedGroup(VoxelGroup group, bool playParticles)
|
||||
{
|
||||
if (!(group == null))
|
||||
{
|
||||
if (group.dirtyMesh)
|
||||
{
|
||||
group.RebuildMesh();
|
||||
}
|
||||
if (group.meshCollider != null)
|
||||
{
|
||||
group.meshCollider.enabled = false;
|
||||
}
|
||||
Vector3 center = group.GetWorldBounds().center;
|
||||
Quaternion rotation = group.transform.rotation;
|
||||
Vector3 localBoundsCenter = GetGroupLocalBoundsCenter(group);
|
||||
ApplyCenteredAnimatedScale(group, center, rotation, localBoundsCenter, 0.01f);
|
||||
PlayLifecycleClip(spawnClip, center, 1f);
|
||||
if (playParticles)
|
||||
{
|
||||
SpawnLifecycleParticle(center, rotation);
|
||||
}
|
||||
float elapsed = 0f;
|
||||
while (elapsed < 0.17f)
|
||||
{
|
||||
elapsed += Time.unscaledDeltaTime;
|
||||
float t = Mathf.Clamp01(elapsed / 0.17f);
|
||||
float uniformScale = Mathf.LerpUnclamped(0.01f, 1.2f, t);
|
||||
ApplyCenteredAnimatedScale(group, center, rotation, localBoundsCenter, uniformScale);
|
||||
yield return null;
|
||||
}
|
||||
elapsed = 0f;
|
||||
while (elapsed < 0.18f)
|
||||
{
|
||||
elapsed += Time.unscaledDeltaTime;
|
||||
float t2 = Mathf.Clamp01(elapsed / 0.18f);
|
||||
float uniformScale2 = Mathf.LerpUnclamped(1.2f, 1f, t2);
|
||||
ApplyCenteredAnimatedScale(group, center, rotation, localBoundsCenter, uniformScale2);
|
||||
yield return null;
|
||||
}
|
||||
ResetAnimatedGroupTransform(group);
|
||||
if (group != null && group.meshCollider != null)
|
||||
{
|
||||
group.meshCollider.enabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator AnimateAndDestroyGroup(VoxelGroup group, bool playParticles)
|
||||
{
|
||||
if (!(group == null))
|
||||
{
|
||||
Vector3 center = group.GetWorldBounds().center;
|
||||
Quaternion rotation = group.transform.rotation;
|
||||
Vector3 localBoundsCenter = GetGroupLocalBoundsCenter(group);
|
||||
if (group.meshCollider != null)
|
||||
{
|
||||
group.meshCollider.enabled = false;
|
||||
}
|
||||
PlayLifecycleClip(deleteClip, center, 1f);
|
||||
float elapsed = 0f;
|
||||
while (elapsed < 0.18f)
|
||||
{
|
||||
elapsed += Time.unscaledDeltaTime;
|
||||
float t = Mathf.Clamp01(elapsed / 0.18f);
|
||||
float uniformScale = Mathf.LerpUnclamped(1f, 1.2f, t);
|
||||
ApplyCenteredAnimatedScale(group, center, rotation, localBoundsCenter, uniformScale);
|
||||
yield return null;
|
||||
}
|
||||
elapsed = 0f;
|
||||
while (elapsed < 0.17f)
|
||||
{
|
||||
elapsed += Time.unscaledDeltaTime;
|
||||
float t2 = Mathf.Clamp01(elapsed / 0.17f);
|
||||
float uniformScale2 = Mathf.LerpUnclamped(1.2f, 0.01f, t2);
|
||||
ApplyCenteredAnimatedScale(group, center, rotation, localBoundsCenter, uniformScale2);
|
||||
yield return null;
|
||||
}
|
||||
if (playParticles)
|
||||
{
|
||||
SpawnLifecycleParticle(center, rotation);
|
||||
}
|
||||
if (group != null)
|
||||
{
|
||||
UnityEngine.Object.Destroy(group.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Vector3 GetGroupLocalBoundsCenter(VoxelGroup group)
|
||||
{
|
||||
return new Vector3(group.size.x, group.size.y, group.size.z) * (voxelSize * 0.5f);
|
||||
}
|
||||
|
||||
private void ApplyCenteredAnimatedScale(VoxelGroup group, Vector3 worldCenter, Quaternion rotation, Vector3 localBoundsCenter, float uniformScale)
|
||||
{
|
||||
if (!(group == null))
|
||||
{
|
||||
Vector3 vector = localBoundsCenter * uniformScale;
|
||||
Vector3 position = worldCenter - rotation * vector;
|
||||
group.transform.SetPositionAndRotation(position, rotation);
|
||||
group.transform.localScale = Vector3.one * uniformScale;
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetAnimatedGroupTransform(VoxelGroup group)
|
||||
{
|
||||
if (!(group == null))
|
||||
{
|
||||
group.transform.localScale = Vector3.one;
|
||||
group.transform.SetPositionAndRotation((Vector3)group.position * voxelSize, GridRotationUtility.ToQuaternion(group.packedRotation));
|
||||
}
|
||||
}
|
||||
|
||||
private void PlayLifecycleClip(AudioClip clip, Vector3 worldPosition, float volume)
|
||||
{
|
||||
if (!(clip == null))
|
||||
{
|
||||
AudioSource.PlayClipAtPoint(clip, worldPosition, volume);
|
||||
}
|
||||
}
|
||||
|
||||
private void SpawnLifecycleParticle(Vector3 worldPosition, Quaternion rotation)
|
||||
{
|
||||
if (!(spawnDestroyParticlePrefab == null))
|
||||
{
|
||||
UnityEngine.Object.Destroy(UnityEngine.Object.Instantiate(spawnDestroyParticlePrefab, worldPosition, rotation), 0.7f);
|
||||
}
|
||||
}
|
||||
|
||||
private VoxelGroupPhotonSync ResolveNetworkSync()
|
||||
{
|
||||
if (networkSync == null)
|
||||
{
|
||||
TryGetComponent<VoxelGroupPhotonSync>(out networkSync);
|
||||
}
|
||||
return networkSync;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user