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"
183#endif
184
185extern bool GetMemoryStatus(int *mem_total, int *mem_used);
186
187extern s52plib *ps52plib;
188extern bool g_bopengl;
189extern bool g_bDebugOGL;
190extern bool g_bSoftwareGL;
191extern ocpnFloatingToolbarDialog *g_MainToolbar;
192extern iENCToolbar *g_iENCToolbar;
193extern bool g_bShowChartBar;
194extern glTextureManager *g_glTextureManager;
195extern bool b_inCompressAllCharts;
196extern bool g_bShowCompassWin;
197
198extern GLenum g_texture_rectangle_format;
199
200extern int g_memCacheLimit;
201extern ColorScheme global_color_scheme;
202extern bool g_bquiting;
203extern ThumbWin *pthumbwin;
204extern int g_mipmap_max_level;
205
206extern int g_OwnShipIconType;
207
208extern ChartDB *ChartData;
209
210extern PlugInManager *g_pi_manager;
211
212extern RouteList *pRouteList;
213extern std::vector<Track *> g_TrackList;
214extern bool b_inCompressAllCharts;
215extern bool g_bGLexpert;
216extern bool g_bcompression_wait;
217extern float g_ShipScaleFactorExp;
218
219float g_GLMinCartographicLineWidth;
220
221extern bool g_fog_overzoom;
222extern double g_overzoom_emphasis_base;
223extern bool g_oz_vector_scale;
224extern TCMgr *ptcmgr;
225extern int g_nCPUCount;
226extern bool g_running;
227
228extern unsigned int g_canvasConfig;
229extern ChartCanvas *g_focusCanvas;
230extern ChartCanvas *g_overlayCanvas;
231extern BasePlatform *g_BasePlatform;
232extern bool g_PrintingInProgress;
233
234ocpnGLOptions g_GLOptions;
235
236wxColor s_regionColor;
237extern ShapeBaseChartSet gShapeBasemap;
238
239// For VBO(s)
240bool g_b_EnableVBO;
241bool g_b_needFinish; // Need glFinish() call on each frame?
242
243// MacOS has some missing parts:
244#ifndef APIENTRY
245#define APIENTRY
246#endif
247#ifndef APIENTRYP
248#define APIENTRYP APIENTRY *
249#endif
250#ifndef GLAPI
251#define GLAPI extern
252#endif
253
254#ifndef GL_COMPRESSED_RGB_FXT1_3DFX
255#define GL_COMPRESSED_RGB_FXT1_3DFX 0x86B0
256#endif
257
258PFNGLGENFRAMEBUFFERSEXTPROC s_glGenFramebuffers;
259PFNGLGENRENDERBUFFERSEXTPROC s_glGenRenderbuffers;
260PFNGLFRAMEBUFFERTEXTURE2DEXTPROC s_glFramebufferTexture2D;
261PFNGLBINDFRAMEBUFFEREXTPROC s_glBindFramebuffer;
262PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC s_glFramebufferRenderbuffer;
263PFNGLRENDERBUFFERSTORAGEEXTPROC s_glRenderbufferStorage;
264PFNGLBINDRENDERBUFFEREXTPROC s_glBindRenderbuffer;
265PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC s_glCheckFramebufferStatus;
266PFNGLDELETEFRAMEBUFFERSEXTPROC s_glDeleteFramebuffers;
267PFNGLDELETERENDERBUFFERSEXTPROC s_glDeleteRenderbuffers;
268
269PFNGLCOMPRESSEDTEXIMAGE2DPROC s_glCompressedTexImage2D;
270PFNGLGETCOMPRESSEDTEXIMAGEPROC s_glGetCompressedTexImage;
271
272// Vertex Buffer Object (VBO) support
273PFNGLGENBUFFERSPROC s_glGenBuffers;
274PFNGLBINDBUFFERPROC s_glBindBuffer;
275PFNGLBUFFERDATAPROC s_glBufferData;
276PFNGLDELETEBUFFERSPROC s_glDeleteBuffers;
277
278#ifndef USE_ANDROID_GLES2
279// #define glDeleteFramebuffers(a, b) (s_glDeleteFramebuffers)(a, b);
280// #define glDeleteRenderbuffers(a, b) (s_glDeleteRenderbuffers)(a, b);
281#endif
282
283typedef void(APIENTRYP PFNGLGETBUFFERPARAMETERIV)(GLenum target, GLenum value,
284 GLint *data);
285PFNGLGETBUFFERPARAMETERIV s_glGetBufferParameteriv;
286
287#include <wx/arrimpl.cpp>
288// WX_DEFINE_OBJARRAY( ArrayOfTexDescriptors );
289
290GLuint g_raster_format = GL_RGB;
291long g_tex_mem_used;
292
293bool b_timeGL;
294wxStopWatch g_glstopwatch;
295double g_gl_ms_per_frame;
296
297int g_tile_size;
298int g_uncompressed_tile_size;
299
300extern wxProgressDialog *pprog;
301extern bool b_skipout;
302extern wxSize pprog_size;
303extern int pprog_count;
304extern int pprog_threads;
305extern MyFrame *gFrame;
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 wxPaintDC(this);
3953
3954 ocpnDC gldc(*this);
3955
3956 int gl_width, gl_height;
3957 gl_width = m_pParentCanvas->VPoint.pix_width;
3958 gl_height = m_pParentCanvas->VPoint.pix_height;
3959
3960 // Take a copy for use later by DC
3961 m_glcanvas_width = gl_width;
3962 m_glcanvas_height = gl_height;
3963
3964 // Avoid some harmonic difficulties with odd-size glCanvas
3965 bool b_odd = false;
3966 if (gl_height & 1) {
3967 gl_height -= 1;
3968 ViewPort *vp = m_pParentCanvas->GetpVP();
3969 vp->pix_height = gl_height;
3970 b_odd = true;
3971 }
3972
3973 if (gl_width & 1) {
3974 gl_width -= 1;
3975 ViewPort *vp = m_pParentCanvas->GetpVP();
3976 vp->pix_width = gl_width;
3977 b_odd = true;
3978 }
3979
3980 // Set the shader viewport transform matrix
3981 // Using the adjusted dimensions
3982 if (b_odd) {
3983 ViewPort *vp = m_pParentCanvas->GetpVP();
3984 mat4x4 m;
3985 mat4x4_identity(m);
3986 mat4x4_scale_aniso((float(*)[4])vp->vp_matrix_transform, m,
3987 2.0 / (float)vp->pix_width, -2.0 / (float)vp->pix_height,
3988 1.0);
3989 mat4x4_translate_in_place((float(*)[4])vp->vp_matrix_transform,
3990 -vp->pix_width / 2, -vp->pix_height / 2, 0);
3991 }
3992
3993 // @todo: If the intention was to work with the same ViewPort object, use a
3994 // reference instead. Making a copy of VPoint here means that any changes to
3995 // VPoint will not affect m_pParentCanvas->VPoint. It's not clear if this is
3996 // the intended behavior.
3997 ViewPort VPoint = m_pParentCanvas->VPoint;
3998
3999 OCPNRegion screen_region(wxRect(0, 0, gl_width, gl_height));
4000 glViewport(0, 0, (GLint)gl_width, (GLint)gl_height);
4001
4002// #ifndef USE_ANDROID_GLES2
4003#if !defined(USE_ANDROID_GLES2)
4004 glMatrixMode(GL_PROJECTION);
4005 glLoadIdentity();
4006
4007 glOrtho(0, (GLint)gl_width, (GLint)gl_height, 0, -1, 1);
4008 glMatrixMode(GL_MODELVIEW);
4009 glLoadIdentity();
4010#endif
4011
4012 if (s_b_useStencil) {
4013 glEnable(GL_STENCIL_TEST);
4014 glStencilMask(0xff);
4015 glClear(GL_STENCIL_BUFFER_BIT);
4016 glDisable(GL_STENCIL_TEST);
4017 }
4018
4019 // set opengl settings that don't normally change
4020 // this should be able to go in SetupOpenGL, but it's
4021 // safer here incase a plugin mangles these
4022 if (g_GLOptions.m_GLLineSmoothing) glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
4023 if (g_GLOptions.m_GLPolygonSmoothing)
4024 glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
4025 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
4026
4027 // Delete any textures known to the GPU that
4028 // belong to charts which will not be used in this render
4029 // This is done chart-by-chart...later we will scrub for unused textures
4030 // that belong to charts which ARE used in this render, if we need to....
4031
4032 g_glTextureManager->TextureCrunch(0.8);
4033
4034 // If we plan to post process the display, don't use accelerated panning
4035 double scale_factor = VPoint.ref_scale / VPoint.chart_scale;
4036
4037 bool bpost_hilite = !m_pParentCanvas->m_pQuilt->GetHiliteRegion().Empty();
4038 bool useFBO = false;
4039 int sx = gl_width;
4040 int sy = gl_height;
4041
4042 // Try to use the framebuffer object's cache of the last frame
4043 // to accelerate drawing this frame (if overlapping)
4044 if (m_b_BuiltFBO && !bpost_hilite
4045 //&& VPoint.tilt == 0 // disabling fbo in tilt mode gives better quality
4046 // but slower
4047 ) {
4048 // Is this viewpoint the same as the previously painted one?
4049 bool b_newview = true;
4050 bool b_full = false;
4051
4052 // If the view is the same we do no updates,
4053 // Just render cached texture to the framebuffer
4054 if (m_cache_vp.view_scale_ppm == VPoint.view_scale_ppm &&
4055 m_cache_vp.rotation == VPoint.rotation &&
4056 m_cache_vp.clat == VPoint.clat && m_cache_vp.clon == VPoint.clon &&
4057 m_cache_vp.IsValid() && m_cache_vp.pix_height == VPoint.pix_height &&
4058 m_cache_current_ch == m_pParentCanvas->m_singleChart) {
4059 b_newview = false;
4060 }
4061
4062#ifdef USE_ANDROID_GLES2
4063 if (recompose) b_newview = true;
4064
4065 if (m_bforcefull) {
4066 b_newview = true;
4067 b_full = true;
4068 }
4069
4070 // If no charts are to be rendered, we need to refresh the entire display
4071 // This fixes a problem with routes/tracks/marks rendering on pans at very
4072 // small scale. It is a workaround, so finding root cause should be
4073 // considered a TODO
4074
4075 if (VPoint.b_quilt) {
4076 ChartBase *chart = m_pParentCanvas->m_pQuilt->GetFirstChart();
4077 if (!chart) b_full = true;
4078 }
4079
4080#endif
4081
4082 if (b_newview) {
4083 float dx = 0;
4084 float dy = 0;
4085
4086 bool accelerated_pan = false;
4087 // if (g_in_inertia)
4088 // printf("--- accpan condition %d %d\n",
4089 // g_GLOptions.m_bUseAcceleratedPanning,
4090 // m_cache_vp.IsValid());
4091 // else
4092 // printf("||| accpan condition %d %d\n",
4093 // g_GLOptions.m_bUseAcceleratedPanning,
4094 // m_cache_vp.IsValid());
4095
4096 if (g_GLOptions.m_bUseAcceleratedPanning && m_cache_vp.IsValid() &&
4097 (VPoint.m_projection_type == PROJECTION_MERCATOR ||
4098 VPoint.m_projection_type == PROJECTION_EQUIRECTANGULAR) &&
4099 m_cache_vp.pix_height == VPoint.pix_height) {
4100 wxPoint2DDouble c_old =
4101 VPoint.GetDoublePixFromLL(VPoint.clat, VPoint.clon) *
4102 m_displayScale;
4103 wxPoint2DDouble c_new =
4104 m_cache_vp.GetDoublePixFromLL(VPoint.clat, VPoint.clon) *
4105 m_displayScale;
4106
4107 dy = wxRound(c_new.m_y - c_old.m_y);
4108 dx = wxRound(c_new.m_x - c_old.m_x);
4109
4110 // The math below using the previous frame's texture does not really
4111 // work for sub-pixel pans.
4112 // TODO is to rethink this.
4113 // Meanwhile, require the accelerated pans to be whole pixel multiples
4114 // only. This is not as bad as it sounds. Keyboard and mouse pans are
4115 // whole_pixel moves. However, autofollow at large scale is certainly
4116 // not.
4117
4118 double deltax = c_new.m_x - c_old.m_x;
4119 double deltay = c_new.m_y - c_old.m_y;
4120
4121 bool b_whole_pixel = true;
4122 if ((fabs(deltax - dx) > 1e-2) || (fabs(deltay - dy) > 1e-2))
4123 b_whole_pixel = false;
4124
4125 accelerated_pan = b_whole_pixel && abs(dx) < m_cache_tex_x &&
4126 abs(dy) < m_cache_tex_y &&
4127 (abs(dx) > 0 || (abs(dy) > 0));
4128
4129 // if (g_in_inertia && !accelerated_pan)
4130 // printf("--- accpan %d %d %g %g\n", accelerated_pan,
4131 // b_whole_pixel, dx, dy);
4132 }
4133
4134 // FBO swapping has trouble with Retina display on MacOS Monterey.
4135 // So, disable accelerated pan ops on this case.
4136 if (m_displayScale > 1) accelerated_pan = false;
4137
4138 // FIXME (dave) There are some display artifact troubles using accPan on
4139 // rotation.
4140 // Especially seen on sparse RNC rendering
4141 if (fabs(VPoint.rotation) > 0) accelerated_pan = false;
4142
4143 // do we allow accelerated panning? can we perform it here?
4144#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
4145#else // GLES2
4146 // enable rendering to texture in framebuffer object
4147 glBindFramebuffer(GL_FRAMEBUFFER, m_fb0);
4148
4149 if (VPoint.chart_scale < 5000) b_full = true;
4150
4151 if (VPoint.chart_scale > 5e7) b_full = true;
4152
4153 if (b_full) accelerated_pan = false;
4154
4155 if (accelerated_pan) {
4156 if ((dx != 0) || (dy != 0)) { // Anything to do?
4157
4158 // calculate the new regions to render
4159 // add extra pixels to avoid coordindate rounding issues at large
4160 // scale
4161 OCPNRegion update_region;
4162
4163 int fluff = 2;
4164
4165 // Avoid rendering artifacts caused by Multi Sampling (MSAA)
4166 if (VPoint.chart_scale < 10000) fluff = 8;
4167
4168 if (dy > 0 && dy < gl_height)
4169 update_region.Union(
4170 wxRect(0, gl_height - (dy + fluff), gl_width, dy + fluff));
4171 else if (dy < 0)
4172 update_region.Union(wxRect(0, 0, gl_width, -dy + fluff));
4173
4174 if (dx > 0 && dx < gl_width)
4175 update_region.Union(
4176 wxRect(gl_width - (dx + fluff), 0, dx + fluff, gl_height));
4177 else if (dx < 0)
4178 update_region.Union(wxRect(0, 0, -dx + fluff, gl_height));
4179
4180 m_cache_page = !m_cache_page; /* page flip */
4181
4182 // Bind the destination (target frame) texture to the frame buffer
4183 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
4184 GL_TEXTURE_2D, m_cache_tex[m_cache_page], 0);
4185
4186 // Before rendering anything, clear the color buffers
4187 // wxColour color = GetGlobalColor(_T ( "NODTA" ));
4188 // glClearColor(color.Red() / 256., color.Green() / 256.,
4189 // color.Blue() / 256., 1.0);
4190 // glClear(GL_COLOR_BUFFER_BIT);
4191
4192 // First render the new content into the update region
4193 // if (g_in_inertia)
4194 // printf("--- R2a %g\n",
4195 // (wxGetLocalTimeMillis() - s_t0).ToDouble());
4196 RenderCharts(m_gldc, update_region);
4197 // if (g_in_inertia)
4198 // printf("--- R2b %g\n",
4199 // (wxGetLocalTimeMillis() - s_t0).ToDouble());
4200 glDisable(g_texture_rectangle_format);
4201 glUseProgram(0);
4202
4203 // Next, render the cached texture as quad to FBO(m_blit_tex) with
4204 // offsets
4205 glBindTexture(GL_TEXTURE_2D, m_cache_tex[!m_cache_page]);
4206 glEnable(GL_TEXTURE_2D);
4207
4208 // Blit the existing content onto the alternate FBO, at the correct
4209 // location
4210 float x1, x2, y1, y2;
4211
4212 if (dx > 0)
4213 x1 = dx, x2 = 0;
4214 else
4215 x1 = 0, x2 = -dx;
4216
4217 if (dy > 0)
4218 y1 = dy, y2 = 0;
4219 else
4220 y1 = 0, y2 = -dy;
4221
4222 // normalize to texture coordinates range from 0 to 1
4223 float tx1, tx2, ty1, ty2;
4224
4225 float xcor = 0;
4226 float ycor = 0;
4227
4228 tx1 = 0;
4229 tx2 = sx / (float)m_cache_tex_x;
4230 ty1 = 0;
4231 ty2 = sy / (float)m_cache_tex_y;
4232
4233 float coords[8];
4234 float uv[8];
4235
4236 // normal uv
4237 uv[0] = tx1;
4238 uv[1] = ty1;
4239 uv[2] = tx2;
4240 uv[3] = ty1;
4241 uv[4] = tx2;
4242 uv[5] = ty2;
4243 uv[6] = tx1;
4244 uv[7] = ty2;
4245
4246 coords[0] = -dx;
4247 coords[1] = dy;
4248 coords[2] = -dx + sx;
4249 coords[3] = dy;
4250 coords[4] = -dx + sx;
4251 coords[5] = dy + sy;
4252 coords[6] = -dx;
4253 coords[7] = dy + sy;
4254
4255 GLShaderProgram *shader =
4256 ptexture_2D_shader_program[GetCanvasIndex()];
4257 shader->Bind();
4258
4259 // Set up the texture sampler to texture unit 0
4260 shader->SetUniform1i("uTex", 0);
4261
4262 mat4x4 m, mvp, I;
4263 mat4x4_identity(m);
4264 mat4x4_scale_aniso(mvp, m, 2.0 / (float)sx, 2.0 / (float)sy, 1.0);
4265 mat4x4_translate_in_place(mvp, -(float)sx / 2, -(float)sy / 2, 0);
4266 shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)mvp);
4267 mat4x4_identity(I);
4268 shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)I);
4269
4270 float co1[8];
4271 co1[0] = coords[0];
4272 co1[1] = coords[1];
4273 co1[2] = coords[2];
4274 co1[3] = coords[3];
4275 co1[4] = coords[6];
4276 co1[5] = coords[7];
4277 co1[6] = coords[4];
4278 co1[7] = coords[5];
4279
4280 float tco1[8];
4281 tco1[0] = uv[0];
4282 tco1[1] = uv[1];
4283 tco1[2] = uv[2];
4284 tco1[3] = uv[3];
4285 tco1[4] = uv[6];
4286 tco1[5] = uv[7];
4287 tco1[6] = uv[4];
4288 tco1[7] = uv[5];
4289
4290 shader->SetAttributePointerf("aPos", co1);
4291 shader->SetAttributePointerf("aUV", tco1);
4292
4293 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
4294
4295 // restore the shader matrix
4296 shader->SetUniformMatrix4fv("MVMatrix",
4297 (GLfloat *)VPoint.vp_matrix_transform);
4298
4299 shader->UnBind();
4300 glBindTexture(g_texture_rectangle_format, 0);
4301
4302 glDisable(g_texture_rectangle_format);
4303 glUseProgram(0);
4304 }
4305
4306 } // accelerated pan
4307
4308 else { // must redraw the entire screen
4309 // qDebug() << "Fullpage";
4310 glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0,
4311 g_texture_rectangle_format,
4312 m_cache_tex[!m_cache_page], 0);
4313
4314 m_fbo_offsetx = 0;
4315 m_fbo_offsety = 0;
4316 m_fbo_swidth = sx;
4317 m_fbo_sheight = sy;
4318
4319 // FIXME (dave) test on Android
4320 // This can be annoying on Android pinch zoom
4321
4322 // Clear the screen to NODTA color
4323 wxColour color = GetGlobalColor(_T ( "NODTA" ));
4324 glClearColor(color.Red() / 256., color.Green() / 256.,
4325 color.Blue() / 256., 1.0);
4326 glClear(GL_COLOR_BUFFER_BIT);
4327
4328 OCPNRegion rscreen_region(VPoint.rv_rect);
4329 // if (g_in_inertia)
4330 // printf("--- R2c %g\n",
4331 // (wxGetLocalTimeMillis() - s_t0).ToDouble());
4332 RenderCharts(m_gldc, rscreen_region);
4333 // if (g_in_inertia)
4334 // printf("--- R2d %g\n",
4335 // (wxGetLocalTimeMillis() - s_t0).ToDouble());
4336
4337 m_cache_page = !m_cache_page; /* page flip */
4338
4339 } // full page render
4340
4341 // Disable Render to FBO
4342 glBindFramebuffer(GL_FRAMEBUFFER, 0);
4343
4344#endif // gles2 for accpan
4345
4346 } // newview
4347
4348 useFBO = true;
4349 }
4350
4351#ifndef __ANDROID__
4352 if (VPoint.tilt) {
4353 glMatrixMode(GL_PROJECTION);
4354 glLoadIdentity();
4355
4356 gluPerspective(2 * 180 / PI * atan2((double)gl_height, (double)gl_width),
4357 (GLfloat)gl_width / (GLfloat)gl_height, 1, gl_width);
4358
4359 glMatrixMode(GL_MODELVIEW);
4360 glLoadIdentity();
4361
4362 glScalef(1, -1, 1);
4363 glTranslatef(-gl_width / 2, -gl_height / 2, -gl_width / 2);
4364 glRotated(VPoint.tilt * 180 / PI, 1, 0, 0);
4365
4366 glGetIntegerv(GL_VIEWPORT, viewport);
4367 glGetDoublev(GL_PROJECTION_MATRIX, projmatrix);
4368 glGetDoublev(GL_MODELVIEW_MATRIX, mvmatrix);
4369 }
4370#endif
4371
4372 // if (g_in_inertia)
4373 // printf("--- R3 %g\n", (wxGetLocalTimeMillis() - s_t0).ToDouble());
4374
4375 if (useFBO) {
4376#if 0 // #ifndef USE_ANDROID_GLES2
4377 glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fb0);
4378 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
4379 glBlitFramebuffer(0, 0, sx, sy, 0, 0, sx*2, sy*2, GL_COLOR_BUFFER_BIT, GL_LINEAR);
4380
4381 glBindFramebuffer(GL_FRAMEBUFFER, 0);
4382
4383#else
4384 // Render the cached texture as quad to screen
4385 glBindTexture(g_texture_rectangle_format, m_cache_tex[m_cache_page]);
4386 glEnable(g_texture_rectangle_format);
4387
4388 float tx, ty, tx0, ty0, divx, divy;
4389
4390 // Normalize, or not?
4391 if (GL_TEXTURE_RECTANGLE_ARB == g_texture_rectangle_format) {
4392 divx = divy = 1.0f;
4393 } else {
4394 divx = m_cache_tex_x;
4395 divy = m_cache_tex_y;
4396 }
4397
4398 tx0 = m_fbo_offsetx / divx;
4399 ty0 = m_fbo_offsety / divy;
4400 tx = (m_fbo_offsetx + m_fbo_swidth) / divx;
4401 ty = (m_fbo_offsety + m_fbo_sheight) / divy;
4402
4403 float coords[8];
4404 float uv[8];
4405
4406 // normal uv
4407 uv[0] = tx0;
4408 uv[1] = ty;
4409 uv[2] = tx;
4410 uv[3] = ty;
4411 uv[4] = tx;
4412 uv[5] = ty0;
4413 uv[6] = tx0;
4414 uv[7] = ty0;
4415
4416 // pixels
4417 coords[0] = 0;
4418 coords[1] = 0;
4419 coords[2] = sx;
4420 coords[3] = 0;
4421 coords[4] = sx;
4422 coords[5] = sy;
4423 coords[6] = 0;
4424 coords[7] = sy;
4425
4426 wxColour color = GetGlobalColor(_T ( "NODTA" ));
4427 glClearColor(color.Red() / 256., color.Green() / 256., color.Blue() / 256.,
4428 1.0);
4429 glClear(GL_COLOR_BUFFER_BIT);
4430
4431 RenderTextures(gldc, coords, uv, 4, m_pParentCanvas->GetpVP());
4432#endif
4433
4434 glDisable(g_texture_rectangle_format);
4435
4436 m_cache_vp = VPoint;
4437 m_cache_vp.Validate();
4438
4439 m_cache_current_ch = m_pParentCanvas->m_singleChart;
4440
4441 if (VPoint.b_quilt) m_pParentCanvas->m_pQuilt->SetRenderedVP(VPoint);
4442
4443 } else // useFBO
4444 {
4445 RenderCharts(m_gldc, screen_region);
4446 }
4447
4448#if 1
4449 // Done with base charts.
4450 // Now the overlays
4451 RenderS57TextOverlay(VPoint);
4452 RenderMBTilesOverlay(VPoint);
4453
4454 // Render static overlay objects
4455 for (OCPNRegionIterator upd(screen_region); upd.HaveRects(); upd.NextRect()) {
4456 wxRect rt = upd.GetRect();
4457 LLRegion region = VPoint.GetLLRegion(rt);
4458 ViewPort cvp = ClippedViewport(VPoint, region);
4459 DrawGroundedOverlayObjects(gldc, cvp);
4460 }
4461
4462 if (m_pParentCanvas->m_bShowTide || m_pParentCanvas->m_bShowCurrent) {
4463 LLRegion screenLLRegion = VPoint.GetLLRegion(screen_region);
4464 LLBBox screenBox = screenLLRegion.GetBox();
4465 // Enlarge the box a bit
4466 screenBox.EnLarge(screenBox.GetLonRange() * 0.05);
4467
4468 // update the tide/current select points, if necessary
4469 if (m_pParentCanvas->m_bShowTide) {
4470 m_pParentCanvas->RebuildTideSelectList(screenBox); // full screen
4471 DrawGLTidesInBBox(gldc, VPoint.GetBBox());
4472 }
4473
4474 if (m_pParentCanvas->m_bShowCurrent) {
4475 m_pParentCanvas->RebuildCurrentSelectList(screenBox);
4476 DrawGLCurrentsInBBox(gldc, VPoint.GetBBox());
4477 }
4478 }
4479
4480 // If multi-canvas, indicate which canvas has keyboard focus
4481 // by drawing a simple blue bar at the top.
4482 if (m_pParentCanvas->m_show_focus_bar &&
4483 (g_canvasConfig != 0)) { // multi-canvas?
4484 if (m_pParentCanvas == wxWindow::FindFocus()) {
4485 g_focusCanvas = m_pParentCanvas;
4486
4487 wxColour colour = GetGlobalColor(_T("BLUE4"));
4488 wxPen ppBlue(colour, 1);
4489 wxBrush ppBrush(colour);
4490 gldc.SetPen(ppBlue);
4491 gldc.SetBrush(ppBrush);
4492 int xw = m_pParentCanvas->GetClientSize().x * m_displayScale;
4493 float rect_pix = m_pParentCanvas->m_focus_indicator_pix * m_displayScale;
4494 wxPoint barPoints[4];
4495 barPoints[0].x = 0;
4496 barPoints[0].y = 0;
4497 barPoints[1].x = xw;
4498 barPoints[1].y = 0;
4499 barPoints[2].x = xw;
4500 barPoints[2].y = rect_pix;
4501 barPoints[3].x = 0;
4502 barPoints[3].y = rect_pix;
4503
4504 gldc.DrawPolygon(4, barPoints, 0, 0, 1, 0);
4505 }
4506 }
4507
4508 DrawDynamicRoutesTracksAndWaypoints(VPoint);
4509
4510 // Now draw all the objects which normally move around and are not
4511 // cached from the previous frame
4512 DrawFloatingOverlayObjects(m_gldc);
4513
4514#ifndef USE_ANDROID_GLES2
4515 // from this point on don't use perspective
4516 if (VPoint.tilt) {
4517 glMatrixMode(GL_PROJECTION);
4518 glLoadIdentity();
4519
4520 glOrtho(0, (GLint)gl_width, (GLint)gl_height, 0, -1, 1);
4521 glMatrixMode(GL_MODELVIEW);
4522 glLoadIdentity();
4523 }
4524#endif
4525
4526 DrawEmboss(m_gldc, m_pParentCanvas->EmbossDepthScale());
4527 DrawEmboss(m_gldc, m_pParentCanvas->EmbossOverzoomIndicator(gldc));
4528
4529 if (g_pi_manager) {
4530 ViewPort &vp = m_pParentCanvas->GetVP();
4531 g_pi_manager->SendViewPortToRequestingPlugIns(vp);
4532 g_pi_manager->RenderAllGLCanvasOverlayPlugIns(
4533 m_pcontext, vp, m_pParentCanvas->m_canvasIndex, OVERLAY_OVER_EMBOSS);
4534 }
4535 if (!g_PrintingInProgress) {
4536 if (m_pParentCanvas->m_pTrackRolloverWin)
4537 m_pParentCanvas->m_pTrackRolloverWin->Draw(gldc);
4538
4539 if (m_pParentCanvas->m_pRouteRolloverWin)
4540 m_pParentCanvas->m_pRouteRolloverWin->Draw(gldc);
4541
4542 if (m_pParentCanvas->m_pAISRolloverWin)
4543 m_pParentCanvas->m_pAISRolloverWin->Draw(gldc);
4544
4545 if (m_pParentCanvas->GetMUIBar())
4546 m_pParentCanvas->GetMUIBar()->DrawGL(gldc, m_displayScale);
4547
4548 if (g_MainToolbar && m_pParentCanvas->IsPrimaryCanvas())
4549 g_MainToolbar->DrawGL(gldc, m_displayScale);
4550
4551 if (g_iENCToolbar && m_pParentCanvas->IsPrimaryCanvas())
4552 g_iENCToolbar->DrawGL(gldc, m_displayScale);
4553 }
4554
4555 // On some platforms, the opengl context window is always on top of any
4556 // standard DC windows, so we need to draw the Chart Info Window
4557 // as overlayed bmps.
4558
4559#ifdef __WXOSX__
4560 if (m_pParentCanvas->m_pCIWin && m_pParentCanvas->m_pCIWin->IsShown()) {
4561 int x, y, width, height;
4562 m_pParentCanvas->m_pCIWin->GetClientSize(&width, &height);
4563 m_pParentCanvas->m_pCIWin->GetPosition(&x, &y);
4564 wxBitmap bmp(width, height, -1);
4565 wxMemoryDC dc(bmp);
4566 if (bmp.IsOk()) {
4567 dc.SetBackground(wxBrush(GetGlobalColor(_T ( "UIBCK" ))));
4568 dc.Clear();
4569
4570 dc.SetTextBackground(GetGlobalColor(_T ( "UIBCK" )));
4571 dc.SetTextForeground(GetGlobalColor(_T ( "UITX1" )));
4572
4573 int yt = 0;
4574 int xt = 0;
4575 wxString s = m_pParentCanvas->m_pCIWin->GetString();
4576 int h = m_pParentCanvas->m_pCIWin->GetCharHeight();
4577
4578 wxStringTokenizer tkz(s, _T("\n"));
4579 wxString token;
4580
4581 while (tkz.HasMoreTokens()) {
4582 token = tkz.GetNextToken();
4583 dc.DrawText(token, xt, yt);
4584 yt += h;
4585 }
4586 dc.SelectObject(wxNullBitmap);
4587
4588 m_gldc.DrawBitmap(bmp, x, y, false);
4589 }
4590 }
4591
4592#endif
4593 // render the chart bar
4594 if (g_bShowChartBar) DrawChartBar(m_gldc);
4595
4596 if (m_pParentCanvas->m_Compass && m_pParentCanvas->m_bShowCompassWin &&
4597 g_bShowCompassWin)
4598 m_pParentCanvas->m_Compass->Paint(gldc);
4599
4600 RenderGLAlertMessage();
4601#endif
4602
4603 if (g_pi_manager) {
4604 ViewPort &vp = m_pParentCanvas->GetVP();
4605 g_pi_manager->SendViewPortToRequestingPlugIns(vp);
4606 g_pi_manager->RenderAllGLCanvasOverlayPlugIns(
4607 m_pcontext, vp, m_pParentCanvas->m_canvasIndex, OVERLAY_OVER_UI);
4608 glActiveTexture(GL_TEXTURE0);
4609 }
4610
4611 // quiting?
4612 if (g_bquiting) DrawQuiting();
4613 if (g_bcompression_wait)
4614 DrawCloseMessage(_("Waiting for raster chart compression thread exit."));
4615
4616 // Some older MSW OpenGL drivers are generally very unstable.
4617 // This helps...
4618
4619 if (g_b_needFinish) glFinish();
4620
4621 SwapBuffers();
4622
4623 g_glTextureManager->TextureCrunch(0.8);
4624 g_glTextureManager->FactoryCrunch(0.6);
4625
4626 // if (g_in_inertia)
4627 // printf("--- Rx %g\n", (wxGetLocalTimeMillis() - s_t0).ToDouble());
4628
4629 m_pParentCanvas->PaintCleanup();
4630 // OCPNPlatform::HideBusySpinner();
4631 m_bforcefull = false;
4632
4633 n_render++;
4634}
4635
4636void glChartCanvas::RenderS57TextOverlay(ViewPort &VPoint) {
4637 // Render the decluttered Text overlay for quilted vector charts, except for
4638 // CM93 Composite
4639 if (VPoint.b_quilt) {
4640 if (m_pParentCanvas->m_pQuilt->IsQuiltVector() && ps52plib &&
4641 ps52plib->GetShowS57Text()) {
4642 ChartBase *chart = m_pParentCanvas->m_pQuilt->GetRefChart();
4643 if (chart && (chart->GetChartType() != CHART_TYPE_CM93COMP)) {
4644 // Clear the text Global declutter list
4645 if (chart) {
4646 ChartPlugInWrapper *ChPI = dynamic_cast<ChartPlugInWrapper *>(chart);
4647 if (ChPI)
4648 ChPI->ClearPLIBTextList();
4649 else
4650 ps52plib->ClearTextList();
4651 }
4652
4653 // Grow the ViewPort a bit laterally, to minimize "jumping" of text
4654 // elements at left side of screen
4655 ViewPort vpx = VPoint;
4656 vpx.BuildExpandedVP(VPoint.pix_width * 12 / 10, VPoint.pix_height);
4657
4658 OCPNRegion screen_region(
4659 wxRect(0, 0, VPoint.pix_width, VPoint.pix_height));
4660 RenderQuiltViewGLText(vpx, screen_region);
4661 }
4662 }
4663 }
4664}
4665void glChartCanvas::RenderSingleMBTileOverlay(const int dbIndex, bool bOverlay,
4666 ViewPort &vp,
4667 OCPNRegion &screen_region,
4668 LLRegion &screenLLRegion) {
4669 ChartBase *chart = ChartData->OpenChartFromDBAndLock(dbIndex, FULL_INIT);
4670
4671 // Chart may have been prevented from initial loading due to size, or some
4672 // other reason...
4673 if (chart == NULL) return;
4674
4675 ChartMbTiles *pcmbt = dynamic_cast<ChartMbTiles *>(chart);
4676 if (!pcmbt) return;
4677
4678 // Is tile an OVERLAY type?
4679 // Render, or not, depending on passed flag.
4680 if (bOverlay && pcmbt->GetTileType() != MbTilesType::OVERLAY) return;
4681
4682 wxFileName tileFile(chart->GetFullPath());
4683 // Size test for 5 GByte
4684 wxULongLong tileSizeMB = tileFile.GetSize() >> 20;
4685
4686 if (!ChartData->CheckAnyCanvasExclusiveTileGroup() ||
4687 (tileSizeMB.GetLo() > 5000)) {
4688 // Check to see if the tile has been "clicked".
4689 // If so, do not add to no-show array again.
4690 if (!m_pParentCanvas->IsTileOverlayIndexInYesShow(dbIndex)) {
4691 if (!m_pParentCanvas->IsTileOverlayIndexInNoShow(dbIndex)) {
4692 m_pParentCanvas->m_tile_noshow_index_array.push_back(dbIndex);
4693 }
4694 }
4695 }
4696
4697 // This test catches the case where the chart is added to no_show list
4698 // when first loaded by OpenChartFromDBAndLock
4699 if (m_pParentCanvas->IsTileOverlayIndexInNoShow(dbIndex)) {
4700 return;
4701 }
4702
4703 pcmbt->RenderRegionViewOnGL(*m_pcontext, vp, screen_region, screenLLRegion);
4704
4705 // Light up the piano key if the chart was rendered
4706 std::vector<int> piano_active_array_tiles =
4707 m_pParentCanvas->m_Piano->GetActiveKeyArray();
4708 bool bfound = false;
4709
4710 if (std::find(piano_active_array_tiles.begin(),
4711 piano_active_array_tiles.end(),
4712 dbIndex) != piano_active_array_tiles.end()) {
4713 bfound = true;
4714 }
4715
4716 if (!bfound) {
4717 piano_active_array_tiles.push_back(dbIndex);
4718 m_pParentCanvas->m_Piano->SetActiveKeyArray(piano_active_array_tiles);
4719 }
4720}
4721
4722void glChartCanvas::RenderMBTilesOverlay(ViewPort &VPoint) {
4723 // Render MBTiles as overlay
4724 std::vector<int> stackIndexArray =
4725 m_pParentCanvas->m_pQuilt->GetExtendedStackIndexArray();
4726 unsigned int im = stackIndexArray.size();
4727 // XXX should
4728 // assert(!VPoint.b_quilt && im == 0)
4729 if (VPoint.b_quilt && im > 0) {
4730 bool regionVPBuilt = false;
4731 OCPNRegion screen_region;
4732 LLRegion screenLLRegion;
4733 LLBBox screenBox;
4734 ViewPort vp;
4735
4736 std::vector<int> tiles_to_show;
4737 for (unsigned int is = 0; is < im; is++) {
4738 const ChartTableEntry &cte =
4739 ChartData->GetChartTableEntry(stackIndexArray[is]);
4740 if (cte.GetChartType() == CHART_TYPE_MBTILES) {
4741 if (m_pParentCanvas->IsTileOverlayIndexInNoShow(stackIndexArray[is])) {
4742 // Turn off the piano highlite
4743 std::vector<int> piano_active_array_tiles =
4744 m_pParentCanvas->m_Piano->GetActiveKeyArray();
4745 bool bfound = false;
4746
4747 for (unsigned int i = 0; i < piano_active_array_tiles.size(); i++) {
4748 if (piano_active_array_tiles[i] == stackIndexArray[is]) {
4749 piano_active_array_tiles.erase(piano_active_array_tiles.begin() +
4750 i); // erase it
4751 bfound = true;
4752 break;
4753 }
4754 }
4755
4756 if (bfound)
4757 m_pParentCanvas->m_Piano->SetActiveKeyArray(
4758 piano_active_array_tiles);
4759
4760 continue;
4761 }
4762
4763 tiles_to_show.push_back(stackIndexArray[is]);
4764 if (!regionVPBuilt) {
4765 screen_region =
4766 OCPNRegion(wxRect(0, 0, VPoint.pix_width, VPoint.pix_height));
4767 screenLLRegion = VPoint.GetLLRegion(screen_region);
4768 screenBox = screenLLRegion.GetBox();
4769
4770 vp = VPoint;
4771 wxPoint p;
4772 p.x = VPoint.pix_width / 2;
4773 p.y = VPoint.pix_height / 2;
4774 VPoint.GetLLFromPix(p, &vp.clat, &vp.clon);
4775
4776 regionVPBuilt = true;
4777 }
4778 }
4779 }
4780
4781 // Render in two passes, to render the OVERLAY types last
4782
4783 // Show the tilesets in reverse order to have the largest scale
4784 // on top
4785
4786 for (std::vector<int>::reverse_iterator rit = tiles_to_show.rbegin();
4787 rit != tiles_to_show.rend(); ++rit) {
4788 RenderSingleMBTileOverlay(*rit, FALSE, vp, screen_region, screenLLRegion);
4789 }
4790 for (std::vector<int>::reverse_iterator rit = tiles_to_show.rbegin();
4791 rit != tiles_to_show.rend(); ++rit) {
4792 RenderSingleMBTileOverlay(*rit, TRUE, vp, screen_region, screenLLRegion);
4793 }
4794
4795 // Render the HiLite on piano rollover of mbTile key
4796 LLRegion hiregion = m_pParentCanvas->m_pQuilt->GetHiliteRegion();
4797
4798 if (!hiregion.Empty()) {
4799 glEnable(GL_BLEND);
4800
4801 double hitrans;
4802 switch (global_color_scheme) {
4803 case GLOBAL_COLOR_SCHEME_DAY:
4804 hitrans = .4;
4805 break;
4806 case GLOBAL_COLOR_SCHEME_DUSK:
4807 hitrans = .2;
4808 break;
4809 case GLOBAL_COLOR_SCHEME_NIGHT:
4810 hitrans = .1;
4811 break;
4812 default:
4813 hitrans = .4;
4814 break;
4815 }
4816
4817#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
4818 glColor4f((float).8, (float).4, (float).4, (float)hitrans);
4819#else
4820 s_regionColor = wxColor(204, 102, 102, hitrans * 256);
4821#endif
4822
4823 DrawRegion(VPoint, hiregion);
4824
4825 glDisable(GL_BLEND);
4826 }
4827 }
4828}
4829
4830#if 0
4831void glChartCanvas::RenderCanvasBackingChart(ocpnDC &dc,
4832 OCPNRegion valid_region) {
4833 // Fill the FBO with the current gshhs world chart
4834 int w, h;
4835 GetClientSize(&w, &h);
4836
4837 glViewport(0, 0, (GLint)m_cache_tex_x, (GLint)m_cache_tex_y);
4838#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
4839 glMatrixMode(GL_PROJECTION);
4840 glLoadIdentity();
4841
4842 glOrtho(0, m_cache_tex_x, m_cache_tex_y, 0, -1, 1);
4843 glMatrixMode(GL_MODELVIEW);
4844 glLoadIdentity();
4845#endif
4846
4847 wxRect rtex(0, 0, m_cache_tex_x, m_cache_tex_y);
4848 ViewPort cvp =
4849 m_pParentCanvas->GetVP().BuildExpandedVP(m_cache_tex_x, m_cache_tex_y);
4850
4851 bool world_view = false;
4852 RenderWorldChart(dc, cvp, rtex, world_view);
4853 gShapeBasemap.RenderViewOnDC(dc, cvp);
4854
4855 // dc.SetPen(wxPen(wxColour(254,254,0), 3));
4856 // dc.DrawLine( 0, 0, m_cache_tex_x, m_cache_tex_y);
4857
4858 // Reset matrices
4859 glViewport(0, 0, (GLint)w, (GLint)h);
4860#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
4861 glMatrixMode(GL_PROJECTION);
4862 glLoadIdentity();
4863
4864 glOrtho(0, (GLint)w, (GLint)h, 0, -1, 1);
4865 glMatrixMode(GL_MODELVIEW);
4866 glLoadIdentity();
4867#endif
4868}
4869#endif
4870
4871void glChartCanvas::FastPan(int dx, int dy) {
4872#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
4873#endif
4874}
4875
4876void glChartCanvas::ZoomProject(float offset_x, float offset_y, float swidth,
4877 float sheight) {
4878 SetCurrent(*m_pcontext);
4879 float sx = GetSize().x;
4880 float sy = GetSize().y;
4881 glClear(GL_COLOR_BUFFER_BIT);
4882
4883 int w, h;
4884 GetClientSize(&w, &h);
4885
4886 if (s_b_useStencil) {
4887 glEnable(GL_STENCIL_TEST);
4888 glStencilMask(0xff);
4889 glClear(GL_STENCIL_BUFFER_BIT);
4890 glDisable(GL_STENCIL_TEST);
4891 }
4892
4893#ifndef __ANDROID__
4894 // Render backing texture
4895 if (1) {
4896 float tx0 = 0;
4897 float ty0 = 0;
4898 float tx = sx;
4899 float ty = sy;
4900
4901 float vx0 = 0;
4902 float vy0 = 0;
4903 float vx = sx;
4904 float vy = sy;
4905
4906 float sxfactor = sx / swidth;
4907 float syfactor = sy / sheight;
4908
4909 glViewport(-offset_x * sx / swidth - (sx * sxfactor / 2),
4910 -offset_y * (sy / sheight) - (sy * syfactor / 2),
4911 sx * sx / swidth * 2, sy * sy / sheight * 2);
4912 glBindTexture(g_texture_rectangle_format, m_TouchBackingTexture);
4913 glEnable(g_texture_rectangle_format);
4914
4915 float uv[8];
4916 float coords[8];
4917
4918 // normal uv Texture coordinates
4919 uv[0] = 0;
4920 uv[1] = 0;
4921 uv[2] = 1;
4922 uv[3] = 0;
4923 uv[4] = 1;
4924 uv[5] = 1;
4925 uv[6] = 0;
4926 uv[7] = 1;
4927
4928 // pixels
4929 coords[0] = vx0;
4930 coords[1] = vy0;
4931 coords[2] = vx;
4932 coords[3] = vy0;
4933 coords[4] = vx;
4934 coords[5] = vy;
4935 coords[6] = vx0;
4936 coords[7] = vy;
4937
4938 RenderTextures(m_gldc, coords, uv, 4, &m_texVP);
4939 glBindTexture(g_texture_rectangle_format, 0);
4940 }
4941#endif
4942
4943 // render zoomed canvas section
4944 if (1) {
4945 float tx, ty, tx0, ty0;
4946 tx = sx, ty = sy;
4947
4948 tx0 = ty0 = 0.;
4949
4950 float vx0 = 0;
4951 float vy0 = 0;
4952 float vy = sy;
4953 float vx = sx;
4954
4955 glBindTexture(g_texture_rectangle_format, 0);
4956
4957 // Render the cached texture as quad to screen
4958 glBindTexture(g_texture_rectangle_format, m_cache_tex[m_cache_page]);
4959 glEnable(g_texture_rectangle_format);
4960
4961 float uv[8];
4962 float coords[8];
4963
4964 // normal uv Texture coordinates
4965 uv[0] = tx0 / m_cache_tex_x;
4966 uv[1] = ty / m_cache_tex_y;
4967 uv[2] = tx / m_cache_tex_x;
4968 uv[3] = ty / m_cache_tex_y;
4969 uv[4] = tx / m_cache_tex_x;
4970 uv[5] = ty0 / m_cache_tex_y;
4971 uv[6] = tx0 / m_cache_tex_x;
4972 uv[7] = ty0 / m_cache_tex_y;
4973
4974 // pixels
4975 coords[0] = vx0;
4976 coords[1] = vy0;
4977 coords[2] = vx;
4978 coords[3] = vy0;
4979 coords[4] = vx;
4980 coords[5] = vy;
4981 coords[6] = vx0;
4982 coords[7] = vy;
4983
4984 glViewport(-offset_x * sx / swidth, -offset_y * (sy / sheight),
4985 sx * sx / swidth, sy * sy / sheight);
4986
4987 RenderTextures(m_gldc, coords, uv, 4, m_pParentCanvas->GetpVP());
4988
4989 glDisable(g_texture_rectangle_format);
4990 glBindTexture(g_texture_rectangle_format, 0);
4991 }
4992
4993#if 0
4994 // For fun, we prove the coordinates of the blank area outside the chart when
4995 // zooming out. Bottom stripe
4996 // wxColour color = GetGlobalColor(_T("YELO1")); //GREY1
4997 wxColour color = GetGlobalColor(_T("GREY1")); //
4998 float ht = -offset_y * (sy / sheight);
4999 wxRect r(0, sy - ht, w, ht);
5000 RenderColorRect(r, color);
5001
5002 // top stripe
5003 wxRect rt(0, 0, w, sy - (ht + (sy * sy / sheight)));
5004 RenderColorRect(rt, color);
5005
5006 // left
5007 float w1 = -offset_x * sx / swidth;
5008 wxRect rl(0, 0, w1, sy);
5009 RenderColorRect(rl, color);
5010
5011 // right
5012 float px = w1 + sx * sx / swidth;
5013 wxRect rr(px, 0, sx - px, sy);
5014 RenderColorRect(rr, color);
5015#endif
5016 // When zooming out, if we go too far, then the frame buffer is repeated
5017 // on-screen due to address wrapping in the frame buffer. Detect this case,
5018 // and render some simple solid covering quads to avoid a confusing display.
5019
5020 SwapBuffers();
5021}
5022
5023void glChartCanvas::onZoomTimerEvent(wxTimerEvent &event) {
5024 // If m_zoomFinal is set, stop the timer.
5025 if (!m_zoomFinal) {
5026 if (m_nRun < m_nTotal) {
5027 m_runoffsetx += m_offsetxStep;
5028 if (m_offsetxStep > 0)
5029 m_runoffsetx = wxMin(m_runoffsetx, m_fbo_offsetx);
5030 else
5031 m_runoffsetx = wxMax(m_runoffsetx, m_fbo_offsetx);
5032
5033 m_runoffsety += m_offsetyStep;
5034 if (m_offsetyStep > 0)
5035 m_runoffsety = wxMin(m_runoffsety, m_fbo_offsety);
5036 else
5037 m_runoffsety = wxMax(m_runoffsety, m_fbo_offsety);
5038
5039 m_runswidth += m_swidthStep;
5040 if (m_swidthStep > 0)
5041 m_runswidth = wxMin(m_runswidth, m_fbo_swidth);
5042 else
5043 m_runswidth = wxMax(m_runswidth, m_fbo_swidth);
5044
5045 m_runsheight += m_sheightStep;
5046 if (m_sheightStep > 0)
5047 m_runsheight = wxMin(m_runsheight, m_fbo_sheight);
5048 else
5049 m_runsheight = wxMax(m_runsheight, m_fbo_sheight);
5050
5051 m_nRun += m_nStep;
5052 }
5053
5054 ZoomProject(m_runoffsetx, m_runoffsety, m_runswidth, m_runsheight);
5055
5056 } else {
5057 zoomTimer.Stop();
5058 if (m_zoomFinal) {
5059 m_pParentCanvas->ZoomCanvasSimple(m_zoomFinalZoom);
5060 if (m_zoomFinaldx || m_zoomFinaldy) {
5061 m_pParentCanvas->PanCanvas(m_zoomFinaldx, m_zoomFinaldy);
5062 }
5063 }
5064 m_zoomFinal = false;
5065 }
5066}
5067
5068void glChartCanvas::FastZoom(float factor, float cp_x, float cp_y, float post_x,
5069 float post_y) {
5070 int sx = GetSize().x;
5071 int sy = GetSize().y;
5072
5073 m_lastfbo_offsetx = m_fbo_offsetx;
5074 m_lastfbo_offsety = m_fbo_offsety;
5075 m_lastfbo_swidth = m_fbo_swidth;
5076 m_lastfbo_sheight = m_fbo_sheight;
5077
5078 float curr_fbo_offset_x = m_fbo_offsetx;
5079 float curr_fbo_offset_y = m_fbo_offsety;
5080 float curr_fbo_swidth = m_fbo_swidth;
5081 float curr_fbo_sheight = m_fbo_sheight;
5082
5083 float fx = (float)cp_x / sx;
5084 float fy = 1.0 - (float)cp_y / sy;
5085
5086 float fbo_ctr_x = curr_fbo_offset_x + (curr_fbo_swidth * fx);
5087 float fbo_ctr_y = curr_fbo_offset_y + (curr_fbo_sheight * fy);
5088
5089 m_fbo_swidth = curr_fbo_swidth / factor;
5090 m_fbo_sheight = curr_fbo_sheight / factor;
5091
5092 m_fbo_offsetx = fbo_ctr_x - (m_fbo_swidth * fx);
5093 m_fbo_offsety = fbo_ctr_y - (m_fbo_sheight * fy);
5094
5095 m_fbo_offsetx += post_x;
5096 m_fbo_offsety += post_y;
5097
5098 {
5099 m_nStep = 20;
5100 m_nTotal = 100;
5101
5102 // m_nStep = 10; // Android?
5103 // m_nTotal = 40;
5104
5105 m_nRun = 0;
5106
5107 float perStep = m_nStep / m_nTotal;
5108
5109 if (zoomTimer.IsRunning()) {
5110 m_offsetxStep = (m_fbo_offsetx - m_runoffsetx) * perStep;
5111 m_offsetyStep = (m_fbo_offsety - m_runoffsety) * perStep;
5112 m_swidthStep = (m_fbo_swidth - m_runswidth) * perStep;
5113 m_sheightStep = (m_fbo_sheight - m_runsheight) * perStep;
5114
5115 } else {
5116 m_offsetxStep = (m_fbo_offsetx - m_lastfbo_offsetx) * perStep;
5117 m_offsetyStep = (m_fbo_offsety - m_lastfbo_offsety) * perStep;
5118 m_swidthStep = (m_fbo_swidth - m_lastfbo_swidth) * perStep;
5119 m_sheightStep = (m_fbo_sheight - m_lastfbo_sheight) * perStep;
5120
5121 m_runoffsetx = m_lastfbo_offsetx;
5122 m_runoffsety = m_lastfbo_offsety;
5123 m_runswidth = m_lastfbo_swidth;
5124 m_runsheight = m_lastfbo_sheight;
5125 }
5126
5127 if (!zoomTimer.IsRunning()) zoomTimer.Start(m_nStep);
5128 m_zoomFinal = false;
5129 }
5130}
5131
5132#ifdef __ANDROID__
5133
5134void glChartCanvas::OnEvtPanGesture(wxQT_PanGestureEvent &event) {
5135 // qDebug() << "OnEvtPanGesture" << m_pParentCanvas->m_canvasIndex <<
5136 // event.cursor_pos.x;
5137
5138 if (m_pParentCanvas->isRouteEditing() || m_pParentCanvas->isMarkEditing())
5139 return;
5140
5141 if (m_binPinch) return;
5142 if (m_bpinchGuard) return;
5143
5144 int x = event.GetOffset().x;
5145 int y = event.GetOffset().y;
5146
5147 int lx = event.GetLastOffset().x;
5148 int ly = event.GetLastOffset().y;
5149
5150 int dx = lx - x;
5151 int dy = y - ly;
5152
5153 switch (event.GetState()) {
5154 case GestureStarted:
5155 if (m_binPan) break;
5156
5157 panx = pany = 0;
5158 m_binPan = true;
5159 m_binGesture = true;
5160 // qDebug() << "pg1";
5161 break;
5162
5163 case GestureUpdated:
5164 if (m_binPan) {
5165 if (!g_GLOptions.m_bUseCanvasPanning) {
5166 // qDebug() << "slowpan" << dx << dy;
5167
5168 m_pParentCanvas->FreezePiano();
5169 m_pParentCanvas->PanCanvas(dx, -dy);
5170 m_pParentCanvas->ThawPiano();
5171
5172 } else {
5173 FastPan(dx, dy);
5174 }
5175
5176 panx -= dx;
5177 pany -= dy;
5178 }
5179 break;
5180
5181 case GestureFinished:
5182 // qDebug() << "panGestureFinished";
5183
5184 m_pParentCanvas->UpdateCanvasControlBar();
5185
5186 m_binPan = false;
5187 m_gestureFinishTimer.Start(500, wxTIMER_ONE_SHOT);
5188
5189 break;
5190
5191 case GestureCanceled:
5192 m_binPan = false;
5193 m_gestureFinishTimer.Start(500, wxTIMER_ONE_SHOT);
5194 break;
5195
5196 default:
5197 break;
5198 }
5199
5200 m_bgestureGuard = true;
5201 m_gestureEeventTimer.Start(500, wxTIMER_ONE_SHOT);
5202 m_bforcefull = false;
5203
5204 // qDebug() << "panGestureDone";
5205}
5206
5207float zoom_inc = 1.0;
5208bool first_zout = false;
5209
5210void glChartCanvas::OnEvtPinchGesture(wxQT_PinchGestureEvent &event) {
5211 float zoom_gain = 1.0;
5212 float zout_gain = 1.0;
5213
5214 float zoom_val;
5215 float total_zoom_val;
5216
5217 float max_zoom_scale = 1000.;
5218 float min_zoom_scale = 2e8;
5219
5220 if (event.GetScaleFactor() > 1)
5221 zoom_val = ((event.GetScaleFactor() - 1.0) * zoom_gain) + 1.0;
5222 else
5223 zoom_val = 1.0 - ((1.0 - event.GetScaleFactor()) * zout_gain);
5224
5225 if (event.GetTotalScaleFactor() > 1)
5226 total_zoom_val = ((event.GetTotalScaleFactor() - 1.0) * zoom_gain) + 1.0;
5227 else
5228#if 0
5229 total_zoom_val = 1.0 - ((1.0 - event.GetTotalScaleFactor()) * zout_gain);
5230
5231 double projected_scale = cc1->GetVP().chart_scale / total_zoom_val;
5232
5233 // Max zoom in is set by scale of quilt reference chart, consistent with chart render limits set elsewhere.
5234 float max_zoom_scale = 1000.;
5235 if( cc1->GetVP().b_quilt) {
5236 int ref_index = cc1->GetQuiltRefChartdbIndex();
5237// if((ref_index >= 0) && ChartData){
5238// max_zoom_scale = ChartData->GetDBChartScale(ref_index) / 8.0;
5239// }
5240 }
5241
5242
5243 float min_zoom_scale = 2e8;
5244
5245#endif
5246
5247 total_zoom_val = 1.0 - ((1.0 - event.GetTotalScaleFactor()) * zoom_gain);
5248
5249 double projected_scale =
5250 m_pParentCanvas->GetVP().chart_scale / total_zoom_val;
5251
5252 switch (event.GetState()) {
5253 case GestureStarted:
5254 first_zout = false;
5255 m_binPinch = true;
5256 m_binPan = false; // cancel any tentative pan gesture, in case the "pan
5257 // cancel" event was lost
5258 m_binGesture = true;
5259 // qDebug() << "pg2";
5260 m_pinchStart = event.GetCenterPoint();
5261 m_lpinchPoint = m_pinchStart;
5262
5263 m_pParentCanvas->GetCanvasPixPoint(event.GetCenterPoint().x,
5264 event.GetCenterPoint().y, m_pinchlat,
5265 m_pinchlon);
5266 // qDebug() << "center" << event.GetCenterPoint().x <<
5267 // event.GetCenterPoint().y;
5268
5269 m_cc_x = m_fbo_offsetx + (m_fbo_swidth / 2);
5270 m_cc_y = m_fbo_offsety + (m_fbo_sheight / 2);
5271
5272 // Render the full charts with overlay objects onto the frame buffer.
5273 SetCurrent(*m_pcontext);
5274 RenderScene();
5275
5276 zoom_inc = 1.0;
5277 break;
5278
5279 case GestureUpdated:
5280 if (g_GLOptions.m_bUseCanvasPanning) {
5281 if (projected_scale < min_zoom_scale) {
5282 wxPoint pinchPoint = event.GetCenterPoint();
5283
5284 float dx = pinchPoint.x - m_lpinchPoint.x;
5285 float dy = pinchPoint.y - m_lpinchPoint.y;
5286
5287 FastZoom(zoom_val, m_pinchStart.x, m_pinchStart.y,
5288 -dx / total_zoom_val, dy / total_zoom_val);
5289
5290 m_lpinchPoint = pinchPoint;
5291 }
5292 } else {
5293 // qDebug() << "update totalzoom" << total_zoom_val << projected_scale;
5294 if (1 || ((total_zoom_val > 1) && !first_zout)) { // Zoom in
5295 wxPoint pinchPoint = event.GetCenterPoint();
5296
5297 float dx = pinchPoint.x - m_lpinchPoint.x;
5298 float dy = pinchPoint.y - m_lpinchPoint.y;
5299
5300 if ((projected_scale > max_zoom_scale) &&
5301 (projected_scale < min_zoom_scale))
5302 FastZoom(zoom_val, m_pinchStart.x, m_pinchStart.y,
5303 -dx / total_zoom_val, dy / total_zoom_val);
5304
5305 m_lpinchPoint = pinchPoint;
5306
5307 } else {
5308 first_zout = true;
5309 zoom_inc *= zoom_val;
5310 if ((zoom_inc < 0.9) || (zoom_inc > 1.1)) {
5311 m_pParentCanvas->ZoomCanvas(zoom_inc, false);
5312 zoom_inc = 1.0;
5313 }
5314
5315 wxPoint pinchPoint = event.GetCenterPoint();
5316 float dx = pinchPoint.x - m_lpinchPoint.x;
5317 float dy = pinchPoint.y - m_lpinchPoint.y;
5318 m_pParentCanvas->PanCanvas(-dx, -dy);
5319 m_lpinchPoint = pinchPoint;
5320
5321 // SetCurrent(*m_pcontext);
5322 // RenderScene();
5323 // g_Piano->DrawGL(cc1->m_canvas_height -
5324 // g_Piano->GetHeight()); SwapBuffers();
5325 }
5326 }
5327
5328 break;
5329
5330 case GestureFinished: {
5331 // qDebug() << "finish totalzoom" << total_zoom_val <<
5332 // projected_scale;
5333
5334 float cc_x = m_fbo_offsetx + (m_fbo_swidth / 2);
5335 float cc_y = m_fbo_offsety + (m_fbo_sheight / 2);
5336 float dy = 0;
5337 float dx = 0;
5338
5339 float tzoom = total_zoom_val;
5340
5341 if (projected_scale >= min_zoom_scale)
5342 tzoom = m_pParentCanvas->GetVP().chart_scale / min_zoom_scale;
5343
5344 if (projected_scale < max_zoom_scale)
5345 tzoom = m_pParentCanvas->GetVP().chart_scale / max_zoom_scale;
5346
5347 dx = (cc_x - m_cc_x) * tzoom;
5348 dy = -(cc_y - m_cc_y) * tzoom;
5349
5350 if (zoomTimer.IsRunning()) {
5351 // qDebug() << "Final zoom";
5352 m_zoomFinal = true;
5353 m_zoomFinalZoom = tzoom;
5354 m_zoomFinaldx = dx;
5355 m_zoomFinaldy = dy;
5356 }
5357
5358 else {
5359 double final_projected_scale =
5360 m_pParentCanvas->GetVP().chart_scale / tzoom;
5361 // qDebug() << "Final pinchB" << tzoom << final_projected_scale;
5362
5363 if (final_projected_scale < min_zoom_scale) {
5364 // qDebug() << "zoomit";
5365 m_pParentCanvas->ZoomCanvas(tzoom, false);
5366 m_pParentCanvas->PanCanvas(dx, dy);
5367 m_pParentCanvas->m_pQuilt->Invalidate();
5368 m_bforcefull = true;
5369
5370 } else {
5371 double new_scale =
5372 m_pParentCanvas->GetCanvasScaleFactor() / min_zoom_scale;
5373 // qDebug() << "clampit";
5374 m_pParentCanvas->SetVPScale(new_scale);
5375 m_pParentCanvas->m_pQuilt->Invalidate();
5376 m_bforcefull = true;
5377 }
5378 }
5379
5380 // if( projected_scale < 3e8)
5381 // m_pParentCanvas->ZoomCanvas( total_zoom_val, false
5382 // );
5383 // else
5384 // m_pParentCanvas->ZoomCanvas(m_pParentCanvas->GetVP().chart_scale
5385 // / 3e8, false);
5386
5387 m_binPinch = false;
5388 m_gestureFinishTimer.Start(500, wxTIMER_ONE_SHOT);
5389 break;
5390 }
5391
5392 case GestureCanceled:
5393 m_binPinch = false;
5394 m_gestureFinishTimer.Start(500, wxTIMER_ONE_SHOT);
5395 break;
5396
5397 default:
5398 break;
5399 }
5400
5401 m_bgestureGuard = true;
5402 // m_bpinchGuard = true;
5403 m_gestureEeventTimer.Start(500, wxTIMER_ONE_SHOT);
5404}
5405
5406void glChartCanvas::onGestureTimerEvent(wxTimerEvent &event) {
5407 // On some devices, the pan GestureFinished event fails to show up
5408 // Watch for this case, and fix it.....
5409 // qDebug() << "onGestureTimerEvent";
5410
5411 if (m_binPan) {
5412 m_binPan = false;
5413 Invalidate();
5414 Update();
5415 }
5416 m_bgestureGuard = false;
5417 m_bpinchGuard = false;
5418 m_binGesture = false;
5419 m_bforcefull = false;
5420}
5421
5422void glChartCanvas::onGestureFinishTimerEvent(wxTimerEvent &event) {
5423 // qDebug() << "onGestureFinishTimerEvent";
5424
5425 // signal gesture is finished after a delay
5426 m_binGesture = false;
5427 m_bforcefull = false;
5428}
5429
5430#else
5431#ifdef HAVE_WX_GESTURE_EVENTS
5432
5433void glChartCanvas::OnEvtPanGesture(wxPanGestureEvent &event) {
5434 // qDebug() << "OnEvtPanGesture" << m_pParentCanvas->m_canvasIndex <<
5435 // event.cursor_pos.x;
5436
5437 if (m_pParentCanvas->isRouteEditing() || m_pParentCanvas->isMarkEditing())
5438 return;
5439
5440 if (m_binPinch) return;
5441 if (m_bpinchGuard) return;
5442
5443 int dx = event.GetDelta().x;
5444 int dy = event.GetDelta().y;
5445
5446 if (event.IsGestureStart()) {
5447 if (m_binPan) return;
5448
5449 panx = pany = 0;
5450 m_binPan = true;
5451 m_binGesture = true;
5452 // qDebug() << "pg1";
5453 }
5454
5455 else if (event.IsGestureEnd()) {
5456 // qDebug() << "panGestureFinished";
5457 m_pParentCanvas->UpdateCanvasControlBar();
5458 m_binPan = false;
5459 m_gestureFinishTimer.Start(500, wxTIMER_ONE_SHOT);
5460 }
5461
5462 else {
5463 if (m_binPan) {
5464 if (!g_GLOptions.m_bUseCanvasPanning) {
5465 // qDebug() << "slowpan" << dx << dy;
5466
5467 m_pParentCanvas->FreezePiano();
5468 m_pParentCanvas->PanCanvas(dx, -dy);
5469 m_pParentCanvas->ThawPiano();
5470
5471 } else {
5472 FastPan(dx, dy);
5473 }
5474
5475 panx -= dx;
5476 pany -= dy;
5477 }
5478 }
5479
5480 m_bgestureGuard = true;
5481 m_gestureEeventTimer.Start(500, wxTIMER_ONE_SHOT);
5482 m_bforcefull = false;
5483}
5484
5485bool first_zout = false;
5486
5487// Generic wxWidgets gesture event processor
5488void glChartCanvas::OnEvtZoomGesture(wxZoomGestureEvent &event) {
5489 float zoom_gain = 1.0;
5490 float zout_gain = 1.0;
5491
5492 float last_zoom_val = m_total_zoom_val;
5493
5494 float max_zoom_scale = 1000.;
5495 float min_zoom_scale = 2e8;
5496
5497 if (event.GetZoomFactor() > 1)
5498 m_total_zoom_val = ((event.GetZoomFactor() - 1.0) * zoom_gain) + 1.0;
5499 else
5500 m_total_zoom_val = 1.0 - ((1.0 - event.GetZoomFactor()) * zout_gain);
5501
5502 float inc_zoom_val =
5503 m_total_zoom_val / last_zoom_val; // the incremental zoom
5504
5505 double projected_scale =
5506 m_pParentCanvas->GetVP().chart_scale / m_total_zoom_val;
5507
5508 if (event.IsGestureStart()) {
5509 // printf("\nStart--------------\n");
5510 first_zout = false;
5511 m_binPinch = true;
5512 m_binPan = false; // cancel any tentative pan gesture, in case the "pan
5513 // cancel" event was lost
5514 m_binGesture = true;
5515 m_pinchStart = event.GetPosition();
5516 m_lpinchPoint = m_pinchStart;
5517 m_total_zoom_val = 1.0;
5518 m_final_zoom_val = 1.0;
5519
5520 m_pParentCanvas->GetCanvasPixPoint(
5521 event.GetPosition().x, event.GetPosition().y, m_pinchlat, m_pinchlon);
5522 // qDebug() << "center" << event.GetCenterPoint().x <<
5523 // event.GetCenterPoint().y;
5524
5525 m_cc_x = m_fbo_offsetx + (m_fbo_swidth / 2);
5526 m_cc_y = m_fbo_offsety + (m_fbo_sheight / 2);
5527
5528 // Render the full charts with overlay objects onto the frame buffer.
5529 SetCurrent(*m_pcontext);
5530 RenderScene();
5531#ifndef __ANDROID__
5532 ViewPort vpr = m_pParentCanvas->VPoint;
5533 m_texVP = vpr;
5534 GetTouchBackingBitmap(vpr);
5535#endif
5536 m_zoom_inc = 1.0;
5537 }
5538
5539 if (event.IsGestureEnd()) {
5540 // printf("End--------------\n");
5541 // qDebug() << "finish totalzoom" << total_zoom_val <<
5542 // projected_scale;
5543
5544 // Some ptaforms generate spurious gestureEnd events. Guard for this.
5545 if (!m_binGesture) return;
5546
5547 float cc_x = m_fbo_offsetx + (m_fbo_swidth / 2);
5548 float cc_y = m_fbo_offsety + (m_fbo_sheight / 2);
5549 float dy = 0;
5550 float dx = 0;
5551
5552 float tzoom = m_final_zoom_val;
5553
5554 dx = (cc_x - m_cc_x) * tzoom;
5555 dy = -(cc_y - m_cc_y) * tzoom;
5556
5557 if (zoomTimer.IsRunning()) {
5558 // qDebug() << "Final zoom";
5559 m_zoomFinal = true;
5560 m_zoomFinalZoom = tzoom;
5561 m_zoomFinaldx = dx;
5562 m_zoomFinaldy = dy;
5563 }
5564
5565 else {
5566 double final_projected_scale =
5567 m_pParentCanvas->GetVP().chart_scale / tzoom;
5568 // qDebug() << "Final pinchB" << tzoom << final_projected_scale;
5569
5570 if (final_projected_scale < min_zoom_scale) {
5571 // qDebug() << "zoomit";
5572 m_pParentCanvas->ZoomCanvas(tzoom, false);
5573 m_pParentCanvas->PanCanvas(dx, dy);
5574 m_pParentCanvas->m_pQuilt->Invalidate();
5575 m_bforcefull = true;
5576
5577 } else {
5578 double new_scale =
5579 m_pParentCanvas->GetCanvasScaleFactor() / min_zoom_scale;
5580 // qDebug() << "clampit";
5581 m_pParentCanvas->SetVPScale(new_scale);
5582 m_pParentCanvas->m_pQuilt->Invalidate();
5583 m_bforcefull = true;
5584 }
5585 }
5586
5587 m_binPinch = false;
5588 m_final_zoom_val = 1.0;
5589 m_total_zoom_val = 1.0;
5590 m_gestureFinishTimer.Start(500, wxTIMER_ONE_SHOT);
5591 }
5592
5593 else {
5594 if (1 /* g_GLOptions.m_bUseCanvasPanning*/) {
5595 if (projected_scale < min_zoom_scale) {
5596 wxPoint pinchPoint = event.GetPosition();
5597
5598 float dx = pinchPoint.x - m_lpinchPoint.x;
5599 float dy = pinchPoint.y - m_lpinchPoint.y;
5600
5601 FastZoom(inc_zoom_val, m_pinchStart.x, m_pinchStart.y,
5602 -dx / m_total_zoom_val, dy / m_total_zoom_val);
5603
5604 m_lpinchPoint = pinchPoint;
5605 m_final_zoom_val *= inc_zoom_val;
5606 }
5607 } else {
5608 // qDebug() << "update totalzoom" << total_zoom_val << projected_scale;
5609 if (1 || ((m_total_zoom_val > 1) && !first_zout)) { // Zoom in
5610 wxPoint pinchPoint = event.GetPosition();
5611
5612 float dx = pinchPoint.x - m_lpinchPoint.x;
5613 float dy = pinchPoint.y - m_lpinchPoint.y;
5614
5615 if ((projected_scale > max_zoom_scale) &&
5616 (projected_scale < min_zoom_scale))
5617 FastZoom(inc_zoom_val, m_pinchStart.x, m_pinchStart.y,
5618 -dx / m_total_zoom_val, dy / m_total_zoom_val);
5619
5620 m_lpinchPoint = pinchPoint;
5621 m_final_zoom_val *= inc_zoom_val;
5622
5623 } else {
5624 first_zout = true;
5625 m_zoom_inc *= inc_zoom_val;
5626 if ((m_zoom_inc < 0.9) || (m_zoom_inc > 1.1)) {
5627 m_pParentCanvas->ZoomCanvas(m_zoom_inc, false);
5628 m_zoom_inc = 1.0;
5629 }
5630
5631 wxPoint pinchPoint = event.GetPosition();
5632 float dx = pinchPoint.x - m_lpinchPoint.x;
5633 float dy = pinchPoint.y - m_lpinchPoint.y;
5634 m_pParentCanvas->PanCanvas(-dx, -dy);
5635 m_lpinchPoint = pinchPoint;
5636 }
5637 }
5638 }
5639 m_bgestureGuard = true;
5640 m_bpinchGuard = true;
5641 m_gestureEeventTimer.Start(500, wxTIMER_ONE_SHOT);
5642}
5643
5644void glChartCanvas::onGestureTimerEvent(wxTimerEvent &event) {
5645 // On some devices, the pan GestureFinished event fails to show up
5646 // Watch for this case, and fix it.....
5647 // qDebug() << "onGestureTimerEvent";
5648
5649 if (m_binPan) {
5650 m_binPan = false;
5651 Invalidate();
5652 Update();
5653 }
5654 m_bgestureGuard = false;
5655 m_bpinchGuard = false;
5656 m_binGesture = false;
5657 m_bforcefull = false;
5658}
5659
5660void glChartCanvas::onGestureFinishTimerEvent(wxTimerEvent &event) {
5661 // qDebug() << "onGestureFinishTimerEvent";
5662
5663 // signal gesture is finished after a delay
5664 m_binGesture = false;
5665 m_bforcefull = false;
5666}
5667
5668#endif
5669#endif
5670
5671void glChartCanvas::configureShaders(ViewPort &vp) {
5672#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
5673 mat4x4 I;
5674 mat4x4_identity(I);
5675
5676 ViewPort *pvp = (ViewPort *)&vp;
5677
5678 GLShaderProgram *shader = pcolor_tri_shader_program[GetCanvasIndex()];
5679 shader->Bind();
5680 shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)pvp->vp_matrix_transform);
5681 shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)I);
5682 shader->UnBind();
5683
5684 // glUseProgram(color_tri_shader_program);
5685 // GLint matloc = glGetUniformLocation(color_tri_shader_program,
5686 // "MVMatrix"); glUniformMatrix4fv(matloc, 1, GL_FALSE,
5687 // (const GLfloat *)pvp->vp_transform);
5688 // GLint transloc =
5689 // glGetUniformLocation(color_tri_shader_program,
5690 // "TransformMatrix");
5691 // glUniformMatrix4fv(transloc, 1, GL_FALSE, (const GLfloat *)I);
5692
5693 // glUseProgram(texture_2D_shader_program);
5694 // matloc = glGetUniformLocation(texture_2D_shader_program, "MVMatrix");
5695 // glUniformMatrix4fv(matloc, 1, GL_FALSE,
5696 // (const GLfloat *)pvp->vp_transform);
5697 // transloc =
5698 // glGetUniformLocation(texture_2D_shader_program,
5699 // "TransformMatrix");
5700 // glUniformMatrix4fv(transloc, 1, GL_FALSE, (const GLfloat *)I);
5701
5702 shader = ptexture_2D_shader_program[GetCanvasIndex()];
5703 shader->Bind();
5704 shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)pvp->vp_matrix_transform);
5705 shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)I);
5706 shader->UnBind();
5707
5708 // glUseProgram(circle_filled_shader_program);
5709 // matloc = glGetUniformLocation(circle_filled_shader_program,
5710 // "MVMatrix"); glUniformMatrix4fv(matloc, 1, GL_FALSE,
5711 // (const GLfloat *)pvp->vp_transform);
5712 // transloc =
5713 // glGetUniformLocation(circle_filled_shader_program,
5714 // "TransformMatrix");
5715 // glUniformMatrix4fv(transloc, 1, GL_FALSE, (const GLfloat *)I);
5716
5717 shader = pcircle_filled_shader_program[GetCanvasIndex()];
5718 shader->Bind();
5719 shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)pvp->vp_matrix_transform);
5720 shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)I);
5721 shader->UnBind();
5722
5723 shader = ptexture_2DA_shader_program[GetCanvasIndex()];
5724 shader->Bind();
5725 shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)pvp->vp_matrix_transform);
5726 shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)I);
5727 shader->UnBind();
5728
5729 // glUseProgram(AALine_shader_program);
5730 // matloc = glGetUniformLocation(AALine_shader_program, "MVMatrix");
5731 // glUniformMatrix4fv(matloc, 1, GL_FALSE,
5732 // (const GLfloat *)pvp->vp_transform);
5733
5734 shader = pAALine_shader_program[GetCanvasIndex()];
5735 shader->Bind();
5736 shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)pvp->vp_matrix_transform);
5737 shader->UnBind();
5738
5739 shader = pring_shader_program[GetCanvasIndex()];
5740 shader->Bind();
5741 shader->SetUniformMatrix4fv("MVMatrix", (GLfloat *)pvp->vp_matrix_transform);
5742 shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)I);
5743 shader->UnBind();
5744
5745 // Leftover shader required by some older Android plugins
5746 if (texture_2DA_shader_program) {
5747 glUseProgram(texture_2DA_shader_program);
5748 GLint matloc = glGetUniformLocation(texture_2DA_shader_program, "MVMatrix");
5749 glUniformMatrix4fv(matloc, 1, GL_FALSE,
5750 (const GLfloat *)pvp->vp_matrix_transform);
5751 GLint transloc =
5752 glGetUniformLocation(texture_2DA_shader_program, "TransformMatrix");
5753 glUniformMatrix4fv(transloc, 1, GL_FALSE, (const GLfloat *)I);
5754 }
5755
5756 m_gldc.m_texfont.PrepareShader(vp.pix_width, vp.pix_height, vp.rotation);
5757
5758#endif
5759}
5760
5761void glChartCanvas::RenderTextures(ocpnDC &dc, float *coords, float *uvCoords,
5762 int nVertex, ViewPort *vp) {
5763// #ifdef USE_ANDROID_GLES2
5764#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
5765 int nl = nVertex / 4;
5766 float *lc = coords;
5767 float *luv = uvCoords;
5768
5769 while (nl) {
5770 RenderSingleTexture(dc, lc, luv, vp, 0, 0, 0);
5771
5772 lc += 8;
5773 luv += 8;
5774 nl--;
5775 }
5776
5777#else
5778 glEnableClientState(GL_VERTEX_ARRAY);
5779 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
5780
5781 glTexCoordPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), uvCoords);
5782 glVertexPointer(2, GL_FLOAT, 2 * sizeof(GLfloat), coords);
5783 glDrawArrays(GL_QUADS, 0, 4);
5784
5785#endif
5786
5787 return;
5788}
5789
5790void glChartCanvas::RenderSingleTexture(ocpnDC &dc, float *coords,
5791 float *uvCoords, ViewPort *vp, float dx,
5792 float dy, float angle_rad) {
5793#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
5794
5795 GLShaderProgram *shader = ptexture_2D_shader_program[dc.m_canvasIndex];
5796 if (!shader) return;
5797
5798 shader->Bind();
5799
5800 // Set up the texture sampler to texture unit 0
5801 shader->SetUniform1i("uTex", 0);
5802
5803 // Rotate
5804 mat4x4 I, Q;
5805 mat4x4_identity(I);
5806 mat4x4_rotate_Z(Q, I, angle_rad);
5807
5808 // Translate
5809 Q[3][0] = dx;
5810 Q[3][1] = dy;
5811
5812 shader->SetUniformMatrix4fv("TransformMatrix", (GLfloat *)Q);
5813
5814 float co1[8];
5815 float tco1[8];
5816
5817 shader->SetAttributePointerf("aPos", co1);
5818 shader->SetAttributePointerf("aUV", tco1);
5819
5820// Perform the actual drawing.
5821
5822// For some reason, glDrawElements is busted on Android
5823// So we do this a hard ugly way, drawing two triangles...
5824#if 0
5825 GLushort indices1[] = {0,1,3,2};
5826 glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, indices1);
5827#else
5828
5829 co1[0] = coords[0];
5830 co1[1] = coords[1];
5831 co1[2] = coords[2];
5832 co1[3] = coords[3];
5833 co1[4] = coords[6];
5834 co1[5] = coords[7];
5835 co1[6] = coords[4];
5836 co1[7] = coords[5];
5837
5838 tco1[0] = uvCoords[0];
5839 tco1[1] = uvCoords[1];
5840 tco1[2] = uvCoords[2];
5841 tco1[3] = uvCoords[3];
5842 tco1[4] = uvCoords[6];
5843 tco1[5] = uvCoords[7];
5844 tco1[6] = uvCoords[4];
5845 tco1[7] = uvCoords[5];
5846
5847 // glVertexAttribPointer(mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, co1);
5848 // glVertexAttribPointer(mUvAttrib, 2, GL_FLOAT, GL_FALSE, 0, tco1);
5849
5850 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
5851
5852 shader->UnBind();
5853
5854#endif
5855
5856#else
5857#endif
5858
5859 return;
5860}
5861
5862void glChartCanvas::RenderColorRect(wxRect r, wxColor &color) {
5863#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
5864
5865 GLShaderProgram *shader = pcolor_tri_shader_program[GetCanvasIndex()];
5866 shader->Bind();
5867
5868 shader->SetUniformMatrix4fv(
5869 "MVMatrix", (GLfloat *)m_pParentCanvas->GetpVP()->vp_matrix_transform);
5870
5871 float colorv[4];
5872 colorv[0] = color.Red() / float(256);
5873 colorv[1] = color.Green() / float(256);
5874 colorv[2] = color.Blue() / float(256);
5875 colorv[3] = 1.0;
5876 shader->SetUniform4fv("color", colorv);
5877
5878 float pf[8];
5879 pf[0] = r.x + r.width;
5880 pf[1] = r.y;
5881 pf[2] = r.x;
5882 pf[3] = r.y;
5883 pf[4] = r.x + r.width;
5884 pf[5] = r.y + r.height;
5885 pf[6] = r.x;
5886 pf[7] = r.y + r.height;
5887 shader->SetAttributePointerf("position", pf);
5888
5889 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
5890
5891 shader->UnBind();
5892
5893#else
5894#endif
5895}
5896
5897void glChartCanvas::RenderScene(bool bRenderCharts, bool bRenderOverlays) {
5898#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
5899
5900 ViewPort VPoint = m_pParentCanvas->VPoint;
5901 ocpnDC gldc(*this);
5902
5903 int w, h;
5904 GetClientSize(&w, &h);
5905 int sx = GetSize().x;
5906 int sy = GetSize().y;
5907
5908 OCPNRegion screen_region(wxRect(0, 0, VPoint.pix_width, VPoint.pix_height));
5909
5910 glViewport(0, 0, (GLint)w, (GLint)h);
5911
5912 if (s_b_useStencil) {
5913 glEnable(GL_STENCIL_TEST);
5914 glStencilMask(0xff);
5915 glClear(GL_STENCIL_BUFFER_BIT);
5916 glDisable(GL_STENCIL_TEST);
5917 }
5918
5919 // Make sure we have a valid quilt composition
5920 m_pParentCanvas->m_pQuilt->Compose(m_pParentCanvas->VPoint);
5921
5922 // set opengl settings that don't normally change
5923 // this should be able to go in SetupOpenGL, but it's
5924 // safer here incase a plugin mangles these
5925 glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
5926 glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
5927 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
5928
5929 // enable rendering to texture in framebuffer object
5930 glBindFramebuffer(GL_FRAMEBUFFER_EXT, m_fb0);
5931
5932 glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0,
5933 g_texture_rectangle_format, m_cache_tex[m_cache_page],
5934 0);
5935
5936 m_fbo_offsetx = 0;
5937 m_fbo_offsety = 0;
5938 m_fbo_swidth = sx;
5939 m_fbo_sheight = sy;
5940
5941 if (bRenderCharts) RenderCharts(gldc, screen_region);
5942
5943 if (bRenderOverlays) {
5944 RenderS57TextOverlay(m_pParentCanvas->VPoint);
5945 RenderMBTilesOverlay(m_pParentCanvas->VPoint);
5946 DrawStaticRoutesTracksAndWaypoints(m_pParentCanvas->VPoint);
5947 DrawDynamicRoutesTracksAndWaypoints(VPoint);
5948 DrawFloatingOverlayObjects(m_gldc);
5949 }
5950
5951 // All done, so disable Render to FBO
5952 glBindFramebuffer(GL_FRAMEBUFFER, 0);
5953
5954#endif
5955}
5956
5957wxBitmap &glChartCanvas::GetTouchBackingBitmap(ViewPort &vp) {
5958 wxBitmap tbm(vp.pix_width, vp.pix_height, -1);
5959 wxMemoryDC tdc(tbm);
5960 tdc.SetBackground(wxBrush(GetGlobalColor("BLUEBACK")));
5961 tdc.Clear();
5962 ocpnDC dc = ocpnDC(tdc);
5963 ViewPort tvp = vp;
5964
5965 tvp.view_scale_ppm /= 2;
5966 tvp.SetBoxes();
5967
5968 gShapeBasemap.SetBasemapLandColor(GetGlobalColor("LANDBACK"));
5969 dc.SetPen(*wxTRANSPARENT_PEN);
5970
5971 gShapeBasemap.RenderViewOnDC(dc, tvp);
5972 tdc.SelectObject(wxNullBitmap);
5973 m_touch_backing_bitmap = tbm;
5974 CreateBackingTexture();
5975
5976 return m_touch_backing_bitmap;
5977}
5978
5979void glChartCanvas::CreateBackingTexture() {
5980 wxImage image = m_touch_backing_bitmap.ConvertToImage();
5981 unsigned char *imgdata = image.GetData();
5982 unsigned char *imgalpha = image.GetAlpha();
5983 m_tex_w = image.GetWidth();
5984 m_tex_h = image.GetHeight();
5985 m_image_width = m_tex_w;
5986 m_image_height = m_tex_h;
5987
5988 GLuint format = GL_RGBA;
5989 GLuint internalformat = g_texture_rectangle_format;
5990#ifndef __ANDROID__
5991 internalformat = GL_RGBA;
5992#endif
5993 int stride = 4;
5994
5995 if (imgdata) {
5996 unsigned char *teximage =
5997 (unsigned char *)malloc(stride * m_tex_w * m_tex_h);
5998
5999 for (int i = 0; i < m_image_height; i++) {
6000 for (int j = 0; j < m_image_width; j++) {
6001 int s = (i * 3 * m_image_width) + (j * 3);
6002 int d = (i * stride * m_tex_w) + (j * stride);
6003
6004 teximage[d + 0] = imgdata[s + 0];
6005 teximage[d + 1] = imgdata[s + 1];
6006 teximage[d + 2] = imgdata[s + 2];
6007 teximage[d + 3] = 255;
6008 }
6009 }
6010
6011 glGenTextures(1, &m_TouchBackingTexture);
6012 glBindTexture(GL_TEXTURE_2D, m_TouchBackingTexture);
6013
6014 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
6015 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
6016 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
6017 GL_NEAREST); // No mipmapping
6018 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
6019
6020 glTexImage2D(GL_TEXTURE_2D, 0, internalformat, m_tex_w, m_tex_h, 0, format,
6021 GL_UNSIGNED_BYTE, teximage);
6022
6023 free(teximage);
6024 glBindTexture(GL_TEXTURE_2D, 0);
6025 }
6026}
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:203
Base class for BSB (Maptech/NOS) format nautical charts.
Definition chartimg.h:131
Base class for all chart types.
Definition chartbase.h:119
Chart display canvas.
Definition chcanv.h:135
bool GetCanvasPointPix(double rlat, double rlon, wxPoint *r)
Convert latitude/longitude to canvas pixel coordinates (physical pixels) rounded to nearest integer.
Definition chcanv.cpp:4518
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:4468
double GetCanvasScaleFactor()
Return the number of logical pixels per meter for the screen.
Definition chcanv.h:439
double GetPixPerMM()
Get the number of logical pixels per millimeter on the screen.
Definition chcanv.h:470
bool MouseEventSetup(wxMouseEvent &event, bool b_handle_dclick=true)
Definition chcanv.cpp:7481
bool PanCanvas(double dx, double dy)
Pans (moves) the canvas by the specified physical pixels in x and y directions.
Definition chcanv.cpp:5019
float GetVPScale()
Return the ViewPort scale factor, in physical pixels per meter.
Definition chcanv.h:428
void ZoomCanvasSimple(double factor)
Perform an immediate zoom operation without smooth transitions.
Definition chcanv.cpp:4599
bool SetVPScale(double sc, bool b_refresh=true)
Sets the viewport scale while maintaining the center point.
Definition chcanv.cpp:5293
void GetCanvasPixPoint(double x, double y, double &lat, double &lon)
Convert canvas pixel coordinates (physical pixels) to latitude/longitude.
Definition chcanv.cpp:4543
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:4604
void GetDoubleCanvasPointPix(double rlat, double rlon, wxPoint2DDouble *r)
Convert latitude/longitude to canvas pixel coordinates (physical pixels) with double precision.
Definition chcanv.cpp:4463
bool MouseEventProcessCanvas(wxMouseEvent &event)
Processes mouse events for core chart panning and zooming operations.
Definition chcanv.cpp:9798
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
Main application frame.
Definition ocpn_frame.h:136
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
Definition route.h:75
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:78
Represents the view port for chart display in OpenCPN.
Definition viewport.h:84
double view_scale_ppm
Requested view scale in physical pixels per meter (ppm), before applying projections.
Definition viewport.h:199
double ref_scale
The nominal scale of the "reference chart" for this view.
Definition viewport.h:216
int pix_height
Height of the viewport in physical pixels.
Definition viewport.h:221
double rotation
Rotation angle of the viewport in radians.
Definition viewport.h:209
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:219
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:211
double skew
Angular distortion (shear transform) applied to the viewport in radians.
Definition viewport.h:207
void GetLLFromPix(const wxPoint &p, double *lat, double *lon)
Convert physical pixel coordinates on the ViewPort to latitude and longitude.
Definition viewport.h:104
double clon
Center longitude of the viewport in degrees.
Definition viewport.h:194
double clat
Center latitude of the viewport in degrees.
Definition viewport.h:192
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:214
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
Tools to send data to plugins.
Represents an entry in the chart table, containing information about a single chart.
Definition chartdbs.h:181