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