KiCad PCB EDA Suite
layer_item_3d.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2015-2016 Mario Luzeiro <[email protected]>
5  * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
25 #include "layer_item_3d.h"
26 #include "3d_fastmath.h"
27 #include <wx/debug.h>
28 
29 
30 LAYER_ITEM::LAYER_ITEM( const OBJECT_2D* aObject2D, float aZMin, float aZMax ) :
32  m_object2d( aObject2D )
33 {
34  wxASSERT( aObject2D );
35 
36  BBOX_2D bbox2d = m_object2d->GetBBox();
37  bbox2d.ScaleNextUp();
38  bbox2d.ScaleNextUp();
39 
40  m_bbox.Reset();
41  m_bbox.Set( SFVEC3F( bbox2d.Min().x, bbox2d.Min().y, aZMin ),
42  SFVEC3F( bbox2d.Max().x, bbox2d.Max().y, aZMax ) );
44  m_bbox.Scale( 1.0001f );
45 
46  m_centroid = SFVEC3F( aObject2D->GetCentroid().x, aObject2D->GetCentroid().y,
47  ( aZMax + aZMin ) * 0.5f );
48 }
49 
50 
51 bool LAYER_ITEM::Intersect( const RAY& aRay, HITINFO& aHitInfo ) const
52 {
53  float tBBoxStart;
54  float tBBoxEnd;
55 
56  if( !m_bbox.Intersect( aRay, &tBBoxStart, &tBBoxEnd ) )
57  return false;
58 
59  if( tBBoxStart >= aHitInfo.m_tHit )
60  return false;
61 
62  if( fabs( tBBoxStart - tBBoxEnd ) <= FLT_EPSILON )
63  return false;
64 
65  const bool startedInside = m_bbox.Inside( aRay.m_Origin );
66 
67  if( !startedInside )
68  {
69  float tTop = FLT_MAX;
70  float tBot = FLT_MAX;
71  bool hit_top = false;
72  bool hit_bot = false;
73 
74  if( (float) fabs( aRay.m_Dir.z ) > FLT_EPSILON )
75  {
76  tBot = ( m_bbox.Min().z - aRay.m_Origin.z ) * aRay.m_InvDir.z;
77  tTop = ( m_bbox.Max().z - aRay.m_Origin.z ) * aRay.m_InvDir.z;
78 
79  float tBBoxStartAdjusted = NextFloatUp( tBBoxStart );
80 
81  if( tBot > FLT_EPSILON )
82  {
83  hit_bot = tBot <= tBBoxStartAdjusted;
84  tBot = NextFloatDown( tBot );
85  }
86 
87  if( tTop > FLT_EPSILON )
88  {
89  hit_top = tTop <= tBBoxStartAdjusted;
90  tTop = NextFloatDown( tTop );
91  }
92  }
93 
94  SFVEC2F topHitPoint2d;
95  SFVEC2F botHitPoint2d;
96 
97  if( hit_top )
98  topHitPoint2d = SFVEC2F( aRay.m_Origin.x + aRay.m_Dir.x * tTop,
99  aRay.m_Origin.y + aRay.m_Dir.y * tTop );
100 
101  if( hit_bot )
102  botHitPoint2d = SFVEC2F( aRay.m_Origin.x + aRay.m_Dir.x * tBot,
103  aRay.m_Origin.y + aRay.m_Dir.y * tBot );
104 
105  if( hit_top && hit_bot )
106  {
107  if( tBot < tTop )
108  {
109  if( m_object2d->IsPointInside( botHitPoint2d ) )
110  {
111  if( tBot < aHitInfo.m_tHit )
112  {
113  aHitInfo.m_tHit = tBot;
114  aHitInfo.m_HitPoint = aRay.at( tBot );
115  aHitInfo.m_HitNormal = SFVEC3F( 0.0f, 0.0f, -1.0f );
116  aHitInfo.pHitObject = this;
117 
118  m_material->Generate( aHitInfo.m_HitNormal, aRay, aHitInfo );
119 
120  return true;
121  }
122 
123  return false;
124  }
125  }
126  else
127  {
128  if( m_object2d->IsPointInside( topHitPoint2d ) )
129  {
130  if( tTop < aHitInfo.m_tHit )
131  {
132  aHitInfo.m_tHit = tTop;
133  aHitInfo.m_HitPoint = aRay.at( tTop );
134  aHitInfo.m_HitNormal = SFVEC3F( 0.0f, 0.0f, 1.0f );
135  aHitInfo.pHitObject = this;
136 
137  m_material->Generate( aHitInfo.m_HitNormal, aRay, aHitInfo );
138 
139  return true;
140  }
141 
142  return false;
143  }
144  }
145  }
146  else
147  {
148  if( hit_top )
149  {
150  if( tTop < tBot )
151  {
152  if( m_object2d->IsPointInside( topHitPoint2d ) )
153  {
154  if( tTop < aHitInfo.m_tHit )
155  {
156  aHitInfo.m_tHit = tTop;
157  aHitInfo.m_HitPoint = aRay.at( tTop );
158  aHitInfo.m_HitNormal = SFVEC3F( 0.0f, 0.0f, 1.0f );
159  aHitInfo.pHitObject = this;
160 
161  m_material->Generate( aHitInfo.m_HitNormal, aRay, aHitInfo );
162 
163  return true;
164  }
165 
166  return false;
167  }
168  }
169  }
170  else
171  {
172  if( hit_bot )
173  {
174  if( tBot < tTop )
175  {
176  if( m_object2d->IsPointInside( botHitPoint2d ) )
177  {
178  if( tBot < aHitInfo.m_tHit )
179  {
180  aHitInfo.m_tHit = tBot;
181  aHitInfo.m_HitPoint = aRay.at( tBot );
182  aHitInfo.m_HitNormal = SFVEC3F( 0.0f, 0.0f, -1.0f );
183  aHitInfo.pHitObject = this;
184 
185  m_material->Generate( aHitInfo.m_HitNormal, aRay, aHitInfo );
186 
187  return true;
188  }
189 
190  return false;
191  }
192  }
193  }
194  else
195  {
196  // At this point, the ray miss the two planes but it still
197  // hits the box. It means that the rays are "(almost)parallel"
198  // to the planes, so must calc the intersection
199  }
200  }
201  }
202 
203  SFVEC3F boxHitPointStart = aRay.at( tBBoxStart );
204  SFVEC3F boxHitPointEnd = aRay.at( tBBoxEnd );
205 
206  SFVEC2F boxHitPointStart2D( boxHitPointStart.x, boxHitPointStart.y );
207  SFVEC2F boxHitPointEnd2D( boxHitPointEnd.x, boxHitPointEnd.y );
208 
209  float tOut;
210  SFVEC2F outNormal;
211  RAYSEG2D raySeg( boxHitPointStart2D, boxHitPointEnd2D );
212 
213  if( m_object2d->Intersect( raySeg, &tOut, &outNormal ) )
214  {
215  // The hitT is a hit value for the segment length 'start' - 'end',
216  // so it ranges from 0.0 - 1.0. We now convert it to a 3D hit position
217  // and calculate the real hitT of the ray.
218  SFVEC3F hitPoint = boxHitPointStart + ( boxHitPointEnd - boxHitPointStart ) * tOut;
219 
220  const float t = glm::length( hitPoint - aRay.m_Origin );
221 
222  if( t < aHitInfo.m_tHit )
223  {
224  aHitInfo.m_tHit = t;
225  aHitInfo.m_HitPoint = hitPoint;
226 
227  if( ( outNormal.x == 0.0f ) && ( outNormal.y == 0.0f ) )
228  {
229  aHitInfo.m_HitNormal = SFVEC3F( 0.0f, 0.0f, 1.0f );
230  }
231  else
232  {
233  aHitInfo.m_HitNormal = SFVEC3F( outNormal.x, outNormal.y, 0.0f );
234  }
235 
236  aHitInfo.pHitObject = this;
237 
238  m_material->Generate( aHitInfo.m_HitNormal, aRay, aHitInfo );
239 
240  return true;
241  }
242  }
243 
244  return false;
245  }
246  else
247  {
249  // Disabled due to refraction artifacts
250  // this will mostly happen inside the board body
251 #if 0
252  // Started inside
253  const SFVEC3F boxHitPointStart = aRay.at( tBBoxStart );
254  const SFVEC3F boxHitPointEnd = aRay.at( tBBoxEnd );
255 
256  const SFVEC2F boxHitPointStart2D( boxHitPointStart.x, boxHitPointStart.y );
257 
258  const SFVEC2F boxHitPointEnd2D( boxHitPointEnd.x, boxHitPointEnd.y );
259 
260  if( !( m_object2d->IsPointInside( boxHitPointStart2D ) &&
261  m_object2d->IsPointInside( boxHitPointEnd2D ) ) )
262  return false;
263 
264  float tOut;
265  SFVEC2F outNormal;
266  RAYSEG2D raySeg( boxHitPointStart2D, boxHitPointEnd2D );
267 
268  if( ( m_object2d->IsPointInside( boxHitPointStart2D ) &&
269  m_object2d->IsPointInside( boxHitPointEnd2D ) ) )
270  {
271  if( tBBoxEnd < aHitInfo.m_tHit )
272  {
273  aHitInfo.m_tHit = tBBoxEnd;
274  aHitInfo.m_HitPoint = aRay.at( tBBoxEnd );
275  aHitInfo.pHitObject = this;
276 
277  if( aRay.m_Dir.z > 0.0f )
278  aHitInfo.m_HitNormal = SFVEC3F( 0.0f, 0.0f, -1.0f );
279  else
280  aHitInfo.m_HitNormal = SFVEC3F( 0.0f, 0.0f, 1.0f );
281 
282  m_material->Generate( aHitInfo.m_HitNormal, aRay, aHitInfo );
283 
284  return true;
285  }
286  }
287  else
288  {
289  if( m_object2d->Intersect( raySeg, &tOut, &outNormal ) )
290  {
291  // The hitT is a hit value for the segment length 'start' - 'end',
292  // so it ranges from 0.0 - 1.0. We now convert it to a 3D hit position
293  // and calculate the real hitT of the ray.
294  const SFVEC3F hitPoint = boxHitPointStart +
295  ( boxHitPointEnd - boxHitPointStart ) * tOut;
296 
297  const float t = glm::length( hitPoint - aRay.m_Origin );
298 
299  if( t < aHitInfo.m_tHit )
300  {
301  aHitInfo.m_tHit = t;
302  aHitInfo.m_HitPoint = hitPoint;
303  aHitInfo.m_HitNormal = SFVEC3F( outNormal.x, outNormal.y, 0.0f );
304  aHitInfo.pHitObject = this;
305 
306  m_material->Generate( aHitInfo.m_HitNormal, aRay, aHitInfo );
307 
308  return true;
309  }
310  }
311  }
312 #endif
313  }
314 
315  return false;
316 }
317 
318 
319 bool LAYER_ITEM::IntersectP( const RAY& aRay, float aMaxDistance ) const
320 {
321  float tBBoxStart;
322  float tBBoxEnd;
323 
324  if( !m_bbox.Intersect( aRay, &tBBoxStart, &tBBoxEnd ) )
325  return false;
326 
327  if( ( tBBoxStart > aMaxDistance ) || ( fabs( tBBoxStart - tBBoxEnd ) < FLT_EPSILON ) )
328  return false;
329 
330  float tTop = FLT_MAX;
331  float tBot = FLT_MAX;
332  bool hit_top = false;
333  bool hit_bot = false;
334 
335  if( (float)fabs( aRay.m_Dir.z ) > FLT_EPSILON )
336  {
337  tBot = ( m_bbox.Min().z - aRay.m_Origin.z ) * aRay.m_InvDir.z;
338  tTop = ( m_bbox.Max().z - aRay.m_Origin.z ) * aRay.m_InvDir.z;
339 
340  const float tBBoxStartAdjusted = NextFloatUp( tBBoxStart );
341 
342  if( tBot > FLT_EPSILON )
343  {
344  hit_bot = tBot <= tBBoxStartAdjusted;
345  tBot = NextFloatDown( tBot );
346  }
347 
348  if( tTop > FLT_EPSILON )
349  {
350  hit_top = tTop <= tBBoxStartAdjusted;
351  tTop = NextFloatDown( tTop );
352  }
353  }
354 
355  tBBoxStart = NextFloatDown( tBBoxStart );
356  tBBoxEnd = NextFloatUp( tBBoxEnd );
357 
358  SFVEC2F topHitPoint2d;
359  SFVEC2F botHitPoint2d;
360 
361  if( hit_top )
362  topHitPoint2d = SFVEC2F( aRay.m_Origin.x + aRay.m_Dir.x * tTop,
363  aRay.m_Origin.y + aRay.m_Dir.y * tTop );
364 
365  if( hit_bot )
366  botHitPoint2d = SFVEC2F( aRay.m_Origin.x + aRay.m_Dir.x * tBot,
367  aRay.m_Origin.y + aRay.m_Dir.y * tBot );
368 
369  if( hit_top && hit_bot )
370  {
371  if( tBot < tTop )
372  {
373  if( m_object2d->IsPointInside( botHitPoint2d ) )
374  {
375  if( tBot < aMaxDistance )
376  return true;
377 
378  return false;
379  }
380  }
381  else
382  {
383  if( m_object2d->IsPointInside( topHitPoint2d ) )
384  {
385  if( tTop < aMaxDistance )
386  return true;
387 
388  return false;
389  }
390  }
391  }
392  else
393  {
394  if( hit_top )
395  {
396  if( tTop < tBot )
397  {
398  if( m_object2d->IsPointInside( topHitPoint2d ) )
399  {
400  if( tTop < aMaxDistance )
401  return true;
402 
403  return false;
404  }
405  }
406  }
407  else
408  {
409  if( hit_bot )
410  {
411  if( tBot < tTop )
412  {
413  if( m_object2d->IsPointInside( botHitPoint2d ) )
414  {
415  if( tBot < aMaxDistance )
416  return true;
417 
418  return false;
419  }
420  }
421  }
422  else
423  {
424  // At this point, the ray miss the two planes but it still
425  // hits the box. It means that the rays are "(almost)parallel"
426  // to the planes, so must calc the intersection
427  }
428  }
429  }
430 
431  SFVEC3F boxHitPointStart = aRay.at( tBBoxStart );
432  SFVEC3F boxHitPointEnd = aRay.at( tBBoxEnd );
433 
434  SFVEC2F boxHitPointStart2D( boxHitPointStart.x, boxHitPointStart.y );
435 
436  SFVEC2F boxHitPointEnd2D( boxHitPointEnd.x, boxHitPointEnd.y );
437 
438  float tOut;
439  SFVEC2F outNormal;
440  RAYSEG2D raySeg( boxHitPointStart2D, boxHitPointEnd2D );
441 
442  if( m_object2d->Intersect( raySeg, &tOut, &outNormal ) )
443  {
444  //if( (tOut > FLT_EPSILON) && (tOut < 1.0f) )
445  {
446  // The hitT is a hit value for the segment length 'start' - 'end',
447  // so it ranges from 0.0 - 1.0. We now convert it to a 3D hit position
448  // and calculate the real hitT of the ray.
449  const SFVEC3F hitPoint = boxHitPointStart +
450  ( boxHitPointEnd - boxHitPointStart ) * tOut;
451  const float t = glm::length( hitPoint - aRay.m_Origin );
452 
453  if( ( t < aMaxDistance ) && ( t > FLT_EPSILON ) )
454  return true;
455  }
456  }
457 
458  return false;
459 }
460 
461 
462 bool LAYER_ITEM::Intersects( const BBOX_3D& aBBox ) const
463 {
464  if( !m_bbox.Intersects( aBBox ) )
465  return false;
466 
467  const BBOX_2D bbox2D( SFVEC2F( aBBox.Min().x, aBBox.Min().y ),
468  SFVEC2F( aBBox.Max().x, aBBox.Max().y ) );
469 
470  return m_object2d->Intersects( bbox2D );
471 }
472 
473 
474 SFVEC3F LAYER_ITEM::GetDiffuseColor( const HITINFO& /* aHitInfo */ ) const
475 {
476  return m_diffusecolor;
477 }
Defines math related functions.
const SFVEC3F & Max() const
Return the maximum vertex pointer.
Definition: bbox_3d.h:198
const SFVEC2F & GetCentroid() const
Definition: object_2d.h:105
SFVEC3F GetDiffuseColor(const HITINFO &aHitInfo) const override
Manage a bounding box defined by two SFVEC3F min max points.
Definition: bbox_3d.h:41
bool Intersect(const RAY &aRay, float *t) const
Definition: bbox_3d_ray.cpp:46
void Generate(SFVEC3F &aNormal, const RAY &aRay, const HITINFO &aHitInfo) const
Definition: material.cpp:89
Definition: ray.h:62
float m_tHit
( 4) distance
Definition: hitinfo.h:38
SFVEC3F at(float t) const
Definition: ray.h:84
bool Inside(const SFVEC3F &aPoint) const
Check if a point is inside this bounding box.
Definition: bbox_3d.cpp:231
const SFVEC3F & Min() const
Return the minimum vertex pointer.
Definition: bbox_3d.h:191
SFVEC3F m_InvDir
Definition: ray.h:70
SFVEC3F m_HitPoint
(12) hit position
Definition: hitinfo.h:44
const SFVEC2F & Max() const
Definition: bbox_2d.h:172
bool Intersect(const RAY &aRay, HITINFO &aHitInfo) const override
void Scale(float aScale)
Scales a bounding box by its center.
Definition: bbox_3d.cpp:182
glm::vec2 SFVEC2F
Definition: xv3d_types.h:42
float NextFloatDown(float v)
Definition: 3d_fastmath.h:157
BBOX_3D m_bbox
Definition: object_3d.h:97
const BBOX_2D & GetBBox() const
Definition: object_2d.h:103
virtual bool Intersect(const RAYSEG2D &aSegRay, float *aOutT, SFVEC2F *aNormalOut) const =0
Manage a bounding box defined by two SFVEC2F min max points.
Definition: bbox_2d.h:41
const MATERIAL * m_material
Definition: object_3d.h:100
const OBJECT_3D * pHitObject
( 4) Object that was hitted
Definition: hitinfo.h:40
SFVEC3F m_Dir
Definition: ray.h:67
void Set(const SFVEC3F &aPbMin, const SFVEC3F &aPbMax)
Set bounding box with new parameters.
Definition: bbox_3d.cpp:68
SFVEC3F m_Origin
Definition: ray.h:64
void ScaleNextUp()
Scale a bounding box to the next float representation making it larger.
Definition: bbox_3d.cpp:194
const SFVEC2F & Min() const
Definition: bbox_2d.h:167
LAYER_ITEM(const OBJECT_2D *aObject2D, float aZMin, float aZMax)
SFVEC3F m_centroid
Definition: object_3d.h:98
bool Intersects(const BBOX_3D &aBBox) const override
Stores the hit information of a ray with a point on the surface of a object.
Definition: hitinfo.h:35
glm::vec3 SFVEC3F
Definition: xv3d_types.h:44
float NextFloatUp(float v)
Definition: 3d_fastmath.h:136
SFVEC3F m_diffusecolor
Definition: layer_item_3d.h:48
void ScaleNextUp()
Scale a bounding box to the next float representation making it larger.
Definition: bbox_2d.cpp:162
SFVEC3F m_HitNormal
(12) normal at the hit point
Definition: hitinfo.h:37
OBJECT_3D_TYPE
Definition: object_3d.h:38
bool IntersectP(const RAY &aRay, float aMaxDistance) const override
const OBJECT_2D * m_object2d
Definition: layer_item_3d.h:45
virtual bool IsPointInside(const SFVEC2F &aPoint) const =0
virtual bool Intersects(const BBOX_2D &aBBox) const =0
a.Intersects(b) ⇔ !a.Disjoint(b) ⇔ !(a ∩ b = ∅)
void Reset()
Reset the bounding box to zero and de-initialize it.
Definition: bbox_3d.cpp:95
bool Intersects(const BBOX_3D &aBBox) const
Test if a bounding box intersects this box.
Definition: bbox_3d.cpp:218
Definition: ray.h:105