0029631: Samples: build AndroidQt sample together with OCCT on Windows platform
[occt.git] / samples / qt / AndroidQt / src / AndroidQt.cxx
1 // Copyright (c) 2014 OPEN CASCADE SAS
2 //
3 // This file is part of Open CASCADE Technology software library.
4 //
5 // This library is free software; you can redistribute it and/or modify it under
6 // the terms of the GNU Lesser General Public License version 2.1 as published
7 // by the Free Software Foundation, with special exception defined in the file
8 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
9 // distribution for complete text of the license and disclaimer of any warranty.
10 //
11 // Alternatively, this file may be used under the terms of Open CASCADE
12 // commercial license or contractual agreement.
13
14 #if defined(_WIN32)
15   #include <windows.h>
16 #endif
17
18 #include "AndroidQt.h"
19 #include "AndroidQt_UserInteractionParameters.h"
20 #include "AndroidQt_Window.h"
21
22 #include <AIS_Shape.hxx>
23 #include <BRepTools.hxx>
24 #include <BRep_Builder.hxx>
25 #include <Message.hxx>
26 #include <Message_Messenger.hxx>
27 #include <OpenGl_GraphicDriver.hxx>
28 #include <Quantity_Color.hxx>
29 #include <Standard_ErrorHandler.hxx>
30 #include <TopoDS.hxx>
31 #include <TopoDS_Face.hxx>
32 #include <TopoDS_Shape.hxx>
33 #include <UnitsAPI.hxx>
34 #include <WNT_Window.hxx>
35
36 #include <EGL/egl.h>
37 #include <QFileInfo>
38
39 // =======================================================================
40 // function : AndroidQt
41 // purpose  :
42 // =======================================================================
43 AndroidQt::AndroidQt()
44 : myFitAllAction (false)
45 {
46   connect (this, SIGNAL (windowChanged (QQuickWindow*)), this, SLOT (handleWindowChanged (QQuickWindow*)));
47
48   // set shaders location variable
49   QByteArray aDataRoot = "/data/data/org.qtproject.example.AndroidQt/files/opencascade/shared";
50   qputenv ("CSF_ShadersDirectory", aDataRoot + "/Shaders");
51 }
52
53 // =======================================================================
54 // function : ReadShapeFromFile
55 // purpose  :
56 // =======================================================================
57 bool AndroidQt::ReadShapeFromFile (QString theFilePath)
58 {
59   QUrl    aFileUrl   (theFilePath);
60   QString aFilePath = theFilePath;
61   if (aFileUrl.isLocalFile())
62   {
63     aFilePath = QUrl (theFilePath).toLocalFile();
64   }
65
66   if (!QFile (aFilePath).exists())
67   {
68     return false;
69   }
70
71   TopoDS_Shape aShape;
72   BRep_Builder aBuildTool;
73   try
74   {
75     OCC_CATCH_SIGNALS
76
77     if (!BRepTools::Read (aShape, aFilePath.toStdString().c_str(), aBuildTool))
78     {
79       return false;
80     }
81
82     if (!myContext.IsNull())
83     {
84       myContext->EraseAll (Standard_False);
85
86       Handle(AIS_Shape) aShapePrs = new AIS_Shape (aShape);
87       aShapePrs->SetColor (Quantity_Color(1.0, 0.73, 0.2, Quantity_TOC_RGB));
88
89       myContext->Display        (aShapePrs, Standard_False);
90       myContext->SetDisplayMode (aShapePrs, AIS_Shaded, Standard_False);
91     }
92
93     myMutex.lock();
94     myFitAllAction = true;
95     myMutex.unlock();
96
97     if (window())
98     {
99       window()->update();
100     }
101   }
102   catch (Standard_Failure)
103   {
104     return false;
105   }
106
107   return true;
108 }
109
110 // =======================================================================
111 // function : InitTouch
112 // purpose  :
113 // =======================================================================
114 void AndroidQt::InitTouch (const double theX,
115                            const double theY)
116 {
117   myMutex.lock();
118   myTouchPoint.SetStarts (theX, theY);
119   myMutex.unlock();
120 }
121
122 // =======================================================================
123 // function : UpdateTouch
124 // purpose  :
125 // =======================================================================
126 void AndroidQt::UpdateTouch (const double theX,
127                              const double theY)
128 {
129   myMutex.lock();
130   myTouchPoint.SetEnds (theX, theY);
131   myMutex.unlock();
132
133   if (window())
134     window()->update();
135 }
136
137 // =======================================================================
138 // function : handleWindowChanged
139 // purpose  :
140 // =======================================================================
141 void AndroidQt::handleWindowChanged (QQuickWindow* theWin)
142 {
143   if (theWin == NULL)
144   {
145     return;
146   }
147
148   connect (theWin, SIGNAL (beforeSynchronizing()), this, SLOT (sync()), Qt::DirectConnection);
149
150   theWin->setClearBeforeRendering (false);
151 }
152
153 // =======================================================================
154 // function : sync
155 // purpose  :
156 // =======================================================================
157 void AndroidQt::sync()
158 {
159   myViewportSize = window()->size() * window()->devicePixelRatio();
160
161   Graphic3d_Vec2i aWinTopLeft (window()->x(), window()->y());
162   Graphic3d_Vec2i aWinSize (myViewportSize.width(), myViewportSize.height());
163   const bool isChangedLeft = (myWinTopLeft.x() != aWinTopLeft.x());
164   const bool isChangedTop = (myWinTopLeft.y() != aWinTopLeft.y());
165   myWinTopLeft = aWinTopLeft;
166
167   if (myViewer.IsNull())
168   {
169     initViewer (Aspect_Drawable (window()->winId()));
170     connect (window(), SIGNAL (beforeRendering()), this, SLOT (paint()), Qt::DirectConnection);
171   }
172   else
173   {
174     Handle(OpenGl_GraphicDriver) aDriver = Handle(OpenGl_GraphicDriver)::DownCast (myViewer->Driver());
175   #ifdef __ANDROID__
176     if (aDriver->getRawGlContext() != eglGetCurrentContext())
177     {
178       initViewer (Aspect_Drawable (window()->winId()));
179     }
180     else
181   #endif
182     {
183     #ifdef __ANDROID__
184       Handle(AndroidQt_Window) aWindow = Handle(AndroidQt_Window)::DownCast(myView->Window());
185       aWindow->SetSize (myViewportSize.width(), myViewportSize.height());
186       //myView->MustBeResized(); // can be used instead of SetWindow() when EGLsurface has not been changed
187
188       EGLContext anEglContext = eglGetCurrentContext();
189       myView->SetWindow (aWindow, (Aspect_RenderingContext )anEglContext);
190     #else
191       if (aWinSize.x() != myWinSize.x()
192        || aWinSize.y() != myWinSize.y())
193       {
194         myView->MustBeResized();
195         myView->Invalidate();
196       }
197       else if (isChangedTop)
198       {
199         myView->MustBeResized();
200       }
201       else if (isChangedLeft)
202       {
203         myView->MustBeResized();
204       }
205     #endif
206     }
207   }
208   myWinSize = aWinSize;
209 }
210
211 // =======================================================================
212 // function : paint
213 // purpose  :
214 // =======================================================================
215 void AndroidQt::paint()
216 {
217   myMutex.lock();
218
219   if (Abs (myTouchPoint.DevX()) + Abs (myTouchPoint.DevY()) > 1)
220   {
221     myView->StartRotation (myTouchPoint.X().first,  myTouchPoint.Y().first);
222     myView->Rotation      (myTouchPoint.X().second, myTouchPoint.Y().second);
223
224     myTouchPoint.ClearDev();
225   }
226
227   if (myFitAllAction)
228   {
229     myView->FitAll();
230     myFitAllAction = false;
231   }
232
233   myMutex.unlock();
234
235   myView->Redraw();
236 }
237
238 // =======================================================================
239 // function : initViewer
240 // purpose  :
241 // =======================================================================
242 bool AndroidQt::initViewer (Aspect_Drawable theWin)
243 {
244   int aWidth = 0, aHeight = 0;
245   Handle(Aspect_DisplayConnection) aDisplayConnection;
246 #ifdef __ANDROID__
247   EGLint aCfgId = 0;
248   EGLDisplay anEglDisplay = eglGetCurrentDisplay();
249   EGLContext anEglContext = eglGetCurrentContext();
250   EGLSurface anEglSurf    = eglGetCurrentSurface (EGL_DRAW);
251
252   if (anEglDisplay == EGL_NO_DISPLAY
253    || anEglContext == EGL_NO_CONTEXT
254    || anEglSurf    == EGL_NO_SURFACE)
255   {
256     release();
257     return false;
258   }
259
260   eglQuerySurface (anEglDisplay, anEglSurf, EGL_WIDTH,     &aWidth);
261   eglQuerySurface (anEglDisplay, anEglSurf, EGL_HEIGHT,    &aHeight);
262   eglQuerySurface (anEglDisplay, anEglSurf, EGL_CONFIG_ID, &aCfgId);
263
264   const EGLint aConfigAttribs[] = { EGL_CONFIG_ID, aCfgId, EGL_NONE };
265   EGLint       aNbConfigs = 0;
266   void*        anEglConfig = NULL;
267
268   if (eglChooseConfig (anEglDisplay, aConfigAttribs, &anEglConfig, 1, &aNbConfigs) != EGL_TRUE)
269   {
270     Message::DefaultMessenger()->Send ("Error: EGL does not provide compatible configurations", Message_Fail);
271     release();
272     return false;
273   }
274
275   if (!myViewer.IsNull())
276   {
277     Handle(OpenGl_GraphicDriver) aDriver = Handle(OpenGl_GraphicDriver)::DownCast (myViewer->Driver());
278     Handle(AndroidQt_Window)     aWindow = Handle(AndroidQt_Window)::DownCast (myView->Window());
279     if (!aDriver->InitEglContext (anEglDisplay, anEglContext, anEglConfig))
280     {
281       Message::DefaultMessenger()->Send ("Error: OpenGl_GraphicDriver can not be initialized", Message_Fail);
282       release();
283       return false;
284     }
285
286     aWindow->SetSize (aWidth, aHeight);
287     myView->SetWindow (aWindow, (Aspect_RenderingContext )anEglContext);
288   }
289
290   Handle(OpenGl_GraphicDriver) aDriver = new OpenGl_GraphicDriver (NULL, Standard_False);
291 #elif defined(_WIN32)
292   HWND  aWinHandle = (HWND)theWin;
293   HDC   aWindowDC = wglGetCurrentDC();
294   HGLRC aRendCtx   = wglGetCurrentContext();
295   if (aWinHandle == NULL
296    || aWindowDC  == NULL
297    || aRendCtx   == NULL)
298   {
299     Message::DefaultMessenger()->Send ("Error: No active WGL context!", Message_Fail);
300     release();
301     return false;
302   }
303
304   RECT aRect;
305   ::GetClientRect (aWinHandle, &aRect);
306   aWidth  = aRect.right - aRect.left;
307   aHeight = aRect.bottom - aRect.top;
308   myWinSize.x() = aWidth;
309   myWinSize.y() = aHeight;
310   if (!myViewer.IsNull())
311   {
312     Handle(WNT_Window) aWindow = new WNT_Window (aWinHandle);
313     myView->SetWindow (aWindow, (Aspect_RenderingContext)aRendCtx);
314     return true;
315   }
316   Handle(OpenGl_GraphicDriver) aDriver = new OpenGl_GraphicDriver (aDisplayConnection, Standard_False);
317 #endif
318
319   aDriver->ChangeOptions().buffersNoSwap = Standard_True;
320   //aDriver->ChangeOptions().glslWarnings  = Standard_True; // for GLSL shaders debug
321
322 #ifdef __ANDROID__
323   if (!aDriver->InitEglContext (anEglDisplay, anEglContext, anEglConfig))
324   {
325     Message::DefaultMessenger()->Send ("Error: OpenGl_GraphicDriver can not be initialized", Message_Fail);
326     release();
327     return false;
328   }
329 #endif
330
331   // create viewer
332   myViewer = new V3d_Viewer (aDriver);
333   myViewer->SetDefaultBackgroundColor (AndroidQt_UserInteractionParameters::BgColor.Name());
334   myViewer->SetDefaultLights();
335   myViewer->SetLightOn();
336
337   // create AIS context
338   myContext = new AIS_InteractiveContext (myViewer);
339   myContext->SetDisplayMode (AIS_Shaded, false);
340
341 #ifdef __ANDROID__
342   Handle(AndroidQt_Window) aWindow = new AndroidQt_Window (aWidth, aHeight);
343 #elif defined(_WIN32)
344   Handle(WNT_Window)       aWindow = new WNT_Window (aWinHandle);
345 #endif
346
347   myView = myViewer->CreateView();
348   myView->SetImmediateUpdate (Standard_False);
349
350 #ifdef __ANDROID__
351   myView->SetWindow (aWindow, (Aspect_RenderingContext )anEglContext);
352 #else
353   myView->SetWindow (aWindow, (Aspect_RenderingContext )aRendCtx);
354 #endif
355   myView->TriedronDisplay (Aspect_TOTP_RIGHT_LOWER, Quantity_NOC_WHITE, 0.08, V3d_ZBUFFER);
356
357   return true;
358 }
359
360 // =======================================================================
361 // function : release
362 // purpose  :
363 // =======================================================================
364 void AndroidQt::release()
365 {
366   myContext.Nullify();
367   myView.Nullify();
368   myViewer.Nullify();
369 }