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