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