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