OpenCPN Partial API docs
Loading...
Searching...
No Matches
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 "ch_info_win.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;
231extern BasePlatform *g_BasePlatform;
232extern bool g_PrintingInProgress;
233extern bool g_bhide_depth_units;
234extern bool g_bhide_overzoom_flag;
235
236wxColor s_regionColor;
237extern ShapeBaseChartSet gShapeBasemap;
238
240
241// For VBO(s)
242bool g_b_EnableVBO;
243bool g_b_needFinish; // Need glFinish() call on each frame?
244
245// MacOS has some missing parts:
246#ifndef APIENTRY
247#define APIENTRY
248#endif
249#ifndef APIENTRYP
250#define APIENTRYP APIENTRY *
251#endif
252#ifndef GLAPI
253#define GLAPI extern
254#endif
255
256#ifndef GL_COMPRESSED_RGB_FXT1_3DFX
257#define GL_COMPRESSED_RGB_FXT1_3DFX 0x86B0
258#endif
259
260PFNGLGENFRAMEBUFFERSEXTPROC s_glGenFramebuffers;
261PFNGLGENRENDERBUFFERSEXTPROC s_glGenRenderbuffers;
262PFNGLFRAMEBUFFERTEXTURE2DEXTPROC s_glFramebufferTexture2D;
263PFNGLBINDFRAMEBUFFEREXTPROC s_glBindFramebuffer;
264PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC s_glFramebufferRenderbuffer;
265PFNGLRENDERBUFFERSTORAGEEXTPROC s_glRenderbufferStorage;
266PFNGLBINDRENDERBUFFEREXTPROC s_glBindRenderbuffer;
267PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC s_glCheckFramebufferStatus;
268PFNGLDELETEFRAMEBUFFERSEXTPROC s_glDeleteFramebuffers;
269PFNGLDELETERENDERBUFFERSEXTPROC s_glDeleteRenderbuffers;
270
271PFNGLCOMPRESSEDTEXIMAGE2DPROC s_glCompressedTexImage2D;
272PFNGLGETCOMPRESSEDTEXIMAGEPROC s_glGetCompressedTexImage;
273
274// Vertex Buffer Object (VBO) support
275PFNGLGENBUFFERSPROC s_glGenBuffers;
276PFNGLBINDBUFFERPROC s_glBindBuffer;
277PFNGLBUFFERDATAPROC s_glBufferData;
278PFNGLDELETEBUFFERSPROC s_glDeleteBuffers;
279
280#ifndef USE_ANDROID_GLES2
281// #define glDeleteFramebuffers(a, b) (s_glDeleteFramebuffers)(a, b);
282// #define glDeleteRenderbuffers(a, b) (s_glDeleteRenderbuffers)(a, b);
283#endif
284
285typedef void(APIENTRYP PFNGLGETBUFFERPARAMETERIV)(GLenum target, GLenum value,
286 GLint *data);
287PFNGLGETBUFFERPARAMETERIV s_glGetBufferParameteriv;
288
289#include <wx/arrimpl.cpp>
290// WX_DEFINE_OBJARRAY( ArrayOfTexDescriptors );
291
292GLuint g_raster_format = GL_RGB;
293long g_tex_mem_used;
294
295bool b_timeGL;
296wxStopWatch g_glstopwatch;
297double g_gl_ms_per_frame;
298
299int g_tile_size;
300int g_uncompressed_tile_size;
301
302extern wxProgressDialog *pprog;
303extern bool b_skipout;
304extern wxSize pprog_size;
305extern int pprog_count;
306extern int pprog_threads;
307
308// #if defined(__MSVC__) && !defined(ocpnUSE_GLES) /* this compiler doesn't
309// support vla */ const #endif extern int g_mipmap_max_level;
310int panx, pany;
311
312bool glChartCanvas::s_b_useScissorTest;
313bool glChartCanvas::s_b_useStencil;
314bool glChartCanvas::s_b_useStencilAP;
315bool glChartCanvas::s_b_useFBO;
316
317#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
318static int s_tess_vertex_idx;
319static int s_tess_vertex_idx_this;
320static int s_tess_buf_len;
321static GLfloat *s_tess_work_buf;
322GLenum s_tess_mode;
323static int s_nvertex;
324static vec4 s_tess_color;
325ViewPort s_tessVP;
326static ocpnDC *s_pdc;
327#endif
328
329#if 0
330/* for debugging */
331static void print_region(OCPNRegion &Region)
332{
333 OCPNRegionIterator upd ( Region );
334 while ( upd.HaveRects() )
335 {
336 wxRect rect = upd.GetRect();
337 printf("[(%d, %d) (%d, %d)] ", rect.x, rect.y, rect.width, rect.height);
338 upd.NextRect();
339 }
340}
341
342#endif
343
344GLboolean QueryExtension(const char *extName) {
345 /*
346 ** Search for extName in the extensions string. Use of strstr()
347 ** is not sufficient because extension names can be prefixes of
348 ** other extension names. Could use strtok() but the constant
349 ** string returned by glGetString might be in read-only memory.
350 */
351 char *p;
352 char *end;
353 int extNameLen;
354
355 extNameLen = strlen(extName);
356
357 p = (char *)glGetString(GL_EXTENSIONS);
358 if (NULL == p) {
359 return GL_FALSE;
360 }
361
362 end = p + strlen(p);
363
364 while (p < end) {
365 int n = strcspn(p, " ");
366 if ((extNameLen == n) && (strncmp(extName, p, n) == 0)) {
367 return GL_TRUE;
368 }
369 p += (n + 1);
370 }
371 return GL_FALSE;
372}
373
374int test_attribs[] = {WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE,
375 16, WX_GL_STENCIL_SIZE, 8,
376 0};
377
378glTestCanvas::glTestCanvas(wxWindow *parent)
379 : wxGLCanvas(parent, wxID_ANY, test_attribs, wxDefaultPosition,
380 wxSize(2, 2)) {}
381
382// This attribute set works OK with vesa software only OpenGL renderer
383int attribs[] = {WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE,
384 16, WX_GL_STENCIL_SIZE, 8,
385 0};
386BEGIN_EVENT_TABLE(glChartCanvas, wxGLCanvas)
387EVT_PAINT(glChartCanvas::OnPaint)
388EVT_ACTIVATE(glChartCanvas::OnActivate)
389EVT_SIZE(glChartCanvas::OnSize)
390EVT_MOUSE_EVENTS(glChartCanvas::MouseEvent)
391END_EVENT_TABLE()
392
393glChartCanvas::glChartCanvas(wxWindow *parent, wxGLCanvas *share)
394 : wxGLCanvas(parent, wxID_ANY, attribs, wxDefaultPosition, wxSize(256, 256),
395 wxFULL_REPAINT_ON_RESIZE | wxBG_STYLE_CUSTOM, "")
396
397{
398 m_pParentCanvas = dynamic_cast<ChartCanvas *>(parent);
399
400 Init();
401}
402
403std::unordered_map<wxPenStyle, std::array<wxDash, 2>> glChartCanvas::dash_map =
404 {
405 {wxPENSTYLE_DOT, {1, 1}},
406 {wxPENSTYLE_LONG_DASH, {5, 5}},
407 {wxPENSTYLE_SHORT_DASH, {1, 5}},
408 {wxPENSTYLE_DOT_DASH, {5, 1}},
409};
410
411void glChartCanvas::Init() {
412 m_bsetup = false;
413
414 // m_pParentCanvas = dynamic_cast<ChartCanvas *>( GetParent() );
415
416 SetBackgroundStyle(wxBG_STYLE_CUSTOM); // on WXMSW, this prevents flashing
417
418 m_cache_current_ch = NULL;
419
420 m_b_paint_enable = true;
421 m_in_glpaint = false;
422
423 m_cache_tex[0] = m_cache_tex[1] = 0;
424 m_cache_page = 0;
425
426 m_b_BuiltFBO = false;
427 m_b_DisableFBO = false;
428
429 ownship_tex = 0;
430 ownship_color = -1;
431
432 m_piano_tex = 0;
433
434 m_binPinch = false;
435 m_binPan = false;
436 m_bpinchGuard = false;
437 m_binGesture = false;
438
439 b_timeGL = true;
440 m_last_render_time = -1;
441
442 m_LRUtime = 0;
443
444 m_tideTex = 0;
445 m_currentTex = 0;
446
447 m_gldc.SetGLCanvas(this);
448 m_gldc.SetDPIFactor(g_BasePlatform->GetDisplayDIPMult(GetParent()));
449
450 m_displayScale = 1.0;
451#if defined(__WXOSX__) || defined(__WXGTK3__)
452 // Support scaled HDPI displays.
453 m_displayScale = GetContentScaleFactor();
454#endif
455 m_pParentCanvas->VPoint.SetPixelScale(m_displayScale);
456
457#ifdef __ANDROID__
458 // Create/connect a dynamic event handler slot for gesture and some timer
459 // events
460 Connect(
461 wxEVT_QT_PANGESTURE,
462 (wxObjectEventFunction)(wxEventFunction)&glChartCanvas::OnEvtPanGesture,
463 NULL, this);
464
465 Connect(
466 wxEVT_QT_PINCHGESTURE,
467 (wxObjectEventFunction)(wxEventFunction)&glChartCanvas::OnEvtPinchGesture,
468 NULL, this);
469
470 Connect(GESTURE_EVENT_TIMER, wxEVT_TIMER,
471 (wxObjectEventFunction)(wxEventFunction)&glChartCanvas::
472 onGestureTimerEvent,
473 NULL, this);
474
475 Connect(GESTURE_FINISH_TIMER, wxEVT_TIMER,
476 (wxObjectEventFunction)(wxEventFunction)&glChartCanvas::
477 onGestureFinishTimerEvent,
478 NULL, this);
479
480 Connect(
481 ZOOM_TIMER, wxEVT_TIMER,
482 (wxObjectEventFunction)(wxEventFunction)&glChartCanvas::onZoomTimerEvent,
483 NULL, this);
484
485 m_gestureEeventTimer.SetOwner(this, GESTURE_EVENT_TIMER);
486 m_gestureFinishTimer.SetOwner(this, GESTURE_FINISH_TIMER);
487 zoomTimer.SetOwner(this, ZOOM_TIMER);
488
489#ifdef USE_ANDROID_GLES2
490// Connect(
491// TEX_FADE_TIMER, wxEVT_TIMER,
492// (wxObjectEventFunction)(wxEventFunction)&glChartCanvas::onFadeTimerEvent,
493// NULL, this);
494// m_fadeTimer.SetOwner(this, TEX_FADE_TIMER);
495#endif
496
497#else
498#ifdef HAVE_WX_GESTURE_EVENTS
499
500 Connect(GESTURE_EVENT_TIMER, wxEVT_TIMER,
501 (wxObjectEventFunction)(wxEventFunction)&glChartCanvas::
502 onGestureTimerEvent,
503 NULL, this);
504
505 Connect(GESTURE_FINISH_TIMER, wxEVT_TIMER,
506 (wxObjectEventFunction)(wxEventFunction)&glChartCanvas::
507 onGestureFinishTimerEvent,
508 NULL, this);
509
510 Connect(
511 ZOOM_TIMER, wxEVT_TIMER,
512 (wxObjectEventFunction)(wxEventFunction)&glChartCanvas::onZoomTimerEvent,
513 NULL, this);
514
515 m_gestureEeventTimer.SetOwner(this, GESTURE_EVENT_TIMER);
516 m_gestureFinishTimer.SetOwner(this, GESTURE_FINISH_TIMER);
517 zoomTimer.SetOwner(this, ZOOM_TIMER);
518 m_zoom_inc = 1.0;
519#endif
520#endif
521
522 m_bgestureGuard = false;
523 m_total_zoom_val = 1.0;
524
525// Gesture support for platforms other than Android
526#ifdef HAVE_WX_GESTURE_EVENTS
527 if (!EnableTouchEvents(wxTOUCH_ZOOM_GESTURE | wxTOUCH_PRESS_GESTURES)) {
528 wxLogError("Failed to enable touch events");
529 }
530
531 // Bind(wxEVT_GESTURE_PAN, &glChartCanvas::OnEvtPanGesture, this);
532 // Connect(
533 // wxEVT_GESTURE_PAN,
534 // (wxObjectEventFunction)(wxEventFunction)&glChartCanvas::OnEvtPanGesture,
535 // NULL, this);
536
537 Bind(wxEVT_GESTURE_ZOOM, &glChartCanvas::OnEvtZoomGesture, this);
538
539 Bind(wxEVT_LONG_PRESS, &ChartCanvas::OnLongPress, m_pParentCanvas);
540 Bind(wxEVT_PRESS_AND_TAP, &ChartCanvas::OnPressAndTap, m_pParentCanvas);
541
542 Bind(wxEVT_RIGHT_UP, &ChartCanvas::OnRightUp, m_pParentCanvas);
543 Bind(wxEVT_RIGHT_DOWN, &ChartCanvas::OnRightDown, m_pParentCanvas);
544
545 Bind(wxEVT_LEFT_UP, &ChartCanvas::OnLeftUp, m_pParentCanvas);
546 Bind(wxEVT_LEFT_DOWN, &ChartCanvas::OnLeftDown, m_pParentCanvas);
547
548 Bind(wxEVT_MOUSEWHEEL, &ChartCanvas::OnWheel, m_pParentCanvas);
549 Bind(wxEVT_MOTION, &ChartCanvas::OnMotion, m_pParentCanvas);
550#endif /* HAVE_WX_GESTURE_EVENTS */
551
552 if (!g_glTextureManager) g_glTextureManager = new glTextureManager;
553}
554
555glChartCanvas::~glChartCanvas() {
556#ifdef __ANDROID__
557 unloadShaders();
558#endif
559}
560
561void glChartCanvas::FlushFBO(void) {
562 if (m_bsetup) BuildFBO();
563}
564
565void glChartCanvas::OnActivate(wxActivateEvent &event) {
566 m_pParentCanvas->OnActivate(event);
567}
568
569void glChartCanvas::OnSize(wxSizeEvent &event) {
570#if 0
571#ifdef __ANDROID__
572 if(!g_running){
573 wxLogMessage("Got OnSize event while NOT running");
574 event.Skip();
575 qDebug() << "OnSizeB";
576
577 return;
578 }
579#endif
580#endif
581
582 if (!IsShown()) return;
583
584 SetCurrent(*m_pcontext);
585
586 if (!g_bopengl) {
587 // Invoked immediately after user has disabled OpenGL.
588 SetSize(GetSize().x, GetSize().y);
589 event.Skip();
590 return;
591 }
592
593 // this is also necessary to update the context on some platforms
594 // OnSize can be called with a different OpenGL context (when a plugin uses a
595 // different GL context).
596 if (m_bsetup && m_pcontext && IsShown()) {
597 SetCurrent(*m_pcontext);
598 }
599
600 // SetSize(m_pParentCanvas->GetClientSize());
601
602 if (m_bsetup) {
603 wxLogMessage("BuildFBO 3");
604 BuildFBO();
605 }
606
607 // Set the shader viewport transform matrix
608 ViewPort *vp = m_pParentCanvas->GetpVP();
609 mat4x4 m;
610 mat4x4_identity(m);
611 mat4x4_scale_aniso((float(*)[4])vp->vp_matrix_transform, m,
612 2.0 / (float)vp->pix_width, -2.0 / (float)vp->pix_height,
613 1.0);
614 mat4x4_translate_in_place((float(*)[4])vp->vp_matrix_transform,
615 -vp->pix_width / 2, -vp->pix_height / 2, 0);
616}
617
618void glChartCanvas::MouseEvent(wxMouseEvent &event) {
619 if (m_pParentCanvas->MouseEventOverlayWindows(event)) return;
620
621#ifndef __ANDROID__
622 if (m_pParentCanvas->MouseEventSetup(event))
623 return; // handled, no further action required
624
625 bool obj_proc = m_pParentCanvas->MouseEventProcessObjects(event);
626
627 if (!obj_proc && !m_pParentCanvas->singleClickEventIsValid)
628 m_pParentCanvas->MouseEventProcessCanvas(event);
629
630 if (!g_btouch) m_pParentCanvas->SetCanvasCursor(event);
631
632#else
633
634 if (m_bgestureGuard) {
635 m_pParentCanvas->r_rband.x = 0; // turn off rubberband temporarily
636
637 // Sometimes we get a Gesture Pan start on a simple tap operation.
638 // When this happens, we usually get no Gesture Finished event.
639 // So, we need to process the next LeftUp event normally, to handle things
640 // like Measure and Route Create.
641
642 // Allow LeftUp() event through if the pan action is very small
643 // Otherwise, drop the LeftUp() event, since it is not wanted for a Pan
644 // Gesture.
645 if (event.LeftUp()) {
646 // qDebug() << panx << pany;
647 if ((abs(panx) > 2) || (abs(pany) > 2)) {
648 return;
649 } else { // Cancel the in=process Gesture state
650 m_gestureEeventTimer.Start(10, wxTIMER_ONE_SHOT); // Short Circuit
651 }
652 } else
653 return;
654 }
655
656 if (m_pParentCanvas->MouseEventSetup(event, false)) {
657 if (!event.LeftDClick()) {
658 return; // handled, no further action required
659 }
660 }
661
662 if (m_binPan && event.RightDown()) {
663 qDebug() << "Skip right on pan";
664 return;
665 } else {
666 bool obj_proc = m_pParentCanvas->MouseEventProcessObjects(event);
667
668 if (!obj_proc && !m_pParentCanvas->singleClickEventIsValid) {
669 if (!m_bgestureGuard)
670 m_pParentCanvas->MouseEventProcessCanvas(
671 event); // This is where a physical mouse gets processed, if
672 // detected
673 }
674 }
675
676#endif
677}
678
679#ifndef GL_MAX_RENDERBUFFER_SIZE
680#define GL_MAX_RENDERBUFFER_SIZE 0x84E8
681#endif
682
683#ifndef USE_ANDROID_GLES2
684bool glChartCanvas::buildFBOSize(int fboSize) {
685 bool retVal = true;
686 if (IsShown()) SetCurrent(*m_pcontext);
687
688 if (m_b_BuiltFBO) {
689 glDeleteTextures(2, m_cache_tex);
690 glDeleteFramebuffers(1, &m_fb0);
691 glDeleteRenderbuffers(1, &m_renderbuffer);
692 m_b_BuiltFBO = false;
693 }
694
695 if (m_b_DisableFBO) return false;
696
697#ifdef __ANDROID__
698 // We use the smallest possible (POT) FBO
699 int rb_x = GetSize().x;
700 int rb_y = GetSize().y;
701 int i = 1;
702 while (i < rb_x) i <<= 1;
703 rb_x = i;
704
705 i = 1;
706 while (i < rb_y) i <<= 1;
707 rb_y = i;
708
709 m_cache_tex_x = wxMax(rb_x, rb_y);
710 m_cache_tex_y = wxMax(rb_x, rb_y);
711
712#else
713 m_cache_tex_x = GetSize().x * m_displayScale;
714 m_cache_tex_y = GetSize().y * m_displayScale;
715#endif
716
717 int err = GL_NO_ERROR;
718 GLint params;
719 glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &params);
720
721 err = glGetError();
722 if (err == GL_INVALID_ENUM) {
723 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &params);
724 err = glGetError();
725 }
726
727 if (err == GL_NO_ERROR) {
728 if (fboSize > params) {
729 wxLogMessage(
730 " OpenGL-> Requested Framebuffer size exceeds "
731 "GL_MAX_RENDERBUFFER_SIZE");
732 return false;
733 }
734 }
735
736 glGenFramebuffers(1, &m_fb0);
737 err = glGetError();
738 if (err) {
739 wxString msg;
740 msg.Printf(" OpenGL-> Framebuffer GenFramebuffers error: %08X", 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(" OpenGL-> Framebuffer GenRenderbuffers error: %08X", err);
750 wxLogMessage(msg);
751 retVal = false;
752 }
753
754 glBindFramebuffer(GL_FRAMEBUFFER_EXT, m_fb0);
755 err = glGetError();
756 if (err) {
757 wxString msg;
758 msg.Printf(" OpenGL-> Framebuffer BindFramebuffers error: %08X", err);
759 wxLogMessage(msg);
760 retVal = false;
761 }
762
763 // initialize color textures
764 glGenTextures(2, m_cache_tex);
765 for (int i = 0; i < 2; i++) {
766 glBindTexture(g_texture_rectangle_format, m_cache_tex[i]);
767 glTexParameterf(g_texture_rectangle_format, GL_TEXTURE_MIN_FILTER,
768 GL_NEAREST);
769 glTexParameteri(g_texture_rectangle_format, GL_TEXTURE_MAG_FILTER,
770 GL_NEAREST);
771 glTexImage2D(g_texture_rectangle_format, 0, GL_RGBA, m_cache_tex_x,
772 m_cache_tex_y, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
773 }
774
775 glBindRenderbuffer(GL_RENDERBUFFER_EXT, m_renderbuffer);
776
777 if (m_b_useFBOStencil) {
778 // initialize composite depth/stencil renderbuffer
779 glRenderbufferStorage(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT,
780 m_cache_tex_x, m_cache_tex_y);
781
782 int err = glGetError();
783 if (err) {
784 wxString msg;
785 msg.Printf(" OpenGL-> glRenderbufferStorage error: %08X", err);
786 wxLogMessage(msg);
787 }
788
789 glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
790 GL_RENDERBUFFER_EXT, m_renderbuffer);
791 err = glGetError();
792 if (err) {
793 wxString msg;
794 msg.Printf(" OpenGL-> glFramebufferRenderbuffer depth error: %08X",
795 err);
796 wxLogMessage(msg);
797 }
798
799 glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT,
800 GL_RENDERBUFFER_EXT, m_renderbuffer);
801 err = glGetError();
802 if (err) {
803 wxString msg;
804 msg.Printf(" OpenGL-> glFramebufferRenderbuffer stencil error: %08X",
805 err);
806 wxLogMessage(msg);
807 }
808
809 } else {
810 GLenum depth_format = GL_DEPTH_COMPONENT24;
811
812 // Need to check for availability of 24 bit depth buffer extension on
813 // GLES
814#ifdef ocpnUSE_GLES
815 if (!QueryExtension("GL_OES_depth24")) depth_format = GL_DEPTH_COMPONENT16;
816#endif
817
818 // initialize depth renderbuffer
819 glRenderbufferStorage(GL_RENDERBUFFER_EXT, depth_format, m_cache_tex_x,
820 m_cache_tex_y);
821 int err = glGetError();
822 if (err) {
823 wxString msg;
824 msg.Printf(" OpenGL-> Framebuffer Depth Buffer Storage error: %08X",
825 err);
826 wxLogMessage(msg);
827 retVal = false;
828 }
829
830 glFramebufferRenderbuffer(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
831 GL_RENDERBUFFER_EXT, m_renderbuffer);
832
833 err = glGetError();
834 if (err) {
835 wxString msg;
836 msg.Printf(" OpenGL-> Framebuffer Depth Buffer Attach error: %08X",
837 err);
838 wxLogMessage(msg);
839 retVal = false;
840 }
841 }
842
843 glBindTexture(GL_TEXTURE_2D, 0);
844 glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
845
846 // Check framebuffer completeness at the end of initialization.
847 glBindFramebuffer(GL_FRAMEBUFFER_EXT, m_fb0);
848
849 glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
850 g_texture_rectangle_format, m_cache_tex[0], 0);
851
852 GLenum fb_status = glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT);
853
854 glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
855
856 if (fb_status != GL_FRAMEBUFFER_COMPLETE_EXT) {
857 wxString msg;
858 msg.Printf(" OpenGL-> buildFBOSize->Framebuffer Incomplete: %08X",
859 fb_status);
860 wxLogMessage(msg);
861 retVal = false;
862 }
863
864 return retVal;
865}
866#endif
867
868#ifdef USE_ANDROID_GLES2
869bool glChartCanvas::buildFBOSize(int fboSize) {
870 bool retVal = true;
871
872 // We use the smallest possible (POT) FBO
873 int rb_x = GetSize().x;
874 int rb_y = GetSize().y;
875 int i = 1;
876 while (i < rb_x) i <<= 1;
877 rb_x = i;
878
879 i = 1;
880 while (i < rb_y) i <<= 1;
881 rb_y = i;
882
883 m_cache_tex_x = wxMax(rb_x, rb_y);
884 m_cache_tex_y = wxMax(rb_x, rb_y);
885
886 // qDebug() << "FBO Size: " << GetSize().x << GetSize().y << m_cache_tex_x;
887
888 int err = GL_NO_ERROR;
889 GLint params;
890 glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &params);
891
892 err = glGetError();
893 if (err == GL_INVALID_ENUM) {
894 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &params);
895 err = glGetError();
896 }
897
898 if (err == GL_NO_ERROR) {
899 if (fboSize > params) {
900 wxLogMessage(
901 " OpenGL-> Requested Framebuffer size exceeds "
902 "GL_MAX_RENDERBUFFER_SIZE");
903 return false;
904 }
905 }
906
907 glGenFramebuffers(1, &m_fb0);
908 err = glGetError();
909 if (err) {
910 wxString msg;
911 msg.Printf(" OpenGL-> Framebuffer GenFramebuffers error: %08X", err);
912 wxLogMessage(msg);
913 retVal = false;
914 }
915
916 glGenRenderbuffers(1, &m_renderbuffer);
917 err = glGetError();
918 if (err) {
919 wxString msg;
920 msg.Printf(" OpenGL-> Framebuffer GenRenderbuffers error: %08X", err);
921 wxLogMessage(msg);
922 retVal = false;
923 }
924
925 glBindFramebuffer(GL_FRAMEBUFFER, m_fb0);
926 err = glGetError();
927 if (err) {
928 wxString msg;
929 msg.Printf(" OpenGL-> Framebuffer BindFramebuffers error: %08X", err);
930 wxLogMessage(msg);
931 retVal = false;
932 }
933
934 // initialize color textures
935 glGenTextures(2, m_cache_tex);
936 for (int i = 0; i < 2; i++) {
937 glBindTexture(g_texture_rectangle_format, m_cache_tex[i]);
938 glTexParameterf(g_texture_rectangle_format, GL_TEXTURE_MIN_FILTER,
939 GL_NEAREST);
940 glTexParameteri(g_texture_rectangle_format, GL_TEXTURE_MAG_FILTER,
941 GL_NEAREST);
942 glTexImage2D(g_texture_rectangle_format, 0, GL_RGBA, m_cache_tex_x,
943 m_cache_tex_y, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
944 }
945
946 glBindRenderbuffer(GL_RENDERBUFFER, m_renderbuffer);
947
948 // initialize composite depth/stencil renderbuffer
949 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, m_cache_tex_x,
950 m_cache_tex_y);
951
952 err = glGetError();
953 if (err) {
954 wxString msg;
955 msg.Printf(" OpenGL-> glRenderbufferStorage error: %08X", err);
956 wxLogMessage(msg);
957 }
958
959 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
960 GL_RENDERBUFFER, m_renderbuffer);
961 err = glGetError();
962 if (err) {
963 wxString msg;
964 msg.Printf(" OpenGL-> glFramebufferRenderbuffer depth error: %08X",
965 err);
966 wxLogMessage(msg);
967 }
968
969 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
970 GL_RENDERBUFFER, m_renderbuffer);
971 err = glGetError();
972 if (err) {
973 wxString msg;
974 msg.Printf(" OpenGL-> glFramebufferRenderbuffer stencil error: %08X",
975 err);
976 wxLogMessage(msg);
977 }
978
979 glBindTexture(GL_TEXTURE_2D, 0);
980 glBindFramebuffer(GL_FRAMEBUFFER, 0);
981
982 // Check framebuffer completeness at the end of initialization.
983 glBindFramebuffer(GL_FRAMEBUFFER, m_fb0);
984
985 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
986 g_texture_rectangle_format, m_cache_tex[0], 0);
987
988 GLenum fb_status = glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT);
989
990 glBindFramebuffer(GL_FRAMEBUFFER, 0);
991
992 if (fb_status != GL_FRAMEBUFFER_COMPLETE) {
993 wxString msg;
994 msg.Printf(" OpenGL-> buildFBOSize->Framebuffer Incomplete: %08X %08X",
995 fb_status);
996 wxLogMessage(msg);
997 retVal = false;
998 }
999
1000 return retVal;
1001}
1002#endif
1003
1004void glChartCanvas::BuildFBO() {
1005 if (m_b_BuiltFBO) {
1006 // return;
1007 glDeleteTextures(2, m_cache_tex);
1008 glDeleteFramebuffers(1, &m_fb0);
1009 glDeleteRenderbuffers(1, &m_renderbuffer);
1010 m_b_BuiltFBO = false;
1011 }
1012
1013 if (m_b_DisableFBO) return;
1014
1015 // int initialSize = 2048;
1016 int gl_width, gl_height;
1017 m_pParentCanvas->GetClientSize(&gl_width, &gl_height);
1018 int initialSize = NextPow2(gl_width * m_displayScale);
1019
1020#ifdef __ANDROID__
1021 // Some low mem-spec devices have trouble with 2048 FBO size.
1022 // Detect here, and choose 1024 size instead
1023 wxString info = androidGetDeviceInfo();
1024
1025 if (wxNOT_FOUND != info.Find("GT-S6312")) initialSize = 1024;
1026#endif
1027
1028 if (!buildFBOSize(initialSize)) {
1029 glDeleteTextures(2, m_cache_tex);
1030 glDeleteFramebuffers(1, &m_fb0);
1031 glDeleteRenderbuffers(1, &m_renderbuffer);
1032
1033 if (!buildFBOSize(1024)) {
1034 wxLogMessage("BuildFBO C");
1035
1036 m_b_DisableFBO = true;
1037 wxLogMessage("OpenGL-> FBO Framebuffer unavailable");
1038 m_b_BuiltFBO = false;
1039
1040 return;
1041 }
1042 }
1043
1044 // All OK
1045
1046 wxString msg;
1047 msg.Printf("OpenGL-> Framebuffer OK, size = %d", m_cache_tex_x);
1048 wxLogMessage(msg);
1049
1050 /* invalidate cache */
1051 Invalidate();
1052
1053 // glClear(GL_COLOR_BUFFER_BIT);
1054 m_b_BuiltFBO = true;
1055
1056 return;
1057}
1058
1059void glChartCanvas::SetupOpenGL() {
1060 SetCurrent(*m_pcontext);
1061
1062 char *str = (char *)glGetString(GL_RENDERER);
1063 if (str == NULL) {
1064 // perhaps we should edit the config and turn off opengl now
1065 wxLogMessage("Failed to initialize OpenGL");
1066 exit(1);
1067 }
1068
1069 char render_string[80];
1070 strncpy(render_string, str, 79);
1071 m_renderer = wxString(render_string, wxConvUTF8);
1072
1073 wxString msg;
1074 if (g_bSoftwareGL) msg.Printf("OpenGL-> Software OpenGL");
1075 msg.Printf("OpenGL-> Renderer String: ");
1076 msg += m_renderer;
1077 wxLogMessage(msg);
1078
1079 if (ps52plib) ps52plib->SetGLRendererString(m_renderer);
1080
1081 char version_string[80];
1082 strncpy(version_string, (char *)glGetString(GL_VERSION), 79);
1083 msg.Printf("OpenGL-> Version reported: ");
1084 m_version = wxString(version_string, wxConvUTF8);
1085 msg += m_version;
1086 wxLogMessage(msg);
1087
1088 char GLSL_version_string[80];
1089 strncpy(GLSL_version_string, (char *)glGetString(GL_SHADING_LANGUAGE_VERSION),
1090 79);
1091 msg.Printf("OpenGL-> GLSL Version reported: ");
1092 m_GLSLversion = wxString(GLSL_version_string, wxConvUTF8);
1093 msg += m_GLSLversion;
1094 wxLogMessage(msg);
1095
1096#ifndef __ANDROID__
1097#ifndef __WXOSX__
1098 GLenum err = glewInit();
1099#ifdef GLEW_ERROR_NO_GLX_DISPLAY
1100 if (GLEW_OK != err && GLEW_ERROR_NO_GLX_DISPLAY != err)
1101#else
1102 if (GLEW_OK != err)
1103#endif
1104 {
1105 printf("GLEW init failed: %s\n", glewGetErrorString(err));
1106 exit(1);
1107 } else {
1108 wxLogMessage("GLEW init success!n");
1109 }
1110#endif
1111#endif
1112
1113 const GLubyte *ext_str = glGetString(GL_EXTENSIONS);
1114 m_extensions = wxString((const char *)ext_str, wxConvUTF8);
1115
1116 // Set the minimum line width
1117 GLint parms[2];
1118#ifndef USE_ANDROID_GLES2
1119 glGetIntegerv(GL_SMOOTH_LINE_WIDTH_RANGE, &parms[0]);
1120#else
1121 glGetIntegerv(GL_ALIASED_LINE_WIDTH_RANGE, &parms[0]);
1122#endif
1123 g_GLMinSymbolLineWidth = wxMax(parms[0], 1);
1124 g_GLMinCartographicLineWidth = wxMax(parms[0], 1);
1125
1126 // Some GL renderers do a poor job of Anti-aliasing very narrow line
1127 // widths. This is most evident on rendered symbols which have horizontal
1128 // or vertical line segments Detect this case, and adjust the render
1129 // parameters.
1130
1131 if (m_renderer.Upper().Find("MESA") != wxNOT_FOUND) {
1132 GLfloat parf;
1133 glGetFloatv(GL_SMOOTH_LINE_WIDTH_GRANULARITY, &parf);
1134
1135 g_GLMinSymbolLineWidth = wxMax(((float)parms[0] + parf), 1);
1136 }
1137
1138 s_b_useScissorTest = true;
1139 // the radeon x600 driver has buggy scissor test
1140 if (GetRendererString().Find("RADEON X600") != wxNOT_FOUND)
1141 s_b_useScissorTest = false;
1142
1143 if (GetRendererString().Find("GeForce") != wxNOT_FOUND) // GeForce GTX 1070
1144 s_b_useScissorTest = false;
1145
1146 bool bad_stencil_code = false;
1147
1148 // And for the lousy Unichrome drivers, too
1149 if (GetRendererString().Find("UniChrome") != wxNOT_FOUND)
1150 bad_stencil_code = true;
1151
1152 // And for the lousy Mali drivers, too
1153 if (GetRendererString().Find("Mali") != wxNOT_FOUND) bad_stencil_code = true;
1154
1155 // Stencil buffer test
1156 glEnable(GL_STENCIL_TEST);
1157 GLboolean stencil = glIsEnabled(GL_STENCIL_TEST);
1158 int sb;
1159 glGetIntegerv(GL_STENCIL_BITS, &sb);
1160 // printf("Stencil Buffer Available: %d\nStencil bits: %d\n", stencil,
1161 // sb);
1162 glDisable(GL_STENCIL_TEST);
1163
1164 s_b_useStencil = false;
1165 if (stencil && (sb == 8)) s_b_useStencil = true;
1166
1167 if (QueryExtension("GL_ARB_texture_non_power_of_two"))
1168 g_texture_rectangle_format = GL_TEXTURE_2D;
1169 else if (QueryExtension("GL_OES_texture_npot"))
1170 g_texture_rectangle_format = GL_TEXTURE_2D;
1171 else if (QueryExtension("GL_ARB_texture_rectangle"))
1172 g_texture_rectangle_format = GL_TEXTURE_RECTANGLE_ARB;
1173 wxLogMessage(wxString::Format("OpenGL-> Texture rectangle format: %x",
1174 g_texture_rectangle_format));
1175
1176#ifdef __ANDROID__
1177 g_texture_rectangle_format = GL_TEXTURE_2D;
1178#endif
1179
1180 // VBO??
1181 g_b_EnableVBO = true;
1182
1183#ifdef __ANDROID__
1184 g_b_EnableVBO = false;
1185#endif
1186
1187 if (g_b_EnableVBO)
1188 wxLogMessage("OpenGL-> Using Vertexbuffer Objects");
1189 else
1190 wxLogMessage("OpenGL-> Vertexbuffer Objects unavailable");
1191
1192 // Can we use the stencil buffer in a FBO?
1193#ifdef ocpnUSE_GLES
1194 m_b_useFBOStencil = QueryExtension("GL_OES_packed_depth_stencil");
1195#else
1196 m_b_useFBOStencil = QueryExtension("GL_EXT_packed_depth_stencil") == GL_TRUE;
1197#endif
1198
1199#ifndef USE_ANDROID_GLES2
1200 // On Intel Graphics platforms, don't use stencil buffer at all
1201 if (bad_stencil_code) s_b_useStencil = false;
1202#endif
1203
1204 g_GLOptions.m_bUseCanvasPanning = false;
1205
1206 // TODO
1207 // Temporarily disable FBO on Windows, pending implementation of MSAA to
1208 // buffers
1209#ifdef __WXMSW__
1210 // m_b_DisableFBO = true;
1211#endif
1212
1213 // Accelerated pan is not used for MacOS Retina display
1214 // So there is no advantage to using FBO
1215 if (m_displayScale > 1) m_b_DisableFBO = true;
1216
1217 // Maybe build FBO(s)
1218 BuildFBO();
1219
1220#ifndef __ANDROID__
1221
1222 if (m_b_BuiltFBO) {
1223 // Check framebuffer completeness at the end of initialization.
1224 glBindFramebuffer(GL_FRAMEBUFFER_EXT, m_fb0);
1225
1226 glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
1227 g_texture_rectangle_format, m_cache_tex[0], 0);
1228
1229 GLenum fb_status = glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT);
1230 glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
1231
1232 if (fb_status != GL_FRAMEBUFFER_COMPLETE_EXT) {
1233 wxString msg;
1234 msg.Printf(" OpenGL-> Framebuffer Incomplete: %08X", fb_status);
1235 wxLogMessage(msg);
1236 m_b_DisableFBO = true;
1237 BuildFBO();
1238 }
1239 }
1240#endif
1241
1242 if (m_b_BuiltFBO && !m_b_useFBOStencil) s_b_useStencil = false;
1243
1244 // If stencil seems to be a problem, force use of depth buffer clipping for
1245 // Area Patterns
1246 s_b_useStencilAP = s_b_useStencil & !bad_stencil_code;
1247
1248#ifdef USE_ANDROID_GLES2
1249 s_b_useStencilAP = s_b_useStencil; // required for GLES2 platform
1250#endif
1251
1252 // Check and determine if GLSL is to be used
1253 m_bUseGLSL = true;
1254
1255 if (m_b_BuiltFBO) {
1256 wxLogMessage("OpenGL-> Using Framebuffer Objects");
1257
1258 if (m_b_useFBOStencil)
1259 wxLogMessage("OpenGL-> Using FBO Stencil buffer");
1260 else
1261 wxLogMessage("OpenGL-> FBO Stencil buffer unavailable");
1262 } else
1263 wxLogMessage("OpenGL-> Framebuffer Objects unavailable");
1264
1265 if (s_b_useStencil)
1266 wxLogMessage("OpenGL-> Using Stencil buffer clipping");
1267 else
1268 wxLogMessage("OpenGL-> Using Depth buffer clipping");
1269
1270 if (s_b_useScissorTest && s_b_useStencil)
1271 wxLogMessage("OpenGL-> Using Scissor Clipping");
1272
1273 /* we upload non-aligned memory */
1274 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
1275
1276 MipMap_ResolveRoutines();
1277 SetupCompression();
1278
1279 wxString lwmsg;
1280 lwmsg.Printf("OpenGL-> Minimum cartographic line width: %4.1f",
1281 g_GLMinCartographicLineWidth);
1282 wxLogMessage(lwmsg);
1283 lwmsg.Printf("OpenGL-> Minimum symbol line width: %4.1f",
1284 g_GLMinSymbolLineWidth);
1285 wxLogMessage(lwmsg);
1286
1287 if (!g_bGLexpert)
1288 g_GLOptions.m_bUseAcceleratedPanning = !m_b_DisableFBO && m_b_BuiltFBO;
1289
1290#ifdef USE_ANDROID_GLES2
1291 g_GLOptions.m_bUseAcceleratedPanning = true;
1292#endif
1293
1294 if (1) // for now upload all levels
1295 {
1296 int max_level = 0;
1297 int tex_dim = g_GLOptions.m_iTextureDimension;
1298 for (int dim = tex_dim; dim > 0; dim /= 2) max_level++;
1299 g_mipmap_max_level = max_level - 1;
1300 }
1301
1302 // Android, even though using GLES, does not require all levels.
1303#ifdef __ANDROID__
1304 g_mipmap_max_level = 4;
1305#endif
1306
1307 s_b_useFBO = m_b_BuiltFBO;
1308
1309 // Inform the S52 PLIB of options determined
1310 if (ps52plib)
1311 ps52plib->SetGLOptions(
1312 s_b_useStencil, s_b_useStencilAP, s_b_useScissorTest, s_b_useFBO,
1313 g_b_EnableVBO, g_texture_rectangle_format, g_GLMinCartographicLineWidth,
1314 g_GLMinSymbolLineWidth);
1315
1316 m_bsetup = true;
1317
1318 SendJSONConfigMessage();
1319}
1320
1321void glChartCanvas::SendJSONConfigMessage() {
1322 if (g_pi_manager) {
1323 wxJSONValue v;
1324 v["setupComplete"] = m_bsetup;
1325 v["useStencil"] = s_b_useStencil;
1326 v["useStencilAP"] = s_b_useStencilAP;
1327 v["useScissorTest"] = s_b_useScissorTest;
1328 v["useFBO"] = s_b_useFBO;
1329 v["useVBO"] = g_b_EnableVBO;
1330 v["TextureRectangleFormat"] = g_texture_rectangle_format;
1331 wxString msg_id("OCPN_OPENGL_CONFIG");
1332 SendJSONMessageToAllPlugins(msg_id, v);
1333 }
1334}
1335void glChartCanvas::SetupCompression() {
1336 int dim = g_GLOptions.m_iTextureDimension;
1337
1338#ifdef __WXMSW__
1339 if (!::IsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE)) {
1340 wxLogMessage("OpenGL-> SSE2 Instruction set not available");
1341 goto no_compression;
1342 }
1343#endif
1344
1345 g_uncompressed_tile_size = dim * dim * 4; // stored as 32bpp in vram
1346 if (!g_GLOptions.m_bTextureCompression) goto no_compression;
1347
1348 g_raster_format = GL_RGB;
1349
1350 // On GLES, we prefer OES_ETC1 compression, if available
1351#ifdef ocpnUSE_GLES
1352 if (QueryExtension("GL_OES_compressed_ETC1_RGB8_texture")) {
1353 g_raster_format = GL_ETC1_RGB8_OES;
1354
1355 wxLogMessage("OpenGL-> Using oes etc1 compression");
1356 }
1357#endif
1358
1359 if (GL_RGB == g_raster_format) {
1360 /* because s3tc is patented, many foss drivers disable
1361 support by default, however the extension dxt1 allows
1362 us to load this texture type which is enough because we
1363 compress in software using libsquish for superior quality anyway */
1364
1365 if ((QueryExtension("GL_EXT_texture_compression_s3tc") ||
1366 QueryExtension("GL_EXT_texture_compression_dxt1"))) {
1367 /* buggy opensource nvidia driver, renders incorrectly,
1368 workaround is to use format with alpha... */
1369 if (GetRendererString().Find("Gallium") != wxNOT_FOUND &&
1370 GetRendererString().Find("NV") != wxNOT_FOUND)
1371 g_raster_format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
1372 else
1373 g_raster_format = GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
1374
1375 wxLogMessage("OpenGL-> Using s3tc dxt1 compression");
1376 } else if (QueryExtension("GL_3DFX_texture_compression_FXT1")) {
1377 g_raster_format = GL_COMPRESSED_RGB_FXT1_3DFX;
1378
1379 wxLogMessage("OpenGL-> Using 3dfx fxt1 compression");
1380 } else {
1381 wxLogMessage("OpenGL-> No Useable compression format found");
1382 goto no_compression;
1383 }
1384 }
1385
1386#ifdef ocpnUSE_GLES /* gles doesn't have GetTexLevelParameter */
1387 g_tile_size = 512 * 512 / 2; /* 4bpp */
1388#else
1389 /* determine compressed size of a level 0 single tile */
1390 GLuint texture;
1391 glGenTextures(1, &texture);
1392 glBindTexture(GL_TEXTURE_2D, texture);
1393 glTexImage2D(GL_TEXTURE_2D, 0, g_raster_format, dim, dim, 0, GL_RGB,
1394 GL_UNSIGNED_BYTE, NULL);
1395 glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_COMPRESSED_IMAGE_SIZE,
1396 &g_tile_size);
1397 glDeleteTextures(1, &texture);
1398#endif
1399
1400 /* disable texture compression if the tile size is 0 */
1401 if (g_tile_size == 0) goto no_compression;
1402
1403 wxLogMessage(wxString::Format("OpenGL-> Compressed tile size: %dkb (%d:1)",
1404 g_tile_size / 1024,
1405 g_uncompressed_tile_size / g_tile_size));
1406 return;
1407
1408no_compression:
1409 g_GLOptions.m_bTextureCompression = false;
1410
1411 g_tile_size = g_uncompressed_tile_size;
1412 wxLogMessage(wxString::Format("OpenGL-> Not Using compression"));
1413}
1414
1415void glChartCanvas::OnPaint(wxPaintEvent &event) {
1416 wxPaintDC dc(this);
1417
1418 if (!m_pcontext) return;
1419
1420 Show(g_bopengl);
1421 if (!g_bopengl) {
1422 event.Skip();
1423 return;
1424 }
1425
1426 SetCurrent(*m_pcontext);
1427
1428 if (!m_bsetup) {
1429 SetupOpenGL();
1430
1431 if (ps52plib) ps52plib->FlushSymbolCaches(ChartCtxFactory());
1432
1433 m_bsetup = true;
1434 // g_bDebugOGL = true;
1435 }
1436
1437 // Paint updates may have been externally disabled (temporarily, to avoid
1438 // Yield() recursion performance loss)
1439 if (!m_b_paint_enable) return;
1440 // Recursion test, sometimes seen on GTK systems when wxBusyCursor is
1441 // activated
1442 if (m_in_glpaint) return;
1443
1444 // If necessary, reconfigure the S52 PLIB
1445 m_pParentCanvas->UpdateCanvasS52PLIBConfig();
1446
1447 // if( m_pParentCanvas->VPoint.b_quilt ){ // quilted
1448 // if( !m_pParentCanvas->m_pQuilt ||
1449 // !m_pParentCanvas->m_pQuilt->IsComposed() )
1450 // return; // not ready
1451 //
1452 // if(m_pParentCanvas->m_pQuilt->IsQuiltVector()){
1453 // if(ps52plib->GetStateHash() !=
1454 // m_pParentCanvas->m_s52StateHash){
1455 // m_pParentCanvas->UpdateS52State();
1456 // m_pParentCanvas->m_s52StateHash =
1457 // ps52plib->GetStateHash();
1458 // }
1459 // }
1460 // }
1461
1462 m_in_glpaint++;
1463 Render();
1464 m_in_glpaint--;
1465}
1466
1467// These routines allow reusable coordinates
1468bool glChartCanvas::HasNormalizedViewPort(const ViewPort &vp) {
1469 return false;
1470#ifndef USE_ANDROID_GLES2
1471 return vp.m_projection_type == PROJECTION_MERCATOR ||
1472 vp.m_projection_type == PROJECTION_POLAR ||
1473 vp.m_projection_type == PROJECTION_EQUIRECTANGULAR;
1474#else
1475 return false;
1476#endif
1477}
1478
1479/* adjust the opengl transformation matrix so that
1480 points plotted using the identity viewport are correct.
1481 and all rotation translation and scaling is now done in opengl
1482
1483 a central lat and lon of 0, 0 can be used, however objects on the far side of
1484 the world can be up to 3 meters off because limited floating point precision,
1485 and if the points cross 180 longitude then two passes will be required to
1486 render them correctly */
1487#define NORM_FACTOR 4096.0
1488void glChartCanvas::MultMatrixViewPort(ViewPort &vp, float lat, float lon) {
1489#ifndef USE_ANDROID_GLES2
1490
1491 wxPoint2DDouble point;
1492
1493 switch (vp.m_projection_type) {
1494 case PROJECTION_MERCATOR:
1495 case PROJECTION_EQUIRECTANGULAR:
1496 case PROJECTION_WEB_MERCATOR:
1497 // m_pParentCanvas->GetDoubleCanvasPointPixVP(vp, lat, lon, &point);
1498 point = vp.GetDoublePixFromLL(lat, lon);
1499 glTranslated(point.m_x, point.m_y, 0);
1500 glScaled(vp.view_scale_ppm / NORM_FACTOR, vp.view_scale_ppm / NORM_FACTOR,
1501 1);
1502 break;
1503
1504 case PROJECTION_POLAR:
1505 // m_pParentCanvas->GetDoubleCanvasPointPixVP(vp, vp.clat > 0 ? 90 : -90,
1506 // vp.clon, &point);
1507 point = vp.GetDoublePixFromLL(vp.clat > 0 ? 90 : -90, vp.clon);
1508 glTranslated(point.m_x, point.m_y, 0);
1509 glRotatef(vp.clon - lon, 0, 0, vp.clat);
1510 glScalef(vp.view_scale_ppm / NORM_FACTOR, vp.view_scale_ppm / NORM_FACTOR,
1511 1);
1512 glTranslatef(-vp.pix_width / 2, -vp.pix_height / 2, 0);
1513 break;
1514
1515 default:
1516 printf("ERROR: Unhandled projection\n");
1517 }
1518
1519 double rotation = vp.rotation;
1520
1521 if (rotation) glRotatef(rotation * 180 / PI, 0, 0, 1);
1522#endif
1523}
1524
1525ViewPort glChartCanvas::NormalizedViewPort(const ViewPort &vp, float lat,
1526 float lon) {
1527 ViewPort cvp = vp;
1528
1529 switch (vp.m_projection_type) {
1530 case PROJECTION_MERCATOR:
1531 case PROJECTION_EQUIRECTANGULAR:
1532 case PROJECTION_WEB_MERCATOR:
1533 cvp.clat = lat;
1534 break;
1535
1536 case PROJECTION_POLAR:
1537 cvp.clat = vp.clat > 0 ? 90 : -90; // either north or south polar
1538 break;
1539
1540 default:
1541 printf("ERROR: Unhandled projection\n");
1542 }
1543
1544 cvp.clon = lon;
1545 cvp.view_scale_ppm = NORM_FACTOR;
1546 cvp.rotation = cvp.skew = 0;
1547 return cvp;
1548}
1549
1550bool glChartCanvas::CanClipViewport(const ViewPort &vp) {
1551 return vp.m_projection_type == PROJECTION_MERCATOR ||
1552 vp.m_projection_type == PROJECTION_WEB_MERCATOR ||
1553 vp.m_projection_type == PROJECTION_EQUIRECTANGULAR;
1554}
1555
1556ViewPort glChartCanvas::ClippedViewport(const ViewPort &vp,
1557 const LLRegion &region) {
1558 if (!CanClipViewport(vp)) return vp;
1559
1560 ViewPort cvp = vp;
1561 LLBBox bbox = region.GetBox();
1562
1563 if (!bbox.GetValid()) return vp;
1564
1565 /* region.GetBox() will always try to give coordinates from -180 to 180 but in
1566 the case where the viewport crosses the IDL, we actually want the clipped
1567 viewport to use coordinates outside this range to ensure the logic in the
1568 various rendering routines works the same here (with accelerated panning)
1569 as it does without, so we can adjust the coordinates here */
1570
1571 if (bbox.GetMaxLon() < cvp.GetBBox().GetMinLon()) {
1572 bbox.Set(bbox.GetMinLat(), bbox.GetMinLon() + 360, bbox.GetMaxLat(),
1573 bbox.GetMaxLon() + 360);
1574 cvp.SetBBoxDirect(bbox);
1575 } else if (bbox.GetMinLon() > cvp.GetBBox().GetMaxLon()) {
1576 bbox.Set(bbox.GetMinLat(), bbox.GetMinLon() - 360, bbox.GetMaxLat(),
1577 bbox.GetMaxLon() - 360);
1578 cvp.SetBBoxDirect(bbox);
1579 } else
1580 cvp.SetBBoxDirect(bbox);
1581
1582 return cvp;
1583}
1584
1585void glChartCanvas::DrawStaticRoutesTracksAndWaypoints(ViewPort &vp) {
1586 if (!m_pParentCanvas->m_bShowNavobjects) return;
1587 ocpnDC dc(*this);
1588
1589 for (Track *pTrackDraw : g_TrackList) {
1590 /* defer rendering active tracks until later */
1591 ActiveTrack *pActiveTrack = dynamic_cast<ActiveTrack *>(pTrackDraw);
1592 if (pActiveTrack && pActiveTrack->IsRunning()) continue;
1593
1594 TrackGui(*pTrackDraw).Draw(m_pParentCanvas, dc, vp, vp.GetBBox());
1595 }
1596
1597 for (wxRouteListNode *node = pRouteList->GetFirst(); node;
1598 node = node->GetNext()) {
1599 Route *pRouteDraw = node->GetData();
1600
1601 if (!pRouteDraw) continue;
1602
1603 /* defer rendering active routes until later */
1604 if (pRouteDraw->IsActive() || pRouteDraw->IsSelected()) continue;
1605
1606 /* defer rendering routes being edited until later */
1607 if (pRouteDraw->m_bIsBeingEdited) continue;
1608
1609 RouteGui(*pRouteDraw).DrawGL(vp, m_pParentCanvas, dc);
1610 // pRouteDraw->DrawGL(vp, m_pParentCanvas, dc);
1611 }
1612
1613 /* Waypoints not drawn as part of routes, and not being edited */
1614 if (vp.GetBBox().GetValid() && pWayPointMan) {
1615 for (wxRoutePointListNode *pnode =
1616 pWayPointMan->GetWaypointList()->GetFirst();
1617 pnode; pnode = pnode->GetNext()) {
1618 RoutePoint *pWP = pnode->GetData();
1619 if (pWP && (!pWP->m_bRPIsBeingEdited) && (!pWP->m_bIsInRoute))
1620 if (vp.GetBBox().ContainsMarge(pWP->m_lat, pWP->m_lon, .5))
1621 RoutePointGui(*pWP).DrawGL(vp, m_pParentCanvas, dc);
1622 // pWP->DrawGL(vp, m_pParentCanvas, dc);
1623 }
1624 }
1625}
1626
1627void glChartCanvas::DrawDynamicRoutesTracksAndWaypoints(ViewPort &vp) {
1628 ocpnDC dc(*this);
1629
1630 for (Track *pTrackDraw : g_TrackList) {
1631 ActiveTrack *pActiveTrack = dynamic_cast<ActiveTrack *>(pTrackDraw);
1632 if (pActiveTrack && pActiveTrack->IsRunning())
1633 TrackGui(*pTrackDraw).Draw(m_pParentCanvas, dc, vp, vp.GetBBox());
1634 // We need Track::Draw() to dynamically render last (ownship) point.
1635 }
1636
1637 for (wxRouteListNode *node = pRouteList->GetFirst(); node;
1638 node = node->GetNext()) {
1639 Route *pRouteDraw = node->GetData();
1640
1641 int drawit = 0;
1642 if (!pRouteDraw) continue;
1643
1644 /* Active routes */
1645 if (pRouteDraw->IsActive() || pRouteDraw->IsSelected()) drawit++;
1646
1647 /* Routes being edited */
1648 if (pRouteDraw->m_bIsBeingEdited) drawit++;
1649
1650 /* Routes Selected */
1651 if (pRouteDraw->IsSelected()) drawit++;
1652
1653 if (drawit) {
1654 const LLBBox &vp_box = vp.GetBBox(), &test_box = pRouteDraw->GetBBox();
1655 if (!vp_box.IntersectOut(test_box))
1656 RouteGui(*pRouteDraw).DrawGL(vp, m_pParentCanvas, dc);
1657 // pRouteDraw->DrawGL(vp, m_pParentCanvas, dc);
1658 }
1659 }
1660
1661 /* Waypoints not drawn as part of routes, which are being edited right now */
1662 if (vp.GetBBox().GetValid() && pWayPointMan) {
1663 for (wxRoutePointListNode *pnode =
1664 pWayPointMan->GetWaypointList()->GetFirst();
1665 pnode; pnode = pnode->GetNext()) {
1666 RoutePoint *pWP = pnode->GetData();
1667 if (pWP && pWP->m_bRPIsBeingEdited && !pWP->m_bIsInRoute)
1668 RoutePointGui(*pWP).DrawGL(vp, m_pParentCanvas, dc);
1669 // pWP->DrawGL(vp, m_pParentCanvas, dc);
1670 }
1671 }
1672}
1673
1674static void GetLatLonCurveDist(const ViewPort &vp, float &lat_dist,
1675 float &lon_dist) {
1676 // This really could use some more thought, and possibly split at different
1677 // intervals based on chart skew and other parameters to optimize performance
1678 switch (vp.m_projection_type) {
1679 case PROJECTION_TRANSVERSE_MERCATOR:
1680 lat_dist = 4, lon_dist = 1;
1681 break;
1682 case PROJECTION_POLYCONIC:
1683 lat_dist = 2, lon_dist = 1;
1684 break;
1685 case PROJECTION_ORTHOGRAPHIC:
1686 lat_dist = 2, lon_dist = 2;
1687 break;
1688 case PROJECTION_POLAR:
1689 lat_dist = 180, lon_dist = 1;
1690 break;
1691 case PROJECTION_STEREOGRAPHIC:
1692 case PROJECTION_GNOMONIC:
1693 lat_dist = 2, lon_dist = 1;
1694 break;
1695 case PROJECTION_EQUIRECTANGULAR:
1696 // this is suboptimal because we don't care unless there is
1697 // a change in both lat AND lon (skewed chart)
1698 lat_dist = 2, lon_dist = 360;
1699 break;
1700 default:
1701 lat_dist = 180, lon_dist = 360;
1702 }
1703}
1704
1705void glChartCanvas::RenderChartOutline(ocpnDC &dc, int dbIndex, ViewPort &vp) {
1706 if (ChartData->GetDBChartType(dbIndex) == CHART_TYPE_PLUGIN &&
1707 !ChartData->IsChartAvailable(dbIndex))
1708 return;
1709
1710 /* quick bounds check */
1711 LLBBox box;
1712 ChartData->GetDBBoundingBox(dbIndex, box);
1713 if (!box.GetValid()) return;
1714
1715 // Don't draw an outline in the case where the chart covers the entire world
1716 // */
1717 if (box.GetLonRange() == 360) return;
1718
1719 LLBBox vpbox = vp.GetBBox();
1720
1721 double lon_bias = 0;
1722 // chart is outside of viewport lat/lon bounding box
1723 if (box.IntersectOutGetBias(vp.GetBBox(), lon_bias)) return;
1724
1725 wxColour color;
1726 if (ChartData->GetDBChartType(dbIndex) == CHART_TYPE_CM93)
1727 color = GetGlobalColor(_T ( "YELO1" ));
1728 else if (ChartData->GetDBChartFamily(dbIndex) == CHART_FAMILY_VECTOR)
1729 color = GetGlobalColor(_T ( "GREEN2" ));
1730 else
1731 color = GetGlobalColor(_T ( "UINFR" ));
1732
1733#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
1734 float plylat, plylon;
1735
1736 if (g_GLOptions.m_GLLineSmoothing) glEnable(GL_LINE_SMOOTH);
1737
1738 glColor3ub(color.Red(), color.Green(), color.Blue());
1739 glLineWidth(g_GLMinSymbolLineWidth);
1740
1741 float lat_dist, lon_dist;
1742 GetLatLonCurveDist(vp, lat_dist, lon_dist);
1743
1744 // Are there any aux ply entries?
1745 int nAuxPlyEntries = ChartData->GetnAuxPlyEntries(dbIndex), nPly;
1746 int j = 0;
1747 do {
1748 if (nAuxPlyEntries)
1749 nPly = ChartData->GetDBAuxPlyPoint(dbIndex, 0, j, 0, 0);
1750 else
1751 nPly = ChartData->GetDBPlyPoint(dbIndex, 0, &plylat, &plylon);
1752
1753 bool begin = false, sml_valid = false;
1754 double sml[2];
1755 float lastplylat = 0.0;
1756 float lastplylon = 0.0;
1757 // modulo is undefined for zero (compiler can use a div operation)
1758 int modulo = (nPly == 0) ? 1 : nPly;
1759 for (int i = 0; i < nPly + 1; i++) {
1760 if (nAuxPlyEntries)
1761 ChartData->GetDBAuxPlyPoint(dbIndex, i % modulo, j, &plylat, &plylon);
1762 else
1763 ChartData->GetDBPlyPoint(dbIndex, i % modulo, &plylat, &plylon);
1764
1765 plylon += lon_bias;
1766
1767 if (lastplylon - plylon > 180)
1768 lastplylon -= 360;
1769 else if (lastplylon - plylon < -180)
1770 lastplylon += 360;
1771
1772 int splits;
1773 if (i == 0)
1774 splits = 1;
1775 else {
1776 int lat_splits = floor(fabs(plylat - lastplylat) / lat_dist);
1777 int lon_splits = floor(fabs(plylon - lastplylon) / lon_dist);
1778 splits = wxMax(lat_splits, lon_splits) + 1;
1779 }
1780
1781 double smj[2];
1782 if (splits != 1) {
1783 // must perform border interpolation in mercator space as this is what
1784 // the charts use
1785 toSM(plylat, plylon, 0, 0, smj + 0, smj + 1);
1786 if (!sml_valid) toSM(lastplylat, lastplylon, 0, 0, sml + 0, sml + 1);
1787 }
1788
1789 for (double c = 0; c < splits; c++) {
1790 double lat, lon;
1791 if (c == splits - 1)
1792 lat = plylat, lon = plylon;
1793 else {
1794 double d = (double)(c + 1) / splits;
1795 fromSM(d * smj[0] + (1 - d) * sml[0], d * smj[1] + (1 - d) * sml[1],
1796 0, 0, &lat, &lon);
1797 }
1798
1799 wxPoint2DDouble s;
1800 m_pParentCanvas->GetDoubleCanvasPointPix(lat, lon, &s);
1801 if (!std::isnan(s.m_x)) {
1802 if (!begin) {
1803 begin = true;
1804 glBegin(GL_LINE_STRIP);
1805 }
1806 glVertex2f(s.m_x, s.m_y);
1807 } else if (begin) {
1808 glEnd();
1809 begin = false;
1810 }
1811 }
1812 if ((sml_valid = splits != 1)) memcpy(sml, smj, sizeof smj);
1813 lastplylat = plylat, lastplylon = plylon;
1814 }
1815
1816 if (begin) glEnd();
1817
1818 } while (++j < nAuxPlyEntries); // There are no aux Ply Point entries
1819
1820 glDisable(GL_LINE_SMOOTH);
1821 // glDisable( GL_BLEND );
1822
1823#else
1824 double nominal_line_width_pix =
1825 wxMax(2.0, floor(m_pParentCanvas->GetPixPerMM() / 4));
1826
1827 if (ChartData->GetDBChartType(dbIndex) == CHART_TYPE_CM93)
1828 dc.SetPen(wxPen(GetGlobalColor(_T ( "YELO1" )), nominal_line_width_pix,
1829 wxPENSTYLE_SOLID));
1830
1831 else if (ChartData->GetDBChartFamily(dbIndex) == CHART_FAMILY_VECTOR)
1832 dc.SetPen(wxPen(GetGlobalColor(_T ( "UINFG" )), nominal_line_width_pix,
1833 wxPENSTYLE_SOLID));
1834
1835 else
1836 dc.SetPen(wxPen(GetGlobalColor(_T ( "UINFR" )), nominal_line_width_pix,
1837 wxPENSTYLE_SOLID));
1838
1839 float plylat1, plylon1;
1840 int pixx1, pixy1;
1841
1842 // Are there any aux ply entries?
1843 int nAuxPlyEntries = ChartData->GetnAuxPlyEntries(dbIndex);
1844 if (0 == nAuxPlyEntries) // There are no aux Ply Point entries
1845 {
1846 wxPoint r, r1;
1847 std::vector<int> points_vector;
1848
1849 std::vector<float> vec = ChartData->GetReducedPlyPoints(dbIndex);
1850 int nPly = vec.size() / 2;
1851
1852 if (nPly == 0) return;
1853
1854 for (int i = 0; i < nPly; i++) {
1855 plylon1 = vec[i * 2];
1856 plylat1 = vec[i * 2 + 1];
1857
1858 m_pParentCanvas->GetCanvasPointPix(plylat1, plylon1, &r1);
1859 pixx1 = r1.x;
1860 pixy1 = r1.y;
1861
1862 points_vector.push_back(pixx1);
1863 points_vector.push_back(pixy1);
1864 }
1865
1866 ChartData->GetDBPlyPoint(dbIndex, 0, &plylat1, &plylon1);
1867 plylon1 += lon_bias;
1868
1869 m_pParentCanvas->GetCanvasPointPix(vec[1], vec[0], &r1);
1870 pixx1 = r1.x;
1871 pixy1 = r1.y;
1872
1873 points_vector.push_back(pixx1);
1874 points_vector.push_back(pixy1);
1875
1876 if (points_vector.size()) {
1877 std::vector<int>::iterator it = points_vector.begin();
1878 dc.DrawLines(points_vector.size() / 2, (wxPoint *)&(*it), 0, 0, true);
1879 }
1880 }
1881
1882 else // Use Aux PlyPoints
1883 {
1884 wxPoint r, r1;
1885
1886 for (int j = 0; j < nAuxPlyEntries; j++) {
1887 std::vector<int> points_vector;
1888
1889 std::vector<float> vec = ChartData->GetReducedAuxPlyPoints(dbIndex, j);
1890 int nAuxPly = vec.size() / 2;
1891
1892 if (nAuxPly == 0) continue;
1893
1894 for (int i = 0; i < nAuxPly; i++) {
1895 plylon1 = vec[i * 2];
1896 plylat1 = vec[i * 2 + 1];
1897
1898 m_pParentCanvas->GetCanvasPointPix(plylat1, plylon1, &r1);
1899 pixx1 = r1.x;
1900 pixy1 = r1.y;
1901
1902 points_vector.push_back(pixx1);
1903 points_vector.push_back(pixy1);
1904 }
1905
1906 m_pParentCanvas->GetCanvasPointPix(vec[1], vec[0], &r1);
1907 pixx1 = r1.x;
1908 pixy1 = r1.y;
1909
1910 points_vector.push_back(pixx1);
1911 points_vector.push_back(pixy1);
1912
1913 if (points_vector.size()) {
1914 std::vector<int>::iterator it = points_vector.begin();
1915 dc.DrawLines(points_vector.size() / 2, (wxPoint *)&(*it), 0, 0, true);
1916 }
1917 }
1918 }
1919
1920#endif
1921}
1922
1923extern void CalcGridSpacing(float WindowDegrees, float &MajorSpacing,
1924 float &MinorSpacing);
1925extern wxString CalcGridText(float latlon, float spacing, bool bPostfix);
1926void glChartCanvas::GridDraw() {
1927 if (!m_pParentCanvas->m_bDisplayGrid) return;
1928
1929 ViewPort &vp = m_pParentCanvas->GetVP();
1930
1931 if (!vp.IsValid() || !vp.GetBBox().GetValid()) return;
1932
1933 // TODO: make minor grid work all the time
1934 bool minorgrid =
1935 fabs(vp.rotation) < 0.0001 && vp.m_projection_type == PROJECTION_MERCATOR;
1936
1937 double nlat, elon, slat, wlon;
1938 float lat, lon;
1939 float gridlatMajor, gridlatMinor, gridlonMajor, gridlonMinor;
1940 wxCoord w, h;
1941
1942 wxColour GridColor = GetGlobalColor(_T ( "SNDG1" ));
1943
1944 if (!m_gridfont.IsBuilt()) {
1945 double dpi_factor = g_BasePlatform->GetDisplayDIPMult(this);
1946 wxFont *dFont = FontMgr::Get().GetFont(_("GridText"), 0);
1947 wxFont font = *dFont;
1948 int font_size = wxMax(10, dFont->GetPointSize());
1949 font.SetPointSize(font_size * m_displayScale);
1950 font.SetWeight(wxFONTWEIGHT_NORMAL);
1951
1952 m_gridfont.SetContentScaleFactor(OCPN_GetDisplayContentScaleFactor());
1953 m_gridfont.Build(font, 1, dpi_factor);
1954 }
1955 m_gridfont.SetColor(GridColor);
1956
1957 w = vp.pix_width;
1958 h = vp.pix_height;
1959
1960 LLBBox llbbox = vp.GetBBox();
1961 nlat = llbbox.GetMaxLat();
1962 slat = llbbox.GetMinLat();
1963 elon = llbbox.GetMaxLon();
1964 wlon = llbbox.GetMinLon();
1965
1966 // calculate distance between latitude grid lines
1967 CalcGridSpacing(vp.view_scale_ppm, gridlatMajor, gridlatMinor);
1968 CalcGridSpacing(vp.view_scale_ppm, gridlonMajor, gridlonMinor);
1969
1970 // if it is known the grid has straight lines it's a bit faster
1971 bool straight_latitudes = vp.m_projection_type == PROJECTION_MERCATOR ||
1972 vp.m_projection_type == PROJECTION_WEB_MERCATOR ||
1973 vp.m_projection_type == PROJECTION_EQUIRECTANGULAR;
1974 bool straight_longitudes = vp.m_projection_type == PROJECTION_MERCATOR ||
1975 vp.m_projection_type == PROJECTION_WEB_MERCATOR ||
1976 vp.m_projection_type == PROJECTION_POLAR ||
1977 vp.m_projection_type == PROJECTION_EQUIRECTANGULAR;
1978
1979 double latmargin;
1980 if (straight_latitudes)
1981 latmargin = 0;
1982 else
1983 latmargin = gridlatMajor / 2; // don't draw on poles
1984
1985 slat = wxMax(slat, -90 + latmargin);
1986 nlat = wxMin(nlat, 90 - latmargin);
1987
1988 float startlat = ceil(slat / gridlatMajor) * gridlatMajor;
1989 float startlon = ceil(wlon / gridlonMajor) * gridlonMajor;
1990 float curved_step = wxMin(sqrt(5e-3 / vp.view_scale_ppm), 3);
1991
1992 ocpnDC gldc(*this);
1993 wxPen *pen = wxThePenList->FindOrCreatePen(GridColor, g_GLMinSymbolLineWidth,
1994 wxPENSTYLE_SOLID);
1995 gldc.SetPen(*pen);
1996
1997 // Draw Major latitude grid lines and text
1998
1999 // calculate position of first major latitude grid line
2000 float lon_step = elon - wlon;
2001 if (!straight_latitudes) lon_step /= ceil(lon_step / curved_step);
2002
2003 for (lat = startlat; lat < nlat; lat += gridlatMajor) {
2004 wxPoint r, s;
2005 s.x = INVALID_COORD;
2006 s.y = INVALID_COORD;
2007 for (lon = wlon; lon < elon + lon_step / 2; lon += lon_step) {
2008 m_pParentCanvas->GetCanvasPointPix(lat, lon, &r);
2009 if (s.x != INVALID_COORD && s.y != INVALID_COORD) {
2010 gldc.DrawLine(s.x, s.y, r.x, r.y, false);
2011 }
2012 s = r;
2013 }
2014 }
2015
2016 if (minorgrid) {
2017 // draw minor latitude grid lines
2018 for (lat = ceil(slat / gridlatMinor) * gridlatMinor; lat < nlat;
2019 lat += gridlatMinor) {
2020 wxPoint r;
2021 m_pParentCanvas->GetCanvasPointPix(lat, (elon + wlon) / 2, &r);
2022 gldc.DrawLine(0, r.y, 10, r.y, true);
2023 gldc.DrawLine(w - 10, r.y, w, r.y, false);
2024 }
2025 }
2026
2027 // draw major longitude grid lines
2028 float lat_step = nlat - slat;
2029 if (!straight_longitudes) lat_step /= ceil(lat_step / curved_step);
2030
2031 for (lon = startlon; lon < elon; lon += gridlonMajor) {
2032 wxPoint r, s;
2033 s.x = INVALID_COORD;
2034 s.y = INVALID_COORD;
2035 for (lat = slat; lat < nlat + lat_step / 2; lat += lat_step) {
2036 m_pParentCanvas->GetCanvasPointPix(lat, lon, &r);
2037 if (s.x != INVALID_COORD && s.y != INVALID_COORD) {
2038 gldc.DrawLine(s.x, s.y, r.x, r.y, false);
2039 }
2040 s = r;
2041 }
2042 }
2043
2044 if (minorgrid) {
2045 // draw minor longitude grid lines
2046 for (lon = ceil(wlon / gridlonMinor) * gridlonMinor; lon < elon;
2047 lon += gridlonMinor) {
2048 wxPoint r;
2049 m_pParentCanvas->GetCanvasPointPix((nlat + slat) / 2, lon, &r);
2050 gldc.DrawLine(r.x, 0, r.x, 10, false);
2051 gldc.DrawLine(r.x, h - 10, r.x, h, false);
2052 }
2053 }
2054
2055 // draw text labels
2056 if (abs(vp.rotation) < .1) {
2057 glEnable(GL_TEXTURE_2D);
2058 glEnable(GL_BLEND);
2059 for (lat = startlat; lat < nlat; lat += gridlatMajor) {
2060 if (fabs(lat - wxRound(lat)) < 1e-5) lat = wxRound(lat);
2061
2062 wxString st =
2063 CalcGridText(lat, gridlatMajor, true); // get text for grid line
2064 int iy;
2065 m_gridfont.GetTextExtent(st, 0, &iy);
2066
2067 if (straight_latitudes) {
2068 wxPoint r, s;
2069 m_pParentCanvas->GetCanvasPointPix(lat, elon, &r);
2070 m_pParentCanvas->GetCanvasPointPix(lat, wlon, &s);
2071
2072 float x = 0, y = -1;
2073 y = (float)(r.y * s.x - s.y * r.x) / (s.x - r.x);
2074 if (y < 0 || y > h) {
2075 y = h - iy;
2076 x = (float)(r.x * s.y - s.x * r.y + (s.x - r.x) * y) / (s.y - r.y);
2077 }
2078
2079 m_gridfont.RenderString(st, x, y);
2080 } else {
2081 // iteratively attempt to find where the latitude line crosses x=0
2082 wxPoint2DDouble r;
2083 double y1, y2, lat1, lon1, lat2, lon2;
2084
2085 y1 = 0, y2 = vp.pix_height;
2086 double error = vp.pix_width, lasterror;
2087 int maxiters = 10;
2088 do {
2089 m_pParentCanvas->GetCanvasPixPoint(0, y1, lat1, lon1);
2090 m_pParentCanvas->GetCanvasPixPoint(0, y2, lat2, lon2);
2091
2092 double y = y1 + (lat1 - lat) * (y2 - y1) / (lat1 - lat2);
2093
2094 m_pParentCanvas->GetDoubleCanvasPointPix(
2095 lat, lon1 + (y1 - y) * (lon2 - lon1) / (y1 - y2), &r);
2096
2097 if (fabs(y - y1) < fabs(y - y2))
2098 y1 = y;
2099 else
2100 y2 = y;
2101
2102 lasterror = error;
2103 error = fabs(r.m_x);
2104 if (--maxiters == 0) break;
2105 } while (error > 1 && error < lasterror);
2106
2107 if (error < 1 && r.m_y >= 0 && r.m_y <= vp.pix_height - iy)
2108 r.m_x = 0;
2109 else
2110 // just draw at center longitude
2111 m_pParentCanvas->GetDoubleCanvasPointPix(lat, vp.clon, &r);
2112
2113 m_gridfont.RenderString(st, r.m_x, r.m_y);
2114 }
2115 }
2116
2117 for (lon = startlon; lon < elon; lon += gridlonMajor) {
2118 if (fabs(lon - wxRound(lon)) < 1e-5) lon = wxRound(lon);
2119
2120 wxPoint r, s;
2121 m_pParentCanvas->GetCanvasPointPix(nlat, lon, &r);
2122 m_pParentCanvas->GetCanvasPointPix(slat, lon, &s);
2123
2124 float xlon = lon;
2125 if (xlon > 180.0)
2126 xlon -= 360.0;
2127 else if (xlon <= -180.0)
2128 xlon += 360.0;
2129
2130 wxString st = CalcGridText(xlon, gridlonMajor, false);
2131 int ix;
2132 m_gridfont.GetTextExtent(st, &ix, 0);
2133
2134 if (straight_longitudes) {
2135 float x = -1, y = 0;
2136 x = (float)(r.x * s.y - s.x * r.y) / (s.y - r.y);
2137 if (x < 0 || x > w) {
2138 x = w - ix;
2139 y = (float)(r.y * s.x - s.y * r.x + (s.y - r.y) * x) / (s.x - r.x);
2140 }
2141
2142 m_gridfont.RenderString(st, x, y);
2143 } else {
2144 // iteratively attempt to find where the latitude line crosses x=0
2145 wxPoint2DDouble r;
2146 double x1, x2, lat1, lon1, lat2, lon2;
2147
2148 x1 = 0, x2 = vp.pix_width;
2149 double error = vp.pix_height, lasterror;
2150 do {
2151 m_pParentCanvas->GetCanvasPixPoint(x1, 0, lat1, lon1);
2152 m_pParentCanvas->GetCanvasPixPoint(x2, 0, lat2, lon2);
2153
2154 double x = x1 + (lon1 - lon) * (x2 - x1) / (lon1 - lon2);
2155
2156 m_pParentCanvas->GetDoubleCanvasPointPix(
2157 lat1 + (x1 - x) * (lat2 - lat1) / (x1 - x2), lon, &r);
2158
2159 if (fabs(x - x1) < fabs(x - x2))
2160 x1 = x;
2161 else
2162 x2 = x;
2163
2164 lasterror = error;
2165 error = fabs(r.m_y);
2166 } while (error > 1 && error < lasterror);
2167
2168 if (error < 1 && r.m_x >= 0 && r.m_x <= vp.pix_width - ix)
2169 r.m_y = 0;
2170 else
2171 // failure, instead just draw the text at center latitude
2172 m_pParentCanvas->GetDoubleCanvasPointPix(
2173 wxMin(wxMax(vp.clat, slat), nlat), lon, &r);
2174
2175 m_gridfont.RenderString(st, r.m_x, r.m_y);
2176 }
2177 }
2178
2179 glDisable(GL_TEXTURE_2D);
2180 glDisable(GL_BLEND);
2181 }
2182}
2183
2184void glChartCanvas::DrawEmboss(ocpnDC &dc, emboss_data *emboss) {
2185 if (!emboss) return;
2186
2187 int w = emboss->width, h = emboss->height;
2188
2189 glEnable(GL_TEXTURE_2D);
2190
2191 // render using opengl and alpha blending
2192 if (!emboss->gltexind) { /* upload to texture */
2193
2194 emboss->glwidth = NextPow2(emboss->width);
2195 emboss->glheight = NextPow2(emboss->height);
2196
2197 /* convert to luminance alpha map */
2198 int size = emboss->glwidth * emboss->glheight;
2199 char *data = new char[2 * size];
2200 for (int i = 0; i < h; i++) {
2201 for (int j = 0; j < emboss->glwidth; j++) {
2202 if (j < w) {
2203 data[2 * ((i * emboss->glwidth) + j)] =
2204 (char)(emboss->pmap[(i * w) + j] > 0 ? 0 : 255);
2205 data[2 * ((i * emboss->glwidth) + j) + 1] =
2206 (char)abs((emboss->pmap[(i * w) + j]));
2207 }
2208 }
2209 }
2210
2211 glGenTextures(1, &emboss->gltexind);
2212 glBindTexture(GL_TEXTURE_2D, emboss->gltexind);
2213 glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, emboss->glwidth,
2214 emboss->glheight, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE,
2215 data);
2216 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2217 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2218
2219 delete[] data;
2220 }
2221
2222 glBindTexture(GL_TEXTURE_2D, emboss->gltexind);
2223
2224 glEnable(GL_BLEND);
2225
2226 int x = emboss->x, y = emboss->y;
2227
2228 float wp = (float)w / emboss->glwidth;
2229 float hp = (float)h / emboss->glheight;
2230
2231 float coords[8];
2232 float uv[8];
2233
2234 // normal uv
2235 uv[0] = 0;
2236 uv[1] = 0;
2237 uv[2] = wp;
2238 uv[3] = 0;
2239 uv[4] = wp;
2240 uv[5] = hp;
2241 uv[6] = 0;
2242 uv[7] = hp;
2243
2244 // pixels
2245 coords[0] = 0;
2246 coords[1] = 0;
2247 coords[2] = w;
2248 coords[3] = 0;
2249 coords[4] = w;
2250 coords[5] = h;
2251 coords[6] = 0;
2252 coords[7] = h;
2253
2254 // FIXME(dave) Find a way to make this thing a little transparaent
2255 RenderSingleTexture(dc, coords, uv, m_pParentCanvas->GetpVP(), x, y, 0);
2256
2257 glDisable(GL_BLEND);
2258 glDisable(GL_TEXTURE_2D);
2259}
2260
2261void glChartCanvas::ShipDraw(ocpnDC &dc) {
2262 if (!m_pParentCanvas->GetVP().IsValid()) return;
2263 wxPoint GPSOffsetPixels(0, 0);
2264 wxPoint2DDouble lGPSPoint, lShipMidPoint;
2265
2266 // COG/SOG may be undefined in NMEA data stream
2267 float pCog = std::isnan(gCog) ? 0 : gCog;
2268 float pSog = std::isnan(gSog) ? 0 : gSog;
2269
2270 // Here, calculate the ownship location on screen, and make it so.
2271 // Special case: No need for such precision on chart drag operations
2272 double shift_dx = 0;
2273 double shift_dy = 0;
2274 bool dynamic = m_pParentCanvas->m_animationActive ||
2275 m_pParentCanvas->m_MouseDragging ||
2276 m_pParentCanvas->m_chart_drag_inertia_active;
2277 if (m_pParentCanvas->m_bFollow && !dynamic) {
2278 lGPSPoint.m_x = m_pParentCanvas->GetVP().pix_width / 2;
2279 lGPSPoint.m_y = m_pParentCanvas->GetVP().pix_height / 2;
2280 if (m_pParentCanvas->m_bLookAhead) {
2281 // In "b_follow" mode, we have a-priori information about the desired
2282 // screen coordinates of ownship, as a pixel offset from center.
2283 // Use this information as a performance optimization,
2284 // at larger viewing scales.
2285 if (m_pParentCanvas->GetVPChartScale() < 2e6) {
2286 double angle = m_pParentCanvas->dir_to_shift * PI / 180.;
2287 angle += m_pParentCanvas->GetVPRotation();
2288 shift_dx = m_pParentCanvas->meters_to_shift * sin(angle) *
2289 m_pParentCanvas->GetVPScale();
2290 lGPSPoint.m_x -= shift_dx / cos(gLat * PI / 180.);
2291 shift_dy = m_pParentCanvas->meters_to_shift * cos(angle) *
2292 m_pParentCanvas->GetVPScale();
2293 lGPSPoint.m_y += shift_dy / cos(gLat * PI / 180.);
2294 } else {
2295 m_pParentCanvas->GetDoubleCanvasPointPix(gLat, gLon, &lGPSPoint);
2296 }
2297 } else {
2298 lGPSPoint.m_x -= m_pParentCanvas->m_OSoffsetx;
2299 lGPSPoint.m_y += m_pParentCanvas->m_OSoffsety;
2300 }
2301 } else {
2302 m_pParentCanvas->GetDoubleCanvasPointPixVP(m_pParentCanvas->GetVP(), gLat,
2303 gLon, &lGPSPoint);
2304 }
2305
2306 lShipMidPoint = lGPSPoint;
2307
2308 // Draw the icon rotated to the COG
2309 // or to the Hdt if available
2310 float icon_hdt = pCog;
2311 if (!std::isnan(gHdt)) icon_hdt = gHdt;
2312
2313 // COG may be undefined in NMEA data stream
2314 if (std::isnan(icon_hdt)) icon_hdt = 0.0;
2315
2316 // Calculate the ownship drawing angle icon_rad using an assumed 10 minute
2317 // predictor
2318 double osd_head_lat, osd_head_lon;
2319 wxPoint2DDouble osd_head_point;
2320
2321 ll_gc_ll(gLat, gLon, icon_hdt, pSog * 10. / 60., &osd_head_lat,
2322 &osd_head_lon);
2323
2324 m_pParentCanvas->GetDoubleCanvasPointPixVP(
2325 m_pParentCanvas->GetVP(), osd_head_lat, osd_head_lon, &osd_head_point);
2326
2327 double icon_rad = atan2f((float)(osd_head_point.m_y - lShipMidPoint.m_y),
2328 (float)(osd_head_point.m_x - lShipMidPoint.m_x));
2329 icon_rad += (float)PI;
2330
2331 if (pSog < 0.2)
2332 icon_rad =
2333 ((icon_hdt + 90.) * PI / 180.) + m_pParentCanvas->GetVP().rotation;
2334
2335 // Another draw test ,based on pixels, assuming the ship icon is a fixed
2336 // nominal size and is just barely outside the viewport ....
2337 BoundingBox bb_screen(0, 0, m_pParentCanvas->GetVP().pix_width,
2338 m_pParentCanvas->GetVP().pix_height);
2339
2340 // TODO: fix to include actual size of boat that will be rendered
2341 int img_height = 0;
2342
2343 if (bb_screen.PointInBox(lShipMidPoint, 20)) {
2344 if (g_GLOptions.m_GLLineSmoothing) glEnable(GL_LINE_SMOOTH);
2345 if (g_GLOptions.m_GLPolygonSmoothing) glEnable(GL_POLYGON_SMOOTH);
2346
2347 if (m_pParentCanvas->GetVP().chart_scale >
2348 300000) // According to S52, this should be 50,000
2349 {
2350 float scale_factor = 1.0;
2351 // Scale the generic icon to ChartScaleFactor, slightly softened....
2352 if ((g_ChartScaleFactorExp > 1.0) && (g_OwnShipIconType == 0))
2353 scale_factor = (log(g_ChartScaleFactorExp) + 1.0) * 1.1;
2354
2355 float nominal_ownship_size_mm = m_pParentCanvas->m_display_size_mm / 44.0;
2356 nominal_ownship_size_mm = wxMin(nominal_ownship_size_mm, 15.0);
2357 nominal_ownship_size_mm = wxMax(nominal_ownship_size_mm, 7.0);
2358
2359 scale_factor *= m_pParentCanvas->GetContentScaleFactor();
2360
2361 float nominal_ownship_size_pixels =
2362 wxMax(20.0, m_pParentCanvas->GetPixPerMM() *
2363 nominal_ownship_size_mm); // nominal length, but not
2364 // less than 20 pixel
2365 float v = (nominal_ownship_size_pixels * scale_factor) / 3;
2366
2367 wxPen ppSmallScaleShip;
2368 if (SHIP_NORMAL == m_pParentCanvas->m_ownship_state)
2369 ppSmallScaleShip =
2370 wxPen(GetGlobalColor(_T ( "URED" )), v / 5, wxPENSTYLE_SOLID);
2371 else
2372 ppSmallScaleShip =
2373 wxPen(GetGlobalColor(_T ( "YELO1" )), v / 5, wxPENSTYLE_SOLID);
2374 dc.SetPen(ppSmallScaleShip);
2375
2376 dc.SetBrush(
2377 wxBrush(GetGlobalColor(_T ( "URED" )), wxBRUSHSTYLE_TRANSPARENT));
2378
2379 // start with cross
2380 dc.DrawLine((-v * 1.2) + lShipMidPoint.m_x, lShipMidPoint.m_y,
2381 (v * 1.2) + lShipMidPoint.m_x, lShipMidPoint.m_y);
2382 dc.DrawLine(lShipMidPoint.m_x, (-v * 1.2) + lShipMidPoint.m_y,
2383 lShipMidPoint.m_x, (v * 1.2) + lShipMidPoint.m_y);
2384
2385 // Two circles
2386 dc.StrokeCircle(lShipMidPoint.m_x, lShipMidPoint.m_y, v);
2387 dc.StrokeCircle(lShipMidPoint.m_x, lShipMidPoint.m_y, 0.6 * v);
2388 img_height = 20;
2389 } else {
2390 int draw_color = SHIP_INVALID;
2391 if (SHIP_NORMAL == m_pParentCanvas->m_ownship_state)
2392 draw_color = SHIP_NORMAL;
2393 else if (SHIP_LOWACCURACY == m_pParentCanvas->m_ownship_state)
2394 draw_color = SHIP_LOWACCURACY;
2395
2396 if (!ownship_tex ||
2397 (draw_color !=
2398 ownship_color)) { /* initial run, create texture for ownship,
2399 also needed at colorscheme changes (not
2400 implemented) */
2401
2402 ownship_color = draw_color;
2403
2404 if (ownship_tex) glDeleteTextures(1, &ownship_tex);
2405
2406 glGenTextures(1, &ownship_tex);
2407 glBindTexture(GL_TEXTURE_2D, ownship_tex);
2408
2409 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2410 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2411
2412 wxImage image;
2413 if (m_pParentCanvas->m_pos_image_user) {
2414 switch (draw_color) {
2415 case SHIP_INVALID:
2416 image = *m_pParentCanvas->m_pos_image_user_grey;
2417 break;
2418 case SHIP_NORMAL:
2419 image = *m_pParentCanvas->m_pos_image_user;
2420 break;
2421 case SHIP_LOWACCURACY:
2422 image = *m_pParentCanvas->m_pos_image_user_yellow;
2423 break;
2424 }
2425 } else {
2426 switch (draw_color) {
2427 case SHIP_INVALID:
2428 image = *m_pParentCanvas->m_pos_image_grey;
2429 break;
2430 case SHIP_NORMAL:
2431 image = *m_pParentCanvas->m_pos_image_red;
2432 break;
2433 case SHIP_LOWACCURACY:
2434 image = *m_pParentCanvas->m_pos_image_yellow;
2435 break;
2436 }
2437 }
2438
2439 int w = image.GetWidth(), h = image.GetHeight();
2440 int glw = NextPow2(w), glh = NextPow2(h);
2441 ownship_size = wxSize(w, h);
2442 ownship_tex_size = wxSize(glw, glh);
2443
2444 unsigned char *d = image.GetData();
2445 unsigned char *a = image.GetAlpha();
2446 unsigned char *e = new unsigned char[4 * w * h];
2447
2448 if (d && e && a) {
2449 for (int p = 0; p < w * h; p++) {
2450 e[4 * p + 0] = d[3 * p + 0];
2451 e[4 * p + 1] = d[3 * p + 1];
2452 e[4 * p + 2] = d[3 * p + 2];
2453 e[4 * p + 3] = a[p];
2454 }
2455 }
2456 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, glw, glh, 0, GL_RGBA,
2457 GL_UNSIGNED_BYTE, 0);
2458
2459 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE,
2460 e);
2461 delete[] e;
2462 }
2463
2464 /* establish ship color */
2465#ifndef USE_ANDROID_GLES2
2466 if (m_pParentCanvas->m_pos_image_user)
2467 glColor4ub(255, 255, 255, 255);
2468 else if (SHIP_NORMAL == m_pParentCanvas->m_ownship_state)
2469 glColor4ub(255, 0, 0, 255);
2470 else if (SHIP_LOWACCURACY == m_pParentCanvas->m_ownship_state)
2471 glColor4ub(255, 255, 0, 255);
2472 else
2473 glColor4ub(128, 128, 128, 255);
2474#endif
2475 float scale_factor_y = 1.0;
2476 float scale_factor_x = 1.0;
2477
2478 int ownShipWidth = 22; // Default values from s_ownship_icon
2479 int ownShipLength = 84;
2480 lShipMidPoint = lGPSPoint;
2481
2482 /* scaled ship? */
2483 if (g_OwnShipIconType != 0)
2484 m_pParentCanvas->ComputeShipScaleFactor(
2485 icon_hdt, ownShipWidth, ownShipLength, lShipMidPoint,
2486 GPSOffsetPixels, lGPSPoint, scale_factor_x, scale_factor_y);
2487
2488 glEnable(GL_BLEND);
2489
2490 // Scale the generic icon to ChartScaleFactor, slightly softened....
2491 if ((g_ShipScaleFactorExp > 1.0) && (g_OwnShipIconType == 0)) {
2492 scale_factor_x = (log(g_ShipScaleFactorExp) + 1.0) * 1.1;
2493 scale_factor_y = (log(g_ShipScaleFactorExp) + 1.0) * 1.1;
2494 }
2495
2496 // Correct for scaled displays, e.g. Retina
2497 scale_factor_x *= m_pParentCanvas->GetContentScaleFactor();
2498 scale_factor_y *= m_pParentCanvas->GetContentScaleFactor();
2499
2500 // Set the size of the little circle showing the GPS reference position
2501 // Set a default early, adjust later based on render type
2502 float gps_circle_radius = 3.0;
2503
2504 if (g_OwnShipIconType == 0) { // Default Bitmap
2505
2506 glEnable(GL_TEXTURE_2D);
2507 glBindTexture(GL_TEXTURE_2D, ownship_tex);
2508
2509 // We choose to render the ownship bitmap at roughly the same size( in
2510 // pixels ) as the DC mode renderer.
2511 // For ultra-high definition displays, we clamp the actual on-screen
2512 // size to be no smaller than 7.0 mm Similarly, for lo-res displays, we
2513 // limit the actual size to be no larger than 15 mm maximum.
2514
2515 // Get bitmap height in pixels
2516 int image_height_bitmap = m_pParentCanvas->m_pos_image_red->GetHeight();
2517 if (m_pParentCanvas->m_pos_image_user)
2518 image_height_bitmap = m_pParentCanvas->m_pos_image_user->GetHeight();
2519
2520 float nominal_ownship_size_mm =
2521 image_height_bitmap / m_pParentCanvas->GetPixPerMM();
2522
2523 nominal_ownship_size_mm = wxMin(nominal_ownship_size_mm, 15.0);
2524 nominal_ownship_size_mm = wxMax(nominal_ownship_size_mm, 7.0);
2525
2526 float nominal_ownship_size_pixels =
2527 m_pParentCanvas->GetPixPerMM() * nominal_ownship_size_mm;
2528
2529 if (m_pParentCanvas->GetContentScaleFactor() == 1.0) {
2530 nominal_ownship_size_pixels = wxMax(
2531 20.0, nominal_ownship_size_pixels); // not less than 20 pixel
2532 }
2533
2534 float h = nominal_ownship_size_pixels * scale_factor_y;
2535 float w = nominal_ownship_size_pixels * scale_factor_x *
2536 ownship_size.x / ownship_size.y;
2537 float glw = ownship_tex_size.x, glh = ownship_tex_size.y;
2538 float u = ownship_size.x / glw, v = ownship_size.y / glh;
2539
2540 // printf("%g %g %g %g %g %g %g\n",
2541 // nominal_ownship_size_mm, nominal_ownship_size_pixels,
2542 // h, w, u, v, m_pParentCanvas->m_display_size_mm);
2543 // tweak GPS reference point indicator size
2544 gps_circle_radius = w / 5;
2545
2546 float uv[8], coords[8];
2547 uv[0] = 0;
2548 uv[1] = 0;
2549 uv[2] = u;
2550 uv[3] = 0;
2551 uv[4] = u;
2552 uv[5] = v;
2553 uv[6] = 0;
2554 uv[7] = v;
2555
2556 coords[0] = -w / 2;
2557 coords[1] = -h / 2;
2558 coords[2] = w / 2;
2559 coords[3] = -h / 2;
2560 coords[4] = w / 2;
2561 coords[5] = h / 2;
2562 coords[6] = -w / 2;
2563 coords[7] = h / 2;
2564
2565 RenderSingleTexture(dc, coords, uv, m_pParentCanvas->GetpVP(),
2566 lShipMidPoint.m_x, lShipMidPoint.m_y,
2567 icon_rad - PI / 2);
2568 glDisable(GL_TEXTURE_2D);
2569 } else if (g_OwnShipIconType == 1) { // Scaled Bitmap
2570
2571 glEnable(GL_TEXTURE_2D);
2572 glBindTexture(GL_TEXTURE_2D, ownship_tex);
2573
2574 float nominal_ownship_size_pixels_y = 84;
2575 float nominal_ownship_size_pixels_x = 22;
2576
2577 float h = nominal_ownship_size_pixels_y * scale_factor_y;
2578 float w = nominal_ownship_size_pixels_x * scale_factor_x;
2579
2580 float u = (float)ownship_size.x / ownship_tex_size.x,
2581 v = (float)ownship_size.y / ownship_tex_size.y;
2582
2583 // tweak GPS reference point indicator size
2584 gps_circle_radius = w / 5;
2585
2586 float uv[8], coords[8];
2587 uv[0] = 0;
2588 uv[1] = 0;
2589 uv[2] = u;
2590 uv[3] = 0;
2591 uv[4] = u;
2592 uv[5] = v;
2593 uv[6] = 0;
2594 uv[7] = v;
2595
2596 coords[0] = -w / 2;
2597 coords[1] = -h / 2;
2598 coords[2] = w / 2;
2599 coords[3] = -h / 2;
2600 coords[4] = w / 2;
2601 coords[5] = h / 2;
2602 coords[6] = -w / 2;
2603 coords[7] = h / 2;
2604
2605 RenderSingleTexture(dc, coords, uv, m_pParentCanvas->GetpVP(),
2606 lShipMidPoint.m_x, lShipMidPoint.m_y,
2607 icon_rad - PI / 2);
2608
2609 glDisable(GL_TEXTURE_2D);
2610 } else if (g_OwnShipIconType == 2) { // Scaled Vector
2611 // static const GLint s_ownship_icon[] = { 5, -42, 11,
2612 // -28, 11, 42, -11, 42,
2613 // -11, -28, -5,
2614 // -42, -11, 0,
2615 // 11, 0, 0, 42,
2616 // 0, -42 };
2617
2618 wxPoint shipPoints[6];
2619
2620 wxColour colour = m_pParentCanvas->ShipColor();
2621 wxPen ppPen(*wxBLACK, 1);
2622 wxBrush ppBrush(colour);
2623 dc.SetPen(ppPen);
2624 dc.SetBrush(ppBrush);
2625
2626 shipPoints[0].x = 0 * scale_factor_x;
2627 shipPoints[0].y = -28 * scale_factor_y;
2628 shipPoints[1].x = 11 * scale_factor_x;
2629 shipPoints[1].y = -28 * scale_factor_y;
2630 shipPoints[2].x = 11 * scale_factor_x;
2631 shipPoints[2].y = 42 * scale_factor_y;
2632 shipPoints[3].x = 0 * scale_factor_x;
2633 shipPoints[3].y = 42 * scale_factor_y;
2634 dc.DrawPolygon(4, shipPoints, lShipMidPoint.m_x, lShipMidPoint.m_y, 1,
2635 icon_rad - PI / 2);
2636
2637 shipPoints[0].x = 0 * scale_factor_x;
2638 shipPoints[0].y = -42 * scale_factor_y;
2639 shipPoints[1].x = 5 * scale_factor_x;
2640 shipPoints[1].y = -42 * scale_factor_y;
2641 shipPoints[2].x = 11 * scale_factor_x;
2642 shipPoints[2].y = -28 * scale_factor_y;
2643 shipPoints[3].x = 0 * scale_factor_x;
2644 shipPoints[3].y = -28 * scale_factor_y;
2645 dc.DrawPolygon(4, shipPoints, lShipMidPoint.m_x, lShipMidPoint.m_y, 1,
2646 icon_rad - PI / 2);
2647
2648 shipPoints[0].x = 0 * scale_factor_x;
2649 shipPoints[0].y = -28 * scale_factor_y;
2650 shipPoints[1].x = -11 * scale_factor_x;
2651 shipPoints[1].y = -28 * scale_factor_y;
2652 shipPoints[2].x = -11 * scale_factor_x;
2653 shipPoints[2].y = 42 * scale_factor_y;
2654 shipPoints[3].x = 0 * scale_factor_x;
2655 shipPoints[3].y = 42 * scale_factor_y;
2656 dc.DrawPolygon(4, shipPoints, lShipMidPoint.m_x, lShipMidPoint.m_y, 1,
2657 icon_rad - PI / 2);
2658
2659 shipPoints[0].x = 0 * scale_factor_x;
2660 shipPoints[0].y = -42 * scale_factor_y;
2661 shipPoints[1].x = -5 * scale_factor_x;
2662 shipPoints[1].y = -42 * scale_factor_y;
2663 shipPoints[2].x = -11 * scale_factor_x;
2664 shipPoints[2].y = -28 * scale_factor_y;
2665 shipPoints[3].x = 0 * scale_factor_x;
2666 shipPoints[3].y = -28 * scale_factor_y;
2667 dc.DrawPolygon(4, shipPoints, lShipMidPoint.m_x, lShipMidPoint.m_y, 1,
2668 icon_rad - PI / 2);
2669
2670 // draw with cross
2671 double p1x = -11 * scale_factor_x;
2672 double p2x = 11 * scale_factor_x;
2673 double p1y = 0;
2674 double p2y = 0;
2675 double p1xr =
2676 ((p1x)*cos(icon_rad - PI / 2)) - ((p1y)*sin(icon_rad - PI / 2));
2677 double p2xr =
2678 ((p2x)*cos(icon_rad - PI / 2)) - ((p2y)*sin(icon_rad - PI / 2));
2679 double p1yr =
2680 ((p1y)*cos(icon_rad - PI / 2)) + ((p1x)*sin(icon_rad - PI / 2));
2681 double p2yr =
2682 ((p2y)*cos(icon_rad - PI / 2)) + ((p2x)*sin(icon_rad - PI / 2));
2683 dc.DrawLine(p1xr + lShipMidPoint.m_x, p1yr + lShipMidPoint.m_y,
2684 p2xr + lShipMidPoint.m_x, p2yr + lShipMidPoint.m_y);
2685
2686 p1x = 0;
2687 p2x = 0;
2688 p1y = -42 * scale_factor_y;
2689 p2y = 42 * scale_factor_y;
2690 p1xr = ((p1x)*cos(icon_rad - PI / 2)) - ((p1y)*sin(icon_rad - PI / 2));
2691 p2xr = ((p2x)*cos(icon_rad - PI / 2)) - ((p2y)*sin(icon_rad - PI / 2));
2692 p1yr = ((p1y)*cos(icon_rad - PI / 2)) + ((p1x)*sin(icon_rad - PI / 2));
2693 p2yr = ((p2y)*cos(icon_rad - PI / 2)) + ((p2x)*sin(icon_rad - PI / 2));
2694 dc.DrawLine(p1xr + lShipMidPoint.m_x, p1yr + lShipMidPoint.m_y,
2695 p2xr + lShipMidPoint.m_x, p2yr + lShipMidPoint.m_y);
2696 }
2697
2698 img_height = ownShipLength * scale_factor_y;
2699
2700 // Reference point, where the GPS antenna is
2701 if (m_pParentCanvas->m_pos_image_user) gps_circle_radius = 1;
2702
2703 wxPen ppPen1(GetGlobalColor(_T ( "UBLCK" )), 1, wxPENSTYLE_SOLID);
2704 dc.SetPen(ppPen1);
2705 dc.SetBrush(wxBrush(GetGlobalColor(_T ( "CHWHT" ))));
2706
2707 dc.StrokeCircle(lGPSPoint.m_x, lGPSPoint.m_y, gps_circle_radius);
2708 }
2709
2710 // glDisableClientState(GL_VERTEX_ARRAY);
2711 glDisable(GL_LINE_SMOOTH);
2712 glDisable(GL_POLYGON_SMOOTH);
2713 glDisable(GL_BLEND);
2714 }
2715
2716 m_pParentCanvas->ShipIndicatorsDraw(dc, img_height, GPSOffsetPixels,
2717 lGPSPoint);
2718}
2719
2720void glChartCanvas::DrawFloatingOverlayObjects(ocpnDC &dc) {
2721 ViewPort &vp = m_pParentCanvas->GetVP();
2722
2723 // Draw any active or selected routes now
2724 extern Routeman *g_pRouteMan;
2725 // extern Track *g_pActiveTrack;
2726 Route *active_route = g_pRouteMan->GetpActiveRoute();
2727
2728 // if( active_route ) active_route->DrawGL( vp, region );
2729 // if( g_pActiveTrack ) g_pActiveTrack->Draw( dc, vp );
2730 // if( m_pParentCanvas->m_pSelectedRoute )
2731 // m_pParentCanvas->m_pSelectedRoute->DrawGL( vp, region );
2732
2733 GridDraw();
2734
2735 g_overlayCanvas = m_pParentCanvas;
2736 if (g_pi_manager) {
2737 g_pi_manager->SendViewPortToRequestingPlugIns(vp);
2738 g_pi_manager->RenderAllGLCanvasOverlayPlugIns(
2739 m_pcontext, vp, m_pParentCanvas->m_canvasIndex, OVERLAY_LEGACY);
2740 }
2741
2742 // all functions called with m_pParentCanvas-> are still slow because they go
2743 // through ocpndc
2744 AISDrawAreaNotices(dc, m_pParentCanvas->GetVP(), m_pParentCanvas);
2745
2746 m_pParentCanvas->DrawAnchorWatchPoints(dc);
2747 AISDraw(dc, m_pParentCanvas->GetVP(), m_pParentCanvas);
2748 ShipDraw(dc);
2749 m_pParentCanvas->AlertDraw(dc);
2750
2751 m_pParentCanvas->RenderVisibleSectorLights(dc);
2752
2753 m_pParentCanvas->RenderRouteLegs(dc);
2754 m_pParentCanvas->RenderShipToActive(dc, true);
2755 m_pParentCanvas->ScaleBarDraw(dc);
2756 s57_DrawExtendedLightSectorsGL(dc, m_pParentCanvas->VPoint,
2757 m_pParentCanvas->extendedSectorLegs);
2758 if (g_pi_manager) {
2759 g_pi_manager->RenderAllGLCanvasOverlayPlugIns(
2760 m_pcontext, vp, m_pParentCanvas->m_canvasIndex, OVERLAY_OVER_SHIPS);
2761 }
2762}
2763
2764void glChartCanvas::DrawChartBar(ocpnDC &dc) {
2765 if (m_pParentCanvas->GetPiano()) {
2766 int canvas_height = GetClientSize().y;
2767 canvas_height *= m_displayScale;
2768
2769 m_pParentCanvas->GetPiano()->DrawGL(
2770 canvas_height - m_pParentCanvas->GetPiano()->GetHeight());
2771 }
2772}
2773
2774void glChartCanvas::DrawQuiting() {
2775#ifndef USE_ANDROID_GLES2
2776 GLubyte pattern[8][8];
2777 for (int y = 0; y < 8; y++)
2778 for (int x = 0; x < 8; x++) pattern[y][x] = (y == x) * 255;
2779
2780 glEnable(GL_BLEND);
2781 glEnable(GL_TEXTURE_2D);
2782 glBindTexture(GL_TEXTURE_2D, 0);
2783
2784 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2785 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2786 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2787
2788 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 8, 8, 0, GL_ALPHA, GL_UNSIGNED_BYTE,
2789 pattern);
2790 glColor3f(0, 0, 0);
2791
2792 float x = GetSize().x, y = GetSize().y;
2793 float u = x / 8, v = y / 8;
2794
2795 glBegin(GL_QUADS);
2796 glTexCoord2f(0, 0);
2797 glVertex2f(0, 0);
2798 glTexCoord2f(0, v);
2799 glVertex2f(0, y);
2800 glTexCoord2f(u, v);
2801 glVertex2f(x, y);
2802 glTexCoord2f(u, 0);
2803 glVertex2f(x, 0);
2804 glEnd();
2805
2806 glDisable(GL_TEXTURE_2D);
2807 glDisable(GL_BLEND);
2808#endif
2809}
2810
2811void glChartCanvas::DrawCloseMessage(wxString msg) {
2812#ifndef USE_ANDROID_GLES2
2813
2814 if (1) {
2815 wxFont *pfont = FontMgr::Get().FindOrCreateFont(
2816 12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
2817
2818 TexFont texfont;
2819
2820 texfont.Build(*pfont, 1, 1);
2821 int w, h;
2822 texfont.GetTextExtent(msg, &w, &h);
2823 h += 2;
2824 int yp = m_pParentCanvas->GetVP().pix_height / 2;
2825 int xp = (m_pParentCanvas->GetVP().pix_width - w) / 2;
2826
2827 glColor3ub(243, 229, 47);
2828
2829 glBegin(GL_QUADS);
2830 glVertex2i(xp, yp);
2831 glVertex2i(xp + w, yp);
2832 glVertex2i(xp + w, yp + h);
2833 glVertex2i(xp, yp + h);
2834 glEnd();
2835
2836 glEnable(GL_BLEND);
2837
2838 glColor3ub(0, 0, 0);
2839 glEnable(GL_TEXTURE_2D);
2840 texfont.RenderString(msg, xp, yp);
2841 glDisable(GL_TEXTURE_2D);
2842 glDisable(GL_BLEND);
2843 }
2844#endif
2845}
2846
2847GLShaderProgram *pStaticShader;
2848
2849static std::list<double *> combine_work_data;
2850static void combineCallbackD(GLdouble coords[3], GLdouble *vertex_data[4],
2851 GLfloat weight[4], GLdouble **dataOut) {
2852 double *vertex = new double[3];
2853 combine_work_data.push_back(vertex);
2854 memcpy(vertex, coords, 3 * (sizeof *coords));
2855 *dataOut = vertex;
2856}
2857
2858#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
2859void vertexCallbackD_GLSL(GLvoid *vertex) {
2860 // Grow the work buffer if necessary
2861 if (s_tess_vertex_idx > s_tess_buf_len - 8) {
2862 int new_buf_len = s_tess_buf_len + 100;
2863 GLfloat *tmp = s_tess_work_buf;
2864
2865 s_tess_work_buf =
2866 (GLfloat *)realloc(s_tess_work_buf, new_buf_len * sizeof(GLfloat));
2867 if (NULL == s_tess_work_buf) {
2868 free(tmp);
2869 tmp = NULL;
2870 } else
2871 s_tess_buf_len = new_buf_len;
2872 }
2873
2874 GLdouble *pointer = (GLdouble *)vertex;
2875
2876 s_tess_work_buf[s_tess_vertex_idx++] = (float)pointer[0];
2877 s_tess_work_buf[s_tess_vertex_idx++] = (float)pointer[1];
2878
2879 s_nvertex++;
2880}
2881
2882void beginCallbackD_GLSL(GLenum mode) {
2883 s_tess_vertex_idx_this = s_tess_vertex_idx;
2884 s_tess_mode = mode;
2885 s_nvertex = 0;
2886}
2887
2888void endCallbackD_GLSL() {
2889 GLShaderProgram *shader = pStaticShader;
2890 shader->Bind();
2891
2892 shader->SetUniformMatrix4fv("MVMatrix",
2893 (GLfloat *)s_tessVP.vp_matrix_transform);
2894
2895 mat4x4 identityMatrix;
2896 mat4x4_identity(identityMatrix);
2897 shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)identityMatrix);
2898
2899 // Use color stored in static variable.
2900 float colorv[4];
2901 colorv[0] = s_regionColor.Red() / float(256);
2902 colorv[1] = s_regionColor.Green() / float(256);
2903 colorv[2] = s_regionColor.Blue() / float(256);
2904 colorv[3] = s_regionColor.Alpha() / float(256);
2905 shader->SetUniform4fv("color", colorv);
2906
2907 float *bufPt = &s_tess_work_buf[s_tess_vertex_idx_this];
2908 shader->SetAttributePointerf("position", bufPt);
2909
2910 glDrawArrays(s_tess_mode, 0, s_nvertex);
2911
2912 shader->UnBind();
2913}
2914#else
2915void vertexCallbackD(GLvoid *vertex) { glVertex3dv((GLdouble *)vertex); }
2916
2917void beginCallbackD(GLenum mode) { glBegin(mode); }
2918
2919void endCallbackD() { glEnd(); }
2920
2921#endif
2922
2923void glChartCanvas::DrawRegion(ViewPort &vp, const LLRegion &region) {
2924 float lat_dist, lon_dist;
2925 GetLatLonCurveDist(vp, lat_dist, lon_dist);
2926
2927 GLUtesselator *tobj = gluNewTess();
2928 if (!pStaticShader) pStaticShader = GetStaticTriShader();
2929
2930#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
2931 gluTessCallback(tobj, GLU_TESS_VERTEX, (_GLUfuncptr)&vertexCallbackD_GLSL);
2932 gluTessCallback(tobj, GLU_TESS_BEGIN, (_GLUfuncptr)&beginCallbackD_GLSL);
2933 gluTessCallback(tobj, GLU_TESS_END, (_GLUfuncptr)&endCallbackD_GLSL);
2934 gluTessCallback(tobj, GLU_TESS_COMBINE, (_GLUfuncptr)&combineCallbackD);
2935 s_tessVP = vp;
2936
2937#else
2938 gluTessCallback(tobj, GLU_TESS_VERTEX, (_GLUfuncptr)&vertexCallbackD);
2939 gluTessCallback(tobj, GLU_TESS_BEGIN, (_GLUfuncptr)&beginCallbackD);
2940 gluTessCallback(tobj, GLU_TESS_END, (_GLUfuncptr)&endCallbackD);
2941 gluTessCallback(tobj, GLU_TESS_COMBINE, (_GLUfuncptr)&combineCallbackD);
2942#endif
2943
2944 gluTessNormal(tobj, 0, 0, 1);
2945
2946 gluTessBeginPolygon(tobj, NULL);
2947 for (std::list<poly_contour>::const_iterator i = region.contours.begin();
2948 i != region.contours.end(); i++) {
2949 gluTessBeginContour(tobj);
2950 contour_pt l = *i->rbegin();
2951 double sml[2];
2952 bool sml_valid = false;
2953 for (poly_contour::const_iterator j = i->begin(); j != i->end(); j++) {
2954 int lat_splits = floor(fabs(j->y - l.y) / lat_dist);
2955 int lon_splits = floor(fabs(j->x - l.x) / lon_dist);
2956 int splits = wxMax(lat_splits, lon_splits) + 1;
2957
2958 double smj[2];
2959 if (splits != 1) {
2960 // must perform border interpolation in mercator space as this is what
2961 // the charts use
2962 toSM(j->y, j->x, 0, 0, smj + 0, smj + 1);
2963 if (!sml_valid) toSM(l.y, l.x, 0, 0, sml + 0, sml + 1);
2964 }
2965
2966 for (int i = 0; i < splits; i++) {
2967 double lat, lon;
2968 if (i == splits - 1)
2969 lat = j->y, lon = j->x;
2970 else {
2971 double d = (double)(i + 1) / splits;
2972 fromSM(d * smj[0] + (1 - d) * sml[0], d * smj[1] + (1 - d) * sml[1],
2973 0, 0, &lat, &lon);
2974 }
2975 wxPoint2DDouble q = vp.GetDoublePixFromLL(lat, lon);
2976 if (std::isnan(q.m_x)) continue;
2977
2978 double *p = new double[6];
2979
2980 // p[0] = q.m_x, p[1] = q.m_y, p[2] = 0;
2981 // It is reasonable to use wxRound() here,
2982 // since we are working with pixel coordinates at this point
2983 p[0] = wxRound(q.m_x), p[1] = wxRound(q.m_y), p[2] = 0;
2984
2985 // wxPoint pt = vp.GetPixFromLL(lat, lon);
2986 // p[0] = pt.x, p[1] = pt.y, p[2] = 0;
2987
2988 gluTessVertex(tobj, p, p);
2989 combine_work_data.push_back(p);
2990 }
2991 l = *j;
2992
2993 if ((sml_valid = splits != 1)) memcpy(sml, smj, sizeof smj);
2994 }
2995 gluTessEndContour(tobj);
2996 }
2997 gluTessEndPolygon(tobj);
2998
2999 gluDeleteTess(tobj);
3000
3001 for (std::list<double *>::iterator i = combine_work_data.begin();
3002 i != combine_work_data.end(); i++)
3003 delete[] *i;
3004 combine_work_data.clear();
3005}
3006
3007/* set stencil buffer to clip in this region, and optionally clear using the
3008 * current color */
3009void glChartCanvas::SetClipRegion(ViewPort &vp, const LLRegion &region) {
3010 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // disable color buffer
3011
3012 if (s_b_useStencil) {
3013 // Create a stencil buffer for clipping to the region
3014 glEnable(GL_STENCIL_TEST);
3015 glStencilMask(0x1); // write only into bit 0 of the stencil buffer
3016 glClear(GL_STENCIL_BUFFER_BIT);
3017
3018 // We are going to write "1" into the stencil buffer wherever the region
3019 // is valid
3020 glStencilFunc(GL_ALWAYS, 1, 1);
3021 glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
3022 }
3023// #ifndef USE_ANDROID_GLES2
3024#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
3025
3026 else // Use depth buffer for clipping
3027 {
3028 glEnable(GL_DEPTH_TEST); // to enable writing to the depth buffer
3029 glDepthFunc(GL_ALWAYS); // to ensure everything you draw passes
3030 glDepthMask(GL_TRUE); // to allow writes to the depth buffer
3031
3032 glClear(GL_DEPTH_BUFFER_BIT); // for a fresh start
3033
3034 // Decompose the region into rectangles, and draw as quads
3035 // With z = 1
3036 // dep buffer clear = 1
3037 // 1 makes 0 in dep buffer, works
3038 // 0 make .5 in depth buffer
3039 // -1 makes 1 in dep buffer
3040
3041 // Depth buffer runs from 0 at z = 1 to 1 at z = -1
3042 // Draw the clip geometry at z = 0.5, giving a depth buffer value of 0.25
3043 // Subsequent drawing at z=0 (depth = 0.5) will pass if using
3044 // glDepthFunc(GL_GREATER);
3045 glTranslatef(0, 0, .5);
3046 }
3047#endif
3048
3049 s_regionColor = wxColor(0, 0, 0, 255);
3050 DrawRegion(vp, region);
3051
3052 if (s_b_useStencil) {
3053 // Now set the stencil ops to subsequently render only where the stencil
3054 // bit is "1"
3055 glStencilFunc(GL_EQUAL, 1, 1);
3056 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
3057 }
3058// #ifndef USE_ANDROID_GLES2
3059#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
3060 else {
3061 glDepthFunc(GL_GREATER); // Set the test value
3062 glDepthMask(GL_FALSE); // disable depth buffer
3063 glTranslatef(0, 0, -.5); // reset translation
3064 }
3065#endif
3066 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // re-enable color buffer
3067}
3068
3069void glChartCanvas::SetClipRect(const ViewPort &vp, const wxRect &rect,
3070 bool b_clear) {
3071 /* for some reason this causes an occasional bug in depth mode, I cannot
3072 seem to solve it yet, so for now: */
3073 if (s_b_useStencil && s_b_useScissorTest) {
3074 wxRect vp_rect(0, 0, vp.pix_width, vp.pix_height);
3075 if (rect != vp_rect) {
3076 glEnable(GL_SCISSOR_TEST);
3077 glScissor(rect.x, vp.pix_height - rect.height - rect.y, rect.width,
3078 rect.height);
3079 }
3080#ifndef USE_ANDROID_GLES2
3081#endif
3082 return;
3083 }
3084}
3085
3086void glChartCanvas::DisableClipRegion() {
3087 glDisable(GL_SCISSOR_TEST);
3088 glDisable(GL_STENCIL_TEST);
3089 glDisable(GL_DEPTH_TEST);
3090}
3091
3092void glChartCanvas::Invalidate() {
3093 /* should probably use a different flag for this */
3094 m_cache_vp.Invalidate();
3095}
3096
3097void glChartCanvas::RenderRasterChartRegionGL(ChartBase *chart, ViewPort &vp,
3098 LLRegion &region) {
3099 ChartBaseBSB *pBSBChart = dynamic_cast<ChartBaseBSB *>(chart);
3100 if (!pBSBChart) return;
3101
3102 if (b_inCompressAllCharts)
3103 return; // don't want multiple texfactories to exist
3104
3105 // Look for the texture factory for this chart
3106 wxString key = chart->GetHashKey();
3107
3108 glTexFactory *pTexFact;
3109 ChartPathHashTexfactType &hash = g_glTextureManager->m_chart_texfactory_hash;
3110 ChartPathHashTexfactType::iterator ittf = hash.find(key);
3111
3112 // Not Found ?
3113 if (ittf == hash.end()) {
3114 hash[key] = new glTexFactory(chart, g_raster_format);
3115 hash[key]->SetHashKey(key);
3116 }
3117
3118 pTexFact = hash[key];
3119 pTexFact->SetLRUTime(++m_LRUtime);
3120
3121 // for small scales, don't use normalized coordinates for accuracy (difference
3122 // is up to 3 meters error)
3123 bool use_norm_vp =
3124 glChartCanvas::HasNormalizedViewPort(vp) && pBSBChart->GetPPM() < 1;
3125 pTexFact->PrepareTiles(vp, use_norm_vp, pBSBChart);
3126
3127 // For underzoom cases, we will define the textures as having their base
3128 // levels equivalent to a level "n" mipmap, where n is calculated, and is
3129 // always binary This way we can avoid loading much texture memory
3130
3131 int base_level;
3132 if (vp.m_projection_type == PROJECTION_MERCATOR &&
3133 chart->GetChartProjectionType() == PROJECTION_MERCATOR) {
3134 double scalefactor = pBSBChart->GetRasterScaleFactor(vp);
3135 base_level = log(scalefactor) / log(2.0);
3136
3137 if (base_level < 0) /* for overzoom */
3138 base_level = 0;
3139 if (base_level > g_mipmap_max_level) base_level = g_mipmap_max_level;
3140 } else
3141 base_level = 0; // base level should be computed per tile, for now load all
3142
3143 /* setup opengl parameters */
3144 glEnable(GL_TEXTURE_2D);
3145#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
3146 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3147
3148 glEnableClientState(GL_VERTEX_ARRAY);
3149 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
3150
3151 if (use_norm_vp) {
3152 glPushMatrix();
3153 double lat, lon;
3154 pTexFact->GetCenter(lat, lon);
3155 MultMatrixViewPort(vp, lat, lon);
3156 }
3157#endif
3158
3159 LLBBox box = region.GetBox();
3160 int numtiles;
3161 int mem_used = 0;
3162 if (g_memCacheLimit > 0) {
3163 // GetMemoryStatus is slow on linux
3164 GetMemoryStatus(0, &mem_used);
3165 }
3166
3167 glTexTile **tiles = pTexFact->GetTiles(numtiles);
3168 for (int i = 0; i < numtiles; i++) {
3169 glTexTile *tile = tiles[i];
3170 if (region.IntersectOut(tile->box)) {
3171 /* user setting is in MB while we count exact bytes */
3172 bool bGLMemCrunch =
3173 g_tex_mem_used > g_GLOptions.m_iTextureMemorySize * 1024 * 1024;
3174 if (bGLMemCrunch) pTexFact->DeleteTexture(tile->rect);
3175 } else {
3176 bool texture = pTexFact->PrepareTexture(base_level, tile->rect,
3177 global_color_scheme, mem_used);
3178
3179 float *coords;
3180 if (use_norm_vp)
3181 coords = tile->m_coords;
3182 else {
3183 coords = new float[2 * tile->m_ncoords];
3184 for (int i = 0; i < tile->m_ncoords; i++) {
3185 wxPoint2DDouble p = vp.GetDoublePixFromLL(tile->m_coords[2 * i + 0],
3186 tile->m_coords[2 * i + 1]);
3187 coords[2 * i + 0] = p.m_x;
3188 coords[2 * i + 1] = p.m_y;
3189 }
3190 }
3191
3192#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
3193 RenderTextures(m_gldc, coords, tile->m_texcoords, 4,
3194 m_pParentCanvas->GetpVP());
3195#else
3196 if (!texture) { // failed to load, draw red
3197 glDisable(GL_TEXTURE_2D);
3198 glColor3f(1, 0, 0);
3199 }
3200
3201 glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), tile->m_texcoords);
3202 glVertexPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), coords);
3203 glDrawArrays(GL_QUADS, 0, tile->m_ncoords);
3204#endif
3205 if (!texture) glEnable(GL_TEXTURE_2D);
3206
3207 if (!use_norm_vp) delete[] coords;
3208 }
3209 }
3210
3211 glDisable(GL_TEXTURE_2D);
3212
3213#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
3214 if (use_norm_vp) glPopMatrix();
3215
3216 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
3217 glDisableClientState(GL_VERTEX_ARRAY);
3218#endif
3219}
3220
3221void glChartCanvas::RenderQuiltViewGL(ViewPort &vp,
3222 const OCPNRegion &rect_region) {
3223 if (!m_pParentCanvas->m_pQuilt->GetnCharts() ||
3224 m_pParentCanvas->m_pQuilt->IsBusy())
3225 return;
3226
3227 // render the quilt
3228 ChartBase *chart = m_pParentCanvas->m_pQuilt->GetFirstChart();
3229
3230 // Check the first, smallest scale chart
3231 if (chart) {
3232 // if( ! m_pParentCanvas->IsChartLargeEnoughToRender( chart, vp )
3233 // )
3234 // chart = NULL;
3235 }
3236
3237 LLRegion region = vp.GetLLRegion(rect_region);
3238
3239 LLRegion rendered_region;
3240 while (chart) {
3241 // This test does not need to be done for raster charts, since
3242 // we can assume that texture binding is acceptably fast regardless of the
3243 // render region, and that the quilt zoom methods choose a reasonable
3244 // reference chart.
3245 if (chart->GetChartFamily() != CHART_FAMILY_RASTER) {
3246 // if( ! m_pParentCanvas->IsChartLargeEnoughToRender(
3247 // chart, vp ) ) {
3248 // chart = m_pParentCanvas->m_pQuilt->GetNextChart();
3249 // continue;
3250 // }
3251 }
3252
3253 QuiltPatch *pqp = m_pParentCanvas->m_pQuilt->GetCurrentPatch();
3254 if (pqp->b_Valid) {
3255 LLRegion get_region = pqp->ActiveRegion;
3256 bool b_rendered = false;
3257
3258 if (!pqp->b_overlay) {
3259 get_region.Intersect(region);
3260 if (!get_region.Empty()) {
3261 if (chart->GetChartFamily() == CHART_FAMILY_RASTER) {
3262 ChartBaseBSB *Patch_Ch_BSB = dynamic_cast<ChartBaseBSB *>(chart);
3263 if (Patch_Ch_BSB) {
3264 SetClipRegion(vp, get_region /*pqp->quilt_region*/);
3265 RenderRasterChartRegionGL(chart, vp, pqp->ActiveRegion);
3266 DisableClipRegion();
3267
3268 b_rendered = true;
3269 } else if (chart->GetChartType() == CHART_TYPE_MBTILES) {
3270 SetClipRegion(vp, pqp->ActiveRegion /*pqp->quilt_region*/);
3271 chart->RenderRegionViewOnGL(*m_pcontext, vp, rect_region,
3272 get_region);
3273 DisableClipRegion();
3274 }
3275
3276 } else if (chart->GetChartFamily() == CHART_FAMILY_VECTOR) {
3277 if (chart->GetChartType() == CHART_TYPE_CM93COMP) {
3278 RenderNoDTA(vp, get_region);
3279 chart->RenderRegionViewOnGL(*m_pcontext, vp, rect_region,
3280 get_region);
3281 } else {
3282 s57chart *Chs57 = dynamic_cast<s57chart *>(chart);
3283 if (Chs57) {
3284 if (Chs57->m_RAZBuilt) {
3285 RenderNoDTA(vp, get_region);
3286 Chs57->RenderRegionViewOnGLNoText(*m_pcontext, vp,
3287 rect_region, get_region);
3288 DisableClipRegion();
3289 } else {
3290 // The SENC is quesed for building, so..
3291 // Show GSHHS with compatible color scheme in the meantime.
3292 ocpnDC gldc(*this);
3293 const LLRegion &oregion = get_region;
3294 LLBBox box = oregion.GetBox();
3295
3296 wxPoint p1 =
3297 vp.GetPixFromLL(box.GetMaxLat(), box.GetMinLon());
3298 wxPoint p2 =
3299 vp.GetPixFromLL(box.GetMaxLat(), box.GetMaxLon());
3300 wxPoint p3 =
3301 vp.GetPixFromLL(box.GetMinLat(), box.GetMaxLon());
3302 wxPoint p4 =
3303 vp.GetPixFromLL(box.GetMinLat(), box.GetMinLon());
3304
3305 wxRect srect(p1.x, p1.y, p3.x - p1.x, p4.y - p2.y);
3306
3307 bool world = false;
3308 ViewPort cvp = ClippedViewport(vp, get_region);
3309 if (m_pParentCanvas->GetWorldBackgroundChart()) {
3310 SetClipRegion(cvp, get_region);
3311 m_pParentCanvas->GetWorldBackgroundChart()->SetColorsDirect(
3312 GetGlobalColor(_T ( "LANDA" )),
3313 GetGlobalColor(_T ( "DEPMS" )));
3314 RenderWorldChart(gldc, cvp, srect, world);
3315 m_pParentCanvas->GetWorldBackgroundChart()->SetColorScheme(
3316 global_color_scheme);
3317 DisableClipRegion();
3318 }
3319 }
3320 } else {
3321 ChartPlugInWrapper *ChPI =
3322 dynamic_cast<ChartPlugInWrapper *>(chart);
3323 if (ChPI) {
3324 SetClipRegion(vp, get_region);
3325 RenderNoDTA(vp, get_region);
3326 ChPI->RenderRegionViewOnGLNoText(*m_pcontext, vp, rect_region,
3327 get_region);
3328 DisableClipRegion();
3329
3330 } else {
3331 SetClipRegion(vp, get_region);
3332 RenderNoDTA(vp, get_region);
3333 chart->RenderRegionViewOnGL(*m_pcontext, vp, rect_region,
3334 get_region);
3335 DisableClipRegion();
3336 }
3337 }
3338 }
3339 }
3340 }
3341 }
3342
3343 if (b_rendered) {
3344 // LLRegion get_region = pqp->ActiveRegion;
3345 // get_region.Intersect( Region ); not technically
3346 // required?
3347 // rendered_region.Union(get_region);
3348 }
3349 }
3350
3351 chart = m_pParentCanvas->m_pQuilt->GetNextChart();
3352 }
3353
3354 // Render any Overlay patches for s57 charts(cells)
3355 if (m_pParentCanvas->m_pQuilt->HasOverlays()) {
3356 ChartBase *pch = m_pParentCanvas->m_pQuilt->GetFirstChart();
3357 while (pch) {
3358 QuiltPatch *pqp = m_pParentCanvas->m_pQuilt->GetCurrentPatch();
3359 if (pqp->b_Valid && pqp->b_overlay &&
3360 pch->GetChartFamily() == CHART_FAMILY_VECTOR) {
3361 LLRegion get_region = pqp->ActiveRegion;
3362
3363 get_region.Intersect(region);
3364 if (!get_region.Empty()) {
3365 s57chart *Chs57 = dynamic_cast<s57chart *>(pch);
3366 if (Chs57)
3367 Chs57->RenderOverlayRegionViewOnGL(*m_pcontext, vp, rect_region,
3368 get_region);
3369 else {
3370 ChartPlugInWrapper *ChPI = dynamic_cast<ChartPlugInWrapper *>(pch);
3371 if (ChPI) {
3372 ChPI->RenderRegionViewOnGL(*m_pcontext, vp, rect_region,
3373 get_region);
3374 }
3375 }
3376 }
3377 }
3378
3379 pch = m_pParentCanvas->m_pQuilt->GetNextChart();
3380 }
3381 }
3382
3383 // Hilite rollover of standard chart key
3384 ViewPort vph = m_pParentCanvas->GetVP();
3385 for (auto &index : m_pParentCanvas->m_pQuilt->GetHiLiteIndexArray()) {
3386 const ChartTableEntry &cte = ChartData->GetChartTableEntry(index);
3387 LLRegion hiregion =
3388 m_pParentCanvas->m_pQuilt->GetChartQuiltRegion(cte, vph);
3389
3390 if (!hiregion.Empty()) {
3391 glEnable(GL_BLEND);
3392
3393 double hitrans;
3394 switch (global_color_scheme) {
3395 case GLOBAL_COLOR_SCHEME_DAY:
3396 hitrans = .4;
3397 break;
3398 case GLOBAL_COLOR_SCHEME_DUSK:
3399 hitrans = .2;
3400 break;
3401 case GLOBAL_COLOR_SCHEME_NIGHT:
3402 hitrans = .1;
3403 break;
3404 default:
3405 hitrans = .4;
3406 break;
3407 }
3408
3409#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
3410
3411 glColor4f((float).8, (float).4, (float).4, (float)hitrans);
3412#else
3413 s_regionColor = wxColor(204, 102, 102, hitrans * 256);
3414#endif
3415
3416 DrawRegion(vp, hiregion);
3417
3418 glDisable(GL_BLEND);
3419 }
3420 }
3421
3422#if 0
3423 LLRegion hiregion = m_pParentCanvas->m_pQuilt->GetHiliteRegion();
3424
3425 if (!hiregion.Empty()) {
3426 glEnable(GL_BLEND);
3427
3428 double hitrans;
3429 switch (global_color_scheme) {
3430 case GLOBAL_COLOR_SCHEME_DAY:
3431 hitrans = .4;
3432 break;
3433 case GLOBAL_COLOR_SCHEME_DUSK:
3434 hitrans = .2;
3435 break;
3436 case GLOBAL_COLOR_SCHEME_NIGHT:
3437 hitrans = .1;
3438 break;
3439 default:
3440 hitrans = .4;
3441 break;
3442 }
3443
3444//#ifndef USE_ANDROID_GLES2
3445#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
3446
3447 glColor4f((float).8, (float).4, (float).4, (float)hitrans);
3448#else
3449 s_regionColor = wxColor(204, 102, 102, hitrans * 256);
3450#endif
3451
3452 DrawRegion(vp, hiregion);
3453
3454 glDisable(GL_BLEND);
3455 }
3456#endif
3457
3458 m_pParentCanvas->m_pQuilt->SetRenderedVP(vp);
3459}
3460
3461void glChartCanvas::RenderQuiltViewGLText(ViewPort &vp,
3462 const OCPNRegion &rect_region) {
3463 if (!m_pParentCanvas->m_pQuilt->GetnCharts() ||
3464 m_pParentCanvas->m_pQuilt->IsBusy())
3465 return;
3466
3467 // render the quilt
3468 ChartBase *chart = m_pParentCanvas->m_pQuilt->GetLargestScaleChart();
3469
3470 LLRegion region = vp.GetLLRegion(rect_region);
3471
3472 LLRegion rendered_region;
3473 while (chart) {
3474 QuiltPatch *pqp = m_pParentCanvas->m_pQuilt->GetCurrentPatch();
3475 if (pqp->b_Valid) {
3476 LLRegion get_region = pqp->ActiveRegion;
3477
3478 if (!pqp->b_overlay) {
3479 if (chart->GetChartFamily() == CHART_FAMILY_VECTOR) {
3480 s57chart *Chs57 = dynamic_cast<s57chart *>(chart);
3481 if (Chs57) {
3482 Chs57->RenderViewOnGLTextOnly(*m_pcontext, vp);
3483 } else {
3484 ChartPlugInWrapper *ChPI =
3485 dynamic_cast<ChartPlugInWrapper *>(chart);
3486 if (ChPI) {
3487 ChPI->RenderRegionViewOnGLTextOnly(*m_pcontext, vp, rect_region);
3488 }
3489 }
3490 }
3491 }
3492 }
3493
3494 chart = m_pParentCanvas->m_pQuilt->GetNextSmallerScaleChart();
3495 }
3496
3497 /*
3498 // Render any Overlay patches for s57 charts(cells)
3499 if( m_pParentCanvas->m_pQuilt->HasOverlays() ) {
3500 ChartBase *pch = m_pParentCanvas->m_pQuilt->GetFirstChart();
3501 while( pch ) {
3502 QuiltPatch *pqp =
3503 m_pParentCanvas->m_pQuilt->GetCurrentPatch(); if( pqp->b_Valid &&
3504 pqp->b_overlay && pch->GetChartFamily() == CHART_FAMILY_VECTOR ) { LLRegion
3505 get_region = pqp->ActiveRegion;
3506
3507 get_region.Intersect( region );
3508 if( !get_region.Empty() ) {
3509 s57chart *Chs57 = dynamic_cast<s57chart*>( pch );
3510 if( Chs57 )
3511 Chs57->RenderOverlayRegionViewOnGL( *m_pcontext,
3512 vp, rect_region, get_region );
3513 }
3514 }
3515
3516 pch = m_pParentCanvas->m_pQuilt->GetNextChart();
3517 }
3518 }
3519 */
3520}
3521
3522void glChartCanvas::RenderCharts(ocpnDC &dc, const OCPNRegion &rect_region) {
3523 ViewPort &vp = m_pParentCanvas->VPoint;
3524
3525 // Only for cm93 (not quilted), SetVPParms can change the valid region of the
3526 // chart we need to know this before rendering the chart so we can compute the
3527 // background region and nodta regions correctly. I would prefer to just
3528 // perform this here (or in SetViewPoint) for all vector charts instead of in
3529 // their render routine, but how to handle quilted cases?
3530 if (!vp.b_quilt &&
3531 m_pParentCanvas->m_singleChart->GetChartType() == CHART_TYPE_CM93COMP)
3532 static_cast<cm93compchart *>(m_pParentCanvas->m_singleChart)
3533 ->SetVPParms(vp);
3534
3535 LLRegion chart_region;
3536 if (!vp.b_quilt &&
3537 (m_pParentCanvas->m_singleChart->GetChartType() == CHART_TYPE_PLUGIN)) {
3538 if (m_pParentCanvas->m_singleChart->GetChartFamily() ==
3539 CHART_FAMILY_RASTER) {
3540 // We do this the hard way, since PlugIn Raster charts do not understand
3541 // LLRegion yet...
3542 double ll[8];
3543 ChartPlugInWrapper *cpw =
3544 dynamic_cast<ChartPlugInWrapper *>(m_pParentCanvas->m_singleChart);
3545 if (!cpw) return;
3546
3547 cpw->chartpix_to_latlong(0, 0, ll + 0, ll + 1);
3548 cpw->chartpix_to_latlong(0, cpw->GetSize_Y(), ll + 2, ll + 3);
3549 cpw->chartpix_to_latlong(cpw->GetSize_X(), cpw->GetSize_Y(), ll + 4,
3550 ll + 5);
3551 cpw->chartpix_to_latlong(cpw->GetSize_X(), 0, ll + 6, ll + 7);
3552
3553 // for now don't allow raster charts to cross both 0 meridian and IDL
3554 // (complicated to deal with)
3555 for (int i = 1; i < 6; i += 2)
3556 if (fabs(ll[i] - ll[i + 2]) > 180) {
3557 // we detect crossing idl here, make all longitudes positive
3558 for (int i = 1; i < 8; i += 2)
3559 if (ll[i] < 0) ll[i] += 360;
3560 break;
3561 }
3562
3563 chart_region = LLRegion(4, ll);
3564 } else {
3565 Extent ext;
3566 m_pParentCanvas->m_singleChart->GetChartExtent(&ext);
3567
3568 double ll[8] = {ext.SLAT, ext.WLON, ext.SLAT, ext.ELON,
3569 ext.NLAT, ext.ELON, ext.NLAT, ext.WLON};
3570 chart_region = LLRegion(4, ll);
3571 }
3572 } else
3573 chart_region = vp.b_quilt
3574 ? m_pParentCanvas->m_pQuilt->GetFullQuiltRegion()
3575 : m_pParentCanvas->m_singleChart->GetValidRegion();
3576
3577 bool world_view = false;
3578 for (OCPNRegionIterator upd(rect_region); upd.HaveRects(); upd.NextRect()) {
3579 wxRect rect = upd.GetRect();
3580 LLRegion background_region = vp.GetLLRegion(rect);
3581 // Remove the valid chart area to find the region NOT covered by the
3582 // charts
3583 background_region.Subtract(chart_region);
3584
3585 if (!background_region.Empty()) {
3586 ViewPort cvp = ClippedViewport(vp, background_region);
3587 SetClipRect(cvp, rect, false);
3588 RenderWorldChart(dc, cvp, rect, world_view);
3589 DisableClipRegion();
3590 }
3591 }
3592
3593 if (vp.b_quilt)
3594 RenderQuiltViewGL(vp, rect_region);
3595 else {
3596 LLRegion region = vp.GetLLRegion(rect_region);
3597 if (m_pParentCanvas->m_singleChart->GetChartFamily() ==
3598 CHART_FAMILY_RASTER) {
3599 if (m_pParentCanvas->m_singleChart->GetChartType() == CHART_TYPE_MBTILES)
3600 m_pParentCanvas->m_singleChart->RenderRegionViewOnGL(
3601 *m_pcontext, vp, rect_region, region);
3602 else
3603 RenderRasterChartRegionGL(m_pParentCanvas->m_singleChart, vp, region);
3604 } else if (m_pParentCanvas->m_singleChart->GetChartFamily() ==
3605 CHART_FAMILY_VECTOR) {
3606 chart_region.Intersect(region);
3607 RenderNoDTA(vp, chart_region);
3608 m_pParentCanvas->m_singleChart->RenderRegionViewOnGL(*m_pcontext, vp,
3609 rect_region, region);
3610 }
3611 }
3612 glUseProgram(0);
3613}
3614
3615void glChartCanvas::RenderNoDTA(ViewPort &vp, const LLRegion &region,
3616 int transparency) {
3617 wxColour color = GetGlobalColor(_T ( "NODTA" ));
3618#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
3619 if (color.IsOk())
3620 glColor4ub(color.Red(), color.Green(), color.Blue(), transparency);
3621 else
3622 glColor4ub(163, 180, 183, transparency);
3623
3624 glEnable(GL_BLEND);
3625 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
3626
3627#else
3628 // Store the color for tesselator callback pickup.
3629 s_regionColor = color;
3630#endif
3631
3632 DrawRegion(vp, region);
3633}
3634
3635/* render world chart, but only in this rectangle */
3636void glChartCanvas::RenderWorldChart(ocpnDC &dc, ViewPort &vp, wxRect &rect,
3637 bool &world_view) {
3638 // set gl color to water
3639 wxColour water = m_pParentCanvas->pWorldBackgroundChart->water;
3640
3641 glEnable(GL_SCISSOR_TEST);
3642 glScissor(rect.x, vp.pix_height - rect.height - rect.y, rect.width,
3643 rect.height);
3644
3645 // clear background
3646 if (!world_view) {
3647 if (!world_view) {
3648 int x1 = rect.x, y1 = rect.y, x2 = x1 + rect.width, y2 = y1 + rect.height;
3649#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
3650
3651 GLShaderProgram *shader = pcolor_tri_shader_program[GetCanvasIndex()];
3652 shader->Bind();
3653
3654 float colorv[4];
3655 colorv[0] = water.Red() / float(256);
3656 colorv[1] = water.Green() / float(256);
3657 colorv[2] = water.Blue() / float(256);
3658 colorv[3] = 1.0;
3659 shader->SetUniform4fv("color", colorv);
3660
3661 float pf[8];
3662 pf[0] = x2;
3663 pf[1] = y1;
3664 pf[2] = x2;
3665 pf[3] = y2;
3666 pf[4] = x1;
3667 pf[5] = y1;
3668 pf[6] = x1;
3669 pf[7] = y2;
3670 shader->SetAttributePointerf("position", pf);
3671
3672 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
3673
3674 shader->UnBind();
3675
3676#else
3677#endif
3678 }
3679 }
3680
3681 // m_pParentCanvas->pWorldBackgroundChart->RenderViewOnDC(dc, vp);
3682 gShapeBasemap.RenderViewOnDC(dc, vp);
3683
3684 glDisable(GL_SCISSOR_TEST);
3685}
3686
3687/* these are the overlay objects which move with the charts and
3688 are not frequently updated (not ships etc..)
3689
3690 many overlay objects are fixed to a geographical location or
3691 grounded as opposed to the floating overlay objects. */
3692void glChartCanvas::DrawGroundedOverlayObjects(ocpnDC &dc, ViewPort &vp) {
3693 m_pParentCanvas->RenderAllChartOutlines(dc, vp);
3694
3695 DrawStaticRoutesTracksAndWaypoints(vp);
3696
3697 DisableClipRegion();
3698}
3699
3700void glChartCanvas::DrawGLTidesInBBox(ocpnDC &dc, LLBBox &BBox) {
3701 // At small scale, we render the Tide icon as a texture for best performance
3702 if (m_pParentCanvas->GetVP().chart_scale > 500000) {
3703 // Prepare the texture if necessary
3704
3705 if (!m_tideTex) {
3706 wxBitmap bmp = m_pParentCanvas->GetTideBitmap();
3707 if (!bmp.Ok()) return;
3708
3709 wxImage image = bmp.ConvertToImage();
3710 int w = image.GetWidth(), h = image.GetHeight();
3711
3712 int tex_w, tex_h;
3713 if (g_texture_rectangle_format == GL_TEXTURE_2D)
3714 tex_w = w, tex_h = h;
3715 else
3716 tex_w = NextPow2(w), tex_h = NextPow2(h);
3717
3718 m_tideTexWidth = tex_w;
3719 m_tideTexHeight = tex_h;
3720
3721 unsigned char *d = image.GetData();
3722 unsigned char *a = image.GetAlpha();
3723
3724 unsigned char mr, mg, mb;
3725 if (!a) image.GetOrFindMaskColour(&mr, &mg, &mb);
3726
3727 unsigned char *e = new unsigned char[4 * w * h];
3728 if (e && d) {
3729 for (int y = 0; y < h; y++)
3730 for (int x = 0; x < w; x++) {
3731 unsigned char r, g, b;
3732 int off = (y * w + x);
3733 r = d[off * 3 + 0];
3734 g = d[off * 3 + 1];
3735 b = d[off * 3 + 2];
3736
3737 e[off * 4 + 0] = r;
3738 e[off * 4 + 1] = g;
3739 e[off * 4 + 2] = b;
3740
3741 e[off * 4 + 3] =
3742 a ? a[off] : ((r == mr) && (g == mg) && (b == mb) ? 0 : 255);
3743 }
3744 }
3745
3746 glGenTextures(1, &m_tideTex);
3747
3748 glBindTexture(GL_TEXTURE_2D, m_tideTex);
3749 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3750 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3751
3752 if (g_texture_rectangle_format == GL_TEXTURE_2D)
3753 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA,
3754 GL_UNSIGNED_BYTE, e);
3755 else {
3756 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex_w, tex_h, 0, GL_RGBA,
3757 GL_UNSIGNED_BYTE, 0);
3758 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE,
3759 e);
3760 }
3761
3762 delete[] e;
3763 }
3764
3765 // Texture is ready
3766
3767 glBindTexture(GL_TEXTURE_2D, m_tideTex);
3768 glEnable(GL_TEXTURE_2D);
3769 glEnable(GL_BLEND);
3770
3771#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
3772#else
3773 for (int i = 1; i < ptcmgr->Get_max_IDX() + 1; i++) {
3774 const IDX_entry *pIDX = ptcmgr->GetIDX_entry(i);
3775
3776 char type = pIDX->IDX_type; // Entry "TCtcIUu" identifier
3777 if ((type == 't') || (type == 'T')) // only Tides
3778 {
3779 double lon = pIDX->IDX_lon;
3780 double lat = pIDX->IDX_lat;
3781
3782 if (BBox.Contains(lat, lon)) {
3783 wxPoint r;
3784 m_pParentCanvas->GetCanvasPointPix(lat, lon, &r);
3785
3786 float xp = r.x;
3787 float yp = r.y;
3788
3789 double scale = 1.0;
3790#ifdef __ANDROID__
3791 scale *= getAndroidDisplayDensity();
3792#endif
3793 double width2 = scale * m_tideTexWidth / 2;
3794 double height2 = scale * m_tideTexHeight / 2;
3795
3796 float coords[8];
3797 float uv[8];
3798
3799 // normal uv
3800 uv[0] = 0;
3801 uv[1] = 0;
3802 uv[2] = 0;
3803 uv[3] = 1;
3804 uv[4] = 1;
3805 uv[5] = 1;
3806 uv[6] = 1;
3807 uv[7] = 0;
3808
3809 coords[0] = xp - width2;
3810 coords[1] = yp - height2;
3811 coords[2] = xp - width2;
3812 coords[3] = yp + height2;
3813 coords[4] = xp + width2;
3814 coords[5] = yp + height2;
3815 coords[6] = xp + width2;
3816 coords[7] = yp - height2;
3817
3818 RenderTextures(dc, coords, uv, 4, m_pParentCanvas->GetpVP());
3819 }
3820 } // type 'T"
3821 } // loop
3822
3823#endif
3824
3825 glDisable(GL_TEXTURE_2D);
3826 glDisable(GL_BLEND);
3827 glBindTexture(GL_TEXTURE_2D, 0);
3828 } else
3829 m_pParentCanvas->DrawAllTidesInBBox(dc, BBox);
3830}
3831
3832void glChartCanvas::DrawGLCurrentsInBBox(ocpnDC &dc, LLBBox &BBox) {
3833 m_pParentCanvas->DrawAllCurrentsInBBox(dc, BBox);
3834}
3835
3836void glChartCanvas::SetColorScheme(ColorScheme cs) {
3837 if (!m_bsetup) return;
3838
3839 glDeleteTextures(1, &m_tideTex);
3840 glDeleteTextures(1, &m_currentTex);
3841 m_tideTex = 0;
3842 m_currentTex = 0;
3843 ownship_color = -1;
3844}
3845
3846void glChartCanvas::RenderGLAlertMessage() {
3847 if (!m_pParentCanvas->GetAlertString().IsEmpty()) {
3848 wxString msg = m_pParentCanvas->GetAlertString();
3849
3850 wxFont *pfont = GetOCPNScaledFont(_("Dialog"));
3851 m_gldc.SetFont(*pfont);
3852
3853 int w, h;
3854 wxScreenDC sdc;
3855 sdc.GetTextExtent(msg, &w, &h, NULL, NULL, pfont);
3856
3857 h += 2;
3858 w += 4;
3859 int yp =
3860 m_pParentCanvas->VPoint.pix_height - GetChartbarHeight() - h - (h / 4);
3861
3862 wxRect sbr = m_pParentCanvas->GetScaleBarRect();
3863 int xp = sbr.x + sbr.width + 5;
3864
3865 wxPen ppPen1(GetGlobalColor(_T ( "UBLCK" )), 1, wxPENSTYLE_SOLID);
3866 m_gldc.SetPen(ppPen1);
3867 m_gldc.SetBrush(wxBrush(GetGlobalColor(_T ( "YELO1" ))));
3868
3869 m_gldc.DrawRectangle(xp, yp, w, h);
3870
3871 m_gldc.DrawText(msg, xp, yp);
3872 }
3873}
3874
3875unsigned long quiltHash;
3876int refChartIndex;
3877extern wxLongLong s_t0;
3878
3879int n_render;
3880void glChartCanvas::Render() {
3881 if (!m_bsetup || !m_pParentCanvas->m_pQuilt ||
3882 (m_pParentCanvas->VPoint.b_quilt && !m_pParentCanvas->m_pQuilt) ||
3883 (!m_pParentCanvas->VPoint.b_quilt && !m_pParentCanvas->m_singleChart)) {
3884#ifdef __WXGTK__ // for some reason in gtk, a swap is needed here to get an
3885 // initial screen update
3886 SwapBuffers();
3887#endif
3888 if (!g_PrintingInProgress) return;
3889 }
3890
3891 if (m_binPinch) return;
3892
3893#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
3894 loadShaders(GetCanvasIndex());
3895 configureShaders(m_pParentCanvas->VPoint);
3896#endif
3897
3898#ifdef USE_ANDROID_GLES2
3899
3900 OCPNStopWatch sw;
3901
3902 if (m_binPinch) return;
3903
3904 // qDebug() << "Render" << m_pParentCanvas->m_canvasIndex << GetPosition().x
3905 // << GetSize().x << m_pParentCanvas->GetPosition().x << m_pcontext;
3906
3907 // if(m_pParentCanvas->m_canvasIndex == 0) return;
3908
3909 // Do any setup required...
3910
3911 bool recompose = false;
3912 if (m_pParentCanvas->VPoint.b_quilt && m_pParentCanvas->m_pQuilt &&
3913 !m_pParentCanvas->m_pQuilt->IsComposed()) {
3914 if (m_pParentCanvas->VPoint.IsValid()) {
3915 m_pParentCanvas->m_pQuilt->Compose(m_pParentCanvas->VPoint);
3916 m_pParentCanvas->UpdateCanvasControlBar();
3917 recompose = true;
3918 } else
3919 return;
3920 }
3921
3922 // Check to see if the Compose() call forced a SENC build.
3923 // If so, zoom the canvas just slightly to force a deferred redraw of the
3924 // full screen.
3925 if (sw.GetTime() > 2000) { // long enough to detect SENC build.
3926 m_pParentCanvas->ZoomCanvas(1.0001, false);
3927 }
3928
3929 // qDebug() << "RenderTime1" << sw.GetTime();
3930
3931 s_tess_vertex_idx = 0;
3932 quiltHash = m_pParentCanvas->m_pQuilt->GetXStackHash();
3933 refChartIndex = m_pParentCanvas->m_pQuilt->GetRefChartdbIndex();
3934
3935#endif
3936
3937#ifdef __WXOSX__
3938 // Support scaled HDPI displays.
3939 m_displayScale = GetContentScaleFactor();
3940#endif
3941 m_pParentCanvas->VPoint.SetPixelScale(m_displayScale);
3942
3943 m_last_render_time = wxDateTime::Now().GetTicks();
3944
3945 // we don't care about jobs that are now off screen
3946 // clear out and it will be repopulated during render
3947 if (g_GLOptions.m_bTextureCompression &&
3948 !g_GLOptions.m_bTextureCompressionCaching)
3949 g_glTextureManager->ClearJobList();
3950
3951 ocpnDC gldc(*this);
3952
3953 int gl_width, gl_height;
3954 gl_width = m_pParentCanvas->VPoint.pix_width;
3955 gl_height = m_pParentCanvas->VPoint.pix_height;
3956
3957 // Take a copy for use later by DC
3958 m_glcanvas_width = gl_width;
3959 m_glcanvas_height = gl_height;
3960
3961 // Avoid some harmonic difficulties with odd-size glCanvas
3962 bool b_odd = false;
3963 if (gl_height & 1) {
3964 gl_height -= 1;
3965 ViewPort *vp = m_pParentCanvas->GetpVP();
3966 vp->pix_height = gl_height;
3967 b_odd = true;
3968 }
3969
3970 if (gl_width & 1) {
3971 gl_width -= 1;
3972 ViewPort *vp = m_pParentCanvas->GetpVP();
3973 vp->pix_width = gl_width;
3974 b_odd = true;
3975 }
3976
3977 // Set the shader viewport transform matrix
3978 // Using the adjusted dimensions
3979 if (b_odd) {
3980 ViewPort *vp = m_pParentCanvas->GetpVP();
3981 mat4x4 m;
3982 mat4x4_identity(m);
3983 mat4x4_scale_aniso((float(*)[4])vp->vp_matrix_transform, m,
3984 2.0 / (float)vp->pix_width, -2.0 / (float)vp->pix_height,
3985 1.0);
3986 mat4x4_translate_in_place((float(*)[4])vp->vp_matrix_transform,
3987 -vp->pix_width / 2, -vp->pix_height / 2, 0);
3988 }
3989
3990 // @todo: If the intention was to work with the same ViewPort object, use a
3991 // reference instead. Making a copy of VPoint here means that any changes to
3992 // VPoint will not affect m_pParentCanvas->VPoint. It's not clear if this is
3993 // the intended behavior.
3994 ViewPort VPoint = m_pParentCanvas->VPoint;
3995
3996 OCPNRegion screen_region(wxRect(0, 0, gl_width, gl_height));
3997 glViewport(0, 0, (GLint)gl_width, (GLint)gl_height);
3998
3999// #ifndef USE_ANDROID_GLES2
4000#if !defined(USE_ANDROID_GLES2)
4001 glMatrixMode(GL_PROJECTION);
4002 glLoadIdentity();
4003
4004 glOrtho(0, (GLint)gl_width, (GLint)gl_height, 0, -1, 1);
4005 glMatrixMode(GL_MODELVIEW);
4006 glLoadIdentity();
4007#endif
4008
4009 if (s_b_useStencil) {
4010 glEnable(GL_STENCIL_TEST);
4011 glStencilMask(0xff);
4012 glClear(GL_STENCIL_BUFFER_BIT);
4013 glDisable(GL_STENCIL_TEST);
4014 }
4015
4016 // set opengl settings that don't normally change
4017 // this should be able to go in SetupOpenGL, but it's
4018 // safer here incase a plugin mangles these
4019 if (g_GLOptions.m_GLLineSmoothing) glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
4020 if (g_GLOptions.m_GLPolygonSmoothing)
4021 glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
4022 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
4023
4024 // Delete any textures known to the GPU that
4025 // belong to charts which will not be used in this render
4026 // This is done chart-by-chart...later we will scrub for unused textures
4027 // that belong to charts which ARE used in this render, if we need to....
4028
4029 g_glTextureManager->TextureCrunch(0.8);
4030
4031 // If we plan to post process the display, don't use accelerated panning
4032 double scale_factor = VPoint.ref_scale / VPoint.chart_scale;
4033
4034 bool bpost_hilite = !m_pParentCanvas->m_pQuilt->GetHiliteRegion().Empty();
4035 bool useFBO = false;
4036 int sx = gl_width;
4037 int sy = gl_height;
4038
4039 // Try to use the framebuffer object's cache of the last frame
4040 // to accelerate drawing this frame (if overlapping)
4041 if (m_b_BuiltFBO && !bpost_hilite
4042 //&& VPoint.tilt == 0 // disabling fbo in tilt mode gives better quality
4043 // but slower
4044 ) {
4045 // Is this viewpoint the same as the previously painted one?
4046 bool b_newview = true;
4047 bool b_full = false;
4048
4049 // If the view is the same we do no updates,
4050 // Just render cached texture to the framebuffer
4051 if (m_cache_vp.view_scale_ppm == VPoint.view_scale_ppm &&
4052 m_cache_vp.rotation == VPoint.rotation &&
4053 m_cache_vp.clat == VPoint.clat && m_cache_vp.clon == VPoint.clon &&
4054 m_cache_vp.IsValid() && m_cache_vp.pix_height == VPoint.pix_height &&
4055 m_cache_current_ch == m_pParentCanvas->m_singleChart) {
4056 b_newview = false;
4057 }
4058
4059#ifdef USE_ANDROID_GLES2
4060 if (recompose) b_newview = true;
4061
4062 if (m_bforcefull) {
4063 b_newview = true;
4064 b_full = true;
4065 }
4066
4067 // If no charts are to be rendered, we need to refresh the entire display
4068 // This fixes a problem with routes/tracks/marks rendering on pans at very
4069 // small scale. It is a workaround, so finding root cause should be
4070 // considered a TODO
4071
4072 if (VPoint.b_quilt) {
4073 ChartBase *chart = m_pParentCanvas->m_pQuilt->GetFirstChart();
4074 if (!chart) b_full = true;
4075 }
4076
4077#endif
4078
4079 if (b_newview) {
4080 float dx = 0;
4081 float dy = 0;
4082
4083 bool accelerated_pan = false;
4084 // if (g_in_inertia)
4085 // printf("--- accpan condition %d %d\n",
4086 // g_GLOptions.m_bUseAcceleratedPanning,
4087 // m_cache_vp.IsValid());
4088 // else
4089 // printf("||| accpan condition %d %d\n",
4090 // g_GLOptions.m_bUseAcceleratedPanning,
4091 // m_cache_vp.IsValid());
4092
4093 if (g_GLOptions.m_bUseAcceleratedPanning && m_cache_vp.IsValid() &&
4094 (VPoint.m_projection_type == PROJECTION_MERCATOR ||
4095 VPoint.m_projection_type == PROJECTION_EQUIRECTANGULAR) &&
4096 m_cache_vp.pix_height == VPoint.pix_height) {
4097 wxPoint2DDouble c_old =
4098 VPoint.GetDoublePixFromLL(VPoint.clat, VPoint.clon) *
4099 m_displayScale;
4100 wxPoint2DDouble c_new =
4101 m_cache_vp.GetDoublePixFromLL(VPoint.clat, VPoint.clon) *
4102 m_displayScale;
4103
4104 dy = wxRound(c_new.m_y - c_old.m_y);
4105 dx = wxRound(c_new.m_x - c_old.m_x);
4106
4107 // The math below using the previous frame's texture does not really
4108 // work for sub-pixel pans.
4109 // TODO is to rethink this.
4110 // Meanwhile, require the accelerated pans to be whole pixel multiples
4111 // only. This is not as bad as it sounds. Keyboard and mouse pans are
4112 // whole_pixel moves. However, autofollow at large scale is certainly
4113 // not.
4114
4115 double deltax = c_new.m_x - c_old.m_x;
4116 double deltay = c_new.m_y - c_old.m_y;
4117
4118 bool b_whole_pixel = true;
4119 if ((fabs(deltax - dx) > 1e-2) || (fabs(deltay - dy) > 1e-2))
4120 b_whole_pixel = false;
4121
4122 accelerated_pan = b_whole_pixel && abs(dx) < m_cache_tex_x &&
4123 abs(dy) < m_cache_tex_y &&
4124 (abs(dx) > 0 || (abs(dy) > 0));
4125
4126 // if (g_in_inertia && !accelerated_pan)
4127 // printf("--- accpan %d %d %g %g\n", accelerated_pan,
4128 // b_whole_pixel, dx, dy);
4129 }
4130
4131 // FBO swapping has trouble with Retina display on MacOS Monterey.
4132 // So, disable accelerated pan ops on this case.
4133 if (m_displayScale > 1) accelerated_pan = false;
4134
4135 // FIXME (dave) There are some display artifact troubles using accPan on
4136 // rotation.
4137 // Especially seen on sparse RNC rendering
4138 if (fabs(VPoint.rotation) > 0) accelerated_pan = false;
4139
4140 // do we allow accelerated panning? can we perform it here?
4141#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
4142#else // GLES2
4143 // enable rendering to texture in framebuffer object
4144 glBindFramebuffer(GL_FRAMEBUFFER, m_fb0);
4145
4146 if (VPoint.chart_scale < 5000) b_full = true;
4147
4148 if (VPoint.chart_scale > 5e7) b_full = true;
4149
4150 if (b_full) accelerated_pan = false;
4151
4152 if (accelerated_pan) {
4153 if ((dx != 0) || (dy != 0)) { // Anything to do?
4154
4155 // calculate the new regions to render
4156 // add extra pixels to avoid coordindate rounding issues at large
4157 // scale
4158 OCPNRegion update_region;
4159
4160 int fluff = 2;
4161
4162 // Avoid rendering artifacts caused by Multi Sampling (MSAA)
4163 if (VPoint.chart_scale < 10000) fluff = 8;
4164
4165 if (dy > 0 && dy < gl_height)
4166 update_region.Union(
4167 wxRect(0, gl_height - (dy + fluff), gl_width, dy + fluff));
4168 else if (dy < 0)
4169 update_region.Union(wxRect(0, 0, gl_width, -dy + fluff));
4170
4171 if (dx > 0 && dx < gl_width)
4172 update_region.Union(
4173 wxRect(gl_width - (dx + fluff), 0, dx + fluff, gl_height));
4174 else if (dx < 0)
4175 update_region.Union(wxRect(0, 0, -dx + fluff, gl_height));
4176
4177 m_cache_page = !m_cache_page; /* page flip */
4178
4179 // Bind the destination (target frame) texture to the frame buffer
4180 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
4181 GL_TEXTURE_2D, m_cache_tex[m_cache_page], 0);
4182
4183 // Before rendering anything, clear the color buffers
4184 // wxColour color = GetGlobalColor(_T ( "NODTA" ));
4185 // glClearColor(color.Red() / 256., color.Green() / 256.,
4186 // color.Blue() / 256., 1.0);
4187 // glClear(GL_COLOR_BUFFER_BIT);
4188
4189 // First render the new content into the update region
4190 // if (g_in_inertia)
4191 // printf("--- R2a %g\n",
4192 // (wxGetLocalTimeMillis() - s_t0).ToDouble());
4193 RenderCharts(m_gldc, update_region);
4194 // if (g_in_inertia)
4195 // printf("--- R2b %g\n",
4196 // (wxGetLocalTimeMillis() - s_t0).ToDouble());
4197 glDisable(g_texture_rectangle_format);
4198 glUseProgram(0);
4199
4200 // Next, render the cached texture as quad to FBO(m_blit_tex) with
4201 // offsets
4202 glBindTexture(GL_TEXTURE_2D, m_cache_tex[!m_cache_page]);
4203 glEnable(GL_TEXTURE_2D);
4204
4205 // Blit the existing content onto the alternate FBO, at the correct
4206 // location
4207 float x1, x2, y1, y2;
4208
4209 if (dx > 0)
4210 x1 = dx, x2 = 0;
4211 else
4212 x1 = 0, x2 = -dx;
4213
4214 if (dy > 0)
4215 y1 = dy, y2 = 0;
4216 else
4217 y1 = 0, y2 = -dy;
4218
4219 // normalize to texture coordinates range from 0 to 1
4220 float tx1, tx2, ty1, ty2;
4221
4222 float xcor = 0;
4223 float ycor = 0;
4224
4225 tx1 = 0;
4226 tx2 = sx / (float)m_cache_tex_x;
4227 ty1 = 0;
4228 ty2 = sy / (float)m_cache_tex_y;
4229
4230 float coords[8];
4231 float uv[8];
4232
4233 // normal uv
4234 uv[0] = tx1;
4235 uv[1] = ty1;
4236 uv[2] = tx2;
4237 uv[3] = ty1;
4238 uv[4] = tx2;
4239 uv[5] = ty2;
4240 uv[6] = tx1;
4241 uv[7] = ty2;
4242
4243 coords[0] = -dx;
4244 coords[1] = dy;
4245 coords[2] = -dx + sx;
4246 coords[3] = dy;
4247 coords[4] = -dx + sx;
4248 coords[5] = dy + sy;
4249 coords[6] = -dx;
4250 coords[7] = dy + sy;
4251
4252 GLShaderProgram *shader =
4253 ptexture_2D_shader_program[GetCanvasIndex()];
4254 shader->Bind();
4255
4256 // Set up the texture sampler to texture unit 0
4257 shader->SetUniform1i("uTex", 0);
4258
4259 mat4x4 m, mvp, I;
4260 mat4x4_identity(m);
4261 mat4x4_scale_aniso(mvp, m, 2.0 / (float)sx, 2.0 / (float)sy, 1.0);
4262 mat4x4_translate_in_place(mvp, -(float)sx / 2, -(float)sy / 2, 0);
4263 shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)mvp);
4264 mat4x4_identity(I);
4265 shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)I);
4266
4267 float co1[8];
4268 co1[0] = coords[0];
4269 co1[1] = coords[1];
4270 co1[2] = coords[2];
4271 co1[3] = coords[3];
4272 co1[4] = coords[6];
4273 co1[5] = coords[7];
4274 co1[6] = coords[4];
4275 co1[7] = coords[5];
4276
4277 float tco1[8];
4278 tco1[0] = uv[0];
4279 tco1[1] = uv[1];
4280 tco1[2] = uv[2];
4281 tco1[3] = uv[3];
4282 tco1[4] = uv[6];
4283 tco1[5] = uv[7];
4284 tco1[6] = uv[4];
4285 tco1[7] = uv[5];
4286
4287 shader->SetAttributePointerf("aPos", co1);
4288 shader->SetAttributePointerf("aUV", tco1);
4289
4290 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
4291
4292 // restore the shader matrix
4293 shader->SetUniformMatrix4fv("MVMatrix",
4294 (GLfloat *)VPoint.vp_matrix_transform);
4295
4296 shader->UnBind();
4297 glBindTexture(g_texture_rectangle_format, 0);
4298
4299 glDisable(g_texture_rectangle_format);
4300 glUseProgram(0);
4301 }
4302
4303 } // accelerated pan
4304
4305 else { // must redraw the entire screen
4306 // qDebug() << "Fullpage";
4307 glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0,
4308 g_texture_rectangle_format,
4309 m_cache_tex[!m_cache_page], 0);
4310
4311 m_fbo_offsetx = 0;
4312 m_fbo_offsety = 0;
4313 m_fbo_swidth = sx;
4314 m_fbo_sheight = sy;
4315
4316 // FIXME (dave) test on Android
4317 // This can be annoying on Android pinch zoom
4318
4319 // Clear the screen to NODTA color
4320 wxColour color = GetGlobalColor(_T ( "NODTA" ));
4321 glClearColor(color.Red() / 256., color.Green() / 256.,
4322 color.Blue() / 256., 1.0);
4323 glClear(GL_COLOR_BUFFER_BIT);
4324
4325 OCPNRegion rscreen_region(VPoint.rv_rect);
4326 // if (g_in_inertia)
4327 // printf("--- R2c %g\n",
4328 // (wxGetLocalTimeMillis() - s_t0).ToDouble());
4329 RenderCharts(m_gldc, rscreen_region);
4330 // if (g_in_inertia)
4331 // printf("--- R2d %g\n",
4332 // (wxGetLocalTimeMillis() - s_t0).ToDouble());
4333
4334 m_cache_page = !m_cache_page; /* page flip */
4335
4336 } // full page render
4337
4338 // Disable Render to FBO
4339 glBindFramebuffer(GL_FRAMEBUFFER, 0);
4340
4341#endif // gles2 for accpan
4342
4343 } // newview
4344
4345 useFBO = true;
4346 }
4347
4348#ifndef __ANDROID__
4349 if (VPoint.tilt) {
4350 glMatrixMode(GL_PROJECTION);
4351 glLoadIdentity();
4352
4353 gluPerspective(2 * 180 / PI * atan2((double)gl_height, (double)gl_width),
4354 (GLfloat)gl_width / (GLfloat)gl_height, 1, gl_width);
4355
4356 glMatrixMode(GL_MODELVIEW);
4357 glLoadIdentity();
4358
4359 glScalef(1, -1, 1);
4360 glTranslatef(-gl_width / 2, -gl_height / 2, -gl_width / 2);
4361 glRotated(VPoint.tilt * 180 / PI, 1, 0, 0);
4362
4363 glGetIntegerv(GL_VIEWPORT, viewport);
4364 glGetDoublev(GL_PROJECTION_MATRIX, projmatrix);
4365 glGetDoublev(GL_MODELVIEW_MATRIX, mvmatrix);
4366 }
4367#endif
4368
4369 // if (g_in_inertia)
4370 // printf("--- R3 %g\n", (wxGetLocalTimeMillis() - s_t0).ToDouble());
4371
4372 if (useFBO) {
4373#if 0 // #ifndef USE_ANDROID_GLES2
4374 glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fb0);
4375 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
4376 glBlitFramebuffer(0, 0, sx, sy, 0, 0, sx*2, sy*2, GL_COLOR_BUFFER_BIT, GL_LINEAR);
4377
4378 glBindFramebuffer(GL_FRAMEBUFFER, 0);
4379
4380#else
4381 // Render the cached texture as quad to screen
4382 glBindTexture(g_texture_rectangle_format, m_cache_tex[m_cache_page]);
4383 glEnable(g_texture_rectangle_format);
4384
4385 float tx, ty, tx0, ty0, divx, divy;
4386
4387 // Normalize, or not?
4388 if (GL_TEXTURE_RECTANGLE_ARB == g_texture_rectangle_format) {
4389 divx = divy = 1.0f;
4390 } else {
4391 divx = m_cache_tex_x;
4392 divy = m_cache_tex_y;
4393 }
4394
4395 tx0 = m_fbo_offsetx / divx;
4396 ty0 = m_fbo_offsety / divy;
4397 tx = (m_fbo_offsetx + m_fbo_swidth) / divx;
4398 ty = (m_fbo_offsety + m_fbo_sheight) / divy;
4399
4400 float coords[8];
4401 float uv[8];
4402
4403 // normal uv
4404 uv[0] = tx0;
4405 uv[1] = ty;
4406 uv[2] = tx;
4407 uv[3] = ty;
4408 uv[4] = tx;
4409 uv[5] = ty0;
4410 uv[6] = tx0;
4411 uv[7] = ty0;
4412
4413 // pixels
4414 coords[0] = 0;
4415 coords[1] = 0;
4416 coords[2] = sx;
4417 coords[3] = 0;
4418 coords[4] = sx;
4419 coords[5] = sy;
4420 coords[6] = 0;
4421 coords[7] = sy;
4422
4423 wxColour color = GetGlobalColor(_T ( "NODTA" ));
4424 glClearColor(color.Red() / 256., color.Green() / 256., color.Blue() / 256.,
4425 1.0);
4426 glClear(GL_COLOR_BUFFER_BIT);
4427
4428 RenderTextures(gldc, coords, uv, 4, m_pParentCanvas->GetpVP());
4429#endif
4430
4431 glDisable(g_texture_rectangle_format);
4432
4433 m_cache_vp = VPoint;
4434 m_cache_vp.Validate();
4435
4436 m_cache_current_ch = m_pParentCanvas->m_singleChart;
4437
4438 if (VPoint.b_quilt) m_pParentCanvas->m_pQuilt->SetRenderedVP(VPoint);
4439
4440 } else // useFBO
4441 {
4442 RenderCharts(m_gldc, screen_region);
4443 }
4444
4445 // Done with base charts.
4446 // Now the overlays
4447 RenderS57TextOverlay(VPoint);
4448 RenderMBTilesOverlay(VPoint);
4449
4450 g_overlayCanvas = m_pParentCanvas;
4451 if (g_pi_manager) {
4452 g_pi_manager->SendViewPortToRequestingPlugIns(VPoint);
4453 g_pi_manager->RenderAllGLCanvasOverlayPlugIns(
4454 m_pcontext, VPoint, m_pParentCanvas->m_canvasIndex, OVERLAY_CHARTS);
4455 }
4456
4457 // Render static overlay objects
4458 for (OCPNRegionIterator upd(screen_region); upd.HaveRects(); upd.NextRect()) {
4459 wxRect rt = upd.GetRect();
4460 LLRegion region = VPoint.GetLLRegion(rt);
4461 ViewPort cvp = ClippedViewport(VPoint, region);
4462 DrawGroundedOverlayObjects(gldc, cvp);
4463 }
4464
4465 if (m_pParentCanvas->m_bShowTide || m_pParentCanvas->m_bShowCurrent) {
4466 LLRegion screenLLRegion = VPoint.GetLLRegion(screen_region);
4467 LLBBox screenBox = screenLLRegion.GetBox();
4468 // Enlarge the box a bit
4469 screenBox.EnLarge(screenBox.GetLonRange() * 0.05);
4470
4471 // update the tide/current select points, if necessary
4472 if (m_pParentCanvas->m_bShowTide) {
4473 m_pParentCanvas->RebuildTideSelectList(screenBox); // full screen
4474 DrawGLTidesInBBox(gldc, VPoint.GetBBox());
4475 }
4476
4477 if (m_pParentCanvas->m_bShowCurrent) {
4478 m_pParentCanvas->RebuildCurrentSelectList(screenBox);
4479 DrawGLCurrentsInBBox(gldc, VPoint.GetBBox());
4480 }
4481 }
4482
4483 // If multi-canvas, indicate which canvas has keyboard focus
4484 // by drawing a simple blue bar at the top.
4485 if (m_pParentCanvas->m_show_focus_bar &&
4486 (g_canvasConfig != 0)) { // multi-canvas?
4487 if (m_pParentCanvas == wxWindow::FindFocus()) {
4488 g_focusCanvas = m_pParentCanvas;
4489
4490 wxColour colour = GetGlobalColor("BLUE4");
4491 wxPen ppBlue(colour, 1);
4492 wxBrush ppBrush(colour);
4493 gldc.SetPen(ppBlue);
4494 gldc.SetBrush(ppBrush);
4495 int xw = m_pParentCanvas->GetClientSize().x * m_displayScale;
4496 float rect_pix = m_pParentCanvas->m_focus_indicator_pix * m_displayScale;
4497 wxPoint barPoints[4];
4498 barPoints[0].x = 0;
4499 barPoints[0].y = 0;
4500 barPoints[1].x = xw;
4501 barPoints[1].y = 0;
4502 barPoints[2].x = xw;
4503 barPoints[2].y = rect_pix;
4504 barPoints[3].x = 0;
4505 barPoints[3].y = rect_pix;
4506
4507 gldc.DrawPolygon(4, barPoints, 0, 0, 1, 0);
4508 }
4509 }
4510
4511 DrawDynamicRoutesTracksAndWaypoints(VPoint);
4512
4513 // Now draw all the objects which normally move around and are not
4514 // cached from the previous frame
4515 DrawFloatingOverlayObjects(m_gldc);
4516
4517#ifndef USE_ANDROID_GLES2
4518 // from this point on don't use perspective
4519 if (VPoint.tilt) {
4520 glMatrixMode(GL_PROJECTION);
4521 glLoadIdentity();
4522
4523 glOrtho(0, (GLint)gl_width, (GLint)gl_height, 0, -1, 1);
4524 glMatrixMode(GL_MODELVIEW);
4525 glLoadIdentity();
4526 }
4527#endif
4528
4529 if (!g_bhide_depth_units)
4530 DrawEmboss(m_gldc, m_pParentCanvas->EmbossDepthScale());
4531 if (!g_bhide_overzoom_flag)
4532 DrawEmboss(m_gldc, m_pParentCanvas->EmbossOverzoomIndicator(gldc));
4533
4534 if (g_pi_manager) {
4535 ViewPort &vp = m_pParentCanvas->GetVP();
4536 g_pi_manager->SendViewPortToRequestingPlugIns(vp);
4537 g_pi_manager->RenderAllGLCanvasOverlayPlugIns(
4538 m_pcontext, vp, m_pParentCanvas->m_canvasIndex, OVERLAY_OVER_EMBOSS);
4539 }
4540 if (!g_PrintingInProgress) {
4541 if (m_pParentCanvas->m_pTrackRolloverWin)
4542 m_pParentCanvas->m_pTrackRolloverWin->Draw(gldc);
4543
4544 if (m_pParentCanvas->m_pRouteRolloverWin)
4545 m_pParentCanvas->m_pRouteRolloverWin->Draw(gldc);
4546
4547 if (m_pParentCanvas->m_pAISRolloverWin)
4548 m_pParentCanvas->m_pAISRolloverWin->Draw(gldc);
4549
4550 if (m_pParentCanvas->GetMUIBar())
4551 m_pParentCanvas->GetMUIBar()->DrawGL(gldc, m_displayScale);
4552
4553 if (g_MainToolbar && m_pParentCanvas->IsPrimaryCanvas())
4554 g_MainToolbar->DrawGL(gldc, m_displayScale);
4555
4556 if (g_iENCToolbar && m_pParentCanvas->IsPrimaryCanvas())
4557 g_iENCToolbar->DrawGL(gldc, m_displayScale);
4558 }
4559
4560 // On some platforms, the opengl context window is always on top of any
4561 // standard DC windows, so we need to draw the Chart Info Window
4562 // as overlayed bmps.
4563
4564#ifdef __WXOSX__
4565 if (m_pParentCanvas->m_pCIWin && m_pParentCanvas->m_pCIWin->IsShown()) {
4566 int x, y, width, height;
4567 m_pParentCanvas->m_pCIWin->GetClientSize(&width, &height);
4568 m_pParentCanvas->m_pCIWin->GetPosition(&x, &y);
4569 wxBitmap bmp(width, height, -1);
4570 wxMemoryDC dc(bmp);
4571 if (bmp.IsOk()) {
4572 dc.SetBackground(wxBrush(GetGlobalColor(_T ( "UIBCK" ))));
4573 dc.Clear();
4574
4575 dc.SetTextBackground(GetGlobalColor(_T ( "UIBCK" )));
4576 dc.SetTextForeground(GetGlobalColor(_T ( "UITX1" )));
4577
4578 int yt = 0;
4579 int xt = 0;
4580 wxString s = m_pParentCanvas->m_pCIWin->GetString();
4581 int h = m_pParentCanvas->m_pCIWin->GetCharHeight();
4582
4583 wxStringTokenizer tkz(s, "\n");
4584 wxString token;
4585
4586 while (tkz.HasMoreTokens()) {
4587 token = tkz.GetNextToken();
4588 dc.DrawText(token, xt, yt);
4589 yt += h;
4590 }
4591 dc.SelectObject(wxNullBitmap);
4592
4593 m_gldc.DrawBitmap(bmp, x, y, false);
4594 }
4595 }
4596
4597#endif
4598 // render the chart bar
4599 if (g_bShowChartBar) DrawChartBar(m_gldc);
4600
4601 if (m_pParentCanvas->m_Compass && m_pParentCanvas->m_bShowCompassWin &&
4602 g_bShowCompassWin)
4603 m_pParentCanvas->m_Compass->Paint(gldc);
4604
4605 if (m_pParentCanvas->IsPrimaryCanvas()) {
4606 auto &noteman = NotificationManager::GetInstance();
4607 if (noteman.GetNotificationCount()) {
4608 m_pParentCanvas->m_notification_button->SetIconSeverity(
4609 noteman.GetMaxSeverity());
4610 if (m_pParentCanvas->m_notification_button->UpdateStatus()) Refresh();
4611 m_pParentCanvas->m_notification_button->Show(true);
4612 m_pParentCanvas->m_notification_button->Paint(gldc);
4613 } else {
4614 m_pParentCanvas->m_notification_button->Show(false);
4615 }
4616 }
4617 RenderGLAlertMessage();
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("YELO1"); //GREY1
5013 wxColour color = GetGlobalColor("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 g_overlayCanvas = m_pParentCanvas;
5963
5964 if (g_pi_manager) {
5965 g_pi_manager->SendViewPortToRequestingPlugIns(VPoint);
5966 g_pi_manager->RenderAllGLCanvasOverlayPlugIns(
5967 m_pcontext, VPoint, m_pParentCanvas->m_canvasIndex, OVERLAY_CHARTS);
5968 }
5969
5970 DrawStaticRoutesTracksAndWaypoints(m_pParentCanvas->VPoint);
5971 DrawDynamicRoutesTracksAndWaypoints(VPoint);
5972 DrawFloatingOverlayObjects(m_gldc);
5973 }
5974
5975 // All done, so disable Render to FBO
5976 glBindFramebuffer(GL_FRAMEBUFFER, 0);
5977
5978#endif
5979}
5980
5981wxBitmap &glChartCanvas::GetTouchBackingBitmap(ViewPort &vp) {
5982 wxBitmap tbm(vp.pix_width, vp.pix_height, -1);
5983 wxMemoryDC tdc(tbm);
5984 tdc.SetBackground(wxBrush(GetGlobalColor("BLUEBACK")));
5985 tdc.Clear();
5986 ocpnDC dc = ocpnDC(tdc);
5987 ViewPort tvp = vp;
5988
5989 tvp.view_scale_ppm /= 2;
5990 tvp.SetBoxes();
5991
5992 gShapeBasemap.SetBasemapLandColor(GetGlobalColor("LANDBACK"));
5993 dc.SetPen(*wxTRANSPARENT_PEN);
5994
5995 gShapeBasemap.RenderViewOnDC(dc, tvp);
5996 tdc.SelectObject(wxNullBitmap);
5997 m_touch_backing_bitmap = tbm;
5998 CreateBackingTexture();
5999
6000 return m_touch_backing_bitmap;
6001}
6002
6003void glChartCanvas::CreateBackingTexture() {
6004 wxImage image = m_touch_backing_bitmap.ConvertToImage();
6005 unsigned char *imgdata = image.GetData();
6006 unsigned char *imgalpha = image.GetAlpha();
6007 m_tex_w = image.GetWidth();
6008 m_tex_h = image.GetHeight();
6009 m_image_width = m_tex_w;
6010 m_image_height = m_tex_h;
6011
6012 GLuint format = GL_RGBA;
6013 GLuint internalformat = g_texture_rectangle_format;
6014#ifndef __ANDROID__
6015 internalformat = GL_RGBA;
6016#endif
6017 int stride = 4;
6018
6019 if (imgdata) {
6020 unsigned char *teximage =
6021 (unsigned char *)malloc(stride * m_tex_w * m_tex_h);
6022
6023 for (int i = 0; i < m_image_height; i++) {
6024 for (int j = 0; j < m_image_width; j++) {
6025 int s = (i * 3 * m_image_width) + (j * 3);
6026 int d = (i * stride * m_tex_w) + (j * stride);
6027
6028 teximage[d + 0] = imgdata[s + 0];
6029 teximage[d + 1] = imgdata[s + 1];
6030 teximage[d + 2] = imgdata[s + 2];
6031 teximage[d + 3] = 255;
6032 }
6033 }
6034
6035 glGenTextures(1, &m_TouchBackingTexture);
6036 glBindTexture(GL_TEXTURE_2D, m_TouchBackingTexture);
6037
6038 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
6039 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
6040 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
6041 GL_NEAREST); // No mipmapping
6042 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
6043
6044 glTexImage2D(GL_TEXTURE_2D, 0, internalformat, m_tex_w, m_tex_h, 0, format,
6045 GL_UNSIGNED_BYTE, teximage);
6046
6047 free(teximage);
6048 glBindTexture(GL_TEXTURE_2D, 0);
6049 }
6050}
Chart info panel.
Wrapper for creating a ChartCtx based on global vars.
General chart base definitions.
ChartDB * ChartData
Global instance.
Definition chartdb.cpp:72
Charts database management
BSB chart management.
ChartCanvas * g_focusCanvas
Global instance.
Definition chcanv.cpp:1194
ChartCanvas * g_overlayCanvas
Global instance.
Definition chcanv.cpp:1193
Generic Chart canvas base.
double GetDisplayDIPMult(wxWindow *win)
Get the display scaling factor for DPI-aware rendering.
Represents an active track that is currently being recorded.
Definition track.h:224
Base class for BSB (Maptech/NOS) format nautical charts.
Definition chartimg.h:127
Base class for all chart types.
Definition chartbase.h:125
ChartCanvas - Main chart display and interaction component.
Definition chcanv.h:151
float GetVPChartScale()
Return the ViewPort chart scale denominator (e.g., 50000 for a 1:50000 scale).
Definition chcanv.h:469
bool GetCanvasPointPix(double rlat, double rlon, wxPoint *r)
Convert latitude/longitude to canvas pixel coordinates (physical pixels) rounded to nearest integer.
Definition chcanv.cpp:4387
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:4337
double GetCanvasScaleFactor()
Return the number of logical pixels per meter for the screen.
Definition chcanv.h:477
double GetPixPerMM()
Get the number of logical pixels per millimeter on the screen.
Definition chcanv.h:508
bool MouseEventSetup(wxMouseEvent &event, bool b_handle_dclick=true)
Definition chcanv.cpp:7707
bool PanCanvas(double dx, double dy)
Pans (moves) the canvas by the specified physical pixels in x and y directions.
Definition chcanv.cpp:4915
float GetVPScale()
Return the ViewPort scale factor, in physical pixels per meter.
Definition chcanv.h:466
void ZoomCanvasSimple(double factor)
Perform an immediate zoom operation without smooth transitions.
Definition chcanv.cpp:4468
bool SetVPScale(double sc, bool b_refresh=true)
Sets the viewport scale while maintaining the center point.
Definition chcanv.cpp:5196
void GetCanvasPixPoint(double x, double y, double &lat, double &lon)
Convert canvas pixel coordinates (physical pixels) to latitude/longitude.
Definition chcanv.cpp:4412
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:4474
void GetDoubleCanvasPointPix(double rlat, double rlon, wxPoint2DDouble *r)
Convert latitude/longitude to canvas pixel coordinates (physical pixels) with double precision.
Definition chcanv.cpp:4332
bool MouseEventProcessCanvas(wxMouseEvent &event)
Processes mouse events for core chart panning and zooming operations.
Definition chcanv.cpp:10051
Manages the chart database and provides access to chart data.
Definition chartdb.h:94
Represents an MBTiles format chart.
Definition mbtiles.h:69
Wrapper class for plugin-based charts.
Definition chartimg.h:388
wxFont * FindOrCreateFont(int point_size, wxFontFamily family, wxFontStyle style, wxFontWeight weight, bool underline=false, const wxString &facename=wxEmptyString, wxFontEncoding encoding=wxFONTENCODING_DEFAULT)
Creates or finds a matching font in the font cache.
Definition FontMgr.cpp:449
wxFont * GetFont(const wxString &TextElement, int requested_font_size=0)
Gets a font object for a UI element.
Definition FontMgr.cpp:200
Wrapper class for OpenGL shader programs.
Definition shaders.h:57
Represents an index entry for tidal and current data.
Definition IDX_entry.h: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:1717
Represents a waypoint or mark within the navigation system.
Definition route_point.h:70
bool m_bRPIsBeingEdited
Flag indicating if this waypoint is currently being edited.
bool m_bIsInRoute
Flag indicating if this waypoint is part of a route.
Represents a navigational route in the navigation system.
Definition route.h:98
bool m_bIsBeingEdited
Flag indicating that the route is currently being edited by the user.
Definition route.h:223
Manages a set of ShapeBaseChart objects at different resolutions.
Definition tcmgr.h:91
Window for displaying chart thumbnails.
Definition thumbwin.h:59
Represents a track, which is a series of connected track points.
Definition track.h:114
ViewPort - Core geographic projection and coordinate transformation engine.
Definition viewport.h:84
double view_scale_ppm
Requested view scale in physical pixels per meter (ppm), before applying projections.
Definition viewport.h:232
double ref_scale
The nominal scale of the "reference chart" for this view.
Definition viewport.h:249
int pix_height
Height of the viewport in physical pixels.
Definition viewport.h:261
void SetBoxes(void)
Computes the bounding box coordinates for the current viewport.
Definition viewport.cpp:825
double rotation
Rotation angle of the viewport in radians.
Definition viewport.h:242
void SetPixelScale(double scale)
Set the physical to logical pixel ratio for the display.
Definition viewport.cpp:135
int pix_width
Width of the viewport in physical pixels.
Definition viewport.h:259
wxPoint2DDouble GetDoublePixFromLL(double lat, double lon)
Convert latitude and longitude on the ViewPort to physical pixel coordinates with double precision.
Definition viewport.cpp:147
double tilt
Tilt angle for perspective view in radians.
Definition viewport.h:244
double skew
Angular distortion (shear transform) applied to the viewport in radians.
Definition viewport.h:240
void GetLLFromPix(const wxPoint &p, double *lat, double *lon)
Convert physical pixel coordinates on the ViewPort to latitude and longitude.
Definition viewport.h:108
double clon
Center longitude of the viewport in degrees.
Definition viewport.h:227
double clat
Center latitude of the viewport in degrees.
Definition viewport.h:225
wxPoint GetPixFromLL(double lat, double lon)
Convert latitude and longitude on the ViewPort to physical pixel coordinates.
Definition viewport.cpp:138
double chart_scale
Chart scale denominator (e.g., 50000 for a 1:50000 scale).
Definition viewport.h:247
Represents a composite CM93 chart covering multiple scales.
Definition cm93.h:416
Stores emboss effect data for textures.
Definition emboss_data.h:35
OpenGL chart rendering canvas.
Floating toolbar for iENC (International Electronic Navigational Chart) functionality.
Definition iENCToolbar.h:45
Device context class that can use either wxDC or OpenGL for drawing.
Definition ocpndc.h:60
void DrawLine(wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2, bool b_hiqual=true)
Draw a line between two points using either wxDC or OpenGL.
Definition ocpndc.cpp:476
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:122
The JSON value class implementation.
Definition jsonval.h:84
Class cm93chart and helpers – CM93 chart state.
Global color handling by name.
wxFont * GetOCPNScaledFont(wxString item, int default_size)
Retrieves a font from FontMgr, optionally scaled for physical readability.
Definition gui_lib.cpp:56
Class NotificationManager.
#define OVERLAY_CHARTS
Lowest priority for overlays to render above all basic charts.
#define OVERLAY_LEGACY
Overlay rendering priorities determine the layering order of plugin graphics.
#define OVERLAY_OVER_UI
Highest priority for overlays above all UI elements.
#define OVERLAY_OVER_EMBOSS
Priority for overlays above embossed chart features.
#define OVERLAY_OVER_SHIPS
Priority for overlays that should appear above ship symbols.
int GetChartbarHeight(void)
Gets height of chart bar in pixels.
double OCPN_GetDisplayContentScaleFactor()
Gets content scaling factor for current display.
Tools to send data to plugins.
PlugInManager and helper classes – Mostly gui parts (dialogs) and plugin API stuff.
Represents an entry in the chart table, containing information about a single chart.
Definition chartdbs.h:182