mirror of
https://github.com/rwengine/openrw.git
synced 2024-09-15 15:02:34 +02:00
Re-implement map rendering with nice circular minimap
This commit is contained in:
parent
d00ef52c2f
commit
7888f05ca4
@ -15,23 +15,22 @@ public:
|
||||
|
||||
struct MapInfo
|
||||
{
|
||||
float scale = 1.f;
|
||||
/// World coordinate center
|
||||
glm::vec2 center;
|
||||
glm::vec2 worldCenter;
|
||||
/// World units to fit on the map
|
||||
float worldSize;
|
||||
|
||||
/// yaw of the map
|
||||
float rotation = 0.f;
|
||||
|
||||
/// Top of the map on the screen
|
||||
glm::vec2 mapScreenBottom;
|
||||
/// Bottom of the map on the screen
|
||||
glm::vec2 mapScreenTop;
|
||||
|
||||
glm::vec2 screenPosition;
|
||||
float screenSize;
|
||||
/// Make the map circular, or don't.
|
||||
bool clipToSize = true;
|
||||
};
|
||||
|
||||
MapRenderer(Renderer* renderer, GameData* data);
|
||||
|
||||
glm::vec2 worldToMap(const glm::vec2& coord);
|
||||
glm::vec2 mapToScreen(const glm::vec2& map, const MapInfo& mi);
|
||||
|
||||
void draw(GameWorld* world, const MapInfo& mi);
|
||||
|
||||
private:
|
||||
@ -40,8 +39,11 @@ private:
|
||||
|
||||
GeometryBuffer rectGeom;
|
||||
DrawBuffer rect;
|
||||
|
||||
GeometryBuffer circleGeom;
|
||||
DrawBuffer circle;
|
||||
|
||||
Renderer::ShaderProgram* rectProg;
|
||||
|
||||
void drawBlip(const glm::vec2& map, const glm::mat4& model, const MapInfo& mi, const std::string& texture, float heading = 0.f);
|
||||
};
|
||||
void drawBlip(const glm::vec2& map, const glm::mat4& view, const MapInfo& mi, const std::string& texture, float heading = 0.f, float size = 18.f);
|
||||
};
|
||||
|
@ -520,7 +520,9 @@ void GameRenderer::renderWorld(GameWorld* world, const ViewCamera &camera, float
|
||||
void GameRenderer::renderPostProcess()
|
||||
{
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
||||
glStencilMask(0xFF);
|
||||
glClearStencil(0x00);
|
||||
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
||||
|
||||
renderer->useProgram(postProg);
|
||||
|
||||
|
@ -34,8 +34,8 @@ out vec4 outColour;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 c = texture(spriteTexture, TexCoord);
|
||||
outColour = colour + c;
|
||||
vec4 c = texture(spriteTexture, TexCoord*0.99);
|
||||
outColour = vec4(colour.rgb + c.rgb, colour.a * c.a);
|
||||
})";
|
||||
|
||||
|
||||
@ -50,6 +50,15 @@ MapRenderer::MapRenderer(Renderer* renderer, GameData* _data)
|
||||
});
|
||||
rect.addGeometry(&rectGeom);
|
||||
rect.setFaceType(GL_TRIANGLE_STRIP);
|
||||
|
||||
std::vector<VertexP2> circleVerts;
|
||||
circleVerts.push_back({0.f, 0.f});
|
||||
for (int v = 0; v < 181; ++v) {
|
||||
circleVerts.push_back({0.5f*cos(2*(v/180.f)*M_PI), 0.5f*sin(2*(v/180.f)*M_PI)});
|
||||
}
|
||||
circleGeom.uploadVertices(circleVerts);
|
||||
circle.addGeometry(&circleGeom);
|
||||
circle.setFaceType(GL_TRIANGLE_FAN);
|
||||
|
||||
rectProg = renderer->createShader(
|
||||
MapVertexShader,
|
||||
@ -61,61 +70,53 @@ MapRenderer::MapRenderer(Renderer* renderer, GameData* _data)
|
||||
|
||||
#define GAME_MAP_SIZE 4000
|
||||
|
||||
glm::vec2 MapRenderer::worldToMap(const glm::vec2& coord)
|
||||
{
|
||||
return glm::vec2(coord.x, -coord.y);
|
||||
}
|
||||
|
||||
glm::vec2 MapRenderer::mapToScreen(const glm::vec2& map, const MapInfo& mi)
|
||||
{
|
||||
glm::vec2 screenSize = ((map-mi.center) * mi.scale);
|
||||
glm::vec2 screenCenter(500.f, 500.f);
|
||||
return screenSize + screenCenter;
|
||||
}
|
||||
|
||||
void MapRenderer::draw(GameWorld* world, const MapInfo& mi)
|
||||
{
|
||||
renderer->pushDebugGroup("Map");
|
||||
renderer->useProgram(rectProg);
|
||||
|
||||
glm::vec2 bottom = glm::min(mi.mapScreenBottom, mi.mapScreenTop);
|
||||
glm::vec2 top = glm::max(mi.mapScreenBottom, mi.mapScreenTop);
|
||||
glm::vec2 screenPos = (bottom+top)/2.f;
|
||||
glm::vec2 scissorSize = top - bottom;
|
||||
const glm::ivec2& vp = renderer->getViewport();
|
||||
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
glScissor(bottom.x, vp.y - bottom.y - scissorSize.y, scissorSize.x, scissorSize.y);
|
||||
|
||||
auto proj = renderer->get2DProjection();
|
||||
glm::mat4 view, model;
|
||||
view = glm::translate(view, glm::vec3(screenPos, 0.f));
|
||||
view = glm::scale(view, glm::vec3(mi.scale));
|
||||
model = glm::rotate(model, mi.rotation, glm::vec3(0.f, 0.f, 1.f));
|
||||
model = glm::translate(model, glm::vec3(-worldToMap(mi.center), 0.f));
|
||||
renderer->setUniform(rectProg, "view", view);
|
||||
renderer->setUniform(rectProg, "proj", proj);
|
||||
|
||||
|
||||
// World out the number of units per tile
|
||||
glm::vec2 worldSize(GAME_MAP_SIZE);
|
||||
const int mapBlockLine = 8;
|
||||
glm::vec2 tileSize = worldSize / (float)mapBlockLine;
|
||||
|
||||
// Determine the scale to show the right number of world units on the screen
|
||||
float worldScale = mi.screenSize / mi.worldSize;
|
||||
|
||||
auto proj = renderer->get2DProjection();
|
||||
glm::mat4 view, model;
|
||||
renderer->setUniform(rectProg, "proj", proj);
|
||||
renderer->setUniform(rectProg, "model", glm::mat4());
|
||||
renderer->setUniform(rectProg, "colour", glm::vec4(0.f, 0.f, 0.f, 1.f));
|
||||
|
||||
view = glm::translate(view, glm::vec3(mi.screenPosition, 0.f));
|
||||
|
||||
if (mi.clipToSize)
|
||||
{
|
||||
glBindVertexArray( circle.getVAOName() );
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glm::mat4 circleView = glm::scale(view, glm::vec3(mi.screenSize));
|
||||
renderer->setUniform(rectProg, "view", circleView);
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
glStencilFunc(GL_ALWAYS, 1, 0xFF);
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
|
||||
glStencilMask(0xFF);
|
||||
glColorMask(0x00, 0x00, 0x00, 0x00);
|
||||
glDrawArrays(GL_TRIANGLE_FAN, 0, 182);
|
||||
glColorMask(0xFF, 0xFF, 0xFF, 0xFF);
|
||||
glStencilFunc(GL_EQUAL, 1, 0xFF);
|
||||
}
|
||||
|
||||
view = glm::scale(view, glm::vec3(worldScale));
|
||||
view = glm::rotate(view, mi.rotation, glm::vec3(0.f, 0.f, 1.f));
|
||||
view = glm::translate(view, glm::vec3(glm::vec2(-1.f, 1.f) * mi.worldCenter, 0.f));
|
||||
renderer->setUniform(rectProg, "view", view);
|
||||
|
||||
glBindVertexArray( rect.getVAOName() );
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
renderer->setUniformTexture(rectProg, "spriteTexture", 0);
|
||||
|
||||
glm::mat4 bg = glm::scale( model, glm::vec3( glm::vec2( worldSize ), 1.f ) );
|
||||
|
||||
renderer->setUniform(rectProg, "model", bg);
|
||||
renderer->setUniform(rectProg, "colour", glm::vec4(0.0f, 0.0f, 0.0f, 1.f));
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
// 2048, -2048
|
||||
|
||||
renderer->setUniform(rectProg, "colour", glm::vec4(0.f));
|
||||
// radar00 = -x, +y
|
||||
// incrementing in X, then Y
|
||||
|
||||
int initX = -(mapBlockLine/2);
|
||||
int initY = -(mapBlockLine/2);
|
||||
@ -141,10 +142,24 @@ void MapRenderer::draw(GameWorld* world, const MapInfo& mi)
|
||||
|
||||
glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );
|
||||
}
|
||||
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
|
||||
renderer->setUniform(rectProg, "view", view);
|
||||
|
||||
// From here on out we will work in screenspace
|
||||
renderer->setUniform(rectProg, "view", glm::mat4());
|
||||
|
||||
if (mi.clipToSize) {
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
// We only need the outer ring if we're clipping.
|
||||
glBlendFuncSeparate(GL_DST_COLOR, GL_ZERO, GL_ONE, GL_ZERO);
|
||||
TextureData::Handle radarDisc = data->findTexture("radardisc");
|
||||
|
||||
glm::mat4 model;
|
||||
model = glm::translate(model, glm::vec3(mi.screenPosition, 0.0f));
|
||||
model = glm::scale(model, glm::vec3(mi.screenSize*1.07));
|
||||
renderer->setUniform(rectProg, "model", model);
|
||||
glBindTexture(GL_TEXTURE_2D, radarDisc->getName());
|
||||
glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
|
||||
}
|
||||
|
||||
for(auto& blip : world->state->radarBlips)
|
||||
{
|
||||
@ -170,7 +185,7 @@ void MapRenderer::draw(GameWorld* world, const MapInfo& mi)
|
||||
}
|
||||
}
|
||||
|
||||
drawBlip(blippos, model, mi, "");
|
||||
drawBlip(blippos, view, mi, blip.second.texture);
|
||||
}
|
||||
|
||||
// Draw the player blip
|
||||
@ -179,9 +194,11 @@ void MapRenderer::draw(GameWorld* world, const MapInfo& mi)
|
||||
{
|
||||
glm::vec2 plyblip(player->getPosition());
|
||||
float hdg = glm::roll(player->getRotation());
|
||||
drawBlip(plyblip, model, mi, "radar_centre", hdg);
|
||||
drawBlip(plyblip, view, mi, "radar_centre", mi.rotation - hdg);
|
||||
}
|
||||
|
||||
|
||||
drawBlip(mi.worldCenter + glm::vec2(0.f, mi.worldSize), view, mi, "radar_north", 0.f, 24.f);
|
||||
|
||||
glBindVertexArray( 0 );
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glUseProgram(0);
|
||||
@ -191,33 +208,31 @@ void MapRenderer::draw(GameWorld* world, const MapInfo& mi)
|
||||
renderer->popDebugGroup();
|
||||
}
|
||||
|
||||
void MapRenderer::drawBlip(const glm::vec2& coord, const glm::mat4& model, const MapInfo& mi, const std::string& texture, float heading)
|
||||
void MapRenderer::drawBlip(const glm::vec2& coord, const glm::mat4& view, const MapInfo& mi, const std::string& texture, float heading, float size)
|
||||
{
|
||||
auto relPos = glm::vec2( model * glm::vec4( worldToMap(coord), 0.f, 1.f ) );
|
||||
|
||||
// Now that relPos is relative to the rotation of the map, we can clip it.
|
||||
glm::vec2 adjustedCoord = coord;
|
||||
if (mi.clipToSize)
|
||||
{
|
||||
float maxDist = mi.worldSize/2.f;
|
||||
float centerDist = glm::distance(coord, mi.worldCenter);
|
||||
if (centerDist > maxDist) {
|
||||
adjustedCoord = mi.worldCenter + ((coord - mi.worldCenter)/centerDist)*maxDist;
|
||||
}
|
||||
}
|
||||
|
||||
float invScale = 1.f/mi.scale;
|
||||
glm::vec2 mapCenter( (mi.mapScreenTop + mi.mapScreenBottom) / 2.f );
|
||||
glm::vec2 mapMin( (mi.mapScreenBottom - mapCenter) );
|
||||
glm::vec2 mapMax( (mi.mapScreenTop - mapCenter) );
|
||||
|
||||
relPos = glm::max( mapMin * invScale, glm::min( mapMax * invScale, relPos ) );
|
||||
glm::vec2 map = glm::vec2( glm::inverse(model) * glm::vec4( relPos, 0.f, 1.f ) );
|
||||
glm::vec3 viewPos(view * glm::vec4(glm::vec2(1.f,-1.f)*adjustedCoord, 0.f, 1.f));
|
||||
glm::mat4 model;
|
||||
model = glm::translate(model, viewPos);
|
||||
model = glm::scale(model, glm::vec3(size));
|
||||
model = glm::rotate(model, heading, glm::vec3(0.f, 0.f, 1.f));
|
||||
renderer->setUniform(rectProg, "model", model);
|
||||
|
||||
glm::mat4 m = model;
|
||||
m = glm::translate(m, glm::vec3(map, 0.f));
|
||||
m = glm::scale(m, glm::vec3(16.f * 1.f/mi.scale));
|
||||
m = glm::rotate(m, heading, glm::vec3(0.f, 0.f, -1.f));
|
||||
|
||||
renderer->setUniform(rectProg, "model", m);
|
||||
|
||||
GLuint tex = 0;
|
||||
if ( !texture.empty() )
|
||||
{
|
||||
auto sprite= data->findTexture(texture);
|
||||
tex = sprite->getName();
|
||||
renderer->setUniform(rectProg, "colour", glm::vec4(0.f));
|
||||
renderer->setUniform(rectProg, "colour", glm::vec4(0.f, 0.f, 0.f, 1.f));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -21,30 +21,31 @@ const glm::vec3 ui_timeColour(RGB_COLOR(196, 165, 119));
|
||||
const glm::vec3 ui_moneyColour(RGB_COLOR(89, 113, 147));
|
||||
const glm::vec3 ui_healthColour(RGB_COLOR(187, 102, 47));
|
||||
const glm::vec3 ui_armourColour(RGB_COLOR(123, 136, 93));
|
||||
const float ui_mapSize = 150.f;
|
||||
const float ui_worldSizeMin = 200.f;
|
||||
const float ui_worldSizeMax = 300.f;
|
||||
|
||||
void drawMap(PlayerController* player, GameWorld* world, GameRenderer* render)
|
||||
void drawMap(ViewCamera& currentView, PlayerController* player, GameWorld* world, GameRenderer* render)
|
||||
{
|
||||
MapRenderer::MapInfo map;
|
||||
map.scale = 0.4f;
|
||||
|
||||
glm::quat plyRot;
|
||||
glm::quat camRot = currentView.rotation;
|
||||
|
||||
map.rotation = glm::roll(camRot) - M_PI/2.f;
|
||||
map.worldSize = ui_worldSizeMin;
|
||||
map.worldSize = ui_worldSizeMax;
|
||||
if( player )
|
||||
{
|
||||
plyRot = player->getCharacter()->getRotation();
|
||||
}
|
||||
|
||||
map.rotation = glm::roll(plyRot);
|
||||
|
||||
const glm::ivec2& vp = render->getRenderer()->getViewport();
|
||||
|
||||
map.mapScreenTop = glm::vec2(260.f, vp.y - 10.f);
|
||||
map.mapScreenBottom = glm::vec2(10.f, vp.y - 210.f);
|
||||
|
||||
if( player )
|
||||
{
|
||||
map.center = glm::vec2(player->getCharacter()->getPosition());
|
||||
map.worldCenter = glm::vec2(player->getCharacter()->getPosition());
|
||||
}
|
||||
|
||||
const glm::ivec2 &vp = render->getRenderer()->getViewport();
|
||||
|
||||
glm::vec2 mapTop = glm::vec2(ui_outerMargin, vp.y - (ui_outerMargin + ui_mapSize));
|
||||
glm::vec2 mapBottom = glm::vec2(ui_outerMargin + ui_mapSize, vp.y - ui_outerMargin);
|
||||
|
||||
map.screenPosition = (mapTop + mapBottom)/2.f;
|
||||
map.screenSize = ui_mapSize * 0.95;
|
||||
|
||||
render->map.draw(world, map);
|
||||
}
|
||||
@ -193,9 +194,9 @@ void drawPlayerInfo(PlayerController* player, GameWorld* world, GameRenderer* re
|
||||
}
|
||||
}
|
||||
|
||||
void drawHUD(PlayerController* player, GameWorld* world, GameRenderer* render)
|
||||
void drawHUD(ViewCamera& currentView, PlayerController* player, GameWorld* world, GameRenderer* render)
|
||||
{
|
||||
drawMap(player, world, render);
|
||||
drawMap(currentView, player, world, render);
|
||||
drawPlayerInfo(player, world, render);
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,6 @@
|
||||
|
||||
class PlayerController;
|
||||
|
||||
void drawHUD(PlayerController* player, GameWorld* world, GameRenderer* render);
|
||||
void drawHUD(ViewCamera& currentView, PlayerController* player, GameWorld* world, GameRenderer* render);
|
||||
|
||||
void drawOnScreenText(GameWorld* world, GameRenderer* renderer);
|
||||
void drawOnScreenText(GameWorld* world, GameRenderer* renderer);
|
||||
|
@ -245,7 +245,7 @@ void IngameState::draw(GameRenderer* r)
|
||||
{
|
||||
if( !getWorld()->state->isCinematic && getWorld()->isCutsceneDone() )
|
||||
{
|
||||
drawHUD(game->getPlayer(), getWorld(), r);
|
||||
drawHUD(_look, game->getPlayer(), getWorld(), r);
|
||||
}
|
||||
|
||||
State::draw(r);
|
||||
|
@ -35,9 +35,10 @@ void PauseState::draw(GameRenderer* r)
|
||||
|
||||
auto& vp = r->getRenderer()->getViewport();
|
||||
|
||||
map.scale = 0.2f;
|
||||
map.mapScreenTop = glm::vec2(vp.x, vp.y);
|
||||
map.mapScreenBottom = glm::vec2(0.f, 0.f);
|
||||
map.worldSize = 4000.f;
|
||||
map.clipToSize = false;
|
||||
map.screenPosition = glm::vec2(vp.x/2, vp.y/2);
|
||||
map.screenSize = std::max(vp.x, vp.y);
|
||||
|
||||
game->getRenderer()->map.draw(getWorld(), map);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user