|
1 /* |
|
2 * Name : Overlay.cpp |
|
3 * Description : |
|
4 * Project : This file is part of OpenMAR, an Open Mobile Augmented Reality browser |
|
5 * Website : http://OpenMAR.org |
|
6 * |
|
7 * Copyright (c) 2010 David Caabeiro |
|
8 * |
|
9 * All rights reserved. This program and the accompanying materials are made available |
|
10 * under the terms of the Eclipse Public License v1.0 which accompanies this |
|
11 * distribution, and is available at http://www.eclipse.org/legal/epl-v10.html |
|
12 * |
|
13 */ |
|
14 |
|
15 #include "Overlay.h" |
|
16 |
|
17 #include <coemain.h> |
|
18 #include <gdi.h> |
|
19 #include <w32std.h> |
|
20 |
|
21 #include "Vector3d.h" |
|
22 #include "Vector4d.h" |
|
23 |
|
24 #include "Accelerometer.h" |
|
25 #include "Magnetometer.h" |
|
26 #include "AutoRotation.h" |
|
27 |
|
28 #include "Logger.h" |
|
29 |
|
30 #include "Manager.h" |
|
31 |
|
32 COverlay* COverlay::NewL(SParameter& aParameter) |
|
33 { |
|
34 COverlay* self = new(ELeave) COverlay(aParameter); |
|
35 CleanupStack::PushL(self); |
|
36 self->ConstructL(); |
|
37 CleanupStack::Pop(self); |
|
38 |
|
39 return self; |
|
40 } |
|
41 |
|
42 COverlay::COverlay(SParameter& aParameter) |
|
43 : iWindow(aParameter.iWindow), iRect(aParameter.iRect) |
|
44 {} |
|
45 |
|
46 void COverlay::ConstructL() |
|
47 { |
|
48 LOGTXT("Initializing EGL.."); |
|
49 |
|
50 iEglDisplay = ::eglGetDisplay(EGL_DEFAULT_DISPLAY); |
|
51 |
|
52 if (iEglDisplay == 0) |
|
53 { |
|
54 _LIT(KGetDisplayFailed, "eglGetDisplay failed"); |
|
55 User::Panic(KGetDisplayFailed, 0); |
|
56 } |
|
57 |
|
58 if (::eglInitialize(iEglDisplay, 0, 0) == EGL_FALSE) |
|
59 { |
|
60 _LIT(KInitializeFailed, "eglInitialize failed"); |
|
61 User::Panic(KInitializeFailed, 0); |
|
62 } |
|
63 |
|
64 EGLConfig* configList = 0; |
|
65 EGLint configSize = 0; |
|
66 EGLint numOfConfigs = 0; |
|
67 |
|
68 // Get the number of possible EGLConfigs |
|
69 if (::eglGetConfigs(iEglDisplay, configList, configSize, &numOfConfigs) == EGL_FALSE) |
|
70 { |
|
71 _LIT(KGetConfigsFailed, "eglGetConfigs failed"); |
|
72 User::Panic( KGetConfigsFailed, 0 ); |
|
73 } |
|
74 |
|
75 configSize = numOfConfigs; |
|
76 |
|
77 // Allocate memory for the configList |
|
78 configList = (EGLConfig*) User::Alloc(sizeof(EGLConfig) * configSize); |
|
79 if (configList == 0) |
|
80 { |
|
81 _LIT(KConfigAllocFailed, "Config alloc failed"); |
|
82 User::Panic(KConfigAllocFailed, 0); |
|
83 } |
|
84 |
|
85 /* |
|
86 * Define properties for the wanted EGLSurface. To get the best possible |
|
87 * performance, choose an EGLConfig with a buffersize matching the current |
|
88 * window's display mode |
|
89 */ |
|
90 |
|
91 TDisplayMode displayMode = iWindow.DisplayMode(); |
|
92 TInt bufferSize = 0; |
|
93 |
|
94 switch (displayMode) |
|
95 { |
|
96 case EColor4K: |
|
97 bufferSize = 12; |
|
98 break; |
|
99 |
|
100 case EColor64K: |
|
101 bufferSize = 16; |
|
102 break; |
|
103 |
|
104 case EColor16M: |
|
105 bufferSize = 24; |
|
106 break; |
|
107 |
|
108 case EColor16MU: |
|
109 case EColor16MA: |
|
110 case EColor16MAP: |
|
111 bufferSize = 32; |
|
112 break; |
|
113 |
|
114 default: |
|
115 _LIT(KDisplayModeError, "Unsupported display mode"); |
|
116 User::Panic(KDisplayModeError, 0); |
|
117 break; |
|
118 } |
|
119 |
|
120 // Define properties for the wanted EGLSurface |
|
121 const EGLint attrib_list[] = { |
|
122 EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, |
|
123 // EGL_TRANSPARENT_TYPE, EGL_TRANSPARENT_RGB, |
|
124 EGL_BUFFER_SIZE, bufferSize, |
|
125 EGL_NONE |
|
126 }; |
|
127 |
|
128 // Choose an EGLConfig that best matches to the properties in attrib_list_fsaa |
|
129 if (::eglChooseConfig(iEglDisplay, attrib_list, configList, configSize, &numOfConfigs) == EGL_FALSE) |
|
130 { |
|
131 _LIT( KChooseConfigFailed, "eglChooseConfig failed"); |
|
132 User::Panic(KChooseConfigFailed, 0); |
|
133 } |
|
134 |
|
135 iConfig = configList[0]; // Choose the best EGLConfig. EGLConfigs |
|
136 // returned by eglChooseConfig are sorted so |
|
137 // that the best matching EGLConfig is first in |
|
138 // the list. |
|
139 User::Free(configList); |
|
140 |
|
141 TInt width = iRect.Size().iWidth; |
|
142 TInt height = iRect.Size().iHeight; |
|
143 |
|
144 LOGARG("Window size is %d x %d", width, height); |
|
145 |
|
146 const EGLint attrib_list2[] = { |
|
147 EGL_WIDTH, width, |
|
148 EGL_HEIGHT, height, |
|
149 EGL_NONE |
|
150 }; |
|
151 |
|
152 // Create a window where the graphics are blitted |
|
153 iEglSurface = ::eglCreatePbufferSurface(iEglDisplay, iConfig, attrib_list2); |
|
154 |
|
155 if (iEglSurface == 0) |
|
156 { |
|
157 _LIT(KCreateWindowSurfaceFailed, "eglCreateWindowSurface failed"); |
|
158 User::Panic(KCreateWindowSurfaceFailed, 0); |
|
159 } |
|
160 |
|
161 // Create a rendering context |
|
162 iEglContext = ::eglCreateContext(iEglDisplay, iConfig, EGL_NO_CONTEXT, 0); |
|
163 |
|
164 if (iEglContext == 0) |
|
165 { |
|
166 _LIT(KCreateContextFailed, "eglCreateContext failed"); |
|
167 User::Panic(KCreateContextFailed, 0); |
|
168 } |
|
169 |
|
170 // Make the context current. Binds context to the current rendering thread and surface. |
|
171 if (::eglMakeCurrent(iEglDisplay, iEglSurface, iEglSurface, iEglContext) == EGL_FALSE) |
|
172 { |
|
173 _LIT(KMakeCurrentFailed, "eglMakeCurrent failed"); |
|
174 User::Panic(KMakeCurrentFailed, 0); |
|
175 } |
|
176 |
|
177 // Create a Symbian bitmap where the graphics from the Pbuffer are copied |
|
178 iPixmap = new(ELeave) CWsBitmap(CCoeEnv::Static()->WsSession()); |
|
179 iPixmap->Create(iRect.Size(), iWindow.DisplayMode()); |
|
180 |
|
181 // Manager is in charge of POIs from different providers |
|
182 iManager = CManager::NewL(); |
|
183 } |
|
184 |
|
185 COverlay::~COverlay() |
|
186 { |
|
187 delete iManager; |
|
188 |
|
189 delete iPixmap; |
|
190 iPixmap = 0; |
|
191 |
|
192 ::eglMakeCurrent(iEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); |
|
193 ::eglDestroyContext(iEglDisplay, iEglContext); |
|
194 ::eglDestroySurface(iEglDisplay, iEglSurface); |
|
195 ::eglTerminate(iEglDisplay); |
|
196 } |
|
197 |
|
198 void COverlay::StartL() |
|
199 { |
|
200 // First off we enable the position and orientation sensors |
|
201 iPosition = CPosition::NewL(*this); |
|
202 iPosition->Request(); |
|
203 |
|
204 #if defined(__MARM__) |
|
205 iAccelerometer = CAccelerometer::NewL(); |
|
206 iAccelerometer->StartL(); |
|
207 |
|
208 iMagnetometer = CMagnetometer::NewL(); |
|
209 iMagnetometer->StartL(); |
|
210 |
|
211 iAutoRotation = CAutoRotation::NewL(); |
|
212 iAutoRotation->ResetL(); |
|
213 #endif |
|
214 |
|
215 // We now calculate the view frustum and create the appropriate projection matrix |
|
216 TReal near = 1.0f; |
|
217 TReal far = 3000.0f; |
|
218 |
|
219 TReal fovy = 0; |
|
220 Math::Tan(fovy, 45 * KDegToRad / 2); |
|
221 |
|
222 TInt width = iWindow.Size().iWidth; |
|
223 TInt height = iWindow.Size().iHeight; |
|
224 TReal aspectRatio = static_cast<TReal>(width) / height; |
|
225 |
|
226 TReal top = near * fovy; |
|
227 TReal bottom = -top; |
|
228 TReal left = bottom * aspectRatio; |
|
229 TReal right = top * aspectRatio; |
|
230 |
|
231 iProjection.Load( |
|
232 2 * near / (right - left), 0, 0, 0, |
|
233 0 , 2 * near / (top - bottom), 0, 0, |
|
234 (right + left) / (right - left), (top + bottom) / (top - bottom), -(far + near) / (far - near), -1, |
|
235 0 , 0 , - 2 * far * near / (far - near), 0 |
|
236 ); |
|
237 |
|
238 ::glViewport(0, 0, width, height); |
|
239 ::glMatrixMode(GL_PROJECTION); |
|
240 ::glLoadMatrixf(iProjection.m); |
|
241 |
|
242 ::glMatrixMode(GL_MODELVIEW); |
|
243 |
|
244 ::glEnableClientState(GL_VERTEX_ARRAY); |
|
245 ::glEnableClientState(GL_TEXTURE_COORD_ARRAY); |
|
246 // ::glEnableClientState(GL_NORMAL_ARRAY); |
|
247 |
|
248 ::glEnable(GL_DEPTH_TEST); |
|
249 ::glDepthFunc(GL_LESS); |
|
250 |
|
251 // Set background transparency |
|
252 ::glClearColor(0.0f, 0.0f, 0.0f, 0.0f); |
|
253 } |
|
254 |
|
255 void COverlay::Stop() |
|
256 { |
|
257 ::glDisable(GL_DEPTH_TEST); |
|
258 |
|
259 // ::glDisableClientState(GL_NORMAL_ARRAY); |
|
260 ::glDisableClientState(GL_TEXTURE_COORD_ARRAY); |
|
261 ::glDisableClientState(GL_VERTEX_ARRAY); |
|
262 |
|
263 #if defined(__MARM__) |
|
264 iAutoRotation->RestoreL(); |
|
265 delete iAutoRotation; |
|
266 iAutoRotation = 0; |
|
267 |
|
268 iMagnetometer->Stop(); |
|
269 delete iMagnetometer; |
|
270 iMagnetometer = 0; |
|
271 |
|
272 iAccelerometer->Stop(); |
|
273 delete iAccelerometer; |
|
274 iAccelerometer = 0; |
|
275 #endif |
|
276 |
|
277 delete iPosition; |
|
278 iPosition = 0; |
|
279 } |
|
280 |
|
281 const CFbsBitmap& COverlay::RenderScene() |
|
282 { |
|
283 // Estimate orientation based on sensor data |
|
284 #if defined(__MARM__) |
|
285 /* |
|
286 * +X is defined as the cross product Y.Z (it is tangential to |
|
287 * the ground at the device's current location and roughly points East) |
|
288 * +Y is tangential to the ground at the device's current location and |
|
289 * points towards the magnetic North Pole |
|
290 * +Z points towards the sky and is perpendicular to the ground |
|
291 */ |
|
292 Vector3d A = iAccelerometer->GetValue(); |
|
293 Vector3d E = iMagnetometer->GetValue(); |
|
294 |
|
295 Vector3d H = Vector3d::Cross(E, A); |
|
296 Scalar hNorm = H.Norm(); |
|
297 H.mX /= hNorm; |
|
298 H.mY /= hNorm; |
|
299 H.mZ /= hNorm; |
|
300 |
|
301 Scalar aNorm = A.Norm(); |
|
302 A.mX /= aNorm; |
|
303 A.mY /= aNorm; |
|
304 A.mZ /= aNorm; |
|
305 |
|
306 Vector3d M = Vector3d::Cross(A, H); |
|
307 |
|
308 iModelView.Load( |
|
309 H.mX, H.mY, H.mZ, 0, |
|
310 M.mX, M.mY, M.mZ, 0, |
|
311 A.mX, A.mY, A.mZ, 0, |
|
312 0, 0, 0, 1 |
|
313 ); |
|
314 ::glLoadMatrixf(iModelView.m); |
|
315 #else |
|
316 ::glLoadIdentity(); |
|
317 #endif |
|
318 |
|
319 ::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
|
320 |
|
321 iManager->Render(); |
|
322 |
|
323 ::eglCopyBuffers(iEglDisplay, iEglSurface, iPixmap); |
|
324 |
|
325 return *iPixmap; |
|
326 } |
|
327 |
|
328 /* |
|
329 * Callback from position provider. We use current position to set up world's origin |
|
330 * and make request to different providers |
|
331 */ |
|
332 void COverlay::PositionUpdateL(TInt aError, const TPosition& aPosition) |
|
333 { |
|
334 LOGARG("Got position: lat=%f,lon=%f,alt=%f", aPosition.Latitude(), aPosition.Longitude(), aPosition.Altitude()); |
|
335 |
|
336 // Set the "world" origin |
|
337 iManager->SetOrigin(aPosition); |
|
338 // Request POIs for that position |
|
339 iManager->RequestL(aPosition); |
|
340 } |
|
341 |
|
342 /* |
|
343 * From the current list of POIs retrieved, find the one that is closest to the |
|
344 * screen center. |
|
345 * |
|
346 * We use both projection and modelview transforms to obtain screen coordinates. |
|
347 * Note that both matrices are in column-major ordering, so we need to transpose them. |
|
348 */ |
|
349 TInt COverlay::GetFocusedPOI() |
|
350 { |
|
351 const TPoint viewportCenter(iRect.Center()); |
|
352 |
|
353 TInt mostCentered = KMaxTInt; |
|
354 TInt focused = KErrNotFound; |
|
355 |
|
356 for (TInt i = 0; i < iManager->iObjectList.Count(); ++i) |
|
357 { |
|
358 const Vector3d position(iManager->iObjectList[i]->GetPosition()); |
|
359 |
|
360 const Vector4d world(position.mX, position.mY, position.mZ, 1); |
|
361 const Vector4d camera(iModelView.Transpose() * world); |
|
362 const Vector4d projection(iProjection.Transpose() * camera); |
|
363 |
|
364 // Screen transformation |
|
365 TReal x = projection.mX / projection.mW; |
|
366 TReal y = projection.mY / projection.mW; |
|
367 TReal z = projection.mZ / projection.mW; |
|
368 |
|
369 TReal screenX = viewportCenter.iX * (x + 1); |
|
370 TReal screenY = viewportCenter.iY * (y + 1); |
|
371 // TReal screenZ = (z + 1) / 2; |
|
372 |
|
373 TBool visible = (x > -1 && x < 1) && (y > -1 && y < 1) && (z > -1 && z < 1); |
|
374 TInt centered = (screenX - viewportCenter.iX) * (screenX - viewportCenter.iX) + (screenY - viewportCenter.iY) * (screenY - viewportCenter.iY); |
|
375 |
|
376 if (visible && centered < mostCentered) |
|
377 { |
|
378 mostCentered = centered; |
|
379 focused = i; |
|
380 } |
|
381 } |
|
382 |
|
383 return focused; |
|
384 } |