1 //============================================================================
2 //  Copyright (c) Kitware, Inc.
3 //  All rights reserved.
4 //  See LICENSE.txt for details.
5 //
6 //  This software is distributed WITHOUT ANY WARRANTY; without even
7 //  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
8 //  PURPOSE.  See the above copyright notice for more information.
9 //============================================================================
10 #include <vtkm/rendering/raytracing/MeshConnectivityBuilder.h>
11 
12 #include <vtkm/cont/Algorithm.h>
13 #include <vtkm/cont/Timer.h>
14 
15 #include <vtkm/worklet/DispatcherMapField.h>
16 #include <vtkm/worklet/DispatcherMapTopology.h>
17 #include <vtkm/worklet/WorkletMapField.h>
18 #include <vtkm/worklet/WorkletMapTopology.h>
19 
20 #include <vtkm/rendering/raytracing/CellTables.h>
21 #include <vtkm/rendering/raytracing/Logger.h>
22 #include <vtkm/rendering/raytracing/MortonCodes.h>
23 #include <vtkm/rendering/raytracing/RayTracingTypeDefs.h>
24 #include <vtkm/rendering/raytracing/Worklets.h>
25 
26 namespace vtkm
27 {
28 namespace rendering
29 {
30 namespace raytracing
31 {
32 
33 struct IsUnique
34 {
35   VTKM_EXEC_CONT
operator ()vtkm::rendering::raytracing::IsUnique36   inline bool operator()(const vtkm::Int32& x) const { return x < 0; }
37 }; //struct IsExternal
38 
39 class CountFaces : public vtkm::worklet::WorkletMapField
40 {
41 public:
42   VTKM_CONT
CountFaces()43   CountFaces() {}
44   using ControlSignature = void(WholeArrayIn, FieldOut);
45   using ExecutionSignature = void(_1, _2, WorkIndex);
46   template <typename ShapePortalType>
operator ()(const ShapePortalType & shapes,vtkm::Id & faces,const vtkm::Id & index) const47   VTKM_EXEC inline void operator()(const ShapePortalType& shapes,
48                                    vtkm::Id& faces,
49                                    const vtkm::Id& index) const
50   {
51     BOUNDS_CHECK(shapes, index);
52     vtkm::UInt8 shapeType = shapes.Get(index);
53     if (shapeType == vtkm::CELL_SHAPE_TETRA)
54     {
55       faces = 4;
56     }
57     else if (shapeType == vtkm::CELL_SHAPE_HEXAHEDRON)
58     {
59       faces = 6;
60     }
61     else if (shapeType == vtkm::CELL_SHAPE_WEDGE)
62     {
63       faces = 5;
64     }
65     else if (shapeType == vtkm::CELL_SHAPE_PYRAMID)
66     {
67       faces = 5;
68     }
69     else
70     {
71       faces = 0;
72     }
73   }
74 }; //class CountFaces
75 
76 class MortonNeighbor : public vtkm::worklet::WorkletMapField
77 {
78 public:
79   VTKM_CONT
MortonNeighbor()80   MortonNeighbor() {}
81   using ControlSignature = void(WholeArrayIn,
82                                 WholeArrayInOut,
83                                 WholeArrayIn,
84                                 WholeArrayIn,
85                                 WholeArrayIn,
86                                 WholeArrayOut,
87                                 WholeArrayInOut);
88   using ExecutionSignature = void(_1, _2, WorkIndex, _3, _4, _5, _6, _7);
89 
90   VTKM_EXEC
GetShapeOffset(const vtkm::UInt8 & shapeType) const91   inline vtkm::Int32 GetShapeOffset(const vtkm::UInt8& shapeType) const
92   {
93 
94     CellTables tables;
95     //TODO: This might be better as if if if
96     vtkm::Int32 tableOffset = 0;
97     if (shapeType == vtkm::CELL_SHAPE_TETRA)
98     {
99       tableOffset = tables.FaceLookUp(1, 0);
100     }
101     else if (shapeType == vtkm::CELL_SHAPE_HEXAHEDRON)
102     {
103       tableOffset = tables.FaceLookUp(0, 0);
104     }
105     else if (shapeType == vtkm::CELL_SHAPE_WEDGE)
106     {
107       tableOffset = tables.FaceLookUp(2, 0);
108     }
109     else if (shapeType == vtkm::CELL_SHAPE_PYRAMID)
110     {
111       tableOffset = tables.FaceLookUp(3, 0);
112     }
113     else
114       printf("Error shape not recognized %d\n", (int)shapeType);
115 
116     return tableOffset;
117   }
118 
119   VTKM_EXEC
IsIn(const vtkm::Id & needle,const vtkm::Id4 & heystack,const vtkm::Int32 & numIndices) const120   inline bool IsIn(const vtkm::Id& needle,
121                    const vtkm::Id4& heystack,
122                    const vtkm::Int32& numIndices) const
123   {
124     bool isIn = false;
125     for (vtkm::Int32 i = 0; i < numIndices; ++i)
126     {
127       if (needle == heystack[i])
128         isIn = true;
129     }
130     return isIn;
131   }
132 
133 
134   template <typename MortonPortalType,
135             typename FaceIdPairsPortalType,
136             typename ConnPortalType,
137             typename ShapePortalType,
138             typename OffsetPortalType,
139             typename ExternalFaceFlagType,
140             typename UniqueFacesType>
operator ()(const MortonPortalType & mortonCodes,FaceIdPairsPortalType & faceIdPairs,const vtkm::Id & index,const ConnPortalType & connectivity,const ShapePortalType & shapes,const OffsetPortalType & offsets,ExternalFaceFlagType & flags,UniqueFacesType & uniqueFaces) const141   VTKM_EXEC inline void operator()(const MortonPortalType& mortonCodes,
142                                    FaceIdPairsPortalType& faceIdPairs,
143                                    const vtkm::Id& index,
144                                    const ConnPortalType& connectivity,
145                                    const ShapePortalType& shapes,
146                                    const OffsetPortalType& offsets,
147                                    ExternalFaceFlagType& flags,
148                                    UniqueFacesType& uniqueFaces) const
149   {
150     if (index == 0)
151     {
152       return;
153     }
154 
155     vtkm::Id currentIndex = index - 1;
156     BOUNDS_CHECK(mortonCodes, index);
157     vtkm::UInt32 myCode = mortonCodes.Get(index);
158     BOUNDS_CHECK(mortonCodes, currentIndex);
159     vtkm::UInt32 myNeighbor = mortonCodes.Get(currentIndex);
160     bool isInternal = false;
161     vtkm::Id connectedCell = -1;
162 
163     CellTables tables;
164     while (currentIndex > -1 && myCode == myNeighbor)
165     {
166       myNeighbor = mortonCodes.Get(currentIndex);
167       // just because the codes are equal does not mean that
168       // they are the same face. We need to double check.
169       if (myCode == myNeighbor)
170       {
171         //get the index of the shape face in the table.
172         BOUNDS_CHECK(faceIdPairs, index);
173         vtkm::Id cellId1 = faceIdPairs.Get(index)[0];
174         BOUNDS_CHECK(faceIdPairs, currentIndex);
175         vtkm::Id cellId2 = faceIdPairs.Get(currentIndex)[0];
176         BOUNDS_CHECK(shapes, cellId1);
177         BOUNDS_CHECK(shapes, cellId2);
178         vtkm::Int32 shape1Offset =
179           GetShapeOffset(shapes.Get(cellId1)) + static_cast<vtkm::Int32>(faceIdPairs.Get(index)[1]);
180         vtkm::Int32 shape2Offset = GetShapeOffset(shapes.Get(cellId2)) +
181           static_cast<vtkm::Int32>(faceIdPairs.Get(currentIndex)[1]);
182 
183         const vtkm::Int32 icount1 = tables.ShapesFaceList(shape1Offset, 0);
184         const vtkm::Int32 icount2 = tables.ShapesFaceList(shape2Offset, 0);
185         //Check to see if we have the same number of idices
186         if (icount1 != icount2)
187           continue;
188 
189 
190         //TODO: we can do better than this
191         vtkm::Id4 indices1;
192         vtkm::Id4 indices2;
193 
194         const auto faceLength = tables.ShapesFaceList(shape1Offset, 0);
195         for (vtkm::Int32 i = 1; i <= faceLength; ++i)
196         {
197           BOUNDS_CHECK(offsets, cellId1);
198           BOUNDS_CHECK(offsets, cellId2);
199           BOUNDS_CHECK(connectivity,
200                        (offsets.Get(cellId1) + tables.ShapesFaceList(shape1Offset, i)));
201           BOUNDS_CHECK(connectivity,
202                        (offsets.Get(cellId2) + tables.ShapesFaceList(shape2Offset, i)));
203           indices1[i - 1] =
204             connectivity.Get(offsets.Get(cellId1) + tables.ShapesFaceList(shape1Offset, i));
205           indices2[i - 1] =
206             connectivity.Get(offsets.Get(cellId2) + tables.ShapesFaceList(shape2Offset, i));
207         }
208 
209         bool isEqual = true;
210         for (vtkm::Int32 i = 0; i < faceLength; ++i)
211         {
212           if (!IsIn(indices1[i], indices2, faceLength))
213             isEqual = false;
214         }
215 
216         if (isEqual)
217         {
218           isInternal = true;
219 
220           connectedCell = cellId2;
221 
222           break;
223         }
224       }
225       currentIndex--;
226     }
227 
228     //this means that this cell is responsible for both itself and the other cell
229     //set the connection for the other cell
230     if (isInternal)
231     {
232       BOUNDS_CHECK(faceIdPairs, index);
233       vtkm::Id3 facePair = faceIdPairs.Get(index);
234       vtkm::Id myCell = facePair[0];
235       facePair[2] = connectedCell;
236       BOUNDS_CHECK(faceIdPairs, index);
237       faceIdPairs.Set(index, facePair);
238 
239       BOUNDS_CHECK(faceIdPairs, currentIndex);
240       facePair = faceIdPairs.Get(currentIndex);
241       facePair[2] = myCell;
242       BOUNDS_CHECK(faceIdPairs, currentIndex);
243       faceIdPairs.Set(currentIndex, facePair);
244       BOUNDS_CHECK(flags, currentIndex);
245       flags.Set(currentIndex, myCell);
246       BOUNDS_CHECK(flags, index);
247       flags.Set(index, connectedCell);
248 
249       // for unstructured, we want all unique faces to intersect with
250       // just choose one and mark as unique so the other gets culled.
251       uniqueFaces.Set(index, 1);
252     }
253   }
254 }; //class Neighbor
255 
256 class ExternalTriangles : public vtkm::worklet::WorkletMapField
257 {
258 public:
259   VTKM_CONT
ExternalTriangles()260   ExternalTriangles() {}
261   using ControlSignature =
262     void(FieldIn, WholeArrayIn, WholeArrayIn, WholeArrayIn, WholeArrayOut, FieldIn);
263   using ExecutionSignature = void(_1, _2, _3, _4, _5, _6);
264   template <typename ShapePortalType,
265             typename InIndicesPortalType,
266             typename OutIndicesPortalType,
267             typename ShapeOffsetsPortal>
operator ()(const vtkm::Id3 & faceIdPair,const ShapePortalType & shapes,const ShapeOffsetsPortal & shapeOffsets,const InIndicesPortalType & indices,const OutIndicesPortalType & outputIndices,const vtkm::Id & outputOffset) const268   VTKM_EXEC inline void operator()(const vtkm::Id3& faceIdPair,
269                                    const ShapePortalType& shapes,
270                                    const ShapeOffsetsPortal& shapeOffsets,
271                                    const InIndicesPortalType& indices,
272                                    const OutIndicesPortalType& outputIndices,
273                                    const vtkm::Id& outputOffset) const
274   {
275     CellTables tables;
276 
277     vtkm::Id cellId = faceIdPair[0];
278     BOUNDS_CHECK(shapeOffsets, cellId);
279     vtkm::Id offset = shapeOffsets.Get(cellId);
280     BOUNDS_CHECK(shapes, cellId);
281     vtkm::Int32 shapeId = static_cast<vtkm::Int32>(shapes.Get(cellId));
282     vtkm::Int32 shapesFaceOffset = tables.FaceLookUp(tables.CellTypeLookUp(shapeId), 0);
283     if (shapesFaceOffset == -1)
284     {
285       printf("Unsupported Shape Type %d\n", shapeId);
286       return;
287     }
288 
289     vtkm::Id4 faceIndices(-1, -1, -1, -1);
290     vtkm::Int32 tableIndex = static_cast<vtkm::Int32>(shapesFaceOffset + faceIdPair[1]);
291     const vtkm::Int32 numIndices = tables.ShapesFaceList(tableIndex, 0);
292 
293     for (vtkm::Int32 i = 1; i <= numIndices; ++i)
294     {
295       BOUNDS_CHECK(indices, offset + tables.ShapesFaceList(tableIndex, i));
296       faceIndices[i - 1] = indices.Get(offset + tables.ShapesFaceList(tableIndex, i));
297     }
298     vtkm::Id4 triangle;
299     triangle[0] = cellId;
300     triangle[1] = faceIndices[0];
301     triangle[2] = faceIndices[1];
302     triangle[3] = faceIndices[2];
303     BOUNDS_CHECK(outputIndices, outputOffset);
304     outputIndices.Set(outputOffset, triangle);
305     if (numIndices == 4)
306     {
307       triangle[1] = faceIndices[0];
308       triangle[2] = faceIndices[2];
309       triangle[3] = faceIndices[3];
310 
311       BOUNDS_CHECK(outputIndices, outputOffset + 1);
312       outputIndices.Set(outputOffset + 1, triangle);
313     }
314   }
315 }; //class External Triangles
316 
317 // Face conn was originally used for filtering out internal
318 // faces and was sorted with faces. To make it usable,
319 // we need to scatter back the connectivity into the original
320 // cell order, i.e., conn for cell 0 at 0,1,2,3,4,5
321 class WriteFaceConn : public vtkm::worklet::WorkletMapField
322 {
323 public:
324   using ControlSignature = void(FieldIn, WholeArrayIn, WholeArrayOut);
325   using ExecutionSignature = void(_1, _2, _3);
326 
327   VTKM_CONT
WriteFaceConn()328   WriteFaceConn() {}
329 
330   template <typename FaceOffsetsPortalType, typename FaceConnectivityPortalType>
operator ()(const vtkm::Id3 & faceIdPair,const FaceOffsetsPortalType & faceOffsets,FaceConnectivityPortalType & faceConn) const331   VTKM_EXEC inline void operator()(const vtkm::Id3& faceIdPair,
332                                    const FaceOffsetsPortalType& faceOffsets,
333                                    FaceConnectivityPortalType& faceConn) const
334   {
335     vtkm::Id cellId = faceIdPair[0];
336     BOUNDS_CHECK(faceOffsets, cellId);
337     vtkm::Id faceOffset = faceOffsets.Get(cellId) + faceIdPair[1]; // cellOffset ++ faceId
338     BOUNDS_CHECK(faceConn, faceOffset);
339     faceConn.Set(faceOffset, faceIdPair[2]);
340   }
341 }; //class WriteFaceConn
342 
343 class StructuredExternalTriangles : public vtkm::worklet::WorkletMapField
344 {
345 protected:
346   using ConnType = vtkm::exec::
347     ConnectivityStructured<vtkm::TopologyElementTagCell, vtkm::TopologyElementTagPoint, 3>;
348   ConnType Connectivity;
349   vtkm::Id Segments[7];
350   vtkm::Id3 CellDims;
351 
352 public:
353   VTKM_CONT
StructuredExternalTriangles(const ConnType & connectivity)354   StructuredExternalTriangles(const ConnType& connectivity)
355     : Connectivity(connectivity)
356   {
357     vtkm::Id3 cellDims = Connectivity.GetPointDimensions();
358     cellDims[0] -= 1;
359     cellDims[1] -= 1;
360     cellDims[2] -= 1;
361     CellDims = cellDims;
362 
363     //We have 6 segments for each of the six faces.
364     Segments[0] = 0;
365     // 0-1 = the two faces parallel to the x-z plane
366     Segments[1] = cellDims[0] * cellDims[2];
367     Segments[2] = Segments[1] + Segments[1];
368     // 2-3 parallel to the y-z plane
369     Segments[3] = Segments[2] + cellDims[1] * cellDims[2];
370     Segments[4] = Segments[3] + cellDims[1] * cellDims[2];
371     // 4-5 parallel to the x-y plane
372     Segments[5] = Segments[4] + cellDims[1] * cellDims[0];
373     Segments[6] = Segments[5] + cellDims[1] * cellDims[0];
374   }
375   using ControlSignature = void(FieldIn, WholeArrayOut);
376   using ExecutionSignature = void(_1, _2);
377   template <typename TrianglePortalType>
operator ()(const vtkm::Id & index,TrianglePortalType & triangles) const378   VTKM_EXEC inline void operator()(const vtkm::Id& index, TrianglePortalType& triangles) const
379   {
380     // Each one of size segments will process
381     // one face of the hex and domain
382     VTKM_STATIC_CONSTEXPR_ARRAY vtkm::Int32 SegmentToFace[6] = { 0, 2, 1, 3, 4, 5 };
383 
384     // Each face/segment has 2 varying dimensions
385     VTKM_STATIC_CONSTEXPR_ARRAY vtkm::Int32 SegmentDirections[6][2] = {
386       { 0, 2 },           //face 0 and 2 have the same directions
387       { 0, 2 }, { 1, 2 }, //1 and 3
388       { 1, 2 }, { 0, 1 }, // 4 and 5
389       { 0, 1 }
390     };
391 
392     //
393     // We get one index per external face
394     //
395 
396     //
397     // Cell face to extract which is also the domain
398     // face in this segment
399     //
400 
401     vtkm::Int32 segment;
402 
403     for (segment = 0; index >= Segments[segment + 1]; ++segment)
404       ;
405 
406     if (segment >= 6)
407     {
408       printf("OUT OF BOUDNS %d", (int)index);
409     }
410     vtkm::Int32 cellFace = SegmentToFace[segment];
411 
412     // Face relative directions of the
413     // 2 varying coordinates.
414     vtkm::Int32 dir1, dir2;
415     dir1 = SegmentDirections[segment][0];
416     dir2 = SegmentDirections[segment][1];
417 
418     // For each face, we will have a relative offset to
419     // the "bottom corner of the face. Three are at the
420     // origin. and we have to adjust for the other faces.
421     vtkm::Id3 cellIndex(0, 0, 0);
422     if (cellFace == 1)
423       cellIndex[0] = CellDims[0] - 1;
424     if (cellFace == 2)
425       cellIndex[1] = CellDims[1] - 1;
426     if (cellFace == 5)
427       cellIndex[2] = CellDims[2] - 1;
428 
429 
430     // index is the global index of all external faces
431     // the offset is the relative index of the cell
432     // on the current 2d face
433 
434     vtkm::Id offset = index - Segments[segment];
435     vtkm::Id dir1Offset = offset % CellDims[dir1];
436     vtkm::Id dir2Offset = offset / CellDims[dir1];
437 
438     cellIndex[dir1] = cellIndex[dir1] + dir1Offset;
439     cellIndex[dir2] = cellIndex[dir2] + dir2Offset;
440     vtkm::Id cellId = Connectivity.LogicalToFlatToIndex(cellIndex);
441     vtkm::VecVariable<vtkm::Id, 8> cellIndices = Connectivity.GetIndices(cellId);
442 
443     // Look up the offset into the face list for each cell type
444     // This should always be zero, but in case this table changes I don't
445     // want to break anything.
446     CellTables tables;
447     vtkm::Int32 shapesFaceOffset =
448       tables.FaceLookUp(tables.CellTypeLookUp(CELL_SHAPE_HEXAHEDRON), 0);
449 
450     vtkm::Id4 faceIndices;
451     vtkm::Int32 tableIndex = shapesFaceOffset + cellFace;
452 
453     // Load the face
454     for (vtkm::Int32 i = 1; i <= 4; ++i)
455     {
456       faceIndices[i - 1] = cellIndices[tables.ShapesFaceList(tableIndex, i)];
457     }
458     const vtkm::Id outputOffset = index * 2;
459     vtkm::Id4 triangle;
460     triangle[0] = cellId;
461     triangle[1] = faceIndices[0];
462     triangle[2] = faceIndices[1];
463     triangle[3] = faceIndices[2];
464     BOUNDS_CHECK(triangles, outputOffset);
465     triangles.Set(outputOffset, triangle);
466     triangle[1] = faceIndices[0];
467     triangle[2] = faceIndices[2];
468     triangle[3] = faceIndices[3];
469     BOUNDS_CHECK(triangles, outputOffset);
470     triangles.Set(outputOffset + 1, triangle);
471   }
472 }; //class  StructuredExternalTriangles
473 
474 //If with output faces or triangles, we still have to calculate the size of the output
475 //array. TODO: switch to faces only
476 class CountExternalTriangles : public vtkm::worklet::WorkletMapField
477 {
478 public:
479   VTKM_CONT
CountExternalTriangles()480   CountExternalTriangles() {}
481   using ControlSignature = void(FieldIn, WholeArrayIn, FieldOut);
482   using ExecutionSignature = void(_1, _2, _3);
483   template <typename ShapePortalType>
operator ()(const vtkm::Id3 & faceIdPair,const ShapePortalType & shapes,vtkm::Id & triangleCount) const484   VTKM_EXEC inline void operator()(const vtkm::Id3& faceIdPair,
485                                    const ShapePortalType& shapes,
486                                    vtkm::Id& triangleCount) const
487   {
488     CellTables tables;
489     vtkm::Id cellId = faceIdPair[0];
490     vtkm::Id cellFace = faceIdPair[1];
491     vtkm::Int32 shapeType = static_cast<vtkm::Int32>(shapes.Get(cellId));
492     vtkm::Int32 faceStartIndex = tables.FaceLookUp(tables.CellTypeLookUp(shapeType), 0);
493     if (faceStartIndex == -1)
494     {
495       //Unsupported Shape Type this should never happen
496       triangleCount = 0;
497       return;
498     }
499     vtkm::Int32 faceType =
500       tables.ShapesFaceList(faceStartIndex + static_cast<vtkm::Int32>(cellFace), 0);
501     // The face will either have 4 or 3 indices, so quad or tri
502     triangleCount = (faceType == 4) ? 2 : 1;
503 
504     //faceConn.Set(faceOffset, faceIdPair[2]);
505   }
506 }; //class WriteFaceConn
507 
508 template <typename CellSetType,
509           typename ShapeHandleType,
510           typename ConnHandleType,
511           typename OffsetsHandleType,
512           typename CoordsType>
GenerateFaceConnnectivity(const CellSetType cellSet,const ShapeHandleType shapes,const ConnHandleType conn,const OffsetsHandleType shapeOffsets,const CoordsType & coords,vtkm::cont::ArrayHandle<vtkm::Id> & faceConnectivity,vtkm::cont::ArrayHandle<vtkm::Id3> & cellFaceId,vtkm::Float32 BoundingBox[6],vtkm::cont::ArrayHandle<vtkm::Id> & faceOffsets,vtkm::cont::ArrayHandle<vtkm::Int32> & uniqueFaces)513 VTKM_CONT void GenerateFaceConnnectivity(const CellSetType cellSet,
514                                          const ShapeHandleType shapes,
515                                          const ConnHandleType conn,
516                                          const OffsetsHandleType shapeOffsets,
517                                          const CoordsType& coords,
518                                          vtkm::cont::ArrayHandle<vtkm::Id>& faceConnectivity,
519                                          vtkm::cont::ArrayHandle<vtkm::Id3>& cellFaceId,
520                                          vtkm::Float32 BoundingBox[6],
521                                          vtkm::cont::ArrayHandle<vtkm::Id>& faceOffsets,
522                                          vtkm::cont::ArrayHandle<vtkm::Int32>& uniqueFaces)
523 {
524 
525   vtkm::cont::Timer timer;
526   timer.Start();
527 
528   vtkm::Id numCells = shapes.GetNumberOfValues();
529 
530   vtkm::cont::ArrayHandle<vtkm::Vec3f> coordinates;
531   vtkm::cont::Algorithm::Copy(coords, coordinates);
532 
533   /*-----------------------------------------------------------------*/
534 
535   // Count the number of total faces in the cell set
536   vtkm::cont::ArrayHandle<vtkm::Id> facesPerCell;
537 
538   vtkm::worklet::DispatcherMapField<CountFaces>(CountFaces()).Invoke(shapes, facesPerCell);
539 
540   vtkm::Id totalFaces = 0;
541   totalFaces = vtkm::cont::Algorithm::Reduce(facesPerCell, vtkm::Id(0));
542 
543   // Calculate the offsets so each cell knows where to insert the morton code
544   // for each face
545   vtkm::cont::ArrayHandle<vtkm::Id> cellOffsets;
546   cellOffsets.Allocate(numCells);
547   vtkm::cont::Algorithm::ScanExclusive(facesPerCell, cellOffsets);
548   // cell offsets also serve as the offsets into the array that tracks connectivity.
549   // For example, we have a hex with 6 faces and each face connects to another cell.
550   // The connecting cells (from each face) are stored beginning at index cellOffsets[cellId]
551   faceOffsets = cellOffsets;
552 
553   // We are creating a spatial hash based on morton codes calculated
554   // from the centriod (point average)  of each face. Each centroid is
555   // calculated in way (consistent order of floating point calcs) that
556   // ensures that each face maps to the same morton code. It is possbilbe
557   // that two non-connecting faces map to the same morton code,  but if
558   // if a face has a matching face from another cell, it will be mapped
559   // to the same morton code. We check for this.
560 
561   // set up everything we need to gen morton codes
562   vtkm::Vec3f_32 inverseExtent;
563   inverseExtent[0] = 1.f / (BoundingBox[1] - BoundingBox[0]);
564   inverseExtent[1] = 1.f / (BoundingBox[3] - BoundingBox[2]);
565   inverseExtent[2] = 1.f / (BoundingBox[5] - BoundingBox[4]);
566   vtkm::Vec3f_32 minPoint;
567   minPoint[0] = BoundingBox[0];
568   minPoint[1] = BoundingBox[2];
569   minPoint[2] = BoundingBox[4];
570 
571   // Morton Codes are created for the centroid of each face.
572   // cellFaceId:
573   //  0) Cell that the face belongs to
574   //  1) Face of the the cell (e.g., hex will have 6 faces and this is 1 of the 6)
575   //  2) cell id of the cell that connects to the corresponding face (1)
576   vtkm::cont::ArrayHandle<vtkm::UInt32> faceMortonCodes;
577   cellFaceId.Allocate(totalFaces);
578   faceMortonCodes.Allocate(totalFaces);
579   uniqueFaces.Allocate(totalFaces);
580 
581   vtkm::worklet::DispatcherMapTopology<MortonCodeFace>(MortonCodeFace(inverseExtent, minPoint))
582     .Invoke(cellSet, coordinates, cellOffsets, faceMortonCodes, cellFaceId);
583 
584   // Sort the "faces" (i.e., morton codes)
585   vtkm::cont::Algorithm::SortByKey(faceMortonCodes, cellFaceId);
586 
587   // Allocate space for the final face-to-face conectivity
588   //faceConnectivity.PrepareForOutput(totalFaces, DeviceAdapter());
589   faceConnectivity.Allocate(totalFaces);
590 
591   // Initialize All connecting faces to -1 (connects to nothing)
592   vtkm::cont::ArrayHandleConstant<vtkm::Id> negOne(-1, totalFaces);
593   vtkm::cont::Algorithm::Copy(negOne, faceConnectivity);
594 
595   vtkm::cont::ArrayHandleConstant<vtkm::Int32> negOne32(-1, totalFaces);
596   vtkm::cont::Algorithm::Copy(negOne32, uniqueFaces);
597 
598   vtkm::worklet::DispatcherMapField<MortonNeighbor>(MortonNeighbor())
599     .Invoke(faceMortonCodes, cellFaceId, conn, shapes, shapeOffsets, faceConnectivity, uniqueFaces);
600 
601   vtkm::Float64 time = timer.GetElapsedTime();
602   Logger::GetInstance()->AddLogData("gen_face_conn", time);
603 }
604 
605 template <typename ShapeHandleType, typename OffsetsHandleType, typename ConnHandleType>
ExtractFaces(vtkm::cont::ArrayHandle<vtkm::Id3> cellFaceId,vtkm::cont::ArrayHandle<vtkm::Int32> uniqueFaces,const ShapeHandleType & shapes,const ConnHandleType & conn,const OffsetsHandleType & shapeOffsets)606 VTKM_CONT vtkm::cont::ArrayHandle<vtkm::Vec<Id, 4>> ExtractFaces(
607   vtkm::cont::ArrayHandle<vtkm::Id3> cellFaceId,    // Map of cell, face, and connecting cell
608   vtkm::cont::ArrayHandle<vtkm::Int32> uniqueFaces, // -1 if the face is unique
609   const ShapeHandleType& shapes,
610   const ConnHandleType& conn,
611   const OffsetsHandleType& shapeOffsets)
612 {
613 
614   vtkm::cont::Timer timer;
615   timer.Start();
616   vtkm::cont::ArrayHandle<vtkm::Id3> externalFacePairs;
617   vtkm::cont::Algorithm::CopyIf(cellFaceId, uniqueFaces, externalFacePairs, IsUnique());
618 
619   // We need to count the number of triangle per external face
620   // If it is a single cell type and it is a tet or hex, this is a special case
621   // i.e., we do not need to calculate it. TODO
622   // Otherwise, we need to check to see if the face is a quad or triangle
623 
624   vtkm::Id numExternalFaces = externalFacePairs.GetNumberOfValues();
625 
626   vtkm::cont::ArrayHandle<vtkm::Id> trianglesPerExternalFace;
627   //trianglesPerExternalFace.PrepareForOutput(numExternalFaces, DeviceAdapter());
628   trianglesPerExternalFace.Allocate(numExternalFaces);
629 
630 
631   vtkm::worklet::DispatcherMapField<CountExternalTriangles>(CountExternalTriangles())
632     .Invoke(externalFacePairs, shapes, trianglesPerExternalFace);
633 
634   vtkm::cont::ArrayHandle<vtkm::Id> externalTriangleOffsets;
635   vtkm::cont::Algorithm::ScanExclusive(trianglesPerExternalFace, externalTriangleOffsets);
636 
637   vtkm::Id totalExternalTriangles;
638   totalExternalTriangles = vtkm::cont::Algorithm::Reduce(trianglesPerExternalFace, vtkm::Id(0));
639   vtkm::cont::ArrayHandle<vtkm::Id4> externalTriangles;
640   //externalTriangles.PrepareForOutput(totalExternalTriangles, DeviceAdapter());
641   externalTriangles.Allocate(totalExternalTriangles);
642   //count the number triangles in the external faces
643 
644   vtkm::worklet::DispatcherMapField<ExternalTriangles>(ExternalTriangles())
645     .Invoke(
646       externalFacePairs, shapes, shapeOffsets, conn, externalTriangles, externalTriangleOffsets);
647 
648 
649   vtkm::Float64 time = timer.GetElapsedTime();
650   Logger::GetInstance()->AddLogData("external_faces", time);
651   return externalTriangles;
652 }
653 
MeshConnectivityBuilder()654 MeshConnectivityBuilder::MeshConnectivityBuilder() {}
~MeshConnectivityBuilder()655 MeshConnectivityBuilder::~MeshConnectivityBuilder() {}
656 
657 
658 VTKM_CONT
BuildConnectivity(vtkm::cont::CellSetSingleType<> & cellSetUnstructured,const vtkm::cont::CoordinateSystem::MultiplexerArrayType & coordinates,vtkm::Bounds coordsBounds)659 void MeshConnectivityBuilder::BuildConnectivity(
660   vtkm::cont::CellSetSingleType<>& cellSetUnstructured,
661   const vtkm::cont::CoordinateSystem::MultiplexerArrayType& coordinates,
662   vtkm::Bounds coordsBounds)
663 {
664   Logger* logger = Logger::GetInstance();
665   logger->OpenLogEntry("mesh_conn");
666   //logger->AddLogData("device", GetDeviceString(DeviceAdapter()));
667   vtkm::cont::Timer timer;
668   timer.Start();
669 
670   vtkm::Float32 BoundingBox[6];
671   BoundingBox[0] = vtkm::Float32(coordsBounds.X.Min);
672   BoundingBox[1] = vtkm::Float32(coordsBounds.X.Max);
673   BoundingBox[2] = vtkm::Float32(coordsBounds.Y.Min);
674   BoundingBox[3] = vtkm::Float32(coordsBounds.Y.Max);
675   BoundingBox[4] = vtkm::Float32(coordsBounds.Z.Min);
676   BoundingBox[5] = vtkm::Float32(coordsBounds.Z.Max);
677 
678   const vtkm::cont::ArrayHandleConstant<vtkm::UInt8> shapes = cellSetUnstructured.GetShapesArray(
679     vtkm::TopologyElementTagCell(), vtkm::TopologyElementTagPoint());
680 
681   const vtkm::cont::ArrayHandle<vtkm::Id> conn = cellSetUnstructured.GetConnectivityArray(
682     vtkm::TopologyElementTagCell(), vtkm::TopologyElementTagPoint());
683 
684   const vtkm::cont::ArrayHandleCounting<vtkm::Id> offsets = cellSetUnstructured.GetOffsetsArray(
685     vtkm::TopologyElementTagCell(), vtkm::TopologyElementTagPoint());
686   const auto shapeOffsets =
687     vtkm::cont::make_ArrayHandleView(offsets, 0, offsets.GetNumberOfValues() - 1);
688 
689   vtkm::cont::ArrayHandle<vtkm::Id> faceConnectivity;
690   vtkm::cont::ArrayHandle<vtkm::Id3> cellFaceId;
691   vtkm::cont::ArrayHandle<vtkm::Int32> uniqueFaces;
692 
693   GenerateFaceConnnectivity(cellSetUnstructured,
694                             shapes,
695                             conn,
696                             shapeOffsets,
697                             coordinates,
698                             faceConnectivity,
699                             cellFaceId,
700                             BoundingBox,
701                             FaceOffsets,
702                             uniqueFaces);
703 
704   vtkm::cont::ArrayHandle<vtkm::Id4> triangles;
705   //Faces
706 
707   triangles = ExtractFaces(cellFaceId, uniqueFaces, shapes, conn, shapeOffsets);
708 
709 
710   // scatter the coonectivity into the original order
711   vtkm::worklet::DispatcherMapField<WriteFaceConn>(WriteFaceConn())
712     .Invoke(cellFaceId, this->FaceOffsets, faceConnectivity);
713 
714   FaceConnectivity = faceConnectivity;
715   Triangles = triangles;
716 
717   vtkm::Float64 time = timer.GetElapsedTime();
718   logger->CloseLogEntry(time);
719 }
720 
721 VTKM_CONT
BuildConnectivity(vtkm::cont::CellSetExplicit<> & cellSetUnstructured,const vtkm::cont::CoordinateSystem::MultiplexerArrayType & coordinates,vtkm::Bounds coordsBounds)722 void MeshConnectivityBuilder::BuildConnectivity(
723   vtkm::cont::CellSetExplicit<>& cellSetUnstructured,
724   const vtkm::cont::CoordinateSystem::MultiplexerArrayType& coordinates,
725   vtkm::Bounds coordsBounds)
726 {
727   Logger* logger = Logger::GetInstance();
728   logger->OpenLogEntry("meah_conn");
729   vtkm::cont::Timer timer;
730   timer.Start();
731 
732   vtkm::Float32 BoundingBox[6];
733   BoundingBox[0] = vtkm::Float32(coordsBounds.X.Min);
734   BoundingBox[1] = vtkm::Float32(coordsBounds.X.Max);
735   BoundingBox[2] = vtkm::Float32(coordsBounds.Y.Min);
736   BoundingBox[3] = vtkm::Float32(coordsBounds.Y.Max);
737   BoundingBox[4] = vtkm::Float32(coordsBounds.Z.Min);
738   BoundingBox[5] = vtkm::Float32(coordsBounds.Z.Max);
739 
740   const vtkm::cont::ArrayHandle<vtkm::UInt8> shapes = cellSetUnstructured.GetShapesArray(
741     vtkm::TopologyElementTagCell(), vtkm::TopologyElementTagPoint());
742 
743   const vtkm::cont::ArrayHandle<vtkm::Id> conn = cellSetUnstructured.GetConnectivityArray(
744     vtkm::TopologyElementTagCell(), vtkm::TopologyElementTagPoint());
745 
746   const vtkm::cont::ArrayHandle<vtkm::Id> offsets = cellSetUnstructured.GetOffsetsArray(
747     vtkm::TopologyElementTagCell(), vtkm::TopologyElementTagPoint());
748   const auto shapeOffsets =
749     vtkm::cont::make_ArrayHandleView(offsets, 0, offsets.GetNumberOfValues() - 1);
750 
751   vtkm::cont::ArrayHandle<vtkm::Id> faceConnectivity;
752   vtkm::cont::ArrayHandle<vtkm::Id3> cellFaceId;
753   vtkm::cont::ArrayHandle<vtkm::Int32> uniqueFaces;
754 
755   GenerateFaceConnnectivity(cellSetUnstructured,
756                             shapes,
757                             conn,
758                             shapeOffsets,
759                             coordinates,
760                             faceConnectivity,
761                             cellFaceId,
762                             BoundingBox,
763                             FaceOffsets,
764                             uniqueFaces);
765 
766   vtkm::cont::ArrayHandle<vtkm::Id4> triangles;
767   //
768   //Faces
769   triangles = ExtractFaces(cellFaceId, uniqueFaces, shapes, conn, shapeOffsets);
770 
771   // scatter the coonectivity into the original order
772   vtkm::worklet::DispatcherMapField<WriteFaceConn>(WriteFaceConn())
773     .Invoke(cellFaceId, this->FaceOffsets, faceConnectivity);
774 
775   FaceConnectivity = faceConnectivity;
776   Triangles = triangles;
777 
778   vtkm::Float64 time = timer.GetElapsedTime();
779   logger->CloseLogEntry(time);
780 }
781 
782 struct StructuredTrianglesFunctor
783 {
784   template <typename Device>
operator ()vtkm::rendering::raytracing::StructuredTrianglesFunctor785   VTKM_CONT bool operator()(Device,
786                             vtkm::cont::ArrayHandleCounting<vtkm::Id>& counting,
787                             vtkm::cont::ArrayHandle<vtkm::Id4>& triangles,
788                             vtkm::cont::CellSetStructured<3>& cellSet) const
789   {
790     VTKM_IS_DEVICE_ADAPTER_TAG(Device);
791     vtkm::cont::Token token;
792     vtkm::worklet::DispatcherMapField<StructuredExternalTriangles> dispatch(
793       StructuredExternalTriangles(cellSet.PrepareForInput(
794         Device(), vtkm::TopologyElementTagCell(), vtkm::TopologyElementTagPoint(), token)));
795 
796     dispatch.SetDevice(Device());
797     dispatch.Invoke(counting, triangles);
798     return true;
799   }
800 };
801 
802 // Should we just make this name BuildConnectivity?
803 VTKM_CONT
ExternalTrianglesStructured(vtkm::cont::CellSetStructured<3> & cellSetStructured)804 vtkm::cont::ArrayHandle<vtkm::Id4> MeshConnectivityBuilder::ExternalTrianglesStructured(
805   vtkm::cont::CellSetStructured<3>& cellSetStructured)
806 {
807   vtkm::cont::Timer timer;
808   timer.Start();
809 
810   vtkm::Id3 cellDims = cellSetStructured.GetCellDimensions();
811   vtkm::Id numFaces =
812     cellDims[0] * cellDims[1] * 2 + cellDims[1] * cellDims[2] * 2 + cellDims[2] * cellDims[0] * 2;
813 
814   vtkm::cont::ArrayHandle<vtkm::Id4> triangles;
815   triangles.Allocate(numFaces * 2);
816   vtkm::cont::ArrayHandleCounting<vtkm::Id> counting(0, 1, numFaces);
817 
818   vtkm::cont::TryExecute(StructuredTrianglesFunctor(), counting, triangles, cellSetStructured);
819 
820   vtkm::Float64 time = timer.GetElapsedTime();
821   Logger::GetInstance()->AddLogData("structured_external_faces", time);
822 
823   return triangles;
824 }
825 
GetFaceConnectivity()826 vtkm::cont::ArrayHandle<vtkm::Id> MeshConnectivityBuilder::GetFaceConnectivity()
827 {
828   return FaceConnectivity;
829 }
830 
GetFaceOffsets()831 vtkm::cont::ArrayHandle<vtkm::Id> MeshConnectivityBuilder::GetFaceOffsets()
832 {
833   return FaceOffsets;
834 }
835 
GetTriangles()836 vtkm::cont::ArrayHandle<vtkm::Id4> MeshConnectivityBuilder::GetTriangles()
837 {
838   return Triangles;
839 }
840 
841 VTKM_CONT
BuildConnectivity(const vtkm::cont::DynamicCellSet & cellset,const vtkm::cont::CoordinateSystem & coordinates)842 MeshConnContainer* MeshConnectivityBuilder::BuildConnectivity(
843   const vtkm::cont::DynamicCellSet& cellset,
844   const vtkm::cont::CoordinateSystem& coordinates)
845 {
846   enum MeshType
847   {
848     Unsupported = 0,
849     Structured = 1,
850     Unstructured = 2,
851     UnstructuredSingle = 3,
852     RZStructured = 3,
853     RZUnstructured = 4
854   };
855 
856   MeshType type = Unsupported;
857   if (cellset.IsSameType(vtkm::cont::CellSetExplicit<>()))
858   {
859     type = Unstructured;
860   }
861 
862   else if (cellset.IsSameType(vtkm::cont::CellSetSingleType<>()))
863   {
864     vtkm::cont::CellSetSingleType<> singleType = cellset.Cast<vtkm::cont::CellSetSingleType<>>();
865     //
866     // Now we need to determine what type of cells this holds
867     //
868     vtkm::cont::ArrayHandleConstant<vtkm::UInt8> shapes =
869       singleType.GetShapesArray(vtkm::TopologyElementTagCell(), vtkm::TopologyElementTagPoint());
870     vtkm::UInt8 shapeType = shapes.ReadPortal().Get(0);
871     if (shapeType == CELL_SHAPE_HEXAHEDRON)
872       type = UnstructuredSingle;
873     if (shapeType == CELL_SHAPE_TETRA)
874       type = UnstructuredSingle;
875     if (shapeType == CELL_SHAPE_WEDGE)
876       type = UnstructuredSingle;
877     if (shapeType == CELL_SHAPE_PYRAMID)
878       type = UnstructuredSingle;
879   }
880   else if (cellset.IsSameType(vtkm::cont::CellSetStructured<3>()))
881   {
882     type = Structured;
883   }
884 
885   if (type == Unsupported)
886   {
887     throw vtkm::cont::ErrorBadValue("MeshConnectivityBuilder: unsupported cell set type");
888   }
889   vtkm::Bounds coordBounds = coordinates.GetBounds();
890 
891   Logger* logger = Logger::GetInstance();
892   logger->OpenLogEntry("mesh_conn_construction");
893 
894   MeshConnContainer* meshConn = nullptr;
895   vtkm::cont::Timer timer;
896   timer.Start();
897 
898   if (type == Unstructured)
899   {
900     vtkm::cont::CellSetExplicit<> cells = cellset.Cast<vtkm::cont::CellSetExplicit<>>();
901     this->BuildConnectivity(cells, coordinates.GetDataAsMultiplexer(), coordBounds);
902     meshConn =
903       new UnstructuredContainer(cells, coordinates, FaceConnectivity, FaceOffsets, Triangles);
904   }
905   else if (type == UnstructuredSingle)
906   {
907     vtkm::cont::CellSetSingleType<> cells = cellset.Cast<vtkm::cont::CellSetSingleType<>>();
908     this->BuildConnectivity(cells, coordinates.GetDataAsMultiplexer(), coordBounds);
909     meshConn = new UnstructuredSingleContainer(cells, coordinates, FaceConnectivity, Triangles);
910   }
911   else if (type == Structured)
912   {
913     vtkm::cont::CellSetStructured<3> cells = cellset.Cast<vtkm::cont::CellSetStructured<3>>();
914     Triangles = this->ExternalTrianglesStructured(cells);
915     meshConn = new StructuredContainer(cells, coordinates, Triangles);
916   }
917   else
918   {
919     throw vtkm::cont::ErrorBadValue(
920       "MeshConnectivityBuilder: unsupported cell set type. Logic error, this should not happen");
921   }
922 
923   vtkm::Float64 time = timer.GetElapsedTime();
924   logger->CloseLogEntry(time);
925   return meshConn;
926 }
927 }
928 }
929 } //namespace vtkm::rendering::raytracing
930