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