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