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