Cogs.Core
SwathIsoSurfacesBuildMeshTask.cpp
1#include <cassert>
2
3#include <glm/glm.hpp>
4
5#include "Platform/Instrumentation.h"
6#include "Services/Variables.h"
7#include "Resources/MeshManager.h"
8
9#include "../Extensions/GeometryProcessing/GeometryProcessing.h"
10#include "../Extensions/IsoSurfaces/IsoSurfaces.h"
11
12#include "../BoundingBox.h"
13#include "../BeamUtils.h"
14
15#include "../Components/BeamGroupComponent.h"
16#include "../Systems/SwathIsoSystem.h"
17
18#include "../Tasks/BeamResampleTask.h"
19#include "../Tasks/SwathIsoSurfacesBuildMeshTask.h"
20#include "../Tasks/DepthResampleTask.h"
21
22#include "Foundation/Logging/Logger.h"
23#include "Foundation/Platform/Timer.h"
24
25namespace {
26 Cogs::Logging::Log logger = Cogs::Logging::getLogger("SwathIsoSurfacesBuildMeshTask");
27
28 const glm::ivec3 taskSize_Classify = glm::ivec3(32, 16, 16);
29 const int taskSize_PopCnt = 10000;
30 const int taskSize_ExtractVtx = 10000;
31 const int taskSize_ExtractIdx = 10000;
32 const int taskSize_Normals = 10000;
33 const int taskSize_BBox = 10000;
34
35 int elapsed_Count = 0;
36 double elapsed_init = 0.0;
37 double elapsed_move = 0.0;
38 double elapsed_Total = 0.0;
39 double elapsed_Resample = 0.0;
40 double elapsed_Linearize = 0.0;
41 double elapsed_ClipVertical = 0.0;
42 double elapsed_Analyze = 0.0;
43 double elapsed_Extract = 0.0;
44 double elapsed_Normals = 0.0;
45 double elapsed_SetupMesh = 0.0;
46
47 template<typename PContainer, typename TContainer, typename RayDirContainer, typename MetaPingContainer>
48 struct VertexEmit
49 {
50 PContainer& P;
51 TContainer& T;
52
53 const float * field;
54 const float threshold;
55 const float separation = 1e1f;
56 const float depthOffset;
57 const float depthStep;
58 const float layer;
59 const uint32_t vertexOffset;
60 const uint32_t uTextureDomain;
61 const uint32_t vTextureDomain;
62 const glm::ivec3 Ns;
63 const glm::ivec3 maxFieldIndex;
64 const RayDirContainer& rayDir;
65 const MetaPingContainer& metaPings;
66
67 VertexEmit(PContainer& P, TContainer& T,
68 const float * field, const float threshold, const float separation, const float depthOffset, const float depthStep, const float layer,
69 const uint32_t sampleCount, const uint32_t beamCount, const uint32_t sliceCount,
70 const uint32_t vertexOffset, const uint32_t uTextureDomain, const uint32_t vTextureDomain,
71 const RayDirContainer& rayDir, const MetaPingContainer& metaPings)
72 : P(P), T(T),
73 field(field), threshold(threshold), separation(separation), depthOffset(depthOffset), depthStep(depthStep), layer(layer),
74 Ns(sampleCount, beamCount, sliceCount),
75 maxFieldIndex(glm::max(glm::ivec3(0), glm::ivec3((int)sampleCount - 1, (int)beamCount - 1, (int)sliceCount - 1))),
76 vertexOffset(vertexOffset), uTextureDomain(uTextureDomain), vTextureDomain(vTextureDomain),
77 rayDir(rayDir), metaPings(metaPings)
78 {}
79
80 VertexEmit(const VertexEmit&) = default;
81
82 void emit(uint32_t index, glm::ivec3 aSample, glm::ivec3 bSample)
83 {
84 glm::ivec3 I[2] = { aSample, bSample };
85 float value[2];
86 float linearValue[2];
87 float depth[2];
88
89 glm::vec3 positions[2];
90 for (int m = 0; m < 2; m++) {
91 glm::ivec3 Ir = I[m];
92 glm::ivec3 Ic = glm::clamp(Ir, glm::ivec3(0), maxFieldIndex);
93
94 value[m] = field[(Ic.z*Ns.y + Ic.y)*Ns.x + Ic.x];
95 linearValue[m] = value[m] - threshold;
96 depth[m] = depthOffset + Ic.x*depthStep;
97
98 glm::vec3 p = metaPings[Ic.z].arrayPositionGlobal + depth[m] * (metaPings[Ic.z].vesselOrientationGlobal * rayDir[Ic.y]);
99 glm::vec3 pp = p;
100
101 if (Ic.x != Ir.x) {
102 int signed_one = Ic.x - Ir.x < 0 ? -1 : 1;
103 //assert(std::abs(signed_one) == 1);
104 p -= separation * signed_one * (metaPings[Ic.z].vesselOrientationGlobal * rayDir[Ic.y]);
105 }
106
107 if (Ic.y != Ir.y) {
108 int signed_one = Ic.y - Ir.y < 0 ? -1 : 1;
109 //assert(0 <= Ic.y + signed_one);
110 //assert(Ic.y + signed_one <= maxFieldIndex.y);
111 glm::vec3 ppp = metaPings[Ic.z].arrayPositionGlobal + depth[m] * (metaPings[Ic.z].vesselOrientationGlobal * rayDir[Ic.y + signed_one]);
112 p += separation * glm::normalize(pp - ppp);
113 }
114
115 if (Ic.z != Ir.z) {
116 int signed_one = Ic.z - Ir.z < 0 ? -1 : 1;
117 //assert(0 <= Ic.z + signed_one);
118 //assert(Ic.z + signed_one <= maxFieldIndex.z);
119 glm::vec3 ppp = metaPings[Ic.z + signed_one].arrayPositionGlobal + depth[m] * (metaPings[Ic.z + signed_one].vesselOrientationGlobal * rayDir[Ic.y]);
120 p += separation * glm::normalize(pp - ppp);
121 }
122
123 positions[m] = p;
124 }
125 float den = (linearValue[1] - linearValue[0]);
126 float t = glm::abs(den) < std::numeric_limits<float>::min() ? 0.5f : -linearValue[0] / den;
127 auto p = glm::mix(positions[0], positions[1], t);
128
129 int vo = vertexOffset + index;
130 P[vo] = p;
131 //N[vo] = glm::vec3(0.f);
132
133 float e[static_cast<uint32_t>(Cogs::Core::EchoSounder::SwathIsoComponent::TextureDomain::Size)];
134 e[static_cast<uint32_t>(Cogs::Core::EchoSounder::SwathIsoComponent::TextureDomain::Zero)] = 0.f;
135 e[static_cast<uint32_t>(Cogs::Core::EchoSounder::SwathIsoComponent::TextureDomain::One)] = 1.f;
136 e[static_cast<uint32_t>(Cogs::Core::EchoSounder::SwathIsoComponent::TextureDomain::PositionX)] = p.x;
137 e[static_cast<uint32_t>(Cogs::Core::EchoSounder::SwathIsoComponent::TextureDomain::PositionY)] = p.y;
138 e[static_cast<uint32_t>(Cogs::Core::EchoSounder::SwathIsoComponent::TextureDomain::VerticalDepth)] = -p.z;
139 e[static_cast<uint32_t>(Cogs::Core::EchoSounder::SwathIsoComponent::TextureDomain::BeamDistance)] = glm::mix(depth[0], depth[1], t);
140 e[static_cast<uint32_t>(Cogs::Core::EchoSounder::SwathIsoComponent::TextureDomain::Value)] = static_cast<float>(threshold);
141 e[static_cast<uint32_t>(Cogs::Core::EchoSounder::SwathIsoComponent::TextureDomain::LayerNormalized)] = layer;
142 e[static_cast<uint32_t>(Cogs::Core::EchoSounder::SwathIsoComponent::TextureDomain::Age)] = 0.f; // glm::mix(time[0], time[1], static_cast<float>(t));
143
144 T[vo].x = e[uTextureDomain];
145 T[vo].y = e[vTextureDomain];
146 }
147
148 void operator()(int32_t ixIndex, glm::ivec4* samples, const uint32_t ixN)
149 {
150 for (uint32_t k = 0; k < ixN; k++) {
151 emit(ixIndex + k,
152 glm::ivec3(samples[0][0],
153 samples[1][0],
154 samples[2][0]),
155 glm::ivec3(samples[0][1 + k],
156 samples[1][1 + k],
157 samples[2][1 + k]));
158 }
159 }
160
161 };
162
163
164
165
166
167 template<bool flip>
168 struct IndexEmit
169 {
170 uint32_t vertexOffset;
171 uint32_t * indices;
172 uint8_t * keepIndex;
173 int32_t minZ, maxZ;
174
175 IndexEmit(uint32_t vertexOffset,
176 uint32_t * indices,
177 uint8_t * keepIndex,
178 int32_t minZ,
179 int32_t maxZ)
180 : vertexOffset(vertexOffset), indices(indices), keepIndex(keepIndex), minZ(minZ), maxZ(maxZ)
181 {}
182
183 IndexEmit(const IndexEmit&) = default;
184
185 void operator()(uint32_t ixIndex, glm::ivec3 cell, const uint32_t* ix, const uint32_t ixN)
186 {
187 for (uint32_t k = 0; k < ixN; k += 3) {
188 auto i = ixIndex + k;
189
190 keepIndex[i / 3] = (minZ <= cell.z) && (cell.z < maxZ);
191 if constexpr (flip == false) {
192 indices[i + 0] = ix[k + 0] + vertexOffset;
193 indices[i + 1] = ix[k + 1] + vertexOffset;
194 indices[i + 2] = ix[k + 2] + vertexOffset;
195 }
196 else {
197 indices[i + 0] = ix[k + 0] + vertexOffset;
198 indices[i + 1] = ix[k + 2] + vertexOffset;
199 indices[i + 2] = ix[k + 1] + vertexOffset;
200 }
201 }
202 }
203 };
204
205}
206
207Cogs::Core::EchoSounder::SwathIsoSurfacesBuildMeshTask::SwathIsoSurfacesBuildMeshTask(Cogs::Core::Context* context,
209 const uint32_t chunkIndex,
214: SwathBuildMeshTask(context, mesh, chunkIndex, isoData.pathSpacing, isoData.beamSpacing, groupComp, dataData, isoData.resamplingPositions, isoData.chunks),
215 seabedClipOffset(isoData.seabedClipOffset),
216 useDecibel(dataData.persistent->config.useDecibel),
217 flipOrientation(isoData.flipOrientation),
218 depthSpacing(isoData.depthSpacing),
219 overflowThreshold(isoData.overflowThreshold),
220 minVerticalDepth(isoData.minVerticalDepth),
221 maxVerticalDepth(isoData.maxVerticalDepth),
222 maxDepthUpsample(context->variables->get("echo.maxDepthUpsample")->getFloat()),
223 depthOffset(dataData.persistent->config.depthOffset),
224 coordSys(dataData.persistent->config.coordSys),
225 depthStep(dataData.persistent->config.depthStep),
226 separation(isoComp.capSeparation),
227 sampleCount(dataData.persistent->config.sampleCount),
228 uTextureDomain(isoComp.uTextureDomain),
229 vTextureDomain(isoComp.vTextureDomain),
230 uTextureRange(isoComp.uTextureRange),
231 vTextureRange(isoComp.vTextureRange),
232 thresholds(isoData.thresholds)
233{
234 Timer timer;
235 const auto & dataConf = dataData.persistent->config;
236 const auto & buffer = dataData.persistent->buffer;
237
238 timer.start();
239 depths.resize(beamCount * sliceCount);
240 values.resize(sampleCount * beamCount * sliceCount);
241 arrayOrientationVessel.resize(sliceCount);
242 arrayPositionVessel.resize(sliceCount);
243 const auto beamCount_src = dataConf.beamCount;
244 for (uint32_t i = 0; i < sliceCount; i++) {
245 uint32_t k = (bix_a + i) % dataConf.capacity;
246 auto &metaPing = buffer.metaPings[k];
247 arrayOrientationVessel[i] = metaPing.arrayOrientationVessel;
248 arrayPositionVessel[i] = metaPing.arrayPositionVessel;
249 for (uint32_t j = 0; j < beamCount; j++) {
250 const auto ix = beams[j];
251 depths[beamCount*i + j] = buffer.bottomDepths[beamCount_src*k + ix];
252 std::memcpy(values.data() + sampleCount*(beamCount*i + j),
253 buffer.values.data() + sampleCount*(beamCount_src*k + ix),
254 sizeof(float)*sampleCount);
255 }
256 }
257 elapsed_init += timer.elapsedSeconds();
258}
259
260Cogs::Core::EchoSounder::SwathIsoSurfacesBuildMeshTask::SwathIsoSurfacesBuildMeshTask(const SwathIsoSurfacesBuildMeshTask& other) // TODO should this be a move?
261: SwathBuildMeshTask(other),
262 seabedClipOffset(other.seabedClipOffset),
263 useDecibel(other.useDecibel),
264 flipOrientation(other.flipOrientation),
265 depthSpacing(other.depthSpacing),
266 maxDepthUpsample(other.maxDepthUpsample),
267 depthOffset(other.depthOffset),
268 coordSys(other.coordSys),
269 depthStep(other.depthStep),
270 separation(other.separation),
271 sampleCount(other.sampleCount),
272 overflowThreshold(other.overflowThreshold),
273 minVerticalDepth(other.minVerticalDepth),
274 maxVerticalDepth(other.maxVerticalDepth),
275 uTextureDomain(other.uTextureDomain),
276 vTextureDomain(other.vTextureDomain),
277 uTextureRange(other.uTextureRange),
278 vTextureRange(other.vTextureRange),
279 thresholds(other.thresholds)
280{
281 Timer timer;
282 timer.start();
283 depths.copy(other.depths);
284 values.copy(other.values);
285 arrayOrientationVessel.copy(other.arrayOrientationVessel);
286 arrayPositionVessel.copy(other.arrayPositionVessel);
287 elapsed_move += timer.elapsedSeconds();
288}
289
290void Cogs::Core::EchoSounder::SwathIsoSurfacesBuildMeshTask::operator()()
291{
292 Cogs::Timer timerTotal, timer;
293 CpuInstrumentationScope(SCOPE_ECHOSOUNDER, "SwathIsoBuildMesh");
294 timerTotal.start();
295
296 timer.start();
297 std::vector<float> linearizedThresholds;
298 linearizeAndClipValues(linearizedThresholds);
299 elapsed_Linearize = timer.elapsedSeconds();
300
301 if (std::isfinite(minVerticalDepth) || std::isfinite(maxVerticalDepth)) {
302 clipVerticalMask();
303 elapsed_ClipVertical = timer.elapsedSeconds();
304 }
305
306
307 std::vector<glm::vec3> beamDir;
308 assert(directionX.size() == beamCount);
309 assert(directionY.size() == beamCount);
310 beamDir.resize(beamCount);
311 CalculateBeamDirectionsTask(beamDir.data(),
312 coordSys,
313 directionX.data(), directionY.data(),
314 1, beamCount, metaPings[0].arrayOrientationVessel,
315 0, beamCount)();
316
317
318 timer.start();
319 depthResample();
320 beamResample(beamDir);
321 pathResample();
322 elapsed_Resample += timer.elapsedSeconds();
323
324
325 // Done resampling, build mesh.
326
327 const auto Ns = glm::ivec3(sampleCount, beamCount, sliceCount);
328 const auto gridA = glm::ivec3(-1, -1, tail ? -1 : 0);
329 const auto gridB = glm::max(gridA, Ns + glm::ivec3(1, 1, head ? 1 : 0));
330 const auto layers = static_cast<uint32_t>(thresholds.size());
331 const auto M = gridB - gridA;
332
333 std::vector<int32_t> vertexOffsets;
334 std::vector<int32_t> indexOffsets;
335 std::vector<int32_t> cellOffsets;
338 Cogs::Memory::TypedBuffer<int32_t> activeCellVertexIndexOffsets;
339 Cogs::Memory::TypedBuffer<int32_t> activeCellTriangleIndexOffsets;
340 Cogs::Memory::TypedBuffer<int32_t> activeCellIndices;
342 {
343 CpuInstrumentationScope(SCOPE_ECHOSOUNDER, "Analyze");
344 timer.start();
345 analyze(context,
346 vertexOffsets, indexOffsets, cellOffsets,
347 cellMap,
348 activeCellCases, activeCellVertexIndexOffsets, activeCellTriangleIndexOffsets, activeCellIndices, activeCellIJK,
349 linearizedThresholds, true, values.data(),
350 Ns, gridA, gridB,
351 nullptr);
352 elapsed_Analyze += timer.elapsedSeconds();
353 }
354
355 const auto vertexCount = vertexOffsets.back();
356 const auto indexCount = indexOffsets.back();
357
358 if (0 < vertexCount) {
359 auto proxyMesh = context->meshManager->createLocked();
360
362 Cogs::Geometry::BoundingBox bbox;
363
364 auto P = proxyMesh->mapPositions(0, vertexCount+1); // TODO: Fix out of bound SSE access (+1)
365 auto N = proxyMesh->map<glm::vec3>(VertexDataType::Normals, VertexFormats::Norm3f, 0, vertexCount);
366 auto T = proxyMesh->map<glm::vec2>(VertexDataType::TexCoords0, VertexFormats::Tex2f, 0, vertexCount);
367
368 indices.resize(indexCount, false);
369 Cogs::Memory::TypedBuffer<uint8_t> keepIndex(indices.size());
370
371
372 // PASS: Extract vertices and indices
373 {
374 CpuInstrumentationScope(SCOPE_ECHOSOUNDER, "Extract");
375 timer.start();
376
377 auto extractGroup = context->taskManager->createGroup();
378 for (uint32_t i = 0; i < layers; i++) {
379 IsoSurfaces::VertexEmitFunc vertexEmit = VertexEmit<decltype(P), decltype(T), decltype(beamDir), decltype(metaPings)>
380 (P, T,
381 values.data(), linearizedThresholds[i], 2.f*separation * (layers - i) , depthOffset, depthStep, (i + 0.5f) / layers,
382 sampleCount, beamCount, sliceCount,
383 vertexOffsets[i], (uint32_t)uTextureDomain, (uint32_t)vTextureDomain,
384 beamDir, metaPings);
385
386 IsoSurfaces::extractVertices(context, extractGroup,
387 vertexEmit,
388 activeCellCases.data() + cellOffsets[i],
389 activeCellVertexIndexOffsets.data() + cellOffsets[i] + i,
390 activeCellIndices.data() + cellOffsets[i],
391 activeCellIJK.data() + cellOffsets[i],
392 cellOffsets[i + 1] - cellOffsets[i],
393 gridA, gridB,
394 nullptr);
395
396 Cogs::Core::IsoSurfaces::IndexEmitFunc indexEmit;
397 if (flipOrientation == false) {
398 indexEmit = IndexEmit<false>(vertexOffsets[i],
399 indices.data() + indexOffsets[i],
400 keepIndex.data() + indexOffsets[i] / 3,
401 (int)sliceA - (tail ? 1 : 0),
402 (int)sliceB + (head ? 1 : 0));
403 }
404 else {
405 indexEmit = IndexEmit<true>(vertexOffsets[i],
406 indices.data() + indexOffsets[i],
407 keepIndex.data() + indexOffsets[i] / 3,
408 (int)sliceA - (tail ? 1 : 0),
409 (int)sliceB + (head ? 1 : 0));
410 }
411
412
413 IsoSurfaces::extractIndices(context,
414 extractGroup,
415 indexEmit,
416 cellMap.data() + i * M.x*M.y*M.z,
417 activeCellCases.data() + cellOffsets[i],
418 activeCellVertexIndexOffsets.data() + cellOffsets[i] + i,
419 activeCellTriangleIndexOffsets.data() + cellOffsets[i] + i,
420 activeCellIndices.data() + cellOffsets[i],
421 activeCellIJK.data() + cellOffsets[i],
422 cellOffsets[i + 1] - cellOffsets[i],
423 gridA, gridB,
424 nullptr);
425
426 }
427 context->taskManager->wait(extractGroup);
428 context->taskManager->destroy(extractGroup);
429
430 elapsed_Extract += timer.elapsedSeconds();
431 }
432
433 {
434 CpuInstrumentationScope(SCOPE_ECHOSOUNDER, "NormalsBBox");
435 timer.start();
436
437 auto bboxGroup = context->taskManager->createGroup();
438 context->taskManager->enqueueChild(bboxGroup, [&]
439 {
440 bbox = Cogs::Geometry::makeEmptyBoundingBox<Cogs::Geometry::BoundingBox>();
441 includeInBBox(context,
442 bbox,
443 reinterpret_cast<const float*>(P.data), 3,
444 vertexOffsets.back(),
445 taskSize_BBox);
446 });
447 GeometryProcessing::normalsFromIndexedTriangles(context,
448 reinterpret_cast<float*>(N.data), 3,
449 reinterpret_cast<const float*>(P.data), 3,
450 vertexOffsets.back(),
451 indices.data(),
452 indexOffsets.back(),
453 taskSize_Normals);
454 context->taskManager->wait(bboxGroup);
455 context->taskManager->destroy(bboxGroup);
456
457 elapsed_Normals += timer.elapsedSeconds();
458 }
459
460 // PASS: Setup mesh
461 {
462 CpuInstrumentationScope(SCOPE_ECHOSOUNDER, "SetupMesh");
463 timer.start();
464
465 std::vector<int32_t> prunedIndexOffsets(layers + 1);
466
467 // remove helper triangles
468 auto d = indices.data();
469
470 for (uint32_t l = 0; l < layers; l++) {
471 for (int i = indexOffsets[l]; i < indexOffsets[l + 1]; i += 3) {
472 if (keepIndex[i / 3]) {
473 *d++ = indices[i + 0];
474 *d++ = indices[i + 1];
475 *d++ = indices[i + 2];
476 }
477 }
478 prunedIndexOffsets[l + 1] = static_cast<int32_t>(d - indices.data());
479 }
480 indices.resize(prunedIndexOffsets.back());
481
482 proxyMesh->setBounds(bbox);
483 proxyMesh->setIndexData(indices.data(), indices.size());
484 proxyMesh->mapSubMeshes(layers);
485 for (uint32_t l = 0; l < layers; l++) {
486 auto & subMesh = proxyMesh->getSubMeshes()[l];
487 subMesh.startIndex = prunedIndexOffsets[l];
488 subMesh.numIndexes = prunedIndexOffsets[l + 1] - prunedIndexOffsets[l];
489 subMesh.primitiveType = Cogs::PrimitiveType::TriangleList;
490 }
491 proxyMesh->setMeshFlag(MeshFlags::ClockwiseWinding);
492
493 this->mesh = proxyMesh.getHandle();
494
495 elapsed_SetupMesh += timer.elapsedSeconds();
496 }
497 }
498
500 //context->callbacks.resourceLoadCallback(context, 0, -1, 0);
501
502 elapsed_Total += timerTotal.elapsedSeconds();
503 elapsed_Count++;
504
505#if defined(COGS_AUTO_PROFILE)
506 if (2.0 < elapsed_Total) {
507 double total = elapsed_Total / elapsed_Count;
508 double resample = elapsed_Resample / elapsed_Count;
509 double linearize = elapsed_Linearize / elapsed_Count;
510 double clipVertical = elapsed_ClipVertical / elapsed_Count;
511 double analyze = elapsed_Analyze / elapsed_Count;
512 double extract = elapsed_Extract / elapsed_Count;
513 double normals = elapsed_Normals / elapsed_Count;
514 double setupmesh = elapsed_SetupMesh / elapsed_Count;
515 LOG_DEBUG(logger, "N=%d TOT=%.1f RS=%.1f L=%.1f CL=%.1f A=%.1f E=%.1f N=%.3f M=%.1f",
516 elapsed_Count,
517 1000.0*total,
518 1000.0*resample,
519 1000.0*linearize,
520 1000.0*clipVertical,
521 1000.0*analyze,
522 1000.0*extract,
523 1000.0*normals,
524 1000.0*setupmesh);
525 elapsed_Count = 0;
526 elapsed_Total = 0.0;
527 elapsed_Resample = 0.0;
528 elapsed_Linearize = 0.0;
529 elapsed_ClipVertical = 0.0;
530 elapsed_Analyze = 0.0;
531 elapsed_Extract = 0.0;
532 elapsed_Normals = 0.0;
533 elapsed_SetupMesh = 0.0;
534 }
535#endif
536
537}
538
539
540void Cogs::Core::EchoSounder::SwathIsoSurfacesBuildMeshTask::linearizeAndClipValues(std::vector<float>& linearizedThresholds)
541{
542 static const float factorA = (float)(log2(10.0) / 10.0);
543 static const float scale_dB = 100.0f; // Constant added to dB value to utilize both positive and negative exponents.
544
545 float overflowThresholdLin = std::exp2(factorA*(overflowThreshold + scale_dB));
546 if (useDecibel) {
547
548 for (uint32_t s = 0; s < sliceCount; s++) {
549 for (uint32_t b = 0; b < beamCount; b++) {
550 auto bottomDepth = depths[s*beamCount + b];
551 auto clipDist = bottomDepth - seabedClipOffset;
552 auto N = (bottomDepth != 0.f && std::isfinite(seabedClipOffset)) ? std::min(static_cast<uint32_t>(std::max(0.f, std::floor((clipDist - depthOffset) / depthStep))), sampleCount) : sampleCount;
553 for (uint32_t i = 0; i < N; i++) {
554 auto v = std::exp2(factorA*(values[(s*beamCount + b)*sampleCount + i] + scale_dB));
555 if (!std::isfinite(v)) {
556 v = v < 0.f ? 0.f : std::numeric_limits<float>::max();
557 }
558 values[(s*beamCount + b)*sampleCount + i] = v;
559 }
560 for (uint32_t i = N; i < sampleCount; i++) {
561 values[(s*beamCount + b)*sampleCount + i] = 0.f;
562 }
563 if (std::isfinite(overflowThresholdLin)) {
564 for (uint32_t i = 0; i < N; i++) {
565 float &v = values[(s*beamCount + b)*sampleCount + i];
566 if (v > overflowThresholdLin) {
567 v = 0;
568 }
569 }
570 }
571 }
572 }
573
574 linearizedThresholds.resize(thresholds.size());
575 for (size_t i = 0; i < thresholds.size(); i++) {
576 auto v = std::exp2(factorA*(thresholds[i] + scale_dB));
577 if (!std::isfinite(v)) {
578 v = v < 0.f ? 0.f : std::numeric_limits<float>::max();
579 }
580 linearizedThresholds[i] = v;
581 }
582 }
583 else {
584
585 for (uint32_t s = 0; s < sliceCount; s++) {
586 for (uint32_t b = 0; b < beamCount; b++) {
587 auto bottomDepth = depths[s*beamCount + b];
588 auto clipDist = bottomDepth - seabedClipOffset;
589 auto N = (bottomDepth != 0.f && std::isfinite(seabedClipOffset)) ? std::min(static_cast<uint32_t>(std::max(0.f, std::floor((clipDist - depthOffset) / depthStep))), sampleCount) : sampleCount;
590 for (uint32_t i = N; i < sampleCount; i++) {
591 values[(s*beamCount)*sampleCount + i] = 0.f;
592 }
593
594 if (std::isfinite(overflowThresholdLin)) {
595 for (uint32_t i = 0; i < N; i++) {
596 if (values[i] > overflowThresholdLin) {
597 values[i] = 0;
598 }
599 }
600 }
601 }
602 }
603
604 linearizedThresholds = thresholds;
605
606 }
607}
608
609void Cogs::Core::EchoSounder::SwathIsoSurfacesBuildMeshTask::clipVerticalMask()
610{
611 const float doff = depthOffset;
612 const float dstp = depthStep;
613 const float maxRange = doff + dstp * sampleCount;
614
615
616 float minVerticalDepth1 = std::isfinite(minVerticalDepth) ? std::max(0.f, minVerticalDepth) : 0.f;
617 float maxVerticalDepth1 = std::isfinite(maxVerticalDepth) ? std::max(minVerticalDepth, maxVerticalDepth) : 10000.f;
618
619 std::vector<glm::vec3> relBeamDir(beamCount);
620
621 for (size_t j = 0; j < beamCount; j++) {
622 relBeamDir[j] = getBeamDir(coordSys, directionX[j], directionY[j]);
623 }
624
625 for (size_t k = 0; k < sliceCount; k++) {
626 std::vector<glm::vec3> vesselBeamDir(beamCount);
627 for (size_t j = 0; j < beamCount; ++j) {
628 vesselBeamDir[j] = arrayOrientationVessel[k] * relBeamDir[j];
629 }
630 clipVertical(values, sampleCount * beamCount * k,
631 arrayPositionVessel[k], vesselBeamDir, 0,
632 minVerticalDepth1, maxVerticalDepth1,
633 depthOffset, depthStep, maxRange,
634 beamCount, sampleCount);
635 }
636}
637
638
639void Cogs::Core::EchoSounder::SwathIsoSurfacesBuildMeshTask::depthResample()
640{
641 if (depthSpacing == 0.f) return;
642
643 CpuInstrumentationScope(SCOPE_ECHOSOUNDER, "DepthResample");
644
645 uint32_t sampleCountResample = std::min(10000u, static_cast<uint32_t>(floor((depthStep * sampleCount) / depthSpacing)));
646 floatTmp.resize(sliceCount * beamCount * sampleCountResample);
647
648 auto depthResampleGroup = context->taskManager->createGroup();
649 for (uint32_t s = 0; s < sliceCount; s++) {
650 context->taskManager->enqueueChild(depthResampleGroup, DepthResampleTask(floatTmp.data() + s * beamCount * sampleCountResample,
651 depthSpacing,
652 sampleCountResample,
653 values.data() + s * beamCount * sampleCount,
654 depthStep,
655 sampleCount,
656 0, beamCount));
657 }
658 context->taskManager->wait(depthResampleGroup);
659 context->taskManager->destroy(depthResampleGroup);
660
661 sampleCount = sampleCountResample;
662 depthStep = depthSpacing;
663 values.swap(floatTmp);
664}
665
666void Cogs::Core::EchoSounder::SwathIsoSurfacesBuildMeshTask::beamResample(std::vector<glm::vec3>& beamDir)
667{
668 if (beamSpacing == 0.f) return;
669
670 std::vector<float> minorAngles(beamCount);
671 minorAngles[0] = 0.f;
672 for (uint32_t i = 1; i < beamCount; i++) {
673 minorAngles[i] = minorAngles[i - 1] + std::acos(glm::dot(beamDir[i - 1], beamDir[i]));
674 }
675 float clampedStep = std::max(beamSpacing, abs((minorAngles.back() - minorAngles.front()) / (maxBeamUpsample*beamCount)));
676
677 std::vector<InterpolationSamplePos> samplesOut;
678 SetupBeamFilterUnivariate(samplesOut, minorAngles, clampedStep, beamCount);
679
680 uint32_t beamCountNew = static_cast<uint32_t>(samplesOut.size());
681
682 std::vector<glm::vec3> beamDirNew(beamCountNew);
683 for (uint32_t i = 0; i < beamCountNew; i++) {
684 const auto & d0 = beamDir[samplesOut[i].i + 0];
685 const auto & d1 = beamDir[samplesOut[i].i + 1];
686
687 // Spherical linear interpolation
688 const auto G = std::acos(glm::dot(d0, d1));
689 const auto t = samplesOut[i].t;
690 const auto d = (1.f / std::sin(G))*(std::sin(G*(1.f - t))*d0 + std::sin(G*t)*d1);
691
692 beamDirNew[i] = d;
693 }
694
695 floatTmp.resize(beamCountNew * sliceCount * sampleCount);
696 auto interpolateGroup1 = context->taskManager->createGroup();
697
698 for (uint32_t s = 0; s < sliceCount; s++) {
699 context->taskManager->enqueueChild(interpolateGroup1,
700 InterpolateBeamDataMinorTask(floatTmp.data() + s* beamCountNew * sampleCount,
701 1, beamCountNew, beamCount, sampleCount,
702 values.data() + s* beamCount * sampleCount,
703 samplesOut.data(), 0, beamCountNew));
704 }
705 context->taskManager->wait(interpolateGroup1);
706 context->taskManager->destroy(interpolateGroup1);
707
708 beamDir.swap(beamDirNew);
709 values.swap(floatTmp);
710 beamCount = beamCountNew;
711}
712
713
714void Cogs::Core::EchoSounder::SwathIsoSurfacesBuildMeshTask::pathResample()
715{
716 if (pathSpacing == 0.f) return;
717
718 CpuInstrumentationScope(SCOPE_ECHOSOUNDER, "PathResample");
719
720 auto sliceCount_new = static_cast<uint32_t>(resamplingPositions.size());
721 decltype(metaPings) newMetaPings(sliceCount_new);
722 floatTmp.resize(sampleCount*beamCount*sliceCount_new);
723
724 const auto o = resamplingPositions[0].bufferIndex;
725 for (uint32_t i = 0; i < sliceCount_new; i++) {
726 const auto k = (resamplingPositions[i].bufferIndex - o + bufferCapacity) % bufferCapacity;
727 const auto t = resamplingPositions[i].fractional;
728 const auto s = (1.f - t);
729
730 newMetaPings[i].vesselPositionGlobal = s*metaPings[k].vesselPositionGlobal + t*metaPings[k + 1].vesselPositionGlobal;
731 newMetaPings[i].arrayPositionVessel = metaPings[k].arrayPositionVessel; // Fixed during a run.
732 newMetaPings[i].arrayPositionGlobal = s*metaPings[k].arrayPositionGlobal + t*metaPings[k + 1].arrayPositionGlobal;
733
734 newMetaPings[i].vesselOrientationGlobal = glm::slerp(metaPings[k].vesselOrientationGlobal, metaPings[k + 1].vesselOrientationGlobal, t);
735 newMetaPings[i].arrayOrientationVessel = metaPings[k].arrayOrientationVessel;
736 newMetaPings[i].arrayOrientationGlobal = newMetaPings[i].vesselOrientationGlobal * newMetaPings[i].arrayOrientationVessel;
737
738 for (uint32_t j = 0; j < beamCount; j++) {
739 for (uint32_t l = 0; l < sampleCount; l++) {
740 floatTmp[(beamCount*i + j)*sampleCount + l] = s*values[(beamCount*k + j)*sampleCount + l] + t*values[(beamCount*(k + 1) + j)*sampleCount + l];
741 }
742 }
743 }
744
745 values.swap(floatTmp);
746 metaPings.swap(newMetaPings);
747 sliceCount = sliceCount_new;
748}
A Context instance contains all the services, systems and runtime components needed to use Cogs.
Definition: Context.h:83
std::unique_ptr< class TaskManager > taskManager
TaskManager service instance.
Definition: Context.h:186
Log implementation class.
Definition: LogManager.h:139
Old timer class.
Definition: Timer.h:37
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
@ ClockwiseWinding
The mesh uses clockwise winding order for it's triangles as opposed to the counter-clockwise default.
Definition: Mesh.h:63
@ TriangleList
List of triangles.
Definition: Common.h:116