KiCad PCB EDA Suite
Loading...
Searching...
No Matches
raytracing/create_scene.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 (C) 2023 CERN
6 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, you may find one here:
20 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21 * or you may search the http://www.gnu.org website for the version 2 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
27#include "shapes3D/plane_3d.h"
31#include "shapes3D/frustum_3d.h"
34#include "shapes2D/ring_2d.h"
35#include "shapes2D/polygon_2d.h"
39#include "3d_fastmath.h"
40#include "3d_math.h"
41
42#include <board.h>
43#include <footprint.h>
45#include <eda_3d_viewer_frame.h>
46#include <project_pcb.h>
47
48#include <base_units.h>
49#include <core/profile.h> // To use GetRunningMicroSecs or another profiling utility
50
59static float TransparencyControl( float aGrayColorValue, float aTransparency )
60{
61 const float aaa = aTransparency * aTransparency * aTransparency;
62
63 // 1.00-1.05*(1.0-x)^3
64 float ca = 1.0f - aTransparency;
65 ca = 1.00f - 1.05f * ca * ca * ca;
66
67 // Squaring gray value makes darker colors more opaque, which improves appearance
68 // of dark solder masks like black where copper would otherwise show through
69 return glm::max( glm::min( aGrayColorValue * aGrayColorValue * ca + aaa, 1.0f ), 0.0f );
70}
71
75#define UNITS3D_TO_UNITSPCB ( pcbIUScale.IU_PER_MM )
76
77
79{
81 m_boardAdapter.m_Cfg->m_Render.raytrace_nrsamples_refractions );
83 m_boardAdapter.m_Cfg->m_Render.raytrace_nrsamples_reflections );
84
86 m_boardAdapter.m_Cfg->m_Render.raytrace_recursivelevel_refractions );
88 m_boardAdapter.m_Cfg->m_Render.raytrace_recursivelevel_reflections );
89
90 double mmTo3Dunits = pcbIUScale.IU_PER_MM * m_boardAdapter.BiuTo3dUnits();
91
92 if( m_boardAdapter.m_Cfg->m_Render.raytrace_procedural_textures )
93 {
94 m_boardMaterial = BOARD_NORMAL( 0.40f * mmTo3Dunits );
95 m_copperMaterial = COPPER_NORMAL( 4.0f * mmTo3Dunits, &m_boardMaterial );
96 m_platedCopperMaterial = PLATED_COPPER_NORMAL( 0.5f * mmTo3Dunits );
98 m_plasticMaterial = PLASTIC_NORMAL( 0.05f * mmTo3Dunits );
99 m_shinyPlasticMaterial = PLASTIC_SHINE_NORMAL( 0.1f * mmTo3Dunits );
100 m_brushedMetalMaterial = BRUSHED_METAL_NORMAL( 0.05f * mmTo3Dunits );
101 m_silkScreenMaterial = SILK_SCREEN_NORMAL( 0.25f * mmTo3Dunits );
102 }
103
104 // http://devernay.free.fr/cours/opengl/materials.html
105 // Copper
106 const SFVEC3F copperSpecularLinear =
107 ConvertSRGBToLinear( glm::clamp( (SFVEC3F) m_boardAdapter.m_CopperColor * 0.5f + 0.25f,
108 SFVEC3F( 0.0f ), SFVEC3F( 1.0f ) ) );
109
111 ConvertSRGBToLinear( (SFVEC3F) m_boardAdapter.m_CopperColor * 0.3f ),
112 SFVEC3F( 0.0f ), copperSpecularLinear, 0.4f * 128.0f, 0.0f, 0.0f );
113
114 if( m_boardAdapter.m_Cfg->m_Render.raytrace_procedural_textures )
115 m_materials.m_Copper.SetGenerator( &m_platedCopperMaterial );
116
117 m_materials.m_NonPlatedCopper = BLINN_PHONG_MATERIAL(
118 ConvertSRGBToLinear( SFVEC3F( 0.191f, 0.073f, 0.022f ) ), SFVEC3F( 0.0f, 0.0f, 0.0f ),
119 SFVEC3F( 0.256f, 0.137f, 0.086f ), 0.15f * 128.0f, 0.0f, 0.0f );
120
121 if( m_boardAdapter.m_Cfg->m_Render.raytrace_procedural_textures )
122 m_materials.m_NonPlatedCopper.SetGenerator( &m_copperMaterial );
123
125 ConvertSRGBToLinear( (SFVEC3F) m_boardAdapter.m_SolderPasteColor )
126 * ConvertSRGBToLinear( (SFVEC3F) m_boardAdapter.m_SolderPasteColor ),
127 SFVEC3F( 0.0f, 0.0f, 0.0f ),
128 ConvertSRGBToLinear( (SFVEC3F) m_boardAdapter.m_SolderPasteColor )
130 (SFVEC3F) m_boardAdapter.m_SolderPasteColor ),
131 0.10f * 128.0f, 0.0f, 0.0f );
132
134 SFVEC3F( 0.0f, 0.0f, 0.0f ),
135 glm::clamp( ( ( SFVEC3F )( 1.0f ) - ConvertSRGBToLinear(
136 (SFVEC3F) m_boardAdapter.m_SilkScreenColorTop ) ),
137 SFVEC3F( 0.0f ), SFVEC3F( 0.10f ) ), 0.078125f * 128.0f, 0.0f, 0.0f );
138
139 if( m_boardAdapter.m_Cfg->m_Render.raytrace_procedural_textures )
140 m_materials.m_SilkS.SetGenerator( &m_silkScreenMaterial );
141
142 // Assume that SolderMaskTop == SolderMaskBot
143 const float solderMask_gray =
144 ( m_boardAdapter.m_SolderMaskColorTop.r + m_boardAdapter.m_SolderMaskColorTop.g
145 + m_boardAdapter.m_SolderMaskColorTop.b )
146 / 3.0f;
147
148 const float solderMask_transparency = TransparencyControl( solderMask_gray,
149 1.0f - m_boardAdapter.m_SolderMaskColorTop.a );
150
151 // For darker solder mask colors, increase shininess for a more realistic appearance.
152 // Darker colors appear to have a sharper specular highlight in real life.
153 const float minSolderMaskShininess = 0.85f * 128.0f;
154 const float maxSolderMaskShininess = 512.0f;
155 const float solderMaskShininess = minSolderMaskShininess
156 + ( maxSolderMaskShininess - minSolderMaskShininess ) * ( 1.0f - solderMask_gray );
157
158 // Darker solder mask colors need lower reflection to prevent washed-out appearance
159 const float solderMaskReflection = glm::clamp( solderMask_gray * 0.3f, 0.02f, 0.16f );
160
161 m_materials.m_SolderMask = BLINN_PHONG_MATERIAL(
162 ConvertSRGBToLinear( (SFVEC3F) m_boardAdapter.m_SolderMaskColorTop ) * 0.10f,
163 SFVEC3F( 0.0f, 0.0f, 0.0f ),
164 SFVEC3F( glm::clamp( solderMask_gray * 2.0f, 0.30f, 1.0f ) ), solderMaskShininess,
165 solderMask_transparency, solderMaskReflection );
166
167 m_materials.m_SolderMask.SetCastShadows( true );
168 m_materials.m_SolderMask.SetRefractionRayCount( 1 );
169
170 if( m_boardAdapter.m_Cfg->m_Render.raytrace_procedural_textures )
171 m_materials.m_SolderMask.SetGenerator( &m_solderMaskMaterial );
172
173 m_materials.m_EpoxyBoard =
174 BLINN_PHONG_MATERIAL( ConvertSRGBToLinear( SFVEC3F( 16.0f / 255.0f, 14.0f / 255.0f,
175 10.0f / 255.0f ) ),
176 SFVEC3F( 0.0f, 0.0f, 0.0f ),
177 ConvertSRGBToLinear( SFVEC3F( 10.0f / 255.0f, 8.0f / 255.0f,
178 10.0f / 255.0f ) ),
179 0.1f * 128.0f, 1.0f - m_boardAdapter.m_BoardBodyColor.a, 0.0f );
180
181 m_materials.m_EpoxyBoard.SetAbsorvance( 10.0f );
182
183 if( m_boardAdapter.m_Cfg->m_Render.raytrace_procedural_textures )
184 m_materials.m_EpoxyBoard.SetGenerator( &m_boardMaterial );
185
186 SFVEC3F bgTop = ConvertSRGBToLinear( (SFVEC3F) m_boardAdapter.m_BgColorTop );
187
188 m_materials.m_Floor = BLINN_PHONG_MATERIAL( bgTop * 0.125f, SFVEC3F( 0.0f, 0.0f, 0.0f ),
189 ( SFVEC3F( 1.0f ) - bgTop ) / 3.0f,
190 0.10f * 128.0f, 1.0f, 0.50f );
191 m_materials.m_Floor.SetCastShadows( false );
192 m_materials.m_Floor.SetReflectionRecursionCount( 1 );
193}
194
195
196void RENDER_3D_RAYTRACE_BASE::createObject( CONTAINER_3D& aDstContainer, const OBJECT_2D* aObject2D,
197 float aZMin, float aZMax, const MATERIAL* aMaterial,
198 const SFVEC3F& aObjColor )
199{
200 switch( aObject2D->GetObjectType() )
201 {
203 {
205
206 XY_PLANE* objPtr;
207 objPtr = new XY_PLANE( BBOX_3D(
208 SFVEC3F( aObject2D->GetBBox().Min().x, aObject2D->GetBBox().Min().y, aZMin ),
209 SFVEC3F( aObject2D->GetBBox().Max().x, aObject2D->GetBBox().Max().y, aZMin ) ) );
210 objPtr->SetMaterial( aMaterial );
211 objPtr->SetColor( ConvertSRGBToLinear( aObjColor ) );
212 aDstContainer.Add( objPtr );
213
214 objPtr = new XY_PLANE( BBOX_3D(
215 SFVEC3F( aObject2D->GetBBox().Min().x, aObject2D->GetBBox().Min().y, aZMax ),
216 SFVEC3F( aObject2D->GetBBox().Max().x, aObject2D->GetBBox().Max().y, aZMax ) ) );
217 objPtr->SetMaterial( aMaterial );
218 objPtr->SetColor( ConvertSRGBToLinear( aObjColor ) );
219 aDstContainer.Add( objPtr );
220 break;
221 }
222
224 {
226
227 const ROUND_SEGMENT_2D* aRoundSeg2D = static_cast<const ROUND_SEGMENT_2D*>( aObject2D );
228 ROUND_SEGMENT* objPtr = new ROUND_SEGMENT( *aRoundSeg2D, aZMin, aZMax );
229 objPtr->SetMaterial( aMaterial );
230 objPtr->SetColor( ConvertSRGBToLinear( aObjColor ) );
231 aDstContainer.Add( objPtr );
232 break;
233 }
234
235 default:
236 {
237 LAYER_ITEM* objPtr = new LAYER_ITEM( aObject2D, aZMin, aZMax );
238 objPtr->SetMaterial( aMaterial );
239 objPtr->SetColor( ConvertSRGBToLinear( aObjColor ) );
240 aDstContainer.Add( objPtr );
241 break;
242 }
243 }
244}
245
246
248 PCB_LAYER_ID aLayer_id,
249 const MATERIAL* aMaterialLayer,
250 const SFVEC3F& aLayerColor,
251 float aLayerZOffset )
252{
253 if( aContainer2d == nullptr )
254 return;
255
257 bool isSilk = aLayer_id == B_SilkS || aLayer_id == F_SilkS;
258 const LIST_OBJECT2D& listObject2d = aContainer2d->GetList();
259
260 if( listObject2d.size() == 0 )
261 return;
262
263 for( const OBJECT_2D* object2d_A : listObject2d )
264 {
265 // not yet used / implemented (can be used in future to clip the objects in the
266 // board borders
267 OBJECT_2D* object2d_C = CSGITEM_FULL;
268
269 std::vector<const OBJECT_2D*>* object2d_B = CSGITEM_EMPTY;
270
271 object2d_B = new std::vector<const OBJECT_2D*>();
272
273 // Subtract holes but not in SolderPaste
274 // (can be added as an option in future)
275 if( !( aLayer_id == B_Paste || aLayer_id == F_Paste ) )
276 {
277 // Check if there are any layerhole that intersects this object
278 // Eg: a segment is cut by a via hole or THT hole.
279 const MAP_CONTAINER_2D_BASE& layerHolesMap = m_boardAdapter.GetLayerHoleMap();
280
281 if( layerHolesMap.find( aLayer_id ) != layerHolesMap.end() )
282 {
283 const BVH_CONTAINER_2D* holes2d = layerHolesMap.at( aLayer_id );
284
285 CONST_LIST_OBJECT2D intersecting;
286
287 holes2d->GetIntersectingObjects( object2d_A->GetBBox(), intersecting );
288
289 for( const OBJECT_2D* hole2d : intersecting )
290 object2d_B->push_back( hole2d );
291 }
292
293 // Check if there are any THT that intersects this object. If we're processing a silk
294 // layer and the flag is set, then clip the silk at the outer edge of the annular ring,
295 // rather than the at the outer edge of the copper plating.
296 const BVH_CONTAINER_2D& throughHoleOuter =
297 cfg.clip_silk_on_via_annuli && isSilk ? m_boardAdapter.GetViaAnnuli()
298 : m_boardAdapter.GetTH_ODs();
299
300 if( !throughHoleOuter.GetList().empty() )
301 {
302 CONST_LIST_OBJECT2D intersecting;
303
304 throughHoleOuter.GetIntersectingObjects( object2d_A->GetBBox(), intersecting );
305
306 for( const OBJECT_2D* hole2d : intersecting )
307 object2d_B->push_back( hole2d );
308 }
309
310 // Clip counterbore/countersink cutouts from copper layers
311 // Front cutouts affect F_Cu, back cutouts affect B_Cu
312 auto clipCutouts = [this, &object2d_A, &object2d_B]( const BVH_CONTAINER_2D& cutouts )
313 {
314 if( !cutouts.GetList().empty() )
315 {
316 CONST_LIST_OBJECT2D intersecting;
317 cutouts.GetIntersectingObjects( object2d_A->GetBBox(), intersecting );
318
319 for( const OBJECT_2D* cutout : intersecting )
320 object2d_B->push_back( cutout );
321 }
322 };
323
324 if( aLayer_id == F_Cu )
325 {
326 clipCutouts( m_boardAdapter.GetFrontCounterboreCutouts() );
327 clipCutouts( m_boardAdapter.GetFrontCountersinkCutouts() );
328 clipCutouts( m_boardAdapter.GetTertiarydrillCutouts() );
329 }
330 else if( aLayer_id == B_Cu )
331 {
332 clipCutouts( m_boardAdapter.GetBackCounterboreCutouts() );
333 clipCutouts( m_boardAdapter.GetBackCountersinkCutouts() );
334 clipCutouts( m_boardAdapter.GetBackdrillCutouts() );
335 }
336 }
337
338 if( !m_antioutlineBoard2dObjects->GetList().empty() )
339 {
340 CONST_LIST_OBJECT2D intersecting;
341
342 m_antioutlineBoard2dObjects->GetIntersectingObjects( object2d_A->GetBBox(),
343 intersecting );
344
345 for( const OBJECT_2D* obj : intersecting )
346 object2d_B->push_back( obj );
347 }
348
349 const MAP_CONTAINER_2D_BASE& mapLayers = m_boardAdapter.GetLayerMap();
350
352 && ( ( aLayer_id == B_SilkS && mapLayers.find( B_Mask ) != mapLayers.end() )
353 || ( aLayer_id == F_SilkS && mapLayers.find( F_Mask ) != mapLayers.end() ) ) )
354 {
355 const PCB_LAYER_ID maskLayer = ( aLayer_id == B_SilkS ) ? B_Mask : F_Mask;
356
357 const BVH_CONTAINER_2D* containerMaskLayer2d = mapLayers.at( maskLayer );
358
359 CONST_LIST_OBJECT2D intersecting;
360
361 if( containerMaskLayer2d ) // can be null if B_Mask or F_Mask is not shown
362 containerMaskLayer2d->GetIntersectingObjects( object2d_A->GetBBox(), intersecting );
363
364 for( const OBJECT_2D* obj2d : intersecting )
365 object2d_B->push_back( obj2d );
366 }
367
368 if( object2d_B->empty() )
369 {
370 delete object2d_B;
371 object2d_B = CSGITEM_EMPTY;
372 }
373
374 if( ( object2d_B == CSGITEM_EMPTY ) && ( object2d_C == CSGITEM_FULL ) )
375 {
376 LAYER_ITEM* objPtr = new LAYER_ITEM( object2d_A,
377 m_boardAdapter.GetLayerBottomZPos( aLayer_id ) - aLayerZOffset,
378 m_boardAdapter.GetLayerTopZPos( aLayer_id ) + aLayerZOffset );
379 objPtr->SetMaterial( aMaterialLayer );
380 objPtr->SetColor( ConvertSRGBToLinear( aLayerColor ) );
381 m_objectContainer.Add( objPtr );
382 }
383 else
384 {
385 LAYER_ITEM_2D* itemCSG2d = new LAYER_ITEM_2D( object2d_A, object2d_B, object2d_C,
386 object2d_A->GetBoardItem() );
387 m_containerWithObjectsToDelete.Add( itemCSG2d );
388
389 LAYER_ITEM* objPtr = new LAYER_ITEM( itemCSG2d,
390 m_boardAdapter.GetLayerBottomZPos( aLayer_id ) - aLayerZOffset,
391 m_boardAdapter.GetLayerTopZPos( aLayer_id ) + aLayerZOffset );
392
393 objPtr->SetMaterial( aMaterialLayer );
394 objPtr->SetColor( ConvertSRGBToLinear( aLayerColor ) );
395
396 m_objectContainer.Add( objPtr );
397 }
398 }
399}
400
401
402extern void buildBoardBoundingBoxPoly( const BOARD* aBoard, SHAPE_POLY_SET& aOutline );
403
404
405void RENDER_3D_RAYTRACE_BASE::Reload( REPORTER* aStatusReporter, REPORTER* aWarningReporter,
406 bool aOnlyLoadCopperAndShapes )
407{
408 m_reloadRequested = false;
409
410 m_modelMaterialMap.clear();
411
414
415 int64_t stats_startReloadTime = GetRunningMicroSecs();
416
417 if( !aOnlyLoadCopperAndShapes )
418 {
419 m_boardAdapter.InitSettings( aStatusReporter, aWarningReporter );
420
421 SFVEC3F camera_pos = m_boardAdapter.GetBoardCenter();
422 m_camera.SetBoardLookAtPos( camera_pos );
423 }
424
425 m_objectContainer.Clear();
427
429
430 if( aStatusReporter )
431 aStatusReporter->Report( _( "Load Raytracing: board" ) );
432
433 // Create and add the outline board
436
439
440 std::bitset<LAYER_3D_END> layerFlags = m_boardAdapter.GetVisibleLayers();
441
442 if( !aOnlyLoadCopperAndShapes )
443 {
444 const int outlineCount = m_boardAdapter.GetBoardPoly().OutlineCount();
445
446 if( outlineCount > 0 )
447 {
448 float divFactor = 0.0f;
449
450 if( m_boardAdapter.GetViaCount() )
451 divFactor = m_boardAdapter.GetAverageViaHoleDiameter() * 18.0f;
452 else if( m_boardAdapter.GetHoleCount() )
453 divFactor = m_boardAdapter.GetAverageHoleDiameter() * 8.0f;
454
455 SHAPE_POLY_SET boardPolyCopy = m_boardAdapter.GetBoardPoly();
456
457 // Calculate an antiboard outline
458 SHAPE_POLY_SET antiboardPoly;
459
460 buildBoardBoundingBoxPoly( m_boardAdapter.GetBoard(), antiboardPoly );
461
462 antiboardPoly.BooleanSubtract( boardPolyCopy );
463 antiboardPoly.Fracture();
464
465 for( int ii = 0; ii < antiboardPoly.OutlineCount(); ii++ )
466 {
468 m_boardAdapter.BiuTo3dUnits(), -1.0f,
469 *m_boardAdapter.GetBoard(), ii );
470 }
471
472 m_antioutlineBoard2dObjects->BuildBVH();
473
474 boardPolyCopy.Fracture();
475
476 for( int ii = 0; ii < boardPolyCopy.OutlineCount(); ii++ )
477 {
479 m_boardAdapter.BiuTo3dUnits(), divFactor,
480 *m_boardAdapter.GetBoard(), ii );
481 }
482
483 if( layerFlags.test( LAYER_3D_BOARD ) )
484 {
485 const LIST_OBJECT2D& listObjects = m_outlineBoard2dObjects->GetList();
486
487 for( const OBJECT_2D* object2d_A : listObjects )
488 {
489 std::vector<const OBJECT_2D*>* object2d_B = new std::vector<const OBJECT_2D*>();
490
491 // Check if there are any THT that intersects this outline object part
492 if( !m_boardAdapter.GetTH_ODs().GetList().empty() )
493 {
494 const BVH_CONTAINER_2D& throughHoles = m_boardAdapter.GetTH_ODs();
495 CONST_LIST_OBJECT2D intersecting;
496
497 throughHoles.GetIntersectingObjects( object2d_A->GetBBox(), intersecting );
498
499 for( const OBJECT_2D* hole : intersecting )
500 {
501 if( object2d_A->Intersects( hole->GetBBox() ) )
502 object2d_B->push_back( hole );
503 }
504 }
505
506 // Subtract counterbore/countersink cutouts from board body
507 auto addCutoutsFromContainer =
508 [&]( const BVH_CONTAINER_2D& aContainer )
509 {
510 if( !aContainer.GetList().empty() )
511 {
512 CONST_LIST_OBJECT2D intersecting;
513 aContainer.GetIntersectingObjects( object2d_A->GetBBox(),
514 intersecting );
515
516 for( const OBJECT_2D* cutout : intersecting )
517 {
518 if( object2d_A->Intersects( cutout->GetBBox() ) )
519 object2d_B->push_back( cutout );
520 }
521 }
522 };
523
524 addCutoutsFromContainer( m_boardAdapter.GetFrontCounterboreCutouts() );
525 addCutoutsFromContainer( m_boardAdapter.GetBackCounterboreCutouts() );
526 addCutoutsFromContainer( m_boardAdapter.GetFrontCountersinkCutouts() );
527 addCutoutsFromContainer( m_boardAdapter.GetBackCountersinkCutouts() );
528
529 // Subtract backdrill holes (which are in layerHoleMap for F_Cu and B_Cu)
530 const MAP_CONTAINER_2D_BASE& layerHolesMap = m_boardAdapter.GetLayerHoleMap();
531
532 if( layerHolesMap.find( F_Cu ) != layerHolesMap.end() )
533 {
534 const BVH_CONTAINER_2D* holes2d = layerHolesMap.at( F_Cu );
535 CONST_LIST_OBJECT2D intersecting;
536
537 holes2d->GetIntersectingObjects( object2d_A->GetBBox(), intersecting );
538
539 for( const OBJECT_2D* hole2d : intersecting )
540 {
541 if( object2d_A->Intersects( hole2d->GetBBox() ) )
542 object2d_B->push_back( hole2d );
543 }
544 }
545
546 if( layerHolesMap.find( B_Cu ) != layerHolesMap.end() )
547 {
548 const BVH_CONTAINER_2D* holes2d = layerHolesMap.at( B_Cu );
549 CONST_LIST_OBJECT2D intersecting;
550
551 holes2d->GetIntersectingObjects( object2d_A->GetBBox(), intersecting );
552
553 for( const OBJECT_2D* hole2d : intersecting )
554 {
555 if( object2d_A->Intersects( hole2d->GetBBox() ) )
556 object2d_B->push_back( hole2d );
557 }
558 }
559
560 if( !m_antioutlineBoard2dObjects->GetList().empty() )
561 {
562 CONST_LIST_OBJECT2D intersecting;
563
564 m_antioutlineBoard2dObjects->GetIntersectingObjects( object2d_A->GetBBox(),
565 intersecting );
566
567 for( const OBJECT_2D* obj : intersecting )
568 object2d_B->push_back( obj );
569 }
570
571 if( object2d_B->empty() )
572 {
573 delete object2d_B;
574 object2d_B = CSGITEM_EMPTY;
575 }
576
577 if( object2d_B == CSGITEM_EMPTY )
578 {
579 LAYER_ITEM* objPtr = new LAYER_ITEM( object2d_A,
580 m_boardAdapter.GetLayerBottomZPos( F_Cu ),
581 m_boardAdapter.GetLayerBottomZPos( B_Cu ) );
582
583 objPtr->SetMaterial( &m_materials.m_EpoxyBoard );
584 objPtr->SetColor( ConvertSRGBToLinear( m_boardAdapter.m_BoardBodyColor ) );
585 m_objectContainer.Add( objPtr );
586 }
587 else
588 {
589
590 LAYER_ITEM_2D* itemCSG2d = new LAYER_ITEM_2D( object2d_A, object2d_B,
592 *m_boardAdapter.GetBoard() );
593
594 m_containerWithObjectsToDelete.Add( itemCSG2d );
595
596 LAYER_ITEM* objPtr = new LAYER_ITEM( itemCSG2d,
597 m_boardAdapter.GetLayerBottomZPos( F_Cu ),
598 m_boardAdapter.GetLayerBottomZPos( B_Cu ) );
599
600 objPtr->SetMaterial( &m_materials.m_EpoxyBoard );
601 objPtr->SetColor( ConvertSRGBToLinear( m_boardAdapter.m_BoardBodyColor ) );
602 m_objectContainer.Add( objPtr );
603 }
604 }
605
606 // Add cylinders of the board body to container
607 // Note: This is actually a workaround for the holes in the board.
608 // The issue is because if a hole is in a border of a divided polygon ( ex
609 // a polygon or dummy block) it will cut also the render of the hole.
610 // So this will add a full hole.
611 // In fact, that is not need if the hole have copper.
612 if( !m_boardAdapter.GetTH_ODs().GetList().empty() )
613 {
614 const LIST_OBJECT2D& holeList = m_boardAdapter.GetTH_ODs().GetList();
615
616 for( const OBJECT_2D* hole2d : holeList )
617 {
618 if( !m_antioutlineBoard2dObjects->GetList().empty() )
619 {
620 CONST_LIST_OBJECT2D intersecting;
621
622 m_antioutlineBoard2dObjects->GetIntersectingObjects( hole2d->GetBBox(),
623 intersecting );
624
625 // Do not add cylinder if it intersects the edge of the board
626 if( !intersecting.empty() )
627 continue;
628 }
629
630 switch( hole2d->GetObjectType() )
631 {
633 {
634 const float radius = hole2d->GetBBox().GetExtent().x * 0.5f * 0.999f;
635
636 CYLINDER* objPtr = new CYLINDER( hole2d->GetCentroid(),
637 NextFloatDown( m_boardAdapter.GetLayerBottomZPos( F_Cu ) ),
638 NextFloatUp( m_boardAdapter.GetLayerBottomZPos( B_Cu ) ),
639 radius );
640
641 objPtr->SetMaterial( &m_materials.m_EpoxyBoard );
642 objPtr->SetColor(
643 ConvertSRGBToLinear( m_boardAdapter.m_BoardBodyColor ) );
644
645 m_objectContainer.Add( objPtr );
646 }
647 break;
648
649 default:
650 break;
651 }
652 }
653 }
654
655 // Create plugs for backdrilled and post-machined areas
657 }
658 }
659 }
660
661 if( aStatusReporter )
662 aStatusReporter->Report( _( "Load Raytracing: layers" ) );
663
664 // Add layers maps (except B_Mask and F_Mask)
665 for( const std::pair<const PCB_LAYER_ID, BVH_CONTAINER_2D*>& entry :
666 m_boardAdapter.GetLayerMap() )
667 {
668 const PCB_LAYER_ID layer_id = entry.first;
669 const BVH_CONTAINER_2D* container2d = entry.second;
670
671 // Only process layers that exist
672 if( !container2d )
673 continue;
674
675 if( aOnlyLoadCopperAndShapes && !IsCopperLayer( layer_id ) )
676 continue;
677
678 // Mask layers are not processed here because they are a special case
679 if( layer_id == B_Mask || layer_id == F_Mask )
680 continue;
681
682 MATERIAL* materialLayer = &m_materials.m_SilkS;
683 SFVEC3F layerColor = SFVEC3F( 0.0f, 0.0f, 0.0f );
684
685 switch( layer_id )
686 {
687 case B_Adhes:
688 case F_Adhes:
689 break;
690
691 case B_Paste:
692 case F_Paste:
693 materialLayer = &m_materials.m_Paste;
694 layerColor = m_boardAdapter.m_SolderPasteColor;
695 break;
696
697 case B_SilkS:
698 materialLayer = &m_materials.m_SilkS;
699 layerColor = m_boardAdapter.m_SilkScreenColorBot;
700 break;
701
702 case F_SilkS:
703 materialLayer = &m_materials.m_SilkS;
704 layerColor = m_boardAdapter.m_SilkScreenColorTop;
705 break;
706
707 case Dwgs_User:
708 layerColor = m_boardAdapter.m_UserDrawingsColor;
709 break;
710
711 case Cmts_User:
712 layerColor = m_boardAdapter.m_UserCommentsColor;
713 break;
714
715 case Eco1_User:
716 layerColor = m_boardAdapter.m_ECO1Color;
717 break;
718
719 case Eco2_User:
720 layerColor = m_boardAdapter.m_ECO2Color;
721 break;
722
723 case B_CrtYd:
724 case F_CrtYd:
725 break;
726
727 case B_Fab:
728 case F_Fab:
729 break;
730
731 default:
732 {
733 int layer3D = MapPCBLayerTo3DLayer( layer_id );
734
735 // Note: MUST do this in LAYER_3D space; User_1..User_45 are NOT contiguous
736 if( layer3D >= LAYER_3D_USER_1 && layer3D <= LAYER_3D_USER_45 )
737 {
738 layerColor = m_boardAdapter.m_UserDefinedLayerColor[ layer3D - LAYER_3D_USER_1 ];
739 }
740 else if( m_boardAdapter.m_Cfg->m_Render.differentiate_plated_copper )
741 {
742 layerColor = SFVEC3F( 184.0f / 255.0f, 115.0f / 255.0f, 50.0f / 255.0f );
743 materialLayer = &m_materials.m_NonPlatedCopper;
744 }
745 else
746 {
747 layerColor = m_boardAdapter.m_CopperColor;
748 materialLayer = &m_materials.m_Copper;
749 }
750
751 break;
752 }
753 }
754
755 createItemsFromContainer( container2d, layer_id, materialLayer, layerColor, 0.0f );
756 } // for each layer on map
757
758 // Create plated copper
759 if( m_boardAdapter.m_Cfg->m_Render.differentiate_plated_copper )
760 {
761 createItemsFromContainer( m_boardAdapter.GetPlatedPadsFront(), F_Cu, &m_materials.m_Copper,
762 m_boardAdapter.m_CopperColor,
763 m_boardAdapter.GetFrontCopperThickness() * 0.1f );
764
765 createItemsFromContainer( m_boardAdapter.GetPlatedPadsBack(), B_Cu, &m_materials.m_Copper,
766 m_boardAdapter.m_CopperColor,
767 -m_boardAdapter.GetBackCopperThickness() * 0.1f );
768 }
769
770 if( !aOnlyLoadCopperAndShapes )
771 {
772 // Add Mask layer
773 // Solder mask layers are "negative" layers so the elements that we have in the container
774 // should remove the board outline. We will check for all objects in the outline if it
775 // intersects any object in the layer container and also any hole.
776 if( ( layerFlags.test( LAYER_3D_SOLDERMASK_TOP )
777 || layerFlags.test( LAYER_3D_SOLDERMASK_BOTTOM ) )
778 && !m_outlineBoard2dObjects->GetList().empty() )
779 {
780 const MATERIAL* materialLayer = &m_materials.m_SolderMask;
781
782 for( const std::pair<const PCB_LAYER_ID, BVH_CONTAINER_2D*>& entry :
783 m_boardAdapter.GetLayerMap() )
784 {
785 const PCB_LAYER_ID layer_id = entry.first;
786 const BVH_CONTAINER_2D* container2d = entry.second;
787
788 // Only process layers that exist
789 if( !container2d )
790 continue;
791
792 // Only get the Solder mask layers (and only if the board has them)
793 if( layer_id == F_Mask && !layerFlags.test( LAYER_3D_SOLDERMASK_TOP ) )
794 continue;
795
796 if( layer_id == B_Mask && !layerFlags.test( LAYER_3D_SOLDERMASK_BOTTOM ) )
797 continue;
798
799 // Only Mask layers are processed here because they are negative layers
800 if( layer_id != F_Mask && layer_id != B_Mask )
801 continue;
802
803 SFVEC3F layerColor;
804
805 if( layer_id == B_Mask )
806 layerColor = m_boardAdapter.m_SolderMaskColorBot;
807 else
808 layerColor = m_boardAdapter.m_SolderMaskColorTop;
809
810 const float zLayerMin = m_boardAdapter.GetLayerBottomZPos( layer_id );
811 const float zLayerMax = m_boardAdapter.GetLayerTopZPos( layer_id );
812
813 // Get the outline board objects
814 for( const OBJECT_2D* object2d_A : m_outlineBoard2dObjects->GetList() )
815 {
816 std::vector<const OBJECT_2D*>* object2d_B = new std::vector<const OBJECT_2D*>();
817
818 // Check if there are any THT that intersects this outline object part
819 if( !m_boardAdapter.GetTH_ODs().GetList().empty() )
820 {
821 const BVH_CONTAINER_2D& throughHoles = m_boardAdapter.GetTH_ODs();
822 CONST_LIST_OBJECT2D intersecting;
823
824 throughHoles.GetIntersectingObjects( object2d_A->GetBBox(), intersecting );
825
826 for( const OBJECT_2D* hole : intersecting )
827 {
828 if( object2d_A->Intersects( hole->GetBBox() ) )
829 object2d_B->push_back( hole );
830 }
831 }
832
833 // Check if there are any objects in the layer to subtract with the current
834 // object
835 if( !container2d->GetList().empty() )
836 {
837 CONST_LIST_OBJECT2D intersecting;
838
839 container2d->GetIntersectingObjects( object2d_A->GetBBox(), intersecting );
840
841 for( const OBJECT_2D* obj : intersecting )
842 object2d_B->push_back( obj );
843 }
844
845 if( object2d_B->empty() )
846 {
847 delete object2d_B;
848 object2d_B = CSGITEM_EMPTY;
849 }
850
851 if( object2d_B == CSGITEM_EMPTY )
852 {
853#if 0
854 createObject( m_objectContainer, object2d_A, zLayerMin, zLayerMax,
855 materialLayer, layerColor );
856#else
857 LAYER_ITEM* objPtr = new LAYER_ITEM( object2d_A, zLayerMin, zLayerMax );
858
859 objPtr->SetMaterial( materialLayer );
860 objPtr->SetColor( ConvertSRGBToLinear( layerColor ) );
861
862 m_objectContainer.Add( objPtr );
863#endif
864 }
865 else
866 {
867 LAYER_ITEM_2D* itemCSG2d = new LAYER_ITEM_2D( object2d_A, object2d_B,
869 object2d_A->GetBoardItem() );
870
871 m_containerWithObjectsToDelete.Add( itemCSG2d );
872
873 LAYER_ITEM* objPtr = new LAYER_ITEM( itemCSG2d, zLayerMin, zLayerMax );
874 objPtr->SetMaterial( materialLayer );
875 objPtr->SetColor( ConvertSRGBToLinear( layerColor ) );
876
877 m_objectContainer.Add( objPtr );
878 }
879 }
880 }
881 }
882
884 }
885
886#ifdef PRINT_STATISTICS_3D_VIEWER
887 int64_t stats_endConvertTime = GetRunningMicroSecs();
888 int64_t stats_startLoad3DmodelsTime = stats_endConvertTime;
889#endif
890
891 if( aStatusReporter )
892 aStatusReporter->Report( _( "Loading 3D models..." ) );
893
894 load3DModels( m_objectContainer, aOnlyLoadCopperAndShapes );
895
896#ifdef PRINT_STATISTICS_3D_VIEWER
897 int64_t stats_endLoad3DmodelsTime = GetRunningMicroSecs();
898#endif
899
900 if( !aOnlyLoadCopperAndShapes )
901 {
902 // Add floor
903 if( m_boardAdapter.m_Cfg->m_Render.raytrace_backfloor )
904 {
905 BBOX_3D boardBBox = m_boardAdapter.GetBBox();
906
907 if( boardBBox.IsInitialized() )
908 {
909 boardBBox.Scale( 3.0f );
910
911 if( m_objectContainer.GetList().size() > 0 )
912 {
913 BBOX_3D containerBBox = m_objectContainer.GetBBox();
914
915 containerBBox.Scale( 1.3f );
916
917 const SFVEC3F centerBBox = containerBBox.GetCenter();
918
919 // Floor triangles
920 const float minZ = glm::min( containerBBox.Min().z, boardBBox.Min().z );
921
922 const SFVEC3F v1 =
923 SFVEC3F( -RANGE_SCALE_3D * 4.0f, -RANGE_SCALE_3D * 4.0f, minZ )
924 + SFVEC3F( centerBBox.x, centerBBox.y, 0.0f );
925
926 const SFVEC3F v3 =
927 SFVEC3F( +RANGE_SCALE_3D * 4.0f, +RANGE_SCALE_3D * 4.0f, minZ )
928 + SFVEC3F( centerBBox.x, centerBBox.y, 0.0f );
929
930 const SFVEC3F v2 = SFVEC3F( v1.x, v3.y, v1.z );
931 const SFVEC3F v4 = SFVEC3F( v3.x, v1.y, v1.z );
932
933 SFVEC3F floorColor = ConvertSRGBToLinear( m_boardAdapter.m_BgColorTop );
934
935 TRIANGLE* newTriangle1 = new TRIANGLE( v1, v2, v3 );
936 TRIANGLE* newTriangle2 = new TRIANGLE( v3, v4, v1 );
937
938 m_objectContainer.Add( newTriangle1 );
939 m_objectContainer.Add( newTriangle2 );
940
941 newTriangle1->SetMaterial( &m_materials.m_Floor );
942 newTriangle2->SetMaterial( &m_materials.m_Floor );
943
944 newTriangle1->SetColor( floorColor );
945 newTriangle2->SetColor( floorColor );
946
947 // Ceiling triangles
948 const float maxZ = glm::max( containerBBox.Max().z, boardBBox.Max().z );
949
950 const SFVEC3F v5 = SFVEC3F( v1.x, v1.y, maxZ );
951 const SFVEC3F v6 = SFVEC3F( v2.x, v2.y, maxZ );
952 const SFVEC3F v7 = SFVEC3F( v3.x, v3.y, maxZ );
953 const SFVEC3F v8 = SFVEC3F( v4.x, v4.y, maxZ );
954
955 TRIANGLE* newTriangle3 = new TRIANGLE( v7, v6, v5 );
956 TRIANGLE* newTriangle4 = new TRIANGLE( v5, v8, v7 );
957
958 m_objectContainer.Add( newTriangle3 );
959 m_objectContainer.Add( newTriangle4 );
960
961 newTriangle3->SetMaterial( &m_materials.m_Floor );
962 newTriangle4->SetMaterial( &m_materials.m_Floor );
963
964 newTriangle3->SetColor( floorColor );
965 newTriangle4->SetColor( floorColor );
966 }
967 }
968 }
969
970 // Init initial lights
971 for( LIGHT* light : m_lights )
972 delete light;
973
974 m_lights.clear();
975
976 auto IsColorZero =
977 []( const SFVEC3F& aSource )
978 {
979 return ( ( aSource.r < ( 1.0f / 255.0f ) ) && ( aSource.g < ( 1.0f / 255.0f ) )
980 && ( aSource.b < ( 1.0f / 255.0f ) ) );
981 };
982
983 SFVEC3F cameraLightColor =
984 m_boardAdapter.GetColor( m_boardAdapter.m_Cfg->m_Render.raytrace_lightColorCamera );
985 SFVEC3F topLightColor =
986 m_boardAdapter.GetColor( m_boardAdapter.m_Cfg->m_Render.raytrace_lightColorTop );
987 SFVEC3F bottomLightColor =
988 m_boardAdapter.GetColor( m_boardAdapter.m_Cfg->m_Render.raytrace_lightColorBottom );
989
990 m_cameraLight = new DIRECTIONAL_LIGHT( SFVEC3F( 0.0f, 0.0f, 0.0f ), cameraLightColor );
991 m_cameraLight->SetCastShadows( false );
992
993 if( !IsColorZero( cameraLightColor ) )
994 m_lights.push_back( m_cameraLight );
995
996 const SFVEC3F& boardCenter = m_boardAdapter.GetBBox().GetCenter();
997
998 if( !IsColorZero( topLightColor ) )
999 {
1000 m_lights.push_back( new POINT_LIGHT( SFVEC3F( boardCenter.x, boardCenter.y,
1001 +RANGE_SCALE_3D * 2.0f ),
1002 topLightColor ) );
1003 }
1004
1005 if( !IsColorZero( bottomLightColor ) )
1006 {
1007 m_lights.push_back( new POINT_LIGHT( SFVEC3F( boardCenter.x, boardCenter.y,
1008 -RANGE_SCALE_3D * 2.0f ),
1009 bottomLightColor ) );
1010 }
1011
1012 for( size_t i = 0; i < m_boardAdapter.m_Cfg->m_Render.raytrace_lightColor.size(); ++i )
1013 {
1014 SFVEC3F lightColor =
1015 m_boardAdapter.GetColor( m_boardAdapter.m_Cfg->m_Render.raytrace_lightColor[i] );
1016
1017 if( !IsColorZero( lightColor ) )
1018 {
1019 const SFVEC2F sc = m_boardAdapter.GetSphericalCoord( i );
1020
1021 m_lights.push_back( new DIRECTIONAL_LIGHT(
1022 SphericalToCartesian( glm::pi<float>() * sc.x, glm::pi<float>() * sc.y ),
1023 lightColor ) );
1024 }
1025 }
1026 }
1027
1028 // Set min. and max. zoom range. This doesn't really fit here, but moving this outside of this
1029 // class would require reimplementing bounding box calculation (feel free to do this if you
1030 // have time and patience).
1031 if( m_objectContainer.GetList().size() > 0 )
1032 {
1033 float ratio =
1034 std::max( 1.0f, m_objectContainer.GetBBox().GetMaxDimension() / RANGE_SCALE_3D );
1035
1036 float max_zoom = CAMERA::DEFAULT_MAX_ZOOM * ratio;
1037 float min_zoom = static_cast<float>( MIN_DISTANCE_IU * m_boardAdapter.BiuTo3dUnits()
1038 / -m_camera.GetCameraInitPos().z );
1039
1040 if( min_zoom > max_zoom )
1041 std::swap( min_zoom, max_zoom );
1042
1043 float zoom_ratio = max_zoom / min_zoom;
1044
1045 // Set the minimum number of zoom 'steps' between max and min.
1046 int steps = 3 * 3;
1047 steps -= static_cast<int>( ceil( log( zoom_ratio ) / log( 1.26f ) ) );
1048 steps = std::max( steps, 0 );
1049
1050 // Resize max and min zoom to accomplish the number of steps.
1051 float increased_zoom = pow( 1.26f, steps / 2 );
1052 max_zoom *= increased_zoom;
1053 min_zoom /= increased_zoom;
1054
1055 if( steps & 1 )
1056 min_zoom /= 1.26f;
1057
1058 min_zoom = std::min( min_zoom, 1.0f );
1059
1060 m_camera.SetMaxZoom( max_zoom );
1061 m_camera.SetMinZoom( min_zoom );
1062 }
1063
1064 // Create an accelerator
1065 delete m_accelerator;
1067
1068 if( aStatusReporter )
1069 {
1070 // Calculation time in seconds
1071 double calculation_time = (double) ( GetRunningMicroSecs() - stats_startReloadTime ) / 1e6;
1072
1073 aStatusReporter->Report( wxString::Format( _( "Reload time %.3f s" ), calculation_time ) );
1074 }
1075}
1076
1077
1079 const SFVEC2F& aCenter,
1080 float aInnerRadius, float aDepth,
1081 float aSurfaceZ, bool aIsFront )
1082{
1083 const float platingThickness = m_boardAdapter.GetHolePlatingThickness()
1084 * m_boardAdapter.BiuTo3dUnits();
1085
1086 if( platingThickness <= 0.0f || aInnerRadius <= 0.0f || aDepth <= 0.0f )
1087 return;
1088
1089 const float outerRadius = aInnerRadius + platingThickness;
1090 const float zOther = aIsFront ? ( aSurfaceZ - aDepth ) : ( aSurfaceZ + aDepth );
1091 const float zMin = std::min( aSurfaceZ, zOther );
1092 const float zMax = std::max( aSurfaceZ, zOther );
1093
1094 RING_2D* ring = new RING_2D( aCenter, aInnerRadius, outerRadius, aSource );
1096
1097 LAYER_ITEM* objPtr = new LAYER_ITEM( ring, zMin, zMax );
1098 objPtr->SetMaterial( &m_materials.m_Copper );
1099 objPtr->SetColor( ConvertSRGBToLinear( m_boardAdapter.m_CopperColor ) );
1100
1101 m_objectContainer.Add( objPtr );
1102}
1103
1104
1106 float aTopInnerRadius,
1107 float aBottomInnerRadius,
1108 float aSurfaceZ, float aDepth,
1109 bool aIsFront )
1110{
1111 const float platingThickness = m_boardAdapter.GetHolePlatingThickness()
1112 * m_boardAdapter.BiuTo3dUnits();
1113
1114 if( platingThickness <= 0.0f || aTopInnerRadius <= 0.0f || aBottomInnerRadius <= 0.0f
1115 || aDepth <= 0.0f )
1116 {
1117 return;
1118 }
1119
1120 const float topOuterRadius = aTopInnerRadius + platingThickness;
1121 const float bottomOuterRadius = aBottomInnerRadius + platingThickness;
1122
1123 const float zOther = aIsFront ? ( aSurfaceZ - aDepth ) : ( aSurfaceZ + aDepth );
1124 const float zTop = std::max( aSurfaceZ, zOther );
1125 const float zBot = std::min( aSurfaceZ, zOther );
1126
1127 if( topOuterRadius <= 0.0f || bottomOuterRadius <= 0.0f )
1128 return;
1129
1130 const float largestDiameter = 2.0f * std::max( aTopInnerRadius, aBottomInnerRadius );
1131 unsigned int segments = std::max( 12u, m_boardAdapter.GetCircleSegmentCount( largestDiameter ) );
1132
1133 const SFVEC3F copperColor = ConvertSRGBToLinear( m_boardAdapter.m_CopperColor );
1134
1135 auto addQuad = [&]( const SFVEC3F& p0, const SFVEC3F& p1,
1136 const SFVEC3F& p2, const SFVEC3F& p3 )
1137 {
1138 TRIANGLE* tri1 = new TRIANGLE( p0, p1, p2 );
1139 TRIANGLE* tri2 = new TRIANGLE( p0, p2, p3 );
1140
1141 tri1->SetMaterial( &m_materials.m_Copper );
1142 tri2->SetMaterial( &m_materials.m_Copper );
1143 tri1->SetColor( copperColor );
1144 tri2->SetColor( copperColor );
1145
1146 m_objectContainer.Add( tri1 );
1147 m_objectContainer.Add( tri2 );
1148 };
1149
1150 auto makePoint = [&]( float radius, float angle, float z )
1151 {
1152 return SFVEC3F( aCenter.x + cosf( angle ) * radius,
1153 aCenter.y + sinf( angle ) * radius,
1154 z );
1155 };
1156
1157 const float step = 2.0f * glm::pi<float>() / (float) segments;
1158
1159 SFVEC3F innerTopPrev = makePoint( aTopInnerRadius, 0.0f, zTop );
1160 SFVEC3F innerBotPrev = makePoint( aBottomInnerRadius, 0.0f, zBot );
1161 SFVEC3F outerTopPrev = makePoint( topOuterRadius, 0.0f, zTop );
1162 SFVEC3F outerBotPrev = makePoint( bottomOuterRadius, 0.0f, zBot );
1163
1164 const SFVEC3F innerTopFirst = innerTopPrev;
1165 const SFVEC3F innerBotFirst = innerBotPrev;
1166 const SFVEC3F outerTopFirst = outerTopPrev;
1167 const SFVEC3F outerBotFirst = outerBotPrev;
1168
1169 for( unsigned int i = 1; i <= segments; ++i )
1170 {
1171 const float angle = ( i == segments ) ? 0.0f : step * i;
1172
1173 const SFVEC3F innerTopCurr = ( i == segments ) ? innerTopFirst
1174 : makePoint( aTopInnerRadius, angle, zTop );
1175 const SFVEC3F innerBotCurr = ( i == segments ) ? innerBotFirst
1176 : makePoint( aBottomInnerRadius, angle, zBot );
1177 const SFVEC3F outerTopCurr = ( i == segments ) ? outerTopFirst
1178 : makePoint( topOuterRadius, angle, zTop );
1179 const SFVEC3F outerBotCurr = ( i == segments ) ? outerBotFirst
1180 : makePoint( bottomOuterRadius, angle, zBot );
1181
1182 // Inner wall
1183 addQuad( innerTopPrev, innerTopCurr, innerBotCurr, innerBotPrev );
1184
1185 // Outer wall
1186 addQuad( outerTopPrev, outerBotPrev, outerBotCurr, outerTopCurr );
1187
1188 // Top rim
1189 addQuad( outerTopPrev, outerTopCurr, innerTopCurr, innerTopPrev );
1190
1191 // Bottom rim
1192 addQuad( outerBotPrev, innerBotPrev, innerBotCurr, outerBotCurr );
1193
1194 innerTopPrev = innerTopCurr;
1195 innerBotPrev = innerBotCurr;
1196 outerTopPrev = outerTopCurr;
1197 outerBotPrev = outerBotCurr;
1198 }
1199}
1200
1201
1203{
1204 if( !m_boardAdapter.GetBoard() )
1205 return;
1206
1207 const float unitScale = m_boardAdapter.BiuTo3dUnits();
1208 const int platingThickness = m_boardAdapter.GetHolePlatingThickness();
1209 const float platingThickness3d = platingThickness * unitScale;
1210 const SFVEC3F boardColor = ConvertSRGBToLinear( m_boardAdapter.m_BoardBodyColor );
1211
1212 const float boardZTop = m_boardAdapter.GetLayerBottomZPos( F_Cu );
1213 const float boardZBot = m_boardAdapter.GetLayerBottomZPos( B_Cu );
1214
1215 // Process vias for backdrill and post-machining plugs
1216 for( const PCB_TRACK* track : m_boardAdapter.GetBoard()->Tracks() )
1217 {
1218 if( track->Type() != PCB_VIA_T )
1219 continue;
1220
1221 const PCB_VIA* via = static_cast<const PCB_VIA*>( track );
1222
1223 const float holeDiameter = via->GetDrillValue() * unitScale;
1224 const float holeInnerRadius = holeDiameter / 2.0f;
1225 const float holeOuterRadius = holeInnerRadius + platingThickness3d;
1226 const SFVEC2F center( via->GetStart().x * unitScale, -via->GetStart().y * unitScale );
1227
1228 PCB_LAYER_ID topLayer, bottomLayer;
1229 via->LayerPair( &topLayer, &bottomLayer );
1230
1231 const float viaZTop = m_boardAdapter.GetLayerBottomZPos( topLayer );
1232 const float viaZBot = m_boardAdapter.GetLayerBottomZPos( bottomLayer );
1233
1234 // Handle backdrill plugs
1235 const auto secondaryDrillSize = via->GetSecondaryDrillSize();
1236
1237 if( secondaryDrillSize.has_value() && secondaryDrillSize.value() > 0 )
1238 {
1239 const float backdrillRadius = secondaryDrillSize.value() * 0.5f * unitScale;
1240
1241 if( backdrillRadius > holeOuterRadius )
1242 {
1243 PCB_LAYER_ID secStart = via->GetSecondaryDrillStartLayer();
1244 PCB_LAYER_ID secEnd = via->GetSecondaryDrillEndLayer();
1245
1246 // Calculate where the backdrill ends and plug should start
1247 const float secEndZ = m_boardAdapter.GetLayerBottomZPos( secEnd );
1248
1249 float plugZTop, plugZBot;
1250
1251 if( secStart == F_Cu )
1252 {
1253 // Backdrill from top: plug goes from below backdrill end to via bottom
1254 plugZTop = secEndZ;
1255 plugZBot = viaZBot;
1256 }
1257 else
1258 {
1259 // Backdrill from bottom: plug goes from via top to above backdrill end
1260 plugZTop = viaZTop;
1261 plugZBot = secEndZ;
1262 }
1263
1264 if( plugZTop > plugZBot )
1265 {
1266 // Create a ring from holeOuterRadius to backdrillRadius
1267 RING_2D* ring = new RING_2D( center, holeOuterRadius, backdrillRadius, *via );
1269
1270 LAYER_ITEM* objPtr = new LAYER_ITEM( ring, plugZBot, plugZTop );
1271 objPtr->SetMaterial( &m_materials.m_EpoxyBoard );
1272 objPtr->SetColor( boardColor );
1273 m_objectContainer.Add( objPtr );
1274 }
1275 }
1276 }
1277
1278 // Handle front post-machining plugs
1279 const auto frontMode = via->GetFrontPostMachining();
1280
1281 if( frontMode.has_value()
1283 && frontMode.value() != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
1284 {
1285 const float frontRadius = via->GetFrontPostMachiningSize() * 0.5f * unitScale;
1286 const float frontDepth = via->GetFrontPostMachiningDepth() * unitScale;
1287
1288 if( frontRadius > holeOuterRadius && frontDepth > 0 )
1289 {
1290 // Plug goes from bottom of post-machining to bottom of via
1291 const float pmBottomZ = viaZTop - frontDepth;
1292 const float plugZBot = viaZBot;
1293
1294 if( pmBottomZ > plugZBot )
1295 {
1296 if( frontMode.value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
1297 {
1298 // For countersink, use a frustum (truncated cone)
1299 EDA_ANGLE angle( via->GetFrontPostMachiningAngle(), TENTHS_OF_A_DEGREE_T );
1300 float angleRad = angle.AsRadians();
1301 if( angleRad < 0.01f )
1302 angleRad = 0.01f;
1303
1304 float radialDiff = frontRadius - holeOuterRadius;
1305 float innerHeight = radialDiff / tanf( angleRad );
1306 float totalHeight = pmBottomZ - plugZBot;
1307
1308 if( innerHeight > totalHeight )
1309 innerHeight = totalHeight;
1310
1311 float zInnerTop = plugZBot + innerHeight;
1312
1313 // Create frustum from holeOuterRadius at zInnerTop to frontRadius at pmBottomZ
1314 TRUNCATED_CONE* frustum = new TRUNCATED_CONE( center, zInnerTop, pmBottomZ,
1315 holeOuterRadius, frontRadius );
1316 frustum->SetMaterial( &m_materials.m_EpoxyBoard );
1317 frustum->SetColor( boardColor );
1318 m_objectContainer.Add( frustum );
1319
1320 // If there's a cylindrical portion below the cone
1321 if( zInnerTop > plugZBot )
1322 {
1323 RING_2D* ring = new RING_2D( center, holeOuterRadius, frontRadius, *via );
1325
1326 LAYER_ITEM* objPtr = new LAYER_ITEM( ring, plugZBot, zInnerTop );
1327 objPtr->SetMaterial( &m_materials.m_EpoxyBoard );
1328 objPtr->SetColor( boardColor );
1329 m_objectContainer.Add( objPtr );
1330 }
1331 }
1332 else
1333 {
1334 RING_2D* ring = new RING_2D( center, holeOuterRadius, frontRadius, *via );
1336
1337 LAYER_ITEM* objPtr = new LAYER_ITEM( ring, plugZBot, pmBottomZ );
1338 objPtr->SetMaterial( &m_materials.m_EpoxyBoard );
1339 objPtr->SetColor( boardColor );
1340 m_objectContainer.Add( objPtr );
1341 }
1342 }
1343 }
1344 }
1345
1346 // Handle back post-machining plugs
1347 const auto backMode = via->GetBackPostMachining();
1348
1349 if( backMode.has_value()
1351 && backMode.value() != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
1352 {
1353 const float backRadius = via->GetBackPostMachiningSize() * 0.5f * unitScale;
1354 const float backDepth = via->GetBackPostMachiningDepth() * unitScale;
1355
1356 if( backRadius > holeOuterRadius && backDepth > 0 )
1357 {
1358 // Plug goes from top of via to top of post-machining
1359 const float plugZTop = viaZTop;
1360 const float pmTopZ = viaZBot + backDepth;
1361
1362 if( plugZTop > pmTopZ )
1363 {
1364 if( backMode.value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
1365 {
1366 // For countersink, use a frustum (truncated cone)
1367 EDA_ANGLE angle( via->GetBackPostMachiningAngle(), TENTHS_OF_A_DEGREE_T );
1368 float angleRad = angle.AsRadians();
1369 if( angleRad < 0.01f )
1370 angleRad = 0.01f;
1371
1372 float radialDiff = backRadius - holeOuterRadius;
1373 float innerHeight = radialDiff / tanf( angleRad );
1374 float totalHeight = plugZTop - pmTopZ;
1375
1376 if( innerHeight > totalHeight )
1377 innerHeight = totalHeight;
1378
1379 float zInnerBot = plugZTop - innerHeight;
1380
1381 // Create frustum from holeOuterRadius at zInnerBot to backRadius at pmTopZ
1382 TRUNCATED_CONE* frustum = new TRUNCATED_CONE( center, pmTopZ, zInnerBot,
1383 backRadius, holeOuterRadius );
1384 frustum->SetMaterial( &m_materials.m_EpoxyBoard );
1385 frustum->SetColor( boardColor );
1386 m_objectContainer.Add( frustum );
1387
1388 // If there's a cylindrical portion above the cone
1389 if( zInnerBot < plugZTop )
1390 {
1391 RING_2D* ring = new RING_2D( center, holeOuterRadius, backRadius, *via );
1393
1394 LAYER_ITEM* objPtr = new LAYER_ITEM( ring, zInnerBot, plugZTop );
1395 objPtr->SetMaterial( &m_materials.m_EpoxyBoard );
1396 objPtr->SetColor( boardColor );
1397 m_objectContainer.Add( objPtr );
1398 }
1399 }
1400 else
1401 {
1402 RING_2D* ring = new RING_2D( center, holeOuterRadius, backRadius, *via );
1404
1405 LAYER_ITEM* objPtr = new LAYER_ITEM( ring, pmTopZ, plugZTop );
1406 objPtr->SetMaterial( &m_materials.m_EpoxyBoard );
1407 objPtr->SetColor( boardColor );
1408 m_objectContainer.Add( objPtr );
1409 }
1410 }
1411 }
1412 }
1413 }
1414
1415 // Process pads for post-machining plugs
1416 for( const FOOTPRINT* footprint : m_boardAdapter.GetBoard()->Footprints() )
1417 {
1418 for( const PAD* pad : footprint->Pads() )
1419 {
1420 if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
1421 continue;
1422
1423 if( !pad->HasHole() )
1424 continue;
1425
1426 if( pad->GetDrillShape() != PAD_DRILL_SHAPE::CIRCLE )
1427 continue;
1428
1429 const SFVEC2F padCenter( pad->GetPosition().x * unitScale,
1430 -pad->GetPosition().y * unitScale );
1431 const float holeInnerRadius = pad->GetDrillSize().x * 0.5f * unitScale;
1432 const float holeOuterRadius = holeInnerRadius + platingThickness3d;
1433
1434 const float padZTop = boardZTop;
1435 const float padZBot = boardZBot;
1436
1437 // Handle front post-machining plugs for pads
1438 const auto frontMode = pad->GetFrontPostMachining();
1439
1440 if( frontMode.has_value()
1442 && frontMode.value() != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
1443 {
1444 const float frontRadius = pad->GetFrontPostMachiningSize() * 0.5f * unitScale;
1445 const float frontDepth = pad->GetFrontPostMachiningDepth() * unitScale;
1446
1447 if( frontRadius > holeOuterRadius && frontDepth > 0 )
1448 {
1449 const float pmBottomZ = padZTop - frontDepth;
1450 const float plugZBot = padZBot;
1451
1452 if( pmBottomZ > plugZBot )
1453 {
1454 if( frontMode.value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
1455 {
1456 // For countersink, use a frustum (truncated cone)
1457 EDA_ANGLE angle( pad->GetFrontPostMachiningAngle(), TENTHS_OF_A_DEGREE_T );
1458 float angleRad = angle.AsRadians();
1459 if( angleRad < 0.01f )
1460 angleRad = 0.01f;
1461
1462 float radialDiff = frontRadius - holeOuterRadius;
1463 float innerHeight = radialDiff / tanf( angleRad );
1464 float totalHeight = pmBottomZ - plugZBot;
1465
1466 if( innerHeight > totalHeight )
1467 innerHeight = totalHeight;
1468
1469 float zInnerTop = plugZBot + innerHeight;
1470
1471 // Create frustum from holeOuterRadius at zInnerTop to frontRadius at pmBottomZ
1472 TRUNCATED_CONE* frustum = new TRUNCATED_CONE( padCenter, zInnerTop, pmBottomZ,
1473 holeOuterRadius, frontRadius );
1474 frustum->SetMaterial( &m_materials.m_EpoxyBoard );
1475 frustum->SetColor( boardColor );
1476 m_objectContainer.Add( frustum );
1477
1478 // If there's a cylindrical portion below the cone
1479 if( zInnerTop > plugZBot )
1480 {
1481 RING_2D* ring = new RING_2D( padCenter, holeOuterRadius, frontRadius, *pad );
1483
1484 LAYER_ITEM* objPtr = new LAYER_ITEM( ring, plugZBot, zInnerTop );
1485 objPtr->SetMaterial( &m_materials.m_EpoxyBoard );
1486 objPtr->SetColor( boardColor );
1487 m_objectContainer.Add( objPtr );
1488 }
1489 }
1490 else
1491 {
1492 RING_2D* ring = new RING_2D( padCenter, holeOuterRadius, frontRadius, *pad );
1494
1495 LAYER_ITEM* objPtr = new LAYER_ITEM( ring, plugZBot, pmBottomZ );
1496 objPtr->SetMaterial( &m_materials.m_EpoxyBoard );
1497 objPtr->SetColor( boardColor );
1498 m_objectContainer.Add( objPtr );
1499 }
1500 }
1501 }
1502 }
1503
1504 // Handle back post-machining plugs for pads
1505 const auto backMode = pad->GetBackPostMachining();
1506
1507 if( backMode.has_value()
1509 && backMode.value() != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
1510 {
1511 const float backRadius = pad->GetBackPostMachiningSize() * 0.5f * unitScale;
1512 const float backDepth = pad->GetBackPostMachiningDepth() * unitScale;
1513
1514 if( backRadius > holeOuterRadius && backDepth > 0 )
1515 {
1516 const float plugZTop = padZTop;
1517 const float pmTopZ = padZBot + backDepth;
1518
1519 if( plugZTop > pmTopZ )
1520 {
1521 if( backMode.value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
1522 {
1523 // For countersink, use a frustum (truncated cone)
1524 EDA_ANGLE angle( pad->GetBackPostMachiningAngle(), TENTHS_OF_A_DEGREE_T );
1525 float angleRad = angle.AsRadians();
1526 if( angleRad < 0.01f )
1527 angleRad = 0.01f;
1528
1529 float radialDiff = backRadius - holeOuterRadius;
1530 float innerHeight = radialDiff / tanf( angleRad );
1531 float totalHeight = plugZTop - pmTopZ;
1532
1533 if( innerHeight > totalHeight )
1534 innerHeight = totalHeight;
1535
1536 float zInnerBot = plugZTop - innerHeight;
1537
1538 // Create frustum from holeOuterRadius at zInnerBot to backRadius at pmTopZ
1539 TRUNCATED_CONE* frustum = new TRUNCATED_CONE( padCenter, pmTopZ, zInnerBot,
1540 backRadius, holeOuterRadius );
1541 frustum->SetMaterial( &m_materials.m_EpoxyBoard );
1542 frustum->SetColor( boardColor );
1543 m_objectContainer.Add( frustum );
1544
1545 // If there's a cylindrical portion above the cone
1546 if( zInnerBot < plugZTop )
1547 {
1548 RING_2D* ring = new RING_2D( padCenter, holeOuterRadius, backRadius, *pad );
1550
1551 LAYER_ITEM* objPtr = new LAYER_ITEM( ring, zInnerBot, plugZTop );
1552 objPtr->SetMaterial( &m_materials.m_EpoxyBoard );
1553 objPtr->SetColor( boardColor );
1554 m_objectContainer.Add( objPtr );
1555 }
1556 }
1557 else
1558 {
1559 RING_2D* ring = new RING_2D( padCenter, holeOuterRadius, backRadius, *pad );
1561
1562 LAYER_ITEM* objPtr = new LAYER_ITEM( ring, pmTopZ, plugZTop );
1563 objPtr->SetMaterial( &m_materials.m_EpoxyBoard );
1564 objPtr->SetColor( boardColor );
1565 m_objectContainer.Add( objPtr );
1566 }
1567 }
1568 }
1569 }
1570 }
1571 }
1572}
1573
1574
1576{
1577 PCB_LAYER_ID top_layer, bottom_layer;
1578 int radiusBUI = ( aVia->GetDrillValue() / 2 );
1579
1580 aVia->LayerPair( &top_layer, &bottom_layer );
1581
1582 float frontDepth = 0.0f;
1583 float backDepth = 0.0f;
1584
1586 frontDepth = aVia->Padstack().FrontPostMachining().depth * m_boardAdapter.BiuTo3dUnits();
1587
1589 backDepth = aVia->Padstack().BackPostMachining().depth * m_boardAdapter.BiuTo3dUnits();
1590
1591 float topZ = m_boardAdapter.GetLayerBottomZPos( top_layer )
1592 + m_boardAdapter.GetFrontCopperThickness() - frontDepth;
1593
1594 float botZ = m_boardAdapter.GetLayerBottomZPos( bottom_layer )
1595 - m_boardAdapter.GetBackCopperThickness() + backDepth;
1596
1597 const float unitScale = m_boardAdapter.BiuTo3dUnits();
1598 const SFVEC2F center = SFVEC2F( aVia->GetStart().x * unitScale, -aVia->GetStart().y * unitScale );
1599
1600 RING_2D* ring = new RING_2D( center, radiusBUI * unitScale,
1601 ( radiusBUI + m_boardAdapter.GetHolePlatingThickness() ) * unitScale, *aVia );
1602
1604
1605 LAYER_ITEM* objPtr = new LAYER_ITEM( ring, topZ, botZ );
1606
1607 objPtr->SetMaterial( &m_materials.m_Copper );
1608 objPtr->SetColor( ConvertSRGBToLinear( m_boardAdapter.m_CopperColor ) );
1609
1610 m_objectContainer.Add( objPtr );
1611
1612 const float holeInnerRadius = radiusBUI * unitScale;
1613 const float frontSurface = topZ + frontDepth;
1614 const float backSurface = botZ - backDepth;
1615
1616 const PAD_DRILL_POST_MACHINING_MODE frontMode =
1618 const float frontRadius = 0.5f * aVia->GetFrontPostMachiningSize() * unitScale;
1619
1620 if( frontDepth > 0.0f && frontRadius > holeInnerRadius )
1621 {
1623 {
1624 addCounterborePlating( *aVia, center, frontRadius, frontDepth, frontSurface, true );
1625 }
1626 else if( frontMode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
1627 {
1628 addCountersinkPlating( center, frontRadius, holeInnerRadius, frontSurface, frontDepth,
1629 true );
1630 }
1631 }
1632
1633 const PAD_DRILL_POST_MACHINING_MODE backMode =
1635 const float backRadius = 0.5f * aVia->GetBackPostMachiningSize() * unitScale;
1636
1637 if( backDepth > 0.0f && backRadius > holeInnerRadius )
1638 {
1640 {
1641 addCounterborePlating( *aVia, center, backRadius, backDepth, backSurface, false );
1642 }
1643 else if( backMode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
1644 {
1645 addCountersinkPlating( center, backRadius, holeInnerRadius, backSurface, backDepth,
1646 false );
1647 }
1648 }
1649}
1650
1651
1653{
1654 const OBJECT_2D* object2d_A = nullptr;
1655
1656 SFVEC3F objColor = m_boardAdapter.m_CopperColor;
1657 const VECTOR2I drillsize = aPad->GetDrillSize();
1658 const bool hasHole = drillsize.x && drillsize.y;
1659 const float unitScale = m_boardAdapter.BiuTo3dUnits();
1660 const bool isRoundHole = drillsize.x == drillsize.y;
1661 SFVEC2F holeCenter = SFVEC2F( 0.0f, 0.0f );
1662 float holeInnerRadius = 0.0f;
1663
1664 if( !hasHole )
1665 return;
1666
1667 CONST_LIST_OBJECT2D antiOutlineIntersectionList;
1668
1669 float frontDepth = 0.0f;
1670 float backDepth = 0.0f;
1671
1673 frontDepth = aPad->GetFrontPostMachiningDepth() * m_boardAdapter.BiuTo3dUnits();
1674
1676 backDepth = aPad->GetBackPostMachiningDepth() * m_boardAdapter.BiuTo3dUnits();
1677
1678 const float topZ = m_boardAdapter.GetLayerBottomZPos( F_Cu )
1679 + m_boardAdapter.GetFrontCopperThickness() * 0.99f - frontDepth;
1680
1681 const float botZ = m_boardAdapter.GetLayerBottomZPos( B_Cu )
1682 - m_boardAdapter.GetBackCopperThickness() * 0.99f + backDepth;
1683
1684 if( isRoundHole ) // usual round hole
1685 {
1686 holeCenter = SFVEC2F( aPad->GetPosition().x * unitScale,
1687 -aPad->GetPosition().y * unitScale );
1688
1689 int innerRadius = drillsize.x / 2;
1690 int outerRadius = innerRadius + m_boardAdapter.GetHolePlatingThickness();
1691 holeInnerRadius = innerRadius * unitScale;
1692
1693 RING_2D* ring = new RING_2D( holeCenter, innerRadius * unitScale,
1694 outerRadius * unitScale, *aPad );
1695
1697
1698 object2d_A = ring;
1699
1700 // If the object (ring) is intersected by an antioutline board,
1701 // it will use instead a CSG of two circles.
1702 if( object2d_A && !m_antioutlineBoard2dObjects->GetList().empty() )
1703 {
1704 m_antioutlineBoard2dObjects->GetIntersectingObjects( object2d_A->GetBBox(),
1705 antiOutlineIntersectionList );
1706 }
1707
1708 if( !antiOutlineIntersectionList.empty() )
1709 {
1710 FILLED_CIRCLE_2D* innerCircle = new FILLED_CIRCLE_2D(
1711 holeCenter, innerRadius * unitScale, *aPad );
1712
1713 FILLED_CIRCLE_2D* outterCircle = new FILLED_CIRCLE_2D(
1714 holeCenter, outerRadius * unitScale, *aPad );
1715 std::vector<const OBJECT_2D*>* object2d_B = new std::vector<const OBJECT_2D*>();
1716 object2d_B->push_back( innerCircle );
1717
1718 LAYER_ITEM_2D* itemCSG2d = new LAYER_ITEM_2D( outterCircle, object2d_B, CSGITEM_FULL,
1719 *aPad );
1720
1721 m_containerWithObjectsToDelete.Add( itemCSG2d );
1722 m_containerWithObjectsToDelete.Add( innerCircle );
1723 m_containerWithObjectsToDelete.Add( outterCircle );
1724
1725 object2d_A = itemCSG2d;
1726 }
1727 }
1728 else // Oblong hole
1729 {
1730 VECTOR2I ends_offset;
1731 int width;
1732
1733 if( drillsize.x > drillsize.y ) // Horizontal oval
1734 {
1735 ends_offset.x = ( drillsize.x - drillsize.y ) / 2;
1736 width = drillsize.y;
1737 }
1738 else // Vertical oval
1739 {
1740 ends_offset.y = ( drillsize.y - drillsize.x ) / 2;
1741 width = drillsize.x;
1742 }
1743
1744 RotatePoint( ends_offset, aPad->GetOrientation() );
1745
1746 VECTOR2I start = VECTOR2I( aPad->GetPosition() ) + ends_offset;
1747 VECTOR2I end = VECTOR2I( aPad->GetPosition() ) - ends_offset;
1748
1749 ROUND_SEGMENT_2D* innerSeg =
1750 new ROUND_SEGMENT_2D( SFVEC2F( start.x * unitScale,
1751 -start.y * unitScale ),
1752 SFVEC2F( end.x * unitScale,
1753 -end.y * unitScale ),
1754 width * unitScale, *aPad );
1755
1756 ROUND_SEGMENT_2D* outerSeg =
1757 new ROUND_SEGMENT_2D( SFVEC2F( start.x * unitScale,
1758 -start.y * unitScale ),
1759 SFVEC2F( end.x * unitScale,
1760 -end.y * unitScale ),
1761 ( width + m_boardAdapter.GetHolePlatingThickness() * 2 )
1762 * unitScale, *aPad );
1763
1764 // NOTE: the round segment width is the "diameter", so we double the thickness
1765 std::vector<const OBJECT_2D*>* object2d_B = new std::vector<const OBJECT_2D*>();
1766 object2d_B->push_back( innerSeg );
1767
1768 LAYER_ITEM_2D* itemCSG2d = new LAYER_ITEM_2D( outerSeg, object2d_B, CSGITEM_FULL, *aPad );
1769
1770 m_containerWithObjectsToDelete.Add( itemCSG2d );
1771 m_containerWithObjectsToDelete.Add( innerSeg );
1772 m_containerWithObjectsToDelete.Add( outerSeg );
1773
1774 object2d_A = itemCSG2d;
1775
1776 if( object2d_A && !m_antioutlineBoard2dObjects->GetList().empty() )
1777 {
1778 m_antioutlineBoard2dObjects->GetIntersectingObjects( object2d_A->GetBBox(),
1779 antiOutlineIntersectionList );
1780 }
1781 }
1782
1783 if( object2d_A )
1784 {
1785 std::vector<const OBJECT_2D*>* object2d_B = new std::vector<const OBJECT_2D*>();
1786
1787 // Check if there are any other THT that intersects this hole
1788 // It will use the non inflated holes
1789 if( !m_boardAdapter.GetTH_IDs().GetList().empty() )
1790 {
1791 CONST_LIST_OBJECT2D intersecting;
1792
1793 m_boardAdapter.GetTH_IDs().GetIntersectingObjects( object2d_A->GetBBox(), intersecting );
1794
1795 for( const OBJECT_2D* hole2d : intersecting )
1796 {
1797 if( object2d_A->Intersects( hole2d->GetBBox() ) )
1798 object2d_B->push_back( hole2d );
1799 }
1800 }
1801
1802 for( const OBJECT_2D* obj : antiOutlineIntersectionList )
1803 object2d_B->push_back( obj );
1804
1805 if( object2d_B->empty() )
1806 {
1807 delete object2d_B;
1808 object2d_B = CSGITEM_EMPTY;
1809 }
1810
1811 if( object2d_B == CSGITEM_EMPTY )
1812 {
1813 LAYER_ITEM* objPtr = new LAYER_ITEM( object2d_A, topZ, botZ );
1814
1815 objPtr->SetMaterial( &m_materials.m_Copper );
1816 objPtr->SetColor( ConvertSRGBToLinear( objColor ) );
1817 m_objectContainer.Add( objPtr );
1818 }
1819 else
1820 {
1821 LAYER_ITEM_2D* itemCSG2d = new LAYER_ITEM_2D( object2d_A, object2d_B, CSGITEM_FULL,
1822 *aPad );
1823
1824 m_containerWithObjectsToDelete.Add( itemCSG2d );
1825
1826 LAYER_ITEM* objPtr = new LAYER_ITEM( itemCSG2d, topZ, botZ );
1827
1828 objPtr->SetMaterial( &m_materials.m_Copper );
1829 objPtr->SetColor( ConvertSRGBToLinear( objColor ) );
1830
1831 m_objectContainer.Add( objPtr );
1832 }
1833 }
1834
1835 if( object2d_A && isRoundHole )
1836 {
1837 const float frontSurface = topZ + frontDepth;
1838 const float backSurface = botZ - backDepth;
1839
1840 const PAD_DRILL_POST_MACHINING_MODE frontMode =
1842 const float frontRadius = 0.5f * aPad->GetFrontPostMachiningSize() * unitScale;
1843
1844 if( frontDepth > 0.0f && frontRadius > holeInnerRadius )
1845 {
1847 {
1848 addCounterborePlating( *aPad, holeCenter, frontRadius, frontDepth, frontSurface,
1849 true );
1850 }
1851 else if( frontMode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
1852 {
1853 addCountersinkPlating( holeCenter, frontRadius, holeInnerRadius, frontSurface,
1854 frontDepth, true );
1855 }
1856 }
1857
1858 const PAD_DRILL_POST_MACHINING_MODE backMode =
1860 const float backRadius = 0.5f * aPad->GetBackPostMachiningSize() * unitScale;
1861
1862 if( backDepth > 0.0f && backRadius > holeInnerRadius )
1863 {
1865 {
1866 addCounterborePlating( *aPad, holeCenter, backRadius, backDepth, backSurface,
1867 false );
1868 }
1869 else if( backMode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
1870 {
1871 addCountersinkPlating( holeCenter, backRadius, holeInnerRadius, backSurface,
1872 backDepth, false );
1873 }
1874 }
1875 }
1876}
1877
1878
1880{
1881 if( !m_boardAdapter.GetBoard() )
1882 return;
1883
1884 // Insert plated vertical holes inside the board
1885
1886 // Insert vias holes (vertical cylinders)
1887 for( PCB_TRACK* track : m_boardAdapter.GetBoard()->Tracks() )
1888 {
1889 if( track->Type() == PCB_VIA_T )
1890 {
1891 const PCB_VIA* via = static_cast<const PCB_VIA*>( track );
1892 insertHole( via );
1893 }
1894 }
1895
1896 // Insert pads holes (vertical cylinders)
1897 for( FOOTPRINT* footprint : m_boardAdapter.GetBoard()->Footprints() )
1898 {
1899 for( PAD* pad : footprint->Pads() )
1900 {
1901 if( pad->GetAttribute() != PAD_ATTRIB::NPTH )
1902 insertHole( pad );
1903 }
1904 }
1905}
1906
1907
1909 bool aSkipMaterialInformation )
1910{
1911 if( !m_boardAdapter.GetBoard() )
1912 return;
1913
1914 if( !m_boardAdapter.m_IsPreviewer
1915 && !m_boardAdapter.m_Cfg->m_Render.show_footprints_normal
1916 && !m_boardAdapter.m_Cfg->m_Render.show_footprints_insert
1917 && !m_boardAdapter.m_Cfg->m_Render.show_footprints_virtual )
1918 {
1919 return;
1920 }
1921
1922 const wxString currentVariant = m_boardAdapter.GetBoard()->GetCurrentVariant();
1923
1924 // Go for all footprints
1925 for( FOOTPRINT* fp : m_boardAdapter.GetBoard()->Footprints() )
1926 {
1927 if( !fp->Models().empty()
1928 && m_boardAdapter.IsFootprintShown( fp ) )
1929 {
1930 // Skip 3D models for footprints that are DNP in the current variant
1931 if( fp->GetDNPForVariant( currentVariant ) )
1932 continue;
1933
1934 double zpos = m_boardAdapter.GetFootprintZPos( fp->IsFlipped() );
1935
1936 VECTOR2I pos = fp->GetPosition();
1937
1938 glm::mat4 fpMatrix = glm::mat4( 1.0f );
1939
1940 fpMatrix = glm::translate( fpMatrix,
1941 SFVEC3F( pos.x * m_boardAdapter.BiuTo3dUnits(),
1942 -pos.y * m_boardAdapter.BiuTo3dUnits(),
1943 zpos ) );
1944
1945 if( !fp->GetOrientation().IsZero() )
1946 {
1947 fpMatrix = glm::rotate( fpMatrix, (float) fp->GetOrientation().AsRadians(),
1948 SFVEC3F( 0.0f, 0.0f, 1.0f ) );
1949 }
1950
1951 if( fp->IsFlipped() )
1952 {
1953 fpMatrix = glm::rotate( fpMatrix, glm::pi<float>(), SFVEC3F( 0.0f, 1.0f, 0.0f ) );
1954
1955 fpMatrix = glm::rotate( fpMatrix, glm::pi<float>(), SFVEC3F( 0.0f, 0.0f, 1.0f ) );
1956 }
1957
1958 const double modelunit_to_3d_units_factor =
1959 m_boardAdapter.BiuTo3dUnits() * UNITS3D_TO_UNITSPCB;
1960
1961 fpMatrix = glm::scale(
1962 fpMatrix, SFVEC3F( modelunit_to_3d_units_factor, modelunit_to_3d_units_factor,
1963 modelunit_to_3d_units_factor ) );
1964
1965 // Get the list of model files for this model
1966 S3D_CACHE* cacheMgr = m_boardAdapter.Get3dCacheManager();
1967
1968 wxString libraryName = fp->GetFPID().GetLibNickname();
1969
1970 wxString footprintBasePath = wxEmptyString;
1971
1972 if( m_boardAdapter.GetBoard()->GetProject() )
1973 {
1974 try
1975 {
1976 // FindRow() can throw an exception
1977 std::optional<LIBRARY_TABLE_ROW*> fpRow =
1978 PROJECT_PCB::FootprintLibAdapter( m_boardAdapter.GetBoard()->GetProject() )
1979 ->GetRow( libraryName );
1980
1981 if( fpRow )
1982 footprintBasePath = LIBRARY_MANAGER::GetFullURI( *fpRow, true );
1983 }
1984 catch( ... )
1985 {
1986 // Do nothing if the libraryName is not found in lib table
1987 }
1988 }
1989
1990 for( FP_3DMODEL& model : fp->Models() )
1991 {
1992 if( !model.m_Show || model.m_Filename.empty() )
1993 continue;
1994
1995 // get it from cache
1996 std::vector<const EMBEDDED_FILES*> embeddedFilesStack;
1997 embeddedFilesStack.push_back( fp->GetEmbeddedFiles() );
1998 embeddedFilesStack.push_back( m_boardAdapter.GetBoard()->GetEmbeddedFiles() );
1999
2000 const S3DMODEL* modelPtr = cacheMgr->GetModel( model.m_Filename, footprintBasePath,
2001 std::move( embeddedFilesStack ) );
2002
2003 // only add it if the return is not NULL.
2004 if( modelPtr )
2005 {
2006 glm::mat4 modelMatrix = fpMatrix;
2007
2008 modelMatrix = glm::translate( modelMatrix,
2009 SFVEC3F( model.m_Offset.x, model.m_Offset.y, model.m_Offset.z ) );
2010
2011 modelMatrix = glm::rotate( modelMatrix,
2012 (float) -( model.m_Rotation.z / 180.0f ) * glm::pi<float>(),
2013 SFVEC3F( 0.0f, 0.0f, 1.0f ) );
2014
2015 modelMatrix = glm::rotate( modelMatrix,
2016 (float) -( model.m_Rotation.y / 180.0f ) * glm::pi<float>(),
2017 SFVEC3F( 0.0f, 1.0f, 0.0f ) );
2018
2019 modelMatrix = glm::rotate( modelMatrix,
2020 (float) -( model.m_Rotation.x / 180.0f ) * glm::pi<float>(),
2021 SFVEC3F( 1.0f, 0.0f, 0.0f ) );
2022
2023 modelMatrix = glm::scale( modelMatrix,
2024 SFVEC3F( model.m_Scale.x, model.m_Scale.y, model.m_Scale.z ) );
2025
2026 addModels( aDstContainer, modelPtr, modelMatrix, (float) model.m_Opacity,
2027 aSkipMaterialInformation, fp );
2028 }
2029 }
2030 }
2031 }
2032}
2033
2034
2036{
2037 MODEL_MATERIALS* materialVector;
2038
2039 // Try find if the materials already exists in the map list
2040 if( m_modelMaterialMap.find( a3DModel ) != m_modelMaterialMap.end() )
2041 {
2042 // Found it, so get the pointer
2043 materialVector = &m_modelMaterialMap[a3DModel];
2044 }
2045 else
2046 {
2047 // Materials was not found in the map, so it will create a new for
2048 // this model.
2049
2050 m_modelMaterialMap[a3DModel] = MODEL_MATERIALS();
2051 materialVector = &m_modelMaterialMap[a3DModel];
2052
2053 materialVector->resize( a3DModel->m_MaterialsSize );
2054
2055 for( unsigned int imat = 0; imat < a3DModel->m_MaterialsSize; ++imat )
2056 {
2057 if( m_boardAdapter.m_Cfg->m_Render.material_mode == MATERIAL_MODE::NORMAL )
2058 {
2059 const SMATERIAL& material = a3DModel->m_Materials[imat];
2060
2061 // http://www.fooplot.com/#W3sidHlwZSI6MCwiZXEiOiJtaW4oc3FydCh4LTAuMzUpKjAuNDAtMC4wNSwxLjApIiwiY29sb3IiOiIjMDAwMDAwIn0seyJ0eXBlIjoxMDAwLCJ3aW5kb3ciOlsiMC4wNzA3NzM2NzMyMzY1OTAxMiIsIjEuNTY5NTcxNjI5MjI1NDY5OCIsIi0wLjI3NDYzNTMyMTc1OTkyOTMiLCIwLjY0NzcwMTg4MTkyNTUzNjIiXSwic2l6ZSI6WzY0NCwzOTRdfV0-
2062
2063 float reflectionFactor = 0.0f;
2064
2065 if( ( material.m_Shininess - 0.35f ) > FLT_EPSILON )
2066 {
2067 reflectionFactor = glm::clamp(
2068 glm::sqrt( ( material.m_Shininess - 0.35f ) ) * 0.40f - 0.05f, 0.0f,
2069 0.5f );
2070 }
2071
2072 BLINN_PHONG_MATERIAL& blinnMaterial = ( *materialVector )[imat];
2073
2074 blinnMaterial = BLINN_PHONG_MATERIAL( ConvertSRGBToLinear( material.m_Ambient ),
2075 ConvertSRGBToLinear( material.m_Emissive ),
2076 ConvertSRGBToLinear( material.m_Specular ), material.m_Shininess * 180.0f,
2077 material.m_Transparency, reflectionFactor );
2078
2079 if( m_boardAdapter.m_Cfg->m_Render.raytrace_procedural_textures )
2080 {
2081 // Guess material type and apply a normal perturbator
2082 if( ( RGBtoGray( material.m_Diffuse ) < 0.3f )
2083 && ( material.m_Shininess < 0.36f )
2084 && ( material.m_Transparency == 0.0f )
2085 && ( ( glm::abs( material.m_Diffuse.r - material.m_Diffuse.g ) < 0.15f )
2086 && ( glm::abs( material.m_Diffuse.b - material.m_Diffuse.g )
2087 < 0.15f )
2088 && ( glm::abs( material.m_Diffuse.r - material.m_Diffuse.b )
2089 < 0.15f ) ) )
2090 {
2091 // This may be a black plastic..
2092 blinnMaterial.SetGenerator( &m_plasticMaterial );
2093 }
2094 else
2095 {
2096 if( ( RGBtoGray( material.m_Diffuse ) > 0.3f )
2097 && ( material.m_Shininess < 0.30f )
2098 && ( material.m_Transparency == 0.0f )
2099 && ( ( glm::abs( material.m_Diffuse.r - material.m_Diffuse.g ) > 0.25f )
2100 || ( glm::abs( material.m_Diffuse.b - material.m_Diffuse.g ) > 0.25f )
2101 || ( glm::abs( material.m_Diffuse.r - material.m_Diffuse.b )
2102 > 0.25f ) ) )
2103 {
2104 // This may be a color plastic ...
2105 blinnMaterial.SetGenerator( &m_shinyPlasticMaterial );
2106 }
2107 else
2108 {
2109 if( ( RGBtoGray( material.m_Diffuse ) > 0.6f )
2110 && ( material.m_Shininess > 0.35f )
2111 && ( material.m_Transparency == 0.0f )
2112 && ( ( glm::abs( material.m_Diffuse.r - material.m_Diffuse.g )
2113 < 0.40f )
2114 && ( glm::abs( material.m_Diffuse.b - material.m_Diffuse.g )
2115 < 0.40f )
2116 && ( glm::abs( material.m_Diffuse.r - material.m_Diffuse.b )
2117 < 0.40f ) ) )
2118 {
2119 // This may be a brushed metal
2120 blinnMaterial.SetGenerator( &m_brushedMetalMaterial );
2121 }
2122 }
2123 }
2124 }
2125 }
2126 else
2127 {
2128 ( *materialVector )[imat] = BLINN_PHONG_MATERIAL(
2129 SFVEC3F( 0.2f ), SFVEC3F( 0.0f ), SFVEC3F( 0.0f ), 0.0f, 0.0f, 0.0f );
2130 }
2131 }
2132 }
2133
2134 return materialVector;
2135}
2136
2137
2138void RENDER_3D_RAYTRACE_BASE::addModels( CONTAINER_3D& aDstContainer, const S3DMODEL* a3DModel,
2139 const glm::mat4& aModelMatrix, float aFPOpacity,
2140 bool aSkipMaterialInformation, BOARD_ITEM* aBoardItem )
2141{
2142 // Validate a3DModel pointers
2143 wxASSERT( a3DModel != nullptr );
2144
2145 if( a3DModel == nullptr )
2146 return;
2147
2148 wxASSERT( a3DModel->m_Materials != nullptr );
2149 wxASSERT( a3DModel->m_Meshes != nullptr );
2150 wxASSERT( a3DModel->m_MaterialsSize > 0 );
2151 wxASSERT( a3DModel->m_MeshesSize > 0 );
2152
2153 if( aFPOpacity > 1.0f )
2154 aFPOpacity = 1.0f;
2155
2156 if( aFPOpacity < 0.0f )
2157 aFPOpacity = 0.0f;
2158
2159 if( ( a3DModel->m_Materials != nullptr ) && ( a3DModel->m_Meshes != nullptr )
2160 && ( a3DModel->m_MaterialsSize > 0 ) && ( a3DModel->m_MeshesSize > 0 ) )
2161 {
2162 MODEL_MATERIALS* materialVector = nullptr;
2163
2164 if( !aSkipMaterialInformation )
2165 {
2166 materialVector = getModelMaterial( a3DModel );
2167 }
2168
2169 const glm::mat3 normalMatrix = glm::transpose( glm::inverse( glm::mat3( aModelMatrix ) ) );
2170
2171 for( unsigned int mesh_i = 0; mesh_i < a3DModel->m_MeshesSize; ++mesh_i )
2172 {
2173 const SMESH& mesh = a3DModel->m_Meshes[mesh_i];
2174
2175 // Validate the mesh pointers
2176 wxASSERT( mesh.m_Positions != nullptr );
2177 wxASSERT( mesh.m_FaceIdx != nullptr );
2178 wxASSERT( mesh.m_Normals != nullptr );
2179 wxASSERT( mesh.m_FaceIdxSize > 0 );
2180 wxASSERT( ( mesh.m_FaceIdxSize % 3 ) == 0 );
2181
2182
2183 if( ( mesh.m_Positions != nullptr ) && ( mesh.m_Normals != nullptr )
2184 && ( mesh.m_FaceIdx != nullptr ) && ( mesh.m_FaceIdxSize > 0 )
2185 && ( mesh.m_VertexSize > 0 ) && ( ( mesh.m_FaceIdxSize % 3 ) == 0 )
2186 && ( mesh.m_MaterialIdx < a3DModel->m_MaterialsSize ) )
2187 {
2188 float fpTransparency;
2189 const BLINN_PHONG_MATERIAL* blinn_material;
2190
2191 if( !aSkipMaterialInformation )
2192 {
2193 blinn_material = &( *materialVector )[mesh.m_MaterialIdx];
2194
2195 fpTransparency =
2196 1.0f - ( ( 1.0f - blinn_material->GetTransparency() ) * aFPOpacity );
2197 }
2198
2199 // Add all face triangles
2200 for( unsigned int faceIdx = 0; faceIdx < mesh.m_FaceIdxSize; faceIdx += 3 )
2201 {
2202 const unsigned int idx0 = mesh.m_FaceIdx[faceIdx + 0];
2203 const unsigned int idx1 = mesh.m_FaceIdx[faceIdx + 1];
2204 const unsigned int idx2 = mesh.m_FaceIdx[faceIdx + 2];
2205
2206 wxASSERT( idx0 < mesh.m_VertexSize );
2207 wxASSERT( idx1 < mesh.m_VertexSize );
2208 wxASSERT( idx2 < mesh.m_VertexSize );
2209
2210 if( ( idx0 < mesh.m_VertexSize ) && ( idx1 < mesh.m_VertexSize )
2211 && ( idx2 < mesh.m_VertexSize ) )
2212 {
2213 const SFVEC3F& v0 = mesh.m_Positions[idx0];
2214 const SFVEC3F& v1 = mesh.m_Positions[idx1];
2215 const SFVEC3F& v2 = mesh.m_Positions[idx2];
2216
2217 const SFVEC3F& n0 = mesh.m_Normals[idx0];
2218 const SFVEC3F& n1 = mesh.m_Normals[idx1];
2219 const SFVEC3F& n2 = mesh.m_Normals[idx2];
2220
2221 // Transform vertex with the model matrix
2222 const SFVEC3F vt0 = SFVEC3F( aModelMatrix * glm::vec4( v0, 1.0f ) );
2223 const SFVEC3F vt1 = SFVEC3F( aModelMatrix * glm::vec4( v1, 1.0f ) );
2224 const SFVEC3F vt2 = SFVEC3F( aModelMatrix * glm::vec4( v2, 1.0f ) );
2225
2226 const SFVEC3F nt0 = glm::normalize( SFVEC3F( normalMatrix * n0 ) );
2227 const SFVEC3F nt1 = glm::normalize( SFVEC3F( normalMatrix * n1 ) );
2228 const SFVEC3F nt2 = glm::normalize( SFVEC3F( normalMatrix * n2 ) );
2229
2230 TRIANGLE* newTriangle = new TRIANGLE( vt0, vt2, vt1, nt0, nt2, nt1 );
2231
2232 newTriangle->SetBoardItem( aBoardItem );
2233
2234 aDstContainer.Add( newTriangle );
2235
2236 if( !aSkipMaterialInformation )
2237 {
2238 newTriangle->SetMaterial( blinn_material );
2239 newTriangle->SetModelTransparency( fpTransparency );
2240
2241 if( mesh.m_Color == nullptr )
2242 {
2243 const SFVEC3F diffuseColor =
2244 a3DModel->m_Materials[mesh.m_MaterialIdx].m_Diffuse;
2245
2246 if( m_boardAdapter.m_Cfg->m_Render.material_mode == MATERIAL_MODE::CAD_MODE )
2247 newTriangle->SetColor( ConvertSRGBToLinear(
2248 MaterialDiffuseToColorCAD( diffuseColor ) ) );
2249 else
2250 newTriangle->SetColor( ConvertSRGBToLinear( diffuseColor ) );
2251 }
2252 else
2253 {
2254 if( m_boardAdapter.m_Cfg->m_Render.material_mode == MATERIAL_MODE::CAD_MODE )
2255 {
2256 newTriangle->SetColor(
2258 mesh.m_Color[idx0] ) ),
2260 mesh.m_Color[idx1] ) ),
2262 mesh.m_Color[idx2] ) ) );
2263 }
2264 else
2265 {
2266 newTriangle->SetColor(
2267 ConvertSRGBToLinear( mesh.m_Color[idx0] ),
2268 ConvertSRGBToLinear( mesh.m_Color[idx1] ),
2269 ConvertSRGBToLinear( mesh.m_Color[idx2] ) );
2270 }
2271 }
2272 }
2273 }
2274 }
2275 }
2276 }
2277 }
2278}
@ NORMAL
Use all material properties from model file.
Definition 3d_enums.h:72
@ CAD_MODE
Use a gray shading based on diffuse material.
Definition 3d_enums.h:74
Defines math related functions.
float NextFloatDown(float v)
float NextFloatUp(float v)
Defines math related functions.
float RGBtoGray(const SFVEC3F &aColor)
Definition 3d_math.h:140
SFVEC3F MaterialDiffuseToColorCAD(const SFVEC3F &aDiffuseColor)
Definition 3d_math.h:147
SFVEC3F SphericalToCartesian(float aInclination, float aAzimuth)
https://en.wikipedia.org/wiki/Spherical_coordinate_system
Definition 3d_math.h:43
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:112
std::map< PCB_LAYER_ID, BVH_CONTAINER_2D * > MAP_CONTAINER_2D_BASE
A type that stores a container of 2d objects for each layer id.
#define RANGE_SCALE_3D
This defines the range that all coord will have to be rendered.
This BVH implementation is based on the source code implementation from the book "Physically Based Re...
Blinn Phong based material https://en.wikipedia.org/wiki/Blinn%E2%80%93Phong_shading_model.
Definition material.h:379
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:83
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:322
Procedural generation of the shiny brushed metal.
Definition material.h:195
void GetIntersectingObjects(const BBOX_2D &aBBox, CONST_LIST_OBJECT2D &aOutList) const override
Get a list of objects that intersects a bounding box.
static const float DEFAULT_MAX_ZOOM
Definition camera.h:106
const LIST_OBJECT2D & GetList() const
void Add(OBJECT_3D *aObject)
Procedural generation of the copper normals.
Definition material.h:76
A vertical cylinder.
Definition cylinder_3d.h:38
void SetColor(SFVEC3F aObjColor)
Definition cylinder_3d.h:48
A light source based only on a directional vector.
Definition light.h:116
double AsRadians() const
Definition eda_angle.h:120
Make solid geometry for objects on layers.
void SetColor(SFVEC3F aObjColor)
std::optional< LIBRARY_TABLE_ROW * > GetRow(const wxString &aNickname, LIBRARY_TABLE_SCOPE aScope=LIBRARY_TABLE_SCOPE::BOTH) const
Like LIBRARY_MANAGER::GetRow but filtered to the LIBRARY_TABLE_TYPE of this adapter.
std::optional< wxString > GetFullURI(LIBRARY_TABLE_TYPE aType, const wxString &aNickname, bool aSubstituted=false) const
Return the full location specifying URI for the LIB, either in original UI form or in environment var...
A base light class to derive to implement other light classes.
Definition light.h:41
Base material class that can be used to derive other material implementations.
Definition material.h:240
static void SetDefaultReflectionRayCount(unsigned int aCount)
Definition material.h:247
static void SetDefaultRefractionRayCount(unsigned int aCount)
Definition material.h:242
static void SetDefaultRefractionRecursionCount(unsigned int aCount)
Definition material.h:252
void SetGenerator(const MATERIAL_GENERATOR *aGenerator)
Definition material.h:328
float GetTransparency() const
Definition material.h:273
static void SetDefaultReflectionRecursionCount(unsigned int aCount)
Definition material.h:257
static OBJECT_2D_STATS & Instance()
Definition object_2d.h:137
void ResetStats()
Definition object_2d.h:122
virtual bool Intersects(const BBOX_2D &aBBox) const =0
a.Intersects(b) ⇔ !a.Disjoint(b) ⇔ !(a ∩ b = ∅)
const BBOX_2D & GetBBox() const
Definition object_2d.h:103
const BOARD_ITEM & GetBoardItem() const
Definition object_2d.h:66
OBJECT_2D_TYPE GetObjectType() const
Definition object_2d.h:107
static OBJECT_3D_STATS & Instance()
Definition object_3d.h:132
void ResetStats()
Definition object_3d.h:115
void SetMaterial(const MATERIAL *aMaterial)
Definition object_3d.h:58
void SetModelTransparency(float aModelTransparency)
Definition object_3d.h:66
void SetBoardItem(BOARD_ITEM *aBoardItem)
Definition object_3d.h:55
POST_MACHINING_PROPS & FrontPostMachining()
Definition padstack.h:357
POST_MACHINING_PROPS & BackPostMachining()
Definition padstack.h:360
Definition pad.h:55
int GetBackPostMachiningSize() const
Definition pad.h:478
const VECTOR2I & GetDrillSize() const
Definition pad.h:317
VECTOR2I GetPosition() const override
Definition pad.h:209
int GetFrontPostMachiningSize() const
Definition pad.h:458
int GetFrontPostMachiningDepth() const
Definition pad.h:460
std::optional< PAD_DRILL_POST_MACHINING_MODE > GetFrontPostMachining() const
Definition pad.h:445
EDA_ANGLE GetOrientation() const
Return the rotation angle of the pad.
Definition pad.h:420
std::optional< PAD_DRILL_POST_MACHINING_MODE > GetBackPostMachining() const
Definition pad.h:465
int GetBackPostMachiningDepth() const
Definition pad.h:480
const VECTOR2I & GetStart() const
Definition pcb_track.h:154
int GetFrontPostMachiningSize() const
Definition pcb_track.h:750
const PADSTACK & Padstack() const
Definition pcb_track.h:463
std::optional< PAD_DRILL_POST_MACHINING_MODE > GetFrontPostMachining() const
Definition pcb_track.h:737
int GetBackPostMachiningSize() const
Definition pcb_track.h:770
int GetDrillValue() const
Calculate the drill value for vias (m_drill if > 0, or default drill value for the board).
std::optional< PAD_DRILL_POST_MACHINING_MODE > GetBackPostMachining() const
Definition pcb_track.h:757
void LayerPair(PCB_LAYER_ID *top_layer, PCB_LAYER_ID *bottom_layer) const
Return the 2 layers used by the via (the via actually uses all layers between these 2 layers)
Procedural generation of the plastic normals.
Definition material.h:146
Procedural generation of the shiny plastic normals.
Definition material.h:170
Point light source based on http://ogldev.atspace.co.uk/www/tutorial20/tutorial20....
Definition light.h:71
static FOOTPRINT_LIBRARY_ADAPTER * FootprintLibAdapter(PROJECT *aProject)
BOARD_ADAPTER & m_boardAdapter
Settings reference in use for this render.
SILK_SCREEN_NORMAL m_silkScreenMaterial
struct RENDER_3D_RAYTRACE_BASE::@013206213056006125230376122042346155134272177300 m_materials
void Reload(REPORTER *aStatusReporter, REPORTER *aWarningReporter, bool aOnlyLoadCopperAndShapes)
void addCountersinkPlating(const SFVEC2F &aCenter, float aTopInnerRadius, float aBottomInnerRadius, float aSurfaceZ, float aDepth, bool aIsFront)
void load3DModels(CONTAINER_3D &aDstContainer, bool aSkipMaterialInformation)
void insertHole(const PCB_VIA *aVia)
BRUSHED_METAL_NORMAL m_brushedMetalMaterial
static constexpr float MIN_DISTANCE_IU
PLATED_COPPER_NORMAL m_platedCopperMaterial
void createItemsFromContainer(const BVH_CONTAINER_2D *aContainer2d, PCB_LAYER_ID aLayer_id, const MATERIAL *aMaterialLayer, const SFVEC3F &aLayerColor, float aLayerZOffset)
PLASTIC_SHINE_NORMAL m_shinyPlasticMaterial
void addCounterborePlating(const BOARD_ITEM &aSource, const SFVEC2F &aCenter, float aInnerRadius, float aDepth, float aSurfaceZ, bool aIsFront)
BVH_CONTAINER_2D * m_antioutlineBoard2dObjects
CONTAINER_2D m_containerWithObjectsToDelete
Store the list of created objects special for RT that will be clear in the end.
MODEL_MATERIALS * getModelMaterial(const S3DMODEL *a3DModel)
SOLDER_MASK_NORMAL m_solderMaskMaterial
void addModels(CONTAINER_3D &aDstContainer, const S3DMODEL *a3DModel, const glm::mat4 &aModelMatrix, float aFPOpacity, bool aSkipMaterialInformation, BOARD_ITEM *aBoardItem)
MAP_MODEL_MATERIALS m_modelMaterialMap
Stores materials of the 3D models.
void createObject(CONTAINER_3D &aDstContainer, const OBJECT_2D *aObject2D, float aZMin, float aZMax, const MATERIAL *aMaterial, const SFVEC3F &aObjColor)
Create one or more 3D objects form a 2D object and Z positions.
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:73
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Report a string with a given severity.
Definition reporter.h:102
void SetColor(SFVEC3F aObjColor)
Cache for storing the 3D shapes.
Definition 3d_cache.h:55
S3DMODEL * GetModel(const wxString &aModelFileName, const wxString &aBasePath, std::vector< const EMBEDDED_FILES * > aEmbeddedFilesStack)
Attempt to load the scene data for a model and to translate it into an S3D_MODEL structure for displa...
Definition 3d_cache.cpp:551
Represent a set of closed polygons.
void Fracture()
Convert a set of polygons with holes to a single outline with "slits"/"fractures" connecting the oute...
int OutlineCount() const
Return the number of outlines in the set.
void BooleanSubtract(const SHAPE_POLY_SET &b)
Perform boolean polyset difference.
Procedural generation of the solder mask.
Definition material.h:126
A triangle object.
Definition triangle_3d.h:43
void SetColor(const SFVEC3F &aColor)
A vertical truncated cone with different radii at top and bottom.
Definition frustum_3d.h:39
void SetColor(SFVEC3F aObjColor)
Definition frustum_3d.h:51
A plane that is parallel to XY plane.
Definition plane_3d.h:38
void SetColor(SFVEC3F aObjColor)
Definition plane_3d.h:49
std::list< OBJECT_2D * > LIST_OBJECT2D
std::list< const OBJECT_2D * > CONST_LIST_OBJECT2D
#define _(s)
#define UNITS3D_TO_UNITSPCB
Implements a model viewer canvas.
Declaration of the eda_3d_viewer class.
@ TENTHS_OF_A_DEGREE_T
Definition eda_angle.h:30
A truncated cone for raytracing, used for countersink visualization.
int MapPCBLayerTo3DLayer(PCB_LAYER_ID aLayer)
Definition layer_id.cpp:334
@ LAYER_3D_USER_1
Definition layer_ids.h:567
@ LAYER_3D_SOLDERMASK_TOP
Definition layer_ids.h:560
@ LAYER_3D_SOLDERMASK_BOTTOM
Definition layer_ids.h:559
@ LAYER_3D_BOARD
Definition layer_ids.h:554
@ LAYER_3D_USER_45
Definition layer_ids.h:611
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:677
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
@ F_CrtYd
Definition layer_ids.h:116
@ B_Adhes
Definition layer_ids.h:103
@ Dwgs_User
Definition layer_ids.h:107
@ F_Paste
Definition layer_ids.h:104
@ Cmts_User
Definition layer_ids.h:108
@ F_Adhes
Definition layer_ids.h:102
@ B_Mask
Definition layer_ids.h:98
@ B_Cu
Definition layer_ids.h:65
@ Eco1_User
Definition layer_ids.h:109
@ F_Mask
Definition layer_ids.h:97
@ B_Paste
Definition layer_ids.h:105
@ F_Fab
Definition layer_ids.h:119
@ F_SilkS
Definition layer_ids.h:100
@ B_CrtYd
Definition layer_ids.h:115
@ Eco2_User
Definition layer_ids.h:110
@ B_SilkS
Definition layer_ids.h:101
@ F_Cu
Definition layer_ids.h:64
@ B_Fab
Definition layer_ids.h:118
#define CSGITEM_EMPTY
#define CSGITEM_FULL
PAD_DRILL_POST_MACHINING_MODE
Definition padstack.h:76
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:103
static wxColor copperColor(220, 180, 30)
void ConvertPolygonToBlocks(const SHAPE_POLY_SET &aMainPath, CONTAINER_2D_BASE &aDstContainer, float aBiuTo3dUnitsScale, float aDivFactor, const BOARD_ITEM &aBoardItem, int aPolyIndex)
Use a polygon in the format of the ClipperLib::Path and process it and create multiple 2d objects (PO...
int64_t GetRunningMicroSecs()
An alternate way to calculate an elapsed time (in microsecondes) to class PROF_COUNTER.
void buildBoardBoundingBoxPoly(const BOARD *aBoard, SHAPE_POLY_SET &aOutline)
Get the complete bounding box of the board (including all items).
static float TransparencyControl(float aGrayColorValue, float aTransparency)
Perform an interpolation step to easy control the transparency based on the gray color value and tran...
static float TransparencyControl(float aGrayColorValue, float aTransparency)
Attempt to control the transparency based on the gray value of the color.
SFVEC3F ConvertSRGBToLinear(const SFVEC3F &aSRGBcolor)
std::vector< BLINN_PHONG_MATERIAL > MODEL_MATERIALS
Vector of materials.
const SFVEC2F & Min() const
Definition bbox_2d.h:175
const SFVEC2F & Max() const
Definition bbox_2d.h:180
Manage a bounding box defined by two SFVEC3F min max points.
Definition bbox_3d.h:43
SFVEC3F GetCenter() const
Return the center point of the bounding box.
Definition bbox_3d.cpp:132
const SFVEC3F & Min() const
Return the minimum vertex pointer.
Definition bbox_3d.h:192
const SFVEC3F & Max() const
Return the maximum vertex pointer.
Definition bbox_3d.h:199
bool IsInitialized() const
Check if this bounding box is already initialized.
Definition bbox_3d.cpp:88
void Scale(float aScale)
Scales a bounding box by its center.
Definition bbox_3d.cpp:182
std::optional< PAD_DRILL_POST_MACHINING_MODE > mode
Definition padstack.h:281
Store the a model based on meshes and materials.
Definition c3dmodel.h:95
SMATERIAL * m_Materials
The materials list of this model.
Definition c3dmodel.h:100
unsigned int m_MeshesSize
Number of meshes in the array.
Definition c3dmodel.h:96
SMESH * m_Meshes
The meshes list of this model.
Definition c3dmodel.h:97
unsigned int m_MaterialsSize
Number of materials in the material array.
Definition c3dmodel.h:99
float m_Shininess
Definition c3dmodel.h:43
SFVEC3F m_Specular
Definition c3dmodel.h:42
SFVEC3F m_Ambient
Definition c3dmodel.h:39
float m_Transparency
1.0 is completely transparent, 0.0 completely opaque
Definition c3dmodel.h:44
SFVEC3F m_Emissive
Definition c3dmodel.h:41
SFVEC3F m_Diffuse
Default diffuse color if m_Color is NULL.
Definition c3dmodel.h:40
Per-vertex normal/color/texcoors structure.
Definition c3dmodel.h:81
unsigned int * m_FaceIdx
Triangle Face Indexes.
Definition c3dmodel.h:88
SFVEC3F * m_Normals
Vertex normals array.
Definition c3dmodel.h:84
unsigned int m_MaterialIdx
Material Index to be used in this mesh (must be < m_MaterialsSize )
Definition c3dmodel.h:89
unsigned int m_VertexSize
Number of vertex in the arrays.
Definition c3dmodel.h:82
unsigned int m_FaceIdxSize
Number of elements of the m_FaceIdx array.
Definition c3dmodel.h:87
SFVEC3F * m_Color
Vertex color array, can be NULL.
Definition c3dmodel.h:86
SFVEC3F * m_Positions
Vertex position array.
Definition c3dmodel.h:83
KIBIS_MODEL * model
VECTOR3I v1(5, 5, 5)
VECTOR2I center
int radius
VECTOR2I end
VECTOR2I v2(1, 0)
VECTOR2I v4(1, 1)
VECTOR2I v5(-70, -70)
VECTOR2I v3(-2, 1)
Implement a triangle ray intersection based on article http://www.flipcode.com/archives/Raytracing_To...
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
Definition trigo.cpp:229
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:97
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695
glm::vec2 SFVEC2F
Definition xv3d_types.h:42
glm::vec3 SFVEC3F
Definition xv3d_types.h:44