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 if (IsShown()) 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 if (IsShown()) 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 (auto i = region.contours.begin(); i != region.contours.end(); i++) {
2860 gluTessBeginContour(tobj);
2861 contour_pt l = *i->rbegin();
2862 double sml[2];
2863 bool sml_valid = false;
2864 for (poly_contour::const_iterator j = i->begin(); j != i->end(); j++) {
2865 int lat_splits = floor(fabs(j->y - l.y) / lat_dist);
2866 int lon_splits = floor(fabs(j->x - l.x) / lon_dist);
2867 int splits = wxMax(lat_splits, lon_splits) + 1;
2868
2869 double smj[2];
2870 if (splits != 1) {
2871 // must perform border interpolation in mercator space as this is what
2872 // the charts use
2873 toSM(j->y, j->x, 0, 0, smj + 0, smj + 1);
2874 if (!sml_valid) toSM(l.y, l.x, 0, 0, sml + 0, sml + 1);
2875 }
2876
2877 for (int i = 0; i < splits; i++) {
2878 double lat, lon;
2879 if (i == splits - 1)
2880 lat = j->y, lon = j->x;
2881 else {
2882 double d = (double)(i + 1) / splits;
2883 fromSM(d * smj[0] + (1 - d) * sml[0], d * smj[1] + (1 - d) * sml[1],
2884 0, 0, &lat, &lon);
2885 }
2886 wxPoint2DDouble q = vp.GetDoublePixFromLL(lat, lon);
2887 if (std::isnan(q.m_x)) continue;
2888
2889 double *p = new double[6];
2890
2891 // p[0] = q.m_x, p[1] = q.m_y, p[2] = 0;
2892 // It is reasonable to use wxRound() here,
2893 // since we are working with pixel coordinates at this point
2894 p[0] = wxRound(q.m_x), p[1] = wxRound(q.m_y), p[2] = 0;
2895
2896 // wxPoint pt = vp.GetPixFromLL(lat, lon);
2897 // p[0] = pt.x, p[1] = pt.y, p[2] = 0;
2898
2899 gluTessVertex(tobj, p, p);
2900 combine_work_data.push_back(p);
2901 }
2902 l = *j;
2903
2904 if ((sml_valid = splits != 1)) memcpy(sml, smj, sizeof smj);
2905 }
2906 gluTessEndContour(tobj);
2907 }
2908 gluTessEndPolygon(tobj);
2909
2910 gluDeleteTess(tobj);
2911
2912 for (std::list<double *>::iterator i = combine_work_data.begin();
2913 i != combine_work_data.end(); i++)
2914 delete[] *i;
2915 combine_work_data.clear();
2916}
2917
2918/* set stencil buffer to clip in this region, and optionally clear using the
2919 * current color */
2920void glChartCanvas::SetClipRegion(ViewPort &vp, const LLRegion &region) {
2921 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // disable color buffer
2922
2923 if (s_b_useStencil) {
2924 // Create a stencil buffer for clipping to the region
2925 glEnable(GL_STENCIL_TEST);
2926 glStencilMask(0x1); // write only into bit 0 of the stencil buffer
2927 glClear(GL_STENCIL_BUFFER_BIT);
2928
2929 // We are going to write "1" into the stencil buffer wherever the region
2930 // is valid
2931 glStencilFunc(GL_ALWAYS, 1, 1);
2932 glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
2933 }
2934// #ifndef USE_ANDROID_GLES2
2935#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
2936
2937 else // Use depth buffer for clipping
2938 {
2939 glEnable(GL_DEPTH_TEST); // to enable writing to the depth buffer
2940 glDepthFunc(GL_ALWAYS); // to ensure everything you draw passes
2941 glDepthMask(GL_TRUE); // to allow writes to the depth buffer
2942
2943 glClear(GL_DEPTH_BUFFER_BIT); // for a fresh start
2944
2945 // Decompose the region into rectangles, and draw as quads
2946 // With z = 1
2947 // dep buffer clear = 1
2948 // 1 makes 0 in dep buffer, works
2949 // 0 make .5 in depth buffer
2950 // -1 makes 1 in dep buffer
2951
2952 // Depth buffer runs from 0 at z = 1 to 1 at z = -1
2953 // Draw the clip geometry at z = 0.5, giving a depth buffer value of 0.25
2954 // Subsequent drawing at z=0 (depth = 0.5) will pass if using
2955 // glDepthFunc(GL_GREATER);
2956 glTranslatef(0, 0, .5);
2957 }
2958#endif
2959
2960 s_regionColor = wxColor(0, 0, 0, 255);
2961 DrawRegion(vp, region);
2962
2963 if (s_b_useStencil) {
2964 // Now set the stencil ops to subsequently render only where the stencil
2965 // bit is "1"
2966 glStencilFunc(GL_EQUAL, 1, 1);
2967 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
2968 }
2969// #ifndef USE_ANDROID_GLES2
2970#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
2971 else {
2972 glDepthFunc(GL_GREATER); // Set the test value
2973 glDepthMask(GL_FALSE); // disable depth buffer
2974 glTranslatef(0, 0, -.5); // reset translation
2975 }
2976#endif
2977 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // re-enable color buffer
2978}
2979
2980void glChartCanvas::SetClipRect(const ViewPort &vp, const wxRect &rect,
2981 bool b_clear) {
2982 /* for some reason this causes an occasional bug in depth mode, I cannot
2983 seem to solve it yet, so for now: */
2984 if (s_b_useStencil && s_b_useScissorTest) {
2985 wxRect vp_rect(0, 0, vp.pix_width, vp.pix_height);
2986 if (rect != vp_rect) {
2987 glEnable(GL_SCISSOR_TEST);
2988 glScissor(rect.x, vp.pix_height - rect.height - rect.y, rect.width,
2989 rect.height);
2990 }
2991#ifndef USE_ANDROID_GLES2
2992#endif
2993 return;
2994 }
2995}
2996
2997void glChartCanvas::DisableClipRegion() {
2998 glDisable(GL_SCISSOR_TEST);
2999 glDisable(GL_STENCIL_TEST);
3000 glDisable(GL_DEPTH_TEST);
3001}
3002
3003void glChartCanvas::Invalidate() {
3004 /* should probably use a different flag for this */
3005 m_cache_vp.Invalidate();
3006}
3007
3008void glChartCanvas::RenderRasterChartRegionGL(ChartBase *chart, ViewPort &vp,
3009 LLRegion &region) {
3010 ChartBaseBSB *pBSBChart = dynamic_cast<ChartBaseBSB *>(chart);
3011 if (!pBSBChart) return;
3012
3014 return; // don't want multiple texfactories to exist
3015
3016 // Look for the texture factory for this chart
3017 wxString key = chart->GetHashKey();
3018
3019 glTexFactory *pTexFact;
3020 ChartPathHashTexfactType &hash = g_glTextureManager->m_chart_texfactory_hash;
3021 ChartPathHashTexfactType::iterator ittf = hash.find(key);
3022
3023 // Not Found ?
3024 if (ittf == hash.end()) {
3025 hash[key] = new glTexFactory(chart, g_raster_format);
3026 hash[key]->SetHashKey(key);
3027 }
3028
3029 pTexFact = hash[key];
3030 pTexFact->SetLRUTime(++m_LRUtime);
3031
3032 // for small scales, don't use normalized coordinates for accuracy (difference
3033 // is up to 3 meters error)
3034 bool use_norm_vp =
3035 glChartCanvas::HasNormalizedViewPort(vp) && pBSBChart->GetPPM() < 1;
3036 pTexFact->PrepareTiles(vp, use_norm_vp, pBSBChart);
3037
3038 // For underzoom cases, we will define the textures as having their base
3039 // levels equivalent to a level "n" mipmap, where n is calculated, and is
3040 // always binary This way we can avoid loading much texture memory
3041
3042 int base_level;
3043 if (vp.m_projection_type == PROJECTION_MERCATOR &&
3044 chart->GetChartProjectionType() == PROJECTION_MERCATOR) {
3045 double scalefactor = pBSBChart->GetRasterScaleFactor(vp);
3046 base_level = log(scalefactor) / log(2.0);
3047
3048 if (base_level < 0) /* for overzoom */
3049 base_level = 0;
3050 if (base_level > g_mipmap_max_level) base_level = g_mipmap_max_level;
3051 } else
3052 base_level = 0; // base level should be computed per tile, for now load all
3053
3054 /* setup opengl parameters */
3055 glEnable(GL_TEXTURE_2D);
3056#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
3057 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3058
3059 glEnableClientState(GL_VERTEX_ARRAY);
3060 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
3061
3062 if (use_norm_vp) {
3063 glPushMatrix();
3064 double lat, lon;
3065 pTexFact->GetCenter(lat, lon);
3066 MultMatrixViewPort(vp, lat, lon);
3067 }
3068#endif
3069
3070 LLBBox box = region.GetBox();
3071 int numtiles;
3072 int mem_used = 0;
3073 if (g_memCacheLimit > 0) {
3074 // GetMemoryStatus is slow on linux
3075 platform::GetMemoryStatus(0, &mem_used);
3076 }
3077
3078 glTexTile **tiles = pTexFact->GetTiles(numtiles);
3079 for (int i = 0; i < numtiles; i++) {
3080 glTexTile *tile = tiles[i];
3081 if (region.IntersectOut(tile->box)) {
3082 /* user setting is in MB while we count exact bytes */
3083 bool bGLMemCrunch =
3084 g_tex_mem_used > g_GLOptions.m_iTextureMemorySize * 1024 * 1024;
3085 if (bGLMemCrunch) pTexFact->DeleteTexture(tile->rect);
3086 } else {
3087 bool texture = pTexFact->PrepareTexture(base_level, tile->rect,
3088 global_color_scheme, mem_used);
3089
3090 float *coords;
3091 if (use_norm_vp)
3092 coords = tile->m_coords;
3093 else {
3094 coords = new float[2 * tile->m_ncoords];
3095 for (int i = 0; i < tile->m_ncoords; i++) {
3096 wxPoint2DDouble p = vp.GetDoublePixFromLL(tile->m_coords[2 * i + 0],
3097 tile->m_coords[2 * i + 1]);
3098 coords[2 * i + 0] = p.m_x;
3099 coords[2 * i + 1] = p.m_y;
3100 }
3101 }
3102
3103#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
3104 RenderTextures(m_gldc, coords, tile->m_texcoords, 4,
3105 m_pParentCanvas->GetpVP());
3106#else
3107 if (!texture) { // failed to load, draw red
3108 glDisable(GL_TEXTURE_2D);
3109 glColor3f(1, 0, 0);
3110 }
3111
3112 glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), tile->m_texcoords);
3113 glVertexPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), coords);
3114 glDrawArrays(GL_QUADS, 0, tile->m_ncoords);
3115#endif
3116 if (!texture) glEnable(GL_TEXTURE_2D);
3117
3118 if (!use_norm_vp) delete[] coords;
3119 }
3120 }
3121
3122 glDisable(GL_TEXTURE_2D);
3123
3124#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
3125 if (use_norm_vp) glPopMatrix();
3126
3127 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
3128 glDisableClientState(GL_VERTEX_ARRAY);
3129#endif
3130}
3131
3132void glChartCanvas::RenderQuiltViewGL(ViewPort &vp,
3133 const OCPNRegion &rect_region) {
3134 if (!m_pParentCanvas->m_pQuilt->GetnCharts() ||
3135 m_pParentCanvas->m_pQuilt->IsBusy())
3136 return;
3137
3138 // render the quilt
3139 ChartBase *chart = m_pParentCanvas->m_pQuilt->GetFirstChart();
3140 if (!chart) {
3141 printf(" Chart NULL\n");
3142 chart = m_pParentCanvas->m_pQuilt->GetFirstChart();
3143 }
3144
3145 // Check the first, smallest scale chart
3146 if (chart) {
3147 // if( ! m_pParentCanvas->IsChartLargeEnoughToRender( chart, vp )
3148 // )
3149 // chart = NULL;
3150 }
3151
3152 LLRegion region = vp.GetLLRegion(rect_region);
3153
3154 LLRegion rendered_region;
3155 while (chart) {
3156 // This test does not need to be done for raster charts, since
3157 // we can assume that texture binding is acceptably fast regardless of the
3158 // render region, and that the quilt zoom methods choose a reasonable
3159 // reference chart.
3160 if (chart->GetChartFamily() != CHART_FAMILY_RASTER) {
3161 // if( ! m_pParentCanvas->IsChartLargeEnoughToRender(
3162 // chart, vp ) ) {
3163 // chart = m_pParentCanvas->m_pQuilt->GetNextChart();
3164 // continue;
3165 // }
3166 }
3167
3168 QuiltPatch *pqp = m_pParentCanvas->m_pQuilt->GetCurrentPatch();
3169 if (pqp->b_Valid) {
3170 LLRegion get_region = pqp->ActiveRegion;
3171 bool b_rendered = false;
3172
3173 if (!pqp->b_overlay) {
3174 get_region.Intersect(region);
3175 if (!get_region.Empty()) {
3176 if (chart->GetChartFamily() == CHART_FAMILY_RASTER) {
3177 ChartBaseBSB *Patch_Ch_BSB = dynamic_cast<ChartBaseBSB *>(chart);
3178 if (Patch_Ch_BSB) {
3179 SetClipRegion(vp, get_region /*pqp->quilt_region*/);
3180 RenderRasterChartRegionGL(chart, vp, pqp->ActiveRegion);
3181 DisableClipRegion();
3182
3183 b_rendered = true;
3184 } else if (chart->GetChartType() == CHART_TYPE_MBTILES) {
3185 SetClipRegion(vp, pqp->ActiveRegion /*pqp->quilt_region*/);
3186 chart->RenderRegionViewOnGL(*m_pcontext, vp, rect_region,
3187 get_region);
3188 DisableClipRegion();
3189 }
3190
3191 } else if (chart->GetChartFamily() == CHART_FAMILY_VECTOR) {
3192 if (chart->GetChartType() == CHART_TYPE_CM93COMP) {
3193 RenderNoDTA(vp, get_region);
3194 chart->RenderRegionViewOnGL(*m_pcontext, vp, rect_region,
3195 get_region);
3196 } else {
3197 s57chart *Chs57 = dynamic_cast<s57chart *>(chart);
3198 if (Chs57) {
3199 if (Chs57->m_RAZBuilt) {
3200 RenderNoDTA(vp, get_region);
3201 Chs57->RenderRegionViewOnGLNoText(*m_pcontext, vp,
3202 rect_region, get_region);
3203 DisableClipRegion();
3204 } else {
3205 // The SENC is quesed for building, so..
3206 // Show GSHHS with compatible color scheme in the meantime.
3207 ocpnDC gldc(*this);
3208 const LLRegion &oregion = get_region;
3209 LLBBox box = oregion.GetBox();
3210
3211 wxPoint p1 =
3212 vp.GetPixFromLL(box.GetMaxLat(), box.GetMinLon());
3213 wxPoint p2 =
3214 vp.GetPixFromLL(box.GetMaxLat(), box.GetMaxLon());
3215 wxPoint p3 =
3216 vp.GetPixFromLL(box.GetMinLat(), box.GetMaxLon());
3217 wxPoint p4 =
3218 vp.GetPixFromLL(box.GetMinLat(), box.GetMinLon());
3219
3220 wxRect srect(p1.x, p1.y, p3.x - p1.x, p4.y - p2.y);
3221
3222 bool world = false;
3223 ViewPort cvp = ClippedViewport(vp, get_region);
3224 if (m_pParentCanvas->GetWorldBackgroundChart()) {
3225 SetClipRegion(cvp, get_region);
3226 m_pParentCanvas->GetWorldBackgroundChart()->SetColorsDirect(
3227 GetGlobalColor("LANDA"), GetGlobalColor("DEPMS"));
3228 RenderWorldChart(gldc, cvp, srect, world);
3229 m_pParentCanvas->GetWorldBackgroundChart()->SetColorScheme(
3230 global_color_scheme);
3231 DisableClipRegion();
3232 }
3233 }
3234 } else {
3235 ChartPlugInWrapper *ChPI =
3236 dynamic_cast<ChartPlugInWrapper *>(chart);
3237 if (ChPI) {
3238 SetClipRegion(vp, get_region);
3239 RenderNoDTA(vp, get_region);
3240 ChPI->RenderRegionViewOnGLNoText(*m_pcontext, vp, rect_region,
3241 get_region);
3242 DisableClipRegion();
3243
3244 } else {
3245 SetClipRegion(vp, get_region);
3246 RenderNoDTA(vp, get_region);
3247 chart->RenderRegionViewOnGL(*m_pcontext, vp, rect_region,
3248 get_region);
3249 DisableClipRegion();
3250 }
3251 }
3252 }
3253 }
3254 }
3255 }
3256
3257 if (b_rendered) {
3258 // LLRegion get_region = pqp->ActiveRegion;
3259 // get_region.Intersect( Region ); not technically
3260 // required?
3261 // rendered_region.Union(get_region);
3262 }
3263 }
3264
3265 chart = m_pParentCanvas->m_pQuilt->GetNextChart();
3266 }
3267
3268 // Render any Overlay patches for s57 charts(cells)
3269 if (m_pParentCanvas->m_pQuilt->HasOverlays()) {
3270 ChartBase *pch = m_pParentCanvas->m_pQuilt->GetFirstChart();
3271 while (pch) {
3272 QuiltPatch *pqp = m_pParentCanvas->m_pQuilt->GetCurrentPatch();
3273 if (pqp->b_Valid && pqp->b_overlay &&
3274 pch->GetChartFamily() == CHART_FAMILY_VECTOR) {
3275 LLRegion get_region = pqp->ActiveRegion;
3276
3277 get_region.Intersect(region);
3278 if (!get_region.Empty()) {
3279 s57chart *Chs57 = dynamic_cast<s57chart *>(pch);
3280 if (Chs57)
3281 Chs57->RenderOverlayRegionViewOnGL(*m_pcontext, vp, rect_region,
3282 get_region);
3283 else {
3284 ChartPlugInWrapper *ChPI = dynamic_cast<ChartPlugInWrapper *>(pch);
3285 if (ChPI) {
3286 ChPI->RenderRegionViewOnGL(*m_pcontext, vp, rect_region,
3287 get_region);
3288 }
3289 }
3290 }
3291 }
3292
3293 pch = m_pParentCanvas->m_pQuilt->GetNextChart();
3294 }
3295 }
3296
3297 // Hilite rollover of standard chart key
3298 ViewPort vph = m_pParentCanvas->GetVP();
3299 for (auto &index : m_pParentCanvas->m_pQuilt->GetHiLiteIndexArray()) {
3300 const ChartTableEntry &cte = ChartData->GetChartTableEntry(index);
3301 LLRegion hiregion =
3302 m_pParentCanvas->m_pQuilt->GetChartQuiltRegion(cte, vph);
3303
3304 if (!hiregion.Empty()) {
3305 glEnable(GL_BLEND);
3306
3307 double hitrans;
3308 switch (global_color_scheme) {
3309 case GLOBAL_COLOR_SCHEME_DAY:
3310 hitrans = .4;
3311 break;
3312 case GLOBAL_COLOR_SCHEME_DUSK:
3313 hitrans = .2;
3314 break;
3315 case GLOBAL_COLOR_SCHEME_NIGHT:
3316 hitrans = .1;
3317 break;
3318 default:
3319 hitrans = .4;
3320 break;
3321 }
3322
3323#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
3324
3325 glColor4f((float).8, (float).4, (float).4, (float)hitrans);
3326#else
3327 s_regionColor = wxColor(204, 102, 102, hitrans * 256);
3328#endif
3329
3330 DrawRegion(vp, hiregion);
3331
3332 glDisable(GL_BLEND);
3333 }
3334 }
3335
3336#if 0
3337 LLRegion hiregion = m_pParentCanvas->m_pQuilt->GetHiliteRegion();
3338
3339 if (!hiregion.Empty()) {
3340 glEnable(GL_BLEND);
3341
3342 double hitrans;
3343 switch (global_color_scheme) {
3344 case GLOBAL_COLOR_SCHEME_DAY:
3345 hitrans = .4;
3346 break;
3347 case GLOBAL_COLOR_SCHEME_DUSK:
3348 hitrans = .2;
3349 break;
3350 case GLOBAL_COLOR_SCHEME_NIGHT:
3351 hitrans = .1;
3352 break;
3353 default:
3354 hitrans = .4;
3355 break;
3356 }
3357
3358//#ifndef USE_ANDROID_GLES2
3359#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
3360
3361 glColor4f((float).8, (float).4, (float).4, (float)hitrans);
3362#else
3363 s_regionColor = wxColor(204, 102, 102, hitrans * 256);
3364#endif
3365
3366 DrawRegion(vp, hiregion);
3367
3368 glDisable(GL_BLEND);
3369 }
3370#endif
3371
3372 m_pParentCanvas->m_pQuilt->SetRenderedVP(vp);
3373}
3374
3375void glChartCanvas::RenderQuiltViewGLText(ViewPort &vp,
3376 const OCPNRegion &rect_region) {
3377 if (!m_pParentCanvas->m_pQuilt->GetnCharts() ||
3378 m_pParentCanvas->m_pQuilt->IsBusy())
3379 return;
3380
3381 // render the quilt
3382 ChartBase *chart = m_pParentCanvas->m_pQuilt->GetLargestScaleChart();
3383
3384 LLRegion region = vp.GetLLRegion(rect_region);
3385
3386 LLRegion rendered_region;
3387 while (chart) {
3388 QuiltPatch *pqp = m_pParentCanvas->m_pQuilt->GetCurrentPatch();
3389 if (pqp->b_Valid) {
3390 LLRegion get_region = pqp->ActiveRegion;
3391
3392 if (!pqp->b_overlay) {
3393 if (chart->GetChartFamily() == CHART_FAMILY_VECTOR) {
3394 s57chart *Chs57 = dynamic_cast<s57chart *>(chart);
3395 if (Chs57) {
3396 Chs57->RenderViewOnGLTextOnly(*m_pcontext, vp);
3397 } else {
3398 ChartPlugInWrapper *ChPI =
3399 dynamic_cast<ChartPlugInWrapper *>(chart);
3400 if (ChPI) {
3401 ChPI->RenderRegionViewOnGLTextOnly(*m_pcontext, vp, rect_region);
3402 }
3403 }
3404 }
3405 }
3406 }
3407
3408 chart = m_pParentCanvas->m_pQuilt->GetNextSmallerScaleChart();
3409 }
3410
3411 /*
3412 // Render any Overlay patches for s57 charts(cells)
3413 if( m_pParentCanvas->m_pQuilt->HasOverlays() ) {
3414 ChartBase *pch = m_pParentCanvas->m_pQuilt->GetFirstChart();
3415 while( pch ) {
3416 QuiltPatch *pqp =
3417 m_pParentCanvas->m_pQuilt->GetCurrentPatch(); if( pqp->b_Valid &&
3418 pqp->b_overlay && pch->GetChartFamily() == CHART_FAMILY_VECTOR ) { LLRegion
3419 get_region = pqp->ActiveRegion;
3420
3421 get_region.Intersect( region );
3422 if( !get_region.Empty() ) {
3423 s57chart *Chs57 = dynamic_cast<s57chart*>( pch );
3424 if( Chs57 )
3425 Chs57->RenderOverlayRegionViewOnGL( *m_pcontext,
3426 vp, rect_region, get_region );
3427 }
3428 }
3429
3430 pch = m_pParentCanvas->m_pQuilt->GetNextChart();
3431 }
3432 }
3433 */
3434}
3435
3436void glChartCanvas::RenderCharts(ocpnDC &dc, const OCPNRegion &rect_region) {
3437 ViewPort &vp = m_pParentCanvas->VPoint;
3438
3439 // Only for cm93 (not quilted), SetVPParms can change the valid region of the
3440 // chart we need to know this before rendering the chart so we can compute the
3441 // background region and nodta regions correctly. I would prefer to just
3442 // perform this here (or in SetViewPoint) for all vector charts instead of in
3443 // their render routine, but how to handle quilted cases?
3444 if (!vp.b_quilt &&
3445 m_pParentCanvas->m_singleChart->GetChartType() == CHART_TYPE_CM93COMP)
3446 static_cast<cm93compchart *>(m_pParentCanvas->m_singleChart)
3447 ->SetVPParms(vp);
3448
3449 LLRegion chart_region;
3450 if (!vp.b_quilt &&
3451 (m_pParentCanvas->m_singleChart->GetChartType() == CHART_TYPE_PLUGIN)) {
3452 if (m_pParentCanvas->m_singleChart->GetChartFamily() ==
3453 CHART_FAMILY_RASTER) {
3454 // We do this the hard way, since PlugIn Raster charts do not understand
3455 // LLRegion yet...
3456 double ll[8];
3457 ChartPlugInWrapper *cpw =
3458 dynamic_cast<ChartPlugInWrapper *>(m_pParentCanvas->m_singleChart);
3459 if (!cpw) return;
3460
3461 cpw->chartpix_to_latlong(0, 0, ll + 0, ll + 1);
3462 cpw->chartpix_to_latlong(0, cpw->GetSize_Y(), ll + 2, ll + 3);
3463 cpw->chartpix_to_latlong(cpw->GetSize_X(), cpw->GetSize_Y(), ll + 4,
3464 ll + 5);
3465 cpw->chartpix_to_latlong(cpw->GetSize_X(), 0, ll + 6, ll + 7);
3466
3467 // for now don't allow raster charts to cross both 0 meridian and IDL
3468 // (complicated to deal with)
3469 for (int i = 1; i < 6; i += 2)
3470 if (fabs(ll[i] - ll[i + 2]) > 180) {
3471 // we detect crossing idl here, make all longitudes positive
3472 for (int i = 1; i < 8; i += 2)
3473 if (ll[i] < 0) ll[i] += 360;
3474 break;
3475 }
3476
3477 chart_region = LLRegion(4, ll);
3478 } else {
3479 Extent ext;
3480 m_pParentCanvas->m_singleChart->GetChartExtent(&ext);
3481
3482 double ll[8] = {ext.SLAT, ext.WLON, ext.SLAT, ext.ELON,
3483 ext.NLAT, ext.ELON, ext.NLAT, ext.WLON};
3484 chart_region = LLRegion(4, ll);
3485 }
3486 } else
3487 chart_region = vp.b_quilt
3488 ? m_pParentCanvas->m_pQuilt->GetFullQuiltRegion()
3489 : m_pParentCanvas->m_singleChart->GetValidRegion();
3490
3491 bool world_view = false;
3492 for (OCPNRegionIterator upd(rect_region); upd.HaveRects(); upd.NextRect()) {
3493 wxRect rect = upd.GetRect();
3494 LLRegion background_region = vp.GetLLRegion(rect);
3495 // Remove the valid chart area to find the region NOT covered by the
3496 // charts
3497 background_region.Subtract(chart_region);
3498
3499 if (!background_region.Empty()) {
3500 ViewPort cvp = ClippedViewport(vp, background_region);
3501 SetClipRect(cvp, rect, false);
3502 RenderWorldChart(dc, cvp, rect, world_view);
3503 DisableClipRegion();
3504 }
3505 }
3506
3507 if (vp.b_quilt) {
3508 RenderQuiltViewGL(vp, rect_region);
3509 int yyp = 4;
3510 } else {
3511 LLRegion region = vp.GetLLRegion(rect_region);
3512 if (m_pParentCanvas->m_singleChart->GetChartFamily() ==
3513 CHART_FAMILY_RASTER) {
3514 if (m_pParentCanvas->m_singleChart->GetChartType() == CHART_TYPE_MBTILES)
3515 m_pParentCanvas->m_singleChart->RenderRegionViewOnGL(
3516 *m_pcontext, vp, rect_region, region);
3517 else
3518 RenderRasterChartRegionGL(m_pParentCanvas->m_singleChart, vp, region);
3519 } else if (m_pParentCanvas->m_singleChart->GetChartFamily() ==
3520 CHART_FAMILY_VECTOR) {
3521 chart_region.Intersect(region);
3522 RenderNoDTA(vp, chart_region);
3523 m_pParentCanvas->m_singleChart->RenderRegionViewOnGL(*m_pcontext, vp,
3524 rect_region, region);
3525 }
3526 }
3527 glUseProgram(0);
3528}
3529
3530void glChartCanvas::RenderNoDTA(ViewPort &vp, const LLRegion &region,
3531 int transparency) {
3532 wxColour color = GetGlobalColor("NODTA");
3533#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
3534 if (color.IsOk())
3535 glColor4ub(color.Red(), color.Green(), color.Blue(), transparency);
3536 else
3537 glColor4ub(163, 180, 183, transparency);
3538
3539 glEnable(GL_BLEND);
3540 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
3541
3542#else
3543 // Store the color for tesselator callback pickup.
3544 s_regionColor = color;
3545#endif
3546
3547 DrawRegion(vp, region);
3548}
3549
3550/* render world chart, but only in this rectangle */
3551void glChartCanvas::RenderWorldChart(ocpnDC &dc, ViewPort &vp, wxRect &rect,
3552 bool &world_view) {
3553 // set gl color to water
3554 wxColour water = m_pParentCanvas->pWorldBackgroundChart->water;
3555
3556 glEnable(GL_SCISSOR_TEST);
3557 glScissor(rect.x, vp.pix_height - rect.height - rect.y, rect.width,
3558 rect.height);
3559
3560 // clear background
3561 if (!world_view) {
3562 if (!world_view) {
3563 int x1 = rect.x, y1 = rect.y, x2 = x1 + rect.width, y2 = y1 + rect.height;
3564#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
3565
3566 GLShaderProgram *shader = pcolor_tri_shader_program[GetCanvasIndex()];
3567 shader->Bind();
3568
3569 float colorv[4];
3570 colorv[0] = water.Red() / float(256);
3571 colorv[1] = water.Green() / float(256);
3572 colorv[2] = water.Blue() / float(256);
3573 colorv[3] = 1.0;
3574 shader->SetUniform4fv("color", colorv);
3575
3576 float pf[8];
3577 pf[0] = x2;
3578 pf[1] = y1;
3579 pf[2] = x2;
3580 pf[3] = y2;
3581 pf[4] = x1;
3582 pf[5] = y1;
3583 pf[6] = x1;
3584 pf[7] = y2;
3585 shader->SetAttributePointerf("position", pf);
3586
3587 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
3588
3589 shader->UnBind();
3590
3591#else
3592#endif
3593 }
3594 }
3595
3596 // m_pParentCanvas->pWorldBackgroundChart->RenderViewOnDC(dc, vp);
3597 gShapeBasemap.RenderViewOnDC(dc, vp);
3598
3599 glDisable(GL_SCISSOR_TEST);
3600}
3601
3602/* these are the overlay objects which move with the charts and
3603 are not frequently updated (not ships etc..)
3604
3605 many overlay objects are fixed to a geographical location or
3606 grounded as opposed to the floating overlay objects. */
3607void glChartCanvas::DrawGroundedOverlayObjects(ocpnDC &dc, ViewPort &vp) {
3608 m_pParentCanvas->RenderAllChartOutlines(dc, vp);
3609
3610 DrawStaticRoutesTracksAndWaypoints(vp);
3611
3612 DisableClipRegion();
3613}
3614
3615void glChartCanvas::DrawGLTidesInBBox(ocpnDC &dc, LLBBox &BBox) {
3616 // At small scale, we render the Tide icon as a texture for best performance
3617 if (m_pParentCanvas->GetVP().chart_scale > 500000) {
3618 // Prepare the texture if necessary
3619
3620 if (!m_tideTex) {
3621 wxBitmap bmp = m_pParentCanvas->GetTideBitmap();
3622 if (!bmp.Ok()) return;
3623
3624 wxImage image = bmp.ConvertToImage();
3625 int w = image.GetWidth(), h = image.GetHeight();
3626
3627 int tex_w, tex_h;
3628 if (g_texture_rectangle_format == GL_TEXTURE_2D)
3629 tex_w = w, tex_h = h;
3630 else
3631 tex_w = NextPow2(w), tex_h = NextPow2(h);
3632
3633 m_tideTexWidth = tex_w;
3634 m_tideTexHeight = tex_h;
3635
3636 unsigned char *d = image.GetData();
3637 unsigned char *a = image.GetAlpha();
3638
3639 unsigned char mr, mg, mb;
3640 if (!a) image.GetOrFindMaskColour(&mr, &mg, &mb);
3641
3642 unsigned char *e = new unsigned char[4 * w * h];
3643 if (e && d) {
3644 for (int y = 0; y < h; y++)
3645 for (int x = 0; x < w; x++) {
3646 unsigned char r, g, b;
3647 int off = (y * w + x);
3648 r = d[off * 3 + 0];
3649 g = d[off * 3 + 1];
3650 b = d[off * 3 + 2];
3651
3652 e[off * 4 + 0] = r;
3653 e[off * 4 + 1] = g;
3654 e[off * 4 + 2] = b;
3655
3656 e[off * 4 + 3] =
3657 a ? a[off] : ((r == mr) && (g == mg) && (b == mb) ? 0 : 255);
3658 }
3659 }
3660
3661 glGenTextures(1, &m_tideTex);
3662
3663 glBindTexture(GL_TEXTURE_2D, m_tideTex);
3664 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3665 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3666
3667 if (g_texture_rectangle_format == GL_TEXTURE_2D)
3668 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA,
3669 GL_UNSIGNED_BYTE, e);
3670 else {
3671 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex_w, tex_h, 0, GL_RGBA,
3672 GL_UNSIGNED_BYTE, 0);
3673 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE,
3674 e);
3675 }
3676
3677 delete[] e;
3678 }
3679
3680 // Texture is ready
3681
3682 glBindTexture(GL_TEXTURE_2D, m_tideTex);
3683 glEnable(GL_TEXTURE_2D);
3684 glEnable(GL_BLEND);
3685
3686#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
3687#else
3688 for (int i = 1; i < ptcmgr->Get_max_IDX() + 1; i++) {
3689 const IDX_entry *pIDX = ptcmgr->GetIDX_entry(i);
3690
3691 char type = pIDX->IDX_type; // Entry "TCtcIUu" identifier
3692 if ((type == 't') || (type == 'T')) // only Tides
3693 {
3694 double lon = pIDX->IDX_lon;
3695 double lat = pIDX->IDX_lat;
3696
3697 if (BBox.Contains(lat, lon)) {
3698 wxPoint r;
3699 m_pParentCanvas->GetCanvasPointPix(lat, lon, &r);
3700
3701 float xp = r.x;
3702 float yp = r.y;
3703
3704 double scale = 1.0;
3705#ifdef __ANDROID__
3706 scale *= getAndroidDisplayDensity();
3707#endif
3708 double width2 = scale * m_tideTexWidth / 2;
3709 double height2 = scale * m_tideTexHeight / 2;
3710
3711 float coords[8];
3712 float uv[8];
3713
3714 // normal uv
3715 uv[0] = 0;
3716 uv[1] = 0;
3717 uv[2] = 0;
3718 uv[3] = 1;
3719 uv[4] = 1;
3720 uv[5] = 1;
3721 uv[6] = 1;
3722 uv[7] = 0;
3723
3724 coords[0] = xp - width2;
3725 coords[1] = yp - height2;
3726 coords[2] = xp - width2;
3727 coords[3] = yp + height2;
3728 coords[4] = xp + width2;
3729 coords[5] = yp + height2;
3730 coords[6] = xp + width2;
3731 coords[7] = yp - height2;
3732
3733 RenderTextures(dc, coords, uv, 4, m_pParentCanvas->GetpVP());
3734 }
3735 } // type 'T"
3736 } // loop
3737
3738#endif
3739
3740 glDisable(GL_TEXTURE_2D);
3741 glDisable(GL_BLEND);
3742 glBindTexture(GL_TEXTURE_2D, 0);
3743 } else
3744 m_pParentCanvas->DrawAllTidesInBBox(dc, BBox);
3745}
3746
3747void glChartCanvas::DrawGLCurrentsInBBox(ocpnDC &dc, LLBBox &BBox) {
3748 m_pParentCanvas->DrawAllCurrentsInBBox(dc, BBox);
3749}
3750
3751void glChartCanvas::SetColorScheme(ColorScheme cs) {
3752 if (!m_bsetup) return;
3753
3754 glDeleteTextures(1, &m_tideTex);
3755 glDeleteTextures(1, &m_currentTex);
3756 m_tideTex = 0;
3757 m_currentTex = 0;
3758 ownship_color = -1;
3759}
3760
3761void glChartCanvas::RenderGLAlertMessage() {
3762 if (!m_pParentCanvas->GetAlertString().IsEmpty()) {
3763 wxString msg = m_pParentCanvas->GetAlertString();
3764
3765 wxFont *pfont = GetOCPNScaledFont(_("Dialog"));
3766 m_gldc.SetFont(*pfont);
3767
3768 int w, h;
3769 wxScreenDC sdc;
3770 sdc.GetTextExtent(msg, &w, &h, NULL, NULL, pfont);
3771
3772 h += 2;
3773 w += 4;
3774 int yp =
3775 m_pParentCanvas->VPoint.pix_height - GetChartbarHeight() - h - (h / 4);
3776
3777 wxRect sbr = m_pParentCanvas->GetScaleBarRect();
3778 int xp = sbr.x + sbr.width + 5;
3779
3780 wxPen ppPen1(GetGlobalColor("UBLCK"), 1, wxPENSTYLE_SOLID);
3781 m_gldc.SetPen(ppPen1);
3782 m_gldc.SetBrush(wxBrush(GetGlobalColor("YELO1")));
3783
3784 m_gldc.DrawRectangle(xp, yp, w, h);
3785
3786 m_gldc.DrawText(msg, xp, yp);
3787 }
3788}
3789
3790unsigned long quiltHash;
3791int refChartIndex;
3792extern wxLongLong s_t0;
3793
3794int n_render;
3795void glChartCanvas::Render() {
3796 if (!m_bsetup || !m_pParentCanvas->m_pQuilt ||
3797 (m_pParentCanvas->VPoint.b_quilt && !m_pParentCanvas->m_pQuilt) ||
3798 (!m_pParentCanvas->VPoint.b_quilt && !m_pParentCanvas->m_singleChart)) {
3799#ifdef __WXGTK__ // for some reason in gtk, a swap is needed here to get an
3800 // initial screen update
3801 SwapBuffers();
3802#endif
3803 if (!g_PrintingInProgress) return;
3804 }
3805
3806 if (!g_true_zoom && m_binPinch) return;
3807
3808 // if (m_binPinch) printf(" %ld Render Start\n", m_glstopwatch.Time());
3809 long render_start_time = m_glstopwatch.Time();
3810
3811#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
3812 loadShaders(GetCanvasIndex());
3813 configureShaders(m_pParentCanvas->VPoint);
3814#endif
3815
3816#ifdef USE_ANDROID_GLES2
3817
3818 OCPNStopWatch sw;
3819
3820 if (m_binPinch) return;
3821
3822 // qDebug() << "Render" << m_pParentCanvas->m_canvasIndex << GetPosition().x
3823 // << GetSize().x << m_pParentCanvas->GetPosition().x << m_pcontext;
3824
3825 // if(m_pParentCanvas->m_canvasIndex == 0) return;
3826
3827 // Do any setup required...
3828
3829 bool recompose = false;
3830 if (m_pParentCanvas->VPoint.b_quilt && m_pParentCanvas->m_pQuilt &&
3831 !m_pParentCanvas->m_pQuilt->IsComposed()) {
3832 if (m_pParentCanvas->VPoint.IsValid()) {
3833 m_pParentCanvas->m_pQuilt->Compose(m_pParentCanvas->VPoint);
3834 m_pParentCanvas->UpdateCanvasControlBar();
3835 recompose = true;
3836 } else
3837 return;
3838 }
3839
3840 // Check to see if the Compose() call forced a SENC build.
3841 // If so, zoom the canvas just slightly to force a deferred redraw of the
3842 // full screen.
3843 if (sw.GetTime() > 2000) { // long enough to detect SENC build.
3844 m_pParentCanvas->ZoomCanvas(1.0001, false);
3845 }
3846
3847 // qDebug() << "RenderTime1" << sw.GetTime();
3848
3849 s_tess_vertex_idx = 0;
3850 quiltHash = m_pParentCanvas->m_pQuilt->GetXStackHash();
3851 refChartIndex = m_pParentCanvas->m_pQuilt->GetRefChartdbIndex();
3852
3853#endif
3854
3855#ifdef __WXOSX__
3856 // Support scaled HDPI displays.
3857 m_displayScale = GetContentScaleFactor();
3858#endif
3859 m_pParentCanvas->VPoint.SetPixelScale(m_displayScale);
3860
3861 m_last_render_time = wxDateTime::Now().GetTicks();
3862
3863 // we don't care about jobs that are now off screen
3864 // clear out and it will be repopulated during render
3865 if (g_GLOptions.m_bTextureCompression &&
3866 !g_GLOptions.m_bTextureCompressionCaching)
3867 g_glTextureManager->ClearJobList();
3868
3869 ocpnDC gldc(*this);
3870
3871 int gl_width, gl_height;
3872 gl_width = m_pParentCanvas->VPoint.pix_width;
3873 gl_height = m_pParentCanvas->VPoint.pix_height;
3874
3875 // Take a copy for use later by DC
3876 m_glcanvas_width = gl_width;
3877 m_glcanvas_height = gl_height;
3878
3879 // Avoid some harmonic difficulties with odd-size glCanvas
3880 bool b_odd = false;
3881 if (gl_height & 1) {
3882 gl_height -= 1;
3883 ViewPort *vp = m_pParentCanvas->GetpVP();
3884 vp->pix_height = gl_height;
3885 b_odd = true;
3886 }
3887
3888 if (gl_width & 1) {
3889 gl_width -= 1;
3890 ViewPort *vp = m_pParentCanvas->GetpVP();
3891 vp->pix_width = gl_width;
3892 b_odd = true;
3893 }
3894
3895 // Set the shader viewport transform matrix
3896 // Using the adjusted dimensions
3897 if (b_odd) {
3898 ViewPort *vp = m_pParentCanvas->GetpVP();
3899 mat4x4 m;
3900 mat4x4_identity(m);
3901 mat4x4_scale_aniso((float(*)[4])vp->vp_matrix_transform, m,
3902 2.0 / (float)vp->pix_width, -2.0 / (float)vp->pix_height,
3903 1.0);
3904 mat4x4_translate_in_place((float(*)[4])vp->vp_matrix_transform,
3905 -vp->pix_width / 2, -vp->pix_height / 2, 0);
3906 }
3907
3908 // @todo: If the intention was to work with the same ViewPort object, use a
3909 // reference instead. Making a copy of VPoint here means that any changes to
3910 // VPoint will not affect m_pParentCanvas->VPoint. It's not clear if this is
3911 // the intended behavior.
3912 ViewPort VPoint = m_pParentCanvas->VPoint;
3913
3914 OCPNRegion screen_region(wxRect(0, 0, gl_width, gl_height));
3915 glViewport(0, 0, (GLint)gl_width, (GLint)gl_height);
3916
3917// #ifndef USE_ANDROID_GLES2
3918#if !defined(USE_ANDROID_GLES2)
3919 glMatrixMode(GL_PROJECTION);
3920 glLoadIdentity();
3921
3922 glOrtho(0, (GLint)gl_width, (GLint)gl_height, 0, -1, 1);
3923 glMatrixMode(GL_MODELVIEW);
3924 glLoadIdentity();
3925#endif
3926
3927 if (s_b_useStencil) {
3928 glEnable(GL_STENCIL_TEST);
3929 glStencilMask(0xff);
3930 glClear(GL_STENCIL_BUFFER_BIT);
3931 glDisable(GL_STENCIL_TEST);
3932 }
3933
3934 // set opengl settings that don't normally change
3935 // this should be able to go in SetupOpenGL, but it's
3936 // safer here incase a plugin mangles these
3937 if (g_GLOptions.m_GLLineSmoothing) glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
3938 if (g_GLOptions.m_GLPolygonSmoothing)
3939 glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
3940 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
3941
3942 // Delete any textures known to the GPU that
3943 // belong to charts which will not be used in this render
3944 // This is done chart-by-chart...later we will scrub for unused textures
3945 // that belong to charts which ARE used in this render, if we need to....
3946
3947 g_glTextureManager->TextureCrunch(0.8);
3948
3949 // If we plan to post process the display, don't use accelerated panning
3950 double scale_factor = VPoint.ref_scale / VPoint.chart_scale;
3951
3952 bool bpost_hilite = !m_pParentCanvas->m_pQuilt->GetHiliteRegion().Empty();
3953 bool useFBO = false;
3954 int sx = gl_width;
3955 int sy = gl_height;
3956
3957 // Try to use the framebuffer object's cache of the last frame
3958 // to accelerate drawing this frame (if overlapping)
3959 if (m_b_BuiltFBO && !bpost_hilite
3960 //&& VPoint.tilt == 0 // disabling fbo in tilt mode gives better quality
3961 // but slower
3962 ) {
3963 // Is this viewpoint the same as the previously painted one?
3964 bool b_newview = true;
3965 bool b_full = false;
3966
3967 // If the view is the same we do no updates,
3968 // Just render cached texture to the framebuffer
3969 if (m_cache_vp.view_scale_ppm == VPoint.view_scale_ppm &&
3970 m_cache_vp.rotation == VPoint.rotation &&
3971 m_cache_vp.clat == VPoint.clat && m_cache_vp.clon == VPoint.clon &&
3972 m_cache_vp.IsValid() && m_cache_vp.pix_height == VPoint.pix_height &&
3973 m_cache_current_ch == m_pParentCanvas->m_singleChart) {
3974 b_newview = false;
3975 }
3976
3977#ifdef USE_ANDROID_GLES2
3978 if (recompose) b_newview = true;
3979
3980 if (m_bforcefull) {
3981 b_newview = true;
3982 b_full = true;
3983 }
3984
3985 // If no charts are to be rendered, we need to refresh the entire display
3986 // This fixes a problem with routes/tracks/marks rendering on pans at very
3987 // small scale. It is a workaround, so finding root cause should be
3988 // considered a TODO
3989
3990 if (VPoint.b_quilt) {
3991 ChartBase *chart = m_pParentCanvas->m_pQuilt->GetFirstChart();
3992 if (!chart) b_full = true;
3993 }
3994
3995#endif
3996
3997 if (b_newview) {
3998 float dx = 0;
3999 float dy = 0;
4000
4001 bool accelerated_pan = false;
4002 // if (g_in_inertia)
4003 // printf("--- accpan condition %d %d\n",
4004 // g_GLOptions.m_bUseAcceleratedPanning,
4005 // m_cache_vp.IsValid());
4006 // else
4007 // printf("||| accpan condition %d %d\n",
4008 // g_GLOptions.m_bUseAcceleratedPanning,
4009 // m_cache_vp.IsValid());
4010
4011 if (g_GLOptions.m_bUseAcceleratedPanning && m_cache_vp.IsValid() &&
4012 (VPoint.m_projection_type == PROJECTION_MERCATOR ||
4013 VPoint.m_projection_type == PROJECTION_EQUIRECTANGULAR) &&
4014 m_cache_vp.pix_height == VPoint.pix_height) {
4015 wxPoint2DDouble c_old =
4016 VPoint.GetDoublePixFromLL(VPoint.clat, VPoint.clon) *
4017 m_displayScale;
4018 wxPoint2DDouble c_new =
4019 m_cache_vp.GetDoublePixFromLL(VPoint.clat, VPoint.clon) *
4020 m_displayScale;
4021
4022 dy = wxRound(c_new.m_y - c_old.m_y);
4023 dx = wxRound(c_new.m_x - c_old.m_x);
4024
4025 // The math below using the previous frame's texture does not really
4026 // work for sub-pixel pans.
4027 // TODO is to rethink this.
4028 // Meanwhile, require the accelerated pans to be whole pixel multiples
4029 // only. This is not as bad as it sounds. Keyboard and mouse pans are
4030 // whole_pixel moves. However, autofollow at large scale is certainly
4031 // not.
4032
4033 double deltax = c_new.m_x - c_old.m_x;
4034 double deltay = c_new.m_y - c_old.m_y;
4035
4036 bool b_whole_pixel = true;
4037 if ((fabs(deltax - dx) > 1e-2) || (fabs(deltay - dy) > 1e-2))
4038 b_whole_pixel = false;
4039
4040 accelerated_pan = b_whole_pixel && abs(dx) < m_cache_tex_x &&
4041 abs(dy) < m_cache_tex_y &&
4042 (abs(dx) > 0 || (abs(dy) > 0));
4043
4044 // if (g_in_inertia && !accelerated_pan)
4045 // printf("--- accpan %d %d %g %g\n", accelerated_pan,
4046 // b_whole_pixel, dx, dy);
4047 }
4048
4049 // FBO swapping has trouble with Retina display on MacOS Monterey.
4050 // So, disable accelerated pan ops on this case.
4051 if (m_displayScale > 1) accelerated_pan = false;
4052
4053 // FIXME (dave) There are some display artifact troubles using accPan on
4054 // rotation.
4055 // Especially seen on sparse RNC rendering
4056 if (fabs(VPoint.rotation) > 0) accelerated_pan = false;
4057
4058 // do we allow accelerated panning? can we perform it here?
4059#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
4060#else // GLES2
4061 // enable rendering to texture in framebuffer object
4062 glBindFramebuffer(GL_FRAMEBUFFER, m_fb0);
4063
4064 if (VPoint.chart_scale < 5000) b_full = true;
4065
4066 if (VPoint.chart_scale > 5e7) b_full = true;
4067
4068 if (b_full) accelerated_pan = false;
4069
4070 if (accelerated_pan) {
4071 if ((dx != 0) || (dy != 0)) { // Anything to do?
4072
4073 // calculate the new regions to render
4074 // add extra pixels to avoid coordindate rounding issues at large
4075 // scale
4076 OCPNRegion update_region;
4077
4078 int fluff = 2;
4079
4080 // Avoid rendering artifacts caused by Multi Sampling (MSAA)
4081 if (VPoint.chart_scale < 10000) fluff = 8;
4082
4083 if (dy > 0 && dy < gl_height)
4084 update_region.Union(
4085 wxRect(0, gl_height - (dy + fluff), gl_width, dy + fluff));
4086 else if (dy < 0)
4087 update_region.Union(wxRect(0, 0, gl_width, -dy + fluff));
4088
4089 if (dx > 0 && dx < gl_width)
4090 update_region.Union(
4091 wxRect(gl_width - (dx + fluff), 0, dx + fluff, gl_height));
4092 else if (dx < 0)
4093 update_region.Union(wxRect(0, 0, -dx + fluff, gl_height));
4094
4095 m_cache_page = !m_cache_page; /* page flip */
4096
4097 // Bind the destination (target frame) texture to the frame buffer
4098 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
4099 GL_TEXTURE_2D, m_cache_tex[m_cache_page], 0);
4100
4101 // Before rendering anything, clear the color buffers
4102 // wxColour color = GetGlobalColor("NODTA");
4103 // glClearColor(color.Red() / 256., color.Green() / 256.,
4104 // color.Blue() / 256., 1.0);
4105 // glClear(GL_COLOR_BUFFER_BIT);
4106
4107 // First render the new content into the update region
4108 // if (g_in_inertia)
4109 // printf("--- R2a %g\n",
4110 // (wxGetLocalTimeMillis() - s_t0).ToDouble());
4111 RenderCharts(m_gldc, update_region);
4112 // if (g_in_inertia)
4113 // printf("--- R2b %g\n",
4114 // (wxGetLocalTimeMillis() - s_t0).ToDouble());
4115 glDisable(g_texture_rectangle_format);
4116 glUseProgram(0);
4117
4118 // Next, render the cached texture as quad to FBO(m_blit_tex) with
4119 // offsets
4120 glBindTexture(GL_TEXTURE_2D, m_cache_tex[!m_cache_page]);
4121 glEnable(GL_TEXTURE_2D);
4122
4123 // Blit the existing content onto the alternate FBO, at the correct
4124 // location
4125 float x1, x2, y1, y2;
4126
4127 if (dx > 0)
4128 x1 = dx, x2 = 0;
4129 else
4130 x1 = 0, x2 = -dx;
4131
4132 if (dy > 0)
4133 y1 = dy, y2 = 0;
4134 else
4135 y1 = 0, y2 = -dy;
4136
4137 // normalize to texture coordinates range from 0 to 1
4138 float tx1, tx2, ty1, ty2;
4139
4140 float xcor = 0;
4141 float ycor = 0;
4142
4143 tx1 = 0;
4144 tx2 = sx / (float)m_cache_tex_x;
4145 ty1 = 0;
4146 ty2 = sy / (float)m_cache_tex_y;
4147
4148 float coords[8];
4149 float uv[8];
4150
4151 // normal uv
4152 uv[0] = tx1;
4153 uv[1] = ty1;
4154 uv[2] = tx2;
4155 uv[3] = ty1;
4156 uv[4] = tx2;
4157 uv[5] = ty2;
4158 uv[6] = tx1;
4159 uv[7] = ty2;
4160
4161 coords[0] = -dx;
4162 coords[1] = dy;
4163 coords[2] = -dx + sx;
4164 coords[3] = dy;
4165 coords[4] = -dx + sx;
4166 coords[5] = dy + sy;
4167 coords[6] = -dx;
4168 coords[7] = dy + sy;
4169
4170 GLShaderProgram *shader =
4171 ptexture_2D_shader_program[GetCanvasIndex()];
4172 shader->Bind();
4173
4174 // Set up the texture sampler to texture unit 0
4175 shader->SetUniform1i("uTex", 0);
4176
4177 mat4x4 m, mvp, I;
4178 mat4x4_identity(m);
4179 mat4x4_scale_aniso(mvp, m, 2.0 / (float)sx, 2.0 / (float)sy, 1.0);
4180 mat4x4_translate_in_place(mvp, -(float)sx / 2, -(float)sy / 2, 0);
4181 shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)mvp);
4182 mat4x4_identity(I);
4183 shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)I);
4184
4185 float co1[8];
4186 co1[0] = coords[0];
4187 co1[1] = coords[1];
4188 co1[2] = coords[2];
4189 co1[3] = coords[3];
4190 co1[4] = coords[6];
4191 co1[5] = coords[7];
4192 co1[6] = coords[4];
4193 co1[7] = coords[5];
4194
4195 float tco1[8];
4196 tco1[0] = uv[0];
4197 tco1[1] = uv[1];
4198 tco1[2] = uv[2];
4199 tco1[3] = uv[3];
4200 tco1[4] = uv[6];
4201 tco1[5] = uv[7];
4202 tco1[6] = uv[4];
4203 tco1[7] = uv[5];
4204
4205 shader->SetAttributePointerf("aPos", co1);
4206 shader->SetAttributePointerf("aUV", tco1);
4207
4208 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
4209
4210 // restore the shader matrix
4211 shader->SetUniformMatrix4fv("MVMatrix",
4212 (GLfloat *)VPoint.vp_matrix_transform);
4213
4214 shader->UnBind();
4215 glBindTexture(g_texture_rectangle_format, 0);
4216
4217 glDisable(g_texture_rectangle_format);
4218 glUseProgram(0);
4219 }
4220
4221 } // accelerated pan
4222
4223 else { // must redraw the entire screen
4224 // qDebug() << "Fullpage";
4225 glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0,
4226 g_texture_rectangle_format,
4227 m_cache_tex[!m_cache_page], 0);
4228
4229 m_fbo_offsetx = 0;
4230 m_fbo_offsety = 0;
4231 m_fbo_swidth = sx;
4232 m_fbo_sheight = sy;
4233
4234 // FIXME (dave) test on Android
4235 // This can be annoying on Android pinch zoom
4236
4237 // Clear the screen to NODTA color
4238 wxColour color = GetGlobalColor("NODTA");
4239 glClearColor(color.Red() / 256., color.Green() / 256.,
4240 color.Blue() / 256., 1.0);
4241 glClear(GL_COLOR_BUFFER_BIT);
4242
4243 OCPNRegion rscreen_region(VPoint.rv_rect);
4244 // if (g_in_inertia)
4245 // printf("--- R2c %g\n",
4246 // (wxGetLocalTimeMillis() - s_t0).ToDouble());
4247 RenderCharts(m_gldc, rscreen_region);
4248 // if (g_in_inertia)
4249 // printf("--- R2d %g\n",
4250 // (wxGetLocalTimeMillis() - s_t0).ToDouble());
4251
4252 m_cache_page = !m_cache_page; /* page flip */
4253
4254 } // full page render
4255
4256 // Disable Render to FBO
4257 glBindFramebuffer(GL_FRAMEBUFFER, 0);
4258
4259#endif // gles2 for accpan
4260
4261 } // newview
4262
4263 useFBO = true;
4264 }
4265
4266#ifndef __ANDROID__
4267 if (VPoint.tilt) {
4268 glMatrixMode(GL_PROJECTION);
4269 glLoadIdentity();
4270
4271 gluPerspective(2 * 180 / PI * atan2((double)gl_height, (double)gl_width),
4272 (GLfloat)gl_width / (GLfloat)gl_height, 1, gl_width);
4273
4274 glMatrixMode(GL_MODELVIEW);
4275 glLoadIdentity();
4276
4277 glScalef(1, -1, 1);
4278 glTranslatef(-gl_width / 2, -gl_height / 2, -gl_width / 2);
4279 glRotated(VPoint.tilt * 180 / PI, 1, 0, 0);
4280
4281 glGetIntegerv(GL_VIEWPORT, viewport);
4282 glGetDoublev(GL_PROJECTION_MATRIX, projmatrix);
4283 glGetDoublev(GL_MODELVIEW_MATRIX, mvmatrix);
4284 }
4285#endif
4286
4287 // if (g_in_inertia)
4288 // printf("--- R3 %g\n", (wxGetLocalTimeMillis() - s_t0).ToDouble());
4289
4290 if (useFBO) {
4291#if 0 // #ifndef USE_ANDROID_GLES2
4292 glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fb0);
4293 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
4294 glBlitFramebuffer(0, 0, sx, sy, 0, 0, sx*2, sy*2, GL_COLOR_BUFFER_BIT, GL_LINEAR);
4295
4296 glBindFramebuffer(GL_FRAMEBUFFER, 0);
4297
4298#else
4299 // Render the cached texture as quad to screen
4300 glBindTexture(g_texture_rectangle_format, m_cache_tex[m_cache_page]);
4301 glEnable(g_texture_rectangle_format);
4302
4303 float tx, ty, tx0, ty0, divx, divy;
4304
4305 // Normalize, or not?
4306 if (GL_TEXTURE_RECTANGLE_ARB == g_texture_rectangle_format) {
4307 divx = divy = 1.0f;
4308 } else {
4309 divx = m_cache_tex_x;
4310 divy = m_cache_tex_y;
4311 }
4312
4313 tx0 = m_fbo_offsetx / divx;
4314 ty0 = m_fbo_offsety / divy;
4315 tx = (m_fbo_offsetx + m_fbo_swidth) / divx;
4316 ty = (m_fbo_offsety + m_fbo_sheight) / divy;
4317
4318 float coords[8];
4319 float uv[8];
4320
4321 // normal uv
4322 uv[0] = tx0;
4323 uv[1] = ty;
4324 uv[2] = tx;
4325 uv[3] = ty;
4326 uv[4] = tx;
4327 uv[5] = ty0;
4328 uv[6] = tx0;
4329 uv[7] = ty0;
4330
4331 // pixels
4332 coords[0] = 0;
4333 coords[1] = 0;
4334 coords[2] = sx;
4335 coords[3] = 0;
4336 coords[4] = sx;
4337 coords[5] = sy;
4338 coords[6] = 0;
4339 coords[7] = sy;
4340
4341 wxColour color = GetGlobalColor("NODTA");
4342 glClearColor(color.Red() / 256., color.Green() / 256., color.Blue() / 256.,
4343 1.0);
4344 glClear(GL_COLOR_BUFFER_BIT);
4345
4346 RenderTextures(gldc, coords, uv, 4, m_pParentCanvas->GetpVP());
4347#endif
4348
4349 glDisable(g_texture_rectangle_format);
4350
4351 m_cache_vp = VPoint;
4352 m_cache_vp.Validate();
4353
4354 m_cache_current_ch = m_pParentCanvas->m_singleChart;
4355
4356 if (VPoint.b_quilt) m_pParentCanvas->m_pQuilt->SetRenderedVP(VPoint);
4357
4358 } else // useFBO
4359 {
4360 RenderCharts(m_gldc, screen_region);
4361 }
4362
4363 // if (m_binPinch)
4364 // printf(" Render Charts Done %ld\n",
4365 // m_glstopwatch.Time() - render_start_time);
4366
4367 // Done with base charts.
4368 // Now the overlays
4369 RenderS57TextOverlay(VPoint);
4370 RenderMBTilesOverlay(VPoint);
4371
4372 g_overlayCanvas = m_pParentCanvas;
4373 if (g_pi_manager) {
4374 g_pi_manager->SendViewPortToRequestingPlugIns(VPoint);
4375 g_pi_manager->RenderAllGLCanvasOverlayPlugIns(
4376 m_pcontext, VPoint, m_pParentCanvas->m_canvasIndex, OVERLAY_CHARTS);
4377 }
4378
4379 // Render static overlay objects
4380 for (OCPNRegionIterator upd(screen_region); upd.HaveRects(); upd.NextRect()) {
4381 wxRect rt = upd.GetRect();
4382 LLRegion region = VPoint.GetLLRegion(rt);
4383 ViewPort cvp = ClippedViewport(VPoint, region);
4384 DrawGroundedOverlayObjects(gldc, cvp);
4385 }
4386
4387 if (m_pParentCanvas->m_bShowTide || m_pParentCanvas->m_bShowCurrent) {
4388 LLRegion screenLLRegion = VPoint.GetLLRegion(screen_region);
4389 LLBBox screenBox = screenLLRegion.GetBox();
4390 // Enlarge the box a bit
4391 screenBox.EnLarge(screenBox.GetLonRange() * 0.05);
4392
4393 // update the tide/current select points, if necessary
4394 if (m_pParentCanvas->m_bShowTide) {
4395 m_pParentCanvas->RebuildTideSelectList(screenBox); // full screen
4396 DrawGLTidesInBBox(gldc, VPoint.GetBBox());
4397 }
4398
4399 if (m_pParentCanvas->m_bShowCurrent) {
4400 m_pParentCanvas->RebuildCurrentSelectList(screenBox);
4401 DrawGLCurrentsInBBox(gldc, VPoint.GetBBox());
4402 }
4403 }
4404
4405 // If multi-canvas, indicate which canvas has keyboard focus
4406 // by drawing a simple blue bar at the top.
4407 if (m_pParentCanvas->m_show_focus_bar &&
4408 (g_canvasConfig != 0)) { // multi-canvas?
4409 if (m_pParentCanvas == wxWindow::FindFocus()) {
4410 g_focusCanvas = m_pParentCanvas;
4411
4412 wxColour colour = GetGlobalColor("BLUE4");
4413 wxPen ppBlue(colour, 1);
4414 wxBrush ppBrush(colour);
4415 gldc.SetPen(ppBlue);
4416 gldc.SetBrush(ppBrush);
4417 int xw = m_pParentCanvas->GetClientSize().x * m_displayScale;
4418 float rect_pix = m_pParentCanvas->m_focus_indicator_pix * m_displayScale;
4419 wxPoint barPoints[4];
4420 barPoints[0].x = 0;
4421 barPoints[0].y = 0;
4422 barPoints[1].x = xw;
4423 barPoints[1].y = 0;
4424 barPoints[2].x = xw;
4425 barPoints[2].y = rect_pix;
4426 barPoints[3].x = 0;
4427 barPoints[3].y = rect_pix;
4428
4429 gldc.DrawPolygon(4, barPoints, 0, 0, 1, 0);
4430 }
4431 }
4432
4433 DrawDynamicRoutesTracksAndWaypoints(VPoint);
4434
4435 // Now draw all the objects which normally move around and are not
4436 // cached from the previous frame
4437 DrawFloatingOverlayObjects(m_gldc);
4438
4439#ifndef USE_ANDROID_GLES2
4440 // from this point on don't use perspective
4441 if (VPoint.tilt) {
4442 glMatrixMode(GL_PROJECTION);
4443 glLoadIdentity();
4444
4445 glOrtho(0, (GLint)gl_width, (GLint)gl_height, 0, -1, 1);
4446 glMatrixMode(GL_MODELVIEW);
4447 glLoadIdentity();
4448 }
4449#endif
4450
4451 if (!g_bhide_depth_units)
4452 DrawEmboss(m_gldc, m_pParentCanvas->EmbossDepthScale());
4453 if (!g_bhide_overzoom_flag)
4454 DrawEmboss(m_gldc, m_pParentCanvas->EmbossOverzoomIndicator(gldc));
4455
4456 if (g_pi_manager) {
4457 ViewPort &vp = m_pParentCanvas->GetVP();
4458 g_pi_manager->SendViewPortToRequestingPlugIns(vp);
4459 g_pi_manager->RenderAllGLCanvasOverlayPlugIns(
4460 m_pcontext, vp, m_pParentCanvas->m_canvasIndex, OVERLAY_OVER_EMBOSS);
4461 }
4462 if (!g_PrintingInProgress) {
4463 if (m_pParentCanvas->m_pTrackRolloverWin)
4464 m_pParentCanvas->m_pTrackRolloverWin->Draw(gldc);
4465
4466 if (m_pParentCanvas->m_pRouteRolloverWin)
4467 m_pParentCanvas->m_pRouteRolloverWin->Draw(gldc);
4468
4469 if (m_pParentCanvas->m_pAISRolloverWin)
4470 m_pParentCanvas->m_pAISRolloverWin->Draw(gldc);
4471
4472 if (m_pParentCanvas->GetMUIBar())
4473 m_pParentCanvas->GetMUIBar()->DrawGL(gldc, m_displayScale);
4474
4475 if (g_MainToolbar && m_pParentCanvas->IsPrimaryCanvas())
4476 g_MainToolbar->DrawGL(gldc, m_displayScale);
4477
4478 if (g_iENCToolbar && m_pParentCanvas->IsPrimaryCanvas())
4479 g_iENCToolbar->DrawGL(gldc, m_displayScale);
4480 }
4481
4482 // On some platforms, the opengl context window is always on top of any
4483 // standard DC windows, so we need to draw the Chart Info Window
4484 // as overlayed bmps.
4485
4486#ifdef __WXOSX__
4487 if (m_pParentCanvas->m_pCIWin && m_pParentCanvas->m_pCIWin->IsShown()) {
4488 int x, y, width, height;
4489 m_pParentCanvas->m_pCIWin->GetClientSize(&width, &height);
4490 m_pParentCanvas->m_pCIWin->GetPosition(&x, &y);
4491 wxBitmap bmp(width, height, -1);
4492 wxMemoryDC dc(bmp);
4493 if (bmp.IsOk()) {
4494 dc.SetBackground(wxBrush(GetGlobalColor("UIBCK")));
4495 dc.Clear();
4496
4497 dc.SetTextBackground(GetGlobalColor("UIBCK"));
4498 dc.SetTextForeground(GetGlobalColor("UITX1"));
4499
4500 int yt = 0;
4501 int xt = 0;
4502 wxString s = m_pParentCanvas->m_pCIWin->GetString();
4503 int h = m_pParentCanvas->m_pCIWin->GetCharHeight();
4504
4505 wxStringTokenizer tkz(s, "\n");
4506 wxString token;
4507
4508 while (tkz.HasMoreTokens()) {
4509 token = tkz.GetNextToken();
4510 dc.DrawText(token, xt, yt);
4511 yt += h;
4512 }
4513 dc.SelectObject(wxNullBitmap);
4514
4515 m_gldc.DrawBitmap(bmp, x, y, false);
4516 }
4517 }
4518
4519#endif
4520 // render the chart bar
4521 if (g_bShowChartBar) DrawChartBar(m_gldc);
4522
4523 if (m_pParentCanvas->m_Compass && m_pParentCanvas->m_bShowCompassWin &&
4524 g_bShowCompassWin)
4525 m_pParentCanvas->m_Compass->Paint(gldc);
4526
4527 if (m_pParentCanvas->IsPrimaryCanvas()) {
4528 auto &noteman = NotificationManager::GetInstance();
4529 if (noteman.GetNotificationCount()) {
4530 m_pParentCanvas->m_notification_button->SetIconSeverity(
4531 noteman.GetMaxSeverity());
4532 if (m_pParentCanvas->m_notification_button->UpdateStatus()) Refresh();
4533 m_pParentCanvas->m_notification_button->Show(true);
4534 m_pParentCanvas->m_notification_button->Paint(gldc);
4535 } else {
4536 m_pParentCanvas->m_notification_button->Show(false);
4537 }
4538 }
4539 RenderGLAlertMessage();
4540
4541 if (g_pi_manager) {
4542 ViewPort &vp = m_pParentCanvas->GetVP();
4543 g_pi_manager->SendViewPortToRequestingPlugIns(vp);
4544 g_pi_manager->RenderAllGLCanvasOverlayPlugIns(
4545 m_pcontext, vp, m_pParentCanvas->m_canvasIndex, OVERLAY_OVER_UI);
4546 glActiveTexture(GL_TEXTURE0);
4547 }
4548
4549 // quiting?
4550 if (g_bquiting) DrawQuiting();
4551 if (g_bcompression_wait)
4552 DrawCloseMessage(_("Waiting for raster chart compression thread exit."));
4553
4554 // Some older MSW OpenGL drivers are generally very unstable.
4555 // This helps...
4556
4557 if (g_b_needFinish) glFinish();
4558
4559 SwapBuffers();
4560
4561 g_glTextureManager->TextureCrunch(0.8);
4562 g_glTextureManager->FactoryCrunch(0.6);
4563
4564 m_pParentCanvas->PaintCleanup();
4565 m_bforcefull = false;
4566
4567 // if (m_binPinch)
4568 // printf(" Render Finished: %ld\n",
4569 // m_glstopwatch.Time() - render_start_time);
4570
4571 n_render++;
4572}
4573
4574void glChartCanvas::RenderS57TextOverlay(ViewPort &VPoint) {
4575 // Render the decluttered Text overlay for quilted vector charts, except for
4576 // CM93 Composite
4577 if (VPoint.b_quilt) {
4578 if (m_pParentCanvas->m_pQuilt->IsQuiltVector() && ps52plib &&
4579 ps52plib->GetShowS57Text()) {
4580 ChartBase *chart = m_pParentCanvas->m_pQuilt->GetRefChart();
4581 if (chart && (chart->GetChartType() != CHART_TYPE_CM93COMP)) {
4582 // Clear the text Global declutter list
4583 if (chart) {
4584 ChartPlugInWrapper *ChPI = dynamic_cast<ChartPlugInWrapper *>(chart);
4585 if (ChPI)
4586 ChPI->ClearPLIBTextList();
4587 else
4588 ps52plib->ClearTextList();
4589 }
4590
4591 // Grow the ViewPort a bit laterally, to minimize "jumping" of text
4592 // elements at right side of screen
4593 ViewPort vpx = VPoint;
4594 vpx.BuildExpandedVP(VPoint.pix_width * 12 / 10, VPoint.pix_height);
4595
4596 OCPNRegion screen_region(
4597 wxRect(0, 0, VPoint.pix_width, VPoint.pix_height));
4598 RenderQuiltViewGLText(vpx, screen_region);
4599 }
4600 }
4601 }
4602}
4603void glChartCanvas::RenderSingleMBTileOverlay(const int dbIndex, bool bOverlay,
4604 ViewPort &vp,
4605 OCPNRegion &screen_region,
4606 LLRegion &screenLLRegion) {
4607 ChartBase *chart = ChartData->OpenChartFromDBAndLock(dbIndex, FULL_INIT);
4608
4609 // Chart may have been prevented from initial loading due to size, or some
4610 // other reason...
4611 if (chart == NULL) return;
4612
4613 ChartMbTiles *pcmbt = dynamic_cast<ChartMbTiles *>(chart);
4614 if (!pcmbt) return;
4615
4616 // Is tile an OVERLAY type?
4617 // Render, or not, depending on passed flag.
4618 if (bOverlay && pcmbt->GetTileType() != MbTilesType::OVERLAY) return;
4619
4620 wxFileName tileFile(chart->GetFullPath());
4621 // Size test for 5 GByte
4622 wxULongLong tileSizeMB = tileFile.GetSize() >> 20;
4623
4624 if (!ChartData->CheckAnyCanvasExclusiveTileGroup() ||
4625 (tileSizeMB.GetLo() > 5000)) {
4626 // Check to see if the tile has been "clicked".
4627 // If so, do not add to no-show array again.
4628 if (!m_pParentCanvas->IsTileOverlayIndexInYesShow(dbIndex)) {
4629 if (!m_pParentCanvas->IsTileOverlayIndexInNoShow(dbIndex)) {
4630 m_pParentCanvas->m_tile_noshow_index_array.push_back(dbIndex);
4631 }
4632 }
4633 }
4634
4635 // This test catches the case where the chart is added to no_show list
4636 // when first loaded by OpenChartFromDBAndLock
4637 if (m_pParentCanvas->IsTileOverlayIndexInNoShow(dbIndex)) {
4638 return;
4639 }
4640
4641 pcmbt->RenderRegionViewOnGL(*m_pcontext, vp, screen_region, screenLLRegion);
4642
4643 // Light up the piano key if the chart was rendered
4644 std::vector<int> piano_active_array_tiles =
4645 m_pParentCanvas->m_Piano->GetActiveKeyArray();
4646 bool bfound = false;
4647
4648 if (std::find(piano_active_array_tiles.begin(),
4649 piano_active_array_tiles.end(),
4650 dbIndex) != piano_active_array_tiles.end()) {
4651 bfound = true;
4652 }
4653
4654 if (!bfound) {
4655 piano_active_array_tiles.push_back(dbIndex);
4656 m_pParentCanvas->m_Piano->SetActiveKeyArray(piano_active_array_tiles);
4657 }
4658}
4659
4660void glChartCanvas::RenderMBTilesOverlay(ViewPort &VPoint) {
4661 // Render MBTiles as overlay
4662 std::vector<int> stackIndexArray =
4663 m_pParentCanvas->m_pQuilt->GetExtendedStackIndexArray();
4664 unsigned int im = stackIndexArray.size();
4665 // XXX should
4666 // assert(!VPoint.b_quilt && im == 0)
4667 if (VPoint.b_quilt && im > 0) {
4668 bool regionVPBuilt = false;
4669 OCPNRegion screen_region;
4670 LLRegion screenLLRegion;
4671 LLBBox screenBox;
4672 ViewPort vp;
4673
4674 std::vector<int> tiles_to_show;
4675 for (unsigned int is = 0; is < im; is++) {
4676 const ChartTableEntry &cte =
4677 ChartData->GetChartTableEntry(stackIndexArray[is]);
4678 if (cte.GetChartType() == CHART_TYPE_MBTILES) {
4679 if (m_pParentCanvas->IsTileOverlayIndexInNoShow(stackIndexArray[is])) {
4680 // Turn off the piano highlite
4681 std::vector<int> piano_active_array_tiles =
4682 m_pParentCanvas->m_Piano->GetActiveKeyArray();
4683 bool bfound = false;
4684
4685 for (unsigned int i = 0; i < piano_active_array_tiles.size(); i++) {
4686 if (piano_active_array_tiles[i] == stackIndexArray[is]) {
4687 piano_active_array_tiles.erase(piano_active_array_tiles.begin() +
4688 i); // erase it
4689 bfound = true;
4690 break;
4691 }
4692 }
4693
4694 if (bfound)
4695 m_pParentCanvas->m_Piano->SetActiveKeyArray(
4696 piano_active_array_tiles);
4697
4698 continue;
4699 }
4700
4701 tiles_to_show.push_back(stackIndexArray[is]);
4702 if (!regionVPBuilt) {
4703 screen_region =
4704 OCPNRegion(wxRect(0, 0, VPoint.pix_width, VPoint.pix_height));
4705 screenLLRegion = VPoint.GetLLRegion(screen_region);
4706 screenBox = screenLLRegion.GetBox();
4707
4708 vp = VPoint;
4709 wxPoint p;
4710 p.x = VPoint.pix_width / 2;
4711 p.y = VPoint.pix_height / 2;
4712 VPoint.GetLLFromPix(p, &vp.clat, &vp.clon);
4713
4714 regionVPBuilt = true;
4715 }
4716 }
4717 }
4718
4719 // Render in two passes, to render the OVERLAY types last
4720
4721 // Show the tilesets in reverse order to have the largest scale
4722 // on top
4723
4724 for (std::vector<int>::reverse_iterator rit = tiles_to_show.rbegin();
4725 rit != tiles_to_show.rend(); ++rit) {
4726 RenderSingleMBTileOverlay(*rit, FALSE, vp, screen_region, screenLLRegion);
4727 }
4728 for (std::vector<int>::reverse_iterator rit = tiles_to_show.rbegin();
4729 rit != tiles_to_show.rend(); ++rit) {
4730 RenderSingleMBTileOverlay(*rit, TRUE, vp, screen_region, screenLLRegion);
4731 }
4732
4733 // Render the HiLite on piano rollover of mbTile key
4734 LLRegion hiregion = m_pParentCanvas->m_pQuilt->GetHiliteRegion();
4735
4736 if (!hiregion.Empty()) {
4737 glEnable(GL_BLEND);
4738
4739 double hitrans;
4740 switch (global_color_scheme) {
4741 case GLOBAL_COLOR_SCHEME_DAY:
4742 hitrans = .4;
4743 break;
4744 case GLOBAL_COLOR_SCHEME_DUSK:
4745 hitrans = .2;
4746 break;
4747 case GLOBAL_COLOR_SCHEME_NIGHT:
4748 hitrans = .1;
4749 break;
4750 default:
4751 hitrans = .4;
4752 break;
4753 }
4754
4755#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
4756 glColor4f((float).8, (float).4, (float).4, (float)hitrans);
4757#else
4758 s_regionColor = wxColor(204, 102, 102, hitrans * 256);
4759#endif
4760
4761 DrawRegion(VPoint, hiregion);
4762
4763 glDisable(GL_BLEND);
4764 }
4765 }
4766}
4767
4768#if 0
4769void glChartCanvas::RenderCanvasBackingChart(ocpnDC &dc,
4770 OCPNRegion valid_region) {
4771 // Fill the FBO with the current gshhs world chart
4772 int w, h;
4773 GetClientSize(&w, &h);
4774
4775 glViewport(0, 0, (GLint)m_cache_tex_x, (GLint)m_cache_tex_y);
4776#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
4777 glMatrixMode(GL_PROJECTION);
4778 glLoadIdentity();
4779
4780 glOrtho(0, m_cache_tex_x, m_cache_tex_y, 0, -1, 1);
4781 glMatrixMode(GL_MODELVIEW);
4782 glLoadIdentity();
4783#endif
4784
4785 wxRect rtex(0, 0, m_cache_tex_x, m_cache_tex_y);
4786 ViewPort cvp =
4787 m_pParentCanvas->GetVP().BuildExpandedVP(m_cache_tex_x, m_cache_tex_y);
4788
4789 bool world_view = false;
4790 RenderWorldChart(dc, cvp, rtex, world_view);
4791 gShapeBasemap.RenderViewOnDC(dc, cvp);
4792
4793 // dc.SetPen(wxPen(wxColour(254,254,0), 3));
4794 // dc.DrawLine( 0, 0, m_cache_tex_x, m_cache_tex_y);
4795
4796 // Reset matrices
4797 glViewport(0, 0, (GLint)w, (GLint)h);
4798#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
4799 glMatrixMode(GL_PROJECTION);
4800 glLoadIdentity();
4801
4802 glOrtho(0, (GLint)w, (GLint)h, 0, -1, 1);
4803 glMatrixMode(GL_MODELVIEW);
4804 glLoadIdentity();
4805#endif
4806}
4807#endif
4808
4809void glChartCanvas::FastPan(int dx, int dy) {
4810#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
4811#endif
4812}
4813
4814void glChartCanvas::ZoomProject(float offset_x, float offset_y, float swidth,
4815 float sheight) {
4816 if (IsShown()) SetCurrent(*m_pcontext);
4817 float sx = GetSize().x;
4818 float sy = GetSize().y;
4819 glClear(GL_COLOR_BUFFER_BIT);
4820
4821 int w, h;
4822 GetClientSize(&w, &h);
4823
4824 if (s_b_useStencil) {
4825 glEnable(GL_STENCIL_TEST);
4826 glStencilMask(0xff);
4827 glClear(GL_STENCIL_BUFFER_BIT);
4828 glDisable(GL_STENCIL_TEST);
4829 }
4830
4831#ifndef __ANDROID__
4832 // Render backing texture
4833 if (1) {
4834 float tx0 = 0;
4835 float ty0 = 0;
4836 float tx = sx;
4837 float ty = sy;
4838
4839 float vx0 = 0;
4840 float vy0 = 0;
4841 float vx = sx;
4842 float vy = sy;
4843
4844 float sxfactor = sx / swidth;
4845 float syfactor = sy / sheight;
4846
4847 glViewport(-offset_x * sx / swidth - (sx * sxfactor / 2),
4848 -offset_y * (sy / sheight) - (sy * syfactor / 2),
4849 sx * sx / swidth * 2, sy * sy / sheight * 2);
4850 glBindTexture(g_texture_rectangle_format, m_TouchBackingTexture);
4851 glEnable(g_texture_rectangle_format);
4852
4853 float uv[8];
4854 float coords[8];
4855
4856 // normal uv Texture coordinates
4857 uv[0] = 0;
4858 uv[1] = 0;
4859 uv[2] = 1;
4860 uv[3] = 0;
4861 uv[4] = 1;
4862 uv[5] = 1;
4863 uv[6] = 0;
4864 uv[7] = 1;
4865
4866 // pixels
4867 coords[0] = vx0;
4868 coords[1] = vy0;
4869 coords[2] = vx;
4870 coords[3] = vy0;
4871 coords[4] = vx;
4872 coords[5] = vy;
4873 coords[6] = vx0;
4874 coords[7] = vy;
4875
4876 RenderTextures(m_gldc, coords, uv, 4, &m_texVP);
4877 glBindTexture(g_texture_rectangle_format, 0);
4878 }
4879#endif
4880
4881 // render zoomed canvas section
4882 if (1) {
4883 float tx, ty, tx0, ty0;
4884 tx = sx, ty = sy;
4885
4886 tx0 = ty0 = 0.;
4887
4888 float vx0 = 0;
4889 float vy0 = 0;
4890 float vy = sy;
4891 float vx = sx;
4892
4893 glBindTexture(g_texture_rectangle_format, 0);
4894
4895 // Render the cached texture as quad to screen
4896 glBindTexture(g_texture_rectangle_format, m_cache_tex[m_cache_page]);
4897 glEnable(g_texture_rectangle_format);
4898
4899 float uv[8];
4900 float coords[8];
4901
4902 // normal uv Texture coordinates
4903 uv[0] = tx0 / m_cache_tex_x;
4904 uv[1] = ty / m_cache_tex_y;
4905 uv[2] = tx / m_cache_tex_x;
4906 uv[3] = ty / m_cache_tex_y;
4907 uv[4] = tx / m_cache_tex_x;
4908 uv[5] = ty0 / m_cache_tex_y;
4909 uv[6] = tx0 / m_cache_tex_x;
4910 uv[7] = ty0 / m_cache_tex_y;
4911
4912 // pixels
4913 coords[0] = vx0;
4914 coords[1] = vy0;
4915 coords[2] = vx;
4916 coords[3] = vy0;
4917 coords[4] = vx;
4918 coords[5] = vy;
4919 coords[6] = vx0;
4920 coords[7] = vy;
4921
4922 glViewport(-offset_x * sx / swidth, -offset_y * (sy / sheight),
4923 sx * sx / swidth, sy * sy / sheight);
4924
4925 RenderTextures(m_gldc, coords, uv, 4, m_pParentCanvas->GetpVP());
4926
4927 glDisable(g_texture_rectangle_format);
4928 glBindTexture(g_texture_rectangle_format, 0);
4929 }
4930
4931#if 0
4932 // For fun, we prove the coordinates of the blank area outside the chart when
4933 // zooming out. Bottom stripe
4934 // wxColour color = GetGlobalColor("YELO1"); //GREY1
4935 wxColour color = GetGlobalColor("GREY1"); //
4936 float ht = -offset_y * (sy / sheight);
4937 wxRect r(0, sy - ht, w, ht);
4938 RenderColorRect(r, color);
4939
4940 // top stripe
4941 wxRect rt(0, 0, w, sy - (ht + (sy * sy / sheight)));
4942 RenderColorRect(rt, color);
4943
4944 // left
4945 float w1 = -offset_x * sx / swidth;
4946 wxRect rl(0, 0, w1, sy);
4947 RenderColorRect(rl, color);
4948
4949 // right
4950 float px = w1 + sx * sx / swidth;
4951 wxRect rr(px, 0, sx - px, sy);
4952 RenderColorRect(rr, color);
4953#endif
4954 // When zooming out, if we go too far, then the frame buffer is repeated
4955 // on-screen due to address wrapping in the frame buffer. Detect this case,
4956 // and render some simple solid covering quads to avoid a confusing display.
4957
4958 SwapBuffers();
4959}
4960
4961void glChartCanvas::onZoomTimerEvent(wxTimerEvent &event) {
4962 // If m_zoomFinal is set, stop the timer.
4963 if (!m_zoomFinal) {
4964 if (m_nRun < m_nTotal) {
4965 m_runoffsetx += m_offsetxStep;
4966 if (m_offsetxStep > 0)
4967 m_runoffsetx = wxMin(m_runoffsetx, m_fbo_offsetx);
4968 else
4969 m_runoffsetx = wxMax(m_runoffsetx, m_fbo_offsetx);
4970
4971 m_runoffsety += m_offsetyStep;
4972 if (m_offsetyStep > 0)
4973 m_runoffsety = wxMin(m_runoffsety, m_fbo_offsety);
4974 else
4975 m_runoffsety = wxMax(m_runoffsety, m_fbo_offsety);
4976
4977 m_runswidth += m_swidthStep;
4978 if (m_swidthStep > 0)
4979 m_runswidth = wxMin(m_runswidth, m_fbo_swidth);
4980 else
4981 m_runswidth = wxMax(m_runswidth, m_fbo_swidth);
4982
4983 m_runsheight += m_sheightStep;
4984 if (m_sheightStep > 0)
4985 m_runsheight = wxMin(m_runsheight, m_fbo_sheight);
4986 else
4987 m_runsheight = wxMax(m_runsheight, m_fbo_sheight);
4988
4989 m_nRun += m_nStep;
4990 }
4991
4992 ZoomProject(m_runoffsetx, m_runoffsety, m_runswidth, m_runsheight);
4993
4994 } else {
4995 zoomTimer.Stop();
4996 if (m_zoomFinal) {
4997 m_pParentCanvas->ZoomCanvasSimple(m_zoomFinalZoom);
4998 if (m_zoomFinaldx || m_zoomFinaldy) {
4999 m_pParentCanvas->PanCanvas(m_zoomFinaldx, m_zoomFinaldy);
5000 }
5001 }
5002 m_zoomFinal = false;
5003 }
5004}
5005
5006void glChartCanvas::FastZoom(float factor, float cp_x, float cp_y, float post_x,
5007 float post_y) {
5008 int sx = GetSize().x;
5009 int sy = GetSize().y;
5010
5011 m_lastfbo_offsetx = m_fbo_offsetx;
5012 m_lastfbo_offsety = m_fbo_offsety;
5013 m_lastfbo_swidth = m_fbo_swidth;
5014 m_lastfbo_sheight = m_fbo_sheight;
5015
5016 float curr_fbo_offset_x = m_fbo_offsetx;
5017 float curr_fbo_offset_y = m_fbo_offsety;
5018 float curr_fbo_swidth = m_fbo_swidth;
5019 float curr_fbo_sheight = m_fbo_sheight;
5020
5021 float fx = (float)cp_x / sx;
5022 float fy = 1.0 - (float)cp_y / sy;
5023
5024 float fbo_ctr_x = curr_fbo_offset_x + (curr_fbo_swidth * fx);
5025 float fbo_ctr_y = curr_fbo_offset_y + (curr_fbo_sheight * fy);
5026
5027 m_fbo_swidth = curr_fbo_swidth / factor;
5028 m_fbo_sheight = curr_fbo_sheight / factor;
5029
5030 m_fbo_offsetx = fbo_ctr_x - (m_fbo_swidth * fx);
5031 m_fbo_offsety = fbo_ctr_y - (m_fbo_sheight * fy);
5032
5033 m_fbo_offsetx += post_x;
5034 m_fbo_offsety += post_y;
5035
5036 {
5037 m_nStep = 20;
5038 m_nTotal = 100;
5039
5040 // m_nStep = 10; // Android?
5041 // m_nTotal = 40;
5042
5043 m_nRun = 0;
5044
5045 float perStep = m_nStep / m_nTotal;
5046
5047 if (zoomTimer.IsRunning()) {
5048 m_offsetxStep = (m_fbo_offsetx - m_runoffsetx) * perStep;
5049 m_offsetyStep = (m_fbo_offsety - m_runoffsety) * perStep;
5050 m_swidthStep = (m_fbo_swidth - m_runswidth) * perStep;
5051 m_sheightStep = (m_fbo_sheight - m_runsheight) * perStep;
5052
5053 } else {
5054 m_offsetxStep = (m_fbo_offsetx - m_lastfbo_offsetx) * perStep;
5055 m_offsetyStep = (m_fbo_offsety - m_lastfbo_offsety) * perStep;
5056 m_swidthStep = (m_fbo_swidth - m_lastfbo_swidth) * perStep;
5057 m_sheightStep = (m_fbo_sheight - m_lastfbo_sheight) * perStep;
5058
5059 m_runoffsetx = m_lastfbo_offsetx;
5060 m_runoffsety = m_lastfbo_offsety;
5061 m_runswidth = m_lastfbo_swidth;
5062 m_runsheight = m_lastfbo_sheight;
5063 }
5064
5065 if (!zoomTimer.IsRunning()) zoomTimer.Start(m_nStep);
5066 m_zoomFinal = false;
5067 }
5068}
5069
5070#ifdef __ANDROID__
5071
5072void glChartCanvas::OnEvtPanGesture(wxQT_PanGestureEvent &event) {
5073 // qDebug() << "OnEvtPanGesture" << m_pParentCanvas->m_canvasIndex <<
5074 // event.cursor_pos.x;
5075
5076 if (m_pParentCanvas->isRouteEditing() || m_pParentCanvas->isMarkEditing())
5077 return;
5078
5079 if (m_binPinch) return;
5080 if (m_bpinchGuard) return;
5081
5082 int x = event.GetOffset().x;
5083 int y = event.GetOffset().y;
5084
5085 int lx = event.GetLastOffset().x;
5086 int ly = event.GetLastOffset().y;
5087
5088 int dx = lx - x;
5089 int dy = y - ly;
5090
5091 switch (event.GetState()) {
5092 case GestureStarted:
5093 if (m_binPan) break;
5094
5095 panx = pany = 0;
5096 m_binPan = true;
5097 m_binGesture = true;
5098 // qDebug() << "pg1";
5099 break;
5100
5101 case GestureUpdated:
5102 if (m_binPan) {
5103 if (!g_GLOptions.m_bUseCanvasPanning) {
5104 // qDebug() << "slowpan" << dx << dy;
5105
5106 m_pParentCanvas->FreezePiano();
5107 m_pParentCanvas->PanCanvas(dx, -dy);
5108 m_pParentCanvas->ThawPiano();
5109
5110 } else {
5111 FastPan(dx, dy);
5112 }
5113
5114 panx -= dx;
5115 pany -= dy;
5116 }
5117 break;
5118
5119 case GestureFinished:
5120 // qDebug() << "panGestureFinished";
5121
5122 m_pParentCanvas->UpdateCanvasControlBar();
5123
5124 m_binPan = false;
5125 m_gestureFinishTimer.Start(500, wxTIMER_ONE_SHOT);
5126
5127 break;
5128
5129 case GestureCanceled:
5130 m_binPan = false;
5131 m_gestureFinishTimer.Start(500, wxTIMER_ONE_SHOT);
5132 break;
5133
5134 default:
5135 break;
5136 }
5137
5138 m_bgestureGuard = true;
5139 m_gestureEeventTimer.Start(500, wxTIMER_ONE_SHOT);
5140 m_bforcefull = false;
5141
5142 // qDebug() << "panGestureDone";
5143}
5144
5145float zoom_inc = 1.0;
5146
5147void glChartCanvas::OnEvtPinchGesture(wxQT_PinchGestureEvent &event) {
5148 float zoom_gain = 1.0;
5149 float zout_gain = 1.0;
5150
5151 float zoom_val;
5152 float total_zoom_val;
5153
5154 float max_zoom_scale = 1000.;
5155 float min_zoom_scale = 2e8;
5156
5157 if (event.GetScaleFactor() > 1)
5158 zoom_val = ((event.GetScaleFactor() - 1.0) * zoom_gain) + 1.0;
5159 else
5160 zoom_val = 1.0 - ((1.0 - event.GetScaleFactor()) * zout_gain);
5161
5162 if (event.GetTotalScaleFactor() > 1)
5163 total_zoom_val = ((event.GetTotalScaleFactor() - 1.0) * zoom_gain) + 1.0;
5164 else
5165#if 0
5166 total_zoom_val = 1.0 - ((1.0 - event.GetTotalScaleFactor()) * zout_gain);
5167
5168 double projected_scale = cc1->GetVP().chart_scale / total_zoom_val;
5169
5170 // Max zoom in is set by scale of quilt reference chart, consistent with chart render limits set elsewhere.
5171 float max_zoom_scale = 1000.;
5172 if( cc1->GetVP().b_quilt) {
5173 int ref_index = cc1->GetQuiltRefChartdbIndex();
5174// if((ref_index >= 0) && ChartData){
5175// max_zoom_scale = ChartData->GetDBChartScale(ref_index) / 8.0;
5176// }
5177 }
5178
5179
5180 float min_zoom_scale = 2e8;
5181
5182#endif
5183
5184 total_zoom_val = 1.0 - ((1.0 - event.GetTotalScaleFactor()) * zoom_gain);
5185
5186 double projected_scale =
5187 m_pParentCanvas->GetVP().chart_scale / total_zoom_val;
5188
5189 switch (event.GetState()) {
5190 case GestureStarted:
5191 m_first_zout = false;
5192 m_binPinch = true;
5193 m_binPan = false; // cancel any tentative pan gesture, in case the "pan
5194 // cancel" event was lost
5195 m_binGesture = true;
5196 // qDebug() << "pg2";
5197 m_pinchStart = event.GetCenterPoint();
5198 m_lpinchPoint = m_pinchStart;
5199
5200 m_pParentCanvas->GetCanvasPixPoint(event.GetCenterPoint().x,
5201 event.GetCenterPoint().y, m_pinchlat,
5202 m_pinchlon);
5203 // qDebug() << "center" << event.GetCenterPoint().x <<
5204 // event.GetCenterPoint().y;
5205
5206 m_cc_x = m_fbo_offsetx + (m_fbo_swidth / 2);
5207 m_cc_y = m_fbo_offsety + (m_fbo_sheight / 2);
5208
5209 // Render the full charts with overlay objects onto the frame buffer.
5210 if (IsShown()) SetCurrent(*m_pcontext);
5211 RenderScene();
5212
5213 zoom_inc = 1.0;
5214 break;
5215
5216 case GestureUpdated:
5217 if (g_GLOptions.m_bUseCanvasPanning) {
5218 if (projected_scale < min_zoom_scale) {
5219 wxPoint pinchPoint = event.GetCenterPoint();
5220
5221 float dx = pinchPoint.x - m_lpinchPoint.x;
5222 float dy = pinchPoint.y - m_lpinchPoint.y;
5223
5224 FastZoom(zoom_val, m_pinchStart.x, m_pinchStart.y,
5225 -dx / total_zoom_val, dy / total_zoom_val);
5226
5227 m_lpinchPoint = pinchPoint;
5228 }
5229 } else {
5230 // qDebug() << "update totalzoom" << total_zoom_val << projected_scale;
5231 if (1 || ((total_zoom_val > 1) && !m_first_zout)) { // Zoom in
5232 wxPoint pinchPoint = event.GetCenterPoint();
5233
5234 float dx = pinchPoint.x - m_lpinchPoint.x;
5235 float dy = pinchPoint.y - m_lpinchPoint.y;
5236
5237 if ((projected_scale > max_zoom_scale) &&
5238 (projected_scale < min_zoom_scale))
5239 FastZoom(zoom_val, m_pinchStart.x, m_pinchStart.y,
5240 -dx / total_zoom_val, dy / total_zoom_val);
5241
5242 m_lpinchPoint = pinchPoint;
5243
5244 } else {
5245 m_first_zout = true;
5246 zoom_inc *= zoom_val;
5247 if ((zoom_inc < 0.9) || (zoom_inc > 1.1)) {
5248 m_pParentCanvas->ZoomCanvas(zoom_inc, false);
5249 zoom_inc = 1.0;
5250 }
5251
5252 wxPoint pinchPoint = event.GetCenterPoint();
5253 float dx = pinchPoint.x - m_lpinchPoint.x;
5254 float dy = pinchPoint.y - m_lpinchPoint.y;
5255 m_pParentCanvas->PanCanvas(-dx, -dy);
5256 m_lpinchPoint = pinchPoint;
5257
5258 // SetCurrent(*m_pcontext);
5259 // RenderScene();
5260 // g_Piano->DrawGL(cc1->m_canvas_height -
5261 // g_Piano->GetHeight()); SwapBuffers();
5262 }
5263 }
5264
5265 break;
5266
5267 case GestureFinished: {
5268 // qDebug() << "finish totalzoom" << total_zoom_val <<
5269 // projected_scale;
5270
5271 float cc_x = m_fbo_offsetx + (m_fbo_swidth / 2);
5272 float cc_y = m_fbo_offsety + (m_fbo_sheight / 2);
5273 float dy = 0;
5274 float dx = 0;
5275
5276 float tzoom = total_zoom_val;
5277
5278 if (projected_scale >= min_zoom_scale)
5279 tzoom = m_pParentCanvas->GetVP().chart_scale / min_zoom_scale;
5280
5281 if (projected_scale < max_zoom_scale)
5282 tzoom = m_pParentCanvas->GetVP().chart_scale / max_zoom_scale;
5283
5284 dx = (cc_x - m_cc_x) * tzoom;
5285 dy = -(cc_y - m_cc_y) * tzoom;
5286
5287 if (zoomTimer.IsRunning()) {
5288 // qDebug() << "Final zoom";
5289 m_zoomFinal = true;
5290 m_zoomFinalZoom = tzoom;
5291 m_zoomFinaldx = dx;
5292 m_zoomFinaldy = dy;
5293 }
5294
5295 else {
5296 double final_projected_scale =
5297 m_pParentCanvas->GetVP().chart_scale / tzoom;
5298 // qDebug() << "Final pinchB" << tzoom << final_projected_scale;
5299
5300 if (final_projected_scale < min_zoom_scale) {
5301 // qDebug() << "zoomit";
5302 m_pParentCanvas->ZoomCanvas(tzoom, false);
5303 m_pParentCanvas->PanCanvas(dx, dy);
5304 m_pParentCanvas->m_pQuilt->Invalidate();
5305 m_bforcefull = true;
5306
5307 } else {
5308 double new_scale =
5309 m_pParentCanvas->GetCanvasScaleFactor() / min_zoom_scale;
5310 // qDebug() << "clampit";
5311 m_pParentCanvas->SetVPScale(new_scale);
5312 m_pParentCanvas->m_pQuilt->Invalidate();
5313 m_bforcefull = true;
5314 }
5315 }
5316
5317 // if( projected_scale < 3e8)
5318 // m_pParentCanvas->ZoomCanvas( total_zoom_val, false
5319 // );
5320 // else
5321 // m_pParentCanvas->ZoomCanvas(m_pParentCanvas->GetVP().chart_scale
5322 // / 3e8, false);
5323
5324 m_binPinch = false;
5325 m_gestureFinishTimer.Start(500, wxTIMER_ONE_SHOT);
5326 break;
5327 }
5328
5329 case GestureCanceled:
5330 m_binPinch = false;
5331 m_gestureFinishTimer.Start(500, wxTIMER_ONE_SHOT);
5332 break;
5333
5334 default:
5335 break;
5336 }
5337
5338 m_bgestureGuard = true;
5339 // m_bpinchGuard = true;
5340 m_gestureEeventTimer.Start(500, wxTIMER_ONE_SHOT);
5341}
5342
5343void glChartCanvas::onGestureTimerEvent(wxTimerEvent &event) {
5344 // On some devices, the pan GestureFinished event fails to show up
5345 // Watch for this case, and fix it.....
5346 // qDebug() << "onGestureTimerEvent";
5347
5348 if (m_binPan) {
5349 m_binPan = false;
5350 Invalidate();
5351 Update();
5352 }
5353 m_bgestureGuard = false;
5354 m_bpinchGuard = false;
5355 m_binGesture = false;
5356 m_bforcefull = false;
5357}
5358
5359void glChartCanvas::onGestureFinishTimerEvent(wxTimerEvent &event) {
5360 // qDebug() << "onGestureFinishTimerEvent";
5361
5362 // signal gesture is finished after a delay
5363 m_binGesture = false;
5364 m_bforcefull = false;
5365}
5366
5367#else
5368#ifdef HAVE_WX_GESTURE_EVENTS
5369
5370void glChartCanvas::OnEvtPanGesture(wxPanGestureEvent &event) {
5371 // qDebug() << "OnEvtPanGesture" << m_pParentCanvas->m_canvasIndex <<
5372 // event.cursor_pos.x;
5373 if (m_pParentCanvas->isRouteEditing() || m_pParentCanvas->isMarkEditing())
5374 return;
5375
5376 if (m_binPinch) return;
5377 if (m_bpinchGuard) return;
5378
5379 int dx = event.GetDelta().x;
5380 int dy = event.GetDelta().y;
5381
5382 if (event.IsGestureStart()) {
5383 if (m_binPan) return;
5384
5385 panx = pany = 0;
5386 m_binPan = true;
5387 m_binGesture = true;
5388 // qDebug() << "pg1";
5389 }
5390
5391 else if (event.IsGestureEnd()) {
5392 // qDebug() << "panGestureFinished";
5393 m_pParentCanvas->UpdateCanvasControlBar();
5394 m_binPan = false;
5395 m_gestureFinishTimer.Start(500, wxTIMER_ONE_SHOT);
5396 }
5397
5398 else {
5399 if (m_binPan) {
5400 if (!g_GLOptions.m_bUseCanvasPanning) {
5401 // qDebug() << "slowpan" << dx << dy;
5402
5403 m_pParentCanvas->FreezePiano();
5404 m_pParentCanvas->PanCanvas(dx, -dy);
5405 m_pParentCanvas->ThawPiano();
5406
5407 } else {
5408 FastPan(dx, dy);
5409 }
5410
5411 panx -= dx;
5412 pany -= dy;
5413 }
5414 }
5415
5416 m_bgestureGuard = true;
5417 m_gestureEeventTimer.Start(500, wxTIMER_ONE_SHOT);
5418 m_bforcefull = false;
5419}
5420
5421// Generic wxWidgets gesture event processor
5422void glChartCanvas::OnEvtZoomGesture(wxZoomGestureEvent &event) {
5423 if (g_true_zoom) {
5424 float zoom_gain = 1.0;
5425 float zout_gain = 1.0;
5426
5427 float last_zoom_val = m_step_zoom_val; // m_total_zoom_val;
5428
5429 float max_zoom_scale = 1000.;
5430 float min_zoom_scale = 2e8;
5431
5432 if (event.GetZoomFactor() > 1)
5433 m_total_zoom_val = ((event.GetZoomFactor() - 1.0) * zoom_gain) + 1.0;
5434 else
5435 m_total_zoom_val = 1.0 - ((1.0 - event.GetZoomFactor()) * zout_gain);
5436
5437 float inc_zoom_val =
5438 m_total_zoom_val / last_zoom_val; // the incremental zoom
5439
5440 double projected_scale = m_cache_vp.chart_scale;
5441
5442 if (event.IsGestureStart()) {
5443 m_glstopwatch.Start();
5444 printf("\nStart--------------\n");
5445 m_binPinch = true;
5446 m_pParentCanvas->m_inPinch = true;
5447 m_binPan = false; // cancel any tentative pan gesture, in case the "pan
5448 // cancel" event was lost
5449 m_binGesture = true;
5450 m_pinchStart = event.GetPosition();
5451 m_lpinchPoint = m_pinchStart;
5452 m_total_zoom_val = 1.0;
5453 m_final_zoom_val = 1.0;
5454 m_step_zoom_val = 1.0;
5455
5456 // Capture the lat/lon of the "pinch-point"
5457 m_pParentCanvas->GetCanvasPixPoint(
5458 event.GetPosition().x, event.GetPosition().y, m_pinchlat, m_pinchlon);
5459 m_zoom_inc = 1.0;
5460 }
5461
5462 if (event.IsGestureEnd()) {
5463 // Some platforms generate spurious gestureEnd events. Guard for this.
5464 if (!m_binGesture) return;
5465 printf("EndZoom--------------\n");
5466
5467 Refresh();
5468
5469 // Let the gesture finish timer (500 msec) signal the pinch is done.
5470 // this will allow any lingering drag operation to avoid starting the
5471 // inertial drag process
5472 m_final_zoom_val = 1.0;
5473 m_total_zoom_val = 1.0;
5474 m_step_zoom_val = 1.0;
5475
5476 m_gestureFinishTimer.Start(500, wxTIMER_ONE_SHOT);
5477 }
5478
5479 else {
5480 // printf("%ld %6g %6g %6g \n", g_glstopwatch.Time(),
5481 // event.GetZoomFactor(),
5482 // inc_zoom_val, projected_scale);
5483 float zoom_step = 5;
5484 float zoom_trigger = 0.05;
5485 if (projected_scale > 1e5)
5486 zoom_trigger = .1;
5487 else if (projected_scale < 3e4)
5488 zoom_trigger = .02;
5489
5490 if (inc_zoom_val != 1.0) {
5491 if (inc_zoom_val > 1 + zoom_step) {
5492 m_step_zoom_val = m_step_zoom_val * (1 + zoom_step);
5493 printf(" Partial zoom: %6g\n", 1 + zoom_step);
5494 m_pParentCanvas->ZoomCanvasSimple(1 + zoom_step);
5495 if (IsShown()) SetCurrent(*m_pcontext);
5496 Render();
5497 } else {
5498 if (fabs(inc_zoom_val - 1.) > zoom_trigger) {
5499 m_step_zoom_val = m_total_zoom_val;
5500 printf(" Zoom: %6g\n", inc_zoom_val);
5501 m_pParentCanvas->ZoomCanvasSimple(inc_zoom_val);
5502
5503 // Consider "zoom to pinch-point"
5504 // Disable ZTC if lookahead is ON, and currently b_follow is active
5505 bool b_allow_ztp = true;
5506 if (m_pParentCanvas->m_bFollow && m_pParentCanvas->m_bLookAhead)
5507 b_allow_ztp = false;
5508
5509 if (g_bEnableZoomToCursor && b_allow_ztp) {
5510 // get the new (after zoom) pixel position of the original
5511 // pinch-point
5512 wxPoint r;
5513 m_pParentCanvas->GetCanvasPointPix(m_pinchlat, m_pinchlon, &r);
5514 // Shift the canvas to follow the pinch-lat/lon
5515 int dx = r.x - event.GetPosition().x;
5516 int dy = r.y - event.GetPosition().y;
5517 m_pParentCanvas->PanCanvas(dx, dy);
5518 }
5519
5520 if (IsShown()) SetCurrent(*m_pcontext);
5521 Render();
5522 }
5523 }
5524 }
5525 }
5526 } // true-zoom
5527 else {
5528 // Legacy zoom behavior
5529 float zoom_gain = 1.0;
5530 float zout_gain = 1.0;
5531
5532 float last_zoom_val = m_total_zoom_val;
5533
5534 float max_zoom_scale = 1000.;
5535 float min_zoom_scale = 2e8;
5536
5537 if (event.GetZoomFactor() > 1)
5538 m_total_zoom_val = ((event.GetZoomFactor() - 1.0) * zoom_gain) + 1.0;
5539 else
5540 m_total_zoom_val = 1.0 - ((1.0 - event.GetZoomFactor()) * zout_gain);
5541
5542 float inc_zoom_val =
5543 m_total_zoom_val / last_zoom_val; // the incremental zoom
5544
5545 double projected_scale =
5546 m_pParentCanvas->GetVP().chart_scale / m_total_zoom_val;
5547
5548 if (event.IsGestureStart()) {
5549 // printf("\nStart--------------\n");
5550 m_first_zout = false;
5551 m_binPinch = true;
5552 m_binPan = false; // cancel any tentative pan gesture, in case the "pan
5553 // cancel" event was lost
5554 m_binGesture = true;
5555 m_pinchStart = event.GetPosition();
5556 m_lpinchPoint = m_pinchStart;
5557 m_total_zoom_val = 1.0;
5558 m_final_zoom_val = 1.0;
5559
5560 m_pParentCanvas->GetCanvasPixPoint(
5561 event.GetPosition().x, event.GetPosition().y, m_pinchlat, m_pinchlon);
5562 // qDebug() << "center" << event.GetCenterPoint().x <<
5563 // event.GetCenterPoint().y;
5564
5565 m_cc_x = m_fbo_offsetx + (m_fbo_swidth / 2);
5566 m_cc_y = m_fbo_offsety + (m_fbo_sheight / 2);
5567
5568 // Render the full charts with overlay objects onto the frame buffer.
5569 if (IsShown()) SetCurrent(*m_pcontext);
5570 RenderScene();
5571#ifndef __ANDROID__
5572 ViewPort vpr = m_pParentCanvas->VPoint;
5573 m_texVP = vpr;
5574 GetTouchBackingBitmap(vpr);
5575#endif
5576 m_zoom_inc = 1.0;
5577 }
5578
5579 if (event.IsGestureEnd()) {
5580 // printf("End--------------\n");
5581 // qDebug() << "finish totalzoom" << total_zoom_val <<
5582 // projected_scale;
5583
5584 // Some ptaforms generate spurious gestureEnd events. Guard for this.
5585 if (!m_binGesture) return;
5586
5587 float cc_x = m_fbo_offsetx + (m_fbo_swidth / 2);
5588 float cc_y = m_fbo_offsety + (m_fbo_sheight / 2);
5589 float dy = 0;
5590 float dx = 0;
5591
5592 float tzoom = m_final_zoom_val;
5593
5594 dx = (cc_x - m_cc_x) * tzoom;
5595 dy = -(cc_y - m_cc_y) * tzoom;
5596
5597 if (zoomTimer.IsRunning()) {
5598 // qDebug() << "Final zoom";
5599 m_zoomFinal = true;
5600 m_zoomFinalZoom = tzoom;
5601 m_zoomFinaldx = dx;
5602 m_zoomFinaldy = dy;
5603 }
5604
5605 else {
5606 double final_projected_scale =
5607 m_pParentCanvas->GetVP().chart_scale / tzoom;
5608 // qDebug() << "Final pinchB" << tzoom << final_projected_scale;
5609
5610 if (final_projected_scale < min_zoom_scale) {
5611 // qDebug() << "zoomit";
5612 m_pParentCanvas->ZoomCanvas(tzoom, false);
5613 m_pParentCanvas->PanCanvas(dx, dy);
5614 m_pParentCanvas->m_pQuilt->Invalidate();
5615 m_bforcefull = true;
5616
5617 } else {
5618 double new_scale =
5619 m_pParentCanvas->GetCanvasScaleFactor() / min_zoom_scale;
5620 // qDebug() << "clampit";
5621 m_pParentCanvas->SetVPScale(new_scale);
5622 m_pParentCanvas->m_pQuilt->Invalidate();
5623 m_bforcefull = true;
5624 }
5625 }
5626
5627 m_binPinch = false;
5628 m_final_zoom_val = 1.0;
5629 m_total_zoom_val = 1.0;
5630 m_gestureFinishTimer.Start(500, wxTIMER_ONE_SHOT);
5631 }
5632
5633 else {
5634 if (1 /* g_GLOptions.m_bUseCanvasPanning*/) {
5635 if (projected_scale < min_zoom_scale) {
5636 wxPoint pinchPoint = event.GetPosition();
5637
5638 float dx = pinchPoint.x - m_lpinchPoint.x;
5639 float dy = pinchPoint.y - m_lpinchPoint.y;
5640
5641 FastZoom(inc_zoom_val, m_pinchStart.x, m_pinchStart.y,
5642 -dx / m_total_zoom_val, dy / m_total_zoom_val);
5643
5644 m_lpinchPoint = pinchPoint;
5645 m_final_zoom_val *= inc_zoom_val;
5646 }
5647 } else {
5648 // qDebug() << "update totalzoom" << total_zoom_val << projected_scale;
5649 if (1 || ((m_total_zoom_val > 1) && !m_first_zout)) { // Zoom in
5650 wxPoint pinchPoint = event.GetPosition();
5651
5652 float dx = pinchPoint.x - m_lpinchPoint.x;
5653 float dy = pinchPoint.y - m_lpinchPoint.y;
5654
5655 if ((projected_scale > max_zoom_scale) &&
5656 (projected_scale < min_zoom_scale))
5657 FastZoom(inc_zoom_val, m_pinchStart.x, m_pinchStart.y,
5658 -dx / m_total_zoom_val, dy / m_total_zoom_val);
5659
5660 m_lpinchPoint = pinchPoint;
5661 m_final_zoom_val *= inc_zoom_val;
5662
5663 } else {
5664 m_first_zout = true;
5665 m_zoom_inc *= inc_zoom_val;
5666 if ((m_zoom_inc < 0.9) || (m_zoom_inc > 1.1)) {
5667 m_pParentCanvas->ZoomCanvas(m_zoom_inc, false);
5668 m_zoom_inc = 1.0;
5669 }
5670
5671 wxPoint pinchPoint = event.GetPosition();
5672 float dx = pinchPoint.x - m_lpinchPoint.x;
5673 float dy = pinchPoint.y - m_lpinchPoint.y;
5674 m_pParentCanvas->PanCanvas(-dx, -dy);
5675 m_lpinchPoint = pinchPoint;
5676 }
5677 }
5678 }
5679 m_gestureEeventTimer.Start(500, wxTIMER_ONE_SHOT);
5680 }
5681
5682 m_bgestureGuard = true;
5683 m_bpinchGuard = true;
5684}
5685
5686void glChartCanvas::onGestureTimerEvent(wxTimerEvent &event) {
5687 // On some devices, the pan GestureFinished event fails to show up
5688 // Watch for this case, and fix it.....
5689
5690 if (m_binPan) {
5691 m_binPan = false;
5692 Invalidate();
5693 Update();
5694 }
5695 m_bgestureGuard = false;
5696 m_bpinchGuard = false;
5697 m_binGesture = false;
5698 m_bforcefull = false;
5699}
5700
5701void glChartCanvas::onGestureFinishTimerEvent(wxTimerEvent &event) {
5702 // qDebug() << "onGestureFinishTimerEvent";
5703
5704 // signal gesture is finished after a delay
5705 m_binGesture = false;
5706 m_bforcefull = false;
5707 m_binPinch = false;
5708 m_pParentCanvas->m_inPinch = false;
5709 printf("******Finish\n");
5710}
5711
5712#endif
5713#endif
5714
5715void glChartCanvas::configureShaders(ViewPort &vp) {
5716#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
5717 mat4x4 I;
5718 mat4x4_identity(I);
5719
5720 ViewPort *pvp = (ViewPort *)&vp;
5721
5722 GLShaderProgram *shader = pcolor_tri_shader_program[GetCanvasIndex()];
5723 shader->Bind();
5724 shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)pvp->vp_matrix_transform);
5725 shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)I);
5726 shader->UnBind();
5727
5728 // glUseProgram(color_tri_shader_program);
5729 // GLint matloc = glGetUniformLocation(color_tri_shader_program,
5730 // "MVMatrix"); glUniformMatrix4fv(matloc, 1, GL_FALSE,
5731 // (const GLfloat *)pvp->vp_transform);
5732 // GLint transloc =
5733 // glGetUniformLocation(color_tri_shader_program,
5734 // "TransformMatrix");
5735 // glUniformMatrix4fv(transloc, 1, GL_FALSE, (const GLfloat *)I);
5736
5737 // glUseProgram(texture_2D_shader_program);
5738 // matloc = glGetUniformLocation(texture_2D_shader_program, "MVMatrix");
5739 // glUniformMatrix4fv(matloc, 1, GL_FALSE,
5740 // (const GLfloat *)pvp->vp_transform);
5741 // transloc =
5742 // glGetUniformLocation(texture_2D_shader_program,
5743 // "TransformMatrix");
5744 // glUniformMatrix4fv(transloc, 1, GL_FALSE, (const GLfloat *)I);
5745
5746 shader = ptexture_2D_shader_program[GetCanvasIndex()];
5747 shader->Bind();
5748 shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)pvp->vp_matrix_transform);
5749 shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)I);
5750 shader->UnBind();
5751
5752 // glUseProgram(circle_filled_shader_program);
5753 // matloc = glGetUniformLocation(circle_filled_shader_program,
5754 // "MVMatrix"); glUniformMatrix4fv(matloc, 1, GL_FALSE,
5755 // (const GLfloat *)pvp->vp_transform);
5756 // transloc =
5757 // glGetUniformLocation(circle_filled_shader_program,
5758 // "TransformMatrix");
5759 // glUniformMatrix4fv(transloc, 1, GL_FALSE, (const GLfloat *)I);
5760
5761 shader = pcircle_filled_shader_program[GetCanvasIndex()];
5762 shader->Bind();
5763 shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)pvp->vp_matrix_transform);
5764 shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)I);
5765 shader->UnBind();
5766
5767 shader = ptexture_2DA_shader_program[GetCanvasIndex()];
5768 shader->Bind();
5769 shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)pvp->vp_matrix_transform);
5770 shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)I);
5771 shader->UnBind();
5772
5773 // glUseProgram(AALine_shader_program);
5774 // matloc = glGetUniformLocation(AALine_shader_program, "MVMatrix");
5775 // glUniformMatrix4fv(matloc, 1, GL_FALSE,
5776 // (const GLfloat *)pvp->vp_transform);
5777
5778 shader = pAALine_shader_program[GetCanvasIndex()];
5779 shader->Bind();
5780 shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)pvp->vp_matrix_transform);
5781 shader->UnBind();
5782
5783 shader = pring_shader_program[GetCanvasIndex()];
5784 shader->Bind();
5785 shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)pvp->vp_matrix_transform);
5786 shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)I);
5787 shader->UnBind();
5788
5789 // Leftover shader required by some older Android plugins
5790 if (texture_2DA_shader_program) {
5791 glUseProgram(texture_2DA_shader_program);
5792 GLint matloc = glGetUniformLocation(texture_2DA_shader_program, "MVMatrix");
5793 glUniformMatrix4fv(matloc, 1, GL_FALSE,
5794 (const GLfloat *)pvp->vp_matrix_transform);
5795 GLint transloc =
5796 glGetUniformLocation(texture_2DA_shader_program, "TransformMatrix");
5797 glUniformMatrix4fv(transloc, 1, GL_FALSE, (const GLfloat *)I);
5798 }
5799
5800 m_gldc.m_texfont.PrepareShader(vp.pix_width, vp.pix_height, vp.rotation);
5801
5802#endif
5803}
5804
5805void glChartCanvas::RenderTextures(ocpnDC &dc, float *coords, float *uvCoords,
5806 int nVertex, ViewPort *vp) {
5807// #ifdef USE_ANDROID_GLES2
5808#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
5809 int nl = nVertex / 4;
5810 float *lc = coords;
5811 float *luv = uvCoords;
5812
5813 while (nl) {
5814 RenderSingleTexture(dc, lc, luv, vp, 0, 0, 0);
5815
5816 lc += 8;
5817 luv += 8;
5818 nl--;
5819 }
5820
5821#else
5822 glEnableClientState(GL_VERTEX_ARRAY);
5823 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
5824
5825 glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), uvCoords);
5826 glVertexPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), coords);
5827 glDrawArrays(GL_QUADS, 0, 4);
5828
5829#endif
5830
5831 return;
5832}
5833
5834void glChartCanvas::RenderSingleTexture(ocpnDC &dc, float *coords,
5835 float *uvCoords, ViewPort *vp, float dx,
5836 float dy, float angle_rad) {
5837#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
5838
5839 GLShaderProgram *shader = ptexture_2D_shader_program[dc.m_canvasIndex];
5840 if (!shader) return;
5841
5842 shader->Bind();
5843
5844 // Set up the texture sampler to texture unit 0
5845 shader->SetUniform1i("uTex", 0);
5846
5847 // Rotate
5848 mat4x4 I, Q;
5849 mat4x4_identity(I);
5850 mat4x4_rotate_Z(Q, I, angle_rad);
5851
5852 // Translate
5853 Q[3][0] = dx;
5854 Q[3][1] = dy;
5855
5856 shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)Q);
5857
5858 float co1[8];
5859 float tco1[8];
5860
5861 shader->SetAttributePointerf("aPos", co1);
5862 shader->SetAttributePointerf("aUV", tco1);
5863
5864// Perform the actual drawing.
5865
5866// For some reason, glDrawElements is busted on Android
5867// So we do this a hard ugly way, drawing two triangles...
5868#if 0
5869 GLushort indices1[] = {0,1,3,2};
5870 glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, indices1);
5871#else
5872
5873 co1[0] = coords[0];
5874 co1[1] = coords[1];
5875 co1[2] = coords[2];
5876 co1[3] = coords[3];
5877 co1[4] = coords[6];
5878 co1[5] = coords[7];
5879 co1[6] = coords[4];
5880 co1[7] = coords[5];
5881
5882 tco1[0] = uvCoords[0];
5883 tco1[1] = uvCoords[1];
5884 tco1[2] = uvCoords[2];
5885 tco1[3] = uvCoords[3];
5886 tco1[4] = uvCoords[6];
5887 tco1[5] = uvCoords[7];
5888 tco1[6] = uvCoords[4];
5889 tco1[7] = uvCoords[5];
5890
5891 // glVertexAttribPointer(mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, co1);
5892 // glVertexAttribPointer(mUvAttrib, 2, GL_FLOAT, GL_FALSE, 0, tco1);
5893
5894 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
5895
5896 shader->UnBind();
5897
5898#endif
5899
5900#else
5901#endif
5902
5903 return;
5904}
5905
5906void glChartCanvas::RenderColorRect(wxRect r, wxColor &color) {
5907#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
5908
5909 GLShaderProgram *shader = pcolor_tri_shader_program[GetCanvasIndex()];
5910 shader->Bind();
5911
5912 shader->SetUniformMatrix4fv(
5913 "MVMatrix", (GLfloat *)m_pParentCanvas->GetpVP()->vp_matrix_transform);
5914
5915 float colorv[4];
5916 colorv[0] = color.Red() / float(256);
5917 colorv[1] = color.Green() / float(256);
5918 colorv[2] = color.Blue() / float(256);
5919 colorv[3] = 1.0;
5920 shader->SetUniform4fv("color", colorv);
5921
5922 float pf[8];
5923 pf[0] = r.x + r.width;
5924 pf[1] = r.y;
5925 pf[2] = r.x;
5926 pf[3] = r.y;
5927 pf[4] = r.x + r.width;
5928 pf[5] = r.y + r.height;
5929 pf[6] = r.x;
5930 pf[7] = r.y + r.height;
5931 shader->SetAttributePointerf("position", pf);
5932
5933 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
5934
5935 shader->UnBind();
5936
5937#else
5938#endif
5939}
5940
5941void glChartCanvas::RenderScene(bool bRenderCharts, bool bRenderOverlays) {
5942#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
5943
5944 ViewPort VPoint = m_pParentCanvas->VPoint;
5945 ocpnDC gldc(*this);
5946
5947 int w, h;
5948 GetClientSize(&w, &h);
5949 int sx = GetSize().x;
5950 int sy = GetSize().y;
5951
5952 OCPNRegion screen_region(wxRect(0, 0, VPoint.pix_width, VPoint.pix_height));
5953
5954 glViewport(0, 0, (GLint)w, (GLint)h);
5955
5956 if (s_b_useStencil) {
5957 glEnable(GL_STENCIL_TEST);
5958 glStencilMask(0xff);
5959 glClear(GL_STENCIL_BUFFER_BIT);
5960 glDisable(GL_STENCIL_TEST);
5961 }
5962
5963 // Make sure we have a valid quilt composition
5964 m_pParentCanvas->m_pQuilt->Compose(m_pParentCanvas->VPoint);
5965
5966 // set opengl settings that don't normally change
5967 // this should be able to go in SetupOpenGL, but it's
5968 // safer here incase a plugin mangles these
5969 glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
5970 glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
5971 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
5972
5973 // enable rendering to texture in framebuffer object
5974 glBindFramebuffer(GL_FRAMEBUFFER_EXT, m_fb0);
5975
5976 glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0,
5977 g_texture_rectangle_format, m_cache_tex[m_cache_page],
5978 0);
5979
5980 m_fbo_offsetx = 0;
5981 m_fbo_offsety = 0;
5982 m_fbo_swidth = sx;
5983 m_fbo_sheight = sy;
5984
5985 if (bRenderCharts) RenderCharts(gldc, screen_region);
5986
5987 if (bRenderOverlays) {
5988 RenderS57TextOverlay(m_pParentCanvas->VPoint);
5989 RenderMBTilesOverlay(m_pParentCanvas->VPoint);
5990 g_overlayCanvas = m_pParentCanvas;
5991
5992 if (g_pi_manager) {
5993 g_pi_manager->SendViewPortToRequestingPlugIns(VPoint);
5994 g_pi_manager->RenderAllGLCanvasOverlayPlugIns(
5995 m_pcontext, VPoint, m_pParentCanvas->m_canvasIndex, OVERLAY_CHARTS);
5996 }
5997
5998 DrawStaticRoutesTracksAndWaypoints(m_pParentCanvas->VPoint);
5999 DrawDynamicRoutesTracksAndWaypoints(VPoint);
6000 DrawFloatingOverlayObjects(m_gldc);
6001 }
6002
6003 // All done, so disable Render to FBO
6004 glBindFramebuffer(GL_FRAMEBUFFER, 0);
6005
6006#endif
6007}
6008
6009wxBitmap &glChartCanvas::GetTouchBackingBitmap(ViewPort &vp) {
6010 wxBitmap tbm(vp.pix_width, vp.pix_height, -1);
6011 wxMemoryDC tdc(tbm);
6012 tdc.SetBackground(wxBrush(GetGlobalColor("BLUEBACK")));
6013 tdc.Clear();
6014 ocpnDC dc = ocpnDC(tdc);
6015 ViewPort tvp = vp;
6016
6017 tvp.view_scale_ppm /= 2;
6018 tvp.SetBoxes();
6019
6020 gShapeBasemap.SetBasemapLandColor(GetGlobalColor("LANDBACK"));
6021 dc.SetPen(*wxTRANSPARENT_PEN);
6022
6023 gShapeBasemap.RenderViewOnDC(dc, tvp);
6024 tdc.SelectObject(wxNullBitmap);
6025 m_touch_backing_bitmap = tbm;
6026 CreateBackingTexture();
6027
6028 return m_touch_backing_bitmap;
6029}
6030
6031void glChartCanvas::CreateBackingTexture() {
6032 wxImage image = m_touch_backing_bitmap.ConvertToImage();
6033 unsigned char *imgdata = image.GetData();
6034 unsigned char *imgalpha = image.GetAlpha();
6035 m_tex_w = image.GetWidth();
6036 m_tex_h = image.GetHeight();
6037 m_image_width = m_tex_w;
6038 m_image_height = m_tex_h;
6039
6040 GLuint format = GL_RGBA;
6041 GLuint internalformat = g_texture_rectangle_format;
6042#ifndef __ANDROID__
6043 internalformat = GL_RGBA;
6044#endif
6045 int stride = 4;
6046
6047 if (imgdata) {
6048 unsigned char *teximage =
6049 (unsigned char *)malloc(stride * m_tex_w * m_tex_h);
6050
6051 for (int i = 0; i < m_image_height; i++) {
6052 for (int j = 0; j < m_image_width; j++) {
6053 int s = (i * 3 * m_image_width) + (j * 3);
6054 int d = (i * stride * m_tex_w) + (j * stride);
6055
6056 teximage[d + 0] = imgdata[s + 0];
6057 teximage[d + 1] = imgdata[s + 1];
6058 teximage[d + 2] = imgdata[s + 2];
6059 teximage[d + 3] = 255;
6060 }
6061 }
6062
6063 glGenTextures(1, &m_TouchBackingTexture);
6064 glBindTexture(GL_TEXTURE_2D, m_TouchBackingTexture);
6065
6066 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
6067 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
6068 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
6069 GL_NEAREST); // No mipmapping
6070 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
6071
6072 glTexImage2D(GL_TEXTURE_2D, 0, internalformat, m_tex_w, m_tex_h, 0, format,
6073 GL_UNSIGNED_BYTE, teximage);
6074
6075 free(teximage);
6076 glBindTexture(GL_TEXTURE_2D, 0);
6077 }
6078}
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:1310
ChartCanvas * g_overlayCanvas
Global instance.
Definition chcanv.cpp:1309
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:476
bool GetCanvasPointPix(double rlat, double rlon, wxPoint *r)
Convert latitude/longitude to canvas pixel coordinates (physical pixels) rounded to nearest integer.
Definition chcanv.cpp:4514
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:4464
double GetCanvasScaleFactor()
Return the number of logical pixels per meter for the screen.
Definition chcanv.h:485
double GetPixPerMM()
Get the number of logical pixels per millimeter on the screen.
Definition chcanv.h:516
bool MouseEventSetup(wxMouseEvent &event, bool b_handle_dclick=true)
Definition chcanv.cpp:7759
bool PanCanvas(double dx, double dy)
Pans (moves) the canvas by the specified physical pixels in x and y directions.
Definition chcanv.cpp:5046
float GetVPScale()
Return the ViewPort scale factor, in physical pixels per meter.
Definition chcanv.h:473
void ZoomCanvasSimple(double factor)
Perform an immediate zoom operation without smooth transitions.
Definition chcanv.cpp:4595
bool SetVPScale(double sc, bool b_refresh=true)
Sets the viewport scale while maintaining the center point.
Definition chcanv.cpp:5326
void GetCanvasPixPoint(double x, double y, double &lat, double &lon)
Convert canvas pixel coordinates (physical pixels) to latitude/longitude.
Definition chcanv.cpp:4539
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:4601
void GetDoubleCanvasPointPix(double rlat, double rlon, wxPoint2DDouble *r)
Convert latitude/longitude to canvas pixel coordinates (physical pixels) with double precision.
Definition chcanv.cpp:4459
bool MouseEventProcessCanvas(wxMouseEvent &event)
Processes mouse events for core chart panning and zooming operations.
Definition chcanv.cpp:10119
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:442
wxFont * GetFont(const wxString &TextElement, int requested_font_size=0)
Get a font object for a UI element.
Definition font_mgr.cpp:193
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:812
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:124
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:136
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:127
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:61
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:86
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.