KiCad PCB EDA Suite
Loading...
Searching...
No Matches
opengl/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-2016 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
22#include "render_3d_opengl.h"
24#include <board.h>
25#include <footprint.h>
26#include <layer_range.h>
27#include <pcb_track.h>
28#include "../../3d_math.h"
30#include <lset.h>
31#include <trigo.h>
32#include <project.h>
33#include <core/profile.h> // To use GetRunningMicroSecs or another profiling utility
35#include <eda_3d_viewer_frame.h>
36#include <project_pcb.h>
38
39
41 TRIANGLE_DISPLAY_LIST* aDstLayer, float aZtop,
42 float aZbot )
43{
44 const SFVEC2F& center = aCircle->GetCenter();
45 const float radius = aCircle->GetRadius() * 2.0f; // Double because the render triangle
46
47 // This is a small adjustment to the circle texture
48 const float texture_factor = ( 8.0f / (float) SIZE_OF_CIRCLE_TEXTURE ) + 1.0f;
49 const float f = ( sqrtf( 2.0f ) / 2.0f ) * radius * texture_factor;
50
51 // Top and Bot segments ends are just triangle semi-circles, so need to add it in duplicated.
52 aDstLayer->m_layer_top_segment_ends->AddTriangle( SFVEC3F( center.x + f, center.y, aZtop ),
53 SFVEC3F( center.x - f, center.y, aZtop ),
54 SFVEC3F( center.x, center.y - f, aZtop ) );
55
56 aDstLayer->m_layer_top_segment_ends->AddTriangle( SFVEC3F( center.x - f, center.y, aZtop ),
57 SFVEC3F( center.x + f, center.y, aZtop ),
58 SFVEC3F( center.x, center.y + f, aZtop ) );
59
60 aDstLayer->m_layer_bot_segment_ends->AddTriangle( SFVEC3F( center.x - f, center.y, aZbot ),
61 SFVEC3F( center.x + f, center.y, aZbot ),
62 SFVEC3F( center.x, center.y - f, aZbot ) );
63
64 aDstLayer->m_layer_bot_segment_ends->AddTriangle( SFVEC3F( center.x + f, center.y, aZbot ),
65 SFVEC3F( center.x - f, center.y, aZbot ),
66 SFVEC3F( center.x, center.y + f, aZbot ) );
67}
68
69
71 TRIANGLE_DISPLAY_LIST* aDstLayer,
72 float aZtop, float aZbot )
73{
74 const SFVEC2F& v0 = aPoly->GetV0();
75 const SFVEC2F& v1 = aPoly->GetV1();
76 const SFVEC2F& v2 = aPoly->GetV2();
77 const SFVEC2F& v3 = aPoly->GetV3();
78
79 addTopAndBottomTriangles( aDstLayer, v0, v2, v1, aZtop, aZbot );
80 addTopAndBottomTriangles( aDstLayer, v2, v0, v3, aZtop, aZbot );
81}
82
83
84void RENDER_3D_OPENGL::generateRing( const SFVEC2F& aCenter, float aInnerRadius,
85 float aOuterRadius, unsigned int aNr_sides_per_circle,
86 std::vector< SFVEC2F >& aInnerContourResult,
87 std::vector< SFVEC2F >& aOuterContourResult,
88 bool aInvertOrder )
89{
90 aInnerContourResult.clear();
91 aInnerContourResult.reserve( aNr_sides_per_circle + 2 );
92
93 aOuterContourResult.clear();
94 aOuterContourResult.reserve( aNr_sides_per_circle + 2 );
95
96 const int delta = 3600 / aNr_sides_per_circle;
97
98 for( int ii = 0; ii < 3600; ii += delta )
99 {
100 float angle = (float)( aInvertOrder ? ( 3600 - ii ) : ii )
101 * 2.0f * glm::pi<float>() / 3600.0f;
102 const SFVEC2F rotatedDir = SFVEC2F( cos( angle ), sin( angle ) );
103
104 aInnerContourResult.emplace_back( aCenter.x + rotatedDir.x * aInnerRadius,
105 aCenter.y + rotatedDir.y * aInnerRadius );
106
107 aOuterContourResult.emplace_back( aCenter.x + rotatedDir.x * aOuterRadius,
108 aCenter.y + rotatedDir.y * aOuterRadius );
109 }
110
111 aInnerContourResult.push_back( aInnerContourResult[0] );
112 aOuterContourResult.push_back( aOuterContourResult[0] );
113
114 wxASSERT( aInnerContourResult.size() == aOuterContourResult.size() );
115}
116
117
119 float aZtop, float aZbot )
120{
121 const SFVEC2F& center = aRing->GetCenter();
122 const float inner = aRing->GetInnerRadius();
123 const float outer = aRing->GetOuterRadius();
124
125 std::vector< SFVEC2F > innerContour;
126 std::vector< SFVEC2F > outerContour;
127
128 generateRing( center, inner, outer, m_boardAdapter.GetCircleSegmentCount( outer * 2.0f ),
129 innerContour, outerContour, false );
130
131 // This will add the top and bot quads that will form the approximated ring
132 for( unsigned int i = 0; i < ( innerContour.size() - 1 ); ++i )
133 {
134 const SFVEC2F& vi0 = innerContour[i + 0];
135 const SFVEC2F& vi1 = innerContour[i + 1];
136 const SFVEC2F& vo0 = outerContour[i + 0];
137 const SFVEC2F& vo1 = outerContour[i + 1];
138
139 aDstLayer->m_layer_top_triangles->AddQuad( SFVEC3F( vi1.x, vi1.y, aZtop ),
140 SFVEC3F( vi0.x, vi0.y, aZtop ),
141 SFVEC3F( vo0.x, vo0.y, aZtop ),
142 SFVEC3F( vo1.x, vo1.y, aZtop ) );
143
144 aDstLayer->m_layer_bot_triangles->AddQuad( SFVEC3F( vi1.x, vi1.y, aZbot ),
145 SFVEC3F( vo1.x, vo1.y, aZbot ),
146 SFVEC3F( vo0.x, vo0.y, aZbot ),
147 SFVEC3F( vi0.x, vi0.y, aZbot ) );
148 }
149}
150
151
153 float aZtop, float aZbot )
154{
155 const SFVEC2F& v1 = aTri->GetP1();
156 const SFVEC2F& v2 = aTri->GetP2();
157 const SFVEC2F& v3 = aTri->GetP3();
158
159 addTopAndBottomTriangles( aDstLayer, v1, v2, v3, aZtop, aZbot );
160}
161
162
164 TRIANGLE_DISPLAY_LIST* aDstLayer,
165 float aZtop, float aZbot )
166{
167 const SFVEC2F& leftStart = aSeg->GetLeftStar();
168 const SFVEC2F& leftEnd = aSeg->GetLeftEnd();
169 const SFVEC2F& leftDir = aSeg->GetLeftDir();
170
171 const SFVEC2F& rightStart = aSeg->GetRightStar();
172 const SFVEC2F& rightEnd = aSeg->GetRightEnd();
173 const SFVEC2F& rightDir = aSeg->GetRightDir();
174 const float radius = aSeg->GetRadius();
175
176 const SFVEC2F& start = aSeg->GetStart();
177 const SFVEC2F& end = aSeg->GetEnd();
178
179 const float texture_factor = ( 12.0f / (float) SIZE_OF_CIRCLE_TEXTURE ) + 1.0f;
180 const float texture_factorF = ( 6.0f / (float) SIZE_OF_CIRCLE_TEXTURE ) + 1.0f;
181
182 const float radius_of_the_square = sqrtf( aSeg->GetRadiusSquared() * 2.0f );
183 const float radius_triangle_factor = ( radius_of_the_square - radius ) / radius;
184
185 const SFVEC2F factorS = SFVEC2F( -rightDir.y * radius * radius_triangle_factor,
186 rightDir.x * radius * radius_triangle_factor );
187
188 const SFVEC2F factorE = SFVEC2F( -leftDir.y * radius * radius_triangle_factor,
189 leftDir.x * radius * radius_triangle_factor );
190
191 // Top end segment triangles (semi-circles)
193 SFVEC3F( rightEnd.x + texture_factor * factorS.x,
194 rightEnd.y + texture_factor * factorS.y,
195 aZtop ),
196 SFVEC3F( leftStart.x + texture_factor * factorE.x,
197 leftStart.y + texture_factor * factorE.y,
198 aZtop ),
199 SFVEC3F( start.x - texture_factorF * leftDir.x * radius * sqrtf( 2.0f ),
200 start.y - texture_factorF * leftDir.y * radius * sqrtf( 2.0f ),
201 aZtop ) );
202
204 SFVEC3F( leftEnd.x + texture_factor * factorE.x,
205 leftEnd.y + texture_factor * factorE.y, aZtop ),
206 SFVEC3F( rightStart.x + texture_factor * factorS.x,
207 rightStart.y + texture_factor * factorS.y, aZtop ),
208 SFVEC3F( end.x - texture_factorF * rightDir.x * radius * sqrtf( 2.0f ),
209 end.y - texture_factorF * rightDir.y * radius * sqrtf( 2.0f ),
210 aZtop ) );
211
212 // Bot end segment triangles (semi-circles)
214 SFVEC3F( leftStart.x + texture_factor * factorE.x,
215 leftStart.y + texture_factor * factorE.y,
216 aZbot ),
217 SFVEC3F( rightEnd.x + texture_factor * factorS.x,
218 rightEnd.y + texture_factor * factorS.y,
219 aZbot ),
220 SFVEC3F( start.x - texture_factorF * leftDir.x * radius * sqrtf( 2.0f ),
221 start.y - texture_factorF * leftDir.y * radius * sqrtf( 2.0f ),
222 aZbot ) );
223
225 SFVEC3F( rightStart.x + texture_factor * factorS.x,
226 rightStart.y + texture_factor * factorS.y, aZbot ),
227 SFVEC3F( leftEnd.x + texture_factor * factorE.x,
228 leftEnd.y + texture_factor * factorE.y, aZbot ),
229 SFVEC3F( end.x - texture_factorF * rightDir.x * radius * sqrtf( 2.0f ),
230 end.y - texture_factorF * rightDir.y * radius * sqrtf( 2.0f ),
231 aZbot ) );
232
233 // Segment top and bot planes
234 aDstLayer->m_layer_top_triangles->AddQuad(
235 SFVEC3F( rightEnd.x, rightEnd.y, aZtop ),
236 SFVEC3F( rightStart.x, rightStart.y, aZtop ),
237 SFVEC3F( leftEnd.x, leftEnd.y, aZtop ),
238 SFVEC3F( leftStart.x, leftStart.y, aZtop ) );
239
240 aDstLayer->m_layer_bot_triangles->AddQuad(
241 SFVEC3F( rightEnd.x, rightEnd.y, aZbot ),
242 SFVEC3F( leftStart.x, leftStart.y, aZbot ),
243 SFVEC3F( leftEnd.x, leftEnd.y, aZbot ),
244 SFVEC3F( rightStart.x, rightStart.y, aZbot ) );
245}
246
247
249 const SHAPE_POLY_SET& aPoly, float aZtop, float aZbot,
250 bool aInvertFaces, const BVH_CONTAINER_2D* aThroughHoles )
251{
252 if( aListHolesObject2d.size() == 0 )
253 return nullptr;
254
255 OPENGL_RENDER_LIST* ret = nullptr;
256 TRIANGLE_DISPLAY_LIST* layerTriangles = new TRIANGLE_DISPLAY_LIST( aListHolesObject2d.size() * 2 );
257
258 // Convert the list of objects(filled circles) to triangle layer structure
259 for( const OBJECT_2D* object2d : aListHolesObject2d )
260 {
261 switch( object2d->GetObjectType() )
262 {
264 addObjectTriangles( static_cast<const FILLED_CIRCLE_2D*>( object2d ), layerTriangles, aZtop, aZbot );
265 break;
266
268 addObjectTriangles( static_cast<const ROUND_SEGMENT_2D*>( object2d ), layerTriangles, aZtop, aZbot );
269 break;
270
271 default:
272 wxFAIL_MSG( wxT( "RENDER_3D_OPENGL::generateHoles: Object type not implemented" ) );
273 break;
274 }
275 }
276
277 // Note: he can have a aListHolesObject2d with holes but without contours
278 // eg: when there are only NPTH on the list and the contours were not added
279 if( aPoly.OutlineCount() > 0 )
280 {
281 layerTriangles->AddToMiddleContours( aPoly, aZbot, aZtop, m_boardAdapter.BiuTo3dUnits(), aInvertFaces,
282 aThroughHoles );
283 }
284
285 ret = new OPENGL_RENDER_LIST( *layerTriangles, m_circleTexture, aZbot, aZtop );
286
287 delete layerTriangles;
288
289 return ret;
290}
291
292
294 const SHAPE_POLY_SET* aPolyList, PCB_LAYER_ID aLayer,
295 const BVH_CONTAINER_2D* aThroughHoles )
296{
297 if( aContainer == nullptr )
298 return nullptr;
299
300 const LIST_OBJECT2D& listObject2d = aContainer->GetList();
301
302 if( listObject2d.size() == 0 )
303 return nullptr;
304
305 float zBot = 0.0f;
306 float zTop = 0.0f;
307
308 getLayerZPos( aLayer, zTop, zBot );
309
310 // Calculate an estimation for the nr of triangles based on the nr of objects
311 unsigned int nrTrianglesEstimation = listObject2d.size() * 8;
312
313 TRIANGLE_DISPLAY_LIST* layerTriangles = new TRIANGLE_DISPLAY_LIST( nrTrianglesEstimation );
314
315 // store in a list so it will be latter deleted
316 m_triangles.push_back( layerTriangles );
317
318 // Load the 2D (X,Y axis) component of shapes
319 for( const OBJECT_2D* object2d : listObject2d )
320 {
321 switch( object2d->GetObjectType() )
322 {
324 addObjectTriangles( static_cast<const FILLED_CIRCLE_2D*>( object2d ), layerTriangles, zTop, zBot );
325 break;
326
328 addObjectTriangles( static_cast<const POLYGON_4PT_2D*>( object2d ), layerTriangles, zTop, zBot );
329 break;
330
332 addObjectTriangles( static_cast<const RING_2D*>( object2d ), layerTriangles, zTop, zBot );
333 break;
334
336 addObjectTriangles( static_cast<const TRIANGLE_2D*>( object2d ), layerTriangles, zTop, zBot );
337 break;
338
340 addObjectTriangles( static_cast<const ROUND_SEGMENT_2D*>( object2d ), layerTriangles, zTop, zBot );
341 break;
342
343 default:
344 wxFAIL_MSG( wxT( "RENDER_3D_OPENGL: Object type is not implemented" ) );
345 break;
346 }
347 }
348
349 if( aPolyList && aPolyList->OutlineCount() > 0 )
350 {
351 layerTriangles->AddToMiddleContours( *aPolyList, zBot, zTop, m_boardAdapter.BiuTo3dUnits(), false,
352 aThroughHoles );
353 }
354
355 // Create display list
356 return new OPENGL_RENDER_LIST( *layerTriangles, m_circleTexture, zBot, zTop );
357}
358
359
361{
362 float layer_z_bot = 0.0f;
363 float layer_z_top = 0.0f;
364
365 getLayerZPos( aLayer, layer_z_top, layer_z_bot );
366
367 TRIANGLE_DISPLAY_LIST* layerTriangles = new TRIANGLE_DISPLAY_LIST( 1 );
368
369 // store in a list so it will be latter deleted
370 m_triangles.push_back( layerTriangles );
371
372 return new OPENGL_RENDER_LIST( *layerTriangles, m_circleTexture, layer_z_bot, layer_z_top );
373}
374
375
377 const BVH_CONTAINER_2D* aThroughHoles )
378{
379 OPENGL_RENDER_LIST* dispLists = nullptr;
380 CONTAINER_2D boardContainer;
381
382 ConvertPolygonToTriangles( aBoardPoly, boardContainer, m_boardAdapter.BiuTo3dUnits(),
383 (const BOARD_ITEM &)*m_boardAdapter.GetBoard() );
384
385 const LIST_OBJECT2D& listBoardObject2d = boardContainer.GetList();
386
387 if( listBoardObject2d.size() > 0 )
388 {
389 // We will set a unitary Z so it will in future used with transformations since the
390 // board poly will be used not only to draw itself but also the solder mask layers.
391 const float layer_z_top = 1.0f;
392 const float layer_z_bot = 0.0f;
393
394 TRIANGLE_DISPLAY_LIST* layerTriangles =
395 new TRIANGLE_DISPLAY_LIST( listBoardObject2d.size() );
396
397 // Convert the list of objects(triangles) to triangle layer structure
398 for( const OBJECT_2D* itemOnLayer : listBoardObject2d )
399 {
400 const OBJECT_2D* object2d_A = itemOnLayer;
401
402 wxASSERT( object2d_A->GetObjectType() == OBJECT_2D_TYPE::TRIANGLE );
403
404 const TRIANGLE_2D* tri = static_cast<const TRIANGLE_2D*>( object2d_A );
405
406 const SFVEC2F& v1 = tri->GetP1();
407 const SFVEC2F& v2 = tri->GetP2();
408 const SFVEC2F& v3 = tri->GetP3();
409
410 addTopAndBottomTriangles( layerTriangles, v1, v2, v3, layer_z_top, layer_z_bot );
411 }
412
413 if( aBoardPoly.OutlineCount() > 0 )
414 {
415 layerTriangles->AddToMiddleContours( aBoardPoly, layer_z_bot, layer_z_top,
416 m_boardAdapter.BiuTo3dUnits(), false,
417 aThroughHoles );
418
419 dispLists = new OPENGL_RENDER_LIST( *layerTriangles, m_circleTexture,
420 layer_z_top, layer_z_top );
421 }
422
423 delete layerTriangles;
424 }
425
426 return dispLists;
427}
428
429
431{
432 if( !m_boardAdapter.GetBoard() )
433 return;
434
435 const int copperLayerCount = m_boardAdapter.GetBoard()->GetCopperLayerCount();
436 const float unitScale = m_boardAdapter.BiuTo3dUnits();
437 const int platingThickness = m_boardAdapter.GetHolePlatingThickness();
438 const float boardBodyThickness = m_boardAdapter.GetBoardBodyThickness();
439
440 // We use the same unit z range as the board (0 to 1) and apply scaling when rendering
441 const float boardZTop = 1.0f; // Top of board body
442 const float boardZBot = 0.0f; // Bottom of board body
443
444 // Helper to convert layer Z position to normalized 0-1 range
445 auto normalizeZ = [&]( float absZ ) -> float
446 {
447 float boardTop = m_boardAdapter.GetLayerBottomZPos( F_Cu );
448 float boardBot = m_boardAdapter.GetLayerBottomZPos( B_Cu );
449 float boardThick = boardTop - boardBot;
450
451 if( boardThick <= 0 )
452 return 0.5f;
453
454 // Map absolute Z to 0-1 range where 0 = B_Cu and 1 = F_Cu
455 return ( absZ - boardBot ) / boardThick;
456 };
457
458 // We'll accumulate all plug geometry into a single triangle list
459 TRIANGLE_DISPLAY_LIST* plugTriangles = new TRIANGLE_DISPLAY_LIST( 1024 );
460
461 // Process vias for backdrill and post-machining plugs
462 for( const PCB_TRACK* track : m_boardAdapter.GetBoard()->Tracks() )
463 {
464 if( track->Type() != PCB_VIA_T )
465 continue;
466
467 const PCB_VIA* via = static_cast<const PCB_VIA*>( track );
468
469 const float holeDiameter = via->GetDrillValue() * unitScale;
470 const float holeInnerRadius = holeDiameter / 2.0f;
471 const float holeOuterRadius = holeInnerRadius + platingThickness * unitScale;
472 const SFVEC2F center( via->GetStart().x * unitScale, -via->GetStart().y * unitScale );
473 const int nrSegments = m_boardAdapter.GetCircleSegmentCount( via->GetDrillValue() );
474
475 PCB_LAYER_ID topLayer, bottomLayer;
476 via->LayerPair( &topLayer, &bottomLayer );
477
478 float viaZTop, viaZBot, dummy;
479 getLayerZPos( topLayer, viaZTop, dummy );
480 getLayerZPos( bottomLayer, dummy, viaZBot );
481
482 // Handle backdrill plugs
483 const float secondaryDrillRadius = via->GetSecondaryDrillSize().value_or( 0 ) * 0.5f * unitScale;
484 const float tertiaryDrillRadius = via->GetTertiaryDrillSize().value_or( 0 ) * 0.5f * unitScale;
485
486 if( secondaryDrillRadius > holeOuterRadius || tertiaryDrillRadius > holeOuterRadius )
487 {
488 PCB_LAYER_ID plug_start_layer = F_Cu;
489 PCB_LAYER_ID plug_end_layer = B_Cu;
490
491 // Case 1: secondary drill exists, so we need to adjust the plug_end_layer
492 if( secondaryDrillRadius > holeOuterRadius )
493 {
494 plug_end_layer = via->GetSecondaryDrillEndLayer();
495 }
496 // Case 2: tertiary drill exists, so we need to adjust the plug_start_layer
497 if( tertiaryDrillRadius > holeOuterRadius )
498 {
499 plug_start_layer = via->GetTertiaryDrillStartLayer();
500 }
501
502 // Calculate where the backdrill ends and plug should start
503 float plugZTop, plugZBot, temp;
504 getLayerZPos( plug_end_layer, temp, plugZBot );
505 getLayerZPos( plug_start_layer, plugZTop, temp );
506
507 // Create a ring from holeOuterRadius to backdrillRadius
508 generateCylinder( center, holeOuterRadius, std::max( secondaryDrillRadius, tertiaryDrillRadius ),
509 plugZTop, plugZBot, nrSegments, plugTriangles );
510 }
511
512 // Handle front post-machining plugs
513 const auto frontMode = via->GetFrontPostMachining();
514
515 if( frontMode.has_value()
517 && frontMode.value() != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
518 {
519 const float frontRadius = via->GetFrontPostMachiningSize() * 0.5f * unitScale;
520 const float frontDepth = via->GetFrontPostMachiningDepth() * unitScale;
521
522 if( frontRadius > holeOuterRadius && frontDepth > 0 )
523 {
524 // Plug goes from bottom of post-machining to bottom of via
525 float pmBottomZ = normalizeZ( viaZTop - frontDepth );
526 float plugZBot = normalizeZ( viaZBot );
527
528 if( pmBottomZ > plugZBot )
529 {
530 if( frontMode.value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
531 {
532 EDA_ANGLE angle( via->GetFrontPostMachiningAngle(), TENTHS_OF_A_DEGREE_T );
533 generateInvCone( center, holeOuterRadius, frontRadius,
534 pmBottomZ, plugZBot, nrSegments, plugTriangles, angle );
535 }
536 else
537 {
538 generateCylinder( center, holeOuterRadius, frontRadius,
539 pmBottomZ, plugZBot, nrSegments, plugTriangles );
540 }
541 }
542 }
543 }
544
545 // Handle back post-machining plugs
546 const auto backMode = via->GetBackPostMachining();
547
548 if( backMode.has_value()
550 && backMode.value() != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
551 {
552 const float backRadius = via->GetBackPostMachiningSize() * 0.5f * unitScale;
553 const float backDepth = via->GetBackPostMachiningDepth() * unitScale;
554
555 if( backRadius > holeOuterRadius && backDepth > 0 )
556 {
557 // Plug goes from top of via to top of post-machining
558 float plugZTop = normalizeZ( viaZTop );
559 float pmTopZ = normalizeZ( viaZBot + backDepth );
560
561 if( plugZTop > pmTopZ )
562 {
563 if( backMode.value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
564 {
565 EDA_ANGLE angle( via->GetBackPostMachiningAngle(), TENTHS_OF_A_DEGREE_T );
566 generateInvCone( center, holeOuterRadius, backRadius,
567 plugZTop, pmTopZ, nrSegments, plugTriangles, angle );
568 }
569 else
570 {
571 generateCylinder( center, holeOuterRadius, backRadius,
572 plugZTop, pmTopZ, nrSegments, plugTriangles );
573 }
574 }
575 }
576 }
577 }
578
579 // Process pads for post-machining plugs
580 for( const FOOTPRINT* footprint : m_boardAdapter.GetBoard()->Footprints() )
581 {
582 for( const PAD* pad : footprint->Pads() )
583 {
584 if( !pad->HasHole() )
585 continue;
586
587 if( pad->GetDrillShape() != PAD_DRILL_SHAPE::CIRCLE )
588 continue;
589
590 const SFVEC2F padCenter( pad->GetPosition().x * unitScale,
591 -pad->GetPosition().y * unitScale );
592 const float holeInnerRadius = pad->GetDrillSize().x * 0.5f * unitScale;
593 const float holeOuterRadius = holeInnerRadius + platingThickness * unitScale;
594 const int nrSegments = m_boardAdapter.GetCircleSegmentCount( pad->GetDrillSize().x );
595
596 float padZTop, padZBot, padDummy;
597 getLayerZPos( F_Cu, padZTop, padDummy );
598 getLayerZPos( B_Cu, padDummy, padZBot );
599
600 // Handle front post-machining plugs for pads
601 const auto frontMode = pad->GetFrontPostMachining();
602
603 if( frontMode.has_value()
605 && frontMode.value() != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
606 {
607 const float frontRadius = pad->GetFrontPostMachiningSize() * 0.5f * unitScale;
608 const float frontDepth = pad->GetFrontPostMachiningDepth() * unitScale;
609
610 if( frontRadius > holeOuterRadius && frontDepth > 0 )
611 {
612 float pmBottomZ = normalizeZ( padZTop - frontDepth );
613 float plugZBot = normalizeZ( padZBot );
614
615 if( pmBottomZ > plugZBot )
616 {
617 if( frontMode.value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
618 {
619 EDA_ANGLE angle( pad->GetFrontPostMachiningAngle(), TENTHS_OF_A_DEGREE_T );
620 generateInvCone( padCenter, holeOuterRadius, frontRadius,
621 pmBottomZ, plugZBot, nrSegments, plugTriangles, angle );
622 }
623 else
624 {
625 generateCylinder( padCenter, holeOuterRadius, frontRadius,
626 pmBottomZ, plugZBot, nrSegments, plugTriangles );
627 }
628 }
629 }
630 }
631
632 // Handle back post-machining plugs for pads
633 const auto backMode = pad->GetBackPostMachining();
634
635 if( backMode.has_value()
637 && backMode.value() != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
638 {
639 const float backRadius = pad->GetBackPostMachiningSize() * 0.5f * unitScale;
640 const float backDepth = pad->GetBackPostMachiningDepth() * unitScale;
641
642 if( backRadius > holeOuterRadius && backDepth > 0 )
643 {
644 float plugZTop = normalizeZ( padZTop );
645 float pmTopZ = normalizeZ( padZBot + backDepth );
646
647 if( plugZTop > pmTopZ )
648 {
649 if( backMode.value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
650 {
651 EDA_ANGLE angle( pad->GetBackPostMachiningAngle(), TENTHS_OF_A_DEGREE_T );
652 generateInvCone( padCenter, holeOuterRadius, backRadius,
653 plugZTop, pmTopZ, nrSegments, plugTriangles, angle );
654 }
655 else
656 {
657 generateCylinder( padCenter, holeOuterRadius, backRadius,
658 plugZTop, pmTopZ, nrSegments, plugTriangles );
659 }
660 }
661 }
662 }
663 }
664 }
665
666 // If we have any plug geometry, create a render list for it
667 if( plugTriangles->m_layer_top_triangles->GetVertexSize() > 0
668 || plugTriangles->m_layer_bot_triangles->GetVertexSize() > 0
669 || plugTriangles->m_layer_middle_contours_quads->GetVertexSize() > 0 )
670 {
671 // Store the triangles for later cleanup
672 m_triangles.push_back( plugTriangles );
673
674 // Create a render list for the plugs using the same Z range as the board
675 // This will be scaled and drawn alongside m_boardWithHoles in renderBoardBody()
677 boardZTop, boardZTop );
678 }
679 else
680 {
681 delete plugTriangles;
682 }
683}
684
685
687{
688 if( !m_boardAdapter.GetBoard() )
689 return;
690
691 const float biuTo3d = m_boardAdapter.BiuTo3dUnits();
692
693 for( FOOTPRINT* fp : m_boardAdapter.GetBoard()->Footprints() )
694 {
695 if( !fp->HasExtrudedBody() )
696 continue;
697
698 const EXTRUDED_3D_BODY* body = fp->GetExtrudedBody();
699
700 if( !body->m_show || !m_boardAdapter.IsFootprintShown( fp ) )
701 continue;
702
703 SHAPE_POLY_SET outline;
704
705 if( !GetExtrusionOutline( fp, outline ) )
706 continue;
707
708 if( outline.OutlineCount() == 0 )
709 continue;
710
711 outline.Simplify();
712
713 VECTOR2I fpPos = fp->GetPosition();
714 ApplyExtrusionTransform( outline, body, fpPos );
715
716 bool isBack = fp->IsFlipped();
717 float boardSurfaceZ = m_boardAdapter.GetFootprintZPos( isBack );
718 float standoff3d = body->m_standoff * biuTo3d;
719 float bodyThickness = ( body->m_height - body->m_standoff ) * biuTo3d * body->m_scale.z;
720 float zOffset3d = pcbIUScale.mmToIU( body->m_offset.z ) * biuTo3d;
721
722 float zBot, zTop;
723
724 if( !isBack )
725 {
726 zBot = boardSurfaceZ + standoff3d + zOffset3d;
727 zTop = zBot + bodyThickness;
728 }
729 else
730 {
731 zTop = boardSurfaceZ - standoff3d - zOffset3d;
732 zBot = zTop - bodyThickness;
733 }
734
735 CONTAINER_2D triContainer;
736 ConvertPolygonToTriangles( outline, triContainer, biuTo3d, *fp );
737
738 const LIST_OBJECT2D& triList = triContainer.GetList();
739
740 if( triList.empty() )
741 continue;
742
743 TRIANGLE_DISPLAY_LIST* layerTri = new TRIANGLE_DISPLAY_LIST( triList.size() );
744
745 for( const OBJECT_2D* obj : triList )
746 {
747 const TRIANGLE_2D* tri = static_cast<const TRIANGLE_2D*>( obj );
748 addTopAndBottomTriangles( layerTri, tri->GetP1(), tri->GetP2(), tri->GetP3(), zTop, zBot );
749 }
750
751 layerTri->AddToMiddleContours( outline, zBot, zTop, biuTo3d, false );
752
753 OPENGL_RENDER_LIST* renderList = new OPENGL_RENDER_LIST( *layerTri, m_circleTexture, zTop, zBot );
754
755 m_triangles.push_back( layerTri );
756 m_extrudedBodyLists[fp] = renderList;
757
758 // Create metallic pin extrusions for THT pads
759 // Start from opposite board side to standoff height
760 if( standoff3d > 0.0f )
761 {
762 SHAPE_POLY_SET pinPoly;
763
764 if( GetExtrusionPinOutline( fp, pinPoly ) )
765 {
766 ApplyExtrusionTransform( pinPoly, body, fpPos );
767
768 float oppositeSurfaceZ = m_boardAdapter.GetFootprintZPos( !isBack );
769 float protrusion = 1.0f * pcbIUScale.IU_PER_MM * biuTo3d;
770 float pinZBot, pinZTop;
771
772 if( !isBack )
773 {
774 pinZBot = oppositeSurfaceZ - protrusion;
775 pinZTop = boardSurfaceZ + standoff3d;
776 }
777 else
778 {
779 pinZTop = oppositeSurfaceZ + protrusion;
780 pinZBot = boardSurfaceZ - standoff3d;
781 }
782
783 CONTAINER_2D pinTriContainer;
784 ConvertPolygonToTriangles( pinPoly, pinTriContainer, biuTo3d, *fp );
785
786 const LIST_OBJECT2D& pinTriList = pinTriContainer.GetList();
787
788 if( !pinTriList.empty() )
789 {
790 TRIANGLE_DISPLAY_LIST* pinLayerTri = new TRIANGLE_DISPLAY_LIST( pinTriList.size() );
791
792 for( const OBJECT_2D* obj : pinTriList )
793 {
794 const TRIANGLE_2D* tri = static_cast<const TRIANGLE_2D*>( obj );
795 addTopAndBottomTriangles( pinLayerTri, tri->GetP1(), tri->GetP2(), tri->GetP3(), pinZTop,
796 pinZBot );
797 }
798
799 pinLayerTri->AddToMiddleContours( pinPoly, pinZBot, pinZTop, biuTo3d, false );
800
801 OPENGL_RENDER_LIST* pinRenderList =
802 new OPENGL_RENDER_LIST( *pinLayerTri, m_circleTexture, pinZTop, pinZBot );
803
804 m_triangles.push_back( pinLayerTri );
805 m_extrudedPadLists[fp] = pinRenderList;
806 }
807 }
808 }
809 }
810}
811
812
813void RENDER_3D_OPENGL::reload( REPORTER* aStatusReporter, REPORTER* aWarningReporter )
814{
815 m_reloadRequested = false;
816
817 freeAllLists();
819
821
822 int64_t stats_startReloadTime = GetRunningMicroSecs();
823
824 m_boardAdapter.InitSettings( aStatusReporter, aWarningReporter );
825
826 SFVEC3F camera_pos = m_boardAdapter.GetBoardCenter();
827 m_camera.SetBoardLookAtPos( camera_pos );
828
829 if( aStatusReporter )
830 aStatusReporter->Report( _( "Load OpenGL: board" ) );
831
832 // Create Board
833 m_board = createBoard( m_boardAdapter.GetBoardPoly(), &m_boardAdapter.GetTH_IDs() );
834
835 m_antiBoardPolys.RemoveAllContours();
836 m_antiBoardPolys.NewOutline();
837 m_antiBoardPolys.Append( VECTOR2I( -INT_MAX/2, -INT_MAX/2 ) );
838 m_antiBoardPolys.Append( VECTOR2I( INT_MAX/2, -INT_MAX/2 ) );
839 m_antiBoardPolys.Append( VECTOR2I( INT_MAX/2, INT_MAX/2 ) );
840 m_antiBoardPolys.Append( VECTOR2I( -INT_MAX/2, INT_MAX/2 ) );
841 m_antiBoardPolys.Outline( 0 ).SetClosed( true );
842
843 m_antiBoardPolys.BooleanSubtract( m_boardAdapter.GetBoardPoly() );
845
846 SHAPE_POLY_SET board_poly_with_holes = m_boardAdapter.GetBoardPoly().CloneDropTriangulation();
847 board_poly_with_holes.BooleanSubtract( m_boardAdapter.GetTH_ODPolys() );
848 board_poly_with_holes.BooleanSubtract( m_boardAdapter.GetNPTH_ODPolys() );
849
850 // Also subtract counterbore, countersink, and backdrill polygons from the board
851 if( m_boardAdapter.GetFrontCounterborePolys().OutlineCount() > 0 )
852 board_poly_with_holes.BooleanSubtract( m_boardAdapter.GetFrontCounterborePolys() );
853
854 if( m_boardAdapter.GetBackCounterborePolys().OutlineCount() > 0 )
855 board_poly_with_holes.BooleanSubtract( m_boardAdapter.GetBackCounterborePolys() );
856
857 if( m_boardAdapter.GetFrontCountersinkPolys().OutlineCount() > 0 )
858 board_poly_with_holes.BooleanSubtract( m_boardAdapter.GetFrontCountersinkPolys() );
859
860 if( m_boardAdapter.GetBackCountersinkPolys().OutlineCount() > 0 )
861 board_poly_with_holes.BooleanSubtract( m_boardAdapter.GetBackCountersinkPolys() );
862
863 if( m_boardAdapter.GetBackdrillPolys().OutlineCount() > 0 )
864 board_poly_with_holes.BooleanSubtract( m_boardAdapter.GetBackdrillPolys() );
865
866 if( m_boardAdapter.GetTertiarydrillPolys().OutlineCount() > 0 )
867 board_poly_with_holes.BooleanSubtract( m_boardAdapter.GetTertiarydrillPolys() );
868
869
870 m_boardWithHoles = createBoard( board_poly_with_holes, &m_boardAdapter.GetTH_IDs() );
871
872 // Create plugs for backdrilled and post-machined areas
874
875 if( m_antiBoard )
876 m_antiBoard->SetItIsTransparent( true );
877
878 // Create Through Holes and vias
879 if( aStatusReporter )
880 aStatusReporter->Report( _( "Load OpenGL: holes and vias" ) );
881
882 SHAPE_POLY_SET outerPolyTHT = m_boardAdapter.GetTH_ODPolys().CloneDropTriangulation();
883
884 // Include NPTH polygons so their barrel walls are also generated
885 if( m_boardAdapter.GetNPTH_ODPolys().OutlineCount() > 0 )
886 outerPolyTHT.BooleanAdd( m_boardAdapter.GetNPTH_ODPolys() );
887
888 outerPolyTHT.BooleanIntersection( m_boardAdapter.GetBoardPoly() );
889
890 m_outerThroughHoles = generateHoles( m_boardAdapter.GetTH_ODs().GetList(), outerPolyTHT,
891 1.0f, 0.0f, false, &m_boardAdapter.GetTH_IDs() );
892
893 m_outerViaThroughHoles = generateHoles( m_boardAdapter.GetViaTH_ODs().GetList(),
894 m_boardAdapter.GetViaTH_ODPolys(), 1.0f, 0.0f, false );
895
896 if( m_boardAdapter.m_Cfg->m_Render.clip_silk_on_via_annuli )
897 {
898 m_outerThroughHoleRings = generateHoles( m_boardAdapter.GetViaAnnuli().GetList(),
899 m_boardAdapter.GetViaAnnuliPolys(),
900 1.0f, 0.0f, false );
901 }
902
903 const MAP_POLY& innerMapHoles = m_boardAdapter.GetHoleIdPolysMap();
904 const MAP_POLY& outerMapHoles = m_boardAdapter.GetHoleOdPolysMap();
905
906 wxASSERT( innerMapHoles.size() == outerMapHoles.size() );
907
908 const MAP_CONTAINER_2D_BASE& map_holes = m_boardAdapter.GetLayerHoleMap();
909
910 if( outerMapHoles.size() > 0 )
911 {
912 float layer_z_bot = 0.0f;
913 float layer_z_top = 0.0f;
914
915 for( const auto& [ layer, poly ] : outerMapHoles )
916 {
917 getLayerZPos( layer, layer_z_top, layer_z_bot );
918
919 m_outerLayerHoles[layer] = generateHoles( map_holes.at( layer )->GetList(), *poly,
920 layer_z_top, layer_z_bot, false );
921 }
922
923 for( const auto& [ layer, poly ] : innerMapHoles )
924 {
925 getLayerZPos( layer, layer_z_top, layer_z_bot );
926
927 m_innerLayerHoles[layer] = generateHoles( map_holes.at( layer )->GetList(), *poly,
928 layer_z_top, layer_z_bot, false );
929 }
930 }
931
932 // Generate vertical cylinders of vias and pads (copper)
934
935 // Add layers maps
936 if( aStatusReporter )
937 aStatusReporter->Report( _( "Load OpenGL: layers" ) );
938
939 std::bitset<LAYER_3D_END> visibilityFlags = m_boardAdapter.GetVisibleLayers();
940 const MAP_POLY& map_poly = m_boardAdapter.GetPolyMap();
941 wxString msg;
942
943 for( const auto& [ layer, container2d ] : m_boardAdapter.GetLayerMap() )
944 {
945 if( !m_boardAdapter.Is3dLayerEnabled( layer, visibilityFlags ) )
946 continue;
947
948 if( aStatusReporter )
949 {
950 msg = m_boardAdapter.GetBoard()->GetLayerName( layer );
951 aStatusReporter->Report( wxString::Format( _( "Load OpenGL layer %s" ), msg ) );
952 }
953
954 SHAPE_POLY_SET polyListSubtracted;
955 SHAPE_POLY_SET* polyList = nullptr;
956
957 // Load the vertical (Z axis) component of shapes
958
959 if( m_boardAdapter.m_Cfg->m_Render.opengl_copper_thickness )
960 {
961 if( map_poly.contains( layer ) )
962 {
963 polyListSubtracted = *map_poly.at( layer );
964
965 if( LSET::PhysicalLayersMask().test( layer ) )
966 {
967 polyListSubtracted.BooleanIntersection( m_boardAdapter.GetBoardPoly() );
968 }
969
970 if( layer != B_Mask && layer != F_Mask )
971 {
972 polyListSubtracted.BooleanSubtract( m_boardAdapter.GetTH_ODPolys() );
973 polyListSubtracted.BooleanSubtract( m_boardAdapter.GetNPTH_ODPolys() );
974
975 // Subtract counterbore/countersink cutouts from copper layers
976 if( layer == F_Cu )
977 {
978 polyListSubtracted.BooleanSubtract( m_boardAdapter.GetFrontCounterborePolys() );
979 polyListSubtracted.BooleanSubtract( m_boardAdapter.GetFrontCountersinkPolys() );
980 }
981 else if( layer == B_Cu )
982 {
983 polyListSubtracted.BooleanSubtract( m_boardAdapter.GetBackCounterborePolys() );
984 polyListSubtracted.BooleanSubtract( m_boardAdapter.GetBackCountersinkPolys() );
985 }
986 }
987
988 if( m_boardAdapter.m_Cfg->m_Render.subtract_mask_from_silk )
989 {
990 if( layer == B_SilkS && map_poly.contains( B_Mask ) )
991 {
992 polyListSubtracted.BooleanSubtract( *map_poly.at( B_Mask ) );
993 }
994 else if( layer == F_SilkS && map_poly.contains( F_Mask ) )
995 {
996 polyListSubtracted.BooleanSubtract( *map_poly.at( F_Mask ) );
997 }
998 }
999
1000 polyList = &polyListSubtracted;
1001 }
1002 }
1003
1004 OPENGL_RENDER_LIST* oglList = generateLayerList( container2d, polyList, layer,
1005 &m_boardAdapter.GetTH_IDs() );
1006
1007 if( oglList != nullptr )
1008 m_layers[layer] = oglList;
1009 }
1010
1011 if( m_boardAdapter.m_Cfg->m_Render.DifferentiatePlatedCopper() )
1012 {
1013 const SHAPE_POLY_SET* frontPlatedCopperPolys = m_boardAdapter.GetFrontPlatedCopperPolys();
1014 const SHAPE_POLY_SET* backPlatedCopperPolys = m_boardAdapter.GetBackPlatedCopperPolys();
1015
1016 if( frontPlatedCopperPolys )
1017 {
1018 SHAPE_POLY_SET poly = frontPlatedCopperPolys->CloneDropTriangulation();
1019 poly.BooleanIntersection( m_boardAdapter.GetBoardPoly() );
1020 poly.BooleanSubtract( m_boardAdapter.GetTH_ODPolys() );
1021 poly.BooleanSubtract( m_boardAdapter.GetNPTH_ODPolys() );
1022 poly.BooleanSubtract( m_boardAdapter.GetFrontCounterborePolys() );
1023 poly.BooleanSubtract( m_boardAdapter.GetFrontCountersinkPolys() );
1024 poly.BooleanSubtract( m_boardAdapter.GetTertiarydrillPolys() );
1025
1026 m_platedPadsFront = generateLayerList( m_boardAdapter.GetPlatedPadsFront(), &poly,
1027 F_Cu );
1028
1029 // An entry for F_Cu must exist in m_layers or we'll never look at m_platedPadsFront
1030 if( m_layers.count( F_Cu ) == 0 )
1032 }
1033
1034 if( backPlatedCopperPolys )
1035 {
1036 SHAPE_POLY_SET poly = backPlatedCopperPolys->CloneDropTriangulation();
1037 poly.BooleanIntersection( m_boardAdapter.GetBoardPoly() );
1038 poly.BooleanSubtract( m_boardAdapter.GetTH_ODPolys() );
1039 poly.BooleanSubtract( m_boardAdapter.GetNPTH_ODPolys() );
1040 poly.BooleanSubtract( m_boardAdapter.GetBackCounterborePolys() );
1041 poly.BooleanSubtract( m_boardAdapter.GetBackCountersinkPolys() );
1042 poly.BooleanSubtract( m_boardAdapter.GetBackdrillPolys() );
1043
1044 m_platedPadsBack = generateLayerList( m_boardAdapter.GetPlatedPadsBack(), &poly, B_Cu );
1045
1046 // An entry for B_Cu must exist in m_layers or we'll never look at m_platedPadsBack
1047 if( m_layers.count( B_Cu ) == 0 )
1049 }
1050 }
1051
1052 if( m_boardAdapter.m_Cfg->m_Render.show_off_board_silk )
1053 {
1054 if( const BVH_CONTAINER_2D* padsFront = m_boardAdapter.GetOffboardPadsFront() )
1055 m_offboardPadsFront = generateLayerList( padsFront, nullptr, F_Cu );
1056
1057 if( const BVH_CONTAINER_2D* padsBack = m_boardAdapter.GetOffboardPadsBack() )
1058 m_offboardPadsBack = generateLayerList( padsBack, nullptr, B_Cu );
1059 }
1060
1061 // Load 3D models
1062 if( aStatusReporter )
1063 aStatusReporter->Report( _( "Loading 3D models..." ) );
1064
1065 load3dModels( aStatusReporter );
1066
1068
1069 if( aStatusReporter )
1070 {
1071 // Calculation time in seconds
1072 double calculation_time = (double)( GetRunningMicroSecs() - stats_startReloadTime) / 1e6;
1073
1074 aStatusReporter->Report( wxString::Format( _( "Reload time %.3f s" ), calculation_time ) );
1075 }
1076}
1077
1078
1080 const SFVEC2F& v1, const SFVEC2F& v2, float top,
1081 float bot )
1082{
1083 aDst->m_layer_bot_triangles->AddTriangle( SFVEC3F( v0.x, v0.y, bot ),
1084 SFVEC3F( v1.x, v1.y, bot ),
1085 SFVEC3F( v2.x, v2.y, bot ) );
1086
1088 SFVEC3F( v1.x, v1.y, top ),
1089 SFVEC3F( v0.x, v0.y, top ) );
1090}
1091
1092
1093void RENDER_3D_OPENGL::getLayerZPos( PCB_LAYER_ID aLayer, float& aOutZtop, float& aOutZbot ) const
1094{
1095 aOutZbot = m_boardAdapter.GetLayerBottomZPos( aLayer );
1096 aOutZtop = m_boardAdapter.GetLayerTopZPos( aLayer );
1097
1098 if( aOutZtop < aOutZbot )
1099 {
1100 float tmpFloat = aOutZbot;
1101 aOutZbot = aOutZtop;
1102 aOutZtop = tmpFloat;
1103 }
1104}
1105
1106
1107void RENDER_3D_OPENGL::generateCylinder( const SFVEC2F& aCenter, float aInnerRadius,
1108 float aOuterRadius, float aZtop, float aZbot,
1109 unsigned int aNr_sides_per_circle,
1110 TRIANGLE_DISPLAY_LIST* aDstLayer )
1111{
1112 std::vector< SFVEC2F > innerContour;
1113 std::vector< SFVEC2F > outerContour;
1114
1115 generateRing( aCenter, aInnerRadius, aOuterRadius, aNr_sides_per_circle, innerContour,
1116 outerContour, false );
1117
1118 for( unsigned int i = 0; i < ( innerContour.size() - 1 ); ++i )
1119 {
1120 const SFVEC2F& vi0 = innerContour[i + 0];
1121 const SFVEC2F& vi1 = innerContour[i + 1];
1122 const SFVEC2F& vo0 = outerContour[i + 0];
1123 const SFVEC2F& vo1 = outerContour[i + 1];
1124
1125 aDstLayer->m_layer_top_triangles->AddQuad( SFVEC3F( vi1.x, vi1.y, aZtop ),
1126 SFVEC3F( vi0.x, vi0.y, aZtop ),
1127 SFVEC3F( vo0.x, vo0.y, aZtop ),
1128 SFVEC3F( vo1.x, vo1.y, aZtop ) );
1129
1130 aDstLayer->m_layer_bot_triangles->AddQuad( SFVEC3F( vi1.x, vi1.y, aZbot ),
1131 SFVEC3F( vo1.x, vo1.y, aZbot ),
1132 SFVEC3F( vo0.x, vo0.y, aZbot ),
1133 SFVEC3F( vi0.x, vi0.y, aZbot ) );
1134 }
1135
1136 aDstLayer->AddToMiddleContours( outerContour, aZbot, aZtop, true );
1137 aDstLayer->AddToMiddleContours( innerContour, aZbot, aZtop, false );
1138}
1139
1140
1141void RENDER_3D_OPENGL::generateInvCone( const SFVEC2F& aCenter, float aInnerRadius,
1142 float aOuterRadius, float aZtop, float aZbot,
1143 unsigned int aNr_sides_per_circle,
1144 TRIANGLE_DISPLAY_LIST* aDstLayer, EDA_ANGLE aAngle )
1145{
1146 // For a countersink cone:
1147 // - The outer contour goes from aZbot to aZtop (full height)
1148 // - The inner contour goes from aZbot to aZbot + innerHeight
1149 // - The top surface is conical, sloping from inner top to outer top
1150 // - aAngle is the half-angle of the cone (in decidegrees from the vertical)
1151
1152 // Calculate the inner contour height based on the cone angle
1153 // tan(angle) = (outerRadius - innerRadius) / innerHeight
1154 // innerHeight = (outerRadius - innerRadius) / tan(angle)
1155 float radialDiff = aOuterRadius - aInnerRadius;
1156 float angleRad = aAngle.AsRadians();
1157
1158 // Clamp angle to avoid division by zero or negative heights
1159 if( angleRad < 0.01f )
1160 angleRad = 0.01f;
1161
1162 float innerHeight = radialDiff / tanf( angleRad );
1163 float totalHeight = aZtop - aZbot;
1164
1165 // Clamp inner height to not exceed total height
1166 if( innerHeight > totalHeight )
1167 innerHeight = totalHeight;
1168
1169 float zInnerTop = aZbot + innerHeight;
1170
1171 std::vector< SFVEC2F > innerContour;
1172 std::vector< SFVEC2F > outerContour;
1173
1174 generateRing( aCenter, aInnerRadius, aOuterRadius, aNr_sides_per_circle, innerContour,
1175 outerContour, false );
1176
1177 for( unsigned int i = 0; i < ( innerContour.size() - 1 ); ++i )
1178 {
1179 const SFVEC2F& vi0 = innerContour[i + 0];
1180 const SFVEC2F& vi1 = innerContour[i + 1];
1181 const SFVEC2F& vo0 = outerContour[i + 0];
1182 const SFVEC2F& vo1 = outerContour[i + 1];
1183
1184 // Conical top surface: from inner contour at zInnerTop to outer contour at aZtop
1185 aDstLayer->m_layer_top_triangles->AddQuad( SFVEC3F( vi1.x, vi1.y, zInnerTop ),
1186 SFVEC3F( vi0.x, vi0.y, zInnerTop ),
1187 SFVEC3F( vo0.x, vo0.y, aZtop ),
1188 SFVEC3F( vo1.x, vo1.y, aZtop ) );
1189
1190 // Flat bottom surface
1191 aDstLayer->m_layer_bot_triangles->AddQuad( SFVEC3F( vi1.x, vi1.y, aZbot ),
1192 SFVEC3F( vo1.x, vo1.y, aZbot ),
1193 SFVEC3F( vo0.x, vo0.y, aZbot ),
1194 SFVEC3F( vi0.x, vi0.y, aZbot ) );
1195 }
1196
1197 // Outer contour wall goes full height
1198 aDstLayer->AddToMiddleContours( outerContour, aZbot, aZtop, true );
1199 // Inner contour wall only goes up to zInnerTop
1200 aDstLayer->AddToMiddleContours( innerContour, aZbot, zInnerTop, false );
1201}
1202
1203
1204void RENDER_3D_OPENGL::generateDisk( const SFVEC2F& aCenter, float aRadius, float aZ,
1205 unsigned int aNr_sides_per_circle, TRIANGLE_DISPLAY_LIST* aDstLayer,
1206 bool aTop )
1207{
1208 const float delta = 2.0f * glm::pi<float>() / (float) aNr_sides_per_circle;
1209
1210 for( unsigned int i = 0; i < aNr_sides_per_circle; ++i )
1211 {
1212 float a0 = delta * i;
1213 float a1 = delta * ( i + 1 );
1214 const SFVEC3F p0( aCenter.x + cosf( a0 ) * aRadius,
1215 aCenter.y + sinf( a0 ) * aRadius, aZ );
1216 const SFVEC3F p1( aCenter.x + cosf( a1 ) * aRadius,
1217 aCenter.y + sinf( a1 ) * aRadius, aZ );
1218 const SFVEC3F c( aCenter.x, aCenter.y, aZ );
1219
1220 if( aTop )
1221 aDstLayer->m_layer_top_triangles->AddTriangle( p1, p0, c );
1222 else
1223 aDstLayer->m_layer_bot_triangles->AddTriangle( p0, p1, c );
1224 }
1225}
1226
1227
1228void RENDER_3D_OPENGL::generateDimple( const SFVEC2F& aCenter, float aRadius, float aZ,
1229 float aDepth, unsigned int aNr_sides_per_circle,
1230 TRIANGLE_DISPLAY_LIST* aDstLayer, bool aTop )
1231{
1232 const float delta = 2.0f * glm::pi<float>() / (float) aNr_sides_per_circle;
1233 const SFVEC3F c( aCenter.x, aCenter.y, aTop ? aZ - aDepth : aZ + aDepth );
1234
1235 for( unsigned int i = 0; i < aNr_sides_per_circle; ++i )
1236 {
1237 float a0 = delta * i;
1238 float a1 = delta * ( i + 1 );
1239 const SFVEC3F p0( aCenter.x + cosf( a0 ) * aRadius,
1240 aCenter.y + sinf( a0 ) * aRadius, aZ );
1241 const SFVEC3F p1( aCenter.x + cosf( a1 ) * aRadius,
1242 aCenter.y + sinf( a1 ) * aRadius, aZ );
1243
1244 if( aTop )
1245 aDstLayer->m_layer_top_triangles->AddTriangle( p0, p1, c );
1246 else
1247 aDstLayer->m_layer_bot_triangles->AddTriangle( p1, p0, c );
1248 }
1249}
1250
1251
1253 const SFVEC2F& aHoleCenter,
1255 int aSizeIU,
1256 int aDepthIU,
1257 float aHoleInnerRadius,
1258 float aZSurface,
1259 bool aIsFront,
1260 float aPlatingThickness3d,
1261 float aUnitScale,
1262 float* aZEnd )
1263{
1264 if( !m_boardAdapter.m_Cfg->m_Render.show_plated_barrels )
1265 return false;
1266
1267 if( !aDstLayer || aPlatingThickness3d <= 0.0f || aHoleInnerRadius <= 0.0f )
1268 return false;
1269
1272 {
1273 return false;
1274 }
1275
1276 if( aSizeIU <= 0 || aDepthIU <= 0 )
1277 return false;
1278
1279 const float radius = aSizeIU * 0.5f * aUnitScale;
1280 const float depth = aDepthIU * aUnitScale;
1281
1282 if( radius <= aHoleInnerRadius || depth <= 0.0f )
1283 return false;
1284
1285 float zEnd = aIsFront ? ( aZSurface - depth ) : ( aZSurface + depth );
1286
1287 if( aZEnd )
1288 *aZEnd = zEnd;
1289
1290 const float zTop = std::max( aZSurface, zEnd );
1291 const float zBot = std::min( aZSurface, zEnd );
1292
1293 const int diameterBIU = std::max( aSizeIU,
1294 std::max( 1,
1295 (int) ( ( aHoleInnerRadius * 2.0f )
1296 / aUnitScale ) ) );
1297 const unsigned int nrSegments =
1298 std::max( 12u, m_boardAdapter.GetCircleSegmentCount( diameterBIU ) );
1299
1301 {
1302 generateCylinder( aHoleCenter, radius, radius + aPlatingThickness3d, zTop, zBot,
1303 nrSegments, aDstLayer );
1304 return true;
1305 }
1306
1307 float csTopRadius = radius;
1308 float csBotRadius = aHoleInnerRadius;
1309
1310 std::vector< SFVEC2F > innerContourTop, outerContourTop;
1311 std::vector< SFVEC2F > innerContourBot, outerContourBot;
1312
1313 generateRing( aHoleCenter, csTopRadius, csTopRadius + aPlatingThickness3d, nrSegments,
1314 innerContourTop, outerContourTop, false );
1315 generateRing( aHoleCenter, csBotRadius, csBotRadius + aPlatingThickness3d, nrSegments,
1316 innerContourBot, outerContourBot, false );
1317
1318 for( unsigned int i = 0; i < ( innerContourTop.size() - 1 ); ++i )
1319 {
1320 const SFVEC2F& vi0_top = innerContourTop[i + 0];
1321 const SFVEC2F& vi1_top = innerContourTop[i + 1];
1322 const SFVEC2F& vo0_top = outerContourTop[i + 0];
1323 const SFVEC2F& vo1_top = outerContourTop[i + 1];
1324
1325 const SFVEC2F& vi0_bot = innerContourBot[i + 0];
1326 const SFVEC2F& vi1_bot = innerContourBot[i + 1];
1327 const SFVEC2F& vo0_bot = outerContourBot[i + 0];
1328 const SFVEC2F& vo1_bot = outerContourBot[i + 1];
1329
1331 SFVEC3F( vi1_top.x, vi1_top.y, zTop ),
1332 SFVEC3F( vi0_top.x, vi0_top.y, zTop ),
1333 SFVEC3F( vi0_bot.x, vi0_bot.y, zBot ),
1334 SFVEC3F( vi1_bot.x, vi1_bot.y, zBot ) );
1335
1337 SFVEC3F( vo1_top.x, vo1_top.y, zTop ),
1338 SFVEC3F( vo0_top.x, vo0_top.y, zTop ),
1339 SFVEC3F( vo0_bot.x, vo0_bot.y, zBot ),
1340 SFVEC3F( vo1_bot.x, vo1_bot.y, zBot ) );
1341 }
1342
1343 return true;
1344}
1345
1346
1347void RENDER_3D_OPENGL::generateViaBarrels( float aPlatingThickness3d, float aUnitScale )
1348{
1349 if( !m_boardAdapter.GetBoard() || m_boardAdapter.GetViaCount() <= 0 )
1350 return;
1351
1352 if( !m_boardAdapter.m_Cfg->m_Render.show_plated_barrels )
1353 return;
1354
1355 float averageDiameter = m_boardAdapter.GetAverageViaHoleDiameter();
1356 unsigned int averageSegCount = m_boardAdapter.GetCircleSegmentCount( averageDiameter );
1357 unsigned int trianglesEstimate = averageSegCount * 8 * m_boardAdapter.GetViaCount();
1358
1359 TRIANGLE_DISPLAY_LIST* layerTriangleVIA = new TRIANGLE_DISPLAY_LIST( trianglesEstimate );
1360
1361 for( const PCB_TRACK* track : m_boardAdapter.GetBoard()->Tracks() )
1362 {
1363 if( track->Type() != PCB_VIA_T )
1364 continue;
1365
1366 const PCB_VIA* via = static_cast<const PCB_VIA*>( track );
1367 bool isBackdrilled = via->GetSecondaryDrillSize().has_value();
1368 bool isTertiarydrilled = via->GetTertiaryDrillSize().has_value();
1369 bool hasFrontPostMachining = via->GetFrontPostMachining().value_or( PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
1371 && via->GetFrontPostMachining().value_or( PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
1373 bool hasBackPostMachining = via->GetBackPostMachining().value_or( PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
1375 && via->GetBackPostMachining().value_or( PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
1377
1378 if( via->GetViaType() == VIATYPE::THROUGH && !isBackdrilled
1379 && !hasFrontPostMachining && !hasBackPostMachining )
1380 {
1381 continue;
1382 }
1383
1384 const float holediameter = via->GetDrillValue() * aUnitScale;
1385 const int nrSegments = m_boardAdapter.GetCircleSegmentCount( via->GetDrillValue() );
1386 const float hole_inner_radius = holediameter / 2.0f;
1387
1388 const SFVEC2F via_center( via->GetStart().x * aUnitScale,
1389 -via->GetStart().y * aUnitScale );
1390
1391 PCB_LAYER_ID top_layer, bottom_layer;
1392 via->LayerPair( &top_layer, &bottom_layer );
1393
1394 float ztop, zbot, dummy;
1395
1396 getLayerZPos( top_layer, ztop, dummy );
1397 getLayerZPos( bottom_layer, dummy, zbot );
1398
1399 wxASSERT( zbot < ztop );
1400
1401 float ztop_plated = ztop;
1402 float zbot_plated = zbot;
1403
1404 if( isBackdrilled )
1405 {
1406 PCB_LAYER_ID secEnd = via->GetSecondaryDrillEndLayer();
1407 float secZEnd;
1408
1409 // Backdrill goes from the back surface up to secEnd, so get the top of that layer
1410 // as the bottom of the plated barrel
1411 getLayerZPos( secEnd, secZEnd, dummy );
1412 zbot_plated = std::max( zbot_plated, secZEnd );
1413 }
1414
1415 if( isTertiarydrilled )
1416 {
1417 PCB_LAYER_ID terEnd = via->GetTertiaryDrillEndLayer();
1418 float terZEnd;
1419
1420 // Tertiary drill goes from the front surface down to terEnd, so get the bottom of that layer
1421 // as the top of the plated barrel
1422 getLayerZPos( terEnd, dummy, terZEnd );
1423 ztop_plated = std::min( ztop_plated, terZEnd );
1424 }
1425
1426 auto applyViaPostMachining = [&]( bool isFront )
1427 {
1428 auto modeOpt = isFront ? via->GetFrontPostMachining()
1429 : via->GetBackPostMachining();
1430
1431 if( !modeOpt
1433 {
1434 return;
1435 }
1436
1437 int sizeIU = isFront ? via->GetFrontPostMachiningSize()
1438 : via->GetBackPostMachiningSize();
1439 int depthIU = isFront ? via->GetFrontPostMachiningDepth()
1440 : via->GetBackPostMachiningDepth();
1441 float zSurface = isFront ? ztop : zbot;
1442 float zEnd = 0.0f;
1443
1444 if( appendPostMachiningGeometry( layerTriangleVIA, via_center, modeOpt.value(),
1445 sizeIU, depthIU, hole_inner_radius, zSurface,
1446 isFront, aPlatingThickness3d, aUnitScale, &zEnd ) )
1447 {
1448 if( isFront )
1449 ztop_plated = std::min( ztop_plated, zEnd );
1450 else
1451 zbot_plated = std::max( zbot_plated, zEnd );
1452 }
1453 };
1454
1455 if( hasFrontPostMachining )
1456 applyViaPostMachining( true );
1457 if( hasBackPostMachining )
1458 applyViaPostMachining( false );
1459
1460 generateCylinder( via_center, hole_inner_radius, hole_inner_radius + aPlatingThickness3d, ztop_plated,
1461 zbot_plated, nrSegments, layerTriangleVIA );
1462 }
1463
1464 const float padFrontSurface =
1465 m_boardAdapter.GetLayerBottomZPos( F_Cu )
1466 + m_boardAdapter.GetFrontCopperThickness() * 0.99f;
1467 const float padBackSurface =
1468 m_boardAdapter.GetLayerBottomZPos( B_Cu )
1469 - m_boardAdapter.GetBackCopperThickness() * 0.99f;
1470
1471 for( const FOOTPRINT* footprint : m_boardAdapter.GetBoard()->Footprints() )
1472 {
1473 for( const PAD* pad : footprint->Pads() )
1474 {
1475 if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
1476 continue;
1477
1478 if( !pad->HasHole() )
1479 continue;
1480
1481 if( pad->GetDrillShape() != PAD_DRILL_SHAPE::CIRCLE )
1482 continue;
1483
1484 const SFVEC2F padCenter( pad->GetPosition().x * aUnitScale,
1485 -pad->GetPosition().y * aUnitScale );
1486 const float holeInnerRadius =
1487 pad->GetDrillSize().x * 0.5f * aUnitScale;
1488
1489 auto emitPadPostMachining = [&]( bool isFront )
1490 {
1491 auto modeOpt = isFront ? pad->GetFrontPostMachining()
1492 : pad->GetBackPostMachining();
1493
1494 if( !modeOpt
1496 {
1497 return;
1498 }
1499
1500 int sizeIU = isFront ? pad->GetFrontPostMachiningSize()
1501 : pad->GetBackPostMachiningSize();
1502 int depthIU = isFront ? pad->GetFrontPostMachiningDepth()
1503 : pad->GetBackPostMachiningDepth();
1504 float zSurface = isFront ? padFrontSurface : padBackSurface;
1505
1506 appendPostMachiningGeometry( layerTriangleVIA, padCenter, modeOpt.value(),
1507 sizeIU, depthIU, holeInnerRadius, zSurface,
1508 isFront, aPlatingThickness3d, aUnitScale,
1509 nullptr );
1510 };
1511
1512 emitPadPostMachining( true );
1513 emitPadPostMachining( false );
1514 }
1515 }
1516
1517 m_microviaHoles = new OPENGL_RENDER_LIST( *layerTriangleVIA, 0, 0.0f, 0.0f );
1518
1519 delete layerTriangleVIA;
1520}
1521
1522
1523void RENDER_3D_OPENGL::generatePlatedHoleShells( int aPlatingThickness, float aUnitScale )
1524{
1525 if( !m_boardAdapter.GetBoard() )
1526 return;
1527
1528 if( m_boardAdapter.GetHoleCount() <= 0 && m_boardAdapter.GetViaCount() <= 0 )
1529 return;
1530
1531 SHAPE_POLY_SET tht_outer_holes_poly; // Stores the outer poly of the copper holes
1532 SHAPE_POLY_SET tht_inner_holes_poly; // Stores the inner poly of the copper holes
1533
1534 tht_outer_holes_poly.RemoveAllContours();
1535 tht_inner_holes_poly.RemoveAllContours();
1536
1537 for( const PCB_TRACK* track : m_boardAdapter.GetBoard()->Tracks() )
1538 {
1539 if( track->Type() != PCB_VIA_T )
1540 continue;
1541
1542 const PCB_VIA* via = static_cast<const PCB_VIA*>( track );
1543
1544 if( via->GetViaType() == VIATYPE::THROUGH )
1545 {
1546 TransformCircleToPolygon( tht_outer_holes_poly, via->GetPosition(),
1547 via->GetDrill() / 2 + aPlatingThickness,
1548 via->GetMaxError(), ERROR_INSIDE );
1549
1550 TransformCircleToPolygon( tht_inner_holes_poly, via->GetPosition(), via->GetDrill() / 2,
1551 via->GetMaxError(), ERROR_INSIDE );
1552 }
1553 }
1554
1555 for( const FOOTPRINT* footprint : m_boardAdapter.GetBoard()->Footprints() )
1556 {
1557 for( const PAD* pad : footprint->Pads() )
1558 {
1559 if( pad->GetAttribute() != PAD_ATTRIB::NPTH )
1560 {
1561 if( !pad->HasHole() )
1562 continue;
1563
1564 pad->TransformHoleToPolygon( tht_outer_holes_poly, aPlatingThickness,
1565 pad->GetMaxError(), ERROR_INSIDE );
1566 pad->TransformHoleToPolygon( tht_inner_holes_poly, 0,
1567 pad->GetMaxError(), ERROR_INSIDE );
1568 }
1569 }
1570 }
1571
1572 tht_outer_holes_poly.BooleanSubtract( tht_inner_holes_poly );
1573
1574 tht_outer_holes_poly.BooleanSubtract( m_antiBoardPolys );
1575
1576 CONTAINER_2D holesContainer;
1577
1578 ConvertPolygonToTriangles( tht_outer_holes_poly, holesContainer,
1579 aUnitScale, *m_boardAdapter.GetBoard() );
1580
1581 const LIST_OBJECT2D& holes2D = holesContainer.GetList();
1582
1583 if( holes2D.size() > 0 && m_boardAdapter.m_Cfg->m_Render.show_plated_barrels )
1584 {
1585 float layer_z_top, layer_z_bot, dummy;
1586
1587 getLayerZPos( F_Cu, layer_z_top, dummy );
1588 getLayerZPos( B_Cu, dummy, layer_z_bot );
1589
1590 TRIANGLE_DISPLAY_LIST* layerTriangles = new TRIANGLE_DISPLAY_LIST( holes2D.size() );
1591
1592 for( const OBJECT_2D* itemOnLayer : holes2D )
1593 {
1594 const OBJECT_2D* object2d_A = itemOnLayer;
1595
1596 wxASSERT( object2d_A->GetObjectType() == OBJECT_2D_TYPE::TRIANGLE );
1597
1598 const TRIANGLE_2D* tri = static_cast<const TRIANGLE_2D*>( object2d_A );
1599
1600 const SFVEC2F& v1 = tri->GetP1();
1601 const SFVEC2F& v2 = tri->GetP2();
1602 const SFVEC2F& v3 = tri->GetP3();
1603
1604 addTopAndBottomTriangles( layerTriangles, v1, v2, v3, layer_z_top, layer_z_bot );
1605 }
1606
1607 wxASSERT( tht_outer_holes_poly.OutlineCount() > 0 );
1608
1609 if( tht_outer_holes_poly.OutlineCount() > 0 )
1610 {
1611 layerTriangles->AddToMiddleContours( tht_outer_holes_poly,
1612 layer_z_bot, layer_z_top,
1613 aUnitScale, false );
1614
1615 m_padHoles = new OPENGL_RENDER_LIST( *layerTriangles, m_circleTexture,
1616 layer_z_top, layer_z_top );
1617 }
1618
1619 delete layerTriangles;
1620 }
1621}
1622
1623
1624void RENDER_3D_OPENGL::generateViaCovers( float aPlatingThickness3d, float aUnitScale )
1625{
1626 if( !m_boardAdapter.GetBoard() || m_boardAdapter.GetViaCount() <= 0 )
1627 return;
1628
1629 TRIANGLE_DISPLAY_LIST* frontCover = new TRIANGLE_DISPLAY_LIST( m_boardAdapter.GetViaCount() );
1630 TRIANGLE_DISPLAY_LIST* backCover = new TRIANGLE_DISPLAY_LIST( m_boardAdapter.GetViaCount() );
1631
1632 for( const PCB_TRACK* track : m_boardAdapter.GetBoard()->Tracks() )
1633 {
1634 if( track->Type() != PCB_VIA_T )
1635 continue;
1636
1637 const PCB_VIA* via = static_cast<const PCB_VIA*>( track );
1638
1639 const float holediameter = via->GetDrillValue() * aUnitScale;
1640 const float hole_radius = holediameter / 2.0f + 2.0f * aPlatingThickness3d;
1641 const SFVEC2F center( via->GetStart().x * aUnitScale,
1642 -via->GetStart().y * aUnitScale );
1643 unsigned int seg = m_boardAdapter.GetCircleSegmentCount( via->GetDrillValue() );
1644
1645 PCB_LAYER_ID top_layer, bottom_layer;
1646 via->LayerPair( &top_layer, &bottom_layer );
1647 float ztop, zbot, dummy;
1648 getLayerZPos( top_layer, ztop, dummy );
1649 getLayerZPos( bottom_layer, dummy, zbot );
1650
1651 bool frontCovering = via->GetFrontCoveringMode() == COVERING_MODE::COVERED
1652 || via->IsTented( F_Mask );
1653 bool backCovering = via->GetBackCoveringMode() == COVERING_MODE::COVERED
1654 || via->IsTented( B_Mask );
1655 bool frontPlugged = via->GetFrontPluggingMode() == PLUGGING_MODE::PLUGGED;
1656 bool backPlugged = via->GetBackPluggingMode() == PLUGGING_MODE::PLUGGED;
1657 bool filled = via->GetFillingMode() == FILLING_MODE::FILLED
1658 || via->GetCappingMode() == CAPPING_MODE::CAPPED;
1659
1660 const auto frontPostMachining =
1661 via->GetFrontPostMachining().value_or( PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED );
1662 const auto backPostMachining =
1663 via->GetBackPostMachining().value_or( PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED );
1664
1665 bool hasFrontPostMachining = frontPostMachining != PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED
1666 && frontPostMachining != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN;
1667 bool hasBackPostMachining = backPostMachining != PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED
1668 && backPostMachining != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN;
1669
1670 bool hasFrontBackdrill = via->GetSecondaryDrillStartLayer() == F_Cu;
1671 bool hasBackBackdrill = via->GetSecondaryDrillStartLayer() == B_Cu;
1672
1673 const float depth = hole_radius * 0.3f;
1674
1675 if( frontCovering && !hasFrontPostMachining && !hasFrontBackdrill )
1676 {
1677 if( filled || !frontPlugged )
1678 generateDisk( center, hole_radius, ztop, seg, frontCover, true );
1679 else
1680 generateDimple( center, hole_radius, ztop, depth, seg, frontCover, true );
1681 }
1682
1683 if( backCovering && !hasBackPostMachining && !hasBackBackdrill )
1684 {
1685 if( filled || !backPlugged )
1686 generateDisk( center, hole_radius, zbot, seg, backCover, false );
1687 else
1688 generateDimple( center, hole_radius, zbot, depth, seg, backCover, false );
1689 }
1690 }
1691
1692 if( frontCover->m_layer_top_triangles->GetVertexSize() > 0 )
1693 m_viaFrontCover = new OPENGL_RENDER_LIST( *frontCover, 0, 0.0f, 0.0f );
1694
1695 if( backCover->m_layer_bot_triangles->GetVertexSize() > 0 )
1696 m_viaBackCover = new OPENGL_RENDER_LIST( *backCover, 0, 0.0f, 0.0f );
1697
1698 delete frontCover;
1699 delete backCover;
1700}
1701
1702
1704{
1705 if( !m_boardAdapter.GetBoard() )
1706 return;
1707
1708 const int platingThickness = m_boardAdapter.GetHolePlatingThickness();
1709 const float unitScale = m_boardAdapter.BiuTo3dUnits();
1710 const float platingThickness3d = platingThickness * unitScale;
1711
1712 generateViaBarrels( platingThickness3d, unitScale );
1713 generatePlatedHoleShells( platingThickness, unitScale );
1714 generateViaCovers( platingThickness3d, unitScale );
1715}
1716
1717
1719{
1720 if( m_3dModelMap.size() > 0 )
1721 return;
1722
1723 if( wxFrame* frame = dynamic_cast<wxFrame*>( m_canvas->GetParent() ) )
1724 {
1725 STATUSBAR_REPORTER activityReporter( frame->GetStatusBar(),
1727 load3dModels( &activityReporter );
1728 }
1729 else
1730 {
1731 load3dModels( nullptr );
1732 }
1733}
1734
1735
1737{
1738 if( !m_boardAdapter.GetBoard() )
1739 return;
1740
1741 // Building the 3D models late crashes on recent versions of macOS
1742 // Unclear the exact mechanism, but as a workaround, just build them
1743 // all the time. See https://gitlab.com/kicad/code/kicad/-/issues/17198
1744#ifndef __WXMAC__
1745 if( !m_boardAdapter.m_IsPreviewer
1746 && !m_boardAdapter.m_Cfg->m_Render.show_footprints_normal
1747 && !m_boardAdapter.m_Cfg->m_Render.show_footprints_insert
1748 && !m_boardAdapter.m_Cfg->m_Render.show_footprints_virtual )
1749 {
1750 return;
1751 }
1752#endif
1753
1754 S3D_CACHE* cacheMgr = m_boardAdapter.Get3dCacheManager();
1755
1756 // Go for all footprints
1757 for( const FOOTPRINT* footprint : m_boardAdapter.GetBoard()->Footprints() )
1758 {
1759 wxString libraryName = footprint->GetFPID().GetLibNickname();
1760 wxString footprintBasePath = wxEmptyString;
1761
1762 if( m_boardAdapter.GetBoard()->GetProject() )
1763 {
1764 try
1765 {
1766 // FindRow() can throw an exception
1767 std::optional<LIBRARY_TABLE_ROW*> fpRow =
1768 PROJECT_PCB::FootprintLibAdapter( m_boardAdapter.GetBoard()->GetProject() )
1769 ->GetRow( libraryName );
1770
1771 if( fpRow )
1772 footprintBasePath = LIBRARY_MANAGER::GetFullURI( *fpRow, true );
1773 }
1774 catch( ... )
1775 {
1776 // Do nothing if the libraryName is not found in lib table
1777 }
1778 }
1779
1780 for( const FP_3DMODEL& fp_model : footprint->Models() )
1781 {
1782 if( fp_model.m_Show && !fp_model.m_Filename.empty() )
1783 {
1784 if( aStatusReporter )
1785 {
1786 // Display the short filename of the 3D fp_model loaded:
1787 // (the full name is usually too long to be displayed)
1788 wxFileName fn( fp_model.m_Filename );
1789 aStatusReporter->Report( wxString::Format( _( "Loading %s..." ),
1790 fn.GetFullName() ) );
1791 }
1792
1793 // Check if the fp_model is not present in our cache map
1794 // (Not already loaded in memory)
1795 if( !m_3dModelMap.contains( fp_model.m_Filename ) )
1796 {
1797 // It is not present, try get it from cache
1798 std::vector<const EMBEDDED_FILES*> embeddedFilesStack;
1799 embeddedFilesStack.push_back( footprint->GetEmbeddedFiles() );
1800 embeddedFilesStack.push_back( m_boardAdapter.GetBoard()->GetEmbeddedFiles() );
1801
1802 const S3DMODEL* modelPtr = cacheMgr->GetModel( fp_model.m_Filename, footprintBasePath,
1803 std::move( embeddedFilesStack ) );
1804
1805 // only add it if the return is not NULL
1806 if( modelPtr )
1807 {
1808 MATERIAL_MODE materialMode = m_boardAdapter.m_Cfg->m_Render.material_mode;
1809 MODEL_3D* model = new MODEL_3D( *modelPtr, materialMode );
1810
1811 m_3dModelMap[ fp_model.m_Filename ] = model;
1812 }
1813 }
1814 }
1815 }
1816 }
1817}
MATERIAL_MODE
Render 3d model shape materials mode.
Definition 3d_enums.h:67
Defines math related functions.
void ApplyExtrusionTransform(SHAPE_POLY_SET &aOutline, const EXTRUDED_3D_BODY *aBody, const VECTOR2I &aFpPos)
Apply 2D extrusion transforms (rotation, scale, offset) to an outline.
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.
@ ERROR_INSIDE
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
std::map< PCB_LAYER_ID, SHAPE_POLY_SET * > MAP_POLY
A type that stores polysets for each layer id.
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.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:81
const LIST_OBJECT2D & GetList() const
double AsRadians() const
Definition eda_angle.h:120
VECTOR3D m_offset
Definition footprint.h:114
VECTOR3D m_scale
Definition footprint.h:112
const SFVEC2F & GetCenter() const
float GetRadius() const
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...
static const LSET & PhysicalLayersMask()
Return a mask holding all layers which are physically realized.
Definition lset.cpp:693
static OBJECT_2D_STATS & Instance()
Definition object_2d.h:133
void ResetStats()
Definition object_2d.h:118
OBJECT_2D_TYPE GetObjectType() const
Definition object_2d.h:103
Store the OpenGL display lists to related with a layer.
Definition pad.h:61
Simple non-intersecting polygon with 4 points.
const SFVEC2F & GetV3() const
const SFVEC2F & GetV0() const
const SFVEC2F & GetV1() const
const SFVEC2F & GetV2() const
static FOOTPRINT_LIBRARY_ADAPTER * FootprintLibAdapter(PROJECT *aProject)
BOARD_ADAPTER & m_boardAdapter
Settings reference in use for this render.
OPENGL_RENDER_LIST * m_board
void reload(REPORTER *aStatusReporter, REPORTER *aWarningReporter)
OPENGL_RENDER_LIST * generateHoles(const LIST_OBJECT2D &aListHolesObject2d, const SHAPE_POLY_SET &aPoly, float aZtop, float aZbot, bool aInvertFaces, const BVH_CONTAINER_2D *aThroughHoles=nullptr)
void generateCylinder(const SFVEC2F &aCenter, float aInnerRadius, float aOuterRadius, float aZtop, float aZbot, unsigned int aNr_sides_per_circle, TRIANGLE_DISPLAY_LIST *aDstLayer)
OPENGL_RENDER_LIST * m_outerThroughHoleRings
OPENGL_RENDER_LIST * m_offboardPadsFront
void generateRing(const SFVEC2F &aCenter, float aInnerRadius, float aOuterRadius, unsigned int aNr_sides_per_circle, std::vector< SFVEC2F > &aInnerContourResult, std::vector< SFVEC2F > &aOuterContourResult, bool aInvertOrder)
SHAPE_POLY_SET m_antiBoardPolys
The negative polygon representation of the board outline.
void load3dModels(REPORTER *aStatusReporter)
Load footprint models from the cache and load it to openGL lists in the form of MODEL_3D objects.
OPENGL_RENDER_LIST * generateLayerList(const BVH_CONTAINER_2D *aContainer, const SHAPE_POLY_SET *aPolyList, PCB_LAYER_ID aLayer, const BVH_CONTAINER_2D *aThroughHoles=nullptr)
OPENGL_RENDER_LIST * m_microviaHoles
OPENGL_RENDER_LIST * createBoard(const SHAPE_POLY_SET &aBoardPoly, const BVH_CONTAINER_2D *aThroughHoles=nullptr)
void Load3dModelsIfNeeded()
Load footprint models if they are not already loaded, i.e.
std::map< const FOOTPRINT *, OPENGL_RENDER_LIST * > m_extrudedPadLists
void addObjectTriangles(const RING_2D *aRing, TRIANGLE_DISPLAY_LIST *aDstLayer, float aZtop, float aZbot)
MAP_OGL_DISP_LISTS m_layers
MAP_OGL_DISP_LISTS m_innerLayerHoles
OPENGL_RENDER_LIST * m_boardWithHoles
void generateInvCone(const SFVEC2F &aCenter, float aInnerRadius, float aOuterRadius, float aZtop, float aZbot, unsigned int aNr_sides_per_circle, TRIANGLE_DISPLAY_LIST *aDstLayer, EDA_ANGLE aAngle)
MAP_OGL_DISP_LISTS m_outerLayerHoles
OPENGL_RENDER_LIST * m_offboardPadsBack
std::map< wxString, MODEL_3D * > m_3dModelMap
OPENGL_RENDER_LIST * m_viaBackCover
OPENGL_RENDER_LIST * m_viaFrontCover
OPENGL_RENDER_LIST * generateEmptyLayerList(PCB_LAYER_ID aLayer)
LIST_TRIANGLES m_triangles
store pointers so can be deleted latter
OPENGL_RENDER_LIST * m_outerViaThroughHoles
OPENGL_RENDER_LIST * m_outerThroughHoles
void generateViaCovers(float aPlatingThickness3d, float aUnitScale)
OPENGL_RENDER_LIST * m_platedPadsFront
void generatePlatedHoleShells(int aPlatingThickness, float aUnitScale)
std::map< const FOOTPRINT *, OPENGL_RENDER_LIST * > m_extrudedBodyLists
void generateDisk(const SFVEC2F &aCenter, float aRadius, float aZ, unsigned int aNr_sides_per_circle, TRIANGLE_DISPLAY_LIST *aDstLayer, bool aTop)
OPENGL_RENDER_LIST * m_postMachinePlugs
Board material plugs for backdrill/counterbore/countersink.
bool appendPostMachiningGeometry(TRIANGLE_DISPLAY_LIST *aDstLayer, const SFVEC2F &aHoleCenter, PAD_DRILL_POST_MACHINING_MODE aMode, int aSizeIU, int aDepthIU, float aHoleInnerRadius, float aZSurface, bool aIsFront, float aPlatingThickness3d, float aUnitScale, float *aZEnd)
OPENGL_RENDER_LIST * m_antiBoard
void getLayerZPos(PCB_LAYER_ID aLayerID, float &aOutZtop, float &aOutZbot) const
EDA_3D_CANVAS * m_canvas
OPENGL_RENDER_LIST * m_padHoles
void addTopAndBottomTriangles(TRIANGLE_DISPLAY_LIST *aDst, const SFVEC2F &v0, const SFVEC2F &v1, const SFVEC2F &v2, float top, float bot)
void generateViaBarrels(float aPlatingThickness3d, float aUnitScale)
OPENGL_RENDER_LIST * m_platedPadsBack
void generateDimple(const SFVEC2F &aCenter, float aRadius, float aZ, float aDepth, unsigned int aNr_sides_per_circle, TRIANGLE_DISPLAY_LIST *aDstLayer, bool aTop)
void backfillPostMachine()
Create ring-shaped plugs for holes that have backdrill or post-machining.
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
float GetOuterRadius() const
Definition ring_2d.h:44
float GetInnerRadius() const
Definition ring_2d.h:43
const SFVEC2F & GetCenter() const
Definition ring_2d.h:42
const SFVEC2F & GetLeftEnd() const
const SFVEC2F & GetRightEnd() const
const SFVEC2F & GetLeftStar() const
const SFVEC2F & GetLeftDir() const
const SFVEC2F & GetEnd() const
float GetRadius() const
float GetRadiusSquared() const
const SFVEC2F & GetStart() const
const SFVEC2F & GetRightDir() const
const SFVEC2F & GetRightStar() const
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 set of closed polygons.
void RemoveAllContours()
Remove all outlines & holes (clears) the polygon set.
void BooleanAdd(const SHAPE_POLY_SET &b)
Perform boolean polyset union.
void Simplify()
Simplify the polyset (merges overlapping polys, eliminates degeneracy/self-intersections)
void BooleanIntersection(const SHAPE_POLY_SET &b)
Perform boolean polyset intersection.
int OutlineCount() const
Return the number of outlines in the set.
SHAPE_POLY_SET CloneDropTriangulation() const
void BooleanSubtract(const SHAPE_POLY_SET &b)
Perform boolean polyset difference.
A wrapper for reporting to a specific text location in a statusbar.
Definition reporter.h:357
const SFVEC2F & GetP2() const
Definition triangle_2d.h:40
const SFVEC2F & GetP3() const
Definition triangle_2d.h:41
const SFVEC2F & GetP1() const
Definition triangle_2d.h:39
Store arrays of triangles to be used to create display lists.
TRIANGLE_LIST * m_layer_bot_segment_ends
void AddToMiddleContours(const SHAPE_LINE_CHAIN &outlinePath, float zBot, float zTop, double aBiuTo3Du, bool aInvertFaceDirection, const BVH_CONTAINER_2D *aThroughHoles=nullptr)
TRIANGLE_LIST * m_layer_middle_contours_quads
TRIANGLE_LIST * m_layer_top_segment_ends
TRIANGLE_LIST * m_layer_bot_triangles
TRIANGLE_LIST * m_layer_top_triangles
void AddTriangle(const SFVEC3F &aV1, const SFVEC3F &aV2, const SFVEC3F &aV3)
void AddQuad(const SFVEC3F &aV1, const SFVEC3F &aV2, const SFVEC3F &aV3, const SFVEC3F &aV4)
unsigned int GetVertexSize() const
std::list< OBJECT_2D * > LIST_OBJECT2D
void TransformCircleToPolygon(SHAPE_LINE_CHAIN &aBuffer, const VECTOR2I &aCenter, int aRadius, int aError, ERROR_LOC aErrorLoc, int aMinSegCount=0)
Convert a circle to a polygon, using multiple straight lines.
#define _(s)
Declaration of the eda_3d_viewer class.
@ TENTHS_OF_A_DEGREE_T
Definition eda_angle.h:30
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ B_Mask
Definition layer_ids.h:94
@ B_Cu
Definition layer_ids.h:61
@ F_Mask
Definition layer_ids.h:93
@ F_SilkS
Definition layer_ids.h:96
@ B_SilkS
Definition layer_ids.h:97
@ F_Cu
Definition layer_ids.h:60
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
int64_t GetRunningMicroSecs()
An alternate way to calculate an elapsed time (in microsecondes) to class PROF_COUNTER.
#define SIZE_OF_CIRCLE_TEXTURE
std::vector< FAB_LAYER_COLOR > dummy
Store the a model based on meshes and materials.
Definition c3dmodel.h:91
KIBIS top(path, &reporter)
KIBIS_MODEL * model
VECTOR3I v1(5, 5, 5)
VECTOR2I center
int radius
VECTOR2I end
VECTOR2I v2(1, 0)
VECTOR2I v3(-2, 1)
int delta
void ConvertPolygonToTriangles(const SHAPE_POLY_SET &aPolyList, CONTAINER_2D_BASE &aDstContainer, float aBiuTo3dUnitsScale, const BOARD_ITEM &aBoardItem)
@ 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