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