GtePicker.cpp
Go to the documentation of this file.
1 // David Eberly, Geometric Tools, Redmond WA 98052
2 // Copyright (c) 1998-2017
3 // Distributed under the Boost Software License, Version 1.0.
4 // http://www.boost.org/LICENSE_1_0.txt
5 // http://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
6 // File Version: 3.0.0 (2016/06/19)
7 
8 #include <GTEnginePCH.h>
12 #include <Graphics/GtePicker.h>
13 using namespace gte;
14 
16 
17 
19 {
20 }
21 
23  :
24  mMaxDistance(0.0f),
25  mOrigin({ 0.0f, 0.0f, 0.0f, 1.0f }),
26  mDirection({ 0.0f, 0.0f, 0.0f, 0.0f}),
27  mTMin(0.0f),
28  mTMax(0.0f)
29 {
30 }
31 
32 void Picker::SetMaxDistance(float maxDistance)
33 {
34  mMaxDistance = std::max(maxDistance, 0.0f);
35 }
36 
38 {
39  return mMaxDistance;
40 }
41 
42 void Picker::operator()(std::shared_ptr<Spatial> const& scene,
43  Vector4<float> const& origin, Vector4<float> const& direction, float tmin, float tmax)
44 {
45 #if defined(_DEBUG)
46  if (tmin == -std::numeric_limits<float>::max())
47  {
48  LogAssert(tmax == std::numeric_limits<float>::max(), "Invalid inputs.");
49  }
50  else
51  {
52  LogAssert(tmin == 0.0f && tmax > 0.0f, "Invalid inputs.");
53  }
54 #endif
55 
56  mOrigin = origin;
57  mDirection = direction;
58  mTMin = tmin;
59  mTMax = tmax;
60 
61  records.clear();
62  ExecuteRecursive(scene);
63 }
64 
66 {
67  if (records.size() > 0)
68  {
69  auto iter = records.begin(), end = records.end(), candidate = iter;
70  float closest = iter->distanceToLinePoint;
71  for (; iter != end; ++iter)
72  {
73  float tmp = iter->distanceToLinePoint;
74  if (tmp < closest)
75  {
76  closest = tmp;
77  candidate = iter;
78  }
79  }
80  return *candidate;
81  }
82  else
83  {
84  return msInvalid;
85  }
86 }
87 
89 {
90  if (records.size() > 0)
91  {
92  // Get first nonnegative value.
93  auto iter = records.begin(), end = records.end(), candidate = iter;
94  float closest = std::numeric_limits<float>::max();
95  for (; iter != end; ++iter)
96  {
97  if (iter->t >= 0.0f)
98  {
99  closest = iter->distanceToLinePoint;
100  candidate = iter;
101  break;
102  }
103  }
104 
105  if (iter != end)
106  {
107  for (++iter; iter != end; ++iter)
108  {
109  if (iter->t >= 0.0f && iter->distanceToLinePoint < closest)
110  {
111  closest = iter->distanceToLinePoint;
112  candidate = iter;
113  }
114  }
115  return *candidate;
116  }
117  else
118  {
119  // All values are negative.
120  return msInvalid;
121  }
122  }
123  else
124  {
125  return msInvalid;
126  }
127 }
128 
130 {
131  if (records.size() > 0)
132  {
133  // Get first nonpositive value.
134  auto iter = records.begin(), end = records.end(), candidate = iter;
135  float closest = std::numeric_limits<float>::max();
136  for (; iter != end; ++iter)
137  {
138  if (iter->t <= 0.0f)
139  {
140  closest = iter->distanceToLinePoint;
141  candidate = iter;
142  break;
143  }
144  }
145 
146  if (iter != end)
147  {
148  for (++iter; iter != end; ++iter)
149  {
150  if (iter->t <= 0.0f && closest < iter->distanceToLinePoint)
151  {
152  closest = iter->distanceToLinePoint;
153  candidate = iter;
154  }
155  }
156  return *candidate;
157  }
158  else
159  {
160  // All values are positive.
161  return msInvalid;
162  }
163  }
164  else
165  {
166  return msInvalid;
167  }
168 }
169 
170 void Picker::ExecuteRecursive(std::shared_ptr<Spatial> const& object)
171 {
172  auto visual = std::dynamic_pointer_cast<Visual>(object);
173  if (visual)
174  {
175  if (visual->worldBound.TestIntersection(mOrigin, mDirection, mTMin, mTMax))
176  {
177  // Convert the linear component to model-space coordinates.
178  Matrix4x4<float> const& invWorldMatrix = visual->worldTransform.GetHInverse();
179  Line3<float> line;
180  Vector4<float> temp;
181 #if defined (GTE_USE_MAT_VEC)
182  temp = invWorldMatrix * mOrigin;
183  line.origin = { temp[0], temp[1], temp[2] };
184  temp = invWorldMatrix * mDirection;
185  line.direction = { temp[0], temp[1], temp[2] };
186 #else
187  temp = mOrigin * invWorldMatrix;
188  line.origin = { temp[0], temp[1], temp[2] };
189  temp = mDirection * invWorldMatrix;
190  line.direction = { temp[0], temp[1], temp[2] };
191 #endif
192  // The world transformation might have non-unit scales, in which case the
193  // model-space line direction is not unit length.
194  Normalize(line.direction);
195 
196  // Get the position data.
197  VertexBuffer* vbuffer = visual->GetVertexBuffer().get();
198  std::set<DFType> required;
199  required.insert(DF_R32G32B32_FLOAT);
200  required.insert(DF_R32G32B32A32_FLOAT);
201  char const* positions = vbuffer->GetChannel(VA_POSITION, 0, required);
202  if (!positions)
203  {
204  LogInformation("Expecting 3D positions.");
205  return;
206  }
207 
208  // The picking algorithm depends on the primitive type.
209  unsigned int vstride = vbuffer->GetElementSize();
210  IndexBuffer* ibuffer = visual->GetIndexBuffer().get();
211  IPType primitiveType = ibuffer->GetPrimitiveType();
212  if (primitiveType & IP_HAS_TRIANGLES)
213  {
214  PickTriangles(visual, positions, vstride, ibuffer, line);
215  }
216  else if (primitiveType & IP_HAS_SEGMENTS)
217  {
218  PickSegments(visual, positions, vstride, ibuffer, line);
219  }
220  else if (primitiveType & IP_HAS_POINTS)
221  {
222  PickPoints(visual, positions, vstride, ibuffer, line);
223  }
224  }
225  return;
226  }
227 
228  auto node = std::dynamic_pointer_cast<Node>(object);
229  if (node)
230  {
231  if (node->worldBound.TestIntersection(mOrigin, mDirection, mTMin, mTMax))
232  {
233  int const numChildren = node->GetNumChildren();
234  for (int i = 0; i < numChildren; ++i)
235  {
236  std::shared_ptr<Spatial> child = node->GetChild(i);
237  if (child)
238  {
239  ExecuteRecursive(child);
240  }
241  }
242  }
243  return;
244  }
245 
246  // We should not get here when the scene graph has only Spatial, Node,
247  // and Visual. However, in case someone adds a new Spatial-derived
248  // type later, let's trap it.
249  LogWarning("Invalid object type.");
250 }
251 
252 void Picker::PickTriangles(std::shared_ptr<Visual> const& visual, char const* positions,
253  unsigned int vstride, IndexBuffer* ibuffer, Line3<float> const& line)
254 {
255  // Compute intersections with the model-space triangles.
256  unsigned int const numTriangles = ibuffer->GetNumPrimitives();
257  bool isIndexed = ibuffer->IsIndexed();
258  IPType primitiveType = ibuffer->GetPrimitiveType();
259  for (unsigned int i = 0; i < numTriangles; ++i)
260  {
261  // Get the vertex indices for the triangle.
262  unsigned int v0, v1, v2;
263  if (isIndexed)
264  {
265  ibuffer->GetTriangle(i, v0, v1, v2);
266  }
267  else if (primitiveType == IP_TRIMESH)
268  {
269  v0 = 3 * i;
270  v1 = v0 + 1;
271  v2 = v0 + 2;
272  }
273  else // primitiveType == IP_TRISTRIP
274  {
275  int offset = (i & 1);
276  v0 = i + offset;
277  v1 = i + 1 + offset;
278  v2 = i + 2 - offset;
279  }
280 
281  // Get the vertex positions.
282  Vector3<float> const& p0 = *(Vector3<float> const*)(positions + v0 * vstride);
283  Vector3<float> const& p1 = *(Vector3<float> const*)(positions + v1 * vstride);
284  Vector3<float> const& p2 = *(Vector3<float> const*)(positions + v2 * vstride);
285 
286  // Create the query triangle in model space.
287  Triangle3<float> triangle(p0, p1, p2);
288 
289  // Compute line-triangle intersection.
291  auto result = query(line, triangle);
292  if (result.intersect && mTMin <= result.parameter && result.parameter <= mTMax)
293  {
294  PickRecord record;
295  record.visual = visual;
296  record.primitiveType = primitiveType;
297  record.primitiveIndex = i;
298  record.vertexIndex[0] = static_cast<int>(v0);
299  record.vertexIndex[1] = static_cast<int>(v1);
300  record.vertexIndex[2] = static_cast<int>(v2);
301  record.t = result.parameter;
302  record.bary[0] = result.triangleBary[0];
303  record.bary[1] = result.triangleBary[1];
304  record.bary[2] = result.triangleBary[2];
305  record.linePoint = HLift(result.point, 1.0f);
306 
307 #if defined (GTE_USE_MAT_VEC)
308  record.linePoint = visual->worldTransform * record.linePoint;
309 #else
310  record.linePoint = record.linePoint * visual->worldTransform;
311 #endif
312  record.primitivePoint = record.linePoint;
313 
314  record.distanceToLinePoint =
315  Length(record.linePoint - mOrigin);
316  record.distanceToPrimitivePoint =
317  Length(record.primitivePoint - mOrigin);
319  Length(record.linePoint - record.primitivePoint);
320 
321  records.push_back(record);
322  }
323  }
324 }
325 
326 void Picker::PickSegments(std::shared_ptr<Visual> const& visual, char const* positions,
327  unsigned int vstride, IndexBuffer* ibuffer, Line3<float> const& line)
328 {
329  // Compute distances from the model-space segments to the line.
330  unsigned int const numSegments = ibuffer->GetNumPrimitives();
331  bool isIndexed = ibuffer->IsIndexed();
332  IPType primitiveType = ibuffer->GetPrimitiveType();
333  for (unsigned int i = 0; i < numSegments; ++i)
334  {
335  // Get the vertex indices for the segment.
336  unsigned int v0, v1;
337  if (isIndexed)
338  {
339  ibuffer->GetSegment(i, v0, v1);
340  }
341  else if (primitiveType == IP_POLYSEGMENT_DISJOINT)
342  {
343  v0 = 2 * i;
344  v1 = v0 + 1;
345  }
346  else // primitiveType == IP_POLYSEGMENT_CONTIGUOUS
347  {
348  v0 = i;
349  v1 = v0 + 1;
350  }
351 
352  // Get the vertex positions.
353  Vector3<float> const& p0 = *(Vector3<float> const*)(positions + v0 * vstride);
354  Vector3<float> const& p1 = *(Vector3<float> const*)(positions + v1 * vstride);
355 
356  // Create the query segment in model space.
357  Segment3<float> segment(p0, p1);
358 
359  // Compute segment-line distance.
361  auto result = query(line, segment);
362  if (result.distance <= mMaxDistance && mTMin <= result.parameter[0] && result.parameter[0] <= mTMax)
363  {
364  PickRecord record;
365  record.visual = visual;
366  record.primitiveType = primitiveType;
367  record.primitiveIndex = i;
368  record.vertexIndex[0] = static_cast<int>(v0);
369  record.vertexIndex[1] = static_cast<int>(v1);
370  record.vertexIndex[2] = -1;
371  record.t = result.parameter[0];
372  record.bary[0] = 1.0f - result.parameter[1];
373  record.bary[1] = result.parameter[1];
374  record.bary[2] = 0.0f;
375  record.linePoint = HLift(result.closestPoint[0], 1.0f);
376  record.primitivePoint = HLift(result.closestPoint[1], 1.0f);
377 
378 #if defined (GTE_USE_MAT_VEC)
379  record.linePoint = visual->worldTransform * record.linePoint;
380  record.primitivePoint = visual->worldTransform * record.primitivePoint;
381 #else
382  record.linePoint = record.linePoint * visual->worldTransform;
383  record.primitivePoint = record.primitivePoint * visual->worldTransform;
384 #endif
385  record.distanceToLinePoint =
386  Length(record.linePoint - mOrigin);
387  record.distanceToPrimitivePoint =
388  Length(record.primitivePoint - mOrigin);
390  Length(record.linePoint - record.primitivePoint);
391 
392  records.push_back(record);
393  }
394  }
395 }
396 
397 void Picker::PickPoints(std::shared_ptr<Visual> const& visual, char const* positions,
398  unsigned int vstride, IndexBuffer* ibuffer, Line3<float> const& line)
399 {
400  // Compute distances from the model-space points to the line.
401  unsigned int const numPoints = ibuffer->GetNumPrimitives();
402  bool isIndexed = ibuffer->IsIndexed();
403  for (unsigned int i = 0; i < numPoints; ++i)
404  {
405  // Get the vertex index for the point.
406  unsigned int v;
407  if (isIndexed)
408  {
409  ibuffer->GetPoint(i, v);
410  }
411  else
412  {
413  v = i;
414  }
415 
416  // Get the vertex position.
417  Vector3<float> const& p = *(Vector3<float> const*)(positions + v * vstride);
418 
419  // Compute point-line distance.
421  auto result = query(p, line);
422  if (result.distance <= mMaxDistance && mTMin <= result.lineParameter && result.lineParameter <= mTMax)
423  {
424  PickRecord record;
425  record.visual = visual;
426  record.primitiveType = IP_POLYPOINT;
427  record.primitiveIndex = i;
428  record.vertexIndex[0] = static_cast<int>(v);
429  record.vertexIndex[1] = -1;
430  record.vertexIndex[2] = -1;
431  record.t = result.lineParameter;
432  record.bary[0] = 1.0f;
433  record.bary[1] = 0.0f;
434  record.bary[2] = 0.0f;
435  record.linePoint = HLift(result.lineClosest, 1.0f);
436  record.primitivePoint = HLift(p, 1.0f);
437 
438 #if defined (GTE_USE_MAT_VEC)
439  record.linePoint = visual->worldTransform * record.linePoint;
440  record.primitivePoint = visual->worldTransform * record.primitivePoint;
441 #else
442  record.linePoint = record.linePoint * visual->worldTransform;
443  record.primitivePoint = record.primitivePoint * visual->worldTransform;
444 #endif
445  record.distanceToLinePoint =
446  Length(record.linePoint - mOrigin);
447  record.distanceToPrimitivePoint =
448  Length(record.primitivePoint - mOrigin);
450  Length(record.linePoint - record.primitivePoint);
451 
452  records.push_back(record);
453  }
454  }
455 }
456 
float mMaxDistance
Definition: GtePicker.h:75
void operator()(std::shared_ptr< Spatial > const &scene, Vector4< float > const &origin, Vector4< float > const &direction, float tmin, float tmax)
Definition: GtePicker.cpp:42
IPType primitiveType
Definition: GtePickRecord.h:35
#define LogAssert(condition, message)
Definition: GteLogger.h:86
IP_TRIMESH
int GetNumChildren() const
Definition: GteNode.cpp:29
uint32_t GetNumPrimitives() const
#define LogInformation(message)
Definition: GteLogger.h:98
GLfloat GLfloat v1
Definition: glcorearb.h:812
bool GetSegment(uint32_t i, uint32_t &v0, uint32_t &v1) const
Vector< N, Real > direction
Definition: GteLine.h:29
float distanceToPrimitivePoint
Definition: GtePickRecord.h:71
void PickSegments(std::shared_ptr< Visual > const &visual, char const *positions, unsigned int vstride, IndexBuffer *ibuffer, Line3< float > const &line)
Definition: GtePicker.cpp:326
Vector4< float > mOrigin
Definition: GtePicker.h:78
float GetMaxDistance() const
Definition: GtePicker.cpp:37
unsigned int GetElementSize() const
Definition: GteResource.h:111
GVector< Real > HLift(GVector< Real > const &v, Real last)
Definition: GteGVector.h:574
IP_HAS_POINTS
VA_POSITION
static PickRecord const msInvalid
Definition: GtePicker.h:84
PickRecord const & GetClosestToZero() const
Definition: GtePicker.cpp:65
GLfixed GLfixed GLint GLint GLfixed GLfixed GLint vstride
Definition: glext.h:4928
GLuint GLuint end
Definition: glcorearb.h:470
void PickPoints(std::shared_ptr< Visual > const &visual, char const *positions, unsigned int vstride, IndexBuffer *ibuffer, Line3< float > const &line)
Definition: GtePicker.cpp:397
Vector4< float > primitivePoint
Definition: GtePickRecord.h:63
DF_R32G32B32A32_FLOAT
Definition: GteDataFormat.h:20
IP_POLYSEGMENT_DISJOINT
std::vector< PickRecord > records
Definition: GtePicker.h:58
#define LogWarning(message)
Definition: GteLogger.h:95
IPType GetPrimitiveType() const
Real Normalize(GVector< Real > &v, bool robust=false)
Definition: GteGVector.h:454
GLfloat v0
Definition: glcorearb.h:811
float mTMax
Definition: GtePicker.h:80
bool GetPoint(uint32_t i, uint32_t &v) const
void SetMaxDistance(float maxDistance)
Definition: GtePicker.cpp:32
std::shared_ptr< Visual > visual
Definition: GtePickRecord.h:34
IP_POLYPOINT
bool IsIndexed() const
IP_HAS_SEGMENTS
float mTMin
Definition: GtePicker.h:80
DualQuaternion< Real > Length(DualQuaternion< Real > const &d, bool robust=false)
const GLdouble * v
Definition: glcorearb.h:832
void PickTriangles(std::shared_ptr< Visual > const &visual, char const *positions, unsigned int vstride, IndexBuffer *ibuffer, Line3< float > const &line)
Definition: GtePicker.cpp:252
GLfloat GLfloat GLfloat v2
Definition: glcorearb.h:813
GLsizei GLsizei numSegments
Definition: glext.h:9703
IP_HAS_TRIANGLES
GLfloat f
Definition: glcorearb.h:1921
float distanceBetweenLinePrimitive
Definition: GtePickRecord.h:72
PickRecord const & GetClosestNonnegative() const
Definition: GtePicker.cpp:88
Vector< N, Real > origin
Definition: GteLine.h:29
Vector4< float > linePoint
Definition: GtePickRecord.h:49
GLintptr offset
Definition: glcorearb.h:660
GLuint object
Definition: glext.h:6426
bool GetTriangle(uint32_t i, uint32_t &v0, uint32_t &v1, uint32_t &v2) const
void ExecuteRecursive(std::shared_ptr< Spatial > const &object)
Definition: GtePicker.cpp:170
PickRecord const & GetClosestNonpositive() const
Definition: GtePicker.cpp:129
DF_R32G32B32_FLOAT
Definition: GteDataFormat.h:20
GLfloat GLfloat p
Definition: glext.h:11668
GLuint64EXT * result
Definition: glext.h:10003
GLenum query
Definition: glext.h:2716
float distanceToLinePoint
Definition: GtePickRecord.h:70
Vector4< float > mDirection
Definition: GtePicker.h:79
char * GetChannel(VASemantic semantic, unsigned int unit, std::set< DFType > const &requiredTypes)


geometric_tools_engine
Author(s): Yijiang Huang
autogenerated on Thu Jul 18 2019 04:00:01