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