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-2024 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 <build_version.h>
41#include <pgm_base.h>
42#include <dpi_scaling_common.h>
44#include <macros.h>
45
49#define UNITS3D_TO_UNITSPCB ( pcbIUScale.IU_PER_MM )
50
59const wxChar* EDA_3D_MODEL_VIEWER::m_logTrace = wxT( "KI_TRACE_EDA_3D_MODEL_VIEWER" );
60
61
62BEGIN_EVENT_TABLE( EDA_3D_MODEL_VIEWER, wxGLCanvas )
64
65 // mouse events
66 EVT_LEFT_DOWN( EDA_3D_MODEL_VIEWER::OnLeftDown )
68 EVT_MIDDLE_UP( EDA_3D_MODEL_VIEWER::OnMiddleUp )
69 EVT_MIDDLE_DOWN( EDA_3D_MODEL_VIEWER::OnMiddleDown)
70 EVT_RIGHT_DOWN( EDA_3D_MODEL_VIEWER::OnRightClick )
71 EVT_MOUSEWHEEL( EDA_3D_MODEL_VIEWER::OnMouseWheel )
73
74#ifdef USE_OSX_MAGNIFY_EVENT
75 EVT_MAGNIFY( EDA_3D_MODEL_VIEWER::OnMagnify )
76#endif
77
78 // other events
79 EVT_ERASE_BACKGROUND( EDA_3D_MODEL_VIEWER::OnEraseBackground )
80END_EVENT_TABLE()
81
82
83// This defines the range that all coord will have to be rendered.
84// It will use this value to convert to a normalized value between
85// -(RANGE_SCALE_3D/2) .. +(RANGE_SCALE_3D/2)
86#define RANGE_SCALE_3D 8.0f
87
88
89EDA_3D_MODEL_VIEWER::EDA_3D_MODEL_VIEWER( wxWindow* aParent, const wxGLAttributes& aGLAttribs,
90 S3D_CACHE* aCacheManager ) :
91 HIDPI_GL_CANVAS( EDA_DRAW_PANEL_GAL::GetVcSettings(), aParent, aGLAttribs, wxID_ANY,
92 wxDefaultPosition, wxDefaultSize,
93 wxFULL_REPAINT_ON_RESIZE ),
94 m_trackBallCamera( RANGE_SCALE_3D * 4.0f ),
95 m_cacheManager( aCacheManager )
96{
97 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::EDA_3D_MODEL_VIEWER" ) );
98
99 m_ogl_initialized = false;
100 m_reload_is_needed = false;
101 m_ogl_3dmodel = nullptr;
102 m_3d_model = nullptr;
103 m_BiuTo3dUnits = 1.0;
104
105 m_glRC = nullptr;
106
107 COMMON_SETTINGS* settings = Pgm().GetCommonSettings();
108
109 const DPI_SCALING_COMMON dpi{ settings, this };
110 SetScaleFactor( dpi.GetScaleFactor() );
111}
112
113
115{
116 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::~EDA_3D_MODEL_VIEWER" ) );
117
118 if( m_glRC )
119 {
121
122 delete m_ogl_3dmodel;
123 m_ogl_3dmodel = nullptr;
124
127 }
128}
129
130
132{
133 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::Set3DModel with a S3DMODEL" ) );
134
135 // Validate a3DModel pointers
136 wxASSERT( a3DModel.m_Materials != nullptr );
137 wxASSERT( a3DModel.m_Meshes != nullptr );
138 wxASSERT( a3DModel.m_MaterialsSize > 0 );
139 wxASSERT( a3DModel.m_MeshesSize > 0 );
140
141 // Delete the old model
142 delete m_ogl_3dmodel;
143 m_ogl_3dmodel = nullptr;
144
145 m_3d_model = nullptr;
146
147 if( ( a3DModel.m_Materials != nullptr ) && ( a3DModel.m_Meshes != nullptr )
148 && ( a3DModel.m_MaterialsSize > 0 ) && ( a3DModel.m_MeshesSize > 0 ) )
149 {
150 m_3d_model = &a3DModel;
151 m_reload_is_needed = true;
152 }
153
154 Refresh();
155}
156
157
158void EDA_3D_MODEL_VIEWER::Set3DModel( const wxString& aModelPathName)
159{
160 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::Set3DModel with a wxString" ) );
161
162 if( m_cacheManager )
163 {
164 const S3DMODEL* model = m_cacheManager->GetModel( aModelPathName, wxEmptyString );
165
166 if( model )
167 Set3DModel( (const S3DMODEL &)*model );
168 else
169 Clear3DModel();
170 }
171}
172
173
175{
176 // Delete the old model
177 m_reload_is_needed = false;
178
179 delete m_ogl_3dmodel;
180 m_ogl_3dmodel = nullptr;
181
182 m_3d_model = nullptr;
183
184 Refresh();
185}
186
187
189{
190 const GLenum err = glewInit();
191
192 if( GLEW_OK != err )
193 {
194 const wxString msgError = (const char*) glewGetErrorString( err );
195
196 wxLogMessage( msgError );
197 }
198 else
199 {
200 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::ogl_initialize Using GLEW version %s" ),
201 From_UTF8( (char*) glewGetString( GLEW_VERSION ) ) );
202 }
203
204 SetOpenGLInfo( (const char*) glGetString( GL_VENDOR ), (const char*) glGetString( GL_RENDERER ),
205 (const char*) glGetString( GL_VERSION ) );
206
207 glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );
208 glHint( GL_LINE_SMOOTH_HINT, GL_NICEST );
209 glHint( GL_POLYGON_SMOOTH_HINT, GL_NICEST );
210
211 glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
212 glEnable( GL_DEPTH_TEST );
213 glEnable( GL_CULL_FACE );
214 glShadeModel( GL_SMOOTH );
215 glEnable( GL_LINE_SMOOTH );
216 glEnable( GL_NORMALIZE );
217
218 // Setup light
219 // https://www.opengl.org/sdk/docs/man2/xhtml/glLight.xml
220 const GLfloat ambient[] = { 0.01f, 0.01f, 0.01f, 1.0f };
221 const GLfloat diffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f };
222 const GLfloat specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
223
224 // defines a directional light that points along the negative z-axis
225 const GLfloat position[] = { 0.0f, 0.0f, 2.0f * RANGE_SCALE_3D, 0.0f };
226
227 const GLfloat lmodel_ambient[] = { 0.0f, 0.0f, 0.0f, 1.0f };
228
229 glLightfv( GL_LIGHT0, GL_AMBIENT, ambient );
230 glLightfv( GL_LIGHT0, GL_DIFFUSE, diffuse );
231 glLightfv( GL_LIGHT0, GL_SPECULAR, specular );
232 glLightfv( GL_LIGHT0, GL_POSITION, position );
233 glLightModelfv( GL_LIGHT_MODEL_AMBIENT, lmodel_ambient );
234}
235
236
238{
239 glEnable( GL_COLOR_MATERIAL );
240 glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );
241
242 const SFVEC4F specular = SFVEC4F( 0.1f, 0.1f, 0.1f, 1.0f );
243
244 glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, &specular.r );
245 glMaterialf( GL_FRONT_AND_BACK, GL_SHININESS, 96.0f );
246}
247
248
249void EDA_3D_MODEL_VIEWER::OnPaint( wxPaintEvent& event )
250{
251 event.Skip( false );
252
253 // SwapBuffer requires the window to be shown before calling
254 if( !IsShownOnScreen() )
255 {
256 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::OnPaint !IsShown" ) );
257 return;
258 }
259
260 // "Makes the OpenGL state that is represented by the OpenGL rendering
261 // context context current, i.e. it will be used by all subsequent OpenGL calls.
262 // This function may only be called when the window is shown on screen"
263 if( m_glRC == nullptr )
265
266 // CreateCtx could and does fail per sentry crash events, lets be graceful
267 if( m_glRC == nullptr )
268 {
269 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::OnPaint creating gl context failed" ) );
270 return;
271 }
272
274
275 // Set the OpenGL viewport according to the client size of this canvas.
276 // This is done here rather than in a wxSizeEvent handler because our
277 // OpenGL rendering context (and thus viewport setting) is used with
278 // multiple canvases: If we updated the viewport in the wxSizeEvent
279 // handler, changing the size of one canvas causes a viewport setting that
280 // is wrong when next another canvas is repainted.
281 wxSize clientSize = GetNativePixelSize();
282
283 if( !m_ogl_initialized )
284 {
285 m_ogl_initialized = true;
287 }
288
290 {
291 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::OnPaint m_reload_is_needed" ) );
292
293 m_reload_is_needed = false;
294 m_ogl_3dmodel = new MODEL_3D( *m_3d_model, MATERIAL_MODE::NORMAL );
295
296 // It convert a model as it was a board, so get the max size dimension of the board
297 // and compute the conversion scale
299 (double) RANGE_SCALE_3D
301 }
302
303 glViewport( 0, 0, clientSize.x, clientSize.y );
304
306
307 // clear color and depth buffers
308 glEnable( GL_DEPTH_TEST );
309 glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
310 glClearDepth( 1.0f );
311 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
312
313 // Set projection and modelview matrices
314 glMatrixMode( GL_PROJECTION );
315 glLoadMatrixf( glm::value_ptr( m_trackBallCamera.GetProjectionMatrix() ) );
316 glMatrixMode( GL_MODELVIEW );
317 glLoadMatrixf( glm::value_ptr( m_trackBallCamera.GetViewMatrix() ) );
318 glEnable( GL_LIGHTING );
319 glEnable( GL_LIGHT0 );
320
321 // Render Model
322 if( m_ogl_3dmodel )
323 {
324 glPushMatrix();
325
326 double modelunit_to_3d_units_factor = m_BiuTo3dUnits * UNITS3D_TO_UNITSPCB;
327
328 glScaled( modelunit_to_3d_units_factor, modelunit_to_3d_units_factor,
329 modelunit_to_3d_units_factor );
330
331 // Center model in the render viewport
332 const SFVEC3F model_center = m_ogl_3dmodel->GetBBox().GetCenter();
333
334 glTranslatef( -model_center.x, -model_center.y, -model_center.z );
335
337
338 m_ogl_3dmodel->DrawOpaque( false );
339
340 glDepthMask( GL_FALSE );
341 glEnable( GL_BLEND );
342 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
343
344 m_ogl_3dmodel->DrawTransparent( 1.0f, false );
345
346 glDisable( GL_BLEND );
347 glDepthMask( GL_TRUE );
348
350
351 glPopMatrix();
352 }
353
354 // YxY squared view port
355 glViewport( 0, 0, clientSize.y / 8 , clientSize.y / 8 );
356 glClear( GL_DEPTH_BUFFER_BIT );
357
358 glMatrixMode( GL_PROJECTION );
359 glLoadIdentity();
360 gluPerspective( 45.0f, 1.0f, 0.01f, RANGE_SCALE_3D * 2.0f );
361
362 glMatrixMode( GL_MODELVIEW );
363 glLoadIdentity();
364
365 const glm::mat4 TranslationMatrix = glm::translate( glm::mat4(1.0f),
366 SFVEC3F( 0.0f, 0.0f, -RANGE_SCALE_3D ) );
367
368 const glm::mat4 ViewMatrix = TranslationMatrix * m_trackBallCamera.GetRotationMatrix();
369
370 glLoadMatrixf( glm::value_ptr( ViewMatrix ) );
371
373
374 glColor3f( 0.9f, 0.0f, 0.0f );
375 DrawRoundArrow( SFVEC3F( 0.0f, 0.0f, 0.0f ), SFVEC3F( RANGE_SCALE_3D / 2.65f, 0.0f, 0.0f ),
376 0.275f );
377
378 glColor3f( 0.0f, 0.9f, 0.0f );
379 DrawRoundArrow( SFVEC3F( 0.0f, 0.0f, 0.0f ), SFVEC3F( 0.0f, RANGE_SCALE_3D / 2.65f, 0.0f ),
380 0.275f );
381
382 glColor3f( 0.0f, 0.0f, 0.9f );
383 DrawRoundArrow( SFVEC3F( 0.0f, 0.0f, 0.0f ), SFVEC3F( 0.0f, 0.0f, RANGE_SCALE_3D / 2.65f ),
384 0.275f );
385
386 // "Swaps the double-buffer of this window, making the back-buffer the
387 // front-buffer and vice versa, so that the output of the previous OpenGL
388 // commands is displayed on the window."
389 SwapBuffers();
390
392}
393
394
395void EDA_3D_MODEL_VIEWER::OnEraseBackground( wxEraseEvent& event )
396{
397 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::OnEraseBackground" ) );
398 // Do nothing, to avoid flashing.
399}
400
401
402void EDA_3D_MODEL_VIEWER::OnMouseWheel( wxMouseEvent& event )
403{
404 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::OnMouseWheel" ) );
405
406 if( event.ShiftDown() )
407 {
408 //if( event.GetWheelRotation() < 0 )
409 //SetView3D( VIEW_3D_TYPE::VIEW3D_PAN_UP ); // move up
410 //else
411 //SetView3D( VIEW_3D_TYPE::VIEW3D_PAN_DOWN ); // move down
412 }
413 else if( event.ControlDown() )
414 {
415 //if( event.GetWheelRotation() > 0 )
416 //SetView3D( VIEW_3D_TYPE::VIEW3D_PAN_RIGHT ); // move right
417 //else
418 //SetView3D( VIEW_3D_TYPE::VIEW3D_PAN_LEFT ); // move left
419 }
420 else
421 {
422 m_trackBallCamera.Zoom( event.GetWheelRotation() > 0 ? 1.1f : 1/1.1f );
423
424 //DisplayStatus();
425 Refresh( false );
426 }
427
428 m_trackBallCamera.SetCurMousePosition( event.GetPosition() );
429}
430
431
432#ifdef USE_OSX_MAGNIFY_EVENT
433void EDA_3D_MODEL_VIEWER::OnMagnify( wxMouseEvent& event )
434{
435}
436#endif
437
438
439void EDA_3D_MODEL_VIEWER::OnMouseMove( wxMouseEvent& event )
440{
441 const wxSize& nativeWinSize = GetNativePixelSize();
442 const wxPoint& nativePosition = GetNativePosition( event.GetPosition() );
443
444 m_trackBallCamera.SetCurWindowSize( nativeWinSize );
445
446 if( event.Dragging() )
447 {
448 if( event.LeftIsDown() ) // Drag
449 m_trackBallCamera.Drag( nativePosition );
450
451 // orientation has changed, redraw mesh
452 Refresh( false );
453 }
454
455 m_trackBallCamera.SetCurMousePosition( nativePosition );
456}
457
458
459void EDA_3D_MODEL_VIEWER::OnLeftDown( wxMouseEvent& event )
460{
461 event.Skip();
462}
463
464
465void EDA_3D_MODEL_VIEWER::OnLeftUp( wxMouseEvent& event )
466{
467 event.Skip();
468}
469
470
471void EDA_3D_MODEL_VIEWER::OnMiddleDown( wxMouseEvent& event )
472{
473 event.Skip();
474}
475
476
477void EDA_3D_MODEL_VIEWER::OnMiddleUp( wxMouseEvent& event )
478{
479 event.Skip();
480}
481
482
483void EDA_3D_MODEL_VIEWER::OnRightClick( wxMouseEvent& event )
484{
485 event.Skip();
486}
487
#define RANGE_SCALE_3D
This defines the range that all coord will have to be rendered.
Definition: board_adapter.h:66
void SetOpenGLInfo(const char *aVendor, const char *aRenderer, const char *aVersion)
A setter for OpenGL info when it's initialized.
glm::mat4 GetRotationMatrix() const
Get the rotation matrix to be applied in a transformation camera.
Definition: camera.cpp:241
const glm::mat4 & GetProjectionMatrix() const
Definition: camera.cpp:473
bool Zoom(float aFactor)
Definition: camera.cpp:597
bool SetCurWindowSize(const wxSize &aSize)
Update the windows size of the camera.
Definition: camera.cpp:570
const glm::mat4 & GetViewMatrix() const
Definition: camera.cpp:509
void SetCurMousePosition(const wxPoint &aPosition)
Update the current mouse position without make any new calculations on camera.
Definition: camera.cpp:553
Class to handle configuration and automatic determination of the DPI scale to use for canvases.
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.
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
virtual COMMON_SETTINGS * GetCommonSettings() const
Definition: pgm_base.cpp:678
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:634
void Drag(const wxPoint &aNewMousePosition) override
Calculate a new mouse drag position.
Definition: track_ball.cpp:67
#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.
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...
PGM_BASE & Pgm()
The global Program "get" accessor.
Definition: pgm_base.cpp:1059
see class PGM_BASE
wxString From_UTF8(const char *cstring)
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