Don't crash while in the lobby when receiving an error IQ stanza without an error...
[0ad.git] / source / gui / MiniMap.cpp
blobab39655a30650d9bee3d0928ceefed1c339128c8
1 /* Copyright (C) 2017 Wildfire Games.
2 * This file is part of 0 A.D.
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.
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.
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/>.
18 #include "precompiled.h"
20 #include <math.h>
22 #include "MiniMap.h"
24 #include "graphics/GameView.h"
25 #include "graphics/LOSTexture.h"
26 #include "graphics/MiniPatch.h"
27 #include "graphics/Terrain.h"
28 #include "graphics/TerrainTextureEntry.h"
29 #include "graphics/TerrainTextureManager.h"
30 #include "graphics/TerritoryTexture.h"
31 #include "gui/GUI.h"
32 #include "gui/GUIManager.h"
33 #include "lib/bits.h"
34 #include "lib/external_libraries/libsdl.h"
35 #include "lib/ogl.h"
36 #include "lib/timer.h"
37 #include "ps/ConfigDB.h"
38 #include "ps/Filesystem.h"
39 #include "ps/Game.h"
40 #include "ps/GameSetup/Config.h"
41 #include "ps/Profile.h"
42 #include "ps/World.h"
43 #include "ps/XML/Xeromyces.h"
44 #include "renderer/Renderer.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/system/ParamNode.h"
51 bool g_GameRestarted = false;
53 // Set max drawn entities to UINT16_MAX for now, which is more than enough
54 // TODO: we should be cleverer about drawing them to reduce clutter
55 const u16 MAX_ENTITIES_DRAWN = 65535;
57 static unsigned int ScaleColor(unsigned int color, float x)
59 unsigned int r = unsigned(float(color & 0xff) * x);
60 unsigned int g = unsigned(float((color>>8) & 0xff) * x);
61 unsigned int b = unsigned(float((color>>16) & 0xff) * x);
62 return (0xff000000 | b | g<<8 | r<<16);
65 CMiniMap::CMiniMap() :
66 m_TerrainTexture(0), m_TerrainData(0), m_MapSize(0), m_Terrain(0), m_TerrainDirty(true), m_MapScale(1.f),
67 m_EntitiesDrawn(0), m_IndexArray(GL_STATIC_DRAW), m_VertexArray(GL_DYNAMIC_DRAW),
68 m_NextBlinkTime(0.0), m_PingDuration(25.0), m_BlinkState(false), m_WaterHeight(0.0)
70 AddSetting(GUIST_CColor, "fov_wedge_color");
71 AddSetting(GUIST_CStrW, "tooltip");
72 AddSetting(GUIST_CStr, "tooltip_style");
73 m_Clicking = false;
74 m_MouseHovering = false;
76 // Register Relax NG validator
77 CXeromyces::AddValidator(g_VFS, "pathfinder", "simulation/data/pathfinder.rng");
79 // Get the maximum height for unit passage in water.
80 CParamNode externalParamNode;
81 CParamNode::LoadXML(externalParamNode, L"simulation/data/pathfinder.xml", "pathfinder");
82 const CParamNode pathingSettings = externalParamNode.GetChild("Pathfinder").GetChild("PassabilityClasses");
83 if (pathingSettings.GetChild("default").IsOk() && pathingSettings.GetChild("default").GetChild("MaxWaterDepth").IsOk())
84 m_ShallowPassageHeight = pathingSettings.GetChild("default").GetChild("MaxWaterDepth").ToFloat();
85 else
86 m_ShallowPassageHeight = 0.0f;
88 m_AttributePos.type = GL_FLOAT;
89 m_AttributePos.elems = 2;
90 m_VertexArray.AddAttribute(&m_AttributePos);
92 m_AttributeColor.type = GL_UNSIGNED_BYTE;
93 m_AttributeColor.elems = 4;
94 m_VertexArray.AddAttribute(&m_AttributeColor);
96 m_VertexArray.SetNumVertices(MAX_ENTITIES_DRAWN);
97 m_VertexArray.Layout();
99 m_IndexArray.SetNumVertices(MAX_ENTITIES_DRAWN);
100 m_IndexArray.Layout();
101 VertexArrayIterator<u16> index = m_IndexArray.GetIterator();
102 for (u16 i = 0; i < MAX_ENTITIES_DRAWN; ++i)
103 *index++ = i;
104 m_IndexArray.Upload();
105 m_IndexArray.FreeBackingStore();
108 VertexArrayIterator<float[2]> attrPos = m_AttributePos.GetIterator<float[2]>();
109 VertexArrayIterator<u8[4]> attrColor = m_AttributeColor.GetIterator<u8[4]>();
110 for (u16 i = 0; i < MAX_ENTITIES_DRAWN; ++i)
112 (*attrColor)[0] = 0;
113 (*attrColor)[1] = 0;
114 (*attrColor)[2] = 0;
115 (*attrColor)[3] = 0;
116 ++attrColor;
118 (*attrPos)[0] = -10000.0f;
119 (*attrPos)[1] = -10000.0f;
121 ++attrPos;
124 m_VertexArray.Upload();
126 double blinkDuration = 1.0;
128 // Tests won't have config initialised
129 if (CConfigDB::IsInitialised())
131 CFG_GET_VAL("gui.session.minimap.pingduration", m_PingDuration);
132 CFG_GET_VAL("gui.session.minimap.blinkduration", blinkDuration);
134 m_HalfBlinkDuration = blinkDuration/2;
137 CMiniMap::~CMiniMap()
139 Destroy();
142 void CMiniMap::HandleMessage(SGUIMessage& Message)
144 switch (Message.type)
146 case GUIM_MOUSE_PRESS_LEFT:
147 if (m_MouseHovering)
149 SetCameraPos();
150 m_Clicking = true;
152 break;
153 case GUIM_MOUSE_RELEASE_LEFT:
154 if (m_MouseHovering && m_Clicking)
155 SetCameraPos();
156 m_Clicking = false;
157 break;
158 case GUIM_MOUSE_DBLCLICK_LEFT:
159 if (m_MouseHovering && m_Clicking)
160 SetCameraPos();
161 m_Clicking = false;
162 break;
163 case GUIM_MOUSE_ENTER:
164 m_MouseHovering = true;
165 break;
166 case GUIM_MOUSE_LEAVE:
167 m_Clicking = false;
168 m_MouseHovering = false;
169 break;
170 case GUIM_MOUSE_RELEASE_RIGHT:
171 CMiniMap::FireWorldClickEvent(SDL_BUTTON_RIGHT, 1);
172 break;
173 case GUIM_MOUSE_DBLCLICK_RIGHT:
174 CMiniMap::FireWorldClickEvent(SDL_BUTTON_RIGHT, 2);
175 break;
176 case GUIM_MOUSE_MOTION:
177 if (m_MouseHovering && m_Clicking)
178 SetCameraPos();
179 break;
180 case GUIM_MOUSE_WHEEL_DOWN:
181 case GUIM_MOUSE_WHEEL_UP:
182 Message.Skip();
183 break;
185 default:
186 break;
190 bool CMiniMap::MouseOver()
192 // Get the mouse position.
193 CPos mousePos = GetMousePos();
194 // Get the position of the center of the minimap.
195 CPos minimapCenter = CPos(m_CachedActualSize.left + m_CachedActualSize.GetWidth() / 2.0, m_CachedActualSize.bottom - m_CachedActualSize.GetHeight() / 2.0);
196 // Take the magnitude of the difference of the mouse position and minimap center.
197 double distFromCenter = sqrt(pow((mousePos.x - minimapCenter.x), 2) + pow((mousePos.y - minimapCenter.y), 2));
198 // If the distance is less then the radius of the minimap (half the width) the mouse is over the minimap.
199 if (distFromCenter < m_CachedActualSize.GetWidth() / 2.0)
200 return true;
201 else
202 return false;
205 void CMiniMap::GetMouseWorldCoordinates(float& x, float& z)
207 // Determine X and Z according to proportion of mouse position and minimap
209 CPos mousePos = GetMousePos();
211 float px = (mousePos.x - m_CachedActualSize.left) / m_CachedActualSize.GetWidth();
212 float py = (m_CachedActualSize.bottom - mousePos.y) / m_CachedActualSize.GetHeight();
214 float angle = GetAngle();
216 // Scale world coordinates for shrunken square map
217 x = TERRAIN_TILE_SIZE * m_MapSize * (m_MapScale * (cos(angle)*(px-0.5) - sin(angle)*(py-0.5)) + 0.5);
218 z = TERRAIN_TILE_SIZE * m_MapSize * (m_MapScale * (cos(angle)*(py-0.5) + sin(angle)*(px-0.5)) + 0.5);
221 void CMiniMap::SetCameraPos()
223 CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
225 CVector3D target;
226 GetMouseWorldCoordinates(target.X, target.Z);
227 target.Y = terrain->GetExactGroundLevel(target.X, target.Z);
228 g_Game->GetView()->MoveCameraTarget(target);
231 float CMiniMap::GetAngle()
233 CVector3D cameraIn = m_Camera->m_Orientation.GetIn();
234 return -atan2(cameraIn.X, cameraIn.Z);
237 void CMiniMap::FireWorldClickEvent(int UNUSED(button), int UNUSED(clicks))
239 JSContext* cx = g_GUI->GetActiveGUI()->GetScriptInterface()->GetContext();
240 JSAutoRequest rq(cx);
242 float x, z;
243 GetMouseWorldCoordinates(x, z);
245 JS::RootedValue coords(cx);
246 g_GUI->GetActiveGUI()->GetScriptInterface()->Eval("({})", &coords);
247 g_GUI->GetActiveGUI()->GetScriptInterface()->SetProperty(coords, "x", x, false);
248 g_GUI->GetActiveGUI()->GetScriptInterface()->SetProperty(coords, "z", z, false);
249 ScriptEvent("worldclick", coords);
252 // This sets up and draws the rectangle on the minimap
253 // which represents the view of the camera in the world.
254 void CMiniMap::DrawViewRect(CMatrix3D transform)
256 // Compute the camera frustum intersected with a fixed-height plane.
257 // Use the water height as a fixed base height, which should be the lowest we can go
258 float h = g_Renderer.GetWaterManager()->m_WaterHeight;
259 const float width = m_CachedActualSize.GetWidth();
260 const float height = m_CachedActualSize.GetHeight();
261 const float invTileMapSize = 1.0f / float(TERRAIN_TILE_SIZE * m_MapSize);
263 CVector3D hitPt[4];
264 hitPt[0] = m_Camera->GetWorldCoordinates(0, g_Renderer.GetHeight(), h);
265 hitPt[1] = m_Camera->GetWorldCoordinates(g_Renderer.GetWidth(), g_Renderer.GetHeight(), h);
266 hitPt[2] = m_Camera->GetWorldCoordinates(g_Renderer.GetWidth(), 0, h);
267 hitPt[3] = m_Camera->GetWorldCoordinates(0, 0, h);
269 float ViewRect[4][2];
270 for (int i = 0; i < 4; ++i)
272 // convert to minimap space
273 ViewRect[i][0] = (width * hitPt[i].X * invTileMapSize);
274 ViewRect[i][1] = (height * hitPt[i].Z * invTileMapSize);
277 float viewVerts[] = {
278 ViewRect[0][0], -ViewRect[0][1],
279 ViewRect[1][0], -ViewRect[1][1],
280 ViewRect[2][0], -ViewRect[2][1],
281 ViewRect[3][0], -ViewRect[3][1]
284 // Enable Scissoring to restrict the rectangle to only the minimap.
285 glScissor(
286 m_CachedActualSize.left * g_GuiScale,
287 g_Renderer.GetHeight() - m_CachedActualSize.bottom * g_GuiScale,
288 width * g_GuiScale,
289 height * g_GuiScale);
290 glEnable(GL_SCISSOR_TEST);
291 glLineWidth(2.0f);
293 CShaderDefines lineDefines;
294 lineDefines.Add(str_MINIMAP_LINE, str_1);
295 CShaderTechniquePtr tech = g_Renderer.GetShaderManager().LoadEffect(str_minimap, g_Renderer.GetSystemShaderDefines(), lineDefines);
296 tech->BeginPass();
297 CShaderProgramPtr shader = tech->GetShader();
298 shader->Uniform(str_transform, transform);
299 shader->Uniform(str_color, 1.0f, 0.3f, 0.3f, 1.0f);
301 shader->VertexPointer(2, GL_FLOAT, 0, viewVerts);
302 shader->AssertPointersBound();
304 if (!g_Renderer.m_SkipSubmit)
305 glDrawArrays(GL_LINE_LOOP, 0, 4);
307 tech->EndPass();
309 glLineWidth(1.0f);
310 glDisable(GL_SCISSOR_TEST);
313 struct MinimapUnitVertex
315 u8 r, g, b, a;
316 float x, y;
319 // Adds a vertex to the passed VertexArray
320 static void inline addVertex(const MinimapUnitVertex& v,
321 VertexArrayIterator<u8[4]>& attrColor,
322 VertexArrayIterator<float[2]>& attrPos)
324 (*attrColor)[0] = v.r;
325 (*attrColor)[1] = v.g;
326 (*attrColor)[2] = v.b;
327 (*attrColor)[3] = v.a;
328 ++attrColor;
330 (*attrPos)[0] = v.x;
331 (*attrPos)[1] = v.y;
333 ++attrPos;
337 void CMiniMap::DrawTexture(CShaderProgramPtr shader, float coordMax, float angle, float x, float y, float x2, float y2, float z)
339 // Rotate the texture coordinates (0,0)-(coordMax,coordMax) around their center point (m,m)
340 // Scale square maps to fit in circular minimap area
341 const float s = sin(angle) * m_MapScale;
342 const float c = cos(angle) * m_MapScale;
343 const float m = coordMax / 2.f;
345 float quadTex[] = {
346 m*(-c + s + 1.f), m*(-c + -s + 1.f),
347 m*(c + s + 1.f), m*(-c + s + 1.f),
348 m*(c + -s + 1.f), m*(c + s + 1.f),
350 m*(c + -s + 1.f), m*(c + s + 1.f),
351 m*(-c + -s + 1.f), m*(c + -s + 1.f),
352 m*(-c + s + 1.f), m*(-c + -s + 1.f)
354 float quadVerts[] = {
355 x, y, z,
356 x2, y, z,
357 x2, y2, z,
359 x2, y2, z,
360 x, y2, z,
361 x, y, z
364 shader->TexCoordPointer(GL_TEXTURE0, 2, GL_FLOAT, 0, quadTex);
365 shader->VertexPointer(3, GL_FLOAT, 0, quadVerts);
366 shader->AssertPointersBound();
368 if (!g_Renderer.m_SkipSubmit)
369 glDrawArrays(GL_TRIANGLES, 0, 6);
372 // TODO: render the minimap in a framebuffer and just draw the frambuffer texture
373 // most of the time, updating the framebuffer twice a frame.
374 // Here it updates as ping-pong either texture or vertex array each sec to lower gpu stalling
375 // (those operations cause a gpu sync, which slows down the way gpu works)
376 void CMiniMap::Draw()
378 PROFILE3("render minimap");
380 // The terrain isn't actually initialized until the map is loaded, which
381 // happens when the game is started, so abort until then.
382 if (!(GetGUI() && g_Game && g_Game->IsGameStarted()))
383 return;
385 CSimulation2* sim = g_Game->GetSimulation2();
386 CmpPtr<ICmpRangeManager> cmpRangeManager(*sim, SYSTEM_ENTITY);
387 ENSURE(cmpRangeManager);
389 // Set our globals in case they hadn't been set before
390 m_Camera = g_Game->GetView()->GetCamera();
391 m_Terrain = g_Game->GetWorld()->GetTerrain();
392 m_Width = (u32)(m_CachedActualSize.right - m_CachedActualSize.left);
393 m_Height = (u32)(m_CachedActualSize.bottom - m_CachedActualSize.top);
394 m_MapSize = m_Terrain->GetVerticesPerSide();
395 m_TextureSize = (GLsizei)round_up_to_pow2((size_t)m_MapSize);
396 m_MapScale = (cmpRangeManager->GetLosCircular() ? 1.f : 1.414f);
398 if (!m_TerrainTexture || g_GameRestarted)
399 CreateTextures();
402 // only update 2x / second
403 // (note: since units only move a few pixels per second on the minimap,
404 // we can get away with infrequent updates; this is slow)
405 // TODO: Update all but camera at same speed as simulation
406 static double last_time;
407 const double cur_time = timer_Time();
408 const bool doUpdate = cur_time - last_time > 0.5;
409 if (doUpdate)
411 last_time = cur_time;
412 if (m_TerrainDirty || m_WaterHeight != g_Renderer.GetWaterManager()->m_WaterHeight)
413 RebuildTerrainTexture();
416 const float x = m_CachedActualSize.left, y = m_CachedActualSize.bottom;
417 const float x2 = m_CachedActualSize.right, y2 = m_CachedActualSize.top;
418 const float z = GetBufferedZ();
419 const float texCoordMax = (float)(m_MapSize - 1) / (float)m_TextureSize;
420 const float angle = GetAngle();
421 const float unitScale = (cmpRangeManager->GetLosCircular() ? 1.f : m_MapScale/2.f);
423 // Disable depth updates to prevent apparent z-fighting-related issues
424 // with some drivers causing units to get drawn behind the texture.
425 glDepthMask(0);
427 CShaderProgramPtr shader;
428 CShaderTechniquePtr tech;
430 CShaderDefines baseDefines;
431 baseDefines.Add(str_MINIMAP_BASE, str_1);
432 tech = g_Renderer.GetShaderManager().LoadEffect(str_minimap, g_Renderer.GetSystemShaderDefines(), baseDefines);
433 tech->BeginPass();
434 shader = tech->GetShader();
436 // Draw the main textured quad
437 shader->BindTexture(str_baseTex, m_TerrainTexture);
438 const CMatrix3D baseTransform = GetDefaultGuiMatrix();
439 CMatrix3D baseTextureTransform;
440 baseTextureTransform.SetIdentity();
441 shader->Uniform(str_transform, baseTransform);
442 shader->Uniform(str_textureTransform, baseTextureTransform);
444 DrawTexture(shader, texCoordMax, angle, x, y, x2, y2, z);
446 // Draw territory boundaries
447 glEnable(GL_BLEND);
449 CTerritoryTexture& territoryTexture = g_Game->GetView()->GetTerritoryTexture();
451 shader->BindTexture(str_baseTex, territoryTexture.GetTexture());
452 const CMatrix3D* territoryTransform = territoryTexture.GetMinimapTextureMatrix();
453 shader->Uniform(str_transform, baseTransform);
454 shader->Uniform(str_textureTransform, *territoryTransform);
456 DrawTexture(shader, 1.0f, angle, x, y, x2, y2, z);
457 tech->EndPass();
459 // Draw the LOS quad in black, using alpha values from the LOS texture
460 CLOSTexture& losTexture = g_Game->GetView()->GetLOSTexture();
462 CShaderDefines losDefines;
463 losDefines.Add(str_MINIMAP_LOS, str_1);
464 tech = g_Renderer.GetShaderManager().LoadEffect(str_minimap, g_Renderer.GetSystemShaderDefines(), losDefines);
465 tech->BeginPass();
466 shader = tech->GetShader();
467 shader->BindTexture(str_baseTex, losTexture.GetTexture());
469 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
471 const CMatrix3D* losTransform = losTexture.GetMinimapTextureMatrix();
472 shader->Uniform(str_transform, baseTransform);
473 shader->Uniform(str_textureTransform, *losTransform);
475 DrawTexture(shader, 1.0f, angle, x, y, x2, y2, z);
476 tech->EndPass();
478 glDisable(GL_BLEND);
480 PROFILE_START("minimap units");
482 CShaderDefines pointDefines;
483 pointDefines.Add(str_MINIMAP_POINT, str_1);
484 tech = g_Renderer.GetShaderManager().LoadEffect(str_minimap, g_Renderer.GetSystemShaderDefines(), pointDefines);
485 tech->BeginPass();
486 shader = tech->GetShader();
487 shader->Uniform(str_transform, baseTransform);
488 shader->Uniform(str_pointSize, 3.f);
490 CMatrix3D unitMatrix;
491 unitMatrix.SetIdentity();
492 // Center the minimap on the origin of the axis of rotation.
493 unitMatrix.Translate(-(x2 - x) / 2.f, -(y2 - y) / 2.f, 0.f);
494 // Rotate the map.
495 unitMatrix.RotateZ(angle);
496 // Scale square maps to fit.
497 unitMatrix.Scale(unitScale, unitScale, 1.f);
498 // Move the minimap back to it's starting position.
499 unitMatrix.Translate((x2 - x) / 2.f, (y2 - y) / 2.f, 0.f);
500 // Move the minimap to it's final location.
501 unitMatrix.Translate(x, y, z);
502 // Apply the gui matrix.
503 unitMatrix *= GetDefaultGuiMatrix();
504 // Load the transform into the shader.
505 shader->Uniform(str_transform, unitMatrix);
507 const float sx = (float)m_Width / ((m_MapSize - 1) * TERRAIN_TILE_SIZE);
508 const float sy = (float)m_Height / ((m_MapSize - 1) * TERRAIN_TILE_SIZE);
510 CSimulation2::InterfaceList ents = sim->GetEntitiesWithInterface(IID_Minimap);
512 if (doUpdate)
514 VertexArrayIterator<float[2]> attrPos = m_AttributePos.GetIterator<float[2]>();
515 VertexArrayIterator<u8[4]> attrColor = m_AttributeColor.GetIterator<u8[4]>();
517 m_EntitiesDrawn = 0;
518 MinimapUnitVertex v;
519 std::vector<MinimapUnitVertex> pingingVertices;
520 pingingVertices.reserve(MAX_ENTITIES_DRAWN / 2);
522 if (cur_time > m_NextBlinkTime)
524 m_BlinkState = !m_BlinkState;
525 m_NextBlinkTime = cur_time + m_HalfBlinkDuration;
528 entity_pos_t posX, posZ;
529 for (CSimulation2::InterfaceList::const_iterator it = ents.begin(); it != ents.end(); ++it)
531 ICmpMinimap* cmpMinimap = static_cast<ICmpMinimap*>(it->second);
532 if (cmpMinimap->GetRenderData(v.r, v.g, v.b, posX, posZ))
534 ICmpRangeManager::ELosVisibility vis = cmpRangeManager->GetLosVisibility(it->first, g_Game->GetSimulation2()->GetSimContext().GetCurrentDisplayedPlayer());
535 if (vis != ICmpRangeManager::VIS_HIDDEN)
537 v.a = 255;
538 v.x = posX.ToFloat() * sx;
539 v.y = -posZ.ToFloat() * sy;
541 // Check minimap pinging to indicate something
542 if (m_BlinkState && cmpMinimap->CheckPing(cur_time, m_PingDuration))
544 v.r = 255; // ping color is white
545 v.g = 255;
546 v.b = 255;
547 pingingVertices.push_back(v);
549 else
551 addVertex(v, attrColor, attrPos);
552 ++m_EntitiesDrawn;
558 // Add the pinged vertices at the end, so they are drawn on top
559 for (size_t v = 0; v < pingingVertices.size(); ++v)
561 addVertex(pingingVertices[v], attrColor, attrPos);
562 ++m_EntitiesDrawn;
565 ENSURE(m_EntitiesDrawn < MAX_ENTITIES_DRAWN);
566 m_VertexArray.Upload();
569 m_VertexArray.PrepareForRendering();
571 if (m_EntitiesDrawn > 0)
573 #if !CONFIG2_GLES
574 if (g_Renderer.GetRenderPath() == CRenderer::RP_SHADER)
575 glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
576 #endif
578 u8* indexBase = m_IndexArray.Bind();
579 u8* base = m_VertexArray.Bind();
580 const GLsizei stride = (GLsizei)m_VertexArray.GetStride();
582 shader->VertexPointer(2, GL_FLOAT, stride, base + m_AttributePos.offset);
583 shader->ColorPointer(4, GL_UNSIGNED_BYTE, stride, base + m_AttributeColor.offset);
584 shader->AssertPointersBound();
586 if (!g_Renderer.m_SkipSubmit)
587 glDrawElements(GL_POINTS, (GLsizei)(m_EntitiesDrawn), GL_UNSIGNED_SHORT, indexBase);
589 g_Renderer.GetStats().m_DrawCalls++;
590 CVertexBuffer::Unbind();
592 #if !CONFIG2_GLES
593 if (g_Renderer.GetRenderPath() == CRenderer::RP_SHADER)
594 glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);
595 #endif
598 tech->EndPass();
600 DrawViewRect(unitMatrix);
602 PROFILE_END("minimap units");
604 // Reset depth mask
605 glDepthMask(1);
608 void CMiniMap::CreateTextures()
610 Destroy();
612 // Create terrain texture
613 glGenTextures(1, &m_TerrainTexture);
614 g_Renderer.BindTexture(0, m_TerrainTexture);
616 // Initialise texture with solid black, for the areas we don't
617 // overwrite with glTexSubImage2D later
618 u32* texData = new u32[m_TextureSize * m_TextureSize];
619 for (ssize_t i = 0; i < m_TextureSize * m_TextureSize; ++i)
620 texData[i] = 0xFF000000;
621 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_TextureSize, m_TextureSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, texData);
622 delete[] texData;
624 m_TerrainData = new u32[(m_MapSize - 1) * (m_MapSize - 1)];
625 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
626 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
627 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
628 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
630 // Rebuild and upload both of them
631 RebuildTerrainTexture();
635 void CMiniMap::RebuildTerrainTexture()
637 u32 x = 0;
638 u32 y = 0;
639 u32 w = m_MapSize - 1;
640 u32 h = m_MapSize - 1;
641 m_WaterHeight = g_Renderer.GetWaterManager()->m_WaterHeight;
643 m_TerrainDirty = false;
645 for (u32 j = 0; j < h; ++j)
647 u32* dataPtr = m_TerrainData + ((y + j) * (m_MapSize - 1)) + x;
648 for (u32 i = 0; i < w; ++i)
650 float avgHeight = ( m_Terrain->GetVertexGroundLevel((int)i, (int)j)
651 + m_Terrain->GetVertexGroundLevel((int)i+1, (int)j)
652 + m_Terrain->GetVertexGroundLevel((int)i, (int)j+1)
653 + m_Terrain->GetVertexGroundLevel((int)i+1, (int)j+1)
654 ) / 4.0f;
656 if (avgHeight < m_WaterHeight && avgHeight > m_WaterHeight - m_ShallowPassageHeight)
658 // shallow water
659 *dataPtr++ = 0xffc09870;
661 else if (avgHeight < m_WaterHeight)
663 // Set water as constant color for consistency on different maps
664 *dataPtr++ = 0xffa07850;
666 else
668 int hmap = ((int)m_Terrain->GetHeightMap()[(y + j) * m_MapSize + x + i]) >> 8;
669 int val = (hmap / 3) + 170;
671 u32 color = 0xFFFFFFFF;
673 CMiniPatch* mp = m_Terrain->GetTile(x + i, y + j);
674 if (mp)
676 CTerrainTextureEntry* tex = mp->GetTextureEntry();
677 if (tex)
679 // If the texture can't be loaded yet, set the dirty flags
680 // so we'll try regenerating the terrain texture again soon
681 if(!tex->GetTexture()->TryLoad())
682 m_TerrainDirty = true;
684 color = tex->GetBaseColor();
688 *dataPtr++ = ScaleColor(color, float(val) / 255.0f);
693 // Upload the texture
694 g_Renderer.BindTexture(0, m_TerrainTexture);
695 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_MapSize - 1, m_MapSize - 1, GL_RGBA, GL_UNSIGNED_BYTE, m_TerrainData);
698 void CMiniMap::Destroy()
700 if (m_TerrainTexture)
702 glDeleteTextures(1, &m_TerrainTexture);
703 m_TerrainTexture = 0;
706 SAFE_ARRAY_DELETE(m_TerrainData);