mirror of
https://github.com/rwengine/openrw.git
synced 2024-11-07 03:12:36 +01:00
Improve map rendering, UI viewport
+ UI Viewport is now window sized + Map renderer now supports rotating
This commit is contained in:
parent
2344024f08
commit
9855bf2967
@ -14,18 +14,22 @@ public:
|
||||
|
||||
struct MapInfo
|
||||
{
|
||||
glm::vec2 viewport;
|
||||
float scale;
|
||||
float scale = 1.f;
|
||||
/// World coordinate center
|
||||
glm::vec2 center;
|
||||
/// yaw of the map
|
||||
float rotation = 0.f;
|
||||
|
||||
glm::vec2 mapSize;
|
||||
glm::vec2 mapPos;
|
||||
/// Top of the map on the screen
|
||||
glm::vec2 mapScreenBottom;
|
||||
/// Bottom of the map on the screen
|
||||
glm::vec2 mapScreenTop;
|
||||
};
|
||||
|
||||
MapRenderer(GameWorld* world, Renderer* renderer);
|
||||
|
||||
glm::vec2 worldToMap(const glm::vec2& coord);
|
||||
glm::vec2 mapToScreen(const glm::vec2& map, const MapInfo& mi);
|
||||
glm::vec2 sizeOnScreen(const glm::vec2& map, const MapInfo& mi);
|
||||
|
||||
void draw(const MapInfo& mi);
|
||||
|
||||
@ -38,5 +42,5 @@ private:
|
||||
|
||||
Renderer::ShaderProgram* rectProg;
|
||||
|
||||
void drawBlip(const glm::vec2& map, const MapInfo& mi, const std::string& texture);
|
||||
void drawBlip(const glm::vec2& map, const glm::mat4& model, const MapInfo& mi, const std::string& texture, float heading = 0.f);
|
||||
};
|
@ -90,15 +90,16 @@ public:
|
||||
virtual void draw(const glm::mat4& model, DrawBuffer* draw, const DrawParameters& p) = 0;
|
||||
virtual void drawArrays(const glm::mat4& model, DrawBuffer* draw, const DrawParameters& p) = 0;
|
||||
|
||||
void setViewport(const glm::ivec2& vp) { viewport = vp; }
|
||||
void setViewport(const glm::ivec2& vp);
|
||||
const glm::ivec2& getViewport() const { return viewport; }
|
||||
|
||||
glm::mat4 get2DProjection() const;
|
||||
const glm::mat4& get2DProjection() const { return projection2D; }
|
||||
|
||||
virtual void invalidate() = 0;
|
||||
|
||||
private:
|
||||
glm::ivec2 viewport;
|
||||
glm::mat4 projection2D;
|
||||
};
|
||||
|
||||
class OpenGLRenderer : public Renderer
|
||||
|
@ -257,6 +257,10 @@ void GameRenderer::renderWorld(const ViewCamera &camera, float alpha)
|
||||
|
||||
// Store the input camera,
|
||||
_camera = camera;
|
||||
|
||||
// Set the viewport
|
||||
const glm::ivec2& vp = getRenderer()->getViewport();
|
||||
glViewport(0, 0, vp.x, vp.y);
|
||||
|
||||
glBindVertexArray( vao );
|
||||
|
||||
|
@ -4,75 +4,119 @@
|
||||
#include <ai/PlayerController.hpp>
|
||||
#include <objects/CharacterObject.hpp>
|
||||
|
||||
const char* MapVertexShader = R"(
|
||||
#version 130
|
||||
#extension GL_ARB_explicit_attrib_location : enable
|
||||
#extension GL_ARB_uniform_buffer_object : enable
|
||||
|
||||
layout(location = 0) in vec2 position;
|
||||
out vec2 TexCoord;
|
||||
|
||||
uniform mat4 proj;
|
||||
uniform mat4 view;
|
||||
uniform mat4 model;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = proj * view * model * vec4(position, 0.0, 1.0);
|
||||
// UI space is top to bottom, so flip Y.
|
||||
TexCoord = position + vec2( 0.5 );
|
||||
})";
|
||||
|
||||
const char* MapFragmentShader = R"(
|
||||
#version 130
|
||||
in vec2 TexCoord;
|
||||
uniform vec4 colour;
|
||||
uniform sampler2D spriteTexture;
|
||||
out vec4 outColour;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 c = texture(spriteTexture, TexCoord);
|
||||
outColour = colour + c;
|
||||
})";
|
||||
|
||||
|
||||
MapRenderer::MapRenderer(GameWorld* world, Renderer* renderer)
|
||||
: world( world ), renderer(renderer)
|
||||
{
|
||||
rectGeom.uploadVertices<VertexP2>({
|
||||
{-1.f, -1.f},
|
||||
{ 1.f, -1.f},
|
||||
{-1.f, 1.f},
|
||||
{ 1.f, 1.f}
|
||||
{-.5f, .5f},
|
||||
{ .5f, .5f},
|
||||
{-.5f, -.5f},
|
||||
{ .5f, -.5f}
|
||||
});
|
||||
rect.addGeometry(&rectGeom);
|
||||
rect.setFaceType(GL_TRIANGLE_STRIP);
|
||||
|
||||
rectProg = renderer->createShader(
|
||||
GameShaders::ScreenSpaceRect::VertexShader,
|
||||
GameShaders::ScreenSpaceRect::FragmentShader
|
||||
MapVertexShader,
|
||||
MapFragmentShader
|
||||
);
|
||||
|
||||
renderer->setUniform(rectProg, "colour", glm::vec4(1.f));
|
||||
renderer->setUniform(rectProg, "size", glm::vec2(1.f, 1.f));
|
||||
renderer->setUniform(rectProg, "offset", glm::vec2(1.f, 1.f));
|
||||
}
|
||||
|
||||
#define GAME_MAP_SIZE 4000
|
||||
|
||||
glm::vec2 MapRenderer::mapToScreen(const glm::vec2& map, const MapInfo& mi)
|
||||
glm::vec2 MapRenderer::worldToMap(const glm::vec2& coord)
|
||||
{
|
||||
glm::vec2 screenSize = ((map-mi.center) * mi.scale) / mi.viewport;
|
||||
glm::vec2 screenCenter = mi.mapPos / (mi.viewport/2.f) - 1.f;
|
||||
return screenSize + screenCenter;
|
||||
return glm::vec2(coord.x, -coord.y);
|
||||
}
|
||||
|
||||
glm::vec2 MapRenderer::sizeOnScreen(const glm::vec2& map, const MapRenderer::MapInfo& mi)
|
||||
glm::vec2 MapRenderer::mapToScreen(const glm::vec2& map, const MapInfo& mi)
|
||||
{
|
||||
glm::vec2 screenSize = ((map) * mi.scale) / mi.viewport;
|
||||
return screenSize;
|
||||
glm::vec2 screenSize = ((map-mi.center) * mi.scale);
|
||||
glm::vec2 screenCenter(500.f, 500.f);
|
||||
return screenSize + screenCenter;
|
||||
}
|
||||
|
||||
void MapRenderer::draw(const MapInfo& mi)
|
||||
{
|
||||
const glm::vec2& viewport = mi.viewport;
|
||||
const glm::vec2& offset = -mi.center;
|
||||
|
||||
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);
|
||||
|
||||
glm::vec2 worldSize(GAME_MAP_SIZE);
|
||||
const int mapBlockLine = 8;
|
||||
glm::vec2 tileSize = worldSize / (float)mapBlockLine;
|
||||
glm::vec2 tileScreenSize = sizeOnScreen(tileSize, mi);
|
||||
glm::vec2 basePos = mi.mapPos - (mi.mapSize/2.f);
|
||||
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
glScissor(basePos.x, basePos.y, mi.mapSize.x, mi.mapSize.y);
|
||||
|
||||
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 ) );
|
||||
|
||||
// Draw backdrop
|
||||
renderer->setUniform(rectProg, "size", sizeOnScreen(worldSize/2.f, mi));
|
||||
renderer->setUniform(rectProg, "model", bg);
|
||||
renderer->setUniform(rectProg, "colour", glm::vec4(0.0f, 0.0f, 0.0f, 1.f));
|
||||
renderer->setUniform(rectProg, "offset", mapToScreen(glm::vec2(0.f, 0.f), mi));
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
// 2048, -2048
|
||||
|
||||
renderer->setUniform(rectProg, "size", tileScreenSize/2.f);
|
||||
renderer->setUniform(rectProg, "colour", glm::vec4(0.0f, 0.0f, 0.0f, 0.f));
|
||||
renderer->setUniform(rectProg, "colour", glm::vec4(0.f));
|
||||
|
||||
int initX = -(mapBlockLine/2);
|
||||
int initY = (mapBlockLine/2);
|
||||
int initY = -(mapBlockLine/2);
|
||||
|
||||
for( int m = 0; m < MAP_BLOCK_SIZE; ++m )
|
||||
{
|
||||
std::string num = (m < 10 ? "0" : "");
|
||||
@ -80,21 +124,25 @@ void MapRenderer::draw(const MapInfo& mi)
|
||||
auto texture = world->gameData.textures[{name,""}];
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, texture.texName);
|
||||
renderer->setUniformTexture(rectProg, "texture", 0);
|
||||
|
||||
int mX = initX + (m % mapBlockLine);
|
||||
int mY = initY - (m / mapBlockLine);
|
||||
auto tc = glm::vec2(mX, mY) * tileSize + (glm::vec2(tileSize.x,-tileSize.y)/2.f);
|
||||
int mY = initY + (m / mapBlockLine);
|
||||
|
||||
auto screen = mapToScreen(tc, mi);
|
||||
auto tc = glm::vec2(mX, mY) * tileSize + glm::vec2(tileSize/2.f);
|
||||
|
||||
renderer->setUniform(rectProg, "offset", screen);
|
||||
glm::mat4 tilemodel = model;
|
||||
tilemodel = glm::translate( tilemodel, glm::vec3( tc, 0.f ) );
|
||||
tilemodel = glm::scale( tilemodel, glm::vec3( tileSize, 1.f ) );
|
||||
|
||||
renderer->setUniform(rectProg, "model", tilemodel);
|
||||
|
||||
glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );
|
||||
}
|
||||
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
|
||||
renderer->setUniform(rectProg, "view", view);
|
||||
|
||||
for(auto& blip : world->state.radarBlips)
|
||||
{
|
||||
glm::vec2 blippos( blip.second.coord );
|
||||
@ -103,7 +151,7 @@ void MapRenderer::draw(const MapInfo& mi)
|
||||
blippos = glm::vec2( blip.second.target->getPosition() );
|
||||
}
|
||||
|
||||
drawBlip(blippos, mi, "");
|
||||
drawBlip(blippos, model, mi, "");
|
||||
}
|
||||
|
||||
// Draw the player blip
|
||||
@ -111,41 +159,52 @@ void MapRenderer::draw(const MapInfo& mi)
|
||||
if( player )
|
||||
{
|
||||
glm::vec2 plyblip(player->getCharacter()->getPosition());
|
||||
drawBlip(plyblip, mi, "radar_centre");
|
||||
float hdg = glm::roll(player->getCharacter()->getRotation());
|
||||
drawBlip(plyblip, model, mi, "radar_centre", hdg);
|
||||
}
|
||||
|
||||
glBindVertexArray( 0 );
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glUseProgram(0);
|
||||
|
||||
/// @TODO migrate to using the renderer
|
||||
renderer->invalidate();
|
||||
}
|
||||
|
||||
void MapRenderer::drawBlip(const glm::vec2& map, const MapInfo& mi, const std::string& texture)
|
||||
void MapRenderer::drawBlip(const glm::vec2& coord, const glm::mat4& model, const MapInfo& mi, const std::string& texture, float heading)
|
||||
{
|
||||
glm::vec2 screen = mapToScreen(map, mi);
|
||||
auto relPos = glm::vec2( model * glm::vec4( worldToMap(coord), 0.f, 1.f ) );
|
||||
|
||||
glm::vec2 screenCenter = mi.mapPos / (mi.viewport/2.f) - 1.f;
|
||||
glm::vec2 screenSize = (mi.mapSize) / mi.viewport;
|
||||
glm::vec2 screenMax = screenCenter + screenSize;
|
||||
glm::vec2 screenMin = screenCenter - screenSize;
|
||||
// Now that relPos is relative to the rotation of the map, we can clip it.
|
||||
|
||||
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) );
|
||||
|
||||
screen = glm::max(screen, screenMin);
|
||||
screen = glm::min(screen, screenMax);
|
||||
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::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= world->gameData.textures[{texture,""}];
|
||||
tex = sprite.texName;
|
||||
renderer->setUniform(rectProg, "colour", glm::vec4(0.0f, 0.0f, 0.0f, 0.f));
|
||||
renderer->setUniform(rectProg, "colour", glm::vec4(0.f));
|
||||
}
|
||||
else
|
||||
{
|
||||
renderer->setUniform(rectProg, "colour", glm::vec4(1.0f, 1.0f, 1.0f, 1.f));
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, tex);
|
||||
|
||||
renderer->setUniform(rectProg, "size", 16.f/mi.viewport);
|
||||
|
||||
renderer->setUniform(rectProg, "offset", screen);
|
||||
glDrawArrays( GL_TRIANGLE_STRIP, 0, 4 );
|
||||
}
|
||||
|
@ -80,20 +80,11 @@ GLuint compileProgram(const char* vertex, const char* fragment)
|
||||
return prog;
|
||||
}
|
||||
|
||||
glm::mat4 Renderer::get2DProjection() const
|
||||
void Renderer::setViewport(const glm::ivec2& vp)
|
||||
{
|
||||
glm::vec2 aspect(1.f, 1.f);
|
||||
if( viewport.x > viewport.y )
|
||||
{
|
||||
// Widescreen
|
||||
aspect.x = viewport.x / (float) viewport.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Tall-o-vision
|
||||
aspect.y = viewport.y / (float)viewport.x;
|
||||
}
|
||||
return glm::ortho(0.f, 800.f * aspect.x, 600.f * aspect.y, 0.f, -1.f, 1.f);
|
||||
viewport = vp;
|
||||
|
||||
projection2D = glm::ortho(0.f, (float)viewport.x, (float)viewport.y, 0.f, -1.f, 1.f);
|
||||
}
|
||||
|
||||
void OpenGLRenderer::useDrawBuffer(DrawBuffer* dbuff)
|
||||
|
@ -191,7 +191,7 @@ void IngameState::draw(GameRenderer* r)
|
||||
{
|
||||
if( !getWorld()->state.isCinematic && getWorld()->isCutsceneDone() )
|
||||
{
|
||||
drawHUD(getWindow());
|
||||
drawHUD(r);
|
||||
}
|
||||
|
||||
State::draw(r);
|
||||
@ -299,17 +299,25 @@ const ViewCamera &IngameState::getCamera()
|
||||
return _look;
|
||||
}
|
||||
|
||||
void IngameState::drawHUD(const sf::RenderWindow& w)
|
||||
void IngameState::drawHUD(GameRenderer* renderer)
|
||||
{
|
||||
MapRenderer::MapInfo map;
|
||||
map.scale = 0.5f;
|
||||
map.viewport = glm::vec2(w.getSize().x, w.getSize().y);
|
||||
map.scale = 0.4f;
|
||||
|
||||
map.mapPos = glm::vec2(map.viewport.x * 0.2f, map.viewport.y * 0.2f);
|
||||
float aspect = map.viewport.x / map.viewport.y;
|
||||
map.mapSize = map.viewport * 0.3f;
|
||||
map.mapPos.x /= aspect;
|
||||
map.mapSize.x /= aspect;
|
||||
glm::quat plyRot;
|
||||
|
||||
if( getPlayer() )
|
||||
{
|
||||
auto p = getPlayer();
|
||||
plyRot = p->getCharacter()->getRotation();
|
||||
}
|
||||
|
||||
map.rotation = glm::roll(plyRot);
|
||||
|
||||
const glm::ivec2& vp = renderer->getRenderer()->getViewport();
|
||||
|
||||
map.mapScreenTop = glm::vec2(260.f, vp.y - 10.f);
|
||||
map.mapScreenBottom = glm::vec2(10.f, vp.y - 210.f);
|
||||
|
||||
if( getWorld()->state.player )
|
||||
{
|
||||
|
@ -31,7 +31,7 @@ public:
|
||||
const ViewCamera& getCamera();
|
||||
|
||||
private:
|
||||
void drawHUD(const sf::RenderWindow& w);
|
||||
void drawHUD(GameRenderer* r);
|
||||
};
|
||||
|
||||
#endif // INGAMESTATE_HPP
|
||||
|
@ -33,17 +33,12 @@ void PauseState::draw(GameRenderer* r)
|
||||
{
|
||||
MapRenderer::MapInfo map;
|
||||
|
||||
auto& w = getWindow();
|
||||
auto& vp = r->getRenderer()->getViewport();
|
||||
|
||||
map.scale = 0.25f;
|
||||
map.viewport = glm::vec2(w.getSize().x, w.getSize().y);
|
||||
map.mapSize = map.viewport;
|
||||
map.mapPos = map.viewport / 2.f;
|
||||
map.scale = 0.2f;
|
||||
map.mapScreenTop = glm::vec2(vp.x, vp.y);
|
||||
map.mapScreenBottom = glm::vec2(0.f, 0.f);
|
||||
|
||||
if( getWorld()->state.player )
|
||||
{
|
||||
map.center = glm::vec2(getWorld()->state.player->getCharacter()->getPosition());
|
||||
}
|
||||
getWorld()->renderer.map.draw(map);
|
||||
|
||||
State::draw(r);
|
||||
|
Loading…
Reference in New Issue
Block a user