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