KiCad PCB EDA Suite
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
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 <mrluzeiro@ua.pt>
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 COLOR4D copper_color = m_boardAdapter.m_BoardEditorColors[aLayerID];
277 m_materials.m_Copper.m_Diffuse = SFVEC3F( copper_color.r, copper_color.g,
278 copper_color.b );
279 OglSetMaterial( m_materials.m_Copper, 1.0f );
280 m_materials.m_NonPlatedCopper.m_Diffuse = m_materials.m_Copper.m_Diffuse;
281 OglSetMaterial( m_materials.m_NonPlatedCopper, 1.0f );
282
283 return;
284 }
285
286 switch( aLayerID )
287 {
288 case F_Mask:
289 case B_Mask:
290 {
291 const SFVEC4F layerColor = aLayerID == F_Mask ? m_boardAdapter.m_SolderMaskColorTop
293
294 m_materials.m_SolderMask.m_Diffuse = layerColor;
295
296 // Convert Opacity to Transparency
297 m_materials.m_SolderMask.m_Transparency = 1.0f - layerColor.a;
298
299 m_materials.m_SolderMask.m_Ambient = m_materials.m_SolderMask.m_Diffuse * 0.3f;
300
301 m_materials.m_SolderMask.m_Specular = m_materials.m_SolderMask.m_Diffuse
302 * m_materials.m_SolderMask.m_Diffuse;
303
304 OglSetMaterial( m_materials.m_SolderMask, 1.0f );
305 break;
306 }
307
308 case B_Paste:
309 case F_Paste:
311 OglSetMaterial( m_materials.m_Paste, 1.0f );
312 break;
313
314 case B_SilkS:
315 m_materials.m_SilkSBot.m_Diffuse = m_boardAdapter.m_SilkScreenColorBot;
316 OglSetMaterial( m_materials.m_SilkSBot, 1.0f );
317 break;
318
319 case F_SilkS:
320 m_materials.m_SilkSTop.m_Diffuse = m_boardAdapter.m_SilkScreenColorTop;
321 OglSetMaterial( m_materials.m_SilkSTop, 1.0f );
322 break;
323
324 case B_Adhes:
325 case F_Adhes:
326 case Dwgs_User:
327 case Cmts_User:
328 case Eco1_User:
329 case Eco2_User:
330 case Edge_Cuts:
331 case Margin:
332 case B_CrtYd:
333 case F_CrtYd:
334 case B_Fab:
335 case F_Fab:
336 switch( aLayerID )
337 {
338 case Dwgs_User: m_materials.m_Plastic.m_Diffuse = m_boardAdapter.m_UserDrawingsColor; break;
339 case Cmts_User: m_materials.m_Plastic.m_Diffuse = m_boardAdapter.m_UserCommentsColor; break;
340 case Eco1_User: m_materials.m_Plastic.m_Diffuse = m_boardAdapter.m_ECO1Color; break;
341 case Eco2_User: m_materials.m_Plastic.m_Diffuse = m_boardAdapter.m_ECO2Color; break;
342 case Edge_Cuts: m_materials.m_Plastic.m_Diffuse = m_boardAdapter.m_UserDrawingsColor; break;
343 case Margin: m_materials.m_Plastic.m_Diffuse = m_boardAdapter.m_UserDrawingsColor; break;
344 default:
345 m_materials.m_Plastic.m_Diffuse = m_boardAdapter.GetLayerColor( aLayerID );
346 break;
347 }
348
349 m_materials.m_Plastic.m_Ambient = SFVEC3F( m_materials.m_Plastic.m_Diffuse.r * 0.05f,
350 m_materials.m_Plastic.m_Diffuse.g * 0.05f,
351 m_materials.m_Plastic.m_Diffuse.b * 0.05f );
352
353 m_materials.m_Plastic.m_Specular = SFVEC3F( m_materials.m_Plastic.m_Diffuse.r * 0.7f,
354 m_materials.m_Plastic.m_Diffuse.g * 0.7f,
355 m_materials.m_Plastic.m_Diffuse.b * 0.7f );
356
357 m_materials.m_Plastic.m_Shininess = 0.078125f * 128.0f;
358 m_materials.m_Plastic.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
359 OglSetMaterial( m_materials.m_Plastic, 1.0f );
360 break;
361
362 default:
363 {
364 int layer3D = MapPCBLayerTo3DLayer( aLayerID );
365
366 // Note: MUST do this in LAYER_3D space; User_1..User_45 are NOT contiguous
367 if( layer3D >= LAYER_3D_USER_1 && layer3D <= LAYER_3D_USER_45 )
368 {
369 int user_idx = layer3D - LAYER_3D_USER_1;
370
371 m_materials.m_Plastic.m_Diffuse = m_boardAdapter.m_UserDefinedLayerColor[ user_idx ];
372 m_materials.m_Plastic.m_Ambient = SFVEC3F( m_materials.m_Plastic.m_Diffuse.r * 0.05f,
373 m_materials.m_Plastic.m_Diffuse.g * 0.05f,
374 m_materials.m_Plastic.m_Diffuse.b * 0.05f );
375
376 m_materials.m_Plastic.m_Specular = SFVEC3F( m_materials.m_Plastic.m_Diffuse.r * 0.7f,
377 m_materials.m_Plastic.m_Diffuse.g * 0.7f,
378 m_materials.m_Plastic.m_Diffuse.b * 0.7f );
379
380 m_materials.m_Plastic.m_Shininess = 0.078125f * 128.0f;
381 m_materials.m_Plastic.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
382 OglSetMaterial( m_materials.m_Plastic, 1.0f );
383 break;
384 }
385
386 m_materials.m_Copper.m_Diffuse = m_boardAdapter.m_CopperColor;
387 OglSetMaterial( m_materials.m_Copper, 1.0f );
388 break;
389 }
390 }
391}
392
393
395{
396 // Setup light
397 // https://www.opengl.org/sdk/docs/man2/xhtml/glLight.xml
398 const GLfloat ambient[] = { 0.084f, 0.084f, 0.084f, 1.0f };
399 const GLfloat diffuse0[] = { 0.3f, 0.3f, 0.3f, 1.0f };
400 const GLfloat specular0[] = { 0.5f, 0.5f, 0.5f, 1.0f };
401
402 glLightfv( GL_LIGHT0, GL_AMBIENT, ambient );
403 glLightfv( GL_LIGHT0, GL_DIFFUSE, diffuse0 );
404 glLightfv( GL_LIGHT0, GL_SPECULAR, specular0 );
405
406 const GLfloat diffuse12[] = { 0.7f, 0.7f, 0.7f, 1.0f };
407 const GLfloat specular12[] = { 0.7f, 0.7f, 0.7f, 1.0f };
408
409 // defines a directional light that points along the negative z-axis
410 GLfloat position[4] = { 0.0f, 0.0f, 1.0f, 0.0f };
411
412 // This makes a vector slight not perpendicular with XZ plane
413 const SFVEC3F vectorLight = SphericalToCartesian( glm::pi<float>() * 0.03f,
414 glm::pi<float>() * 0.25f );
415
416 position[0] = vectorLight.x;
417 position[1] = vectorLight.y;
418 position[2] = vectorLight.z;
419
420 glLightfv( GL_LIGHT1, GL_AMBIENT, ambient );
421 glLightfv( GL_LIGHT1, GL_DIFFUSE, diffuse12 );
422 glLightfv( GL_LIGHT1, GL_SPECULAR, specular12 );
423 glLightfv( GL_LIGHT1, GL_POSITION, position );
424
425 // defines a directional light that points along the positive z-axis
426 position[2] = -position[2];
427
428 glLightfv( GL_LIGHT2, GL_AMBIENT, ambient );
429 glLightfv( GL_LIGHT2, GL_DIFFUSE, diffuse12 );
430 glLightfv( GL_LIGHT2, GL_SPECULAR, specular12 );
431 glLightfv( GL_LIGHT2, GL_POSITION, position );
432
433 const GLfloat lmodel_ambient[] = { 0.0f, 0.0f, 0.0f, 1.0f };
434
435 glLightModelfv( GL_LIGHT_MODEL_AMBIENT, lmodel_ambient );
436
437 glLightModeli( GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE );
438}
439
440
442{
443 OglSetMaterial( m_materials.m_NonPlatedCopper, 1.0f );
444}
445
446
448{
449 glEnable( GL_POLYGON_OFFSET_FILL );
450 glPolygonOffset( -0.1f, -2.0f );
451 setLayerMaterial( aLayer_id );
452}
453
454
456{
457 glDisable( GL_POLYGON_OFFSET_FILL );
458}
459
460
461void RENDER_3D_OPENGL::renderBoardBody( bool aSkipRenderHoles )
462{
463 m_materials.m_EpoxyBoard.m_Diffuse = m_boardAdapter.m_BoardBodyColor;
464
465 // opacity to transparency
466 m_materials.m_EpoxyBoard.m_Transparency = 1.0f - m_boardAdapter.m_BoardBodyColor.a;
467
468 OglSetMaterial( m_materials.m_EpoxyBoard, 1.0f );
469
470 OPENGL_RENDER_LIST* ogl_disp_list = nullptr;
471
472 if( aSkipRenderHoles )
473 ogl_disp_list = m_board;
474 else
475 ogl_disp_list = m_boardWithHoles;
476
477 if( ogl_disp_list )
478 {
481
482 ogl_disp_list->SetItIsTransparent( true );
483 ogl_disp_list->DrawAll();
484 }
485}
486
487
488static inline SFVEC4F premultiplyAlpha( const SFVEC4F& aInput )
489{
490 return SFVEC4F( aInput.r * aInput.a, aInput.g * aInput.a, aInput.b * aInput.a, aInput.a );
491}
492
493
494bool RENDER_3D_OPENGL::Redraw( bool aIsMoving, REPORTER* aStatusReporter,
495 REPORTER* aWarningReporter )
496{
497 // Initialize OpenGL
499 {
500 if( !initializeOpenGL() )
501 return false;
502 }
503
505
507 {
508 std::unique_ptr<BUSY_INDICATOR> busy = CreateBusyIndicator();
509
510 if( aStatusReporter )
511 aStatusReporter->Report( _( "Loading..." ) );
512
513 // Careful here!
514 // We are in the middle of rendering and the reload method may show
515 // a dialog box that requires the opengl context for a redraw
516 Pgm().GetGLContextManager()->RunWithoutCtxLock( [this, aStatusReporter, aWarningReporter]()
517 {
518 reload( aStatusReporter, aWarningReporter );
519 } );
520
521 // generate a new 3D grid as the size of the board may had changed
522 m_lastGridType = static_cast<GRID3D_TYPE>( cfg.grid_type );
524 }
525 else
526 {
527 // Check if grid was changed
528 if( cfg.grid_type != m_lastGridType )
529 {
530 // and generate a new one
531 m_lastGridType = static_cast<GRID3D_TYPE>( cfg.grid_type );
533 }
534 }
535
537
538 // Initial setup
539 glDepthFunc( GL_LESS );
540 glEnable( GL_CULL_FACE );
541 glFrontFace( GL_CCW ); // This is the OpenGL default
542 glEnable( GL_NORMALIZE ); // This allow OpenGL to normalize the normals after transformations
543 glViewport( 0, 0, m_windowSize.x, m_windowSize.y );
544
545 if( aIsMoving && cfg.opengl_AA_disableOnMove )
546 glDisable( GL_MULTISAMPLE );
547 else
548 glEnable( GL_MULTISAMPLE );
549
550 // clear color and depth buffers
551 glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
552 glClearDepth( 1.0f );
553 glClearStencil( 0x00 );
554 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
555
557
558 // Draw the background ( rectangle with color gradient)
561
562 glEnable( GL_DEPTH_TEST );
563
564 // Set projection and modelview matrixes
565 glMatrixMode( GL_PROJECTION );
566 glLoadMatrixf( glm::value_ptr( m_camera.GetProjectionMatrix() ) );
567 glMatrixMode( GL_MODELVIEW );
568 glLoadIdentity();
569 glLoadMatrixf( glm::value_ptr( m_camera.GetViewMatrix() ) );
570
571 // Position the headlight
572 setLightFront( true );
573 setLightTop( true );
574 setLightBottom( true );
575
576 glEnable( GL_LIGHTING );
577
578 {
579 const SFVEC3F& cameraPos = m_camera.GetPos();
580
581 // Place the light at a minimum Z so the diffuse factor will not drop
582 // and the board will still look with good light.
583 float zpos;
584
585 if( cameraPos.z > 0.0f )
586 zpos = glm::max( cameraPos.z, 0.5f ) + cameraPos.z * cameraPos.z;
587 else
588 zpos = glm::min( cameraPos.z,-0.5f ) - cameraPos.z * cameraPos.z;
589
590 // This is a point light.
591 const GLfloat headlight_pos[] = { cameraPos.x, cameraPos.y, zpos, 1.0f };
592
593 glLightfv( GL_LIGHT0, GL_POSITION, headlight_pos );
594 }
595
596 bool skipThickness = aIsMoving && cfg.opengl_thickness_disableOnMove;
597 bool skipRenderHoles = aIsMoving && cfg.opengl_holes_disableOnMove;
598 bool skipRenderVias = aIsMoving && cfg.opengl_vias_disableOnMove;
599 bool showThickness = !skipThickness;
600
601 std::bitset<LAYER_3D_END> layerFlags = m_boardAdapter.GetVisibleLayers();
602
604
605 if( !( skipRenderVias || skipRenderHoles ) && m_vias )
606 m_vias->DrawAll();
607
608 if( !skipRenderHoles && m_padHoles )
610
611 // Display copper and tech layers
612 for( MAP_OGL_DISP_LISTS::const_iterator ii = m_layers.begin(); ii != m_layers.end(); ++ii )
613 {
614 const PCB_LAYER_ID layer = ( PCB_LAYER_ID )( ii->first );
615 bool isSilkLayer = layer == F_SilkS || layer == B_SilkS;
616 bool isMaskLayer = layer == F_Mask || layer == B_Mask;
617 bool isPasteLayer = layer == F_Paste || layer == B_Paste;
618
619 // Mask layers are not processed here because they are a special case
620 if( isMaskLayer )
621 continue;
622
623 // Do not show inner layers when it is displaying the board and board body is opaque
624 // enough: the time to create inner layers can be *really significant*.
625 // So avoid creating them is they are not very visible
626 const double opacity_min = 0.8;
627
628 if( layerFlags.test( LAYER_3D_BOARD ) && m_boardAdapter.m_BoardBodyColor.a > opacity_min )
629 {
630 // generating internal copper layers is time consuming. so skip them
631 // if the board body is masking them (i.e. if the opacity is near 1.0)
632 // B_Cu is layer 2 and all inner layers are higher values
633 if( layer > B_Cu && IsCopperLayer( layer ) )
634 continue;
635 }
636
637 glPushMatrix();
638
639 OPENGL_RENDER_LIST* pLayerDispList = static_cast<OPENGL_RENDER_LIST*>( ii->second );
640
641 if( IsCopperLayer( layer ) )
642 {
643 if( cfg.DifferentiatePlatedCopper() )
645 else
646 setLayerMaterial( layer );
647
648 OPENGL_RENDER_LIST* outerTH = nullptr;
649 OPENGL_RENDER_LIST* viaHoles = nullptr;
650
651 if( !skipRenderHoles )
652 {
653 outerTH = m_outerThroughHoles;
654 viaHoles = m_outerLayerHoles[layer];
655 }
656
657 if( m_antiBoard )
658 m_antiBoard->ApplyScalePosition( pLayerDispList );
659
660 if( outerTH )
661 outerTH->ApplyScalePosition( pLayerDispList );
662
663 pLayerDispList->DrawCulled( showThickness, outerTH, viaHoles, m_antiBoard );
664
665 // Draw plated & offboard pads
666 if( layer == F_Cu && ( m_platedPadsFront || m_offboardPadsFront ) )
667 {
669
671 m_platedPadsFront->DrawCulled( showThickness, outerTH, viaHoles, m_antiBoard );
672
674 m_offboardPadsFront->DrawCulled( showThickness, outerTH, viaHoles );
675 }
676 else if( layer == B_Cu && ( m_platedPadsBack || m_offboardPadsBack ) )
677 {
679
680 if( m_platedPadsBack )
681 m_platedPadsBack->DrawCulled( showThickness, outerTH, viaHoles, m_antiBoard );
682
684 m_offboardPadsBack->DrawCulled( showThickness, outerTH, viaHoles );
685 }
686
688 }
689 else if( isPasteLayer && skipRenderHoles )
690 {
691 // Do not render paste layers when skipRenderHoles is enabled or we get z-fight issues
692 }
693 else
694 {
695 setLayerMaterial( layer );
696
697 OPENGL_RENDER_LIST* throughHolesOuter = nullptr;
698 OPENGL_RENDER_LIST* anti_board = nullptr;
699 OPENGL_RENDER_LIST* solder_mask = nullptr;
700
701 if( isSilkLayer && cfg.clip_silk_on_via_annuli )
702 throughHolesOuter = m_outerThroughHoleRings;
703 else
704 throughHolesOuter = m_outerThroughHoles;
705
706 if( isSilkLayer && cfg.show_off_board_silk )
707 anti_board = nullptr;
708 else if( LSET::PhysicalLayersMask().test( layer ) )
709 anti_board = m_antiBoard;
710
711 if( isSilkLayer && cfg.subtract_mask_from_silk && !cfg.show_off_board_silk )
712 solder_mask = m_layers[ ( layer == B_SilkS) ? B_Mask : F_Mask ];
713
714 if( throughHolesOuter )
715 throughHolesOuter->ApplyScalePosition( pLayerDispList );
716
717 if( anti_board )
718 anti_board->ApplyScalePosition( pLayerDispList );
719
720 if( solder_mask )
721 solder_mask->ApplyScalePosition( pLayerDispList );
722
723 pLayerDispList->DrawCulled( showThickness, solder_mask, throughHolesOuter, anti_board );
724 }
725
726 glPopMatrix();
727 }
728
729 glm::mat4 cameraViewMatrix;
730
731 glGetFloatv( GL_MODELVIEW_MATRIX, glm::value_ptr( cameraViewMatrix ) );
732
733 // Render 3D Models (Non-transparent)
734 renderOpaqueModels( cameraViewMatrix );
735
736 // Display board body
737 if( layerFlags.test( LAYER_3D_BOARD ) )
738 renderBoardBody( skipRenderHoles );
739
740 // Display transparent mask layers
741 if( layerFlags.test( LAYER_3D_SOLDERMASK_TOP )
742 || layerFlags.test( LAYER_3D_SOLDERMASK_BOTTOM ) )
743 {
744 // add a depth buffer offset, it will help to hide some artifacts
745 // on silkscreen where the SolderMask is removed
746 glEnable( GL_POLYGON_OFFSET_FILL );
747 glPolygonOffset( 0.0f, -2.0f );
748
749 if( m_camera.GetPos().z > 0 )
750 {
751 if( layerFlags.test( LAYER_3D_SOLDERMASK_BOTTOM ) )
752 {
754 showThickness, skipRenderHoles );
755 }
756
757 if( layerFlags.test( LAYER_3D_SOLDERMASK_TOP ) )
758 {
760 showThickness, skipRenderHoles );
761 }
762 }
763 else
764 {
765 if( layerFlags.test( LAYER_3D_SOLDERMASK_TOP ) )
766 {
768 showThickness, skipRenderHoles );
769 }
770
771 if( layerFlags.test( LAYER_3D_SOLDERMASK_BOTTOM ) )
772 {
774 showThickness, skipRenderHoles );
775 }
776 }
777
778 glDisable( GL_POLYGON_OFFSET_FILL );
779 glPolygonOffset( 0.0f, 0.0f );
780 }
781
782 // Render 3D Models (Transparent)
783 // !TODO: this can be optimized. If there are no transparent models (or no opacity),
784 // then there is no need to make this function call.
785 glDepthMask( GL_FALSE );
786 glEnable( GL_BLEND );
787 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
788
789 // Enables Texture Env so it can combine model transparency with each footprint opacity
790 glEnable( GL_TEXTURE_2D );
791 glActiveTexture( GL_TEXTURE0 );
792
793 // Uses an existent texture so the glTexEnv operations will work.
794 glBindTexture( GL_TEXTURE_2D, m_circleTexture );
795
796 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE );
797 glTexEnvf( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE );
798 glTexEnvf( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE );
799
800 glTexEnvi( GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PRIMARY_COLOR );
801 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR );
802
803 glTexEnvi( GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PREVIOUS );
804 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR );
805
806 glTexEnvi( GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PRIMARY_COLOR );
807 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA );
808 glTexEnvi( GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_CONSTANT );
809 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA );
810
811 renderTransparentModels( cameraViewMatrix );
812
813 glDisable( GL_BLEND );
815
816 glDepthMask( GL_TRUE );
817
818 // Render Grid
819 if( cfg.grid_type != GRID3D_TYPE::NONE )
820 {
821 glDisable( GL_LIGHTING );
822
823 if( glIsList( m_grid ) )
824 glCallList( m_grid );
825
826 glEnable( GL_LIGHTING );
827 }
828
829 // Render 3D arrows
830 if( cfg.show_axis )
832
833 // Return back to the original viewport (this is important if we want
834 // to take a screenshot after the render)
835 glViewport( 0, 0, m_windowSize.x, m_windowSize.y );
836
837 return false;
838}
839
840
842{
843 glEnable( GL_LINE_SMOOTH );
844 glShadeModel( GL_SMOOTH );
845
846 // 4-byte pixel alignment
847 glPixelStorei( GL_UNPACK_ALIGNMENT, 4 );
848
849 // Initialize the open GL texture to draw the filled semi-circle of the segments
851
852 if( !circleImage )
853 return false;
854
855 unsigned int circleRadius = ( SIZE_OF_CIRCLE_TEXTURE / 2 ) - 4;
856
857 circleImage->CircleFilled( ( SIZE_OF_CIRCLE_TEXTURE / 2 ) - 0,
858 ( SIZE_OF_CIRCLE_TEXTURE / 2 ) - 0,
859 circleRadius,
860 0xFF );
861
862 IMAGE* circleImageBlured = new IMAGE( circleImage->GetWidth(), circleImage->GetHeight() );
863
864 circleImageBlured->EfxFilter_SkipCenter( circleImage, IMAGE_FILTER::GAUSSIAN_BLUR, circleRadius - 8 );
865
866 m_circleTexture = OglLoadTexture( *circleImageBlured );
867
868 delete circleImageBlured;
869 circleImageBlured = nullptr;
870
871 delete circleImage;
872 circleImage = nullptr;
873
874 init_lights();
875
876 // Use this mode if you want see the triangle lines (debug proposes)
877 //glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
878 m_canvasInitialized = true;
879
880 return true;
881}
882
883
885{
886 glEnable( GL_COLOR_MATERIAL );
887 glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );
888
889 const SFVEC4F ambient = SFVEC4F( 0.0f, 0.0f, 0.0f, 1.0f );
890 const SFVEC4F diffuse = SFVEC4F( 0.0f, 0.0f, 0.0f, 1.0f );
891 const SFVEC4F emissive = SFVEC4F( 0.0f, 0.0f, 0.0f, 1.0f );
892 const SFVEC4F specular = SFVEC4F( 0.1f, 0.1f, 0.1f, 1.0f );
893
894 glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, &specular.r );
895 glMaterialf( GL_FRONT_AND_BACK, GL_SHININESS, 96.0f );
896
897 glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, &ambient.r );
898 glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, &diffuse.r );
899 glMaterialfv( GL_FRONT_AND_BACK, GL_EMISSION, &emissive.r );
900}
901
902
904{
905#define DELETE_AND_FREE( ptr ) \
906 { \
907 delete ptr; \
908 ptr = nullptr; \
909 } \
910
911#define DELETE_AND_FREE_MAP( map ) \
912 { \
913 for( auto& [ layer, ptr ] : map ) \
914 delete ptr; \
915 \
916 map.clear(); \
917 }
918
919 if( glIsList( m_grid ) )
920 glDeleteLists( m_grid, 1 );
921
922 m_grid = 0;
923
925
930
933
935 delete list;
936
937 m_triangles.clear();
938
940
941 m_3dModelMatrixMap.clear();
942
946
950
953}
954
955
957 bool aShowThickness, bool aSkipRenderHoles )
958{
959 wxASSERT( (aLayerID == B_Mask) || (aLayerID == F_Mask) );
960
961 if( m_board )
962 {
963 OPENGL_RENDER_LIST* solder_mask = m_layers[ aLayerID ];
964 OPENGL_RENDER_LIST* via_holes = aSkipRenderHoles ? nullptr : m_outerThroughHoles;
965
966 if( via_holes )
968
970
971 setLayerMaterial( aLayerID );
973 m_board->DrawCulled( aShowThickness, solder_mask, via_holes );
974 }
975}
976
977
978void RENDER_3D_OPENGL::get3dModelsSelected( std::list<MODELTORENDER> &aDstRenderList, bool aGetTop,
979 bool aGetBot, bool aRenderTransparentOnly,
980 bool aRenderSelectedOnly )
981{
982 wxASSERT( ( aGetTop == true ) || ( aGetBot == true ) );
983
984 if( !m_boardAdapter.GetBoard() )
985 return;
986
988
989 // Go for all footprints
991 {
992 bool highlight = false;
993
995 {
996 if( fp->IsSelected() )
997 highlight = true;
998
1000 highlight = true;
1001
1002 if( aRenderSelectedOnly != highlight )
1003 continue;
1004 }
1005
1006 if( !fp->Models().empty() )
1007 {
1008 if( m_boardAdapter.IsFootprintShown( (FOOTPRINT_ATTR_T) fp->GetAttributes() ) )
1009 {
1010 const bool isFlipped = fp->IsFlipped();
1011
1012 if( aGetTop == !isFlipped || aGetBot == isFlipped )
1013 get3dModelsFromFootprint( aDstRenderList, fp, aRenderTransparentOnly,
1014 highlight );
1015 }
1016 }
1017 }
1018}
1019
1020
1021void RENDER_3D_OPENGL::get3dModelsFromFootprint( std::list<MODELTORENDER> &aDstRenderList,
1022 const FOOTPRINT* aFootprint,
1023 bool aRenderTransparentOnly, bool aIsSelected )
1024{
1025 if( !aFootprint->Models().empty() )
1026 {
1027 const double zpos = m_boardAdapter.GetFootprintZPos( aFootprint->IsFlipped() );
1028
1029 VECTOR2I pos = aFootprint->GetPosition();
1030
1031 glm::mat4 fpMatrix( 1.0f );
1032
1033 fpMatrix = glm::translate( fpMatrix, SFVEC3F( pos.x * m_boardAdapter.BiuTo3dUnits(),
1034 -pos.y * m_boardAdapter.BiuTo3dUnits(),
1035 zpos ) );
1036
1037 if( !aFootprint->GetOrientation().IsZero() )
1038 {
1039 fpMatrix = glm::rotate( fpMatrix, (float) aFootprint->GetOrientation().AsRadians(),
1040 SFVEC3F( 0.0f, 0.0f, 1.0f ) );
1041 }
1042
1043 if( aFootprint->IsFlipped() )
1044 {
1045 fpMatrix = glm::rotate( fpMatrix, glm::pi<float>(), SFVEC3F( 0.0f, 1.0f, 0.0f ) );
1046 fpMatrix = glm::rotate( fpMatrix, glm::pi<float>(), SFVEC3F( 0.0f, 0.0f, 1.0f ) );
1047 }
1048
1049 double modelunit_to_3d_units_factor = m_boardAdapter.BiuTo3dUnits() * UNITS3D_TO_UNITSPCB;
1050
1051 fpMatrix = glm::scale( fpMatrix, SFVEC3F( modelunit_to_3d_units_factor ) );
1052
1053 // Get the list of model files for this model
1054 for( const FP_3DMODEL& sM : aFootprint->Models() )
1055 {
1056 if( !sM.m_Show || sM.m_Filename.empty() )
1057 continue;
1058
1059 // Check if the model is present in our cache map
1060 auto cache_i = m_3dModelMap.find( sM.m_Filename );
1061
1062 if( cache_i == m_3dModelMap.end() )
1063 continue;
1064
1065 if( const MODEL_3D* modelPtr = cache_i->second )
1066 {
1067 bool opaque = sM.m_Opacity >= 1.0;
1068
1069 if( ( !aRenderTransparentOnly && modelPtr->HasOpaqueMeshes() && opaque ) ||
1070 ( aRenderTransparentOnly && ( modelPtr->HasTransparentMeshes() || !opaque ) ) )
1071 {
1072 glm::mat4 modelworldMatrix = fpMatrix;
1073
1074 const SFVEC3F offset = SFVEC3F( sM.m_Offset.x, sM.m_Offset.y, sM.m_Offset.z );
1075 const SFVEC3F rotation = SFVEC3F( sM.m_Rotation.x, sM.m_Rotation.y,
1076 sM.m_Rotation.z );
1077 const SFVEC3F scale = SFVEC3F( sM.m_Scale.x, sM.m_Scale.y, sM.m_Scale.z );
1078
1079 std::vector<float> key = { offset.x, offset.y, offset.z,
1080 rotation.x, rotation.y, rotation.z,
1081 scale.x, scale.y, scale.z };
1082
1083 auto it = m_3dModelMatrixMap.find( key );
1084
1085 if( it != m_3dModelMatrixMap.end() )
1086 {
1087 modelworldMatrix *= it->second;
1088 }
1089 else
1090 {
1091 glm::mat4 mtx( 1.0f );
1092 mtx = glm::translate( mtx, offset );
1093 mtx = glm::rotate( mtx, glm::radians( -rotation.z ), { 0.0f, 0.0f, 1.0f } );
1094 mtx = glm::rotate( mtx, glm::radians( -rotation.y ), { 0.0f, 1.0f, 0.0f } );
1095 mtx = glm::rotate( mtx, glm::radians( -rotation.x ), { 1.0f, 0.0f, 0.0f } );
1096 mtx = glm::scale( mtx, scale );
1097 m_3dModelMatrixMap[ key ] = mtx;
1098
1099 modelworldMatrix *= mtx;
1100 }
1101
1102 aDstRenderList.emplace_back( modelworldMatrix, modelPtr,
1103 aRenderTransparentOnly ? sM.m_Opacity : 1.0f,
1104 aRenderTransparentOnly,
1105 aFootprint->IsSelected() || aIsSelected );
1106 }
1107 }
1108 }
1109 }
1110}
1111
1112
1113void RENDER_3D_OPENGL::renderOpaqueModels( const glm::mat4 &aCameraViewMatrix )
1114{
1116
1117 const SFVEC3F selColor = m_boardAdapter.GetColor( cfg.opengl_selection_color );
1118
1119 glPushMatrix();
1120
1121 std::list<MODELTORENDER> renderList;
1122
1124 {
1125 renderList.clear();
1126
1127 get3dModelsSelected( renderList, true, true, false, true );
1128
1129 if( !renderList.empty() )
1130 {
1131 MODEL_3D::BeginDrawMulti( false );
1132
1133 for( const MODELTORENDER& mtr : renderList )
1134 renderModel( aCameraViewMatrix, mtr, selColor, nullptr );
1135
1137 }
1138 }
1139
1140 renderList.clear();
1141 get3dModelsSelected( renderList, true, true, false, false );
1142
1143 if( !renderList.empty() )
1144 {
1146
1147 for( const MODELTORENDER& mtr : renderList )
1148 renderModel( aCameraViewMatrix, mtr, selColor, nullptr );
1149
1151 }
1152
1153 glPopMatrix();
1154}
1155
1156
1157void RENDER_3D_OPENGL::renderTransparentModels( const glm::mat4 &aCameraViewMatrix )
1158{
1160
1161 const SFVEC3F selColor = m_boardAdapter.GetColor( cfg.opengl_selection_color );
1162
1163 std::list<MODELTORENDER> renderListModels; // do not clear it until this function returns
1164
1166 {
1167 // Get Transparent Selected
1168 get3dModelsSelected( renderListModels, true, true, true, true );
1169 }
1170
1171 // Get Transparent Not Selected
1172 get3dModelsSelected( renderListModels, true, true, true, false );
1173
1174 if( renderListModels.empty() )
1175 return;
1176
1177 std::vector<std::pair<const MODELTORENDER *, float>> transparentModelList;
1178
1179 transparentModelList.reserve( renderListModels.size() );
1180
1181 // Calculate the distance to the camera for each model
1182 const SFVEC3F &cameraPos = m_camera.GetPos();
1183
1184 for( const MODELTORENDER& mtr : renderListModels )
1185 {
1186 const BBOX_3D& bBox = mtr.m_model->GetBBox();
1187 const SFVEC3F& bBoxCenter = bBox.GetCenter();
1188 const SFVEC3F bBoxWorld = mtr.m_modelWorldMat * glm::vec4( bBoxCenter, 1.0f );
1189
1190 const float distanceToCamera = glm::length( cameraPos - bBoxWorld );
1191
1192 transparentModelList.emplace_back( &mtr, distanceToCamera );
1193 }
1194
1195 // Sort from back to front
1196 std::sort( transparentModelList.begin(), transparentModelList.end(),
1197 [&]( std::pair<const MODELTORENDER *, float>& a,
1198 std::pair<const MODELTORENDER *, float>& b )
1199 {
1200 if( a.second != b.second )
1201 return a.second > b.second;
1202
1203 return a.first > b.first; // use pointers as a last resort
1204 } );
1205
1206 // Start rendering calls
1207 glPushMatrix();
1208
1209 bool isUsingColorInformation = !( transparentModelList.begin()->first->m_isSelected &&
1211
1212 MODEL_3D::BeginDrawMulti( isUsingColorInformation );
1213
1214 for( const std::pair<const MODELTORENDER *, float>& mtr : transparentModelList )
1215 {
1217 {
1218 // Toggle between using model color or the select color
1219 if( !isUsingColorInformation && !mtr.first->m_isSelected )
1220 {
1221 isUsingColorInformation = true;
1222
1223 glEnableClientState( GL_COLOR_ARRAY );
1224 glEnableClientState( GL_TEXTURE_COORD_ARRAY );
1225 glEnable( GL_COLOR_MATERIAL );
1226 }
1227 else if( isUsingColorInformation && mtr.first->m_isSelected )
1228 {
1229 isUsingColorInformation = false;
1230
1231 glDisableClientState( GL_COLOR_ARRAY );
1232 glDisableClientState( GL_TEXTURE_COORD_ARRAY );
1233 glDisable( GL_COLOR_MATERIAL );
1234 }
1235 }
1236
1237 // Render model, sort each individuall material group
1238 // by passing cameraPos
1239 renderModel( aCameraViewMatrix, *mtr.first, selColor, &cameraPos );
1240 }
1241
1243
1244 glPopMatrix();
1245}
1246
1247
1248void RENDER_3D_OPENGL::renderModel( const glm::mat4 &aCameraViewMatrix,
1249 const MODELTORENDER &aModelToRender,
1250 const SFVEC3F &aSelColor, const SFVEC3F *aCameraWorldPos )
1251{
1253
1254 const glm::mat4 modelviewMatrix = aCameraViewMatrix * aModelToRender.m_modelWorldMat;
1255
1256 glLoadMatrixf( glm::value_ptr( modelviewMatrix ) );
1257
1258 aModelToRender.m_model->Draw( aModelToRender.m_isTransparent, aModelToRender.m_opacity,
1259 aModelToRender.m_isSelected, aSelColor,
1260 &aModelToRender.m_modelWorldMat, aCameraWorldPos );
1261
1262 if( cfg.show_model_bbox )
1263 {
1264 const bool wasBlendEnabled = glIsEnabled( GL_BLEND );
1265
1266 if( !wasBlendEnabled )
1267 {
1268 glEnable( GL_BLEND );
1269 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
1270 }
1271
1272 glDisable( GL_LIGHTING );
1273
1274 glLineWidth( 1 );
1275 aModelToRender.m_model->DrawBboxes();
1276
1277 glLineWidth( 4 );
1278 aModelToRender.m_model->DrawBbox();
1279
1280 glEnable( GL_LIGHTING );
1281
1282 if( !wasBlendEnabled )
1283 glDisable( GL_BLEND );
1284 }
1285}
1286
1287
1289{
1290 if( glIsList( m_grid ) )
1291 glDeleteLists( m_grid, 1 );
1292
1293 m_grid = 0;
1294
1295 if( aGridType == GRID3D_TYPE::NONE )
1296 return;
1297
1298 m_grid = glGenLists( 1 );
1299
1300 if( !glIsList( m_grid ) )
1301 return;
1302
1303 glNewList( m_grid, GL_COMPILE );
1304
1305 glEnable( GL_BLEND );
1306 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
1307
1308 const double zpos = 0.0;
1309
1310 // Color of grid lines
1311 const SFVEC3F gridColor = m_boardAdapter.GetColor( DARKGRAY );
1312
1313 // Color of grid lines every 5 lines
1314 const SFVEC3F gridColor_marker = m_boardAdapter.GetColor( LIGHTBLUE );
1315 const double scale = m_boardAdapter.BiuTo3dUnits();
1316 const GLfloat transparency = 0.35f;
1317
1318 double griSizeMM = 0.0;
1319
1320 switch( aGridType )
1321 {
1322 case GRID3D_TYPE::GRID_1MM: griSizeMM = 1.0; break;
1323 case GRID3D_TYPE::GRID_2P5MM: griSizeMM = 2.5; break;
1324 case GRID3D_TYPE::GRID_5MM: griSizeMM = 5.0; break;
1325 case GRID3D_TYPE::GRID_10MM: griSizeMM = 10.0; break;
1326
1327 default:
1328 case GRID3D_TYPE::NONE: return;
1329 }
1330
1331 glNormal3f( 0.0, 0.0, 1.0 );
1332
1333 const VECTOR2I brd_size = m_boardAdapter.GetBoardSize();
1334 VECTOR2I brd_center_pos = m_boardAdapter.GetBoardPos();
1335
1336 brd_center_pos.y = -brd_center_pos.y;
1337
1338 const int xsize = std::max( brd_size.x, pcbIUScale.mmToIU( 100 ) ) * 1.2;
1339 const int ysize = std::max( brd_size.y, pcbIUScale.mmToIU( 100 ) ) * 1.2;
1340
1341 // Grid limits, in 3D units
1342 double xmin = ( brd_center_pos.x - xsize / 2 ) * scale;
1343 double xmax = ( brd_center_pos.x + xsize / 2 ) * scale;
1344 double ymin = ( brd_center_pos.y - ysize / 2 ) * scale;
1345 double ymax = ( brd_center_pos.y + ysize / 2 ) * scale;
1346 double zmin = pcbIUScale.mmToIU( -50 ) * scale;
1347 double zmax = pcbIUScale.mmToIU( 100 ) * scale;
1348
1349 // Set rasterised line width (min value = 1)
1350 glLineWidth( 1 );
1351
1352 // Draw horizontal grid centered on 3D origin (center of the board)
1353 for( int ii = 0; ; ii++ )
1354 {
1355 if( (ii % 5) )
1356 glColor4f( gridColor.r, gridColor.g, gridColor.b, transparency );
1357 else
1358 glColor4f( gridColor_marker.r, gridColor_marker.g, gridColor_marker.b,
1359 transparency );
1360
1361 const int delta = KiROUND( ii * griSizeMM * pcbIUScale.IU_PER_MM );
1362
1363 if( delta <= xsize / 2 ) // Draw grid lines parallel to X axis
1364 {
1365 glBegin( GL_LINES );
1366 glVertex3f( (brd_center_pos.x + delta) * scale, -ymin, zpos );
1367 glVertex3f( (brd_center_pos.x + delta) * scale, -ymax, zpos );
1368 glEnd();
1369
1370 if( ii != 0 )
1371 {
1372 glBegin( GL_LINES );
1373 glVertex3f( (brd_center_pos.x - delta) * scale, -ymin, zpos );
1374 glVertex3f( (brd_center_pos.x - delta) * scale, -ymax, zpos );
1375 glEnd();
1376 }
1377 }
1378
1379 if( delta <= ysize / 2 ) // Draw grid lines parallel to Y axis
1380 {
1381 glBegin( GL_LINES );
1382 glVertex3f( xmin, -( brd_center_pos.y + delta ) * scale, zpos );
1383 glVertex3f( xmax, -( brd_center_pos.y + delta ) * scale, zpos );
1384 glEnd();
1385
1386 if( ii != 0 )
1387 {
1388 glBegin( GL_LINES );
1389 glVertex3f( xmin, -( brd_center_pos.y - delta ) * scale, zpos );
1390 glVertex3f( xmax, -( brd_center_pos.y - delta ) * scale, zpos );
1391 glEnd();
1392 }
1393 }
1394
1395 if( ( delta > ysize / 2 ) && ( delta > xsize / 2 ) )
1396 break;
1397 }
1398
1399 // Draw vertical grid on Z axis
1400 glNormal3f( 0.0, -1.0, 0.0 );
1401
1402 // Draw vertical grid lines (parallel to Z axis)
1403 double posy = -brd_center_pos.y * scale;
1404
1405 for( int ii = 0; ; ii++ )
1406 {
1407 if( (ii % 5) )
1408 glColor4f( gridColor.r, gridColor.g, gridColor.b, transparency );
1409 else
1410 glColor4f( gridColor_marker.r, gridColor_marker.g, gridColor_marker.b,
1411 transparency );
1412
1413 const double delta = ii * griSizeMM * pcbIUScale.IU_PER_MM;
1414
1415 glBegin( GL_LINES );
1416 xmax = ( brd_center_pos.x + delta ) * scale;
1417
1418 glVertex3f( xmax, posy, zmin );
1419 glVertex3f( xmax, posy, zmax );
1420 glEnd();
1421
1422 if( ii != 0 )
1423 {
1424 glBegin( GL_LINES );
1425 xmin = ( brd_center_pos.x - delta ) * scale;
1426 glVertex3f( xmin, posy, zmin );
1427 glVertex3f( xmin, posy, zmax );
1428 glEnd();
1429 }
1430
1431 if( delta > xsize / 2.0f )
1432 break;
1433 }
1434
1435 // Draw horizontal grid lines on Z axis (parallel to X axis)
1436 for( int ii = 0; ; ii++ )
1437 {
1438 if( ii % 5 )
1439 glColor4f( gridColor.r, gridColor.g, gridColor.b, transparency );
1440 else
1441 glColor4f( gridColor_marker.r, gridColor_marker.g, gridColor_marker.b, transparency );
1442
1443 const double delta = ii * griSizeMM * pcbIUScale.IU_PER_MM * scale;
1444
1445 if( delta <= zmax )
1446 {
1447 // Draw grid lines on Z axis (positive Z axis coordinates)
1448 glBegin( GL_LINES );
1449 glVertex3f( xmin, posy, delta );
1450 glVertex3f( xmax, posy, delta );
1451 glEnd();
1452 }
1453
1454 if( delta <= -zmin && ( ii != 0 ) )
1455 {
1456 // Draw grid lines on Z axis (negative Z axis coordinates)
1457 glBegin( GL_LINES );
1458 glVertex3f( xmin, posy, -delta );
1459 glVertex3f( xmax, posy, -delta );
1460 glEnd();
1461 }
1462
1463 if( ( delta > zmax ) && ( delta > -zmin ) )
1464 break;
1465 }
1466
1467 glDisable( GL_BLEND );
1468
1469 glEndList();
1470}
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 GetLayerColor(int aLayerId) const
Get the technical color of a layer.
bool GetUseBoardEditorCopperLayerColors() const
SFVEC4F m_SolderPasteColor
in realistic mode: solder paste color
SFVEC4F m_UserDefinedLayerColor[45]
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 m_ECO1Color
SFVEC4F m_UserDrawingsColor
SFVEC4F m_BgColorBot
background bottom color
const FOOTPRINTS & Footprints() const
Definition: board.h:338
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:116
EDA_ANGLE GetOrientation() const
Definition: footprint.h:232
bool IsFlipped() const
Definition: footprint.h:396
std::vector< FP_3DMODEL > & Models()
Definition: footprint.h:225
VECTOR2I GetPosition() const override
Definition: footprint.h:229
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:658
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:73
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:79
static const wxChar * m_logTrace
Trace mask used to enable or disable the trace output of this class.
int MapPCBLayerTo3DLayer(PCB_LAYER_ID aLayer)
Definition: layer_id.cpp:331
@ LAYER_3D_USER_1
Definition: layer_ids.h:554
@ LAYER_3D_SOLDERMASK_TOP
Definition: layer_ids.h:547
@ LAYER_3D_SOLDERMASK_BOTTOM
Definition: layer_ids.h:546
@ LAYER_3D_BOARD
Definition: layer_ids.h:541
@ LAYER_3D_USER_45
Definition: layer_ids.h:598
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition: layer_ids.h:663
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:1071
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