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