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