-
Notifications
You must be signed in to change notification settings - Fork 0
/
VolumeViewerMarcher.cs
141 lines (113 loc) · 4.9 KB
/
VolumeViewerMarcher.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
/* Semester: Spring 2018
*/
using UnityEngine;
using System.Collections.Generic;
using MarchingCubesProject;
using VolumeViewer;
public enum MARCHING_MODE { CUBES, TETRAHEDRON };
[RequireComponent(typeof(VolumeComponent))]
/* Semester: Spring 2018
|------------------ public class VolumeViewerMarcher-------------------
| This class allows the DICOM image, once any modifications needed are
| made, to be converted into a mesh, which can then be converted into
| an STL.
*/
public class VolumeViewerMarcher : MonoBehaviour
{
public Material m_material;
public MARCHING_MODE mode = MARCHING_MODE.CUBES;
public int seed = 0;
List<GameObject> meshes = new List<GameObject>();
public void ConvertToMesh(float cutoff = 0.5f)
{
//Set the mode used to create the mesh.
//Cubes is faster and creates less verts, tetrahedrons is slower and creates more verts but better represents the mesh surface.
Marching marching = null;
if (mode == MARCHING_MODE.TETRAHEDRON)
marching = new MarchingTertrahedron();
else
marching = new MarchingCubes();
//Surface is the value that represents the surface of mesh
//For example the perlin noise has a range of -1 to 1 so the mid point is where we want the surface to cut through.
//The target value does not have to be the mid point it can be any value with in the range.
marching.Surface = cutoff;
VolumeFileLoader vfl = GetComponent<VolumeFileLoader>();
//The size of voxel array.
int width = vfl.dataVolume.nx;
int height = vfl.dataVolume.ny;
int length = vfl.dataVolume.nz;
float[] voxels = new float[width * height * length];
Color[] pixels = vfl.dataVolume.texture.GetPixels();
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
for (int z = 0; z < length; z++)
{
float fx = x / (width - 1.0f);
float fy = y / (height - 1.0f);
float fz = z / (length - 1.0f);
int idx = x + y * width + z * width * height;
Color pixel = pixels[idx];
if (pixel.a > 0f)
{
voxels[idx] = pixel.a;
}
}
}
}
List<Vector3> verts = new List<Vector3>();
List<int> indices = new List<int>();
Debug.Log("Starting marching cubes...");
//The mesh produced is not optimal. There is one vert for each index.
//Would need to weld vertices for better quality mesh.
marching.Generate(voxels, width, height, length, verts, indices);
/*A mesh in unity can only be made up of 65000 verts,
| and need to split the verts between multiple meshes. */
int maxVertsPerMesh = 30000; //must be divisible by 3, ie 3 verts == 1 triangle
int numMeshes = verts.Count / maxVertsPerMesh + 1;
Debug.Log(verts.Count + " vertices scanned.");
GameObject marchingObject = new GameObject("MarchingObject");
marchingObject.transform.parent = transform;
for (int i = 0; i < numMeshes; i++)
{
List<Vector3> splitVerts = new List<Vector3>();
List<int> splitIndices = new List<int>();
for (int j = 0; j < maxVertsPerMesh; j++)
{
int idx = i * maxVertsPerMesh + j;
if (idx < verts.Count)
{
splitVerts.Add(verts[idx]);
splitIndices.Add(j);
}
}
if (splitVerts.Count == 0) continue;
/*
| Creates mesh with default vertices and edges
*/
Mesh mesh = new Mesh();
mesh.SetVertices(splitVerts);
mesh.SetTriangles(splitIndices, 0);
mesh.RecalculateBounds();
mesh.RecalculateNormals();
/*
|
*/
GameObject go = new GameObject("Mesh");
go.transform.parent = marchingObject.transform;
go.AddComponent<MeshFilter>();
go.AddComponent<MeshRenderer>();
go.GetComponent<Renderer>().material = m_material;
go.GetComponent<MeshFilter>().mesh = mesh;
go.transform.localRotation = Quaternion.Euler(Vector3.zero);
go.transform.localPosition = new Vector3((float)-width / 2, (float)-height / 2, (float)-length / 2);
meshes.Add(go);
}
marchingObject.transform.localPosition = Vector3.zero;
marchingObject.transform.localRotation = Quaternion.Euler(Vector3.zero);
marchingObject.transform.localScale = new Vector3(1f / width, 1f / height, 1f / length);
Debug.Log("Finishing marching cubes...");
GetComponent<MeshRenderer>().enabled = false;
}
}