diff --git a/rwengine/include/render/MapRenderer.hpp b/rwengine/include/render/MapRenderer.hpp index 7f7b85a6..bbcfcf99 100644 --- a/rwengine/include/render/MapRenderer.hpp +++ b/rwengine/include/render/MapRenderer.hpp @@ -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); }; \ No newline at end of file diff --git a/rwengine/include/render/OpenGLRenderer.hpp b/rwengine/include/render/OpenGLRenderer.hpp index 5579f8b9..997071e8 100644 --- a/rwengine/include/render/OpenGLRenderer.hpp +++ b/rwengine/include/render/OpenGLRenderer.hpp @@ -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 diff --git a/rwengine/src/render/GameRenderer.cpp b/rwengine/src/render/GameRenderer.cpp index 67c9c33c..1c061b45 100644 --- a/rwengine/src/render/GameRenderer.cpp +++ b/rwengine/src/render/GameRenderer.cpp @@ -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 ); diff --git a/rwengine/src/render/MapRenderer.cpp b/rwengine/src/render/MapRenderer.cpp index 539b488c..0b93611d 100644 --- a/rwengine/src/render/MapRenderer.cpp +++ b/rwengine/src/render/MapRenderer.cpp @@ -4,75 +4,119 @@ #include #include +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({ - {-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 ); } diff --git a/rwengine/src/render/OpenGLRenderer.cpp b/rwengine/src/render/OpenGLRenderer.cpp index 90a29ed8..25fb10fb 100644 --- a/rwengine/src/render/OpenGLRenderer.cpp +++ b/rwengine/src/render/OpenGLRenderer.cpp @@ -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) diff --git a/rwgame/ingamestate.cpp b/rwgame/ingamestate.cpp index d84db280..b5745545 100644 --- a/rwgame/ingamestate.cpp +++ b/rwgame/ingamestate.cpp @@ -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 ) { diff --git a/rwgame/ingamestate.hpp b/rwgame/ingamestate.hpp index 2a8b7a3c..5e17786b 100644 --- a/rwgame/ingamestate.hpp +++ b/rwgame/ingamestate.hpp @@ -31,7 +31,7 @@ public: const ViewCamera& getCamera(); private: - void drawHUD(const sf::RenderWindow& w); + void drawHUD(GameRenderer* r); }; #endif // INGAMESTATE_HPP diff --git a/rwgame/pausestate.cpp b/rwgame/pausestate.cpp index 35329ab0..6999ffa2 100644 --- a/rwgame/pausestate.cpp +++ b/rwgame/pausestate.cpp @@ -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);