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