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