KiCad PCB EDA Suite
Loading...
Searching...
No Matches
eda_3d_model_viewer.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-2016 Mario Luzeiro <[email protected]>
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
25
26#include <kicad_gl/kiglu.h> // Must be included first
27#include <kicad_gl/gl_utils.h>
29
30#include <iostream>
31#include <optional>
32#include <wx/dcclient.h>
33
34#include <base_units.h>
35#include <build_version.h>
37#include <dpi_scaling_common.h>
38#include <macros.h>
39#include <pgm_base.h>
41
45#include "3d_cache/3d_cache.h"
46#include "eda_3d_model_viewer.h"
47
51#define UNITS3D_TO_UNITSPCB ( pcbIUScale.IU_PER_MM )
52
61const wxChar* EDA_3D_MODEL_VIEWER::m_logTrace = wxT( "KI_TRACE_EDA_3D_MODEL_VIEWER" );
62
63
64BEGIN_EVENT_TABLE( EDA_3D_MODEL_VIEWER, wxGLCanvas )
66
67 // mouse events
68 EVT_LEFT_DOWN( EDA_3D_MODEL_VIEWER::OnLeftDown )
70 EVT_MIDDLE_UP( EDA_3D_MODEL_VIEWER::OnMiddleUp )
71 EVT_MIDDLE_DOWN( EDA_3D_MODEL_VIEWER::OnMiddleDown)
72 EVT_RIGHT_DOWN( EDA_3D_MODEL_VIEWER::OnRightClick )
73 EVT_MOUSEWHEEL( EDA_3D_MODEL_VIEWER::OnMouseWheel )
75
76#ifdef USE_OSX_MAGNIFY_EVENT
77 EVT_MAGNIFY( EDA_3D_MODEL_VIEWER::OnMagnify )
78#endif
79
80 // other events
81 EVT_ERASE_BACKGROUND( EDA_3D_MODEL_VIEWER::OnEraseBackground )
82END_EVENT_TABLE()
83
84
85// This defines the range that all coord will have to be rendered.
86// It will use this value to convert to a normalized value between
87// -(RANGE_SCALE_3D/2) .. +(RANGE_SCALE_3D/2)
88#define RANGE_SCALE_3D 8.0f
89
90
91EDA_3D_MODEL_VIEWER::EDA_3D_MODEL_VIEWER( wxWindow* aParent, const wxGLAttributes& aGLAttribs,
92 S3D_CACHE* aCacheManager ) :
93 HIDPI_GL_CANVAS( EDA_DRAW_PANEL_GAL::GetVcSettings(), aParent, aGLAttribs, wxID_ANY,
94 wxDefaultPosition, wxDefaultSize,
95 wxFULL_REPAINT_ON_RESIZE ),
97 m_cacheManager( aCacheManager )
98{
99 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::EDA_3D_MODEL_VIEWER" ) );
100
101 m_ogl_initialized = false;
102 m_reload_is_needed = false;
103 m_ogl_3dmodel = nullptr;
104 m_3d_model = nullptr;
105 m_BiuTo3dUnits = 1.0;
106
107 m_glRC = nullptr;
108}
109
110
112{
113 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::~EDA_3D_MODEL_VIEWER" ) );
115 wxASSERT( gl_mgr );
116
117 if( m_glRC && gl_mgr )
118 {
119 // Without the native window our context cannot be made current, and the model's
120 // buffers must not be deleted against whichever context is
121 std::optional<ORPHANED_GL_OBJECTS> orphaned;
122
123 if( !gl_mgr->LockCtx( m_glRC, this ) )
124 orphaned.emplace();
125
126 delete m_ogl_3dmodel;
127 m_ogl_3dmodel = nullptr;
128
129 gl_mgr->UnlockCtx( m_glRC );
130 gl_mgr->DestroyCtx( m_glRC );
131 }
132}
133
134
136{
137 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::Set3DModel with a S3DMODEL" ) );
138
139 // Validate a3DModel pointers
140 wxASSERT( a3DModel.m_Materials != nullptr );
141 wxASSERT( a3DModel.m_Meshes != nullptr );
142 wxASSERT( a3DModel.m_MaterialsSize > 0 );
143 wxASSERT( a3DModel.m_MeshesSize > 0 );
144
145 // Delete the old model
146 delete m_ogl_3dmodel;
147 m_ogl_3dmodel = nullptr;
148
149 m_3d_model = nullptr;
150
151 if( ( a3DModel.m_Materials != nullptr ) && ( a3DModel.m_Meshes != nullptr )
152 && ( a3DModel.m_MaterialsSize > 0 ) && ( a3DModel.m_MeshesSize > 0 ) )
153 {
154 m_3d_model = &a3DModel;
155 m_reload_is_needed = true;
156 }
157
158 Refresh();
159}
160
161
162void EDA_3D_MODEL_VIEWER::Set3DModel( const wxString& aModelPathName)
163{
164 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::Set3DModel with a wxString" ) );
165
166 if( m_cacheManager )
167 {
168 const S3DMODEL* model = m_cacheManager->GetModel( aModelPathName, wxEmptyString, {} );
169
170 if( model )
171 Set3DModel( (const S3DMODEL &)*model );
172 else
173 Clear3DModel();
174 }
175}
176
177
179{
180 // Delete the old model
181 m_reload_is_needed = false;
182
183 delete m_ogl_3dmodel;
184 m_ogl_3dmodel = nullptr;
185
186 m_3d_model = nullptr;
187
188 Refresh();
189}
190
191
193{
195
196 const int glVersion = gladLoaderLoadGL();
197
198 if( glVersion == 0 )
199 {
200 wxLogTrace( m_logTrace, wxT( "Failed to load OpenGL via loader" ) );
201 return false;
202 }
203
204 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::ogl_initialize Using OpenGL version %s" ),
205 From_UTF8( (char*) glGetString( GL_VERSION ) ) );
206
207 SetOpenGLInfo( (const char*) glGetString( GL_VENDOR ), (const char*) glGetString( GL_RENDERER ),
208 (const char*) glGetString( GL_VERSION ) );
209
210 glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );
211 glHint( GL_LINE_SMOOTH_HINT, GL_NICEST );
212 glHint( GL_POLYGON_SMOOTH_HINT, GL_NICEST );
213
214 glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
215 glEnable( GL_DEPTH_TEST );
216 glEnable( GL_CULL_FACE );
217 glShadeModel( GL_SMOOTH );
218 glEnable( GL_LINE_SMOOTH );
219 glEnable( GL_NORMALIZE );
220
221 // Setup light
222 // https://www.opengl.org/sdk/docs/man2/xhtml/glLight.xml
223 const GLfloat ambient[] = { 0.01f, 0.01f, 0.01f, 1.0f };
224 const GLfloat diffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f };
225 const GLfloat specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
226
227 // defines a directional light that points along the negative z-axis
228 const GLfloat position[] = { 0.0f, 0.0f, 2.0f * RANGE_SCALE_3D, 0.0f };
229
230 const GLfloat lmodel_ambient[] = { 0.0f, 0.0f, 0.0f, 1.0f };
231
232 glLightfv( GL_LIGHT0, GL_AMBIENT, ambient );
233 glLightfv( GL_LIGHT0, GL_DIFFUSE, diffuse );
234 glLightfv( GL_LIGHT0, GL_SPECULAR, specular );
235 glLightfv( GL_LIGHT0, GL_POSITION, position );
236 glLightModelfv( GL_LIGHT_MODEL_AMBIENT, lmodel_ambient );
237
238 return true;
239}
240
241
243{
244 glEnable( GL_COLOR_MATERIAL );
245 glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );
246
247 const SFVEC4F specular = SFVEC4F( 0.1f, 0.1f, 0.1f, 1.0f );
248
249 glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, &specular.r );
250 glMaterialf( GL_FRONT_AND_BACK, GL_SHININESS, 96.0f );
251}
252
253
254void EDA_3D_MODEL_VIEWER::OnPaint( wxPaintEvent& event )
255{
256 event.Skip( false );
257 wxPaintDC dc( this );
258
260 return;
261
262 // SwapBuffer requires the window to be shown before calling
263 if( !IsShownOnScreen() )
264 {
265 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::OnPaint !IsShown" ) );
266 return;
267 }
268
269 // "Makes the OpenGL state that is represented by the OpenGL rendering
270 // context context current, i.e. it will be used by all subsequent OpenGL calls.
271 // This function may only be called when the window is shown on screen"
273
274 if( !gl_mgr )
275 return;
276
277 if( m_glRC == nullptr )
278 m_glRC = gl_mgr->CreateCtx( this );
279
280 // CreateCtx could and does fail per sentry crash events, lets be graceful
281 if( m_glRC == nullptr )
282 {
283 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::OnPaint creating gl context failed" ) );
284 return;
285 }
286
287 gl_mgr->LockCtx( m_glRC, this );
288
289 // Set the OpenGL viewport according to the client size of this canvas.
290 // This is done here rather than in a wxSizeEvent handler because our
291 // OpenGL rendering context (and thus viewport setting) is used with
292 // multiple canvases: If we updated the viewport in the wxSizeEvent
293 // handler, changing the size of one canvas causes a viewport setting that
294 // is wrong when next another canvas is repainted.
295 wxSize clientSize = GetNativePixelSize();
296
297 if( !m_ogl_initialized )
298 {
300
301 if( !m_ogl_initialized )
302 {
303 m_ogl_init_failed = true;
304 gl_mgr->UnlockCtx( m_glRC );
305 return;
306 }
307 }
308
310 {
311 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::OnPaint m_reload_is_needed" ) );
312
313 m_reload_is_needed = false;
315
316 // It convert a model as it was a board, so get the max size dimension of the board
317 // and compute the conversion scale
319 (double) RANGE_SCALE_3D
320 / ( (double) m_ogl_3dmodel->GetBBox().GetMaxDimension() * UNITS3D_TO_UNITSPCB );
321 }
322
323 glViewport( 0, 0, clientSize.x, clientSize.y );
324
325 m_trackBallCamera.SetCurWindowSize( clientSize );
326
327 // clear color and depth buffers
328 glEnable( GL_DEPTH_TEST );
329 glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
330 glClearDepth( 1.0f );
331 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
332
333 // Set projection and modelview matrices
334 glMatrixMode( GL_PROJECTION );
335 glLoadMatrixf( glm::value_ptr( m_trackBallCamera.GetProjectionMatrix() ) );
336 glMatrixMode( GL_MODELVIEW );
337 glLoadMatrixf( glm::value_ptr( m_trackBallCamera.GetViewMatrix() ) );
338 glEnable( GL_LIGHTING );
339 glEnable( GL_LIGHT0 );
340
341 // Render Model
342 if( m_ogl_3dmodel )
343 {
344 glPushMatrix();
345
346 double modelunit_to_3d_units_factor = m_BiuTo3dUnits * UNITS3D_TO_UNITSPCB;
347
348 glScaled( modelunit_to_3d_units_factor, modelunit_to_3d_units_factor,
349 modelunit_to_3d_units_factor );
350
351 // Center model in the render viewport
352 const SFVEC3F model_center = m_ogl_3dmodel->GetBBox().GetCenter();
353
354 glTranslatef( -model_center.x, -model_center.y, -model_center.z );
355
356 m_ogl_3dmodel->BeginDrawMulti( true );
357
358 m_ogl_3dmodel->DrawOpaque( false );
359
360 glDepthMask( GL_FALSE );
361 glEnable( GL_BLEND );
362 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
363
364 m_ogl_3dmodel->DrawTransparent( 1.0f, false );
365
366 glDisable( GL_BLEND );
367 glDepthMask( GL_TRUE );
368
369 m_ogl_3dmodel->EndDrawMulti();
370
371 glPopMatrix();
372 }
373
374 // YxY squared view port
375 glViewport( 0, 0, clientSize.y / 8 , clientSize.y / 8 );
376 glClear( GL_DEPTH_BUFFER_BIT );
377
378 glMatrixMode( GL_PROJECTION );
379 glLoadIdentity();
380 gluPerspective( 45.0f, 1.0f, 0.01f, RANGE_SCALE_3D * 2.0f );
381
382 glMatrixMode( GL_MODELVIEW );
383 glLoadIdentity();
384
385 const glm::mat4 TranslationMatrix = glm::translate( glm::mat4(1.0f),
386 SFVEC3F( 0.0f, 0.0f, -RANGE_SCALE_3D ) );
387
388 const glm::mat4 ViewMatrix = TranslationMatrix * m_trackBallCamera.GetRotationMatrix();
389
390 glLoadMatrixf( glm::value_ptr( ViewMatrix ) );
391
393
394 glColor3f( 0.9f, 0.0f, 0.0f );
395 DrawRoundArrow( SFVEC3F( 0.0f, 0.0f, 0.0f ), SFVEC3F( RANGE_SCALE_3D / 2.65f, 0.0f, 0.0f ),
396 0.275f );
397
398 glColor3f( 0.0f, 0.9f, 0.0f );
399 DrawRoundArrow( SFVEC3F( 0.0f, 0.0f, 0.0f ), SFVEC3F( 0.0f, RANGE_SCALE_3D / 2.65f, 0.0f ),
400 0.275f );
401
402 glColor3f( 0.0f, 0.0f, 0.9f );
403 DrawRoundArrow( SFVEC3F( 0.0f, 0.0f, 0.0f ), SFVEC3F( 0.0f, 0.0f, RANGE_SCALE_3D / 2.65f ),
404 0.275f );
405
406 // "Swaps the double-buffer of this window, making the back-buffer the
407 // front-buffer and vice versa, so that the output of the previous OpenGL
408 // commands is displayed on the window."
409 SwapBuffers();
410
411 gl_mgr->UnlockCtx( m_glRC );
412}
413
414
415void EDA_3D_MODEL_VIEWER::OnEraseBackground( wxEraseEvent& event )
416{
417 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::OnEraseBackground" ) );
418 // Do nothing, to avoid flashing.
419}
420
421
422void EDA_3D_MODEL_VIEWER::OnMouseWheel( wxMouseEvent& event )
423{
424 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::OnMouseWheel" ) );
425
426 if( event.ShiftDown() )
427 {
428 //if( event.GetWheelRotation() < 0 )
429 //SetView3D( VIEW_3D_TYPE::VIEW3D_PAN_UP ); // move up
430 //else
431 //SetView3D( VIEW_3D_TYPE::VIEW3D_PAN_DOWN ); // move down
432 }
433 else if( event.ControlDown() )
434 {
435 //if( event.GetWheelRotation() > 0 )
436 //SetView3D( VIEW_3D_TYPE::VIEW3D_PAN_RIGHT ); // move right
437 //else
438 //SetView3D( VIEW_3D_TYPE::VIEW3D_PAN_LEFT ); // move left
439 }
440 else
441 {
442 m_trackBallCamera.Zoom( event.GetWheelRotation() > 0 ? 1.1f : 1/1.1f );
443
444 //DisplayStatus();
445 Refresh( false );
446 }
447
448 m_trackBallCamera.SetCurMousePosition( event.GetPosition() );
449}
450
451
452#ifdef USE_OSX_MAGNIFY_EVENT
453void EDA_3D_MODEL_VIEWER::OnMagnify( wxMouseEvent& event )
454{
455}
456#endif
457
458
459void EDA_3D_MODEL_VIEWER::OnMouseMove( wxMouseEvent& event )
460{
461 const wxSize& nativeWinSize = GetNativePixelSize();
462 const wxPoint& nativePosition = GetNativePosition( event.GetPosition() );
463
464 m_trackBallCamera.SetCurWindowSize( nativeWinSize );
465
466 if( event.Dragging() )
467 {
468 if( event.LeftIsDown() ) // Drag
469 m_trackBallCamera.Drag( nativePosition );
470
471 // orientation has changed, redraw mesh
472 Refresh( false );
473 }
474
475 m_trackBallCamera.SetCurMousePosition( nativePosition );
476}
477
478
479void EDA_3D_MODEL_VIEWER::OnLeftDown( wxMouseEvent& event )
480{
481 event.Skip();
482}
483
484
485void EDA_3D_MODEL_VIEWER::OnLeftUp( wxMouseEvent& event )
486{
487 event.Skip();
488}
489
490
491void EDA_3D_MODEL_VIEWER::OnMiddleDown( wxMouseEvent& event )
492{
493 event.Skip();
494}
495
496
497void EDA_3D_MODEL_VIEWER::OnMiddleUp( wxMouseEvent& event )
498{
499 event.Skip();
500}
501
502
503void EDA_3D_MODEL_VIEWER::OnRightClick( wxMouseEvent& event )
504{
505 event.Skip();
506}
507
@ NORMAL
Use all material properties from model file.
Definition 3d_enums.h:68
#define RANGE_SCALE_3D
This defines the range that all coord will have to be rendered.
void SetOpenGLInfo(const char *aVendor, const char *aRenderer, const char *aVersion)
A setter for OpenGL info when it's initialized.
void SetOpenGLBackendInfo(wxString aBackend)
A setter for OpenGL backend info after the canvas is created.
Implement a canvas based on a wxGLCanvas.
MODEL_3D * m_ogl_3dmodel
Class holder for 3d model to display on openGL.
void OnMiddleDown(wxMouseEvent &event)
EDA_3D_MODEL_VIEWER(wxWindow *aParent, const wxGLAttributes &aGLAttribs, S3D_CACHE *aCacheManager=nullptr)
Create a new 3D Canvas with a attribute list.
void OnMouseMove(wxMouseEvent &event)
void OnEraseBackground(wxEraseEvent &event)
S3D_CACHE * m_cacheManager
Optional cache manager.
void Clear3DModel()
Unload the displayed 3D model.
bool m_reload_is_needed
Flag that we have a new model and it need to be reloaded when the paint is called.
void OnLeftDown(wxMouseEvent &event)
double m_BiuTo3dUnits
factor to convert the model or any other items to keep it in relation to the +/-RANGE_SCALE_3D (it is...
void OnRightClick(wxMouseEvent &event)
void Set3DModel(const S3DMODEL &a3DModel)
Set this model to be displayed.
void OnPaint(wxPaintEvent &event)
bool m_ogl_initialized
Flag if open gl was initialized.
const S3DMODEL * m_3d_model
Original 3d model data.
TRACK_BALL m_trackBallCamera
Camera used in this canvas.
wxGLContext * m_glRC
openGL context
void OnMiddleUp(wxMouseEvent &event)
void OnLeftUp(wxMouseEvent &event)
void OnMouseWheel(wxMouseEvent &event)
void UnlockCtx(wxGLContext *aContext)
Allow other canvases to bind an OpenGL context.
void DestroyCtx(wxGLContext *aContext)
Destroy a managed OpenGL context.
bool LockCtx(wxGLContext *aContext, wxGLCanvas *aCanvas)
Set a context as current and prevents other canvases from switching it.
wxGLContext * CreateCtx(wxGLCanvas *aCanvas, const wxGLContext *aOther=nullptr)
Create a managed OpenGL context.
static wxString DetectGLBackend(wxGLCanvas *aCanvas)
Definition gl_utils.cpp:50
HIDPI_GL_CANVAS(const KIGFX::VC_SETTINGS &aSettings, wxWindow *aParent, const wxGLAttributes &aGLAttribs, wxWindowID aId=wxID_ANY, const wxPoint &aPos=wxDefaultPosition, const wxSize &aSize=wxDefaultSize, long aStyle=0, const wxString &aName=wxGLCanvasName, const wxPalette &aPalette=wxNullPalette)
virtual wxSize GetNativePixelSize() const
wxPoint GetNativePosition(const wxPoint &aPoint) const
Convert the given point from client coordinates to native pixel coordinates.
GL_CONTEXT_MANAGER * GetGLContextManager()
Definition pgm_base.h:113
Cache for storing the 3D shapes.
Definition 3d_cache.h:53
#define UNITS3D_TO_UNITSPCB
Implements a model viewer canvas.
Implements a model viewer canvas.
static const wxChar * m_logTrace
Trace mask used to enable or disable the trace output of this class.
static wxString From_UTF8(const char *cstring)
Convert a UTF8 encoded C string to a wxString for all wxWidgets build modes.
Definition idf_helpers.h:44
This file contains miscellaneous commonly used macros and functions.
void DrawRoundArrow(SFVEC3F aPosition, SFVEC3F aTargetPos, float aSize)
Draw a round arrow.
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
Store the a model based on meshes and materials.
Definition c3dmodel.h:111
SMATERIAL * m_Materials
The materials list of this model.
Definition c3dmodel.h:116
unsigned int m_MeshesSize
Number of meshes in the array.
Definition c3dmodel.h:112
SMESH * m_Meshes
The meshes list of this model.
Definition c3dmodel.h:113
unsigned int m_MaterialsSize
Number of materials in the material array.
Definition c3dmodel.h:115
KIBIS_MODEL * model
glm::vec3 SFVEC3F
Definition xv3d_types.h:40
glm::vec4 SFVEC4F
Definition xv3d_types.h:42