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 (C) 1992-2021 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, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
30#include <gal/opengl/kiglew.h> // Must be included first
31#include <iostream>
33#include "eda_3d_model_viewer.h"
34#include "../3d_rendering/opengl/opengl_utils.h"
35#include "../3d_cache/3d_cache.h"
36#include <wx/dcclient.h>
37#include <base_units.h>
38#include <gl_context_mgr.h>
40#include <pgm_base.h>
41#include <gal/dpi_scaling.h>
42#include <macros.h>
43
47#define UNITS3D_TO_UNITSPCB ( pcbIUScale.IU_PER_MM )
48
57const wxChar* EDA_3D_MODEL_VIEWER::m_logTrace = wxT( "KI_TRACE_EDA_3D_MODEL_VIEWER" );
58
59
60BEGIN_EVENT_TABLE( EDA_3D_MODEL_VIEWER, wxGLCanvas )
62
63 // mouse events
64 EVT_LEFT_DOWN( EDA_3D_MODEL_VIEWER::OnLeftDown )
66 EVT_MIDDLE_UP( EDA_3D_MODEL_VIEWER::OnMiddleUp )
67 EVT_MIDDLE_DOWN( EDA_3D_MODEL_VIEWER::OnMiddleDown)
68 EVT_RIGHT_DOWN( EDA_3D_MODEL_VIEWER::OnRightClick )
69 EVT_MOUSEWHEEL( EDA_3D_MODEL_VIEWER::OnMouseWheel )
71
72#ifdef USE_OSX_MAGNIFY_EVENT
73 EVT_MAGNIFY( EDA_3D_MODEL_VIEWER::OnMagnify )
74#endif
75
76 // other events
77 EVT_ERASE_BACKGROUND( EDA_3D_MODEL_VIEWER::OnEraseBackground )
78END_EVENT_TABLE()
79
80
81// This defines the range that all coord will have to be rendered.
82// It will use this value to convert to a normalized value between
83// -(RANGE_SCALE_3D/2) .. +(RANGE_SCALE_3D/2)
84#define RANGE_SCALE_3D 8.0f
85
86
87EDA_3D_MODEL_VIEWER::EDA_3D_MODEL_VIEWER( wxWindow* aParent, const int* aAttribList,
88 S3D_CACHE* aCacheManager ) :
89 HIDPI_GL_CANVAS( aParent, wxID_ANY, aAttribList, wxDefaultPosition, wxDefaultSize,
90 wxFULL_REPAINT_ON_RESIZE ),
91 m_trackBallCamera( RANGE_SCALE_3D * 4.0f ),
92 m_cacheManager( aCacheManager )
93{
94 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::EDA_3D_MODEL_VIEWER" ) );
95
96 m_ogl_initialized = false;
97 m_reload_is_needed = false;
98 m_ogl_3dmodel = nullptr;
99 m_3d_model = nullptr;
100 m_BiuTo3dUnits = 1.0;
101
102 m_glRC = nullptr;
103
104 COMMON_SETTINGS* settings = Pgm().GetCommonSettings();
105
106 const DPI_SCALING dpi{ settings, this };
107 SetScaleFactor( dpi.GetScaleFactor() );
108}
109
110
112{
113 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::~EDA_3D_MODEL_VIEWER" ) );
114
115 if( m_glRC )
116 {
118
119 delete m_ogl_3dmodel;
120 m_ogl_3dmodel = nullptr;
121
124 }
125}
126
127
129{
130 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::Set3DModel with a S3DMODEL" ) );
131
132 // Validate a3DModel pointers
133 wxASSERT( a3DModel.m_Materials != nullptr );
134 wxASSERT( a3DModel.m_Meshes != nullptr );
135 wxASSERT( a3DModel.m_MaterialsSize > 0 );
136 wxASSERT( a3DModel.m_MeshesSize > 0 );
137
138 // Delete the old model
139 delete m_ogl_3dmodel;
140 m_ogl_3dmodel = nullptr;
141
142 m_3d_model = nullptr;
143
144 if( ( a3DModel.m_Materials != nullptr ) && ( a3DModel.m_Meshes != nullptr )
145 && ( a3DModel.m_MaterialsSize > 0 ) && ( a3DModel.m_MeshesSize > 0 ) )
146 {
147 m_3d_model = &a3DModel;
148 m_reload_is_needed = true;
149 }
150
151 Refresh();
152}
153
154
155void EDA_3D_MODEL_VIEWER::Set3DModel( const wxString& aModelPathName)
156{
157 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::Set3DModel with a wxString" ) );
158
159 if( m_cacheManager )
160 {
161 const S3DMODEL* model = m_cacheManager->GetModel( aModelPathName, wxEmptyString );
162
163 if( model )
164 Set3DModel( (const S3DMODEL &)*model );
165 else
166 Clear3DModel();
167 }
168}
169
170
172{
173 // Delete the old model
174 m_reload_is_needed = false;
175
176 delete m_ogl_3dmodel;
177 m_ogl_3dmodel = nullptr;
178
179 m_3d_model = nullptr;
180
181 Refresh();
182}
183
184
186{
187 const GLenum err = glewInit();
188
189 if( GLEW_OK != err )
190 {
191 const wxString msgError = (const char*) glewGetErrorString( err );
192
193 wxLogMessage( msgError );
194 }
195 else
196 {
197 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::ogl_initialize Using GLEW version %s" ),
198 FROM_UTF8( (char*) glewGetString( GLEW_VERSION ) ) );
199 }
200
201 glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );
202 glHint( GL_LINE_SMOOTH_HINT, GL_NICEST );
203 glHint( GL_POLYGON_SMOOTH_HINT, GL_NICEST );
204
205 glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
206 glEnable( GL_DEPTH_TEST );
207 glEnable( GL_CULL_FACE );
208 glShadeModel( GL_SMOOTH );
209 glEnable( GL_LINE_SMOOTH );
210 glEnable( GL_NORMALIZE );
211
212 // Setup light
213 // https://www.opengl.org/sdk/docs/man2/xhtml/glLight.xml
214 const GLfloat ambient[] = { 0.01f, 0.01f, 0.01f, 1.0f };
215 const GLfloat diffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f };
216 const GLfloat specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
217
218 // defines a directional light that points along the negative z-axis
219 const GLfloat position[] = { 0.0f, 0.0f, 2.0f * RANGE_SCALE_3D, 0.0f };
220
221 const GLfloat lmodel_ambient[] = { 0.0f, 0.0f, 0.0f, 1.0f };
222
223 glLightfv( GL_LIGHT0, GL_AMBIENT, ambient );
224 glLightfv( GL_LIGHT0, GL_DIFFUSE, diffuse );
225 glLightfv( GL_LIGHT0, GL_SPECULAR, specular );
226 glLightfv( GL_LIGHT0, GL_POSITION, position );
227 glLightModelfv( GL_LIGHT_MODEL_AMBIENT, lmodel_ambient );
228}
229
230
232{
233 glEnable( GL_COLOR_MATERIAL );
234 glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );
235
236 const SFVEC4F specular = SFVEC4F( 0.1f, 0.1f, 0.1f, 1.0f );
237
238 glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, &specular.r );
239 glMaterialf( GL_FRONT_AND_BACK, GL_SHININESS, 96.0f );
240}
241
242
243void EDA_3D_MODEL_VIEWER::OnPaint( wxPaintEvent& event )
244{
245 event.Skip( false );
246
247 // SwapBuffer requires the window to be shown before calling
248 if( !IsShownOnScreen() )
249 {
250 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::OnPaint !IsShown" ) );
251 return;
252 }
253
254 // "Makes the OpenGL state that is represented by the OpenGL rendering
255 // context context current, i.e. it will be used by all subsequent OpenGL calls.
256 // This function may only be called when the window is shown on screen"
257 if( m_glRC == nullptr )
259
260 // CreateCtx could and does fail per sentry crash events, lets be graceful
261 if( m_glRC == nullptr )
262 {
263 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::OnPaint creating gl context failed" ) );
264 return;
265 }
266
268
269 // Set the OpenGL viewport according to the client size of this canvas.
270 // This is done here rather than in a wxSizeEvent handler because our
271 // OpenGL rendering context (and thus viewport setting) is used with
272 // multiple canvases: If we updated the viewport in the wxSizeEvent
273 // handler, changing the size of one canvas causes a viewport setting that
274 // is wrong when next another canvas is repainted.
275 wxSize clientSize = GetNativePixelSize();
276
277 if( !m_ogl_initialized )
278 {
279 m_ogl_initialized = true;
281 }
282
284 {
285 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::OnPaint m_reload_is_needed" ) );
286
287 m_reload_is_needed = false;
288 m_ogl_3dmodel = new MODEL_3D( *m_3d_model, MATERIAL_MODE::NORMAL );
289
290 // It convert a model as it was a board, so get the max size dimension of the board
291 // and compute the conversion scale
293 (double) RANGE_SCALE_3D
295 }
296
297 glViewport( 0, 0, clientSize.x, clientSize.y );
298
300
301 // clear color and depth buffers
302 glEnable( GL_DEPTH_TEST );
303 glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
304 glClearDepth( 1.0f );
305 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
306
307 // Set projection and modelview matrices
308 glMatrixMode( GL_PROJECTION );
309 glLoadMatrixf( glm::value_ptr( m_trackBallCamera.GetProjectionMatrix() ) );
310 glMatrixMode( GL_MODELVIEW );
311 glLoadMatrixf( glm::value_ptr( m_trackBallCamera.GetViewMatrix() ) );
312 glEnable( GL_LIGHTING );
313 glEnable( GL_LIGHT0 );
314
315 // Render Model
316 if( m_ogl_3dmodel )
317 {
318 glPushMatrix();
319
320 double modelunit_to_3d_units_factor = m_BiuTo3dUnits * UNITS3D_TO_UNITSPCB;
321
322 glScaled( modelunit_to_3d_units_factor, modelunit_to_3d_units_factor,
323 modelunit_to_3d_units_factor );
324
325 // Center model in the render viewport
326 const SFVEC3F model_center = m_ogl_3dmodel->GetBBox().GetCenter();
327
328 glTranslatef( -model_center.x, -model_center.y, -model_center.z );
329
331
332 m_ogl_3dmodel->DrawOpaque( false );
333
334 glDepthMask( GL_FALSE );
335 glEnable( GL_BLEND );
336 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
337
338 m_ogl_3dmodel->DrawTransparent( 1.0f, false );
339
340 glDisable( GL_BLEND );
341 glDepthMask( GL_TRUE );
342
344
345 glPopMatrix();
346 }
347
348 // YxY squared view port
349 glViewport( 0, 0, clientSize.y / 8 , clientSize.y / 8 );
350 glClear( GL_DEPTH_BUFFER_BIT );
351
352 glMatrixMode( GL_PROJECTION );
353 glLoadIdentity();
354 gluPerspective( 45.0f, 1.0f, 0.01f, RANGE_SCALE_3D * 2.0f );
355
356 glMatrixMode( GL_MODELVIEW );
357 glLoadIdentity();
358
359 const glm::mat4 TranslationMatrix = glm::translate( glm::mat4(1.0f),
360 SFVEC3F( 0.0f, 0.0f, -RANGE_SCALE_3D ) );
361
362 const glm::mat4 ViewMatrix = TranslationMatrix * m_trackBallCamera.GetRotationMatrix();
363
364 glLoadMatrixf( glm::value_ptr( ViewMatrix ) );
365
367
368 glColor3f( 0.9f, 0.0f, 0.0f );
369 DrawRoundArrow( SFVEC3F( 0.0f, 0.0f, 0.0f ), SFVEC3F( RANGE_SCALE_3D / 2.65f, 0.0f, 0.0f ),
370 0.275f );
371
372 glColor3f( 0.0f, 0.9f, 0.0f );
373 DrawRoundArrow( SFVEC3F( 0.0f, 0.0f, 0.0f ), SFVEC3F( 0.0f, RANGE_SCALE_3D / 2.65f, 0.0f ),
374 0.275f );
375
376 glColor3f( 0.0f, 0.0f, 0.9f );
377 DrawRoundArrow( SFVEC3F( 0.0f, 0.0f, 0.0f ), SFVEC3F( 0.0f, 0.0f, RANGE_SCALE_3D / 2.65f ),
378 0.275f );
379
380 // "Swaps the double-buffer of this window, making the back-buffer the
381 // front-buffer and vice versa, so that the output of the previous OpenGL
382 // commands is displayed on the window."
383 SwapBuffers();
384
386}
387
388
389void EDA_3D_MODEL_VIEWER::OnEraseBackground( wxEraseEvent& event )
390{
391 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::OnEraseBackground" ) );
392 // Do nothing, to avoid flashing.
393}
394
395
396void EDA_3D_MODEL_VIEWER::OnMouseWheel( wxMouseEvent& event )
397{
398 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::OnMouseWheel" ) );
399
400 if( event.ShiftDown() )
401 {
402 //if( event.GetWheelRotation() < 0 )
403 //SetView3D( WXK_UP ); // move up
404 //else
405 //SetView3D( WXK_DOWN ); // move down
406 }
407 else if( event.ControlDown() )
408 {
409 //if( event.GetWheelRotation() > 0 )
410 //SetView3D( WXK_RIGHT ); // move right
411 //else
412 //SetView3D( WXK_LEFT ); // move left
413 }
414 else
415 {
416 m_trackBallCamera.Zoom( event.GetWheelRotation() > 0 ? 1.1f : 1/1.1f );
417
418 //DisplayStatus();
419 Refresh( false );
420 }
421
422 m_trackBallCamera.SetCurMousePosition( event.GetPosition() );
423}
424
425
426#ifdef USE_OSX_MAGNIFY_EVENT
427void EDA_3D_MODEL_VIEWER::OnMagnify( wxMouseEvent& event )
428{
429}
430#endif
431
432
433void EDA_3D_MODEL_VIEWER::OnMouseMove( wxMouseEvent& event )
434{
435 const wxSize& nativeWinSize = GetNativePixelSize();
436 const wxPoint& nativePosition = GetNativePosition( event.GetPosition() );
437
438 m_trackBallCamera.SetCurWindowSize( nativeWinSize );
439
440 if( event.Dragging() )
441 {
442 if( event.LeftIsDown() ) // Drag
443 m_trackBallCamera.Drag( nativePosition );
444
445 // orientation has changed, redraw mesh
446 Refresh( false );
447 }
448
449 m_trackBallCamera.SetCurMousePosition( nativePosition );
450}
451
452
453void EDA_3D_MODEL_VIEWER::OnLeftDown( wxMouseEvent& event )
454{
455 event.Skip();
456}
457
458
459void EDA_3D_MODEL_VIEWER::OnLeftUp( wxMouseEvent& event )
460{
461 event.Skip();
462}
463
464
465void EDA_3D_MODEL_VIEWER::OnMiddleDown( wxMouseEvent& event )
466{
467 event.Skip();
468}
469
470
471void EDA_3D_MODEL_VIEWER::OnMiddleUp( wxMouseEvent& event )
472{
473 event.Skip();
474}
475
476
477void EDA_3D_MODEL_VIEWER::OnRightClick( wxMouseEvent& event )
478{
479 event.Skip();
480}
481
#define RANGE_SCALE_3D
This defines the range that all coord will have to be rendered.
Definition: board_adapter.h:62
glm::mat4 GetRotationMatrix() const
Get the rotation matrix to be applied in a transformation camera.
Definition: camera.cpp:182
const glm::mat4 & GetProjectionMatrix() const
Definition: camera.cpp:414
bool Zoom(float aFactor)
Definition: camera.cpp:538
bool SetCurWindowSize(const wxSize &aSize)
Update the windows size of the camera.
Definition: camera.cpp:511
const glm::mat4 & GetViewMatrix() const
Definition: camera.cpp:450
void SetCurMousePosition(const wxPoint &aPosition)
Update the current mouse position without make any new calculations on camera.
Definition: camera.cpp:494
Class to handle configuration and automatic determination of the DPI scale to use for canvases.
Definition: dpi_scaling.h:37
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)
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)
EDA_3D_MODEL_VIEWER(wxWindow *aParent, const int *aAttribList=nullptr, S3D_CACHE *aCacheManager=nullptr)
Create a new 3D Canvas with a attribute list.
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.
void LockCtx(wxGLContext *aContext, wxGLCanvas *aCanvas)
Set a context as current and prevents other canvases from switching it.
static GL_CONTEXT_MANAGER & Get()
Return the GL_CONTEXT_MANAGER instance (singleton).
wxGLContext * CreateCtx(wxGLCanvas *aCanvas, const wxGLContext *aOther=nullptr)
Create a managed OpenGL context.
wxGLCanvas wrapper for HiDPI/Retina support.
void SetScaleFactor(double aFactor)
Set the canvas scale factor, probably for a hi-DPI display.
virtual wxSize GetNativePixelSize() const
wxPoint GetNativePosition(const wxPoint &aPoint) const
Convert the given point from client coordinates to native pixel coordinates.
static void EndDrawMulti()
Cleanup render states after drawing multiple models.
Definition: 3d_model.cpp:404
static void BeginDrawMulti(bool aUseColorInformation)
Set some basic render states before drawing multiple models.
Definition: 3d_model.cpp:388
void DrawTransparent(float aOpacity, bool aUseSelectedMaterial, SFVEC3F aSelectionColor=SFVEC3F(0.0f)) const
Render the model into the current context.
Definition: 3d_model.h:63
void DrawOpaque(bool aUseSelectedMaterial, SFVEC3F aSelectionColor=SFVEC3F(0.0f)) const
Render the model into the current context.
Definition: 3d_model.h:55
const BBOX_3D & GetBBox() const
Get the main bounding box.
Definition: 3d_model.h:103
Cache for storing the 3D shapes.
Definition: 3d_cache.h:53
S3DMODEL * GetModel(const wxString &aModelFileName, const wxString &aBasePath)
Attempt to load the scene data for a model and to translate it into an S3D_MODEL structure for displa...
Definition: 3d_cache.cpp:635
void Drag(const wxPoint &aNewMousePosition) override
Calculate a new mouse drag position.
Definition: track_ball.cpp:53
#define UNITS3D_TO_UNITSPCB
Implements a model viewer canvas.
#define RANGE_SCALE_3D
Implements a model viewer canvas.
static const wxChar * m_logTrace
Trace mask used to enable or disable the trace output of this class.
This file contains miscellaneous commonly used macros and functions.
static wxString FROM_UTF8(const char *cstring)
Convert a UTF8 encoded C string to a wxString for all wxWidgets build modes.
Definition: macros.h:110
void DrawRoundArrow(SFVEC3F aPosition, SFVEC3F aTargetPos, float aSize)
Draw a round arrow.
void Refresh()
Update the board display after modifying it by a python script (note: it is automatically called by a...
see class PGM_BASE
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:115
SFVEC3F GetCenter() const
Return the center point of the bounding box.
Definition: bbox_3d.cpp:132
float GetMaxDimension() const
Definition: bbox_3d.cpp:167
Store the a model based on meshes and materials.
Definition: c3dmodel.h:91
SMATERIAL * m_Materials
The materials list of this model.
Definition: c3dmodel.h:96
unsigned int m_MeshesSize
Number of meshes in the array.
Definition: c3dmodel.h:92
SMESH * m_Meshes
The meshes list of this model.
Definition: c3dmodel.h:93
unsigned int m_MaterialsSize
Number of materials in the material array.
Definition: c3dmodel.h:95
glm::vec3 SFVEC3F
Definition: xv3d_types.h:44
glm::vec4 SFVEC4F
Definition: xv3d_types.h:46