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