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