5b9aae375ba0dba8d17e84f2af5671016ae81a88
[occt.git] / samples / qt / AndroidQt / 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 #include <AndroidQt_Window.h>
15 #include <AndroidQt.h>
16 #include <AndroidQt_UserInteractionParameters.h>
17
18 #include <AIS_Shape.hxx>
19 #include <BRepTools.hxx>
20 #include <BRep_Builder.hxx>
21 #include <OpenGl_GraphicDriver.hxx>
22 #include <Quantity_Color.hxx>
23 #include <Standard_ErrorHandler.hxx>
24 #include <TopoDS.hxx>
25 #include <TopoDS_Face.hxx>
26 #include <TopoDS_Shape.hxx>
27 #include <UnitsAPI.hxx>
28
29 #include <EGL/egl.h>
30 #include <QFileInfo>
31
32 // =======================================================================
33 // function : AndroidQt
34 // purpose  :
35 // =======================================================================
36 AndroidQt::AndroidQt()
37 : myFitAllAction (false)
38 {
39   connect (this, SIGNAL(windowChanged(QQuickWindow*)), this, SLOT(handleWindowChanged(QQuickWindow*)));
40
41   // set shaders location variable
42   QByteArray aDataRoot = "/data/data/org.qtproject.example.AndroidQt/files/opencascade/shared";
43   qputenv ("CSF_ShadersDirectory", aDataRoot + "/Shaders");
44 }
45
46 // =======================================================================
47 // function : ReadShapeFromFile
48 // purpose  :
49 // =======================================================================
50 bool AndroidQt::ReadShapeFromFile (QString theFilePath)
51 {
52   QUrl    aFileUrl   (theFilePath);
53   QString aFilePath = theFilePath;
54   if (aFileUrl.isLocalFile())
55   {
56     aFilePath = QUrl (theFilePath).toLocalFile();
57   }
58
59   if (!QFile (aFilePath).exists())
60   {
61     return false;
62   }
63
64   TopoDS_Shape aShape;
65   BRep_Builder aBuildTool;
66   try
67   {
68     OCC_CATCH_SIGNALS
69
70     if (!BRepTools::Read (aShape, aFilePath.toStdString().c_str(), aBuildTool))
71     {
72       return false;
73     }
74
75     if (!myContext.IsNull())
76     {
77       myContext->EraseAll (Standard_False);
78
79       Handle(AIS_Shape) aShapePrs = new AIS_Shape (aShape);
80       aShapePrs->SetColor (Quantity_Color(1.0, 0.73, 0.2, Quantity_TOC_RGB));
81
82       myContext->Display        (aShapePrs, Standard_False);
83       myContext->SetDisplayMode (aShapePrs, AIS_Shaded, Standard_False);
84     }
85
86     myMutex.lock();
87     myFitAllAction = true;
88     myMutex.unlock();
89
90     if (window())
91     {
92       window()->update();
93     }
94   }
95   catch (Standard_Failure)
96   {
97     return false;
98   }
99
100   return true;
101 }
102
103 // =======================================================================
104 // function : InitTouch
105 // purpose  :
106 // =======================================================================
107 void AndroidQt::InitTouch (const double theX,
108                            const double theY)
109 {
110   myMutex.lock();
111   myTouchPoint.SetStarts (theX, theY);
112   myMutex.unlock();
113 }
114
115 // =======================================================================
116 // function : UpdateTouch
117 // purpose  :
118 // =======================================================================
119 void AndroidQt::UpdateTouch (const double theX,
120                              const double theY)
121 {
122   myMutex.lock();
123   myTouchPoint.SetEnds (theX, theY);
124   myMutex.unlock();
125
126   if (window())
127     window()->update();
128 }
129
130 // =======================================================================
131 // function : handleWindowChanged
132 // purpose  :
133 // =======================================================================
134 void AndroidQt::handleWindowChanged (QQuickWindow* theWin)
135 {
136   if (theWin == NULL)
137   {
138     return;
139   }
140
141   connect(theWin, SIGNAL(beforeSynchronizing()), this, SLOT(sync()), Qt::DirectConnection);
142
143   theWin->setClearBeforeRendering (false);
144 }
145
146 // =======================================================================
147 // function : sync
148 // purpose  :
149 // =======================================================================
150 void AndroidQt::sync()
151 {
152   myViewportSize = window()->size() * window()->devicePixelRatio();
153
154   if (myViewer.IsNull())
155   {
156     initViewer();
157     connect (window(), SIGNAL(beforeRendering()), this, SLOT(paint()), Qt::DirectConnection);
158   }
159   else
160   {
161     Handle(OpenGl_GraphicDriver) aDriver = Handle(OpenGl_GraphicDriver)::DownCast (myViewer->Driver());
162     if (aDriver->getRawGlContext() != eglGetCurrentContext())
163     {
164       initViewer();
165     }
166     else
167     {
168       Handle(AndroidQt_Window) aWindow = Handle(AndroidQt_Window)::DownCast (myView->Window());
169       aWindow->SetSize (myViewportSize.width(), myViewportSize.height());
170       //myView->MustBeResized(); // can be used instead of SetWindow() when EGLsurface has not been changed
171
172       EGLContext anEglContext = eglGetCurrentContext();
173       myView->SetWindow (aWindow, (Aspect_RenderingContext )anEglContext, NULL, NULL);
174     }
175   }
176 }
177
178 // =======================================================================
179 // function : paint
180 // purpose  :
181 // =======================================================================
182 void AndroidQt::paint()
183 {
184   myMutex.lock();
185
186   if (Abs(myTouchPoint.DevX()) + Abs(myTouchPoint.DevY()) > 1)
187   {
188     myView->StartRotation (myTouchPoint.X().first,  myTouchPoint.Y().first);
189     myView->Rotation      (myTouchPoint.X().second, myTouchPoint.Y().second);
190
191     myTouchPoint.ClearDev();
192   }
193
194   if (myFitAllAction)
195   {
196     myView->FitAll();
197     myFitAllAction = false;
198   }
199
200   myMutex.unlock();
201
202   myView->Redraw();
203 }
204
205 // =======================================================================
206 // function : initViewer
207 // purpose  :
208 // =======================================================================
209 bool AndroidQt::initViewer()
210 {
211   EGLint aCfgId = 0;
212   int aWidth = 0, aHeight = 0;
213   EGLDisplay anEglDisplay = eglGetCurrentDisplay();
214   EGLContext anEglContext = eglGetCurrentContext();
215   EGLSurface anEglSurf    = eglGetCurrentSurface (EGL_DRAW);
216
217   if (anEglDisplay == EGL_NO_DISPLAY
218    || anEglContext == EGL_NO_CONTEXT
219    || anEglSurf    == EGL_NO_SURFACE)
220   {
221     release();
222     return false;
223   }
224
225   eglQuerySurface (anEglDisplay, anEglSurf, EGL_WIDTH,     &aWidth);
226   eglQuerySurface (anEglDisplay, anEglSurf, EGL_HEIGHT,    &aHeight);
227   eglQuerySurface (anEglDisplay, anEglSurf, EGL_CONFIG_ID, &aCfgId);
228
229   const EGLint aConfigAttribs[] = { EGL_CONFIG_ID, aCfgId, EGL_NONE };
230   EGLint       aNbConfigs = 0;
231   void*        anEglConfig = NULL;
232
233   if (eglChooseConfig (anEglDisplay, aConfigAttribs, &anEglConfig, 1, &aNbConfigs) != EGL_TRUE)
234   {
235     release();
236     return false;
237   }
238
239   if (!myViewer.IsNull())
240   {
241     Handle(OpenGl_GraphicDriver) aDriver = Handle(OpenGl_GraphicDriver)::DownCast (myViewer->Driver());
242     Handle(AndroidQt_Window)     aWindow = Handle(AndroidQt_Window)::DownCast (myView->Window());
243     if (!aDriver->InitEglContext (anEglDisplay, anEglContext, anEglConfig))
244     {
245       release();
246       return false;
247     }
248
249     aWindow->SetSize (aWidth, aHeight);
250     myView->SetWindow (aWindow, (Aspect_RenderingContext )anEglContext, NULL, NULL);
251   }
252
253   Handle(OpenGl_GraphicDriver) aDriver = new OpenGl_GraphicDriver (NULL, Standard_False);
254   aDriver->ChangeOptions().buffersNoSwap = Standard_True;
255   //aDriver->ChangeOptions().glslWarnings  = Standard_True; // for GLSL shaders debug
256
257   if (!aDriver->InitEglContext (anEglDisplay, anEglContext, anEglConfig))
258   {
259     release();
260     return false;
261   }
262
263   // create viewer
264   myViewer = new V3d_Viewer (aDriver);
265   myViewer->SetDefaultBackgroundColor (AndroidQt_UserInteractionParameters::BgColor.Name());
266   myViewer->SetDefaultLights();
267   myViewer->SetLightOn();
268
269   // create AIS context
270   myContext = new AIS_InteractiveContext (myViewer);
271   myContext->SetDisplayMode (AIS_Shaded, false);
272
273   Handle(AndroidQt_Window) aWindow = new AndroidQt_Window (aWidth, aHeight);
274   myView = myViewer->CreateView();
275
276   myView->SetWindow (aWindow, (Aspect_RenderingContext )anEglContext, NULL, NULL);
277   myView->TriedronDisplay (Aspect_TOTP_RIGHT_LOWER, Quantity_NOC_WHITE, 0.08, V3d_ZBUFFER);
278
279   return true;
280 }
281
282 // =======================================================================
283 // function : release
284 // purpose  :
285 // =======================================================================
286 void AndroidQt::release()
287 {
288   myContext.Nullify();
289   myView.Nullify();
290   myViewer.Nullify();
291 }