KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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-2022 Mario Luzeiro <[email protected]>
5 * Copyright The 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, see <https://www.gnu.org/licenses/>.
19 */
20
21#include "layer_item_3d.h"
22#include "3d_fastmath.h"
23#include <wx/debug.h>
24#include <advanced_config.h>
25
26
27extern float g_BevelThickness3DU;
28
29
30LAYER_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 ) );
43 m_bbox.ScaleNextUp();
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
51bool 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 aHitInfo.pHitObject = this;
227
228 const float zNormalDir = hit_top?1.0f:hit_bot?-1.0f:0.0f;
229
230 if( ( outNormal.x == 0.0f ) && ( outNormal.y == 0.0f ) )
231 {
232 aHitInfo.m_HitNormal = SFVEC3F( 0.0f, 0.0f, zNormalDir );
233 }
234 else
235 {
236 // Calculate smooth bevel normal
237 float zBend = 0.0f;
238
239 if( hit_top || hit_bot )
240 {
241 float zDistanceToTopOrBot;
242
243 // Calculate the distance from hitpoint z to the Max/Min z of the layer
244 if( hit_top )
245 {
246 zDistanceToTopOrBot = ( m_bbox.Max().z - hitPoint.z );
247 }
248 else
249 {
250 zDistanceToTopOrBot = ( hitPoint.z - m_bbox.Min().z );
251 }
252
253 // For items that are > than g_BevelThickness3DU
254 // (eg on board vias / plated holeS) use a factor based on
255 // m_bbox.GetExtent().z
256 const float bevelThickness = glm::max(
258 m_bbox.GetExtent().z
259 * (float) ADVANCED_CFG::GetCfg().m_3DRT_BevelExtentFactor );
260
261 if( ( zDistanceToTopOrBot > 0.0f )
262 && ( zDistanceToTopOrBot < bevelThickness ) )
263 {
264 // Invert and Normalize the distance 0..1
265 zBend = ( bevelThickness - zDistanceToTopOrBot ) / bevelThickness;
266 }
267 }
268
269 const SFVEC3F normalLateral = SFVEC3F( outNormal.x, outNormal.y, 0.0f );
270 const SFVEC3F normalTopBot = SFVEC3F( 0.0f, 0.0f, zNormalDir );
271
272 // Interpolate between the regular lateral normal and the top/bot normal
273 aHitInfo.m_HitNormal = glm::mix( normalLateral, normalTopBot, zBend );
274 }
275
276 m_material->Generate( aHitInfo.m_HitNormal, aRay, aHitInfo );
277
278 return true;
279 }
280 }
281
282 return false;
283 }
284 else
285 {
287 // Disabled due to refraction artifacts
288 // this will mostly happen inside the board body
289#if 0
290 // Started inside
291 const SFVEC3F boxHitPointStart = aRay.at( tBBoxStart );
292 const SFVEC3F boxHitPointEnd = aRay.at( tBBoxEnd );
293
294 const SFVEC2F boxHitPointStart2D( boxHitPointStart.x, boxHitPointStart.y );
295
296 const SFVEC2F boxHitPointEnd2D( boxHitPointEnd.x, boxHitPointEnd.y );
297
298 if( !( m_object2d->IsPointInside( boxHitPointStart2D ) &&
299 m_object2d->IsPointInside( boxHitPointEnd2D ) ) )
300 return false;
301
302 float tOut;
303 SFVEC2F outNormal;
304 RAYSEG2D raySeg( boxHitPointStart2D, boxHitPointEnd2D );
305
306 if( ( m_object2d->IsPointInside( boxHitPointStart2D )
307 && m_object2d->IsPointInside( boxHitPointEnd2D ) ) )
308 {
309 if( tBBoxEnd < aHitInfo.m_tHit )
310 {
311 aHitInfo.m_tHit = tBBoxEnd;
312 aHitInfo.m_HitPoint = aRay.at( tBBoxEnd );
313 aHitInfo.pHitObject = this;
314
315 if( aRay.m_Dir.z > 0.0f )
316 aHitInfo.m_HitNormal = SFVEC3F( 0.0f, 0.0f, -1.0f );
317 else
318 aHitInfo.m_HitNormal = SFVEC3F( 0.0f, 0.0f, 1.0f );
319
320 m_material->Generate( aHitInfo.m_HitNormal, aRay, aHitInfo );
321
322 return true;
323 }
324 }
325 else
326 {
327 if( m_object2d->Intersect( raySeg, &tOut, &outNormal ) )
328 {
329 // The hitT is a hit value for the segment length 'start' - 'end',
330 // so it ranges from 0.0 - 1.0. We now convert it to a 3D hit position
331 // and calculate the real hitT of the ray.
332 const SFVEC3F hitPoint = boxHitPointStart +
333 ( boxHitPointEnd - boxHitPointStart ) * tOut;
334
335 const float t = glm::length( hitPoint - aRay.m_Origin );
336
337 if( t < aHitInfo.m_tHit )
338 {
339 aHitInfo.m_tHit = t;
340 aHitInfo.m_HitPoint = hitPoint;
341 aHitInfo.m_HitNormal = SFVEC3F( outNormal.x, outNormal.y, 0.0f );
342 aHitInfo.pHitObject = this;
343
344 m_material->Generate( aHitInfo.m_HitNormal, aRay, aHitInfo );
345
346 return true;
347 }
348 }
349 }
350#endif
351 }
352
353 return false;
354}
355
356
357bool LAYER_ITEM::IntersectP( const RAY& aRay, float aMaxDistance ) const
358{
359 float tBBoxStart;
360 float tBBoxEnd;
361
362 if( !m_bbox.Intersect( aRay, &tBBoxStart, &tBBoxEnd ) )
363 return false;
364
365 if( ( tBBoxStart > aMaxDistance ) || ( fabs( tBBoxStart - tBBoxEnd ) < FLT_EPSILON ) )
366 return false;
367
368 float tTop = FLT_MAX;
369 float tBot = FLT_MAX;
370 bool hit_top = false;
371 bool hit_bot = false;
372
373 if( (float)fabs( aRay.m_Dir.z ) > FLT_EPSILON )
374 {
375 tBot = ( m_bbox.Min().z - aRay.m_Origin.z ) * aRay.m_InvDir.z;
376 tTop = ( m_bbox.Max().z - aRay.m_Origin.z ) * aRay.m_InvDir.z;
377
378 const float tBBoxStartAdjusted = NextFloatUp( tBBoxStart );
379
380 if( tBot > FLT_EPSILON )
381 {
382 hit_bot = tBot <= tBBoxStartAdjusted;
383 tBot = NextFloatDown( tBot );
384 }
385
386 if( tTop > FLT_EPSILON )
387 {
388 hit_top = tTop <= tBBoxStartAdjusted;
389 tTop = NextFloatDown( tTop );
390 }
391 }
392
393 tBBoxStart = NextFloatDown( tBBoxStart );
394 tBBoxEnd = NextFloatUp( tBBoxEnd );
395
396 SFVEC2F topHitPoint2d;
397 SFVEC2F botHitPoint2d;
398
399 if( hit_top )
400 topHitPoint2d = SFVEC2F( aRay.m_Origin.x + aRay.m_Dir.x * tTop,
401 aRay.m_Origin.y + aRay.m_Dir.y * tTop );
402
403 if( hit_bot )
404 botHitPoint2d = SFVEC2F( aRay.m_Origin.x + aRay.m_Dir.x * tBot,
405 aRay.m_Origin.y + aRay.m_Dir.y * tBot );
406
407 if( hit_top && hit_bot )
408 {
409 if( tBot < tTop )
410 {
411 if( m_object2d->IsPointInside( botHitPoint2d ) )
412 {
413 if( tBot < aMaxDistance )
414 return true;
415
416 return false;
417 }
418 }
419 else
420 {
421 if( m_object2d->IsPointInside( topHitPoint2d ) )
422 {
423 if( tTop < aMaxDistance )
424 return true;
425
426 return false;
427 }
428 }
429 }
430 else
431 {
432 if( hit_top )
433 {
434 if( tTop < tBot )
435 {
436 if( m_object2d->IsPointInside( topHitPoint2d ) )
437 {
438 if( tTop < aMaxDistance )
439 return true;
440
441 return false;
442 }
443 }
444 }
445 else
446 {
447 if( hit_bot )
448 {
449 if( tBot < tTop )
450 {
451 if( m_object2d->IsPointInside( botHitPoint2d ) )
452 {
453 if( tBot < aMaxDistance )
454 return true;
455
456 return false;
457 }
458 }
459 }
460 else
461 {
462 // At this point, the ray miss the two planes but it still
463 // hits the box. It means that the rays are "(almost)parallel"
464 // to the planes, so must calc the intersection
465 }
466 }
467 }
468
469 SFVEC3F boxHitPointStart = aRay.at( tBBoxStart );
470 SFVEC3F boxHitPointEnd = aRay.at( tBBoxEnd );
471
472 SFVEC2F boxHitPointStart2D( boxHitPointStart.x, boxHitPointStart.y );
473
474 SFVEC2F boxHitPointEnd2D( boxHitPointEnd.x, boxHitPointEnd.y );
475
476 float tOut;
477 SFVEC2F outNormal;
478 RAYSEG2D raySeg( boxHitPointStart2D, boxHitPointEnd2D );
479
480 if( m_object2d->Intersect( raySeg, &tOut, &outNormal ) )
481 {
482 //if( (tOut > FLT_EPSILON) && (tOut < 1.0f) )
483 {
484 // The hitT is a hit value for the segment length 'start' - 'end',
485 // so it ranges from 0.0 - 1.0. We now convert it to a 3D hit position
486 // and calculate the real hitT of the ray.
487 const SFVEC3F hitPoint = boxHitPointStart +
488 ( boxHitPointEnd - boxHitPointStart ) * tOut;
489 const float t = glm::length( hitPoint - aRay.m_Origin );
490
491 if( ( t < aMaxDistance ) && ( t > FLT_EPSILON ) )
492 return true;
493 }
494 }
495
496 return false;
497}
498
499
500bool LAYER_ITEM::Intersects( const BBOX_3D& aBBox ) const
501{
502 if( !m_bbox.Intersects( aBBox ) )
503 return false;
504
505 const BBOX_2D bbox2D( SFVEC2F( aBBox.Min().x, aBBox.Min().y ),
506 SFVEC2F( aBBox.Max().x, aBBox.Max().y ) );
507
508 return m_object2d->Intersects( bbox2D );
509}
510
511
512SFVEC3F LAYER_ITEM::GetDiffuseColor( const HITINFO& /* aHitInfo */ ) const
513{
514 return m_diffusecolor;
515}
Defines math related functions.
float NextFloatDown(float v)
float NextFloatUp(float v)
float g_BevelThickness3DU
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
bool Intersects(const BBOX_3D &aBBox) const override
SFVEC3F m_diffusecolor
SFVEC3F GetDiffuseColor(const HITINFO &aHitInfo) const override
const OBJECT_2D * m_object2d
LAYER_ITEM(const OBJECT_2D *aObject2D, float aZMin, float aZMax)
bool IntersectP(const RAY &aRay, float aMaxDistance) const override
bool Intersect(const RAY &aRay, HITINFO &aHitInfo) const override
const SFVEC2F & GetCentroid() const
Definition object_2d.h:101
BBOX_3D m_bbox
Definition object_3d.h:93
SFVEC3F m_centroid
Definition object_3d.h:94
OBJECT_3D(OBJECT_3D_TYPE aObjType)
Definition object_3d.cpp:36
const MATERIAL * m_material
Definition object_3d.h:96
OBJECT_3D_TYPE
Definition object_3d.h:35
Manage a bounding box defined by two SFVEC2F min max points.
Definition bbox_2d.h:38
const SFVEC2F & Min() const
Definition bbox_2d.h:171
const SFVEC2F & Max() const
Definition bbox_2d.h:176
void ScaleNextUp()
Scale a bounding box to the next float representation making it larger.
Definition bbox_2d.cpp:158
Manage a bounding box defined by two SFVEC3F min max points.
Definition bbox_3d.h:39
const SFVEC3F & Min() const
Return the minimum vertex pointer.
Definition bbox_3d.h:188
const SFVEC3F & Max() const
Return the maximum vertex pointer.
Definition bbox_3d.h:195
Stores the hit information of a ray with a point on the surface of a object.
Definition hitinfo.h:32
float m_tHit
( 4) distance
Definition hitinfo.h:34
const OBJECT_3D * pHitObject
( 4) Object that was hitted
Definition hitinfo.h:36
SFVEC3F m_HitNormal
(12) normal at the hit point
Definition hitinfo.h:33
SFVEC3F m_HitPoint
(12) hit position
Definition hitinfo.h:40
Definition ray.h:59
SFVEC3F m_Dir
Definition ray.h:63
SFVEC3F m_InvDir
Definition ray.h:66
SFVEC3F m_Origin
Definition ray.h:60
SFVEC3F at(float t) const
Definition ray.h:80
glm::vec2 SFVEC2F
Definition xv3d_types.h:38
glm::vec3 SFVEC3F
Definition xv3d_types.h:40