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