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