KiCad PCB EDA Suite
Loading...
Searching...
No Matches
render_3d_opengl.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2015-2020 Mario Luzeiro <[email protected]>
5 * Copyright (C) 2023 CERN
6 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, you may find one here:
20 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21 * or you may search the http://www.gnu.org website for the version 2 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
26#include <cstdint>
27#include <gal/opengl/kiglew.h> // Must be included first
28
30#include "render_3d_opengl.h"
31#include "opengl_utils.h"
33#include <board.h>
34#include <footprint.h>
36#include <3d_math.h>
37#include <glm/geometric.hpp>
38#include <lset.h>
39#include <pgm_base.h>
40#include <math/util.h> // for KiROUND
41#include <utility>
42#include <vector>
43#include <wx/log.h>
44
45#include <base_units.h>
46
50#define UNITS3D_TO_UNITSPCB ( pcbIUScale.IU_PER_MM )
51
53 RENDER_3D_BASE( aAdapter, aCamera ),
54 m_canvas( aCanvas )
55{
56 wxLogTrace( m_logTrace, wxT( "RENDER_3D_OPENGL::RENDER_3D_OPENGL" ) );
57
58 m_layers.clear();
59 m_outerLayerHoles.clear();
60 m_innerLayerHoles.clear();
61 m_triangles.clear();
62 m_board = nullptr;
63 m_antiBoard = nullptr;
64
65 m_platedPadsFront = nullptr;
66 m_platedPadsBack = nullptr;
67 m_offboardPadsFront = nullptr;
68 m_offboardPadsBack = nullptr;
69
70 m_outerThroughHoles = nullptr;
72 m_outerViaThroughHoles = nullptr;
73 m_microviaHoles = nullptr;
74 m_padHoles = nullptr;
75 m_viaFrontCover = nullptr;
76 m_viaBackCover = nullptr;
77
79 m_grid = 0;
81 m_currentRollOverItem = nullptr;
82 m_boardWithHoles = nullptr;
83
84 m_3dModelMap.clear();
85
86 m_spheres_gizmo = new SPHERES_GIZMO( 4, 4 );
87}
88
89
91{
92 wxLogTrace( m_logTrace, wxT( "RENDER_3D_OPENGL::RENDER_3D_OPENGL" ) );
93
95
96 glDeleteTextures( 1, &m_circleTexture );
97
98 delete m_spheres_gizmo;
99}
100
101
103{
104 return 50; // ms
105}
106
107
108void RENDER_3D_OPENGL::SetCurWindowSize( const wxSize& aSize )
109{
110 if( m_windowSize != aSize )
111 {
112 int viewport[4];
113 int fbWidth, fbHeight;
114 glGetIntegerv( GL_VIEWPORT, viewport );
115
116 m_windowSize = aSize;
117 glViewport( 0, 0, m_windowSize.x, m_windowSize.y );
119 // Initialize here any screen dependent data here
120 }
121}
122
123
125{
126 if( enabled )
127 glEnable( GL_LIGHT0 );
128 else
129 glDisable( GL_LIGHT0 );
130}
131
132
134{
135 if( enabled )
136 glEnable( GL_LIGHT1 );
137 else
138 glDisable( GL_LIGHT1 );
139}
140
141
143{
144 if( enabled )
145 glEnable( GL_LIGHT2 );
146 else
147 glDisable( GL_LIGHT2 );
148}
149
150
152{
153 m_spheres_gizmo->resetSelectedGizmoSphere();
154}
155
156
161
162
163void RENDER_3D_OPENGL::setGizmoViewport( int x, int y, int width, int height )
164{
165 m_spheres_gizmo->setViewport( x, y, width, height );
166}
167
168
169std::tuple<int, int, int, int> RENDER_3D_OPENGL::getGizmoViewport() const
170{
171 return m_spheres_gizmo->getViewport();
172}
173
174
175void RENDER_3D_OPENGL::handleGizmoMouseInput( int mouseX, int mouseY )
176{
177 m_spheres_gizmo->handleMouseInput( mouseX, mouseY );
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,
190 m_boardAdapter.m_CopperColor.g * 0.1f,
191 m_boardAdapter.m_CopperColor.b * 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 -
200 m_boardAdapter.m_CopperColor.g ),
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
217 m_materials.m_Paste.m_Ambient = SFVEC3F( m_boardAdapter.m_SolderPasteColor.r,
218 m_boardAdapter.m_SolderPasteColor.g,
219 m_boardAdapter.m_SolderPasteColor.b );
220
221 m_materials.m_Paste.m_Specular = SFVEC3F( m_boardAdapter.m_SolderPasteColor.r *
222 m_boardAdapter.m_SolderPasteColor.r,
223 m_boardAdapter.m_SolderPasteColor.g *
224 m_boardAdapter.m_SolderPasteColor.g,
225 m_boardAdapter.m_SolderPasteColor.b *
226 m_boardAdapter.m_SolderPasteColor.b );
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,
233 m_boardAdapter.m_SilkScreenColorTop.g,
234 m_boardAdapter.m_SilkScreenColorTop.b );
235
236 m_materials.m_SilkSTop.m_Specular = SFVEC3F(
237 m_boardAdapter.m_SilkScreenColorTop.r * m_boardAdapter.m_SilkScreenColorTop.r + 0.10f,
238 m_boardAdapter.m_SilkScreenColorTop.g * m_boardAdapter.m_SilkScreenColorTop.g + 0.10f,
239 m_boardAdapter.m_SilkScreenColorTop.b * m_boardAdapter.m_SilkScreenColorTop.b + 0.10f );
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,
246 m_boardAdapter.m_SilkScreenColorBot.g,
247 m_boardAdapter.m_SilkScreenColorBot.b );
248
249 m_materials.m_SilkSBot.m_Specular = SFVEC3F(
250 m_boardAdapter.m_SilkScreenColorBot.r * m_boardAdapter.m_SilkScreenColorBot.r + 0.10f,
251 m_boardAdapter.m_SilkScreenColorBot.g * m_boardAdapter.m_SilkScreenColorBot.g + 0.10f,
252 m_boardAdapter.m_SilkScreenColorBot.b * m_boardAdapter.m_SilkScreenColorBot.b + 0.10f );
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{
274 if( m_boardAdapter.GetUseBoardEditorCopperLayerColors() && IsCopperLayer( aLayerID ) )
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
292 : m_boardAdapter.m_SolderMaskColorBot;
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:
310 m_materials.m_Paste.m_Diffuse = m_boardAdapter.m_SolderPasteColor;
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 {
479 ogl_disp_list->ApplyScalePosition( -m_boardAdapter.GetBoardBodyThickness() / 2.0f,
480 m_boardAdapter.GetBoardBodyThickness() );
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)
560 premultiplyAlpha( m_boardAdapter.m_BgColorBot ) );
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 skipRenderMicroVias = aIsMoving && cfg.opengl_microvias_disableOnMove;
599 bool showThickness = !skipThickness;
600
601 std::bitset<LAYER_3D_END> layerFlags = m_boardAdapter.GetVisibleLayers();
602
604
605 if( !( skipRenderMicroVias || skipRenderHoles ) && m_microviaHoles )
606 m_microviaHoles->DrawAll();
607
608 if( !skipRenderHoles && m_padHoles )
609 m_padHoles->DrawAll();
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
690 {
691 setLayerMaterial( layer );
692
693 OPENGL_RENDER_LIST* throughHolesOuter = nullptr;
694 OPENGL_RENDER_LIST* anti_board = nullptr;
695 OPENGL_RENDER_LIST* solder_mask = nullptr;
696
697 if( !skipRenderHoles )
698 {
699 if( isSilkLayer && cfg.clip_silk_on_via_annuli )
700 throughHolesOuter = m_outerThroughHoleRings;
701 else
702 throughHolesOuter = m_outerThroughHoles;
703 }
704
705 if( isSilkLayer && cfg.show_off_board_silk )
706 anti_board = nullptr;
707 else if( LSET::PhysicalLayersMask().test( layer ) )
708 anti_board = m_antiBoard;
709
710 if( isSilkLayer && cfg.subtract_mask_from_silk && !cfg.show_off_board_silk )
711 solder_mask = m_layers[ ( layer == B_SilkS) ? B_Mask : F_Mask ];
712
713 if( throughHolesOuter )
714 throughHolesOuter->ApplyScalePosition( pLayerDispList );
715
716 if( anti_board )
717 anti_board->ApplyScalePosition( pLayerDispList );
718
719 if( solder_mask )
720 solder_mask->ApplyScalePosition( pLayerDispList );
721
722 pLayerDispList->DrawCulled( showThickness, solder_mask, throughHolesOuter, anti_board );
723 }
724
725 glPopMatrix();
726 }
727
728 glm::mat4 cameraViewMatrix;
729
730 glGetFloatv( GL_MODELVIEW_MATRIX, glm::value_ptr( cameraViewMatrix ) );
731
732 // Render 3D Models (Non-transparent)
733 renderOpaqueModels( cameraViewMatrix );
734
735 // Display board body
736 if( layerFlags.test( LAYER_3D_BOARD ) )
737 renderBoardBody( skipRenderHoles );
738
739 // Display transparent mask layers
740 if( layerFlags.test( LAYER_3D_SOLDERMASK_TOP )
741 || layerFlags.test( LAYER_3D_SOLDERMASK_BOTTOM ) )
742 {
743 // add a depth buffer offset, it will help to hide some artifacts
744 // on silkscreen where the SolderMask is removed
745 glEnable( GL_POLYGON_OFFSET_FILL );
746 glPolygonOffset( 0.0f, -2.0f );
747
748 if( m_camera.GetPos().z > 0 )
749 {
750 if( layerFlags.test( LAYER_3D_SOLDERMASK_BOTTOM ) )
751 {
753 showThickness, skipRenderHoles );
754 }
755
756 if( layerFlags.test( LAYER_3D_SOLDERMASK_TOP ) )
757 {
758 renderSolderMaskLayer( F_Mask, m_boardAdapter.GetLayerBottomZPos( F_Mask ),
759 showThickness, skipRenderHoles );
760 }
761 }
762 else
763 {
764 if( layerFlags.test( LAYER_3D_SOLDERMASK_TOP ) )
765 {
766 renderSolderMaskLayer( F_Mask, m_boardAdapter.GetLayerBottomZPos( F_Mask ),
767 showThickness, skipRenderHoles );
768 }
769
770 if( layerFlags.test( LAYER_3D_SOLDERMASK_BOTTOM ) )
771 {
773 showThickness, skipRenderHoles );
774 }
775 }
776
777 glDisable( GL_POLYGON_OFFSET_FILL );
778 glPolygonOffset( 0.0f, 0.0f );
779 }
780
781 // Render 3D Models (Transparent)
782 // !TODO: this can be optimized. If there are no transparent models (or no opacity),
783 // then there is no need to make this function call.
784 glDepthMask( GL_FALSE );
785 glEnable( GL_BLEND );
786 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
787
788 // Enables Texture Env so it can combine model transparency with each footprint opacity
789 glEnable( GL_TEXTURE_2D );
790 glActiveTexture( GL_TEXTURE0 );
791
792 // Uses an existent texture so the glTexEnv operations will work.
793 glBindTexture( GL_TEXTURE_2D, m_circleTexture );
794
795 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE );
796 glTexEnvf( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE );
797 glTexEnvf( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE );
798
799 glTexEnvi( GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PRIMARY_COLOR );
800 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR );
801
802 glTexEnvi( GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PREVIOUS );
803 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR );
804
805 glTexEnvi( GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PRIMARY_COLOR );
806 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA );
807 glTexEnvi( GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_CONSTANT );
808 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA );
809
810 renderTransparentModels( cameraViewMatrix );
811
812 glDisable( GL_BLEND );
814
815 glDepthMask( GL_TRUE );
816
817 // Render Grid
818 if( cfg.grid_type != GRID3D_TYPE::NONE )
819 {
820 glDisable( GL_LIGHTING );
821
822 if( glIsList( m_grid ) )
823 glCallList( m_grid );
824
825 glEnable( GL_LIGHTING );
826 }
827
828 // Render 3D arrows
829 if( cfg.show_navigator )
830 m_spheres_gizmo->render3dSpheresGizmo( m_camera.GetRotationMatrix() );
831
832 // Return back to the original viewport (this is important if we want
833 // to take a screenshot after the render)
834 glViewport( 0, 0, m_windowSize.x, m_windowSize.y );
835
836 return false;
837}
838
839
841{
842 glEnable( GL_LINE_SMOOTH );
843 glShadeModel( GL_SMOOTH );
844
845 // 4-byte pixel alignment
846 glPixelStorei( GL_UNPACK_ALIGNMENT, 4 );
847
848 // Initialize the open GL texture to draw the filled semi-circle of the segments
850
851 if( !circleImage )
852 return false;
853
854 unsigned int circleRadius = ( SIZE_OF_CIRCLE_TEXTURE / 2 ) - 4;
855
856 circleImage->CircleFilled( ( SIZE_OF_CIRCLE_TEXTURE / 2 ) - 0,
857 ( SIZE_OF_CIRCLE_TEXTURE / 2 ) - 0,
858 circleRadius,
859 0xFF );
860
861 IMAGE* circleImageBlured = new IMAGE( circleImage->GetWidth(), circleImage->GetHeight() );
862
863 circleImageBlured->EfxFilter_SkipCenter( circleImage, IMAGE_FILTER::GAUSSIAN_BLUR, circleRadius - 8 );
864
865 m_circleTexture = OglLoadTexture( *circleImageBlured );
866
867 delete circleImageBlured;
868 circleImageBlured = nullptr;
869
870 delete circleImage;
871 circleImage = nullptr;
872
873 init_lights();
874
875 // Use this mode if you want see the triangle lines (debug proposes)
876 //glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
877 m_canvasInitialized = true;
878
879 return true;
880}
881
882
884{
885 glEnable( GL_COLOR_MATERIAL );
886 glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );
887
888 const SFVEC4F ambient = SFVEC4F( 0.0f, 0.0f, 0.0f, 1.0f );
889 const SFVEC4F diffuse = SFVEC4F( 0.0f, 0.0f, 0.0f, 1.0f );
890 const SFVEC4F emissive = SFVEC4F( 0.0f, 0.0f, 0.0f, 1.0f );
891 const SFVEC4F specular = SFVEC4F( 0.1f, 0.1f, 0.1f, 1.0f );
892
893 glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, &specular.r );
894 glMaterialf( GL_FRONT_AND_BACK, GL_SHININESS, 96.0f );
895
896 glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, &ambient.r );
897 glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, &diffuse.r );
898 glMaterialfv( GL_FRONT_AND_BACK, GL_EMISSION, &emissive.r );
899}
900
901
903{
904#define DELETE_AND_FREE( ptr ) \
905 { \
906 delete ptr; \
907 ptr = nullptr; \
908 } \
909
910#define DELETE_AND_FREE_MAP( map ) \
911 { \
912 for( auto& [ layer, ptr ] : map ) \
913 delete ptr; \
914 \
915 map.clear(); \
916 }
917
918 if( glIsList( m_grid ) )
919 glDeleteLists( m_grid, 1 );
920
921 m_grid = 0;
922
924
929
932
934 delete list;
935
936 m_triangles.clear();
937
939
940 m_3dModelMatrixMap.clear();
941
945
949
954}
955
956
958 bool aShowThickness, bool aSkipRenderHoles )
959{
960 wxASSERT( (aLayerID == B_Mask) || (aLayerID == F_Mask) );
961
962 if( m_board )
963 {
964 OPENGL_RENDER_LIST* solder_mask = m_layers[ aLayerID ];
965 OPENGL_RENDER_LIST* via_holes = aSkipRenderHoles ? nullptr : m_outerThroughHoles;
966
967 if( via_holes )
968 via_holes->ApplyScalePosition( aZPos, m_boardAdapter.GetNonCopperLayerThickness() );
969
970 m_board->ApplyScalePosition( aZPos, m_boardAdapter.GetNonCopperLayerThickness() );
971
972 setLayerMaterial( aLayerID );
973 m_board->SetItIsTransparent( true );
974 m_board->DrawCulled( aShowThickness, solder_mask, via_holes );
975
976 if( aLayerID == F_Mask && m_viaFrontCover )
977 {
978 m_viaFrontCover->ApplyScalePosition( aZPos, 4 * m_boardAdapter.GetNonCopperLayerThickness() );
979 m_viaFrontCover->DrawTop();
980 }
981 else if( aLayerID == B_Mask && m_viaBackCover )
982 {
983 m_viaBackCover->ApplyScalePosition( aZPos, 4 * m_boardAdapter.GetNonCopperLayerThickness() );
984 m_viaBackCover->DrawBot();
985 }
986 }
987}
988
989
990void RENDER_3D_OPENGL::get3dModelsSelected( std::list<MODELTORENDER> &aDstRenderList, bool aGetTop,
991 bool aGetBot, bool aRenderTransparentOnly,
992 bool aRenderSelectedOnly )
993{
994 wxASSERT( ( aGetTop == true ) || ( aGetBot == true ) );
995
996 if( !m_boardAdapter.GetBoard() )
997 return;
998
1000
1001 // Go for all footprints
1002 for( FOOTPRINT* fp : m_boardAdapter.GetBoard()->Footprints() )
1003 {
1004 bool highlight = false;
1005
1006 if( m_boardAdapter.m_IsBoardView )
1007 {
1008 if( fp->IsSelected() )
1009 highlight = true;
1010
1012 highlight = true;
1013
1014 if( aRenderSelectedOnly != highlight )
1015 continue;
1016 }
1017
1018 if( !fp->Models().empty() )
1019 {
1020 if( m_boardAdapter.IsFootprintShown( (FOOTPRINT_ATTR_T) fp->GetAttributes() ) )
1021 {
1022 const bool isFlipped = fp->IsFlipped();
1023
1024 if( aGetTop == !isFlipped || aGetBot == isFlipped )
1025 get3dModelsFromFootprint( aDstRenderList, fp, aRenderTransparentOnly,
1026 highlight );
1027 }
1028 }
1029 }
1030}
1031
1032
1033void RENDER_3D_OPENGL::get3dModelsFromFootprint( std::list<MODELTORENDER> &aDstRenderList,
1034 const FOOTPRINT* aFootprint,
1035 bool aRenderTransparentOnly, bool aIsSelected )
1036{
1037 if( !aFootprint->Models().empty() )
1038 {
1039 const double zpos = m_boardAdapter.GetFootprintZPos( aFootprint->IsFlipped() );
1040
1041 VECTOR2I pos = aFootprint->GetPosition();
1042
1043 glm::mat4 fpMatrix( 1.0f );
1044
1045 fpMatrix = glm::translate( fpMatrix, SFVEC3F( pos.x * m_boardAdapter.BiuTo3dUnits(),
1046 -pos.y * m_boardAdapter.BiuTo3dUnits(),
1047 zpos ) );
1048
1049 if( !aFootprint->GetOrientation().IsZero() )
1050 {
1051 fpMatrix = glm::rotate( fpMatrix, (float) aFootprint->GetOrientation().AsRadians(),
1052 SFVEC3F( 0.0f, 0.0f, 1.0f ) );
1053 }
1054
1055 if( aFootprint->IsFlipped() )
1056 {
1057 fpMatrix = glm::rotate( fpMatrix, glm::pi<float>(), SFVEC3F( 0.0f, 1.0f, 0.0f ) );
1058 fpMatrix = glm::rotate( fpMatrix, glm::pi<float>(), SFVEC3F( 0.0f, 0.0f, 1.0f ) );
1059 }
1060
1061 double modelunit_to_3d_units_factor = m_boardAdapter.BiuTo3dUnits() * UNITS3D_TO_UNITSPCB;
1062
1063 fpMatrix = glm::scale( fpMatrix, SFVEC3F( modelunit_to_3d_units_factor ) );
1064
1065 // Get the list of model files for this model
1066 for( const FP_3DMODEL& sM : aFootprint->Models() )
1067 {
1068 if( !sM.m_Show || sM.m_Filename.empty() )
1069 continue;
1070
1071 // Check if the model is present in our cache map
1072 auto cache_i = m_3dModelMap.find( sM.m_Filename );
1073
1074 if( cache_i == m_3dModelMap.end() )
1075 continue;
1076
1077 if( const MODEL_3D* modelPtr = cache_i->second )
1078 {
1079 bool opaque = sM.m_Opacity >= 1.0;
1080
1081 if( ( !aRenderTransparentOnly && modelPtr->HasOpaqueMeshes() && opaque ) ||
1082 ( aRenderTransparentOnly && ( modelPtr->HasTransparentMeshes() || !opaque ) ) )
1083 {
1084 glm::mat4 modelworldMatrix = fpMatrix;
1085
1086 const SFVEC3F offset = SFVEC3F( sM.m_Offset.x, sM.m_Offset.y, sM.m_Offset.z );
1087 const SFVEC3F rotation = SFVEC3F( sM.m_Rotation.x, sM.m_Rotation.y,
1088 sM.m_Rotation.z );
1089 const SFVEC3F scale = SFVEC3F( sM.m_Scale.x, sM.m_Scale.y, sM.m_Scale.z );
1090
1091 std::vector<float> key = { offset.x, offset.y, offset.z,
1092 rotation.x, rotation.y, rotation.z,
1093 scale.x, scale.y, scale.z };
1094
1095 auto it = m_3dModelMatrixMap.find( key );
1096
1097 if( it != m_3dModelMatrixMap.end() )
1098 {
1099 modelworldMatrix *= it->second;
1100 }
1101 else
1102 {
1103 glm::mat4 mtx( 1.0f );
1104 mtx = glm::translate( mtx, offset );
1105 mtx = glm::rotate( mtx, glm::radians( -rotation.z ), { 0.0f, 0.0f, 1.0f } );
1106 mtx = glm::rotate( mtx, glm::radians( -rotation.y ), { 0.0f, 1.0f, 0.0f } );
1107 mtx = glm::rotate( mtx, glm::radians( -rotation.x ), { 1.0f, 0.0f, 0.0f } );
1108 mtx = glm::scale( mtx, scale );
1109 m_3dModelMatrixMap[ key ] = mtx;
1110
1111 modelworldMatrix *= mtx;
1112 }
1113
1114 aDstRenderList.emplace_back( modelworldMatrix, modelPtr,
1115 aRenderTransparentOnly ? sM.m_Opacity : 1.0f,
1116 aRenderTransparentOnly,
1117 aFootprint->IsSelected() || aIsSelected );
1118 }
1119 }
1120 }
1121 }
1122}
1123
1124
1125void RENDER_3D_OPENGL::renderOpaqueModels( const glm::mat4 &aCameraViewMatrix )
1126{
1128
1129 const SFVEC3F selColor = m_boardAdapter.GetColor( cfg.opengl_selection_color );
1130
1131 glPushMatrix();
1132
1133 std::list<MODELTORENDER> renderList;
1134
1135 if( m_boardAdapter.m_IsBoardView )
1136 {
1137 renderList.clear();
1138
1139 get3dModelsSelected( renderList, true, true, false, true );
1140
1141 if( !renderList.empty() )
1142 {
1143 MODEL_3D::BeginDrawMulti( false );
1144
1145 for( const MODELTORENDER& mtr : renderList )
1146 renderModel( aCameraViewMatrix, mtr, selColor, nullptr );
1147
1149 }
1150 }
1151
1152 renderList.clear();
1153 get3dModelsSelected( renderList, true, true, false, false );
1154
1155 if( !renderList.empty() )
1156 {
1158
1159 for( const MODELTORENDER& mtr : renderList )
1160 renderModel( aCameraViewMatrix, mtr, selColor, nullptr );
1161
1163 }
1164
1165 glPopMatrix();
1166}
1167
1168
1169void RENDER_3D_OPENGL::renderTransparentModels( const glm::mat4 &aCameraViewMatrix )
1170{
1172
1173 const SFVEC3F selColor = m_boardAdapter.GetColor( cfg.opengl_selection_color );
1174
1175 std::list<MODELTORENDER> renderListModels; // do not clear it until this function returns
1176
1177 if( m_boardAdapter.m_IsBoardView )
1178 {
1179 // Get Transparent Selected
1180 get3dModelsSelected( renderListModels, true, true, true, true );
1181 }
1182
1183 // Get Transparent Not Selected
1184 get3dModelsSelected( renderListModels, true, true, true, false );
1185
1186 if( renderListModels.empty() )
1187 return;
1188
1189 std::vector<std::pair<const MODELTORENDER *, float>> transparentModelList;
1190
1191 transparentModelList.reserve( renderListModels.size() );
1192
1193 // Calculate the distance to the camera for each model
1194 const SFVEC3F &cameraPos = m_camera.GetPos();
1195
1196 for( const MODELTORENDER& mtr : renderListModels )
1197 {
1198 const BBOX_3D& bBox = mtr.m_model->GetBBox();
1199 const SFVEC3F& bBoxCenter = bBox.GetCenter();
1200 const SFVEC3F bBoxWorld = mtr.m_modelWorldMat * glm::vec4( bBoxCenter, 1.0f );
1201
1202 const float distanceToCamera = glm::length( cameraPos - bBoxWorld );
1203
1204 transparentModelList.emplace_back( &mtr, distanceToCamera );
1205 }
1206
1207 // Sort from back to front
1208 std::sort( transparentModelList.begin(), transparentModelList.end(),
1209 [&]( std::pair<const MODELTORENDER *, float>& a,
1210 std::pair<const MODELTORENDER *, float>& b )
1211 {
1212 if( a.second != b.second )
1213 return a.second > b.second;
1214
1215 return a.first > b.first; // use pointers as a last resort
1216 } );
1217
1218 // Start rendering calls
1219 glPushMatrix();
1220
1221 bool isUsingColorInformation = !( transparentModelList.begin()->first->m_isSelected &&
1222 m_boardAdapter.m_IsBoardView );
1223
1224 MODEL_3D::BeginDrawMulti( isUsingColorInformation );
1225
1226 for( const std::pair<const MODELTORENDER *, float>& mtr : transparentModelList )
1227 {
1228 if( m_boardAdapter.m_IsBoardView )
1229 {
1230 // Toggle between using model color or the select color
1231 if( !isUsingColorInformation && !mtr.first->m_isSelected )
1232 {
1233 isUsingColorInformation = true;
1234
1235 glEnableClientState( GL_COLOR_ARRAY );
1236 glEnableClientState( GL_TEXTURE_COORD_ARRAY );
1237 glEnable( GL_COLOR_MATERIAL );
1238 }
1239 else if( isUsingColorInformation && mtr.first->m_isSelected )
1240 {
1241 isUsingColorInformation = false;
1242
1243 glDisableClientState( GL_COLOR_ARRAY );
1244 glDisableClientState( GL_TEXTURE_COORD_ARRAY );
1245 glDisable( GL_COLOR_MATERIAL );
1246 }
1247 }
1248
1249 // Render model, sort each individuall material group
1250 // by passing cameraPos
1251 renderModel( aCameraViewMatrix, *mtr.first, selColor, &cameraPos );
1252 }
1253
1255
1256 glPopMatrix();
1257}
1258
1259
1260void RENDER_3D_OPENGL::renderModel( const glm::mat4 &aCameraViewMatrix,
1261 const MODELTORENDER &aModelToRender,
1262 const SFVEC3F &aSelColor, const SFVEC3F *aCameraWorldPos )
1263{
1265
1266 const glm::mat4 modelviewMatrix = aCameraViewMatrix * aModelToRender.m_modelWorldMat;
1267
1268 glLoadMatrixf( glm::value_ptr( modelviewMatrix ) );
1269
1270 aModelToRender.m_model->Draw( aModelToRender.m_isTransparent, aModelToRender.m_opacity,
1271 aModelToRender.m_isSelected, aSelColor,
1272 &aModelToRender.m_modelWorldMat, aCameraWorldPos );
1273
1274 if( cfg.show_model_bbox )
1275 {
1276 const bool wasBlendEnabled = glIsEnabled( GL_BLEND );
1277
1278 if( !wasBlendEnabled )
1279 {
1280 glEnable( GL_BLEND );
1281 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
1282 }
1283
1284 glDisable( GL_LIGHTING );
1285
1286 glLineWidth( 1 );
1287 aModelToRender.m_model->DrawBboxes();
1288
1289 glLineWidth( 4 );
1290 aModelToRender.m_model->DrawBbox();
1291
1292 glEnable( GL_LIGHTING );
1293
1294 if( !wasBlendEnabled )
1295 glDisable( GL_BLEND );
1296 }
1297}
1298
1299
1301{
1302 if( glIsList( m_grid ) )
1303 glDeleteLists( m_grid, 1 );
1304
1305 m_grid = 0;
1306
1307 if( aGridType == GRID3D_TYPE::NONE )
1308 return;
1309
1310 m_grid = glGenLists( 1 );
1311
1312 if( !glIsList( m_grid ) )
1313 return;
1314
1315 glNewList( m_grid, GL_COMPILE );
1316
1317 glEnable( GL_BLEND );
1318 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
1319
1320 const double zpos = 0.0;
1321
1322 // Color of grid lines
1323 const SFVEC3F gridColor = m_boardAdapter.GetColor( DARKGRAY );
1324
1325 // Color of grid lines every 5 lines
1326 const SFVEC3F gridColor_marker = m_boardAdapter.GetColor( LIGHTBLUE );
1327 const double scale = m_boardAdapter.BiuTo3dUnits();
1328 const GLfloat transparency = 0.35f;
1329
1330 double griSizeMM = 0.0;
1331
1332 switch( aGridType )
1333 {
1334 case GRID3D_TYPE::GRID_1MM: griSizeMM = 1.0; break;
1335 case GRID3D_TYPE::GRID_2P5MM: griSizeMM = 2.5; break;
1336 case GRID3D_TYPE::GRID_5MM: griSizeMM = 5.0; break;
1337 case GRID3D_TYPE::GRID_10MM: griSizeMM = 10.0; break;
1338
1339 default:
1340 case GRID3D_TYPE::NONE: return;
1341 }
1342
1343 glNormal3f( 0.0, 0.0, 1.0 );
1344
1345 const VECTOR2I brd_size = m_boardAdapter.GetBoardSize();
1346 VECTOR2I brd_center_pos = m_boardAdapter.GetBoardPos();
1347
1348 brd_center_pos.y = -brd_center_pos.y;
1349
1350 const int xsize = std::max( brd_size.x, pcbIUScale.mmToIU( 100 ) ) * 1.2;
1351 const int ysize = std::max( brd_size.y, pcbIUScale.mmToIU( 100 ) ) * 1.2;
1352
1353 // Grid limits, in 3D units
1354 double xmin = ( brd_center_pos.x - xsize / 2 ) * scale;
1355 double xmax = ( brd_center_pos.x + xsize / 2 ) * scale;
1356 double ymin = ( brd_center_pos.y - ysize / 2 ) * scale;
1357 double ymax = ( brd_center_pos.y + ysize / 2 ) * scale;
1358 double zmin = pcbIUScale.mmToIU( -50 ) * scale;
1359 double zmax = pcbIUScale.mmToIU( 100 ) * scale;
1360
1361 // Set rasterised line width (min value = 1)
1362 glLineWidth( 1 );
1363
1364 // Draw horizontal grid centered on 3D origin (center of the board)
1365 for( int ii = 0; ; ii++ )
1366 {
1367 if( (ii % 5) )
1368 glColor4f( gridColor.r, gridColor.g, gridColor.b, transparency );
1369 else
1370 glColor4f( gridColor_marker.r, gridColor_marker.g, gridColor_marker.b,
1371 transparency );
1372
1373 const int delta = KiROUND( ii * griSizeMM * pcbIUScale.IU_PER_MM );
1374
1375 if( delta <= xsize / 2 ) // Draw grid lines parallel to X axis
1376 {
1377 glBegin( GL_LINES );
1378 glVertex3f( (brd_center_pos.x + delta) * scale, -ymin, zpos );
1379 glVertex3f( (brd_center_pos.x + delta) * scale, -ymax, zpos );
1380 glEnd();
1381
1382 if( ii != 0 )
1383 {
1384 glBegin( GL_LINES );
1385 glVertex3f( (brd_center_pos.x - delta) * scale, -ymin, zpos );
1386 glVertex3f( (brd_center_pos.x - delta) * scale, -ymax, zpos );
1387 glEnd();
1388 }
1389 }
1390
1391 if( delta <= ysize / 2 ) // Draw grid lines parallel to Y axis
1392 {
1393 glBegin( GL_LINES );
1394 glVertex3f( xmin, -( brd_center_pos.y + delta ) * scale, zpos );
1395 glVertex3f( xmax, -( brd_center_pos.y + delta ) * scale, zpos );
1396 glEnd();
1397
1398 if( ii != 0 )
1399 {
1400 glBegin( GL_LINES );
1401 glVertex3f( xmin, -( brd_center_pos.y - delta ) * scale, zpos );
1402 glVertex3f( xmax, -( brd_center_pos.y - delta ) * scale, zpos );
1403 glEnd();
1404 }
1405 }
1406
1407 if( ( delta > ysize / 2 ) && ( delta > xsize / 2 ) )
1408 break;
1409 }
1410
1411 // Draw vertical grid on Z axis
1412 glNormal3f( 0.0, -1.0, 0.0 );
1413
1414 // Draw vertical grid lines (parallel to Z axis)
1415 double posy = -brd_center_pos.y * scale;
1416
1417 for( int ii = 0; ; ii++ )
1418 {
1419 if( (ii % 5) )
1420 glColor4f( gridColor.r, gridColor.g, gridColor.b, transparency );
1421 else
1422 glColor4f( gridColor_marker.r, gridColor_marker.g, gridColor_marker.b,
1423 transparency );
1424
1425 const double delta = ii * griSizeMM * pcbIUScale.IU_PER_MM;
1426
1427 glBegin( GL_LINES );
1428 xmax = ( brd_center_pos.x + delta ) * scale;
1429
1430 glVertex3f( xmax, posy, zmin );
1431 glVertex3f( xmax, posy, zmax );
1432 glEnd();
1433
1434 if( ii != 0 )
1435 {
1436 glBegin( GL_LINES );
1437 xmin = ( brd_center_pos.x - delta ) * scale;
1438 glVertex3f( xmin, posy, zmin );
1439 glVertex3f( xmin, posy, zmax );
1440 glEnd();
1441 }
1442
1443 if( delta > xsize / 2.0f )
1444 break;
1445 }
1446
1447 // Draw horizontal grid lines on Z axis (parallel to X axis)
1448 for( int ii = 0; ; ii++ )
1449 {
1450 if( ii % 5 )
1451 glColor4f( gridColor.r, gridColor.g, gridColor.b, transparency );
1452 else
1453 glColor4f( gridColor_marker.r, gridColor_marker.g, gridColor_marker.b, transparency );
1454
1455 const double delta = ii * griSizeMM * pcbIUScale.IU_PER_MM * scale;
1456
1457 if( delta <= zmax )
1458 {
1459 // Draw grid lines on Z axis (positive Z axis coordinates)
1460 glBegin( GL_LINES );
1461 glVertex3f( xmin, posy, delta );
1462 glVertex3f( xmax, posy, delta );
1463 glEnd();
1464 }
1465
1466 if( delta <= -zmin && ( ii != 0 ) )
1467 {
1468 // Draw grid lines on Z axis (negative Z axis coordinates)
1469 glBegin( GL_LINES );
1470 glVertex3f( xmin, posy, -delta );
1471 glVertex3f( xmax, posy, -delta );
1472 glEnd();
1473 }
1474
1475 if( ( delta > zmax ) && ( delta > -zmin ) )
1476 break;
1477 }
1478
1479 glDisable( GL_BLEND );
1480
1481 glEndList();
1482}
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:112
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
Helper class to handle information needed to display 3D board.
A class used to derive camera objects from.
Definition camera.h:103
Implement a canvas based on a wxGLCanvas.
bool IsZero() const
Definition eda_angle.h:136
double AsRadians() const
Definition eda_angle.h:120
bool IsSelected() const
Definition eda_item.h:127
EDA_ANGLE GetOrientation() const
Definition footprint.h:248
bool IsFlipped() const
Definition footprint.h:434
std::vector< FP_3DMODEL > & Models()
Definition footprint.h:241
VECTOR2I GetPosition() const override
Definition footprint.h:245
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 const LSET & PhysicalLayersMask()
Return a mask holding all layers which are physically realized.
Definition lset.cpp:680
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
std::unique_ptr< BUSY_INDICATOR > CreateBusyIndicator() const
Return a created busy indicator, if a factory has been set, else a null pointer.
RENDER_3D_BASE(BOARD_ADAPTER &aBoardAdapter, CAMERA &aCamera)
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_outerThroughHoleRings
OPENGL_RENDER_LIST * m_offboardPadsFront
SPHERES_GIZMO::GizmoSphereSelection getSelectedGizmoSphere() const
GRID3D_TYPE m_lastGridType
Stores the last grid type.
std::tuple< int, int, int, int > getGizmoViewport() const
OPENGL_RENDER_LIST * m_microviaHoles
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
OPENGL_RENDER_LIST * m_viaBackCover
OPENGL_RENDER_LIST * m_viaFrontCover
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
struct RENDER_3D_OPENGL::@136145154067207014164113243162246125147361200233 m_materials
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,...
EDA_3D_CANVAS * m_canvas
OPENGL_RENDER_LIST * m_padHoles
SPHERES_GIZMO * m_spheres_gizmo
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)
void handleGizmoMouseInput(int mouseX, int mouseY)
void setLightBottom(bool enabled)
void setGizmoViewport(int x, int y, int width, int height)
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)
Report a string with a given severity.
Definition reporter.h:102
Renders a set of colored spheres in 3D space that act as a directional orientation gizmo.
GizmoSphereSelection
Enum to indicate which sphere (direction) is selected.
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:80
static const wxChar * m_logTrace
Trace mask used to enable or disable the trace output of this class.
@ GAUSSIAN_BLUR
Definition image.h:65
int MapPCBLayerTo3DLayer(PCB_LAYER_ID aLayer)
Definition layer_id.cpp:332
@ LAYER_3D_USER_1
Definition layer_ids.h:564
@ LAYER_3D_SOLDERMASK_TOP
Definition layer_ids.h:557
@ LAYER_3D_SOLDERMASK_BOTTOM
Definition layer_ids.h:556
@ LAYER_3D_BOARD
Definition layer_ids.h:551
@ LAYER_3D_USER_45
Definition layer_ids.h:608
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:674
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.
void OglSetMaterial(const SMATERIAL &aMaterial, float aOpacity, bool aUseSelectedMaterial, SFVEC3F aSelectionColor)
Set OpenGL materials.
GLuint OglLoadTexture(const IMAGE &aImage)
Generate a new OpenGL texture.
Definition ogl_utils.cpp:96
void OglDrawBackground(const SFVEC4F &aTopColor, const SFVEC4F &aBotColor)
Define generic OpenGL functions that are common to any OpenGL target.
PGM_BASE & Pgm()
The global program "get" accessor.
Definition pgm_base.cpp:913
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...
int delta
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695
glm::vec3 SFVEC3F
Definition xv3d_types.h:44
glm::vec4 SFVEC4F
Definition xv3d_types.h:46