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
488int glChartCanvas::GetCanvasIndex() { return m_pParentCanvas->m_canvasIndex; }
489
490void glChartCanvas::FlushFBO() {
491 if (m_bsetup) BuildFBO();
492}
493
494void glChartCanvas::OnActivate(wxActivateEvent &event) {
495 m_pParentCanvas->OnActivate(event);
496}
497
498void glChartCanvas::OnSize(wxSizeEvent &event) {
499#if 0
500#ifdef __ANDROID__
501 if(!g_running){
502 wxLogMessage("Got OnSize event while NOT running");
503 event.Skip();
504 qDebug() << "OnSizeB";
505
506 return;
507 }
508#endif
509#endif
510
511 if (!IsShown() || !m_bsetup) return;
512
513 SetCurrent(*m_pcontext);
514
515 if (!g_bopengl) {
516 // Invoked immediately after user has disabled OpenGL.
517 SetSize(GetSize().x, GetSize().y);
518 event.Skip();
519 return;
520 }
521
522 // this is also necessary to update the context on some platforms
523 // OnSize can be called with a different OpenGL context (when a plugin uses a
524 // different GL context).
525 if (m_pcontext && IsShown()) {
526 SetCurrent(*m_pcontext);
527 }
528
529 // SetSize(m_pParentCanvas->GetClientSize());
530
531 wxLogDebug("BuildFBO 3");
532 BuildFBO();
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 wxLogDebug(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 if (ChartData->IsBusy()) return;
1346
1347 Show(g_bopengl);
1348 if (!g_bopengl) {
1349 event.Skip();
1350 return;
1351 }
1352
1353 if (IsShown()) SetCurrent(*m_pcontext);
1354
1355 if (!m_bsetup) {
1356 SetupOpenGL();
1357
1358 if (ps52plib) ps52plib->FlushSymbolCaches(ChartCtxFactory());
1359
1360 m_bsetup = true;
1361 // g_bDebugOGL = true;
1362 }
1363
1364 // Paint updates may have been externally disabled (temporarily, to avoid
1365 // Yield() recursion performance loss)
1366 if (!m_b_paint_enable) return;
1367 // Recursion test, sometimes seen on GTK systems when wxBusyCursor is
1368 // activated
1369 if (m_in_glpaint) return;
1370
1371 // If necessary, reconfigure the S52 PLIB
1372 m_pParentCanvas->UpdateCanvasS52PLIBConfig();
1373
1374 // if( m_pParentCanvas->VPoint.b_quilt ){ // quilted
1375 // if( !m_pParentCanvas->m_pQuilt ||
1376 // !m_pParentCanvas->m_pQuilt->IsComposed() )
1377 // return; // not ready
1378 //
1379 // if(m_pParentCanvas->m_pQuilt->IsQuiltVector()){
1380 // if(ps52plib->GetStateHash() !=
1381 // m_pParentCanvas->m_s52StateHash){
1382 // m_pParentCanvas->UpdateS52State();
1383 // m_pParentCanvas->m_s52StateHash =
1384 // ps52plib->GetStateHash();
1385 // }
1386 // }
1387 // }
1388
1389 m_in_glpaint++;
1390 Render();
1391 m_in_glpaint--;
1392}
1393
1394// These routines allow reusable coordinates
1395bool glChartCanvas::HasNormalizedViewPort(const ViewPort &vp) {
1396 return false;
1397#ifndef USE_ANDROID_GLES2
1398 return vp.m_projection_type == PROJECTION_MERCATOR ||
1399 vp.m_projection_type == PROJECTION_POLAR ||
1400 vp.m_projection_type == PROJECTION_EQUIRECTANGULAR;
1401#else
1402 return false;
1403#endif
1404}
1405
1406/* adjust the opengl transformation matrix so that
1407 points plotted using the identity viewport are correct.
1408 and all rotation translation and scaling is now done in opengl
1409
1410 a central lat and lon of 0, 0 can be used, however objects on the far side of
1411 the world can be up to 3 meters off because limited floating point precision,
1412 and if the points cross 180 longitude then two passes will be required to
1413 render them correctly */
1414#define NORM_FACTOR 4096.0
1415void glChartCanvas::MultMatrixViewPort(ViewPort &vp, float lat, float lon) {
1416#ifndef USE_ANDROID_GLES2
1417
1418 wxPoint2DDouble point;
1419
1420 switch (vp.m_projection_type) {
1421 case PROJECTION_MERCATOR:
1422 case PROJECTION_EQUIRECTANGULAR:
1423 case PROJECTION_WEB_MERCATOR:
1424 // m_pParentCanvas->GetDoubleCanvasPointPixVP(vp, lat, lon, &point);
1425 point = vp.GetDoublePixFromLL(lat, lon);
1426 glTranslated(point.m_x, point.m_y, 0);
1427 glScaled(vp.view_scale_ppm / NORM_FACTOR, vp.view_scale_ppm / NORM_FACTOR,
1428 1);
1429 break;
1430
1431 case PROJECTION_POLAR:
1432 // m_pParentCanvas->GetDoubleCanvasPointPixVP(vp, vp.clat > 0 ? 90 : -90,
1433 // vp.clon, &point);
1434 point = vp.GetDoublePixFromLL(vp.clat > 0 ? 90 : -90, vp.clon);
1435 glTranslated(point.m_x, point.m_y, 0);
1436 glRotatef(vp.clon - lon, 0, 0, vp.clat);
1437 glScalef(vp.view_scale_ppm / NORM_FACTOR, vp.view_scale_ppm / NORM_FACTOR,
1438 1);
1439 glTranslatef(-vp.pix_width / 2, -vp.pix_height / 2, 0);
1440 break;
1441
1442 default:
1443 printf("ERROR: Unhandled projection\n");
1444 }
1445
1446 double rotation = vp.rotation;
1447
1448 if (rotation) glRotatef(rotation * 180 / PI, 0, 0, 1);
1449#endif
1450}
1451
1452ViewPort glChartCanvas::NormalizedViewPort(const ViewPort &vp, float lat,
1453 float lon) {
1454 ViewPort cvp = vp;
1455
1456 switch (vp.m_projection_type) {
1457 case PROJECTION_MERCATOR:
1458 case PROJECTION_EQUIRECTANGULAR:
1459 case PROJECTION_WEB_MERCATOR:
1460 cvp.clat = lat;
1461 break;
1462
1463 case PROJECTION_POLAR:
1464 cvp.clat = vp.clat > 0 ? 90 : -90; // either north or south polar
1465 break;
1466
1467 default:
1468 printf("ERROR: Unhandled projection\n");
1469 }
1470
1471 cvp.clon = lon;
1472 cvp.view_scale_ppm = NORM_FACTOR;
1473 cvp.rotation = cvp.skew = 0;
1474 return cvp;
1475}
1476
1477bool glChartCanvas::CanClipViewport(const ViewPort &vp) {
1478 return vp.m_projection_type == PROJECTION_MERCATOR ||
1479 vp.m_projection_type == PROJECTION_WEB_MERCATOR ||
1480 vp.m_projection_type == PROJECTION_EQUIRECTANGULAR;
1481}
1482
1483ViewPort glChartCanvas::ClippedViewport(const ViewPort &vp,
1484 const LLRegion &region) {
1485 if (!CanClipViewport(vp)) return vp;
1486
1487 ViewPort cvp = vp;
1488 LLBBox bbox = region.GetBox();
1489
1490 if (!bbox.GetValid()) return vp;
1491
1492 /* region.GetBox() will always try to give coordinates from -180 to 180 but in
1493 the case where the viewport crosses the IDL, we actually want the clipped
1494 viewport to use coordinates outside this range to ensure the logic in the
1495 various rendering routines works the same here (with accelerated panning)
1496 as it does without, so we can adjust the coordinates here */
1497
1498 if (bbox.GetMaxLon() < cvp.GetBBox().GetMinLon()) {
1499 bbox.Set(bbox.GetMinLat(), bbox.GetMinLon() + 360, bbox.GetMaxLat(),
1500 bbox.GetMaxLon() + 360);
1501 cvp.SetBBoxDirect(bbox);
1502 } else if (bbox.GetMinLon() > cvp.GetBBox().GetMaxLon()) {
1503 bbox.Set(bbox.GetMinLat(), bbox.GetMinLon() - 360, bbox.GetMaxLat(),
1504 bbox.GetMaxLon() - 360);
1505 cvp.SetBBoxDirect(bbox);
1506 } else
1507 cvp.SetBBoxDirect(bbox);
1508
1509 return cvp;
1510}
1511
1512void glChartCanvas::DrawStaticRoutesTracksAndWaypoints(ViewPort &vp) {
1513 if (!m_pParentCanvas->m_bShowNavobjects) return;
1514 ocpnDC dc(*this);
1515
1516 for (Track *pTrackDraw : g_TrackList) {
1517 /* defer rendering active tracks until later */
1518 ActiveTrack *pActiveTrack = dynamic_cast<ActiveTrack *>(pTrackDraw);
1519 if (pActiveTrack && pActiveTrack->IsRunning()) continue;
1520
1521 TrackGui(*pTrackDraw).Draw(m_pParentCanvas, dc, vp, vp.GetBBox());
1522 }
1523
1524 for (Route *pRouteDraw : *pRouteList) {
1525 if (!pRouteDraw) continue;
1526
1527 /* defer rendering active routes until later */
1528 if (pRouteDraw->IsActive() || pRouteDraw->IsSelected()) continue;
1529
1530 /* defer rendering routes being edited until later */
1531 if (pRouteDraw->m_bIsBeingEdited) continue;
1532
1533 RouteGui(*pRouteDraw).DrawGL(vp, m_pParentCanvas, dc);
1534 // pRouteDraw->DrawGL(vp, m_pParentCanvas, dc);
1535 }
1536
1537 /* Waypoints not drawn as part of routes, and not being edited */
1538 if (vp.GetBBox().GetValid() && pWayPointMan) {
1539 for (RoutePoint *pWP : *pWayPointMan->GetWaypointList()) {
1540 if (pWP && (!pWP->m_bRPIsBeingEdited) && (!pWP->m_bIsInRoute))
1541 if (vp.GetBBox().ContainsMarge(pWP->m_lat, pWP->m_lon, .5))
1542 RoutePointGui(*pWP).DrawGL(vp, m_pParentCanvas, dc);
1543 }
1544 }
1545}
1546
1547void glChartCanvas::DrawDynamicRoutesTracksAndWaypoints(ViewPort &vp) {
1548 ocpnDC dc(*this);
1549
1550 for (Track *pTrackDraw : g_TrackList) {
1551 ActiveTrack *pActiveTrack = dynamic_cast<ActiveTrack *>(pTrackDraw);
1552 if (pActiveTrack && pActiveTrack->IsRunning())
1553 TrackGui(*pTrackDraw).Draw(m_pParentCanvas, dc, vp, vp.GetBBox());
1554 // We need Track::Draw() to dynamically render last (ownship) point.
1555 }
1556
1557 for (Route *pRouteDraw : *pRouteList) {
1558 int drawit = 0;
1559 if (!pRouteDraw) continue;
1560
1561 /* Active routes */
1562 if (pRouteDraw->IsActive() || pRouteDraw->IsSelected()) drawit++;
1563
1564 /* Routes being edited */
1565 if (pRouteDraw->m_bIsBeingEdited) drawit++;
1566
1567 /* Routes Selected */
1568 if (pRouteDraw->IsSelected()) drawit++;
1569
1570 if (drawit) {
1571 const LLBBox &vp_box = vp.GetBBox(), &test_box = pRouteDraw->GetBBox();
1572 if (!vp_box.IntersectOut(test_box))
1573 RouteGui(*pRouteDraw).DrawGL(vp, m_pParentCanvas, dc);
1574 // pRouteDraw->DrawGL(vp, m_pParentCanvas, dc);
1575 }
1576 }
1577
1578 /* Waypoints not drawn as part of routes, which are being edited right now */
1579 if (vp.GetBBox().GetValid() && pWayPointMan) {
1580 for (RoutePoint *pWP : *pWayPointMan->GetWaypointList()) {
1581 if (pWP && pWP->m_bRPIsBeingEdited && !pWP->m_bIsInRoute)
1582 RoutePointGui(*pWP).DrawGL(vp, m_pParentCanvas, dc);
1583 // pWP->DrawGL(vp, m_pParentCanvas, dc);
1584 }
1585 }
1586}
1587
1588static void GetLatLonCurveDist(const ViewPort &vp, float &lat_dist,
1589 float &lon_dist) {
1590 // This really could use some more thought, and possibly split at different
1591 // intervals based on chart skew and other parameters to optimize performance
1592 switch (vp.m_projection_type) {
1593 case PROJECTION_TRANSVERSE_MERCATOR:
1594 lat_dist = 4, lon_dist = 1;
1595 break;
1596 case PROJECTION_POLYCONIC:
1597 lat_dist = 2, lon_dist = 1;
1598 break;
1599 case PROJECTION_ORTHOGRAPHIC:
1600 lat_dist = 2, lon_dist = 2;
1601 break;
1602 case PROJECTION_POLAR:
1603 lat_dist = 180, lon_dist = 1;
1604 break;
1605 case PROJECTION_STEREOGRAPHIC:
1606 case PROJECTION_GNOMONIC:
1607 lat_dist = 2, lon_dist = 1;
1608 break;
1609 case PROJECTION_EQUIRECTANGULAR:
1610 // this is suboptimal because we don't care unless there is
1611 // a change in both lat AND lon (skewed chart)
1612 lat_dist = 2, lon_dist = 360;
1613 break;
1614 default:
1615 lat_dist = 180, lon_dist = 360;
1616 }
1617}
1618
1619void glChartCanvas::RenderChartOutline(ocpnDC &dc, int dbIndex, ViewPort &vp) {
1620 if (ChartData->GetDBChartType(dbIndex) == CHART_TYPE_PLUGIN &&
1621 !ChartData->IsChartAvailable(dbIndex))
1622 return;
1623
1624 /* quick bounds check */
1625 LLBBox box;
1626 ChartData->GetDBBoundingBox(dbIndex, box);
1627 if (!box.GetValid()) return;
1628
1629 // Don't draw an outline in the case where the chart covers the entire world
1630 // */
1631 if (box.GetLonRange() == 360) return;
1632
1633 LLBBox vpbox = vp.GetBBox();
1634
1635 double lon_bias = 0;
1636 // chart is outside of viewport lat/lon bounding box
1637 if (box.IntersectOutGetBias(vp.GetBBox(), lon_bias)) return;
1638
1639 wxColour color;
1640 if (ChartData->GetDBChartType(dbIndex) == CHART_TYPE_CM93)
1641 color = GetGlobalColor("YELO1");
1642 else if (ChartData->GetDBChartFamily(dbIndex) == CHART_FAMILY_VECTOR)
1643 color = GetGlobalColor("GREEN2");
1644 else
1645 color = GetGlobalColor("UINFR");
1646
1647#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
1648 float plylat, plylon;
1649
1650 if (g_GLOptions.m_GLLineSmoothing) glEnable(GL_LINE_SMOOTH);
1651
1652 glColor3ub(color.Red(), color.Green(), color.Blue());
1653 glLineWidth(g_GLMinSymbolLineWidth);
1654
1655 float lat_dist, lon_dist;
1656 GetLatLonCurveDist(vp, lat_dist, lon_dist);
1657
1658 // Are there any aux ply entries?
1659 int nAuxPlyEntries = ChartData->GetnAuxPlyEntries(dbIndex), nPly;
1660 int j = 0;
1661 do {
1662 if (nAuxPlyEntries)
1663 nPly = ChartData->GetDBAuxPlyPoint(dbIndex, 0, j, 0, 0);
1664 else
1665 nPly = ChartData->GetDBPlyPoint(dbIndex, 0, &plylat, &plylon);
1666
1667 bool begin = false, sml_valid = false;
1668 double sml[2];
1669 float lastplylat = 0.0;
1670 float lastplylon = 0.0;
1671 // modulo is undefined for zero (compiler can use a div operation)
1672 int modulo = (nPly == 0) ? 1 : nPly;
1673 for (int i = 0; i < nPly + 1; i++) {
1674 if (nAuxPlyEntries)
1675 ChartData->GetDBAuxPlyPoint(dbIndex, i % modulo, j, &plylat, &plylon);
1676 else
1677 ChartData->GetDBPlyPoint(dbIndex, i % modulo, &plylat, &plylon);
1678
1679 plylon += lon_bias;
1680
1681 if (lastplylon - plylon > 180)
1682 lastplylon -= 360;
1683 else if (lastplylon - plylon < -180)
1684 lastplylon += 360;
1685
1686 int splits;
1687 if (i == 0)
1688 splits = 1;
1689 else {
1690 int lat_splits = floor(fabs(plylat - lastplylat) / lat_dist);
1691 int lon_splits = floor(fabs(plylon - lastplylon) / lon_dist);
1692 splits = wxMax(lat_splits, lon_splits) + 1;
1693 }
1694
1695 double smj[2];
1696 if (splits != 1) {
1697 // must perform border interpolation in mercator space as this is what
1698 // the charts use
1699 toSM(plylat, plylon, 0, 0, smj + 0, smj + 1);
1700 if (!sml_valid) toSM(lastplylat, lastplylon, 0, 0, sml + 0, sml + 1);
1701 }
1702
1703 for (double c = 0; c < splits; c++) {
1704 double lat, lon;
1705 if (c == splits - 1)
1706 lat = plylat, lon = plylon;
1707 else {
1708 double d = (double)(c + 1) / splits;
1709 fromSM(d * smj[0] + (1 - d) * sml[0], d * smj[1] + (1 - d) * sml[1],
1710 0, 0, &lat, &lon);
1711 }
1712
1713 wxPoint2DDouble s;
1714 m_pParentCanvas->GetDoubleCanvasPointPix(lat, lon, &s);
1715 if (!std::isnan(s.m_x)) {
1716 if (!begin) {
1717 begin = true;
1718 glBegin(GL_LINE_STRIP);
1719 }
1720 glVertex2f(s.m_x, s.m_y);
1721 } else if (begin) {
1722 glEnd();
1723 begin = false;
1724 }
1725 }
1726 if ((sml_valid = splits != 1)) memcpy(sml, smj, sizeof smj);
1727 lastplylat = plylat, lastplylon = plylon;
1728 }
1729
1730 if (begin) glEnd();
1731
1732 } while (++j < nAuxPlyEntries); // There are no aux Ply Point entries
1733
1734 glDisable(GL_LINE_SMOOTH);
1735 // glDisable( GL_BLEND );
1736
1737#else
1738 double nominal_line_width_pix =
1739 wxMax(2.0, floor(m_pParentCanvas->GetPixPerMM() / 4));
1740
1741 if (ChartData->GetDBChartType(dbIndex) == CHART_TYPE_CM93)
1742 dc.SetPen(wxPen(GetGlobalColor("YELO1"), nominal_line_width_pix,
1743 wxPENSTYLE_SOLID));
1744
1745 else if (ChartData->GetDBChartFamily(dbIndex) == CHART_FAMILY_VECTOR)
1746 dc.SetPen(wxPen(GetGlobalColor("UINFG"), nominal_line_width_pix,
1747 wxPENSTYLE_SOLID));
1748
1749 else
1750 dc.SetPen(wxPen(GetGlobalColor("UINFR"), nominal_line_width_pix,
1751 wxPENSTYLE_SOLID));
1752
1753 float plylat1, plylon1;
1754 int pixx1, pixy1;
1755
1756 // Are there any aux ply entries?
1757 int nAuxPlyEntries = ChartData->GetnAuxPlyEntries(dbIndex);
1758 if (0 == nAuxPlyEntries) // There are no aux Ply Point entries
1759 {
1760 wxPoint r, r1;
1761 std::vector<int> points_vector;
1762
1763 std::vector<float> vec = ChartData->GetReducedPlyPoints(dbIndex);
1764 int nPly = vec.size() / 2;
1765
1766 if (nPly == 0) return;
1767
1768 for (int i = 0; i < nPly; i++) {
1769 plylon1 = vec[i * 2];
1770 plylat1 = vec[i * 2 + 1];
1771
1772 m_pParentCanvas->GetCanvasPointPix(plylat1, plylon1, &r1);
1773 pixx1 = r1.x;
1774 pixy1 = r1.y;
1775
1776 points_vector.push_back(pixx1);
1777 points_vector.push_back(pixy1);
1778 }
1779
1780 ChartData->GetDBPlyPoint(dbIndex, 0, &plylat1, &plylon1);
1781 plylon1 += lon_bias;
1782
1783 m_pParentCanvas->GetCanvasPointPix(vec[1], vec[0], &r1);
1784 pixx1 = r1.x;
1785 pixy1 = r1.y;
1786
1787 points_vector.push_back(pixx1);
1788 points_vector.push_back(pixy1);
1789
1790 if (points_vector.size()) {
1791 std::vector<int>::iterator it = points_vector.begin();
1792 dc.DrawLines(points_vector.size() / 2, (wxPoint *)&(*it), 0, 0, true);
1793 }
1794 }
1795
1796 else // Use Aux PlyPoints
1797 {
1798 wxPoint r, r1;
1799
1800 for (int j = 0; j < nAuxPlyEntries; j++) {
1801 std::vector<int> points_vector;
1802
1803 std::vector<float> vec = ChartData->GetReducedAuxPlyPoints(dbIndex, j);
1804 int nAuxPly = vec.size() / 2;
1805
1806 if (nAuxPly == 0) continue;
1807
1808 for (int i = 0; i < nAuxPly; i++) {
1809 plylon1 = vec[i * 2];
1810 plylat1 = vec[i * 2 + 1];
1811
1812 m_pParentCanvas->GetCanvasPointPix(plylat1, plylon1, &r1);
1813 pixx1 = r1.x;
1814 pixy1 = r1.y;
1815
1816 points_vector.push_back(pixx1);
1817 points_vector.push_back(pixy1);
1818 }
1819
1820 m_pParentCanvas->GetCanvasPointPix(vec[1], vec[0], &r1);
1821 pixx1 = r1.x;
1822 pixy1 = r1.y;
1823
1824 points_vector.push_back(pixx1);
1825 points_vector.push_back(pixy1);
1826
1827 if (points_vector.size()) {
1828 std::vector<int>::iterator it = points_vector.begin();
1829 dc.DrawLines(points_vector.size() / 2, (wxPoint *)&(*it), 0, 0, true);
1830 }
1831 }
1832 }
1833
1834#endif
1835}
1836
1837extern void CalcGridSpacing(float WindowDegrees, float &MajorSpacing,
1838 float &MinorSpacing);
1839extern wxString CalcGridText(float latlon, float spacing, bool bPostfix);
1840void glChartCanvas::GridDraw() {
1841 if (!m_pParentCanvas->m_bDisplayGrid) return;
1842
1843 ViewPort &vp = m_pParentCanvas->GetVP();
1844
1845 if (!vp.IsValid() || !vp.GetBBox().GetValid()) return;
1846
1847 // TODO: make minor grid work all the time
1848 bool minorgrid =
1849 fabs(vp.rotation) < 0.0001 && vp.m_projection_type == PROJECTION_MERCATOR;
1850
1851 double nlat, elon, slat, wlon;
1852 float lat, lon;
1853 float gridlatMajor, gridlatMinor, gridlonMajor, gridlonMinor;
1854 wxCoord w, h;
1855
1856 wxColour GridColor = GetGlobalColor("SNDG1");
1857
1858 if (!m_gridfont.IsBuilt()) {
1859 double dpi_factor = g_BasePlatform->GetDisplayDIPMult(this);
1860 wxFont *dFont = FontMgr::Get().GetFont(_("GridText"), 0);
1861 wxFont font = *dFont;
1862 int font_size = wxMax(10, dFont->GetPointSize());
1863 font.SetPointSize(font_size * m_displayScale);
1864 font.SetWeight(wxFONTWEIGHT_NORMAL);
1865
1866 m_gridfont.SetContentScaleFactor(OCPN_GetDisplayContentScaleFactor());
1867 m_gridfont.Build(font, 1, dpi_factor);
1868 }
1869 m_gridfont.SetColor(GridColor);
1870
1871 w = vp.pix_width;
1872 h = vp.pix_height;
1873
1874 LLBBox llbbox = vp.GetBBox();
1875 nlat = llbbox.GetMaxLat();
1876 slat = llbbox.GetMinLat();
1877 elon = llbbox.GetMaxLon();
1878 wlon = llbbox.GetMinLon();
1879
1880 // calculate distance between latitude grid lines
1881 CalcGridSpacing(vp.view_scale_ppm, gridlatMajor, gridlatMinor);
1882 CalcGridSpacing(vp.view_scale_ppm, gridlonMajor, gridlonMinor);
1883
1884 // if it is known the grid has straight lines it's a bit faster
1885 bool straight_latitudes = vp.m_projection_type == PROJECTION_MERCATOR ||
1886 vp.m_projection_type == PROJECTION_WEB_MERCATOR ||
1887 vp.m_projection_type == PROJECTION_EQUIRECTANGULAR;
1888 bool straight_longitudes = vp.m_projection_type == PROJECTION_MERCATOR ||
1889 vp.m_projection_type == PROJECTION_WEB_MERCATOR ||
1890 vp.m_projection_type == PROJECTION_POLAR ||
1891 vp.m_projection_type == PROJECTION_EQUIRECTANGULAR;
1892
1893 double latmargin;
1894 if (straight_latitudes)
1895 latmargin = 0;
1896 else
1897 latmargin = gridlatMajor / 2; // don't draw on poles
1898
1899 slat = wxMax(slat, -90 + latmargin);
1900 nlat = wxMin(nlat, 90 - latmargin);
1901
1902 float startlat = ceil(slat / gridlatMajor) * gridlatMajor;
1903 float startlon = ceil(wlon / gridlonMajor) * gridlonMajor;
1904 float curved_step = wxMin(sqrt(5e-3 / vp.view_scale_ppm), 3);
1905
1906 ocpnDC gldc(*this);
1907 wxPen *pen = wxThePenList->FindOrCreatePen(GridColor, g_GLMinSymbolLineWidth,
1908 wxPENSTYLE_SOLID);
1909 gldc.SetPen(*pen);
1910
1911 // Draw Major latitude grid lines and text
1912
1913 // calculate position of first major latitude grid line
1914 float lon_step = elon - wlon;
1915 if (!straight_latitudes) lon_step /= ceil(lon_step / curved_step);
1916
1917 for (lat = startlat; lat < nlat; lat += gridlatMajor) {
1918 wxPoint r, s;
1919 s.x = INVALID_COORD;
1920 s.y = INVALID_COORD;
1921 for (lon = wlon; lon < elon + lon_step / 2; lon += lon_step) {
1922 m_pParentCanvas->GetCanvasPointPix(lat, lon, &r);
1923 if (s.x != INVALID_COORD && s.y != INVALID_COORD) {
1924 gldc.DrawLine(s.x, s.y, r.x, r.y, false);
1925 }
1926 s = r;
1927 }
1928 }
1929
1930 if (minorgrid) {
1931 // draw minor latitude grid lines
1932 for (lat = ceil(slat / gridlatMinor) * gridlatMinor; lat < nlat;
1933 lat += gridlatMinor) {
1934 wxPoint r;
1935 m_pParentCanvas->GetCanvasPointPix(lat, (elon + wlon) / 2, &r);
1936 gldc.DrawLine(0, r.y, 10, r.y, true);
1937 gldc.DrawLine(w - 10, r.y, w, r.y, false);
1938 }
1939 }
1940
1941 // draw major longitude grid lines
1942 float lat_step = nlat - slat;
1943 if (!straight_longitudes) lat_step /= ceil(lat_step / curved_step);
1944
1945 for (lon = startlon; lon < elon; lon += gridlonMajor) {
1946 wxPoint r, s;
1947 s.x = INVALID_COORD;
1948 s.y = INVALID_COORD;
1949 for (lat = slat; lat < nlat + lat_step / 2; lat += lat_step) {
1950 m_pParentCanvas->GetCanvasPointPix(lat, lon, &r);
1951 if (s.x != INVALID_COORD && s.y != INVALID_COORD) {
1952 gldc.DrawLine(s.x, s.y, r.x, r.y, false);
1953 }
1954 s = r;
1955 }
1956 }
1957
1958 if (minorgrid) {
1959 // draw minor longitude grid lines
1960 for (lon = ceil(wlon / gridlonMinor) * gridlonMinor; lon < elon;
1961 lon += gridlonMinor) {
1962 wxPoint r;
1963 m_pParentCanvas->GetCanvasPointPix((nlat + slat) / 2, lon, &r);
1964 gldc.DrawLine(r.x, 0, r.x, 10, false);
1965 gldc.DrawLine(r.x, h - 10, r.x, h, false);
1966 }
1967 }
1968
1969 // draw text labels
1970 if (abs(vp.rotation) < .1) {
1971 glEnable(GL_TEXTURE_2D);
1972 glEnable(GL_BLEND);
1973 for (lat = startlat; lat < nlat; lat += gridlatMajor) {
1974 if (fabs(lat - wxRound(lat)) < 1e-5) lat = wxRound(lat);
1975
1976 wxString st =
1977 CalcGridText(lat, gridlatMajor, true); // get text for grid line
1978 int iy;
1979 m_gridfont.GetTextExtent(st, 0, &iy);
1980
1981 if (straight_latitudes) {
1982 wxPoint r, s;
1983 m_pParentCanvas->GetCanvasPointPix(lat, elon, &r);
1984 m_pParentCanvas->GetCanvasPointPix(lat, wlon, &s);
1985
1986 float x = 0, y = -1;
1987 y = (float)(r.y * s.x - s.y * r.x) / (s.x - r.x);
1988 if (y < 0 || y > h) {
1989 y = h - iy;
1990 x = (float)(r.x * s.y - s.x * r.y + (s.x - r.x) * y) / (s.y - r.y);
1991 }
1992
1993 m_gridfont.RenderString(st, x, y);
1994 } else {
1995 // iteratively attempt to find where the latitude line crosses x=0
1996 wxPoint2DDouble r;
1997 double y1, y2, lat1, lon1, lat2, lon2;
1998
1999 y1 = 0, y2 = vp.pix_height;
2000 double error = vp.pix_width, lasterror;
2001 int maxiters = 10;
2002 do {
2003 m_pParentCanvas->GetCanvasPixPoint(0, y1, lat1, lon1);
2004 m_pParentCanvas->GetCanvasPixPoint(0, y2, lat2, lon2);
2005
2006 double y = y1 + (lat1 - lat) * (y2 - y1) / (lat1 - lat2);
2007
2008 m_pParentCanvas->GetDoubleCanvasPointPix(
2009 lat, lon1 + (y1 - y) * (lon2 - lon1) / (y1 - y2), &r);
2010
2011 if (fabs(y - y1) < fabs(y - y2))
2012 y1 = y;
2013 else
2014 y2 = y;
2015
2016 lasterror = error;
2017 error = fabs(r.m_x);
2018 if (--maxiters == 0) break;
2019 } while (error > 1 && error < lasterror);
2020
2021 if (error < 1 && r.m_y >= 0 && r.m_y <= vp.pix_height - iy)
2022 r.m_x = 0;
2023 else
2024 // just draw at center longitude
2025 m_pParentCanvas->GetDoubleCanvasPointPix(lat, vp.clon, &r);
2026
2027 m_gridfont.RenderString(st, r.m_x, r.m_y);
2028 }
2029 }
2030
2031 for (lon = startlon; lon < elon; lon += gridlonMajor) {
2032 if (fabs(lon - wxRound(lon)) < 1e-5) lon = wxRound(lon);
2033
2034 wxPoint r, s;
2035 m_pParentCanvas->GetCanvasPointPix(nlat, lon, &r);
2036 m_pParentCanvas->GetCanvasPointPix(slat, lon, &s);
2037
2038 float xlon = lon;
2039 if (xlon > 180.0)
2040 xlon -= 360.0;
2041 else if (xlon <= -180.0)
2042 xlon += 360.0;
2043
2044 wxString st = CalcGridText(xlon, gridlonMajor, false);
2045 int ix;
2046 m_gridfont.GetTextExtent(st, &ix, 0);
2047
2048 if (straight_longitudes) {
2049 float x = -1, y = 0;
2050 x = (float)(r.x * s.y - s.x * r.y) / (s.y - r.y);
2051 if (x < 0 || x > w) {
2052 x = w - ix;
2053 y = (float)(r.y * s.x - s.y * r.x + (s.y - r.y) * x) / (s.x - r.x);
2054 }
2055
2056 m_gridfont.RenderString(st, x, y);
2057 } else {
2058 // iteratively attempt to find where the latitude line crosses x=0
2059 wxPoint2DDouble r;
2060 double x1, x2, lat1, lon1, lat2, lon2;
2061
2062 x1 = 0, x2 = vp.pix_width;
2063 double error = vp.pix_height, lasterror;
2064 do {
2065 m_pParentCanvas->GetCanvasPixPoint(x1, 0, lat1, lon1);
2066 m_pParentCanvas->GetCanvasPixPoint(x2, 0, lat2, lon2);
2067
2068 double x = x1 + (lon1 - lon) * (x2 - x1) / (lon1 - lon2);
2069
2070 m_pParentCanvas->GetDoubleCanvasPointPix(
2071 lat1 + (x1 - x) * (lat2 - lat1) / (x1 - x2), lon, &r);
2072
2073 if (fabs(x - x1) < fabs(x - x2))
2074 x1 = x;
2075 else
2076 x2 = x;
2077
2078 lasterror = error;
2079 error = fabs(r.m_y);
2080 } while (error > 1 && error < lasterror);
2081
2082 if (error < 1 && r.m_x >= 0 && r.m_x <= vp.pix_width - ix)
2083 r.m_y = 0;
2084 else
2085 // failure, instead just draw the text at center latitude
2086 m_pParentCanvas->GetDoubleCanvasPointPix(
2087 wxMin(wxMax(vp.clat, slat), nlat), lon, &r);
2088
2089 m_gridfont.RenderString(st, r.m_x, r.m_y);
2090 }
2091 }
2092
2093 glDisable(GL_TEXTURE_2D);
2094 glDisable(GL_BLEND);
2095 }
2096}
2097
2098void glChartCanvas::DrawEmboss(ocpnDC &dc, emboss_data *emboss) {
2099 if (!emboss) return;
2100
2101 int w = emboss->width, h = emboss->height;
2102
2103 glEnable(GL_TEXTURE_2D);
2104
2105 // render using opengl and alpha blending
2106 if (!emboss->gltexind) { /* upload to texture */
2107
2108 emboss->glwidth = NextPow2(emboss->width);
2109 emboss->glheight = NextPow2(emboss->height);
2110
2111 /* convert to luminance alpha map */
2112 int size = emboss->glwidth * emboss->glheight;
2113 char *data = new char[2 * size];
2114 for (int i = 0; i < h; i++) {
2115 for (int j = 0; j < emboss->glwidth; j++) {
2116 if (j < w) {
2117 data[2 * ((i * emboss->glwidth) + j)] =
2118 (char)(emboss->pmap[(i * w) + j] > 0 ? 0 : 255);
2119 data[2 * ((i * emboss->glwidth) + j) + 1] =
2120 (char)abs((emboss->pmap[(i * w) + j]));
2121 }
2122 }
2123 }
2124
2125 glGenTextures(1, &emboss->gltexind);
2126 glBindTexture(GL_TEXTURE_2D, emboss->gltexind);
2127 glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, emboss->glwidth,
2128 emboss->glheight, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE,
2129 data);
2130 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2131 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2132
2133 delete[] data;
2134 }
2135
2136 glBindTexture(GL_TEXTURE_2D, emboss->gltexind);
2137
2138 glEnable(GL_BLEND);
2139
2140 int x = emboss->x, y = emboss->y;
2141
2142 float wp = (float)w / emboss->glwidth;
2143 float hp = (float)h / emboss->glheight;
2144
2145 float coords[8];
2146 float uv[8];
2147
2148 // normal uv
2149 uv[0] = 0;
2150 uv[1] = 0;
2151 uv[2] = wp;
2152 uv[3] = 0;
2153 uv[4] = wp;
2154 uv[5] = hp;
2155 uv[6] = 0;
2156 uv[7] = hp;
2157
2158 // pixels
2159 coords[0] = 0;
2160 coords[1] = 0;
2161 coords[2] = w;
2162 coords[3] = 0;
2163 coords[4] = w;
2164 coords[5] = h;
2165 coords[6] = 0;
2166 coords[7] = h;
2167
2168 // FIXME(dave) Find a way to make this thing a little transparaent
2169 RenderSingleTexture(dc, coords, uv, m_pParentCanvas->GetpVP(), x, y, 0);
2170
2171 glDisable(GL_BLEND);
2172 glDisable(GL_TEXTURE_2D);
2173}
2174
2175void glChartCanvas::ShipDraw(ocpnDC &dc) {
2176 if (!m_pParentCanvas->GetVP().IsValid()) return;
2177 wxPoint GPSOffsetPixels(0, 0);
2178 wxPoint2DDouble lGPSPoint, lShipMidPoint;
2179
2180 // COG/SOG may be undefined in NMEA data stream
2181 float pCog = std::isnan(gCog) ? 0 : gCog;
2182 float pSog = std::isnan(gSog) ? 0 : gSog;
2183
2184 // Here, calculate the ownship location on screen, and make it so.
2185 // Special case: No need for such precision on chart drag operations
2186 double shift_dx = 0;
2187 double shift_dy = 0;
2188 bool dynamic = m_pParentCanvas->m_animationActive ||
2189 m_pParentCanvas->m_MouseDragging ||
2190 m_pParentCanvas->m_chart_drag_inertia_active;
2191 if (m_pParentCanvas->m_bFollow && !dynamic) {
2192 lGPSPoint.m_x = m_pParentCanvas->GetVP().pix_width / 2;
2193 lGPSPoint.m_y = m_pParentCanvas->GetVP().pix_height / 2;
2194 if (m_pParentCanvas->m_bLookAhead) {
2195 // In "b_follow" mode, we have a-priori information about the desired
2196 // screen coordinates of ownship, as a pixel offset from center.
2197 // Use this information as a performance optimization,
2198 // at larger viewing scales.
2199 if (m_pParentCanvas->GetVPChartScale() < 2e6) {
2200 double angle = m_pParentCanvas->dir_to_shift * PI / 180.;
2201 angle += m_pParentCanvas->GetVPRotation();
2202 shift_dx = m_pParentCanvas->meters_to_shift * sin(angle) *
2203 m_pParentCanvas->GetVPScale();
2204 lGPSPoint.m_x -= shift_dx / cos(gLat * PI / 180.);
2205 shift_dy = m_pParentCanvas->meters_to_shift * cos(angle) *
2206 m_pParentCanvas->GetVPScale();
2207 lGPSPoint.m_y += shift_dy / cos(gLat * PI / 180.);
2208 } else {
2209 m_pParentCanvas->GetDoubleCanvasPointPix(gLat, gLon, &lGPSPoint);
2210 }
2211 } else {
2212 lGPSPoint.m_x -= m_pParentCanvas->m_OSoffsetx;
2213 lGPSPoint.m_y += m_pParentCanvas->m_OSoffsety;
2214 }
2215 } else {
2216 m_pParentCanvas->GetDoubleCanvasPointPixVP(m_pParentCanvas->GetVP(), gLat,
2217 gLon, &lGPSPoint);
2218 }
2219
2220 lShipMidPoint = lGPSPoint;
2221
2222 // Draw the icon rotated to the COG
2223 // or to the Hdt if available
2224 float icon_hdt = pCog;
2225 if (!std::isnan(gHdt)) icon_hdt = gHdt;
2226
2227 // COG may be undefined in NMEA data stream
2228 if (std::isnan(icon_hdt)) icon_hdt = 0.0;
2229
2230 // Calculate the ownship drawing angle icon_rad using an assumed 10 minute
2231 // predictor
2232 double osd_head_lat, osd_head_lon;
2233 wxPoint2DDouble osd_head_point;
2234
2235 ll_gc_ll(gLat, gLon, icon_hdt, pSog * 10. / 60., &osd_head_lat,
2236 &osd_head_lon);
2237
2238 m_pParentCanvas->GetDoubleCanvasPointPixVP(
2239 m_pParentCanvas->GetVP(), osd_head_lat, osd_head_lon, &osd_head_point);
2240
2241 double icon_rad = atan2f((float)(osd_head_point.m_y - lShipMidPoint.m_y),
2242 (float)(osd_head_point.m_x - lShipMidPoint.m_x));
2243 icon_rad += (float)PI;
2244
2245 if (pSog < 0.2)
2246 icon_rad =
2247 ((icon_hdt + 90.) * PI / 180.) + m_pParentCanvas->GetVP().rotation;
2248
2249 // Another draw test ,based on pixels, assuming the ship icon is a fixed
2250 // nominal size and is just barely outside the viewport ....
2251 BoundingBox bb_screen(0, 0, m_pParentCanvas->GetVP().pix_width,
2252 m_pParentCanvas->GetVP().pix_height);
2253
2254 // TODO: fix to include actual size of boat that will be rendered
2255 int img_height = 0;
2256
2257 if (bb_screen.PointInBox(lShipMidPoint, 20)) {
2258 if (g_GLOptions.m_GLLineSmoothing) glEnable(GL_LINE_SMOOTH);
2259 if (g_GLOptions.m_GLPolygonSmoothing) glEnable(GL_POLYGON_SMOOTH);
2260
2261 if (m_pParentCanvas->GetVP().chart_scale >
2262 300000) // According to S52, this should be 50,000
2263 {
2264 float scale_factor = 1.0;
2265 // Scale the generic icon to ChartScaleFactor, slightly softened....
2266 if ((g_ChartScaleFactorExp > 1.0) && (g_OwnShipIconType == 0))
2267 scale_factor = (log(g_ChartScaleFactorExp) + 1.0) * 1.1;
2268
2269 float nominal_ownship_size_mm = m_pParentCanvas->m_display_size_mm / 44.0;
2270 nominal_ownship_size_mm = wxMin(nominal_ownship_size_mm, 15.0);
2271 nominal_ownship_size_mm = wxMax(nominal_ownship_size_mm, 7.0);
2272
2273 scale_factor *= m_pParentCanvas->GetContentScaleFactor();
2274
2275 float nominal_ownship_size_pixels =
2276 wxMax(20.0, m_pParentCanvas->GetPixPerMM() *
2277 nominal_ownship_size_mm); // nominal length, but not
2278 // less than 20 pixel
2279 float v = (nominal_ownship_size_pixels * scale_factor) / 3;
2280
2281 wxPen ppSmallScaleShip;
2282 if (SHIP_NORMAL == m_pParentCanvas->m_ownship_state)
2283 ppSmallScaleShip =
2284 wxPen(GetGlobalColor("URED"), v / 5, wxPENSTYLE_SOLID);
2285 else
2286 ppSmallScaleShip =
2287 wxPen(GetGlobalColor("YELO1"), v / 5, wxPENSTYLE_SOLID);
2288 dc.SetPen(ppSmallScaleShip);
2289
2290 dc.SetBrush(wxBrush(GetGlobalColor("URED"), wxBRUSHSTYLE_TRANSPARENT));
2291
2292 // start with cross
2293 dc.DrawLine((-v * 1.2) + lShipMidPoint.m_x, lShipMidPoint.m_y,
2294 (v * 1.2) + lShipMidPoint.m_x, lShipMidPoint.m_y);
2295 dc.DrawLine(lShipMidPoint.m_x, (-v * 1.2) + lShipMidPoint.m_y,
2296 lShipMidPoint.m_x, (v * 1.2) + lShipMidPoint.m_y);
2297
2298 // Two circles
2299 dc.StrokeCircle(lShipMidPoint.m_x, lShipMidPoint.m_y, v);
2300 dc.StrokeCircle(lShipMidPoint.m_x, lShipMidPoint.m_y, 0.6 * v);
2301 img_height = 20;
2302 } else {
2303 int draw_color = SHIP_INVALID;
2304 if (SHIP_NORMAL == m_pParentCanvas->m_ownship_state)
2305 draw_color = SHIP_NORMAL;
2306 else if (SHIP_LOWACCURACY == m_pParentCanvas->m_ownship_state)
2307 draw_color = SHIP_LOWACCURACY;
2308
2309 if (!ownship_tex ||
2310 (draw_color !=
2311 ownship_color)) { /* initial run, create texture for ownship,
2312 also needed at colorscheme changes (not
2313 implemented) */
2314
2315 ownship_color = draw_color;
2316
2317 if (ownship_tex) glDeleteTextures(1, &ownship_tex);
2318
2319 glGenTextures(1, &ownship_tex);
2320 glBindTexture(GL_TEXTURE_2D, ownship_tex);
2321
2322 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2323 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2324
2325 wxImage image;
2326 if (m_pParentCanvas->m_pos_image_user) {
2327 switch (draw_color) {
2328 case SHIP_INVALID:
2329 image = *m_pParentCanvas->m_pos_image_user_grey;
2330 break;
2331 case SHIP_NORMAL:
2332 image = *m_pParentCanvas->m_pos_image_user;
2333 break;
2334 case SHIP_LOWACCURACY:
2335 image = *m_pParentCanvas->m_pos_image_user_yellow;
2336 break;
2337 }
2338 } else {
2339 switch (draw_color) {
2340 case SHIP_INVALID:
2341 image = *m_pParentCanvas->m_pos_image_grey;
2342 break;
2343 case SHIP_NORMAL:
2344 image = *m_pParentCanvas->m_pos_image_red;
2345 break;
2346 case SHIP_LOWACCURACY:
2347 image = *m_pParentCanvas->m_pos_image_yellow;
2348 break;
2349 }
2350 }
2351
2352 int w = image.GetWidth(), h = image.GetHeight();
2353 int glw = NextPow2(w), glh = NextPow2(h);
2354 ownship_size = wxSize(w, h);
2355 ownship_tex_size = wxSize(glw, glh);
2356
2357 unsigned char *d = image.GetData();
2358 unsigned char *a = image.GetAlpha();
2359 unsigned char *e = new unsigned char[4 * w * h];
2360
2361 if (d && e && a) {
2362 for (int p = 0; p < w * h; p++) {
2363 e[4 * p + 0] = d[3 * p + 0];
2364 e[4 * p + 1] = d[3 * p + 1];
2365 e[4 * p + 2] = d[3 * p + 2];
2366 e[4 * p + 3] = a[p];
2367 }
2368 }
2369 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, glw, glh, 0, GL_RGBA,
2370 GL_UNSIGNED_BYTE, 0);
2371
2372 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE,
2373 e);
2374 delete[] e;
2375 }
2376
2377 /* establish ship color */
2378#ifndef USE_ANDROID_GLES2
2379 if (m_pParentCanvas->m_pos_image_user)
2380 glColor4ub(255, 255, 255, 255);
2381 else if (SHIP_NORMAL == m_pParentCanvas->m_ownship_state)
2382 glColor4ub(255, 0, 0, 255);
2383 else if (SHIP_LOWACCURACY == m_pParentCanvas->m_ownship_state)
2384 glColor4ub(255, 255, 0, 255);
2385 else
2386 glColor4ub(128, 128, 128, 255);
2387#endif
2388 float scale_factor_y = 1.0;
2389 float scale_factor_x = 1.0;
2390
2391 int ownShipWidth = 22; // Default values from s_ownship_icon
2392 int ownShipLength = 84;
2393 lShipMidPoint = lGPSPoint;
2394
2395 /* scaled ship? */
2396 if (g_OwnShipIconType != 0)
2397 m_pParentCanvas->ComputeShipScaleFactor(
2398 icon_hdt, ownShipWidth, ownShipLength, lShipMidPoint,
2399 GPSOffsetPixels, lGPSPoint, scale_factor_x, scale_factor_y);
2400
2401 glEnable(GL_BLEND);
2402
2403 // Scale the generic icon to ChartScaleFactor, slightly softened....
2404 if ((g_ShipScaleFactorExp > 1.0) && (g_OwnShipIconType == 0)) {
2405 scale_factor_x = (log(g_ShipScaleFactorExp) + 1.0) * 1.1;
2406 scale_factor_y = (log(g_ShipScaleFactorExp) + 1.0) * 1.1;
2407 }
2408
2409 // Correct for scaled displays, e.g. Retina
2410 scale_factor_x *= m_pParentCanvas->GetContentScaleFactor();
2411 scale_factor_y *= m_pParentCanvas->GetContentScaleFactor();
2412
2413 // Set the size of the little circle showing the GPS reference position
2414 // Set a default early, adjust later based on render type
2415 float gps_circle_radius = 3.0;
2416
2417 if (g_OwnShipIconType == 0) { // Default Bitmap
2418
2419 glEnable(GL_TEXTURE_2D);
2420 glBindTexture(GL_TEXTURE_2D, ownship_tex);
2421
2422 // We choose to render the ownship bitmap at roughly the same size( in
2423 // pixels ) as the DC mode renderer.
2424 // For ultra-high definition displays, we clamp the actual on-screen
2425 // size to be no smaller than 7.0 mm Similarly, for lo-res displays, we
2426 // limit the actual size to be no larger than 15 mm maximum.
2427
2428 // Get bitmap height in pixels
2429 int image_height_bitmap = m_pParentCanvas->m_pos_image_red->GetHeight();
2430 if (m_pParentCanvas->m_pos_image_user)
2431 image_height_bitmap = m_pParentCanvas->m_pos_image_user->GetHeight();
2432
2433 float nominal_ownship_size_mm =
2434 image_height_bitmap / m_pParentCanvas->GetPixPerMM();
2435
2436 nominal_ownship_size_mm = wxMin(nominal_ownship_size_mm, 15.0);
2437 nominal_ownship_size_mm = wxMax(nominal_ownship_size_mm, 7.0);
2438
2439 float nominal_ownship_size_pixels =
2440 m_pParentCanvas->GetPixPerMM() * nominal_ownship_size_mm;
2441
2442 if (m_pParentCanvas->GetContentScaleFactor() == 1.0) {
2443 nominal_ownship_size_pixels = wxMax(
2444 20.0, nominal_ownship_size_pixels); // not less than 20 pixel
2445 }
2446
2447 float h = nominal_ownship_size_pixels * scale_factor_y;
2448 float w = nominal_ownship_size_pixels * scale_factor_x *
2449 ownship_size.x / ownship_size.y;
2450 float glw = ownship_tex_size.x, glh = ownship_tex_size.y;
2451 float u = ownship_size.x / glw, v = ownship_size.y / glh;
2452
2453 // printf("%g %g %g %g %g %g %g\n",
2454 // nominal_ownship_size_mm, nominal_ownship_size_pixels,
2455 // h, w, u, v, m_pParentCanvas->m_display_size_mm);
2456 // tweak GPS reference point indicator size
2457 gps_circle_radius = w / 5;
2458
2459 float uv[8], coords[8];
2460 uv[0] = 0;
2461 uv[1] = 0;
2462 uv[2] = u;
2463 uv[3] = 0;
2464 uv[4] = u;
2465 uv[5] = v;
2466 uv[6] = 0;
2467 uv[7] = v;
2468
2469 coords[0] = -w / 2;
2470 coords[1] = -h / 2;
2471 coords[2] = w / 2;
2472 coords[3] = -h / 2;
2473 coords[4] = w / 2;
2474 coords[5] = h / 2;
2475 coords[6] = -w / 2;
2476 coords[7] = h / 2;
2477
2478 RenderSingleTexture(dc, coords, uv, m_pParentCanvas->GetpVP(),
2479 lShipMidPoint.m_x, lShipMidPoint.m_y,
2480 icon_rad - PI / 2);
2481 glDisable(GL_TEXTURE_2D);
2482 } else if (g_OwnShipIconType == 1) { // Scaled Bitmap
2483
2484 glEnable(GL_TEXTURE_2D);
2485 glBindTexture(GL_TEXTURE_2D, ownship_tex);
2486
2487 float nominal_ownship_size_pixels_y = 84;
2488 float nominal_ownship_size_pixels_x = 22;
2489
2490 float h = nominal_ownship_size_pixels_y * scale_factor_y;
2491 float w = nominal_ownship_size_pixels_x * scale_factor_x;
2492
2493 float u = (float)ownship_size.x / ownship_tex_size.x,
2494 v = (float)ownship_size.y / ownship_tex_size.y;
2495
2496 // tweak GPS reference point indicator size
2497 gps_circle_radius = w / 5;
2498
2499 float uv[8], coords[8];
2500 uv[0] = 0;
2501 uv[1] = 0;
2502 uv[2] = u;
2503 uv[3] = 0;
2504 uv[4] = u;
2505 uv[5] = v;
2506 uv[6] = 0;
2507 uv[7] = v;
2508
2509 coords[0] = -w / 2;
2510 coords[1] = -h / 2;
2511 coords[2] = w / 2;
2512 coords[3] = -h / 2;
2513 coords[4] = w / 2;
2514 coords[5] = h / 2;
2515 coords[6] = -w / 2;
2516 coords[7] = h / 2;
2517
2518 RenderSingleTexture(dc, coords, uv, m_pParentCanvas->GetpVP(),
2519 lShipMidPoint.m_x, lShipMidPoint.m_y,
2520 icon_rad - PI / 2);
2521
2522 glDisable(GL_TEXTURE_2D);
2523 } else if (g_OwnShipIconType == 2) { // Scaled Vector
2524 // static const GLint s_ownship_icon[] = { 5, -42, 11,
2525 // -28, 11, 42, -11, 42,
2526 // -11, -28, -5,
2527 // -42, -11, 0,
2528 // 11, 0, 0, 42,
2529 // 0, -42 };
2530
2531 wxPoint shipPoints[6];
2532
2533 wxColour colour = m_pParentCanvas->ShipColor();
2534 wxPen ppPen(*wxBLACK, 1);
2535 wxBrush ppBrush(colour);
2536 dc.SetPen(ppPen);
2537 dc.SetBrush(ppBrush);
2538
2539 shipPoints[0].x = 0 * scale_factor_x;
2540 shipPoints[0].y = -28 * scale_factor_y;
2541 shipPoints[1].x = 11 * scale_factor_x;
2542 shipPoints[1].y = -28 * scale_factor_y;
2543 shipPoints[2].x = 11 * scale_factor_x;
2544 shipPoints[2].y = 42 * scale_factor_y;
2545 shipPoints[3].x = 0 * scale_factor_x;
2546 shipPoints[3].y = 42 * scale_factor_y;
2547 dc.DrawPolygon(4, shipPoints, lShipMidPoint.m_x, lShipMidPoint.m_y, 1,
2548 icon_rad - PI / 2);
2549
2550 shipPoints[0].x = 0 * scale_factor_x;
2551 shipPoints[0].y = -42 * scale_factor_y;
2552 shipPoints[1].x = 5 * scale_factor_x;
2553 shipPoints[1].y = -42 * scale_factor_y;
2554 shipPoints[2].x = 11 * scale_factor_x;
2555 shipPoints[2].y = -28 * scale_factor_y;
2556 shipPoints[3].x = 0 * scale_factor_x;
2557 shipPoints[3].y = -28 * scale_factor_y;
2558 dc.DrawPolygon(4, shipPoints, lShipMidPoint.m_x, lShipMidPoint.m_y, 1,
2559 icon_rad - PI / 2);
2560
2561 shipPoints[0].x = 0 * scale_factor_x;
2562 shipPoints[0].y = -28 * scale_factor_y;
2563 shipPoints[1].x = -11 * scale_factor_x;
2564 shipPoints[1].y = -28 * scale_factor_y;
2565 shipPoints[2].x = -11 * scale_factor_x;
2566 shipPoints[2].y = 42 * scale_factor_y;
2567 shipPoints[3].x = 0 * scale_factor_x;
2568 shipPoints[3].y = 42 * scale_factor_y;
2569 dc.DrawPolygon(4, shipPoints, lShipMidPoint.m_x, lShipMidPoint.m_y, 1,
2570 icon_rad - PI / 2);
2571
2572 shipPoints[0].x = 0 * scale_factor_x;
2573 shipPoints[0].y = -42 * scale_factor_y;
2574 shipPoints[1].x = -5 * scale_factor_x;
2575 shipPoints[1].y = -42 * scale_factor_y;
2576 shipPoints[2].x = -11 * scale_factor_x;
2577 shipPoints[2].y = -28 * scale_factor_y;
2578 shipPoints[3].x = 0 * scale_factor_x;
2579 shipPoints[3].y = -28 * scale_factor_y;
2580 dc.DrawPolygon(4, shipPoints, lShipMidPoint.m_x, lShipMidPoint.m_y, 1,
2581 icon_rad - PI / 2);
2582
2583 // draw with cross
2584 double p1x = -11 * scale_factor_x;
2585 double p2x = 11 * scale_factor_x;
2586 double p1y = 0;
2587 double p2y = 0;
2588 double p1xr =
2589 ((p1x)*cos(icon_rad - PI / 2)) - ((p1y)*sin(icon_rad - PI / 2));
2590 double p2xr =
2591 ((p2x)*cos(icon_rad - PI / 2)) - ((p2y)*sin(icon_rad - PI / 2));
2592 double p1yr =
2593 ((p1y)*cos(icon_rad - PI / 2)) + ((p1x)*sin(icon_rad - PI / 2));
2594 double p2yr =
2595 ((p2y)*cos(icon_rad - PI / 2)) + ((p2x)*sin(icon_rad - PI / 2));
2596 dc.DrawLine(p1xr + lShipMidPoint.m_x, p1yr + lShipMidPoint.m_y,
2597 p2xr + lShipMidPoint.m_x, p2yr + lShipMidPoint.m_y);
2598
2599 p1x = 0;
2600 p2x = 0;
2601 p1y = -42 * scale_factor_y;
2602 p2y = 42 * scale_factor_y;
2603 p1xr = ((p1x)*cos(icon_rad - PI / 2)) - ((p1y)*sin(icon_rad - PI / 2));
2604 p2xr = ((p2x)*cos(icon_rad - PI / 2)) - ((p2y)*sin(icon_rad - PI / 2));
2605 p1yr = ((p1y)*cos(icon_rad - PI / 2)) + ((p1x)*sin(icon_rad - PI / 2));
2606 p2yr = ((p2y)*cos(icon_rad - PI / 2)) + ((p2x)*sin(icon_rad - PI / 2));
2607 dc.DrawLine(p1xr + lShipMidPoint.m_x, p1yr + lShipMidPoint.m_y,
2608 p2xr + lShipMidPoint.m_x, p2yr + lShipMidPoint.m_y);
2609 }
2610
2611 img_height = ownShipLength * scale_factor_y;
2612
2613 // Reference point, where the GPS antenna is
2614 if (m_pParentCanvas->m_pos_image_user) gps_circle_radius = 1;
2615
2616 wxPen ppPen1(GetGlobalColor("UBLCK"), 1, wxPENSTYLE_SOLID);
2617 dc.SetPen(ppPen1);
2618 dc.SetBrush(wxBrush(GetGlobalColor("CHWHT")));
2619
2620 dc.StrokeCircle(lGPSPoint.m_x, lGPSPoint.m_y, gps_circle_radius);
2621 }
2622
2623 // glDisableClientState(GL_VERTEX_ARRAY);
2624 glDisable(GL_LINE_SMOOTH);
2625 glDisable(GL_POLYGON_SMOOTH);
2626 glDisable(GL_BLEND);
2627 }
2628
2629 m_pParentCanvas->ShipIndicatorsDraw(dc, img_height, GPSOffsetPixels,
2630 lGPSPoint);
2631}
2632
2633void glChartCanvas::DrawFloatingOverlayObjects(ocpnDC &dc) {
2634 ViewPort &vp = m_pParentCanvas->GetVP();
2635
2636 // Draw any active or selected routes now
2637 extern Routeman *g_pRouteMan;
2638 // extern Track *g_pActiveTrack;
2639 Route *active_route = g_pRouteMan->GetpActiveRoute();
2640
2641 // if( active_route ) active_route->DrawGL( vp, region );
2642 // if( g_pActiveTrack ) g_pActiveTrack->Draw( dc, vp );
2643 // if( m_pParentCanvas->m_pSelectedRoute )
2644 // m_pParentCanvas->m_pSelectedRoute->DrawGL( vp, region );
2645
2646 GridDraw();
2647
2648 g_overlayCanvas = m_pParentCanvas;
2649 if (g_pi_manager) {
2650 g_pi_manager->SendViewPortToRequestingPlugIns(vp);
2651 g_pi_manager->RenderAllGLCanvasOverlayPlugIns(
2652 m_pcontext, vp, m_pParentCanvas->m_canvasIndex, OVERLAY_LEGACY);
2653 }
2654
2655 // all functions called with m_pParentCanvas-> are still slow because they go
2656 // through ocpndc
2657 AISDrawAreaNotices(dc, m_pParentCanvas->GetVP(), m_pParentCanvas);
2658
2659 m_pParentCanvas->DrawAnchorWatchPoints(dc);
2660 AISDraw(dc, m_pParentCanvas->GetVP(), m_pParentCanvas);
2661 ShipDraw(dc);
2662 m_pParentCanvas->AlertDraw(dc);
2663
2664 m_pParentCanvas->RenderVisibleSectorLights(dc);
2665
2666 m_pParentCanvas->RenderRouteLegs(dc);
2667 m_pParentCanvas->RenderShipToActive(dc, true);
2668 m_pParentCanvas->ScaleBarDraw(dc);
2669 s57_DrawExtendedLightSectorsGL(dc, m_pParentCanvas->VPoint,
2670 m_pParentCanvas->extendedSectorLegs);
2671 if (g_pi_manager) {
2672 g_pi_manager->RenderAllGLCanvasOverlayPlugIns(
2673 m_pcontext, vp, m_pParentCanvas->m_canvasIndex, OVERLAY_OVER_SHIPS);
2674 }
2675}
2676
2677void glChartCanvas::DrawChartBar(ocpnDC &dc) {
2678 if (m_pParentCanvas->GetPiano()) {
2679 int canvas_height = GetClientSize().y;
2680 canvas_height *= m_displayScale;
2681
2682 m_pParentCanvas->GetPiano()->DrawGL(
2683 canvas_height - m_pParentCanvas->GetPiano()->GetHeight());
2684 }
2685}
2686
2687void glChartCanvas::DrawQuiting() {
2688#ifndef USE_ANDROID_GLES2
2689 GLubyte pattern[8][8];
2690 for (int y = 0; y < 8; y++)
2691 for (int x = 0; x < 8; x++) pattern[y][x] = (y == x) * 255;
2692
2693 glEnable(GL_BLEND);
2694 glEnable(GL_TEXTURE_2D);
2695 glBindTexture(GL_TEXTURE_2D, 0);
2696
2697 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2698 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2699 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2700
2701 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 8, 8, 0, GL_ALPHA, GL_UNSIGNED_BYTE,
2702 pattern);
2703 glColor3f(0, 0, 0);
2704
2705 float x = GetSize().x, y = GetSize().y;
2706 float u = x / 8, v = y / 8;
2707
2708 glBegin(GL_QUADS);
2709 glTexCoord2f(0, 0);
2710 glVertex2f(0, 0);
2711 glTexCoord2f(0, v);
2712 glVertex2f(0, y);
2713 glTexCoord2f(u, v);
2714 glVertex2f(x, y);
2715 glTexCoord2f(u, 0);
2716 glVertex2f(x, 0);
2717 glEnd();
2718
2719 glDisable(GL_TEXTURE_2D);
2720 glDisable(GL_BLEND);
2721#endif
2722}
2723
2724void glChartCanvas::DrawCloseMessage(wxString msg) {
2725#ifndef USE_ANDROID_GLES2
2726
2727 if (1) {
2728 wxFont *pfont = FontMgr::Get().FindOrCreateFont(
2729 12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
2730
2731 TexFont texfont;
2732
2733 texfont.Build(*pfont, 1, 1);
2734 int w, h;
2735 texfont.GetTextExtent(msg, &w, &h);
2736 h += 2;
2737 int yp = m_pParentCanvas->GetVP().pix_height / 2;
2738 int xp = (m_pParentCanvas->GetVP().pix_width - w) / 2;
2739
2740 glColor3ub(243, 229, 47);
2741
2742 glBegin(GL_QUADS);
2743 glVertex2i(xp, yp);
2744 glVertex2i(xp + w, yp);
2745 glVertex2i(xp + w, yp + h);
2746 glVertex2i(xp, yp + h);
2747 glEnd();
2748
2749 glEnable(GL_BLEND);
2750
2751 glColor3ub(0, 0, 0);
2752 glEnable(GL_TEXTURE_2D);
2753 texfont.RenderString(msg, xp, yp);
2754 glDisable(GL_TEXTURE_2D);
2755 glDisable(GL_BLEND);
2756 }
2757#endif
2758}
2759
2760GLShaderProgram *pStaticShader;
2761
2762static std::list<double *> combine_work_data;
2763static void combineCallbackD(GLdouble coords[3], GLdouble *vertex_data[4],
2764 GLfloat weight[4], GLdouble **dataOut) {
2765 double *vertex = new double[3];
2766 combine_work_data.push_back(vertex);
2767 memcpy(vertex, coords, 3 * (sizeof *coords));
2768 *dataOut = vertex;
2769}
2770
2771#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
2772void vertexCallbackD_GLSL(GLvoid *vertex) {
2773 // Grow the work buffer if necessary
2774 if (s_tess_vertex_idx > s_tess_buf_len - 8) {
2775 int new_buf_len = s_tess_buf_len + 100;
2776 GLfloat *tmp = s_tess_work_buf;
2777
2778 s_tess_work_buf =
2779 (GLfloat *)realloc(s_tess_work_buf, new_buf_len * sizeof(GLfloat));
2780 if (NULL == s_tess_work_buf) {
2781 free(tmp);
2782 tmp = NULL;
2783 } else
2784 s_tess_buf_len = new_buf_len;
2785 }
2786
2787 GLdouble *pointer = (GLdouble *)vertex;
2788
2789 s_tess_work_buf[s_tess_vertex_idx++] = (float)pointer[0];
2790 s_tess_work_buf[s_tess_vertex_idx++] = (float)pointer[1];
2791
2792 s_nvertex++;
2793}
2794
2795void beginCallbackD_GLSL(GLenum mode) {
2796 s_tess_vertex_idx_this = s_tess_vertex_idx;
2797 s_tess_mode = mode;
2798 s_nvertex = 0;
2799}
2800
2801void endCallbackD_GLSL() {
2802 GLShaderProgram *shader = pStaticShader;
2803 shader->Bind();
2804
2805 shader->SetUniformMatrix4fv("MVMatrix",
2806 (GLfloat *)s_tessVP.vp_matrix_transform);
2807
2808 mat4x4 identityMatrix;
2809 mat4x4_identity(identityMatrix);
2810 shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)identityMatrix);
2811
2812 // Use color stored in static variable.
2813 float colorv[4];
2814 colorv[0] = s_regionColor.Red() / float(256);
2815 colorv[1] = s_regionColor.Green() / float(256);
2816 colorv[2] = s_regionColor.Blue() / float(256);
2817 colorv[3] = s_regionColor.Alpha() / float(256);
2818 shader->SetUniform4fv("color", colorv);
2819
2820 float *bufPt = &s_tess_work_buf[s_tess_vertex_idx_this];
2821 shader->SetAttributePointerf("position", bufPt);
2822
2823 glDrawArrays(s_tess_mode, 0, s_nvertex);
2824
2825 shader->UnBind();
2826}
2827#else
2828void vertexCallbackD(GLvoid *vertex) { glVertex3dv((GLdouble *)vertex); }
2829
2830void beginCallbackD(GLenum mode) { glBegin(mode); }
2831
2832void endCallbackD() { glEnd(); }
2833
2834#endif
2835
2836void glChartCanvas::DrawRegion(ViewPort &vp, const LLRegion &region) {
2837 float lat_dist, lon_dist;
2838 GetLatLonCurveDist(vp, lat_dist, lon_dist);
2839
2840 GLUtesselator *tobj = gluNewTess();
2841 if (!pStaticShader) pStaticShader = GetStaticTriShader();
2842
2843#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
2844 gluTessCallback(tobj, GLU_TESS_VERTEX, (_GLUfuncptr)&vertexCallbackD_GLSL);
2845 gluTessCallback(tobj, GLU_TESS_BEGIN, (_GLUfuncptr)&beginCallbackD_GLSL);
2846 gluTessCallback(tobj, GLU_TESS_END, (_GLUfuncptr)&endCallbackD_GLSL);
2847 gluTessCallback(tobj, GLU_TESS_COMBINE, (_GLUfuncptr)&combineCallbackD);
2848 s_tessVP = vp;
2849
2850#else
2851 gluTessCallback(tobj, GLU_TESS_VERTEX, (_GLUfuncptr)&vertexCallbackD);
2852 gluTessCallback(tobj, GLU_TESS_BEGIN, (_GLUfuncptr)&beginCallbackD);
2853 gluTessCallback(tobj, GLU_TESS_END, (_GLUfuncptr)&endCallbackD);
2854 gluTessCallback(tobj, GLU_TESS_COMBINE, (_GLUfuncptr)&combineCallbackD);
2855#endif
2856
2857 gluTessNormal(tobj, 0, 0, 1);
2858
2859 gluTessBeginPolygon(tobj, NULL);
2860 for (auto i = region.contours.begin(); i != region.contours.end(); i++) {
2861 gluTessBeginContour(tobj);
2862 contour_pt l = *i->rbegin();
2863 double sml[2];
2864 bool sml_valid = false;
2865 for (poly_contour::const_iterator j = i->begin(); j != i->end(); j++) {
2866 int lat_splits = floor(fabs(j->y - l.y) / lat_dist);
2867 int lon_splits = floor(fabs(j->x - l.x) / lon_dist);
2868 int splits = wxMax(lat_splits, lon_splits) + 1;
2869
2870 double smj[2];
2871 if (splits != 1) {
2872 // must perform border interpolation in mercator space as this is what
2873 // the charts use
2874 toSM(j->y, j->x, 0, 0, smj + 0, smj + 1);
2875 if (!sml_valid) toSM(l.y, l.x, 0, 0, sml + 0, sml + 1);
2876 }
2877
2878 for (int i = 0; i < splits; i++) {
2879 double lat, lon;
2880 if (i == splits - 1)
2881 lat = j->y, lon = j->x;
2882 else {
2883 double d = (double)(i + 1) / splits;
2884 fromSM(d * smj[0] + (1 - d) * sml[0], d * smj[1] + (1 - d) * sml[1],
2885 0, 0, &lat, &lon);
2886 }
2887 wxPoint2DDouble q = vp.GetDoublePixFromLL(lat, lon);
2888 if (std::isnan(q.m_x)) continue;
2889
2890 double *p = new double[6];
2891
2892 // p[0] = q.m_x, p[1] = q.m_y, p[2] = 0;
2893 // It is reasonable to use wxRound() here,
2894 // since we are working with pixel coordinates at this point
2895 p[0] = wxRound(q.m_x), p[1] = wxRound(q.m_y), p[2] = 0;
2896
2897 // wxPoint pt = vp.GetPixFromLL(lat, lon);
2898 // p[0] = pt.x, p[1] = pt.y, p[2] = 0;
2899
2900 gluTessVertex(tobj, p, p);
2901 combine_work_data.push_back(p);
2902 }
2903 l = *j;
2904
2905 if ((sml_valid = splits != 1)) memcpy(sml, smj, sizeof smj);
2906 }
2907 gluTessEndContour(tobj);
2908 }
2909 gluTessEndPolygon(tobj);
2910
2911 gluDeleteTess(tobj);
2912
2913 for (std::list<double *>::iterator i = combine_work_data.begin();
2914 i != combine_work_data.end(); i++)
2915 delete[] *i;
2916 combine_work_data.clear();
2917}
2918
2919/* set stencil buffer to clip in this region, and optionally clear using the
2920 * current color */
2921void glChartCanvas::SetClipRegion(ViewPort &vp, const LLRegion &region) {
2922 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // disable color buffer
2923
2924 if (s_b_useStencil) {
2925 // Create a stencil buffer for clipping to the region
2926 glEnable(GL_STENCIL_TEST);
2927 glStencilMask(0x1); // write only into bit 0 of the stencil buffer
2928 glClear(GL_STENCIL_BUFFER_BIT);
2929
2930 // We are going to write "1" into the stencil buffer wherever the region
2931 // is valid
2932 glStencilFunc(GL_ALWAYS, 1, 1);
2933 glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
2934 }
2935// #ifndef USE_ANDROID_GLES2
2936#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
2937
2938 else // Use depth buffer for clipping
2939 {
2940 glEnable(GL_DEPTH_TEST); // to enable writing to the depth buffer
2941 glDepthFunc(GL_ALWAYS); // to ensure everything you draw passes
2942 glDepthMask(GL_TRUE); // to allow writes to the depth buffer
2943
2944 glClear(GL_DEPTH_BUFFER_BIT); // for a fresh start
2945
2946 // Decompose the region into rectangles, and draw as quads
2947 // With z = 1
2948 // dep buffer clear = 1
2949 // 1 makes 0 in dep buffer, works
2950 // 0 make .5 in depth buffer
2951 // -1 makes 1 in dep buffer
2952
2953 // Depth buffer runs from 0 at z = 1 to 1 at z = -1
2954 // Draw the clip geometry at z = 0.5, giving a depth buffer value of 0.25
2955 // Subsequent drawing at z=0 (depth = 0.5) will pass if using
2956 // glDepthFunc(GL_GREATER);
2957 glTranslatef(0, 0, .5);
2958 }
2959#endif
2960
2961 s_regionColor = wxColor(0, 0, 0, 255);
2962 DrawRegion(vp, region);
2963
2964 if (s_b_useStencil) {
2965 // Now set the stencil ops to subsequently render only where the stencil
2966 // bit is "1"
2967 glStencilFunc(GL_EQUAL, 1, 1);
2968 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
2969 }
2970// #ifndef USE_ANDROID_GLES2
2971#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
2972 else {
2973 glDepthFunc(GL_GREATER); // Set the test value
2974 glDepthMask(GL_FALSE); // disable depth buffer
2975 glTranslatef(0, 0, -.5); // reset translation
2976 }
2977#endif
2978 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // re-enable color buffer
2979}
2980
2981void glChartCanvas::SetClipRect(const ViewPort &vp, const wxRect &rect,
2982 bool b_clear) {
2983 /* for some reason this causes an occasional bug in depth mode, I cannot
2984 seem to solve it yet, so for now: */
2985 if (s_b_useStencil && s_b_useScissorTest) {
2986 wxRect vp_rect(0, 0, vp.pix_width, vp.pix_height);
2987 if (rect != vp_rect) {
2988 glEnable(GL_SCISSOR_TEST);
2989 glScissor(rect.x, vp.pix_height - rect.height - rect.y, rect.width,
2990 rect.height);
2991 }
2992#ifndef USE_ANDROID_GLES2
2993#endif
2994 return;
2995 }
2996}
2997
2998void glChartCanvas::DisableClipRegion() {
2999 glDisable(GL_SCISSOR_TEST);
3000 glDisable(GL_STENCIL_TEST);
3001 glDisable(GL_DEPTH_TEST);
3002}
3003
3004void glChartCanvas::Invalidate() {
3005 /* should probably use a different flag for this */
3006 m_cache_vp.Invalidate();
3007}
3008
3009void glChartCanvas::RenderRasterChartRegionGL(ChartBase *chart, ViewPort &vp,
3010 LLRegion &region) {
3011 ChartBaseBSB *pBSBChart = dynamic_cast<ChartBaseBSB *>(chart);
3012 if (!pBSBChart) return;
3013
3015 return; // don't want multiple texfactories to exist
3016
3017 // Look for the texture factory for this chart
3018 wxString key = chart->GetHashKey();
3019
3020 glTexFactory *pTexFact;
3021 ChartPathHashTexfactType &hash = g_glTextureManager->m_chart_texfactory_hash;
3022 ChartPathHashTexfactType::iterator ittf = hash.find(key);
3023
3024 // Not Found ?
3025 if (ittf == hash.end()) {
3026 hash[key] = new glTexFactory(chart, g_raster_format);
3027 hash[key]->SetHashKey(key);
3028 }
3029
3030 pTexFact = hash[key];
3031 pTexFact->SetLRUTime(++m_LRUtime);
3032
3033 // for small scales, don't use normalized coordinates for accuracy (difference
3034 // is up to 3 meters error)
3035 bool use_norm_vp =
3036 glChartCanvas::HasNormalizedViewPort(vp) && pBSBChart->GetPPM() < 1;
3037 pTexFact->PrepareTiles(vp, use_norm_vp, pBSBChart);
3038
3039 // For underzoom cases, we will define the textures as having their base
3040 // levels equivalent to a level "n" mipmap, where n is calculated, and is
3041 // always binary This way we can avoid loading much texture memory
3042
3043 int base_level;
3044 if (vp.m_projection_type == PROJECTION_MERCATOR &&
3045 chart->GetChartProjectionType() == PROJECTION_MERCATOR) {
3046 double scalefactor = pBSBChart->GetRasterScaleFactor(vp);
3047 base_level = log(scalefactor) / log(2.0);
3048
3049 if (base_level < 0) /* for overzoom */
3050 base_level = 0;
3051 if (base_level > g_mipmap_max_level) base_level = g_mipmap_max_level;
3052 } else
3053 base_level = 0; // base level should be computed per tile, for now load all
3054
3055 /* setup opengl parameters */
3056 glEnable(GL_TEXTURE_2D);
3057#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
3058 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3059
3060 glEnableClientState(GL_VERTEX_ARRAY);
3061 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
3062
3063 if (use_norm_vp) {
3064 glPushMatrix();
3065 double lat, lon;
3066 pTexFact->GetCenter(lat, lon);
3067 MultMatrixViewPort(vp, lat, lon);
3068 }
3069#endif
3070
3071 LLBBox box = region.GetBox();
3072 int numtiles;
3073 int mem_used = 0;
3074 if (g_memCacheLimit > 0) {
3075 // GetMemoryStatus is slow on linux
3076 platform::GetMemoryStatus(0, &mem_used);
3077 }
3078
3079 glTexTile **tiles = pTexFact->GetTiles(numtiles);
3080 for (int i = 0; i < numtiles; i++) {
3081 glTexTile *tile = tiles[i];
3082 if (region.IntersectOut(tile->box)) {
3083 /* user setting is in MB while we count exact bytes */
3084 bool bGLMemCrunch =
3085 g_tex_mem_used > g_GLOptions.m_iTextureMemorySize * 1024 * 1024;
3086 if (bGLMemCrunch) pTexFact->DeleteTexture(tile->rect);
3087 } else {
3088 bool texture = pTexFact->PrepareTexture(base_level, tile->rect,
3089 global_color_scheme, mem_used);
3090
3091 float *coords;
3092 if (use_norm_vp)
3093 coords = tile->m_coords;
3094 else {
3095 coords = new float[2 * tile->m_ncoords];
3096 for (int i = 0; i < tile->m_ncoords; i++) {
3097 wxPoint2DDouble p = vp.GetDoublePixFromLL(tile->m_coords[2 * i + 0],
3098 tile->m_coords[2 * i + 1]);
3099 coords[2 * i + 0] = p.m_x;
3100 coords[2 * i + 1] = p.m_y;
3101 }
3102 }
3103
3104#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
3105 RenderTextures(m_gldc, coords, tile->m_texcoords, 4,
3106 m_pParentCanvas->GetpVP());
3107#else
3108 if (!texture) { // failed to load, draw red
3109 glDisable(GL_TEXTURE_2D);
3110 glColor3f(1, 0, 0);
3111 }
3112
3113 glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), tile->m_texcoords);
3114 glVertexPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), coords);
3115 glDrawArrays(GL_QUADS, 0, tile->m_ncoords);
3116#endif
3117 if (!texture) glEnable(GL_TEXTURE_2D);
3118
3119 if (!use_norm_vp) delete[] coords;
3120 }
3121 }
3122
3123 glDisable(GL_TEXTURE_2D);
3124
3125#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
3126 if (use_norm_vp) glPopMatrix();
3127
3128 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
3129 glDisableClientState(GL_VERTEX_ARRAY);
3130#endif
3131}
3132
3133void glChartCanvas::RenderQuiltViewGL(ViewPort &vp,
3134 const OCPNRegion &rect_region) {
3135 if (!m_pParentCanvas->m_pQuilt->GetnCharts() ||
3136 m_pParentCanvas->m_pQuilt->IsBusy())
3137 return;
3138
3139 // render the quilt
3140 ChartBase *chart = m_pParentCanvas->m_pQuilt->GetFirstChart();
3141 if (!chart) {
3142 printf(" Chart NULL\n");
3143 chart = m_pParentCanvas->m_pQuilt->GetFirstChart();
3144 }
3145
3146 // Check the first, smallest scale chart
3147 if (chart) {
3148 // if( ! m_pParentCanvas->IsChartLargeEnoughToRender( chart, vp )
3149 // )
3150 // chart = NULL;
3151 }
3152
3153 LLRegion region = vp.GetLLRegion(rect_region);
3154
3155 LLRegion rendered_region;
3156 while (chart) {
3157 // This test does not need to be done for raster charts, since
3158 // we can assume that texture binding is acceptably fast regardless of the
3159 // render region, and that the quilt zoom methods choose a reasonable
3160 // reference chart.
3161 if (chart->GetChartFamily() != CHART_FAMILY_RASTER) {
3162 // if( ! m_pParentCanvas->IsChartLargeEnoughToRender(
3163 // chart, vp ) ) {
3164 // chart = m_pParentCanvas->m_pQuilt->GetNextChart();
3165 // continue;
3166 // }
3167 }
3168
3169 QuiltPatch *pqp = m_pParentCanvas->m_pQuilt->GetCurrentPatch();
3170 if (pqp->b_Valid) {
3171 LLRegion get_region = pqp->ActiveRegion;
3172 bool b_rendered = false;
3173
3174 if (!pqp->b_overlay) {
3175 get_region.Intersect(region);
3176 if (!get_region.Empty()) {
3177 if (chart->GetChartFamily() == CHART_FAMILY_RASTER) {
3178 ChartBaseBSB *Patch_Ch_BSB = dynamic_cast<ChartBaseBSB *>(chart);
3179 if (Patch_Ch_BSB) {
3180 SetClipRegion(vp, get_region /*pqp->quilt_region*/);
3181 RenderRasterChartRegionGL(chart, vp, pqp->ActiveRegion);
3182 DisableClipRegion();
3183
3184 b_rendered = true;
3185 } else if (chart->GetChartType() == CHART_TYPE_MBTILES) {
3186 SetClipRegion(vp, pqp->ActiveRegion /*pqp->quilt_region*/);
3187 chart->RenderRegionViewOnGL(*m_pcontext, vp, rect_region,
3188 get_region);
3189 DisableClipRegion();
3190 }
3191
3192 } else if (chart->GetChartFamily() == CHART_FAMILY_VECTOR) {
3193 if (chart->GetChartType() == CHART_TYPE_CM93COMP) {
3194 RenderNoDTA(vp, get_region);
3195 chart->RenderRegionViewOnGL(*m_pcontext, vp, rect_region,
3196 get_region);
3197 } else {
3198 s57chart *Chs57 = dynamic_cast<s57chart *>(chart);
3199 if (Chs57) {
3200 if (Chs57->m_RAZBuilt) {
3201 RenderNoDTA(vp, get_region);
3202 Chs57->RenderRegionViewOnGLNoText(*m_pcontext, vp,
3203 rect_region, get_region);
3204 DisableClipRegion();
3205 } else {
3206 // The SENC is quesed for building, so..
3207 // Show GSHHS with compatible color scheme in the meantime.
3208 ocpnDC gldc(*this);
3209 const LLRegion &oregion = get_region;
3210 LLBBox box = oregion.GetBox();
3211
3212 wxPoint p1 =
3213 vp.GetPixFromLL(box.GetMaxLat(), box.GetMinLon());
3214 wxPoint p2 =
3215 vp.GetPixFromLL(box.GetMaxLat(), box.GetMaxLon());
3216 wxPoint p3 =
3217 vp.GetPixFromLL(box.GetMinLat(), box.GetMaxLon());
3218 wxPoint p4 =
3219 vp.GetPixFromLL(box.GetMinLat(), box.GetMinLon());
3220
3221 wxRect srect(p1.x, p1.y, p3.x - p1.x, p4.y - p2.y);
3222
3223 bool world = false;
3224 ViewPort cvp = ClippedViewport(vp, get_region);
3225 if (m_pParentCanvas->GetWorldBackgroundChart()) {
3226 SetClipRegion(cvp, get_region);
3227 m_pParentCanvas->GetWorldBackgroundChart()->SetColorsDirect(
3228 GetGlobalColor("LANDA"), GetGlobalColor("DEPMS"));
3229 RenderWorldChart(gldc, cvp, srect, world);
3230 m_pParentCanvas->GetWorldBackgroundChart()->SetColorScheme(
3231 global_color_scheme);
3232 DisableClipRegion();
3233 }
3234 }
3235 } else {
3236 ChartPlugInWrapper *ChPI =
3237 dynamic_cast<ChartPlugInWrapper *>(chart);
3238 if (ChPI) {
3239 SetClipRegion(vp, get_region);
3240 RenderNoDTA(vp, get_region);
3241 ChPI->RenderRegionViewOnGLNoText(*m_pcontext, vp, rect_region,
3242 get_region);
3243 DisableClipRegion();
3244
3245 } else {
3246 SetClipRegion(vp, get_region);
3247 RenderNoDTA(vp, get_region);
3248 chart->RenderRegionViewOnGL(*m_pcontext, vp, rect_region,
3249 get_region);
3250 DisableClipRegion();
3251 }
3252 }
3253 }
3254 }
3255 }
3256 }
3257
3258 if (b_rendered) {
3259 // LLRegion get_region = pqp->ActiveRegion;
3260 // get_region.Intersect( Region ); not technically
3261 // required?
3262 // rendered_region.Union(get_region);
3263 }
3264 }
3265
3266 chart = m_pParentCanvas->m_pQuilt->GetNextChart();
3267 }
3268
3269 // Render any Overlay patches for s57 charts(cells)
3270 if (m_pParentCanvas->m_pQuilt->HasOverlays()) {
3271 ChartBase *pch = m_pParentCanvas->m_pQuilt->GetFirstChart();
3272 while (pch) {
3273 QuiltPatch *pqp = m_pParentCanvas->m_pQuilt->GetCurrentPatch();
3274 if (pqp->b_Valid && pqp->b_overlay &&
3275 pch->GetChartFamily() == CHART_FAMILY_VECTOR) {
3276 LLRegion get_region = pqp->ActiveRegion;
3277
3278 get_region.Intersect(region);
3279 if (!get_region.Empty()) {
3280 s57chart *Chs57 = dynamic_cast<s57chart *>(pch);
3281 if (Chs57)
3282 Chs57->RenderOverlayRegionViewOnGL(*m_pcontext, vp, rect_region,
3283 get_region);
3284 else {
3285 ChartPlugInWrapper *ChPI = dynamic_cast<ChartPlugInWrapper *>(pch);
3286 if (ChPI) {
3287 ChPI->RenderRegionViewOnGL(*m_pcontext, vp, rect_region,
3288 get_region);
3289 }
3290 }
3291 }
3292 }
3293
3294 pch = m_pParentCanvas->m_pQuilt->GetNextChart();
3295 }
3296 }
3297
3298 // Hilite rollover of standard chart key
3299 ViewPort vph = m_pParentCanvas->GetVP();
3300 for (auto &index : m_pParentCanvas->m_pQuilt->GetHiLiteIndexArray()) {
3301 const ChartTableEntry &cte = ChartData->GetChartTableEntry(index);
3302 LLRegion hiregion =
3303 m_pParentCanvas->m_pQuilt->GetChartQuiltRegion(cte, vph);
3304
3305 if (!hiregion.Empty()) {
3306 glEnable(GL_BLEND);
3307
3308 double hitrans;
3309 switch (global_color_scheme) {
3310 case GLOBAL_COLOR_SCHEME_DAY:
3311 hitrans = .4;
3312 break;
3313 case GLOBAL_COLOR_SCHEME_DUSK:
3314 hitrans = .2;
3315 break;
3316 case GLOBAL_COLOR_SCHEME_NIGHT:
3317 hitrans = .1;
3318 break;
3319 default:
3320 hitrans = .4;
3321 break;
3322 }
3323
3324#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
3325
3326 glColor4f((float).8, (float).4, (float).4, (float)hitrans);
3327#else
3328 s_regionColor = wxColor(204, 102, 102, hitrans * 256);
3329#endif
3330
3331 DrawRegion(vp, hiregion);
3332
3333 glDisable(GL_BLEND);
3334 }
3335 }
3336
3337#if 0
3338 LLRegion hiregion = m_pParentCanvas->m_pQuilt->GetHiliteRegion();
3339
3340 if (!hiregion.Empty()) {
3341 glEnable(GL_BLEND);
3342
3343 double hitrans;
3344 switch (global_color_scheme) {
3345 case GLOBAL_COLOR_SCHEME_DAY:
3346 hitrans = .4;
3347 break;
3348 case GLOBAL_COLOR_SCHEME_DUSK:
3349 hitrans = .2;
3350 break;
3351 case GLOBAL_COLOR_SCHEME_NIGHT:
3352 hitrans = .1;
3353 break;
3354 default:
3355 hitrans = .4;
3356 break;
3357 }
3358
3359//#ifndef USE_ANDROID_GLES2
3360#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
3361
3362 glColor4f((float).8, (float).4, (float).4, (float)hitrans);
3363#else
3364 s_regionColor = wxColor(204, 102, 102, hitrans * 256);
3365#endif
3366
3367 DrawRegion(vp, hiregion);
3368
3369 glDisable(GL_BLEND);
3370 }
3371#endif
3372
3373 m_pParentCanvas->m_pQuilt->SetRenderedVP(vp);
3374}
3375
3376void glChartCanvas::RenderQuiltViewGLText(ViewPort &vp,
3377 const OCPNRegion &rect_region) {
3378 if (!m_pParentCanvas->m_pQuilt->GetnCharts() ||
3379 m_pParentCanvas->m_pQuilt->IsBusy())
3380 return;
3381
3382 // render the quilt
3383 ChartBase *chart = m_pParentCanvas->m_pQuilt->GetLargestScaleChart();
3384
3385 LLRegion region = vp.GetLLRegion(rect_region);
3386
3387 LLRegion rendered_region;
3388 while (chart) {
3389 QuiltPatch *pqp = m_pParentCanvas->m_pQuilt->GetCurrentPatch();
3390 if (pqp->b_Valid) {
3391 LLRegion get_region = pqp->ActiveRegion;
3392
3393 if (!pqp->b_overlay) {
3394 if (chart->GetChartFamily() == CHART_FAMILY_VECTOR) {
3395 s57chart *Chs57 = dynamic_cast<s57chart *>(chart);
3396 if (Chs57) {
3397 Chs57->RenderViewOnGLTextOnly(*m_pcontext, vp);
3398 } else {
3399 ChartPlugInWrapper *ChPI =
3400 dynamic_cast<ChartPlugInWrapper *>(chart);
3401 if (ChPI) {
3402 ChPI->RenderRegionViewOnGLTextOnly(*m_pcontext, vp, rect_region);
3403 }
3404 }
3405 }
3406 }
3407 }
3408
3409 chart = m_pParentCanvas->m_pQuilt->GetNextSmallerScaleChart();
3410 }
3411
3412 /*
3413 // Render any Overlay patches for s57 charts(cells)
3414 if( m_pParentCanvas->m_pQuilt->HasOverlays() ) {
3415 ChartBase *pch = m_pParentCanvas->m_pQuilt->GetFirstChart();
3416 while( pch ) {
3417 QuiltPatch *pqp =
3418 m_pParentCanvas->m_pQuilt->GetCurrentPatch(); if( pqp->b_Valid &&
3419 pqp->b_overlay && pch->GetChartFamily() == CHART_FAMILY_VECTOR ) { LLRegion
3420 get_region = pqp->ActiveRegion;
3421
3422 get_region.Intersect( region );
3423 if( !get_region.Empty() ) {
3424 s57chart *Chs57 = dynamic_cast<s57chart*>( pch );
3425 if( Chs57 )
3426 Chs57->RenderOverlayRegionViewOnGL( *m_pcontext,
3427 vp, rect_region, get_region );
3428 }
3429 }
3430
3431 pch = m_pParentCanvas->m_pQuilt->GetNextChart();
3432 }
3433 }
3434 */
3435}
3436
3437void glChartCanvas::RenderCharts(ocpnDC &dc, const OCPNRegion &rect_region) {
3438 ViewPort &vp = m_pParentCanvas->VPoint;
3439
3440 // Only for cm93 (not quilted), SetVPParms can change the valid region of the
3441 // chart we need to know this before rendering the chart so we can compute the
3442 // background region and nodta regions correctly. I would prefer to just
3443 // perform this here (or in SetViewPoint) for all vector charts instead of in
3444 // their render routine, but how to handle quilted cases?
3445 if (!vp.b_quilt &&
3446 m_pParentCanvas->m_singleChart->GetChartType() == CHART_TYPE_CM93COMP)
3447 static_cast<cm93compchart *>(m_pParentCanvas->m_singleChart)
3448 ->SetVPParms(vp);
3449
3450 LLRegion chart_region;
3451 if (!vp.b_quilt &&
3452 (m_pParentCanvas->m_singleChart->GetChartType() == CHART_TYPE_PLUGIN)) {
3453 if (m_pParentCanvas->m_singleChart->GetChartFamily() ==
3454 CHART_FAMILY_RASTER) {
3455 // We do this the hard way, since PlugIn Raster charts do not understand
3456 // LLRegion yet...
3457 double ll[8];
3458 ChartPlugInWrapper *cpw =
3459 dynamic_cast<ChartPlugInWrapper *>(m_pParentCanvas->m_singleChart);
3460 if (!cpw) return;
3461
3462 cpw->chartpix_to_latlong(0, 0, ll + 0, ll + 1);
3463 cpw->chartpix_to_latlong(0, cpw->GetSize_Y(), ll + 2, ll + 3);
3464 cpw->chartpix_to_latlong(cpw->GetSize_X(), cpw->GetSize_Y(), ll + 4,
3465 ll + 5);
3466 cpw->chartpix_to_latlong(cpw->GetSize_X(), 0, ll + 6, ll + 7);
3467
3468 // for now don't allow raster charts to cross both 0 meridian and IDL
3469 // (complicated to deal with)
3470 for (int i = 1; i < 6; i += 2)
3471 if (fabs(ll[i] - ll[i + 2]) > 180) {
3472 // we detect crossing idl here, make all longitudes positive
3473 for (int i = 1; i < 8; i += 2)
3474 if (ll[i] < 0) ll[i] += 360;
3475 break;
3476 }
3477
3478 chart_region = LLRegion(4, ll);
3479 } else {
3480 Extent ext;
3481 m_pParentCanvas->m_singleChart->GetChartExtent(&ext);
3482
3483 double ll[8] = {ext.SLAT, ext.WLON, ext.SLAT, ext.ELON,
3484 ext.NLAT, ext.ELON, ext.NLAT, ext.WLON};
3485 chart_region = LLRegion(4, ll);
3486 }
3487 } else
3488 chart_region = vp.b_quilt
3489 ? m_pParentCanvas->m_pQuilt->GetFullQuiltRegion()
3490 : m_pParentCanvas->m_singleChart->GetValidRegion();
3491
3492 bool world_view = false;
3493 for (OCPNRegionIterator upd(rect_region); upd.HaveRects(); upd.NextRect()) {
3494 wxRect rect = upd.GetRect();
3495 LLRegion background_region = vp.GetLLRegion(rect);
3496 // Remove the valid chart area to find the region NOT covered by the
3497 // charts
3498 background_region.Subtract(chart_region);
3499
3500 if (!background_region.Empty()) {
3501 ViewPort cvp = ClippedViewport(vp, background_region);
3502 SetClipRect(cvp, rect, false);
3503 RenderWorldChart(dc, cvp, rect, world_view);
3504 DisableClipRegion();
3505 }
3506 }
3507
3508 if (vp.b_quilt) {
3509 RenderQuiltViewGL(vp, rect_region);
3510 int yyp = 4;
3511 } else {
3512 LLRegion region = vp.GetLLRegion(rect_region);
3513 if (m_pParentCanvas->m_singleChart->GetChartFamily() ==
3514 CHART_FAMILY_RASTER) {
3515 if (m_pParentCanvas->m_singleChart->GetChartType() == CHART_TYPE_MBTILES)
3516 m_pParentCanvas->m_singleChart->RenderRegionViewOnGL(
3517 *m_pcontext, vp, rect_region, region);
3518 else
3519 RenderRasterChartRegionGL(m_pParentCanvas->m_singleChart, vp, region);
3520 } else if (m_pParentCanvas->m_singleChart->GetChartFamily() ==
3521 CHART_FAMILY_VECTOR) {
3522 chart_region.Intersect(region);
3523 RenderNoDTA(vp, chart_region);
3524 m_pParentCanvas->m_singleChart->RenderRegionViewOnGL(*m_pcontext, vp,
3525 rect_region, region);
3526 }
3527 }
3528 glUseProgram(0);
3529}
3530
3531void glChartCanvas::RenderNoDTA(ViewPort &vp, const LLRegion &region,
3532 int transparency) {
3533 wxColour color = GetGlobalColor("NODTA");
3534#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
3535 if (color.IsOk())
3536 glColor4ub(color.Red(), color.Green(), color.Blue(), transparency);
3537 else
3538 glColor4ub(163, 180, 183, transparency);
3539
3540 glEnable(GL_BLEND);
3541 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
3542
3543#else
3544 // Store the color for tesselator callback pickup.
3545 s_regionColor = color;
3546#endif
3547
3548 DrawRegion(vp, region);
3549}
3550
3551/* render world chart, but only in this rectangle */
3552void glChartCanvas::RenderWorldChart(ocpnDC &dc, ViewPort &vp, wxRect &rect,
3553 bool &world_view) {
3554 // set gl color to water
3555 wxColour water = m_pParentCanvas->pWorldBackgroundChart->water;
3556
3557 glEnable(GL_SCISSOR_TEST);
3558 glScissor(rect.x, vp.pix_height - rect.height - rect.y, rect.width,
3559 rect.height);
3560
3561 // clear background
3562 if (!world_view) {
3563 if (!world_view) {
3564 int x1 = rect.x, y1 = rect.y, x2 = x1 + rect.width, y2 = y1 + rect.height;
3565#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
3566
3567 GLShaderProgram *shader = pcolor_tri_shader_program[GetCanvasIndex()];
3568 shader->Bind();
3569
3570 float colorv[4];
3571 colorv[0] = water.Red() / float(256);
3572 colorv[1] = water.Green() / float(256);
3573 colorv[2] = water.Blue() / float(256);
3574 colorv[3] = 1.0;
3575 shader->SetUniform4fv("color", colorv);
3576
3577 float pf[8];
3578 pf[0] = x2;
3579 pf[1] = y1;
3580 pf[2] = x2;
3581 pf[3] = y2;
3582 pf[4] = x1;
3583 pf[5] = y1;
3584 pf[6] = x1;
3585 pf[7] = y2;
3586 shader->SetAttributePointerf("position", pf);
3587
3588 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
3589
3590 shader->UnBind();
3591
3592#else
3593#endif
3594 }
3595 }
3596
3597 // m_pParentCanvas->pWorldBackgroundChart->RenderViewOnDC(dc, vp);
3598 gShapeBasemap.RenderViewOnDC(dc, vp);
3599
3600 glDisable(GL_SCISSOR_TEST);
3601}
3602
3603/* these are the overlay objects which move with the charts and
3604 are not frequently updated (not ships etc..)
3605
3606 many overlay objects are fixed to a geographical location or
3607 grounded as opposed to the floating overlay objects. */
3608void glChartCanvas::DrawGroundedOverlayObjects(ocpnDC &dc, ViewPort &vp) {
3609 m_pParentCanvas->RenderAllChartOutlines(dc, vp);
3610
3611 DrawStaticRoutesTracksAndWaypoints(vp);
3612
3613 DisableClipRegion();
3614}
3615
3616void glChartCanvas::DrawGLTidesInBBox(ocpnDC &dc, LLBBox &BBox) {
3617 // At small scale, we render the Tide icon as a texture for best performance
3618 if (m_pParentCanvas->GetVP().chart_scale > 500000) {
3619 // Prepare the texture if necessary
3620
3621 if (!m_tideTex) {
3622 wxBitmap bmp = m_pParentCanvas->GetTideBitmap();
3623 if (!bmp.Ok()) return;
3624
3625 wxImage image = bmp.ConvertToImage();
3626 int w = image.GetWidth(), h = image.GetHeight();
3627
3628 int tex_w, tex_h;
3629 if (g_texture_rectangle_format == GL_TEXTURE_2D)
3630 tex_w = w, tex_h = h;
3631 else
3632 tex_w = NextPow2(w), tex_h = NextPow2(h);
3633
3634 m_tideTexWidth = tex_w;
3635 m_tideTexHeight = tex_h;
3636
3637 unsigned char *d = image.GetData();
3638 unsigned char *a = image.GetAlpha();
3639
3640 unsigned char mr, mg, mb;
3641 if (!a) image.GetOrFindMaskColour(&mr, &mg, &mb);
3642
3643 unsigned char *e = new unsigned char[4 * w * h];
3644 if (e && d) {
3645 for (int y = 0; y < h; y++)
3646 for (int x = 0; x < w; x++) {
3647 unsigned char r, g, b;
3648 int off = (y * w + x);
3649 r = d[off * 3 + 0];
3650 g = d[off * 3 + 1];
3651 b = d[off * 3 + 2];
3652
3653 e[off * 4 + 0] = r;
3654 e[off * 4 + 1] = g;
3655 e[off * 4 + 2] = b;
3656
3657 e[off * 4 + 3] =
3658 a ? a[off] : ((r == mr) && (g == mg) && (b == mb) ? 0 : 255);
3659 }
3660 }
3661
3662 glGenTextures(1, &m_tideTex);
3663
3664 glBindTexture(GL_TEXTURE_2D, m_tideTex);
3665 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3666 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3667
3668 if (g_texture_rectangle_format == GL_TEXTURE_2D)
3669 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA,
3670 GL_UNSIGNED_BYTE, e);
3671 else {
3672 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex_w, tex_h, 0, GL_RGBA,
3673 GL_UNSIGNED_BYTE, 0);
3674 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE,
3675 e);
3676 }
3677
3678 delete[] e;
3679 }
3680
3681 // Texture is ready
3682
3683 glBindTexture(GL_TEXTURE_2D, m_tideTex);
3684 glEnable(GL_TEXTURE_2D);
3685 glEnable(GL_BLEND);
3686
3687#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
3688#else
3689 for (int i = 1; i < ptcmgr->Get_max_IDX() + 1; i++) {
3690 const IDX_entry *pIDX = ptcmgr->GetIDX_entry(i);
3691
3692 char type = pIDX->IDX_type; // Entry "TCtcIUu" identifier
3693 if ((type == 't') || (type == 'T')) // only Tides
3694 {
3695 double lon = pIDX->IDX_lon;
3696 double lat = pIDX->IDX_lat;
3697
3698 if (BBox.Contains(lat, lon)) {
3699 wxPoint r;
3700 m_pParentCanvas->GetCanvasPointPix(lat, lon, &r);
3701
3702 float xp = r.x;
3703 float yp = r.y;
3704
3705 double scale = 1.0;
3706#ifdef __ANDROID__
3707 scale *= getAndroidDisplayDensity();
3708#endif
3709 double width2 = scale * m_tideTexWidth / 2;
3710 double height2 = scale * m_tideTexHeight / 2;
3711
3712 float coords[8];
3713 float uv[8];
3714
3715 // normal uv
3716 uv[0] = 0;
3717 uv[1] = 0;
3718 uv[2] = 0;
3719 uv[3] = 1;
3720 uv[4] = 1;
3721 uv[5] = 1;
3722 uv[6] = 1;
3723 uv[7] = 0;
3724
3725 coords[0] = xp - width2;
3726 coords[1] = yp - height2;
3727 coords[2] = xp - width2;
3728 coords[3] = yp + height2;
3729 coords[4] = xp + width2;
3730 coords[5] = yp + height2;
3731 coords[6] = xp + width2;
3732 coords[7] = yp - height2;
3733
3734 RenderTextures(dc, coords, uv, 4, m_pParentCanvas->GetpVP());
3735 }
3736 } // type 'T"
3737 } // loop
3738
3739#endif
3740
3741 glDisable(GL_TEXTURE_2D);
3742 glDisable(GL_BLEND);
3743 glBindTexture(GL_TEXTURE_2D, 0);
3744 } else
3745 m_pParentCanvas->DrawAllTidesInBBox(dc, BBox);
3746}
3747
3748void glChartCanvas::DrawGLCurrentsInBBox(ocpnDC &dc, LLBBox &BBox) {
3749 m_pParentCanvas->DrawAllCurrentsInBBox(dc, BBox);
3750}
3751
3752void glChartCanvas::SetColorScheme(ColorScheme cs) {
3753 if (!m_bsetup) return;
3754
3755 glDeleteTextures(1, &m_tideTex);
3756 glDeleteTextures(1, &m_currentTex);
3757 m_tideTex = 0;
3758 m_currentTex = 0;
3759 ownship_color = -1;
3760}
3761
3762void glChartCanvas::RenderGLAlertMessage() {
3763 if (!m_pParentCanvas->GetAlertString().IsEmpty()) {
3764 wxString msg = m_pParentCanvas->GetAlertString();
3765
3766 wxFont *pfont = GetOCPNScaledFont(_("Dialog"));
3767 m_gldc.SetFont(*pfont);
3768
3769 int w, h;
3770 wxScreenDC sdc;
3771 sdc.GetTextExtent(msg, &w, &h, NULL, NULL, pfont);
3772
3773 h += 2;
3774 w += 4;
3775 int yp =
3776 m_pParentCanvas->VPoint.pix_height - GetChartbarHeight() - h - (h / 4);
3777
3778 wxRect sbr = m_pParentCanvas->GetScaleBarRect();
3779 int xp = sbr.x + sbr.width + 5;
3780
3781 wxPen ppPen1(GetGlobalColor("UBLCK"), 1, wxPENSTYLE_SOLID);
3782 m_gldc.SetPen(ppPen1);
3783 m_gldc.SetBrush(wxBrush(GetGlobalColor("YELO1")));
3784
3785 m_gldc.DrawRectangle(xp, yp, w, h);
3786
3787 m_gldc.DrawText(msg, xp, yp);
3788 }
3789}
3790
3791unsigned long quiltHash;
3792int refChartIndex;
3793extern wxLongLong s_t0;
3794
3795int n_render;
3796void glChartCanvas::Render() {
3797 if (!m_bsetup || !m_pParentCanvas->m_pQuilt ||
3798 (m_pParentCanvas->VPoint.b_quilt && !m_pParentCanvas->m_pQuilt) ||
3799 (!m_pParentCanvas->VPoint.b_quilt && !m_pParentCanvas->m_singleChart)) {
3800#ifdef __WXGTK__ // for some reason in gtk, a swap is needed here to get an
3801 // initial screen update
3802 SwapBuffers();
3803#endif
3804 if (!g_PrintingInProgress) return;
3805 }
3806
3807 if (!g_true_zoom && m_binPinch) return;
3808
3809 // if (m_binPinch) printf(" %ld Render Start\n", m_glstopwatch.Time());
3810 long render_start_time = m_glstopwatch.Time();
3811
3812#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
3813 loadShaders(GetCanvasIndex());
3814 configureShaders(m_pParentCanvas->VPoint);
3815#endif
3816
3817#ifdef USE_ANDROID_GLES2
3818
3819 OCPNStopWatch sw;
3820
3821 if (m_binPinch) return;
3822
3823 // qDebug() << "Render" << m_pParentCanvas->m_canvasIndex << GetPosition().x
3824 // << GetSize().x << m_pParentCanvas->GetPosition().x << m_pcontext;
3825
3826 // if(m_pParentCanvas->m_canvasIndex == 0) return;
3827
3828 // Do any setup required...
3829
3830 bool recompose = false;
3831 if (m_pParentCanvas->VPoint.b_quilt && m_pParentCanvas->m_pQuilt &&
3832 !m_pParentCanvas->m_pQuilt->IsComposed()) {
3833 if (m_pParentCanvas->VPoint.IsValid()) {
3834 m_pParentCanvas->m_pQuilt->Compose(m_pParentCanvas->VPoint);
3835 m_pParentCanvas->UpdateCanvasControlBar();
3836 recompose = true;
3837 } else
3838 return;
3839 }
3840
3841 // Check to see if the Compose() call forced a SENC build.
3842 // If so, zoom the canvas just slightly to force a deferred redraw of the
3843 // full screen.
3844 if (sw.GetTime() > 2000) { // long enough to detect SENC build.
3845 m_pParentCanvas->ZoomCanvas(1.0001, false);
3846 }
3847
3848 // qDebug() << "RenderTime1" << sw.GetTime();
3849
3850 s_tess_vertex_idx = 0;
3851 quiltHash = m_pParentCanvas->m_pQuilt->GetXStackHash();
3852 refChartIndex = m_pParentCanvas->m_pQuilt->GetRefChartdbIndex();
3853
3854#endif
3855
3856#ifdef __WXOSX__
3857 // Support scaled HDPI displays.
3858 m_displayScale = GetContentScaleFactor();
3859#endif
3860 m_pParentCanvas->VPoint.SetPixelScale(m_displayScale);
3861
3862 m_last_render_time = wxDateTime::Now().GetTicks();
3863
3864 // we don't care about jobs that are now off screen
3865 // clear out and it will be repopulated during render
3866 if (g_GLOptions.m_bTextureCompression &&
3867 !g_GLOptions.m_bTextureCompressionCaching)
3868 g_glTextureManager->ClearJobList();
3869
3870 ocpnDC gldc(*this);
3871
3872 int gl_width, gl_height;
3873 gl_width = m_pParentCanvas->VPoint.pix_width;
3874 gl_height = m_pParentCanvas->VPoint.pix_height;
3875
3876 // Take a copy for use later by DC
3877 m_glcanvas_width = gl_width;
3878 m_glcanvas_height = gl_height;
3879
3880 // Avoid some harmonic difficulties with odd-size glCanvas
3881 bool b_odd = false;
3882 if (gl_height & 1) {
3883 gl_height -= 1;
3884 ViewPort *vp = m_pParentCanvas->GetpVP();
3885 vp->pix_height = gl_height;
3886 b_odd = true;
3887 }
3888
3889 if (gl_width & 1) {
3890 gl_width -= 1;
3891 ViewPort *vp = m_pParentCanvas->GetpVP();
3892 vp->pix_width = gl_width;
3893 b_odd = true;
3894 }
3895
3896 // Set the shader viewport transform matrix
3897 // Using the adjusted dimensions
3898 if (b_odd) {
3899 ViewPort *vp = m_pParentCanvas->GetpVP();
3900 mat4x4 m;
3901 mat4x4_identity(m);
3902 mat4x4_scale_aniso((float(*)[4])vp->vp_matrix_transform, m,
3903 2.0 / (float)vp->pix_width, -2.0 / (float)vp->pix_height,
3904 1.0);
3905 mat4x4_translate_in_place((float(*)[4])vp->vp_matrix_transform,
3906 -vp->pix_width / 2, -vp->pix_height / 2, 0);
3907 }
3908
3909 // @todo: If the intention was to work with the same ViewPort object, use a
3910 // reference instead. Making a copy of VPoint here means that any changes to
3911 // VPoint will not affect m_pParentCanvas->VPoint. It's not clear if this is
3912 // the intended behavior.
3913 ViewPort VPoint = m_pParentCanvas->VPoint;
3914
3915 OCPNRegion screen_region(wxRect(0, 0, gl_width, gl_height));
3916 glViewport(0, 0, (GLint)gl_width, (GLint)gl_height);
3917
3918// #ifndef USE_ANDROID_GLES2
3919#if !defined(USE_ANDROID_GLES2)
3920 glMatrixMode(GL_PROJECTION);
3921 glLoadIdentity();
3922
3923 glOrtho(0, (GLint)gl_width, (GLint)gl_height, 0, -1, 1);
3924 glMatrixMode(GL_MODELVIEW);
3925 glLoadIdentity();
3926#endif
3927
3928 if (s_b_useStencil) {
3929 glEnable(GL_STENCIL_TEST);
3930 glStencilMask(0xff);
3931 glClear(GL_STENCIL_BUFFER_BIT);
3932 glDisable(GL_STENCIL_TEST);
3933 }
3934
3935 // set opengl settings that don't normally change
3936 // this should be able to go in SetupOpenGL, but it's
3937 // safer here incase a plugin mangles these
3938 if (g_GLOptions.m_GLLineSmoothing) glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
3939 if (g_GLOptions.m_GLPolygonSmoothing)
3940 glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
3941 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
3942
3943 // Delete any textures known to the GPU that
3944 // belong to charts which will not be used in this render
3945 // This is done chart-by-chart...later we will scrub for unused textures
3946 // that belong to charts which ARE used in this render, if we need to....
3947
3948 g_glTextureManager->TextureCrunch(0.8);
3949
3950 // If we plan to post process the display, don't use accelerated panning
3951 double scale_factor = VPoint.ref_scale / VPoint.chart_scale;
3952
3953 bool bpost_hilite = !m_pParentCanvas->m_pQuilt->GetHiliteRegion().Empty();
3954 bool useFBO = false;
3955 int sx = gl_width;
3956 int sy = gl_height;
3957
3958 // Try to use the framebuffer object's cache of the last frame
3959 // to accelerate drawing this frame (if overlapping)
3960 if (m_b_BuiltFBO && !bpost_hilite
3961 //&& VPoint.tilt == 0 // disabling fbo in tilt mode gives better quality
3962 // but slower
3963 ) {
3964 // Is this viewpoint the same as the previously painted one?
3965 bool b_newview = true;
3966 bool b_full = false;
3967
3968 // If the view is the same we do no updates,
3969 // Just render cached texture to the framebuffer
3970 if (m_cache_vp.view_scale_ppm == VPoint.view_scale_ppm &&
3971 m_cache_vp.rotation == VPoint.rotation &&
3972 m_cache_vp.clat == VPoint.clat && m_cache_vp.clon == VPoint.clon &&
3973 m_cache_vp.IsValid() && m_cache_vp.pix_height == VPoint.pix_height &&
3974 m_cache_current_ch == m_pParentCanvas->m_singleChart) {
3975 b_newview = false;
3976 }
3977
3978#ifdef USE_ANDROID_GLES2
3979 if (recompose) b_newview = true;
3980
3981 if (m_bforcefull) {
3982 b_newview = true;
3983 b_full = true;
3984 }
3985
3986 // If no charts are to be rendered, we need to refresh the entire display
3987 // This fixes a problem with routes/tracks/marks rendering on pans at very
3988 // small scale. It is a workaround, so finding root cause should be
3989 // considered a TODO
3990
3991 if (VPoint.b_quilt) {
3992 ChartBase *chart = m_pParentCanvas->m_pQuilt->GetFirstChart();
3993 if (!chart) b_full = true;
3994 }
3995
3996#endif
3997
3998 if (b_newview) {
3999 float dx = 0;
4000 float dy = 0;
4001
4002 bool accelerated_pan = false;
4003 // if (g_in_inertia)
4004 // printf("--- accpan condition %d %d\n",
4005 // g_GLOptions.m_bUseAcceleratedPanning,
4006 // m_cache_vp.IsValid());
4007 // else
4008 // printf("||| accpan condition %d %d\n",
4009 // g_GLOptions.m_bUseAcceleratedPanning,
4010 // m_cache_vp.IsValid());
4011
4012 if (g_GLOptions.m_bUseAcceleratedPanning && m_cache_vp.IsValid() &&
4013 (VPoint.m_projection_type == PROJECTION_MERCATOR ||
4014 VPoint.m_projection_type == PROJECTION_EQUIRECTANGULAR) &&
4015 m_cache_vp.pix_height == VPoint.pix_height) {
4016 wxPoint2DDouble c_old =
4017 VPoint.GetDoublePixFromLL(VPoint.clat, VPoint.clon) *
4018 m_displayScale;
4019 wxPoint2DDouble c_new =
4020 m_cache_vp.GetDoublePixFromLL(VPoint.clat, VPoint.clon) *
4021 m_displayScale;
4022
4023 dy = wxRound(c_new.m_y - c_old.m_y);
4024 dx = wxRound(c_new.m_x - c_old.m_x);
4025
4026 // The math below using the previous frame's texture does not really
4027 // work for sub-pixel pans.
4028 // TODO is to rethink this.
4029 // Meanwhile, require the accelerated pans to be whole pixel multiples
4030 // only. This is not as bad as it sounds. Keyboard and mouse pans are
4031 // whole_pixel moves. However, autofollow at large scale is certainly
4032 // not.
4033
4034 double deltax = c_new.m_x - c_old.m_x;
4035 double deltay = c_new.m_y - c_old.m_y;
4036
4037 bool b_whole_pixel = true;
4038 if ((fabs(deltax - dx) > 1e-2) || (fabs(deltay - dy) > 1e-2))
4039 b_whole_pixel = false;
4040
4041 accelerated_pan = b_whole_pixel && abs(dx) < m_cache_tex_x &&
4042 abs(dy) < m_cache_tex_y &&
4043 (abs(dx) > 0 || (abs(dy) > 0));
4044
4045 // if (g_in_inertia && !accelerated_pan)
4046 // printf("--- accpan %d %d %g %g\n", accelerated_pan,
4047 // b_whole_pixel, dx, dy);
4048 }
4049
4050 // FBO swapping has trouble with Retina display on MacOS Monterey.
4051 // So, disable accelerated pan ops on this case.
4052 if (m_displayScale > 1) accelerated_pan = false;
4053
4054 // FIXME (dave) There are some display artifact troubles using accPan on
4055 // rotation.
4056 // Especially seen on sparse RNC rendering
4057 if (fabs(VPoint.rotation) > 0) accelerated_pan = false;
4058
4059 // do we allow accelerated panning? can we perform it here?
4060#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
4061#else // GLES2
4062 // enable rendering to texture in framebuffer object
4063 glBindFramebuffer(GL_FRAMEBUFFER, m_fb0);
4064
4065 if (VPoint.chart_scale < 5000) b_full = true;
4066
4067 if (VPoint.chart_scale > 5e7) b_full = true;
4068
4069 if (b_full) accelerated_pan = false;
4070
4071 if (accelerated_pan) {
4072 if ((dx != 0) || (dy != 0)) { // Anything to do?
4073
4074 // calculate the new regions to render
4075 // add extra pixels to avoid coordindate rounding issues at large
4076 // scale
4077 OCPNRegion update_region;
4078
4079 int fluff = 2;
4080
4081 // Avoid rendering artifacts caused by Multi Sampling (MSAA)
4082 if (VPoint.chart_scale < 10000) fluff = 8;
4083
4084 if (dy > 0 && dy < gl_height)
4085 update_region.Union(
4086 wxRect(0, gl_height - (dy + fluff), gl_width, dy + fluff));
4087 else if (dy < 0)
4088 update_region.Union(wxRect(0, 0, gl_width, -dy + fluff));
4089
4090 if (dx > 0 && dx < gl_width)
4091 update_region.Union(
4092 wxRect(gl_width - (dx + fluff), 0, dx + fluff, gl_height));
4093 else if (dx < 0)
4094 update_region.Union(wxRect(0, 0, -dx + fluff, gl_height));
4095
4096 m_cache_page = !m_cache_page; /* page flip */
4097
4098 // Bind the destination (target frame) texture to the frame buffer
4099 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
4100 GL_TEXTURE_2D, m_cache_tex[m_cache_page], 0);
4101
4102 // Before rendering anything, clear the color buffers
4103 // wxColour color = GetGlobalColor("NODTA");
4104 // glClearColor(color.Red() / 256., color.Green() / 256.,
4105 // color.Blue() / 256., 1.0);
4106 // glClear(GL_COLOR_BUFFER_BIT);
4107
4108 // First render the new content into the update region
4109 // if (g_in_inertia)
4110 // printf("--- R2a %g\n",
4111 // (wxGetLocalTimeMillis() - s_t0).ToDouble());
4112 RenderCharts(m_gldc, update_region);
4113 // if (g_in_inertia)
4114 // printf("--- R2b %g\n",
4115 // (wxGetLocalTimeMillis() - s_t0).ToDouble());
4116 glDisable(g_texture_rectangle_format);
4117 glUseProgram(0);
4118
4119 // Next, render the cached texture as quad to FBO(m_blit_tex) with
4120 // offsets
4121 glBindTexture(GL_TEXTURE_2D, m_cache_tex[!m_cache_page]);
4122 glEnable(GL_TEXTURE_2D);
4123
4124 // Blit the existing content onto the alternate FBO, at the correct
4125 // location
4126 float x1, x2, y1, y2;
4127
4128 if (dx > 0)
4129 x1 = dx, x2 = 0;
4130 else
4131 x1 = 0, x2 = -dx;
4132
4133 if (dy > 0)
4134 y1 = dy, y2 = 0;
4135 else
4136 y1 = 0, y2 = -dy;
4137
4138 // normalize to texture coordinates range from 0 to 1
4139 float tx1, tx2, ty1, ty2;
4140
4141 float xcor = 0;
4142 float ycor = 0;
4143
4144 tx1 = 0;
4145 tx2 = sx / (float)m_cache_tex_x;
4146 ty1 = 0;
4147 ty2 = sy / (float)m_cache_tex_y;
4148
4149 float coords[8];
4150 float uv[8];
4151
4152 // normal uv
4153 uv[0] = tx1;
4154 uv[1] = ty1;
4155 uv[2] = tx2;
4156 uv[3] = ty1;
4157 uv[4] = tx2;
4158 uv[5] = ty2;
4159 uv[6] = tx1;
4160 uv[7] = ty2;
4161
4162 coords[0] = -dx;
4163 coords[1] = dy;
4164 coords[2] = -dx + sx;
4165 coords[3] = dy;
4166 coords[4] = -dx + sx;
4167 coords[5] = dy + sy;
4168 coords[6] = -dx;
4169 coords[7] = dy + sy;
4170
4171 GLShaderProgram *shader =
4172 ptexture_2D_shader_program[GetCanvasIndex()];
4173 shader->Bind();
4174
4175 // Set up the texture sampler to texture unit 0
4176 shader->SetUniform1i("uTex", 0);
4177
4178 mat4x4 m, mvp, I;
4179 mat4x4_identity(m);
4180 mat4x4_scale_aniso(mvp, m, 2.0 / (float)sx, 2.0 / (float)sy, 1.0);
4181 mat4x4_translate_in_place(mvp, -(float)sx / 2, -(float)sy / 2, 0);
4182 shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)mvp);
4183 mat4x4_identity(I);
4184 shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)I);
4185
4186 float co1[8];
4187 co1[0] = coords[0];
4188 co1[1] = coords[1];
4189 co1[2] = coords[2];
4190 co1[3] = coords[3];
4191 co1[4] = coords[6];
4192 co1[5] = coords[7];
4193 co1[6] = coords[4];
4194 co1[7] = coords[5];
4195
4196 float tco1[8];
4197 tco1[0] = uv[0];
4198 tco1[1] = uv[1];
4199 tco1[2] = uv[2];
4200 tco1[3] = uv[3];
4201 tco1[4] = uv[6];
4202 tco1[5] = uv[7];
4203 tco1[6] = uv[4];
4204 tco1[7] = uv[5];
4205
4206 shader->SetAttributePointerf("aPos", co1);
4207 shader->SetAttributePointerf("aUV", tco1);
4208
4209 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
4210
4211 // restore the shader matrix
4212 shader->SetUniformMatrix4fv("MVMatrix",
4213 (GLfloat *)VPoint.vp_matrix_transform);
4214
4215 shader->UnBind();
4216 glBindTexture(g_texture_rectangle_format, 0);
4217
4218 glDisable(g_texture_rectangle_format);
4219 glUseProgram(0);
4220 }
4221
4222 } // accelerated pan
4223
4224 else { // must redraw the entire screen
4225 // qDebug() << "Fullpage";
4226 glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0,
4227 g_texture_rectangle_format,
4228 m_cache_tex[!m_cache_page], 0);
4229
4230 m_fbo_offsetx = 0;
4231 m_fbo_offsety = 0;
4232 m_fbo_swidth = sx;
4233 m_fbo_sheight = sy;
4234
4235 // FIXME (dave) test on Android
4236 // This can be annoying on Android pinch zoom
4237
4238 // Clear the screen to NODTA color
4239 wxColour color = GetGlobalColor("NODTA");
4240 glClearColor(color.Red() / 256., color.Green() / 256.,
4241 color.Blue() / 256., 1.0);
4242 glClear(GL_COLOR_BUFFER_BIT);
4243
4244 OCPNRegion rscreen_region(VPoint.rv_rect);
4245 // if (g_in_inertia)
4246 // printf("--- R2c %g\n",
4247 // (wxGetLocalTimeMillis() - s_t0).ToDouble());
4248 RenderCharts(m_gldc, rscreen_region);
4249 // if (g_in_inertia)
4250 // printf("--- R2d %g\n",
4251 // (wxGetLocalTimeMillis() - s_t0).ToDouble());
4252
4253 m_cache_page = !m_cache_page; /* page flip */
4254
4255 } // full page render
4256
4257 // Disable Render to FBO
4258 glBindFramebuffer(GL_FRAMEBUFFER, 0);
4259
4260#endif // gles2 for accpan
4261
4262 } // newview
4263
4264 useFBO = true;
4265 }
4266
4267#ifndef __ANDROID__
4268 if (VPoint.tilt) {
4269 glMatrixMode(GL_PROJECTION);
4270 glLoadIdentity();
4271
4272 gluPerspective(2 * 180 / PI * atan2((double)gl_height, (double)gl_width),
4273 (GLfloat)gl_width / (GLfloat)gl_height, 1, gl_width);
4274
4275 glMatrixMode(GL_MODELVIEW);
4276 glLoadIdentity();
4277
4278 glScalef(1, -1, 1);
4279 glTranslatef(-gl_width / 2, -gl_height / 2, -gl_width / 2);
4280 glRotated(VPoint.tilt * 180 / PI, 1, 0, 0);
4281
4282 glGetIntegerv(GL_VIEWPORT, viewport);
4283 glGetDoublev(GL_PROJECTION_MATRIX, projmatrix);
4284 glGetDoublev(GL_MODELVIEW_MATRIX, mvmatrix);
4285 }
4286#endif
4287
4288 // if (g_in_inertia)
4289 // printf("--- R3 %g\n", (wxGetLocalTimeMillis() - s_t0).ToDouble());
4290
4291 if (useFBO) {
4292#if 0 // #ifndef USE_ANDROID_GLES2
4293 glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fb0);
4294 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
4295 glBlitFramebuffer(0, 0, sx, sy, 0, 0, sx*2, sy*2, GL_COLOR_BUFFER_BIT, GL_LINEAR);
4296
4297 glBindFramebuffer(GL_FRAMEBUFFER, 0);
4298
4299#else
4300 // Render the cached texture as quad to screen
4301 glBindTexture(g_texture_rectangle_format, m_cache_tex[m_cache_page]);
4302 glEnable(g_texture_rectangle_format);
4303
4304 float tx, ty, tx0, ty0, divx, divy;
4305
4306 // Normalize, or not?
4307 if (GL_TEXTURE_RECTANGLE_ARB == g_texture_rectangle_format) {
4308 divx = divy = 1.0f;
4309 } else {
4310 divx = m_cache_tex_x;
4311 divy = m_cache_tex_y;
4312 }
4313
4314 tx0 = m_fbo_offsetx / divx;
4315 ty0 = m_fbo_offsety / divy;
4316 tx = (m_fbo_offsetx + m_fbo_swidth) / divx;
4317 ty = (m_fbo_offsety + m_fbo_sheight) / divy;
4318
4319 float coords[8];
4320 float uv[8];
4321
4322 // normal uv
4323 uv[0] = tx0;
4324 uv[1] = ty;
4325 uv[2] = tx;
4326 uv[3] = ty;
4327 uv[4] = tx;
4328 uv[5] = ty0;
4329 uv[6] = tx0;
4330 uv[7] = ty0;
4331
4332 // pixels
4333 coords[0] = 0;
4334 coords[1] = 0;
4335 coords[2] = sx;
4336 coords[3] = 0;
4337 coords[4] = sx;
4338 coords[5] = sy;
4339 coords[6] = 0;
4340 coords[7] = sy;
4341
4342 wxColour color = GetGlobalColor("NODTA");
4343 glClearColor(color.Red() / 256., color.Green() / 256., color.Blue() / 256.,
4344 1.0);
4345 glClear(GL_COLOR_BUFFER_BIT);
4346
4347 RenderTextures(gldc, coords, uv, 4, m_pParentCanvas->GetpVP());
4348#endif
4349
4350 glDisable(g_texture_rectangle_format);
4351
4352 m_cache_vp = VPoint;
4353 m_cache_vp.Validate();
4354
4355 m_cache_current_ch = m_pParentCanvas->m_singleChart;
4356
4357 if (VPoint.b_quilt) m_pParentCanvas->m_pQuilt->SetRenderedVP(VPoint);
4358
4359 } else // useFBO
4360 {
4361 RenderCharts(m_gldc, screen_region);
4362 }
4363
4364 // if (m_binPinch)
4365 // printf(" Render Charts Done %ld\n",
4366 // m_glstopwatch.Time() - render_start_time);
4367
4368 // Done with base charts.
4369 // Now the overlays
4370 RenderS57TextOverlay(VPoint);
4371 RenderMBTilesOverlay(VPoint);
4372
4373 g_overlayCanvas = m_pParentCanvas;
4374 if (g_pi_manager) {
4375 g_pi_manager->SendViewPortToRequestingPlugIns(VPoint);
4376 g_pi_manager->RenderAllGLCanvasOverlayPlugIns(
4377 m_pcontext, VPoint, m_pParentCanvas->m_canvasIndex, OVERLAY_CHARTS);
4378 }
4379
4380 // Render static overlay objects
4381 for (OCPNRegionIterator upd(screen_region); upd.HaveRects(); upd.NextRect()) {
4382 wxRect rt = upd.GetRect();
4383 LLRegion region = VPoint.GetLLRegion(rt);
4384 ViewPort cvp = ClippedViewport(VPoint, region);
4385 DrawGroundedOverlayObjects(gldc, cvp);
4386 }
4387
4388 if (m_pParentCanvas->m_bShowTide || m_pParentCanvas->m_bShowCurrent) {
4389 LLRegion screenLLRegion = VPoint.GetLLRegion(screen_region);
4390 LLBBox screenBox = screenLLRegion.GetBox();
4391 // Enlarge the box a bit
4392 screenBox.EnLarge(screenBox.GetLonRange() * 0.05);
4393
4394 // update the tide/current select points, if necessary
4395 if (m_pParentCanvas->m_bShowTide) {
4396 m_pParentCanvas->RebuildTideSelectList(screenBox); // full screen
4397 DrawGLTidesInBBox(gldc, VPoint.GetBBox());
4398 }
4399
4400 if (m_pParentCanvas->m_bShowCurrent) {
4401 m_pParentCanvas->RebuildCurrentSelectList(screenBox);
4402 DrawGLCurrentsInBBox(gldc, VPoint.GetBBox());
4403 }
4404 }
4405
4406 // If multi-canvas, indicate which canvas has keyboard focus
4407 // by drawing a simple blue bar at the top.
4408 if (m_pParentCanvas->m_show_focus_bar &&
4409 (g_canvasConfig != 0)) { // multi-canvas?
4410 if (m_pParentCanvas == wxWindow::FindFocus()) {
4411 g_focusCanvas = m_pParentCanvas;
4412
4413 wxColour colour = GetGlobalColor("BLUE4");
4414 wxPen ppBlue(colour, 1);
4415 wxBrush ppBrush(colour);
4416 gldc.SetPen(ppBlue);
4417 gldc.SetBrush(ppBrush);
4418 int xw = m_pParentCanvas->GetClientSize().x * m_displayScale;
4419 float rect_pix = m_pParentCanvas->m_focus_indicator_pix * m_displayScale;
4420 wxPoint barPoints[4];
4421 barPoints[0].x = 0;
4422 barPoints[0].y = 0;
4423 barPoints[1].x = xw;
4424 barPoints[1].y = 0;
4425 barPoints[2].x = xw;
4426 barPoints[2].y = rect_pix;
4427 barPoints[3].x = 0;
4428 barPoints[3].y = rect_pix;
4429
4430 gldc.DrawPolygon(4, barPoints, 0, 0, 1, 0);
4431 }
4432 }
4433
4434 DrawDynamicRoutesTracksAndWaypoints(VPoint);
4435
4436 // Now draw all the objects which normally move around and are not
4437 // cached from the previous frame
4438 DrawFloatingOverlayObjects(m_gldc);
4439
4440#ifndef USE_ANDROID_GLES2
4441 // from this point on don't use perspective
4442 if (VPoint.tilt) {
4443 glMatrixMode(GL_PROJECTION);
4444 glLoadIdentity();
4445
4446 glOrtho(0, (GLint)gl_width, (GLint)gl_height, 0, -1, 1);
4447 glMatrixMode(GL_MODELVIEW);
4448 glLoadIdentity();
4449 }
4450#endif
4451
4452 if (!g_bhide_depth_units)
4453 DrawEmboss(m_gldc, m_pParentCanvas->EmbossDepthScale());
4454 if (!g_bhide_overzoom_flag)
4455 DrawEmboss(m_gldc, m_pParentCanvas->EmbossOverzoomIndicator(gldc));
4456
4457 if (g_pi_manager) {
4458 ViewPort &vp = m_pParentCanvas->GetVP();
4459 g_pi_manager->SendViewPortToRequestingPlugIns(vp);
4460 g_pi_manager->RenderAllGLCanvasOverlayPlugIns(
4461 m_pcontext, vp, m_pParentCanvas->m_canvasIndex, OVERLAY_OVER_EMBOSS);
4462 }
4463 if (!g_PrintingInProgress) {
4464 if (m_pParentCanvas->m_pTrackRolloverWin)
4465 m_pParentCanvas->m_pTrackRolloverWin->Draw(gldc);
4466
4467 if (m_pParentCanvas->m_pRouteRolloverWin)
4468 m_pParentCanvas->m_pRouteRolloverWin->Draw(gldc);
4469
4470 if (m_pParentCanvas->m_pAISRolloverWin)
4471 m_pParentCanvas->m_pAISRolloverWin->Draw(gldc);
4472
4473 if (m_pParentCanvas->GetMUIBar())
4474 m_pParentCanvas->GetMUIBar()->DrawGL(gldc, m_displayScale);
4475
4476 if (g_MainToolbar && m_pParentCanvas->IsPrimaryCanvas())
4477 g_MainToolbar->DrawGL(gldc, m_displayScale);
4478
4479 if (g_iENCToolbar && m_pParentCanvas->IsPrimaryCanvas())
4480 g_iENCToolbar->DrawGL(gldc, m_displayScale);
4481 }
4482
4483 // On some platforms, the opengl context window is always on top of any
4484 // standard DC windows, so we need to draw the Chart Info Window
4485 // as overlayed bmps.
4486
4487#ifdef __WXOSX__
4488 if (m_pParentCanvas->m_pCIWin && m_pParentCanvas->m_pCIWin->IsShown()) {
4489 int x, y, width, height;
4490 m_pParentCanvas->m_pCIWin->GetClientSize(&width, &height);
4491 m_pParentCanvas->m_pCIWin->GetPosition(&x, &y);
4492 wxBitmap bmp(width, height, -1);
4493 wxMemoryDC dc(bmp);
4494 if (bmp.IsOk()) {
4495 dc.SetBackground(wxBrush(GetGlobalColor("UIBCK")));
4496 dc.Clear();
4497
4498 dc.SetTextBackground(GetGlobalColor("UIBCK"));
4499 dc.SetTextForeground(GetGlobalColor("UITX1"));
4500
4501 int yt = 0;
4502 int xt = 0;
4503 wxString s = m_pParentCanvas->m_pCIWin->GetString();
4504 int h = m_pParentCanvas->m_pCIWin->GetCharHeight();
4505
4506 wxStringTokenizer tkz(s, "\n");
4507 wxString token;
4508
4509 while (tkz.HasMoreTokens()) {
4510 token = tkz.GetNextToken();
4511 dc.DrawText(token, xt, yt);
4512 yt += h;
4513 }
4514 dc.SelectObject(wxNullBitmap);
4515
4516 m_gldc.DrawBitmap(bmp, x, y, false);
4517 }
4518 }
4519
4520#endif
4521 // render the chart bar
4522 if (g_bShowChartBar) DrawChartBar(m_gldc);
4523
4524 if (m_pParentCanvas->m_Compass && m_pParentCanvas->m_bShowCompassWin &&
4525 g_bShowCompassWin)
4526 m_pParentCanvas->m_Compass->Paint(gldc);
4527
4528 if (m_pParentCanvas->IsPrimaryCanvas()) {
4529 auto &noteman = NotificationManager::GetInstance();
4530 if (noteman.GetNotificationCount()) {
4531 m_pParentCanvas->m_notification_button->SetIconSeverity(
4532 noteman.GetMaxSeverity());
4533 if (m_pParentCanvas->m_notification_button->UpdateStatus()) Refresh();
4534 m_pParentCanvas->m_notification_button->Show(true);
4535 m_pParentCanvas->m_notification_button->Paint(gldc);
4536 } else {
4537 m_pParentCanvas->m_notification_button->Show(false);
4538 }
4539 }
4540 RenderGLAlertMessage();
4541
4542 if (g_pi_manager) {
4543 ViewPort &vp = m_pParentCanvas->GetVP();
4544 g_pi_manager->SendViewPortToRequestingPlugIns(vp);
4545 g_pi_manager->RenderAllGLCanvasOverlayPlugIns(
4546 m_pcontext, vp, m_pParentCanvas->m_canvasIndex, OVERLAY_OVER_UI);
4547 glActiveTexture(GL_TEXTURE0);
4548 }
4549
4550 // quiting?
4551 if (g_bquiting) DrawQuiting();
4552 if (g_bcompression_wait)
4553 DrawCloseMessage(_("Waiting for raster chart compression thread exit."));
4554
4555 // Some older MSW OpenGL drivers are generally very unstable.
4556 // This helps...
4557
4558 if (g_b_needFinish) glFinish();
4559
4560 SwapBuffers();
4561
4562 g_glTextureManager->TextureCrunch(0.8);
4563 g_glTextureManager->FactoryCrunch(0.6);
4564
4565 m_pParentCanvas->PaintCleanup();
4566 m_bforcefull = false;
4567
4568 // if (m_binPinch)
4569 // printf(" Render Finished: %ld\n",
4570 // m_glstopwatch.Time() - render_start_time);
4571
4572 n_render++;
4573}
4574
4575void glChartCanvas::RenderS57TextOverlay(ViewPort &VPoint) {
4576 // Render the decluttered Text overlay for quilted vector charts, except for
4577 // CM93 Composite
4578 if (VPoint.b_quilt) {
4579 if (m_pParentCanvas->m_pQuilt->IsQuiltVector() && ps52plib &&
4580 ps52plib->GetShowS57Text()) {
4581 ChartBase *chart = m_pParentCanvas->m_pQuilt->GetRefChart();
4582 if (chart && (chart->GetChartType() != CHART_TYPE_CM93COMP)) {
4583 // Clear the text Global declutter list
4584 if (chart) {
4585 ChartPlugInWrapper *ChPI = dynamic_cast<ChartPlugInWrapper *>(chart);
4586 if (ChPI)
4587 ChPI->ClearPLIBTextList();
4588 else
4589 ps52plib->ClearTextList();
4590 }
4591
4592 // Grow the ViewPort a bit laterally, to minimize "jumping" of text
4593 // elements at right side of screen
4594 ViewPort vpx = VPoint;
4595 vpx.BuildExpandedVP(VPoint.pix_width * 12 / 10, VPoint.pix_height);
4596
4597 OCPNRegion screen_region(
4598 wxRect(0, 0, VPoint.pix_width, VPoint.pix_height));
4599 RenderQuiltViewGLText(vpx, screen_region);
4600 }
4601 }
4602 }
4603}
4604void glChartCanvas::RenderSingleMBTileOverlay(const int dbIndex, bool bOverlay,
4605 ViewPort &vp,
4606 OCPNRegion &screen_region,
4607 LLRegion &screenLLRegion) {
4608 ChartBase *chart = ChartData->OpenChartFromDBAndLock(dbIndex, FULL_INIT);
4609
4610 // Chart may have been prevented from initial loading due to size, or some
4611 // other reason...
4612 if (chart == NULL) return;
4613
4614 ChartMbTiles *pcmbt = dynamic_cast<ChartMbTiles *>(chart);
4615 if (!pcmbt) return;
4616
4617 // Is tile an OVERLAY type?
4618 // Render, or not, depending on passed flag.
4619 if (bOverlay && pcmbt->GetTileType() != MbTilesType::OVERLAY) return;
4620
4621 wxFileName tileFile(chart->GetFullPath());
4622 // Size test for 5 GByte
4623 wxULongLong tileSizeMB = tileFile.GetSize() >> 20;
4624
4625 if (!ChartData->CheckAnyCanvasExclusiveTileGroup() ||
4626 (tileSizeMB.GetLo() > 5000)) {
4627 // Check to see if the tile has been "clicked".
4628 // If so, do not add to no-show array again.
4629 if (!m_pParentCanvas->IsTileOverlayIndexInYesShow(dbIndex)) {
4630 if (!m_pParentCanvas->IsTileOverlayIndexInNoShow(dbIndex)) {
4631 m_pParentCanvas->m_tile_noshow_index_array.push_back(dbIndex);
4632 }
4633 }
4634 }
4635
4636 // This test catches the case where the chart is added to no_show list
4637 // when first loaded by OpenChartFromDBAndLock
4638 if (m_pParentCanvas->IsTileOverlayIndexInNoShow(dbIndex)) {
4639 return;
4640 }
4641
4642 pcmbt->RenderRegionViewOnGL(*m_pcontext, vp, screen_region, screenLLRegion);
4643
4644 // Light up the piano key if the chart was rendered
4645 std::vector<int> piano_active_array_tiles =
4646 m_pParentCanvas->m_Piano->GetActiveKeyArray();
4647 bool bfound = false;
4648
4649 if (std::find(piano_active_array_tiles.begin(),
4650 piano_active_array_tiles.end(),
4651 dbIndex) != piano_active_array_tiles.end()) {
4652 bfound = true;
4653 }
4654
4655 if (!bfound) {
4656 piano_active_array_tiles.push_back(dbIndex);
4657 m_pParentCanvas->m_Piano->SetActiveKeyArray(piano_active_array_tiles);
4658 }
4659}
4660
4661void glChartCanvas::RenderMBTilesOverlay(ViewPort &VPoint) {
4662 // Render MBTiles as overlay
4663 std::vector<int> stackIndexArray =
4664 m_pParentCanvas->m_pQuilt->GetExtendedStackIndexArray();
4665 unsigned int im = stackIndexArray.size();
4666 // XXX should
4667 // assert(!VPoint.b_quilt && im == 0)
4668 if (VPoint.b_quilt && im > 0) {
4669 bool regionVPBuilt = false;
4670 OCPNRegion screen_region;
4671 LLRegion screenLLRegion;
4672 LLBBox screenBox;
4673 ViewPort vp;
4674
4675 std::vector<int> tiles_to_show;
4676 for (unsigned int is = 0; is < im; is++) {
4677 const ChartTableEntry &cte =
4678 ChartData->GetChartTableEntry(stackIndexArray[is]);
4679 if (cte.GetChartType() == CHART_TYPE_MBTILES) {
4680 if (m_pParentCanvas->IsTileOverlayIndexInNoShow(stackIndexArray[is])) {
4681 // Turn off the piano highlite
4682 std::vector<int> piano_active_array_tiles =
4683 m_pParentCanvas->m_Piano->GetActiveKeyArray();
4684 bool bfound = false;
4685
4686 for (unsigned int i = 0; i < piano_active_array_tiles.size(); i++) {
4687 if (piano_active_array_tiles[i] == stackIndexArray[is]) {
4688 piano_active_array_tiles.erase(piano_active_array_tiles.begin() +
4689 i); // erase it
4690 bfound = true;
4691 break;
4692 }
4693 }
4694
4695 if (bfound)
4696 m_pParentCanvas->m_Piano->SetActiveKeyArray(
4697 piano_active_array_tiles);
4698
4699 continue;
4700 }
4701
4702 tiles_to_show.push_back(stackIndexArray[is]);
4703 if (!regionVPBuilt) {
4704 screen_region =
4705 OCPNRegion(wxRect(0, 0, VPoint.pix_width, VPoint.pix_height));
4706 screenLLRegion = VPoint.GetLLRegion(screen_region);
4707 screenBox = screenLLRegion.GetBox();
4708
4709 vp = VPoint;
4710 wxPoint p;
4711 p.x = VPoint.pix_width / 2;
4712 p.y = VPoint.pix_height / 2;
4713 VPoint.GetLLFromPix(p, &vp.clat, &vp.clon);
4714
4715 regionVPBuilt = true;
4716 }
4717 }
4718 }
4719
4720 // Render in two passes, to render the OVERLAY types last
4721
4722 // Show the tilesets in reverse order to have the largest scale
4723 // on top
4724
4725 for (std::vector<int>::reverse_iterator rit = tiles_to_show.rbegin();
4726 rit != tiles_to_show.rend(); ++rit) {
4727 RenderSingleMBTileOverlay(*rit, FALSE, vp, screen_region, screenLLRegion);
4728 }
4729 for (std::vector<int>::reverse_iterator rit = tiles_to_show.rbegin();
4730 rit != tiles_to_show.rend(); ++rit) {
4731 RenderSingleMBTileOverlay(*rit, TRUE, vp, screen_region, screenLLRegion);
4732 }
4733
4734 // Render the HiLite on piano rollover of mbTile key
4735 LLRegion hiregion = m_pParentCanvas->m_pQuilt->GetHiliteRegion();
4736
4737 if (!hiregion.Empty()) {
4738 glEnable(GL_BLEND);
4739
4740 double hitrans;
4741 switch (global_color_scheme) {
4742 case GLOBAL_COLOR_SCHEME_DAY:
4743 hitrans = .4;
4744 break;
4745 case GLOBAL_COLOR_SCHEME_DUSK:
4746 hitrans = .2;
4747 break;
4748 case GLOBAL_COLOR_SCHEME_NIGHT:
4749 hitrans = .1;
4750 break;
4751 default:
4752 hitrans = .4;
4753 break;
4754 }
4755
4756#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
4757 glColor4f((float).8, (float).4, (float).4, (float)hitrans);
4758#else
4759 s_regionColor = wxColor(204, 102, 102, hitrans * 256);
4760#endif
4761
4762 DrawRegion(VPoint, hiregion);
4763
4764 glDisable(GL_BLEND);
4765 }
4766 }
4767}
4768
4769#if 0
4770void glChartCanvas::RenderCanvasBackingChart(ocpnDC &dc,
4771 OCPNRegion valid_region) {
4772 // Fill the FBO with the current gshhs world chart
4773 int w, h;
4774 GetClientSize(&w, &h);
4775
4776 glViewport(0, 0, (GLint)m_cache_tex_x, (GLint)m_cache_tex_y);
4777#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
4778 glMatrixMode(GL_PROJECTION);
4779 glLoadIdentity();
4780
4781 glOrtho(0, m_cache_tex_x, m_cache_tex_y, 0, -1, 1);
4782 glMatrixMode(GL_MODELVIEW);
4783 glLoadIdentity();
4784#endif
4785
4786 wxRect rtex(0, 0, m_cache_tex_x, m_cache_tex_y);
4787 ViewPort cvp =
4788 m_pParentCanvas->GetVP().BuildExpandedVP(m_cache_tex_x, m_cache_tex_y);
4789
4790 bool world_view = false;
4791 RenderWorldChart(dc, cvp, rtex, world_view);
4792 gShapeBasemap.RenderViewOnDC(dc, cvp);
4793
4794 // dc.SetPen(wxPen(wxColour(254,254,0), 3));
4795 // dc.DrawLine( 0, 0, m_cache_tex_x, m_cache_tex_y);
4796
4797 // Reset matrices
4798 glViewport(0, 0, (GLint)w, (GLint)h);
4799#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
4800 glMatrixMode(GL_PROJECTION);
4801 glLoadIdentity();
4802
4803 glOrtho(0, (GLint)w, (GLint)h, 0, -1, 1);
4804 glMatrixMode(GL_MODELVIEW);
4805 glLoadIdentity();
4806#endif
4807}
4808#endif
4809
4810void glChartCanvas::FastPan(int dx, int dy) {
4811#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
4812#endif
4813}
4814
4815void glChartCanvas::ZoomProject(float offset_x, float offset_y, float swidth,
4816 float sheight) {
4817 if (IsShown()) SetCurrent(*m_pcontext);
4818 float sx = GetSize().x;
4819 float sy = GetSize().y;
4820 glClear(GL_COLOR_BUFFER_BIT);
4821
4822 int w, h;
4823 GetClientSize(&w, &h);
4824
4825 if (s_b_useStencil) {
4826 glEnable(GL_STENCIL_TEST);
4827 glStencilMask(0xff);
4828 glClear(GL_STENCIL_BUFFER_BIT);
4829 glDisable(GL_STENCIL_TEST);
4830 }
4831
4832#ifndef __ANDROID__
4833 // Render backing texture
4834 if (1) {
4835 float tx0 = 0;
4836 float ty0 = 0;
4837 float tx = sx;
4838 float ty = sy;
4839
4840 float vx0 = 0;
4841 float vy0 = 0;
4842 float vx = sx;
4843 float vy = sy;
4844
4845 float sxfactor = sx / swidth;
4846 float syfactor = sy / sheight;
4847
4848 glViewport(-offset_x * sx / swidth - (sx * sxfactor / 2),
4849 -offset_y * (sy / sheight) - (sy * syfactor / 2),
4850 sx * sx / swidth * 2, sy * sy / sheight * 2);
4851 glBindTexture(g_texture_rectangle_format, m_TouchBackingTexture);
4852 glEnable(g_texture_rectangle_format);
4853
4854 float uv[8];
4855 float coords[8];
4856
4857 // normal uv Texture coordinates
4858 uv[0] = 0;
4859 uv[1] = 0;
4860 uv[2] = 1;
4861 uv[3] = 0;
4862 uv[4] = 1;
4863 uv[5] = 1;
4864 uv[6] = 0;
4865 uv[7] = 1;
4866
4867 // pixels
4868 coords[0] = vx0;
4869 coords[1] = vy0;
4870 coords[2] = vx;
4871 coords[3] = vy0;
4872 coords[4] = vx;
4873 coords[5] = vy;
4874 coords[6] = vx0;
4875 coords[7] = vy;
4876
4877 RenderTextures(m_gldc, coords, uv, 4, &m_texVP);
4878 glBindTexture(g_texture_rectangle_format, 0);
4879 }
4880#endif
4881
4882 // render zoomed canvas section
4883 if (1) {
4884 float tx, ty, tx0, ty0;
4885 tx = sx, ty = sy;
4886
4887 tx0 = ty0 = 0.;
4888
4889 float vx0 = 0;
4890 float vy0 = 0;
4891 float vy = sy;
4892 float vx = sx;
4893
4894 glBindTexture(g_texture_rectangle_format, 0);
4895
4896 // Render the cached texture as quad to screen
4897 glBindTexture(g_texture_rectangle_format, m_cache_tex[m_cache_page]);
4898 glEnable(g_texture_rectangle_format);
4899
4900 float uv[8];
4901 float coords[8];
4902
4903 // normal uv Texture coordinates
4904 uv[0] = tx0 / m_cache_tex_x;
4905 uv[1] = ty / m_cache_tex_y;
4906 uv[2] = tx / m_cache_tex_x;
4907 uv[3] = ty / m_cache_tex_y;
4908 uv[4] = tx / m_cache_tex_x;
4909 uv[5] = ty0 / m_cache_tex_y;
4910 uv[6] = tx0 / m_cache_tex_x;
4911 uv[7] = ty0 / m_cache_tex_y;
4912
4913 // pixels
4914 coords[0] = vx0;
4915 coords[1] = vy0;
4916 coords[2] = vx;
4917 coords[3] = vy0;
4918 coords[4] = vx;
4919 coords[5] = vy;
4920 coords[6] = vx0;
4921 coords[7] = vy;
4922
4923 glViewport(-offset_x * sx / swidth, -offset_y * (sy / sheight),
4924 sx * sx / swidth, sy * sy / sheight);
4925
4926 RenderTextures(m_gldc, coords, uv, 4, m_pParentCanvas->GetpVP());
4927
4928 glDisable(g_texture_rectangle_format);
4929 glBindTexture(g_texture_rectangle_format, 0);
4930 }
4931
4932#if 0
4933 // For fun, we prove the coordinates of the blank area outside the chart when
4934 // zooming out. Bottom stripe
4935 // wxColour color = GetGlobalColor("YELO1"); //GREY1
4936 wxColour color = GetGlobalColor("GREY1"); //
4937 float ht = -offset_y * (sy / sheight);
4938 wxRect r(0, sy - ht, w, ht);
4939 RenderColorRect(r, color);
4940
4941 // top stripe
4942 wxRect rt(0, 0, w, sy - (ht + (sy * sy / sheight)));
4943 RenderColorRect(rt, color);
4944
4945 // left
4946 float w1 = -offset_x * sx / swidth;
4947 wxRect rl(0, 0, w1, sy);
4948 RenderColorRect(rl, color);
4949
4950 // right
4951 float px = w1 + sx * sx / swidth;
4952 wxRect rr(px, 0, sx - px, sy);
4953 RenderColorRect(rr, color);
4954#endif
4955 // When zooming out, if we go too far, then the frame buffer is repeated
4956 // on-screen due to address wrapping in the frame buffer. Detect this case,
4957 // and render some simple solid covering quads to avoid a confusing display.
4958
4959 SwapBuffers();
4960}
4961
4962void glChartCanvas::onZoomTimerEvent(wxTimerEvent &event) {
4963 // If m_zoomFinal is set, stop the timer.
4964 if (!m_zoomFinal) {
4965 if (m_nRun < m_nTotal) {
4966 m_runoffsetx += m_offsetxStep;
4967 if (m_offsetxStep > 0)
4968 m_runoffsetx = wxMin(m_runoffsetx, m_fbo_offsetx);
4969 else
4970 m_runoffsetx = wxMax(m_runoffsetx, m_fbo_offsetx);
4971
4972 m_runoffsety += m_offsetyStep;
4973 if (m_offsetyStep > 0)
4974 m_runoffsety = wxMin(m_runoffsety, m_fbo_offsety);
4975 else
4976 m_runoffsety = wxMax(m_runoffsety, m_fbo_offsety);
4977
4978 m_runswidth += m_swidthStep;
4979 if (m_swidthStep > 0)
4980 m_runswidth = wxMin(m_runswidth, m_fbo_swidth);
4981 else
4982 m_runswidth = wxMax(m_runswidth, m_fbo_swidth);
4983
4984 m_runsheight += m_sheightStep;
4985 if (m_sheightStep > 0)
4986 m_runsheight = wxMin(m_runsheight, m_fbo_sheight);
4987 else
4988 m_runsheight = wxMax(m_runsheight, m_fbo_sheight);
4989
4990 m_nRun += m_nStep;
4991 }
4992
4993 ZoomProject(m_runoffsetx, m_runoffsety, m_runswidth, m_runsheight);
4994
4995 } else {
4996 zoomTimer.Stop();
4997 if (m_zoomFinal) {
4998 m_pParentCanvas->ZoomCanvasSimple(m_zoomFinalZoom);
4999 if (m_zoomFinaldx || m_zoomFinaldy) {
5000 m_pParentCanvas->PanCanvas(m_zoomFinaldx, m_zoomFinaldy);
5001 }
5002 }
5003 m_zoomFinal = false;
5004 }
5005}
5006
5007void glChartCanvas::FastZoom(float factor, float cp_x, float cp_y, float post_x,
5008 float post_y) {
5009 int sx = GetSize().x;
5010 int sy = GetSize().y;
5011
5012 m_lastfbo_offsetx = m_fbo_offsetx;
5013 m_lastfbo_offsety = m_fbo_offsety;
5014 m_lastfbo_swidth = m_fbo_swidth;
5015 m_lastfbo_sheight = m_fbo_sheight;
5016
5017 float curr_fbo_offset_x = m_fbo_offsetx;
5018 float curr_fbo_offset_y = m_fbo_offsety;
5019 float curr_fbo_swidth = m_fbo_swidth;
5020 float curr_fbo_sheight = m_fbo_sheight;
5021
5022 float fx = (float)cp_x / sx;
5023 float fy = 1.0 - (float)cp_y / sy;
5024
5025 float fbo_ctr_x = curr_fbo_offset_x + (curr_fbo_swidth * fx);
5026 float fbo_ctr_y = curr_fbo_offset_y + (curr_fbo_sheight * fy);
5027
5028 m_fbo_swidth = curr_fbo_swidth / factor;
5029 m_fbo_sheight = curr_fbo_sheight / factor;
5030
5031 m_fbo_offsetx = fbo_ctr_x - (m_fbo_swidth * fx);
5032 m_fbo_offsety = fbo_ctr_y - (m_fbo_sheight * fy);
5033
5034 m_fbo_offsetx += post_x;
5035 m_fbo_offsety += post_y;
5036
5037 {
5038 m_nStep = 20;
5039 m_nTotal = 100;
5040
5041 // m_nStep = 10; // Android?
5042 // m_nTotal = 40;
5043
5044 m_nRun = 0;
5045
5046 float perStep = m_nStep / m_nTotal;
5047
5048 if (zoomTimer.IsRunning()) {
5049 m_offsetxStep = (m_fbo_offsetx - m_runoffsetx) * perStep;
5050 m_offsetyStep = (m_fbo_offsety - m_runoffsety) * perStep;
5051 m_swidthStep = (m_fbo_swidth - m_runswidth) * perStep;
5052 m_sheightStep = (m_fbo_sheight - m_runsheight) * perStep;
5053
5054 } else {
5055 m_offsetxStep = (m_fbo_offsetx - m_lastfbo_offsetx) * perStep;
5056 m_offsetyStep = (m_fbo_offsety - m_lastfbo_offsety) * perStep;
5057 m_swidthStep = (m_fbo_swidth - m_lastfbo_swidth) * perStep;
5058 m_sheightStep = (m_fbo_sheight - m_lastfbo_sheight) * perStep;
5059
5060 m_runoffsetx = m_lastfbo_offsetx;
5061 m_runoffsety = m_lastfbo_offsety;
5062 m_runswidth = m_lastfbo_swidth;
5063 m_runsheight = m_lastfbo_sheight;
5064 }
5065
5066 if (!zoomTimer.IsRunning()) zoomTimer.Start(m_nStep);
5067 m_zoomFinal = false;
5068 }
5069}
5070
5071#ifdef __ANDROID__
5072
5073void glChartCanvas::OnEvtPanGesture(wxQT_PanGestureEvent &event) {
5074 // qDebug() << "OnEvtPanGesture" << m_pParentCanvas->m_canvasIndex <<
5075 // event.cursor_pos.x;
5076
5077 if (m_pParentCanvas->isRouteEditing() || m_pParentCanvas->isMarkEditing())
5078 return;
5079
5080 if (m_binPinch) return;
5081 if (m_bpinchGuard) return;
5082
5083 int x = event.GetOffset().x;
5084 int y = event.GetOffset().y;
5085
5086 int lx = event.GetLastOffset().x;
5087 int ly = event.GetLastOffset().y;
5088
5089 int dx = lx - x;
5090 int dy = y - ly;
5091
5092 switch (event.GetState()) {
5093 case GestureStarted:
5094 if (m_binPan) break;
5095
5096 panx = pany = 0;
5097 m_binPan = true;
5098 m_binGesture = true;
5099 // qDebug() << "pg1";
5100 break;
5101
5102 case GestureUpdated:
5103 if (m_binPan) {
5104 if (!g_GLOptions.m_bUseCanvasPanning) {
5105 // qDebug() << "slowpan" << dx << dy;
5106
5107 m_pParentCanvas->FreezePiano();
5108 m_pParentCanvas->PanCanvas(dx, -dy);
5109 m_pParentCanvas->ThawPiano();
5110
5111 } else {
5112 FastPan(dx, dy);
5113 }
5114
5115 panx -= dx;
5116 pany -= dy;
5117 }
5118 break;
5119
5120 case GestureFinished:
5121 // qDebug() << "panGestureFinished";
5122
5123 m_pParentCanvas->UpdateCanvasControlBar();
5124
5125 m_binPan = false;
5126 m_gestureFinishTimer.Start(500, wxTIMER_ONE_SHOT);
5127
5128 break;
5129
5130 case GestureCanceled:
5131 m_binPan = false;
5132 m_gestureFinishTimer.Start(500, wxTIMER_ONE_SHOT);
5133 break;
5134
5135 default:
5136 break;
5137 }
5138
5139 m_bgestureGuard = true;
5140 m_gestureEeventTimer.Start(500, wxTIMER_ONE_SHOT);
5141 m_bforcefull = false;
5142
5143 // qDebug() << "panGestureDone";
5144}
5145
5146float zoom_inc = 1.0;
5147
5148void glChartCanvas::OnEvtPinchGesture(wxQT_PinchGestureEvent &event) {
5149 float zoom_gain = 1.0;
5150 float zout_gain = 1.0;
5151
5152 float zoom_val;
5153 float total_zoom_val;
5154
5155 float max_zoom_scale = 1000.;
5156 float min_zoom_scale = 2e8;
5157
5158 if (event.GetScaleFactor() > 1)
5159 zoom_val = ((event.GetScaleFactor() - 1.0) * zoom_gain) + 1.0;
5160 else
5161 zoom_val = 1.0 - ((1.0 - event.GetScaleFactor()) * zout_gain);
5162
5163 if (event.GetTotalScaleFactor() > 1)
5164 total_zoom_val = ((event.GetTotalScaleFactor() - 1.0) * zoom_gain) + 1.0;
5165 else
5166#if 0
5167 total_zoom_val = 1.0 - ((1.0 - event.GetTotalScaleFactor()) * zout_gain);
5168
5169 double projected_scale = cc1->GetVP().chart_scale / total_zoom_val;
5170
5171 // Max zoom in is set by scale of quilt reference chart, consistent with chart render limits set elsewhere.
5172 float max_zoom_scale = 1000.;
5173 if( cc1->GetVP().b_quilt) {
5174 int ref_index = cc1->GetQuiltRefChartdbIndex();
5175// if((ref_index >= 0) && ChartData){
5176// max_zoom_scale = ChartData->GetDBChartScale(ref_index) / 8.0;
5177// }
5178 }
5179
5180
5181 float min_zoom_scale = 2e8;
5182
5183#endif
5184
5185 total_zoom_val = 1.0 - ((1.0 - event.GetTotalScaleFactor()) * zoom_gain);
5186
5187 double projected_scale =
5188 m_pParentCanvas->GetVP().chart_scale / total_zoom_val;
5189
5190 switch (event.GetState()) {
5191 case GestureStarted:
5192 m_first_zout = false;
5193 m_binPinch = true;
5194 m_binPan = false; // cancel any tentative pan gesture, in case the "pan
5195 // cancel" event was lost
5196 m_binGesture = true;
5197 // qDebug() << "pg2";
5198 m_pinchStart = event.GetCenterPoint();
5199 m_lpinchPoint = m_pinchStart;
5200
5201 m_pParentCanvas->GetCanvasPixPoint(event.GetCenterPoint().x,
5202 event.GetCenterPoint().y, m_pinchlat,
5203 m_pinchlon);
5204 // qDebug() << "center" << event.GetCenterPoint().x <<
5205 // event.GetCenterPoint().y;
5206
5207 m_cc_x = m_fbo_offsetx + (m_fbo_swidth / 2);
5208 m_cc_y = m_fbo_offsety + (m_fbo_sheight / 2);
5209
5210 // Render the full charts with overlay objects onto the frame buffer.
5211 if (IsShown()) SetCurrent(*m_pcontext);
5212 RenderScene();
5213
5214 zoom_inc = 1.0;
5215 break;
5216
5217 case GestureUpdated:
5218 if (g_GLOptions.m_bUseCanvasPanning) {
5219 if (projected_scale < min_zoom_scale) {
5220 wxPoint pinchPoint = event.GetCenterPoint();
5221
5222 float dx = pinchPoint.x - m_lpinchPoint.x;
5223 float dy = pinchPoint.y - m_lpinchPoint.y;
5224
5225 FastZoom(zoom_val, m_pinchStart.x, m_pinchStart.y,
5226 -dx / total_zoom_val, dy / total_zoom_val);
5227
5228 m_lpinchPoint = pinchPoint;
5229 }
5230 } else {
5231 // qDebug() << "update totalzoom" << total_zoom_val << projected_scale;
5232 if (1 || ((total_zoom_val > 1) && !m_first_zout)) { // Zoom in
5233 wxPoint pinchPoint = event.GetCenterPoint();
5234
5235 float dx = pinchPoint.x - m_lpinchPoint.x;
5236 float dy = pinchPoint.y - m_lpinchPoint.y;
5237
5238 if ((projected_scale > max_zoom_scale) &&
5239 (projected_scale < min_zoom_scale))
5240 FastZoom(zoom_val, m_pinchStart.x, m_pinchStart.y,
5241 -dx / total_zoom_val, dy / total_zoom_val);
5242
5243 m_lpinchPoint = pinchPoint;
5244
5245 } else {
5246 m_first_zout = true;
5247 zoom_inc *= zoom_val;
5248 if ((zoom_inc < 0.9) || (zoom_inc > 1.1)) {
5249 m_pParentCanvas->ZoomCanvas(zoom_inc, false);
5250 zoom_inc = 1.0;
5251 }
5252
5253 wxPoint pinchPoint = event.GetCenterPoint();
5254 float dx = pinchPoint.x - m_lpinchPoint.x;
5255 float dy = pinchPoint.y - m_lpinchPoint.y;
5256 m_pParentCanvas->PanCanvas(-dx, -dy);
5257 m_lpinchPoint = pinchPoint;
5258
5259 // SetCurrent(*m_pcontext);
5260 // RenderScene();
5261 // g_Piano->DrawGL(cc1->m_canvas_height -
5262 // g_Piano->GetHeight()); SwapBuffers();
5263 }
5264 }
5265
5266 break;
5267
5268 case GestureFinished: {
5269 // qDebug() << "finish totalzoom" << total_zoom_val <<
5270 // projected_scale;
5271
5272 float cc_x = m_fbo_offsetx + (m_fbo_swidth / 2);
5273 float cc_y = m_fbo_offsety + (m_fbo_sheight / 2);
5274 float dy = 0;
5275 float dx = 0;
5276
5277 float tzoom = total_zoom_val;
5278
5279 if (projected_scale >= min_zoom_scale)
5280 tzoom = m_pParentCanvas->GetVP().chart_scale / min_zoom_scale;
5281
5282 if (projected_scale < max_zoom_scale)
5283 tzoom = m_pParentCanvas->GetVP().chart_scale / max_zoom_scale;
5284
5285 dx = (cc_x - m_cc_x) * tzoom;
5286 dy = -(cc_y - m_cc_y) * tzoom;
5287
5288 if (zoomTimer.IsRunning()) {
5289 // qDebug() << "Final zoom";
5290 m_zoomFinal = true;
5291 m_zoomFinalZoom = tzoom;
5292 m_zoomFinaldx = dx;
5293 m_zoomFinaldy = dy;
5294 }
5295
5296 else {
5297 double final_projected_scale =
5298 m_pParentCanvas->GetVP().chart_scale / tzoom;
5299 // qDebug() << "Final pinchB" << tzoom << final_projected_scale;
5300
5301 if (final_projected_scale < min_zoom_scale) {
5302 // qDebug() << "zoomit";
5303 m_pParentCanvas->ZoomCanvas(tzoom, false);
5304 m_pParentCanvas->PanCanvas(dx, dy);
5305 m_pParentCanvas->m_pQuilt->Invalidate();
5306 m_bforcefull = true;
5307
5308 } else {
5309 double new_scale =
5310 m_pParentCanvas->GetCanvasScaleFactor() / min_zoom_scale;
5311 // qDebug() << "clampit";
5312 m_pParentCanvas->SetVPScale(new_scale);
5313 m_pParentCanvas->m_pQuilt->Invalidate();
5314 m_bforcefull = true;
5315 }
5316 }
5317
5318 // if( projected_scale < 3e8)
5319 // m_pParentCanvas->ZoomCanvas( total_zoom_val, false
5320 // );
5321 // else
5322 // m_pParentCanvas->ZoomCanvas(m_pParentCanvas->GetVP().chart_scale
5323 // / 3e8, false);
5324
5325 m_binPinch = false;
5326 m_gestureFinishTimer.Start(500, wxTIMER_ONE_SHOT);
5327 break;
5328 }
5329
5330 case GestureCanceled:
5331 m_binPinch = false;
5332 m_gestureFinishTimer.Start(500, wxTIMER_ONE_SHOT);
5333 break;
5334
5335 default:
5336 break;
5337 }
5338
5339 m_bgestureGuard = true;
5340 // m_bpinchGuard = true;
5341 m_gestureEeventTimer.Start(500, wxTIMER_ONE_SHOT);
5342}
5343
5344void glChartCanvas::onGestureTimerEvent(wxTimerEvent &event) {
5345 // On some devices, the pan GestureFinished event fails to show up
5346 // Watch for this case, and fix it.....
5347 // qDebug() << "onGestureTimerEvent";
5348
5349 if (m_binPan) {
5350 m_binPan = false;
5351 Invalidate();
5352 Update();
5353 }
5354 m_bgestureGuard = false;
5355 m_bpinchGuard = false;
5356 m_binGesture = false;
5357 m_bforcefull = false;
5358}
5359
5360void glChartCanvas::onGestureFinishTimerEvent(wxTimerEvent &event) {
5361 // qDebug() << "onGestureFinishTimerEvent";
5362
5363 // signal gesture is finished after a delay
5364 m_binGesture = false;
5365 m_bforcefull = false;
5366}
5367
5368#else
5369#ifdef HAVE_WX_GESTURE_EVENTS
5370
5371void glChartCanvas::OnEvtPanGesture(wxPanGestureEvent &event) {
5372 // qDebug() << "OnEvtPanGesture" << m_pParentCanvas->m_canvasIndex <<
5373 // event.cursor_pos.x;
5374 if (m_pParentCanvas->isRouteEditing() || m_pParentCanvas->isMarkEditing())
5375 return;
5376
5377 if (m_binPinch) return;
5378 if (m_bpinchGuard) return;
5379
5380 int dx = event.GetDelta().x;
5381 int dy = event.GetDelta().y;
5382
5383 if (event.IsGestureStart()) {
5384 if (m_binPan) return;
5385
5386 panx = pany = 0;
5387 m_binPan = true;
5388 m_binGesture = true;
5389 // qDebug() << "pg1";
5390 }
5391
5392 else if (event.IsGestureEnd()) {
5393 // qDebug() << "panGestureFinished";
5394 m_pParentCanvas->UpdateCanvasControlBar();
5395 m_binPan = false;
5396 m_gestureFinishTimer.Start(500, wxTIMER_ONE_SHOT);
5397 }
5398
5399 else {
5400 if (m_binPan) {
5401 if (!g_GLOptions.m_bUseCanvasPanning) {
5402 // qDebug() << "slowpan" << dx << dy;
5403
5404 m_pParentCanvas->FreezePiano();
5405 m_pParentCanvas->PanCanvas(dx, -dy);
5406 m_pParentCanvas->ThawPiano();
5407
5408 } else {
5409 FastPan(dx, dy);
5410 }
5411
5412 panx -= dx;
5413 pany -= dy;
5414 }
5415 }
5416
5417 m_bgestureGuard = true;
5418 m_gestureEeventTimer.Start(500, wxTIMER_ONE_SHOT);
5419 m_bforcefull = false;
5420}
5421
5422// Generic wxWidgets gesture event processor
5423void glChartCanvas::OnEvtZoomGesture(wxZoomGestureEvent &event) {
5424 if (g_true_zoom) {
5425 float zoom_gain = 1.0;
5426 float zout_gain = 1.0;
5427
5428 float last_zoom_val = m_step_zoom_val; // m_total_zoom_val;
5429
5430 float max_zoom_scale = 1000.;
5431 float min_zoom_scale = 2e8;
5432
5433 if (event.GetZoomFactor() > 1)
5434 m_total_zoom_val = ((event.GetZoomFactor() - 1.0) * zoom_gain) + 1.0;
5435 else
5436 m_total_zoom_val = 1.0 - ((1.0 - event.GetZoomFactor()) * zout_gain);
5437
5438 float inc_zoom_val =
5439 m_total_zoom_val / last_zoom_val; // the incremental zoom
5440
5441 double projected_scale = m_cache_vp.chart_scale;
5442
5443 if (event.IsGestureStart()) {
5444 m_glstopwatch.Start();
5445 printf("\nStart--------------\n");
5446 m_binPinch = true;
5447 m_pParentCanvas->m_inPinch = true;
5448 m_binPan = false; // cancel any tentative pan gesture, in case the "pan
5449 // cancel" event was lost
5450 m_binGesture = true;
5451 m_pinchStart = event.GetPosition();
5452 m_lpinchPoint = m_pinchStart;
5453 m_total_zoom_val = 1.0;
5454 m_final_zoom_val = 1.0;
5455 m_step_zoom_val = 1.0;
5456
5457 // Capture the lat/lon of the "pinch-point"
5458 m_pParentCanvas->GetCanvasPixPoint(
5459 event.GetPosition().x, event.GetPosition().y, m_pinchlat, m_pinchlon);
5460 m_zoom_inc = 1.0;
5461 }
5462
5463 if (event.IsGestureEnd()) {
5464 // Some platforms generate spurious gestureEnd events. Guard for this.
5465 if (!m_binGesture) return;
5466 printf("EndZoom--------------\n");
5467
5468 Refresh();
5469
5470 // Let the gesture finish timer (500 msec) signal the pinch is done.
5471 // this will allow any lingering drag operation to avoid starting the
5472 // inertial drag process
5473 m_final_zoom_val = 1.0;
5474 m_total_zoom_val = 1.0;
5475 m_step_zoom_val = 1.0;
5476
5477 m_gestureFinishTimer.Start(500, wxTIMER_ONE_SHOT);
5478 }
5479
5480 else {
5481 // printf("%ld %6g %6g %6g \n", g_glstopwatch.Time(),
5482 // event.GetZoomFactor(),
5483 // inc_zoom_val, projected_scale);
5484 float zoom_step = 5;
5485 float zoom_trigger = 0.05;
5486 if (projected_scale > 1e5)
5487 zoom_trigger = .1;
5488 else if (projected_scale < 3e4)
5489 zoom_trigger = .02;
5490
5491 if (inc_zoom_val != 1.0) {
5492 if (inc_zoom_val > 1 + zoom_step) {
5493 m_step_zoom_val = m_step_zoom_val * (1 + zoom_step);
5494 printf(" Partial zoom: %6g\n", 1 + zoom_step);
5495 m_pParentCanvas->ZoomCanvasSimple(1 + zoom_step);
5496 if (IsShown()) SetCurrent(*m_pcontext);
5497 Render();
5498 } else {
5499 if (fabs(inc_zoom_val - 1.) > zoom_trigger) {
5500 m_step_zoom_val = m_total_zoom_val;
5501 printf(" Zoom: %6g\n", inc_zoom_val);
5502 m_pParentCanvas->ZoomCanvasSimple(inc_zoom_val);
5503
5504 // Consider "zoom to pinch-point"
5505 // Disable ZTC if lookahead is ON, and currently b_follow is active
5506 bool b_allow_ztp = true;
5507 if (m_pParentCanvas->m_bFollow && m_pParentCanvas->m_bLookAhead)
5508 b_allow_ztp = false;
5509
5510 if (g_bEnableZoomToCursor && b_allow_ztp) {
5511 // get the new (after zoom) pixel position of the original
5512 // pinch-point
5513 wxPoint r;
5514 m_pParentCanvas->GetCanvasPointPix(m_pinchlat, m_pinchlon, &r);
5515 // Shift the canvas to follow the pinch-lat/lon
5516 int dx = r.x - event.GetPosition().x;
5517 int dy = r.y - event.GetPosition().y;
5518 m_pParentCanvas->PanCanvas(dx, dy);
5519 }
5520
5521 if (IsShown()) SetCurrent(*m_pcontext);
5522 Render();
5523 }
5524 }
5525 }
5526 }
5527 } // true-zoom
5528 else {
5529 // Legacy zoom behavior
5530 float zoom_gain = 1.0;
5531 float zout_gain = 1.0;
5532
5533 float last_zoom_val = m_total_zoom_val;
5534
5535 float max_zoom_scale = 1000.;
5536 float min_zoom_scale = 2e8;
5537
5538 if (event.GetZoomFactor() > 1)
5539 m_total_zoom_val = ((event.GetZoomFactor() - 1.0) * zoom_gain) + 1.0;
5540 else
5541 m_total_zoom_val = 1.0 - ((1.0 - event.GetZoomFactor()) * zout_gain);
5542
5543 float inc_zoom_val =
5544 m_total_zoom_val / last_zoom_val; // the incremental zoom
5545
5546 double projected_scale =
5547 m_pParentCanvas->GetVP().chart_scale / m_total_zoom_val;
5548
5549 if (event.IsGestureStart()) {
5550 // printf("\nStart--------------\n");
5551 m_first_zout = false;
5552 m_binPinch = true;
5553 m_binPan = false; // cancel any tentative pan gesture, in case the "pan
5554 // cancel" event was lost
5555 m_binGesture = true;
5556 m_pinchStart = event.GetPosition();
5557 m_lpinchPoint = m_pinchStart;
5558 m_total_zoom_val = 1.0;
5559 m_final_zoom_val = 1.0;
5560
5561 m_pParentCanvas->GetCanvasPixPoint(
5562 event.GetPosition().x, event.GetPosition().y, m_pinchlat, m_pinchlon);
5563 // qDebug() << "center" << event.GetCenterPoint().x <<
5564 // event.GetCenterPoint().y;
5565
5566 m_cc_x = m_fbo_offsetx + (m_fbo_swidth / 2);
5567 m_cc_y = m_fbo_offsety + (m_fbo_sheight / 2);
5568
5569 // Render the full charts with overlay objects onto the frame buffer.
5570 if (IsShown()) SetCurrent(*m_pcontext);
5571 RenderScene();
5572#ifndef __ANDROID__
5573 ViewPort vpr = m_pParentCanvas->VPoint;
5574 m_texVP = vpr;
5575 GetTouchBackingBitmap(vpr);
5576#endif
5577 m_zoom_inc = 1.0;
5578 }
5579
5580 if (event.IsGestureEnd()) {
5581 // printf("End--------------\n");
5582 // qDebug() << "finish totalzoom" << total_zoom_val <<
5583 // projected_scale;
5584
5585 // Some ptaforms generate spurious gestureEnd events. Guard for this.
5586 if (!m_binGesture) return;
5587
5588 float cc_x = m_fbo_offsetx + (m_fbo_swidth / 2);
5589 float cc_y = m_fbo_offsety + (m_fbo_sheight / 2);
5590 float dy = 0;
5591 float dx = 0;
5592
5593 float tzoom = m_final_zoom_val;
5594
5595 dx = (cc_x - m_cc_x) * tzoom;
5596 dy = -(cc_y - m_cc_y) * tzoom;
5597
5598 if (zoomTimer.IsRunning()) {
5599 // qDebug() << "Final zoom";
5600 m_zoomFinal = true;
5601 m_zoomFinalZoom = tzoom;
5602 m_zoomFinaldx = dx;
5603 m_zoomFinaldy = dy;
5604 }
5605
5606 else {
5607 double final_projected_scale =
5608 m_pParentCanvas->GetVP().chart_scale / tzoom;
5609 // qDebug() << "Final pinchB" << tzoom << final_projected_scale;
5610
5611 if (final_projected_scale < min_zoom_scale) {
5612 // qDebug() << "zoomit";
5613 m_pParentCanvas->ZoomCanvas(tzoom, false);
5614 m_pParentCanvas->PanCanvas(dx, dy);
5615 m_pParentCanvas->m_pQuilt->Invalidate();
5616 m_bforcefull = true;
5617
5618 } else {
5619 double new_scale =
5620 m_pParentCanvas->GetCanvasScaleFactor() / min_zoom_scale;
5621 // qDebug() << "clampit";
5622 m_pParentCanvas->SetVPScale(new_scale);
5623 m_pParentCanvas->m_pQuilt->Invalidate();
5624 m_bforcefull = true;
5625 }
5626 }
5627
5628 m_binPinch = false;
5629 m_final_zoom_val = 1.0;
5630 m_total_zoom_val = 1.0;
5631 m_gestureFinishTimer.Start(500, wxTIMER_ONE_SHOT);
5632 }
5633
5634 else {
5635 if (1 /* g_GLOptions.m_bUseCanvasPanning*/) {
5636 if (projected_scale < min_zoom_scale) {
5637 wxPoint pinchPoint = event.GetPosition();
5638
5639 float dx = pinchPoint.x - m_lpinchPoint.x;
5640 float dy = pinchPoint.y - m_lpinchPoint.y;
5641
5642 FastZoom(inc_zoom_val, m_pinchStart.x, m_pinchStart.y,
5643 -dx / m_total_zoom_val, dy / m_total_zoom_val);
5644
5645 m_lpinchPoint = pinchPoint;
5646 m_final_zoom_val *= inc_zoom_val;
5647 }
5648 } else {
5649 // qDebug() << "update totalzoom" << total_zoom_val << projected_scale;
5650 if (1 || ((m_total_zoom_val > 1) && !m_first_zout)) { // Zoom in
5651 wxPoint pinchPoint = event.GetPosition();
5652
5653 float dx = pinchPoint.x - m_lpinchPoint.x;
5654 float dy = pinchPoint.y - m_lpinchPoint.y;
5655
5656 if ((projected_scale > max_zoom_scale) &&
5657 (projected_scale < min_zoom_scale))
5658 FastZoom(inc_zoom_val, m_pinchStart.x, m_pinchStart.y,
5659 -dx / m_total_zoom_val, dy / m_total_zoom_val);
5660
5661 m_lpinchPoint = pinchPoint;
5662 m_final_zoom_val *= inc_zoom_val;
5663
5664 } else {
5665 m_first_zout = true;
5666 m_zoom_inc *= inc_zoom_val;
5667 if ((m_zoom_inc < 0.9) || (m_zoom_inc > 1.1)) {
5668 m_pParentCanvas->ZoomCanvas(m_zoom_inc, false);
5669 m_zoom_inc = 1.0;
5670 }
5671
5672 wxPoint pinchPoint = event.GetPosition();
5673 float dx = pinchPoint.x - m_lpinchPoint.x;
5674 float dy = pinchPoint.y - m_lpinchPoint.y;
5675 m_pParentCanvas->PanCanvas(-dx, -dy);
5676 m_lpinchPoint = pinchPoint;
5677 }
5678 }
5679 }
5680 m_gestureEeventTimer.Start(500, wxTIMER_ONE_SHOT);
5681 }
5682
5683 m_bgestureGuard = true;
5684 m_bpinchGuard = true;
5685}
5686
5687void glChartCanvas::onGestureTimerEvent(wxTimerEvent &event) {
5688 // On some devices, the pan GestureFinished event fails to show up
5689 // Watch for this case, and fix it.....
5690
5691 if (m_binPan) {
5692 m_binPan = false;
5693 Invalidate();
5694 Update();
5695 }
5696 m_bgestureGuard = false;
5697 m_bpinchGuard = false;
5698 m_binGesture = false;
5699 m_bforcefull = false;
5700}
5701
5702void glChartCanvas::onGestureFinishTimerEvent(wxTimerEvent &event) {
5703 // qDebug() << "onGestureFinishTimerEvent";
5704
5705 // signal gesture is finished after a delay
5706 m_binGesture = false;
5707 m_bforcefull = false;
5708 m_binPinch = false;
5709 m_pParentCanvas->m_inPinch = false;
5710 printf("******Finish\n");
5711}
5712
5713#endif
5714#endif
5715
5716void glChartCanvas::configureShaders(ViewPort &vp) {
5717#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
5718 mat4x4 I;
5719 mat4x4_identity(I);
5720
5721 ViewPort *pvp = (ViewPort *)&vp;
5722
5723 GLShaderProgram *shader = pcolor_tri_shader_program[GetCanvasIndex()];
5724 shader->Bind();
5725 shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)pvp->vp_matrix_transform);
5726 shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)I);
5727 shader->UnBind();
5728
5729 // glUseProgram(color_tri_shader_program);
5730 // GLint matloc = glGetUniformLocation(color_tri_shader_program,
5731 // "MVMatrix"); glUniformMatrix4fv(matloc, 1, GL_FALSE,
5732 // (const GLfloat *)pvp->vp_transform);
5733 // GLint transloc =
5734 // glGetUniformLocation(color_tri_shader_program,
5735 // "TransformMatrix");
5736 // glUniformMatrix4fv(transloc, 1, GL_FALSE, (const GLfloat *)I);
5737
5738 // glUseProgram(texture_2D_shader_program);
5739 // matloc = glGetUniformLocation(texture_2D_shader_program, "MVMatrix");
5740 // glUniformMatrix4fv(matloc, 1, GL_FALSE,
5741 // (const GLfloat *)pvp->vp_transform);
5742 // transloc =
5743 // glGetUniformLocation(texture_2D_shader_program,
5744 // "TransformMatrix");
5745 // glUniformMatrix4fv(transloc, 1, GL_FALSE, (const GLfloat *)I);
5746
5747 shader = ptexture_2D_shader_program[GetCanvasIndex()];
5748 shader->Bind();
5749 shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)pvp->vp_matrix_transform);
5750 shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)I);
5751 shader->UnBind();
5752
5753 // glUseProgram(circle_filled_shader_program);
5754 // matloc = glGetUniformLocation(circle_filled_shader_program,
5755 // "MVMatrix"); glUniformMatrix4fv(matloc, 1, GL_FALSE,
5756 // (const GLfloat *)pvp->vp_transform);
5757 // transloc =
5758 // glGetUniformLocation(circle_filled_shader_program,
5759 // "TransformMatrix");
5760 // glUniformMatrix4fv(transloc, 1, GL_FALSE, (const GLfloat *)I);
5761
5762 shader = pcircle_filled_shader_program[GetCanvasIndex()];
5763 shader->Bind();
5764 shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)pvp->vp_matrix_transform);
5765 shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)I);
5766 shader->UnBind();
5767
5768 shader = ptexture_2DA_shader_program[GetCanvasIndex()];
5769 shader->Bind();
5770 shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)pvp->vp_matrix_transform);
5771 shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)I);
5772 shader->UnBind();
5773
5774 // glUseProgram(AALine_shader_program);
5775 // matloc = glGetUniformLocation(AALine_shader_program, "MVMatrix");
5776 // glUniformMatrix4fv(matloc, 1, GL_FALSE,
5777 // (const GLfloat *)pvp->vp_transform);
5778
5779 shader = pAALine_shader_program[GetCanvasIndex()];
5780 shader->Bind();
5781 shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)pvp->vp_matrix_transform);
5782 shader->UnBind();
5783
5784 shader = pring_shader_program[GetCanvasIndex()];
5785 shader->Bind();
5786 shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)pvp->vp_matrix_transform);
5787 shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)I);
5788 shader->UnBind();
5789
5790 // Leftover shader required by some older Android plugins
5791 if (texture_2DA_shader_program) {
5792 glUseProgram(texture_2DA_shader_program);
5793 GLint matloc = glGetUniformLocation(texture_2DA_shader_program, "MVMatrix");
5794 glUniformMatrix4fv(matloc, 1, GL_FALSE,
5795 (const GLfloat *)pvp->vp_matrix_transform);
5796 GLint transloc =
5797 glGetUniformLocation(texture_2DA_shader_program, "TransformMatrix");
5798 glUniformMatrix4fv(transloc, 1, GL_FALSE, (const GLfloat *)I);
5799 }
5800
5801 m_gldc.m_texfont.PrepareShader(vp.pix_width, vp.pix_height, vp.rotation);
5802
5803#endif
5804}
5805
5806void glChartCanvas::RenderTextures(ocpnDC &dc, float *coords, float *uvCoords,
5807 int nVertex, ViewPort *vp) {
5808// #ifdef USE_ANDROID_GLES2
5809#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
5810 int nl = nVertex / 4;
5811 float *lc = coords;
5812 float *luv = uvCoords;
5813
5814 while (nl) {
5815 RenderSingleTexture(dc, lc, luv, vp, 0, 0, 0);
5816
5817 lc += 8;
5818 luv += 8;
5819 nl--;
5820 }
5821
5822#else
5823 glEnableClientState(GL_VERTEX_ARRAY);
5824 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
5825
5826 glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), uvCoords);
5827 glVertexPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), coords);
5828 glDrawArrays(GL_QUADS, 0, 4);
5829
5830#endif
5831
5832 return;
5833}
5834
5835void glChartCanvas::RenderSingleTexture(ocpnDC &dc, float *coords,
5836 float *uvCoords, ViewPort *vp, float dx,
5837 float dy, float angle_rad) {
5838#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
5839
5840 GLShaderProgram *shader = ptexture_2D_shader_program[dc.m_canvasIndex];
5841 if (!shader) return;
5842
5843 shader->Bind();
5844
5845 // Set up the texture sampler to texture unit 0
5846 shader->SetUniform1i("uTex", 0);
5847
5848 // Rotate
5849 mat4x4 I, Q;
5850 mat4x4_identity(I);
5851 mat4x4_rotate_Z(Q, I, angle_rad);
5852
5853 // Translate
5854 Q[3][0] = dx;
5855 Q[3][1] = dy;
5856
5857 shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)Q);
5858
5859 float co1[8];
5860 float tco1[8];
5861
5862 shader->SetAttributePointerf("aPos", co1);
5863 shader->SetAttributePointerf("aUV", tco1);
5864
5865// Perform the actual drawing.
5866
5867// For some reason, glDrawElements is busted on Android
5868// So we do this a hard ugly way, drawing two triangles...
5869#if 0
5870 GLushort indices1[] = {0,1,3,2};
5871 glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, indices1);
5872#else
5873
5874 co1[0] = coords[0];
5875 co1[1] = coords[1];
5876 co1[2] = coords[2];
5877 co1[3] = coords[3];
5878 co1[4] = coords[6];
5879 co1[5] = coords[7];
5880 co1[6] = coords[4];
5881 co1[7] = coords[5];
5882
5883 tco1[0] = uvCoords[0];
5884 tco1[1] = uvCoords[1];
5885 tco1[2] = uvCoords[2];
5886 tco1[3] = uvCoords[3];
5887 tco1[4] = uvCoords[6];
5888 tco1[5] = uvCoords[7];
5889 tco1[6] = uvCoords[4];
5890 tco1[7] = uvCoords[5];
5891
5892 // glVertexAttribPointer(mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, co1);
5893 // glVertexAttribPointer(mUvAttrib, 2, GL_FLOAT, GL_FALSE, 0, tco1);
5894
5895 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
5896
5897 shader->UnBind();
5898
5899#endif
5900
5901#else
5902#endif
5903
5904 return;
5905}
5906
5907void glChartCanvas::RenderColorRect(wxRect r, wxColor &color) {
5908#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
5909
5910 GLShaderProgram *shader = pcolor_tri_shader_program[GetCanvasIndex()];
5911 shader->Bind();
5912
5913 shader->SetUniformMatrix4fv(
5914 "MVMatrix", (GLfloat *)m_pParentCanvas->GetpVP()->vp_matrix_transform);
5915
5916 float colorv[4];
5917 colorv[0] = color.Red() / float(256);
5918 colorv[1] = color.Green() / float(256);
5919 colorv[2] = color.Blue() / float(256);
5920 colorv[3] = 1.0;
5921 shader->SetUniform4fv("color", colorv);
5922
5923 float pf[8];
5924 pf[0] = r.x + r.width;
5925 pf[1] = r.y;
5926 pf[2] = r.x;
5927 pf[3] = r.y;
5928 pf[4] = r.x + r.width;
5929 pf[5] = r.y + r.height;
5930 pf[6] = r.x;
5931 pf[7] = r.y + r.height;
5932 shader->SetAttributePointerf("position", pf);
5933
5934 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
5935
5936 shader->UnBind();
5937
5938#else
5939#endif
5940}
5941
5942void glChartCanvas::RenderScene(bool bRenderCharts, bool bRenderOverlays) {
5943#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
5944
5945 ViewPort VPoint = m_pParentCanvas->VPoint;
5946 ocpnDC gldc(*this);
5947
5948 int w, h;
5949 GetClientSize(&w, &h);
5950 int sx = GetSize().x;
5951 int sy = GetSize().y;
5952
5953 OCPNRegion screen_region(wxRect(0, 0, VPoint.pix_width, VPoint.pix_height));
5954
5955 glViewport(0, 0, (GLint)w, (GLint)h);
5956
5957 if (s_b_useStencil) {
5958 glEnable(GL_STENCIL_TEST);
5959 glStencilMask(0xff);
5960 glClear(GL_STENCIL_BUFFER_BIT);
5961 glDisable(GL_STENCIL_TEST);
5962 }
5963
5964 // Make sure we have a valid quilt composition
5965 m_pParentCanvas->m_pQuilt->Compose(m_pParentCanvas->VPoint);
5966
5967 // set opengl settings that don't normally change
5968 // this should be able to go in SetupOpenGL, but it's
5969 // safer here incase a plugin mangles these
5970 glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
5971 glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
5972 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
5973
5974 // enable rendering to texture in framebuffer object
5975 glBindFramebuffer(GL_FRAMEBUFFER_EXT, m_fb0);
5976
5977 glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0,
5978 g_texture_rectangle_format, m_cache_tex[m_cache_page],
5979 0);
5980
5981 m_fbo_offsetx = 0;
5982 m_fbo_offsety = 0;
5983 m_fbo_swidth = sx;
5984 m_fbo_sheight = sy;
5985
5986 if (bRenderCharts) RenderCharts(gldc, screen_region);
5987
5988 if (bRenderOverlays) {
5989 RenderS57TextOverlay(m_pParentCanvas->VPoint);
5990 RenderMBTilesOverlay(m_pParentCanvas->VPoint);
5991 g_overlayCanvas = m_pParentCanvas;
5992
5993 if (g_pi_manager) {
5994 g_pi_manager->SendViewPortToRequestingPlugIns(VPoint);
5995 g_pi_manager->RenderAllGLCanvasOverlayPlugIns(
5996 m_pcontext, VPoint, m_pParentCanvas->m_canvasIndex, OVERLAY_CHARTS);
5997 }
5998
5999 DrawStaticRoutesTracksAndWaypoints(m_pParentCanvas->VPoint);
6000 DrawDynamicRoutesTracksAndWaypoints(VPoint);
6001 DrawFloatingOverlayObjects(m_gldc);
6002 }
6003
6004 // All done, so disable Render to FBO
6005 glBindFramebuffer(GL_FRAMEBUFFER, 0);
6006
6007#endif
6008}
6009
6010wxBitmap &glChartCanvas::GetTouchBackingBitmap(ViewPort &vp) {
6011 wxBitmap tbm(vp.pix_width, vp.pix_height, -1);
6012 wxMemoryDC tdc(tbm);
6013 tdc.SetBackground(wxBrush(GetGlobalColor("BLUEBACK")));
6014 tdc.Clear();
6015 ocpnDC dc = ocpnDC(tdc);
6016 ViewPort tvp = vp;
6017
6018 tvp.view_scale_ppm /= 2;
6019 tvp.SetBoxes();
6020
6021 gShapeBasemap.SetBasemapLandColor(GetGlobalColor("LANDBACK"));
6022 dc.SetPen(*wxTRANSPARENT_PEN);
6023
6024 gShapeBasemap.RenderViewOnDC(dc, tvp);
6025 tdc.SelectObject(wxNullBitmap);
6026 m_touch_backing_bitmap = tbm;
6027 CreateBackingTexture();
6028
6029 return m_touch_backing_bitmap;
6030}
6031
6032void glChartCanvas::CreateBackingTexture() {
6033 wxImage image = m_touch_backing_bitmap.ConvertToImage();
6034 unsigned char *imgdata = image.GetData();
6035 unsigned char *imgalpha = image.GetAlpha();
6036 m_tex_w = image.GetWidth();
6037 m_tex_h = image.GetHeight();
6038 m_image_width = m_tex_w;
6039 m_image_height = m_tex_h;
6040
6041 GLuint format = GL_RGBA;
6042 GLuint internalformat = g_texture_rectangle_format;
6043#ifndef __ANDROID__
6044 internalformat = GL_RGBA;
6045#endif
6046 int stride = 4;
6047
6048 if (imgdata) {
6049 unsigned char *teximage =
6050 (unsigned char *)malloc(stride * m_tex_w * m_tex_h);
6051
6052 for (int i = 0; i < m_image_height; i++) {
6053 for (int j = 0; j < m_image_width; j++) {
6054 int s = (i * 3 * m_image_width) + (j * 3);
6055 int d = (i * stride * m_tex_w) + (j * stride);
6056
6057 teximage[d + 0] = imgdata[s + 0];
6058 teximage[d + 1] = imgdata[s + 1];
6059 teximage[d + 2] = imgdata[s + 2];
6060 teximage[d + 3] = 255;
6061 }
6062 }
6063
6064 glGenTextures(1, &m_TouchBackingTexture);
6065 glBindTexture(GL_TEXTURE_2D, m_TouchBackingTexture);
6066
6067 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
6068 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
6069 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
6070 GL_NEAREST); // No mipmapping
6071 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
6072
6073 glTexImage2D(GL_TEXTURE_2D, 0, internalformat, m_tex_w, m_tex_h, 0, format,
6074 GL_UNSIGNED_BYTE, teximage);
6075
6076 free(teximage);
6077 glBindTexture(GL_TEXTURE_2D, 0);
6078 }
6079}
BasePlatform * g_BasePlatform
points to g_platform, handles brain-dead MS linker.
Basic platform specific support utilities without GUI deps.
bool GetMemoryStatus(int *mem_total, int *mem_used)
Return total system RAM and size of program Values returned are in kilobytes.
Chart info panel.
Wrapper for creating a ChartCtx based on global vars.
General chart base definitions.
ChartDB * ChartData
Global instance.
Definition chartdb.cpp:71
Charts database management
BSB chart management.
ChartCanvas * g_focusCanvas
Global instance.
Definition chcanv.cpp:1315
ChartCanvas * g_overlayCanvas
Global instance.
Definition chcanv.cpp:1314
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:126
ChartCanvas - Main chart display and interaction component.
Definition chcanv.h:173
float GetVPChartScale()
Return the ViewPort chart scale denominator (e.g., 50000 for a 1:50000 scale).
Definition chcanv.h:502
bool GetCanvasPointPix(double rlat, double rlon, wxPoint *r)
Convert latitude/longitude to canvas pixel coordinates (physical pixels) rounded to nearest integer.
Definition chcanv.cpp:4547
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:4497
double GetCanvasScaleFactor()
Return the number of logical pixels per meter for the screen.
Definition chcanv.h:511
double GetPixPerMM()
Get the number of logical pixels per millimeter on the screen.
Definition chcanv.h:542
bool MouseEventSetup(wxMouseEvent &event, bool b_handle_dclick=true)
Definition chcanv.cpp:7808
bool PanCanvas(double dx, double dy)
Pans (moves) the canvas by the specified physical pixels in x and y directions.
Definition chcanv.cpp:5079
void ZoomCanvasSimple(double factor)
Perform an immediate zoom operation without smooth transitions.
Definition chcanv.cpp:4628
bool SetVPScale(double sc, bool b_refresh=true)
Sets the viewport scale while maintaining the center point.
Definition chcanv.cpp:5359
float GetVPScale() override
Return ViewPort scale factor, in physical pixels per meter.
Definition chcanv.h:183
void GetCanvasPixPoint(double x, double y, double &lat, double &lon)
Convert canvas pixel coordinates (physical pixels) to latitude/longitude.
Definition chcanv.cpp:4572
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:4634
void GetDoubleCanvasPointPix(double rlat, double rlon, wxPoint2DDouble *r)
Convert latitude/longitude to canvas pixel coordinates (physical pixels) with double precision.
Definition chcanv.cpp:4492
bool MouseEventProcessCanvas(wxMouseEvent &event)
Processes mouse events for core chart panning and zooming operations.
Definition chcanv.cpp:10244
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:1779
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:809
double view_scale_ppm
Requested view scale in physical pixels per meter (ppm), before applying projections.
Definition viewport.h:204
double ref_scale
The nominal scale of the "reference chart" for this view.
Definition viewport.h:221
int pix_height
Height of the viewport in physical pixels.
Definition viewport.h:233
double rotation
Rotation angle of the viewport in radians.
Definition viewport.h:214
void SetPixelScale(double scale)
Set the physical to logical pixel ratio for the display.
Definition viewport.cpp:121
int pix_width
Width of the viewport in physical pixels.
Definition viewport.h:231
wxPoint2DDouble GetDoublePixFromLL(double lat, double lon)
Convert latitude and longitude on the ViewPort to physical pixel coordinates with double precision.
Definition viewport.cpp:133
double tilt
Tilt angle for perspective view in radians.
Definition viewport.h:216
double skew
Angular distortion (shear transform) applied to the viewport in radians.
Definition viewport.h:212
void GetLLFromPix(const wxPoint &p, double *lat, double *lon)
Convert physical pixel coordinates on the ViewPort to latitude and longitude.
Definition viewport.h:80
double clon
Center longitude of the viewport in degrees.
Definition viewport.h:199
double clat
Center latitude of the viewport in degrees.
Definition viewport.h:197
wxPoint GetPixFromLL(double lat, double lon)
Convert latitude and longitude on the ViewPort to physical pixel coordinates.
Definition viewport.cpp:124
double chart_scale
Chart scale denominator (e.g., 50000 for a 1:50000 scale).
Definition viewport.h:219
Represents a composite CM93 chart covering multiple scales.
Definition cm93.h:416
Stores emboss effect data for textures.
Definition emboss_data.h:34
OpenGL chart rendering canvas.
Device context class that can use either wxDC or OpenGL for drawing.
Definition ocpndc.h:60
void DrawLine(wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2, bool b_hiqual=true)
Draw a line between two points using either wxDC or OpenGL.
Definition ocpndc.cpp:473
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:87
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:187
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:66
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.