OSDN Git Service

ゲーム状態取得処理の並列化。
[gvonavish/GVONavish.git] / GVONavish / GVONavish / GVORenderer.cpp
1 #include "stdafx.h"
2 #include "GVORenderer.h"
3 #include "GVOWorldMap.h"
4 #include "GVOConfig.h"
5
6
7
8 namespace {
9         const double k_scaleStep = 0.125;       // 12.5%
10         const double k_minScale = 0.125;        // 12.5%
11         const double k_maxScale = 4.00;         // 400%
12
13         // Google\90æ\90\9eH\82­\81u\92n\8b\85\82Ì\8aO\8eü\82Í40,075km\81v\81u1\83m\83b\83g\82Í1.85200km\81v
14         // 1\90¢\8aE\8dÀ\95W\82Í40,075km/16384points
15         // \8eÀ\8e\9e\8aÔ1\95b\82Å\83Q\81[\83\80\93à0.4\8e\9e\8aÔ
16         //
17         // \92n\8b\85\8aO\8eü\82ð\90Ô\93¹\94¼\8ca\82©\82ç\8eZ\8fo\82·\82é\81B
18         // \90Ô\93¹\94¼\8ca\82Í6378.137\82È\82Ì\82Å\8aO\8eü\82Í2*M_PI_*6378.137
19         inline double s_velocityByKnot( const double velocity )
20         {
21                 static const double k_knotFactor = (2 * M_PI * 6378.137) / 16384.0 / 0.4 / 1.852;
22                 return velocity * k_knotFactor;
23         }
24 }
25
26
27 void GVORenderer::setup( HDC hdcPrimary )
28 {
29         m_hdcPrimary = hdcPrimary;
30 }
31
32
33 void GVORenderer::teardown()
34 {
35         m_hdcPrimary = NULL;
36 }
37
38
39 void GVORenderer::setWorldMap( const GVOWorldMap& worldMap )
40 {
41         m_worldMap = &worldMap;
42         const GVOImage & map = m_worldMap->image();
43         m_mapImage.copy( map );
44 }
45
46
47 void GVORenderer::setConfig( const GVOConfig& config )
48 {
49         m_positionUpdated = config.m_traceShipPositionEnabled;
50         m_focusPointInWorldCoord = config.m_initialSurveyCoord;
51         m_shipPointInWorld = config.m_initialSurveyCoord;
52         m_previousDrawPointInWorld = m_shipPointInWorld;
53         m_shipVectorLineEnabled = config.m_shipVectorLineEnabled;
54         m_speedMeterEnabled = config.m_speedMeterEnabled;
55         m_traceShipEnabled = config.m_traceShipPositionEnabled;
56 }
57
58
59 void GVORenderer::setViewSize( const SIZE& viewSize )
60 {
61         m_viewSize = viewSize;
62 }
63
64
65 SIZE GVORenderer::scaledMapSize() const
66 {
67         SIZE size = {
68                 LONG( m_mapImage.width() * m_viewScale ),
69                 LONG( m_mapImage.height() * m_viewScale )
70         };
71         return size;
72 }
73
74
75 POINT GVORenderer::mapOriginInView() const
76 {
77         const POINT viewCenter = viewCenterPoint();
78         const SIZE mapSize = scaledMapSize();
79         const POINT worldPosInView = drawOffsetFromWorldCoord( m_focusPointInWorldCoord );
80
81         POINT mapTopLeft = {
82                 viewCenter.x - worldPosInView.x,
83                 viewCenter.y - worldPosInView.y
84         };
85         if ( m_viewSize.cx < mapSize.cx ) {
86                 while ( 0 < mapTopLeft.x ) {
87                         mapTopLeft.x -= mapSize.cx;
88                 }
89         }
90
91         return mapTopLeft;
92 }
93
94
95 void GVORenderer::offsetFocusInViewCoord( const POINT& offset )
96 {
97         const double dx = ((double)offset.x / m_viewScale) / m_mapImage.width();
98         const double dy = ((double)offset.y / m_viewScale) / m_mapImage.height();
99
100         LONG x = m_focusPointInWorldCoord.x + LONG( dx * k_worldWidth );
101         LONG y = m_focusPointInWorldCoord.y + LONG( dy * k_worldHeight );
102         y = max( 0, min( y, k_worldHeight ) );
103         while ( x < 0 ) {
104                 x += k_worldWidth;
105         }
106         while ( k_worldWidth < x ) {
107                 x -= k_worldWidth;
108         }
109
110         m_focusPointInWorldCoord.x = x;
111         m_focusPointInWorldCoord.y = y;
112 }
113
114
115 bool GVORenderer::zoomIn()
116 {
117         double scale = m_viewScale;
118         double step = k_scaleStep;
119
120         scale = m_viewScale + step;
121         if ( k_maxScale < scale ) {
122                 scale = k_maxScale;
123         }
124         if ( m_viewScale != scale ) {
125                 m_viewScale = scale;
126                 return true;
127         }
128         return false;
129 }
130
131
132 bool GVORenderer::zoomOut()
133 {
134         double scale = m_viewScale;
135         double step = k_scaleStep;
136
137         scale = m_viewScale - step;
138         if ( scale < k_minScale ) {
139                 scale = k_minScale;
140         }
141         if ( m_viewScale != scale ) {
142                 m_viewScale = scale;
143                 return true;
144         }
145         return false;
146 }
147
148
149 void GVORenderer::resetViewScale()
150 {
151         m_viewScale = 1.0;
152 }
153
154
155 POINT GVORenderer::drawOffsetFromWorldCoord( const POINT&worldCoord ) const
156 {
157         const POINT worldPosInImage = m_worldMap->imageCoordFromWorldCoord( worldCoord );
158         const POINT drawOffset = {
159                 LONG( worldPosInImage.x * m_viewScale ),
160                 LONG( worldPosInImage.y * m_viewScale )
161         };
162         return drawOffset;
163 }
164
165
166 void GVORenderer::updateShipState( const POINT& worldCoord, const GVOVector& shipVector, const double shipVelocity )
167 {
168         m_shipVector = shipVector;
169         m_shipVelocity = shipVelocity;
170
171         if ( m_traceShipEnabled ) {
172                 m_focusPointInWorldCoord = worldCoord;
173         }
174         if ( m_shipPointInWorld.x != worldCoord.x
175                 || m_shipPointInWorld.y != worldCoord.y ) {
176
177                 m_positionUpdated = true;
178                 m_shipPointInWorld = worldCoord;
179         }
180         updateShipRouteMap();
181 }
182
183
184 void GVORenderer::updateShipRouteMap()
185 {
186         // \88Ú\93®\82µ\82Ä\82¢\82È\82¯\82ê\82Î\89½\82à\82µ\82È\82¢
187         if ( !m_positionUpdated ) {
188                 return;
189         }
190         // \88Ú\93®\82µ\82Ä\82¢\82Ä\82à\8dq\98H\82ð\8cq\82°\82È\82¢\82È\82ç\95`\89æ\82µ\82È\82¢
191         if ( !m_linkRoute ) {
192                 m_previousDrawPointInWorld = m_shipPointInWorld;
193                 m_linkRoute = true;
194                 return;
195         }
196
197         const POINT& latestPoint = m_worldMap->imageCoordFromWorldCoord( m_shipPointInWorld );
198         const POINT& previousPoint = m_worldMap->imageCoordFromWorldCoord( m_previousDrawPointInWorld );
199
200         // \95`\89æ\8dÀ\95W\82ª\88ê\8f\8f\82È\82ç\89½\82à\82µ\82È\82¢
201         if ( latestPoint.x == previousPoint.x
202                 && latestPoint.y == previousPoint.y ) {
203                 return;
204         }
205
206
207         // \8dÅ\90V\8dÀ\95W\82©\82ç\88ê\82Â\91O\82Ì\8dÀ\95W\82Ö\90ü\82ð\88ø\82­
208         // \90¢\8aE\82ð\8c×\82®\8fê\8d\87\82É\8dl\97\82ª\95K\97v\81B
209         // \88Ú\93®\97Ê\82ª\88ê\92è\82ð\92´\82¦\82½\82ç\95`\89æ\8f\88\97\9d\82µ\82È\82¢\82æ\82¤\82É\82·\82é\82Ì\82ª\83X\83}\81[\83g\82©\82È
210
211
212         // \90¢\8aE\82ð\8c×\82¢\82¾\82Æ\82Ý\82È\82·è\87\92l
213         const int k_distanceThreshold = m_mapImage.width() / 2;
214
215         const LONG xMin = min( latestPoint.x, previousPoint.x );
216         const LONG xMax = max( latestPoint.x, previousPoint.x );
217         const int xDistance = xMax - xMin;
218
219
220         HDC hdcMem = ::CreateCompatibleDC( m_hdcPrimary );
221         ::SaveDC( hdcMem );
222
223         HPEN hpen = ::CreatePen( PS_SOLID, 1, RGB( 255, 255, 255 ) );
224
225         ::SelectObject( hdcMem, m_mapImage.bitmapHandle() );
226         ::SelectObject( hdcMem, hpen );
227
228         if ( k_distanceThreshold < xDistance ) {
229                 if ( previousPoint.x < latestPoint.x ) {        // \90¢\8aE\82ð\90¼\82É\8cü\82©\82Á\82Ä\8c×\82¢\82¾
230                         // \8d\82Ì\89æ\96Ê\8aO\82©\82ç\8cü\82©\82¤\90ü\82ð\95`\89æ
231                         LONG x3 = latestPoint.x - m_mapImage.width();
232
233                         ::MoveToEx( hdcMem, x3, latestPoint.y, NULL );
234                         ::LineTo( hdcMem, previousPoint.x, previousPoint.y );
235
236                         // \89E\82Ì\89æ\96Ê\8aO\82É\8cü\82©\82¤\90ü\82ð\95`\89æ
237                         LONG x4 = previousPoint.x + m_mapImage.width();
238                         ::MoveToEx( hdcMem, latestPoint.x, latestPoint.y, NULL );
239                         ::LineTo( hdcMem, x4, previousPoint.y );
240                 }
241                 else {                          // \90¢\8aE\82ð\93\8c\82É\8cü\82©\82Á\82Ä\8c×\82¢\82¾
242                         // \8d\82Ì\89æ\96Ê\8aO\82©\82ç\8cü\82©\82¤\90ü\82ð\95`\89æ
243                         LONG x3 = previousPoint.x - m_mapImage.width();
244
245                         ::MoveToEx( hdcMem, latestPoint.x, latestPoint.y, NULL );
246                         ::LineTo( hdcMem, x3, previousPoint.y );
247
248                         // \89E\82Ì\89æ\96Ê\8aO\82É\8cü\82©\82¤\90ü\82ð\95`\89æ
249                         LONG x4 = latestPoint.x + m_mapImage.width();
250                         ::MoveToEx( hdcMem, x4, latestPoint.y, NULL );
251                         ::LineTo( hdcMem, previousPoint.x, previousPoint.y );
252                 }
253         }
254         else {
255                 // \8c×\82ª\82È\82¢\95`\89æ
256                 ::MoveToEx( hdcMem, latestPoint.x, latestPoint.y, NULL );
257                 ::LineTo( hdcMem, previousPoint.x, previousPoint.y );
258         }
259
260
261         ::RestoreDC( hdcMem, -1 );
262         ::DeleteDC( hdcMem );
263         ::DeleteObject( hpen );
264
265         m_previousDrawPointInWorld = m_shipPointInWorld;
266 }
267
268
269 void GVORenderer::clearShipRoute()
270 {
271         m_mapImage.copy( m_worldMap->image() );
272 }
273
274
275 void GVORenderer::render()
276 {
277         if ( !m_backBuffer.isCompatible(m_viewSize) ) {
278                 m_backBuffer.createDIBImage( m_viewSize );
279         }
280         HDC hdcBackbuffer = ::CreateCompatibleDC( m_hdcPrimary );
281         ::SaveDC( hdcBackbuffer );
282
283         ::SelectObject( hdcBackbuffer, m_backBuffer.bitmapHandle() );
284         RECT rc = { 0, 0, m_backBuffer.width(), m_backBuffer.height()};
285         ::FillRect( hdcBackbuffer, &rc, (HBRUSH)::GetStockObject( BLACK_BRUSH ) );
286
287         drawMap( hdcBackbuffer, m_shipVector );
288
289         if ( m_speedMeterEnabled ) {
290                 drawSpeedMeter( hdcBackbuffer, m_shipVelocity );
291         }
292
293         ::BitBlt( m_hdcPrimary, 0, 0, m_backBuffer.width(), m_backBuffer.height(),
294                 hdcBackbuffer, 0, 0, SRCCOPY );
295         ::RestoreDC( hdcBackbuffer, -1 );
296         ::DeleteDC( hdcBackbuffer );
297 }
298
299
300 void GVORenderer::drawMap( HDC hdc, const GVOVector& shipVector )
301 {
302         const SIZE mapSize = scaledMapSize();
303         const GVOImage *mapImage = &m_mapImage;
304
305         ::SaveDC( hdc );
306         if ( m_viewScale < 1.0 ) {
307                 POINT org;
308                 ::GetBrushOrgEx( hdc, &org );
309                 ::SetStretchBltMode( hdc, HALFTONE );
310                 ::SetBrushOrgEx( hdc, org.x, org.y, NULL );
311         }
312         else {
313                 ::SetStretchBltMode( hdc, COLORONCOLOR );
314         }
315
316         HDC hdcMem = ::CreateCompatibleDC( hdc );
317         ::SaveDC( hdcMem );
318
319         ::SelectObject( hdcMem, mapImage->bitmapHandle() );
320
321         const POINT mapTopLeft = mapOriginInView();
322
323         int xDrawOrigin, yDrawOrigin;
324         xDrawOrigin = mapTopLeft.x;
325         yDrawOrigin = mapTopLeft.y;
326
327         if ( 0 < xDrawOrigin ) {
328                 xDrawOrigin = (xDrawOrigin % mapSize.cx) - mapSize.cx;
329         }
330         const int xInitial = xDrawOrigin;       // \8d\92[\82Ì\95`\89æ\8aJ\8enx\8dÀ\95W
331         int drawn = xInitial;                           // \95`\89æ\8dÏ\82Ý\8dÀ\95W
332
333         // \90¢\8aE\92n\90}\82ð\89¡\82É\95À\82×\82Ä\95`\89æ
334         // \81i\95`\89æ\8dÅ\93K\89»\82Í\8fÈ\97ª\81j
335         while ( drawn < m_viewSize.cx ) {
336                 ::StretchBlt( hdc,
337                         xDrawOrigin, yDrawOrigin,
338                         mapSize.cx, mapSize.cy,
339                         hdcMem,
340                         0, 0,
341                         mapImage->width(), mapImage->height(),
342                         SRCCOPY );
343
344                 xDrawOrigin += mapSize.cx;
345                 drawn += mapSize.cx;
346         }
347
348
349         const POINT shipPointOffset = drawOffsetFromWorldCoord( m_shipPointInWorld );
350
351         // \90j\98H\97\\91ª\90ü\82ð\95`\89æ
352         if ( shipVector.length() != 0.0 && m_shipVectorLineEnabled ) {
353                 const int penWidth = max( 1, int( 1 * m_viewScale ) );
354                 HPEN courseLinePen = ::CreatePen( PS_SOLID, penWidth, RGB( 255, 0, 255 ) );
355                 HGDIOBJ oldPen = ::SelectObject( hdc, courseLinePen );
356
357                 const LONG k_lineLength = k_worldHeight;
358                 const POINT reachPointOffset = drawOffsetFromWorldCoord(
359                         shipVector.pointFromOriginWithLength( m_shipPointInWorld, k_lineLength )
360                         );
361
362                 // \8c©\82¦\82Ä\82é\92n\90}\89æ\91\9c\82Ì\95ª\82¾\82¯\95`\89æ\82·\82é
363                 drawn = xInitial;
364                 xDrawOrigin = xInitial;
365                 while ( drawn < m_viewSize.cx ) {
366                         const POINT shipPointInView = {
367                                 xDrawOrigin + shipPointOffset.x,
368                                 yDrawOrigin + shipPointOffset.y
369                         };
370                         const POINT reachPointInView = {
371                                 xDrawOrigin + reachPointOffset.x,
372                                 yDrawOrigin + reachPointOffset.y
373                         };
374                         ::MoveToEx( hdc, shipPointInView.x, shipPointInView.y, NULL );
375                         ::LineTo( hdc, reachPointInView.x, reachPointInView.y );
376                         xDrawOrigin += mapSize.cx;
377                         drawn += mapSize.cx;
378                 }
379
380                 ::SelectObject( hdc, oldPen );
381                 ::DeleteObject( courseLinePen );
382         }
383
384         // \8e©\91D\82Ì\88Ê\92u\82ð\95`\89æ
385         const SIZE shipMarkSize = { 6, 6 };
386         HBRUSH shipBrush = ::CreateSolidBrush( RGB( 51, 238, 153 ) );
387         HGDIOBJ prevBrush = ::SelectObject( hdc, shipBrush );
388
389         // \8c©\82¦\82Ä\82é\92n\90}\89æ\91\9c\82Ì\95ª\82¾\82¯\95`\89æ\82·\82é
390         drawn = xInitial;
391         xDrawOrigin = xInitial;
392         while ( drawn < m_viewSize.cx ) {
393                 ::Ellipse( hdc,
394                         xDrawOrigin + shipPointOffset.x - shipMarkSize.cx / 2,
395                         yDrawOrigin + shipPointOffset.y - shipMarkSize.cy / 2,
396                         xDrawOrigin + shipPointOffset.x + shipMarkSize.cx,
397                         yDrawOrigin + shipPointOffset.y + shipMarkSize.cy );
398
399                 xDrawOrigin += mapSize.cx;
400                 drawn += mapSize.cx;
401         }
402         ::SelectObject( hdc, prevBrush );
403         ::DeleteObject( shipBrush );
404
405
406         ::RestoreDC( hdcMem, -1 );
407         ::DeleteDC( hdcMem );
408         ::RestoreDC( hdc, -1 );
409 }
410
411
412 void GVORenderer::drawSpeedMeter( HDC hdc, double shipVelocity )
413 {
414         const double velocity = s_velocityByKnot( shipVelocity );
415         wchar_t buf[4096] = { 0 };
416         swprintf( buf, _countof( buf ), L"%.2f kt", velocity );
417
418         RECT rc = { 0 };
419         ::DrawText( hdc, buf, -1, &rc, DT_SINGLELINE | DT_RIGHT | DT_TOP | DT_CALCRECT );
420         const int width = rc.right - rc.left;
421         rc.left = m_viewSize.cx - width;
422         rc.right = rc.left + width;
423         ::DrawText( hdc, buf, -1, &rc, DT_SINGLELINE | DT_RIGHT | DT_TOP );
424 }