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