Voxelizer.cs: The mains cript. Put it on a GameObject called “Manager” (the script is only needed once). Don’t put it on the obejct you want to voxelize!
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System;
using UnityEditor; //for PrefabUtility
public class Voxelizer : MonoBehaviour {
public GameObject objectToVoxelize;
public GameObject voxelizedObject;
public float delay = 0.5f;
Vector3 p0;
Vector3 p1;
private Vector3 voxelPos;
public GameObject voxelPrefab;
public bool waitForTime = false;
public bool waitOneFrame = false;
public bool fill = true;
public float fillPercentage_s = 100f;
public float fillPercentage_i = 100f;
public Color[] fillColors;
public bool createPB = true;
public bool keepVO = true;
// Use this for initialization
void Start () {
LoadVoxelPrefab();
}
// Update is called once per frame
void Update () {
if(Input.GetKeyDown(KeyCode.X)) {
StartCoroutine(Voxelize(objectToVoxelize, 16, fill, fillPercentage_s, fillPercentage_i, fillColors, createPB, "Models", keepVO, true));
}
Manager.helper.DrawCross(p0, 0.03125f, Color.grey, 0);
Manager.helper.DrawCross(p1, 0.03125f, Color.grey, 0);
}
public void LoadVoxelPrefab() {
voxelPrefab = Resources.Load("Models/Misc/Voxel") as GameObject;
}
public void StartVoxelize(GameObject go, int resolution, bool fillInside, float fillPercentage_shell, float fillPercentage_inside, Color[] insideColorArray, bool createPrefab, string prefabPath, bool keepVoxelizedObject, bool useMeshName) {
StartCoroutine(Voxelize(go, resolution, fillInside, fillPercentage_shell, fillPercentage_inside, insideColorArray, createPrefab, prefabPath, keepVoxelizedObject, useMeshName));
}
public IEnumerator Voxelize(GameObject go, int resolution, bool fillInside, float fillPercentage_shell, float fillPercentage_inside, Color[] insideColorArray, bool createPrefab, string prefabPath, bool keepVoxelizedObject, bool useMeshName) {
float voxelSize = 1f / resolution;
float voxelSizeHalf = voxelSize / 2;
Mesh mesh = Manager.helper.GetMesh(go);
go.SetActive(false);
GameObject goClone = GameObject.CreatePrimitive(PrimitiveType.Cube);
goClone.name = go.name + "_temp";
goClone.transform.position = go.transform.position;
goClone.transform.rotation = go.transform.rotation;
Collider[] colliders = goClone.GetComponents<Collider>();
foreach(Collider collider in colliders) {
if (Application.isPlaying) {
Destroy(collider);
}
else {
DestroyImmediate(collider);
}
}
goClone.AddComponent<MeshCollider>();
goClone.GetComponent<MeshCollider>().sharedMesh = mesh;
goClone.GetComponent<MeshFilter>().sharedMesh = mesh;
if(Application.isPlaying) {
goClone.GetComponent<MeshRenderer>().material = go.GetComponent<Renderer>().material;
}
else {
goClone.GetComponent<MeshRenderer>().material = go.GetComponent<Renderer>().sharedMaterial;
}
goClone.layer = go.layer;
/*
if(go.GetComponent<Rigidbody>() != null) {
go.GetComponent<Rigidbody>().isKinematic = true;
}
go.GetComponent<Collider>().enabled = false;
*/
/*
go.SetActive(false);
GameObject goClone = Instantiate(go, go.transform.position, go.transform.rotation) as GameObject;
Mesh mesh = GetMesh(goClone);
Renderer renderer = Manager.helper.GetRenderer(goClone);
if(goClone.GetComponent<Rigidbody>() != null) {
if (Application.isPlaying) {
Destroy(goClone.GetComponent<Rigidbody>());
}
else {
DestroyImmediate(goClone.GetComponent<Rigidbody>());
}
}
Collider[] colliders = goClone.GetComponents<Collider>();
foreach(Collider collider in colliders) {
if (Application.isPlaying) {
Destroy(collider);
}
else {
DestroyImmediate(collider);
}
}
yield return null;
goClone.AddComponent<MeshCollider>();
goClone.GetComponent<MeshCollider>().sharedMesh = mesh;
goClone.SetActive(true);
*/
Texture2D texture = null;
if(Application.isPlaying) {
texture = goClone.GetComponent<Renderer>().material.mainTexture as Texture2D;
}
else {
texture = goClone.GetComponent<Renderer>().sharedMaterial.mainTexture as Texture2D;
}
string layerName = LayerMask.LayerToName(goClone.layer);
LayerMask layerMask = new LayerMask();
layerMask |= (1 << LayerMask.NameToLayer(layerName));
Vector3 p0_pre = goClone.GetComponent<Renderer>().bounds.min;
Vector3 p1_pre = goClone.GetComponent<Renderer>().bounds.max;
//Debug.Log("p0_pre = " + p0_pre.x + " , " + p0_pre.y + " , " + p0_pre.z);
//Debug.Log("p1_pre = " + p1_pre.x + " , " + p1_pre.y + " , " + p1_pre.z);
Vector3 p0_pre_aligned = WorldToVoxelSpace(p0_pre, resolution, true);
Vector3 p1_pre_aligned = WorldToVoxelSpace(p1_pre, resolution, true);
//Debug.Log("p0_pre_aligned = " + p0_pre_aligned.x + " , " + p0_pre_aligned.y + " , " + p0_pre_aligned.z);
float offset_x = 0f;
float offset_y = 0f;
float offset_z = 0f;
if(p0_pre.x >= p0_pre_aligned.x) {
offset_x = (p0_pre.x - p0_pre_aligned.x) * -1;
}
else {
offset_x = p0_pre_aligned.x - p0_pre.x;
}
if(p0_pre.y >= p0_pre_aligned.y) {
offset_y = (p0_pre.y - p0_pre_aligned.y) * -1;
}
else {
offset_y = p0_pre_aligned.y - p0_pre.y;
}
if(p0_pre.z >= p0_pre_aligned.z) {
offset_z = (p0_pre.z - p0_pre_aligned.z) * -1;
}
else {
offset_z = p0_pre_aligned.z - p0_pre.z;
}
Vector3 offset = new Vector3(offset_x - voxelSizeHalf, offset_y - voxelSizeHalf, offset_z - voxelSizeHalf);
//Debug.Log("offset = " + offset.x + " , " + offset.y + " , " + offset.z);
goClone.transform.position = new Vector3(goClone.transform.position.x + offset.x, goClone.transform.position.y + offset.y, goClone.transform.position.z + offset.z);
Vector3 p0_post = goClone.GetComponent<Renderer>().bounds.min;
Vector3 p1_post = goClone.GetComponent<Renderer>().bounds.max;
//Debug.Log("p0_post = " + p0_post.x + " , " + p0_post.y + " , " + p0_post.z);
p0 = WorldToVoxelSpace(goClone.GetComponent<Renderer>().bounds.min + new Vector3(voxelSizeHalf, voxelSizeHalf, voxelSizeHalf), resolution, true);
p1 = WorldToVoxelSpace(goClone.GetComponent<Renderer>().bounds.max - new Vector3(voxelSizeHalf, voxelSizeHalf, voxelSizeHalf), resolution, true);
//Debug.Log("p0 = " + p0.x + " , " + p0.y + " , " + p0.z);
//Debug.Log("p1 = " + p1.x + " , " + p1.y + " , " + p1.z);
GameObject go_vox_root_prefab = Resources.Load("Models/Misc/EmptyGameObject") as GameObject;
GameObject go_vox_root = Instantiate(go_vox_root_prefab, goClone.transform.position, goClone.transform.rotation) as GameObject;
if(useMeshName == true) {
go_vox_root.name = mesh.name + "_voxelized";
}
else {
go_vox_root.name = go.name + "_voxelized";
}
Vector3 p0_voxelSpace = VoxelToLocalVoxelSpace(p0, resolution, Vector3.zero);
Vector3 p1_voxelSpace = VoxelToLocalVoxelSpace(p1, resolution, Vector3.zero);
//Debug.Log("p0_voxelSpace = " + p0_voxelSpace.x + " , " + p0_voxelSpace.y + " , " + p0_voxelSpace.z);
//Debug.Log("p1_voxelSpace = " + p1_voxelSpace.x + " , " + p1_voxelSpace.y + " , " + p1_voxelSpace.z);
int length_0 = (int)(p1_voxelSpace.x - p0_voxelSpace.x) + 1;
int length_1 = (int)(p1_voxelSpace.y - p0_voxelSpace.y) + 1;
int length_2 = (int)(p1_voxelSpace.z - p0_voxelSpace.z) + 1;
//Debug.Log("length_0 = " + length_0 + ", length_1 = " + length_1 + ", length_2 = " + length_2);
go_vox_root.AddComponent<VoxelObject>();
go_vox_root.GetComponent<VoxelObject>().voxelSpaceArray = new VoxelSpace[length_0,length_1,length_2];
VoxelSpace[,,] voxelSpaceArray = go_vox_root.GetComponent<VoxelObject>().voxelSpaceArray;
int x = 0;
int y = 0;
int z = 0;
for(x = 0; x < voxelSpaceArray.GetLength(0); x++) {
for(y = 0; y < voxelSpaceArray.GetLength(1); y++) {
for(z = 0; z < voxelSpaceArray.GetLength(2); z++) {
voxelSpaceArray[x,y,z] = new VoxelSpace();
}
}
}
//Register outer voxels
for(x = 0; x < voxelSpaceArray.GetLength(0); x++) {
for(y = 0; y < voxelSpaceArray.GetLength(1); y++) {
for(z = 0; z < voxelSpaceArray.GetLength(2); z++) {
Vector3 voxelWorldPos = new Vector3(p0.x + x * voxelSize, p0.y + y * voxelSize, p0.z + z * voxelSize);
Manager.helper.DrawCross(voxelWorldPos, voxelSizeHalf / 4, Color.blue, delay);
/*
Vector3 voxelCorner_000 = new Vector3(p0.x + x * voxelSize - voxelSizeHalf, p0.y + y * voxelSize - voxelSizeHalf, p0.z + z * voxelSize - voxelSizeHalf);
Vector3 voxelCorner_001 = new Vector3(p0.x + x * voxelSize - voxelSizeHalf, p0.y + y * voxelSize - voxelSizeHalf, p0.z + z * voxelSize + voxelSizeHalf);
Vector3 voxelCorner_010 = new Vector3(p0.x + x * voxelSize - voxelSizeHalf, p0.y + y * voxelSize + voxelSizeHalf, p0.z + z * voxelSize - voxelSizeHalf);
Vector3 voxelCorner_011 = new Vector3(p0.x + x * voxelSize - voxelSizeHalf, p0.y + y * voxelSize + voxelSizeHalf, p0.z + z * voxelSize + voxelSizeHalf);
Vector3 voxelCorner_100 = new Vector3(p0.x + x * voxelSize + voxelSizeHalf, p0.y + y * voxelSize - voxelSizeHalf, p0.z + z * voxelSize - voxelSizeHalf);
Vector3 voxelCorner_101 = new Vector3(p0.x + x * voxelSize + voxelSizeHalf, p0.y + y * voxelSize - voxelSizeHalf, p0.z + z * voxelSize + voxelSizeHalf);
Vector3 voxelCorner_110 = new Vector3(p0.x + x * voxelSize + voxelSizeHalf, p0.y + y * voxelSize + voxelSizeHalf, p0.z + z * voxelSize - voxelSizeHalf);
Vector3 voxelCorner_111 = new Vector3(p0.x + x * voxelSize + voxelSizeHalf, p0.y + y * voxelSize + voxelSizeHalf, p0.z + z * voxelSize + voxelSizeHalf);
*/
Manager.helper.DrawCube(voxelWorldPos, voxelSize, Color.magenta, delay);
/*
Vector3 ray_z_positive_origin = voxelWorldPos - Vector3.forward * voxelSize;
Vector3 ray_z_positive_dir = Vector3.forward;
Ray ray_z_positive = new Ray(ray_z_positive_origin, ray_z_positive_dir);
DrawCross(ray_z_positive_origin, voxelSizeHalf / 4, Color.green, delay);
//Debug.DrawRay(ray_z_positive_origin, ray_z_positive_dir, Color.green, delay);
RaycastHit ray_z_positive_hit = new RaycastHit();
if(Physics.Raycast(ray_z_positive_origin, ray_z_positive_dir, out ray_z_positive_hit, voxelSize, layerMask)) {
Debug.DrawLine(ray_z_positive_origin, ray_z_positive_hit.point, Color.red, delay);
//Center of the voxelspace that was hit
Vector3 insideHit_aligned_center = WorldToVoxelSpace(ray_z_positive_hit.point + ray_z_positive_dir * voxelSizeHalf, resolution, true);
//Debug.Log("insideHit_aligned_center: " + insideHit_aligned_center.x + " , " + insideHit_aligned_center.y + " , " + insideHit_aligned_center.z);
//the xyz position of the voxel that was hit, in GLOBAL voxelspace
Vector3 insideHit_voxelSpace = VoxelToLocalVoxelSpace(insideHit_aligned_center, resolution, Vector3.zero);
//Debug.Log("insideHit_voxelSpace: " + insideHit_voxelSpace.x + " , " + insideHit_voxelSpace.y + " , " + insideHit_voxelSpace.z);
//the voxelspace xyz position in LOCAL voxelspace
Coordinate voxelPos = new Coordinate((int)(insideHit_voxelSpace.x - p0_voxelSpace.x), (int)(insideHit_voxelSpace.y - p0_voxelSpace.y), (int)(insideHit_voxelSpace.z - p0_voxelSpace.z));
//Debug.Log("voxelPos: " + voxelPos.x + " , " + voxelPos.y + " , " + voxelPos.z);
VoxelSpace voxelSpace = voxelSpaceArray[voxelPos.x, voxelPos.y, voxelPos.z];
voxelSpace.hit_s = true;
if(voxelSpace.filled == false) {
voxelSpace.filled = true;
Vector2 pixelUV = ray_z_positive_hit.textureCoord;
pixelUV.x *= texture.width;
pixelUV.y *= texture.height;
Color color = texture.GetPixel((int)pixelUV.x, (int)pixelUV.y);
CreateVoxelInArray(voxelSpace, insideHit_aligned_center, color, resolution, go_vox_root.transform);
}
}
*/
Vector3[] ray_origin_array = new Vector3[] {
voxelWorldPos - Vector3.forward * voxelSize,
voxelWorldPos + Vector3.forward * voxelSize,
voxelWorldPos - Vector3.right * voxelSize,
voxelWorldPos + Vector3.right * voxelSize,
voxelWorldPos - Vector3.up * voxelSize,
voxelWorldPos + Vector3.up * voxelSize
};
Vector3[] ray_dir_array = new Vector3[] {
Vector3.forward,
-Vector3.forward,
Vector3.right,
-Vector3.right,
Vector3.up,
-Vector3.up
};
int i = 0;
for(i = 0; i < 6; i++) {
Manager.helper.DrawCross(ray_origin_array[i], voxelSizeHalf / 4, Color.green, delay);
//Debug.DrawRay(ray_origin_array[i], ray_dir_array[i], Color.green, delay);
Ray ray = new Ray(ray_origin_array[i], ray_dir_array[i]);
RaycastHit hit = new RaycastHit();
RaycastHit[] hitArray = Physics.RaycastAll(ray, voxelSize, layerMask).OrderBy(h=>h.distance).ToArray();
bool hasHit = false;
foreach(RaycastHit hitCur in hitArray) {
if(hitCur.collider.gameObject == goClone) {
hasHit = true;
hit = hitCur;
break;
}
}
//if(Physics.Raycast(ray, out hit, voxelSize, layerMask)) {
if(hasHit == true) {
Debug.DrawLine(ray.origin, hit.point, Color.red, delay);
//Center of the voxelspace that was hit
Vector3 insideHit_aligned_center = WorldToVoxelSpace(hit.point + ray.direction * voxelSizeHalf, resolution, true);
//Debug.Log("insideHit_aligned_center: " + insideHit_aligned_center.x + " , " + insideHit_aligned_center.y + " , " + insideHit_aligned_center.z);
//the xyz position of the voxel that was hit, in GLOBAL voxelspace
Vector3 insideHit_voxelSpace = VoxelToLocalVoxelSpace(insideHit_aligned_center, resolution, Vector3.zero);
//Debug.Log("insideHit_voxelSpace: " + insideHit_voxelSpace.x + " , " + insideHit_voxelSpace.y + " , " + insideHit_voxelSpace.z);
//the voxelspace xyz position in LOCAL voxelspace
Coordinate voxelPos = new Coordinate((int)(insideHit_voxelSpace.x - p0_voxelSpace.x), (int)(insideHit_voxelSpace.y - p0_voxelSpace.y), (int)(insideHit_voxelSpace.z - p0_voxelSpace.z));
//Debug.Log("voxelPos: " + voxelPos.x + " , " + voxelPos.y + " , " + voxelPos.z);
VoxelSpace voxelSpace = voxelSpaceArray[voxelPos.x, voxelPos.y, voxelPos.z];
if(i == 0) {voxelSpace.hit_s = true;}
else if(i == 1) {voxelSpace.hit_n = true;}
else if(i == 2) {voxelSpace.hit_w = true;}
else if(i == 3) {voxelSpace.hit_e = true;}
else if(i == 4) {voxelSpace.hit_b = true;}
else if(i == 5) {voxelSpace.hit_t = true;}
if(voxelSpace.filled == false) {
voxelSpace.filled = true;
if(fillPercentage_shell > 0) {
if(UnityEngine.Random.Range(0f, 100f) <= fillPercentage_shell) {
Vector2 pixelUV = hit.textureCoord;
pixelUV.x *= texture.width;
pixelUV.y *= texture.height;
Color color = texture.GetPixel((int)pixelUV.x, (int)pixelUV.y);
CreateVoxelInArray(voxelSpace, insideHit_aligned_center, color, resolution, go_vox_root.transform);
}
}
}
}
}
if(waitForTime == true) {
if(delay > 0) {
yield return new WaitForSeconds(delay);
}
}
else if(waitOneFrame == true) {
yield return null;
}
}
}
}
//Fill in inner voxels
if(fillInside == true) {
for(x = 0; x < voxelSpaceArray.GetLength(0); x++) {
for(y = 0; y < voxelSpaceArray.GetLength(1); y++) {
for(z = 0; z < voxelSpaceArray.GetLength(2); z++) {
if(waitForTime == true) {
if(delay > 0) {
yield return new WaitForSeconds(delay);
}
}
else if(waitOneFrame == true) {
yield return null;
}
VoxelSpace voxelSpace = voxelSpaceArray[x,y,z];
Vector3 voxelWorldPos = new Vector3(p0.x + x * voxelSize, p0.y + y * voxelSize, p0.z + z * voxelSize);
Manager.helper.DrawCube(voxelWorldPos, voxelSize, Color.magenta, delay);
if(voxelSpace.filled == false) {
continue;
}
if(z + 1 >= voxelSpaceArray.GetLength(2)) {
continue;
}
int i = 0;
int toFill = 0;
for(i = z + 1; i < voxelSpaceArray.GetLength(2); i++) {
if(i == z + 1 && voxelSpaceArray[x,y,i].filled == true) {
break;
}
toFill ++;
if(i == voxelSpaceArray.GetLength(2) - 1 && voxelSpaceArray[x,y,i].filled == false) {
toFill = 0;
break;
}
if(voxelSpaceArray[x,y,i].filled == true) {
break;
}
}
if(toFill > 0) {
for(i = z + 1; i < z + 1 + toFill; i++) {
Vector3 voxelWorldPos_temp = new Vector3(p0.x + x * voxelSize, p0.y + y * voxelSize, p0.z + i * voxelSize);
Manager.helper.DrawCube(voxelWorldPos_temp, voxelSize, Color.yellow, delay);
Vector3 pos = new Vector3(p0.x + x * voxelSize, p0.y + y * voxelSize, p0.z + i * voxelSize);
if(voxelSpaceArray[x,y,i].filled == false) {
if(fillPercentage_inside > 0) {
if(UnityEngine.Random.Range(0f, 100f) > fillPercentage_inside) {
continue;
}
}
else {
continue;
}
Color insideColor = Color.white;
if(insideColorArray.Length > 0) {
if(insideColorArray.Length == 1) {
insideColor = insideColorArray[0];
}
else {
insideColor = insideColorArray[UnityEngine.Random.Range (0, insideColorArray.Length)];
}
}
CreateVoxelInArray(voxelSpaceArray[x,y,i], pos, insideColor, resolution, go_vox_root.transform);
}
if(waitForTime == true) {
if(delay > 0) {
yield return new WaitForSeconds(delay);
}
}
}
break;
}
}
}
}
}
go_vox_root.transform.parent = goClone.transform;
goClone.transform.position = go.transform.position;
go_vox_root.transform.parent = null;
if (Application.isPlaying) {
Destroy(goClone);
}
else {
DestroyImmediate(goClone);
}
go.SetActive(true);
/*
go.GetComponent<Collider>().enabled = true;//Not neccessary anymore since RayCastAll is used to get multiple hits on teh same layer, and then all hits are checked if they belong to goClone
if(go.GetComponent<Rigidbody>()) {
go.GetComponent<Rigidbody>().isKinematic = false;//Not neccessary anymore since RayCastAll is used to get multiple hits on teh same layer, and then all hits are checked if they belong to goClone
}
*/
go_vox_root.AddComponent<MeshFilter>();
go_vox_root.GetComponent<MeshFilter>().mesh = mesh;
voxelizedObject = go_vox_root;
voxelizedObject.GetComponent<VoxelObject>().SerializeVoxelSpaceArray();
if(createPrefab == true) {
PrefabUtility.CreatePrefab("Assets/Resources/" + prefabPath + "/" + go_vox_root.name + ".prefab", voxelizedObject, ReplacePrefabOptions.ReplaceNameBased);
}
if(keepVoxelizedObject == false) {
if (Application.isPlaying) {
Destroy(go_vox_root);
}
else {
DestroyImmediate(go_vox_root);
}
}
else {
voxelizedObject.GetComponent<VoxelObject>().AssignColors();
}
yield return null;
}
public Vector3 WorldToVoxelSpace(Vector3 worldPos, int resolution, bool center) {
return WorldToVoxelSpace(worldPos.x, worldPos.y, worldPos.z, resolution, center);
}
public Vector3 WorldToVoxelSpace(float worldPos_x, float worldPos_y, float worldPos_z, int resolution, bool center) {
return new Vector3(WorldToVoxelSpace(worldPos_x, resolution, center), WorldToVoxelSpace(worldPos_y, resolution, center), WorldToVoxelSpace(worldPos_z, resolution, center));
}
public float WorldToVoxelSpace(float worldPos, int resolution, bool center) {
float voxelSize = 1f / resolution;
float offset = 0;
if(center == true) {
offset = voxelSize / 2;
}
float voxelSizeHalf = voxelSize / 2;
return (float)(Math.Round((worldPos + voxelSizeHalf) * resolution) / (float)resolution) - offset;
}
public Vector3 VoxelToLocalVoxelSpace(Vector3 voxelSpacePos, int resolution, Vector3 pos_min) {
return VoxelToLocalVoxelSpace(voxelSpacePos.x, voxelSpacePos.y, voxelSpacePos.z, resolution, pos_min);
}
public Vector3 VoxelToLocalVoxelSpace(float voxelSpacePos_x, float voxelSpacePos_y, float voxelSpacePos_z, int resolution, Vector3 pos_min) {
return new Vector3(VoxelToLocalVoxelSpace(voxelSpacePos_x, resolution, pos_min.x), VoxelToLocalVoxelSpace(voxelSpacePos_y, resolution, pos_min.y),VoxelToLocalVoxelSpace(voxelSpacePos_z, resolution, pos_min.z));
}
public float VoxelToLocalVoxelSpace(float pos, int resolution, float pos_min) {
float voxelSize = 1f / resolution;
return Mathf.Floor(pos / voxelSize);
}
public void CreateVoxelInArray(VoxelSpace voxelSpace, Vector3 pos, Color color, int resolution, Transform parent) {
float voxelSize = 1f / resolution;
voxelSpace.pos = pos;
voxelSpace.filled = true;
voxelSpace.color = color;
//voxelSpace.voxel = GameObject.CreatePrimitive(PrimitiveType.Cube);
voxelSpace.voxel = Instantiate(voxelPrefab, pos, Quaternion.identity) as GameObject;
voxelSpace.voxel.layer = LayerMask.NameToLayer("Voxel");
voxelSpace.voxel.transform.localScale = new Vector3(voxelSize, voxelSize, voxelSize);
//voxelSpace.voxel.GetComponent<MeshRenderer>().material.color = color;
voxelSpace.voxel.transform.parent = parent;
}
public void CreateVoxelInArray(VoxelSpace voxelSpace, Vector3 pos, Vector2 textureCoords, int resolution, Transform parent) {
float voxelSize = 1f / resolution;
voxelSpace.pos = pos;
voxelSpace.filled = true;
voxelSpace.color = Color.red;
//voxelSpace.voxel = GameObject.CreatePrimitive(PrimitiveType.Cube);
voxelSpace.voxel = Instantiate(voxelPrefab, pos, Quaternion.identity) as GameObject;
//voxelSpace.voxel.layer = LayerMask.NameToLayer("Voxel");
voxelSpace.voxel.transform.localScale = new Vector3(voxelSize, voxelSize, voxelSize);
voxelSpace.voxel.transform.parent = parent;
Mesh mesh = voxelSpace.voxel.GetComponent<MeshFilter>().sharedMesh;
Vector2[] uvs = new Vector2[mesh.uv.Length];
for(int i = 0; i < uvs.Length; i++) {
uvs[i] = textureCoords;
}
mesh.uv = uvs;
voxelSpace.voxel.GetComponent<MeshFilter>().sharedMesh = mesh;
}
}
VoxelObject.cs: This will get added to the voxelized object by script, so you don’t have to add it manually. Just Put the file somewhere in your Assets folder.
using UnityEngine;
using System.Collections;
public class VoxelObject : MonoBehaviour {
public VoxelSpace[,,] voxelSpaceArray;
public X[] voxelSpaceArray2;
void Start() {
if(voxelSpaceArray2 != null) {
AssignColors();
}
}
public void AssignColors() {
if(voxelSpaceArray2 == null) {
return;
}
for(int x = 0; x < voxelSpaceArray2.Length; x++) {
for(int y = 0; y < voxelSpaceArray2[x].y.Length; y++) {
for(int z = 0; z < voxelSpaceArray2[x].y[y].z.Length; z++) {
VoxelSpace voxelSpace = voxelSpaceArray2[x].y[y].z[z];
GameObject voxel = voxelSpace.voxel;
if(voxel == null) {
continue;
}
if(Application.isPlaying == true) {
voxel.GetComponent<Renderer>().material.color = voxelSpace.color;
}
else {
voxel.GetComponent<Renderer>().sharedMaterial.color = voxelSpace.color;
}
}
}
}
}
public void SerializeVoxelSpaceArray() {
if(voxelSpaceArray == null) {
return;
}
int x = 0;
int y = 0;
int z = 0;
voxelSpaceArray2 = new X[voxelSpaceArray.GetLength(0)];
for(x = 0; x < voxelSpaceArray.GetLength(0); x++) {
voxelSpaceArray2[x] = new X();
voxelSpaceArray2[x].y = new Y[voxelSpaceArray.GetLength(1)];
for(y = 0; y < voxelSpaceArray.GetLength(1); y++) {
voxelSpaceArray2[x].y[y] = new Y();
voxelSpaceArray2[x].y[y].z = new VoxelSpace[voxelSpaceArray.GetLength(2)];
for(z = 0; z < voxelSpaceArray.GetLength(2); z++) {
voxelSpaceArray2[x].y[y].z[z] = new VoxelSpace();
VoxelSpace voxelSpace = voxelSpaceArray[x,y,z];
voxelSpaceArray2[x].y[y].z[z] = new VoxelSpace();
VoxelSpace voxelSpace2 = voxelSpaceArray2[x].y[y].z[z];
//voxelSpace2 = voxelSpace;//don't call this; it will erase all references after the loops are finished; Assign references by hand further down below
if(voxelSpace.voxel == null) {
continue;
}
voxelSpace2.voxel = voxelSpace.voxel;
voxelSpace2.color = voxelSpace.color;
}
}
}
}
}
[System.Serializable]
public class X {
public Y[] y;
public X() {
y = new Y[1];
}
}
[System.Serializable]
public class Y {
public VoxelSpace[] z;
public Y() {
z = new VoxelSpace[1];
}
}
[System.Serializable]
public class VoxelSpace {
public Vector3 pos;
public bool filled = false;
public bool hit_t;
public bool hit_n;
public bool hit_e;
public bool hit_s;
public bool hit_w;
public bool hit_b;
public GameObject voxel;
public Color color;
}
VoxelizerWindow.cs: Editor Script. Put it into your Editor folder. You can open the window with Windows/Voxelizer. Use it to actually voxelize an object.
createPrefab: Should a prefab of the voxelized object be created?
keepVoxelizedObject: Should the voxelized obejct be destroyed after voxelizing (useful if you just want to create a prefab without needing the voxelized object in your scene right now)
fillInside: Should the inside of the voxelized object be filled with voxels, too? (Performance heavy depending on the size of the object). If no, then only the outer shell will be made out of voxels.
fillPercentage_shell: What percentage of the outer voxels will actually be created.
fillPercentage_inside: If the inside is filled, what percentage of the inner voxels will be created.
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System;
class VoxelizerWindow : EditorWindow {
[MenuItem ("Window/Voxelizer")]
public static void ShowWindow () {
EditorWindow.GetWindow(typeof(VoxelizerWindow), false, "Voxelizer", true);
}
private int buttonSize = 32;
private Vector2 scrollPos_window = Vector2.zero;
private GameObject selectedObject;
private Voxelizer voxelizer;
private bool createPrefab = true;
private string prefabPath = "Models";
private bool keepVoxelizedObject = false;
private bool useMeshName = true;
private bool fillInside = true;
private float fillPercentage_shell = 100;
private float fillPercentage_inside = 100;
void Update() {
if(Selection.activeObject != null) {
selectedObject = (GameObject)Selection.activeObject;
if(selectedObject.GetComponent<VoxelObject>()) {
}
}
else {
selectedObject = null;
}
}
public void OnInspectorUpdate() {
// This will only get called 10 times per second.
Repaint();
}
void OnFocus() {
GameObject managerObject = GameObject.Find("Manager");
if(managerObject.GetComponent<Voxelizer>()) {
voxelizer = managerObject.GetComponent<Voxelizer>();
}
else {
Debug.Log("Can't find Voxelizer component on Manager!");
}
}
void OnGUI () {
if(voxelizer == null) {
GUILayout.Label("Voxelizer not found.", EditorStyles.boldLabel);
return;
}
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
string selectedGameObjectName = "No GameObject selected.";
if(selectedObject!= null) {
selectedGameObjectName = Selection.activeGameObject.name;
}
GUILayout.Label(selectedGameObjectName, EditorStyles.boldLabel);
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
GUILayout.Box("", new GUILayoutOption[]{GUILayout.ExpandWidth(true), GUILayout.Height(1)});
voxelizer.voxelPrefab = EditorGUILayout.ObjectField("Voxel Prefab", voxelizer.voxelPrefab, typeof(GameObject), true, GUILayout.ExpandWidth(true)) as GameObject;
if(GUILayout.Button(new GUIContent("Load Voxel Prefab", ""), GUILayout.Width(buttonSize * 4))) {
voxelizer.LoadVoxelPrefab();
}
createPrefab = EditorGUILayout.BeginToggleGroup( new GUIContent("Create Prefab", ""), createPrefab);
prefabPath = EditorGUILayout.TextField("Prefab Path", prefabPath);
EditorGUILayout.EndToggleGroup();
if(GUILayout.Toggle(keepVoxelizedObject, new GUIContent("Keep Voxelized Object", ""), GUILayout.Width(buttonSize * 6)) != keepVoxelizedObject) {
Event.current.Use();
keepVoxelizedObject = !keepVoxelizedObject;
}
if(GUILayout.Toggle(useMeshName, new GUIContent("Use Mesh Name", ""), GUILayout.Width(buttonSize * 6)) != useMeshName) {
Event.current.Use();
useMeshName = !useMeshName;
}
if(GUILayout.Toggle(fillInside, new GUIContent("Fill Inside", ""), GUILayout.Width(buttonSize * 6)) != fillInside) {
Event.current.Use();
fillInside = !fillInside;
}
fillPercentage_shell = EditorGUILayout.FloatField("Fill Percentage (Shell)", fillPercentage_shell);
fillPercentage_inside = EditorGUILayout.FloatField("Fill Percentage (Inside)", fillPercentage_inside);
GUILayout.Box("", new GUILayoutOption[]{GUILayout.ExpandWidth(true), GUILayout.Height(1)});
if(GUILayout.Button(new GUIContent("Voxelize!", ""), GUILayout.Width(buttonSize * 4))) {
if(voxelizer.voxelPrefab == null) {
Debug.Log("voxelizer.voxelPrefab is null!");
return;
}
voxelizer.StartVoxelize(selectedObject, 16, fillInside, fillPercentage_shell, fillPercentage_inside, new Color[]{Color.red}, createPrefab, prefabPath, keepVoxelizedObject, useMeshName);
}
scrollPos_window = EditorGUILayout.BeginScrollView(scrollPos_window);
EditorGUILayout.EndScrollView();
}
}