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