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