Cogs.Core
DepthAxisSystem.cpp
1#include "DepthAxisSystem.h"
2
3#include "Context.h"
4
5#include "Systems/Core/CameraSystem.h"
6#include "Systems/Core/TransformSystem.h"
7
8#include "Utilities/Math.h"
9#include "Math/LinePlaneIntersection.h"
10
11#include "Components/Core/MeshComponent.h"
12#include "Components/Core/TextComponent.h"
13#include "Components/Core/SubMeshRenderComponent.h"
14#include "Components/Data/TrajectoryComponent.h"
15#include "../Components/AnnotationAxisComponent.h"
16
17#include "Resources/MeshManager.h"
18#include "Resources/MaterialManager.h"
19#include "Resources/FontManager.h"
20#include "Resources/DefaultMaterial.h"
21
22#include "Foundation/ComponentModel/Entity.h"
23#include "Foundation/Geometry/BoundingBox.hpp"
24#include "Foundation/Geometry/SampleListGenerator.hpp"
25#include "Foundation/Geometry/PathGenerator.hpp"
26
27#include <algorithm>
28#include <sstream>
29#include <iomanip>
30
31namespace Cogs
32{
33 namespace Core
34 {
35 glm::vec3 project(const CameraData & data, const glm::vec3 & in)
36 {
37 auto projected = data.viewProjection * glm::vec4(in, 1);
38
39 projected /= projected.w != 0.0f ? projected.w : 1.0f;
40
41 return glm::vec3(projected);
42 }
43
44 float getWorldToScreenScale(Context * /*context*/, const CameraData & cameraData, const glm::vec3 & worldCenter, float normRadius)
45 {
46 // Find screen space coordinates of sphere center point and tangent
47 // point.
48 const auto screenCenter = project(cameraData, worldCenter) + glm::vec3(normRadius, 0, 0);
49
50 const auto projectionPoint = glm::vec3(cameraData.inverseViewMatrix * glm::vec4(0, 0, 0, 1));
51 const auto reverseProjectedW = cameraData.inverseViewProjectionMatrix * glm::vec4(screenCenter.x, screenCenter.y, -1, 1);
52
53 const auto reverseProjected = glm::vec3(reverseProjectedW / reverseProjectedW.w);
54
55 const auto centerToCamera = worldCenter - projectionPoint;
56
57 if (!glm::length(centerToCamera)) return 1;
58
59 const float planeDistance = glm::dot(glm::normalize(centerToCamera), worldCenter);
60
61 glm::vec3 intersection;
62 Cogs::Geometry::intersect(glm::normalize(centerToCamera), planeDistance, projectionPoint, glm::normalize(reverseProjected - projectionPoint), intersection);
63
64 return glm::length(intersection - worldCenter);
65 }
66 }
67}
68
70{
71 const auto cameraComponent = context->cameraSystem->getMainCamera();
72 const auto & cameraData = context->cameraSystem->getData(cameraComponent);
73
74 for (auto & component : pool) {
75 if (!component.isActive() || !component.trajectory ||
76 component.trajectory->getComponent<TrajectoryComponent>()->positions.size() < 2) {
77
78 //clean up old resources if they exist
79 auto meshComponent = component.getComponent<MeshComponent>();
80 if (HandleIsValid(meshComponent->meshHandle)) {
81 meshComponent->meshHandle = MeshHandle::NoHandle;
82 auto annotationComponent = component.getComponent<AnnotationAxisComponent>();
83 annotationComponent->strings.clear();
84 annotationComponent->positions.clear();
85
86 }
87 continue;
88 }
89
90 auto trajectory = component.trajectory->getComponent<TrajectoryComponent>();
91 auto textComponent = component.getComponent<TextComponent>();
92 auto annotation = component.getComponent<AnnotationAxisComponent>();
93 auto & data = getData(&component);
94
95 if (component.hasChanged() || trajectory->hasChanged()) {
96 data.majorTickDepths.clear();
97 data.minorTickDepths.clear();
98 data.currentAxis = glm::vec3();
99
100 getAnnotationTicks(component, trajectory, data.majorTickDepths, data.minorTickDepths);
101
102 data.majorPositions.resize(data.majorTickDepths.size());
103 data.majorDirections.resize(data.majorTickDepths.size());
104
105 Cogs::Geometry::PathGenerator::generateLinearPath(data.majorTickDepths.data(),
106 static_cast<int>(data.majorTickDepths.size()),
107 trajectory->indexes.data(),
108 trajectory->positions.data(),
109 static_cast<int>(trajectory->positions.size()),
110 data.majorPositions.data(),
111 data.majorDirections.data());
112
113 if (data.minorTickDepths.size()) {
114 data.minorPositions.resize(data.minorTickDepths.size());
115 data.minorDirections.resize(data.minorTickDepths.size());
116
117 Cogs::Geometry::PathGenerator::generateLinearPath(data.minorTickDepths.data(),
118 static_cast<int>(data.minorTickDepths.size()),
119 trajectory->indexes.data(),
120 trajectory->positions.data(),
121 static_cast<int>(trajectory->positions.size()),
122 data.minorPositions.data(),
123 data.minorDirections.data());
124 }
125
126 if (data.majorTickDepths.size()) {
127 data.majorTexts.resize(data.majorTickDepths.size());
128
129 addText(component, data.majorTexts.data(), data.majorTickDepths.size(), data.majorTickDepths.data());
130
131 if (component.enableMinorText && data.minorTickDepths.size()) {
132 data.minorTexts.resize(data.minorTickDepths.size());
133
134 if (data.minorTexts.size()) {
135 addText(component, data.minorTexts.data(), data.minorTickDepths.size(), data.minorTickDepths.data());
136 }
137 }
138
139 if (component.axis.x > 0) {
140 textComponent->horizontalJustification = HorizontalJustification::Left;
141 } else {
142 textComponent->horizontalJustification = HorizontalJustification::Right;
143 }
144
145 auto meshComponent = component.getComponent<MeshComponent>();
146
147 if (!HandleIsValid(meshComponent->meshHandle)) {
148 meshComponent->meshHandle = context->meshManager->create();
149 }
150
151 auto renderComponent = component.getComponent<SubMeshRenderComponent>();
152
153 if (renderComponent->materials.size() != 2) {
154 renderComponent->materials.resize(2);
155
156 auto majorMaterial = context->materialInstanceManager->createMaterialInstance(context->materialManager->getDefaultMaterial());
157 auto minorMaterial = context->materialInstanceManager->createMaterialInstance(context->materialManager->getDefaultMaterial());
158
159 majorMaterial->setPermutation("Line");
160 minorMaterial->setPermutation("Line");
161
162 majorMaterial->setVariant("EnableLighting", false);
163 minorMaterial->setVariant("EnableLighting", false);
164
165 majorMaterial->setBoolProperty(DefaultMaterial::EnableLighting, false);
166 minorMaterial->setBoolProperty(DefaultMaterial::EnableLighting, false);
167
168 majorMaterial->setVec4Property(DefaultMaterial::DiffuseColor, component.diffuseColor);
169 minorMaterial->setVec4Property(DefaultMaterial::DiffuseColor, glm::vec4(component.diffuseColor.r, component.diffuseColor.g, component.diffuseColor.b, component.diffuseColor.a * 0.5f));
170
171 renderComponent->materials[0] = majorMaterial;
172 renderComponent->materials[1] = minorMaterial;
173 } else {
174 auto majorMaterial = renderComponent->materials[0];
175 auto minorMaterial = renderComponent->materials[1];
176
177 majorMaterial->setVec4Property(DefaultMaterial::DiffuseColor, component.diffuseColor);
178 minorMaterial->setVec4Property(DefaultMaterial::DiffuseColor, glm::vec4(component.diffuseColor.r, component.diffuseColor.g, component.diffuseColor.b, component.diffuseColor.a * 0.5f));
179 }
180
181 textComponent->color = component.diffuseColor;
182 if (!HandleIsValid(textComponent->fontHandle)) {
183 textComponent->fontHandle = context->fontManager->getHandle(context->fontManager->DefaultRegularFont);
184 }
185 }
186 }
187
188 glm::vec3 axis = glm::vec3(cameraData.inverseViewMatrix * glm::vec4(component.axis, 0));
189
190 if (!glm::all(glm::epsilonEqual(data.currentAxis, axis, 0.02f))) {
191 data.currentAxis = glm::normalize(axis);
192
193 glm::vec3 trajectoryAxis = data.majorDirections.size() ? data.majorDirections[0] : glm::vec3(0, 0, -1);
194
195 if (component.enableMaxSize) {
196 std::vector<float> majorScales(data.majorTickDepths.size());
197 std::vector<float> minorScales(data.minorTickDepths.size());
198
199 const float distance = component.majorDistance;
200 const float invertedDistance = 1.0f / distance;
201
202 const float projectedSize = component.maxSize;
203 const float nsize = projectedSize / static_cast<float>(cameraComponent->viewportSize.x);
204
205 auto & modelMatrix = context->transformSystem->getLocalToWorld(component.getComponent<TransformComponent>());
206 std::vector<glm::vec3> positions(data.majorTickDepths.size());
207
208 for (size_t j = 0; j < data.majorTickDepths.size(); ++j) {
209 positions[j] = Cogs::Geometry::transform(data.majorPositions[j], modelMatrix);
210 }
211
212 for (size_t i = 0; i < data.majorTickDepths.size(); ++i) {
213 auto scale = getWorldToScreenScale(context, cameraData, positions[i], nsize) * invertedDistance;
214
215 majorScales[i] = scale;
216
217 if (majorScales[i] > 1 || majorScales[i] != majorScales[i]) majorScales[i] = 1;
218 }
219
220 positions.resize(data.minorTickDepths.size());
221
222 for (size_t j = 0; j < data.minorTickDepths.size(); ++j) {
223 positions[j] = Cogs::Geometry::transform(data.minorPositions[j], modelMatrix);
224 }
225
226 for (size_t i = 0; i < data.minorTickDepths.size(); ++i) {
227 minorScales[i] = getWorldToScreenScale(context, cameraData, positions[i], nsize) * invertedDistance;
228
229 if (minorScales[i] > 1 || minorScales[i] != minorScales[i]) minorScales[i] = 1;
230 }
231
232 std::vector<glm::vec3> vertexes;
233 std::vector<uint32_t> majorIndexes;
234 std::vector<uint32_t> minorIndexes;
235
236 addTicks(component, vertexes, majorIndexes, data.majorPositions, data.majorDirections, axis, trajectoryAxis, component.majorDistance, majorScales);
237 addTicks(component, vertexes, minorIndexes, data.minorPositions, data.minorDirections, axis, trajectoryAxis, component.minorDistance, minorScales);
238
239 auto meshComponent = component.getComponent<MeshComponent>();
240 auto mesh = meshComponent->meshHandle.resolve();
241
242 mesh->clear();
243 mesh->setIndexes(std::vector<uint32_t>());
244 mesh->setPositions(std::move(vertexes));
245 mesh->addSubMesh(std::span(majorIndexes), PrimitiveType::LineList);
246 mesh->addSubMesh(std::span(minorIndexes), PrimitiveType::LineList);
247
248 annotation->strings.clear();
249 annotation->positions.clear();
250
251 addAnnotations(component, annotation, data.majorPositions, data.majorDirections, data.majorTexts, axis, trajectoryAxis, component.majorDistance, majorScales);
252
253 if (component.enableMinorText) {
254 addAnnotations(component, annotation, data.minorPositions, data.minorDirections, data.minorTexts, axis, trajectoryAxis, component.minorDistance, minorScales, true);
255 }
256 } else {
257 std::vector<glm::vec3> vertexes;
258 std::vector<uint32_t> majorIndexes;
259 std::vector<uint32_t> minorIndexes;
260
261 addTicks(component, vertexes, majorIndexes, data.majorPositions, data.majorDirections, axis, trajectoryAxis, component.majorDistance, std::vector<float>());
262 addTicks(component, vertexes, minorIndexes, data.minorPositions, data.minorDirections, axis, trajectoryAxis, component.minorDistance, std::vector<float>());
263
264 auto meshComponent = component.getComponent<MeshComponent>();
265 auto mesh = meshComponent->meshHandle.resolve();
266
267 mesh->clear();
268 mesh->setIndexes(std::vector<uint32_t>());
269 mesh->setPositions(std::move(vertexes));
270 mesh->addSubMesh(std::span(majorIndexes), PrimitiveType::LineList);
271 mesh->addSubMesh(std::span(minorIndexes), PrimitiveType::LineList);
272
273 annotation->strings.clear();
274 annotation->positions.clear();
275
276 addAnnotations(component, annotation, data.majorPositions, data.majorDirections, data.majorTexts, axis, trajectoryAxis, component.majorDistance, std::vector<float>());
277
278 if (component.enableMinorText) {
279 addAnnotations(component, annotation, data.minorPositions, data.minorDirections, data.minorTexts, axis, trajectoryAxis, component.minorDistance, std::vector<float>());
280 }
281 }
282 }
283 }
284}
285
286namespace Cogs
287{
288 namespace Calculation
289 {
290 template<class TReal, bool ShiftStartToNearestIntervalSize>
291 void calculateIntervals(TReal start, TReal end, TReal interval, std::vector<TReal> & majorIntervals)
292 {
293 if ((end - start) <= 0) {
294 return;
295 }
296
297 if (ShiftStartToNearestIntervalSize) {
298 float startFloor = std::floor(start / interval);
299 float truncatedStart = startFloor * interval;
300
301 if (truncatedStart < start) {
302 truncatedStart += interval;
303 }
304
305 start = truncatedStart;
306 }
307
308 float value = start;
309
310 while (value <= end) {
311 majorIntervals.push_back(value);
312 value += interval;
313 }
314 }
315
316 template<class TReal>
317 void calculateIntermediateIntervals(TReal start, TReal end, TReal interval, const std::vector<TReal> & majorIntervals, std::vector<TReal> & intervals, float epsilon = FLT_EPSILON)
318 {
319 if (!majorIntervals.size()) return;
320
321 float value = majorIntervals[0] - interval;
322
323 while (value >= start) {
324 intervals.push_back(value);
325 value -= interval;
326 }
327
328 for (size_t i = 0; i < majorIntervals.size() - 1; ++i) {
329 value = majorIntervals[i] + interval;
330
331 while (value < majorIntervals[i + 1] - epsilon) {
332 intervals.push_back(value);
333 value += interval;
334 }
335 }
336
337 value = majorIntervals[majorIntervals.size() - 1] + interval;
338
339 while (value <= end) {
340 intervals.push_back(value);
341 value += interval;
342 }
343 }
344 }
345}
346
347void Cogs::Core::DepthAxisSystem::getAnnotationTicks(DepthAxisComponent & component, const TrajectoryComponent * trajectory, std::vector<float> & majorTicks, std::vector<float> & minorTicks)
348{
349 const float scaledMajorInterval = component.majorInterval / component.unitScale;
350 const float scaledMinorInterval = component.minorInterval / component.unitScale;
351
352 const bool endpointsInclusive = component.endpointsInclusive;
353 const bool endpointsOnly = false;
354
355 const float startDepth = std::max(component.startMeasuredDepth, trajectory->indexes[0]);
356 const float endDepth = std::min(component.endMeasuredDepth, trajectory->indexes[trajectory->indexes.size() - 1]);
357
358 if (endpointsInclusive && (fmod(startDepth, scaledMajorInterval) != 0 || endpointsOnly)) {
359 majorTicks.push_back(startDepth);
360 }
361
362 if (!endpointsOnly) {
363 Cogs::Calculation::calculateIntervals<float, true>(startDepth, endDepth, scaledMajorInterval, majorTicks);
364 }
365
366 if (endpointsInclusive) {
367 majorTicks.push_back(endDepth);
368 }
369
370 if (!endpointsOnly) {
371 Cogs::Calculation::calculateIntermediateIntervals(startDepth, endDepth, scaledMinorInterval, majorTicks, minorTicks, scaledMinorInterval / 2);
372 }
373}
374
375void Cogs::Core::DepthAxisSystem::addText(DepthAxisComponent & component, std::string * texts, const size_t count, const float * depths)
376{
377 const std::string & unit = component.unitOfMeasurement;
378 const float unitScale = component.unitScale;
379
380 for (size_t i = 0; i < count; ++i) {
381 const float depth = depths[i] * unitScale;
382 const float remainder = depth - std::floor(depth + 0.5f);
383
384 std::stringstream ss;
385
386 if (fabs(remainder) > 0.01f) {
387 ss << std::fixed << std::setprecision(2) << depth << unit;
388 texts[i] = ss.str();
389 } else {
390 ss << std::fixed << std::setprecision(0) << depth << unit;
391 texts[i] = ss.str();
392 }
393 }
394}
395
396void Cogs::Core::DepthAxisSystem::addTicks(DepthAxisComponent & component, std::vector<glm::vec3> & vertexes, std::vector<uint32_t> & indexes, const std::vector<glm::vec3> & positions, const std::vector<glm::vec3> & directions, const glm::vec3 & axis, const glm::vec3 & principalAxis, const float extent, const std::vector<float> & scales)
397{
398 const size_t offset = vertexes.size();
399
400 vertexes.resize(offset + positions.size() * 2);
401 indexes.resize(positions.size() * 2);
402
403 float radius = component.radius;
404 bool useTrajectoryOrientedAxis = component.useTrajectoryOrientedAxis;
405
406 for (size_t i = 0; i < positions.size(); ++i) {
407 glm::vec3 base;
408
409 glm::quat rotation = Cogs::Core::getRotation(principalAxis, directions[i]);
410 base = glm::normalize(rotation * axis);
411
412 glm::vec3 start = positions[i] + (base * radius);
413
414 float scale = extent;
415
416 if (scales.size()) {
417 scale *= scales[i];
418 }
419
420 vertexes[offset + i * 2] = start;
421 vertexes[offset + i * 2 + 1] = start + (useTrajectoryOrientedAxis ? base : axis) * 0.9f * scale;
422 }
423
424 // We generate indices for the line set simply by treating the vertex array as pairs of vertices making up a tick.
425 int idx = 0;
426 for (size_t i = 0; i < positions.size(); i++) {
427 indexes[idx++] = static_cast<uint32_t>(offset + i * 2);
428 indexes[idx++] = static_cast<uint32_t>(offset + i * 2 + 1);
429 }
430}
431
432void Cogs::Core::DepthAxisSystem::addAnnotations(DepthAxisComponent & component, AnnotationAxisComponent * annotationAxis, const std::vector<glm::vec3> & positions, const std::vector<glm::vec3> & directions, const std::vector<std::string> & texts, const glm::vec3 & axis, const glm::vec3 & principalAxis, const float extent, const std::vector<float> & scales, bool removeDistantAnnotations /*= false*/)
433{
434 const size_t numAnnotations = positions.size();
435 const size_t offset = annotationAxis->strings.size();
436
437 // Make sure we have enough allocated memory for annotations.
438 annotationAxis->strings.resize(offset + numAnnotations);
439 annotationAxis->positions.resize(offset + numAnnotations);
440
441 float radius = component.radius;
442 float scaleThreshold = component.minorScaleThreshold;
443 bool useTrajectoryOrientedAxis = component.useTrajectoryOrientedAxis;
444
445 size_t index = offset;
446
447 for (size_t i = 0; i < numAnnotations; ++i) {
448 float distance = extent;
449
450 if (scales.size()) {
451 float scale = scales[i];
452
453 // Drop annotations that have too high scale.
454 if (removeDistantAnnotations && scale > scaleThreshold) {
455 continue;
456 }
457
458 distance *= scale;
459 }
460
461 glm::vec3 base;
462
463 glm::quat rotation = Cogs::Core::getRotation(principalAxis, directions[i]);
464 base = rotation * axis;
465
466 glm::vec3 start = positions[i] + (base * radius);
467
468 annotationAxis->strings[index] = texts[i];
469 annotationAxis->positions[index++] = start + (useTrajectoryOrientedAxis ? base : axis) * distance;
470 }
471
472 // Since we might have dropped some annotations, we need to make sure the arrays are correctly sized.
473 annotationAxis->strings.resize(index);
474 annotationAxis->positions.resize(index);
475
476 annotationAxis->setChanged();
477}
ComponentType * getComponent() const
Definition: Component.h:159
Context * context
Pointer to the Context instance the system lives in.
void update()
Updates the system state to that of the current frame.
ComponentPool< ComponentType > pool
Pool of components managed by the system.
A Context instance contains all the services, systems and runtime components needed to use Cogs.
Definition: Context.h:83
Contains a handle to a Mesh resource to use when rendering using the MeshRenderComponent.
Definition: MeshComponent.h:15
MeshHandle meshHandle
Handle to a Mesh resource to use when rendering.
Definition: MeshComponent.h:29
Renders a mesh with flexible submesh usage.
std::vector< MaterialInstanceHandle > materials
Materials used to render individual sub-meshes.
Renders the given text(s) by generating sprites.
Definition: TextComponent.h:77
std::vector< glm::vec3 > positions
Offset positions for each of the strings in texts.
Defines a 4x4 transformation matrix for the entity and a global offset for root entities.
bool HandleIsValid(const ResourceHandle_t< T > &handle)
Check if the given resource is valid, that is not equal to NoHandle or InvalidHandle.
@ Right
The rightmost character is placed to the left of the screen position.
@ Left
The leftmost character is placed to the right of the screen position.
Contains all Cogs related functionality.
Definition: FieldSetter.h:23
static const ResourceHandle_t NoHandle
Handle representing a default (or none if default not present) resource.
ResourceType * resolve() const
Resolve the handle, returning a pointer to the actual resource.
Data component defining a 3D trajectory, for example a Well trajectory.
std::vector< glm::vec3 > positions
Trajectory positions. For wells the Z component is a measurement of True Vertical Depth (TVD)....
@ LineList
List of lines.
Definition: Common.h:120