KiCad PCB EDA Suite
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 <mrluzeiro@ua.pt>
5  * Copyright (C) 1992-2020 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 
32 #include <iostream>
34 #include "3d_model_viewer.h"
35 #include "../3d_rendering/legacy/ogl_legacy_utils.h"
36 #include "../3d_cache/3d_cache.h"
37 #include "common_ogl/ogl_utils.h"
38 #include <wx/dcclient.h>
39 #include <base_units.h>
40 #include <gl_context_mgr.h>
41 
45 #define UNITS3D_TO_UNITSPCB (IU_PER_MM)
46 
55 const wxChar* C3D_MODEL_VIEWER::m_logTrace = wxT( "KI_TRACE_EDA_3D_MODEL_VIEWER" );
56 
57 
58 BEGIN_EVENT_TABLE( C3D_MODEL_VIEWER, wxGLCanvas )
59  EVT_PAINT( C3D_MODEL_VIEWER::OnPaint )
60 
61  // mouse events
62  EVT_LEFT_DOWN( C3D_MODEL_VIEWER::OnLeftDown )
63  EVT_LEFT_UP( C3D_MODEL_VIEWER::OnLeftUp )
64  EVT_MIDDLE_UP( C3D_MODEL_VIEWER::OnMiddleUp )
66  EVT_RIGHT_DOWN( C3D_MODEL_VIEWER::OnRightClick )
67  EVT_MOUSEWHEEL( C3D_MODEL_VIEWER::OnMouseWheel )
68  EVT_MOTION( C3D_MODEL_VIEWER::OnMouseMove )
69 
70 #ifdef USE_OSX_MAGNIFY_EVENT
71  EVT_MAGNIFY( C3D_MODEL_VIEWER::OnMagnify )
72 #endif
73 
74  // other events
75  EVT_ERASE_BACKGROUND( C3D_MODEL_VIEWER::OnEraseBackground )
76 END_EVENT_TABLE()
77 
78 
79 // This defines the range that all coord will have to be rendered.
80 // It will use this value to convert to a normalized value between
81 // -(RANGE_SCALE_3D/2) .. +(RANGE_SCALE_3D/2)
82 #define RANGE_SCALE_3D 8.0f
83 
84 
85 C3D_MODEL_VIEWER::C3D_MODEL_VIEWER( wxWindow* aParent, const int* aAttribList,
86  S3D_CACHE* aCacheManager ) :
87  HIDPI_GL_CANVAS( aParent, wxID_ANY, aAttribList, wxDefaultPosition, wxDefaultSize,
88  wxFULL_REPAINT_ON_RESIZE ),
89  m_trackBallCamera( RANGE_SCALE_3D * 2.0f ), m_cacheManager( aCacheManager )
90 {
91  wxLogTrace( m_logTrace, wxT( "C3D_MODEL_VIEWER::C3D_MODEL_VIEWER" ) );
92 
93  m_ogl_initialized = false;
94  m_reload_is_needed = false;
95  m_ogl_3dmodel = nullptr;
96  m_3d_model = nullptr;
97  m_BiuTo3dUnits = 1.0;
98 
99  m_glRC = nullptr;
100 }
101 
102 
104 {
105  wxLogTrace( m_logTrace, wxT( "C3D_MODEL_VIEWER::~C3D_MODEL_VIEWER" ) );
106 
107  if( m_glRC )
108  {
110 
111  delete m_ogl_3dmodel;
112  m_ogl_3dmodel = nullptr;
113 
116  }
117 }
118 
119 
120 void C3D_MODEL_VIEWER::Set3DModel( const S3DMODEL& a3DModel )
121 {
122  wxLogTrace( m_logTrace, wxT( "C3D_MODEL_VIEWER::Set3DModel with a S3DMODEL" ) );
123 
124  // Validate a3DModel pointers
125  wxASSERT( a3DModel.m_Materials != nullptr );
126  wxASSERT( a3DModel.m_Meshes != nullptr );
127  wxASSERT( a3DModel.m_MaterialsSize > 0 );
128  wxASSERT( a3DModel.m_MeshesSize > 0 );
129 
130  // Delete the old model
131  delete m_ogl_3dmodel;
132  m_ogl_3dmodel = nullptr;
133 
134  m_3d_model = nullptr;
135 
136  if( ( a3DModel.m_Materials != nullptr ) && ( a3DModel.m_Meshes != nullptr )
137  && ( a3DModel.m_MaterialsSize > 0 ) && ( a3DModel.m_MeshesSize > 0 ) )
138  {
139  m_3d_model = &a3DModel;
140  m_reload_is_needed = true;
141  }
142 
143  Refresh();
144 }
145 
146 
147 void C3D_MODEL_VIEWER::Set3DModel(const wxString& aModelPathName)
148 {
149  wxLogTrace( m_logTrace, wxT( "C3D_MODEL_VIEWER::Set3DModel with a wxString" ) );
150 
151  if( m_cacheManager )
152  {
153  const S3DMODEL* model = m_cacheManager->GetModel( aModelPathName );
154 
155  if( model )
156  Set3DModel( (const S3DMODEL &)*model );
157  else
158  Clear3DModel();
159  }
160 }
161 
162 
164 {
165  // Delete the old model
166  m_reload_is_needed = false;
167 
168  delete m_ogl_3dmodel;
169  m_ogl_3dmodel = nullptr;
170 
171  m_3d_model = nullptr;
172 
173  Refresh();
174 }
175 
176 
178 {
179  glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST );
180  glHint( GL_LINE_SMOOTH_HINT, GL_NICEST );
181  glHint( GL_POLYGON_SMOOTH_HINT, GL_NICEST );
182 
183  glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
184  glEnable( GL_DEPTH_TEST );
185  glEnable( GL_CULL_FACE );
186  glShadeModel( GL_SMOOTH );
187  glEnable( GL_LINE_SMOOTH );
188  glEnable( GL_NORMALIZE );
189 
190  // Setup light
191  // https://www.opengl.org/sdk/docs/man2/xhtml/glLight.xml
192  const GLfloat ambient[] = { 0.01f, 0.01f, 0.01f, 1.0f };
193  const GLfloat diffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f };
194  const GLfloat specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
195 
196  // defines a directional light that points along the negative z-axis
197  const GLfloat position[] = { 0.0f, 0.0f, 2.0f * RANGE_SCALE_3D, 0.0f };
198 
199  const GLfloat lmodel_ambient[] = { 0.0f, 0.0f, 0.0f, 1.0f };
200 
201  glLightfv( GL_LIGHT0, GL_AMBIENT, ambient );
202  glLightfv( GL_LIGHT0, GL_DIFFUSE, diffuse );
203  glLightfv( GL_LIGHT0, GL_SPECULAR, specular );
204  glLightfv( GL_LIGHT0, GL_POSITION, position );
205  glLightModelfv( GL_LIGHT_MODEL_AMBIENT, lmodel_ambient );
206 }
207 
208 
210 {
211  glEnable( GL_COLOR_MATERIAL );
212  glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );
213 
214  const SFVEC4F specular = SFVEC4F( 0.1f, 0.1f, 0.1f, 1.0f );
215 
216  glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, &specular.r );
217  glMaterialf( GL_FRONT_AND_BACK, GL_SHININESS, 96.0f );
218 }
219 
220 
221 void C3D_MODEL_VIEWER::OnPaint( wxPaintEvent& event )
222 {
223  event.Skip( false );
224 
225  // SwapBuffer requires the window to be shown before calling
226  if( !IsShownOnScreen() )
227  {
228  wxLogTrace( m_logTrace, wxT( "C3D_MODEL_VIEWER::OnPaint !IsShown" ) );
229  return;
230  }
231 
232  // "Makes the OpenGL state that is represented by the OpenGL rendering
233  // context context current, i.e. it will be used by all subsequent OpenGL calls.
234  // This function may only be called when the window is shown on screen"
235  if( m_glRC == nullptr )
237 
239 
240  // Set the OpenGL viewport according to the client size of this canvas.
241  // This is done here rather than in a wxSizeEvent handler because our
242  // OpenGL rendering context (and thus viewport setting) is used with
243  // multiple canvases: If we updated the viewport in the wxSizeEvent
244  // handler, changing the size of one canvas causes a viewport setting that
245  // is wrong when next another canvas is repainted.
246  wxSize clientSize = GetClientSize();
247 
248  if( !m_ogl_initialized )
249  {
250  m_ogl_initialized = true;
251  ogl_initialize();
252  }
253 
254  if( m_reload_is_needed )
255  {
256  wxLogTrace( m_logTrace, wxT( "C3D_MODEL_VIEWER::OnPaint m_reload_is_needed" ) );
257 
258  m_reload_is_needed = false;
260 
261  // It convert a model as it was a board, so get the max size dimension of the board
262  // and compute the conversion scale
264  (double) RANGE_SCALE_3D
266  }
267 
268  glViewport( 0, 0, clientSize.x, clientSize.y );
269 
270  m_trackBallCamera.SetCurWindowSize( clientSize );
271 
272  // clear color and depth buffers
273  glEnable( GL_DEPTH_TEST );
274  glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
275  glClearDepth( 1.0f );
276  glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
277 
278  // Set projection and modelview matrices
279  glMatrixMode( GL_PROJECTION );
280  glLoadMatrixf( glm::value_ptr( m_trackBallCamera.GetProjectionMatrix() ) );
281  glMatrixMode( GL_MODELVIEW );
282  glLoadMatrixf( glm::value_ptr( m_trackBallCamera.GetViewMatrix() ) );
283  glEnable( GL_LIGHTING );
284  glEnable( GL_LIGHT0 );
285 
286  // Render Model
287  if( m_ogl_3dmodel )
288  {
289  glPushMatrix();
290 
291  double modelunit_to_3d_units_factor = m_BiuTo3dUnits * UNITS3D_TO_UNITSPCB;
292 
293  glScaled( modelunit_to_3d_units_factor, modelunit_to_3d_units_factor,
294  modelunit_to_3d_units_factor );
295 
296  // Center model in the render viewport
297  const SFVEC3F model_center = m_ogl_3dmodel->GetBBox().GetCenter();
298 
299  glTranslatef( -model_center.x, -model_center.y, -model_center.z );
300 
301  m_ogl_3dmodel->BeginDrawMulti( true );
302 
303  m_ogl_3dmodel->DrawOpaque( false );
304  m_ogl_3dmodel->DrawTransparent( 1.0f, false );
305 
307 
308  glPopMatrix();
309  }
310 
311  // YxY squared view port
312  glViewport( 0, 0, clientSize.y / 8 , clientSize.y / 8 );
313  glClear( GL_DEPTH_BUFFER_BIT );
314 
315  glMatrixMode( GL_PROJECTION );
316  glLoadIdentity();
317  gluPerspective( 45.0f, 1.0f, 0.01f, RANGE_SCALE_3D * 2.0f );
318 
319  glMatrixMode( GL_MODELVIEW );
320  glLoadIdentity();
321 
322  const glm::mat4 TranslationMatrix = glm::translate( glm::mat4(1.0f),
323  SFVEC3F( 0.0f, 0.0f, -RANGE_SCALE_3D ) );
324 
325  const glm::mat4 ViewMatrix = TranslationMatrix * m_trackBallCamera.GetRotationMatrix();
326 
327  glLoadMatrixf( glm::value_ptr( ViewMatrix ) );
328 
330 
331  glColor3f( 0.9f, 0.0f, 0.0f );
332  DrawRoundArrow( SFVEC3F( 0.0f, 0.0f, 0.0f ), SFVEC3F( RANGE_SCALE_3D / 2.65f, 0.0f, 0.0f ),
333  0.275f );
334 
335  glColor3f( 0.0f, 0.9f, 0.0f );
336  DrawRoundArrow( SFVEC3F( 0.0f, 0.0f, 0.0f ), SFVEC3F( 0.0f, RANGE_SCALE_3D / 2.65f, 0.0f ),
337  0.275f );
338 
339  glColor3f( 0.0f, 0.0f, 0.9f );
340  DrawRoundArrow( SFVEC3F( 0.0f, 0.0f, 0.0f ), SFVEC3F( 0.0f, 0.0f, RANGE_SCALE_3D / 2.65f ),
341  0.275f );
342 
343  // "Swaps the double-buffer of this window, making the back-buffer the
344  // front-buffer and vice versa, so that the output of the previous OpenGL
345  // commands is displayed on the window."
346  SwapBuffers();
347 
349 }
350 
351 
352 void C3D_MODEL_VIEWER::OnEraseBackground( wxEraseEvent& event )
353 {
354  wxLogTrace( m_logTrace, wxT( "C3D_MODEL_VIEWER::OnEraseBackground" ) );
355  // Do nothing, to avoid flashing.
356 }
357 
358 
359 void C3D_MODEL_VIEWER::OnMouseWheel( wxMouseEvent& event )
360 {
361  wxLogTrace( m_logTrace, wxT( "C3D_MODEL_VIEWER::OnMouseWheel" ) );
362 
363  if( event.ShiftDown() )
364  {
365  //if( event.GetWheelRotation() < 0 )
366  //SetView3D( WXK_UP ); // move up
367  //else
368  //SetView3D( WXK_DOWN ); // move down
369  }
370  else if( event.ControlDown() )
371  {
372  //if( event.GetWheelRotation() > 0 )
373  //SetView3D( WXK_RIGHT ); // move right
374  //else
375  //SetView3D( WXK_LEFT ); // move left
376  }
377  else
378  {
379  m_trackBallCamera.Zoom( event.GetWheelRotation() > 0 ? 1.1f : 1/1.1f );
380 
381  //DisplayStatus();
382  Refresh( false );
383  }
384 
385  m_trackBallCamera.SetCurMousePosition( event.GetPosition() );
386 }
387 
388 
389 #ifdef USE_OSX_MAGNIFY_EVENT
390 void C3D_MODEL_VIEWER::OnMagnify( wxMouseEvent& event )
391 {
392 }
393 #endif
394 
395 
396 void C3D_MODEL_VIEWER::OnMouseMove( wxMouseEvent& event )
397 {
398  m_trackBallCamera.SetCurWindowSize( GetClientSize() );
399 
400  if( event.Dragging() )
401  {
402  if( event.LeftIsDown() ) // Drag
403  m_trackBallCamera.Drag( event.GetPosition() );
404 
405  // orientation has changed, redraw mesh
406  Refresh( false );
407  }
408 
409  m_trackBallCamera.SetCurMousePosition( event.GetPosition() );
410 }
411 
412 
413 void C3D_MODEL_VIEWER::OnLeftDown( wxMouseEvent& event )
414 {
415  event.Skip();
416 }
417 
418 
419 void C3D_MODEL_VIEWER::OnLeftUp( wxMouseEvent& event )
420 {
421  event.Skip();
422 }
423 
424 
425 void C3D_MODEL_VIEWER::OnMiddleDown( wxMouseEvent& event )
426 {
427  event.Skip();
428 }
429 
430 
431 void C3D_MODEL_VIEWER::OnMiddleUp( wxMouseEvent& event )
432 {
433  event.Skip();
434 }
435 
436 
437 void C3D_MODEL_VIEWER::OnRightClick( wxMouseEvent& event )
438 {
439  event.Skip();
440 }
441 
const S3DMODEL * m_3d_model
Original 3d model data.
bool m_reload_is_needed
Flag that we have a new model and it need to be reloaded when the paint is called.
void DrawRoundArrow(SFVEC3F aPosition, SFVEC3F aTargetPos, float aSize)
Draw a round arrow.
#define UNITS3D_TO_UNITSPCB
Scale conversion from 3d model units to pcb units.
wxGLContext * m_glRC
openGL context
void OnMiddleDown(wxMouseEvent &event)
Implementation of conversion functions that require both schematic and board internal units.
const glm::mat4 & GetProjectionMatrix() const
Definition: camera.cpp:376
glm::vec4 SFVEC4F
Definition: xv3d_types.h:46
void OnLeftDown(wxMouseEvent &event)
void OnMouseWheel(wxMouseEvent &event)
wxGLCanvas wrapper for HiDPI/Retina support.
MODEL_3D * m_ogl_3dmodel
Class holder for 3d model to display on openGL.
void OnMiddleUp(wxMouseEvent &event)
Cache for storing the 3D shapes.
Definition: 3d_cache.h:52
const glm::mat4 GetRotationMatrix() const
Get the rotation matrix to be applied in a transformation camera.
Definition: camera.cpp:151
Implement a canvas based on a wxGLCanvas.
void UnlockCtx(wxGLContext *aContext)
Allow other canvases to bind an OpenGL context.
SMESH * m_Meshes
The meshes list of this model.
Definition: c3dmodel.h:93
C3D_MODEL_VIEWER(wxWindow *aParent, const int *aAttribList=0, S3D_CACHE *aCacheManager=nullptr)
Create a new 3D Canvas with a attribute list.
S3DMODEL * GetModel(const wxString &aModelFileName)
Attempt to load the scene data for a model and to translate it into an S3D_MODEL structure for displa...
Definition: 3d_cache.cpp:646
static GL_CONTEXT_MANAGER & Get()
Return the GL_CONTEXT_MANAGER instance (singleton).
SFVEC3F GetCenter() const
Return the center point of the bounding box.
Definition: bbox_3d.cpp:132
#define RANGE_SCALE_3D
void Refresh()
Update the board display after modifying it by a python script (note: it is automatically called by a...
void OnRightClick(wxMouseEvent &event)
Use all material properties from model file.
float GetMaxDimension() const
Definition: bbox_3d.cpp:167
const glm::mat4 & GetViewMatrix() const
Definition: camera.cpp:406
void Set3DModel(const S3DMODEL &a3DModel)
Set this model to be displayed.
void SetCurMousePosition(const wxPoint &aPosition)
Update the current mouse position without make any new calculations on camera.
Definition: camera.cpp:418
EVT_MIDDLE_DOWN(mpWindow::OnMouseMiddleDown) EVT_MOUSEWHEEL(mpWindow
Definition: mathplot.cpp:1680
S3D_CACHE * m_cacheManager
Optional cache manager.
wxGLContext * CreateCtx(wxGLCanvas *aCanvas, const wxGLContext *aOther=nullptr)
Create a managed OpenGL context.
unsigned int m_MaterialsSize
Number of materials in the material array.
Definition: c3dmodel.h:95
void LockCtx(wxGLContext *aContext, wxGLCanvas *aCanvas)
Set a context as current and prevents other canvases from switching it.
bool Zoom(float aFactor)
Definition: camera.cpp:461
double m_BiuTo3dUnits
factor to convert the model or any other items to keep it in relation to the +/-RANGE_SCALE_3D (it is...
TRACK_BALL m_trackBallCamera
Camera used in this canvas.
bool SetCurWindowSize(const wxSize &aSize)
Update the windows size of the camera.
Definition: camera.cpp:435
void DrawOpaque(bool aUseSelectedMaterial, SFVEC3F aSelectionColor=SFVEC3F(0.0f)) const
Render the model into the current context.
Definition: 3d_model.h:58
static const wxChar * m_logTrace
Trace mask used to enable or disable the trace output of this class.
static void EndDrawMulti()
Cleanup render states after 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:66
glm::vec3 SFVEC3F
Definition: xv3d_types.h:44
void OnLeftUp(wxMouseEvent &event)
SMATERIAL * m_Materials
The materials list of this model.
Definition: c3dmodel.h:96
Define generic OpenGL functions that are common to any OpenGL target.
void OnEraseBackground(wxEraseEvent &event)
const BBOX_3D & GetBBox() const
Get the main bounding box.
Definition: 3d_model.h:96
void OnMouseMove(wxMouseEvent &event)
void DestroyCtx(wxGLContext *aContext)
Destroy a managed OpenGL context.
Store the a model based on meshes and materials.
Definition: c3dmodel.h:90
void Clear3DModel()
Unload the displayed 3D model.
bool m_ogl_initialized
Flag if open gl was initialized.
void OnPaint(wxPaintEvent &event)
static void BeginDrawMulti(bool aUseColorInformation)
Set some basic render states before drawing multiple models.
Definition: 3d_model.cpp:372
unsigned int m_MeshesSize
Number of meshes in the array.
Definition: c3dmodel.h:92
Implement a legacy 3D model render.
void Drag(const wxPoint &aNewMousePosition) override
Calculate a new mouse drag position.
Definition: track_ball.cpp:51