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