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