KiCad PCB EDA Suite
Loading...
Searching...
No Matches
render_3d_opengl.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-2020 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 <cstdint>
27#include <gal/opengl/kiglew.h> // Must be included first
28
30#include "render_3d_opengl.h"
31#include "opengl_utils.h"
33#include <board.h>
34#include <footprint.h>
36#include <3d_math.h>
37#include <glm/geometric.hpp>
38#include <lset.h>
39#include <pgm_base.h>
40#include <math/util.h> // for KiROUND
41#include <utility>
42#include <vector>
43#include <wx/log.h>
44
45#include <base_units.h>
46
50#define UNITS3D_TO_UNITSPCB ( pcbIUScale.IU_PER_MM )
51
53 CAMERA& aCamera ) :
54 RENDER_3D_BASE( aAdapter, aCamera ),
55 m_canvas( aCanvas )
56{
57 wxLogTrace( m_logTrace, wxT( "RENDER_3D_OPENGL::RENDER_3D_OPENGL" ) );
58
59 m_layers.clear();
60 m_outerLayerHoles.clear();
61 m_innerLayerHoles.clear();
62 m_triangles.clear();
63 m_board = nullptr;
64 m_antiBoard = nullptr;
65
66 m_platedPadsFront = nullptr;
67 m_platedPadsBack = nullptr;
68 m_offboardPadsFront = nullptr;
69 m_offboardPadsBack = nullptr;
70
71 m_outerThroughHoles = nullptr;
73 m_outerViaThroughHoles = nullptr;
74 m_vias = nullptr;
75 m_padHoles = nullptr;
76
78 m_grid = 0;
79 m_lastGridType = GRID3D_TYPE::NONE;
80 m_currentRollOverItem = nullptr;
81 m_boardWithHoles = nullptr;
82
83 m_3dModelMap.clear();
84}
85
86
88{
89 wxLogTrace( m_logTrace, wxT( "RENDER_3D_OPENGL::RENDER_3D_OPENGL" ) );
90
92
93 glDeleteTextures( 1, &m_circleTexture );
94}
95
96
98{
99 return 50; // ms
100}
101
102
103void RENDER_3D_OPENGL::SetCurWindowSize( const wxSize& aSize )
104{
105 if( m_windowSize != aSize )
106 {
107 m_windowSize = aSize;
108 glViewport( 0, 0, m_windowSize.x, m_windowSize.y );
109
110 // Initialize here any screen dependent data here
111 }
112}
113
114
116{
117 if( enabled )
118 glEnable( GL_LIGHT0 );
119 else
120 glDisable( GL_LIGHT0 );
121}
122
123
125{
126 if( enabled )
127 glEnable( GL_LIGHT1 );
128 else
129 glDisable( GL_LIGHT1 );
130}
131
132
134{
135 if( enabled )
136 glEnable( GL_LIGHT2 );
137 else
138 glDisable( GL_LIGHT2 );
139}
140
141
143{
144 const float arrow_size = RANGE_SCALE_3D * 0.30f;
145
146 glDisable( GL_CULL_FACE );
147
148 // YxY squared view port, this is on propose
149 glViewport( 4, 4, m_windowSize.y / 8 , m_windowSize.y / 8 );
150 glClear( GL_DEPTH_BUFFER_BIT );
151
152 glMatrixMode( GL_PROJECTION );
153 glLoadIdentity();
154 gluPerspective( 45.0f, 1.0f, 0.001f, RANGE_SCALE_3D );
155
156 glMatrixMode( GL_MODELVIEW );
157 glLoadIdentity();
158
159 const glm::mat4 TranslationMatrix =
160 glm::translate( glm::mat4( 1.0f ), SFVEC3F( 0.0f, 0.0f, -( arrow_size * 2.75f ) ) );
161
162 const glm::mat4 ViewMatrix = TranslationMatrix * m_camera.GetRotationMatrix();
163
164 glLoadMatrixf( glm::value_ptr( ViewMatrix ) );
165
167
168 glColor3f( 0.9f, 0.0f, 0.0f );
169 DrawRoundArrow( SFVEC3F( 0.0f, 0.0f, 0.0f ), SFVEC3F( arrow_size, 0.0f, 0.0f ), 0.275f );
170
171 glColor3f( 0.0f, 0.9f, 0.0f );
172 DrawRoundArrow( SFVEC3F( 0.0f, 0.0f, 0.0f ), SFVEC3F( 0.0f, arrow_size, 0.0f ), 0.275f );
173
174 glColor3f( 0.0f, 0.0f, 0.9f );
175 DrawRoundArrow( SFVEC3F( 0.0f, 0.0f, 0.0f ), SFVEC3F( 0.0f, 0.0f, arrow_size ), 0.275f );
176
177 glEnable( GL_CULL_FACE );
178}
179
180
182{
183 m_materials = {};
184
185 // http://devernay.free.fr/cours/opengl/materials.html
186
187 // Plated copper
188 // Copper material mixed with the copper color
189 m_materials.m_Copper.m_Ambient = SFVEC3F( m_boardAdapter.m_CopperColor.r * 0.1f,
192
193 m_materials.m_Copper.m_Specular = SFVEC3F( m_boardAdapter.m_CopperColor.r * 0.75f + 0.25f,
194 m_boardAdapter.m_CopperColor.g * 0.75f + 0.25f,
195 m_boardAdapter.m_CopperColor.b * 0.75f + 0.25f );
196
197 // This guess the material type(ex: copper vs gold) to determine the
198 // shininess factor between 0.1 and 0.4
199 float shininessfactor = 0.40f - mapf( fabs( m_boardAdapter.m_CopperColor.r -
201 0.15f, 1.00f,
202 0.00f, 0.30f );
203
204 m_materials.m_Copper.m_Shininess = shininessfactor * 128.0f;
205 m_materials.m_Copper.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
206
207
208 // Non plated copper (raw copper)
209 m_materials.m_NonPlatedCopper.m_Ambient = SFVEC3F( 0.191f, 0.073f, 0.022f );
210 m_materials.m_NonPlatedCopper.m_Diffuse = SFVEC3F( 184.0f / 255.0f, 115.0f / 255.0f,
211 50.0f / 255.0f );
212 m_materials.m_NonPlatedCopper.m_Specular = SFVEC3F( 0.256f, 0.137f, 0.086f );
213 m_materials.m_NonPlatedCopper.m_Shininess = 0.1f * 128.0f;
214 m_materials.m_NonPlatedCopper.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
215
216 // Paste material mixed with paste color
220
221 m_materials.m_Paste.m_Specular = SFVEC3F( m_boardAdapter.m_SolderPasteColor.r *
227
228 m_materials.m_Paste.m_Shininess = 0.1f * 128.0f;
229 m_materials.m_Paste.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
230
231 // Silk screen material mixed with silk screen color
232 m_materials.m_SilkSTop.m_Ambient = SFVEC3F( m_boardAdapter.m_SilkScreenColorTop.r,
235
236 m_materials.m_SilkSTop.m_Specular = SFVEC3F(
240
241 m_materials.m_SilkSTop.m_Shininess = 0.078125f * 128.0f;
242 m_materials.m_SilkSTop.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
243
244 // Silk screen material mixed with silk screen color
245 m_materials.m_SilkSBot.m_Ambient = SFVEC3F( m_boardAdapter.m_SilkScreenColorBot.r,
248
249 m_materials.m_SilkSBot.m_Specular = SFVEC3F(
253
254 m_materials.m_SilkSBot.m_Shininess = 0.078125f * 128.0f;
255 m_materials.m_SilkSBot.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
256
257 m_materials.m_SolderMask.m_Shininess = 0.8f * 128.0f;
258 m_materials.m_SolderMask.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
259
260 // Epoxy material
261 m_materials.m_EpoxyBoard.m_Ambient = SFVEC3F( 117.0f / 255.0f, 97.0f / 255.0f,
262 47.0f / 255.0f );
263
264 m_materials.m_EpoxyBoard.m_Specular = SFVEC3F( 18.0f / 255.0f, 3.0f / 255.0f,
265 20.0f / 255.0f );
266
267 m_materials.m_EpoxyBoard.m_Shininess = 0.1f * 128.0f;
268 m_materials.m_EpoxyBoard.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
269}
270
271
273{
275
276 if( cfg.use_board_editor_copper_colors && IsCopperLayer( aLayerID ) )
277 {
278 COLOR4D copper_color = m_boardAdapter.m_BoardEditorColors[aLayerID];
279 m_materials.m_Copper.m_Diffuse = SFVEC3F( copper_color.r, copper_color.g,
280 copper_color.b );
281 OglSetMaterial( m_materials.m_Copper, 1.0f );
282 m_materials.m_NonPlatedCopper.m_Diffuse = m_materials.m_Copper.m_Diffuse;
283 OglSetMaterial( m_materials.m_NonPlatedCopper, 1.0f );
284
285 return;
286 }
287
288 switch( aLayerID )
289 {
290 case F_Mask:
291 case B_Mask:
292 {
293 const SFVEC4F layerColor = aLayerID == F_Mask ? m_boardAdapter.m_SolderMaskColorTop
295
296 m_materials.m_SolderMask.m_Diffuse = layerColor;
297
298 // Convert Opacity to Transparency
299 m_materials.m_SolderMask.m_Transparency = 1.0f - layerColor.a;
300
301 m_materials.m_SolderMask.m_Ambient = m_materials.m_SolderMask.m_Diffuse * 0.3f;
302
303 m_materials.m_SolderMask.m_Specular = m_materials.m_SolderMask.m_Diffuse
304 * m_materials.m_SolderMask.m_Diffuse;
305
306 OglSetMaterial( m_materials.m_SolderMask, 1.0f );
307 break;
308 }
309
310 case B_Paste:
311 case F_Paste:
313 OglSetMaterial( m_materials.m_Paste, 1.0f );
314 break;
315
316 case B_SilkS:
317 m_materials.m_SilkSBot.m_Diffuse = m_boardAdapter.m_SilkScreenColorBot;
318 OglSetMaterial( m_materials.m_SilkSBot, 1.0f );
319 break;
320
321 case F_SilkS:
322 m_materials.m_SilkSTop.m_Diffuse = m_boardAdapter.m_SilkScreenColorTop;
323 OglSetMaterial( m_materials.m_SilkSTop, 1.0f );
324 break;
325
326 case B_Adhes:
327 case F_Adhes:
328 case Dwgs_User:
329 case Cmts_User:
330 case Eco1_User:
331 case Eco2_User:
332 case Edge_Cuts:
333 case Margin:
334 case B_CrtYd:
335 case F_CrtYd:
336 case B_Fab:
337 case F_Fab:
338 switch( aLayerID )
339 {
340 case Dwgs_User: m_materials.m_Plastic.m_Diffuse = m_boardAdapter.m_UserDrawingsColor; break;
341 case Cmts_User: m_materials.m_Plastic.m_Diffuse = m_boardAdapter.m_UserCommentsColor; break;
342 case Eco1_User: m_materials.m_Plastic.m_Diffuse = m_boardAdapter.m_ECO1Color; break;
343 case Eco2_User: m_materials.m_Plastic.m_Diffuse = m_boardAdapter.m_ECO2Color; break;
344 case Edge_Cuts: m_materials.m_Plastic.m_Diffuse = m_boardAdapter.m_UserDrawingsColor; break;
345 case Margin: m_materials.m_Plastic.m_Diffuse = m_boardAdapter.m_UserDrawingsColor; break;
346 default:
347 m_materials.m_Plastic.m_Diffuse = m_boardAdapter.GetLayerColor( aLayerID );
348 break;
349 }
350
351 m_materials.m_Plastic.m_Ambient = SFVEC3F( m_materials.m_Plastic.m_Diffuse.r * 0.05f,
352 m_materials.m_Plastic.m_Diffuse.g * 0.05f,
353 m_materials.m_Plastic.m_Diffuse.b * 0.05f );
354
355 m_materials.m_Plastic.m_Specular = SFVEC3F( m_materials.m_Plastic.m_Diffuse.r * 0.7f,
356 m_materials.m_Plastic.m_Diffuse.g * 0.7f,
357 m_materials.m_Plastic.m_Diffuse.b * 0.7f );
358
359 m_materials.m_Plastic.m_Shininess = 0.078125f * 128.0f;
360 m_materials.m_Plastic.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
361 OglSetMaterial( m_materials.m_Plastic, 1.0f );
362 break;
363
364 default:
365 m_materials.m_Copper.m_Diffuse = m_boardAdapter.m_CopperColor;
366 OglSetMaterial( m_materials.m_Copper, 1.0f );
367 break;
368 }
369}
370
371
373{
374 // Setup light
375 // https://www.opengl.org/sdk/docs/man2/xhtml/glLight.xml
376 const GLfloat ambient[] = { 0.084f, 0.084f, 0.084f, 1.0f };
377 const GLfloat diffuse0[] = { 0.3f, 0.3f, 0.3f, 1.0f };
378 const GLfloat specular0[] = { 0.5f, 0.5f, 0.5f, 1.0f };
379
380 glLightfv( GL_LIGHT0, GL_AMBIENT, ambient );
381 glLightfv( GL_LIGHT0, GL_DIFFUSE, diffuse0 );
382 glLightfv( GL_LIGHT0, GL_SPECULAR, specular0 );
383
384 const GLfloat diffuse12[] = { 0.7f, 0.7f, 0.7f, 1.0f };
385 const GLfloat specular12[] = { 0.7f, 0.7f, 0.7f, 1.0f };
386
387 // defines a directional light that points along the negative z-axis
388 GLfloat position[4] = { 0.0f, 0.0f, 1.0f, 0.0f };
389
390 // This makes a vector slight not perpendicular with XZ plane
391 const SFVEC3F vectorLight = SphericalToCartesian( glm::pi<float>() * 0.03f,
392 glm::pi<float>() * 0.25f );
393
394 position[0] = vectorLight.x;
395 position[1] = vectorLight.y;
396 position[2] = vectorLight.z;
397
398 glLightfv( GL_LIGHT1, GL_AMBIENT, ambient );
399 glLightfv( GL_LIGHT1, GL_DIFFUSE, diffuse12 );
400 glLightfv( GL_LIGHT1, GL_SPECULAR, specular12 );
401 glLightfv( GL_LIGHT1, GL_POSITION, position );
402
403 // defines a directional light that points along the positive z-axis
404 position[2] = -position[2];
405
406 glLightfv( GL_LIGHT2, GL_AMBIENT, ambient );
407 glLightfv( GL_LIGHT2, GL_DIFFUSE, diffuse12 );
408 glLightfv( GL_LIGHT2, GL_SPECULAR, specular12 );
409 glLightfv( GL_LIGHT2, GL_POSITION, position );
410
411 const GLfloat lmodel_ambient[] = { 0.0f, 0.0f, 0.0f, 1.0f };
412
413 glLightModelfv( GL_LIGHT_MODEL_AMBIENT, lmodel_ambient );
414
415 glLightModeli( GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE );
416}
417
418
420{
421 OglSetMaterial( m_materials.m_NonPlatedCopper, 1.0f );
422}
423
424
426{
427 glEnable( GL_POLYGON_OFFSET_FILL );
428 glPolygonOffset( -0.1f, -2.0f );
429 setLayerMaterial( aLayer_id );
430}
431
432
434{
435 glDisable( GL_POLYGON_OFFSET_FILL );
436}
437
438
439void RENDER_3D_OPENGL::renderBoardBody( bool aSkipRenderHoles )
440{
441 m_materials.m_EpoxyBoard.m_Diffuse = m_boardAdapter.m_BoardBodyColor;
442
443 // opacity to transparency
444 m_materials.m_EpoxyBoard.m_Transparency = 1.0f - m_boardAdapter.m_BoardBodyColor.a;
445
446 OglSetMaterial( m_materials.m_EpoxyBoard, 1.0f );
447
448 OPENGL_RENDER_LIST* ogl_disp_list = nullptr;
449
450 if( aSkipRenderHoles )
451 ogl_disp_list = m_board;
452 else
453 ogl_disp_list = m_boardWithHoles;
454
455 if( ogl_disp_list )
456 {
459
460 ogl_disp_list->SetItIsTransparent( true );
461 ogl_disp_list->DrawAll();
462 }
463}
464
465
466static inline SFVEC4F premultiplyAlpha( const SFVEC4F& aInput )
467{
468 return SFVEC4F( aInput.r * aInput.a, aInput.g * aInput.a, aInput.b * aInput.a, aInput.a );
469}
470
471
472bool RENDER_3D_OPENGL::Redraw( bool aIsMoving, REPORTER* aStatusReporter,
473 REPORTER* aWarningReporter )
474{
475 // Initialize OpenGL
477 {
478 if( !initializeOpenGL() )
479 return false;
480 }
481
483
485 {
486 std::unique_ptr<BUSY_INDICATOR> busy = CreateBusyIndicator();
487
488 if( aStatusReporter )
489 aStatusReporter->Report( _( "Loading..." ) );
490
491 // Careful here!
492 // We are in the middle of rendering and the reload method may show
493 // a dialog box that requires the opengl context for a redraw
494 Pgm().GetGLContextManager()->RunWithoutCtxLock( [this, aStatusReporter, aWarningReporter]()
495 {
496 reload( aStatusReporter, aWarningReporter );
497 } );
498
499 // generate a new 3D grid as the size of the board may had changed
500 m_lastGridType = static_cast<GRID3D_TYPE>( cfg.grid_type );
502 }
503 else
504 {
505 // Check if grid was changed
506 if( cfg.grid_type != m_lastGridType )
507 {
508 // and generate a new one
509 m_lastGridType = static_cast<GRID3D_TYPE>( cfg.grid_type );
511 }
512 }
513
515
516 // Initial setup
517 glDepthFunc( GL_LESS );
518 glEnable( GL_CULL_FACE );
519 glFrontFace( GL_CCW ); // This is the OpenGL default
520 glEnable( GL_NORMALIZE ); // This allow OpenGL to normalize the normals after transformations
521 glViewport( 0, 0, m_windowSize.x, m_windowSize.y );
522
523 if( aIsMoving && cfg.opengl_AA_disableOnMove )
524 glDisable( GL_MULTISAMPLE );
525 else
526 glEnable( GL_MULTISAMPLE );
527
528 // clear color and depth buffers
529 glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
530 glClearDepth( 1.0f );
531 glClearStencil( 0x00 );
532 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
533
535
536 // Draw the background ( rectangle with color gradient)
539
540 glEnable( GL_DEPTH_TEST );
541
542 // Set projection and modelview matrixes
543 glMatrixMode( GL_PROJECTION );
544 glLoadMatrixf( glm::value_ptr( m_camera.GetProjectionMatrix() ) );
545 glMatrixMode( GL_MODELVIEW );
546 glLoadIdentity();
547 glLoadMatrixf( glm::value_ptr( m_camera.GetViewMatrix() ) );
548
549 // Position the headlight
550 setLightFront( true );
551 setLightTop( true );
552 setLightBottom( true );
553
554 glEnable( GL_LIGHTING );
555
556 {
557 const SFVEC3F& cameraPos = m_camera.GetPos();
558
559 // Place the light at a minimum Z so the diffuse factor will not drop
560 // and the board will still look with good light.
561 float zpos;
562
563 if( cameraPos.z > 0.0f )
564 zpos = glm::max( cameraPos.z, 0.5f ) + cameraPos.z * cameraPos.z;
565 else
566 zpos = glm::min( cameraPos.z,-0.5f ) - cameraPos.z * cameraPos.z;
567
568 // This is a point light.
569 const GLfloat headlight_pos[] = { cameraPos.x, cameraPos.y, zpos, 1.0f };
570
571 glLightfv( GL_LIGHT0, GL_POSITION, headlight_pos );
572 }
573
574 bool skipThickness = aIsMoving && cfg.opengl_thickness_disableOnMove;
575 bool skipRenderHoles = aIsMoving && cfg.opengl_holes_disableOnMove;
576 bool skipRenderVias = aIsMoving && cfg.opengl_vias_disableOnMove;
577 bool showThickness = !skipThickness;
578
579 std::bitset<LAYER_3D_END> layerFlags = m_boardAdapter.GetVisibleLayers();
580
582
583 if( !( skipRenderVias || skipRenderHoles ) && m_vias )
584 m_vias->DrawAll();
585
586 if( !skipRenderHoles && m_padHoles )
588
589 // Display copper and tech layers
590 for( MAP_OGL_DISP_LISTS::const_iterator ii = m_layers.begin(); ii != m_layers.end(); ++ii )
591 {
592 const PCB_LAYER_ID layer = ( PCB_LAYER_ID )( ii->first );
593 bool isSilkLayer = layer == F_SilkS || layer == B_SilkS;
594 bool isMaskLayer = layer == F_Mask || layer == B_Mask;
595 bool isPasteLayer = layer == F_Paste || layer == B_Paste;
596
597 // Mask layers are not processed here because they are a special case
598 if( isMaskLayer )
599 continue;
600
601 // Do not show inner layers when it is displaying the board and board body is opaque
602 // enough: the time to create inner layers can be *really significant*.
603 // So avoid creating them is they are not very visible
604 const double opacity_min = 0.8;
605
606 if( layerFlags.test( LAYER_3D_BOARD ) && m_boardAdapter.m_BoardBodyColor.a > opacity_min )
607 {
608 // generating internal copper layers is time consuming. so skip them
609 // if the board body is masking them (i.e. if the opacity is near 1.0)
610 // B_Cu is layer 2 and all inner layers are higher values
611 if( layer > B_Cu && IsCopperLayer( layer ) )
612 continue;
613 }
614
615 glPushMatrix();
616
617 OPENGL_RENDER_LIST* pLayerDispList = static_cast<OPENGL_RENDER_LIST*>( ii->second );
618
619 if( IsCopperLayer( layer ) )
620 {
621 if( cfg.DifferentiatePlatedCopper() )
623 else
624 setLayerMaterial( layer );
625
626 OPENGL_RENDER_LIST* outerTH = nullptr;
627 OPENGL_RENDER_LIST* viaHoles = nullptr;
628
629 if( !skipRenderHoles )
630 {
631 outerTH = m_outerThroughHoles;
632 viaHoles = m_outerLayerHoles[layer];
633 }
634
635 if( m_antiBoard )
636 m_antiBoard->ApplyScalePosition( pLayerDispList );
637
638 if( outerTH )
639 outerTH->ApplyScalePosition( pLayerDispList );
640
641 pLayerDispList->DrawCulled( showThickness, outerTH, viaHoles, m_antiBoard );
642
643 // Draw plated & offboard pads
644 if( layer == F_Cu && ( m_platedPadsFront || m_offboardPadsFront ) )
645 {
647
649 m_platedPadsFront->DrawCulled( showThickness, outerTH, viaHoles, m_antiBoard );
650
652 m_offboardPadsFront->DrawCulled( showThickness, outerTH, viaHoles );
653 }
654 else if( layer == B_Cu && ( m_platedPadsBack || m_offboardPadsBack ) )
655 {
657
658 if( m_platedPadsBack )
659 m_platedPadsBack->DrawCulled( showThickness, outerTH, viaHoles, m_antiBoard );
660
662 m_offboardPadsBack->DrawCulled( showThickness, outerTH, viaHoles );
663 }
664
666 }
667 else if( isPasteLayer && skipRenderHoles )
668 {
669 // Do not render paste layers when skipRenderHoles is enabled or we get z-fight issues
670 }
671 else
672 {
673 setLayerMaterial( layer );
674
675 OPENGL_RENDER_LIST* throughHolesOuter = nullptr;
676 OPENGL_RENDER_LIST* anti_board = nullptr;
677 OPENGL_RENDER_LIST* solder_mask = nullptr;
678
679 if( isSilkLayer && cfg.clip_silk_on_via_annuli )
680 throughHolesOuter = m_outerThroughHoleRings;
681 else
682 throughHolesOuter = m_outerThroughHoles;
683
684 if( isSilkLayer && cfg.show_off_board_silk )
685 anti_board = nullptr;
686 else if( LSET::PhysicalLayersMask().test( layer ) )
687 anti_board = m_antiBoard;
688
689 if( isSilkLayer && cfg.subtract_mask_from_silk && !cfg.show_off_board_silk )
690 solder_mask = m_layers[ ( layer == B_SilkS) ? B_Mask : F_Mask ];
691
692 if( throughHolesOuter )
693 throughHolesOuter->ApplyScalePosition( pLayerDispList );
694
695 if( anti_board )
696 anti_board->ApplyScalePosition( pLayerDispList );
697
698 if( solder_mask )
699 solder_mask->ApplyScalePosition( pLayerDispList );
700
701 pLayerDispList->DrawCulled( showThickness, solder_mask, throughHolesOuter, anti_board );
702 }
703
704 glPopMatrix();
705 }
706
707 glm::mat4 cameraViewMatrix;
708
709 glGetFloatv( GL_MODELVIEW_MATRIX, glm::value_ptr( cameraViewMatrix ) );
710
711 // Render 3D Models (Non-transparent)
712 renderOpaqueModels( cameraViewMatrix );
713
714 // Display board body
715 if( layerFlags.test( LAYER_3D_BOARD ) )
716 renderBoardBody( skipRenderHoles );
717
718 // Display transparent mask layers
719 if( layerFlags.test( LAYER_3D_SOLDERMASK_TOP )
720 || layerFlags.test( LAYER_3D_SOLDERMASK_BOTTOM ) )
721 {
722 // add a depth buffer offset, it will help to hide some artifacts
723 // on silkscreen where the SolderMask is removed
724 glEnable( GL_POLYGON_OFFSET_FILL );
725 glPolygonOffset( 0.0f, -2.0f );
726
727 if( m_camera.GetPos().z > 0 )
728 {
729 if( layerFlags.test( LAYER_3D_SOLDERMASK_BOTTOM ) )
730 {
732 showThickness, skipRenderHoles );
733 }
734
735 if( layerFlags.test( LAYER_3D_SOLDERMASK_TOP ) )
736 {
738 showThickness, skipRenderHoles );
739 }
740 }
741 else
742 {
743 if( layerFlags.test( LAYER_3D_SOLDERMASK_TOP ) )
744 {
746 showThickness, skipRenderHoles );
747 }
748
749 if( layerFlags.test( LAYER_3D_SOLDERMASK_BOTTOM ) )
750 {
752 showThickness, skipRenderHoles );
753 }
754 }
755
756 glDisable( GL_POLYGON_OFFSET_FILL );
757 glPolygonOffset( 0.0f, 0.0f );
758 }
759
760 // Render 3D Models (Transparent)
761 // !TODO: this can be optimized. If there are no transparent models (or no opacity),
762 // then there is no need to make this function call.
763 glDepthMask( GL_FALSE );
764 glEnable( GL_BLEND );
765 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
766
767 // Enables Texture Env so it can combine model transparency with each footprint opacity
768 glEnable( GL_TEXTURE_2D );
769 glActiveTexture( GL_TEXTURE0 );
770
771 // Uses an existent texture so the glTexEnv operations will work.
772 glBindTexture( GL_TEXTURE_2D, m_circleTexture );
773
774 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE );
775 glTexEnvf( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE );
776 glTexEnvf( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE );
777
778 glTexEnvi( GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PRIMARY_COLOR );
779 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR );
780
781 glTexEnvi( GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PREVIOUS );
782 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR );
783
784 glTexEnvi( GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PRIMARY_COLOR );
785 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA );
786 glTexEnvi( GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_CONSTANT );
787 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA );
788
789 renderTransparentModels( cameraViewMatrix );
790
791 glDisable( GL_BLEND );
793
794 glDepthMask( GL_TRUE );
795
796 // Render Grid
797 if( cfg.grid_type != GRID3D_TYPE::NONE )
798 {
799 glDisable( GL_LIGHTING );
800
801 if( glIsList( m_grid ) )
802 glCallList( m_grid );
803
804 glEnable( GL_LIGHTING );
805 }
806
807 // Render 3D arrows
808 if( cfg.show_axis )
810
811 // Return back to the original viewport (this is important if we want
812 // to take a screenshot after the render)
813 glViewport( 0, 0, m_windowSize.x, m_windowSize.y );
814
815 return false;
816}
817
818
820{
821 glEnable( GL_LINE_SMOOTH );
822 glShadeModel( GL_SMOOTH );
823
824 // 4-byte pixel alignment
825 glPixelStorei( GL_UNPACK_ALIGNMENT, 4 );
826
827 // Initialize the open GL texture to draw the filled semi-circle of the segments
829
830 if( !circleImage )
831 return false;
832
833 unsigned int circleRadius = ( SIZE_OF_CIRCLE_TEXTURE / 2 ) - 4;
834
835 circleImage->CircleFilled( ( SIZE_OF_CIRCLE_TEXTURE / 2 ) - 0,
836 ( SIZE_OF_CIRCLE_TEXTURE / 2 ) - 0,
837 circleRadius,
838 0xFF );
839
840 IMAGE* circleImageBlured = new IMAGE( circleImage->GetWidth(), circleImage->GetHeight() );
841
842 circleImageBlured->EfxFilter_SkipCenter( circleImage, IMAGE_FILTER::GAUSSIAN_BLUR, circleRadius - 8 );
843
844 m_circleTexture = OglLoadTexture( *circleImageBlured );
845
846 delete circleImageBlured;
847 circleImageBlured = nullptr;
848
849 delete circleImage;
850 circleImage = nullptr;
851
852 init_lights();
853
854 // Use this mode if you want see the triangle lines (debug proposes)
855 //glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
856 m_canvasInitialized = true;
857
858 return true;
859}
860
861
863{
864 glEnable( GL_COLOR_MATERIAL );
865 glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );
866
867 const SFVEC4F ambient = SFVEC4F( 0.0f, 0.0f, 0.0f, 1.0f );
868 const SFVEC4F diffuse = SFVEC4F( 0.0f, 0.0f, 0.0f, 1.0f );
869 const SFVEC4F emissive = SFVEC4F( 0.0f, 0.0f, 0.0f, 1.0f );
870 const SFVEC4F specular = SFVEC4F( 0.1f, 0.1f, 0.1f, 1.0f );
871
872 glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, &specular.r );
873 glMaterialf( GL_FRONT_AND_BACK, GL_SHININESS, 96.0f );
874
875 glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, &ambient.r );
876 glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, &diffuse.r );
877 glMaterialfv( GL_FRONT_AND_BACK, GL_EMISSION, &emissive.r );
878}
879
880
882{
883#define DELETE_AND_FREE( ptr ) \
884 { \
885 delete ptr; \
886 ptr = nullptr; \
887 } \
888
889#define DELETE_AND_FREE_MAP( map ) \
890 { \
891 for( auto& [ layer, ptr ] : map ) \
892 delete ptr; \
893 \
894 map.clear(); \
895 }
896
897 if( glIsList( m_grid ) )
898 glDeleteLists( m_grid, 1 );
899
900 m_grid = 0;
901
903
908
911
913 delete list;
914
915 m_triangles.clear();
916
918
919 m_3dModelMatrixMap.clear();
920
924
928
931}
932
933
935 bool aShowThickness, bool aSkipRenderHoles )
936{
937 wxASSERT( (aLayerID == B_Mask) || (aLayerID == F_Mask) );
938
939 if( m_board )
940 {
941 OPENGL_RENDER_LIST* solder_mask = m_layers[ aLayerID ];
942 OPENGL_RENDER_LIST* via_holes = aSkipRenderHoles ? nullptr : m_outerThroughHoles;
943
944 if( via_holes )
946
948
949 setLayerMaterial( aLayerID );
951 m_board->DrawCulled( aShowThickness, solder_mask, via_holes );
952 }
953}
954
955
956void RENDER_3D_OPENGL::get3dModelsSelected( std::list<MODELTORENDER> &aDstRenderList, bool aGetTop,
957 bool aGetBot, bool aRenderTransparentOnly,
958 bool aRenderSelectedOnly )
959{
960 wxASSERT( ( aGetTop == true ) || ( aGetBot == true ) );
961
962 if( !m_boardAdapter.GetBoard() )
963 return;
964
966
967 // Go for all footprints
969 {
970 bool highlight = false;
971
973 {
974 if( fp->IsSelected() )
975 highlight = true;
976
978 highlight = true;
979
980 if( aRenderSelectedOnly != highlight )
981 continue;
982 }
983
984 if( !fp->Models().empty() )
985 {
986 if( m_boardAdapter.IsFootprintShown( (FOOTPRINT_ATTR_T) fp->GetAttributes() ) )
987 {
988 const bool isFlipped = fp->IsFlipped();
989
990 if( aGetTop == !isFlipped || aGetBot == isFlipped )
991 get3dModelsFromFootprint( aDstRenderList, fp, aRenderTransparentOnly,
992 highlight );
993 }
994 }
995 }
996}
997
998
999void RENDER_3D_OPENGL::get3dModelsFromFootprint( std::list<MODELTORENDER> &aDstRenderList,
1000 const FOOTPRINT* aFootprint,
1001 bool aRenderTransparentOnly, bool aIsSelected )
1002{
1003 if( !aFootprint->Models().empty() )
1004 {
1005 const double zpos = m_boardAdapter.GetFootprintZPos( aFootprint->IsFlipped() );
1006
1007 VECTOR2I pos = aFootprint->GetPosition();
1008
1009 glm::mat4 fpMatrix( 1.0f );
1010
1011 fpMatrix = glm::translate( fpMatrix, SFVEC3F( pos.x * m_boardAdapter.BiuTo3dUnits(),
1012 -pos.y * m_boardAdapter.BiuTo3dUnits(),
1013 zpos ) );
1014
1015 if( !aFootprint->GetOrientation().IsZero() )
1016 {
1017 fpMatrix = glm::rotate( fpMatrix, (float) aFootprint->GetOrientation().AsRadians(),
1018 SFVEC3F( 0.0f, 0.0f, 1.0f ) );
1019 }
1020
1021 if( aFootprint->IsFlipped() )
1022 {
1023 fpMatrix = glm::rotate( fpMatrix, glm::pi<float>(), SFVEC3F( 0.0f, 1.0f, 0.0f ) );
1024 fpMatrix = glm::rotate( fpMatrix, glm::pi<float>(), SFVEC3F( 0.0f, 0.0f, 1.0f ) );
1025 }
1026
1027 double modelunit_to_3d_units_factor = m_boardAdapter.BiuTo3dUnits() * UNITS3D_TO_UNITSPCB;
1028
1029 fpMatrix = glm::scale( fpMatrix, SFVEC3F( modelunit_to_3d_units_factor ) );
1030
1031 // Get the list of model files for this model
1032 for( const FP_3DMODEL& sM : aFootprint->Models() )
1033 {
1034 if( !sM.m_Show || sM.m_Filename.empty() )
1035 continue;
1036
1037 // Check if the model is present in our cache map
1038 auto cache_i = m_3dModelMap.find( sM.m_Filename );
1039
1040 if( cache_i == m_3dModelMap.end() )
1041 continue;
1042
1043 if( const MODEL_3D* modelPtr = cache_i->second )
1044 {
1045 bool opaque = sM.m_Opacity >= 1.0;
1046
1047 if( ( !aRenderTransparentOnly && modelPtr->HasOpaqueMeshes() && opaque ) ||
1048 ( aRenderTransparentOnly && ( modelPtr->HasTransparentMeshes() || !opaque ) ) )
1049 {
1050 glm::mat4 modelworldMatrix = fpMatrix;
1051
1052 const SFVEC3F offset = SFVEC3F( sM.m_Offset.x, sM.m_Offset.y, sM.m_Offset.z );
1053 const SFVEC3F rotation = SFVEC3F( sM.m_Rotation.x, sM.m_Rotation.y,
1054 sM.m_Rotation.z );
1055 const SFVEC3F scale = SFVEC3F( sM.m_Scale.x, sM.m_Scale.y, sM.m_Scale.z );
1056
1057 std::vector<float> key = { offset.x, offset.y, offset.z,
1058 rotation.x, rotation.y, rotation.z,
1059 scale.x, scale.y, scale.z };
1060
1061 auto it = m_3dModelMatrixMap.find( key );
1062
1063 if( it != m_3dModelMatrixMap.end() )
1064 {
1065 modelworldMatrix *= it->second;
1066 }
1067 else
1068 {
1069 glm::mat4 mtx( 1.0f );
1070 mtx = glm::translate( mtx, offset );
1071 mtx = glm::rotate( mtx, glm::radians( -rotation.z ), { 0.0f, 0.0f, 1.0f } );
1072 mtx = glm::rotate( mtx, glm::radians( -rotation.y ), { 0.0f, 1.0f, 0.0f } );
1073 mtx = glm::rotate( mtx, glm::radians( -rotation.x ), { 1.0f, 0.0f, 0.0f } );
1074 mtx = glm::scale( mtx, scale );
1075 m_3dModelMatrixMap[ key ] = mtx;
1076
1077 modelworldMatrix *= mtx;
1078 }
1079
1080 aDstRenderList.emplace_back( modelworldMatrix, modelPtr,
1081 aRenderTransparentOnly ? sM.m_Opacity : 1.0f,
1082 aRenderTransparentOnly,
1083 aFootprint->IsSelected() || aIsSelected );
1084 }
1085 }
1086 }
1087 }
1088}
1089
1090
1091void RENDER_3D_OPENGL::renderOpaqueModels( const glm::mat4 &aCameraViewMatrix )
1092{
1094
1095 const SFVEC3F selColor = m_boardAdapter.GetColor( cfg.opengl_selection_color );
1096
1097 glPushMatrix();
1098
1099 std::list<MODELTORENDER> renderList;
1100
1102 {
1103 renderList.clear();
1104
1105 get3dModelsSelected( renderList, true, true, false, true );
1106
1107 if( !renderList.empty() )
1108 {
1109 MODEL_3D::BeginDrawMulti( false );
1110
1111 for( const MODELTORENDER& mtr : renderList )
1112 renderModel( aCameraViewMatrix, mtr, selColor, nullptr );
1113
1115 }
1116 }
1117
1118 renderList.clear();
1119 get3dModelsSelected( renderList, true, true, false, false );
1120
1121 if( !renderList.empty() )
1122 {
1124
1125 for( const MODELTORENDER& mtr : renderList )
1126 renderModel( aCameraViewMatrix, mtr, selColor, nullptr );
1127
1129 }
1130
1131 glPopMatrix();
1132}
1133
1134
1135void RENDER_3D_OPENGL::renderTransparentModels( const glm::mat4 &aCameraViewMatrix )
1136{
1138
1139 const SFVEC3F selColor = m_boardAdapter.GetColor( cfg.opengl_selection_color );
1140
1141 std::list<MODELTORENDER> renderListModels; // do not clear it until this function returns
1142
1144 {
1145 // Get Transparent Selected
1146 get3dModelsSelected( renderListModels, true, true, true, true );
1147 }
1148
1149 // Get Transparent Not Selected
1150 get3dModelsSelected( renderListModels, true, true, true, false );
1151
1152 if( renderListModels.empty() )
1153 return;
1154
1155 std::vector<std::pair<const MODELTORENDER *, float>> transparentModelList;
1156
1157 transparentModelList.reserve( renderListModels.size() );
1158
1159 // Calculate the distance to the camera for each model
1160 const SFVEC3F &cameraPos = m_camera.GetPos();
1161
1162 for( const MODELTORENDER& mtr : renderListModels )
1163 {
1164 const BBOX_3D& bBox = mtr.m_model->GetBBox();
1165 const SFVEC3F& bBoxCenter = bBox.GetCenter();
1166 const SFVEC3F bBoxWorld = mtr.m_modelWorldMat * glm::vec4( bBoxCenter, 1.0f );
1167
1168 const float distanceToCamera = glm::length( cameraPos - bBoxWorld );
1169
1170 transparentModelList.emplace_back( &mtr, distanceToCamera );
1171 }
1172
1173 // Sort from back to front
1174 std::sort( transparentModelList.begin(), transparentModelList.end(),
1175 [&]( std::pair<const MODELTORENDER *, float>& a,
1176 std::pair<const MODELTORENDER *, float>& b )
1177 {
1178 if( a.second != b.second )
1179 return a.second > b.second;
1180
1181 return a.first > b.first; // use pointers as a last resort
1182 } );
1183
1184 // Start rendering calls
1185 glPushMatrix();
1186
1187 bool isUsingColorInformation = !( transparentModelList.begin()->first->m_isSelected &&
1189
1190 MODEL_3D::BeginDrawMulti( isUsingColorInformation );
1191
1192 for( const std::pair<const MODELTORENDER *, float>& mtr : transparentModelList )
1193 {
1195 {
1196 // Toggle between using model color or the select color
1197 if( !isUsingColorInformation && !mtr.first->m_isSelected )
1198 {
1199 isUsingColorInformation = true;
1200
1201 glEnableClientState( GL_COLOR_ARRAY );
1202 glEnableClientState( GL_TEXTURE_COORD_ARRAY );
1203 glEnable( GL_COLOR_MATERIAL );
1204 }
1205 else if( isUsingColorInformation && mtr.first->m_isSelected )
1206 {
1207 isUsingColorInformation = false;
1208
1209 glDisableClientState( GL_COLOR_ARRAY );
1210 glDisableClientState( GL_TEXTURE_COORD_ARRAY );
1211 glDisable( GL_COLOR_MATERIAL );
1212 }
1213 }
1214
1215 // Render model, sort each individuall material group
1216 // by passing cameraPos
1217 renderModel( aCameraViewMatrix, *mtr.first, selColor, &cameraPos );
1218 }
1219
1221
1222 glPopMatrix();
1223}
1224
1225
1226void RENDER_3D_OPENGL::renderModel( const glm::mat4 &aCameraViewMatrix,
1227 const MODELTORENDER &aModelToRender,
1228 const SFVEC3F &aSelColor, const SFVEC3F *aCameraWorldPos )
1229{
1231
1232 const glm::mat4 modelviewMatrix = aCameraViewMatrix * aModelToRender.m_modelWorldMat;
1233
1234 glLoadMatrixf( glm::value_ptr( modelviewMatrix ) );
1235
1236 aModelToRender.m_model->Draw( aModelToRender.m_isTransparent, aModelToRender.m_opacity,
1237 aModelToRender.m_isSelected, aSelColor,
1238 &aModelToRender.m_modelWorldMat, aCameraWorldPos );
1239
1240 if( cfg.show_model_bbox )
1241 {
1242 const bool wasBlendEnabled = glIsEnabled( GL_BLEND );
1243
1244 if( !wasBlendEnabled )
1245 {
1246 glEnable( GL_BLEND );
1247 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
1248 }
1249
1250 glDisable( GL_LIGHTING );
1251
1252 glLineWidth( 1 );
1253 aModelToRender.m_model->DrawBboxes();
1254
1255 glLineWidth( 4 );
1256 aModelToRender.m_model->DrawBbox();
1257
1258 glEnable( GL_LIGHTING );
1259
1260 if( !wasBlendEnabled )
1261 glDisable( GL_BLEND );
1262 }
1263}
1264
1265
1267{
1268 if( glIsList( m_grid ) )
1269 glDeleteLists( m_grid, 1 );
1270
1271 m_grid = 0;
1272
1273 if( aGridType == GRID3D_TYPE::NONE )
1274 return;
1275
1276 m_grid = glGenLists( 1 );
1277
1278 if( !glIsList( m_grid ) )
1279 return;
1280
1281 glNewList( m_grid, GL_COMPILE );
1282
1283 glEnable( GL_BLEND );
1284 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
1285
1286 const double zpos = 0.0;
1287
1288 // Color of grid lines
1289 const SFVEC3F gridColor = m_boardAdapter.GetColor( DARKGRAY );
1290
1291 // Color of grid lines every 5 lines
1292 const SFVEC3F gridColor_marker = m_boardAdapter.GetColor( LIGHTBLUE );
1293 const double scale = m_boardAdapter.BiuTo3dUnits();
1294 const GLfloat transparency = 0.35f;
1295
1296 double griSizeMM = 0.0;
1297
1298 switch( aGridType )
1299 {
1300 case GRID3D_TYPE::GRID_1MM: griSizeMM = 1.0; break;
1301 case GRID3D_TYPE::GRID_2P5MM: griSizeMM = 2.5; break;
1302 case GRID3D_TYPE::GRID_5MM: griSizeMM = 5.0; break;
1303 case GRID3D_TYPE::GRID_10MM: griSizeMM = 10.0; break;
1304
1305 default:
1306 case GRID3D_TYPE::NONE: return;
1307 }
1308
1309 glNormal3f( 0.0, 0.0, 1.0 );
1310
1311 const VECTOR2I brd_size = m_boardAdapter.GetBoardSize();
1312 VECTOR2I brd_center_pos = m_boardAdapter.GetBoardPos();
1313
1314 brd_center_pos.y = -brd_center_pos.y;
1315
1316 const int xsize = std::max( brd_size.x, pcbIUScale.mmToIU( 100 ) ) * 1.2;
1317 const int ysize = std::max( brd_size.y, pcbIUScale.mmToIU( 100 ) ) * 1.2;
1318
1319 // Grid limits, in 3D units
1320 double xmin = ( brd_center_pos.x - xsize / 2 ) * scale;
1321 double xmax = ( brd_center_pos.x + xsize / 2 ) * scale;
1322 double ymin = ( brd_center_pos.y - ysize / 2 ) * scale;
1323 double ymax = ( brd_center_pos.y + ysize / 2 ) * scale;
1324 double zmin = pcbIUScale.mmToIU( -50 ) * scale;
1325 double zmax = pcbIUScale.mmToIU( 100 ) * scale;
1326
1327 // Set rasterised line width (min value = 1)
1328 glLineWidth( 1 );
1329
1330 // Draw horizontal grid centered on 3D origin (center of the board)
1331 for( int ii = 0; ; ii++ )
1332 {
1333 if( (ii % 5) )
1334 glColor4f( gridColor.r, gridColor.g, gridColor.b, transparency );
1335 else
1336 glColor4f( gridColor_marker.r, gridColor_marker.g, gridColor_marker.b,
1337 transparency );
1338
1339 const int delta = KiROUND( ii * griSizeMM * pcbIUScale.IU_PER_MM );
1340
1341 if( delta <= xsize / 2 ) // Draw grid lines parallel to X axis
1342 {
1343 glBegin( GL_LINES );
1344 glVertex3f( (brd_center_pos.x + delta) * scale, -ymin, zpos );
1345 glVertex3f( (brd_center_pos.x + delta) * scale, -ymax, zpos );
1346 glEnd();
1347
1348 if( ii != 0 )
1349 {
1350 glBegin( GL_LINES );
1351 glVertex3f( (brd_center_pos.x - delta) * scale, -ymin, zpos );
1352 glVertex3f( (brd_center_pos.x - delta) * scale, -ymax, zpos );
1353 glEnd();
1354 }
1355 }
1356
1357 if( delta <= ysize / 2 ) // Draw grid lines parallel to Y axis
1358 {
1359 glBegin( GL_LINES );
1360 glVertex3f( xmin, -( brd_center_pos.y + delta ) * scale, zpos );
1361 glVertex3f( xmax, -( brd_center_pos.y + delta ) * scale, zpos );
1362 glEnd();
1363
1364 if( ii != 0 )
1365 {
1366 glBegin( GL_LINES );
1367 glVertex3f( xmin, -( brd_center_pos.y - delta ) * scale, zpos );
1368 glVertex3f( xmax, -( brd_center_pos.y - delta ) * scale, zpos );
1369 glEnd();
1370 }
1371 }
1372
1373 if( ( delta > ysize / 2 ) && ( delta > xsize / 2 ) )
1374 break;
1375 }
1376
1377 // Draw vertical grid on Z axis
1378 glNormal3f( 0.0, -1.0, 0.0 );
1379
1380 // Draw vertical grid lines (parallel to Z axis)
1381 double posy = -brd_center_pos.y * scale;
1382
1383 for( int ii = 0; ; ii++ )
1384 {
1385 if( (ii % 5) )
1386 glColor4f( gridColor.r, gridColor.g, gridColor.b, transparency );
1387 else
1388 glColor4f( gridColor_marker.r, gridColor_marker.g, gridColor_marker.b,
1389 transparency );
1390
1391 const double delta = ii * griSizeMM * pcbIUScale.IU_PER_MM;
1392
1393 glBegin( GL_LINES );
1394 xmax = ( brd_center_pos.x + delta ) * scale;
1395
1396 glVertex3f( xmax, posy, zmin );
1397 glVertex3f( xmax, posy, zmax );
1398 glEnd();
1399
1400 if( ii != 0 )
1401 {
1402 glBegin( GL_LINES );
1403 xmin = ( brd_center_pos.x - delta ) * scale;
1404 glVertex3f( xmin, posy, zmin );
1405 glVertex3f( xmin, posy, zmax );
1406 glEnd();
1407 }
1408
1409 if( delta > xsize / 2.0f )
1410 break;
1411 }
1412
1413 // Draw horizontal grid lines on Z axis (parallel to X axis)
1414 for( int ii = 0; ; ii++ )
1415 {
1416 if( ii % 5 )
1417 glColor4f( gridColor.r, gridColor.g, gridColor.b, transparency );
1418 else
1419 glColor4f( gridColor_marker.r, gridColor_marker.g, gridColor_marker.b, transparency );
1420
1421 const double delta = ii * griSizeMM * pcbIUScale.IU_PER_MM * scale;
1422
1423 if( delta <= zmax )
1424 {
1425 // Draw grid lines on Z axis (positive Z axis coordinates)
1426 glBegin( GL_LINES );
1427 glVertex3f( xmin, posy, delta );
1428 glVertex3f( xmax, posy, delta );
1429 glEnd();
1430 }
1431
1432 if( delta <= -zmin && ( ii != 0 ) )
1433 {
1434 // Draw grid lines on Z axis (negative Z axis coordinates)
1435 glBegin( GL_LINES );
1436 glVertex3f( xmin, posy, -delta );
1437 glVertex3f( xmax, posy, -delta );
1438 glEnd();
1439 }
1440
1441 if( ( delta > zmax ) && ( delta > -zmin ) )
1442 break;
1443 }
1444
1445 glDisable( GL_BLEND );
1446
1447 glEndList();
1448}
GRID3D_TYPE
Grid types.
Definition: 3d_enums.h:54
Defines math related functions.
float mapf(float x, float in_min, float in_max, float out_min, float out_max)
Definition: 3d_math.h:133
SFVEC3F SphericalToCartesian(float aInclination, float aAzimuth)
https://en.wikipedia.org/wiki/Spherical_coordinate_system
Definition: 3d_math.h:43
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:108
#define RANGE_SCALE_3D
This defines the range that all coord will have to be rendered.
Definition: board_adapter.h:66
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition: box2.h:990
Helper class to handle information needed to display 3D board.
Definition: board_adapter.h:73
std::map< int, COLOR4D > m_BoardEditorColors
list of colors used by the board editor
double BiuTo3dUnits() const noexcept
Board integer units To 3D units.
SFVEC4F m_BgColorTop
background top color
SFVEC4F m_ECO2Color
SFVEC4F m_SolderPasteColor
in realistic mode: solder paste color
bool IsFootprintShown(FOOTPRINT_ATTR_T aFPAttributes) const
Test if footprint should be displayed in relation to attributes and the flags.
VECTOR2I GetBoardSize() const noexcept
Get the board size.
std::bitset< LAYER_3D_END > GetVisibleLayers() const
SFVEC4F m_SolderMaskColorBot
in realistic mode: solder mask color ( bot )
VECTOR2I GetBoardPos() const noexcept
Get the board center.
float GetLayerBottomZPos(PCB_LAYER_ID aLayerId) const noexcept
Get the bottom z position.
SFVEC4F m_SolderMaskColorTop
in realistic mode: solder mask color ( top )
SFVEC4F GetColor(const COLOR4D &aColor) const
SFVEC4F m_CopperColor
in realistic mode: copper color
const BOARD * GetBoard() const noexcept
float GetFootprintZPos(bool aIsFlipped) const
Get the position of the footprint in 3d integer units considering if it is flipped or not.
float GetNonCopperLayerThickness() const noexcept
Get the non copper layers thickness (in 3D units).
float GetLayerTopZPos(PCB_LAYER_ID aLayerId) const noexcept
Get the top z position.
EDA_3D_VIEWER_SETTINGS * m_Cfg
SFVEC4F m_SilkScreenColorTop
in realistic mode: SilkScreen color ( top )
SFVEC4F m_SilkScreenColorBot
in realistic mode: SilkScreen color ( bot )
float GetBoardBodyThickness() const noexcept
Get the board body thickness, including internal copper layers (in 3D units).
SFVEC4F m_BoardBodyColor
in realistic mode: FR4 board color
SFVEC4F m_UserCommentsColor
SFVEC4F GetLayerColor(PCB_LAYER_ID aLayerId) const
Get the technical color of a layer.
SFVEC4F m_ECO1Color
SFVEC4F m_UserDrawingsColor
SFVEC4F m_BgColorBot
background bottom color
const FOOTPRINTS & Footprints() const
Definition: board.h:336
A class used to derive camera objects from.
Definition: camera.h:103
glm::mat4 GetRotationMatrix() const
Get the rotation matrix to be applied in a transformation camera.
Definition: camera.cpp:241
const glm::mat4 & GetProjectionMatrix() const
Definition: camera.cpp:474
const SFVEC3F & GetPos() const
Definition: camera.h:136
const glm::mat4 & GetViewMatrix() const
Definition: camera.cpp:510
Implement a canvas based on a wxGLCanvas.
Definition: eda_3d_canvas.h:51
bool IsZero() const
Definition: eda_angle.h:133
double AsRadians() const
Definition: eda_angle.h:117
bool IsSelected() const
Definition: eda_item.h:110
EDA_ANGLE GetOrientation() const
Definition: footprint.h:225
bool IsFlipped() const
Definition: footprint.h:389
std::vector< FP_3DMODEL > & Models()
Definition: footprint.h:218
VECTOR2I GetPosition() const override
Definition: footprint.h:222
auto RunWithoutCtxLock(Func &&aFunction, Args &&... args)
Run the given function first releasing the GL context lock, then restoring it.
Manage an 8-bit channel image.
Definition: image.h:90
void CircleFilled(int aCx, int aCy, int aRadius, unsigned char aValue)
Definition: image.cpp:173
void EfxFilter_SkipCenter(IMAGE *aInImg, IMAGE_FILTER aFilterType, unsigned int aRadius)
Apply a filter to the input image and store it in the image class.
Definition: image.cpp:527
unsigned int GetHeight() const
Definition: image.h:214
unsigned int GetWidth() const
Definition: image.h:213
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:104
double r
Red component.
Definition: color4d.h:392
double g
Green component.
Definition: color4d.h:393
double b
Blue component.
Definition: color4d.h:394
static LSET PhysicalLayersMask()
Return a mask holding all layers which are physically realized.
Definition: lset.cpp:644
void DrawBbox() const
Draw main bounding box of the model.
Definition: 3d_model.cpp:571
static void EndDrawMulti()
Cleanup render states after drawing multiple models.
Definition: 3d_model.cpp:405
void Draw(bool aTransparent, float aOpacity, bool aUseSelectedMaterial, const SFVEC3F &aSelectionColor, const glm::mat4 *aModelWorldMatrix, const SFVEC3F *aCameraWorldPos) const
Render the model into the current context.
Definition: 3d_model.cpp:418
static void BeginDrawMulti(bool aUseColorInformation)
Set some basic render states before drawing multiple models.
Definition: 3d_model.cpp:389
void DrawBboxes() const
Draw individual bounding boxes of each mesh.
Definition: 3d_model.cpp:590
Store the OpenGL display lists to related with a layer.
void ApplyScalePosition(float aZposition, float aZscale)
void SetItIsTransparent(bool aSetTransparent)
void DrawCulled(bool aDrawMiddle, const OPENGL_RENDER_LIST *aSubtractList=nullptr, const OPENGL_RENDER_LIST *bSubtractList=nullptr, const OPENGL_RENDER_LIST *cSubtractList=nullptr, const OPENGL_RENDER_LIST *dSubtractList=nullptr) const
Draw all layers if they are visible by the camera if camera position is above the layer.
void DrawAll(bool aDrawMiddle=true) const
Call to draw all the display lists.
GL_CONTEXT_MANAGER * GetGLContextManager()
Definition: pgm_base.h:115
This is a base class to hold data and functions for render targets.
std::unique_ptr< BUSY_INDICATOR > CreateBusyIndicator() const
Return a created busy indicator, if a factory has been set, else a null pointer.
bool m_canvasInitialized
Flag if the canvas specific for this render was already initialized.
wxSize m_windowSize
The window size that this camera is working.
BOARD_ADAPTER & m_boardAdapter
Settings reference in use for this render.
OPENGL_RENDER_LIST * m_board
OPENGL_RENDER_LIST * m_vias
OPENGL_RENDER_LIST * m_outerThroughHoleRings
OPENGL_RENDER_LIST * m_offboardPadsFront
GRID3D_TYPE m_lastGridType
Stores the last grid type.
void renderOpaqueModels(const glm::mat4 &aCameraViewMatrix)
void generate3dGrid(GRID3D_TYPE aGridType)
Create a 3D grid to an OpenGL display list.
void setLightFront(bool enabled)
bool Redraw(bool aIsMoving, REPORTER *aStatusReporter, REPORTER *aWarningReporter) override
Redraw the view.
MAP_OGL_DISP_LISTS m_layers
MAP_OGL_DISP_LISTS m_innerLayerHoles
OPENGL_RENDER_LIST * m_boardWithHoles
RENDER_3D_OPENGL(EDA_3D_CANVAS *aCanvas, BOARD_ADAPTER &aAdapter, CAMERA &aCamera)
MAP_OGL_DISP_LISTS m_outerLayerHoles
OPENGL_RENDER_LIST * m_offboardPadsBack
BOARD_ITEM * m_currentRollOverItem
void renderBoardBody(bool aSkipRenderHoles)
std::map< std::vector< float >, glm::mat4 > m_3dModelMatrixMap
std::map< wxString, MODEL_3D * > m_3dModelMap
LIST_TRIANGLES m_triangles
store pointers so can be deleted latter
OPENGL_RENDER_LIST * m_outerViaThroughHoles
OPENGL_RENDER_LIST * m_outerThroughHoles
void setLayerMaterial(PCB_LAYER_ID aLayerID)
OPENGL_RENDER_LIST * m_platedPadsFront
void renderModel(const glm::mat4 &aCameraViewMatrix, const MODELTORENDER &aModelToRender, const SFVEC3F &aSelColor, const SFVEC3F *aCameraWorldPos)
int GetWaitForEditingTimeOut() override
Give the interface the time (in ms) that it should wait for editing or movements before (this works f...
void renderSolderMaskLayer(PCB_LAYER_ID aLayerID, float aZPos, bool aShowThickness, bool aSkipRenderHoles)
void renderTransparentModels(const glm::mat4 &aCameraViewMatrix)
void get3dModelsSelected(std::list< MODELTORENDER > &aDstRenderList, bool aGetTop, bool aGetBot, bool aRenderTransparentOnly, bool aRenderSelectedOnly)
void setPlatedCopperAndDepthOffset(PCB_LAYER_ID aLayer_id)
OPENGL_RENDER_LIST * m_antiBoard
void SetCurWindowSize(const wxSize &aSize) override
Before each render, the canvas will tell the render what is the size of its windows,...
OPENGL_RENDER_LIST * m_padHoles
GLuint m_grid
oGL list that stores current grid
OPENGL_RENDER_LIST * m_platedPadsBack
void get3dModelsFromFootprint(std::list< MODELTORENDER > &aDstRenderList, const FOOTPRINT *aFootprint, bool aRenderTransparentOnly, bool aIsSelected)
struct RENDER_3D_OPENGL::@2 m_materials
void setLightBottom(bool enabled)
void setLightTop(bool enabled)
A pure virtual class used to derive REPORTER objects from.
Definition: reporter.h:72
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)=0
Report a string with a given severity.
Store arrays of triangles to be used to create display lists.
@ LIGHTBLUE
Definition: color4d.h:62
@ DARKGRAY
Definition: color4d.h:46
#define DELETE_AND_FREE_MAP(map)
#define DELETE_AND_FREE(ptr)
#define _(s)
#define UNITS3D_TO_UNITSPCB
Implements a model viewer canvas.
FOOTPRINT_ATTR_T
The set of attributes allowed within a FOOTPRINT, using FOOTPRINT::SetAttributes() and FOOTPRINT::Get...
Definition: footprint.h:78
static const wxChar * m_logTrace
Trace mask used to enable or disable the trace output of this class.
@ LAYER_3D_SOLDERMASK_TOP
Definition: layer_ids.h:510
@ LAYER_3D_SOLDERMASK_BOTTOM
Definition: layer_ids.h:509
@ LAYER_3D_BOARD
Definition: layer_ids.h:504
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition: layer_ids.h:581
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
@ F_CrtYd
Definition: layer_ids.h:116
@ B_Adhes
Definition: layer_ids.h:103
@ Edge_Cuts
Definition: layer_ids.h:112
@ Dwgs_User
Definition: layer_ids.h:107
@ F_Paste
Definition: layer_ids.h:104
@ Cmts_User
Definition: layer_ids.h:108
@ F_Adhes
Definition: layer_ids.h:102
@ B_Mask
Definition: layer_ids.h:98
@ B_Cu
Definition: layer_ids.h:65
@ Eco1_User
Definition: layer_ids.h:109
@ F_Mask
Definition: layer_ids.h:97
@ B_Paste
Definition: layer_ids.h:105
@ F_Fab
Definition: layer_ids.h:119
@ Margin
Definition: layer_ids.h:113
@ F_SilkS
Definition: layer_ids.h:100
@ B_CrtYd
Definition: layer_ids.h:115
@ Eco2_User
Definition: layer_ids.h:110
@ B_SilkS
Definition: layer_ids.h:101
@ F_Cu
Definition: layer_ids.h:64
@ B_Fab
Definition: layer_ids.h:118
void OglResetTextureState()
Reset to default state the texture settings.
Definition: ogl_utils.cpp:214
void OglSetMaterial(const SMATERIAL &aMaterial, float aOpacity, bool aUseSelectedMaterial, SFVEC3F aSelectionColor)
Set OpenGL materials.
Definition: ogl_utils.cpp:144
GLuint OglLoadTexture(const IMAGE &aImage)
Generate a new OpenGL texture.
Definition: ogl_utils.cpp:96
void OglDrawBackground(const SFVEC4F &aTopColor, const SFVEC4F &aBotColor)
Definition: ogl_utils.cpp:185
Define generic OpenGL functions that are common to any OpenGL target.
void DrawRoundArrow(SFVEC3F aPosition, SFVEC3F aTargetPos, float aSize)
Draw a round arrow.
PGM_BASE & Pgm()
The global program "get" accessor.
Definition: pgm_base.cpp:1073
see class PGM_BASE
void init_lights()
static SFVEC4F premultiplyAlpha(const SFVEC4F &aInput)
#define SIZE_OF_CIRCLE_TEXTURE
const int scale
Manage a bounding box defined by two SFVEC3F min max points.
Definition: bbox_3d.h:43
SFVEC3F GetCenter() const
Return the center point of the bounding box.
Definition: bbox_3d.cpp:132
bool DifferentiatePlatedCopper()
return true if platted copper aeras and non platted copper areas must be drawn using a different colo...
const double IU_PER_MM
Definition: base_units.h:76
constexpr int mmToIU(double mm) const
Definition: base_units.h:88
constexpr int delta
glm::vec3 SFVEC3F
Definition: xv3d_types.h:44
glm::vec4 SFVEC4F
Definition: xv3d_types.h:46