1 /******************************************************************************
2 *
3 * Project: OpenCPN
4 * Authors: David Register
5 * Sean D'Epagnier
6 *
7 ***************************************************************************
8 * Copyright (C) 2014 by David S. Register *
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 * This program is distributed in the hope that it will be useful, *
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
18 * GNU General Public License for more details. *
19 * *
20 * You should have received a copy of the GNU General Public License *
21 * along with this program; if not, write to the *
22 * Free Software Foundation, Inc., *
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
24 ***************************************************************************
25 */
26
27 #include <wx/wxprec.h>
28
29 #ifndef WX_PRECOMP
30 #include "wx/wx.h"
31 #endif //precompiled headers
32
33 #include <wx/tokenzr.h>
34
35 #include <stdint.h>
36
37 #include "config.h"
38
39 #if defined( __UNIX__ ) && !defined(__WXOSX__) // high resolution stopwatch for profiling
40 class OCPNStopWatch
41 {
42 public:
OCPNStopWatch()43 OCPNStopWatch() { Reset(); }
Reset()44 void Reset() { clock_gettime(CLOCK_REALTIME, &tp); }
45
GetTime()46 double GetTime() {
47 timespec tp_end;
48 clock_gettime(CLOCK_REALTIME, &tp_end);
49 return (tp_end.tv_sec - tp.tv_sec) * 1.e3 + (tp_end.tv_nsec - tp.tv_nsec) / 1.e6;
50 }
51
52 private:
53 timespec tp;
54 };
55 #endif
56
57
58 #if defined(__OCPN__ANDROID__)
59 #include "androidUTIL.h"
60 #else
61 #include <GL/glx.h>
62 #endif
63
64 #include "dychart.h"
65
66 #include "glChartCanvas.h"
67 #include "chcanv.h"
68 #include "s52plib.h"
69 #include "Quilt.h"
70 #include "pluginmanager.h"
71 #include "routeman.h"
72 #include "chartbase.h"
73 #include "chartimg.h"
74 #include "ChInfoWin.h"
75 #include "thumbwin.h"
76 #include "chartdb.h"
77 #include "navutil.h"
78 #include "TexFont.h"
79 #include "glTexCache.h"
80 #include "gshhs.h"
81 #include "ais.h"
82 #include "OCPNPlatform.h"
83 #include "toolbar.h"
84 #include "piano.h"
85 #include "tcmgr.h"
86 #include "compass.h"
87 #include "FontMgr.h"
88 #include "mipmap/mipmap.h"
89 #include "chartimg.h"
90 #include "Track.h"
91 #include "emboss_data.h"
92 #include "Route.h"
93 #include "mbtiles.h"
94 #include <vector>
95 #include <algorithm>
96
97 #ifndef GL_ETC1_RGB8_OES
98 #define GL_ETC1_RGB8_OES 0x8D64
99 #endif
100
101 #include "cm93.h" // for chart outline draw
102 #include "s57chart.h" // for ArrayOfS57Obj
103 #include "s52plib.h"
104
105 #include "lz4.h"
106
107 #ifdef __OCPN__ANDROID__
108 // arm gcc compiler has a lot of trouble passing doubles as function aruments.
109 // We don't really need double precision here, so fix with a (faster) macro.
110 extern "C" void glOrthof(float left, float right, float bottom, float top, float near, float far);
111 #define glOrtho(a,b,c,d,e,f); glOrthof(a,b,c,d,e,f);
112
113 #endif
114
115 #include "cm93.h" // for chart outline draw
116 #include "s57chart.h" // for ArrayOfS57Obj
117 #include "s52plib.h"
118
119 #ifdef USE_ANDROID_GLES2
120 #include <gl2.h>
121 #include "linmath.h"
122 #include "shaders.h"
123 #endif
124
125 extern bool GetMemoryStatus(int *mem_total, int *mem_used);
126
127 #ifndef GL_DEPTH_STENCIL_ATTACHMENT
128 #define GL_DEPTH_STENCIL_ATTACHMENT 0x821A
129 #endif
130
131 extern s52plib *ps52plib;
132 extern bool g_bopengl;
133 extern bool g_bDebugOGL;
134 extern bool g_bShowFPS;
135 extern bool g_bSoftwareGL;
136 extern bool g_btouch;
137 extern ocpnFloatingToolbarDialog *g_MainToolbar;
138 extern bool g_bShowChartBar;
139 extern glTextureManager *g_glTextureManager;
140 extern bool b_inCompressAllCharts;
141
142 GLenum g_texture_rectangle_format;
143
144 extern int g_memCacheLimit;
145 extern ColorScheme global_color_scheme;
146 extern bool g_bquiting;
147 extern ThumbWin *pthumbwin;
148 extern int g_mipmap_max_level;
149
150 extern double gLat, gLon, gCog, gSog, gHdt;
151
152 extern int g_OwnShipIconType;
153
154
155 extern ChartDB *ChartData;
156
157 extern PlugInManager* g_pi_manager;
158
159 extern WayPointman *pWayPointMan;
160 extern RouteList *pRouteList;
161 extern TrackList *pTrackList;
162 extern bool b_inCompressAllCharts;
163 extern bool g_bGLexpert;
164 extern bool g_bcompression_wait;
165 extern float g_ShipScaleFactorExp;
166
167 float g_GLMinSymbolLineWidth;
168 float g_GLMinCartographicLineWidth;
169
170 extern bool g_fog_overzoom;
171 extern double g_overzoom_emphasis_base;
172 extern bool g_oz_vector_scale;
173 extern TCMgr *ptcmgr;
174 extern int g_nCPUCount;
175 extern bool g_running;
176
177 extern unsigned int g_canvasConfig;
178 extern ChartCanvas *g_focusCanvas;
179 extern ChartCanvas *g_overlayCanvas;
180
181 ocpnGLOptions g_GLOptions;
182
183 wxColor s_regionColor;
184
185 // For VBO(s)
186 bool g_b_EnableVBO;
187 bool g_b_needFinish; //Need glFinish() call on each frame?
188
189
190 PFNGLGENFRAMEBUFFERSEXTPROC s_glGenFramebuffers;
191 PFNGLGENRENDERBUFFERSEXTPROC s_glGenRenderbuffers;
192 PFNGLFRAMEBUFFERTEXTURE2DEXTPROC s_glFramebufferTexture2D;
193 PFNGLBINDFRAMEBUFFEREXTPROC s_glBindFramebuffer;
194 PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC s_glFramebufferRenderbuffer;
195 PFNGLRENDERBUFFERSTORAGEEXTPROC s_glRenderbufferStorage;
196 PFNGLBINDRENDERBUFFEREXTPROC s_glBindRenderbuffer;
197 PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC s_glCheckFramebufferStatus;
198 PFNGLDELETEFRAMEBUFFERSEXTPROC s_glDeleteFramebuffers;
199 PFNGLDELETERENDERBUFFERSEXTPROC s_glDeleteRenderbuffers;
200
201 PFNGLCOMPRESSEDTEXIMAGE2DPROC s_glCompressedTexImage2D;
202 PFNGLGETCOMPRESSEDTEXIMAGEPROC s_glGetCompressedTexImage;
203
204 // Vertex Buffer Object (VBO) support
205 PFNGLGENBUFFERSPROC s_glGenBuffers;
206 PFNGLBINDBUFFERPROC s_glBindBuffer;
207 PFNGLBUFFERDATAPROC s_glBufferData;
208 PFNGLDELETEBUFFERSPROC s_glDeleteBuffers;
209
210 #ifndef USE_ANDROID_GLES2
211 #define glDeleteFramebuffers(a,b) (s_glDeleteFramebuffers)(a,b);
212 #define glDeleteRenderbuffers(a,b) (s_glDeleteRenderbuffers)(a,b);
213 #endif
214
215
216 typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIV) (GLenum target, GLenum value, GLint *data);
217 PFNGLGETBUFFERPARAMETERIV s_glGetBufferParameteriv;
218
219 #include <wx/arrimpl.cpp>
220 //WX_DEFINE_OBJARRAY( ArrayOfTexDescriptors );
221
222 GLuint g_raster_format = GL_RGB;
223 long g_tex_mem_used;
224
225 bool b_timeGL;
226 wxStopWatch g_glstopwatch;
227 double g_gl_ms_per_frame;
228
229 int g_tile_size;
230 int g_uncompressed_tile_size;
231
232 extern wxProgressDialog *pprog;
233 extern bool b_skipout;
234 extern wxSize pprog_size;
235 extern int pprog_count;
236 extern int pprog_threads;
237 extern float g_ChartScaleFactorExp;
238 extern MyFrame *gFrame;
239
240 //#if defined(__MSVC__) && !defined(ocpnUSE_GLES) /* this compiler doesn't support vla */
241 //const
242 //#endif
243 //extern int g_mipmap_max_level;
244 int panx, pany;
245
246 bool glChartCanvas::s_b_useScissorTest;
247 bool glChartCanvas::s_b_useStencil;
248 bool glChartCanvas::s_b_useStencilAP;
249 bool glChartCanvas::s_b_useFBO;
250
251 #ifdef USE_ANDROID_GLES2
252 static int s_tess_vertex_idx;
253 static int s_tess_vertex_idx_this;
254 static int s_tess_buf_len;
255 static GLfloat *s_tess_work_buf;
256 GLenum s_tess_mode;
257 static int s_nvertex;
258 static vec4 s_tess_color;
259 ViewPort s_tessVP;
260 #endif
261
262
263 #if 0
264 /* for debugging */
265 static void print_region(OCPNRegion &Region)
266 {
267 OCPNRegionIterator upd ( Region );
268 while ( upd.HaveRects() )
269 {
270 wxRect rect = upd.GetRect();
271 printf("[(%d, %d) (%d, %d)] ", rect.x, rect.y, rect.width, rect.height);
272 upd.NextRect();
273 }
274 }
275 #endif
276
QueryExtension(const char * extName)277 GLboolean QueryExtension( const char *extName )
278 {
279 /*
280 ** Search for extName in the extensions string. Use of strstr()
281 ** is not sufficient because extension names can be prefixes of
282 ** other extension names. Could use strtok() but the constant
283 ** string returned by glGetString might be in read-only memory.
284 */
285 char *p;
286 char *end;
287 int extNameLen;
288
289 extNameLen = strlen( extName );
290
291 p = (char *) glGetString( GL_EXTENSIONS );
292 if( NULL == p ) {
293 return GL_FALSE;
294 }
295
296 end = p + strlen( p );
297
298 while( p < end ) {
299 int n = strcspn( p, " " );
300 if( ( extNameLen == n ) && ( strncmp( extName, p, n ) == 0 ) ) {
301 return GL_TRUE;
302 }
303 p += ( n + 1 );
304 }
305 return GL_FALSE;
306 }
307
308 typedef void (*GenericFunction)(void);
309
310 #if defined(__WXMSW__)
311 #define systemGetProcAddress(ADDR) wglGetProcAddress(ADDR)
312 #elif defined(__WXOSX__)
313 #include <dlfcn.h>
314 #define systemGetProcAddress(ADDR) dlsym( RTLD_DEFAULT, ADDR)
315 #elif defined(__OCPN__ANDROID__)
316 #define systemGetProcAddress(ADDR) eglGetProcAddress(ADDR)
317 #else
318 #define systemGetProcAddress(ADDR) glXGetProcAddress((const GLubyte*)ADDR)
319 #endif
320
ocpnGetProcAddress(const char * addr,const char * extension)321 GenericFunction ocpnGetProcAddress(const char *addr, const char *extension)
322 {
323 char addrbuf[256];
324 if(!extension)
325 return (GenericFunction)NULL;
326
327 #ifndef __OCPN__ANDROID__
328 // If this is an extension entry point,
329 // We look explicitly in the extensions list to confirm
330 // that the request is actually supported.
331 // This may be redundant, but is conservative, and only happens once per session.
332 if(extension && strlen(extension)){
333 wxString s_extension(&addr[2], wxConvUTF8);
334 wxString s_family;
335 s_family = wxString(extension, wxConvUTF8);
336 s_extension.Prepend(_T("_"));
337 s_extension.Prepend(s_family);
338
339 s_extension.Prepend(_T("GL_"));
340
341 if(!QueryExtension( s_extension.mb_str() )){
342 return (GenericFunction)NULL;
343 }
344 }
345 #endif
346
347 snprintf(addrbuf, sizeof addrbuf, "%s%s", addr, extension);
348 return (GenericFunction)systemGetProcAddress(addrbuf);
349
350 }
351
352 bool b_glEntryPointsSet;
353
GetglEntryPoints(OCPN_GLCaps * pcaps)354 void GetglEntryPoints( OCPN_GLCaps *pcaps )
355 {
356
357 // the following are all part of framebuffer object,
358 // according to opengl spec, we cannot mix EXT and ARB extensions
359 // (I don't know that it could ever happen, but if it did, bad things would happen)
360
361 #ifndef __OCPN__ANDROID__
362 const char *extensions[] = {"", "ARB", "EXT", 0 };
363 #else
364 const char *extensions[] = {"OES", 0 };
365 #endif
366
367 unsigned int n_ext = (sizeof extensions) / (sizeof *extensions);
368
369 unsigned int i;
370 for(i=0; i<n_ext; i++) {
371 if((pcaps->m_glGenFramebuffers = (PFNGLGENFRAMEBUFFERSEXTPROC)
372 ocpnGetProcAddress( "glGenFramebuffers", extensions[i])))
373 break;
374 }
375
376 if(i<n_ext){
377 pcaps->m_glGenRenderbuffers = (PFNGLGENRENDERBUFFERSEXTPROC)
378 ocpnGetProcAddress( "glGenRenderbuffers", extensions[i]);
379 pcaps->m_glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC)
380 ocpnGetProcAddress( "glFramebufferTexture2D", extensions[i]);
381 pcaps->m_glBindFramebuffer = (PFNGLBINDFRAMEBUFFEREXTPROC)
382 ocpnGetProcAddress( "glBindFramebuffer", extensions[i]);
383 pcaps->m_glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC)
384 ocpnGetProcAddress( "glFramebufferRenderbuffer", extensions[i]);
385 pcaps->m_glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEEXTPROC)
386 ocpnGetProcAddress( "glRenderbufferStorage", extensions[i]);
387 pcaps->m_glBindRenderbuffer = (PFNGLBINDRENDERBUFFEREXTPROC)
388 ocpnGetProcAddress( "glBindRenderbuffer", extensions[i]);
389 pcaps->m_glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC)
390 ocpnGetProcAddress( "glCheckFramebufferStatus", extensions[i]);
391 pcaps->m_glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSEXTPROC)
392 ocpnGetProcAddress( "glDeleteFramebuffers", extensions[i]);
393 pcaps->m_glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSEXTPROC)
394 ocpnGetProcAddress( "glDeleteRenderbuffers", extensions[i]);
395
396 //VBO
397 pcaps->m_glGenBuffers = (PFNGLGENBUFFERSPROC)
398 ocpnGetProcAddress( "glGenBuffers", extensions[i]);
399 pcaps->m_glBindBuffer = (PFNGLBINDBUFFERPROC)
400 ocpnGetProcAddress( "glBindBuffer", extensions[i]);
401 pcaps->m_glBufferData = (PFNGLBUFFERDATAPROC)
402 ocpnGetProcAddress( "glBufferData", extensions[i]);
403 pcaps->m_glDeleteBuffers = (PFNGLDELETEBUFFERSPROC)
404 ocpnGetProcAddress( "glDeleteBuffers", extensions[i]);
405
406
407 }
408
409 // Retry VBO entry points with all extensions
410 if(0 == pcaps->m_glGenBuffers){
411 for( i=0; i<n_ext; i++) {
412 if((pcaps->m_glGenBuffers = (PFNGLGENBUFFERSPROC)ocpnGetProcAddress( "glGenBuffers", extensions[i])) )
413 break;
414 }
415
416 if( i < n_ext ){
417 pcaps->m_glBindBuffer = (PFNGLBINDBUFFERPROC) ocpnGetProcAddress( "glBindBuffer", extensions[i]);
418 pcaps->m_glBufferData = (PFNGLBUFFERDATAPROC) ocpnGetProcAddress( "glBufferData", extensions[i]);
419 pcaps->m_glDeleteBuffers = (PFNGLDELETEBUFFERSPROC) ocpnGetProcAddress( "glDeleteBuffers", extensions[i]);
420 }
421 }
422
423
424 #ifndef __OCPN__ANDROID__
425 for(i=0; i<n_ext; i++) {
426 if((pcaps->m_glCompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DPROC)
427 ocpnGetProcAddress( "glCompressedTexImage2D", extensions[i])))
428 break;
429 }
430
431 if(i<n_ext){
432 pcaps->m_glGetCompressedTexImage = (PFNGLGETCOMPRESSEDTEXIMAGEPROC)
433 ocpnGetProcAddress( "glGetCompressedTexImage", extensions[i]);
434 }
435 #else
436 pcaps->m_glCompressedTexImage2D = glCompressedTexImage2D;
437 #endif
438
439 }
440
441
442
443
444
GetglEntryPoints(void)445 static void GetglEntryPoints( void )
446 {
447 b_glEntryPointsSet = true;
448
449 // the following are all part of framebuffer object,
450 // according to opengl spec, we cannot mix EXT and ARB extensions
451 // (I don't know that it could ever happen, but if it did, bad things would happen)
452
453 #ifndef __OCPN__ANDROID__
454 const char *extensions[] = {"", "ARB", "EXT", 0 };
455 #else
456 const char *extensions[] = {"", "OES", 0 };
457 #endif
458
459 unsigned int n_ext = (sizeof extensions) / (sizeof *extensions);
460
461 unsigned int i;
462 for(i=0; i<n_ext; i++) {
463 if((s_glGenFramebuffers = (PFNGLGENFRAMEBUFFERSEXTPROC)
464 ocpnGetProcAddress( "glGenFramebuffers", extensions[i])))
465 break;
466 }
467
468 if(i<n_ext){
469 s_glGenRenderbuffers = (PFNGLGENRENDERBUFFERSEXTPROC)
470 ocpnGetProcAddress( "glGenRenderbuffers", extensions[i]);
471 s_glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC)
472 ocpnGetProcAddress( "glFramebufferTexture2D", extensions[i]);
473 s_glBindFramebuffer = (PFNGLBINDFRAMEBUFFEREXTPROC)
474 ocpnGetProcAddress( "glBindFramebuffer", extensions[i]);
475 s_glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC)
476 ocpnGetProcAddress( "glFramebufferRenderbuffer", extensions[i]);
477 s_glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEEXTPROC)
478 ocpnGetProcAddress( "glRenderbufferStorage", extensions[i]);
479 s_glBindRenderbuffer = (PFNGLBINDRENDERBUFFEREXTPROC)
480 ocpnGetProcAddress( "glBindRenderbuffer", extensions[i]);
481 s_glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC)
482 ocpnGetProcAddress( "glCheckFramebufferStatus", extensions[i]);
483 s_glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSEXTPROC)
484 ocpnGetProcAddress( "glDeleteFramebuffers", extensions[i]);
485 s_glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSEXTPROC)
486 ocpnGetProcAddress( "glDeleteRenderbuffers", extensions[i]);
487
488 //VBO
489 s_glGenBuffers = (PFNGLGENBUFFERSPROC)
490 ocpnGetProcAddress( "glGenBuffers", extensions[i]);
491 s_glBindBuffer = (PFNGLBINDBUFFERPROC)
492 ocpnGetProcAddress( "glBindBuffer", extensions[i]);
493 s_glBufferData = (PFNGLBUFFERDATAPROC)
494 ocpnGetProcAddress( "glBufferData", extensions[i]);
495 s_glDeleteBuffers = (PFNGLDELETEBUFFERSPROC)
496 ocpnGetProcAddress( "glDeleteBuffers", extensions[i]);
497
498 }
499
500 // Retry VBO entry points with all extensions
501 if(0 == s_glGenBuffers){
502 for( i=0; i<n_ext; i++) {
503 if((s_glGenBuffers = (PFNGLGENBUFFERSPROC)ocpnGetProcAddress( "glGenBuffers", extensions[i])) )
504 break;
505 }
506
507 if( i < n_ext ){
508 s_glBindBuffer = (PFNGLBINDBUFFERPROC) ocpnGetProcAddress( "glBindBuffer", extensions[i]);
509 s_glBufferData = (PFNGLBUFFERDATAPROC) ocpnGetProcAddress( "glBufferData", extensions[i]);
510 s_glDeleteBuffers = (PFNGLDELETEBUFFERSPROC) ocpnGetProcAddress( "glDeleteBuffers", extensions[i]);
511 }
512 }
513
514
515 #ifndef __OCPN__ANDROID__
516 for(i=0; i<n_ext; i++) {
517 if((s_glCompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DPROC)
518 ocpnGetProcAddress( "glCompressedTexImage2D", extensions[i])))
519 break;
520 }
521
522 if(i<n_ext){
523 s_glGetCompressedTexImage = (PFNGLGETCOMPRESSEDTEXIMAGEPROC)
524 ocpnGetProcAddress( "glGetCompressedTexImage", extensions[i]);
525 }
526 #else
527 s_glCompressedTexImage2D = glCompressedTexImage2D;
528 #endif
529
530 }
531
532 int test_attribs[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 16, WX_GL_STENCIL_SIZE, 8, 0 };
533
glTestCanvas(wxWindow * parent)534 glTestCanvas::glTestCanvas( wxWindow *parent ) :
535 wxGLCanvas( parent, wxID_ANY, test_attribs, wxDefaultPosition, wxSize( 2, 2 ) )
536 {
537 }
538
539
540 // This attribute set works OK with vesa software only OpenGL renderer
541 int attribs[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 16, WX_GL_STENCIL_SIZE, 8, 0 };
BEGIN_EVENT_TABLE(glChartCanvas,wxGLCanvas)542 BEGIN_EVENT_TABLE ( glChartCanvas, wxGLCanvas ) EVT_PAINT ( glChartCanvas::OnPaint )
543 EVT_ACTIVATE ( glChartCanvas::OnActivate )
544 EVT_SIZE ( glChartCanvas::OnSize )
545 EVT_MOUSE_EVENTS ( glChartCanvas::MouseEvent )
546 END_EVENT_TABLE()
547
548 glChartCanvas::glChartCanvas( wxWindow *parent, wxGLCanvas *share ) :
549 wxGLCanvas( parent, wxID_ANY, attribs, wxDefaultPosition, wxSize( 256, 256 ),
550 wxFULL_REPAINT_ON_RESIZE | wxBG_STYLE_CUSTOM, _T("") )
551
552 {
553 m_pParentCanvas = dynamic_cast<ChartCanvas *>( parent );
554
555 Init();
556 }
557
558
Init()559 void glChartCanvas::Init()
560 {
561 m_bsetup = false;
562
563 // m_pParentCanvas = dynamic_cast<ChartCanvas *>( GetParent() );
564
565 SetBackgroundStyle ( wxBG_STYLE_CUSTOM ); // on WXMSW, this prevents flashing
566
567 m_cache_current_ch = NULL;
568
569 m_b_paint_enable = true;
570 m_in_glpaint = false;
571
572 m_cache_tex[0] = m_cache_tex[1] = 0;
573 m_cache_page = 0;
574
575 m_b_BuiltFBO = false;
576 m_b_DisableFBO = false;
577
578 ownship_tex = 0;
579 ownship_color = -1;
580
581 m_piano_tex = 0;
582
583 m_binPinch = false;
584 m_binPan = false;
585 m_bpinchGuard = false;
586 m_binGesture = false;
587
588 b_timeGL = true;
589 m_last_render_time = -1;
590
591 m_LRUtime = 0;
592
593 m_tideTex = 0;
594 m_currentTex = 0;
595 m_inFade = false;
596
597 m_gldc.SetGLCanvas( this );
598
599
600 #ifdef __OCPN__ANDROID__
601 // Create/connect a dynamic event handler slot for gesture and some timer events
602 Connect( wxEVT_QT_PANGESTURE,
603 (wxObjectEventFunction) (wxEventFunction) &glChartCanvas::OnEvtPanGesture, NULL, this );
604
605 Connect( wxEVT_QT_PINCHGESTURE,
606 (wxObjectEventFunction) (wxEventFunction) &glChartCanvas::OnEvtPinchGesture, NULL, this );
607
608 Connect( GESTURE_EVENT_TIMER, wxEVT_TIMER,
609 (wxObjectEventFunction) (wxEventFunction) &glChartCanvas::onGestureTimerEvent, NULL, this );
610
611 Connect( GESTURE_FINISH_TIMER, wxEVT_TIMER,
612 (wxObjectEventFunction) (wxEventFunction) &glChartCanvas::onGestureFinishTimerEvent, NULL, this );
613
614 Connect( ZOOM_TIMER, wxEVT_TIMER,
615 (wxObjectEventFunction) (wxEventFunction) &glChartCanvas::onZoomTimerEvent, NULL, this );
616
617
618 m_gestureEeventTimer.SetOwner( this, GESTURE_EVENT_TIMER );
619 m_gestureFinishTimer.SetOwner( this, GESTURE_FINISH_TIMER );
620 zoomTimer.SetOwner( this, ZOOM_TIMER );
621
622 #ifdef USE_ANDROID_GLES2
623 Connect( TEX_FADE_TIMER, wxEVT_TIMER,
624 (wxObjectEventFunction) (wxEventFunction) &glChartCanvas::onFadeTimerEvent, NULL, this );
625 m_fadeTimer.SetOwner( this, TEX_FADE_TIMER );
626 #endif
627
628 m_bgestureGuard = false;
629
630 #endif
631
632 if( !g_glTextureManager)
633 g_glTextureManager = new glTextureManager;
634 }
635
~glChartCanvas()636 glChartCanvas::~glChartCanvas()
637 {
638 #ifdef __OCPN__ANDROID__
639 unloadShaders();
640 #endif
641 }
642
FlushFBO(void)643 void glChartCanvas::FlushFBO( void )
644 {
645 if(m_bsetup)
646 BuildFBO();
647 }
648
649
OnActivate(wxActivateEvent & event)650 void glChartCanvas::OnActivate( wxActivateEvent& event )
651 {
652 m_pParentCanvas->OnActivate( event );
653 }
654
OnSize(wxSizeEvent & event)655 void glChartCanvas::OnSize( wxSizeEvent& event )
656 {
657 #if 0
658 #ifdef __OCPN__ANDROID__
659 if(!g_running){
660 wxLogMessage(_T("Got OnSize event while NOT running"));
661 event.Skip();
662 qDebug() << "OnSizeB";
663
664 return;
665 }
666 #endif
667 #endif
668
669 if(!IsShown())
670 return;
671
672 SetCurrent(*m_pcontext);
673
674 if( !g_bopengl ) {
675 SetSize( GetSize().x, GetSize().y );
676 event.Skip();
677 return;
678 }
679
680
681 // this is also necessary to update the context on some platforms
682 // OnSize can be called with a different OpenGL context (when a plugin uses a different GL context).
683 if( m_bsetup && m_pcontext && IsShown()) {
684 SetCurrent(*m_pcontext);
685 }
686
687 /* expand opengl widget to fill viewport */
688 //if( GetSize() != m_pParentCanvas->GetSize() )
689 {
690
691 SetSize( m_pParentCanvas->GetSize() );
692 if( m_bsetup ){
693 wxLogMessage(_T("BuildFBO 3"));
694
695 BuildFBO();
696 }
697 }
698
699 GetClientSize( &m_pParentCanvas->m_canvas_width, &m_pParentCanvas->m_canvas_height );
700
701 #ifdef USE_ANDROID_GLES2
702 qDebug() << " glChartCanvas::OnSize()" << m_pParentCanvas->m_canvasIndex << m_pParentCanvas->m_canvas_width << m_pParentCanvas->m_canvas_height;
703
704 if(m_pParentCanvas->m_canvasIndex >0){
705 int xnew = gFrame->GetClientSize().x -m_pParentCanvas->m_canvas_width;
706 //qDebug() << "XNEW" << xnew;
707 //SetPosition(wxPoint(xnew, 0));
708 SetSize( xnew, 0, m_pParentCanvas->m_canvas_width, m_pParentCanvas->m_canvas_height);
709 }
710
711 // Set the shader viewport transform matrix
712 mat4x4 m;
713 ViewPort *vp = m_pParentCanvas->GetpVP();
714 mat4x4_identity(m);
715 mat4x4_scale_aniso((float (*)[4])vp->vp_transform, m, 2.0 / (float)vp->pix_width, -2.0 / (float)vp->pix_height, 1.0);
716 mat4x4_translate_in_place((float (*)[4])vp->vp_transform, -vp->pix_width/2, -vp->pix_height/2, 0);
717 #endif
718
719 }
720
MouseEvent(wxMouseEvent & event)721 void glChartCanvas::MouseEvent( wxMouseEvent& event )
722 {
723 if(m_pParentCanvas->MouseEventOverlayWindows( event ))
724 return;
725
726 #ifndef __OCPN__ANDROID__
727 if(m_pParentCanvas->MouseEventSetup( event ))
728 return; // handled, no further action required
729
730 bool obj_proc = m_pParentCanvas->MouseEventProcessObjects( event );
731
732 if(!obj_proc && !m_pParentCanvas->singleClickEventIsValid )
733 m_pParentCanvas->MouseEventProcessCanvas( event );
734
735 if( !g_btouch )
736 m_pParentCanvas->SetCanvasCursor( event );
737
738 #else
739
740 if(m_bgestureGuard){
741 m_pParentCanvas->r_rband.x = 0; // turn off rubberband temporarily
742
743 // Sometimes we get a Gesture Pan start on a simple tap operation.
744 // When this happens, we usually get no Gesture Finished event.
745 // So, we need to process the next LeftUp event normally, to handle things like Measure and Route Create.
746
747 // Allow LeftUp() event through if the pan action is very small
748 // Otherwise, drop the LeftUp() event, since it is not wanted for a Pan Gesture.
749 if(event.LeftUp()){
750 //qDebug() << panx << pany;
751 if((abs(panx) > 2) || (abs(pany) > 2)){
752 return;
753 }
754 else{ // Cancel the in=process Gesture state
755 m_gestureEeventTimer.Start(10, wxTIMER_ONE_SHOT); // Short Circuit
756 }
757 }
758 else
759 return;
760 }
761
762
763 if(m_pParentCanvas->MouseEventSetup( event, false )) {
764 if(!event.LeftDClick()){
765 return; // handled, no further action required
766 }
767 }
768
769 if(m_binPan && event.RightDown()){
770 qDebug() << "Skip right on pan";
771 return;
772 }
773 else{
774 bool obj_proc = m_pParentCanvas->MouseEventProcessObjects( event );
775
776 if(!obj_proc && !m_pParentCanvas->singleClickEventIsValid ) {
777 if(!m_bgestureGuard)
778 m_pParentCanvas->MouseEventProcessCanvas( event ); // This is where a physical mouse gets processed, if detected
779 }
780
781 }
782
783 #endif
784
785 }
786
787 #ifndef GL_MAX_RENDERBUFFER_SIZE
788 #define GL_MAX_RENDERBUFFER_SIZE 0x84E8
789 #endif
790
791 #ifndef USE_ANDROID_GLES2
buildFBOSize(int fboSize)792 bool glChartCanvas::buildFBOSize(int fboSize)
793 {
794 bool retVal = true;
795 if(IsShown())
796 SetCurrent(*m_pcontext);
797
798 if( m_b_BuiltFBO ) {
799 glDeleteTextures( 2, m_cache_tex );
800 ( s_glDeleteFramebuffers )( 1, &m_fb0 );
801 ( s_glDeleteRenderbuffers )( 1, &m_renderbuffer );
802 m_b_BuiltFBO = false;
803 }
804
805 if( m_b_DisableFBO)
806 return false;
807
808 #ifdef __OCPN__ANDROID__
809 // We use the smallest possible (POT) FBO
810 int rb_x = GetSize().x;
811 int rb_y = GetSize().y;
812 int i=1;
813 while(i < rb_x) i <<= 1;
814 rb_x = i;
815
816 i=1;
817 while(i < rb_y) i <<= 1;
818 rb_y = i;
819
820 m_cache_tex_x = wxMax(rb_x, rb_y);
821 m_cache_tex_y = wxMax(rb_x, rb_y);
822
823 #else
824 m_cache_tex_x = GetSize().x;
825 m_cache_tex_y = GetSize().y;
826 #endif
827
828 int err = GL_NO_ERROR;
829 GLint params;
830 glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE,¶ms);
831
832 err = glGetError();
833 if(err == GL_INVALID_ENUM){
834 glGetIntegerv(GL_MAX_TEXTURE_SIZE,¶ms);
835 err = glGetError();
836
837 }
838
839 if(err == GL_NO_ERROR){
840 if( fboSize > params ){
841 wxLogMessage(_T(" OpenGL-> Requested Framebuffer size exceeds GL_MAX_RENDERBUFFER_SIZE") );
842 return false;
843 }
844 }
845
846
847
848
849
850 ( s_glGenFramebuffers )( 1, &m_fb0 );
851 err = glGetError();
852 if(err){
853 wxString msg;
854 msg.Printf( _T(" OpenGL-> Framebuffer GenFramebuffers error: %08X"), err );
855 wxLogMessage(msg);
856 retVal = false;
857 }
858
859 ( s_glGenRenderbuffers )( 1, &m_renderbuffer );
860 err = glGetError();
861 if(err){
862 wxString msg;
863 msg.Printf( _T(" OpenGL-> Framebuffer GenRenderbuffers error: %08X"), err );
864 wxLogMessage(msg);
865 retVal = false;
866 }
867
868
869 ( s_glBindFramebuffer )( GL_FRAMEBUFFER_EXT, m_fb0 );
870 err = glGetError();
871 if(err){
872 wxString msg;
873 msg.Printf( _T(" OpenGL-> Framebuffer BindFramebuffers error: %08X"), err );
874 wxLogMessage(msg);
875 retVal = false;
876 }
877
878 // initialize color textures
879 glGenTextures( 2, m_cache_tex );
880 for(int i=0; i<2; i++) {
881 glBindTexture( g_texture_rectangle_format, m_cache_tex[i] );
882 glTexParameterf( g_texture_rectangle_format, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
883 glTexParameteri( g_texture_rectangle_format, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
884 glTexImage2D( g_texture_rectangle_format, 0, GL_RGBA, m_cache_tex_x, m_cache_tex_y, 0, GL_RGBA,
885 GL_UNSIGNED_BYTE, NULL );
886
887 }
888
889 ( s_glBindRenderbuffer )( GL_RENDERBUFFER_EXT, m_renderbuffer );
890
891 if( m_b_useFBOStencil ) {
892 // initialize composite depth/stencil renderbuffer
893 #if 1
894 ( s_glRenderbufferStorage )( GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT,
895 m_cache_tex_x, m_cache_tex_y );
896
897 int err = glGetError();
898 if(err){
899 wxString msg;
900 msg.Printf( _T(" OpenGL-> glRenderbufferStorage error: %08X"), err );
901 wxLogMessage(msg);
902 }
903
904 ( s_glFramebufferRenderbuffer )( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
905 GL_RENDERBUFFER_EXT, m_renderbuffer );
906 err = glGetError();
907 if(err){
908 wxString msg;
909 msg.Printf( _T(" OpenGL-> glFramebufferRenderbuffer depth error: %08X"), err );
910 wxLogMessage(msg);
911 }
912
913
914 ( s_glFramebufferRenderbuffer )( GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT,
915 GL_RENDERBUFFER_EXT, m_renderbuffer );
916 err = glGetError();
917 if(err){
918 wxString msg;
919 msg.Printf( _T(" OpenGL-> glFramebufferRenderbuffer stencil error: %08X"), err );
920 wxLogMessage(msg);
921 }
922
923 #else
924 ( s_glRenderbufferStorage )( GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES,
925 m_cache_tex_x, m_cache_tex_y );
926
927 ( s_glFramebufferRenderbuffer )( GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
928 GL_RENDERBUFFER, m_renderbuffer );
929
930 ( s_glFramebufferRenderbuffer )( GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
931 GL_RENDERBUFFER, m_renderbuffer );
932 #endif
933
934 } else {
935
936 GLenum depth_format = GL_DEPTH_COMPONENT24;
937
938 // Need to check for availability of 24 bit depth buffer extension on GLES
939 #ifdef ocpnUSE_GLES
940 if( !QueryExtension("GL_OES_depth24") )
941 depth_format = GL_DEPTH_COMPONENT16;
942 #endif
943
944 // initialize depth renderbuffer
945 ( s_glRenderbufferStorage )( GL_RENDERBUFFER_EXT, depth_format,
946 m_cache_tex_x, m_cache_tex_y );
947 int err = glGetError();
948 if(err){
949 wxString msg;
950 msg.Printf( _T(" OpenGL-> Framebuffer Depth Buffer Storage error: %08X"), err );
951 wxLogMessage(msg);
952 retVal = false;
953 }
954
955 ( s_glFramebufferRenderbuffer )( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
956 GL_RENDERBUFFER_EXT, m_renderbuffer );
957
958 err = glGetError();
959 if(err){
960 wxString msg;
961 msg.Printf( _T(" OpenGL-> Framebuffer Depth Buffer Attach error: %08X"), err );
962 wxLogMessage(msg);
963 retVal = false;
964 }
965 }
966
967 glBindTexture(GL_TEXTURE_2D,0);
968 ( s_glBindFramebuffer )( GL_FRAMEBUFFER_EXT, 0 );
969
970 // Check framebuffer completeness at the end of initialization.
971 ( s_glBindFramebuffer )( GL_FRAMEBUFFER_EXT, m_fb0 );
972
973 ( s_glFramebufferTexture2D )
974 ( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
975 g_texture_rectangle_format, m_cache_tex[0], 0 );
976
977 GLenum fb_status = ( s_glCheckFramebufferStatus )( GL_FRAMEBUFFER_EXT );
978
979
980 ( s_glBindFramebuffer )( GL_FRAMEBUFFER_EXT, 0 );
981
982 if( fb_status != GL_FRAMEBUFFER_COMPLETE_EXT ) {
983 wxString msg;
984 msg.Printf( _T(" OpenGL-> buildFBOSize->Framebuffer Incomplete: %08X"), fb_status );
985 wxLogMessage( msg );
986 retVal = false;
987 }
988
989 return retVal;
990 }
991 #endif
992
993 #ifdef USE_ANDROID_GLES2
buildFBOSize(int fboSize)994 bool glChartCanvas::buildFBOSize(int fboSize)
995 {
996 bool retVal = true;
997
998
999 // We use the smallest possible (POT) FBO
1000 int rb_x = GetSize().x;
1001 int rb_y = GetSize().y;
1002 int i=1;
1003 while(i < rb_x) i <<= 1;
1004 rb_x = i;
1005
1006 i=1;
1007 while(i < rb_y) i <<= 1;
1008 rb_y = i;
1009
1010 m_cache_tex_x = wxMax(rb_x, rb_y);
1011 m_cache_tex_y = wxMax(rb_x, rb_y);
1012
1013 //qDebug() << "FBO Size: " << GetSize().x << GetSize().y << m_cache_tex_x;
1014
1015
1016 int err = GL_NO_ERROR;
1017 GLint params;
1018 glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE,¶ms);
1019
1020 err = glGetError();
1021 if(err == GL_INVALID_ENUM){
1022 glGetIntegerv(GL_MAX_TEXTURE_SIZE,¶ms);
1023 err = glGetError();
1024
1025 }
1026
1027 if(err == GL_NO_ERROR){
1028 if( fboSize > params ){
1029 wxLogMessage(_T(" OpenGL-> Requested Framebuffer size exceeds GL_MAX_RENDERBUFFER_SIZE") );
1030 return false;
1031 }
1032 }
1033
1034
1035
1036
1037
1038 glGenFramebuffers ( 1, &m_fb0 );
1039 err = glGetError();
1040 if(err){
1041 wxString msg;
1042 msg.Printf( _T(" OpenGL-> Framebuffer GenFramebuffers error: %08X"), err );
1043 wxLogMessage(msg);
1044 retVal = false;
1045 }
1046
1047 glGenRenderbuffers( 1, &m_renderbuffer );
1048 err = glGetError();
1049 if(err){
1050 wxString msg;
1051 msg.Printf( _T(" OpenGL-> Framebuffer GenRenderbuffers error: %08X"), err );
1052 wxLogMessage(msg);
1053 retVal = false;
1054 }
1055
1056
1057 glBindFramebuffer( GL_FRAMEBUFFER, m_fb0 );
1058 err = glGetError();
1059 if(err){
1060 wxString msg;
1061 msg.Printf( _T(" OpenGL-> Framebuffer BindFramebuffers error: %08X"), err );
1062 wxLogMessage(msg);
1063 retVal = false;
1064 }
1065
1066 // initialize color textures
1067 glGenTextures( 2, m_cache_tex );
1068 for(int i=0; i<2; i++) {
1069 glBindTexture( g_texture_rectangle_format, m_cache_tex[i] );
1070 glTexParameterf( g_texture_rectangle_format, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
1071 glTexParameteri( g_texture_rectangle_format, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
1072 glTexImage2D( g_texture_rectangle_format, 0, GL_RGBA, m_cache_tex_x, m_cache_tex_y, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL );
1073
1074 }
1075
1076 glBindRenderbuffer( GL_RENDERBUFFER, m_renderbuffer );
1077
1078 // initialize composite depth/stencil renderbuffer
1079 glRenderbufferStorage( GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, m_cache_tex_x, m_cache_tex_y );
1080
1081 err = glGetError();
1082 if(err){
1083 wxString msg;
1084 msg.Printf( _T(" OpenGL-> glRenderbufferStorage error: %08X"), err );
1085 wxLogMessage(msg);
1086 }
1087
1088 glFramebufferRenderbuffer( GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_renderbuffer );
1089 err = glGetError();
1090 if(err){
1091 wxString msg;
1092 msg.Printf( _T(" OpenGL-> glFramebufferRenderbuffer depth error: %08X"), err );
1093 wxLogMessage(msg);
1094 }
1095
1096
1097 glFramebufferRenderbuffer( GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_renderbuffer );
1098 err = glGetError();
1099 if(err){
1100 wxString msg;
1101 msg.Printf( _T(" OpenGL-> glFramebufferRenderbuffer stencil error: %08X"), err );
1102 wxLogMessage(msg);
1103 }
1104
1105
1106 glBindTexture(GL_TEXTURE_2D,0);
1107 glBindFramebuffer( GL_FRAMEBUFFER, 0 );
1108
1109 // Check framebuffer completeness at the end of initialization.
1110 glBindFramebuffer( GL_FRAMEBUFFER, m_fb0 );
1111
1112 glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, g_texture_rectangle_format, m_cache_tex[0], 0 );
1113
1114 GLenum fb_status = glCheckFramebufferStatus( GL_FRAMEBUFFER_EXT );
1115
1116
1117 glBindFramebuffer( GL_FRAMEBUFFER, 0 );
1118
1119 if( fb_status != GL_FRAMEBUFFER_COMPLETE) {
1120 wxString msg;
1121 msg.Printf( _T(" OpenGL-> buildFBOSize->Framebuffer Incomplete: %08X %08X"), fb_status );
1122 wxLogMessage( msg );
1123 retVal = false;
1124 }
1125
1126 return retVal;
1127 }
1128 #endif
1129
BuildFBO()1130 void glChartCanvas::BuildFBO( )
1131 {
1132
1133 if( m_b_BuiltFBO ) {
1134 //return;
1135 glDeleteTextures( 2, m_cache_tex );
1136 glDeleteFramebuffers( 1, &m_fb0 );
1137 glDeleteRenderbuffers( 1, &m_renderbuffer );
1138 m_b_BuiltFBO = false;
1139 }
1140
1141 if( m_b_DisableFBO)
1142 return;
1143
1144
1145 int initialSize = 2048;
1146
1147 #ifdef __OCPN__ANDROID__
1148 // Some low mem-spec devices have trouble with 2048 FBO size.
1149 // Detect here, and choose 1024 size instead
1150 wxString info = androidGetDeviceInfo();
1151
1152 if(wxNOT_FOUND != info.Find(_T("GT-S6312")) )
1153 initialSize = 1024;
1154 #endif
1155
1156 if(!buildFBOSize(initialSize)){
1157
1158 glDeleteTextures( 2, m_cache_tex );
1159 glDeleteFramebuffers( 1, &m_fb0 );
1160 glDeleteRenderbuffers( 1, &m_renderbuffer );
1161
1162 if(!buildFBOSize(1024)){
1163 wxLogMessage(_T("BuildFBO C"));
1164
1165 m_b_DisableFBO = true;
1166 wxLogMessage( _T("OpenGL-> FBO Framebuffer unavailable") );
1167 m_b_BuiltFBO = false;
1168
1169 return;
1170 }
1171 }
1172
1173 // All OK
1174
1175 wxString msg;
1176 msg.Printf( _T("OpenGL Framebuffer OK, size = %d"), m_cache_tex_x );
1177 wxLogMessage(msg);
1178
1179 /* invalidate cache */
1180 Invalidate();
1181
1182 glClear( GL_COLOR_BUFFER_BIT );
1183 m_b_BuiltFBO = true;
1184
1185 return;
1186
1187 }
1188
1189
SetupOpenGL()1190 void glChartCanvas::SetupOpenGL()
1191 {
1192
1193 SetCurrent(*m_pcontext);
1194
1195 char *str = (char *) glGetString( GL_RENDERER );
1196 if (str == NULL) {
1197 // perhaps we should edit the config and turn off opengl now
1198 wxLogMessage(_T("Failed to initialize OpenGL"));
1199 exit(1);
1200 }
1201
1202 char render_string[80];
1203 strncpy( render_string, str, 79 );
1204 m_renderer = wxString( render_string, wxConvUTF8 );
1205
1206 wxString msg;
1207 if(g_bSoftwareGL)
1208 msg.Printf( _T("OpenGL-> Software OpenGL") );
1209 msg.Printf( _T("OpenGL-> Renderer String: ") );
1210 msg += m_renderer;
1211 wxLogMessage( msg );
1212
1213 if( ps52plib ) ps52plib->SetGLRendererString( m_renderer );
1214
1215 char version_string[80];
1216 strncpy( version_string, (char *) glGetString( GL_VERSION ), 79 );
1217 msg.Printf( _T("OpenGL-> Version reported: "));
1218 m_version = wxString( version_string, wxConvUTF8 );
1219 msg += m_version;
1220 wxLogMessage( msg );
1221
1222 const GLubyte *ext_str = glGetString(GL_EXTENSIONS);
1223 m_extensions = wxString( (const char *)ext_str, wxConvUTF8 );
1224 #ifdef __WXQT__
1225 wxLogMessage( _T("OpenGL extensions available: ") );
1226 wxLogMessage(m_extensions );
1227 #endif
1228
1229 bool b_oldIntel = false;
1230 if( GetRendererString().Upper().Find( _T("INTEL") ) != wxNOT_FOUND ){
1231 if( GetRendererString().Upper().Find( _T("965") ) != wxNOT_FOUND ){
1232 wxLogMessage( _T("OpenGL-> Detected early Intel renderer, disabling some GL features") );
1233 b_oldIntel = true;
1234 }
1235 }
1236
1237 // Set the minimum line width
1238 GLint parms[2];
1239 #ifndef USE_ANDROID_GLES2
1240 glGetIntegerv( GL_SMOOTH_LINE_WIDTH_RANGE, &parms[0] );
1241 #else
1242 glGetIntegerv( GL_ALIASED_LINE_WIDTH_RANGE, &parms[0] );
1243 #endif
1244 g_GLMinSymbolLineWidth = wxMax(parms[0], 1);
1245 g_GLMinCartographicLineWidth = wxMax(parms[0], 1);
1246
1247 // Some GL renderers do a poor job of Anti-aliasing very narrow line widths.
1248 // This is most evident on rendered symbols which have horizontal or vertical line segments
1249 // Detect this case, and adjust the render parameters.
1250
1251 if( m_renderer.Upper().Find( _T("MESA") ) != wxNOT_FOUND ){
1252 GLfloat parf;
1253 glGetFloatv( GL_SMOOTH_LINE_WIDTH_GRANULARITY, &parf );
1254
1255 g_GLMinSymbolLineWidth = wxMax(((float)parms[0] + parf), 1);
1256 }
1257
1258 s_b_useScissorTest = true;
1259 // the radeon x600 driver has buggy scissor test
1260 if( GetRendererString().Find( _T("RADEON X600") ) != wxNOT_FOUND )
1261 s_b_useScissorTest = false;
1262
1263 if( GetRendererString().Find( _T("GeForce") ) != wxNOT_FOUND ) //GeForce GTX 1070
1264 s_b_useScissorTest = false;
1265
1266 // This little hack fixes a problem seen with some Intel 945 graphics chips
1267 // We need to not do anything that requires (some) complicated stencil operations.
1268
1269 bool bad_stencil_code = false;
1270 if( GetRendererString().Find( _T("Intel") ) != wxNOT_FOUND ) {
1271 wxLogMessage( _T("OpenGL-> Detected Intel renderer, disabling stencil buffer") );
1272 bad_stencil_code = true;
1273 }
1274
1275 // And for the lousy Unichrome drivers, too
1276 if( GetRendererString().Find( _T("UniChrome") ) != wxNOT_FOUND )
1277 bad_stencil_code = true;
1278
1279 // And for the lousy Mali drivers, too
1280 if( GetRendererString().Find( _T("Mali") ) != wxNOT_FOUND )
1281 bad_stencil_code = true;
1282
1283 //XP Generic Needs stencil buffer
1284 //W7 Generic Needs stencil buffer
1285 // if( GetRendererString().Find( _T("Generic") ) != wxNOT_FOUND ) {
1286 // wxLogMessage( _T("OpenGL-> Detected Generic renderer, disabling stencil buffer") );
1287 // bad_stencil_code = true;
1288 // }
1289
1290 // Seen with intel processor on VBox Win7
1291 if( GetRendererString().Find( _T("Chromium") ) != wxNOT_FOUND ) {
1292 wxLogMessage( _T("OpenGL-> Detected Chromium renderer, disabling stencil buffer") );
1293 bad_stencil_code = true;
1294 }
1295
1296 // Stencil buffer test
1297 glEnable( GL_STENCIL_TEST );
1298 GLboolean stencil = glIsEnabled( GL_STENCIL_TEST );
1299 int sb;
1300 glGetIntegerv( GL_STENCIL_BITS, &sb );
1301 // printf("Stencil Buffer Available: %d\nStencil bits: %d\n", stencil, sb);
1302 glDisable( GL_STENCIL_TEST );
1303
1304 s_b_useStencil = false;
1305 if( stencil && ( sb == 8 ) )
1306 s_b_useStencil = true;
1307
1308 if( QueryExtension( "GL_ARB_texture_non_power_of_two" ) )
1309 g_texture_rectangle_format = GL_TEXTURE_2D;
1310 else if( QueryExtension( "GL_OES_texture_npot" ) )
1311 g_texture_rectangle_format = GL_TEXTURE_2D;
1312 else if( QueryExtension( "GL_ARB_texture_rectangle" ) )
1313 g_texture_rectangle_format = GL_TEXTURE_RECTANGLE_ARB;
1314 wxLogMessage( wxString::Format(_T("OpenGL-> Texture rectangle format: %x"),
1315 g_texture_rectangle_format));
1316
1317 #ifndef __OCPN__ANDROID__
1318 // We require certain extensions to support FBO rendering
1319 if(!g_texture_rectangle_format)
1320 m_b_DisableFBO = true;
1321
1322 if(!QueryExtension( "GL_EXT_framebuffer_object" ))
1323 m_b_DisableFBO = true;
1324 #endif
1325
1326 #ifdef __OCPN__ANDROID__
1327 g_texture_rectangle_format = GL_TEXTURE_2D;
1328 #endif
1329
1330 GetglEntryPoints();
1331
1332 if( !s_glGenFramebuffers || !s_glGenRenderbuffers || !s_glFramebufferTexture2D ||
1333 !s_glBindFramebuffer || !s_glFramebufferRenderbuffer || !s_glRenderbufferStorage ||
1334 !s_glBindRenderbuffer || !s_glCheckFramebufferStatus || !s_glDeleteFramebuffers ||
1335 !s_glDeleteRenderbuffers )
1336 m_b_DisableFBO = true;
1337
1338 // VBO??
1339
1340 g_b_EnableVBO = true;
1341 if( !s_glBindBuffer || !s_glBufferData || !s_glGenBuffers || !s_glDeleteBuffers )
1342 g_b_EnableVBO = false;
1343
1344 #if defined( __WXMSW__ ) || defined(__WXOSX__)
1345 if(b_oldIntel)
1346 g_b_EnableVBO = false;
1347 #endif
1348
1349 #ifdef __OCPN__ANDROID__
1350 g_b_EnableVBO = false;
1351 #endif
1352
1353 #if defined( __WXMSW__ )
1354 g_b_EnableVBO = false;
1355 wxLogMessage( _T("OpenGL-> DISABLING VBO for Intel test.") );
1356 #endif
1357
1358 if(g_b_EnableVBO)
1359 wxLogMessage( _T("OpenGL-> Using Vertexbuffer Objects") );
1360 else
1361 wxLogMessage( _T("OpenGL-> Vertexbuffer Objects unavailable") );
1362
1363 // #if defined(__WXOSX__)
1364 // wxLogMessage( _T("OpenGL-> DISABLING VBO for Mac/Intel test.") );
1365 // g_b_EnableVBO = false;
1366 // #endif
1367
1368 // Can we use the stencil buffer in a FBO?
1369 #ifdef ocpnUSE_GLES
1370 m_b_useFBOStencil = QueryExtension( "GL_OES_packed_depth_stencil" );
1371 #else
1372 m_b_useFBOStencil = QueryExtension( "GL_EXT_packed_depth_stencil" ) == GL_TRUE;
1373 #endif
1374
1375 #ifndef USE_ANDROID_GLES2
1376 // On Intel Graphics platforms, don't use stencil buffer at all
1377 if( bad_stencil_code)
1378 s_b_useStencil = false;
1379 #endif
1380
1381 g_GLOptions.m_bUseCanvasPanning = false;
1382
1383
1384 // Maybe build FBO(s)
1385
1386 BuildFBO();
1387
1388
1389
1390
1391 #ifndef __OCPN__ANDROID__
1392 /* this test sometimes fails when the fbo still works */
1393 // But we need to be ultra-conservative here, so run all the tests we can think of
1394
1395
1396 // But we cannot even run this test on some platforms
1397 // So we simply have to declare FBO unavailable
1398 #ifdef __WXMSW__
1399 if( GetRendererString().Upper().Find( _T("INTEL") ) != wxNOT_FOUND ) {
1400 if(GetRendererString().Upper().Find( _T("MOBILE") ) != wxNOT_FOUND ){
1401 wxLogMessage( _T("OpenGL-> Detected Windows Intel Mobile renderer, disabling Frame Buffer Objects") );
1402 m_b_DisableFBO = true;
1403 BuildFBO();
1404 }
1405 }
1406 #endif
1407
1408 if( m_b_BuiltFBO ) {
1409 // Check framebuffer completeness at the end of initialization.
1410 ( s_glBindFramebuffer )( GL_FRAMEBUFFER_EXT, m_fb0 );
1411
1412 ( s_glFramebufferTexture2D )
1413 ( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
1414 g_texture_rectangle_format, m_cache_tex[0], 0 );
1415
1416 GLenum fb_status = ( s_glCheckFramebufferStatus )( GL_FRAMEBUFFER_EXT );
1417 ( s_glBindFramebuffer )( GL_FRAMEBUFFER_EXT, 0 );
1418
1419 if( fb_status != GL_FRAMEBUFFER_COMPLETE_EXT ) {
1420 wxString msg;
1421 msg.Printf( _T(" OpenGL-> Framebuffer Incomplete: %08X"), fb_status );
1422 wxLogMessage( msg );
1423 m_b_DisableFBO = true;
1424 BuildFBO();
1425 }
1426 }
1427 #endif
1428
1429 if( m_b_BuiltFBO && !m_b_useFBOStencil )
1430 s_b_useStencil = false;
1431
1432 // If stencil seems to be a problem, force use of depth buffer clipping for Area Patterns
1433 s_b_useStencilAP = s_b_useStencil & !bad_stencil_code;
1434
1435 #ifdef USE_ANDROID_GLES2
1436 s_b_useStencilAP = s_b_useStencil; // required for GLES2 platform
1437 #endif
1438
1439 if( m_b_BuiltFBO ) {
1440 wxLogMessage( _T("OpenGL-> Using Framebuffer Objects") );
1441
1442 if( m_b_useFBOStencil )
1443 wxLogMessage( _T("OpenGL-> Using FBO Stencil buffer") );
1444 else
1445 wxLogMessage( _T("OpenGL-> FBO Stencil buffer unavailable") );
1446 } else
1447 wxLogMessage( _T("OpenGL-> Framebuffer Objects unavailable") );
1448
1449 if( s_b_useStencil ) wxLogMessage( _T("OpenGL-> Using Stencil buffer clipping") );
1450 else
1451 wxLogMessage( _T("OpenGL-> Using Depth buffer clipping") );
1452
1453 if(s_b_useScissorTest && s_b_useStencil)
1454 wxLogMessage( _T("OpenGL-> Using Scissor Clipping") );
1455
1456 /* we upload non-aligned memory */
1457 glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
1458
1459 MipMap_ResolveRoutines();
1460 SetupCompression();
1461
1462 wxString lwmsg;
1463 lwmsg.Printf(_T("OpenGL-> Minimum cartographic line width: %4.1f"), g_GLMinCartographicLineWidth);
1464 wxLogMessage(lwmsg);
1465 lwmsg.Printf(_T("OpenGL-> Minimum symbol line width: %4.1f"), g_GLMinSymbolLineWidth);
1466 wxLogMessage(lwmsg);
1467
1468 m_benableFog = true;
1469 m_benableVScale = true;
1470 #ifdef __OCPN__ANDROID__
1471 m_benableFog = false;
1472 m_benableVScale = false;
1473 #endif
1474
1475 if(!g_bGLexpert)
1476 g_GLOptions.m_bUseAcceleratedPanning = !m_b_DisableFBO && m_b_BuiltFBO;
1477
1478 #ifdef USE_ANDROID_GLES2
1479 g_GLOptions.m_bUseAcceleratedPanning = true;
1480 #endif
1481
1482 if(1) // for now upload all levels
1483 {
1484 int max_level = 0;
1485 int tex_dim = g_GLOptions.m_iTextureDimension;
1486 for(int dim=tex_dim; dim>0; dim/=2)
1487 max_level++;
1488 g_mipmap_max_level = max_level - 1;
1489 }
1490
1491 // Android, even though using GLES, does not require all levels.
1492 #ifdef __OCPN__ANDROID__
1493 g_mipmap_max_level = 4;
1494 #endif
1495
1496
1497 s_b_useFBO = m_b_BuiltFBO;
1498
1499 // Some older Intel GL drivers need a glFinish() call after each full frame render
1500 if(b_oldIntel)
1501 g_b_needFinish = true;
1502
1503 // Inform the S52 PLIB of options determined
1504 if(ps52plib)
1505 ps52plib->SetGLOptions(s_b_useStencil, s_b_useStencilAP, s_b_useScissorTest, s_b_useFBO, g_b_EnableVBO, g_texture_rectangle_format);
1506
1507 m_bsetup = true;
1508
1509 SendJSONConfigMessage();
1510 }
1511
SendJSONConfigMessage()1512 void glChartCanvas::SendJSONConfigMessage()
1513 {
1514 if(g_pi_manager){
1515 wxJSONValue v;
1516 v[_T("setupComplete")] = m_bsetup;
1517 v[_T("useStencil")] = s_b_useStencil;
1518 v[_T("useStencilAP")] = s_b_useStencilAP;
1519 v[_T("useScissorTest")] = s_b_useScissorTest;
1520 v[_T("useFBO")] = s_b_useFBO;
1521 v[_T("useVBO")] = g_b_EnableVBO;
1522 v[_T("TextureRectangleFormat")] = g_texture_rectangle_format;
1523 wxString msg_id( _T("OCPN_OPENGL_CONFIG") );
1524 g_pi_manager->SendJSONMessageToAllPlugins( msg_id, v );
1525 }
1526 }
SetupCompression()1527 void glChartCanvas::SetupCompression()
1528 {
1529 int dim = g_GLOptions.m_iTextureDimension;
1530
1531 #ifdef __WXMSW__
1532 if(!::IsProcessorFeaturePresent( PF_XMMI64_INSTRUCTIONS_AVAILABLE )) {
1533 wxLogMessage( _T("OpenGL-> SSE2 Instruction set not available") );
1534 goto no_compression;
1535 }
1536 #endif
1537
1538 g_uncompressed_tile_size = dim*dim*4; // stored as 32bpp in vram
1539 if(!g_GLOptions.m_bTextureCompression)
1540 goto no_compression;
1541
1542 g_raster_format = GL_RGB;
1543
1544 // On GLES, we prefer OES_ETC1 compression, if available
1545 #ifdef ocpnUSE_GLES
1546 if(QueryExtension("GL_OES_compressed_ETC1_RGB8_texture") && s_glCompressedTexImage2D) {
1547 g_raster_format = GL_ETC1_RGB8_OES;
1548
1549 wxLogMessage( _T("OpenGL-> Using oes etc1 compression") );
1550 }
1551 #endif
1552
1553 if(GL_RGB == g_raster_format) {
1554 /* because s3tc is patented, many foss drivers disable
1555 support by default, however the extension dxt1 allows
1556 us to load this texture type which is enough because we
1557 compress in software using libsquish for superior quality anyway */
1558
1559 if((QueryExtension("GL_EXT_texture_compression_s3tc") ||
1560 QueryExtension("GL_EXT_texture_compression_dxt1")) &&
1561 s_glCompressedTexImage2D) {
1562 /* buggy opensource nvidia driver, renders incorrectly,
1563 workaround is to use format with alpha... */
1564 if(GetRendererString().Find( _T("Gallium") ) != wxNOT_FOUND &&
1565 GetRendererString().Find( _T("NV") ) != wxNOT_FOUND )
1566 g_raster_format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
1567 else
1568 g_raster_format = GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
1569
1570 wxLogMessage( _T("OpenGL-> Using s3tc dxt1 compression") );
1571 } else if(QueryExtension("GL_3DFX_texture_compression_FXT1") &&
1572 s_glCompressedTexImage2D && s_glGetCompressedTexImage) {
1573 g_raster_format = GL_COMPRESSED_RGB_FXT1_3DFX;
1574
1575 wxLogMessage( _T("OpenGL-> Using 3dfx fxt1 compression") );
1576 } else {
1577 wxLogMessage( _T("OpenGL-> No Useable compression format found") );
1578 goto no_compression;
1579 }
1580 }
1581
1582 #ifdef ocpnUSE_GLES /* gles doesn't have GetTexLevelParameter */
1583 g_tile_size = 512*512/2; /* 4bpp */
1584 #else
1585 /* determine compressed size of a level 0 single tile */
1586 GLuint texture;
1587 glGenTextures( 1, &texture );
1588 glBindTexture( GL_TEXTURE_2D, texture );
1589 glTexImage2D( GL_TEXTURE_2D, 0, g_raster_format, dim, dim,
1590 0, GL_RGB, GL_UNSIGNED_BYTE, NULL );
1591 glGetTexLevelParameteriv(GL_TEXTURE_2D, 0,
1592 GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &g_tile_size);
1593 glDeleteTextures(1, &texture);
1594 #endif
1595
1596 /* disable texture compression if the tile size is 0 */
1597 if(g_tile_size == 0)
1598 goto no_compression;
1599
1600 wxLogMessage( wxString::Format( _T("OpenGL-> Compressed tile size: %dkb (%d:1)"),
1601 g_tile_size / 1024,
1602 g_uncompressed_tile_size / g_tile_size));
1603 return;
1604
1605 no_compression:
1606 g_GLOptions.m_bTextureCompression = false;
1607
1608 g_tile_size = g_uncompressed_tile_size;
1609 wxLogMessage( wxString::Format( _T("OpenGL-> Not Using compression")));
1610 }
1611
OnPaint(wxPaintEvent & event)1612 void glChartCanvas::OnPaint( wxPaintEvent &event )
1613 {
1614 wxPaintDC dc( this );
1615
1616 if(!m_pcontext)
1617 return;
1618
1619 Show( g_bopengl );
1620 if( !g_bopengl ) {
1621 event.Skip();
1622 return;
1623 }
1624
1625 SetCurrent(*m_pcontext);
1626
1627 if( !m_bsetup ) {
1628 SetupOpenGL();
1629
1630 if( ps52plib )
1631 ps52plib->FlushSymbolCaches();
1632
1633 m_bsetup = true;
1634 // g_bDebugOGL = true;
1635 }
1636
1637 // Paint updates may have been externally disabled (temporarily, to avoid Yield() recursion performance loss)
1638 if(!m_b_paint_enable)
1639 return;
1640 // Recursion test, sometimes seen on GTK systems when wxBusyCursor is activated
1641 if( m_in_glpaint ) return;
1642
1643 // If necessary, reconfigure the S52 PLIB
1644 m_pParentCanvas->UpdateCanvasS52PLIBConfig();
1645
1646 // if( m_pParentCanvas->VPoint.b_quilt ){ // quilted
1647 // if( !m_pParentCanvas->m_pQuilt || !m_pParentCanvas->m_pQuilt->IsComposed() )
1648 // return; // not ready
1649 //
1650 // if(m_pParentCanvas->m_pQuilt->IsQuiltVector()){
1651 // if(ps52plib->GetStateHash() != m_pParentCanvas->m_s52StateHash){
1652 // m_pParentCanvas->UpdateS52State();
1653 // m_pParentCanvas->m_s52StateHash = ps52plib->GetStateHash();
1654 // }
1655 // }
1656 // }
1657
1658 m_in_glpaint++;
1659 Render();
1660 m_in_glpaint--;
1661
1662 }
1663
1664
1665 // These routines allow reusable coordinates
HasNormalizedViewPort(const ViewPort & vp)1666 bool glChartCanvas::HasNormalizedViewPort(const ViewPort &vp)
1667 {
1668 #ifndef USE_ANDROID_GLES2
1669 return vp.m_projection_type == PROJECTION_MERCATOR ||
1670 vp.m_projection_type == PROJECTION_POLAR ||
1671 vp.m_projection_type == PROJECTION_EQUIRECTANGULAR;
1672 #else
1673 return false;
1674 #endif
1675 }
1676
1677 /* adjust the opengl transformation matrix so that
1678 points plotted using the identity viewport are correct.
1679 and all rotation translation and scaling is now done in opengl
1680
1681 a central lat and lon of 0, 0 can be used, however objects on the far side of the world
1682 can be up to 3 meters off because limited floating point precision, and if the
1683 points cross 180 longitude then two passes will be required to render them correctly */
1684 #define NORM_FACTOR 4096.0
MultMatrixViewPort(ViewPort & vp,float lat,float lon)1685 void glChartCanvas::MultMatrixViewPort(ViewPort &vp, float lat, float lon)
1686 {
1687 #ifndef USE_ANDROID_GLES2
1688
1689 wxPoint2DDouble point;
1690
1691 switch(vp.m_projection_type) {
1692 case PROJECTION_MERCATOR:
1693 case PROJECTION_EQUIRECTANGULAR:
1694 case PROJECTION_WEB_MERCATOR:
1695 //m_pParentCanvas->GetDoubleCanvasPointPixVP(vp, lat, lon, &point);
1696 point = vp.GetDoublePixFromLL(lat, lon);
1697 glTranslated(point.m_x, point.m_y, 0);
1698 glScaled(vp.view_scale_ppm/NORM_FACTOR, vp.view_scale_ppm/NORM_FACTOR, 1);
1699 break;
1700
1701 case PROJECTION_POLAR:
1702 //m_pParentCanvas->GetDoubleCanvasPointPixVP(vp, vp.clat > 0 ? 90 : -90, vp.clon, &point);
1703 point = vp.GetDoublePixFromLL(vp.clat > 0 ? 90 : -90, vp.clon);
1704 glTranslated(point.m_x, point.m_y, 0);
1705 glRotatef(vp.clon - lon, 0, 0, vp.clat);
1706 glScalef(vp.view_scale_ppm/NORM_FACTOR, vp.view_scale_ppm/NORM_FACTOR, 1);
1707 glTranslatef(-vp.pix_width/2, -vp.pix_height/2, 0);
1708 break;
1709
1710 default:
1711 printf("ERROR: Unhandled projection\n");
1712 }
1713
1714 double rotation = vp.rotation;
1715
1716 if (rotation)
1717 glRotatef(rotation*180/PI, 0, 0, 1);
1718 #endif
1719 }
1720
NormalizedViewPort(const ViewPort & vp,float lat,float lon)1721 ViewPort glChartCanvas::NormalizedViewPort(const ViewPort &vp, float lat, float lon)
1722 {
1723 ViewPort cvp = vp;
1724
1725 switch(vp.m_projection_type) {
1726 case PROJECTION_MERCATOR:
1727 case PROJECTION_EQUIRECTANGULAR:
1728 case PROJECTION_WEB_MERCATOR:
1729 cvp.clat = lat;
1730 break;
1731
1732 case PROJECTION_POLAR:
1733 cvp.clat = vp.clat > 0 ? 90 : -90; // either north or south polar
1734 break;
1735
1736 default:
1737 printf("ERROR: Unhandled projection\n");
1738 }
1739
1740 cvp.clon = lon;
1741 cvp.view_scale_ppm = NORM_FACTOR;
1742 cvp.rotation = cvp.skew = 0;
1743 return cvp;
1744 }
1745
CanClipViewport(const ViewPort & vp)1746 bool glChartCanvas::CanClipViewport(const ViewPort &vp)
1747 {
1748 return vp.m_projection_type == PROJECTION_MERCATOR || vp.m_projection_type == PROJECTION_WEB_MERCATOR ||
1749 vp.m_projection_type == PROJECTION_EQUIRECTANGULAR;
1750 }
1751
ClippedViewport(const ViewPort & vp,const LLRegion & region)1752 ViewPort glChartCanvas::ClippedViewport(const ViewPort &vp, const LLRegion ®ion)
1753 {
1754 if(!CanClipViewport(vp))
1755 return vp;
1756
1757 ViewPort cvp = vp;
1758 LLBBox bbox = region.GetBox();
1759
1760 if(!bbox.GetValid())
1761 return vp;
1762
1763 /* region.GetBox() will always try to give coordinates from -180 to 180 but in
1764 the case where the viewport crosses the IDL, we actually want the clipped viewport
1765 to use coordinates outside this range to ensure the logic in the various rendering
1766 routines works the same here (with accelerated panning) as it does without, so we
1767 can adjust the coordinates here */
1768
1769 if(bbox.GetMaxLon() < cvp.GetBBox().GetMinLon()) {
1770 bbox.Set(bbox.GetMinLat(), bbox.GetMinLon() + 360,
1771 bbox.GetMaxLat(), bbox.GetMaxLon() + 360);
1772 cvp.SetBBoxDirect(bbox);
1773 } else if(bbox.GetMinLon() > cvp.GetBBox().GetMaxLon()) {
1774 bbox.Set(bbox.GetMinLat(), bbox.GetMinLon() - 360,
1775 bbox.GetMaxLat(), bbox.GetMaxLon() - 360);
1776 cvp.SetBBoxDirect(bbox);
1777 } else
1778 cvp.SetBBoxDirect(bbox);
1779
1780 return cvp;
1781 }
1782
1783
DrawStaticRoutesTracksAndWaypoints(ViewPort & vp)1784 void glChartCanvas::DrawStaticRoutesTracksAndWaypoints( ViewPort &vp )
1785 {
1786 if(!m_pParentCanvas->m_bShowNavobjects)
1787 return;
1788 ocpnDC dc(*this);
1789
1790 for(wxTrackListNode *node = pTrackList->GetFirst();
1791 node; node = node->GetNext() ) {
1792 Track *pTrackDraw = node->GetData();
1793 /* defer rendering active tracks until later */
1794 ActiveTrack *pActiveTrack = dynamic_cast<ActiveTrack *>(pTrackDraw);
1795 if(pActiveTrack && pActiveTrack->IsRunning() )
1796 continue;
1797
1798 pTrackDraw->Draw( m_pParentCanvas, dc, vp, vp.GetBBox() );
1799 }
1800
1801 for(wxRouteListNode *node = pRouteList->GetFirst();
1802 node; node = node->GetNext() ) {
1803 Route *pRouteDraw = node->GetData();
1804
1805 if( !pRouteDraw )
1806 continue;
1807
1808 /* defer rendering active routes until later */
1809 if( pRouteDraw->IsActive() || pRouteDraw->IsSelected() )
1810 continue;
1811
1812 /* defer rendering routes being edited until later */
1813 if( pRouteDraw->m_bIsBeingEdited )
1814 continue;
1815
1816 pRouteDraw->DrawGL( vp, m_pParentCanvas );
1817 }
1818
1819 /* Waypoints not drawn as part of routes, and not being edited */
1820 if( vp.GetBBox().GetValid() && pWayPointMan) {
1821 for(wxRoutePointListNode *pnode = pWayPointMan->GetWaypointList()->GetFirst(); pnode; pnode = pnode->GetNext() ) {
1822 RoutePoint *pWP = pnode->GetData();
1823 if( pWP && (!pWP->m_bRPIsBeingEdited) &&(!pWP->m_bIsInRoute ) )
1824 if(vp.GetBBox().ContainsMarge(pWP->m_lat, pWP->m_lon, .5))
1825 pWP->DrawGL( vp, m_pParentCanvas );
1826 }
1827 }
1828 }
1829
DrawDynamicRoutesTracksAndWaypoints(ViewPort & vp)1830 void glChartCanvas::DrawDynamicRoutesTracksAndWaypoints( ViewPort &vp )
1831 {
1832 ocpnDC dc(*this);
1833
1834 for(wxTrackListNode *node = pTrackList->GetFirst();
1835 node; node = node->GetNext() ) {
1836 Track *pTrackDraw = node->GetData();
1837 ActiveTrack *pActiveTrack = dynamic_cast<ActiveTrack *>(pTrackDraw);
1838 if(pActiveTrack && pActiveTrack->IsRunning() )
1839 pTrackDraw->Draw( m_pParentCanvas, dc, vp, vp.GetBBox() ); // We need Track::Draw() to dynamically render last (ownship) point.
1840 }
1841
1842 for(wxRouteListNode *node = pRouteList->GetFirst(); node; node = node->GetNext() ) {
1843 Route *pRouteDraw = node->GetData();
1844
1845 int drawit = 0;
1846 if( !pRouteDraw )
1847 continue;
1848
1849 /* Active routes */
1850 if( pRouteDraw->IsActive() || pRouteDraw->IsSelected() )
1851 drawit++;
1852
1853 /* Routes being edited */
1854 if( pRouteDraw->m_bIsBeingEdited )
1855 drawit++;
1856
1857 /* Routes Selected */
1858 if( pRouteDraw->IsSelected() )
1859 drawit++;
1860
1861 if(drawit) {
1862 const LLBBox &vp_box = vp.GetBBox(), &test_box = pRouteDraw->GetBBox();
1863 if(!vp_box.IntersectOut(test_box))
1864 pRouteDraw->DrawGL( vp, m_pParentCanvas );
1865 }
1866 }
1867
1868
1869 /* Waypoints not drawn as part of routes, which are being edited right now */
1870 if( vp.GetBBox().GetValid() && pWayPointMan) {
1871
1872 for(wxRoutePointListNode *pnode = pWayPointMan->GetWaypointList()->GetFirst(); pnode; pnode = pnode->GetNext() ) {
1873 RoutePoint *pWP = pnode->GetData();
1874 if( pWP && pWP->m_bRPIsBeingEdited && !pWP->m_bIsInRoute )
1875 pWP->DrawGL( vp, m_pParentCanvas );
1876 }
1877 }
1878
1879 }
1880
GetLatLonCurveDist(const ViewPort & vp,float & lat_dist,float & lon_dist)1881 static void GetLatLonCurveDist(const ViewPort &vp, float &lat_dist, float &lon_dist)
1882 {
1883 // This really could use some more thought, and possibly split at different
1884 // intervals based on chart skew and other parameters to optimize performance
1885 switch(vp.m_projection_type) {
1886 case PROJECTION_TRANSVERSE_MERCATOR:
1887 lat_dist = 4, lon_dist = 1; break;
1888 case PROJECTION_POLYCONIC:
1889 lat_dist = 2, lon_dist = 1; break;
1890 case PROJECTION_ORTHOGRAPHIC:
1891 lat_dist = 2, lon_dist = 2; break;
1892 case PROJECTION_POLAR:
1893 lat_dist = 180, lon_dist = 1; break;
1894 case PROJECTION_STEREOGRAPHIC:
1895 case PROJECTION_GNOMONIC:
1896 lat_dist = 2, lon_dist = 1; break;
1897 case PROJECTION_EQUIRECTANGULAR:
1898 // this is suboptimal because we don't care unless there is
1899 // a change in both lat AND lon (skewed chart)
1900 lat_dist = 2, lon_dist = 360; break;
1901 default:
1902 lat_dist = 180, lon_dist = 360;
1903 }
1904 }
1905
RenderChartOutline(ocpnDC & dc,int dbIndex,ViewPort & vp)1906 void glChartCanvas::RenderChartOutline( ocpnDC &dc, int dbIndex, ViewPort &vp )
1907 {
1908 if( ChartData->GetDBChartType( dbIndex ) == CHART_TYPE_PLUGIN &&
1909 !ChartData->IsChartAvailable( dbIndex ) )
1910 return;
1911
1912 /* quick bounds check */
1913 LLBBox box;
1914 ChartData->GetDBBoundingBox( dbIndex, box );
1915 if(!box.GetValid())
1916 return;
1917
1918
1919 // Don't draw an outline in the case where the chart covers the entire world */
1920 if(box.GetLonRange() == 360)
1921 return;
1922
1923 LLBBox vpbox = vp.GetBBox();
1924
1925 double lon_bias = 0;
1926 // chart is outside of viewport lat/lon bounding box
1927 if( box.IntersectOutGetBias( vp.GetBBox(), lon_bias ) )
1928 return;
1929
1930 float plylat, plylon;
1931
1932 wxColour color;
1933
1934 if( ChartData->GetDBChartType( dbIndex ) == CHART_TYPE_CM93 )
1935 color = GetGlobalColor( _T ( "YELO1" ) );
1936 else if( ChartData->GetDBChartFamily( dbIndex ) == CHART_FAMILY_VECTOR )
1937 color = GetGlobalColor( _T ( "GREEN2" ) );
1938 else
1939 color = GetGlobalColor( _T ( "UINFR" ) );
1940
1941 #ifndef USE_ANDROID_GLES2
1942 if( g_GLOptions.m_GLLineSmoothing )
1943 glEnable( GL_LINE_SMOOTH );
1944
1945 glColor3ub(color.Red(), color.Green(), color.Blue());
1946 glLineWidth( g_GLMinSymbolLineWidth );
1947
1948 float lat_dist, lon_dist;
1949 GetLatLonCurveDist(vp, lat_dist, lon_dist);
1950
1951 // Are there any aux ply entries?
1952 int nAuxPlyEntries = ChartData->GetnAuxPlyEntries( dbIndex ), nPly;
1953 int j=0;
1954 do {
1955 if(nAuxPlyEntries)
1956 nPly = ChartData->GetDBAuxPlyPoint( dbIndex, 0, j, 0, 0 );
1957 else
1958 nPly = ChartData->GetDBPlyPoint( dbIndex, 0, &plylat, &plylon );
1959
1960 bool begin = false, sml_valid = false;
1961 double sml[2];
1962 float lastplylat = 0.0;
1963 float lastplylon = 0.0;
1964 // modulo is undefined for zero (compiler can use a div operation)
1965 int modulo = (nPly == 0)?1:nPly;
1966 for( int i = 0; i < nPly+1; i++ ) {
1967 if(nAuxPlyEntries)
1968 ChartData->GetDBAuxPlyPoint( dbIndex, i % modulo, j, &plylat, &plylon );
1969 else
1970 ChartData->GetDBPlyPoint( dbIndex, i % modulo, &plylat, &plylon );
1971
1972 plylon += lon_bias;
1973
1974 if(lastplylon - plylon > 180)
1975 lastplylon -= 360;
1976 else if(lastplylon - plylon < -180)
1977 lastplylon += 360;
1978
1979 int splits;
1980 if(i==0)
1981 splits = 1;
1982 else {
1983 int lat_splits = floor(fabs(plylat-lastplylat) / lat_dist);
1984 int lon_splits = floor(fabs(plylon-lastplylon) / lon_dist);
1985 splits = wxMax(lat_splits, lon_splits) + 1;
1986 }
1987
1988 double smj[2];
1989 if(splits != 1) {
1990 // must perform border interpolation in mercator space as this is what the charts use
1991 toSM(plylat, plylon, 0, 0, smj+0, smj+1);
1992 if(!sml_valid)
1993 toSM(lastplylat, lastplylon, 0, 0, sml+0, sml+1);
1994 }
1995
1996 for(double c=0; c<splits; c++) {
1997 double lat, lon;
1998 if(c == splits - 1)
1999 lat = plylat, lon = plylon;
2000 else {
2001 double d = (double)(c+1) / splits;
2002 fromSM(d*smj[0] + (1-d)*sml[0], d*smj[1] + (1-d)*sml[1], 0, 0, &lat, &lon);
2003 }
2004
2005 wxPoint2DDouble s;
2006 m_pParentCanvas->GetDoubleCanvasPointPix( lat, lon, &s );
2007 if(!std::isnan(s.m_x)) {
2008 if(!begin) {
2009 begin = true;
2010 glBegin(GL_LINE_STRIP);
2011 }
2012 glVertex2f( s.m_x, s.m_y );
2013 } else if(begin) {
2014 glEnd();
2015 begin = false;
2016 }
2017 }
2018 if((sml_valid = splits != 1))
2019 memcpy(sml, smj, sizeof smj);
2020 lastplylat = plylat, lastplylon = plylon;
2021 }
2022
2023 if(begin)
2024 glEnd();
2025
2026 } while(++j < nAuxPlyEntries ); // There are no aux Ply Point entries
2027
2028 glDisable( GL_LINE_SMOOTH );
2029 // glDisable( GL_BLEND );
2030
2031 #else
2032 double nominal_line_width_pix = wxMax(2.0, floor(m_pParentCanvas->GetPixPerMM() / 4));
2033
2034 if( ChartData->GetDBChartType( dbIndex ) == CHART_TYPE_CM93 )
2035 dc.SetPen( wxPen( GetGlobalColor( _T ( "YELO1" ) ), nominal_line_width_pix, wxPENSTYLE_SOLID ) );
2036
2037 else if( ChartData->GetDBChartFamily( dbIndex ) == CHART_FAMILY_VECTOR )
2038 dc.SetPen( wxPen( GetGlobalColor( _T ( "UINFG" ) ), nominal_line_width_pix, wxPENSTYLE_SOLID ) );
2039
2040 else
2041 dc.SetPen( wxPen( GetGlobalColor( _T ( "UINFR" ) ), nominal_line_width_pix, wxPENSTYLE_SOLID ) );
2042
2043
2044
2045 float plylat1, plylon1;
2046 int pixx, pixy, pixx1, pixy1;
2047
2048 // Are there any aux ply entries?
2049 int nAuxPlyEntries = ChartData->GetnAuxPlyEntries( dbIndex );
2050 if( 0 == nAuxPlyEntries ) // There are no aux Ply Point entries
2051 {
2052 wxPoint r, r1;
2053 std::vector<int> points_vector;
2054
2055 std::vector<float> vec = ChartData->GetReducedPlyPoints(dbIndex);
2056 int nPly = vec.size()/2;
2057
2058 if(nPly == 0)
2059 return;
2060
2061 for( int i = 0; i < nPly; i++ ) {
2062 plylon1 = vec[i *2];
2063 plylat1 = vec[i*2 + 1];
2064
2065 m_pParentCanvas->GetCanvasPointPix( plylat1, plylon1, &r1 );
2066 pixx1 = r1.x;
2067 pixy1 = r1.y;
2068
2069 points_vector.push_back(pixx1);
2070 points_vector.push_back(pixy1);
2071 }
2072
2073 ChartData->GetDBPlyPoint( dbIndex, 0, &plylat1, &plylon1 );
2074 plylon1 += lon_bias;
2075
2076 m_pParentCanvas->GetCanvasPointPix( vec[1], vec[0], &r1 );
2077 pixx1 = r1.x;
2078 pixy1 = r1.y;
2079
2080 points_vector.push_back(pixx1);
2081 points_vector.push_back(pixy1);
2082
2083 if(points_vector.size()){
2084 std::vector <int>::iterator it = points_vector.begin();
2085 dc.DrawLines( points_vector.size() / 2, (wxPoint *)&(*it), 0, 0, true);
2086 }
2087 }
2088
2089 else // Use Aux PlyPoints
2090 {
2091 wxPoint r, r1;
2092
2093 for( int j = 0; j < nAuxPlyEntries; j++ ) {
2094
2095 std::vector<int> points_vector;
2096
2097 std::vector<float> vec = ChartData->GetReducedAuxPlyPoints(dbIndex, j);
2098 int nAuxPly = vec.size()/2;
2099
2100 if(nAuxPly == 0)
2101 continue;
2102
2103 for( int i = 0; i < nAuxPly; i++ ) {
2104 plylon1 = vec[i *2];
2105 plylat1 = vec[i*2 + 1];
2106
2107 m_pParentCanvas->GetCanvasPointPix( plylat1, plylon1, &r1 );
2108 pixx1 = r1.x;
2109 pixy1 = r1.y;
2110
2111 points_vector.push_back(pixx1);
2112 points_vector.push_back(pixy1);
2113 }
2114
2115 m_pParentCanvas->GetCanvasPointPix( vec[1], vec[0], &r1 );
2116 pixx1 = r1.x;
2117 pixy1 = r1.y;
2118
2119 points_vector.push_back(pixx1);
2120 points_vector.push_back(pixy1);
2121
2122 if(points_vector.size()){
2123 std::vector <int>::iterator it = points_vector.begin();
2124 dc.DrawLines( points_vector.size() / 2, (wxPoint *)&(*it), 0, 0, true);
2125 }
2126 }
2127 }
2128
2129 #endif
2130 }
2131
2132 extern void CalcGridSpacing( float WindowDegrees, float& MajorSpacing, float&MinorSpacing );
2133 extern wxString CalcGridText( float latlon, float spacing, bool bPostfix );
GridDraw()2134 void glChartCanvas::GridDraw( )
2135 {
2136
2137 if( !m_pParentCanvas->m_bDisplayGrid ) return;
2138
2139 ViewPort &vp = m_pParentCanvas->GetVP();
2140
2141 if(!vp.IsValid() || !vp.GetBBox().GetValid())
2142 return;
2143
2144 // TODO: make minor grid work all the time
2145 bool minorgrid = fabs( vp.rotation ) < 0.0001 &&
2146 vp.m_projection_type == PROJECTION_MERCATOR;
2147
2148 double nlat, elon, slat, wlon;
2149 float lat, lon;
2150 float gridlatMajor, gridlatMinor, gridlonMajor, gridlonMinor;
2151 wxCoord w, h;
2152
2153 wxColour GridColor = GetGlobalColor( _T ( "SNDG1" ) );
2154
2155 if(!m_gridfont.IsBuilt()){
2156 wxFont *dFont = FontMgr::Get().GetFont( _("ChartTexts"), 0 );
2157 wxFont font = *dFont;
2158 font.SetPointSize(8);
2159 font.SetWeight(wxFONTWEIGHT_NORMAL);
2160
2161 m_gridfont.Build(font);
2162 }
2163
2164 w = vp.pix_width;
2165 h = vp.pix_height;
2166
2167 LLBBox llbbox = vp.GetBBox();
2168 nlat = llbbox.GetMaxLat();
2169 slat = llbbox.GetMinLat();
2170 elon = llbbox.GetMaxLon();
2171 wlon = llbbox.GetMinLon();
2172
2173 // calculate distance between latitude grid lines
2174 CalcGridSpacing( vp.view_scale_ppm, gridlatMajor, gridlatMinor );
2175 CalcGridSpacing( vp.view_scale_ppm, gridlonMajor, gridlonMinor );
2176
2177
2178 // if it is known the grid has straight lines it's a bit faster
2179 bool straight_latitudes =
2180 vp.m_projection_type == PROJECTION_MERCATOR ||
2181 vp.m_projection_type == PROJECTION_WEB_MERCATOR ||
2182 vp.m_projection_type == PROJECTION_EQUIRECTANGULAR;
2183 bool straight_longitudes =
2184 vp.m_projection_type == PROJECTION_MERCATOR ||
2185 vp.m_projection_type == PROJECTION_WEB_MERCATOR ||
2186 vp.m_projection_type == PROJECTION_POLAR ||
2187 vp.m_projection_type == PROJECTION_EQUIRECTANGULAR;
2188
2189 double latmargin;
2190 if(straight_latitudes)
2191 latmargin = 0;
2192 else
2193 latmargin = gridlatMajor / 2; // don't draw on poles
2194
2195 slat = wxMax(slat, -90 + latmargin);
2196 nlat = wxMin(nlat, 90 - latmargin);
2197
2198 float startlat = ceil( slat / gridlatMajor ) * gridlatMajor;
2199 float startlon = ceil( wlon / gridlonMajor ) * gridlonMajor;
2200 float curved_step = wxMin(sqrt(5e-3 / vp.view_scale_ppm), 3);
2201
2202 ocpnDC gldc( *this );
2203 wxPen *pen = wxThePenList->FindOrCreatePen( GridColor, g_GLMinSymbolLineWidth, wxPENSTYLE_SOLID );
2204 gldc.SetPen( *pen );
2205
2206 // Draw Major latitude grid lines and text
2207
2208 // calculate position of first major latitude grid line
2209 float lon_step = elon - wlon;
2210 if(!straight_latitudes)
2211 lon_step /= ceil(lon_step / curved_step);
2212
2213 for(lat = startlat; lat < nlat; lat += gridlatMajor) {
2214 wxPoint2DDouble r, s;
2215 s.m_x = NAN;
2216
2217 for(lon = wlon; lon < elon+lon_step/2; lon += lon_step) {
2218 m_pParentCanvas->GetDoubleCanvasPointPix( lat, lon, &r );
2219 if(!std::isnan(s.m_x) && !std::isnan(r.m_x)) {
2220 gldc.DrawLine( s.m_x, s.m_y, r.m_x, r.m_y, true );
2221 }
2222 s = r;
2223 }
2224 }
2225
2226 if(minorgrid) {
2227 // draw minor latitude grid lines
2228 for(lat = ceil( slat / gridlatMinor ) * gridlatMinor; lat < nlat; lat += gridlatMinor) {
2229
2230 wxPoint r;
2231 m_pParentCanvas->GetCanvasPointPix( lat, ( elon + wlon ) / 2, &r );
2232 gldc.DrawLine( 0, r.y, 10, r.y, true );
2233 gldc.DrawLine( w - 10, r.y, w, r.y, true );
2234
2235 lat = lat + gridlatMinor;
2236 }
2237 }
2238
2239 // draw major longitude grid lines
2240 float lat_step = nlat - slat;
2241 if(!straight_longitudes)
2242 lat_step /= ceil(lat_step / curved_step);
2243
2244 for(lon = startlon; lon < elon; lon += gridlonMajor) {
2245 wxPoint2DDouble r, s;
2246 s.m_x = NAN;
2247 for(lat = slat; lat < nlat+lat_step/2; lat+=lat_step) {
2248 m_pParentCanvas->GetDoubleCanvasPointPix( lat, lon, &r );
2249
2250 if(!std::isnan(s.m_x) && !std::isnan(r.m_x)) {
2251 gldc.DrawLine( s.m_x, s.m_y, r.m_x, r.m_y, true );
2252 }
2253 s = r;
2254 }
2255 }
2256
2257 if(minorgrid) {
2258 // draw minor longitude grid lines
2259 for(lon = ceil( wlon / gridlonMinor ) * gridlonMinor; lon < elon; lon += gridlonMinor) {
2260 wxPoint r;
2261 m_pParentCanvas->GetCanvasPointPix( ( nlat + slat ) / 2, lon, &r );
2262 gldc.DrawLine( r.x, 0, r.x, 10, true );
2263 gldc.DrawLine( r.x, h-10, r.x, h, true );
2264 }
2265 }
2266
2267
2268 // draw text labels
2269 glEnable(GL_TEXTURE_2D);
2270 glEnable( GL_BLEND );
2271 for(lat = startlat; lat < nlat; lat += gridlatMajor) {
2272 if( fabs( lat - wxRound( lat ) ) < 1e-5 )
2273 lat = wxRound( lat );
2274
2275 wxString st = CalcGridText( lat, gridlatMajor, true ); // get text for grid line
2276 int iy;
2277 m_gridfont.GetTextExtent(st, 0, &iy);
2278
2279 if(straight_latitudes) {
2280 wxPoint r, s;
2281 m_pParentCanvas->GetCanvasPointPix( lat, elon, &r );
2282 m_pParentCanvas->GetCanvasPointPix( lat, wlon, &s );
2283
2284 float x = 0, y = -1;
2285 y = (float)(r.y*s.x - s.y*r.x) / (s.x - r.x);
2286 if(y < 0 || y > h) {
2287 y = h - iy;
2288 x = (float)(r.x*s.y - s.x*r.y + (s.x - r.x)*y) / (s.y - r.y);
2289 }
2290
2291 m_gridfont.RenderString(st, x, y);
2292 } else {
2293 // iteratively attempt to find where the latitude line crosses x=0
2294 wxPoint2DDouble r;
2295 double y1, y2, lat1, lon1, lat2, lon2;
2296
2297 y1 = 0, y2 = vp.pix_height;
2298 double error = vp.pix_width, lasterror;
2299 int maxiters = 10;
2300 do {
2301 m_pParentCanvas->GetCanvasPixPoint(0, y1, lat1, lon1);
2302 m_pParentCanvas->GetCanvasPixPoint(0, y2, lat2, lon2);
2303
2304 double y = y1 + (lat1 - lat) * (y2 - y1) / (lat1 - lat2);
2305
2306 m_pParentCanvas->GetDoubleCanvasPointPix( lat, lon1 + (y1 - y) * (lon2 - lon1) / (y1 - y2), &r);
2307
2308 if(fabs(y - y1) < fabs(y - y2))
2309 y1 = y;
2310 else
2311 y2 = y;
2312
2313 lasterror = error;
2314 error = fabs(r.m_x);
2315 if(--maxiters == 0)
2316 break;
2317 } while(error > 1 && error < lasterror);
2318
2319 if(error < 1 && r.m_y >= 0 && r.m_y <= vp.pix_height - iy )
2320 r.m_x = 0;
2321 else
2322 // just draw at center longitude
2323 m_pParentCanvas->GetDoubleCanvasPointPix( lat, vp.clon, &r);
2324
2325 m_gridfont.RenderString(st, r.m_x, r.m_y);
2326 }
2327 }
2328
2329
2330 for(lon = startlon; lon < elon; lon += gridlonMajor) {
2331 if( fabs( lon - wxRound( lon ) ) < 1e-5 )
2332 lon = wxRound( lon );
2333
2334 wxPoint r, s;
2335 m_pParentCanvas->GetCanvasPointPix( nlat, lon, &r );
2336 m_pParentCanvas->GetCanvasPointPix( slat, lon, &s );
2337
2338 float xlon = lon;
2339 if( xlon > 180.0 )
2340 xlon -= 360.0;
2341 else if( xlon <= -180.0 )
2342 xlon += 360.0;
2343
2344 wxString st = CalcGridText( xlon, gridlonMajor, false );
2345 int ix;
2346 m_gridfont.GetTextExtent(st, &ix, 0);
2347
2348 if(straight_longitudes) {
2349 float x = -1, y = 0;
2350 x = (float)(r.x*s.y - s.x*r.y) / (s.y - r.y);
2351 if(x < 0 || x > w) {
2352 x = w - ix;
2353 y = (float)(r.y*s.x - s.y*r.x + (s.y - r.y)*x) / (s.x - r.x);
2354 }
2355
2356 m_gridfont.RenderString(st, x, y);
2357 } else {
2358 // iteratively attempt to find where the latitude line crosses x=0
2359 wxPoint2DDouble r;
2360 double x1, x2, lat1, lon1, lat2, lon2;
2361
2362 x1 = 0, x2 = vp.pix_width;
2363 double error = vp.pix_height, lasterror;
2364 do {
2365 m_pParentCanvas->GetCanvasPixPoint(x1, 0, lat1, lon1);
2366 m_pParentCanvas->GetCanvasPixPoint(x2, 0, lat2, lon2);
2367
2368 double x = x1 + (lon1 - lon) * (x2 - x1) / (lon1 - lon2);
2369
2370 m_pParentCanvas->GetDoubleCanvasPointPix( lat1 + (x1 - x) * (lat2 - lat1) / (x1 - x2), lon, &r);
2371
2372 if(fabs(x - x1) < fabs(x - x2))
2373 x1 = x;
2374 else
2375 x2 = x;
2376
2377 lasterror = error;
2378 error = fabs(r.m_y);
2379 } while(error > 1 && error < lasterror);
2380
2381 if(error < 1 && r.m_x >= 0 && r.m_x <= vp.pix_width - ix)
2382 r.m_y = 0;
2383 else
2384 // failure, instead just draw the text at center latitude
2385 m_pParentCanvas->GetDoubleCanvasPointPix( wxMin(wxMax(vp.clat, slat), nlat), lon, &r);
2386
2387 m_gridfont.RenderString(st, r.m_x, r.m_y);
2388 }
2389 }
2390
2391 glDisable(GL_TEXTURE_2D);
2392
2393 glDisable( GL_BLEND );
2394 }
2395
2396
DrawEmboss(emboss_data * emboss)2397 void glChartCanvas::DrawEmboss( emboss_data *emboss )
2398 {
2399 if( !emboss ) return;
2400
2401 int w = emboss->width, h = emboss->height;
2402
2403 glEnable( GL_TEXTURE_2D );
2404
2405 // render using opengl and alpha blending
2406 if( !emboss->gltexind ) { /* upload to texture */
2407
2408 emboss->glwidth = NextPow2(emboss->width);
2409 emboss->glheight = NextPow2(emboss->height);
2410
2411 /* convert to luminance alpha map */
2412 int size = emboss->glwidth * emboss->glheight;
2413 char *data = new char[2 * size];
2414 for( int i = 0; i < h; i++ ) {
2415 for( int j = 0; j < emboss->glwidth; j++ ) {
2416 if( j < w ) {
2417 data[2 * ( ( i * emboss->glwidth ) + j )] =
2418 (char) (emboss->pmap[( i * w ) + j] > 0 ? 0 : 255);
2419 data[2 * ( ( i * emboss->glwidth ) + j ) + 1] =
2420 (char) abs( (emboss->pmap[( i * w ) + j]) );
2421 }
2422 }
2423 }
2424
2425 glGenTextures( 1, &emboss->gltexind );
2426 glBindTexture( GL_TEXTURE_2D, emboss->gltexind );
2427 glTexImage2D( GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, emboss->glwidth, emboss->glheight, 0,
2428 GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, data );
2429 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
2430 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
2431
2432 delete[] data;
2433 }
2434
2435 glBindTexture( GL_TEXTURE_2D, emboss->gltexind );
2436
2437 glEnable( GL_BLEND );
2438
2439 int x = emboss->x, y = emboss->y;
2440
2441 float wp = (float) w / emboss->glwidth;
2442 float hp = (float) h / emboss->glheight;
2443
2444 #ifndef USE_ANDROID_GLES2
2445
2446 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
2447 const float factor = 200;
2448 glColor4f( 1, 1, 1, factor / 256 );
2449
2450 glBegin( GL_QUADS );
2451 glTexCoord2f( 0, 0 ), glVertex2i( x, y );
2452 glTexCoord2f( wp, 0 ), glVertex2i( x + w, y );
2453 glTexCoord2f( wp, hp ), glVertex2i( x + w, y + h );
2454 glTexCoord2f( 0, hp ), glVertex2i( x, y + h );
2455 glEnd();
2456
2457
2458 #else
2459 float coords[8];
2460 float uv[8];
2461
2462 //normal uv
2463 uv[0] = 0; uv[1] = 0; uv[2] = wp; uv[3] = 0;
2464 uv[4] = wp; uv[5] = hp; uv[6] = 0; uv[7] = hp;
2465
2466 // pixels
2467 coords[0] = 0; coords[1] = 0; coords[2] = w; coords[3] = 0;
2468 coords[4] = w; coords[5] = h; coords[6] = 0; coords[7] = h;
2469
2470 RenderSingleTexture(coords, uv, m_pParentCanvas->GetpVP(), x, y, 0);
2471
2472 #endif
2473
2474 glDisable( GL_BLEND );
2475 glDisable( GL_TEXTURE_2D );
2476 }
2477
ShipDraw(ocpnDC & dc)2478 void glChartCanvas::ShipDraw(ocpnDC& dc)
2479 {
2480 if( !m_pParentCanvas->GetVP().IsValid() ) return;
2481 wxPoint lGPSPoint, lShipMidPoint, GPSOffsetPixels(0,0);
2482
2483 // COG/SOG may be undefined in NMEA data stream
2484 float pCog = std::isnan(gCog) ? 0 : gCog;
2485 float pSog = std::isnan(gSog) ? 0 : gSog;
2486
2487 m_pParentCanvas->GetCanvasPointPix( gLat, gLon, &lGPSPoint );
2488 lShipMidPoint = lGPSPoint;
2489
2490 // Draw the icon rotated to the COG
2491 // or to the Hdt if available
2492 float icon_hdt = pCog;
2493 if( !std::isnan( gHdt ) ) icon_hdt = gHdt;
2494
2495 // COG may be undefined in NMEA data stream
2496 if( std::isnan(icon_hdt) ) icon_hdt = 0.0;
2497
2498 // Calculate the ownship drawing angle icon_rad using an assumed 10 minute predictor
2499 double osd_head_lat, osd_head_lon;
2500 wxPoint osd_head_point;
2501
2502 ll_gc_ll( gLat, gLon, icon_hdt, pSog * 10. / 60., &osd_head_lat, &osd_head_lon );
2503
2504 m_pParentCanvas->GetCanvasPointPix( osd_head_lat, osd_head_lon, &osd_head_point );
2505
2506 float icon_rad = atan2f( (float) ( osd_head_point.y - lShipMidPoint.y ),
2507 (float) ( osd_head_point.x - lShipMidPoint.x ) );
2508 icon_rad += (float)PI;
2509
2510 if( pSog < 0.2 ) icon_rad = ( ( icon_hdt + 90. ) * PI / 180. ) + m_pParentCanvas->GetVP().rotation;
2511
2512 // Another draw test ,based on pixels, assuming the ship icon is a fixed nominal size
2513 // and is just barely outside the viewport ....
2514 wxBoundingBox bb_screen( 0, 0, m_pParentCanvas->GetVP().pix_width, m_pParentCanvas->GetVP().pix_height );
2515
2516 // TODO: fix to include actual size of boat that will be rendered
2517 int img_height = 0;
2518
2519 if( bb_screen.PointInBox( lShipMidPoint, 20 ) ) {
2520 if( g_GLOptions.m_GLLineSmoothing )
2521 glEnable( GL_LINE_SMOOTH );
2522 if( g_GLOptions.m_GLPolygonSmoothing )
2523 glEnable( GL_POLYGON_SMOOTH );
2524
2525 if( m_pParentCanvas->GetVP().chart_scale > 300000 ) // According to S52, this should be 50,000
2526 {
2527 float scale_factor = 1.0;
2528 // Scale the generic icon to ChartScaleFactor, slightly softened....
2529 if((g_ChartScaleFactorExp > 1.0) && ( g_OwnShipIconType == 0 ))
2530 scale_factor = (log(g_ChartScaleFactorExp) + 1.0) * 1.1;
2531
2532 float nominal_ownship_size_mm = m_pParentCanvas->m_display_size_mm / 44.0;
2533 nominal_ownship_size_mm = wxMin(nominal_ownship_size_mm, 15.0);
2534 nominal_ownship_size_mm = wxMax(nominal_ownship_size_mm, 7.0);
2535
2536 float nominal_ownship_size_pixels = wxMax(20.0, m_pParentCanvas->GetPixPerMM() * nominal_ownship_size_mm); // nominal length, but not less than 20 pixel
2537 float v = (nominal_ownship_size_pixels * scale_factor) / 3;
2538
2539 wxPen ppPen1( GetGlobalColor( _T ( "YELO1" ) ), v / 10 , wxPENSTYLE_SOLID );
2540 dc.SetPen( ppPen1 );
2541 dc.SetBrush( wxBrush( GetGlobalColor( _T ( "URED" ) ), wxBRUSHSTYLE_TRANSPARENT ) );
2542
2543 // start with cross
2544 dc.DrawLine( (-v * 1.2) + lShipMidPoint.x, lShipMidPoint.y, (v * 1.2) + lShipMidPoint.x, lShipMidPoint.y);
2545 dc.DrawLine( lShipMidPoint.x, (-v * 1.2) + lShipMidPoint.y, lShipMidPoint.x, (v * 1.2) + lShipMidPoint.y);
2546
2547 // Two circles
2548 dc.StrokeCircle( lShipMidPoint.x, lShipMidPoint.y, v );
2549 dc.StrokeCircle( lShipMidPoint.x, lShipMidPoint.y, 0.6 * v );
2550 img_height = 20;
2551 } else {
2552 int draw_color = SHIP_INVALID;
2553 if( SHIP_NORMAL == m_pParentCanvas->m_ownship_state )
2554 draw_color = SHIP_NORMAL;
2555 else if( SHIP_LOWACCURACY == m_pParentCanvas->m_ownship_state )
2556 draw_color = SHIP_LOWACCURACY;
2557
2558 if(!ownship_tex || (draw_color != ownship_color)) { /* initial run, create texture for ownship,
2559 also needed at colorscheme changes (not implemented) */
2560
2561 ownship_color = draw_color;
2562
2563 if(ownship_tex)
2564 glDeleteTextures(1, &ownship_tex);
2565
2566 glGenTextures( 1, &ownship_tex );
2567 glBindTexture(GL_TEXTURE_2D, ownship_tex);
2568
2569 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
2570 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
2571
2572 wxImage image;
2573 if(m_pParentCanvas->m_pos_image_user) {
2574 switch (draw_color) {
2575 case SHIP_INVALID: image = *m_pParentCanvas->m_pos_image_user_grey; break;
2576 case SHIP_NORMAL: image = *m_pParentCanvas->m_pos_image_user; break;
2577 case SHIP_LOWACCURACY: image = *m_pParentCanvas->m_pos_image_user_yellow; break;
2578 }
2579 }
2580 else {
2581 switch (draw_color) {
2582 case SHIP_INVALID: image = *m_pParentCanvas->m_pos_image_grey; break;
2583 case SHIP_NORMAL: image = *m_pParentCanvas->m_pos_image_red; break;
2584 case SHIP_LOWACCURACY: image = *m_pParentCanvas->m_pos_image_yellow; break;
2585 }
2586 }
2587
2588 int w = image.GetWidth(), h = image.GetHeight();
2589 int glw = NextPow2(w), glh = NextPow2(h);
2590 ownship_size = wxSize(w, h);
2591 ownship_tex_size = wxSize(glw, glh);
2592
2593 unsigned char *d = image.GetData();
2594 unsigned char *a = image.GetAlpha();
2595 unsigned char *e = new unsigned char[4 * w * h];
2596
2597 if(d && e && a){
2598 for( int p = 0; p < w*h; p++ ) {
2599 e[4*p+0] = d[3*p+0];
2600 e[4*p+1] = d[3*p+1];
2601 e[4*p+2] = d[3*p+2];
2602 e[4*p+3] = a[p];
2603 }
2604 }
2605 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
2606 glw, glh, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
2607
2608 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
2609 w, h, GL_RGBA, GL_UNSIGNED_BYTE, e);
2610 delete [] e;
2611 }
2612
2613 /* establish ship color */
2614 #ifndef USE_ANDROID_GLES2
2615 if( m_pParentCanvas->m_pos_image_user )
2616 glColor4ub(255, 255, 255, 255);
2617 else if( SHIP_NORMAL == m_pParentCanvas->m_ownship_state )
2618 glColor4ub(255, 0, 0, 255);
2619 else if( SHIP_LOWACCURACY == m_pParentCanvas->m_ownship_state )
2620 glColor4ub(255, 255, 0, 255);
2621 else
2622 glColor4ub(128, 128, 128, 255);
2623 #endif
2624 float scale_factor_y = 1.0;
2625 float scale_factor_x = 1.0;
2626
2627 int ownShipWidth = 22; // Default values from s_ownship_icon
2628 int ownShipLength= 84;
2629 lShipMidPoint = lGPSPoint;
2630
2631 /* scaled ship? */
2632 if( g_OwnShipIconType != 0 )
2633 m_pParentCanvas->ComputeShipScaleFactor
2634 (icon_hdt, ownShipWidth, ownShipLength, lShipMidPoint,
2635 GPSOffsetPixels, lGPSPoint, scale_factor_x, scale_factor_y);
2636
2637 glEnable(GL_BLEND);
2638
2639 // glEnableClientState(GL_VERTEX_ARRAY);
2640
2641 int x = lShipMidPoint.x, y = lShipMidPoint.y;
2642
2643 // Scale the generic icon to ChartScaleFactor, slightly softened....
2644 if((g_ShipScaleFactorExp > 1.0) && ( g_OwnShipIconType == 0 )){
2645 scale_factor_x = (log(g_ShipScaleFactorExp) + 1.0) * 1.1;
2646 scale_factor_y = (log(g_ShipScaleFactorExp) + 1.0) * 1.1;
2647 }
2648
2649 // Set the size of the little circle showing the GPS reference position
2650 // Set a default early, adjust later based on render type
2651 float gps_circle_radius = 3.0;
2652
2653
2654 if( g_OwnShipIconType == 0 ) { // Default Bitmap
2655
2656 glEnable(GL_TEXTURE_2D);
2657 glBindTexture(GL_TEXTURE_2D, ownship_tex);
2658
2659 //We choose to render the ownship bitmap at roughly the same size( in pixels ) as the DC mode renderer.
2660 // For ultra-high definition displays, we clamp the actual on-screen size to be no smaller than 7.0 mm
2661 // Similarly, for lo-res displays, we limit the actual size to be no larger than 15 mm maximum.
2662
2663 // Get bitmap height in pixels
2664 int image_height_bitmap = m_pParentCanvas->m_pos_image_red->GetHeight();
2665 if(m_pParentCanvas->m_pos_image_user)
2666 image_height_bitmap = m_pParentCanvas->m_pos_image_user->GetHeight();
2667
2668 float nominal_ownship_size_mm = image_height_bitmap / m_pParentCanvas->GetPixPerMM();
2669
2670 nominal_ownship_size_mm = wxMin(nominal_ownship_size_mm, 15.0);
2671 nominal_ownship_size_mm = wxMax(nominal_ownship_size_mm, 7.0);
2672
2673 float nominal_ownship_size_pixels = wxMax(20.0, m_pParentCanvas->GetPixPerMM() * nominal_ownship_size_mm); // nominal length, but not less than 20 pixel
2674 float h = nominal_ownship_size_pixels * scale_factor_y;
2675 float w = nominal_ownship_size_pixels * scale_factor_x * ownship_size.x / ownship_size.y ;
2676 float glw = ownship_tex_size.x, glh = ownship_tex_size.y;
2677 float u = ownship_size.x/glw, v = ownship_size.y/glh;
2678
2679 // printf("%g %g %g %g %g %g %g\n", nominal_ownship_size_mm, nominal_ownship_size_pixels, h, w, u, v, m_pParentCanvas->m_display_size_mm);
2680 // tweak GPS reference point indicator size
2681 gps_circle_radius = w / 5;
2682
2683 float uv[8], coords[8];
2684 uv[0] = 0; uv[1] = 0; uv[2] = u; uv[3] = 0;
2685 uv[4] = u; uv[5] = v; uv[6] = 0; uv[7] = v;
2686
2687 coords[0] = -w/2; coords[1] = -h/2; coords[2] = w/2; coords[3] = -h/2;
2688 coords[4] = w/2; coords[5] = h/2; coords[6] = -w/2; coords[7] = h/2;
2689
2690 RenderSingleTexture(coords, uv, m_pParentCanvas->GetpVP(), x, y, icon_rad - PI/2);
2691
2692 glDisable(GL_TEXTURE_2D);
2693 } else if( g_OwnShipIconType == 1 ) { // Scaled Bitmap
2694
2695 glEnable(GL_TEXTURE_2D);
2696 glBindTexture(GL_TEXTURE_2D, ownship_tex);
2697
2698 float nominal_ownship_size_pixels_y = 84;
2699 float nominal_ownship_size_pixels_x = 22;
2700
2701 float h = nominal_ownship_size_pixels_y * scale_factor_y;
2702 float w = nominal_ownship_size_pixels_x * scale_factor_x;
2703
2704 float u = (float)ownship_size.x/ownship_tex_size.x, v = (float)ownship_size.y/ownship_tex_size.y;
2705
2706 // tweak GPS reference point indicator size
2707 gps_circle_radius = w / 5;
2708
2709 float uv[8], coords[8];
2710 uv[0] = 0; uv[1] = 0; uv[2] = u; uv[3] = 0;
2711 uv[4] = u; uv[5] = v; uv[6] = 0; uv[7] = v;
2712
2713 coords[0] = -w/2; coords[1] = -h/2; coords[2] = w/2; coords[3] = -h/2;
2714 coords[4] = w/2; coords[5] = h/2; coords[6] = -w/2; coords[7] = h/2;
2715
2716 RenderSingleTexture(coords, uv, m_pParentCanvas->GetpVP(), x, y, icon_rad - PI/2);
2717
2718 glDisable(GL_TEXTURE_2D);
2719 } else if( g_OwnShipIconType == 2 ) { // Scaled Vector
2720 // static const GLint s_ownship_icon[] = { 5, -42, 11, -28, 11, 42, -11, 42,
2721 // -11, -28, -5, -42, -11, 0, 11, 0,
2722 // 0, 42, 0, -42 };
2723
2724 wxPoint shipPoints[6];
2725
2726 wxColour colour = m_pParentCanvas->ShipColor();
2727 wxPen ppPen ( *wxBLACK, 1 );
2728 wxBrush ppBrush( colour );
2729 dc.SetPen( ppPen );
2730 dc.SetBrush( ppBrush );
2731
2732 shipPoints[0].x = 0*scale_factor_x; shipPoints[0].y = -28*scale_factor_y;
2733 shipPoints[1].x = 11*scale_factor_x; shipPoints[1].y = -28*scale_factor_y;
2734 shipPoints[2].x = 11*scale_factor_x; shipPoints[2].y = 42*scale_factor_y;
2735 shipPoints[3].x = 0*scale_factor_x; shipPoints[3].y = 42*scale_factor_y;
2736 dc.DrawPolygon( 4, shipPoints, lShipMidPoint.x, lShipMidPoint.y, 1, icon_rad - PI/2 );
2737
2738 shipPoints[0].x = 0*scale_factor_x; shipPoints[0].y = -42*scale_factor_y;
2739 shipPoints[1].x = 5*scale_factor_x; shipPoints[1].y = -42*scale_factor_y;
2740 shipPoints[2].x = 11*scale_factor_x; shipPoints[2].y = -28*scale_factor_y;
2741 shipPoints[3].x = 0*scale_factor_x; shipPoints[3].y = -28*scale_factor_y;
2742 dc.DrawPolygon( 4, shipPoints, lShipMidPoint.x, lShipMidPoint.y, 1, icon_rad - PI/2 );
2743
2744 shipPoints[0].x = 0*scale_factor_x; shipPoints[0].y = -28*scale_factor_y;
2745 shipPoints[1].x = -11*scale_factor_x; shipPoints[1].y = -28*scale_factor_y;
2746 shipPoints[2].x = -11*scale_factor_x; shipPoints[2].y = 42*scale_factor_y;
2747 shipPoints[3].x = 0*scale_factor_x; shipPoints[3].y = 42*scale_factor_y;
2748 dc.DrawPolygon( 4, shipPoints, lShipMidPoint.x, lShipMidPoint.y, 1, icon_rad - PI/2 );
2749
2750 shipPoints[0].x = 0*scale_factor_x; shipPoints[0].y = -42*scale_factor_y;
2751 shipPoints[1].x = -5*scale_factor_x; shipPoints[1].y = -42*scale_factor_y;
2752 shipPoints[2].x = -11*scale_factor_x; shipPoints[2].y = -28*scale_factor_y;
2753 shipPoints[3].x = 0*scale_factor_x; shipPoints[3].y = -28*scale_factor_y;
2754 dc.DrawPolygon( 4, shipPoints, lShipMidPoint.x, lShipMidPoint.y, 1, icon_rad - PI/2 );
2755
2756 // draw with cross
2757 double p1x = -11 * scale_factor_x;
2758 double p2x = 11 * scale_factor_x;
2759 double p1y = 0;
2760 double p2y = 0;
2761 double p1xr = ((p1x) * cos(icon_rad - PI/2)) - ((p1y) * sin(icon_rad - PI/2));
2762 double p2xr = ((p2x) * cos(icon_rad - PI/2)) - ((p2y) * sin(icon_rad - PI/2));
2763 double p1yr = ((p1y) * cos(icon_rad - PI/2)) + ((p1x) * sin(icon_rad - PI/2));
2764 double p2yr = ((p2y) * cos(icon_rad - PI/2)) + ((p2x) * sin(icon_rad - PI/2));
2765 dc.DrawLine(p1xr+ lShipMidPoint.x, p1yr+ lShipMidPoint.y, p2xr+ lShipMidPoint.x, p2yr+ lShipMidPoint.y);
2766
2767 p1x = 0;
2768 p2x = 0;
2769 p1y = -42 * scale_factor_y;
2770 p2y = 42 * scale_factor_y;
2771 p1xr = ((p1x) * cos(icon_rad - PI/2)) - ((p1y) * sin(icon_rad - PI/2));
2772 p2xr = ((p2x) * cos(icon_rad - PI/2)) - ((p2y) * sin(icon_rad - PI/2));
2773 p1yr = ((p1y) * cos(icon_rad - PI/2)) + ((p1x) * sin(icon_rad - PI/2));
2774 p2yr = ((p2y) * cos(icon_rad - PI/2)) + ((p2x) * sin(icon_rad - PI/2));
2775 dc.DrawLine(p1xr+ lShipMidPoint.x, p1yr+ lShipMidPoint.y, p2xr+ lShipMidPoint.x, p2yr+ lShipMidPoint.y);
2776
2777
2778 }
2779
2780 img_height = ownShipLength * scale_factor_y;
2781
2782 // Reference point, where the GPS antenna is
2783 if( m_pParentCanvas->m_pos_image_user ) gps_circle_radius = 1;
2784
2785 float cx = lGPSPoint.x, cy = lGPSPoint.y;
2786 wxPen ppPen1( GetGlobalColor( _T ( "UBLCK" ) ), 1, wxPENSTYLE_SOLID );
2787 dc.SetPen( ppPen1 );
2788 dc.SetBrush( wxBrush( GetGlobalColor( _T ( "CHWHT" ) ) ) );
2789
2790 dc.StrokeCircle(cx, cy, gps_circle_radius);
2791
2792 }
2793
2794 // glDisableClientState(GL_VERTEX_ARRAY);
2795 glDisable( GL_LINE_SMOOTH );
2796 glDisable( GL_POLYGON_SMOOTH );
2797 glDisable(GL_BLEND);
2798 }
2799
2800 m_pParentCanvas->ShipIndicatorsDraw(dc, img_height, GPSOffsetPixels, lGPSPoint);
2801 }
2802
DrawFloatingOverlayObjects(ocpnDC & dc)2803 void glChartCanvas::DrawFloatingOverlayObjects( ocpnDC &dc )
2804 {
2805 ViewPort &vp = m_pParentCanvas->GetVP();
2806
2807 // Draw any active or selected routes now
2808 extern Routeman *g_pRouteMan;
2809 // extern Track *g_pActiveTrack;
2810 Route *active_route = g_pRouteMan->GetpActiveRoute();
2811
2812 // if( active_route ) active_route->DrawGL( vp, region );
2813 // if( g_pActiveTrack ) g_pActiveTrack->Draw( dc, vp );
2814 // if( m_pParentCanvas->m_pSelectedRoute ) m_pParentCanvas->m_pSelectedRoute->DrawGL( vp, region );
2815
2816 GridDraw( );
2817
2818 g_overlayCanvas = m_pParentCanvas;
2819 if (g_pi_manager) {
2820 g_pi_manager->SendViewPortToRequestingPlugIns(vp);
2821 g_pi_manager->RenderAllGLCanvasOverlayPlugIns(m_pcontext, vp, m_pParentCanvas->m_canvasIndex);
2822 }
2823
2824 // all functions called with m_pParentCanvas-> are still slow because they go through ocpndc
2825 AISDrawAreaNotices( dc, m_pParentCanvas->GetVP(), m_pParentCanvas );
2826
2827 m_pParentCanvas->DrawAnchorWatchPoints( dc );
2828 AISDraw( dc, m_pParentCanvas->GetVP(), m_pParentCanvas );
2829 ShipDraw( dc );
2830 m_pParentCanvas->AlertDraw( dc );
2831
2832 m_pParentCanvas->RenderRouteLegs( dc );
2833 m_pParentCanvas->ScaleBarDraw( dc );
2834 s57_DrawExtendedLightSectors( dc, m_pParentCanvas->VPoint, m_pParentCanvas->extendedSectorLegs );
2835 }
2836
DrawChartBar(ocpnDC & dc)2837 void glChartCanvas::DrawChartBar( ocpnDC &dc )
2838 {
2839 if(m_pParentCanvas->GetPiano())
2840 m_pParentCanvas->GetPiano()->DrawGL(m_pParentCanvas->GetClientSize().y - m_pParentCanvas->GetPiano()->GetHeight());
2841 }
2842
DrawQuiting()2843 void glChartCanvas::DrawQuiting()
2844 {
2845 #ifndef USE_ANDROID_GLES2
2846 GLubyte pattern[8][8];
2847 for( int y = 0; y < 8; y++ )
2848 for( int x = 0; x < 8; x++ )
2849 pattern[y][x] = (y == x) * 255;
2850
2851 glEnable( GL_BLEND );
2852 glEnable( GL_TEXTURE_2D );
2853 glBindTexture(GL_TEXTURE_2D, 0);
2854
2855 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2856 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2857 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
2858
2859 glTexImage2D( GL_TEXTURE_2D, 0, GL_ALPHA, 8, 8,
2860 0, GL_ALPHA, GL_UNSIGNED_BYTE, pattern );
2861 glColor3f( 0, 0, 0 );
2862
2863 float x = GetSize().x, y = GetSize().y;
2864 float u = x / 8, v = y / 8;
2865
2866 glBegin( GL_QUADS );
2867 glTexCoord2f(0, 0); glVertex2f( 0, 0 );
2868 glTexCoord2f(0, v); glVertex2f( 0, y );
2869 glTexCoord2f(u, v); glVertex2f( x, y );
2870 glTexCoord2f(u, 0); glVertex2f( x, 0 );
2871 glEnd();
2872
2873 glDisable( GL_TEXTURE_2D );
2874 glDisable( GL_BLEND );
2875 #endif
2876 }
2877
DrawCloseMessage(wxString msg)2878 void glChartCanvas::DrawCloseMessage(wxString msg)
2879 {
2880 #ifndef USE_ANDROID_GLES2
2881
2882 if(1){
2883
2884 wxFont *pfont = FontMgr::Get().FindOrCreateFont(12, wxFONTFAMILY_DEFAULT,
2885 wxFONTSTYLE_NORMAL,
2886 wxFONTWEIGHT_NORMAL);
2887
2888 TexFont texfont;
2889
2890 texfont.Build(*pfont);
2891 int w, h;
2892 texfont.GetTextExtent( msg, &w, &h);
2893 h += 2;
2894 int yp = m_pParentCanvas->GetVP().pix_height/2;
2895 int xp = (m_pParentCanvas->GetVP().pix_width - w)/2;
2896
2897 glColor3ub( 243, 229, 47 );
2898
2899 glBegin(GL_QUADS);
2900 glVertex2i(xp, yp);
2901 glVertex2i(xp+w, yp);
2902 glVertex2i(xp+w, yp+h);
2903 glVertex2i(xp, yp+h);
2904 glEnd();
2905
2906 glEnable(GL_BLEND);
2907
2908 glColor3ub( 0, 0, 0 );
2909 glEnable(GL_TEXTURE_2D);
2910 texfont.RenderString( msg, xp, yp);
2911 glDisable(GL_TEXTURE_2D);
2912 glDisable(GL_BLEND);
2913 }
2914 #endif
2915 }
2916
RotateToViewPort(const ViewPort & vp)2917 void glChartCanvas::RotateToViewPort(const ViewPort &vp)
2918 {
2919 #ifndef USE_ANDROID_GLES2
2920
2921 float angle = vp.rotation;
2922
2923 if( fabs( angle ) > 0.0001 )
2924 {
2925 // Rotations occur around 0,0, so translate to rotate around screen center
2926 float xt = vp.pix_width / 2.0, yt = vp.pix_height / 2.0;
2927
2928 glTranslatef( xt, yt, 0 );
2929 glRotatef( angle * 180. / PI, 0, 0, 1 );
2930 glTranslatef( -xt, -yt, 0 );
2931 }
2932 #endif
2933 }
2934
2935 static std::list<double*> combine_work_data;
combineCallbackD(GLdouble coords[3],GLdouble * vertex_data[4],GLfloat weight[4],GLdouble ** dataOut)2936 static void combineCallbackD(GLdouble coords[3],
2937 GLdouble *vertex_data[4],
2938 GLfloat weight[4], GLdouble **dataOut )
2939 {
2940 double *vertex = new double[3];
2941 combine_work_data.push_back(vertex);
2942 memcpy(vertex, coords, 3*(sizeof *coords));
2943 *dataOut = vertex;
2944 }
2945
2946 #ifdef USE_ANDROID_GLES2
vertexCallbackD_GLSL(GLvoid * vertex)2947 void vertexCallbackD_GLSL(GLvoid *vertex)
2948 {
2949
2950 // Grow the work buffer if necessary
2951 if(s_tess_vertex_idx > s_tess_buf_len - 8)
2952 {
2953 int new_buf_len = s_tess_buf_len + 100;
2954 GLfloat * tmp = s_tess_work_buf;
2955
2956 s_tess_work_buf = (GLfloat *)realloc(s_tess_work_buf, new_buf_len * sizeof(GLfloat));
2957 if (NULL == s_tess_work_buf)
2958 {
2959 free(tmp);
2960 tmp = NULL;
2961 }
2962 else
2963 s_tess_buf_len = new_buf_len;
2964 }
2965
2966 GLdouble *pointer = (GLdouble *) vertex;
2967
2968 s_tess_work_buf[s_tess_vertex_idx++] = (float)pointer[0];
2969 s_tess_work_buf[s_tess_vertex_idx++] = (float)pointer[1];
2970
2971 s_nvertex++;
2972 }
2973
beginCallbackD_GLSL(GLenum mode)2974 void beginCallbackD_GLSL( GLenum mode)
2975 {
2976 s_tess_vertex_idx_this = s_tess_vertex_idx;
2977 s_tess_mode = mode;
2978 s_nvertex = 0;
2979 }
2980
endCallbackD_GLSL()2981 void endCallbackD_GLSL()
2982 {
2983 glUseProgram(color_tri_shader_program);
2984
2985 // Disable VBO's (vertex buffer objects) for attributes.
2986 glBindBuffer( GL_ARRAY_BUFFER, 0 );
2987 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
2988
2989 float *bufPt = &s_tess_work_buf[s_tess_vertex_idx_this];
2990 GLint pos = glGetAttribLocation(color_tri_shader_program, "position");
2991 glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), bufPt);
2992 glEnableVertexAttribArray(pos);
2993
2994 GLint matloc = glGetUniformLocation(color_tri_shader_program,"MVMatrix");
2995 glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)s_tessVP.vp_transform);
2996
2997 // Use color stored in static variable.
2998 float colorv[4];
2999 colorv[0] = s_regionColor.Red() / float(256);
3000 colorv[1] = s_regionColor.Green() / float(256);
3001 colorv[2] = s_regionColor.Blue() / float(256);
3002 colorv[3] = s_regionColor.Alpha() / float(256);
3003
3004 GLint colloc = glGetUniformLocation(color_tri_shader_program,"color");
3005 glUniform4fv(colloc, 1, colorv);
3006
3007 glDrawArrays(s_tess_mode, 0, s_nvertex);
3008
3009 }
3010 #else
vertexCallbackD(GLvoid * vertex)3011 void vertexCallbackD(GLvoid *vertex)
3012 {
3013 glVertex3dv( (GLdouble *)vertex);
3014 }
3015
beginCallbackD(GLenum mode)3016 void beginCallbackD( GLenum mode)
3017 {
3018 glBegin( mode );
3019 }
3020
endCallbackD()3021 void endCallbackD()
3022 {
3023 glEnd();
3024 }
3025 #endif
3026
3027
DrawRegion(ViewPort & vp,const LLRegion & region)3028 void glChartCanvas::DrawRegion(ViewPort &vp, const LLRegion ®ion)
3029 {
3030 float lat_dist, lon_dist;
3031 GetLatLonCurveDist(vp, lat_dist, lon_dist);
3032
3033 GLUtesselator *tobj = gluNewTess();
3034
3035 #ifdef USE_ANDROID_GLES2
3036 gluTessCallback( tobj, GLU_TESS_VERTEX, (_GLUfuncptr) &vertexCallbackD_GLSL );
3037 gluTessCallback( tobj, GLU_TESS_BEGIN, (_GLUfuncptr) &beginCallbackD_GLSL );
3038 gluTessCallback( tobj, GLU_TESS_END, (_GLUfuncptr) &endCallbackD_GLSL );
3039 gluTessCallback( tobj, GLU_TESS_COMBINE, (_GLUfuncptr) &combineCallbackD );
3040 s_tessVP = vp;
3041
3042 #else
3043 gluTessCallback( tobj, GLU_TESS_VERTEX, (_GLUfuncptr) &vertexCallbackD );
3044 gluTessCallback( tobj, GLU_TESS_BEGIN, (_GLUfuncptr) &beginCallbackD );
3045 gluTessCallback( tobj, GLU_TESS_END, (_GLUfuncptr) &endCallbackD );
3046 gluTessCallback( tobj, GLU_TESS_COMBINE, (_GLUfuncptr) &combineCallbackD );
3047 #endif
3048
3049 gluTessNormal( tobj, 0, 0, 1);
3050
3051 gluTessBeginPolygon(tobj, NULL);
3052 for(std::list<poly_contour>::const_iterator i = region.contours.begin(); i != region.contours.end(); i++) {
3053 gluTessBeginContour(tobj);
3054 contour_pt l = *i->rbegin();
3055 double sml[2];
3056 bool sml_valid = false;
3057 for(poly_contour::const_iterator j = i->begin(); j != i->end(); j++) {
3058 int lat_splits = floor(fabs(j->y - l.y) / lat_dist);
3059 int lon_splits = floor(fabs(j->x - l.x) / lon_dist);
3060 int splits = wxMax(lat_splits, lon_splits) + 1;
3061
3062 double smj[2];
3063 if(splits != 1) {
3064 // must perform border interpolation in mercator space as this is what the charts use
3065 toSM(j->y, j->x, 0, 0, smj+0, smj+1);
3066 if(!sml_valid)
3067 toSM(l.y, l.x, 0, 0, sml+0, sml+1);
3068 }
3069
3070 for(int i = 0; i<splits; i++) {
3071 double lat, lon;
3072 if(i == splits - 1)
3073 lat = j->y, lon = j->x;
3074 else {
3075 double d = (double)(i+1) / splits;
3076 fromSM(d*smj[0] + (1-d)*sml[0], d*smj[1] + (1-d)*sml[1], 0, 0, &lat, &lon);
3077 }
3078 wxPoint2DDouble q = vp.GetDoublePixFromLL(lat, lon);
3079 if(std::isnan(q.m_x))
3080 continue;
3081
3082 double *p = new double[6];
3083 p[0] = q.m_x, p[1] = q.m_y, p[2] = 0;
3084 gluTessVertex(tobj, p, p);
3085 combine_work_data.push_back(p);
3086 }
3087 l = *j;
3088
3089 if((sml_valid = splits != 1))
3090 memcpy(sml, smj, sizeof smj);
3091 }
3092 gluTessEndContour(tobj);
3093 }
3094 gluTessEndPolygon(tobj);
3095
3096 gluDeleteTess(tobj);
3097
3098 for(std::list<double*>::iterator i = combine_work_data.begin(); i!=combine_work_data.end(); i++)
3099 delete [] *i;
3100 combine_work_data.clear();
3101 }
3102
3103 /* set stencil buffer to clip in this region, and optionally clear using the current color */
SetClipRegion(ViewPort & vp,const LLRegion & region)3104 void glChartCanvas::SetClipRegion(ViewPort &vp, const LLRegion ®ion)
3105 {
3106 glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE ); // disable color buffer
3107
3108 if( s_b_useStencil ) {
3109 // Create a stencil buffer for clipping to the region
3110 glEnable( GL_STENCIL_TEST );
3111 glStencilMask( 0x1 ); // write only into bit 0 of the stencil buffer
3112 glClear( GL_STENCIL_BUFFER_BIT );
3113
3114 // We are going to write "1" into the stencil buffer wherever the region is valid
3115 glStencilFunc( GL_ALWAYS, 1, 1 );
3116 glStencilOp( GL_KEEP, GL_KEEP, GL_REPLACE );
3117 }
3118 #ifndef USE_ANDROID_GLES2
3119
3120 else // Use depth buffer for clipping
3121 {
3122 glEnable( GL_DEPTH_TEST ); // to enable writing to the depth buffer
3123 glDepthFunc( GL_ALWAYS ); // to ensure everything you draw passes
3124 glDepthMask( GL_TRUE ); // to allow writes to the depth buffer
3125
3126 glClear( GL_DEPTH_BUFFER_BIT ); // for a fresh start
3127
3128 // Decompose the region into rectangles, and draw as quads
3129 // With z = 1
3130 // dep buffer clear = 1
3131 // 1 makes 0 in dep buffer, works
3132 // 0 make .5 in depth buffer
3133 // -1 makes 1 in dep buffer
3134
3135 // Depth buffer runs from 0 at z = 1 to 1 at z = -1
3136 // Draw the clip geometry at z = 0.5, giving a depth buffer value of 0.25
3137 // Subsequent drawing at z=0 (depth = 0.5) will pass if using glDepthFunc(GL_GREATER);
3138 glTranslatef( 0, 0, .5 );
3139 }
3140 #endif
3141
3142 DrawRegion(vp, region);
3143
3144 if( s_b_useStencil ) {
3145 // Now set the stencil ops to subsequently render only where the stencil bit is "1"
3146 glStencilFunc( GL_EQUAL, 1, 1 );
3147 glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP );
3148 }
3149 #ifndef USE_ANDROID_GLES2
3150 else {
3151 glDepthFunc( GL_GREATER ); // Set the test value
3152 glDepthMask( GL_FALSE ); // disable depth buffer
3153 glTranslatef( 0, 0, -.5 ); // reset translation
3154 }
3155 #endif
3156 glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE ); // re-enable color buffer
3157 }
3158
SetClipRect(const ViewPort & vp,const wxRect & rect,bool b_clear)3159 void glChartCanvas::SetClipRect(const ViewPort &vp, const wxRect &rect, bool b_clear)
3160 {
3161 /* for some reason this causes an occasional bug in depth mode, I cannot
3162 seem to solve it yet, so for now: */
3163 if(s_b_useStencil && s_b_useScissorTest) {
3164 wxRect vp_rect(0, 0, vp.pix_width, vp.pix_height);
3165 if(rect != vp_rect) {
3166 glEnable(GL_SCISSOR_TEST);
3167 glScissor(rect.x, vp.pix_height - rect.height - rect.y, rect.width, rect.height);
3168 }
3169 #ifndef USE_ANDROID_GLES2
3170 if(b_clear) {
3171 glBegin(GL_QUADS);
3172 glVertex2i( rect.x, rect.y );
3173 glVertex2i( rect.x + rect.width, rect.y );
3174 glVertex2i( rect.x + rect.width, rect.y + rect.height );
3175 glVertex2i( rect.x, rect.y + rect.height );
3176 glEnd();
3177 }
3178
3179 /* the code in s52plib depends on the depth buffer being
3180 initialized to this value, this code should go there instead and
3181 only a flag set here. */
3182 if(!s_b_useStencil) {
3183 glClearDepth( 0.25 );
3184 glDepthMask( GL_TRUE ); // to allow writes to the depth buffer
3185 glClear( GL_DEPTH_BUFFER_BIT );
3186 glDepthMask( GL_FALSE );
3187 glClearDepth( 1 ); // set back to default of 1
3188 glDepthFunc( GL_GREATER ); // Set the test value
3189 }
3190 #endif
3191 return;
3192 }
3193
3194 #ifndef USE_ANDROID_GLES2
3195 // slower way if there is no scissor support
3196 if(!b_clear)
3197 glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE ); // disable color buffer
3198
3199 if( s_b_useStencil ) {
3200 // Create a stencil buffer for clipping to the region
3201 glEnable( GL_STENCIL_TEST );
3202 glStencilMask( 0x1 ); // write only into bit 0 of the stencil buffer
3203 glClear( GL_STENCIL_BUFFER_BIT );
3204
3205 // We are going to write "1" into the stencil buffer wherever the region is valid
3206 glStencilFunc( GL_ALWAYS, 1, 1 );
3207 glStencilOp( GL_KEEP, GL_KEEP, GL_REPLACE );
3208 } else // Use depth buffer for clipping
3209 {
3210 glEnable( GL_DEPTH_TEST ); // to enable writing to the depth buffer
3211 glDepthFunc( GL_ALWAYS ); // to ensure everything you draw passes
3212 glDepthMask( GL_TRUE ); // to allow writes to the depth buffer
3213
3214 glClear( GL_DEPTH_BUFFER_BIT ); // for a fresh start
3215
3216 // Decompose the region into rectangles, and draw as quads
3217 // With z = 1
3218 // dep buffer clear = 1
3219 // 1 makes 0 in dep buffer, works
3220 // 0 make .5 in depth buffer
3221 // -1 makes 1 in dep buffer
3222
3223 // Depth buffer runs from 0 at z = 1 to 1 at z = -1
3224 // Draw the clip geometry at z = 0.5, giving a depth buffer value of 0.25
3225 // Subsequent drawing at z=0 (depth = 0.5) will pass if using glDepthFunc(GL_GREATER);
3226 glTranslatef( 0, 0, .5 );
3227 }
3228
3229 glBegin(GL_QUADS);
3230 glVertex2i( rect.x, rect.y );
3231 glVertex2i( rect.x + rect.width, rect.y );
3232 glVertex2i( rect.x + rect.width, rect.y + rect.height );
3233 glVertex2i( rect.x, rect.y + rect.height );
3234 glEnd();
3235
3236 if( s_b_useStencil ) {
3237 // Now set the stencil ops to subsequently render only where the stencil bit is "1"
3238 glStencilFunc( GL_EQUAL, 1, 1 );
3239 glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP );
3240 } else {
3241 glDepthFunc( GL_GREATER ); // Set the test value
3242 glDepthMask( GL_FALSE ); // disable depth buffer
3243 glTranslatef( 0, 0, -.5 ); // reset translation
3244 }
3245
3246 if(!b_clear)
3247 glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE ); // re-enable color buffer
3248 #endif
3249 }
3250
DisableClipRegion()3251 void glChartCanvas::DisableClipRegion()
3252 {
3253 glDisable( GL_SCISSOR_TEST );
3254 glDisable( GL_STENCIL_TEST );
3255 glDisable( GL_DEPTH_TEST );
3256 }
3257
Invalidate()3258 void glChartCanvas::Invalidate()
3259 {
3260 /* should probably use a different flag for this */
3261
3262 m_cache_vp.Invalidate();
3263
3264 }
3265
RenderRasterChartRegionGL(ChartBase * chart,ViewPort & vp,LLRegion & region)3266 void glChartCanvas::RenderRasterChartRegionGL( ChartBase *chart, ViewPort &vp, LLRegion ®ion )
3267 {
3268 ChartBaseBSB *pBSBChart = dynamic_cast<ChartBaseBSB*>( chart );
3269 if( !pBSBChart ) return;
3270
3271 if(b_inCompressAllCharts) return; // don't want multiple texfactories to exist
3272
3273
3274 // Look for the texture factory for this chart
3275 wxString key = chart->GetHashKey();
3276
3277 glTexFactory *pTexFact;
3278 ChartPathHashTexfactType &hash = g_glTextureManager->m_chart_texfactory_hash;
3279 ChartPathHashTexfactType::iterator ittf = hash.find( key );
3280
3281 // Not Found ?
3282 if( ittf == hash.end() ){
3283 hash[key] = new glTexFactory(chart, g_raster_format);
3284 hash[key]->SetHashKey(key);
3285 }
3286
3287 pTexFact = hash[key];
3288 pTexFact->SetLRUTime(++m_LRUtime);
3289
3290 // for small scales, don't use normalized coordinates for accuracy (difference is up to 3 meters error)
3291 bool use_norm_vp = glChartCanvas::HasNormalizedViewPort(vp) && pBSBChart->GetPPM() < 1;
3292 pTexFact->PrepareTiles(vp, use_norm_vp, pBSBChart);
3293
3294 // For underzoom cases, we will define the textures as having their base levels
3295 // equivalent to a level "n" mipmap, where n is calculated, and is always binary
3296 // This way we can avoid loading much texture memory
3297
3298 int base_level;
3299 if(vp.m_projection_type == PROJECTION_MERCATOR &&
3300 chart->GetChartProjectionType() == PROJECTION_MERCATOR) {
3301 double scalefactor = pBSBChart->GetRasterScaleFactor(vp);
3302 base_level = log(scalefactor) / log(2.0);
3303
3304 if(base_level < 0) /* for overzoom */
3305 base_level = 0;
3306 if(base_level > g_mipmap_max_level)
3307 base_level = g_mipmap_max_level;
3308 } else
3309 base_level = 0; // base level should be computed per tile, for now load all
3310
3311 /* setup opengl parameters */
3312 glEnable( GL_TEXTURE_2D );
3313 #ifndef USE_ANDROID_GLES2
3314 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
3315
3316 glEnableClientState(GL_VERTEX_ARRAY);
3317 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
3318
3319 if(use_norm_vp) {
3320 glPushMatrix();
3321 double lat, lon;
3322 pTexFact->GetCenter(lat, lon);
3323 MultMatrixViewPort(vp, lat, lon);
3324 }
3325 #endif
3326
3327 LLBBox box = region.GetBox();
3328 int numtiles;
3329 int mem_used = 0;
3330 if (g_memCacheLimit > 0) {
3331 // GetMemoryStatus is slow on linux
3332 GetMemoryStatus(0, &mem_used);
3333 }
3334
3335 glTexTile **tiles = pTexFact->GetTiles(numtiles);
3336 for(int i = 0; i<numtiles; i++) {
3337 glTexTile *tile = tiles[i];
3338 if(region.IntersectOut(tile->box)) {
3339
3340 /* user setting is in MB while we count exact bytes */
3341 bool bGLMemCrunch = g_tex_mem_used > g_GLOptions.m_iTextureMemorySize * 1024 * 1024;
3342 if( bGLMemCrunch)
3343 pTexFact->DeleteTexture( tile->rect );
3344 } else {
3345 bool texture = pTexFact->PrepareTexture( base_level, tile->rect, global_color_scheme, mem_used );
3346
3347 float *coords;
3348 if(use_norm_vp)
3349 coords = tile->m_coords;
3350 else {
3351 coords = new float[2 * tile->m_ncoords];
3352 for(int i=0; i<tile->m_ncoords; i++) {
3353 wxPoint2DDouble p = vp.GetDoublePixFromLL(tile->m_coords[2*i+0],
3354 tile->m_coords[2*i+1]);
3355 coords[2*i+0] = p.m_x;
3356 coords[2*i+1] = p.m_y;
3357 }
3358 }
3359
3360 #ifdef USE_ANDROID_GLES2
3361 RenderTextures(coords, tile->m_texcoords, 4, m_pParentCanvas->GetpVP());
3362 #else
3363 if(!texture) { // failed to load, draw red
3364 glDisable(GL_TEXTURE_2D);
3365 glColor3f(1, 0, 0);
3366 }
3367
3368 glTexCoordPointer(2, GL_FLOAT, 2*sizeof(GLfloat), tile->m_texcoords);
3369 glVertexPointer(2, GL_FLOAT, 2*sizeof(GLfloat), coords);
3370 glDrawArrays(GL_QUADS, 0, tile->m_ncoords);
3371 #endif
3372 if(!texture)
3373 glEnable(GL_TEXTURE_2D);
3374
3375 if(!use_norm_vp)
3376 delete [] coords;
3377 }
3378 }
3379
3380 glDisable(GL_TEXTURE_2D);
3381
3382 #ifndef USE_ANDROID_GLES2
3383 if(use_norm_vp)
3384 glPopMatrix();
3385
3386 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
3387 glDisableClientState(GL_VERTEX_ARRAY);
3388 #endif
3389 }
3390
RenderQuiltViewGL(ViewPort & vp,const OCPNRegion & rect_region)3391 void glChartCanvas::RenderQuiltViewGL( ViewPort &vp, const OCPNRegion &rect_region )
3392 {
3393 if( !m_pParentCanvas->m_pQuilt->GetnCharts() || m_pParentCanvas->m_pQuilt->IsBusy() )
3394 return;
3395
3396 // render the quilt
3397 ChartBase *chart = m_pParentCanvas->m_pQuilt->GetFirstChart();
3398
3399 // Check the first, smallest scale chart
3400 if(chart) {
3401 // if( ! m_pParentCanvas->IsChartLargeEnoughToRender( chart, vp ) )
3402 // chart = NULL;
3403 }
3404
3405 LLRegion region = vp.GetLLRegion(rect_region);
3406
3407 LLRegion rendered_region;
3408 while( chart ) {
3409
3410 // This test does not need to be done for raster charts, since
3411 // we can assume that texture binding is acceptably fast regardless of the render region,
3412 // and that the quilt zoom methods choose a reasonable reference chart.
3413 if(chart->GetChartFamily() != CHART_FAMILY_RASTER)
3414 {
3415 // if( ! m_pParentCanvas->IsChartLargeEnoughToRender( chart, vp ) ) {
3416 // chart = m_pParentCanvas->m_pQuilt->GetNextChart();
3417 // continue;
3418 // }
3419 }
3420
3421 QuiltPatch *pqp = m_pParentCanvas->m_pQuilt->GetCurrentPatch();
3422 if( pqp->b_Valid ) {
3423 LLRegion get_region = pqp->ActiveRegion;
3424 bool b_rendered = false;
3425
3426 if( !pqp->b_overlay ) {
3427 get_region.Intersect( region );
3428 if( !get_region.Empty() ) {
3429 if( chart->GetChartFamily() == CHART_FAMILY_RASTER ) {
3430
3431 ChartBaseBSB *Patch_Ch_BSB = dynamic_cast<ChartBaseBSB*>( chart );
3432 if (Patch_Ch_BSB) {
3433 SetClipRegion(vp, pqp->ActiveRegion/*pqp->quilt_region*/);
3434 RenderRasterChartRegionGL( chart, vp, get_region );
3435 DisableClipRegion();
3436 b_rendered = true;
3437 }
3438 else if(chart->GetChartType() == CHART_TYPE_MBTILES){
3439 SetClipRegion(vp, pqp->ActiveRegion/*pqp->quilt_region*/);
3440 chart->RenderRegionViewOnGL( *m_pcontext, vp, rect_region, get_region );
3441 DisableClipRegion();
3442 }
3443
3444 } else if(chart->GetChartFamily() == CHART_FAMILY_VECTOR ) {
3445
3446 if(chart->GetChartType() == CHART_TYPE_CM93COMP){
3447 RenderNoDTA(vp, get_region);
3448 chart->RenderRegionViewOnGL( *m_pcontext, vp, rect_region, get_region );
3449 }
3450 else{
3451 s57chart *Chs57 = dynamic_cast<s57chart*>( chart );
3452 if(Chs57){
3453 if(Chs57->m_RAZBuilt){
3454 RenderNoDTA(vp, get_region);
3455 Chs57->RenderRegionViewOnGLNoText( *m_pcontext, vp, rect_region, get_region );
3456 DisableClipRegion();
3457 }
3458 else{
3459 // The SENC is quesed for building, so..
3460 // Show GSHHS with compatible color scheme in the meantime.
3461 ocpnDC gldc( *this );
3462 const LLRegion &oregion = get_region;
3463 LLBBox box = oregion.GetBox();
3464
3465 wxPoint p1 = vp.GetPixFromLL( box.GetMaxLat(), box.GetMinLon());
3466 wxPoint p2 = vp.GetPixFromLL( box.GetMaxLat(), box.GetMaxLon());
3467 wxPoint p3 = vp.GetPixFromLL( box.GetMinLat(), box.GetMaxLon());
3468 wxPoint p4 = vp.GetPixFromLL( box.GetMinLat(), box.GetMinLon());
3469
3470 wxRect srect(p1.x, p1.y, p3.x - p1.x, p4.y - p2.y);
3471
3472 bool world = false;
3473 ViewPort cvp = ClippedViewport(vp, get_region);
3474 if( m_pParentCanvas->GetWorldBackgroundChart()){
3475 SetClipRegion(cvp, get_region);
3476 m_pParentCanvas->GetWorldBackgroundChart()->SetColorsDirect(GetGlobalColor( _T ( "LANDA" ) ), GetGlobalColor( _T ( "DEPMS" )));
3477 RenderWorldChart(gldc, cvp, srect, world);
3478 m_pParentCanvas->GetWorldBackgroundChart()->SetColorScheme(global_color_scheme);
3479 DisableClipRegion();
3480 }
3481 }
3482 }
3483 else{
3484 ChartPlugInWrapper *ChPI = dynamic_cast<ChartPlugInWrapper*>( chart );
3485 if(ChPI){
3486 RenderNoDTA(vp, get_region);
3487 ChPI->RenderRegionViewOnGLNoText( *m_pcontext, vp, rect_region, get_region );
3488 }
3489 else{
3490 RenderNoDTA(vp, get_region);
3491 chart->RenderRegionViewOnGL( *m_pcontext, vp, rect_region, get_region );
3492 }
3493 }
3494 }
3495 }
3496 }
3497 }
3498
3499 if(b_rendered) {
3500 // LLRegion get_region = pqp->ActiveRegion;
3501 // get_region.Intersect( Region ); not technically required?
3502 // rendered_region.Union(get_region);
3503 }
3504 }
3505
3506
3507 chart = m_pParentCanvas->m_pQuilt->GetNextChart();
3508 }
3509
3510 // Render any Overlay patches for s57 charts(cells)
3511 if( m_pParentCanvas->m_pQuilt->HasOverlays() ) {
3512 ChartBase *pch = m_pParentCanvas->m_pQuilt->GetFirstChart();
3513 while( pch ) {
3514 QuiltPatch *pqp = m_pParentCanvas->m_pQuilt->GetCurrentPatch();
3515 if( pqp->b_Valid && pqp->b_overlay && pch->GetChartFamily() == CHART_FAMILY_VECTOR ) {
3516 LLRegion get_region = pqp->ActiveRegion;
3517
3518 get_region.Intersect( region );
3519 if( !get_region.Empty() ) {
3520 s57chart *Chs57 = dynamic_cast<s57chart*>( pch );
3521 if( Chs57 )
3522 Chs57->RenderOverlayRegionViewOnGL( *m_pcontext, vp, rect_region, get_region );
3523 else{
3524 ChartPlugInWrapper *ChPI = dynamic_cast<ChartPlugInWrapper*>( pch );
3525 if(ChPI){
3526 ChPI->RenderRegionViewOnGL( *m_pcontext, vp, rect_region, get_region );
3527 }
3528 }
3529
3530 }
3531 }
3532
3533 pch = m_pParentCanvas->m_pQuilt->GetNextChart();
3534 }
3535 }
3536
3537 // Hilite rollover patch
3538 LLRegion hiregion = m_pParentCanvas->m_pQuilt->GetHiliteRegion();
3539
3540 if( !hiregion.Empty() ) {
3541 glEnable( GL_BLEND );
3542
3543 double hitrans;
3544 switch( global_color_scheme ) {
3545 case GLOBAL_COLOR_SCHEME_DAY:
3546 hitrans = .4;
3547 break;
3548 case GLOBAL_COLOR_SCHEME_DUSK:
3549 hitrans = .2;
3550 break;
3551 case GLOBAL_COLOR_SCHEME_NIGHT:
3552 hitrans = .1;
3553 break;
3554 default:
3555 hitrans = .4;
3556 break;
3557 }
3558
3559 #ifndef USE_ANDROID_GLES2
3560 glColor4f( (float) .8, (float) .4, (float) .4, (float) hitrans );
3561 #else
3562 s_regionColor = wxColor(204, 102, 102, hitrans * 256);
3563 #endif
3564
3565 DrawRegion(vp, hiregion);
3566
3567 glDisable( GL_BLEND );
3568 }
3569 m_pParentCanvas->m_pQuilt->SetRenderedVP( vp );
3570
3571 }
3572
RenderQuiltViewGLText(ViewPort & vp,const OCPNRegion & rect_region)3573 void glChartCanvas::RenderQuiltViewGLText( ViewPort &vp, const OCPNRegion &rect_region )
3574 {
3575 if( !m_pParentCanvas->m_pQuilt->GetnCharts() || m_pParentCanvas->m_pQuilt->IsBusy() )
3576 return;
3577
3578 // render the quilt
3579 ChartBase *chart = m_pParentCanvas->m_pQuilt->GetLargestScaleChart();
3580
3581 LLRegion region = vp.GetLLRegion(rect_region);
3582
3583 LLRegion rendered_region;
3584 while( chart ) {
3585
3586 QuiltPatch *pqp = m_pParentCanvas->m_pQuilt->GetCurrentPatch();
3587 if( pqp->b_Valid ) {
3588 LLRegion get_region = pqp->ActiveRegion;
3589
3590 if( !pqp->b_overlay ) {
3591 if(chart->GetChartFamily() == CHART_FAMILY_VECTOR ) {
3592
3593 s57chart *Chs57 = dynamic_cast<s57chart*>( chart );
3594 if(Chs57){
3595 Chs57->RenderViewOnGLTextOnly( *m_pcontext, vp);
3596 }
3597 else{
3598 ChartPlugInWrapper *ChPI = dynamic_cast<ChartPlugInWrapper*>( chart );
3599 if(ChPI){
3600 ChPI->RenderRegionViewOnGLTextOnly( *m_pcontext, vp, rect_region);
3601 }
3602 }
3603 }
3604 }
3605 }
3606
3607
3608 chart = m_pParentCanvas->m_pQuilt->GetNextSmallerScaleChart();
3609 }
3610
3611 /*
3612 // Render any Overlay patches for s57 charts(cells)
3613 if( m_pParentCanvas->m_pQuilt->HasOverlays() ) {
3614 ChartBase *pch = m_pParentCanvas->m_pQuilt->GetFirstChart();
3615 while( pch ) {
3616 QuiltPatch *pqp = m_pParentCanvas->m_pQuilt->GetCurrentPatch();
3617 if( pqp->b_Valid && pqp->b_overlay && pch->GetChartFamily() == CHART_FAMILY_VECTOR ) {
3618 LLRegion get_region = pqp->ActiveRegion;
3619
3620 get_region.Intersect( region );
3621 if( !get_region.Empty() ) {
3622 s57chart *Chs57 = dynamic_cast<s57chart*>( pch );
3623 if( Chs57 )
3624 Chs57->RenderOverlayRegionViewOnGL( *m_pcontext, vp, rect_region, get_region );
3625 }
3626 }
3627
3628 pch = m_pParentCanvas->m_pQuilt->GetNextChart();
3629 }
3630 }
3631 */
3632 }
3633
RenderCharts(ocpnDC & dc,const OCPNRegion & rect_region)3634 void glChartCanvas::RenderCharts(ocpnDC &dc, const OCPNRegion &rect_region)
3635 {
3636 ViewPort &vp = m_pParentCanvas->VPoint;
3637
3638 // Only for cm93 (not quilted), SetVPParms can change the valid region of the chart
3639 // we need to know this before rendering the chart so we can compute the background region
3640 // and nodta regions correctly. I would prefer to just perform this here (or in SetViewPoint)
3641 // for all vector charts instead of in their render routine, but how to handle quilted cases?
3642 if(!vp.b_quilt && m_pParentCanvas->m_singleChart->GetChartType() == CHART_TYPE_CM93COMP)
3643 static_cast<cm93compchart*>( m_pParentCanvas->m_singleChart )->SetVPParms( vp );
3644
3645 LLRegion chart_region;
3646 if( !vp.b_quilt && (m_pParentCanvas->m_singleChart->GetChartType() == CHART_TYPE_PLUGIN) ){
3647 if(m_pParentCanvas->m_singleChart->GetChartFamily() == CHART_FAMILY_RASTER){
3648 // We do this the hard way, since PlugIn Raster charts do not understand LLRegion yet...
3649 double ll[8];
3650 ChartPlugInWrapper *cpw = dynamic_cast<ChartPlugInWrapper*> ( m_pParentCanvas->m_singleChart );
3651 if( !cpw) return;
3652
3653 cpw->chartpix_to_latlong(0, 0, ll+0, ll+1);
3654 cpw->chartpix_to_latlong(0, cpw->GetSize_Y(), ll+2, ll+3);
3655 cpw->chartpix_to_latlong(cpw->GetSize_X(), cpw->GetSize_Y(), ll+4, ll+5);
3656 cpw->chartpix_to_latlong(cpw->GetSize_X(), 0, ll+6, ll+7);
3657
3658 // for now don't allow raster charts to cross both 0 meridian and IDL (complicated to deal with)
3659 for(int i=1; i<6; i+=2)
3660 if(fabs(ll[i] - ll[i+2]) > 180) {
3661 // we detect crossing idl here, make all longitudes positive
3662 for(int i=1; i<8; i+=2)
3663 if(ll[i] < 0)
3664 ll[i] += 360;
3665 break;
3666 }
3667
3668 chart_region = LLRegion(4, ll);
3669 }
3670 else{
3671 Extent ext;
3672 m_pParentCanvas->m_singleChart->GetChartExtent(&ext);
3673
3674 double ll[8] = {ext.SLAT, ext.WLON,
3675 ext.SLAT, ext.ELON,
3676 ext.NLAT, ext.ELON,
3677 ext.NLAT, ext.WLON};
3678 chart_region = LLRegion(4, ll);
3679 }
3680 }
3681 else
3682 chart_region = vp.b_quilt ? m_pParentCanvas->m_pQuilt->GetFullQuiltRegion() : m_pParentCanvas->m_singleChart->GetValidRegion();
3683
3684 bool world_view = false;
3685 for(OCPNRegionIterator upd ( rect_region ); upd.HaveRects(); upd.NextRect()) {
3686 wxRect rect = upd.GetRect();
3687 LLRegion background_region = vp.GetLLRegion(rect);
3688 // Remove the valid chart area to find the region NOT covered by the charts
3689 background_region.Subtract(chart_region);
3690
3691 if(!background_region.Empty()) {
3692 ViewPort cvp = ClippedViewport(vp, background_region);
3693 RenderWorldChart(dc, cvp, rect, world_view);
3694 }
3695 }
3696
3697 if(vp.b_quilt)
3698 RenderQuiltViewGL( vp, rect_region );
3699 else {
3700 LLRegion region = vp.GetLLRegion(rect_region);
3701 if( m_pParentCanvas->m_singleChart->GetChartFamily() == CHART_FAMILY_RASTER ){
3702 if(m_pParentCanvas->m_singleChart->GetChartType() == CHART_TYPE_MBTILES)
3703 m_pParentCanvas->m_singleChart->RenderRegionViewOnGL( *m_pcontext, vp, rect_region, region );
3704 else
3705 RenderRasterChartRegionGL( m_pParentCanvas->m_singleChart, vp, region );
3706 }
3707 else if( m_pParentCanvas->m_singleChart->GetChartFamily() == CHART_FAMILY_VECTOR ) {
3708 chart_region.Intersect(region);
3709 RenderNoDTA(vp, chart_region);
3710 m_pParentCanvas->m_singleChart->RenderRegionViewOnGL( *m_pcontext, vp, rect_region, region );
3711 }
3712 }
3713
3714 }
3715
RenderNoDTA(ViewPort & vp,const LLRegion & region,int transparency)3716 void glChartCanvas::RenderNoDTA(ViewPort &vp, const LLRegion ®ion, int transparency)
3717 {
3718 wxColour color = GetGlobalColor( _T ( "NODTA" ) );
3719 #ifndef USE_ANDROID_GLES2
3720 if( color.IsOk() )
3721 glColor4ub( color.Red(), color.Green(), color.Blue(), transparency );
3722 else
3723 glColor4ub( 163, 180, 183, transparency );
3724
3725 glEnable(GL_BLEND);
3726 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
3727
3728 #else
3729 // Store the color for tesselator callback pickup.
3730 s_regionColor = color;
3731 #endif
3732
3733 DrawRegion(vp, region);
3734
3735 glDisable(GL_BLEND);
3736 }
3737
RenderNoDTA(ViewPort & vp,ChartBase * chart)3738 void glChartCanvas::RenderNoDTA(ViewPort &vp, ChartBase *chart)
3739 {
3740 }
3741
3742 /* render world chart, but only in this rectangle */
RenderWorldChart(ocpnDC & dc,ViewPort & vp,wxRect & rect,bool & world_view)3743 void glChartCanvas::RenderWorldChart(ocpnDC &dc, ViewPort &vp, wxRect &rect, bool &world_view)
3744 {
3745 // set gl color to water
3746 wxColour water = m_pParentCanvas->pWorldBackgroundChart->water;
3747
3748 // clear background
3749 if(!world_view) {
3750 #ifndef USE_ANDROID_GLES2
3751 // set gl color to water
3752 glColor3ub(water.Red(), water.Green(), water.Blue());
3753
3754 if(vp.m_projection_type == PROJECTION_ORTHOGRAPHIC) {
3755 // for this projection, if zoomed out far enough that the earth does
3756 // not fill the viewport we need to first clear the screen black and
3757 // draw a blue circle representing the earth
3758
3759 ViewPort tvp = vp;
3760 tvp.clat = 0, tvp.clon = 0;
3761 tvp.rotation = 0;
3762 wxPoint2DDouble p = tvp.GetDoublePixFromLL( 89.99, 0);
3763 float w = ((float)tvp.pix_width)/2, h = ((float)tvp.pix_height)/2;
3764 double world_r = h - p.m_y;
3765 const float pi_ovr100 = float(M_PI)/100;
3766 if(world_r*world_r < w*w + h*h) {
3767 glClear( GL_COLOR_BUFFER_BIT );
3768
3769 glBegin(GL_TRIANGLE_FAN);
3770 float w = ((float)vp.pix_width)/2, h = ((float)vp.pix_height)/2;
3771 for(float theta = 0; theta < 2*M_PI+.01f; theta+=pi_ovr100)
3772 glVertex2f(w + world_r*sinf(theta), h + world_r*cosf(theta));
3773 glEnd();
3774
3775 world_view = true;
3776 }
3777 } else if(vp.m_projection_type == PROJECTION_EQUIRECTANGULAR) {
3778 // for this projection we will draw black outside of the earth (beyond the pole)
3779 glClear( GL_COLOR_BUFFER_BIT );
3780
3781 wxPoint2DDouble p[4] = {
3782 vp.GetDoublePixFromLL( 90, vp.clon - 170 ),
3783 vp.GetDoublePixFromLL( 90, vp.clon + 170 ),
3784 vp.GetDoublePixFromLL(-90, vp.clon + 170 ),
3785 vp.GetDoublePixFromLL(-90, vp.clon - 170 )};
3786
3787 glBegin(GL_QUADS);
3788 for(int i = 0; i<4; i++)
3789 glVertex2f(p[i].m_x, p[i].m_y);
3790 glEnd();
3791
3792 world_view = true;
3793 }
3794 #endif
3795
3796 if(!world_view) {
3797 int x1 = rect.x, y1 = rect.y, x2 = x1 + rect.width, y2 = y1 + rect.height;
3798 #ifdef USE_ANDROID_GLES2
3799 glUseProgram(color_tri_shader_program);
3800
3801 float pf[6];
3802 pf[0] = x1; pf[1] = y1; pf[2] = x2; pf[3] = y1; pf[4] = x2; pf[5] = y2;
3803
3804 GLint pos = glGetAttribLocation(color_tri_shader_program, "position");
3805 glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), pf);
3806 glEnableVertexAttribArray(pos);
3807
3808 GLint matloc = glGetUniformLocation(color_tri_shader_program,"MVMatrix");
3809 glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)vp.vp_transform);
3810
3811 float colorv[4];
3812 colorv[0] = water.Red() / float(256);
3813 colorv[1] = water.Green() / float(256);
3814 colorv[2] = water.Blue() / float(256);
3815 colorv[3] = 1.0;
3816
3817 GLint colloc = glGetUniformLocation(color_tri_shader_program,"color");
3818 glUniform4fv(colloc, 1, colorv);
3819
3820 //qDebug() << "triuni" << matloc << colloc;
3821
3822 glDrawArrays(GL_TRIANGLES, 0, 3);
3823
3824 pf[0] = x2; pf[1] = y2; pf[2] = x1; pf[3] = y2; pf[4] = x1; pf[5] = y1;
3825 glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), pf);
3826 glEnableVertexAttribArray(pos);
3827
3828 glDrawArrays(GL_TRIANGLES, 0, 3);
3829
3830 #else
3831 glColor3ub(water.Red(), water.Green(), water.Blue());
3832 glBegin( GL_QUADS );
3833 glVertex2i( x1, y1 );
3834 glVertex2i( x2, y1 );
3835 glVertex2i( x2, y2 );
3836 glVertex2i( x1, y2 );
3837 glEnd();
3838 #endif
3839 }
3840 }
3841
3842 m_pParentCanvas->pWorldBackgroundChart->RenderViewOnDC( dc, vp );
3843 }
3844
3845 /* these are the overlay objects which move with the charts and
3846 are not frequently updated (not ships etc..)
3847
3848 many overlay objects are fixed to a geographical location or
3849 grounded as opposed to the floating overlay objects. */
DrawGroundedOverlayObjects(ocpnDC & dc,ViewPort & vp)3850 void glChartCanvas::DrawGroundedOverlayObjects(ocpnDC &dc, ViewPort &vp)
3851 {
3852 m_pParentCanvas->RenderAllChartOutlines( dc, vp );
3853
3854 DrawStaticRoutesTracksAndWaypoints( vp );
3855
3856 DisableClipRegion();
3857 }
3858
3859
DrawGLTidesInBBox(ocpnDC & dc,LLBBox & BBox)3860 void glChartCanvas::DrawGLTidesInBBox(ocpnDC& dc, LLBBox& BBox)
3861 {
3862 // At small scale, we render the Tide icon as a texture for best performance
3863 if( m_pParentCanvas->GetVP().chart_scale > 500000 ) {
3864
3865 // Prepare the texture if necessary
3866
3867 if(!m_tideTex){
3868 wxBitmap bmp = m_pParentCanvas->GetTideBitmap();
3869 if(!bmp.Ok())
3870 return;
3871
3872 wxImage image = bmp.ConvertToImage();
3873 int w = image.GetWidth(), h = image.GetHeight();
3874
3875 int tex_w, tex_h;
3876 if(g_texture_rectangle_format == GL_TEXTURE_2D)
3877 tex_w = w, tex_h = h;
3878 else
3879 tex_w = NextPow2(w), tex_h = NextPow2(h);
3880
3881 m_tideTexWidth = tex_w;
3882 m_tideTexHeight = tex_h;
3883
3884 unsigned char *d = image.GetData();
3885 unsigned char *a = image.GetAlpha();
3886
3887 unsigned char mr, mg, mb;
3888 if (!a)
3889 image.GetOrFindMaskColour( &mr, &mg, &mb );
3890
3891 unsigned char *e = new unsigned char[4 * w * h];
3892 if(e && d){
3893 for( int y = 0; y < h; y++ )
3894 for( int x = 0; x < w; x++ ) {
3895 unsigned char r, g, b;
3896 int off = ( y * w + x );
3897 r = d[off * 3 + 0];
3898 g = d[off * 3 + 1];
3899 b = d[off * 3 + 2];
3900
3901 e[off * 4 + 0] = r;
3902 e[off * 4 + 1] = g;
3903 e[off * 4 + 2] = b;
3904
3905 e[off * 4 + 3] =
3906 a ? a[off] : ( ( r == mr ) && ( g == mg ) && ( b == mb ) ? 0 : 255 );
3907 }
3908 }
3909
3910
3911 glGenTextures( 1, &m_tideTex );
3912
3913 glBindTexture(GL_TEXTURE_2D, m_tideTex);
3914 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3915 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3916
3917 if(g_texture_rectangle_format == GL_TEXTURE_2D)
3918 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, e );
3919 else {
3920 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, tex_w, tex_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0 );
3921 glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, e );
3922 }
3923
3924 delete [] e;
3925 }
3926
3927 // Texture is ready
3928
3929 glBindTexture( GL_TEXTURE_2D, m_tideTex);
3930 glEnable( GL_TEXTURE_2D );
3931 glEnable(GL_BLEND);
3932
3933 #ifndef USE_ANDROID_GLES2
3934 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
3935
3936 for( int i = 1; i < ptcmgr->Get_max_IDX() + 1; i++ ) {
3937 const IDX_entry *pIDX = ptcmgr->GetIDX_entry( i );
3938
3939 char type = pIDX->IDX_type; // Entry "TCtcIUu" identifier
3940 if( ( type == 't' ) || ( type == 'T' ) ) // only Tides
3941 {
3942 double lon = pIDX->IDX_lon;
3943 double lat = pIDX->IDX_lat;
3944
3945 if( BBox.Contains( lat, lon ) ) {
3946 wxPoint r;
3947 m_pParentCanvas->GetCanvasPointPix( lat, lon, &r );
3948
3949 float xp = r.x;
3950 float yp = r.y;
3951
3952 double scale = 1.0;
3953 #ifdef __OCPN__ANDROID__
3954 scale *= getAndroidDisplayDensity();
3955 #endif
3956 double width2 = scale * m_tideTexWidth/2;
3957 double height2 = scale * m_tideTexHeight/2;
3958
3959 glBegin( GL_QUADS );
3960 glTexCoord2f( 0, 0 ); glVertex2f( xp - width2, yp - height2 );
3961 glTexCoord2f( 0, 1 ); glVertex2f( xp - width2, yp + height2 );
3962 glTexCoord2f( 1, 1 ); glVertex2f( xp + width2, yp + height2 );
3963 glTexCoord2f( 1, 0 ); glVertex2f( xp + width2, yp - height2 );
3964 glEnd();
3965 }
3966 } // type 'T"
3967 } //loop
3968 #else
3969 for( int i = 1; i < ptcmgr->Get_max_IDX() + 1; i++ ) {
3970 const IDX_entry *pIDX = ptcmgr->GetIDX_entry( i );
3971
3972 char type = pIDX->IDX_type; // Entry "TCtcIUu" identifier
3973 if( ( type == 't' ) || ( type == 'T' ) ) // only Tides
3974 {
3975 double lon = pIDX->IDX_lon;
3976 double lat = pIDX->IDX_lat;
3977
3978 if( BBox.Contains( lat, lon ) ) {
3979 wxPoint r;
3980 m_pParentCanvas->GetCanvasPointPix( lat, lon, &r );
3981
3982 float xp = r.x;
3983 float yp = r.y;
3984
3985 double scale = 1.0;
3986 scale *= getAndroidDisplayDensity();
3987 double width2 = scale * m_tideTexWidth/2;
3988 double height2 = scale * m_tideTexHeight/2;
3989
3990 float coords[8];
3991 float uv[8];
3992
3993 //normal uv
3994 uv[0] = 0; uv[1] = 0; uv[2] = 0; uv[3] = 1;
3995 uv[4] = 1; uv[5] = 1; uv[6] = 1; uv[7] = 0;
3996
3997 coords[0] = xp - width2; coords[1] = yp - height2; coords[2] = xp - width2; coords[3] = yp + height2;
3998 coords[4] = xp + width2; coords[5] = yp + height2; coords[6] = xp + width2; coords[7] = yp - height2;
3999
4000 RenderTextures(coords, uv, 4, m_pParentCanvas->GetpVP());
4001 }
4002 } // type 'T"
4003 } //loop
4004
4005
4006 #endif
4007
4008 glDisable( GL_TEXTURE_2D );
4009 glDisable(GL_BLEND);
4010 glBindTexture( GL_TEXTURE_2D, 0);
4011 }
4012 else
4013 m_pParentCanvas->DrawAllTidesInBBox( dc, BBox );
4014 }
4015
DrawGLCurrentsInBBox(ocpnDC & dc,LLBBox & BBox)4016 void glChartCanvas::DrawGLCurrentsInBBox(ocpnDC& dc, LLBBox& BBox)
4017 {
4018 m_pParentCanvas->DrawAllCurrentsInBBox(dc, BBox);
4019 }
4020
4021
SetColorScheme(ColorScheme cs)4022 void glChartCanvas::SetColorScheme(ColorScheme cs)
4023 {
4024 if(!m_bsetup)
4025 return;
4026
4027 glDeleteTextures(1, &m_tideTex);
4028 glDeleteTextures(1, &m_currentTex);
4029 m_tideTex = 0;
4030 m_currentTex = 0;
4031 ownship_color = -1;
4032
4033 }
4034
RenderGLAlertMessage()4035 void glChartCanvas::RenderGLAlertMessage()
4036 {
4037 if(!m_pParentCanvas->GetAlertString().IsEmpty())
4038 {
4039 wxString msg = m_pParentCanvas->GetAlertString();
4040
4041 wxFont *pfont = GetOCPNScaledFont(_("Dialog"));
4042 m_gldc.SetFont( *pfont);
4043
4044 int w, h;
4045 wxScreenDC sdc;
4046 sdc.GetTextExtent(msg, &w, &h, NULL, NULL, pfont);
4047
4048 h += 2;
4049 w += 4;
4050 int yp = m_pParentCanvas->VPoint.pix_height - GetChartbarHeight() - h - (h/4);
4051
4052 wxRect sbr = m_pParentCanvas->GetScaleBarRect();
4053 int xp = sbr.x+sbr.width + 5;
4054
4055 wxPen ppPen1( GetGlobalColor( _T ( "UBLCK" ) ), 1, wxPENSTYLE_SOLID );
4056 m_gldc.SetPen( ppPen1 );
4057 m_gldc.SetBrush( wxBrush( GetGlobalColor( _T ( "YELO1" ) ) ) );
4058
4059 m_gldc.DrawRectangle( xp, yp, w, h );
4060
4061 m_gldc.DrawText( msg, xp, yp );
4062
4063 }
4064 }
4065
4066 unsigned long quiltHash;
4067 int refChartIndex;
4068
4069
4070 int n_render;
Render()4071 void glChartCanvas::Render()
4072 {
4073 if( !m_bsetup || !m_pParentCanvas->m_pQuilt ||
4074 ( m_pParentCanvas->VPoint.b_quilt && !m_pParentCanvas->m_pQuilt ) ||
4075 ( !m_pParentCanvas->VPoint.b_quilt && !m_pParentCanvas->m_singleChart ) ) {
4076 #ifdef __WXGTK__ // for some reason in gtk, a swap is needed here to get an initial screen update
4077 SwapBuffers();
4078 #endif
4079 return;
4080 }
4081
4082 #ifdef USE_ANDROID_GLES2
4083
4084 OCPNStopWatch sw;
4085
4086 if(m_inFade)
4087 return;
4088
4089 if(m_binPinch)
4090 return;
4091
4092 //qDebug() << "Render" << m_pParentCanvas->m_canvasIndex << GetPosition().x << GetSize().x << m_pParentCanvas->GetPosition().x << m_pcontext;
4093
4094 //if(m_pParentCanvas->m_canvasIndex == 0) return;
4095
4096 // Do any setup required...
4097 loadShaders(0/*m_pParentCanvas->m_canvasIndex*/);
4098 configureShaders( m_pParentCanvas->VPoint);
4099
4100 bool recompose = false;
4101 if(m_pParentCanvas->VPoint.b_quilt && m_pParentCanvas->m_pQuilt && !m_pParentCanvas->m_pQuilt->IsComposed()){
4102 if(m_pParentCanvas->VPoint.IsValid()){
4103 m_pParentCanvas->m_pQuilt->Compose(m_pParentCanvas->VPoint);
4104 m_pParentCanvas->UpdateCanvasControlBar();
4105 recompose = true;
4106 }
4107 else
4108 return;
4109 }
4110
4111 // Check to see if the Compose() call forced a SENC build.
4112 // If so, zoom the canvas just slightly to force a deferred redraw of the full screen.
4113 if(sw.GetTime() > 2000){ // long enough to detect SENC build.
4114 m_pParentCanvas->ZoomCanvas( 1.0001, false );
4115 }
4116
4117 //qDebug() << "RenderTime1" << sw.GetTime();
4118
4119 s_tess_vertex_idx = 0;
4120 quiltHash = m_pParentCanvas->m_pQuilt->GetXStackHash();
4121 refChartIndex = m_pParentCanvas->m_pQuilt->GetRefChartdbIndex();
4122
4123 #endif
4124
4125 m_last_render_time = wxDateTime::Now().GetTicks();
4126
4127 // we don't care about jobs that are now off screen
4128 // clear out and it will be repopulated during render
4129 if(g_GLOptions.m_bTextureCompression &&
4130 !g_GLOptions.m_bTextureCompressionCaching)
4131 g_glTextureManager->ClearJobList();
4132
4133 if(b_timeGL && g_bShowFPS){
4134 if(n_render % 10){
4135 glFinish();
4136 g_glstopwatch.Start();
4137 }
4138 }
4139 wxPaintDC( this );
4140
4141 ViewPort VPoint = m_pParentCanvas->VPoint;
4142 ocpnDC gldc( *this );
4143
4144 int gl_width, gl_height;
4145 m_pParentCanvas->GetClientSize( &gl_width, &gl_height );
4146
4147 #ifdef __WXOSX__
4148 gl_height = m_pParentCanvas->GetClientSize().y;
4149 #endif
4150
4151 OCPNRegion screen_region(wxRect(0, 0, VPoint.pix_width, VPoint.pix_height));
4152
4153 glViewport( 0, 0, (GLint) gl_width, (GLint) gl_height );
4154
4155 #ifndef USE_ANDROID_GLES2
4156 glMatrixMode (GL_PROJECTION);
4157 glLoadIdentity();
4158
4159 glOrtho( 0, (GLint) gl_width, (GLint) gl_height, 0, -1, 1 );
4160 glMatrixMode(GL_MODELVIEW);
4161 glLoadIdentity();
4162 #endif
4163 if( s_b_useStencil ) {
4164 glEnable( GL_STENCIL_TEST );
4165 glStencilMask( 0xff );
4166 glClear( GL_STENCIL_BUFFER_BIT );
4167 glDisable( GL_STENCIL_TEST );
4168 }
4169
4170 // set opengl settings that don't normally change
4171 // this should be able to go in SetupOpenGL, but it's
4172 // safer here incase a plugin mangles these
4173 if( g_GLOptions.m_GLLineSmoothing )
4174 glHint( GL_LINE_SMOOTH_HINT, GL_NICEST );
4175 if( g_GLOptions.m_GLPolygonSmoothing )
4176 glHint( GL_POLYGON_SMOOTH_HINT, GL_NICEST );
4177 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
4178
4179 // Delete any textures known to the GPU that
4180 // belong to charts which will not be used in this render
4181 // This is done chart-by-chart...later we will scrub for unused textures
4182 // that belong to charts which ARE used in this render, if we need to....
4183
4184 g_glTextureManager->TextureCrunch(0.8);
4185
4186 // If we plan to post process the display, don't use accelerated panning
4187 double scale_factor = VPoint.ref_scale/VPoint.chart_scale;
4188
4189 m_bfogit = m_benableFog && g_fog_overzoom && (scale_factor > g_overzoom_emphasis_base) && VPoint.b_quilt;
4190 bool scale_it = m_benableVScale && g_oz_vector_scale && (scale_factor > g_overzoom_emphasis_base) && VPoint.b_quilt;
4191
4192 bool bpost_hilite = !m_pParentCanvas->m_pQuilt->GetHiliteRegion( ).Empty();
4193 bool useFBO = false;
4194 int sx = gl_width;
4195 int sy = gl_height;
4196
4197 // Try to use the framebuffer object's cache of the last frame
4198 // to accelerate drawing this frame (if overlapping)
4199 if(m_b_BuiltFBO && !m_bfogit && !scale_it && !bpost_hilite
4200 //&& VPoint.tilt == 0 // disabling fbo in tilt mode gives better quality but slower
4201 ) {
4202 // Is this viewpoint the same as the previously painted one?
4203 bool b_newview = true;
4204 bool b_full = false;
4205
4206 // If the view is the same we do no updates,
4207 // cached texture to the framebuffer
4208 if( m_cache_vp.view_scale_ppm == VPoint.view_scale_ppm
4209 && m_cache_vp.rotation == VPoint.rotation
4210 && m_cache_vp.clat == VPoint.clat
4211 && m_cache_vp.clon == VPoint.clon
4212 && m_cache_vp.IsValid()
4213 && m_cache_vp.pix_height == VPoint.pix_height
4214 && m_cache_current_ch == m_pParentCanvas->m_singleChart ) {
4215 b_newview = false;
4216 }
4217
4218 #ifdef USE_ANDROID_GLES2
4219 if(recompose)
4220 b_newview = true;
4221
4222 if(m_bforcefull){
4223 b_newview = true;
4224 b_full = true;
4225 }
4226
4227
4228 // If no charts are to be rendered, we need to refresh the entire display
4229 // This fixes a problem with routes/tracks/marks rendering on pans at very small scale.
4230 // It is a workaround, so finding root cause should be considered a TODO
4231
4232 if(VPoint.b_quilt){
4233 ChartBase *chart = m_pParentCanvas->m_pQuilt->GetFirstChart();
4234 if(!chart)
4235 b_full = true;
4236 }
4237
4238 #endif
4239
4240 if( b_newview ) {
4241
4242 bool busy = false;
4243 if(VPoint.b_quilt && m_pParentCanvas->m_pQuilt->IsQuiltVector() &&
4244 ( m_cache_vp.view_scale_ppm != VPoint.view_scale_ppm || m_cache_vp.rotation != VPoint.rotation))
4245 {
4246 OCPNPlatform::ShowBusySpinner();
4247 busy = true;
4248 }
4249
4250 float dx = 0;
4251 float dy = 0;
4252
4253 bool accelerated_pan = false;
4254 if( g_GLOptions.m_bUseAcceleratedPanning && m_cache_vp.IsValid()
4255 && ( VPoint.m_projection_type == PROJECTION_MERCATOR
4256 || VPoint.m_projection_type == PROJECTION_EQUIRECTANGULAR )
4257 && m_cache_vp.pix_height == VPoint.pix_height )
4258 {
4259 wxPoint2DDouble c_old = VPoint.GetDoublePixFromLL( VPoint.clat, VPoint.clon );
4260 wxPoint2DDouble c_new = m_cache_vp.GetDoublePixFromLL( VPoint.clat, VPoint.clon );
4261
4262 dy = wxRound(c_new.m_y - c_old.m_y);
4263 dx = wxRound(c_new.m_x - c_old.m_x);
4264
4265 // The math below using the previous frame's texture does not really
4266 // work for sub-pixel pans.
4267 // TODO is to rethink this.
4268 // Meanwhile, require the accelerated pans to be whole pixel multiples only.
4269 // This is not as bad as it sounds. Keyboard and mouse pans are whole_pixel moves.
4270 // However, autofollow at large scale is certainly not.
4271
4272 double deltax = c_new.m_x - c_old.m_x;
4273 double deltay = c_new.m_y - c_old.m_y;
4274
4275 bool b_whole_pixel = true;
4276 if( ( fabs( deltax - dx ) > 1e-2 ) || ( fabs( deltay - dy ) > 1e-2 ) )
4277 b_whole_pixel = false;
4278
4279 accelerated_pan = b_whole_pixel && abs(dx) < m_cache_tex_x && abs(dy) < m_cache_tex_y;
4280 }
4281
4282 // do we allow accelerated panning? can we perform it here?
4283 #ifndef USE_ANDROID_GLES2
4284 // enable rendering to texture in framebuffer object
4285 ( s_glBindFramebuffer )( GL_FRAMEBUFFER_EXT, m_fb0 );
4286
4287 if(accelerated_pan) {
4288 if((dx != 0) || (dy != 0)){ // Anything to do?
4289 m_cache_page = !m_cache_page; /* page flip */
4290
4291 /* perform accelerated pan rendering to the new framebuffer */
4292 ( s_glFramebufferTexture2D )
4293 ( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
4294 g_texture_rectangle_format, m_cache_tex[m_cache_page], 0 );
4295
4296 //calculate the new regions to render
4297 // add an extra pixel avoid coorindate rounding issues
4298 OCPNRegion update_region;
4299
4300 if( dy > 0 && dy < VPoint.pix_height)
4301 update_region.Union(wxRect( 0, VPoint.pix_height - dy, VPoint.pix_width, dy ));
4302 else if(dy < 0)
4303 update_region.Union(wxRect( 0, 0, VPoint.pix_width, -dy ));
4304
4305 if( dx > 0 && dx < VPoint.pix_width )
4306 update_region.Union(wxRect( VPoint.pix_width - dx, 0, dx, VPoint.pix_height ));
4307 else if (dx < 0)
4308 update_region.Union(wxRect( 0, 0, -dx, VPoint.pix_height ));
4309
4310 RenderCharts(m_gldc, update_region);
4311
4312 // using the old framebuffer
4313 glBindTexture( g_texture_rectangle_format, m_cache_tex[!m_cache_page] );
4314 glEnable( g_texture_rectangle_format );
4315 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
4316
4317 // Render the reuseable portion of the cached texture
4318 // Render the cached texture as quad to FBO(m_blit_tex) with offsets
4319 int x1, x2, y1, y2;
4320
4321 int ow = VPoint.pix_width - abs( dx );
4322 int oh = VPoint.pix_height - abs( dy );
4323 if( dx > 0 )
4324 x1 = dx, x2 = 0;
4325 else
4326 x1 = 0, x2 = -dx;
4327
4328 if( dy > 0 )
4329 y1 = dy, y2 = 0;
4330 else
4331 y1 = 0, y2 = -dy;
4332
4333 // normalize to texture coordinates range from 0 to 1
4334 float tx1 = x1, tx2 = x1 + ow, ty1 = sy - y1, ty2 = sy - (y1 + oh);
4335 if( GL_TEXTURE_RECTANGLE_ARB != g_texture_rectangle_format )
4336 tx1 /= sx, tx2 /= sx, ty1 /= sy, ty2 /= sy;
4337
4338 glBegin( GL_QUADS );
4339 glTexCoord2f( tx1, ty1 ); glVertex2f( x2, y2 );
4340 glTexCoord2f( tx2, ty1 ); glVertex2f( x2 + ow, y2 );
4341 glTexCoord2f( tx2, ty2 ); glVertex2f( x2 + ow, y2 + oh );
4342 glTexCoord2f( tx1, ty2 ); glVertex2f( x2, y2 + oh );
4343 glEnd();
4344
4345 // Done with cached texture "blit"
4346 glDisable( g_texture_rectangle_format );
4347 }
4348
4349 } else { // must redraw the entire screen
4350 ( s_glFramebufferTexture2D )( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
4351 g_texture_rectangle_format,
4352 m_cache_tex[m_cache_page], 0 );
4353
4354 m_fbo_offsetx = 0;
4355 m_fbo_offsety = 0;
4356 m_fbo_swidth = sx;
4357 m_fbo_sheight = sy;
4358 wxRect rect(m_fbo_offsetx, m_fbo_offsety, (GLint) sx, (GLint) sy);
4359 RenderCharts(m_gldc, screen_region);
4360 }
4361
4362 // Disable Render to FBO
4363 ( s_glBindFramebuffer )( GL_FRAMEBUFFER_EXT, 0 );
4364
4365
4366 #else // GLES2
4367 // enable rendering to texture in framebuffer object
4368 glBindFramebuffer( GL_FRAMEBUFFER, m_fb0 );
4369
4370 if(VPoint.chart_scale < 5000)
4371 b_full = true;
4372
4373 if(VPoint.chart_scale > 5e7)
4374 b_full = true;
4375
4376 if(b_full)
4377 accelerated_pan = false;
4378
4379 if(accelerated_pan) {
4380 //qDebug() << "AccPan";
4381 if((dx != 0) || (dy != 0)){ // Anything to do?
4382 m_cache_page = !m_cache_page; /* page flip */
4383
4384 /* perform accelerated pan rendering to the current target texture */
4385 glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, g_texture_rectangle_format, m_cache_tex[m_cache_page], 0 );
4386
4387 //calculate the new regions to render
4388 // add extra pixels to avoid coordindate rounding issues at large scale
4389 OCPNRegion update_region;
4390
4391 int fluff = 0;
4392
4393 // Avoid rendering artifacts caused by Multi Sampling (MSAA)
4394 if(VPoint.chart_scale < 10000)
4395 fluff = 8;
4396
4397 if( dy > 0 && dy < VPoint.pix_height)
4398 update_region.Union(wxRect( 0, VPoint.pix_height - (dy + fluff), VPoint.pix_width, dy+fluff ));
4399 else if(dy < 0)
4400 update_region.Union(wxRect( 0, 0, VPoint.pix_width, -dy + fluff ));
4401
4402 if( dx > 0 && dx < VPoint.pix_width )
4403 update_region.Union(wxRect( VPoint.pix_width - (dx+fluff), 0, dx+fluff, VPoint.pix_height ));
4404 else if (dx < 0)
4405 update_region.Union(wxRect( 0, 0, -dx + fluff, VPoint.pix_height ));
4406
4407 wxColour color = GetGlobalColor( _T ( "NODTA" ) );
4408 glClearColor( color.Red() / 256., color.Green()/256., color.Blue()/256., 1.0 );
4409 //glClearColor(1.0f, 0.0f, 0.f, 1.0f);
4410 glClear(GL_COLOR_BUFFER_BIT);
4411
4412 // Render the new content
4413 //OCPNStopWatch swr1;
4414 // RenderCharts(gldc, update_region);
4415 glViewport( 0, 0, (GLint) sx, (GLint) sy );
4416
4417
4418 // using the old framebuffer
4419 glBindTexture( g_texture_rectangle_format, m_cache_tex[!m_cache_page] );
4420 glEnable( g_texture_rectangle_format );
4421
4422 // Render the reuseable portion of the cached texture
4423 // Render the cached texture as quad to FBO(m_blit_tex) with offsets
4424 float x1, x2, y1, y2;
4425
4426 if( dx > 0 )
4427 x1 = dx, x2 = 0;
4428 else
4429 x1 = 0, x2 = -dx;
4430
4431 if( dy > 0 )
4432 y1 = dy, y2 = 0;
4433 else
4434 y1 = 0, y2 = -dy;
4435
4436 // normalize to texture coordinates range from 0 to 1
4437 float tx1, tx2, ty1, ty2;
4438
4439
4440 float xcor = 0;
4441 float ycor = 0;
4442
4443 tx1 = 0; tx2 = sx/(float)m_cache_tex_x; ty1 = 0 ; ty2 = sy/(float)m_cache_tex_y;
4444
4445 float coords[8];
4446 float uv[8];
4447
4448 //normal uv
4449 uv[0] = tx1; uv[1] = ty1; uv[2] = tx2; uv[3] = ty1;
4450 uv[4] = tx2; uv[5] = ty2; uv[6] = tx1; uv[7] = ty2;
4451
4452
4453 //<<<<<<< HEAD
4454
4455 coords[0] = -dx; coords[1] = dy; coords[2] = -dx + sx; coords[3] = dy;
4456 coords[4] = -dx + sx; coords[5] = dy + sy; coords[6] = -dx; coords[7] = dy+sy;
4457
4458
4459 // qDebug() << coords[0] << coords[1] << coords[2] << coords[3];
4460 // qDebug() << coords[4] << coords[5] << coords[6] << coords[7];
4461 // qDebug() << uv[0] << uv[1] << uv[2] << uv[3];
4462 // qDebug() << uv[4] << uv[5] << uv[6] << uv[7];
4463
4464
4465 //build_texture_shaders();
4466 glUseProgram( FBO_texture_2D_shader_program );
4467
4468 // Get pointers to the attributes in the program.
4469 GLint mPosAttrib = glGetAttribLocation( FBO_texture_2D_shader_program, "aPos" );
4470 GLint mUvAttrib = glGetAttribLocation( FBO_texture_2D_shader_program, "aUV" );
4471
4472 // Set up the texture sampler to texture unit 0
4473 GLint texUni = glGetUniformLocation( FBO_texture_2D_shader_program, "uTex" );
4474 glUniform1i( texUni, 0 );
4475
4476 // Disable VBO's (vertex buffer objects) for attributes.
4477 glBindBuffer( GL_ARRAY_BUFFER, 0 );
4478 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
4479
4480 // Set the attribute mPosAttrib with the vertices in the screen coordinates...
4481 glVertexAttribPointer( mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, coords );
4482 // ... and enable it.
4483 glEnableVertexAttribArray( mPosAttrib );
4484
4485 // Set the attribute mUvAttrib with the vertices in the GL coordinates...
4486 glVertexAttribPointer( mUvAttrib, 2, GL_FLOAT, GL_FALSE, 0, uv );
4487 // ... and enable it.
4488 glEnableVertexAttribArray( mUvAttrib );
4489
4490
4491 GLint matloc = glGetUniformLocation(FBO_texture_2D_shader_program,"MVMatrix");
4492
4493 mat4x4 m, mvp;
4494
4495 mat4x4_identity(m);
4496 mat4x4_scale_aniso(mvp, m, 2.0 / sx, 2.0 / sy , 1.0);
4497 mat4x4_translate_in_place(mvp, -sx/2, -sy/2 , 0);
4498
4499 glUniformMatrix4fv( matloc, 1, GL_FALSE, (const float *)mvp);
4500
4501 // Select the active texture unit.
4502 glActiveTexture( GL_TEXTURE0 );
4503 // Bind our texture to the texturing unit target.
4504 glBindTexture( g_texture_rectangle_format, m_cache_tex[!m_cache_page] );
4505
4506 // Perform the actual drawing.
4507
4508 float co1[8];
4509 co1[0] = coords[0];
4510 co1[1] = coords[1];
4511 co1[2] = coords[2];
4512 co1[3] = coords[3];
4513 co1[4] = coords[6];
4514 co1[5] = coords[7];
4515 co1[6] = coords[4];
4516 co1[7] = coords[5];
4517
4518 float tco1[8];
4519 tco1[0] = uv[0];
4520 tco1[1] = uv[1];
4521 tco1[2] = uv[2];
4522 tco1[3] = uv[3];
4523 tco1[4] = uv[6];
4524 tco1[5] = uv[7];
4525 tco1[6] = uv[4];
4526 tco1[7] = uv[5];
4527
4528 glVertexAttribPointer( mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, co1 );
4529 glVertexAttribPointer( mUvAttrib, 2, GL_FLOAT, GL_FALSE, 0, tco1 );
4530
4531 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
4532
4533 //qDebug() << "RenderTime2" << sw.GetTime();
4534
4535
4536 // Render the new content
4537 RenderCharts(m_gldc, update_region);
4538 //qDebug() << "RenderTime3" << sw.GetTime();
4539
4540
4541 //qDebug() << "RenderTime4" << sw.GetTime();
4542
4543 glDisable( g_texture_rectangle_format );
4544 }
4545
4546 } // accelerated pan
4547
4548 else { // must redraw the entire screen
4549 //qDebug() << "Fullpage";
4550 glFramebufferTexture2D( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0, g_texture_rectangle_format, m_cache_tex[!m_cache_page], 0 );
4551
4552 m_fbo_offsetx = 0;
4553 m_fbo_offsety = 0;
4554 m_fbo_swidth = sx;
4555 m_fbo_sheight = sy;
4556
4557 // Do not need to clear screen, especially annoying on pinch zoom
4558 //wxColour color = GetGlobalColor( _T ( "NODTA" ) );
4559 //glClearColor( color.Red() / 256., color.Green() / 256. , color.Blue()/ 256. ,1.0 );
4560 //glClear(GL_COLOR_BUFFER_BIT);
4561
4562 RenderCharts(m_gldc, screen_region);
4563
4564 //qDebug() << "RenderTimeFULL" << sw.GetTime();
4565
4566 m_cache_page = !m_cache_page; /* page flip */
4567
4568
4569 } // full page render
4570
4571 // Disable Render to FBO
4572 glBindFramebuffer( GL_FRAMEBUFFER, 0 );
4573
4574 #endif //gles2 for accpan
4575
4576
4577
4578
4579 if(busy)
4580 OCPNPlatform::HideBusySpinner();
4581
4582 } // newview
4583
4584 useFBO = true;
4585 }
4586
4587 #ifndef __OCPN__ANDROID__
4588 if(VPoint.tilt) {
4589 glMatrixMode (GL_PROJECTION);
4590 glLoadIdentity();
4591
4592 gluPerspective(2*180/PI*atan2((double)gl_height, (double)gl_width), (GLfloat) gl_width/(GLfloat) gl_height, 1, gl_width);
4593
4594 glMatrixMode(GL_MODELVIEW);
4595 glLoadIdentity();
4596
4597 glScalef(1, -1, 1);
4598 glTranslatef(-gl_width/2, -gl_height/2, -gl_width/2);
4599 glRotated(VPoint.tilt*180/PI, 1, 0, 0);
4600
4601 glGetIntegerv(GL_VIEWPORT, viewport);
4602 glGetDoublev(GL_PROJECTION_MATRIX, projmatrix);
4603 glGetDoublev(GL_MODELVIEW_MATRIX, mvmatrix);
4604 }
4605 #endif
4606
4607 if(useFBO) {
4608 // Render the cached texture as quad to screen
4609 glBindTexture( g_texture_rectangle_format, m_cache_tex[m_cache_page]);
4610 glEnable( g_texture_rectangle_format );
4611
4612 float tx, ty, tx0, ty0, divx, divy;
4613
4614 // Normalize, or not?
4615 if( GL_TEXTURE_RECTANGLE_ARB == g_texture_rectangle_format ){
4616 divx = divy = 1.0f;
4617 }
4618 else{
4619 divx = m_cache_tex_x;
4620 divy = m_cache_tex_y;
4621 }
4622
4623 tx0 = m_fbo_offsetx/divx;
4624 ty0 = m_fbo_offsety/divy;
4625 tx = (m_fbo_offsetx + m_fbo_swidth)/divx;
4626 ty = (m_fbo_offsety + m_fbo_sheight)/divy;
4627
4628 #ifndef USE_ANDROID_GLES2
4629 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
4630 glBegin( GL_QUADS );
4631 glTexCoord2f( tx0, ty ); glVertex2f( 0, 0 );
4632 glTexCoord2f( tx, ty ); glVertex2f( sx, 0 );
4633 glTexCoord2f( tx, ty0 ); glVertex2f( sx, sy );
4634 glTexCoord2f( tx0, ty0 ); glVertex2f( 0, sy );
4635 glEnd();
4636 #else
4637 float coords[8];
4638 float uv[8];
4639
4640 //normal uv
4641 uv[0] = tx0; uv[1] = ty; uv[2] = tx; uv[3] = ty;
4642 uv[4] = tx; uv[5] = ty0; uv[6] = tx0; uv[7] = ty0;
4643
4644 // pixels
4645 coords[0] = 0; coords[1] = 0; coords[2] = sx; coords[3] = 0;
4646 coords[4] = sx; coords[5] = sy; coords[6] = 0; coords[7] = sy;
4647
4648 if(!m_inFade){
4649 RenderTextures(coords, uv, 4, m_pParentCanvas->GetpVP());
4650 }
4651 else
4652 qDebug() << "skip FBO update for inFade";
4653
4654 #endif
4655
4656
4657 glDisable( g_texture_rectangle_format );
4658
4659 m_cache_vp = VPoint;
4660 m_cache_current_ch = m_pParentCanvas->m_singleChart;
4661
4662 if(VPoint.b_quilt)
4663 m_pParentCanvas->m_pQuilt->SetRenderedVP( VPoint );
4664
4665 } else // useFBO
4666 {
4667 //qDebug() << "RenderCharts No FBO";
4668 RenderCharts(m_gldc, screen_region);
4669 }
4670
4671 // Done with base charts.
4672 // Now the overlays
4673
4674 RenderS57TextOverlay( VPoint );
4675 RenderMBTilesOverlay( VPoint );
4676
4677
4678 // Render static overlay objects
4679 for(OCPNRegionIterator upd ( screen_region ); upd.HaveRects(); upd.NextRect()) {
4680 LLRegion region = VPoint.GetLLRegion(upd.GetRect());
4681 ViewPort cvp = ClippedViewport(VPoint, region);
4682 DrawGroundedOverlayObjects(gldc, cvp);
4683 }
4684
4685
4686 if( m_pParentCanvas->m_bShowTide || m_pParentCanvas->m_bShowCurrent ){
4687 LLRegion screenLLRegion = VPoint.GetLLRegion( screen_region );
4688 LLBBox screenBox = screenLLRegion.GetBox();
4689 // Enlarge the box a bit
4690 screenBox.EnLarge(screenBox.GetLonRange()* 0.05);
4691
4692 // update the tide/current select points, if necessary
4693 if( m_pParentCanvas->m_bShowTide ) {
4694 m_pParentCanvas->RebuildTideSelectList(screenBox); // full screen
4695 DrawGLTidesInBBox( gldc, VPoint.GetBBox() );
4696 }
4697
4698 if( m_pParentCanvas->m_bShowCurrent ){
4699 m_pParentCanvas->RebuildCurrentSelectList(screenBox);
4700 DrawGLCurrentsInBBox( gldc, VPoint.GetBBox() );
4701 }
4702 }
4703
4704
4705 // If multi-canvas, indicate which canvas has keyboard focus
4706 // by drawing a simple blue bar at the top.
4707 if(g_canvasConfig != 0){ // multi-canvas?
4708 if( m_pParentCanvas == wxWindow::FindFocus()){
4709 g_focusCanvas = m_pParentCanvas;
4710
4711 wxColour colour = GetGlobalColor(_T("BLUE4"));
4712 wxPen ppBlue ( colour, 1 );
4713 wxBrush ppBrush( colour );
4714 gldc.SetPen( ppBlue );
4715 gldc.SetBrush( ppBrush );
4716 int xw = m_pParentCanvas->GetClientSize().x;
4717 float rect_pix = m_pParentCanvas->m_focus_indicator_pix;
4718 wxPoint barPoints[4];
4719 barPoints[0].x = 0; barPoints[0].y = 0;
4720 barPoints[1].x = xw; barPoints[1].y = 0;
4721 barPoints[2].x = xw; barPoints[2].y = rect_pix;
4722 barPoints[3].x = 0; barPoints[3].y = rect_pix;
4723
4724 gldc.DrawPolygon( 4, barPoints, 0, 0, 1, 0);
4725 }
4726 }
4727
4728
4729 DrawDynamicRoutesTracksAndWaypoints( VPoint );
4730
4731 // Now draw all the objects which normally move around and are not
4732 // cached from the previous frame
4733 DrawFloatingOverlayObjects( m_gldc );
4734
4735 #ifndef USE_ANDROID_GLES2
4736 // from this point on don't use perspective
4737 if(VPoint.tilt) {
4738 glMatrixMode (GL_PROJECTION);
4739 glLoadIdentity();
4740
4741 glOrtho( 0, (GLint) gl_width, (GLint) gl_height, 0, -1, 1 );
4742 glMatrixMode(GL_MODELVIEW);
4743 glLoadIdentity();
4744 }
4745 #endif
4746
4747 DrawEmboss(m_pParentCanvas->EmbossDepthScale() );
4748 DrawEmboss(m_pParentCanvas->EmbossOverzoomIndicator( gldc ) );
4749
4750 if( m_pParentCanvas->m_pTrackRolloverWin )
4751 m_pParentCanvas->m_pTrackRolloverWin->Draw(gldc);
4752
4753 if( m_pParentCanvas->m_pRouteRolloverWin )
4754 m_pParentCanvas->m_pRouteRolloverWin->Draw(gldc);
4755
4756 if( m_pParentCanvas->m_pAISRolloverWin )
4757 m_pParentCanvas->m_pAISRolloverWin->Draw(gldc);
4758
4759 // On some platforms, the opengl context window is always on top of any standard DC windows,
4760 // so we need to draw the Chart Info Window and the Thumbnail as overlayed bmps.
4761
4762 #ifdef __WXOSX__
4763 if(m_pParentCanvas->m_pCIWin && m_pParentCanvas->m_pCIWin->IsShown()) {
4764 int x, y, width, height;
4765 m_pParentCanvas->m_pCIWin->GetClientSize( &width, &height );
4766 m_pParentCanvas->m_pCIWin->GetPosition( &x, &y );
4767 wxBitmap bmp(width, height, -1);
4768 wxMemoryDC dc(bmp);
4769 if(bmp.IsOk()){
4770 dc.SetBackground( wxBrush(GetGlobalColor( _T ( "UIBCK" ) ) ));
4771 dc.Clear();
4772
4773 dc.SetTextBackground( GetGlobalColor( _T ( "UIBCK" ) ) );
4774 dc.SetTextForeground( GetGlobalColor( _T ( "UITX1" ) ) );
4775
4776 int yt = 0;
4777 int xt = 0;
4778 wxString s = m_pParentCanvas->m_pCIWin->GetString();
4779 int h = m_pParentCanvas->m_pCIWin->GetCharHeight();
4780
4781 wxStringTokenizer tkz( s, _T("\n") );
4782 wxString token;
4783
4784 while(tkz.HasMoreTokens()) {
4785 token = tkz.GetNextToken();
4786 dc.DrawText(token, xt, yt);
4787 yt += h;
4788 }
4789 dc.SelectObject(wxNullBitmap);
4790
4791 m_gldc.DrawBitmap( bmp, x, y, false);
4792 }
4793 }
4794
4795 if( pthumbwin && pthumbwin->IsShown()) {
4796 int thumbx, thumby;
4797 pthumbwin->GetPosition( &thumbx, &thumby );
4798 if( pthumbwin->GetBitmap().IsOk())
4799 m_gldc.DrawBitmap( pthumbwin->GetBitmap(), thumbx, thumby, false);
4800 }
4801
4802 if(g_MainToolbar && g_MainToolbar->m_pRecoverwin ){
4803 int recoverx, recovery;
4804 g_MainToolbar->m_pRecoverwin->GetPosition( &recoverx, &recovery );
4805 if( g_MainToolbar->m_pRecoverwin->GetBitmap().IsOk())
4806 m_gldc.DrawBitmap( g_MainToolbar->m_pRecoverwin->GetBitmap(), recoverx, recovery, true);
4807 }
4808
4809
4810 #endif
4811 // render the chart bar
4812 if(g_bShowChartBar)
4813 DrawChartBar(m_gldc);
4814
4815 if (m_pParentCanvas->m_Compass)
4816 m_pParentCanvas->m_Compass->Paint(gldc);
4817
4818 RenderGLAlertMessage();
4819
4820 //quiting?
4821 if( g_bquiting )
4822 DrawQuiting();
4823 if( g_bcompression_wait)
4824 DrawCloseMessage( _("Waiting for raster chart compression thread exit."));
4825
4826
4827
4828 // Some older MSW OpenGL drivers are generally very unstable.
4829 // This helps...
4830
4831 if(g_b_needFinish)
4832 glFinish();
4833
4834 SwapBuffers();
4835 if(b_timeGL && g_bShowFPS){
4836 if(n_render % 10){
4837 glFinish();
4838
4839 double filter = .05;
4840
4841 // Simple low pass filter
4842 g_gl_ms_per_frame = g_gl_ms_per_frame * (1. - filter) + ((double)(g_glstopwatch.Time()) * filter);
4843 // if(g_gl_ms_per_frame > 0)
4844 // printf(" OpenGL frame time: %3.0f %3.0f\n", g_gl_ms_per_frame, 1000./ g_gl_ms_per_frame);
4845 }
4846 }
4847
4848 g_glTextureManager->TextureCrunch(0.8);
4849 g_glTextureManager->FactoryCrunch(0.6);
4850
4851 m_pParentCanvas->PaintCleanup();
4852 //OCPNPlatform::HideBusySpinner();
4853 m_bforcefull = false;
4854
4855 n_render++;
4856 }
4857
RenderS57TextOverlay(ViewPort & VPoint)4858 void glChartCanvas::RenderS57TextOverlay( ViewPort &VPoint)
4859 {
4860 // Render the decluttered Text overlay for quilted vector charts, except for CM93 Composite
4861 if( VPoint.b_quilt ) {
4862 if(m_pParentCanvas->m_pQuilt->IsQuiltVector() && ps52plib && ps52plib->GetShowS57Text()){
4863
4864 ChartBase *chart = m_pParentCanvas->m_pQuilt->GetRefChart();
4865 if(chart && (chart->GetChartType() != CHART_TYPE_CM93COMP)){
4866 // Clear the text Global declutter list
4867 if(chart){
4868 ChartPlugInWrapper *ChPI = dynamic_cast<ChartPlugInWrapper*>( chart );
4869 if(ChPI)
4870 ChPI->ClearPLIBTextList();
4871 else
4872 ps52plib->ClearTextList();
4873 }
4874
4875 // Grow the ViewPort a bit laterally, to minimize "jumping" of text elements at left side of screen
4876 ViewPort vpx = VPoint;
4877 vpx.BuildExpandedVP(VPoint.pix_width * 12 / 10, VPoint.pix_height);
4878
4879 OCPNRegion screen_region(wxRect(0, 0, VPoint.pix_width, VPoint.pix_height));
4880 RenderQuiltViewGLText( vpx, screen_region );
4881 }
4882 }
4883 }
4884 }
4885
RenderMBTilesOverlay(ViewPort & VPoint)4886 void glChartCanvas::RenderMBTilesOverlay( ViewPort &VPoint)
4887 {
4888 // Render MBTiles as overlay
4889 std::vector<int> stackIndexArray = m_pParentCanvas->m_pQuilt->GetExtendedStackIndexArray();
4890 unsigned int im = stackIndexArray.size();
4891 // XXX should
4892 // assert(!VPoint.b_quilt && im == 0)
4893 if( VPoint.b_quilt && im > 0 ) {
4894 bool regionVPBuilt = false;
4895 OCPNRegion screen_region;
4896 LLRegion screenLLRegion;
4897 LLBBox screenBox;
4898 ViewPort vp;
4899
4900 std::vector<int> tiles_to_show;
4901 for( unsigned int is = 0; is < im; is++ ) {
4902 const ChartTableEntry &cte = ChartData->GetChartTableEntry( stackIndexArray[is] );
4903 if(cte.GetChartType() == CHART_TYPE_MBTILES){
4904 if(m_pParentCanvas->IsTileOverlayIndexInNoShow(stackIndexArray[is])){
4905 // Turn off the piano highlite
4906 std::vector<int> piano_active_array_tiles = m_pParentCanvas->m_Piano->GetActiveKeyArray();
4907 bool bfound = false;
4908
4909 for( unsigned int i = 0; i < piano_active_array_tiles.size(); i++ ) {
4910 if( piano_active_array_tiles[i] == stackIndexArray[is] ){
4911 piano_active_array_tiles.erase(piano_active_array_tiles.begin() + i ); // erase it
4912 bfound = true;
4913 break;
4914 }
4915 }
4916
4917 if(bfound)
4918 m_pParentCanvas->m_Piano->SetActiveKeyArray( piano_active_array_tiles );
4919
4920 continue;
4921 }
4922
4923 tiles_to_show.push_back(stackIndexArray[is]);
4924 if(!regionVPBuilt){
4925 screen_region = OCPNRegion(wxRect(0, 0, VPoint.pix_width, VPoint.pix_height));
4926 screenLLRegion = VPoint.GetLLRegion( screen_region );
4927 screenBox = screenLLRegion.GetBox();
4928
4929 vp = VPoint;
4930 wxPoint p;
4931 p.x = VPoint.pix_width / 2; p.y = VPoint.pix_height / 2;
4932 VPoint.GetLLFromPix( p, &vp.clat, &vp.clon);
4933
4934 regionVPBuilt = true;
4935 }
4936 }
4937 }
4938
4939 // We need to show the tilesets in reverse order to have the largest scale on top
4940 for(std::vector<int>::reverse_iterator rit = tiles_to_show.rbegin();
4941 rit != tiles_to_show.rend(); ++rit) {
4942 ChartBase *chart = ChartData->OpenChartFromDBAndLock(*rit, FULL_INIT);
4943
4944 // Chart may have been prevented from initial loading due to size, or some other reason...
4945 if(chart == NULL)
4946 continue;
4947
4948 wxFileName tileFile(chart->GetFullPath());
4949 wxULongLong tileSize = tileFile.GetSize();
4950
4951 if(!ChartData->CheckAnyCanvasExclusiveTileGroup() || (tileSize > 5e9)){
4952 // Check to see if the tile has been "clicked".
4953 // If so, do not add to no-show array again.
4954 if(!m_pParentCanvas->IsTileOverlayIndexInYesShow(*rit)){
4955 if(!m_pParentCanvas->IsTileOverlayIndexInNoShow(*rit)){
4956 m_pParentCanvas->m_tile_noshow_index_array.push_back( *rit );
4957 }
4958 }
4959 }
4960
4961
4962 // This test catches the case where the chart is added to no_show list when first loaded by OpenChartFromDBAndLock
4963 if(m_pParentCanvas->IsTileOverlayIndexInNoShow(*rit)){
4964 continue;
4965 }
4966
4967 ChartMBTiles *pcmbt = dynamic_cast<ChartMBTiles*>( chart );
4968 if(pcmbt){
4969 pcmbt->RenderRegionViewOnGL(*m_pcontext, vp, screen_region, screenLLRegion);
4970
4971 //Light up the piano key if the chart was rendered
4972 std::vector<int> piano_active_array_tiles = m_pParentCanvas->m_Piano->GetActiveKeyArray();
4973 bool bfound = false;
4974
4975 if(std::find(piano_active_array_tiles.begin(), piano_active_array_tiles.end(), *rit) != piano_active_array_tiles.end()) {
4976 bfound = true;
4977 }
4978
4979 if(!bfound){
4980 piano_active_array_tiles.push_back( *rit );
4981 m_pParentCanvas->m_Piano->SetActiveKeyArray( piano_active_array_tiles );
4982 }
4983 }
4984 }
4985
4986 // Render the HiLite on piano rollover
4987 LLRegion hiregion = m_pParentCanvas->m_pQuilt->GetHiliteRegion();
4988
4989 if( !hiregion.Empty() ) {
4990 glEnable( GL_BLEND );
4991
4992 double hitrans;
4993 switch( global_color_scheme ) {
4994 case GLOBAL_COLOR_SCHEME_DAY:
4995 hitrans = .4;
4996 break;
4997 case GLOBAL_COLOR_SCHEME_DUSK:
4998 hitrans = .2;
4999 break;
5000 case GLOBAL_COLOR_SCHEME_NIGHT:
5001 hitrans = .1;
5002 break;
5003 default:
5004 hitrans = .4;
5005 break;
5006 }
5007
5008 #ifndef USE_ANDROID_GLES2
5009 glColor4f( (float) .8, (float) .4, (float) .4, (float) hitrans );
5010 #else
5011 s_regionColor = wxColor(204, 102, 102, hitrans * 256);
5012 #endif
5013
5014 DrawRegion(VPoint, hiregion);
5015
5016 glDisable( GL_BLEND );
5017 }
5018 }
5019 }
5020
5021
RenderCanvasBackingChart(ocpnDC & dc,OCPNRegion valid_region)5022 void glChartCanvas::RenderCanvasBackingChart( ocpnDC &dc, OCPNRegion valid_region)
5023 {
5024 // Fill the FBO with the current gshhs world chart
5025 int w, h;
5026 GetClientSize( &w, &h );
5027
5028 glViewport( 0, 0, (GLint) m_cache_tex_x, (GLint) m_cache_tex_y );
5029 #ifndef USE_ANDROID_GLES2
5030 glMatrixMode (GL_PROJECTION);
5031 glLoadIdentity();
5032
5033 glOrtho( 0, m_cache_tex_x, m_cache_tex_y, 0, -1, 1 );
5034 glMatrixMode(GL_MODELVIEW);
5035 glLoadIdentity();
5036 #endif
5037
5038 wxRect rtex( 0, 0, m_cache_tex_x, m_cache_tex_y );
5039 ViewPort cvp = m_pParentCanvas->GetVP().BuildExpandedVP( m_cache_tex_x, m_cache_tex_y );
5040
5041 bool world_view = false;
5042 RenderWorldChart(dc, cvp, rtex, world_view);
5043
5044
5045 // dc.SetPen(wxPen(wxColour(254,254,0), 3));
5046 // dc.DrawLine( 0, 0, m_cache_tex_x, m_cache_tex_y);
5047
5048 // Reset matrices
5049 glViewport( 0, 0, (GLint) w, (GLint) h );
5050 #ifndef USE_ANDROID_GLES2
5051 glMatrixMode (GL_PROJECTION);
5052 glLoadIdentity();
5053
5054 glOrtho( 0, (GLint) w, (GLint) h, 0, -1, 1 );
5055 glMatrixMode(GL_MODELVIEW);
5056 glLoadIdentity();
5057 #endif
5058 }
5059
5060
FastPan(int dx,int dy)5061 void glChartCanvas::FastPan(int dx, int dy)
5062 {
5063 #ifndef USE_ANDROID_GLES2
5064
5065 int sx = GetSize().x;
5066 int sy = GetSize().y;
5067
5068 // ViewPort VPoint = m_pParentCanvas->VPoint;
5069 // ViewPort svp = VPoint;
5070 // svp.pix_width = svp.rv_rect.width;
5071 // svp.pix_height = svp.rv_rect.height;
5072
5073 // OCPNRegion chart_get_region( 0, 0, m_pParentCanvas->VPoint.rv_rect.width, m_pParentCanvas->VPoint.rv_rect.height );
5074
5075 // ocpnDC gldc( *this );
5076
5077 int w, h;
5078 GetClientSize( &w, &h );
5079 glViewport( 0, 0, (GLint) w, (GLint) h );
5080 glMatrixMode (GL_PROJECTION);
5081 glLoadIdentity();
5082
5083 glOrtho( 0, (GLint) w, (GLint) h, 0, -1, 1 );
5084 glMatrixMode(GL_MODELVIEW);
5085 glLoadIdentity();
5086
5087 if( s_b_useStencil ) {
5088 glEnable( GL_STENCIL_TEST );
5089 glStencilMask( 0xff );
5090 glClear( GL_STENCIL_BUFFER_BIT );
5091 glDisable( GL_STENCIL_TEST );
5092 }
5093
5094 float vx0 = 0;
5095 float vy0 = 0;
5096 float vy = sy;
5097 float vx = sx;
5098
5099 glBindTexture( g_texture_rectangle_format, 0);
5100
5101 // /* glColor3ub(0, 0, 120);
5102 // * glBegin( GL_QUADS );
5103 // * glVertex2f( 0, 0 );
5104 // * glVertex2f( w, 0 );
5105 // * glVertex2f( w, h );
5106 // * glVertex2f( 0, h );
5107 // * glEnd();
5108 // */
5109
5110
5111
5112 float tx, ty, tx0, ty0;
5113 //if( GL_TEXTURE_RECTANGLE_ARB == g_texture_rectangle_format )
5114 // tx = sx, ty = sy;
5115 //else
5116 tx = 1, ty = 1;
5117
5118 tx0 = ty0 = 0.;
5119
5120 m_fbo_offsety += dy;
5121 m_fbo_offsetx += dx;
5122
5123 tx0 = m_fbo_offsetx;
5124 ty0 = m_fbo_offsety;
5125 tx = m_fbo_offsetx + sx;
5126 ty = m_fbo_offsety + sy;
5127
5128
5129 if((m_fbo_offsety ) < 0){
5130 ty0 = 0;
5131 ty = m_fbo_offsety + sy;
5132
5133 vy0 = 0;
5134 vy = sy + m_fbo_offsety;
5135
5136 glColor3ub(80, 0, 0);
5137 glBegin( GL_QUADS );
5138 glVertex2f( 0, vy );
5139 glVertex2f( w, vy );
5140 glVertex2f( w, h );
5141 glVertex2f( 0, h );
5142 glEnd();
5143
5144 }
5145 else if((m_fbo_offsety + sy) > m_cache_tex_y){
5146 ty0 = m_fbo_offsety;
5147 ty = m_cache_tex_y;
5148
5149 vy = sy;
5150 vy0 = (m_fbo_offsety + sy - m_cache_tex_y);
5151
5152 glColor3ub(80, 0, 0);
5153 glBegin( GL_QUADS );
5154 glVertex2f( 0, 0 );
5155 glVertex2f( w, 0 );
5156 glVertex2f( w, vy0 );
5157 glVertex2f( 0, vy0 );
5158 glEnd();
5159 }
5160
5161
5162
5163 if((m_fbo_offsetx) < 0){
5164 tx0 = 0;
5165 tx = m_fbo_offsetx + sx;
5166
5167 vx0 = -m_fbo_offsetx;
5168 vx = sx;
5169
5170 glColor3ub(80, 0, 0);
5171 glBegin( GL_QUADS );
5172 glVertex2f( 0, 0 );
5173 glVertex2f( vx0, 0 );
5174 glVertex2f( vx0, h );
5175 glVertex2f( 0, h );
5176 glEnd();
5177 }
5178 else if((m_fbo_offsetx + sx) > m_cache_tex_x){
5179 tx0 = m_fbo_offsetx;
5180 tx = m_cache_tex_x;
5181
5182 vx0 = 0;
5183 vx = m_cache_tex_x - m_fbo_offsetx;
5184
5185 glColor3ub(80, 0, 0);
5186 glBegin( GL_QUADS );
5187 glVertex2f( vx, 0 );
5188 glVertex2f( w, 0 );
5189 glVertex2f( w, h );
5190 glVertex2f( vx, h );
5191 glEnd();
5192 }
5193
5194
5195 // Render the cached texture as quad to screen
5196 glBindTexture( g_texture_rectangle_format, m_cache_tex[m_cache_page]);
5197 glEnable( g_texture_rectangle_format );
5198 glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
5199
5200 glBegin( GL_QUADS );
5201 glTexCoord2f( tx0/m_cache_tex_x, ty/m_cache_tex_y ); glVertex2f( vx0, vy0 );
5202 glTexCoord2f( tx/m_cache_tex_x, ty/m_cache_tex_y ); glVertex2f( vx, vy0 );
5203 glTexCoord2f( tx/m_cache_tex_x, ty0/m_cache_tex_y ); glVertex2f( vx, vy );
5204 glTexCoord2f( tx0/m_cache_tex_x, ty0/m_cache_tex_y ); glVertex2f( vx0, vy );
5205 glEnd();
5206
5207 glDisable( g_texture_rectangle_format );
5208 glBindTexture( g_texture_rectangle_format, 0);
5209
5210
5211 SwapBuffers();
5212
5213 m_canvasregion.Union(tx0, ty0, sx, sy);
5214 #endif
5215 }
5216
ZoomProject(float offset_x,float offset_y,float swidth,float sheight)5217 void glChartCanvas::ZoomProject(float offset_x, float offset_y, float swidth, float sheight)
5218 {
5219 SetCurrent(*m_pcontext);
5220
5221 float sx = GetSize().x;
5222 float sy = GetSize().y;
5223
5224 float tx, ty, tx0, ty0;
5225 //if( GL_TEXTURE_RECTANGLE_ARB == g_texture_rectangle_format )
5226 // tx = sx, ty = sy;
5227 //else
5228 tx = 1, ty = 1;
5229
5230 tx0 = ty0 = 0.;
5231
5232 tx0 = offset_x;
5233 ty0 = offset_y;
5234 tx = offset_x + swidth;
5235 ty = offset_y + sheight;
5236
5237
5238 int w, h;
5239 GetClientSize( &w, &h );
5240 glViewport( 0, 0, (GLint) w, (GLint) h );
5241 #if 0
5242 glMatrixMode (GL_PROJECTION);
5243 glLoadIdentity();
5244
5245 glOrtho( 0, (GLint) w, (GLint) h, 0, -1, 1 );
5246 glMatrixMode(GL_MODELVIEW);
5247 glLoadIdentity();
5248 #endif
5249
5250 if( s_b_useStencil ) {
5251 glEnable( GL_STENCIL_TEST );
5252 glStencilMask( 0xff );
5253 glClear( GL_STENCIL_BUFFER_BIT );
5254 glDisable( GL_STENCIL_TEST );
5255 }
5256
5257
5258 float vx0 = 0;
5259 float vy0 = 0;
5260 float vy = sy;
5261 float vx = sx;
5262
5263 glBindTexture( g_texture_rectangle_format, 0);
5264
5265 // Render the cached texture as quad to screen
5266 glBindTexture( g_texture_rectangle_format, m_cache_tex[m_cache_page]);
5267 glEnable( g_texture_rectangle_format );
5268 // glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
5269
5270
5271 float uv[8];
5272 float coords[8];
5273
5274 //normal uv Texture coordinates
5275 uv[0] = tx0/m_cache_tex_x; uv[1] = ty/m_cache_tex_y ; uv[2] = tx/m_cache_tex_x; uv[3] = ty/m_cache_tex_y ;
5276 uv[4] = tx/m_cache_tex_x; uv[5] = ty0/m_cache_tex_y ; uv[6] = tx0/m_cache_tex_x; uv[7] = ty0/m_cache_tex_y ;
5277
5278 // pixels
5279 coords[0] = vx0; coords[1] = vy0; coords[2] = vx; coords[3] = vy0;
5280 coords[4] = vx; coords[5] = vy; coords[6] = vx0; coords[7] = vy;
5281
5282 RenderTextures(coords, uv, 4, m_pParentCanvas->GetpVP());
5283
5284 // glBegin( GL_QUADS );
5285 // glTexCoord2f( tx0/m_cache_tex_x, ty/m_cache_tex_y ); glVertex2f( vx0, vy0 );
5286 // glTexCoord2f( tx/m_cache_tex_x, ty/m_cache_tex_y ); glVertex2f( vx, vy0 );
5287 // glTexCoord2f( tx/m_cache_tex_x, ty0/m_cache_tex_y ); glVertex2f( vx, vy );
5288 // glTexCoord2f( tx0/m_cache_tex_x, ty0/m_cache_tex_y ); glVertex2f( vx0, vy );
5289 // glEnd();
5290
5291 glDisable( g_texture_rectangle_format );
5292 glBindTexture( g_texture_rectangle_format, 0);
5293
5294 // For fun, we prove the coordinates of the blank area outside the chart when zooming out.
5295 // Bottom stripe
5296 //wxColour color = GetGlobalColor(_T("YELO1")); //GREY1
5297 wxColour color = GetGlobalColor(_T("GREY1")); //
5298 float ht = -offset_y * ( sy / sheight);
5299 wxRect r(0,sy - ht,w,ht);
5300 RenderColorRect(r, color);
5301
5302 // top stripe
5303 wxRect rt(0,0,w, sy - (ht + (sy * sy / sheight)));
5304 RenderColorRect(rt, color);
5305
5306 // left
5307 float w1 = -offset_x * sx / swidth;
5308 wxRect rl(0,0,w1, sy);
5309 RenderColorRect(rl, color);
5310
5311 //right
5312 float px = w1 + sx*sx/swidth;
5313 wxRect rr(px, 0, sx - px, sy);
5314 RenderColorRect(rr, color);
5315
5316
5317
5318
5319 // When zooming out, if we go too far, then the frame buffer is repeated on-screen due
5320 // to address wrapping in the frame buffer.
5321 // Detect this case, and render some simple solid covering quads to avoid a confusing display.
5322
5323
5324 #if 0
5325 if( (m_fbo_sheight > m_cache_tex_y) || (m_fbo_swidth > m_cache_tex_x) ){
5326 wxColour color = GetGlobalColor(_T("YELO1")); //GREY1
5327 glColor3ub(color.Red(), color.Green(), color.Blue());
5328
5329 if( m_fbo_sheight > m_cache_tex_y ){
5330 float h1 = sy * (1.0 - m_cache_tex_y/m_fbo_sheight) / 2.;
5331
5332 wxRect r(0,0,w,h1);
5333 RenderColorRect(r, color);
5334
5335 wxRect r1(0,sy - h1,w,h1);
5336 RenderColorRect(r1, color);
5337
5338 #if 0
5339 glBegin( GL_QUADS );
5340 glVertex2f( 0, 0 );
5341 glVertex2f( w, 0 );
5342 glVertex2f( w, h1 );
5343 glVertex2f( 0, h1 );
5344 glEnd();
5345
5346 glBegin( GL_QUADS );
5347 glVertex2f( 0, sy );
5348 glVertex2f( w, sy );
5349 glVertex2f( w, sy - h1 );
5350 glVertex2f( 0, sy - h1 );
5351 glEnd();
5352 #endif
5353 }
5354 #endif
5355
5356 #if 0
5357 // horizontal axis
5358 if( m_fbo_swidth > m_cache_tex_x ){
5359 float w1 = sx * (1.0 - m_cache_tex_x/m_fbo_swidth) / 2.;
5360
5361 wxRect r(0,0,w1,sy);
5362 RenderColorRect(r, color);
5363
5364 wxRect r1(sx-w1,0,w1,sy);
5365 RenderColorRect(r1, color);
5366
5367 #if 0
5368 glBegin( GL_QUADS );
5369 glVertex2f( 0, 0 );
5370 glVertex2f( w1, 0 );
5371 glVertex2f( w1, sy );
5372 glVertex2f( 0, sy );
5373 glEnd();
5374
5375 glBegin( GL_QUADS );
5376 glVertex2f( sx, 0 );
5377 glVertex2f( sx - w1, 0 );
5378 glVertex2f( sx - w1, sy );
5379 glVertex2f( sx, sy );
5380 glEnd();
5381 #endif
5382 }
5383 }
5384
5385
5386 // horizontal
5387 if(m_fbo_offsetx < 0){
5388 wxColour color = GetGlobalColor(_T("TEAL1"));
5389 glColor3ub(color.Red(), color.Green(), color.Blue());
5390 float w1 = -offset_x * sx / swidth;
5391 qDebug() << "tealA" << sx << sy << w1;
5392 wxRect r(0,0,w1, sy);
5393 RenderColorRect(r, color);
5394 /*
5395 glBegin( GL_QUADS );
5396 glVertex2f( 0, 0 );
5397 glVertex2f( w1, 0 );
5398 glVertex2f( w1, sy );
5399 glVertex2f( 0, sy );
5400 glEnd();
5401 */
5402 }
5403
5404
5405 qDebug() << "horiz1" << m_fbo_offsetx << m_fbo_swidth << m_cache_tex_x << m_fbo_offsetx + m_fbo_swidth;
5406
5407 if(m_fbo_offsetx + m_fbo_swidth > m_cache_tex_x){
5408 wxColour color = GetGlobalColor(_T("TEAL1"));
5409 glColor3ub(color.Red(), color.Green(), color.Blue());
5410 float w1 = ((offset_x + swidth) - m_cache_tex_x) * ( sx / swidth);
5411 wxRect r(sx ,0, -w1, sy);
5412 qDebug() << "tealB" << sx << w1 << sy;
5413 RenderColorRect(r, color);
5414 /*
5415 glBegin( GL_QUADS );
5416 glVertex2f( sx, 0 );
5417 glVertex2f( sx - w1, 0 );
5418 glVertex2f( sx - w1, sy );
5419 glVertex2f( sx, sy );
5420 glEnd();
5421 */
5422 }
5423
5424
5425
5426 // Vertical
5427 if(m_fbo_offsety < 0){
5428 wxColour color = GetGlobalColor(_T("RED1"));
5429 glColor3ub(color.Red(), color.Green(), color.Blue());
5430 float w1 = -offset_y * sy / sheight;
5431
5432 wxRect r(0,0, sx, w1);
5433 RenderColorRect(r, color);
5434 /*
5435 glBegin( GL_QUADS );
5436 glVertex2f( 0, sy );
5437 glVertex2f( sx, sy );
5438 glVertex2f( sx, sy - w1 );
5439 glVertex2f( 0, sy - w1 );
5440 glEnd();
5441 */
5442 }
5443
5444
5445
5446 if(m_fbo_offsety + m_fbo_sheight > m_cache_tex_y){
5447 wxColour color = GetGlobalColor(_T("RED1"));
5448 glColor3ub(color.Red(), color.Green(), color.Blue());
5449 float w1 = ((offset_y + sheight) - m_cache_tex_y) * ( sy / sheight);
5450
5451 wxRect r(0,0, sx, w1);
5452 RenderColorRect(r, color);
5453 /*
5454 glBegin( GL_QUADS );
5455 glVertex2f( 0, 0 );
5456 glVertex2f( 0, w1 );
5457 glVertex2f( sx, w1 );
5458 glVertex2f( sx, 0 );
5459 glEnd();
5460 */
5461 }
5462 #endif
5463 SwapBuffers();
5464
5465 }
5466
5467
5468
onZoomTimerEvent(wxTimerEvent & event)5469 void glChartCanvas::onZoomTimerEvent(wxTimerEvent &event)
5470 {
5471 // If m_zoomFinal is set, shortcut the timer sequence.
5472
5473 if( (m_nRun < m_nTotal) && !m_zoomFinal){
5474 m_runoffsetx += m_offsetxStep;
5475 if(m_offsetxStep > 0)
5476 m_runoffsetx = wxMin(m_runoffsetx, m_fbo_offsetx);
5477 else
5478 m_runoffsetx = wxMax(m_runoffsetx, m_fbo_offsetx);
5479
5480 m_runoffsety += m_offsetyStep;
5481 if(m_offsetyStep > 0)
5482 m_runoffsety = wxMin(m_runoffsety, m_fbo_offsety);
5483 else
5484 m_runoffsety = wxMax(m_runoffsety, m_fbo_offsety);
5485
5486 m_runswidth += m_swidthStep;
5487 if(m_swidthStep > 0)
5488 m_runswidth = wxMin(m_runswidth, m_fbo_swidth);
5489 else
5490 m_runswidth = wxMax(m_runswidth, m_fbo_swidth);
5491
5492 m_runsheight += m_sheightStep;
5493 if(m_sheightStep > 0)
5494 m_runsheight = wxMin(m_runsheight, m_fbo_sheight);
5495 else
5496 m_runsheight = wxMax(m_runsheight, m_fbo_sheight);
5497
5498 // qDebug() << "onZoomTimerEvent" << m_nRun << m_nTotal << m_runoffsetx << m_offsetxStep;
5499
5500 ZoomProject(m_runoffsetx, m_runoffsety, m_runswidth, m_runsheight);
5501 m_nRun += m_nStep;
5502 }
5503 else{
5504 //qDebug() << "onZoomTimerEvent DONE" << m_nRun << m_nTotal;
5505
5506 zoomTimer.Stop();
5507 if(m_zoomFinal){
5508 //qDebug() << "onZoomTimerEvent FINALZOOM" << m_zoomFinalZoom;
5509
5510
5511 if(m_zoomFinalZoom > 1)
5512 m_inFade = true;
5513
5514 m_pParentCanvas->ZoomCanvas( m_zoomFinalZoom, false );
5515
5516 if(m_zoomFinaldx || m_zoomFinaldy){
5517 m_pParentCanvas->PanCanvas( m_zoomFinaldx, m_zoomFinaldy );
5518 }
5519
5520 if(m_zoomFinalZoom > 1){ // only fade on ZIN
5521
5522 if( (quiltHash != m_pParentCanvas->m_pQuilt->GetXStackHash()) || (refChartIndex != m_pParentCanvas->m_pQuilt->GetRefChartdbIndex()) ){
5523
5524 // Make next full render happen on new page.
5525 m_cache_page = !m_cache_page;
5526
5527 RenderScene();
5528
5529 //qDebug() << "fboFade()";
5530 fboFade(m_cache_tex[0],m_cache_tex[1]);
5531 }
5532 else
5533 m_inFade = false;
5534
5535 }
5536 else
5537 m_inFade = false;
5538
5539
5540 }
5541 m_zoomFinal = false;
5542 }
5543
5544 }
5545
5546
FastZoom(float factor,float cp_x,float cp_y,float post_x,float post_y)5547 void glChartCanvas::FastZoom(float factor, float cp_x, float cp_y, float post_x, float post_y)
5548 {
5549 //qDebug() << "FastZoom" << factor << post_x << post_y << m_nRun;
5550
5551 int sx = GetSize().x;
5552 int sy = GetSize().y;
5553
5554 m_lastfbo_offsetx = m_fbo_offsetx;
5555 m_lastfbo_offsety = m_fbo_offsety;
5556 m_lastfbo_swidth = m_fbo_swidth;
5557 m_lastfbo_sheight = m_fbo_sheight;
5558
5559 float curr_fbo_offset_x = m_fbo_offsetx;
5560 float curr_fbo_offset_y = m_fbo_offsety;
5561 float curr_fbo_swidth = m_fbo_swidth;
5562 float curr_fbo_sheight = m_fbo_sheight;
5563
5564
5565 float fx = (float)cp_x / sx;
5566 float fy = 1.0 - (float)cp_y / sy;
5567 if(factor < 1.0f){
5568 // fx = 0.5; // center screen
5569 // fy = 0.5;
5570 }
5571
5572 float fbo_ctr_x = curr_fbo_offset_x + (curr_fbo_swidth * fx);
5573 float fbo_ctr_y = curr_fbo_offset_y + (curr_fbo_sheight * fy);
5574
5575 m_fbo_swidth = curr_fbo_swidth / factor;
5576 m_fbo_sheight = curr_fbo_sheight / factor;
5577
5578 m_fbo_offsetx = fbo_ctr_x - (m_fbo_swidth * fx);
5579 m_fbo_offsety = fbo_ctr_y - (m_fbo_sheight * fy);
5580
5581
5582 m_fbo_offsetx += post_x;
5583 m_fbo_offsety += post_y;
5584
5585 // if(factor < 1.0f){
5586 // ZoomProject(m_fbo_offsetx, m_fbo_offsety, m_fbo_swidth, m_fbo_sheight);
5587 // zoomTimer.Stop();
5588 // m_zoomFinal = false;
5589 // }
5590 // else
5591 {
5592 m_nStep = 20;
5593 m_nTotal = 100;
5594
5595 m_nStep = 10;
5596 m_nTotal = 40;
5597
5598 m_nRun = 0;
5599
5600 float perStep = m_nStep / m_nTotal;
5601
5602
5603 if(zoomTimer.IsRunning()){
5604 m_offsetxStep = (m_fbo_offsetx - m_runoffsetx) * perStep;
5605 m_offsetyStep = (m_fbo_offsety - m_runoffsety) * perStep;
5606 m_swidthStep = (m_fbo_swidth - m_runswidth) * perStep;
5607 m_sheightStep = (m_fbo_sheight - m_runsheight) * perStep;
5608
5609 }
5610 else{
5611 m_offsetxStep = (m_fbo_offsetx - m_lastfbo_offsetx) * perStep;
5612 m_offsetyStep = (m_fbo_offsety - m_lastfbo_offsety) * perStep;
5613 m_swidthStep = (m_fbo_swidth - m_lastfbo_swidth) * perStep;
5614 m_sheightStep = (m_fbo_sheight - m_lastfbo_sheight) * perStep;
5615
5616 m_runoffsetx = m_lastfbo_offsetx;
5617 m_runoffsety = m_lastfbo_offsety;
5618 m_runswidth = m_lastfbo_swidth;
5619 m_runsheight = m_lastfbo_sheight;
5620 }
5621
5622 if(!zoomTimer.IsRunning())
5623 zoomTimer.Start(m_nStep);
5624 m_zoomFinal = false;
5625 }
5626 }
5627
5628 #ifdef __OCPN__ANDROID__
5629
5630
OnEvtPanGesture(wxQT_PanGestureEvent & event)5631 void glChartCanvas::OnEvtPanGesture( wxQT_PanGestureEvent &event)
5632 {
5633 //qDebug() << "OnEvtPanGesture" << m_pParentCanvas->m_canvasIndex << event.cursor_pos.x;
5634
5635
5636 if( m_pParentCanvas->isRouteEditing() || m_pParentCanvas->isMarkEditing() )
5637 return;
5638
5639 if(m_binPinch)
5640 return;
5641 if(m_bpinchGuard)
5642 return;
5643
5644 int x = event.GetOffset().x;
5645 int y = event.GetOffset().y;
5646
5647 int lx = event.GetLastOffset().x;
5648 int ly = event.GetLastOffset().y;
5649
5650 int dx = lx - x;
5651 int dy = y - ly;
5652
5653 switch(event.GetState()){
5654 case GestureStarted:
5655 if(m_binPan)
5656 break;
5657
5658 panx = pany = 0;
5659 m_binPan = true;
5660 m_binGesture = true;
5661 //qDebug() << "pg1";
5662 break;
5663
5664 case GestureUpdated:
5665 if(m_binPan){
5666 if(!g_GLOptions.m_bUseCanvasPanning || m_bfogit){
5667 //qDebug() << "slowpan" << dx << dy;
5668
5669 m_pParentCanvas->FreezePiano();
5670 m_pParentCanvas->PanCanvas( dx, -dy );
5671 m_pParentCanvas->ThawPiano();
5672
5673 }
5674 else{
5675 FastPan( dx, dy );
5676 }
5677
5678
5679 panx -= dx;
5680 pany -= dy;
5681 }
5682 break;
5683
5684 case GestureFinished:
5685 //qDebug() << "panGestureFinished";
5686
5687 m_pParentCanvas->UpdateCanvasControlBar();
5688
5689 m_binPan = false;
5690 m_gestureFinishTimer.Start(500, wxTIMER_ONE_SHOT);
5691
5692 break;
5693
5694 case GestureCanceled:
5695 m_binPan = false;
5696 m_gestureFinishTimer.Start(500, wxTIMER_ONE_SHOT);
5697 break;
5698
5699 default:
5700 break;
5701 }
5702
5703 m_bgestureGuard = true;
5704 m_gestureEeventTimer.Start(500, wxTIMER_ONE_SHOT);
5705 m_bforcefull = false;
5706
5707 //qDebug() << "panGestureDone";
5708
5709 }
5710
5711 float zoom_inc = 1.0;
5712 bool first_zout = false;
5713
OnEvtPinchGesture(wxQT_PinchGestureEvent & event)5714 void glChartCanvas::OnEvtPinchGesture( wxQT_PinchGestureEvent &event)
5715 {
5716
5717 float zoom_gain = 1.0;
5718 float zout_gain = 1.0;
5719
5720 float zoom_val;
5721 float total_zoom_val;
5722
5723 float max_zoom_scale = 1000.;
5724 float min_zoom_scale = 2e8;
5725
5726 if( event.GetScaleFactor() > 1)
5727 zoom_val = ((event.GetScaleFactor() - 1.0) * zoom_gain) + 1.0;
5728 else
5729 zoom_val = 1.0 - ((1.0 - event.GetScaleFactor()) * zout_gain);
5730
5731 if( event.GetTotalScaleFactor() > 1)
5732 total_zoom_val = ((event.GetTotalScaleFactor() - 1.0) * zoom_gain) + 1.0;
5733 else
5734 #if 0
5735 total_zoom_val = 1.0 - ((1.0 - event.GetTotalScaleFactor()) * zout_gain);
5736
5737 double projected_scale = cc1->GetVP().chart_scale / total_zoom_val;
5738
5739 // Max zoom in is set by scale of quilt reference chart, consistent with chart render limits set elsewhere.
5740 float max_zoom_scale = 1000.;
5741 if( cc1->GetVP().b_quilt) {
5742 int ref_index = cc1->GetQuiltRefChartdbIndex();
5743 // if((ref_index >= 0) && ChartData){
5744 // max_zoom_scale = ChartData->GetDBChartScale(ref_index) / 8.0;
5745 // }
5746 }
5747
5748
5749 float min_zoom_scale = 2e8;
5750
5751 =======
5752 #endif
5753
5754 total_zoom_val = 1.0 - ((1.0 - event.GetTotalScaleFactor()) * zoom_gain);
5755
5756 double projected_scale = m_pParentCanvas->GetVP().chart_scale / total_zoom_val;
5757
5758 switch(event.GetState()){
5759 case GestureStarted:
5760 first_zout = false;
5761 m_binPinch = true;
5762 m_binPan = false; // cancel any tentative pan gesture, in case the "pan cancel" event was lost
5763 m_binGesture = true;
5764 //qDebug() << "pg2";
5765 m_pinchStart = event.GetCenterPoint();
5766 m_lpinchPoint = m_pinchStart;
5767
5768 m_pParentCanvas->GetCanvasPixPoint(event.GetCenterPoint().x, event.GetCenterPoint().y, m_pinchlat, m_pinchlon);
5769 // qDebug() << "center" << event.GetCenterPoint().x << event.GetCenterPoint().y;
5770
5771 m_cc_x = m_fbo_offsetx + (m_fbo_swidth/2);
5772 m_cc_y = m_fbo_offsety + (m_fbo_sheight/2);
5773
5774 // Render the full charts with overlay objects onto the frame buffer.
5775 SetCurrent(*m_pcontext);
5776 RenderScene( );
5777
5778 zoom_inc = 1.0;
5779 break;
5780
5781 case GestureUpdated:
5782 if(g_GLOptions.m_bUseCanvasPanning){
5783
5784 if( projected_scale < min_zoom_scale){
5785 wxPoint pinchPoint = event.GetCenterPoint();
5786
5787 float dx = pinchPoint.x - m_lpinchPoint.x;
5788 float dy = pinchPoint.y - m_lpinchPoint.y;
5789
5790 FastZoom(zoom_val, m_pinchStart.x, m_pinchStart.y, -dx / total_zoom_val, dy / total_zoom_val);
5791
5792 m_lpinchPoint = pinchPoint;
5793
5794 }
5795 }
5796 else{
5797 //qDebug() << "update totalzoom" << total_zoom_val << projected_scale;
5798 if( 1 || ((total_zoom_val > 1) && !first_zout)){ // Zoom in
5799 wxPoint pinchPoint = event.GetCenterPoint();
5800
5801 float dx = pinchPoint.x - m_lpinchPoint.x;
5802 float dy = pinchPoint.y - m_lpinchPoint.y;
5803
5804 if(!m_inFade){
5805 if( (projected_scale > max_zoom_scale) && ( projected_scale < min_zoom_scale))
5806 FastZoom(zoom_val, m_pinchStart.x, m_pinchStart.y, -dx / total_zoom_val, dy / total_zoom_val);
5807 }
5808 else
5809 qDebug() << "Skip fastzoom on zin";
5810
5811 m_lpinchPoint = pinchPoint;
5812
5813 }
5814 else{
5815 first_zout = true;
5816 zoom_inc *= zoom_val;
5817 if((zoom_inc < 0.9) || (zoom_inc > 1.1)){
5818 m_pParentCanvas->ZoomCanvas( zoom_inc, false );
5819 zoom_inc = 1.0;
5820 }
5821
5822 wxPoint pinchPoint = event.GetCenterPoint();
5823 float dx = pinchPoint.x - m_lpinchPoint.x;
5824 float dy = pinchPoint.y - m_lpinchPoint.y;
5825 m_pParentCanvas->PanCanvas( -dx, -dy );
5826 m_lpinchPoint = pinchPoint;
5827
5828
5829 // SetCurrent(*m_pcontext);
5830 // RenderScene();
5831 // g_Piano->DrawGL(cc1->m_canvas_height - g_Piano->GetHeight());
5832 // SwapBuffers();
5833
5834 }
5835 }
5836
5837 break;
5838
5839 case GestureFinished:{
5840 // qDebug() << "finish totalzoom" << total_zoom_val << projected_scale;
5841
5842 float cc_x = m_fbo_offsetx + (m_fbo_swidth/2);
5843 float cc_y = m_fbo_offsety + (m_fbo_sheight/2);
5844 float dy = 0;
5845 float dx = 0;
5846
5847 float tzoom = total_zoom_val;
5848
5849 if( projected_scale >= min_zoom_scale)
5850 tzoom = m_pParentCanvas->GetVP().chart_scale / min_zoom_scale;
5851
5852 if( projected_scale < max_zoom_scale)
5853 tzoom = m_pParentCanvas->GetVP().chart_scale / max_zoom_scale;
5854
5855 dx = (cc_x - m_cc_x) * tzoom;
5856 dy = -(cc_y - m_cc_y) * tzoom;
5857
5858 if(m_inFade){
5859 qDebug() << "Fade interrupt";
5860 m_binPinch = false;
5861 m_gestureFinishTimer.Start(500, wxTIMER_ONE_SHOT);
5862 break;
5863 }
5864
5865 if(zoomTimer.IsRunning()){
5866 // qDebug() << "Final zoom";
5867 m_zoomFinal = true;
5868 m_zoomFinalZoom = tzoom;
5869 m_zoomFinaldx = dx;
5870 m_zoomFinaldy = dy;
5871 }
5872
5873 else{
5874 double final_projected_scale = m_pParentCanvas->GetVP().chart_scale / tzoom;
5875 //qDebug() << "Final pinchB" << tzoom << final_projected_scale;
5876
5877 if( final_projected_scale < min_zoom_scale){
5878 //qDebug() << "zoomit";
5879 m_pParentCanvas->ZoomCanvas( tzoom, false );
5880 m_pParentCanvas->PanCanvas( dx, dy );
5881 m_pParentCanvas->m_pQuilt->Invalidate();
5882 m_bforcefull = true;
5883
5884 }
5885 else{
5886 double new_scale = m_pParentCanvas->GetCanvasScaleFactor() / min_zoom_scale;
5887 //qDebug() << "clampit";
5888 m_pParentCanvas->SetVPScale( new_scale );
5889 m_pParentCanvas->m_pQuilt->Invalidate();
5890 m_bforcefull = true;
5891 }
5892
5893 }
5894
5895
5896 // if( projected_scale < 3e8)
5897 // m_pParentCanvas->ZoomCanvas( total_zoom_val, false );
5898 // else
5899 // m_pParentCanvas->ZoomCanvas(m_pParentCanvas->GetVP().chart_scale / 3e8, false);
5900
5901 m_binPinch = false;
5902 m_gestureFinishTimer.Start(500, wxTIMER_ONE_SHOT);
5903 break;
5904 }
5905
5906 case GestureCanceled:
5907 m_binPinch = false;
5908 m_gestureFinishTimer.Start(500, wxTIMER_ONE_SHOT);
5909 break;
5910
5911 default:
5912 break;
5913 }
5914
5915 m_bgestureGuard = true;
5916 // m_bpinchGuard = true;
5917 m_gestureEeventTimer.Start(500, wxTIMER_ONE_SHOT);
5918
5919 }
5920
onGestureTimerEvent(wxTimerEvent & event)5921 void glChartCanvas::onGestureTimerEvent(wxTimerEvent &event)
5922 {
5923 // On some devices, the pan GestureFinished event fails to show up
5924 // Watch for this case, and fix it.....
5925 //qDebug() << "onGestureTimerEvent";
5926
5927 if(m_binPan){
5928 m_binPan = false;
5929 Invalidate();
5930 Update();
5931 }
5932 m_bgestureGuard = false;
5933 m_bpinchGuard = false;
5934 m_binGesture = false;
5935 m_bforcefull = false;
5936
5937 }
5938
onGestureFinishTimerEvent(wxTimerEvent & event)5939 void glChartCanvas::onGestureFinishTimerEvent(wxTimerEvent &event)
5940 {
5941 //qDebug() << "onGestureFinishTimerEvent";
5942
5943 // signal gesture is finished after a delay
5944 m_binGesture = false;
5945 m_bforcefull = false;
5946
5947 }
5948
5949
5950
5951 #endif
5952
configureShaders(ViewPort & vp)5953 void glChartCanvas::configureShaders( ViewPort &vp)
5954 {
5955 #ifdef USE_ANDROID_GLES2
5956 mat4x4 I;
5957 mat4x4_identity(I);
5958
5959 ViewPort *pvp = (ViewPort *)&vp;
5960
5961 glUseProgram(color_tri_shader_program);
5962 GLint matloc = glGetUniformLocation(color_tri_shader_program,"MVMatrix");
5963 glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)pvp->vp_transform);
5964 GLint transloc = glGetUniformLocation(color_tri_shader_program,"TransformMatrix");
5965 glUniformMatrix4fv( transloc, 1, GL_FALSE, (const GLfloat*)I);
5966
5967 glUseProgram(texture_2D_shader_program);
5968 matloc = glGetUniformLocation(texture_2D_shader_program,"MVMatrix");
5969 glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)pvp->vp_transform);
5970 transloc = glGetUniformLocation(texture_2D_shader_program,"TransformMatrix");
5971 glUniformMatrix4fv( transloc, 1, GL_FALSE, (const GLfloat*)I);
5972
5973 glUseProgram(circle_filled_shader_program);
5974 matloc = glGetUniformLocation(circle_filled_shader_program,"MVMatrix");
5975 glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)pvp->vp_transform);
5976 transloc = glGetUniformLocation(circle_filled_shader_program,"TransformMatrix");
5977 glUniformMatrix4fv( transloc, 1, GL_FALSE, (const GLfloat*)I);
5978
5979 glUseProgram(texture_2DA_shader_program);
5980 matloc = glGetUniformLocation(texture_2DA_shader_program,"MVMatrix");
5981 glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)pvp->vp_transform);
5982 transloc = glGetUniformLocation(texture_2DA_shader_program,"TransformMatrix");
5983 glUniformMatrix4fv( transloc, 1, GL_FALSE, (const GLfloat*)I);
5984
5985
5986 #endif
5987 }
5988
5989
5990 GLint m_fadeTexOld;
5991 GLint m_fadeTexNew;
5992 float m_fadeFactor;
5993 int n_fade;
5994 unsigned int s_cachepage;
5995
onFadeTimerEvent(wxTimerEvent & event)5996 void glChartCanvas::onFadeTimerEvent(wxTimerEvent &event)
5997 {
5998 #ifdef USE_ANDROID_GLES2
5999 // if(m_cache_page != s_cachepage)
6000 // qDebug() << "CACHE PAGE lost";
6001
6002 if(m_fadeFactor < 0.2)
6003 m_fadeFactor = 0;
6004
6005 //qDebug() << "fade" << m_fadeFactor << m_cache_page << n_fade;
6006
6007 SetCurrent(*m_pcontext);
6008
6009 int sx = GetSize().x;
6010 int sy = GetSize().y;
6011
6012 float coords[8];
6013 float uv[8];
6014
6015 float divx, divy;
6016 // Normalize, or not?
6017 if( GL_TEXTURE_RECTANGLE_ARB == g_texture_rectangle_format ){
6018 divx = divy = 1.0f;
6019 }
6020 else{
6021 divx = m_cache_tex_x;
6022 divy = m_cache_tex_y;
6023 }
6024
6025 /// ZoomProject(m_runoffsetx, m_runoffsety, m_runswidth, m_runsheight);
6026
6027 ///
6028 /// tx = 1, ty = 1;
6029
6030 /// tx0 = ty0 = 0.;
6031
6032 float tx0 = m_runoffsetx;
6033 float ty0 = m_runoffsety;
6034 float tx = m_runoffsetx + m_runswidth;
6035 float ty = m_runoffsety + m_runsheight;
6036
6037
6038 float vx0 = 0;
6039 float vy0 = 0;
6040 float vy = sy;
6041 float vx = sx;
6042
6043 // pixels
6044 coords[0] = vx0; coords[1] = vy0; coords[2] = vx; coords[3] = vy0;
6045 coords[4] = vx; coords[5] = vy; coords[6] = vx0; coords[7] = vy;
6046
6047
6048 // uv coordinates for first texture
6049 uv[0] = tx0/m_cache_tex_x; uv[1] = ty/m_cache_tex_y ; uv[2] = tx/m_cache_tex_x; uv[3] = ty/m_cache_tex_y ;
6050 uv[4] = tx/m_cache_tex_x; uv[5] = ty0/m_cache_tex_y ; uv[6] = tx0/m_cache_tex_x; uv[7] = ty0/m_cache_tex_y ;
6051
6052
6053
6054 // uv coordinates for second texture
6055 float tx02 = m_fbo_offsetx/divx;
6056 float ty02 = m_fbo_offsety/divy;
6057 float tx2 = (m_fbo_offsetx + m_fbo_swidth)/divx;
6058 float ty2 = (m_fbo_offsety + m_fbo_sheight)/divy;
6059
6060 //normal uv
6061 float uv2[8];
6062 uv2[0] = tx02; uv2[1] = ty2; uv2[2] = tx2; uv2[3] = ty2;
6063 uv2[4] = tx2; uv2[5] = ty02; uv2[6] = tx02; uv2[7] = ty02;
6064
6065
6066 ///
6067
6068 // float tx0 = m_fbo_offsetx/divx;
6069 // float ty0 = m_fbo_offsety/divy;
6070 // float tx = (m_fbo_offsetx + m_fbo_swidth)/divx;
6071 // float ty = (m_fbo_offsety + m_fbo_sheight)/divy;
6072 //
6073 // //normal uv
6074 // uv[0] = tx0; uv[1] = ty; uv[2] = tx; uv[3] = ty;
6075 // uv[4] = tx; uv[5] = ty0; uv[6] = tx0; uv[7] = ty0;
6076 //
6077 // // pixels
6078 // coords[0] = 0; coords[1] = 0; coords[2] = sx; coords[3] = 0;
6079 // coords[4] = sx; coords[5] = sy; coords[6] = 0; coords[7] = sy;
6080
6081 glEnable(GL_BLEND);
6082
6083 //glClearColor(0.f, 0.f, 0.f, 1.0f);
6084 wxColour color = GetGlobalColor( _T ( "NODTA" ) );
6085 glClearColor( color.Red() / 256., color.Green() / 256. , color.Blue()/ 256. , 1.0);
6086
6087 glClear(GL_COLOR_BUFFER_BIT);
6088
6089
6090 // Render old texture using shader
6091 glUseProgram( fade_texture_2D_shader_program );
6092
6093 // Get pointers to the attributes in the program.
6094 GLint mPosAttrib = glGetAttribLocation( fade_texture_2D_shader_program, "aPos" );
6095 GLint mUvAttrib = glGetAttribLocation( fade_texture_2D_shader_program, "aUV" );
6096 GLint mUvAttrib2 = glGetAttribLocation( fade_texture_2D_shader_program, "aUV2" );
6097
6098 // Set up the texture sampler to texture unit 0
6099 GLint texUni = glGetUniformLocation( fade_texture_2D_shader_program, "uTex" );
6100 glUniform1i( texUni, 0 );
6101
6102 GLint texUni2 = glGetUniformLocation( fade_texture_2D_shader_program, "uTex2" );
6103 glUniform1i( texUni2, 1 );
6104
6105 // Disable VBO's (vertex buffer objects) for attributes.
6106 glBindBuffer( GL_ARRAY_BUFFER, 0 );
6107 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
6108
6109 // Set the attribute mPosAttrib with the vertices in the screen coordinates...
6110 glVertexAttribPointer( mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, coords );
6111 // ... and enable it.
6112 glEnableVertexAttribArray( mPosAttrib );
6113
6114 // Set the attribute mUvAttrib with the vertices in the GL coordinates...
6115 glVertexAttribPointer( mUvAttrib, 2, GL_FLOAT, GL_FALSE, 0, uv );
6116 // ... and enable it.
6117 glEnableVertexAttribArray( mUvAttrib );
6118
6119 // Set the attribute mUvAttrib with the vertices in the GL coordinates...
6120 glVertexAttribPointer( mUvAttrib2, 2, GL_FLOAT, GL_FALSE, 0, uv2 );
6121 // ... and enable it.
6122 glEnableVertexAttribArray( mUvAttrib2 );
6123
6124
6125 GLint matloc = glGetUniformLocation(fade_texture_2D_shader_program,"MVMatrix");
6126 glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)(m_pParentCanvas->GetpVP()->vp_transform) );
6127
6128 // GLint transloc = glGetUniformLocation(fade_texture_2D_shader_program,"trans");
6129 // float colVec[4] = {1.0, 1.0, 1.0, 1.0};
6130 // colVec[3] = m_fadeFactor;
6131 // glUniform4fv( transloc, 1, colVec );
6132
6133 GLint alphaloc = glGetUniformLocation(fade_texture_2D_shader_program,"texAlpha");
6134 glUniform1f(alphaloc, m_fadeFactor);
6135
6136 // Select the active texture unit.
6137 glActiveTexture( GL_TEXTURE0 );
6138 glBindTexture( g_texture_rectangle_format, m_cache_tex[!m_cache_page]);
6139
6140 // Select the active texture unit.
6141 glActiveTexture( GL_TEXTURE1 );
6142 glBindTexture( g_texture_rectangle_format, m_cache_tex[m_cache_page]);
6143
6144
6145 // Perform the actual drawing.
6146
6147 // For some reason, glDrawElements is busted on Android
6148 // So we do this a hard ugly way, drawing two triangles...
6149 #if 1
6150 GLushort indices1[] = {0,1,3,2};
6151 glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, indices1);
6152 #else
6153
6154 float co1[8];
6155 co1[0] = coords[0];
6156 co1[1] = coords[1];
6157 co1[2] = coords[2];
6158 co1[3] = coords[3];
6159 co1[4] = coords[6];
6160 co1[5] = coords[7];
6161 co1[6] = coords[4];
6162 co1[7] = coords[5];
6163
6164 float tco1[8];
6165 tco1[0] = uv[0];
6166 tco1[1] = uv[1];
6167 tco1[2] = uv[2];
6168 tco1[3] = uv[3];
6169 tco1[4] = uv[6];
6170 tco1[5] = uv[7];
6171 tco1[6] = uv[4];
6172 tco1[7] = uv[5];
6173
6174 glVertexAttribPointer( mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, co1 );
6175 glVertexAttribPointer( mUvAttrib, 2, GL_FLOAT, GL_FALSE, 0, tco1 );
6176
6177 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
6178 #endif
6179
6180 #if 0
6181 // Render new texture using shader
6182 if(0){
6183
6184 float tx0 = m_fbo_offsetx/divx;
6185 float ty0 = m_fbo_offsety/divy;
6186 float tx = (m_fbo_offsetx + m_fbo_swidth)/divx;
6187 float ty = (m_fbo_offsety + m_fbo_sheight)/divy;
6188
6189 //normal uv
6190 uv[0] = tx0; uv[1] = ty; uv[2] = tx; uv[3] = ty;
6191 uv[4] = tx; uv[5] = ty0; uv[6] = tx0; uv[7] = ty0;
6192
6193 // pixels
6194 coords[0] = 0; coords[1] = 0; coords[2] = sx; coords[3] = 0;
6195 coords[4] = sx; coords[5] = sy; coords[6] = 0; coords[7] = sy;
6196
6197 glUseProgram( fade_texture_2D_shader_program );
6198
6199 // Get pointers to the attributes in the program.
6200 GLint mPosAttrib = glGetAttribLocation( fade_texture_2D_shader_program, "aPos" );
6201 GLint mUvAttrib = glGetAttribLocation( fade_texture_2D_shader_program, "aUV" );
6202
6203 // Set up the texture sampler to texture unit 0
6204 GLint texUni = glGetUniformLocation( fade_texture_2D_shader_program, "uTex" );
6205 glUniform1i( texUni, 0 );
6206
6207 // Disable VBO's (vertex buffer objects) for attributes.
6208 glBindBuffer( GL_ARRAY_BUFFER, 0 );
6209 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
6210
6211 // Set the attribute mPosAttrib with the vertices in the screen coordinates...
6212 glVertexAttribPointer( mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, coords );
6213 // ... and enable it.
6214 glEnableVertexAttribArray( mPosAttrib );
6215
6216 // Set the attribute mUvAttrib with the vertices in the GL coordinates...
6217 glVertexAttribPointer( mUvAttrib, 2, GL_FLOAT, GL_FALSE, 0, uv );
6218 // ... and enable it.
6219 glEnableVertexAttribArray( mUvAttrib );
6220
6221
6222 GLint matloc = glGetUniformLocation(fade_texture_2D_shader_program,"MVMatrix");
6223 glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)(cc1->GetpVP()->vp_transform) );
6224
6225 GLint transloc = glGetUniformLocation(fade_texture_2D_shader_program,"trans");
6226 float colVec[4] = {1.0, 1.0, 1.0, 1.0};
6227 colVec[3] = 1.0 - m_fadeFactor;
6228 glUniform4fv( transloc, 1, colVec );
6229
6230 // Select the active texture unit.
6231 glActiveTexture( GL_TEXTURE0 );
6232
6233 // Bind our texture to the texturing target.
6234 glBindTexture( g_texture_rectangle_format, m_cache_tex[m_cache_page]);
6235
6236
6237 // Perform the actual drawing.
6238
6239 // For some reason, glDrawElements is busted on Android
6240 // So we do this a hard ugly way, drawing two triangles...
6241 #if 1
6242 GLushort indices1[] = {0,1,3,2};
6243 glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, indices1);
6244 #else
6245 float co1[8];
6246 co1[0] = coords[0];
6247 co1[1] = coords[1];
6248 co1[2] = coords[2];
6249 co1[3] = coords[3];
6250 co1[4] = coords[6];
6251 co1[5] = coords[7];
6252 co1[6] = coords[4];
6253 co1[7] = coords[5];
6254
6255 float tco1[8];
6256 tco1[0] = uv[0];
6257 tco1[1] = uv[1];
6258 tco1[2] = uv[2];
6259 tco1[3] = uv[3];
6260 tco1[4] = uv[6];
6261 tco1[5] = uv[7];
6262 tco1[6] = uv[4];
6263 tco1[7] = uv[5];
6264
6265 glVertexAttribPointer( mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, co1 );
6266 glVertexAttribPointer( mUvAttrib, 2, GL_FLOAT, GL_FALSE, 0, tco1 );
6267
6268 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
6269 #endif
6270 }
6271 #endif
6272
6273 glActiveTexture( GL_TEXTURE0 );
6274 glBindTexture( g_texture_rectangle_format, 0);
6275
6276 // Select the active texture unit.
6277 glActiveTexture( GL_TEXTURE1 );
6278 glBindTexture( g_texture_rectangle_format, 0);
6279
6280 m_pParentCanvas->GetPiano()->DrawGL(m_pParentCanvas->m_canvas_height - m_pParentCanvas->GetPiano()->GetHeight());
6281
6282 SwapBuffers();
6283
6284 n_fade++;
6285
6286 m_fadeFactor *= 0.8;
6287 if(m_fadeFactor > 0.1){
6288 m_fadeTimer.Start(5, wxTIMER_ONE_SHOT);
6289 }
6290 else{
6291 //qDebug() << "fade DONE";
6292 m_inFade = false;
6293 m_pParentCanvas->GetPiano()->DrawGL(m_pParentCanvas->m_canvas_height - m_pParentCanvas->GetPiano()->GetHeight());
6294
6295 glDisable(GL_BLEND);
6296
6297 Refresh();
6298 }
6299
6300 #endif
6301 }
6302
6303
fboFade(GLint tex0,GLint tex1)6304 void glChartCanvas::fboFade(GLint tex0, GLint tex1)
6305 {
6306 // we have 2 FBO textures, and we want to fade between the two
6307 m_fadeTexOld = tex0;
6308 m_fadeTexNew = tex1;
6309 m_fadeFactor = 1.0;
6310 n_fade = 0;
6311
6312 s_cachepage = m_cache_page;
6313
6314 m_inFade = true;
6315
6316 // Start a timer
6317 m_fadeTimer.Start(10, wxTIMER_ONE_SHOT);
6318 }
6319
RenderTextures(float * coords,float * uvCoords,int nVertex,ViewPort * vp)6320 void glChartCanvas::RenderTextures(float *coords, float *uvCoords, int nVertex, ViewPort *vp)
6321 {
6322 #ifdef USE_ANDROID_GLES2
6323 int nl = nVertex / 4;
6324 float *lc = coords;
6325 float *luv = uvCoords;
6326
6327 while(nl){
6328 RenderSingleTexture(lc, luv, vp, 0, 0, 0);
6329
6330 lc += 8;
6331 luv += 8;
6332 nl--;
6333 }
6334
6335 #else
6336 glEnableClientState(GL_VERTEX_ARRAY);
6337 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
6338
6339 glTexCoordPointer(2, GL_FLOAT, 2*sizeof(GLfloat), uvCoords);
6340 glVertexPointer(2, GL_FLOAT, 2*sizeof(GLfloat), coords);
6341 glDrawArrays(GL_QUADS, 0, 4);
6342
6343 #endif
6344
6345 return;
6346 }
6347
RenderSingleTexture(float * coords,float * uvCoords,ViewPort * vp,float dx,float dy,float angle_rad)6348 void glChartCanvas::RenderSingleTexture(float *coords, float *uvCoords,ViewPort *vp, float dx, float dy, float angle_rad)
6349 {
6350 #ifdef USE_ANDROID_GLES2
6351 //build_texture_shaders();
6352 glUseProgram( texture_2D_shader_program );
6353
6354 // Get pointers to the attributes in the program.
6355 GLint mPosAttrib = glGetAttribLocation( texture_2D_shader_program, "aPos" );
6356 GLint mUvAttrib = glGetAttribLocation( texture_2D_shader_program, "aUV" );
6357
6358 // Set up the texture sampler to texture unit 0
6359 GLint texUni = glGetUniformLocation( texture_2D_shader_program, "uTex" );
6360 glUniform1i( texUni, 0 );
6361
6362 // Disable VBO's (vertex buffer objects) for attributes.
6363 glBindBuffer( GL_ARRAY_BUFFER, 0 );
6364 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
6365
6366 // Set the attribute mPosAttrib with the vertices in the screen coordinates...
6367 glVertexAttribPointer( mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, coords );
6368 // ... and enable it.
6369 glEnableVertexAttribArray( mPosAttrib );
6370
6371 // Set the attribute mUvAttrib with the vertices in the GL coordinates...
6372 glVertexAttribPointer( mUvAttrib, 2, GL_FLOAT, GL_FALSE, 0, uvCoords );
6373 // ... and enable it.
6374 glEnableVertexAttribArray( mUvAttrib );
6375
6376 // Rotate
6377 mat4x4 I, Q;
6378 mat4x4_identity(I);
6379 mat4x4_rotate_Z(Q, I, angle_rad);
6380
6381 // Translate
6382 Q[3][0] = dx;
6383 Q[3][1] = dy;
6384
6385
6386 //mat4x4 X;
6387 //mat4x4_mul(X, (float (*)[4])vp->vp_transform, Q);
6388
6389 GLint matloc = glGetUniformLocation(texture_2D_shader_program,"TransformMatrix");
6390 glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)Q);
6391
6392 // Select the active texture unit.
6393 glActiveTexture( GL_TEXTURE0 );
6394
6395 // Bind our texture to the texturing target.
6396 //glBindTexture( GL_TEXTURE_2D, tex );
6397
6398 // Perform the actual drawing.
6399
6400 // For some reason, glDrawElements is busted on Android
6401 // So we do this a hard ugly way, drawing two triangles...
6402 #if 0
6403 GLushort indices1[] = {0,1,3,2};
6404 glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, indices1);
6405 #else
6406
6407 float co1[8];
6408 co1[0] = coords[0];
6409 co1[1] = coords[1];
6410 co1[2] = coords[2];
6411 co1[3] = coords[3];
6412 co1[4] = coords[6];
6413 co1[5] = coords[7];
6414 co1[6] = coords[4];
6415 co1[7] = coords[5];
6416
6417 float tco1[8];
6418 tco1[0] = uvCoords[0];
6419 tco1[1] = uvCoords[1];
6420 tco1[2] = uvCoords[2];
6421 tco1[3] = uvCoords[3];
6422 tco1[4] = uvCoords[6];
6423 tco1[5] = uvCoords[7];
6424 tco1[6] = uvCoords[4];
6425 tco1[7] = uvCoords[5];
6426
6427 glVertexAttribPointer( mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, co1 );
6428 glVertexAttribPointer( mUvAttrib, 2, GL_FLOAT, GL_FALSE, 0, tco1 );
6429
6430 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
6431
6432 #endif
6433
6434
6435 #else
6436 glEnableClientState(GL_VERTEX_ARRAY);
6437 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
6438
6439 glPushMatrix();
6440 glTranslatef(dx, dy, 0);
6441 glRotatef(180/PI * angle_rad, 0, 0, 1);
6442
6443 glTexCoordPointer(2, GL_FLOAT, 2*sizeof(GLfloat), uvCoords);
6444 glVertexPointer(2, GL_FLOAT, 2*sizeof(GLfloat), coords);
6445 glDrawArrays(GL_QUADS, 0, 4);
6446 glPopMatrix();
6447
6448 #endif
6449
6450 return;
6451 }
6452
6453
RenderColorRect(wxRect r,wxColor & color)6454 void glChartCanvas::RenderColorRect(wxRect r, wxColor &color)
6455 {
6456
6457 #ifdef USE_ANDROID_GLES2
6458 glUseProgram(color_tri_shader_program);
6459
6460 float pf[8];
6461 pf[0] = r.x + r.width; pf[1] = r.y; pf[2] = r.x; pf[3] = r.y; pf[4] = r.x + r.width; pf[5] = r.y + r.height;
6462 pf[6] = r.x; pf[7] = r.y + r.height;
6463
6464 GLint pos = glGetAttribLocation(color_tri_shader_program, "position");
6465 glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), pf);
6466 glEnableVertexAttribArray(pos);
6467
6468 GLint matloc = glGetUniformLocation(color_tri_shader_program,"MVMatrix");
6469 glUniformMatrix4fv( matloc, 1, GL_FALSE, (const GLfloat*)m_pParentCanvas->GetpVP()->vp_transform);
6470
6471 float colorv[4];
6472 colorv[0] = color.Red() / float(256);
6473 colorv[1] = color.Green() / float(256);
6474 colorv[2] = color.Blue() / float(256);
6475 colorv[3] = 1.0;
6476
6477 GLint colloc = glGetUniformLocation(color_tri_shader_program,"color");
6478 glUniform4fv(colloc, 1, colorv);
6479
6480
6481 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
6482
6483 // pf[0] = r.x; pf[1] = r.y; pf[2] = r.x + r.width; pf[3] = r.y + r.height; pf[4] = r.x; pf[5] = r.y + r.height;
6484
6485 // glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), pf);
6486 // glEnableVertexAttribArray(pos);
6487 // glDrawArrays(GL_TRIANGLES, 0, 3);
6488
6489
6490 #else
6491 glColor3ub(color.Red(), color.Green(), color.Blue());
6492
6493 glBegin( GL_QUADS );
6494 glVertex2f( r.x, r.y );
6495 glVertex2f( r.x + r.width, r.y );
6496 glVertex2f( r.x + r.width, r.y + r.height );
6497 glVertex2f( r.x, r.y + r.height );
6498 glEnd();
6499 #endif
6500 }
6501
RenderScene(bool bRenderCharts,bool bRenderOverlays)6502 void glChartCanvas::RenderScene( bool bRenderCharts, bool bRenderOverlays )
6503 {
6504 //qDebug() << "RenderScene";
6505
6506 #ifdef USE_ANDROID_GLES2
6507
6508 ViewPort VPoint = m_pParentCanvas->VPoint;
6509 ocpnDC gldc( *this );
6510
6511 int w, h;
6512 GetClientSize( &w, &h );
6513 int sx = GetSize().x;
6514 int sy = GetSize().y;
6515
6516 OCPNRegion screen_region(wxRect(0, 0, VPoint.pix_width, VPoint.pix_height));
6517
6518 glViewport( 0, 0, (GLint) w, (GLint) h );
6519
6520 if( s_b_useStencil ) {
6521 glEnable( GL_STENCIL_TEST );
6522 glStencilMask( 0xff );
6523 glClear( GL_STENCIL_BUFFER_BIT );
6524 glDisable( GL_STENCIL_TEST );
6525 }
6526
6527 // Make sure we have a valid quilt composition
6528 m_pParentCanvas->m_pQuilt->Compose(m_pParentCanvas->VPoint);
6529
6530 // set opengl settings that don't normally change
6531 // this should be able to go in SetupOpenGL, but it's
6532 // safer here incase a plugin mangles these
6533 glHint( GL_LINE_SMOOTH_HINT, GL_NICEST );
6534 glHint( GL_POLYGON_SMOOTH_HINT, GL_NICEST );
6535 glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
6536
6537
6538
6539 // enable rendering to texture in framebuffer object
6540 glBindFramebuffer( GL_FRAMEBUFFER_EXT, m_fb0 );
6541
6542 glFramebufferTexture2D( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0, g_texture_rectangle_format, m_cache_tex[m_cache_page], 0 );
6543
6544 m_fbo_offsetx = 0;
6545 m_fbo_offsety = 0;
6546 m_fbo_swidth = sx;
6547 m_fbo_sheight = sy;
6548
6549 if(bRenderCharts)
6550 RenderCharts(gldc, screen_region);
6551
6552 if(bRenderOverlays){
6553 RenderS57TextOverlay( m_pParentCanvas->VPoint );
6554 RenderMBTilesOverlay( m_pParentCanvas->VPoint );
6555 DrawStaticRoutesTracksAndWaypoints( m_pParentCanvas->VPoint );
6556 DrawDynamicRoutesTracksAndWaypoints( VPoint );
6557 DrawFloatingOverlayObjects( m_gldc );
6558 }
6559
6560 // All done, so disable Render to FBO
6561 glBindFramebuffer( GL_FRAMEBUFFER, 0 );
6562
6563 #endif
6564
6565 }
6566