Isis 3 Programmer Reference
EmbreeShapeModel.cpp
1
6/* SPDX-License-Identifier: CC0-1.0 */
7
8#include "EmbreeShapeModel.h"
9
10#include <numeric>
11#include <float.h>
12
13#include <QtGlobal>
14#include <QList>
15
16#include "IException.h"
17#include "IString.h"
18#include "Latitude.h"
19#include "Longitude.h"
20#include "NaifStatus.h"
21#include "Pvl.h"
22#include "ShapeModel.h"
23#include "Target.h"
24
25
26using namespace std;
27
28namespace Isis {
29
34 : ShapeModel(),
35 m_targetShape(0),
36 m_targetManager(0),
37 m_tolerance(DBL_MAX),
38 m_shapeFile("") {
39 // defaults for ShapeModel parent class include:
40 // name = empty string
41 // surfacePoint = null sp
42 // hasIntersection = false
43 // hasNormal = false
44 // normal = (0,0,0)
45 // hasEllipsoidIntersection = false
46 setName("Embree");
47 }
48
49
63 EmbreeTargetManager *targetManager)
64 : ShapeModel(target),
65 m_targetShape(0),
66 m_targetManager(targetManager),
67 m_tolerance(DBL_MAX),
68 m_shapeFile("") {
69
70 // defaults for ShapeModel parent class include:
71 // name = empty string
72 // surfacePoint = null sp
73 // hasIntersection = false
74 // hasNormal = false
75 // normal = (0,0,0)
76 // hasEllipsoidIntersection = false
77
78 setName("Embree"); // Really is used as type in the system at present!
79
80 PvlGroup &kernels = pvl.findGroup("Kernels", Pvl::Traverse);
81
82 if (kernels.hasKeyword("ElevationModel")) {
83 m_shapeFile = (QString) kernels["ElevationModel"];
84 }
85 else { // if (kernels.hasKeyword("ShapeModel")) {
86 m_shapeFile = (QString) kernels["ShapeModel"];
87 }
88
89 try {
90 // Request the EmbreeTargetShape from the manager
91 // If the shapefile is being used by something else this will get a pointer
92 // to the same target shape, otherwise it creates a new one.
93 m_targetShape = m_targetManager->create(m_shapeFile);
94 }
95 catch(IException &e) {
96 QString msg = "Cannot create a EmbreeShape from " + m_shapeFile;
97 throw IException(e, IException::User, msg, _FILEINFO_);
98 }
99 }
100
101
109 EmbreeShapeModel::EmbreeShapeModel(Target *target, const QString &shapefile,
110 EmbreeTargetManager *targetManager)
111 : ShapeModel(target),
112 m_targetShape(0),
113 m_targetManager(targetManager),
114 m_tolerance(DBL_MAX),
115 m_shapeFile(shapefile) {
116
117 // defaults for ShapeModel parent class include:
118 // name = empty string
119 // surfacePoint = null sp
120 // hasIntersection = false
121 // hasNormal = false
122 // normal = (0,0,0)
123 // hasEllipsoidIntersection = false
124
125 setName("Embree"); // Really is used as type in the system at present!
126
127 try {
128 // Request the EmbreeTargetShape from the manager
129 // If the shapefile is being used by something else this will get a pointer
130 // to the same target shape, otherwise it creates a new one.
131 m_targetShape = m_targetManager->create(m_shapeFile);
132 }
133 catch(IException &e) {
134 QString msg = "Cannot create a EmbreeShape from " + m_shapeFile;
135 throw IException(e, IException::User, msg, _FILEINFO_);
136 }
137 }
138
139
151
152
162 bool EmbreeShapeModel::intersectSurface(std::vector<double> observerPos,
163 std::vector<double> lookDirection) {
164 // Remove any previous intersection
166
167 // Create a ray from the observer in the look direction
168 RTCMultiHitRay ray(observerPos, lookDirection);
169
170 m_targetShape->intersectRay(ray);
171
172 // If nothing was hit
173 if (ray.lastHit < 0) {
174 setHasIntersection(false);
175 }
176 else {
177 // Get the intersection point and the surface normal
178 RayHitInformation hitInfo = m_targetShape->getHitInformation(ray, 0);
179
180 // Update the surface point and surface normal
181 updateIntersection(hitInfo);
182 }
183
184 return hasIntersection();
185 }
186
187
208 const std::vector<double> &observerPos,
209 const bool &backCheck) {
210 // Remove any previous intersection
212
213 // Create a ray from the origin through the surface point
214 RTCMultiHitRay ray = latlonToRay(lat,lon);
215
216 m_targetShape->intersectRay(ray);
217
218 // If no intersections (unlikely for this case), we are done!
219 if ( ray.lastHit < 0 ) {
220 return ( false );
221 }
222 LinearAlgebra::Vector observer = LinearAlgebra::vector(observerPos[0],
223 observerPos[1],
224 observerPos[2]);
225
226 // Sorts hits based on distance to the observer
227 QVector<RayHitInformation> hits = sortHits(ray, observer);
228
229 // If desired, check occlusion
230 if ( backCheck ) {
231 for (int i = 0 ; i < hits.size() ; i++) {
232 LinearAlgebra::Vector obsToIntersection = hits[i].intersection - observer;
233 LinearAlgebra::Vector lookVector = LinearAlgebra::normalize(obsToIntersection);
234
235 // Cast a ray from the observer to the intersection and check if it is occluded
236 RTCOcclusionRay obsRay;
237 obsRay.ray.org_x = observerPos[0];
238 obsRay.ray.org_y = observerPos[1];
239 obsRay.ray.org_z = observerPos[2];
240 obsRay.ray.dir_x = lookVector[0];
241 obsRay.ray.dir_y = lookVector[1];
242 obsRay.ray.dir_z = lookVector[2];
243 obsRay.ray.tnear = 0.0;
244 obsRay.ray.tfar = LinearAlgebra::magnitude(obsToIntersection) - 0.0005;
245 obsRay.hit.instID[0] = RTC_INVALID_GEOMETRY_ID;
246 obsRay.hit.geomID = RTC_INVALID_GEOMETRY_ID;
247 obsRay.hit.primID = RTC_INVALID_GEOMETRY_ID;
248 obsRay.ray.mask = 0xFFFFFFFF;
249 obsRay.ignorePrimID = hits[i].primID;
250
251 // If the intersection point is not occluded,
252 // then it is the closest intersection to the oberserver.
253 if ( !m_targetShape->isOccluded(obsRay) ) {
254 updateIntersection( hits[i] );
255 break;
256 }
257 }
258 }
259 else {
260 // If not testing for occlusion, take the hit closest to the observer
261 updateIntersection( hits[0] );
262 }
263
264 return ( hasIntersection() );
265 }
266
267
286 const std::vector<double> &observerPos,
287 const bool &backCheck) {
288 // Remove any previous intersection
290
291 // Set up for finding all rays along origin vector through lat/lon surface point
292 RTCMultiHitRay ray = pointToRay(surfpt);
293
294 // Extend the ray to be 1.5 times the length of the SurfacePoint's radius
295 ray.ray.tfar *= 1.5;
296
297 m_targetShape->intersectRay(ray);
298
299 // If no intersections (unlikely for this case), we are done!
300 if ( ray.lastHit < 0 ) {
301 return ( false );
302 }
303 // Convert the observer to a LinearAlgebra vector for occlusion testing
304 LinearAlgebra::Vector observer = LinearAlgebra::vector(observerPos[0],
305 observerPos[1],
306 observerPos[2]);
307
308 // Convert the surface point to a LinearAlgebra vector for sorting hits
309 LinearAlgebra::Vector surfPoint(3);
310 surfpt.ToNaifArray( &surfPoint[0] );
311
312 // Sorts hits based on distance to the surface point
313 QVector< RayHitInformation > hits = sortHits(ray, surfPoint);
314
315 // If desired, check occlusion
316 if ( backCheck ) {
317 for (int i = 0 ; i < hits.size() ; i++) {
318 LinearAlgebra::Vector obsToIntersection = hits[i].intersection - observer;
319 LinearAlgebra::Vector lookVector = LinearAlgebra::normalize(obsToIntersection);
320
321 // Cast a ray from the observer to the intersection and check if it is occluded
322 RTCOcclusionRay obsRay;
323 obsRay.ray.org_x = observerPos[0];
324 obsRay.ray.org_y = observerPos[1];
325 obsRay.ray.org_z = observerPos[2];
326 obsRay.ray.dir_x = lookVector[0];
327 obsRay.ray.dir_y = lookVector[1];
328 obsRay.ray.dir_z = lookVector[2];
329 obsRay.ray.tnear = 0.0;
330 obsRay.ray.tfar = LinearAlgebra::magnitude(obsToIntersection);
331 obsRay.hit.instID[0] = RTC_INVALID_GEOMETRY_ID;
332 obsRay.hit.geomID = RTC_INVALID_GEOMETRY_ID;
333 obsRay.hit.primID = RTC_INVALID_GEOMETRY_ID;
334 obsRay.ray.mask = 0xFFFFFFFF;
335 obsRay.ignorePrimID = hits[i].primID;
336
337 // If the intersection point is no occluded,
338 // then it is the closest intersection to the oberserver.
339 if ( !m_targetShape->isOccluded(obsRay) ) {
340 updateIntersection( hits[i] );
341 break;
342 }
343 }
344 }
345 else {
346 // If not testing for occlusion, take the hit closest to the surface point
347 updateIntersection( hits[0] );
348 }
349
350 return ( hasIntersection() );
351 }
352
353
360 // Flag that there is an intersection
361 setHasIntersection(true);
362
363 // Create the surfacepoint
364 SurfacePoint intersectPoint;
365 std::vector<double> intersectArray(3);
366 std::copy( hitInfo.intersection.data().begin(),
367 hitInfo.intersection.data().end(),
368 intersectArray.begin() );
369 intersectPoint.FromNaifArray( &intersectArray[0] );
370 setSurfacePoint(intersectPoint);
371
372 // Save the surface normal
373 setNormal( hitInfo.surfaceNormal[0],
374 hitInfo.surfaceNormal[1],
375 hitInfo.surfaceNormal[2] );
376 }
377
378
391
392
405 const Longitude &lon) {
406
407 // Create a ray from the origin to the surface point
408 RTCMultiHitRay ray = latlonToRay(lat,lon);
409
410 // Extend the ray to 2.5 times the maximum radius
411 ray.ray.tfar *= 2.5;
412
413 m_targetShape->intersectRay(ray);
414
415 // If no intersections (unlikely for this case), we are done!
416 if ( ray.lastHit < 0 ) {
417 return ( Distance() );
418 }
419 // Otherwise, get the first intersection
420 RayHitInformation hitInfo = m_targetShape->getHitInformation( ray, 0 );
421
422 // Return the distance to the intersection
423 return ( Distance( LinearAlgebra::magnitude(hitInfo.intersection),
425 }
426
427
436 return false;
437 }
438
439
450 bool EmbreeShapeModel::isVisibleFrom(const std::vector<double> observerPos,
451 const std::vector<double> lookDirection) {
452 //TODO check if there is a saved intersection
453 // Create a ray from the observer in the look direction
454 RTCMultiHitRay ray(observerPos, lookDirection);
455
456 m_targetShape->intersectRay(ray);
457
458 // If nothing was hit, something went really wrong. Just return false.
459 if (ray.lastHit < 0) {
460 return false;
461 }
462 else {
463 // Get the new intersection point
464 RayHitInformation hitInfo = m_targetShape->getHitInformation(ray, 0);
465
466 // Check the distance between the new intersection and the saved intersection
467 std::vector<double> intersectVect(3);
468 surfaceIntersection()->ToNaifArray( &intersectVect[0] );
469 LinearAlgebra::Vector oldIntersection = LinearAlgebra::vector( intersectVect[0],
470 intersectVect[1],
471 intersectVect[2] );
472 return ( LinearAlgebra::magnitude(oldIntersection - hitInfo.intersection) < getTolerance() );
473 }
474 }
475
476
502 void EmbreeShapeModel::calculateLocalNormal(QVector<double *> neighborPoints) {
503 // Sanity check
504 if ( !hasIntersection() ) { // hasIntersection() <==> hasNormall()
505 QString mess = "Intercept point does not exist - cannot provide normal vector";
506 throw IException(IException::Programmer, mess, _FILEINFO_);
507 }
508
509 return;
510 }
511
512
517 // ShapeModel (parent class) throws error if no intersection
519 }
520
521
526 // ShapeModel (parent class) throws error if no intersection
527 setNormal(ellipsoidNormal().toStdVector());// this takes care of setHasNormal(true);
528 return;
529 }
530
531
547
548 // Sanity check on state
549 if ( !hasIntersection() ) {
550 QString msg = "An intersection must be defined before computing the surface normal.";
551 throw IException(IException::Programmer, msg, _FILEINFO_);
552 }
553
554 if ( !surfaceIntersection()->Valid() ) {
555 QString msg = "The surface point intersection must be valid to compute the surface normal.";
556 throw IException(IException::Programmer, msg, _FILEINFO_);
557 }
558
559 if (!hasValidTarget()) {
560 QString msg = "A valid target must be defined before computing the surface normal.";
561 throw IException(IException::Programmer, msg, _FILEINFO_);
562 }
563
564 // Get the coordinates of the current surface point
565 SpiceDouble pB[3];
566 surfaceIntersection()->ToNaifArray(pB);
567
568 // Get the body radii and compute the true normal of the ellipsoid
569 QVector<double> norm(3);
570 // need a case for target == NULL
571 QVector<Distance> radii = QVector<Distance>::fromStdVector(targetRadii());
573 surfnm_c(radii[0].kilometers(), radii[1].kilometers(), radii[2].kilometers(),
574 pB, &norm[0]);
576
577 return (norm);
578 }
579
580
597 double EmbreeShapeModel::incidenceAngle(const std::vector<double> &illuminatorBodyFixedPosition) {
598
599 // If there is already a normal save it, because it's probably the local normal
600 std::vector<double> localNormal;
601 bool hadNormal = hasNormal();
602 if ( hadNormal ) {
604 }
605
606 // Calculate the ellipsoid surface normal
608
609 // Use ShapeModel to calculate the ellipsoid incidence angle
610 double ellipsoidEmission = ShapeModel::incidenceAngle(illuminatorBodyFixedPosition);
611
612 // If there's a saved normal, reset it
613 if ( hadNormal ) {
615 }
616
617 // Return the ellipsoid incidence angle
618 return ellipsoidEmission;
619 }
620
621
635 // Initialize ray
636 RTCMultiHitRay ray;
637 ray.ray.org_x = 0.0;
638 ray.ray.org_y = 0.0;
639 ray.ray.org_z = 0.0;
640
641 // Convert the lat, lon to a unit look direction
642 double latAngle = lat.radians();
643 double lonAngle = lon.radians();
644 ray.ray.dir_x = cos(latAngle) * cos(lonAngle);
645 ray.ray.dir_y = cos(latAngle) * sin(lonAngle);
646 ray.ray.dir_z = sin(latAngle);
647
648 // Set the ray's length to extend to the scene boundary
649 ray.ray.tfar = m_targetShape->maximumSceneDistance();
650
651 return (ray);
652 }
653
654
665 // Setup everything but the direction component
666 RTCMultiHitRay ray;
667 ray.ray.org_x = 0.0;
668 ray.ray.org_y = 0.0;
669 ray.ray.org_z = 0.0;
670 ray.ray.tnear = 0.0;
671 ray.hit.instID[0] = RTC_INVALID_GEOMETRY_ID; // instance
672 ray.hit.geomID = RTC_INVALID_GEOMETRY_ID;
673 ray.hit.primID = RTC_INVALID_GEOMETRY_ID; // primitive id (triangle id)
674 ray.ray.mask = 0xFFFFFFFF;
675 ray.lastHit = -1;
676
677 // Get the vector from the origin to the surface point
678 std::vector<double> surfVect(3);
679 surfpt.ToNaifArray( &surfVect[0] );
680 LinearAlgebra::Vector direction = LinearAlgebra::vector( surfVect[0],
681 surfVect[1],
682 surfVect[2] );
683
684 // Normalize the vector and store it in the ray
685 direction = LinearAlgebra::normalize(direction);
686 ray.ray.dir_x = direction[0];
687 ray.ray.dir_y = direction[1];
688 ray.ray.dir_z = direction[2];
689
690 // Extend the ray to the surface point
691 ray.ray.tfar = surfpt.GetLocalRadius().kilometers();
692 return (ray);
693 }
694
695
710 QVector<RayHitInformation> EmbreeShapeModel::sortHits(RTCMultiHitRay &ray,
711 LinearAlgebra::Vector &observer) {
712 int hitCount = ray.lastHit + 1;
713 // Container that holds the hit (x, y, z) and distance from the origin
714 // QMap sorts based upon the key, distance, so this handles sorting hits.
715 QMap< double , RayHitInformation > hitsMap;
716 for (int i = 0 ; i < hitCount ; i++) {
717 RayHitInformation hitInfo = m_targetShape->getHitInformation( ray, i );
718 hitsMap.insert( LinearAlgebra::magnitude(observer - hitInfo.intersection), hitInfo );
719 }
720
721 // Return sorted vector of hits
722 return ( QVector< RayHitInformation >::fromList( hitsMap.values() ) );
723 }
724
725
735 return m_tolerance;
736 }
737
738
747 void EmbreeShapeModel::setTolerance(const double &tolerance) {
748 m_tolerance = tolerance;
749 }
750}; // namespace Isis
Distance measurement, usually in meters.
Definition Distance.h:34
@ Kilometers
The distance is being specified in kilometers.
Definition Distance.h:45
EmbreeTargetManager * m_targetManager
!< The target body and Embree objects for intersection.
QVector< double > ellipsoidNormal()
Compute the true surface normal vector of an ellipsoid.
virtual bool isVisibleFrom(const std::vector< double > observerPos, const std::vector< double > lookDirection)
Check if the current internalized surface point is visible from an observer position and look directi...
RTCMultiHitRay pointToRay(const SurfacePoint &point) const
Given a surface point, create a ray that goes from the origin of the target to the surface point.
virtual bool intersectSurface(std::vector< double > observerPos, std::vector< double > lookDirection)
This method computes an intercept point given an observer location and look direction using the Embre...
virtual Distance localRadius(const Latitude &lat, const Longitude &lon)
Determine radius at a given lat/lon grid point.
virtual void calculateDefaultNormal()
Return the surface normal of the ellipsoid as the default.
virtual void clearSurfacePoint()
Flag that the ShapeModel does not have a surface point or normal.
void setTolerance(const double &tolerance)
Set the tolerance used when checking if the stored surface point is visible.
QString m_shapeFile
!< Tolerance for checking visibility.
virtual void calculateSurfaceNormal()
Return the surface normal of the ellipsoid.
RTCMultiHitRay latlonToRay(const Latitude &lat, const Longitude &lon) const
Given a latitude and longitude, create a ray that goes from the origin of the target through that lat...
virtual ~EmbreeShapeModel()
Destructor that notifies the target shape manager that the target shape is no longer in use.
void updateIntersection(const RayHitInformation hitInfo)
Update the ShapeModel given an intersection and normal.
double getTolerance() const
Get the tolerance used when checking if the stored surface point is visible.
double m_tolerance
!< This manages EmbreeTargetShapes to allow for sharing of them between EmbreeShapeModels and deletes...
virtual double incidenceAngle(const std::vector< double > &uB)
Computes and returns incidence angle, in degrees, given the illuminator position.
virtual bool isDEM() const
Indicates that this shape model is not from a DEM.
QVector< RayHitInformation > sortHits(RTCMultiHitRay &ray, LinearAlgebra::Vector &observer)
Sort all intersections by a ray based on distance to a point.
virtual void calculateLocalNormal(QVector< double * > cornerNeighborPoints)
Compute the normal for a local region of surface points.
EmbreeShapeModel()
Default constructor sets type to a TIN.
Class for managing the construction and destruction of EmbreeTargetShapes.
EmbreeTargetShape * create(const QString &shapeFile)
Get a pointer to an EmbreeTargetShape containing the information from a shape file.
void free(const QString &shapeFile)
Notify the manager that an EmbreeTargetShape is no longer in use.
bool isOccluded(RTCOcclusionRay &ray)
Check if a ray intersects the target body.
RayHitInformation getHitInformation(RTCMultiHitRay &ray, int hitIndex)
Extract the intersection point and unit surface normal from an RTCMultiHitRay that has been intersect...
void intersectRay(RTCMultiHitRay &ray)
Intersect a ray with the target shape.
double maximumSceneDistance() const
Return the maximum distance within the scene.
Isis exception class.
Definition IException.h:91
@ User
A type of error that could only have occurred due to a mistake on the user's part (e....
Definition IException.h:126
@ Programmer
This error is for when a programmer made an API call that was illegal.
Definition IException.h:146
This class is designed to encapsulate the concept of a Latitude.
Definition Latitude.h:51
static Vector vector(double v0, double v1, double v2)
Constructs a 3 dimensional vector with the given component values.
static double magnitude(const Vector &vector)
Computes the magnitude (i.e., the length) of the given vector using the Euclidean norm (L2 norm).
boost::numeric::ublas::vector< double > Vector
Definition for an Isis::LinearAlgebra::Vector of doubles.
static Vector normalize(const Vector &vector)
Returns a unit vector that is codirectional with the given vector by dividing each component of the v...
This class is designed to encapsulate the concept of a Longitude.
Definition Longitude.h:40
static void CheckErrors(bool resetNaif=true)
This method looks for any naif errors that might have occurred.
bool hasKeyword(const QString &name) const
Check to see if a keyword exists.
Contains multiple PvlContainers.
Definition PvlGroup.h:41
Container for cube-like labels.
Definition Pvl.h:119
@ Traverse
Search child objects.
Definition PvlObject.h:158
Define shapes and provide utilities for Isis targets.
Definition ShapeModel.h:66
virtual void clearSurfacePoint()
Clears or resets the current surface point.
void setHasNormal(bool status)
Sets the flag to indicate whether this ShapeModel has a surface normal.
bool hasIntersection()
Returns intersection status.
void setHasIntersection(bool b)
Sets the flag to indicate whether this ShapeModel has an intersection.
void setNormal(const std::vector< double >)
Sets the surface normal for the currect intersection point.
virtual SurfacePoint * surfaceIntersection() const
Returns the surface intersection for this ShapeModel.
bool hasValidTarget() const
Returns the status of the target.
virtual double incidenceAngle(const std::vector< double > &uB)
Computes and returns incidence angle, in degrees, given the illuminator position.
virtual void setSurfacePoint(const SurfacePoint &surfacePoint)
Set surface intersection point.
virtual std::vector< double > normal()
Returns the surface normal at the current intersection point.
virtual std::vector< double > localNormal()
Returns the local surface normal at the current intersection point.
std::vector< Distance > targetRadii() const
Returns the radii of the body in km.
void setHasLocalNormal(bool status)
Sets the flag to indicate whether this ShapeModel has a local normal.
bool hasNormal() const
Returns surface point normal status.
void setName(QString name)
Sets the shape name.
This class defines a body-fixed surface point.
This class is used to create and store valid Isis targets.
Definition Target.h:63
This is free and unencumbered software released into the public domain.
Definition Calculator.h:18
This is free and unencumbered software released into the public domain.
Definition Apollo.h:16
Namespace for the standard library.
Struct for capturing multiple intersections when using embree::rtcintersectscene.
int lastHit
Index of the last hit in the hit containers.
Struct for capturing occluded plates when using embree::rtcintersectscene.
Container that holds the body fixed intersection point and unit surface normal for a hit.