Cogs.Core
BeamUtils.cpp
1#include <cassert>
2#include <numeric>
3#include "BeamUtils.h"
4#include <glm/gtx/quaternion.hpp>
5
6#include "Context.h"
7#include "Resources/MeshManager.h"
8
9using glm::quat;
10using glm::vec3;
11using glm::vec4;
12using glm::sin;
13using glm::sqrt;
14using glm::cross;
15using glm::normalize;
16using glm::angleAxis;
17using glm::radians;
18using glm::dot;
19using glm::min;
20using glm::max;
21using glm::mat4;
22using namespace Cogs::Core;
23
24namespace {
25
26 // Using Cramer's rule to solve the three simultaneous equations.
27 // No check for degeneracy.
28 glm::vec3 triplePlaneIntersection(const glm::vec4& eq1,
29 const glm::vec4& eq2,
30 const glm::vec4& eq3)
31 {
32
33 auto A = glm::determinant(glm::mat3(eq1.x, eq2.x, eq3.x,
34 eq1.y, eq2.y, eq3.y,
35 eq1.z, eq2.z, eq3.z));
36
37 auto A1 = glm::determinant(glm::mat3(-eq1.w, -eq2.w, -eq3.w,
38 eq1.y, eq2.y, eq3.y,
39 eq1.z, eq2.z, eq3.z));
40
41 auto A2 = glm::determinant(glm::mat3(eq1.x, eq2.x, eq3.x,
42 -eq1.w, -eq2.w, -eq3.w,
43 eq1.z, eq2.z, eq3.z));
44
45 auto A3 = glm::determinant(glm::mat3(eq1.x, eq2.x, eq3.x,
46 eq1.y, eq2.y, eq3.y,
47 -eq1.w, -eq2.w, -eq3.w));
48
49 return (1.f / A)*glm::vec3(A1, A2, A3);
50 }
51
52}
53
54void EchoSounder::getBoundingFrustum(vec4* planes,
55 const glm::quat& rotation,
56 const glm::vec3& translation,
57 const uint32_t coordSys,
58 const float minDirX, const float maxDirX,
59 const float minDirY, const float maxDirY,
60 const float depthMin, const float depthMax)
61{
62 auto d00 = rotation * getBeamDir(coordSys, minDirX, minDirY);
63 auto d01 = rotation * getBeamDir(coordSys, maxDirX, minDirY);
64 auto d10 = rotation * getBeamDir(coordSys, minDirX, maxDirY);
65 auto d11 = rotation * getBeamDir(coordSys, maxDirX, maxDirY);
66
67 auto avg = normalize(d00 + d01 + d10 + d11);
68
69 vec3 n[4]{
70 normalize(cross(d00, d01)),
71 normalize(cross(d11, d10)),
72 normalize(cross(d01, d11)),
73 normalize(cross(d10, d00)),
74 };
75
76 auto b = min(min(dot(d00, avg), dot(d10, avg)),
77 min(dot(d01, avg), dot(d11, avg)));
78
79 planes[0] = vec4(n[0], -dot(n[0], translation));
80 planes[1] = vec4(n[1], -dot(n[1], translation));
81 planes[2] = vec4(n[2], -dot(n[2], translation));
82 planes[3] = vec4(n[3], -dot(n[3], translation));
83 planes[4] = vec4(avg, -b*depthMin - dot(avg, translation));
84 planes[5] = vec4(-avg, depthMax + dot(avg, translation));
85
86 // Planes points towards inside:
87 //auto m = translation + 0.5f*(depthMax + depthMin)*avg;
88 //for (int i = 0; i < 6; i++) {
89 // assert( dot(planes[i], vec4(m, 1.f)) > 0.f);
90 //}
91}
92
93
94void EchoSounder::getAxisAlignedBoundingBox(glm::vec3& minCorner,
95 glm::vec3& maxCorner,
96 const glm::quat& rotation,
97 const glm::vec3& translation,
98 const uint32_t coordSys,
99 const float minDirX, const float maxDirX,
100 const float minDirY, const float maxDirY,
101 const float depthMin, const float depthMax)
102{
103 if (coordSys == 0) {
104 auto sx = sin(minDirX);
105 auto sy = sin(minDirY);
106 float r = glm::sqrt(1.f - sx*sx - sy*sy);
107
108 vec3 _min = depthMin*(rotation * vec3(sx, sy, r));
109 vec3 _max = _min;
110
111 for (uint32_t j = 0; j < 25; j++) {
112 auto v = (1.f / 24.f)*j;
113
114 for (uint32_t i = 0; i < 25; i++) {
115 auto u = (1.f / 24.f)*i;
116 auto sx_ = sin((1.f - u)*minDirX + u*maxDirX);
117 auto sy_ = sin((1.f - v)*minDirY + v*maxDirY);
118 float r_ = glm::sqrt(1.f - sx_*sx_ - sy_*sy_);
119 auto p = (rotation * vec3(sx_, sy_, r_));
120 auto a = depthMin*p;
121 auto b = depthMax*p;
122 _min = min(_min, min(a, b));
123 _max = max(_max, max(a, b));
124 }
125 }
126
127 minCorner = _min + translation;
128 maxCorner = _max + translation;
129 }
130 else {
131 assert(false && "FIXME");
132 }
133}
134
135vec3 EchoSounder::getBeamDir(uint32_t coordSys, float dirX, float dirY)
136{
137 vec3 rv;
138 switch (coordSys) {
139 case 0:
140 {
141 // Echo sounder coordsys (EK80, ME70, MS70)
142 auto sx = sin(dirX);
143 auto sy = sin(dirY);
144 float r = glm::sqrt(1.f - sx*sx - sy*sy);
145 rv = vec3(sx, sy, r);
146 break;
147 }
148 case 1:
149 {
150 // SN90 coordsys
151 auto cx = cos(dirX);
152 auto cy = cos(dirY);
153 auto sx = sin(dirX);
154 auto sy = sin(dirY);
155 rv = vec3(sx, sy*cx, cx*cy);
156 break;
157 }
158 case 2:
159 { // ST90 coordsys
160 auto cx = cos(dirX);
161 auto cy = cos(dirY);
162 auto sx = sin(dirX);
163 auto sy = sin(dirY);
164 rv = vec3(cx*cy, cx*sy, -sx);
165 break;
166 }
167 default:
168 assert(false && "unrecognized coordinate system");
169 break;
170 }
171
172 return rv;
173}
174
175bool EchoSounder::isXDominant(const std::vector<float> &directionX, const std::vector<float> &directionY)
176{
177 return std::abs(directionY.back() - directionY.front()) < std::abs(directionX.back() - directionX.front());
178}
179
180
181void EchoSounder::getArrayToVesselTransform(quat& rotation,
182 vec3& translation,
183 const vec3& alpha,
184 const vec3& offset)
185{
186 auto aX = angleAxis(alpha.x, vec3(1, 0, 0));
187 auto aY = angleAxis(alpha.y, vec3(0, 1, 0));
188 auto aZ = angleAxis(alpha.z, vec3(0, 0, 1));
189 auto a = aZ * aY * aX;
190 // We're now in SIMRAD vessel coords, X is towards bow, Y is towards
191 // starboard, Z is downwards.
192
193
194 // In TD50 coords, vessel is heading north, X is easting, Y is northing,
195 // and Z is upwards.
196 auto bX = angleAxis(radians(180.f), vec3(1, 0, 0)); // Rotate s.t. Z is up
197 auto bZ = angleAxis(radians(-90.f), vec3(0, 0, 1)); // Rotate s.t. old X becomes Y.
198 auto b = bX * bZ;
199
200 rotation = b * a;
201 translation = b * offset;
202}
203
204
205
206void EchoSounder::inflateFans(std::vector<float>& directionX,
207 std::vector<float>& directionY,
208 const std::vector<float>& inDirectionX,
209 const std::vector<float>& inBeamWidthX,
210 const std::vector<float>& inDirectionY,
211 const std::vector<float>& inBeamWidthY,
212 const std::vector<uint32_t>& beams,
213 const Topology /*topology*/,
214 const size_t minorCount)
215{
216 const size_t majorCount = beams.size() / minorCount;
217 if (beams.size() == 1) {
218 const auto srcIx = beams[0];
219 directionX.resize(4);
220 directionY.resize(4);
221 directionX[0] = inDirectionX[srcIx] - 0.5f*inBeamWidthX[srcIx];
222 directionY[0] = inDirectionY[srcIx] - 0.5f*inBeamWidthY[srcIx];
223 directionX[1] = inDirectionX[srcIx] - 0.5f*inBeamWidthX[srcIx];
224 directionY[1] = inDirectionY[srcIx] + 0.5f*inBeamWidthY[srcIx];
225 directionX[2] = inDirectionX[srcIx] + 0.5f*inBeamWidthX[srcIx];
226 directionY[2] = inDirectionY[srcIx] - 0.5f*inBeamWidthY[srcIx];
227 directionX[3] = inDirectionX[srcIx] + 0.5f*inBeamWidthX[srcIx];
228 directionY[3] = inDirectionY[srcIx] + 0.5f*inBeamWidthY[srcIx];
229 }
230 else if (majorCount == 1 || minorCount == 1) {
231 size_t largestDimensionCount = std::max(majorCount, minorCount);
232 bool xDom = isXDominant(inDirectionX, inDirectionY);
233 directionX.resize(2 * largestDimensionCount);
234 directionY.resize(2 * largestDimensionCount);
235 for (size_t i = 0; i < largestDimensionCount; i++) {
236 const auto srcIx = beams[i];
237 directionX[i] = inDirectionX[srcIx] - (xDom ? 0.0f : 0.5f)*inBeamWidthX[srcIx];
238 directionY[i] = inDirectionY[srcIx] - (xDom ? 0.5f : 0.f)*inBeamWidthY[srcIx];
239 directionX[i + minorCount] = inDirectionX[srcIx] + (xDom ? 0.f : 0.5f)*inBeamWidthX[srcIx];
240 directionY[i + minorCount] = inDirectionY[srcIx] + (xDom ? 0.5f : 0.f)*inBeamWidthY[srcIx];
241 }
242 }
243 //if marjorCount > 1 and minorCount > 1 there is not need to inflate the fan, it's alreday a grid
244}
245
246
247MeshHandle EchoSounder::buildBeamBundleOutline(Context* context,
248 const glm::quat& rotation,
249 const glm::vec3& translation,
250 const uint32_t coordSys,
251 const float* dirX, const size_t nX,
252 const float* dirY, const size_t nY,
253 const float depthMin, const float depthMax)
254{
255 auto minorCount = nX;
256 auto majorCount = nY;
257
258 auto mesh = context->meshManager->create();
259
260 auto P = mesh->mapPositions(0, 2 * minorCount*majorCount);
261 auto C = mesh->map<glm::vec4>(VertexDataType::Colors0, VertexFormats::Color4f, 0, 2 * minorCount*majorCount);
262
263
264 std::vector<uint32_t> indices;
265 indices.reserve(8 * majorCount*minorCount);
266 for (size_t j = 0; j < majorCount; j++) {
267 for (size_t i = 0; i < minorCount; i++) {
268 float v = i / (minorCount - 1.f);
269 float u = j / (majorCount - 1.f);
270
271 auto d = rotation *getBeamDir(coordSys, dirX[j], dirY[i]);
272
273 //auto d = orientation * beamDirection(dataData->beamAngleAthwartship[i],
274 // dataData->beamAngleAlongship[j]);
275
276 P[2 * (minorCount*j + i) + 0] = translation + depthMin*d;
277 P[2 * (minorCount*j + i) + 1] = translation + depthMax*d;
278 C[2 * (minorCount*j + i) + 0] = glm::vec4(u, v, 0.f, 1.f);
279 C[2 * (minorCount*j + i) + 1] = glm::vec4(u, v, 0.f, 1.f);
280
281 if (0 < i) {
282 indices.push_back(static_cast<uint32_t>(2 * (minorCount*j + i - 1) + 0));
283 indices.push_back(static_cast<uint32_t>(2 * (minorCount*j + i) + 0));
284 indices.push_back(static_cast<uint32_t>(2 * (minorCount*j + i - 1) + 1));
285 indices.push_back(static_cast<uint32_t>(2 * (minorCount*j + i) + 1));
286 }
287 if (0 < j) {
288 indices.push_back(static_cast<uint32_t>(2 * (minorCount*(j - 1) + i) + 0));
289 indices.push_back(static_cast<uint32_t>(2 * (minorCount*j + i) + 0));
290 indices.push_back(static_cast<uint32_t>(2 * (minorCount*(j - 1) + i) + 1));
291 indices.push_back(static_cast<uint32_t>(2 * (minorCount*j + i) + 1));
292 }
293 }
294 }
295 indices.push_back(static_cast<uint32_t>(2 * (minorCount * 0 + 0) + 0));
296 indices.push_back(static_cast<uint32_t>(2 * (minorCount * 0 + 0) + 1));
297 indices.push_back(static_cast<uint32_t>(2 * (minorCount * 0 + minorCount - 1) + 0));
298 indices.push_back(static_cast<uint32_t>(2 * (minorCount * 0 + minorCount - 1) + 1));
299 indices.push_back(static_cast<uint32_t>(2 * (minorCount * (majorCount - 1) + 0) + 0));
300 indices.push_back(static_cast<uint32_t>(2 * (minorCount * (majorCount - 1) + 0) + 1));
301 indices.push_back(static_cast<uint32_t>(2 * (minorCount * (majorCount - 1) + minorCount - 1) + 0));
302 indices.push_back(static_cast<uint32_t>(2 * (minorCount * (majorCount - 1) + minorCount - 1) + 1));
303
304
305 mesh->setIndexData(indices.data(), indices.size());
306 mesh->primitiveType = Cogs::PrimitiveType::LineList;
307 mesh->setBounds(calculateBounds(mesh.operator->()));
308
309 return mesh;
310}
311
312
313void EchoSounder::buildBeamBundleOutline2(std::vector<glm::vec3>& V,
314 std::vector<uint32_t>& indices,
315 Context* context,
316 const glm::quat& rotation,
317 const glm::vec3& translation,
318 const uint32_t coordSys,
319 const float* dirX, const size_t nX,
320 const float* dirY, const size_t nY,
321 const float depthMin, const float depthMax,
322 const bool minorClosed,
323 const bool individualBeams)
324{
325 auto minorCount = nY;
326 auto majorCount = nX;
327
328 size_t numDepthRings = 2;
329 std::vector<float> depthRingValues;
330 for (size_t k = 0; k < numDepthRings; ++k) {
331 depthRingValues.push_back((depthMax - depthMin) * (k / (numDepthRings - 1.f)) + depthMin);
332 }
333
334 auto mesh = context->meshManager->create();
335
336 for (uint32_t j = 0; j < majorCount; j++) {
337 for (uint32_t i = 0; i < minorCount; i++) {
338 auto d = rotation *getBeamDir(coordSys,
339 dirX[minorCount*j + i],
340 dirY[minorCount*j + i]);
341
342 for (size_t k = 0; k < numDepthRings; ++k) {
343 V.push_back(translation + depthRingValues[k] * d);
344 }
345
346 //line between vertices which has the same depthRing value across minor direction
347 if (0 < i && (individualBeams || j == 0 || j == majorCount -1)) {
348 for (size_t k = 0; k < numDepthRings; ++k) {
349 indices.push_back(static_cast<uint32_t>(numDepthRings * (minorCount*j + i - 1) + k));
350 indices.push_back(static_cast<uint32_t>(numDepthRings * (minorCount*j + i) + k));
351 }
352 }
353
354 //line between vertices which has the same depthRing value across major direction
355 if (!minorClosed) {
356 if (0 < j && (individualBeams || i == 0 || i == minorCount - 1)) {
357 for (size_t k = 0; k < numDepthRings; ++k) {
358 indices.push_back(static_cast<uint32_t>(numDepthRings * (minorCount*(j - 1) + i) + k));
359 indices.push_back(static_cast<uint32_t>(numDepthRings * (minorCount*j + i) + k));
360 }
361 }
362 }
363 }
364 }
365
366
367 if (!minorClosed) {
368 // End caps
369 indices.push_back(static_cast<uint32_t>(numDepthRings * (minorCount * 0 + 0) + 0));
370 indices.push_back(static_cast<uint32_t>(numDepthRings * (minorCount * 0 + 0) + numDepthRings - 1));
371 indices.push_back(static_cast<uint32_t>(numDepthRings * (minorCount * 0 + minorCount - 1) + 0));
372 indices.push_back(static_cast<uint32_t>(numDepthRings * (minorCount * 0 + minorCount - 1) + numDepthRings - 1));
373 indices.push_back(static_cast<uint32_t>(numDepthRings * (minorCount * (majorCount - 1) + 0) + 0));
374 indices.push_back(static_cast<uint32_t>(numDepthRings * (minorCount * (majorCount - 1) + 0) + numDepthRings - 1));
375 indices.push_back(static_cast<uint32_t>(numDepthRings * (minorCount * (majorCount - 1) + minorCount - 1) + 0));
376 indices.push_back(static_cast<uint32_t>(numDepthRings * (minorCount * (majorCount - 1) + minorCount - 1) + numDepthRings - 1));
377 }
378}
379
380MeshHandle EchoSounder::buildFrustumOutline(Context* context,
381 const glm::vec4* planes)
382{
383 auto mesh = context->meshManager->create();
384 auto P = mesh->mapPositions(0, 8);
385 auto C = mesh->map<glm::vec4>(VertexDataType::Colors0, VertexFormats::Color4f, 0, 8);
386
387 for (int i = 0; i < 8; i++) {
388 P[i] = triplePlaneIntersection(planes[0 + ((i >> 0) & 1)],
389 planes[2 + ((i >> 1) & 1)],
390 planes[4 + ((i >> 2) & 1)]);
391 C[i] = glm::vec4(0, 1, 1, 1);
392 }
393
394 std::vector<uint32_t> indices =
395 {
396 0, 1, 1, 3, 3, 2, 2, 0,
397 4, 5, 5, 7, 7, 6, 6, 4,
398 0, 4, 1, 5, 2, 6, 3, 7
399 };
400
401 mesh->setIndexData(indices.data(), indices.size());
402 mesh->primitiveType = Cogs::PrimitiveType::LineList;
403 mesh->setBounds(calculateBounds(mesh.operator->()));
404
405 return mesh;
406}
407
408
409MeshHandle EchoSounder::buildBoxOutline(Context* context,
410 const glm::vec3& minCorner,
411 const glm::vec3& maxCorner)
412{
413 auto mesh = context->meshManager->create();
414 auto P = mesh->mapPositions(0, 8);
415 auto C = mesh->map<glm::vec4>(VertexDataType::Colors0, VertexFormats::Color4f, 0, 8);
416
417 for (int i = 0; i < 8; i++) {
418 P[i] = vec3(((i >> 0) & 1) == 0 ? minCorner.x : maxCorner.x,
419 ((i >> 1) & 1) == 0 ? minCorner.y : maxCorner.y,
420 ((i >> 2) & 1) == 0 ? minCorner.z : maxCorner.z);
421 C[i] = glm::vec4(0, 0, 1, 1);
422 }
423
424 std::vector<uint32_t> indices =
425 {
426 0, 1, 1, 3, 3, 2, 2, 0,
427 4, 5, 5, 7, 7, 6, 6, 4,
428 0, 4, 1, 5, 2, 6, 3, 7
429 };
430
431 mesh->setIndexData(indices.data(), indices.size());
432 mesh->primitiveType = Cogs::PrimitiveType::LineList;
433 mesh->setBounds(calculateBounds(mesh.operator->()));
434
435 return mesh;
436}
437
438void EchoSounder::clipVertical(Cogs::Memory::TypedBuffer<float> & values, size_t startValueIndex,
439 const glm::vec3& beamStartPos,
440 const std::vector<glm::vec3>& beamDir, const size_t startBeamIndex,
441 const float minVerticalDepth, const float maxVerticalDepth,
442 const float depthOffset, const float depthStep, const float maxRange,
443 const size_t beamCount, const size_t sampleCount)
444{
445
446 for (size_t j = 0; j < beamCount; ++j) {
447 auto valueIndex = startValueIndex + j * sampleCount;
448 auto & d = beamDir[j + startBeamIndex];
449 auto & o = beamStartPos;
450
451 size_t la;
452 if (minVerticalDepth < 1.f) {
453 la = 0;
454 }
455 else if (-maxRange * d.z < minVerticalDepth) {
456 la = sampleCount;
457 }
458 else {
459 float t0 = -(minVerticalDepth + o.z) / d.z;
460 la = static_cast<uint32_t>(std::max(0.f, std::floor((t0 - depthOffset) / depthStep)));
461 }
462 la = std::min(la, sampleCount);
463
464 size_t lb;
465 if (maxVerticalDepth <= minVerticalDepth) {
466 lb = la;
467 }
468 else if (-maxRange * d.z < minVerticalDepth) {
469 lb = sampleCount;
470 }
471 else {
472 float t0 = -(maxVerticalDepth + o.z) / d.z;
473 lb = std::max(la, static_cast<size_t>(std::max(0.f, std::floor((t0 - depthOffset) / depthStep))));
474 }
475 lb = std::min(lb, sampleCount);
476
477 auto out1 = valueIndex + 0;
478 for (size_t i = 0; i < la; i++) {
479 values[out1++] = 0.f;
480 }
481
482 auto out2 = valueIndex + lb;
483 for (size_t i = lb; i < sampleCount; i++) {
484 values[out2++] = 0.f;
485 }
486 }
487}
A Context instance contains all the services, systems and runtime components needed to use Cogs.
Definition: Context.h:83
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
Cogs::Geometry::BoundingBox COGSCORE_DLL_API calculateBounds(Mesh *mesh)
Calculate a bounding box for the given mesh.
Definition: MeshHelper.cpp:283
@ LineList
List of lines.
Definition: Common.h:120