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 
2811  ok = ok && w.jsonBeginArray();
2812  ok = ok && w.jsonKey(UT_VoxelArrayJSON::getToken(
2814  ok = ok && w.jsonInt(type);
2815  ok = ok && w.jsonKey(UT_VoxelArrayJSON::getToken(
2817 
2818  ok = ok && w.beginUniformArray(xres()*yres()*zres()*tuple_size,
2819  w.jid<ScalarType>());
2820  for (int z = 0; z < zres(); z++)
2821  for (int y = 0; y < yres(); y++)
2822  for (int x = 0; x < xres(); x++)
2823  {
2824  value = (*this)(x, y, z);
2825  if constexpr (tuple_size == 1)
2826  {
2827  ok = ok && w.uniformWrite(value);
2828  }
2829  else
2830  {
2831  for (int i = 0; i < tuple_size; i++)
2832  {
2833  ok = ok && w.uniformWrite(value(i));
2834  }
2835  }
2836  }
2837  ok = ok && w.endUniformArray();
2838  ok = ok && w.jsonEndArray();
2839  return ok;
2840  }
2841 
2842  int len;
2843  char type = myCompressionType;
2844 
2845  UT_ASSERT(type >= 0 && type < COMPRESS_ENGINE);
2846  ok = ok && w.jsonBeginArray();
2847  ok = ok && w.jsonKey(UT_VoxelArrayJSON::getToken(
2849  ok = ok && w.jsonInt(type);
2850  ok = ok && w.jsonKey(UT_VoxelArrayJSON::getToken(
2852 
2853  switch (myCompressionType)
2854  {
2855  case COMPRESS_RAW:
2856  len = myRes[2] * myRes[1] * myRes[0];
2857  ok = ok && w.jsonUniformArray(len * tuple_size, (ScalarType *)myData);
2858  break;
2859 
2860  case COMPRESS_FPREAL16:
2861  len = myRes[2] * myRes[1] * myRes[0] * UT_FixedVectorTraits<T>::TupleSize;
2862  ok = ok && w.jsonUniformArray(len, (fpreal16 *)myData);
2863  break;
2864 
2865  case COMPRESS_RAWFULL:
2866  len = TILESIZE * TILESIZE * myRes[2];
2867  ok = ok && w.jsonUniformArray(len * tuple_size, (ScalarType *)myData);
2868  break;
2869 
2870  case COMPRESS_CONSTANT:
2871  if constexpr (tuple_size == 1)
2872  ok = ok && w.jsonValue(rawConstVal());
2873  else
2874  ok = ok && w.jsonUniformArray(tuple_size, rawConstVal().data());
2875  break;
2876  }
2877 
2878  ok = ok && w.jsonEndArray();
2879  return ok;
2880 }
2881 
2882 template <typename T>
2883 bool
2885 {
2886  constexpr exint tuple_size = UT_FixedVectorTraits<T>::TupleSize;
2887  using ScalarType = typename UT_FixedVectorTraits<T>::DataType;
2888 
2890  UT_WorkBuffer key;
2891  int8 type, otype;
2892  int len;
2893 
2894  it = p.beginArray();
2895  if (it.atEnd() || !it.getLowerKey(key))
2896  return false;
2897  if (UT_VoxelArrayJSON::getTileID(key.buffer()) !=
2899  return false;
2900  if (!p.parseNumber(type))
2901  return false;
2902 
2903  ++it;
2904 
2905  if (it.atEnd() || !it.getLowerKey(key))
2906  return false;
2907  if (UT_VoxelArrayJSON::getTileID(key.buffer()) !=
2909  return false;
2910 
2911  // Perform the indirection to find out our native type.
2912  if (type >= 0 && type < compress.entries())
2913  {
2914  otype = type;
2915  type = compress(type);
2916  if (type == -1)
2917  {
2918  std::cerr << "Missing compression engine " << (int) otype << "\n";
2919  }
2920  }
2921 
2922  if (type >= COMPRESS_ENGINE)
2923  {
2924  freeData();
2925  myCompressionType = type;
2926 
2927  if (type - COMPRESS_ENGINE >= getCompressionEngines().entries())
2928  {
2929  // Invalid type!
2930  std::cerr << "Invalid compression engine " << (int) otype << "\n";
2931  return false;
2932  }
2933 
2934  UT_VoxelTileCompress<T> *engine = getCompressionEngine(myCompressionType);
2935 
2936  bool engine_ok = engine->load(p, *this);
2937  ++it;
2938  return it.atEnd() && engine_ok;
2939  }
2940 
2941  UT_ASSERT(type >= 0);
2942  if (type < 0)
2943  {
2944  return false;
2945  }
2946 
2947  freeData();
2948 
2949  myCompressionType = type;
2950 
2951  switch (myCompressionType)
2952  {
2953  case COMPRESS_RAW:
2954  len = myRes[2] * myRes[1] * myRes[0];
2955  myData = UT_VOXEL_ALLOC(sizeof(T) * len);
2956  len *= tuple_size;
2957  if (p.parseUniformArray((ScalarType *)myData, len) != len)
2958  return false;
2959  break;
2960 
2961  case COMPRESS_FPREAL16:
2962  len = myRes[2] * myRes[1] * myRes[0] * UT_FixedVectorTraits<T>::TupleSize;
2963  myData = UT_VOXEL_ALLOC(sizeof(fpreal16) * len);
2964  if (p.parseUniformArray((fpreal16 *)myData, len) != len)
2965  return false;
2966  break;
2967 
2968  case COMPRESS_RAWFULL:
2969  len = TILESIZE * TILESIZE * myRes[2];
2970  myData = UT_VOXEL_ALLOC(sizeof(T) * len);
2971  len *= tuple_size;
2972  if (p.parseUniformArray((ScalarType *)myData, len) != len)
2973  return false;
2974  break;
2975 
2976  case COMPRESS_CONSTANT:
2977  if (!inlineConstant())
2978  myData = UT_VOXEL_ALLOC(sizeof(T));
2979  if constexpr (tuple_size == 1)
2980  {
2981  if (!p.parseNumber(*rawConstData()))
2982  return false;
2983  }
2984  else
2985  {
2986  if (!p.parseUniformArray((ScalarType *) rawConstData()->data(), tuple_size))
2987  return false;
2988  }
2989  break;
2990  }
2991 
2992  ++it;
2993  return it.atEnd();
2994 }
2995 
2996 template <typename T>
2997 void
2999 {
3000  int16 ntype = getCompressionEngines().entries();
3001  int i;
3002 
3003  ntype += COMPRESS_ENGINE;
3004 
3005  UTwrite(os, &ntype);
3006 
3007  UT_ASSERT(COMPRESS_ENGINE == 4); // Change lower lines!
3009  UTsaveStringBinary(os, "rawfull", UT_STRING_8BIT_IO);
3010  UTsaveStringBinary(os, "constant", UT_STRING_8BIT_IO);
3011  UTsaveStringBinary(os, "fpreal16", UT_STRING_8BIT_IO);
3012 
3013  ntype -= COMPRESS_ENGINE;
3014  for (i = 0; i < ntype; i++)
3015  {
3016  UTsaveStringBinary(os, getCompressionEngines()(i)->getName(), UT_STRING_8BIT_IO);
3017  }
3018 }
3019 
3020 template <typename T>
3021 void
3023 {
3024  int16 ntype;
3025  int i, idx;
3026 
3027  compress.entries(0);
3028 
3029  is.read(&ntype);
3030 
3031  for (i = 0; i < ntype; i++)
3032  {
3033  UT_String name;
3034 
3036  if (name == "raw")
3037  compress.append(COMPRESS_RAW);
3038  else if (name == "rawfull")
3039  compress.append(COMPRESS_RAWFULL);
3040  else if (name == "constant")
3041  compress.append(COMPRESS_CONSTANT);
3042  else if (name == "fpreal16")
3043  compress.append(COMPRESS_FPREAL16);
3044  else
3045  {
3046  idx = lookupCompressionEngine(name);
3047 
3048  // -1 means a failure to find it in our set..
3049  // this is only a bad thing if a tile actually uses this engine.
3050 
3051  compress.append(idx);
3052  }
3053  }
3054 }
3055 
3056 template <typename T>
3057 bool
3059 {
3060  int16 ntype = getCompressionEngines().entries();
3061  int i;
3062  bool ok = true;
3063 
3064  ntype += COMPRESS_ENGINE;
3065 
3066  UT_ASSERT(COMPRESS_ENGINE == 4); // Change lower lines!
3067  ok = ok && w.beginUniformArray(ntype, UT_JID_STRING);
3068  ok = ok && w.uniformWrite("raw");
3069  ok = ok && w.uniformWrite("rawfull");
3070  ok = ok && w.uniformWrite("constant");
3071  ok = ok && w.uniformWrite("fpreal16");
3072 
3073  ntype -= COMPRESS_ENGINE;
3074  for (i = 0; i < ntype; i++)
3075  {
3076  ok = ok && w.uniformWrite(getCompressionEngines()(i)->getName());
3077  }
3078 
3079  ok = ok && w.endUniformArray();
3080  return ok;
3081 }
3082 
3083 template <typename T>
3084 bool
3086 {
3089  int idx;
3090 
3091  compress.entries(0);
3092  for (it = p.beginArray(); !it.atEnd(); ++it)
3093  {
3094  if (!p.parseString(buffer))
3095  return false;
3096 
3097  if (!buffer.strcmp("raw"))
3098  compress.append(COMPRESS_RAW);
3099  else if (!buffer.strcmp("rawfull"))
3100  compress.append(COMPRESS_RAWFULL);
3101  else if (!buffer.strcmp("constant"))
3102  compress.append(COMPRESS_CONSTANT);
3103  else if (!buffer.strcmp("fpreal16"))
3104  compress.append(COMPRESS_FPREAL16);
3105  else
3106  {
3107  idx = lookupCompressionEngine(buffer.buffer());
3108 
3109  // -1 means a failure to find it in our set..
3110  // this is only a bad thing if a tile actually uses this engine.
3111 
3112  compress.append(idx);
3113  }
3114  }
3115  return true;
3116 }
3117 
3118 
3119 
3120 //
3121 // VoxelArray definitions.
3122 //
3123 
3124 template <typename T>
3126 {
3127  myRes[0] = 0;
3128  myRes[1] = 0;
3129  myRes[2] = 0;
3130  myTileRes[0] = 0;
3131  myTileRes[1] = 0;
3132  myTileRes[2] = 0;
3133 
3134  myTiles = 0;
3135 
3136  myInvRes = 1;
3137 
3138  myBorderValue = 0;
3139  myBorderScale[0] = 0;
3140  myBorderScale[1] = 0;
3141  myBorderScale[2] = 0;
3142  myBorderType = UT_VOXELBORDER_STREAK;
3143 
3144  mySharedMem = 0;
3145  mySharedMemView = 0;
3146 }
3147 
3148 template <typename T>
3150 {
3151  deleteVoxels();
3152 }
3153 
3154 template <typename T>
3156 {
3157  myRes[0] = 0;
3158  myRes[1] = 0;
3159  myRes[2] = 0;
3160  myInvRes = 1;
3161  myTileRes[0] = 0;
3162  myTileRes[1] = 0;
3163  myTileRes[2] = 0;
3164 
3165  myTiles = 0;
3166 
3167  mySharedMem = 0;
3168  mySharedMemView = 0;
3169 
3170  *this = src;
3171 }
3172 
3173 template <typename T>
3174 void
3176  const UT_JobInfo &info)
3177 {
3178  UT_ASSERT(isMatching(src));
3179  UT_VoxelArrayIterator<T> vit(this);
3180  vit.splitByTile(info);
3181  for (vit.rewind(); !vit.atEnd(); vit.advanceTile())
3182  {
3183  int i = vit.getLinearTileNum();
3184  myTiles[i] = src.myTiles[i];
3185  }
3186 }
3187 
3188 template <typename T>
3189 const UT_VoxelArray<T> &
3191 {
3192  // Paranoid code:
3193  if (&src == this)
3194  return *this;
3195 
3196  myBorderScale[0] = src.myBorderScale[0];
3197  myBorderScale[1] = src.myBorderScale[1];
3198  myBorderScale[2] = src.myBorderScale[2];
3199  myBorderValue = src.myBorderValue;
3200  myBorderType = src.myBorderType;
3201 
3202  myCompressionOptions = src.myCompressionOptions;
3203 
3204  // Allocate proper size; don't bother setting to zero, since we'll copy the
3205  // data immediately after.
3206  size(src.myRes[0], src.myRes[1], src.myRes[2], false);
3207 
3208  copyData(src);
3209 
3210  return *this;
3211 }
3212 
3213 // If defined, construction and destruction of tiles will be multithreaded.
3214 // Otherwise, new[] and delete[] take care of calling these methods.
3215 // Unfortunately, this doesn't work at the moment...
3216 #define __MULTITHREADED_STRUCTORS__
3217 template <typename T>
3218 void
3220 {
3221 #ifdef __MULTITHREADED_STRUCTORS__
3222  // Free up each tile's heap data, then bulk-deallocate the array.
3224  [&](const UT_BlockedRange<int>& range)
3225  {
3226  for(int i = range.begin(); i < range.end(); i++)
3227  {
3228  myTiles[i].~UT_VoxelTile<T>();
3229  }
3230  });
3231  operator delete(myTiles, std::nothrow);
3232 #else
3233  delete [] myTiles;
3234 #endif
3235  myTiles = 0;
3236 
3237  myTileRes[0] = myTileRes[1] = myTileRes[2] = 0;
3238  myRes[0] = myRes[1] = myRes[2] = 0;
3239 
3240  delete mySharedMemView;
3241  mySharedMemView = 0;
3242 
3243  delete mySharedMem;
3244  mySharedMem = 0;
3245 }
3246 
3247 template <typename T>
3248 void
3249 UT_VoxelArray<T>::size(int xres, int yres, int zres, bool zero)
3250 {
3251  // Check if the sizes are already right.
3252  if (myRes[0] == xres && myRes[1] == yres && myRes[2] == zres)
3253  {
3254  if (zero)
3255  {
3256  T tzero;
3257  tzero = 0;
3258  constant(tzero);
3259  }
3260  return;
3261  }
3262 
3263  deleteVoxels();
3264 
3265  // Initialize our tiles.
3266  int tile_res[3];
3267  tile_res[0] = (xres + TILEMASK) >> TILEBITS;
3268  tile_res[1] = (yres + TILEMASK) >> TILEBITS;
3269  tile_res[2] = (zres + TILEMASK) >> TILEBITS;
3270 
3271  exint ntiles = ((exint)tile_res[0]) * tile_res[1] * tile_res[2];
3272  if (ntiles)
3273  {
3274 #ifdef __MULTITHREADED_STRUCTORS__
3275  // Allocate the big array of the right size, then use placement new to
3276  // construct each tile.
3277  // Note that it is not guaranteed that the returned pointer is properly
3278  // aligned with raw operator new. However, we only practically need 8-byte
3279  // alignment given the current implementation, which should be respected by
3280  // operator new.
3281  // We have to wait for C++17 for version of operator new that accepts
3282  // alignment requirements...
3283  myTiles = (UT_VoxelTile<T>*) operator new(sizeof(UT_VoxelTile<T>) * ntiles,
3284  std::nothrow);
3286  [&](const UT_BlockedRange<int>& range)
3287  {
3288  for(int k = range.begin(); k < range.end(); k++)
3289  {
3290  new(myTiles + k) UT_VoxelTile<T>();
3291 
3292  // Set the resolution of this tile.
3293  int tilex = k % tile_res[0];
3294  int k2 = k / tile_res[0];
3295  int tiley = k2 % tile_res[1];
3296  int tilez = k2 / tile_res[1];
3297  myTiles[k].setRes(SYSmin(TILESIZE, xres - tilex * TILESIZE),
3298  SYSmin(TILESIZE, yres - tiley * TILESIZE),
3299  SYSmin(TILESIZE, zres - tilez * TILESIZE));
3300  }
3301  });
3302 #else
3303  myTiles = new UT_VoxelTile<T>[ntiles];
3304 #endif
3305 
3306  // Only set resolutions *AFTER* we successfully allocate
3307  myRes[0] = xres;
3308  myRes[1] = yres;
3309  myRes[2] = zres;
3310 
3311  myInvRes = 1;
3312  if (xres)
3313  myInvRes.x() = 1.0f / myRes[0];
3314  if (yres)
3315  myInvRes.y() = 1.0f / myRes[1];
3316  if (zres)
3317  myInvRes.z() = 1.0f / myRes[2];
3318 
3319  myTileRes[0] = tile_res[0];
3320  myTileRes[1] = tile_res[1];
3321  myTileRes[2] = tile_res[2];
3322 
3323 #ifndef __MULTITHREADED_STRUCTORS__
3324  int i = 0;
3325  for (int tz = 0; tz < myTileRes[2]; tz++)
3326  {
3327  int zr;
3328  if (tz < myTileRes[2]-1)
3329  zr = TILESIZE;
3330  else
3331  zr = zres - tz * TILESIZE;
3332 
3333  for (int ty = 0; ty < myTileRes[1]; ty++)
3334  {
3335  int yr;
3336  if (ty < myTileRes[1]-1)
3337  yr = TILESIZE;
3338  else
3339  yr = yres - ty * TILESIZE;
3340 
3341  int tx, xr = TILESIZE;
3342  for (tx = 0; tx < myTileRes[0]-1; tx++)
3343  {
3344  myTiles[i].setRes(xr, yr, zr);
3345 
3346  i++;
3347  }
3348  xr = xres - tx * TILESIZE;
3349  myTiles[i].setRes(xr, yr, zr);
3350  i++;
3351  }
3352  }
3353 #endif
3354  }
3355  else
3356  myTiles = 0;
3357 }
3358 
3359 template <typename T>
3360 void
3362 {
3363  // This call will only actually resize if the sizes are different. If it
3364  // does not resize, contents will be left alone.
3365  size(src.getXRes(), src.getYRes(), src.getZRes(), false);
3366 
3367  // Update border conditions.
3368  myBorderType = src.myBorderType;
3369  myBorderScale[0] = src.myBorderScale[0];
3370  myBorderScale[1] = src.myBorderScale[1];
3371  myBorderScale[2] = src.myBorderScale[2];
3372  myBorderValue = src.myBorderValue;
3373 
3374  // Match our compression tolerances
3375  myCompressionOptions = src.myCompressionOptions;
3376 }
3377 
3378 template <typename T>
3379 int64
3381 {
3382  int64 mem = inclusive ? sizeof(*this) : 0;
3383 
3384  int ntiles = numTiles();
3385  for (int i = 0; i < ntiles; i++)
3386  mem += myTiles[i].getMemoryUsage(true);
3387 
3388  if (mySharedMem)
3389  mem += mySharedMem->getMemoryUsage(true);
3390 
3391  if (mySharedMemView)
3392  mem += mySharedMemView->getMemoryUsage(true);
3393 
3394  return mem;
3395 }
3396 
3397 template <typename T>
3398 void
3400 {
3401  UT_VoxelArrayIterator<T> vit(this);
3402  // We don't want split tile as we update the actual tiles here
3403  // so will have false-sharing if we interleaved.
3404  vit.setPartialRange(info.job(), info.numJobs());
3405  for (vit.rewind(); !vit.atEnd(); vit.advanceTile())
3406  {
3407  int i = vit.getLinearTileNum();
3408  myTiles[i].makeConstant(t);
3409  }
3410 }
3411 
3412 template <typename T>
3413 bool
3415 {
3416  int i, ntiles;
3417  T cval;
3418  cval = 0;
3419  const fpreal tol = SYS_FTOLERANCE_R;
3420 
3421  ntiles = numTiles();
3422  for (i = 0; i < ntiles; i++)
3423  {
3424  if (!myTiles[i].isConstant())
3425  {
3426  return false;
3427  }
3428 
3429  if (!i)
3430  {
3431  // First tile, get the constant value.
3432  cval = myTiles[i].rawConstVal();
3433  }
3434  else
3435  {
3436  // See if we have deviated too much.
3437  if (UT_VoxelTile<T>::dist(cval, myTiles[i].rawConstVal()) > tol)
3438  {
3439  return false;
3440  }
3441  }
3442  }
3443 
3444  // All tiles are both constant and within tolerance of each
3445  // other. Write out our constant value and return true.
3446  if (t)
3447  *t = cval;
3448 
3449  return true;
3450 }
3451 
3452 template <typename T>
3453 bool
3455 {
3456  int i, ntiles;
3457 
3458  ntiles = numTiles();
3459  for (i = 0; i < ntiles; i++)
3460  {
3461  if (myTiles[i].hasNan())
3462  {
3463  return true;
3464  }
3465  }
3466 
3467  return false;
3468 }
3469 
3470 template <typename T>
3471 T
3473 {
3474  // We go from the position in the unit cube into the index.
3475  // The center of cells must map to the exact integer indices.
3476  pos.x() *= myRes[0];
3477  pos.y() *= myRes[1];
3478  pos.z() *= myRes[2];
3479  pos.x() -= 0.5;
3480  pos.y() -= 0.5;
3481  pos.z() -= 0.5;
3482 
3483  return lerpVoxelCoord(pos);
3484 }
3485 
3486 template <typename T>
3487 T
3489 {
3490  int x, y, z;
3491  // Yes, these have to be 32 becaues split float requires 32!
3492  fpreal32 fx, fy, fz;
3493 
3494  splitVoxelCoord(pos, x, y, z, fx, fy, fz);
3495 
3496  return lerpVoxel(x, y, z, fx, fy, fz);
3497 }
3498 
3499 template <typename T>
3500 template <int AXIS2D>
3501 T
3503 {
3504  int x, y, z;
3505  // Yes, these have to be 32 becaues split float requires 32!
3506  fpreal32 fx, fy, fz;
3507 
3508  splitVoxelCoordAxis<AXIS2D>(pos, x, y, z, fx, fy, fz);
3509 
3510  return lerpVoxelAxis<AXIS2D>(x, y, z, fx, fy, fz);
3511 }
3512 
3513 template <typename T>
3514 T
3515 UT_VoxelArray<T>::lerpVoxel(int x, int y, int z,
3516  float fx, float fy, float fz) const
3517 {
3518  // Do trilinear interpolation.
3519  T vx, vx1, vy, vy1, vz;
3520 
3521  // Optimization for common cases (values are within the voxel range and
3522  // are all within the same tile)
3523  if ( !((x | y | z) < 0) &&
3524  (((x - myRes[0]+1) & (y - myRes[1]+1) & (z - myRes[2]+1)) < 0))
3525 
3526  // (x > 0) && (y > 0) && (z > 0) &&
3527  // Do not use x+1 < foo as if x is MAX_INT that will falsely
3528  // report in bounds!
3529  // (x < myRes[0]-1) && (y < myRes[1]-1) && (z < myRes[2]-1) )
3530  {
3531  int xm, ym, zm;
3532 
3533  xm = x & TILEMASK;
3534  ym = y & TILEMASK;
3535  zm = z & TILEMASK;
3536 
3537  if ((xm != TILEMASK) && (ym != TILEMASK) && (zm != TILEMASK))
3538  {
3539  const UT_VoxelTile<T> *tile =
3540  getTile(x >> TILEBITS, y >> TILEBITS, z >> TILEBITS);
3541 
3542  vz = tile->lerp(xm, ym, zm, fx, fy, fz);
3543  }
3544  else
3545  {
3546  // We cross tile boundaries but we remain within
3547  // the voxel grid. We can thus avoid any bounds checking
3548  // and use operator() rather than getValue.
3549 
3550  // Lerp x:x+1, y, z
3551  vx = UT_VoxelTile<T>::lerpValues((*this)(x, y, z),
3552  (*this)(x+1, y, z),
3553  fx);
3554  // Lerp x:x+1, y+1, z
3555  vx1= UT_VoxelTile<T>::lerpValues((*this)(x, y+1, z),
3556  (*this)(x+1, y+1, z),
3557  fx);
3558  // Lerp x:x+1, y:y+1, z
3559  vy = UT_VoxelTile<T>::lerpValues(vx, vx1, fy);
3560 
3561  // Lerp x:x+1, y, z+1
3562  vx = UT_VoxelTile<T>::lerpValues((*this)(x, y, z+1),
3563  (*this)(x+1, y, z+1),
3564  fx);
3565  // Lerp x:x+1, y+1, z+1
3566  vx1= UT_VoxelTile<T>::lerpValues((*this)(x, y+1, z+1),
3567  (*this)(x+1, y+1, z+1),
3568  fx);
3569 
3570  // Lerp x:x+1, y:y+1, z+1
3571  vy1 = UT_VoxelTile<T>::lerpValues(vx, vx1, fy);
3572 
3573  // Lerp x:x+1, y:y+1, z:z+1
3574  vz = UT_VoxelTile<T>::lerpValues(vy, vy1, fz);
3575  }
3576  }
3577  else
3578  {
3579  // Lerp x:x+1, y, z
3580  vx = UT_VoxelTile<T>::lerpValues(getValue(x, y, z),
3581  getValue(x+1, y, z),
3582  fx);
3583  // Lerp x:x+1, y+1, z
3584  vx1= UT_VoxelTile<T>::lerpValues(getValue(x, y+1, z),
3585  getValue(x+1, y+1, z),
3586  fx);
3587 
3588  // Lerp x:x+1, y:y+1, z
3589  vy = UT_VoxelTile<T>::lerpValues(vx, vx1, fy);
3590 
3591  // Lerp x:x+1, y, z+1
3592  vx = UT_VoxelTile<T>::lerpValues(getValue(x, y, z+1),
3593  getValue(x+1, y, z+1),
3594  fx);
3595  // Lerp x:x+1, y+1, z+1
3596  vx1= UT_VoxelTile<T>::lerpValues(getValue(x, y+1, z+1),
3597  getValue(x+1, y+1, z+1),
3598  fx);
3599 
3600  // Lerp x:x+1, y:y+1, z+1
3601  vy1 = UT_VoxelTile<T>::lerpValues(vx, vx1, fy);
3602 
3603  // Lerp x:x+1, y:y+1, z:z+1
3604  vz = UT_VoxelTile<T>::lerpValues(vy, vy1, fz);
3605  }
3606 
3607  return vz;
3608 }
3609 
3610 template <typename T>
3611 template <int AXIS2D>
3612 T
3614  float fx, float fy, float fz) const
3615 {
3616  // Do bilinear interpolation.
3617  T vx, vx1, vy, vy1, vz;
3618 
3619  int lesscomp = 0, greatercomp = -1;
3620 
3621  if (AXIS2D != 0)
3622  {
3623  lesscomp |= x;
3624  greatercomp &= (x - myRes[0]+1);
3625  }
3626  if (AXIS2D != 1)
3627  {
3628  lesscomp |= y;
3629  greatercomp &= (y - myRes[1]+1);
3630  }
3631  if (AXIS2D != 2)
3632  {
3633  lesscomp |= z;
3634  greatercomp &= (z - myRes[2]+1);
3635  }
3636 
3637  // Optimization for common cases (values are within the voxel range and
3638  // are all within the same tile)
3639  if ( !(lesscomp < 0) && (greatercomp < 0) )
3640  {
3641  int xm, ym, zm;
3642 
3643  xm = x & TILEMASK;
3644  ym = y & TILEMASK;
3645  zm = z & TILEMASK;
3646 
3647  if ((AXIS2D == 0 || xm != TILEMASK) &&
3648  (AXIS2D == 1 || ym != TILEMASK) &&
3649  (AXIS2D == 2 || zm != TILEMASK))
3650  {
3651  const UT_VoxelTile<T> *tile =
3652  getTile( (AXIS2D == 0) ? 0 : (x >> TILEBITS),
3653  (AXIS2D == 1) ? 0 : (y >> TILEBITS),
3654  (AXIS2D == 2) ? 0 : (z >> TILEBITS) );
3655 
3656  vz = tile->template lerpAxis<AXIS2D>(xm, ym, zm, fx, fy, fz);
3657  }
3658  else
3659  {
3660  // We cross tile boundaries but we remain within
3661  // the voxel grid. We can thus avoid any bounds checking
3662  // and use operator() rather than getValue.
3663 
3664  // Lerp x:x+1, y, z
3665  if (AXIS2D != 0)
3666  vx = UT_VoxelTile<T>::lerpValues((*this)(x, y, z),
3667  (*this)(x+1, y, z),
3668  fx);
3669  else
3670  vx = (*this)(x, y, z);
3671 
3672  if (AXIS2D != 1)
3673  {
3674  // Lerp x:x+1, y+1, z
3675  if (AXIS2D != 0)
3676  vx1= UT_VoxelTile<T>::lerpValues((*this)(x, y+1, z),
3677  (*this)(x+1, y+1, z),
3678  fx);
3679  else
3680  vx1 = (*this)(x, y+1, z);
3681  // Lerp x:x+1, y:y+1, z
3682  vy = UT_VoxelTile<T>::lerpValues(vx, vx1, fy);
3683  }
3684  else
3685  vy = vx;
3686 
3687  if (AXIS2D != 2)
3688  {
3689  // Lerp x:x+1, y, z+1
3690  if (AXIS2D != 0)
3691  vx = UT_VoxelTile<T>::lerpValues((*this)(x, y, z+1),
3692  (*this)(x+1, y, z+1),
3693  fx);
3694  else
3695  vx = (*this)(x, y, z+1);
3696 
3697  if (AXIS2D != 1)
3698  {
3699  // Lerp x:x+1, y+1, z+1
3700  if (AXIS2D != 0)
3701  vx1= UT_VoxelTile<T>::lerpValues((*this)(x, y+1, z+1),
3702  (*this)(x+1, y+1, z+1),
3703  fx);
3704  else
3705  vx1 = (*this)(x, y+1, z+1);
3706  // Lerp x:x+1, y:y+1, z+1
3707  vy1 = UT_VoxelTile<T>::lerpValues(vx, vx1, fy);
3708  }
3709  else
3710  vy1 = vx;
3711 
3712  // Lerp x:x+1, y:y+1, z:z+1
3713  vz = UT_VoxelTile<T>::lerpValues(vy, vy1, fz);
3714  }
3715  else
3716  vz = vy;
3717  }
3718  }
3719  else
3720  {
3721  // Lerp x:x+1, y, z
3722  if (AXIS2D != 0)
3723  vx = UT_VoxelTile<T>::lerpValues(getValue(x, y, z),
3724  getValue(x+1, y, z),
3725  fx);
3726  else
3727  vx = getValue(x, y, z);
3728 
3729  if (AXIS2D != 1)
3730  {
3731  // Lerp x:x+1, y+1, z
3732  if (AXIS2D != 0)
3733  vx1= UT_VoxelTile<T>::lerpValues(getValue(x, y+1, z),
3734  getValue(x+1, y+1, z),
3735  fx);
3736  else
3737  vx1 = getValue(x, y+1, z);
3738  // Lerp x:x+1, y:y+1, z
3739  vy = UT_VoxelTile<T>::lerpValues(vx, vx1, fy);
3740  }
3741  else
3742  vy = vx;
3743 
3744  if (AXIS2D != 2)
3745  {
3746  // Lerp x:x+1, y, z+1
3747  if (AXIS2D != 0)
3748  vx = UT_VoxelTile<T>::lerpValues(getValue(x, y, z+1),
3749  getValue(x+1, y, z+1),
3750  fx);
3751  else
3752  vx = getValue(x, y, z+1);
3753 
3754  if (AXIS2D != 1)
3755  {
3756  // Lerp x:x+1, y+1, z+1
3757  if (AXIS2D != 0)
3758  vx1= UT_VoxelTile<T>::lerpValues(getValue(x, y+1, z+1),
3759  getValue(x+1, y+1, z+1),
3760  fx);
3761  else
3762  vx1 = getValue(x, y+1, z+1);
3763  // Lerp x:x+1, y:y+1, z+1
3764  vy1 = UT_VoxelTile<T>::lerpValues(vx, vx1, fy);
3765  }
3766  else
3767  vy1 = vx;
3768 
3769  // Lerp x:x+1, y:y+1, z:z+1
3770  vz = UT_VoxelTile<T>::lerpValues(vy, vy1, fz);
3771  }
3772  else
3773  vz = vy;
3774  }
3775 
3776  return vz;
3777 }
3778 
3779 template <typename T>
3780 void
3782  UT_Vector3F pos) const
3783 {
3784  // We go from the position in the unit cube into the index.
3785  // The center of cells must map to the exact integer indices.
3786  pos.x() *= myRes[0];
3787  pos.y() *= myRes[1];
3788  pos.z() *= myRes[2];
3789  pos.x() -= 0.5;
3790  pos.y() -= 0.5;
3791  pos.z() -= 0.5;
3792 
3793  lerpVoxelCoordMinMax(lerp, lmin, lmax, pos);
3794 }
3795 
3796 template <typename T>
3797 void
3799  UT_Vector3F pos) const
3800 {
3801  int x, y, z;
3802  // Yes, these have to be 32 becaues split float requires 32!
3803  fpreal32 fx, fy, fz;
3804 
3805  splitVoxelCoord(pos, x, y, z, fx, fy, fz);
3806 
3807  lerpVoxelMinMax(lerp, lmin, lmax, x, y, z, fx, fy, fz);
3808 }
3809 
3810 
3811 template <typename T>
3812 template <int AXIS2D>
3813 void
3815  UT_Vector3F pos) const
3816 {
3817  int x, y, z;
3818  // Yes, these have to be 32 becaues split float requires 32!
3819  fpreal32 fx, fy, fz;
3820 
3821  splitVoxelCoordAxis<AXIS2D>(pos, x, y, z, fx, fy, fz);
3822 
3823  lerpVoxelMinMaxAxis<AXIS2D>(lerp, lmin, lmax, x, y, z, fx, fy, fz);
3824 }
3825 
3826 
3827 template <typename T>
3828 void
3830  T &lerp, T &smin, T &smax,
3831  int x, int y, int z,
3832  float fx, float fy, float fz) const
3833 {
3834  T samples[8];
3835 
3836  if (extractSample(x, y, z, samples))
3837  {
3838  lerp = smin = smax = samples[0];
3839  return;
3840  }
3841 
3842  lerp = lerpSample(samples, fx, fy, fz);
3843 
3844  T smin1, smax1;
3845 
3846  SYSminmax(samples[0], samples[1], samples[2], samples[3],
3847  smin, smax);
3848  SYSminmax(samples[4+0], samples[4+1], samples[4+2], samples[4+3],
3849  smin1, smax1);
3850 
3851  smin = SYSmin(smin, smin1);
3852  smax = SYSmax(smax, smax1);
3853 }
3854 
3855 template <typename T>
3856 template <int AXIS2D>
3857 void
3859  T &lerp, T &smin, T &smax,
3860  int x, int y, int z,
3861  float fx, float fy, float fz) const
3862 {
3863  T samples[8];
3864 
3865  if (extractSampleAxis<AXIS2D>(x, y, z, samples))
3866  {
3867  lerp = smin = smax = samples[0];
3868  return;
3869  }
3870 
3871  lerp = lerpSampleAxis<AXIS2D>(samples, fx, fy, fz);
3872 
3873  if (AXIS2D == 0)
3874  SYSminmax(samples[0], samples[2], samples[4], samples[6],
3875  smin, smax);
3876  else if (AXIS2D == 1)
3877  SYSminmax(samples[0], samples[1], samples[4], samples[5],
3878  smin, smax);
3879  else if (AXIS2D == 2)
3880  SYSminmax(samples[0], samples[1], samples[2], samples[3],
3881  smin, smax);
3882  else
3883  {
3884  T smin1, smax1;
3885 
3886  SYSminmax(samples[0], samples[1], samples[2], samples[3],
3887  smin, smax);
3888  SYSminmax(samples[4+0], samples[4+1], samples[4+2], samples[4+3],
3889  smin1, smax1);
3890 
3891  smin = SYSmin(smin, smin1);
3892  smax = SYSmax(smax, smax1);
3893  }
3894 }
3895 
3896 template <typename T>
3897 bool
3899  T *samples) const
3900 {
3901  // Optimization for common cases (values are within the voxel range and
3902  // are all within the same tile)
3903  if ( !((x | y | z) < 0) &&
3904  (((x - myRes[0]+1) & (y - myRes[1]+1) & (z - myRes[2]+1)) < 0))
3905 
3906  // (x > 0) && (y > 0) && (z > 0) &&
3907  // Do not use x+1 < foo as if x is MAX_INT that will falsely
3908  // report in bounds!
3909  // (x < myRes[0]-1) && (y < myRes[1]-1) && (z < myRes[2]-1) )
3910  {
3911  int xm, ym, zm;
3912 
3913  xm = x & TILEMASK;
3914  ym = y & TILEMASK;
3915  zm = z & TILEMASK;
3916 
3917  if ((xm != TILEMASK) && (ym != TILEMASK) && (zm != TILEMASK))
3918  {
3919  const UT_VoxelTile<T> *tile =
3920  getTile(x >> TILEBITS, y >> TILEBITS, z >> TILEBITS);
3921 
3922  return tile->extractSample(xm, ym, zm, samples);
3923  }
3924  else
3925  {
3926  // We cross tile boundaries but we remain within
3927  // the voxel grid. We can thus avoid any bounds checking
3928  // and use operator() rather than getValue.
3929  samples[0] = (*this)(x, y, z);
3930  samples[1] = (*this)(x+1, y, z);
3931  samples[2+0] = (*this)(x, y+1, z);
3932  samples[2+1] = (*this)(x+1, y+1, z);
3933  samples[4+0] = (*this)(x, y, z+1);
3934  samples[4+1] = (*this)(x+1, y, z+1);
3935  samples[4+2+0] = (*this)(x, y+1, z+1);
3936  samples[4+2+1] = (*this)(x+1, y+1, z+1);
3937  }
3938  }
3939  else
3940  {
3941  samples[0] = getValue(x, y, z);
3942  samples[1] = getValue(x+1, y, z);
3943  samples[2+0] = getValue(x, y+1, z);
3944  samples[2+1] = getValue(x+1, y+1, z);
3945  samples[4+0] = getValue(x, y, z+1);
3946  samples[4+1] = getValue(x+1, y, z+1);
3947  samples[4+2+0] = getValue(x, y+1, z+1);
3948  samples[4+2+1] = getValue(x+1, y+1, z+1);
3949  }
3950 
3951  return false;
3952 }
3953 
3954 
3955 template <typename T>
3956 template <int AXIS2D>
3957 bool
3959  T *samples) const
3960 {
3961  // Optimization for common cases (values are within the voxel range and
3962  // are all within the same tile)
3963  int lesscomp = 0, greatercomp = -1;
3964 
3965  if (AXIS2D != 0)
3966  {
3967  lesscomp |= x;
3968  greatercomp &= (x - myRes[0]+1);
3969  }
3970  if (AXIS2D != 1)
3971  {
3972  lesscomp |= y;
3973  greatercomp &= (y - myRes[1]+1);
3974  }
3975  if (AXIS2D != 2)
3976  {
3977  lesscomp |= z;
3978  greatercomp &= (z - myRes[2]+1);
3979  }
3980 
3981  // Optimization for common cases (values are within the voxel range and
3982  // are all within the same tile)
3983  if ( !(lesscomp < 0) && (greatercomp < 0) )
3984  {
3985  int xm, ym, zm;
3986 
3987  xm = x & TILEMASK;
3988  ym = y & TILEMASK;
3989  zm = z & TILEMASK;
3990 
3991  if ((AXIS2D == 0 || xm != TILEMASK) &&
3992  (AXIS2D == 1 || ym != TILEMASK) &&
3993  (AXIS2D == 2 || zm != TILEMASK))
3994  {
3995  const UT_VoxelTile<T> *tile =
3996  getTile( (AXIS2D == 0) ? 0 : (x >> TILEBITS),
3997  (AXIS2D == 1) ? 0 : (y >> TILEBITS),
3998  (AXIS2D == 2) ? 0 : (z >> TILEBITS) );
3999 
4000  return tile->template extractSampleAxis<AXIS2D>(xm, ym, zm, samples);
4001  }
4002  else
4003  {
4004  // We cross tile boundaries but we remain within
4005  // the voxel grid. We can thus avoid any bounds checking
4006  // and use operator() rather than getValue.
4007  samples[0] = (*this)(x, y, z);
4008  if (AXIS2D != 0)
4009  samples[1] = (*this)(x+1, y, z);
4010  if (AXIS2D != 1)
4011  {
4012  samples[2+0] = (*this)(x, y+1, z);
4013  if (AXIS2D != 0)
4014  samples[2+1] = (*this)(x+1, y+1, z);
4015  }
4016  if (AXIS2D != 2)
4017  {
4018  samples[4+0] = (*this)(x, y, z+1);
4019  if (AXIS2D != 0)
4020  samples[4+1] = (*this)(x+1, y, z+1);
4021  if (AXIS2D != 1)
4022  {
4023  samples[4+2+0] = (*this)(x, y+1, z+1);
4024  if (AXIS2D != 0)
4025  samples[4+2+1] = (*this)(x+1, y+1, z+1);
4026  }
4027  }
4028  }
4029  }
4030  else
4031  {
4032  samples[0] = getValue(x, y, z);
4033  if (AXIS2D != 0)
4034  samples[1] = getValue(x+1, y, z);
4035  if (AXIS2D != 1)
4036  {
4037  samples[2+0] = getValue(x, y+1, z);
4038  if (AXIS2D != 0)
4039  samples[2+1] = getValue(x+1, y+1, z);
4040  }
4041  if (AXIS2D != 2)
4042  {
4043  samples[4+0] = getValue(x, y, z+1);
4044  if (AXIS2D != 0)
4045  samples[4+1] = getValue(x+1, y, z+1);
4046  if (AXIS2D != 1)
4047  {
4048  samples[4+2+0] = getValue(x, y+1, z+1);
4049  if (AXIS2D != 0)
4050  samples[4+2+1] = getValue(x+1, y+1, z+1);
4051  }
4052  }
4053  }
4054 
4055  return false;
4056 }
4057 
4058 template <typename T>
4059 bool
4061  T *samples) const
4062 {
4063  // Optimization for common cases (values are within the voxel range and
4064  // are all within the same tile)
4065  if ( !(((x-1) | (y-1) | (z-1)) < 0) &&
4066  (((x - myRes[0]+1) & (y - myRes[1]+1) & (z - myRes[2]+1)) < 0))
4067 
4068  // (x > 0) && (y > 0) && (z > 0) &&
4069  // Do not use x+1 < foo as if x is MAX_INT that will falsely
4070  // report in bounds!
4071  // (x < myRes[0]-1) && (y < myRes[1]-1) && (z < myRes[2]-1) )
4072  {
4073  int xm, ym, zm;
4074 
4075  xm = x & TILEMASK;
4076  ym = y & TILEMASK;
4077  zm = z & TILEMASK;
4078 
4079  if (xm && ym && zm && (xm != TILEMASK) && (ym != TILEMASK) && (zm != TILEMASK))
4080  {
4081  const UT_VoxelTile<T> *tile =
4082  getTile(x >> TILEBITS, y >> TILEBITS, z >> TILEBITS);
4083 
4084  return tile->extractSamplePlus(xm, ym, zm, samples);
4085  }
4086  else
4087  {
4088  // We cross tile boundaries but we remain within
4089  // the voxel grid. We can thus avoid any bounds checking
4090  // and use operator() rather than getValue.
4091  samples[0] = (*this)(x-1, y, z);
4092  samples[1] = (*this)(x+1, y, z);
4093  samples[2+0] = (*this)(x, y-1, z);
4094  samples[2+1] = (*this)(x, y+1, z);
4095  samples[4+0] = (*this)(x, y, z-1);
4096  samples[4+1] = (*this)(x, y, z+1);
4097  samples[6] = (*this)(x, y, z);
4098  }
4099  }
4100  else
4101  {
4102  samples[0] = getValue(x-1, y, z);
4103  samples[1] = getValue(x+1, y, z);
4104  samples[2+0] = getValue(x, y-1, z);
4105  samples[2+1] = getValue(x, y+1, z);
4106  samples[4+0] = getValue(x, y, z-1);
4107  samples[4+1] = getValue(x, y, z+1);
4108  samples[6] = getValue(x, y, z);
4109  }
4110 
4111  return false;
4112 }
4113 
4114 #if 0
4115 /// Implementation of UT_VoxelTile::extractSampleCube() has an error (see the
4116 /// comments for it), which cascaded to here. Simply removing this function for
4117 /// now, as there isn't any need for it--at least in our own code.
4118 template <typename T>
4119 bool
4120 UT_VoxelArray<T>::extractSampleCube(int x, int y, int z,
4121  T *samples) const
4122 {
4123  // Optimization for common cases (values are within the voxel range and
4124  // are all within the same tile)
4125  if ( !(((x-1) | (y-1) | (z-1)) < 0) &&
4126  (((x - myRes[0]+1) & (y - myRes[1]+1) & (z - myRes[2]+1)) < 0))
4127 
4128  // (x > 0) && (y > 0) && (z > 0) &&
4129  // Do not use x+1 < foo as if x is MAX_INT that will falsely
4130  // report in bounds!
4131  // (x < myRes[0]-1) && (y < myRes[1]-1) && (z < myRes[2]-1) )
4132  {
4133  int xm, ym, zm;
4134 
4135  xm = x & TILEMASK;
4136  ym = y & TILEMASK;
4137  zm = z & TILEMASK;
4138 
4139  if (xm && ym && zm && (xm != TILEMASK) && (ym != TILEMASK) && (zm != TILEMASK))
4140  {
4141  const UT_VoxelTile<T> *tile =
4142  getTile(x >> TILEBITS, y >> TILEBITS, z >> TILEBITS);
4143 
4144  return tile->extractSampleCube(xm, ym, zm, samples);
4145  }
4146  else
4147  {
4148  // We cross tile boundaries but we remain within
4149  // the voxel grid. We can thus avoid any bounds checking
4150  // and use operator() rather than getValue.
4151  int sampidx = 0;
4152  for (int dz = -1; dz <= 1; dz++)
4153  {
4154  for (int dy = -1; dy <= 1; dy++)
4155  {
4156  for (int dx = -1; dx <= 1; dx++)
4157  {
4158  samples[sampidx++] = (*this)(x+dx, y+dy, z+dz);
4159  }
4160  }
4161  }
4162  }
4163  }
4164  else
4165  {
4166  int sampidx = 0;
4167  for (int dz = -1; dz <= 1; dz++)
4168  {
4169  for (int dy = -1; dy <= 1; dy++)
4170  {
4171  for (int dx = -1; dx <= 1; dx++)
4172  {
4173  samples[sampidx++] = getValue(x+dx, y+dy, z+dz);
4174  }
4175  }
4176  }
4177  }
4178 
4179  return false;
4180 }
4181 #endif
4182 
4183 template <typename T>
4184 T
4186  float fx, float fy, float fz) const
4187 {
4188 #if 1
4189  // Do trilinear interpolation.
4190  T vx, vx1, vy, vy1, vz;
4191 
4192  // Lerp x:x+1, y, z
4193  vx = UT_VoxelTile<T>::lerpValues(samples[0],
4194  samples[1],
4195  fx);
4196  // Lerp x:x+1, y+1, z
4197  vx1= UT_VoxelTile<T>::lerpValues(samples[2],
4198  samples[2+1],
4199  fx);
4200  // Lerp x:x+1, y:y+1, z
4201  vy = UT_VoxelTile<T>::lerpValues(vx, vx1, fy);
4202 
4203  // Lerp x:x+1, y, z+1
4204  vx = UT_VoxelTile<T>::lerpValues(samples[4],
4205  samples[4+1],
4206  fx);
4207  // Lerp x:x+1, y+1, z+1
4208  vx1= UT_VoxelTile<T>::lerpValues(samples[4+2],
4209  samples[4+2+1],
4210  fx);
4211 
4212  // Lerp x:x+1, y:y+1, z+1
4213  vy1 = UT_VoxelTile<T>::lerpValues(vx, vx1, fy);
4214 
4215  // Lerp x:x+1, y:y+1, z:z+1
4216  vz = UT_VoxelTile<T>::lerpValues(vy, vy1, fz);
4217 
4218  return vz;
4219 #else
4220  v4uf a, b, vfx, vfy, vfz;
4221 
4222  a = v4uf(&samples[0]);
4223  b = v4uf(&samples[4]);
4224 
4225  vfx = v4uf(fx);
4226  vfy = v4uf(fy);
4227  vfz = v4uf(fz);
4228 
4229  b -= a;
4230  a = madd(b, fz, a);
4231 
4232  b = a.swizzle<2, 3, 0, 1>();
4233  b -= a;
4234  a = madd(b, fy, a);
4235 
4236  b = a.swizzle<1, 2, 3, 0>();
4237  b -= a;
4238  a = madd(b, fx, a);
4239 
4240  return a[0];
4241 #endif
4242 }
4243 
4244 template <typename T>
4245 template <int AXIS2D>
4246 T
4248  float fx, float fy, float fz) const
4249 {
4250  // Do trilinear interpolation.
4251  T vx, vx1, vy, vy1, vz;
4252 
4253  // Lerp x:x+1, y, z
4254  if (AXIS2D != 0)
4255  vx = UT_VoxelTile<T>::lerpValues(samples[0], samples[1], fx);
4256  else
4257  vx = samples[0];
4258 
4259  if (AXIS2D != 1)
4260  {
4261  // Lerp x:x+1, y+1, z
4262  if (AXIS2D != 0)
4263  vx1= UT_VoxelTile<T>::lerpValues(samples[2], samples[2+1], fx);
4264  else
4265  vx1= samples[2];
4266 
4267  // Lerp x:x+1, y:y+1, z
4268  vy = UT_VoxelTile<T>::lerpValues(vx, vx1, fy);
4269  }
4270  else
4271  vy = vx;
4272 
4273  if (AXIS2D != 2)
4274  {
4275  // Lerp x:x+1, y, z+1
4276  if (AXIS2D != 0)
4277  vx = UT_VoxelTile<T>::lerpValues(samples[4], samples[4+1], fx);
4278  else
4279  vx = samples[4];
4280 
4281  if (AXIS2D != 1)
4282  {
4283  // Lerp x:x+1, y+1, z+1
4284  if (AXIS2D != 0)
4285  vx1= UT_VoxelTile<T>::lerpValues(samples[4+2], samples[4+2+1], fx);
4286  else
4287  vx1= samples[4+2];
4288 
4289  // Lerp x:x+1, y:y+1, z+1
4290  vy1 = UT_VoxelTile<T>::lerpValues(vx, vx1, fy);
4291  }
4292  else
4293  vy1 = vx;
4294 
4295  // Lerp x:x+1, y:y+1, z:z+1
4296  vz = UT_VoxelTile<T>::lerpValues(vy, vy1, fz);
4297  }
4298  else
4299  vz = vy;
4300 
4301  return vz;
4302 }
4303 
4304 template <typename T>
4305 T
4307 {
4308  int x, y, z;
4309  // Yes, these have to be 32 becaues split float requires 32!
4310  fpreal32 fx, fy, fz;
4311 
4312  // We go from the position in the unit cube into the index.
4313  // The center of cells must map to the exact integer indices.
4314  pos.x() *= myRes[0];
4315  pos.y() *= myRes[1];
4316  pos.z() *= myRes[2];
4317  pos.x() -= 0.5;
4318  pos.y() -= 0.5;
4319  pos.z() -= 0.5;
4320 
4321  // Determine integer & fractional components.
4322  fx = pos.x();
4323  SYSfastSplitFloat(fx, x);
4324  fy = pos.y();
4325  SYSfastSplitFloat(fy, y);
4326  fz = pos.z();
4327  SYSfastSplitFloat(fz, z);
4328 
4329  // Do trilinear interpolation.
4330  T vx, vx1, vy, vy1, vz;
4331 
4332  // NOTE:
4333  // If you get a crash reading one of these getValues, note
4334  // that if you are reading from the same voxel array that you
4335  // are writing to, and doing so in a multi-threaded, tile-by-tile
4336  // approach, just because your positions match doesn't mean you
4337  // won't access neighbouring tiles. To avoid this, perform
4338  // the obvious optimization of checking for aligned tiles
4339  // and using direct indices.
4340  // (I have avoided using if (!fx && !fy && !fz) as the test
4341  // as that is both potentially inaccurate (round off in conversion
4342  // may mess us up) and said optimzation is a useful one anyways)
4343 
4344  // Optimization for common cases (values are within the voxel range and
4345  // are all within the same tile)
4346  if ( !((x | y | z) < 0) &&
4347  (((x - myRes[0]+1) & (y - myRes[1]+1) & (z - myRes[2]+1)) < 0))
4348  // if ( (x > 0) && (y > 0) && (z > 0) &&
4349  // Do not use x+1 < foo as if x is MAX_INT that will falsely
4350  // report in bounds!
4351  // (x < myRes[0]-1) && (y < myRes[1]-1) && (z < myRes[2]-1) )
4352  {
4353  int xm, ym, zm;
4354 
4355  xm = x & TILEMASK;
4356  ym = y & TILEMASK;
4357  zm = z & TILEMASK;
4358 
4359  if ((xm != TILEMASK) && (ym != TILEMASK) && (zm != TILEMASK))
4360  {
4361  const UT_VoxelTile<T> *tile =
4362  getTile(x >> TILEBITS, y >> TILEBITS, z >> TILEBITS);
4363 
4364  vz = tile->lerp(xm, ym, zm, fx, fy, fz);
4365  }
4366  else
4367  {
4368  // We cross tile boundaries but we remain within
4369  // the voxel grid. We can thus avoid any bounds checking
4370  // and use operator() rather than getValue.
4371 
4372  // Lerp x:x+1, y, z
4373  vx = UT_VoxelTile<T>::lerpValues((*this)(x, y, z),
4374  (*this)(x+1, y, z),
4375  fx);
4376  // Lerp x:x+1, y+1, z
4377  vx1= UT_VoxelTile<T>::lerpValues((*this)(x, y+1, z),
4378  (*this)(x+1, y+1, z),
4379  fx);
4380  // Lerp x:x+1, y:y+1, z
4381  vy = UT_VoxelTile<T>::lerpValues(vx, vx1, fy);
4382 
4383  // Lerp x:x+1, y, z+1
4384  vx = UT_VoxelTile<T>::lerpValues((*this)(x, y, z+1),
4385  (*this)(x+1, y, z+1),
4386  fx);
4387  // Lerp x:x+1, y+1, z+1
4388  vx1= UT_VoxelTile<T>::lerpValues((*this)(x, y+1, z+1),
4389  (*this)(x+1, y+1, z+1),
4390  fx);
4391 
4392  // Lerp x:x+1, y:y+1, z+1
4393  vy1 = UT_VoxelTile<T>::lerpValues(vx, vx1, fy);
4394 
4395  // Lerp x:x+1, y:y+1, z:z+1
4396  vz = UT_VoxelTile<T>::lerpValues(vy, vy1, fz);
4397  }
4398  }
4399  else
4400  {
4401  // Lerp x:x+1, y, z
4402  vx = UT_VoxelTile<T>::lerpValues(getValue(x, y, z),
4403  getValue(x+1, y, z),
4404  fx);
4405  // Lerp x:x+1, y+1, z
4406  vx1= UT_VoxelTile<T>::lerpValues(getValue(x, y+1, z),
4407  getValue(x+1, y+1, z),
4408  fx);
4409 
4410  // Lerp x:x+1, y:y+1, z
4411  vy = UT_VoxelTile<T>::lerpValues(vx, vx1, fy);
4412 
4413  // Lerp x:x+1, y, z+1
4414  vx = UT_VoxelTile<T>::lerpValues(getValue(x, y, z+1),
4415  getValue(x+1, y, z+1),
4416  fx);
4417  // Lerp x:x+1, y+1, z+1
4418  vx1= UT_VoxelTile<T>::lerpValues(getValue(x, y+1, z+1),
4419  getValue(x+1, y+1, z+1),
4420  fx);
4421 
4422  // Lerp x:x+1, y:y+1, z+1
4423  vy1 = UT_VoxelTile<T>::lerpValues(vx, vx1, fy);
4424 
4425  // Lerp x:x+1, y:y+1, z:z+1
4426  vz = UT_VoxelTile<T>::lerpValues(vy, vy1, fz);
4427  }
4428 
4429  return vz;
4430 }
4431 
4432 #if 0
4433 template <typename T>
4434 T
4436 {
4437  int x, y, z;
4438 
4439  v4ui idx;
4440 
4441  // We go from the position in the unit cube into the index.
4442  // The center of cells must map to the exact integer indices.
4443  pos *= v4uf((float) myRes[0], (float) myRes[1], (float) myRes[2], 0.0f);
4444 
4445  // Because we use truncation we'll get inaccurate results
4446  // at the zero boundary, we thus bump up by two
4447  pos += 1.5;
4448 
4449  idx = pos.splitFloat();
4450 
4451  // And counteract that bump
4452  idx -= 2;
4453 
4454  x = idx[0];
4455  y = idx[1];
4456  z = idx[2];
4457 
4458  // Do trilinear interpolation.
4459  // A | B
4460  // 0 +z +y +yz | +x +xz +xy +xyz
4461 
4462  v4uf a, b, fx, fy, fz;
4463 
4464  int xm, ym, zm;
4465 
4466  xm = x & TILEMASK;
4467  ym = y & TILEMASK;
4468  zm = z & TILEMASK;
4469 
4470  // Optimization for common case (values are within the voxel range and
4471  // are all within the same tile)
4472  if ( (x > 0) & (y > 0) & (z > 0) &
4473  (x < myRes[0]-1) & (y < myRes[1]-1) & (z < myRes[2]-1) &
4474  (xm != TILEMASK) & (ym != TILEMASK) & (zm != TILEMASK) )
4475 /*
4476  if (isValidIndex(x, y, z) && isValidIndex(x+1, y+1, z+1) &&
4477  (x & TILEMASK) != TILEMASK &&
4478  (y & TILEMASK) != TILEMASK &&
4479  (z & TILEMASK) != TILEMASK)
4480 */
4481  {
4482  const UT_VoxelTile<T> *tile =
4483  getTile(x >> TILEBITS, y >> TILEBITS, z >> TILEBITS);
4484 
4485  return tile->lerp(pos, xm, ym, zm);
4486  }
4487  else
4488  {
4489  a = v4uf( getValue(x, y, z),
4490  getValue(x, y, z+1),
4491  getValue(x, y+1, z),
4492  getValue(x, y+1, z+1) );
4493  b = v4uf( getValue(x+1, y, z),
4494  getValue(x+1, y, z+1),
4495  getValue(x+1, y+1, z),
4496  getValue(x+1, y+1, z+1) );
4497  }
4498 
4499  fx = pos.swizzle<0, 0, 0, 0>();
4500  fy = pos.swizzle<1, 1, 1, 1>();
4501  fz = pos.swizzle<2, 2, 2, 2>();
4502 
4503  b -= a;
4504  a = madd(b, fx, a);
4505 
4506  b = a.swizzle<2, 3, 0, 1>();
4507  b -= a;
4508  a = madd(b, fy, a);
4509 
4510  b = a.swizzle<1, 2, 3, 0>();
4511  b -= a;
4512  a = madd(b, fz, a);
4513 
4514  return a[0];
4515 }
4516 #endif
4517 
4518 static inline int
4519 firstTile(int &start, int &end, int res)
4520 {
4521  if (start < 0)
4522  {
4523  start += res;
4524  end += res;
4525  }
4526  return start;
4527 }
4528 
4529 static inline int
4530 nextTile(int &tile, int &pstart, int &start, int &end, int res)
4531 {
4532  int pend;
4533 
4534  if (pstart >= res)
4535  {
4536  pstart -= res;
4537  start -= res;
4538  end -= res;
4539  }
4540  tile = pstart >> TILEBITS;
4541  pend = SYSmin((tile+1) * TILESIZE, end, res);
4542 
4543  UT_ASSERT(pstart >= 0 && pstart < res);
4544  UT_ASSERT(pend > 0 && pstart <= res);
4545  UT_ASSERT(pend-pstart > 0);
4546  UT_ASSERT(pend-pstart <= TILESIZE);
4547 
4548  return pend;
4549 }
4550 
4551 template <typename T>
4552 T
4554  fpreal radius, int clampaxis) const
4555 {
4556  UT_Vector3 tpos;
4557  UT_FilterWindow win[3];
4558  UT_FilterWrap wrap[3];
4559  UT_VoxelTile<T> *tile;
4560  const float *weights[3];
4561  fpreal visible;
4562  int start[3], end[3], size[3];
4563  int pstart[3], pend[3];
4564  int tx, ty, tz, i;
4565  T result;
4566 
4567  if (!myTiles)
4568  {
4569  // If the array is empty, just use the border value.
4570  return myBorderValue;
4571  }
4572 
4573  UT_FilterWrap basewrap;
4574  switch (myBorderType)
4575  {
4576  case UT_VOXELBORDER_CONSTANT: basewrap = UT_WRAP_BORDER; break;
4577  case UT_VOXELBORDER_REPEAT: basewrap = UT_WRAP_REPEAT; break;
4578  case UT_VOXELBORDER_STREAK: basewrap = UT_WRAP_CLAMP; break;
4579 
4580  // NOTE: These are incorrect!
4581  case UT_VOXELBORDER_MIRROR: basewrap = UT_WRAP_CLAMP; break;
4582  case UT_VOXELBORDER_EXTRAP: basewrap = UT_WRAP_CLAMP; break;
4583  default: basewrap = UT_WRAP_CLAMP; break;
4584  }
4585  for (i = 0; i < 3; i++)
4586  {
4587  if (i == clampaxis)
4588  wrap[i] = UT_WRAP_CLAMP;
4589  else
4590  wrap[i] = basewrap;
4591  }
4592 
4593  memset(&result, 0, sizeof(result));
4594 
4595  radius *= filter.getSupport();
4596 
4597  // Make a local copy of the position so that we can modify it.
4598  tpos = pos;
4599  visible = 1;
4600  for (i = 0; i < 3; i++)
4601  {
4602  tpos[i] = tpos[i]*myRes[i];
4603  if (!win[i].setWeights(filter, tpos[i], radius, myRes[i], wrap[i]))
4604  return result;
4605 
4606  weights[i] = win[i].getWeights();
4607  start[i] = win[i].getStart() % myRes[i];
4608  size[i] = win[i].getSize();
4609 
4610  UT_ASSERT(start[i] >= 0);
4611  UT_ASSERT(size[i] <= myRes[i]);
4612 
4613  end[i] = start[i] + size[i];
4614  visible *= win[i].getVisible();
4615  }
4616 
4617  // Accumulate filtered results
4618  pstart[2] = firstTile(start[2], end[2], myRes[2]);
4619  while (pstart[2] < end[2])
4620  {
4621  pend[2] = nextTile(tz, pstart[2], start[2], end[2], myRes[2]);
4622  pstart[1] = firstTile(start[1], end[1], myRes[1]);
4623  while (pstart[1] < end[1])
4624  {
4625  pend[1] = nextTile(ty, pstart[1], start[1], end[1], myRes[1]);
4626  pstart[0] = firstTile(start[0], end[0], myRes[0]);
4627  while (pstart[0] < end[0])
4628  {
4629  pend[0] = nextTile(tx, pstart[0], start[0], end[0], myRes[0]);
4630  tile = getTile(tx, ty, tz);
4631  UT_ASSERT(tile);
4632  tile->weightedSum(pstart, pend, weights, start, result);
4633  pstart[0] = pend[0];
4634  }
4635  pstart[1] = pend[1];
4636  }
4637  pstart[2] = pend[2];
4638  }
4639 
4640  if (visible < 1)
4641  {
4642  result += (1-visible)*myBorderValue;
4643  }
4644 
4645  return result;
4646 }
4647 
4648 template <typename T>
4649 T
4651  fpreal radius, int clampaxis) const
4652 {
4653  UT_Vector3 tpos;
4654  UT_FilterWindow win[3];
4655  UT_FilterWrap wrap[3];
4656  UT_VoxelTile<T> *tile;
4657  int start[3], end[3], size[3];
4658  int pstart[3], pend[3];
4659  int tx, ty, tz, i;
4660  T result;
4661 
4662  if (!myTiles)
4663  {
4664  // If the array is empty, just use the border value.
4665  return myBorderValue;
4666  }
4667 
4668  UT_FilterWrap basewrap;
4669  switch (myBorderType)
4670  {
4671  case UT_VOXELBORDER_CONSTANT: basewrap = UT_WRAP_BORDER; break;
4672  case UT_VOXELBORDER_REPEAT: basewrap = UT_WRAP_REPEAT; break;
4673  case UT_VOXELBORDER_STREAK: basewrap = UT_WRAP_CLAMP; break;
4674 
4675  // NOTE: These are incorrect!
4676  case UT_VOXELBORDER_MIRROR: basewrap = UT_WRAP_CLAMP; break;
4677  case UT_VOXELBORDER_EXTRAP: basewrap = UT_WRAP_CLAMP; break;
4678  default: basewrap = UT_WRAP_CLAMP; break;
4679  }
4680  for (i = 0; i < 3; i++)
4681  {
4682  if (i == clampaxis)
4683  wrap[i] = UT_WRAP_CLAMP;
4684  else
4685  wrap[i] = basewrap;
4686  }
4687 
4688  memset(&result, 0, sizeof(result));
4689 
4690  radius *= filter.getSupport();
4691 
4692  // Make a local copy of the position so that we can modify it.
4693  tpos = pos;
4694  for (i = 0; i < 3; i++)
4695  {
4696  tpos[i] = tpos[i]*myRes[i];
4697  if (!win[i].setWeights(filter, tpos[i], radius, myRes[i], wrap[i]))
4698  return result;
4699 
4700  start[i] = win[i].getStart() % myRes[i];
4701  size[i] = win[i].getSize();
4702 
4703  UT_ASSERT(start[i] >= 0);
4704  UT_ASSERT(size[i] <= myRes[i]);
4705 
4706  end[i] = start[i] + size[i];
4707  }
4708 
4709  // Accumulate filtered results
4710  pstart[2] = firstTile(start[2], end[2], myRes[2]);
4711  while (pstart[2] < end[2])
4712  {
4713  pend[2] = nextTile(tz, pstart[2], start[2], end[2], myRes[2]);
4714  pstart[1] = firstTile(start[1], end[1], myRes[1]);
4715  while (pstart[1] < end[1])
4716  {
4717  pend[1] = nextTile(ty, pstart[1], start[1], end[1], myRes[1]);
4718  pstart[0] = firstTile(start[0], end[0], myRes[0]);
4719  while (pstart[0] < end[0])
4720  {
4721  pend[0] = nextTile(tx, pstart[0], start[0], end[0], myRes[0]);
4722  tile = getTile(tx, ty, tz);
4723  UT_ASSERT(tile);
4724  tile->avgNonZero(pstart, pend, start, result);
4725  pstart[0] = pend[0];
4726  }
4727  pstart[1] = pend[1];
4728  }
4729  pstart[2] = pend[2];
4730  }
4731 
4732  return result;
4733 }
4734 
4735 template <typename T>
4736 void
4738  UT_FilterType filtertype,
4739  float filterwidthscale,
4740  int clampaxis)
4741 {
4742  fpreal radius;
4743  UT_Filter *filter;
4744 
4745  filter = UT_Filter::getFilter(filtertype);
4746 
4747  radius = SYSmax( src.getXRes() / (fpreal)getXRes(),
4748  src.getYRes() / (fpreal)getYRes(),
4749  src.getZRes() / (fpreal)getZRes(),
4750  1.0f );
4751  radius *= 0.5 * filterwidthscale;
4752 
4753  resamplethread(src, filter, radius, clampaxis);
4754 
4755  UT_Filter::releaseFilter(filter);
4756 }
4757 
4758 template <typename T>
4759 template <typename OP>
4760 void
4761 UT_VoxelArray<T>::forEachTile(const OP &op, bool shouldthread)
4762 {
4763  auto blockop = [&op, this](const UT_BlockedRange<int> &range)
4764  {
4766  for (int tileidx = range.begin(); tileidx != range.end(); tileidx++)
4767  {
4768  vit.setLinearTile(tileidx, this);
4769  op(vit);
4770  }
4771  };
4772 
4773  if (!shouldthread)
4774  {
4775  UTserialForEachNumber(numTiles(), blockop);
4776  }
4777  else
4778  {
4779  UTparallelForEachNumber(numTiles(), blockop);
4780  }
4781 }
4782 
4783 template <typename T>
4784 void
4785 UT_VoxelArray<T>::flattenPartial(T *flatarray, exint ystride, exint zstride,
4786  const UT_JobInfo &info) const
4787 {
4788  // Check to see if we are 2d, if so, we wish to use the
4789  // axis flatten.
4790  if (getXRes() == 1 && ystride == 1)
4791  {
4792  flattenPartialAxis<0>(flatarray, zstride, info);
4793  }
4794  else if (getYRes() == 1 && zstride == ystride)
4795  {
4796  flattenPartialAxis<1>(flatarray, zstride, info);
4797  }
4798  else if (getZRes() == 1)
4799  {
4800  flattenPartialAxis<2>(flatarray, ystride, info);
4801  }
4802  else
4803  {
4805 
4806  // Const cast.
4807  vit.setArray((UT_VoxelArray<T> *)this);
4808  // We can't use splitByTile as then our writes will fight
4809  // over 4k pages.
4810  vit.setPartialRange(info.job(), info.numJobs());
4811 
4812  for (vit.rewind(); !vit.atEnd(); vit.advance())
4813  {
4814  flatarray[vit.x() + vit.y()*ystride + vit.z()*zstride] = vit.getValue();
4815  }
4816  }
4817 }
4818 
4819 
4820 template <typename T>
4821 template <int AXIS2D>
4822 void
4824  const UT_JobInfo &info) const
4825 {
4826  // The x/y refer to the destination x/y.
4827  // The actual source index depends on AXIS2D.
4828  const int ax = (AXIS2D == 0) ? 1 : 0;
4829  const int ay = (AXIS2D == 2) ? 1 : 2;
4830  int tileidx[3] = { 0, 0, 0 };
4831 
4832  while (1)
4833  {
4834  exint tiley = info.nextTask();
4835  exint ystart = tiley * TILESIZE;
4836  if (ystart >= getRes(ay))
4837  break;
4838 
4839  T *stripe = &flatarray[ystart * ystride];
4840 
4841  int yres = SYSmin(getRes(ay) - ystart, TILESIZE);
4842 
4843  for (int tilex = 0, ntilex = getTileRes(ax); tilex < ntilex; tilex++)
4844  {
4845  tileidx[ax] = tilex;
4846  tileidx[ay] = tiley;
4847  auto tile = getTile(tileidx[0], tileidx[1], tileidx[2]);
4848 
4849  int xres = tile->getRes(ax);
4850  T *stripey = stripe;
4851  if (tile->isConstant())
4852  {
4853  const T *srcdata = tile->rawData();
4854 
4855  for (int y = 0; y < yres; y++)
4856  {
4857  for (int x = 0; x < xres; x++)
4858  {
4859  memcpy(&stripey[x], srcdata, sizeof(T));
4860  }
4861  stripey += ystride;
4862  }
4863  }
4864  else if (tile->isSimpleCompression())
4865  {
4866  const T *srcdata = tile->rawData();
4867 
4868  if (xres != TILESIZE)
4869  {
4870  for (int y = 0; y < yres; y++)
4871  {
4872  memcpy(stripey, srcdata, sizeof(T) * xres);
4873  srcdata += xres;
4874  stripey += ystride;
4875  }
4876  }
4877  else
4878  {
4879  for (int y = 0; y < yres; y++)
4880  {
4881  memcpy(stripey, srcdata, sizeof(T) * TILESIZE);
4882  srcdata += TILESIZE;
4883  stripey += ystride;
4884  }
4885  }
4886  }
4887  else
4888  {
4889  for (int y = 0; y < yres; y++)
4890  {
4891  int idx[3] = { 0, 0, 0 };
4892  idx[ay] = y;
4893  for (int x = 0; x < xres; x++)
4894  {
4895  idx[ax] = x;
4896  stripey[x] = (*tile)(idx[0], idx[1], idx[2]);
4897  }
4898  stripey += ystride;
4899  }
4900  }
4901 
4902  stripe += TILESIZE;
4903  }
4904  }
4905 }
4906 
4907 
4908 template <>
4909 inline void
4911  exint ystride, exint zstride,
4912  UT_Vector4F,
4913  const UT_JobInfo &info) const
4914 {
4916 
4917  // Const cast.
4918  vit.setArray(SYSconst_cast(this));
4919  // We can't use splitByTile as then our writes will fight
4920  // over 4k pages.
4921  vit.setPartialRange(info.job(), info.numJobs());
4922 
4923  for (vit.rewind(); !vit.atEnd(); vit.advance())
4924  {
4925  UT_Vector4F v = vit.getValue();
4926  int idx = (vit.x() + vit.y()*ystride + vit.z()*zstride) * 4;
4927 
4928  flatarray[idx] = (uint8) SYSclamp(v.x() * 255.0f, 0.0f, 255.0f);
4929  flatarray[idx+1] = (uint8) SYSclamp(v.y() * 255.0f, 0.0f, 255.0f);
4930  flatarray[idx+2] = (uint8) SYSclamp(v.z() * 255.0f, 0.0f, 255.0f);
4931  flatarray[idx+3] = (uint8) SYSclamp(v.w() * 255.0f, 0.0f, 255.0f);
4932  }
4933 }
4934 
4935 template <typename T>
4936 void
4938  exint ystride, exint zstride,
4939  T, const UT_JobInfo &info) const
4940 {
4941  UT_ASSERT(!"This template requires specific instantiations.");
4942 }
4943 
4944 template <>
4945 inline void
4947  exint ystride, exint zstride,
4948  UT_Vector4F,
4949  const UT_JobInfo &info) const
4950 {
4952 
4953  // Const cast.
4954  vit.setArray(SYSconst_cast(this));
4955  // We can't use splitByTile as then our writes will fight
4956  // over 4k pages.
4957  vit.setPartialRange(info.job(), info.numJobs());
4958 
4959  for (vit.rewind(); !vit.atEnd(); vit.advance())
4960  flatarray[vit.x() + vit.y()*ystride + vit.z()*zstride] = vit.getValue();
4961 }
4962 
4963 template <typename T>
4964 void
4966  exint ystride, exint zstride,
4967  T, const UT_JobInfo &info) const
4968 {
4969  UT_ASSERT(!"This template requires specific instantiations.");
4970 }
4971 
4972 template <>
4973 inline void
4975  exint ystride, exint zstride,
4976  UT_Vector4F,
4977  const UT_JobInfo &info) const
4978 {
4980 
4981  // Const cast.
4982  vit.setArray(SYSconst_cast(this));
4983  // We can't use splitByTile as then our writes will fight
4984  // over 4k pages.
4985  vit.setPartialRange(info.job(), info.numJobs());
4986 
4987  for (vit.rewind(); !vit.atEnd(); vit.advance())
4988  {
4989  UT_Vector4F v = vit.getValue();
4990 
4991  // NOTE: This works around an Nvidia driver bug on OSX, and older
4992  // Nvidia drivers on other platforms. The problem was that very
4993  // small FP values were causing huge random values to appear in
4994  // the 3D Texture.
4995  if(SYSabs(v.x()) < 1e-9)
4996  v.x() = 0.0;
4997  if(SYSabs(v.y()) < 1e-9)
4998  v.y() = 0.0;
4999  if(SYSabs(v.z()) < 1e-9)
5000  v.z() = 0.0;
5001  if(SYSabs(v.w()) < 1e-9)
5002  v.w() = 0.0;
5003 
5004  flatarray[vit.x() + vit.y()*ystride + vit.z()*zstride] = v;
5005  }
5006 }
5007 
5008 template <typename T>
5009 void
5011  exint ystride, exint zstride,
5012  T,const UT_JobInfo &info) const
5013 {
5014  UT_ASSERT(!"This template requires specific instantiations.");
5015 }
5016 
5017 
5018 template <typename T>
5019 void
5021  exint ystride, exint zstride,
5022  const UT_JobInfo &info)
5023 {
5025 
5026  vit.setArray(this);
5027  vit.splitByTile(info);
5028  vit.setCompressOnExit(true);
5029 
5030  for (vit.rewind(); !vit.atEnd(); vit.advance())
5031  {
5032  vit.setValue(flatarray[vit.x() + vit.y()*ystride + vit.z()*zstride]);
5033  }
5034 }
5035 
5036 template <typename T>
5037 template <int SLICE, typename S>
5038 S *
5039 UT_VoxelArray<T>::extractSlice(S *dstdata, int slice, bool half_slice) const
5040 {
5041  int slice_res = getRes(SLICE);
5042  // Exit out early if the slice isn't inside the array proper.
5043  if (slice < 0 || slice >= slice_res ||
5044  (half_slice && slice == slice_res - 1))
5045  return nullptr;
5046 
5047  constexpr int AXIS1 = SLICE == 0 ? 2 : 0;
5048  constexpr int AXIS2 = SLICE == 1 ? 2 : 1;
5049 
5050  // Voxel resolution along the major axis of the voxel array.
5051  int a1_res = getRes(AXIS1);
5052  // Tile resolutions of the voxel array.
5053  int a1_tiles = getTileRes(AXIS1);
5054  int a2_tiles = getTileRes(AXIS2);
5055  int ntiles = a1_tiles * a2_tiles;
5056  // Strides used to figure out the starting index of each tile's voxels in
5057  // the destination array.
5058  int tile_stride2 = (a1_res << TILEBITS);
5059  int tile_stride1 = TILESIZE;
5060  // Index of the tile for the slice and the local coordinate within it.
5061  int tile_slice = (slice >> TILEBITS);
5062  int local_slice = (slice & TILEMASK);
5063  // If half_slice_st is true, we are averaging two voxel slices which lie
5064  // within the same tile slices.
5065  bool half_slice_st = half_slice && (local_slice != TILEMASK);
5066  // If half_slice_tb is true, we are averaging current value of dstdata with
5067  // the tile's values.
5068  bool half_slice_tb = false;
5069 
5070  // This functor sets the destination array value. idx might get changed
5071  // internally, but it comes out the way it went in.
5072  // Behaviour of this function is controlled by the following booleans, in
5073  // this order.
5074  // * half_slice_st: sets the value to the average of tile values at local
5075  // local idx and one offset along SLICE by 1;
5076  // * half_slice_tb: sets the value to the average between the current value
5077  // in the destination array and the tile's value at the local idx;
5078  // * when neither of the above is true, sets to tile's value at the local
5079  // idx.
5080  auto set_functor = [&](const UT_VoxelTile<T>* tile, int idx[3], int vidx)
5081  {
5082  if (half_slice_st)
5083  {
5084  dstdata[vidx] = (*tile)(idx[0], idx[1], idx[2]);
5085  idx[SLICE]++;
5086  dstdata[vidx] = 0.5f * (dstdata[vidx] +
5087  (*tile)(idx[0], idx[1], idx[2]));
5088  idx[SLICE]--;
5089  }
5090  else if (half_slice_tb)
5091  dstdata[vidx] = 0.5f * (dstdata[vidx] +
5092  (*tile)(idx[0], idx[1], idx[2]));
5093  else
5094  dstdata[vidx] = (*tile)(idx[0], idx[1], idx[2]);
5095  };
5096  // This functor will be executed by the multithreaded loop below. Uses
5097  // set_functor to write the correct value into dstdata.
5098  auto loop_functor = [&](const UT_BlockedRange<int>& range)
5099  {
5100  // Tile index array.
5101  int is[3];
5102  is[SLICE] = tile_slice;
5103  // Array of indices within the tile.
5104  int is_local[3];
5105  is_local[SLICE] = local_slice;
5106 
5107  for (int i = range.begin(); i < range.end(); i++)
5108  {
5109  // Figure out coordinates of the current tile.
5110  is[AXIS1] = i % a1_tiles;
5111  is[AXIS2] = i / a1_tiles;
5112 
5113  // Get the current tile and its size.
5114  const UT_VoxelTile<T>* tile = getTile(is[0], is[1], is[2]);
5115  int a1_tile_res = tile->getRes(AXIS1);
5116  int a2_tile_res = tile->getRes(AXIS2);
5117  // Get the index we should write this tile to.
5118  int vcounter = is[AXIS2] * tile_stride2 + is[AXIS1] * tile_stride1;
5119 
5120  for (is_local[AXIS2] = 0; is_local[AXIS2] < a2_tile_res;
5121  is_local[AXIS2]++)
5122  {
5123  for (is_local[AXIS1] = 0; is_local[AXIS1] < a1_tile_res;
5124  is_local[AXIS1]++)
5125  {
5126  set_functor(tile, is_local, vcounter);
5127  vcounter++;
5128  }
5129  // Wrap the counter to the next row.
5130  vcounter += a1_res - a1_tile_res;
5131  }
5132  }
5133  };
5134 
5135  // Run the loop.
5136  UTparallelFor(UT_BlockedRange<int>(0, ntiles), loop_functor);
5137 
5138  // If we're halfway between two voxel slices that lie in different tiles, we
5139  // need to do another sweep and modify the results.
5140  if (half_slice && !half_slice_st)
5141  {
5142  half_slice_tb = true;
5143  tile_slice++;
5144  local_slice = 0;
5145  UTparallelFor(UT_BlockedRange<int>(0, ntiles), loop_functor);
5146  }
5147 
5148  return dstdata;
5149 }
5150 
5151 template <typename T>
5152 template <typename S>
5153 S *
5155  const UT_IntArray &tilelist) const
5156 {
5157  for (int i = 0; i < tilelist.entries(); i++)
5158  {
5159  UT_ASSERT(tilelist(i) >= 0 && tilelist(i) < numTiles());
5160  const UT_VoxelTile<T> *tile = getLinearTile(tilelist(i));
5161 
5162  tile->flatten(dstdata, stride);
5163  dstdata += tile->numVoxels() * stride;
5164  }
5165  return dstdata;
5166 }
5167 
5168 template <typename T>
5169 template <typename S, typename IDX>
5170 S *
5172  const IDX *ix, const IDX *iy, const IDX *iz,
5173  const UT_Array<UT_VoxelArrayTileDataDescr> &tilelist) const
5174 {
5175  int srcidx = 0;
5176 
5177  for (auto && tiledata : tilelist)
5178  {
5179  int tileidx = tiledata.tileidx;
5180  UT_ASSERT(tileidx >= 0 && tileidx < numTiles());
5181 
5182  const UT_VoxelTile<T> *tile = getLinearTile(tileidx);
5183  int tilevoxel = tile->numVoxels();
5184 
5185  if (tilevoxel == tiledata.numvoxel)
5186  {
5187  // Full tile.
5188  tile->flatten(dstdata, stride);
5189  dstdata += tiledata.numvoxel * stride;
5190  srcidx += tiledata.numvoxel;
5191  }
5192  else
5193  {
5194  // Partial tile...
5195  int basex, basey, basez;
5196  linearTileToXYZ(tileidx, basex, basey, basez);
5197 
5198  basex <<= TILEBITS;
5199  basey <<= TILEBITS;
5200  basez <<= TILEBITS;
5201 
5202  if (tile->isSimpleCompression())
5203  {
5204  const T *src = tile->rawData();
5205  if (tile->isConstant())
5206  {
5207  S cval = *src;
5208  for (int i = 0; i < tiledata.numvoxel; i++)
5209  {
5210  *dstdata = cval;
5211  dstdata += stride;
5212  srcidx++;
5213  }
5214  }
5215  else
5216  {
5217  int w = tile->xres();
5218  int h = tile->yres();
5219  for (int i = 0; i < tiledata.numvoxel; i++)
5220  {
5221  UT_ASSERT_P(ix[srcidx] >= basex && ix[srcidx] < basex+TILESIZE);
5222  UT_ASSERT_P(iy[srcidx] >= basey && iy[srcidx] < basey+TILESIZE);
5223  UT_ASSERT_P(iz[srcidx] >= basez && iz[srcidx] < basez+TILESIZE);
5224  *dstdata = src[ (ix[srcidx] - basex)
5225  + (iy[srcidx] - basey) * w
5226  + (iz[srcidx] - basez) * w * h];
5227  dstdata += stride;
5228  srcidx++;
5229  }
5230  }
5231  }
5232  else
5233  {
5234  for (int i = 0; i < tiledata.numvoxel; i++)
5235  {
5236  UT_ASSERT_P(ix[srcidx] >= basex && ix[srcidx] < basex+TILESIZE);
5237  UT_ASSERT_P(iy[srcidx] >= basey && iy[srcidx] < basey+TILESIZE);
5238  UT_ASSERT_P(iz[srcidx] >= basez && iz[srcidx] < basez+TILESIZE);
5239  *dstdata = (*tile)(ix[srcidx] - basex,
5240  iy[srcidx] - basey,
5241  iz[srcidx] - basez);
5242  dstdata += stride;
5243  srcidx++;
5244  }
5245  }
5246  }
5247  }
5248  return dstdata;
5249 }
5250 
5251 template <typename T>
5252 template <typename S>
5253 const S *
5255  const UT_IntArray &tilelist)
5256 {
5257  bool docompress = getCompressionOptions().compressionEnabled();
5258  for (int i = 0; i < tilelist.entries(); i++)
5259  {
5260  UT_ASSERT(tilelist(i) >= 0 && tilelist(i) < numTiles());
5261  UT_VoxelTile<T> *tile = getLinearTile(tilelist(i));
5262 
5263  tile->writeData(srcdata, stride);
5264  if (docompress)
5265  tile->tryCompress(getCompressionOptions());
5266  srcdata += tile->numVoxels() * stride;
5267  }
5268  return srcdata;
5269 }
5270 
5271 template <typename T>
5272 template <typename S, typename IDX>
5273 const S *
5275  const IDX *ix, const IDX *iy, const IDX *iz,
5276  const UT_Array<UT_VoxelArrayTileDataDescr> &tilelist)
5277 {
5278  bool docompress = getCompressionOptions().compressionEnabled();
5279  int srcidx = 0;
5280 
5281  for (auto && tiledata : tilelist)
5282  {
5283  int tileidx = tiledata.tileidx;
5284  UT_ASSERT(tileidx >= 0 && tileidx < numTiles());
5285 
5286  UT_VoxelTile<T> *tile = getLinearTile(tileidx);
5287  int tilevoxel = tile->numVoxels();
5288 
5289  if (tilevoxel == tiledata.numvoxel)
5290  {
5291  tile->writeData(srcdata, stride);
5292  srcdata += tiledata.numvoxel * stride;
5293  srcidx += tiledata.numvoxel;
5294  }
5295  else
5296  {
5297  // Partial tile.
5298  int basex, basey, basez;
5299  linearTileToXYZ(tileidx, basex, basey, basez);
5300 
5301  basex <<= TILEBITS;
5302  basey <<= TILEBITS;
5303  basez <<= TILEBITS;
5304 
5305  for (int i = 0; i < tiledata.numvoxel; i++)
5306  {
5307  UT_ASSERT_P(ix[srcidx] >= basex && ix[srcidx] < basex+TILESIZE);
5308  UT_ASSERT_P(iy[srcidx] >= basey && iy[srcidx] < basey+TILESIZE);
5309  UT_ASSERT_P(iz[srcidx] >= basez && iz[srcidx] < basez+TILESIZE);
5310  tile->setValue(ix[srcidx] - basex,
5311  iy[srcidx] - basey,
5312  iz[srcidx] - basez,
5313  *srcdata);
5314  srcdata += stride;
5315  srcidx++;
5316  }
5317  }
5318 
5319  if (docompress)
5320  tile->tryCompress(getCompressionOptions());
5321  }
5322  return srcdata;
5323 }
5324 
5325 ///
5326 /// This helper class can be used for iterating over a voxel array in relation to another
5327 /// one. In particular, dst is the destination voxel array that is to be iterated over; its
5328 /// tile at [X, Y, Z] is taken to be coincident with tile [X + xoff, Y + yoff, Z + zoff] of
5329 /// src. Tiles of dst are partitioned into two sets:
5330 /// - BP (boundary pass): these are tiles of dst that are coincident with tiles of src that
5331 /// are at the boundary (so they affect extrapolation);
5332 /// - IP (internal pass): these tiles are coincident with internal tiles of src (those that
5333 /// are irrelevant for extrapolation).
5334 /// Once created, an object of this class can return the number of tiles in each set and
5335 /// convert from a linear 0-N index to [X, Y, Z] coordinates that identify the tile within
5336 /// dst.
5337 ///
5338 /// This class can be roughly used as follows... Main function:
5339 ///
5340 /// __linearTileIndexConverter index(dst, src, xoff, yoff, zoff);
5341 /// customFunctionBP(..., index);
5342 /// customFunctionIP(..., index);
5343 ///
5344 /// BPPartial(..., const __linearTileIndexConverter& index, const UT_JobInfo& i) function:
5345 ///
5346 /// int t0, t1;
5347 /// i.divideWork(index.getNTilesBP(), t0, t1);
5348 /// for (int j = t0; j < t1; j++)
5349 /// {
5350 /// int x, y, z;
5351 /// index.toLinearBP(j, x, y, z);
5352 /// UT_VoxerTile<T>* dtile = src.getTile(x, y, z);
5353 /// ...
5354 /// }
5355 ///
5356 /// With a similar implementation for IP. See moveTilesWithOffset() for an example.
5357 ///
5359 {
5360 public:
5361  /// Creates an index converter from the given voxel arrays. This object partitions tiles
5362  /// of dst into those that are affected by
5363  template <typename T>
5365  const UT_VoxelArray<T>* src,
5366  int xoff, int yoff, int zoff)
5367  {
5368  myXMax = dst->getTileRes(0);
5369  myYMax = dst->getTileRes(1);
5370  myZMax = dst->getTileRes(2);
5371 
5372  // If source has a constant border, no tile affects extrapolation; so all tiles
5373  // can be put into IP. Otherwise, the outer layer of src's tiles are part of BP.
5374  int m = (src->getBorder() == UT_VOXELBORDER_CONSTANT) ? 0 : 1;
5375  myXSkipMin = SYSmax(m - xoff, 0);
5376  myXSkipLen =
5377  SYSmax(SYSmin(myXMax, src->getTileRes(0) - xoff - m) - myXSkipMin, 0);
5378  myYSkipMin = SYSmax(m - yoff, 0);
5379  myYSkipLen =
5380  SYSmax(SYSmin(myYMax, src->getTileRes(1) - yoff - m) - myYSkipMin, 0);
5381  myZSkipMin = SYSmax(m - zoff, 0);
5382  myZSkipLen =
5383  SYSmax(SYSmin(myZMax, src->getTileRes(2) - zoff - m) - myZSkipMin, 0);
5384  }
5385 
5386  /// Returns the number of tiles in each part.
5387  int getNTilesBP() const
5388  {
5389  return myXMax * myYMax * myZMax - getNTilesIP();
5390  }
5391  int getNTilesIP() const
5392  {
5393  return myXSkipLen * myYSkipLen * myZSkipLen;
5394  }
5395 
5396  /// Converts a linear index (between 0 and getNTiles*P()-1) to the [X, Y, Z] tile
5397  /// coordinate with respect to dst.
5398  void toLinearBP(int k, int& x, int& y, int& z) const
5399  {
5400  // Number of voxels before the first Z slice with a hole.
5401  const int check_0 = myXMax * myYMax * myZSkipMin;
5402  // Check if we are before the first Z slice of the hole or if there is no hole.
5403  if (myXSkipLen == 0 || myYSkipLen == 0 || myZSkipLen == 0 || k < check_0)
5404  {
5405  _toRegularLinear(k, myXMax, myYMax, x, y, z);
5406  return;
5407  }
5408  k -= check_0;
5409 
5410  // Number of voxels per Z-slice with a hole.
5411  const int check_1a = myXMax * myYMax - myXSkipLen * myYSkipLen;
5412  // Total number of voxels in Z-slices with a hole.
5413  const int check_1b = check_1a * myZSkipLen;
5414  // Check if we are past the hole...
5415  if (k >= check_1b)
5416  {
5417  _toRegularLinear(k - check_1b, myXMax, myYMax, x, y, z);
5418  z += myZSkipMin + myZSkipLen;
5419  return;
5420  }
5421 
5422  // We are in the holed slices... First, determine the Z-slice.
5423  z = k / check_1a + myZSkipMin;
5424  // The remainder (index within the slice).
5425  k = k % check_1a;
5426  // Check if we are before the first Y slice with a hole.
5427  const int check_2 = myXMax * myYSkipMin;
5428  if (k < check_2)
5429  {
5430  y = k / myXMax;
5431  x = k % myXMax;
5432  return;
5433  }
5434  k -= check_2;
5435  // Check if we are past the last Y slice with a ahole.
5436  const int check_3 = (myXMax - myXSkipLen) * myYSkipLen;
5437  if (k >= check_3)
5438  {
5439  k -= check_3;
5440  y = k / myXMax + myYSkipMin + myYSkipLen;
5441  x = k % myXMax;
5442  return;
5443  }
5444 
5445  // We are in the holed slices. Find the y coordinate.
5446  y = k / (myXMax - myXSkipLen) + myYSkipMin;
5447  x = k % (myXMax - myXSkipLen);
5448  if (x >= myXSkipMin)
5449  x += myXSkipLen;
5450  }
5451  void toLinearIP(int k, int& x, int& y, int& z) const
5452  {
5453  _toRegularLinear(k, myXSkipLen, myYSkipLen, x, y, z);
5454  x += myXSkipMin;
5455  y += myYSkipMin;
5456  z += myZSkipMin;
5457  }
5458 
5459 protected:
5460  static void _toRegularLinear(int k, int xdim, int ydim, int& x, int& y, int& z)
5461  {
5462  x = k % xdim;
5463  k = k / xdim;
5464  y = k % ydim;
5465  z = k / ydim;
5466  }
5467 
5468 protected:
5472 };
5473 
5474 template <typename T>
5475 void
5477  int tileoffy, int tileoffz)
5478 {
5479  __linearTileIndexConverter index(this, &src, tileoffx, tileoffy, tileoffz);
5480  UTparallelInvoke(true, [&]
5481  {
5482  // Divide up the work (only internal tiles). Source tiles for these guys can be
5483  // safely changed without affecting other threads.
5485  [&](const UT_BlockedRange<int>& range)
5486  {
5487  for (int i = range.begin(); i < range.end(); i++)
5488  {
5489  // Get the location of the current tile...
5490  int xyz[3];
5491  index.toLinearIP(i, xyz[0], xyz[1], xyz[2]);
5492 
5493  // Get the tiles and exchange data.
5494  UT_VoxelTile<T>* tile = getTile(xyz[0], xyz[1], xyz[2]);
5495  UT_VoxelTile<T>* srctile = src.getTile(xyz[0] + tileoffx,
5496  xyz[1] + tileoffy,
5497  xyz[2] + tileoffz);
5498 
5499  // If tiles are of the same size, simply exchange the data. We know these
5500  // tiles do not affect results outside of src.
5501  if (tile->xres() == srctile->xres() && tile->yres() == srctile->yres()
5502  && tile->zres() == srctile->zres())
5503  {
5504  // The tiles are of the same size, so we can just exchange the data
5505  // pointers.
5506  UTswap(tile->myData, srctile->myData);
5507  UTswap(tile->myCompressionType, srctile->myCompressionType);
5508  UTswap(tile->myForeignData, srctile->myForeignData);
5509  }
5510  else
5511  {
5512  // Otherwise, manually fill in the values.
5513  int offv[] = {(xyz[0] + tileoffx) * TILESIZE,
5514  (xyz[1] + tileoffy) * TILESIZE,
5515  (xyz[2] + tileoffz) * TILESIZE};
5516  for (int z = 0; z < tile->zres(); z++)
5517  {
5518  for (int y = 0; y < tile->yres(); y++)
5519  {
5520  for (int x = 0; x < tile->xres(); x++)
5521  {
5522  tile->setValue(x, y, z, src.getValue(x + offv[0],
5523  y + offv[1],
5524  z + offv[2]));
5525  }
5526  }
5527  }
5528  }
5529  }
5530  });
5531  }, [&]
5532  {
5533  // Is the border constant?
5534  const bool const_src_border = (src.getBorder() == UT_VOXELBORDER_CONSTANT);
5535  const T border_val = src.getBorderValue();
5536 
5537  // Offsets in terms of tiles.
5538  const int off[] = {tileoffx, tileoffy, tileoffz};
5539  // Tile resolution of the source.
5540  const int src_tileres[] = {src.getTileRes(0), src.getTileRes(1), src.getTileRes(2)};
5541 
5542  // Divide up the work (only boundary tiles). Source tiles for these guys cannot be
5543  // modified safely.
5545  [&](const UT_BlockedRange<int>& range)
5546  {
5547  for (int i = range.begin(); i < range.end(); i++)
5548  {
5549  // Get the location of the current tile...
5550  int xyz[3];
5551  index.toLinearBP(i, xyz[0], xyz[1], xyz[2]);
5552 
5553  // Get the current tile...
5554  UT_VoxelTile<T>* tile = getTile(xyz[0], xyz[1], xyz[2]);
5555  bool outside = false;
5556  for (int j = 0; j < 3; j++)
5557  {
5558  xyz[j] += off[j];
5559  outside = outside || (xyz[j] < 0 || xyz[j] >= src_tileres[j]);
5560  }
5561 
5562  // If we are completely outside, and borders are constant, we can go ahead and
5563  // make this tile constant.
5564  if (outside && const_src_border)
5565  {
5566  tile->makeConstant(border_val);
5567  }
5568  else
5569  {
5570  // Otherwise, manually fill in the values.
5571  int offv[] = {xyz[0] * TILESIZE, xyz[1] * TILESIZE, xyz[2] * TILESIZE};
5572  for (int z = 0; z < tile->zres(); z++)
5573  {
5574  for (int y = 0; y < tile->yres(); y++)
5575  {
5576  for (int x = 0; x < tile->xres(); x++)
5577  {
5578  tile->setValue(x, y, z, src.getValue(x + offv[0],
5579  y + offv[1],
5580  z + offv[2]));
5581  }
5582  }
5583  }
5584  }
5585  }
5586  });
5587  });
5588 }
5589 
5590 template <typename T>
5591 void
5593  int offx, int offy, int offz)
5594 {
5595  UT_VoxelBorderType srcborder;
5596  T srcborderval;
5597 
5598  srcborder = src.getBorder();
5599  srcborderval = src.getBorderValue();
5600 
5601  if (srcborder != UT_VOXELBORDER_EXTRAP)
5602  {
5603  // These borders may be constant friendly
5604  T srcval;
5605 
5606  if (src.isConstant(&srcval))
5607  {
5608  if (srcborder != UT_VOXELBORDER_CONSTANT ||
5609  SYSisEqual(srcborderval, srcval))
5610  {
5611  // We can trivially make ourselves constant.
5612  constant(srcval);
5613  return;
5614  }
5615  }
5616  }
5617 
5618  copyWithOffsetInternal(src, offx, offy, offz);
5619 }
5620 
5621 template <typename T>
5622 void
5624  int offx, int offy, int offz,
5625  const UT_JobInfo &info)
5626 {
5628  UT_Vector3I off(offx, offy, offz);
5629  bool can_copy_tiles = ((offx & TILEMASK) == 0) && ((offy & TILEMASK) == 0)
5630  && ((offz & TILEMASK) == 0);
5631 
5632  vit.setArray(this);
5633  vit.splitByTile(info);
5634  vit.setCompressOnExit(true);
5635 
5636  // Iterate over all tiles
5637  for (vit.rewind(); !vit.atEnd(); vit.advanceTile())
5638  {
5639  // Check out this tile
5640  UT_VoxelTile<T> *tile = vit.getTile();
5641  UT_VoxelTile<T> *srctile;
5642  int tx, ty, tz;
5643 
5644  UT_Vector3I start, end, srctileidx, srctileoff, srcend, srcendtile;
5645  vit.getTileVoxels(start, end);
5646 
5647  // If offsets are all multiples of TILESIZE, it is possible to do direct copies,
5648  // assuming source tile is inside and of the same size.
5649  if (can_copy_tiles)
5650  {
5651  // Start by getting the source tile index and ensuring it is inside the
5652  // source array.
5653  bool inside = true;
5654  for (int i = 0; i < 3; i++)
5655  {
5656  srctileidx(i) = vit.myTilePos[i] + (off(i) >> TILEBITS);
5657  inside = inside && srctileidx(i) >= 0
5658  && srctileidx(i) < src.getTileRes(i);
5659  }
5660  // If the tile is inside, we make sure the tile sizes are the same, in which
5661  // case we can do a copy and move on.
5662  if (inside)
5663  {
5664  srctile = src.getTile(srctileidx.x(), srctileidx.y(), srctileidx.z());
5665  if (tile->xres() == srctile->xres() && tile->yres() == srctile->yres()
5666  && tile->zres() == srctile->zres())
5667  {
5668  *tile = *srctile;
5669  continue;
5670  }
5671  }
5672  }
5673 
5674  srctileidx = start;
5675  srctileidx += off;
5676  srctileidx.x() >>= TILEBITS;
5677  srctileidx.y() >>= TILEBITS;
5678  srctileidx.z() >>= TILEBITS;
5679  srctileoff.x() = off.x() & TILEMASK;
5680  srctileoff.y() = off.y() & TILEMASK;
5681  srctileoff.z() = off.z() & TILEMASK;
5682 
5683  srcend = start;
5684  // We are very careful here to be inclusive, so we don't trigger
5685  // the next tile. This is the largest index we expect to index.
5686  srcend.x() += tile->xres() - 1;
5687  srcend.y() += tile->yres() - 1;
5688  srcend.z() += tile->zres() - 1;
5689  srcend += off;
5690  srcendtile = srcend;
5691  srcendtile.x() >>= TILEBITS;
5692  srcendtile.y() >>= TILEBITS;
5693  srcendtile.z() >>= TILEBITS;
5694 
5695  UT_ASSERT(srcendtile.x() == srctileidx.x() ||
5696  srcendtile.x() == srctileidx.x()+1);
5697  UT_ASSERT(srcendtile.y() == srctileidx.y() ||
5698  srcendtile.y() == srctileidx.y()+1);
5699  UT_ASSERT(srcendtile.z() == srctileidx.z() ||
5700  srcendtile.z() == srctileidx.z()+1);
5701 
5702  // Check if we are fully in bounds.
5703  if (srctileidx.x() >= 0 &&
5704  srctileidx.y() >= 0 &&
5705  srctileidx.z() >= 0 &&
5706  srcendtile.x() < src.getTileRes(0) &&
5707  srcendtile.y() < src.getTileRes(1) &&
5708  srcendtile.z() < src.getTileRes(2) &&
5709  srcend.x() < src.getXRes() &&
5710  srcend.y() < src.getYRes() &&
5711  srcend.z() < src.getZRes())
5712  {
5713  bool allconst = true, firsttile = true;
5714  T constval, cval;
5715  // Check if we are all constant...
5716  for (tz = srctileidx.z(); allconst && tz <= srcendtile.z(); tz++)
5717  for (ty = srctileidx.y(); allconst && ty <= srcendtile.y(); ty++)
5718  for (tx = srctileidx.x(); tx <= srcendtile.x(); tx++)
5719  {
5720  srctile = src.getTile(tx, ty, tz);
5721  if (!srctile->isConstant())
5722  {
5723  allconst = false;
5724  break;
5725  }
5726  cval = (*srctile)(0, 0, 0);
5727  if (firsttile)
5728  {
5729  firsttile = false;
5730  constval = cval;
5731  }
5732  if (!SYSisEqual(cval, constval))
5733  {
5734  allconst = false;
5735  break;
5736  }
5737  }
5738  if (allconst)
5739  {
5740  // Should have found at least one tile!
5741  UT_ASSERT(!firsttile);
5742  tile->makeConstant(constval);
5743 
5744  // Onto the next tile!
5745  continue;
5746  }
5747  }
5748 
5749  // All of the fragments aren't constant, or aren't all inside
5750  // our range. So we have to work fragment at a time..
5751  for (tz = srctileidx.z(); tz <= srcendtile.z(); tz++)
5752  for (ty = srctileidx.y(); ty <= srcendtile.y(); ty++)
5753  for (tx = srctileidx.x(); tx <= srcendtile.x(); tx++)
5754  {
5755  int destx, desty, destz;
5756  int srcx, srcy, srcz;
5757 
5758  destx = (tx == srctileidx.x()) ? 0 : (TILESIZE-srctileoff.x());
5759  desty = (ty == srctileidx.y()) ? 0 : (TILESIZE-srctileoff.y());
5760  destz = (tz == srctileidx.z()) ? 0 : (TILESIZE-srctileoff.z());
5761  srcx = (tx == srctileidx.x()) ? srctileoff.x() : 0;
5762  srcy = (ty == srctileidx.y()) ? srctileoff.y() : 0;
5763  srcz = (tz == srctileidx.z()) ? srctileoff.z() : 0;
5764 #if 1
5765  if (tx >= 0 &&
5766  ty >= 0 &&
5767  tz >= 0 &&
5768  tx < src.getTileRes(0) &&
5769  ty < src.getTileRes(1) &&
5770  tz < src.getTileRes(2) &&
5771  ((tx != src.getTileRes(0)-1) ||
5772  srcend.x() < src.getXRes()) &&
5773  ((ty != src.getTileRes(1)-1) ||
5774  srcend.y() < src.getYRes()) &&
5775  ((tz != src.getTileRes(2)-1) ||
5776  srcend.z() < src.getZRes())
5777  )
5778  {
5779  srctile = src.getTile(tx, ty, tz);
5780  // In bounds
5781  tile->copyFragment(
5782  destx, desty, destz,
5783  *srctile,
5784  srcx, srcy, srcz
5785  );
5786  }
5787  else
5788 #endif
5789  {
5790  // Out of bounds!
5791  int maxd = SYSmin(tile->zres(),
5792  TILESIZE-srcz);
5793  int maxh = SYSmin(tile->yres(),
5794  TILESIZE-srcy);
5795  int maxw = SYSmin(tile->xres(),
5796  TILESIZE-srcx);
5797  for (int z = destz; z < maxd; z++)
5798  {
5799  for (int y = desty; y < maxh; y++)
5800  {
5801  for (int x = destx; x < maxw; x++)
5802  {
5803  T val;
5804 
5805  val = src.getValue(x + vit.x() + offx,
5806  y + vit.y() + offy,
5807  z + vit.z() + offz);
5808  tile->setValue(x, y, z, val);
5809  }
5810  }
5811  }
5812  }
5813  }
5814  }
5815 }
5816 
5817 template <typename T>
5818 void
5820  const UT_Filter *filter, float radius,
5821  int clampaxis,
5822  const UT_JobInfo &info)
5823 {
5825  UT_Vector3 pos;
5826  UT_Vector3 ratio;
5827  UT_Interrupt *boss = UTgetInterrupt();
5828 
5829  vit.setArray(this);
5830  vit.splitByTile(info);
5831  vit.setCompressOnExit(true);
5832  vit.setInterrupt(boss);
5833 
5834  ratio.x() = 1.0f / getXRes();
5835  ratio.y() = 1.0f / getYRes();
5836  ratio.z() = 1.0f / getZRes();
5837 
5838  for (vit.rewind(); !vit.atEnd(); vit.advance())
5839  {
5840  pos.x() = vit.x()+0.5f;
5841  pos.y() = vit.y()+0.5f;
5842  pos.z() = vit.z()+0.5f;
5843  pos *= ratio;
5844 
5845  vit.setValue(src.evaluate(pos, *filter, radius, clampaxis));
5846  }
5847 }
5848 
5849 template <typename T>
5850 bool
5851 UT_VoxelArray<T>::posToIndex(UT_Vector3 pos, int &x, int &y, int &z) const
5852 {
5853  // We go from the position in the unit cube into the index.
5854  // The center of cells must map to the exact integer indices.
5855  pos.x() *= myRes[0];
5856  pos.y() *= myRes[1];
5857  pos.z() *= myRes[2];
5858 
5859  // The centers of cells are now mapped .5 too high. Ie, the true
5860  // center of cell index (0,0,0) would be (0.5,0.5,0.5) This, however,
5861  // is exactly what we want for rounding.
5862  x = (int) SYSfloor(pos.x());
5863  y = (int) SYSfloor(pos.y());
5864  z = (int) SYSfloor(pos.z());
5865 
5866  // Determine if out of bounds.
5867  return isValidIndex(x, y, z);
5868 }
5869 
5870 template <typename T>
5871 bool
5873 {
5874  // We go from the position in the unit cube into the index.
5875  // The center of cells must map to the exact integer indices.
5876  pos.x() *= myRes[0];
5877  pos.y() *= myRes[1];
5878  pos.z() *= myRes[2];
5879 
5880  // The centers of cells are now mapped .5 too high. Ie, the true
5881  // center of cell index (0,0,0) would be (0.5,0.5,0.5)
5882  pos.x() -= 0.5;
5883  pos.y() -= 0.5;
5884  pos.z() -= 0.5;
5885 
5886  ipos = pos;
5887 
5888  // Determine if out of bounds.
5889  if (pos.x() < 0 || pos.x() >= myRes[0] ||
5890  pos.y() < 0 || pos.y() >= myRes[1] ||
5891  pos.z() < 0 || pos.z() >= myRes[2])
5892  return false;
5893 
5894  return true;
5895 }
5896 
5897 template <typename T>
5898 bool
5900 {
5901  // We go from the position in the unit cube into the index.
5902  // The center of cells must map to the exact integer indices.
5903  pos.x() *= myRes[0];
5904  pos.y() *= myRes[1];
5905  pos.z() *= myRes[2];
5906 
5907  // The centers of cells are now mapped .5 too high. Ie, the true
5908  // center of cell index (0,0,0) would be (0.5,0.5,0.5) This, however,
5909  // is exactly what we want for rounding.
5910  x = (exint) SYSfloor(pos.x());
5911  y = (exint) SYSfloor(pos.y());
5912  z = (exint) SYSfloor(pos.z());
5913 
5914  // Determine if out of bounds.
5915  return isValidIndex(x, y, z);
5916 }
5917 
5918 template <typename T>
5919 bool
5921 {
5922  // We go from the position in the unit cube into the index.
5923  // The center of cells must map to the exact integer indices.
5924  pos.x() *= myRes[0];
5925  pos.y() *= myRes[1];
5926  pos.z() *= myRes[2];
5927 
5928  // The centers of cells are now mapped .5 too high. Ie, the true
5929  // center of cell index (0,0,0) would be (0.5,0.5,0.5)
5930  pos.x() -= 0.5;
5931  pos.y() -= 0.5;
5932  pos.z() -= 0.5;
5933 
5934  ipos = pos;
5935 
5936  // Determine if out of bounds.
5937  if (pos.x() < 0 || pos.x() >= myRes[0] ||
5938  pos.y() < 0 || pos.y() >= myRes[1] ||
5939  pos.z() < 0 || pos.z() >= myRes[2])
5940  return false;
5941 
5942  return true;
5943 }
5944 
5945 template <typename T>
5946 bool
5947 UT_VoxelArray<T>::indexToPos(int x, int y, int z, UT_Vector3F &pos) const
5948 {
5949  pos.x() = x;
5950  pos.y() = y;
5951  pos.z() = z;
5952 
5953  // Move the indices to the centers of the cells.
5954  pos.x() += 0.5;
5955  pos.y() += 0.5;
5956  pos.z() += 0.5;
5957 
5958  // And scale into the unit cube.
5959  pos *= myInvRes;
5960 
5961  // Return true if the original coordinates were in range.
5962  return isValidIndex(x, y, z);
5963 }
5964 
5965 template <typename T>
5966 bool
5968 {
5969  pos.x() = x;
5970  pos.y() = y;
5971  pos.z() = z;
5972 
5973  // Move the indices to the centers of the cells.
5974  pos.x() += 0.5;
5975  pos.y() += 0.5;
5976  pos.z() += 0.5;
5977 
5978  // And scale into the unit cube.
5979  pos *= myInvRes;
5980 
5981  // Return true if the original coordinates were in range.
5982  return isValidIndex(x, y, z);
5983 }
5984 
5985 template <typename T>
5986 void
5988 {
5989  pos = index;
5990 
5991  // Move the indices to the centers of the cells.
5992  pos.x() += 0.5;
5993  pos.y() += 0.5;
5994  pos.z() += 0.5;
5995 
5996  // And scale into the unit cube.
5997  pos *= myInvRes;
5998 }
5999 
6000 template <typename T>
6001 void
6003 {
6004  pos = index;
6005 
6006  // Move the indices to the centers of the cells.
6007  pos.x() += 0.5;
6008  pos.y() += 0.5;
6009  pos.z() += 0.5;
6010 
6011  // And scale into the unit cube.
6012  pos *= myInvRes;
6013 }
6014 
6015 template <typename T>
6016 void
6018 {
6019  myBorderType = type;
6020  myBorderValue = t;
6021 }
6022 
6023 template <typename T>
6024 void
6026 {
6027  myBorderScale[0] = sx;
6028  myBorderScale[1] = sy;
6029  myBorderScale[2] = sz;
6030 }
6031 
6032 template <typename T>
6033 void
6035 {
6036  UT_VoxelArrayIterator<T> vit(this);
6037  vit.splitByTile(info);
6038  for (vit.rewind(); !vit.atEnd(); vit.advanceTile())
6039  {
6040  int i = vit.getLinearTileNum();
6041  myTiles[i].tryCompress(getCompressionOptions());
6042  }
6043 }
6044 
6045 template <typename T>
6046 void
6048 {
6049  UT_VoxelArrayIterator<T> vit(this);
6050  vit.splitByTile(info);
6051  for (vit.rewind(); !vit.atEnd(); vit.advanceTile())
6052  {
6053  int i = vit.getLinearTileNum();
6054  myTiles[i].uncompress();
6055  }
6056 }
6057 
6058 template <typename T>
6059 void
6061 {
6062  UT_VoxelArrayIterator<T> vit(this);
6063  vit.splitByTile(info);
6064  for (vit.rewind(); !vit.atEnd(); vit.advanceTile())
6065  {
6066  int i = vit.getLinearTileNum();
6067  if (!myTiles[i].isConstant())
6068  myTiles[i].uncompress();
6069  }
6070 }
6071 
6072 template <typename T>
6073 void
6074 UT_VoxelArray<T>::saveData(std::ostream &os) const
6075 {
6076  T cval;
6077  char version;
6078 
6079  // First determine if we are fully constant.
6080  if (isConstant(&cval))
6081  {
6082  // Save a constant array.
6083  version = 0;
6084  UTwrite(os, &version, 1);
6085  UTwrite<T>(os, &cval);
6086  return;
6087  }
6088 
6089  // Compressed tiles.
6090  version = 1;
6091  UTwrite(os, &version, 1);
6092 
6093  // Store list of compression types
6095 
6096  int i, ntiles;
6097 
6098  ntiles = numTiles();
6099  for (i = 0; i < ntiles; i++)
6100  {
6101  myTiles[i].save(os);
6102  }
6103 }
6104 
6105 
6106 template <typename T>
6107 void
6109 {
6110  T cval;
6111  char version;
6112 
6113  is.readChar(version);
6114 
6115  // First determine if we are fully constant.
6116  if (version == 0)
6117  {
6118  // Save a constant array.
6119  is.read<T>(&cval);
6120 
6121  constant(cval);
6122  return;
6123  }
6124 
6125  if (version == 1)
6126  {
6127  UT_IntArray compressions;
6128 
6129  // Store list of compression types
6130  UT_VoxelTile<T>::loadCompressionTypes(is, compressions);
6131 
6132  int i, ntiles;
6133 
6134  ntiles = numTiles();
6135  for (i = 0; i < ntiles; i++)
6136  {
6137  myTiles[i].load(is, compressions);
6138  }
6139  }
6140 }
6141 
6142 template <typename T>
6143 bool
6144 UT_VoxelArray<T>::saveData(UT_JSONWriter &w, const char *shared_mem_owner) const
6145 {
6146  constexpr exint tuple_size = UT_FixedVectorTraits<T>::TupleSize;
6147 
6148  bool ok = true;
6149  T cval;
6150  int8 version;
6151 
6152  // First determine if we are fully constant.
6153  if (isConstant(&cval))
6154  {
6155  ok = ok && w.jsonBeginArray();
6156  // Save a constant array.
6157  ok = ok && w.jsonKey(UT_VoxelArrayJSON::getToken(
6159  if constexpr (tuple_size == 1)
6160  ok = ok && w.jsonValue(cval);
6161  else
6162  ok = ok && w.jsonUniformArray(tuple_size, cval.data());
6163  ok = ok && w.jsonEndArray();
6164  return ok;
6165  }
6166 
6167  if (shared_mem_owner)
6168  {
6169  SYS_SharedMemory *shm;
6170  shm = copyToSharedMemory(shared_mem_owner);
6171  if (shm)
6172  {
6173  ok = ok && w.jsonBeginArray();
6174  // Save a shared memory array.
6175  ok = ok && w.jsonKey(UT_VoxelArrayJSON::getToken(
6177  ok = ok && w.jsonString(shm->id());
6178  ok = ok && w.jsonEndArray();
6179  return ok;
6180  }
6181  // Fall back on raw write.
6182  }
6183 
6184 
6185  // Compressed tiles.
6186  ok = ok && w.jsonBeginArray();
6187  ok = ok && w.jsonKey(UT_VoxelArrayJSON::getToken(
6189  ok = ok && w.jsonBeginArray();
6190  ok = ok && w.jsonKey(UT_VoxelArrayJSON::getToken(
6192  version = 1;
6193  ok = ok && w.jsonValue(version);
6194 
6195  // Store list of compression types
6196  ok = ok && w.jsonKey(UT_VoxelArrayJSON::getToken(
6199 
6200  ok = ok && w.jsonKey(UT_VoxelArrayJSON::getToken(
6202  ok = ok && w.jsonBeginArray();
6203  int i, ntiles;
6204 
6205  ntiles = numTiles();
6206  for (i = 0; i < ntiles; i++)
6207  {
6208  ok = ok && myTiles[i].save(w);
6209  }
6210  ok = ok && w.jsonEndArray();
6211 
6212  ok = ok && w.jsonEndArray();
6213 
6214  ok = ok && w.jsonEndArray();
6215  return ok;
6216 }
6217 
6218 template <typename T>
6219 bool
6221 {
6222  constexpr exint tuple_size = UT_FixedVectorTraits<T>::TupleSize;
6223 
6224  T cval;
6225  int8 version;
6226  UT_WorkBuffer key;
6227  int key_id;
6228  bool array_error = false;
6229 
6230  delete mySharedMemView;
6231  mySharedMemView = 0;
6232 
6233  delete mySharedMem;
6234  mySharedMem = 0;
6235 
6236  if (!p.parseBeginArray(array_error) || array_error)
6237  return false;
6238 
6239  if (!p.parseString(key))
6240  return false;
6241  key_id = UT_VoxelArrayJSON::getArrayID(key.buffer());
6243  {
6244  if constexpr (tuple_size == 1)
6245  {
6246  if (!p.parseNumber(cval))
6247  return false;
6248  }
6249  else
6250  {
6251  if (!p.parseUniformArray(cval.data(), tuple_size))
6252  return false;
6253  }
6254 
6255  constant(cval);
6256  }
6257  else if (key_id == UT_VoxelArrayJSON::ARRAY_SHAREDARRAY)
6258  {
6259  UT_WorkBuffer shm_id;
6260  if (!p.parseString(shm_id))
6261  return false;
6262 
6263  if (!populateFromSharedMemory(shm_id.buffer()))
6264  {
6265  // If the shared memory disappears before we get a chance to read
6266  // it, don't abort, just set a constant value. That way Mantra has
6267  // a chance to recover. Not the most elegant solution but works for
6268  // now.
6269  T cval;
6270  cval = 0;
6271  constant(cval);
6272  }
6273  }
6274  else if (key_id == UT_VoxelArrayJSON::ARRAY_TILEDARRAY)
6275  {
6276  UT_JSONParser::traverser it, tile_it;
6277  UT_IntArray compressions;
6278  int i, ntiles;
6279 
6280 
6281  for (it = p.beginArray(); !it.atEnd(); ++it)
6282  {
6283  if (!it.getLowerKey(key))
6284  return false;
6285  switch (UT_VoxelArrayJSON::getArrayID(key.buffer()))
6286  {
6288  if (!p.parseNumber(version))
6289  return false;
6290  break;
6292  // Store list of compression types
6293  if (!UT_VoxelTile<T>::loadCompressionTypes(p, compressions))
6294  return false;
6295  break;
6296 
6298  ntiles = numTiles();
6299  for (tile_it = p.beginArray(), i = 0; !tile_it.atEnd();
6300  ++tile_it, ++i)
6301  {
6302  if (i < ntiles)
6303  {
6304  if (!myTiles[i].load(p, compressions))
6305  return false;
6306  }
6307  else
6308  {
6309  UT_VoxelTile<T> dummy_tile;
6310  dummy_tile.setRes(TILESIZE, TILESIZE, TILESIZE);
6311  if (!dummy_tile.load(p, compressions))
6312  return false;
6313  }
6314  }
6315  if (i != ntiles)
6316  return false;
6317  break;
6318  default:
6319  p.addWarning("Unexpected key for voxel data: %s",
6320  key.buffer());
6321  if (!p.skipNextObject())
6322  return false;
6323  }
6324  }
6325  }
6326  else
6327  {
6328  p.addWarning("Unexpected voxel key: %s\n", key.buffer());
6329  p.skipNextObject();
6330  return false;
6331  }
6332 
6333  if (!p.parseEndArray(array_error) || array_error)
6334  return false;
6335 
6336  return true;
6337 }
6338 
6339 template<typename T>
6341 UT_VoxelArray<T>::copyToSharedMemory(const char *shared_mem_owner) const
6342 {
6344 
6345  int ntiles;
6346 
6347  ntiles = numTiles();
6348 
6349  // Tally up the size we need.
6350  exint total_mem_size;
6351 
6352  total_mem_size = sizeof(exint); // Tile count.
6353 
6354  UT_Array<exint> tile_sizes;
6355  UT_Array<exint> next_block_offsets;
6356 
6357  for (int i = 0; i < ntiles; i++)
6358  {
6359  exint tile_size, block_size;
6360  if (myTiles[i].isConstant())
6361  tile_size = sizeof(T);
6362  else
6363  tile_size = myTiles[i].getDataLength();
6364 
6365  tile_sizes.append(tile_size);
6366 
6367  block_size = SYSroundUpToMultipleOf<exint>(tile_size, sizeof(exint));
6368  block_size += sizeof(exint) * 2; // Offset + compression type
6369 
6370  if (i < (ntiles-1))
6371  next_block_offsets.append(block_size / sizeof(exint));
6372  else
6373  next_block_offsets.append(0);
6374 
6375  total_mem_size += block_size;
6376  }
6377 
6378  // Start with an empty shared memory.
6379  SYS_SharedMemory *shared_mem;
6380  UT_WorkBuffer shared_mem_key;
6381 
6382  shared_mem_key.sprintf("%s:%p", shared_mem_owner, this);
6383 
6384  shared_mem = shmgr.get(shared_mem_key.buffer());
6385 
6386  // Does the existing block have the same number of tiles and same
6387  // offsets? In that case we don't reset the shared memory, just write
6388  // the tiles out again. There's very little adverse effect in changing
6389  // the voxel tile data.
6390  bool same_meta_data = false;
6391  if (shared_mem->size() >= sizeof(exint))
6392  {
6393  same_meta_data = true;
6394  {
6395  SYS_SharedMemoryView sv_tc(*shared_mem, 0, sizeof(exint));
6396 
6397  exint *ptr_ds = (exint *)sv_tc.data();
6398  if (*ptr_ds != ntiles)
6399  same_meta_data = false;
6400  }
6401 
6402  if (same_meta_data)
6403  {
6404  // Check if the offsets + compression type is the same.
6405  exint offset = sizeof(exint);
6406  for (int i = 0; i < ntiles; i++)
6407  {
6408  SYS_SharedMemoryView sv_tile(*shared_mem, offset,
6409  sizeof(exint) * 2);
6410 
6411  exint *ptr_tile = (exint *)sv_tile.data();
6412  if (ptr_tile[0] != next_block_offsets(i) ||
6413  ptr_tile[1] != (exint)myTiles[i].myCompressionType)
6414  {
6415  same_meta_data = false;
6416  break;
6417  }
6418  offset += next_block_offsets(i) * sizeof(exint);
6419  }
6420  }
6421  }
6422 
6423  if (!same_meta_data)
6424  {
6425  if (!shared_mem->reset(total_mem_size) ||
6426  shared_mem->size() != total_mem_size)
6427  {
6428  return nullptr;
6429  }
6430  }
6431 
6432  {
6433  SYS_SharedMemoryView sv_tc(*shared_mem, 0, sizeof(exint));
6434 
6435  exint *ptr_ds = (exint *)sv_tc.data();
6436  *ptr_ds = ntiles;
6437  }
6438 
6439  exint offset = sizeof(exint);
6440  for (int i = 0; i < ntiles; i++)
6441  {
6442  SYS_SharedMemoryView sv_tile(*shared_mem, offset,
6443  sizeof(exint) * 2 + tile_sizes(i));
6444 
6445  exint *ptr_tile = (exint *)sv_tile.data();
6446 
6447  // Offset to next tile in exint units.
6448  ptr_tile[0] = next_block_offsets(i);
6449  ptr_tile[1] = myTiles[i].myCompressionType;
6450 
6451  ::memcpy(&ptr_tile[2], myTiles[i].rawData(), tile_sizes(i));
6452 
6453  offset += ptr_tile[0] * sizeof(exint);
6454  }
6455 
6456  return shared_mem;
6457 }
6458 
6459 template<typename T>
6460 bool
6462 {
6463  mySharedMem = new SYS_SharedMemory(id, /*read_only=*/true);
6464  if (!mySharedMem->size())
6465  {
6466  delete mySharedMem;
6467  mySharedMem = 0;
6468  return false;
6469  }
6470 
6471  exint ntiles;
6472  {
6473  SYS_SharedMemoryView sv_tc(*mySharedMem, 0, sizeof(exint));
6474  ntiles = *(const exint *)sv_tc.data();
6475  }
6476  if (ntiles != numTiles())
6477  {
6478  delete mySharedMem;
6479  mySharedMem = 0;
6480  return false;
6481  }
6482 
6483  mySharedMemView = new SYS_SharedMemoryView(*mySharedMem, sizeof(exint));
6484 
6485  exint *data = (exint *)mySharedMemView->data();
6486 
6487  for (int i = 0; i < ntiles; i++)
6488  {
6489  exint offset = data[0];
6490  int8 ctype = (int8)data[1];
6491 
6492  myTiles[i].setForeignData(&data[2], ctype);
6493 
6494  data += offset;
6495  }
6496 
6497  return true;
6498 }
6499 
6500 
6501 
6502 
6503 //
6504 // UT_VoxelMipMap functions
6505 //
6506 template <typename T>
6508 {
6509  initializePrivate();
6510 }
6511 
6512 template <typename T>
6514 {
6515  destroyPrivate();
6516 }
6517 
6518 template <typename T>
6520 {
6521  initializePrivate();
6522 
6523  *this = src;
6524 }
6525 
6526 template <typename T>
6527 const UT_VoxelMipMap<T> &
6529 {
6531  int level, i;
6532 
6533  if (&src == this)
6534  return *this;
6535 
6536  destroyPrivate();
6537 
6538  // We do a deep copy. We have learned from UT_String that
6539  // shallow copies will destroy us in the end.
6540  myOwnBase = true;
6541  myBaseLevel = new UT_VoxelArray<T>;
6542  *myBaseLevel = *src.myBaseLevel;
6543 
6544  myNumLevels = src.myNumLevels;
6545 
6546  for (i = 0; i < src.myLevels.entries(); i++)
6547  {
6548  levels = new UT_VoxelArray<T> *[myNumLevels];
6549  myLevels.append(levels);
6550 
6551  for (level = 0; level < myNumLevels; level++)
6552  {
6553  levels[level] = new UT_VoxelArray<T>;
6554  *levels[level] = *src.myLevels(i)[level];
6555  }
6556  }
6557  return *this;
6558 }
6559 
6560 template <typename T>
6561 void
6563  mipmaptype function)
6564 {
6565  UT_Array<mipmaptype> functions;
6566 
6567  functions.append(function);
6568  build(baselevel, functions);
6569 }
6570 
6571 template <typename T>
6572 void
6574  const UT_Array<mipmaptype> &functions)
6575 {
6576  destroyPrivate();
6577 
6578  // Setup our baselevel.
6579  myBaseLevel = baselevel;
6580  myOwnBase = false;
6581 
6582  // Now build from bottom up. First, calculate the number
6583  // of levels we'll need.
6584  myNumLevels = 0;
6585  int maxres, level;
6586 
6587  maxres = SYSmax(myBaseLevel->getXRes(), myBaseLevel->getYRes());
6588  maxres = SYSmax(maxres, myBaseLevel->getZRes());
6589 
6590  // std::cerr << "Max res " << maxres << std::endl;
6591 
6592  while (maxres > 1)
6593  {
6594  myNumLevels++;
6595  maxres++;
6596  maxres >>= 1;
6597  }
6598 
6599  // std::cerr << "Levels: " << myNumLevels << std::endl;
6600 
6601  // Create our desired voxel array list.
6602  for (int i = 0; i < functions.entries(); i++)
6603  {
6604  mipmaptype function = functions(i);
6605 
6606  UT_VoxelArray<T> **levels = new UT_VoxelArray<T> *[myNumLevels];
6607  myLevels.append(levels);
6608 
6609  UT_VoxelArray<T> *lastlevel = myBaseLevel;
6610  for (level = myNumLevels-1; level >= 0; level--)
6611  {
6612  // Find our resolutions.
6613  int xres = (lastlevel->getXRes() + 1) >> 1;
6614  int yres = (lastlevel->getYRes() + 1) >> 1;
6615  int zres = (lastlevel->getZRes() + 1) >> 1;
6616 
6617  levels[level] = new UT_VoxelArray<T>;
6618  levels[level]->size(xres, yres, zres);
6619 
6620  downsample(*levels[level], *lastlevel, function);
6621 
6622  lastlevel = levels[level];
6623  }
6624  }
6625 }
6626 
6627 template <typename T>
6628 void
6631  const UT_VoxelArray<T> &src,
6632  mipmaptype function,
6633  const UT_JobInfo &info)
6634 {
6635  UT_VoxelArrayIterator<T> vit(&dst);
6636 
6637  vit.splitByTile(info);
6638  vit.setCompressOnExit(true);
6639 
6640  for (vit.rewind(); !vit.atEnd(); vit.advance())
6641  {
6642  int x = 2*vit.x();
6643  int y = 2*vit.y();
6644  int z = 2*vit.z();
6645  T sam[8];
6646 
6647  if (src.extractSample(x, y, z, sam))
6648  {
6649  vit.setValue(sam[0]);
6650  }
6651  else
6652  {
6653  vit.setValue(
6654  mixValues(
6655  mixValues(
6656  mixValues(sam[0], sam[1], function),
6657  mixValues(sam[2], sam[3], function), function),
6658  mixValues(
6659  mixValues(sam[4], sam[5], function),
6660  mixValues(sam[6], sam[7], function), function),
6661  function));
6662  }
6663  }
6664 }
6665 
6666 
6667 template <typename T>
6668 int64
6670 {
6671  int64 mem = inclusive ? sizeof(*this) : 0;
6672 
6673  mem += myLevels.getMemoryUsage(false);
6674  mem += myLevels.entries() * myNumLevels * sizeof(UT_VoxelArray<T>*);
6675  for (exint j = 0; j < myLevels.entries(); j++)
6676  {
6677  for (int i = 0; i < myNumLevels; i++)
6678  mem += myLevels(j)[i]->getMemoryUsage(true);
6679  }
6680 
6681  return mem;
6682 }
6683 
6684 template <typename T>
6685 void
6686 UT_VoxelMipMap<T>::traverseTopDown(Callback function, void *data) const
6687 {
6688  doTraverse(0, 0, 0, 0, function, data);
6689 }
6690 
6691 template <typename T>
6692 void
6693 UT_VoxelMipMap<T>::doTraverse(int x, int y, int z, int level,
6694  Callback function, void *data) const
6695 {
6696  UT_StackBuffer<T> tval(myLevels.entries());
6697  bool isfinal;
6698  UT_VoxelArray<T> *vox;
6699  int shift;
6700 
6701  if (level == myNumLevels)
6702  {
6703  isfinal = true;
6704  vox = myBaseLevel;
6705  tval[0] = (*vox)(x, y, z);
6706  for (int i = 1; i < myLevels.entries(); i++)
6707  tval[i] = tval[0];
6708  }
6709  else
6710  {
6711  isfinal = false;
6712  for (int i = 0; i < myLevels.entries(); i++)
6713  {
6714  vox = myLevels(i)[level];
6715  tval[i] = (*vox)(x, y, z);
6716  }
6717  }
6718 
6719  shift = myNumLevels - level;
6720 
6721  UT_BoundingBox box;
6722 
6723  box.initBounds(SYSmin(x << shift, myBaseLevel->getXRes()),
6724  SYSmin(y << shift, myBaseLevel->getYRes()),
6725  SYSmin(z << shift, myBaseLevel->getZRes()));
6726  box.enlargeBounds(SYSmin((x+1) << shift, myBaseLevel->getXRes()),
6727  SYSmin((y+1) << shift, myBaseLevel->getYRes()),
6728  SYSmin((z+1) << shift, myBaseLevel->getZRes()));
6729 
6730  if (!function(tval, box, isfinal, data))
6731  {
6732  // Function asked for an early exit.
6733  // Give it.
6734  return;
6735  }
6736 
6737  // We now want to proceed to the next level.
6738  if (isfinal)
6739  return;
6740 
6741  level++;
6742  shift--;
6743  x <<= 1;
6744  y <<= 1;
6745  z <<= 1;
6746 
6747  bool xinc, yinc, zinc;
6748 
6749  // Determine which of our children are valid.
6750  if ( ((x+1) << shift) < myBaseLevel->getXRes() )
6751  xinc = true;
6752  else
6753  xinc = false;
6754  if ( ((y+1) << shift) < myBaseLevel->getYRes() )
6755  yinc = true;
6756  else
6757  yinc = false;
6758  if ( ((z+1) << shift) < myBaseLevel->getZRes() )
6759  zinc = true;
6760  else
6761  zinc = false;
6762 
6763  //
6764  // Traverse all valid children. Note that this is deliberately a gray
6765  // order traversal for full nodes.
6766  //
6767 
6768  doTraverse(x, y, z, level, function, data);
6769 
6770  if (yinc)
6771  {
6772  doTraverse(x, y+1, z, level, function, data);
6773  if (zinc)
6774  doTraverse(x, y+1, z+1, level, function, data);
6775  }
6776  if (zinc)
6777  doTraverse(x, y, z+1, level, function, data);
6778 
6779  if (xinc)
6780  {
6781  if (zinc)
6782  doTraverse(x+1, y, z+1, level, function, data);
6783  doTraverse(x+1, y, z, level, function, data);
6784  if (yinc)
6785  {
6786  doTraverse(x+1, y+1, z, level, function, data);
6787  if (zinc)
6788  doTraverse(x+1, y+1, z+1, level, function, data);
6789  }
6790  }
6791 }
6792 
6793 template <typename T>
6794 template <typename OP>
6795 void
6797 {
6798  doTraverse(0, 0, 0, numLevels()-1, op);
6799 }
6800 
6801 template <typename T>
6802 template <typename OP>
6803 void
6804 UT_VoxelMipMap<T>::doTraverse(int x, int y, int z, int level,
6805  OP &op) const
6806 {
6807  int shift;
6808 
6809  shift = level;
6810 
6811  UT_BoundingBoxI box;
6812 
6813  box.initBounds(SYSmin(x << shift, myBaseLevel->getXRes()),
6814  SYSmin(y << shift, myBaseLevel->getYRes()),
6815  SYSmin(z << shift, myBaseLevel->getZRes()));
6816  box.enlargeBounds(SYSmin((x+1) << shift, myBaseLevel->getXRes()),
6817  SYSmin((y+1) << shift, myBaseLevel->getYRes()),
6818  SYSmin((z+1) << shift, myBaseLevel->getZRes()));
6819 
6820  if (!op(box, level))
6821  {
6822  // Function asked for an early exit.
6823  // Give it.
6824  return;
6825  }
6826 
6827  level--;
6828  // We now want to proceed to the next level.
6829  if (level < 0)
6830  return;
6831 
6832  x <<= 1;
6833  y <<= 1;
6834  z <<= 1;
6835 
6836  bool xinc, yinc, zinc;
6837 
6838  // Determine which of our children are valid.
6839  if ( ((x+1) << level) < myBaseLevel->getXRes() )
6840  xinc = true;
6841  else
6842  xinc = false;
6843  if ( ((y+1) << level) < myBaseLevel->getYRes() )
6844  yinc = true;
6845  else
6846  yinc = false;
6847  if ( ((z+1) << level) < myBaseLevel->getZRes() )
6848  zinc = true;
6849  else
6850  zinc = false;
6851 
6852  //
6853  // Traverse all valid children. Note that this is deliberately a gray
6854  // order traversal for full nodes.
6855  //
6856 
6857  doTraverse(x, y, z, level, op);
6858 
6859  if (yinc)
6860  {
6861  doTraverse(x, y+1, z, level, op);
6862  if (zinc)
6863  doTraverse(x, y+1, z+1, level, op);
6864  }
6865  if (zinc)
6866  doTraverse(x, y, z+1, level, op);
6867 
6868  if (xinc)
6869  {
6870  if (zinc)
6871  doTraverse(x+1, y, z+1, level, op);
6872  doTraverse(x+1, y, z, level, op);
6873  if (yinc)
6874  {
6875  doTraverse(x+1, y+1, z, level, op);
6876  if (zinc)
6877  doTraverse(x+1, y+1, z+1, level, op);
6878  }
6879  }
6880 }
6881 
6882 template <typename T>
6883 template <typename OP>
6884 void
6886 {
6887  doTraverseSorted(0, 0, 0, numLevels()-1, op);
6888 }
6889 
6891 {
6892 public:
6893  float value;
6894  int key;
6895 };
6896 
6898 {
6899  return lhs.value < rhs.value;
6900 }
6901 
6902 template <typename T>
6903 template <typename OP>
6904 void
6905 UT_VoxelMipMap<T>::doTraverseSorted(int x, int y, int z, int level,
6906  OP &op) const
6907 {
6908  UT_BoundingBoxI box;
6909 
6910  box.initBounds(SYSmin(x << level, myBaseLevel->getXRes()),
6911  SYSmin(y << level, myBaseLevel->getYRes()),
6912  SYSmin(z << level, myBaseLevel->getZRes()));
6913  box.enlargeBounds(SYSmin((x+1) << level, myBaseLevel->getXRes()),
6914  SYSmin((y+1) << level, myBaseLevel->getYRes()),
6915  SYSmin((z+1) << level, myBaseLevel->getZRes()));
6916 
6917  if (!op(box, level))
6918  {
6919  // Function asked for an early exit.
6920  // Give it.
6921  return;
6922  }
6923 
6924  level--;
6925  // We now want to proceed to the next level.
6926  if (level < 0)
6927  return;
6928 
6929  x <<= 1;
6930  y <<= 1;
6931  z <<= 1;
6932 
6933  bool xinc, yinc, zinc;
6934 
6935  // Determine which of our children are valid.
6936  if ( ((x+1) << level) < myBaseLevel->getXRes() )
6937  xinc = true;
6938  else
6939  xinc = false;
6940  if ( ((y+1) << level) < myBaseLevel->getYRes() )
6941  yinc = true;
6942  else
6943  yinc = false;
6944  if ( ((z+1) << level) < myBaseLevel->getZRes() )
6945  zinc = true;
6946  else
6947  zinc = false;
6948 
6949  UT_BoundingBoxI boxes[8];
6950  int numboxes = 0;
6951 
6952  // Build all of our bounding boxes.
6953  for (int dz = 0; dz < 2; dz++)
6954  {
6955  if (dz && !zinc)
6956  continue;
6957  for (int dy = 0; dy < 2; dy++)
6958  {
6959  if (dy && !yinc)
6960  continue;
6961  for (int dx = 0; dx < 2; dx++)
6962  {
6963  if (dx && !xinc)
6964  continue;
6965  boxes[numboxes].initBounds(
6966  SYSmin((x+dx) << level, myBaseLevel->getXRes()),
6967  SYSmin((y+dy) << level, myBaseLevel->getYRes()),
6968  SYSmin((z+dz) << level, myBaseLevel->getZRes()));
6969  boxes[numboxes].enlargeBounds(
6970  SYSmin((x+dx+1) << level, myBaseLevel->getXRes()),
6971  SYSmin((y+dy+1) << level, myBaseLevel->getYRes()),
6972  SYSmin((z+dz+1) << level, myBaseLevel->getZRes()));
6973  numboxes++;
6974  }
6975  }
6976  }
6977 
6978  ut_VoxelMipMapSortCompare sortstats[8];
6979  for (int i = 0; i < numboxes; i++)
6980  {
6981  sortstats[i].value = op.sortValue(boxes[i], level);
6982  sortstats[i].key = i;
6983  }
6984  std::stable_sort(sortstats, &sortstats[numboxes]);
6985 
6986  for (int i = 0; i < numboxes; i++)
6987  {
6988  int whichbox = sortstats[i].key;
6989  doTraverseSorted(boxes[whichbox](0,0)>>level, boxes[whichbox](1,0)>>level, boxes[whichbox](2,0)>>level, level, op);
6990  }
6991 }
6992 
6993 template <typename T>
6994 void
6996 {
6997  myNumLevels = 0;
6998  myBaseLevel = 0;
6999  myOwnBase = false;
7000 }
7001 
7002 template <typename T>
7003 void
7005 {
7006  for (exint i = 0; i < myLevels.entries(); i++)
7007  {
7008  for (exint level = 0; level < myNumLevels; level++)
7009  delete myLevels(i)[level];
7010  delete [] myLevels(i);
7011  }
7012  myLevels.entries(0);
7013 
7014  if (myOwnBase)
7015  delete myBaseLevel;
7016 
7017  initializePrivate();
7018 }
7019 
7020 //
7021 // UT_VoxelArrayIterator implementation
7022 //
7023 template <typename T>
7025 {
7026  myArray = 0;
7027  myHandle.resetHandle();
7028  myCurTile = -1;
7029  myShouldCompressOnExit = false;
7030  myUseTileList = false;
7031  myJobInfo = 0;
7032  myInterrupt = 0;
7033 }
7034 
7035 template <typename T>
7037 {
7038  myShouldCompressOnExit = false;
7039  myUseTileList = false;
7040  myJobInfo = 0;
7041  setArray(vox);
7042  myInterrupt = 0;
7043 }
7044 
7045 template <typename T>
7047 {
7048  myShouldCompressOnExit = false;
7049  myUseTileList = false;
7050  myJobInfo = 0;
7051  setHandle(handle);
7052  myInterrupt = 0;
7053 }
7054 
7055 template <typename T>
7057 {
7058 }
7059 
7060 template <typename T>
7061 void
7063 {
7064  int numtiles;
7065  fpreal tileperrange;
7066 
7067  // Must already have been set.
7068  UT_ASSERT(myArray);
7069  if (!myArray)
7070  return;
7071 
7072  // Divide our tiles in to num ranges groups.
7073  numtiles = myArray->numTiles();
7074 
7075  // If we are using a tile list, use it instead
7076  if (myUseTileList)
7077  numtiles = myTileList.entries();
7078 
7079  // Trivial case.
7080  if (!numtiles)
7081  {
7082  myTileStart = 0;
7083  myTileEnd = 0;
7084  return;
7085  }
7086 
7087  // Sanity check idx.
7088  if (idx < 0 || idx >= numranges)
7089  {
7090  UT_ASSERT(!"Idx out of bounds");
7091  myTileStart = -1;
7092  myTileEnd = -1;
7093  return;
7094  }
7095 
7096  // Sanity test numranges
7097  if (numranges < 1)
7098  {
7099  UT_ASSERT(!"Invalid range count!");
7100  numranges = 1;
7101  }
7102 
7103  // Compute tile per range.
7104  // We use floating point so, if you have 15 tiles and
7105  // 16 processors, you don't decide to do all 15
7106  // tiles on one processor.
7107  tileperrange = (fpreal)numtiles / (fpreal)numranges;
7108 
7109  myTileStart = (int) SYSfloor(idx * tileperrange);
7110  myTileEnd = (int) SYSfloor((idx+1) * tileperrange);
7111 
7112  // Special case to ensure we always get the last tile,
7113  // despite the evils of floating point.
7114  if (idx == numranges-1)
7115  myTileEnd = numtiles;
7116 
7117  UT_ASSERT(myTileStart >= 0);
7118  UT_ASSERT(myTileEnd <= numtiles);
7119 }
7120 
7121 template <typename T>
7122 void
7124 {
7125  // Must already have been set.
7126  UT_ASSERT(myArray);
7127  if (!myArray)
7128  return;
7129 
7130  // If there is one thread, don't bother
7131  if (info.numJobs() == 1)
7132  myJobInfo = 0;
7133  else
7134  myJobInfo = &info;
7135 }
7136 
7137 template <typename T>
7138 void
7140 {
7141  UT_Vector3 pmin, pmax, vmin, vmax;
7142 
7143  pmin = bbox.minvec();
7144  pmax = bbox.maxvec();
7145 
7146  pmin.x() = SYSmax(pmin.x(), 0.0f);
7147  pmin.y() = SYSmax(pmin.y(), 0.0f);
7148  pmin.z() = SYSmax(pmin.z(), 0.0f);
7149  pmax.x() = SYSmin(pmax.x(), 1.0f);
7150  pmax.y() = SYSmin(pmax.y(), 1.0f);
7151  pmax.z() = SYSmin(pmax.z(), 1.0f);
7152 
7153  myArray->posToIndex(pmin, vmin);
7154  myArray->posToIndex(pmax, vmax);
7155 
7156  restrictToBBox(SYSfloor(vmin.x()), SYSceil(vmax.x()),
7157  SYSfloor(vmin.y()), SYSceil(vmax.y()),
7158  SYSfloor(vmin.z()), SYSceil(vmax.z()));
7159 }
7160 
7161 template <typename T>
7162 void
7164  int ymin, int ymax,
7165  int zmin, int zmax)
7166 {
7167  int xres, yres, zres, x, y, z;
7168 
7169  xres = myArray->getXRes();
7170  yres = myArray->getYRes();
7171  zres = myArray->getZRes();
7172 
7173  myTileList.entries(0);
7174  myUseTileList = true;
7175  // Make sure we have any tiles that are valid.
7176  if (xmin < xres && xmax >= 0 &&
7177  ymin < yres && ymax >= 0 &&
7178  zmin < zres && zmax >= 0)
7179  {
7180  // Clamp into valid range.
7181  myArray->clampIndex(xmin, ymin, zmin);
7182  myArray->clampIndex(xmax, ymax, zmax);
7183 
7184  // Convert to tiles...
7185  xmin >>= TILEBITS;
7186  ymin >>= TILEBITS;
7187  zmin >>= TILEBITS;
7188  xmax >>= TILEBITS;
7189  ymax >>= TILEBITS;
7190  zmax >>= TILEBITS;
7191 
7192  // Check if we are all accounted for.
7193  if (myArray->numTiles() == (xmax-xmin+1)*(ymax-ymin+1)*(zmax-zmin+1))
7194  {
7195  UT_ASSERT(xmin == 0 && ymin == 0 && zmin == 0);
7196  // No need for a tile list, just run everything!
7197  myUseTileList = false;
7198  }
7199  else
7200  {
7201  myTileList.setCapacity((int64)(zmax-zmin+1) * (int64)(ymax-ymin+1) * (int64)(xmax-xmin+1));
7202 
7203  // Iterate over all active tiles, adding to our list.
7204  for (z = zmin; z <= zmax; z++)
7205  {
7206  for (y = ymin; y <= ymax; y++)
7207  {
7208  for (x = xmin; x <= xmax; x++)
7209  {
7210  myTileList.append(myArray->xyzTileToLinear(x, y, z));
7211  }
7212  }
7213  }
7214  }
7215  }
7216 
7217  // Recompute our ranges.
7218  myCurTile = -1;
7219  setPartialRange(0, 1);
7220 }
7221 
7222 
7223 template <typename T>
7224 void
7226 {
7227  // Ensure we have at least one tile in each direction
7228  if (!myArray ||
7229  !myArray->getRes(0) || !myArray->getRes(1) || !myArray->getRes(2))
7230  {
7231  myCurTile = -1;
7232  return;
7233  }
7234 
7235  if (myUseTileList)
7236  {
7237  if (myJobInfo)
7238  myCurTileListIdx = myJobInfo->nextTask();
7239  else
7240  myCurTileListIdx = myTileStart;
7241  if (myCurTileListIdx < 0 || myCurTileListIdx >= myTileEnd)
7242  {
7243  myCurTile = -1;
7244  return;
7245  }
7246  myCurTile = myTileList(myCurTileListIdx);
7247  }
7248  else
7249  {
7250  if (myJobInfo)
7251  myCurTile = myJobInfo->nextTask();
7252  else
7253  myCurTile = myTileStart;
7254  // If this is an empty range, we quit right away.
7255  if (myCurTile < 0 || myCurTile >= myTileEnd)
7256  {
7257  myCurTile = -1;
7258  return;
7259  }
7260  }
7261 
7262  UT_VoxelTile<T> *tile;
7263 
7264  tile = myArray->getLinearTile(myCurTile);
7265 
7266  // Get the tile position
7267  if (myCurTile)
7268  {
7269  myArray->linearTileToXYZ(myCurTile,
7270  myTilePos[0], myTilePos[1], myTilePos[2]);
7271  myPos[0] = TILESIZE * myTilePos[0];
7272  myPos[1] = TILESIZE * myTilePos[1];
7273  myPos[2] = TILESIZE * myTilePos[2];
7274  }
7275  else
7276  {
7277  myTilePos[0] = 0;
7278  myTilePos[1] = 0;
7279  myTilePos[2] = 0;
7280  myPos[0] = 0;
7281  myPos[1] = 0;
7282  myPos[2] = 0;
7283  }
7284 
7285  myTileLocalPos[0] = 0;
7286  myTileLocalPos[1] = 0;
7287  myTileLocalPos[2] = 0;
7288 
7289  myTileSize[0] = tile->xres();
7290  myTileSize[1] = tile->yres();
7291  myTileSize[2] = tile->zres();
7292 }
7293 
7294 template <typename T>
7295 void
7297 {
7298  if (myInterrupt && myInterrupt->opInterrupt())
7299  {
7300  myCurTile = -1;
7301  return;
7302  }
7303  if (myUseTileList)
7304  {
7305  if (getCompressOnExit())
7306  {
7307  // Verify our last tile was a legitimate one.
7308  if (myCurTile >= 0 && myCurTileListIdx < myTileEnd)
7309  {
7310  myArray->getLinearTile(myCurTile)->tryCompress(myArray->getCompressionOptions());
7311  }
7312  }
7313 
7314  // Advance our myCurTileListIdx and rebuild from
7315  if (myJobInfo)
7316  myCurTileListIdx = myJobInfo->nextTask();
7317  else
7318  myCurTileListIdx++;
7319  if (myCurTileListIdx >= myTileEnd)
7320  {
7321  myCurTile = -1;
7322  return;
7323  }
7324 
7325  myCurTile = myTileList(myCurTileListIdx);
7326 
7327  myArray->linearTileToXYZ(myCurTile,
7328  myTilePos[0], myTilePos[1], myTilePos[2]);
7329 
7330  UT_VoxelTile<T> *tile;
7331 
7332  tile = myArray->getLinearTile(myCurTile);
7333  myTileLocalPos[0] = 0;
7334  myTileLocalPos[1] = 0;
7335  myTileLocalPos[2] = 0;
7336  myTileSize[0] = tile->xres();
7337  myTileSize[1] = tile->yres();
7338  myTileSize[2] = tile->zres();
7339 
7340  myPos[0] = TILESIZE * myTilePos[0];
7341  myPos[1] = TILESIZE * myTilePos[1];
7342  myPos[2] = TILESIZE * myTilePos[2];
7343  return;
7344  }
7345 
7346  // If requested, compress our last tile.
7347  if (getCompressOnExit())
7348  {
7349  // Verify our last tile was a legitimate one.
7350  if (myCurTile >= 0 && myCurTile < myTileEnd)
7351  {
7352  myArray->getLinearTile(myCurTile)->tryCompress(myArray->getCompressionOptions());
7353  }
7354  }
7355 
7356  if (myJobInfo)
7357  {
7358  myCurTile = myJobInfo->nextTask();
7359  if (myCurTile >= myTileEnd)
7360  {
7361  myCurTile = -1;
7362  return;
7363  }
7364  myArray->linearTileToXYZ(myCurTile,
7365  myTilePos[0], myTilePos[1], myTilePos[2]);
7366  }
7367  else
7368  {
7369  // Advance our myTilePos, then rebuild everything from there.
7370  myTilePos[0]++;
7371  if (myTilePos[0] >= myArray->getTileRes(0))
7372  {
7373  myTilePos[0] = 0;
7374  myTilePos[1]++;
7375  if (myTilePos[1] >= myArray->getTileRes(1))
7376  {
7377  myTilePos[1] = 0;
7378  myTilePos[2]++;
7379  if (myTilePos[2] >= myArray->getTileRes(2))
7380  {
7381  // All done!
7382  myCurTile = -1;
7383  return;
7384  }
7385  }
7386  }
7387  myCurTile = myArray->xyzTileToLinear(myTilePos[0], myTilePos[1], myTilePos[2]);
7388  }
7389 
7390  UT_VoxelTile<T> *tile;
7391 
7392  // Rebuild our settings from this tile.
7393 
7394  // See if we hit our end.
7395  if (myCurTile >= myTileEnd)
7396  {
7397  myCurTile = -1;
7398  return;
7399  }
7400 
7401  tile = myArray->getLinearTile(myCurTile);
7402  myTileLocalPos[0] = 0;
7403  myTileLocalPos[1] = 0;
7404  myTileLocalPos[2] = 0;
7405  myTileSize[0] = tile->xres();
7406  myTileSize[1] = tile->yres();
7407  myTileSize[2] = tile->zres();
7408 
7409  myPos[0] = TILESIZE * myTilePos[0];
7410  myPos[1] = TILESIZE * myTilePos[1];
7411  myPos[2] = TILESIZE * myTilePos[2];
7412 }
7413 
7414 
7415 template <typename T>
7416 void
7418 {
7419  myTileLocalPos[0] = myTileSize[0];
7420  myTileLocalPos[1] = myTileSize[1];
7421  myTileLocalPos[2] = myTileSize[2];
7422 }
7423 
7424 template <typename T>
7425 template <typename OP>
7426 void
7428 {
7429  rewind();
7430 
7431  while (!atEnd())
7432  {
7433  UT_VoxelTile<T> *tile;
7434 
7435  tile = myArray->getLinearTile(myCurTile);
7436 
7437  if (!tile->isSimpleCompression())
7438  tile->uncompress();
7439 
7440  if (tile->isConstant())
7441  {
7442  T val;
7443 
7444  val = tile->rawData()[0];
7445 
7446  val = op(val, a);
7447 
7448  tile->rawData()[0] = val;
7449  }
7450  else
7451  {
7452  T *val;
7453 
7454  val = tile->rawData();
7455 
7456  int n = tile->numVoxels();
7457  for (int i = 0; i < n; i++)
7458  {
7459  val[i] = op(val[i], a);
7460  }
7461  }
7462 
7463  advanceTile();
7464  }
7465 }
7466 
7467 template <typename T>
7468 template <typename OP, typename S>
7469 void
7471  const UT_VoxelArray<S> &a)
7472 {
7473  UT_ASSERT(myArray->isMatching(a));
7474 
7475  rewind();
7476 
7477  while (!atEnd())
7478  {
7479  UT_VoxelTile<S> *atile;
7480  UT_VoxelTile<T> *tile;
7481 
7482  tile = myArray->getLinearTile(myCurTile);
7483  atile = a.getLinearTile(myCurTile);
7484 
7485  if (!atile->isSimpleCompression())
7486  {
7487  // Have to use getValue...
7488  for (int z = 0; z < tile->zres(); z++)
7489  for (int y = 0; y < tile->yres(); y++)
7490  for (int x = 0; x < tile->xres(); x++)
7491  {
7492  S aval;
7493  T val;
7494 
7495  val = tile->operator()(x, y, z);
7496  aval = atile->operator()(x, y, z);
7497 
7498  val = op(val, aval);
7499 
7500  tile->setValue(x, y, z, val);
7501  }
7502  }
7503  else
7504  {
7505  int ainc = atile->isConstant() ? 0 : 1;
7506 
7507  // Check if the incoming tile is constant and is a no-op
7508  // If so, we do not want to decompress!
7509  if (!ainc)
7510  {
7511  S aval;
7512  aval = atile->rawData()[0];
7513  if (op.isNoop(aval))
7514  {
7515  advanceTile();
7516  continue;
7517  }
7518  }
7519 
7520  if (!tile->isSimpleCompression())
7521  tile->uncompress();
7522 
7523  if (tile->isConstant())
7524  {
7525  if (!ainc)
7526  {
7527  // Woohoo! Too simple!
7528  S aval;
7529  T val;
7530 
7531  val = tile->rawData()[0];
7532  aval = atile->rawData()[0];
7533 
7534  val = op(val, aval);
7535 
7536  tile->rawData()[0] = val;
7537  }
7538  else
7539  {
7540  tile->uncompress();
7541  }
7542  }
7543 
7544  // In case we uncompressed ourself above...
7545  // we have to test again
7546  if (!tile->isConstant())
7547  {
7548  const S *aval;
7549  T *val;
7550 
7551  val = tile->rawData();
7552  aval = atile->rawData();
7553  ainc = atile->isConstant() ? 0 : 1;
7554 
7555  int n = tile->numVoxels();
7556  for (int i = 0; i < n; i++)
7557  {
7558  val[i] = op(val[i], *aval);
7559  aval += ainc;
7560  }
7561  }
7562  }
7563 
7564  advanceTile();
7565  }
7566 }
7567 
7568 template <typename T>
7569 template <typename OP>
7570 void
7572 {
7573  rewind();
7574 
7575  if (op.isNoop(a))
7576  {
7577  while (!atEnd())
7578  advanceTile();
7579  return;
7580  }
7581 
7582  applyOperation(op, a);
7583 }
7584 
7585 template<int OPERANDS, bool USE_SELF,
7586  typename T, typename OP, typename S, typename R, typename Q>
7587 static inline T
7588 conditionalCallOperator(const OP& op, const T& a0, const S& a1, const R& a2, const Q& a3)
7589 {
7590  // Call the correct () operator, depending on how many operands there are.
7591  T val;
7592  if constexpr (OPERANDS == 0 && USE_SELF)
7593  val = op(a0);
7594  else if constexpr (OPERANDS == 1 && !USE_SELF)
7595  val = op(a1);
7596  else if constexpr (OPERANDS == 1 && USE_SELF)
7597  val = op(a0, a1);
7598  else if constexpr (OPERANDS == 2 && !USE_SELF)
7599  val = op(a1, a2);
7600  else if constexpr (OPERANDS == 2 && USE_SELF)
7601  val = op(a0, a1, a2);
7602  else if constexpr (OPERANDS == 3 && !USE_SELF)
7603  val = op(a1, a2, a3);
7604  else if constexpr (OPERANDS == 3 && USE_SELF)
7605  val = op(a0, a1, a2, a3);
7606 
7607  return val;
7608 }
7609 
7610 /// To avoid code duplication, this helper function does the
7611 /// actual assignment. Works for number of operands ranging
7612 /// from 0 to 3, and masking on or off.
7613 template<int OPERANDS, bool MASKED, bool USE_SELF,
7614  typename T, typename OP, typename S, typename R, typename Q, typename M,
7615  typename ITERATOR>
7616 static inline void
7617 assignOperationOnIterator(ITERATOR &it, const OP& op, const UT_VoxelArray<S>* a,
7618  const UT_VoxelArray<R>* b, const UT_VoxelArray<Q>* c,
7619  const UT_VoxelArray<M>* mask)
7620 {
7621  // Works with at most 3 extra operands...
7622  UT_ASSERT(OPERANDS <= 3);
7623  // If there are no extra operands, must use our own value...
7624  UT_ASSERT(OPERANDS > 0 || USE_SELF);
7625 
7626  T val;
7627  S aval = {};
7628  R bval = {};
7629  Q cval = {};
7630 
7631  it.rewind();
7632  while (!it.atEnd())
7633  {
7634  // Get the tiles for the iterator and the source values.
7635  int tileNum = it.getLinearTileNum();
7636  UT_VoxelTile<M> *mtile = MASKED ? mask->getLinearTile(tileNum) : nullptr;
7637  UT_VoxelTile<S> *atile = OPERANDS > 0 ? a->getLinearTile(tileNum) : nullptr;
7638  UT_VoxelTile<R> *btile = OPERANDS > 1 ? b->getLinearTile(tileNum) : nullptr;
7639  UT_VoxelTile<Q> *ctile = OPERANDS > 2 ? c->getLinearTile(tileNum) : nullptr;
7640  UT_VoxelTile<T> *tile = it.getTile();
7641 
7642  // Skip if the mask tile exists, is constant and masked out.
7643  if (MASKED && mtile->isConstant() && ((*mtile)(0, 0, 0) <= ((M) 0.5)))
7644  {
7645  it.advanceTile();
7646  continue;
7647  }
7648 
7649  // Check for any complex compression. These don't allow
7650  // rawData so we have to use the getValue path.
7651  if ((USE_SELF && !tile->isSimpleCompression()) ||
7652  (OPERANDS > 0 && !atile->isSimpleCompression()) ||
7653  (OPERANDS > 1 && !btile->isSimpleCompression()) ||
7654  (OPERANDS > 2 && !ctile->isSimpleCompression()) ||
7655  (MASKED && !mtile->isSimpleCompression()))
7656  {
7657  // Have to use getValue...
7658  for (int z = 0; z < tile->zres(); z++)
7659  {
7660  for (int y = 0; y < tile->yres(); y++)
7661  {
7662  for (int x = 0; x < tile->xres(); x++)
7663  {
7664  if (!MASKED || ((*mtile)(x, y, z) > ((M) 0.5)))
7665  {
7666  switch (OPERANDS)
7667  {
7668  case 3:
7669  cval = (*ctile)(x, y, z);
7671  case 2:
7672  bval = (*btile)(x, y, z);
7674  case 1:
7675  aval = (*atile)(x, y, z);
7676  break;
7677  default:
7678  break;
7679  }
7680 
7681  if (USE_SELF) val = (*tile)(x, y, z);
7682 
7683  val = conditionalCallOperator<OPERANDS, USE_SELF>(
7684  op, val, aval, bval, cval);
7685  tile->setValue(x, y, z, val);
7686  }
7687  }
7688  }
7689  }
7690  }
7691  else
7692  {
7693  int ainc = (OPERANDS < 1 || atile->isConstant()) ? 0 : 1;
7694  int binc = (OPERANDS < 2 || btile->isConstant()) ? 0 : 1;
7695  int cinc = (OPERANDS < 3 || ctile->isConstant()) ? 0 : 1;
7696  int minc = (!MASKED || mtile->isConstant()) ? 0 : 1;
7697 
7698  // Check for constant sources. The destination needs
7699  // to be constant if USE_SELF is on.
7700  if ((!USE_SELF || tile->isConstant()) &&
7701  ainc == 0 && binc == 0 && cinc == 0 && minc == 0)
7702  {
7703  switch(OPERANDS)
7704  {
7705  case 3:
7706  cval = ctile->rawData()[0];
7708  case 2:
7709  bval = btile->rawData()[0];
7711  case 1:
7712  aval = atile->rawData()[0];
7713  break;
7714  default:
7715  break;
7716  }
7717 
7718  // Note that we know that we are either not masked or
7719  // the mask is constant; in the latter case, if the
7720  // mask were constant 0, then we would not enter this
7721  // area at all, so no need to check the actual value.
7722  if (USE_SELF) val = tile->rawData()[0];
7723  val = conditionalCallOperator<OPERANDS, USE_SELF>(op,
7724  val, aval, bval, cval);
7725  tile->makeConstant(val);
7726  }
7727  else
7728  {
7729  // Output is varying, so pre-uncompress our tile so
7730  // we can write directly with rawData.
7731  tile->uncompress();
7732  // The tile we uncompressed could well be one of the other operands;
7733  // thus, the increments must be updated.
7734  ainc = (OPERANDS < 1 || atile->isConstant()) ? 0 : 1;
7735  binc = (OPERANDS < 2 || btile->isConstant()) ? 0 : 1;
7736  cinc = (OPERANDS < 3 || ctile->isConstant()) ? 0 : 1;
7737  minc = (!MASKED || mtile->isConstant()) ? 0 : 1;
7738 
7739  const S* a_array = OPERANDS > 0 ? atile->rawData() : nullptr;
7740  const R* b_array = OPERANDS > 1 ? btile->rawData() : nullptr;
7741  const Q* c_array = OPERANDS > 2 ? ctile->rawData() : nullptr;
7742  const M* m_array = MASKED ? mtile->rawData() : nullptr;
7743  T* val_array = tile->rawData();
7744 
7745  int n = tile->numVoxels();
7746  for (int i = 0; i < n; i++)
7747  {
7748  if (!MASKED || (*m_array > ((M) 0.5)))
7749  {
7750  val_array[i] = conditionalCallOperator<OPERANDS, USE_SELF>(op,
7751  val_array[i], OPERANDS > 0 ? *a_array : aval,
7752  OPERANDS > 1 ? *b_array : bval,
7753  OPERANDS > 2 ? *c_array : cval);
7754  }
7755 
7756  // Move along the arrays...
7757  switch(OPERANDS)
7758  {
7759  case 3:
7760  c_array += cinc;
7762  case 2:
7763  b_array += binc;
7765  case 1:
7766  a_array += ainc;
7767  break;
7768  default:
7769  break;
7770  }
7771  if(MASKED)
7772  m_array += minc;
7773  }
7774  }
7775  }
7776 
7777  it.advanceTile();
7778  }
7779 }
7780 
7781 template <typename T>
7782 template <typename OP>
7783 void
7785 {
7786  assignOperationOnIterator<0, false, true, T, OP, float, float, float, float>
7787  (*this, op, nullptr, nullptr, nullptr, nullptr);
7788 }
7789 
7790 template <typename T>
7791 template <typename OP, typename S>
7792 void
7794  const UT_VoxelArray<S> &a)
7795 {
7796  UT_ASSERT(myArray->isMatching(a));
7797 
7798  assignOperationOnIterator<1, false, true, T, OP, S, float, float, float>
7799  (*this, op, &a, nullptr, nullptr, nullptr);
7800 }
7801 
7802 template <typename T>
7803 template <typename OP, typename S, typename R>
7804 void
7806  const UT_VoxelArray<S> &a,
7807  const UT_VoxelArray<R> &b)
7808 {
7809  UT_ASSERT(myArray->isMatching(a));
7810  UT_ASSERT(myArray->isMatching(b));
7811 
7812  assignOperationOnIterator<2, false, true, T, OP, S, R, float, float>
7813  (*this, op, &a, &b, nullptr, nullptr);
7814 }
7815 
7816 template <typename T>
7817 template <typename OP, typename S, typename R, typename Q>
7818 void
7820  const UT_VoxelArray<S> &a,
7821  const UT_VoxelArray<R> &b,
7822  const UT_VoxelArray<Q> &c)
7823 {
7824  UT_ASSERT(myArray->isMatching(a));
7825  UT_ASSERT(myArray->isMatching(b));
7826  UT_ASSERT(myArray->isMatching(c));
7827 
7828  assignOperationOnIterator<3, false, true, T, OP, S, R, Q, float>
7829  (*this, op, &a, &b, &c, nullptr);
7830 }
7831 
7832 template <typename T>
7833 template <typename OP, typename M>
7834 void
7836  const UT_VoxelArray<M> &mask)
7837 {
7838  UT_ASSERT(myArray->isMatching(mask));
7839 
7840  assignOperationOnIterator<0, true, true, T, OP, float, float, float, M>
7841  (*this, op, nullptr, nullptr, nullptr, &mask);
7842 }
7843 
7844 template <typename T>
7845 template <typename OP, typename S, typename M>
7846 void
7848  const UT_VoxelArray<S> &a,
7849  const UT_VoxelArray<M> &mask)
7850 {
7851  UT_ASSERT(myArray->isMatching(a));
7852  UT_ASSERT(myArray->isMatching(mask));
7853 
7854  assignOperationOnIterator<1, true, true, T, OP, S, float, float, M>
7855  (*this, op, &a, nullptr, nullptr, &mask);
7856 }
7857 
7858 template <typename T>
7859 template <typename OP, typename S, typename R, typename M>
7860 void
7862  const UT_VoxelArray<S> &a,
7863  const UT_VoxelArray<R> &b,
7864  const UT_VoxelArray<M> &mask)
7865 {
7866  UT_ASSERT(myArray->isMatching(a));
7867  UT_ASSERT(myArray->isMatching(b));
7868  UT_ASSERT(myArray->isMatching(mask));
7869 
7870  assignOperationOnIterator<2, true, true, T, OP, S, R, float, M>
7871  (*this, op, &a, &b, nullptr, &mask);
7872 }
7873 
7874 template <typename T>
7875 template <typename OP, typename S, typename R, typename Q, typename M>
7876 void
7878  const UT_VoxelArray<S> &a,
7879  const UT_VoxelArray<R> &b,
7880  const UT_VoxelArray<Q> &c,
7881  const UT_VoxelArray<M> &mask)
7882 {
7883  UT_ASSERT(myArray->isMatching(a));
7884  UT_ASSERT(myArray->isMatching(b));
7885  UT_ASSERT(myArray->isMatching(c));
7886  UT_ASSERT(myArray->isMatching(mask));
7887 
7888  assignOperationOnIterator<3, true, true, T, OP, S, R, Q, M>
7889  (*this, op, &a, &b, &c, &mask);
7890 }
7891 
7892 template <typename T>
7893 template <typename OP, typename S>
7894 void
7896  const UT_VoxelArray<S> &a)
7897 {
7898  UT_ASSERT(myArray->isMatching(a));
7899 
7900  assignOperationOnIterator<1, false, false, T, OP, S, float, float, float>
7901  (*this, op, &a, nullptr, nullptr, nullptr);
7902 }
7903 
7904 template <typename T>
7905 template <typename OP, typename S, typename R>
7906 void
7908  const UT_VoxelArray<S> &a,
7909  const UT_VoxelArray<R> &b)
7910 {
7911  UT_ASSERT(myArray->isMatching(a));
7912  UT_ASSERT(myArray->isMatching(b));
7913 
7914  assignOperationOnIterator<2, false, false, T, OP, S, R, float, float>
7915  (*this, op, &a, &b, nullptr, nullptr);
7916 }
7917 
7918 template <typename T>
7919 template <typename OP, typename S, typename R, typename Q>
7920 void
7922  const UT_VoxelArray<S> &a,
7923  const UT_VoxelArray<R> &b,
7924  const UT_VoxelArray<Q> &c)
7925 {
7926  UT_ASSERT(myArray->isMatching(a));
7927  UT_ASSERT(myArray->isMatching(b));
7928  UT_ASSERT(myArray->isMatching(c));
7929 
7930  assignOperationOnIterator<3, false, false, T, OP, S, R, Q, float>
7931  (*this, op, &a, &b, &c, nullptr);
7932 }
7933 
7934 template <typename T>
7935 template <typename OP, typename S, typename M>
7936 void
7938  const UT_VoxelArray<S> &a,
7939  const UT_VoxelArray<M> &mask)
7940 {
7941  UT_ASSERT(myArray->isMatching(a));
7942  UT_ASSERT(myArray->isMatching(mask));
7943 
7944  assignOperationOnIterator<1, true, false, T, OP, S, float, float, M>
7945  (*this, op, &a, nullptr, nullptr, &mask);
7946 }
7947 
7948 template <typename T>
7949 template <typename OP, typename S, typename R, typename M>
7950 void
7952  const UT_VoxelArray<S> &a,
7953  const UT_VoxelArray<R> &b,
7954  const UT_VoxelArray<M> &mask)
7955 {
7956  UT_ASSERT(myArray->isMatching(a));
7957  UT_ASSERT(myArray->isMatching(b));
7958  UT_ASSERT(myArray->isMatching(mask));
7959 
7960  assignOperationOnIterator<2, true, false, T, OP, S, R, float, M>
7961  (*this, op, &a, &b, nullptr, &mask);
7962 }
7963 
7964 template <typename T>
7965 template <typename OP, typename S, typename R, typename Q, typename M>
7966 void
7968  const UT_VoxelArray<S> &a,
7969  const UT_VoxelArray<R> &b,
7970  const UT_VoxelArray<Q> &c,
7971  const UT_VoxelArray<M> &mask)
7972 {
7973  UT_ASSERT(myArray->isMatching(a));
7974  UT_ASSERT(myArray->isMatching(b));
7975  UT_ASSERT(myArray->isMatching(c));
7976  UT_ASSERT(myArray->isMatching(mask));
7977 
7978  assignOperationOnIterator<3, true, false, T, OP, S, R, Q, M>
7979  (*this, op, &a, &b, &c, &mask);
7980 }
7981 
7982 template <typename T>
7983 template <typename OP>
7984 void
7986 {
7987  rewind();
7988 
7989  while (!atEnd())
7990  {
7991  UT_VoxelTile<T> *tile;
7992 
7993  tile = myArray->getLinearTile(myCurTile);
7994 
7995  if (!tile->isSimpleCompression())
7996  {
7997  // Have to use getValue...
7998  for (int z = 0; z < tile->zres(); z++)
7999  for (int y = 0; y < tile->yres(); y++)
8000  for (int x = 0; x < tile->xres(); x++)
8001  {
8002  T val;
8003 
8004  val = tile->operator()(x, y, z);
8005 
8006  op.reduce(val);
8007  }
8008  }
8009  else if (tile->isConstant())
8010  {
8011  // Woohoo! Too simple!
8012  T val;
8013 
8014  val = tile->rawData()[0];
8015  int n = tile->numVoxels();
8016 
8017  op.reduceMany(val, n);
8018  }
8019  else
8020  {
8021  T *val;
8022 
8023  val = tile->rawData();
8024 
8025  int n = tile->numVoxels();
8026  for (int i = 0; i < n; i++)
8027  {
8028  op.reduce(val[i]);
8029  }
8030  }
8031 
8032  advanceTile();
8033  }
8034 }
8035 
8036 //
8037 // UT_VoxelTileIterator implementation
8038 //
8039 template <typename T>
8041 {
8042  myCurTile = 0;
8043  myLinearTileNum = -1;
8044  myArray = 0;
8045  myAtEnd = true;
8046  myShouldCompressOnExit = false;
8047 }
8048 
8049 template <typename T>
8051 {
8052  myCurTile = 0;
8053  myLinearTileNum = -1;
8054  myArray = 0;
8055  myAtEnd = true;
8056  myShouldCompressOnExit = false;
8057  setTile(vit);
8058 }
8059 
8060 template <typename T>
8061 template <typename S>
8063 {
8064  myCurTile = 0;
8065  myLinearTileNum = -1;
8066  myArray = 0;
8067  myAtEnd = true;
8068  myShouldCompressOnExit = false;
8069  setTile(vit, array);
8070 }
8071 
8072 template <typename T>
8074 {
8075 }
8076 
8077 template <typename T>
8078 void
8080 {
8081  // Ensure we have at least one voxel in each direction
8082  if (!myCurTile ||
8083  !myCurTile->xres() || !myCurTile->yres() || !myCurTile->zres())
8084  {
8085  myCurTile = 0;
8086  return;
8087  }
8088 
8089  myPos[0] = myTileStart[0];
8090  myPos[1] = myTileStart[1];
8091  myPos[2] = myTileStart[2];
8092 
8093  myTileLocalPos[0] = 0;
8094  myTileLocalPos[1] = 0;
8095  myTileLocalPos[2] = 0;
8096 
8097  myTileSize[0] = myCurTile->xres();
8098  myTileSize[1] = myCurTile->yres();
8099  myTileSize[2] = myCurTile->zres();
8100 
8101  myAtEnd = false;
8102 }
8103 
8104 template <typename T>
8105 void
8107 {
8108  if (getCompressOnExit())
8109  {
8110  // Verify our last tile was a legitimate one.
8111  if (myCurTile)
8112  {
8113  myCurTile->tryCompress(myArray->getCompressionOptions());
8114  }
8115  }
8116  myAtEnd = true;
8117 }
8118 
8119 template <typename T>
8120 template <typename OP>
8121 void
8123 {
8124  assignOperationOnIterator<0, false, true, T, OP, float, float, float, float>
8125  (*this, op, nullptr, nullptr, nullptr, nullptr);
8126 }
8127 
8128 template <typename T>
8129 template <typename OP, typename S>
8130 void
8132  const UT_VoxelArray<S> &a)
8133 {
8134  UT_ASSERT(myArray->isMatching(a));
8135 
8136  assignOperationOnIterator<1, false, true, T, OP, S, float, float, float>
8137  (*this, op, &a, nullptr, nullptr, nullptr);
8138 }
8139 
8140 template <typename T>
8141 template <typename OP, typename S, typename R>
8142 void
8144  const UT_VoxelArray<S> &a,
8145  const UT_VoxelArray<R> &b)
8146 {
8147  UT_ASSERT(myArray->isMatching(a));
8148  UT_ASSERT(myArray->isMatching(b));
8149 
8150  assignOperationOnIterator<2, false, true, T, OP, S, R, float, float>
8151  (*this, op, &a, &b, nullptr, nullptr);
8152 }
8153 
8154 template <typename T>
8155 template <typename OP, typename S, typename R, typename Q>
8156 void
8158  const UT_VoxelArray<S> &a,
8159  const UT_VoxelArray<R> &b,
8160  const UT_VoxelArray<Q> &c)
8161 {
8162  UT_ASSERT(myArray->isMatching(a));
8163  UT_ASSERT(myArray->isMatching(b));
8164  UT_ASSERT(myArray->isMatching(c));
8165 
8166  assignOperationOnIterator<3, false, true, T, OP, S, R, Q, float>
8167  (*this, op, &a, &b, &c, nullptr);
8168 }
8169 
8170 template <typename T>
8171 template <typename OP, typename S>
8172 void
8174  const UT_VoxelArray<S> &a)
8175 {
8176  UT_ASSERT(myArray->isMatching(a));
8177 
8178  assignOperationOnIterator<1, false, false, T, OP, S, float, float, float>
8179  (*this, op, &a, nullptr, nullptr, nullptr);
8180 }
8181 
8182 template <typename T>
8183 template <typename OP, typename S, typename R>
8184 void
8186  const UT_VoxelArray<S> &a,
8187  const UT_VoxelArray<R> &b)
8188 {
8189  UT_ASSERT(myArray->isMatching(a));
8190  UT_ASSERT(myArray->isMatching(b));
8191 
8192  assignOperationOnIterator<2, false, false, T, OP, S, R, float, float>
8193  (*this, op, &a, &b, nullptr, nullptr);
8194 }
8195 
8196 template <typename T>
8197 template <typename OP, typename S, typename R, typename Q>
8198 void
8200  const UT_VoxelArray<S> &a,
8201  const UT_VoxelArray<R> &b,
8202  const UT_VoxelArray<Q> &c)
8203 {
8204  UT_ASSERT(myArray->isMatching(a));
8205  UT_ASSERT(myArray->isMatching(b));
8206  UT_ASSERT(myArray->isMatching(c));
8207 
8208  assignOperationOnIterator<3, false, false, T, OP, S, R, Q, float>
8209  (*this, op, &a, &b, &c, nullptr);
8210 }
8211 
8212 template <typename T>
8213 template <typename OP>
8214 bool
8216 {
8217  rewind();
8218 
8219  if (!myCurTile->isSimpleCompression())
8220  {
8221  // Have to use getValue...
8222  for (int z = 0; z < myTileSize[2]; z++)
8223  for (int y = 0; y < myTileSize[1]; y++)
8224  for (int x = 0; x < myTileSize[0]; x++)
8225  {
8226  T val;
8227 
8228  val = myCurTile->operator()(x, y, z);
8229 
8230  if (!op.reduce(val))
8231  return false;
8232  }
8233  }
8234  else if (myCurTile->isConstant())
8235  {
8236  // Woohoo! Too simple!
8237  T val;
8238 
8239  val = myCurTile->rawData()[0];
8240  int n = myCurTile->numVoxels();
8241 
8242  if (!op.reduceMany(val, n))
8243  return false;
8244  }
8245  else
8246  {
8247  T *val;
8248 
8249  val = myCurTile->rawData();
8250 
8251  int n = myCurTile->numVoxels();
8252  for (int i = 0; i < n; i++)
8253  {
8254  if (!op.reduce(val[i]))
8255  return false;
8256  }
8257  }
8258  return true;
8259 }
8260 
8261 ///
8262 /// UT_VoxelProbe methods
8263 ///
8264 
8265 template <typename T, bool DoRead, bool DoWrite, bool TestForWrites>
8267 {
8268  myCurLine = 0;
8269  myAllocCacheLine = 0;
8270  myDirty = false;
8271 
8272  myArray = 0;
8273 }
8274 
8275 template <typename T, bool DoRead, bool DoWrite, bool TestForWrites>
8277 {
8278  // A sure signal it hasn't been reset.
8279  myCurLine = 0;
8280  myAllocCacheLine = 0;
8281  myDirty = false;
8282 
8283  setArray(vox, prex, postx);
8284 }
8285 
8286 template <typename T, bool DoRead, bool DoWrite, bool TestForWrites>
8288 {
8289  if (DoWrite)
8290  {
8291  if (!TestForWrites || myDirty)
8292  {
8293  // Final write...
8294  writeCacheLine();
8295  }
8296  }
8297  delete [] myAllocCacheLine;
8298 }
8299 
8300 template <typename T, bool DoRead, bool DoWrite, bool TestForWrites>
8301 void
8303 {
8304  // If in write-only mode, makes no sense to have prex and postx...
8305  if (!DoRead && (prex != 0 || postx != 0))
8306  {
8307  UT_ASSERT(!"Voxel probe cannot be padded if set to not read.");
8308  prex = 0;
8309  postx = 0;
8310  }
8311 
8312  // Round up our pre and post
8313  int prepad, postpad;
8314 
8315  myCurLine = 0;
8316 
8317  prepad = (prex - 3) / 4;
8318  postpad = (postx + 3) / 4;
8319 
8320  myAllocCacheLine = new T [TILESIZE - prepad*4 + postpad*4];
8321  myCacheLine = &myAllocCacheLine[-prepad * 4];
8322 
8323  myPreX = prex;
8324  myPostX = postx;
8325 
8326  myForceCopy = false;
8327  if (myPreX || myPostX)
8328  myForceCopy = true;
8329 
8330  myDirty = false;
8331 
8332  myArray = vox;
8333 }
8334 
8335 template <typename T, bool DoRead, bool DoWrite, bool TestForWrites>
8336 bool
8338 {
8339  // Check if we have to reload our cache.
8340  if (myCurLine && y == myY && z == myZ)
8341  {
8342  if (x < myMaxValidX)
8343  {
8344  if (x == myX+1)
8345  {
8346  // A simple advanceX sufficies
8347  advanceX();
8348  return false;
8349  }
8350  else if (x >= myMinValidX)
8351  {
8352  // We can just recenter our search location.
8353  resetX(x);
8354  // Other functions can't just do advanceX as that
8355  // just does ++ in X.
8356  return true;
8357  }
8358  }
8359  }
8360 
8361  // Store existing cache...
8362  if (DoWrite)
8363  {
8364  if (!TestForWrites || myDirty)
8365  writeCacheLine();
8366  }
8367 
8368  // Everything failed, return to reloading the cache.
8369  reloadCache(x, y, z);
8370 
8371  if (TestForWrites)
8372  myDirty = false;
8373 
8374  return true;
8375 }
8376 
8377 template <typename T, bool DoRead, bool DoWrite, bool TestForWrites>
8378 void
8380 {
8381  UT_VoxelTile<T> *tile;
8382  bool xout = false, yout = false, zout = false;
8383  bool manualbuild = false;
8384 
8385  myX = x;
8386  myY = y;
8387  myZ = z;
8388  myMinValidX = x & ~TILEMASK;
8389  myMaxValidX = myMinValidX + TILESIZE;
8390  // UT_VoxelTile::fillCacheLine will fill in our cache up to the tile size;
8391  // we always want a full cache line, so we're on our own for the remainder.
8392  // This variable holds the number of voxels that need to be manually filled
8393  // in. Note that we should never access those out-of-bound voxels if we are
8394  // not reading--so don't bother filling in in that case.
8395  int topad = DoRead ? SYSmax(0, myMaxValidX - myArray->getXRes()) : 0;
8396 
8397  // We say that x is invalid only when the entire line is outside the array
8398  // proper.
8399  if (myMaxValidX <= 0 || myMinValidX >= myArray->getXRes())
8400  xout = true;
8401  if (y < 0 || y >= myArray->getYRes())
8402  yout = true;
8403  if (z < 0 || z >= myArray->getZRes())
8404  zout = true;
8405 
8406  // If y or z are invalid, they will be invalid for every voxel
8407  if (yout || zout)
8408  {
8409  // We can often handle this by clamping...
8410  switch (myArray->getBorder())
8411  {
8413  buildConstantCache(myArray->getBorderValue());
8414 
8415  // ALL DONE
8416  return;
8417 
8418  case UT_VOXELBORDER_REPEAT:
8419  // Simply modulate our lookup.
8420  if (yout)
8421  {
8422  y %= myArray->getYRes();
8423  if (y < 0)
8424  y += myArray->getYRes();
8425  }
8426  if (zout)
8427  {
8428  z %= myArray->getZRes();
8429  if (z < 0)
8430  z += myArray->getZRes();
8431  }
8432  break;
8433 
8434  case UT_VOXELBORDER_MIRROR:
8435  if (yout)
8436  y = UT_VoxelArray<T>::mirrorCoordinates(y, myArray->getYRes());
8437  if (zout)
8438  z = UT_VoxelArray<T>::mirrorCoordinates(z, myArray->getZRes());
8439  break;
8440 
8441  case UT_VOXELBORDER_STREAK:
8442  {
8443  // Clamp
8444  int tx = 0;
8445  myArray->clampIndex(tx, y, z);
8446  break;
8447  }
8448 
8449  case UT_VOXELBORDER_EXTRAP:
8450  {
8451  // Force a manual build.
8452  manualbuild = true;
8453  break;
8454  }
8455  }
8456  }
8457 
8458  // Note y and z may no longer equal myY and myZ, this is not
8459  // a problem however as we have set up our cached versions
8460  // to the unclamped versions.
8461 
8462  if (xout || manualbuild)
8463  {
8464  // We have to manually build if we are extrap or repeat type,
8465  // not just generate a single constant!
8466  if (myArray->getBorder() == UT_VOXELBORDER_EXTRAP ||
8467  myArray->getBorder() == UT_VOXELBORDER_REPEAT ||
8468  myArray->getBorder() == UT_VOXELBORDER_MIRROR)
8469  {
8470  manualbuild = true;
8471  }
8472 
8473  // If there is no pre & post, this will always be constant.
8474  if (!myPreX && !myPostX && !manualbuild)
8475  {
8476  buildConstantCache(myArray->getValue(x, y, z));
8477 
8478  // ALL DONE
8479  return;
8480  }
8481  else
8482  {
8483  // If we are STREAK or CONSTANT, we have a constant
8484  // value in our own cache.
8485  // If we are REPEAT, we want to modulo our x value
8486  // and run the normal code path.
8487  int i;
8488 
8489  for (i = myPreX; i < 0; i++)
8490  {
8491  myCacheLine[i] = myArray->getValue(myMinValidX+i, y, z);
8492  }
8493 
8494  if ((myArray->getBorder() == UT_VOXELBORDER_EXTRAP) ||
8495  (myArray->getBorder() == UT_VOXELBORDER_REPEAT) ||
8496  (myArray->getBorder() == UT_VOXELBORDER_MIRROR))
8497  {
8498  // Explicitly load extrap values as they are not constant.
8499  for (; i < TILESIZE; i++)
8500  myCacheLine[i] = myArray->getValue(myMinValidX+i, y, z);
8501  }
8502  else
8503  {
8504  // CONSTANT and STREAK will have constant values
8505  // in this
8506  T value = myArray->getValue(x, y, z);
8507  for (; i < TILESIZE; i++)
8508  {
8509  myCacheLine[i] = value;
8510  }
8511  }
8512 
8513  for (; i < TILESIZE + myPostX; i++)
8514  {
8515  myCacheLine[i] = myArray->getValue(myMinValidX+i, y, z);
8516  }
8517 
8518  myCurLine = &myCacheLine[x & TILEMASK];
8519  myStride = 1;
8520 
8521  // ALL DONE
8522  return;
8523  }
8524  }
8525 
8526  int xtile, ytile, ztile, tileidx;
8527  int lx, ly, lz;
8528  int i;
8529 
8530  xtile = x >> TILEBITS;
8531  ytile = y >> TILEBITS;
8532  ztile = z >> TILEBITS;
8533 
8534  // Get our local indices
8535  lx = x & TILEMASK;
8536  ly = y & TILEMASK;
8537  lz = z & TILEMASK;
8538 
8539  tileidx = (ztile * myArray->getTileRes(1) + ytile) * myArray->getTileRes(0);
8540 
8541  if (myPreX)
8542  {
8543  if (xtile)
8544  {
8545  // Simple to fetch...
8546  tile = myArray->getLinearTile(tileidx+xtile-1);
8547  for (i = myPreX; i < 0; i++)
8548  {
8549  // Safe to to & TILEMASK as we know earlier tiles
8550  // are always full...
8551  myCacheLine[i] = (*tile)(i & TILEMASK, ly, lz);
8552  }
8553  }
8554  else
8555  {
8556  if (myArray->getBorder() == UT_VOXELBORDER_REPEAT)
8557  {
8558  int resx = myArray->getXRes();
8559  int xpos;
8560 
8561  xpos = myPreX;
8562  xpos %= resx;
8563  // We add resx to guarantee we are in range.
8564  xpos += resx;
8565 
8566  // Manually invoke getValue()
8567  for (i = myPreX; i < 0; i++)
8568  {
8569  myCacheLine[i] = (*myArray)(xpos, y, z);
8570  xpos++;
8571  if (xpos > resx)
8572  xpos -= resx;
8573  }
8574  }
8575  else if (myArray->getBorder() == UT_VOXELBORDER_MIRROR)
8576  {
8577  int resx = myArray->getXRes();
8578  int resx2 = resx * 2;
8579  // Send the starting X position the the index within the array
8580  // and its one reflection.
8581  int xpos = myPreX % resx2;
8582  if (xpos < 0)
8583  xpos += resx2;
8584  // This is the increment. If we're on even repetitions of the
8585  // array, we move forward, otherwise we move backwards:
8586  // 0 1 2 3 4 5 6 6 5 4 3 2 1 0 0 1 2 3 4 5 6
8587  // \-----------/ \-----------/ \-----------/
8588  // inside decreasing increasing
8589  int dir = 1;
8590  if (xpos >= resx)
8591  {
8592  dir = -1;
8593  xpos = resx2 - xpos - 1;
8594  }
8595 
8596  for (i = myPreX; i < 0; i++)
8597  {
8598  myCacheLine[i] = (*myArray)(xpos, y, z);
8599  xpos += dir;
8600  // If we finished a reflection in the backward direction,
8601  // restart going forward.
8602  if (xpos < 0)
8603  {
8604  xpos = 0;
8605  dir = 1;
8606  }
8607  // If we finished a reflection in the forward direction,
8608  // restart going backward.
8609  else if (xpos >= resx)
8610  {
8611  xpos = resx - 1;
8612  dir = -1;
8613  }
8614  }
8615  }
8616  else
8617  {
8618  T value;
8619 
8620  if (myArray->getBorder() == UT_VOXELBORDER_STREAK)
8621  {
8622  tile = myArray->getLinearTile(tileidx+xtile);
8623  value = (*tile)(0, ly, lz);
8624  }
8625  else
8626  value = myArray->getBorderValue();
8627 
8628  // Fill in value.
8629  for (i = myPreX; i < 0; i++)
8630  myCacheLine[i] = value;
8631  }
8632  }
8633  }
8634 
8635  if (myPostX)
8636  {
8637  int cachelen = TILESIZE;
8638  int resx = myArray->getXRes();
8639 
8640  // Append our end part in.
8641  // First, determine if we read past the end...
8642  if (myMaxValidX + myPostX > myArray->getXRes())
8643  {
8644  // This can be very messy. For example, we may have
8645  // a 1 wide tile after this tile and be looking two voxels
8646  // ahead, which means we can't guarantee our lookup
8647  // is entirely within one tile.
8648  // However, we can break it into two loops.
8649  int xpos = myMaxValidX;
8650 
8651  // Portion that still fits in the next tile...
8652  i = 0;
8653  if (xpos < resx)
8654  {
8655  tile = myArray->getLinearTile(tileidx+xtile+1);
8656  for (; i < myPostX && xpos < resx; i++)
8657  {
8658  myCacheLine[i + cachelen] = (*tile)(i, ly, lz);
8659  xpos++;
8660  }
8661  }
8662  // Portion that reads past the end.
8663  if (i < myPostX)
8664  {
8665  if (myArray->getBorder() == UT_VOXELBORDER_REPEAT)
8666  {
8667  xpos = xpos % resx;
8668 
8669  // Revert to the array operator.
8670  for (; i < myPostX; i++)
8671  {
8672  myCacheLine[i + cachelen] = (*myArray)(xpos, y, z);
8673  xpos++;
8674  if (xpos > resx)
8675  xpos -= resx;
8676  }
8677  }
8678  else if (myArray->getBorder() == UT_VOXELBORDER_MIRROR)
8679  {
8680  // We've just gone past the end of the array, so start
8681  // heading in the opposite direction.
8682  xpos = resx - 1;
8683  int dir = -1;
8684  for (; i < myPostX; i++)
8685  {
8686  myCacheLine[i + cachelen] = (*myArray)(xpos, y, z);
8687  xpos += dir;
8688  // If we finished a reflection in the backward direction,
8689  // restart going forward.
8690  if (xpos < 0)
8691  {
8692  xpos = 0;
8693  dir = 1;
8694  }
8695  // If we finished a reflection in the forward direction,
8696  // restart going backward.
8697  else if (xpos >= resx)
8698  {
8699  xpos = resx - 1;
8700  dir = -1;
8701  }
8702  }
8703  }
8704  else
8705  {
8706  T value;
8707 
8708  if (myArray->getBorder() == UT_VOXELBORDER_STREAK)
8709  {
8710  tile = myArray->getLinearTile(tileidx+xtile);
8711  value = (*tile)(tile->xres()-1, ly, lz);
8712  }
8713  else
8714  value = myArray->getBorderValue();
8715 
8716  for (; i < myPostX; i++)
8717  myCacheLine[i + cachelen] = value;
8718  }
8719  }
8720  }
8721  else
8722  {
8723  // All groovy, we fit in so thus must fit in the next tile
8724  tile = myArray->getLinearTile(tileidx+xtile+1);
8725  for (i = 0; i < myPostX; i++)
8726  {
8727  // Safe to to & TILEMASK as we know earlier tiles
8728  // are always full...
8729  myCacheLine[i + cachelen] = (*tile)(i, ly, lz);
8730  }
8731  }
8732 
8733  }
8734 
8735  tile = myArray->getLinearTile(tileidx+xtile);
8736  // We'll be filling in the rest of the cache line if padding needs to be
8737  // performed; thus, force copy in that case.
8738  myCurLine = tile->fillCacheLine(myCacheLine, myStride, lx, ly, lz,
8739  myForceCopy || topad > 0, DoWrite);
8740 
8741  // Pad the remainder of the cacheline.
8742  if (topad > 0)
8743  {
8744  // Make the array do the extrapolation for us...
8745  if ((myArray->getBorder() == UT_VOXELBORDER_EXTRAP) ||
8746  (myArray->getBorder() == UT_VOXELBORDER_REPEAT) ||
8747  (myArray->getBorder() == UT_VOXELBORDER_MIRROR))
8748  {
8749  for (i = topad; i > 0; i--)
8750  {
8751  myCacheLine[TILESIZE - i]
8752  = myArray->getValue(myMaxValidX - i, y, z);
8753  }
8754  }
8755  else
8756  {
8757  // The rest of the cache line is constant in these cases...
8758  T value;
8759  if (myArray->getBorder() == UT_VOXELBORDER_STREAK)
8760  // Streak: use the last internal value from the line.
8761  value = myCacheLine[TILESIZE - topad - 1];
8762  else
8763  // Constant: use the border value.
8764  value = myArray->getBorderValue();
8765 
8766  for (i = topad; i > 0; i--)
8767  {
8768  myCacheLine[TILESIZE - i] = value;
8769  }
8770  }
8771  }
8772 }
8773 
8774 template <typename T, bool DoRead, bool DoWrite, bool TestForWrites>
8775 void
8777 {
8778  if (DoWrite)
8779  {
8780  // Force a full copy.
8781  myStride = 1;
8782 
8783  int i;
8784 
8785  for (i = myPreX; i < TILESIZE+myPostX; i++)
8786  myCacheLine[i] = value;
8788  }
8789  else
8790  {
8791  myCacheLine[0] = value;
8792  // These are to ensure our SSE is compatible.
8793  myCacheLine[1] = value;
8794  myCacheLine[2] = value;
8795  myCacheLine[3] = value;
8796 
8798  myStride = 0;
8799  }
8800 }
8801 
8802 template <typename T, bool DoRead, bool DoWrite, bool TestForWrites>
8803 void
8805 {
8806  if (!DoWrite)
8807  {
8808  UT_ASSERT(0);
8809  return;
8810  }
8811  // Ensure we have a valid loaded line, otherwise no point
8812  // doing a write back...
8813  if (!myCurLine)
8814  return;
8815 
8816  // Reset our current line...
8817  myCurLine -= myX - myMinValidX;
8818 
8819  // Determine if we actually have to write back,
8820  // if we had a pointer inside the tile we don't have to.
8821  if (myCurLine != myCacheLine)
8822  return;
8823 
8824  // Look up our voxel
8825  int xtile, ytile, ztile, y, z;
8826  UT_VoxelTile<T> *tile;
8827 
8828  xtile = myMinValidX >> TILEBITS;
8829  ytile = myY >> TILEBITS;
8830  ztile = myZ >> TILEBITS;
8831  y = myY & TILEMASK;
8832  z = myZ & TILEMASK;
8833 
8834  tile = myArray->getTile(xtile, ytile, ztile);
8835 
8836  // Write back our results
8837  tile->writeCacheLine(myCurLine, y, z);
8838 }
8839 
8840 ///
8841 /// VoxelProbeCube functions
8842 ///
8843 template <typename T>
8845 {
8846  myValid = false;
8847 }
8848 
8849 template <typename T>
8851 {
8852 }
8853 
8854 template <typename T>
8855 void
8857 {
8858  UT_ASSERT(vox != nullptr);
8859  myLines[0][0].setConstArray(vox, -1, 1);
8860  myLines[0][1].setConstArray(vox, -1, 1);
8861  myLines[0][2].setConstArray(vox, -1, 1);
8862 
8863  myLines[1][0].setConstArray(vox, -1, 1);
8864  myLines[1][1].setConstArray(vox, -1, 1);
8865  myLines[1][2].setConstArray(vox, -1, 1);
8866 
8867  myLines[2][0].setConstArray(vox, -1, 1);
8868  myLines[2][1].setConstArray(vox, -1, 1);
8869  myLines[2][2].setConstArray(vox, -1, 1);
8870 
8871  myValid = false;
8872 }
8873 
8874 template <typename T>
8875 void
8877 {
8878  UT_ASSERT(vox != nullptr);
8879  /// This coudl be 0,0, but by keeping it the full range
8880  /// we ensure it is legal to rotate when we do a +1
8881  myLines[0][1].setConstArray(vox, -1, 1);
8882 
8883  myLines[1][0].setConstArray(vox, 0, 0);
8884  myLines[1][1].setConstArray(vox, -1, 1);
8885  myLines[1][2].setConstArray(vox, 0, 0);
8886 
8887  myLines[2][1].setConstArray(vox, -1, 1);
8888 
8889  myValid = false;
8890 }
8891 
8892 template <typename T>
8893 bool
8895 {
8896  if (myValid && myZ == z)
8897  {
8898  if (myY == y)
8899  {
8900  // Potential for a simple advance...
8901  if (x < myMaxValidX && x == myX+1)
8902  {
8903  // AdvanceX.
8904  myLines[0][0].advanceX();
8905  myLines[0][1].advanceX();
8906  myLines[0][2].advanceX();
8907 
8908  myLines[1][0].advanceX();
8909  myLines[1][1].advanceX();
8910  myLines[1][2].advanceX();
8911 
8912  myLines[2][0].advanceX();
8913  myLines[2][1].advanceX();
8914  myLines[2][2].advanceX();
8915 
8916  // Update our cache.
8917  myX = x;
8918 
8919  return false;
8920  }
8921  }
8922 #if 1
8923  else if (y == myY+1 && x < myMaxValidX && x >= myMinValidX)
8924  {
8925  // We have finished our x pass and just incremented y by one
8926  // Rather than resetting all our lines we can just swap
8927  // our y+1 lines into our current lines and then run the
8928  // normal reset.
8929  rotateLines(myLines[0][0], myLines[1][0], myLines[2][0]);
8930  rotateLines(myLines[0][1], myLines[1][1], myLines[2][1]);
8931  rotateLines(myLines[0][2], myLines[1][2], myLines[2][2]);
8932 
8933  // The first 6 lines can just reset their X values
8934  // directly
8935  myLines[0][0].resetX(x);
8936  myLines[0][1].resetX(x);
8937  myLines[0][2].resetX(x);
8938 
8939  myLines[1][0].resetX(x);
8940  myLines[1][1].resetX(x);
8941  myLines[1][2].resetX(x);
8942 
8943  // Only the new lines need a reload.
8944  myLines[2][0].setIndex(x, y+1, z-1);
8945  myLines[2][1].setIndex(x, y+1, z);
8946  myLines[2][2].setIndex(x, y+1, z+1);
8947 
8948  // Update the cache values that have changed.
8949  myX = x;
8950  myY = y;
8951 
8952  return true;
8953  }
8954 #endif
8955  }
8956 
8957  // Now just invoke setIndex on all children
8958  myLines[0][0].setIndex(x, y-1, z-1);
8959  myLines[0][1].setIndex(x, y-1, z);
8960  myLines[0][2].setIndex(x, y-1, z+1);
8961 
8962  myLines[1][0].setIndex(x, y, z-1);
8963  myLines[1][1].setIndex(x, y, z);
8964  myLines[1][2].setIndex(x, y, z+1);
8965 
8966  myLines[2][0].setIndex(x, y+1, z-1);
8967  myLines[2][1].setIndex(x, y+1, z);
8968  myLines[2][2].setIndex(x, y+1, z+1);
8969 
8970  // update our cache values
8971  myX = x;
8972  myY = y;
8973  myZ = z;
8974  myValid = true;
8975  myMinValidX = myLines[1][1].myMinValidX;
8976  myMaxValidX = myLines[1][1].myMaxValidX;
8977 
8978  return true;
8979 }
8980 
8981 template <typename T>
8982 bool
8984 {
8985  if (myValid && myZ == z)
8986  {
8987  if (myY == y)
8988  {
8989  // Potential for a simple advance...
8990  if (x < myMaxValidX && x == myX+1)
8991  {
8992  // AdvanceX.
8993  myLines[0][1].advanceX();
8994 
8995  myLines[1][0].advanceX();
8996  myLines[1][1].advanceX();
8997  myLines[1][2].advanceX();
8998 
8999  myLines[2][1].advanceX();
9000 
9001  // Update our cache.
9002  myX = x;
9003 
9004  return false;
9005  }
9006  }
9007  else if (y == myY+1 && x < myMaxValidX && x >= myMinValidX)
9008  {
9009  // We have finished our x pass and just incremented y by one
9010  // We can thus rotate the meaning of our central
9011  // cache lines and just reset their x pointers, leaving
9012  // only three real resets to be done.
9013  rotateLines(myLines[0][1], myLines[1][1], myLines[2][1]);
9014 
9015  myLines[0][1].resetX(x);
9016  myLines[1][1].resetX(x);
9017 
9018  myLines[1][0].setIndex(x, y, z-1);
9019  myLines[1][2].setIndex(x, y, z+1);
9020 
9021  myLines[2][1].setIndex(x, y+1, z);
9022 
9023  myX = x;
9024  myY = y;
9025  return true;
9026  }
9027  }
9028 
9029  // Now just invoke setIndex on all children
9030  myLines[0][1].setIndex(x, y-1, z);
9031 
9032  myLines[1][0].setIndex(x, y, z-1);
9033  myLines[1][1].setIndex(x, y, z);
9034  myLines[1][2].setIndex(x, y, z+1);
9035 
9036  myLines[2][1].setIndex(x, y+1, z);
9037 
9038  // update our cache values
9039  myX = x;
9040  myY = y;
9041  myZ = z;
9042  myValid = true;
9043  myMinValidX = myLines[1][1].myMinValidX;
9044  myMaxValidX = myLines[1][1].myMaxValidX;
9045 
9046  return true;
9047 }
9048 
9049 template <typename T>
9050 fpreal64
9052 {
9053  // These are our derivatives of Phi.
9054  fpreal64 Px, Py, Pz;
9055  fpreal64 Pxx, Pyy, Pzz;
9056  fpreal64 Pxy, Pxz, Pyz;
9057  fpreal64 gradlen;
9058  fpreal64 k;
9059 
9060  // Compute first derivatives.
9061  // dPhi = (Phi+1 - Phi-1) / 2 * dx
9062 
9063  Px = getValue(1, 0, 0) - getValue(-1, 0, 0);
9064  Px *= 0.5 * invvoxelsize.x();
9065 
9066  Py = getValue(0, 1, 0) - getValue(0, -1, 0);
9067  Py *= 0.5 * invvoxelsize.y();
9068 
9069  Pz = getValue(0, 0, 1) - getValue(0, 0, -1);
9070  Pz *= 0.5 * invvoxelsize.z();
9071 
9072  // Compute second derivatives. (Note Pxy == Pyx)
9073 
9074  // d^2Phi = (Phi+1 - 2 Phi + Phi-1) / (dx*dx)
9075  Pxx = getValue(1, 0, 0)
9076  - 2 * getValue(0, 0, 0)
9077  + getValue(-1, 0, 0);
9078  Pxx *= invvoxelsize.x() * invvoxelsize.x();
9079 
9080  Pyy = getValue(0, 1, 0)
9081  - 2 * getValue(0, 0, 0)
9082  + getValue(0, -1, 0);
9083  Pyy *= invvoxelsize.y() * invvoxelsize.y();
9084 
9085  Pzz = getValue(0, 0, 1)
9086  - 2 * getValue(0, 0, 0)
9087  + getValue(0, 0, -1);
9088  Pzz *= invvoxelsize.z() * invvoxelsize.z();
9089 
9090  // A bit more complicated :>
9091  Pxy = getValue(1, 1,0) - getValue(-1, 1,0);
9092  Pxy -= getValue(1,-1,0) - getValue(-1,-1,0);
9093  Pxy *= 0.25 * invvoxelsize.x() * invvoxelsize.y();
9094 
9095  Pxz = getValue(1,0, 1) - getValue(-1,0, 1);
9096  Pxz -= getValue(1,0,-1) - getValue(-1,0,-1);
9097  Pxz *= 0.25 * invvoxelsize.x() * invvoxelsize.z();
9098 
9099  Pyz = getValue(0,1, 1) - getValue(0,-1, 1);
9100  Pyz -= getValue(0,1,-1) - getValue(0,-1,-1);
9101  Pyz *= 0.25 * invvoxelsize.y() * invvoxelsize.z();
9102 
9103  // Calculate the |grad(phi)| term;
9104  gradlen = SYSsqrt(Px * Px + Py * Py + Pz * Pz);
9105 
9106  // Finally, our curvature!
9107  // This is equation 1.8 from the holy book.
9108  // The problem is that it implies that 0 gradient means 0 curvature.
9109  // This is not true!
9110  // Even if Px,Py,Pz == 0, if Pxx != 0, we have a curved surface
9111  // consider a point at the maxima of a circle.
9112  k = Px*Px * (Pyy + Pzz) + Py*Py * (Pxx + Pzz) + Pz*Pz * (Pxx + Pyy);
9113  k -= 2 * (Pxy*Px*Py + Pyz*Py*Pz + Pxz*Px*Pz);
9114 
9115  // Avoid #IND in places with exactly zero curvature.
9116  if (!gradlen)
9117  k = 0;
9118  else
9119  k /= gradlen * gradlen * gradlen;
9120 
9121  // Clamp our curvature...
9122  fpreal64 maxk;
9123 
9124  maxk = invvoxelsize.maxComponent();
9125  if (k < -maxk)
9126  k = -maxk;
9127  if (k > maxk)
9128  k = maxk;
9129 
9130  return k;
9131 }
9132 
9133 template <typename T>
9134 fpreal64
9136 {
9137  fpreal64 Pxx, Pyy, Pzz;
9138  fpreal64 centralval;
9139 
9140  centralval = getValue(0, 0, 0);
9141 
9142  // d^2Phi = (Phi+1 - 2 Phi + Phi-1) / (dx*dx)
9143  Pxx = getValue(1, 0, 0)
9144  - 2 * centralval
9145  + getValue(-1, 0, 0);
9146  Pxx *= invvoxelsize.x() * invvoxelsize.x();
9147 
9148  Pyy = getValue(0, 1, 0)
9149  - 2 * centralval
9150  + getValue(0, -1, 0);
9151  Pyy *= invvoxelsize.y() * invvoxelsize.y();
9152 
9153  Pzz = getValue(0, 0, +1)
9154  - 2 * centralval
9155  + getValue(0, 0, -1);
9156  Pzz *= invvoxelsize.z() * invvoxelsize.z();
9157 
9158  return Pxx + Pyy + Pzz;
9159 }
9160 
9161 template <typename T>
9162 void
9166 {
9167  T *tmpcache, *tmpalloc;
9168 
9169  // We take advantage of the fact we know only a small portion
9170  // of the cache lines needs to be copied.
9171  tmpcache = ym.myCacheLine;
9172  tmpalloc = ym.myAllocCacheLine;
9173  //const T *tmpcur = ym.myCurLine;
9174 
9175  ym.myCacheLine = y0.myCacheLine;
9177  ym.myCurLine = y0.myCurLine;
9178  ym.myStride = y0.myStride;
9179  ym.myY++;
9180 
9181  y0.myCacheLine = yp.myCacheLine;
9183  y0.myCurLine = yp.myCurLine;
9184  y0.myStride = yp.myStride;
9185  y0.myY++;
9186 
9187  yp.myCacheLine = tmpcache;
9188  yp.myAllocCacheLine = tmpalloc;
9189  // Setting to zero will force a rebuild.
9190  yp.myCurLine = 0;
9191 }
9192 
9193 ///
9194 /// UT_VoxelProbeFace methods
9195 ///
9196 template <typename T>
9198 {
9199  myValid = false;
9200 }
9201 
9202 template <typename T>
9204 {
9205 }
9206 
9207 
9208 template <typename T>
9209 void
9211 {
9212  // We need one extra to the right on the X probe
9213  myLines[0][0].setConstArray(vx, 0, 1);
9214 
9215  // The rest can be direct reads
9216  myLines[1][0].setConstArray(vy, 0, 0);
9217  myLines[1][1].setConstArray(vy, 0, 0);
9218 
9219  myLines[2][0].setConstArray(vz, 0, 0);
9220  myLines[2][1].setConstArray(vz, 0, 0);
9221 
9222  myValid = false;
9223 }
9224 
9225 template <typename T>
9226 void
9228 {
9229  myVoxelSize = size;
9230  myInvVoxelSize = 1;
9231  myInvVoxelSize /= myVoxelSize;
9232 }
9233 
9234 template <typename T>
9235 bool
9237 {
9238  if (myValid && myZ == z)
9239  {
9240  if (myY == y)
9241  {
9242  // Potential for a simple advance...
9243  if (x < myMaxValidX && x == myX+1)
9244  {
9245  // AdvanceX.
9246  myLines[0][0].advanceX();
9247 
9248  myLines[1][0].advanceX();
9249  myLines[1][1].advanceX();
9250 
9251  myLines[2][0].advanceX();
9252  myLines[2][1].advanceX();
9253 
9254  // Update our cache.
9255  myX = x;
9256 
9257  return false;
9258  }
9259  }
9260  else if (y == myY+1 && x < myMaxValidX && x >= myMinValidX)
9261  {
9262  // We have finished our x pass and just incremented y by one
9263  // We can swap our y lines to get to the next read for
9264  // those lines.
9265  swapLines(myLines[1][0], myLines[1][1]);
9266 
9267  myLines[1][0].resetX(x);
9268 
9269  // All the other lines need to be reloaded.
9270  myLines[0][0].setIndex(x, y, z);
9271  myLines[1][1].setIndex(x, y+1, z);
9272 
9273  myLines[2][0].setIndex(x, y, z);
9274  myLines[2][1].setIndex(x, y, z+1);
9275 
9276  myX = x;
9277  myY = y;
9278  return true;
9279  }
9280  }
9281 
9282  // Now just invoke setIndex on all children
9283  myLines[0][0].setIndex(x, y, z);
9284 
9285  myLines[1][0].setIndex(x, y, z);
9286  myLines[1][1].setIndex(x, y+1, z);
9287 
9288  myLines[2][0].setIndex(x, y, z);
9289  myLines[2][1].setIndex(x, y, z+1);
9290 
9291  // update our cache values
9292  myX = x;
9293  myY = y;
9294  myZ = z;
9295  myValid = true;
9296  myMinValidX = myLines[0][0].myMinValidX;
9297  myMaxValidX = myLines[0][0].myMaxValidX;
9298 
9299  return true;
9300 }
9301 
9302 template <typename T>
9303 void
9306 {
9307  T *tmpcache, *tmpalloc;
9308 
9309  // We take advantage of the fact we know only a small portion
9310  // of the cache lines needs to be copied.
9311  tmpcache = ym.myCacheLine;
9312  tmpalloc = ym.myAllocCacheLine;
9313  //const T *tmpcur = ym.myCurLine;
9314 
9315  ym.myCacheLine = yp.myCacheLine;
9317  ym.myCurLine = yp.myCurLine;
9318  ym.myStride = yp.myStride;
9319  ym.myY++;
9320 
9321  yp.myCacheLine = tmpcache;
9322  yp.myAllocCacheLine = tmpalloc;
9323  // Setting to zero will force a rebuild.
9324  yp.myCurLine = 0;
9325 }
9326 
9327 ///
9328 /// VoxelProbeAverage methods
9329 ///
9330 template <typename T, int XStep, int YStep, int ZStep>
9331 void
9333 {
9334  int prex = (XStep < 0) ? XStep : 0;
9335  int postx = (XStep > 0) ? XStep : 0;
9336 
9337  myLines[0][0].setArray((UT_VoxelArray<T> *)vox, prex, postx);
9338  if (YStep)
9339  {
9340  myLines[1][0].setArray((UT_VoxelArray<T> *)vox, prex, postx);
9341  if (ZStep)
9342  {
9343  myLines[1][1].setArray((UT_VoxelArray<T> *)vox, prex, postx);
9344  }
9345  }
9346  if (ZStep)
9347  myLines[0][1].setArray((UT_VoxelArray<T> *)vox, prex, postx);
9348 }
9349 
9350 template <typename T, int XStep, int YStep, int ZStep>
9351 bool
9353 {
9354  bool result = false;
9355 
9356  // Adjust x, y, and z according to our half step.
9357  // y and z negative steps require us decrementing. x steps
9358  // do not require a change as we use the pre/post to affect this,
9359  // if we adjusted the actual x we would get twice the cache misses.
9360  if (YStep < 0)
9361  y--;
9362  if (ZStep < 0)
9363  z--;
9364 
9365  result |= myLines[0][0].setIndex(x, y, z);
9366  if (YStep)
9367  {
9368  result |= myLines[1][0].setIndex(x, y+1, z);
9369  if (ZStep)
9370  result |= myLines[1][1].setIndex(x, y+1, z+1);
9371  }
9372  if (ZStep)
9373  result |= myLines[0][1].setIndex(x, y, z+1);
9374 
9375  return result;
9376 }
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:1952
bool SYSisEqual(const UT_Vector2T< T > &a, const UT_Vector2T< T > &b, S tol)
Componentwise equality.
Definition: UT_Vector2.h:677
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)
SYS_API fpreal32 SYSceil(const fpreal32 val)
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)
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:495
void UTparallelForEachNumber(IntType nitems, const Body &body, const bool force_use_task_scope=true)
const GLdouble * v
Definition: glcorearb.h:837
bool setIndex(const UT_VoxelArrayIterator< S > &vit)
T operator()(UT_Vector3D pos) const
bool setIndex(const UT_VoxelArrayIterator< S > &vit)
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:669
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:242
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:1954
UT_FilterWrap
Definition: UT_FilterType.h:42
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:39
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
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
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:493
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
bool setIndexPlus(const UT_VoxelArrayIterator< S > &vit)
SYS_FORCE_INLINE bool extractSampleAxis(int x, int y, int z, T *sample) const
void resetX(int x)
void setVoxelSize(const UT_Vector3 &voxelsize)
PXL_API bool isRaw(const ColorSpace *s)
static UT_JID jid()
Returns the JID that matches the given type.
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:497
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:164
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:271
#define SYS_FALLTHROUGH
Definition: SYS_Compiler.h:61
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:1059
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
SYS_API fpreal32 SYSfloor(const fpreal32 val)
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
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)
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
exint entries() const
Alias of size(). size() is preferred.
Definition: UT_Array.h:669
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
bool setIndexCube(const UT_VoxelArrayIterator< S > &vit)
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
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:283
UT_Vector3T< T > minvec() const
void toLinearIP(int k, int &x, int &y, int &z) const
#define SYS_FTOLERANCE_R
Definition: SYS_Types.h:289
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:223
float getSupport() const
Definition: UT_Filter.h:164
constexpr SYS_FORCE_INLINE T & w() noexcept
Definition: UT_Vector4.h:499
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 setIndex(const UT_VoxelArrayIterator< S > &vit)
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:165
bool readChar(char &result)
Definition: UT_IStream.h:377
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 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:667
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:410
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:1953
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
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:665