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