0030429: Samples - add simple glfw 3D Viewer sample
[occt.git] / samples / glfw / GlfwOcctView.cpp
1 // Copyright (c) 2019 OPEN CASCADE SAS
2 //
3 // This file is part of the examples of the Open CASCADE Technology software library.
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the "Software"), to deal
7 // in the Software without restriction, including without limitation the rights
8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be included in all
13 // copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
21
22 #include "GlfwOcctView.h"
23
24 #include <AIS_Shape.hxx>
25 #include <Aspect_Handle.hxx>
26 #include <Aspect_DisplayConnection.hxx>
27 #include <BRepPrimAPI_MakeBox.hxx>
28 #include <BRepPrimAPI_MakeCone.hxx>
29 #include <Message.hxx>
30 #include <Message_Messenger.hxx>
31 #include <OpenGl_GraphicDriver.hxx>
32 #include <TopAbs_ShapeEnum.hxx>
33
34 #include <iostream>
35
36 #include <GLFW/glfw3.h>
37
38 // ================================================================
39 // Function : GlfwOcctView
40 // Purpose  :
41 // ================================================================
42 GlfwOcctView::GlfwOcctView()
43 : myCurAction3d (CurAction3d_Nothing),
44   myToRedraw (true)
45 {
46 }
47
48 // ================================================================
49 // Function : ~GlfwOcctView
50 // Purpose  :
51 // ================================================================
52 GlfwOcctView::~GlfwOcctView()
53 {
54 }
55
56 // ================================================================
57 // Function : toView
58 // Purpose  :
59 // ================================================================
60 GlfwOcctView* GlfwOcctView::toView (GLFWwindow* theWin)
61 {
62   return static_cast<GlfwOcctView*>(glfwGetWindowUserPointer (theWin));
63 }
64
65 // ================================================================
66 // Function : errorCallback
67 // Purpose  :
68 // ================================================================
69 void GlfwOcctView::errorCallback (int theError, const char* theDescription)
70 {
71   Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error") + theError + ": " + theDescription, Message_Fail);
72 }
73
74 // ================================================================
75 // Function : run
76 // Purpose  :
77 // ================================================================
78 void GlfwOcctView::run()
79 {
80   initWindow (800, 600, "glfw occt");
81   initViewer();
82   initDemoScene();
83   if (myView.IsNull())
84   {
85     return;
86   }
87
88   myView->MustBeResized();
89   myOcctWindow->Map();
90   mainloop();
91   cleanup();
92 }
93
94 // ================================================================
95 // Function : initWindow
96 // Purpose  :
97 // ================================================================
98 void GlfwOcctView::initWindow (int theWidth, int theHeight, const char* theTitle)
99 {
100   glfwSetErrorCallback (GlfwOcctView::errorCallback);
101   glfwInit();
102   const bool toAskCoreProfile = true;
103   if (toAskCoreProfile)
104   {
105     glfwWindowHint (GLFW_CONTEXT_VERSION_MAJOR, 3);
106     glfwWindowHint (GLFW_CONTEXT_VERSION_MINOR, 3);
107 #if defined (__APPLE__)
108     glfwWindowHint (GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
109 #endif
110     glfwWindowHint (GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
111   }
112   myOcctWindow = new GlfwOcctWindow (theWidth, theHeight, theTitle);
113   glfwSetWindowUserPointer       (myOcctWindow->getGlfwWindow(), this);
114   // window callback
115   glfwSetWindowSizeCallback      (myOcctWindow->getGlfwWindow(), GlfwOcctView::onResizeCallback);
116   glfwSetFramebufferSizeCallback (myOcctWindow->getGlfwWindow(), GlfwOcctView::onFBResizeCallback);
117   // mouse callback
118   glfwSetScrollCallback          (myOcctWindow->getGlfwWindow(), GlfwOcctView::onMouseScrollCallback);
119   glfwSetMouseButtonCallback     (myOcctWindow->getGlfwWindow(), GlfwOcctView::onMouseButtonCallback);
120   glfwSetCursorPosCallback       (myOcctWindow->getGlfwWindow(), GlfwOcctView::onMouseMoveCallback);
121 }
122
123 // ================================================================
124 // Function : initViewer
125 // Purpose  :
126 // ================================================================
127 void GlfwOcctView::initViewer()
128 {
129   if (myOcctWindow.IsNull()
130    || myOcctWindow->getGlfwWindow() == nullptr)
131   {
132     return;
133   }
134
135   Handle(OpenGl_GraphicDriver) aGraphicDriver = new OpenGl_GraphicDriver (myOcctWindow->GetDisplay(), false);
136   Handle(V3d_Viewer) aViewer = new V3d_Viewer (aGraphicDriver);
137   aViewer->SetDefaultLights();
138   aViewer->SetLightOn();
139   aViewer->SetDefaultTypeOfView (V3d_PERSPECTIVE);
140   aViewer->ActivateGrid (Aspect_GT_Rectangular, Aspect_GDM_Lines);
141   myView = aViewer->CreateView();
142   myView->SetImmediateUpdate (false);
143   myView->SetWindow (myOcctWindow, myOcctWindow->NativeGlContext());
144   myView->ChangeRenderingParams().ToShowStats = true;
145   myContext = new AIS_InteractiveContext (aViewer);
146 }
147
148 // ================================================================
149 // Function : initDemoScene
150 // Purpose  :
151 // ================================================================
152 void GlfwOcctView::initDemoScene()
153 {
154   if (myContext.IsNull())
155   {
156     return;
157   }
158
159   myView->TriedronDisplay (Aspect_TOTP_LEFT_LOWER, Quantity_NOC_GOLD, 0.08, V3d_WIREFRAME);
160
161   gp_Ax2 anAxis;
162   anAxis.SetLocation (gp_Pnt (0.0, 0.0, 0.0));
163   Handle(AIS_Shape) aBox = new AIS_Shape (BRepPrimAPI_MakeBox (anAxis, 50, 50, 50).Shape());
164   myContext->Display (aBox, AIS_Shaded, 0, false);
165   anAxis.SetLocation (gp_Pnt (25.0, 125.0, 0.0));
166   Handle(AIS_Shape) aCone = new AIS_Shape (BRepPrimAPI_MakeCone (anAxis, 25, 0, 50).Shape());
167   myContext->Display (aCone, AIS_Shaded, 0, false);
168
169   TCollection_AsciiString aGlInfo;
170   {
171     TColStd_IndexedDataMapOfStringString aRendInfo;
172     myView->DiagnosticInformation (aRendInfo, Graphic3d_DiagnosticInfo_Basic);
173     for (TColStd_IndexedDataMapOfStringString::Iterator aValueIter (aRendInfo); aValueIter.More(); aValueIter.Next())
174     {
175       if (!aGlInfo.IsEmpty()) { aGlInfo += "\n"; }
176       aGlInfo += TCollection_AsciiString("  ") + aValueIter.Key() + ": " + aValueIter.Value();
177     }
178   }
179   Message::DefaultMessenger()->Send (TCollection_AsciiString("OpenGL info:\n") + aGlInfo, Message_Info);
180 }
181
182 // ================================================================
183 // Function : mainloop
184 // Purpose  :
185 // ================================================================
186 void GlfwOcctView::mainloop()
187 {
188   while (!glfwWindowShouldClose (myOcctWindow->getGlfwWindow()))
189   {
190     // glfwPollEvents() for continuous rendering (immediate return if there are no new events)
191     // and glfwWaitEvents() for rendering on demand (something actually happened in the viewer)
192     //glfwPollEvents();
193     glfwWaitEvents();
194     if (!myView.IsNull())
195     {
196       if (myView->IsInvalidated())
197       {
198         myView->Redraw();
199       }
200       else if (myToRedraw)
201       {
202         myView->RedrawImmediate();
203       }
204       myToRedraw = false;
205     }
206   }
207 }
208
209 // ================================================================
210 // Function : cleanup
211 // Purpose  :
212 // ================================================================
213 void GlfwOcctView::cleanup()
214 {
215   if (!myView.IsNull())
216   {
217     myView->Remove();
218   }
219   if (!myOcctWindow.IsNull())
220   {
221     myOcctWindow->Close();
222   }
223   glfwTerminate();
224 }
225
226 // ================================================================
227 // Function : onResize
228 // Purpose  :
229 // ================================================================
230 void GlfwOcctView::onResize (int theWidth, int theHeight)
231 {
232   if (theWidth  != 0
233    && theHeight != 0
234    && !myView.IsNull())
235   {
236     myView->Window()->DoResize();
237     myView->MustBeResized();
238     myView->Invalidate();
239     myView->Redraw();
240     //myToRedraw = true;
241   }
242 }
243
244 // ================================================================
245 // Function : onMouseScroll
246 // Purpose  :
247 // ================================================================
248 void GlfwOcctView::onMouseScroll (double theOffsetX, double theOffsetY)
249 {
250   if (myView.IsNull()) { return; }
251
252   const Graphic3d_Vec2i aPos = myOcctWindow->CursorPosition();
253   myView->StartZoomAtPoint (aPos.x(), aPos.y());
254   myView->ZoomAtPoint (0, 0, int(theOffsetY * 4.0), int(theOffsetY * 4.0));
255   myView->Invalidate();
256   myToRedraw = true;
257 }
258
259 // ================================================================
260 // Function : onMouseButton
261 // Purpose  :
262 // ================================================================
263 void GlfwOcctView::onMouseButton (int theButton, int theAction, int theMods)
264 {
265   if (myView.IsNull()) { return; }
266
267   const Graphic3d_Vec2i aPos = myOcctWindow->CursorPosition();
268   if (theAction != GLFW_PRESS)
269   {
270     myCurAction3d = CurAction3d_Nothing;
271     return;
272   }
273
274   myMouseMin = aPos;
275   myMouseMax = aPos;
276   switch (theButton)
277   {
278     case GLFW_MOUSE_BUTTON_RIGHT:
279     {
280       myCurAction3d = CurAction3d_DynamicRoation;
281       myView->StartRotation (aPos.x(), aPos.y());
282       break;
283     }
284     case GLFW_MOUSE_BUTTON_MIDDLE:
285     {
286       myCurAction3d = CurAction3d_DynamicPanning;
287       break;
288     }
289   }
290 }
291
292 // ================================================================
293 // Function : onMouseMove
294 // Purpose  :
295 // ================================================================
296 void GlfwOcctView::onMouseMove (int thePosX, int thePosY)
297 {
298   if (myView.IsNull()) { return; }
299
300   switch (myCurAction3d)
301   {
302     case CurAction3d_DynamicRoation:
303     {
304       myView->Rotation (thePosX, thePosY);
305       myView->Invalidate();
306       myToRedraw = true;
307       break;
308     }
309     case CurAction3d_DynamicPanning:
310     {
311       myView->Pan (thePosX - myMouseMax.x(), -(thePosY - myMouseMax.y()));
312       myView->Invalidate();
313       myToRedraw = true;
314       myMouseMax.SetValues (thePosX, thePosY);
315       break;
316     }
317     default:
318     {
319       myContext->MoveTo (thePosX, thePosY, myView, false);
320       myToRedraw = true;
321       break;
322     }
323   }
324 }