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 // In "b_follow" mode, we have a-priori information about the desired screen
2281 // coordinates of ownship
2282 // Here, calculate the ownship location on screen, and make it so.
2283 // Special case: No need for such precision on chart drag operations
2284 double shift_dx = 0;
2285 double shift_dy = 0;
2286 if (m_pParentCanvas->m_bFollow && !m_pParentCanvas->m_MouseDragging) {
2287 lGPSPoint.m_x = m_pParentCanvas->GetVP().pix_width / 2;
2288 lGPSPoint.m_y = m_pParentCanvas->GetVP().pix_height / 2;
2289 if (m_pParentCanvas->m_bLookAhead) {
2290 double angle = m_pParentCanvas->dir_to_shift * PI / 180.;
2291 angle += m_pParentCanvas->GetVPRotation();
2292 shift_dx = m_pParentCanvas->meters_to_shift * sin(angle) *
2293 m_pParentCanvas->GetVPScale();
2294 lGPSPoint.m_x -= shift_dx / cos(gLat * PI / 180.);
2295 shift_dy = m_pParentCanvas->meters_to_shift * cos(angle) *
2296 m_pParentCanvas->GetVPScale();
2297 lGPSPoint.m_y += shift_dy / cos(gLat * PI / 180.);
2298 } else {
2299 lGPSPoint.m_x -= m_pParentCanvas->m_OSoffsetx;
2300 lGPSPoint.m_y += m_pParentCanvas->m_OSoffsety;
2301 }
2302 } else {
2303 m_pParentCanvas->GetDoubleCanvasPointPixVP(m_pParentCanvas->GetVP(), gLat,
2304 gLon, &lGPSPoint);
2305 }
2306
2307 lShipMidPoint = lGPSPoint;
2308
2309 // Draw the icon rotated to the COG
2310 // or to the Hdt if available
2311 float icon_hdt = pCog;
2312 if (!std::isnan(gHdt)) icon_hdt = gHdt;
2313
2314 // COG may be undefined in NMEA data stream
2315 if (std::isnan(icon_hdt)) icon_hdt = 0.0;
2316
2317 // Calculate the ownship drawing angle icon_rad using an assumed 10 minute
2318 // predictor
2319 double osd_head_lat, osd_head_lon;
2320 wxPoint2DDouble osd_head_point;
2321
2322 ll_gc_ll(gLat, gLon, icon_hdt, pSog * 10. / 60., &osd_head_lat,
2323 &osd_head_lon);
2324
2325 m_pParentCanvas->GetDoubleCanvasPointPixVP(
2326 m_pParentCanvas->GetVP(), osd_head_lat, osd_head_lon, &osd_head_point);
2327
2328 double icon_rad = atan2f((float)(osd_head_point.m_y - lShipMidPoint.m_y),
2329 (float)(osd_head_point.m_x - lShipMidPoint.m_x));
2330 icon_rad += (float)PI;
2331
2332 if (pSog < 0.2)
2333 icon_rad =
2334 ((icon_hdt + 90.) * PI / 180.) + m_pParentCanvas->GetVP().rotation;
2335
2336 // Another draw test ,based on pixels, assuming the ship icon is a fixed
2337 // nominal size and is just barely outside the viewport ....
2338 BoundingBox bb_screen(0, 0, m_pParentCanvas->GetVP().pix_width,
2339 m_pParentCanvas->GetVP().pix_height);
2340
2341 // TODO: fix to include actual size of boat that will be rendered
2342 int img_height = 0;
2343
2344 if (bb_screen.PointInBox(lShipMidPoint, 20)) {
2345 if (g_GLOptions.m_GLLineSmoothing) glEnable(GL_LINE_SMOOTH);
2346 if (g_GLOptions.m_GLPolygonSmoothing) glEnable(GL_POLYGON_SMOOTH);
2347
2348 if (m_pParentCanvas->GetVP().chart_scale >
2349 300000) // According to S52, this should be 50,000
2350 {
2351 float scale_factor = 1.0;
2352 // Scale the generic icon to ChartScaleFactor, slightly softened....
2353 if ((g_ChartScaleFactorExp > 1.0) && (g_OwnShipIconType == 0))
2354 scale_factor = (log(g_ChartScaleFactorExp) + 1.0) * 1.1;
2355
2356 float nominal_ownship_size_mm = m_pParentCanvas->m_display_size_mm / 44.0;
2357 nominal_ownship_size_mm = wxMin(nominal_ownship_size_mm, 15.0);
2358 nominal_ownship_size_mm = wxMax(nominal_ownship_size_mm, 7.0);
2359
2360 scale_factor *= m_pParentCanvas->GetContentScaleFactor();
2361
2362 float nominal_ownship_size_pixels =
2363 wxMax(20.0, m_pParentCanvas->GetPixPerMM() *
2364 nominal_ownship_size_mm); // nominal length, but not
2365 // less than 20 pixel
2366 float v = (nominal_ownship_size_pixels * scale_factor) / 3;
2367
2368 wxPen ppSmallScaleShip;
2369 if (SHIP_NORMAL == m_pParentCanvas->m_ownship_state)
2370 ppSmallScaleShip =
2371 wxPen(GetGlobalColor(_T ( "URED" )), v / 5, wxPENSTYLE_SOLID);
2372 else
2373 ppSmallScaleShip =
2374 wxPen(GetGlobalColor(_T ( "YELO1" )), v / 5, wxPENSTYLE_SOLID);
2375 dc.SetPen(ppSmallScaleShip);
2376
2377 dc.SetBrush(
2378 wxBrush(GetGlobalColor(_T ( "URED" )), wxBRUSHSTYLE_TRANSPARENT));
2379
2380 // start with cross
2381 dc.DrawLine((-v * 1.2) + lShipMidPoint.m_x, lShipMidPoint.m_y,
2382 (v * 1.2) + lShipMidPoint.m_x, lShipMidPoint.m_y);
2383 dc.DrawLine(lShipMidPoint.m_x, (-v * 1.2) + lShipMidPoint.m_y,
2384 lShipMidPoint.m_x, (v * 1.2) + lShipMidPoint.m_y);
2385
2386 // Two circles
2387 dc.StrokeCircle(lShipMidPoint.m_x, lShipMidPoint.m_y, v);
2388 dc.StrokeCircle(lShipMidPoint.m_x, lShipMidPoint.m_y, 0.6 * v);
2389 img_height = 20;
2390 } else {
2391 int draw_color = SHIP_INVALID;
2392 if (SHIP_NORMAL == m_pParentCanvas->m_ownship_state)
2393 draw_color = SHIP_NORMAL;
2394 else if (SHIP_LOWACCURACY == m_pParentCanvas->m_ownship_state)
2395 draw_color = SHIP_LOWACCURACY;
2396
2397 if (!ownship_tex ||
2398 (draw_color !=
2399 ownship_color)) { /* initial run, create texture for ownship,
2400 also needed at colorscheme changes (not
2401 implemented) */
2402
2403 ownship_color = draw_color;
2404
2405 if (ownship_tex) glDeleteTextures(1, &ownship_tex);
2406
2407 glGenTextures(1, &ownship_tex);
2408 glBindTexture(GL_TEXTURE_2D, ownship_tex);
2409
2410 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2411 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2412
2413 wxImage image;
2414 if (m_pParentCanvas->m_pos_image_user) {
2415 switch (draw_color) {
2416 case SHIP_INVALID:
2417 image = *m_pParentCanvas->m_pos_image_user_grey;
2418 break;
2419 case SHIP_NORMAL:
2420 image = *m_pParentCanvas->m_pos_image_user;
2421 break;
2422 case SHIP_LOWACCURACY:
2423 image = *m_pParentCanvas->m_pos_image_user_yellow;
2424 break;
2425 }
2426 } else {
2427 switch (draw_color) {
2428 case SHIP_INVALID:
2429 image = *m_pParentCanvas->m_pos_image_grey;
2430 break;
2431 case SHIP_NORMAL:
2432 image = *m_pParentCanvas->m_pos_image_red;
2433 break;
2434 case SHIP_LOWACCURACY:
2435 image = *m_pParentCanvas->m_pos_image_yellow;
2436 break;
2437 }
2438 }
2439
2440 int w = image.GetWidth(), h = image.GetHeight();
2441 int glw = NextPow2(w), glh = NextPow2(h);
2442 ownship_size = wxSize(w, h);
2443 ownship_tex_size = wxSize(glw, glh);
2444
2445 unsigned char *d = image.GetData();
2446 unsigned char *a = image.GetAlpha();
2447 unsigned char *e = new unsigned char[4 * w * h];
2448
2449 if (d && e && a) {
2450 for (int p = 0; p < w * h; p++) {
2451 e[4 * p + 0] = d[3 * p + 0];
2452 e[4 * p + 1] = d[3 * p + 1];
2453 e[4 * p + 2] = d[3 * p + 2];
2454 e[4 * p + 3] = a[p];
2455 }
2456 }
2457 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, glw, glh, 0, GL_RGBA,
2458 GL_UNSIGNED_BYTE, 0);
2459
2460 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE,
2461 e);
2462 delete[] e;
2463 }
2464
2465 /* establish ship color */
2466#ifndef USE_ANDROID_GLES2
2467 if (m_pParentCanvas->m_pos_image_user)
2468 glColor4ub(255, 255, 255, 255);
2469 else if (SHIP_NORMAL == m_pParentCanvas->m_ownship_state)
2470 glColor4ub(255, 0, 0, 255);
2471 else if (SHIP_LOWACCURACY == m_pParentCanvas->m_ownship_state)
2472 glColor4ub(255, 255, 0, 255);
2473 else
2474 glColor4ub(128, 128, 128, 255);
2475#endif
2476 float scale_factor_y = 1.0;
2477 float scale_factor_x = 1.0;
2478
2479 int ownShipWidth = 22; // Default values from s_ownship_icon
2480 int ownShipLength = 84;
2481 lShipMidPoint = lGPSPoint;
2482
2483 /* scaled ship? */
2484 if (g_OwnShipIconType != 0)
2485 m_pParentCanvas->ComputeShipScaleFactor(
2486 icon_hdt, ownShipWidth, ownShipLength, lShipMidPoint,
2487 GPSOffsetPixels, lGPSPoint, scale_factor_x, scale_factor_y);
2488
2489 glEnable(GL_BLEND);
2490
2491 // Scale the generic icon to ChartScaleFactor, slightly softened....
2492 if ((g_ShipScaleFactorExp > 1.0) && (g_OwnShipIconType == 0)) {
2493 scale_factor_x = (log(g_ShipScaleFactorExp) + 1.0) * 1.1;
2494 scale_factor_y = (log(g_ShipScaleFactorExp) + 1.0) * 1.1;
2495 }
2496
2497 // Correct for scaled displays, e.g. Retina
2498 scale_factor_x *= m_pParentCanvas->GetContentScaleFactor();
2499 scale_factor_y *= m_pParentCanvas->GetContentScaleFactor();
2500
2501 // Set the size of the little circle showing the GPS reference position
2502 // Set a default early, adjust later based on render type
2503 float gps_circle_radius = 3.0;
2504
2505 if (g_OwnShipIconType == 0) { // Default Bitmap
2506
2507 glEnable(GL_TEXTURE_2D);
2508 glBindTexture(GL_TEXTURE_2D, ownship_tex);
2509
2510 // We choose to render the ownship bitmap at roughly the same size( in
2511 // pixels ) as the DC mode renderer.
2512 // For ultra-high definition displays, we clamp the actual on-screen
2513 // size to be no smaller than 7.0 mm Similarly, for lo-res displays, we
2514 // limit the actual size to be no larger than 15 mm maximum.
2515
2516 // Get bitmap height in pixels
2517 int image_height_bitmap = m_pParentCanvas->m_pos_image_red->GetHeight();
2518 if (m_pParentCanvas->m_pos_image_user)
2519 image_height_bitmap = m_pParentCanvas->m_pos_image_user->GetHeight();
2520
2521 float nominal_ownship_size_mm =
2522 image_height_bitmap / m_pParentCanvas->GetPixPerMM();
2523
2524 nominal_ownship_size_mm = wxMin(nominal_ownship_size_mm, 15.0);
2525 nominal_ownship_size_mm = wxMax(nominal_ownship_size_mm, 7.0);
2526
2527 float nominal_ownship_size_pixels =
2528 m_pParentCanvas->GetPixPerMM() * nominal_ownship_size_mm;
2529
2530 if (m_pParentCanvas->GetContentScaleFactor() == 1.0) {
2531 nominal_ownship_size_pixels = wxMax(
2532 20.0, nominal_ownship_size_pixels); // not less than 20 pixel
2533 }
2534
2535 float h = nominal_ownship_size_pixels * scale_factor_y;
2536 float w = nominal_ownship_size_pixels * scale_factor_x *
2537 ownship_size.x / ownship_size.y;
2538 float glw = ownship_tex_size.x, glh = ownship_tex_size.y;
2539 float u = ownship_size.x / glw, v = ownship_size.y / glh;
2540
2541 // printf("%g %g %g %g %g %g %g\n",
2542 // nominal_ownship_size_mm, nominal_ownship_size_pixels,
2543 // h, w, u, v, m_pParentCanvas->m_display_size_mm);
2544 // tweak GPS reference point indicator size
2545 gps_circle_radius = w / 5;
2546
2547 float uv[8], coords[8];
2548 uv[0] = 0;
2549 uv[1] = 0;
2550 uv[2] = u;
2551 uv[3] = 0;
2552 uv[4] = u;
2553 uv[5] = v;
2554 uv[6] = 0;
2555 uv[7] = v;
2556
2557 coords[0] = -w / 2;
2558 coords[1] = -h / 2;
2559 coords[2] = w / 2;
2560 coords[3] = -h / 2;
2561 coords[4] = w / 2;
2562 coords[5] = h / 2;
2563 coords[6] = -w / 2;
2564 coords[7] = h / 2;
2565
2566 RenderSingleTexture(dc, coords, uv, m_pParentCanvas->GetpVP(),
2567 lShipMidPoint.m_x, lShipMidPoint.m_y,
2568 icon_rad - PI / 2);
2569 glDisable(GL_TEXTURE_2D);
2570 } else if (g_OwnShipIconType == 1) { // Scaled Bitmap
2571
2572 glEnable(GL_TEXTURE_2D);
2573 glBindTexture(GL_TEXTURE_2D, ownship_tex);
2574
2575 float nominal_ownship_size_pixels_y = 84;
2576 float nominal_ownship_size_pixels_x = 22;
2577
2578 float h = nominal_ownship_size_pixels_y * scale_factor_y;
2579 float w = nominal_ownship_size_pixels_x * scale_factor_x;
2580
2581 float u = (float)ownship_size.x / ownship_tex_size.x,
2582 v = (float)ownship_size.y / ownship_tex_size.y;
2583
2584 // tweak GPS reference point indicator size
2585 gps_circle_radius = w / 5;
2586
2587 float uv[8], coords[8];
2588 uv[0] = 0;
2589 uv[1] = 0;
2590 uv[2] = u;
2591 uv[3] = 0;
2592 uv[4] = u;
2593 uv[5] = v;
2594 uv[6] = 0;
2595 uv[7] = v;
2596
2597 coords[0] = -w / 2;
2598 coords[1] = -h / 2;
2599 coords[2] = w / 2;
2600 coords[3] = -h / 2;
2601 coords[4] = w / 2;
2602 coords[5] = h / 2;
2603 coords[6] = -w / 2;
2604 coords[7] = h / 2;
2605
2606 RenderSingleTexture(dc, coords, uv, m_pParentCanvas->GetpVP(),
2607 lShipMidPoint.m_x, lShipMidPoint.m_y,
2608 icon_rad - PI / 2);
2609
2610 glDisable(GL_TEXTURE_2D);
2611 } else if (g_OwnShipIconType == 2) { // Scaled Vector
2612 // static const GLint s_ownship_icon[] = { 5, -42, 11,
2613 // -28, 11, 42, -11, 42,
2614 // -11, -28, -5,
2615 // -42, -11, 0,
2616 // 11, 0, 0, 42,
2617 // 0, -42 };
2618
2619 wxPoint shipPoints[6];
2620
2621 wxColour colour = m_pParentCanvas->ShipColor();
2622 wxPen ppPen(*wxBLACK, 1);
2623 wxBrush ppBrush(colour);
2624 dc.SetPen(ppPen);
2625 dc.SetBrush(ppBrush);
2626
2627 shipPoints[0].x = 0 * scale_factor_x;
2628 shipPoints[0].y = -28 * scale_factor_y;
2629 shipPoints[1].x = 11 * scale_factor_x;
2630 shipPoints[1].y = -28 * scale_factor_y;
2631 shipPoints[2].x = 11 * scale_factor_x;
2632 shipPoints[2].y = 42 * scale_factor_y;
2633 shipPoints[3].x = 0 * scale_factor_x;
2634 shipPoints[3].y = 42 * scale_factor_y;
2635 dc.DrawPolygon(4, shipPoints, lShipMidPoint.m_x, lShipMidPoint.m_y, 1,
2636 icon_rad - PI / 2);
2637
2638 shipPoints[0].x = 0 * scale_factor_x;
2639 shipPoints[0].y = -42 * scale_factor_y;
2640 shipPoints[1].x = 5 * scale_factor_x;
2641 shipPoints[1].y = -42 * scale_factor_y;
2642 shipPoints[2].x = 11 * scale_factor_x;
2643 shipPoints[2].y = -28 * scale_factor_y;
2644 shipPoints[3].x = 0 * scale_factor_x;
2645 shipPoints[3].y = -28 * scale_factor_y;
2646 dc.DrawPolygon(4, shipPoints, lShipMidPoint.m_x, lShipMidPoint.m_y, 1,
2647 icon_rad - PI / 2);
2648
2649 shipPoints[0].x = 0 * scale_factor_x;
2650 shipPoints[0].y = -28 * scale_factor_y;
2651 shipPoints[1].x = -11 * scale_factor_x;
2652 shipPoints[1].y = -28 * scale_factor_y;
2653 shipPoints[2].x = -11 * scale_factor_x;
2654 shipPoints[2].y = 42 * scale_factor_y;
2655 shipPoints[3].x = 0 * scale_factor_x;
2656 shipPoints[3].y = 42 * scale_factor_y;
2657 dc.DrawPolygon(4, shipPoints, lShipMidPoint.m_x, lShipMidPoint.m_y, 1,
2658 icon_rad - PI / 2);
2659
2660 shipPoints[0].x = 0 * scale_factor_x;
2661 shipPoints[0].y = -42 * scale_factor_y;
2662 shipPoints[1].x = -5 * scale_factor_x;
2663 shipPoints[1].y = -42 * scale_factor_y;
2664 shipPoints[2].x = -11 * scale_factor_x;
2665 shipPoints[2].y = -28 * scale_factor_y;
2666 shipPoints[3].x = 0 * scale_factor_x;
2667 shipPoints[3].y = -28 * scale_factor_y;
2668 dc.DrawPolygon(4, shipPoints, lShipMidPoint.m_x, lShipMidPoint.m_y, 1,
2669 icon_rad - PI / 2);
2670
2671 // draw with cross
2672 double p1x = -11 * scale_factor_x;
2673 double p2x = 11 * scale_factor_x;
2674 double p1y = 0;
2675 double p2y = 0;
2676 double p1xr =
2677 ((p1x)*cos(icon_rad - PI / 2)) - ((p1y)*sin(icon_rad - PI / 2));
2678 double p2xr =
2679 ((p2x)*cos(icon_rad - PI / 2)) - ((p2y)*sin(icon_rad - PI / 2));
2680 double p1yr =
2681 ((p1y)*cos(icon_rad - PI / 2)) + ((p1x)*sin(icon_rad - PI / 2));
2682 double p2yr =
2683 ((p2y)*cos(icon_rad - PI / 2)) + ((p2x)*sin(icon_rad - PI / 2));
2684 dc.DrawLine(p1xr + lShipMidPoint.m_x, p1yr + lShipMidPoint.m_y,
2685 p2xr + lShipMidPoint.m_x, p2yr + lShipMidPoint.m_y);
2686
2687 p1x = 0;
2688 p2x = 0;
2689 p1y = -42 * scale_factor_y;
2690 p2y = 42 * scale_factor_y;
2691 p1xr = ((p1x)*cos(icon_rad - PI / 2)) - ((p1y)*sin(icon_rad - PI / 2));
2692 p2xr = ((p2x)*cos(icon_rad - PI / 2)) - ((p2y)*sin(icon_rad - PI / 2));
2693 p1yr = ((p1y)*cos(icon_rad - PI / 2)) + ((p1x)*sin(icon_rad - PI / 2));
2694 p2yr = ((p2y)*cos(icon_rad - PI / 2)) + ((p2x)*sin(icon_rad - PI / 2));
2695 dc.DrawLine(p1xr + lShipMidPoint.m_x, p1yr + lShipMidPoint.m_y,
2696 p2xr + lShipMidPoint.m_x, p2yr + lShipMidPoint.m_y);
2697 }
2698
2699 img_height = ownShipLength * scale_factor_y;
2700
2701 // Reference point, where the GPS antenna is
2702 if (m_pParentCanvas->m_pos_image_user) gps_circle_radius = 1;
2703
2704 wxPen ppPen1(GetGlobalColor(_T ( "UBLCK" )), 1, wxPENSTYLE_SOLID);
2705 dc.SetPen(ppPen1);
2706 dc.SetBrush(wxBrush(GetGlobalColor(_T ( "CHWHT" ))));
2707
2708 dc.StrokeCircle(lGPSPoint.m_x, lGPSPoint.m_y, gps_circle_radius);
2709 }
2710
2711 // glDisableClientState(GL_VERTEX_ARRAY);
2712 glDisable(GL_LINE_SMOOTH);
2713 glDisable(GL_POLYGON_SMOOTH);
2714 glDisable(GL_BLEND);
2715 }
2716
2717 m_pParentCanvas->ShipIndicatorsDraw(dc, img_height, GPSOffsetPixels,
2718 lGPSPoint);
2719}
2720
2721void glChartCanvas::DrawFloatingOverlayObjects(ocpnDC &dc) {
2722 ViewPort &vp = m_pParentCanvas->GetVP();
2723
2724 // Draw any active or selected routes now
2725 extern Routeman *g_pRouteMan;
2726 // extern Track *g_pActiveTrack;
2727 Route *active_route = g_pRouteMan->GetpActiveRoute();
2728
2729 // if( active_route ) active_route->DrawGL( vp, region );
2730 // if( g_pActiveTrack ) g_pActiveTrack->Draw( dc, vp );
2731 // if( m_pParentCanvas->m_pSelectedRoute )
2732 // m_pParentCanvas->m_pSelectedRoute->DrawGL( vp, region );
2733
2734 GridDraw();
2735
2736 g_overlayCanvas = m_pParentCanvas;
2737 if (g_pi_manager) {
2738 g_pi_manager->SendViewPortToRequestingPlugIns(vp);
2739 g_pi_manager->RenderAllGLCanvasOverlayPlugIns(
2740 m_pcontext, vp, m_pParentCanvas->m_canvasIndex, OVERLAY_LEGACY);
2741 }
2742
2743 // all functions called with m_pParentCanvas-> are still slow because they go
2744 // through ocpndc
2745 AISDrawAreaNotices(dc, m_pParentCanvas->GetVP(), m_pParentCanvas);
2746
2747 m_pParentCanvas->DrawAnchorWatchPoints(dc);
2748 AISDraw(dc, m_pParentCanvas->GetVP(), m_pParentCanvas);
2749 ShipDraw(dc);
2750 m_pParentCanvas->AlertDraw(dc);
2751
2752 m_pParentCanvas->RenderVisibleSectorLights(dc);
2753
2754 m_pParentCanvas->RenderRouteLegs(dc);
2755 m_pParentCanvas->RenderShipToActive(dc, true);
2756 m_pParentCanvas->ScaleBarDraw(dc);
2757 s57_DrawExtendedLightSectorsGL(dc, m_pParentCanvas->VPoint,
2758 m_pParentCanvas->extendedSectorLegs);
2759 if (g_pi_manager) {
2760 g_pi_manager->RenderAllGLCanvasOverlayPlugIns(
2761 m_pcontext, vp, m_pParentCanvas->m_canvasIndex, OVERLAY_OVER_SHIPS);
2762 }
2763}
2764
2765void glChartCanvas::DrawChartBar(ocpnDC &dc) {
2766 if (m_pParentCanvas->GetPiano()) {
2767 int canvas_height = GetClientSize().y;
2768 canvas_height *= m_displayScale;
2769
2770 m_pParentCanvas->GetPiano()->DrawGL(
2771 canvas_height - m_pParentCanvas->GetPiano()->GetHeight());
2772 }
2773}
2774
2775void glChartCanvas::DrawQuiting() {
2776#ifndef USE_ANDROID_GLES2
2777 GLubyte pattern[8][8];
2778 for (int y = 0; y < 8; y++)
2779 for (int x = 0; x < 8; x++) pattern[y][x] = (y == x) * 255;
2780
2781 glEnable(GL_BLEND);
2782 glEnable(GL_TEXTURE_2D);
2783 glBindTexture(GL_TEXTURE_2D, 0);
2784
2785 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
2786 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2787 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2788
2789 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 8, 8, 0, GL_ALPHA, GL_UNSIGNED_BYTE,
2790 pattern);
2791 glColor3f(0, 0, 0);
2792
2793 float x = GetSize().x, y = GetSize().y;
2794 float u = x / 8, v = y / 8;
2795
2796 glBegin(GL_QUADS);
2797 glTexCoord2f(0, 0);
2798 glVertex2f(0, 0);
2799 glTexCoord2f(0, v);
2800 glVertex2f(0, y);
2801 glTexCoord2f(u, v);
2802 glVertex2f(x, y);
2803 glTexCoord2f(u, 0);
2804 glVertex2f(x, 0);
2805 glEnd();
2806
2807 glDisable(GL_TEXTURE_2D);
2808 glDisable(GL_BLEND);
2809#endif
2810}
2811
2812void glChartCanvas::DrawCloseMessage(wxString msg) {
2813#ifndef USE_ANDROID_GLES2
2814
2815 if (1) {
2816 wxFont *pfont = FontMgr::Get().FindOrCreateFont(
2817 12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
2818
2819 TexFont texfont;
2820
2821 texfont.Build(*pfont, 1, 1);
2822 int w, h;
2823 texfont.GetTextExtent(msg, &w, &h);
2824 h += 2;
2825 int yp = m_pParentCanvas->GetVP().pix_height / 2;
2826 int xp = (m_pParentCanvas->GetVP().pix_width - w) / 2;
2827
2828 glColor3ub(243, 229, 47);
2829
2830 glBegin(GL_QUADS);
2831 glVertex2i(xp, yp);
2832 glVertex2i(xp + w, yp);
2833 glVertex2i(xp + w, yp + h);
2834 glVertex2i(xp, yp + h);
2835 glEnd();
2836
2837 glEnable(GL_BLEND);
2838
2839 glColor3ub(0, 0, 0);
2840 glEnable(GL_TEXTURE_2D);
2841 texfont.RenderString(msg, xp, yp);
2842 glDisable(GL_TEXTURE_2D);
2843 glDisable(GL_BLEND);
2844 }
2845#endif
2846}
2847
2848GLShaderProgram *pStaticShader;
2849
2850static std::list<double *> combine_work_data;
2851static void combineCallbackD(GLdouble coords[3], GLdouble *vertex_data[4],
2852 GLfloat weight[4], GLdouble **dataOut) {
2853 double *vertex = new double[3];
2854 combine_work_data.push_back(vertex);
2855 memcpy(vertex, coords, 3 * (sizeof *coords));
2856 *dataOut = vertex;
2857}
2858
2859#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
2860void vertexCallbackD_GLSL(GLvoid *vertex) {
2861 // Grow the work buffer if necessary
2862 if (s_tess_vertex_idx > s_tess_buf_len - 8) {
2863 int new_buf_len = s_tess_buf_len + 100;
2864 GLfloat *tmp = s_tess_work_buf;
2865
2866 s_tess_work_buf =
2867 (GLfloat *)realloc(s_tess_work_buf, new_buf_len * sizeof(GLfloat));
2868 if (NULL == s_tess_work_buf) {
2869 free(tmp);
2870 tmp = NULL;
2871 } else
2872 s_tess_buf_len = new_buf_len;
2873 }
2874
2875 GLdouble *pointer = (GLdouble *)vertex;
2876
2877 s_tess_work_buf[s_tess_vertex_idx++] = (float)pointer[0];
2878 s_tess_work_buf[s_tess_vertex_idx++] = (float)pointer[1];
2879
2880 s_nvertex++;
2881}
2882
2883void beginCallbackD_GLSL(GLenum mode) {
2884 s_tess_vertex_idx_this = s_tess_vertex_idx;
2885 s_tess_mode = mode;
2886 s_nvertex = 0;
2887}
2888
2889void endCallbackD_GLSL() {
2890 GLShaderProgram *shader = pStaticShader;
2891 shader->Bind();
2892
2893 shader->SetUniformMatrix4fv("MVMatrix",
2894 (GLfloat *)s_tessVP.vp_matrix_transform);
2895
2896 mat4x4 identityMatrix;
2897 mat4x4_identity(identityMatrix);
2898 shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)identityMatrix);
2899
2900 // Use color stored in static variable.
2901 float colorv[4];
2902 colorv[0] = s_regionColor.Red() / float(256);
2903 colorv[1] = s_regionColor.Green() / float(256);
2904 colorv[2] = s_regionColor.Blue() / float(256);
2905 colorv[3] = s_regionColor.Alpha() / float(256);
2906 shader->SetUniform4fv("color", colorv);
2907
2908 float *bufPt = &s_tess_work_buf[s_tess_vertex_idx_this];
2909 shader->SetAttributePointerf("position", bufPt);
2910
2911 glDrawArrays(s_tess_mode, 0, s_nvertex);
2912
2913 shader->UnBind();
2914}
2915#else
2916void vertexCallbackD(GLvoid *vertex) { glVertex3dv((GLdouble *)vertex); }
2917
2918void beginCallbackD(GLenum mode) { glBegin(mode); }
2919
2920void endCallbackD() { glEnd(); }
2921
2922#endif
2923
2924void glChartCanvas::DrawRegion(ViewPort &vp, const LLRegion &region) {
2925 float lat_dist, lon_dist;
2926 GetLatLonCurveDist(vp, lat_dist, lon_dist);
2927
2928 GLUtesselator *tobj = gluNewTess();
2929 if (!pStaticShader) pStaticShader = GetStaticTriShader();
2930
2931#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
2932 gluTessCallback(tobj, GLU_TESS_VERTEX, (_GLUfuncptr)&vertexCallbackD_GLSL);
2933 gluTessCallback(tobj, GLU_TESS_BEGIN, (_GLUfuncptr)&beginCallbackD_GLSL);
2934 gluTessCallback(tobj, GLU_TESS_END, (_GLUfuncptr)&endCallbackD_GLSL);
2935 gluTessCallback(tobj, GLU_TESS_COMBINE, (_GLUfuncptr)&combineCallbackD);
2936 s_tessVP = vp;
2937
2938#else
2939 gluTessCallback(tobj, GLU_TESS_VERTEX, (_GLUfuncptr)&vertexCallbackD);
2940 gluTessCallback(tobj, GLU_TESS_BEGIN, (_GLUfuncptr)&beginCallbackD);
2941 gluTessCallback(tobj, GLU_TESS_END, (_GLUfuncptr)&endCallbackD);
2942 gluTessCallback(tobj, GLU_TESS_COMBINE, (_GLUfuncptr)&combineCallbackD);
2943#endif
2944
2945 gluTessNormal(tobj, 0, 0, 1);
2946
2947 gluTessBeginPolygon(tobj, NULL);
2948 for (std::list<poly_contour>::const_iterator i = region.contours.begin();
2949 i != region.contours.end(); i++) {
2950 gluTessBeginContour(tobj);
2951 contour_pt l = *i->rbegin();
2952 double sml[2];
2953 bool sml_valid = false;
2954 for (poly_contour::const_iterator j = i->begin(); j != i->end(); j++) {
2955 int lat_splits = floor(fabs(j->y - l.y) / lat_dist);
2956 int lon_splits = floor(fabs(j->x - l.x) / lon_dist);
2957 int splits = wxMax(lat_splits, lon_splits) + 1;
2958
2959 double smj[2];
2960 if (splits != 1) {
2961 // must perform border interpolation in mercator space as this is what
2962 // the charts use
2963 toSM(j->y, j->x, 0, 0, smj + 0, smj + 1);
2964 if (!sml_valid) toSM(l.y, l.x, 0, 0, sml + 0, sml + 1);
2965 }
2966
2967 for (int i = 0; i < splits; i++) {
2968 double lat, lon;
2969 if (i == splits - 1)
2970 lat = j->y, lon = j->x;
2971 else {
2972 double d = (double)(i + 1) / splits;
2973 fromSM(d * smj[0] + (1 - d) * sml[0], d * smj[1] + (1 - d) * sml[1],
2974 0, 0, &lat, &lon);
2975 }
2976 wxPoint2DDouble q = vp.GetDoublePixFromLL(lat, lon);
2977 if (std::isnan(q.m_x)) continue;
2978
2979 double *p = new double[6];
2980
2981 // p[0] = q.m_x, p[1] = q.m_y, p[2] = 0;
2982 // It is reasonable to use wxRound() here,
2983 // since we are working with pixel coordinates at this point
2984 p[0] = wxRound(q.m_x), p[1] = wxRound(q.m_y), p[2] = 0;
2985
2986 // wxPoint pt = vp.GetPixFromLL(lat, lon);
2987 // p[0] = pt.x, p[1] = pt.y, p[2] = 0;
2988
2989 gluTessVertex(tobj, p, p);
2990 combine_work_data.push_back(p);
2991 }
2992 l = *j;
2993
2994 if ((sml_valid = splits != 1)) memcpy(sml, smj, sizeof smj);
2995 }
2996 gluTessEndContour(tobj);
2997 }
2998 gluTessEndPolygon(tobj);
2999
3000 gluDeleteTess(tobj);
3001
3002 for (std::list<double *>::iterator i = combine_work_data.begin();
3003 i != combine_work_data.end(); i++)
3004 delete[] *i;
3005 combine_work_data.clear();
3006}
3007
3008/* set stencil buffer to clip in this region, and optionally clear using the
3009 * current color */
3010void glChartCanvas::SetClipRegion(ViewPort &vp, const LLRegion &region) {
3011 glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // disable color buffer
3012
3013 if (s_b_useStencil) {
3014 // Create a stencil buffer for clipping to the region
3015 glEnable(GL_STENCIL_TEST);
3016 glStencilMask(0x1); // write only into bit 0 of the stencil buffer
3017 glClear(GL_STENCIL_BUFFER_BIT);
3018
3019 // We are going to write "1" into the stencil buffer wherever the region
3020 // is valid
3021 glStencilFunc(GL_ALWAYS, 1, 1);
3022 glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
3023 }
3024// #ifndef USE_ANDROID_GLES2
3025#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
3026
3027 else // Use depth buffer for clipping
3028 {
3029 glEnable(GL_DEPTH_TEST); // to enable writing to the depth buffer
3030 glDepthFunc(GL_ALWAYS); // to ensure everything you draw passes
3031 glDepthMask(GL_TRUE); // to allow writes to the depth buffer
3032
3033 glClear(GL_DEPTH_BUFFER_BIT); // for a fresh start
3034
3035 // Decompose the region into rectangles, and draw as quads
3036 // With z = 1
3037 // dep buffer clear = 1
3038 // 1 makes 0 in dep buffer, works
3039 // 0 make .5 in depth buffer
3040 // -1 makes 1 in dep buffer
3041
3042 // Depth buffer runs from 0 at z = 1 to 1 at z = -1
3043 // Draw the clip geometry at z = 0.5, giving a depth buffer value of 0.25
3044 // Subsequent drawing at z=0 (depth = 0.5) will pass if using
3045 // glDepthFunc(GL_GREATER);
3046 glTranslatef(0, 0, .5);
3047 }
3048#endif
3049
3050 s_regionColor = wxColor(0, 0, 0, 255);
3051 DrawRegion(vp, region);
3052
3053 if (s_b_useStencil) {
3054 // Now set the stencil ops to subsequently render only where the stencil
3055 // bit is "1"
3056 glStencilFunc(GL_EQUAL, 1, 1);
3057 glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
3058 }
3059// #ifndef USE_ANDROID_GLES2
3060#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
3061 else {
3062 glDepthFunc(GL_GREATER); // Set the test value
3063 glDepthMask(GL_FALSE); // disable depth buffer
3064 glTranslatef(0, 0, -.5); // reset translation
3065 }
3066#endif
3067 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // re-enable color buffer
3068}
3069
3070void glChartCanvas::SetClipRect(const ViewPort &vp, const wxRect &rect,
3071 bool b_clear) {
3072 /* for some reason this causes an occasional bug in depth mode, I cannot
3073 seem to solve it yet, so for now: */
3074 if (s_b_useStencil && s_b_useScissorTest) {
3075 wxRect vp_rect(0, 0, vp.pix_width, vp.pix_height);
3076 if (rect != vp_rect) {
3077 glEnable(GL_SCISSOR_TEST);
3078 glScissor(rect.x, vp.pix_height - rect.height - rect.y, rect.width,
3079 rect.height);
3080 }
3081#ifndef USE_ANDROID_GLES2
3082#endif
3083 return;
3084 }
3085}
3086
3087void glChartCanvas::DisableClipRegion() {
3088 glDisable(GL_SCISSOR_TEST);
3089 glDisable(GL_STENCIL_TEST);
3090 glDisable(GL_DEPTH_TEST);
3091}
3092
3093void glChartCanvas::Invalidate() {
3094 /* should probably use a different flag for this */
3095 m_cache_vp.Invalidate();
3096}
3097
3098void glChartCanvas::RenderRasterChartRegionGL(ChartBase *chart, ViewPort &vp,
3099 LLRegion &region) {
3100 ChartBaseBSB *pBSBChart = dynamic_cast<ChartBaseBSB *>(chart);
3101 if (!pBSBChart) return;
3102
3103 if (b_inCompressAllCharts)
3104 return; // don't want multiple texfactories to exist
3105
3106 // Look for the texture factory for this chart
3107 wxString key = chart->GetHashKey();
3108
3109 glTexFactory *pTexFact;
3110 ChartPathHashTexfactType &hash = g_glTextureManager->m_chart_texfactory_hash;
3111 ChartPathHashTexfactType::iterator ittf = hash.find(key);
3112
3113 // Not Found ?
3114 if (ittf == hash.end()) {
3115 hash[key] = new glTexFactory(chart, g_raster_format);
3116 hash[key]->SetHashKey(key);
3117 }
3118
3119 pTexFact = hash[key];
3120 pTexFact->SetLRUTime(++m_LRUtime);
3121
3122 // for small scales, don't use normalized coordinates for accuracy (difference
3123 // is up to 3 meters error)
3124 bool use_norm_vp =
3125 glChartCanvas::HasNormalizedViewPort(vp) && pBSBChart->GetPPM() < 1;
3126 pTexFact->PrepareTiles(vp, use_norm_vp, pBSBChart);
3127
3128 // For underzoom cases, we will define the textures as having their base
3129 // levels equivalent to a level "n" mipmap, where n is calculated, and is
3130 // always binary This way we can avoid loading much texture memory
3131
3132 int base_level;
3133 if (vp.m_projection_type == PROJECTION_MERCATOR &&
3134 chart->GetChartProjectionType() == PROJECTION_MERCATOR) {
3135 double scalefactor = pBSBChart->GetRasterScaleFactor(vp);
3136 base_level = log(scalefactor) / log(2.0);
3137
3138 if (base_level < 0) /* for overzoom */
3139 base_level = 0;
3140 if (base_level > g_mipmap_max_level) base_level = g_mipmap_max_level;
3141 } else
3142 base_level = 0; // base level should be computed per tile, for now load all
3143
3144 /* setup opengl parameters */
3145 glEnable(GL_TEXTURE_2D);
3146#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
3147 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
3148
3149 glEnableClientState(GL_VERTEX_ARRAY);
3150 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
3151
3152 if (use_norm_vp) {
3153 glPushMatrix();
3154 double lat, lon;
3155 pTexFact->GetCenter(lat, lon);
3156 MultMatrixViewPort(vp, lat, lon);
3157 }
3158#endif
3159
3160 LLBBox box = region.GetBox();
3161 int numtiles;
3162 int mem_used = 0;
3163 if (g_memCacheLimit > 0) {
3164 // GetMemoryStatus is slow on linux
3165 GetMemoryStatus(0, &mem_used);
3166 }
3167
3168 glTexTile **tiles = pTexFact->GetTiles(numtiles);
3169 for (int i = 0; i < numtiles; i++) {
3170 glTexTile *tile = tiles[i];
3171 if (region.IntersectOut(tile->box)) {
3172 /* user setting is in MB while we count exact bytes */
3173 bool bGLMemCrunch =
3174 g_tex_mem_used > g_GLOptions.m_iTextureMemorySize * 1024 * 1024;
3175 if (bGLMemCrunch) pTexFact->DeleteTexture(tile->rect);
3176 } else {
3177 bool texture = pTexFact->PrepareTexture(base_level, tile->rect,
3178 global_color_scheme, mem_used);
3179
3180 float *coords;
3181 if (use_norm_vp)
3182 coords = tile->m_coords;
3183 else {
3184 coords = new float[2 * tile->m_ncoords];
3185 for (int i = 0; i < tile->m_ncoords; i++) {
3186 wxPoint2DDouble p = vp.GetDoublePixFromLL(tile->m_coords[2 * i + 0],
3187 tile->m_coords[2 * i + 1]);
3188 coords[2 * i + 0] = p.m_x;
3189 coords[2 * i + 1] = p.m_y;
3190 }
3191 }
3192
3193#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
3194 RenderTextures(m_gldc, coords, tile->m_texcoords, 4,
3195 m_pParentCanvas->GetpVP());
3196#else
3197 if (!texture) { // failed to load, draw red
3198 glDisable(GL_TEXTURE_2D);
3199 glColor3f(1, 0, 0);
3200 }
3201
3202 glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), tile->m_texcoords);
3203 glVertexPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), coords);
3204 glDrawArrays(GL_QUADS, 0, tile->m_ncoords);
3205#endif
3206 if (!texture) glEnable(GL_TEXTURE_2D);
3207
3208 if (!use_norm_vp) delete[] coords;
3209 }
3210 }
3211
3212 glDisable(GL_TEXTURE_2D);
3213
3214#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
3215 if (use_norm_vp) glPopMatrix();
3216
3217 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
3218 glDisableClientState(GL_VERTEX_ARRAY);
3219#endif
3220}
3221
3222void glChartCanvas::RenderQuiltViewGL(ViewPort &vp,
3223 const OCPNRegion &rect_region) {
3224 if (!m_pParentCanvas->m_pQuilt->GetnCharts() ||
3225 m_pParentCanvas->m_pQuilt->IsBusy())
3226 return;
3227
3228 // render the quilt
3229 ChartBase *chart = m_pParentCanvas->m_pQuilt->GetFirstChart();
3230
3231 // Check the first, smallest scale chart
3232 if (chart) {
3233 // if( ! m_pParentCanvas->IsChartLargeEnoughToRender( chart, vp )
3234 // )
3235 // chart = NULL;
3236 }
3237
3238 LLRegion region = vp.GetLLRegion(rect_region);
3239
3240 LLRegion rendered_region;
3241 while (chart) {
3242 // This test does not need to be done for raster charts, since
3243 // we can assume that texture binding is acceptably fast regardless of the
3244 // render region, and that the quilt zoom methods choose a reasonable
3245 // reference chart.
3246 if (chart->GetChartFamily() != CHART_FAMILY_RASTER) {
3247 // if( ! m_pParentCanvas->IsChartLargeEnoughToRender(
3248 // chart, vp ) ) {
3249 // chart = m_pParentCanvas->m_pQuilt->GetNextChart();
3250 // continue;
3251 // }
3252 }
3253
3254 QuiltPatch *pqp = m_pParentCanvas->m_pQuilt->GetCurrentPatch();
3255 if (pqp->b_Valid) {
3256 LLRegion get_region = pqp->ActiveRegion;
3257 bool b_rendered = false;
3258
3259 if (!pqp->b_overlay) {
3260 get_region.Intersect(region);
3261 if (!get_region.Empty()) {
3262 if (chart->GetChartFamily() == CHART_FAMILY_RASTER) {
3263 ChartBaseBSB *Patch_Ch_BSB = dynamic_cast<ChartBaseBSB *>(chart);
3264 if (Patch_Ch_BSB) {
3265 SetClipRegion(vp, get_region /*pqp->quilt_region*/);
3266 RenderRasterChartRegionGL(chart, vp, pqp->ActiveRegion);
3267 DisableClipRegion();
3268
3269 b_rendered = true;
3270 } else if (chart->GetChartType() == CHART_TYPE_MBTILES) {
3271 SetClipRegion(vp, pqp->ActiveRegion /*pqp->quilt_region*/);
3272 chart->RenderRegionViewOnGL(*m_pcontext, vp, rect_region,
3273 get_region);
3274 DisableClipRegion();
3275 }
3276
3277 } else if (chart->GetChartFamily() == CHART_FAMILY_VECTOR) {
3278 if (chart->GetChartType() == CHART_TYPE_CM93COMP) {
3279 RenderNoDTA(vp, get_region);
3280 chart->RenderRegionViewOnGL(*m_pcontext, vp, rect_region,
3281 get_region);
3282 } else {
3283 s57chart *Chs57 = dynamic_cast<s57chart *>(chart);
3284 if (Chs57) {
3285 if (Chs57->m_RAZBuilt) {
3286 RenderNoDTA(vp, get_region);
3287 Chs57->RenderRegionViewOnGLNoText(*m_pcontext, vp,
3288 rect_region, get_region);
3289 DisableClipRegion();
3290 } else {
3291 // The SENC is quesed for building, so..
3292 // Show GSHHS with compatible color scheme in the meantime.
3293 ocpnDC gldc(*this);
3294 const LLRegion &oregion = get_region;
3295 LLBBox box = oregion.GetBox();
3296
3297 wxPoint p1 =
3298 vp.GetPixFromLL(box.GetMaxLat(), box.GetMinLon());
3299 wxPoint p2 =
3300 vp.GetPixFromLL(box.GetMaxLat(), box.GetMaxLon());
3301 wxPoint p3 =
3302 vp.GetPixFromLL(box.GetMinLat(), box.GetMaxLon());
3303 wxPoint p4 =
3304 vp.GetPixFromLL(box.GetMinLat(), box.GetMinLon());
3305
3306 wxRect srect(p1.x, p1.y, p3.x - p1.x, p4.y - p2.y);
3307
3308 bool world = false;
3309 ViewPort cvp = ClippedViewport(vp, get_region);
3310 if (m_pParentCanvas->GetWorldBackgroundChart()) {
3311 SetClipRegion(cvp, get_region);
3312 m_pParentCanvas->GetWorldBackgroundChart()->SetColorsDirect(
3313 GetGlobalColor(_T ( "LANDA" )),
3314 GetGlobalColor(_T ( "DEPMS" )));
3315 RenderWorldChart(gldc, cvp, srect, world);
3316 m_pParentCanvas->GetWorldBackgroundChart()->SetColorScheme(
3317 global_color_scheme);
3318 DisableClipRegion();
3319 }
3320 }
3321 } else {
3322 ChartPlugInWrapper *ChPI =
3323 dynamic_cast<ChartPlugInWrapper *>(chart);
3324 if (ChPI) {
3325 SetClipRegion(vp, get_region);
3326 RenderNoDTA(vp, get_region);
3327 ChPI->RenderRegionViewOnGLNoText(*m_pcontext, vp, rect_region,
3328 get_region);
3329 DisableClipRegion();
3330
3331 } else {
3332 SetClipRegion(vp, get_region);
3333 RenderNoDTA(vp, get_region);
3334 chart->RenderRegionViewOnGL(*m_pcontext, vp, rect_region,
3335 get_region);
3336 DisableClipRegion();
3337 }
3338 }
3339 }
3340 }
3341 }
3342 }
3343
3344 if (b_rendered) {
3345 // LLRegion get_region = pqp->ActiveRegion;
3346 // get_region.Intersect( Region ); not technically
3347 // required?
3348 // rendered_region.Union(get_region);
3349 }
3350 }
3351
3352 chart = m_pParentCanvas->m_pQuilt->GetNextChart();
3353 }
3354
3355 // Render any Overlay patches for s57 charts(cells)
3356 if (m_pParentCanvas->m_pQuilt->HasOverlays()) {
3357 ChartBase *pch = m_pParentCanvas->m_pQuilt->GetFirstChart();
3358 while (pch) {
3359 QuiltPatch *pqp = m_pParentCanvas->m_pQuilt->GetCurrentPatch();
3360 if (pqp->b_Valid && pqp->b_overlay &&
3361 pch->GetChartFamily() == CHART_FAMILY_VECTOR) {
3362 LLRegion get_region = pqp->ActiveRegion;
3363
3364 get_region.Intersect(region);
3365 if (!get_region.Empty()) {
3366 s57chart *Chs57 = dynamic_cast<s57chart *>(pch);
3367 if (Chs57)
3368 Chs57->RenderOverlayRegionViewOnGL(*m_pcontext, vp, rect_region,
3369 get_region);
3370 else {
3371 ChartPlugInWrapper *ChPI = dynamic_cast<ChartPlugInWrapper *>(pch);
3372 if (ChPI) {
3373 ChPI->RenderRegionViewOnGL(*m_pcontext, vp, rect_region,
3374 get_region);
3375 }
3376 }
3377 }
3378 }
3379
3380 pch = m_pParentCanvas->m_pQuilt->GetNextChart();
3381 }
3382 }
3383
3384 // Hilite rollover of standard chart key
3385 ViewPort vph = m_pParentCanvas->GetVP();
3386 for (auto &index : m_pParentCanvas->m_pQuilt->GetHiLiteIndexArray()) {
3387 const ChartTableEntry &cte = ChartData->GetChartTableEntry(index);
3388 LLRegion hiregion =
3389 m_pParentCanvas->m_pQuilt->GetChartQuiltRegion(cte, vph);
3390
3391 if (!hiregion.Empty()) {
3392 glEnable(GL_BLEND);
3393
3394 double hitrans;
3395 switch (global_color_scheme) {
3396 case GLOBAL_COLOR_SCHEME_DAY:
3397 hitrans = .4;
3398 break;
3399 case GLOBAL_COLOR_SCHEME_DUSK:
3400 hitrans = .2;
3401 break;
3402 case GLOBAL_COLOR_SCHEME_NIGHT:
3403 hitrans = .1;
3404 break;
3405 default:
3406 hitrans = .4;
3407 break;
3408 }
3409
3410#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
3411
3412 glColor4f((float).8, (float).4, (float).4, (float)hitrans);
3413#else
3414 s_regionColor = wxColor(204, 102, 102, hitrans * 256);
3415#endif
3416
3417 DrawRegion(vp, hiregion);
3418
3419 glDisable(GL_BLEND);
3420 }
3421 }
3422
3423#if 0
3424 LLRegion hiregion = m_pParentCanvas->m_pQuilt->GetHiliteRegion();
3425
3426 if (!hiregion.Empty()) {
3427 glEnable(GL_BLEND);
3428
3429 double hitrans;
3430 switch (global_color_scheme) {
3431 case GLOBAL_COLOR_SCHEME_DAY:
3432 hitrans = .4;
3433 break;
3434 case GLOBAL_COLOR_SCHEME_DUSK:
3435 hitrans = .2;
3436 break;
3437 case GLOBAL_COLOR_SCHEME_NIGHT:
3438 hitrans = .1;
3439 break;
3440 default:
3441 hitrans = .4;
3442 break;
3443 }
3444
3445//#ifndef USE_ANDROID_GLES2
3446#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
3447
3448 glColor4f((float).8, (float).4, (float).4, (float)hitrans);
3449#else
3450 s_regionColor = wxColor(204, 102, 102, hitrans * 256);
3451#endif
3452
3453 DrawRegion(vp, hiregion);
3454
3455 glDisable(GL_BLEND);
3456 }
3457#endif
3458
3459 m_pParentCanvas->m_pQuilt->SetRenderedVP(vp);
3460}
3461
3462void glChartCanvas::RenderQuiltViewGLText(ViewPort &vp,
3463 const OCPNRegion &rect_region) {
3464 if (!m_pParentCanvas->m_pQuilt->GetnCharts() ||
3465 m_pParentCanvas->m_pQuilt->IsBusy())
3466 return;
3467
3468 // render the quilt
3469 ChartBase *chart = m_pParentCanvas->m_pQuilt->GetLargestScaleChart();
3470
3471 LLRegion region = vp.GetLLRegion(rect_region);
3472
3473 LLRegion rendered_region;
3474 while (chart) {
3475 QuiltPatch *pqp = m_pParentCanvas->m_pQuilt->GetCurrentPatch();
3476 if (pqp->b_Valid) {
3477 LLRegion get_region = pqp->ActiveRegion;
3478
3479 if (!pqp->b_overlay) {
3480 if (chart->GetChartFamily() == CHART_FAMILY_VECTOR) {
3481 s57chart *Chs57 = dynamic_cast<s57chart *>(chart);
3482 if (Chs57) {
3483 Chs57->RenderViewOnGLTextOnly(*m_pcontext, vp);
3484 } else {
3485 ChartPlugInWrapper *ChPI =
3486 dynamic_cast<ChartPlugInWrapper *>(chart);
3487 if (ChPI) {
3488 ChPI->RenderRegionViewOnGLTextOnly(*m_pcontext, vp, rect_region);
3489 }
3490 }
3491 }
3492 }
3493 }
3494
3495 chart = m_pParentCanvas->m_pQuilt->GetNextSmallerScaleChart();
3496 }
3497
3498 /*
3499 // Render any Overlay patches for s57 charts(cells)
3500 if( m_pParentCanvas->m_pQuilt->HasOverlays() ) {
3501 ChartBase *pch = m_pParentCanvas->m_pQuilt->GetFirstChart();
3502 while( pch ) {
3503 QuiltPatch *pqp =
3504 m_pParentCanvas->m_pQuilt->GetCurrentPatch(); if( pqp->b_Valid &&
3505 pqp->b_overlay && pch->GetChartFamily() == CHART_FAMILY_VECTOR ) { LLRegion
3506 get_region = pqp->ActiveRegion;
3507
3508 get_region.Intersect( region );
3509 if( !get_region.Empty() ) {
3510 s57chart *Chs57 = dynamic_cast<s57chart*>( pch );
3511 if( Chs57 )
3512 Chs57->RenderOverlayRegionViewOnGL( *m_pcontext,
3513 vp, rect_region, get_region );
3514 }
3515 }
3516
3517 pch = m_pParentCanvas->m_pQuilt->GetNextChart();
3518 }
3519 }
3520 */
3521}
3522
3523void glChartCanvas::RenderCharts(ocpnDC &dc, const OCPNRegion &rect_region) {
3524 ViewPort &vp = m_pParentCanvas->VPoint;
3525
3526 // Only for cm93 (not quilted), SetVPParms can change the valid region of the
3527 // chart we need to know this before rendering the chart so we can compute the
3528 // background region and nodta regions correctly. I would prefer to just
3529 // perform this here (or in SetViewPoint) for all vector charts instead of in
3530 // their render routine, but how to handle quilted cases?
3531 if (!vp.b_quilt &&
3532 m_pParentCanvas->m_singleChart->GetChartType() == CHART_TYPE_CM93COMP)
3533 static_cast<cm93compchart *>(m_pParentCanvas->m_singleChart)
3534 ->SetVPParms(vp);
3535
3536 LLRegion chart_region;
3537 if (!vp.b_quilt &&
3538 (m_pParentCanvas->m_singleChart->GetChartType() == CHART_TYPE_PLUGIN)) {
3539 if (m_pParentCanvas->m_singleChart->GetChartFamily() ==
3540 CHART_FAMILY_RASTER) {
3541 // We do this the hard way, since PlugIn Raster charts do not understand
3542 // LLRegion yet...
3543 double ll[8];
3544 ChartPlugInWrapper *cpw =
3545 dynamic_cast<ChartPlugInWrapper *>(m_pParentCanvas->m_singleChart);
3546 if (!cpw) return;
3547
3548 cpw->chartpix_to_latlong(0, 0, ll + 0, ll + 1);
3549 cpw->chartpix_to_latlong(0, cpw->GetSize_Y(), ll + 2, ll + 3);
3550 cpw->chartpix_to_latlong(cpw->GetSize_X(), cpw->GetSize_Y(), ll + 4,
3551 ll + 5);
3552 cpw->chartpix_to_latlong(cpw->GetSize_X(), 0, ll + 6, ll + 7);
3553
3554 // for now don't allow raster charts to cross both 0 meridian and IDL
3555 // (complicated to deal with)
3556 for (int i = 1; i < 6; i += 2)
3557 if (fabs(ll[i] - ll[i + 2]) > 180) {
3558 // we detect crossing idl here, make all longitudes positive
3559 for (int i = 1; i < 8; i += 2)
3560 if (ll[i] < 0) ll[i] += 360;
3561 break;
3562 }
3563
3564 chart_region = LLRegion(4, ll);
3565 } else {
3566 Extent ext;
3567 m_pParentCanvas->m_singleChart->GetChartExtent(&ext);
3568
3569 double ll[8] = {ext.SLAT, ext.WLON, ext.SLAT, ext.ELON,
3570 ext.NLAT, ext.ELON, ext.NLAT, ext.WLON};
3571 chart_region = LLRegion(4, ll);
3572 }
3573 } else
3574 chart_region = vp.b_quilt
3575 ? m_pParentCanvas->m_pQuilt->GetFullQuiltRegion()
3576 : m_pParentCanvas->m_singleChart->GetValidRegion();
3577
3578 bool world_view = false;
3579 for (OCPNRegionIterator upd(rect_region); upd.HaveRects(); upd.NextRect()) {
3580 wxRect rect = upd.GetRect();
3581 LLRegion background_region = vp.GetLLRegion(rect);
3582 // Remove the valid chart area to find the region NOT covered by the
3583 // charts
3584 background_region.Subtract(chart_region);
3585
3586 if (!background_region.Empty()) {
3587 ViewPort cvp = ClippedViewport(vp, background_region);
3588 SetClipRect(cvp, rect, false);
3589 RenderWorldChart(dc, cvp, rect, world_view);
3590 DisableClipRegion();
3591 }
3592 }
3593
3594 if (vp.b_quilt)
3595 RenderQuiltViewGL(vp, rect_region);
3596 else {
3597 LLRegion region = vp.GetLLRegion(rect_region);
3598 if (m_pParentCanvas->m_singleChart->GetChartFamily() ==
3599 CHART_FAMILY_RASTER) {
3600 if (m_pParentCanvas->m_singleChart->GetChartType() == CHART_TYPE_MBTILES)
3601 m_pParentCanvas->m_singleChart->RenderRegionViewOnGL(
3602 *m_pcontext, vp, rect_region, region);
3603 else
3604 RenderRasterChartRegionGL(m_pParentCanvas->m_singleChart, vp, region);
3605 } else if (m_pParentCanvas->m_singleChart->GetChartFamily() ==
3606 CHART_FAMILY_VECTOR) {
3607 chart_region.Intersect(region);
3608 RenderNoDTA(vp, chart_region);
3609 m_pParentCanvas->m_singleChart->RenderRegionViewOnGL(*m_pcontext, vp,
3610 rect_region, region);
3611 }
3612 }
3613 glUseProgram(0);
3614}
3615
3616void glChartCanvas::RenderNoDTA(ViewPort &vp, const LLRegion &region,
3617 int transparency) {
3618 wxColour color = GetGlobalColor(_T ( "NODTA" ));
3619#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
3620 if (color.IsOk())
3621 glColor4ub(color.Red(), color.Green(), color.Blue(), transparency);
3622 else
3623 glColor4ub(163, 180, 183, transparency);
3624
3625 glEnable(GL_BLEND);
3626 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
3627
3628#else
3629 // Store the color for tesselator callback pickup.
3630 s_regionColor = color;
3631#endif
3632
3633 DrawRegion(vp, region);
3634}
3635
3636/* render world chart, but only in this rectangle */
3637void glChartCanvas::RenderWorldChart(ocpnDC &dc, ViewPort &vp, wxRect &rect,
3638 bool &world_view) {
3639 // set gl color to water
3640 wxColour water = m_pParentCanvas->pWorldBackgroundChart->water;
3641
3642 glEnable(GL_SCISSOR_TEST);
3643 glScissor(rect.x, vp.pix_height - rect.height - rect.y, rect.width,
3644 rect.height);
3645
3646 // clear background
3647 if (!world_view) {
3648 if (!world_view) {
3649 int x1 = rect.x, y1 = rect.y, x2 = x1 + rect.width, y2 = y1 + rect.height;
3650#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
3651
3652 GLShaderProgram *shader = pcolor_tri_shader_program[GetCanvasIndex()];
3653 shader->Bind();
3654
3655 float colorv[4];
3656 colorv[0] = water.Red() / float(256);
3657 colorv[1] = water.Green() / float(256);
3658 colorv[2] = water.Blue() / float(256);
3659 colorv[3] = 1.0;
3660 shader->SetUniform4fv("color", colorv);
3661
3662 float pf[8];
3663 pf[0] = x2;
3664 pf[1] = y1;
3665 pf[2] = x2;
3666 pf[3] = y2;
3667 pf[4] = x1;
3668 pf[5] = y1;
3669 pf[6] = x1;
3670 pf[7] = y2;
3671 shader->SetAttributePointerf("position", pf);
3672
3673 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
3674
3675 shader->UnBind();
3676
3677#else
3678#endif
3679 }
3680 }
3681
3682 // m_pParentCanvas->pWorldBackgroundChart->RenderViewOnDC(dc, vp);
3683 gShapeBasemap.RenderViewOnDC(dc, vp);
3684
3685 glDisable(GL_SCISSOR_TEST);
3686}
3687
3688/* these are the overlay objects which move with the charts and
3689 are not frequently updated (not ships etc..)
3690
3691 many overlay objects are fixed to a geographical location or
3692 grounded as opposed to the floating overlay objects. */
3693void glChartCanvas::DrawGroundedOverlayObjects(ocpnDC &dc, ViewPort &vp) {
3694 m_pParentCanvas->RenderAllChartOutlines(dc, vp);
3695
3696 DrawStaticRoutesTracksAndWaypoints(vp);
3697
3698 DisableClipRegion();
3699}
3700
3701void glChartCanvas::DrawGLTidesInBBox(ocpnDC &dc, LLBBox &BBox) {
3702 // At small scale, we render the Tide icon as a texture for best performance
3703 if (m_pParentCanvas->GetVP().chart_scale > 500000) {
3704 // Prepare the texture if necessary
3705
3706 if (!m_tideTex) {
3707 wxBitmap bmp = m_pParentCanvas->GetTideBitmap();
3708 if (!bmp.Ok()) return;
3709
3710 wxImage image = bmp.ConvertToImage();
3711 int w = image.GetWidth(), h = image.GetHeight();
3712
3713 int tex_w, tex_h;
3714 if (g_texture_rectangle_format == GL_TEXTURE_2D)
3715 tex_w = w, tex_h = h;
3716 else
3717 tex_w = NextPow2(w), tex_h = NextPow2(h);
3718
3719 m_tideTexWidth = tex_w;
3720 m_tideTexHeight = tex_h;
3721
3722 unsigned char *d = image.GetData();
3723 unsigned char *a = image.GetAlpha();
3724
3725 unsigned char mr, mg, mb;
3726 if (!a) image.GetOrFindMaskColour(&mr, &mg, &mb);
3727
3728 unsigned char *e = new unsigned char[4 * w * h];
3729 if (e && d) {
3730 for (int y = 0; y < h; y++)
3731 for (int x = 0; x < w; x++) {
3732 unsigned char r, g, b;
3733 int off = (y * w + x);
3734 r = d[off * 3 + 0];
3735 g = d[off * 3 + 1];
3736 b = d[off * 3 + 2];
3737
3738 e[off * 4 + 0] = r;
3739 e[off * 4 + 1] = g;
3740 e[off * 4 + 2] = b;
3741
3742 e[off * 4 + 3] =
3743 a ? a[off] : ((r == mr) && (g == mg) && (b == mb) ? 0 : 255);
3744 }
3745 }
3746
3747 glGenTextures(1, &m_tideTex);
3748
3749 glBindTexture(GL_TEXTURE_2D, m_tideTex);
3750 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3751 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3752
3753 if (g_texture_rectangle_format == GL_TEXTURE_2D)
3754 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA,
3755 GL_UNSIGNED_BYTE, e);
3756 else {
3757 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex_w, tex_h, 0, GL_RGBA,
3758 GL_UNSIGNED_BYTE, 0);
3759 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE,
3760 e);
3761 }
3762
3763 delete[] e;
3764 }
3765
3766 // Texture is ready
3767
3768 glBindTexture(GL_TEXTURE_2D, m_tideTex);
3769 glEnable(GL_TEXTURE_2D);
3770 glEnable(GL_BLEND);
3771
3772#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
3773#else
3774 for (int i = 1; i < ptcmgr->Get_max_IDX() + 1; i++) {
3775 const IDX_entry *pIDX = ptcmgr->GetIDX_entry(i);
3776
3777 char type = pIDX->IDX_type; // Entry "TCtcIUu" identifier
3778 if ((type == 't') || (type == 'T')) // only Tides
3779 {
3780 double lon = pIDX->IDX_lon;
3781 double lat = pIDX->IDX_lat;
3782
3783 if (BBox.Contains(lat, lon)) {
3784 wxPoint r;
3785 m_pParentCanvas->GetCanvasPointPix(lat, lon, &r);
3786
3787 float xp = r.x;
3788 float yp = r.y;
3789
3790 double scale = 1.0;
3791#ifdef __ANDROID__
3792 scale *= getAndroidDisplayDensity();
3793#endif
3794 double width2 = scale * m_tideTexWidth / 2;
3795 double height2 = scale * m_tideTexHeight / 2;
3796
3797 float coords[8];
3798 float uv[8];
3799
3800 // normal uv
3801 uv[0] = 0;
3802 uv[1] = 0;
3803 uv[2] = 0;
3804 uv[3] = 1;
3805 uv[4] = 1;
3806 uv[5] = 1;
3807 uv[6] = 1;
3808 uv[7] = 0;
3809
3810 coords[0] = xp - width2;
3811 coords[1] = yp - height2;
3812 coords[2] = xp - width2;
3813 coords[3] = yp + height2;
3814 coords[4] = xp + width2;
3815 coords[5] = yp + height2;
3816 coords[6] = xp + width2;
3817 coords[7] = yp - height2;
3818
3819 RenderTextures(dc, coords, uv, 4, m_pParentCanvas->GetpVP());
3820 }
3821 } // type 'T"
3822 } // loop
3823
3824#endif
3825
3826 glDisable(GL_TEXTURE_2D);
3827 glDisable(GL_BLEND);
3828 glBindTexture(GL_TEXTURE_2D, 0);
3829 } else
3830 m_pParentCanvas->DrawAllTidesInBBox(dc, BBox);
3831}
3832
3833void glChartCanvas::DrawGLCurrentsInBBox(ocpnDC &dc, LLBBox &BBox) {
3834 m_pParentCanvas->DrawAllCurrentsInBBox(dc, BBox);
3835}
3836
3837void glChartCanvas::SetColorScheme(ColorScheme cs) {
3838 if (!m_bsetup) return;
3839
3840 glDeleteTextures(1, &m_tideTex);
3841 glDeleteTextures(1, &m_currentTex);
3842 m_tideTex = 0;
3843 m_currentTex = 0;
3844 ownship_color = -1;
3845}
3846
3847void glChartCanvas::RenderGLAlertMessage() {
3848 if (!m_pParentCanvas->GetAlertString().IsEmpty()) {
3849 wxString msg = m_pParentCanvas->GetAlertString();
3850
3851 wxFont *pfont = GetOCPNScaledFont(_("Dialog"));
3852 m_gldc.SetFont(*pfont);
3853
3854 int w, h;
3855 wxScreenDC sdc;
3856 sdc.GetTextExtent(msg, &w, &h, NULL, NULL, pfont);
3857
3858 h += 2;
3859 w += 4;
3860 int yp =
3861 m_pParentCanvas->VPoint.pix_height - GetChartbarHeight() - h - (h / 4);
3862
3863 wxRect sbr = m_pParentCanvas->GetScaleBarRect();
3864 int xp = sbr.x + sbr.width + 5;
3865
3866 wxPen ppPen1(GetGlobalColor(_T ( "UBLCK" )), 1, wxPENSTYLE_SOLID);
3867 m_gldc.SetPen(ppPen1);
3868 m_gldc.SetBrush(wxBrush(GetGlobalColor(_T ( "YELO1" ))));
3869
3870 m_gldc.DrawRectangle(xp, yp, w, h);
3871
3872 m_gldc.DrawText(msg, xp, yp);
3873 }
3874}
3875
3876unsigned long quiltHash;
3877int refChartIndex;
3878extern wxLongLong s_t0;
3879
3880int n_render;
3881void glChartCanvas::Render() {
3882 if (!m_bsetup || !m_pParentCanvas->m_pQuilt ||
3883 (m_pParentCanvas->VPoint.b_quilt && !m_pParentCanvas->m_pQuilt) ||
3884 (!m_pParentCanvas->VPoint.b_quilt && !m_pParentCanvas->m_singleChart)) {
3885#ifdef __WXGTK__ // for some reason in gtk, a swap is needed here to get an
3886 // initial screen update
3887 SwapBuffers();
3888#endif
3889 if (!g_PrintingInProgress) return;
3890 }
3891
3892 if (m_binPinch) return;
3893
3894#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
3895 loadShaders(GetCanvasIndex());
3896 configureShaders(m_pParentCanvas->VPoint);
3897#endif
3898
3899#ifdef USE_ANDROID_GLES2
3900
3901 OCPNStopWatch sw;
3902
3903 if (m_binPinch) return;
3904
3905 // qDebug() << "Render" << m_pParentCanvas->m_canvasIndex << GetPosition().x
3906 // << GetSize().x << m_pParentCanvas->GetPosition().x << m_pcontext;
3907
3908 // if(m_pParentCanvas->m_canvasIndex == 0) return;
3909
3910 // Do any setup required...
3911
3912 bool recompose = false;
3913 if (m_pParentCanvas->VPoint.b_quilt && m_pParentCanvas->m_pQuilt &&
3914 !m_pParentCanvas->m_pQuilt->IsComposed()) {
3915 if (m_pParentCanvas->VPoint.IsValid()) {
3916 m_pParentCanvas->m_pQuilt->Compose(m_pParentCanvas->VPoint);
3917 m_pParentCanvas->UpdateCanvasControlBar();
3918 recompose = true;
3919 } else
3920 return;
3921 }
3922
3923 // Check to see if the Compose() call forced a SENC build.
3924 // If so, zoom the canvas just slightly to force a deferred redraw of the
3925 // full screen.
3926 if (sw.GetTime() > 2000) { // long enough to detect SENC build.
3927 m_pParentCanvas->ZoomCanvas(1.0001, false);
3928 }
3929
3930 // qDebug() << "RenderTime1" << sw.GetTime();
3931
3932 s_tess_vertex_idx = 0;
3933 quiltHash = m_pParentCanvas->m_pQuilt->GetXStackHash();
3934 refChartIndex = m_pParentCanvas->m_pQuilt->GetRefChartdbIndex();
3935
3936#endif
3937
3938#ifdef __WXOSX__
3939 // Support scaled HDPI displays.
3940 m_displayScale = GetContentScaleFactor();
3941#endif
3942 m_pParentCanvas->VPoint.SetPixelScale(m_displayScale);
3943
3944 m_last_render_time = wxDateTime::Now().GetTicks();
3945
3946 // we don't care about jobs that are now off screen
3947 // clear out and it will be repopulated during render
3948 if (g_GLOptions.m_bTextureCompression &&
3949 !g_GLOptions.m_bTextureCompressionCaching)
3950 g_glTextureManager->ClearJobList();
3951
3952 ocpnDC gldc(*this);
3953
3954 int gl_width, gl_height;
3955 gl_width = m_pParentCanvas->VPoint.pix_width;
3956 gl_height = m_pParentCanvas->VPoint.pix_height;
3957
3958 // Take a copy for use later by DC
3959 m_glcanvas_width = gl_width;
3960 m_glcanvas_height = gl_height;
3961
3962 // Avoid some harmonic difficulties with odd-size glCanvas
3963 bool b_odd = false;
3964 if (gl_height & 1) {
3965 gl_height -= 1;
3966 ViewPort *vp = m_pParentCanvas->GetpVP();
3967 vp->pix_height = gl_height;
3968 b_odd = true;
3969 }
3970
3971 if (gl_width & 1) {
3972 gl_width -= 1;
3973 ViewPort *vp = m_pParentCanvas->GetpVP();
3974 vp->pix_width = gl_width;
3975 b_odd = true;
3976 }
3977
3978 // Set the shader viewport transform matrix
3979 // Using the adjusted dimensions
3980 if (b_odd) {
3981 ViewPort *vp = m_pParentCanvas->GetpVP();
3982 mat4x4 m;
3983 mat4x4_identity(m);
3984 mat4x4_scale_aniso((float(*)[4])vp->vp_matrix_transform, m,
3985 2.0 / (float)vp->pix_width, -2.0 / (float)vp->pix_height,
3986 1.0);
3987 mat4x4_translate_in_place((float(*)[4])vp->vp_matrix_transform,
3988 -vp->pix_width / 2, -vp->pix_height / 2, 0);
3989 }
3990
3991 // @todo: If the intention was to work with the same ViewPort object, use a
3992 // reference instead. Making a copy of VPoint here means that any changes to
3993 // VPoint will not affect m_pParentCanvas->VPoint. It's not clear if this is
3994 // the intended behavior.
3995 ViewPort VPoint = m_pParentCanvas->VPoint;
3996
3997 OCPNRegion screen_region(wxRect(0, 0, gl_width, gl_height));
3998 glViewport(0, 0, (GLint)gl_width, (GLint)gl_height);
3999
4000// #ifndef USE_ANDROID_GLES2
4001#if !defined(USE_ANDROID_GLES2)
4002 glMatrixMode(GL_PROJECTION);
4003 glLoadIdentity();
4004
4005 glOrtho(0, (GLint)gl_width, (GLint)gl_height, 0, -1, 1);
4006 glMatrixMode(GL_MODELVIEW);
4007 glLoadIdentity();
4008#endif
4009
4010 if (s_b_useStencil) {
4011 glEnable(GL_STENCIL_TEST);
4012 glStencilMask(0xff);
4013 glClear(GL_STENCIL_BUFFER_BIT);
4014 glDisable(GL_STENCIL_TEST);
4015 }
4016
4017 // set opengl settings that don't normally change
4018 // this should be able to go in SetupOpenGL, but it's
4019 // safer here incase a plugin mangles these
4020 if (g_GLOptions.m_GLLineSmoothing) glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
4021 if (g_GLOptions.m_GLPolygonSmoothing)
4022 glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
4023 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
4024
4025 // Delete any textures known to the GPU that
4026 // belong to charts which will not be used in this render
4027 // This is done chart-by-chart...later we will scrub for unused textures
4028 // that belong to charts which ARE used in this render, if we need to....
4029
4030 g_glTextureManager->TextureCrunch(0.8);
4031
4032 // If we plan to post process the display, don't use accelerated panning
4033 double scale_factor = VPoint.ref_scale / VPoint.chart_scale;
4034
4035 bool bpost_hilite = !m_pParentCanvas->m_pQuilt->GetHiliteRegion().Empty();
4036 bool useFBO = false;
4037 int sx = gl_width;
4038 int sy = gl_height;
4039
4040 // Try to use the framebuffer object's cache of the last frame
4041 // to accelerate drawing this frame (if overlapping)
4042 if (m_b_BuiltFBO && !bpost_hilite
4043 //&& VPoint.tilt == 0 // disabling fbo in tilt mode gives better quality
4044 // but slower
4045 ) {
4046 // Is this viewpoint the same as the previously painted one?
4047 bool b_newview = true;
4048 bool b_full = false;
4049
4050 // If the view is the same we do no updates,
4051 // Just render cached texture to the framebuffer
4052 if (m_cache_vp.view_scale_ppm == VPoint.view_scale_ppm &&
4053 m_cache_vp.rotation == VPoint.rotation &&
4054 m_cache_vp.clat == VPoint.clat && m_cache_vp.clon == VPoint.clon &&
4055 m_cache_vp.IsValid() && m_cache_vp.pix_height == VPoint.pix_height &&
4056 m_cache_current_ch == m_pParentCanvas->m_singleChart) {
4057 b_newview = false;
4058 }
4059
4060#ifdef USE_ANDROID_GLES2
4061 if (recompose) b_newview = true;
4062
4063 if (m_bforcefull) {
4064 b_newview = true;
4065 b_full = true;
4066 }
4067
4068 // If no charts are to be rendered, we need to refresh the entire display
4069 // This fixes a problem with routes/tracks/marks rendering on pans at very
4070 // small scale. It is a workaround, so finding root cause should be
4071 // considered a TODO
4072
4073 if (VPoint.b_quilt) {
4074 ChartBase *chart = m_pParentCanvas->m_pQuilt->GetFirstChart();
4075 if (!chart) b_full = true;
4076 }
4077
4078#endif
4079
4080 if (b_newview) {
4081 float dx = 0;
4082 float dy = 0;
4083
4084 bool accelerated_pan = false;
4085 // if (g_in_inertia)
4086 // printf("--- accpan condition %d %d\n",
4087 // g_GLOptions.m_bUseAcceleratedPanning,
4088 // m_cache_vp.IsValid());
4089 // else
4090 // printf("||| accpan condition %d %d\n",
4091 // g_GLOptions.m_bUseAcceleratedPanning,
4092 // m_cache_vp.IsValid());
4093
4094 if (g_GLOptions.m_bUseAcceleratedPanning && m_cache_vp.IsValid() &&
4095 (VPoint.m_projection_type == PROJECTION_MERCATOR ||
4096 VPoint.m_projection_type == PROJECTION_EQUIRECTANGULAR) &&
4097 m_cache_vp.pix_height == VPoint.pix_height) {
4098 wxPoint2DDouble c_old =
4099 VPoint.GetDoublePixFromLL(VPoint.clat, VPoint.clon) *
4100 m_displayScale;
4101 wxPoint2DDouble c_new =
4102 m_cache_vp.GetDoublePixFromLL(VPoint.clat, VPoint.clon) *
4103 m_displayScale;
4104
4105 dy = wxRound(c_new.m_y - c_old.m_y);
4106 dx = wxRound(c_new.m_x - c_old.m_x);
4107
4108 // The math below using the previous frame's texture does not really
4109 // work for sub-pixel pans.
4110 // TODO is to rethink this.
4111 // Meanwhile, require the accelerated pans to be whole pixel multiples
4112 // only. This is not as bad as it sounds. Keyboard and mouse pans are
4113 // whole_pixel moves. However, autofollow at large scale is certainly
4114 // not.
4115
4116 double deltax = c_new.m_x - c_old.m_x;
4117 double deltay = c_new.m_y - c_old.m_y;
4118
4119 bool b_whole_pixel = true;
4120 if ((fabs(deltax - dx) > 1e-2) || (fabs(deltay - dy) > 1e-2))
4121 b_whole_pixel = false;
4122
4123 accelerated_pan = b_whole_pixel && abs(dx) < m_cache_tex_x &&
4124 abs(dy) < m_cache_tex_y &&
4125 (abs(dx) > 0 || (abs(dy) > 0));
4126
4127 // if (g_in_inertia && !accelerated_pan)
4128 // printf("--- accpan %d %d %g %g\n", accelerated_pan,
4129 // b_whole_pixel, dx, dy);
4130 }
4131
4132 // FBO swapping has trouble with Retina display on MacOS Monterey.
4133 // So, disable accelerated pan ops on this case.
4134 if (m_displayScale > 1) accelerated_pan = false;
4135
4136 // FIXME (dave) There are some display artifact troubles using accPan on
4137 // rotation.
4138 // Especially seen on sparse RNC rendering
4139 if (fabs(VPoint.rotation) > 0) accelerated_pan = false;
4140
4141 // do we allow accelerated panning? can we perform it here?
4142#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
4143#else // GLES2
4144 // enable rendering to texture in framebuffer object
4145 glBindFramebuffer(GL_FRAMEBUFFER, m_fb0);
4146
4147 if (VPoint.chart_scale < 5000) b_full = true;
4148
4149 if (VPoint.chart_scale > 5e7) b_full = true;
4150
4151 if (b_full) accelerated_pan = false;
4152
4153 if (accelerated_pan) {
4154 if ((dx != 0) || (dy != 0)) { // Anything to do?
4155
4156 // calculate the new regions to render
4157 // add extra pixels to avoid coordindate rounding issues at large
4158 // scale
4159 OCPNRegion update_region;
4160
4161 int fluff = 2;
4162
4163 // Avoid rendering artifacts caused by Multi Sampling (MSAA)
4164 if (VPoint.chart_scale < 10000) fluff = 8;
4165
4166 if (dy > 0 && dy < gl_height)
4167 update_region.Union(
4168 wxRect(0, gl_height - (dy + fluff), gl_width, dy + fluff));
4169 else if (dy < 0)
4170 update_region.Union(wxRect(0, 0, gl_width, -dy + fluff));
4171
4172 if (dx > 0 && dx < gl_width)
4173 update_region.Union(
4174 wxRect(gl_width - (dx + fluff), 0, dx + fluff, gl_height));
4175 else if (dx < 0)
4176 update_region.Union(wxRect(0, 0, -dx + fluff, gl_height));
4177
4178 m_cache_page = !m_cache_page; /* page flip */
4179
4180 // Bind the destination (target frame) texture to the frame buffer
4181 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
4182 GL_TEXTURE_2D, m_cache_tex[m_cache_page], 0);
4183
4184 // Before rendering anything, clear the color buffers
4185 // wxColour color = GetGlobalColor(_T ( "NODTA" ));
4186 // glClearColor(color.Red() / 256., color.Green() / 256.,
4187 // color.Blue() / 256., 1.0);
4188 // glClear(GL_COLOR_BUFFER_BIT);
4189
4190 // First render the new content into the update region
4191 // if (g_in_inertia)
4192 // printf("--- R2a %g\n",
4193 // (wxGetLocalTimeMillis() - s_t0).ToDouble());
4194 RenderCharts(m_gldc, update_region);
4195 // if (g_in_inertia)
4196 // printf("--- R2b %g\n",
4197 // (wxGetLocalTimeMillis() - s_t0).ToDouble());
4198 glDisable(g_texture_rectangle_format);
4199 glUseProgram(0);
4200
4201 // Next, render the cached texture as quad to FBO(m_blit_tex) with
4202 // offsets
4203 glBindTexture(GL_TEXTURE_2D, m_cache_tex[!m_cache_page]);
4204 glEnable(GL_TEXTURE_2D);
4205
4206 // Blit the existing content onto the alternate FBO, at the correct
4207 // location
4208 float x1, x2, y1, y2;
4209
4210 if (dx > 0)
4211 x1 = dx, x2 = 0;
4212 else
4213 x1 = 0, x2 = -dx;
4214
4215 if (dy > 0)
4216 y1 = dy, y2 = 0;
4217 else
4218 y1 = 0, y2 = -dy;
4219
4220 // normalize to texture coordinates range from 0 to 1
4221 float tx1, tx2, ty1, ty2;
4222
4223 float xcor = 0;
4224 float ycor = 0;
4225
4226 tx1 = 0;
4227 tx2 = sx / (float)m_cache_tex_x;
4228 ty1 = 0;
4229 ty2 = sy / (float)m_cache_tex_y;
4230
4231 float coords[8];
4232 float uv[8];
4233
4234 // normal uv
4235 uv[0] = tx1;
4236 uv[1] = ty1;
4237 uv[2] = tx2;
4238 uv[3] = ty1;
4239 uv[4] = tx2;
4240 uv[5] = ty2;
4241 uv[6] = tx1;
4242 uv[7] = ty2;
4243
4244 coords[0] = -dx;
4245 coords[1] = dy;
4246 coords[2] = -dx + sx;
4247 coords[3] = dy;
4248 coords[4] = -dx + sx;
4249 coords[5] = dy + sy;
4250 coords[6] = -dx;
4251 coords[7] = dy + sy;
4252
4253 GLShaderProgram *shader =
4254 ptexture_2D_shader_program[GetCanvasIndex()];
4255 shader->Bind();
4256
4257 // Set up the texture sampler to texture unit 0
4258 shader->SetUniform1i("uTex", 0);
4259
4260 mat4x4 m, mvp, I;
4261 mat4x4_identity(m);
4262 mat4x4_scale_aniso(mvp, m, 2.0 / (float)sx, 2.0 / (float)sy, 1.0);
4263 mat4x4_translate_in_place(mvp, -(float)sx / 2, -(float)sy / 2, 0);
4264 shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)mvp);
4265 mat4x4_identity(I);
4266 shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)I);
4267
4268 float co1[8];
4269 co1[0] = coords[0];
4270 co1[1] = coords[1];
4271 co1[2] = coords[2];
4272 co1[3] = coords[3];
4273 co1[4] = coords[6];
4274 co1[5] = coords[7];
4275 co1[6] = coords[4];
4276 co1[7] = coords[5];
4277
4278 float tco1[8];
4279 tco1[0] = uv[0];
4280 tco1[1] = uv[1];
4281 tco1[2] = uv[2];
4282 tco1[3] = uv[3];
4283 tco1[4] = uv[6];
4284 tco1[5] = uv[7];
4285 tco1[6] = uv[4];
4286 tco1[7] = uv[5];
4287
4288 shader->SetAttributePointerf("aPos", co1);
4289 shader->SetAttributePointerf("aUV", tco1);
4290
4291 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
4292
4293 // restore the shader matrix
4294 shader->SetUniformMatrix4fv("MVMatrix",
4295 (GLfloat *)VPoint.vp_matrix_transform);
4296
4297 shader->UnBind();
4298 glBindTexture(g_texture_rectangle_format, 0);
4299
4300 glDisable(g_texture_rectangle_format);
4301 glUseProgram(0);
4302 }
4303
4304 } // accelerated pan
4305
4306 else { // must redraw the entire screen
4307 // qDebug() << "Fullpage";
4308 glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0,
4309 g_texture_rectangle_format,
4310 m_cache_tex[!m_cache_page], 0);
4311
4312 m_fbo_offsetx = 0;
4313 m_fbo_offsety = 0;
4314 m_fbo_swidth = sx;
4315 m_fbo_sheight = sy;
4316
4317 // FIXME (dave) test on Android
4318 // This can be annoying on Android pinch zoom
4319
4320 // Clear the screen to NODTA color
4321 wxColour color = GetGlobalColor(_T ( "NODTA" ));
4322 glClearColor(color.Red() / 256., color.Green() / 256.,
4323 color.Blue() / 256., 1.0);
4324 glClear(GL_COLOR_BUFFER_BIT);
4325
4326 OCPNRegion rscreen_region(VPoint.rv_rect);
4327 // if (g_in_inertia)
4328 // printf("--- R2c %g\n",
4329 // (wxGetLocalTimeMillis() - s_t0).ToDouble());
4330 RenderCharts(m_gldc, rscreen_region);
4331 // if (g_in_inertia)
4332 // printf("--- R2d %g\n",
4333 // (wxGetLocalTimeMillis() - s_t0).ToDouble());
4334
4335 m_cache_page = !m_cache_page; /* page flip */
4336
4337 } // full page render
4338
4339 // Disable Render to FBO
4340 glBindFramebuffer(GL_FRAMEBUFFER, 0);
4341
4342#endif // gles2 for accpan
4343
4344 } // newview
4345
4346 useFBO = true;
4347 }
4348
4349#ifndef __ANDROID__
4350 if (VPoint.tilt) {
4351 glMatrixMode(GL_PROJECTION);
4352 glLoadIdentity();
4353
4354 gluPerspective(2 * 180 / PI * atan2((double)gl_height, (double)gl_width),
4355 (GLfloat)gl_width / (GLfloat)gl_height, 1, gl_width);
4356
4357 glMatrixMode(GL_MODELVIEW);
4358 glLoadIdentity();
4359
4360 glScalef(1, -1, 1);
4361 glTranslatef(-gl_width / 2, -gl_height / 2, -gl_width / 2);
4362 glRotated(VPoint.tilt * 180 / PI, 1, 0, 0);
4363
4364 glGetIntegerv(GL_VIEWPORT, viewport);
4365 glGetDoublev(GL_PROJECTION_MATRIX, projmatrix);
4366 glGetDoublev(GL_MODELVIEW_MATRIX, mvmatrix);
4367 }
4368#endif
4369
4370 // if (g_in_inertia)
4371 // printf("--- R3 %g\n", (wxGetLocalTimeMillis() - s_t0).ToDouble());
4372
4373 if (useFBO) {
4374#if 0 // #ifndef USE_ANDROID_GLES2
4375 glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fb0);
4376 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
4377 glBlitFramebuffer(0, 0, sx, sy, 0, 0, sx*2, sy*2, GL_COLOR_BUFFER_BIT, GL_LINEAR);
4378
4379 glBindFramebuffer(GL_FRAMEBUFFER, 0);
4380
4381#else
4382 // Render the cached texture as quad to screen
4383 glBindTexture(g_texture_rectangle_format, m_cache_tex[m_cache_page]);
4384 glEnable(g_texture_rectangle_format);
4385
4386 float tx, ty, tx0, ty0, divx, divy;
4387
4388 // Normalize, or not?
4389 if (GL_TEXTURE_RECTANGLE_ARB == g_texture_rectangle_format) {
4390 divx = divy = 1.0f;
4391 } else {
4392 divx = m_cache_tex_x;
4393 divy = m_cache_tex_y;
4394 }
4395
4396 tx0 = m_fbo_offsetx / divx;
4397 ty0 = m_fbo_offsety / divy;
4398 tx = (m_fbo_offsetx + m_fbo_swidth) / divx;
4399 ty = (m_fbo_offsety + m_fbo_sheight) / divy;
4400
4401 float coords[8];
4402 float uv[8];
4403
4404 // normal uv
4405 uv[0] = tx0;
4406 uv[1] = ty;
4407 uv[2] = tx;
4408 uv[3] = ty;
4409 uv[4] = tx;
4410 uv[5] = ty0;
4411 uv[6] = tx0;
4412 uv[7] = ty0;
4413
4414 // pixels
4415 coords[0] = 0;
4416 coords[1] = 0;
4417 coords[2] = sx;
4418 coords[3] = 0;
4419 coords[4] = sx;
4420 coords[5] = sy;
4421 coords[6] = 0;
4422 coords[7] = sy;
4423
4424 wxColour color = GetGlobalColor(_T ( "NODTA" ));
4425 glClearColor(color.Red() / 256., color.Green() / 256., color.Blue() / 256.,
4426 1.0);
4427 glClear(GL_COLOR_BUFFER_BIT);
4428
4429 RenderTextures(gldc, coords, uv, 4, m_pParentCanvas->GetpVP());
4430#endif
4431
4432 glDisable(g_texture_rectangle_format);
4433
4434 m_cache_vp = VPoint;
4435 m_cache_vp.Validate();
4436
4437 m_cache_current_ch = m_pParentCanvas->m_singleChart;
4438
4439 if (VPoint.b_quilt) m_pParentCanvas->m_pQuilt->SetRenderedVP(VPoint);
4440
4441 } else // useFBO
4442 {
4443 RenderCharts(m_gldc, screen_region);
4444 }
4445
4446#if 1
4447 // Done with base charts.
4448 // Now the overlays
4449 RenderS57TextOverlay(VPoint);
4450 RenderMBTilesOverlay(VPoint);
4451
4452 // Render static overlay objects
4453 for (OCPNRegionIterator upd(screen_region); upd.HaveRects(); upd.NextRect()) {
4454 wxRect rt = upd.GetRect();
4455 LLRegion region = VPoint.GetLLRegion(rt);
4456 ViewPort cvp = ClippedViewport(VPoint, region);
4457 DrawGroundedOverlayObjects(gldc, cvp);
4458 }
4459
4460 if (m_pParentCanvas->m_bShowTide || m_pParentCanvas->m_bShowCurrent) {
4461 LLRegion screenLLRegion = VPoint.GetLLRegion(screen_region);
4462 LLBBox screenBox = screenLLRegion.GetBox();
4463 // Enlarge the box a bit
4464 screenBox.EnLarge(screenBox.GetLonRange() * 0.05);
4465
4466 // update the tide/current select points, if necessary
4467 if (m_pParentCanvas->m_bShowTide) {
4468 m_pParentCanvas->RebuildTideSelectList(screenBox); // full screen
4469 DrawGLTidesInBBox(gldc, VPoint.GetBBox());
4470 }
4471
4472 if (m_pParentCanvas->m_bShowCurrent) {
4473 m_pParentCanvas->RebuildCurrentSelectList(screenBox);
4474 DrawGLCurrentsInBBox(gldc, VPoint.GetBBox());
4475 }
4476 }
4477
4478 // If multi-canvas, indicate which canvas has keyboard focus
4479 // by drawing a simple blue bar at the top.
4480 if (m_pParentCanvas->m_show_focus_bar &&
4481 (g_canvasConfig != 0)) { // multi-canvas?
4482 if (m_pParentCanvas == wxWindow::FindFocus()) {
4483 g_focusCanvas = m_pParentCanvas;
4484
4485 wxColour colour = GetGlobalColor(_T("BLUE4"));
4486 wxPen ppBlue(colour, 1);
4487 wxBrush ppBrush(colour);
4488 gldc.SetPen(ppBlue);
4489 gldc.SetBrush(ppBrush);
4490 int xw = m_pParentCanvas->GetClientSize().x * m_displayScale;
4491 float rect_pix = m_pParentCanvas->m_focus_indicator_pix * m_displayScale;
4492 wxPoint barPoints[4];
4493 barPoints[0].x = 0;
4494 barPoints[0].y = 0;
4495 barPoints[1].x = xw;
4496 barPoints[1].y = 0;
4497 barPoints[2].x = xw;
4498 barPoints[2].y = rect_pix;
4499 barPoints[3].x = 0;
4500 barPoints[3].y = rect_pix;
4501
4502 gldc.DrawPolygon(4, barPoints, 0, 0, 1, 0);
4503 }
4504 }
4505
4506 DrawDynamicRoutesTracksAndWaypoints(VPoint);
4507
4508 // Now draw all the objects which normally move around and are not
4509 // cached from the previous frame
4510 DrawFloatingOverlayObjects(m_gldc);
4511
4512#ifndef USE_ANDROID_GLES2
4513 // from this point on don't use perspective
4514 if (VPoint.tilt) {
4515 glMatrixMode(GL_PROJECTION);
4516 glLoadIdentity();
4517
4518 glOrtho(0, (GLint)gl_width, (GLint)gl_height, 0, -1, 1);
4519 glMatrixMode(GL_MODELVIEW);
4520 glLoadIdentity();
4521 }
4522#endif
4523
4524 DrawEmboss(m_gldc, m_pParentCanvas->EmbossDepthScale());
4525 DrawEmboss(m_gldc, m_pParentCanvas->EmbossOverzoomIndicator(gldc));
4526
4527 if (g_pi_manager) {
4528 ViewPort &vp = m_pParentCanvas->GetVP();
4529 g_pi_manager->SendViewPortToRequestingPlugIns(vp);
4530 g_pi_manager->RenderAllGLCanvasOverlayPlugIns(
4531 m_pcontext, vp, m_pParentCanvas->m_canvasIndex, OVERLAY_OVER_EMBOSS);
4532 }
4533 if (!g_PrintingInProgress) {
4534 if (m_pParentCanvas->m_pTrackRolloverWin)
4535 m_pParentCanvas->m_pTrackRolloverWin->Draw(gldc);
4536
4537 if (m_pParentCanvas->m_pRouteRolloverWin)
4538 m_pParentCanvas->m_pRouteRolloverWin->Draw(gldc);
4539
4540 if (m_pParentCanvas->m_pAISRolloverWin)
4541 m_pParentCanvas->m_pAISRolloverWin->Draw(gldc);
4542
4543 if (m_pParentCanvas->GetMUIBar())
4544 m_pParentCanvas->GetMUIBar()->DrawGL(gldc, m_displayScale);
4545
4546 if (g_MainToolbar && m_pParentCanvas->IsPrimaryCanvas())
4547 g_MainToolbar->DrawGL(gldc, m_displayScale);
4548
4549 if (g_iENCToolbar && m_pParentCanvas->IsPrimaryCanvas())
4550 g_iENCToolbar->DrawGL(gldc, m_displayScale);
4551 }
4552
4553 // On some platforms, the opengl context window is always on top of any
4554 // standard DC windows, so we need to draw the Chart Info Window
4555 // as overlayed bmps.
4556
4557#ifdef __WXOSX__
4558 if (m_pParentCanvas->m_pCIWin && m_pParentCanvas->m_pCIWin->IsShown()) {
4559 int x, y, width, height;
4560 m_pParentCanvas->m_pCIWin->GetClientSize(&width, &height);
4561 m_pParentCanvas->m_pCIWin->GetPosition(&x, &y);
4562 wxBitmap bmp(width, height, -1);
4563 wxMemoryDC dc(bmp);
4564 if (bmp.IsOk()) {
4565 dc.SetBackground(wxBrush(GetGlobalColor(_T ( "UIBCK" ))));
4566 dc.Clear();
4567
4568 dc.SetTextBackground(GetGlobalColor(_T ( "UIBCK" )));
4569 dc.SetTextForeground(GetGlobalColor(_T ( "UITX1" )));
4570
4571 int yt = 0;
4572 int xt = 0;
4573 wxString s = m_pParentCanvas->m_pCIWin->GetString();
4574 int h = m_pParentCanvas->m_pCIWin->GetCharHeight();
4575
4576 wxStringTokenizer tkz(s, _T("\n"));
4577 wxString token;
4578
4579 while (tkz.HasMoreTokens()) {
4580 token = tkz.GetNextToken();
4581 dc.DrawText(token, xt, yt);
4582 yt += h;
4583 }
4584 dc.SelectObject(wxNullBitmap);
4585
4586 m_gldc.DrawBitmap(bmp, x, y, false);
4587 }
4588 }
4589
4590#endif
4591 // render the chart bar
4592 if (g_bShowChartBar) DrawChartBar(m_gldc);
4593
4594 if (m_pParentCanvas->m_Compass && m_pParentCanvas->m_bShowCompassWin &&
4595 g_bShowCompassWin)
4596 m_pParentCanvas->m_Compass->Paint(gldc);
4597
4598 if (m_pParentCanvas->IsPrimaryCanvas()) {
4599 auto &noteman = NotificationManager::GetInstance();
4600 if (noteman.GetNotificationCount()) {
4601 m_pParentCanvas->m_notification_button->SetIconSeverity(
4602 noteman.GetMaxSeverity());
4603 if (m_pParentCanvas->m_notification_button->UpdateStatus()) Refresh();
4604 m_pParentCanvas->m_notification_button->Show(true);
4605 m_pParentCanvas->m_notification_button->Paint(gldc);
4606 } else {
4607 m_pParentCanvas->m_notification_button->Show(false);
4608 }
4609 }
4610 RenderGLAlertMessage();
4611#endif
4612
4613 if (g_pi_manager) {
4614 ViewPort &vp = m_pParentCanvas->GetVP();
4615 g_pi_manager->SendViewPortToRequestingPlugIns(vp);
4616 g_pi_manager->RenderAllGLCanvasOverlayPlugIns(
4617 m_pcontext, vp, m_pParentCanvas->m_canvasIndex, OVERLAY_OVER_UI);
4618 glActiveTexture(GL_TEXTURE0);
4619 }
4620
4621 // quiting?
4622 if (g_bquiting) DrawQuiting();
4623 if (g_bcompression_wait)
4624 DrawCloseMessage(_("Waiting for raster chart compression thread exit."));
4625
4626 // Some older MSW OpenGL drivers are generally very unstable.
4627 // This helps...
4628
4629 if (g_b_needFinish) glFinish();
4630
4631 SwapBuffers();
4632
4633 g_glTextureManager->TextureCrunch(0.8);
4634 g_glTextureManager->FactoryCrunch(0.6);
4635
4636 // if (g_in_inertia)
4637 // printf("--- Rx %g\n", (wxGetLocalTimeMillis() - s_t0).ToDouble());
4638
4639 m_pParentCanvas->PaintCleanup();
4640 // OCPNPlatform::HideBusySpinner();
4641 m_bforcefull = false;
4642
4643 n_render++;
4644}
4645
4646void glChartCanvas::RenderS57TextOverlay(ViewPort &VPoint) {
4647 // Render the decluttered Text overlay for quilted vector charts, except for
4648 // CM93 Composite
4649 if (VPoint.b_quilt) {
4650 if (m_pParentCanvas->m_pQuilt->IsQuiltVector() && ps52plib &&
4651 ps52plib->GetShowS57Text()) {
4652 ChartBase *chart = m_pParentCanvas->m_pQuilt->GetRefChart();
4653 if (chart && (chart->GetChartType() != CHART_TYPE_CM93COMP)) {
4654 // Clear the text Global declutter list
4655 if (chart) {
4656 ChartPlugInWrapper *ChPI = dynamic_cast<ChartPlugInWrapper *>(chart);
4657 if (ChPI)
4658 ChPI->ClearPLIBTextList();
4659 else
4660 ps52plib->ClearTextList();
4661 }
4662
4663 // Grow the ViewPort a bit laterally, to minimize "jumping" of text
4664 // elements at right side of screen
4665 ViewPort vpx = VPoint;
4666 vpx.BuildExpandedVP(VPoint.pix_width * 12 / 10, VPoint.pix_height);
4667
4668 OCPNRegion screen_region(
4669 wxRect(0, 0, VPoint.pix_width, VPoint.pix_height));
4670 RenderQuiltViewGLText(vpx, screen_region);
4671 }
4672 }
4673 }
4674}
4675void glChartCanvas::RenderSingleMBTileOverlay(const int dbIndex, bool bOverlay,
4676 ViewPort &vp,
4677 OCPNRegion &screen_region,
4678 LLRegion &screenLLRegion) {
4679 ChartBase *chart = ChartData->OpenChartFromDBAndLock(dbIndex, FULL_INIT);
4680
4681 // Chart may have been prevented from initial loading due to size, or some
4682 // other reason...
4683 if (chart == NULL) return;
4684
4685 ChartMbTiles *pcmbt = dynamic_cast<ChartMbTiles *>(chart);
4686 if (!pcmbt) return;
4687
4688 // Is tile an OVERLAY type?
4689 // Render, or not, depending on passed flag.
4690 if (bOverlay && pcmbt->GetTileType() != MbTilesType::OVERLAY) return;
4691
4692 wxFileName tileFile(chart->GetFullPath());
4693 // Size test for 5 GByte
4694 wxULongLong tileSizeMB = tileFile.GetSize() >> 20;
4695
4696 if (!ChartData->CheckAnyCanvasExclusiveTileGroup() ||
4697 (tileSizeMB.GetLo() > 5000)) {
4698 // Check to see if the tile has been "clicked".
4699 // If so, do not add to no-show array again.
4700 if (!m_pParentCanvas->IsTileOverlayIndexInYesShow(dbIndex)) {
4701 if (!m_pParentCanvas->IsTileOverlayIndexInNoShow(dbIndex)) {
4702 m_pParentCanvas->m_tile_noshow_index_array.push_back(dbIndex);
4703 }
4704 }
4705 }
4706
4707 // This test catches the case where the chart is added to no_show list
4708 // when first loaded by OpenChartFromDBAndLock
4709 if (m_pParentCanvas->IsTileOverlayIndexInNoShow(dbIndex)) {
4710 return;
4711 }
4712
4713 pcmbt->RenderRegionViewOnGL(*m_pcontext, vp, screen_region, screenLLRegion);
4714
4715 // Light up the piano key if the chart was rendered
4716 std::vector<int> piano_active_array_tiles =
4717 m_pParentCanvas->m_Piano->GetActiveKeyArray();
4718 bool bfound = false;
4719
4720 if (std::find(piano_active_array_tiles.begin(),
4721 piano_active_array_tiles.end(),
4722 dbIndex) != piano_active_array_tiles.end()) {
4723 bfound = true;
4724 }
4725
4726 if (!bfound) {
4727 piano_active_array_tiles.push_back(dbIndex);
4728 m_pParentCanvas->m_Piano->SetActiveKeyArray(piano_active_array_tiles);
4729 }
4730}
4731
4732void glChartCanvas::RenderMBTilesOverlay(ViewPort &VPoint) {
4733 // Render MBTiles as overlay
4734 std::vector<int> stackIndexArray =
4735 m_pParentCanvas->m_pQuilt->GetExtendedStackIndexArray();
4736 unsigned int im = stackIndexArray.size();
4737 // XXX should
4738 // assert(!VPoint.b_quilt && im == 0)
4739 if (VPoint.b_quilt && im > 0) {
4740 bool regionVPBuilt = false;
4741 OCPNRegion screen_region;
4742 LLRegion screenLLRegion;
4743 LLBBox screenBox;
4744 ViewPort vp;
4745
4746 std::vector<int> tiles_to_show;
4747 for (unsigned int is = 0; is < im; is++) {
4748 const ChartTableEntry &cte =
4749 ChartData->GetChartTableEntry(stackIndexArray[is]);
4750 if (cte.GetChartType() == CHART_TYPE_MBTILES) {
4751 if (m_pParentCanvas->IsTileOverlayIndexInNoShow(stackIndexArray[is])) {
4752 // Turn off the piano highlite
4753 std::vector<int> piano_active_array_tiles =
4754 m_pParentCanvas->m_Piano->GetActiveKeyArray();
4755 bool bfound = false;
4756
4757 for (unsigned int i = 0; i < piano_active_array_tiles.size(); i++) {
4758 if (piano_active_array_tiles[i] == stackIndexArray[is]) {
4759 piano_active_array_tiles.erase(piano_active_array_tiles.begin() +
4760 i); // erase it
4761 bfound = true;
4762 break;
4763 }
4764 }
4765
4766 if (bfound)
4767 m_pParentCanvas->m_Piano->SetActiveKeyArray(
4768 piano_active_array_tiles);
4769
4770 continue;
4771 }
4772
4773 tiles_to_show.push_back(stackIndexArray[is]);
4774 if (!regionVPBuilt) {
4775 screen_region =
4776 OCPNRegion(wxRect(0, 0, VPoint.pix_width, VPoint.pix_height));
4777 screenLLRegion = VPoint.GetLLRegion(screen_region);
4778 screenBox = screenLLRegion.GetBox();
4779
4780 vp = VPoint;
4781 wxPoint p;
4782 p.x = VPoint.pix_width / 2;
4783 p.y = VPoint.pix_height / 2;
4784 VPoint.GetLLFromPix(p, &vp.clat, &vp.clon);
4785
4786 regionVPBuilt = true;
4787 }
4788 }
4789 }
4790
4791 // Render in two passes, to render the OVERLAY types last
4792
4793 // Show the tilesets in reverse order to have the largest scale
4794 // on top
4795
4796 for (std::vector<int>::reverse_iterator rit = tiles_to_show.rbegin();
4797 rit != tiles_to_show.rend(); ++rit) {
4798 RenderSingleMBTileOverlay(*rit, FALSE, vp, screen_region, screenLLRegion);
4799 }
4800 for (std::vector<int>::reverse_iterator rit = tiles_to_show.rbegin();
4801 rit != tiles_to_show.rend(); ++rit) {
4802 RenderSingleMBTileOverlay(*rit, TRUE, vp, screen_region, screenLLRegion);
4803 }
4804
4805 // Render the HiLite on piano rollover of mbTile key
4806 LLRegion hiregion = m_pParentCanvas->m_pQuilt->GetHiliteRegion();
4807
4808 if (!hiregion.Empty()) {
4809 glEnable(GL_BLEND);
4810
4811 double hitrans;
4812 switch (global_color_scheme) {
4813 case GLOBAL_COLOR_SCHEME_DAY:
4814 hitrans = .4;
4815 break;
4816 case GLOBAL_COLOR_SCHEME_DUSK:
4817 hitrans = .2;
4818 break;
4819 case GLOBAL_COLOR_SCHEME_NIGHT:
4820 hitrans = .1;
4821 break;
4822 default:
4823 hitrans = .4;
4824 break;
4825 }
4826
4827#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
4828 glColor4f((float).8, (float).4, (float).4, (float)hitrans);
4829#else
4830 s_regionColor = wxColor(204, 102, 102, hitrans * 256);
4831#endif
4832
4833 DrawRegion(VPoint, hiregion);
4834
4835 glDisable(GL_BLEND);
4836 }
4837 }
4838}
4839
4840#if 0
4841void glChartCanvas::RenderCanvasBackingChart(ocpnDC &dc,
4842 OCPNRegion valid_region) {
4843 // Fill the FBO with the current gshhs world chart
4844 int w, h;
4845 GetClientSize(&w, &h);
4846
4847 glViewport(0, 0, (GLint)m_cache_tex_x, (GLint)m_cache_tex_y);
4848#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
4849 glMatrixMode(GL_PROJECTION);
4850 glLoadIdentity();
4851
4852 glOrtho(0, m_cache_tex_x, m_cache_tex_y, 0, -1, 1);
4853 glMatrixMode(GL_MODELVIEW);
4854 glLoadIdentity();
4855#endif
4856
4857 wxRect rtex(0, 0, m_cache_tex_x, m_cache_tex_y);
4858 ViewPort cvp =
4859 m_pParentCanvas->GetVP().BuildExpandedVP(m_cache_tex_x, m_cache_tex_y);
4860
4861 bool world_view = false;
4862 RenderWorldChart(dc, cvp, rtex, world_view);
4863 gShapeBasemap.RenderViewOnDC(dc, cvp);
4864
4865 // dc.SetPen(wxPen(wxColour(254,254,0), 3));
4866 // dc.DrawLine( 0, 0, m_cache_tex_x, m_cache_tex_y);
4867
4868 // Reset matrices
4869 glViewport(0, 0, (GLint)w, (GLint)h);
4870#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
4871 glMatrixMode(GL_PROJECTION);
4872 glLoadIdentity();
4873
4874 glOrtho(0, (GLint)w, (GLint)h, 0, -1, 1);
4875 glMatrixMode(GL_MODELVIEW);
4876 glLoadIdentity();
4877#endif
4878}
4879#endif
4880
4881void glChartCanvas::FastPan(int dx, int dy) {
4882#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
4883#endif
4884}
4885
4886void glChartCanvas::ZoomProject(float offset_x, float offset_y, float swidth,
4887 float sheight) {
4888 SetCurrent(*m_pcontext);
4889 float sx = GetSize().x;
4890 float sy = GetSize().y;
4891 glClear(GL_COLOR_BUFFER_BIT);
4892
4893 int w, h;
4894 GetClientSize(&w, &h);
4895
4896 if (s_b_useStencil) {
4897 glEnable(GL_STENCIL_TEST);
4898 glStencilMask(0xff);
4899 glClear(GL_STENCIL_BUFFER_BIT);
4900 glDisable(GL_STENCIL_TEST);
4901 }
4902
4903#ifndef __ANDROID__
4904 // Render backing texture
4905 if (1) {
4906 float tx0 = 0;
4907 float ty0 = 0;
4908 float tx = sx;
4909 float ty = sy;
4910
4911 float vx0 = 0;
4912 float vy0 = 0;
4913 float vx = sx;
4914 float vy = sy;
4915
4916 float sxfactor = sx / swidth;
4917 float syfactor = sy / sheight;
4918
4919 glViewport(-offset_x * sx / swidth - (sx * sxfactor / 2),
4920 -offset_y * (sy / sheight) - (sy * syfactor / 2),
4921 sx * sx / swidth * 2, sy * sy / sheight * 2);
4922 glBindTexture(g_texture_rectangle_format, m_TouchBackingTexture);
4923 glEnable(g_texture_rectangle_format);
4924
4925 float uv[8];
4926 float coords[8];
4927
4928 // normal uv Texture coordinates
4929 uv[0] = 0;
4930 uv[1] = 0;
4931 uv[2] = 1;
4932 uv[3] = 0;
4933 uv[4] = 1;
4934 uv[5] = 1;
4935 uv[6] = 0;
4936 uv[7] = 1;
4937
4938 // pixels
4939 coords[0] = vx0;
4940 coords[1] = vy0;
4941 coords[2] = vx;
4942 coords[3] = vy0;
4943 coords[4] = vx;
4944 coords[5] = vy;
4945 coords[6] = vx0;
4946 coords[7] = vy;
4947
4948 RenderTextures(m_gldc, coords, uv, 4, &m_texVP);
4949 glBindTexture(g_texture_rectangle_format, 0);
4950 }
4951#endif
4952
4953 // render zoomed canvas section
4954 if (1) {
4955 float tx, ty, tx0, ty0;
4956 tx = sx, ty = sy;
4957
4958 tx0 = ty0 = 0.;
4959
4960 float vx0 = 0;
4961 float vy0 = 0;
4962 float vy = sy;
4963 float vx = sx;
4964
4965 glBindTexture(g_texture_rectangle_format, 0);
4966
4967 // Render the cached texture as quad to screen
4968 glBindTexture(g_texture_rectangle_format, m_cache_tex[m_cache_page]);
4969 glEnable(g_texture_rectangle_format);
4970
4971 float uv[8];
4972 float coords[8];
4973
4974 // normal uv Texture coordinates
4975 uv[0] = tx0 / m_cache_tex_x;
4976 uv[1] = ty / m_cache_tex_y;
4977 uv[2] = tx / m_cache_tex_x;
4978 uv[3] = ty / m_cache_tex_y;
4979 uv[4] = tx / m_cache_tex_x;
4980 uv[5] = ty0 / m_cache_tex_y;
4981 uv[6] = tx0 / m_cache_tex_x;
4982 uv[7] = ty0 / m_cache_tex_y;
4983
4984 // pixels
4985 coords[0] = vx0;
4986 coords[1] = vy0;
4987 coords[2] = vx;
4988 coords[3] = vy0;
4989 coords[4] = vx;
4990 coords[5] = vy;
4991 coords[6] = vx0;
4992 coords[7] = vy;
4993
4994 glViewport(-offset_x * sx / swidth, -offset_y * (sy / sheight),
4995 sx * sx / swidth, sy * sy / sheight);
4996
4997 RenderTextures(m_gldc, coords, uv, 4, m_pParentCanvas->GetpVP());
4998
4999 glDisable(g_texture_rectangle_format);
5000 glBindTexture(g_texture_rectangle_format, 0);
5001 }
5002
5003#if 0
5004 // For fun, we prove the coordinates of the blank area outside the chart when
5005 // zooming out. Bottom stripe
5006 // wxColour color = GetGlobalColor(_T("YELO1")); //GREY1
5007 wxColour color = GetGlobalColor(_T("GREY1")); //
5008 float ht = -offset_y * (sy / sheight);
5009 wxRect r(0, sy - ht, w, ht);
5010 RenderColorRect(r, color);
5011
5012 // top stripe
5013 wxRect rt(0, 0, w, sy - (ht + (sy * sy / sheight)));
5014 RenderColorRect(rt, color);
5015
5016 // left
5017 float w1 = -offset_x * sx / swidth;
5018 wxRect rl(0, 0, w1, sy);
5019 RenderColorRect(rl, color);
5020
5021 // right
5022 float px = w1 + sx * sx / swidth;
5023 wxRect rr(px, 0, sx - px, sy);
5024 RenderColorRect(rr, color);
5025#endif
5026 // When zooming out, if we go too far, then the frame buffer is repeated
5027 // on-screen due to address wrapping in the frame buffer. Detect this case,
5028 // and render some simple solid covering quads to avoid a confusing display.
5029
5030 SwapBuffers();
5031}
5032
5033void glChartCanvas::onZoomTimerEvent(wxTimerEvent &event) {
5034 // If m_zoomFinal is set, stop the timer.
5035 if (!m_zoomFinal) {
5036 if (m_nRun < m_nTotal) {
5037 m_runoffsetx += m_offsetxStep;
5038 if (m_offsetxStep > 0)
5039 m_runoffsetx = wxMin(m_runoffsetx, m_fbo_offsetx);
5040 else
5041 m_runoffsetx = wxMax(m_runoffsetx, m_fbo_offsetx);
5042
5043 m_runoffsety += m_offsetyStep;
5044 if (m_offsetyStep > 0)
5045 m_runoffsety = wxMin(m_runoffsety, m_fbo_offsety);
5046 else
5047 m_runoffsety = wxMax(m_runoffsety, m_fbo_offsety);
5048
5049 m_runswidth += m_swidthStep;
5050 if (m_swidthStep > 0)
5051 m_runswidth = wxMin(m_runswidth, m_fbo_swidth);
5052 else
5053 m_runswidth = wxMax(m_runswidth, m_fbo_swidth);
5054
5055 m_runsheight += m_sheightStep;
5056 if (m_sheightStep > 0)
5057 m_runsheight = wxMin(m_runsheight, m_fbo_sheight);
5058 else
5059 m_runsheight = wxMax(m_runsheight, m_fbo_sheight);
5060
5061 m_nRun += m_nStep;
5062 }
5063
5064 ZoomProject(m_runoffsetx, m_runoffsety, m_runswidth, m_runsheight);
5065
5066 } else {
5067 zoomTimer.Stop();
5068 if (m_zoomFinal) {
5069 m_pParentCanvas->ZoomCanvasSimple(m_zoomFinalZoom);
5070 if (m_zoomFinaldx || m_zoomFinaldy) {
5071 m_pParentCanvas->PanCanvas(m_zoomFinaldx, m_zoomFinaldy);
5072 }
5073 }
5074 m_zoomFinal = false;
5075 }
5076}
5077
5078void glChartCanvas::FastZoom(float factor, float cp_x, float cp_y, float post_x,
5079 float post_y) {
5080 int sx = GetSize().x;
5081 int sy = GetSize().y;
5082
5083 m_lastfbo_offsetx = m_fbo_offsetx;
5084 m_lastfbo_offsety = m_fbo_offsety;
5085 m_lastfbo_swidth = m_fbo_swidth;
5086 m_lastfbo_sheight = m_fbo_sheight;
5087
5088 float curr_fbo_offset_x = m_fbo_offsetx;
5089 float curr_fbo_offset_y = m_fbo_offsety;
5090 float curr_fbo_swidth = m_fbo_swidth;
5091 float curr_fbo_sheight = m_fbo_sheight;
5092
5093 float fx = (float)cp_x / sx;
5094 float fy = 1.0 - (float)cp_y / sy;
5095
5096 float fbo_ctr_x = curr_fbo_offset_x + (curr_fbo_swidth * fx);
5097 float fbo_ctr_y = curr_fbo_offset_y + (curr_fbo_sheight * fy);
5098
5099 m_fbo_swidth = curr_fbo_swidth / factor;
5100 m_fbo_sheight = curr_fbo_sheight / factor;
5101
5102 m_fbo_offsetx = fbo_ctr_x - (m_fbo_swidth * fx);
5103 m_fbo_offsety = fbo_ctr_y - (m_fbo_sheight * fy);
5104
5105 m_fbo_offsetx += post_x;
5106 m_fbo_offsety += post_y;
5107
5108 {
5109 m_nStep = 20;
5110 m_nTotal = 100;
5111
5112 // m_nStep = 10; // Android?
5113 // m_nTotal = 40;
5114
5115 m_nRun = 0;
5116
5117 float perStep = m_nStep / m_nTotal;
5118
5119 if (zoomTimer.IsRunning()) {
5120 m_offsetxStep = (m_fbo_offsetx - m_runoffsetx) * perStep;
5121 m_offsetyStep = (m_fbo_offsety - m_runoffsety) * perStep;
5122 m_swidthStep = (m_fbo_swidth - m_runswidth) * perStep;
5123 m_sheightStep = (m_fbo_sheight - m_runsheight) * perStep;
5124
5125 } else {
5126 m_offsetxStep = (m_fbo_offsetx - m_lastfbo_offsetx) * perStep;
5127 m_offsetyStep = (m_fbo_offsety - m_lastfbo_offsety) * perStep;
5128 m_swidthStep = (m_fbo_swidth - m_lastfbo_swidth) * perStep;
5129 m_sheightStep = (m_fbo_sheight - m_lastfbo_sheight) * perStep;
5130
5131 m_runoffsetx = m_lastfbo_offsetx;
5132 m_runoffsety = m_lastfbo_offsety;
5133 m_runswidth = m_lastfbo_swidth;
5134 m_runsheight = m_lastfbo_sheight;
5135 }
5136
5137 if (!zoomTimer.IsRunning()) zoomTimer.Start(m_nStep);
5138 m_zoomFinal = false;
5139 }
5140}
5141
5142#ifdef __ANDROID__
5143
5144void glChartCanvas::OnEvtPanGesture(wxQT_PanGestureEvent &event) {
5145 // qDebug() << "OnEvtPanGesture" << m_pParentCanvas->m_canvasIndex <<
5146 // event.cursor_pos.x;
5147
5148 if (m_pParentCanvas->isRouteEditing() || m_pParentCanvas->isMarkEditing())
5149 return;
5150
5151 if (m_binPinch) return;
5152 if (m_bpinchGuard) return;
5153
5154 int x = event.GetOffset().x;
5155 int y = event.GetOffset().y;
5156
5157 int lx = event.GetLastOffset().x;
5158 int ly = event.GetLastOffset().y;
5159
5160 int dx = lx - x;
5161 int dy = y - ly;
5162
5163 switch (event.GetState()) {
5164 case GestureStarted:
5165 if (m_binPan) break;
5166
5167 panx = pany = 0;
5168 m_binPan = true;
5169 m_binGesture = true;
5170 // qDebug() << "pg1";
5171 break;
5172
5173 case GestureUpdated:
5174 if (m_binPan) {
5175 if (!g_GLOptions.m_bUseCanvasPanning) {
5176 // qDebug() << "slowpan" << dx << dy;
5177
5178 m_pParentCanvas->FreezePiano();
5179 m_pParentCanvas->PanCanvas(dx, -dy);
5180 m_pParentCanvas->ThawPiano();
5181
5182 } else {
5183 FastPan(dx, dy);
5184 }
5185
5186 panx -= dx;
5187 pany -= dy;
5188 }
5189 break;
5190
5191 case GestureFinished:
5192 // qDebug() << "panGestureFinished";
5193
5194 m_pParentCanvas->UpdateCanvasControlBar();
5195
5196 m_binPan = false;
5197 m_gestureFinishTimer.Start(500, wxTIMER_ONE_SHOT);
5198
5199 break;
5200
5201 case GestureCanceled:
5202 m_binPan = false;
5203 m_gestureFinishTimer.Start(500, wxTIMER_ONE_SHOT);
5204 break;
5205
5206 default:
5207 break;
5208 }
5209
5210 m_bgestureGuard = true;
5211 m_gestureEeventTimer.Start(500, wxTIMER_ONE_SHOT);
5212 m_bforcefull = false;
5213
5214 // qDebug() << "panGestureDone";
5215}
5216
5217float zoom_inc = 1.0;
5218bool first_zout = false;
5219
5220void glChartCanvas::OnEvtPinchGesture(wxQT_PinchGestureEvent &event) {
5221 float zoom_gain = 1.0;
5222 float zout_gain = 1.0;
5223
5224 float zoom_val;
5225 float total_zoom_val;
5226
5227 float max_zoom_scale = 1000.;
5228 float min_zoom_scale = 2e8;
5229
5230 if (event.GetScaleFactor() > 1)
5231 zoom_val = ((event.GetScaleFactor() - 1.0) * zoom_gain) + 1.0;
5232 else
5233 zoom_val = 1.0 - ((1.0 - event.GetScaleFactor()) * zout_gain);
5234
5235 if (event.GetTotalScaleFactor() > 1)
5236 total_zoom_val = ((event.GetTotalScaleFactor() - 1.0) * zoom_gain) + 1.0;
5237 else
5238#if 0
5239 total_zoom_val = 1.0 - ((1.0 - event.GetTotalScaleFactor()) * zout_gain);
5240
5241 double projected_scale = cc1->GetVP().chart_scale / total_zoom_val;
5242
5243 // Max zoom in is set by scale of quilt reference chart, consistent with chart render limits set elsewhere.
5244 float max_zoom_scale = 1000.;
5245 if( cc1->GetVP().b_quilt) {
5246 int ref_index = cc1->GetQuiltRefChartdbIndex();
5247// if((ref_index >= 0) && ChartData){
5248// max_zoom_scale = ChartData->GetDBChartScale(ref_index) / 8.0;
5249// }
5250 }
5251
5252
5253 float min_zoom_scale = 2e8;
5254
5255#endif
5256
5257 total_zoom_val = 1.0 - ((1.0 - event.GetTotalScaleFactor()) * zoom_gain);
5258
5259 double projected_scale =
5260 m_pParentCanvas->GetVP().chart_scale / total_zoom_val;
5261
5262 switch (event.GetState()) {
5263 case GestureStarted:
5264 first_zout = false;
5265 m_binPinch = true;
5266 m_binPan = false; // cancel any tentative pan gesture, in case the "pan
5267 // cancel" event was lost
5268 m_binGesture = true;
5269 // qDebug() << "pg2";
5270 m_pinchStart = event.GetCenterPoint();
5271 m_lpinchPoint = m_pinchStart;
5272
5273 m_pParentCanvas->GetCanvasPixPoint(event.GetCenterPoint().x,
5274 event.GetCenterPoint().y, m_pinchlat,
5275 m_pinchlon);
5276 // qDebug() << "center" << event.GetCenterPoint().x <<
5277 // event.GetCenterPoint().y;
5278
5279 m_cc_x = m_fbo_offsetx + (m_fbo_swidth / 2);
5280 m_cc_y = m_fbo_offsety + (m_fbo_sheight / 2);
5281
5282 // Render the full charts with overlay objects onto the frame buffer.
5283 SetCurrent(*m_pcontext);
5284 RenderScene();
5285
5286 zoom_inc = 1.0;
5287 break;
5288
5289 case GestureUpdated:
5290 if (g_GLOptions.m_bUseCanvasPanning) {
5291 if (projected_scale < min_zoom_scale) {
5292 wxPoint pinchPoint = event.GetCenterPoint();
5293
5294 float dx = pinchPoint.x - m_lpinchPoint.x;
5295 float dy = pinchPoint.y - m_lpinchPoint.y;
5296
5297 FastZoom(zoom_val, m_pinchStart.x, m_pinchStart.y,
5298 -dx / total_zoom_val, dy / total_zoom_val);
5299
5300 m_lpinchPoint = pinchPoint;
5301 }
5302 } else {
5303 // qDebug() << "update totalzoom" << total_zoom_val << projected_scale;
5304 if (1 || ((total_zoom_val > 1) && !first_zout)) { // Zoom in
5305 wxPoint pinchPoint = event.GetCenterPoint();
5306
5307 float dx = pinchPoint.x - m_lpinchPoint.x;
5308 float dy = pinchPoint.y - m_lpinchPoint.y;
5309
5310 if ((projected_scale > max_zoom_scale) &&
5311 (projected_scale < min_zoom_scale))
5312 FastZoom(zoom_val, m_pinchStart.x, m_pinchStart.y,
5313 -dx / total_zoom_val, dy / total_zoom_val);
5314
5315 m_lpinchPoint = pinchPoint;
5316
5317 } else {
5318 first_zout = true;
5319 zoom_inc *= zoom_val;
5320 if ((zoom_inc < 0.9) || (zoom_inc > 1.1)) {
5321 m_pParentCanvas->ZoomCanvas(zoom_inc, false);
5322 zoom_inc = 1.0;
5323 }
5324
5325 wxPoint pinchPoint = event.GetCenterPoint();
5326 float dx = pinchPoint.x - m_lpinchPoint.x;
5327 float dy = pinchPoint.y - m_lpinchPoint.y;
5328 m_pParentCanvas->PanCanvas(-dx, -dy);
5329 m_lpinchPoint = pinchPoint;
5330
5331 // SetCurrent(*m_pcontext);
5332 // RenderScene();
5333 // g_Piano->DrawGL(cc1->m_canvas_height -
5334 // g_Piano->GetHeight()); SwapBuffers();
5335 }
5336 }
5337
5338 break;
5339
5340 case GestureFinished: {
5341 // qDebug() << "finish totalzoom" << total_zoom_val <<
5342 // projected_scale;
5343
5344 float cc_x = m_fbo_offsetx + (m_fbo_swidth / 2);
5345 float cc_y = m_fbo_offsety + (m_fbo_sheight / 2);
5346 float dy = 0;
5347 float dx = 0;
5348
5349 float tzoom = total_zoom_val;
5350
5351 if (projected_scale >= min_zoom_scale)
5352 tzoom = m_pParentCanvas->GetVP().chart_scale / min_zoom_scale;
5353
5354 if (projected_scale < max_zoom_scale)
5355 tzoom = m_pParentCanvas->GetVP().chart_scale / max_zoom_scale;
5356
5357 dx = (cc_x - m_cc_x) * tzoom;
5358 dy = -(cc_y - m_cc_y) * tzoom;
5359
5360 if (zoomTimer.IsRunning()) {
5361 // qDebug() << "Final zoom";
5362 m_zoomFinal = true;
5363 m_zoomFinalZoom = tzoom;
5364 m_zoomFinaldx = dx;
5365 m_zoomFinaldy = dy;
5366 }
5367
5368 else {
5369 double final_projected_scale =
5370 m_pParentCanvas->GetVP().chart_scale / tzoom;
5371 // qDebug() << "Final pinchB" << tzoom << final_projected_scale;
5372
5373 if (final_projected_scale < min_zoom_scale) {
5374 // qDebug() << "zoomit";
5375 m_pParentCanvas->ZoomCanvas(tzoom, false);
5376 m_pParentCanvas->PanCanvas(dx, dy);
5377 m_pParentCanvas->m_pQuilt->Invalidate();
5378 m_bforcefull = true;
5379
5380 } else {
5381 double new_scale =
5382 m_pParentCanvas->GetCanvasScaleFactor() / min_zoom_scale;
5383 // qDebug() << "clampit";
5384 m_pParentCanvas->SetVPScale(new_scale);
5385 m_pParentCanvas->m_pQuilt->Invalidate();
5386 m_bforcefull = true;
5387 }
5388 }
5389
5390 // if( projected_scale < 3e8)
5391 // m_pParentCanvas->ZoomCanvas( total_zoom_val, false
5392 // );
5393 // else
5394 // m_pParentCanvas->ZoomCanvas(m_pParentCanvas->GetVP().chart_scale
5395 // / 3e8, false);
5396
5397 m_binPinch = false;
5398 m_gestureFinishTimer.Start(500, wxTIMER_ONE_SHOT);
5399 break;
5400 }
5401
5402 case GestureCanceled:
5403 m_binPinch = false;
5404 m_gestureFinishTimer.Start(500, wxTIMER_ONE_SHOT);
5405 break;
5406
5407 default:
5408 break;
5409 }
5410
5411 m_bgestureGuard = true;
5412 // m_bpinchGuard = true;
5413 m_gestureEeventTimer.Start(500, wxTIMER_ONE_SHOT);
5414}
5415
5416void glChartCanvas::onGestureTimerEvent(wxTimerEvent &event) {
5417 // On some devices, the pan GestureFinished event fails to show up
5418 // Watch for this case, and fix it.....
5419 // qDebug() << "onGestureTimerEvent";
5420
5421 if (m_binPan) {
5422 m_binPan = false;
5423 Invalidate();
5424 Update();
5425 }
5426 m_bgestureGuard = false;
5427 m_bpinchGuard = false;
5428 m_binGesture = false;
5429 m_bforcefull = false;
5430}
5431
5432void glChartCanvas::onGestureFinishTimerEvent(wxTimerEvent &event) {
5433 // qDebug() << "onGestureFinishTimerEvent";
5434
5435 // signal gesture is finished after a delay
5436 m_binGesture = false;
5437 m_bforcefull = false;
5438}
5439
5440#else
5441#ifdef HAVE_WX_GESTURE_EVENTS
5442
5443void glChartCanvas::OnEvtPanGesture(wxPanGestureEvent &event) {
5444 // qDebug() << "OnEvtPanGesture" << m_pParentCanvas->m_canvasIndex <<
5445 // event.cursor_pos.x;
5446
5447 if (m_pParentCanvas->isRouteEditing() || m_pParentCanvas->isMarkEditing())
5448 return;
5449
5450 if (m_binPinch) return;
5451 if (m_bpinchGuard) return;
5452
5453 int dx = event.GetDelta().x;
5454 int dy = event.GetDelta().y;
5455
5456 if (event.IsGestureStart()) {
5457 if (m_binPan) return;
5458
5459 panx = pany = 0;
5460 m_binPan = true;
5461 m_binGesture = true;
5462 // qDebug() << "pg1";
5463 }
5464
5465 else if (event.IsGestureEnd()) {
5466 // qDebug() << "panGestureFinished";
5467 m_pParentCanvas->UpdateCanvasControlBar();
5468 m_binPan = false;
5469 m_gestureFinishTimer.Start(500, wxTIMER_ONE_SHOT);
5470 }
5471
5472 else {
5473 if (m_binPan) {
5474 if (!g_GLOptions.m_bUseCanvasPanning) {
5475 // qDebug() << "slowpan" << dx << dy;
5476
5477 m_pParentCanvas->FreezePiano();
5478 m_pParentCanvas->PanCanvas(dx, -dy);
5479 m_pParentCanvas->ThawPiano();
5480
5481 } else {
5482 FastPan(dx, dy);
5483 }
5484
5485 panx -= dx;
5486 pany -= dy;
5487 }
5488 }
5489
5490 m_bgestureGuard = true;
5491 m_gestureEeventTimer.Start(500, wxTIMER_ONE_SHOT);
5492 m_bforcefull = false;
5493}
5494
5495bool first_zout = false;
5496
5497// Generic wxWidgets gesture event processor
5498void glChartCanvas::OnEvtZoomGesture(wxZoomGestureEvent &event) {
5499 float zoom_gain = 1.0;
5500 float zout_gain = 1.0;
5501
5502 float last_zoom_val = m_total_zoom_val;
5503
5504 float max_zoom_scale = 1000.;
5505 float min_zoom_scale = 2e8;
5506
5507 if (event.GetZoomFactor() > 1)
5508 m_total_zoom_val = ((event.GetZoomFactor() - 1.0) * zoom_gain) + 1.0;
5509 else
5510 m_total_zoom_val = 1.0 - ((1.0 - event.GetZoomFactor()) * zout_gain);
5511
5512 float inc_zoom_val =
5513 m_total_zoom_val / last_zoom_val; // the incremental zoom
5514
5515 double projected_scale =
5516 m_pParentCanvas->GetVP().chart_scale / m_total_zoom_val;
5517
5518 if (event.IsGestureStart()) {
5519 // printf("\nStart--------------\n");
5520 first_zout = false;
5521 m_binPinch = true;
5522 m_binPan = false; // cancel any tentative pan gesture, in case the "pan
5523 // cancel" event was lost
5524 m_binGesture = true;
5525 m_pinchStart = event.GetPosition();
5526 m_lpinchPoint = m_pinchStart;
5527 m_total_zoom_val = 1.0;
5528 m_final_zoom_val = 1.0;
5529
5530 m_pParentCanvas->GetCanvasPixPoint(
5531 event.GetPosition().x, event.GetPosition().y, m_pinchlat, m_pinchlon);
5532 // qDebug() << "center" << event.GetCenterPoint().x <<
5533 // event.GetCenterPoint().y;
5534
5535 m_cc_x = m_fbo_offsetx + (m_fbo_swidth / 2);
5536 m_cc_y = m_fbo_offsety + (m_fbo_sheight / 2);
5537
5538 // Render the full charts with overlay objects onto the frame buffer.
5539 SetCurrent(*m_pcontext);
5540 RenderScene();
5541#ifndef __ANDROID__
5542 ViewPort vpr = m_pParentCanvas->VPoint;
5543 m_texVP = vpr;
5544 GetTouchBackingBitmap(vpr);
5545#endif
5546 m_zoom_inc = 1.0;
5547 }
5548
5549 if (event.IsGestureEnd()) {
5550 // printf("End--------------\n");
5551 // qDebug() << "finish totalzoom" << total_zoom_val <<
5552 // projected_scale;
5553
5554 // Some ptaforms generate spurious gestureEnd events. Guard for this.
5555 if (!m_binGesture) return;
5556
5557 float cc_x = m_fbo_offsetx + (m_fbo_swidth / 2);
5558 float cc_y = m_fbo_offsety + (m_fbo_sheight / 2);
5559 float dy = 0;
5560 float dx = 0;
5561
5562 float tzoom = m_final_zoom_val;
5563
5564 dx = (cc_x - m_cc_x) * tzoom;
5565 dy = -(cc_y - m_cc_y) * tzoom;
5566
5567 if (zoomTimer.IsRunning()) {
5568 // qDebug() << "Final zoom";
5569 m_zoomFinal = true;
5570 m_zoomFinalZoom = tzoom;
5571 m_zoomFinaldx = dx;
5572 m_zoomFinaldy = dy;
5573 }
5574
5575 else {
5576 double final_projected_scale =
5577 m_pParentCanvas->GetVP().chart_scale / tzoom;
5578 // qDebug() << "Final pinchB" << tzoom << final_projected_scale;
5579
5580 if (final_projected_scale < min_zoom_scale) {
5581 // qDebug() << "zoomit";
5582 m_pParentCanvas->ZoomCanvas(tzoom, false);
5583 m_pParentCanvas->PanCanvas(dx, dy);
5584 m_pParentCanvas->m_pQuilt->Invalidate();
5585 m_bforcefull = true;
5586
5587 } else {
5588 double new_scale =
5589 m_pParentCanvas->GetCanvasScaleFactor() / min_zoom_scale;
5590 // qDebug() << "clampit";
5591 m_pParentCanvas->SetVPScale(new_scale);
5592 m_pParentCanvas->m_pQuilt->Invalidate();
5593 m_bforcefull = true;
5594 }
5595 }
5596
5597 m_binPinch = false;
5598 m_final_zoom_val = 1.0;
5599 m_total_zoom_val = 1.0;
5600 m_gestureFinishTimer.Start(500, wxTIMER_ONE_SHOT);
5601 }
5602
5603 else {
5604 if (1 /* g_GLOptions.m_bUseCanvasPanning*/) {
5605 if (projected_scale < min_zoom_scale) {
5606 wxPoint pinchPoint = event.GetPosition();
5607
5608 float dx = pinchPoint.x - m_lpinchPoint.x;
5609 float dy = pinchPoint.y - m_lpinchPoint.y;
5610
5611 FastZoom(inc_zoom_val, m_pinchStart.x, m_pinchStart.y,
5612 -dx / m_total_zoom_val, dy / m_total_zoom_val);
5613
5614 m_lpinchPoint = pinchPoint;
5615 m_final_zoom_val *= inc_zoom_val;
5616 }
5617 } else {
5618 // qDebug() << "update totalzoom" << total_zoom_val << projected_scale;
5619 if (1 || ((m_total_zoom_val > 1) && !first_zout)) { // Zoom in
5620 wxPoint pinchPoint = event.GetPosition();
5621
5622 float dx = pinchPoint.x - m_lpinchPoint.x;
5623 float dy = pinchPoint.y - m_lpinchPoint.y;
5624
5625 if ((projected_scale > max_zoom_scale) &&
5626 (projected_scale < min_zoom_scale))
5627 FastZoom(inc_zoom_val, m_pinchStart.x, m_pinchStart.y,
5628 -dx / m_total_zoom_val, dy / m_total_zoom_val);
5629
5630 m_lpinchPoint = pinchPoint;
5631 m_final_zoom_val *= inc_zoom_val;
5632
5633 } else {
5634 first_zout = true;
5635 m_zoom_inc *= inc_zoom_val;
5636 if ((m_zoom_inc < 0.9) || (m_zoom_inc > 1.1)) {
5637 m_pParentCanvas->ZoomCanvas(m_zoom_inc, false);
5638 m_zoom_inc = 1.0;
5639 }
5640
5641 wxPoint pinchPoint = event.GetPosition();
5642 float dx = pinchPoint.x - m_lpinchPoint.x;
5643 float dy = pinchPoint.y - m_lpinchPoint.y;
5644 m_pParentCanvas->PanCanvas(-dx, -dy);
5645 m_lpinchPoint = pinchPoint;
5646 }
5647 }
5648 }
5649 m_bgestureGuard = true;
5650 m_bpinchGuard = true;
5651 m_gestureEeventTimer.Start(500, wxTIMER_ONE_SHOT);
5652}
5653
5654void glChartCanvas::onGestureTimerEvent(wxTimerEvent &event) {
5655 // On some devices, the pan GestureFinished event fails to show up
5656 // Watch for this case, and fix it.....
5657 // qDebug() << "onGestureTimerEvent";
5658
5659 if (m_binPan) {
5660 m_binPan = false;
5661 Invalidate();
5662 Update();
5663 }
5664 m_bgestureGuard = false;
5665 m_bpinchGuard = false;
5666 m_binGesture = false;
5667 m_bforcefull = false;
5668}
5669
5670void glChartCanvas::onGestureFinishTimerEvent(wxTimerEvent &event) {
5671 // qDebug() << "onGestureFinishTimerEvent";
5672
5673 // signal gesture is finished after a delay
5674 m_binGesture = false;
5675 m_bforcefull = false;
5676}
5677
5678#endif
5679#endif
5680
5681void glChartCanvas::configureShaders(ViewPort &vp) {
5682#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
5683 mat4x4 I;
5684 mat4x4_identity(I);
5685
5686 ViewPort *pvp = (ViewPort *)&vp;
5687
5688 GLShaderProgram *shader = pcolor_tri_shader_program[GetCanvasIndex()];
5689 shader->Bind();
5690 shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)pvp->vp_matrix_transform);
5691 shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)I);
5692 shader->UnBind();
5693
5694 // glUseProgram(color_tri_shader_program);
5695 // GLint matloc = glGetUniformLocation(color_tri_shader_program,
5696 // "MVMatrix"); glUniformMatrix4fv(matloc, 1, GL_FALSE,
5697 // (const GLfloat *)pvp->vp_transform);
5698 // GLint transloc =
5699 // glGetUniformLocation(color_tri_shader_program,
5700 // "TransformMatrix");
5701 // glUniformMatrix4fv(transloc, 1, GL_FALSE, (const GLfloat *)I);
5702
5703 // glUseProgram(texture_2D_shader_program);
5704 // matloc = glGetUniformLocation(texture_2D_shader_program, "MVMatrix");
5705 // glUniformMatrix4fv(matloc, 1, GL_FALSE,
5706 // (const GLfloat *)pvp->vp_transform);
5707 // transloc =
5708 // glGetUniformLocation(texture_2D_shader_program,
5709 // "TransformMatrix");
5710 // glUniformMatrix4fv(transloc, 1, GL_FALSE, (const GLfloat *)I);
5711
5712 shader = ptexture_2D_shader_program[GetCanvasIndex()];
5713 shader->Bind();
5714 shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)pvp->vp_matrix_transform);
5715 shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)I);
5716 shader->UnBind();
5717
5718 // glUseProgram(circle_filled_shader_program);
5719 // matloc = glGetUniformLocation(circle_filled_shader_program,
5720 // "MVMatrix"); glUniformMatrix4fv(matloc, 1, GL_FALSE,
5721 // (const GLfloat *)pvp->vp_transform);
5722 // transloc =
5723 // glGetUniformLocation(circle_filled_shader_program,
5724 // "TransformMatrix");
5725 // glUniformMatrix4fv(transloc, 1, GL_FALSE, (const GLfloat *)I);
5726
5727 shader = pcircle_filled_shader_program[GetCanvasIndex()];
5728 shader->Bind();
5729 shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)pvp->vp_matrix_transform);
5730 shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)I);
5731 shader->UnBind();
5732
5733 shader = ptexture_2DA_shader_program[GetCanvasIndex()];
5734 shader->Bind();
5735 shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)pvp->vp_matrix_transform);
5736 shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)I);
5737 shader->UnBind();
5738
5739 // glUseProgram(AALine_shader_program);
5740 // matloc = glGetUniformLocation(AALine_shader_program, "MVMatrix");
5741 // glUniformMatrix4fv(matloc, 1, GL_FALSE,
5742 // (const GLfloat *)pvp->vp_transform);
5743
5744 shader = pAALine_shader_program[GetCanvasIndex()];
5745 shader->Bind();
5746 shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)pvp->vp_matrix_transform);
5747 shader->UnBind();
5748
5749 shader = pring_shader_program[GetCanvasIndex()];
5750 shader->Bind();
5751 shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)pvp->vp_matrix_transform);
5752 shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)I);
5753 shader->UnBind();
5754
5755 // Leftover shader required by some older Android plugins
5756 if (texture_2DA_shader_program) {
5757 glUseProgram(texture_2DA_shader_program);
5758 GLint matloc = glGetUniformLocation(texture_2DA_shader_program, "MVMatrix");
5759 glUniformMatrix4fv(matloc, 1, GL_FALSE,
5760 (const GLfloat *)pvp->vp_matrix_transform);
5761 GLint transloc =
5762 glGetUniformLocation(texture_2DA_shader_program, "TransformMatrix");
5763 glUniformMatrix4fv(transloc, 1, GL_FALSE, (const GLfloat *)I);
5764 }
5765
5766 m_gldc.m_texfont.PrepareShader(vp.pix_width, vp.pix_height, vp.rotation);
5767
5768#endif
5769}
5770
5771void glChartCanvas::RenderTextures(ocpnDC &dc, float *coords, float *uvCoords,
5772 int nVertex, ViewPort *vp) {
5773// #ifdef USE_ANDROID_GLES2
5774#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
5775 int nl = nVertex / 4;
5776 float *lc = coords;
5777 float *luv = uvCoords;
5778
5779 while (nl) {
5780 RenderSingleTexture(dc, lc, luv, vp, 0, 0, 0);
5781
5782 lc += 8;
5783 luv += 8;
5784 nl--;
5785 }
5786
5787#else
5788 glEnableClientState(GL_VERTEX_ARRAY);
5789 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
5790
5791 glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), uvCoords);
5792 glVertexPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), coords);
5793 glDrawArrays(GL_QUADS, 0, 4);
5794
5795#endif
5796
5797 return;
5798}
5799
5800void glChartCanvas::RenderSingleTexture(ocpnDC &dc, float *coords,
5801 float *uvCoords, ViewPort *vp, float dx,
5802 float dy, float angle_rad) {
5803#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
5804
5805 GLShaderProgram *shader = ptexture_2D_shader_program[dc.m_canvasIndex];
5806 if (!shader) return;
5807
5808 shader->Bind();
5809
5810 // Set up the texture sampler to texture unit 0
5811 shader->SetUniform1i("uTex", 0);
5812
5813 // Rotate
5814 mat4x4 I, Q;
5815 mat4x4_identity(I);
5816 mat4x4_rotate_Z(Q, I, angle_rad);
5817
5818 // Translate
5819 Q[3][0] = dx;
5820 Q[3][1] = dy;
5821
5822 shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)Q);
5823
5824 float co1[8];
5825 float tco1[8];
5826
5827 shader->SetAttributePointerf("aPos", co1);
5828 shader->SetAttributePointerf("aUV", tco1);
5829
5830// Perform the actual drawing.
5831
5832// For some reason, glDrawElements is busted on Android
5833// So we do this a hard ugly way, drawing two triangles...
5834#if 0
5835 GLushort indices1[] = {0,1,3,2};
5836 glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, indices1);
5837#else
5838
5839 co1[0] = coords[0];
5840 co1[1] = coords[1];
5841 co1[2] = coords[2];
5842 co1[3] = coords[3];
5843 co1[4] = coords[6];
5844 co1[5] = coords[7];
5845 co1[6] = coords[4];
5846 co1[7] = coords[5];
5847
5848 tco1[0] = uvCoords[0];
5849 tco1[1] = uvCoords[1];
5850 tco1[2] = uvCoords[2];
5851 tco1[3] = uvCoords[3];
5852 tco1[4] = uvCoords[6];
5853 tco1[5] = uvCoords[7];
5854 tco1[6] = uvCoords[4];
5855 tco1[7] = uvCoords[5];
5856
5857 // glVertexAttribPointer(mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, co1);
5858 // glVertexAttribPointer(mUvAttrib, 2, GL_FLOAT, GL_FALSE, 0, tco1);
5859
5860 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
5861
5862 shader->UnBind();
5863
5864#endif
5865
5866#else
5867#endif
5868
5869 return;
5870}
5871
5872void glChartCanvas::RenderColorRect(wxRect r, wxColor &color) {
5873#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
5874
5875 GLShaderProgram *shader = pcolor_tri_shader_program[GetCanvasIndex()];
5876 shader->Bind();
5877
5878 shader->SetUniformMatrix4fv(
5879 "MVMatrix", (GLfloat *)m_pParentCanvas->GetpVP()->vp_matrix_transform);
5880
5881 float colorv[4];
5882 colorv[0] = color.Red() / float(256);
5883 colorv[1] = color.Green() / float(256);
5884 colorv[2] = color.Blue() / float(256);
5885 colorv[3] = 1.0;
5886 shader->SetUniform4fv("color", colorv);
5887
5888 float pf[8];
5889 pf[0] = r.x + r.width;
5890 pf[1] = r.y;
5891 pf[2] = r.x;
5892 pf[3] = r.y;
5893 pf[4] = r.x + r.width;
5894 pf[5] = r.y + r.height;
5895 pf[6] = r.x;
5896 pf[7] = r.y + r.height;
5897 shader->SetAttributePointerf("position", pf);
5898
5899 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
5900
5901 shader->UnBind();
5902
5903#else
5904#endif
5905}
5906
5907void glChartCanvas::RenderScene(bool bRenderCharts, bool bRenderOverlays) {
5908#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
5909
5910 ViewPort VPoint = m_pParentCanvas->VPoint;
5911 ocpnDC gldc(*this);
5912
5913 int w, h;
5914 GetClientSize(&w, &h);
5915 int sx = GetSize().x;
5916 int sy = GetSize().y;
5917
5918 OCPNRegion screen_region(wxRect(0, 0, VPoint.pix_width, VPoint.pix_height));
5919
5920 glViewport(0, 0, (GLint)w, (GLint)h);
5921
5922 if (s_b_useStencil) {
5923 glEnable(GL_STENCIL_TEST);
5924 glStencilMask(0xff);
5925 glClear(GL_STENCIL_BUFFER_BIT);
5926 glDisable(GL_STENCIL_TEST);
5927 }
5928
5929 // Make sure we have a valid quilt composition
5930 m_pParentCanvas->m_pQuilt->Compose(m_pParentCanvas->VPoint);
5931
5932 // set opengl settings that don't normally change
5933 // this should be able to go in SetupOpenGL, but it's
5934 // safer here incase a plugin mangles these
5935 glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
5936 glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
5937 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
5938
5939 // enable rendering to texture in framebuffer object
5940 glBindFramebuffer(GL_FRAMEBUFFER_EXT, m_fb0);
5941
5942 glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0,
5943 g_texture_rectangle_format, m_cache_tex[m_cache_page],
5944 0);
5945
5946 m_fbo_offsetx = 0;
5947 m_fbo_offsety = 0;
5948 m_fbo_swidth = sx;
5949 m_fbo_sheight = sy;
5950
5951 if (bRenderCharts) RenderCharts(gldc, screen_region);
5952
5953 if (bRenderOverlays) {
5954 RenderS57TextOverlay(m_pParentCanvas->VPoint);
5955 RenderMBTilesOverlay(m_pParentCanvas->VPoint);
5956 DrawStaticRoutesTracksAndWaypoints(m_pParentCanvas->VPoint);
5957 DrawDynamicRoutesTracksAndWaypoints(VPoint);
5958 DrawFloatingOverlayObjects(m_gldc);
5959 }
5960
5961 // All done, so disable Render to FBO
5962 glBindFramebuffer(GL_FRAMEBUFFER, 0);
5963
5964#endif
5965}
5966
5967wxBitmap &glChartCanvas::GetTouchBackingBitmap(ViewPort &vp) {
5968 wxBitmap tbm(vp.pix_width, vp.pix_height, -1);
5969 wxMemoryDC tdc(tbm);
5970 tdc.SetBackground(wxBrush(GetGlobalColor("BLUEBACK")));
5971 tdc.Clear();
5972 ocpnDC dc = ocpnDC(tdc);
5973 ViewPort tvp = vp;
5974
5975 tvp.view_scale_ppm /= 2;
5976 tvp.SetBoxes();
5977
5978 gShapeBasemap.SetBasemapLandColor(GetGlobalColor("LANDBACK"));
5979 dc.SetPen(*wxTRANSPARENT_PEN);
5980
5981 gShapeBasemap.RenderViewOnDC(dc, tvp);
5982 tdc.SelectObject(wxNullBitmap);
5983 m_touch_backing_bitmap = tbm;
5984 CreateBackingTexture();
5985
5986 return m_touch_backing_bitmap;
5987}
5988
5989void glChartCanvas::CreateBackingTexture() {
5990 wxImage image = m_touch_backing_bitmap.ConvertToImage();
5991 unsigned char *imgdata = image.GetData();
5992 unsigned char *imgalpha = image.GetAlpha();
5993 m_tex_w = image.GetWidth();
5994 m_tex_h = image.GetHeight();
5995 m_image_width = m_tex_w;
5996 m_image_height = m_tex_h;
5997
5998 GLuint format = GL_RGBA;
5999 GLuint internalformat = g_texture_rectangle_format;
6000#ifndef __ANDROID__
6001 internalformat = GL_RGBA;
6002#endif
6003 int stride = 4;
6004
6005 if (imgdata) {
6006 unsigned char *teximage =
6007 (unsigned char *)malloc(stride * m_tex_w * m_tex_h);
6008
6009 for (int i = 0; i < m_image_height; i++) {
6010 for (int j = 0; j < m_image_width; j++) {
6011 int s = (i * 3 * m_image_width) + (j * 3);
6012 int d = (i * stride * m_tex_w) + (j * stride);
6013
6014 teximage[d + 0] = imgdata[s + 0];
6015 teximage[d + 1] = imgdata[s + 1];
6016 teximage[d + 2] = imgdata[s + 2];
6017 teximage[d + 3] = 255;
6018 }
6019 }
6020
6021 glGenTextures(1, &m_TouchBackingTexture);
6022 glBindTexture(GL_TEXTURE_2D, m_TouchBackingTexture);
6023
6024 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
6025 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
6026 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
6027 GL_NEAREST); // No mipmapping
6028 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
6029
6030 glTexImage2D(GL_TEXTURE_2D, 0, internalformat, m_tex_w, m_tex_h, 0, format,
6031 GL_UNSIGNED_BYTE, teximage);
6032
6033 free(teximage);
6034 glBindTexture(GL_TEXTURE_2D, 0);
6035 }
6036}
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:226
Base class for BSB (Maptech/NOS) format nautical charts.
Definition chartimg.h:131
Base class for all chart types.
Definition chartbase.h:119
ChartCanvas - Main chart display and interaction component.
Definition chcanv.h:151
bool GetCanvasPointPix(double rlat, double rlon, wxPoint *r)
Convert latitude/longitude to canvas pixel coordinates (physical pixels) rounded to nearest integer.
Definition chcanv.cpp:4563
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:4513
double GetCanvasScaleFactor()
Return the number of logical pixels per meter for the screen.
Definition chcanv.h:461
double GetPixPerMM()
Get the number of logical pixels per millimeter on the screen.
Definition chcanv.h:492
bool MouseEventSetup(wxMouseEvent &event, bool b_handle_dclick=true)
Definition chcanv.cpp:7568
bool PanCanvas(double dx, double dy)
Pans (moves) the canvas by the specified physical pixels in x and y directions.
Definition chcanv.cpp:5064
float GetVPScale()
Return the ViewPort scale factor, in physical pixels per meter.
Definition chcanv.h:450
void ZoomCanvasSimple(double factor)
Perform an immediate zoom operation without smooth transitions.
Definition chcanv.cpp:4644
bool SetVPScale(double sc, bool b_refresh=true)
Sets the viewport scale while maintaining the center point.
Definition chcanv.cpp:5338
void GetCanvasPixPoint(double x, double y, double &lat, double &lon)
Convert canvas pixel coordinates (physical pixels) to latitude/longitude.
Definition chcanv.cpp:4588
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:4649
void GetDoubleCanvasPointPix(double rlat, double rlon, wxPoint2DDouble *r)
Convert latitude/longitude to canvas pixel coordinates (physical pixels) with double precision.
Definition chcanv.cpp:4508
bool MouseEventProcessCanvas(wxMouseEvent &event)
Processes mouse events for core chart panning and zooming operations.
Definition chcanv.cpp:9888
Manages the chart database and provides access to chart data.
Definition chartdb.h:95
Represents an MBTiles format chart.
Definition mbtiles.h:69
Wrapper class for plugin-based charts.
Definition chartimg.h:392
wxFont * FindOrCreateFont(int point_size, wxFontFamily family, wxFontStyle style, wxFontWeight weight, bool underline=false, const wxString &facename=wxEmptyString, wxFontEncoding encoding=wxFONTENCODING_DEFAULT)
Creates or finds a matching font in the font cache.
Definition FontMgr.cpp:450
wxFont * GetFont(const wxString &TextElement, int requested_font_size=0)
Gets a font object for a UI element.
Definition FontMgr.cpp:186
Wrapper class for OpenGL shader programs.
Definition shaders.h:57
Represents an index entry for tidal and current data.
Definition IDX_entry.h:49
char IDX_type
Entry type identifier "TCtcIUu".
Definition IDX_entry.h:61
double IDX_lat
Latitude of the station (in degrees, +North)
Definition IDX_entry.h:65
double IDX_lon
Longitude of the station (in degrees, +East)
Definition IDX_entry.h:64
An iterator class for OCPNRegion.
Definition OCPNRegion.h:156
A wrapper class for wxRegion with additional functionality.
Definition OCPNRegion.h:56
bool Compose(const ViewPort &vp)
Definition Quilt.cpp:1695
Represents a waypoint or mark within the navigation system.
Definition route_point.h:68
Represents a navigational route in the navigation system.
Definition route.h:96
Manages a set of ShapeBaseChart objects at different resolutions.
Definition tcmgr.h:86
Window for displaying chart thumbnails.
Definition thumbwin.h:56
Represents a track, which is a series of connected track points.
Definition track.h:111
ViewPort - Core geographic projection and coordinate transformation engine.
Definition viewport.h:81
double view_scale_ppm
Requested view scale in physical pixels per meter (ppm), before applying projections.
Definition viewport.h:229
double ref_scale
The nominal scale of the "reference chart" for this view.
Definition viewport.h:246
int pix_height
Height of the viewport in physical pixels.
Definition viewport.h:258
void SetBoxes(void)
Computes the bounding box coordinates for the current viewport.
Definition viewport.cpp:823
double rotation
Rotation angle of the viewport in radians.
Definition viewport.h:239
void SetPixelScale(double scale)
Set the physical to logical pixel ratio for the display.
Definition viewport.cpp:133
int pix_width
Width of the viewport in physical pixels.
Definition viewport.h:256
wxPoint2DDouble GetDoublePixFromLL(double lat, double lon)
Convert latitude and longitude on the ViewPort to physical pixel coordinates with double precision.
Definition viewport.cpp:145
double tilt
Tilt angle for perspective view in radians.
Definition viewport.h:241
double skew
Angular distortion (shear transform) applied to the viewport in radians.
Definition viewport.h:237
void GetLLFromPix(const wxPoint &p, double *lat, double *lon)
Convert physical pixel coordinates on the ViewPort to latitude and longitude.
Definition viewport.h:105
double clon
Center longitude of the viewport in degrees.
Definition viewport.h:224
double clat
Center latitude of the viewport in degrees.
Definition viewport.h:222
wxPoint GetPixFromLL(double lat, double lon)
Convert latitude and longitude on the ViewPort to physical pixel coordinates.
Definition viewport.cpp:136
double chart_scale
Chart scale denominator (e.g., 50000 for a 1:50000 scale).
Definition viewport.h:244
Represents a composite CM93 chart covering multiple scales.
Definition cm93.h:424
Stores emboss effect data for textures.
Definition emboss_data.h:35
OpenGL chart rendering canvas.
Floating toolbar for iENC (International Electronic Navigational Chart) functionality.
Definition iENCToolbar.h:43
Device context class that can use either wxDC or OpenGL for drawing.
Definition ocpndc.h:64
void DrawLine(wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2, bool b_hiqual=true)
Draw a line between two points using either wxDC or OpenGL.
Definition ocpndc.cpp:476
Floating toolbar dialog for OpenCPN.
Definition toolbar.h:386
bool m_bUseCanvasPanning
Controls OpenGL canvas hardware-accelerated panning mode.
Represents an S57 format electronic navigational chart in OpenCPN.
Definition s57chart.h:120
The JSON value class implementation.
Definition jsonval.h:84
wxFont * GetOCPNScaledFont(wxString item, int default_size)
Retrieves a font from FontMgr, optionally scaled for physical readability.
Definition gui_lib.cpp:54
Class NotificationManager.
int GetChartbarHeight(void)
Gets height of chart bar in pixels.
double OCPN_GetDisplayContentScaleFactor()
Gets content scaling factor for current display.
Tools to send data to plugins.
Represents an entry in the chart table, containing information about a single chart.
Definition chartdbs.h:181