gg.easy.airship 0.1.1661 → 0.1.1663

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,6 @@
1
1
  using System;
2
2
  using UnityEngine;
3
+ using System.Collections.Generic;
3
4
 
4
5
  #if UNITY_EDITOR
5
6
  using UnityEditor;
@@ -32,6 +33,9 @@ public class SelectionZone : MonoBehaviour
32
33
  [NonSerialized]
33
34
  private float previousThickness;
34
35
 
36
+ [NonSerialized]
37
+ public VoxelWorld voxelWorld;
38
+
35
39
  void OnEnable()
36
40
  {
37
41
  mesh = new Mesh();
@@ -253,105 +257,338 @@ public class SelectionZone : MonoBehaviour
253
257
 
254
258
 
255
259
  #if UNITY_EDITOR
260
+
256
261
  [CustomEditor(typeof(SelectionZone))]
257
- public class SelectionZoneEditor : Editor {
258
- private const float handleSize = 0.1f;
259
- private const float snapValue = 1.0f;
262
+ public class SelectionZoneEditor : UnityEditor.Editor {
263
+ private const float handleSize = 0.3f;
264
+
265
+ private bool mouseDown = false;
266
+
267
+ static bool haveCopiedData = false;
268
+ static UInt16[] copiedData;
269
+ static Vector3Int copiedSize;
270
+
271
+ // Define local handle positions based on the cube's size
272
+ Vector3[] localHandleVectors = new Vector3[6] {
273
+ new Vector3(1, 0, 0), // Right
274
+ new Vector3(-1, 0, 0), // Left
275
+ new Vector3(0, 1, 0), // Top
276
+ new Vector3(0, -1, 0), // Bottom
277
+ new Vector3(0, 0, 1), // Front
278
+ new Vector3(0, 0, -1) // Back
279
+ };
280
+
281
+ Color[] axisColors = new Color[6] {
282
+ Color.red,
283
+ Color.red,
284
+ Color.green,
285
+ Color.green,
286
+ Color.blue,
287
+ Color.blue
288
+ };
289
+
290
+ float[] handleOffset = new float[6] {
291
+ 1,1,1,1,1,1
292
+ };
293
+
294
+ float[] trueHandleOffset = new float[6] {
295
+ 1,1,1,1,1,1
296
+ };
297
+
298
+ //Gui
299
+ public override void OnInspectorGUI() {
300
+ //draw default
301
+ //DrawDefaultInspector();
302
+
303
+ //Add typeins for size x y and z
304
+ SelectionZone cube = (SelectionZone)target;
305
+ Vector3Int oldSize = new Vector3Int((int)cube.size.x, (int)cube.size.y, (int)cube.size.z);
306
+ Vector3Int newSize = EditorGUILayout.Vector3IntField("Size", oldSize);
307
+
308
+ if (newSize != oldSize) {
309
+ cube.size = newSize;
310
+ SnapToGrid();
311
+ cube.BuildCube();
312
+ ResetHandles();
313
+ }
260
314
 
261
- void Awake() {
315
+ //Draw a reset button
316
+ if (GUILayout.Button("Reset")) {
317
+ handleOffset = new float[6] {
318
+ 1,1,1,1,1,1
319
+ };
320
+ trueHandleOffset = new float[6] {
321
+ 1,1,1,1,1,1
322
+ };
323
+
324
+ cube.size = new Vector3(1, 1, 1);
325
+ cube.BuildCube();
326
+ }
327
+ if (cube.voxelWorld == null) {
328
+ return;
329
+ }
330
+ if (cube.voxelWorld.voxelBlocks == null) {
331
+ return;
332
+ }
333
+ VoxelEditManager voxelEditManager = VoxelEditManager.Instance;
334
+
335
+ //Add Copy Button
336
+ if (GUILayout.Button("Fill")) {
337
+ //walk the bounds
338
+ float dx = cube.size.x / 2;
339
+ float dy = cube.size.y / 2;
340
+ float dz = cube.size.z / 2;
341
+ float px = cube.transform.localPosition.x;
342
+ float py = cube.transform.localPosition.y;
343
+ float pz = cube.transform.localPosition.z;
344
+
345
+ if (cube.voxelWorld) {
346
+
347
+ List<VoxelEditAction.EditInfo> edits = new();
348
+
349
+ int selectedIndex = cube.voxelWorld.selectedBlockIndex;
350
+ //Walk the current selection zone
351
+ for (int x = Mathf.FloorToInt(px - dx); x < Mathf.CeilToInt(px + dx); x++) {
352
+ for (int y = Mathf.FloorToInt(py - dy); y < Mathf.CeilToInt(py + dy); y++) {
353
+ for (int z = Mathf.FloorToInt(pz - dz); z < Mathf.CeilToInt(pz + dz); z++) {
354
+ UInt16 prevData = cube.voxelWorld.ReadVoxelAt(new Vector3Int(x, y, z));
355
+ edits.Add(new VoxelEditAction.EditInfo(new Vector3Int(x, y, z), prevData, (UInt16)selectedIndex));
356
+ }
357
+ }
358
+ }
359
+ voxelEditManager.AddEdits(cube.voxelWorld, edits, "Fill Voxels");
360
+ }
361
+ }
262
362
 
263
- //Add a handler for the gizmo refresh event
264
- SceneView.duringSceneGui += GizmoRefreshEvent;
363
+ if (GUILayout.Button("Copy")) {
364
+ //walk the bounds
365
+ float dx = cube.size.x / 2;
366
+ float dy = cube.size.y / 2;
367
+ float dz = cube.size.z / 2;
368
+ float px = cube.transform.localPosition.x;
369
+ float py = cube.transform.localPosition.y;
370
+ float pz = cube.transform.localPosition.z;
371
+
372
+ haveCopiedData = true;
373
+ copiedSize = new Vector3Int((int)cube.size.x, (int)cube.size.y, (int)cube.size.z);
374
+
375
+ copiedData = new UInt16[(int)cube.size.x * (int)cube.size.y * (int)cube.size.z];
376
+
377
+ if (cube.voxelWorld) {
378
+
379
+ int index = 0;
380
+ //Walk the current selection zone
381
+ for (int x = Mathf.FloorToInt(px - dx); x < Mathf.CeilToInt(px + dx); x++) {
382
+ for (int y = Mathf.FloorToInt(py - dy); y < Mathf.CeilToInt(py + dy); y++) {
383
+ for (int z = Mathf.FloorToInt(pz - dz); z < Mathf.CeilToInt(pz + dz); z++) {
384
+ copiedData[index++] = cube.voxelWorld.ReadVoxelAt(new Vector3Int(x, y, z));
385
+ }
386
+ }
387
+ }
388
+ }
389
+ }
390
+
391
+ if (haveCopiedData == false) {
392
+ //Disable ui
393
+ GUI.enabled = false;
394
+ //Make fake paste button
395
+ if (GUILayout.Button("Paste")) {
396
+ //Do nothing
397
+ }
398
+
399
+ GUI.enabled = true;
400
+ }else {
401
+ //Actual paste
402
+ if (GUILayout.Button("Paste")) {
403
+ //walk the bouns
404
+ float dx = copiedSize.x / 2;
405
+ float dy = copiedSize.y / 2;
406
+ float dz = copiedSize.z / 2;
407
+ float px = cube.transform.localPosition.x;
408
+ float py = cube.transform.localPosition.y;
409
+ float pz = cube.transform.localPosition.z;
410
+
411
+ if (cube.voxelWorld) {
412
+ int index = 0;
413
+
414
+ List<VoxelEditAction.EditInfo> edits = new();
415
+
416
+ //Walk the current selection zone
417
+ for (int x = Mathf.FloorToInt(px - dx); x < Mathf.CeilToInt(px + dx); x++) {
418
+ for (int y = Mathf.FloorToInt(py - dy); y < Mathf.CeilToInt(py + dy); y++) {
419
+ for (int z = Mathf.FloorToInt(pz - dz); z < Mathf.CeilToInt(pz + dz); z++) {
420
+ //cube.voxelWorld.WriteVoxelAt(new Vector3Int(x, y, z), copiedData[index++], false);
421
+ UInt16 prevData = cube.voxelWorld.ReadVoxelAt(new Vector3Int(x, y, z));
422
+ edits.Add(new VoxelEditAction.EditInfo(new Vector3Int(x, y, z), prevData, copiedData[index++]));
423
+ }
424
+ }
425
+ }
426
+ voxelEditManager.AddEdits(cube.voxelWorld, edits, "Paste Voxels");
427
+
428
+
429
+ }
430
+
431
+ //resize the box to whatever we pasted
432
+ cube.size = new Vector3(copiedSize.x, copiedSize.y, copiedSize.z);
433
+ SnapToGrid();
434
+ cube.BuildCube();
435
+ ResetHandles();
436
+ }
437
+ }
265
438
  }
266
439
 
440
+
441
+ void Awake() {
442
+ // Add a handler for the gizmo refresh event
443
+ SceneView.duringSceneGui += GizmoRefreshEvent;
444
+
445
+ SnapToGrid();
446
+ }
447
+
448
+ private void ResetHandles() {
449
+ SelectionZone cube = (SelectionZone)target;
450
+ trueHandleOffset[0] = (cube.size.x / 2) + 0.5f;
451
+ trueHandleOffset[1] = (cube.size.x / 2) + 0.5f;
452
+ trueHandleOffset[2] = (cube.size.y / 2) + 0.5f;
453
+ trueHandleOffset[3] = (cube.size.y / 2) + 0.5f;
454
+ trueHandleOffset[4] = (cube.size.z / 2) + 0.5f;
455
+ trueHandleOffset[5] = (cube.size.z / 2) + 0.5f;
456
+ for (int j = 0; j < 6; j++) {
457
+ handleOffset[j] = trueHandleOffset[j];
458
+ }
459
+ }
460
+ private void OnDestroy() {
461
+
462
+ SceneView.duringSceneGui -= GizmoRefreshEvent;
463
+ }
464
+
465
+ public void SnapToGrid() {
466
+ SelectionZone cube = (SelectionZone)target;
467
+ Transform cubeTransform = cube.transform;
468
+
469
+ // Snap the position to the nearest 0.5 unit grid
470
+ float x = Mathf.Floor(cubeTransform.localPosition.x);
471
+ float y = Mathf.Floor(cubeTransform.localPosition.y);
472
+ float z = Mathf.Floor(cubeTransform.localPosition.z);
473
+
474
+ // Adjust snapping if the size is even
475
+ if (Mathf.Round(cube.size.x) % 2 == 1) {
476
+ x += 0.5f;
477
+ }
478
+ if (Mathf.Round(cube.size.y) % 2 == 1) {
479
+ y += 0.5f;
480
+ }
481
+ if (Mathf.Round(cube.size.z) % 2 == 1) {
482
+ z += 0.5f;
483
+ }
484
+
485
+ // Set the snapped position
486
+ cubeTransform.localPosition = new Vector3(x, y, z);
487
+ cubeTransform.localRotation = Quaternion.identity;
488
+ cubeTransform.localScale = Vector3.one;
489
+ }
490
+
267
491
  void GizmoRefreshEvent(SceneView obj) {
268
492
  SelectionZone cube = (SelectionZone)target;
493
+ if (target == null) {
494
+ return;
495
+ }
269
496
 
270
- // Define handle positions based on the cube's size
271
- Vector3[] handles = new Vector3[]
272
- {
273
- cube.transform.position + new Vector3(cube.size.x / 2, 0, 0), // Right
274
- cube.transform.position + new Vector3(-cube.size.x / 2, 0, 0), // Left
275
- cube.transform.position + new Vector3(0, cube.size.y / 2, 0), // Top
276
- cube.transform.position + new Vector3(0, -cube.size.y / 2, 0), // Bottom
277
- cube.transform.position + new Vector3(0, 0, cube.size.z / 2), // Front
278
- cube.transform.position + new Vector3(0, 0, -cube.size.z / 2) // Back
279
- };
497
+ if (cube.transform.hasChanged) {
498
+ //Debug.Log("Has changed");
499
+ SnapToGrid();
500
+ cube.transform.hasChanged = false;
501
+ }
502
+
503
+
504
+
505
+ //capture mouse up and mouse down
506
+ if (Event.current.type == EventType.MouseUp) {
507
+ mouseDown = false;
508
+
509
+ }
510
+ if (Event.current.type == EventType.MouseDown) {
511
+ mouseDown = true;
512
+ }
280
513
 
514
+ Transform cubeTransform = cube.transform;
515
+
281
516
  EditorGUI.BeginChangeCheck();
282
517
 
518
+ Vector3 motion = Vector3.zero;
519
+
283
520
  // Move handles with constraints
284
- for (int i = 0; i < handles.Length; i++) {
521
+ for (int i = 0; i < localHandleVectors.Length; i++) {
522
+ Vector3 localHandleStartPos = localHandleVectors[i] * handleOffset[i];
523
+ Vector3 worldHandleStartPos = cubeTransform.TransformPoint(localHandleStartPos);
285
524
  Vector3 axis = Vector3.zero;
286
- bool isNegativeHandle = false;
525
+ float isNegativeHandle = 1;
526
+
287
527
  switch (i) {
288
528
  case 0: axis = Vector3.right; break;
289
- case 1: axis = Vector3.right; isNegativeHandle = true; break;
529
+ case 1: axis = Vector3.right; isNegativeHandle = -1; break;
290
530
  case 2: axis = Vector3.up; break;
291
- case 3: axis = Vector3.up; isNegativeHandle = true; break;
531
+ case 3: axis = Vector3.up; isNegativeHandle = -1; break;
292
532
  case 4: axis = Vector3.forward; break;
293
- case 5: axis = Vector3.forward; isNegativeHandle = true; break;
533
+ case 5: axis = Vector3.forward; isNegativeHandle = -1; break;
294
534
  }
295
535
 
536
+
296
537
  // Draw spheres as handles and constrain movement
297
- Vector3 newPos = Handles.Slider(handles[i], axis, handleSize, Handles.SphereHandleCap, 0);
298
-
299
- // Calculate the snapped movement
300
- float movement = Mathf.Floor((newPos - handles[i]).magnitude / snapValue) * snapValue * Mathf.Sign(Vector3.Dot(newPos - handles[i], axis));
538
+ //Set the color
539
+ Handles.color = axisColors[i];
540
+ Vector3 newWorldPos = Handles.Slider(worldHandleStartPos, cubeTransform.TransformDirection(axis), handleSize, Handles.SphereHandleCap,0);
301
541
 
302
- float resize = movement;
542
+ Handles.color = Color.white;
303
543
 
304
- if (movement != 0) {
305
- // Invert the movement for negative handles
306
- if (isNegativeHandle) {
307
- resize = -resize;
308
- }
544
+ Vector3 localHandlePos = cubeTransform.InverseTransformPoint(newWorldPos);
309
545
 
310
- switch (i) {
311
- case 0:
312
- case 1:
313
- cube.size.x += resize;
314
- cube.transform.position += axis * (movement / 2);
315
- break;
316
- case 2:
317
- case 3:
318
- cube.size.y += resize;
319
- cube.transform.position += axis * (movement / 2);
320
- break;
321
- case 4:
322
- case 5:
323
- cube.size.z += resize;
324
- cube.transform.position += axis * (movement / 2);
325
- break;
326
- }
546
+ bool moved = false;
547
+ if ((newWorldPos - worldHandleStartPos).magnitude > 0) {
548
+ moved = true;
327
549
  }
328
- }
329
-
330
- if (EditorGUI.EndChangeCheck()) {
331
- Undo.RecordObject(cube, "Resize Cube");
332
- cube.size = new Vector3(Mathf.Max(1, Mathf.Round(cube.size.x)), Mathf.Max(1, Mathf.Round(cube.size.y)), Mathf.Max(1, Mathf.Round(cube.size.z)));
333
-
334
- //Snap the position
335
- //cube.transform.position = new Vector3(Mathf.Floor(cube.transform.position.x), Mathf.Floor(cube.transform.position.y), Mathf.Floor(cube.transform.position.z));
336
550
 
337
- //Snap each axis of the position separately, depending on if that size is odd or even
338
- float x = Mathf.Floor(cube.transform.position.x);
339
- float y = Mathf.Floor(cube.transform.position.y);
340
- float z = Mathf.Floor(cube.transform.position.z);
341
-
342
- if (Mathf.Round(cube.size.x) % 2 == 0) {
343
- x += 0.5f;
344
- }
345
- if (Mathf.Round(cube.size.y) % 2 == 0) {
346
- y += 0.5f;
347
- }
348
- if (Mathf.Round(cube.size.z) % 2 == 0) {
349
- z += 0.5f;
551
+ if (mouseDown == false && Mathf.Abs(trueHandleOffset[i] - handleOffset[i]) > Mathf.Epsilon) {
552
+ handleOffset[i] = trueHandleOffset[i]; //Reset it
350
553
  }
351
- //Set it
352
- cube.transform.position = new Vector3(x, y, z);
353
-
554
+
555
+ if (moved == true) {
556
+
557
+ float distance = localHandlePos.magnitude - trueHandleOffset[i];
558
+ float steps = Mathf.Floor(distance);
559
+
560
+ handleOffset[i] = localHandlePos.magnitude;
561
+
562
+ if (steps != 0) {
563
+
564
+ cube.size += steps * axis;
565
+ cube.transform.localPosition += (steps * axis / 2) * isNegativeHandle;
566
+ SnapToGrid();
567
+
568
+ //Recalc handle pos
569
+ trueHandleOffset[0] = (cube.size.x / 2) + 0.5f;
570
+ trueHandleOffset[1] = (cube.size.x / 2) + 0.5f;
571
+ trueHandleOffset[2] = (cube.size.y / 2) + 0.5f;
572
+ trueHandleOffset[3] = (cube.size.y / 2) + 0.5f;
573
+ trueHandleOffset[4] = (cube.size.z / 2) + 0.5f;
574
+ trueHandleOffset[5] = (cube.size.z / 2) + 0.5f;
575
+ for (int j = 0; j < 6; j++) {
576
+ //Reset all handles except the one being dragged
577
+ if (j==i) {
578
+ continue;
579
+ }
580
+ handleOffset[j] = trueHandleOffset[j];
581
+ }
582
+
583
+ handleOffset[i] -= (steps * 0.5f);
354
584
 
585
+ }
586
+
587
+ }
588
+ }
589
+
590
+ if (EditorGUI.EndChangeCheck()) {
591
+
355
592
  cube.BuildCube();
356
593
  EditorUtility.SetDirty(cube);
357
594
  }
@@ -53,6 +53,14 @@ namespace Code.Airship.Resources.VoxelRenderer.Editor {
53
53
  return voxelWorld;
54
54
  }
55
55
  }
56
+
57
+ if (selectedObject) {
58
+ var selectionZone = selectedObject.GetComponentInParent<SelectionZone>();
59
+ if (selectionZone && selectionZone.voxelWorld) {
60
+ return selectionZone.voxelWorld;
61
+ }
62
+ }
63
+
56
64
  return null;
57
65
  }
58
66
 
@@ -64,9 +72,13 @@ namespace Code.Airship.Resources.VoxelRenderer.Editor {
64
72
 
65
73
  //Shows a list of all the VoxelWorld objects in the scene as clickable buttons
66
74
  VoxelWorld[] voxelWorlds = GameObject.FindObjectsOfType<VoxelWorld>();
75
+
67
76
  for (int i = 0; i < voxelWorlds.Length; i++) {
68
77
 
69
- if (Selection.activeGameObject == voxelWorlds[i].gameObject) {
78
+
79
+ SelectionZone selectionZone = voxelWorlds[i].GetComponentInChildren<SelectionZone>();
80
+
81
+ if (Selection.activeGameObject == voxelWorlds[i].gameObject || (selectionZone!=null && Selection.activeGameObject == selectionZone.gameObject)) {
70
82
  GUI.backgroundColor = Color.green;
71
83
  }
72
84
  else {
@@ -85,6 +97,7 @@ namespace Code.Airship.Resources.VoxelRenderer.Editor {
85
97
 
86
98
  GUI.backgroundColor = Color.white;
87
99
  }
100
+
88
101
 
89
102
  void OnGUI() {
90
103
  //Create an active toggle as a button that toggles on and off
@@ -96,11 +109,43 @@ namespace Code.Airship.Resources.VoxelRenderer.Editor {
96
109
 
97
110
  ShowSelectionGui();
98
111
 
112
+
113
+
99
114
  VoxelWorld world = GetVoxelWorld();
115
+ SelectionZone selection = null;
100
116
  if (world == null || world.voxelBlocks == null) {
101
- GUI.enabled = true;
117
+ GUI.enabled = true; //cleanup from above
102
118
  return;
103
119
  }
120
+
121
+
122
+ //See if we're in the selection mode
123
+ if (VoxelWorldSelectionToolBase.buttonActive == true) {
124
+ //Find or create the SelectionZone for this voxelWorld
125
+
126
+ selection = world.GetComponentInChildren<SelectionZone>();
127
+ if (selection == null) {
128
+ selection = new GameObject("SelectionZone").AddComponent<SelectionZone>();
129
+ selection.hideFlags = HideFlags.DontSave;
130
+ selection.transform.parent = world.transform;
131
+ selection.transform.localPosition = Vector3.zero;
132
+ selection.voxelWorld = world;
133
+ }
134
+ //Select this
135
+ Selection.activeGameObject = selection.gameObject;
136
+ }
137
+ if (VoxelWorldEditorToolBase.buttonActive == true) {
138
+
139
+ //If we're not in selection mode, destroy the selection zone
140
+ selection = world.GetComponentInChildren<SelectionZone>();
141
+
142
+ if (selection) {
143
+ //Select the world
144
+ Selection.activeGameObject = world.gameObject;
145
+ //Destroy it
146
+ DestroyImmediate(selection.gameObject);
147
+ }
148
+ }
104
149
 
105
150
  //Show a foldable help box
106
151
  EditorGUILayout.HelpBox("Left click to add\nShift+click to delete\nCtrl+click for repeat placement", MessageType.Info);
@@ -119,13 +164,19 @@ namespace Code.Airship.Resources.VoxelRenderer.Editor {
119
164
 
120
165
  GUIStyle selectedStyle = new GUIStyle(GUI.skin.button);
121
166
  selectedStyle.normal.textColor = Color.green;
122
-
167
+ selectedStyle.hover.textColor = Color.green;
123
168
 
124
169
  foreach (var pair in world.voxelBlocks.loadedBlocks) {
170
+
171
+ string name = pair.Value.definition.name;
172
+ if (name == "") {
173
+ name = "Air";
174
+ }
175
+
125
176
  if (pair.Key == world.selectedBlockIndex) {
126
- GUILayout.Button(pair.Value.definition.name, selectedStyle);
177
+ GUILayout.Button(name, selectedStyle);
127
178
  } else {
128
- if (GUILayout.Button(pair.Value.definition.name)) {
179
+ if (GUILayout.Button(name)) {
129
180
  world.selectedBlockIndex = pair.Key;
130
181
  }
131
182
  }
@@ -133,6 +184,10 @@ namespace Code.Airship.Resources.VoxelRenderer.Editor {
133
184
  GUILayout.EndScrollView();
134
185
  GUI.enabled = true;
135
186
  }
187
+
188
+ void OnEnable() {
189
+ base.autoRepaintOnSceneChange = true;
190
+ }
136
191
  }
137
192
  }
138
193
  #endif
@@ -7,17 +7,32 @@ using UnityEditor.EditorTools;
7
7
  using UnityEngine;
8
8
  using System;
9
9
  using static UnityEditor.PlayerSettings;
10
+ using static VoxelEditAction;
10
11
 
11
12
  public class VoxelEditAction {
12
- public Vector3Int position;
13
- public ushort oldValue;
14
- public ushort newValue;
13
+
14
+ public struct EditInfo{
15
+ public Vector3Int position;
16
+ public ushort oldValue;
17
+ public ushort newValue;
18
+ //constructor
19
+ public EditInfo(Vector3Int position, ushort oldValue, ushort newValue){
20
+ this.position = position;
21
+ this.oldValue = oldValue;
22
+ this.newValue = newValue;
23
+ }
24
+ }
25
+ public List<EditInfo> edits = new List<EditInfo>();
26
+
15
27
  [NonSerialized]
16
28
  public WeakReference<VoxelWorld> world;
17
- public void Initialize(VoxelWorld world, Vector3Int position, ushort oldValue, ushort newValue) {
18
- this.position = position;
19
- this.oldValue = oldValue;
20
- this.newValue = newValue;
29
+ public void CreateSingleEdit(VoxelWorld world, Vector3Int position, ushort oldValue, ushort newValue) {
30
+ edits.Add(new EditInfo(position, oldValue, newValue));
31
+ this.world = new(world);
32
+ }
33
+
34
+ public void CreateMultiEdit(VoxelWorld world, List<EditInfo> edits) {
35
+ this.edits = edits;
21
36
  this.world = new(world);
22
37
  }
23
38
  }
@@ -30,11 +45,10 @@ public class VoxelEditManager : Singleton<VoxelEditManager> {
30
45
  VoxelEditMarker undoObject;
31
46
  public List<VoxelEditAction> edits = new List<VoxelEditAction>();
32
47
  public List<VoxelEditAction> redos = new List<VoxelEditAction>();
33
-
34
-
48
+
35
49
  public void AddEdit(VoxelWorld world, Vector3Int position, ushort oldValue, ushort newValue, string name) {
36
50
  VoxelEditAction edit = new VoxelEditAction();
37
- edit.Initialize(world, position, oldValue, newValue);
51
+ edit.CreateSingleEdit(world, position, oldValue, newValue);
38
52
  edits.Add(edit);
39
53
 
40
54
  //If we're adding a new edit, clear the redos
@@ -52,9 +66,33 @@ public class VoxelEditManager : Singleton<VoxelEditManager> {
52
66
  world.DirtyNeighborMeshes(position);
53
67
 
54
68
  world.hasUnsavedChanges = true;
55
-
56
69
  }
57
70
 
71
+ public void AddEdits(VoxelWorld world, List<EditInfo> editInfos, string name) {
72
+ VoxelEditAction edit = new VoxelEditAction();
73
+ edit.CreateMultiEdit(world, editInfos);
74
+ edits.Add(edit);
75
+
76
+ //If we're adding a new edit, clear the redos
77
+ redos.Clear();
78
+
79
+ if (undoObject == null) {
80
+ undoObject = ScriptableObject.CreateInstance<VoxelEditMarker>();
81
+ }
82
+ undoObject.lastAction = edit;
83
+
84
+ //Save the state of this whole object into the undo system
85
+ Undo.RegisterCompleteObjectUndo(undoObject, name);
86
+
87
+ foreach (EditInfo editInfo in editInfos) {
88
+ world.WriteVoxelAtInternal(editInfo.position, editInfo.newValue);
89
+ world.DirtyNeighborMeshes(editInfo.position);
90
+ }
91
+
92
+ world.hasUnsavedChanges = true;
93
+ }
94
+
95
+
58
96
  //Constructor
59
97
  public VoxelEditManager() {
60
98
  Undo.undoRedoEvent += UndoRedoEvent;
@@ -69,8 +107,10 @@ public class VoxelEditManager : Singleton<VoxelEditManager> {
69
107
 
70
108
  edit.world.TryGetTarget(out VoxelWorld currentWorld);
71
109
  if (currentWorld){
72
- currentWorld.WriteVoxelAtInternal(edit.position, edit.oldValue);
73
- currentWorld.DirtyNeighborMeshes(edit.position);
110
+ foreach (var editInfo in edit.edits) {
111
+ currentWorld.WriteVoxelAtInternal(editInfo.position, editInfo.oldValue);
112
+ currentWorld.DirtyNeighborMeshes(editInfo.position);
113
+ }
74
114
  currentWorld.hasUnsavedChanges = true;
75
115
  }
76
116
 
@@ -83,8 +123,10 @@ public class VoxelEditManager : Singleton<VoxelEditManager> {
83
123
  redos.RemoveAt(redos.Count - 1);
84
124
  edit.world.TryGetTarget(out VoxelWorld currentWorld);
85
125
  if (currentWorld) {
86
- currentWorld.WriteVoxelAtInternal(edit.position, edit.newValue);
87
- currentWorld.DirtyNeighborMeshes(edit.position);
126
+ foreach (var editInfo in edit.edits) {
127
+ currentWorld.WriteVoxelAtInternal(editInfo.position, editInfo.newValue);
128
+ currentWorld.DirtyNeighborMeshes(editInfo.position);
129
+ }
88
130
  currentWorld.hasUnsavedChanges = true;
89
131
  }
90
132
 
@@ -380,9 +422,7 @@ public class VoxelWorldEditor : UnityEditor.Editor {
380
422
  handle.name = "_SelectionHandle";
381
423
  handle.hideFlags = HideFlags.HideAndDontSave;
382
424
  }
383
-
384
-
385
-
425
+
386
426
  if (faceHandle == null) {
387
427
  faceHandle = GameObject.CreatePrimitive(PrimitiveType.Quad);
388
428
  faceHandle.transform.localScale = new Vector3(1.01f, 1.01f, 1.01f);
@@ -396,13 +436,12 @@ public class VoxelWorldEditor : UnityEditor.Editor {
396
436
  if (Event.current.shift) { //Delete
397
437
  DestroyImmediate(faceHandle);
398
438
  faceHandle = null;
399
-
400
439
  }
401
-
402
440
 
403
441
  if (handle) {
404
442
  handle.transform.position = world.TransformPointToWorldSpace(lastPos + new Vector3(0.5f,0.5f,0.5f));
405
443
  handle.transform.localScale = new Vector3(1.01f, 1.01f, 1.01f);
444
+ handle.transform.localRotation = Quaternion.identity;
406
445
 
407
446
  WireCube wireCube = handle.GetComponent<WireCube>();
408
447
  if (wireCube) {
@@ -422,7 +461,7 @@ public class VoxelWorldEditor : UnityEditor.Editor {
422
461
 
423
462
  if (faceHandle) {
424
463
  faceHandle.transform.position = world.TransformPointToWorldSpace(lastPos + new Vector3(0.5f, 0.5f, 0.5f) + lastNormal * 0.51f);
425
- faceHandle.transform.rotation = Quaternion.LookRotation(lastNormal);
464
+ faceHandle.transform.rotation = Quaternion.LookRotation(world.TransformVectorToWorldSpace(lastNormal));
426
465
 
427
466
  MeshRenderer ren = faceHandle.GetComponent<MeshRenderer>();
428
467
  if (leftControlDown == true) {
@@ -447,7 +486,7 @@ public class VoxelWorldEditor : UnityEditor.Editor {
447
486
  Event e = Event.current;
448
487
 
449
488
  //Only allow editing if both the editor window is active and the gizmo toolbar is active
450
- bool enabled = VoxelBuilderEditorWindow.Enabled() && VoxelWorldEditorTool.buttonActive;
489
+ bool enabled = VoxelBuilderEditorWindow.Enabled() && VoxelWorldEditorToolBase.buttonActive;
451
490
 
452
491
  if (enabled != lastEnabled) {
453
492
  CleanupHandles();
@@ -567,26 +606,35 @@ public class VoxelWorldEditor : UnityEditor.Editor {
567
606
  SceneView.duringSceneGui += GizmoRefreshEvent;
568
607
  }
569
608
 
570
- void OnSelectionChanged() {
609
+ private void OnDestroy() {
610
+ //Remove selection handler
611
+ Selection.selectionChanged -= OnSelectionChanged;
612
+
613
+ //Remove the gizmo refresh event handler
614
+ SceneView.duringSceneGui -= GizmoRefreshEvent;
615
+ }
616
+
617
+ void OnSelectionChanged() {
571
618
  if (target == null) {
572
619
  return;
573
620
  }
574
621
  //If we're seleceted
575
622
  if (Selection.activeGameObject == ((VoxelWorld)target).gameObject) {
576
- ToolManager.SetActiveTool<VoxelWorldEditorTool>();
623
+ ToolManager.SetActiveTool<VoxelWorldEditorToolBase>();
577
624
  }
625
+
626
+
578
627
 
579
628
  }
580
629
  }
581
630
 
582
631
 
583
632
  //Create the spiffy toolbar addition
584
- [EditorTool("Edit Voxel World", typeof(VoxelWorld))]
585
- public class VoxelWorldEditorTool : EditorTool {
633
+ public class VoxelWorldEditorToolBase : EditorTool {
586
634
 
587
635
  public static bool buttonActive = false;
588
636
 
589
- GUIContent iconContent = null;
637
+ static GUIContent iconContent = null;
590
638
 
591
639
  public override void OnActivated() {
592
640
  buttonActive = true;
@@ -608,4 +656,49 @@ public class VoxelWorldEditorTool : EditorTool {
608
656
  }
609
657
  }
610
658
 
659
+
660
+
661
+ public class VoxelWorldSelectionToolBase : EditorTool {
662
+
663
+ public static bool buttonActive = false;
664
+
665
+ static GUIContent iconContent = null;
666
+
667
+ public override void OnActivated() {
668
+ buttonActive = true;
669
+
670
+ }
671
+ public override void OnWillBeDeactivated() {
672
+ buttonActive = false;
673
+ }
674
+ public override GUIContent toolbarIcon {
675
+ get {
676
+ if (iconContent == null) {
677
+ iconContent = new GUIContent() {
678
+ image = Resources.Load<Texture>("SelectIcon"),
679
+ tooltip = "Selection"
680
+ };
681
+ }
682
+ return iconContent;
683
+ }
684
+ }
685
+ }
686
+
687
+ [EditorTool("Edit Voxel World", typeof(VoxelWorld))]
688
+ public class VoxelWorldSelectionToolVW : VoxelWorldSelectionToolBase {
689
+ }
690
+
691
+ [EditorTool("Edit Voxel Selection", typeof(VoxelWorld))]
692
+ public class VoxelWorldEditorToolVW : VoxelWorldEditorToolBase {
693
+ }
694
+
695
+ //Same again for SelectionZone
696
+ [EditorTool("Edit Voxel World", typeof(SelectionZone))]
697
+ public class VoxelWorldSelectionToolSZ : VoxelWorldSelectionToolBase {
698
+ }
699
+
700
+ [EditorTool("Edit Voxel Selection", typeof(SelectionZone))]
701
+ public class VoxelWorldEditorToolSZ : VoxelWorldEditorToolBase {
702
+ }
703
+
611
704
  #endif
@@ -0,0 +1,153 @@
1
+ fileFormatVersion: 2
2
+ guid: e096bf0b5cc9a8240915e1374148925c
3
+ TextureImporter:
4
+ internalIDToNameTable: []
5
+ externalObjects: {}
6
+ serializedVersion: 13
7
+ mipmaps:
8
+ mipMapMode: 0
9
+ enableMipMap: 0
10
+ sRGBTexture: 1
11
+ linearTexture: 0
12
+ fadeOut: 0
13
+ borderMipMap: 0
14
+ mipMapsPreserveCoverage: 0
15
+ alphaTestReferenceValue: 0.5
16
+ mipMapFadeDistanceStart: 1
17
+ mipMapFadeDistanceEnd: 3
18
+ bumpmap:
19
+ convertToNormalMap: 0
20
+ externalNormalMap: 0
21
+ heightScale: 0.25
22
+ normalMapFilter: 0
23
+ flipGreenChannel: 0
24
+ isReadable: 0
25
+ streamingMipmaps: 0
26
+ streamingMipmapsPriority: 0
27
+ vTOnly: 0
28
+ ignoreMipmapLimit: 0
29
+ grayScaleToAlpha: 0
30
+ generateCubemap: 6
31
+ cubemapConvolution: 0
32
+ seamlessCubemap: 0
33
+ textureFormat: 1
34
+ maxTextureSize: 2048
35
+ textureSettings:
36
+ serializedVersion: 2
37
+ filterMode: 1
38
+ aniso: 1
39
+ mipBias: 0
40
+ wrapU: 1
41
+ wrapV: 1
42
+ wrapW: 0
43
+ nPOTScale: 0
44
+ lightmap: 0
45
+ compressionQuality: 50
46
+ spriteMode: 0
47
+ spriteExtrude: 1
48
+ spriteMeshType: 1
49
+ alignment: 0
50
+ spritePivot: {x: 0.5, y: 0.5}
51
+ spritePixelsToUnits: 100
52
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
53
+ spriteGenerateFallbackPhysicsShape: 1
54
+ alphaUsage: 1
55
+ alphaIsTransparency: 1
56
+ spriteTessellationDetail: -1
57
+ textureType: 2
58
+ textureShape: 1
59
+ singleChannelComponent: 0
60
+ flipbookRows: 1
61
+ flipbookColumns: 1
62
+ maxTextureSizeSet: 0
63
+ compressionQualitySet: 0
64
+ textureFormatSet: 0
65
+ ignorePngGamma: 0
66
+ applyGammaDecoding: 0
67
+ swizzle: 50462976
68
+ cookieLightType: 0
69
+ platformSettings:
70
+ - serializedVersion: 3
71
+ buildTarget: DefaultTexturePlatform
72
+ maxTextureSize: 2048
73
+ resizeAlgorithm: 0
74
+ textureFormat: -1
75
+ textureCompression: 1
76
+ compressionQuality: 50
77
+ crunchedCompression: 1
78
+ allowsAlphaSplitting: 0
79
+ overridden: 0
80
+ ignorePlatformSupport: 0
81
+ androidETC2FallbackOverride: 0
82
+ forceMaximumCompressionQuality_BC6H_BC7: 0
83
+ - serializedVersion: 3
84
+ buildTarget: Standalone
85
+ maxTextureSize: 2048
86
+ resizeAlgorithm: 0
87
+ textureFormat: -1
88
+ textureCompression: 1
89
+ compressionQuality: 50
90
+ crunchedCompression: 0
91
+ allowsAlphaSplitting: 0
92
+ overridden: 0
93
+ ignorePlatformSupport: 0
94
+ androidETC2FallbackOverride: 0
95
+ forceMaximumCompressionQuality_BC6H_BC7: 0
96
+ - serializedVersion: 3
97
+ buildTarget: Server
98
+ maxTextureSize: 2048
99
+ resizeAlgorithm: 0
100
+ textureFormat: -1
101
+ textureCompression: 1
102
+ compressionQuality: 50
103
+ crunchedCompression: 0
104
+ allowsAlphaSplitting: 0
105
+ overridden: 0
106
+ ignorePlatformSupport: 0
107
+ androidETC2FallbackOverride: 0
108
+ forceMaximumCompressionQuality_BC6H_BC7: 0
109
+ - serializedVersion: 3
110
+ buildTarget: Android
111
+ maxTextureSize: 2048
112
+ resizeAlgorithm: 0
113
+ textureFormat: -1
114
+ textureCompression: 1
115
+ compressionQuality: 50
116
+ crunchedCompression: 0
117
+ allowsAlphaSplitting: 0
118
+ overridden: 0
119
+ ignorePlatformSupport: 0
120
+ androidETC2FallbackOverride: 0
121
+ forceMaximumCompressionQuality_BC6H_BC7: 0
122
+ - serializedVersion: 3
123
+ buildTarget: iPhone
124
+ maxTextureSize: 2048
125
+ resizeAlgorithm: 0
126
+ textureFormat: -1
127
+ textureCompression: 1
128
+ compressionQuality: 50
129
+ crunchedCompression: 0
130
+ allowsAlphaSplitting: 0
131
+ overridden: 0
132
+ ignorePlatformSupport: 0
133
+ androidETC2FallbackOverride: 0
134
+ forceMaximumCompressionQuality_BC6H_BC7: 0
135
+ spriteSheet:
136
+ serializedVersion: 2
137
+ sprites: []
138
+ outline: []
139
+ physicsShape: []
140
+ bones: []
141
+ spriteID:
142
+ internalID: 0
143
+ vertices: []
144
+ indices:
145
+ edges: []
146
+ weights: []
147
+ secondaryTextures: []
148
+ nameFileIdTable: {}
149
+ mipmapLimitGroupName:
150
+ pSDRemoveMatte: 0
151
+ userData:
152
+ assetBundleName:
153
+ assetBundleVariant:
@@ -138,6 +138,11 @@ public partial class VoxelWorld : MonoBehaviour {
138
138
  return transform.localToWorldMatrix.MultiplyPoint(point);
139
139
  }
140
140
 
141
+ public Vector3 TransformVectorToWorldSpace(Vector3 vec) {
142
+ return transform.localToWorldMatrix.MultiplyVector(vec);
143
+ }
144
+
145
+
141
146
  public void InvokeOnFinishedReplicatingChunksFromServer() {
142
147
  this.finishedReplicatingChunksFromServer = true;
143
148
  this.OnFinishedReplicatingChunksFromServer?.Invoke();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gg.easy.airship",
3
- "version": "0.1.1661",
3
+ "version": "0.1.1663",
4
4
  "displayName": "Airship",
5
5
  "unity": "2021.3",
6
6
  "unityRelease": "12f1",