Moves MiniMap texture rendering to a separate framebuffer to update it less frequently.
Tested By: Langbart Differential Revision: https://code.wildfiregames.com/D4331 This was SVN commit r25993.
This commit is contained in:
parent
36eb92f9a4
commit
b991ef919b
@ -70,6 +70,9 @@ CLOSTexture::CLOSTexture(CSimulation2& simulation)
|
||||
|
||||
CLOSTexture::~CLOSTexture()
|
||||
{
|
||||
if (m_smoothFbo)
|
||||
pglDeleteFramebuffersEXT(1, &m_smoothFbo);
|
||||
|
||||
if (m_Texture)
|
||||
DeleteTexture();
|
||||
}
|
||||
|
@ -19,12 +19,18 @@
|
||||
|
||||
#include "MiniMapTexture.h"
|
||||
|
||||
#include "graphics/GameView.h"
|
||||
#include "graphics/LOSTexture.h"
|
||||
#include "graphics/MiniPatch.h"
|
||||
#include "graphics/ShaderManager.h"
|
||||
#include "graphics/ShaderProgramPtr.h"
|
||||
#include "graphics/Terrain.h"
|
||||
#include "graphics/TerrainTextureEntry.h"
|
||||
#include "graphics/TerrainTextureManager.h"
|
||||
#include "graphics/TerritoryTexture.h"
|
||||
#include "lib/bits.h"
|
||||
#include "lib/timer.h"
|
||||
#include "ps/ConfigDB.h"
|
||||
#include "ps/CStrInternStatic.h"
|
||||
#include "ps/Filesystem.h"
|
||||
#include "ps/Game.h"
|
||||
@ -42,23 +48,130 @@
|
||||
namespace
|
||||
{
|
||||
|
||||
// Set max drawn entities to UINT16_MAX for now, which is more than enough
|
||||
// TODO: we should be cleverer about drawing them to reduce clutter
|
||||
const u16 MAX_ENTITIES_DRAWN = 65535;
|
||||
|
||||
const size_t FINAL_TEXTURE_SIZE = 512;
|
||||
|
||||
unsigned int ScaleColor(unsigned int color, float x)
|
||||
{
|
||||
unsigned int r = unsigned(float(color & 0xff) * x);
|
||||
unsigned int g = unsigned(float((color>>8) & 0xff) * x);
|
||||
unsigned int b = unsigned(float((color>>16) & 0xff) * x);
|
||||
return (0xff000000 | b | g<<8 | r<<16);
|
||||
unsigned int g = unsigned(float((color >> 8) & 0xff) * x);
|
||||
unsigned int b = unsigned(float((color >> 16) & 0xff) * x);
|
||||
return (0xff000000 | b | g << 8 | r << 16);
|
||||
}
|
||||
|
||||
void DrawTexture(CShaderProgramPtr shader)
|
||||
{
|
||||
const float quadUVs[] =
|
||||
{
|
||||
0.0f, 0.0f,
|
||||
1.0f, 0.0f,
|
||||
1.0f, 1.0f,
|
||||
|
||||
1.0f, 1.0f,
|
||||
0.0f, 1.0f,
|
||||
0.0f, 0.0f
|
||||
};
|
||||
const float quadVertices[] =
|
||||
{
|
||||
-1.0f, -1.0f, 0.0f,
|
||||
1.0f, -1.0f, 0.0f,
|
||||
1.0f, 1.0f, 0.0f,
|
||||
|
||||
1.0f, 1.0f, 0.0f,
|
||||
-1.0f, 1.0f, 0.0f,
|
||||
-1.0f, -1.0f, 0.0f
|
||||
};
|
||||
|
||||
shader->TexCoordPointer(GL_TEXTURE0, 2, GL_FLOAT, 0, quadUVs);
|
||||
shader->VertexPointer(3, GL_FLOAT, 0, quadVertices);
|
||||
shader->AssertPointersBound();
|
||||
|
||||
if (!g_Renderer.DoSkipSubmit())
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||
}
|
||||
|
||||
struct MinimapUnitVertex
|
||||
{
|
||||
// This struct is copyable for convenience and because to move is to copy for primitives.
|
||||
u8 r, g, b, a;
|
||||
float x, y;
|
||||
};
|
||||
|
||||
// Adds a vertex to the passed VertexArray
|
||||
static void inline addVertex(const MinimapUnitVertex& v,
|
||||
VertexArrayIterator<u8[4]>& attrColor,
|
||||
VertexArrayIterator<float[2]>& attrPos)
|
||||
{
|
||||
(*attrColor)[0] = v.r;
|
||||
(*attrColor)[1] = v.g;
|
||||
(*attrColor)[2] = v.b;
|
||||
(*attrColor)[3] = v.a;
|
||||
++attrColor;
|
||||
|
||||
(*attrPos)[0] = v.x;
|
||||
(*attrPos)[1] = v.y;
|
||||
|
||||
++attrPos;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
CMiniMapTexture::CMiniMapTexture(CSimulation2& simulation)
|
||||
: m_Simulation(simulation)
|
||||
: m_Simulation(simulation), m_IndexArray(GL_STATIC_DRAW), m_VertexArray(GL_DYNAMIC_DRAW)
|
||||
{
|
||||
// Register Relax NG validator.
|
||||
CXeromyces::AddValidator(g_VFS, "pathfinder", "simulation/data/pathfinder.rng");
|
||||
|
||||
m_ShallowPassageHeight = GetShallowPassageHeight();
|
||||
|
||||
double blinkDuration = 1.0;
|
||||
// Tests won't have config initialised
|
||||
if (CConfigDB::IsInitialised())
|
||||
{
|
||||
CFG_GET_VAL("gui.session.minimap.blinkduration", blinkDuration);
|
||||
CFG_GET_VAL("gui.session.minimap.pingduration", m_PingDuration);
|
||||
}
|
||||
m_HalfBlinkDuration = blinkDuration / 2.0;
|
||||
|
||||
m_AttributePos.type = GL_FLOAT;
|
||||
m_AttributePos.elems = 2;
|
||||
m_VertexArray.AddAttribute(&m_AttributePos);
|
||||
|
||||
m_AttributeColor.type = GL_UNSIGNED_BYTE;
|
||||
m_AttributeColor.elems = 4;
|
||||
m_VertexArray.AddAttribute(&m_AttributeColor);
|
||||
|
||||
m_VertexArray.SetNumVertices(MAX_ENTITIES_DRAWN);
|
||||
m_VertexArray.Layout();
|
||||
|
||||
m_IndexArray.SetNumVertices(MAX_ENTITIES_DRAWN);
|
||||
m_IndexArray.Layout();
|
||||
VertexArrayIterator<u16> index = m_IndexArray.GetIterator();
|
||||
for (u16 i = 0; i < MAX_ENTITIES_DRAWN; ++i)
|
||||
*index++ = i;
|
||||
m_IndexArray.Upload();
|
||||
m_IndexArray.FreeBackingStore();
|
||||
|
||||
VertexArrayIterator<float[2]> attrPos = m_AttributePos.GetIterator<float[2]>();
|
||||
VertexArrayIterator<u8[4]> attrColor = m_AttributeColor.GetIterator<u8[4]>();
|
||||
for (u16 i = 0; i < MAX_ENTITIES_DRAWN; ++i)
|
||||
{
|
||||
(*attrColor)[0] = 0;
|
||||
(*attrColor)[1] = 0;
|
||||
(*attrColor)[2] = 0;
|
||||
(*attrColor)[3] = 0;
|
||||
++attrColor;
|
||||
|
||||
(*attrPos)[0] = -10000.0f;
|
||||
(*attrPos)[1] = -10000.0f;
|
||||
|
||||
++attrPos;
|
||||
|
||||
}
|
||||
m_VertexArray.Upload();
|
||||
}
|
||||
|
||||
CMiniMapTexture::~CMiniMapTexture()
|
||||
@ -69,34 +182,34 @@ CMiniMapTexture::~CMiniMapTexture()
|
||||
void CMiniMapTexture::Update(const float UNUSED(deltaRealTime))
|
||||
{
|
||||
if (m_WaterHeight != g_Renderer.GetWaterManager()->m_WaterHeight)
|
||||
m_Dirty = true;
|
||||
{
|
||||
m_TerrainTextureDirty = true;
|
||||
m_FinalTextureDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
void CMiniMapTexture::Render()
|
||||
{
|
||||
if (!m_Dirty)
|
||||
return;
|
||||
|
||||
const CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
|
||||
if (!terrain)
|
||||
return;
|
||||
|
||||
CmpPtr<ICmpRangeManager> cmpRangeManager(m_Simulation, SYSTEM_ENTITY);
|
||||
ENSURE(cmpRangeManager);
|
||||
if (!m_TerrainTexture)
|
||||
CreateTextures(terrain);
|
||||
|
||||
if (m_TerrainTextureDirty)
|
||||
RebuildTerrainTexture(terrain);
|
||||
|
||||
RenderFinalTexture();
|
||||
}
|
||||
|
||||
void CMiniMapTexture::CreateTextures(const CTerrain* terrain)
|
||||
{
|
||||
DestroyTextures();
|
||||
|
||||
m_MapSize = terrain->GetVerticesPerSide();
|
||||
m_TextureSize = static_cast<GLsizei>(round_up_to_pow2(static_cast<size_t>(m_MapSize)));
|
||||
|
||||
if (!m_TerrainTexture)
|
||||
CreateTextures();
|
||||
|
||||
RebuildTerrainTexture(terrain);
|
||||
}
|
||||
|
||||
void CMiniMapTexture::CreateTextures()
|
||||
{
|
||||
DestroyTextures();
|
||||
|
||||
// Create terrain texture
|
||||
glGenTextures(1, &m_TerrainTexture);
|
||||
g_Renderer.BindTexture(0, m_TerrainTexture);
|
||||
@ -114,6 +227,27 @@ void CMiniMapTexture::CreateTextures()
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
glGenTextures(1, &m_FinalTexture);
|
||||
g_Renderer.BindTexture(0, m_FinalTexture);
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, FINAL_TEXTURE_SIZE, FINAL_TEXTURE_SIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
pglGenFramebuffersEXT(1, &m_FinalTextureFBO);
|
||||
pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_FinalTextureFBO);
|
||||
pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_FinalTexture, 0);
|
||||
|
||||
GLenum status = pglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
|
||||
{
|
||||
LOGWARNING("MiniMapTexture Framebuffer object incomplete (A): 0x%04X", status);
|
||||
}
|
||||
|
||||
pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
|
||||
}
|
||||
|
||||
void CMiniMapTexture::DestroyTextures()
|
||||
@ -124,6 +258,12 @@ void CMiniMapTexture::DestroyTextures()
|
||||
m_TerrainTexture = 0;
|
||||
}
|
||||
|
||||
if (m_FinalTexture)
|
||||
{
|
||||
glDeleteTextures(1, &m_FinalTexture);
|
||||
m_FinalTexture = 0;
|
||||
}
|
||||
|
||||
SAFE_ARRAY_DELETE(m_TerrainData);
|
||||
}
|
||||
|
||||
@ -135,7 +275,7 @@ void CMiniMapTexture::RebuildTerrainTexture(const CTerrain* terrain)
|
||||
const u32 height = m_MapSize - 1;
|
||||
|
||||
m_WaterHeight = g_Renderer.GetWaterManager()->m_WaterHeight;
|
||||
m_Dirty = false;
|
||||
m_TerrainTextureDirty = false;
|
||||
|
||||
for (u32 j = 0; j < height; ++j)
|
||||
{
|
||||
@ -174,7 +314,7 @@ void CMiniMapTexture::RebuildTerrainTexture(const CTerrain* terrain)
|
||||
// If the texture can't be loaded yet, set the dirty flags
|
||||
// so we'll try regenerating the terrain texture again soon
|
||||
if(!tex->GetTexture()->TryLoad())
|
||||
m_Dirty = true;
|
||||
m_TerrainTextureDirty = true;
|
||||
|
||||
color = tex->GetBaseColor();
|
||||
}
|
||||
@ -191,6 +331,198 @@ void CMiniMapTexture::RebuildTerrainTexture(const CTerrain* terrain)
|
||||
g_Renderer.BindTexture(0, 0);
|
||||
}
|
||||
|
||||
void CMiniMapTexture::RenderFinalTexture()
|
||||
{
|
||||
// only update 2x / second
|
||||
// (note: since units only move a few pixels per second on the minimap,
|
||||
// we can get away with infrequent updates; this is slow)
|
||||
// TODO: Update all but camera at same speed as simulation
|
||||
const double currentTime = timer_Time();
|
||||
const bool doUpdate = (currentTime - m_LastFinalTextureUpdate > 0.5) || m_FinalTextureDirty;
|
||||
if (doUpdate)
|
||||
m_LastFinalTextureUpdate = currentTime;
|
||||
else
|
||||
return;
|
||||
m_FinalTextureDirty = false;
|
||||
|
||||
pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_FinalTextureFBO);
|
||||
|
||||
const SViewPort oldViewPort = g_Renderer.GetViewport();
|
||||
const SViewPort viewPort = { 0, 0, FINAL_TEXTURE_SIZE, FINAL_TEXTURE_SIZE };
|
||||
g_Renderer.SetViewport(viewPort);
|
||||
|
||||
CSimulation2* sim = g_Game->GetSimulation2();
|
||||
CmpPtr<ICmpRangeManager> cmpRangeManager(*sim, SYSTEM_ENTITY);
|
||||
ENSURE(cmpRangeManager);
|
||||
CLOSTexture& losTexture = g_Game->GetView()->GetLOSTexture();
|
||||
|
||||
const float invTileMapSize = 1.0f / static_cast<float>(TERRAIN_TILE_SIZE * m_MapSize);
|
||||
const float texCoordMax = (float)(m_MapSize - 1) / (float)m_TextureSize;
|
||||
|
||||
CShaderProgramPtr shader;
|
||||
CShaderTechniquePtr tech;
|
||||
|
||||
CShaderDefines baseDefines;
|
||||
baseDefines.Add(str_MINIMAP_BASE, str_1);
|
||||
|
||||
tech = g_Renderer.GetShaderManager().LoadEffect(str_minimap, g_Renderer.GetSystemShaderDefines(), baseDefines);
|
||||
tech->BeginPass();
|
||||
shader = tech->GetShader();
|
||||
|
||||
if (m_TerrainTexture)
|
||||
shader->BindTexture(str_baseTex, m_TerrainTexture);
|
||||
|
||||
CMatrix3D baseTransform;
|
||||
baseTransform.SetIdentity();
|
||||
CMatrix3D baseTextureTransform;
|
||||
baseTextureTransform.SetIdentity();
|
||||
|
||||
CMatrix3D terrainTransform;
|
||||
terrainTransform.SetIdentity();
|
||||
terrainTransform.Scale(texCoordMax, texCoordMax, 1.0f);
|
||||
shader->Uniform(str_transform, baseTransform);
|
||||
shader->Uniform(str_textureTransform, terrainTransform);
|
||||
|
||||
if (m_TerrainTexture)
|
||||
DrawTexture(shader);
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glColorMask(1, 1, 1, 0);
|
||||
|
||||
// Draw territory boundaries
|
||||
CTerritoryTexture& territoryTexture = g_Game->GetView()->GetTerritoryTexture();
|
||||
|
||||
shader->BindTexture(str_baseTex, territoryTexture.GetTexture());
|
||||
shader->Uniform(str_transform, baseTransform);
|
||||
CMatrix3D territoryTransform = *territoryTexture.GetMinimapTextureMatrix();
|
||||
shader->Uniform(str_textureTransform, territoryTransform);
|
||||
|
||||
DrawTexture(shader);
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
glColorMask(0, 0, 0, 1);
|
||||
|
||||
shader->BindTexture(str_baseTex, losTexture.GetTexture());
|
||||
CMatrix3D losTransform = *losTexture.GetMinimapTextureMatrix();
|
||||
shader->Uniform(str_transform, baseTransform);
|
||||
shader->Uniform(str_textureTransform, losTransform);
|
||||
|
||||
DrawTexture(shader);
|
||||
|
||||
tech->EndPass();
|
||||
|
||||
glColorMask(1, 1, 1, 1);
|
||||
|
||||
CShaderDefines pointDefines;
|
||||
pointDefines.Add(str_MINIMAP_POINT, str_1);
|
||||
tech = g_Renderer.GetShaderManager().LoadEffect(str_minimap, g_Renderer.GetSystemShaderDefines(), pointDefines);
|
||||
tech->BeginPass();
|
||||
shader = tech->GetShader();
|
||||
shader->Uniform(str_transform, baseTransform);
|
||||
shader->Uniform(str_pointSize, 9.0f);
|
||||
|
||||
CMatrix3D unitMatrix;
|
||||
unitMatrix.SetIdentity();
|
||||
// Convert world space coordinates into [0, 2].
|
||||
const float unitScale = invTileMapSize;
|
||||
unitMatrix.Scale(unitScale * 2.0f, unitScale * 2.0f, 1.0f);
|
||||
// Offset the coordinates to [-1, 1].
|
||||
unitMatrix.Translate(CVector3D(-1.0f, -1.0f, 0.0f));
|
||||
shader->Uniform(str_transform, unitMatrix);
|
||||
|
||||
CSimulation2::InterfaceList ents = sim->GetEntitiesWithInterface(IID_Minimap);
|
||||
|
||||
if (doUpdate)
|
||||
{
|
||||
VertexArrayIterator<float[2]> attrPos = m_AttributePos.GetIterator<float[2]>();
|
||||
VertexArrayIterator<u8[4]> attrColor = m_AttributeColor.GetIterator<u8[4]>();
|
||||
|
||||
m_EntitiesDrawn = 0;
|
||||
MinimapUnitVertex v;
|
||||
std::vector<MinimapUnitVertex> pingingVertices;
|
||||
pingingVertices.reserve(MAX_ENTITIES_DRAWN / 2);
|
||||
|
||||
if (currentTime > m_NextBlinkTime)
|
||||
{
|
||||
m_BlinkState = !m_BlinkState;
|
||||
m_NextBlinkTime = currentTime + m_HalfBlinkDuration;
|
||||
}
|
||||
|
||||
entity_pos_t posX, posZ;
|
||||
for (CSimulation2::InterfaceList::const_iterator it = ents.begin(); it != ents.end(); ++it)
|
||||
{
|
||||
ICmpMinimap* cmpMinimap = static_cast<ICmpMinimap*>(it->second);
|
||||
if (cmpMinimap->GetRenderData(v.r, v.g, v.b, posX, posZ))
|
||||
{
|
||||
LosVisibility vis = cmpRangeManager->GetLosVisibility(it->first, g_Game->GetSimulation2()->GetSimContext().GetCurrentDisplayedPlayer());
|
||||
if (vis != LosVisibility::HIDDEN)
|
||||
{
|
||||
v.a = 255;
|
||||
v.x = posX.ToFloat();
|
||||
v.y = posZ.ToFloat();
|
||||
|
||||
// Check minimap pinging to indicate something
|
||||
if (m_BlinkState && cmpMinimap->CheckPing(currentTime, m_PingDuration))
|
||||
{
|
||||
v.r = 255; // ping color is white
|
||||
v.g = 255;
|
||||
v.b = 255;
|
||||
pingingVertices.push_back(v);
|
||||
}
|
||||
else
|
||||
{
|
||||
addVertex(v, attrColor, attrPos);
|
||||
++m_EntitiesDrawn;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add the pinged vertices at the end, so they are drawn on top
|
||||
for (const MinimapUnitVertex& vertex : pingingVertices)
|
||||
{
|
||||
addVertex(vertex, attrColor, attrPos);
|
||||
++m_EntitiesDrawn;
|
||||
}
|
||||
|
||||
ENSURE(m_EntitiesDrawn < MAX_ENTITIES_DRAWN);
|
||||
m_VertexArray.Upload();
|
||||
}
|
||||
|
||||
m_VertexArray.PrepareForRendering();
|
||||
|
||||
if (m_EntitiesDrawn > 0)
|
||||
{
|
||||
#if !CONFIG2_GLES
|
||||
glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
|
||||
#endif
|
||||
|
||||
u8* indexBase = m_IndexArray.Bind();
|
||||
u8* base = m_VertexArray.Bind();
|
||||
const GLsizei stride = (GLsizei)m_VertexArray.GetStride();
|
||||
|
||||
shader->VertexPointer(2, GL_FLOAT, stride, base + m_AttributePos.offset);
|
||||
shader->ColorPointer(4, GL_UNSIGNED_BYTE, stride, base + m_AttributeColor.offset);
|
||||
shader->AssertPointersBound();
|
||||
|
||||
if (!g_Renderer.DoSkipSubmit())
|
||||
glDrawElements(GL_POINTS, (GLsizei)(m_EntitiesDrawn), GL_UNSIGNED_SHORT, indexBase);
|
||||
|
||||
g_Renderer.GetStats().m_DrawCalls++;
|
||||
CVertexBuffer::Unbind();
|
||||
|
||||
#if !CONFIG2_GLES
|
||||
glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);
|
||||
#endif
|
||||
}
|
||||
|
||||
tech->EndPass();
|
||||
|
||||
pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
|
||||
g_Renderer.SetViewport(oldViewPort);
|
||||
}
|
||||
|
||||
// static
|
||||
float CMiniMapTexture::GetShallowPassageHeight()
|
||||
{
|
||||
|
@ -19,6 +19,7 @@
|
||||
#define INCLUDED_MINIMAPTEXTURE
|
||||
|
||||
#include "lib/ogl.h"
|
||||
#include "renderer/VertexArray.h"
|
||||
|
||||
class CSimulation2;
|
||||
class CTerrain;
|
||||
@ -40,9 +41,7 @@ public:
|
||||
*/
|
||||
void Render();
|
||||
|
||||
GLuint GetTerrainTexture() const { return m_TerrainTexture; }
|
||||
|
||||
GLsizei GetTerrainTextureSize() const { return m_TextureSize; }
|
||||
GLuint GetTexture() const { return m_FinalTexture; }
|
||||
|
||||
/**
|
||||
* @return The maximum height for unit passage in water.
|
||||
@ -50,16 +49,22 @@ public:
|
||||
static float GetShallowPassageHeight();
|
||||
|
||||
private:
|
||||
void CreateTextures();
|
||||
void CreateTextures(const CTerrain* terrain);
|
||||
void DestroyTextures();
|
||||
void RebuildTerrainTexture(const CTerrain* terrain);
|
||||
void RenderFinalTexture();
|
||||
|
||||
CSimulation2& m_Simulation;
|
||||
|
||||
bool m_Dirty = true;
|
||||
bool m_TerrainTextureDirty = true;
|
||||
bool m_FinalTextureDirty = true;
|
||||
double m_LastFinalTextureUpdate = 0.0;
|
||||
|
||||
// minimap texture handles
|
||||
GLuint m_TerrainTexture = 0;
|
||||
GLuint m_FinalTexture = 0;
|
||||
|
||||
GLuint m_FinalTextureFBO = 0;
|
||||
|
||||
// texture data
|
||||
u32* m_TerrainData = nullptr;
|
||||
@ -73,6 +78,18 @@ private:
|
||||
// Maximal water height to allow the passage of a unit (for underwater shallows).
|
||||
float m_ShallowPassageHeight = 0.0f;
|
||||
float m_WaterHeight = 0.0f;
|
||||
|
||||
VertexIndexArray m_IndexArray;
|
||||
VertexArray m_VertexArray;
|
||||
VertexArray::Attribute m_AttributePos;
|
||||
VertexArray::Attribute m_AttributeColor;
|
||||
|
||||
size_t m_EntitiesDrawn = 0;
|
||||
|
||||
double m_PingDuration = 25.0;
|
||||
double m_HalfBlinkDuration = 0.0;
|
||||
double m_NextBlinkTime = 0.0;
|
||||
bool m_BlinkState = false;
|
||||
};
|
||||
|
||||
#endif // INCLUDED_MINIMAPTEXTURE
|
||||
|
@ -29,7 +29,6 @@
|
||||
#include "graphics/Terrain.h"
|
||||
#include "graphics/TerrainTextureEntry.h"
|
||||
#include "graphics/TerrainTextureManager.h"
|
||||
#include "graphics/TerritoryTexture.h"
|
||||
#include "gui/CGUI.h"
|
||||
#include "gui/GUIManager.h"
|
||||
#include "gui/GUIMatrix.h"
|
||||
@ -62,10 +61,6 @@
|
||||
namespace
|
||||
{
|
||||
|
||||
// Set max drawn entities to UINT16_MAX for now, which is more than enough
|
||||
// TODO: we should be cleverer about drawing them to reduce clutter
|
||||
const u16 MAX_ENTITIES_DRAWN = 65535;
|
||||
|
||||
// Adds segments pieces lying inside the circle to lines.
|
||||
void CropPointsByCircle(const std::array<CVector3D, 4>& points, const CVector3D& center, const float radius, std::vector<CVector3D>* lines)
|
||||
{
|
||||
@ -97,13 +92,13 @@ void CropPointsByCircle(const std::array<CVector3D, 4>& points, const CVector3D&
|
||||
}
|
||||
}
|
||||
|
||||
void DrawTexture(CShaderProgramPtr shader, float coordMax, float angle, float x, float y, float x2, float y2, float mapScale)
|
||||
void DrawTexture(CShaderProgramPtr shader, float angle, float x, float y, float x2, float y2, float mapScale)
|
||||
{
|
||||
// Rotate the texture coordinates (0,0)-(coordMax,coordMax) around their center point (m,m)
|
||||
// Scale square maps to fit in circular minimap area
|
||||
const float s = sin(angle) * mapScale;
|
||||
const float c = cos(angle) * mapScale;
|
||||
const float m = coordMax / 2.f;
|
||||
const float m = 0.5f;
|
||||
|
||||
float quadTex[] = {
|
||||
m*(-c + s + 1.f), m*(-c + -s + 1.f),
|
||||
@ -138,62 +133,13 @@ const CStr CMiniMap::EventNameWorldClick = "WorldClick";
|
||||
|
||||
CMiniMap::CMiniMap(CGUI& pGUI) :
|
||||
IGUIObject(pGUI),
|
||||
m_MapSize(0), m_MapScale(1.f),
|
||||
m_EntitiesDrawn(0), m_IndexArray(GL_STATIC_DRAW), m_VertexArray(GL_DYNAMIC_DRAW), m_Mask(this, "mask", false),
|
||||
m_NextBlinkTime(0.0), m_PingDuration(25.0), m_BlinkState(false),
|
||||
m_MapSize(0), m_MapScale(1.f), m_Mask(this, "mask", false),
|
||||
m_FlareTextureCount(this, "flare_texture_count", 0), m_FlareRenderSize(this, "flare_render_size", 0),
|
||||
m_FlareInterleave(this, "flare_interleave", false), m_FlareAnimationSpeed(this, "flare_animation_speed", 0.0f),
|
||||
m_FlareLifetimeSeconds(this, "flare_lifetime_seconds", 0.0f)
|
||||
{
|
||||
m_Clicking = false;
|
||||
m_MouseHovering = false;
|
||||
|
||||
m_AttributePos.type = GL_FLOAT;
|
||||
m_AttributePos.elems = 2;
|
||||
m_VertexArray.AddAttribute(&m_AttributePos);
|
||||
|
||||
m_AttributeColor.type = GL_UNSIGNED_BYTE;
|
||||
m_AttributeColor.elems = 4;
|
||||
m_VertexArray.AddAttribute(&m_AttributeColor);
|
||||
|
||||
m_VertexArray.SetNumVertices(MAX_ENTITIES_DRAWN);
|
||||
m_VertexArray.Layout();
|
||||
|
||||
m_IndexArray.SetNumVertices(MAX_ENTITIES_DRAWN);
|
||||
m_IndexArray.Layout();
|
||||
VertexArrayIterator<u16> index = m_IndexArray.GetIterator();
|
||||
for (u16 i = 0; i < MAX_ENTITIES_DRAWN; ++i)
|
||||
*index++ = i;
|
||||
m_IndexArray.Upload();
|
||||
m_IndexArray.FreeBackingStore();
|
||||
|
||||
VertexArrayIterator<float[2]> attrPos = m_AttributePos.GetIterator<float[2]>();
|
||||
VertexArrayIterator<u8[4]> attrColor = m_AttributeColor.GetIterator<u8[4]>();
|
||||
for (u16 i = 0; i < MAX_ENTITIES_DRAWN; ++i)
|
||||
{
|
||||
(*attrColor)[0] = 0;
|
||||
(*attrColor)[1] = 0;
|
||||
(*attrColor)[2] = 0;
|
||||
(*attrColor)[3] = 0;
|
||||
++attrColor;
|
||||
|
||||
(*attrPos)[0] = -10000.0f;
|
||||
(*attrPos)[1] = -10000.0f;
|
||||
|
||||
++attrPos;
|
||||
|
||||
}
|
||||
m_VertexArray.Upload();
|
||||
|
||||
double blinkDuration = 1.0;
|
||||
|
||||
// Tests won't have config initialised
|
||||
if (CConfigDB::IsInitialised())
|
||||
{
|
||||
CFG_GET_VAL("gui.session.minimap.pingduration", m_PingDuration);
|
||||
CFG_GET_VAL("gui.session.minimap.blinkduration", blinkDuration);
|
||||
}
|
||||
m_HalfBlinkDuration = blinkDuration/2;
|
||||
}
|
||||
|
||||
CMiniMap::~CMiniMap() = default;
|
||||
@ -410,34 +356,6 @@ void CMiniMap::DrawFlare(CCanvas2D& canvas, const MapFlare& flare, double curren
|
||||
}
|
||||
}
|
||||
|
||||
struct MinimapUnitVertex
|
||||
{
|
||||
// This struct is copyable for convenience and because to move is to copy for primitives.
|
||||
u8 r, g, b, a;
|
||||
float x, y;
|
||||
};
|
||||
|
||||
// Adds a vertex to the passed VertexArray
|
||||
static void inline addVertex(const MinimapUnitVertex& v,
|
||||
VertexArrayIterator<u8[4]>& attrColor,
|
||||
VertexArrayIterator<float[2]>& attrPos)
|
||||
{
|
||||
(*attrColor)[0] = v.r;
|
||||
(*attrColor)[1] = v.g;
|
||||
(*attrColor)[2] = v.b;
|
||||
(*attrColor)[3] = v.a;
|
||||
++attrColor;
|
||||
|
||||
(*attrPos)[0] = v.x;
|
||||
(*attrPos)[1] = v.y;
|
||||
|
||||
++attrPos;
|
||||
}
|
||||
|
||||
// TODO: render the minimap in a framebuffer and just draw the frambuffer texture
|
||||
// most of the time, updating the framebuffer twice a frame.
|
||||
// Here it updates as ping-pong either texture or vertex array each sec to lower gpu stalling
|
||||
// (those operations cause a gpu sync, which slows down the way gpu works)
|
||||
void CMiniMap::Draw(CCanvas2D& canvas)
|
||||
{
|
||||
PROFILE3("render minimap");
|
||||
@ -447,247 +365,66 @@ void CMiniMap::Draw(CCanvas2D& canvas)
|
||||
if (!g_Game || !g_Game->IsGameStarted())
|
||||
return;
|
||||
|
||||
if (!m_Mask)
|
||||
canvas.DrawRect(m_CachedActualSize, CColor(0.0f, 0.0f, 0.0f, 1.0f));
|
||||
|
||||
canvas.Flush();
|
||||
|
||||
CSimulation2* sim = g_Game->GetSimulation2();
|
||||
CmpPtr<ICmpRangeManager> cmpRangeManager(*sim, SYSTEM_ENTITY);
|
||||
ENSURE(cmpRangeManager);
|
||||
|
||||
CLOSTexture& losTexture = g_Game->GetView()->GetLOSTexture();
|
||||
CMiniMapTexture& miniMapTexture = g_Game->GetView()->GetMiniMapTexture();
|
||||
|
||||
// Set our globals in case they hadn't been set before
|
||||
const CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
|
||||
ssize_t width = (u32)(m_CachedActualSize.right - m_CachedActualSize.left);
|
||||
ssize_t height = (u32)(m_CachedActualSize.bottom - m_CachedActualSize.top);
|
||||
m_MapSize = terrain->GetVerticesPerSide();
|
||||
GLsizei textureSize = miniMapTexture.GetTerrainTextureSize();
|
||||
m_MapScale = (cmpRangeManager->GetLosCircular() ? 1.f : 1.414f);
|
||||
|
||||
// only update 2x / second
|
||||
// (note: since units only move a few pixels per second on the minimap,
|
||||
// we can get away with infrequent updates; this is slow)
|
||||
// TODO: Update all but camera at same speed as simulation
|
||||
static double last_time;
|
||||
const double cur_time = timer_Time();
|
||||
const bool doUpdate = cur_time - last_time > 0.5;
|
||||
if (doUpdate)
|
||||
last_time = cur_time;
|
||||
|
||||
const float x = m_CachedActualSize.left, y = m_CachedActualSize.bottom;
|
||||
const float x2 = m_CachedActualSize.right, y2 = m_CachedActualSize.top;
|
||||
const float texCoordMax = (float)(m_MapSize - 1) / (float)textureSize;
|
||||
const float angle = GetAngle();
|
||||
const float unitScale = (cmpRangeManager->GetLosCircular() ? 1.f : m_MapScale/2.f);
|
||||
|
||||
CShaderProgramPtr shader;
|
||||
CShaderTechniquePtr tech;
|
||||
|
||||
CShaderDefines baseDefines;
|
||||
baseDefines.Add(str_MINIMAP_BASE, str_1);
|
||||
if (m_Mask)
|
||||
baseDefines.Add(str_MINIMAP_MASK, str_1);
|
||||
|
||||
tech = g_Renderer.GetShaderManager().LoadEffect(str_minimap, g_Renderer.GetSystemShaderDefines(), baseDefines);
|
||||
tech->BeginPass();
|
||||
shader = tech->GetShader();
|
||||
|
||||
// Draw the main textured quad
|
||||
if (miniMapTexture.GetTerrainTexture())
|
||||
shader->BindTexture(str_baseTex, miniMapTexture.GetTerrainTexture());
|
||||
if (m_Mask)
|
||||
CMiniMapTexture& miniMapTexture = g_Game->GetView()->GetMiniMapTexture();
|
||||
if (miniMapTexture.GetTexture())
|
||||
{
|
||||
shader->BindTexture(str_maskTex, losTexture.GetTexture());
|
||||
CMatrix3D maskTextureTransform = *losTexture.GetMinimapTextureMatrix();
|
||||
// We need to have texture coordinates in the same coordinate space.
|
||||
const float scale = 1.0f / texCoordMax;
|
||||
maskTextureTransform.Scale(scale, scale, 1.0f);
|
||||
shader->Uniform(str_maskTextureTransform, maskTextureTransform);
|
||||
}
|
||||
const CMatrix3D baseTransform = GetDefaultGuiMatrix();
|
||||
CMatrix3D baseTextureTransform;
|
||||
baseTextureTransform.SetIdentity();
|
||||
shader->Uniform(str_transform, baseTransform);
|
||||
shader->Uniform(str_textureTransform, baseTextureTransform);
|
||||
CShaderProgramPtr shader;
|
||||
CShaderTechniquePtr tech;
|
||||
|
||||
if (m_Mask)
|
||||
{
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
}
|
||||
CShaderDefines baseDefines;
|
||||
baseDefines.Add(str_MINIMAP_BASE, str_1);
|
||||
|
||||
if (miniMapTexture.GetTerrainTexture())
|
||||
DrawTexture(shader, texCoordMax, angle, x, y, x2, y2, m_MapScale);
|
||||
|
||||
if (!m_Mask)
|
||||
{
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
}
|
||||
|
||||
// Draw territory boundaries
|
||||
CTerritoryTexture& territoryTexture = g_Game->GetView()->GetTerritoryTexture();
|
||||
|
||||
shader->BindTexture(str_baseTex, territoryTexture.GetTexture());
|
||||
if (m_Mask)
|
||||
{
|
||||
shader->BindTexture(str_maskTex, losTexture.GetTexture());
|
||||
shader->Uniform(str_maskTextureTransform, *losTexture.GetMinimapTextureMatrix());
|
||||
}
|
||||
const CMatrix3D* territoryTransform = territoryTexture.GetMinimapTextureMatrix();
|
||||
shader->Uniform(str_transform, baseTransform);
|
||||
shader->Uniform(str_textureTransform, *territoryTransform);
|
||||
|
||||
DrawTexture(shader, 1.0f, angle, x, y, x2, y2, m_MapScale);
|
||||
tech->EndPass();
|
||||
|
||||
// Draw the LOS quad in black, using alpha values from the LOS texture
|
||||
if (!m_Mask)
|
||||
{
|
||||
CShaderDefines losDefines;
|
||||
losDefines.Add(str_MINIMAP_LOS, str_1);
|
||||
tech = g_Renderer.GetShaderManager().LoadEffect(str_minimap, g_Renderer.GetSystemShaderDefines(), losDefines);
|
||||
tech = g_Renderer.GetShaderManager().LoadEffect(str_minimap, g_Renderer.GetSystemShaderDefines(), baseDefines);
|
||||
tech->BeginPass();
|
||||
shader = tech->GetShader();
|
||||
shader->BindTexture(str_baseTex, losTexture.GetTexture());
|
||||
|
||||
const CMatrix3D* losTransform = losTexture.GetMinimapTextureMatrix();
|
||||
shader->BindTexture(str_baseTex, miniMapTexture.GetTexture());
|
||||
const CMatrix3D baseTransform = GetDefaultGuiMatrix();
|
||||
CMatrix3D baseTextureTransform;
|
||||
baseTextureTransform.SetIdentity();
|
||||
shader->Uniform(str_transform, baseTransform);
|
||||
shader->Uniform(str_textureTransform, *losTransform);
|
||||
shader->Uniform(str_textureTransform, baseTextureTransform);
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
const float x = m_CachedActualSize.left, y = m_CachedActualSize.bottom;
|
||||
const float x2 = m_CachedActualSize.right, y2 = m_CachedActualSize.top;
|
||||
const float angle = GetAngle();
|
||||
DrawTexture(shader, angle, x, y, x2, y2, m_MapScale);
|
||||
|
||||
DrawTexture(shader, 1.0f, angle, x, y, x2, y2, m_MapScale);
|
||||
tech->EndPass();
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
PROFILE_START("minimap units and flares");
|
||||
|
||||
CShaderDefines pointDefines;
|
||||
pointDefines.Add(str_MINIMAP_POINT, str_1);
|
||||
tech = g_Renderer.GetShaderManager().LoadEffect(str_minimap, g_Renderer.GetSystemShaderDefines(), pointDefines);
|
||||
tech->BeginPass();
|
||||
shader = tech->GetShader();
|
||||
shader->Uniform(str_transform, baseTransform);
|
||||
shader->Uniform(str_pointSize, 3.f);
|
||||
|
||||
CMatrix3D unitMatrix;
|
||||
unitMatrix.SetIdentity();
|
||||
// Center the minimap on the origin of the axis of rotation.
|
||||
unitMatrix.Translate(-(x2 - x) / 2.f, -(y2 - y) / 2.f, 0.f);
|
||||
// Rotate the map.
|
||||
unitMatrix.RotateZ(angle);
|
||||
// Scale square maps to fit.
|
||||
unitMatrix.Scale(unitScale, unitScale, 1.f);
|
||||
// Move the minimap back to it's starting position.
|
||||
unitMatrix.Translate((x2 - x) / 2.f, (y2 - y) / 2.f, 0.f);
|
||||
// Move the minimap to it's final location.
|
||||
unitMatrix.Translate(x, y, 0.0f);
|
||||
// Apply the gui matrix.
|
||||
unitMatrix *= GetDefaultGuiMatrix();
|
||||
// Load the transform into the shader.
|
||||
shader->Uniform(str_transform, unitMatrix);
|
||||
|
||||
const float sx = (float)width / ((m_MapSize - 1) * TERRAIN_TILE_SIZE);
|
||||
const float sy = (float)height / ((m_MapSize - 1) * TERRAIN_TILE_SIZE);
|
||||
|
||||
CSimulation2::InterfaceList ents = sim->GetEntitiesWithInterface(IID_Minimap);
|
||||
|
||||
if (doUpdate)
|
||||
{
|
||||
VertexArrayIterator<float[2]> attrPos = m_AttributePos.GetIterator<float[2]>();
|
||||
VertexArrayIterator<u8[4]> attrColor = m_AttributeColor.GetIterator<u8[4]>();
|
||||
|
||||
m_EntitiesDrawn = 0;
|
||||
MinimapUnitVertex v;
|
||||
std::vector<MinimapUnitVertex> pingingVertices;
|
||||
pingingVertices.reserve(MAX_ENTITIES_DRAWN / 2);
|
||||
|
||||
if (cur_time > m_NextBlinkTime)
|
||||
{
|
||||
m_BlinkState = !m_BlinkState;
|
||||
m_NextBlinkTime = cur_time + m_HalfBlinkDuration;
|
||||
}
|
||||
|
||||
entity_pos_t posX, posZ;
|
||||
for (CSimulation2::InterfaceList::const_iterator it = ents.begin(); it != ents.end(); ++it)
|
||||
{
|
||||
ICmpMinimap* cmpMinimap = static_cast<ICmpMinimap*>(it->second);
|
||||
if (cmpMinimap->GetRenderData(v.r, v.g, v.b, posX, posZ))
|
||||
{
|
||||
LosVisibility vis = cmpRangeManager->GetLosVisibility(it->first, g_Game->GetSimulation2()->GetSimContext().GetCurrentDisplayedPlayer());
|
||||
if (vis != LosVisibility::HIDDEN)
|
||||
{
|
||||
v.a = 255;
|
||||
v.x = posX.ToFloat() * sx;
|
||||
v.y = -posZ.ToFloat() * sy;
|
||||
|
||||
// Check minimap pinging to indicate something
|
||||
if (m_BlinkState && cmpMinimap->CheckPing(cur_time, m_PingDuration))
|
||||
{
|
||||
v.r = 255; // ping color is white
|
||||
v.g = 255;
|
||||
v.b = 255;
|
||||
pingingVertices.push_back(v);
|
||||
}
|
||||
else
|
||||
{
|
||||
addVertex(v, attrColor, attrPos);
|
||||
++m_EntitiesDrawn;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add the pinged vertices at the end, so they are drawn on top
|
||||
for (const MinimapUnitVertex& vertex : pingingVertices)
|
||||
{
|
||||
addVertex(vertex, attrColor, attrPos);
|
||||
++m_EntitiesDrawn;
|
||||
}
|
||||
|
||||
ENSURE(m_EntitiesDrawn < MAX_ENTITIES_DRAWN);
|
||||
m_VertexArray.Upload();
|
||||
}
|
||||
|
||||
m_VertexArray.PrepareForRendering();
|
||||
|
||||
if (m_EntitiesDrawn > 0)
|
||||
{
|
||||
#if !CONFIG2_GLES
|
||||
glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
|
||||
#endif
|
||||
|
||||
u8* indexBase = m_IndexArray.Bind();
|
||||
u8* base = m_VertexArray.Bind();
|
||||
const GLsizei stride = (GLsizei)m_VertexArray.GetStride();
|
||||
|
||||
shader->VertexPointer(2, GL_FLOAT, stride, base + m_AttributePos.offset);
|
||||
shader->ColorPointer(4, GL_UNSIGNED_BYTE, stride, base + m_AttributeColor.offset);
|
||||
shader->AssertPointersBound();
|
||||
|
||||
if (!g_Renderer.DoSkipSubmit())
|
||||
glDrawElements(GL_POINTS, (GLsizei)(m_EntitiesDrawn), GL_UNSIGNED_SHORT, indexBase);
|
||||
|
||||
g_Renderer.GetStats().m_DrawCalls++;
|
||||
CVertexBuffer::Unbind();
|
||||
|
||||
#if !CONFIG2_GLES
|
||||
glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);
|
||||
#endif
|
||||
}
|
||||
|
||||
tech->EndPass();
|
||||
PROFILE_START("minimap flares");
|
||||
|
||||
DrawViewRect(canvas);
|
||||
|
||||
while (!m_MapFlares.empty() && m_FlareLifetimeSeconds + m_MapFlares.front().time < cur_time)
|
||||
const double currentTime = timer_Time();
|
||||
while (!m_MapFlares.empty() && m_FlareLifetimeSeconds + m_MapFlares.front().time < currentTime)
|
||||
m_MapFlares.pop_front();
|
||||
|
||||
for (const MapFlare& flare : m_MapFlares)
|
||||
DrawFlare(canvas, flare, cur_time);
|
||||
DrawFlare(canvas, flare, currentTime);
|
||||
|
||||
PROFILE_END("minimap units and flares");
|
||||
PROFILE_END("minimap flares");
|
||||
}
|
||||
|
||||
bool CMiniMap::Flare(const CVector2D& pos, const CStr& colorStr)
|
||||
|
@ -100,18 +100,6 @@ private:
|
||||
|
||||
float GetAngle() const;
|
||||
CVector2D WorldSpaceToMiniMapSpace(const CVector3D& worldPosition) const;
|
||||
|
||||
VertexIndexArray m_IndexArray;
|
||||
VertexArray m_VertexArray;
|
||||
VertexArray::Attribute m_AttributePos;
|
||||
VertexArray::Attribute m_AttributeColor;
|
||||
|
||||
size_t m_EntitiesDrawn;
|
||||
|
||||
double m_PingDuration;
|
||||
double m_HalfBlinkDuration;
|
||||
double m_NextBlinkTime;
|
||||
bool m_BlinkState;
|
||||
};
|
||||
|
||||
#endif // INCLUDED_MINIMAP
|
||||
|
@ -929,6 +929,7 @@ void InitGraphics(const CmdLineArgs& args, int flags, const std::vector<CStr>& i
|
||||
if ((ogl_HaveExtensions(0, "GL_ARB_vertex_program", "GL_ARB_fragment_program", NULL) != 0 // ARB
|
||||
&& ogl_HaveExtensions(0, "GL_ARB_vertex_shader", "GL_ARB_fragment_shader", NULL) != 0) // GLSL
|
||||
|| !ogl_HaveExtension("GL_ARB_vertex_buffer_object") // VBO
|
||||
|| (!ogl_HaveExtension("GL_EXT_framebuffer_object") && !ogl_HaveExtension("GL_ARB_framebuffer_object"))
|
||||
|| RenderPathEnum::FromString(renderPath) == FIXED)
|
||||
{
|
||||
// It doesn't make sense to continue working here, because we're not
|
||||
|
Loading…
Reference in New Issue
Block a user