HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
UT_VoxelArray.C
Go to the documentation of this file.
1 /*
2  * PROPRIETARY INFORMATION. This software is proprietary to
3  * Side Effects Software Inc., and is not to be reproduced,
4  * transmitted, or disclosed in any way without written permission.
5  *
6  * NAME: UT_VoxelArray.C ( UT Library, C++)
7  *
8  * COMMENTS:
9  * Tiled Voxel Array Implementation.
10  */
11 
12 #include "UT_VoxelArray.h"
13 
14 #include "UT_Array.h"
15 #include "UT_Assert.h"
16 #include "UT_BoundingBox.h"
17 #include "UT_COW.h"
18 #include "UT_Debug.h"
19 #include "UT_Filter.h"
20 #include "UT_FilterType.h"
21 #include "UT_Interrupt.h"
22 #include "UT_IStream.h"
23 #include "UT_JSONDefines.h"
24 #include "UT_JSONParser.h"
25 #include "UT_JSONWriter.h"
26 #include "UT_NTStreamUtil.h"
27 #include "UT_ParallelUtil.h"
28 #include "UT_SharedMemoryManager.h"
29 #include "UT_StackBuffer.h"
30 #include "UT_String.h"
31 #include "UT_ThreadedAlgorithm.h"
32 #include "UT_ValArray.h"
33 #include "UT_Vector2.h"
34 #include "UT_Vector3.h"
35 #include "UT_Vector4.h"
36 #include "UT_VectorTypes.h"
37 #include "UT_VoxelArrayJSON.h"
38 #include "UT_WorkBuffer.h"
39 #include <SYS/SYS_Compiler.h>
40 #include <SYS/SYS_Floor.h>
41 #include <SYS/SYS_Math.h>
42 #include <SYS/SYS_SharedMemory.h>
43 #include <SYS/SYS_Types.h>
44 
45 #include <algorithm>
46 #include <iostream>
47 
48 #include <string.h>
49 
50 ///
51 /// fpreal16 conversion functions
52 ///
53 inline fpreal16
55 inline fpreal16
57 inline fpreal16
59 inline fpreal16
60 UTvoxelConvertFP16(int8 a) { return a; }
61 inline fpreal16
63 inline fpreal16
65 inline fpreal16
67 
68 ///
69 /// VoxelTileCompress definitions
70 ///
71 template <typename T>
72 void
74  T &min, T &max) const
75 {
76  int x, y, z;
77 
78  min = getValue(tile, 0, 0, 0);
79  max = min;
80 
81  // Generic approach is to just use the getValue() ability.
82  for (z = 0; z < tile.zres(); z++)
83  {
84  for (y = 0; y < tile.yres(); y++)
85  {
86  for (x = 0; x < tile.xres(); x++)
87  {
88  tile.expandMinMax(getValue(tile, x, y, z), min, max);
89  }
90  }
91  }
92  return;
93 }
94 
95 //
96 // VoxelTile definitions.
97 //
98 
99 template <typename T>
101 {
102  myRes[0] = 0;
103  myRes[1] = 0;
104  myRes[2] = 0;
105 
106  myCompressionType = COMPRESS_CONSTANT;
107  myForeignData = false;
108 
109  if (sizeof(T) <= sizeof(T*))
110  {
111  myData = 0;
112  }
113  else
114  {
115  myData = UT_VOXEL_ALLOC(sizeof(T));
116 
117  // It is not accidental that this is not a static_cast!
118  // There isn't a UT_Vector3(fpreal) constructor (as we have a foolish
119  // UT_Vector3(fpreal *) constructor that would cause massive problems)
120  // but there is a UT_Vector3 operator=(fpreal)!
121  ((T *)myData)[0] = 0;
122  }
123 }
124 
125 template <typename T>
127 {
128  freeData();
129 }
130 
131 template <typename T>
133 {
134  myData = 0;
135 
136  // Use assignment operator.
137  *this = src;
138 }
139 
140 template <typename T>
141 const UT_VoxelTile<T> &
143 {
144  if (&src == this)
145  return *this;
146 
147  freeData();
148 
149  myRes[0] = src.myRes[0];
150  myRes[1] = src.myRes[1];
151  myRes[2] = src.myRes[2];
152 
153  myCompressionType = src.myCompressionType;
154  switch (myCompressionType)
155  {
156  case COMPRESS_RAW:
157  myData = UT_VOXEL_ALLOC(
158  sizeof(T) * myRes[0] * myRes[1] * myRes[2]);
159  memcpy(myData, src.myData, sizeof(T) * myRes[0] * myRes[1] * myRes[2]);
160  break;
161  case COMPRESS_CONSTANT:
162  if (inlineConstant())
163  {
164  myData = src.myData;
165  }
166  else
167  {
168  myData = UT_VOXEL_ALLOC(sizeof(T));
169  memcpy(myData, src.myData, sizeof(T));
170  }
171  break;
172  case COMPRESS_RAWFULL:
173  myData = UT_VOXEL_ALLOC(
174  sizeof(T) * TILESIZE * TILESIZE * myRes[2]);
175  memcpy(myData, src.myData,
176  sizeof(T) * TILESIZE * TILESIZE * myRes[2]);
177  break;
178  case COMPRESS_FPREAL16:
179  {
180  constexpr exint tuple_size = UT_FixedVectorTraits<T>::TupleSize;
181  myData = UT_VOXEL_ALLOC(
182  sizeof(fpreal16) * myRes[0] * myRes[1] * myRes[2] * tuple_size);
183  memcpy(myData, src.myData,
184  sizeof(fpreal16) * myRes[0] * myRes[1] * myRes[2] * tuple_size);
185  break;
186  }
187  default:
188  {
189  UT_VoxelTileCompress<T> *engine;
190 
191  engine = getCompressionEngine(myCompressionType);
192  myData = UT_VOXEL_ALLOC(engine->getDataLength(src));
193  memcpy(myData, src.myData, engine->getDataLength(src));
194  break;
195  }
196  }
197 
198  return *this;
199 }
200 
201 template <typename T>
202 bool
204 {
205  switch (myCompressionType)
206  {
207  case COMPRESS_RAW:
208  // Do the assignment.
209  ((T *)myData)[ ((z * myRes[1]) + y) * myRes[0] + x ] = t;
210  return true;
211 
212  case COMPRESS_CONSTANT:
213  if (rawConstVal() == t)
214  {
215  // Trivially true.
216  return true;
217  }
218  return false;
219 
220  case COMPRESS_RAWFULL:
221  ((T *)myData)[ ((z * TILESIZE) + y) * TILESIZE + x ] = t;
222  return true;
223 
224  case COMPRESS_FPREAL16:
225  return false;
226  }
227 
228  // Use the compression engine.
229  UT_VoxelTileCompress<T> *engine;
230 
231  engine = getCompressionEngine(myCompressionType);
232  return engine->writeThrough(*this, x, y, z, t);
233 }
234 
235 template <typename T>
236 T
237 UT_VoxelTile<T>::operator()(int x, int y, int z) const
238 {
239  UT_ASSERT_P(x >= 0 && y >= 0 && z >= 0);
240  UT_ASSERT_P(x < myRes[0] && y < myRes[1] && z < myRes[2]);
241 
242  switch (myCompressionType)
243  {
244  case COMPRESS_RAW:
245  return ((T *)myData)[
246  ((z * myRes[1]) + y) * myRes[0] + x ];
247 
248  case COMPRESS_CONSTANT:
249  return rawConstVal();
250 
251  case COMPRESS_RAWFULL:
252  return ((T *)myData)[
253  ((z * TILESIZE) + y) * TILESIZE + x ];
254 
255  case COMPRESS_FPREAL16:
256  {
257  static constexpr UT_FromUnbounded<T> convertFromFP16{};
258 
259  fpreal16* data = (fpreal16*) myData;
260  int offset = ((z * myRes[1] + y) * myRes[0] + x)
262  T result = convertFromFP16(data + offset);
263  return result;
264  }
265  }
266 
267  // By default use the compression engine.
268  UT_VoxelTileCompress<T> *engine;
269 
270  engine = getCompressionEngine(myCompressionType);
271  return engine->getValue(*this, x, y, z);
272 }
273 
274 template <typename T>
275 T
276 UT_VoxelTile<T>::lerp(int x, int y, int z, fpreal32 fx, fpreal32 fy, fpreal32 fz) const
277 {
278  T vx, vx1, vy, vy1, vz;
279 
280  switch (myCompressionType)
281  {
282  case COMPRESS_RAW:
283  {
284  T *data = (T *) myData;
285  int offset = (z * myRes[1] + y) * myRes[0] + x;
286  int yinc = myRes[0];
287  int zinc = myRes[0] * myRes[1];
288 
289  // Lerp x:x+1, y, z
290  vx = lerpValues(data[offset], data[offset+1], fx);
291  // Lerp x:x+1, y+1, z
292  vx1 = lerpValues(data[offset+yinc], data[offset+yinc+1], fx);
293 
294  // Lerp x:x+1, y:y+1, z
295  vy = lerpValues(vx, vx1, fy);
296 
297  // Lerp x:x+1, y, z+1
298  vx = lerpValues(data[offset+zinc], data[offset+zinc+1], fx);
299  // Lerp x:x+1, y+1, z+1
300  vx1 = lerpValues(data[offset+zinc+yinc], data[offset+zinc+yinc+1], fx);
301 
302  // Lerp x:x+1, y:y+1, z+1
303  vy1 = lerpValues(vx, vx1, fy);
304 
305  // Lerp x:x+1, y:y+1, z:z+1
306  vz = lerpValues(vy, vy1, fz);
307  break;
308  }
309  case COMPRESS_RAWFULL:
310  {
311  T *data = (T *) myData;
312  int offset = (z * TILESIZE + y) * TILESIZE + x;
313  int yinc = TILESIZE;
314  int zinc = TILESIZE * TILESIZE;
315 
316  // Lerp x:x+1, y, z
317  vx = lerpValues(data[offset], data[offset+1], fx);
318  // Lerp x:x+1, y+1, z
319  vx1 = lerpValues(data[offset+yinc], data[offset+yinc+1], fx);
320 
321  // Lerp x:x+1, y:y+1, z
322  vy = lerpValues(vx, vx1, fy);
323 
324  // Lerp x:x+1, y, z+1
325  vx = lerpValues(data[offset+zinc], data[offset+zinc+1], fx);
326  // Lerp x:x+1, y+1, z+1
327  vx1 = lerpValues(data[offset+zinc+yinc], data[offset+zinc+yinc+1], fx);
328 
329  // Lerp x:x+1, y:y+1, z+1
330  vy1 = lerpValues(vx, vx1, fy);
331 
332  // Lerp x:x+1, y:y+1, z:z+1
333  vz = lerpValues(vy, vy1, fz);
334  break;
335  }
336  case COMPRESS_FPREAL16:
337  {
338  constexpr exint tuple_size = UT_FixedVectorTraits<T>::TupleSize;
339  static constexpr UT_FromUnbounded<T> convertFromFP16{};
340 
341  fpreal16 *data = (fpreal16 *) myData;
342  int xinc = tuple_size;
343  int yinc = myRes[0] * xinc;
344  int zinc = myRes[1] * yinc;
345  int offset = z * zinc + y * yinc + x * xinc;
346  fpreal16 vx, vx1, vy, vy1;
347  fpreal16 result[tuple_size];
348 
349  for (int j = 0; j < tuple_size; j++, offset++)
350  {
351  // Lerp x:x+1, y, z
352  vx = SYSlerp(data[offset], data[offset+xinc], fx);
353  // Lerp x:x+1, y+1, z
354  vx1 = SYSlerp(data[offset+yinc], data[offset+yinc+xinc], fx);
355 
356  // Lerp x:x+1, y:y+1, z
357  vy = SYSlerp(vx, vx1, fy);
358 
359  // Lerp x:x+1, y, z+1
360  vx = SYSlerp(data[offset+zinc], data[offset+zinc+xinc], fx);
361  // Lerp x:x+1, y+1, z+1
362  vx1 = SYSlerp(data[offset+zinc+yinc], data[offset+zinc+yinc+xinc], fx);
363 
364  // Lerp x:x+1, y:y+1, z+1
365  vy1 = SYSlerp(vx, vx1, fy);
366 
367  // Lerp x:x+1, y:y+1, z:z+1
368  result[j] = SYSlerp(vy, vy1, fz);
369  }
370 
371  return convertFromFP16(result);
372  }
373  case COMPRESS_CONSTANT:
374  {
375  // This is quite trivial to do a trilerp on.
376  vz = rawConstVal();
377  break;
378  }
379 
380  default:
381  {
382  UT_VoxelTileCompress<T> *engine;
383 
384  engine = getCompressionEngine(myCompressionType);
385  // Lerp x:x+1, y, z
386  vx = lerpValues(engine->getValue(*this, x, y, z),
387  engine->getValue(*this, x+1, y, z),
388  fx);
389  // Lerp x:x+1, y+1, z
390  vx1 = lerpValues(engine->getValue(*this, x, y+1, z),
391  engine->getValue(*this, x+1, y+1, z),
392  fx);
393 
394  // Lerp x:x+1, y:y+1, z
395  vy = lerpValues(vx, vx1, fy);
396 
397  // Lerp x:x+1, y, z+1
398  vx = lerpValues(engine->getValue(*this, x, y, z+1),
399  engine->getValue(*this, x+1, y, z+1),
400  fx);
401  // Lerp x:x+1, y+1, z+1
402  vx1 = lerpValues(engine->getValue(*this, x, y+1, z+1),
403  engine->getValue(*this, x+1, y+1, z+1),
404  fx);
405 
406  // Lerp x:x+1, y:y+1, z+1
407  vy1 = lerpValues(vx, vx1, fy);
408 
409  // Lerp x:x+1, y:y+1, z:z+1
410  vz = lerpValues(vy, vy1, fz);
411  break;
412  }
413  }
414 
415  return vz;
416 }
417 
418 template <typename T>
419 template <int AXIS2D>
420 T
421 UT_VoxelTile<T>::lerpAxis(int x, int y, int z, fpreal32 fx, fpreal32 fy, fpreal32 fz) const
422 {
423  T vx, vx1, vy, vy1, vz;
424 
425  switch (myCompressionType)
426  {
427  case COMPRESS_RAW:
428  {
429  T *data = (T *) myData;
430  int offset = (z * myRes[1] + y) * myRes[0] + x;
431  int yinc = myRes[0];
432  int zinc = myRes[0] * myRes[1];
433 
434  // Lerp x:x+1, y, z
435  if (AXIS2D != 0)
436  vx = lerpValues(data[offset],
437  data[offset+1],
438  fx);
439  else
440  vx = data[offset];
441 
442  if (AXIS2D != 1)
443  {
444  // Lerp x:x+1, y+1, z
445  if (AXIS2D != 0)
446  vx1= lerpValues(data[offset+yinc],
447  data[offset+yinc+1],
448  fx);
449  else
450  vx1 = data[offset+yinc];
451  // Lerp x:x+1, y:y+1, z
452  vy = lerpValues(vx, vx1, fy);
453  }
454  else
455  vy = vx;
456 
457  if (AXIS2D != 2)
458  {
459  // Lerp x:x+1, y, z+1
460  if (AXIS2D != 0)
461  vx = lerpValues(data[offset+zinc],
462  data[offset+zinc+1],
463  fx);
464  else
465  vx = data[offset+zinc];
466 
467  if (AXIS2D != 1)
468  {
469  // Lerp x:x+1, y+1, z+1
470  if (AXIS2D != 0)
471  vx1= lerpValues(data[offset+zinc+yinc],
472  data[offset+zinc+yinc+1],
473  fx);
474  else
475  vx1 = data[offset+zinc+yinc];
476  // Lerp x:x+1, y:y+1, z+1
477  vy1 = lerpValues(vx, vx1, fy);
478  }
479  else
480  vy1 = vx;
481 
482  // Lerp x:x+1, y:y+1, z:z+1
483  vz = lerpValues(vy, vy1, fz);
484  }
485  else
486  vz = vy;
487  break;
488  }
489  case COMPRESS_RAWFULL:
490  {
491  T *data = (T *) myData;
492  int offset = (z * TILESIZE + y) * TILESIZE + x;
493  int yinc = TILESIZE;
494  int zinc = TILESIZE * TILESIZE;
495 
496  // Lerp x:x+1, y, z
497  if (AXIS2D != 0)
498  vx = lerpValues(data[offset],
499  data[offset+1],
500  fx);
501  else
502  vx = data[offset];
503 
504  if (AXIS2D != 1)
505  {
506  // Lerp x:x+1, y+1, z
507  if (AXIS2D != 0)
508  vx1= lerpValues(data[offset+yinc],
509  data[offset+yinc+1],
510  fx);
511  else
512  vx1 = data[offset+yinc];
513  // Lerp x:x+1, y:y+1, z
514  vy = lerpValues(vx, vx1, fy);
515  }
516  else
517  vy = vx;
518 
519  if (AXIS2D != 2)
520  {
521  // Lerp x:x+1, y, z+1
522  if (AXIS2D != 0)
523  vx = lerpValues(data[offset+zinc],
524  data[offset+zinc+1],
525  fx);
526  else
527  vx = data[offset+zinc];
528 
529  if (AXIS2D != 1)
530  {
531  // Lerp x:x+1, y+1, z+1
532  if (AXIS2D != 0)
533  vx1= lerpValues(data[offset+zinc+yinc],
534  data[offset+zinc+yinc+1],
535  fx);
536  else
537  vx1 = data[offset+zinc+yinc];
538  // Lerp x:x+1, y:y+1, z+1
539  vy1 = lerpValues(vx, vx1, fy);
540  }
541  else
542  vy1 = vx;
543 
544  // Lerp x:x+1, y:y+1, z:z+1
545  vz = lerpValues(vy, vy1, fz);
546  }
547  else
548  vz = vy;
549  break;
550  }
551  case COMPRESS_FPREAL16:
552  {
553  constexpr exint tuple_size = UT_FixedVectorTraits<T>::TupleSize;
554  static constexpr UT_FromUnbounded<T> convertFromFP16{};
555 
556  fpreal16 *data = (fpreal16 *) myData;
557  int xinc = tuple_size;
558  int yinc = myRes[0] * xinc;
559  int zinc = myRes[1] * yinc;
560  int offset = z * zinc + y * yinc + x * xinc;
561  fpreal16 vx, vx1, vy, vy1;
562  fpreal16 result[tuple_size];
563 
564  for (int j = 0; j < tuple_size; j++, offset++)
565  {
566  // Lerp x:x+1, y, z
567  if (AXIS2D != 0)
568  vx = SYSlerp(data[offset],
569  data[offset+xinc],
570  fx);
571  else
572  vx = data[offset];
573 
574  if (AXIS2D != 1)
575  {
576  // Lerp x:x+1, y+1, z
577  if (AXIS2D != 0)
578  vx1= SYSlerp(data[offset+yinc],
579  data[offset+yinc+xinc],
580  fx);
581  else
582  vx1 = data[offset+yinc];
583  // Lerp x:x+1, y:y+1, z
584  vy = SYSlerp(vx, vx1, fy);
585  }
586  else
587  vy = vx;
588 
589  if (AXIS2D != 2)
590  {
591  // Lerp x:x+1, y, z+1
592  if (AXIS2D != 0)
593  vx = SYSlerp(data[offset+zinc],
594  data[offset+zinc+xinc],
595  fx);
596  else
597  vx = data[offset+zinc];
598 
599  if (AXIS2D != 1)
600  {
601  // Lerp x:x+1, y+1, z+1
602  if (AXIS2D != 0)
603  vx1= SYSlerp(data[offset+zinc+yinc],
604  data[offset+zinc+yinc+xinc],
605  fx);
606  else
607  vx1 = data[offset+zinc+yinc];
608  // Lerp x:x+1, y:y+1, z+1
609  vy1 = SYSlerp(vx, vx1, fy);
610  }
611  else
612  vy1 = vx;
613 
614  // Lerp x:x+1, y:y+1, z:z+1
615  result[j] = SYSlerp(vy, vy1, fz);
616  }
617  else
618  result[j] = vy;
619  }
620 
621  return convertFromFP16(result);
622  }
623  case COMPRESS_CONSTANT:
624  {
625  // This is quite trivial to do a bilerp on.
626  vz = rawConstVal();
627  break;
628  }
629 
630  default:
631  {
632  UT_VoxelTileCompress<T> *engine;
633 
634  engine = getCompressionEngine(myCompressionType);
635  // Lerp x:x+1, y, z
636  if (AXIS2D != 0)
637  vx = lerpValues(engine->getValue(*this, x, y, z),
638  engine->getValue(*this, x+1, y, z),
639  fx);
640  else
641  vx = engine->getValue(*this, x, y, z);
642 
643  if (AXIS2D != 1)
644  {
645  // Lerp x:x+1, y+1, z
646  if (AXIS2D != 0)
647  vx1= lerpValues(engine->getValue(*this, x, y+1, z),
648  engine->getValue(*this, x+1, y+1, z),
649  fx);
650  else
651  vx1 = engine->getValue(*this, x, y+1, z);
652  // Lerp x:x+1, y:y+1, z
653  vy = lerpValues(vx, vx1, fy);
654  }
655  else
656  vy = vx;
657 
658  if (AXIS2D != 2)
659  {
660  // Lerp x:x+1, y, z+1
661  if (AXIS2D != 0)
662  vx = lerpValues(engine->getValue(*this, x, y, z+1),
663  engine->getValue(*this, x+1, y, z+1),
664  fx);
665  else
666  vx = engine->getValue(*this, x, y, z+1);
667 
668  if (AXIS2D != 1)
669  {
670  // Lerp x:x+1, y+1, z+1
671  if (AXIS2D != 0)
672  vx1= lerpValues(engine->getValue(*this, x, y+1, z+1),
673  engine->getValue(*this, x+1, y+1, z+1),
674  fx);
675  else
676  vx1 = engine->getValue(*this, x, y+1, z+1);
677  // Lerp x:x+1, y:y+1, z+1
678  vy1 = lerpValues(vx, vx1, fy);
679  }
680  else
681  vy1 = vx;
682 
683  // Lerp x:x+1, y:y+1, z:z+1
684  vz = lerpValues(vy, vy1, fz);
685  }
686  else
687  vz = vy;
688  break;
689  }
690  }
691 
692  return vz;
693 }
694 
695 template <typename T>
696 bool
697 UT_VoxelTile<T>::extractSample(int x, int y, int z, T *sample) const
698 {
699  switch (myCompressionType)
700  {
701  case COMPRESS_RAW:
702  {
703  T *data = (T *) myData;
704  int offset = (z * myRes[1] + y) * myRes[0] + x;
705  int yinc = myRes[0];
706  int zinc = myRes[0] * myRes[1];
707 
708  sample[0] = data[offset];
709  sample[1] = data[offset+1];
710  sample[2+0] = data[offset+yinc];
711  sample[2+1] = data[offset+yinc+1];
712  sample[4+0] = data[offset+zinc];
713  sample[4+1] = data[offset+zinc+1];
714  sample[4+2+0] = data[offset+zinc+yinc];
715  sample[4+2+1] = data[offset+zinc+yinc+1];
716  break;
717  }
718  case COMPRESS_RAWFULL:
719  {
720  T *data = (T *) myData;
721  int offset = (z * TILESIZE + y) * TILESIZE + x;
722  int yinc = TILESIZE;
723  int zinc = TILESIZE * TILESIZE;
724 
725  sample[0] = data[offset];
726  sample[1] = data[offset+1];
727  sample[2+0] = data[offset+yinc];
728  sample[2+1] = data[offset+yinc+1];
729  sample[4+0] = data[offset+zinc];
730  sample[4+1] = data[offset+zinc+1];
731  sample[4+2+0] = data[offset+zinc+yinc];
732  sample[4+2+1] = data[offset+zinc+yinc+1];
733  break;
734  }
735  case COMPRESS_FPREAL16:
736  {
737  constexpr exint tuple_size = UT_FixedVectorTraits<T>::TupleSize;
738  static constexpr UT_FromUnbounded<T> convertFromFP16{};
739 
740  fpreal16 *data = (fpreal16 *) myData;
741  int xinc = tuple_size;
742  int yinc = myRes[0] * xinc;
743  int zinc = myRes[1] * yinc;
744  int offset = z * zinc + y * yinc + x * xinc;
745 
746  data += offset;
747  sample[0] = convertFromFP16(data);
748  sample[1] = convertFromFP16(data + xinc);
749  sample[2+0] = convertFromFP16(data + yinc);
750  sample[2+1] = convertFromFP16(data + yinc + xinc);
751  sample[4+0] = convertFromFP16(data + zinc);
752  sample[4+1] = convertFromFP16(data + zinc + xinc);
753  sample[4+2+0] = convertFromFP16(data + zinc + yinc);
754  sample[4+2+1] = convertFromFP16(data + zinc + yinc + xinc);
755  break;
756  }
757  case COMPRESS_CONSTANT:
758  {
759  sample[0] = rawConstVal();
760  return true;
761  }
762 
763  default:
764  {
765  UT_VoxelTileCompress<T> *engine;
766 
767  engine = getCompressionEngine(myCompressionType);
768  // Lerp x:x+1, y, z
769  sample[0] = engine->getValue(*this, x, y, z);
770  sample[1] = engine->getValue(*this, x+1, y, z);
771  sample[2+0] = engine->getValue(*this, x, y+1, z);
772  sample[2+1] = engine->getValue(*this, x+1, y+1, z);
773  sample[4+0] = engine->getValue(*this, x, y, z+1);
774  sample[4+1] = engine->getValue(*this, x+1, y, z+1);
775  sample[4+2+0] = engine->getValue(*this, x, y+1, z+1);
776  sample[4+2+1] = engine->getValue(*this, x+1, y+1, z+1);
777  break;
778  }
779  }
780  return false;
781 }
782 
783 template <typename T>
784 bool
785 UT_VoxelTile<T>::extractSamplePlus(int x, int y, int z, T *sample) const
786 {
787  switch (myCompressionType)
788  {
789  case COMPRESS_RAW:
790  {
791  T *data = (T *) myData;
792  int offset = (z * myRes[1] + y) * myRes[0] + x;
793  int yinc = myRes[0];
794  int zinc = myRes[0] * myRes[1];
795 
796  sample[0] = data[offset-1];
797  sample[1] = data[offset+1];
798  sample[2+0] = data[offset-yinc];
799  sample[2+1] = data[offset+yinc];
800  sample[4+0] = data[offset-zinc];
801  sample[4+1] = data[offset+zinc];
802  sample[6] = data[offset];
803  break;
804  }
805  case COMPRESS_RAWFULL:
806  {
807  T *data = (T *) myData;
808  int offset = (z * TILESIZE + y) * TILESIZE + x;
809  int yinc = TILESIZE;
810  int zinc = TILESIZE * TILESIZE;
811 
812  sample[0] = data[offset-1];
813  sample[1] = data[offset+1];
814  sample[2+0] = data[offset-yinc];
815  sample[2+1] = data[offset+yinc];
816  sample[4+0] = data[offset-zinc];
817  sample[4+1] = data[offset+zinc];
818  sample[6] = data[offset];
819  break;
820  }
821  case COMPRESS_FPREAL16:
822  {
823  constexpr exint tuple_size = UT_FixedVectorTraits<T>::TupleSize;
824  static constexpr UT_FromUnbounded<T> convertFromFP16{};
825 
826  fpreal16 *data = (fpreal16 *) myData;
827  int xinc = tuple_size;
828  int yinc = myRes[0] * xinc;
829  int zinc = myRes[1] * yinc;
830  int offset = z * zinc + y * yinc + x * xinc;
831 
832  data += offset;
833  sample[0] = convertFromFP16(data - xinc);
834  sample[1] = convertFromFP16(data + xinc);
835  sample[2+0] = convertFromFP16(data - yinc);
836  sample[2+1] = convertFromFP16(data + yinc);
837  sample[4+0] = convertFromFP16(data - zinc);
838  sample[4+1] = convertFromFP16(data + zinc);
839  sample[6] = convertFromFP16(data);
840  break;
841  }
842  case COMPRESS_CONSTANT:
843  {
844  sample[0] = rawConstVal();
845  return true;
846  }
847 
848  default:
849  {
850  UT_VoxelTileCompress<T> *engine;
851 
852  engine = getCompressionEngine(myCompressionType);
853  // Lerp x:x+1, y, z
854  sample[0] = engine->getValue(*this, x-1, y, z);
855  sample[1] = engine->getValue(*this, x+1, y, z);
856  sample[2+0] = engine->getValue(*this, x, y-1, z);
857  sample[2+1] = engine->getValue(*this, x, y+1, z);
858  sample[4+0] = engine->getValue(*this, x, y, z+1);
859  sample[4+1] = engine->getValue(*this, x, y, z-1);
860  sample[6] = engine->getValue(*this, x, y, z);
861  break;
862  }
863  }
864  return false;
865 }
866 
867 #if 0
868 /// Implementation of this function has an error. The outer dz loop assumes that
869 /// the incoming offset is for y=0, as it immediately subtracts yinc to get to
870 /// y=-1; it then loops over the 3 y layers, adding yinc every time. After this
871 /// dz loop, yinc is subtracted thrice, bringing us back to y=-1. On the next
872 /// iteration of the dz loop, this violates the y=0 assumption.
873 /// There aren't any users of these methods in our own code.
874 template <typename T>
875 bool
876 UT_VoxelTile<T>::extractSampleCube(int x, int y, int z, T *sample) const
877 {
878  switch (myCompressionType)
879  {
880  case COMPRESS_RAW:
881  {
882  T *data = (T *) myData;
883  int offset = (z * myRes[1] + y) * myRes[0] + x;
884  int yinc = myRes[0];
885  int zinc = myRes[0] * myRes[1];
886  int sampidx = 0;
887 
888  offset -= zinc;
889  for (int dz = -1; dz <= 1; dz++)
890  {
891  offset -= yinc;
892  for (int dy = -1; dy <= 1; dy++)
893  {
894  sample[sampidx] = data[offset-1];
895  sample[sampidx+1] = data[offset];
896  sample[sampidx+2] = data[offset+1];
897  sampidx += 3;
898  offset += yinc;
899  }
900  offset -= yinc * 3;
901  offset += zinc;
902  }
903  break;
904  }
905  case COMPRESS_RAWFULL:
906  {
907  T *data = (T *) myData;
908  int offset = (z * TILESIZE + y) * TILESIZE + x;
909  int yinc = TILESIZE;
910  int zinc = TILESIZE * TILESIZE;
911  int sampidx = 0;
912 
913  offset -= zinc;
914  for (int dz = -1; dz <= 1; dz++)
915  {
916  offset -= yinc;
917  for (int dy = -1; dy <= 1; dy++)
918  {
919  sample[sampidx] = data[offset-1];
920  sample[sampidx+1] = data[offset];
921  sample[sampidx+2] = data[offset+1];
922  sampidx += 3;
923  offset += yinc;
924  }
925  offset -= yinc * 3;
926  offset += zinc;
927  }
928  break;
929  }
930  case COMPRESS_FPREAL16:
931  {
932  fpreal16 *data = (fpreal16 *) myData;
933  int offset = (z * myRes[1] + y) * myRes[0] + x;
934  int yinc = myRes[0];
935  int zinc = myRes[0] * myRes[1];
936  int sampidx = 0;
937 
938  offset -= zinc;
939  for (int dz = -1; dz <= 1; dz++)
940  {
941  offset -= yinc;
942  for (int dy = -1; dy <= 1; dy++)
943  {
944  sample[sampidx] = data[offset-1];
945  sample[sampidx+1] = data[offset];
946  sample[sampidx+2] = data[offset+1];
947  sampidx += 3;
948  offset += yinc;
949  }
950  offset -= yinc * 3;
951  offset += zinc;
952  }
953  break;
954  }
955  case COMPRESS_CONSTANT:
956  {
957  sample[0] = rawConstVal();
958  return true;
959  }
960 
961  default:
962  {
963  UT_VoxelTileCompress<T> *engine;
964 
965  engine = getCompressionEngine(myCompressionType);
966  int sampidx = 0;
967  // Lerp x:x+1, y, z
968  for (int dz = -1; dz <= 1; dz++)
969  {
970  for (int dy = -1; dy <= 1; dy++)
971  {
972  for (int dx = -1; dx <= 1; dx++)
973  {
974  sample[sampidx++] = engine->getValue(*this, x+dx, y+dy, z+dz);
975  }
976  }
977  }
978  break;
979  }
980  }
981  return false;
982 }
983 #endif
984 
985 template <typename T>
986 template <int AXIS2D>
987 bool
988 UT_VoxelTile<T>::extractSampleAxis(int x, int y, int z, T *sample) const
989 {
990  switch (myCompressionType)
991  {
992  case COMPRESS_RAW:
993  {
994  T *data = (T *) myData;
995  int offset = (z * myRes[1] + y) * myRes[0] + x;
996  int yinc = myRes[0];
997  int zinc = myRes[0] * myRes[1];
998 
999  sample[0] = data[offset];
1000  if (AXIS2D != 0)
1001  sample[1] = data[offset+1];
1002  if (AXIS2D != 1)
1003  {
1004  sample[2+0] = data[offset+yinc];
1005  if (AXIS2D != 0)
1006  sample[2+1] = data[offset+yinc+1];
1007  }
1008  if (AXIS2D != 2)
1009  {
1010  sample[4+0] = data[offset+zinc];
1011  if (AXIS2D != 0)
1012  sample[4+1] = data[offset+zinc+1];
1013  if (AXIS2D != 1)
1014  {
1015  sample[4+2+0] = data[offset+zinc+yinc];
1016  if (AXIS2D != 0)
1017  sample[4+2+1] = data[offset+zinc+yinc+1];
1018  }
1019  }
1020  break;
1021  }
1022  case COMPRESS_RAWFULL:
1023  {
1024  T *data = (T *) myData;
1025  int offset = (z * TILESIZE + y) * TILESIZE + x;
1026  int yinc = TILESIZE;
1027  int zinc = TILESIZE * TILESIZE;
1028 
1029  sample[0] = data[offset];
1030  if (AXIS2D != 0)
1031  sample[1] = data[offset+1];
1032  if (AXIS2D != 1)
1033  {
1034  sample[2+0] = data[offset+yinc];
1035  if (AXIS2D != 0)
1036  sample[2+1] = data[offset+yinc+1];
1037  }
1038  if (AXIS2D != 2)
1039  {
1040  sample[4+0] = data[offset+zinc];
1041  if (AXIS2D != 0)
1042  sample[4+1] = data[offset+zinc+1];
1043  if (AXIS2D != 1)
1044  {
1045  sample[4+2+0] = data[offset+zinc+yinc];
1046  if (AXIS2D != 0)
1047  sample[4+2+1] = data[offset+zinc+yinc+1];
1048  }
1049  }
1050  break;
1051  }
1052  case COMPRESS_FPREAL16:
1053  {
1054  constexpr exint tuple_size = UT_FixedVectorTraits<T>::TupleSize;
1055  static constexpr UT_FromUnbounded<T> convertFromFP16{};
1056 
1057  fpreal16 *data = (fpreal16 *) myData;
1058  int xinc = tuple_size;
1059  int yinc = myRes[0] * xinc;
1060  int zinc = myRes[1] * yinc;
1061  int offset = z * zinc + y * yinc + x * xinc;
1062 
1063  data += offset;
1064  sample[0] = convertFromFP16(data);
1065  if (AXIS2D != 0)
1066  sample[1] = convertFromFP16(data + xinc);
1067  if (AXIS2D != 1)
1068  {
1069  sample[2+0] = convertFromFP16(data + yinc);
1070  if (AXIS2D != 0)
1071  sample[2+1] = convertFromFP16(data + yinc + xinc);
1072  }
1073  if (AXIS2D != 2)
1074  {
1075  sample[4+0] = convertFromFP16(data + zinc);
1076  if (AXIS2D != 0)
1077  sample[4+1] = convertFromFP16(data + zinc + xinc);
1078  if (AXIS2D != 1)
1079  {
1080  sample[4+2+0] = convertFromFP16(data + zinc + yinc);
1081  if (AXIS2D != 0)
1082  sample[4+2+1] = convertFromFP16(data + zinc + yinc + xinc);
1083  }
1084  }
1085  break;
1086  }
1087  case COMPRESS_CONSTANT:
1088  {
1089  sample[0] = rawConstVal();
1090  return true;
1091  }
1092 
1093  default:
1094  {
1095  UT_VoxelTileCompress<T> *engine;
1096 
1097  engine = getCompressionEngine(myCompressionType);
1098  // Lerp x:x+1, y, z
1099  sample[0] = engine->getValue(*this, x, y, z);
1100  if (AXIS2D != 0)
1101  sample[1] = engine->getValue(*this, x+1, y, z);
1102  if (AXIS2D != 1)
1103  {
1104  sample[2+0] = engine->getValue(*this, x, y+1, z);
1105  if (AXIS2D != 0)
1106  sample[2+1] = engine->getValue(*this, x+1, y+1, z);
1107  }
1108  if (AXIS2D != 2)
1109  {
1110  sample[4+0] = engine->getValue(*this, x, y, z+1);
1111  if (AXIS2D != 0)
1112  sample[4+1] = engine->getValue(*this, x+1, y, z+1);
1113  if (AXIS2D != 1)
1114  {
1115  sample[4+2+0] = engine->getValue(*this, x, y+1, z+1);
1116  if (AXIS2D != 0)
1117  sample[4+2+1] = engine->getValue(*this, x+1, y+1, z+1);
1118  }
1119  }
1120  break;
1121  }
1122  }
1123  return false;
1124 }
1125 
1126 #if 0
1127 template <typename T>
1128 T
1129 UT_VoxelTile<T>::lerp(v4uf frac, int x, int y, int z) const
1130 {
1131  v4uf a, b;
1132 
1133  switch (myCompressionType)
1134  {
1135  case COMPRESS_RAW:
1136  case COMPRESS_RAWFULL:
1137  {
1138  T *data = (T *) myData;
1139  int offset = (z * myRes[1] + y) * myRes[0] + x;
1140  int yinc = myRes[0];
1141  int zinc = myRes[0] * myRes[1];
1142 
1143  a = v4uf( data[offset],
1144  data[offset+zinc],
1145  data[offset+yinc],
1146  data[offset+yinc+zinc] );
1147  b = v4uf( data[offset+1],
1148  data[offset+zinc+1],
1149  data[offset+yinc+1],
1150  data[offset+yinc+zinc+1] );
1151  break;
1152  }
1153 
1154  case COMPRESS_CONSTANT:
1155  {
1156  // This is quite trivial to do a trilerp on.
1157  return rawConstVal();
1158  }
1159 
1160  default:
1161  {
1162  UT_VoxelTileCompress<T> *engine;
1163 
1164  engine = getCompressionEngine(myCompressionType);
1165  // Lerp x:x+1, y, z
1166  a = v4uf( engine->getValue(*this, x, y, z),
1167  engine->getValue(*this, x, y, z+1),
1168  engine->getValue(*this, x, y+1, z),
1169  engine->getValue(*this, x, y+1, z+1) );
1170  b = v4uf( engine->getValue(*this, x+1, y, z),
1171  engine->getValue(*this, x+1, y, z+1),
1172  engine->getValue(*this, x+1, y+1, z),
1173  engine->getValue(*this, x+1, y+1, z+1) );
1174  break;
1175  }
1176  }
1177 
1178  v4uf fx, fy, fz;
1179 
1180  fx = frac.swizzle<0, 0, 0, 0>();
1181  fy = frac.swizzle<1, 1, 1, 1>();
1182  fz = frac.swizzle<2, 2, 2, 2>();
1183 
1184  b -= a;
1185  a = madd(b, fx, a);
1186 
1187  b = a.swizzle<2, 3, 0, 1>();
1188  b -= a;
1189  a = madd(b, fy, a);
1190 
1191  b = a.swizzle<1, 2, 3, 0>();
1192  b -= a;
1193  a = madd(b, fz, a);
1194 
1195  return a[0];
1196 }
1197 #endif
1198 
1199 template <typename T>
1200 T *
1201 UT_VoxelTile<T>::fillCacheLine(T *cacheline, int &stride, int x, int y, int z, bool forcecopy, bool strideofone) const
1202 {
1203  UT_ASSERT_P(x >= 0 && y >= 0 && z >= 0);
1204  UT_ASSERT_P(x < myRes[0] && y < myRes[1] && z < myRes[2]);
1205 
1206  T *src;
1207  int i, xres = myRes[0];
1208 
1209  // All the directly handled types exit directly from this switch.
1210  switch (myCompressionType)
1211  {
1212  case COMPRESS_RAW:
1213  stride = 1;
1214  src = (T *)myData + (z * myRes[1] + y) * xres;
1215  if (!forcecopy)
1216  return &src[x];
1217 
1218  for (i = 0; i < xres; i++)
1219  cacheline[i] = src[i];
1220 
1221  return &cacheline[x];
1222 
1223  case COMPRESS_FPREAL16:
1224  {
1225  constexpr exint tuple_size = UT_FixedVectorTraits<T>::TupleSize;
1226  static constexpr UT_FromUnbounded<T> convertFromFP16{};
1227 
1228  fpreal16 *src = (fpreal16 *) myData;
1229  int xinc = tuple_size;
1230  int yinc = myRes[0] * xinc;
1231  int zinc = myRes[1] * yinc;
1232  int offset = z * zinc + y * yinc;
1233 
1234  stride = 1;
1235  src += offset;
1236 
1237  for (i = 0; i < xres; i++, src += xinc)
1238  cacheline[i] = convertFromFP16(src);
1239 
1240  return &cacheline[x];
1241  }
1242 
1243 
1244  case COMPRESS_CONSTANT:
1245  src = rawConstData();
1246  if (!forcecopy && !strideofone)
1247  {
1248  stride = 0;
1249  return src;
1250  }
1251  stride = 1;
1252  // Fill out a constant value
1253  for (i = 0; i < xres; i++)
1254  cacheline[i] = *src;
1255 
1256  return &cacheline[x];
1257 
1258 
1259  case COMPRESS_RAWFULL:
1260  stride = 1;
1261  src = (T *)myData + (z * TILESIZE + y) * TILESIZE;
1262  if (!forcecopy)
1263  return &src[x];
1264 
1265  for (i = 0; i < xres; i++)
1266  cacheline[i] = src[i];
1267 
1268  return &cacheline[x];
1269  }
1270 
1271  // By default use the compression engine.
1272  UT_VoxelTileCompress<T> *engine;
1273 
1274  engine = getCompressionEngine(myCompressionType);
1275 
1276  // We could add support for a direct cacheline fill to accelerate
1277  // this case as well.
1278  stride = 1;
1279  for (i = 0; i < xres; i++)
1280  cacheline[i] = engine->getValue(*this, i, y, z);
1281 
1282  return &cacheline[x];
1283 }
1284 
1285 template <typename T>
1286 void
1287 UT_VoxelTile<T>::writeCacheLine(T *cacheline, int y, int z)
1288 {
1289  UT_ASSERT_P(y >= 0 && z >= 0);
1290  UT_ASSERT_P(y < myRes[1] && z < myRes[2]);
1291 
1292  T *dst, value;
1293  int i, xres = myRes[0];
1294 
1295  // All the directly handled types exit directly from this switch.
1296  switch (myCompressionType)
1297  {
1298  case COMPRESS_RAW:
1299  dst = (T *)myData + (z * myRes[1] + y) * xres;
1300  for (i = 0; i < xres; i++)
1301  *dst++ = *cacheline++;
1302  return;
1303 
1304  case COMPRESS_CONSTANT:
1305  value = rawConstVal();
1306  for (i = 0; i < xres; i++)
1307  if (cacheline[i] != value)
1308  break;
1309  // If everything was equal, our write is trivial.
1310  if (i == xres)
1311  return;
1312 
1313  break;
1314 
1315  case COMPRESS_RAWFULL:
1316  dst = (T *)myData + (z * TILESIZE + y) * TILESIZE;
1317  for (i = 0; i < TILESIZE; i++)
1318  *dst++ = *cacheline++;
1319 
1320  return;
1321  }
1322 
1323  // Switch back to invoking writeThrough. Ideally we'd have
1324  // a version that can handle a whole cache line at once
1325  for (i = 0; i < xres; i++)
1326  if (!writeThrough(i, y, z, cacheline[i]))
1327  break;
1328 
1329  // Determine if we failed to write everything through
1330  if (i != xres)
1331  {
1332  // Uncompress and reinvoke ourselves.
1333  uncompress();
1334  writeCacheLine(cacheline, y, z);
1335  }
1336 }
1337 
1338 template <typename T>
1339 void
1340 UT_VoxelTile<T>::copyFragment(int dstx, int dsty, int dstz,
1341  const UT_VoxelTile<T> &srctile,
1342  int srcx, int srcy, int srcz)
1343 {
1344  int w = SYSmin(xres() - dstx, srctile.xres() - srcx);
1345  int h = SYSmin(yres() - dsty, srctile.yres() - srcy);
1346  int d = SYSmin(zres() - dstz, srctile.zres() - srcz);
1347 
1348 #if 1
1349  if (srctile.isSimpleCompression())
1350  {
1351  T *dst;
1352  const T *src;
1353  int srcinc;
1354 
1355  src = srctile.rawData();
1356  srcinc = srctile.isConstant() ? 0 : 1;
1357 
1358  // Ensure we are easy to write to.
1359  uncompress();
1360 
1361  dst = rawData();
1362  dst += dstx + (dsty + dstz*yres())*xres();
1363 
1364  if (srcinc)
1365  src += srcx + (srcy + srcz*srctile.yres())*srctile.xres();
1366 
1367  for (int z = 0; z < d; z++)
1368  {
1369  for (int y = 0; y < h; y++)
1370  {
1371  if (srcinc)
1372  {
1373  for (int x = 0; x < w; x++)
1374  dst[x] = src[x];
1375  }
1376  else
1377  {
1378  for (int x = 0; x < w; x++)
1379  dst[x] = *src;
1380  }
1381  dst += xres();
1382  if (srcinc)
1383  src += srctile.xres();
1384  }
1385  dst += (yres()-h) * xres();
1386  if (srcinc)
1387  src += (srctile.yres() - h) * srctile.xres();
1388  }
1389 
1390  return;
1391  }
1392 #endif
1393 
1394  // Fail safe approach.
1395  for (int z = 0; z < d; z++)
1396  for (int y = 0; y < h; y++)
1397  for (int x = 0; x < w; x++)
1398  {
1399  setValue(dstx+x, dsty+y, dstz+z,
1400  srctile(srcx+x, srcy+y, srcz+z));
1401  }
1402 }
1403 
1404 template <typename T>
1405 template <typename S>
1406 void
1408 {
1409  int w = xres();
1410  int h = yres();
1411  int d = zres();
1412 
1413  if (isSimpleCompression())
1414  {
1415  const T *src;
1416  int srcinc;
1417 
1418  src = rawData();
1419  srcinc = isConstant() ? 0 : 1;
1420 
1421  if (stride == 1)
1422  {
1423  if (srcinc == 1)
1424  {
1425  // Super trivial!
1426  for (int i = 0; i < w * h * d; i++)
1427  {
1428  *dst++ = T(*src++);
1429  }
1430  }
1431  else
1432  {
1433  // Constant, also trivial!
1434  T cval = T(*src);
1435 
1436  for (int i = 0; i < w * h * d; i++)
1437  {
1438  *dst++ = cval;
1439  }
1440  }
1441  }
1442  else
1443  {
1444  for (int z = 0; z < d; z++)
1445  {
1446  for (int y = 0; y < h; y++)
1447  {
1448  if (srcinc)
1449  {
1450  for (int x = 0; x < w; x++)
1451  {
1452  *dst = S(src[x]);
1453  dst += stride;
1454  }
1455  }
1456  else
1457  {
1458  for (int x = 0; x < w; x++)
1459  {
1460  *dst = S(*src);
1461  dst += stride;
1462  }
1463  }
1464  if (srcinc)
1465  src += w;
1466  }
1467  }
1468  }
1469 
1470  return;
1471  }
1472 
1473  // Fail safe approach.
1474  for (int z = 0; z < d; z++)
1475  for (int y = 0; y < h; y++)
1476  for (int x = 0; x < w; x++)
1477  {
1478  *dst = S((*this)(x, y, z));
1479  dst += stride;
1480  }
1481 }
1482 
1483 template <typename T>
1484 template <typename S>
1485 void
1486 UT_VoxelTile<T>::writeData(const S *srcdata, int srcstride)
1487 {
1488  int w = xres();
1489  int h = yres();
1490  int d = zres();
1491  int i, n = w * h * d;
1492 
1493  // Check if src is constant
1494  S compare = srcdata[0];
1495  int srcoff = srcstride;
1496 
1497  if (srcstride == 0)
1498  {
1499  // Trivially constant
1500  makeConstant(T(compare));
1501  return;
1502  }
1503 
1504  for (i = 1; i < n; i++)
1505  {
1506  if (srcdata[srcoff] != compare)
1507  break;
1508  srcoff += srcstride;
1509  }
1510 
1511  if (i == n)
1512  {
1513  // Constant source!
1514  makeConstant(compare);
1515  return;
1516  }
1517 
1518  // Create a raw tile, expanding constants
1519  uncompress();
1520 
1521  if (srcstride == 1)
1522  {
1523  T *dst = rawData();
1524  for (i = 0; i < n; i++)
1525  {
1526  *dst++ = T(*srcdata++);
1527  }
1528  }
1529  else
1530  {
1531  T *dst = rawData();
1532 
1533  srcoff = 0;
1534  for (i = 0; i < n; i++)
1535  {
1536  dst[i] = T(srcdata[srcoff]);
1537  srcoff += srcstride;
1538  }
1539  }
1540 }
1541 
1542 template <typename T>
1543 void
1544 UT_VoxelTile<T>::setValue(int x, int y, int z, T t)
1545 {
1546  UT_ASSERT_P(x >= 0 && y >= 0 && z >= 0);
1547  UT_ASSERT_P(x < myRes[0] && y < myRes[1] && z < myRes[2]);
1548 
1549  // Determine if assignment is compatible with current
1550  // compression technique.
1551  if (writeThrough(x, y, z, t))
1552  {
1553  return;
1554  }
1555  // Can't write to our current type of tile with the
1556  // given value, so abandon.
1557  uncompress();
1558 
1559  // Attempt to write through again. This must succeed
1560  // as we should now be uncompressed.
1561  UT_VERIFY_P(writeThrough(x, y, z, t));
1562 }
1563 
1564 template <typename T>
1565 void
1567 {
1568  switch (myCompressionType)
1569  {
1570  case COMPRESS_RAW:
1571  // Trivial.
1572  return;
1573 
1574  case COMPRESS_CONSTANT:
1575  {
1576  // Must expand the tile!
1577  T cval;
1578  int i, n;
1579 
1580  cval = rawConstVal();
1581  freeData();
1582 
1583  myCompressionType = COMPRESS_RAW;
1584 
1585  if (myRes[0] == TILESIZE &&
1586  myRes[1] == TILESIZE)
1587  {
1588  // Flag that we can do fast lookups on this tile.
1589  myCompressionType = COMPRESS_RAWFULL;
1590  }
1591 
1592  n = myRes[0] * myRes[1] * myRes[2];
1593  myData = UT_VOXEL_ALLOC(sizeof(T) * n);
1594 
1595  for (i = 0; i < n; i++)
1596  {
1597  ((T *)myData)[i] = cval;
1598  }
1599  return;
1600  }
1601  case COMPRESS_RAWFULL:
1602  {
1603  T *raw;
1604  int x, y, z, i, n;
1605 
1606  if (myRes[0] == TILESIZE &&
1607  myRes[1] == TILESIZE)
1608  {
1609  // Trivial
1610  return;
1611  }
1612 
1613  // Need to contract to the actual compact size.
1614  n = myRes[0] * myRes[1] * myRes[2];
1615  raw = (T *)UT_VOXEL_ALLOC(sizeof(T) * n);
1616  i = 0;
1617  for (z = 0; z < myRes[2]; z++)
1618  {
1619  for (y = 0; y < myRes[1]; y++)
1620  {
1621  for (x = 0; x < myRes[0]; x++)
1622  {
1623  raw[i++] = ((T *)myData)[x+(y+z*TILESIZE)*TILESIZE];
1624  }
1625  }
1626  }
1627 
1628  freeData();
1629  myCompressionType = COMPRESS_RAW;
1630  myData = raw;
1631 
1632  return;
1633  }
1634 
1635  case COMPRESS_FPREAL16:
1636  {
1637  constexpr exint tuple_size = UT_FixedVectorTraits<T>::TupleSize;
1638  static constexpr UT_FromUnbounded<T> convertFromFP16{};
1639 
1640  T *raw;
1641  int x, y, z, i, n;
1642  fpreal16 *src = (fpreal16 *) myData;
1643 
1644  n = myRes[0] * myRes[1] * myRes[2];
1645  raw = (T *)UT_VOXEL_ALLOC(sizeof(T) * n);
1646  i = 0;
1647  for (z = 0; z < myRes[2]; z++)
1648  {
1649  for (y = 0; y < myRes[1]; y++)
1650  {
1651  for (x = 0; x < myRes[0]; x++, src += tuple_size)
1652  {
1653  raw[i] = convertFromFP16(src);
1654  i++;
1655  }
1656  }
1657  }
1658  freeData();
1659  myCompressionType = COMPRESS_RAW;
1660  myData = raw;
1661  return;
1662  }
1663  }
1664 
1665  // Use the compression engine.
1666  UT_VoxelTileCompress<T> *engine;
1667 
1668  engine = getCompressionEngine(myCompressionType);
1669 
1670  // We use the read ability to set our values.
1671  int x, y, z, i;
1672  T *raw;
1673 
1674  raw = (T *) UT_VOXEL_ALLOC(sizeof(T) * myRes[0] * myRes[1] * myRes[2]);
1675  i = 0;
1676  for (z = 0; z < myRes[2]; z++)
1677  {
1678  for (y = 0; y < myRes[1]; y++)
1679  {
1680  for (x = 0; x < myRes[0]; x++)
1681  {
1682  raw[i++] = engine->getValue(*this, x, y, z);
1683  }
1684  }
1685  }
1686 
1687  freeData();
1688 
1689  // Now copy in the result
1690  myCompressionType = COMPRESS_RAW;
1691  if (myRes[0] == TILESIZE &&
1692  myRes[1] == TILESIZE)
1693  {
1694  // Flag that we can do fast lookups on this tile.
1695  myCompressionType = COMPRESS_RAWFULL;
1696  }
1697 
1698  myData = raw;
1699 }
1700 
1701 template <typename T>
1702 void
1704 {
1705  T *raw;
1706  int x, y, z, i;
1707 
1708  if (myCompressionType == COMPRESS_RAWFULL)
1709  return;
1710 
1711  uncompress();
1712 
1713  UT_ASSERT(myCompressionType == COMPRESS_RAW);
1714 
1715  // The RAWFULL format only differs from RAW when the tile dimensions
1716  // are smaller than the maximum tile size.
1717  if (myRes[0] < TILESIZE || myRes[1] < TILESIZE)
1718  {
1719  raw = (T *)UT_VOXEL_ALLOC(sizeof(T) * TILESIZE * TILESIZE * myRes[2]);
1720  i = 0;
1721  for (z = 0; z < myRes[2]; z++)
1722  {
1723  for (y = 0; y < myRes[1]; y++)
1724  {
1725  for (x = 0; x < myRes[0]; x++)
1726  {
1727  raw[x+(y+z*TILESIZE)*TILESIZE] = ((T *)myData)[i++];
1728  }
1729  }
1730  }
1731  freeData();
1732  myData = raw;
1733  }
1734  myCompressionType = COMPRESS_RAWFULL;
1735 }
1736 
1737 template <typename T>
1738 void
1740 {
1741  if (isRaw() || isRawFull())
1742  return;
1743 
1744  freeData();
1745 
1746  if (myRes[0] == TILESIZE && myRes[1] == TILESIZE)
1747  myCompressionType = COMPRESS_RAWFULL;
1748  else
1749  myCompressionType = COMPRESS_RAW;
1750 
1751  myData = UT_VOXEL_ALLOC(sizeof(T) * numVoxels());
1752 }
1753 
1754 template <typename T>
1755 void
1757 {
1758  float irx, iry, irz;
1759 
1760  irx = 1.0 / myRes[0];
1761  iry = 1.0 / myRes[1];
1762  irz = 1.0 / myRes[2];
1763  switch (myCompressionType)
1764  {
1765  case COMPRESS_RAW:
1766  {
1767  int i;
1768  const T *data = (const T*) myData;
1769 
1770  i = 0;
1771  T zavg;
1772  zavg = 0;
1773  for (int z = 0; z < myRes[2]; z++)
1774  {
1775  T yavg;
1776  yavg = 0;
1777  for (int y = 0; y < myRes[1]; y++)
1778  {
1779  T xavg;
1780  xavg = 0;
1781  for (int x = 0; x < myRes[0]; x++)
1782  {
1783  xavg += data[i++];
1784  }
1785  xavg *= irx;
1786  yavg += xavg;
1787  }
1788  yavg *= iry;
1789  zavg += yavg;
1790  }
1791  zavg *= irz;
1792 
1793  avg = zavg;
1794  return;
1795  }
1796 
1797  case COMPRESS_FPREAL16:
1798  {
1799  constexpr exint tuple_size = UT_FixedVectorTraits<T>::TupleSize;
1800  static constexpr UT_FromUnbounded<T> convertFromFP16{};
1801 
1802  int i;
1803  const fpreal16 *data = (fpreal16 *) myData;
1804 
1805  i = 0;
1806  // The code below uses 4-wide vectors for averaging (though only the
1807  // needed components are actually used). This needs to be increased
1808  // in size if we need to start working with voxel arrays of vectors
1809  // with more components.
1810  static_assert(tuple_size <= 4);
1811  fpreal16 zavg[] = { 0, 0, 0, 0 };
1812  for (int z = 0; z < myRes[2]; z++)
1813  {
1814  fpreal16 yavg[] = { 0, 0, 0, 0 };
1815  for (int y = 0; y < myRes[1]; y++)
1816  {
1817  fpreal16 xavg[] = { 0, 0, 0, 0 };
1818  for (int x = 0; x < myRes[0]; x++)
1819  {
1820  for (int j = 0; j < tuple_size; j++)
1821  {
1822  xavg[j] += data[i++];
1823  }
1824  }
1825  for (int j = 0; j < tuple_size; j++)
1826  {
1827  xavg[j] *= irx;
1828  yavg[j] += xavg[j];
1829  }
1830  }
1831  for (int j = 0; j < tuple_size; j++)
1832  {
1833  yavg[j] *= iry;
1834  zavg[j] += yavg[j];
1835  }
1836  }
1837  for (int j = 0; j < tuple_size; j++)
1838  {
1839  zavg[j] *= irz;
1840  }
1841 
1842  avg = convertFromFP16(zavg);
1843  return;
1844  }
1845 
1846  case COMPRESS_CONSTANT:
1847  avg = rawConstVal();
1848  return;
1849 
1850  case COMPRESS_RAWFULL:
1851  {
1852  int offset;
1853  const T *data = (const T*) myData;
1854 
1855  offset = 0;
1856  T zavg;
1857  zavg = 0;
1858  for (int z = 0; z < myRes[2]; z++)
1859  {
1860  T yavg;
1861  yavg = 0;
1862  for (int y = 0; y < myRes[1]; y++)
1863  {
1864  T xavg;
1865  xavg = 0;
1866  for (int x = 0; x < myRes[0]; x++)
1867  {
1868  xavg += data[x+offset];
1869  }
1870  xavg *= irx;
1871  yavg += xavg;
1872  offset += TILESIZE;
1873  }
1874  yavg *= iry;
1875  zavg += yavg;
1876  }
1877  zavg *= irz;
1878 
1879  avg = zavg;
1880  return;
1881  }
1882 
1883  default:
1884  {
1885  T zavg;
1886  zavg = 0;
1887  for (int z = 0; z < myRes[2]; z++)
1888  {
1889  T yavg;
1890  yavg = 0;
1891  for (int y = 0; y < myRes[1]; y++)
1892  {
1893  T xavg;
1894  xavg = 0;
1895  for (int x = 0; x < myRes[0]; x++)
1896  {
1897  xavg += (*this)(x, y, z);
1898  }
1899  xavg *= irx;
1900  yavg += xavg;
1901  }
1902  yavg *= iry;
1903  zavg += yavg;
1904  }
1905  zavg *= irz;
1906 
1907  avg = zavg;
1908  return;
1909  }
1910  }
1911 }
1912 
1913 template <typename T>
1914 void
1916 {
1917  switch (myCompressionType)
1918  {
1919  case COMPRESS_RAW:
1920  {
1921  int n = myRes[0] * myRes[1] * myRes[2];
1922  int i;
1923 
1924  min = max = *(T*)myData;
1925  for (i = 1; i < n; i++)
1926  {
1927  expandMinMax( ((T*)myData)[i], min, max );
1928  }
1929  return;
1930  }
1931 
1932  case COMPRESS_FPREAL16:
1933  {
1934  constexpr exint tuple_size = UT_FixedVectorTraits<T>::TupleSize;
1935  static constexpr UT_FromUnbounded<T> convertFromFP16{};
1936 
1937  int n = myRes[0] * myRes[1] * myRes[2];
1938  int i;
1939  fpreal16 *src = (fpreal16 *)myData;
1940 
1941  min = max = *src;
1942  for (i = 1; i < n; i++, src += tuple_size)
1943  {
1944  T val = convertFromFP16(src);
1945  expandMinMax( val, min, max );
1946  }
1947  return;
1948  }
1949 
1950  case COMPRESS_CONSTANT:
1951  min = max = rawConstVal();
1952  return;
1953 
1954  case COMPRESS_RAWFULL:
1955  {
1956  int x, y, z, offset;
1957 
1958  min = max = *(T*)myData;
1959  offset = 0;
1960  for (z = 0; z < myRes[2]; z++)
1961  {
1962  for (y = 0; y < myRes[1]; y++)
1963  {
1964  for (x = 0; x < myRes[0]; x++)
1965  {
1966  expandMinMax(
1967  ((T*)myData)[x+offset],
1968  min, max );
1969  }
1970  offset += TILESIZE;
1971  }
1972  }
1973  return;
1974  }
1975 
1976  default:
1977  {
1978  // Use the compression engine.
1979  UT_VoxelTileCompress<T> *engine;
1980 
1981  engine = getCompressionEngine(myCompressionType);
1982 
1983  engine->findMinMax(*this, min, max);
1984  return;
1985  }
1986  }
1987 }
1988 
1989 template <typename T>
1990 bool
1992 {
1993  switch (myCompressionType)
1994  {
1995  case COMPRESS_RAW:
1996  case COMPRESS_RAWFULL:
1997  {
1998  int n = myRes[0] * myRes[1] * myRes[2];
1999  int i;
2000 
2001  for (i = 0; i < n; i++)
2002  {
2003  if (SYSisNan( ((T*)myData)[i] ))
2004  return true;
2005  }
2006  return false;
2007  }
2008 
2009  case COMPRESS_FPREAL16:
2010  {
2011  return false;
2012  }
2013 
2014  case COMPRESS_CONSTANT:
2015  if (SYSisNan(rawConstVal()))
2016  return true;
2017  return false;
2018 
2019  default:
2020  {
2021  // Use the compression engine.
2022  UT_VoxelTileCompress<T> *engine;
2023  int x, y, z;
2024 
2025  engine = getCompressionEngine(myCompressionType);
2026 
2027  for (z = 0; z < myRes[2]; z++)
2028  for (y = 0; y < myRes[1]; y++)
2029  for (x = 0; x < myRes[0]; x++)
2030  if (SYSisNan(engine->getValue(*this, x, y, z)))
2031  {
2032  return true;
2033  }
2034 
2035  return false;
2036  }
2037  }
2038 }
2039 
2040 template <typename T>
2041 bool
2043 {
2044  T min, max;
2045  bool losslessonly = (options.myQuantizeTol == 0.0);
2046  int i;
2047  UT_VoxelTileCompress<T> *engine;
2048 
2049  // This is as easy as it gets.
2050  if (myCompressionType == COMPRESS_CONSTANT)
2051  return false;
2052 
2053  findMinMax(min, max);
2054 
2055  // See if we can be made into a constant tile.
2056  if (dist(min, max) <= options.myConstantTol)
2057  {
2058  // Ignore if we are already constant.
2059  if (myCompressionType == COMPRESS_CONSTANT)
2060  return false;
2061 
2062  // We are fully constant!
2063  if (min != max)
2064  {
2065  T avg;
2066  findAverage(avg);
2067  makeConstant(avg);
2068  }
2069  else
2070  {
2071  makeConstant(min);
2072  }
2073  return true;
2074  }
2075 
2076  bool result = false;
2077 
2078  if (options.myAllowFP16)
2079  {
2080  if (myCompressionType == COMPRESS_RAW ||
2081  myCompressionType == COMPRESS_RAWFULL)
2082  {
2083  makeFpreal16();
2084  result = true;
2085  }
2086  }
2087 
2088  for (i = 0; i < getCompressionEngines().entries(); i++)
2089  {
2090  engine = getCompressionEngines()(i);
2091 
2092  // Ignore possibly expensive lossy compressions.
2093  if (losslessonly && !engine->isLossless())
2094  continue;
2095 
2096  if (engine->tryCompress(*this, options, min, max))
2097  {
2098  myCompressionType = i + COMPRESS_ENGINE;
2099  result = true;
2100  // We keep testing in case another compression engine
2101  // can get a better number...
2102  }
2103  }
2104 
2105  // If we are RAW compress, check to see if we could become
2106  // RAWFULL for faster access.
2107  if (myCompressionType == COMPRESS_RAW)
2108  {
2109  if (myRes[0] == TILESIZE && myRes[1] == TILESIZE)
2110  {
2111  myCompressionType = COMPRESS_RAWFULL;
2112  }
2113  }
2114 
2115  // No suitable compression found.
2116  return result;
2117 }
2118 
2119 template <typename T>
2120 void
2122 {
2123  if (!isConstant())
2124  {
2125  freeData();
2126 
2127  if (!inlineConstant())
2128  myData = UT_VOXEL_ALLOC(sizeof(T));
2129  }
2130 
2131  myCompressionType = COMPRESS_CONSTANT;
2132  *rawConstData() = t;
2133 }
2134 
2135 template <typename T>
2136 void
2138 {
2139  constexpr exint tuple_size = UT_FixedVectorTraits<T>::TupleSize;
2140  using ScalarType = typename UT_FixedVectorTraits<T>::DataType;
2141 
2142  if (isConstant())
2143  {
2144  return;
2145  }
2146 
2147  if (myCompressionType == COMPRESS_FPREAL16)
2148  return;
2149 
2150  // Get our new data.
2151  int len = myRes[2] * myRes[1] * myRes[0] * tuple_size;
2152  fpreal16 *data = (fpreal16 *)UT_VOXEL_ALLOC(sizeof(fpreal16) * len);
2153 
2154  if (myCompressionType == COMPRESS_RAW ||
2155  myCompressionType == COMPRESS_RAWFULL)
2156  {
2157  for (int i = 0; i < len; i++)
2158  {
2159  data[i] = UTvoxelConvertFP16(((ScalarType*) myData)[i]);
2160  }
2161  }
2162  else
2163  {
2164  // Apply any converters.
2165  int i = 0;
2166 
2167  for (int z = 0; z < myRes[2]; z++)
2168  {
2169  for (int y = 0; y < myRes[1]; y++)
2170  {
2171  for (int x = 0; x < myRes[0]; x++)
2172  {
2173  if constexpr (tuple_size == 1)
2174  data[i++] = UTvoxelConvertFP16((*this)(x, y, z));
2175  else
2176  {
2177  T value = (*this)(x, y, z);
2178  for (int j = 0; j < tuple_size; j++)
2179  {
2180  data[i++] = UTvoxelConvertFP16(value(j));
2181  }
2182  }
2183  }
2184  }
2185  }
2186  }
2187 
2188  freeData();
2189  myData = data;
2190  myCompressionType = COMPRESS_FPREAL16;
2191 }
2192 
2193 template <typename T>
2194 int64
2195 UT_VoxelTile<T>::getMemoryUsage(bool inclusive) const
2196 {
2197  int64 mem = inclusive ? sizeof(*this) : 0;
2198  mem += getDataLength();
2199  return mem;
2200 }
2201 
2202 template <typename T>
2203 int64
2205 {
2206  exint usage;
2207 
2208  switch (myCompressionType)
2209  {
2210  case COMPRESS_RAW:
2211  usage = sizeof(T) * xres() * yres() * zres();
2212  break;
2213  case COMPRESS_FPREAL16:
2214  usage = sizeof(fpreal16) * xres() * yres() * zres()
2216  break;
2217  case COMPRESS_CONSTANT:
2218  if (inlineConstant())
2219  usage = 0;
2220  else
2221  usage = sizeof(T);
2222  break;
2223  case COMPRESS_RAWFULL:
2224  usage = sizeof(T) * TILESIZE * TILESIZE * zres();
2225  break;
2226  default:
2227  {
2228  // Use the compression engine.
2229  UT_VoxelTileCompress<T> *engine;
2230  engine = getCompressionEngine(myCompressionType);
2231  usage = engine->getDataLength(*this);
2232  break;
2233  }
2234  }
2235  return usage;
2236 }
2237 
2238 template <typename T>
2239 void
2240 UT_VoxelTile<T>::weightedSum(int pstart[3], int pend[3],
2241  const float *weights[3], int start[3],
2242  T &result)
2243 {
2244  int ix, iy, iz, i;
2245  int px, py, pz;
2246  int ixstart, ixend;
2247  int tstart[3];
2248  fpreal w, pw;
2249  T psumx, psumy;
2250 
2251  switch (myCompressionType)
2252  {
2253  case COMPRESS_CONSTANT:
2254  {
2255  w = 1;
2256  for (i = 0; i < 3; i++)
2257  {
2258  pw = 0;
2259  for (ix = 0; ix < pend[i]-pstart[i]; ix++)
2260  pw += weights[i][ix+pstart[i]-start[i]];
2261  w *= pw;
2262  }
2263  result += w * rawConstVal();
2264  break;
2265  }
2266 
2267  case COMPRESS_RAW:
2268  {
2269  tstart[0] = pstart[0] & TILEMASK;
2270  tstart[1] = pstart[1] & TILEMASK;
2271  tstart[2] = pstart[2] & TILEMASK;
2272  ixstart = pstart[0]-start[0];
2273  ixend = pend[0]-start[0];
2274  pz = tstart[2];
2275  UT_ASSERT(pz < myRes[2]);
2276  UT_ASSERT(ixend - ixstart <= myRes[0]);
2277  for (iz = pstart[2]; iz < pend[2]; iz++, pz++)
2278  {
2279  psumy = 0;
2280  py = tstart[1];
2281  UT_ASSERT(py < myRes[1]);
2282  for (iy = pstart[1]; iy < pend[1]; iy++, py++)
2283  {
2284  psumx = 0;
2285  px = ((pz * myRes[1]) + py) * myRes[0] + tstart[0];
2286  for (ix = ixstart; ix < ixend; ix++, px++)
2287  {
2288  psumx += weights[0][ix]* ((T*)myData)[px];
2289  }
2290  psumy += weights[1][iy-start[1]] * psumx;
2291  }
2292  result += weights[2][iz-start[2]] * psumy;
2293  }
2294  break;
2295  }
2296 
2297  case COMPRESS_FPREAL16:
2298  {
2299  constexpr exint tuple_size = UT_FixedVectorTraits<T>::TupleSize;
2300  static constexpr UT_FromUnbounded<T> convertFromFP16{};
2301 
2302  int xinc = tuple_size;
2303  int yinc = myRes[0] * xinc;
2304  int zinc = myRes[1] * yinc;
2305 
2306  fpreal16 *src = (fpreal16 *) myData;
2307 
2308  tstart[0] = pstart[0] & TILEMASK;
2309  tstart[1] = pstart[1] & TILEMASK;
2310  tstart[2] = pstart[2] & TILEMASK;
2311  ixstart = pstart[0]-start[0];
2312  ixend = pend[0]-start[0];
2313  pz = tstart[2];
2314  UT_ASSERT(pz < myRes[2]);
2315  UT_ASSERT(ixend - ixstart <= myRes[0]);
2316  for (iz = pstart[2]; iz < pend[2]; iz++, pz++)
2317  {
2318  psumy = 0;
2319  py = tstart[1];
2320  UT_ASSERT(py < myRes[1]);
2321  for (iy = pstart[1]; iy < pend[1]; iy++, py++)
2322  {
2323  psumx = 0;
2324  px = pz * zinc + py * yinc + tstart[0] * xinc;
2325  for (ix = ixstart; ix < ixend; ix++, px += xinc)
2326  {
2327  T val = convertFromFP16(src + px);
2328  psumx += weights[0][ix]* val;
2329  }
2330  psumy += weights[1][iy-start[1]] * psumx;
2331  }
2332  result += weights[2][iz-start[2]] * psumy;
2333  }
2334  break;
2335  }
2336  case COMPRESS_RAWFULL:
2337  {
2338  tstart[0] = pstart[0] & TILEMASK;
2339  tstart[1] = pstart[1] & TILEMASK;
2340  tstart[2] = pstart[2] & TILEMASK;
2341  ixstart = pstart[0]-start[0];
2342  ixend = pend[0]-start[0];
2343  pz = tstart[2];
2344  for (iz = pstart[2]; iz < pend[2]; iz++, pz++)
2345  {
2346  psumy = 0;
2347  py = tstart[1];
2348  for (iy = pstart[1]; iy < pend[1]; iy++, py++)
2349  {
2350  psumx = 0;
2351  px = ((pz * TILESIZE) + py) * TILESIZE + tstart[0];
2352  for (ix = ixstart; ix < ixend; ix++, px++)
2353  {
2354  psumx += weights[0][ix]* ((T*)myData)[px];
2355  }
2356  psumy += weights[1][iy-start[1]] * psumx;
2357  }
2358  result += weights[2][iz-start[2]] * psumy;
2359  }
2360  break;
2361  }
2362 
2363  default:
2364  {
2365  // For all other compression types, we use our
2366  // getValue() accessor rather than trying to
2367  // do anything fancy.
2368  tstart[0] = pstart[0] & TILEMASK;
2369  tstart[1] = pstart[1] & TILEMASK;
2370  tstart[2] = pstart[2] & TILEMASK;
2371  ixstart = pstart[0]-start[0];
2372  ixend = pend[0]-start[0];
2373  pz = tstart[2];
2374  for (iz = pstart[2]; iz < pend[2]; iz++, pz++)
2375  {
2376  psumy = 0;
2377  py = tstart[1];
2378  for (iy = pstart[1]; iy < pend[1]; iy++, py++)
2379  {
2380  psumx = 0;
2381  px = tstart[0];
2382  for (ix = ixstart; ix < ixend; ix++, px++)
2383  {
2384  psumx += weights[0][ix] *
2385  (*this)(px, py, pz);
2386  }
2387  psumy += weights[1][iy-start[1]] * psumx;
2388  }
2389  result += weights[2][iz-start[2]] * psumy;
2390  }
2391  break;
2392  }
2393  }
2394 }
2395 
2396 template <typename T>
2397 void
2398 UT_VoxelTile<T>::avgNonZero(int pstart[3], int pend[3], int start[3],
2399  T &result)
2400 {
2401  int ix, iy, iz;
2402  int px, py, pz;
2403  int ixstart, ixend;
2404  int tstart[3];
2405 
2406  #define COUNT_NONZERO(VAL, COUNT) \
2407  if constexpr (SYS_IsSame_v<T, float>) \
2408  { \
2409  if (VAL != T(0)) \
2410  COUNT++; \
2411  } \
2412  if constexpr (SYS_IsSame_v<T, UT_Vector2>) \
2413  { \
2414  if (!VAL.isZero()) \
2415  COUNT++; \
2416  } \
2417  if constexpr (SYS_IsSame_v<T, UT_Vector3>) \
2418  { \
2419  if (!VAL.isZero()) \
2420  COUNT++; \
2421  } \
2422  if constexpr (SYS_IsSame_v<T, UT_Vector4>) \
2423  { \
2424  if (!VAL.isZero()) \
2425  COUNT++; \
2426  } \
2427 
2428  switch (myCompressionType)
2429  {
2430  case COMPRESS_CONSTANT:
2431  {
2432  result += rawConstVal();
2433  break;
2434  }
2435 
2436  case COMPRESS_RAW:
2437  {
2438  tstart[0] = pstart[0] & TILEMASK;
2439  tstart[1] = pstart[1] & TILEMASK;
2440  tstart[2] = pstart[2] & TILEMASK;
2441  ixstart = pstart[0]-start[0];
2442  ixend = pend[0]-start[0];
2443  pz = tstart[2];
2444  UT_ASSERT(pz < myRes[2]);
2445  UT_ASSERT(ixend - ixstart <= myRes[0]);
2446  int count = 0;
2447  for (iz = pstart[2]; iz < pend[2]; iz++, pz++)
2448  {
2449  py = tstart[1];
2450  UT_ASSERT(py < myRes[1]);
2451  for (iy = pstart[1]; iy < pend[1]; iy++, py++)
2452  {
2453  px = ((pz * myRes[1]) + py) * myRes[0] + tstart[0];
2454  for (ix = ixstart; ix < ixend; ix++, px++)
2455  {
2456  T val = ((T*)myData)[px];
2457  result += val;
2458  COUNT_NONZERO(val, count);
2459  }
2460  }
2461  }
2462  result *= SYSsaferecip(float(count));
2463  break;
2464  }
2465 
2466  case COMPRESS_FPREAL16:
2467  {
2468  constexpr exint tuple_size = UT_FixedVectorTraits<T>::TupleSize;
2469  static constexpr UT_FromUnbounded<T> convertFromFP16{};
2470 
2471  int xinc = tuple_size;
2472  int yinc = myRes[0] * xinc;
2473  int zinc = myRes[1] * yinc;
2474 
2475  fpreal16 *src = (fpreal16 *) myData;
2476 
2477  tstart[0] = pstart[0] & TILEMASK;
2478  tstart[1] = pstart[1] & TILEMASK;
2479  tstart[2] = pstart[2] & TILEMASK;
2480  ixstart = pstart[0]-start[0];
2481  ixend = pend[0]-start[0];
2482  pz = tstart[2];
2483  UT_ASSERT(pz < myRes[2]);
2484  UT_ASSERT(ixend - ixstart <= myRes[0]);
2485  int count = 0;
2486  for (iz = pstart[2]; iz < pend[2]; iz++, pz++)
2487  {
2488  py = tstart[1];
2489  UT_ASSERT(py < myRes[1]);
2490  for (iy = pstart[1]; iy < pend[1]; iy++, py++)
2491  {
2492  px = pz * zinc + py * yinc + tstart[0] * xinc;
2493  for (ix = ixstart; ix < ixend; ix++, px += xinc)
2494  {
2495  T val = convertFromFP16(src + px);
2496  result += val;
2497  COUNT_NONZERO(val, count);
2498  }
2499  }
2500  }
2501  result *= SYSsaferecip(float(count));
2502  break;
2503  }
2504  case COMPRESS_RAWFULL:
2505  {
2506  tstart[0] = pstart[0] & TILEMASK;
2507  tstart[1] = pstart[1] & TILEMASK;
2508  tstart[2] = pstart[2] & TILEMASK;
2509  ixstart = pstart[0]-start[0];
2510  ixend = pend[0]-start[0];
2511  pz = tstart[2];
2512  int count = 0;
2513  for (iz = pstart[2]; iz < pend[2]; iz++, pz++)
2514  {
2515  py = tstart[1];
2516  for (iy = pstart[1]; iy < pend[1]; iy++, py++)
2517  {
2518  px = ((pz * TILESIZE) + py) * TILESIZE + tstart[0];
2519  for (ix = ixstart; ix < ixend; ix++, px++)
2520  {
2521  T val = ((T*)myData)[px];
2522  result += val;
2523  COUNT_NONZERO(val, count);
2524  }
2525  }
2526  }
2527  result *= SYSsaferecip(float(count));
2528  break;
2529  }
2530 
2531  default:
2532  {
2533  // For all other compression types, we use our
2534  // getValue() accessor rather than trying to
2535  // do anything fancy.
2536  tstart[0] = pstart[0] & TILEMASK;
2537  tstart[1] = pstart[1] & TILEMASK;
2538  tstart[2] = pstart[2] & TILEMASK;
2539  ixstart = pstart[0]-start[0];
2540  ixend = pend[0]-start[0];
2541  pz = tstart[2];
2542  int count = 0;
2543  for (iz = pstart[2]; iz < pend[2]; iz++, pz++)
2544  {
2545  py = tstart[1];
2546  for (iy = pstart[1]; iy < pend[1]; iy++, py++)
2547  {
2548  px = tstart[0];
2549  for (ix = ixstart; ix < ixend; ix++, px++)
2550  {
2551  T val = (*this)(px, py, pz);
2552  result += val;
2553  COUNT_NONZERO(val, count);
2554  }
2555  }
2556  }
2557  result *= SYSsaferecip(float(count));
2558  break;
2559  }
2560  }
2561 }
2562 
2563 template <typename T>
2564 void
2566 {
2567  int i;
2568 
2569  // Repalce old copy, if any.
2570  for (i = 0; i < getCompressionEngines().entries(); i++)
2571  {
2572  if (!strcmp(engine->getName(), getCompressionEngines()(i)->getName()))
2573  {
2574  getCompressionEngines()(i) = engine;
2575  return;
2576  }
2577  }
2578 
2579  getCompressionEngines().append(engine);
2580 }
2581 
2582 template <typename T>
2583 int
2585 {
2586  int i;
2587 
2588  if (!name)
2589  return -1;
2590 
2591  for (i = 0; i < getCompressionEngines().entries(); i++)
2592  {
2593  if (!strcmp(name, getCompressionEngines()(i)->getName()))
2594  {
2595  return i + COMPRESS_ENGINE;
2596  }
2597  }
2598 
2599  return -1;
2600 }
2601 
2602 template <typename T>
2605 {
2606  index -= COMPRESS_ENGINE;
2607 
2608  return getCompressionEngines()(index);
2609 }
2610 
2611 template <typename T>
2612 void
2613 UT_VoxelTile<T>::save(std::ostream &os) const
2614 {
2615  // Always save in our native format...
2616  if (myCompressionType >= COMPRESS_ENGINE)
2617  {
2618  UT_VoxelTileCompress<T> *engine = getCompressionEngine(myCompressionType);
2619 
2620  if (engine->canSave())
2621  {
2622  char type = myCompressionType;
2623 
2624  UTwrite(os, &type, 1);
2625  engine->save(os, *this);
2626  return;
2627  }
2628 
2629  // Can't save, must save as raw.
2630  char type = COMPRESS_RAW;
2631  T value;
2632 
2633  UTwrite(os, &type, 1);
2634 
2635  for (int z = 0; z < zres(); z++)
2636  for (int y = 0; y < yres(); y++)
2637  for (int x = 0; x < xres(); x++)
2638  {
2639  value = (*this)(x, y, z);
2640  UTwrite<T>(os, &value, 1);
2641  }
2642  return;
2643  }
2644 
2645  int len;
2646  char type = myCompressionType;
2647 
2648  if (type == COMPRESS_RAWFULL &&
2649  myRes[0] == TILESIZE &&
2650  myRes[1] == TILESIZE &&
2651  myRes[2] != TILESIZE)
2652  {
2653  // Forbid saving this as a raw full as that will confuse
2654  // older versions of Houdini.
2655  type = COMPRESS_RAW;
2656  }
2657 
2658  UT_ASSERT(type >= 0 && type < COMPRESS_ENGINE);
2659 
2660  UTwrite(os, &type, 1);
2661 
2662  switch ((CompressionType) type)
2663  {
2664  case COMPRESS_RAW:
2665  len = myRes[2] * myRes[1] * myRes[0];
2666  UTwrite<T>(os, (T *) myData, len);
2667  break;
2668 
2669  case COMPRESS_FPREAL16:
2670  len = myRes[2] * myRes[1] * myRes[0]
2672  UTwrite(os, (int16 *) myData, len);
2673  break;
2674 
2675  case COMPRESS_RAWFULL:
2676  len = TILESIZE * TILESIZE * TILESIZE;
2677  UTwrite<T>(os, (T *) myData, len);
2678  break;
2679 
2680  case COMPRESS_CONSTANT:
2681  UTwrite<T>(os, rawConstData(), 1);
2682  break;
2683 
2684  case COMPRESS_ENGINE:
2685  UT_ASSERT(!"Invalid compression type");
2686  break;
2687  }
2688 }
2689 
2690 template <typename T>
2691 void
2693 {
2694  char type, otype;
2695  int len;
2696 
2697  is.readChar(type);
2698 
2699  // Perform the indirection to find out our native type.
2700  if (type >= 0 && type < compress.entries())
2701  {
2702  otype = type;
2703  type = compress(type);
2704  if (type == -1)
2705  {
2706  std::cerr << "Missing compression engine " << (int) otype << "\n";
2707  }
2708  }
2709 
2710  if (type >= COMPRESS_ENGINE)
2711  {
2712  freeData();
2713  myCompressionType = type;
2714 
2715  if (type - COMPRESS_ENGINE >= getCompressionEngines().entries())
2716  {
2717  // Invalid type!
2718  std::cerr << "Invalid compression engine " << (int) otype << "\n";
2719  return;
2720  }
2721 
2722  UT_VoxelTileCompress<T> *engine = getCompressionEngine(myCompressionType);
2723 
2724  engine->load(is, *this);
2725 
2726  return;
2727  }
2728 
2729  UT_ASSERT(type >= 0);
2730  if (type < 0)
2731  {
2732  return;
2733  }
2734 
2735  freeData();
2736 
2737  myCompressionType = type;
2738 
2739  switch ((CompressionType) myCompressionType)
2740  {
2741  case COMPRESS_RAW:
2742  len = myRes[2] * myRes[1] * myRes[0];
2743  myData = UT_VOXEL_ALLOC(sizeof(T) * len);
2744  is.read<T>((T *) myData, len);
2745  break;
2746 
2747  case COMPRESS_FPREAL16:
2748  len = myRes[2] * myRes[1] * myRes[0]
2750  myData = UT_VOXEL_ALLOC(sizeof(fpreal16) * len);
2751  is.read((int16 *) myData, len);
2752  break;
2753 
2754  case COMPRESS_RAWFULL:
2755  len = TILESIZE * TILESIZE * TILESIZE;
2756  myData = UT_VOXEL_ALLOC(sizeof(T) * len);
2757  is.read<T>((T *) myData, len);
2758  break;
2759 
2760  case COMPRESS_CONSTANT:
2761  if (!inlineConstant())
2762  myData = UT_VOXEL_ALLOC(sizeof(T));
2763  is.read<T>(rawConstData(), 1);
2764  break;
2765 
2766  case COMPRESS_ENGINE:
2767  UT_ASSERT(!"Invalid compression type");
2768  break;
2769  }
2770 
2771  // Recompress raw full that are not complete in z
2772  if (myCompressionType == COMPRESS_RAW &&
2773  myRes[0] == TILESIZE &&
2774  myRes[1] == TILESIZE)
2775  myCompressionType = COMPRESS_RAWFULL;
2776 }
2777 
2778 template <typename T>
2779 bool
2781 {
2782  constexpr exint tuple_size = UT_FixedVectorTraits<T>::TupleSize;
2783  using ScalarType = typename UT_FixedVectorTraits<T>::DataType;
2784 
2785  bool ok = true;
2786 
2787  // Always save in our native format...
2788  if (myCompressionType >= COMPRESS_ENGINE)
2789  {
2790  UT_VoxelTileCompress<T> *engine = getCompressionEngine(myCompressionType);
2791 
2792  if (engine->canSave())
2793  {
2794  char type = myCompressionType;
2795 
2796  ok = ok && w.jsonBeginArray();
2797  ok = ok && w.jsonKey(UT_VoxelArrayJSON::getToken(
2799  ok = ok && w.jsonInt(type);
2800  ok = ok && w.jsonKey(UT_VoxelArrayJSON::getToken(
2802  ok = ok && engine->save(w, *this);
2803  ok = ok && w.jsonEndArray();
2804  return ok;
2805  }
2806 
2807  // Can't save, must save as raw.
2808  char type = COMPRESS_RAW;
2809  T value;
2810  ScalarType dummy;
2811 
2812  ok = ok && w.jsonBeginArray();
2813  ok = ok && w.jsonKey(UT_VoxelArrayJSON::getToken(
2815  ok = ok && w.jsonInt(type);
2816  ok = ok && w.jsonKey(UT_VoxelArrayJSON::getToken(
2818 
2819  ok = ok && w.beginUniformArray(xres()*yres()*zres()*tuple_size,
2820  w.jidFromValue(&dummy));
2821  for (int z = 0; z < zres(); z++)
2822  for (int y = 0; y < yres(); y++)
2823  for (int x = 0; x < xres(); x++)
2824  {
2825  value = (*this)(x, y, z);
2826  if constexpr (tuple_size == 1)
2827  {
2828  ok = ok && w.uniformWrite(value);
2829  }
2830  else
2831  {
2832  for (int i = 0; i < tuple_size; i++)
2833  {
2834  ok = ok && w.uniformWrite(value(i));
2835  }
2836  }
2837  }
2838  ok = ok && w.endUniformArray();
2839  ok = ok && w.jsonEndArray();
2840  return ok;
2841  }
2842 
2843  int len;
2844  char type = myCompressionType;
2845 
2846  UT_ASSERT(type >= 0 && type < COMPRESS_ENGINE);
2847  ok = ok && w.jsonBeginArray();
2848  ok = ok && w.jsonKey(UT_VoxelArrayJSON::getToken(
2850  ok = ok && w.jsonInt(type);
2851  ok = ok && w.jsonKey(UT_VoxelArrayJSON::getToken(
2853 
2854  switch (myCompressionType)
2855  {
2856  case COMPRESS_RAW:
2857  len = myRes[2] * myRes[1] * myRes[0];
2858  ok = ok && w.jsonUniformArray(len * tuple_size, (ScalarType *)myData);
2859  break;
2860 
2861  case COMPRESS_FPREAL16:
2862  len = myRes[2] * myRes[1] * myRes[0] * UT_FixedVectorTraits<T>::TupleSize;
2863  ok = ok && w.jsonUniformArray(len, (fpreal16 *)myData);
2864  break;
2865 
2866  case COMPRESS_RAWFULL:
2867  len = TILESIZE * TILESIZE * myRes[2];
2868  ok = ok && w.jsonUniformArray(len * tuple_size, (ScalarType *)myData);
2869  break;
2870 
2871  case COMPRESS_CONSTANT:
2872  if constexpr (tuple_size == 1)
2873  ok = ok && w.jsonValue(rawConstVal());
2874  else
2875  ok = ok && w.jsonUniformArray(tuple_size, rawConstVal().data());
2876  break;
2877  }
2878 
2879  ok = ok && w.jsonEndArray();
2880  return ok;
2881 }
2882 
2883 template <typename T>
2884 bool
2886 {
2887  constexpr exint tuple_size = UT_FixedVectorTraits<T>::TupleSize;
2888  using ScalarType = typename UT_FixedVectorTraits<T>::DataType;
2889 
2891  UT_WorkBuffer key;
2892  int8 type, otype;
2893  int len;
2894 
2895  it = p.beginArray();
2896  if (it.atEnd() || !it.getLowerKey(key))
2897  return false;
2898  if (UT_VoxelArrayJSON::getTileID(key.buffer()) !=
2900  return false;
2901  if (!p.parseNumber(type))
2902  return false;
2903 
2904  ++it;
2905 
2906  if (it.atEnd() || !it.getLowerKey(key))
2907  return false;
2908  if (UT_VoxelArrayJSON::getTileID(key.buffer()) !=
2910  return false;
2911 
2912  // Perform the indirection to find out our native type.
2913  if (type >= 0 && type < compress.entries())
2914  {
2915  otype = type;
2916  type = compress(type);
2917  if (type == -1)
2918  {
2919  std::cerr << "Missing compression engine " << (int) otype << "\n";
2920  }
2921  }
2922 
2923  if (type >= COMPRESS_ENGINE)
2924  {
2925  freeData();
2926  myCompressionType = type;
2927 
2928  if (type - COMPRESS_ENGINE >= getCompressionEngines().entries())
2929  {
2930  // Invalid type!
2931  std::cerr << "Invalid compression engine " << (int) otype << "\n";
2932  return false;
2933  }
2934 
2935  UT_VoxelTileCompress<T> *engine = getCompressionEngine(myCompressionType);
2936 
2937  bool engine_ok = engine->load(p, *this);
2938  ++it;
2939  return it.atEnd() && engine_ok;
2940  }
2941 
2942  UT_ASSERT(type >= 0);
2943  if (type < 0)
2944  {
2945  return false;
2946  }
2947 
2948  freeData();
2949 
2950  myCompressionType = type;
2951 
2952  switch (myCompressionType)
2953  {
2954  case COMPRESS_RAW:
2955  len = myRes[2] * myRes[1] * myRes[0];
2956  myData = UT_VOXEL_ALLOC(sizeof(T) * len);
2957  len *= tuple_size;
2958  if (p.parseUniformArray((ScalarType *)myData, len) != len)
2959  return false;
2960  break;
2961 
2962  case COMPRESS_FPREAL16:
2963  len = myRes[2] * myRes[1] * myRes[0] * UT_FixedVectorTraits<T>::TupleSize;
2964  myData = UT_VOXEL_ALLOC(sizeof(fpreal16) * len);
2965  if (p.parseUniformArray((fpreal16 *)myData, len) != len)
2966  return false;
2967  break;
2968 
2969  case COMPRESS_RAWFULL:
2970  len = TILESIZE * TILESIZE * myRes[2];
2971  myData = UT_VOXEL_ALLOC(sizeof(T) * len);
2972  len *= tuple_size;
2973  if (p.parseUniformArray((ScalarType *)myData, len) != len)
2974  return false;
2975  break;
2976 
2977  case COMPRESS_CONSTANT:
2978  if (!inlineConstant())
2979  myData = UT_VOXEL_ALLOC(sizeof(T));
2980  if constexpr (tuple_size == 1)
2981  {
2982  if (!p.parseNumber(*rawConstData()))
2983  return false;
2984  }
2985  else
2986  {
2987  if (!p.parseUniformArray((ScalarType *) rawConstData()->data(), tuple_size))
2988  return false;
2989  }
2990  break;
2991  }
2992 
2993  ++it;
2994  return it.atEnd();
2995 }
2996 
2997 template <typename T>
2998 void
3000 {
3001  int16 ntype = getCompressionEngines().entries();
3002  int i;
3003 
3004  ntype += COMPRESS_ENGINE;
3005 
3006  UTwrite(os, &ntype);
3007 
3008  UT_ASSERT(COMPRESS_ENGINE == 4); // Change lower lines!
3010  UTsaveStringBinary(os, "rawfull", UT_STRING_8BIT_IO);
3011  UTsaveStringBinary(os, "constant", UT_STRING_8BIT_IO);
3012  UTsaveStringBinary(os, "fpreal16", UT_STRING_8BIT_IO);
3013 
3014  ntype -= COMPRESS_ENGINE;
3015  for (i = 0; i < ntype; i++)
3016  {
3017  UTsaveStringBinary(os, getCompressionEngines()(i)->getName(), UT_STRING_8BIT_IO);
3018  }
3019 }
3020 
3021 template <typename T>
3022 void
3024 {
3025  int16 ntype;
3026  int i, idx;
3027 
3028  compress.entries(0);
3029 
3030  is.read(&ntype);
3031 
3032  for (i = 0; i < ntype; i++)
3033  {
3034  UT_String name;
3035 
3037  if (name == "raw")
3038  compress.append(COMPRESS_RAW);
3039  else if (name == "rawfull")
3040  compress.append(COMPRESS_RAWFULL);
3041  else if (name == "constant")
3042  compress.append(COMPRESS_CONSTANT);
3043  else if (name == "fpreal16")
3044  compress.append(COMPRESS_FPREAL16);
3045  else
3046  {
3047  idx = lookupCompressionEngine(name);
3048 
3049  // -1 means a failure to find it in our set..
3050  // this is only a bad thing if a tile actually uses this engine.
3051 
3052  compress.append(idx);
3053  }
3054  }
3055 }
3056 
3057 template <typename T>
3058 bool
3060 {
3061  int16 ntype = getCompressionEngines().entries();
3062  int i;
3063  bool ok = true;
3064 
3065  ntype += COMPRESS_ENGINE;
3066 
3067  UT_ASSERT(COMPRESS_ENGINE == 4); // Change lower lines!
3068  ok = ok && w.beginUniformArray(ntype, UT_JID_STRING);
3069  ok = ok && w.uniformWrite("raw");
3070  ok = ok && w.uniformWrite("rawfull");
3071  ok = ok && w.uniformWrite("constant");
3072  ok = ok && w.uniformWrite("fpreal16");
3073 
3074  ntype -= COMPRESS_ENGINE;
3075  for (i = 0; i < ntype; i++)
3076  {
3077  ok = ok && w.uniformWrite(getCompressionEngines()(i)->getName());
3078  }
3079 
3080  ok = ok && w.endUniformArray();
3081  return ok;
3082 }
3083 
3084 template <typename T>
3085 bool
3087 {
3090  int idx;
3091 
3092  compress.entries(0);
3093  for (it = p.beginArray(); !it.atEnd(); ++it)
3094  {
3095  if (!p.parseString(buffer))
3096  return false;
3097 
3098  if (!buffer.strcmp("raw"))
3099  compress.append(COMPRESS_RAW);
3100  else if (!buffer.strcmp("rawfull"))
3101  compress.append(COMPRESS_RAWFULL);
3102  else if (!buffer.strcmp("constant"))
3103  compress.append(COMPRESS_CONSTANT);
3104  else if (!buffer.strcmp("fpreal16"))
3105  compress.append(COMPRESS_FPREAL16);
3106  else
3107  {
3108  idx = lookupCompressionEngine(buffer.buffer());
3109 
3110  // -1 means a failure to find it in our set..
3111  // this is only a bad thing if a tile actually uses this engine.
3112 
3113  compress.append(idx);
3114  }
3115  }
3116  return true;
3117 }
3118 
3119 
3120 
3121 //
3122 // VoxelArray definitions.
3123 //
3124 
3125 template <typename T>
3127 {
3128  myRes[0] = 0;
3129  myRes[1] = 0;
3130  myRes[2] = 0;
3131  myTileRes[0] = 0;
3132  myTileRes[1] = 0;
3133  myTileRes[2] = 0;
3134 
3135  myTiles = 0;
3136 
3137  myInvRes = 1;
3138 
3139  myBorderValue = 0;
3140  myBorderScale[0] = 0;
3141  myBorderScale[1] = 0;
3142  myBorderScale[2] = 0;
3143  myBorderType = UT_VOXELBORDER_STREAK;
3144 
3145  mySharedMem = 0;
3146  mySharedMemView = 0;
3147 }
3148 
3149 template <typename T>
3151 {
3152  deleteVoxels();
3153 }
3154 
3155 template <typename T>
3157 {
3158  myRes[0] = 0;
3159  myRes[1] = 0;
3160  myRes[2] = 0;
3161  myInvRes = 1;
3162  myTileRes[0] = 0;
3163  myTileRes[1] = 0;
3164  myTileRes[2] = 0;
3165 
3166  myTiles = 0;
3167 
3168  mySharedMem = 0;
3169  mySharedMemView = 0;
3170 
3171  *this = src;
3172 }
3173 
3174 template <typename T>
3175 void
3177  const UT_JobInfo &info)
3178 {
3179  UT_ASSERT(isMatching(src));
3180  UT_VoxelArrayIterator<T> vit(this);
3181  vit.splitByTile(info);
3182  for (vit.rewind(); !vit.atEnd(); vit.advanceTile())
3183  {
3184  int i = vit.getLinearTileNum();
3185  myTiles[i] = src.myTiles[i];
3186  }
3187 }
3188 
3189 template <typename T>
3190 const UT_VoxelArray<T> &
3192 {
3193  // Paranoid code:
3194  if (&src == this)
3195  return *this;
3196 
3197  myBorderScale[0] = src.myBorderScale[0];
3198  myBorderScale[1] = src.myBorderScale[1];
3199  myBorderScale[2] = src.myBorderScale[2];
3200  myBorderValue = src.myBorderValue;
3201  myBorderType = src.myBorderType;
3202 
3203  myCompressionOptions = src.myCompressionOptions;
3204 
3205  // Allocate proper size; don't bother setting to zero, since we'll copy the
3206  // data immediately after.
3207  size(src.myRes[0], src.myRes[1], src.myRes[2], false);
3208 
3209  copyData(src);
3210 
3211  return *this;
3212 }
3213 
3214 // If defined, construction and destruction of tiles will be multithreaded.
3215 // Otherwise, new[] and delete[] take care of calling these methods.
3216 // Unfortunately, this doesn't work at the moment...
3217 #define __MULTITHREADED_STRUCTORS__
3218 template <typename T>
3219 void
3221 {
3222 #ifdef __MULTITHREADED_STRUCTORS__
3223  // Free up each tile's heap data, then bulk-deallocate the array.
3225  [&](const UT_BlockedRange<int>& range)
3226  {
3227  for(int i = range.begin(); i < range.end(); i++)
3228  {
3229  myTiles[i].~UT_VoxelTile<T>();
3230  }
3231  });
3232  operator delete(myTiles, std::nothrow);
3233 #else
3234  delete [] myTiles;
3235 #endif
3236  myTiles = 0;
3237 
3238  myTileRes[0] = myTileRes[1] = myTileRes[2] = 0;
3239  myRes[0] = myRes[1] = myRes[2] = 0;
3240 
3241  delete mySharedMemView;
3242  mySharedMemView = 0;
3243 
3244  delete mySharedMem;
3245  mySharedMem = 0;
3246 }
3247 
3248 template <typename T>
3249 void
3250 UT_VoxelArray<T>::size(int xres, int yres, int zres, bool zero)
3251 {
3252  // Check if the sizes are already right.
3253  if (myRes[0] == xres && myRes[1] == yres && myRes[2] == zres)
3254  {
3255  if (zero)
3256  {
3257  T tzero;
3258  tzero = 0;
3259  constant(tzero);
3260  }
3261  return;
3262  }
3263 
3264  deleteVoxels();
3265 
3266  // Initialize our tiles.
3267  int tile_res[3];
3268  tile_res[0] = (xres + TILEMASK) >> TILEBITS;
3269  tile_res[1] = (yres + TILEMASK) >> TILEBITS;
3270  tile_res[2] = (zres + TILEMASK) >> TILEBITS;
3271 
3272  exint ntiles = ((exint)tile_res[0]) * tile_res[1] * tile_res[2];
3273  if (ntiles)
3274  {
3275 #ifdef __MULTITHREADED_STRUCTORS__
3276  // Allocate the big array of the right size, then use placement new to
3277  // construct each tile.
3278  // Note that it is not guaranteed that the returned pointer is properly
3279  // aligned with raw operator new. However, we only practically need 8-byte
3280  // alignment given the current implementation, which should be respected by
3281  // operator new.
3282  // We have to wait for C++17 for version of operator new that accepts
3283  // alignment requirements...
3284  myTiles = (UT_VoxelTile<T>*) operator new(sizeof(UT_VoxelTile<T>) * ntiles,
3285  std::nothrow);
3287  [&](const UT_BlockedRange<int>& range)
3288  {
3289  for(int k = range.begin(); k < range.end(); k++)
3290  {
3291  new(myTiles + k) UT_VoxelTile<T>();
3292 
3293  // Set the resolution of this tile.
3294  int tilex = k % tile_res[0];
3295  int k2 = k / tile_res[0];
3296  int tiley = k2 % tile_res[1];
3297  int tilez = k2 / tile_res[1];
3298  myTiles[k].setRes(SYSmin(TILESIZE, xres - tilex * TILESIZE),
3299  SYSmin(TILESIZE, yres - tiley * TILESIZE),
3300  SYSmin(TILESIZE, zres - tilez * TILESIZE));
3301  }
3302  });
3303 #else
3304  myTiles = new UT_VoxelTile<T>[ntiles];
3305 #endif
3306 
3307  // Only set resolutions *AFTER* we successfully allocate
3308  myRes[0] = xres;
3309  myRes[1] = yres;
3310  myRes[2] = zres;
3311 
3312  myInvRes = 1;
3313  if (xres)
3314  myInvRes.x() = 1.0f / myRes[0];
3315  if (yres)
3316  myInvRes.y() = 1.0f / myRes[1];
3317  if (zres)
3318  myInvRes.z() = 1.0f / myRes[2];
3319 
3320  myTileRes[0] = tile_res[0];
3321  myTileRes[1] = tile_res[1];
3322  myTileRes[2] = tile_res[2];
3323 
3324 #ifndef __MULTITHREADED_STRUCTORS__
3325  int i = 0;
3326  for (int tz = 0; tz < myTileRes[2]; tz++)
3327  {
3328  int zr;
3329  if (tz < myTileRes[2]-1)
3330  zr = TILESIZE;
3331  else
3332  zr = zres - tz * TILESIZE;
3333 
3334  for (int ty = 0; ty < myTileRes[1]; ty++)
3335  {
3336  int yr;
3337  if (ty < myTileRes[1]-1)
3338  yr = TILESIZE;
3339  else
3340  yr = yres - ty * TILESIZE;
3341 
3342  int tx, xr = TILESIZE;
3343  for (tx = 0; tx < myTileRes[0]-1; tx++)
3344  {
3345  myTiles[i].setRes(xr, yr, zr);
3346 
3347  i++;
3348  }
3349  xr = xres - tx * TILESIZE;
3350  myTiles[i].setRes(xr, yr, zr);
3351  i++;
3352  }
3353  }
3354 #endif
3355  }
3356  else
3357  myTiles = 0;
3358 }
3359 
3360 template <typename T>
3361 void
3363 {
3364  // This call will only actually resize if the sizes are different. If it
3365  // does not resize, contents will be left alone.
3366  size(src.getXRes(), src.getYRes(), src.getZRes(), false);
3367 
3368  // Update border conditions.
3369  myBorderType = src.myBorderType;
3370  myBorderScale[0] = src.myBorderScale[0];
3371  myBorderScale[1] = src.myBorderScale[1];
3372  myBorderScale[2] = src.myBorderScale[2];
3373  myBorderValue = src.myBorderValue;
3374 
3375  // Match our compression tolerances
3376  myCompressionOptions = src.myCompressionOptions;
3377 }
3378 
3379 template <typename T>
3380 int64
3382 {
3383  int64 mem = inclusive ? sizeof(*this) : 0;
3384 
3385  int ntiles = numTiles();
3386  for (int i = 0; i < ntiles; i++)
3387  mem += myTiles[i].getMemoryUsage(true);
3388 
3389  if (mySharedMem)
3390  mem += mySharedMem->getMemoryUsage(true);
3391 
3392  if (mySharedMemView)
3393  mem += mySharedMemView->getMemoryUsage(true);
3394 
3395  return mem;
3396 }
3397 
3398 template <typename T>
3399 void
3401 {
3402  UT_VoxelArrayIterator<T> vit(this);
3403  // We don't want split tile as we update the actual tiles here
3404  // so will have false-sharing if we interleaved.
3405  vit.setPartialRange(info.job(), info.numJobs());
3406  for (vit.rewind(); !vit.atEnd(); vit.advanceTile())
3407  {
3408  int i = vit.getLinearTileNum();
3409  myTiles[i].makeConstant(t);
3410  }
3411 }
3412 
3413 template <typename T>
3414 bool
3416 {
3417  int i, ntiles;
3418  T cval;
3419  cval = 0;
3420  const fpreal tol = SYS_FTOLERANCE_R;
3421 
3422  ntiles = numTiles();
3423  for (i = 0; i < ntiles; i++)
3424  {
3425  if (!myTiles[i].isConstant())
3426  {
3427  return false;
3428  }
3429 
3430  if (!i)
3431  {
3432  // First tile, get the constant value.
3433  cval = myTiles[i].rawConstVal();
3434  }
3435  else
3436  {
3437  // See if we have deviated too much.
3438  if (UT_VoxelTile<T>::dist(cval, myTiles[i].rawConstVal()) > tol)
3439  {
3440  return false;
3441  }
3442  }
3443  }
3444 
3445  // All tiles are both constant and within tolerance of each
3446  // other. Write out our constant value and return true.
3447  if (t)
3448  *t = cval;
3449 
3450  return true;
3451 }
3452 
3453 template <typename T>
3454 bool
3456 {
3457  int i, ntiles;
3458 
3459  ntiles = numTiles();
3460  for (i = 0; i < ntiles; i++)
3461  {
3462  if (myTiles[i].hasNan())
3463  {
3464  return true;
3465  }
3466  }
3467 
3468  return false;
3469 }
3470 
3471 template <typename T>
3472 T
3474 {
3475  // We go from the position in the unit cube into the index.
3476  // The center of cells must map to the exact integer indices.
3477  pos.x() *= myRes[0];
3478  pos.y() *= myRes[1];
3479  pos.z() *= myRes[2];
3480  pos.x() -= 0.5;
3481  pos.y() -= 0.5;
3482  pos.z() -= 0.5;
3483 
3484  return lerpVoxelCoord(pos);
3485 }
3486 
3487 template <typename T>
3488 T
3490 {
3491  int x, y, z;
3492  // Yes, these have to be 32 becaues split float requires 32!
3493  fpreal32 fx, fy, fz;
3494 
3495  splitVoxelCoord(pos, x, y, z, fx, fy, fz);
3496 
3497  return lerpVoxel(x, y, z, fx, fy, fz);
3498 }
3499 
3500 template <typename T>
3501 template <int AXIS2D>
3502 T
3504 {
3505  int x, y, z;
3506  // Yes, these have to be 32 becaues split float requires 32!
3507  fpreal32 fx, fy, fz;
3508 
3509  splitVoxelCoordAxis<AXIS2D>(pos, x, y, z, fx, fy, fz);
3510 
3511  return lerpVoxelAxis<AXIS2D>(x, y, z, fx, fy, fz);
3512 }
3513 
3514 template <typename T>
3515 T
3516 UT_VoxelArray<T>::lerpVoxel(int x, int y, int z,
3517  float fx, float fy, float fz) const
3518 {
3519  // Do trilinear interpolation.
3520  T vx, vx1, vy, vy1, vz;
3521 
3522  // Optimization for common cases (values are within the voxel range and
3523  // are all within the same tile)
3524  if ( !((x | y | z) < 0) &&
3525  (((x - myRes[0]+1) & (y - myRes[1]+1) & (z - myRes[2]+1)) < 0))
3526 
3527  // (x > 0) && (y > 0) && (z > 0) &&
3528  // Do not use x+1 < foo as if x is MAX_INT that will falsely
3529  // report in bounds!
3530  // (x < myRes[0]-1) && (y < myRes[1]-1) && (z < myRes[2]-1) )
3531  {
3532  int xm, ym, zm;
3533 
3534  xm = x & TILEMASK;
3535  ym = y & TILEMASK;
3536  zm = z & TILEMASK;
3537 
3538  if ((xm != TILEMASK) && (ym != TILEMASK) && (zm != TILEMASK))
3539  {
3540  const UT_VoxelTile<T> *tile =
3541  getTile(x >> TILEBITS, y >> TILEBITS, z >> TILEBITS);
3542 
3543  vz = tile->lerp(xm, ym, zm, fx, fy, fz);
3544  }
3545  else
3546  {
3547  // We cross tile boundaries but we remain within
3548  // the voxel grid. We can thus avoid any bounds checking
3549  // and use operator() rather than getValue.
3550 
3551  // Lerp x:x+1, y, z
3552  vx = UT_VoxelTile<T>::lerpValues((*this)(x, y, z),
3553  (*this)(x+1, y, z),
3554  fx);
3555  // Lerp x:x+1, y+1, z
3556  vx1= UT_VoxelTile<T>::lerpValues((*this)(x, y+1, z),
3557  (*this)(x+1, y+1, z),
3558  fx);
3559  // Lerp x:x+1, y:y+1, z
3560  vy = UT_VoxelTile<T>::lerpValues(vx, vx1, fy);
3561 
3562  // Lerp x:x+1, y, z+1
3563  vx = UT_VoxelTile<T>::lerpValues((*this)(x, y, z+1),
3564  (*this)(x+1, y, z+1),
3565  fx);
3566  // Lerp x:x+1, y+1, z+1
3567  vx1= UT_VoxelTile<T>::lerpValues((*this)(x, y+1, z+1),
3568  (*this)(x+1, y+1, z+1),
3569  fx);
3570 
3571  // Lerp x:x+1, y:y+1, z+1
3572  vy1 = UT_VoxelTile<T>::lerpValues(vx, vx1, fy);
3573 
3574  // Lerp x:x+1, y:y+1, z:z+1
3575  vz = UT_VoxelTile<T>::lerpValues(vy, vy1, fz);
3576  }
3577  }
3578  else
3579  {
3580  // Lerp x:x+1, y, z
3581  vx = UT_VoxelTile<T>::lerpValues(getValue(x, y, z),
3582  getValue(x+1, y, z),
3583  fx);
3584  // Lerp x:x+1, y+1, z
3585  vx1= UT_VoxelTile<T>::lerpValues(getValue(x, y+1, z),
3586  getValue(x+1, y+1, z),
3587  fx);
3588 
3589  // Lerp x:x+1, y:y+1, z
3590  vy = UT_VoxelTile<T>::lerpValues(vx, vx1, fy);
3591 
3592  // Lerp x:x+1, y, z+1
3593  vx = UT_VoxelTile<T>::lerpValues(getValue(x, y, z+1),
3594  getValue(x+1, y, z+1),
3595  fx);
3596  // Lerp x:x+1, y+1, z+1
3597  vx1= UT_VoxelTile<T>::lerpValues(getValue(x, y+1, z+1),
3598  getValue(x+1, y+1, z+1),
3599  fx);
3600 
3601  // Lerp x:x+1, y:y+1, z+1
3602  vy1 = UT_VoxelTile<T>::lerpValues(vx, vx1, fy);
3603 
3604  // Lerp x:x+1, y:y+1, z:z+1
3605  vz = UT_VoxelTile<T>::lerpValues(vy, vy1, fz);
3606  }
3607 
3608  return vz;
3609 }
3610 
3611 template <typename T>
3612 template <int AXIS2D>
3613 T
3615  float fx, float fy, float fz) const
3616 {
3617  // Do bilinear interpolation.
3618  T vx, vx1, vy, vy1, vz;
3619 
3620  int lesscomp = 0, greatercomp = -1;
3621 
3622  if (AXIS2D != 0)
3623  {
3624  lesscomp |= x;
3625  greatercomp &= (x - myRes[0]+1);
3626  }
3627  if (AXIS2D != 1)
3628  {
3629  lesscomp |= y;
3630  greatercomp &= (y - myRes[1]+1);
3631  }
3632  if (AXIS2D != 2)
3633  {
3634  lesscomp |= z;
3635  greatercomp &= (z - myRes[2]+1);
3636  }
3637 
3638  // Optimization for common cases (values are within the voxel range and
3639  // are all within the same tile)
3640  if ( !(lesscomp < 0) && (greatercomp < 0) )
3641  {
3642  int xm, ym, zm;
3643 
3644  xm = x & TILEMASK;
3645  ym = y & TILEMASK;
3646  zm = z & TILEMASK;
3647 
3648  if ((AXIS2D == 0 || xm != TILEMASK) &&
3649  (AXIS2D == 1 || ym != TILEMASK) &&
3650  (AXIS2D == 2 || zm != TILEMASK))
3651  {
3652  const UT_VoxelTile<T> *tile =
3653  getTile( (AXIS2D == 0) ? 0 : (x >> TILEBITS),
3654  (AXIS2D == 1) ? 0 : (y >> TILEBITS),
3655  (AXIS2D == 2) ? 0 : (z >> TILEBITS) );
3656 
3657  vz = tile->template lerpAxis<AXIS2D>(xm, ym, zm, fx, fy, fz);
3658  }
3659  else
3660  {
3661  // We cross tile boundaries but we remain within
3662  // the voxel grid. We can thus avoid any bounds checking
3663  // and use operator() rather than getValue.
3664 
3665  // Lerp x:x+1, y, z
3666  if (AXIS2D != 0)
3667  vx = UT_VoxelTile<T>::lerpValues((*this)(x, y, z),
3668  (*this)(x+1, y, z),
3669  fx);
3670  else
3671  vx = (*this)(x, y, z);
3672 
3673  if (AXIS2D != 1)
3674  {
3675  // Lerp x:x+1, y+1, z
3676  if (AXIS2D != 0)
3677  vx1= UT_VoxelTile<T>::lerpValues((*this)(x, y+1, z),
3678  (*this)(x+1, y+1, z),
3679  fx);
3680  else
3681  vx1 = (*this)(x, y+1, z);
3682  // Lerp x:x+1, y:y+1, z
3683  vy = UT_VoxelTile<T>::lerpValues(vx, vx1, fy);
3684  }
3685  else
3686  vy = vx;
3687 
3688  if (AXIS2D != 2)
3689  {
3690  // Lerp x:x+1, y, z+1
3691  if (AXIS2D != 0)
3692  vx = UT_VoxelTile<T>::lerpValues((*this)(x, y, z+1),
3693  (*this)(x+1, y, z+1),
3694  fx);
3695  else
3696  vx = (*this)(x, y, z+1);
3697 
3698  if (AXIS2D != 1)
3699  {
3700  // Lerp x:x+1, y+1, z+1
3701  if (AXIS2D != 0)
3702  vx1= UT_VoxelTile<T>::lerpValues((*this)(x, y+1, z+1),
3703  (*this)(x+1, y+1, z+1),
3704  fx);
3705  else
3706  vx1 = (*this)(x, y+1, z+1);
3707  // Lerp x:x+1, y:y+1, z+1
3708  vy1 = UT_VoxelTile<T>::lerpValues(vx, vx1, fy);
3709  }
3710  else
3711  vy1 = vx;
3712 
3713  // Lerp x:x+1, y:y+1, z:z+1
3714  vz = UT_VoxelTile<T>::lerpValues(vy, vy1, fz);
3715  }
3716  else
3717  vz = vy;
3718  }
3719  }
3720  else
3721  {
3722  // Lerp x:x+1, y, z
3723  if (AXIS2D != 0)
3724  vx = UT_VoxelTile<T>::lerpValues(getValue(x, y, z),
3725  getValue(x+1, y, z),
3726  fx);
3727  else
3728  vx = getValue(x, y, z);
3729 
3730  if (AXIS2D != 1)
3731  {
3732  // Lerp x:x+1, y+1, z
3733  if (AXIS2D != 0)
3734  vx1= UT_VoxelTile<T>::lerpValues(getValue(x, y+1, z),
3735  getValue(x+1, y+1, z),
3736  fx);
3737  else
3738  vx1 = getValue(x, y+1, z);
3739  // Lerp x:x+1, y:y+1, z
3740  vy = UT_VoxelTile<T>::lerpValues(vx, vx1, fy);
3741  }
3742  else
3743  vy = vx;
3744 
3745  if (AXIS2D != 2)
3746  {
3747  // Lerp x:x+1, y, z+1
3748  if (AXIS2D != 0)
3749  vx = UT_VoxelTile<T>::lerpValues(getValue(x, y, z+1),
3750  getValue(x+1, y, z+1),
3751  fx);
3752  else
3753  vx = getValue(x, y, z+1);
3754 
3755  if (AXIS2D != 1)
3756  {
3757  // Lerp x:x+1, y+1, z+1
3758  if (AXIS2D != 0)
3759  vx1= UT_VoxelTile<T>::lerpValues(getValue(x, y+1, z+1),
3760  getValue(x+1, y+1, z+1),
3761  fx);
3762  else
3763  vx1 = getValue(x, y+1, z+1);
3764  // Lerp x:x+1, y:y+1, z+1
3765  vy1 = UT_VoxelTile<T>::lerpValues(vx, vx1, fy);
3766  }
3767  else
3768  vy1 = vx;
3769 
3770  // Lerp x:x+1, y:y+1, z:z+1
3771  vz = UT_VoxelTile<T>::lerpValues(vy, vy1, fz);
3772  }
3773  else
3774  vz = vy;
3775  }
3776 
3777  return vz;
3778 }
3779 
3780 template <typename T>
3781 void
3783  UT_Vector3F pos) const
3784 {
3785  // We go from the position in the unit cube into the index.
3786  // The center of cells must map to the exact integer indices.
3787  pos.x() *= myRes[0];
3788  pos.y() *= myRes[1];
3789  pos.z() *= myRes[2];
3790  pos.x() -= 0.5;
3791  pos.y() -= 0.5;
3792  pos.z() -= 0.5;
3793 
3794  lerpVoxelCoordMinMax(lerp, lmin, lmax, pos);
3795 }
3796 
3797 template <typename T>
3798 void
3800  UT_Vector3F pos) const
3801 {
3802  int x, y, z;
3803  // Yes, these have to be 32 becaues split float requires 32!
3804  fpreal32 fx, fy, fz;
3805 
3806  splitVoxelCoord(pos, x, y, z, fx, fy, fz);
3807 
3808  lerpVoxelMinMax(lerp, lmin, lmax, x, y, z, fx, fy, fz);
3809 }
3810 
3811 
3812 template <typename T>
3813 template <int AXIS2D>
3814 void
3816  UT_Vector3F pos) const
3817 {
3818  int x, y, z;
3819  // Yes, these have to be 32 becaues split float requires 32!
3820  fpreal32 fx, fy, fz;
3821 
3822  splitVoxelCoordAxis<AXIS2D>(pos, x, y, z, fx, fy, fz);
3823 
3824  lerpVoxelMinMaxAxis<AXIS2D>(lerp, lmin, lmax, x, y, z, fx, fy, fz);
3825 }
3826 
3827 
3828 template <typename T>
3829 void
3831  T &lerp, T &smin, T &smax,
3832  int x, int y, int z,
3833  float fx, float fy, float fz) const
3834 {
3835  T samples[8];
3836 
3837  if (extractSample(x, y, z, samples))
3838  {
3839  lerp = smin = smax = samples[0];
3840  return;
3841  }
3842 
3843  lerp = lerpSample(samples, fx, fy, fz);
3844 
3845  T smin1, smax1;
3846 
3847  SYSminmax(samples[0], samples[1], samples[2], samples[3],
3848  smin, smax);
3849  SYSminmax(samples[4+0], samples[4+1], samples[4+2], samples[4+3],
3850  smin1, smax1);
3851 
3852  smin = SYSmin(smin, smin1);
3853  smax = SYSmax(smax, smax1);
3854 }
3855 
3856 template <typename T>
3857 template <int AXIS2D>
3858 void
3860  T &lerp, T &smin, T &smax,
3861  int x, int y, int z,
3862  float fx, float fy, float fz) const
3863 {
3864  T samples[8];
3865 
3866  if (extractSampleAxis<AXIS2D>(x, y, z, samples))
3867  {
3868  lerp = smin = smax = samples[0];
3869  return;
3870  }
3871 
3872  lerp = lerpSampleAxis<AXIS2D>(samples, fx, fy, fz);
3873 
3874  if (AXIS2D == 0)
3875  SYSminmax(samples[0], samples[2], samples[4], samples[6],
3876  smin, smax);
3877  else if (AXIS2D == 1)
3878  SYSminmax(samples[0], samples[1], samples[4], samples[5],
3879  smin, smax);
3880  else if (AXIS2D == 2)
3881  SYSminmax(samples[0], samples[1], samples[2], samples[3],
3882  smin, smax);
3883  else
3884  {
3885  T smin1, smax1;
3886 
3887  SYSminmax(samples[0], samples[1], samples[2], samples[3],
3888  smin, smax);
3889  SYSminmax(samples[4+0], samples[4+1], samples[4+2], samples[4+3],
3890  smin1, smax1);
3891 
3892  smin = SYSmin(smin, smin1);
3893  smax = SYSmax(smax, smax1);
3894  }
3895 }
3896 
3897 template <typename T>
3898 bool
3900  T *samples) const
3901 {
3902  // Optimization for common cases (values are within the voxel range and
3903  // are all within the same tile)
3904  if ( !((x | y | z) < 0) &&
3905  (((x - myRes[0]+1) & (y - myRes[1]+1) & (z - myRes[2]+1)) < 0))
3906 
3907  // (x > 0) && (y > 0) && (z > 0) &&
3908  // Do not use x+1 < foo as if x is MAX_INT that will falsely
3909  // report in bounds!
3910  // (x < myRes[0]-1) && (y < myRes[1]-1) && (z < myRes[2]-1) )
3911  {
3912  int xm, ym, zm;
3913 
3914  xm = x & TILEMASK;
3915  ym = y & TILEMASK;
3916  zm = z & TILEMASK;
3917 
3918  if ((xm != TILEMASK) && (ym != TILEMASK) && (zm != TILEMASK))
3919  {
3920  const UT_VoxelTile<T> *tile =
3921  getTile(x >> TILEBITS, y >> TILEBITS, z >> TILEBITS);
3922 
3923  return tile->extractSample(xm, ym, zm, samples);
3924  }
3925  else
3926  {
3927  // We cross tile boundaries but we remain within
3928  // the voxel grid. We can thus avoid any bounds checking
3929  // and use operator() rather than getValue.
3930  samples[0] = (*this)(x, y, z);
3931  samples[1] = (*this)(x+1, y, z);
3932  samples[2+0] = (*this)(x, y+1, z);
3933  samples[2+1] = (*this)(x+1, y+1, z);
3934  samples[4+0] = (*this)(x, y, z+1);
3935  samples[4+1] = (*this)(x+1, y, z+1);
3936  samples[4+2+0] = (*this)(x, y+1, z+1);
3937  samples[4+2+1] = (*this)(x+1, y+1, z+1);
3938  }
3939  }
3940  else
3941  {
3942  samples[0] = getValue(x, y, z);
3943  samples[1] = getValue(x+1, y, z);
3944  samples[2+0] = getValue(x, y+1, z);
3945  samples[2+1] = getValue(x+1, y+1, z);
3946  samples[4+0] = getValue(x, y, z+1);
3947  samples[4+1] = getValue(x+1, y, z+1);
3948  samples[4+2+0] = getValue(x, y+1, z+1);
3949  samples[4+2+1] = getValue(x+1, y+1, z+1);
3950  }
3951 
3952  return false;
3953 }
3954 
3955 
3956 template <typename T>
3957 template <int AXIS2D>
3958 bool
3960  T *samples) const
3961 {
3962  // Optimization for common cases (values are within the voxel range and
3963  // are all within the same tile)
3964  int lesscomp = 0, greatercomp = -1;
3965 
3966  if (AXIS2D != 0)
3967  {
3968  lesscomp |= x;
3969  greatercomp &= (x - myRes[0]+1);
3970  }
3971  if (AXIS2D != 1)
3972  {
3973  lesscomp |= y;
3974  greatercomp &= (y - myRes[1]+1);
3975  }
3976  if (AXIS2D != 2)
3977  {
3978  lesscomp |= z;
3979  greatercomp &= (z - myRes[2]+1);
3980  }
3981 
3982  // Optimization for common cases (values are within the voxel range and
3983  // are all within the same tile)
3984  if ( !(lesscomp < 0) && (greatercomp < 0) )
3985  {
3986  int xm, ym, zm;
3987 
3988  xm = x & TILEMASK;
3989  ym = y & TILEMASK;
3990  zm = z & TILEMASK;
3991 
3992  if ((AXIS2D == 0 || xm != TILEMASK) &&
3993  (AXIS2D == 1 || ym != TILEMASK) &&
3994  (AXIS2D == 2 || zm != TILEMASK))
3995  {
3996  const UT_VoxelTile<T> *tile =
3997  getTile( (AXIS2D == 0) ? 0 : (x >> TILEBITS),
3998  (AXIS2D == 1) ? 0 : (y >> TILEBITS),
3999  (AXIS2D == 2) ? 0 : (z >> TILEBITS) );
4000 
4001  return tile->template extractSampleAxis<AXIS2D>(xm, ym, zm, samples);
4002  }
4003  else
4004  {
4005  // We cross tile boundaries but we remain within
4006  // the voxel grid. We can thus avoid any bounds checking
4007  // and use operator() rather than getValue.
4008  samples[0] = (*this)(x, y, z);
4009  if (AXIS2D != 0)
4010  samples[1] = (*this)(x+1, y, z);
4011  if (AXIS2D != 1)
4012  {
4013  samples[2+0] = (*this)(x, y+1, z);
4014  if (AXIS2D != 0)
4015  samples[2+1] = (*this)(x+1, y+1, z);
4016  }
4017  if (AXIS2D != 2)
4018  {
4019  samples[4+0] = (*this)(x, y, z+1);
4020  if (AXIS2D != 0)
4021  samples[4+1] = (*this)(x+1, y, z+1);
4022  if (AXIS2D != 1)
4023  {
4024  samples[4+2+0] = (*this)(x, y+1, z+1);
4025  if (AXIS2D != 0)
4026  samples[4+2+1] = (*this)(x+1, y+1, z+1);
4027  }
4028  }
4029  }
4030  }
4031  else
4032  {
4033  samples[0] = getValue(x, y, z);
4034  if (AXIS2D != 0)
4035  samples[1] = getValue(x+1, y, z);
4036  if (AXIS2D != 1)
4037  {
4038  samples[2+0] = getValue(x, y+1, z);
4039  if (AXIS2D != 0)
4040  samples[2+1] = getValue(x+1, y+1, z);
4041  }
4042  if (AXIS2D != 2)
4043  {
4044  samples[4+0] = getValue(x, y, z+1);
4045  if (AXIS2D != 0)
4046  samples[4+1] = getValue(x+1, y, z+1);
4047  if (AXIS2D != 1)
4048  {
4049  samples[4+2+0] = getValue(x, y+1, z+1);
4050  if (AXIS2D != 0)
4051  samples[4+2+1] = getValue(x+1, y+1, z+1);
4052  }
4053  }
4054  }
4055 
4056  return false;
4057 }
4058 
4059 template <typename T>
4060 bool
4062  T *samples) const
4063 {
4064  // Optimization for common cases (values are within the voxel range and
4065  // are all within the same tile)
4066  if ( !(((x-1) | (y-1) | (z-1)) < 0) &&
4067  (((x - myRes[0]+1) & (y - myRes[1]+1) & (z - myRes[2]+1)) < 0))
4068 
4069  // (x > 0) && (y > 0) && (z > 0) &&
4070  // Do not use x+1 < foo as if x is MAX_INT that will falsely
4071  // report in bounds!
4072  // (x < myRes[0]-1) && (y < myRes[1]-1) && (z < myRes[2]-1) )
4073  {
4074  int xm, ym, zm;
4075 
4076  xm = x & TILEMASK;
4077  ym = y & TILEMASK;
4078  zm = z & TILEMASK;
4079 
4080  if (xm && ym && zm && (xm != TILEMASK) && (ym != TILEMASK) && (zm != TILEMASK))
4081  {
4082  const UT_VoxelTile<T> *tile =
4083  getTile(x >> TILEBITS, y >> TILEBITS, z >> TILEBITS);
4084 
4085  return tile->extractSamplePlus(xm, ym, zm, samples);
4086  }
4087  else
4088  {
4089  // We cross tile boundaries but we remain within
4090  // the voxel grid. We can thus avoid any bounds checking
4091  // and use operator() rather than getValue.
4092  samples[0] = (*this)(x-1, y, z);
4093  samples[1] = (*this)(x+1, y, z);
4094  samples[2+0] = (*this)(x, y-1, z);
4095  samples[2+1] = (*this)(x, y+1, z);
4096  samples[4+0] = (*this)(x, y, z-1);
4097  samples[4+1] = (*this)(x, y, z+1);
4098  samples[6] = (*this)(x, y, z);
4099  }
4100  }
4101  else
4102  {
4103  samples[0] = getValue(x-1, y, z);
4104  samples[1] = getValue(x+1, y, z);
4105  samples[2+0] = getValue(x, y-1, z);
4106  samples[2+1] = getValue(x, y+1, z);
4107  samples[4+0] = getValue(x, y, z-1);
4108  samples[4+1] = getValue(x, y, z+1);
4109  samples[6] = getValue(x, y, z);
4110  }
4111 
4112  return false;
4113 }
4114 
4115 #if 0
4116 /// Implementation of UT_VoxelTile::extractSampleCube() has an error (see the
4117 /// comments for it), which cascaded to here. Simply removing this function for
4118 /// now, as there isn't any need for it--at least in our own code.
4119 template <typename T>
4120 bool
4121 UT_VoxelArray<T>::extractSampleCube(int x, int y, int z,
4122  T *samples) const
4123 {
4124  // Optimization for common cases (values are within the voxel range and
4125  // are all within the same tile)
4126  if ( !(((x-1) | (y-1) | (z-1)) < 0) &&
4127  (((x - myRes[0]+1) & (y - myRes[1]+1) & (z - myRes[2]+1)) < 0))
4128 
4129  // (x > 0) && (y > 0) && (z > 0) &&
4130  // Do not use x+1 < foo as if x is MAX_INT that will falsely
4131  // report in bounds!
4132  // (x < myRes[0]-1) && (y < myRes[1]-1) && (z < myRes[2]-1) )
4133  {
4134  int xm, ym, zm;
4135 
4136  xm = x & TILEMASK;
4137  ym = y & TILEMASK;
4138  zm = z & TILEMASK;
4139 
4140  if (xm && ym && zm && (xm != TILEMASK) && (ym != TILEMASK) && (zm != TILEMASK))
4141  {
4142  const UT_VoxelTile<T> *tile =
4143  getTile(x >> TILEBITS, y >> TILEBITS, z >> TILEBITS);
4144 
4145  return tile->extractSampleCube(xm, ym, zm, samples);
4146  }
4147  else
4148  {
4149  // We cross tile boundaries but we remain within
4150  // the voxel grid. We can thus avoid any bounds checking
4151  // and use operator() rather than getValue.
4152  int sampidx = 0;
4153  for (int dz = -1; dz <= 1; dz++)
4154  {
4155  for (int dy = -1; dy <= 1; dy++)
4156  {
4157  for (int dx = -1; dx <= 1; dx++)
4158  {
4159  samples[sampidx++] = (*this)(x+dx, y+dy, z+dz);
4160  }
4161  }
4162  }
4163  }
4164  }
4165  else
4166  {
4167  int sampidx = 0;
4168  for (int dz = -1; dz <= 1; dz++)
4169  {
4170  for (int dy = -1; dy <= 1; dy++)
4171  {
4172  for (int dx = -1; dx <= 1; dx++)
4173  {
4174  samples[sampidx++] = getValue(x+dx, y+dy, z+dz);
4175  }
4176  }
4177  }
4178  }
4179 
4180  return false;
4181 }
4182 #endif
4183 
4184 template <typename T>
4185 T
4187  float fx, float fy, float fz) const
4188 {
4189 #if 1
4190  // Do trilinear interpolation.
4191  T vx, vx1, vy, vy1, vz;
4192 
4193  // Lerp x:x+1, y, z
4194  vx = UT_VoxelTile<T>::lerpValues(samples[0],
4195  samples[1],
4196  fx);
4197  // Lerp x:x+1, y+1, z
4198  vx1= UT_VoxelTile<T>::lerpValues(samples[2],
4199  samples[2+1],
4200  fx);
4201  // Lerp x:x+1, y:y+1, z
4202  vy = UT_VoxelTile<T>::lerpValues(vx, vx1, fy);
4203 
4204  // Lerp x:x+1, y, z+1
4205  vx = UT_VoxelTile<T>::lerpValues(samples[4],
4206  samples[4+1],
4207  fx);
4208  // Lerp x:x+1, y+1, z+1
4209  vx1= UT_VoxelTile<T>::lerpValues(samples[4+2],
4210  samples[4+2+1],
4211  fx);
4212 
4213  // Lerp x:x+1, y:y+1, z+1
4214  vy1 = UT_VoxelTile<T>::lerpValues(vx, vx1, fy);
4215 
4216  // Lerp x:x+1, y:y+1, z:z+1
4217  vz = UT_VoxelTile<T>::lerpValues(vy, vy1, fz);
4218 
4219  return vz;
4220 #else
4221  v4uf a, b, vfx, vfy, vfz;
4222 
4223  a = v4uf(&samples[0]);
4224  b = v4uf(&samples[4]);
4225 
4226  vfx = v4uf(fx);
4227  vfy = v4uf(fy);
4228  vfz = v4uf(fz);
4229 
4230  b -= a;
4231  a = madd(b, fz, a);
4232 
4233  b = a.swizzle<2, 3, 0, 1>();
4234  b -= a;
4235  a = madd(b, fy, a);
4236 
4237  b = a.swizzle<1, 2, 3, 0>();
4238  b -= a;
4239  a = madd(b, fx, a);
4240 
4241  return a[0];
4242 #endif
4243 }
4244 
4245 template <typename T>
4246 template <int AXIS2D>
4247 T
4249  float fx, float fy, float fz) const
4250 {
4251  // Do trilinear interpolation.
4252  T vx, vx1, vy, vy1, vz;
4253 
4254  // Lerp x:x+1, y, z
4255  if (AXIS2D != 0)
4256  vx = UT_VoxelTile<T>::lerpValues(samples[0], samples[1], fx);
4257  else
4258  vx = samples[0];
4259 
4260  if (AXIS2D != 1)
4261  {
4262  // Lerp x:x+1, y+1, z
4263  if (AXIS2D != 0)
4264  vx1= UT_VoxelTile<T>::lerpValues(samples[2], samples[2+1], fx);
4265  else
4266  vx1= samples[2];
4267 
4268  // Lerp x:x+1, y:y+1, z
4269  vy = UT_VoxelTile<T>::lerpValues(vx, vx1, fy);
4270  }
4271  else
4272  vy = vx;
4273 
4274  if (AXIS2D != 2)
4275  {
4276  // Lerp x:x+1, y, z+1
4277  if (AXIS2D != 0)
4278  vx = UT_VoxelTile<T>::lerpValues(samples[4], samples[4+1], fx);
4279  else
4280  vx = samples[4];
4281 
4282  if (AXIS2D != 1)
4283  {
4284  // Lerp x:x+1, y+1, z+1
4285  if (AXIS2D != 0)
4286  vx1= UT_VoxelTile<T>::lerpValues(samples[4+2], samples[4+2+1], fx);
4287  else
4288  vx1= samples[4+2];
4289 
4290  // Lerp x:x+1, y:y+1, z+1
4291  vy1 = UT_VoxelTile<T>::lerpValues(vx, vx1, fy);
4292  }
4293  else
4294  vy1 = vx;
4295 
4296  // Lerp x:x+1, y:y+1, z:z+1
4297  vz = UT_VoxelTile<T>::lerpValues(vy, vy1, fz);
4298  }
4299  else
4300  vz = vy;
4301 
4302  return vz;
4303 }
4304 
4305 template <typename T>
4306 T
4308 {
4309  int x, y, z;
4310  // Yes, these have to be 32 becaues split float requires 32!
4311  fpreal32 fx, fy, fz;
4312 
4313  // We go from the position in the unit cube into the index.
4314  // The center of cells must map to the exact integer indices.
4315  pos.x() *= myRes[0];
4316  pos.y() *= myRes[1];
4317  pos.z() *= myRes[2];
4318  pos.x() -= 0.5;
4319  pos.y() -= 0.5;
4320  pos.z() -= 0.5;
4321 
4322  // Determine integer & fractional components.
4323  fx = pos.x();
4324  SYSfastSplitFloat(fx, x);
4325  fy = pos.y();
4326  SYSfastSplitFloat(fy, y);
4327  fz = pos.z();
4328  SYSfastSplitFloat(fz, z);
4329 
4330  // Do trilinear interpolation.
4331  T vx, vx1, vy, vy1, vz;
4332 
4333  // NOTE:
4334  // If you get a crash reading one of these getValues, note
4335  // that if you are reading from the same voxel array that you
4336  // are writing to, and doing so in a multi-threaded, tile-by-tile
4337  // approach, just because your positions match doesn't mean you
4338  // won't access neighbouring tiles. To avoid this, perform
4339  // the obvious optimization of checking for aligned tiles
4340  // and using direct indices.
4341  // (I have avoided using if (!fx && !fy && !fz) as the test
4342  // as that is both potentially inaccurate (round off in conversion
4343  // may mess us up) and said optimzation is a useful one anyways)
4344 
4345  // Optimization for common cases (values are within the voxel range and
4346  // are all within the same tile)
4347  if ( !((x | y | z) < 0) &&
4348  (((x - myRes[0]+1) & (y - myRes[1]+1) & (z - myRes[2]+1)) < 0))
4349  // if ( (x > 0) && (y > 0) && (z > 0) &&
4350  // Do not use x+1 < foo as if x is MAX_INT that will falsely
4351  // report in bounds!
4352  // (x < myRes[0]-1) && (y < myRes[1]-1) && (z < myRes[2]-1) )
4353  {
4354  int xm, ym, zm;
4355 
4356  xm = x & TILEMASK;
4357  ym = y & TILEMASK;
4358  zm = z & TILEMASK;
4359 
4360  if ((xm != TILEMASK) && (ym != TILEMASK) && (zm != TILEMASK))
4361  {
4362  const UT_VoxelTile<T> *tile =
4363  getTile(x >> TILEBITS, y >> TILEBITS, z >> TILEBITS);
4364 
4365  vz = tile->lerp(xm, ym, zm, fx, fy, fz);
4366  }
4367  else
4368  {
4369  // We cross tile boundaries but we remain within
4370  // the voxel grid. We can thus avoid any bounds checking
4371  // and use operator() rather than getValue.
4372 
4373  // Lerp x:x+1, y, z
4374  vx = UT_VoxelTile<T>::lerpValues((*this)(x, y, z),
4375  (*this)(x+1, y, z),
4376  fx);
4377  // Lerp x:x+1, y+1, z
4378  vx1= UT_VoxelTile<T>::lerpValues((*this)(x, y+1, z),
4379  (*this)(x+1, y+1, z),
4380  fx);
4381  // Lerp x:x+1, y:y+1, z
4382  vy = UT_VoxelTile<T>::lerpValues(vx, vx1, fy);
4383 
4384  // Lerp x:x+1, y, z+1
4385  vx = UT_VoxelTile<T>::lerpValues((*this)(x, y, z+1),
4386  (*this)(x+1, y, z+1),
4387  fx);
4388  // Lerp x:x+1, y+1, z+1
4389  vx1= UT_VoxelTile<T>::lerpValues((*this)(x, y+1, z+1),
4390  (*this)(x+1, y+1, z+1),
4391  fx);
4392 
4393  // Lerp x:x+1, y:y+1, z+1
4394  vy1 = UT_VoxelTile<T>::lerpValues(vx, vx1, fy);
4395 
4396  // Lerp x:x+1, y:y+1, z:z+1
4397  vz = UT_VoxelTile<T>::lerpValues(vy, vy1, fz);
4398  }
4399  }
4400  else
4401  {
4402  // Lerp x:x+1, y, z
4403  vx = UT_VoxelTile<T>::lerpValues(getValue(x, y, z),
4404  getValue(x+1, y, z),
4405  fx);
4406  // Lerp x:x+1, y+1, z
4407  vx1= UT_VoxelTile<T>::lerpValues(getValue(x, y+1, z),
4408  getValue(x+1, y+1, z),
4409  fx);
4410 
4411  // Lerp x:x+1, y:y+1, z
4412  vy = UT_VoxelTile<T>::lerpValues(vx, vx1, fy);
4413 
4414  // Lerp x:x+1, y, z+1
4415  vx = UT_VoxelTile<T>::lerpValues(getValue(x, y, z+1),
4416  getValue(x+1, y, z+1),
4417  fx);
4418  // Lerp x:x+1, y+1, z+1
4419  vx1= UT_VoxelTile<T>::lerpValues(getValue(x, y+1, z+1),
4420  getValue(x+1, y+1, z+1),
4421  fx);
4422 
4423  // Lerp x:x+1, y:y+1, z+1
4424  vy1 = UT_VoxelTile<T>::lerpValues(vx, vx1, fy);
4425 
4426  // Lerp x:x+1, y:y+1, z:z+1
4427  vz = UT_VoxelTile<T>::lerpValues(vy, vy1, fz);
4428  }
4429 
4430  return vz;
4431 }
4432 
4433 #if 0
4434 template <typename T>
4435 T
4437 {
4438  int x, y, z;
4439 
4440  v4ui idx;
4441 
4442  // We go from the position in the unit cube into the index.
4443  // The center of cells must map to the exact integer indices.
4444  pos *= v4uf((float) myRes[0], (float) myRes[1], (float) myRes[2], 0.0f);
4445 
4446  // Because we use truncation we'll get inaccurate results
4447  // at the zero boundary, we thus bump up by two
4448  pos += 1.5;
4449 
4450  idx = pos.splitFloat();
4451 
4452  // And counteract that bump
4453  idx -= 2;
4454 
4455  x = idx[0];
4456  y = idx[1];
4457  z = idx[2];
4458 
4459  // Do trilinear interpolation.
4460  // A | B
4461  // 0 +z +y +yz | +x +xz +xy +xyz
4462 
4463  v4uf a, b, fx, fy, fz;
4464 
4465  int xm, ym, zm;
4466 
4467  xm = x & TILEMASK;
4468  ym = y & TILEMASK;
4469  zm = z & TILEMASK;
4470 
4471  // Optimization for common case (values are within the voxel range and
4472  // are all within the same tile)
4473  if ( (x > 0) & (y > 0) & (z > 0) &
4474  (x < myRes[0]-1) & (y < myRes[1]-1) & (z < myRes[2]-1) &
4475  (xm != TILEMASK) & (ym != TILEMASK) & (zm != TILEMASK) )
4476 /*
4477  if (isValidIndex(x, y, z) && isValidIndex(x+1, y+1, z+1) &&
4478  (x & TILEMASK) != TILEMASK &&
4479  (y & TILEMASK) != TILEMASK &&
4480  (z & TILEMASK) != TILEMASK)
4481 */
4482  {
4483  const UT_VoxelTile<T> *tile =
4484  getTile(x >> TILEBITS, y >> TILEBITS, z >> TILEBITS);
4485 
4486  return tile->lerp(pos, xm, ym, zm);
4487  }
4488  else
4489  {
4490  a = v4uf( getValue(x, y, z),
4491  getValue(x, y, z+1),
4492  getValue(x, y+1, z),
4493  getValue(x, y+1, z+1) );
4494  b = v4uf( getValue(x+1, y, z),
4495  getValue(x+1, y, z+1),
4496  getValue(x+1, y+1, z),
4497  getValue(x+1, y+1, z+1) );
4498  }
4499 
4500  fx = pos.swizzle<0, 0, 0, 0>();
4501  fy = pos.swizzle<1, 1, 1, 1>();
4502  fz = pos.swizzle<2, 2, 2, 2>();
4503 
4504  b -= a;
4505  a = madd(b, fx, a);
4506 
4507  b = a.swizzle<2, 3, 0, 1>();
4508  b -= a;
4509  a = madd(b, fy, a);
4510 
4511  b = a.swizzle<1, 2, 3, 0>();
4512  b -= a;
4513  a = madd(b, fz, a);
4514 
4515  return a[0];
4516 }
4517 #endif
4518 
4519 static inline int
4520 firstTile(int &start, int &end, int res)
4521 {
4522  if (start < 0)
4523  {
4524  start += res;
4525  end += res;
4526  }
4527  return start;
4528 }
4529 
4530 static inline int
4531 nextTile(int &tile, int &pstart, int &start, int &end, int res)
4532 {
4533  int pend;
4534 
4535  if (pstart >= res)
4536  {
4537  pstart -= res;
4538  start -= res;
4539  end -= res;
4540  }
4541  tile = pstart >> TILEBITS;
4542  pend = SYSmin((tile+1) * TILESIZE, end, res);
4543 
4544  UT_ASSERT(pstart >= 0 && pstart < res);
4545  UT_ASSERT(pend > 0 && pstart <= res);
4546  UT_ASSERT(pend-pstart > 0);
4547  UT_ASSERT(pend-pstart <= TILESIZE);
4548 
4549  return pend;
4550 }
4551 
4552 template <typename T>
4553 T
4555  fpreal radius, int clampaxis) const
4556 {
4557  UT_Vector3 tpos;
4558  UT_FilterWindow win[3];
4559  UT_FilterWrap wrap[3];
4560  UT_VoxelTile<T> *tile;
4561  const float *weights[3];
4562  fpreal visible;
4563  int start[3], end[3], size[3];
4564  int pstart[3], pend[3];
4565  int tx, ty, tz, i;
4566  T result;
4567 
4568  if (!myTiles)
4569  {
4570  // If the array is empty, just use the border value.
4571  return myBorderValue;
4572  }
4573 
4574  UT_FilterWrap basewrap;
4575  switch (myBorderType)
4576  {
4577  case UT_VOXELBORDER_CONSTANT: basewrap = UT_WRAP_BORDER; break;
4578  case UT_VOXELBORDER_REPEAT: basewrap = UT_WRAP_REPEAT; break;
4579  case UT_VOXELBORDER_STREAK: basewrap = UT_WRAP_CLAMP; break;
4580 
4581  // NOTE: These are incorrect!
4582  case UT_VOXELBORDER_MIRROR: basewrap = UT_WRAP_CLAMP; break;
4583  case UT_VOXELBORDER_EXTRAP: basewrap = UT_WRAP_CLAMP; break;
4584  default: basewrap = UT_WRAP_CLAMP; break;
4585  }
4586  for (i = 0; i < 3; i++)
4587  {
4588  if (i == clampaxis)
4589  wrap[i] = UT_WRAP_CLAMP;
4590  else
4591  wrap[i] = basewrap;
4592  }
4593 
4594  memset(&result, 0, sizeof(result));
4595 
4596  radius *= filter.getSupport();
4597 
4598  // Make a local copy of the position so that we can modify it.
4599  tpos = pos;
4600  visible = 1;
4601  for (i = 0; i < 3; i++)
4602  {
4603  tpos[i] = tpos[i]*myRes[i];
4604  if (!win[i].setWeights(filter, tpos[i], radius, myRes[i], wrap[i]))
4605  return result;
4606 
4607  weights[i] = win[i].getWeights();
4608  start[i] = win[i].getStart() % myRes[i];
4609  size[i] = win[i].getSize();
4610 
4611  UT_ASSERT(start[i] >= 0);
4612  UT_ASSERT(size[i] <= myRes[i]);
4613 
4614  end[i] = start[i] + size[i];
4615  visible *= win[i].getVisible();
4616  }
4617 
4618  // Accumulate filtered results
4619  pstart[2] = firstTile(start[2], end[2], myRes[2]);
4620  while (pstart[2] < end[2])
4621  {
4622  pend[2] = nextTile(tz, pstart[2], start[2], end[2], myRes[2]);
4623  pstart[1] = firstTile(start[1], end[1], myRes[1]);
4624  while (pstart[1] < end[1])
4625  {
4626  pend[1] = nextTile(ty, pstart[1], start[1], end[1], myRes[1]);
4627  pstart[0] = firstTile(start[0], end[0], myRes[0]);
4628  while (pstart[0] < end[0])
4629  {
4630  pend[0] = nextTile(tx, pstart[0], start[0], end[0], myRes[0]);
4631  tile = getTile(tx, ty, tz);
4632  UT_ASSERT(tile);
4633  tile->weightedSum(pstart, pend, weights, start, result);
4634  pstart[0] = pend[0];
4635  }
4636  pstart[1] = pend[1];
4637  }
4638  pstart[2] = pend[2];
4639  }
4640 
4641  if (visible < 1)
4642  {
4643  result += (1-visible)*myBorderValue;
4644  }
4645 
4646  return result;
4647 }
4648 
4649 template <typename T>
4650 T
4652  fpreal radius, int clampaxis) const
4653 {
4654  UT_Vector3 tpos;
4655  UT_FilterWindow win[3];
4656  UT_FilterWrap wrap[3];
4657  UT_VoxelTile<T> *tile;
4658  int start[3], end[3], size[3];
4659  int pstart[3], pend[3];
4660  int tx, ty, tz, i;
4661  T result;
4662 
4663  if (!myTiles)
4664  {
4665  // If the array is empty, just use the border value.
4666  return myBorderValue;
4667  }
4668 
4669  UT_FilterWrap basewrap;
4670  switch (myBorderType)
4671  {
4672  case UT_VOXELBORDER_CONSTANT: basewrap = UT_WRAP_BORDER; break;
4673  case UT_VOXELBORDER_REPEAT: basewrap = UT_WRAP_REPEAT; break;
4674  case UT_VOXELBORDER_STREAK: basewrap = UT_WRAP_CLAMP; break;
4675 
4676  // NOTE: These are incorrect!
4677  case UT_VOXELBORDER_MIRROR: basewrap = UT_WRAP_CLAMP; break;
4678  case UT_VOXELBORDER_EXTRAP: basewrap = UT_WRAP_CLAMP; break;
4679  default: basewrap = UT_WRAP_CLAMP; break;
4680  }
4681  for (i = 0; i < 3; i++)
4682  {
4683  if (i == clampaxis)
4684  wrap[i] = UT_WRAP_CLAMP;
4685  else
4686  wrap[i] = basewrap;
4687  }
4688 
4689  memset(&result, 0, sizeof(result));
4690 
4691  radius *= filter.getSupport();
4692 
4693  // Make a local copy of the position so that we can modify it.
4694  tpos = pos;
4695  for (i = 0; i < 3; i++)
4696  {
4697  tpos[i] = tpos[i]*myRes[i];
4698  if (!win[i].setWeights(filter, tpos[i], radius, myRes[i], wrap[i]))
4699  return result;
4700 
4701  start[i] = win[i].getStart() % myRes[i];
4702  size[i] = win[i].getSize();
4703 
4704  UT_ASSERT(start[i] >= 0);
4705  UT_ASSERT(size[i] <= myRes[i]);
4706 
4707  end[i] = start[i] + size[i];
4708  }
4709 
4710  // Accumulate filtered results
4711  pstart[2] = firstTile(start[2], end[2], myRes[2]);
4712  while (pstart[2] < end[2])
4713  {
4714  pend[2] = nextTile(tz, pstart[2], start[2], end[2], myRes[2]);
4715  pstart[1] = firstTile(start[1], end[1], myRes[1]);
4716  while (pstart[1] < end[1])
4717  {
4718  pend[1] = nextTile(ty, pstart[1], start[1], end[1], myRes[1]);
4719  pstart[0] = firstTile(start[0], end[0], myRes[0]);
4720  while (pstart[0] < end[0])
4721  {
4722  pend[0] = nextTile(tx, pstart[0], start[0], end[0], myRes[0]);
4723  tile = getTile(tx, ty, tz);
4724  UT_ASSERT(tile);
4725  tile->avgNonZero(pstart, pend, start, result);
4726  pstart[0] = pend[0];
4727  }
4728  pstart[1] = pend[1];
4729  }
4730  pstart[2] = pend[2];
4731  }
4732 
4733  return result;
4734 }
4735 
4736 template <typename T>
4737 void
4739  UT_FilterType filtertype,
4740  float filterwidthscale,
4741  int clampaxis)
4742 {
4743  fpreal radius;
4744  UT_Filter *filter;
4745 
4746  filter = UT_Filter::getFilter(filtertype);
4747 
4748  radius = SYSmax( src.getXRes() / (fpreal)getXRes(),
4749  src.getYRes() / (fpreal)getYRes(),
4750  src.getZRes() / (fpreal)getZRes(),
4751  1.0f );
4752  radius *= 0.5 * filterwidthscale;
4753 
4754  resamplethread(src, filter, radius, clampaxis);
4755 
4756  UT_Filter::releaseFilter(filter);
4757 }
4758 
4759 template <typename T>
4760 template <typename OP>
4761 void
4762 UT_VoxelArray<T>::forEachTile(const OP &op, bool shouldthread)
4763 {
4764  auto blockop = [&op, this](const UT_BlockedRange<int> &range)
4765  {
4767  for (int tileidx = range.begin(); tileidx != range.end(); tileidx++)
4768  {
4769  vit.setLinearTile(tileidx, this);
4770  op(vit);
4771  }
4772  };
4773 
4774  if (!shouldthread)
4775  {
4776  UTserialForEachNumber(numTiles(), blockop);
4777  }
4778  else
4779  {
4780  UTparallelForEachNumber(numTiles(), blockop);
4781  }
4782 }
4783 
4784 template <typename T>
4785 void
4786 UT_VoxelArray<T>::flattenPartial(T *flatarray, exint ystride, exint zstride,
4787  const UT_JobInfo &info) const
4788 {
4789  // Check to see if we are 2d, if so, we wish to use the
4790  // axis flatten.
4791  if (getXRes() == 1 && ystride == 1)
4792  {
4793  flattenPartialAxis<0>(flatarray, zstride, info);
4794  }
4795  else if (getYRes() == 1 && zstride == ystride)
4796  {
4797  flattenPartialAxis<1>(flatarray, zstride, info);
4798  }
4799  else if (getZRes() == 1)
4800  {
4801  flattenPartialAxis<2>(flatarray, ystride, info);
4802  }
4803  else
4804  {
4806 
4807  // Const cast.
4808  vit.setArray((UT_VoxelArray<T> *)this);
4809  // We can't use splitByTile as then our writes will fight
4810  // over 4k pages.
4811  vit.setPartialRange(info.job(), info.numJobs());
4812 
4813  for (vit.rewind(); !vit.atEnd(); vit.advance())
4814  {
4815  flatarray[vit.x() + vit.y()*ystride + vit.z()*zstride] = vit.getValue();
4816  }
4817  }
4818 }
4819 
4820 
4821 template <typename T>
4822 template <int AXIS2D>
4823 void
4825  const UT_JobInfo &info) const
4826 {
4827  // The x/y refer to the destination x/y.
4828  // The actual source index depends on AXIS2D.
4829  const int ax = (AXIS2D == 0) ? 1 : 0;
4830  const int ay = (AXIS2D == 2) ? 1 : 2;
4831  int tileidx[3] = { 0, 0, 0 };
4832 
4833  while (1)
4834  {
4835  exint tiley = info.nextTask();
4836  exint ystart = tiley * TILESIZE;
4837  if (ystart >= getRes(ay))
4838  break;
4839 
4840  T *stripe = &flatarray[ystart * ystride];
4841 
4842  int yres = SYSmin(getRes(ay) - ystart, TILESIZE);
4843 
4844  for (int tilex = 0, ntilex = getTileRes(ax); tilex < ntilex; tilex++)
4845  {
4846  tileidx[ax] = tilex;
4847  tileidx[ay] = tiley;
4848  auto tile = getTile(tileidx[0], tileidx[1], tileidx[2]);
4849 
4850  int xres = tile->getRes(ax);
4851  T *stripey = stripe;
4852  if (tile->isConstant())
4853  {
4854  const T *srcdata = tile->rawData();
4855 
4856  for (int y = 0; y < yres; y++)
4857  {
4858  for (int x = 0; x < xres; x++)
4859  {
4860  memcpy(&stripey[x], srcdata, sizeof(T));
4861  }
4862  stripey += ystride;
4863  }
4864  }
4865  else if (tile->isSimpleCompression())
4866  {
4867  const T *srcdata = tile->rawData();
4868 
4869  if (xres != TILESIZE)
4870  {
4871  for (int y = 0; y < yres; y++)
4872  {
4873  memcpy(stripey, srcdata, sizeof(T) * xres);
4874  srcdata += xres;
4875  stripey += ystride;
4876  }
4877  }
4878  else
4879  {
4880  for (int y = 0; y < yres; y++)
4881  {
4882  memcpy(stripey, srcdata, sizeof(T) * TILESIZE);
4883  srcdata += TILESIZE;
4884  stripey += ystride;
4885  }
4886  }
4887  }
4888  else
4889  {
4890  for (int y = 0; y < yres; y++)
4891  {
4892  int idx[3] = { 0, 0, 0 };
4893  idx[ay] = y;
4894  for (int x = 0; x < xres; x++)
4895  {
4896  idx[ax] = x;
4897  stripey[x] = (*tile)(idx[0], idx[1], idx[2]);
4898  }
4899  stripey += ystride;
4900  }
4901  }
4902 
4903  stripe += TILESIZE;
4904  }
4905  }
4906 }
4907 
4908 
4909 template <>
4910 inline void
4912  exint ystride, exint zstride,
4913  UT_Vector4F,
4914  const UT_JobInfo &info) const
4915 {
4917 
4918  // Const cast.
4919  vit.setArray(SYSconst_cast(this));
4920  // We can't use splitByTile as then our writes will fight
4921  // over 4k pages.
4922  vit.setPartialRange(info.job(), info.numJobs());
4923 
4924  for (vit.rewind(); !vit.atEnd(); vit.advance())
4925  {
4926  UT_Vector4F v = vit.getValue();
4927  int idx = (vit.x() + vit.y()*ystride + vit.z()*zstride) * 4;
4928 
4929  flatarray[idx] = (uint8) SYSclamp(v.x() * 255.0f, 0.0f, 255.0f);
4930  flatarray[idx+1] = (uint8) SYSclamp(v.y() * 255.0f, 0.0f, 255.0f);
4931  flatarray[idx+2] = (uint8) SYSclamp(v.z() * 255.0f, 0.0f, 255.0f);
4932  flatarray[idx+3] = (uint8) SYSclamp(v.w() * 255.0f, 0.0f, 255.0f);
4933  }
4934 }
4935 
4936 template <typename T>
4937 void
4939  exint ystride, exint zstride,
4940  T, const UT_JobInfo &info) const
4941 {
4942  UT_ASSERT(!"This template requires specific instantiations.");
4943 }
4944 
4945 template <>
4946 inline void
4948  exint ystride, exint zstride,
4949  UT_Vector4F,
4950  const UT_JobInfo &info) const
4951 {
4953 
4954  // Const cast.
4955  vit.setArray(SYSconst_cast(this));
4956  // We can't use splitByTile as then our writes will fight
4957  // over 4k pages.
4958  vit.setPartialRange(info.job(), info.numJobs());
4959 
4960  for (vit.rewind(); !vit.atEnd(); vit.advance())
4961  flatarray[vit.x() + vit.y()*ystride + vit.z()*zstride] = vit.getValue();
4962 }
4963 
4964 template <typename T>
4965 void
4967  exint ystride, exint zstride,
4968  T, const UT_JobInfo &info) const
4969 {
4970  UT_ASSERT(!"This template requires specific instantiations.");
4971 }
4972 
4973 template <>
4974 inline void
4976  exint ystride, exint zstride,
4977  UT_Vector4F,
4978  const UT_JobInfo &info) const
4979 {
4981 
4982  // Const cast.
4983  vit.setArray(SYSconst_cast(this));
4984  // We can't use splitByTile as then our writes will fight
4985  // over 4k pages.
4986  vit.setPartialRange(info.job(), info.numJobs());
4987 
4988  for (vit.rewind(); !vit.atEnd(); vit.advance())
4989  {
4990  UT_Vector4F v = vit.getValue();
4991 
4992  // NOTE: This works around an Nvidia driver bug on OSX, and older
4993  // Nvidia drivers on other platforms. The problem was that very
4994  // small FP values were causing huge random values to appear in
4995  // the 3D Texture.
4996  if(SYSabs(v.x()) < 1e-9)
4997  v.x() = 0.0;
4998  if(SYSabs(v.y()) < 1e-9)
4999  v.y() = 0.0;
5000  if(SYSabs(v.z()) < 1e-9)
5001  v.z() = 0.0;
5002  if(SYSabs(v.w()) < 1e-9)
5003  v.w() = 0.0;
5004 
5005  flatarray[vit.x() + vit.y()*ystride + vit.z()*zstride] = v;
5006  }
5007 }
5008 
5009 template <typename T>
5010 void
5012  exint ystride, exint zstride,
5013  T,const UT_JobInfo &info) const
5014 {
5015  UT_ASSERT(!"This template requires specific instantiations.");
5016 }
5017 
5018 
5019 template <typename T>
5020 void
5022  exint ystride, exint zstride,
5023  const UT_JobInfo &info)
5024 {
5026 
5027  vit.setArray(this);
5028  vit.splitByTile(info);
5029  vit.setCompressOnExit(true);
5030 
5031  for (vit.rewind(); !vit.atEnd(); vit.advance())
5032  {
5033  vit.setValue(flatarray[vit.x() + vit.y()*ystride + vit.z()*zstride]);
5034  }
5035 }
5036 
5037 template <typename T>
5038 template <int SLICE, typename S>
5039 S *
5040 UT_VoxelArray<T>::extractSlice(S *dstdata, int slice, bool half_slice) const
5041 {
5042  int slice_res = getRes(SLICE);
5043  // Exit out early if the slice isn't inside the array proper.
5044  if (slice < 0 || slice >= slice_res ||
5045  (half_slice && slice == slice_res - 1))
5046  return nullptr;
5047 
5048  constexpr int AXIS1 = SLICE == 0 ? 2 : 0;
5049  constexpr int AXIS2 = SLICE == 1 ? 2 : 1;
5050 
5051  // Voxel resolution along the major axis of the voxel array.
5052  int a1_res = getRes(AXIS1);
5053  // Tile resolutions of the voxel array.
5054  int a1_tiles = getTileRes(AXIS1);
5055  int a2_tiles = getTileRes(AXIS2);
5056  int ntiles = a1_tiles * a2_tiles;
5057  // Strides used to figure out the starting index of each tile's voxels in
5058  // the destination array.
5059  int tile_stride2 = (a1_res << TILEBITS);
5060  int tile_stride1 = TILESIZE;
5061  // Index of the tile for the slice and the local coordinate within it.
5062  int tile_slice = (slice >> TILEBITS);
5063  int local_slice = (slice & TILEMASK);
5064  // If half_slice_st is true, we are averaging two voxel slices which lie
5065  // within the same tile slices.
5066  bool half_slice_st = half_slice && (local_slice != TILEMASK);
5067  // If half_slice_tb is true, we are averaging current value of dstdata with
5068  // the tile's values.
5069  bool half_slice_tb = false;
5070 
5071  // This functor sets the destination array value. idx might get changed
5072  // internally, but it comes out the way it went in.
5073  // Behaviour of this function is controlled by the following booleans, in
5074  // this order.
5075  // * half_slice_st: sets the value to the average of tile values at local
5076  // local idx and one offset along SLICE by 1;
5077  // * half_slice_tb: sets the value to the average between the current value
5078  // in the destination array and the tile's value at the local idx;
5079  // * when neither of the above is true, sets to tile's value at the local
5080  // idx.
5081  auto set_functor = [&](const UT_VoxelTile<T>* tile, int idx[3], int vidx)
5082  {
5083  if (half_slice_st)
5084  {
5085  dstdata[vidx] = (*tile)(idx[0], idx[1], idx[2]);
5086  idx[SLICE]++;
5087  dstdata[vidx] = 0.5f * (dstdata[vidx] +
5088  (*tile)(idx[0], idx[1], idx[2]));
5089  idx[SLICE]--;
5090  }
5091  else if (half_slice_tb)
5092  dstdata[vidx] = 0.5f * (dstdata[vidx] +
5093  (*tile)(idx[0], idx[1], idx[2]));
5094  else
5095  dstdata[vidx] = (*tile)(idx[0], idx[1], idx[2]);
5096  };
5097  // This functor will be executed by the multithreaded loop below. Uses
5098  // set_functor to write the correct value into dstdata.
5099  auto loop_functor = [&](const UT_BlockedRange<int>& range)
5100  {
5101  // Tile index array.
5102  int is[3];
5103  is[SLICE] = tile_slice;
5104  // Array of indices within the tile.
5105  int is_local[3];
5106  is_local[SLICE] = local_slice;
5107 
5108  for (int i = range.begin(); i < range.end(); i++)
5109  {
5110  // Figure out coordinates of the current tile.
5111  is[AXIS1] = i % a1_tiles;
5112  is[AXIS2] = i / a1_tiles;
5113 
5114  // Get the current tile and its size.
5115  const UT_VoxelTile<T>* tile = getTile(is[0], is[1], is[2]);
5116  int a1_tile_res = tile->getRes(AXIS1);
5117  int a2_tile_res = tile->getRes(AXIS2);
5118  // Get the index we should write this tile to.
5119  int vcounter = is[AXIS2] * tile_stride2 + is[AXIS1] * tile_stride1;
5120 
5121  for (is_local[AXIS2] = 0; is_local[AXIS2] < a2_tile_res;
5122  is_local[AXIS2]++)
5123  {
5124  for (is_local[AXIS1] = 0; is_local[AXIS1] < a1_tile_res;
5125  is_local[AXIS1]++)
5126  {
5127  set_functor(tile, is_local, vcounter);
5128  vcounter++;
5129  }
5130  // Wrap the counter to the next row.
5131  vcounter += a1_res - a1_tile_res;
5132  }
5133  }
5134  };
5135 
5136  // Run the loop.
5137  UTparallelFor(UT_BlockedRange<int>(0, ntiles), loop_functor);
5138 
5139  // If we're halfway between two voxel slices that lie in different tiles, we
5140  // need to do another sweep and modify the results.
5141  if (half_slice && !half_slice_st)
5142  {
5143  half_slice_tb = true;
5144  tile_slice++;
5145  local_slice = 0;
5146  UTparallelFor(UT_BlockedRange<int>(0, ntiles), loop_functor);
5147  }
5148 
5149  return dstdata;
5150 }
5151 
5152 template <typename T>
5153 template <typename S>
5154 S *
5156  const UT_IntArray &tilelist) const
5157 {
5158  for (int i = 0; i < tilelist.entries(); i++)
5159  {
5160  UT_ASSERT(tilelist(i) >= 0 && tilelist(i) < numTiles());
5161  const UT_VoxelTile<T> *tile = getLinearTile(tilelist(i));
5162 
5163  tile->flatten(dstdata, stride);
5164  dstdata += tile->numVoxels() * stride;
5165  }
5166  return dstdata;
5167 }
5168 
5169 template <typename T>
5170 template <typename S, typename IDX>
5171 S *
5173  const IDX *ix, const IDX *iy, const IDX *iz,
5174  const UT_Array<UT_VoxelArrayTileDataDescr> &tilelist) const
5175 {
5176  int srcidx = 0;
5177 
5178  for (auto && tiledata : tilelist)
5179  {
5180  int tileidx = tiledata.tileidx;
5181  UT_ASSERT(tileidx >= 0 && tileidx < numTiles());
5182 
5183  const UT_VoxelTile<T> *tile = getLinearTile(tileidx);
5184  int tilevoxel = tile->numVoxels();
5185 
5186  if (tilevoxel == tiledata.numvoxel)
5187  {
5188  // Full tile.
5189  tile->flatten(dstdata, stride);
5190  dstdata += tiledata.numvoxel * stride;
5191  srcidx += tiledata.numvoxel;
5192  }
5193  else
5194  {
5195  // Partial tile...
5196  int basex, basey, basez;
5197  linearTileToXYZ(tileidx, basex, basey, basez);
5198 
5199  basex <<= TILEBITS;
5200  basey <<= TILEBITS;
5201  basez <<= TILEBITS;
5202 
5203  if (tile->isSimpleCompression())
5204  {
5205  const T *src = tile->rawData();
5206  if (tile->isConstant())
5207  {
5208  S cval = *src;
5209  for (int i = 0; i < tiledata.numvoxel; i++)
5210  {
5211  *dstdata = cval;
5212  dstdata += stride;
5213  srcidx++;
5214  }
5215  }
5216  else
5217  {
5218  int w = tile->xres();
5219  int h = tile->yres();
5220  for (int i = 0; i < tiledata.numvoxel; i++)
5221  {
5222  UT_ASSERT_P(ix[srcidx] >= basex && ix[srcidx] < basex+TILESIZE);
5223  UT_ASSERT_P(iy[srcidx] >= basey && iy[srcidx] < basey+TILESIZE);
5224  UT_ASSERT_P(iz[srcidx] >= basez && iz[srcidx] < basez+TILESIZE);
5225  *dstdata = src[ (ix[srcidx] - basex)
5226  + (iy[srcidx] - basey) * w
5227  + (iz[srcidx] - basez) * w * h];
5228  dstdata += stride;
5229  srcidx++;
5230  }
5231  }
5232  }
5233  else
5234  {
5235  for (int i = 0; i < tiledata.numvoxel; i++)
5236  {
5237  UT_ASSERT_P(ix[srcidx] >= basex && ix[srcidx] < basex+TILESIZE);
5238  UT_ASSERT_P(iy[srcidx] >= basey && iy[srcidx] < basey+TILESIZE);
5239  UT_ASSERT_P(iz[srcidx] >= basez && iz[srcidx] < basez+TILESIZE);
5240  *dstdata = (*tile)(ix[srcidx] - basex,
5241  iy[srcidx] - basey,
5242  iz[srcidx] - basez);
5243  dstdata += stride;
5244  srcidx++;
5245  }
5246  }
5247  }
5248  }
5249  return dstdata;
5250 }
5251 
5252 template <typename T>
5253 template <typename S>
5254 const S *
5256  const UT_IntArray &tilelist)
5257 {
5258  bool docompress = getCompressionOptions().compressionEnabled();
5259  for (int i = 0; i < tilelist.entries(); i++)
5260  {
5261  UT_ASSERT(tilelist(i) >= 0 && tilelist(i) < numTiles());
5262  UT_VoxelTile<T> *tile = getLinearTile(tilelist(i));
5263 
5264  tile->writeData(srcdata, stride);
5265  if (docompress)
5266  tile->tryCompress(getCompressionOptions());
5267  srcdata += tile->numVoxels() * stride;
5268  }
5269  return srcdata;
5270 }
5271 
5272 template <typename T>
5273 template <typename S, typename IDX>
5274 const S *
5276  const IDX *ix, const IDX *iy, const IDX *iz,
5277  const UT_Array<UT_VoxelArrayTileDataDescr> &tilelist)
5278 {
5279  bool docompress = getCompressionOptions().compressionEnabled();
5280  int srcidx = 0;
5281 
5282  for (auto && tiledata : tilelist)
5283  {
5284  int tileidx = tiledata.tileidx;
5285  UT_ASSERT(tileidx >= 0 && tileidx < numTiles());
5286 
5287  UT_VoxelTile<T> *tile = getLinearTile(tileidx);
5288  int tilevoxel = tile->numVoxels();
5289 
5290  if (tilevoxel == tiledata.numvoxel)
5291  {
5292  tile->writeData(srcdata, stride);
5293  srcdata += tiledata.numvoxel * stride;
5294  srcidx += tiledata.numvoxel;
5295  }
5296  else
5297  {
5298  // Partial tile.
5299  int basex, basey, basez;
5300  linearTileToXYZ(tileidx, basex, basey, basez);
5301 
5302  basex <<= TILEBITS;
5303  basey <<= TILEBITS;
5304  basez <<= TILEBITS;
5305 
5306  for (int i = 0; i < tiledata.numvoxel; i++)
5307  {
5308  UT_ASSERT_P(ix[srcidx] >= basex && ix[srcidx] < basex+TILESIZE);
5309  UT_ASSERT_P(iy[srcidx] >= basey && iy[srcidx] < basey+TILESIZE);
5310  UT_ASSERT_P(iz[srcidx] >= basez && iz[srcidx] < basez+TILESIZE);
5311  tile->setValue(ix[srcidx] - basex,
5312  iy[srcidx] - basey,
5313  iz[srcidx] - basez,
5314  *srcdata);
5315  srcdata += stride;
5316  srcidx++;
5317  }
5318  }
5319 
5320  if (docompress)
5321  tile->tryCompress(getCompressionOptions());
5322  }
5323  return srcdata;
5324 }
5325 
5326 ///
5327 /// This helper class can be used for iterating over a voxel array in relation to another
5328 /// one. In particular, dst is the destination voxel array that is to be iterated over; its
5329 /// tile at [X, Y, Z] is taken to be coincident with tile [X + xoff, Y + yoff, Z + zoff] of
5330 /// src. Tiles of dst are partitioned into two sets:
5331 /// - BP (boundary pass): these are tiles of dst that are coincident with tiles of src that
5332 /// are at the boundary (so they affect extrapolation);
5333 /// - IP (internal pass): these tiles are coincident with internal tiles of src (those that
5334 /// are irrelevant for extrapolation).
5335 /// Once created, an object of this class can return the number of tiles in each set and
5336 /// convert from a linear 0-N index to [X, Y, Z] coordinates that identify the tile within
5337 /// dst.
5338 ///
5339 /// This class can be roughly used as follows... Main function:
5340 ///
5341 /// __linearTileIndexConverter index(dst, src, xoff, yoff, zoff);
5342 /// customFunctionBP(..., index);
5343 /// customFunctionIP(..., index);
5344 ///
5345 /// BPPartial(..., const __linearTileIndexConverter& index, const UT_JobInfo& i) function:
5346 ///
5347 /// int t0, t1;
5348 /// i.divideWork(index.getNTilesBP(), t0, t1);
5349 /// for (int j = t0; j < t1; j++)
5350 /// {
5351 /// int x, y, z;
5352 /// index.toLinearBP(j, x, y, z);
5353 /// UT_VoxerTile<T>* dtile = src.getTile(x, y, z);
5354 /// ...
5355 /// }
5356 ///
5357 /// With a similar implementation for IP. See moveTilesWithOffset() for an example.
5358 ///
5360 {
5361 public:
5362  /// Creates an index converter from the given voxel arrays. This object partitions tiles
5363  /// of dst into those that are affected by
5364  template <typename T>
5366  const UT_VoxelArray<T>* src,
5367  int xoff, int yoff, int zoff)
5368  {
5369  myXMax = dst->getTileRes(0);
5370  myYMax = dst->getTileRes(1);
5371  myZMax = dst->getTileRes(2);
5372 
5373  // If source has a constant border, no tile affects extrapolation; so all tiles
5374  // can be put into IP. Otherwise, the outer layer of src's tiles are part of BP.
5375  int m = (src->getBorder() == UT_VOXELBORDER_CONSTANT) ? 0 : 1;
5376  myXSkipMin = SYSmax(m - xoff, 0);
5377  myXSkipLen =
5378  SYSmax(SYSmin(myXMax, src->getTileRes(0) - xoff - m) - myXSkipMin, 0);
5379  myYSkipMin = SYSmax(m - yoff, 0);
5380  myYSkipLen =
5381  SYSmax(SYSmin(myYMax, src->getTileRes(1) - yoff - m) - myYSkipMin, 0);
5382  myZSkipMin = SYSmax(m - zoff, 0);
5383  myZSkipLen =
5384  SYSmax(SYSmin(myZMax, src->getTileRes(2) - zoff - m) - myZSkipMin, 0);
5385  }
5386 
5387  /// Returns the number of tiles in each part.
5388  int getNTilesBP() const
5389  {
5390  return myXMax * myYMax * myZMax - getNTilesIP();
5391  }
5392  int getNTilesIP() const
5393  {
5394  return myXSkipLen * myYSkipLen * myZSkipLen;
5395  }
5396 
5397  /// Converts a linear index (between 0 and getNTiles*P()-1) to the [X, Y, Z] tile
5398  /// coordinate with respect to dst.
5399  void toLinearBP(int k, int& x, int& y, int& z) const
5400  {
5401  // Number of voxels before the first Z slice with a hole.
5402  const int check_0 = myXMax * myYMax * myZSkipMin;
5403  // Check if we are before the first Z slice of the hole or if there is no hole.
5404  if (myXSkipLen == 0 || myYSkipLen == 0 || myZSkipLen == 0 || k < check_0)
5405  {
5406  _toRegularLinear(k, myXMax, myYMax, x, y, z);
5407  return;
5408  }
5409  k -= check_0;
5410 
5411  // Number of voxels per Z-slice with a hole.
5412  const int check_1a = myXMax * myYMax - myXSkipLen * myYSkipLen;
5413  // Total number of voxels in Z-slices with a hole.
5414  const int check_1b = check_1a * myZSkipLen;
5415  // Check if we are past the hole...
5416  if (k >= check_1b)
5417  {
5418  _toRegularLinear(k - check_1b, myXMax, myYMax, x, y, z);
5419  z += myZSkipMin + myZSkipLen;
5420  return;
5421  }
5422 
5423  // We are in the holed slices... First, determine the Z-slice.
5424  z = k / check_1a + myZSkipMin;
5425  // The remainder (index within the slice).
5426  k = k % check_1a;
5427  // Check if we are before the first Y slice with a hole.
5428  const int check_2 = myXMax * myYSkipMin;
5429  if (k < check_2)
5430  {
5431  y = k / myXMax;
5432  x = k % myXMax;
5433  return;
5434  }
5435  k -= check_2;
5436  // Check if we are past the last Y slice with a ahole.
5437  const int check_3 = (myXMax - myXSkipLen) * myYSkipLen;
5438  if (k >= check_3)
5439  {
5440  k -= check_3;
5441  y = k / myXMax + myYSkipMin + myYSkipLen;
5442  x = k % myXMax;
5443  return;
5444  }
5445 
5446  // We are in the holed slices. Find the y coordinate.
5447  y = k / (myXMax - myXSkipLen) + myYSkipMin;
5448  x = k % (myXMax - myXSkipLen);
5449  if (x >= myXSkipMin)
5450  x += myXSkipLen;
5451  }
5452  void toLinearIP(int k, int& x, int& y, int& z) const
5453  {
5454  _toRegularLinear(k, myXSkipLen, myYSkipLen, x, y, z);
5455  x += myXSkipMin;
5456  y += myYSkipMin;
5457  z += myZSkipMin;
5458  }
5459 
5460 protected:
5461  static void _toRegularLinear(int k, int xdim, int ydim, int& x, int& y, int& z)
5462  {
5463  x = k % xdim;
5464  k = k / xdim;
5465  y = k % ydim;
5466  z = k / ydim;
5467  }
5468 
5469 protected:
5473 };
5474 
5475 template <typename T>
5476 void
5478  int tileoffy, int tileoffz)
5479 {
5480  __linearTileIndexConverter index(this, &src, tileoffx, tileoffy, tileoffz);
5481  UTparallelInvoke(true, [&]
5482  {
5483  // Divide up the work (only internal tiles). Source tiles for these guys can be
5484  // safely changed without affecting other threads.
5486  [&](const UT_BlockedRange<int>& range)
5487  {
5488  for (int i = range.begin(); i < range.end(); i++)
5489  {
5490  // Get the location of the current tile...
5491  int xyz[3];
5492  index.toLinearIP(i, xyz[0], xyz[1], xyz[2]);
5493 
5494  // Get the tiles and exchange data.
5495  UT_VoxelTile<T>* tile = getTile(xyz[0], xyz[1], xyz[2]);
5496  UT_VoxelTile<T>* srctile = src.getTile(xyz[0] + tileoffx,
5497  xyz[1] + tileoffy,
5498  xyz[2] + tileoffz);
5499 
5500  // If tiles are of the same size, simply exchange the data. We know these
5501  // tiles do not affect results outside of src.
5502  if (tile->xres() == srctile->xres() && tile->yres() == srctile->yres()
5503  && tile->zres() == srctile->zres())
5504  {
5505  // The tiles are of the same size, so we can just exchange the data
5506  // pointers.
5507  UTswap(tile->myData, srctile->myData);
5508  UTswap(tile->myCompressionType, srctile->myCompressionType);
5509  UTswap(tile->myForeignData, srctile->myForeignData);
5510  }
5511  else
5512  {
5513  // Otherwise, manually fill in the values.
5514  int offv[] = {(xyz[0] + tileoffx) * TILESIZE,
5515  (xyz[1] + tileoffy) * TILESIZE,
5516  (xyz[2] + tileoffz) * TILESIZE};
5517  for (int z = 0; z < tile->zres(); z++)
5518  {
5519  for (int y = 0; y < tile->yres(); y++)
5520  {
5521  for (int x = 0; x < tile->xres(); x++)
5522  {
5523  tile->setValue(x, y, z, src.getValue(x + offv[0],
5524  y + offv[1],
5525  z + offv[2]));
5526  }
5527  }
5528  }
5529  }
5530  }
5531  });
5532  }, [&]
5533  {
5534  // Is the border constant?
5535  const bool const_src_border = (src.getBorder() == UT_VOXELBORDER_CONSTANT);
5536  const T border_val = src.getBorderValue();
5537 
5538  // Offsets in terms of tiles.
5539  const int off[] = {tileoffx, tileoffy, tileoffz};
5540  // Tile resolution of the source.
5541  const int src_tileres[] = {src.getTileRes(0), src.getTileRes(1), src.getTileRes(2)};
5542 
5543  // Divide up the work (only boundary tiles). Source tiles for these guys cannot be
5544  // modified safely.
5546  [&](const UT_BlockedRange<int>& range)
5547  {
5548  for (int i = range.begin(); i < range.end(); i++)
5549  {
5550  // Get the location of the current tile...
5551  int xyz[3];
5552  index.toLinearBP(i, xyz[0], xyz[1], xyz[2]);
5553 
5554  // Get the current tile...
5555  UT_VoxelTile<T>* tile = getTile(xyz[0], xyz[1], xyz[2]);
5556  bool outside = false;
5557  for (int j = 0; j < 3; j++)
5558  {
5559  xyz[j] += off[j];
5560  outside = outside || (xyz[j] < 0 || xyz[j] >= src_tileres[j]);
5561  }
5562 
5563  // If we are completely outside, and borders are constant, we can go ahead and
5564  // make this tile constant.
5565  if (outside && const_src_border)
5566  {
5567  tile->makeConstant(border_val);
5568  }
5569  else
5570  {
5571  // Otherwise, manually fill in the values.
5572  int offv[] = {xyz[0] * TILESIZE, xyz[1] * TILESIZE, xyz[2] * TILESIZE};
5573  for (int z = 0; z < tile->zres(); z++)
5574  {
5575  for (int y = 0; y < tile->yres(); y++)
5576  {
5577  for (int x = 0; x < tile->xres(); x++)
5578  {
5579  tile->setValue(x, y, z, src.getValue(x + offv[0],
5580  y + offv[1],
5581  z + offv[2]));
5582  }
5583  }
5584  }
5585  }
5586  }
5587  });
5588  });
5589 }
5590 
5591 template <typename T>
5592 void
5594  int offx, int offy, int offz)
5595 {
5596  UT_VoxelBorderType srcborder;
5597  T srcborderval;
5598 
5599  srcborder = src.getBorder();
5600  srcborderval = src.getBorderValue();
5601 
5602  if (srcborder != UT_VOXELBORDER_EXTRAP)
5603  {
5604  // These borders may be constant friendly
5605  T srcval;
5606 
5607  if (src.isConstant(&srcval))
5608  {
5609  if (srcborder != UT_VOXELBORDER_CONSTANT ||
5610  SYSisEqual(srcborderval, srcval))
5611  {
5612  // We can trivially make ourselves constant.
5613  constant(srcval);
5614  return;
5615  }
5616  }
5617  }
5618 
5619  copyWithOffsetInternal(src, offx, offy, offz);
5620 }
5621 
5622 template <typename T>
5623 void
5625  int offx, int offy, int offz,
5626  const UT_JobInfo &info)
5627 {
5629  UT_Vector3I off(offx, offy, offz);
5630  bool can_copy_tiles = ((offx & TILEMASK) == 0) && ((offy & TILEMASK) == 0)
5631  && ((offz & TILEMASK) == 0);
5632 
5633  vit.setArray(this);
5634  vit.splitByTile(info);
5635  vit.setCompressOnExit(true);
5636 
5637  // Iterate over all tiles
5638  for (vit.rewind(); !vit.atEnd(); vit.advanceTile())
5639  {
5640  // Check out this tile
5641  UT_VoxelTile<T> *tile = vit.getTile();
5642  UT_VoxelTile<T> *srctile;
5643  int tx, ty, tz;
5644 
5645  UT_Vector3I start, end, srctileidx, srctileoff, srcend, srcendtile;
5646  vit.getTileVoxels(start, end);
5647 
5648  // If offsets are all multiples of TILESIZE, it is possible to do direct copies,
5649  // assuming source tile is inside and of the same size.
5650  if (can_copy_tiles)
5651  {
5652  // Start by getting the source tile index and ensuring it is inside the
5653  // source array.
5654  bool inside = true;
5655  for (int i = 0; i < 3; i++)
5656  {
5657  srctileidx(i) = vit.myTilePos[i] + (off(i) >> TILEBITS);
5658  inside = inside && srctileidx(i) >= 0
5659  && srctileidx(i) < src.getTileRes(i);
5660  }
5661  // If the tile is inside, we make sure the tile sizes are the same, in which
5662  // case we can do a copy and move on.
5663  if (inside)
5664  {
5665  srctile = src.getTile(srctileidx.x(), srctileidx.y(), srctileidx.z());
5666  if (tile->xres() == srctile->xres() && tile->yres() == srctile->yres()
5667  && tile->zres() == srctile->zres())
5668  {
5669  *tile = *srctile;
5670  continue;
5671  }
5672  }
5673  }
5674 
5675  srctileidx = start;
5676  srctileidx += off;
5677  srctileidx.x() >>= TILEBITS;
5678  srctileidx.y() >>= TILEBITS;
5679  srctileidx.z() >>= TILEBITS;
5680  srctileoff.x() = off.x() & TILEMASK;
5681  srctileoff.y() = off.y() & TILEMASK;
5682  srctileoff.z() = off.z() & TILEMASK;
5683 
5684  srcend = start;
5685  // We are very careful here to be inclusive, so we don't trigger
5686  // the next tile. This is the largest index we expect to index.
5687  srcend.x() += tile->xres() - 1;
5688  srcend.y() += tile->yres() - 1;
5689  srcend.z() += tile->zres() - 1;
5690  srcend += off;
5691  srcendtile = srcend;
5692  srcendtile.x() >>= TILEBITS;
5693  srcendtile.y() >>= TILEBITS;
5694  srcendtile.z() >>= TILEBITS;
5695 
5696  UT_ASSERT(srcendtile.x() == srctileidx.x() ||
5697  srcendtile.x() == srctileidx.x()+1);
5698  UT_ASSERT(srcendtile.y() == srctileidx.y() ||
5699  srcendtile.y() == srctileidx.y()+1);
5700  UT_ASSERT(srcendtile.z() == srctileidx.z() ||
5701  srcendtile.z() == srctileidx.z()+1);
5702 
5703  // Check if we are fully in bounds.
5704  if (srctileidx.x() >= 0 &&
5705  srctileidx.y() >= 0 &&
5706  srctileidx.z() >= 0 &&
5707  srcendtile.x() < src.getTileRes(0) &&
5708  srcendtile.y() < src.getTileRes(1) &&
5709  srcendtile.z() < src.getTileRes(2) &&
5710  srcend.x() < src.getXRes() &&
5711  srcend.y() < src.getYRes() &&
5712  srcend.z() < src.getZRes())
5713  {
5714  bool allconst = true, firsttile = true;
5715  T constval, cval;
5716  // Check if we are all constant...
5717  for (tz = srctileidx.z(); allconst && tz <= srcendtile.z(); tz++)
5718  for (ty = srctileidx.y(); allconst && ty <= srcendtile.y(); ty++)
5719  for (tx = srctileidx.x(); tx <= srcendtile.x(); tx++)
5720  {
5721  srctile = src.getTile(tx, ty, tz);
5722  if (!srctile->isConstant())
5723  {
5724  allconst = false;
5725  break;
5726  }
5727  cval = (*srctile)(0, 0, 0);
5728  if (firsttile)
5729  {
5730  firsttile = false;
5731  constval = cval;
5732  }
5733  if (!SYSisEqual(cval, constval))
5734  {
5735  allconst = false;
5736  break;
5737  }
5738  }
5739  if (allconst)
5740  {
5741  // Should have found at least one tile!
5742  UT_ASSERT(!firsttile);
5743  tile->makeConstant(constval);
5744 
5745  // Onto the next tile!
5746  continue;
5747  }
5748  }
5749 
5750  // All of the fragments aren't constant, or aren't all inside
5751  // our range. So we have to work fragment at a time..
5752  for (tz = srctileidx.z(); tz <= srcendtile.z(); tz++)
5753  for (ty = srctileidx.y(); ty <= srcendtile.y(); ty++)
5754  for (tx = srctileidx.x(); tx <= srcendtile.x(); tx++)
5755  {
5756  int destx, desty, destz;
5757  int srcx, srcy, srcz;
5758 
5759  destx = (tx == srctileidx.x()) ? 0 : (TILESIZE-srctileoff.x());
5760  desty = (ty == srctileidx.y()) ? 0 : (TILESIZE-srctileoff.y());
5761  destz = (tz == srctileidx.z()) ? 0 : (TILESIZE-srctileoff.z());
5762  srcx = (tx == srctileidx.x()) ? srctileoff.x() : 0;
5763  srcy = (ty == srctileidx.y()) ? srctileoff.y() : 0;
5764  srcz = (tz == srctileidx.z()) ? srctileoff.z() : 0;
5765 #if 1
5766  if (tx >= 0 &&
5767  ty >= 0 &&
5768  tz >= 0 &&
5769  tx < src.getTileRes(0) &&
5770  ty < src.getTileRes(1) &&
5771  tz < src.getTileRes(2) &&
5772  ((tx != src.getTileRes(0)-1) ||
5773  srcend.x() < src.getXRes()) &&
5774  ((ty != src.getTileRes(1)-1) ||
5775  srcend.y() < src.getYRes()) &&
5776  ((tz != src.getTileRes(2)-1) ||
5777  srcend.z() < src.getZRes())
5778  )
5779  {
5780  srctile = src.getTile(tx, ty, tz);
5781  // In bounds
5782  tile->copyFragment(
5783  destx, desty, destz,
5784  *srctile,
5785  srcx, srcy, srcz
5786  );
5787  }
5788  else
5789 #endif
5790  {
5791  // Out of bounds!
5792  int maxd = SYSmin(tile->zres(),
5793  TILESIZE-srcz);
5794  int maxh = SYSmin(tile->yres(),
5795  TILESIZE-srcy);
5796  int maxw = SYSmin(tile->xres(),
5797  TILESIZE-srcx);
5798  for (int z = destz; z < maxd; z++)
5799  {
5800  for (int y = desty; y < maxh; y++)
5801  {
5802  for (int x = destx; x < maxw; x++)
5803  {
5804  T val;
5805 
5806  val = src.getValue(x + vit.x() + offx,
5807  y + vit.y() + offy,
5808  z + vit.z() + offz);
5809  tile->setValue(x, y, z, val);
5810  }
5811  }
5812  }
5813  }
5814  }
5815  }
5816 }
5817 
5818 template <typename T>
5819 void
5821  const UT_Filter *filter, float radius,
5822  int clampaxis,
5823  const UT_JobInfo &info)
5824 {
5826  UT_Vector3 pos;
5827  UT_Vector3 ratio;
5828  UT_Interrupt *boss = UTgetInterrupt();
5829 
5830  vit.setArray(this);
5831  vit.splitByTile(info);
5832  vit.setCompressOnExit(true);
5833  vit.setInterrupt(boss);
5834 
5835  ratio.x() = 1.0f / getXRes();
5836  ratio.y() = 1.0f / getYRes();
5837  ratio.z() = 1.0f / getZRes();
5838 
5839  for (vit.rewind(); !vit.atEnd(); vit.advance())
5840  {
5841  pos.x() = vit.x()+0.5f;
5842  pos.y() = vit.y()+0.5f;
5843  pos.z() = vit.z()+0.5f;
5844  pos *= ratio;
5845 
5846  vit.setValue(src.evaluate(pos, *filter, radius, clampaxis));
5847  }
5848 }
5849 
5850 template <typename T>
5851 bool
5852 UT_VoxelArray<T>::posToIndex(UT_Vector3 pos, int &x, int &y, int &z) const
5853 {
5854  // We go from the position in the unit cube into the index.
5855  // The center of cells must map to the exact integer indices.
5856  pos.x() *= myRes[0];
5857  pos.y() *= myRes[1];
5858  pos.z() *= myRes[2];
5859 
5860  // The centers of cells are now mapped .5 too high. Ie, the true
5861  // center of cell index (0,0,0) would be (0.5,0.5,0.5) This, however,
5862  // is exactly what we want for rounding.
5863  x = (int) SYSfloor(pos.x());
5864  y = (int) SYSfloor(pos.y());
5865  z = (int) SYSfloor(pos.z());
5866 
5867  // Determine if out of bounds.
5868  return isValidIndex(x, y, z);
5869 }
5870 
5871 template <typename T>
5872 bool
5874 {
5875  // We go from the position in the unit cube into the index.
5876  // The center of cells must map to the exact integer indices.
5877  pos.x() *= myRes[0];
5878  pos.y() *= myRes[1];
5879  pos.z() *= myRes[2];
5880 
5881  // The centers of cells are now mapped .5 too high. Ie, the true
5882  // center of cell index (0,0,0) would be (0.5,0.5,0.5)
5883  pos.x() -= 0.5;
5884  pos.y() -= 0.5;
5885  pos.z() -= 0.5;
5886 
5887  ipos = pos;
5888 
5889  // Determine if out of bounds.
5890  if (pos.x() < 0 || pos.x() >= myRes[0] ||
5891  pos.y() < 0 || pos.y() >= myRes[1] ||
5892  pos.z() < 0 || pos.z() >= myRes[2])
5893  return false;
5894 
5895  return true;
5896 }
5897 
5898 template <typename T>
5899 bool
5901 {
5902  // We go from the position in the unit cube into the index.
5903  // The center of cells must map to the exact integer indices.
5904  pos.x() *= myRes[0];
5905  pos.y() *= myRes[1];
5906  pos.z() *= myRes[2];
5907 
5908  // The centers of cells are now mapped .5 too high. Ie, the true
5909  // center of cell index (0,0,0) would be (0.5,0.5,0.5) This, however,
5910  // is exactly what we want for rounding.
5911  x = (exint) SYSfloor(pos.x());
5912  y = (exint) SYSfloor(pos.y());
5913  z = (exint) SYSfloor(pos.z());
5914 
5915  // Determine if out of bounds.
5916  return isValidIndex(x, y, z);
5917 }
5918 
5919 template <typename T>
5920 bool
5922 {
5923  // We go from the position in the unit cube into the index.
5924  // The center of cells must map to the exact integer indices.
5925  pos.x() *= myRes[0];
5926  pos.y() *= myRes[1];
5927  pos.z() *= myRes[2];
5928 
5929  // The centers of cells are now mapped .5 too high. Ie, the true
5930  // center of cell index (0,0,0) would be (0.5,0.5,0.5)
5931  pos.x() -= 0.5;
5932  pos.y() -= 0.5;
5933  pos.z() -= 0.5;
5934 
5935  ipos = pos;
5936 
5937  // Determine if out of bounds.
5938  if (pos.x() < 0 || pos.x() >= myRes[0] ||
5939  pos.y() < 0 || pos.y() >= myRes[1] ||
5940  pos.z() < 0 || pos.z() >= myRes[2])
5941  return false;
5942 
5943  return true;
5944 }
5945 
5946 template <typename T>
5947 bool
5948 UT_VoxelArray<T>::indexToPos(int x, int y, int z, UT_Vector3F &pos) const
5949 {
5950  pos.x() = x;
5951  pos.y() = y;
5952  pos.z() = z;
5953 
5954  // Move the indices to the centers of the cells.
5955  pos.x() += 0.5;
5956  pos.y() += 0.5;
5957  pos.z() += 0.5;
5958 
5959  // And scale into the unit cube.
5960  pos *= myInvRes;
5961 
5962  // Return true if the original coordinates were in range.
5963  return isValidIndex(x, y, z);
5964 }
5965 
5966 template <typename T>
5967 bool
5969 {
5970  pos.x() = x;
5971  pos.y() = y;
5972  pos.z() = z;
5973 
5974  // Move the indices to the centers of the cells.
5975  pos.x() += 0.5;
5976  pos.y() += 0.5;
5977  pos.z() += 0.5;
5978 
5979  // And scale into the unit cube.
5980  pos *= myInvRes;
5981 
5982  // Return true if the original coordinates were in range.
5983  return isValidIndex(x, y, z);
5984 }
5985 
5986 template <typename T>
5987 void
5989 {
5990  pos = index;
5991 
5992  // Move the indices to the centers of the cells.
5993  pos.x() += 0.5;
5994  pos.y() += 0.5;
5995  pos.z() += 0.5;
5996 
5997  // And scale into the unit cube.
5998  pos *= myInvRes;
5999 }
6000 
6001 template <typename T>
6002 void
6004 {
6005  pos = index;
6006 
6007  // Move the indices to the centers of the cells.
6008  pos.x() += 0.5;
6009  pos.y() += 0.5;
6010  pos.z() += 0.5;
6011 
6012  // And scale into the unit cube.
6013  pos *= myInvRes;
6014 }
6015 
6016 template <typename T>
6017 void
6019 {
6020  myBorderType = type;
6021  myBorderValue = t;
6022 }
6023 
6024 template <typename T>
6025 void
6027 {
6028  myBorderScale[0] = sx;
6029  myBorderScale[1] = sy;
6030  myBorderScale[2] = sz;
6031 }
6032 
6033 template <typename T>
6034 void
6036 {
6037  UT_VoxelArrayIterator<T> vit(this);
6038  vit.splitByTile(info);
6039  for (vit.rewind(); !vit.atEnd(); vit.advanceTile())
6040  {
6041  int i = vit.getLinearTileNum();
6042  myTiles[i].tryCompress(getCompressionOptions());
6043  }
6044 }
6045 
6046 template <typename T>
6047 void
6049 {
6050  UT_VoxelArrayIterator<T> vit(this);
6051  vit.splitByTile(info);
6052  for (vit.rewind(); !vit.atEnd(); vit.advanceTile())
6053  {
6054  int i = vit.getLinearTileNum();
6055  myTiles[i].uncompress();
6056  }
6057 }
6058 
6059 template <typename T>
6060 void
6062 {
6063  UT_VoxelArrayIterator<T> vit(this);
6064  vit.splitByTile(info);
6065  for (vit.rewind(); !vit.atEnd(); vit.advanceTile())
6066  {
6067  int i = vit.getLinearTileNum();
6068  if (!myTiles[i].isConstant())
6069  myTiles[i].uncompress();
6070  }
6071 }
6072 
6073 template <typename T>
6074 void
6075 UT_VoxelArray<T>::saveData(std::ostream &os) const
6076 {
6077  T cval;
6078  char version;
6079 
6080  // First determine if we are fully constant.
6081  if (isConstant(&cval))
6082  {
6083  // Save a constant array.
6084  version = 0;
6085  UTwrite(os, &version, 1);
6086  UTwrite<T>(os, &cval);
6087  return;
6088  }
6089 
6090  // Compressed tiles.
6091  version = 1;
6092  UTwrite(os, &version, 1);
6093 
6094  // Store list of compression types
6096 
6097  int i, ntiles;
6098 
6099  ntiles = numTiles();
6100  for (i = 0; i < ntiles; i++)
6101  {
6102  myTiles[i].save(os);
6103  }
6104 }
6105 
6106 
6107 template <typename T>
6108 void
6110 {
6111  T cval;
6112  char version;
6113 
6114  is.readChar(version);
6115 
6116  // First determine if we are fully constant.
6117  if (version == 0)
6118  {
6119  // Save a constant array.
6120  is.read<T>(&cval);
6121 
6122  constant(cval);
6123  return;
6124  }
6125 
6126  if (version == 1)
6127  {
6128  UT_IntArray compressions;
6129 
6130  // Store list of compression types
6131  UT_VoxelTile<T>::loadCompressionTypes(is, compressions);
6132 
6133  int i, ntiles;
6134 
6135  ntiles = numTiles();
6136  for (i = 0; i < ntiles; i++)
6137  {
6138  myTiles[i].load(is, compressions);
6139  }
6140  }
6141 }
6142 
6143 template <typename T>
6144 bool
6145 UT_VoxelArray<T>::saveData(UT_JSONWriter &w, const char *shared_mem_owner) const
6146 {
6147  constexpr exint tuple_size = UT_FixedVectorTraits<T>::TupleSize;
6148 
6149  bool ok = true;
6150  T cval;
6151  int8 version;
6152 
6153  // First determine if we are fully constant.
6154  if (isConstant(&cval))
6155  {
6156  ok = ok && w.jsonBeginArray();
6157  // Save a constant array.
6158  ok = ok && w.jsonKey(UT_VoxelArrayJSON::getToken(
6160  if constexpr (tuple_size == 1)
6161  ok = ok && w.jsonValue(cval);
6162  else
6163  ok = ok && w.jsonUniformArray(tuple_size, cval.data());
6164  ok = ok && w.jsonEndArray();
6165  return ok;
6166  }
6167 
6168  if (shared_mem_owner)
6169  {
6170  SYS_SharedMemory *shm;
6171  shm = copyToSharedMemory(shared_mem_owner);
6172  if (shm)
6173  {
6174  ok = ok && w.jsonBeginArray();
6175  // Save a shared memory array.
6176  ok = ok && w.jsonKey(UT_VoxelArrayJSON::getToken(
6178  ok = ok && w.jsonString(shm->id());
6179  ok = ok && w.jsonEndArray();
6180  return ok;
6181  }
6182  // Fall back on raw write.
6183  }
6184 
6185 
6186  // Compressed tiles.
6187  ok = ok && w.jsonBeginArray();
6188  ok = ok && w.jsonKey(UT_VoxelArrayJSON::getToken(
6190  ok = ok && w.jsonBeginArray();
6191  ok = ok && w.jsonKey(UT_VoxelArrayJSON::getToken(
6193  version = 1;
6194  ok = ok && w.jsonValue(version);
6195 
6196  // Store list of compression types
6197  ok = ok && w.jsonKey(UT_VoxelArrayJSON::getToken(
6200 
6201  ok = ok && w.jsonKey(UT_VoxelArrayJSON::getToken(
6203  ok = ok && w.jsonBeginArray();
6204  int i, ntiles;
6205 
6206  ntiles = numTiles();
6207  for (i = 0; i < ntiles; i++)
6208  {
6209  ok = ok && myTiles[i].save(w);
6210  }
6211  ok = ok && w.jsonEndArray();
6212 
6213  ok = ok && w.jsonEndArray();
6214 
6215  ok = ok && w.jsonEndArray();
6216  return ok;
6217 }
6218 
6219 template <typename T>
6220 bool
6222 {
6223  constexpr exint tuple_size = UT_FixedVectorTraits<T>::TupleSize;
6224 
6225  T cval;
6226  int8 version;
6227  UT_WorkBuffer key;
6228  int key_id;
6229  bool array_error = false;
6230 
6231  delete mySharedMemView;
6232  mySharedMemView = 0;
6233 
6234  delete mySharedMem;
6235  mySharedMem = 0;
6236 
6237  if (!p.parseBeginArray(array_error) || array_error)
6238  return false;
6239 
6240  if (!p.parseString(key))
6241  return false;
6242  key_id = UT_VoxelArrayJSON::getArrayID(key.buffer());
6244  {
6245  if constexpr (tuple_size == 1)
6246  {
6247  if (!p.parseNumber(cval))
6248  return false;
6249  }
6250  else
6251  {
6252  if (!p.parseUniformArray(cval.data(), tuple_size))
6253  return false;
6254  }
6255 
6256  constant(cval);
6257  }
6258  else if (key_id == UT_VoxelArrayJSON::ARRAY_SHAREDARRAY)
6259  {
6260  UT_WorkBuffer shm_id;
6261  if (!p.parseString(shm_id))
6262  return false;
6263 
6264  if (!populateFromSharedMemory(shm_id.buffer()))
6265  {
6266  // If the shared memory disappears before we get a chance to read
6267  // it, don't abort, just set a constant value. That way Mantra has
6268  // a chance to recover. Not the most elegant solution but works for
6269  // now.
6270  T cval;
6271  cval = 0;
6272  constant(cval);
6273  }
6274  }
6275  else if (key_id == UT_VoxelArrayJSON::ARRAY_TILEDARRAY)
6276  {
6277  UT_JSONParser::traverser it, tile_it;
6278  UT_IntArray compressions;
6279  int i, ntiles;
6280 
6281 
6282  for (it = p.beginArray(); !it.atEnd(); ++it)
6283  {
6284  if (!it.getLowerKey(key))
6285  return false;
6286  switch (UT_VoxelArrayJSON::getArrayID(key.buffer()))
6287  {
6289  if (!p.parseNumber(version))
6290  return false;
6291  break;
6293  // Store list of compression types
6294  if (!UT_VoxelTile<T>::loadCompressionTypes(p, compressions))
6295  return false;
6296  break;
6297 
6299  ntiles = numTiles();
6300  for (tile_it = p.beginArray(), i = 0; !tile_it.atEnd();
6301  ++tile_it, ++i)
6302  {
6303  if (i < ntiles)
6304  {
6305  if (!myTiles[i].load(p, compressions))
6306  return false;
6307  }
6308  else
6309  {
6310  UT_VoxelTile<T> dummy_tile;
6311  dummy_tile.setRes(TILESIZE, TILESIZE, TILESIZE);
6312  if (!dummy_tile.load(p, compressions))
6313  return false;
6314  }
6315  }
6316  if (i != ntiles)
6317  return false;
6318  break;
6319  default:
6320  p.addWarning("Unexpected key for voxel data: %s",
6321  key.buffer());
6322  if (!p.skipNextObject())
6323  return false;
6324  }
6325  }
6326  }
6327  else
6328  {
6329  p.addWarning("Unexpected voxel key: %s\n", key.buffer());
6330  p.skipNextObject();
6331  return false;
6332  }
6333 
6334  if (!p.parseEndArray(array_error) || array_error)
6335  return false;
6336 
6337  return true;
6338 }
6339 
6340 template<typename T>
6342 UT_VoxelArray<T>::copyToSharedMemory(const char *shared_mem_owner) const
6343 {
6345 
6346  int ntiles;
6347 
6348  ntiles = numTiles();
6349 
6350  // Tally up the size we need.
6351  exint total_mem_size;
6352 
6353  total_mem_size = sizeof(exint); // Tile count.
6354 
6355  UT_Array<exint> tile_sizes;
6356  UT_Array<exint> next_block_offsets;
6357 
6358  for (int i = 0; i < ntiles; i++)
6359  {
6360  exint tile_size, block_size;
6361  if (myTiles[i].isConstant())
6362  tile_size = sizeof(T);
6363  else
6364  tile_size = myTiles[i].getDataLength();
6365 
6366  tile_sizes.append(tile_size);
6367 
6368  block_size = SYSroundUpToMultipleOf<exint>(tile_size, sizeof(exint));
6369  block_size += sizeof(exint) * 2; // Offset + compression type
6370 
6371  if (i < (ntiles-1))
6372  next_block_offsets.append(block_size / sizeof(exint));
6373  else
6374  next_block_offsets.append(0);
6375 
6376  total_mem_size += block_size;
6377  }
6378 
6379  // Start with an empty shared memory.
6380  SYS_SharedMemory *shared_mem;
6381  UT_WorkBuffer shared_mem_key;
6382 
6383  shared_mem_key.sprintf("%s:%p", shared_mem_owner, this);
6384 
6385  shared_mem = shmgr.get(shared_mem_key.buffer());
6386 
6387  // Does the existing block have the same number of tiles and same
6388  // offsets? In that case we don't reset the shared memory, just write
6389  // the tiles out again. There's very little adverse effect in changing
6390  // the voxel tile data.
6391  bool same_meta_data = false;
6392  if (shared_mem->size() >= sizeof(exint))
6393  {
6394  same_meta_data = true;
6395  {
6396  SYS_SharedMemoryView sv_tc(*shared_mem, 0, sizeof(exint));
6397 
6398  exint *ptr_ds = (exint *)sv_tc.data();
6399  if (*ptr_ds != ntiles)
6400  same_meta_data = false;
6401  }
6402 
6403  if (same_meta_data)
6404  {
6405  // Check if the offsets + compression type is the same.
6406  exint offset = sizeof(exint);
6407  for (int i = 0; i < ntiles; i++)
6408  {
6409  SYS_SharedMemoryView sv_tile(*shared_mem, offset,
6410  sizeof(exint) * 2);
6411 
6412  exint *ptr_tile = (exint *)sv_tile.data();
6413  if (ptr_tile[0] != next_block_offsets(i) ||
6414  ptr_tile[1] != (exint)myTiles[i].myCompressionType)
6415  {
6416  same_meta_data = false;
6417  break;
6418  }
6419  offset += next_block_offsets(i) * sizeof(exint);
6420  }
6421  }
6422  }
6423 
6424  if (!same_meta_data)
6425  {
6426  if (!shared_mem->reset(total_mem_size) ||
6427  shared_mem->size() != total_mem_size)
6428  {
6429  return nullptr;
6430  }
6431  }
6432 
6433  {
6434  SYS_SharedMemoryView sv_tc(*shared_mem, 0, sizeof(exint));
6435 
6436  exint *ptr_ds = (exint *)sv_tc.data();
6437  *ptr_ds = ntiles;
6438  }
6439 
6440  exint offset = sizeof(exint);
6441  for (int i = 0; i < ntiles; i++)
6442  {
6443  SYS_SharedMemoryView sv_tile(*shared_mem, offset,
6444  sizeof(exint) * 2 + tile_sizes(i));
6445 
6446  exint *ptr_tile = (exint *)sv_tile.data();
6447 
6448  // Offset to next tile in exint units.
6449  ptr_tile[0] = next_block_offsets(i);
6450  ptr_tile[1] = myTiles[i].myCompressionType;
6451 
6452  ::memcpy(&ptr_tile[2], myTiles[i].rawData(), tile_sizes(i));
6453 
6454  offset += ptr_tile[0] * sizeof(exint);
6455  }
6456 
6457  return shared_mem;
6458 }
6459 
6460 template<typename T>
6461 bool
6463 {
6464  mySharedMem = new SYS_SharedMemory(id, /*read_only=*/true);
6465  if (!mySharedMem->size())
6466  {
6467  delete mySharedMem;
6468  mySharedMem = 0;
6469  return false;
6470  }
6471 
6472  exint ntiles;
6473  {
6474  SYS_SharedMemoryView sv_tc(*mySharedMem, 0, sizeof(exint));
6475  ntiles = *(const exint *)sv_tc.data();
6476  }
6477  if (ntiles != numTiles())
6478  {
6479  delete mySharedMem;
6480  mySharedMem = 0;
6481  return false;
6482  }
6483 
6484  mySharedMemView = new SYS_SharedMemoryView(*mySharedMem, sizeof(exint));
6485 
6486  exint *data = (exint *)mySharedMemView->data();
6487 
6488  for (int i = 0; i < ntiles; i++)
6489  {
6490  exint offset = data[0];
6491  int8 ctype = (int8)data[1];
6492 
6493  myTiles[i].setForeignData(&data[2], ctype);
6494 
6495  data += offset;
6496  }
6497 
6498  return true;
6499 }
6500 
6501 
6502 
6503 
6504 //
6505 // UT_VoxelMipMap functions
6506 //
6507 template <typename T>
6509 {
6510  initializePrivate();
6511 }
6512 
6513 template <typename T>
6515 {
6516  destroyPrivate();
6517 }
6518 
6519 template <typename T>
6521 {
6522  initializePrivate();
6523 
6524  *this = src;
6525 }
6526 
6527 template <typename T>
6528 const UT_VoxelMipMap<T> &
6530 {
6532  int level, i;
6533 
6534  if (&src == this)
6535  return *this;
6536 
6537  destroyPrivate();
6538 
6539  // We do a deep copy. We have learned from UT_String that
6540  // shallow copies will destroy us in the end.
6541  myOwnBase = true;
6542  myBaseLevel = new UT_VoxelArray<T>;
6543  *myBaseLevel = *src.myBaseLevel;
6544 
6545  myNumLevels = src.myNumLevels;
6546 
6547  for (i = 0; i < src.myLevels.entries(); i++)
6548  {
6549  levels = new UT_VoxelArray<T> *[myNumLevels];
6550  myLevels.append(levels);
6551 
6552  for (level = 0; level < myNumLevels; level++)
6553  {
6554  levels[level] = new UT_VoxelArray<T>;
6555  *levels[level] = *src.myLevels(i)[level];
6556  }
6557  }
6558  return *this;
6559 }
6560 
6561 template <typename T>
6562 void
6564  mipmaptype function)
6565 {
6566  UT_Array<mipmaptype> functions;
6567 
6568  functions.append(function);
6569  build(baselevel, functions);
6570 }
6571 
6572 template <typename T>
6573 void
6575  const UT_Array<mipmaptype> &functions)
6576 {
6577  destroyPrivate();
6578 
6579  // Setup our baselevel.
6580  myBaseLevel = baselevel;
6581  myOwnBase = false;
6582 
6583  // Now build from bottom up. First, calculate the number
6584  // of levels we'll need.
6585  myNumLevels = 0;
6586  int maxres, level;
6587 
6588  maxres = SYSmax(myBaseLevel->getXRes(), myBaseLevel->getYRes());
6589  maxres = SYSmax(maxres, myBaseLevel->getZRes());
6590 
6591  // std::cerr << "Max res " << maxres << std::endl;
6592 
6593  while (maxres > 1)
6594  {
6595  myNumLevels++;
6596  maxres++;
6597  maxres >>= 1;
6598  }
6599 
6600  // std::cerr << "Levels: " << myNumLevels << std::endl;
6601 
6602  // Create our desired voxel array list.
6603  for (int i = 0; i < functions.entries(); i++)
6604  {
6605  mipmaptype function = functions(i);
6606 
6607  UT_VoxelArray<T> **levels = new UT_VoxelArray<T> *[myNumLevels];
6608  myLevels.append(levels);
6609 
6610  UT_VoxelArray<T> *lastlevel = myBaseLevel;
6611  for (level = myNumLevels-1; level >= 0; level--)
6612  {
6613  // Find our resolutions.
6614  int xres = (lastlevel->getXRes() + 1) >> 1;
6615  int yres = (lastlevel->getYRes() + 1) >> 1;
6616  int zres = (lastlevel->getZRes() + 1) >> 1;
6617 
6618  levels[level] = new UT_VoxelArray<T>;
6619  levels[level]->size(xres, yres, zres);
6620 
6621  downsample(*levels[level], *lastlevel, function);
6622 
6623  lastlevel = levels[level];
6624  }
6625  }
6626 }
6627 
6628 template <typename T>
6629 void
6632  const UT_VoxelArray<T> &src,
6633  mipmaptype function,
6634  const UT_JobInfo &info)
6635 {
6636  UT_VoxelArrayIterator<T> vit(&dst);
6637 
6638  vit.splitByTile(info);
6639  vit.setCompressOnExit(true);
6640 
6641  for (vit.rewind(); !vit.atEnd(); vit.advance())
6642  {
6643  int x = 2*vit.x();
6644  int y = 2*vit.y();
6645  int z = 2*vit.z();
6646  T sam[8];
6647 
6648  if (src.extractSample(x, y, z, sam))
6649  {
6650  vit.setValue(sam[0]);
6651  }
6652  else
6653  {
6654  vit.setValue(
6655  mixValues(
6656  mixValues(
6657  mixValues(sam[0], sam[1], function),
6658  mixValues(sam[2], sam[3], function), function),
6659  mixValues(
6660  mixValues(sam[4], sam[5], function),
6661  mixValues(sam[6], sam[7], function), function),
6662  function));
6663  }
6664  }
6665 }
6666 
6667 
6668 template <typename T>
6669 int64
6671 {
6672  int64 mem = inclusive ? sizeof(*this) : 0;
6673 
6674  mem += myLevels.getMemoryUsage(false);
6675  mem += myLevels.entries() * myNumLevels * sizeof(UT_VoxelArray<T>*);
6676  for (exint j = 0; j < myLevels.entries(); j++)
6677  {
6678  for (int i = 0; i < myNumLevels; i++)
6679  mem += myLevels(j)[i]->getMemoryUsage(true);
6680  }
6681 
6682  return mem;
6683 }
6684 
6685 template <typename T>
6686 void
6687 UT_VoxelMipMap<T>::traverseTopDown(Callback function, void *data) const
6688 {
6689  doTraverse(0, 0, 0, 0, function, data);
6690 }
6691 
6692 template <typename T>
6693 void
6694 UT_VoxelMipMap<T>::doTraverse(int x, int y, int z, int level,
6695  Callback function, void *data) const
6696 {
6697  UT_StackBuffer<T> tval(myLevels.entries());
6698  bool isfinal;
6699  UT_VoxelArray<T> *vox;
6700  int shift;
6701 
6702  if (level == myNumLevels)
6703  {
6704  isfinal = true;
6705  vox = myBaseLevel;
6706  tval[0] = (*vox)(x, y, z);
6707  for (int i = 1; i < myLevels.entries(); i++)
6708  tval[i] = tval[0];
6709  }
6710  else
6711  {
6712  isfinal = false;
6713  for (int i = 0; i < myLevels.entries(); i++)
6714  {
6715  vox = myLevels(i)[level];
6716  tval[i] = (*vox)(x, y, z);
6717  }
6718  }
6719 
6720  shift = myNumLevels - level;
6721 
6722  UT_BoundingBox box;
6723 
6724  box.initBounds(SYSmin(x << shift, myBaseLevel->getXRes()),
6725  SYSmin(y << shift, myBaseLevel->getYRes()),
6726  SYSmin(z << shift, myBaseLevel->getZRes()));
6727  box.enlargeBounds(SYSmin((x+1) << shift, myBaseLevel->getXRes()),
6728  SYSmin((y+1) << shift, myBaseLevel->getYRes()),
6729  SYSmin((z+1) << shift, myBaseLevel->getZRes()));
6730 
6731  if (!function(tval, box, isfinal, data))
6732  {
6733  // Function asked for an early exit.
6734  // Give it.
6735  return;
6736  }
6737 
6738  // We now want to proceed to the next level.
6739  if (isfinal)
6740  return;
6741 
6742  level++;
6743  shift--;
6744  x <<= 1;
6745  y <<= 1;
6746  z <<= 1;
6747 
6748  bool xinc, yinc, zinc;
6749 
6750  // Determine which of our children are valid.
6751  if ( ((x+1) << shift) < myBaseLevel->getXRes() )
6752  xinc = true;
6753  else
6754  xinc = false;
6755  if ( ((y+1) << shift) < myBaseLevel->getYRes() )
6756  yinc = true;
6757  else
6758  yinc = false;
6759  if ( ((z+1) << shift) < myBaseLevel->getZRes() )
6760  zinc = true;
6761  else
6762  zinc = false;
6763 
6764  //
6765  // Traverse all valid children. Note that this is deliberately a gray
6766  // order traversal for full nodes.
6767  //
6768 
6769  doTraverse(x, y, z, level, function, data);
6770 
6771  if (yinc)
6772  {
6773  doTraverse(x, y+1, z, level, function, data);
6774  if (zinc)
6775  doTraverse(x, y+1, z+1, level, function, data);
6776  }
6777  if (zinc)
6778  doTraverse(x, y, z+1, level, function, data);
6779 
6780  if (xinc)
6781  {
6782  if (zinc)
6783  doTraverse(x+1, y, z+1, level, function, data);
6784  doTraverse(x+1, y, z, level, function, data);
6785  if (yinc)
6786  {
6787  doTraverse(x+1, y+1, z, level, function, data);
6788  if (zinc)
6789  doTraverse(x+1, y+1, z+1, level, function, data);
6790  }
6791  }
6792 }
6793 
6794 template <typename T>
6795 template <typename OP>
6796 void
6798 {
6799  doTraverse(0, 0, 0, numLevels()-1, op);
6800 }
6801 
6802 template <typename T>
6803 template <typename OP>
6804 void
6805 UT_VoxelMipMap<T>::doTraverse(int x, int y, int z, int level,
6806  OP &op) const
6807 {
6808  int shift;
6809 
6810  shift = level;
6811 
6812  UT_BoundingBoxI box;
6813 
6814  box.initBounds(SYSmin(x << shift, myBaseLevel->getXRes()),
6815  SYSmin(y << shift, myBaseLevel->getYRes()),
6816  SYSmin(z << shift, myBaseLevel->getZRes()));
6817  box.enlargeBounds(SYSmin((x+1) << shift, myBaseLevel->getXRes()),
6818  SYSmin((y+1) << shift, myBaseLevel->getYRes()),
6819  SYSmin((z+1) << shift, myBaseLevel->getZRes()));
6820 
6821  if (!op(box, level))
6822  {
6823  // Function asked for an early exit.
6824  // Give it.
6825  return;
6826  }
6827 
6828  level--;
6829  // We now want to proceed to the next level.
6830  if (level < 0)
6831  return;
6832 
6833  x <<= 1;
6834  y <<= 1;
6835  z <<= 1;
6836 
6837  bool xinc, yinc, zinc;
6838 
6839  // Determine which of our children are valid.
6840  if ( ((x+1) << level) < myBaseLevel->getXRes() )
6841  xinc = true;
6842  else
6843  xinc = false;
6844  if ( ((y+1) << level) < myBaseLevel->getYRes() )
6845  yinc = true;
6846  else
6847  yinc = false;
6848  if ( ((z+1) << level) < myBaseLevel->getZRes() )
6849  zinc = true;
6850  else
6851  zinc = false;
6852 
6853  //
6854  // Traverse all valid children. Note that this is deliberately a gray
6855  // order traversal for full nodes.
6856  //
6857 
6858  doTraverse(x, y, z, level, op);
6859 
6860  if (yinc)
6861  {
6862  doTraverse(x, y+1, z, level, op);
6863  if (zinc)
6864  doTraverse(x, y+1, z+1, level, op);
6865  }
6866  if (zinc)
6867  doTraverse(x, y, z+1, level, op);
6868 
6869  if (xinc)
6870  {
6871  if (zinc)
6872  doTraverse(x+1, y, z+1, level, op);
6873  doTraverse(x+1, y, z, level, op);
6874  if (yinc)
6875  {
6876  doTraverse(x+1, y+1, z, level, op);
6877  if (zinc)
6878  doTraverse(x+1, y+1, z+1, level, op);
6879  }
6880  }
6881 }
6882 
6883 template <typename T>
6884 template <typename OP>
6885 void
6887 {
6888  doTraverseSorted(0, 0, 0, numLevels()-1, op);
6889 }
6890 
6892 {
6893 public:
6894  float value;
6895  int key;
6896 };
6897 
6899 {
6900  return lhs.value < rhs.value;
6901 }
6902 
6903 template <typename T>
6904 template <typename OP>
6905 void
6906 UT_VoxelMipMap<T>::doTraverseSorted(int x, int y, int z, int level,
6907  OP &op) const
6908 {
6909  UT_BoundingBoxI box;
6910 
6911  box.initBounds(SYSmin(x << level, myBaseLevel->getXRes()),
6912  SYSmin(y << level, myBaseLevel->getYRes()),
6913  SYSmin(z << level, myBaseLevel->getZRes()));
6914  box.enlargeBounds(SYSmin((x+1) << level, myBaseLevel->getXRes()),
6915  SYSmin((y+1) << level, myBaseLevel->getYRes()),
6916  SYSmin((z+1) << level, myBaseLevel->getZRes()));
6917 
6918  if (!op(box, level))
6919  {
6920  // Function asked for an early exit.
6921  // Give it.
6922  return;
6923  }
6924 
6925  level--;
6926  // We now want to proceed to the next level.
6927  if (level < 0)
6928  return;
6929 
6930  x <<= 1;
6931  y <<= 1;
6932  z <<= 1;
6933 
6934  bool xinc, yinc, zinc;
6935 
6936  // Determine which of our children are valid.
6937  if ( ((x+1) << level) < myBaseLevel->getXRes() )
6938  xinc = true;
6939  else
6940  xinc = false;
6941  if ( ((y+1) << level) < myBaseLevel->getYRes() )
6942  yinc = true;
6943  else
6944  yinc = false;
6945  if ( ((z+1) << level) < myBaseLevel->getZRes() )
6946  zinc = true;
6947  else
6948  zinc = false;
6949 
6950  UT_BoundingBoxI boxes[8];
6951  int numboxes = 0;
6952 
6953  // Build all of our bounding boxes.
6954  for (int dz = 0; dz < 2; dz++)
6955  {
6956  if (dz && !zinc)
6957  continue;
6958  for (int dy = 0; dy < 2; dy++)
6959  {
6960  if (dy && !yinc)
6961  continue;
6962  for (int dx = 0; dx < 2; dx++)
6963  {
6964  if (dx && !xinc)
6965  continue;
6966  boxes[numboxes].initBounds(
6967  SYSmin((x+dx) << level, myBaseLevel->getXRes()),
6968  SYSmin((y+dy) << level, myBaseLevel->getYRes()),
6969  SYSmin((z+dz) << level, myBaseLevel->getZRes()));
6970  boxes[numboxes].enlargeBounds(
6971  SYSmin((x+dx+1) << level, myBaseLevel->getXRes()),
6972  SYSmin((y+dy+1) << level, myBaseLevel->getYRes()),
6973  SYSmin((z+dz+1) << level, myBaseLevel->getZRes()));
6974  numboxes++;
6975  }
6976  }
6977  }
6978 
6979  ut_VoxelMipMapSortCompare sortstats[8];
6980  for (int i = 0; i < numboxes; i++)
6981  {
6982  sortstats[i].value = op.sortValue(boxes[i], level);
6983  sortstats[i].key = i;
6984  }
6985  std::stable_sort(sortstats, &sortstats[numboxes]);
6986 
6987  for (int i = 0; i < numboxes; i++)
6988  {
6989  int whichbox = sortstats[i].key;
6990  doTraverseSorted(boxes[whichbox](0,0)>>level, boxes[whichbox](1,0)>>level, boxes[whichbox](2,0)>>level, level, op);
6991  }
6992 }
6993 
6994 template <typename T>
6995 void
6997 {
6998  myNumLevels = 0;
6999  myBaseLevel = 0;
7000  myOwnBase = false;
7001 }
7002 
7003 template <typename T>
7004 void
7006 {
7007  for (exint i = 0; i < myLevels.entries(); i++)
7008  {
7009  for (exint level = 0; level < myNumLevels; level++)
7010  delete myLevels(i)[level];
7011  delete [] myLevels(i);
7012  }
7013  myLevels.entries(0);
7014 
7015  if (myOwnBase)
7016  delete myBaseLevel;
7017 
7018  initializePrivate();
7019 }
7020 
7021 //
7022 // UT_VoxelArrayIterator implementation
7023 //
7024 template <typename T>
7026 {
7027  myArray = 0;
7028  myHandle.resetHandle();
7029  myCurTile = -1;
7030  myShouldCompressOnExit = false;
7031  myUseTileList = false;
7032  myJobInfo = 0;
7033  myInterrupt = 0;
7034 }
7035 
7036 template <typename T>
7038 {
7039  myShouldCompressOnExit = false;
7040  myUseTileList = false;
7041  myJobInfo = 0;
7042  setArray(vox);
7043  myInterrupt = 0;
7044 }
7045 
7046 template <typename T>
7048 {
7049  myShouldCompressOnExit = false;
7050  myUseTileList = false;
7051  myJobInfo = 0;
7052  setHandle(handle);
7053  myInterrupt = 0;
7054 }
7055 
7056 template <typename T>
7058 {
7059 }
7060 
7061 template <typename T>
7062 void
7064 {
7065  int numtiles;
7066  fpreal tileperrange;
7067 
7068  // Must already have been set.
7069  UT_ASSERT(myArray);
7070  if (!myArray)
7071  return;
7072 
7073  // Divide our tiles in to num ranges groups.
7074  numtiles = myArray->numTiles();
7075 
7076  // If we are using a tile list, use it instead
7077  if (myUseTileList)
7078  numtiles = myTileList.entries();
7079 
7080  // Trivial case.
7081  if (!numtiles)
7082  {
7083  myTileStart = 0;
7084  myTileEnd = 0;
7085  return;
7086  }
7087 
7088  // Sanity check idx.
7089  if (idx < 0 || idx >= numranges)
7090  {
7091  UT_ASSERT(!"Idx out of bounds");
7092  myTileStart = -1;
7093  myTileEnd = -1;
7094  return;
7095  }
7096 
7097  // Sanity test numranges
7098  if (numranges < 1)
7099  {
7100  UT_ASSERT(!"Invalid range count!");
7101  numranges = 1;
7102  }
7103 
7104  // Compute tile per range.
7105  // We use floating point so, if you have 15 tiles and
7106  // 16 processors, you don't decide to do all 15
7107  // tiles on one processor.
7108  tileperrange = (fpreal)numtiles / (fpreal)numranges;
7109 
7110  myTileStart = (int) SYSfloor(idx * tileperrange);
7111  myTileEnd = (int) SYSfloor((idx+1) * tileperrange);
7112 
7113  // Special case to ensure we always get the last tile,
7114  // despite the evils of floating point.
7115  if (idx == numranges-1)
7116  myTileEnd = numtiles;
7117 
7118  UT_ASSERT(myTileStart >= 0);
7119  UT_ASSERT(myTileEnd <= numtiles);
7120 }
7121 
7122 template <typename T>
7123 void
7125 {
7126  // Must already have been set.
7127  UT_ASSERT(myArray);
7128  if (!myArray)
7129  return;
7130 
7131  // If there is one thread, don't bother
7132  if (info.numJobs() == 1)
7133  myJobInfo = 0;
7134  else
7135  myJobInfo = &info;
7136 }
7137 
7138 template <typename T>
7139 void
7141 {
7142  UT_Vector3 pmin, pmax, vmin, vmax;
7143 
7144  pmin = bbox.minvec();
7145  pmax = bbox.maxvec();
7146 
7147  pmin.x() = SYSmax(pmin.x(), 0.0f);
7148  pmin.y() = SYSmax(pmin.y(), 0.0f);
7149  pmin.z() = SYSmax(pmin.z(), 0.0f);
7150  pmax.x() = SYSmin(pmax.x(), 1.0f);
7151  pmax.y() = SYSmin(pmax.y(), 1.0f);
7152  pmax.z() = SYSmin(pmax.z(), 1.0f);
7153 
7154  myArray->posToIndex(pmin, vmin);
7155  myArray->posToIndex(pmax, vmax);
7156 
7157  restrictToBBox(SYSfloor(vmin.x()), SYSceil(vmax.x()),
7158  SYSfloor(vmin.y()), SYSceil(vmax.y()),
7159  SYSfloor(vmin.z()), SYSceil(vmax.z()));
7160 }
7161 
7162 template <typename T>
7163 void
7165  int ymin, int ymax,
7166  int zmin, int zmax)
7167 {
7168  int xres, yres, zres, x, y, z;
7169 
7170  xres = myArray->getXRes();
7171  yres = myArray->getYRes();
7172  zres = myArray->getZRes();
7173 
7174  myTileList.entries(0);
7175  myUseTileList = true;
7176  // Make sure we have any tiles that are valid.
7177  if (xmin < xres && xmax >= 0 &&
7178  ymin < yres && ymax >= 0 &&
7179  zmin < zres && zmax >= 0)
7180  {
7181  // Clamp into valid range.
7182  myArray->clampIndex(xmin, ymin, zmin);
7183  myArray->clampIndex(xmax, ymax, zmax);
7184 
7185  // Convert to tiles...
7186  xmin >>= TILEBITS;
7187  ymin >>= TILEBITS;
7188  zmin >>= TILEBITS;
7189  xmax >>= TILEBITS;
7190  ymax >>= TILEBITS;
7191  zmax >>= TILEBITS;
7192 
7193  // Check if we are all accounted for.
7194  if (myArray->numTiles() == (xmax-xmin+1)*(ymax-ymin+1)*(zmax-zmin+1))
7195  {
7196  UT_ASSERT(xmin == 0 && ymin == 0 && zmin == 0);
7197  // No need for a tile list, just run everything!
7198  myUseTileList = false;
7199  }
7200  else
7201  {
7202  // Iterate over all active tiles, adding to our list.
7203  for (z = zmin; z <= zmax; z++)
7204  {
7205  for (y = ymin; y <= ymax; y++)
7206  {
7207  for (x = xmin; x <= xmax; x++)
7208  {
7209  myTileList.append(myArray->xyzTileToLinear(x, y, z));
7210  }
7211  }
7212  }
7213  }
7214  }
7215 
7216  // Recompute our ranges.
7217  myCurTile = -1;
7218  setPartialRange(0, 1);
7219 }
7220 
7221 
7222 template <typename T>
7223 void
7225 {
7226  // Ensure we have at least one tile in each direction
7227  if (!myArray ||
7228  !myArray->getRes(0) || !myArray->getRes(1) || !myArray->getRes(2))
7229  {
7230  myCurTile = -1;
7231  return;
7232  }
7233 
7234  if (myUseTileList)
7235  {
7236  if (myJobInfo)
7237  myCurTileListIdx = myJobInfo->nextTask();
7238  else
7239  myCurTileListIdx = myTileStart;
7240  if (myCurTileListIdx < 0 || myCurTileListIdx >= myTileEnd)
7241  {
7242  myCurTile = -1;
7243  return;
7244  }
7245  myCurTile = myTileList(myCurTileListIdx);
7246  }
7247  else
7248  {
7249  if (myJobInfo)
7250  myCurTile = myJobInfo->nextTask();
7251  else
7252  myCurTile = myTileStart;
7253  // If this is an empty range, we quit right away.
7254  if (myCurTile < 0 || myCurTile >= myTileEnd)
7255  {
7256  myCurTile = -1;
7257  return;
7258  }
7259  }
7260 
7261  UT_VoxelTile<T> *tile;
7262 
7263  tile = myArray->getLinearTile(myCurTile);
7264 
7265  // Get the tile position
7266  if (myCurTile)
7267  {
7268  myArray->linearTileToXYZ(myCurTile,
7269  myTilePos[0], myTilePos[1], myTilePos[2]);
7270  myPos[0] = TILESIZE * myTilePos[0];
7271  myPos[1] = TILESIZE * myTilePos[1];
7272  myPos[2] = TILESIZE * myTilePos[2];
7273  }
7274  else
7275  {
7276  myTilePos[0] = 0;
7277  myTilePos[1] = 0;
7278  myTilePos[2] = 0;
7279  myPos[0] = 0;
7280  myPos[1] = 0;
7281  myPos[2] = 0;
7282  }
7283 
7284  myTileLocalPos[0] = 0;
7285  myTileLocalPos[1] = 0;
7286  myTileLocalPos[2] = 0;
7287 
7288  myTileSize[0] = tile->xres();
7289  myTileSize[1] = tile->yres();
7290  myTileSize[2] = tile->zres();
7291 }
7292 
7293 template <typename T>
7294 void
7296 {
7297  if (myInterrupt && myInterrupt->opInterrupt())
7298  {
7299  myCurTile = -1;
7300  return;
7301  }
7302  if (myUseTileList)
7303  {
7304  if (getCompressOnExit())
7305  {
7306  // Verify our last tile was a legitimate one.
7307  if (myCurTile >= 0 && myCurTileListIdx < myTileEnd)
7308  {
7309  myArray->getLinearTile(myCurTile)->tryCompress(myArray->getCompressionOptions());
7310  }
7311  }
7312 
7313  // Advance our myCurTileListIdx and rebuild from
7314  if (myJobInfo)
7315  myCurTileListIdx = myJobInfo->nextTask();
7316  else
7317  myCurTileListIdx++;
7318  if (myCurTileListIdx >= myTileEnd)
7319  {
7320  myCurTile = -1;
7321  return;
7322  }
7323 
7324  myCurTile = myTileList(myCurTileListIdx);
7325 
7326  myArray->linearTileToXYZ(myCurTile,
7327  myTilePos[0], myTilePos[1], myTilePos[2]);
7328 
7329  UT_VoxelTile<T> *tile;
7330 
7331  tile = myArray->getLinearTile(myCurTile);
7332  myTileLocalPos[0] = 0;
7333  myTileLocalPos[1] = 0;
7334  myTileLocalPos[2] = 0;
7335  myTileSize[0] = tile->xres();
7336  myTileSize[1] = tile->yres();
7337  myTileSize[2] = tile->zres();
7338 
7339  myPos[0] = TILESIZE * myTilePos[0];
7340  myPos[1] = TILESIZE * myTilePos[1];
7341  myPos[2] = TILESIZE * myTilePos[2];
7342  return;
7343  }
7344 
7345  // If requested, compress our last tile.
7346  if (getCompressOnExit())
7347  {
7348  // Verify our last tile was a legitimate one.
7349  if (myCurTile >= 0 && myCurTile < myTileEnd)
7350  {
7351  myArray->getLinearTile(myCurTile)->tryCompress(myArray->getCompressionOptions());
7352  }
7353  }
7354 
7355  if (myJobInfo)
7356  {
7357  myCurTile = myJobInfo->nextTask();
7358  if (myCurTile >= myTileEnd)
7359  {
7360  myCurTile = -1;
7361  return;
7362  }
7363  myArray->linearTileToXYZ(myCurTile,
7364  myTilePos[0], myTilePos[1], myTilePos[2]);
7365  }
7366  else
7367  {
7368  // Advance our myTilePos, then rebuild everything from there.
7369  myTilePos[0]++;
7370  if (myTilePos[0] >= myArray->getTileRes(0))
7371  {
7372  myTilePos[0] = 0;
7373  myTilePos[1]++;
7374  if (myTilePos[1] >= myArray->getTileRes(1))
7375  {
7376  myTilePos[1] = 0;
7377  myTilePos[2]++;
7378  if (myTilePos[2] >= myArray->getTileRes(2))
7379  {
7380  // All done!
7381  myCurTile = -1;
7382  return;
7383  }
7384  }
7385  }
7386  myCurTile = myArray->xyzTileToLinear(myTilePos[0], myTilePos[1], myTilePos[2]);
7387  }
7388 
7389  UT_VoxelTile<T> *tile;
7390 
7391  // Rebuild our settings from this tile.
7392 
7393  // See if we hit our end.
7394  if (myCurTile >= myTileEnd)
7395  {
7396  myCurTile = -1;
7397  return;
7398  }
7399 
7400  tile = myArray->getLinearTile(myCurTile);
7401  myTileLocalPos[0] = 0;
7402  myTileLocalPos[1] = 0;
7403  myTileLocalPos[2] = 0;
7404  myTileSize[0] = tile->xres();
7405  myTileSize[1] = tile->yres();
7406  myTileSize[2] = tile->zres();
7407 
7408  myPos[0] = TILESIZE * myTilePos[0];
7409  myPos[1] = TILESIZE * myTilePos[1];
7410  myPos[2] = TILESIZE * myTilePos[2];
7411 }
7412 
7413 
7414 template <typename T>
7415 void
7417 {
7418  myTileLocalPos[0] = myTileSize[0];
7419  myTileLocalPos[1] = myTileSize[1];
7420  myTileLocalPos[2] = myTileSize[2];
7421 }
7422 
7423 template <typename T>
7424 template <typename OP>
7425 void
7427 {
7428  rewind();
7429 
7430  while (!atEnd())
7431  {
7432  UT_VoxelTile<T> *tile;
7433 
7434  tile = myArray->getLinearTile(myCurTile);
7435 
7436  if (!tile->isSimpleCompression())
7437  tile->uncompress();
7438 
7439  if (tile->isConstant())
7440  {
7441  T val;
7442 
7443  val = tile->rawData()[0];
7444 
7445  val = op(val, a);
7446 
7447  tile->rawData()[0] = val;
7448  }
7449  else
7450  {
7451  T *val;
7452 
7453  val = tile->rawData();
7454 
7455  int n = tile->numVoxels();
7456  for (int i = 0; i < n; i++)
7457  {
7458  val[i] = op(val[i], a);
7459  }
7460  }
7461 
7462  advanceTile();
7463  }
7464 }
7465 
7466 template <typename T>
7467 template <typename OP, typename S>
7468 void
7470  const UT_VoxelArray<S> &a)
7471 {
7472  UT_ASSERT(myArray->isMatching(a));
7473 
7474  rewind();
7475 
7476  while (!atEnd())
7477  {
7478  UT_VoxelTile<S> *atile;
7479  UT_VoxelTile<T> *tile;
7480 
7481  tile = myArray->getLinearTile(myCurTile);
7482  atile = a.getLinearTile(myCurTile);
7483 
7484  if (!atile->isSimpleCompression())
7485  {
7486  // Have to use getValue...
7487  for (int z = 0; z < tile->zres(); z++)
7488  for (int y = 0; y < tile->yres(); y++)
7489  for (int x = 0; x < tile->xres(); x++)
7490  {
7491  S aval;
7492  T val;
7493 
7494  val = tile->operator()(x, y, z);
7495  aval = atile->operator()(x, y, z);
7496 
7497  val = op(val, aval);
7498 
7499  tile->setValue(x, y, z, val);
7500  }
7501  }
7502  else
7503  {
7504  int ainc = atile->isConstant() ? 0 : 1;
7505 
7506  // Check if the incoming tile is constant and is a no-op
7507  // If so, we do not want to decompress!
7508  if (!ainc)
7509  {
7510  S aval;
7511  aval = atile->rawData()[0];
7512  if (op.isNoop(aval))
7513  {
7514  advanceTile();
7515  continue;
7516  }
7517  }
7518 
7519  if (!tile->isSimpleCompression())
7520  tile->uncompress();
7521 
7522  if (tile->isConstant())
7523  {
7524  if (!ainc)
7525  {
7526  // Woohoo! Too simple!
7527  S aval;
7528  T val;
7529 
7530  val = tile->rawData()[0];
7531  aval = atile->rawData()[0];
7532 
7533  val = op(val, aval);
7534 
7535  tile->rawData()[0] = val;
7536  }
7537  else
7538  {
7539  tile->uncompress();
7540  }
7541  }
7542 
7543  // In case we uncompressed ourself above...
7544  // we have to test again
7545  if (!tile->isConstant())
7546  {
7547  const S *aval;
7548  T *val;
7549 
7550  val = tile->rawData();
7551  aval = atile->rawData();
7552  ainc = atile->isConstant() ? 0 : 1;
7553 
7554  int n = tile->numVoxels();
7555  for (int i = 0; i < n; i++)
7556  {
7557  val[i] = op(val[i], *aval);
7558  aval += ainc;
7559  }
7560  }
7561  }
7562 
7563  advanceTile();
7564  }
7565 }
7566 
7567 template <typename T>
7568 template <typename OP>
7569 void
7571 {
7572  rewind();
7573 
7574  if (op.isNoop(a))
7575  {
7576  while (!atEnd())
7577  advanceTile();
7578  return;
7579  }
7580 
7581  applyOperation(op, a);
7582 }
7583 
7584 template<int OPERANDS, bool USE_SELF,
7585  typename T, typename OP, typename S, typename R, typename Q>
7586 static inline T
7587 conditionalCallOperator(const OP& op, const T& a0, const S& a1, const R& a2, const Q& a3)
7588 {
7589  // Call the correct () operator, depending on how many operands there are.
7590  T val;
7591  if constexpr (OPERANDS == 0 && USE_SELF)
7592  val = op(a0);
7593  else if constexpr (OPERANDS == 1 && !USE_SELF)
7594  val = op(a1);
7595  else if constexpr (OPERANDS == 1 && USE_SELF)
7596  val = op(a0, a1);
7597  else if constexpr (OPERANDS == 2 && !USE_SELF)
7598  val = op(a1, a2);
7599  else if constexpr (OPERANDS == 2 && USE_SELF)
7600  val = op(a0, a1, a2);
7601  else if constexpr (OPERANDS == 3 && !USE_SELF)
7602  val = op(a1, a2, a3);
7603  else if constexpr (OPERANDS == 3 && USE_SELF)
7604  val = op(a0, a1, a2, a3);
7605 
7606  return val;
7607 }
7608 
7609 /// To avoid code duplication, this helper function does the
7610 /// actual assignment. Works for number of operands ranging
7611 /// from 0 to 3, and masking on or off.
7612 template<int OPERANDS, bool MASKED, bool USE_SELF,
7613  typename T, typename OP, typename S, typename R, typename Q, typename M,
7614  typename ITERATOR>
7615 static inline void
7616 assignOperationOnIterator(ITERATOR &it, const OP& op, const UT_VoxelArray<S>* a,
7617  const UT_VoxelArray<R>* b, const UT_VoxelArray<Q>* c,
7618  const UT_VoxelArray<M>* mask)
7619 {
7620  // Works with at most 3 extra operands...
7621  UT_ASSERT(OPERANDS <= 3);
7622  // If there are no extra operands, must use our own value...
7623  UT_ASSERT(OPERANDS > 0 || USE_SELF);
7624 
7625  T val;
7626  S aval = {};
7627  R bval = {};
7628  Q cval = {};
7629 
7630  it.rewind();
7631  while (!it.atEnd())
7632  {
7633  // Get the tiles for the iterator and the source values.
7634  int tileNum = it.getLinearTileNum();
7635  UT_VoxelTile<M> *mtile = MASKED ? mask->getLinearTile(tileNum) : nullptr;
7636  UT_VoxelTile<S> *atile = OPERANDS > 0 ? a->getLinearTile(tileNum) : nullptr;
7637  UT_VoxelTile<R> *btile = OPERANDS > 1 ? b->getLinearTile(tileNum) : nullptr;
7638  UT_VoxelTile<Q> *ctile = OPERANDS > 2 ? c->getLinearTile(tileNum) : nullptr;
7639  UT_VoxelTile<T> *tile = it.getTile();
7640 
7641  // Skip if the mask tile exists, is constant and masked out.
7642  if (MASKED && mtile->isConstant() && ((*mtile)(0, 0, 0) <= ((M) 0.5)))
7643  {
7644  it.advanceTile();
7645  continue;
7646  }
7647 
7648  // Check for any complex compression. These don't allow
7649  // rawData so we have to use the getValue path.
7650  if ((USE_SELF && !tile->isSimpleCompression()) ||
7651  (OPERANDS > 0 && !atile->isSimpleCompression()) ||
7652  (OPERANDS > 1 && !btile->isSimpleCompression()) ||
7653  (OPERANDS > 2 && !ctile->isSimpleCompression()) ||
7654  (MASKED && !mtile->isSimpleCompression()))
7655  {
7656  // Have to use getValue...
7657  for (int z = 0; z < tile->zres(); z++)
7658  {
7659  for (int y = 0; y < tile->yres(); y++)
7660  {
7661  for (int x = 0; x < tile->xres(); x++)
7662  {
7663  if (!MASKED || ((*mtile)(x, y, z) > ((M) 0.5)))
7664  {
7665  switch (OPERANDS)
7666  {
7667  case 3:
7668  cval = (*ctile)(x, y, z);
7670  case 2:
7671  bval = (*btile)(x, y, z);
7673  case 1:
7674  aval = (*atile)(x, y, z);
7675  break;
7676  default:
7677  break;
7678  }
7679 
7680  if (USE_SELF) val = (*tile)(x, y, z);
7681 
7682  val = conditionalCallOperator<OPERANDS, USE_SELF>(
7683  op, val, aval, bval, cval);
7684  tile->setValue(x, y, z, val);
7685  }
7686  }
7687  }
7688  }
7689  }
7690  else
7691  {
7692  int ainc = (OPERANDS < 1 || atile->isConstant()) ? 0 : 1;
7693  int binc = (OPERANDS < 2 || btile->isConstant()) ? 0 : 1;
7694  int cinc = (OPERANDS < 3 || ctile->isConstant()) ? 0 : 1;
7695  int minc = (!MASKED || mtile->isConstant()) ? 0 : 1;
7696 
7697  // Check for constant sources. The destination needs
7698  // to be constant if USE_SELF is on.
7699  if ((!USE_SELF || tile->isConstant()) &&
7700  ainc == 0 && binc == 0 && cinc == 0 && minc == 0)
7701  {
7702  switch(OPERANDS)
7703  {
7704  case 3:
7705  cval = ctile->rawData()[0];
7707  case 2:
7708  bval = btile->rawData()[0];
7710  case 1:
7711  aval = atile->rawData()[0];
7712  break;
7713  default:
7714  break;
7715  }
7716 
7717  // Note that we know that we are either not masked or
7718  // the mask is constant; in the latter case, if the
7719  // mask were constant 0, then we would not enter this
7720  // area at all, so no need to check the actual value.
7721  if (USE_SELF) val = tile->rawData()[0];
7722  val = conditionalCallOperator<OPERANDS, USE_SELF>(op,
7723  val, aval, bval, cval);
7724  tile->makeConstant(val);
7725  }
7726  else
7727  {
7728  // Output is varying, so pre-uncompress our tile so
7729  // we can write directly with rawData.
7730  tile->uncompress();
7731  // The tile we uncompressed could well be one of the other operands;
7732  // thus, the increments must be updated.
7733  ainc = (OPERANDS < 1 || atile->isConstant()) ? 0 : 1;
7734  binc = (OPERANDS < 2 || btile->isConstant()) ? 0 : 1;
7735  cinc = (OPERANDS < 3 || ctile->isConstant()) ? 0 : 1;
7736  minc = (!MASKED || mtile->isConstant()) ? 0 : 1;
7737 
7738  const S* a_array = OPERANDS > 0 ? atile->rawData() : nullptr;
7739  const R* b_array = OPERANDS > 1 ? btile->rawData() : nullptr;
7740  const Q* c_array = OPERANDS > 2 ? ctile->rawData() : nullptr;
7741  const M* m_array = MASKED ? mtile->rawData() : nullptr;
7742  T* val_array = tile->rawData();
7743 
7744  int n = tile->numVoxels();
7745  for (int i = 0; i < n; i++)
7746  {
7747  if (!MASKED || (*m_array > ((M) 0.5)))
7748  {
7749  val_array[i] = conditionalCallOperator<OPERANDS, USE_SELF>(op,
7750  val_array[i], OPERANDS > 0 ? *a_array : aval,
7751  OPERANDS > 1 ? *b_array : bval,
7752  OPERANDS > 2 ? *c_array : cval);
7753  }
7754 
7755  // Move along the arrays...
7756  switch(OPERANDS)
7757  {
7758  case 3:
7759  c_array += cinc;
7761  case 2:
7762  b_array += binc;
7764  case 1:
7765  a_array += ainc;
7766  break;
7767  default:
7768  break;
7769  }
7770  if(MASKED)
7771  m_array += minc;
7772  }
7773  }
7774  }
7775 
7776  it.advanceTile();
7777  }
7778 }
7779 
7780 template <typename T>
7781 template <typename OP>
7782 void
7784 {
7785  assignOperationOnIterator<0, false, true, T, OP, float, float, float, float>
7786  (*this, op, nullptr, nullptr, nullptr, nullptr);
7787 }
7788 
7789 template <typename T>
7790 template <typename OP, typename S>
7791 void
7793  const UT_VoxelArray<S> &a)
7794 {
7795  UT_ASSERT(myArray->isMatching(a));
7796 
7797  assignOperationOnIterator<1, false, true, T, OP, S, float, float, float>
7798  (*this, op, &a, nullptr, nullptr, nullptr);
7799 }
7800 
7801 template <typename T>
7802 template <typename OP, typename S, typename R>
7803 void
7805  const UT_VoxelArray<S> &a,
7806  const UT_VoxelArray<R> &b)
7807 {
7808  UT_ASSERT(myArray->isMatching(a));
7809  UT_ASSERT(myArray->isMatching(b));
7810 
7811  assignOperationOnIterator<2, false, true, T, OP, S, R, float, float>
7812  (*this, op, &a, &b, nullptr, nullptr);
7813 }
7814 
7815 template <typename T>
7816 template <typename OP, typename S, typename R, typename Q>
7817 void
7819  const UT_VoxelArray<S> &a,
7820  const UT_VoxelArray<R> &b,
7821  const UT_VoxelArray<Q> &c)
7822 {
7823  UT_ASSERT(myArray->isMatching(a));
7824  UT_ASSERT(myArray->isMatching(b));
7825  UT_ASSERT(myArray->isMatching(c));
7826 
7827  assignOperationOnIterator<3, false, true, T, OP, S, R, Q, float>
7828  (*this, op, &a, &b, &c, nullptr);
7829 }
7830 
7831 template <typename T>
7832 template <typename OP, typename M>
7833 void
7835  const UT_VoxelArray<M> &mask)
7836 {
7837  UT_ASSERT(myArray->isMatching(mask));
7838 
7839  assignOperationOnIterator<0, true, true, T, OP, float, float, float, M>
7840  (*this, op, nullptr, nullptr, nullptr, &mask);
7841 }
7842 
7843 template <typename T>
7844 template <typename OP, typename S, typename M>
7845 void
7847  const UT_VoxelArray<S> &a,
7848  const UT_VoxelArray<M> &mask)
7849 {
7850  UT_ASSERT(myArray->isMatching(a));
7851  UT_ASSERT(myArray->isMatching(mask));
7852 
7853  assignOperationOnIterator<1, true, true, T, OP, S, float, float, M>
7854  (*this, op, &a, nullptr, nullptr, &mask);
7855 }
7856 
7857 template <typename T>
7858 template <typename OP, typename S, typename R, typename M>
7859 void
7861  const UT_VoxelArray<S> &a,
7862  const UT_VoxelArray<R> &b,
7863  const UT_VoxelArray<M> &mask)
7864 {
7865  UT_ASSERT(myArray->isMatching(a));
7866  UT_ASSERT(myArray->isMatching(b));
7867  UT_ASSERT(myArray->isMatching(mask));
7868 
7869  assignOperationOnIterator<2, true, true, T, OP, S, R, float, M>
7870  (*this, op, &a, &b, nullptr, &mask);
7871 }
7872 
7873 template <typename T>
7874 template <typename OP, typename S, typename R, typename Q, typename M>
7875 void
7877  const UT_VoxelArray<S> &a,
7878  const UT_VoxelArray<R> &b,
7879  const UT_VoxelArray<Q> &c,
7880  const UT_VoxelArray<M> &mask)
7881 {
7882  UT_ASSERT(myArray->isMatching(a));
7883  UT_ASSERT(myArray->isMatching(b));
7884  UT_ASSERT(myArray->isMatching(c));
7885  UT_ASSERT(myArray->isMatching(mask));
7886 
7887  assignOperationOnIterator<3, true, true, T, OP, S, R, Q, M>
7888  (*this, op, &a, &b, &c, &mask);
7889 }
7890 
7891 template <typename T>
7892 template <typename OP, typename S>
7893 void
7895  const UT_VoxelArray<S> &a)
7896 {
7897  UT_ASSERT(myArray->isMatching(a));
7898 
7899  assignOperationOnIterator<1, false, false, T, OP, S, float, float, float>
7900  (*this, op, &a, nullptr, nullptr, nullptr);
7901 }
7902 
7903 template <typename T>
7904 template <typename OP, typename S, typename R>
7905 void
7907  const UT_VoxelArray<S> &a,
7908  const UT_VoxelArray<R> &b)
7909 {
7910  UT_ASSERT(myArray->isMatching(a));
7911  UT_ASSERT(myArray->isMatching(b));
7912 
7913  assignOperationOnIterator<2, false, false, T, OP, S, R, float, float>
7914  (*this, op, &a, &b, nullptr, nullptr);
7915 }
7916 
7917 template <typename T>
7918 template <typename OP, typename S, typename R, typename Q>
7919 void
7921  const UT_VoxelArray<S> &a,
7922  const UT_VoxelArray<R> &b,
7923  const UT_VoxelArray<Q> &c)
7924 {
7925  UT_ASSERT(myArray->isMatching(a));
7926  UT_ASSERT(myArray->isMatching(b));
7927  UT_ASSERT(myArray->isMatching(c));
7928 
7929  assignOperationOnIterator<3, false, false, T, OP, S, R, Q, float>
7930  (*this, op, &a, &b, &c, nullptr);
7931 }
7932 
7933 template <typename T>
7934 template <typename OP, typename S, typename M>
7935 void
7937  const UT_VoxelArray<S> &a,
7938  const UT_VoxelArray<M> &mask)
7939 {
7940  UT_ASSERT(myArray->isMatching(a));
7941  UT_ASSERT(myArray->isMatching(mask));
7942 
7943  assignOperationOnIterator<1, true, false, T, OP, S, float, float, M>
7944  (*this, op, &a, nullptr, nullptr, &mask);
7945 }
7946 
7947 template <typename T>
7948 template <typename OP, typename S, typename R, typename M>
7949 void
7951  const UT_VoxelArray<S> &a,
7952  const UT_VoxelArray<R> &b,
7953  const UT_VoxelArray<M> &mask)
7954 {
7955  UT_ASSERT(myArray->isMatching(a));
7956  UT_ASSERT(myArray->isMatching(b));
7957  UT_ASSERT(myArray->isMatching(mask));
7958 
7959  assignOperationOnIterator<2, true, false, T, OP, S, R, float, M>
7960  (*this, op, &a, &b, nullptr, &mask);
7961 }
7962 
7963 template <typename T>
7964 template <typename OP, typename S, typename R, typename Q, typename M>
7965 void
7967  const UT_VoxelArray<S> &a,
7968  const UT_VoxelArray<R> &b,
7969  const UT_VoxelArray<Q> &c,
7970  const UT_VoxelArray<M> &mask)
7971 {
7972  UT_ASSERT(myArray->isMatching(a));
7973  UT_ASSERT(myArray->isMatching(b));
7974  UT_ASSERT(myArray->isMatching(c));
7975  UT_ASSERT(myArray->isMatching(mask));
7976 
7977  assignOperationOnIterator<3, true, false, T, OP, S, R, Q, M>
7978  (*this, op, &a, &b, &c, &mask);
7979 }
7980 
7981 template <typename T>
7982 template <typename OP>
7983 void
7985 {
7986  rewind();
7987 
7988  while (!atEnd())
7989  {
7990  UT_VoxelTile<T> *tile;
7991 
7992  tile = myArray->getLinearTile(myCurTile);
7993 
7994  if (!tile->isSimpleCompression())
7995  {
7996  // Have to use getValue...
7997  for (int z = 0; z < tile->zres(); z++)
7998  for (int y = 0; y < tile->yres(); y++)
7999  for (int x = 0; x < tile->xres(); x++)
8000  {
8001  T val;
8002 
8003  val = tile->operator()(x, y, z);
8004 
8005  op.reduce(val);
8006  }
8007  }
8008  else if (tile->isConstant())
8009  {
8010  // Woohoo! Too simple!
8011  T val;
8012 
8013  val = tile->rawData()[0];
8014  int n = tile->numVoxels();
8015 
8016  op.reduceMany(val, n);
8017  }
8018  else
8019  {
8020  T *val;
8021 
8022  val = tile->rawData();
8023 
8024  int n = tile->numVoxels();
8025  for (int i = 0; i < n; i++)
8026  {
8027  op.reduce(val[i]);
8028  }
8029  }
8030 
8031  advanceTile();
8032  }
8033 }
8034 
8035 //
8036 // UT_VoxelTileIterator implementation
8037 //
8038 template <typename T>
8040 {
8041  myCurTile = 0;
8042  myLinearTileNum = -1;
8043  myArray = 0;
8044  myAtEnd = true;
8045  myShouldCompressOnExit = false;
8046 }
8047 
8048 template <typename T>
8050 {
8051  myCurTile = 0;
8052  myLinearTileNum = -1;
8053  myArray = 0;
8054  myAtEnd = true;
8055  myShouldCompressOnExit = false;
8056  setTile(vit);
8057 }
8058 
8059 template <typename T>
8060 template <typename S>
8062 {
8063  myCurTile = 0;
8064  myLinearTileNum = -1;
8065  myArray = 0;
8066  myAtEnd = true;
8067  myShouldCompressOnExit = false;
8068  setTile(vit, array);
8069 }
8070 
8071 template <typename T>
8073 {
8074 }
8075 
8076 template <typename T>
8077 void
8079 {
8080  // Ensure we have at least one voxel in each direction
8081  if (!myCurTile ||
8082  !myCurTile->xres() || !myCurTile->yres() || !myCurTile->zres())
8083  {
8084  myCurTile = 0;
8085  return;
8086  }
8087 
8088  myPos[0] = myTileStart[0];
8089  myPos[1] = myTileStart[1];
8090  myPos[2] = myTileStart[2];
8091 
8092  myTileLocalPos[0] = 0;
8093  myTileLocalPos[1] = 0;
8094  myTileLocalPos[2] = 0;
8095 
8096  myTileSize[0] = myCurTile->xres();
8097  myTileSize[1] = myCurTile->yres();
8098  myTileSize[2] = myCurTile->zres();
8099 
8100  myAtEnd = false;
8101 }
8102 
8103 template <typename T>
8104 void
8106 {
8107  if (getCompressOnExit())
8108  {
8109  // Verify our last tile was a legitimate one.
8110  if (myCurTile)
8111  {
8112  myCurTile->tryCompress(myArray->getCompressionOptions());
8113  }
8114  }
8115  myAtEnd = true;
8116 }
8117 
8118 template <typename T>
8119 template <typename OP>
8120 void
8122 {
8123  assignOperationOnIterator<0, false, true, T, OP, float, float, float, float>
8124  (*this, op, nullptr, nullptr, nullptr, nullptr);
8125 }
8126 
8127 template <typename T>
8128 template <typename OP, typename S>
8129 void
8131  const UT_VoxelArray<S> &a)
8132 {
8133  UT_ASSERT(myArray->isMatching(a));
8134 
8135  assignOperationOnIterator<1, false, true, T, OP, S, float, float, float>
8136  (*this, op, &a, nullptr, nullptr, nullptr);
8137 }
8138 
8139 template <typename T>
8140 template <typename OP, typename S, typename R>
8141 void
8143  const UT_VoxelArray<S> &a,
8144  const UT_VoxelArray<R> &b)
8145 {
8146  UT_ASSERT(myArray->isMatching(a));
8147  UT_ASSERT(myArray->isMatching(b));
8148 
8149  assignOperationOnIterator<2, false, true, T, OP, S, R, float, float>
8150  (*this, op, &a, &b, nullptr, nullptr);
8151 }
8152 
8153 template <typename T>
8154 template <typename OP, typename S, typename R, typename Q>
8155 void
8157  const UT_VoxelArray<S> &a,
8158  const UT_VoxelArray<R> &b,
8159  const UT_VoxelArray<Q> &c)
8160 {
8161  UT_ASSERT(myArray->isMatching(a));
8162  UT_ASSERT(myArray->isMatching(b));
8163  UT_ASSERT(myArray->isMatching(c));
8164 
8165  assignOperationOnIterator<3, false, true, T, OP, S, R, Q, float>
8166  (*this, op, &a, &b, &c, nullptr);
8167 }
8168 
8169 template <typename T>
8170 template <typename OP, typename S>
8171 void
8173  const UT_VoxelArray<S> &a)
8174 {
8175  UT_ASSERT(myArray->isMatching(a));
8176 
8177  assignOperationOnIterator<1, false, false, T, OP, S, float, float, float>
8178  (*this, op, &a, nullptr, nullptr, nullptr);
8179 }
8180 
8181 template <typename T>
8182 template <typename OP, typename S, typename R>
8183 void
8185  const UT_VoxelArray<S> &a,
8186  const UT_VoxelArray<R> &b)
8187 {
8188  UT_ASSERT(myArray->isMatching(a));
8189  UT_ASSERT(myArray->isMatching(b));
8190 
8191  assignOperationOnIterator<2, false, false, T, OP, S, R, float, float>
8192  (*this, op, &a, &b, nullptr, nullptr);
8193 }
8194 
8195 template <typename T>
8196 template <typename OP, typename S, typename R, typename Q>
8197 void
8199  const UT_VoxelArray<S> &a,
8200  const UT_VoxelArray<R> &b,
8201  const UT_VoxelArray<Q> &c)
8202 {
8203  UT_ASSERT(myArray->isMatching(a));
8204  UT_ASSERT(myArray->isMatching(b));
8205  UT_ASSERT(myArray->isMatching(c));
8206 
8207  assignOperationOnIterator<3, false, false, T, OP, S, R, Q, float>
8208  (*this, op, &a, &b, &c, nullptr);
8209 }
8210 
8211 template <typename T>
8212 template <typename OP>
8213 bool
8215 {
8216  rewind();
8217 
8218  if (!myCurTile->isSimpleCompression())
8219  {
8220  // Have to use getValue...
8221  for (int z = 0; z < myTileSize[2]; z++)
8222  for (int y = 0; y < myTileSize[1]; y++)
8223  for (int x = 0; x < myTileSize[0]; x++)
8224  {
8225  T val;
8226 
8227  val = myCurTile->operator()(x, y, z);
8228 
8229  if (!op.reduce(val))
8230  return false;
8231  }
8232  }
8233  else if (myCurTile->isConstant())
8234  {
8235  // Woohoo! Too simple!
8236  T val;
8237 
8238  val = myCurTile->rawData()[0];
8239  int n = myCurTile->numVoxels();
8240 
8241  if (!op.reduceMany(val, n))
8242  return false;
8243  }
8244  else
8245  {
8246  T *val;
8247 
8248  val = myCurTile->rawData();
8249 
8250  int n = myCurTile->numVoxels();
8251  for (int i = 0; i < n; i++)
8252  {
8253  if (!op.reduce(val[i]))
8254  return false;
8255  }
8256  }
8257  return true;
8258 }
8259 
8260 ///
8261 /// UT_VoxelProbe methods
8262 ///
8263 
8264 template <typename T, bool DoRead, bool DoWrite, bool TestForWrites>
8266 {
8267  myCurLine = 0;
8268  myAllocCacheLine = 0;
8269  myDirty = false;
8270 
8271  myArray = 0;
8272 }
8273 
8274 template <typename T, bool DoRead, bool DoWrite, bool TestForWrites>
8276 {
8277  // A sure signal it hasn't been reset.
8278  myCurLine = 0;
8279  myAllocCacheLine = 0;
8280  myDirty = false;
8281 
8282  setArray(vox, prex, postx);
8283 }
8284 
8285 template <typename T, bool DoRead, bool DoWrite, bool TestForWrites>
8287 {
8288  if (DoWrite)
8289  {
8290  if (!TestForWrites || myDirty)
8291  {
8292  // Final write...
8293  writeCacheLine();
8294  }
8295  }
8296  delete [] myAllocCacheLine;
8297 }
8298 
8299 template <typename T, bool DoRead, bool DoWrite, bool TestForWrites>
8300 void
8302 {
8303  // If in write-only mode, makes no sense to have prex and postx...
8304  if (!DoRead && (prex != 0 || postx != 0))
8305  {
8306  UT_ASSERT(!"Voxel probe cannot be padded if set to not read.");
8307  prex = 0;
8308  postx = 0;
8309  }
8310 
8311  // Round up our pre and post
8312  int prepad, postpad;
8313 
8314  myCurLine = 0;
8315 
8316  prepad = (prex - 3) / 4;
8317  postpad = (postx + 3) / 4;
8318 
8319  myAllocCacheLine = new T [TILESIZE - prepad*4 + postpad*4];
8320  myCacheLine = &myAllocCacheLine[-prepad * 4];
8321 
8322  myPreX = prex;
8323  myPostX = postx;
8324 
8325  myForceCopy = false;
8326  if (myPreX || myPostX)
8327  myForceCopy = true;
8328 
8329  myDirty = false;
8330 
8331  myArray = vox;
8332 }
8333 
8334 template <typename T, bool DoRead, bool DoWrite, bool TestForWrites>
8335 bool
8337 {
8338  // Check if we have to reload our cache.
8339  if (myCurLine && y == myY && z == myZ)
8340  {
8341  if (x < myMaxValidX)
8342  {
8343  if (x == myX+1)
8344  {
8345  // A simple advanceX sufficies
8346  advanceX();
8347  return false;
8348  }
8349  else if (x >= myMinValidX)
8350  {
8351  // We can just recenter our search location.
8352  resetX(x);
8353  // Other functions can't just do advanceX as that
8354  // just does ++ in X.
8355  return true;
8356  }
8357  }
8358  }
8359 
8360  // Store existing cache...
8361  if (DoWrite)
8362  {
8363  if (!TestForWrites || myDirty)
8364  writeCacheLine();
8365  }
8366 
8367  // Everything failed, return to reloading the cache.
8368  reloadCache(x, y, z);
8369 
8370  if (TestForWrites)
8371  myDirty = false;
8372 
8373  return true;
8374 }
8375 
8376 template <typename T, bool DoRead, bool DoWrite, bool TestForWrites>
8377 void
8379 {
8380  UT_VoxelTile<T> *tile;
8381  bool xout = false, yout = false, zout = false;
8382  bool manualbuild = false;
8383 
8384  myX = x;
8385  myY = y;
8386  myZ = z;
8387  myMinValidX = x & ~TILEMASK;
8388  myMaxValidX = myMinValidX + TILESIZE;
8389  // UT_VoxelTile::fillCacheLine will fill in our cache up to the tile size;
8390  // we always want a full cache line, so we're on our own for the remainder.
8391  // This variable holds the number of voxels that need to be manually filled
8392  // in. Note that we should never access those out-of-bound voxels if we are
8393  // not reading--so don't bother filling in in that case.
8394  int topad = DoRead ? SYSmax(0, myMaxValidX - myArray->getXRes()) : 0;
8395 
8396  // We say that x is invalid only when the entire line is outside the array
8397  // proper.
8398  if (myMaxValidX <= 0 || myMinValidX >= myArray->getXRes())
8399  xout = true;
8400  if (y < 0 || y >= myArray->getYRes())
8401  yout = true;
8402  if (z < 0 || z >= myArray->getZRes())
8403  zout = true;
8404 
8405  // If y or z are invalid, they will be invalid for every voxel
8406  if (yout || zout)
8407  {
8408  // We can often handle this by clamping...
8409  switch (myArray->getBorder())
8410  {
8412  buildConstantCache(myArray->getBorderValue());
8413 
8414  // ALL DONE
8415  return;
8416 
8417  case UT_VOXELBORDER_REPEAT:
8418  // Simply modulate our lookup.
8419  if (yout)
8420  {
8421  y %= myArray->getYRes();
8422  if (y < 0)
8423  y += myArray->getYRes();
8424  }
8425  if (zout)
8426  {
8427  z %= myArray->getZRes();
8428  if (z < 0)
8429  z += myArray->getZRes();
8430  }
8431  break;
8432 
8433  case UT_VOXELBORDER_MIRROR:
8434  if (yout)
8435  y = UT_VoxelArray<T>::mirrorCoordinates(y, myArray->getYRes());
8436  if (zout)
8437  z = UT_VoxelArray<T>::mirrorCoordinates(z, myArray->getZRes());
8438  break;
8439 
8440  case UT_VOXELBORDER_STREAK:
8441  {
8442  // Clamp
8443  int tx = 0;
8444  myArray->clampIndex(tx, y, z);
8445  break;
8446  }
8447 
8448  case UT_VOXELBORDER_EXTRAP:
8449  {
8450  // Force a manual build.
8451  manualbuild = true;
8452  break;
8453  }
8454  }
8455  }
8456 
8457  // Note y and z may no longer equal myY and myZ, this is not
8458  // a problem however as we have set up our cached versions
8459  // to the unclamped versions.
8460 
8461  if (xout || manualbuild)
8462  {
8463  // We have to manually build if we are extrap or repeat type,
8464  // not just generate a single constant!
8465  if (myArray->getBorder() == UT_VOXELBORDER_EXTRAP ||
8466  myArray->getBorder() == UT_VOXELBORDER_REPEAT ||
8467  myArray->getBorder() == UT_VOXELBORDER_MIRROR)
8468  {
8469  manualbuild = true;
8470  }
8471 
8472  // If there is no pre & post, this will always be constant.
8473  if (!myPreX && !myPostX && !manualbuild)
8474  {
8475  buildConstantCache(myArray->getValue(x, y, z));
8476 
8477  // ALL DONE
8478  return;
8479  }
8480  else
8481  {
8482  // If we are STREAK or CONSTANT, we have a constant
8483  // value in our own cache.
8484  // If we are REPEAT, we want to modulo our x value
8485  // and run the normal code path.
8486  int i;
8487 
8488  for (i = myPreX; i < 0; i++)
8489  {
8490  myCacheLine[i] = myArray->getValue(myMinValidX+i, y, z);
8491  }
8492 
8493  if ((myArray->getBorder() == UT_VOXELBORDER_EXTRAP) ||
8494  (myArray->getBorder() == UT_VOXELBORDER_REPEAT) ||
8495  (myArray->getBorder() == UT_VOXELBORDER_MIRROR))
8496  {
8497  // Explicitly load extrap values as they are not constant.
8498  for (; i < TILESIZE; i++)
8499  myCacheLine[i] = myArray->getValue(myMinValidX+i, y, z);
8500  }
8501  else
8502  {
8503  // CONSTANT and STREAK will have constant values
8504  // in this
8505  T value = myArray->getValue(x, y, z);
8506  for (; i < TILESIZE; i++)
8507  {
8508  myCacheLine[i] = value;
8509  }
8510  }
8511 
8512  for (; i < TILESIZE + myPostX; i++)
8513  {
8514  myCacheLine[i] = myArray->getValue(myMinValidX+i, y, z);
8515  }
8516 
8517  myCurLine = &myCacheLine[x & TILEMASK];
8518  myStride = 1;
8519 
8520  // ALL DONE
8521  return;
8522  }
8523  }
8524 
8525  int xtile, ytile, ztile, tileidx;
8526  int lx, ly, lz;
8527  int i;
8528 
8529  xtile = x >> TILEBITS;
8530  ytile = y >> TILEBITS;
8531  ztile = z >> TILEBITS;
8532 
8533  // Get our local indices
8534  lx = x & TILEMASK;
8535  ly = y & TILEMASK;
8536  lz = z & TILEMASK;
8537 
8538  tileidx = (ztile * myArray->getTileRes(1) + ytile) * myArray->getTileRes(0);
8539 
8540  if (myPreX)
8541  {
8542  if (xtile)
8543  {
8544  // Simple to fetch...
8545  tile = myArray->getLinearTile(tileidx+xtile-1);
8546  for (i = myPreX; i < 0; i++)
8547  {
8548  // Safe to to & TILEMASK as we know earlier tiles
8549  // are always full...
8550  myCacheLine[i] = (*tile)(i & TILEMASK, ly, lz);
8551  }
8552  }
8553  else
8554  {
8555  if (myArray->getBorder() == UT_VOXELBORDER_REPEAT)
8556  {
8557  int resx = myArray->getXRes();
8558  int xpos;
8559 
8560  xpos = myPreX;
8561  xpos %= resx;
8562  // We add resx to guarantee we are in range.
8563  xpos += resx;
8564 
8565  // Manually invoke getValue()
8566  for (i = myPreX; i < 0; i++)
8567  {
8568  myCacheLine[i] = (*myArray)(xpos, y, z);
8569  xpos++;
8570  if (xpos > resx)
8571  xpos -= resx;
8572  }
8573  }
8574  else if (myArray->getBorder() == UT_VOXELBORDER_MIRROR)
8575  {
8576  int resx = myArray->getXRes();
8577  int resx2 = resx * 2;
8578  // Send the starting X position the the index within the array
8579  // and its one reflection.
8580  int xpos = myPreX % resx2;
8581  if (xpos < 0)
8582  xpos += resx2;
8583  // This is the increment. If we're on even repetitions of the
8584  // array, we move forward, otherwise we move backwards:
8585  // 0 1 2 3 4 5 6 6 5 4 3 2 1 0 0 1 2 3 4 5 6
8586  // \-----------/ \-----------/ \-----------/
8587  // inside decreasing increasing
8588  int dir = 1;
8589  if (xpos >= resx)
8590  {
8591  dir = -1;
8592  xpos = resx2 - xpos - 1;
8593  }
8594 
8595  for (i = myPreX; i < 0; i++)
8596  {
8597  myCacheLine[i] = (*myArray)(xpos, y, z);
8598  xpos += dir;
8599  // If we finished a reflection in the backward direction,
8600  // restart going forward.
8601  if (xpos < 0)
8602  {
8603  xpos = 0;
8604  dir = 1;
8605  }
8606  // If we finished a reflection in the forward direction,
8607  // restart going backward.
8608  else if (xpos >= resx)
8609  {
8610  xpos = resx - 1;
8611  dir = -1;
8612  }
8613  }
8614  }
8615  else
8616  {
8617  T value;
8618 
8619  if (myArray->getBorder() == UT_VOXELBORDER_STREAK)
8620  {
8621  tile = myArray->getLinearTile(tileidx+xtile);
8622  value = (*tile)(0, ly, lz);
8623  }
8624  else
8625  value = myArray->getBorderValue();
8626 
8627  // Fill in value.
8628  for (i = myPreX; i < 0; i++)
8629  myCacheLine[i] = value;
8630  }
8631  }
8632  }
8633 
8634  if (myPostX)
8635  {
8636  int cachelen = TILESIZE;
8637  int resx = myArray->getXRes();
8638 
8639  // Append our end part in.
8640  // First, determine if we read past the end...
8641  if (myMaxValidX + myPostX > myArray->getXRes())
8642  {
8643  // This can be very messy. For example, we may have
8644  // a 1 wide tile after this tile and be looking two voxels
8645  // ahead, which means we can't guarantee our lookup
8646  // is entirely within one tile.
8647  // However, we can break it into two loops.
8648  int xpos = myMaxValidX;
8649 
8650  // Portion that still fits in the next tile...
8651  i = 0;
8652  if (xpos < resx)
8653  {
8654  tile = myArray->getLinearTile(tileidx+xtile+1);
8655  for (; i < myPostX && xpos < resx; i++)
8656  {
8657  myCacheLine[i + cachelen] = (*tile)(i, ly, lz);
8658  xpos++;
8659  }
8660  }
8661  // Portion that reads past the end.
8662  if (i < myPostX)
8663  {
8664  if (myArray->getBorder() == UT_VOXELBORDER_REPEAT)
8665  {
8666  xpos = xpos % resx;
8667 
8668  // Revert to the array operator.
8669  for (; i < myPostX; i++)
8670  {
8671  myCacheLine[i + cachelen] = (*myArray)(xpos, y, z);
8672  xpos++;
8673  if (xpos > resx)
8674  xpos -= resx;
8675  }
8676  }
8677  else if (myArray->getBorder() == UT_VOXELBORDER_MIRROR)
8678  {
8679  // We've just gone past the end of the array, so start
8680  // heading in the opposite direction.
8681  xpos = resx - 1;
8682  int dir = -1;
8683  for (; i < myPostX; i++)
8684  {
8685  myCacheLine[i + cachelen] = (*myArray)(xpos, y, z);
8686  xpos += dir;
8687  // If we finished a reflection in the backward direction,
8688  // restart going forward.
8689  if (xpos < 0)
8690  {
8691  xpos = 0;
8692  dir = 1;
8693  }
8694  // If we finished a reflection in the forward direction,
8695  // restart going backward.
8696  else if (xpos >= resx)
8697  {
8698  xpos = resx - 1;
8699  dir = -1;
8700  }
8701  }
8702  }
8703  else
8704  {
8705  T value;
8706 
8707  if (myArray->getBorder() == UT_VOXELBORDER_STREAK)
8708  {
8709  tile = myArray->getLinearTile(tileidx+xtile);
8710  value = (*tile)(tile->xres()-1, ly, lz);
8711  }
8712  else
8713  value = myArray->getBorderValue();
8714 
8715  for (; i < myPostX; i++)
8716  myCacheLine[i + cachelen] = value;
8717  }
8718  }
8719  }
8720  else
8721  {
8722  // All groovy, we fit in so thus must fit in the next tile
8723  tile = myArray->getLinearTile(tileidx+xtile+1);
8724  for (i = 0; i < myPostX; i++)
8725  {
8726  // Safe to to & TILEMASK as we know earlier tiles
8727  // are always full...
8728  myCacheLine[i + cachelen] = (*tile)(i, ly, lz);
8729  }
8730  }
8731 
8732  }
8733 
8734  tile = myArray->getLinearTile(tileidx+xtile);
8735  // We'll be filling in the rest of the cache line if padding needs to be
8736  // performed; thus, force copy in that case.
8737  myCurLine = tile->fillCacheLine(myCacheLine, myStride, lx, ly, lz,
8738  myForceCopy || topad > 0, DoWrite);
8739 
8740  // Pad the remainder of the cacheline.
8741  if (topad > 0)
8742  {
8743  // Make the array do the extrapolation for us...
8744  if ((myArray->getBorder() == UT_VOXELBORDER_EXTRAP) ||
8745  (myArray->getBorder() == UT_VOXELBORDER_REPEAT) ||
8746  (myArray->getBorder() == UT_VOXELBORDER_MIRROR))
8747  {
8748  for (i = topad; i > 0; i--)
8749  {
8750  myCacheLine[TILESIZE - i]
8751  = myArray->getValue(myMaxValidX - i, y, z);
8752  }
8753  }
8754  else
8755  {
8756  // The rest of the cache line is constant in these cases...
8757  T value;
8758  if (myArray->getBorder() == UT_VOXELBORDER_STREAK)
8759  // Streak: use the last internal value from the line.
8760  value = myCacheLine[TILESIZE - topad - 1];
8761  else
8762  // Constant: use the border value.
8763  value = myArray->getBorderValue();
8764 
8765  for (i = topad; i > 0; i--)
8766  {
8767  myCacheLine[TILESIZE - i] = value;
8768  }
8769  }
8770  }
8771 }
8772 
8773 template <typename T, bool DoRead, bool DoWrite, bool TestForWrites>
8774 void
8776 {
8777  if (DoWrite)
8778  {
8779  // Force a full copy.
8780  myStride = 1;
8781 
8782  int i;
8783 
8784  for (i = myPreX; i < TILESIZE+myPostX; i++)
8785  myCacheLine[i] = value;
8787  }
8788  else
8789  {
8790  myCacheLine[0] = value;
8791  // These are to ensure our SSE is compatible.
8792  myCacheLine[1] = value;
8793  myCacheLine[2] = value;
8794  myCacheLine[3] = value;
8795 
8797  myStride = 0;
8798  }
8799 }
8800 
8801 template <typename T, bool DoRead, bool DoWrite, bool TestForWrites>
8802 void
8804 {
8805  if (!DoWrite)
8806  {
8807  UT_ASSERT(0);
8808  return;
8809  }
8810  // Ensure we have a valid loaded line, otherwise no point
8811  // doing a write back...
8812  if (!myCurLine)
8813  return;
8814 
8815  // Reset our current line...
8816  myCurLine -= myX - myMinValidX;
8817 
8818  // Determine if we actually have to write back,
8819  // if we had a pointer inside the tile we don't have to.
8820  if (myCurLine != myCacheLine)
8821  return;
8822 
8823  // Look up our voxel
8824  int xtile, ytile, ztile, y, z;
8825  UT_VoxelTile<T> *tile;
8826 
8827  xtile = myMinValidX >> TILEBITS;
8828  ytile = myY >> TILEBITS;
8829  ztile = myZ >> TILEBITS;
8830  y = myY & TILEMASK;
8831  z = myZ & TILEMASK;
8832 
8833  tile = myArray->getTile(xtile, ytile, ztile);
8834 
8835  // Write back our results
8836  tile->writeCacheLine(myCurLine, y, z);
8837 }
8838 
8839 ///
8840 /// VoxelProbeCube functions
8841 ///
8842 template <typename T>
8844 {
8845  myValid = false;
8846 }
8847 
8848 template <typename T>
8850 {
8851 }
8852 
8853 template <typename T>
8854 void
8856 {
8857  UT_ASSERT(vox != nullptr);
8858  myLines[0][0].setConstArray(vox, -1, 1);
8859  myLines[0][1].setConstArray(vox, -1, 1);
8860  myLines[0][2].setConstArray(vox, -1, 1);
8861 
8862  myLines[1][0].setConstArray(vox, -1, 1);
8863  myLines[1][1].setConstArray(vox, -1, 1);
8864  myLines[1][2].setConstArray(vox, -1, 1);
8865 
8866  myLines[2][0].setConstArray(vox, -1, 1);
8867  myLines[2][1].setConstArray(vox, -1, 1);
8868  myLines[2][2].setConstArray(vox, -1, 1);
8869 
8870  myValid = false;
8871 }
8872 
8873 template <typename T>
8874 void
8876 {
8877  UT_ASSERT(vox != nullptr);
8878  /// This coudl be 0,0, but by keeping it the full range
8879  /// we ensure it is legal to rotate when we do a +1
8880  myLines[0][1].setConstArray(vox, -1, 1);
8881 
8882  myLines[1][0].setConstArray(vox, 0, 0);
8883  myLines[1][1].setConstArray(vox, -1, 1);
8884  myLines[1][2].setConstArray(vox, 0, 0);
8885 
8886  myLines[2][1].setConstArray(vox, -1, 1);
8887 
8888  myValid = false;
8889 }
8890 
8891 template <typename T>
8892 bool
8894 {
8895  if (myValid && myZ == z)
8896  {
8897  if (myY == y)
8898  {
8899  // Potential for a simple advance...
8900  if (x < myMaxValidX && x == myX+1)
8901  {
8902  // AdvanceX.
8903  myLines[0][0].advanceX();
8904  myLines[0][1].advanceX();
8905  myLines[0][2].advanceX();
8906 
8907  myLines[1][0].advanceX();
8908  myLines[1][1].advanceX();
8909  myLines[1][2].advanceX();
8910 
8911  myLines[2][0].advanceX();
8912  myLines[2][1].advanceX();
8913  myLines[2][2].advanceX();
8914 
8915  // Update our cache.
8916  myX = x;
8917 
8918  return false;
8919  }
8920  }
8921 #if 1
8922  else if (y == myY+1 && x < myMaxValidX && x >= myMinValidX)
8923  {
8924  // We have finished our x pass and just incremented y by one
8925  // Rather than resetting all our lines we can just swap
8926  // our y+1 lines into our current lines and then run the
8927  // normal reset.
8928  rotateLines(myLines[0][0], myLines[1][0], myLines[2][0]);
8929  rotateLines(myLines[0][1], myLines[1][1], myLines[2][1]);
8930  rotateLines(myLines[0][2], myLines[1][2], myLines[2][2]);
8931 
8932  // The first 6 lines can just reset their X values
8933  // directly
8934  myLines[0][0].resetX(x);
8935  myLines[0][1].resetX(x);
8936  myLines[0][2].resetX(x);
8937 
8938  myLines[1][0].resetX(x);
8939  myLines[1][1].resetX(x);
8940  myLines[1][2].resetX(x);
8941 
8942  // Only the new lines need a reload.
8943  myLines[2][0].setIndex(x, y+1, z-1);
8944  myLines[2][1].setIndex(x, y+1, z);
8945  myLines[2][2].setIndex(x, y+1, z+1);
8946 
8947  // Update the cache values that have changed.
8948  myX = x;
8949  myY = y;
8950 
8951  return true;
8952  }
8953 #endif
8954  }
8955 
8956  // Now just invoke setIndex on all children
8957  myLines[0][0].setIndex(x, y-1, z-1);
8958  myLines[0][1].setIndex(x, y-1, z);
8959  myLines[0][2].setIndex(x, y-1, z+1);
8960 
8961  myLines[1][0].setIndex(x, y, z-1);
8962  myLines[1][1].setIndex(x, y, z);
8963  myLines[1][2].setIndex(x, y, z+1);
8964 
8965  myLines[2][0].setIndex(x, y+1, z-1);
8966  myLines[2][1].setIndex(x, y+1, z);
8967  myLines[2][2].setIndex(x, y+1, z+1);
8968 
8969  // update our cache values
8970  myX = x;
8971  myY = y;
8972  myZ = z;
8973  myValid = true;
8974  myMinValidX = myLines[1][1].myMinValidX;
8975  myMaxValidX = myLines[1][1].myMaxValidX;
8976 
8977  return true;
8978 }
8979 
8980 template <typename T>
8981 bool
8983 {
8984  if (myValid && myZ == z)
8985  {
8986  if (myY == y)
8987  {
8988  // Potential for a simple advance...
8989  if (x < myMaxValidX && x == myX+1)
8990  {
8991  // AdvanceX.
8992  myLines[0][1].advanceX();
8993 
8994  myLines[1][0].advanceX();
8995  myLines[1][1].advanceX();
8996  myLines[1][2].advanceX();
8997 
8998  myLines[2][1].advanceX();
8999 
9000  // Update our cache.
9001  myX = x;
9002 
9003  return false;
9004  }
9005  }
9006  else if (y == myY+1 && x < myMaxValidX && x >= myMinValidX)
9007  {
9008  // We have finished our x pass and just incremented y by one
9009  // We can thus rotate the meaning of our central
9010  // cache lines and just reset their x pointers, leaving
9011  // only three real resets to be done.
9012  rotateLines(myLines[0][1], myLines[1][1], myLines[2][1]);
9013 
9014  myLines[0][1].resetX(x);
9015  myLines[1][1].resetX(x);
9016 
9017  myLines[1][0].setIndex(x, y, z-1);
9018  myLines[1][2].setIndex(x, y, z+1);
9019 
9020  myLines[2][1].setIndex(x, y+1, z);
9021 
9022  myX = x;
9023  myY = y;
9024  return true;
9025  }
9026  }
9027 
9028  // Now just invoke setIndex on all children
9029  myLines[0][1].setIndex(x, y-1, z);
9030 
9031  myLines[1][0].setIndex(x, y, z-1);
9032  myLines[1][1].setIndex(x, y, z);
9033  myLines[1][2].setIndex(x, y, z+1);
9034 
9035  myLines[2][1].setIndex(x, y+1, z);
9036 
9037  // update our cache values
9038  myX = x;
9039  myY = y;
9040  myZ = z;
9041  myValid = true;
9042  myMinValidX = myLines[1][1].myMinValidX;
9043  myMaxValidX = myLines[1][1].myMaxValidX;
9044 
9045  return true;
9046 }
9047 
9048 template <typename T>
9049 fpreal64
9051 {
9052  // These are our derivatives of Phi.
9053  fpreal64 Px, Py, Pz;
9054  fpreal64 Pxx, Pyy, Pzz;
9055  fpreal64 Pxy, Pxz, Pyz;
9056  fpreal64 gradlen;
9057  fpreal64 k;
9058 
9059  // Compute first derivatives.
9060  // dPhi = (Phi+1 - Phi-1) / 2 * dx
9061 
9062  Px = getValue(1, 0, 0) - getValue(-1, 0, 0);
9063  Px *= 0.5 * invvoxelsize.x();
9064 
9065  Py = getValue(0, 1, 0) - getValue(0, -1, 0);
9066  Py *= 0.5 * invvoxelsize.y();
9067 
9068  Pz = getValue(0, 0, 1) - getValue(0, 0, -1);
9069  Pz *= 0.5 * invvoxelsize.z();
9070 
9071  // Compute second derivatives. (Note Pxy == Pyx)
9072 
9073  // d^2Phi = (Phi+1 - 2 Phi + Phi-1) / (dx*dx)
9074  Pxx = getValue(1, 0, 0)
9075  - 2 * getValue(0, 0, 0)
9076  + getValue(-1, 0, 0);
9077  Pxx *= invvoxelsize.x() * invvoxelsize.x();
9078 
9079  Pyy = getValue(0, 1, 0)
9080  - 2 * getValue(0, 0, 0)
9081  + getValue(0, -1, 0);
9082  Pyy *= invvoxelsize.y() * invvoxelsize.y();
9083 
9084  Pzz = getValue(0, 0, 1)
9085  - 2 * getValue(0, 0, 0)
9086  + getValue(0, 0, -1);
9087  Pzz *= invvoxelsize.z() * invvoxelsize.z();
9088 
9089  // A bit more complicated :>
9090  Pxy = getValue(1, 1,0) - getValue(-1, 1,0);
9091  Pxy -= getValue(1,-1,0) - getValue(-1,-1,0);
9092  Pxy *= 0.25 * invvoxelsize.x() * invvoxelsize.y();
9093 
9094  Pxz = getValue(1,0, 1) - getValue(-1,0, 1);
9095  Pxz -= getValue(1,0,-1) - getValue(-1,0,-1);
9096  Pxz *= 0.25 * invvoxelsize.x() * invvoxelsize.z();
9097 
9098  Pyz = getValue(0,1, 1) - getValue(0,-1, 1);
9099  Pyz -= getValue(0,1,-1) - getValue(0,-1,-1);
9100  Pyz *= 0.25 * invvoxelsize.y() * invvoxelsize.z();
9101 
9102  // Calculate the |grad(phi)| term;
9103  gradlen = SYSsqrt(Px * Px + Py * Py + Pz * Pz);
9104 
9105  // Finally, our curvature!
9106  // This is equation 1.8 from the holy book.
9107  // The problem is that it implies that 0 gradient means 0 curvature.
9108  // This is not true!
9109  // Even if Px,Py,Pz == 0, if Pxx != 0, we have a curved surface
9110  // consider a point at the maxima of a circle.
9111  k = Px*Px * (Pyy + Pzz) + Py*Py * (Pxx + Pzz) + Pz*Pz * (Pxx + Pyy);
9112  k -= 2 * (Pxy*Px*Py + Pyz*Py*Pz + Pxz*Px*Pz);
9113 
9114  // Avoid #IND in places with exactly zero curvature.
9115  if (!gradlen)
9116  k = 0;
9117  else
9118  k /= gradlen * gradlen * gradlen;
9119 
9120  // Clamp our curvature...
9121  fpreal64 maxk;
9122 
9123  maxk = invvoxelsize.maxComponent();
9124  if (k < -maxk)
9125  k = -maxk;
9126  if (k > maxk)
9127  k = maxk;
9128 
9129  return k;
9130 }
9131 
9132 template <typename T>
9133 fpreal64
9135 {
9136  fpreal64 Pxx, Pyy, Pzz;
9137  fpreal64 centralval;
9138 
9139  centralval = getValue(0, 0, 0);
9140 
9141  // d^2Phi = (Phi+1 - 2 Phi + Phi-1) / (dx*dx)
9142  Pxx = getValue(1, 0, 0)
9143  - 2 * centralval
9144  + getValue(-1, 0, 0);
9145  Pxx *= invvoxelsize.x() * invvoxelsize.x();
9146 
9147  Pyy = getValue(0, 1, 0)
9148  - 2 * centralval
9149  + getValue(0, -1, 0);
9150  Pyy *= invvoxelsize.y() * invvoxelsize.y();
9151 
9152  Pzz = getValue(0, 0, +1)
9153  - 2 * centralval
9154  + getValue(0, 0, -1);
9155  Pzz *= invvoxelsize.z() * invvoxelsize.z();
9156 
9157  return Pxx + Pyy + Pzz;
9158 }
9159 
9160 template <typename T>
9161 void
9165 {
9166  T *tmpcache, *tmpalloc;
9167 
9168  // We take advantage of the fact we know only a small portion
9169  // of the cache lines needs to be copied.
9170  tmpcache = ym.myCacheLine;
9171  tmpalloc = ym.myAllocCacheLine;
9172  //const T *tmpcur = ym.myCurLine;
9173 
9174  ym.myCacheLine = y0.myCacheLine;
9176  ym.myCurLine = y0.myCurLine;
9177  ym.myStride = y0.myStride;
9178  ym.myY++;
9179 
9180  y0.myCacheLine = yp.myCacheLine;
9182  y0.myCurLine = yp.myCurLine;
9183  y0.myStride = yp.myStride;
9184  y0.myY++;
9185 
9186  yp.myCacheLine = tmpcache;
9187  yp.myAllocCacheLine = tmpalloc;
9188  // Setting to zero will force a rebuild.
9189  yp.myCurLine = 0;
9190 }
9191 
9192 ///
9193 /// UT_VoxelProbeFace methods
9194 ///
9195 template <typename T>
9197 {
9198  myValid = false;
9199 }
9200 
9201 template <typename T>
9203 {
9204 }
9205 
9206 
9207 template <typename T>
9208 void
9210 {
9211  // We need one extra to the right on the X probe
9212  myLines[0][0].setConstArray(vx, 0, 1);
9213 
9214  // The rest can be direct reads
9215  myLines[1][0].setConstArray(vy, 0, 0);
9216  myLines[1][1].setConstArray(vy, 0, 0);
9217 
9218  myLines[2][0].setConstArray(vz, 0, 0);
9219  myLines[2][1].setConstArray(vz, 0, 0);
9220 
9221  myValid = false;
9222 }
9223 
9224 template <typename T>
9225 void
9227 {
9228  myVoxelSize = size;
9229  myInvVoxelSize = 1;
9230  myInvVoxelSize /= myVoxelSize;
9231 }
9232 
9233 template <typename T>
9234 bool
9236 {
9237  if (myValid && myZ == z)
9238  {
9239  if (myY == y)
9240  {
9241  // Potential for a simple advance...
9242  if (x < myMaxValidX && x == myX+1)
9243  {
9244  // AdvanceX.
9245  myLines[0][0].advanceX();
9246 
9247  myLines[1][0].advanceX();
9248  myLines[1][1].advanceX();
9249 
9250  myLines[2][0].advanceX();
9251  myLines[2][1].advanceX();
9252 
9253  // Update our cache.
9254  myX = x;
9255 
9256  return false;
9257  }
9258  }
9259  else if (y == myY+1 && x < myMaxValidX && x >= myMinValidX)
9260  {
9261  // We have finished our x pass and just incremented y by one
9262  // We can swap our y lines to get to the next read for
9263  // those lines.
9264  swapLines(myLines[1][0], myLines[1][1]);
9265 
9266  myLines[1][0].resetX(x);
9267 
9268  // All the other lines need to be reloaded.
9269  myLines[0][0].setIndex(x, y, z);
9270  myLines[1][1].setIndex(x, y+1, z);
9271 
9272  myLines[2][0].setIndex(x, y, z);
9273  myLines[2][1].setIndex(x, y, z+1);
9274 
9275  myX = x;
9276  myY = y;
9277  return true;
9278  }
9279  }
9280 
9281  // Now just invoke setIndex on all children
9282  myLines[0][0].setIndex(x, y, z);
9283 
9284  myLines[1][0].setIndex(x, y, z);
9285  myLines[1][1].setIndex(x, y+1, z);
9286 
9287  myLines[2][0].setIndex(x, y, z);
9288  myLines[2][1].setIndex(x, y, z+1);
9289 
9290  // update our cache values
9291  myX = x;
9292  myY = y;
9293  myZ = z;
9294  myValid = true;
9295  myMinValidX = myLines[0][0].myMinValidX;
9296  myMaxValidX = myLines[0][0].myMaxValidX;
9297 
9298  return true;
9299 }
9300 
9301 template <typename T>
9302 void
9305 {
9306  T *tmpcache, *tmpalloc;
9307 
9308  // We take advantage of the fact we know only a small portion
9309  // of the cache lines needs to be copied.
9310  tmpcache = ym.myCacheLine;
9311  tmpalloc = ym.myAllocCacheLine;
9312  //const T *tmpcur = ym.myCurLine;
9313 
9314  ym.myCacheLine = yp.myCacheLine;
9316  ym.myCurLine = yp.myCurLine;
9317  ym.myStride = yp.myStride;
9318  ym.myY++;
9319 
9320  yp.myCacheLine = tmpcache;
9321  yp.myAllocCacheLine = tmpalloc;
9322  // Setting to zero will force a rebuild.
9323  yp.myCurLine = 0;
9324 }
9325 
9326 ///
9327 /// VoxelProbeAverage methods
9328 ///
9329 template <typename T, int XStep, int YStep, int ZStep>
9330 void
9332 {
9333  int prex = (XStep < 0) ? XStep : 0;
9334  int postx = (XStep > 0) ? XStep : 0;
9335 
9336  myLines[0][0].setArray((UT_VoxelArray<T> *)vox, prex, postx);
9337  if (YStep)
9338  {
9339  myLines[1][0].setArray((UT_VoxelArray<T> *)vox, prex, postx);
9340  if (ZStep)
9341  {
9342  myLines[1][1].setArray((UT_VoxelArray<T> *)vox, prex, postx);
9343  }
9344  }
9345  if (ZStep)
9346  myLines[0][1].setArray((UT_VoxelArray<T> *)vox, prex, postx);
9347 }
9348 
9349 template <typename T, int XStep, int YStep, int ZStep>
9350 bool
9352 {
9353  bool result = false;
9354 
9355  // Adjust x, y, and z according to our half step.
9356  // y and z negative steps require us decrementing. x steps
9357  // do not require a change as we use the pre/post to affect this,
9358  // if we adjusted the actual x we would get twice the cache misses.
9359  if (YStep < 0)
9360  y--;
9361  if (ZStep < 0)
9362  z--;
9363 
9364  result |= myLines[0][0].setIndex(x, y, z);
9365  if (YStep)
9366  {
9367  result |= myLines[1][0].setIndex(x, y+1, z);
9368  if (ZStep)
9369  result |= myLines[1][1].setIndex(x, y+1, z+1);
9370  }
9371  if (ZStep)
9372  result |= myLines[0][1].setIndex(x, y, z+1);
9373 
9374  return result;
9375 }
bool uniformWrite(bool value)
bool readBinaryString(UT_String &str, UT_ISTREAM_RLE_IO startbits)
int x() const
Retrieve the current location of the iterator.
type
Definition: core.h:556
void applyOperation(const OP &op)
#define SYSmax(a, b)
Definition: SYS_Math.h:1582
bool jsonValue(bool value)
bool beginUniformArray(int64 length, UT_JID id)
void findexToPos(UT_Vector3F ipos, UT_Vector3F &pos) const
typedef int(APIENTRYP RE_PFNGLXSWAPINTERVALSGIPROC)(int)
GA_API const UT_StringHolder dist
SYS_FORCE_INLINE T lerpSample(T *samples, float fx, float fy, float fz) const
Lerps the given sample using trilinear interpolation.
void UTparallelFor(const Range &range, const Body &body, const int subscribe_ratio=2, const int min_grain_size=1, const bool force_use_task_scope=true)
const UT_VoxelTile< T > & operator=(const UT_VoxelTile< T > &src)
void setArray(UT_VoxelArray< T > *vox, int prex=0, int postx=0)
UT_VoxelTile< T > * getTile() const
Returns the VoxelTile we are currently processing.
void findAverage(T &avg) const
Determines the average value of the tile.
bool parseString(UT_WorkBuffer &v)
int int32
Definition: SYS_Types.h:39
GLenum GLint * range
Definition: glcorearb.h:1925
void setInterrupt(UT_Interrupt *interrupt)
float getVisible() const
Definition: UT_Filter.h:82
void loadData(UT_IStream &is)
Load an array, requires you have already size()d this array.
#define COUNT_NONZERO(VAL, COUNT)
UT_VoxelBorderType getBorder() const
void splitByTile(const UT_JobInfo &info)
exint getDataLength() const
Returns the amount of data used by the tile myData pointer.
UT_FromUnbounded creates a V from an unbounded array-like type.
Definition: UT_Matrix2.h:733
bool atEnd() const
Returns true if we have iterated over all of the voxels.
void match(const UT_VoxelArray< T > &src)
static UT_JID jidFromValue(const bool *)
Returns the JID that matches the given type.
virtual const char * getName()=0
void restrictToBBox(const UT_BoundingBox &bbox)
void traverseTopDownSorted(OP &op) const
void resample(const UT_VoxelArray< T > &src, UT_FilterType filtertype=UT_FILTER_POINT, float filterwidthscale=1.0f, int clampaxis=-1)
Fills this by resampling the given voxel array.
void UTswap(T &a, T &b)
Definition: UT_Swap.h:35
int64 getMemoryUsage(bool inclusive) const
Return the amount of memory used by this array.
GLboolean * data
Definition: glcorearb.h:131
constexpr SYS_FORCE_INLINE T & y() noexcept
Definition: UT_Vector4.h:493
void UTparallelForEachNumber(IntType nitems, const Body &body, const bool force_use_task_scope=true)
const GLdouble * v
Definition: glcorearb.h:837
T operator()(UT_Vector3D pos) const
int numVoxels() const
GLuint start
Definition: glcorearb.h:475
GLsizei const GLfloat * value
Definition: glcorearb.h:824
virtual T getValue(const UT_VoxelTile< T > &tile, int x, int y, int z) const =0
CompareResults OIIO_API compare(const ImageBuf &A, const ImageBuf &B, float failthresh, float warnthresh, float failrelative, float warnrelative, ROI roi={}, int nthreads=0)
T * fillCacheLine(T *cacheline, int &stride, int x, int y, int z, bool forcecopy, bool strideofone) const
static void registerCompressionEngine(UT_VoxelTileCompress< T > *engine)
fpreal myQuantizeTol
Tolerance for quantizing to reduced bit depth.
virtual bool lerp(GA_AttributeOperand &d, GA_AttributeOperand &a, GA_AttributeOperand &b, GA_AttributeOperand &t) const
d = SYSlerp(a, b, t);
#define UT_VOXEL_ALLOC(x)
Definition: UT_VoxelArray.h:52
SYS_FORCE_INLINE T * SYSconst_cast(const T *foo)
Definition: SYS_Types.h:136
GLdouble GLdouble GLdouble z
Definition: glcorearb.h:848
exint size() const
Returns the size of the shared memory, in bytes.
void traverseTopDown(Callback function, void *data) const
void reloadCache(int x, int y, int z)
UT_VoxelArray< T > * myBaseLevel
constexpr SYS_FORCE_INLINE T & z() noexcept
Definition: UT_Vector3.h:667
int64 exint
Definition: SYS_Types.h:125
UT_Vector3T< T > maxvec() const
GLint level
Definition: glcorearb.h:108
constexpr bool SYSisNan(const F f)
Definition: SYS_Math.h:184
SYS_FORCE_INLINE const char * buffer() const
void setValue(T t) const
Sets the voxel we are currently pointing to the given value.
GLboolean GLboolean GLboolean GLboolean a
Definition: glcorearb.h:1222
UT_VoxelBorderType
Definition: UT_VoxelArray.h:70
#define SYSabs(a)
Definition: SYS_Math.h:1584
UT_FilterWrap
Definition: UT_FilterType.h:40
iterator beginArray()
JSON reader class which handles parsing of JSON or bJSON files.
Definition: UT_JSONParser.h:87
bool posToIndex(UT_Vector3 pos, int &x, int &y, int &z) const
ImageBuf OIIO_API min(Image_or_Const A, Image_or_Const B, ROI roi={}, int nthreads=0)
void setConstPlusArray(const UT_VoxelArray< T > *vox)
UT_VoxelArray< T > * myArray
void setArray(UT_VoxelArray< T > *vox)
GLint y
Definition: glcorearb.h:103
int getStart() const
Definition: UT_Filter.h:79
Class which writes ASCII or binary JSON streams.
Definition: UT_JSONWriter.h:37
int myTilePos[3]
Which tile we are as per tx,ty,tz rather than linear index.
void UTparallelForLightItems(const Range &range, const Body &body, const bool force_use_task_scope=true)
void UTserialForEachNumber(IntType nitems, const Body &body, bool usetaskscope=true)
void copyWithOffset(const UT_VoxelArray< T > &src, int offx, int offy, int offz)
**But if you need a result
Definition: thread.h:622
__hostdev__ void setValue(uint32_t offset, bool v)
Definition: NanoVDB.h:5750
bool isConstant(T *cval=0) const
void makeConstant(T t)
Turns this tile into a constant tile of the given value.
bool indexToPos(int x, int y, int z, UT_Vector3F &pos) const
void toLinearBP(int k, int &x, int &y, int &z) const
UT_Matrix2T< T > SYSlerp(const UT_Matrix2T< T > &v1, const UT_Matrix2T< T > &v2, S t)
Definition: UT_Matrix2.h:675
bool setIndex(UT_VoxelArrayIterator< S > &vit)
UT_FilterType
Definition: UT_FilterType.h:16
float fpreal32
Definition: SYS_Types.h:200
void setArray(const UT_VoxelArray< T > *vox)
GLuint buffer
Definition: glcorearb.h:660
int myMinValidX
Half inclusive [,) range of valid x queries for current cache.
void flatten(S *dst, int dststride) const
Flattens ourself into the given destination buffer.
void makeFpreal16()
Explicit compress to fpreal16. Lossy. No-op if already constant.
const float * getWeights() const
Definition: UT_Filter.h:78
void size(int xres, int yres, int zres, bool reset=true)
S * extractSlice(S *dstdata, int slice, bool half_slice) const
constexpr SYS_FORCE_INLINE T & x() noexcept
Definition: UT_Vector4.h:491
int zres() const
bool jsonString(const char *value, int64 length=0)
__hostdev__ float getValue(uint32_t i) const
Definition: NanoVDB.h:5578
virtual bool writeThrough(UT_VoxelTile< T > &tile, int x, int y, int z, T t) const =0
static int getArrayID(const char *symbol)
void rewind()
Resets the iterator to point to the first voxel.
SYS_FORCE_INLINE bool extractSample(int x, int y, int z, T *sample) const
double fpreal64
Definition: SYS_Types.h:201
unsigned char uint8
Definition: SYS_Types.h:36
SYS_NO_DISCARD_RESULT SYS_FORCE_INLINE bool extractSample(int x, int y, int z, T *sample) const
bool writeThrough(int x, int y, int z, T t)
int yres() const
void setConstCubeArray(const UT_VoxelArray< T > *vox)
void moveTilesWithOffset(UT_VoxelArray< T > &src, int tileoffx, int tileoffy, int tileoffz)
void setPartialRange(int idx, int numranges)
const UT_VoxelMipMap< T > & operator=(const UT_VoxelMipMap< T > &src)
Assignment operator:
__linearTileIndexConverter(const UT_VoxelArray< T > *dst, const UT_VoxelArray< T > *src, int xoff, int yoff, int zoff)
GLdouble n
Definition: glcorearb.h:2008
const S * writeTiles(const S *srcdata, int srcstride, const UT_IntArray &tilelist)
GLfloat f
Definition: glcorearb.h:1926
GLint GLint GLsizei GLint GLenum GLenum type
Definition: glcorearb.h:108
bool hasNan() const
Returns true if any NANs are in this tile.
static const char * getToken(ArrayTokenID id)
GLintptr offset
Definition: glcorearb.h:665
SYS_FORCE_INLINE bool extractSampleAxis(int x, int y, int z, T *sample) const
void resetX(int x)
void setVoxelSize(const UT_Vector3 &voxelsize)
static UT_Filter * getFilter(UT_FilterType type)
fpreal64 laplacian(const UT_Vector3 &invvoxelsize) const
static void rotateLines(UT_VoxelProbe< T, true, false, false > &ym, UT_VoxelProbe< T, true, false, false > &y0, UT_VoxelProbe< T, true, false, false > &yp)
virtual void load(UT_IStream &is, UT_VoxelTile< T > &tile) const
void rewind()
Resets the iterator to point to the first voxel.
int64 getMemoryUsage(bool inclusive) const
Returns the amount of memory used by this tile.
int getYRes() const
void weightedSum(int pstart[3], int pend[3], const float *weights[3], int start[3], T &result)
SYS_FORCE_INLINE T lerpAxis(int x, int y, int z, float fx, float fy, float fz) const
int getLinearTileNum() const
constexpr SYS_FORCE_INLINE T & z() noexcept
Definition: UT_Vector4.h:495
void build(UT_VoxelArray< T > *baselevel, mipmaptype function)
SYS_FORCE_INLINE bool extractSamplePlus(int x, int y, int z, T *sample) const
static void saveCompressionTypes(std::ostream &os)
Stores a list of compresson engines to os.
T getBorderValue() const
PXL_API const char * getName(const ColorSpace *space)
Return the name of the color space.
Definition: VM_SIMD.h:48
#define UT_ASSERT_P(ZZ)
Definition: UT_Assert.h:155
static int mirrorCoordinates(int x, int res)
UT_API void UTsaveStringBinary(std::ostream &os, const char *str, UT_STRING_BINARY_IO minbits)
bool reset(exint size, const char *id=nullptr)
int getNTilesBP() const
Returns the number of tiles in each part.
exint read(bool *array, exint sz=1)
Definition: UT_IStream.h:276
#define SYS_FALLTHROUGH
Definition: SYS_Compiler.h:68
GLuint GLuint end
Definition: glcorearb.h:475
virtual void save(std::ostream &os, const UT_VoxelTile< T > &tile) const
static UT_SharedMemoryManager & get()
fpreal16 UTvoxelConvertFP16(fpreal16 a)
Definition: UT_VoxelArray.C:54
const UT_VoxelArray< T > & operator=(const UT_VoxelArray< T > &src)
Assignment operator:
void setArray(const UT_VoxelArray< T > *vx, const UT_VoxelArray< T > *vy, const UT_VoxelArray< T > *vz)
UT_Vector3T< T > SYSclamp(const UT_Vector3T< T > &v, const UT_Vector3T< T > &min, const UT_Vector3T< T > &max)
Definition: UT_Vector3.h:1057
Traverse an array object in the parser.
bool skipNextObject()
Simple convenience method to skip the next object in the stream.
GLint GLenum GLboolean GLsizei stride
Definition: glcorearb.h:872
void makeRawUninitialized()
Definition: VM_SIMD.h:188
GLint GLuint mask
Definition: glcorearb.h:124
UT_VoxelTile< T > * getTile(int tx, int ty, int tz) const
constexpr enabler dummy
An instance to use in EnableIf.
Definition: CLI11.h:985
OIIO_FORCEINLINE OIIO_HOSTDEVICE float madd(float a, float b, float c)
Fused multiply and add: (a*b + c)
Definition: fmath.h:421
static void releaseFilter(UT_Filter *filter)
void setCompressOnExit(bool shouldcompress)
T evaluate(const UT_Vector3 &pos, const UT_Filter &filter, fpreal radius, int clampaxis=-1) const
long long int64
Definition: SYS_Types.h:116
SYS_NO_DISCARD_RESULT SYS_FORCE_INLINE bool extractSampleAxis(int x, int y, int z, T *sample) const
bool tryCompress(const UT_VoxelCompressOptions &options)
void void addWarning(const char *fmt,...) SYS_PRINTF_CHECK_ATTRIBUTE(2
bool jsonKey(const char *value, int64 length=0)
virtual bool canSave() const
Does this engine support saving and loading?
int getRes(int dim) const
int getXRes() const
void setRes(int xr, int yr, int zr)
SYS_API fpreal32 SYSfloor(fpreal32 val)
bool setIndex(UT_VoxelArrayIterator< S > &vit)
GLuint const GLchar * name
Definition: glcorearb.h:786
virtual bool isLossless() const
Returns true if the compression type is lossless.
signed char int8
Definition: SYS_Types.h:35
bool jsonEndArray(bool newline=true)
GLboolean GLboolean GLboolean b
Definition: glcorearb.h:1222
void enlargeBounds(const UT_Vector3T< T > &min, const UT_Vector3T< T > &max)
GLint GLenum GLint x
Definition: glcorearb.h:409
void writeCacheLine(T *cacheline, int y, int z)
Fills a cache line from an external buffer into our own data.
int32 nextTask() const
GLsizei levels
Definition: glcorearb.h:2224
void advanceX()
Blindly advances our current pointer.
static void _toRegularLinear(int k, int xdim, int ydim, int &x, int &y, int &z)
void setValue(int x, int y, int z, T t)
SYS_FORCE_INLINE T lerpVoxelCoordAxis(UT_Vector3F pos) const
virtual int getDataLength(const UT_VoxelTile< T > &tile) const =0
exint append()
Definition: UT_Array.h:142
bool parseNumber(int8 &v)
Generic parsing of a number (int)
GLdouble t
Definition: glad.h:2397
GLsizei samples
Definition: glcorearb.h:1298
int getSize() const
Definition: UT_Filter.h:81
int sprintf(const char *fmt,...) SYS_PRINTF_CHECK_ATTRIBUTE(2
void getTileVoxels(UT_Vector3I &start, UT_Vector3I &end) const
This tile will iterate over the voxels indexed [start,end).
bool myAllowFP16
Conversion to fpreal16, only valid for scalar data.
void buildConstantCache(T value)
GT_API const UT_StringHolder version
SYS_FORCE_INLINE T lerpVoxelCoord(UT_Vector3F pos) const
bool setIndex(UT_VoxelArrayIterator< S > &vit)
exint entries() const
Alias of size(). size() is preferred.
Definition: UT_Array.h:655
int getZRes() const
void applyOperationCheckNoop(const OP &op, const UT_VoxelArray< S > &a)
int64 parseUniformArray(T *data, int64 len)
IFDmantra py
Definition: HDK_Image.dox:266
static UT_VoxelTileCompress< T > * getCompressionEngine(int index)
GLint j
Definition: glad.h:2733
SYS_FORCE_INLINE int strcmp(const char *src) const
GLsizeiptr size
Definition: glcorearb.h:664
GLfloat GLfloat GLfloat GLfloat h
Definition: glcorearb.h:2002
GLenum GLenum dst
Definition: glcorearb.h:1793
virtual void findMinMax(const UT_VoxelTile< T > &tile, T &min, T &max) const
Definition: UT_VoxelArray.C:73
void setLinearTile(exint lineartilenum, UT_VoxelArray< T > *array)
GLsizeiptr const void GLenum usage
Definition: glcorearb.h:664
bool hasNan() const
Returns true if any element of the voxel array is NAN.
int numJobs() const
SYS_FORCE_INLINE void lerpVoxelMinMaxAxis(T &lerp, T &lmin, T &lmax, int x, int y, int z, float fx, float fy, float fz) const
SYS_STATIC_FORCE_INLINE T lerpValues(T v1, T v2, fpreal32 bias)
Lerps two numbers, templated to work with T.
T getValue(int x, int y, int z) const
bool setIndexPlus(UT_VoxelArrayIterator< S > &vit)
SYS_FORCE_INLINE T lerpVoxel(int x, int y, int z, float fx, float fy, float fz) const
void UTparallelInvoke(bool parallel, F1 &&f1, F2 &&f2)
void copyFragment(int dstx, int dsty, int dstz, const UT_VoxelTile< T > &srctile, int srcx, int srcy, int srcz)
void uncompress()
Turns a compressed tile into a raw tile.
void reduceOperation(OP &op)
void maskedAssignOperation(const OP &op, const UT_VoxelArray< S > &a, const UT_VoxelArray< M > &mask)
short int16
Definition: SYS_Types.h:37
bool parseEndArray(bool &error)
fpreal64 fpreal
Definition: SYS_Types.h:278
UT_Vector3T< T > minvec() const
void toLinearIP(int k, int &x, int &y, int &z) const
#define SYS_FTOLERANCE_R
Definition: SYS_Types.h:284
UT_API UT_Interrupt * UTgetInterrupt()
Obtain global UT_Interrupt singleton.
void forEachTile(const OP &op, bool shouldthread=true)
GLuint index
Definition: glcorearb.h:786
#define UT_VERIFY_P(expr)
Definition: UT_Assert.h:210
float getSupport() const
Definition: UT_Filter.h:164
constexpr SYS_FORCE_INLINE T & w() noexcept
Definition: UT_Vector4.h:497
v4uu splitFloat()
Definition: VM_SIMD.h:327
void saveData(std::ostream &os) const
UT_ValArray< UT_VoxelArray< T > ** > myLevels
GLuint GLfloat * val
Definition: glcorearb.h:1608
ImageBuf OIIO_API max(Image_or_Const A, Image_or_Const B, ROI roi={}, int nthreads=0)
SYS_FORCE_INLINE void lerpVoxelCoordMinMax(T &lerp, T &lmin, T &lmax, UT_Vector3F pos) const
bool jsonBeginArray()
Begin a generic array object.
SYS_FORCE_INLINE void initBounds()
int64 getMemoryUsage(bool inclusive) const
Return the amount of memory used by this mipmap.
const char * id() const
if(num_boxed_items<=0)
Definition: UT_RTreeImpl.h:697
bool parseBeginArray(bool &error)
static void swapLines(UT_VoxelProbe< T, true, false, false > &ym, UT_VoxelProbe< T, true, false, false > &yp)
void save(std::ostream &os) const
void maskedApplyOperation(const OP &op, const UT_VoxelArray< M > &mask)
SYS_FORCE_INLINE v4uf swizzle() const
Definition: VM_SIMD.h:335
static int getTileID(const char *symbol)
UT_VoxelTile< T > * getLinearTile(int idx) const
void load(UT_IStream &is, const UT_IntArray &compression)
static int lookupCompressionEngine(const char *name)
GLubyte GLubyte GLubyte GLubyte w
Definition: glcorearb.h:857
#define UT_ASSERT(ZZ)
Definition: UT_Assert.h:156
bool readChar(char &result)
Definition: UT_IStream.h:382
SYS_FORCE_INLINE void lerpVoxelMinMax(T &lerp, T &lmin, T &lmax, int x, int y, int z, float fx, float fy, float fz) const
bool setIndexCube(UT_VoxelArrayIterator< S > &vit)
bool jsonUniformArray(int64 length, const int8 *value)
Efficent method of writing a uniform array of int8 values.
void uncompressFull()
Turns a tile into a raw full tile.
bool endUniformArray(int64 *nwritten=0)
void setBorder(UT_VoxelBorderType type, T t)
SYS_FORCE_INLINE T operator()(int x, int y, int z) const
void assignOperation(const OP &op, const UT_VoxelArray< S > &a)
virtual bool tryCompress(UT_VoxelTile< T > &tile, const UT_VoxelCompressOptions &options, T min, T max) const =0
SYS_FORCE_INLINE void lerpVoxelCoordMinMaxAxis(T &lerp, T &lmin, T &lmax, UT_Vector3F pos) const
static void expandMinMax(T v, T &min, T &max)
Designed to be specialized according to T.
void assignOperation(const OP &op, const UT_VoxelArray< S > &a)
constexpr SYS_FORCE_INLINE T & y() noexcept
Definition: UT_Vector3.h:665
bool SYSisEqual(const UT_Vector2T< T > &a, const UT_Vector2T< T > &b, S tol=SYS_FTOLERANCE)
Componentwise equality.
Definition: UT_Vector2.h:674
void findMinMax(T &min, T &max) const
Finds the minimum and maximum T values.
constexpr SYS_FORCE_INLINE T maxComponent() const noexcept
Definition: UT_Vector3.h:408
T avgNonZero(const UT_Vector3 &pos, const UT_Filter &filter, fpreal radius, int clampaxis=-1) const
average of non-zero values of the voxel array.
#define SYSmin(a, b)
Definition: SYS_Math.h:1583
SYS_FORCE_INLINE T lerpVoxelAxis(int x, int y, int z, float fx, float fy, float fz) const
Declare prior to use.
void setBorderScale(T scalex, T scaley, T scalez)
void avgNonZero(int pstart[3], int pend[3], int start[3], T &result)
int xres() const
Read the current resolution.
fpreal64 curvature(const UT_Vector3 &invvoxelsize) const
S * extractTiles(S *dstdata, int stride, const UT_IntArray &tilelist) const
bool getLowerKey(T &key)
Get a lower case map key (for case insensitive maps)
void writeData(const S *src, int srcstride)
bool isSimpleCompression() const
bool reduceOperation(OP &op)
SYS_FORCE_INLINE bool extractSamplePlus(int x, int y, int z, T *sample) const
GLint GLsizei count
Definition: glcorearb.h:405
bool isConstant() const
Returns if this tile is constant.
SYS_FORCE_INLINE T lerpSampleAxis(T *samples, float fx, float fy, float fz) const
Definition: format.h:1821
static void loadCompressionTypes(UT_IStream &is, UT_IntArray &compressions)
int getTileRes(int dim) const
SYS_API fpreal32 SYSceil(fpreal32 val)
void evaluateMinMax(T &lerp, T &lmin, T &lmax, UT_Vector3F pos) const
bool jsonInt(int32 value)
Write an integer value.
void writeCacheLine()
bool operator<(const ut_VoxelMipMapSortCompare &lhs, const ut_VoxelMipMapSortCompare &rhs)
int job() const
void flattenPartialAxis(T *flatarray, exint ystride, const UT_JobInfo &info) const
void advance()
Advances the iterator to point to the next voxel.
void applyOperation(const OP &op)
SYS_FORCE_INLINE T lerp(int x, int y, int z, float fx, float fy, float fz) const
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
Definition: glcorearb.h:1297
GLenum src
Definition: glcorearb.h:1793
constexpr SYS_FORCE_INLINE T & x() noexcept
Definition: UT_Vector3.h:663