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
108
110{
111 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::~EDA_3D_MODEL_VIEWER" ) );
112
113 if( m_glRC )
114 {
116
117 delete m_ogl_3dmodel;
118 m_ogl_3dmodel = nullptr;
119
122 }
123}
124
125
127{
128 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::Set3DModel with a S3DMODEL" ) );
129
130 // Validate a3DModel pointers
131 wxASSERT( a3DModel.m_Materials != nullptr );
132 wxASSERT( a3DModel.m_Meshes != nullptr );
133 wxASSERT( a3DModel.m_MaterialsSize > 0 );
134 wxASSERT( a3DModel.m_MeshesSize > 0 );
135
136 // Delete the old model
137 delete m_ogl_3dmodel;
138 m_ogl_3dmodel = nullptr;
139
140 m_3d_model = nullptr;
141
142 if( ( a3DModel.m_Materials != nullptr ) && ( a3DModel.m_Meshes != nullptr )
143 && ( a3DModel.m_MaterialsSize > 0 ) && ( a3DModel.m_MeshesSize > 0 ) )
144 {
145 m_3d_model = &a3DModel;
146 m_reload_is_needed = true;
147 }
148
149 Refresh();
150}
151
152
153void EDA_3D_MODEL_VIEWER::Set3DModel( const wxString& aModelPathName)
154{
155 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::Set3DModel with a wxString" ) );
156
157 if( m_cacheManager )
158 {
159 const S3DMODEL* model = m_cacheManager->GetModel( aModelPathName, wxEmptyString, nullptr );
160
161 if( model )
162 Set3DModel( (const S3DMODEL &)*model );
163 else
164 Clear3DModel();
165 }
166}
167
168
170{
171 // Delete the old model
172 m_reload_is_needed = false;
173
174 delete m_ogl_3dmodel;
175 m_ogl_3dmodel = nullptr;
176
177 m_3d_model = nullptr;
178
179 Refresh();
180}
181
182
184{
185 const GLenum err = glewInit();
186
187 if( GLEW_OK != err )
188 {
189 const wxString msgError = (const char*) glewGetErrorString( err );
190
191 wxLogMessage( msgError );
192 }
193 else
194 {
195 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::ogl_initialize Using GLEW version %s" ),
196 From_UTF8( (char*) glewGetString( GLEW_VERSION ) ) );
197 }
198
199 SetOpenGLInfo( (const char*) glGetString( GL_VENDOR ), (const char*) glGetString( GL_RENDERER ),
200 (const char*) glGetString( GL_VERSION ) );
201
202 glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );
203 glHint( GL_LINE_SMOOTH_HINT, GL_NICEST );
204 glHint( GL_POLYGON_SMOOTH_HINT, GL_NICEST );
205
206 glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
207 glEnable( GL_DEPTH_TEST );
208 glEnable( GL_CULL_FACE );
209 glShadeModel( GL_SMOOTH );
210 glEnable( GL_LINE_SMOOTH );
211 glEnable( GL_NORMALIZE );
212
213 // Setup light
214 // https://www.opengl.org/sdk/docs/man2/xhtml/glLight.xml
215 const GLfloat ambient[] = { 0.01f, 0.01f, 0.01f, 1.0f };
216 const GLfloat diffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f };
217 const GLfloat specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
218
219 // defines a directional light that points along the negative z-axis
220 const GLfloat position[] = { 0.0f, 0.0f, 2.0f * RANGE_SCALE_3D, 0.0f };
221
222 const GLfloat lmodel_ambient[] = { 0.0f, 0.0f, 0.0f, 1.0f };
223
224 glLightfv( GL_LIGHT0, GL_AMBIENT, ambient );
225 glLightfv( GL_LIGHT0, GL_DIFFUSE, diffuse );
226 glLightfv( GL_LIGHT0, GL_SPECULAR, specular );
227 glLightfv( GL_LIGHT0, GL_POSITION, position );
228 glLightModelfv( GL_LIGHT_MODEL_AMBIENT, lmodel_ambient );
229}
230
231
233{
234 glEnable( GL_COLOR_MATERIAL );
235 glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );
236
237 const SFVEC4F specular = SFVEC4F( 0.1f, 0.1f, 0.1f, 1.0f );
238
239 glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, &specular.r );
240 glMaterialf( GL_FRONT_AND_BACK, GL_SHININESS, 96.0f );
241}
242
243
244void EDA_3D_MODEL_VIEWER::OnPaint( wxPaintEvent& event )
245{
246 event.Skip( false );
247
248 // SwapBuffer requires the window to be shown before calling
249 if( !IsShownOnScreen() )
250 {
251 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::OnPaint !IsShown" ) );
252 return;
253 }
254
255 // "Makes the OpenGL state that is represented by the OpenGL rendering
256 // context context current, i.e. it will be used by all subsequent OpenGL calls.
257 // This function may only be called when the window is shown on screen"
258 if( m_glRC == nullptr )
260
261 // CreateCtx could and does fail per sentry crash events, lets be graceful
262 if( m_glRC == nullptr )
263 {
264 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::OnPaint creating gl context failed" ) );
265 return;
266 }
267
269
270 // Set the OpenGL viewport according to the client size of this canvas.
271 // This is done here rather than in a wxSizeEvent handler because our
272 // OpenGL rendering context (and thus viewport setting) is used with
273 // multiple canvases: If we updated the viewport in the wxSizeEvent
274 // handler, changing the size of one canvas causes a viewport setting that
275 // is wrong when next another canvas is repainted.
276 wxSize clientSize = GetNativePixelSize();
277
278 if( !m_ogl_initialized )
279 {
280 m_ogl_initialized = true;
282 }
283
285 {
286 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::OnPaint m_reload_is_needed" ) );
287
288 m_reload_is_needed = false;
289 m_ogl_3dmodel = new MODEL_3D( *m_3d_model, MATERIAL_MODE::NORMAL );
290
291 // It convert a model as it was a board, so get the max size dimension of the board
292 // and compute the conversion scale
294 (double) RANGE_SCALE_3D
296 }
297
298 glViewport( 0, 0, clientSize.x, clientSize.y );
299
301
302 // clear color and depth buffers
303 glEnable( GL_DEPTH_TEST );
304 glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
305 glClearDepth( 1.0f );
306 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
307
308 // Set projection and modelview matrices
309 glMatrixMode( GL_PROJECTION );
310 glLoadMatrixf( glm::value_ptr( m_trackBallCamera.GetProjectionMatrix() ) );
311 glMatrixMode( GL_MODELVIEW );
312 glLoadMatrixf( glm::value_ptr( m_trackBallCamera.GetViewMatrix() ) );
313 glEnable( GL_LIGHTING );
314 glEnable( GL_LIGHT0 );
315
316 // Render Model
317 if( m_ogl_3dmodel )
318 {
319 glPushMatrix();
320
321 double modelunit_to_3d_units_factor = m_BiuTo3dUnits * UNITS3D_TO_UNITSPCB;
322
323 glScaled( modelunit_to_3d_units_factor, modelunit_to_3d_units_factor,
324 modelunit_to_3d_units_factor );
325
326 // Center model in the render viewport
327 const SFVEC3F model_center = m_ogl_3dmodel->GetBBox().GetCenter();
328
329 glTranslatef( -model_center.x, -model_center.y, -model_center.z );
330
332
333 m_ogl_3dmodel->DrawOpaque( false );
334
335 glDepthMask( GL_FALSE );
336 glEnable( GL_BLEND );
337 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
338
339 m_ogl_3dmodel->DrawTransparent( 1.0f, false );
340
341 glDisable( GL_BLEND );
342 glDepthMask( GL_TRUE );
343
345
346 glPopMatrix();
347 }
348
349 // YxY squared view port
350 glViewport( 0, 0, clientSize.y / 8 , clientSize.y / 8 );
351 glClear( GL_DEPTH_BUFFER_BIT );
352
353 glMatrixMode( GL_PROJECTION );
354 glLoadIdentity();
355 gluPerspective( 45.0f, 1.0f, 0.01f, RANGE_SCALE_3D * 2.0f );
356
357 glMatrixMode( GL_MODELVIEW );
358 glLoadIdentity();
359
360 const glm::mat4 TranslationMatrix = glm::translate( glm::mat4(1.0f),
361 SFVEC3F( 0.0f, 0.0f, -RANGE_SCALE_3D ) );
362
363 const glm::mat4 ViewMatrix = TranslationMatrix * m_trackBallCamera.GetRotationMatrix();
364
365 glLoadMatrixf( glm::value_ptr( ViewMatrix ) );
366
368
369 glColor3f( 0.9f, 0.0f, 0.0f );
370 DrawRoundArrow( SFVEC3F( 0.0f, 0.0f, 0.0f ), SFVEC3F( RANGE_SCALE_3D / 2.65f, 0.0f, 0.0f ),
371 0.275f );
372
373 glColor3f( 0.0f, 0.9f, 0.0f );
374 DrawRoundArrow( SFVEC3F( 0.0f, 0.0f, 0.0f ), SFVEC3F( 0.0f, RANGE_SCALE_3D / 2.65f, 0.0f ),
375 0.275f );
376
377 glColor3f( 0.0f, 0.0f, 0.9f );
378 DrawRoundArrow( SFVEC3F( 0.0f, 0.0f, 0.0f ), SFVEC3F( 0.0f, 0.0f, RANGE_SCALE_3D / 2.65f ),
379 0.275f );
380
381 // "Swaps the double-buffer of this window, making the back-buffer the
382 // front-buffer and vice versa, so that the output of the previous OpenGL
383 // commands is displayed on the window."
384 SwapBuffers();
385
387}
388
389
390void EDA_3D_MODEL_VIEWER::OnEraseBackground( wxEraseEvent& event )
391{
392 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::OnEraseBackground" ) );
393 // Do nothing, to avoid flashing.
394}
395
396
397void EDA_3D_MODEL_VIEWER::OnMouseWheel( wxMouseEvent& event )
398{
399 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::OnMouseWheel" ) );
400
401 if( event.ShiftDown() )
402 {
403 //if( event.GetWheelRotation() < 0 )
404 //SetView3D( VIEW_3D_TYPE::VIEW3D_PAN_UP ); // move up
405 //else
406 //SetView3D( VIEW_3D_TYPE::VIEW3D_PAN_DOWN ); // move down
407 }
408 else if( event.ControlDown() )
409 {
410 //if( event.GetWheelRotation() > 0 )
411 //SetView3D( VIEW_3D_TYPE::VIEW3D_PAN_RIGHT ); // move right
412 //else
413 //SetView3D( VIEW_3D_TYPE::VIEW3D_PAN_LEFT ); // move left
414 }
415 else
416 {
417 m_trackBallCamera.Zoom( event.GetWheelRotation() > 0 ? 1.1f : 1/1.1f );
418
419 //DisplayStatus();
420 Refresh( false );
421 }
422
423 m_trackBallCamera.SetCurMousePosition( event.GetPosition() );
424}
425
426
427#ifdef USE_OSX_MAGNIFY_EVENT
428void EDA_3D_MODEL_VIEWER::OnMagnify( wxMouseEvent& event )
429{
430}
431#endif
432
433
434void EDA_3D_MODEL_VIEWER::OnMouseMove( wxMouseEvent& event )
435{
436 const wxSize& nativeWinSize = GetNativePixelSize();
437 const wxPoint& nativePosition = GetNativePosition( event.GetPosition() );
438
439 m_trackBallCamera.SetCurWindowSize( nativeWinSize );
440
441 if( event.Dragging() )
442 {
443 if( event.LeftIsDown() ) // Drag
444 m_trackBallCamera.Drag( nativePosition );
445
446 // orientation has changed, redraw mesh
447 Refresh( false );
448 }
449
450 m_trackBallCamera.SetCurMousePosition( nativePosition );
451}
452
453
454void EDA_3D_MODEL_VIEWER::OnLeftDown( wxMouseEvent& event )
455{
456 event.Skip();
457}
458
459
460void EDA_3D_MODEL_VIEWER::OnLeftUp( wxMouseEvent& event )
461{
462 event.Skip();
463}
464
465
466void EDA_3D_MODEL_VIEWER::OnMiddleDown( wxMouseEvent& event )
467{
468 event.Skip();
469}
470
471
472void EDA_3D_MODEL_VIEWER::OnMiddleUp( wxMouseEvent& event )
473{
474 event.Skip();
475}
476
477
478void EDA_3D_MODEL_VIEWER::OnRightClick( wxMouseEvent& event )
479{
480 event.Skip();
481}
482
#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
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.
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:54
S3DMODEL * GetModel(const wxString &aModelFileName, const wxString &aBasePath, const EMBEDDED_FILES *aEmbeddedFiles)
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...
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