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 <wx/dcclient.h>
32
33#include <base_units.h>
34#include <build_version.h>
36#include <dpi_scaling_common.h>
37#include <macros.h>
38#include <pgm_base.h>
40
43#include "3d_cache/3d_cache.h"
44#include "eda_3d_model_viewer.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 ),
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" ) );
113 wxASSERT( gl_mgr );
114
115 if( m_glRC && gl_mgr )
116 {
117 gl_mgr->LockCtx( m_glRC, this );
118
119 delete m_ogl_3dmodel;
120 m_ogl_3dmodel = nullptr;
121
122 gl_mgr->UnlockCtx( m_glRC );
123 gl_mgr->DestroyCtx( m_glRC );
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{
188
189 const int glVersion = gladLoaderLoadGL();
190
191 if( glVersion == 0 )
192 {
193 wxLogMessage( wxT( "Failed to load OpenGL via loader" ) );
194 }
195 else
196 {
197 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::ogl_initialize Using OpenGL version %s" ),
198 From_UTF8( (char*) glGetString( GL_VERSION ) ) );
199 }
200
201 SetOpenGLInfo( (const char*) glGetString( GL_VENDOR ), (const char*) glGetString( GL_RENDERER ),
202 (const char*) glGetString( GL_VERSION ) );
203
204 glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );
205 glHint( GL_LINE_SMOOTH_HINT, GL_NICEST );
206 glHint( GL_POLYGON_SMOOTH_HINT, GL_NICEST );
207
208 glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
209 glEnable( GL_DEPTH_TEST );
210 glEnable( GL_CULL_FACE );
211 glShadeModel( GL_SMOOTH );
212 glEnable( GL_LINE_SMOOTH );
213 glEnable( GL_NORMALIZE );
214
215 // Setup light
216 // https://www.opengl.org/sdk/docs/man2/xhtml/glLight.xml
217 const GLfloat ambient[] = { 0.01f, 0.01f, 0.01f, 1.0f };
218 const GLfloat diffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f };
219 const GLfloat specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
220
221 // defines a directional light that points along the negative z-axis
222 const GLfloat position[] = { 0.0f, 0.0f, 2.0f * RANGE_SCALE_3D, 0.0f };
223
224 const GLfloat lmodel_ambient[] = { 0.0f, 0.0f, 0.0f, 1.0f };
225
226 glLightfv( GL_LIGHT0, GL_AMBIENT, ambient );
227 glLightfv( GL_LIGHT0, GL_DIFFUSE, diffuse );
228 glLightfv( GL_LIGHT0, GL_SPECULAR, specular );
229 glLightfv( GL_LIGHT0, GL_POSITION, position );
230 glLightModelfv( GL_LIGHT_MODEL_AMBIENT, lmodel_ambient );
231}
232
233
235{
236 glEnable( GL_COLOR_MATERIAL );
237 glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );
238
239 const SFVEC4F specular = SFVEC4F( 0.1f, 0.1f, 0.1f, 1.0f );
240
241 glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, &specular.r );
242 glMaterialf( GL_FRONT_AND_BACK, GL_SHININESS, 96.0f );
243}
244
245
246void EDA_3D_MODEL_VIEWER::OnPaint( wxPaintEvent& event )
247{
248 event.Skip( false );
249
250 // SwapBuffer requires the window to be shown before calling
251 if( !IsShownOnScreen() )
252 {
253 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::OnPaint !IsShown" ) );
254 return;
255 }
256
257 // "Makes the OpenGL state that is represented by the OpenGL rendering
258 // context context current, i.e. it will be used by all subsequent OpenGL calls.
259 // This function may only be called when the window is shown on screen"
261
262 if( !gl_mgr )
263 return;
264
265 if( m_glRC == nullptr )
266 m_glRC = gl_mgr->CreateCtx( this );
267
268 // CreateCtx could and does fail per sentry crash events, lets be graceful
269 if( m_glRC == nullptr )
270 {
271 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::OnPaint creating gl context failed" ) );
272 return;
273 }
274
275 gl_mgr->LockCtx( m_glRC, this );
276
277 // Set the OpenGL viewport according to the client size of this canvas.
278 // This is done here rather than in a wxSizeEvent handler because our
279 // OpenGL rendering context (and thus viewport setting) is used with
280 // multiple canvases: If we updated the viewport in the wxSizeEvent
281 // handler, changing the size of one canvas causes a viewport setting that
282 // is wrong when next another canvas is repainted.
283 wxSize clientSize = GetNativePixelSize();
284
285 if( !m_ogl_initialized )
286 {
287 m_ogl_initialized = true;
289 }
290
292 {
293 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::OnPaint m_reload_is_needed" ) );
294
295 m_reload_is_needed = false;
297
298 // It convert a model as it was a board, so get the max size dimension of the board
299 // and compute the conversion scale
301 (double) RANGE_SCALE_3D
302 / ( (double) m_ogl_3dmodel->GetBBox().GetMaxDimension() * UNITS3D_TO_UNITSPCB );
303 }
304
305 glViewport( 0, 0, clientSize.x, clientSize.y );
306
307 m_trackBallCamera.SetCurWindowSize( clientSize );
308
309 // clear color and depth buffers
310 glEnable( GL_DEPTH_TEST );
311 glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
312 glClearDepth( 1.0f );
313 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
314
315 // Set projection and modelview matrices
316 glMatrixMode( GL_PROJECTION );
317 glLoadMatrixf( glm::value_ptr( m_trackBallCamera.GetProjectionMatrix() ) );
318 glMatrixMode( GL_MODELVIEW );
319 glLoadMatrixf( glm::value_ptr( m_trackBallCamera.GetViewMatrix() ) );
320 glEnable( GL_LIGHTING );
321 glEnable( GL_LIGHT0 );
322
323 // Render Model
324 if( m_ogl_3dmodel )
325 {
326 glPushMatrix();
327
328 double modelunit_to_3d_units_factor = m_BiuTo3dUnits * UNITS3D_TO_UNITSPCB;
329
330 glScaled( modelunit_to_3d_units_factor, modelunit_to_3d_units_factor,
331 modelunit_to_3d_units_factor );
332
333 // Center model in the render viewport
334 const SFVEC3F model_center = m_ogl_3dmodel->GetBBox().GetCenter();
335
336 glTranslatef( -model_center.x, -model_center.y, -model_center.z );
337
338 m_ogl_3dmodel->BeginDrawMulti( true );
339
340 m_ogl_3dmodel->DrawOpaque( false );
341
342 glDepthMask( GL_FALSE );
343 glEnable( GL_BLEND );
344 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
345
346 m_ogl_3dmodel->DrawTransparent( 1.0f, false );
347
348 glDisable( GL_BLEND );
349 glDepthMask( GL_TRUE );
350
351 m_ogl_3dmodel->EndDrawMulti();
352
353 glPopMatrix();
354 }
355
356 // YxY squared view port
357 glViewport( 0, 0, clientSize.y / 8 , clientSize.y / 8 );
358 glClear( GL_DEPTH_BUFFER_BIT );
359
360 glMatrixMode( GL_PROJECTION );
361 glLoadIdentity();
362 gluPerspective( 45.0f, 1.0f, 0.01f, RANGE_SCALE_3D * 2.0f );
363
364 glMatrixMode( GL_MODELVIEW );
365 glLoadIdentity();
366
367 const glm::mat4 TranslationMatrix = glm::translate( glm::mat4(1.0f),
368 SFVEC3F( 0.0f, 0.0f, -RANGE_SCALE_3D ) );
369
370 const glm::mat4 ViewMatrix = TranslationMatrix * m_trackBallCamera.GetRotationMatrix();
371
372 glLoadMatrixf( glm::value_ptr( ViewMatrix ) );
373
375
376 glColor3f( 0.9f, 0.0f, 0.0f );
377 DrawRoundArrow( SFVEC3F( 0.0f, 0.0f, 0.0f ), SFVEC3F( RANGE_SCALE_3D / 2.65f, 0.0f, 0.0f ),
378 0.275f );
379
380 glColor3f( 0.0f, 0.9f, 0.0f );
381 DrawRoundArrow( SFVEC3F( 0.0f, 0.0f, 0.0f ), SFVEC3F( 0.0f, RANGE_SCALE_3D / 2.65f, 0.0f ),
382 0.275f );
383
384 glColor3f( 0.0f, 0.0f, 0.9f );
385 DrawRoundArrow( SFVEC3F( 0.0f, 0.0f, 0.0f ), SFVEC3F( 0.0f, 0.0f, RANGE_SCALE_3D / 2.65f ),
386 0.275f );
387
388 // "Swaps the double-buffer of this window, making the back-buffer the
389 // front-buffer and vice versa, so that the output of the previous OpenGL
390 // commands is displayed on the window."
391 SwapBuffers();
392
393 gl_mgr->UnlockCtx( m_glRC );
394}
395
396
397void EDA_3D_MODEL_VIEWER::OnEraseBackground( wxEraseEvent& event )
398{
399 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::OnEraseBackground" ) );
400 // Do nothing, to avoid flashing.
401}
402
403
404void EDA_3D_MODEL_VIEWER::OnMouseWheel( wxMouseEvent& event )
405{
406 wxLogTrace( m_logTrace, wxT( "EDA_3D_MODEL_VIEWER::OnMouseWheel" ) );
407
408 if( event.ShiftDown() )
409 {
410 //if( event.GetWheelRotation() < 0 )
411 //SetView3D( VIEW_3D_TYPE::VIEW3D_PAN_UP ); // move up
412 //else
413 //SetView3D( VIEW_3D_TYPE::VIEW3D_PAN_DOWN ); // move down
414 }
415 else if( event.ControlDown() )
416 {
417 //if( event.GetWheelRotation() > 0 )
418 //SetView3D( VIEW_3D_TYPE::VIEW3D_PAN_RIGHT ); // move right
419 //else
420 //SetView3D( VIEW_3D_TYPE::VIEW3D_PAN_LEFT ); // move left
421 }
422 else
423 {
424 m_trackBallCamera.Zoom( event.GetWheelRotation() > 0 ? 1.1f : 1/1.1f );
425
426 //DisplayStatus();
427 Refresh( false );
428 }
429
430 m_trackBallCamera.SetCurMousePosition( event.GetPosition() );
431}
432
433
434#ifdef USE_OSX_MAGNIFY_EVENT
435void EDA_3D_MODEL_VIEWER::OnMagnify( wxMouseEvent& event )
436{
437}
438#endif
439
440
441void EDA_3D_MODEL_VIEWER::OnMouseMove( wxMouseEvent& event )
442{
443 const wxSize& nativeWinSize = GetNativePixelSize();
444 const wxPoint& nativePosition = GetNativePosition( event.GetPosition() );
445
446 m_trackBallCamera.SetCurWindowSize( nativeWinSize );
447
448 if( event.Dragging() )
449 {
450 if( event.LeftIsDown() ) // Drag
451 m_trackBallCamera.Drag( nativePosition );
452
453 // orientation has changed, redraw mesh
454 Refresh( false );
455 }
456
457 m_trackBallCamera.SetCurMousePosition( nativePosition );
458}
459
460
461void EDA_3D_MODEL_VIEWER::OnLeftDown( wxMouseEvent& event )
462{
463 event.Skip();
464}
465
466
467void EDA_3D_MODEL_VIEWER::OnLeftUp( wxMouseEvent& event )
468{
469 event.Skip();
470}
471
472
473void EDA_3D_MODEL_VIEWER::OnMiddleDown( wxMouseEvent& event )
474{
475 event.Skip();
476}
477
478
479void EDA_3D_MODEL_VIEWER::OnMiddleUp( wxMouseEvent& event )
480{
481 event.Skip();
482}
483
484
485void EDA_3D_MODEL_VIEWER::OnRightClick( wxMouseEvent& event )
486{
487 event.Skip();
488}
489
@ 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.
void 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:114
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.
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
wxString From_UTF8(const char *cstring)
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
KIBIS_MODEL * model
glm::vec3 SFVEC3F
Definition xv3d_types.h:40
glm::vec4 SFVEC4F
Definition xv3d_types.h:42