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 (C) 2015-2023 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 <footprint.h>
34#include <3d_math.h>
35#include <glm/geometric.hpp>
36#include <math/util.h> // for KiROUND
37#include <utility>
38#include <vector>
39#include <wx/log.h>
40
41#include <base_units.h>
42
46#define UNITS3D_TO_UNITSPCB ( pcbIUScale.IU_PER_MM )
47
49 CAMERA& aCamera ) :
50 RENDER_3D_BASE( aCanvas, aAdapter, aCamera )
51{
52 wxLogTrace( m_logTrace, wxT( "RENDER_3D_OPENGL::RENDER_3D_OPENGL" ) );
53
54 m_layers.clear();
55 m_outerLayerHoles.clear();
56 m_innerLayerHoles.clear();
57 m_triangles.clear();
58 m_board = nullptr;
59 m_antiBoard = nullptr;
60
61 m_platedPadsFront = nullptr;
62 m_platedPadsBack = nullptr;
63
64 m_outerThroughHoles = nullptr;
66 m_outerViaThroughHoles = nullptr;
67 m_vias = nullptr;
68 m_padHoles = nullptr;
69
71 m_grid = 0;
72 m_lastGridType = GRID3D_TYPE::NONE;
73 m_currentRollOverItem = nullptr;
74 m_boardWithHoles = nullptr;
75
76 m_3dModelMap.clear();
77}
78
79
81{
82 wxLogTrace( m_logTrace, wxT( "RENDER_3D_OPENGL::RENDER_3D_OPENGL" ) );
83
85
86 glDeleteTextures( 1, &m_circleTexture );
87}
88
89
91{
92 return 50; // ms
93}
94
95
96void RENDER_3D_OPENGL::SetCurWindowSize( const wxSize& aSize )
97{
98 if( m_windowSize != aSize )
99 {
100 m_windowSize = aSize;
101 glViewport( 0, 0, m_windowSize.x, m_windowSize.y );
102
103 // Initialize here any screen dependent data here
104 }
105}
106
107
109{
110 if( enabled )
111 glEnable( GL_LIGHT0 );
112 else
113 glDisable( GL_LIGHT0 );
114}
115
116
118{
119 if( enabled )
120 glEnable( GL_LIGHT1 );
121 else
122 glDisable( GL_LIGHT1 );
123}
124
125
127{
128 if( enabled )
129 glEnable( GL_LIGHT2 );
130 else
131 glDisable( GL_LIGHT2 );
132}
133
134
136{
137 const float arrow_size = RANGE_SCALE_3D * 0.30f;
138
139 glDisable( GL_CULL_FACE );
140
141 // YxY squared view port, this is on propose
142 glViewport( 4, 4, m_windowSize.y / 8 , m_windowSize.y / 8 );
143 glClear( GL_DEPTH_BUFFER_BIT );
144
145 glMatrixMode( GL_PROJECTION );
146 glLoadIdentity();
147 gluPerspective( 45.0f, 1.0f, 0.001f, RANGE_SCALE_3D );
148
149 glMatrixMode( GL_MODELVIEW );
150 glLoadIdentity();
151
152 const glm::mat4 TranslationMatrix =
153 glm::translate( glm::mat4( 1.0f ), SFVEC3F( 0.0f, 0.0f, -( arrow_size * 2.75f ) ) );
154
155 const glm::mat4 ViewMatrix = TranslationMatrix * m_camera.GetRotationMatrix();
156
157 glLoadMatrixf( glm::value_ptr( ViewMatrix ) );
158
160
161 glColor3f( 0.9f, 0.0f, 0.0f );
162 DrawRoundArrow( SFVEC3F( 0.0f, 0.0f, 0.0f ), SFVEC3F( arrow_size, 0.0f, 0.0f ), 0.275f );
163
164 glColor3f( 0.0f, 0.9f, 0.0f );
165 DrawRoundArrow( SFVEC3F( 0.0f, 0.0f, 0.0f ), SFVEC3F( 0.0f, arrow_size, 0.0f ), 0.275f );
166
167 glColor3f( 0.0f, 0.0f, 0.9f );
168 DrawRoundArrow( SFVEC3F( 0.0f, 0.0f, 0.0f ), SFVEC3F( 0.0f, 0.0f, arrow_size ), 0.275f );
169
170 glEnable( GL_CULL_FACE );
171}
172
173
175{
176 m_materials = {};
177
178 // http://devernay.free.fr/cours/opengl/materials.html
179
180 // Plated copper
181 // Copper material mixed with the copper color
182 m_materials.m_Copper.m_Ambient = SFVEC3F( m_boardAdapter.m_CopperColor.r * 0.1f,
185
186 m_materials.m_Copper.m_Specular = SFVEC3F( m_boardAdapter.m_CopperColor.r * 0.75f + 0.25f,
187 m_boardAdapter.m_CopperColor.g * 0.75f + 0.25f,
188 m_boardAdapter.m_CopperColor.b * 0.75f + 0.25f );
189
190 // This guess the material type(ex: copper vs gold) to determine the
191 // shininess factor between 0.1 and 0.4
192 float shininessfactor = 0.40f - mapf( fabs( m_boardAdapter.m_CopperColor.r -
194 0.15f, 1.00f,
195 0.00f, 0.30f );
196
197 m_materials.m_Copper.m_Shininess = shininessfactor * 128.0f;
198 m_materials.m_Copper.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
199
200
201 // Non plated copper (raw copper)
202 m_materials.m_NonPlatedCopper.m_Ambient = SFVEC3F( 0.191f, 0.073f, 0.022f );
203 m_materials.m_NonPlatedCopper.m_Diffuse = SFVEC3F( 184.0f / 255.0f, 115.0f / 255.0f,
204 50.0f / 255.0f );
205 m_materials.m_NonPlatedCopper.m_Specular = SFVEC3F( 0.256f, 0.137f, 0.086f );
206 m_materials.m_NonPlatedCopper.m_Shininess = 0.1f * 128.0f;
207 m_materials.m_NonPlatedCopper.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
208
209 // Paste material mixed with paste color
213
214 m_materials.m_Paste.m_Specular = SFVEC3F( m_boardAdapter.m_SolderPasteColor.r *
220
221 m_materials.m_Paste.m_Shininess = 0.1f * 128.0f;
222 m_materials.m_Paste.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
223
224 // Silk screen material mixed with silk screen color
225 m_materials.m_SilkSTop.m_Ambient = SFVEC3F( m_boardAdapter.m_SilkScreenColorTop.r,
228
229 m_materials.m_SilkSTop.m_Specular = SFVEC3F(
233
234 m_materials.m_SilkSTop.m_Shininess = 0.078125f * 128.0f;
235 m_materials.m_SilkSTop.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
236
237 // Silk screen material mixed with silk screen color
238 m_materials.m_SilkSBot.m_Ambient = SFVEC3F( m_boardAdapter.m_SilkScreenColorBot.r,
241
242 m_materials.m_SilkSBot.m_Specular = SFVEC3F(
246
247 m_materials.m_SilkSBot.m_Shininess = 0.078125f * 128.0f;
248 m_materials.m_SilkSBot.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
249
250 m_materials.m_SolderMask.m_Shininess = 0.8f * 128.0f;
251 m_materials.m_SolderMask.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
252
253 // Epoxy material
254 m_materials.m_EpoxyBoard.m_Ambient = SFVEC3F( 117.0f / 255.0f, 97.0f / 255.0f,
255 47.0f / 255.0f );
256
257 m_materials.m_EpoxyBoard.m_Specular = SFVEC3F( 18.0f / 255.0f, 3.0f / 255.0f,
258 20.0f / 255.0f );
259
260 m_materials.m_EpoxyBoard.m_Shininess = 0.1f * 128.0f;
261 m_materials.m_EpoxyBoard.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
262}
263
264
266{
267 switch( aLayerID )
268 {
269 case F_Mask:
270 case B_Mask:
271 {
272 const SFVEC4F layerColor = aLayerID == F_Mask ? m_boardAdapter.m_SolderMaskColorTop
274
275 m_materials.m_SolderMask.m_Diffuse = layerColor;
276
277 // Convert Opacity to Transparency
278 m_materials.m_SolderMask.m_Transparency = 1.0f - layerColor.a;
279
280 m_materials.m_SolderMask.m_Ambient = m_materials.m_SolderMask.m_Diffuse * 0.3f;
281
282 m_materials.m_SolderMask.m_Specular = m_materials.m_SolderMask.m_Diffuse
283 * m_materials.m_SolderMask.m_Diffuse;
284
285 OglSetMaterial( m_materials.m_SolderMask, 1.0f );
286 break;
287 }
288
289 case B_Paste:
290 case F_Paste:
292 OglSetMaterial( m_materials.m_Paste, 1.0f );
293 break;
294
295 case B_SilkS:
296 m_materials.m_SilkSBot.m_Diffuse = m_boardAdapter.m_SilkScreenColorBot;
297 OglSetMaterial( m_materials.m_SilkSBot, 1.0f );
298 break;
299
300 case F_SilkS:
301 m_materials.m_SilkSTop.m_Diffuse = m_boardAdapter.m_SilkScreenColorTop;
302 OglSetMaterial( m_materials.m_SilkSTop, 1.0f );
303 break;
304
305 case B_Adhes:
306 case F_Adhes:
307 case Dwgs_User:
308 case Cmts_User:
309 case Eco1_User:
310 case Eco2_User:
311 case Edge_Cuts:
312 case Margin:
313 case B_CrtYd:
314 case F_CrtYd:
315 case B_Fab:
316 case F_Fab:
317 switch( aLayerID )
318 {
319 case Dwgs_User: m_materials.m_Plastic.m_Diffuse = m_boardAdapter.m_UserDrawingsColor; break;
320 case Cmts_User: m_materials.m_Plastic.m_Diffuse = m_boardAdapter.m_UserCommentsColor; break;
321 case Eco1_User: m_materials.m_Plastic.m_Diffuse = m_boardAdapter.m_ECO1Color; break;
322 case Eco2_User: m_materials.m_Plastic.m_Diffuse = m_boardAdapter.m_ECO2Color; break;
323 case Edge_Cuts: m_materials.m_Plastic.m_Diffuse = m_boardAdapter.m_UserDrawingsColor; break;
324 case Margin: m_materials.m_Plastic.m_Diffuse = m_boardAdapter.m_UserDrawingsColor; break;
325 default:
326 m_materials.m_Plastic.m_Diffuse = m_boardAdapter.GetLayerColor( aLayerID );
327 break;
328 }
329
330 m_materials.m_Plastic.m_Ambient = SFVEC3F( m_materials.m_Plastic.m_Diffuse.r * 0.05f,
331 m_materials.m_Plastic.m_Diffuse.g * 0.05f,
332 m_materials.m_Plastic.m_Diffuse.b * 0.05f );
333
334 m_materials.m_Plastic.m_Specular = SFVEC3F( m_materials.m_Plastic.m_Diffuse.r * 0.7f,
335 m_materials.m_Plastic.m_Diffuse.g * 0.7f,
336 m_materials.m_Plastic.m_Diffuse.b * 0.7f );
337
338 m_materials.m_Plastic.m_Shininess = 0.078125f * 128.0f;
339 m_materials.m_Plastic.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
340 OglSetMaterial( m_materials.m_Plastic, 1.0f );
341 break;
342
343 default:
344 m_materials.m_Copper.m_Diffuse = m_boardAdapter.m_CopperColor;
345 OglSetMaterial( m_materials.m_Copper, 1.0f );
346 break;
347 }
348}
349
350
352{
353 // Setup light
354 // https://www.opengl.org/sdk/docs/man2/xhtml/glLight.xml
355 const GLfloat ambient[] = { 0.084f, 0.084f, 0.084f, 1.0f };
356 const GLfloat diffuse0[] = { 0.3f, 0.3f, 0.3f, 1.0f };
357 const GLfloat specular0[] = { 0.5f, 0.5f, 0.5f, 1.0f };
358
359 glLightfv( GL_LIGHT0, GL_AMBIENT, ambient );
360 glLightfv( GL_LIGHT0, GL_DIFFUSE, diffuse0 );
361 glLightfv( GL_LIGHT0, GL_SPECULAR, specular0 );
362
363 const GLfloat diffuse12[] = { 0.7f, 0.7f, 0.7f, 1.0f };
364 const GLfloat specular12[] = { 0.7f, 0.7f, 0.7f, 1.0f };
365
366 // defines a directional light that points along the negative z-axis
367 GLfloat position[4] = { 0.0f, 0.0f, 1.0f, 0.0f };
368
369 // This makes a vector slight not perpendicular with XZ plane
370 const SFVEC3F vectorLight = SphericalToCartesian( glm::pi<float>() * 0.03f,
371 glm::pi<float>() * 0.25f );
372
373 position[0] = vectorLight.x;
374 position[1] = vectorLight.y;
375 position[2] = vectorLight.z;
376
377 glLightfv( GL_LIGHT1, GL_AMBIENT, ambient );
378 glLightfv( GL_LIGHT1, GL_DIFFUSE, diffuse12 );
379 glLightfv( GL_LIGHT1, GL_SPECULAR, specular12 );
380 glLightfv( GL_LIGHT1, GL_POSITION, position );
381
382 // defines a directional light that points along the positive z-axis
383 position[2] = -position[2];
384
385 glLightfv( GL_LIGHT2, GL_AMBIENT, ambient );
386 glLightfv( GL_LIGHT2, GL_DIFFUSE, diffuse12 );
387 glLightfv( GL_LIGHT2, GL_SPECULAR, specular12 );
388 glLightfv( GL_LIGHT2, GL_POSITION, position );
389
390 const GLfloat lmodel_ambient[] = { 0.0f, 0.0f, 0.0f, 1.0f };
391
392 glLightModelfv( GL_LIGHT_MODEL_AMBIENT, lmodel_ambient );
393
394 glLightModeli( GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE );
395}
396
397
399{
400 OglSetMaterial( m_materials.m_NonPlatedCopper, 1.0f );
401}
402
403
405{
406 glEnable( GL_POLYGON_OFFSET_FILL );
407 glPolygonOffset( -0.1f, -2.0f );
408 setLayerMaterial( aLayer_id );
409}
410
411
413{
414 glDisable( GL_POLYGON_OFFSET_FILL );
415}
416
417
418void RENDER_3D_OPENGL::renderBoardBody( bool aSkipRenderHoles )
419{
420 m_materials.m_EpoxyBoard.m_Diffuse = m_boardAdapter.m_BoardBodyColor;
421
422 // opacity to transparency
423 m_materials.m_EpoxyBoard.m_Transparency = 1.0f - m_boardAdapter.m_BoardBodyColor.a;
424
425 OglSetMaterial( m_materials.m_EpoxyBoard, 1.0f );
426
427 OPENGL_RENDER_LIST* ogl_disp_list = nullptr;
428
429 if( aSkipRenderHoles )
430 ogl_disp_list = m_board;
431 else
432 ogl_disp_list = m_boardWithHoles;
433
434 if( ogl_disp_list )
435 {
438
439 ogl_disp_list->SetItIsTransparent( true );
440
441 ogl_disp_list->DrawAll();
442 }
443}
444
445
446bool RENDER_3D_OPENGL::Redraw( bool aIsMoving, REPORTER* aStatusReporter,
447 REPORTER* aWarningReporter )
448{
449 // Initialize OpenGL
451 {
452 if( !initializeOpenGL() )
453 return false;
454 }
455
457 {
458 std::unique_ptr<BUSY_INDICATOR> busy = CreateBusyIndicator();
459
460 if( aStatusReporter )
461 aStatusReporter->Report( _( "Loading..." ) );
462
463 reload( aStatusReporter, aWarningReporter );
464
465 // generate a new 3D grid as the size of the board may had changed
468 }
469 else
470 {
471 // Check if grid was changed
473 {
474 // and generate a new one
477 }
478 }
479
481
482 // Initial setup
483 glDepthFunc( GL_LESS );
484 glEnable( GL_CULL_FACE );
485 glFrontFace( GL_CCW ); // This is the OpenGL default
486 glEnable( GL_NORMALIZE ); // This allow OpenGL to normalize the normals after transformations
487 glViewport( 0, 0, m_windowSize.x, m_windowSize.y );
488
490 glDisable( GL_MULTISAMPLE );
491 else
492 glEnable( GL_MULTISAMPLE );
493
494 // clear color and depth buffers
495 glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
496 glClearDepth( 1.0f );
497 glClearStencil( 0x00 );
498 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
499
501
502 // Draw the background ( rectangle with color gradient)
505
506 glEnable( GL_DEPTH_TEST );
507
508 // Set projection and modelview matrixes
509 glMatrixMode( GL_PROJECTION );
510 glLoadMatrixf( glm::value_ptr( m_camera.GetProjectionMatrix() ) );
511 glMatrixMode( GL_MODELVIEW );
512 glLoadIdentity();
513 glLoadMatrixf( glm::value_ptr( m_camera.GetViewMatrix() ) );
514
515 // Position the headlight
516 setLightFront( true );
517 setLightTop( true );
518 setLightBottom( true );
519
520 glEnable( GL_LIGHTING );
521
522 {
523 const SFVEC3F& cameraPos = m_camera.GetPos();
524
525 // Place the light at a minimum Z so the diffuse factor will not drop
526 // and the board will still look with good light.
527 float zpos;
528
529 if( cameraPos.z > 0.0f )
530 zpos = glm::max( cameraPos.z, 0.5f ) + cameraPos.z * cameraPos.z;
531 else
532 zpos = glm::min( cameraPos.z,-0.5f ) - cameraPos.z * cameraPos.z;
533
534 // This is a point light.
535 const GLfloat headlight_pos[] = { cameraPos.x, cameraPos.y, zpos, 1.0f };
536
537 glLightfv( GL_LIGHT0, GL_POSITION, headlight_pos );
538 }
539
540 bool skipThickness = aIsMoving && m_boardAdapter.m_Cfg->m_Render.opengl_thickness_disableOnMove;
541 bool skipRenderHoles = aIsMoving && m_boardAdapter.m_Cfg->m_Render.opengl_holes_disableOnMove;
542 bool skipRenderVias = aIsMoving && m_boardAdapter.m_Cfg->m_Render.opengl_vias_disableOnMove;
543
544 bool drawMiddleSegments = !skipThickness;
545
546 std::bitset<LAYER_3D_END> layerFlags = m_boardAdapter.GetVisibleLayers();
547
549
550 if( !( skipRenderVias || skipRenderHoles ) && m_vias )
551 m_vias->DrawAll();
552
553 if( !skipRenderHoles && m_padHoles )
555
556 // Display copper and tech layers
557 for( MAP_OGL_DISP_LISTS::const_iterator ii = m_layers.begin(); ii != m_layers.end(); ++ii )
558 {
559 const PCB_LAYER_ID layer_id = ( PCB_LAYER_ID )( ii->first );
560
561 // Mask layers are not processed here because they are a special case
562 if( ( layer_id == B_Mask ) || ( layer_id == F_Mask ) )
563 continue;
564
565 // Do not show inner layers when it is displaying the board and board body is opaque
566 // enough: the time to create inner layers can be *really significant*.
567 // So avoid creating them is they are not very visible
568 const double opacity_min = 0.8;
569
570 if( layerFlags.test( LAYER_3D_BOARD ) && m_boardAdapter.m_BoardBodyColor.a > opacity_min )
571 {
572 if( ( layer_id > F_Cu ) && ( layer_id < B_Cu ) )
573 continue;
574 }
575
576 glPushMatrix();
577
578 OPENGL_RENDER_LIST* pLayerDispList = static_cast<OPENGL_RENDER_LIST*>( ii->second );
579
580 if( ( layer_id >= F_Cu ) && ( layer_id <= B_Cu ) )
581 {
584 else
585 setLayerMaterial( layer_id );
586
587 if( skipRenderHoles )
588 {
589 pLayerDispList->DrawAllCameraCulled( m_camera.GetPos().z, drawMiddleSegments );
590
591 // Draw plated pads
592 if( layer_id == F_Cu && m_platedPadsFront )
593 {
596 drawMiddleSegments );
597 }
598 else if( layer_id == B_Cu && m_platedPadsBack )
599 {
602 drawMiddleSegments );
603 }
604
606 }
607 else
608 {
610 {
612 pLayerDispList->GetZTop()
613 - pLayerDispList->GetZBot() );
614 }
615
616 if( m_antiBoard )
617 {
618 m_antiBoard->ApplyScalePosition( pLayerDispList->GetZBot(),
619 pLayerDispList->GetZTop()
620 - pLayerDispList->GetZBot() );
621 }
622
623 if( m_outerLayerHoles.find( layer_id ) != m_outerLayerHoles.end() )
624 {
625 const OPENGL_RENDER_LIST* viasHolesLayer = m_outerLayerHoles.at( layer_id );
626
627 wxASSERT( viasHolesLayer != nullptr );
628
629 if( viasHolesLayer != nullptr )
630 {
631 pLayerDispList->DrawAllCameraCulled( drawMiddleSegments,
632 m_outerThroughHoles, viasHolesLayer,
633 m_antiBoard );
634
635 // Draw plated pads
636 if( layer_id == F_Cu && m_platedPadsFront )
637 {
639 m_platedPadsFront->DrawAllCameraCulled( drawMiddleSegments,
641 viasHolesLayer, m_antiBoard );
642 }
643 else if( layer_id == B_Cu && m_platedPadsBack )
644 {
646 m_platedPadsBack->DrawAllCameraCulled( drawMiddleSegments,
648 viasHolesLayer, m_antiBoard );
649 }
650
652 }
653 }
654 else
655 {
656 pLayerDispList->DrawAllCameraCulled( drawMiddleSegments, m_outerThroughHoles,
657 m_antiBoard );
658
659 if( layer_id == F_Cu && m_platedPadsFront )
660 {
662 m_platedPadsFront->DrawAllCameraCulled( drawMiddleSegments,
664 }
665 else if( layer_id == B_Cu && m_platedPadsBack )
666 {
668 m_platedPadsBack->DrawAllCameraCulled( drawMiddleSegments,
670 }
671
673 }
674 }
675 }
676 else
677 {
678 setLayerMaterial( layer_id );
679
680 OPENGL_RENDER_LIST* throughHolesOuter = m_outerThroughHoles;
681
683 && ( layer_id == B_SilkS || layer_id == F_SilkS ) )
684 {
685 throughHolesOuter = m_outerThroughHoleRings;
686 }
687
688 if( throughHolesOuter )
689 {
690 throughHolesOuter->ApplyScalePosition( pLayerDispList->GetZBot(),
691 pLayerDispList->GetZTop()
692 - pLayerDispList->GetZBot() );
693 }
694
695 OPENGL_RENDER_LIST* anti_board = nullptr;
696
697 if( ( layer_id == F_SilkS || layer_id == B_SilkS )
699 {
700 anti_board = nullptr;
701 }
702 else if( LSET::PhysicalLayersMask().test( layer_id ) )
703 {
704 anti_board = m_antiBoard;
705 }
706
707 if( anti_board )
708 {
709 anti_board->ApplyScalePosition( pLayerDispList->GetZBot(),
710 pLayerDispList->GetZTop()
711 - pLayerDispList->GetZBot() );
712 }
713
714 if( skipRenderHoles )
715 {
716 // Do not render Paste layers when skipRenderHoles is enabled
717 // otherwise it will cause z-fight issues
718 if( layer_id != B_Paste && layer_id != F_Paste )
719 pLayerDispList->DrawAllCameraCulled( drawMiddleSegments, anti_board );
720 }
723 && ( ( layer_id == B_SilkS && m_layers.find( B_Mask ) != m_layers.end() )
724 || ( layer_id == F_SilkS && m_layers.find( F_Mask ) != m_layers.end() ) ) )
725 {
726 const PCB_LAYER_ID layerMask_id = (layer_id == B_SilkS) ? B_Mask : F_Mask;
727
728 const OPENGL_RENDER_LIST* pLayerDispListMask = m_layers.at( layerMask_id );
729
730 pLayerDispList->DrawAllCameraCulled( drawMiddleSegments, pLayerDispListMask,
731 throughHolesOuter, anti_board );
732 }
733 else if( throughHolesOuter )
734 {
735 pLayerDispList->DrawAllCameraCulled( drawMiddleSegments, throughHolesOuter,
736 anti_board );
737 }
738 else
739 {
740 pLayerDispList->DrawAllCameraCulled( drawMiddleSegments, anti_board );
741 }
742 }
743
744 glPopMatrix();
745 }
746
747 glm::mat4 cameraViewMatrix;
748
749 glGetFloatv( GL_MODELVIEW_MATRIX, glm::value_ptr( cameraViewMatrix ) );
750
751 // Render 3D Models (Non-transparent)
752 renderOpaqueModels( cameraViewMatrix );
753
754 // Display board body
755 if( layerFlags.test( LAYER_3D_BOARD ) )
756 renderBoardBody( skipRenderHoles );
757
758 // Display transparent mask layers
759 if( layerFlags.test( LAYER_3D_SOLDERMASK_TOP ) || layerFlags.test( LAYER_3D_SOLDERMASK_BOTTOM ) )
760 {
761 // add a depth buffer offset, it will help to hide some artifacts
762 // on silkscreen where the SolderMask is removed
763 glEnable( GL_POLYGON_OFFSET_FILL );
764 glPolygonOffset( 0.0f, -2.0f );
765
766 if( m_camera.GetPos().z > 0 )
767 {
768 if( layerFlags.test( LAYER_3D_SOLDERMASK_BOTTOM ) )
769 {
771 drawMiddleSegments, skipRenderHoles );
772 }
773
774 if( layerFlags.test( LAYER_3D_SOLDERMASK_TOP ) )
775 {
777 drawMiddleSegments, skipRenderHoles );
778 }
779 }
780 else
781 {
782 if( layerFlags.test( LAYER_3D_SOLDERMASK_TOP ) )
783 {
785 drawMiddleSegments, skipRenderHoles );
786 }
787
788 if( layerFlags.test( LAYER_3D_SOLDERMASK_BOTTOM ) )
789 {
791 drawMiddleSegments, skipRenderHoles );
792 }
793 }
794
795 glDisable( GL_POLYGON_OFFSET_FILL );
796 glPolygonOffset( 0.0f, 0.0f );
797 }
798
799 // Render 3D Models (Transparent)
800 // !TODO: this can be optimized. If there are no transparent models (or no opacity),
801 // then there is no need to make this function call.
802 glDepthMask( GL_FALSE );
803 glEnable( GL_BLEND );
804 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
805
806 // Enables Texture Env so it can combine model transparency with each footprint opacity
807 glEnable( GL_TEXTURE_2D );
808 glActiveTexture( GL_TEXTURE0 );
809 glBindTexture( GL_TEXTURE_2D, m_circleTexture ); // Uses an existent texture so the glTexEnv operations will work
810
811 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE );
812 glTexEnvf( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE );
813 glTexEnvf( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE );
814
815 glTexEnvi( GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PRIMARY_COLOR );
816 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR );
817
818 glTexEnvi( GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PREVIOUS );
819 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR );
820
821 glTexEnvi( GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PRIMARY_COLOR );
822 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA );
823 glTexEnvi( GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_CONSTANT );
824 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA );
825
826 renderTransparentModels( cameraViewMatrix );
827
828 glDisable( GL_BLEND );
830
831 glDepthMask( GL_TRUE );
832
833 // Render Grid
834 if( m_boardAdapter.m_Cfg->m_Render.grid_type != GRID3D_TYPE::NONE )
835 {
836 glDisable( GL_LIGHTING );
837
838 if( glIsList( m_grid ) )
839 glCallList( m_grid );
840
841 glEnable( GL_LIGHTING );
842 }
843
844 // Render 3D arrows
847
848 // Return back to the original viewport (this is important if we want
849 // to take a screenshot after the render)
850 glViewport( 0, 0, m_windowSize.x, m_windowSize.y );
851
852 return false;
853}
854
855
857{
858 glEnable( GL_LINE_SMOOTH );
859 glShadeModel( GL_SMOOTH );
860
861 // 4-byte pixel alignment
862 glPixelStorei( GL_UNPACK_ALIGNMENT, 4 );
863
864 // Initialize the open GL texture to draw the filled semi-circle of the segments
866
867 if( !circleImage )
868 return false;
869
870 unsigned int circleRadius = ( SIZE_OF_CIRCLE_TEXTURE / 2 ) - 4;
871
872 circleImage->CircleFilled( ( SIZE_OF_CIRCLE_TEXTURE / 2 ) - 0,
873 ( SIZE_OF_CIRCLE_TEXTURE / 2 ) - 0,
874 circleRadius,
875 0xFF );
876
877 IMAGE* circleImageBlured = new IMAGE( circleImage->GetWidth(), circleImage->GetHeight() );
878
879 circleImageBlured->EfxFilter_SkipCenter( circleImage, IMAGE_FILTER::GAUSSIAN_BLUR, circleRadius - 8 );
880
881 m_circleTexture = OglLoadTexture( *circleImageBlured );
882
883 delete circleImageBlured;
884 circleImageBlured = nullptr;
885
886 delete circleImage;
887 circleImage = nullptr;
888
889 init_lights();
890
891 // Use this mode if you want see the triangle lines (debug proposes)
892 //glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
894
895 return true;
896}
897
898
900{
901 glEnable( GL_COLOR_MATERIAL );
902 glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );
903
904 const SFVEC4F ambient = SFVEC4F( 0.0f, 0.0f, 0.0f, 1.0f );
905 const SFVEC4F diffuse = SFVEC4F( 0.0f, 0.0f, 0.0f, 1.0f );
906 const SFVEC4F emissive = SFVEC4F( 0.0f, 0.0f, 0.0f, 1.0f );
907 const SFVEC4F specular = SFVEC4F( 0.1f, 0.1f, 0.1f, 1.0f );
908
909 glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, &specular.r );
910 glMaterialf( GL_FRONT_AND_BACK, GL_SHININESS, 96.0f );
911
912 glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, &ambient.r );
913 glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, &diffuse.r );
914 glMaterialfv( GL_FRONT_AND_BACK, GL_EMISSION, &emissive.r );
915}
916
917
919{
920 if( glIsList( m_grid ) )
921 glDeleteLists( m_grid, 1 );
922
923 m_grid = 0;
924
925 for( MAP_OGL_DISP_LISTS::const_iterator ii = m_layers.begin(); ii != m_layers.end(); ++ii )
926 {
927 OPENGL_RENDER_LIST* pLayerDispList = static_cast<OPENGL_RENDER_LIST*>( ii->second );
928 delete pLayerDispList;
929 }
930
931 m_layers.clear();
932
933 delete m_platedPadsFront;
934 m_platedPadsFront = nullptr;
935
936 delete m_platedPadsBack;
937 m_platedPadsBack = nullptr;
938
939
940 for( const std::pair<const PCB_LAYER_ID, OPENGL_RENDER_LIST*> entry : m_outerLayerHoles )
941 delete entry.second;
942
943 m_outerLayerHoles.clear();
944
945 for( const std::pair<const PCB_LAYER_ID, OPENGL_RENDER_LIST*> entry : m_innerLayerHoles )
946 delete entry.second;
947
948 m_innerLayerHoles.clear();
949
950 for( LIST_TRIANGLES::const_iterator ii = m_triangles.begin(); ii != m_triangles.end(); ++ii )
951 delete *ii;
952
953 m_triangles.clear();
954
955 for( const std::pair<const wxString, MODEL_3D*>& entry : m_3dModelMap )
956 delete entry.second;
957
958 m_3dModelMap.clear();
959
960 m_3dModelMatrixMap.clear();
961
962 delete m_board;
963 m_board = nullptr;
964
965 delete m_boardWithHoles;
966 m_boardWithHoles = nullptr;
967
968 delete m_antiBoard;
969 m_antiBoard = nullptr;
970
971 delete m_outerThroughHoles;
972 m_outerThroughHoles = nullptr;
973
975 m_outerViaThroughHoles = nullptr;
976
978 m_outerThroughHoleRings = nullptr;
979
980 delete m_vias;
981 m_vias = nullptr;
982
983 delete m_padHoles;
984 m_padHoles = nullptr;
985}
986
987
989 bool aDrawMiddleSegments, bool aSkipRenderHoles )
990{
991 wxASSERT( (aLayerID == B_Mask) || (aLayerID == F_Mask) );
992
993 float nonCopperThickness = m_boardAdapter.GetNonCopperLayerThickness();
994
995 if( m_board )
996 {
997 if( m_layers.find( aLayerID ) != m_layers.end() )
998 {
999 OPENGL_RENDER_LIST* pLayerDispListMask = m_layers.at( aLayerID );
1000
1002 m_outerViaThroughHoles->ApplyScalePosition( aZPosition, nonCopperThickness );
1003
1004 m_board->ApplyScalePosition( aZPosition, nonCopperThickness );
1005
1006 setLayerMaterial( aLayerID );
1007
1008 m_board->SetItIsTransparent( true );
1009
1010 if( aSkipRenderHoles )
1011 {
1012 m_board->DrawAllCameraCulled( m_camera.GetPos().z, aDrawMiddleSegments );
1013 }
1014 else
1015 {
1016 m_board->DrawAllCameraCulled( aDrawMiddleSegments, pLayerDispListMask,
1018 }
1019 }
1020 else
1021 {
1022 // This case there is no layer with mask, so we will render the full board as mask
1024 m_outerViaThroughHoles->ApplyScalePosition( aZPosition, nonCopperThickness );
1025
1026 m_board->ApplyScalePosition( aZPosition, nonCopperThickness );
1027
1028 setLayerMaterial( aLayerID );
1029
1030 m_board->SetItIsTransparent( true );
1031
1032 if( aSkipRenderHoles )
1033 {
1034 m_board->DrawAllCameraCulled( m_camera.GetPos().z, aDrawMiddleSegments );
1035 }
1036 else
1037 {
1038 m_board->DrawAllCameraCulled( aDrawMiddleSegments, m_outerViaThroughHoles );
1039 }
1040 }
1041 }
1042}
1043
1044
1045void RENDER_3D_OPENGL::get3dModelsSelected( std::list<MODELTORENDER> &aDstRenderList,
1046 bool aGetTop, bool aGetBot, bool aRenderTransparentOnly,
1047 bool aRenderSelectedOnly )
1048{
1049 wxASSERT( ( aGetTop == true ) || ( aGetBot == true ) );
1050
1051 if( !m_boardAdapter.GetBoard() )
1052 return;
1053
1054 // Go for all footprints
1056 {
1057 bool highlight = false;
1058
1060 {
1061 if( fp->IsSelected() )
1062 highlight = true;
1063
1065 && fp == m_currentRollOverItem )
1066 {
1067 highlight = true;
1068 }
1069
1070 if( aRenderSelectedOnly != highlight )
1071 continue;
1072 }
1073
1074 if( !fp->Models().empty() )
1075 {
1076 if( m_boardAdapter.IsFootprintShown( (FOOTPRINT_ATTR_T) fp->GetAttributes() ) )
1077 {
1078 const bool isFlipped = fp->IsFlipped();
1079
1080 if( ( aGetTop == !isFlipped ) ||
1081 ( aGetBot == isFlipped ) )
1082 get3dModelsFromFootprint( aDstRenderList, fp, aRenderTransparentOnly, highlight );
1083 }
1084 }
1085 }
1086}
1087
1088
1089void RENDER_3D_OPENGL::get3dModelsFromFootprint( std::list<MODELTORENDER> &aDstRenderList,
1090 const FOOTPRINT* aFootprint, bool aRenderTransparentOnly,
1091 bool aIsSelected )
1092{
1093 if( !aFootprint->Models().empty() )
1094 {
1095 const double zpos = m_boardAdapter.GetFootprintZPos( aFootprint->IsFlipped() );
1096
1097 VECTOR2I pos = aFootprint->GetPosition();
1098
1099 glm::mat4 fpMatrix( 1.0f );
1100
1101 fpMatrix = glm::translate( fpMatrix,
1103 -pos.y * m_boardAdapter.BiuTo3dUnits(),
1104 zpos ) );
1105
1106 if( !aFootprint->GetOrientation().IsZero() )
1107 {
1108 fpMatrix = glm::rotate( fpMatrix,
1109 (float) aFootprint->GetOrientation().AsRadians(),
1110 SFVEC3F( 0.0f, 0.0f, 1.0f ) );
1111 }
1112
1113 if( aFootprint->IsFlipped() )
1114 {
1115 fpMatrix = glm::rotate( fpMatrix, glm::pi<float>(), SFVEC3F( 0.0f, 1.0f, 0.0f ) );
1116 fpMatrix = glm::rotate( fpMatrix, glm::pi<float>(), SFVEC3F( 0.0f, 0.0f, 1.0f ) );
1117 }
1118
1119 const double modelunit_to_3d_units_factor = m_boardAdapter.BiuTo3dUnits() *
1121
1122 fpMatrix = glm::scale( fpMatrix,
1123 SFVEC3F( modelunit_to_3d_units_factor ) );
1124
1125 // Get the list of model files for this model
1126 for( const FP_3DMODEL& sM : aFootprint->Models() )
1127 {
1128 if( !sM.m_Show || sM.m_Filename.empty() )
1129 continue;
1130
1131 // Check if the model is present in our cache map
1132 auto cache_i = m_3dModelMap.find( sM.m_Filename );
1133
1134 if( cache_i == m_3dModelMap.end() )
1135 continue;
1136
1137 if( const MODEL_3D* modelPtr = cache_i->second )
1138 {
1139 bool opaque = sM.m_Opacity >= 1.0;
1140
1141 if( ( !aRenderTransparentOnly && modelPtr->HasOpaqueMeshes() && opaque ) ||
1142 ( aRenderTransparentOnly && ( modelPtr->HasTransparentMeshes() || !opaque ) ) )
1143 {
1144 glm::mat4 modelworldMatrix = fpMatrix;
1145
1146 const SFVEC3F offset = SFVEC3F( sM.m_Offset.x, sM.m_Offset.y, sM.m_Offset.z );
1147 const SFVEC3F rotation = SFVEC3F( sM.m_Rotation.x, sM.m_Rotation.y, sM.m_Rotation.z );
1148 const SFVEC3F scale = SFVEC3F( sM.m_Scale.x, sM.m_Scale.y, sM.m_Scale.z );
1149
1150 std::vector<float> key = { offset.x, offset.y, offset.z,
1151 rotation.x, rotation.y, rotation.z,
1152 scale.x, scale.y, scale.z };
1153
1154 auto it = m_3dModelMatrixMap.find( key );
1155
1156 if( it != m_3dModelMatrixMap.end() )
1157 {
1158 modelworldMatrix *= it->second;
1159 }
1160 else
1161 {
1162 glm::mat4 mtx( 1.0f );
1163 mtx = glm::translate( mtx, offset );
1164 mtx = glm::rotate( mtx, glm::radians( -rotation.z ), { 0.0f, 0.0f, 1.0f } );
1165 mtx = glm::rotate( mtx, glm::radians( -rotation.y ), { 0.0f, 1.0f, 0.0f } );
1166 mtx = glm::rotate( mtx, glm::radians( -rotation.x ), { 1.0f, 0.0f, 0.0f } );
1167 mtx = glm::scale( mtx, scale );
1168 m_3dModelMatrixMap[ key ] = mtx;
1169
1170 modelworldMatrix *= mtx;
1171 }
1172
1173 if( aRenderTransparentOnly )
1174 {
1175 aDstRenderList.emplace_back( modelworldMatrix,
1176 modelPtr,
1177 sM.m_Opacity,
1178 true,
1179 aFootprint->IsSelected() || aIsSelected );
1180 }
1181 else
1182 {
1183 aDstRenderList.emplace_back( modelworldMatrix,
1184 modelPtr,
1185 1.0f,
1186 false,
1187 aFootprint->IsSelected() || aIsSelected );
1188 }
1189 }
1190 }
1191 }
1192 }
1193}
1194
1195
1196void RENDER_3D_OPENGL::renderOpaqueModels( const glm::mat4 &aCameraViewMatrix )
1197{
1199
1200 glPushMatrix();
1201
1202 std::list<MODELTORENDER> renderList;
1203
1205 {
1206 renderList.clear();
1207
1208 get3dModelsSelected( renderList, true, true, false, true );
1209
1210 if( !renderList.empty() )
1211 {
1212 MODEL_3D::BeginDrawMulti( false );
1213
1214 for( const MODELTORENDER& mtr : renderList )
1215 renderModel( aCameraViewMatrix, mtr, selColor, nullptr );
1216
1218 }
1219 }
1220
1221 renderList.clear();
1222 get3dModelsSelected( renderList, true, true, false, false );
1223
1224 if( !renderList.empty() )
1225 {
1227
1228 for( const MODELTORENDER& mtr : renderList )
1229 renderModel( aCameraViewMatrix, mtr, selColor, nullptr );
1230
1232 }
1233
1234 glPopMatrix();
1235}
1236
1237
1238void RENDER_3D_OPENGL::renderTransparentModels( const glm::mat4 &aCameraViewMatrix )
1239{
1241
1242 std::list<MODELTORENDER> renderListModels; // do not clear it until this function returns
1243
1245 {
1246 // Get Transparent Selected
1247 get3dModelsSelected( renderListModels, true, true, true, true );
1248 }
1249
1250 // Get Transparent Not Selected
1251 get3dModelsSelected( renderListModels, true, true, true, false );
1252
1253 if( renderListModels.empty() )
1254 return;
1255
1256 std::vector<std::pair<const MODELTORENDER *, float>> transparentModelList;
1257
1258 transparentModelList.reserve( renderListModels.size() );
1259
1260 // Calculate the distance to the camera for each model
1261 const SFVEC3F &cameraPos = m_camera.GetPos();
1262
1263 for( const MODELTORENDER& mtr : renderListModels )
1264 {
1265 const BBOX_3D& bBox = mtr.m_model->GetBBox();
1266 const SFVEC3F& bBoxCenter = bBox.GetCenter();
1267 const SFVEC3F bBoxWorld = mtr.m_modelWorldMat * glm::vec4( bBoxCenter, 1.0f );
1268
1269 const float distanceToCamera = glm::length( cameraPos - bBoxWorld );
1270
1271 transparentModelList.emplace_back( &mtr, distanceToCamera );
1272 }
1273
1274 // Sort from back to front
1275 std::sort( transparentModelList.begin(), transparentModelList.end(),
1276 [&]( std::pair<const MODELTORENDER *, float>& a,
1277 std::pair<const MODELTORENDER *, float>& b )
1278 {
1279 return a.second > b.second;
1280 } );
1281
1282 // Start rendering calls
1283 glPushMatrix();
1284
1285 bool isUsingColorInformation = !( transparentModelList.begin()->first->m_isSelected &&
1287
1288 MODEL_3D::BeginDrawMulti( isUsingColorInformation );
1289
1290 for( const std::pair<const MODELTORENDER *, float>& mtr : transparentModelList )
1291 {
1293 {
1294 // Toggle between using model color or the select color
1295 if( !isUsingColorInformation && !mtr.first->m_isSelected )
1296 {
1297 isUsingColorInformation = true;
1298
1299 glEnableClientState( GL_COLOR_ARRAY );
1300 glEnableClientState( GL_TEXTURE_COORD_ARRAY );
1301 glEnable( GL_COLOR_MATERIAL );
1302 }
1303 else
1304 {
1305 if( isUsingColorInformation && mtr.first->m_isSelected )
1306 {
1307 isUsingColorInformation = false;
1308
1309 glDisableClientState( GL_COLOR_ARRAY );
1310 glDisableClientState( GL_TEXTURE_COORD_ARRAY );
1311 glDisable( GL_COLOR_MATERIAL );
1312 }
1313 }
1314 }
1315
1316 // Render model, sort each individuall material group
1317 // by passing cameraPos
1318 renderModel( aCameraViewMatrix, *mtr.first, selColor, &cameraPos );
1319 }
1320
1322
1323 glPopMatrix();
1324}
1325
1326
1327void RENDER_3D_OPENGL::renderModel( const glm::mat4 &aCameraViewMatrix,
1328 const MODELTORENDER &aModelToRender,
1329 const SFVEC3F &aSelColor,
1330 const SFVEC3F *aCameraWorldPos )
1331{
1332 const glm::mat4 modelviewMatrix = aCameraViewMatrix * aModelToRender.m_modelWorldMat;
1333
1334 glLoadMatrixf( glm::value_ptr( modelviewMatrix ) );
1335
1336 aModelToRender.m_model->Draw( aModelToRender.m_isTransparent, aModelToRender.m_opacity,
1337 aModelToRender.m_isSelected, aSelColor,
1338 &aModelToRender.m_modelWorldMat, aCameraWorldPos );
1339
1341 {
1342 const bool wasBlendEnabled = glIsEnabled( GL_BLEND );
1343
1344 if( !wasBlendEnabled )
1345 {
1346 glEnable( GL_BLEND );
1347 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
1348 }
1349
1350 glDisable( GL_LIGHTING );
1351
1352 glLineWidth( 1 );
1353 aModelToRender.m_model->DrawBboxes();
1354
1355 glLineWidth( 4 );
1356 aModelToRender.m_model->DrawBbox();
1357
1358 glEnable( GL_LIGHTING );
1359
1360 if( !wasBlendEnabled )
1361 glDisable( GL_BLEND );
1362 }
1363}
1364
1365
1367{
1368 if( glIsList( m_grid ) )
1369 glDeleteLists( m_grid, 1 );
1370
1371 m_grid = 0;
1372
1373 if( aGridType == GRID3D_TYPE::NONE )
1374 return;
1375
1376 m_grid = glGenLists( 1 );
1377
1378 if( !glIsList( m_grid ) )
1379 return;
1380
1381 glNewList( m_grid, GL_COMPILE );
1382
1383 glEnable( GL_BLEND );
1384 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
1385
1386 const double zpos = 0.0;
1387
1388 // Color of grid lines
1389 const SFVEC3F gridColor = m_boardAdapter.GetColor( DARKGRAY );
1390
1391 // Color of grid lines every 5 lines
1392 const SFVEC3F gridColor_marker = m_boardAdapter.GetColor( LIGHTBLUE );
1393 const double scale = m_boardAdapter.BiuTo3dUnits();
1394 const GLfloat transparency = 0.35f;
1395
1396 double griSizeMM = 0.0;
1397
1398 switch( aGridType )
1399 {
1400 case GRID3D_TYPE::GRID_1MM: griSizeMM = 1.0; break;
1401 case GRID3D_TYPE::GRID_2P5MM: griSizeMM = 2.5; break;
1402 case GRID3D_TYPE::GRID_5MM: griSizeMM = 5.0; break;
1403 case GRID3D_TYPE::GRID_10MM: griSizeMM = 10.0; break;
1404
1405 default:
1406 case GRID3D_TYPE::NONE: return;
1407 }
1408
1409 glNormal3f( 0.0, 0.0, 1.0 );
1410
1411 const VECTOR2I brd_size = m_boardAdapter.GetBoardSize();
1412 VECTOR2I brd_center_pos = m_boardAdapter.GetBoardPos();
1413
1414 brd_center_pos.y = -brd_center_pos.y;
1415
1416 const int xsize = std::max( brd_size.x, pcbIUScale.mmToIU( 100 ) ) * 1.2;
1417 const int ysize = std::max( brd_size.y, pcbIUScale.mmToIU( 100 ) ) * 1.2;
1418
1419 // Grid limits, in 3D units
1420 double xmin = ( brd_center_pos.x - xsize / 2 ) * scale;
1421 double xmax = ( brd_center_pos.x + xsize / 2 ) * scale;
1422 double ymin = ( brd_center_pos.y - ysize / 2 ) * scale;
1423 double ymax = ( brd_center_pos.y + ysize / 2 ) * scale;
1424 double zmin = pcbIUScale.mmToIU( -50 ) * scale;
1425 double zmax = pcbIUScale.mmToIU( 100 ) * scale;
1426
1427 // Set rasterised line width (min value = 1)
1428 glLineWidth( 1 );
1429
1430 // Draw horizontal grid centered on 3D origin (center of the board)
1431 for( int ii = 0; ; ii++ )
1432 {
1433 if( (ii % 5) )
1434 glColor4f( gridColor.r, gridColor.g, gridColor.b, transparency );
1435 else
1436 glColor4f( gridColor_marker.r, gridColor_marker.g, gridColor_marker.b,
1437 transparency );
1438
1439 const int delta = KiROUND( ii * griSizeMM * pcbIUScale.IU_PER_MM );
1440
1441 if( delta <= xsize / 2 ) // Draw grid lines parallel to X axis
1442 {
1443 glBegin( GL_LINES );
1444 glVertex3f( (brd_center_pos.x + delta) * scale, -ymin, zpos );
1445 glVertex3f( (brd_center_pos.x + delta) * scale, -ymax, zpos );
1446 glEnd();
1447
1448 if( ii != 0 )
1449 {
1450 glBegin( GL_LINES );
1451 glVertex3f( (brd_center_pos.x - delta) * scale, -ymin, zpos );
1452 glVertex3f( (brd_center_pos.x - delta) * scale, -ymax, zpos );
1453 glEnd();
1454 }
1455 }
1456
1457 if( delta <= ysize / 2 ) // Draw grid lines parallel to Y axis
1458 {
1459 glBegin( GL_LINES );
1460 glVertex3f( xmin, -( brd_center_pos.y + delta ) * scale, zpos );
1461 glVertex3f( xmax, -( brd_center_pos.y + delta ) * scale, zpos );
1462 glEnd();
1463
1464 if( ii != 0 )
1465 {
1466 glBegin( GL_LINES );
1467 glVertex3f( xmin, -( brd_center_pos.y - delta ) * scale, zpos );
1468 glVertex3f( xmax, -( brd_center_pos.y - delta ) * scale, zpos );
1469 glEnd();
1470 }
1471 }
1472
1473 if( ( delta > ysize / 2 ) && ( delta > xsize / 2 ) )
1474 break;
1475 }
1476
1477 // Draw vertical grid on Z axis
1478 glNormal3f( 0.0, -1.0, 0.0 );
1479
1480 // Draw vertical grid lines (parallel to Z axis)
1481 double posy = -brd_center_pos.y * scale;
1482
1483 for( int ii = 0; ; ii++ )
1484 {
1485 if( (ii % 5) )
1486 glColor4f( gridColor.r, gridColor.g, gridColor.b, transparency );
1487 else
1488 glColor4f( gridColor_marker.r, gridColor_marker.g, gridColor_marker.b,
1489 transparency );
1490
1491 const double delta = ii * griSizeMM * pcbIUScale.IU_PER_MM;
1492
1493 glBegin( GL_LINES );
1494 xmax = ( brd_center_pos.x + delta ) * scale;
1495
1496 glVertex3f( xmax, posy, zmin );
1497 glVertex3f( xmax, posy, zmax );
1498 glEnd();
1499
1500 if( ii != 0 )
1501 {
1502 glBegin( GL_LINES );
1503 xmin = ( brd_center_pos.x - delta ) * scale;
1504 glVertex3f( xmin, posy, zmin );
1505 glVertex3f( xmin, posy, zmax );
1506 glEnd();
1507 }
1508
1509 if( delta > xsize / 2.0f )
1510 break;
1511 }
1512
1513 // Draw horizontal grid lines on Z axis (parallel to X axis)
1514 for( int ii = 0; ; ii++ )
1515 {
1516 if( ii % 5 )
1517 glColor4f( gridColor.r, gridColor.g, gridColor.b, transparency );
1518 else
1519 glColor4f( gridColor_marker.r, gridColor_marker.g, gridColor_marker.b, transparency );
1520
1521 const double delta = ii * griSizeMM * pcbIUScale.IU_PER_MM * scale;
1522
1523 if( delta <= zmax )
1524 {
1525 // Draw grid lines on Z axis (positive Z axis coordinates)
1526 glBegin( GL_LINES );
1527 glVertex3f( xmin, posy, delta );
1528 glVertex3f( xmax, posy, delta );
1529 glEnd();
1530 }
1531
1532 if( delta <= -zmin && ( ii != 0 ) )
1533 {
1534 // Draw grid lines on Z axis (negative Z axis coordinates)
1535 glBegin( GL_LINES );
1536 glVertex3f( xmin, posy, -delta );
1537 glVertex3f( xmax, posy, -delta );
1538 glEnd();
1539 }
1540
1541 if( ( delta > zmax ) && ( delta > -zmin ) )
1542 break;
1543 }
1544
1545 glDisable( GL_BLEND );
1546
1547 glEndList();
1548}
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:109
#define RANGE_SCALE_3D
This defines the range that all coord will have to be rendered.
Definition: board_adapter.h:63
Helper class to handle information needed to display 3D board.
Definition: board_adapter.h:70
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
FOOTPRINTS & Footprints()
Definition: board.h:313
A class used to derive camera objects from.
Definition: camera.h:78
glm::mat4 GetRotationMatrix() const
Get the rotation matrix to be applied in a transformation camera.
Definition: camera.cpp:182
const glm::mat4 & GetProjectionMatrix() const
Definition: camera.cpp:414
const SFVEC3F & GetPos() const
Definition: camera.h:110
const glm::mat4 & GetViewMatrix() const
Definition: camera.cpp:450
Implement a canvas based on a wxGLCanvas.
Definition: eda_3d_canvas.h:49
bool IsZero() const
Definition: eda_angle.h:169
double AsRadians() const
Definition: eda_angle.h:153
bool IsSelected() const
Definition: eda_item.h:106
EDA_ANGLE GetOrientation() const
Definition: footprint.h:209
bool IsFlipped() const
Definition: footprint.h:348
std::vector< FP_3DMODEL > & Models()
Definition: footprint.h:202
VECTOR2I GetPosition() const override
Definition: footprint.h:206
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
static LSET PhysicalLayersMask()
Return a mask holding all layers which are physically realized.
Definition: lset.cpp:870
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:404
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:417
static void BeginDrawMulti(bool aUseColorInformation)
Set some basic render states before drawing multiple models.
Definition: 3d_model.cpp:388
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.
float GetZBot() const
float GetZTop() const
void DrawAllCameraCulled(float zCameraPos, bool aDrawMiddle=true) const
Draw all layers if they are visible by the camera if camera position is above the layer.
void ApplyScalePosition(float aZposition, float aZscale)
void SetItIsTransparent(bool aSetTransparent)
void DrawAll(bool aDrawMiddle=true) const
Call to draw all the display lists.
This is a base class to hold data and functions for render targets.
CAMERA & m_camera
Flag if the opengl specific for this render was already initialized.
std::unique_ptr< BUSY_INDICATOR > CreateBusyIndicator() const
Return a created busy indicator, if a factory has been set, else a null pointer.
bool m_reloadRequested
The window size that this camera is working.
bool m_is_opengl_initialized
BOARD_ADAPTER & m_boardAdapter
OPENGL_RENDER_LIST * m_board
OPENGL_RENDER_LIST * m_vias
OPENGL_RENDER_LIST * m_outerThroughHoleRings
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
BOARD_ITEM * m_currentRollOverItem
void renderBoardBody(bool aSkipRenderHoles)
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)
std::map< wxString, MODEL_3D * > m_3dModelMap
OPENGL_RENDER_LIST * m_platedPadsFront
void renderSolderMaskLayer(PCB_LAYER_ID aLayerID, float aZPosition, bool aDrawMiddleSegments, bool aSkipRenderHoles)
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 renderTransparentModels(const glm::mat4 &aCameraViewMatrix)
std::map< std::vector< float >, glm::mat4 > m_3dModelMatrixMap
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:71
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)=0
Report a string with a given severity.
@ LIGHTBLUE
Definition: color4d.h:62
@ DARKGRAY
Definition: color4d.h:46
#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:71
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:451
@ LAYER_3D_SOLDERMASK_BOTTOM
Definition: layer_ids.h:450
@ LAYER_3D_BOARD
Definition: layer_ids.h:445
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
@ F_CrtYd
Definition: layer_ids.h:118
@ B_Adhes
Definition: layer_ids.h:98
@ Edge_Cuts
Definition: layer_ids.h:114
@ Dwgs_User
Definition: layer_ids.h:110
@ F_Paste
Definition: layer_ids.h:102
@ Cmts_User
Definition: layer_ids.h:111
@ F_Adhes
Definition: layer_ids.h:99
@ B_Mask
Definition: layer_ids.h:107
@ B_Cu
Definition: layer_ids.h:96
@ Eco1_User
Definition: layer_ids.h:112
@ F_Mask
Definition: layer_ids.h:108
@ B_Paste
Definition: layer_ids.h:101
@ F_Fab
Definition: layer_ids.h:121
@ Margin
Definition: layer_ids.h:115
@ F_SilkS
Definition: layer_ids.h:105
@ B_CrtYd
Definition: layer_ids.h:117
@ Eco2_User
Definition: layer_ids.h:113
@ B_SilkS
Definition: layer_ids.h:104
@ F_Cu
Definition: layer_ids.h:65
@ B_Fab
Definition: layer_ids.h:120
void OglResetTextureState()
Reset to default state the texture settings.
Definition: ogl_utils.cpp:189
void OglDrawBackground(const SFVEC3F &aTopColor, const SFVEC3F &aBotColor)
Definition: ogl_utils.cpp:160
void OglSetMaterial(const SMATERIAL &aMaterial, float aOpacity, bool aUseSelectedMaterial, SFVEC3F aSelectionColor)
Set OpenGL materials.
Definition: ogl_utils.cpp:119
GLuint OglLoadTexture(const IMAGE &aImage)
Generate a new OpenGL texture.
Definition: ogl_utils.cpp:71
Define generic OpenGL functions that are common to any OpenGL target.
void DrawRoundArrow(SFVEC3F aPosition, SFVEC3F aTargetPos, float aSize)
Draw a round arrow.
void init_lights()
#define SIZE_OF_CIRCLE_TEXTURE
const int scale
Manage a bounding box defined by two SFVEC3F min max points.
Definition: bbox_3d.h:42
SFVEC3F GetCenter() const
Return the center point of the bounding box.
Definition: bbox_3d.cpp:132
const double IU_PER_MM
Definition: base_units.h:77
constexpr int mmToIU(double mm) const
Definition: base_units.h:89
constexpr int delta
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:85
glm::vec3 SFVEC3F
Definition: xv3d_types.h:44
glm::vec4 SFVEC4F
Definition: xv3d_types.h:46