Skip to content

Commit 0af309d

Browse files
authored
Merge pull request mlavik1#1 from vahpy/vahpy-new-features
Adding downscaling feature
2 parents 22c3e6a + d654f84 commit 0af309d

File tree

1 file changed

+164
-6
lines changed

1 file changed

+164
-6
lines changed

Assets/Scripts/VolumeData/VolumeDataset.cs

Lines changed: 164 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System;
1+
using System;
22
using UnityEngine;
33

44
namespace UnityVolumeRendering
@@ -13,9 +13,13 @@ public class VolumeDataset : ScriptableObject
1313
[SerializeField]
1414
public int[] data = null;
1515

16+
// Flattened 3D array of downscaled data sample values.
1617
[SerializeField]
17-
public int dimX, dimY, dimZ;
18-
18+
public float[] downScaledData = null;
19+
20+
[SerializeField]
21+
public int dimX, dimY, dimZ, halfDimX, halfDimY, halfDimZ;
22+
1923
[SerializeField]
2024
public float scaleX = 0.0f, scaleY = 0.0f, scaleZ = 0.0f;
2125

@@ -26,12 +30,23 @@ public class VolumeDataset : ScriptableObject
2630
private int maxDataValue = int.MinValue;
2731
private Texture3D dataTexture = null;
2832
private Texture3D gradientTexture = null;
33+
private bool downScaled = false;
2934

3035
public Texture3D GetDataTexture()
36+
{
37+
return GetDataTexture(false);
38+
}
39+
40+
public Texture3D GetDataTexture(bool forceDownscaling)
3141
{
3242
if (dataTexture == null)
3343
{
34-
dataTexture = CreateTextureInternal();
44+
if (forceDownscaling /* || some conditions */)
45+
{
46+
dataTexture = CreateDownScaledTextureInternal();
47+
downScaled = true;
48+
}
49+
else dataTexture = CreateTextureInternal();
3550
}
3651
return dataTexture;
3752
}
@@ -40,7 +55,8 @@ public Texture3D GetGradientTexture()
4055
{
4156
if (gradientTexture == null)
4257
{
43-
gradientTexture = CreateGradientTextureInternal();
58+
if (downScaled) gradientTexture = CreateDownScaledGradientTextureInternal();
59+
else gradientTexture = CreateGradientTextureInternal();
4460
}
4561
return gradientTexture;
4662
}
@@ -112,7 +128,7 @@ private Texture3D CreateTextureInternal()
112128
TextureFormat texformat = SystemInfo.SupportsTextureFormat(TextureFormat.RHalf) ? TextureFormat.RHalf : TextureFormat.RFloat;
113129
Texture3D texture = new Texture3D(dimX, dimY, dimZ, texformat, false);
114130
texture.wrapMode = TextureWrapMode.Clamp;
115-
131+
116132
int minValue = GetMinDataValue();
117133
int maxValue = GetMaxDataValue();
118134
int maxRange = maxValue - minValue;
@@ -197,5 +213,147 @@ private Texture3D CreateGradientTextureInternal()
197213
texture.Apply();
198214
return texture;
199215
}
216+
217+
218+
private Texture3D CreateDownScaledTextureInternal()
219+
{
220+
halfDimX = dimX / 2 + dimX % 2;
221+
halfDimY = dimY / 2 + dimY % 2;
222+
halfDimZ = dimZ / 2 + dimZ % 2;
223+
224+
TextureFormat texformat = SystemInfo.SupportsTextureFormat(TextureFormat.RHalf) ? TextureFormat.RHalf : TextureFormat.RFloat;
225+
226+
Texture3D texture = new Texture3D(halfDimX, halfDimY, halfDimZ, texformat, false);
227+
texture.wrapMode = TextureWrapMode.Clamp;
228+
229+
downScaledData = new float[halfDimX * halfDimY * halfDimZ];
230+
231+
int minValue = GetMinDataValue();
232+
int maxValue = GetMaxDataValue();
233+
int maxRange = maxValue - minValue;
234+
235+
bool isHalfFloat = texformat == TextureFormat.RHalf;
236+
int sampleSize = isHalfFloat ? 2 : 4;
237+
byte[] bytes;
238+
try
239+
{
240+
// Create a byte array for filling the texture. Store has half (16 bit) or single (32 bit) float values.
241+
bytes = new byte[halfDimX * halfDimY * halfDimZ * sampleSize]; // This can cause OutOfMemoryException
242+
}
243+
catch (OutOfMemoryException ex)
244+
{
245+
Debug.LogWarning("Out of memory when creating texture. Using fallback method.");
246+
bytes = null;
247+
}
248+
249+
for (int x = 0; x < halfDimX; x++)
250+
{
251+
for (int y = 0; y < halfDimY; y++)
252+
{
253+
for (int z = 0; z < halfDimZ; z++)
254+
{
255+
float avg = GetAvgerageVoxelValues(x * 2, y * 2, z * 2);
256+
int index = x + y * halfDimX + z * (halfDimX * halfDimY);
257+
downScaledData[index] = (avg - minValue) / maxRange; //Store downscaled data for gradient texture generation, etc.
258+
259+
if (bytes == null)
260+
{
261+
texture.SetPixel(x, y, z, new Color(downScaledData[index], 0.0f, 0.0f, 0.0f));
262+
}
263+
else
264+
{
265+
byte[] pixelBytes = isHalfFloat ? BitConverter.GetBytes(Mathf.FloatToHalf(downScaledData[index])) : BitConverter.GetBytes(downScaledData[index]);
266+
Array.Copy(pixelBytes, 0, bytes, index * sampleSize, sampleSize);
267+
}
268+
}
269+
}
270+
}
271+
272+
if (bytes != null) texture.SetPixelData(bytes, 0);
273+
274+
texture.Apply();
275+
return texture;
276+
}
277+
278+
private Texture3D CreateDownScaledGradientTextureInternal()
279+
{
280+
if (!downScaled || downScaledData == null) throw new InvalidOperationException("Downscaled gradient texture must be generated after downscaled main texture!");
281+
282+
TextureFormat texformat = SystemInfo.SupportsTextureFormat(TextureFormat.RGBAHalf) ? TextureFormat.RGBAHalf : TextureFormat.RGBAFloat;
283+
Texture3D texture = new Texture3D(halfDimX, halfDimY, halfDimZ, texformat, false);
284+
texture.wrapMode = TextureWrapMode.Clamp;
285+
286+
Color[] cols;
287+
try
288+
{
289+
cols = new Color[downScaledData.Length];
290+
}
291+
catch (OutOfMemoryException ex)
292+
{
293+
cols = null;
294+
}
295+
for (int x = 0; x < halfDimX; x++)
296+
{
297+
for (int y = 0; y < halfDimY; y++)
298+
{
299+
for (int z = 0; z < halfDimZ; z++)
300+
{
301+
int iData = x + y * halfDimX + z * (halfDimX * halfDimY);
302+
303+
float x1 = downScaledData[Math.Min(x + 1, halfDimX - 1) + y * halfDimX + z * (halfDimX * halfDimY)];
304+
float x2 = downScaledData[Math.Max(x - 1, 0) + y * halfDimX + z * (halfDimX * halfDimY)];
305+
float y1 = downScaledData[x + Math.Min(y + 1, halfDimY - 1) * halfDimX + z * (halfDimX * halfDimY)];
306+
float y2 = downScaledData[x + Math.Max(y - 1, 0) * halfDimX + z * (halfDimX * halfDimY)];
307+
float z1 = downScaledData[x + y * halfDimX + Math.Min(z + 1, halfDimZ - 1) * (halfDimX * halfDimY)];
308+
float z2 = downScaledData[x + y * halfDimX + Math.Max(z - 1, 0) * (halfDimX * halfDimY)];
309+
310+
Vector3 grad = new Vector3(x2 - x1, y2 - y1, z2 - z1);
311+
312+
if (cols == null)
313+
{
314+
texture.SetPixel(x, y, z, new Color(grad.x, grad.y, grad.z, downScaledData[iData]));
315+
}
316+
else
317+
{
318+
cols[iData] = new Color(grad.x, grad.y, grad.z, downScaledData[iData]);
319+
}
320+
}
321+
}
322+
}
323+
if (cols != null) texture.SetPixels(cols);
324+
texture.Apply();
325+
return texture;
326+
}
327+
328+
public float GetAvgerageVoxelValues(int x, int y, int z)
329+
{
330+
// if a dimension length is not an even number
331+
bool xC = x + 1 == dimX;
332+
bool yC = y + 1 == dimY;
333+
bool zC = z + 1 == dimZ;
334+
335+
//if expression can only be true on the edges of the texture
336+
if (xC || yC || zC)
337+
{
338+
if (!xC && yC && zC) return (GetData(x, y, z) + GetData(x + 1, y, z)) / 2.0f;
339+
else if (xC && !yC && zC) return (GetData(x, y, z) + GetData(x, y + 1, z)) / 2.0f;
340+
else if (xC && yC && !zC) return (GetData(x, y, z) + GetData(x, y, z + 1)) / 2.0f;
341+
else if (!xC && !yC && zC) return (GetData(x, y, z) + GetData(x + 1, y, z) + GetData(x, y + 1, z) + GetData(x + 1, y + 1, z)) / 4.0f;
342+
else if (!xC && yC && !zC) return (GetData(x, y, z) + GetData(x + 1, y, z) + GetData(x, y, z + 1) + GetData(x + 1, y, z + 1)) / 4.0f;
343+
else if (xC && !yC && !zC) return (GetData(x, y, z) + GetData(x, y + 1, z) + GetData(x, y, z + 1) + GetData(x, y + 1, z + 1)) / 4.0f;
344+
else return GetData(x, y, z); // if xC && yC && zC
345+
}
346+
return (GetData(x, y, z) + GetData(x + 1, y, z) + GetData(x, y + 1, z) + GetData(x + 1, y + 1, z)
347+
+ GetData(x, y, z + 1) + GetData(x, y + 1, z + 1) + GetData(x + 1, y, z + 1) + GetData(x + 1, y + 1, z + 1)) / 8.0f;
348+
}
349+
350+
public int GetData(int x, int y, int z)
351+
{
352+
return data[x + y * dimX + z * (dimX * dimY)];
353+
}
354+
public float GetDownScaledData(int x, int y, int z)
355+
{
356+
return downScaledData[x + y * halfDimX + z * (halfDimX * halfDimY)];
357+
}
200358
}
201359
}

0 commit comments

Comments
 (0)