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