1
- using System ;
1
+ using System ;
2
2
using UnityEngine ;
3
3
4
4
namespace UnityVolumeRendering
@@ -13,9 +13,13 @@ public class VolumeDataset : ScriptableObject
13
13
[ SerializeField ]
14
14
public int [ ] data = null ;
15
15
16
+ // Flattened 3D array of downscaled data sample values.
16
17
[ 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
+
19
23
[ SerializeField ]
20
24
public float scaleX = 0.0f , scaleY = 0.0f , scaleZ = 0.0f ;
21
25
@@ -26,12 +30,23 @@ public class VolumeDataset : ScriptableObject
26
30
private int maxDataValue = int . MinValue ;
27
31
private Texture3D dataTexture = null ;
28
32
private Texture3D gradientTexture = null ;
33
+ private bool downScaled = false ;
29
34
30
35
public Texture3D GetDataTexture ( )
36
+ {
37
+ return GetDataTexture ( false ) ;
38
+ }
39
+
40
+ public Texture3D GetDataTexture ( bool forceDownscaling )
31
41
{
32
42
if ( dataTexture == null )
33
43
{
34
- dataTexture = CreateTextureInternal ( ) ;
44
+ if ( forceDownscaling /* || some conditions */ )
45
+ {
46
+ dataTexture = CreateDownScaledTextureInternal ( ) ;
47
+ downScaled = true ;
48
+ }
49
+ else dataTexture = CreateTextureInternal ( ) ;
35
50
}
36
51
return dataTexture ;
37
52
}
@@ -40,7 +55,8 @@ public Texture3D GetGradientTexture()
40
55
{
41
56
if ( gradientTexture == null )
42
57
{
43
- gradientTexture = CreateGradientTextureInternal ( ) ;
58
+ if ( downScaled ) gradientTexture = CreateDownScaledGradientTextureInternal ( ) ;
59
+ else gradientTexture = CreateGradientTextureInternal ( ) ;
44
60
}
45
61
return gradientTexture ;
46
62
}
@@ -112,7 +128,7 @@ private Texture3D CreateTextureInternal()
112
128
TextureFormat texformat = SystemInfo . SupportsTextureFormat ( TextureFormat . RHalf ) ? TextureFormat . RHalf : TextureFormat . RFloat ;
113
129
Texture3D texture = new Texture3D ( dimX , dimY , dimZ , texformat , false ) ;
114
130
texture . wrapMode = TextureWrapMode . Clamp ;
115
-
131
+
116
132
int minValue = GetMinDataValue ( ) ;
117
133
int maxValue = GetMaxDataValue ( ) ;
118
134
int maxRange = maxValue - minValue ;
@@ -197,5 +213,147 @@ private Texture3D CreateGradientTextureInternal()
197
213
texture . Apply ( ) ;
198
214
return texture ;
199
215
}
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
+ }
200
358
}
201
359
}
0 commit comments