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, see <https://www.gnu.org/licenses/>.
20 */
21
23#include "shapes3D/plane_3d.h"
27#include "shapes3D/frustum_3d.h"
30#include "shapes2D/ring_2d.h"
31#include "shapes2D/polygon_2d.h"
36#include "3d_fastmath.h"
37#include "3d_math.h"
39
40#include <board.h>
41#include <footprint.h>
43#include <eda_3d_viewer_frame.h>
44#include <project_pcb.h>
45#include <pad.h>
46#include <pcb_track.h>
47
48#include <base_units.h>
49#include <core/profile.h> // To use GetRunningMicroSecs or another profiling utility
50
59static float TransparencyControl( float aGrayColorValue, float aTransparency )
60{
61 const float aaa = aTransparency * aTransparency * aTransparency;
62
63 // 1.00-1.05*(1.0-x)^3
64 float ca = 1.0f - aTransparency;
65 ca = 1.00f - 1.05f * ca * ca * ca;
66
67 return glm::max( glm::min( aGrayColorValue * ca + aaa, 1.0f ), 0.0f );
68}
69
73#define UNITS3D_TO_UNITSPCB ( pcbIUScale.IU_PER_MM )
74
75
77{
79 m_boardAdapter.m_Cfg->m_Render.raytrace_nrsamples_refractions );
81 m_boardAdapter.m_Cfg->m_Render.raytrace_nrsamples_reflections );
82
84 m_boardAdapter.m_Cfg->m_Render.raytrace_recursivelevel_refractions );
86 m_boardAdapter.m_Cfg->m_Render.raytrace_recursivelevel_reflections );
87
88 double mmTo3Dunits = pcbIUScale.IU_PER_MM * m_boardAdapter.BiuTo3dUnits();
89
90 if( m_boardAdapter.m_Cfg->m_Render.raytrace_procedural_textures )
91 {
92 m_boardMaterial = BOARD_NORMAL( 0.40f * mmTo3Dunits );
93 m_copperMaterial = COPPER_NORMAL( 4.0f * mmTo3Dunits, &m_boardMaterial );
94 m_platedCopperMaterial = PLATED_COPPER_NORMAL( 0.5f * mmTo3Dunits );
96 m_plasticMaterial = PLASTIC_NORMAL( 0.05f * mmTo3Dunits );
97 m_shinyPlasticMaterial = PLASTIC_SHINE_NORMAL( 0.1f * mmTo3Dunits );
98 m_brushedMetalMaterial = BRUSHED_METAL_NORMAL( 0.05f * mmTo3Dunits );
99 m_silkScreenMaterial = SILK_SCREEN_NORMAL( 0.25f * mmTo3Dunits );
100 }
101
102 // http://devernay.free.fr/cours/opengl/materials.html
103 // Copper
104 const SFVEC3F copperSpecularLinear =
105 ConvertSRGBToLinear( glm::clamp( (SFVEC3F) m_boardAdapter.m_CopperColor * 0.5f + 0.25f,
106 SFVEC3F( 0.0f ), SFVEC3F( 1.0f ) ) );
107
109 ConvertSRGBToLinear( (SFVEC3F) m_boardAdapter.m_CopperColor * 0.3f ),
110 SFVEC3F( 0.0f ), copperSpecularLinear, 0.4f * 128.0f, 0.0f, 0.0f );
111
112 if( m_boardAdapter.m_Cfg->m_Render.raytrace_procedural_textures )
113 m_materials.m_Copper.SetGenerator( &m_platedCopperMaterial );
114
115 m_materials.m_NonPlatedCopper = BLINN_PHONG_MATERIAL(
116 ConvertSRGBToLinear( SFVEC3F( 0.191f, 0.073f, 0.022f ) ), SFVEC3F( 0.0f, 0.0f, 0.0f ),
117 SFVEC3F( 0.256f, 0.137f, 0.086f ), 0.15f * 128.0f, 0.0f, 0.0f );
118
119 if( m_boardAdapter.m_Cfg->m_Render.raytrace_procedural_textures )
120 m_materials.m_NonPlatedCopper.SetGenerator( &m_copperMaterial );
121
123 ConvertSRGBToLinear( (SFVEC3F) m_boardAdapter.m_SolderPasteColor )
124 * ConvertSRGBToLinear( (SFVEC3F) m_boardAdapter.m_SolderPasteColor ),
125 SFVEC3F( 0.0f, 0.0f, 0.0f ),
126 ConvertSRGBToLinear( (SFVEC3F) m_boardAdapter.m_SolderPasteColor )
128 (SFVEC3F) m_boardAdapter.m_SolderPasteColor ),
129 0.10f * 128.0f, 0.0f, 0.0f );
130
132 SFVEC3F( 0.0f, 0.0f, 0.0f ),
133 glm::clamp( ( ( SFVEC3F )( 1.0f ) - ConvertSRGBToLinear(
134 (SFVEC3F) m_boardAdapter.m_SilkScreenColorTop ) ),
135 SFVEC3F( 0.0f ), SFVEC3F( 0.10f ) ), 0.078125f * 128.0f, 0.0f, 0.0f );
136
137 if( m_boardAdapter.m_Cfg->m_Render.raytrace_procedural_textures )
138 m_materials.m_SilkS.SetGenerator( &m_silkScreenMaterial );
139
140 // Assume that SolderMaskTop == SolderMaskBot
141 const float solderMask_gray =
142 ( m_boardAdapter.m_SolderMaskColorTop.r + m_boardAdapter.m_SolderMaskColorTop.g
143 + m_boardAdapter.m_SolderMaskColorTop.b )
144 / 3.0f;
145
146 const float solderMask_transparency = TransparencyControl( solderMask_gray,
147 1.0f - m_boardAdapter.m_SolderMaskColorTop.a );
148
149 // For darker solder mask colors, increase shininess for a more realistic appearance.
150 // Darker colors appear to have a sharper specular highlight in real life.
151 const float minSolderMaskShininess = 0.85f * 128.0f;
152 const float maxSolderMaskShininess = 512.0f;
153 const float solderMaskShininess = minSolderMaskShininess
154 + ( maxSolderMaskShininess - minSolderMaskShininess ) * ( 1.0f - solderMask_gray );
155
156 // Darker solder mask colors need lower reflection to prevent washed-out appearance
157 const float solderMaskReflection = glm::clamp( solderMask_gray * 0.3f, 0.02f, 0.16f );
158
159 m_materials.m_SolderMask = BLINN_PHONG_MATERIAL(
160 ConvertSRGBToLinear( (SFVEC3F) m_boardAdapter.m_SolderMaskColorTop ) * 0.10f,
161 SFVEC3F( 0.0f, 0.0f, 0.0f ),
162 SFVEC3F( glm::clamp( solderMask_gray * 2.0f, 0.30f, 1.0f ) ), solderMaskShininess,
163 solderMask_transparency, solderMaskReflection );
164
165 m_materials.m_SolderMask.SetCastShadows( true );
166 m_materials.m_SolderMask.SetRefractionRayCount( 1 );
167
168 if( m_boardAdapter.m_Cfg->m_Render.raytrace_procedural_textures )
169 m_materials.m_SolderMask.SetGenerator( &m_solderMaskMaterial );
170
171 m_materials.m_EpoxyBoard =
172 BLINN_PHONG_MATERIAL( ConvertSRGBToLinear( SFVEC3F( 16.0f / 255.0f, 14.0f / 255.0f,
173 10.0f / 255.0f ) ),
174 SFVEC3F( 0.0f, 0.0f, 0.0f ),
175 ConvertSRGBToLinear( SFVEC3F( 10.0f / 255.0f, 8.0f / 255.0f,
176 10.0f / 255.0f ) ),
177 0.1f * 128.0f, 1.0f - m_boardAdapter.m_BoardBodyColor.a, 0.0f );
178
179 m_materials.m_EpoxyBoard.SetAbsorvance( 10.0f );
180
181 if( m_boardAdapter.m_Cfg->m_Render.raytrace_procedural_textures )
182 m_materials.m_EpoxyBoard.SetGenerator( &m_boardMaterial );
183
184 SFVEC3F bgTop = ConvertSRGBToLinear( (SFVEC3F) m_boardAdapter.m_BgColorTop );
185
186 m_materials.m_Floor = BLINN_PHONG_MATERIAL( bgTop * 0.125f, SFVEC3F( 0.0f, 0.0f, 0.0f ),
187 ( SFVEC3F( 1.0f ) - bgTop ) / 3.0f,
188 0.10f * 128.0f, 1.0f, 0.50f );
189 m_materials.m_Floor.SetCastShadows( false );
190 m_materials.m_Floor.SetReflectionRecursionCount( 1 );
191}
192
193
194void RENDER_3D_RAYTRACE_BASE::createObject( CONTAINER_3D& aDstContainer, const OBJECT_2D* aObject2D,
195 float aZMin, float aZMax, const MATERIAL* aMaterial,
196 const SFVEC3F& aObjColor )
197{
198 switch( aObject2D->GetObjectType() )
199 {
201 {
203
204 XY_PLANE* objPtr;
205 objPtr = new XY_PLANE( BBOX_3D(
206 SFVEC3F( aObject2D->GetBBox().Min().x, aObject2D->GetBBox().Min().y, aZMin ),
207 SFVEC3F( aObject2D->GetBBox().Max().x, aObject2D->GetBBox().Max().y, aZMin ) ) );
208 objPtr->SetMaterial( aMaterial );
209 objPtr->SetColor( ConvertSRGBToLinear( aObjColor ) );
210 aDstContainer.Add( objPtr );
211
212 objPtr = new XY_PLANE( BBOX_3D(
213 SFVEC3F( aObject2D->GetBBox().Min().x, aObject2D->GetBBox().Min().y, aZMax ),
214 SFVEC3F( aObject2D->GetBBox().Max().x, aObject2D->GetBBox().Max().y, aZMax ) ) );
215 objPtr->SetMaterial( aMaterial );
216 objPtr->SetColor( ConvertSRGBToLinear( aObjColor ) );
217 aDstContainer.Add( objPtr );
218 break;
219 }
220
222 {
224
225 const ROUND_SEGMENT_2D* aRoundSeg2D = static_cast<const ROUND_SEGMENT_2D*>( aObject2D );
226 ROUND_SEGMENT* objPtr = new ROUND_SEGMENT( *aRoundSeg2D, aZMin, aZMax );
227 objPtr->SetMaterial( aMaterial );
228 objPtr->SetColor( ConvertSRGBToLinear( aObjColor ) );
229 aDstContainer.Add( objPtr );
230 break;
231 }
232
233 default:
234 {
235 LAYER_ITEM* objPtr = new LAYER_ITEM( aObject2D, aZMin, aZMax );
236 objPtr->SetMaterial( aMaterial );
237 objPtr->SetColor( ConvertSRGBToLinear( aObjColor ) );
238 aDstContainer.Add( objPtr );
239 break;
240 }
241 }
242}
243
244
246 PCB_LAYER_ID aLayer_id,
247 const MATERIAL* aMaterialLayer,
248 const SFVEC3F& aLayerColor,
249 float aLayerZOffset )
250{
251 if( aContainer2d == nullptr )
252 return;
253
255 bool isSilk = aLayer_id == B_SilkS || aLayer_id == F_SilkS;
256 const LIST_OBJECT2D& listObject2d = aContainer2d->GetList();
257
258 if( listObject2d.size() == 0 )
259 return;
260
261 for( const OBJECT_2D* object2d_A : listObject2d )
262 {
263 // not yet used / implemented (can be used in future to clip the objects in the
264 // board borders
265 OBJECT_2D* object2d_C = CSGITEM_FULL;
266
267 std::vector<const OBJECT_2D*>* object2d_B = CSGITEM_EMPTY;
268
269 object2d_B = new std::vector<const OBJECT_2D*>();
270
271 // Subtract holes but not in SolderPaste
272 // (can be added as an option in future)
273 if( !( aLayer_id == B_Paste || aLayer_id == F_Paste ) )
274 {
275 // Check if there are any layerhole that intersects this object
276 // Eg: a segment is cut by a via hole or THT hole.
277 const MAP_CONTAINER_2D_BASE& layerHolesMap = m_boardAdapter.GetLayerHoleMap();
278
279 if( layerHolesMap.find( aLayer_id ) != layerHolesMap.end() )
280 {
281 const BVH_CONTAINER_2D* holes2d = layerHolesMap.at( aLayer_id );
282
283 CONST_LIST_OBJECT2D intersecting;
284
285 holes2d->GetIntersectingObjects( object2d_A->GetBBox(), intersecting );
286
287 for( const OBJECT_2D* hole2d : intersecting )
288 object2d_B->push_back( hole2d );
289 }
290
291 // Check if there are any THT that intersects this object. If we're processing a silk
292 // layer and the flag is set, then clip the silk at the outer edge of the annular ring,
293 // rather than the at the outer edge of the copper plating.
294 const BVH_CONTAINER_2D& throughHoleOuter =
295 cfg.clip_silk_on_via_annuli && isSilk ? m_boardAdapter.GetViaAnnuli()
296 : m_boardAdapter.GetTH_ODs();
297
298 if( !throughHoleOuter.GetList().empty() )
299 {
300 CONST_LIST_OBJECT2D intersecting;
301
302 throughHoleOuter.GetIntersectingObjects( object2d_A->GetBBox(), intersecting );
303
304 for( const OBJECT_2D* hole2d : intersecting )
305 object2d_B->push_back( hole2d );
306 }
307
308 // Clip counterbore/countersink cutouts from copper layers
309 // Front cutouts affect F_Cu, back cutouts affect B_Cu
310 auto clipCutouts = [this, &object2d_A, &object2d_B]( const BVH_CONTAINER_2D& cutouts )
311 {
312 if( !cutouts.GetList().empty() )
313 {
314 CONST_LIST_OBJECT2D intersecting;
315 cutouts.GetIntersectingObjects( object2d_A->GetBBox(), intersecting );
316
317 for( const OBJECT_2D* cutout : intersecting )
318 object2d_B->push_back( cutout );
319 }
320 };
321
322 if( aLayer_id == F_Cu )
323 {
324 clipCutouts( m_boardAdapter.GetFrontCounterboreCutouts() );
325 clipCutouts( m_boardAdapter.GetFrontCountersinkCutouts() );
326 clipCutouts( m_boardAdapter.GetTertiarydrillCutouts() );
327 }
328 else if( aLayer_id == B_Cu )
329 {
330 clipCutouts( m_boardAdapter.GetBackCounterboreCutouts() );
331 clipCutouts( m_boardAdapter.GetBackCountersinkCutouts() );
332 clipCutouts( m_boardAdapter.GetBackdrillCutouts() );
333 }
334 }
335
336 if( !m_antioutlineBoard2dObjects->GetList().empty() )
337 {
338 CONST_LIST_OBJECT2D intersecting;
339
340 m_antioutlineBoard2dObjects->GetIntersectingObjects( object2d_A->GetBBox(),
341 intersecting );
342
343 for( const OBJECT_2D* obj : intersecting )
344 object2d_B->push_back( obj );
345 }
346
347 const MAP_CONTAINER_2D_BASE& mapLayers = m_boardAdapter.GetLayerMap();
348
350 && ( ( aLayer_id == B_SilkS && mapLayers.find( B_Mask ) != mapLayers.end() )
351 || ( aLayer_id == F_SilkS && mapLayers.find( F_Mask ) != mapLayers.end() ) ) )
352 {
353 const PCB_LAYER_ID maskLayer = ( aLayer_id == B_SilkS ) ? B_Mask : F_Mask;
354
355 const BVH_CONTAINER_2D* containerMaskLayer2d = mapLayers.at( maskLayer );
356
357 CONST_LIST_OBJECT2D intersecting;
358
359 if( containerMaskLayer2d ) // can be null if B_Mask or F_Mask is not shown
360 containerMaskLayer2d->GetIntersectingObjects( object2d_A->GetBBox(), intersecting );
361
362 for( const OBJECT_2D* obj2d : intersecting )
363 object2d_B->push_back( obj2d );
364 }
365
366 if( object2d_B->empty() )
367 {
368 delete object2d_B;
369 object2d_B = CSGITEM_EMPTY;
370 }
371
372 if( ( object2d_B == CSGITEM_EMPTY ) && ( object2d_C == CSGITEM_FULL ) )
373 {
374 LAYER_ITEM* objPtr = new LAYER_ITEM( object2d_A,
375 m_boardAdapter.GetLayerBottomZPos( aLayer_id ) - aLayerZOffset,
376 m_boardAdapter.GetLayerTopZPos( aLayer_id ) + aLayerZOffset );
377 objPtr->SetMaterial( aMaterialLayer );
378 objPtr->SetColor( ConvertSRGBToLinear( aLayerColor ) );
379 m_objectContainer.Add( objPtr );
380 }
381 else
382 {
383 LAYER_ITEM_2D* itemCSG2d = new LAYER_ITEM_2D( object2d_A, object2d_B, object2d_C,
384 object2d_A->GetBoardItem() );
385 m_containerWithObjectsToDelete.Add( itemCSG2d );
386
387 LAYER_ITEM* objPtr = new LAYER_ITEM( itemCSG2d,
388 m_boardAdapter.GetLayerBottomZPos( aLayer_id ) - aLayerZOffset,
389 m_boardAdapter.GetLayerTopZPos( aLayer_id ) + aLayerZOffset );
390
391 objPtr->SetMaterial( aMaterialLayer );
392 objPtr->SetColor( ConvertSRGBToLinear( aLayerColor ) );
393
394 m_objectContainer.Add( objPtr );
395 }
396 }
397}
398
399
400extern void buildBoardBoundingBoxPoly( const BOARD* aBoard, SHAPE_POLY_SET& aOutline );
401
402
403void RENDER_3D_RAYTRACE_BASE::Reload( bool aOnlyLoadCopperAndShapes, std::stop_token aStop )
404{
405 m_reloadRequested = false;
406
407 m_modelMaterialMap.clear();
408
411
412 int64_t stats_startReloadTime = GetRunningMicroSecs();
413
414 if( !aOnlyLoadCopperAndShapes )
415 {
417 m_boardAdapter.CreateLayers( m_activityReporter );
418
419 SFVEC3F camera_pos = m_boardAdapter.GetBoardCenter();
420 m_camera.SetBoardLookAtPos( camera_pos );
421 }
422
423 {
424 // Drop the BVH before freeing objects it indexes (and before any layer rebuild).
425 std::lock_guard<std::mutex> lock( m_hitTestMutex );
426 m_accelerator.reset();
427 }
428
429 m_objectContainer.Clear();
431 m_extrusionMaterials.clear();
432
434
435 if( aStop.stop_requested() )
436 return;
437
439 {
440 if( aOnlyLoadCopperAndShapes )
441 m_activityReporter->Report( _( "Building hit-test scene..." ) );
442 else
443 m_activityReporter->Report( _( "Load Raytracing: board" ) );
444 }
445
446 // Create and add the outline board
449
452
453 std::bitset<LAYER_3D_END> layerFlags = m_boardAdapter.GetVisibleLayers();
454
455 if( !aOnlyLoadCopperAndShapes )
456 {
457 const int outlineCount = m_boardAdapter.GetBoardPoly().OutlineCount();
458
459 if( outlineCount > 0 )
460 {
461 float divFactor = 0.0f;
462
463 if( m_boardAdapter.GetViaCount() )
464 divFactor = m_boardAdapter.GetAverageViaHoleDiameter() * 18.0f;
465 else if( m_boardAdapter.GetHoleCount() )
466 divFactor = m_boardAdapter.GetAverageHoleDiameter() * 8.0f;
467
468 SHAPE_POLY_SET boardPolyCopy = m_boardAdapter.GetBoardPoly();
469
470 // Calculate an antiboard outline
471 SHAPE_POLY_SET antiboardPoly;
472
473 buildBoardBoundingBoxPoly( m_boardAdapter.GetBoard(), antiboardPoly );
474
475 antiboardPoly.BooleanSubtract( boardPolyCopy );
476 antiboardPoly.Fracture();
477
478 for( int ii = 0; ii < antiboardPoly.OutlineCount(); ii++ )
479 {
481 m_boardAdapter.BiuTo3dUnits(), -1.0f,
482 *m_boardAdapter.GetBoard(), ii );
483 }
484
485 m_antioutlineBoard2dObjects->BuildBVH();
486
487 boardPolyCopy.Fracture();
488
489 for( int ii = 0; ii < boardPolyCopy.OutlineCount(); ii++ )
490 {
492 m_boardAdapter.BiuTo3dUnits(), divFactor,
493 *m_boardAdapter.GetBoard(), ii );
494 }
495
496 if( layerFlags.test( LAYER_3D_BOARD ) )
497 {
498 const LIST_OBJECT2D& listObjects = m_outlineBoard2dObjects->GetList();
499
500 for( const OBJECT_2D* object2d_A : listObjects )
501 {
502 std::vector<const OBJECT_2D*>* object2d_B = new std::vector<const OBJECT_2D*>();
503
504 // Check if there are any THT that intersects this outline object part
505 if( !m_boardAdapter.GetTH_ODs().GetList().empty() )
506 {
507 const BVH_CONTAINER_2D& throughHoles = m_boardAdapter.GetTH_ODs();
508 CONST_LIST_OBJECT2D intersecting;
509
510 throughHoles.GetIntersectingObjects( object2d_A->GetBBox(), intersecting );
511
512 for( const OBJECT_2D* hole : intersecting )
513 {
514 if( object2d_A->Intersects( hole->GetBBox() ) )
515 object2d_B->push_back( hole );
516 }
517 }
518
519 // Subtract counterbore/countersink cutouts from board body
520 auto addCutoutsFromContainer =
521 [&]( const BVH_CONTAINER_2D& aContainer )
522 {
523 if( !aContainer.GetList().empty() )
524 {
525 CONST_LIST_OBJECT2D intersecting;
526 aContainer.GetIntersectingObjects( object2d_A->GetBBox(),
527 intersecting );
528
529 for( const OBJECT_2D* cutout : intersecting )
530 {
531 if( object2d_A->Intersects( cutout->GetBBox() ) )
532 object2d_B->push_back( cutout );
533 }
534 }
535 };
536
537 addCutoutsFromContainer( m_boardAdapter.GetFrontCounterboreCutouts() );
538 addCutoutsFromContainer( m_boardAdapter.GetBackCounterboreCutouts() );
539 addCutoutsFromContainer( m_boardAdapter.GetFrontCountersinkCutouts() );
540 addCutoutsFromContainer( m_boardAdapter.GetBackCountersinkCutouts() );
541
542 // Subtract backdrill holes (which are in layerHoleMap for F_Cu and B_Cu)
543 const MAP_CONTAINER_2D_BASE& layerHolesMap = m_boardAdapter.GetLayerHoleMap();
544
545 if( layerHolesMap.find( F_Cu ) != layerHolesMap.end() )
546 {
547 const BVH_CONTAINER_2D* holes2d = layerHolesMap.at( F_Cu );
548 CONST_LIST_OBJECT2D intersecting;
549
550 holes2d->GetIntersectingObjects( object2d_A->GetBBox(), intersecting );
551
552 for( const OBJECT_2D* hole2d : intersecting )
553 {
554 if( object2d_A->Intersects( hole2d->GetBBox() ) )
555 object2d_B->push_back( hole2d );
556 }
557 }
558
559 if( layerHolesMap.find( B_Cu ) != layerHolesMap.end() )
560 {
561 const BVH_CONTAINER_2D* holes2d = layerHolesMap.at( B_Cu );
562 CONST_LIST_OBJECT2D intersecting;
563
564 holes2d->GetIntersectingObjects( object2d_A->GetBBox(), intersecting );
565
566 for( const OBJECT_2D* hole2d : intersecting )
567 {
568 if( object2d_A->Intersects( hole2d->GetBBox() ) )
569 object2d_B->push_back( hole2d );
570 }
571 }
572
573 if( !m_antioutlineBoard2dObjects->GetList().empty() )
574 {
575 CONST_LIST_OBJECT2D intersecting;
576
577 m_antioutlineBoard2dObjects->GetIntersectingObjects( object2d_A->GetBBox(),
578 intersecting );
579
580 for( const OBJECT_2D* obj : intersecting )
581 object2d_B->push_back( obj );
582 }
583
584 if( object2d_B->empty() )
585 {
586 delete object2d_B;
587 object2d_B = CSGITEM_EMPTY;
588 }
589
590 if( object2d_B == CSGITEM_EMPTY )
591 {
592 LAYER_ITEM* objPtr = new LAYER_ITEM( object2d_A,
593 m_boardAdapter.GetLayerBottomZPos( F_Cu ),
594 m_boardAdapter.GetLayerBottomZPos( B_Cu ) );
595
596 objPtr->SetMaterial( &m_materials.m_EpoxyBoard );
597 objPtr->SetColor( ConvertSRGBToLinear( m_boardAdapter.m_BoardBodyColor ) );
598 m_objectContainer.Add( objPtr );
599 }
600 else
601 {
602
603 LAYER_ITEM_2D* itemCSG2d = new LAYER_ITEM_2D( object2d_A, object2d_B,
605 *m_boardAdapter.GetBoard() );
606
607 m_containerWithObjectsToDelete.Add( itemCSG2d );
608
609 LAYER_ITEM* objPtr = new LAYER_ITEM( itemCSG2d,
610 m_boardAdapter.GetLayerBottomZPos( F_Cu ),
611 m_boardAdapter.GetLayerBottomZPos( B_Cu ) );
612
613 objPtr->SetMaterial( &m_materials.m_EpoxyBoard );
614 objPtr->SetColor( ConvertSRGBToLinear( m_boardAdapter.m_BoardBodyColor ) );
615 m_objectContainer.Add( objPtr );
616 }
617 }
618
619 // Add cylinders of the board body to container
620 // Note: This is actually a workaround for the holes in the board.
621 // The issue is because if a hole is in a border of a divided polygon ( ex
622 // a polygon or dummy block) it will cut also the render of the hole.
623 // So this will add a full hole.
624 // In fact, that is not need if the hole have copper.
625 if( !m_boardAdapter.GetTH_ODs().GetList().empty() )
626 {
627 const LIST_OBJECT2D& holeList = m_boardAdapter.GetTH_ODs().GetList();
628
629 for( const OBJECT_2D* hole2d : holeList )
630 {
631 if( !m_antioutlineBoard2dObjects->GetList().empty() )
632 {
633 CONST_LIST_OBJECT2D intersecting;
634
635 m_antioutlineBoard2dObjects->GetIntersectingObjects( hole2d->GetBBox(),
636 intersecting );
637
638 // Do not add cylinder if it intersects the edge of the board
639 if( !intersecting.empty() )
640 continue;
641 }
642
643 switch( hole2d->GetObjectType() )
644 {
646 {
647 const float radius = hole2d->GetBBox().GetExtent().x * 0.5f * 0.999f;
648
649 CYLINDER* objPtr = new CYLINDER( hole2d->GetCentroid(),
650 NextFloatDown( m_boardAdapter.GetLayerBottomZPos( F_Cu ) ),
651 NextFloatUp( m_boardAdapter.GetLayerBottomZPos( B_Cu ) ),
652 radius );
653
654 objPtr->SetMaterial( &m_materials.m_EpoxyBoard );
655 objPtr->SetColor(
656 ConvertSRGBToLinear( m_boardAdapter.m_BoardBodyColor ) );
657
658 m_objectContainer.Add( objPtr );
659 }
660 break;
661
662 default:
663 break;
664 }
665 }
666 }
667
668 // Create plugs for backdrilled and post-machined areas
670 }
671 }
672 }
673
675 {
676 if( aOnlyLoadCopperAndShapes )
677 m_activityReporter->Report( _( "Building hit-test scene: layers..." ) );
678 else
679 m_activityReporter->Report( _( "Load Raytracing: layers" ) );
680 }
681
682 // Add layers maps (except B_Mask and F_Mask)
683 for( const std::pair<const PCB_LAYER_ID, BVH_CONTAINER_2D*>& entry :
684 m_boardAdapter.GetLayerMap() )
685 {
686 const PCB_LAYER_ID layer_id = entry.first;
687 const BVH_CONTAINER_2D* container2d = entry.second;
688
689 // Only process layers that exist
690 if( !container2d )
691 continue;
692
693 if( aOnlyLoadCopperAndShapes && !IsCopperLayer( layer_id ) )
694 continue;
695
696 // Mask layers are not processed here because they are a special case
697 if( layer_id == B_Mask || layer_id == F_Mask )
698 continue;
699
700 MATERIAL* materialLayer = &m_materials.m_SilkS;
701 SFVEC3F layerColor = SFVEC3F( 0.0f, 0.0f, 0.0f );
702
703 switch( layer_id )
704 {
705 case B_Adhes:
706 case F_Adhes:
707 break;
708
709 case B_Paste:
710 case F_Paste:
711 materialLayer = &m_materials.m_Paste;
712 layerColor = m_boardAdapter.m_SolderPasteColor;
713 break;
714
715 case B_SilkS:
716 materialLayer = &m_materials.m_SilkS;
717 layerColor = m_boardAdapter.m_SilkScreenColorBot;
718 break;
719
720 case F_SilkS:
721 materialLayer = &m_materials.m_SilkS;
722 layerColor = m_boardAdapter.m_SilkScreenColorTop;
723 break;
724
725 case Dwgs_User:
726 layerColor = m_boardAdapter.m_UserDrawingsColor;
727 break;
728
729 case Cmts_User:
730 layerColor = m_boardAdapter.m_UserCommentsColor;
731 break;
732
733 case Eco1_User:
734 layerColor = m_boardAdapter.m_ECO1Color;
735 break;
736
737 case Eco2_User:
738 layerColor = m_boardAdapter.m_ECO2Color;
739 break;
740
741 case B_CrtYd:
742 layerColor = m_boardAdapter.m_BCourtyardColor;
743 break;
744
745 case F_CrtYd:
746 layerColor = m_boardAdapter.m_FCourtyardColor;
747 break;
748
749 case B_Fab:
750 layerColor = m_boardAdapter.m_BFabColor;
751 break;
752
753 case F_Fab:
754 layerColor = m_boardAdapter.m_FFabColor;
755 break;
756
757 default:
758 {
759 int layer3D = MapPCBLayerTo3DLayer( layer_id );
760
761 // Note: MUST do this in LAYER_3D space; User_1..User_45 are NOT contiguous
762 if( layer3D >= LAYER_3D_USER_1 && layer3D <= LAYER_3D_USER_45 )
763 {
764 layerColor = m_boardAdapter.m_UserDefinedLayerColor[ layer3D - LAYER_3D_USER_1 ];
765 }
766 else if( m_boardAdapter.m_Cfg->m_Render.differentiate_plated_copper )
767 {
768 layerColor = SFVEC3F( 184.0f / 255.0f, 115.0f / 255.0f, 50.0f / 255.0f );
769 materialLayer = &m_materials.m_NonPlatedCopper;
770 }
771 else
772 {
773 layerColor = m_boardAdapter.m_CopperColor;
774 materialLayer = &m_materials.m_Copper;
775 }
776
777 break;
778 }
779 }
780
781 createItemsFromContainer( container2d, layer_id, materialLayer, layerColor, 0.0f );
782
783 if( aStop.stop_requested() )
784 return;
785 } // for each layer on map
786
787 // Create plated copper
788 if( m_boardAdapter.m_Cfg->m_Render.differentiate_plated_copper )
789 {
790 createItemsFromContainer( m_boardAdapter.GetPlatedPadsFront(), F_Cu, &m_materials.m_Copper,
791 m_boardAdapter.m_CopperColor,
792 m_boardAdapter.GetFrontCopperThickness() * 0.1f );
793
794 createItemsFromContainer( m_boardAdapter.GetPlatedPadsBack(), B_Cu, &m_materials.m_Copper,
795 m_boardAdapter.m_CopperColor,
796 -m_boardAdapter.GetBackCopperThickness() * 0.1f );
797 }
798
799 if( !aOnlyLoadCopperAndShapes )
800 {
801 // Add Mask layer
802 // Solder mask layers are "negative" layers so the elements that we have in the container
803 // should remove the board outline. We will check for all objects in the outline if it
804 // intersects any object in the layer container and also any hole.
805 if( ( layerFlags.test( LAYER_3D_SOLDERMASK_TOP )
806 || layerFlags.test( LAYER_3D_SOLDERMASK_BOTTOM ) )
807 && !m_outlineBoard2dObjects->GetList().empty() )
808 {
809 const MATERIAL* materialLayer = &m_materials.m_SolderMask;
810
811 for( const std::pair<const PCB_LAYER_ID, BVH_CONTAINER_2D*>& entry :
812 m_boardAdapter.GetLayerMap() )
813 {
814 const PCB_LAYER_ID layer_id = entry.first;
815 const BVH_CONTAINER_2D* container2d = entry.second;
816
817 // Only process layers that exist
818 if( !container2d )
819 continue;
820
821 // Only get the Solder mask layers (and only if the board has them)
822 if( layer_id == F_Mask && !layerFlags.test( LAYER_3D_SOLDERMASK_TOP ) )
823 continue;
824
825 if( layer_id == B_Mask && !layerFlags.test( LAYER_3D_SOLDERMASK_BOTTOM ) )
826 continue;
827
828 // Only Mask layers are processed here because they are negative layers
829 if( layer_id != F_Mask && layer_id != B_Mask )
830 continue;
831
832 SFVEC3F layerColor;
833
834 if( layer_id == B_Mask )
835 layerColor = m_boardAdapter.m_SolderMaskColorBot;
836 else
837 layerColor = m_boardAdapter.m_SolderMaskColorTop;
838
839 const float zLayerMin = m_boardAdapter.GetLayerBottomZPos( layer_id );
840 const float zLayerMax = m_boardAdapter.GetLayerTopZPos( layer_id );
841
842 // Get the outline board objects
843 for( const OBJECT_2D* object2d_A : m_outlineBoard2dObjects->GetList() )
844 {
845 std::vector<const OBJECT_2D*>* object2d_B = new std::vector<const OBJECT_2D*>();
846
847 // Check if there are any THT that intersects this outline object part
848 if( !m_boardAdapter.GetTH_ODs().GetList().empty() )
849 {
850 const BVH_CONTAINER_2D& throughHoles = m_boardAdapter.GetTH_ODs();
851 CONST_LIST_OBJECT2D intersecting;
852
853 throughHoles.GetIntersectingObjects( object2d_A->GetBBox(), intersecting );
854
855 for( const OBJECT_2D* hole : intersecting )
856 {
857 if( object2d_A->Intersects( hole->GetBBox() ) )
858 object2d_B->push_back( hole );
859 }
860 }
861
862 // Check if there are any objects in the layer to subtract with the current
863 // object
864 if( !container2d->GetList().empty() )
865 {
866 CONST_LIST_OBJECT2D intersecting;
867
868 container2d->GetIntersectingObjects( object2d_A->GetBBox(), intersecting );
869
870 for( const OBJECT_2D* obj : intersecting )
871 object2d_B->push_back( obj );
872 }
873
874 if( object2d_B->empty() )
875 {
876 delete object2d_B;
877 object2d_B = CSGITEM_EMPTY;
878 }
879
880 if( object2d_B == CSGITEM_EMPTY )
881 {
882#if 0
883 createObject( m_objectContainer, object2d_A, zLayerMin, zLayerMax,
884 materialLayer, layerColor );
885#else
886 LAYER_ITEM* objPtr = new LAYER_ITEM( object2d_A, zLayerMin, zLayerMax );
887
888 objPtr->SetMaterial( materialLayer );
889 objPtr->SetColor( ConvertSRGBToLinear( layerColor ) );
890
891 m_objectContainer.Add( objPtr );
892#endif
893 }
894 else
895 {
896 LAYER_ITEM_2D* itemCSG2d = new LAYER_ITEM_2D( object2d_A, object2d_B,
898 object2d_A->GetBoardItem() );
899
900 m_containerWithObjectsToDelete.Add( itemCSG2d );
901
902 LAYER_ITEM* objPtr = new LAYER_ITEM( itemCSG2d, zLayerMin, zLayerMax );
903 objPtr->SetMaterial( materialLayer );
904 objPtr->SetColor( ConvertSRGBToLinear( layerColor ) );
905
906 m_objectContainer.Add( objPtr );
907 }
908 }
909 }
910 }
911
913 }
914
915#ifdef PRINT_STATISTICS_3D_VIEWER
916 int64_t stats_endConvertTime = GetRunningMicroSecs();
917 int64_t stats_startLoad3DmodelsTime = stats_endConvertTime;
918#endif
919
921 {
922 if( aOnlyLoadCopperAndShapes )
923 m_activityReporter->Report( _( "Building hit-test scene: 3D models..." ) );
924 else
925 m_activityReporter->Report( _( "Loading 3D models..." ) );
926 }
927
928 load3DModels( m_objectContainer, aOnlyLoadCopperAndShapes, aStop );
929
930 if( aStop.stop_requested() )
931 return;
932
933#ifdef PRINT_STATISTICS_3D_VIEWER
934 int64_t stats_endLoad3DmodelsTime = GetRunningMicroSecs();
935#endif
936
937 if( !aOnlyLoadCopperAndShapes )
938 {
939 // Add floor
940 if( m_boardAdapter.m_Cfg->m_Render.raytrace_backfloor )
941 {
942 BBOX_3D boardBBox = m_boardAdapter.GetBBox();
943
944 if( boardBBox.IsInitialized() )
945 {
946 boardBBox.Scale( 3.0f );
947
948 if( m_objectContainer.GetList().size() > 0 )
949 {
950 BBOX_3D containerBBox = m_objectContainer.GetBBox();
951
952 containerBBox.Scale( 1.3f );
953
954 const SFVEC3F centerBBox = containerBBox.GetCenter();
955
956 // Floor triangles
957 const float minZ = glm::min( containerBBox.Min().z, boardBBox.Min().z );
958
959 const SFVEC3F v1 =
960 SFVEC3F( -RANGE_SCALE_3D * 4.0f, -RANGE_SCALE_3D * 4.0f, minZ )
961 + SFVEC3F( centerBBox.x, centerBBox.y, 0.0f );
962
963 const SFVEC3F v3 =
964 SFVEC3F( +RANGE_SCALE_3D * 4.0f, +RANGE_SCALE_3D * 4.0f, minZ )
965 + SFVEC3F( centerBBox.x, centerBBox.y, 0.0f );
966
967 const SFVEC3F v2 = SFVEC3F( v1.x, v3.y, v1.z );
968 const SFVEC3F v4 = SFVEC3F( v3.x, v1.y, v1.z );
969
970 SFVEC3F floorColor = ConvertSRGBToLinear( m_boardAdapter.m_BgColorTop );
971
972 TRIANGLE* newTriangle1 = new TRIANGLE( v1, v2, v3 );
973 TRIANGLE* newTriangle2 = new TRIANGLE( v3, v4, v1 );
974
975 m_objectContainer.Add( newTriangle1 );
976 m_objectContainer.Add( newTriangle2 );
977
978 newTriangle1->SetMaterial( &m_materials.m_Floor );
979 newTriangle2->SetMaterial( &m_materials.m_Floor );
980
981 newTriangle1->SetColor( floorColor );
982 newTriangle2->SetColor( floorColor );
983
984 // Ceiling triangles
985 const float maxZ = glm::max( containerBBox.Max().z, boardBBox.Max().z );
986
987 const SFVEC3F v5 = SFVEC3F( v1.x, v1.y, maxZ );
988 const SFVEC3F v6 = SFVEC3F( v2.x, v2.y, maxZ );
989 const SFVEC3F v7 = SFVEC3F( v3.x, v3.y, maxZ );
990 const SFVEC3F v8 = SFVEC3F( v4.x, v4.y, maxZ );
991
992 TRIANGLE* newTriangle3 = new TRIANGLE( v7, v6, v5 );
993 TRIANGLE* newTriangle4 = new TRIANGLE( v5, v8, v7 );
994
995 m_objectContainer.Add( newTriangle3 );
996 m_objectContainer.Add( newTriangle4 );
997
998 newTriangle3->SetMaterial( &m_materials.m_Floor );
999 newTriangle4->SetMaterial( &m_materials.m_Floor );
1000
1001 newTriangle3->SetColor( floorColor );
1002 newTriangle4->SetColor( floorColor );
1003 }
1004 }
1005 }
1006
1007 // Init initial lights
1008 for( LIGHT* light : m_lights )
1009 delete light;
1010
1011 m_lights.clear();
1012
1013 auto IsColorZero =
1014 []( const SFVEC3F& aSource )
1015 {
1016 return ( ( aSource.r < ( 1.0f / 255.0f ) ) && ( aSource.g < ( 1.0f / 255.0f ) )
1017 && ( aSource.b < ( 1.0f / 255.0f ) ) );
1018 };
1019
1020 SFVEC3F cameraLightColor =
1021 m_boardAdapter.GetColor( m_boardAdapter.m_Cfg->m_Render.raytrace_lightColorCamera );
1022 SFVEC3F topLightColor =
1023 m_boardAdapter.GetColor( m_boardAdapter.m_Cfg->m_Render.raytrace_lightColorTop );
1024 SFVEC3F bottomLightColor =
1025 m_boardAdapter.GetColor( m_boardAdapter.m_Cfg->m_Render.raytrace_lightColorBottom );
1026
1027 m_cameraLight = new DIRECTIONAL_LIGHT( SFVEC3F( 0.0f, 0.0f, 0.0f ), cameraLightColor );
1028 m_cameraLight->SetCastShadows( false );
1029
1030 if( !IsColorZero( cameraLightColor ) )
1031 m_lights.push_back( m_cameraLight );
1032
1033 const SFVEC3F& boardCenter = m_boardAdapter.GetBBox().GetCenter();
1034
1035 if( !IsColorZero( topLightColor ) )
1036 {
1037 m_lights.push_back( new POINT_LIGHT( SFVEC3F( boardCenter.x, boardCenter.y,
1038 +RANGE_SCALE_3D * 2.0f ),
1039 topLightColor ) );
1040 }
1041
1042 if( !IsColorZero( bottomLightColor ) )
1043 {
1044 m_lights.push_back( new POINT_LIGHT( SFVEC3F( boardCenter.x, boardCenter.y,
1045 -RANGE_SCALE_3D * 2.0f ),
1046 bottomLightColor ) );
1047 }
1048
1049 for( size_t i = 0; i < m_boardAdapter.m_Cfg->m_Render.raytrace_lightColor.size(); ++i )
1050 {
1051 SFVEC3F lightColor =
1052 m_boardAdapter.GetColor( m_boardAdapter.m_Cfg->m_Render.raytrace_lightColor[i] );
1053
1054 if( !IsColorZero( lightColor ) )
1055 {
1056 const SFVEC2F sc = m_boardAdapter.GetSphericalCoord( i );
1057
1058 m_lights.push_back( new DIRECTIONAL_LIGHT(
1059 SphericalToCartesian( glm::pi<float>() * sc.x, glm::pi<float>() * sc.y ),
1060 lightColor ) );
1061 }
1062 }
1063 }
1064
1065 // Set min. and max. zoom range. This doesn't really fit here, but moving this outside of this
1066 // class would require reimplementing bounding box calculation (feel free to do this if you
1067 // have time and patience).
1068 if( m_objectContainer.GetList().size() > 0 )
1069 {
1070 float ratio =
1071 std::max( 1.0f, m_objectContainer.GetBBox().GetMaxDimension() / RANGE_SCALE_3D );
1072
1073 float max_zoom = CAMERA::DEFAULT_MAX_ZOOM * ratio;
1074 float min_zoom = static_cast<float>( MIN_DISTANCE_IU * m_boardAdapter.BiuTo3dUnits()
1075 / -m_camera.GetCameraInitPos().z );
1076
1077 if( min_zoom > max_zoom )
1078 std::swap( min_zoom, max_zoom );
1079
1080 float zoom_ratio = max_zoom / min_zoom;
1081
1082 // Set the minimum number of zoom 'steps' between max and min.
1083 int steps = 3 * 3;
1084 steps -= static_cast<int>( ceil( log( zoom_ratio ) / log( 1.26f ) ) );
1085 steps = std::max( steps, 0 );
1086
1087 // Resize max and min zoom to accomplish the number of steps.
1088 float increased_zoom = pow( 1.26f, steps / 2 );
1089 max_zoom *= increased_zoom;
1090 min_zoom /= increased_zoom;
1091
1092 if( steps & 1 )
1093 min_zoom /= 1.26f;
1094
1095 min_zoom = std::min( min_zoom, 1.0f );
1096
1097 m_camera.SetMaxZoom( max_zoom );
1098 m_camera.SetMinZoom( min_zoom );
1099 }
1100
1101 if( aStop.stop_requested() )
1102 return;
1103
1104 // Create an accelerator
1105 std::unique_ptr<ACCELERATOR_3D> accelerator =
1106 std::make_unique<BVH_PBRT>( m_objectContainer, 8, SPLITMETHOD::MIDDLE );
1107
1108 {
1109 std::lock_guard<std::mutex> lock( m_hitTestMutex );
1110 m_accelerator = std::move( accelerator );
1111 }
1112
1113 if( m_activityReporter )
1114 {
1115 // Calculation time in seconds
1116 double calculation_time = (double) ( GetRunningMicroSecs() - stats_startReloadTime ) / 1e6;
1117
1118 if( aOnlyLoadCopperAndShapes )
1119 {
1120 m_activityReporter->Report( wxString::Format( _( "Hit-test scene ready in %.3f s" ), calculation_time ) );
1121 }
1122 else
1123 {
1124 m_activityReporter->Report( wxString::Format( _( "Reload time %.3f s" ), calculation_time ) );
1125 }
1126 }
1127}
1128
1129
1131 const SFVEC2F& aCenter,
1132 float aInnerRadius, float aDepth,
1133 float aSurfaceZ, bool aIsFront )
1134{
1135 const float platingThickness = m_boardAdapter.GetHolePlatingThickness()
1136 * m_boardAdapter.BiuTo3dUnits();
1137
1138 if( platingThickness <= 0.0f || aInnerRadius <= 0.0f || aDepth <= 0.0f )
1139 return;
1140
1141 const float outerRadius = aInnerRadius + platingThickness;
1142 const float zOther = aIsFront ? ( aSurfaceZ - aDepth ) : ( aSurfaceZ + aDepth );
1143 const float zMin = std::min( aSurfaceZ, zOther );
1144 const float zMax = std::max( aSurfaceZ, zOther );
1145
1146 RING_2D* ring = new RING_2D( aCenter, aInnerRadius, outerRadius, aSource );
1148
1149 LAYER_ITEM* objPtr = new LAYER_ITEM( ring, zMin, zMax );
1150 objPtr->SetMaterial( &m_materials.m_Copper );
1151 objPtr->SetColor( ConvertSRGBToLinear( m_boardAdapter.m_CopperColor ) );
1152
1153 m_objectContainer.Add( objPtr );
1154}
1155
1156
1158 float aTopInnerRadius,
1159 float aBottomInnerRadius,
1160 float aSurfaceZ, float aDepth,
1161 bool aIsFront )
1162{
1163 const float platingThickness = m_boardAdapter.GetHolePlatingThickness()
1164 * m_boardAdapter.BiuTo3dUnits();
1165
1166 if( platingThickness <= 0.0f || aTopInnerRadius <= 0.0f || aBottomInnerRadius <= 0.0f
1167 || aDepth <= 0.0f )
1168 {
1169 return;
1170 }
1171
1172 const float topOuterRadius = aTopInnerRadius + platingThickness;
1173 const float bottomOuterRadius = aBottomInnerRadius + platingThickness;
1174
1175 const float zOther = aIsFront ? ( aSurfaceZ - aDepth ) : ( aSurfaceZ + aDepth );
1176 const float zTop = std::max( aSurfaceZ, zOther );
1177 const float zBot = std::min( aSurfaceZ, zOther );
1178
1179 if( topOuterRadius <= 0.0f || bottomOuterRadius <= 0.0f )
1180 return;
1181
1182 const float largestDiameter = 2.0f * std::max( aTopInnerRadius, aBottomInnerRadius );
1183 unsigned int segments = std::max( 12u, m_boardAdapter.GetCircleSegmentCount( largestDiameter ) );
1184
1185 const SFVEC3F copperColor = ConvertSRGBToLinear( m_boardAdapter.m_CopperColor );
1186
1187 auto addQuad = [&]( const SFVEC3F& p0, const SFVEC3F& p1,
1188 const SFVEC3F& p2, const SFVEC3F& p3 )
1189 {
1190 TRIANGLE* tri1 = new TRIANGLE( p0, p1, p2 );
1191 TRIANGLE* tri2 = new TRIANGLE( p0, p2, p3 );
1192
1193 tri1->SetMaterial( &m_materials.m_Copper );
1194 tri2->SetMaterial( &m_materials.m_Copper );
1195 tri1->SetColor( copperColor );
1196 tri2->SetColor( copperColor );
1197
1198 m_objectContainer.Add( tri1 );
1199 m_objectContainer.Add( tri2 );
1200 };
1201
1202 auto makePoint = [&]( float radius, float angle, float z )
1203 {
1204 return SFVEC3F( aCenter.x + cosf( angle ) * radius,
1205 aCenter.y + sinf( angle ) * radius,
1206 z );
1207 };
1208
1209 const float step = 2.0f * glm::pi<float>() / (float) segments;
1210
1211 SFVEC3F innerTopPrev = makePoint( aTopInnerRadius, 0.0f, zTop );
1212 SFVEC3F innerBotPrev = makePoint( aBottomInnerRadius, 0.0f, zBot );
1213 SFVEC3F outerTopPrev = makePoint( topOuterRadius, 0.0f, zTop );
1214 SFVEC3F outerBotPrev = makePoint( bottomOuterRadius, 0.0f, zBot );
1215
1216 const SFVEC3F innerTopFirst = innerTopPrev;
1217 const SFVEC3F innerBotFirst = innerBotPrev;
1218 const SFVEC3F outerTopFirst = outerTopPrev;
1219 const SFVEC3F outerBotFirst = outerBotPrev;
1220
1221 for( unsigned int i = 1; i <= segments; ++i )
1222 {
1223 const float angle = ( i == segments ) ? 0.0f : step * i;
1224
1225 const SFVEC3F innerTopCurr = ( i == segments ) ? innerTopFirst
1226 : makePoint( aTopInnerRadius, angle, zTop );
1227 const SFVEC3F innerBotCurr = ( i == segments ) ? innerBotFirst
1228 : makePoint( aBottomInnerRadius, angle, zBot );
1229 const SFVEC3F outerTopCurr = ( i == segments ) ? outerTopFirst
1230 : makePoint( topOuterRadius, angle, zTop );
1231 const SFVEC3F outerBotCurr = ( i == segments ) ? outerBotFirst
1232 : makePoint( bottomOuterRadius, angle, zBot );
1233
1234 // Inner wall
1235 addQuad( innerTopPrev, innerTopCurr, innerBotCurr, innerBotPrev );
1236
1237 // Outer wall
1238 addQuad( outerTopPrev, outerBotPrev, outerBotCurr, outerTopCurr );
1239
1240 // Top rim
1241 addQuad( outerTopPrev, outerTopCurr, innerTopCurr, innerTopPrev );
1242
1243 // Bottom rim
1244 addQuad( outerBotPrev, innerBotPrev, innerBotCurr, outerBotCurr );
1245
1246 innerTopPrev = innerTopCurr;
1247 innerBotPrev = innerBotCurr;
1248 outerTopPrev = outerTopCurr;
1249 outerBotPrev = outerBotCurr;
1250 }
1251}
1252
1253
1255{
1256 if( !m_boardAdapter.GetBoard() )
1257 return;
1258
1259 const float unitScale = m_boardAdapter.BiuTo3dUnits();
1260 const int platingThickness = m_boardAdapter.GetHolePlatingThickness();
1261 const float platingThickness3d = platingThickness * unitScale;
1262 const SFVEC3F boardColor = ConvertSRGBToLinear( m_boardAdapter.m_BoardBodyColor );
1263
1264 const float boardZTop = m_boardAdapter.GetLayerBottomZPos( F_Cu );
1265 const float boardZBot = m_boardAdapter.GetLayerBottomZPos( B_Cu );
1266
1267 // Process vias for backdrill and post-machining plugs
1268 for( const PCB_TRACK* track : m_boardAdapter.GetBoard()->Tracks() )
1269 {
1270 if( track->Type() != PCB_VIA_T )
1271 continue;
1272
1273 const PCB_VIA* via = static_cast<const PCB_VIA*>( track );
1274
1275 const float holeDiameter = via->GetDrillValue() * unitScale;
1276 const float holeInnerRadius = holeDiameter / 2.0f;
1277 const float holeOuterRadius = holeInnerRadius + platingThickness3d;
1278 const SFVEC2F center( via->GetStart().x * unitScale, -via->GetStart().y * unitScale );
1279
1280 PCB_LAYER_ID topLayer, bottomLayer;
1281 via->LayerPair( &topLayer, &bottomLayer );
1282
1283 const float viaZTop = m_boardAdapter.GetLayerBottomZPos( topLayer );
1284 const float viaZBot = m_boardAdapter.GetLayerBottomZPos( bottomLayer );
1285
1286 // Handle backdrill plugs
1287 const auto secondaryDrillSize = via->GetSecondaryDrillSize();
1288
1289 if( secondaryDrillSize.has_value() && secondaryDrillSize.value() > 0 )
1290 {
1291 const float backdrillRadius = secondaryDrillSize.value() * 0.5f * unitScale;
1292
1293 if( backdrillRadius > holeOuterRadius )
1294 {
1295 PCB_LAYER_ID secStart = via->GetSecondaryDrillStartLayer();
1296 PCB_LAYER_ID secEnd = via->GetSecondaryDrillEndLayer();
1297
1298 // Calculate where the backdrill ends and plug should start
1299 const float secEndZ = m_boardAdapter.GetLayerBottomZPos( secEnd );
1300
1301 float plugZTop, plugZBot;
1302
1303 if( secStart == F_Cu )
1304 {
1305 // Backdrill from top: plug goes from below backdrill end to via bottom
1306 plugZTop = secEndZ;
1307 plugZBot = viaZBot;
1308 }
1309 else
1310 {
1311 // Backdrill from bottom: plug goes from via top to above backdrill end
1312 plugZTop = viaZTop;
1313 plugZBot = secEndZ;
1314 }
1315
1316 if( plugZTop > plugZBot )
1317 {
1318 // Create a ring from holeOuterRadius to backdrillRadius
1319 RING_2D* ring = new RING_2D( center, holeOuterRadius, backdrillRadius, *via );
1321
1322 LAYER_ITEM* objPtr = new LAYER_ITEM( ring, plugZBot, plugZTop );
1323 objPtr->SetMaterial( &m_materials.m_EpoxyBoard );
1324 objPtr->SetColor( boardColor );
1325 m_objectContainer.Add( objPtr );
1326 }
1327 }
1328 }
1329
1330 // Handle front post-machining plugs
1331 const auto frontMode = via->GetFrontPostMachining();
1332
1333 if( frontMode.has_value()
1335 && frontMode.value() != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
1336 {
1337 const float frontRadius = via->GetFrontPostMachiningSize() * 0.5f * unitScale;
1338 const float frontDepth = via->GetFrontPostMachiningDepth() * unitScale;
1339
1340 if( frontRadius > holeOuterRadius && frontDepth > 0 )
1341 {
1342 // Plug goes from bottom of post-machining to bottom of via
1343 const float pmBottomZ = viaZTop - frontDepth;
1344 const float plugZBot = viaZBot;
1345
1346 if( pmBottomZ > plugZBot )
1347 {
1348 if( frontMode.value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
1349 {
1350 // For countersink, use a frustum (truncated cone)
1351 EDA_ANGLE angle( via->GetFrontPostMachiningAngle(), TENTHS_OF_A_DEGREE_T );
1352 float angleRad = angle.AsRadians();
1353 if( angleRad < 0.01f )
1354 angleRad = 0.01f;
1355
1356 float radialDiff = frontRadius - holeOuterRadius;
1357 float innerHeight = radialDiff / tanf( angleRad );
1358 float totalHeight = pmBottomZ - plugZBot;
1359
1360 if( innerHeight > totalHeight )
1361 innerHeight = totalHeight;
1362
1363 float zInnerTop = plugZBot + innerHeight;
1364
1365 // Create frustum from holeOuterRadius at zInnerTop to frontRadius at pmBottomZ
1366 TRUNCATED_CONE* frustum = new TRUNCATED_CONE( center, zInnerTop, pmBottomZ,
1367 holeOuterRadius, frontRadius );
1368 frustum->SetMaterial( &m_materials.m_EpoxyBoard );
1369 frustum->SetColor( boardColor );
1370 m_objectContainer.Add( frustum );
1371
1372 // If there's a cylindrical portion below the cone
1373 if( zInnerTop > plugZBot )
1374 {
1375 RING_2D* ring = new RING_2D( center, holeOuterRadius, frontRadius, *via );
1377
1378 LAYER_ITEM* objPtr = new LAYER_ITEM( ring, plugZBot, zInnerTop );
1379 objPtr->SetMaterial( &m_materials.m_EpoxyBoard );
1380 objPtr->SetColor( boardColor );
1381 m_objectContainer.Add( objPtr );
1382 }
1383 }
1384 else
1385 {
1386 RING_2D* ring = new RING_2D( center, holeOuterRadius, frontRadius, *via );
1388
1389 LAYER_ITEM* objPtr = new LAYER_ITEM( ring, plugZBot, pmBottomZ );
1390 objPtr->SetMaterial( &m_materials.m_EpoxyBoard );
1391 objPtr->SetColor( boardColor );
1392 m_objectContainer.Add( objPtr );
1393 }
1394 }
1395 }
1396 }
1397
1398 // Handle back post-machining plugs
1399 const auto backMode = via->GetBackPostMachining();
1400
1401 if( backMode.has_value()
1403 && backMode.value() != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
1404 {
1405 const float backRadius = via->GetBackPostMachiningSize() * 0.5f * unitScale;
1406 const float backDepth = via->GetBackPostMachiningDepth() * unitScale;
1407
1408 if( backRadius > holeOuterRadius && backDepth > 0 )
1409 {
1410 // Plug goes from top of via to top of post-machining
1411 const float plugZTop = viaZTop;
1412 const float pmTopZ = viaZBot + backDepth;
1413
1414 if( plugZTop > pmTopZ )
1415 {
1416 if( backMode.value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
1417 {
1418 // For countersink, use a frustum (truncated cone)
1419 EDA_ANGLE angle( via->GetBackPostMachiningAngle(), TENTHS_OF_A_DEGREE_T );
1420 float angleRad = angle.AsRadians();
1421 if( angleRad < 0.01f )
1422 angleRad = 0.01f;
1423
1424 float radialDiff = backRadius - holeOuterRadius;
1425 float innerHeight = radialDiff / tanf( angleRad );
1426 float totalHeight = plugZTop - pmTopZ;
1427
1428 if( innerHeight > totalHeight )
1429 innerHeight = totalHeight;
1430
1431 float zInnerBot = plugZTop - innerHeight;
1432
1433 // Create frustum from holeOuterRadius at zInnerBot to backRadius at pmTopZ
1434 TRUNCATED_CONE* frustum = new TRUNCATED_CONE( center, pmTopZ, zInnerBot,
1435 backRadius, holeOuterRadius );
1436 frustum->SetMaterial( &m_materials.m_EpoxyBoard );
1437 frustum->SetColor( boardColor );
1438 m_objectContainer.Add( frustum );
1439
1440 // If there's a cylindrical portion above the cone
1441 if( zInnerBot < plugZTop )
1442 {
1443 RING_2D* ring = new RING_2D( center, holeOuterRadius, backRadius, *via );
1445
1446 LAYER_ITEM* objPtr = new LAYER_ITEM( ring, zInnerBot, plugZTop );
1447 objPtr->SetMaterial( &m_materials.m_EpoxyBoard );
1448 objPtr->SetColor( boardColor );
1449 m_objectContainer.Add( objPtr );
1450 }
1451 }
1452 else
1453 {
1454 RING_2D* ring = new RING_2D( center, holeOuterRadius, backRadius, *via );
1456
1457 LAYER_ITEM* objPtr = new LAYER_ITEM( ring, pmTopZ, plugZTop );
1458 objPtr->SetMaterial( &m_materials.m_EpoxyBoard );
1459 objPtr->SetColor( boardColor );
1460 m_objectContainer.Add( objPtr );
1461 }
1462 }
1463 }
1464 }
1465 }
1466
1467 // Process pads for post-machining plugs
1468 for( const FOOTPRINT* footprint : m_boardAdapter.GetBoard()->Footprints() )
1469 {
1470 for( const PAD* pad : footprint->Pads() )
1471 {
1472 if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
1473 continue;
1474
1475 if( !pad->HasHole() )
1476 continue;
1477
1478 if( pad->GetDrillShape() != PAD_DRILL_SHAPE::CIRCLE )
1479 continue;
1480
1481 const SFVEC2F padCenter( pad->GetPosition().x * unitScale,
1482 -pad->GetPosition().y * unitScale );
1483 const float holeInnerRadius = pad->GetDrillSize().x * 0.5f * unitScale;
1484 const float holeOuterRadius = holeInnerRadius + platingThickness3d;
1485
1486 const float padZTop = boardZTop;
1487 const float padZBot = boardZBot;
1488
1489 // Handle front post-machining plugs for pads
1490 const auto frontMode = pad->GetFrontPostMachining();
1491
1492 if( frontMode.has_value()
1494 && frontMode.value() != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
1495 {
1496 const float frontRadius = pad->GetFrontPostMachiningSize() * 0.5f * unitScale;
1497 const float frontDepth = pad->GetFrontPostMachiningDepth() * unitScale;
1498
1499 if( frontRadius > holeOuterRadius && frontDepth > 0 )
1500 {
1501 const float pmBottomZ = padZTop - frontDepth;
1502 const float plugZBot = padZBot;
1503
1504 if( pmBottomZ > plugZBot )
1505 {
1506 if( frontMode.value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
1507 {
1508 // For countersink, use a frustum (truncated cone)
1509 EDA_ANGLE angle( pad->GetFrontPostMachiningAngle(), TENTHS_OF_A_DEGREE_T );
1510 float angleRad = angle.AsRadians();
1511 if( angleRad < 0.01f )
1512 angleRad = 0.01f;
1513
1514 float radialDiff = frontRadius - holeOuterRadius;
1515 float innerHeight = radialDiff / tanf( angleRad );
1516 float totalHeight = pmBottomZ - plugZBot;
1517
1518 if( innerHeight > totalHeight )
1519 innerHeight = totalHeight;
1520
1521 float zInnerTop = plugZBot + innerHeight;
1522
1523 // Create frustum from holeOuterRadius at zInnerTop to frontRadius at pmBottomZ
1524 TRUNCATED_CONE* frustum = new TRUNCATED_CONE( padCenter, zInnerTop, pmBottomZ,
1525 holeOuterRadius, frontRadius );
1526 frustum->SetMaterial( &m_materials.m_EpoxyBoard );
1527 frustum->SetColor( boardColor );
1528 m_objectContainer.Add( frustum );
1529
1530 // If there's a cylindrical portion below the cone
1531 if( zInnerTop > plugZBot )
1532 {
1533 RING_2D* ring = new RING_2D( padCenter, holeOuterRadius, frontRadius, *pad );
1535
1536 LAYER_ITEM* objPtr = new LAYER_ITEM( ring, plugZBot, zInnerTop );
1537 objPtr->SetMaterial( &m_materials.m_EpoxyBoard );
1538 objPtr->SetColor( boardColor );
1539 m_objectContainer.Add( objPtr );
1540 }
1541 }
1542 else
1543 {
1544 RING_2D* ring = new RING_2D( padCenter, holeOuterRadius, frontRadius, *pad );
1546
1547 LAYER_ITEM* objPtr = new LAYER_ITEM( ring, plugZBot, pmBottomZ );
1548 objPtr->SetMaterial( &m_materials.m_EpoxyBoard );
1549 objPtr->SetColor( boardColor );
1550 m_objectContainer.Add( objPtr );
1551 }
1552 }
1553 }
1554 }
1555
1556 // Handle back post-machining plugs for pads
1557 const auto backMode = pad->GetBackPostMachining();
1558
1559 if( backMode.has_value()
1561 && backMode.value() != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
1562 {
1563 const float backRadius = pad->GetBackPostMachiningSize() * 0.5f * unitScale;
1564 const float backDepth = pad->GetBackPostMachiningDepth() * unitScale;
1565
1566 if( backRadius > holeOuterRadius && backDepth > 0 )
1567 {
1568 const float plugZTop = padZTop;
1569 const float pmTopZ = padZBot + backDepth;
1570
1571 if( plugZTop > pmTopZ )
1572 {
1573 if( backMode.value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
1574 {
1575 // For countersink, use a frustum (truncated cone)
1576 EDA_ANGLE angle( pad->GetBackPostMachiningAngle(), TENTHS_OF_A_DEGREE_T );
1577 float angleRad = angle.AsRadians();
1578 if( angleRad < 0.01f )
1579 angleRad = 0.01f;
1580
1581 float radialDiff = backRadius - holeOuterRadius;
1582 float innerHeight = radialDiff / tanf( angleRad );
1583 float totalHeight = plugZTop - pmTopZ;
1584
1585 if( innerHeight > totalHeight )
1586 innerHeight = totalHeight;
1587
1588 float zInnerBot = plugZTop - innerHeight;
1589
1590 // Create frustum from holeOuterRadius at zInnerBot to backRadius at pmTopZ
1591 TRUNCATED_CONE* frustum = new TRUNCATED_CONE( padCenter, pmTopZ, zInnerBot,
1592 backRadius, holeOuterRadius );
1593 frustum->SetMaterial( &m_materials.m_EpoxyBoard );
1594 frustum->SetColor( boardColor );
1595 m_objectContainer.Add( frustum );
1596
1597 // If there's a cylindrical portion above the cone
1598 if( zInnerBot < plugZTop )
1599 {
1600 RING_2D* ring = new RING_2D( padCenter, holeOuterRadius, backRadius, *pad );
1602
1603 LAYER_ITEM* objPtr = new LAYER_ITEM( ring, zInnerBot, plugZTop );
1604 objPtr->SetMaterial( &m_materials.m_EpoxyBoard );
1605 objPtr->SetColor( boardColor );
1606 m_objectContainer.Add( objPtr );
1607 }
1608 }
1609 else
1610 {
1611 RING_2D* ring = new RING_2D( padCenter, holeOuterRadius, backRadius, *pad );
1613
1614 LAYER_ITEM* objPtr = new LAYER_ITEM( ring, pmTopZ, plugZTop );
1615 objPtr->SetMaterial( &m_materials.m_EpoxyBoard );
1616 objPtr->SetColor( boardColor );
1617 m_objectContainer.Add( objPtr );
1618 }
1619 }
1620 }
1621 }
1622 }
1623 }
1624}
1625
1626
1628{
1629 if( !m_boardAdapter.m_Cfg->m_Render.show_plated_barrels )
1630 return;
1631
1632 PCB_LAYER_ID top_layer, bottom_layer;
1633 int radiusBUI = ( aVia->GetDrillValue() / 2 );
1634
1635 aVia->LayerPair( &top_layer, &bottom_layer );
1636
1637 float frontDepth = 0.0f;
1638 float backDepth = 0.0f;
1639
1641 frontDepth = aVia->Padstack().FrontPostMachining().depth * m_boardAdapter.BiuTo3dUnits();
1642
1644 backDepth = aVia->Padstack().BackPostMachining().depth * m_boardAdapter.BiuTo3dUnits();
1645
1646 float topZ = m_boardAdapter.GetLayerBottomZPos( top_layer )
1647 + m_boardAdapter.GetFrontCopperThickness() - frontDepth;
1648
1649 float botZ = m_boardAdapter.GetLayerBottomZPos( bottom_layer )
1650 - m_boardAdapter.GetBackCopperThickness() + backDepth;
1651
1652 const float unitScale = m_boardAdapter.BiuTo3dUnits();
1653 const SFVEC2F center = SFVEC2F( aVia->GetStart().x * unitScale, -aVia->GetStart().y * unitScale );
1654
1655 RING_2D* ring = new RING_2D( center, radiusBUI * unitScale,
1656 ( radiusBUI + m_boardAdapter.GetHolePlatingThickness() ) * unitScale, *aVia );
1657
1659
1660 LAYER_ITEM* objPtr = new LAYER_ITEM( ring, topZ, botZ );
1661
1662 objPtr->SetMaterial( &m_materials.m_Copper );
1663 objPtr->SetColor( ConvertSRGBToLinear( m_boardAdapter.m_CopperColor ) );
1664
1665 m_objectContainer.Add( objPtr );
1666
1667 const float holeInnerRadius = radiusBUI * unitScale;
1668 const float frontSurface = topZ + frontDepth;
1669 const float backSurface = botZ - backDepth;
1670
1671 const PAD_DRILL_POST_MACHINING_MODE frontMode =
1673 const float frontRadius = 0.5f * aVia->GetFrontPostMachiningSize() * unitScale;
1674
1675 if( frontDepth > 0.0f && frontRadius > holeInnerRadius )
1676 {
1678 {
1679 addCounterborePlating( *aVia, center, frontRadius, frontDepth, frontSurface, true );
1680 }
1681 else if( frontMode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
1682 {
1683 addCountersinkPlating( center, frontRadius, holeInnerRadius, frontSurface, frontDepth,
1684 true );
1685 }
1686 }
1687
1688 const PAD_DRILL_POST_MACHINING_MODE backMode =
1690 const float backRadius = 0.5f * aVia->GetBackPostMachiningSize() * unitScale;
1691
1692 if( backDepth > 0.0f && backRadius > holeInnerRadius )
1693 {
1695 {
1696 addCounterborePlating( *aVia, center, backRadius, backDepth, backSurface, false );
1697 }
1698 else if( backMode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
1699 {
1700 addCountersinkPlating( center, backRadius, holeInnerRadius, backSurface, backDepth,
1701 false );
1702 }
1703 }
1704}
1705
1706
1708{
1709 if( !m_boardAdapter.m_Cfg->m_Render.show_plated_barrels )
1710 return;
1711
1712 const OBJECT_2D* object2d_A = nullptr;
1713
1714 SFVEC3F objColor = m_boardAdapter.m_CopperColor;
1715 const VECTOR2I drillsize = aPad->GetDrillSize();
1716 const bool hasHole = drillsize.x && drillsize.y;
1717 const float unitScale = m_boardAdapter.BiuTo3dUnits();
1718 const bool isRoundHole = drillsize.x == drillsize.y;
1719 SFVEC2F holeCenter = SFVEC2F( 0.0f, 0.0f );
1720 float holeInnerRadius = 0.0f;
1721
1722 if( !hasHole )
1723 return;
1724
1725 CONST_LIST_OBJECT2D antiOutlineIntersectionList;
1726
1727 float frontDepth = 0.0f;
1728 float backDepth = 0.0f;
1729
1731 frontDepth = aPad->GetFrontPostMachiningDepth() * m_boardAdapter.BiuTo3dUnits();
1732
1734 backDepth = aPad->GetBackPostMachiningDepth() * m_boardAdapter.BiuTo3dUnits();
1735
1736 const float topZ = m_boardAdapter.GetLayerBottomZPos( F_Cu )
1737 + m_boardAdapter.GetFrontCopperThickness() * 0.99f - frontDepth;
1738
1739 const float botZ = m_boardAdapter.GetLayerBottomZPos( B_Cu )
1740 - m_boardAdapter.GetBackCopperThickness() * 0.99f + backDepth;
1741
1742 if( isRoundHole ) // usual round hole
1743 {
1744 holeCenter = SFVEC2F( aPad->GetPosition().x * unitScale,
1745 -aPad->GetPosition().y * unitScale );
1746
1747 int innerRadius = drillsize.x / 2;
1748 int outerRadius = innerRadius + m_boardAdapter.GetHolePlatingThickness();
1749 holeInnerRadius = innerRadius * unitScale;
1750
1751 RING_2D* ring = new RING_2D( holeCenter, innerRadius * unitScale,
1752 outerRadius * unitScale, *aPad );
1753
1755
1756 object2d_A = ring;
1757
1758 // If the object (ring) is intersected by an antioutline board,
1759 // it will use instead a CSG of two circles.
1760 if( object2d_A && !m_antioutlineBoard2dObjects->GetList().empty() )
1761 {
1762 m_antioutlineBoard2dObjects->GetIntersectingObjects( object2d_A->GetBBox(),
1763 antiOutlineIntersectionList );
1764 }
1765
1766 if( !antiOutlineIntersectionList.empty() )
1767 {
1768 FILLED_CIRCLE_2D* innerCircle = new FILLED_CIRCLE_2D(
1769 holeCenter, innerRadius * unitScale, *aPad );
1770
1771 FILLED_CIRCLE_2D* outterCircle = new FILLED_CIRCLE_2D(
1772 holeCenter, outerRadius * unitScale, *aPad );
1773 std::vector<const OBJECT_2D*>* object2d_B = new std::vector<const OBJECT_2D*>();
1774 object2d_B->push_back( innerCircle );
1775
1776 LAYER_ITEM_2D* itemCSG2d = new LAYER_ITEM_2D( outterCircle, object2d_B, CSGITEM_FULL,
1777 *aPad );
1778
1779 m_containerWithObjectsToDelete.Add( itemCSG2d );
1780 m_containerWithObjectsToDelete.Add( innerCircle );
1781 m_containerWithObjectsToDelete.Add( outterCircle );
1782
1783 object2d_A = itemCSG2d;
1784 }
1785 }
1786 else // Oblong hole
1787 {
1788 VECTOR2I ends_offset;
1789 int width;
1790
1791 if( drillsize.x > drillsize.y ) // Horizontal oval
1792 {
1793 ends_offset.x = ( drillsize.x - drillsize.y ) / 2;
1794 width = drillsize.y;
1795 }
1796 else // Vertical oval
1797 {
1798 ends_offset.y = ( drillsize.y - drillsize.x ) / 2;
1799 width = drillsize.x;
1800 }
1801
1802 RotatePoint( ends_offset, aPad->GetOrientation() );
1803
1804 VECTOR2I start = VECTOR2I( aPad->GetPosition() ) + ends_offset;
1805 VECTOR2I end = VECTOR2I( aPad->GetPosition() ) - ends_offset;
1806
1807 ROUND_SEGMENT_2D* innerSeg =
1808 new ROUND_SEGMENT_2D( SFVEC2F( start.x * unitScale,
1809 -start.y * unitScale ),
1810 SFVEC2F( end.x * unitScale,
1811 -end.y * unitScale ),
1812 width * unitScale, *aPad );
1813
1814 ROUND_SEGMENT_2D* outerSeg =
1815 new ROUND_SEGMENT_2D( SFVEC2F( start.x * unitScale,
1816 -start.y * unitScale ),
1817 SFVEC2F( end.x * unitScale,
1818 -end.y * unitScale ),
1819 ( width + m_boardAdapter.GetHolePlatingThickness() * 2 )
1820 * unitScale, *aPad );
1821
1822 // NOTE: the round segment width is the "diameter", so we double the thickness
1823 std::vector<const OBJECT_2D*>* object2d_B = new std::vector<const OBJECT_2D*>();
1824 object2d_B->push_back( innerSeg );
1825
1826 LAYER_ITEM_2D* itemCSG2d = new LAYER_ITEM_2D( outerSeg, object2d_B, CSGITEM_FULL, *aPad );
1827
1828 m_containerWithObjectsToDelete.Add( itemCSG2d );
1829 m_containerWithObjectsToDelete.Add( innerSeg );
1830 m_containerWithObjectsToDelete.Add( outerSeg );
1831
1832 object2d_A = itemCSG2d;
1833
1834 if( object2d_A && !m_antioutlineBoard2dObjects->GetList().empty() )
1835 {
1836 m_antioutlineBoard2dObjects->GetIntersectingObjects( object2d_A->GetBBox(),
1837 antiOutlineIntersectionList );
1838 }
1839 }
1840
1841 if( object2d_A )
1842 {
1843 std::vector<const OBJECT_2D*>* object2d_B = new std::vector<const OBJECT_2D*>();
1844
1845 // Check if there are any other THT that intersects this hole
1846 // It will use the non inflated holes
1847 if( !m_boardAdapter.GetTH_IDs().GetList().empty() )
1848 {
1849 CONST_LIST_OBJECT2D intersecting;
1850
1851 m_boardAdapter.GetTH_IDs().GetIntersectingObjects( object2d_A->GetBBox(), intersecting );
1852
1853 for( const OBJECT_2D* hole2d : intersecting )
1854 {
1855 if( object2d_A->Intersects( hole2d->GetBBox() ) )
1856 object2d_B->push_back( hole2d );
1857 }
1858 }
1859
1860 for( const OBJECT_2D* obj : antiOutlineIntersectionList )
1861 object2d_B->push_back( obj );
1862
1863 if( object2d_B->empty() )
1864 {
1865 delete object2d_B;
1866 object2d_B = CSGITEM_EMPTY;
1867 }
1868
1869 if( object2d_B == CSGITEM_EMPTY )
1870 {
1871 LAYER_ITEM* objPtr = new LAYER_ITEM( object2d_A, topZ, botZ );
1872
1873 objPtr->SetMaterial( &m_materials.m_Copper );
1874 objPtr->SetColor( ConvertSRGBToLinear( objColor ) );
1875 m_objectContainer.Add( objPtr );
1876 }
1877 else
1878 {
1879 LAYER_ITEM_2D* itemCSG2d = new LAYER_ITEM_2D( object2d_A, object2d_B, CSGITEM_FULL,
1880 *aPad );
1881
1882 m_containerWithObjectsToDelete.Add( itemCSG2d );
1883
1884 LAYER_ITEM* objPtr = new LAYER_ITEM( itemCSG2d, topZ, botZ );
1885
1886 objPtr->SetMaterial( &m_materials.m_Copper );
1887 objPtr->SetColor( ConvertSRGBToLinear( objColor ) );
1888
1889 m_objectContainer.Add( objPtr );
1890 }
1891 }
1892
1893 if( object2d_A && isRoundHole )
1894 {
1895 const float frontSurface = topZ + frontDepth;
1896 const float backSurface = botZ - backDepth;
1897
1898 const PAD_DRILL_POST_MACHINING_MODE frontMode =
1900 const float frontRadius = 0.5f * aPad->GetFrontPostMachiningSize() * unitScale;
1901
1902 if( frontDepth > 0.0f && frontRadius > holeInnerRadius )
1903 {
1905 {
1906 addCounterborePlating( *aPad, holeCenter, frontRadius, frontDepth, frontSurface,
1907 true );
1908 }
1909 else if( frontMode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
1910 {
1911 addCountersinkPlating( holeCenter, frontRadius, holeInnerRadius, frontSurface,
1912 frontDepth, true );
1913 }
1914 }
1915
1916 const PAD_DRILL_POST_MACHINING_MODE backMode =
1918 const float backRadius = 0.5f * aPad->GetBackPostMachiningSize() * unitScale;
1919
1920 if( backDepth > 0.0f && backRadius > holeInnerRadius )
1921 {
1923 {
1924 addCounterborePlating( *aPad, holeCenter, backRadius, backDepth, backSurface,
1925 false );
1926 }
1927 else if( backMode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
1928 {
1929 addCountersinkPlating( holeCenter, backRadius, holeInnerRadius, backSurface,
1930 backDepth, false );
1931 }
1932 }
1933 }
1934}
1935
1936
1938{
1939 if( !m_boardAdapter.GetBoard() )
1940 return;
1941
1942 // Insert plated vertical holes inside the board
1943
1944 // Insert vias holes (vertical cylinders)
1945 for( PCB_TRACK* track : m_boardAdapter.GetBoard()->Tracks() )
1946 {
1947 if( track->Type() == PCB_VIA_T )
1948 {
1949 const PCB_VIA* via = static_cast<const PCB_VIA*>( track );
1950 insertHole( via );
1951 }
1952 }
1953
1954 // Insert pads holes (vertical cylinders)
1955 for( FOOTPRINT* footprint : m_boardAdapter.GetBoard()->Footprints() )
1956 {
1957 for( PAD* pad : footprint->Pads() )
1958 {
1959 if( pad->GetAttribute() != PAD_ATTRIB::NPTH )
1960 insertHole( pad );
1961 }
1962 }
1963}
1964
1965
1967 const glm::mat4& aFpMatrix, bool aHasExtrudedBody,
1968 float aOpacity )
1969{
1970 if( aHasExtrudedBody )
1971 return;
1972
1973 BOX2I localBox = CalcPlaceholderLocalBox( aFootprint );
1974
1975 float bboxW = std::abs( localBox.GetWidth() ) / pcbIUScale.IU_PER_MM * 0.9f;
1976 float bboxH = std::abs( localBox.GetHeight() ) / pcbIUScale.IU_PER_MM * 0.9f;
1977 float scaleZ = std::min( bboxW, bboxH ) * 0.5f;
1978
1979 VECTOR2I localCenter = localBox.GetCenter();
1980 float offsetX = localCenter.x / pcbIUScale.IU_PER_MM;
1981 float offsetY = -localCenter.y / pcbIUScale.IU_PER_MM;
1982
1983 if( aFootprint->IsFlipped() )
1984 offsetY = -offsetY;
1985
1986 SFVEC3F boxMin( offsetX - bboxW * 0.5f, offsetY - bboxH * 0.5f, 0.0f );
1987 SFVEC3F boxMax( offsetX + bboxW * 0.5f, offsetY + bboxH * 0.5f, scaleZ );
1988
1989 SFVEC3F corners[8];
1990
1991 for( int i = 0; i < 8; ++i )
1992 {
1993 SFVEC3F corner( ( i & 1 ) ? boxMax.x : boxMin.x, ( i & 2 ) ? boxMax.y : boxMin.y,
1994 ( i & 4 ) ? boxMax.z : boxMin.z );
1995
1996 corners[i] = SFVEC3F( aFpMatrix * glm::vec4( corner, 1.0f ) );
1997 }
1998
1999 static const int faces[6][4] = { { 4, 5, 7, 6 }, { 0, 2, 3, 1 }, { 0, 1, 5, 4 },
2000 { 3, 2, 6, 7 }, { 0, 4, 6, 2 }, { 1, 3, 7, 5 } };
2001
2002 auto addTriangle = [&]( const SFVEC3F& aV0, const SFVEC3F& aV1, const SFVEC3F& aV2 )
2003 {
2004 TRIANGLE* triangle = new TRIANGLE( aV0, aV1, aV2 );
2005 triangle->SetBoardItem( const_cast<FOOTPRINT*>( aFootprint ) );
2006 triangle->SetMaterial( &m_materials.m_EpoxyBoard );
2007 triangle->SetModelTransparency( 1.0f - aOpacity );
2008 triangle->SetColor( SFVEC3F( 1.0f, 0.5f, 0.0f ) );
2009 aDstContainer.Add( triangle );
2010 };
2011
2012 for( const int* face : faces )
2013 {
2014 addTriangle( corners[face[0]], corners[face[2]], corners[face[1]] );
2015 addTriangle( corners[face[0]], corners[face[3]], corners[face[2]] );
2016 }
2017}
2018
2019
2020static size_t addOutlineToRaytracerObjects( CONTAINER_2D& aObjContainer, const SHAPE_POLY_SET& aOutline, float aBiuTo3d,
2021 const BOARD_ITEM& aBoardItem )
2022{
2023 size_t added = 0;
2024
2025 for( int oi = 0; oi < aOutline.OutlineCount(); oi++ )
2026 {
2027 const SHAPE_LINE_CHAIN& chain = aOutline.COutline( oi );
2028
2029 if( chain.PointCount() < 3 )
2030 continue;
2031
2032 // Convert points to 3D-scaled coordinates
2033 SEGMENTS_WIDTH_NORMALS segNormals;
2034 SFVEC2F prevPt;
2035
2036 for( int i = 0; i < chain.PointCount(); i++ )
2037 {
2038 const VECTOR2I& a = chain.CPoint( i );
2039 SFVEC2F pt( (float) a.x * aBiuTo3d, (float) ( -a.y ) * aBiuTo3d );
2040
2041 if( ( i == 0 ) || ( fabs( prevPt.x - pt.x ) > FLT_EPSILON ) || ( fabs( prevPt.y - pt.y ) > FLT_EPSILON ) )
2042 {
2043 prevPt = pt;
2044
2046 sn.m_Start = pt;
2047 segNormals.push_back( sn );
2048 }
2049 }
2050
2051 // Build side-wall
2052 if( segNormals.size() >= 3 )
2053 {
2054 std::vector<SFVEC2F> tmpNormals( segNormals.size() );
2055 unsigned int j = segNormals.size() - 1;
2056
2057 for( unsigned int i = 0; i < segNormals.size(); j = i++ )
2058 {
2059 SFVEC2F slope = segNormals[j].m_Start - segNormals[i].m_Start;
2060 segNormals[i].m_Precalc_slope = slope;
2061 tmpNormals[i] = glm::normalize( SFVEC2F( slope.y, -slope.x ) );
2062 }
2063
2064 j = segNormals.size() - 1;
2065
2066 for( unsigned int i = 0; i < segNormals.size(); j = i++ )
2067 {
2068 const SFVEC2F& nBefore = tmpNormals[j];
2069 const SFVEC2F& nCur = tmpNormals[i];
2070 const SFVEC2F& nAfter = tmpNormals[( i + 1 ) % segNormals.size()];
2071
2072 float dotBefore = glm::dot( nBefore, nCur );
2073 float dotAfter = glm::dot( nAfter, nCur );
2074
2075 segNormals[i].m_Normals.m_Start =
2076 ( dotBefore < 0.7f ) ? nCur : glm::normalize( nBefore * dotBefore + nCur );
2077 segNormals[i].m_Normals.m_End = ( dotAfter < 0.7f ) ? nCur : glm::normalize( nAfter * dotAfter + nCur );
2078 }
2079
2080 SEGMENTS capSegments( segNormals.size() );
2081
2082 for( unsigned int i = 0; i < segNormals.size(); i++ )
2083 capSegments[i].m_Start = segNormals[i].m_Start;
2084
2085 j = capSegments.size() - 1;
2086
2087 for( unsigned int i = 0; i < capSegments.size(); j = i++ )
2088 {
2089 capSegments[i].m_inv_JY_minus_IY = 1.0f / ( capSegments[j].m_Start.y - capSegments[i].m_Start.y );
2090 capSegments[i].m_JX_minus_IX = capSegments[j].m_Start.x - capSegments[i].m_Start.x;
2091 }
2092
2093 OUTERS_AND_HOLES outersAndHoles;
2094 outersAndHoles.m_Outers.push_back( capSegments );
2095
2096 aObjContainer.Add( new POLYGON_2D( segNormals, outersAndHoles, aBoardItem ) );
2097 added++;
2098 }
2099 }
2100
2101 // top/bottom caps
2102 size_t prevSize = aObjContainer.GetList().size();
2103 ConvertPolygonToTriangles( aOutline, aObjContainer, aBiuTo3d, aBoardItem );
2104 added += aObjContainer.GetList().size() - prevSize;
2105
2106 return added;
2107}
2108
2109
2111{
2112 SHAPE_POLY_SET outline;
2113
2114 if( !GetExtrusionOutline( aFootprint, outline ) )
2115 return false;
2116
2117 if( outline.OutlineCount() == 0 )
2118 return false;
2119
2120 outline.Simplify();
2121
2122 const EXTRUDED_3D_BODY* body = aFootprint->GetExtrudedBody();
2123 const float biuTo3d = m_boardAdapter.BiuTo3dUnits();
2124
2125 VECTOR2I fpPos = aFootprint->GetPosition();
2126 ApplyExtrusionTransform( outline, body, fpPos );
2127
2128 bool isBack = aFootprint->IsFlipped();
2129 float boardSurfaceZ = m_boardAdapter.GetFootprintZPos( isBack );
2130 float standoff3d = body->m_standoff * biuTo3d;
2131 float bodyThickness = ( body->m_height - body->m_standoff ) * biuTo3d * body->m_scale.z;
2132 float zOffset3d = pcbIUScale.mmToIU( body->m_offset.z ) * biuTo3d;
2133
2134 float zBot, zTop;
2135
2136 if( !isBack )
2137 {
2138 zBot = boardSurfaceZ + standoff3d + zOffset3d;
2139 zTop = zBot + bodyThickness;
2140 }
2141 else
2142 {
2143 zTop = boardSurfaceZ - standoff3d - zOffset3d;
2144 zBot = zTop - bodyThickness;
2145 }
2146
2147 KIGFX::COLOR4D c = body->m_color;
2148
2151
2152 SFVEC3F objColor = ConvertSRGBToLinear( SFVEC3F( c.r, c.g, c.b ) );
2153
2154 // Build body 2D objects (side walls + caps)
2155 outline.Fracture();
2156
2157 const LIST_OBJECT2D& objList = m_containerWithObjectsToDelete.GetList();
2158 size_t prevCount = objList.size();
2159
2160 addOutlineToRaytracerObjects( m_containerWithObjectsToDelete, outline, biuTo3d, *aFootprint );
2161
2162 EXTRUSION_MATERIAL_PROPS props = GetMaterialProps( body->m_material, objColor );
2163
2164 m_extrusionMaterials.push_back( std::make_unique<BLINN_PHONG_MATERIAL>(
2165 props.m_Ambient, SFVEC3F( 0.0f ), props.m_Specular, props.m_Shininess, 0.0f, 0.0f ) );
2166
2167 const MATERIAL* bodyMaterial = m_extrusionMaterials.back().get();
2168
2169 auto it = objList.begin();
2170 std::advance( it, prevCount );
2171
2172 for( ; it != objList.end(); ++it )
2173 {
2174 LAYER_ITEM* layerItem = new LAYER_ITEM( *it, zBot, zTop );
2175 layerItem->SetBoardItem( const_cast<FOOTPRINT*>( aFootprint ) );
2176 layerItem->SetMaterial( bodyMaterial );
2177 layerItem->SetColor( objColor );
2178 layerItem->SetModelTransparency( 1.0f - (float) c.a );
2179 aDstContainer.Add( layerItem );
2180 }
2181
2182 // Create metallic pin extrusions for THT pads (from opposite board side to standoff height)
2183 if( standoff3d > 0.0f )
2184 {
2185 SHAPE_POLY_SET pinPoly;
2186
2187 if( GetExtrusionPinOutline( aFootprint, pinPoly ) )
2188 {
2189 ApplyExtrusionTransform( pinPoly, body, fpPos );
2190
2191 float oppositeSurfaceZ = m_boardAdapter.GetFootprintZPos( !isBack );
2192 float protrusion = 1.0f * pcbIUScale.IU_PER_MM * biuTo3d;
2193 float pinZBot, pinZTop;
2194
2195 if( !isBack )
2196 {
2197 pinZBot = oppositeSurfaceZ - protrusion;
2198 pinZTop = boardSurfaceZ + standoff3d;
2199 }
2200 else
2201 {
2202 pinZTop = oppositeSurfaceZ + protrusion;
2203 pinZBot = boardSurfaceZ - standoff3d;
2204 }
2205
2206 SFVEC3F metalColor = ConvertSRGBToLinear( SFVEC3F( 0.75f, 0.75f, 0.75f ) );
2207
2208 pinPoly.Fracture();
2209
2210 size_t prevPinCount = objList.size();
2211
2212 addOutlineToRaytracerObjects( m_containerWithObjectsToDelete, pinPoly, biuTo3d, *aFootprint );
2213
2214 // Wrap pin objects with material and Z extents
2215 auto pinIt = objList.begin();
2216 std::advance( pinIt, prevPinCount );
2217
2218 for( ; pinIt != objList.end(); ++pinIt )
2219 {
2220 LAYER_ITEM* layerItem = new LAYER_ITEM( *pinIt, pinZBot, pinZTop );
2221 layerItem->SetBoardItem( const_cast<FOOTPRINT*>( aFootprint ) );
2222 layerItem->SetMaterial( &m_materials.m_Copper );
2223 layerItem->SetColor( metalColor );
2224 aDstContainer.Add( layerItem );
2225 }
2226 }
2227 }
2228
2229 return true;
2230}
2231
2232
2233void RENDER_3D_RAYTRACE_BASE::load3DModels( CONTAINER_3D& aDstContainer, bool aSkipMaterialInformation,
2234 std::stop_token aStop )
2235{
2236 if( !m_boardAdapter.GetBoard() )
2237 return;
2238
2239 if( !m_boardAdapter.m_IsPreviewer
2240 && !m_boardAdapter.m_Cfg->m_Render.show_footprints_normal
2241 && !m_boardAdapter.m_Cfg->m_Render.show_footprints_insert
2242 && !m_boardAdapter.m_Cfg->m_Render.show_footprints_virtual )
2243 {
2244 return;
2245 }
2246
2247 // Go for all footprints
2248 for( FOOTPRINT* fp : m_boardAdapter.GetBoard()->Footprints() )
2249 {
2250 if( aStop.stop_requested() )
2251 return;
2252
2253 bool hasModels = !fp->Models().empty();
2254 bool showMissing = m_boardAdapter.m_Cfg->m_Render.show_missing_models;
2255
2256 // Placeholder is suppressed when an extrusion was built.
2257 bool hasExtrudedBody = false;
2258
2259 if( fp->HasExtrudedBody() && fp->GetExtrudedBody()->m_show && m_boardAdapter.IsFootprintShown( fp ) )
2260 hasExtrudedBody = addExtrudedBodyToRaytracer( aDstContainer, fp );
2261
2262 if( ( hasModels || showMissing ) && m_boardAdapter.IsFootprintShown( fp ) )
2263 {
2264 double zpos = m_boardAdapter.GetFootprintZPos( fp->IsFlipped() );
2265
2266 VECTOR2I pos = fp->GetPosition();
2267
2268 glm::mat4 fpMatrix = glm::mat4( 1.0f );
2269
2270 fpMatrix = glm::translate( fpMatrix,
2271 SFVEC3F( pos.x * m_boardAdapter.BiuTo3dUnits(),
2272 -pos.y * m_boardAdapter.BiuTo3dUnits(),
2273 zpos ) );
2274
2275 if( !fp->GetOrientation().IsZero() )
2276 {
2277 fpMatrix = glm::rotate( fpMatrix, (float) fp->GetOrientation().AsRadians(),
2278 SFVEC3F( 0.0f, 0.0f, 1.0f ) );
2279 }
2280
2281 if( fp->IsFlipped() )
2282 {
2283 fpMatrix = glm::rotate( fpMatrix, glm::pi<float>(), SFVEC3F( 0.0f, 1.0f, 0.0f ) );
2284
2285 fpMatrix = glm::rotate( fpMatrix, glm::pi<float>(), SFVEC3F( 0.0f, 0.0f, 1.0f ) );
2286 }
2287
2288 const double modelunit_to_3d_units_factor =
2289 m_boardAdapter.BiuTo3dUnits() * UNITS3D_TO_UNITSPCB;
2290
2291 fpMatrix = glm::scale(
2292 fpMatrix, SFVEC3F( modelunit_to_3d_units_factor, modelunit_to_3d_units_factor,
2293 modelunit_to_3d_units_factor ) );
2294
2295 // Get the list of model files for this model
2296 S3D_CACHE* cacheMgr = m_boardAdapter.Get3dCacheManager();
2297
2298 wxString libraryName = fp->GetFPID().GetLibNickname();
2299
2300 wxString footprintBasePath = wxEmptyString;
2301
2302 if( m_boardAdapter.GetBoard()->GetProject() )
2303 {
2304 try
2305 {
2306 // FindRow() can throw an exception
2307 std::optional<LIBRARY_TABLE_ROW*> fpRow =
2308 PROJECT_PCB::FootprintLibAdapter( m_boardAdapter.GetBoard()->GetProject() )
2309 ->GetRow( libraryName );
2310
2311 if( fpRow )
2312 footprintBasePath = LIBRARY_MANAGER::GetFullURI( *fpRow, true );
2313 }
2314 catch( ... )
2315 {
2316 // Do nothing if the libraryName is not found in lib table
2317 }
2318 }
2319
2320 bool placeholderAdded = false;
2321
2322 for( FP_3DMODEL& model : fp->Models() )
2323 {
2324 if( !model.m_Show || model.m_Filename.empty() )
2325 continue;
2326
2327 // get it from cache
2328 std::vector<const EMBEDDED_FILES*> embeddedFilesStack;
2329 embeddedFilesStack.push_back( fp->GetEmbeddedFiles() );
2330 embeddedFilesStack.push_back( m_boardAdapter.GetBoard()->GetEmbeddedFiles() );
2331
2332 const S3DMODEL* modelPtr = cacheMgr->GetModel( model.m_Filename, footprintBasePath,
2333 std::move( embeddedFilesStack ) );
2334
2335 // only add it if the return is not NULL.
2336 if( modelPtr )
2337 {
2338 glm::mat4 modelMatrix = fpMatrix;
2339
2340 modelMatrix = glm::translate( modelMatrix,
2341 SFVEC3F( model.m_Offset.x, model.m_Offset.y, model.m_Offset.z ) );
2342
2343 modelMatrix = glm::rotate( modelMatrix,
2344 (float) -( model.m_Rotation.z / 180.0f ) * glm::pi<float>(),
2345 SFVEC3F( 0.0f, 0.0f, 1.0f ) );
2346
2347 modelMatrix = glm::rotate( modelMatrix,
2348 (float) -( model.m_Rotation.y / 180.0f ) * glm::pi<float>(),
2349 SFVEC3F( 0.0f, 1.0f, 0.0f ) );
2350
2351 modelMatrix = glm::rotate( modelMatrix,
2352 (float) -( model.m_Rotation.x / 180.0f ) * glm::pi<float>(),
2353 SFVEC3F( 1.0f, 0.0f, 0.0f ) );
2354
2355 modelMatrix = glm::scale( modelMatrix,
2356 SFVEC3F( model.m_Scale.x, model.m_Scale.y, model.m_Scale.z ) );
2357
2358 addModels( aDstContainer, modelPtr, modelMatrix, (float) model.m_Opacity,
2359 aSkipMaterialInformation, fp );
2360 }
2361 else if( showMissing && !placeholderAdded )
2362 {
2363 addPlaceholderToRaytracer( aDstContainer, fp, fpMatrix, hasExtrudedBody, (float) model.m_Opacity );
2364 placeholderAdded = true;
2365 }
2366 }
2367
2368 // Footprint with no models assigned at all
2369 if( !hasModels && showMissing )
2370 {
2371 addPlaceholderToRaytracer( aDstContainer, fp, fpMatrix, hasExtrudedBody, 1.0f );
2372 }
2373 }
2374 }
2375}
2376
2377
2379{
2380 MODEL_MATERIALS* materialVector;
2381
2382 // Try find if the materials already exists in the map list
2383 if( m_modelMaterialMap.find( a3DModel ) != m_modelMaterialMap.end() )
2384 {
2385 // Found it, so get the pointer
2386 materialVector = &m_modelMaterialMap[a3DModel];
2387 }
2388 else
2389 {
2390 // Materials was not found in the map, so it will create a new for
2391 // this model.
2392
2393 m_modelMaterialMap[a3DModel] = MODEL_MATERIALS();
2394 materialVector = &m_modelMaterialMap[a3DModel];
2395
2396 materialVector->resize( a3DModel->m_MaterialsSize );
2397
2398 for( unsigned int imat = 0; imat < a3DModel->m_MaterialsSize; ++imat )
2399 {
2400 if( m_boardAdapter.m_Cfg->m_Render.material_mode == MATERIAL_MODE::NORMAL )
2401 {
2402 const SMATERIAL& material = a3DModel->m_Materials[imat];
2403
2404 // http://www.fooplot.com/#W3sidHlwZSI6MCwiZXEiOiJtaW4oc3FydCh4LTAuMzUpKjAuNDAtMC4wNSwxLjApIiwiY29sb3IiOiIjMDAwMDAwIn0seyJ0eXBlIjoxMDAwLCJ3aW5kb3ciOlsiMC4wNzA3NzM2NzMyMzY1OTAxMiIsIjEuNTY5NTcxNjI5MjI1NDY5OCIsIi0wLjI3NDYzNTMyMTc1OTkyOTMiLCIwLjY0NzcwMTg4MTkyNTUzNjIiXSwic2l6ZSI6WzY0NCwzOTRdfV0-
2405
2406 float reflectionFactor = 0.0f;
2407
2408 if( ( material.m_Shininess - 0.35f ) > FLT_EPSILON )
2409 {
2410 reflectionFactor = glm::clamp(
2411 glm::sqrt( ( material.m_Shininess - 0.35f ) ) * 0.40f - 0.05f, 0.0f,
2412 0.5f );
2413 }
2414
2415 BLINN_PHONG_MATERIAL& blinnMaterial = ( *materialVector )[imat];
2416
2417 blinnMaterial = BLINN_PHONG_MATERIAL( ConvertSRGBToLinear( material.m_Ambient ),
2418 ConvertSRGBToLinear( material.m_Emissive ),
2419 ConvertSRGBToLinear( material.m_Specular ), material.m_Shininess * 180.0f,
2420 material.m_Transparency, reflectionFactor );
2421
2422 if( m_boardAdapter.m_Cfg->m_Render.raytrace_procedural_textures )
2423 {
2424 // Guess material type and apply a normal perturbator
2425 if( ( RGBtoGray( material.m_Diffuse ) < 0.3f )
2426 && ( material.m_Shininess < 0.36f )
2427 && ( material.m_Transparency == 0.0f )
2428 && ( ( glm::abs( material.m_Diffuse.r - material.m_Diffuse.g ) < 0.15f )
2429 && ( glm::abs( material.m_Diffuse.b - material.m_Diffuse.g )
2430 < 0.15f )
2431 && ( glm::abs( material.m_Diffuse.r - material.m_Diffuse.b )
2432 < 0.15f ) ) )
2433 {
2434 // This may be a black plastic..
2435 blinnMaterial.SetGenerator( &m_plasticMaterial );
2436 }
2437 else
2438 {
2439 if( ( RGBtoGray( material.m_Diffuse ) > 0.3f )
2440 && ( material.m_Shininess < 0.30f )
2441 && ( material.m_Transparency == 0.0f )
2442 && ( ( glm::abs( material.m_Diffuse.r - material.m_Diffuse.g ) > 0.25f )
2443 || ( glm::abs( material.m_Diffuse.b - material.m_Diffuse.g ) > 0.25f )
2444 || ( glm::abs( material.m_Diffuse.r - material.m_Diffuse.b )
2445 > 0.25f ) ) )
2446 {
2447 // This may be a color plastic ...
2448 blinnMaterial.SetGenerator( &m_shinyPlasticMaterial );
2449 }
2450 else
2451 {
2452 if( ( RGBtoGray( material.m_Diffuse ) > 0.6f )
2453 && ( material.m_Shininess > 0.35f )
2454 && ( material.m_Transparency == 0.0f )
2455 && ( ( glm::abs( material.m_Diffuse.r - material.m_Diffuse.g )
2456 < 0.40f )
2457 && ( glm::abs( material.m_Diffuse.b - material.m_Diffuse.g )
2458 < 0.40f )
2459 && ( glm::abs( material.m_Diffuse.r - material.m_Diffuse.b )
2460 < 0.40f ) ) )
2461 {
2462 // This may be a brushed metal
2463 blinnMaterial.SetGenerator( &m_brushedMetalMaterial );
2464 }
2465 }
2466 }
2467 }
2468 }
2469 else
2470 {
2471 ( *materialVector )[imat] = BLINN_PHONG_MATERIAL(
2472 SFVEC3F( 0.2f ), SFVEC3F( 0.0f ), SFVEC3F( 0.0f ), 0.0f, 0.0f, 0.0f );
2473 }
2474 }
2475 }
2476
2477 return materialVector;
2478}
2479
2480
2481void RENDER_3D_RAYTRACE_BASE::addModels( CONTAINER_3D& aDstContainer, const S3DMODEL* a3DModel,
2482 const glm::mat4& aModelMatrix, float aFPOpacity,
2483 bool aSkipMaterialInformation, BOARD_ITEM* aBoardItem )
2484{
2485 // Validate a3DModel pointers
2486 wxASSERT( a3DModel != nullptr );
2487
2488 if( a3DModel == nullptr )
2489 return;
2490
2491 wxASSERT( a3DModel->m_Materials != nullptr );
2492 wxASSERT( a3DModel->m_Meshes != nullptr );
2493 wxASSERT( a3DModel->m_MaterialsSize > 0 );
2494 wxASSERT( a3DModel->m_MeshesSize > 0 );
2495
2496 if( aFPOpacity > 1.0f )
2497 aFPOpacity = 1.0f;
2498
2499 if( aFPOpacity < 0.0f )
2500 aFPOpacity = 0.0f;
2501
2502 if( ( a3DModel->m_Materials != nullptr ) && ( a3DModel->m_Meshes != nullptr )
2503 && ( a3DModel->m_MaterialsSize > 0 ) && ( a3DModel->m_MeshesSize > 0 ) )
2504 {
2505 MODEL_MATERIALS* materialVector = nullptr;
2506
2507 if( !aSkipMaterialInformation )
2508 {
2509 materialVector = getModelMaterial( a3DModel );
2510 }
2511
2512 const glm::mat3 normalMatrix = glm::transpose( glm::inverse( glm::mat3( aModelMatrix ) ) );
2513
2514 for( unsigned int mesh_i = 0; mesh_i < a3DModel->m_MeshesSize; ++mesh_i )
2515 {
2516 const SMESH& mesh = a3DModel->m_Meshes[mesh_i];
2517
2518 // Validate the mesh pointers
2519 wxASSERT( mesh.m_Positions != nullptr );
2520 wxASSERT( mesh.m_FaceIdx != nullptr );
2521 wxASSERT( mesh.m_Normals != nullptr );
2522 wxASSERT( mesh.m_FaceIdxSize > 0 );
2523 wxASSERT( ( mesh.m_FaceIdxSize % 3 ) == 0 );
2524
2525
2526 if( ( mesh.m_Positions != nullptr ) && ( mesh.m_Normals != nullptr )
2527 && ( mesh.m_FaceIdx != nullptr ) && ( mesh.m_FaceIdxSize > 0 )
2528 && ( mesh.m_VertexSize > 0 ) && ( ( mesh.m_FaceIdxSize % 3 ) == 0 )
2529 && ( mesh.m_MaterialIdx < a3DModel->m_MaterialsSize ) )
2530 {
2531 float fpTransparency;
2532 const BLINN_PHONG_MATERIAL* blinn_material;
2533
2534 if( !aSkipMaterialInformation )
2535 {
2536 blinn_material = &( *materialVector )[mesh.m_MaterialIdx];
2537
2538 fpTransparency =
2539 1.0f - ( ( 1.0f - blinn_material->GetTransparency() ) * aFPOpacity );
2540 }
2541
2542 // Add all face triangles
2543 for( unsigned int faceIdx = 0; faceIdx < mesh.m_FaceIdxSize; faceIdx += 3 )
2544 {
2545 const unsigned int idx0 = mesh.m_FaceIdx[faceIdx + 0];
2546 const unsigned int idx1 = mesh.m_FaceIdx[faceIdx + 1];
2547 const unsigned int idx2 = mesh.m_FaceIdx[faceIdx + 2];
2548
2549 wxASSERT( idx0 < mesh.m_VertexSize );
2550 wxASSERT( idx1 < mesh.m_VertexSize );
2551 wxASSERT( idx2 < mesh.m_VertexSize );
2552
2553 if( IsTriangleInRange( mesh.m_FaceIdx, faceIdx, mesh.m_VertexSize ) )
2554 {
2555 const SFVEC3F& v0 = mesh.m_Positions[idx0];
2556 const SFVEC3F& v1 = mesh.m_Positions[idx1];
2557 const SFVEC3F& v2 = mesh.m_Positions[idx2];
2558
2559 const SFVEC3F& n0 = mesh.m_Normals[idx0];
2560 const SFVEC3F& n1 = mesh.m_Normals[idx1];
2561 const SFVEC3F& n2 = mesh.m_Normals[idx2];
2562
2563 // Transform vertex with the model matrix
2564 const SFVEC3F vt0 = SFVEC3F( aModelMatrix * glm::vec4( v0, 1.0f ) );
2565 const SFVEC3F vt1 = SFVEC3F( aModelMatrix * glm::vec4( v1, 1.0f ) );
2566 const SFVEC3F vt2 = SFVEC3F( aModelMatrix * glm::vec4( v2, 1.0f ) );
2567
2568 const SFVEC3F nt0 = glm::normalize( SFVEC3F( normalMatrix * n0 ) );
2569 const SFVEC3F nt1 = glm::normalize( SFVEC3F( normalMatrix * n1 ) );
2570 const SFVEC3F nt2 = glm::normalize( SFVEC3F( normalMatrix * n2 ) );
2571
2572 TRIANGLE* newTriangle = new TRIANGLE( vt0, vt2, vt1, nt0, nt2, nt1 );
2573
2574 newTriangle->SetBoardItem( aBoardItem );
2575
2576 aDstContainer.Add( newTriangle );
2577
2578 if( !aSkipMaterialInformation )
2579 {
2580 newTriangle->SetMaterial( blinn_material );
2581 newTriangle->SetModelTransparency( fpTransparency );
2582
2583 if( mesh.m_Color == nullptr )
2584 {
2585 const SFVEC3F diffuseColor =
2586 a3DModel->m_Materials[mesh.m_MaterialIdx].m_Diffuse;
2587
2588 if( m_boardAdapter.m_Cfg->m_Render.material_mode == MATERIAL_MODE::CAD_MODE )
2589 newTriangle->SetColor( ConvertSRGBToLinear(
2590 MaterialDiffuseToColorCAD( diffuseColor ) ) );
2591 else
2592 newTriangle->SetColor( ConvertSRGBToLinear( diffuseColor ) );
2593 }
2594 else
2595 {
2596 if( m_boardAdapter.m_Cfg->m_Render.material_mode == MATERIAL_MODE::CAD_MODE )
2597 {
2598 newTriangle->SetColor(
2600 mesh.m_Color[idx0] ) ),
2602 mesh.m_Color[idx1] ) ),
2604 mesh.m_Color[idx2] ) ) );
2605 }
2606 else
2607 {
2608 newTriangle->SetColor(
2609 ConvertSRGBToLinear( mesh.m_Color[idx0] ),
2610 ConvertSRGBToLinear( mesh.m_Color[idx1] ),
2611 ConvertSRGBToLinear( mesh.m_Color[idx2] ) );
2612 }
2613 }
2614 }
2615 }
2616 }
2617 }
2618 }
2619 }
2620}
@ NORMAL
Use all material properties from model file.
Definition 3d_enums.h:68
@ CAD_MODE
Use a gray shading based on diffuse material.
Definition 3d_enums.h:70
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:136
SFVEC3F MaterialDiffuseToColorCAD(const SFVEC3F &aDiffuseColor)
Definition 3d_math.h:143
SFVEC3F SphericalToCartesian(float aInclination, float aAzimuth)
https://en.wikipedia.org/wiki/Spherical_coordinate_system
Definition 3d_math.h:39
void ApplyExtrusionTransform(SHAPE_POLY_SET &aOutline, const EXTRUDED_3D_BODY *aBody, const VECTOR2I &aFpPos)
Apply 2D extrusion transforms (rotation, scale, offset) to an outline.
EXTRUSION_MATERIAL_PROPS GetMaterialProps(EXTRUSION_MATERIAL aMaterial, const SFVEC3F &aDiffuse)
bool GetExtrusionPinOutline(const FOOTPRINT *aFootprint, SHAPE_POLY_SET &aPinPoly)
Get the pin outline polygons for extruded THT pin rendering.
bool GetExtrusionOutline(const FOOTPRINT *aFootprint, SHAPE_POLY_SET &aOutline, PCB_LAYER_ID aLayerOverride)
Get the extrusion outline polygon for a footprint in board coordinates.
BOX2I CalcPlaceholderLocalBox(const FOOTPRINT *aFootprint)
Calculate a local space bounding box for a placeholder 3D model.
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
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.
BOX2< VECTOR2I > BOX2I
Definition box2.h:927
This BVH implementation is based on the source code implementation from the book "Physically Based Re...
bool IsTriangleInRange(const unsigned int *aFaceIdx, unsigned int aTriangleIdx, unsigned int aVertexCount)
Test whether the three face indices of a triangle all reference valid vertices.
Definition c3dmodel.h:100
Blinn Phong based material https://en.wikipedia.org/wiki/Blinn%E2%80%93Phong_shading_model.
Definition material.h:375
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:84
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:409
constexpr size_type GetWidth() const
Definition box2.h:211
constexpr const Vec GetCenter() const
Definition box2.h:227
constexpr size_type GetHeight() const
Definition box2.h:212
Procedural generation of the shiny brushed metal.
Definition material.h:191
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:102
void Add(OBJECT_2D *aObject)
const LIST_OBJECT2D & GetList() const
void Add(OBJECT_3D *aObject)
Procedural generation of the copper normals.
Definition material.h:72
A vertical cylinder.
Definition cylinder_3d.h:34
void SetColor(SFVEC3F aObjColor)
Definition cylinder_3d.h:44
A light source based only on a directional vector.
Definition light.h:112
double AsRadians() const
Definition eda_angle.h:120
KIGFX::COLOR4D m_color
Definition footprint.h:120
VECTOR3D m_offset
Definition footprint.h:126
static KIGFX::COLOR4D GetDefaultColor(EXTRUSION_MATERIAL aMaterial)
Definition footprint.h:128
VECTOR3D m_scale
Definition footprint.h:124
EXTRUSION_MATERIAL m_material
Definition footprint.h:121
const EXTRUDED_3D_BODY * GetExtrudedBody() const
Definition footprint.h:428
bool IsFlipped() const
Definition footprint.h:660
VECTOR2I GetPosition() const override
Definition footprint.h:435
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
double r
Red component.
Definition color4d.h:390
double g
Green component.
Definition color4d.h:391
double a
Alpha component.
Definition color4d.h:393
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition color4d.h:399
double b
Blue component.
Definition color4d.h:392
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)
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:37
Base material class that can be used to derive other material implementations.
Definition material.h:236
static void SetDefaultReflectionRayCount(unsigned int aCount)
Definition material.h:243
static void SetDefaultRefractionRayCount(unsigned int aCount)
Definition material.h:238
static void SetDefaultRefractionRecursionCount(unsigned int aCount)
Definition material.h:248
void SetGenerator(const MATERIAL_GENERATOR *aGenerator)
Definition material.h:324
float GetTransparency() const
Definition material.h:269
static void SetDefaultReflectionRecursionCount(unsigned int aCount)
Definition material.h:253
static OBJECT_2D_STATS & Instance()
Definition object_2d.h:133
void ResetStats()
Definition object_2d.h:118
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:99
const BOARD_ITEM & GetBoardItem() const
Definition object_2d.h:62
OBJECT_2D_TYPE GetObjectType() const
Definition object_2d.h:103
static OBJECT_3D_STATS & Instance()
Definition object_3d.h:128
void ResetStats()
Definition object_3d.h:111
void SetMaterial(const MATERIAL *aMaterial)
Definition object_3d.h:54
void SetModelTransparency(float aModelTransparency)
Definition object_3d.h:62
void SetBoardItem(BOARD_ITEM *aBoardItem)
Definition object_3d.h:51
POST_MACHINING_PROPS & FrontPostMachining()
Definition padstack.h:370
POST_MACHINING_PROPS & BackPostMachining()
Definition padstack.h:373
Definition pad.h:61
int GetBackPostMachiningSize() const
Definition pad.h:473
VECTOR2I GetPosition() const override
Definition pad.cpp:246
VECTOR2I GetDrillSize() const
Definition pad.h:318
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.cpp:1747
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:93
int GetFrontPostMachiningSize() const
Definition pcb_track.h:716
const PADSTACK & Padstack() const
Definition pcb_track.h:418
std::optional< PAD_DRILL_POST_MACHINING_MODE > GetFrontPostMachining() const
Definition pcb_track.h:703
int GetBackPostMachiningSize() const
Definition pcb_track.h:736
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:723
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:142
Procedural generation of the shiny plastic normals.
Definition material.h:166
Point light source based on http://ogldev.atspace.co.uk/www/tutorial20/tutorial20....
Definition light.h:67
Represent a sub polygon block.
Definition polygon_2d.h:90
static FOOTPRINT_LIBRARY_ADAPTER * FootprintLibAdapter(PROJECT *aProject)
std::shared_ptr< REPORTER > m_warningReporter
std::shared_ptr< REPORTER > m_activityReporter
BOARD_ADAPTER & m_boardAdapter
Settings reference in use for this render.
SILK_SCREEN_NORMAL m_silkScreenMaterial
struct RENDER_3D_RAYTRACE_BASE::@013206213056006125230376122042346155134272177300 m_materials
std::mutex m_hitTestMutex
Serializes hover BVH use.
void addCountersinkPlating(const SFVEC2F &aCenter, float aTopInnerRadius, float aBottomInnerRadius, float aSurfaceZ, float aDepth, bool aIsFront)
std::vector< std::unique_ptr< BLINN_PHONG_MATERIAL > > m_extrusionMaterials
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
bool addExtrudedBodyToRaytracer(CONTAINER_3D &aDstContainer, const FOOTPRINT *aFootprint)
void addCounterborePlating(const BOARD_ITEM &aSource, const SFVEC2F &aCenter, float aInnerRadius, float aDepth, float aSurfaceZ, bool aIsFront)
void load3DModels(CONTAINER_3D &aDstContainer, bool aSkipMaterialInformation, std::stop_token aStop={})
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.
std::unique_ptr< ACCELERATOR_3D > m_accelerator
void addPlaceholderToRaytracer(CONTAINER_3D &aDstContainer, const FOOTPRINT *aFootprint, const glm::mat4 &aFpMatrix, bool aHasExtrudedBody=false, float aOpacity=1.0f)
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.
void Reload(bool aOnlyLoadCopperAndShapes, std::stop_token aStop={})
void SetColor(SFVEC3F aObjColor)
Cache for storing the 3D shapes.
Definition 3d_cache.h:53
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:603
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
Represent a set of closed polygons.
void Simplify()
Simplify the polyset (merges overlapping polys, eliminates degeneracy/self-intersections)
int OutlineCount() const
Return the number of outlines in the set.
void Fracture(bool aSimplify=true)
Convert a set of polygons with holes to a single outline with "slits"/"fractures" connecting the oute...
void BooleanSubtract(const SHAPE_POLY_SET &b)
Perform boolean polyset difference.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
Procedural generation of the solder mask.
Definition material.h:122
A triangle object.
Definition triangle_3d.h:39
void SetColor(const SFVEC3F &aColor)
A vertical truncated cone with different radii at top and bottom.
Definition frustum_3d.h:35
void SetColor(SFVEC3F aObjColor)
Definition frustum_3d.h:47
A plane that is parallel to XY plane.
Definition plane_3d.h:34
void SetColor(SFVEC3F aObjColor)
Definition plane_3d.h:45
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:347
@ LAYER_3D_USER_1
Definition layer_ids.h:593
@ LAYER_3D_SOLDERMASK_TOP
Definition layer_ids.h:582
@ LAYER_3D_SOLDERMASK_BOTTOM
Definition layer_ids.h:581
@ LAYER_3D_BOARD
Definition layer_ids.h:576
@ LAYER_3D_USER_45
Definition layer_ids.h:637
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:703
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ F_CrtYd
Definition layer_ids.h:112
@ B_Adhes
Definition layer_ids.h:99
@ Dwgs_User
Definition layer_ids.h:103
@ F_Paste
Definition layer_ids.h:100
@ Cmts_User
Definition layer_ids.h:104
@ F_Adhes
Definition layer_ids.h:98
@ B_Mask
Definition layer_ids.h:94
@ B_Cu
Definition layer_ids.h:61
@ Eco1_User
Definition layer_ids.h:105
@ F_Mask
Definition layer_ids.h:93
@ B_Paste
Definition layer_ids.h:101
@ F_Fab
Definition layer_ids.h:115
@ F_SilkS
Definition layer_ids.h:96
@ B_CrtYd
Definition layer_ids.h:111
@ Eco2_User
Definition layer_ids.h:106
@ B_SilkS
Definition layer_ids.h:97
@ F_Cu
Definition layer_ids.h:60
@ B_Fab
Definition layer_ids.h:114
#define CSGITEM_EMPTY
#define CSGITEM_FULL
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:411
PAD_DRILL_POST_MACHINING_MODE
Definition padstack.h:75
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:102
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...
std::vector< SEGMENT_WITH_NORMALS > SEGMENTS_WIDTH_NORMALS
List used to test ray2d intersections.
Definition polygon_2d.h:65
std::vector< POLYSEGMENT > SEGMENTS
Definition polygon_2d.h:57
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 size_t addOutlineToRaytracerObjects(CONTAINER_2D &aObjContainer, const SHAPE_POLY_SET &aOutline, float aBiuTo3d, const BOARD_ITEM &aBoardItem)
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:171
const SFVEC2F & Max() const
Definition bbox_2d.h:176
Manage a bounding box defined by two SFVEC3F min max points.
Definition bbox_3d.h:39
SFVEC3F GetCenter() const
Return the center point of the bounding box.
Definition bbox_3d.cpp:128
const SFVEC3F & Min() const
Return the minimum vertex pointer.
Definition bbox_3d.h:188
const SFVEC3F & Max() const
Return the maximum vertex pointer.
Definition bbox_3d.h:195
bool IsInitialized() const
Check if this bounding box is already initialized.
Definition bbox_3d.cpp:84
void Scale(float aScale)
Scales a bounding box by its center.
Definition bbox_3d.cpp:178
Handle a subset of a polygon.
Definition polygon_2d.h:75
std::vector< SEGMENTS > m_Outers
Definition polygon_2d.h:76
std::optional< PAD_DRILL_POST_MACHINING_MODE > mode
Definition padstack.h:287
Store the a model based on meshes and materials.
Definition c3dmodel.h:111
SMATERIAL * m_Materials
The materials list of this model.
Definition c3dmodel.h:116
unsigned int m_MeshesSize
Number of meshes in the array.
Definition c3dmodel.h:112
SMESH * m_Meshes
The meshes list of this model.
Definition c3dmodel.h:113
unsigned int m_MaterialsSize
Number of materials in the material array.
Definition c3dmodel.h:115
float m_Shininess
Definition c3dmodel.h:39
SFVEC3F m_Specular
Definition c3dmodel.h:38
SFVEC3F m_Ambient
Definition c3dmodel.h:35
float m_Transparency
1.0 is completely transparent, 0.0 completely opaque
Definition c3dmodel.h:40
SFVEC3F m_Emissive
Definition c3dmodel.h:37
SFVEC3F m_Diffuse
Default diffuse color if m_Color is NULL.
Definition c3dmodel.h:36
Per-vertex normal/color/texcoors structure.
Definition c3dmodel.h:77
unsigned int * m_FaceIdx
Triangle Face Indexes.
Definition c3dmodel.h:84
SFVEC3F * m_Normals
Vertex normals array.
Definition c3dmodel.h:80
unsigned int m_MaterialIdx
Material Index to be used in this mesh (must be < m_MaterialsSize )
Definition c3dmodel.h:85
unsigned int m_VertexSize
Number of vertex in the arrays.
Definition c3dmodel.h:78
unsigned int m_FaceIdxSize
Number of elements of the m_FaceIdx array.
Definition c3dmodel.h:83
SFVEC3F * m_Color
Vertex color array, can be NULL.
Definition c3dmodel.h:82
SFVEC3F * m_Positions
Vertex position array.
Definition c3dmodel.h:79
KIBIS_MODEL * model
VECTOR3I v1(5, 5, 5)
VECTOR2I center
const SHAPE_LINE_CHAIN chain
int radius
VECTOR2I end
VECTOR2I v2(1, 0)
VECTOR2I v4(1, 1)
VECTOR2I v5(-70, -70)
VECTOR2I v3(-2, 1)
void ConvertPolygonToTriangles(const SHAPE_POLY_SET &aPolyList, CONTAINER_2D_BASE &aDstContainer, float aBiuTo3dUnitsScale, const BOARD_ITEM &aBoardItem)
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:225
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:89
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
glm::vec2 SFVEC2F
Definition xv3d_types.h:38
glm::vec3 SFVEC3F
Definition xv3d_types.h:40