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