Cogs.Core
SwathSeabedBuildMeshTask.cpp
1#include "Resources/MeshManager.h"
2
3#include "../Components/BeamGroupComponent.h"
4#include "../Components/DataSetComponent.h"
5#include "../Components/WindowComponent.h"
6#include "../Systems/SwathBottomSystem.h"
7
8#include "../BeamUtils.h"
9
10#include "../Tasks/SwathSeabedBuildMeshTask.h"
11#include "../Tasks/BeamResampleTask.h"
12
13#include <glm/gtx/compatibility.hpp>
14
15Cogs::Core::EchoSounder::SwathSeabedBuildMeshTask::SwathSeabedBuildMeshTask(Cogs::Core::Context* context,
17 const uint32_t chunkIndex,
22: SwathBuildMeshTask(context, mesh, chunkIndex, swathData.pathSpacing, swathData.beamSpacing, groupComp, dataData, swathData.resamplingPositions, swathData.chunks),
23 uTexDom(swathComp.uTextureDomain),
24 uTexRange(swathComp.uTextureRange),
25 vTexDom(swathComp.vTextureDomain),
26 vTexRange(swathComp.vTextureRange)
27{
28 const auto & dataConf = dataData.persistent->config;
29 const auto & buffer = dataData.persistent->buffer;
30
31 depths.resize(beamCount * sliceCount);
32 reflectivities.resize(beamCount * sliceCount);
33
34 const auto beamCount_src = dataConf.beamCount;
35 for (uint32_t i = 0; i < sliceCount; i++) {
36 uint32_t k = (bix_a + i) % dataConf.capacity;
37 for (uint32_t j = 0; j < beamCount; j++) {
38 const auto ix = beams[j];
39 depths[beamCount*i + j] = buffer.bottomDepths[beamCount_src*k + ix];
40 reflectivities[beamCount*i + j] = buffer.bottomReflectivities[beamCount_src*k + ix];
41 }
42 }
43}
44
45Cogs::Core::EchoSounder::SwathSeabedBuildMeshTask::SwathSeabedBuildMeshTask(const SwathSeabedBuildMeshTask& other)
46: SwathBuildMeshTask(other),
47 uTexDom(other.uTexDom),
48 vTexDom(other.vTexDom),
49 uTexRange(other.uTexRange),
50 vTexRange(other.vTexRange)
51{
52 depths.copy(other.depths);
53 reflectivities.copy(other.reflectivities);
54}
55
56void Cogs::Core::EchoSounder::SwathSeabedBuildMeshTask::operator()()
57{
58 // We use NaN for undefined values. The NaN will creep into expressions using
59 // undefined values in any way, rendering the results undefined. Using zero,
60 // we would get linear interpolations towards zero with just exactly zero
61 // wiped out, which will look like spikes.
62 for (uint32_t i = 0; i < sliceCount*beamCount; i++) {
63 if (depths[i] == 0.f) depths[i] = std::numeric_limits<float>::quiet_NaN();
64 }
65
66 if (pathSpacing != 0.f) {
67 pathResample();
68 }
69
70 std::vector<glm::vec3> beamDir(beamCount);
71 for (uint32_t i = 0; i < beamCount; i++) {
72 beamDir[i] = metaPings[0].arrayOrientationVessel * EchoSounder::getBeamDir(coordSys, directionX[i], directionY[i]);
73 }
74
75 if (beamSpacing != 0.f) {
76 beamResample(beamDir);
77 }
78
79 auto proxy = context->meshManager->createLocked();
80 auto P = proxy->mapPositions(0, sliceCount*beamCount);
81 auto N = proxy->map<glm::vec3>(VertexDataType::Normals, VertexFormats::Norm3f, 0, sliceCount*beamCount);
82 auto T = proxy->map<glm::vec2>(VertexDataType::TexCoords0, VertexFormats::Tex2f, 0, sliceCount*beamCount);
83 auto bbox = Cogs::Geometry::makeEmptyBoundingBox<Cogs::Geometry::BoundingBox>();
84 std::vector<uint32_t> indices;
85 std::vector<uint32_t> supportIndices; //used to get smooth normals between chunks
86
87 calcVertexPositions(bbox, P, T, beamDir);
88 calcIndices(indices, supportIndices);
89 calcNormalVectors(P, N, indices, supportIndices);
90 if (!indices.empty()) {
91 proxy->setIndexData(indices.data(), indices.size());
92 proxy->primitiveType = Cogs::PrimitiveType::TriangleList;
94 proxy->setBounds(bbox);
95
96 mesh = proxy.getHandle();
97 if (context->callbacks.resourceLoadCallback) {
98
99 // HACK: Force redraw of view.
100 context->callbacks.resourceLoadCallback(context, 0, -1, 0);
101 }
102 }
103}
104
105void Cogs::Core::EchoSounder::SwathSeabedBuildMeshTask::pathResample()
106{
107 auto sliceCount_new = static_cast<uint32_t>(resamplingPositions.size());
108
109 decltype(depths) newDepths(beamCount * sliceCount_new);
110 decltype(reflectivities) newReflectivities(beamCount * sliceCount_new);
111 decltype(metaPings) newMetaPings(sliceCount_new);
112
113 const auto o = resamplingPositions[0].bufferIndex;
114 for (uint32_t i = 0; i < sliceCount_new; i++) {
115 const auto k = (resamplingPositions[i].bufferIndex - o + bufferCapacity) % bufferCapacity;
116 const auto t = resamplingPositions[i].fractional;
117 const auto s = (1.f - t);
118
119 newMetaPings[i].vesselPositionGlobal = s*metaPings[k].vesselPositionGlobal + t*metaPings[k + 1].vesselPositionGlobal;
120 newMetaPings[i].arrayPositionVessel = metaPings[k].arrayPositionVessel; // Fixed during a run.
121 newMetaPings[i].arrayPositionGlobal = s*metaPings[k].arrayPositionGlobal + t*metaPings[k + 1].arrayPositionGlobal;
122
123 newMetaPings[i].vesselOrientationGlobal = glm::slerp(metaPings[k].vesselOrientationGlobal, metaPings[k + 1].vesselOrientationGlobal, t);
124 newMetaPings[i].arrayOrientationVessel = metaPings[k].arrayOrientationVessel;
125 newMetaPings[i].arrayOrientationGlobal = newMetaPings[i].vesselOrientationGlobal * newMetaPings[i].arrayOrientationVessel;
126
127
128 for (uint32_t j = 0; j < beamCount; j++) {
129 newDepths[beamCount*i + j] = s*depths[beamCount*k + j] + t*depths[beamCount*(k + 1) + j];
130 newReflectivities[beamCount*i + j] = s*reflectivities[beamCount*k + j] + t*reflectivities[beamCount*(k + 1) + j];
131 }
132 }
133
134 depths.swap(newDepths);
135 reflectivities.swap(newReflectivities);
136 metaPings.swap(newMetaPings);
137 sliceCount = sliceCount_new;
138}
139
140void Cogs::Core::EchoSounder::SwathSeabedBuildMeshTask::beamResample(std::vector<glm::vec3>& beamDir)
141{
142 if (beamSpacing == 0.0) return;
143
144 std::vector<float> minorAngles(beamCount);
145 minorAngles[0] = 0.f;
146 for (uint32_t i = 1; i < beamCount; i++) {
147 minorAngles[i] = minorAngles[i - 1] + std::acos(glm::dot(beamDir[i - 1], beamDir[i]));
148 }
149 float clampedStep = std::max(beamSpacing, abs((minorAngles.back() - minorAngles.front()) / (maxBeamUpsample*beamCount)));
150
151 std::vector<InterpolationSamplePos> samplesOut;
152 SetupBeamFilterUnivariate(samplesOut, minorAngles, clampedStep, beamCount);
153
154 uint32_t beamCountNew = static_cast<uint32_t>(samplesOut.size());
155
156 std::vector<glm::vec3> beamDirNew(beamCountNew);
157 for (uint32_t i = 0; i < beamCountNew; i++) {
158 const auto & d0 = beamDir[samplesOut[i].i + 0];
159 const auto & d1 = beamDir[samplesOut[i].i + 1];
160
161 // Spherical linear interpolation
162 const auto G = std::acos(glm::dot(d0, d1));
163 const auto t = samplesOut[i].t;
164 const auto d = (1.f / std::sin(G))*(std::sin(G*(1.f - t))*d0 + std::sin(G*t)*d1);
165
166 beamDirNew[i] = d;
167 }
168
169 Cogs::Memory::TypedBuffer<float> depths_new(sliceCount*beamCountNew);
170 Cogs::Memory::TypedBuffer<float> reflectivities_new(sliceCount*beamCountNew);
171 for (uint32_t j = 0; j < sliceCount; j++) {
172 for (uint32_t i = 0; i < beamCountNew; i++) {
173 // Convert to vertical depth before interpolation
174 const auto & s = samplesOut[i];
175 const auto depthA = depths[j*beamCount + s.i] * beamDir[s.i].z;
176 const auto depthB = depths[j*beamCount + s.i + 1] * beamDir[s.i + 1].z;
177 const auto depth = ((1.f - s.t)*depthA + s.t*depthB) / beamDirNew[i].z;
178 depths_new[j*beamCountNew + i] = depth;
179 const auto reflA = reflectivities[j*beamCount + s.i];
180 const auto reflB = reflectivities[j*beamCount + s.i + 1];
181 reflectivities_new[j*beamCountNew + i] = (1.f - s.t)*reflA + s.t*reflB;
182 }
183 }
184
185 beamDir.swap(beamDirNew);
186 depths.swap(depths_new);
187 reflectivities.swap(reflectivities_new);
188 beamCount = beamCountNew;
189}
190
191template<typename PType, typename TType, typename DType>
192void Cogs::Core::EchoSounder::SwathSeabedBuildMeshTask::calcVertexPositions(Cogs::Geometry::BoundingBox& bbox, PType& P, TType& T, DType& beamDir)
193{
194 const glm::vec2 uReMap(uTexRange.x, 1.f / glm::max(std::numeric_limits<float>::min(), uTexRange.y - uTexRange.x));
195 const glm::vec2 vReMap(vTexRange.x, 1.f / glm::max(std::numeric_limits<float>::min(), vTexRange.y - vTexRange.x));
196 for (uint32_t s = 0; s < sliceCount; s++) {
197 for (uint32_t b = 0; b < beamCount; b++) {
198 const auto o = s*beamCount + b;
199 const auto d = depths[o];
200 const auto p = metaPings[s].arrayPositionGlobal + d * (metaPings[s].vesselOrientationGlobal * beamDir[b]);
201
202 if (std::isfinite(d)) {
203 bbox += p;
204 }
205
206 P[o] = p;
207
208 float e[static_cast<int32_t>(EchoSounder::SwathBottomComponent::TextureDomain::Size)];
209 e[static_cast<int32_t>(EchoSounder::SwathBottomComponent::TextureDomain::Zero)] = 0.f;
210 e[static_cast<int32_t>(EchoSounder::SwathBottomComponent::TextureDomain::One)] = 1.f;
211 e[static_cast<int32_t>(EchoSounder::SwathBottomComponent::TextureDomain::PositionX)] = p.x;
212 e[static_cast<int32_t>(EchoSounder::SwathBottomComponent::TextureDomain::PositionY)] = p.y;
213 e[static_cast<int32_t>(EchoSounder::SwathBottomComponent::TextureDomain::VerticalDepth)] = -p.z;
214 e[static_cast<int32_t>(EchoSounder::SwathBottomComponent::TextureDomain::BeamDistance)] = d;
215 e[static_cast<int32_t>(EchoSounder::SwathBottomComponent::TextureDomain::Reflectivity)] = reflectivities[o];
216 float tu = e[static_cast<int32_t>(uTexDom)];
217 float tv = e[static_cast<int32_t>(vTexDom)];
218 T[o].x = uReMap.y*(tu - uReMap.x);
219 T[o].y = vReMap.y*(tv - vReMap.x);
220 }
221 }
222}
223
224template<typename PType, typename NType, typename IType>
225void Cogs::Core::EchoSounder::SwathSeabedBuildMeshTask::calcNormalVectors(PType& P, NType& N, IType& I1, IType& I2) {
226 for (uint32_t i = 0; i < sliceCount*beamCount; i++) {
227 N[i] = glm::vec3(0.0);
228 }
229
230 calcNormalVectors(P, N, I1);
231 calcNormalVectors(P, N, I2);
232
233 for (uint32_t i = 0; i < sliceCount*beamCount; i++) {
234 N[i] = glm::normalize(N[i]);
235 if (!glm::all(glm::isfinite(N[i]))) {
236 N[i] = glm::vec3(0, 0, 1);
237 }
238 }
239}
240
241template<typename PType, typename NType, typename IType>
242void Cogs::Core::EchoSounder::SwathSeabedBuildMeshTask::calcNormalVectors(PType& P, NType& N, IType& I)
243{
244 for (uint32_t i = 0; i < I.size(); ) {
245 auto index0 = I[i++];
246 auto index1 = I[i++];
247 auto index2 = I[i++];
248 auto p0 = P[index0];
249 auto p1 = P[index1];
250 auto p2 = P[index2];
251 auto n = glm::cross(p2 - p0, p1 - p0);
252 N[index0] += n;
253 N[index1] += n;
254 N[index2] += n;
255 }
256}
257
258void Cogs::Core::EchoSounder::SwathSeabedBuildMeshTask::calcIndices(std::vector<uint32_t>& indices,
259 std::vector<uint32_t>& supportIndices)
260{
261 indices.clear();
262 indices.reserve(6 * sliceCount*beamCount);
263 supportIndices.clear();
264 supportIndices.reserve(6 * beamCount *(sliceA + (sliceCount-1 - sliceB))); //contains indices defining triangles in skirt of chunk [0, sliceA] and [sliceB, sliceCount-1]
265
266 for (uint32_t s = 0; s + 1 < sliceCount; ++s) {
267 for (uint32_t b = 0; b + 1 < beamCount; b++) {
268 auto c00 = (s + 0)*beamCount + (b + 0);
269 auto c01 = (s + 0)*beamCount + (b + 1);
270 auto c10 = (s + 1)*beamCount + (b + 0);
271 auto c11 = (s + 1)*beamCount + (b + 1);
272
273 uint32_t CellIndices[2][3] = { { (uint32_t)c00, (uint32_t)c01, (uint32_t)c10 },{ (uint32_t)c11, (uint32_t)c10, (uint32_t)c01 } };
274 if (!isfinite(depths[c01]) || !isfinite(depths[c10])) {
275 CellIndices[0][0] = c01;
276 CellIndices[0][1] = c11;
277 CellIndices[0][2] = c00;
278 CellIndices[1][0] = c10;
279 CellIndices[1][1] = c00;
280 CellIndices[1][2] = c11;
281 }
282
283 for (int t = 0; t < 2; t++) {
284 if (!isfinite(depths[CellIndices[t][0]]) ||
285 !isfinite(depths[CellIndices[t][1]]) ||
286 !isfinite(depths[CellIndices[t][2]]))
287 {
288 continue;
289 }
290 auto& indicesList = (s < sliceA || s >= sliceB) ? supportIndices : indices;
291
292 indicesList.push_back(CellIndices[t][0]);
293 indicesList.push_back(CellIndices[t][1]);
294 indicesList.push_back(CellIndices[t][2]);
295 }
296 }
297 }
298}
A Context instance contains all the services, systems and runtime components needed to use Cogs.
Definition: Context.h:83
@ 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