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