OSDN Git Service

6bf1bfd7c20342777d053862adaaf8464c057470
[gvonavish/GVONavish.git] / GVONavish / GVONavish / GVORenderer.cpp
1 #include "stdafx.h"
2 #include "GVORenderer.h"
3 #include "GVOWorldMap.h"
4 #include "GVOConfig.h"
5 #include "GVOTexture.h"
6 #include "GVOShipRouteList.h"
7 #include "GVOShipRoute.h"
8
9
10
11 namespace {
12         const double k_scaleStep = 0.125;       // 12.5%
13         const double k_minScale = 0.125;        // 12.5%
14         const double k_maxScale = 4.00;         // 400%
15
16         // Google\90æ\90\9eH\82­\81u\92n\8b\85\82Ì\8aO\8eü\82Í40,075km\81v\81u1\83m\83b\83g\82Í1.85200km\81v
17         // 1\90¢\8aE\8dÀ\95W\82Í40,075km/16384points
18         // \8eÀ\8e\9e\8aÔ1\95b\82Å\83Q\81[\83\80\93à0.4\8e\9e\8aÔ
19         //
20         // \92n\8b\85\8aO\8eü\82ð\90Ô\93¹\94¼\8ca\82©\82ç\8eZ\8fo\82·\82é\81B
21         // \90Ô\93¹\94¼\8ca\82Í6378.137\82È\82Ì\82Å\8aO\8eü\82Í2*M_PI_*6378.137
22         inline double s_velocityByKnot( const double velocity )
23         {
24                 static const double k_knotFactor = (2 * M_PI * 6378.137) / 16384.0 / 0.4 / 1.852;
25                 return velocity * k_knotFactor;
26         }
27 }
28
29
30 void GVORenderer::setup( const GVOConfig * config, HDC hdcPrimary, const GVOWorldMap * worldMap )
31 {
32         m_hdcPrimary = hdcPrimary;
33         setConfig( config );
34         setupGL();
35         setWorldMap( worldMap );
36 }
37
38
39 void GVORenderer::teardown()
40 {
41         delete m_worldMapTexture;
42         m_worldMapTexture = NULL;
43
44         ::wglMakeCurrent( NULL, NULL );
45         ::wglDeleteContext( m_hglrc );
46         m_hglrc = NULL;
47         m_hdcPrimary = NULL;
48 }
49
50
51 void GVORenderer::setConfig( const GVOConfig * config )
52 {
53         m_focusPointInWorldCoord = config->m_initialSurveyCoord;
54         m_shipPointInWorld.x = -1;
55         m_shipPointInWorld.y = -1;
56         m_shipVectorLineEnabled = config->m_shipVectorLineEnabled;
57         m_speedMeterEnabled = config->m_speedMeterEnabled;
58         m_traceShipEnabled = config->m_traceShipPositionEnabled;
59 }
60
61
62 void GVORenderer::setupGL()
63 {
64         PIXELFORMATDESCRIPTOR pfd = { sizeof(pfd) };
65         pfd.nVersion = 1;
66         pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER | PFD_SWAP_EXCHANGE;
67         pfd.iPixelType = PFD_TYPE_RGBA;
68         pfd.cColorBits = 24;
69         pfd.cAlphaBits = 8;
70         pfd.cDepthBits = 16;
71         pfd.iLayerType = PFD_MAIN_PLANE;
72         ::SetPixelFormat( m_hdcPrimary, ::ChoosePixelFormat( m_hdcPrimary, &pfd ), &pfd );
73         m_hglrc = ::wglCreateContext( m_hdcPrimary );
74         ::wglMakeCurrent( m_hdcPrimary, m_hglrc );
75         ::glDisable( GL_DEPTH_TEST );
76         ::glDisable( GL_LIGHTING );
77         ::glEnable( GL_CULL_FACE );
78         ::glCullFace( GL_BACK );
79 }
80
81
82 void GVORenderer::setWorldMap( const GVOWorldMap * worldMap )
83 {
84         m_worldMap = worldMap;
85         m_worldMapTexture = new GVOTexture();
86         m_worldMapTexture->setImage( worldMap->image() );
87 }
88
89
90 void GVORenderer::setViewSize( const SIZE& viewSize )
91 {
92         m_viewSize = viewSize;
93         ::wglMakeCurrent( m_hdcPrimary, m_hglrc );
94         ::glMatrixMode( GL_PROJECTION );
95         ::glLoadIdentity();
96         ::glViewport( 0, 0, m_viewSize.cx, m_viewSize.cy );
97         ::gluOrtho2D( 0, m_viewSize.cx, m_viewSize.cy, 0 );
98 }
99
100
101 SIZE GVORenderer::scaledMapSize() const
102 {
103         SIZE size = {
104                 LONG( m_worldMap->image().width() * m_viewScale ),
105                 LONG( m_worldMap->image().height() * m_viewScale )
106         };
107         return size;
108 }
109
110
111 POINT GVORenderer::mapOriginInView() const
112 {
113         const POINT viewCenter = viewCenterPoint();
114         const SIZE mapSize = scaledMapSize();
115         const POINT worldPosInView = drawOffsetFromWorldCoord( m_focusPointInWorldCoord );
116
117         POINT mapTopLeft = {
118                 viewCenter.x - worldPosInView.x,
119                 viewCenter.y - worldPosInView.y
120         };
121         if ( m_viewSize.cx < mapSize.cx ) {
122                 while ( 0 < mapTopLeft.x ) {
123                         mapTopLeft.x -= mapSize.cx;
124                 }
125         }
126
127         return mapTopLeft;
128 }
129
130
131 void GVORenderer::offsetFocusInViewCoord( const POINT& offset )
132 {
133         const double dx = ((double)offset.x / m_viewScale) / m_worldMap->image().width();
134         const double dy = ((double)offset.y / m_viewScale) / m_worldMap->image().height();
135
136         LONG x = m_focusPointInWorldCoord.x + LONG( dx * k_worldWidth );
137         LONG y = m_focusPointInWorldCoord.y + LONG( dy * k_worldHeight );
138         y = std::max<LONG>( 0, std::min<LONG>( y, k_worldHeight ) );
139         while ( x < 0 ) {
140                 x += k_worldWidth;
141         }
142         while ( k_worldWidth < x ) {
143                 x -= k_worldWidth;
144         }
145
146         m_focusPointInWorldCoord.x = x;
147         m_focusPointInWorldCoord.y = y;
148 }
149
150
151 bool GVORenderer::zoomIn()
152 {
153         double scale = m_viewScale;
154         double step = k_scaleStep;
155
156         scale = m_viewScale + step;
157         if ( k_maxScale < scale ) {
158                 scale = k_maxScale;
159         }
160         if ( m_viewScale != scale ) {
161                 m_viewScale = scale;
162                 return true;
163         }
164         return false;
165 }
166
167
168 bool GVORenderer::zoomOut()
169 {
170         double scale = m_viewScale;
171         double step = k_scaleStep;
172
173         scale = m_viewScale - step;
174         if ( scale < k_minScale ) {
175                 scale = k_minScale;
176         }
177         if ( m_viewScale != scale ) {
178                 m_viewScale = scale;
179                 return true;
180         }
181         return false;
182 }
183
184
185 void GVORenderer::resetViewScale()
186 {
187         m_viewScale = 1.0;
188 }
189
190
191 POINT GVORenderer::drawOffsetFromWorldCoord( const POINT&worldCoord ) const
192 {
193         const POINT worldPosInImage = m_worldMap->imageCoordFromWorldCoord( worldCoord );
194         const POINT drawOffset = {
195                 LONG( worldPosInImage.x * m_viewScale ),
196                 LONG( worldPosInImage.y * m_viewScale )
197         };
198         return drawOffset;
199 }
200
201
202 void GVORenderer::setShipPositionInWorld( const POINT& shipPositionInWorld )
203 {
204         if ( m_traceShipEnabled ) {
205                 m_focusPointInWorldCoord = shipPositionInWorld;
206         }
207         m_shipPointInWorld = shipPositionInWorld;
208 }
209
210
211 void GVORenderer::render( const GVOVector& shipVector, double shipVelocity, GVOTexture * shipTexture, const GVOShipRouteList * shipRouteList )
212 {
213         ::wglMakeCurrent( m_hdcPrimary, m_hglrc );
214         ::glClearColor( 0.2f, 0.2f, 0.3f, 0.0f );
215         ::glClear( GL_COLOR_BUFFER_BIT );
216         ::glDisable( GL_BLEND );
217
218         renderMap( shipVector, shipTexture, shipRouteList );
219
220         if ( m_speedMeterEnabled ) {
221                 renderSpeedMeter( shipVelocity );
222         }
223
224         ::glFlush();
225         ::SwapBuffers( m_hdcPrimary );
226         ::wglMakeCurrent( NULL, NULL );
227 }
228
229
230 void GVORenderer::renderMap( const GVOVector& shipVector, GVOTexture * shipTexture, const GVOShipRouteList * shipRouteList )
231 {
232         _ASSERT( m_worldMapTexture != NULL );
233
234         const SIZE mapSize = scaledMapSize();
235
236         const POINT mapTopLeft = mapOriginInView();
237         int xDrawOrigin, yDrawOrigin;
238         xDrawOrigin = mapTopLeft.x;
239         yDrawOrigin = mapTopLeft.y;
240         if ( 0 < xDrawOrigin ) {
241                 xDrawOrigin = (xDrawOrigin % mapSize.cx) - mapSize.cx;
242         }
243         const int xInitial = xDrawOrigin;       // \8d\92[\82Ì\95`\89æ\8aJ\8enx\8dÀ\95W
244         int drawn = xInitial;
245
246         // \90¢\8aE\92n\90}\82ð\89¡\82É\95À\82×\82Ä\95`\89æ
247         // \81i\95`\89æ\8dÅ\93K\89»\82Í\8fÈ\97ª\81j
248         ::glMatrixMode( GL_MODELVIEW );
249         ::glLoadIdentity();
250         ::glTranslatef( (float)xDrawOrigin, (float)yDrawOrigin, 0 );
251         while ( drawn < m_viewSize.cx ) {
252                 // \92n\90}\82ð1\96\87\95`\89æ
253                 renderTexture( *m_worldMapTexture, (float)mapSize.cx, (float)mapSize.cy );
254
255                 // \8dq\98H\82ð1\96\87\95ª\95`\89æ
256                 renderShipRouteList( mapSize.cx, mapSize.cy, shipRouteList );
257
258                 xDrawOrigin += mapSize.cx;
259                 drawn += mapSize.cx;
260                 ::glTranslatef( (float)mapSize.cx, 0.0f, 0.0f );
261         }
262
263
264         // \95s\90³\82È\8e©\91D\88Ê\92u\82È\82ç\82±\82ê\88È\8d~\82Ì\95`\89æ\82Í\82µ\82È\82¢\81B
265         if ( m_shipPointInWorld.x < 0 || m_shipPointInWorld.y < 0 ) {
266                 return;
267         }
268
269         const POINT shipPointOffset = drawOffsetFromWorldCoord( m_shipPointInWorld );
270
271         // \90j\98H\97\\91ª\90ü\82ð\95`\89æ
272         if ( shipVector.length() != 0.0 && m_shipVectorLineEnabled ) {
273                 const float lineWidth = max<float>( 1, float( 1 * m_viewScale ) );
274                 ::glLineWidth( lineWidth );
275                 ::glColor3f( 255, 0, 255 );
276
277                 const LONG k_lineLength = k_worldHeight;
278                 const POINT reachPointOffset = drawOffsetFromWorldCoord(
279                         shipVector.pointFromOriginWithLength( m_shipPointInWorld, k_lineLength )
280                         );
281
282                 // \8c©\82¦\82Ä\82é\92n\90}\89æ\91\9c\82Ì\95ª\82¾\82¯\95`\89æ\82·\82é
283                 drawn = xInitial;
284                 xDrawOrigin = xInitial;
285                 ::glMatrixMode( GL_MODELVIEW );
286                 ::glLoadIdentity();
287                 ::glTranslatef( (float)xDrawOrigin, (float)yDrawOrigin, 0 );
288                 while ( drawn < m_viewSize.cx ) {
289                         ::glBegin( GL_LINES );
290                         ::glVertex2i( shipPointOffset.x, shipPointOffset.y );
291                         ::glVertex2i( reachPointOffset.x, reachPointOffset.y );
292                         ::glEnd();
293
294                         xDrawOrigin += mapSize.cx;
295                         drawn += mapSize.cx;
296                         ::glTranslatef( (float)mapSize.cx, 0.0f, 0.0f );
297                 }
298         }
299
300
301         // \8e©\91D\82Ì\88Ê\92u\82ð\95`\89æ
302         if ( shipTexture ) {
303                 const float shipMarkSize = 16.0f;
304
305                 // \8c©\82¦\82Ä\82é\92n\90}\89æ\91\9c\82Ì\95ª\82¾\82¯\95`\89æ\82·\82é
306                 drawn = xInitial;
307                 xDrawOrigin = xInitial;
308                 ::glMatrixMode( GL_MODELVIEW );
309                 ::glLoadIdentity();
310                 ::glLineWidth( 1.0f );
311                 ::glTranslatef( (float)xDrawOrigin, (float)yDrawOrigin, 0 );
312                 while ( drawn < m_viewSize.cx ) {
313                         const float x = shipPointOffset.x - shipTexture->width() / 2.0f;
314                         const float y = shipPointOffset.y - shipTexture->height() / 2.0f;
315
316                         ::glEnable( GL_BLEND );
317                         ::glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
318                         ::glColor4f( 1.0f, 1.0f, 1.0f, 1.0f );
319                         renderTexture( *shipTexture, x, y, shipMarkSize, shipMarkSize );
320                         ::glDisable( GL_BLEND );
321
322                         xDrawOrigin += mapSize.cx;
323                         drawn += mapSize.cx;
324                         ::glTranslatef( (float)mapSize.cx, 0.0f, 0.0f );
325                 }
326         }
327 }
328
329
330 void GVORenderer::renderShipRouteList( int width, int height, const GVOShipRouteList * shipRouteList )
331 {
332         _ASSERT( 0 <= width );
333         _ASSERT( 0 <= height );
334         _ASSERT(shipRouteList != NULL);
335
336         const float lineWidth = max<float>( 1, float( 1 * m_viewScale ) );
337         ::glLineWidth( lineWidth );
338         // \8dÅ\90V\8dq\98H\88È\8aO\82ð\94¼\93§\96¾\82Å\95`\89æ
339         if ( 1 < shipRouteList->getList().size() ) {
340                 ::glEnable( GL_BLEND );
341                 ::glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
342                 ::glColor4f( 1.0f, 1.0f, 1.0f, 0.5f );
343         }
344
345         for ( const GVOShipRoute *route : shipRouteList->getList() ) {
346                 // \8dÅ\90V\8dq\98H\82¾\82¯\82ð\95s\93§\96¾\82Å\95`\89æ
347                 if ( shipRouteList->getList().back() == route ) {
348                         ::glLineWidth( lineWidth );
349                         ::glColor3f( 1.0f, 1.0f, 1.0f );
350                         ::glDisable( GL_BLEND );
351                 }
352
353                 if ( route->isFavorite() ) {
354                         ::glColor4f( 1.0f, 1.0f, 0.0f, 0.75f );
355                         if ( route->isHilight() ) {
356                                 ::glLineWidth( lineWidth * 2 );
357                         }
358                 }
359                 else {
360                         if ( route->isHilight() ) {
361                                 ::glLineWidth( lineWidth * 2 );
362                                 ::glColor4f( 0.5f, 1.0f, 1.0f, 0.75f );
363                         }
364                         else {
365                                 ::glLineWidth( lineWidth );
366                                 ::glColor4f( 1.0f, 1.0f, 1.0f, 0.5f );
367                         }
368                 }
369
370                 for ( const GVOShipRoute::Line & line : route->getLines() ) {
371                         if ( line.size() < 2 ) {
372                                 // 2\93_\96¢\96\9e\82Å\82Í\90ü\82ð\88ø\82¯\82È\82¢
373                                 continue;
374                         }
375                         ::glBegin( GL_LINE_STRIP );
376                         for ( const GVONormalizedPoint & point : line ) {
377                                 const float x = point.x() * width;
378                                 const float y = point.y() * height;
379                                 ::glVertex2f( x, y );
380                         }
381                         ::glEnd();
382                 }
383         }
384         ::glDisable( GL_BLEND );
385 }
386
387
388 void GVORenderer::renderSpeedMeter( double shipVelocity )
389 {
390         // THE\8eè\94²\82«\95\8e\9a\83e\83N\83X\83`\83\83\81[\8dì\90¬
391
392         HDC hdcMem = ::CreateCompatibleDC( m_hdcPrimary );
393         ::SaveDC( hdcMem );
394
395         const double velocity = s_velocityByKnot( shipVelocity );
396         wchar_t buf[4096] = { 0 };
397         swprintf( buf, _countof( buf ), L"%.2f kt", velocity );
398
399         RECT rc = { 0 };
400         ::DrawText( hdcMem, buf, -1, &rc, DT_SINGLELINE | DT_RIGHT | DT_TOP | DT_CALCRECT );
401         const int width = rc.right - rc.left;
402         const int stride = width + (4 - width % 4) % 4;
403         const int height = rc.bottom - rc.top;
404         GVOImage workImage;
405         workImage.createImage( stride, height );
406         ::SelectObject( hdcMem, workImage.bitmapHandle() );
407         ::DrawText( hdcMem, buf, -1, &rc, DT_SINGLELINE | DT_RIGHT | DT_TOP );
408         ::RestoreDC( hdcMem, -1 );
409         ::DeleteDC( hdcMem );
410
411         GVOTexture workTexture;
412         workTexture.setImage( workImage );
413
414         ::glMatrixMode( GL_MODELVIEW );
415         ::glLoadIdentity();
416         renderTexture( workTexture, float( m_viewSize.cx - stride ), 0.0f, (float)stride, (float)height );
417 }
418
419
420 void GVORenderer::renderTexture( GVOTexture & texture, float w, float h )
421 {
422         renderTexture( texture, 0, 0, w, h );
423 }
424
425 void GVORenderer::renderTexture( GVOTexture & texture, float x, float y, float w, float h )
426 {
427         texture.bind();
428
429         ::glBegin( GL_QUADS );
430
431         ::glTexCoord2d( 0, 0 );
432         ::glVertex2d( x, y );
433
434         ::glTexCoord2d( 0, 1 );
435         ::glVertex2d( x, y + h );
436
437         ::glTexCoord2d( 1, 1 );
438         ::glVertex2d( x + w, y + h );
439
440         ::glTexCoord2d( 1, 0 );
441         ::glVertex2d( x + w, y );
442
443         ::glEnd();
444
445         texture.unbind();
446 }
447
448
449 GVOTexture * GVORenderer::createTextureFromImage( const GVOImage & image )
450 {
451         ::wglMakeCurrent( m_hdcPrimary, m_hglrc );
452         std::auto_ptr<GVOTexture> texture( new GVOTexture() );
453         texture->setImage( image );
454         ::wglMakeCurrent( NULL, NULL );
455
456         return texture.release();
457 }