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
47#include <glm/gtc/type_ptr.hpp>
48
58static float TransparencyControl( float aGrayColorValue, float aTransparency )
59{
60 const float aaa = aTransparency * aTransparency * aTransparency;
61
62 // 1.00-1.05*(1.0-x)^3
63 float ca = 1.0f - aTransparency;
64 ca = 1.00f - 1.05f * ca * ca * ca;
65
66 // Squaring gray value makes darker colors more opaque, which improves appearance
67 // of dark solder masks like black where copper would otherwise show through
68 return glm::clamp( aGrayColorValue * aGrayColorValue * ca + aaa, 0.0f, 1.0f );
69}
70
74#define UNITS3D_TO_UNITSPCB ( pcbIUScale.IU_PER_MM )
75
77 RENDER_3D_BASE( aAdapter, aCamera ),
78 m_canvas( aCanvas )
79{
80 wxLogTrace( m_logTrace, wxT( "RENDER_3D_OPENGL::RENDER_3D_OPENGL" ) );
81
82 m_layers.clear();
83 m_outerLayerHoles.clear();
84 m_innerLayerHoles.clear();
85 m_triangles.clear();
86 m_board = nullptr;
87 m_antiBoard = nullptr;
88
89 m_platedPadsFront = nullptr;
90 m_platedPadsBack = nullptr;
91 m_offboardPadsFront = nullptr;
92 m_offboardPadsBack = nullptr;
93
94 m_outerThroughHoles = nullptr;
96 m_outerViaThroughHoles = nullptr;
97 m_microviaHoles = nullptr;
98 m_padHoles = nullptr;
99 m_viaFrontCover = nullptr;
100 m_viaBackCover = nullptr;
101
102 m_circleTexture = 0;
103 m_grid = 0;
105 m_currentRollOverItem = nullptr;
106 m_boardWithHoles = nullptr;
107 m_postMachinePlugs = nullptr;
108
109 m_3dModelMap.clear();
110
111 m_spheres_gizmo = new SPHERES_GIZMO( 4, 4 );
112}
113
114
116{
117 wxLogTrace( m_logTrace, wxT( "RENDER_3D_OPENGL::RENDER_3D_OPENGL" ) );
118
119 freeAllLists();
120
121 glDeleteTextures( 1, &m_circleTexture );
122
123 delete m_spheres_gizmo;
124}
125
126
128{
129 return 50; // ms
130}
131
132
133void RENDER_3D_OPENGL::SetCurWindowSize( const wxSize& aSize )
134{
135 if( m_windowSize != aSize )
136 {
137 int viewport[4];
138 int fbWidth, fbHeight;
139 glGetIntegerv( GL_VIEWPORT, viewport );
140
141 m_windowSize = aSize;
142 glViewport( 0, 0, m_windowSize.x, m_windowSize.y );
144 // Initialize here any screen dependent data here
145 }
146}
147
148
150{
151 if( enabled )
152 glEnable( GL_LIGHT0 );
153 else
154 glDisable( GL_LIGHT0 );
155}
156
157
159{
160 if( enabled )
161 glEnable( GL_LIGHT1 );
162 else
163 glDisable( GL_LIGHT1 );
164}
165
166
168{
169 if( enabled )
170 glEnable( GL_LIGHT2 );
171 else
172 glDisable( GL_LIGHT2 );
173}
174
175
177{
178 m_spheres_gizmo->resetSelectedGizmoSphere();
179}
180
181
186
187
188void RENDER_3D_OPENGL::setGizmoViewport( int x, int y, int width, int height )
189{
190 m_spheres_gizmo->setViewport( x, y, width, height );
191}
192
193
194std::tuple<int, int, int, int> RENDER_3D_OPENGL::getGizmoViewport() const
195{
196 return m_spheres_gizmo->getViewport();
197}
198
199
200void RENDER_3D_OPENGL::handleGizmoMouseInput( int mouseX, int mouseY )
201{
202 m_spheres_gizmo->handleMouseInput( mouseX, mouseY );
203}
204
205
207{
208 m_materials = {};
209
210 // http://devernay.free.fr/cours/opengl/materials.html
211
212 // Plated copper
213 // Copper material mixed with the copper color
214 m_materials.m_Copper.m_Ambient = SFVEC3F( m_boardAdapter.m_CopperColor.r * 0.1f,
215 m_boardAdapter.m_CopperColor.g * 0.1f,
216 m_boardAdapter.m_CopperColor.b * 0.1f);
217
218 m_materials.m_Copper.m_Specular = SFVEC3F( m_boardAdapter.m_CopperColor.r * 0.75f + 0.25f,
219 m_boardAdapter.m_CopperColor.g * 0.75f + 0.25f,
220 m_boardAdapter.m_CopperColor.b * 0.75f + 0.25f );
221
222 // This guess the material type(ex: copper vs gold) to determine the
223 // shininess factor between 0.1 and 0.4
224 float shininessfactor = 0.40f - mapf( fabs( m_boardAdapter.m_CopperColor.r -
225 m_boardAdapter.m_CopperColor.g ),
226 0.15f, 1.00f,
227 0.00f, 0.30f );
228
229 m_materials.m_Copper.m_Shininess = shininessfactor * 128.0f;
230 m_materials.m_Copper.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
231
232
233 // Non plated copper (raw copper)
234 m_materials.m_NonPlatedCopper.m_Ambient = SFVEC3F( 0.191f, 0.073f, 0.022f );
235 m_materials.m_NonPlatedCopper.m_Diffuse = SFVEC3F( 184.0f / 255.0f, 115.0f / 255.0f,
236 50.0f / 255.0f );
237 m_materials.m_NonPlatedCopper.m_Specular = SFVEC3F( 0.256f, 0.137f, 0.086f );
238 m_materials.m_NonPlatedCopper.m_Shininess = 0.1f * 128.0f;
239 m_materials.m_NonPlatedCopper.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
240
241 // Paste material mixed with paste color
242 m_materials.m_Paste.m_Ambient = SFVEC3F( m_boardAdapter.m_SolderPasteColor.r,
243 m_boardAdapter.m_SolderPasteColor.g,
244 m_boardAdapter.m_SolderPasteColor.b );
245
246 m_materials.m_Paste.m_Specular = SFVEC3F( m_boardAdapter.m_SolderPasteColor.r *
247 m_boardAdapter.m_SolderPasteColor.r,
248 m_boardAdapter.m_SolderPasteColor.g *
249 m_boardAdapter.m_SolderPasteColor.g,
250 m_boardAdapter.m_SolderPasteColor.b *
251 m_boardAdapter.m_SolderPasteColor.b );
252
253 m_materials.m_Paste.m_Shininess = 0.1f * 128.0f;
254 m_materials.m_Paste.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
255
256 // Silk screen material mixed with silk screen color
257 m_materials.m_SilkSTop.m_Ambient = SFVEC3F( m_boardAdapter.m_SilkScreenColorTop.r,
258 m_boardAdapter.m_SilkScreenColorTop.g,
259 m_boardAdapter.m_SilkScreenColorTop.b );
260
261 m_materials.m_SilkSTop.m_Specular = SFVEC3F(
262 m_boardAdapter.m_SilkScreenColorTop.r * m_boardAdapter.m_SilkScreenColorTop.r + 0.10f,
263 m_boardAdapter.m_SilkScreenColorTop.g * m_boardAdapter.m_SilkScreenColorTop.g + 0.10f,
264 m_boardAdapter.m_SilkScreenColorTop.b * m_boardAdapter.m_SilkScreenColorTop.b + 0.10f );
265
266 m_materials.m_SilkSTop.m_Shininess = 0.078125f * 128.0f;
267 m_materials.m_SilkSTop.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
268
269 // Silk screen material mixed with silk screen color
270 m_materials.m_SilkSBot.m_Ambient = SFVEC3F( m_boardAdapter.m_SilkScreenColorBot.r,
271 m_boardAdapter.m_SilkScreenColorBot.g,
272 m_boardAdapter.m_SilkScreenColorBot.b );
273
274 m_materials.m_SilkSBot.m_Specular = SFVEC3F(
275 m_boardAdapter.m_SilkScreenColorBot.r * m_boardAdapter.m_SilkScreenColorBot.r + 0.10f,
276 m_boardAdapter.m_SilkScreenColorBot.g * m_boardAdapter.m_SilkScreenColorBot.g + 0.10f,
277 m_boardAdapter.m_SilkScreenColorBot.b * m_boardAdapter.m_SilkScreenColorBot.b + 0.10f );
278
279 m_materials.m_SilkSBot.m_Shininess = 0.078125f * 128.0f;
280 m_materials.m_SilkSBot.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
281
282 // Shininess is computed dynamically in setLayerMaterial() based on color darkness
283 m_materials.m_SolderMask.m_Shininess = 0.85f * 128.0f;
284 m_materials.m_SolderMask.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
285
286 // Epoxy material
287 m_materials.m_EpoxyBoard.m_Ambient = SFVEC3F( 117.0f / 255.0f, 97.0f / 255.0f,
288 47.0f / 255.0f );
289
290 m_materials.m_EpoxyBoard.m_Specular = SFVEC3F( 18.0f / 255.0f, 3.0f / 255.0f,
291 20.0f / 255.0f );
292
293 m_materials.m_EpoxyBoard.m_Shininess = 0.1f * 128.0f;
294 m_materials.m_EpoxyBoard.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
295}
296
297
299{
300 if( m_boardAdapter.GetUseBoardEditorCopperLayerColors() && IsCopperLayer( aLayerID ) )
301 {
302 COLOR4D copper_color = m_boardAdapter.m_BoardEditorColors[aLayerID];
303 m_materials.m_Copper.m_Diffuse = SFVEC3F( copper_color.r, copper_color.g,
304 copper_color.b );
305 OglSetMaterial( m_materials.m_Copper, 1.0f );
306 m_materials.m_NonPlatedCopper.m_Diffuse = m_materials.m_Copper.m_Diffuse;
307 OglSetMaterial( m_materials.m_NonPlatedCopper, 1.0f );
308
309 return;
310 }
311
312 switch( aLayerID )
313 {
314 case F_Mask:
315 case B_Mask:
316 {
317 const SFVEC4F layerColor = aLayerID == F_Mask ? m_boardAdapter.m_SolderMaskColorTop
318 : m_boardAdapter.m_SolderMaskColorBot;
319
320 m_materials.m_SolderMask.m_Diffuse = layerColor;
321
322 // Compute gray value for material property adjustments based on color darkness
323 const float solderMask_gray = ( layerColor.r + layerColor.g + layerColor.b ) / 3.0f;
324
325 // Use TransparencyControl to make darker colors more opaque, preventing copper
326 // show-through on dark solder masks
327 const float baseTransparency = 1.0f - layerColor.a;
328 m_materials.m_SolderMask.m_Transparency = TransparencyControl( solderMask_gray,
329 baseTransparency );
330
331 m_materials.m_SolderMask.m_Ambient = m_materials.m_SolderMask.m_Diffuse * 0.3f;
332
333 // Darker solder masks need a higher specular floor to avoid washed-out appearance
334 const SFVEC3F baseSpecular = m_materials.m_SolderMask.m_Diffuse
335 * m_materials.m_SolderMask.m_Diffuse;
336 m_materials.m_SolderMask.m_Specular = glm::max( baseSpecular, SFVEC3F( 0.30f ) );
337
338 // Darker colors get higher shininess for a tighter specular highlight, matching
339 // how dark solder masks appear in real life
340 const float minSolderMaskShininess = 0.85f * 128.0f;
341 const float maxSolderMaskShininess = 512.0f;
342 m_materials.m_SolderMask.m_Shininess = minSolderMaskShininess
343 + ( maxSolderMaskShininess - minSolderMaskShininess ) * ( 1.0f - solderMask_gray );
344
345 OglSetMaterial( m_materials.m_SolderMask, 1.0f );
346 break;
347 }
348
349 case B_Paste:
350 case F_Paste:
351 m_materials.m_Paste.m_Diffuse = m_boardAdapter.m_SolderPasteColor;
352 OglSetMaterial( m_materials.m_Paste, 1.0f );
353 break;
354
355 case B_SilkS:
356 m_materials.m_SilkSBot.m_Diffuse = m_boardAdapter.m_SilkScreenColorBot;
357 OglSetMaterial( m_materials.m_SilkSBot, 1.0f );
358 break;
359
360 case F_SilkS:
361 m_materials.m_SilkSTop.m_Diffuse = m_boardAdapter.m_SilkScreenColorTop;
362 OglSetMaterial( m_materials.m_SilkSTop, 1.0f );
363 break;
364
365 case B_Adhes:
366 case F_Adhes:
367 case Dwgs_User:
368 case Cmts_User:
369 case Eco1_User:
370 case Eco2_User:
371 case Edge_Cuts:
372 case Margin:
373 case B_CrtYd:
374 case F_CrtYd:
375 case B_Fab:
376 case F_Fab:
377 switch( aLayerID )
378 {
379 case Dwgs_User: m_materials.m_Plastic.m_Diffuse = m_boardAdapter.m_UserDrawingsColor; break;
380 case Cmts_User: m_materials.m_Plastic.m_Diffuse = m_boardAdapter.m_UserCommentsColor; break;
381 case Eco1_User: m_materials.m_Plastic.m_Diffuse = m_boardAdapter.m_ECO1Color; break;
382 case Eco2_User: m_materials.m_Plastic.m_Diffuse = m_boardAdapter.m_ECO2Color; break;
383 case Edge_Cuts: m_materials.m_Plastic.m_Diffuse = m_boardAdapter.m_UserDrawingsColor; break;
384 case Margin: m_materials.m_Plastic.m_Diffuse = m_boardAdapter.m_UserDrawingsColor; break;
385 default:
386 m_materials.m_Plastic.m_Diffuse = m_boardAdapter.GetLayerColor( aLayerID );
387 break;
388 }
389
390 m_materials.m_Plastic.m_Ambient = SFVEC3F( m_materials.m_Plastic.m_Diffuse.r * 0.05f,
391 m_materials.m_Plastic.m_Diffuse.g * 0.05f,
392 m_materials.m_Plastic.m_Diffuse.b * 0.05f );
393
394 m_materials.m_Plastic.m_Specular = SFVEC3F( m_materials.m_Plastic.m_Diffuse.r * 0.7f,
395 m_materials.m_Plastic.m_Diffuse.g * 0.7f,
396 m_materials.m_Plastic.m_Diffuse.b * 0.7f );
397
398 m_materials.m_Plastic.m_Shininess = 0.078125f * 128.0f;
399 m_materials.m_Plastic.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
400 OglSetMaterial( m_materials.m_Plastic, 1.0f );
401 break;
402
403 default:
404 {
405 int layer3D = MapPCBLayerTo3DLayer( aLayerID );
406
407 // Note: MUST do this in LAYER_3D space; User_1..User_45 are NOT contiguous
408 if( layer3D >= LAYER_3D_USER_1 && layer3D <= LAYER_3D_USER_45 )
409 {
410 int user_idx = layer3D - LAYER_3D_USER_1;
411
412 m_materials.m_Plastic.m_Diffuse = m_boardAdapter.m_UserDefinedLayerColor[ user_idx ];
413 m_materials.m_Plastic.m_Ambient = SFVEC3F( m_materials.m_Plastic.m_Diffuse.r * 0.05f,
414 m_materials.m_Plastic.m_Diffuse.g * 0.05f,
415 m_materials.m_Plastic.m_Diffuse.b * 0.05f );
416
417 m_materials.m_Plastic.m_Specular = SFVEC3F( m_materials.m_Plastic.m_Diffuse.r * 0.7f,
418 m_materials.m_Plastic.m_Diffuse.g * 0.7f,
419 m_materials.m_Plastic.m_Diffuse.b * 0.7f );
420
421 m_materials.m_Plastic.m_Shininess = 0.078125f * 128.0f;
422 m_materials.m_Plastic.m_Emissive = SFVEC3F( 0.0f, 0.0f, 0.0f );
423 OglSetMaterial( m_materials.m_Plastic, 1.0f );
424 break;
425 }
426
427 m_materials.m_Copper.m_Diffuse = m_boardAdapter.m_CopperColor;
428 OglSetMaterial( m_materials.m_Copper, 1.0f );
429 break;
430 }
431 }
432}
433
434
436{
437 // Setup light
438 // https://www.opengl.org/sdk/docs/man2/xhtml/glLight.xml
439 const GLfloat ambient[] = { 0.084f, 0.084f, 0.084f, 1.0f };
440 const GLfloat diffuse0[] = { 0.3f, 0.3f, 0.3f, 1.0f };
441 const GLfloat specular0[] = { 0.5f, 0.5f, 0.5f, 1.0f };
442
443 glLightfv( GL_LIGHT0, GL_AMBIENT, ambient );
444 glLightfv( GL_LIGHT0, GL_DIFFUSE, diffuse0 );
445 glLightfv( GL_LIGHT0, GL_SPECULAR, specular0 );
446
447 const GLfloat diffuse12[] = { 0.7f, 0.7f, 0.7f, 1.0f };
448 const GLfloat specular12[] = { 0.7f, 0.7f, 0.7f, 1.0f };
449
450 // defines a directional light that points along the negative z-axis
451 GLfloat position[4] = { 0.0f, 0.0f, 1.0f, 0.0f };
452
453 // This makes a vector slight not perpendicular with XZ plane
454 const SFVEC3F vectorLight = SphericalToCartesian( glm::pi<float>() * 0.03f,
455 glm::pi<float>() * 0.25f );
456
457 position[0] = vectorLight.x;
458 position[1] = vectorLight.y;
459 position[2] = vectorLight.z;
460
461 glLightfv( GL_LIGHT1, GL_AMBIENT, ambient );
462 glLightfv( GL_LIGHT1, GL_DIFFUSE, diffuse12 );
463 glLightfv( GL_LIGHT1, GL_SPECULAR, specular12 );
464 glLightfv( GL_LIGHT1, GL_POSITION, position );
465
466 // defines a directional light that points along the positive z-axis
467 position[2] = -position[2];
468
469 glLightfv( GL_LIGHT2, GL_AMBIENT, ambient );
470 glLightfv( GL_LIGHT2, GL_DIFFUSE, diffuse12 );
471 glLightfv( GL_LIGHT2, GL_SPECULAR, specular12 );
472 glLightfv( GL_LIGHT2, GL_POSITION, position );
473
474 const GLfloat lmodel_ambient[] = { 0.0f, 0.0f, 0.0f, 1.0f };
475
476 glLightModelfv( GL_LIGHT_MODEL_AMBIENT, lmodel_ambient );
477
478 glLightModeli( GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE );
479}
480
481
483{
484 OglSetMaterial( m_materials.m_NonPlatedCopper, 1.0f );
485}
486
487
489{
490 glEnable( GL_POLYGON_OFFSET_FILL );
491 glPolygonOffset( -0.1f, -2.0f );
492 setLayerMaterial( aLayer_id );
493}
494
495
497{
498 glDisable( GL_POLYGON_OFFSET_FILL );
499}
500
501
502void RENDER_3D_OPENGL::renderBoardBody( bool aSkipRenderHoles )
503{
504 m_materials.m_EpoxyBoard.m_Diffuse = m_boardAdapter.m_BoardBodyColor;
505
506 // opacity to transparency
507 m_materials.m_EpoxyBoard.m_Transparency = 1.0f - m_boardAdapter.m_BoardBodyColor.a;
508
509 OglSetMaterial( m_materials.m_EpoxyBoard, 1.0f );
510
511 OPENGL_RENDER_LIST* ogl_disp_list = nullptr;
512
513 if( aSkipRenderHoles )
514 ogl_disp_list = m_board;
515 else
516 ogl_disp_list = m_boardWithHoles;
517
518 if( ogl_disp_list )
519 {
520 ogl_disp_list->ApplyScalePosition( -m_boardAdapter.GetBoardBodyThickness() / 2.0f,
521 m_boardAdapter.GetBoardBodyThickness() );
522
523 ogl_disp_list->SetItIsTransparent( true );
524 ogl_disp_list->DrawAll();
525 }
526
527 // Also render post-machining plugs (board material that remains after backdrill/counterbore/countersink)
528 if( !aSkipRenderHoles && m_postMachinePlugs )
529 {
530 m_postMachinePlugs->ApplyScalePosition( -m_boardAdapter.GetBoardBodyThickness() / 2.0f,
531 m_boardAdapter.GetBoardBodyThickness() );
532
533 m_postMachinePlugs->SetItIsTransparent( true );
534 m_postMachinePlugs->DrawAll();
535 }
536}
537
538
539static inline SFVEC4F premultiplyAlpha( const SFVEC4F& aInput )
540{
541 return SFVEC4F( aInput.r * aInput.a, aInput.g * aInput.a, aInput.b * aInput.a, aInput.a );
542}
543
544
545bool RENDER_3D_OPENGL::Redraw( bool aIsMoving, REPORTER* aStatusReporter,
546 REPORTER* aWarningReporter )
547{
548 // Initialize OpenGL
550 {
551 if( !initializeOpenGL() )
552 return false;
553 }
554
556
558 {
559 std::unique_ptr<BUSY_INDICATOR> busy = CreateBusyIndicator();
560
561 if( aStatusReporter )
562 aStatusReporter->Report( _( "Loading..." ) );
563
564 // Careful here!
565 // We are in the middle of rendering and the reload method may show
566 // a dialog box that requires the opengl context for a redraw
567 Pgm().GetGLContextManager()->RunWithoutCtxLock( [this, aStatusReporter, aWarningReporter]()
568 {
569 reload( aStatusReporter, aWarningReporter );
570 } );
571
572 // generate a new 3D grid as the size of the board may had changed
573 m_lastGridType = static_cast<GRID3D_TYPE>( cfg.grid_type );
575 }
576 else
577 {
578 // Check if grid was changed
579 if( cfg.grid_type != m_lastGridType )
580 {
581 // and generate a new one
582 m_lastGridType = static_cast<GRID3D_TYPE>( cfg.grid_type );
584 }
585 }
586
588
589 // Initial setup
590 glDepthFunc( GL_LESS );
591 glEnable( GL_CULL_FACE );
592 glFrontFace( GL_CCW ); // This is the OpenGL default
593 glEnable( GL_NORMALIZE ); // This allow OpenGL to normalize the normals after transformations
594 glViewport( 0, 0, m_windowSize.x, m_windowSize.y );
595
596 if( aIsMoving && cfg.opengl_AA_disableOnMove )
597 glDisable( GL_MULTISAMPLE );
598 else
599 glEnable( GL_MULTISAMPLE );
600
601 // clear color and depth buffers
602 glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
603 glClearDepth( 1.0f );
604 glClearStencil( 0x00 );
605 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
606
608
609 // Draw the background ( rectangle with color gradient)
611 premultiplyAlpha( m_boardAdapter.m_BgColorBot ) );
612
613 glEnable( GL_DEPTH_TEST );
614
615 // Set projection and modelview matrixes
616 glMatrixMode( GL_PROJECTION );
617 glLoadMatrixf( glm::value_ptr( m_camera.GetProjectionMatrix() ) );
618 glMatrixMode( GL_MODELVIEW );
619 glLoadIdentity();
620 glLoadMatrixf( glm::value_ptr( m_camera.GetViewMatrix() ) );
621
622 // Position the headlight
623 setLightFront( true );
624 setLightTop( true );
625 setLightBottom( true );
626
627 glEnable( GL_LIGHTING );
628
629 {
630 const SFVEC3F& cameraPos = m_camera.GetPos();
631
632 // Place the light at a minimum Z so the diffuse factor will not drop
633 // and the board will still look with good light.
634 float zpos;
635
636 if( cameraPos.z > 0.0f )
637 zpos = glm::max( cameraPos.z, 0.5f ) + cameraPos.z * cameraPos.z;
638 else
639 zpos = glm::min( cameraPos.z,-0.5f ) - cameraPos.z * cameraPos.z;
640
641 // This is a point light.
642 const GLfloat headlight_pos[] = { cameraPos.x, cameraPos.y, zpos, 1.0f };
643
644 glLightfv( GL_LIGHT0, GL_POSITION, headlight_pos );
645 }
646
647 bool skipThickness = aIsMoving && cfg.opengl_thickness_disableOnMove;
648 bool skipRenderHoles = aIsMoving && cfg.opengl_holes_disableOnMove;
649 bool skipRenderMicroVias = aIsMoving && cfg.opengl_microvias_disableOnMove;
650 bool showThickness = !skipThickness;
651
652 std::bitset<LAYER_3D_END> layerFlags = m_boardAdapter.GetVisibleLayers();
653
655
656 if( !( skipRenderMicroVias || skipRenderHoles ) && m_microviaHoles )
657 m_microviaHoles->DrawAll();
658
659 if( !skipRenderHoles && m_padHoles )
660 m_padHoles->DrawAll();
661
662 // Display copper and tech layers
663 for( MAP_OGL_DISP_LISTS::const_iterator ii = m_layers.begin(); ii != m_layers.end(); ++ii )
664 {
665 const PCB_LAYER_ID layer = ( PCB_LAYER_ID )( ii->first );
666 bool isSilkLayer = layer == F_SilkS || layer == B_SilkS;
667 bool isMaskLayer = layer == F_Mask || layer == B_Mask;
668 bool isPasteLayer = layer == F_Paste || layer == B_Paste;
669
670 // Mask layers are not processed here because they are a special case
671 if( isMaskLayer )
672 continue;
673
674 // Do not show inner layers when it is displaying the board and board body is opaque
675 // enough: the time to create inner layers can be *really significant*.
676 // So avoid creating them is they are not very visible
677 const double opacity_min = 0.8;
678
679 if( layerFlags.test( LAYER_3D_BOARD ) && m_boardAdapter.m_BoardBodyColor.a > opacity_min )
680 {
681 // generating internal copper layers is time consuming. so skip them
682 // if the board body is masking them (i.e. if the opacity is near 1.0)
683 // B_Cu is layer 2 and all inner layers are higher values
684 if( layer > B_Cu && IsCopperLayer( layer ) )
685 continue;
686 }
687
688 glPushMatrix();
689
690 OPENGL_RENDER_LIST* pLayerDispList = static_cast<OPENGL_RENDER_LIST*>( ii->second );
691
692 if( IsCopperLayer( layer ) )
693 {
694 if( cfg.DifferentiatePlatedCopper() )
696 else
697 setLayerMaterial( layer );
698
699 OPENGL_RENDER_LIST* outerTH = nullptr;
700 OPENGL_RENDER_LIST* viaHoles = nullptr;
701
702 if( !skipRenderHoles )
703 {
704 outerTH = m_outerThroughHoles;
705 viaHoles = m_outerLayerHoles[layer];
706 }
707
708 if( m_antiBoard )
709 m_antiBoard->ApplyScalePosition( pLayerDispList );
710
711 if( outerTH )
712 outerTH->ApplyScalePosition( pLayerDispList );
713
714 pLayerDispList->DrawCulled( showThickness, outerTH, viaHoles, m_antiBoard );
715
716 // Draw plated & offboard pads
717 if( layer == F_Cu && ( m_platedPadsFront || m_offboardPadsFront ) )
718 {
720
722 m_platedPadsFront->DrawCulled( showThickness, outerTH, viaHoles, m_antiBoard );
723
725 m_offboardPadsFront->DrawCulled( showThickness, outerTH, viaHoles );
726 }
727 else if( layer == B_Cu && ( m_platedPadsBack || m_offboardPadsBack ) )
728 {
730
731 if( m_platedPadsBack )
732 m_platedPadsBack->DrawCulled( showThickness, outerTH, viaHoles, m_antiBoard );
733
735 m_offboardPadsBack->DrawCulled( showThickness, outerTH, viaHoles );
736 }
737
739 }
740 else
741 {
742 setLayerMaterial( layer );
743
744 OPENGL_RENDER_LIST* throughHolesOuter = nullptr;
745 OPENGL_RENDER_LIST* anti_board = nullptr;
746 OPENGL_RENDER_LIST* solder_mask = nullptr;
747
748 if( !skipRenderHoles )
749 {
750 if( isSilkLayer && cfg.clip_silk_on_via_annuli )
751 throughHolesOuter = m_outerThroughHoleRings;
752 else
753 throughHolesOuter = m_outerThroughHoles;
754 }
755
756 if( isSilkLayer && cfg.show_off_board_silk )
757 anti_board = nullptr;
758 else if( LSET::PhysicalLayersMask().test( layer ) )
759 anti_board = m_antiBoard;
760
761 if( isSilkLayer && cfg.subtract_mask_from_silk && !cfg.show_off_board_silk )
762 solder_mask = m_layers[ ( layer == B_SilkS) ? B_Mask : F_Mask ];
763
764 if( throughHolesOuter )
765 throughHolesOuter->ApplyScalePosition( pLayerDispList );
766
767 if( anti_board )
768 anti_board->ApplyScalePosition( pLayerDispList );
769
770 if( solder_mask )
771 solder_mask->ApplyScalePosition( pLayerDispList );
772
773 pLayerDispList->DrawCulled( showThickness, solder_mask, throughHolesOuter, anti_board );
774 }
775
776 glPopMatrix();
777 }
778
779 glm::mat4 cameraViewMatrix;
780
781 glGetFloatv( GL_MODELVIEW_MATRIX, glm::value_ptr( cameraViewMatrix ) );
782
783 // Render 3D Models (Non-transparent)
784 renderOpaqueModels( cameraViewMatrix );
785
786 // Display board body
787 if( layerFlags.test( LAYER_3D_BOARD ) )
788 renderBoardBody( skipRenderHoles );
789
790 // Display transparent mask layers
791 if( layerFlags.test( LAYER_3D_SOLDERMASK_TOP )
792 || layerFlags.test( LAYER_3D_SOLDERMASK_BOTTOM ) )
793 {
794 // add a depth buffer offset, it will help to hide some artifacts
795 // on silkscreen where the SolderMask is removed
796 glEnable( GL_POLYGON_OFFSET_FILL );
797 glPolygonOffset( 0.0f, -2.0f );
798
799 if( m_camera.GetPos().z > 0 )
800 {
801 if( layerFlags.test( LAYER_3D_SOLDERMASK_BOTTOM ) )
802 {
804 showThickness, skipRenderHoles );
805 }
806
807 if( layerFlags.test( LAYER_3D_SOLDERMASK_TOP ) )
808 {
809 renderSolderMaskLayer( F_Mask, m_boardAdapter.GetLayerBottomZPos( F_Mask ),
810 showThickness, skipRenderHoles );
811 }
812 }
813 else
814 {
815 if( layerFlags.test( LAYER_3D_SOLDERMASK_TOP ) )
816 {
817 renderSolderMaskLayer( F_Mask, m_boardAdapter.GetLayerBottomZPos( F_Mask ),
818 showThickness, skipRenderHoles );
819 }
820
821 if( layerFlags.test( LAYER_3D_SOLDERMASK_BOTTOM ) )
822 {
824 showThickness, skipRenderHoles );
825 }
826 }
827
828 glDisable( GL_POLYGON_OFFSET_FILL );
829 glPolygonOffset( 0.0f, 0.0f );
830 }
831
832 // Render 3D Models (Transparent)
833 // !TODO: this can be optimized. If there are no transparent models (or no opacity),
834 // then there is no need to make this function call.
835 glDepthMask( GL_FALSE );
836 glEnable( GL_BLEND );
837 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
838
839 // Enables Texture Env so it can combine model transparency with each footprint opacity
840 glEnable( GL_TEXTURE_2D );
841 glActiveTexture( GL_TEXTURE0 );
842
843 // Uses an existent texture so the glTexEnv operations will work.
844 glBindTexture( GL_TEXTURE_2D, m_circleTexture );
845
846 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE );
847 glTexEnvf( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE );
848 glTexEnvf( GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE );
849
850 glTexEnvi( GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PRIMARY_COLOR );
851 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR );
852
853 glTexEnvi( GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PREVIOUS );
854 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR );
855
856 glTexEnvi( GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PRIMARY_COLOR );
857 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA );
858 glTexEnvi( GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_CONSTANT );
859 glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA );
860
861 renderTransparentModels( cameraViewMatrix );
862
863 glDisable( GL_BLEND );
865
866 glDepthMask( GL_TRUE );
867
868 // Render Grid
869 if( cfg.grid_type != GRID3D_TYPE::NONE )
870 {
871 glDisable( GL_LIGHTING );
872
873 if( glIsList( m_grid ) )
874 glCallList( m_grid );
875
876 glEnable( GL_LIGHTING );
877 }
878
879 // Render 3D arrows
880 if( cfg.show_navigator )
881 m_spheres_gizmo->render3dSpheresGizmo( m_camera.GetRotationMatrix() );
882
883 // Return back to the original viewport (this is important if we want
884 // to take a screenshot after the render)
885 glViewport( 0, 0, m_windowSize.x, m_windowSize.y );
886
887 return false;
888}
889
890
892{
893 glEnable( GL_LINE_SMOOTH );
894 glShadeModel( GL_SMOOTH );
895
896 // 4-byte pixel alignment
897 glPixelStorei( GL_UNPACK_ALIGNMENT, 4 );
898
899 // Initialize the open GL texture to draw the filled semi-circle of the segments
901
902 if( !circleImage )
903 return false;
904
905 unsigned int circleRadius = ( SIZE_OF_CIRCLE_TEXTURE / 2 ) - 4;
906
907 circleImage->CircleFilled( ( SIZE_OF_CIRCLE_TEXTURE / 2 ) - 0,
908 ( SIZE_OF_CIRCLE_TEXTURE / 2 ) - 0,
909 circleRadius,
910 0xFF );
911
912 IMAGE* circleImageBlured = new IMAGE( circleImage->GetWidth(), circleImage->GetHeight() );
913
914 circleImageBlured->EfxFilter_SkipCenter( circleImage, IMAGE_FILTER::GAUSSIAN_BLUR, circleRadius - 8 );
915
916 m_circleTexture = OglLoadTexture( *circleImageBlured );
917
918 delete circleImageBlured;
919 circleImageBlured = nullptr;
920
921 delete circleImage;
922 circleImage = nullptr;
923
924 init_lights();
925
926 // Use this mode if you want see the triangle lines (debug proposes)
927 //glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
928 m_canvasInitialized = true;
929
930 return true;
931}
932
933
935{
936 glEnable( GL_COLOR_MATERIAL );
937 glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );
938
939 const SFVEC4F ambient = SFVEC4F( 0.0f, 0.0f, 0.0f, 1.0f );
940 const SFVEC4F diffuse = SFVEC4F( 0.0f, 0.0f, 0.0f, 1.0f );
941 const SFVEC4F emissive = SFVEC4F( 0.0f, 0.0f, 0.0f, 1.0f );
942 const SFVEC4F specular = SFVEC4F( 0.1f, 0.1f, 0.1f, 1.0f );
943
944 glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, &specular.r );
945 glMaterialf( GL_FRONT_AND_BACK, GL_SHININESS, 96.0f );
946
947 glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, &ambient.r );
948 glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, &diffuse.r );
949 glMaterialfv( GL_FRONT_AND_BACK, GL_EMISSION, &emissive.r );
950}
951
952
954{
955#define DELETE_AND_FREE( ptr ) \
956 { \
957 delete ptr; \
958 ptr = nullptr; \
959 } \
960
961#define DELETE_AND_FREE_MAP( map ) \
962 { \
963 for( auto& [ layer, ptr ] : map ) \
964 delete ptr; \
965 \
966 map.clear(); \
967 }
968
969 if( glIsList( m_grid ) )
970 glDeleteLists( m_grid, 1 );
971
972 m_grid = 0;
973
975
980
983
985 delete list;
986
987 m_triangles.clear();
988
990
991 m_3dModelMatrixMap.clear();
992
997
1001
1006}
1007
1008
1010 bool aShowThickness, bool aSkipRenderHoles )
1011{
1012 wxASSERT( (aLayerID == B_Mask) || (aLayerID == F_Mask) );
1013
1014 if( m_board )
1015 {
1016 OPENGL_RENDER_LIST* solder_mask = m_layers[ aLayerID ];
1017 OPENGL_RENDER_LIST* via_holes = aSkipRenderHoles ? nullptr : m_outerThroughHoles;
1018
1019 if( via_holes )
1020 via_holes->ApplyScalePosition( aZPos, m_boardAdapter.GetNonCopperLayerThickness() );
1021
1022 m_board->ApplyScalePosition( aZPos, m_boardAdapter.GetNonCopperLayerThickness() );
1023
1024 setLayerMaterial( aLayerID );
1025 m_board->SetItIsTransparent( true );
1026 m_board->DrawCulled( aShowThickness, solder_mask, via_holes );
1027
1028 if( aLayerID == F_Mask && m_viaFrontCover )
1029 {
1030 m_viaFrontCover->ApplyScalePosition( aZPos, 4 * m_boardAdapter.GetNonCopperLayerThickness() );
1031 m_viaFrontCover->DrawTop();
1032 }
1033 else if( aLayerID == B_Mask && m_viaBackCover )
1034 {
1035 m_viaBackCover->ApplyScalePosition( aZPos, 4 * m_boardAdapter.GetNonCopperLayerThickness() );
1036 m_viaBackCover->DrawBot();
1037 }
1038 }
1039}
1040
1041
1042void RENDER_3D_OPENGL::get3dModelsSelected( std::list<MODELTORENDER> &aDstRenderList, bool aGetTop,
1043 bool aGetBot, bool aRenderTransparentOnly,
1044 bool aRenderSelectedOnly )
1045{
1046 wxASSERT( ( aGetTop == true ) || ( aGetBot == true ) );
1047
1048 if( !m_boardAdapter.GetBoard() )
1049 return;
1050
1052 const wxString currentVariant = m_boardAdapter.GetBoard()->GetCurrentVariant();
1053
1054 // Go for all footprints
1055 for( FOOTPRINT* fp : m_boardAdapter.GetBoard()->Footprints() )
1056 {
1057 bool highlight = false;
1058
1059 if( m_boardAdapter.m_IsBoardView )
1060 {
1061 if( fp->IsSelected() )
1062 highlight = true;
1063
1065 highlight = true;
1066
1067 if( aRenderSelectedOnly != highlight )
1068 continue;
1069 }
1070
1071 if( !fp->Models().empty() )
1072 {
1073 if( m_boardAdapter.IsFootprintShown( fp ) )
1074 {
1075 // Skip 3D models for footprints that are DNP in the current variant
1076 if( fp->GetDNPForVariant( currentVariant ) )
1077 continue;
1078
1079 const bool isFlipped = fp->IsFlipped();
1080
1081 if( aGetTop == !isFlipped || aGetBot == isFlipped )
1082 get3dModelsFromFootprint( aDstRenderList, fp, aRenderTransparentOnly,
1083 highlight );
1084 }
1085 }
1086 }
1087}
1088
1089
1090void RENDER_3D_OPENGL::get3dModelsFromFootprint( std::list<MODELTORENDER> &aDstRenderList,
1091 const FOOTPRINT* aFootprint,
1092 bool aRenderTransparentOnly, bool aIsSelected )
1093{
1094 if( !aFootprint->Models().empty() )
1095 {
1096 const double zpos = m_boardAdapter.GetFootprintZPos( aFootprint->IsFlipped() );
1097
1098 VECTOR2I pos = aFootprint->GetPosition();
1099
1100 glm::mat4 fpMatrix( 1.0f );
1101
1102 fpMatrix = glm::translate( fpMatrix, SFVEC3F( pos.x * m_boardAdapter.BiuTo3dUnits(),
1103 -pos.y * m_boardAdapter.BiuTo3dUnits(),
1104 zpos ) );
1105
1106 if( !aFootprint->GetOrientation().IsZero() )
1107 {
1108 fpMatrix = glm::rotate( fpMatrix, (float) aFootprint->GetOrientation().AsRadians(),
1109 SFVEC3F( 0.0f, 0.0f, 1.0f ) );
1110 }
1111
1112 if( aFootprint->IsFlipped() )
1113 {
1114 fpMatrix = glm::rotate( fpMatrix, glm::pi<float>(), SFVEC3F( 0.0f, 1.0f, 0.0f ) );
1115 fpMatrix = glm::rotate( fpMatrix, glm::pi<float>(), SFVEC3F( 0.0f, 0.0f, 1.0f ) );
1116 }
1117
1118 double modelunit_to_3d_units_factor = m_boardAdapter.BiuTo3dUnits() * UNITS3D_TO_UNITSPCB;
1119
1120 fpMatrix = glm::scale( fpMatrix, SFVEC3F( modelunit_to_3d_units_factor ) );
1121
1122 // Get the list of model files for this model
1123 for( const FP_3DMODEL& sM : aFootprint->Models() )
1124 {
1125 if( !sM.m_Show || sM.m_Filename.empty() )
1126 continue;
1127
1128 // Check if the model is present in our cache map
1129 auto cache_i = m_3dModelMap.find( sM.m_Filename );
1130
1131 if( cache_i == m_3dModelMap.end() )
1132 continue;
1133
1134 if( const MODEL_3D* modelPtr = cache_i->second )
1135 {
1136 bool opaque = sM.m_Opacity >= 1.0;
1137
1138 if( ( !aRenderTransparentOnly && modelPtr->HasOpaqueMeshes() && opaque ) ||
1139 ( aRenderTransparentOnly && ( modelPtr->HasTransparentMeshes() || !opaque ) ) )
1140 {
1141 glm::mat4 modelworldMatrix = fpMatrix;
1142
1143 const SFVEC3F offset = SFVEC3F( sM.m_Offset.x, sM.m_Offset.y, sM.m_Offset.z );
1144 const SFVEC3F rotation = SFVEC3F( sM.m_Rotation.x, sM.m_Rotation.y,
1145 sM.m_Rotation.z );
1146 const SFVEC3F scale = SFVEC3F( sM.m_Scale.x, sM.m_Scale.y, sM.m_Scale.z );
1147
1148 std::vector<float> key = { offset.x, offset.y, offset.z,
1149 rotation.x, rotation.y, rotation.z,
1150 scale.x, scale.y, scale.z };
1151
1152 auto it = m_3dModelMatrixMap.find( key );
1153
1154 if( it != m_3dModelMatrixMap.end() )
1155 {
1156 modelworldMatrix *= it->second;
1157 }
1158 else
1159 {
1160 glm::mat4 mtx( 1.0f );
1161 mtx = glm::translate( mtx, offset );
1162 mtx = glm::rotate( mtx, glm::radians( -rotation.z ), { 0.0f, 0.0f, 1.0f } );
1163 mtx = glm::rotate( mtx, glm::radians( -rotation.y ), { 0.0f, 1.0f, 0.0f } );
1164 mtx = glm::rotate( mtx, glm::radians( -rotation.x ), { 1.0f, 0.0f, 0.0f } );
1165 mtx = glm::scale( mtx, scale );
1166 m_3dModelMatrixMap[ key ] = mtx;
1167
1168 modelworldMatrix *= mtx;
1169 }
1170
1171 aDstRenderList.emplace_back( modelworldMatrix, modelPtr,
1172 aRenderTransparentOnly ? sM.m_Opacity : 1.0f,
1173 aRenderTransparentOnly,
1174 aFootprint->IsSelected() || aIsSelected );
1175 }
1176 }
1177 }
1178 }
1179}
1180
1181
1182void RENDER_3D_OPENGL::renderOpaqueModels( const glm::mat4 &aCameraViewMatrix )
1183{
1185
1186 const SFVEC3F selColor = m_boardAdapter.GetColor( cfg.opengl_selection_color );
1187
1188 glPushMatrix();
1189
1190 std::list<MODELTORENDER> renderList;
1191
1192 if( m_boardAdapter.m_IsBoardView )
1193 {
1194 renderList.clear();
1195
1196 get3dModelsSelected( renderList, true, true, false, true );
1197
1198 if( !renderList.empty() )
1199 {
1200 MODEL_3D::BeginDrawMulti( false );
1201
1202 for( const MODELTORENDER& mtr : renderList )
1203 renderModel( aCameraViewMatrix, mtr, selColor, nullptr );
1204
1206 }
1207 }
1208
1209 renderList.clear();
1210 get3dModelsSelected( renderList, true, true, false, false );
1211
1212 if( !renderList.empty() )
1213 {
1215
1216 for( const MODELTORENDER& mtr : renderList )
1217 renderModel( aCameraViewMatrix, mtr, selColor, nullptr );
1218
1220 }
1221
1222 glPopMatrix();
1223}
1224
1225
1226void RENDER_3D_OPENGL::renderTransparentModels( const glm::mat4 &aCameraViewMatrix )
1227{
1229
1230 const SFVEC3F selColor = m_boardAdapter.GetColor( cfg.opengl_selection_color );
1231
1232 std::list<MODELTORENDER> renderListModels; // do not clear it until this function returns
1233
1234 if( m_boardAdapter.m_IsBoardView )
1235 {
1236 // Get Transparent Selected
1237 get3dModelsSelected( renderListModels, true, true, true, true );
1238 }
1239
1240 // Get Transparent Not Selected
1241 get3dModelsSelected( renderListModels, true, true, true, false );
1242
1243 if( renderListModels.empty() )
1244 return;
1245
1246 std::vector<std::pair<const MODELTORENDER *, float>> transparentModelList;
1247
1248 transparentModelList.reserve( renderListModels.size() );
1249
1250 // Calculate the distance to the camera for each model
1251 const SFVEC3F &cameraPos = m_camera.GetPos();
1252
1253 for( const MODELTORENDER& mtr : renderListModels )
1254 {
1255 const BBOX_3D& bBox = mtr.m_model->GetBBox();
1256 const SFVEC3F& bBoxCenter = bBox.GetCenter();
1257 const SFVEC3F bBoxWorld = mtr.m_modelWorldMat * glm::vec4( bBoxCenter, 1.0f );
1258
1259 const float distanceToCamera = glm::length( cameraPos - bBoxWorld );
1260
1261 transparentModelList.emplace_back( &mtr, distanceToCamera );
1262 }
1263
1264 // Sort from back to front
1265 std::sort( transparentModelList.begin(), transparentModelList.end(),
1266 [&]( std::pair<const MODELTORENDER *, float>& a,
1267 std::pair<const MODELTORENDER *, float>& b )
1268 {
1269 if( a.second != b.second )
1270 return a.second > b.second;
1271
1272 return a.first > b.first; // use pointers as a last resort
1273 } );
1274
1275 // Start rendering calls
1276 glPushMatrix();
1277
1278 bool isUsingColorInformation = !( transparentModelList.begin()->first->m_isSelected &&
1279 m_boardAdapter.m_IsBoardView );
1280
1281 MODEL_3D::BeginDrawMulti( isUsingColorInformation );
1282
1283 for( const std::pair<const MODELTORENDER *, float>& mtr : transparentModelList )
1284 {
1285 if( m_boardAdapter.m_IsBoardView )
1286 {
1287 // Toggle between using model color or the select color
1288 if( !isUsingColorInformation && !mtr.first->m_isSelected )
1289 {
1290 isUsingColorInformation = true;
1291
1292 glEnableClientState( GL_COLOR_ARRAY );
1293 glEnableClientState( GL_TEXTURE_COORD_ARRAY );
1294 glEnable( GL_COLOR_MATERIAL );
1295 }
1296 else if( isUsingColorInformation && mtr.first->m_isSelected )
1297 {
1298 isUsingColorInformation = false;
1299
1300 glDisableClientState( GL_COLOR_ARRAY );
1301 glDisableClientState( GL_TEXTURE_COORD_ARRAY );
1302 glDisable( GL_COLOR_MATERIAL );
1303 }
1304 }
1305
1306 // Render model, sort each individuall material group
1307 // by passing cameraPos
1308 renderModel( aCameraViewMatrix, *mtr.first, selColor, &cameraPos );
1309 }
1310
1312
1313 glPopMatrix();
1314}
1315
1316
1317void RENDER_3D_OPENGL::renderModel( const glm::mat4 &aCameraViewMatrix,
1318 const MODELTORENDER &aModelToRender,
1319 const SFVEC3F &aSelColor, const SFVEC3F *aCameraWorldPos )
1320{
1322
1323 const glm::mat4 modelviewMatrix = aCameraViewMatrix * aModelToRender.m_modelWorldMat;
1324
1325 glLoadMatrixf( glm::value_ptr( modelviewMatrix ) );
1326
1327 aModelToRender.m_model->Draw( aModelToRender.m_isTransparent, aModelToRender.m_opacity,
1328 aModelToRender.m_isSelected, aSelColor,
1329 &aModelToRender.m_modelWorldMat, aCameraWorldPos );
1330
1331 if( cfg.show_model_bbox )
1332 {
1333 const bool wasBlendEnabled = glIsEnabled( GL_BLEND );
1334
1335 if( !wasBlendEnabled )
1336 {
1337 glEnable( GL_BLEND );
1338 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
1339 }
1340
1341 glDisable( GL_LIGHTING );
1342
1343 glLineWidth( 1 );
1344 aModelToRender.m_model->DrawBboxes();
1345
1346 glLineWidth( 4 );
1347 aModelToRender.m_model->DrawBbox();
1348
1349 glEnable( GL_LIGHTING );
1350
1351 if( !wasBlendEnabled )
1352 glDisable( GL_BLEND );
1353 }
1354}
1355
1356
1358{
1359 if( glIsList( m_grid ) )
1360 glDeleteLists( m_grid, 1 );
1361
1362 m_grid = 0;
1363
1364 if( aGridType == GRID3D_TYPE::NONE )
1365 return;
1366
1367 m_grid = glGenLists( 1 );
1368
1369 if( !glIsList( m_grid ) )
1370 return;
1371
1372 glNewList( m_grid, GL_COMPILE );
1373
1374 glEnable( GL_BLEND );
1375 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
1376
1377 const double zpos = 0.0;
1378
1379 // Color of grid lines
1380 const SFVEC3F gridColor = m_boardAdapter.GetColor( DARKGRAY );
1381
1382 // Color of grid lines every 5 lines
1383 const SFVEC3F gridColor_marker = m_boardAdapter.GetColor( LIGHTBLUE );
1384 const double scale = m_boardAdapter.BiuTo3dUnits();
1385 const GLfloat transparency = 0.35f;
1386
1387 double griSizeMM = 0.0;
1388
1389 switch( aGridType )
1390 {
1391 case GRID3D_TYPE::GRID_1MM: griSizeMM = 1.0; break;
1392 case GRID3D_TYPE::GRID_2P5MM: griSizeMM = 2.5; break;
1393 case GRID3D_TYPE::GRID_5MM: griSizeMM = 5.0; break;
1394 case GRID3D_TYPE::GRID_10MM: griSizeMM = 10.0; break;
1395
1396 default:
1397 case GRID3D_TYPE::NONE: return;
1398 }
1399
1400 glNormal3f( 0.0, 0.0, 1.0 );
1401
1402 const VECTOR2I brd_size = m_boardAdapter.GetBoardSize();
1403 VECTOR2I brd_center_pos = m_boardAdapter.GetBoardPos();
1404
1405 brd_center_pos.y = -brd_center_pos.y;
1406
1407 const int xsize = std::max( brd_size.x, pcbIUScale.mmToIU( 100 ) ) * 1.2;
1408 const int ysize = std::max( brd_size.y, pcbIUScale.mmToIU( 100 ) ) * 1.2;
1409
1410 // Grid limits, in 3D units
1411 double xmin = ( brd_center_pos.x - xsize / 2 ) * scale;
1412 double xmax = ( brd_center_pos.x + xsize / 2 ) * scale;
1413 double ymin = ( brd_center_pos.y - ysize / 2 ) * scale;
1414 double ymax = ( brd_center_pos.y + ysize / 2 ) * scale;
1415 double zmin = pcbIUScale.mmToIU( -50 ) * scale;
1416 double zmax = pcbIUScale.mmToIU( 100 ) * scale;
1417
1418 // Set rasterised line width (min value = 1)
1419 glLineWidth( 1 );
1420
1421 // Draw horizontal grid centered on 3D origin (center of the board)
1422 for( int ii = 0; ; ii++ )
1423 {
1424 if( (ii % 5) )
1425 glColor4f( gridColor.r, gridColor.g, gridColor.b, transparency );
1426 else
1427 glColor4f( gridColor_marker.r, gridColor_marker.g, gridColor_marker.b,
1428 transparency );
1429
1430 const int delta = KiROUND( ii * griSizeMM * pcbIUScale.IU_PER_MM );
1431
1432 if( delta <= xsize / 2 ) // Draw grid lines parallel to X axis
1433 {
1434 glBegin( GL_LINES );
1435 glVertex3f( (brd_center_pos.x + delta) * scale, -ymin, zpos );
1436 glVertex3f( (brd_center_pos.x + delta) * scale, -ymax, zpos );
1437 glEnd();
1438
1439 if( ii != 0 )
1440 {
1441 glBegin( GL_LINES );
1442 glVertex3f( (brd_center_pos.x - delta) * scale, -ymin, zpos );
1443 glVertex3f( (brd_center_pos.x - delta) * scale, -ymax, zpos );
1444 glEnd();
1445 }
1446 }
1447
1448 if( delta <= ysize / 2 ) // Draw grid lines parallel to Y axis
1449 {
1450 glBegin( GL_LINES );
1451 glVertex3f( xmin, -( brd_center_pos.y + delta ) * scale, zpos );
1452 glVertex3f( xmax, -( brd_center_pos.y + delta ) * scale, zpos );
1453 glEnd();
1454
1455 if( ii != 0 )
1456 {
1457 glBegin( GL_LINES );
1458 glVertex3f( xmin, -( brd_center_pos.y - delta ) * scale, zpos );
1459 glVertex3f( xmax, -( brd_center_pos.y - delta ) * scale, zpos );
1460 glEnd();
1461 }
1462 }
1463
1464 if( ( delta > ysize / 2 ) && ( delta > xsize / 2 ) )
1465 break;
1466 }
1467
1468 // Draw vertical grid on Z axis
1469 glNormal3f( 0.0, -1.0, 0.0 );
1470
1471 // Draw vertical grid lines (parallel to Z axis)
1472 double posy = -brd_center_pos.y * scale;
1473
1474 for( int ii = 0; ; ii++ )
1475 {
1476 if( (ii % 5) )
1477 glColor4f( gridColor.r, gridColor.g, gridColor.b, transparency );
1478 else
1479 glColor4f( gridColor_marker.r, gridColor_marker.g, gridColor_marker.b,
1480 transparency );
1481
1482 const double delta = ii * griSizeMM * pcbIUScale.IU_PER_MM;
1483
1484 glBegin( GL_LINES );
1485 xmax = ( brd_center_pos.x + delta ) * scale;
1486
1487 glVertex3f( xmax, posy, zmin );
1488 glVertex3f( xmax, posy, zmax );
1489 glEnd();
1490
1491 if( ii != 0 )
1492 {
1493 glBegin( GL_LINES );
1494 xmin = ( brd_center_pos.x - delta ) * scale;
1495 glVertex3f( xmin, posy, zmin );
1496 glVertex3f( xmin, posy, zmax );
1497 glEnd();
1498 }
1499
1500 if( delta > xsize / 2.0f )
1501 break;
1502 }
1503
1504 // Draw horizontal grid lines on Z axis (parallel to X axis)
1505 for( int ii = 0; ; ii++ )
1506 {
1507 if( ii % 5 )
1508 glColor4f( gridColor.r, gridColor.g, gridColor.b, transparency );
1509 else
1510 glColor4f( gridColor_marker.r, gridColor_marker.g, gridColor_marker.b, transparency );
1511
1512 const double delta = ii * griSizeMM * pcbIUScale.IU_PER_MM * scale;
1513
1514 if( delta <= zmax )
1515 {
1516 // Draw grid lines on Z axis (positive Z axis coordinates)
1517 glBegin( GL_LINES );
1518 glVertex3f( xmin, posy, delta );
1519 glVertex3f( xmax, posy, delta );
1520 glEnd();
1521 }
1522
1523 if( delta <= -zmin && ( ii != 0 ) )
1524 {
1525 // Draw grid lines on Z axis (negative Z axis coordinates)
1526 glBegin( GL_LINES );
1527 glVertex3f( xmin, posy, -delta );
1528 glVertex3f( xmax, posy, -delta );
1529 glEnd();
1530 }
1531
1532 if( ( delta > zmax ) && ( delta > -zmin ) )
1533 break;
1534 }
1535
1536 glDisable( GL_BLEND );
1537
1538 glEndList();
1539}
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:330
bool IsFlipped() const
Definition footprint.h:524
std::vector< FP_3DMODEL > & Models()
Definition footprint.h:323
VECTOR2I GetPosition() const override
Definition footprint.h:327
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:105
double r
Red component.
Definition color4d.h:393
double g
Green component.
Definition color4d.h:394
double b
Blue component.
Definition color4d.h:395
static const LSET & PhysicalLayersMask()
Return a mask holding all layers which are physically realized.
Definition lset.cpp:697
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:122
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)
OPENGL_RENDER_LIST * m_postMachinePlugs
Board material plugs for backdrill/counterbore/countersink.
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.
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:334
@ LAYER_3D_USER_1
Definition layer_ids.h:567
@ LAYER_3D_SOLDERMASK_TOP
Definition layer_ids.h:560
@ LAYER_3D_SOLDERMASK_BOTTOM
Definition layer_ids.h:559
@ LAYER_3D_BOARD
Definition layer_ids.h:554
@ LAYER_3D_USER_45
Definition layer_ids.h:611
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:677
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.
see class PGM_BASE
void init_lights()
static SFVEC4F premultiplyAlpha(const SFVEC4F &aInput)
static float TransparencyControl(float aGrayColorValue, float aTransparency)
Attempt to control the transparency based on the gray value of the color.
#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