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