Skip to content

Instantly share code, notes, and snippets.

@GarethIW
Created August 17, 2016 13:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save GarethIW/8ca2bdf9c60a902a6ca3016b026a33a0 to your computer and use it in GitHub Desktop.
Save GarethIW/8ca2bdf9c60a902a6ca3016b026a33a0 to your computer and use it in GitHub Desktop.
/////////////////////////////////////////////////////////////////////////
//
// PicaVoxel - The tiny voxel engine for Unity - http://picavoxel.com
// By Gareth Williams - @garethiw - http://gareth.pw
//
// Source code distributed under standard Asset Store licence:
// http://unity3d.com/legal/as_terms
//
/////////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
using System.Collections;
using System.Threading;
#if UNITY_WINRT && !UNITY_EDITOR
using System.Threading.Tasks;
#endif
using UnityEngine.UI;
namespace PicaVoxel
{
[AddComponentMenu("")]
public class Chunk : MonoBehaviour
{
private enum ChunkStatus
{
NoChange,
CalculatingMesh,
Ready
}
//public const int CHUNK_SIZE = 16;
public bool IsUpdated;
private ChunkStatus status = ChunkStatus.NoChange;
private List<Vector3> vertices = new List<Vector3>();
private List<Vector2> uvs = new List<Vector2>();
private List<Color32> colors = new List<Color32>();
private List<int> indexes = new List<int>();
private Vector3[] vertArray;
private Vector2[] uvArray;
private Color32[] colorArray;
private int[] indexArray;
private MeshFilter mf;
private MeshCollider mc;
private bool hasCreatedRuntimeMesh = false;
private bool hasCreatedRuntimeColMesh = false;
private bool updateColliderNextFrame = false;
private void Update()
{
if (status == ChunkStatus.Ready)
{
SetMesh();
status = ChunkStatus.NoChange;
}
if (updateColliderNextFrame)
{
UpdateCollider();
}
}
#if UNITY_EDITOR
public void GenerateMesh(Voxel[] voxels, float voxelSize, float overlapAmount, int xOffset, int yOffset, int zOffset, int xSize,
int ySize, int zSize, int ub0, int ub1, int ub2, float selfShadeIntensity, MeshingMode mode, MeshingMode colliderMode, bool immediate,
EditorPaintMode paintMode)
#else
public void GenerateMesh(Voxel[] voxels, float voxelSize,float overlapAmount, int xOffset, int yOffset, int zOffset, int xSize, int ySize, int zSize, int ub0, int ub1, int ub2, float selfShadeIntensity, MeshingMode mode, MeshingMode colliderMode, bool immediate)
#endif
{
if (mf == null) mf = GetComponent<MeshFilter>();
if (mc == null) mc = GetComponent<MeshCollider>();
if (immediate)
{
#if UNITY_EDITOR
Generate(ref voxels, voxelSize, overlapAmount, xOffset, yOffset, zOffset, xSize, ySize, zSize, ub0, ub1, ub2,
selfShadeIntensity, mode, paintMode);
#else
Generate(ref voxels, voxelSize, overlapAmount, xOffset, yOffset, zOffset, xSize, ySize, zSize, ub0, ub1, ub2,
selfShadeIntensity, mode);
#endif
SetMesh();
if (colliderMode != mode &&transform.parent.parent.parent.GetComponent<Volume>().CollisionMode != CollisionMode.None)
{
#if UNITY_EDITOR
Generate(ref voxels, voxelSize, overlapAmount, xOffset, yOffset, zOffset, xSize, ySize, zSize, ub0, ub1, ub2,
selfShadeIntensity, mode, paintMode);
#else
Generate(ref voxels, voxelSize, overlapAmount, xOffset, yOffset, zOffset, xSize, ySize, zSize, ub0, ub1, ub2,
selfShadeIntensity, mode);
#endif
}
if (transform.parent.parent.parent.GetComponent<Volume>().CollisionMode != CollisionMode.None)
SetColliderMesh();
}
else
{
if (status != ChunkStatus.NoChange) return;
#if UNITY_WINRT && !UNITY_EDITOR
System.Threading.Tasks.Task.Run(() =>
{
GenerateThreaded(ref voxels, voxelSize, overlapAmount, xOffset, yOffset, zOffset, xSize, ySize, zSize,ub0, ub1, ub2,
selfShadeIntensity, mode);
});
#else
#if UNITY_WEBGL && !UNITY_EDITOR
// WEBGL platform does not support threading (yet)
Generate(ref voxels, voxelSize, overlapAmount, xOffset, yOffset, zOffset, xSize, ySize, zSize, ub0, ub1, ub2,
selfShadeIntensity, mode);
SetMesh();
if (colliderMode != mode &&transform.parent.parent.parent.GetComponent<Volume>().CollisionMode != CollisionMode.None)
{
Generate(ref voxels, voxelSize, overlapAmount, xOffset, yOffset, zOffset, xSize, ySize, zSize, ub0, ub1, ub2,
selfShadeIntensity, mode);
}
if (transform.parent.parent.parent.GetComponent<Volume>().CollisionMode != CollisionMode.None)
SetColliderMesh();
#else
#if UNITY_EDITOR
if(!ThreadPool.QueueUserWorkItem(delegate
{
GenerateThreaded(ref voxels, voxelSize, overlapAmount, xOffset, yOffset, zOffset, xSize, ySize, zSize, ub0, ub1, ub2,
selfShadeIntensity, mode, paintMode);
})) Generate(ref voxels, voxelSize, overlapAmount, xOffset, yOffset, zOffset, xSize, ySize, zSize, ub0, ub1, ub2,
selfShadeIntensity, mode, paintMode);
#else
if(!ThreadPool.QueueUserWorkItem(delegate
{
GenerateThreaded(ref voxels, voxelSize, overlapAmount, xOffset, yOffset, zOffset, xSize, ySize, zSize,ub0, ub1, ub2,
selfShadeIntensity, mode);
})) Generate(ref voxels, voxelSize, overlapAmount, xOffset, yOffset, zOffset, xSize, ySize, zSize, ub0, ub1, ub2,
selfShadeIntensity, mode);
#endif
#endif
#endif
}
}
#if UNITY_EDITOR
private void GenerateThreaded(ref Voxel[] voxels, float voxelSize, float overlapAmount, int xOffset, int yOffset, int zOffset,
int xSize, int ySize, int zSize, int ub0, int ub1, int ub2, float selfShadeIntensity, MeshingMode meshMode, EditorPaintMode paintMode)
#else
private void GenerateThreaded(ref Voxel[] voxels, float voxelSize,float overlapAmount, int xOffset, int yOffset, int zOffset, int xSize, int ySize, int zSize, int ub0, int ub1, int ub2, float selfShadeIntensity, MeshingMode meshMode)
#endif
{
status = ChunkStatus.CalculatingMesh;
#if UNITY_EDITOR
Generate(ref voxels, voxelSize, overlapAmount, xOffset, yOffset, zOffset, xSize, ySize, zSize, ub0, ub1, ub2,
selfShadeIntensity, meshMode, paintMode);
#else
Generate(ref voxels, voxelSize, overlapAmount, xOffset, yOffset, zOffset, xSize, ySize, zSize, ub0, ub1, ub2,
selfShadeIntensity, meshMode);
#endif
status = ChunkStatus.Ready;
}
#if UNITY_EDITOR
private void Generate(ref Voxel[] voxels, float voxelSize, float overlapAmount, int xOffset, int yOffset, int zOffset, int xSize, int ySize, int zSize, int ub0, int ub1, int ub2, float selfShadeIntensity, MeshingMode meshMode, EditorPaintMode paintMode)
#else
private void Generate(ref Voxel[] voxels, float voxelSize,float overlapAmount, int xOffset, int yOffset, int zOffset, int xSize, int ySize, int zSize, int ub0, int ub1, int ub2, float selfShadeIntensity, MeshingMode meshMode)
#endif
{
#if UNITY_EDITOR
switch (meshMode)
{
case MeshingMode.Culled:
MeshGenerator.GenerateCulled(vertices, uvs, colors, indexes, ref vertArray, ref uvArray, ref colorArray,
ref indexArray, ref voxels, voxelSize, overlapAmount, xOffset, yOffset, zOffset, xSize, ySize, zSize, ub0, ub1, ub2,
selfShadeIntensity, paintMode);
break;
case MeshingMode.Greedy:
MeshGenerator.GenerateGreedy(vertices, uvs, colors, indexes, ref vertArray, ref uvArray, ref colorArray,
ref indexArray, ref voxels, voxelSize, overlapAmount, xOffset, yOffset, zOffset, xSize, ySize, zSize, ub0, ub1, ub2,
selfShadeIntensity, paintMode);
break;
case MeshingMode.Marching:
MeshGenerator.GenerateMarching(vertices, uvs, colors, indexes, ref vertArray, ref uvArray, ref colorArray,
ref indexArray, ref voxels, voxelSize, xOffset, yOffset, zOffset, xSize, ySize, zSize, ub0, ub1, ub2,
selfShadeIntensity, paintMode);
break;
}
#else
switch (meshMode)
{
case MeshingMode.Culled:
MeshGenerator.GenerateCulled(vertices, uvs, colors, indexes, ref vertArray, ref uvArray, ref colorArray,
ref indexArray, ref voxels, voxelSize, overlapAmount,xOffset, yOffset, zOffset, xSize, ySize, zSize, ub0, ub1, ub2,
selfShadeIntensity);
break;
case MeshingMode.Greedy:
MeshGenerator.GenerateGreedy(vertices, uvs, colors, indexes, ref vertArray, ref uvArray, ref colorArray,
ref indexArray, ref voxels, voxelSize, overlapAmount,xOffset, yOffset, zOffset, xSize, ySize, zSize, ub0, ub1, ub2,
selfShadeIntensity);
break;
case MeshingMode.Marching:
MeshGenerator.GenerateMarching(vertices, uvs, colors, indexes, ref vertArray, ref uvArray, ref colorArray,
ref indexArray, ref voxels, voxelSize, xOffset, yOffset, zOffset, xSize, ySize, zSize, ub0, ub1, ub2,
selfShadeIntensity);
break;
}
#endif
}
private void SetMesh()
{
Volume vol = transform.parent.parent.parent.GetComponent<Volume>();
if (vol == null || vertArray==null) return;
if (vertArray.Length == 0)
{
if (Application.isPlaying && !hasCreatedRuntimeMesh)
{
if(mf.sharedMesh!=null)
mf.sharedMesh = (Mesh)Instantiate(mf.sharedMesh);
hasCreatedRuntimeMesh = true;
}
if (mf.sharedMesh != null)
{
mf.sharedMesh.Clear();
mf.sharedMesh = null;
}
if (mc != null && vol.CollisionMode != CollisionMode.None && !vol.GenerateMeshColliderSeparately)
{
mc.sharedMesh = null;
}
return;
}
if (mf.sharedMesh == null)
{
mf.sharedMesh = new Mesh();
}
if (Application.isPlaying && !hasCreatedRuntimeMesh)
{
mf.sharedMesh = (Mesh)Instantiate(mf.sharedMesh);
hasCreatedRuntimeMesh = true;
}
mf.sharedMesh.Clear();
mf.sharedMesh.vertices = vertArray;
mf.sharedMesh.colors32 = colorArray;
mf.sharedMesh.uv = uvArray;
mf.sharedMesh.triangles = indexArray;
mf.sharedMesh.Optimize();
mf.sharedMesh.RecalculateNormals();
mf.GetComponent<Renderer>().sharedMaterial = vol.Material;
if (mc != null && vol.CollisionMode != CollisionMode.None && !vol.GenerateMeshColliderSeparately)
{
updateColliderNextFrame = true;
}
#if UNITY_EDITOR
if (!Application.isPlaying && uvArray.Length > 0)
{
MeshUtility.SetMeshCompression(mf.sharedMesh, vol.MeshCompression);
}
#endif
}
private void UpdateCollider()
{
mc.sharedMesh = null;
mf.sharedMesh.RecalculateBounds();
mc.sharedMesh = mf.sharedMesh;
updateColliderNextFrame = false;
}
private void SetColliderMesh()
{
Volume vol = transform.parent.parent.parent.GetComponent<Volume>();
if (vol == null) return;
if (vertArray.Length == 0)
{
if (mc.sharedMesh != null)
{
mc.sharedMesh.Clear();
mc.sharedMesh = null;
}
return;
}
if (mc.sharedMesh == null || (Application.isPlaying && !hasCreatedRuntimeColMesh))
{
mc.sharedMesh = new Mesh();
if (Application.isPlaying) hasCreatedRuntimeColMesh = true;
}
mc.sharedMesh.Clear();
mc.sharedMesh.vertices = vertArray;
mc.sharedMesh.colors32 = colorArray;
mc.sharedMesh.uv = uvArray;
mc.sharedMesh.triangles = indexArray;
mc.sharedMesh.Optimize();
mc.sharedMesh.RecalculateNormals();
mc.sharedMesh.RecalculateBounds();
#if UNITY_EDITOR
if (!Application.isPlaying && uvArray.Length > 0)
{
MeshUtility.SetMeshCompression(mc.sharedMesh, vol.MeshCompression);
}
#endif
}
#if UNITY_EDITOR
public void SaveMeshes()
{
if (Application.isPlaying) return;
Volume vol = transform.parent.parent.parent.GetComponent<Volume>();
if (vol == null) return;
Frame frame = transform.parent.parent.GetComponent<Frame>();
if (frame == null) return;
if (PrefabUtility.GetPrefabType(vol) == PrefabType.PrefabInstance) return;
//if (vol.AssetGuid == Guid.Empty) vol.g = Guid.NewGuid();
string path = Path.Combine(Helper.GetMeshStorePath(), GuidEncoder.Encode(vol.AssetGuid));
if (mf.sharedMesh != null)
{
Mesh tempMesh = (Mesh)Instantiate(mf.sharedMesh);
AssetDatabase.CreateAsset(tempMesh, Path.Combine(path, transform.name.Replace("Chunk","").Replace("(","").Replace(")","").Replace(",","").Replace(" ","") + GuidEncoder.Encode(frame.AssetGuid) + ".asset"));
mf.sharedMesh = tempMesh;
}
if (mc!=null && mc.sharedMesh != null)
{
Mesh tempMesh = (Mesh)Instantiate(mc.sharedMesh);
AssetDatabase.CreateAsset(tempMesh, Path.Combine(path, transform.name.Replace("Chunk", "").Replace("(", "").Replace(")", "").Replace(",", "").Replace(" ", "") + GuidEncoder.Encode(frame.AssetGuid) + "_mc" + ".asset"));
mc.sharedMesh = tempMesh;
}
}
#endif
}
public static class GuidEncoder
{
public static string Encode(string guidText)
{
Guid guid = new Guid(guidText);
return Encode(guid);
}
public static string Encode(Guid guid)
{
string enc = Convert.ToBase64String(guid.ToByteArray());
enc = enc.Replace("/", "_");
enc = enc.Replace("+", "-");
return enc.Substring(0, 22);
}
public static Guid Decode(string encoded)
{
encoded = encoded.Replace("_", "/");
encoded = encoded.Replace("-", "+");
byte[] buffer = Convert.FromBase64String(encoded + "==");
return new Guid(buffer);
}
}
}
/////////////////////////////////////////////////////////////////////////
//
// PicaVoxel - The tiny voxel engine for Unity - http://picavoxel.com
// By Gareth Williams - @garethiw - http://gareth.pw
//
// Source code distributed under standard Asset Store licence:
// http://unity3d.com/legal/as_terms
//
/////////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using System.Collections;
using ICSharpCode.SharpZipLib.GZip;
namespace PicaVoxel
{
/// <summary>
/// A frame of animation
/// </summary>
/// This is the real heart of PicaVoxel - one frame of a parent Volume. This is where the voxel array is kept for a frame, and Chunks are created
/// below the Frame in the heirarchy
[ExecuteInEditMode]
[AddComponentMenu("")]
public class Frame : MonoBehaviour, ISerializationCallbackReceiver
{
public GameObject ChunkPrefab;
public Volume ParentVolume;
[NonSerialized]
public Voxel[] Voxels;
[NonSerialized]
public Voxel[] EditingVoxels;
public int XSize = 32;
public int YSize = 32;
public int ZSize = 32;
#if UNITY_EDITOR
public string AssetGuid;
#endif
[SerializeField]
[HideInInspector]
private byte[] bserializedVoxels;
private Chunk[,,] chunks;
private List<PicaVoxelPoint> chunksToUpdate = new List<PicaVoxelPoint>();
private Quaternion lastRotation;
private Vector3 lastPosition;
private Matrix4x4 transformMatrix;
private void Start()
{
//Debug.Log("Frame start");
if (!Application.isPlaying)
{
GetChunkReferences();
if (chunks == null && (ParentVolume != null && !ParentVolume.RuntimOnlyMesh))
CreateChunks();
}
}
private void Awake()
{
//Debug.Log("Frame awake");
if (transform.parent != null) ParentVolume = transform.parent.GetComponent<Volume>();
if (ParentVolume != null)
{
if (ParentVolume.RuntimOnlyMesh)// || !Application.isPlaying)
UpdateAllChunks();
}
transformMatrix = transform.worldToLocalMatrix;
}
private void Update()
{
#if UNITY_EDITOR
if (chunks != null && chunks[0, 0, 0] != null)
ParentVolume.ChunkLayer = chunks[0, 0, 0].gameObject.layer;
if (!Application.isPlaying)
return;
#endif
if (chunksToUpdate.Count > 0) UpdateChunks(false);
// We have to keep track of the volume's world-local matrix for when we do any voxel position calculations
// But worldToLocalMatrix is expensive, so let's check to see if the transform has changed before we do the math
if (transform.rotation != lastRotation || transform.position != lastPosition)
{
transformMatrix = transform.worldToLocalMatrix;
lastPosition = transform.position;
lastRotation = transform.rotation;
}
}
/// <summary>
/// Returns a voxel contained in this frame, at a given world position
/// </summary>
/// <param name="pos">The world position in the scene</param>
/// <returns>A voxel if position is within this volume, otherwise null</returns>
public Voxel? GetVoxelAtWorldPosition(Vector3 pos)
{
Vector3 localPos = transformMatrix.MultiplyPoint3x4(pos); //transform.InverseTransformPoint(pos);
if (!IsLocalPositionInBounds(localPos)) return null;
int testX = (int)(localPos.x / ParentVolume.VoxelSize);
int testY = (int)(localPos.y / ParentVolume.VoxelSize);
int testZ = (int)(localPos.z / ParentVolume.VoxelSize);
if (testX < 0 || testY < 0 || testZ < 0 || testX >= XSize || testY >= YSize || testZ >= ZSize) return null;
return Voxels[testX + XSize * (testY + YSize * testZ)];
}
/// <summary>
/// Returns a voxel contained in this frame, at a given array position
/// </summary>
/// <param name="x">X array position</param>
/// <param name="y">Y array position</param>
/// <param name="z">Z array position</param>
/// <returns>A voxel if position is within the array, otherwise null</returns>
public Voxel? GetVoxelAtArrayPosition(int x, int y, int z)
{
if (x < 0 || y < 0 || z < 0 || x >= XSize || y >= YSize || z >= ZSize) return null;
return Voxels[x + XSize * (y + YSize * z)];
}
/// <summary>
/// Attempts to set a voxel within this frame, at a given world position, to the supplied voxel value
/// </summary>
/// <param name="pos">The world position in the scene</param>
/// <param name="vox">The new voxel to set to</param>
public Vector3 SetVoxelAtWorldPosition(Vector3 pos, Voxel vox)
{
Vector3 localPos = transform.InverseTransformPoint(pos);
Vector3 arrayPos = new Vector3((int)(localPos.x / ParentVolume.VoxelSize),
(int)(localPos.y / ParentVolume.VoxelSize),
(int)(localPos.z / ParentVolume.VoxelSize));
SetVoxelAtArrayPosition((int)arrayPos.x, (int)arrayPos.y, (int)arrayPos.z, vox);
return arrayPos;
}
/// <summary>
/// Attempts to set a voxel's state within this frame, at a given world position, to the supplied value
/// </summary>
/// <param name="pos">The world position in the scene</param>
/// <param name="state">The new voxel state to set to</param>
public Vector3 SetVoxelStateAtWorldPosition(Vector3 pos, VoxelState state)
{
Vector3 localPos = transform.InverseTransformPoint(pos);
Vector3 arrayPos = new Vector3((int)(localPos.x / ParentVolume.VoxelSize),
(int)(localPos.y / ParentVolume.VoxelSize),
(int)(localPos.z / ParentVolume.VoxelSize));
SetVoxelStateAtArrayPosition((int)arrayPos.x, (int)arrayPos.y, (int)arrayPos.z, state);
return arrayPos;
}
/// <summary>
/// Attempts to set a voxel within this frame, at a specified array position
/// </summary>
/// <param name="pos">A PicaVoxelPoint location within the 3D array of voxels</param>
/// <param name="vox">The new voxel to set to</param>
public void SetVoxelAtArrayPosition(PicaVoxelPoint pos, Voxel vox)
{
SetVoxelAtArrayPosition(pos.X, pos.Y, pos.Z, vox);
}
/// <summary>
/// Attempts to set a voxel within this frame, at a specified x,y,z array position
/// </summary>
/// <param name="x">X array position</param>
/// <param name="y">Y array position</param>
/// <param name="z">Z array position</param>
/// <param name="vox">The new voxel to set to</param>
public void SetVoxelAtArrayPosition(int x, int y, int z, Voxel vox)
{
if (x < 0 || y < 0 || z < 0 || x >= XSize || y >= YSize || z >= ZSize) return;
bool updateVox = false;
int index = x + XSize * (y + YSize * z);
#if UNITY_EDITOR
if (EditingVoxels != null)
{
EditingVoxels[index] = vox;
updateVox = true;
}
else
{
#endif
if (vox.Active != Voxels[index].Active) updateVox = true;
if (Voxels[index].Active && (vox.Color.r != Voxels[index].Color.r || vox.Color.g != Voxels[index].Color.g || vox.Color.b != Voxels[index].Color.b || vox.Value != Voxels[index].Value))
updateVox = true;
Voxels[index] = vox;
#if UNITY_EDITOR
}
#endif
if (!updateVox) return;
if (chunks == null) GetChunkReferences();
AddChunkToUpdateList(x / ParentVolume.XChunkSize, y / ParentVolume.YChunkSize, z / ParentVolume.ZChunkSize);
// If we're at the edge of a chunk, we should update the next voxel as well
if (x % ParentVolume.XChunkSize == 0 && (x - 1) >= 0) AddChunkToUpdateList((x - 1) / ParentVolume.XChunkSize, y / ParentVolume.YChunkSize, z / ParentVolume.ZChunkSize);
if (x % ParentVolume.XChunkSize == ParentVolume.XChunkSize - 1 && (x + 1) < XSize) AddChunkToUpdateList((x + 1) / ParentVolume.XChunkSize, y / ParentVolume.YChunkSize, z / ParentVolume.ZChunkSize);
if (y % ParentVolume.YChunkSize == 0 && (y - 1) >= 0) AddChunkToUpdateList(x / ParentVolume.XChunkSize, (y - 1) / ParentVolume.YChunkSize, z / ParentVolume.ZChunkSize);
if (y % ParentVolume.YChunkSize == ParentVolume.YChunkSize - 1 && (y + 1) < YSize) AddChunkToUpdateList(x / ParentVolume.XChunkSize, (y + 1) / ParentVolume.YChunkSize, z / ParentVolume.ZChunkSize);
if (z % ParentVolume.ZChunkSize == 0 && (z - 1) >= 0) AddChunkToUpdateList(x / ParentVolume.XChunkSize, y / ParentVolume.YChunkSize, (z - 1) / ParentVolume.ZChunkSize);
if (z % ParentVolume.ZChunkSize == ParentVolume.ZChunkSize - 1 && (z + 1) < ZSize) AddChunkToUpdateList(x / ParentVolume.XChunkSize, y / ParentVolume.YChunkSize, (z + 1) / ParentVolume.ZChunkSize);
}
/// <summary>
/// Attempts to set a voxel's state within this frame, at a specified array position
/// </summary>
/// <param name="pos">A PicaVoxelPoint location within the 3D array of voxels</param>
/// <param name="state">The new state to set to</param>
public void SetVoxelStateAtArrayPosition(PicaVoxelPoint pos, VoxelState state)
{
SetVoxelStateAtArrayPosition(pos.X, pos.Y, pos.Z, state);
}
/// <summary>
/// Attempts to set a voxel's state within this frame, at a specified x,y,z array position
/// </summary>
/// <param name="x">X array position</param>
/// <param name="y">Y array position</param>
/// <param name="z">Z array position</param>
/// <param name="state">The new voxel state to set to</param>
public void SetVoxelStateAtArrayPosition(int x, int y, int z, VoxelState state)
{
if (x < 0 || y < 0 || z < 0 || x >= XSize || y >= YSize || z >= ZSize) return;
bool updateVox = false;
int index = x + XSize * (y + YSize * z);
if (state != Voxels[index].State) updateVox = true;
Voxels[index].State = state;
if (!updateVox) return;
if (chunks == null) GetChunkReferences();
AddChunkToUpdateList(x / ParentVolume.XChunkSize, y / ParentVolume.YChunkSize, z / ParentVolume.ZChunkSize);
// If we're at the edge of a chunk, we should update the next voxel as well
if (x % ParentVolume.XChunkSize == 0 && (x - 1) >= 0) AddChunkToUpdateList((x - 1) / ParentVolume.XChunkSize, y / ParentVolume.YChunkSize, z / ParentVolume.ZChunkSize);
if (x % ParentVolume.XChunkSize == ParentVolume.XChunkSize - 1 && (x + 1) < XSize) AddChunkToUpdateList((x + 1) / ParentVolume.XChunkSize, y / ParentVolume.YChunkSize, z / ParentVolume.ZChunkSize);
if (y % ParentVolume.YChunkSize == 0 && (y - 1) >= 0) AddChunkToUpdateList(x / ParentVolume.XChunkSize, (y - 1) / ParentVolume.YChunkSize, z / ParentVolume.ZChunkSize);
if (y % ParentVolume.YChunkSize == ParentVolume.YChunkSize - 1 && (y + 1) < YSize) AddChunkToUpdateList(x / ParentVolume.XChunkSize, (y + 1) / ParentVolume.YChunkSize, z / ParentVolume.ZChunkSize);
if (z % ParentVolume.ZChunkSize == 0 && (z - 1) >= 0) AddChunkToUpdateList(x / ParentVolume.XChunkSize, y / ParentVolume.YChunkSize, (z - 1) / ParentVolume.ZChunkSize);
if (z % ParentVolume.ZChunkSize == ParentVolume.ZChunkSize - 1 && (z + 1) < ZSize) AddChunkToUpdateList(x / ParentVolume.XChunkSize, y / ParentVolume.YChunkSize, (z + 1) / ParentVolume.ZChunkSize);
}
/// <summary>
/// Returns the local position of a voxel within this frame, at a specified world position
/// </summary>
/// <param name="pos">The world position in the scene</param>
/// <returns>The local voxel position</returns>
public Vector3 GetVoxelPosition(Vector3 scenePos)
{
Vector3 localPos = transform.InverseTransformPoint(scenePos);
return new Vector3((int)(localPos.x / ParentVolume.VoxelSize), (int)(localPos.y / ParentVolume.VoxelSize),
(int)(localPos.z / ParentVolume.VoxelSize));
}
/// <summary>
/// Returns the array position of a voxel within this frame, at a specified world position
/// </summary>
/// <param name="pos">The world position in the scene</param>
/// <returns>The array position of the voxel</returns>
public PicaVoxelPoint GetVoxelArrayPosition(Vector3 scenePos)
{
Vector3 localPos = transform.InverseTransformPoint(scenePos);
return new PicaVoxelPoint((int)(localPos.x / ParentVolume.VoxelSize),
(int)(localPos.y / ParentVolume.VoxelSize), (int)(localPos.z / ParentVolume.VoxelSize));
}
/// <summary>
/// Returns the world position of a voxel given its array positions
/// </summary>
/// <param name="x">The X position of the voxel in the array</param>
/// <param name="y">The Y position of the voxel in the array</param>
/// <param name="z">The Z position of the voxel in the array</param>
/// <returns>The world position of the center of the voxel</returns>
public Vector3 GetVoxelWorldPosition(int x, int y, int z)
{
Vector3 localPos = (new Vector3(x * ParentVolume.VoxelSize, y * ParentVolume.VoxelSize, z * ParentVolume.VoxelSize) + (Vector3.one * (ParentVolume.VoxelSize / 2f)));
return transform.TransformPoint(localPos);
}
/// <summary>
/// Update the pivot position. Use this after setting Pivot.
/// </summary>
public void UpdatePivot()
{
transform.FindChild("Chunks").localPosition = -ParentVolume.Pivot;
}
public void UpdateTransformMatrix()
{
transformMatrix = transform.worldToLocalMatrix;
}
/// <summary>
/// Generates a 32x32x32 frame, filled with white voxels of value 128
/// </summary>
public void GenerateBasic(FillMode fillMode)
{
ParentVolume = ParentVolume.GetComponent<Volume>();
Voxels = new Voxel[XSize * YSize * ZSize];
for (int x = 0; x < XSize; x++)
for (int y = 0; y < YSize; y++)
for (int z = 0; z < ZSize; z++)
{
int index = x + XSize * (y + YSize * z);
if (fillMode == FillMode.AllVoxels || (fillMode == FillMode.BaseOnly && y == 0))
Voxels[index].State = VoxelState.Active;
Voxels[index].Value = 128;
Voxels[index].Color = ParentVolume.PaletteColors[0];
}
SaveForSerialize();
}
/// <summary>
/// Initialise this frame with an array of empty voxels
/// </summary>
public void GenerateNewFrame()
{
ParentVolume = transform.parent.GetComponent<Volume>();
Voxels = new Voxel[XSize * YSize * ZSize];
for (int i = 0; i < Voxels.Length; i++) Voxels[i].Value = 128;
SaveForSerialize();
}
/// <summary>
/// Initialise this frame and copy the voxels from a source frame
/// </summary>
/// <param name="sourceFrame"></param>
public void GenerateNewFrame(Frame sourceFrame)
{
ParentVolume = transform.parent.GetComponent<Volume>();
Voxels = new Voxel[XSize * YSize * ZSize];
Helper.CopyVoxelsInBox(ref sourceFrame.Voxels, ref Voxels, new PicaVoxelPoint(XSize, YSize, ZSize), new PicaVoxelPoint(XSize, YSize, ZSize), false);
CreateChunks();
SaveForSerialize();
}
/// <summary>
/// Generate the colliders for all of the chunks
/// </summary>
public void GenerateMeshColliders()
{
if (ParentVolume.CollisionMode == CollisionMode.None)
{
DestroyMeshColliders();
return;
}
for (int i = transform.Find("Chunks").childCount - 1; i >= 0; i--)
{
var chunk = transform.Find("Chunks").GetChild(i);
if (chunk.GetComponent<MeshCollider>() == null)
chunk.gameObject.AddComponent(typeof(MeshCollider));
//Mesh m = chunk.GetComponent<MeshFilter>().sharedMesh;
//m.RecalculateBounds();
//chunk.GetComponent<MeshCollider>().sharedMesh = m;
chunk.GetComponent<MeshCollider>().convex = (ParentVolume.CollisionMode ==
CollisionMode.MeshColliderConvex);
chunk.GetComponent<MeshCollider>().sharedMaterial = ParentVolume.PhysicMaterial;
chunk.GetComponent<MeshCollider>().isTrigger = ParentVolume.CollisionTrigger;
}
UpdateAllChunks();
}
/// <summary>
/// Destroy the mesh colliders on all of the chunks
/// </summary>
public void DestroyMeshColliders()
{
//Debug.Log("Frame DestroyMeshColliders");
for (int i = transform.Find("Chunks").childCount - 1; i >= 0; i--)
{
var chunk = transform.Find("Chunks").GetChild(i);
if (chunk.GetComponent<MeshCollider>() != null)
DestroyImmediate(chunk.GetComponent<MeshCollider>());
}
}
/// <summary>
/// Update only the chunks which have changed voxels
/// </summary>
/// <param name="immediate">If true, don't use threading to perform this update</param>
public void UpdateChunks(bool immediate)
{
if (chunksToUpdate.Count == 0) return;
if (ParentVolume == null) return;
if (ParentVolume.RuntimOnlyMesh && !Application.isPlaying) return;
if (Voxels == null) return;
if (chunks == null) GetChunkReferences();
while (chunksToUpdate.Count > 0)
{
int x = chunksToUpdate[0].X;
int y = chunksToUpdate[0].Y;
int z = chunksToUpdate[0].Z;
chunks[x, y, z].IsUpdated = false;
#if UNITY_EDITOR
chunks[x, y, z].GenerateMesh(EditingVoxels == null ? Voxels : EditingVoxels.Length > 0 ? EditingVoxels : Voxels,
ParentVolume.VoxelSize, ParentVolume.OverlapAmount,
x * ParentVolume.XChunkSize, y * ParentVolume.YChunkSize, z * ParentVolume.ZChunkSize,
(XSize - (x * ParentVolume.XChunkSize) < ParentVolume.XChunkSize
? XSize - (x * ParentVolume.XChunkSize)
: ParentVolume.XChunkSize),
(YSize - (y * ParentVolume.YChunkSize) < ParentVolume.YChunkSize
? YSize - (y * ParentVolume.YChunkSize)
: ParentVolume.YChunkSize),
(ZSize - (z * ParentVolume.ZChunkSize) < ParentVolume.ZChunkSize
? ZSize - (z * ParentVolume.ZChunkSize)
: ParentVolume.ZChunkSize), XSize - 1, YSize - 1, ZSize - 1,
ParentVolume.SelfShadingIntensity, ParentVolume.MeshingMode, ParentVolume.MeshColliderMeshingMode, immediate,
ParentVolume.PaintMode);
#else
chunks[x, y, z].GenerateMesh(EditingVoxels==null?Voxels:EditingVoxels.Length>0?EditingVoxels:Voxels,
ParentVolume.VoxelSize, ParentVolume.OverlapAmount,
x* ParentVolume.XChunkSize, y* ParentVolume.YChunkSize, z* ParentVolume.ZChunkSize,
(XSize - (x* ParentVolume.XChunkSize) < ParentVolume.XChunkSize
? XSize - (x* ParentVolume.XChunkSize)
: ParentVolume.XChunkSize),
(YSize - (y* ParentVolume.YChunkSize) < ParentVolume.YChunkSize
? YSize - (y* ParentVolume.YChunkSize)
: ParentVolume.YChunkSize),
(ZSize - (z* ParentVolume.ZChunkSize) < ParentVolume.ZChunkSize
? ZSize - (z* ParentVolume.ZChunkSize)
: ParentVolume.ZChunkSize), XSize-1, YSize-1, ZSize-1,
ParentVolume.SelfShadingIntensity,ParentVolume.MeshingMode,ParentVolume.MeshColliderMeshingMode, immediate);
#endif
chunksToUpdate.RemoveAt(0);
}
}
/// <summary>
/// Immediately update all chunks
/// </summary>
public void UpdateAllChunks()
{
// Debug.Log("UpdateAllChunks " + ParentVolume.transform.name);
if (ParentVolume == null) return;
if (ParentVolume.RuntimOnlyMesh && !Application.isPlaying)
{
DestroyMeshColliders();
Transform chunkContainer = transform.FindChild("Chunks");
for (int i = chunkContainer.childCount - 1; i >= 0; i--)
if (chunkContainer.GetChild(i).GetComponent<Chunk>() != null)
DestroyImmediate(chunkContainer.GetChild(i).gameObject);
return;
}
if (Voxels == null) return;
if (chunks == null)
{
GetChunkReferences();
}
int progress = 0;
for (int x = 0; x < (int)Mathf.Ceil((float)XSize / ParentVolume.XChunkSize); x++)
{
for (int y = 0; y < (int)Mathf.Ceil((float)YSize / ParentVolume.YChunkSize); y++)
{
for (int z = 0; z < (int)Mathf.Ceil((float)ZSize / ParentVolume.ZChunkSize); z++)
{
#if UNITY_EDITOR
chunks[x, y, z].GenerateMesh(
EditingVoxels == null ? Voxels : EditingVoxels.Length > 0 ? EditingVoxels : Voxels,
ParentVolume.VoxelSize, ParentVolume.OverlapAmount,
x * ParentVolume.XChunkSize, y * ParentVolume.YChunkSize, z * ParentVolume.ZChunkSize,
(XSize - (x * ParentVolume.XChunkSize) < ParentVolume.XChunkSize
? XSize - (x * ParentVolume.XChunkSize)
: ParentVolume.XChunkSize),
(YSize - (y * ParentVolume.YChunkSize) < ParentVolume.YChunkSize
? YSize - (y * ParentVolume.YChunkSize)
: ParentVolume.YChunkSize),
(ZSize - (z * ParentVolume.ZChunkSize) < ParentVolume.ZChunkSize
? ZSize - (z * ParentVolume.ZChunkSize)
: ParentVolume.ZChunkSize), XSize - 1, YSize - 1, ZSize - 1,
ParentVolume.SelfShadingIntensity, ParentVolume.MeshingMode,
ParentVolume.MeshColliderMeshingMode, true,
ParentVolume.PaintMode);
#else
chunks[x, y, z].GenerateMesh(EditingVoxels==null?Voxels:EditingVoxels.Length>0?EditingVoxels:Voxels,
ParentVolume.VoxelSize, ParentVolume.OverlapAmount,
x* ParentVolume.XChunkSize, y* ParentVolume.YChunkSize, z* ParentVolume.ZChunkSize,
(XSize - (x* ParentVolume.XChunkSize) < ParentVolume.XChunkSize
? XSize - (x* ParentVolume.XChunkSize)
: ParentVolume.XChunkSize),
(YSize - (y* ParentVolume.YChunkSize) < ParentVolume.YChunkSize
? YSize - (y* ParentVolume.YChunkSize)
: ParentVolume.YChunkSize),
(ZSize - (z* ParentVolume.ZChunkSize) < ParentVolume.ZChunkSize
? ZSize - (z* ParentVolume.ZChunkSize)
: ParentVolume.ZChunkSize), XSize-1, YSize-1, ZSize-1,
ParentVolume.SelfShadingIntensity,ParentVolume.MeshingMode,ParentVolume.MeshColliderMeshingMode, true);
#endif
progress++;
}
}
#if UNITY_EDITOR
if (!Application.isPlaying)
UnityEditor.EditorUtility.DisplayProgressBar("PicaVoxel", "Updating Chunks", (1f / (float)(XSize * YSize * ZSize)) * (float)(progress));
#endif
}
#if UNITY_EDITOR
if (!Application.isPlaying)
UnityEditor.EditorUtility.ClearProgressBar();
#endif
transformMatrix = transform.worldToLocalMatrix;
}
/// <summary>
/// Updates all chunks next frame (threaded)
/// </summary>
public void UpdateAllChunksNextFrame()
{
if (chunks == null) GetChunkReferences();
for (int x = 0; x < chunks.GetLength(0); x++)
for (int y = 0; y < chunks.GetLength(1); y++)
for (int z = 0; z < chunks.GetLength(2); z++)
AddChunkToUpdateList(x, y, z);
}
/// <summary>
/// Re-create all chunks
/// </summary>
public void CreateChunks()
{
//Debug.Log("CreateChunks " + ParentVolume.transform.name);
if (ParentVolume.RuntimOnlyMesh && !Application.isPlaying) return;
if (Voxels == null) return;
DestroyMeshColliders();
Transform chunkContainer = transform.FindChild("Chunks");
for (int i = chunkContainer.childCount - 1; i >= 0; i--)
if (chunkContainer.GetChild(i).GetComponent<Chunk>() != null)
DestroyImmediate(chunkContainer.GetChild(i).gameObject);
chunks =
new Chunk[(int)Mathf.Ceil((float)XSize / ParentVolume.XChunkSize),
(int)Mathf.Ceil((float)YSize / ParentVolume.YChunkSize), (int)Mathf.Ceil((float)ZSize / ParentVolume.YChunkSize)];
int x = 0;
int y = 0;
int z = 0;
int chunkX = 0;
int chunkY = 0;
int chunkZ = 0;
while (x < XSize)
{
while (y < YSize)
{
while (z < ZSize)
{
var newChunk =
Instantiate(ChunkPrefab, new Vector3(x, y, z) * ParentVolume.VoxelSize, Quaternion.identity)
as GameObject;
newChunk.name = "Chunk (" + chunkX + "," + chunkY + "," + chunkZ + ")";
newChunk.layer = ParentVolume.ChunkLayer;
newChunk.transform.parent = chunkContainer;
newChunk.transform.localPosition = new Vector3(x, y, z) * ParentVolume.VoxelSize;
newChunk.transform.rotation = transform.rotation;
newChunk.transform.localScale = transform.localScale;
newChunk.GetComponent<MeshRenderer>().shadowCastingMode = ParentVolume.CastShadows;
newChunk.GetComponent<MeshRenderer>().receiveShadows = ParentVolume.ReceiveShadows;
chunks[chunkX, chunkY, chunkZ] = newChunk.GetComponent<Chunk>();
z += ParentVolume.ZChunkSize;
chunkZ++;
}
z = 0;
chunkZ = 0;
y += ParentVolume.YChunkSize;
chunkY++;
}
y = 0;
chunkY = 0;
x += ParentVolume.XChunkSize;
chunkX++;
#if UNITY_EDITOR
if (!Application.isPlaying)
UnityEditor.EditorUtility.DisplayProgressBar("PicaVoxel", "Creating Chunks", (1f / (float)(XSize * YSize * ZSize)) * (float)(x * y * z));
#endif
}
#if UNITY_EDITOR
if (!Application.isPlaying)
UnityEditor.EditorUtility.ClearProgressBar();
#endif
GenerateMeshColliders();
UpdateAllChunks();
SaveChunkMeshes(false);
}
public void SaveChunkMeshes(bool forceNew)
{
#if UNITY_EDITOR
if (string.IsNullOrEmpty(ParentVolume.AssetGuid)) ParentVolume.AssetGuid = Guid.NewGuid().ToString();
if (string.IsNullOrEmpty(AssetGuid) || forceNew) AssetGuid = Guid.NewGuid().ToString();
string path = Path.Combine(Helper.GetMeshStorePath(), GuidEncoder.Encode(ParentVolume.AssetGuid.ToString()));
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
if (forceNew)
{
UpdateAllChunks();
//foreach (Chunk c in chunks) c.SaveMeshes();
}
GetChunkReferences();
int progress = 0;
if (chunks != null)
{
foreach (Chunk c in chunks)
{
if (!Application.isPlaying)
UnityEditor.EditorUtility.DisplayProgressBar("PicaVoxel", "Saving Chunks",
(1f / (float)(chunks.Length)) * (float)(progress));
c.SaveMeshes();
progress++;
}
if (!Application.isPlaying)
UnityEditor.EditorUtility.ClearProgressBar();
}
#endif
}
public void SetChunkAtVoxelPositionDirty(int x, int y, int z)
{
if (x < 0 || y < 0 || z < 0 || x >= ParentVolume.XSize || y >= ParentVolume.YSize || z >= ParentVolume.ZSize)
return;
PicaVoxelPoint chunkPos = new PicaVoxelPoint((int)(x / ParentVolume.XChunkSize), (int)(y / ParentVolume.YChunkSize),
(int)(z / ParentVolume.ZChunkSize));
if (chunks[chunkPos.X, chunkPos.Y, chunkPos.Z] != null) AddChunkToUpdateList(chunkPos.X, chunkPos.Y, chunkPos.Z);
}
private void AddChunkToUpdateList(int x, int y, int z)
{
var pvp = new PicaVoxelPoint(x, y, z);
if (chunksToUpdate.Contains(pvp)) return;
chunks[x, y, z].IsUpdated = true;
chunksToUpdate.Add(pvp);
}
private void GetChunkReferences()
{
if (ParentVolume.RuntimOnlyMesh && !Application.isPlaying) return;
//Debug.Log("Frame GetChunkReferences");
chunks =
new Chunk[(int)Mathf.Ceil((float)XSize / ParentVolume.XChunkSize),
(int)Mathf.Ceil((float)YSize / ParentVolume.YChunkSize), (int)Mathf.Ceil((float)ZSize / ParentVolume.ZChunkSize)];
for (int x = 0; x < (int)Mathf.Ceil((float)XSize / ParentVolume.XChunkSize); x++)
for (int y = 0; y < (int)Mathf.Ceil((float)YSize / ParentVolume.YChunkSize); y++)
for (int z = 0; z < (int)Mathf.Ceil((float)ZSize / ParentVolume.ZChunkSize); z++)
{
Transform c = transform.FindChild("Chunks/Chunk (" + x + "," + y + "," + z + ")");
if (c == null)
{
//Debug.LogWarning("Couldn't get chunk refs - should probably work out why!");
CreateChunks();
return;
}
chunks[x, y, z] = c.GetComponent<Chunk>();
}
}
public bool IsLocalPositionInBounds(Vector3 pos)
{
Vector3 size = new Vector3(XSize * ParentVolume.VoxelSize, YSize * ParentVolume.VoxelSize,
ZSize * ParentVolume.VoxelSize);
Vector3 v1 = -size;
Vector3 v2 = size;
if (pos.x >= v1.x && pos.y >= v1.y && pos.z >= v1.z && pos.x <= v2.x && pos.y <= v2.y && pos.z <= v2.z)
return true;
return false;
}
/// <summary>
/// Scroll voxels along X axis
/// </summary>
/// <param name="amount">The amount of voxels to scroll by</param>
/// This shouldn't really be used at runtime
public void ScrollX(int amount)
{
Voxel[] tempVoxels = new Voxel[XSize * YSize * ZSize];
int reverse = -amount;
if (amount < 0)
{
Helper.CopyVoxelsInBox(ref Voxels, ref tempVoxels, new PicaVoxelBox(0, 0, 0, reverse - 1, YSize - 1, ZSize - 1), new PicaVoxelBox(0, 0, 0, reverse - 1, YSize - 1, ZSize - 1), new PicaVoxelPoint(XSize, YSize, ZSize), new PicaVoxelPoint(XSize, YSize, ZSize), false);
Helper.CopyVoxelsInBox(ref Voxels, ref Voxels, new PicaVoxelBox(reverse, 0, 0, XSize - 1, YSize - 1, ZSize - 1), new PicaVoxelBox(0, 0, 0, XSize - 1 - reverse - 1, YSize - 1, ZSize - 1), new PicaVoxelPoint(XSize, YSize, ZSize), new PicaVoxelPoint(XSize, YSize, ZSize), false);
Helper.CopyVoxelsInBox(ref tempVoxels, ref Voxels, new PicaVoxelBox(0, 0, 0, reverse - 1, YSize - 1, ZSize - 1), new PicaVoxelBox(XSize - reverse, 0, 0, XSize - 1, YSize - 1, ZSize - 1), new PicaVoxelPoint(XSize, YSize, ZSize), new PicaVoxelPoint(XSize, YSize, ZSize), false);
}
else
{
Helper.CopyVoxelsInBox(ref Voxels, ref tempVoxels, new PicaVoxelBox(0, 0, 0, XSize - 1 - amount, YSize - 1, ZSize - 1), new PicaVoxelBox(0, 0, 0, XSize - 1 - amount, YSize, ZSize), new PicaVoxelPoint(XSize, YSize, ZSize), new PicaVoxelPoint(XSize, YSize, ZSize), false);
Helper.CopyVoxelsInBox(ref Voxels, ref Voxels, new PicaVoxelBox(XSize - amount, 0, 0, XSize - 1, YSize - 1, ZSize - 1), new PicaVoxelBox(0, 0, 0, amount - 1, YSize - 1, ZSize - 1), new PicaVoxelPoint(XSize, YSize, ZSize), new PicaVoxelPoint(XSize, YSize, ZSize), false);
Helper.CopyVoxelsInBox(ref tempVoxels, ref Voxels, new PicaVoxelBox(0, 0, 0, XSize - 1 - amount, YSize - 1, ZSize - 1), new PicaVoxelBox(amount, 0, 0, XSize - 1, YSize - 1, ZSize - 1), new PicaVoxelPoint(XSize, YSize, ZSize), new PicaVoxelPoint(XSize, YSize, ZSize), false);
}
UpdateAllChunks();
SaveForSerialize();
}
/// <summary>
/// Scroll voxels along Y axis
/// </summary>
/// <param name="amount">The amount of voxels to scroll by</param>
/// This shouldn't really be used at runtime
public void ScrollY(int amount)
{
Voxel[] tempVoxels = new Voxel[XSize * YSize * ZSize];
int reverse = -amount;
if (amount < 0)
{
Helper.CopyVoxelsInBox(ref Voxels, ref tempVoxels, new PicaVoxelBox(0, 0, 0, XSize - 1, reverse - 1, ZSize - 1), new PicaVoxelBox(0, 0, 0, XSize - 1, reverse - 1, ZSize - 1), new PicaVoxelPoint(XSize, YSize, ZSize), new PicaVoxelPoint(XSize, YSize, ZSize), false);
Helper.CopyVoxelsInBox(ref Voxels, ref Voxels, new PicaVoxelBox(0, reverse, 0, XSize - 1, YSize - 1, ZSize - 1), new PicaVoxelBox(0, 0, 0, XSize - 1, YSize - reverse - 1, ZSize - 1), new PicaVoxelPoint(XSize, YSize, ZSize), new PicaVoxelPoint(XSize, YSize, ZSize), false);
Helper.CopyVoxelsInBox(ref tempVoxels, ref Voxels, new PicaVoxelBox(0, 0, 0, XSize - 1, reverse - 1, ZSize - 1), new PicaVoxelBox(0, YSize - reverse, 0, XSize - 1, YSize - 1, ZSize - 1), new PicaVoxelPoint(XSize, YSize, ZSize), new PicaVoxelPoint(XSize, YSize, ZSize), false);
}
else
{
Helper.CopyVoxelsInBox(ref Voxels, ref tempVoxels, new PicaVoxelBox(0, 0, 0, XSize - 1, YSize - 1 - amount, ZSize - 1), new PicaVoxelBox(0, 0, 0, XSize - 1, YSize - 1 - amount, ZSize - 1), new PicaVoxelPoint(XSize, YSize, ZSize), new PicaVoxelPoint(XSize, YSize, ZSize), false);
Helper.CopyVoxelsInBox(ref Voxels, ref Voxels, new PicaVoxelBox(0, YSize - amount, 0, XSize - 1, YSize - 1, ZSize - 1), new PicaVoxelBox(0, 0, 0, XSize - 1, amount - 1, ZSize - 1), new PicaVoxelPoint(XSize, YSize, ZSize), new PicaVoxelPoint(XSize, YSize, ZSize), false);
Helper.CopyVoxelsInBox(ref tempVoxels, ref Voxels, new PicaVoxelBox(0, 0, 0, XSize - 1, YSize - 1 - amount, ZSize - 1), new PicaVoxelBox(0, amount, 0, XSize - 1, YSize - 1, ZSize - 1), new PicaVoxelPoint(XSize, YSize, ZSize), new PicaVoxelPoint(XSize, YSize, ZSize), false);
}
UpdateAllChunks();
SaveForSerialize();
}
/// <summary>
/// Scroll voxels along Z axis
/// </summary>
/// <param name="amount">The amount of voxels to scroll by</param>
/// This shouldn't really be used at runtime
public void ScrollZ(int amount)
{
Voxel[] tempVoxels = new Voxel[XSize * YSize * ZSize];
int reverse = -amount;
if (amount < 0)
{
Helper.CopyVoxelsInBox(ref Voxels, ref tempVoxels, new PicaVoxelBox(0, 0, 0, XSize - 1, YSize - 1, reverse - 1), new PicaVoxelBox(0, 0, 0, XSize - 1, YSize - 1, reverse - 1), new PicaVoxelPoint(XSize, YSize, ZSize), new PicaVoxelPoint(XSize, YSize, ZSize), false);
Helper.CopyVoxelsInBox(ref Voxels, ref Voxels, new PicaVoxelBox(0, 0, reverse, XSize - 1, YSize - 1, ZSize - 1), new PicaVoxelBox(0, 0, 0, XSize - 1, YSize - 1, ZSize - reverse - 1), new PicaVoxelPoint(XSize, YSize, ZSize), new PicaVoxelPoint(XSize, YSize, ZSize), false);
Helper.CopyVoxelsInBox(ref tempVoxels, ref Voxels, new PicaVoxelBox(0, 0, 0, XSize - 1, YSize - 1, reverse - 1), new PicaVoxelBox(0, 0, ZSize - reverse, XSize - 1, YSize - 1, ZSize - 1), new PicaVoxelPoint(XSize, YSize, ZSize), new PicaVoxelPoint(XSize, YSize, ZSize), false);
}
else
{
Helper.CopyVoxelsInBox(ref Voxels, ref tempVoxels, new PicaVoxelBox(0, 0, 0, XSize - 1, YSize - 1, ZSize - 1 - amount), new PicaVoxelBox(0, 0, 0, XSize - 1, YSize - 1, ZSize - 1 - amount), new PicaVoxelPoint(XSize, YSize, ZSize), new PicaVoxelPoint(XSize, YSize, ZSize), false);
Helper.CopyVoxelsInBox(ref Voxels, ref Voxels, new PicaVoxelBox(0, 0, ZSize - amount, XSize - 1, YSize - 1, ZSize - 1), new PicaVoxelBox(0, 0, 0, XSize - 1, YSize - 1, amount - 1), new PicaVoxelPoint(XSize, YSize, ZSize), new PicaVoxelPoint(XSize, YSize, ZSize), false);
Helper.CopyVoxelsInBox(ref tempVoxels, ref Voxels, new PicaVoxelBox(0, 0, 0, XSize - 1, YSize - 1, ZSize - 1 - amount), new PicaVoxelBox(0, 0, amount, XSize - 1, YSize - 1, ZSize - 1), new PicaVoxelPoint(XSize, YSize, ZSize), new PicaVoxelPoint(XSize, YSize, ZSize), false);
}
UpdateAllChunks();
SaveForSerialize();
}
/// <summary>
/// Restore all voxels that have been destroyed (Voxel.State = VoxelState.Hidden)
/// </summary>
public void Rebuild()
{
if (chunks == null) GetChunkReferences();
for (int x = 0; x < XSize; x++)
for (int y = 0; y < YSize; y++)
for (int z = 0; z < ZSize; z++)
{
if (Voxels[x + XSize * (y + YSize * z)].State == VoxelState.Hidden)
{
Voxels[x + XSize * (y + YSize * z)].State = VoxelState.Active;
}
AddChunkToUpdateList(x / ParentVolume.XChunkSize, y / ParentVolume.YChunkSize, z / ParentVolume.ZChunkSize);
}
}
/// <summary>
/// Serialise voxel array to byte array ready for Unity serialisation
/// </summary>
public void SaveForSerialize()
{
// We don't need to be saving for serialization at runtime
if (Application.isPlaying) return;
//Debug.Log("Frame SaveForSerialize");
if (Voxels == null)
{
Debug.LogError("Voxels are null upon saving!");
return;
}
try
{
bserializedVoxels = ToCompressedByteArray();
}
catch (Exception e)
{
Debug.LogError(e);
}
}
public byte[] ToCompressedByteArray()
{
byte[] returnArray;
byte[] streambuff = new byte[(XSize * YSize * ZSize) * Voxel.BYTE_SIZE];
byte[] buffer = new byte[Voxel.BYTE_SIZE];
int o = 0;
for (int x = 0; x < XSize; x++)
for (int y = 0; y < YSize; y++)
for (int z = 0; z < ZSize; z++)
{
buffer = Voxels[x + XSize * (y + YSize * z)].ToBytes();
for (int i = 0; i < Voxel.BYTE_SIZE; i++)
{
streambuff[o] = buffer[i];
o++;
}
}
using (var ms = new MemoryStream())
{
using (var gzs = new GZipOutputStream(ms))
{
gzs.SetLevel(1);
gzs.Write(streambuff, 0, streambuff.Length);
}
returnArray = ms.ToArray();
}
return returnArray;
}
public void OnBeforeSerialize()
{
}
#if UNITY_EDITOR
public bool HasDeserialized = false;
#endif
public void OnAfterDeserialize()
{
// Debug.Log("Frame OnAfterSerialize");
try
{
if (bserializedVoxels == null) return;
if (bserializedVoxels.Length == 0) return;
FromCompressedByteArray(bserializedVoxels);
#if UNITY_EDITOR
HasDeserialized = true;
#endif
}
catch (Exception ex)
{
Debug.LogError(ex.ToString());
Debug.LogError("Couldn't deserialise something");
}
}
public void FromCompressedByteArray(byte[] compressed)
{
byte[] streambuff = new byte[XSize * YSize * ZSize * Voxel.BYTE_SIZE];
using (var ms = new MemoryStream(compressed))
{
using (var gzs = new GZipInputStream(ms))
{
gzs.Read(streambuff, 0, streambuff.Length);
}
}
Voxels = new Voxel[XSize * YSize * ZSize];
int o = 0;
byte[] buffer = new byte[Voxel.BYTE_SIZE];
for (int x = 0; x < XSize; x++)
for (int y = 0; y < YSize; y++)
for (int z = 0; z < ZSize; z++)
{
for (int i = 0; i < Voxel.BYTE_SIZE; i++) buffer[i] = streambuff[o + i];
Voxels[x + XSize * (y + YSize * z)] = new Voxel(buffer);
o += Voxel.BYTE_SIZE;
}
}
}
}
/////////////////////////////////////////////////////////////////////////
//
// PicaVoxel - The tiny voxel engine for Unity - http://picavoxel.com
// By Gareth Williams - @garethiw - http://gareth.pw
//
// Source code distributed under standard Asset Store licence:
// http://unity3d.com/legal/as_terms
//
/////////////////////////////////////////////////////////////////////////
using System;
using System.IO;
using System.Threading;
using UnityEngine;
using System.Collections.Generic;
using UnityEngine.Rendering;
using Debug = UnityEngine.Debug;
using Random = UnityEngine.Random;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace PicaVoxel
{
/// <summary>
/// Which type of mesh collider to use when generating meshes
/// </summary>
public enum CollisionMode
{
None,
MeshColliderConvex,
MeshColliderConcave
}
public enum MeshingMode
{
Greedy,
Culled,
Marching
}
#if UNITY_EDITOR
/// <summary>
/// Toggles between Color and Value rendering when generating meshes - editor only
/// </summary>
public enum EditorPaintMode
{
Color,
Value
}
public enum Importer
{
None,
Magica,
Image
}
#endif
/// <summary>
/// The main parent script for a PicaVoxel volume
/// </summary>
[AddComponentMenu("PicaVoxel/PicaVoxel Volume")]
[Serializable]
[ExecuteInEditMode]
public class Volume : MonoBehaviour, ISerializationCallbackReceiver
{
public GameObject FramePrefab;
public int XChunkSize = 16;
public int YChunkSize = 16;
public int ZChunkSize = 16;
public int XSize = 32;
public int YSize = 32;
public int ZSize = 32;
public float VoxelSize = 0.1f;
public float OverlapAmount = 0f;
public Vector3 Pivot = Vector3.zero;
public int NumFrames
{
get { return Frames.Count; }
}
public int CurrentFrame = 0;
public List<Frame> Frames;
// Chunk generation settings
public BoxCollider Hitbox;
public MeshingMode MeshingMode;
public MeshingMode MeshColliderMeshingMode;
public bool GenerateMeshColliderSeparately = false;
public Material Material;
public PhysicMaterial PhysicMaterial;
public bool CollisionTrigger;
public CollisionMode CollisionMode;
public float SelfShadingIntensity = 0.2f;
public ShadowCastingMode CastShadows = ShadowCastingMode.On;
public bool ReceiveShadows = true;
public int ChunkLayer;
public Color[] PaletteColors = new Color[25];
public bool RuntimOnlyMesh = false;
#if UNITY_EDITOR
public bool IsEnabledForEditing = false;
public bool DrawGrid = false;
public bool DrawMesh = false;
public string AssetGuid;
public EditorPaintMode PaintMode = EditorPaintMode.Color;
public ModelImporterMeshCompression MeshCompression = ModelImporterMeshCompression.Off;
public Importer ImportedFrom;
public string ImportedFile;
public Color ImportedCutoutColor;
[SerializeField] private int thisInstanceId;
#endif
private void Awake()
{
Hitbox = transform.FindChild("Hitbox").GetComponent<BoxCollider>();
int startFrame = CurrentFrame;
if (Application.isPlaying)
{
// Pre-enable all animation frames to avoid mesh generation when frame is changed for the first time
for (int i = 0; i < NumFrames; i++)
NextFrame();
}
CurrentFrame = startFrame;
#if UNITY_EDITOR
// This used to perform a check to see if the object had been duplicated in the editor
// However it is unreliable at best, so I've removed it until Unity provide a proper way to check for copy+paste/duplicate
// Instead, there's a button on the Volume inspector to create new mesh asset instances
if (!Application.isPlaying)//if in the editor
{
if (thisInstanceId != GetInstanceID())
{
if (thisInstanceId == 0)
{
thisInstanceId = GetInstanceID();
//Debug.Log("Not Copied");
}
else
{
// This used to perform a check to see if the object had been duplicated in the editor
// However it is unreliable at best, so I've removed it until Unity provide a proper way to check for copy+paste/duplicate
// Instead, there's a button on the Volume inspector to create new mesh asset instances
thisInstanceId = GetInstanceID();
//if (thisInstanceId < 0)
//{
// SaveChunkMeshes(true);
// //Debug.Log("DUPLICATE/COPY");
//}
}
}
}
#endif
}
/// <summary>
/// Returns a voxel contained in this volume's current frame, at a given world position
/// </summary>
/// <param name="pos">The world position in the scene</param>
/// <returns>A voxel if position is within this volume, otherwise null</returns>
public Voxel? GetVoxelAtWorldPosition(Vector3 pos)
{
return Frames[CurrentFrame].GetVoxelAtWorldPosition(pos);
}
/// <summary>
/// Returns a voxel contained in this volume's current frame,, at a given array position
/// </summary>
/// <param name="x">X array position</param>
/// <param name="y">Y array position</param>
/// <param name="z">Z array position</param>
/// <returns>A voxel if position is within the array, otherwise null</returns>
public Voxel? GetVoxelAtArrayPosition(int x, int y, int z)
{
return Frames[CurrentFrame].GetVoxelAtArrayPosition(x, y, z);
}
/// <summary>
/// Attempts to set a voxel within this volume's current frame, at a given world position, to the supplied voxel value
/// </summary>
/// <param name="pos">The world position in the scene</param>
/// <param name="vox">The new voxel to set to</param>
/// <returns>The array position of the voxel</returns>
public Vector3 SetVoxelAtWorldPosition(Vector3 pos, Voxel vox)
{
return Frames[CurrentFrame].SetVoxelAtWorldPosition(pos, vox);
}
/// <summary>
/// Attempts to set a voxel's state within this volume's current frame, at a given world position, to the supplied value
/// </summary>
/// <param name="pos">The world position in the scene</param>
/// <param name="state">The new voxel state to set to</param>
/// <returns>The array position of the voxel</returns>
public Vector3 SetVoxelStateAtWorldPosition(Vector3 pos, VoxelState state)
{
return Frames[CurrentFrame].SetVoxelStateAtWorldPosition(pos, state);
}
/// <summary>
/// Attempts to set a voxel within this volume's current frame, at a specified array position
/// </summary>
/// <param name="pos">A PicaVoxelPoint location within the 3D array of voxels</param>
/// <param name="vox">The new voxel to set to</param>
public void SetVoxelAtArrayPosition(PicaVoxelPoint pos, Voxel vox)
{
Frames[CurrentFrame].SetVoxelAtArrayPosition(pos, vox);
}
/// <summary>
/// Attempts to set a voxel's state within this volume's current frame, at a specified array position
/// </summary>
/// <param name="pos">A PicaVoxelPoint location within the 3D array of voxels</param>
/// <param name="state">The new state to set to</param>
public void SetVoxelStateAtArrayPosition(PicaVoxelPoint pos, VoxelState state)
{
Frames[CurrentFrame].SetVoxelStateAtArrayPosition(pos, state);
}
/// <summary>
/// Attempts to set a voxel within this volume's current frame, at a specified x,y,z array position
/// </summary>
/// <param name="x">X array position</param>
/// <param name="y">Y array position</param>
/// <param name="z">Z array position</param>
/// <param name="vox">The new voxel to set to</param>
public void SetVoxelAtArrayPosition(int x, int y, int z, Voxel vox)
{
Frames[CurrentFrame].SetVoxelAtArrayPosition(x, y, z, vox);
}
/// <summary>
/// Attempts to set a voxel's state within this volume's current frame, at a specified x,y,z array position
/// </summary>
/// <param name="x">X array position</param>
/// <param name="y">Y array position</param>
/// <param name="z">Z array position</param>
/// <param name="state">The new state to set to</param>
public void SetVoxelStateAtArrayPosition(int x, int y, int z, VoxelState state)
{
Frames[CurrentFrame].SetVoxelStateAtArrayPosition(x, y, z, state);
}
/// <summary>
/// Returns the local position of a voxel within this volume's current frame, at a specified world position
/// </summary>
/// <param name="pos">The world position in the scene</param>
/// <returns>The local voxel position</returns>
public Vector3 GetVoxelPosition(Vector3 pos)
{
return Frames[CurrentFrame].GetVoxelPosition(pos);
}
/// <summary>
/// Returns the array position of a voxel within this volume's current frame, at a specified world position
/// </summary>
/// <param name="pos">The world position in the scene</param>
/// <returns>The array position of the voxel</returns>
public PicaVoxelPoint GetVoxelArrayPosition(Vector3 pos)
{
return Frames[CurrentFrame].GetVoxelArrayPosition(pos);
}
/// <summary>
/// Returns the world position of a voxel given its array positions
/// </summary>
/// <param name="x">The X position of the voxel in the array</param>
/// <param name="y">The Y position of the voxel in the array</param>
/// <param name="z">The Z position of the voxel in the array</param>
/// <returns>The world position of the center of the voxel</returns>
public Vector3 GetVoxelWorldPosition(int x, int y, int z)
{
return Frames[CurrentFrame].GetVoxelWorldPosition(x,y,z);
}
/// <summary>
/// Get the current animation frame
/// </summary>
/// <returns>The current animation frame</returns>
public Frame GetCurrentFrame()
{
return Frames[CurrentFrame];
}
/// <summary>
/// Update the pivot position. Use this after setting Pivot.
/// </summary>
public void UpdatePivot()
{
foreach (Frame frame in Frames)
frame.transform.localPosition = -Pivot;
UpdateBoxCollider();
}
/// <summary>
/// Generates a 32x32x32 frame, filled with white voxels of value 128
/// </summary>
public void GenerateBasic(FillMode fillMode)
{
//Debug.Log("Object GenerateBasic");
AddFrame(0);
Frames[0].GenerateBasic(fillMode);
UpdateBoxCollider();
}
/// <summary>
/// Insert a new animation frame before the supplied frame number
/// </summary>
/// <param name="where">The frame number to insert the new frame before</param>
public void AddFrame(int where)
{
//Debug.Log("Object AddFrame");
var newFrame = Instantiate(FramePrefab, Vector3.zero, Quaternion.identity) as GameObject;
Frame newPVFrame = newFrame.GetComponent<Frame>();
newPVFrame.XSize = XSize;
newPVFrame.YSize = YSize;
newPVFrame.ZSize = ZSize;
newFrame.name = "Frame";
newFrame.layer = gameObject.layer;
newFrame.transform.parent = transform;
newFrame.transform.localPosition = -Pivot;
newFrame.transform.rotation = transform.rotation;
newFrame.transform.localScale = transform.localScale;
if (Frames.Count > 0)
newPVFrame.GenerateNewFrame(Frames[CurrentFrame]);
else
newPVFrame.GenerateNewFrame();
Frames.Insert(where, newPVFrame);
SetFrame(where);
UpdateFrameNames();
}
/// <summary>
/// Delete the current frame
/// </summary>
public void DeleteFrame()
{
if (Frames.Count <= 1) return;
DestroyImmediate(Frames[CurrentFrame].gameObject);
Frames.RemoveAt(CurrentFrame);
if (CurrentFrame >= Frames.Count) CurrentFrame = Frames.Count - 1;
SetFrame(CurrentFrame);
UpdateFrameNames();
}
public void MoveFrameRight()
{
if (CurrentFrame >= NumFrames-1) return;
Frame tempFrame = Frames[CurrentFrame];
Frames[CurrentFrame] = Frames[CurrentFrame + 1];
Frames[CurrentFrame + 1] = tempFrame;
CurrentFrame++;
UpdateFrameNames();
}
public void MoveFrameLeft()
{
if (CurrentFrame <= 0) return;
Frame tempFrame = Frames[CurrentFrame];
Frames[CurrentFrame] = Frames[CurrentFrame - 1];
Frames[CurrentFrame - 1] = tempFrame;
CurrentFrame--;
UpdateFrameNames();
}
/// <summary>
/// Move to the next animation frame (wraps around)
/// </summary>
public void NextFrame()
{
if (NumFrames <= 1) return;
CurrentFrame++;
if (CurrentFrame >= NumFrames) CurrentFrame = 0;
ChangeFrame();
}
/// <summary>
/// Move to the previous animation frame (wraps around)
/// </summary>
public void PrevFrame()
{
if (NumFrames <= 1) return;
CurrentFrame--;
if (CurrentFrame < 0) CurrentFrame = NumFrames - 1;
ChangeFrame();
}
/// <summary>
/// Sets the current animation frame
/// </summary>
/// <param name="frame">The frame number to set to</param>
/// Will not set the frame if supplied frame is invalid
public void SetFrame(int frame)
{
if (frame < 0 || frame >= NumFrames) return;
CurrentFrame = frame;
ChangeFrame();
}
private void ChangeFrame()
{
foreach (var frame in Frames)
{
frame.gameObject.SetActive(false);
}
Frames[CurrentFrame].gameObject.SetActive(true);
}
private void UpdateFrameNames()
{
foreach (Frame f in Frames)
{
f.gameObject.name = "Frame " + (Frames.IndexOf(f)+1);
}
}
/// <summary>
/// Update only the chunks which have changed voxels on the current frame
/// </summary>
/// <param name="immediate">If true, don't use threading to perform this update</param>
public void UpdateChunks(bool immediate)
{
//Debug.Log("Object UpdateChunks");
Frames[CurrentFrame].UpdateChunks(immediate);
}
/// <summary>
/// Immediately update all chunks on the current frame
/// </summary>
public void UpdateAllChunks()
{
Frames[CurrentFrame].UpdateAllChunks();
}
/// <summary>
/// Updates all chunks on the current animation frame next game frame (threaded)
/// </summary>
public void UpdateAllChunksNextFrame()
{
Frames[CurrentFrame].UpdateAllChunksNextFrame();
}
/// <summary>
/// Re-create all chunks on all animation frames
/// </summary>
public void CreateChunks()
{
foreach (Frame frame in Frames)
if(frame!=null) frame.CreateChunks();
UpdateBoxCollider();
}
public void SaveChunkMeshes(bool forceNew)
{
#if UNITY_EDITOR
if (RuntimOnlyMesh) return;
if (string.IsNullOrEmpty(AssetGuid) || forceNew) AssetGuid = Guid.NewGuid().ToString();
string path = Path.Combine(Helper.GetMeshStorePath(), GuidEncoder.Encode(AssetGuid.ToString()));
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
#if !UNITY_WEBPLAYER
DirectoryInfo di = new DirectoryInfo(path);
foreach (FileInfo f in di.GetFiles())
f.Delete();
#endif
foreach (Frame frame in Frames)
if (frame != null) frame.SaveChunkMeshes(true);
#endif
}
/// <summary>
/// Deactivates all particles in the current frame, within a supplied radius of a world position
/// </summary>
/// <param name="position">The world position of the centre of the explosion</param>
/// <param name="explosionRadius">The radius of the explosion</param>
/// <returns>A Batch of voxels that were destroyed by the explosion</returns>
public Batch Explode(Vector3 position, float explosionRadius, int valueFilter, Exploder.ExplodeValueFilterOperation valueFilterOperation)
{
Batch batch = new Batch(this);
Color tint = Material.GetColor("_Tint");
Matrix4x4 transformMatrix = transform.worldToLocalMatrix;
position += (transform.rotation * (Pivot));
for (float x = position.x - explosionRadius; x <= position.x + explosionRadius; x += VoxelSize * 0.5f)
for (float y = position.y - explosionRadius; y <= position.y + explosionRadius; y += VoxelSize * 0.5f)
for (float z = position.z - explosionRadius; z <= position.z + explosionRadius; z += VoxelSize * 0.5f)
{
Vector3 checkPos = new Vector3(x, y, z);
if (Vector3.Distance(checkPos, position) <= explosionRadius)
{
Vector3 localPos = transformMatrix.MultiplyPoint3x4(checkPos); //transform.InverseTransformPoint(pos);
//if (!Frames[CurrentFrame].IsLocalPositionInBounds(localPos)) continue;
int testX = (int)(localPos.x / VoxelSize);
int testY = (int)(localPos.y / VoxelSize);
int testZ = (int)(localPos.z / VoxelSize);
if (testX < 0 || testY < 0 || testZ < 0 || testX >= XSize || testY >= YSize || testZ >= ZSize) continue;
if (Frames[CurrentFrame].Voxels[testX + XSize * (testY + YSize * testZ)].Active &&
FilterExplosion(Frames[CurrentFrame].Voxels[testX + XSize * (testY + YSize * testZ)].Value, valueFilter, valueFilterOperation) &&
Vector3.Distance(position, checkPos) <= explosionRadius)
{
Voxel v = Frames[CurrentFrame].Voxels[testX + XSize * (testY + YSize * testZ)];
v.Color *= tint;
batch.Add(v, testX, testY, testZ, checkPos - (transform.rotation * (Pivot)));
SetVoxelStateAtArrayPosition(testX, testY, testZ, VoxelState.Hidden);
}
}
}
return batch;
}
/// <summary>
/// Adds particles to the PicaVoxel Particle System (if available) representing the shape of this volume
/// Use it before destroying/deactivating the volume to leave particles behind
/// </summary>
/// <param name="particleVelocity">Initial velocity of the created particles (outward from center of volume)</param>
/// <param name="actuallyDestroyVoxels">If true, will set all the voxels to inactive</param>
public void Destruct(float particleVelocity, bool actuallyDestroyVoxels)
{
Batch batch = new Batch(this);
Vector3 posZero = transform.position + (transform.rotation * (-Pivot + (Vector3.one * (VoxelSize * 0.5f))));
Vector3 oneX = transform.rotation * (new Vector3(VoxelSize, 0, 0));
Vector3 oneY = transform.rotation * (new Vector3(0f, VoxelSize, 0));
Vector3 oneZ = transform.rotation * (new Vector3(0, 0, VoxelSize));
Vector3 partPos = posZero;
for (int x = 0; x < XSize; x++)
{
Vector3 xmult = oneX * x;
for (int y = 0; y < YSize; y++)
{
Vector3 ymult = oneY * y;
for (int z = 0; z < ZSize; z++)
{
Vector3 zmult = oneZ * z;
partPos.x = posZero.x + xmult.x + ymult.x + zmult.x;
partPos.y = posZero.y + xmult.y + ymult.y + zmult.y;
partPos.z = posZero.z + xmult.z + ymult.z + zmult.z;
if (Frames[CurrentFrame].Voxels[x + XSize * (y + YSize * z)].Active)
{
Voxel v = Frames[CurrentFrame].Voxels[x + XSize * (y + YSize * z)];
v.Color *= Material.GetColor("_Tint");
batch.Add(v, x, y, z, partPos);
v.State = VoxelState.Hidden;
if (actuallyDestroyVoxels) SetVoxelAtArrayPosition(x, y, z, v);
}
}
}
}
if (batch.Voxels.Count > 0 && VoxelParticleSystem.Instance != null)
VoxelParticleSystem.Instance.SpawnBatch(batch,
pos => (pos - transform.position).normalized * Random.Range(0f, particleVelocity * 2f));
batch.Dispose();
}
/// <summary>
/// Restore all voxels that have been destroyed (Voxel.State = VoxelState.Hidden) on all frames
/// </summary>
public void Rebuild()
{
foreach (Frame frame in Frames)
frame.Rebuild();
}
private bool FilterExplosion(byte value, int valueFilter, Exploder.ExplodeValueFilterOperation valueFilterOperation)
{
switch (valueFilterOperation)
{
case Exploder.ExplodeValueFilterOperation.LessThan:
return value < valueFilter;
case Exploder.ExplodeValueFilterOperation.LessThanOrEqualTo:
return value <= valueFilter;
case Exploder.ExplodeValueFilterOperation.EqualTo:
return value == valueFilter;
case Exploder.ExplodeValueFilterOperation.GreaterThanOrEqualTo:
return value >= valueFilter;
case Exploder.ExplodeValueFilterOperation.GreaterThan:
return value > valueFilter;
}
return false;
}
/// <summary>
/// Rotate the entire volume around the X axis
/// </summary>
public void RotateX()
{
int tempSize = YSize;
YSize = ZSize;
ZSize = tempSize;
foreach (Frame frame in Frames)
{
Helper.RotateVoxelArrayX(ref frame.Voxels,new PicaVoxelPoint(frame.XSize, frame.YSize, frame.ZSize));
frame.XSize = XSize;
frame.YSize = YSize;
frame.ZSize = ZSize;
}
CreateChunks();
SaveForSerialize();
}
/// <summary>
/// Rotate the entire volume around the Y axis
/// </summary>
public void RotateY()
{
int tempSize = XSize;
XSize = ZSize;
ZSize = tempSize;
foreach (Frame frame in Frames)
{
Helper.RotateVoxelArrayY(ref frame.Voxels, new PicaVoxelPoint(frame.XSize, frame.YSize, frame.ZSize));
frame.XSize = XSize;
frame.YSize = YSize;
frame.ZSize = ZSize;
}
CreateChunks();
SaveForSerialize();
}
/// <summary>
/// Rotate the entire volume around the Z axis
/// </summary>
public void RotateZ()
{
int tempSize = YSize;
YSize = XSize;
XSize = tempSize;
foreach (Frame frame in Frames)
{
Helper.RotateVoxelArrayZ(ref frame.Voxels, new PicaVoxelPoint(frame.XSize, frame.YSize, frame.ZSize));
frame.XSize = XSize;
frame.YSize = YSize;
frame.ZSize = ZSize;
}
CreateChunks();
SaveForSerialize();
}
public void ScrollX(int amount, bool allFrames)
{
if (allFrames)
foreach (Frame frame in Frames)
frame.ScrollX(amount);
else
GetCurrentFrame().ScrollX(amount);
}
public void ScrollY(int amount, bool allFrames)
{
if (allFrames)
foreach (Frame frame in Frames)
frame.ScrollY(amount);
else
GetCurrentFrame().ScrollY(amount);
}
public void ScrollZ(int amount, bool allFrames)
{
if (allFrames)
foreach (Frame frame in Frames)
frame.ScrollZ(amount);
else
GetCurrentFrame().ScrollZ(amount);
}
private void UpdateBoxCollider()
{
Hitbox = transform.FindChild("Hitbox").GetComponent<BoxCollider>();
Hitbox.size = new Vector3(XSize*VoxelSize, YSize*VoxelSize, ZSize*VoxelSize);
Hitbox.center = new Vector3(XSize*VoxelSize, YSize*VoxelSize, ZSize*VoxelSize)/2f;
transform.FindChild("Hitbox").transform.localPosition = -Pivot;
}
/// <summary>
/// Serialise all frames to byte array ready for Unity serialisation
/// </summary>
public void SaveForSerialize()
{
//Debug.Log("Object SaveForSerialize");
foreach (Frame frame in Frames) frame.SaveForSerialize();
}
/// <summary>
/// Change the mesh collision mode of all frames
/// </summary>
/// <param name="collisionMode">The CollisonMode to change to</param>
public void ChangeCollisionMode(CollisionMode collisionMode)
{
CollisionMode = collisionMode;
foreach (Frame frame in Frames) frame.GenerateMeshColliders();
}
public void OnBeforeSerialize()
{
}
public void OnAfterDeserialize()
{
}
// Editor-specific functionality
#if UNITY_EDITOR
public bool Undone = false;
public void OnUndoRedo()
{
Undone = true;
if (this == null) return;
//CreateChunks();
}
public void OnDrawGizmos()
{
foreach (Frame frame in Frames)
{
for (int i = 0; i < frame.transform.FindChild("Chunks").childCount; i++)
{
var o = frame.transform.FindChild("Chunks").GetChild(i).gameObject;
if (o.GetComponent<Renderer>())
EditorUtility.SetSelectedWireframeHidden(o.GetComponent<Renderer>(), IsEnabledForEditing && !DrawMesh);
}
}
if (!IsEnabledForEditing) return;
if (DrawGrid)
{
Gizmos.matrix = Frames[CurrentFrame].transform.FindChild("Chunks").localToWorldMatrix;
Gizmos.color = new Color(1f, 1f, 1f, 0.1f);
for (int x = 0; x <= XSize; x++)
for (int y = 0; y <= YSize; y++)
for (int z = 0; z <= ZSize; z++)
{
if (x == 0)
Gizmos.DrawLine(new Vector3(x, y, z)*VoxelSize, new Vector3(x + XSize, y, z)*VoxelSize);
if (y == 0)
Gizmos.DrawLine(new Vector3(x, y, z)*VoxelSize, new Vector3(x, y + YSize, z)*VoxelSize);
if (z == 0)
Gizmos.DrawLine(new Vector3(x, y, z)*VoxelSize, new Vector3(x, y, z + ZSize)*VoxelSize);
}
}
}
#endif
}
}
/////////////////////////////////////////////////////////////////////////
//
// PicaVoxel - The tiny voxel engine for Unity - http://picavoxel.com
// By Gareth Williams - @garethiw - http://gareth.pw
//
// Source code distributed under standard Asset Store licence:
// http://unity3d.com/legal/as_terms
//
/////////////////////////////////////////////////////////////////////////
using System.Collections.Generic;
using System.IO;
using Object = UnityEngine.Object;
#if UNITY_EDITOR
using System;
using UnityEditor;
using UnityEngine;
using System.Collections;
namespace PicaVoxel
{
public enum EditorCursorMode
{
Add,
Subtract,
Paint,
BoxAdd,
BoxSubtract,
BoxPaint,
Select,
PickColor,
PickValue,
BrushAdd,
BrushSubtract,
BrushPaint
}
[CanEditMultipleObjects]
[CustomEditor(typeof (Volume))]
public partial class VolumeEditor : Editor
{
private Volume voxelObject;
private Vector3 cursorPosition;
private SerializedProperty voxelSizeProperty;
private SerializedProperty overlapAmountProperty;
private SerializedProperty collisionMode;
private SerializedProperty meshingMode;
private SerializedProperty meshCompression;
private SerializedProperty colliderMeshingMode;
private SerializedProperty separateColliderMesh;
private SerializedProperty pivotProperty;
private SerializedProperty runtimeOnlyMesh;
private SerializedProperty material;
private SerializedProperty selfShadeInt;
private SerializedProperty physicMaterial;
private SerializedProperty castShadows;
private SerializedProperty receiveShadows;
private SerializedProperty chunkLayer;
private SerializedProperty collisionTrigger;
private float voxelSize;
private float overlapAmount;
private Vector3 pivot;
private bool drawGrid;
private bool drawMesh;
private Color newColor;
private PicaVoxelPoint boxStart;
private PicaVoxelBox currentBox;
private PicaVoxelPoint previousBrushPos = new PicaVoxelPoint(0,0,0);
private EditorPaintMode paintMode = EditorPaintMode.Color;
private bool propogateAllFrames = false;
private bool allFrames = true;
private PicaVoxelBox changedVoxelExtents;
private bool buttonJustClicked = false;
private void OnEnable()
{
voxelObject = (Volume) target;
if (voxelObject == null) return;
Undo.undoRedoPerformed += () => voxelObject.OnUndoRedo();
voxelSizeProperty = serializedObject.FindProperty("VoxelSize");
overlapAmountProperty = serializedObject.FindProperty("OverlapAmount");
collisionMode = serializedObject.FindProperty("CollisionMode");
meshingMode = serializedObject.FindProperty("MeshingMode");
meshCompression = serializedObject.FindProperty("MeshCompression");
colliderMeshingMode = serializedObject.FindProperty("MeshColliderMeshingMode");
separateColliderMesh = serializedObject.FindProperty("GenerateMeshColliderSeparately");
pivotProperty = serializedObject.FindProperty("Pivot");
voxelSize = voxelSizeProperty.floatValue;
overlapAmount = overlapAmountProperty.floatValue;
pivot = pivotProperty.vector3Value;
selfShadeInt = serializedObject.FindProperty("SelfShadingIntensity");
drawGrid = voxelObject.DrawGrid;
drawMesh = voxelObject.DrawMesh;
runtimeOnlyMesh = serializedObject.FindProperty("RuntimOnlyMesh");
material = serializedObject.FindProperty("Material");
physicMaterial = serializedObject.FindProperty("PhysicMaterial");
castShadows = serializedObject.FindProperty("CastShadows");
receiveShadows = serializedObject.FindProperty("ReceiveShadows");
chunkLayer = serializedObject.FindProperty("ChunkLayer");
collisionTrigger = serializedObject.FindProperty("CollisionTrigger");
if (voxelObject != null && !Application.isPlaying && PrefabUtility.GetPrefabType(voxelObject) != PrefabType.Prefab && PrefabUtility.GetPrefabType(voxelObject) != PrefabType.PrefabInstance)
{
string path = Path.Combine(Helper.GetMeshStorePath(), voxelObject.AssetGuid!=""?GuidEncoder.Encode(voxelObject.AssetGuid):"");
if (!Directory.Exists(path))
voxelObject.CreateChunks();
}
if (!Application.isPlaying && voxelObject.gameObject.activeSelf && PrefabUtility.GetPrefabType(voxelObject) != PrefabType.Prefab && !voxelObject.RuntimOnlyMesh)
foreach (var frame in voxelObject.Frames)
{
if (frame.HasDeserialized) frame.UpdateChunks(true);
frame.HasDeserialized = false;
}
paintMode = voxelObject.PaintMode;
if (EditorPersistence.SelectBox.BottomLeftFront.X > voxelObject.XSize - 1 ||
EditorPersistence.SelectBox.BottomLeftFront.Y > voxelObject.YSize - 1 ||
EditorPersistence.SelectBox.BottomLeftFront.Z > voxelObject.ZSize - 1 ||
EditorPersistence.SelectBox.TopRightBack.X > voxelObject.XSize - 1 ||
EditorPersistence.SelectBox.TopRightBack.Y > voxelObject.YSize - 1 ||
EditorPersistence.SelectBox.TopRightBack.Z > voxelObject.ZSize - 1)
EditorPersistence.SelectBox = new PicaVoxelBox(0, 0, 0, voxelObject.XSize - 1, voxelObject.YSize - 1, voxelObject.ZSize - 1);
}
private void OnSceneGUI()
{
voxelObject.GetCurrentFrame().UpdateTransformMatrix();
if (Selection.Contains(voxelObject.gameObject))
UnityEditor.Tools.hidden = voxelObject.IsEnabledForEditing;
else
UnityEditor.Tools.hidden = false;
buttonJustClicked = false;
if (voxelObject.transform.localScale != Vector3.one && !Application.isPlaying)
voxelObject.transform.localScale = Vector3.one;
foreach (Frame frame in voxelObject.Frames)
{
if (frame.transform.localScale != Vector3.one && !Application.isPlaying)
frame.transform.localScale = Vector3.one;
if (frame.transform.FindChild("Chunks").localScale != Vector3.one && !Application.isPlaying)
frame.transform.FindChild("Chunks").localScale = Vector3.one;
}
if (!voxelObject.IsEnabledForEditing || Selection.objects.Length > 1 || EditorUtility.Buttons.Count == 0)
return;
if (Event.current.type == EventType.Layout)
{
HandleUtility.AddDefaultControl(0);
}
Event e = Event.current;
GUILayout.BeginArea(new Rect(10, 10, 120, 500));
GUILayout.BeginVertical();
GUISkin skin = EditorGUIUtility.GetBuiltinSkin(EditorSkin.Scene);
paintMode =
(EditorPaintMode)
EditorGUILayout.EnumPopup(voxelObject.PaintMode,
new GUIStyle(skin.GetStyle("DropDown")) {margin = new RectOffset(0, 0, 0, 5)},
GUILayout.Width(100));
if (paintMode != voxelObject.PaintMode)
{
voxelObject.PaintMode = paintMode;
voxelObject.UpdateAllChunks();
}
EditorPersistence.SelectedValue =
(byte)
GUILayout.HorizontalSlider(EditorPersistence.SelectedValue, 0f, 255f,
new GUIStyle(GUI.skin.horizontalSlider) {margin = new RectOffset(0, 0, 0, 0)},
new GUIStyle(GUI.skin.horizontalSliderThumb) {margin = new RectOffset(0, 0, 0, 0)},
GUILayout.Width(100)); //.IntSlider((int)selectedValue, 0, 255, GUILayout.Width(120));
GUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Value:",
new GUIStyle(GUI.skin.label) {padding = new RectOffset(0, 0, 0, 0), margin = new RectOffset(0, 0, 0, 5)},
GUILayout.Width(42));
string inputSelectedValue = EditorGUILayout.TextField(EditorPersistence.SelectedValue.ToString(),
new GUIStyle(GUI.skin.textField)
{
padding = new RectOffset(2, 0, 2, 0),
margin = new RectOffset(0, 0, 0, 5),
fontStyle = FontStyle.Bold
}, GUILayout.Width(50), GUILayout.Height(20));
int trySelectedValue;
int.TryParse(inputSelectedValue, out trySelectedValue);
if (trySelectedValue >= 0 && trySelectedValue <= 255)
EditorPersistence.SelectedValue = (byte) trySelectedValue;
GUILayout.EndHorizontal();
int colNum = 0;
for (int y = 0; y < 5; y++)
{
GUILayout.BeginHorizontal();
for (int x = 0; x < 5; x++)
{
if (PaletteButton(voxelObject.PaletteColors[colNum], EditorPersistence.SelectedColor == colNum))
{
EditorPersistence.SelectedColor = colNum;
buttonJustClicked = true;
}
colNum++;
}
GUILayout.EndHorizontal();
}
newColor = EditorGUILayout.ColorField(voxelObject.PaletteColors[EditorPersistence.SelectedColor],
GUILayout.Width(90));
if (newColor != voxelObject.PaletteColors[EditorPersistence.SelectedColor])
voxelObject.PaletteColors[EditorPersistence.SelectedColor] = newColor;
GUILayout.EndVertical();
try
{
EditorCursorMode oldMode = EditorPersistence.CursorMode;
GUILayout.BeginHorizontal();
if (HighlightButton(EditorUtility.Buttons["pvButton_Add"],
EditorPersistence.CursorMode == EditorCursorMode.Add,
"Add single voxels"))
{
buttonJustClicked = true;
EditorPersistence.CursorMode = EditorCursorMode.Add;
}
if (HighlightButton(EditorUtility.Buttons["pvButton_Subtract"],
EditorPersistence.CursorMode == EditorCursorMode.Subtract, "Subtract single voxels"))
{
buttonJustClicked = true;
EditorPersistence.CursorMode = EditorCursorMode.Subtract;
}
if (HighlightButton(EditorUtility.Buttons["pvButton_Paint"],
EditorPersistence.CursorMode == EditorCursorMode.Paint, "Paint single voxels"))
{
buttonJustClicked = true;
EditorPersistence.CursorMode = EditorCursorMode.Paint;
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
if (HighlightButton(EditorUtility.Buttons["pvButton_BoxAdd"],
EditorPersistence.CursorMode == EditorCursorMode.BoxAdd, "Draw a box to add voxels"))
{
buttonJustClicked = true;
EditorPersistence.CursorMode = EditorCursorMode.BoxAdd;
}
if (HighlightButton(EditorUtility.Buttons["pvButton_BoxSubtract"],
EditorPersistence.CursorMode == EditorCursorMode.BoxSubtract, "Draw a box to subtract voxels"))
{
buttonJustClicked = true;
EditorPersistence.CursorMode = EditorCursorMode.BoxSubtract;
}
if (HighlightButton(EditorUtility.Buttons["pvButton_BoxPaint"],
EditorPersistence.CursorMode == EditorCursorMode.BoxPaint, "Draw a box to paint voxels"))
{
buttonJustClicked = true;
EditorPersistence.CursorMode = EditorCursorMode.BoxPaint;
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
if (HighlightButton(EditorUtility.Buttons["pvButton_Select"],
EditorPersistence.CursorMode == EditorCursorMode.Select, "Select voxels to create a brush"))
{
buttonJustClicked = true;
EditorPersistence.CursorMode = EditorCursorMode.Select;
}
if (HighlightButton(EditorUtility.Buttons["pvButton_PickColor"],
EditorPersistence.CursorMode == EditorCursorMode.PickColor,
"Change the currently selected color to the color of an existing voxel"))
{
buttonJustClicked = true;
EditorPersistence.CursorMode = EditorCursorMode.PickColor;
}
if (HighlightButton(EditorUtility.Buttons["pvButton_PickValue"],
EditorPersistence.CursorMode == EditorCursorMode.PickValue,
"Change the currently selected value to the value of an existing voxel"))
{
buttonJustClicked = true;
EditorPersistence.CursorMode = EditorCursorMode.PickValue;
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
if (HighlightButton(EditorUtility.Buttons["pvButton_BrushAdd"],
EditorPersistence.CursorMode == EditorCursorMode.BrushAdd,
"Add voxels using stored brush"))
{
buttonJustClicked = true;
EditorPersistence.CursorMode = EditorCursorMode.BrushAdd;
}
if (HighlightButton(EditorUtility.Buttons["pvButton_BrushSubtract"],
EditorPersistence.CursorMode == EditorCursorMode.BrushSubtract, "Subtract voxels using stored brush"))
{
buttonJustClicked = true;
EditorPersistence.CursorMode = EditorCursorMode.BrushSubtract;
}
if (HighlightButton(EditorUtility.Buttons["pvButton_BrushPaint"],
EditorPersistence.CursorMode == EditorCursorMode.BrushPaint, "Paint voxels using stored brush"))
{
buttonJustClicked = true;
EditorPersistence.CursorMode = EditorCursorMode.BrushPaint;
}
GUILayout.EndHorizontal();
if (EditorPersistence.CursorMode == EditorCursorMode.Select)
{
GUILayout.Space(10);
EditorGUILayout.LabelField("Selection");
if (GUILayout.Button("Create Brush", new GUIStyle(skin.button) {margin = new RectOffset(0, 0, 0, 5)},
GUILayout.Width(100)))
{
buttonJustClicked = true;
EditorPersistence.Brush =
new Voxel[((EditorPersistence.SelectBox.TopRightBack.X + 1) - EditorPersistence.SelectBox.BottomLeftFront.X) *
((EditorPersistence.SelectBox.TopRightBack.Y + 1) - EditorPersistence.SelectBox.BottomLeftFront.Y) *
((EditorPersistence.SelectBox.TopRightBack.Z + 1) - EditorPersistence.SelectBox.BottomLeftFront.Z)];
EditorPersistence.BrushSize = new PicaVoxelPoint((EditorPersistence.SelectBox.TopRightBack.X + 1) - EditorPersistence.SelectBox.BottomLeftFront.X,
(EditorPersistence.SelectBox.TopRightBack.Y + 1) - EditorPersistence.SelectBox.BottomLeftFront.Y,
(EditorPersistence.SelectBox.TopRightBack.Z + 1) - EditorPersistence.SelectBox.BottomLeftFront.Z);
PicaVoxelBox destBox = new PicaVoxelBox(0, 0, 0, EditorPersistence.BrushSize.X-1,EditorPersistence.BrushSize.Y-1, EditorPersistence.BrushSize.Z-1);
Helper.CopyVoxelsInBox(ref voxelObject.GetCurrentFrame().Voxels, ref EditorPersistence.Brush,
EditorPersistence.SelectBox, destBox, new PicaVoxelPoint(voxelObject.XSize, voxelObject.YSize, voxelObject.ZSize), EditorPersistence.BrushSize, false);
EditorPersistence.CursorMode = EditorCursorMode.BrushAdd;
voxelObject.GetCurrentFrame().EditingVoxels =
new Voxel[voxelObject.XSize * voxelObject.YSize * voxelObject.ZSize];
Helper.CopyVoxelsInBox(ref voxelObject.GetCurrentFrame().Voxels,
ref voxelObject.GetCurrentFrame().EditingVoxels,
new PicaVoxelPoint(voxelObject.XSize, voxelObject.YSize, voxelObject.ZSize),
new PicaVoxelPoint(voxelObject.XSize, voxelObject.YSize, voxelObject.ZSize), false);
}
GUILayout.Space(10);
GUILayout.BeginHorizontal();
if (GUILayout.Button("Fill", new GUIStyle(skin.button) {margin = new RectOffset(0, 0, 0, 0)},
GUILayout.Width(50)))
{
RegisterUndo();
if (propogateAllFrames)
{
foreach (Frame f in voxelObject.Frames)
{
f.EditingVoxels = null;
for(int x=EditorPersistence.SelectBox.BottomLeftFront.X;x<=EditorPersistence.SelectBox.TopRightBack.X;x++)
for(int y=EditorPersistence.SelectBox.BottomLeftFront.Y;y<=EditorPersistence.SelectBox.TopRightBack.Y;y++)
for(int z=EditorPersistence.SelectBox.BottomLeftFront.Z;z<=EditorPersistence.SelectBox.TopRightBack.Z;z++)
f.Voxels[x + voxelObject.XSize*(y + voxelObject.YSize*z)] = new Voxel()
{
State = VoxelState.Active,
Color = voxelObject.PaletteColors[EditorPersistence.SelectedColor],
Value = EditorPersistence.SelectedValue
};
f.SaveForSerialize();
f.UpdateAllChunks();
}
}
else
{
voxelObject.Frames[voxelObject.CurrentFrame].EditingVoxels = null;
for (int x = EditorPersistence.SelectBox.BottomLeftFront.X; x <= EditorPersistence.SelectBox.TopRightBack.X; x++)
for (int y = EditorPersistence.SelectBox.BottomLeftFront.Y; y <= EditorPersistence.SelectBox.TopRightBack.Y; y++)
for (int z = EditorPersistence.SelectBox.BottomLeftFront.Z; z <= EditorPersistence.SelectBox.TopRightBack.Z; z++)
voxelObject.Frames[voxelObject.CurrentFrame].Voxels[x + voxelObject.XSize * (y + voxelObject.YSize * z)] = new Voxel()
{
State = VoxelState.Active,
Color = voxelObject.PaletteColors[EditorPersistence.SelectedColor],
Value = EditorPersistence.SelectedValue
};
voxelObject.Frames[voxelObject.CurrentFrame].SaveForSerialize();
voxelObject.Frames[voxelObject.CurrentFrame].UpdateAllChunks();
}
buttonJustClicked = true;
}
if (GUILayout.Button("Clear", new GUIStyle(skin.button) { margin = new RectOffset(0, 0, 0, 0) },
GUILayout.Width(50)))
{
RegisterUndo();
if (propogateAllFrames)
{
foreach (Frame f in voxelObject.Frames)
{
f.EditingVoxels = null;
for (int x = EditorPersistence.SelectBox.BottomLeftFront.X; x <= EditorPersistence.SelectBox.TopRightBack.X; x++)
for (int y = EditorPersistence.SelectBox.BottomLeftFront.Y; y <= EditorPersistence.SelectBox.TopRightBack.Y; y++)
for (int z = EditorPersistence.SelectBox.BottomLeftFront.Z; z <= EditorPersistence.SelectBox.TopRightBack.Z; z++)
f.Voxels[x + voxelObject.XSize * (y + voxelObject.YSize * z)] = new Voxel()
{
State = VoxelState.Inactive,
Color = voxelObject.PaletteColors[EditorPersistence.SelectedColor],
Value = EditorPersistence.SelectedValue
};
f.SaveForSerialize();
f.UpdateAllChunks();
}
}
else
{
voxelObject.Frames[voxelObject.CurrentFrame].EditingVoxels = null;
for (int x = EditorPersistence.SelectBox.BottomLeftFront.X; x <= EditorPersistence.SelectBox.TopRightBack.X; x++)
for (int y = EditorPersistence.SelectBox.BottomLeftFront.Y; y <= EditorPersistence.SelectBox.TopRightBack.Y; y++)
for (int z = EditorPersistence.SelectBox.BottomLeftFront.Z; z <= EditorPersistence.SelectBox.TopRightBack.Z; z++)
voxelObject.Frames[voxelObject.CurrentFrame].Voxels[x + voxelObject.XSize * (y + voxelObject.YSize * z)] = new Voxel()
{
State = VoxelState.Inactive,
Color = voxelObject.PaletteColors[EditorPersistence.SelectedColor],
Value = EditorPersistence.SelectedValue
};
voxelObject.Frames[voxelObject.CurrentFrame].SaveForSerialize();
voxelObject.Frames[voxelObject.CurrentFrame].UpdateAllChunks();
}
buttonJustClicked = true;
}
GUILayout.EndHorizontal();
EditorGUILayout.LabelField("Nudge Selection");
GUILayout.BeginHorizontal();
if (GUILayout.Button("X - 1", new GUIStyle(skin.button) { margin = new RectOffset(0, 0, 0, 0) },
GUILayout.Width(50)))
{
NudgeSelection(-1, 0, 0);
buttonJustClicked = true;
}
if (GUILayout.Button("X + 1", new GUIStyle(skin.button) { margin = new RectOffset(0, 0, 0, 0) },
GUILayout.Width(50)))
{
NudgeSelection(1, 0, 0);
buttonJustClicked = true;
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
if (GUILayout.Button("Y - 1", new GUIStyle(skin.button) { margin = new RectOffset(0, 0, 0, 0) },
GUILayout.Width(50)))
{
NudgeSelection(0, -1, 0);
buttonJustClicked = true;
}
if (GUILayout.Button("Y + 1", new GUIStyle(skin.button) { margin = new RectOffset(0, 0, 0, 0) },
GUILayout.Width(50)))
{
NudgeSelection(0, 1, 0);
buttonJustClicked = true;
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
if (GUILayout.Button("Z - 1", new GUIStyle(skin.button) { margin = new RectOffset(0, 0, 0, 0) },
GUILayout.Width(50)))
{
NudgeSelection(0, 0, -1);
buttonJustClicked = true;
}
if (GUILayout.Button("Z + 1", new GUIStyle(skin.button) { margin = new RectOffset(0, 0, 0, 0) },
GUILayout.Width(50)))
{
NudgeSelection(0, 0, 1);
buttonJustClicked = true;
}
GUILayout.EndHorizontal();
}
if (EditorPersistence.CursorMode == EditorCursorMode.BrushAdd ||
EditorPersistence.CursorMode == EditorCursorMode.BrushSubtract ||
EditorPersistence.CursorMode == EditorCursorMode.BrushPaint)
{
if (EditorPersistence.Brush == null) EditorPersistence.CursorMode = EditorCursorMode.Select;
GUILayout.Space(10);
EditorPersistence.BrushReplace = EditorGUILayout.ToggleLeft(" Replace", EditorPersistence.BrushReplace);
GUILayout.Space(10);
EditorGUILayout.LabelField("Brush Anchor");
EditorPersistence.BrushAnchorX =
(AnchorX)
EditorGUILayout.EnumPopup(EditorPersistence.BrushAnchorX,
new GUIStyle(skin.GetStyle("DropDown")) {margin = new RectOffset(0, 0, 0, 5)},
GUILayout.Width(100));
EditorPersistence.BrushAnchorY =
(AnchorY)
EditorGUILayout.EnumPopup(EditorPersistence.BrushAnchorY,
new GUIStyle(skin.GetStyle("DropDown")) {margin = new RectOffset(0, 0, 0, 5)},
GUILayout.Width(100));
EditorPersistence.BrushAnchorZ =
(AnchorZ)
EditorGUILayout.EnumPopup(EditorPersistence.BrushAnchorZ,
new GUIStyle(skin.GetStyle("DropDown")) {margin = new RectOffset(0, 0, 0, 5)},
GUILayout.Width(100));
EditorGUILayout.LabelField("Brush Rotate");
GUILayout.BeginHorizontal();
if (GUILayout.Button(new GUIContent("X", "Rotate brush 90 degrees around X axis"),
new GUIStyle(GUI.skin.button)
{
padding = new RectOffset(0, 0, 0, 0),
margin = new RectOffset(0, 2, 2, 0)
}, GUILayout.Width(32), GUILayout.Height(32)))
{
buttonJustClicked = true;
RotateBrush(RotateAxis.X);
voxelObject.UpdateAllChunks();
}
if (GUILayout.Button(new GUIContent("Y", "Rotate brush 90 degrees around Y axis"),
new GUIStyle(GUI.skin.button)
{
padding = new RectOffset(0, 0, 0, 0),
margin = new RectOffset(0, 2, 2, 0)
}, GUILayout.Width(32), GUILayout.Height(32)))
{
buttonJustClicked = true;
RotateBrush(RotateAxis.Y);
voxelObject.UpdateAllChunks();
}
if (GUILayout.Button(new GUIContent("Z", "Rotate brush 90 degrees around Z axis"),
new GUIStyle(GUI.skin.button)
{
padding = new RectOffset(0, 0, 0, 0),
margin = new RectOffset(0, 2, 2, 0)
}, GUILayout.Width(32), GUILayout.Height(32)))
{
buttonJustClicked = true;
RotateBrush(RotateAxis.Z);
voxelObject.UpdateAllChunks();
}
GUILayout.EndHorizontal();
}
if ((oldMode == EditorCursorMode.BrushAdd || oldMode == EditorCursorMode.BrushPaint ||
oldMode == EditorCursorMode.BrushSubtract) && oldMode != EditorPersistence.CursorMode)
{
voxelObject.GetCurrentFrame().EditingVoxels = null;
voxelObject.UpdateAllChunks();
}
}
catch (Exception)
{
}
GUILayout.EndVertical();
GUILayout.EndArea();
// Animation
GUI.backgroundColor = (EditorGUIUtility.isProSkin ? Color.white : Color.grey);
GUILayout.BeginArea(new Rect(Screen.width - 410, Screen.height - 100, 400, 50),
EditorUtility.Buttons["pvButton_AnimBG"]);
GUILayout.BeginHorizontal(new GUIStyle() {alignment = TextAnchor.MiddleCenter}, GUILayout.Width(260));
if (GUILayout.Button(new GUIContent(EditorUtility.Buttons["pvButton_AddFramePrev"], "Add frame before"),
new GUIStyle(GUI.skin.button)
{
padding = new RectOffset(0, 0, 0, 0),
margin = new RectOffset(3, 0, 9, 0)
}, GUILayout.Width(32), GUILayout.Height(32)))
{
buttonJustClicked = true;
voxelObject.AddFrame(voxelObject.CurrentFrame);
}
if (GUILayout.Button(new GUIContent(EditorUtility.Buttons["pvButton_DeleteFrame"], "Delete frame"),
new GUIStyle(GUI.skin.button)
{
padding = new RectOffset(0, 0, 0, 0),
margin = new RectOffset(2, 0, 9, 0)
}, GUILayout.Width(32), GUILayout.Height(32)))
{
buttonJustClicked = true;
voxelObject.DeleteFrame();
}
if (GUILayout.Button(new GUIContent(EditorUtility.Buttons["pvButton_MoveLeft"], "Move frame left"),
new GUIStyle(GUI.skin.button)
{
padding = new RectOffset(0, 0, 0, 0),
margin = new RectOffset(2, 0, 9, 0)
}, GUILayout.Width(32), GUILayout.Height(32)))
{
buttonJustClicked = true;
voxelObject.MoveFrameLeft();
}
if (GUILayout.Button(new GUIContent(EditorUtility.Buttons["pvButton_PrevFrame"], "Previous frame"),
new GUIStyle(GUI.skin.button)
{
padding = new RectOffset(0, 0, 0, 0),
margin = new RectOffset(2, 0, 9, 0)
}, GUILayout.Width(32), GUILayout.Height(32)))
{
buttonJustClicked = true;
voxelObject.PrevFrame();
}
GUILayout.Label(voxelObject.CurrentFrame + 1 + "/" + voxelObject.NumFrames,
new GUIStyle(GUI.skin.label)
{
normal = new GUIStyleState() {textColor = Color.white},
fontSize = 20,
fontStyle = FontStyle.Bold,
alignment = TextAnchor.MiddleCenter,
padding = new RectOffset(2, 0, 0, 3)
}, GUILayout.Width(80), GUILayout.Height(50));
if (GUILayout.Button(new GUIContent(EditorUtility.Buttons["pvButton_NextFrame"], "Next frame"),
new GUIStyle(GUI.skin.button)
{
padding = new RectOffset(0, 0, 0, 0),
margin = new RectOffset(2, 0, 9, 0)
}, GUILayout.Width(32), GUILayout.Height(32)))
{
buttonJustClicked = true;
voxelObject.NextFrame();
}
if (GUILayout.Button(new GUIContent(EditorUtility.Buttons["pvButton_MoveRight"], "Move frame right"),
new GUIStyle(GUI.skin.button)
{
padding = new RectOffset(0, 0, 0, 0),
margin = new RectOffset(2, 0, 9, 0)
}, GUILayout.Width(32), GUILayout.Height(32)))
{
buttonJustClicked = true;
voxelObject.MoveFrameRight();
}
if (GUILayout.Button(new GUIContent(EditorUtility.Buttons["pvButton_AddFrameNext"], "Add frame after"),
new GUIStyle(GUI.skin.button)
{
padding = new RectOffset(0, 0, 0, 0),
margin = new RectOffset(2, 0, 9, 0)
}, GUILayout.Width(32), GUILayout.Height(32)))
{
buttonJustClicked = true;
voxelObject.AddFrame(voxelObject.CurrentFrame + 1);
}
if (GUILayout.Button(new GUIContent(EditorUtility.Buttons["pvButton_CopyFrame"], "Copy frame to clipboard"),
new GUIStyle(GUI.skin.button)
{
padding = new RectOffset(0, 0, 0, 0),
margin = new RectOffset(2, 0, 9, 0)
}, GUILayout.Width(32), GUILayout.Height(32)))
{
buttonJustClicked = true;
EditorPersistence.AnimFrameClipboard = new Voxel[voxelObject.XSize * voxelObject.YSize * voxelObject.ZSize];
Array.Copy(voxelObject.Frames[voxelObject.CurrentFrame].Voxels, EditorPersistence.AnimFrameClipboard,
voxelObject.Frames[voxelObject.CurrentFrame].Voxels.Length);
EditorPersistence.AnimFrameClipboardSize = new PicaVoxelPoint(voxelObject.XSize, voxelObject.YSize, voxelObject.ZSize);
}
if (
GUILayout.Button(
new GUIContent(EditorUtility.Buttons["pvButton_PasteFrame"], "Paste frame from clipboard"),
new GUIStyle(GUI.skin.button)
{
padding = new RectOffset(0, 0, 0, 0),
margin = new RectOffset(2, 0, 9, 0)
}, GUILayout.Width(32), GUILayout.Height(32)) && EditorPersistence.AnimFrameClipboard!=null)
{
buttonJustClicked = true;
voxelObject.AddFrame(voxelObject.CurrentFrame);
voxelObject.SetFrame(voxelObject.CurrentFrame+1);
for(int x=0;x<EditorPersistence.AnimFrameClipboardSize.X;x++)
for(int y=0;y<EditorPersistence.AnimFrameClipboardSize.Y;y++)
for (int z = 0; z < EditorPersistence.AnimFrameClipboardSize.Z; z++)
{
if (x < voxelObject.XSize && y < voxelObject.YSize && z < voxelObject.ZSize)
{
voxelObject.Frames[voxelObject.CurrentFrame].Voxels[x + voxelObject.XSize*(y + voxelObject.YSize*z)] =
EditorPersistence.AnimFrameClipboard[x + EditorPersistence.AnimFrameClipboardSize.X * (y + EditorPersistence.AnimFrameClipboardSize.Y * z)];
}
}
voxelObject.SaveForSerialize();
voxelObject.UpdateAllChunks();
}
GUILayout.EndArea();
GUI.backgroundColor = Color.white;
Handles.SetCamera(Camera.current);
DoCursor(e);
//HandleUtility.Repaint();
}
private void OnDrawGizmos()
{
voxelObject.OnDrawGizmos();
}
private void DoCursor(Event e)
{
//if (buttonJustClicked && e.type == EventType.MouseUp && e.button == 0)
// buttonJustClicked = false;
if (GUIUtility.hotControl == 0 && EditorGUIUtility.hotControl==0 && GUI.GetNameOfFocusedControl() == "" && buttonJustClicked==false)
{
bool validBrushPos = false;
if (voxelObject.Undone)
{
//voxelObject.CreateChunks();
voxelObject.Undone = false;
}
foreach (var frame in voxelObject.Frames)
{
if (frame.HasDeserialized) frame.CreateChunks();
frame.HasDeserialized = false;
}
Ray r =
Camera.current.ScreenPointToRay(new Vector3(e.mousePosition.x,
-e.mousePosition.y + Camera.current.pixelHeight));
//Debug.DrawRay(r.origin, r.direction*200f, Color.red );
for (float d = 0f; d < voxelObject.VoxelSize*200f; d += voxelObject.VoxelSize*0.1f)
{
Voxel? v = voxelObject.GetVoxelAtWorldPosition(r.GetPoint(d));
if ((EditorPersistence.CursorMode == EditorCursorMode.Add ||
EditorPersistence.CursorMode == EditorCursorMode.BoxAdd ||
(EditorPersistence.CursorMode == EditorCursorMode.BrushAdd && !EditorPersistence.BrushReplace)) &&
v.HasValue)
{
if (!v.Value.Active) continue;
d -= voxelObject.VoxelSize*0.1f;
v = voxelObject.GetVoxelAtWorldPosition(r.GetPoint(d));
if (!v.HasValue) break;
}
if (v.HasValue &&
(v.Value.Active || EditorPersistence.CursorMode == EditorCursorMode.Add ||
EditorPersistence.CursorMode == EditorCursorMode.BoxAdd ||
(EditorPersistence.CursorMode == EditorCursorMode.BrushAdd && !EditorPersistence.BrushReplace)))
{
cursorPosition =
voxelObject.transform.TransformPoint(((voxelObject.GetVoxelPosition(r.GetPoint(d))*
voxelObject.VoxelSize) - voxelObject.Pivot) +
((Vector3.one*voxelObject.VoxelSize)*0.5f));
if (EditorPersistence.CursorMode != EditorCursorMode.BrushAdd &&
EditorPersistence.CursorMode != EditorCursorMode.BrushPaint &&
EditorPersistence.CursorMode != EditorCursorMode.BrushSubtract)
{
Gizmos.matrix =
voxelObject.GetCurrentFrame().transform.FindChild("Chunks").localToWorldMatrix;
Handles.color = (EditorPersistence.CursorMode == EditorCursorMode.Subtract ||
EditorPersistence.CursorMode == EditorCursorMode.BoxSubtract ||
EditorPersistence.CursorMode == EditorCursorMode.PickColor ||
EditorPersistence.CursorMode == EditorCursorMode.PickValue)
? Color.red*0.5f
: voxelObject.PaletteColors[EditorPersistence.SelectedColor]*0.5f;
Handles.CubeCap(0, cursorPosition, voxelObject.transform.rotation, voxelObject.VoxelSize);
Repaint();
SceneView.RepaintAll();
Gizmos.matrix = Matrix4x4.identity;
}
// Mouse down (non-brush)
if (e.type == EventType.MouseDown && e.button == 0 &&
(EditorPersistence.CursorMode != EditorCursorMode.BrushAdd &&
EditorPersistence.CursorMode != EditorCursorMode.BrushPaint &&
EditorPersistence.CursorMode != EditorCursorMode.BrushSubtract))
{
//RegisterUndo();
if (EditorPersistence.CursorMode != EditorCursorMode.PickColor &&
EditorPersistence.CursorMode != EditorCursorMode.PickValue)
{
if (propogateAllFrames)
{
foreach (Frame f in voxelObject.Frames)
{
f.EditingVoxels =
new Voxel[voxelObject.XSize*voxelObject.YSize*voxelObject.ZSize];
Helper.CopyVoxelsInBox(ref f.Voxels,
ref f.EditingVoxels,
new PicaVoxelPoint(voxelObject.XSize, voxelObject.YSize, voxelObject.ZSize),
new PicaVoxelPoint(voxelObject.XSize, voxelObject.YSize, voxelObject.ZSize),
false);
}
}
else
{
voxelObject.GetCurrentFrame().EditingVoxels =
new Voxel[voxelObject.XSize*voxelObject.YSize*voxelObject.ZSize];
Helper.CopyVoxelsInBox(ref voxelObject.GetCurrentFrame().Voxels,
ref voxelObject.GetCurrentFrame().EditingVoxels,
new PicaVoxelPoint(voxelObject.XSize, voxelObject.YSize, voxelObject.ZSize),
new PicaVoxelPoint(voxelObject.XSize, voxelObject.YSize, voxelObject.ZSize),
false);
}
}
if (EditorPersistence.CursorMode == EditorCursorMode.BoxAdd ||
EditorPersistence.CursorMode == EditorCursorMode.BoxSubtract ||
EditorPersistence.CursorMode == EditorCursorMode.BoxPaint)
{
boxStart = voxelObject.GetVoxelArrayPosition(r.GetPoint(d));
currentBox = new PicaVoxelBox(boxStart, boxStart);
changedVoxelExtents = new PicaVoxelBox(boxStart, boxStart);
}
if (EditorPersistence.CursorMode == EditorCursorMode.Add ||
EditorPersistence.CursorMode == EditorCursorMode.Subtract ||
EditorPersistence.CursorMode == EditorCursorMode.Paint)
{
changedVoxelExtents = new PicaVoxelBox(voxelObject.GetVoxelArrayPosition(r.GetPoint(d)), voxelObject.GetVoxelArrayPosition(r.GetPoint(d)));
}
if (EditorPersistence.CursorMode == EditorCursorMode.PickColor)
{
bool colorFound = false;
for (int c = 0; c < 25; c++)
if (((Color32) voxelObject.PaletteColors[c]).r == ((Color32) v.Value.Color).r &&
((Color32) voxelObject.PaletteColors[c]).g == ((Color32) v.Value.Color).g &&
((Color32) voxelObject.PaletteColors[c]).b == ((Color32) v.Value.Color).b)
{
EditorPersistence.SelectedColor = c;
colorFound = true;
}
if (!colorFound)
voxelObject.PaletteColors[EditorPersistence.SelectedColor] = v.Value.Color;
}
if (EditorPersistence.CursorMode == EditorCursorMode.PickValue)
{
EditorPersistence.SelectedValue = v.Value.Value;
}
}
// Mouse drag (non-brush)
if ((e.type == EventType.MouseDown || e.type == EventType.MouseDrag) && e.button == 0)
{
if (e.type == EventType.MouseDrag &&
(EditorPersistence.CursorMode == EditorCursorMode.BoxAdd ||
EditorPersistence.CursorMode == EditorCursorMode.BoxSubtract ||
EditorPersistence.CursorMode == EditorCursorMode.BoxPaint) && boxStart != null)
{
if (propogateAllFrames)
{
foreach (Frame f in voxelObject.Frames)
{
Helper.CopyVoxelsInBox(ref f.Voxels,
ref f.EditingVoxels, currentBox, currentBox,
new PicaVoxelPoint(voxelObject.XSize, voxelObject.YSize, voxelObject.ZSize),
new PicaVoxelPoint(voxelObject.XSize, voxelObject.YSize, voxelObject.ZSize),
false);
}
}
else
{
Helper.CopyVoxelsInBox(ref voxelObject.GetCurrentFrame().Voxels,
ref voxelObject.GetCurrentFrame().EditingVoxels, currentBox, currentBox,
new PicaVoxelPoint(voxelObject.XSize, voxelObject.YSize, voxelObject.ZSize),
new PicaVoxelPoint(voxelObject.XSize, voxelObject.YSize, voxelObject.ZSize),
false);
}
//voxelObject.UpdateAllChunks();
PicaVoxelPoint boxEnd = voxelObject.GetVoxelArrayPosition(r.GetPoint(d));
currentBox = new PicaVoxelBox(boxStart, boxEnd);
if (currentBox.BottomLeftFront.X < changedVoxelExtents.BottomLeftFront.X)
changedVoxelExtents.BottomLeftFront.X = currentBox.BottomLeftFront.X;
if (currentBox.BottomLeftFront.Y < changedVoxelExtents.BottomLeftFront.Y)
changedVoxelExtents.BottomLeftFront.Y = currentBox.BottomLeftFront.Y;
if (currentBox.BottomLeftFront.Z < changedVoxelExtents.BottomLeftFront.Z)
changedVoxelExtents.BottomLeftFront.Z = currentBox.BottomLeftFront.Z;
if (currentBox.TopRightBack.X > changedVoxelExtents.TopRightBack.X)
changedVoxelExtents.TopRightBack.X = currentBox.TopRightBack.X;
if (currentBox.TopRightBack.Y > changedVoxelExtents.TopRightBack.Y)
changedVoxelExtents.TopRightBack.Y = currentBox.TopRightBack.Y;
if (currentBox.TopRightBack.Z > changedVoxelExtents.TopRightBack.Z)
changedVoxelExtents.TopRightBack.Z = currentBox.TopRightBack.Z;
for (int x = currentBox.BottomLeftFront.X; x <= currentBox.TopRightBack.X; x++)
for (int y = currentBox.BottomLeftFront.Y; y <= currentBox.TopRightBack.Y; y++)
for (int z = currentBox.BottomLeftFront.Z; z <= currentBox.TopRightBack.Z; z++)
{
if (propogateAllFrames)
{
foreach (Frame f in voxelObject.Frames)
{
switch (EditorPersistence.CursorMode)
{
case EditorCursorMode.BoxAdd:
if (
!f.Voxels[
x + voxelObject.XSize*(y + voxelObject.YSize*z)]
.Active)
{
f.EditingVoxels[
x + voxelObject.XSize*(y + voxelObject.YSize*z)] = new Voxel
()
{
Color =
voxelObject.PaletteColors[
EditorPersistence.SelectedColor],
Value = EditorPersistence.SelectedValue,
State =
(EditorPersistence.CursorMode !=
EditorCursorMode.BoxSubtract)?VoxelState.Active:VoxelState.Inactive
};
}
break;
case EditorCursorMode.BoxSubtract:
f.EditingVoxels[
x + voxelObject.XSize*(y + voxelObject.YSize*z)] = new Voxel
()
{
Color =
voxelObject.PaletteColors[
EditorPersistence.SelectedColor],
Value = EditorPersistence.SelectedValue,
State =
(EditorPersistence.CursorMode !=
EditorCursorMode.BoxSubtract) ? VoxelState.Active : VoxelState.Inactive
};
break;
case EditorCursorMode.BoxPaint:
if (
f.Voxels[x + voxelObject.XSize*(y + voxelObject.YSize*z)
].Active)
{
switch (paintMode)
{
case EditorPaintMode.Color:
f.EditingVoxels[
x +
voxelObject.XSize*(y + voxelObject.YSize*z)]
= new Voxel()
{
Color =
voxelObject.PaletteColors[
EditorPersistence.SelectedColor],
Value =
f.Voxels[
x +
voxelObject.XSize*
(y + voxelObject.YSize*z)].Value,
State =
f.Voxels[
x +
voxelObject.XSize*
(y + voxelObject.YSize*z)]
.State
};
//GetCurrentFrame().EditingVoxels[x, y, z].Color = voxelObject.PaletteColors[selectedColor];
break;
case EditorPaintMode.Value:
f.EditingVoxels[
x +
voxelObject.XSize*(y + voxelObject.YSize*z)]
= new Voxel()
{
Color =
f.Voxels[
x +
voxelObject.XSize*
(y + voxelObject.YSize*z)].Color,
Value = EditorPersistence.SelectedValue,
State =
f.Voxels[
x +
voxelObject.XSize*
(y + voxelObject.YSize*z)]
.State
};
//voxelObject.GetCurrentFrame().EditingVoxels[x, y, z].Value = selectedValue;
break;
}
}
break;
}
}
}
else
{
switch (EditorPersistence.CursorMode)
{
case EditorCursorMode.BoxAdd:
if (
!voxelObject.GetCurrentFrame().Voxels[
x + voxelObject.XSize*(y + voxelObject.YSize*z)].Active)
{
voxelObject.GetCurrentFrame().EditingVoxels[
x + voxelObject.XSize*(y + voxelObject.YSize*z)] = new Voxel
()
{
Color =
voxelObject.PaletteColors[
EditorPersistence.SelectedColor],
Value = EditorPersistence.SelectedValue,
State =
(EditorPersistence.CursorMode !=
EditorCursorMode.BoxSubtract) ? VoxelState.Active : VoxelState.Inactive
};
}
break;
case EditorCursorMode.BoxSubtract:
voxelObject.GetCurrentFrame().EditingVoxels[
x + voxelObject.XSize*(y + voxelObject.YSize*z)] = new Voxel
()
{
Color =
voxelObject.PaletteColors[
EditorPersistence.SelectedColor],
Value = EditorPersistence.SelectedValue,
State =
(EditorPersistence.CursorMode !=
EditorCursorMode.BoxSubtract) ? VoxelState.Active : VoxelState.Inactive
};
break;
case EditorCursorMode.BoxPaint:
if (
voxelObject.GetCurrentFrame().Voxels[
x + voxelObject.XSize*(y + voxelObject.YSize*z)].Active)
{
switch (paintMode)
{
case EditorPaintMode.Color:
voxelObject.GetCurrentFrame().EditingVoxels[
x + voxelObject.XSize*(y + voxelObject.YSize*z)]
= new Voxel()
{
Color =
voxelObject.PaletteColors[
EditorPersistence.SelectedColor],
Value =
voxelObject.GetCurrentFrame().Voxels[
x +
voxelObject.XSize*
(y + voxelObject.YSize*z)].Value,
State =
voxelObject.GetCurrentFrame().Voxels[
x +
voxelObject.XSize*
(y + voxelObject.YSize*z)].State
};
//GetCurrentFrame().EditingVoxels[x, y, z].Color = voxelObject.PaletteColors[selectedColor];
break;
case EditorPaintMode.Value:
voxelObject.GetCurrentFrame().EditingVoxels[
x + voxelObject.XSize*(y + voxelObject.YSize*z)]
= new Voxel()
{
Color =
voxelObject.GetCurrentFrame().Voxels[
x +
voxelObject.XSize*
(y + voxelObject.YSize*z)].Color,
Value = EditorPersistence.SelectedValue,
State =
voxelObject.GetCurrentFrame().Voxels[
x +
voxelObject.XSize*
(y + voxelObject.YSize*z)].State
};
//voxelObject.GetCurrentFrame().EditingVoxels[x, y, z].Value = selectedValue;
break;
}
}
break;
}
}
}
for (int x = changedVoxelExtents.BottomLeftFront.X-1;
x <= changedVoxelExtents.TopRightBack.X+1;
x ++)
for (int y = changedVoxelExtents.BottomLeftFront.Y-1;
y <= changedVoxelExtents.TopRightBack.Y+1;
y ++)
for (int z = changedVoxelExtents.BottomLeftFront.Z-1;
z <= changedVoxelExtents.TopRightBack.Z+1;
z ++)
if (propogateAllFrames)
foreach (Frame f in voxelObject.Frames)
f.SetChunkAtVoxelPositionDirty(x, y, z);
else voxelObject.GetCurrentFrame().SetChunkAtVoxelPositionDirty(x, y, z);
if (propogateAllFrames)
foreach (Frame f in voxelObject.Frames) f.UpdateChunks(true);
else voxelObject.UpdateChunks(true);
}
if (EditorPersistence.CursorMode == EditorCursorMode.Add ||
EditorPersistence.CursorMode == EditorCursorMode.Subtract ||
EditorPersistence.CursorMode == EditorCursorMode.Paint)
{
PicaVoxelPoint pv = voxelObject.GetVoxelArrayPosition(r.GetPoint(d));
if (pv.X < changedVoxelExtents.BottomLeftFront.X)
changedVoxelExtents.BottomLeftFront.X = pv.X;
if (pv.Y < changedVoxelExtents.BottomLeftFront.Y)
changedVoxelExtents.BottomLeftFront.Y = pv.Y;
if (pv.Z < changedVoxelExtents.BottomLeftFront.Z)
changedVoxelExtents.BottomLeftFront.Z = pv.Z;
if (pv.X > changedVoxelExtents.TopRightBack.X)
changedVoxelExtents.TopRightBack.X = pv.X;
if (pv.Y > changedVoxelExtents.TopRightBack.Y)
changedVoxelExtents.TopRightBack.Y = pv.Y;
if (pv.Z > changedVoxelExtents.TopRightBack.Z)
changedVoxelExtents.TopRightBack.Z = pv.Z;
if (propogateAllFrames)
{
foreach (Frame f in voxelObject.Frames)
{
f.SetVoxelAtWorldPosition(r.GetPoint(d),
new Voxel()
{
State = (EditorPersistence.CursorMode != EditorCursorMode.Subtract)?VoxelState.Active : VoxelState.Inactive,
Color = voxelObject.PaletteColors[EditorPersistence.SelectedColor],
Value = EditorPersistence.SelectedValue
});
f.UpdateChunks(true);
}
}
else
{
voxelObject.SetVoxelAtWorldPosition(r.GetPoint(d),
new Voxel()
{
State = (EditorPersistence.CursorMode != EditorCursorMode.Subtract) ? VoxelState.Active : VoxelState.Inactive,
Color = voxelObject.PaletteColors[EditorPersistence.SelectedColor],
Value = EditorPersistence.SelectedValue
});
voxelObject.UpdateChunks(true);
}
}
}
// Mouse move (brush update)
if ((EditorPersistence.CursorMode == EditorCursorMode.BrushAdd ||
EditorPersistence.CursorMode == EditorCursorMode.BrushPaint ||
EditorPersistence.CursorMode == EditorCursorMode.BrushSubtract))
{
validBrushPos = true;
if (e.type == EventType.MouseMove)
{
if (propogateAllFrames)
{
foreach (Frame f in voxelObject.Frames)
{
if (f.EditingVoxels == null)
{
f.EditingVoxels =
new Voxel[voxelObject.XSize * voxelObject.YSize * voxelObject.ZSize];
Helper.CopyVoxelsInBox(ref voxelObject.GetCurrentFrame().Voxels,
ref f.EditingVoxels,
new PicaVoxelPoint(voxelObject.XSize, voxelObject.YSize, voxelObject.ZSize),
new PicaVoxelPoint(voxelObject.XSize, voxelObject.YSize, voxelObject.ZSize),
false);
}
//f.UpdateAllChunks();
}
}
else
{
if (voxelObject.GetCurrentFrame().EditingVoxels == null)
{
voxelObject.GetCurrentFrame().EditingVoxels =
new Voxel[voxelObject.XSize*voxelObject.YSize*voxelObject.ZSize];
Helper.CopyVoxelsInBox(ref voxelObject.GetCurrentFrame().Voxels,
ref voxelObject.GetCurrentFrame().EditingVoxels,
new PicaVoxelPoint(voxelObject.XSize, voxelObject.YSize, voxelObject.ZSize),
new PicaVoxelPoint(voxelObject.XSize, voxelObject.YSize, voxelObject.ZSize),
false);
}
//voxelObject.GetCurrentFrame().EditingVoxels = null;
//voxelObject.UpdateAllChunks();
}
PicaVoxelPoint brushPos = new PicaVoxelPoint(voxelObject.GetVoxelPosition(r.GetPoint(d)));
if (propogateAllFrames)
{
foreach (Frame f in voxelObject.Frames)
{
//f.EditingVoxels =
// new Voxel[voxelObject.XSize * voxelObject.YSize * voxelObject.ZSize];
Helper.CopyVoxelsInBox(ref f.Voxels, ref f.EditingVoxels,
new PicaVoxelBox(previousBrushPos.X - voxelObject.XChunkSize, previousBrushPos.Y - voxelObject.YChunkSize, previousBrushPos.Z - voxelObject.ZChunkSize, previousBrushPos.X + EditorPersistence.BrushSize.X + voxelObject.XChunkSize, previousBrushPos.Y + EditorPersistence.BrushSize.Y + voxelObject.YChunkSize, previousBrushPos.Z + EditorPersistence.BrushSize.Z + voxelObject.ZChunkSize),
new PicaVoxelBox(previousBrushPos.X - voxelObject.XChunkSize, previousBrushPos.Y - voxelObject.YChunkSize, previousBrushPos.Z - voxelObject.ZChunkSize, previousBrushPos.X + EditorPersistence.BrushSize.X + voxelObject.XChunkSize, previousBrushPos.Y + EditorPersistence.BrushSize.Y + voxelObject.YChunkSize, previousBrushPos.Z + EditorPersistence.BrushSize.Z + voxelObject.ZChunkSize),
new PicaVoxelPoint(voxelObject.XSize, voxelObject.YSize, voxelObject.ZSize),
new PicaVoxelPoint(voxelObject.XSize, voxelObject.YSize, voxelObject.ZSize),
false);
}
}
else
{
//voxelObject.GetCurrentFrame().EditingVoxels =
// new Voxel[voxelObject.XSize * voxelObject.YSize * voxelObject.ZSize];
//Helper.CopyVoxelsInBox(ref voxelObject.GetCurrentFrame().Voxels,
// ref voxelObject.GetCurrentFrame().EditingVoxels,
// new PicaVoxelPoint(voxelObject.XSize, voxelObject.YSize, voxelObject.ZSize),
// new PicaVoxelPoint(voxelObject.XSize, voxelObject.YSize, voxelObject.ZSize),
// false);
Helper.CopyVoxelsInBox(ref voxelObject.GetCurrentFrame().Voxels,
ref voxelObject.GetCurrentFrame().EditingVoxels,
new PicaVoxelBox(previousBrushPos.X - voxelObject.XChunkSize, previousBrushPos.Y - voxelObject.YChunkSize, previousBrushPos.Z - voxelObject.ZChunkSize, previousBrushPos.X + EditorPersistence.BrushSize.X + voxelObject.XChunkSize, previousBrushPos.Y + EditorPersistence.BrushSize.Y + voxelObject.YChunkSize, previousBrushPos.Z + EditorPersistence.BrushSize.Z + voxelObject.ZChunkSize),
new PicaVoxelBox(previousBrushPos.X - voxelObject.XChunkSize, previousBrushPos.Y - voxelObject.YChunkSize, previousBrushPos.Z - voxelObject.ZChunkSize, previousBrushPos.X + EditorPersistence.BrushSize.X + voxelObject.XChunkSize, previousBrushPos.Y + EditorPersistence.BrushSize.Y + voxelObject.YChunkSize, previousBrushPos.Z + EditorPersistence.BrushSize.Z + voxelObject.ZChunkSize),
new PicaVoxelPoint(voxelObject.XSize, voxelObject.YSize, voxelObject.ZSize),
new PicaVoxelPoint(voxelObject.XSize, voxelObject.YSize, voxelObject.ZSize),
false);
}
SetChunksInBoxDirty(new PicaVoxelBox(previousBrushPos.X - voxelObject.XChunkSize,
previousBrushPos.Y - voxelObject.YChunkSize, previousBrushPos.Z - voxelObject.ZChunkSize,
previousBrushPos.X + EditorPersistence.BrushSize.X + voxelObject.XChunkSize,
previousBrushPos.Y + EditorPersistence.BrushSize.Y + voxelObject.YChunkSize,
previousBrushPos.Z + EditorPersistence.BrushSize.Z + voxelObject.ZChunkSize));
int bx = brushPos.X -
(EditorPersistence.BrushAnchorX == AnchorX.Center
? EditorPersistence.BrushSize.X/2
: EditorPersistence.BrushAnchorX == AnchorX.Right
? EditorPersistence.BrushSize.X - 1
: 0);
int by = brushPos.Y -
(EditorPersistence.BrushAnchorY == AnchorY.Center
? EditorPersistence.BrushSize.Y/2
: EditorPersistence.BrushAnchorY == AnchorY.Top
? EditorPersistence.BrushSize.Y - 1
: 0);
int bz = brushPos.Z -
(EditorPersistence.BrushAnchorZ == AnchorZ.Center
? EditorPersistence.BrushSize.Z/2
: EditorPersistence.BrushAnchorZ == AnchorZ.Back
? EditorPersistence.BrushSize.Z - 1
: 0);
previousBrushPos = new PicaVoxelPoint(bx,by,bz);
for (int x = 0; x < EditorPersistence.BrushSize.X; x++)
{
for (int y = 0; y < EditorPersistence.BrushSize.Y; y++)
{
for (int z = 0; z < EditorPersistence.BrushSize.Z; z++)
{
if (bx < 0 || by < 0 || bz < 0 || bx >= voxelObject.XSize ||
by >= voxelObject.YSize || bz >= voxelObject.ZSize)
{
bz++;
continue;
}
if (
EditorPersistence.Brush[
x +
(EditorPersistence.BrushSize.X)*
(y + (EditorPersistence.BrushSize.Y)*z)].Active)
{
if (propogateAllFrames)
{
foreach (Frame f in voxelObject.Frames)
{
if (EditorPersistence.CursorMode == EditorCursorMode.BrushPaint &&
!f.Voxels[bx + voxelObject.XSize*(by + voxelObject.YSize*bz)
].Active) continue;
f.SetVoxelAtArrayPosition(bx, by, bz, new Voxel()
{
Color =
EditorPersistence.Brush[
x +
(EditorPersistence.BrushSize.X)*
(y + (EditorPersistence.BrushSize.Y)*z)].Color,
Value =
EditorPersistence.Brush[
x +
(EditorPersistence.BrushSize.X)*
(y + (EditorPersistence.BrushSize.Y)*z)].Value,
State =
(EditorPersistence.CursorMode !=
EditorCursorMode.BrushSubtract)?VoxelState.Active : VoxelState.Inactive
});
}
}
else
{
if (EditorPersistence.CursorMode == EditorCursorMode.BrushPaint &&
!voxelObject.GetCurrentFrame().Voxels[
bx + voxelObject.XSize*(by + voxelObject.YSize*bz)].Active)
continue;
voxelObject.SetVoxelAtArrayPosition(bx, by, bz, new Voxel()
{
Color =
EditorPersistence.Brush[
x +
(EditorPersistence.BrushSize.X)*
(y + (EditorPersistence.BrushSize.Y)*z)].Color,
Value =
EditorPersistence.Brush[
x +
(EditorPersistence.BrushSize.X)*
(y + (EditorPersistence.BrushSize.Y)*z)].Value,
State =
(EditorPersistence.CursorMode !=
EditorCursorMode.BrushSubtract) ? VoxelState.Active : VoxelState.Inactive
});
}
}
bz++;
}
bz = brushPos.Z -
(EditorPersistence.BrushAnchorZ == AnchorZ.Center
? EditorPersistence.BrushSize.Z/2
: EditorPersistence.BrushAnchorZ == AnchorZ.Back
? EditorPersistence.BrushSize.Z - 1
: 0);
by++;
}
by = brushPos.Y -
(EditorPersistence.BrushAnchorY == AnchorY.Center
? EditorPersistence.BrushSize.Y/2
: EditorPersistence.BrushAnchorY == AnchorY.Top
? EditorPersistence.BrushSize.Y - 1
: 0);
bx++;
}
if (propogateAllFrames)
foreach (Frame f in voxelObject.Frames) f.UpdateChunks(true);
else voxelObject.UpdateChunks(true);
}
}
// Mouse Up (Brush mode)
if (validBrushPos && e.type == EventType.MouseUp && e.button == 0)// &&
// voxelObject.GetCurrentFrame().EditingVoxels != null)
{
if ((EditorPersistence.CursorMode == EditorCursorMode.BrushAdd ||
EditorPersistence.CursorMode == EditorCursorMode.BrushPaint ||
EditorPersistence.CursorMode == EditorCursorMode.BrushSubtract))
{
voxelObject.GetCurrentFrame().EditingVoxels = null;
if (propogateAllFrames)
{
List<Object> undoObjects = new List<Object>();
foreach (Frame frame in voxelObject.Frames)
{
undoObjects.Add(frame);
}
undoObjects.Add(voxelObject);
Undo.RecordObjects(undoObjects.ToArray(), "Voxel Editing");
//foreach (Frame f in voxelObject.Frames)
//{
// ////Helper.CopyVoxelsInBox(ref f.EditingVoxels,
// //// ref f.Voxels,
// //// new PicaVoxelPoint(voxelObject.XSize, voxelObject.YSize, voxelObject.ZSize),
// //// new PicaVoxelPoint(voxelObject.XSize, voxelObject.YSize, voxelObject.ZSize),
// //// false);
// //Helper.CopyVoxelsInBox(ref f.EditingVoxels,
// // ref f.Voxels,
// // new PicaVoxelBox(previousBrushPos.X , previousBrushPos.Y , previousBrushPos.Y , previousBrushPos.X + EditorPersistence.BrushSize.X, previousBrushPos.Y + EditorPersistence.BrushSize.Y, previousBrushPos.Z + EditorPersistence.BrushSize.Z),
// // new PicaVoxelBox(previousBrushPos.X, previousBrushPos.Y, previousBrushPos.Y, previousBrushPos.X + EditorPersistence.BrushSize.X , previousBrushPos.Y + EditorPersistence.BrushSize.Y, previousBrushPos.Z + EditorPersistence.BrushSize.Z),
// // new PicaVoxelPoint(voxelObject.XSize, voxelObject.YSize, voxelObject.ZSize),
// // new PicaVoxelPoint(voxelObject.XSize, voxelObject.YSize, voxelObject.ZSize),
// // false);
//}
}
else
{
Undo.RecordObjects(new UnityEngine.Object[]
{
voxelObject.GetCurrentFrame(),
voxelObject,
}, "Voxel Editing");
//Helper.CopyVoxelsInBox(ref voxelObject.GetCurrentFrame().EditingVoxels,
// ref voxelObject.GetCurrentFrame().Voxels,
// new PicaVoxelPoint(voxelObject.XSize, voxelObject.YSize, voxelObject.ZSize),
// new PicaVoxelPoint(voxelObject.XSize, voxelObject.YSize, voxelObject.ZSize),
// false);
//Helper.CopyVoxelsInBox(ref voxelObject.GetCurrentFrame().EditingVoxels,
// ref voxelObject.GetCurrentFrame().Voxels,
// new PicaVoxelBox(previousBrushPos.X, previousBrushPos.Y, previousBrushPos.Y, previousBrushPos.X + EditorPersistence.BrushSize.X, previousBrushPos.Y + EditorPersistence.BrushSize.Y, previousBrushPos.Z + EditorPersistence.BrushSize.Z),
// new PicaVoxelBox(previousBrushPos.X, previousBrushPos.Y, previousBrushPos.Y, previousBrushPos.X + EditorPersistence.BrushSize.X, previousBrushPos.Y + EditorPersistence.BrushSize.Y, previousBrushPos.Z + EditorPersistence.BrushSize.Z),
// new PicaVoxelPoint(voxelObject.XSize, voxelObject.YSize, voxelObject.ZSize),
// new PicaVoxelPoint(voxelObject.XSize, voxelObject.YSize, voxelObject.ZSize),
// false);
}
PicaVoxelPoint brushPos = new PicaVoxelPoint(voxelObject.GetVoxelPosition(r.GetPoint(d)));
int bx = brushPos.X -
(EditorPersistence.BrushAnchorX == AnchorX.Center
? EditorPersistence.BrushSize.X/2
: EditorPersistence.BrushAnchorX == AnchorX.Right
? EditorPersistence.BrushSize.X - 1
: 0);
int by = brushPos.Y -
(EditorPersistence.BrushAnchorY == AnchorY.Center
? EditorPersistence.BrushSize.Y/2
: EditorPersistence.BrushAnchorY == AnchorY.Top
? EditorPersistence.BrushSize.Y - 1
: 0);
int bz = brushPos.Z -
(EditorPersistence.BrushAnchorZ == AnchorZ.Center
? EditorPersistence.BrushSize.Z/2
: EditorPersistence.BrushAnchorZ == AnchorZ.Back
? EditorPersistence.BrushSize.Z - 1
: 0);
previousBrushPos = new PicaVoxelPoint(bx,by,bz);
for (int x = 0; x < EditorPersistence.BrushSize.X; x++)
{
for (int y = 0; y < EditorPersistence.BrushSize.Y; y++)
{
for (int z = 0; z < EditorPersistence.BrushSize.Z; z++)
{
if (bx < 0 || by < 0 || bz < 0 || bx >= voxelObject.XSize ||
by >= voxelObject.YSize || bz >= voxelObject.ZSize)
{
bz++;
continue;
}
if (
EditorPersistence.Brush[
x +
(EditorPersistence.BrushSize.X)*
(y + (EditorPersistence.BrushSize.Y)*z)].Active)
{
if (propogateAllFrames)
{
foreach (Frame f in voxelObject.Frames)
{
if (EditorPersistence.CursorMode == EditorCursorMode.BrushPaint &&
!f.Voxels[bx + voxelObject.XSize*(by + voxelObject.YSize*bz)
].Active) continue;
f.SetVoxelAtArrayPosition(bx, by, bz, new Voxel()
{
Color =
EditorPersistence.Brush[
x +
(EditorPersistence.BrushSize.X)*
(y + (EditorPersistence.BrushSize.Y)*z)].Color,
Value =
EditorPersistence.Brush[
x +
(EditorPersistence.BrushSize.X)*
(y + (EditorPersistence.BrushSize.Y)*z)].Value,
State =
(EditorPersistence.CursorMode !=
EditorCursorMode.BrushSubtract) ? VoxelState.Active : VoxelState.Inactive
});
}
}
else
{
if (EditorPersistence.CursorMode == EditorCursorMode.BrushPaint &&
!voxelObject.GetCurrentFrame().Voxels[
bx + voxelObject.XSize*(by + voxelObject.YSize*bz)].Active)
continue;
voxelObject.SetVoxelAtArrayPosition(bx, by, bz, new Voxel()
{
Color =
EditorPersistence.Brush[
x +
(EditorPersistence.BrushSize.X)*
(y + (EditorPersistence.BrushSize.Y)*z)].Color,
Value =
EditorPersistence.Brush[
x +
(EditorPersistence.BrushSize.X)*
(y + (EditorPersistence.BrushSize.Y)*z)].Value,
State =
(EditorPersistence.CursorMode !=
EditorCursorMode.BrushSubtract) ? VoxelState.Active : VoxelState.Inactive
});
}
}
bz++;
}
bz = brushPos.Z -
(EditorPersistence.BrushAnchorZ == AnchorZ.Center
? EditorPersistence.BrushSize.Z/2
: EditorPersistence.BrushAnchorZ == AnchorZ.Back
? EditorPersistence.BrushSize.Z - 1
: 0);
by++;
}
by = brushPos.Y -
(EditorPersistence.BrushAnchorY == AnchorY.Center
? EditorPersistence.BrushSize.Y/2
: EditorPersistence.BrushAnchorY == AnchorY.Top
? EditorPersistence.BrushSize.Y - 1
: 0);
bx++;
}
if (propogateAllFrames)
foreach (Frame f in voxelObject.Frames) f.UpdateChunks(true);
else voxelObject.UpdateChunks(true);
currentBox = null;
boxStart = null;
voxelObject.SaveForSerialize();
}
}
break;
}
}
//if (!validBrushPos && (EditorPersistence.CursorMode == EditorCursorMode.BrushAdd ||
// EditorPersistence.CursorMode == EditorCursorMode.BrushPaint ||
// EditorPersistence.CursorMode == EditorCursorMode.BrushSubtract))
//{
// if (propogateAllFrames)
// foreach (Frame f in voxelObject.Frames) f.EditingVoxels = null;
// else voxelObject.GetCurrentFrame().EditingVoxels = null;
//}
// Mouse Up (non-brush)
if (e.type == EventType.MouseUp && e.button == 0 && voxelObject.GetCurrentFrame().EditingVoxels != null)
{
if ((EditorPersistence.CursorMode != EditorCursorMode.BrushAdd &&
EditorPersistence.CursorMode != EditorCursorMode.BrushPaint &&
EditorPersistence.CursorMode != EditorCursorMode.BrushSubtract))
{
if (propogateAllFrames)
{
List<Object> undoObjects = new List<Object>();
foreach (Frame frame in voxelObject.Frames)
{
undoObjects.Add(frame);
}
undoObjects.Add(voxelObject);
Undo.RecordObjects(undoObjects.ToArray(), "Voxel Editing");
foreach (Frame f in voxelObject.Frames)
{
//Helper.CopyVoxelsInBox(ref f.EditingVoxels,
// ref f.Voxels,
// new PicaVoxelPoint(voxelObject.XSize, voxelObject.YSize, voxelObject.ZSize),
// new PicaVoxelPoint(voxelObject.XSize, voxelObject.YSize, voxelObject.ZSize), false);
Helper.CopyVoxelsInBox(ref f.EditingVoxels,
ref f.Voxels, changedVoxelExtents, changedVoxelExtents,
new PicaVoxelPoint(voxelObject.XSize, voxelObject.YSize, voxelObject.ZSize),
new PicaVoxelPoint(voxelObject.XSize, voxelObject.YSize, voxelObject.ZSize),
false);
f.EditingVoxels = null;
}
}
else
{
Undo.RecordObjects(new UnityEngine.Object[]
{
voxelObject.GetCurrentFrame(),
voxelObject,
}, "Voxel Editing");
//Helper.CopyVoxelsInBox(ref voxelObject.GetCurrentFrame().EditingVoxels,
// ref voxelObject.GetCurrentFrame().Voxels, new PicaVoxelPoint(voxelObject.XSize, voxelObject.YSize, voxelObject.ZSize), new PicaVoxelPoint(voxelObject.XSize, voxelObject.YSize, voxelObject.ZSize), false);
Helper.CopyVoxelsInBox(ref voxelObject.GetCurrentFrame().EditingVoxels,
ref voxelObject.GetCurrentFrame().Voxels, changedVoxelExtents, changedVoxelExtents,
new PicaVoxelPoint(voxelObject.XSize, voxelObject.YSize, voxelObject.ZSize),
new PicaVoxelPoint(voxelObject.XSize, voxelObject.YSize, voxelObject.ZSize),
false);
voxelObject.GetCurrentFrame().EditingVoxels = null;
//voxelObject.SaveForSerialize();
}
currentBox = null;
boxStart = null;
voxelObject.SaveForSerialize();
}
}
}
if (EditorPersistence.CursorMode == EditorCursorMode.Select)
{
Gizmos.matrix = voxelObject.GetCurrentFrame().transform.FindChild("Chunks").localToWorldMatrix;
Handles.color = Color.red;
int i = 0;
Vector3 newPos = Vector3.zero;
bool changedBox = false;
for (int x = EditorPersistence.SelectBox.BottomLeftFront.X;
x <= EditorPersistence.SelectBox.TopRightBack.X + 1;
x += EditorPersistence.SelectBox.TopRightBack.X + 1 - EditorPersistence.SelectBox.BottomLeftFront.X)
{
for (int y = EditorPersistence.SelectBox.BottomLeftFront.Y;
y <= EditorPersistence.SelectBox.TopRightBack.Y + 1;
y +=
EditorPersistence.SelectBox.TopRightBack.Y + 1 -
EditorPersistence.SelectBox.BottomLeftFront.Y)
{
for (int z = EditorPersistence.SelectBox.BottomLeftFront.Z;
z <= EditorPersistence.SelectBox.TopRightBack.Z + 1;
z +=
EditorPersistence.SelectBox.TopRightBack.Z + 1 -
EditorPersistence.SelectBox.BottomLeftFront.Z)
{
Vector3 handlePos =
voxelObject.transform.TransformPoint((new Vector3(x, y, z)*voxelObject.VoxelSize) -
voxelObject.Pivot);
EditorHandles.DragHandleResult dhResult;
newPos = EditorHandles.DragHandle(handlePos, HandleUtility.GetHandleSize(handlePos)*0.25f,
Handles.SphereCap, Color.red, out dhResult);
if (dhResult == EditorHandles.DragHandleResult.LMBDrag)
{
Voxel? handleVox = voxelObject.GetVoxelAtWorldPosition(newPos);
if (handleVox.HasValue)
{
if (i == 0)
{
EditorPersistence.SelectBox =
new PicaVoxelBox(
new PicaVoxelPoint((int) voxelObject.GetVoxelPosition(newPos).x,
(int) voxelObject.GetVoxelPosition(newPos).y,
(int) voxelObject.GetVoxelPosition(newPos).z),
new PicaVoxelPoint(EditorPersistence.SelectBox.TopRightBack.X,
EditorPersistence.SelectBox.TopRightBack.Y,
EditorPersistence.SelectBox.TopRightBack.Z));
changedBox = true;
}
if (i == 1)
{
EditorPersistence.SelectBox =
new PicaVoxelBox(
new PicaVoxelPoint((int)voxelObject.GetVoxelPosition(newPos).x,
(int)voxelObject.GetVoxelPosition(newPos).y,
EditorPersistence.SelectBox.BottomLeftFront.Z),
new PicaVoxelPoint(EditorPersistence.SelectBox.TopRightBack.X,
EditorPersistence.SelectBox.TopRightBack.Y,
(int)voxelObject.GetVoxelPosition(newPos).z));
changedBox = true;
}
if (i == 2)
{
EditorPersistence.SelectBox =
new PicaVoxelBox(
new PicaVoxelPoint((int)voxelObject.GetVoxelPosition(newPos).x,
EditorPersistence.SelectBox.BottomLeftFront.Y,
(int)voxelObject.GetVoxelPosition(newPos).z),
new PicaVoxelPoint(EditorPersistence.SelectBox.TopRightBack.X,
(int)voxelObject.GetVoxelPosition(newPos).y,
EditorPersistence.SelectBox.TopRightBack.Z));
changedBox = true;
}
if (i == 3)
{
EditorPersistence.SelectBox =
new PicaVoxelBox(
new PicaVoxelPoint((int)voxelObject.GetVoxelPosition(newPos).x,
EditorPersistence.SelectBox.BottomLeftFront.Y,
EditorPersistence.SelectBox.BottomLeftFront.Z),
new PicaVoxelPoint(EditorPersistence.SelectBox.TopRightBack.X,
(int)voxelObject.GetVoxelPosition(newPos).y,
(int)voxelObject.GetVoxelPosition(newPos).z));
changedBox = true;
}
if (i == 4)
{
EditorPersistence.SelectBox =
new PicaVoxelBox(
new PicaVoxelPoint(EditorPersistence.SelectBox.BottomLeftFront.X,
(int)voxelObject.GetVoxelPosition(newPos).y,
(int)voxelObject.GetVoxelPosition(newPos).z),
new PicaVoxelPoint(
(int)voxelObject.GetVoxelPosition(newPos).x,
EditorPersistence.SelectBox.TopRightBack.Y,
EditorPersistence.SelectBox.TopRightBack.Z));
changedBox = true;
}
if (i == 5)
{
EditorPersistence.SelectBox = new PicaVoxelBox(
new PicaVoxelPoint(EditorPersistence.SelectBox.BottomLeftFront.X,
(int)voxelObject.GetVoxelPosition(newPos).y,
EditorPersistence.SelectBox.BottomLeftFront.Z),
new PicaVoxelPoint(
(int)voxelObject.GetVoxelPosition(newPos).x,
EditorPersistence.SelectBox.TopRightBack.Y,
(int)voxelObject.GetVoxelPosition(newPos).z));
changedBox = true;
}
if (i == 6)
{
EditorPersistence.SelectBox = new PicaVoxelBox(
new PicaVoxelPoint(EditorPersistence.SelectBox.BottomLeftFront.X,
EditorPersistence.SelectBox.BottomLeftFront.Y,
(int)voxelObject.GetVoxelPosition(newPos).z),
new PicaVoxelPoint(
(int)voxelObject.GetVoxelPosition(newPos).x,
(int)voxelObject.GetVoxelPosition(newPos).y,
EditorPersistence.SelectBox.TopRightBack.Z));
changedBox = true;
}
if (i == 7)
{
EditorPersistence.SelectBox = new PicaVoxelBox(
new PicaVoxelPoint(EditorPersistence.SelectBox.BottomLeftFront.X,
EditorPersistence.SelectBox.BottomLeftFront.Y,
EditorPersistence.SelectBox.BottomLeftFront.Z),
new PicaVoxelPoint(
(int)voxelObject.GetVoxelPosition(newPos).x,
(int)voxelObject.GetVoxelPosition(newPos).y,
(int)voxelObject.GetVoxelPosition(newPos).z));
changedBox = true;
}
}
}
i++;
if (changedBox) break;
}
if (changedBox) break;
}
if (changedBox) break;
}
Vector3[] corners =
{
voxelObject.transform.TransformPoint((EditorPersistence.SelectBox.BottomLeftFront.ToVector3()*
voxelSize) - voxelObject.Pivot),
voxelObject.transform.TransformPoint(
(new Vector3(EditorPersistence.SelectBox.BottomLeftFront.X,
EditorPersistence.SelectBox.BottomLeftFront.Y,
EditorPersistence.SelectBox.TopRightBack.Z + 1)*voxelSize) - voxelObject.Pivot),
voxelObject.transform.TransformPoint(
(new Vector3(EditorPersistence.SelectBox.TopRightBack.X + 1,
EditorPersistence.SelectBox.BottomLeftFront.Y,
EditorPersistence.SelectBox.TopRightBack.Z + 1)*voxelSize) - voxelObject.Pivot),
voxelObject.transform.TransformPoint(
(new Vector3(EditorPersistence.SelectBox.TopRightBack.X + 1,
EditorPersistence.SelectBox.BottomLeftFront.Y, EditorPersistence.SelectBox.BottomLeftFront.Z)*
voxelSize) - voxelObject.Pivot),
voxelObject.transform.TransformPoint(
(new Vector3(EditorPersistence.SelectBox.BottomLeftFront.X,
EditorPersistence.SelectBox.TopRightBack.Y + 1,
EditorPersistence.SelectBox.BottomLeftFront.Z)*voxelSize) - voxelObject.Pivot),
voxelObject.transform.TransformPoint(
(new Vector3(EditorPersistence.SelectBox.BottomLeftFront.X,
EditorPersistence.SelectBox.TopRightBack.Y + 1,
EditorPersistence.SelectBox.TopRightBack.Z + 1)*voxelSize) - voxelObject.Pivot),
voxelObject.transform.TransformPoint(((EditorPersistence.SelectBox.TopRightBack.ToVector3() +
Vector3.one)*voxelSize) - voxelObject.Pivot),
voxelObject.transform.TransformPoint(
(new Vector3(EditorPersistence.SelectBox.TopRightBack.X + 1,
EditorPersistence.SelectBox.TopRightBack.Y + 1,
EditorPersistence.SelectBox.BottomLeftFront.Z)*voxelSize) - voxelObject.Pivot)
};
Handles.DrawSolidRectangleWithOutline(new[] {corners[0], corners[1], corners[2], corners[3]},
Color.white*0.1f, Color.black);
Handles.DrawSolidRectangleWithOutline(new[] {corners[0], corners[4], corners[7], corners[3]},
Color.white*0.1f, Color.black);
Handles.DrawSolidRectangleWithOutline(new[] {corners[1], corners[5], corners[4], corners[0]},
Color.white*0.1f, Color.black);
Handles.DrawSolidRectangleWithOutline(new[] {corners[2], corners[6], corners[5], corners[1]},
Color.white*0.1f, Color.black);
Handles.DrawSolidRectangleWithOutline(new[] {corners[3], corners[7], corners[6], corners[2]},
Color.white*0.1f, Color.black);
Handles.DrawSolidRectangleWithOutline(new[] {corners[4], corners[5], corners[6], corners[7]},
Color.white*0.1f, Color.black);
}
}
private void SetChunksInBoxDirty(PicaVoxelBox picaVoxelBox)
{
for(int x=picaVoxelBox.BottomLeftFront.X;x<=picaVoxelBox.TopRightBack.X;x+= voxelObject.XChunkSize)
for(int y=picaVoxelBox.BottomLeftFront.Y;y<=picaVoxelBox.TopRightBack.Y;y+= voxelObject.YChunkSize)
for(int z=picaVoxelBox.BottomLeftFront.Z;z<=picaVoxelBox.TopRightBack.Z;z+= voxelObject.ZChunkSize)
if(propogateAllFrames)
foreach(Frame f in voxelObject.Frames) f.SetChunkAtVoxelPositionDirty(x,y,z);
else
voxelObject.GetCurrentFrame().SetChunkAtVoxelPositionDirty(x,y,z);
}
private void RegisterUndo()
{
if (propogateAllFrames)
{
List<Object> undoObjects = new List<Object>();
foreach (Frame frame in voxelObject.Frames)
{
undoObjects.Add(frame);
}
undoObjects.Add(voxelObject);
Undo.RecordObjects(undoObjects.ToArray(), "Voxel Editing");
}
else
{
Undo.RecordObjects(new UnityEngine.Object[]
{
voxelObject.GetCurrentFrame(),
voxelObject,
}, "Voxel Editing");
}
}
private void NudgeSelection(int dx, int dy, int dz)
{
PicaVoxelBox dest = new PicaVoxelBox(EditorPersistence.SelectBox.BottomLeftFront.X+dx,
EditorPersistence.SelectBox.BottomLeftFront.Y+dy,
EditorPersistence.SelectBox.BottomLeftFront.Z+dz,
EditorPersistence.SelectBox.TopRightBack.X+dx,
EditorPersistence.SelectBox.TopRightBack.Y+dy,
EditorPersistence.SelectBox.TopRightBack.Z+dz);
if (dest.BottomLeftFront.X < 0 || dest.BottomLeftFront.Y < 0 || dest.BottomLeftFront.Z < 0 ||
dest.TopRightBack.X >= voxelObject.XSize || dest.TopRightBack.Y >= voxelObject.YSize ||
dest.TopRightBack.Z >= voxelObject.ZSize) return;
int destWidth = (dest.TopRightBack.X - dest.BottomLeftFront.X) +1;
int destHeight = (dest.TopRightBack.Y - dest.BottomLeftFront.Y)+1;
int destDepth = (dest.TopRightBack.Z - dest.BottomLeftFront.Z)+1;
Voxel[] tempVox = new Voxel[destWidth *destHeight * destDepth];
RegisterUndo();
if (propogateAllFrames)
{
foreach (Frame f in voxelObject.Frames)
{
Helper.CopyVoxelsInBox(ref f.Voxels, ref tempVox,
EditorPersistence.SelectBox,
new PicaVoxelBox(0, 0, 0, destWidth - 1, destHeight - 1, destDepth - 1),
new PicaVoxelPoint(voxelObject.XSize, voxelObject.YSize, voxelObject.ZSize),
new PicaVoxelPoint(destWidth, destHeight, destDepth), false);
f.EditingVoxels = null;
for (int x = EditorPersistence.SelectBox.BottomLeftFront.X; x <= EditorPersistence.SelectBox.TopRightBack.X; x++)
for (int y = EditorPersistence.SelectBox.BottomLeftFront.Y; y <= EditorPersistence.SelectBox.TopRightBack.Y; y++)
for (int z = EditorPersistence.SelectBox.BottomLeftFront.Z; z <= EditorPersistence.SelectBox.TopRightBack.Z; z++)
f.Voxels[x + voxelObject.XSize * (y + voxelObject.YSize * z)] = new Voxel()
{
State = VoxelState.Inactive,
Color = voxelObject.PaletteColors[EditorPersistence.SelectedColor],
Value = EditorPersistence.SelectedValue
};
Helper.CopyVoxelsInBox(ref tempVox, ref f.Voxels,
new PicaVoxelBox(0, 0, 0, destWidth - 1, destHeight - 1, destDepth - 1),
dest,
new PicaVoxelPoint(destWidth, destHeight, destDepth),
new PicaVoxelPoint(voxelObject.XSize, voxelObject.YSize, voxelObject.ZSize), false);
f.SaveForSerialize();
f.UpdateAllChunks();
}
EditorPersistence.SelectBox = dest;
}
else
{
Helper.CopyVoxelsInBox(ref voxelObject.Frames[voxelObject.CurrentFrame].Voxels, ref tempVox,
EditorPersistence.SelectBox,
new PicaVoxelBox(0, 0, 0, destWidth-1, destHeight-1, destDepth-1),
new PicaVoxelPoint(voxelObject.XSize, voxelObject.YSize, voxelObject.ZSize),
new PicaVoxelPoint(destWidth, destHeight, destDepth), false);
voxelObject.Frames[voxelObject.CurrentFrame].EditingVoxels = null;
for (int x = EditorPersistence.SelectBox.BottomLeftFront.X; x <= EditorPersistence.SelectBox.TopRightBack.X; x++)
for (int y = EditorPersistence.SelectBox.BottomLeftFront.Y; y <= EditorPersistence.SelectBox.TopRightBack.Y; y++)
for (int z = EditorPersistence.SelectBox.BottomLeftFront.Z; z <= EditorPersistence.SelectBox.TopRightBack.Z; z++)
voxelObject.Frames[voxelObject.CurrentFrame].Voxels[x + voxelObject.XSize * (y + voxelObject.YSize * z)] = new Voxel()
{
State = VoxelState.Inactive,
Color = voxelObject.PaletteColors[EditorPersistence.SelectedColor],
Value = EditorPersistence.SelectedValue
};
Helper.CopyVoxelsInBox(ref tempVox, ref voxelObject.Frames[voxelObject.CurrentFrame].Voxels,
new PicaVoxelBox(0, 0, 0, destWidth - 1, destHeight - 1, destDepth - 1),
dest,
new PicaVoxelPoint(destWidth, destHeight, destDepth),
new PicaVoxelPoint(voxelObject.XSize, voxelObject.YSize, voxelObject.ZSize), false);
EditorPersistence.SelectBox = dest;
voxelObject.Frames[voxelObject.CurrentFrame].SaveForSerialize();
voxelObject.Frames[voxelObject.CurrentFrame].UpdateAllChunks();
}
}
private void RotateBrush(RotateAxis axis)
{
switch (axis)
{
case RotateAxis.X:
Helper.RotateVoxelArrayX(ref EditorPersistence.Brush, EditorPersistence.BrushSize);
int tempY = EditorPersistence.BrushSize.Y;
EditorPersistence.BrushSize.Y = EditorPersistence.BrushSize.Z;
EditorPersistence.BrushSize.Z = tempY;
break;
case RotateAxis.Y:
Helper.RotateVoxelArrayY(ref EditorPersistence.Brush, EditorPersistence.BrushSize);
int tempZ = EditorPersistence.BrushSize.Z;
EditorPersistence.BrushSize.Z = EditorPersistence.BrushSize.X;
EditorPersistence.BrushSize.X = tempZ;
break;
case RotateAxis.Z:
Helper.RotateVoxelArrayZ(ref EditorPersistence.Brush, EditorPersistence.BrushSize);
int tempX = EditorPersistence.BrushSize.X;
EditorPersistence.BrushSize.X = EditorPersistence.BrushSize.Y;
EditorPersistence.BrushSize.Y = tempX;
break;
}
}
private static bool HighlightButton(Texture2D texture, bool on, string tooltip)
{
if (on)
{
GUI.contentColor = (Color.white);
GUI.backgroundColor = Color.black;
}
else
{
GUI.contentColor = Color.white;
GUI.backgroundColor = (EditorGUIUtility.isProSkin ? Color.white : Color.grey);
}
bool pressed = GUILayout.Button(new GUIContent(texture, tooltip),
new GUIStyle(GUI.skin.button)
{
padding = new RectOffset(0, 0, 0, 0),
margin = new RectOffset(0, 2, 2, 0)
}, GUILayout.Width(32), GUILayout.Height(32));
GUI.backgroundColor = Color.white;
GUI.contentColor = Color.white;
return pressed;
}
private static bool PaletteButton(Color col, bool on)
{
GUI.contentColor = col;
if (on) GUI.backgroundColor = Color.black;
else GUI.backgroundColor = (EditorGUIUtility.isProSkin ? Color.white : Color.grey);
bool pressed = false;
try
{
pressed = GUILayout.Button(new GUIContent(EditorUtility.Buttons["pvButton_Palette"]),
new GUIStyle(GUI.skin.button)
{
padding = new RectOffset(0, 0, 0, 0),
margin = new RectOffset(0, 0, 0, 0)
}, GUILayout.Width(20), GUILayout.Height(20));
}
catch (Exception)
{
}
GUI.backgroundColor = Color.white;
GUI.contentColor = Color.white;
return pressed;
}
}
#endif
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment