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