source: ps/trunk/source/gui/ObjectTypes/CMiniMap.cpp@ 25254

Last change on this file since 25254 was 25254, checked in by Vladislav Belov, 3 years ago

Fixes warnings after minimap changes in rP25241.

  • Property svn:eol-style set to native
File size: 24.2 KB
Line 
1/* Copyright (C) 2021 Wildfire Games.
2 * This file is part of 0 A.D.
3 *
4 * 0 A.D. is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * 0 A.D. is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "precompiled.h"
19
20#include "CMiniMap.h"
21
22#include "graphics/GameView.h"
23#include "graphics/LOSTexture.h"
24#include "graphics/MiniPatch.h"
25#include "graphics/Terrain.h"
26#include "graphics/TerrainTextureEntry.h"
27#include "graphics/TerrainTextureManager.h"
28#include "graphics/TerritoryTexture.h"
29#include "gui/CGUI.h"
30#include "gui/GUIManager.h"
31#include "gui/GUIMatrix.h"
32#include "lib/bits.h"
33#include "lib/external_libraries/libsdl.h"
34#include "lib/ogl.h"
35#include "lib/timer.h"
36#include "ps/ConfigDB.h"
37#include "ps/Filesystem.h"
38#include "ps/Game.h"
39#include "ps/GameSetup/Config.h"
40#include "ps/Profile.h"
41#include "ps/World.h"
42#include "ps/XML/Xeromyces.h"
43#include "renderer/Renderer.h"
44#include "renderer/RenderingOptions.h"
45#include "renderer/WaterManager.h"
46#include "scriptinterface/ScriptInterface.h"
47#include "simulation2/Simulation2.h"
48#include "simulation2/components/ICmpMinimap.h"
49#include "simulation2/components/ICmpRangeManager.h"
50#include "simulation2/helpers/Los.h"
51#include "simulation2/system/ParamNode.h"
52
53#include <array>
54#include <cmath>
55#include <vector>
56
57extern bool g_GameRestarted;
58
59namespace
60{
61
62// Set max drawn entities to UINT16_MAX for now, which is more than enough
63// TODO: we should be cleverer about drawing them to reduce clutter
64const u16 MAX_ENTITIES_DRAWN = 65535;
65
66unsigned int ScaleColor(unsigned int color, float x)
67{
68 unsigned int r = unsigned(float(color & 0xff) * x);
69 unsigned int g = unsigned(float((color>>8) & 0xff) * x);
70 unsigned int b = unsigned(float((color>>16) & 0xff) * x);
71 return (0xff000000 | b | g<<8 | r<<16);
72}
73
74// Adds segments pieces lying inside the circle to lines.
75void CropPointsByCircle(const std::array<CVector3D, 4>& points, const CVector3D& center, const float radius, std::vector<CVector3D>* lines)
76{
77 constexpr float EPS = 1e-3f;
78 lines->reserve(points.size() * 2);
79 for (size_t idx = 0; idx < points.size(); ++idx)
80 {
81 const CVector3D& currentPoint = points[idx];
82 const CVector3D& nextPoint = points[(idx + 1) % points.size()];
83 const CVector3D direction = (nextPoint - currentPoint).Normalized();
84 const CVector3D normal(direction.Z, 0.0f, -direction.X);
85 const float offset = normal.Dot(currentPoint) - normal.Dot(center);
86 // We need to have lines only inside the circle.
87 if (std::abs(offset) + EPS >= radius)
88 continue;
89 const CVector3D closestPoint = center + normal * offset;
90 const float halfChordLength = sqrt(radius * radius - offset * offset);
91 const CVector3D intersectionA = closestPoint - direction * halfChordLength;
92 const CVector3D intersectionB = closestPoint + direction * halfChordLength;
93 // We have no intersection if the segment is lying outside of the circle.
94 if (direction.Dot(currentPoint) + EPS > direction.Dot(intersectionB) ||
95 direction.Dot(nextPoint) - EPS < direction.Dot(intersectionA))
96 continue;
97
98 lines->emplace_back(
99 direction.Dot(currentPoint) > direction.Dot(intersectionA) ? currentPoint : intersectionA);
100 lines->emplace_back(
101 direction.Dot(nextPoint) < direction.Dot(intersectionB) ? nextPoint : intersectionB);
102 }
103}
104
105} // anonymous namespace
106
107const CStr CMiniMap::EventNameWorldClick = "WorldClick";
108
109CMiniMap::CMiniMap(CGUI& pGUI) :
110 IGUIObject(pGUI),
111 m_TerrainTexture(0), m_TerrainData(0), m_MapSize(0), m_Terrain(0), m_TerrainDirty(true), m_MapScale(1.f),
112 m_EntitiesDrawn(0), m_IndexArray(GL_STATIC_DRAW), m_VertexArray(GL_DYNAMIC_DRAW), m_Mask(false),
113 m_NextBlinkTime(0.0), m_PingDuration(25.0), m_BlinkState(false), m_WaterHeight(0.0)
114{
115 RegisterSetting("mask", m_Mask);
116
117 m_Clicking = false;
118 m_MouseHovering = false;
119
120 // Register Relax NG validator
121 CXeromyces::AddValidator(g_VFS, "pathfinder", "simulation/data/pathfinder.rng");
122
123 m_ShallowPassageHeight = GetShallowPassageHeight();
124
125 m_AttributePos.type = GL_FLOAT;
126 m_AttributePos.elems = 2;
127 m_VertexArray.AddAttribute(&m_AttributePos);
128
129 m_AttributeColor.type = GL_UNSIGNED_BYTE;
130 m_AttributeColor.elems = 4;
131 m_VertexArray.AddAttribute(&m_AttributeColor);
132
133 m_VertexArray.SetNumVertices(MAX_ENTITIES_DRAWN);
134 m_VertexArray.Layout();
135
136 m_IndexArray.SetNumVertices(MAX_ENTITIES_DRAWN);
137 m_IndexArray.Layout();
138 VertexArrayIterator<u16> index = m_IndexArray.GetIterator();
139 for (u16 i = 0; i < MAX_ENTITIES_DRAWN; ++i)
140 *index++ = i;
141 m_IndexArray.Upload();
142 m_IndexArray.FreeBackingStore();
143
144
145 VertexArrayIterator<float[2]> attrPos = m_AttributePos.GetIterator<float[2]>();
146 VertexArrayIterator<u8[4]> attrColor = m_AttributeColor.GetIterator<u8[4]>();
147 for (u16 i = 0; i < MAX_ENTITIES_DRAWN; ++i)
148 {
149 (*attrColor)[0] = 0;
150 (*attrColor)[1] = 0;
151 (*attrColor)[2] = 0;
152 (*attrColor)[3] = 0;
153 ++attrColor;
154
155 (*attrPos)[0] = -10000.0f;
156 (*attrPos)[1] = -10000.0f;
157
158 ++attrPos;
159
160 }
161 m_VertexArray.Upload();
162
163 double blinkDuration = 1.0;
164
165 // Tests won't have config initialised
166 if (CConfigDB::IsInitialised())
167 {
168 CFG_GET_VAL("gui.session.minimap.pingduration", m_PingDuration);
169 CFG_GET_VAL("gui.session.minimap.blinkduration", blinkDuration);
170 }
171 m_HalfBlinkDuration = blinkDuration/2;
172}
173
174CMiniMap::~CMiniMap()
175{
176 Destroy();
177}
178
179void CMiniMap::HandleMessage(SGUIMessage& Message)
180{
181 IGUIObject::HandleMessage(Message);
182 switch (Message.type)
183 {
184 case GUIM_MOUSE_PRESS_LEFT:
185 if (m_MouseHovering)
186 {
187 if (!CMiniMap::FireWorldClickEvent(SDL_BUTTON_LEFT, 1))
188 {
189 SetCameraPos();
190 m_Clicking = true;
191 }
192 }
193 break;
194 case GUIM_MOUSE_RELEASE_LEFT:
195 if (m_MouseHovering && m_Clicking)
196 SetCameraPos();
197 m_Clicking = false;
198 break;
199 case GUIM_MOUSE_DBLCLICK_LEFT:
200 if (m_MouseHovering && m_Clicking)
201 SetCameraPos();
202 m_Clicking = false;
203 break;
204 case GUIM_MOUSE_ENTER:
205 m_MouseHovering = true;
206 break;
207 case GUIM_MOUSE_LEAVE:
208 m_Clicking = false;
209 m_MouseHovering = false;
210 break;
211 case GUIM_MOUSE_RELEASE_RIGHT:
212 CMiniMap::FireWorldClickEvent(SDL_BUTTON_RIGHT, 1);
213 break;
214 case GUIM_MOUSE_DBLCLICK_RIGHT:
215 CMiniMap::FireWorldClickEvent(SDL_BUTTON_RIGHT, 2);
216 break;
217 case GUIM_MOUSE_MOTION:
218 if (m_MouseHovering && m_Clicking)
219 SetCameraPos();
220 break;
221 case GUIM_MOUSE_WHEEL_DOWN:
222 case GUIM_MOUSE_WHEEL_UP:
223 Message.Skip();
224 break;
225
226 default:
227 break;
228 }
229}
230
231bool CMiniMap::IsMouseOver() const
232{
233 // Get the mouse position.
234 const CVector2D& mousePos = m_pGUI.GetMousePos();
235 // Get the position of the center of the minimap.
236 CVector2D minimapCenter = CVector2D(m_CachedActualSize.left + m_CachedActualSize.GetWidth() / 2.0, m_CachedActualSize.bottom - m_CachedActualSize.GetHeight() / 2.0);
237 // Take the magnitude of the difference of the mouse position and minimap center.
238 double distFromCenter = sqrt(pow((mousePos.X - minimapCenter.X), 2) + pow((mousePos.Y - minimapCenter.Y), 2));
239 // If the distance is less then the radius of the minimap (half the width) the mouse is over the minimap.
240 if (distFromCenter < m_CachedActualSize.GetWidth() / 2.0)
241 return true;
242 else
243 return false;
244}
245
246void CMiniMap::GetMouseWorldCoordinates(float& x, float& z) const
247{
248 // Determine X and Z according to proportion of mouse position and minimap
249
250 const CVector2D& mousePos = m_pGUI.GetMousePos();
251
252 float px = (mousePos.X - m_CachedActualSize.left) / m_CachedActualSize.GetWidth();
253 float py = (m_CachedActualSize.bottom - mousePos.Y) / m_CachedActualSize.GetHeight();
254
255 float angle = GetAngle();
256
257 // Scale world coordinates for shrunken square map
258 x = TERRAIN_TILE_SIZE * m_MapSize * (m_MapScale * (cos(angle)*(px-0.5) - sin(angle)*(py-0.5)) + 0.5);
259 z = TERRAIN_TILE_SIZE * m_MapSize * (m_MapScale * (cos(angle)*(py-0.5) + sin(angle)*(px-0.5)) + 0.5);
260}
261
262void CMiniMap::SetCameraPos()
263{
264 CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
265
266 CVector3D target;
267 GetMouseWorldCoordinates(target.X, target.Z);
268 target.Y = terrain->GetExactGroundLevel(target.X, target.Z);
269 g_Game->GetView()->MoveCameraTarget(target);
270}
271
272float CMiniMap::GetAngle() const
273{
274 CVector3D cameraIn = m_Camera->GetOrientation().GetIn();
275 return -atan2(cameraIn.X, cameraIn.Z);
276}
277
278bool CMiniMap::FireWorldClickEvent(int button, int UNUSED(clicks))
279{
280 ScriptRequest rq(g_GUI->GetActiveGUI()->GetScriptInterface());
281
282 float x, z;
283 GetMouseWorldCoordinates(x, z);
284
285 JS::RootedValue coords(rq.cx);
286 ScriptInterface::CreateObject(rq, &coords, "x", x, "z", z);
287
288 JS::RootedValue buttonJs(rq.cx);
289 ScriptInterface::ToJSVal(rq, &buttonJs, button);
290
291 JS::RootedValueVector paramData(rq.cx);
292 ignore_result(paramData.append(coords));
293 ignore_result(paramData.append(buttonJs));
294
295 return ScriptEventWithReturn(EventNameWorldClick, paramData);
296}
297
298// This sets up and draws the rectangle on the minimap
299// which represents the view of the camera in the world.
300void CMiniMap::DrawViewRect(const CMatrix3D& transform) const
301{
302 // Compute the camera frustum intersected with a fixed-height plane.
303 // Use the water height as a fixed base height, which should be the lowest we can go
304 float h = g_Renderer.GetWaterManager()->m_WaterHeight;
305 const float width = m_CachedActualSize.GetWidth();
306 const float height = m_CachedActualSize.GetHeight();
307 const float invTileMapSize = 1.0f / float(TERRAIN_TILE_SIZE * m_MapSize);
308
309 const std::array<CVector3D, 4> hitPoints = {
310 m_Camera->GetWorldCoordinates(0, g_Renderer.GetHeight(), h),
311 m_Camera->GetWorldCoordinates(g_Renderer.GetWidth(), g_Renderer.GetHeight(), h),
312 m_Camera->GetWorldCoordinates(g_Renderer.GetWidth(), 0, h),
313 m_Camera->GetWorldCoordinates(0, 0, h)
314 };
315
316 std::vector<CVector3D> lines;
317 // We need to prevent drawing view bounds out of the map.
318 const float halfMapSize = static_cast<float>((m_MapSize - 1) * TERRAIN_TILE_SIZE) * 0.5f;
319 CropPointsByCircle(hitPoints, CVector3D(halfMapSize, 0.0f, halfMapSize), halfMapSize * m_MapScale, &lines);
320 if (lines.empty())
321 return;
322
323 std::vector<float> vertices;
324 vertices.reserve(lines.size() * 2);
325 for (const CVector3D& point : lines)
326 {
327 // Convert to minimap space.
328 vertices.emplace_back(width * point.X * invTileMapSize);
329 vertices.emplace_back(-(height * point.Z * invTileMapSize));
330 }
331
332 // Enable Scissoring to restrict the rectangle to only the minimap.
333 glScissor(
334 m_CachedActualSize.left * g_GuiScale,
335 g_Renderer.GetHeight() - m_CachedActualSize.bottom * g_GuiScale,
336 width * g_GuiScale,
337 height * g_GuiScale);
338 glEnable(GL_SCISSOR_TEST);
339 glLineWidth(2.0f);
340
341 CShaderDefines lineDefines;
342 lineDefines.Add(str_MINIMAP_LINE, str_1);
343 CShaderTechniquePtr tech = g_Renderer.GetShaderManager().LoadEffect(str_minimap, g_Renderer.GetSystemShaderDefines(), lineDefines);
344 tech->BeginPass();
345 CShaderProgramPtr shader = tech->GetShader();
346 shader->Uniform(str_transform, transform);
347 shader->Uniform(str_color, 1.0f, 0.3f, 0.3f, 1.0f);
348
349 shader->VertexPointer(2, GL_FLOAT, 0, vertices.data());
350 shader->AssertPointersBound();
351
352 if (!g_Renderer.m_SkipSubmit)
353 glDrawArrays(GL_LINES, 0, vertices.size() / 2);
354
355 tech->EndPass();
356
357 glLineWidth(1.0f);
358 glDisable(GL_SCISSOR_TEST);
359}
360
361struct MinimapUnitVertex
362{
363 // This struct is copyable for convenience and because to move is to copy for primitives.
364 u8 r, g, b, a;
365 float x, y;
366};
367
368// Adds a vertex to the passed VertexArray
369static void inline addVertex(const MinimapUnitVertex& v,
370 VertexArrayIterator<u8[4]>& attrColor,
371 VertexArrayIterator<float[2]>& attrPos)
372{
373 (*attrColor)[0] = v.r;
374 (*attrColor)[1] = v.g;
375 (*attrColor)[2] = v.b;
376 (*attrColor)[3] = v.a;
377 ++attrColor;
378
379 (*attrPos)[0] = v.x;
380 (*attrPos)[1] = v.y;
381
382 ++attrPos;
383}
384
385
386void CMiniMap::DrawTexture(CShaderProgramPtr shader, float coordMax, float angle, float x, float y, float x2, float y2, float z) const
387{
388 // Rotate the texture coordinates (0,0)-(coordMax,coordMax) around their center point (m,m)
389 // Scale square maps to fit in circular minimap area
390 const float s = sin(angle) * m_MapScale;
391 const float c = cos(angle) * m_MapScale;
392 const float m = coordMax / 2.f;
393
394 float quadTex[] = {
395 m*(-c + s + 1.f), m*(-c + -s + 1.f),
396 m*(c + s + 1.f), m*(-c + s + 1.f),
397 m*(c + -s + 1.f), m*(c + s + 1.f),
398
399 m*(c + -s + 1.f), m*(c + s + 1.f),
400 m*(-c + -s + 1.f), m*(c + -s + 1.f),
401 m*(-c + s + 1.f), m*(-c + -s + 1.f)
402 };
403 float quadVerts[] = {
404 x, y, z,
405 x2, y, z,
406 x2, y2, z,
407
408 x2, y2, z,
409 x, y2, z,
410 x, y, z
411 };
412
413 shader->TexCoordPointer(GL_TEXTURE0, 2, GL_FLOAT, 0, quadTex);
414 shader->VertexPointer(3, GL_FLOAT, 0, quadVerts);
415 shader->AssertPointersBound();
416
417 if (!g_Renderer.m_SkipSubmit)
418 glDrawArrays(GL_TRIANGLES, 0, 6);
419}
420
421// TODO: render the minimap in a framebuffer and just draw the frambuffer texture
422// most of the time, updating the framebuffer twice a frame.
423// Here it updates as ping-pong either texture or vertex array each sec to lower gpu stalling
424// (those operations cause a gpu sync, which slows down the way gpu works)
425void CMiniMap::Draw()
426{
427 PROFILE3("render minimap");
428
429 // The terrain isn't actually initialized until the map is loaded, which
430 // happens when the game is started, so abort until then.
431 if (!g_Game || !g_Game->IsGameStarted())
432 return;
433
434 CSimulation2* sim = g_Game->GetSimulation2();
435 CmpPtr<ICmpRangeManager> cmpRangeManager(*sim, SYSTEM_ENTITY);
436 ENSURE(cmpRangeManager);
437
438 // Set our globals in case they hadn't been set before
439 m_Camera = g_Game->GetView()->GetCamera();
440 m_Terrain = g_Game->GetWorld()->GetTerrain();
441 m_Width = (u32)(m_CachedActualSize.right - m_CachedActualSize.left);
442 m_Height = (u32)(m_CachedActualSize.bottom - m_CachedActualSize.top);
443 m_MapSize = m_Terrain->GetVerticesPerSide();
444 m_TextureSize = (GLsizei)round_up_to_pow2((size_t)m_MapSize);
445 m_MapScale = (cmpRangeManager->GetLosCircular() ? 1.f : 1.414f);
446
447 if (!m_TerrainTexture || g_GameRestarted)
448 CreateTextures();
449
450
451 // only update 2x / second
452 // (note: since units only move a few pixels per second on the minimap,
453 // we can get away with infrequent updates; this is slow)
454 // TODO: Update all but camera at same speed as simulation
455 static double last_time;
456 const double cur_time = timer_Time();
457 const bool doUpdate = cur_time - last_time > 0.5;
458 if (doUpdate)
459 {
460 last_time = cur_time;
461 if (m_TerrainDirty || m_WaterHeight != g_Renderer.GetWaterManager()->m_WaterHeight)
462 RebuildTerrainTexture();
463 }
464
465 const float x = m_CachedActualSize.left, y = m_CachedActualSize.bottom;
466 const float x2 = m_CachedActualSize.right, y2 = m_CachedActualSize.top;
467 const float z = GetBufferedZ();
468 const float texCoordMax = (float)(m_MapSize - 1) / (float)m_TextureSize;
469 const float angle = GetAngle();
470 const float unitScale = (cmpRangeManager->GetLosCircular() ? 1.f : m_MapScale/2.f);
471
472 CLOSTexture& losTexture = g_Game->GetView()->GetLOSTexture();
473
474 CShaderProgramPtr shader;
475 CShaderTechniquePtr tech;
476
477 CShaderDefines baseDefines;
478 baseDefines.Add(str_MINIMAP_BASE, str_1);
479 if (m_Mask)
480 baseDefines.Add(str_MINIMAP_MASK, str_1);
481
482 tech = g_Renderer.GetShaderManager().LoadEffect(str_minimap, g_Renderer.GetSystemShaderDefines(), baseDefines);
483 tech->BeginPass();
484 shader = tech->GetShader();
485
486 // Draw the main textured quad
487 shader->BindTexture(str_baseTex, m_TerrainTexture);
488 if (m_Mask)
489 {
490 shader->BindTexture(str_maskTex, losTexture.GetTexture());
491 CMatrix3D maskTextureTransform = *losTexture.GetMinimapTextureMatrix();
492 // We need to have texture coordinates in the same coordinate space.
493 const float scale = 1.0f / texCoordMax;
494 maskTextureTransform.Scale(scale, scale, 1.0f);
495 shader->Uniform(str_maskTextureTransform, maskTextureTransform);
496 }
497 const CMatrix3D baseTransform = GetDefaultGuiMatrix();
498 CMatrix3D baseTextureTransform;
499 baseTextureTransform.SetIdentity();
500 shader->Uniform(str_transform, baseTransform);
501 shader->Uniform(str_textureTransform, baseTextureTransform);
502
503 if (m_Mask)
504 {
505 glEnable(GL_BLEND);
506 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
507 }
508
509 DrawTexture(shader, texCoordMax, angle, x, y, x2, y2, z);
510
511 if (!m_Mask)
512 {
513 glEnable(GL_BLEND);
514 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
515 }
516
517 // Draw territory boundaries
518 CTerritoryTexture& territoryTexture = g_Game->GetView()->GetTerritoryTexture();
519
520 shader->BindTexture(str_baseTex, territoryTexture.GetTexture());
521 if (m_Mask)
522 {
523 shader->BindTexture(str_maskTex, losTexture.GetTexture());
524 shader->Uniform(str_maskTextureTransform, *losTexture.GetMinimapTextureMatrix());
525 }
526 const CMatrix3D* territoryTransform = territoryTexture.GetMinimapTextureMatrix();
527 shader->Uniform(str_transform, baseTransform);
528 shader->Uniform(str_textureTransform, *territoryTransform);
529
530 DrawTexture(shader, 1.0f, angle, x, y, x2, y2, z);
531 tech->EndPass();
532
533 // Draw the LOS quad in black, using alpha values from the LOS texture
534 if (!m_Mask)
535 {
536 CShaderDefines losDefines;
537 losDefines.Add(str_MINIMAP_LOS, str_1);
538 tech = g_Renderer.GetShaderManager().LoadEffect(str_minimap, g_Renderer.GetSystemShaderDefines(), losDefines);
539 tech->BeginPass();
540 shader = tech->GetShader();
541 shader->BindTexture(str_baseTex, losTexture.GetTexture());
542
543 const CMatrix3D* losTransform = losTexture.GetMinimapTextureMatrix();
544 shader->Uniform(str_transform, baseTransform);
545 shader->Uniform(str_textureTransform, *losTransform);
546
547 DrawTexture(shader, 1.0f, angle, x, y, x2, y2, z);
548 tech->EndPass();
549 }
550
551 glDisable(GL_BLEND);
552
553 PROFILE_START("minimap units");
554
555 CShaderDefines pointDefines;
556 pointDefines.Add(str_MINIMAP_POINT, str_1);
557 tech = g_Renderer.GetShaderManager().LoadEffect(str_minimap, g_Renderer.GetSystemShaderDefines(), pointDefines);
558 tech->BeginPass();
559 shader = tech->GetShader();
560 shader->Uniform(str_transform, baseTransform);
561 shader->Uniform(str_pointSize, 3.f);
562
563 CMatrix3D unitMatrix;
564 unitMatrix.SetIdentity();
565 // Center the minimap on the origin of the axis of rotation.
566 unitMatrix.Translate(-(x2 - x) / 2.f, -(y2 - y) / 2.f, 0.f);
567 // Rotate the map.
568 unitMatrix.RotateZ(angle);
569 // Scale square maps to fit.
570 unitMatrix.Scale(unitScale, unitScale, 1.f);
571 // Move the minimap back to it's starting position.
572 unitMatrix.Translate((x2 - x) / 2.f, (y2 - y) / 2.f, 0.f);
573 // Move the minimap to it's final location.
574 unitMatrix.Translate(x, y, z);
575 // Apply the gui matrix.
576 unitMatrix *= GetDefaultGuiMatrix();
577 // Load the transform into the shader.
578 shader->Uniform(str_transform, unitMatrix);
579
580 const float sx = (float)m_Width / ((m_MapSize - 1) * TERRAIN_TILE_SIZE);
581 const float sy = (float)m_Height / ((m_MapSize - 1) * TERRAIN_TILE_SIZE);
582
583 CSimulation2::InterfaceList ents = sim->GetEntitiesWithInterface(IID_Minimap);
584
585 if (doUpdate)
586 {
587 VertexArrayIterator<float[2]> attrPos = m_AttributePos.GetIterator<float[2]>();
588 VertexArrayIterator<u8[4]> attrColor = m_AttributeColor.GetIterator<u8[4]>();
589
590 m_EntitiesDrawn = 0;
591 MinimapUnitVertex v;
592 std::vector<MinimapUnitVertex> pingingVertices;
593 pingingVertices.reserve(MAX_ENTITIES_DRAWN / 2);
594
595 if (cur_time > m_NextBlinkTime)
596 {
597 m_BlinkState = !m_BlinkState;
598 m_NextBlinkTime = cur_time + m_HalfBlinkDuration;
599 }
600
601 entity_pos_t posX, posZ;
602 for (CSimulation2::InterfaceList::const_iterator it = ents.begin(); it != ents.end(); ++it)
603 {
604 ICmpMinimap* cmpMinimap = static_cast<ICmpMinimap*>(it->second);
605 if (cmpMinimap->GetRenderData(v.r, v.g, v.b, posX, posZ))
606 {
607 LosVisibility vis = cmpRangeManager->GetLosVisibility(it->first, g_Game->GetSimulation2()->GetSimContext().GetCurrentDisplayedPlayer());
608 if (vis != LosVisibility::HIDDEN)
609 {
610 v.a = 255;
611 v.x = posX.ToFloat() * sx;
612 v.y = -posZ.ToFloat() * sy;
613
614 // Check minimap pinging to indicate something
615 if (m_BlinkState && cmpMinimap->CheckPing(cur_time, m_PingDuration))
616 {
617 v.r = 255; // ping color is white
618 v.g = 255;
619 v.b = 255;
620 pingingVertices.push_back(v);
621 }
622 else
623 {
624 addVertex(v, attrColor, attrPos);
625 ++m_EntitiesDrawn;
626 }
627 }
628 }
629 }
630
631 // Add the pinged vertices at the end, so they are drawn on top
632 for (const MinimapUnitVertex& vertex : pingingVertices)
633 {
634 addVertex(vertex, attrColor, attrPos);
635 ++m_EntitiesDrawn;
636 }
637
638 ENSURE(m_EntitiesDrawn < MAX_ENTITIES_DRAWN);
639 m_VertexArray.Upload();
640 }
641
642 m_VertexArray.PrepareForRendering();
643
644 if (m_EntitiesDrawn > 0)
645 {
646#if !CONFIG2_GLES
647 glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
648#endif
649
650 u8* indexBase = m_IndexArray.Bind();
651 u8* base = m_VertexArray.Bind();
652 const GLsizei stride = (GLsizei)m_VertexArray.GetStride();
653
654 shader->VertexPointer(2, GL_FLOAT, stride, base + m_AttributePos.offset);
655 shader->ColorPointer(4, GL_UNSIGNED_BYTE, stride, base + m_AttributeColor.offset);
656 shader->AssertPointersBound();
657
658 if (!g_Renderer.m_SkipSubmit)
659 glDrawElements(GL_POINTS, (GLsizei)(m_EntitiesDrawn), GL_UNSIGNED_SHORT, indexBase);
660
661 g_Renderer.GetStats().m_DrawCalls++;
662 CVertexBuffer::Unbind();
663
664#if !CONFIG2_GLES
665 glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);
666#endif
667 }
668
669 tech->EndPass();
670
671 DrawViewRect(unitMatrix);
672
673 PROFILE_END("minimap units");
674}
675
676void CMiniMap::CreateTextures()
677{
678 Destroy();
679
680 // Create terrain texture
681 glGenTextures(1, &m_TerrainTexture);
682 g_Renderer.BindTexture(0, m_TerrainTexture);
683
684 // Initialise texture with solid black, for the areas we don't
685 // overwrite with glTexSubImage2D later
686 u32* texData = new u32[m_TextureSize * m_TextureSize];
687 for (ssize_t i = 0; i < m_TextureSize * m_TextureSize; ++i)
688 texData[i] = 0xFF000000;
689 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_TextureSize, m_TextureSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, texData);
690 delete[] texData;
691
692 m_TerrainData = new u32[(m_MapSize - 1) * (m_MapSize - 1)];
693 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
694 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
695 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
696 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
697
698 // Rebuild and upload both of them
699 RebuildTerrainTexture();
700}
701
702
703void CMiniMap::RebuildTerrainTexture()
704{
705 u32 x = 0;
706 u32 y = 0;
707 u32 w = m_MapSize - 1;
708 u32 h = m_MapSize - 1;
709 m_WaterHeight = g_Renderer.GetWaterManager()->m_WaterHeight;
710
711 m_TerrainDirty = false;
712
713 for (u32 j = 0; j < h; ++j)
714 {
715 u32* dataPtr = m_TerrainData + ((y + j) * (m_MapSize - 1)) + x;
716 for (u32 i = 0; i < w; ++i)
717 {
718 float avgHeight = ( m_Terrain->GetVertexGroundLevel((int)i, (int)j)
719 + m_Terrain->GetVertexGroundLevel((int)i+1, (int)j)
720 + m_Terrain->GetVertexGroundLevel((int)i, (int)j+1)
721 + m_Terrain->GetVertexGroundLevel((int)i+1, (int)j+1)
722 ) / 4.0f;
723
724 if (avgHeight < m_WaterHeight && avgHeight > m_WaterHeight - m_ShallowPassageHeight)
725 {
726 // shallow water
727 *dataPtr++ = 0xffc09870;
728 }
729 else if (avgHeight < m_WaterHeight)
730 {
731 // Set water as constant color for consistency on different maps
732 *dataPtr++ = 0xffa07850;
733 }
734 else
735 {
736 int hmap = ((int)m_Terrain->GetHeightMap()[(y + j) * m_MapSize + x + i]) >> 8;
737 int val = (hmap / 3) + 170;
738
739 u32 color = 0xFFFFFFFF;
740
741 CMiniPatch* mp = m_Terrain->GetTile(x + i, y + j);
742 if (mp)
743 {
744 CTerrainTextureEntry* tex = mp->GetTextureEntry();
745 if (tex)
746 {
747 // If the texture can't be loaded yet, set the dirty flags
748 // so we'll try regenerating the terrain texture again soon
749 if(!tex->GetTexture()->TryLoad())
750 m_TerrainDirty = true;
751
752 color = tex->GetBaseColor();
753 }
754 }
755
756 *dataPtr++ = ScaleColor(color, float(val) / 255.0f);
757 }
758 }
759 }
760
761 // Upload the texture
762 g_Renderer.BindTexture(0, m_TerrainTexture);
763 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_MapSize - 1, m_MapSize - 1, GL_RGBA, GL_UNSIGNED_BYTE, m_TerrainData);
764}
765
766void CMiniMap::Destroy()
767{
768 if (m_TerrainTexture)
769 {
770 glDeleteTextures(1, &m_TerrainTexture);
771 m_TerrainTexture = 0;
772 }
773
774 SAFE_ARRAY_DELETE(m_TerrainData);
775}
776
777// static
778float CMiniMap::GetShallowPassageHeight()
779{
780 float shallowPassageHeight = 0.0f;
781 CParamNode externalParamNode;
782 CParamNode::LoadXML(externalParamNode, L"simulation/data/pathfinder.xml", "pathfinder");
783 const CParamNode pathingSettings = externalParamNode.GetChild("Pathfinder").GetChild("PassabilityClasses");
784 if (pathingSettings.GetChild("default").IsOk() && pathingSettings.GetChild("default").GetChild("MaxWaterDepth").IsOk())
785 shallowPassageHeight = pathingSettings.GetChild("default").GetChild("MaxWaterDepth").ToFloat();
786 return shallowPassageHeight;
787}
Note: See TracBrowser for help on using the repository browser.