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 <mrluzeiro@ua.pt>
5  * Copyright (C) 1992-2020 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 
29 #include "layer_item_3d.h"
30 #include "3d_fastmath.h"
31 #include <wx/debug.h>
32 
33 
34 LAYER_ITEM::LAYER_ITEM( const OBJECT_2D* aObject2D, float aZMin, float aZMax ) :
36  m_object2d( aObject2D )
37 {
38  wxASSERT( aObject2D );
39 
40  BBOX_2D bbox2d = m_object2d->GetBBox();
41  bbox2d.ScaleNextUp();
42  bbox2d.ScaleNextUp();
43 
44  m_bbox.Reset();
45  m_bbox.Set( SFVEC3F( bbox2d.Min().x, bbox2d.Min().y, aZMin ),
46  SFVEC3F( bbox2d.Max().x, bbox2d.Max().y, aZMax ) );
48  m_bbox.Scale( 1.0001f );
49 
50  m_centroid = SFVEC3F( aObject2D->GetCentroid().x, aObject2D->GetCentroid().y,
51  ( aZMax + aZMin ) * 0.5f );
52 }
53 
54 
55 bool LAYER_ITEM::Intersect( const RAY& aRay, HITINFO& aHitInfo ) const
56 {
57  float tBBoxStart;
58  float tBBoxEnd;
59 
60  if( !m_bbox.Intersect( aRay, &tBBoxStart, &tBBoxEnd ) )
61  return false;
62 
63  if( tBBoxStart >= aHitInfo.m_tHit )
64  return false;
65 
66  if( fabs( tBBoxStart - tBBoxEnd ) <= FLT_EPSILON )
67  return false;
68 
69  const bool startedInside = m_bbox.Inside( aRay.m_Origin );
70 
71  if( !startedInside )
72  {
73  float tTop = FLT_MAX;
74  float tBot = FLT_MAX;
75  bool hit_top = false;
76  bool hit_bot = false;
77 
78  if( (float) fabs( aRay.m_Dir.z ) > FLT_EPSILON )
79  {
80  tBot = ( m_bbox.Min().z - aRay.m_Origin.z ) * aRay.m_InvDir.z;
81  tTop = ( m_bbox.Max().z - aRay.m_Origin.z ) * aRay.m_InvDir.z;
82 
83  float tBBoxStartAdjusted = NextFloatUp( tBBoxStart );
84 
85  if( tBot > FLT_EPSILON )
86  {
87  hit_bot = tBot <= tBBoxStartAdjusted;
88  tBot = NextFloatDown( tBot );
89  }
90 
91  if( tTop > FLT_EPSILON )
92  {
93  hit_top = tTop <= tBBoxStartAdjusted;
94  tTop = NextFloatDown( tTop );
95  }
96  }
97 
98  SFVEC2F topHitPoint2d;
99  SFVEC2F botHitPoint2d;
100 
101  if( hit_top )
102  topHitPoint2d = SFVEC2F( aRay.m_Origin.x + aRay.m_Dir.x * tTop,
103  aRay.m_Origin.y + aRay.m_Dir.y * tTop );
104 
105  if( hit_bot )
106  botHitPoint2d = SFVEC2F( aRay.m_Origin.x + aRay.m_Dir.x * tBot,
107  aRay.m_Origin.y + aRay.m_Dir.y * tBot );
108 
109  if( hit_top && hit_bot )
110  {
111  if( tBot < tTop )
112  {
113  if( m_object2d->IsPointInside( botHitPoint2d ) )
114  {
115  if( tBot < aHitInfo.m_tHit )
116  {
117  aHitInfo.m_tHit = tBot;
118  aHitInfo.m_HitPoint = aRay.at( tBot );
119  aHitInfo.m_HitNormal = SFVEC3F( 0.0f, 0.0f, -1.0f );
120  aHitInfo.pHitObject = this;
121 
122  m_material->Generate( aHitInfo.m_HitNormal, aRay, aHitInfo );
123 
124  return true;
125  }
126 
127  return false;
128  }
129  }
130  else
131  {
132  if( m_object2d->IsPointInside( topHitPoint2d ) )
133  {
134  if( tTop < aHitInfo.m_tHit )
135  {
136  aHitInfo.m_tHit = tTop;
137  aHitInfo.m_HitPoint = aRay.at( tTop );
138  aHitInfo.m_HitNormal = SFVEC3F( 0.0f, 0.0f, 1.0f );
139  aHitInfo.pHitObject = this;
140 
141  m_material->Generate( aHitInfo.m_HitNormal, aRay, aHitInfo );
142 
143  return true;
144  }
145 
146  return false;
147  }
148  }
149  }
150  else
151  {
152  if( hit_top )
153  {
154  if( tTop < tBot )
155  {
156  if( m_object2d->IsPointInside( topHitPoint2d ) )
157  {
158  if( tTop < aHitInfo.m_tHit )
159  {
160  aHitInfo.m_tHit = tTop;
161  aHitInfo.m_HitPoint = aRay.at( tTop );
162  aHitInfo.m_HitNormal = SFVEC3F( 0.0f, 0.0f, 1.0f );
163  aHitInfo.pHitObject = this;
164 
165  m_material->Generate( aHitInfo.m_HitNormal, aRay, aHitInfo );
166 
167  return true;
168  }
169 
170  return false;
171  }
172  }
173  }
174  else
175  {
176  if( hit_bot )
177  {
178  if( tBot < tTop )
179  {
180  if( m_object2d->IsPointInside( botHitPoint2d ) )
181  {
182  if( tBot < aHitInfo.m_tHit )
183  {
184  aHitInfo.m_tHit = tBot;
185  aHitInfo.m_HitPoint = aRay.at( tBot );
186  aHitInfo.m_HitNormal = SFVEC3F( 0.0f, 0.0f, -1.0f );
187  aHitInfo.pHitObject = this;
188 
189  m_material->Generate( aHitInfo.m_HitNormal, aRay, aHitInfo );
190 
191  return true;
192  }
193 
194  return false;
195  }
196  }
197  }
198  else
199  {
200  // At this point, the ray miss the two planes but it still
201  // hits the box. It means that the rays are "(almost)parallel"
202  // to the planes, so must calc the intersection
203  }
204  }
205  }
206 
207  SFVEC3F boxHitPointStart = aRay.at( tBBoxStart );
208  SFVEC3F boxHitPointEnd = aRay.at( tBBoxEnd );
209 
210  SFVEC2F boxHitPointStart2D( boxHitPointStart.x, boxHitPointStart.y );
211  SFVEC2F boxHitPointEnd2D( boxHitPointEnd.x, boxHitPointEnd.y );
212 
213  float tOut;
214  SFVEC2F outNormal;
215  RAYSEG2D raySeg( boxHitPointStart2D, boxHitPointEnd2D );
216 
217  if( m_object2d->Intersect( raySeg, &tOut, &outNormal ) )
218  {
219  // The hitT is a hit value for the segment length 'start' - 'end',
220  // so it ranges from 0.0 - 1.0. We now convert it to a 3D hit position
221  // and calculate the real hitT of the ray.
222  SFVEC3F hitPoint = boxHitPointStart + ( boxHitPointEnd - boxHitPointStart ) * tOut;
223 
224  const float t = glm::length( hitPoint - aRay.m_Origin );
225 
226  if( t < aHitInfo.m_tHit )
227  {
228  aHitInfo.m_tHit = t;
229  aHitInfo.m_HitPoint = hitPoint;
230 
231  if( ( outNormal.x == 0.0f ) && ( outNormal.y == 0.0f ) )
232  {
233  aHitInfo.m_HitNormal = SFVEC3F( 0.0f, 0.0f, 1.0f );
234  }
235  else
236  {
237  aHitInfo.m_HitNormal = SFVEC3F( outNormal.x, outNormal.y, 0.0f );
238  }
239 
240  aHitInfo.pHitObject = this;
241 
242  m_material->Generate( aHitInfo.m_HitNormal, aRay, aHitInfo );
243 
244  return true;
245  }
246  }
247 
248  return false;
249  }
250  else
251  {
253  // Disabled due to refraction artifacts
254  // this will mostly happen inside the board body
255 #if 0
256  // Started inside
257  const SFVEC3F boxHitPointStart = aRay.at( tBBoxStart );
258  const SFVEC3F boxHitPointEnd = aRay.at( tBBoxEnd );
259 
260  const SFVEC2F boxHitPointStart2D( boxHitPointStart.x, boxHitPointStart.y );
261 
262  const SFVEC2F boxHitPointEnd2D( boxHitPointEnd.x, boxHitPointEnd.y );
263 
264  if( !( m_object2d->IsPointInside( boxHitPointStart2D ) &&
265  m_object2d->IsPointInside( boxHitPointEnd2D ) ) )
266  return false;
267 
268  float tOut;
269  SFVEC2F outNormal;
270  RAYSEG2D raySeg( boxHitPointStart2D, boxHitPointEnd2D );
271 
272  if( ( m_object2d->IsPointInside( boxHitPointStart2D ) &&
273  m_object2d->IsPointInside( boxHitPointEnd2D ) ) )
274  {
275  if( tBBoxEnd < aHitInfo.m_tHit )
276  {
277  aHitInfo.m_tHit = tBBoxEnd;
278  aHitInfo.m_HitPoint = aRay.at( tBBoxEnd );
279  aHitInfo.pHitObject = this;
280 
281  if( aRay.m_Dir.z > 0.0f )
282  aHitInfo.m_HitNormal = SFVEC3F( 0.0f, 0.0f, -1.0f );
283  else
284  aHitInfo.m_HitNormal = SFVEC3F( 0.0f, 0.0f, 1.0f );
285 
286  m_material->Generate( aHitInfo.m_HitNormal, aRay, aHitInfo );
287 
288  return true;
289  }
290  }
291  else
292  {
293  if( m_object2d->Intersect( raySeg, &tOut, &outNormal ) )
294  {
295  // The hitT is a hit value for the segment length 'start' - 'end',
296  // so it ranges from 0.0 - 1.0. We now convert it to a 3D hit position
297  // and calculate the real hitT of the ray.
298  const SFVEC3F hitPoint = boxHitPointStart +
299  ( boxHitPointEnd - boxHitPointStart ) * tOut;
300 
301  const float t = glm::length( hitPoint - aRay.m_Origin );
302 
303  if( t < aHitInfo.m_tHit )
304  {
305  aHitInfo.m_tHit = t;
306  aHitInfo.m_HitPoint = hitPoint;
307  aHitInfo.m_HitNormal = SFVEC3F( outNormal.x, outNormal.y, 0.0f );
308  aHitInfo.pHitObject = this;
309 
310  m_material->Generate( aHitInfo.m_HitNormal, aRay, aHitInfo );
311 
312  return true;
313  }
314  }
315  }
316 #endif
317  }
318 
319  return false;
320 }
321 
322 
323 bool LAYER_ITEM::IntersectP( const RAY& aRay, float aMaxDistance ) const
324 {
325  float tBBoxStart;
326  float tBBoxEnd;
327 
328  if( !m_bbox.Intersect( aRay, &tBBoxStart, &tBBoxEnd ) )
329  return false;
330 
331  if( ( tBBoxStart > aMaxDistance ) || ( fabs( tBBoxStart - tBBoxEnd ) < FLT_EPSILON ) )
332  return false;
333 
334  float tTop = FLT_MAX;
335  float tBot = FLT_MAX;
336  bool hit_top = false;
337  bool hit_bot = false;
338 
339  if( (float)fabs( aRay.m_Dir.z ) > FLT_EPSILON )
340  {
341  tBot = ( m_bbox.Min().z - aRay.m_Origin.z ) * aRay.m_InvDir.z;
342  tTop = ( m_bbox.Max().z - aRay.m_Origin.z ) * aRay.m_InvDir.z;
343 
344  const float tBBoxStartAdjusted = NextFloatUp( tBBoxStart );
345 
346  if( tBot > FLT_EPSILON )
347  {
348  hit_bot = tBot <= tBBoxStartAdjusted;
349  tBot = NextFloatDown( tBot );
350  }
351 
352  if( tTop > FLT_EPSILON )
353  {
354  hit_top = tTop <= tBBoxStartAdjusted;
355  tTop = NextFloatDown( tTop );
356  }
357  }
358 
359  tBBoxStart = NextFloatDown( tBBoxStart );
360  tBBoxEnd = NextFloatUp( tBBoxEnd );
361 
362  SFVEC2F topHitPoint2d;
363  SFVEC2F botHitPoint2d;
364 
365  if( hit_top )
366  topHitPoint2d = SFVEC2F( aRay.m_Origin.x + aRay.m_Dir.x * tTop,
367  aRay.m_Origin.y + aRay.m_Dir.y * tTop );
368 
369  if( hit_bot )
370  botHitPoint2d = SFVEC2F( aRay.m_Origin.x + aRay.m_Dir.x * tBot,
371  aRay.m_Origin.y + aRay.m_Dir.y * tBot );
372 
373  if( hit_top && hit_bot )
374  {
375  if( tBot < tTop )
376  {
377  if( m_object2d->IsPointInside( botHitPoint2d ) )
378  {
379  if( tBot < aMaxDistance )
380  return true;
381 
382  return false;
383  }
384  }
385  else
386  {
387  if( m_object2d->IsPointInside( topHitPoint2d ) )
388  {
389  if( tTop < aMaxDistance )
390  return true;
391 
392  return false;
393  }
394  }
395  }
396  else
397  {
398  if( hit_top )
399  {
400  if( tTop < tBot )
401  {
402  if( m_object2d->IsPointInside( topHitPoint2d ) )
403  {
404  if( tTop < aMaxDistance )
405  return true;
406 
407  return false;
408  }
409  }
410  }
411  else
412  {
413  if( hit_bot )
414  {
415  if( tBot < tTop )
416  {
417  if( m_object2d->IsPointInside( botHitPoint2d ) )
418  {
419  if( tBot < aMaxDistance )
420  return true;
421 
422  return false;
423  }
424  }
425  }
426  else
427  {
428  // At this point, the ray miss the two planes but it still
429  // hits the box. It means that the rays are "(almost)parallel"
430  // to the planes, so must calc the intersection
431  }
432  }
433  }
434 
435  SFVEC3F boxHitPointStart = aRay.at( tBBoxStart );
436  SFVEC3F boxHitPointEnd = aRay.at( tBBoxEnd );
437 
438  SFVEC2F boxHitPointStart2D( boxHitPointStart.x, boxHitPointStart.y );
439 
440  SFVEC2F boxHitPointEnd2D( boxHitPointEnd.x, boxHitPointEnd.y );
441 
442  float tOut;
443  SFVEC2F outNormal;
444  RAYSEG2D raySeg( boxHitPointStart2D, boxHitPointEnd2D );
445 
446  if( m_object2d->Intersect( raySeg, &tOut, &outNormal ) )
447  {
448  //if( (tOut > FLT_EPSILON) && (tOut < 1.0f) )
449  {
450  // The hitT is a hit value for the segment length 'start' - 'end',
451  // so it ranges from 0.0 - 1.0. We now convert it to a 3D hit position
452  // and calculate the real hitT of the ray.
453  const SFVEC3F hitPoint = boxHitPointStart +
454  ( boxHitPointEnd - boxHitPointStart ) * tOut;
455  const float t = glm::length( hitPoint - aRay.m_Origin );
456 
457  if( ( t < aMaxDistance ) && ( t > FLT_EPSILON ) )
458  return true;
459  }
460  }
461 
462  return false;
463 }
464 
465 
466 bool LAYER_ITEM::Intersects( const BBOX_3D& aBBox ) const
467 {
468  if( !m_bbox.Intersects( aBBox ) )
469  return false;
470 
471  const BBOX_2D bbox2D( SFVEC2F( aBBox.Min().x, aBBox.Min().y ),
472  SFVEC2F( aBBox.Max().x, aBBox.Max().y ) );
473 
474  return m_object2d->Intersects( bbox2D );
475 }
476 
477 
479 {
480  (void)aHitInfo; // unused
481 
482  return m_diffusecolor;
483 }
Defines math related functions.
const SFVEC3F & Max() const
Return the maximum vertex pointer.
Definition: bbox_3d.h:203
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:93
Definition: ray.h:67
float m_tHit
( 4) distance
Definition: hitinfo.h:43
SFVEC3F at(float t) const
Definition: ray.h:89
bool Inside(const SFVEC3F &aPoint) const
Check if a point is inside this bounding box.
Definition: bbox_3d.cpp:240
const SFVEC3F & Min() const
Return the minimum vertex pointer.
Definition: bbox_3d.h:196
SFVEC3F m_InvDir
Definition: ray.h:75
SFVEC3F m_HitPoint
(12) hit position
Definition: hitinfo.h:49
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:191
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:45
SFVEC3F m_Dir
Definition: ray.h:72
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:69
void ScaleNextUp()
Scale a bounding box to the next float representation making it larger.
Definition: bbox_3d.cpp:203
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:40
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:52
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:42
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:49
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:227
Definition: ray.h:110