Cogs.Core
TrajectoryCrossSectionsSystem.cpp
1#define _USE_MATH_DEFINES
2#include <cmath>
3
4#include "Context.h"
5#include "Resources/MeshManager.h"
6
7#include "Components/Core/MeshComponent.h"
8#include "Components/Core/MeshRenderComponent.h"
9#include "Components/Data/TrajectoryComponent.h"
10#include "Components/Core/SceneComponent.h"
11#include "Components/Core/LodComponent.h"
12
13#include "Systems/Core/LodSystem.h"
14#include "Systems/Core/CameraSystem.h"
15#include "Systems/Core/TransformSystem.h"
16#include "Systems/Data/TrajectorySystem.h"
17
18#include "Utilities/FrustumClassification.h"
19#include "Utilities/TransformVertices.h"
20#include "Utilities/TessellationPredicates.h"
21
22#include "LoftedCrossSectionsComponent.h"
23#include "TrajectoryCrossSectionsSystem.h"
24#include "LoftedCrossSectionsSystem.h"
25
26#include "Foundation/ComponentModel/Entity.h"
27#include "Foundation/Logging/Logger.h"
28
29using namespace Cogs::Geometry;
30using namespace Cogs::Core;
31
32namespace {
33 Cogs::Logging::Log logger = Cogs::Logging::getLogger("TrajectoryCrossSectionSystem");
34
35 template<typename Store> struct StoreTraits;
36
37 template<>
38 struct StoreTraits < std::vector<Cogs::Core::PositionNormalVertex> >
39 {
40 typedef std::vector<Cogs::Core::PositionNormalVertex> Storage;
41 typedef glm::vec3 Pos;
42 typedef glm::vec2 Tex;
43 typedef glm::vec3 Nrm;
44
45 inline static void set(Storage& V, int i, const Pos& p, const Tex& /*t*/, const Nrm& n)
46 {
47 V[i].position = p;
48 V[i].normal = n;
49 }
50 };
51
52 template<>
53 struct StoreTraits < std::vector<Cogs::Core::PositionNormalTexVertex> >
54 {
55 typedef std::vector<Cogs::Core::PositionNormalTexVertex> Storage;
56 typedef glm::vec3 Pos;
57 typedef glm::vec2 Tex;
58 typedef glm::vec3 Nrm;
59
60 inline static void set(Storage& V, int i, const Pos& p, const Tex& t, const Nrm& n)
61 {
62 V[i].position = p;
63 V[i].normal = n;
64 V[i].tex = t;
65 }
66 };
67
68 template<TrajectoryCrossSectionsComponent::Parameterization type> struct ParameterizationTraits;
69
70 template<>
71 struct ParameterizationTraits < TrajectoryCrossSectionsComponent::Parameterization::None >
72 {
73 typedef std::vector<Cogs::Core::PositionNormalVertex> Store;
74
75 static StoreTraits<Store>::Tex map(float /*u*/, float /*v*/, float /*circumference*/, float /*acc_coordlen*/)
76 {
77 return StoreTraits<Store>::Tex(0.f);
78 }
79
80 static const bool doubleSeam = false;
81 };
82
83 template<>
84 struct ParameterizationTraits < TrajectoryCrossSectionsComponent::Parameterization::UnitSquare >
85 {
86 typedef std::vector<Cogs::Core::PositionNormalTexVertex> Store;
87
88 static StoreTraits<Store>::Tex map(float u, float v, float /*circumference*/, float /*acc_coordlen*/)
89 {
90 return StoreTraits<Store>::Tex(u, v);
91 }
92
93 static const bool doubleSeam = true;
94 };
95
96 template<>
97 struct ParameterizationTraits < TrajectoryCrossSectionsComponent::Parameterization::CoordLengthOverCircumference >
98 {
99 typedef std::vector<Cogs::Core::PositionNormalTexVertex> Store;
100
101 static StoreTraits<Store>::Tex map(float u, float /*v*/, float circumference, float acc_coordlen)
102 {
103 return StoreTraits<Store>::Tex(u, acc_coordlen / circumference);
104 }
105
106 static const bool doubleSeam = true;
107 };
108
109 template<bool hollow, bool caps, TrajectoryCrossSectionsComponent::Parameterization ParamType>
110 int sampleShape(typename ParameterizationTraits<ParamType>::Store& vertices,
111 std::vector<glm::vec3>& spine,
112 const std::vector<float>& depths,
113 const std::vector<glm::vec3>& positions,
114 const std::vector<glm::quat>& orientations,
115 const std::vector<bool>& include,
116 const float inner_radius,
117 const float outer_radius,
118 const float start_depth,
119 const float end_depth,
120 const int trajectory_offset,
121 const int slices,
122 const int samples)
123 {
124 typedef typename ParameterizationTraits<ParamType>::Store Store;
125 const bool doubleSeam = ParameterizationTraits<ParamType>::doubleSeam;
126
127 std::vector<glm::vec2> profile(samples);
128 for (int i = 0; i < samples; i++) {
129 float t = static_cast<float>(2.0*M_PI)*(static_cast<float>(i) / static_cast<float>(samples - (doubleSeam ? 1 : 0)));
130 profile[i].x = cos(t);
131 profile[i].y = sin(t);
132 }
133
134 int actualSlices = caps ? 4 : 2;
135 for (int i = 1; i + 1 < slices; i++) {
136 actualSlices += include[trajectory_offset + i] ? 1 : 0;
137 }
138
139 float circumference = 2.f*float(M_PI)*outer_radius;
140 float coord_length = 0.f;
141 const int f_h = hollow ? 2 : 1;
142 const int f_c = caps ? 1 : 0;
143
144 vertices.resize((hollow ? 2 : 1) * samples * actualSlices);
145 spine.resize(actualSlices);
146
147 if (true) {
148 // first slice, do lerp
149 size_t o = trajectory_offset;
150 float t = (start_depth - depths[o]) / (depths[o + 1] - depths[o]);
151 glm::quat q = glm::mix(orientations[o], orientations[o + 1], t);
152 glm::vec3 u = q*glm::vec3(1.f, 0.f, 0.f);
153 glm::vec3 v = q*glm::vec3(0.f, 1.f, 0.f);
154 glm::vec3 w = q*glm::vec3(0.f, 0.f, 1.f);
155 glm::vec3 p = glm::mix(positions[o], positions[o + 1], t);
156 spine[0] = p;
157 if (caps) {
158 spine[1] = p;
159 }
160 for (int i = 0; i < samples; i++) {
161 float unit_u = static_cast<float>(i) / (samples - (doubleSeam ? 1 : 0));
162 typename StoreTraits<Store>::Tex tex = ParameterizationTraits<ParamType>::map(unit_u, 0.f, circumference, 0.f);
163 typename StoreTraits<Store>::Nrm n = u * profile[i].x + v * profile[i].y;
164 if (caps) {
165 StoreTraits<Store>::set(vertices, f_h * (samples * 0 + i) + 0, p + outer_radius*n, tex, -w);
166 }
167 StoreTraits<Store>::set(vertices, f_h * (samples * f_c + i) + 0, p + outer_radius*n, tex, n);
168 if (hollow) {
169 if (caps) {
170 StoreTraits<Store>::set(vertices, f_h * (samples * 0 + i) + 1, p + inner_radius*n, tex, -w);
171 }
172 StoreTraits<Store>::set(vertices, f_h * (samples * f_c + i) + 1, p + inner_radius*n, tex, -n);
173 }
174 }
175 coord_length += (1.f - t)*glm::distance(positions[o], positions[o + 1]);
176 }
177
178 int slice = caps ? 2 : 1;
179 for (int j = 1; j < slices - 1; j++) { // intermediate slices
180 int k = j + trajectory_offset;
181 if (j > 1) {
182 coord_length += glm::distance(positions[k - 1], positions[k]);
183 }
184 if (include[k]) {
185 glm::vec3 u = orientations[k] * glm::vec3(1.f, 0.f, 0.f);
186 glm::vec3 v = orientations[k] * glm::vec3(0.f, 1.f, 0.f);
187 typename StoreTraits<Store>::Pos p = positions[k];
188 float unit_v = ((depths[k] - start_depth) / (end_depth - start_depth));
189
190 spine[slice] = p;
191 for (int i = 0; i < samples; i++) {
192 float unit_u = static_cast<float>(i) / (samples - (doubleSeam ? 1 : 0));
193 typename StoreTraits<Store>::Tex tex = ParameterizationTraits<ParamType>::map(unit_u, unit_v, circumference, coord_length);
194 if (hollow) {
195 typename StoreTraits<Store>::Nrm n = u * profile[i].x + v * profile[i].y;
196 StoreTraits<Store>::set(vertices, 2 * (samples * slice + i) + 0, p + outer_radius*n, tex, n);
197 StoreTraits<Store>::set(vertices, 2 * (samples * slice + i) + 1, p + inner_radius*n, tex, -n);
198 }
199 else {
200 typename StoreTraits<Store>::Nrm n = u * profile[i].x + v * profile[i].y;
201 StoreTraits<Store>::set(vertices, samples * slice + i + 0, p + outer_radius*n, tex, n);
202 }
203 }
204 slice++;
205 }
206 }
207
208 if (true) { // last slice, do lerp
209 size_t o = trajectory_offset + slices - 2;
210 float t = (end_depth - depths[o]) / (depths[o + 1] - depths[o]);
211 glm::quat q = glm::mix(orientations[o], orientations[o + 1], t);
212 glm::vec3 u = q*glm::vec3(1.f, 0.f, 0.f);
213 glm::vec3 v = q*glm::vec3(0.f, 1.f, 0.f);
214 glm::vec3 w = q*glm::vec3(0.f, 0.f, 1.f);
215 glm::vec3 p = glm::mix(positions[o], positions[o + 1], t);
216 coord_length += t*glm::distance(positions[o], positions[o + 1]);
217 spine[slice + 0] = p;
218 if (caps){
219 spine[slice + 1] = p;
220 }
221 for (int i = 0; i < samples; i++) {
222 float unit_u = static_cast<float>(i) / (samples - (doubleSeam ? 1 : 0));
223 typename StoreTraits<Store>::Tex tex = ParameterizationTraits<ParamType>::map(unit_u, 1.f, circumference, coord_length);
224 typename StoreTraits<Store>::Nrm n = u * profile[i].x + v * profile[i].y;
225 StoreTraits<Store>::set(vertices, f_h * (samples * (slice + 0) + i) + 0, p + outer_radius*n, tex, n);
226 if (caps) {
227 StoreTraits<Store>::set(vertices, f_h * (samples * (slice + 1) + i) + 0, p + outer_radius*n, tex, w);
228 }
229 if (hollow) {
230 StoreTraits<Store>::set(vertices, f_h * (samples * (slice + 0) + i) + 1, p + inner_radius*n, tex, -n);
231 if (caps){
232 StoreTraits<Store>::set(vertices, f_h * (samples * (slice + 1) + i) + 1, p + inner_radius*n, tex, w);
233 }
234 }
235 }
236 }
237
238 return actualSlices;
239 }
240
241
242 template<TrajectoryCrossSectionsComponent::Parameterization ParamType>
243 void process(const TrajectoryCrossSectionsComponent& cSectComp,
245 TrajectoryComponent* trajComp,
246 TrajectoryData& trajData,
248 {
249 typedef typename ParameterizationTraits<ParamType>::Store Store;
250 Store vertices;
251 std::vector<glm::vec3> spine;
252
253 const bool doubleSeam = ParameterizationTraits<ParamType>::doubleSeam;
254 const int samples = cSectData.samples + (doubleSeam ? 1 : 0);
255 const float startMeasuredDepth = cSectData.startMeasuredDepth;
256 const float stopMeasuredDepth = cSectData.stopMeasuredDepth;
257 const float innerRadius = cSectComp.radiusScale * cSectComp.innerRadius;
258 const float outerRadius = cSectComp.radiusScale * cSectComp.outerRadius;
259 int rings = cSectData.rings;
260
261 switch (cSectComp.shape) {
262
263 case LoftedCrossSectionsComponent::Shape::Hollow:
264 rings = sampleShape<true, true, ParamType>(vertices, spine,
265 trajComp->indexes, trajComp->positions, trajData.frames, trajData.lodSelection,
266 innerRadius, outerRadius, startMeasuredDepth, stopMeasuredDepth,
267 cSectData.index0, rings, samples);
268
269 loftComp->set(std::move(vertices), std::move(spine), LoftedCrossSectionsComponent::Shape::Hollow, samples, rings, doubleSeam);
270 break;
271
272 case LoftedCrossSectionsComponent::Shape::Solid:
273 rings = sampleShape<false, true, ParamType>(vertices, spine,
274 trajComp->indexes, trajComp->positions, trajData.frames, trajData.lodSelection,
275 innerRadius, outerRadius, startMeasuredDepth, stopMeasuredDepth,
276 cSectData.index0, rings, samples);
277
278 loftComp->set(std::move(vertices), std::move(spine), LoftedCrossSectionsComponent::Shape::Solid, samples, rings, doubleSeam);
279 break;
280
281 case LoftedCrossSectionsComponent::Shape::Shell:
282 rings = sampleShape<false, false, ParamType>(vertices, spine,
283 trajComp->indexes, trajComp->positions, trajData.frames, trajData.lodSelection,
284 innerRadius, outerRadius, startMeasuredDepth, stopMeasuredDepth,
285 cSectData.index0, rings, samples);
286
287 loftComp->set(std::move(vertices), std::move(spine), LoftedCrossSectionsComponent::Shape::Shell, samples, rings, doubleSeam);
288 break;
289 }
290 }
291
292 void minMaxFromVertices3(Context* /*context*/,
293 float* min_v3,
294 float* max_v3,
295 const uint8_t* src,
296 const size_t src_stride,
297 const size_t /*src_bytes*/,
298 const size_t src_count)
299 {
300 if (src_count == 0) {
301 return;
302 }
303
304 glm::vec3& min = *reinterpret_cast<glm::vec3*>(min_v3);
305 glm::vec3& max = *reinterpret_cast<glm::vec3*>(max_v3);
306
307 min = max = *reinterpret_cast<const glm::vec3*>(src);
308
309 for (size_t i = 1; i < src_count; i++) {
310 const glm::vec3& s = *reinterpret_cast<const glm::vec3*>(src + src_stride * i);
311 min = glm::min(min, s);
312 max = glm::max(max, s);
313 }
314 }
315
316
317 inline void minMaxFromVertices(Context* context,
318 glm::vec3& min,
319 glm::vec3& max,
320 const std::vector<glm::vec3>& src,
321 size_t offset,
322 size_t count)
323 {
324 assert(offset + count <= src.size());
325 minMaxFromVertices3(context, glm::value_ptr(min), glm::value_ptr(max),
326 reinterpret_cast<const uint8_t*>(src.data() + offset),
327 sizeof(glm::vec3), sizeof(glm::vec3)*(src.size() - offset), count);
328 }
329
330} // of anonymous namespace
331
332
334{
335 CameraComponent* camComp = context->cameraSystem->getMainCamera();
336
337 for (const auto & cSectComp : pool) {
338 if (!cSectComp.trajectory) {
339 continue;
340 }
341
342 TrajectoryComponent * trajComp = cSectComp.trajectory->getComponent<TrajectoryComponent>();
343 LodComponent * lodComp = cSectComp.getComponent<LodComponent>();
344 LodData & lodData = context->lodSystem->getData<LodData>(lodComp);
346
347 auto& cSectData = getData(&cSectComp);
348 auto& trajData = context->trajectorySystem->getData(trajComp);
349
350
351 // update cross sections if required
352 if (cSectComp.hasChanged() || lodComp->hasChanged() || trajComp->hasChanged()) {
353
354 if (trajComp->positions.size() < 2) {
355 LOG_WARNING(logger, "trajectory must have at least two samples, skipping.");
356 loftComp->visibility = false;
357 loftComp->setChanged();
358 continue;
359 }
360
361 if (trajComp->positions.size() != trajComp->indexes.size()) {
362 LOG_WARNING(logger, "trajectory has mismatching position and index counts, skipping.");
363 loftComp->visibility = false;
364 loftComp->setChanged();
365 continue;
366 }
367
368 if (cSectComp.stopMeasuredDepth <= cSectComp.startMeasuredDepth + std::numeric_limits<float>::epsilon()) {
369 LOG_WARNING(logger, "trajectory subset has illegal measured depth extent, skipping.");
370 loftComp->visibility = false;
371 loftComp->setChanged();
372 continue;
373 }
374
375 // clip measured depth interval to defined range
376 cSectData.startMeasuredDepth = std::max(trajComp->indexes.front(), cSectComp.startMeasuredDepth);
377 cSectData.stopMeasuredDepth = std::min(trajComp->indexes.back(), cSectComp.stopMeasuredDepth);
378
379 // find start and stop along trajectory
380 trajComp->determineindices(cSectData.index0, cSectData.index1, cSectData.rings,
381 cSectData.startMeasuredDepth, cSectData.stopMeasuredDepth);
382
383 if (cSectData.rings < 2) {
384 LOG_ERROR(logger, "Less than two slices will be produced, skipping.");
385 loftComp->visibility = false;
386 loftComp->setChanged();
387 continue;
388 }
389
390 // Determine bounding box (bbox of trajectory points grown by outer radius)
391 minMaxFromVertices(context,
392 cSectData.trajectoryBoundingBox.min,
393 cSectData.trajectoryBoundingBox.max,
394 trajComp->positions, cSectData.index0,
395 cSectData.rings);
396 cSectData.boundingBox.min = cSectData.trajectoryBoundingBox.min - glm::vec3(cSectComp.radiusScale*cSectComp.outerRadius);
397 cSectData.boundingBox.max = cSectData.trajectoryBoundingBox.max + glm::vec3(cSectComp.radiusScale*cSectComp.outerRadius);
398
399 // We should recreate the cross sections. However, due to culling, this
400 // does not necessarily happen this frame, so we store this information
401 // in a persistent flag.
402 cSectData.generate = true;
403 }
404
405 // --- Bounding box check ------------------------------------------------
406 //glm::mat4 localToClip = worldToClip * context->transformSystem->getLocalToWorld(transComp);
407 FrustumPlanes frustumCheck = frustumClassifyBoundingBox(lodData.localToClip,
408 cSectData.boundingBox.min,
409 cSectData.boundingBox.max);
410 if ((frustumCheck&FrustumPlanes::InsideAllButFar) != FrustumPlanes::InsideAllButFar) {
411 // we're outside the frustum, don't generate any geometry.
412 if (loftComp->visibility) {
413 loftComp->visibility = false;
414 loftComp->setChanged();
415 }
416 continue;
417 }
418 else if (loftComp->visibility == false) {
419 // we have popped inside the frustum from outside, recreate geometry
420 loftComp->visibility = true;
421 loftComp->setChanged();
422 cSectData.generate = true;
423 }
424
425 // --- Level of detail check ---------------------------------------------
426 if (lodComp->policy == LodPolicy::GeometricTolerance) {
427
428 // --- Screen space reference point ------------------------------------
429 // Find screen-space reference point that we are going to use for
430 // determining screen-space radius. To avoid div-by-zero and extreme
431 // tessellation, we clamp this position to the near-plane.
432 bool inFrontOfNear = false;
433 glm::vec4 screenReferencePoint = lodData.localToClip * glm::vec4(cSectData.trajectoryBoundingBox.min, 1.f);
434 for (int i = 0; i < 8; i++) {
435 glm::vec3 p((i & 1) ? cSectData.trajectoryBoundingBox.min.x : cSectData.trajectoryBoundingBox.max.x,
436 (i & 2) ? cSectData.trajectoryBoundingBox.min.y : cSectData.trajectoryBoundingBox.max.y,
437 (i & 4) ? cSectData.trajectoryBoundingBox.min.z : cSectData.trajectoryBoundingBox.max.z);
438 glm::vec4 h = lodData.localToClip * glm::vec4(p, 1.f);
439 if (h.z / h.w < screenReferencePoint.z / screenReferencePoint.w) {
440 screenReferencePoint = h;
441 }
442 if (h.z < -h.w) {
443 inFrontOfNear = true;
444 }
445 }
446 if (inFrontOfNear) {
447 screenReferencePoint = glm::vec4(0.f, 0.f, -1.f, 1.f);
448 }
449
450 // --- Find screen space radius and determine refinement level ---------
451 float minSamples = 3.f;
452 float maxSamples = 4.f * float(cSectComp.samples);
453 float epsilon = lodComp->geometricTolerance;
454 glm::vec2 rr = localSphereRadiusInScreenSpace(lodData.localToClip, lodData.clipToLocal,
455 screenReferencePoint,
456 cSectComp.radiusScale*cSectComp.outerRadius);
457 float nonIntegerSamples = 0.0f;
458 switch (context->lodSystem->geometricErrorKind)
459 {
460 case GeometricErrorKind::AreaBased:
461 nonIntegerSamples = sphereSectorGeometricAreaPredicate(rr, camComp->viewportSize,
462 epsilon*epsilon, minSamples, maxSamples);
463 break;
464 case GeometricErrorKind::DistanceBased:
465 nonIntegerSamples = sphereSectorGeometricDistancePredicate(rr, camComp->viewportSize,
466 epsilon, minSamples, maxSamples);
467 break;
468 }
469
470 // --- Check LoD trigger geometry recreation ---------------------------
471 int samples = int(std::ceil(nonIntegerSamples));
472 bool change = samples != cSectData.samples;
473 if (change) {
474 cSectData.samples = samples;
475 cSectData.generate = true;
476 }
477 }
478 else {
479 cSectData.samples = cSectComp.samples;
480 }
481
482 // --- Create cross sections ---------------------------------------------
483 if (cSectData.generate) {
484
485 switch (cSectComp.parameterization)
486 {
488 process<TrajectoryCrossSectionsComponent::Parameterization::None>(cSectComp, cSectData, trajComp, trajData, loftComp);
489 break;
491 process<TrajectoryCrossSectionsComponent::Parameterization::UnitSquare>(cSectComp, cSectData, trajComp, trajData, loftComp);
492 break;
494 process<TrajectoryCrossSectionsComponent::Parameterization::CoordLengthOverCircumference>(cSectComp, cSectData, trajComp, trajData, loftComp);
495 break;
496 }
497 cSectData.generate = false;
498 }
499 }
500}
ComponentType * getComponent() const
Definition: Component.h:159
glm::vec2 viewportSize
Size of the viewport covered by this instance, given in pixels.
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 data describing level of detail behavior for the entity the component belongs to.
Definition: LodComponent.h:43
Log implementation class.
Definition: LogManager.h:139
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
COGSCORE_DLL_API glm::vec2 localSphereRadiusInScreenSpace(const glm::mat4 &localToClip, const glm::mat4 &clipToLocal, const glm::vec4 &clipPosition, const float localRadius)
Given a screen space reference position and a local space radius, find the corresponding screen space...
@ GeometricTolerance
Use a geometric error bound to determine how refined geometry needs to be at its current position.
Contains geometry calculations and generation.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:180
STL namespace.
Defines level of detail data calculated by the LodSystem.
Definition: LodSystem.h:15
int samples
Number of sample points in each of the cross sections.
Data component defining a 3D trajectory, for example a Well trajectory.
std::vector< float > indexes
Positive distances along trajectory. For wells Measured Depth (MD). Set same length of indexes and po...
COGSCORE_DLL_API void determineindices(int &ix0, int &ix1, int &slices, const float startDepth, const float stopDepth)
std::vector< glm::vec3 > positions
Trajectory positions. For wells the Z component is a measurement of True Vertical Depth (TVD)....
int samples
Number of samples around the circumference.
@ CoordLengthOverCircumference
U maps to [0,1) around circumference, V is coordlength divided by circumference.
float outerRadius
The outer radius of the tubular shape, used for all shapes.
float stopMeasuredDepth
Measured depth along trajectory where casing stops.
float innerRadius
The inner radius of the tubular shape, only used for Hollow shape.
float radiusScale
Factor by which to scale the inner and outer radius.
int samples
Number of perimeter samples used when cross sections were created.
int rings
Number of rings around trajectory in tessellation.
std::vector< glm::quat > frames
Frames of reference along trajectory with minimal torsion.
std::vector< bool > lodSelection
Set of trajectory stations that should be included in current lod-situation.