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 const wxString currentVariant = m_boardAdapter.GetBoard()->GetCurrentVariant();
1911
1912 // Go for all footprints
1913 for( FOOTPRINT* fp : m_boardAdapter.GetBoard()->Footprints() )
1914 {
1915 if( !fp->Models().empty()
1916 && m_boardAdapter.IsFootprintShown( fp ) )
1917 {
1918 // Skip 3D models for footprints that are DNP in the current variant
1919 if( fp->GetDNPForVariant( currentVariant ) )
1920 continue;
1921
1922 double zpos = m_boardAdapter.GetFootprintZPos( fp->IsFlipped() );
1923
1924 VECTOR2I pos = fp->GetPosition();
1925
1926 glm::mat4 fpMatrix = glm::mat4( 1.0f );
1927
1928 fpMatrix = glm::translate( fpMatrix,
1929 SFVEC3F( pos.x * m_boardAdapter.BiuTo3dUnits(),
1930 -pos.y * m_boardAdapter.BiuTo3dUnits(),
1931 zpos ) );
1932
1933 if( !fp->GetOrientation().IsZero() )
1934 {
1935 fpMatrix = glm::rotate( fpMatrix, (float) fp->GetOrientation().AsRadians(),
1936 SFVEC3F( 0.0f, 0.0f, 1.0f ) );
1937 }
1938
1939 if( fp->IsFlipped() )
1940 {
1941 fpMatrix = glm::rotate( fpMatrix, glm::pi<float>(), SFVEC3F( 0.0f, 1.0f, 0.0f ) );
1942
1943 fpMatrix = glm::rotate( fpMatrix, glm::pi<float>(), SFVEC3F( 0.0f, 0.0f, 1.0f ) );
1944 }
1945
1946 const double modelunit_to_3d_units_factor =
1947 m_boardAdapter.BiuTo3dUnits() * UNITS3D_TO_UNITSPCB;
1948
1949 fpMatrix = glm::scale(
1950 fpMatrix, SFVEC3F( modelunit_to_3d_units_factor, modelunit_to_3d_units_factor,
1951 modelunit_to_3d_units_factor ) );
1952
1953 // Get the list of model files for this model
1954 S3D_CACHE* cacheMgr = m_boardAdapter.Get3dCacheManager();
1955
1956 wxString libraryName = fp->GetFPID().GetLibNickname();
1957
1958 wxString footprintBasePath = wxEmptyString;
1959
1960 if( m_boardAdapter.GetBoard()->GetProject() )
1961 {
1962 try
1963 {
1964 // FindRow() can throw an exception
1965 std::optional<LIBRARY_TABLE_ROW*> fpRow =
1966 PROJECT_PCB::FootprintLibAdapter( m_boardAdapter.GetBoard()->GetProject() )
1967 ->GetRow( libraryName );
1968
1969 if( fpRow )
1970 footprintBasePath = LIBRARY_MANAGER::GetFullURI( *fpRow, true );
1971 }
1972 catch( ... )
1973 {
1974 // Do nothing if the libraryName is not found in lib table
1975 }
1976 }
1977
1978 for( FP_3DMODEL& model : fp->Models() )
1979 {
1980 if( !model.m_Show || model.m_Filename.empty() )
1981 continue;
1982
1983 // get it from cache
1984 std::vector<const EMBEDDED_FILES*> embeddedFilesStack;
1985 embeddedFilesStack.push_back( fp->GetEmbeddedFiles() );
1986 embeddedFilesStack.push_back( m_boardAdapter.GetBoard()->GetEmbeddedFiles() );
1987
1988 const S3DMODEL* modelPtr = cacheMgr->GetModel( model.m_Filename, footprintBasePath,
1989 std::move( embeddedFilesStack ) );
1990
1991 // only add it if the return is not NULL.
1992 if( modelPtr )
1993 {
1994 glm::mat4 modelMatrix = fpMatrix;
1995
1996 modelMatrix = glm::translate( modelMatrix,
1997 SFVEC3F( model.m_Offset.x, model.m_Offset.y, model.m_Offset.z ) );
1998
1999 modelMatrix = glm::rotate( modelMatrix,
2000 (float) -( model.m_Rotation.z / 180.0f ) * glm::pi<float>(),
2001 SFVEC3F( 0.0f, 0.0f, 1.0f ) );
2002
2003 modelMatrix = glm::rotate( modelMatrix,
2004 (float) -( model.m_Rotation.y / 180.0f ) * glm::pi<float>(),
2005 SFVEC3F( 0.0f, 1.0f, 0.0f ) );
2006
2007 modelMatrix = glm::rotate( modelMatrix,
2008 (float) -( model.m_Rotation.x / 180.0f ) * glm::pi<float>(),
2009 SFVEC3F( 1.0f, 0.0f, 0.0f ) );
2010
2011 modelMatrix = glm::scale( modelMatrix,
2012 SFVEC3F( model.m_Scale.x, model.m_Scale.y, model.m_Scale.z ) );
2013
2014 addModels( aDstContainer, modelPtr, modelMatrix, (float) model.m_Opacity,
2015 aSkipMaterialInformation, fp );
2016 }
2017 }
2018 }
2019 }
2020}
2021
2022
2024{
2025 MODEL_MATERIALS* materialVector;
2026
2027 // Try find if the materials already exists in the map list
2028 if( m_modelMaterialMap.find( a3DModel ) != m_modelMaterialMap.end() )
2029 {
2030 // Found it, so get the pointer
2031 materialVector = &m_modelMaterialMap[a3DModel];
2032 }
2033 else
2034 {
2035 // Materials was not found in the map, so it will create a new for
2036 // this model.
2037
2038 m_modelMaterialMap[a3DModel] = MODEL_MATERIALS();
2039 materialVector = &m_modelMaterialMap[a3DModel];
2040
2041 materialVector->resize( a3DModel->m_MaterialsSize );
2042
2043 for( unsigned int imat = 0; imat < a3DModel->m_MaterialsSize; ++imat )
2044 {
2045 if( m_boardAdapter.m_Cfg->m_Render.material_mode == MATERIAL_MODE::NORMAL )
2046 {
2047 const SMATERIAL& material = a3DModel->m_Materials[imat];
2048
2049 // http://www.fooplot.com/#W3sidHlwZSI6MCwiZXEiOiJtaW4oc3FydCh4LTAuMzUpKjAuNDAtMC4wNSwxLjApIiwiY29sb3IiOiIjMDAwMDAwIn0seyJ0eXBlIjoxMDAwLCJ3aW5kb3ciOlsiMC4wNzA3NzM2NzMyMzY1OTAxMiIsIjEuNTY5NTcxNjI5MjI1NDY5OCIsIi0wLjI3NDYzNTMyMTc1OTkyOTMiLCIwLjY0NzcwMTg4MTkyNTUzNjIiXSwic2l6ZSI6WzY0NCwzOTRdfV0-
2050
2051 float reflectionFactor = 0.0f;
2052
2053 if( ( material.m_Shininess - 0.35f ) > FLT_EPSILON )
2054 {
2055 reflectionFactor = glm::clamp(
2056 glm::sqrt( ( material.m_Shininess - 0.35f ) ) * 0.40f - 0.05f, 0.0f,
2057 0.5f );
2058 }
2059
2060 BLINN_PHONG_MATERIAL& blinnMaterial = ( *materialVector )[imat];
2061
2062 blinnMaterial = BLINN_PHONG_MATERIAL( ConvertSRGBToLinear( material.m_Ambient ),
2063 ConvertSRGBToLinear( material.m_Emissive ),
2064 ConvertSRGBToLinear( material.m_Specular ), material.m_Shininess * 180.0f,
2065 material.m_Transparency, reflectionFactor );
2066
2067 if( m_boardAdapter.m_Cfg->m_Render.raytrace_procedural_textures )
2068 {
2069 // Guess material type and apply a normal perturbator
2070 if( ( RGBtoGray( material.m_Diffuse ) < 0.3f )
2071 && ( material.m_Shininess < 0.36f )
2072 && ( material.m_Transparency == 0.0f )
2073 && ( ( glm::abs( material.m_Diffuse.r - material.m_Diffuse.g ) < 0.15f )
2074 && ( glm::abs( material.m_Diffuse.b - material.m_Diffuse.g )
2075 < 0.15f )
2076 && ( glm::abs( material.m_Diffuse.r - material.m_Diffuse.b )
2077 < 0.15f ) ) )
2078 {
2079 // This may be a black plastic..
2080 blinnMaterial.SetGenerator( &m_plasticMaterial );
2081 }
2082 else
2083 {
2084 if( ( RGBtoGray( material.m_Diffuse ) > 0.3f )
2085 && ( material.m_Shininess < 0.30f )
2086 && ( material.m_Transparency == 0.0f )
2087 && ( ( glm::abs( material.m_Diffuse.r - material.m_Diffuse.g ) > 0.25f )
2088 || ( glm::abs( material.m_Diffuse.b - material.m_Diffuse.g ) > 0.25f )
2089 || ( glm::abs( material.m_Diffuse.r - material.m_Diffuse.b )
2090 > 0.25f ) ) )
2091 {
2092 // This may be a color plastic ...
2093 blinnMaterial.SetGenerator( &m_shinyPlasticMaterial );
2094 }
2095 else
2096 {
2097 if( ( RGBtoGray( material.m_Diffuse ) > 0.6f )
2098 && ( material.m_Shininess > 0.35f )
2099 && ( material.m_Transparency == 0.0f )
2100 && ( ( glm::abs( material.m_Diffuse.r - material.m_Diffuse.g )
2101 < 0.40f )
2102 && ( glm::abs( material.m_Diffuse.b - material.m_Diffuse.g )
2103 < 0.40f )
2104 && ( glm::abs( material.m_Diffuse.r - material.m_Diffuse.b )
2105 < 0.40f ) ) )
2106 {
2107 // This may be a brushed metal
2108 blinnMaterial.SetGenerator( &m_brushedMetalMaterial );
2109 }
2110 }
2111 }
2112 }
2113 }
2114 else
2115 {
2116 ( *materialVector )[imat] = BLINN_PHONG_MATERIAL(
2117 SFVEC3F( 0.2f ), SFVEC3F( 0.0f ), SFVEC3F( 0.0f ), 0.0f, 0.0f, 0.0f );
2118 }
2119 }
2120 }
2121
2122 return materialVector;
2123}
2124
2125
2126void RENDER_3D_RAYTRACE_BASE::addModels( CONTAINER_3D& aDstContainer, const S3DMODEL* a3DModel,
2127 const glm::mat4& aModelMatrix, float aFPOpacity,
2128 bool aSkipMaterialInformation, BOARD_ITEM* aBoardItem )
2129{
2130 // Validate a3DModel pointers
2131 wxASSERT( a3DModel != nullptr );
2132
2133 if( a3DModel == nullptr )
2134 return;
2135
2136 wxASSERT( a3DModel->m_Materials != nullptr );
2137 wxASSERT( a3DModel->m_Meshes != nullptr );
2138 wxASSERT( a3DModel->m_MaterialsSize > 0 );
2139 wxASSERT( a3DModel->m_MeshesSize > 0 );
2140
2141 if( aFPOpacity > 1.0f )
2142 aFPOpacity = 1.0f;
2143
2144 if( aFPOpacity < 0.0f )
2145 aFPOpacity = 0.0f;
2146
2147 if( ( a3DModel->m_Materials != nullptr ) && ( a3DModel->m_Meshes != nullptr )
2148 && ( a3DModel->m_MaterialsSize > 0 ) && ( a3DModel->m_MeshesSize > 0 ) )
2149 {
2150 MODEL_MATERIALS* materialVector = nullptr;
2151
2152 if( !aSkipMaterialInformation )
2153 {
2154 materialVector = getModelMaterial( a3DModel );
2155 }
2156
2157 const glm::mat3 normalMatrix = glm::transpose( glm::inverse( glm::mat3( aModelMatrix ) ) );
2158
2159 for( unsigned int mesh_i = 0; mesh_i < a3DModel->m_MeshesSize; ++mesh_i )
2160 {
2161 const SMESH& mesh = a3DModel->m_Meshes[mesh_i];
2162
2163 // Validate the mesh pointers
2164 wxASSERT( mesh.m_Positions != nullptr );
2165 wxASSERT( mesh.m_FaceIdx != nullptr );
2166 wxASSERT( mesh.m_Normals != nullptr );
2167 wxASSERT( mesh.m_FaceIdxSize > 0 );
2168 wxASSERT( ( mesh.m_FaceIdxSize % 3 ) == 0 );
2169
2170
2171 if( ( mesh.m_Positions != nullptr ) && ( mesh.m_Normals != nullptr )
2172 && ( mesh.m_FaceIdx != nullptr ) && ( mesh.m_FaceIdxSize > 0 )
2173 && ( mesh.m_VertexSize > 0 ) && ( ( mesh.m_FaceIdxSize % 3 ) == 0 )
2174 && ( mesh.m_MaterialIdx < a3DModel->m_MaterialsSize ) )
2175 {
2176 float fpTransparency;
2177 const BLINN_PHONG_MATERIAL* blinn_material;
2178
2179 if( !aSkipMaterialInformation )
2180 {
2181 blinn_material = &( *materialVector )[mesh.m_MaterialIdx];
2182
2183 fpTransparency =
2184 1.0f - ( ( 1.0f - blinn_material->GetTransparency() ) * aFPOpacity );
2185 }
2186
2187 // Add all face triangles
2188 for( unsigned int faceIdx = 0; faceIdx < mesh.m_FaceIdxSize; faceIdx += 3 )
2189 {
2190 const unsigned int idx0 = mesh.m_FaceIdx[faceIdx + 0];
2191 const unsigned int idx1 = mesh.m_FaceIdx[faceIdx + 1];
2192 const unsigned int idx2 = mesh.m_FaceIdx[faceIdx + 2];
2193
2194 wxASSERT( idx0 < mesh.m_VertexSize );
2195 wxASSERT( idx1 < mesh.m_VertexSize );
2196 wxASSERT( idx2 < mesh.m_VertexSize );
2197
2198 if( ( idx0 < mesh.m_VertexSize ) && ( idx1 < mesh.m_VertexSize )
2199 && ( idx2 < mesh.m_VertexSize ) )
2200 {
2201 const SFVEC3F& v0 = mesh.m_Positions[idx0];
2202 const SFVEC3F& v1 = mesh.m_Positions[idx1];
2203 const SFVEC3F& v2 = mesh.m_Positions[idx2];
2204
2205 const SFVEC3F& n0 = mesh.m_Normals[idx0];
2206 const SFVEC3F& n1 = mesh.m_Normals[idx1];
2207 const SFVEC3F& n2 = mesh.m_Normals[idx2];
2208
2209 // Transform vertex with the model matrix
2210 const SFVEC3F vt0 = SFVEC3F( aModelMatrix * glm::vec4( v0, 1.0f ) );
2211 const SFVEC3F vt1 = SFVEC3F( aModelMatrix * glm::vec4( v1, 1.0f ) );
2212 const SFVEC3F vt2 = SFVEC3F( aModelMatrix * glm::vec4( v2, 1.0f ) );
2213
2214 const SFVEC3F nt0 = glm::normalize( SFVEC3F( normalMatrix * n0 ) );
2215 const SFVEC3F nt1 = glm::normalize( SFVEC3F( normalMatrix * n1 ) );
2216 const SFVEC3F nt2 = glm::normalize( SFVEC3F( normalMatrix * n2 ) );
2217
2218 TRIANGLE* newTriangle = new TRIANGLE( vt0, vt2, vt1, nt0, nt2, nt1 );
2219
2220 newTriangle->SetBoardItem( aBoardItem );
2221
2222 aDstContainer.Add( newTriangle );
2223
2224 if( !aSkipMaterialInformation )
2225 {
2226 newTriangle->SetMaterial( blinn_material );
2227 newTriangle->SetModelTransparency( fpTransparency );
2228
2229 if( mesh.m_Color == nullptr )
2230 {
2231 const SFVEC3F diffuseColor =
2232 a3DModel->m_Materials[mesh.m_MaterialIdx].m_Diffuse;
2233
2234 if( m_boardAdapter.m_Cfg->m_Render.material_mode == MATERIAL_MODE::CAD_MODE )
2235 newTriangle->SetColor( ConvertSRGBToLinear(
2236 MaterialDiffuseToColorCAD( diffuseColor ) ) );
2237 else
2238 newTriangle->SetColor( ConvertSRGBToLinear( diffuseColor ) );
2239 }
2240 else
2241 {
2242 if( m_boardAdapter.m_Cfg->m_Render.material_mode == MATERIAL_MODE::CAD_MODE )
2243 {
2244 newTriangle->SetColor(
2246 mesh.m_Color[idx0] ) ),
2248 mesh.m_Color[idx1] ) ),
2250 mesh.m_Color[idx2] ) ) );
2251 }
2252 else
2253 {
2254 newTriangle->SetColor(
2255 ConvertSRGBToLinear( mesh.m_Color[idx0] ),
2256 ConvertSRGBToLinear( mesh.m_Color[idx1] ),
2257 ConvertSRGBToLinear( mesh.m_Color[idx2] ) );
2258 }
2259 }
2260 }
2261 }
2262 }
2263 }
2264 }
2265 }
2266}
@ 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:350
POST_MACHINING_PROPS & BackPostMachining()
Definition padstack.h:353
Definition pad.h:55
int GetBackPostMachiningSize() const
Definition pad.h:478
const VECTOR2I & GetDrillSize() const
Definition pad.h:317
VECTOR2I GetPosition() const override
Definition pad.h:209
int GetFrontPostMachiningSize() const
Definition pad.h:458
int GetFrontPostMachiningDepth() const
Definition pad.h:460
std::optional< PAD_DRILL_POST_MACHINING_MODE > GetFrontPostMachining() const
Definition pad.h:445
EDA_ANGLE GetOrientation() const
Return the rotation angle of the pad.
Definition pad.h:420
std::optional< PAD_DRILL_POST_MACHINING_MODE > GetBackPostMachining() const
Definition pad.h:465
int GetBackPostMachiningDepth() const
Definition pad.h:480
const VECTOR2I & GetStart() const
Definition pcb_track.h:154
int GetFrontPostMachiningSize() const
Definition pcb_track.h:750
const PADSTACK & Padstack() const
Definition pcb_track.h:463
std::optional< PAD_DRILL_POST_MACHINING_MODE > GetFrontPostMachining() const
Definition pcb_track.h:737
int GetBackPostMachiningSize() const
Definition pcb_track.h:770
int GetDrillValue() const
Calculate the drill value for vias (m_drill if > 0, or default drill value for the board).
std::optional< PAD_DRILL_POST_MACHINING_MODE > GetBackPostMachining() const
Definition pcb_track.h:757
void LayerPair(PCB_LAYER_ID *top_layer, PCB_LAYER_ID *bottom_layer) const
Return the 2 layers used by the via (the via actually uses all layers between these 2 layers)
Procedural generation of the plastic normals.
Definition material.h:146
Procedural generation of the shiny plastic normals.
Definition material.h:170
Point light source based on http://ogldev.atspace.co.uk/www/tutorial20/tutorial20....
Definition light.h:71
static FOOTPRINT_LIBRARY_ADAPTER * FootprintLibAdapter(PROJECT *aProject)
BOARD_ADAPTER & m_boardAdapter
Settings reference in use for this render.
SILK_SCREEN_NORMAL m_silkScreenMaterial
struct RENDER_3D_RAYTRACE_BASE::@013206213056006125230376122042346155134272177300 m_materials
void Reload(REPORTER *aStatusReporter, REPORTER *aWarningReporter, bool aOnlyLoadCopperAndShapes)
void addCountersinkPlating(const SFVEC2F &aCenter, float aTopInnerRadius, float aBottomInnerRadius, float aSurfaceZ, float aDepth, bool aIsFront)
void load3DModels(CONTAINER_3D &aDstContainer, bool aSkipMaterialInformation)
void insertHole(const PCB_VIA *aVia)
BRUSHED_METAL_NORMAL m_brushedMetalMaterial
static constexpr float MIN_DISTANCE_IU
PLATED_COPPER_NORMAL m_platedCopperMaterial
void createItemsFromContainer(const BVH_CONTAINER_2D *aContainer2d, PCB_LAYER_ID aLayer_id, const MATERIAL *aMaterialLayer, const SFVEC3F &aLayerColor, float aLayerZOffset)
PLASTIC_SHINE_NORMAL m_shinyPlasticMaterial
void addCounterborePlating(const BOARD_ITEM &aSource, const SFVEC2F &aCenter, float aInnerRadius, float aDepth, float aSurfaceZ, bool aIsFront)
BVH_CONTAINER_2D * m_antioutlineBoard2dObjects
CONTAINER_2D m_containerWithObjectsToDelete
Store the list of created objects special for RT that will be clear in the end.
MODEL_MATERIALS * getModelMaterial(const S3DMODEL *a3DModel)
SOLDER_MASK_NORMAL m_solderMaskMaterial
void addModels(CONTAINER_3D &aDstContainer, const S3DMODEL *a3DModel, const glm::mat4 &aModelMatrix, float aFPOpacity, bool aSkipMaterialInformation, BOARD_ITEM *aBoardItem)
MAP_MODEL_MATERIALS m_modelMaterialMap
Stores materials of the 3D models.
void createObject(CONTAINER_3D &aDstContainer, const OBJECT_2D *aObject2D, float aZMin, float aZMax, const MATERIAL *aMaterial, const SFVEC3F &aObjColor)
Create one or more 3D objects form a 2D object and Z positions.
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:73
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Report a string with a given severity.
Definition reporter.h:102
void SetColor(SFVEC3F aObjColor)
Cache for storing the 3D shapes.
Definition 3d_cache.h:55
S3DMODEL * GetModel(const wxString &aModelFileName, const wxString &aBasePath, std::vector< const EMBEDDED_FILES * > aEmbeddedFilesStack)
Attempt to load the scene data for a model and to translate it into an S3D_MODEL structure for displa...
Definition 3d_cache.cpp:551
Represent a set of closed polygons.
void Fracture()
Convert a set of polygons with holes to a single outline with "slits"/"fractures" connecting the oute...
int OutlineCount() const
Return the number of outlines in the set.
void BooleanSubtract(const SHAPE_POLY_SET &b)
Perform boolean polyset difference.
Procedural generation of the solder mask.
Definition material.h:126
A triangle object.
Definition triangle_3d.h:43
void SetColor(const SFVEC3F &aColor)
A vertical truncated cone with different radii at top and bottom.
Definition frustum_3d.h:39
void SetColor(SFVEC3F aObjColor)
Definition frustum_3d.h:51
A plane that is parallel to XY plane.
Definition plane_3d.h:38
void SetColor(SFVEC3F aObjColor)
Definition plane_3d.h:49
std::list< OBJECT_2D * > LIST_OBJECT2D
std::list< const OBJECT_2D * > CONST_LIST_OBJECT2D
#define _(s)
#define UNITS3D_TO_UNITSPCB
Implements a model viewer canvas.
Declaration of the eda_3d_viewer class.
@ TENTHS_OF_A_DEGREE_T
Definition eda_angle.h:30
A truncated cone for raytracing, used for countersink visualization.
int MapPCBLayerTo3DLayer(PCB_LAYER_ID aLayer)
Definition layer_id.cpp:334
@ LAYER_3D_USER_1
Definition layer_ids.h:567
@ LAYER_3D_SOLDERMASK_TOP
Definition layer_ids.h:560
@ LAYER_3D_SOLDERMASK_BOTTOM
Definition layer_ids.h:559
@ LAYER_3D_BOARD
Definition layer_ids.h:554
@ LAYER_3D_USER_45
Definition layer_ids.h:611
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:677
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
@ F_CrtYd
Definition layer_ids.h:116
@ B_Adhes
Definition layer_ids.h:103
@ Dwgs_User
Definition layer_ids.h:107
@ F_Paste
Definition layer_ids.h:104
@ Cmts_User
Definition layer_ids.h:108
@ F_Adhes
Definition layer_ids.h:102
@ B_Mask
Definition layer_ids.h:98
@ B_Cu
Definition layer_ids.h:65
@ Eco1_User
Definition layer_ids.h:109
@ F_Mask
Definition layer_ids.h:97
@ B_Paste
Definition layer_ids.h:105
@ F_Fab
Definition layer_ids.h:119
@ F_SilkS
Definition layer_ids.h:100
@ B_CrtYd
Definition layer_ids.h:115
@ Eco2_User
Definition layer_ids.h:110
@ B_SilkS
Definition layer_ids.h:101
@ F_Cu
Definition layer_ids.h:64
@ B_Fab
Definition layer_ids.h:118
#define CSGITEM_EMPTY
#define CSGITEM_FULL
PAD_DRILL_POST_MACHINING_MODE
Definition padstack.h:76
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:103
static wxColor copperColor(220, 180, 30)
void ConvertPolygonToBlocks(const SHAPE_POLY_SET &aMainPath, CONTAINER_2D_BASE &aDstContainer, float aBiuTo3dUnitsScale, float aDivFactor, const BOARD_ITEM &aBoardItem, int aPolyIndex)
Use a polygon in the format of the ClipperLib::Path and process it and create multiple 2d objects (PO...
int64_t GetRunningMicroSecs()
An alternate way to calculate an elapsed time (in microsecondes) to class PROF_COUNTER.
void buildBoardBoundingBoxPoly(const BOARD *aBoard, SHAPE_POLY_SET &aOutline)
Get the complete bounding box of the board (including all items).
static float TransparencyControl(float aGrayColorValue, float aTransparency)
Perform an interpolation step to easy control the transparency based on the gray color value and tran...
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