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"
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"
32 #include "gui/GUIManager.h"
34 #include "lib/external_libraries/libsdl.h"
36 #include "lib/timer.h"
37 #include "ps/ConfigDB.h"
38 #include "ps/Filesystem.h"
40 #include "ps/GameSetup/Config.h"
41 #include "ps/Profile.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");
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();
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
)
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
)
118 (*attrPos
)[0] = -10000.0f
;
119 (*attrPos
)[1] = -10000.0f
;
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()
142 void CMiniMap::HandleMessage(SGUIMessage
& Message
)
144 switch (Message
.type
)
146 case GUIM_MOUSE_PRESS_LEFT
:
153 case GUIM_MOUSE_RELEASE_LEFT
:
154 if (m_MouseHovering
&& m_Clicking
)
158 case GUIM_MOUSE_DBLCLICK_LEFT
:
159 if (m_MouseHovering
&& m_Clicking
)
163 case GUIM_MOUSE_ENTER
:
164 m_MouseHovering
= true;
166 case GUIM_MOUSE_LEAVE
:
168 m_MouseHovering
= false;
170 case GUIM_MOUSE_RELEASE_RIGHT
:
171 CMiniMap::FireWorldClickEvent(SDL_BUTTON_RIGHT
, 1);
173 case GUIM_MOUSE_DBLCLICK_RIGHT
:
174 CMiniMap::FireWorldClickEvent(SDL_BUTTON_RIGHT
, 2);
176 case GUIM_MOUSE_MOTION
:
177 if (m_MouseHovering
&& m_Clicking
)
180 case GUIM_MOUSE_WHEEL_DOWN
:
181 case GUIM_MOUSE_WHEEL_UP
:
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)
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();
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
);
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
);
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.
286 m_CachedActualSize
.left
* g_GuiScale
,
287 g_Renderer
.GetHeight() - m_CachedActualSize
.bottom
* g_GuiScale
,
289 height
* g_GuiScale
);
290 glEnable(GL_SCISSOR_TEST
);
293 CShaderDefines lineDefines
;
294 lineDefines
.Add(str_MINIMAP_LINE
, str_1
);
295 CShaderTechniquePtr tech
= g_Renderer
.GetShaderManager().LoadEffect(str_minimap
, g_Renderer
.GetSystemShaderDefines(), lineDefines
);
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);
310 glDisable(GL_SCISSOR_TEST
);
313 struct MinimapUnitVertex
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
;
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
;
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
[] = {
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()))
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
)
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;
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.
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
);
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
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
);
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
);
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
);
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
);
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
);
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
);
514 VertexArrayIterator
<float[2]> attrPos
= m_AttributePos
.GetIterator
<float[2]>();
515 VertexArrayIterator
<u8
[4]> attrColor
= m_AttributeColor
.GetIterator
<u8
[4]>();
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
)
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
547 pingingVertices
.push_back(v
);
551 addVertex(v
, attrColor
, attrPos
);
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
);
565 ENSURE(m_EntitiesDrawn
< MAX_ENTITIES_DRAWN
);
566 m_VertexArray
.Upload();
569 m_VertexArray
.PrepareForRendering();
571 if (m_EntitiesDrawn
> 0)
574 if (g_Renderer
.GetRenderPath() == CRenderer::RP_SHADER
)
575 glEnable(GL_VERTEX_PROGRAM_POINT_SIZE
);
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();
593 if (g_Renderer
.GetRenderPath() == CRenderer::RP_SHADER
)
594 glDisable(GL_VERTEX_PROGRAM_POINT_SIZE
);
600 DrawViewRect(unitMatrix
);
602 PROFILE_END("minimap units");
608 void CMiniMap::CreateTextures()
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
);
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()
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)
656 if (avgHeight
< m_WaterHeight
&& avgHeight
> m_WaterHeight
- m_ShallowPassageHeight
)
659 *dataPtr
++ = 0xffc09870;
661 else if (avgHeight
< m_WaterHeight
)
663 // Set water as constant color for consistency on different maps
664 *dataPtr
++ = 0xffa07850;
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
);
676 CTerrainTextureEntry
* tex
= mp
->GetTextureEntry();
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
);