1
0
mirror of https://github.com/rwengine/openrw.git synced 2024-09-15 06:52:34 +02:00

Text rendering

This commit is contained in:
Daniel Evans 2015-02-07 22:55:06 +00:00
parent 538d0c02f9
commit 2344024f08
21 changed files with 405 additions and 129 deletions

View File

@ -28,6 +28,7 @@ class SCMFile;
* @brief Stores simple data about Textures such as transparency flags. * @brief Stores simple data about Textures such as transparency flags.
* *
* @todo Covert usage to TextureHandles or something for streaming. * @todo Covert usage to TextureHandles or something for streaming.
* @todo Move out of GameData.hpp and into TextureInfo.hpp
*/ */
struct TextureInfo struct TextureInfo
{ {

View File

@ -10,6 +10,7 @@
#include <render/OpenGLRenderer.hpp> #include <render/OpenGLRenderer.hpp>
#include "MapRenderer.hpp" #include "MapRenderer.hpp"
#include "TextRenderer.hpp"
class Model; class Model;
class ModelFrame; class ModelFrame;
@ -244,6 +245,7 @@ public:
} }
MapRenderer map; MapRenderer map;
TextRenderer text;
}; };
#endif #endif

View File

@ -77,6 +77,7 @@ public:
/// @todo dont use GLint in the interface. /// @todo dont use GLint in the interface.
virtual void setProgramBlockBinding(ShaderProgram* p, const std::string& name, GLint point) = 0; virtual void setProgramBlockBinding(ShaderProgram* p, const std::string& name, GLint point) = 0;
virtual void setUniformTexture(ShaderProgram*p, const std::string& name, GLint tex) = 0; virtual void setUniformTexture(ShaderProgram*p, const std::string& name, GLint tex) = 0;
virtual void setUniform(ShaderProgram*p, const std::string& name, const glm::mat4& m) = 0;
virtual void setUniform(ShaderProgram*p, const std::string& name, const glm::vec4& v) = 0; virtual void setUniform(ShaderProgram*p, const std::string& name, const glm::vec4& v) = 0;
virtual void setUniform(ShaderProgram*p, const std::string& name, const glm::vec3& v) = 0; virtual void setUniform(ShaderProgram*p, const std::string& name, const glm::vec3& v) = 0;
virtual void setUniform(ShaderProgram*p, const std::string& name, const glm::vec2& v) = 0; virtual void setUniform(ShaderProgram*p, const std::string& name, const glm::vec2& v) = 0;
@ -89,7 +90,15 @@ public:
virtual void draw(const glm::mat4& model, DrawBuffer* draw, const DrawParameters& p) = 0; 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; virtual void drawArrays(const glm::mat4& model, DrawBuffer* draw, const DrawParameters& p) = 0;
void setViewport(const glm::ivec2& vp) { viewport = vp; }
const glm::ivec2& getViewport() const { return viewport; }
glm::mat4 get2DProjection() const;
virtual void invalidate() = 0; virtual void invalidate() = 0;
private:
glm::ivec2 viewport;
}; };
class OpenGLRenderer : public Renderer class OpenGLRenderer : public Renderer
@ -127,6 +136,7 @@ public:
ShaderProgram* createShader(const std::string &vert, const std::string &frag); ShaderProgram* createShader(const std::string &vert, const std::string &frag);
void setProgramBlockBinding(ShaderProgram* p, const std::string &name, GLint point); void setProgramBlockBinding(ShaderProgram* p, const std::string &name, GLint point);
void setUniformTexture(ShaderProgram* p, const std::string &name, GLint tex); void setUniformTexture(ShaderProgram* p, const std::string &name, GLint tex);
void setUniform(ShaderProgram* p, const std::string& name, const glm::mat4& m);
void setUniform(ShaderProgram* p, const std::string& name, const glm::vec4& m); void setUniform(ShaderProgram* p, const std::string& name, const glm::vec4& m);
void setUniform(ShaderProgram* p, const std::string& name, const glm::vec3& m); void setUniform(ShaderProgram* p, const std::string& name, const glm::vec3& m);
void setUniform(ShaderProgram* p, const std::string& name, const glm::vec2& m); void setUniform(ShaderProgram* p, const std::string& name, const glm::vec2& m);

View File

@ -0,0 +1,53 @@
#pragma once
#include <engine/GameData.hpp>
#include "OpenGLRenderer.hpp"
#define GAME_FONTS 3
class GameWorld;
class GameRenderer;
/**
* @brief Handles rendering of bitmap font textures.
*
* In future, strings textures might be cached to improve performance, but
* for now, we just render each glyph on it's own quad
*/
class TextRenderer
{
public:
/**
* @todo Can this be merged with the gamestate text entries?
*/
struct TextInfo
{
/// Font index @see TextRenderer::setFontTexture
int font;
/// Message to be displayed (including markup)
std::string text;
/// On screen position
glm::vec2 screenPosition;
/// font size
float size;
/// Base colour
glm::vec3 baseColour;
TextInfo();
};
TextRenderer(GameWorld* engine, GameRenderer* renderer);
~TextRenderer();
void setFontTexture( int index, const std::string& font );
void renderText( const TextInfo& ti );
private:
std::string fonts[GAME_FONTS];
GameWorld* engine;
GameRenderer* renderer;
Renderer::ShaderProgram* textShader;
GeometryBuffer gb;
DrawBuffer db;
};

View File

@ -107,6 +107,7 @@ void GameData::load()
_knownFiles.insert({"hud.txd", {false, datpath+"/models/hud.txd"}}); _knownFiles.insert({"hud.txd", {false, datpath+"/models/hud.txd"}});
_knownFiles.insert({"english.gxt", {false, datpath+"/TEXT/english.gxt"}}); _knownFiles.insert({"english.gxt", {false, datpath+"/TEXT/english.gxt"}});
_knownFiles.insert({"ped.ifp", {false, datpath+"/anim/ped.ifp"}}); _knownFiles.insert({"ped.ifp", {false, datpath+"/anim/ped.ifp"}});
_knownFiles.insert({"fonts.txd", {false, datpath+"/models/fonts.txd"}});
_knownFiles.insert({"news.txd", {false, datpath+"/txd/NEWS.TXD"}}); _knownFiles.insert({"news.txd", {false, datpath+"/txd/NEWS.TXD"}});
_knownFiles.insert({"splash1.txd", {false, datpath+"/txd/SPLASH1.TXD"}}); _knownFiles.insert({"splash1.txd", {false, datpath+"/txd/SPLASH1.TXD"}});
@ -118,6 +119,7 @@ void GameData::load()
loadDFF("arrow.dff"); loadDFF("arrow.dff");
loadTXD("particle.txd"); loadTXD("particle.txd");
loadTXD("hud.txd"); loadTXD("hud.txd");
loadTXD("fonts.txd");
loadCarcols(datpath+"/data/carcols.dat"); loadCarcols(datpath+"/data/carcols.dat");
loadWeather(datpath+"/data/timecyc.dat"); loadWeather(datpath+"/data/timecyc.dat");

View File

@ -80,7 +80,7 @@ DrawBuffer ssRectDraw;
GameRenderer::GameRenderer(GameWorld* engine) GameRenderer::GameRenderer(GameWorld* engine)
: engine(engine), renderer(new OpenGLRenderer), _renderAlpha(0.f), : engine(engine), renderer(new OpenGLRenderer), _renderAlpha(0.f),
map(engine, renderer) map(engine, renderer), text(engine, this)
{ {
engine->logInfo("[DRAW] " + renderer->getIDString()); engine->logInfo("[DRAW] " + renderer->getIDString());

View File

@ -1,6 +1,7 @@
#include <render/OpenGLRenderer.hpp> #include <render/OpenGLRenderer.hpp>
#include <GL/glew.h> #include <GL/glew.h>
#include <glm/gtc/type_ptr.hpp> #include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <sstream> #include <sstream>
#include <iostream> #include <iostream>
@ -79,6 +80,22 @@ GLuint compileProgram(const char* vertex, const char* fragment)
return prog; return prog;
} }
glm::mat4 Renderer::get2DProjection() const
{
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);
}
void OpenGLRenderer::useDrawBuffer(DrawBuffer* dbuff) void OpenGLRenderer::useDrawBuffer(DrawBuffer* dbuff)
{ {
if( dbuff != currentDbuff ) if( dbuff != currentDbuff )
@ -147,6 +164,13 @@ void OpenGLRenderer::setUniformTexture(Renderer::ShaderProgram* p, const std::st
glUniform1i(currentProgram->getUniformLocation(name), tex); glUniform1i(currentProgram->getUniformLocation(name), tex);
} }
void OpenGLRenderer::setUniform(Renderer::ShaderProgram* p, const std::string& name, const glm::mat4& m)
{
useProgram(p);
glUniformMatrix4fv(currentProgram->getUniformLocation(name.c_str()), 1, GL_FALSE, glm::value_ptr(m));
}
void OpenGLRenderer::setUniform(Renderer::ShaderProgram* p, const std::string& name, const glm::vec4& m) void OpenGLRenderer::setUniform(Renderer::ShaderProgram* p, const std::string& name, const glm::vec4& m)
{ {
useProgram(p); useProgram(p);

View File

@ -0,0 +1,178 @@
#include "render/TextRenderer.hpp"
#include <render/GameRenderer.hpp>
#include <engine/GameWorld.hpp>
const char* TextVertexShader = R"(
#version 130
#extension GL_ARB_explicit_attrib_location : enable
#extension GL_ARB_uniform_buffer_object : enable
layout(location = 0) in vec2 position;
layout(location = 3) in vec2 texcoord;
layout(location = 2) in vec3 colour;
out vec2 TexCoord;
out vec3 Colour;
uniform mat4 proj;
void main()
{
gl_Position = proj * vec4(position, 0.0, 1.0);
TexCoord = texcoord;
Colour = colour;
})";
const char* TextFragmentShader = R"(
#version 130
in vec2 TexCoord;
in vec3 Colour;
uniform vec4 colour;
uniform sampler2D fontTexture;
out vec4 outColour;
void main()
{
float a = texture(fontTexture, TexCoord).a;
outColour = vec4(Colour, a);
})";
struct TextVertex
{
glm::vec2 position;
glm::vec2 texcoord;
glm::vec3 colour;
static const AttributeList vertex_attributes() {
return {
{ATRS_Position, 2, sizeof(TextVertex), 0ul},
{ATRS_TexCoord, 2, sizeof(TextVertex), 0ul + sizeof(glm::vec2)},
{ATRS_Colour, 3, sizeof(TextVertex), 0ul + sizeof(glm::vec2) * 2},
};
}
};
TextRenderer::TextInfo::TextInfo()
: font(0), size(1.f), baseColour({1.f, 1.f, 1.f})
{
}
TextRenderer::TextRenderer(GameWorld* engine, GameRenderer* renderer)
: fonts({}), engine(engine), renderer(renderer)
{
textShader = renderer->getRenderer()->createShader(
TextVertexShader, TextFragmentShader );
}
TextRenderer::~TextRenderer()
{
}
void TextRenderer::setFontTexture(int index, const std::string& texture)
{
if( index < GAME_FONTS )
{
fonts[index] = texture;
}
}
/// @todo This is very rough
int charToIndex(char g)
{
if( g >= '0' && g <= '9' )
{
return 16 + (g - '0');
}
else if( g >= 'A' && g <= 'Z' )
{
return 33 + (g - 'A');
}
else if( g >= 'a' && g <= 'z' )
{
return 65 + (g - 'a');
}
switch(g)
{
default: return 0;
case '!': return 1;
case '"': return 2;
case '#': return 3;
case '$': return 4;
case '%': return 5;
case '&': return 6;
case '\'': return 7;
case '(': return 8;
case ')': return 9;
case '*': return 10;
case '+': return 11;
case ',': return 12;
case '-': return 13;
case '.': return 14;
case '/': return 15;
}
}
glm::vec4 indexToCoord(int font, int index)
{
int x = index % 16;
int y = index / 16;
glm::vec2 gsize( 1.f / 16.f, 1.f / ((font == 0) ? 16.f : 13.f) );
return glm::vec4( x, y, x + 1, y + 1 ) *
glm::vec4( gsize, gsize ); // + glm::vec4( 0.0001f, 0.0001f,-0.0001f,-0.0001f);
}
void TextRenderer::renderText(const TextRenderer::TextInfo& ti)
{
renderer->getRenderer()->useProgram(textShader);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glActiveTexture(GL_TEXTURE0);
renderer->getRenderer()->setUniform(textShader, "proj", renderer->getRenderer()->get2DProjection());
renderer->getRenderer()->setUniformTexture(textShader, "fontTexture", 0);
glm::vec2 coord = ti.screenPosition;
Renderer::DrawParameters dp;
dp.start = 0;
dp.count = gb.getCount();
dp.texture = engine->gameData.textures[{fonts[ti.font], ""}].texName;
glm::vec2 ss( ti.size );
glm::vec3 colour = ti.baseColour;
/// @todo make this less wastefull
for( const char& c : ti.text )
{
// Handle special chars.
if( c == '\n' )
{
coord.x = ti.screenPosition.x;
coord.y += ss.y;
continue;
}
int glyph = charToIndex(c);
auto tex = indexToCoord(ti.font, glyph);
glm::vec2 p = coord;
coord.x += ss.x;
std::vector<TextVertex> geo = {
{ { p.x, p.y + ss.y }, {tex.x, tex.w}, colour },
{ { p.x + ss.x, p.y + ss.y }, {tex.z, tex.w}, colour },
{ { p.x, p.y }, {tex.x, tex.y}, colour },
{ { p.x + ss.x, p.y }, {tex.z, tex.y}, colour },
};
gb.uploadVertices(geo);
db.addGeometry(&gb);
db.setFaceType(GL_TRIANGLE_STRIP);
renderer->getRenderer()->drawArrays(glm::mat4(), &db, dp);
}
}

View File

@ -2,17 +2,16 @@
#define _GAME_MENUSYSTEM_HPP_ #define _GAME_MENUSYSTEM_HPP_
#include <string> #include <string>
#include <memory> #include <memory>
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include <render/GameRenderer.hpp>
#include <functional> #include <functional>
class Menu class Menu
{ {
sf::Font font; int font;
public: public:
Menu(const sf::Font& font) Menu(int font)
: font(font), activeEntry(-1) {} : font(font), activeEntry(-1) {}
struct MenuEntry struct MenuEntry
@ -20,19 +19,26 @@ public:
std::string name; std::string name;
float _size; float _size;
MenuEntry(const std::string& n, float size = 38.f) : name(n), _size(size) {} MenuEntry(const std::string& n, float size = 30.f) : name(n), _size(size) {}
float getHeight() { return _size; } float getHeight() { return _size; }
virtual void draw(const sf::Font& font, sf::RenderWindow& window, glm::vec2& basis) virtual void draw(int font, bool active, GameRenderer* r, glm::vec2& basis)
{ {
sf::Text t; TextRenderer::TextInfo ti;
t.setFont(font); ti.font = font;
t.setPosition(basis.x + 6, basis.y + 2); ti.screenPosition = basis;
t.setString(name); ti.text = name;
auto cSize = getHeight() - 10.f; ti.size = getHeight();
t.setCharacterSize(cSize); if( ! active )
window.draw(t); {
ti.baseColour = glm::vec3(1.f, 1.f, 1.f);
}
else
{
ti.baseColour = glm::vec3(1.f, 1.f, 0.f);
}
r->text.renderText(ti);
basis.y += getHeight(); basis.y += getHeight();
} }
@ -49,7 +55,7 @@ public:
void activate(float clickX, float clickY) { callback(); } void activate(float clickX, float clickY) { callback(); }
}; };
static std::shared_ptr<MenuEntry> lambda(const std::string& n, std::function<void (void)> callback, float size = 38.f) static std::shared_ptr<MenuEntry> lambda(const std::string& n, std::function<void (void)> callback, float size = 30.f)
{ {
return std::shared_ptr<MenuEntry>(new Entry(n, callback, size)); return std::shared_ptr<MenuEntry>(new Entry(n, callback, size));
} }
@ -68,20 +74,19 @@ public:
entries.push_back(entry); entries.push_back(entry);
} }
void draw(sf::RenderWindow& window) void draw(GameRenderer* r)
{ {
glm::vec2 basis(offset); glm::vec2 basis(offset);
for(size_t i = 0; for(size_t i = 0;
i < entries.size(); i < entries.size();
++i) ++i)
{ {
if(activeEntry >= 0 && i == (unsigned) activeEntry) { bool active = false;
sf::RectangleShape rs(sf::Vector2f(250.f, entries[i]->getHeight())); if(activeEntry >= 0 && i == (unsigned) activeEntry)
rs.setPosition(basis.x, basis.y); {
rs.setFillColor(sf::Color::Cyan); active = true;
window.draw(rs);
} }
entries[i]->draw(font, window, basis); entries[i]->draw(font, active, r, basis);
} }
} }

View File

@ -4,6 +4,7 @@
#include <engine/GameObject.hpp> #include <engine/GameObject.hpp>
#include <engine/GameState.hpp> #include <engine/GameState.hpp>
#include <engine/GameWorld.hpp>
#include <render/GameRenderer.hpp> #include <render/GameRenderer.hpp>
#include <render/DebugDraw.hpp> #include <render/DebugDraw.hpp>
#include <script/ScriptMachine.hpp> #include <script/ScriptMachine.hpp>
@ -19,10 +20,6 @@ RWGame::RWGame(const std::string& gamepath, int argc, char* argv[])
: engine(nullptr), inFocus(true), : engine(nullptr), inFocus(true),
accum(0.f), timescale(1.f) accum(0.f), timescale(1.f)
{ {
if(! font.loadFromFile(gamepath + "/DejaVuSansMono.ttf")) {
std::cerr << "Failed to load font" << std::endl;
}
size_t w = GAME_WINDOW_WIDTH, h = GAME_WINDOW_HEIGHT; size_t w = GAME_WINDOW_WIDTH, h = GAME_WINDOW_HEIGHT;
bool fullscreen = false; bool fullscreen = false;
@ -64,6 +61,11 @@ RWGame::RWGame(const std::string& gamepath, int argc, char* argv[])
engine->gameData.loadIMG("/models/gta3"); engine->gameData.loadIMG("/models/gta3");
engine->gameData.loadIMG("/models/txd"); engine->gameData.loadIMG("/models/txd");
engine->gameData.loadIMG("/anim/cuts"); engine->gameData.loadIMG("/anim/cuts");
// Set up text renderer
engine->renderer.text.setFontTexture(0, "pager");
engine->renderer.text.setFontTexture(1, "font1");
engine->renderer.text.setFontTexture(2, "font2");
/// @TODO expand this here. /// @TODO expand this here.
engine->load(); engine->load();
@ -139,7 +141,7 @@ int RWGame::run()
render(alpha); render(alpha);
StateManager::get().draw(window); StateManager::get().draw(&engine->renderer);
window.display(); window.display();
@ -202,6 +204,9 @@ void RWGame::tick(float dt)
void RWGame::render(float alpha) void RWGame::render(float alpha)
{ {
auto size = getWindow().getSize();
engine->renderer.getRenderer()->setViewport({size.x, size.y});
ViewCamera viewCam; ViewCamera viewCam;
if( engine->state.currentCutscene != nullptr && engine->state.cutsceneStartTime >= 0.f ) if( engine->state.currentCutscene != nullptr && engine->state.cutsceneStartTime >= 0.f )
{ {
@ -285,8 +290,6 @@ void RWGame::render(float alpha)
debug->flush(&engine->renderer); debug->flush(&engine->renderer);
#endif #endif
window.resetGLStates();
std::stringstream ss; std::stringstream ss;
ss << std::setfill('0') << "Time: " << std::setw(2) << engine->getHour() ss << std::setfill('0') << "Time: " << std::setw(2) << engine->getHour()
<< ":" << std::setw(2) << engine->getMinute() << " (" << engine->gameTime << "s)\n"; << ":" << std::setw(2) << engine->getMinute() << " (" << engine->gameTime << "s)\n";
@ -302,39 +305,40 @@ void RWGame::render(float alpha)
} }
ss << std::endl; ss << std::endl;
} }
sf::Text text(ss.str(), font, 14); TextRenderer::TextInfo ti;
text.setPosition(10, 10); ti.text = ss.str();
window.draw(text); ti.font = 2;
ti.screenPosition = glm::vec2( 10.f, 10.f );
ti.size = 20.f;
engine->renderer.text.renderText(ti);
while( engine->log.size() > 0 && engine->log.front().time + 10.f < engine->gameTime ) { while( engine->log.size() > 0 && engine->log.front().time + 10.f < engine->gameTime ) {
engine->log.pop_front(); engine->log.pop_front();
} }
sf::Vector2f tpos(10.f, window.getSize().y - 30.f); ti.screenPosition = glm::vec2( 10.f, 500.f );
text.setCharacterSize(14); ti.size = 15.f;
for(auto it = engine->log.begin(); it != engine->log.end(); ++it) { for(auto it = engine->log.begin(); it != engine->log.end(); ++it) {
text.setString(it->message); ti.text = it->message;
switch(it->type) { switch(it->type) {
case GameWorld::LogEntry::Error: case GameWorld::LogEntry::Error:
text.setColor(sf::Color::Red); ti.baseColour = glm::vec3(1.f, 0.f, 0.f);
break; break;
case GameWorld::LogEntry::Warning: case GameWorld::LogEntry::Warning:
text.setColor(sf::Color::Yellow); ti.baseColour = glm::vec3(1.f, 1.f, 0.f);
break; break;
default: default:
text.setColor(sf::Color::White); ti.baseColour = glm::vec3(1.f, 1.f, 1.f);
break; break;
} }
// Interpolate the color // Interpolate the color
auto c = text.getColor(); // c.a = (engine->gameTime - it->time > 5.f) ? 255 - (((engine->gameTime - it->time) - 5.f)/5.f) * 255 : 255;
c.a = (engine->gameTime - it->time > 5.f) ? 255 - (((engine->gameTime - it->time) - 5.f)/5.f) * 255 : 255; // text.setColor(c);
text.setColor(c);
text.setPosition(tpos); engine->renderer.text.renderText(ti);
window.draw(text); ti.screenPosition.y -= ti.size;
tpos.y -= text.getLocalBounds().height;
} }
for( int i = 0; i < engine->state.text.size(); ) for( int i = 0; i < engine->state.text.size(); )
@ -351,77 +355,79 @@ void RWGame::render(float alpha)
for(OnscreenText& t : engine->state.text) for(OnscreenText& t : engine->state.text)
{ {
float fontSize = 15.f; float fontSize = 20.f;
switch(t.osTextStyle) switch(t.osTextStyle)
{ {
default: default:
fontSize = 15.f; fontSize = 20.f;
break; break;
case 1: case 1:
fontSize = 25.f; fontSize = 40.f;
break; break;
case 2: case 2:
fontSize = 20.f; fontSize = 20.f;
break; break;
} }
sf::Text messageText(t.osTextString, font, fontSize); ti.size = fontSize;
auto sz = window.getSize(); ti.screenPosition = glm::vec2(0.f);
ti.font = 0;
auto b = messageText.getLocalBounds();
if(t.osTextStyle == 1) if(t.osTextStyle == 1)
{ {
messageText.setPosition(sz.x / 2.f - std::round(b.width / 2.f), sz.y / 2.f - std::round(b.height / 2.f)); ti.screenPosition = glm::vec2(500.f, 500.f);
} }
else if(t.osTextStyle == 2) else if(t.osTextStyle == 2)
{ {
messageText.setPosition(sz.x * 0.9f - std::round(b.width), sz.y * 0.8f - std::round(b.height / 2.f)); ti.screenPosition = glm::vec2(500.f, 500.f);
} }
else if(t.osTextStyle == 12) else if(t.osTextStyle == 12)
{ {
messageText.setPosition(15.f, 15.f); ti.screenPosition = glm::vec2(20.f, 20.f);
ti.font = 2;
// messageText.setPosition(15.f, 15.f);
// Insert line breaks into the message string. // // Insert line breaks into the message string.
auto m = messageText.getString(); // auto m = messageText.getString();
const float boxWidth = 250.f; // const float boxWidth = 250.f;
int lastSpace = 0; // int lastSpace = 0;
float lineLength = 0.f, wordLength = 0.f; // float lineLength = 0.f, wordLength = 0.f;
for( int c = 0; c < m.getSize(); ++c ) // for( int c = 0; c < m.getSize(); ++c )
{ // {
if(m[c] == ' ') // if(m[c] == ' ')
{ // {
lastSpace = c; // lastSpace = c;
lineLength += wordLength; // lineLength += wordLength;
wordLength = 0.f; // wordLength = 0.f;
} // }
//
auto& metrics = font.getGlyph(m[c], fontSize, false); // auto& metrics = font.getGlyph(m[c], fontSize, false);
wordLength += metrics.bounds.width; // wordLength += metrics.bounds.width;
//
if( lineLength + wordLength > boxWidth ) // if( lineLength + wordLength > boxWidth )
{ // {
m[lastSpace] = '\n'; // m[lastSpace] = '\n';
lineLength = 0.f; // lineLength = 0.f;
} // }
} // }
messageText.setString(m); // messageText.setString(m);
//
auto bds = messageText.getGlobalBounds(); // auto bds = messageText.getGlobalBounds();
sf::RectangleShape bg(sf::Vector2f(bds.width, bds.height) + sf::Vector2f(10.f, 10.f)); // sf::RectangleShape bg(sf::Vector2f(bds.width, bds.height) + sf::Vector2f(10.f, 10.f));
bg.setFillColor(sf::Color::Black); // bg.setFillColor(sf::Color::Black);
bg.setPosition(sf::Vector2f(bds.left, bds.top) - sf::Vector2f(5.f, 5.f)); // bg.setPosition(sf::Vector2f(bds.left, bds.top) - sf::Vector2f(5.f, 5.f));
window.draw(bg); // window.draw(bg);
} }
else else
{ {
float lowerBar = sz.y - sz.y * 0.1f; float lowerBar = 550.f;
messageText.setPosition(sz.x / 2.f - std::round(b.width / 2.f), lowerBar - std::round(b.height / 2.f)); ti.screenPosition = glm::vec2(300.f, lowerBar);
} }
window.draw(messageText);
ti.text = t.osTextString;
engine->renderer.text.renderText(ti);
} }
for(auto& t : engine->state.texts) { /*for(auto& t : engine->state.texts) {
sf::Text messageText(t.text, font, 15); sf::Text messageText(t.text, font, 15);
glm::vec2 scpos(t.position.x, t.position.y); glm::vec2 scpos(t.position.x, t.position.y);
@ -432,7 +438,7 @@ void RWGame::render(float alpha)
messageText.setPosition(scpos.x, scpos.y); messageText.setPosition(scpos.x, scpos.y);
window.draw(messageText); window.draw(messageText);
} }*/
} }
void RWGame::globalKeyEvent(const sf::Event& event) void RWGame::globalKeyEvent(const sf::Event& event)

View File

@ -15,8 +15,6 @@ class RWGame
float accum; float accum;
float timescale; float timescale;
sf::Font font;
public: public:
RWGame(const std::string& gamepath, int argc, char* argv[]); RWGame(const std::string& gamepath, int argc, char* argv[]);
@ -34,11 +32,6 @@ public:
return window; return window;
} }
sf::Font& getFont()
{
return font;
}
bool hitWorldRay(glm::vec3 &hit, glm::vec3 &normal, GameObject** object = nullptr) bool hitWorldRay(glm::vec3 &hit, glm::vec3 &normal, GameObject** object = nullptr)
{ {
auto vc = nextCam; auto vc = nextCam;

View File

@ -2,7 +2,8 @@
#define _GAME_STATE_HPP_ #define _GAME_STATE_HPP_
#include <functional> #include <functional>
#include <queue> #include <queue>
#include <SFML/Graphics/RenderWindow.hpp> #include <SFML/Graphics.hpp>
#include <SFML/System.hpp>
#include <render/ViewCamera.hpp> #include <render/ViewCamera.hpp>
#include "MenuSystem.hpp" #include "MenuSystem.hpp"
#include <glm/gtc/quaternion.hpp> #include <glm/gtc/quaternion.hpp>
@ -26,10 +27,10 @@ struct State
virtual void tick(float dt) = 0; virtual void tick(float dt) = 0;
virtual void draw(sf::RenderWindow& w) virtual void draw(GameRenderer* r)
{ {
if(getCurrentMenu()) { if(getCurrentMenu()) {
getCurrentMenu()->draw(w); getCurrentMenu()->draw(r);
} }
} }
@ -117,9 +118,9 @@ struct StateManager
states.back()->tick(dt); states.back()->tick(dt);
} }
void draw(sf::RenderWindow& w) void draw(GameRenderer* r)
{ {
states.back()->draw(w); states.back()->draw(r);
} }
void exit() void exit()

View File

@ -24,8 +24,8 @@ void jumpCharacter(RWGame* game, CharacterController* controller, const glm::vec
DebugState::DebugState(RWGame* game, const glm::vec3& vp, const glm::quat& vd) DebugState::DebugState(RWGame* game, const glm::vec3& vp, const glm::quat& vd)
: State(game), _freeLook( false ), _sonicMode( false ) : State(game), _freeLook( false ), _sonicMode( false )
{ {
Menu *m = new Menu(game->getFont()); Menu *m = new Menu(2);
m->offset = glm::vec2(50.f, 100.f); m->offset = glm::vec2(200.f, 200.f);
float entryHeight = 24.f; float entryHeight = 24.f;
#if 0 #if 0
m->addEntry(Menu::lambda("Random Vehicle", [this] { m->addEntry(Menu::lambda("Random Vehicle", [this] {

View File

@ -187,14 +187,14 @@ void IngameState::tick(float dt)
} }
} }
void IngameState::draw(sf::RenderWindow& w) void IngameState::draw(GameRenderer* r)
{ {
if( !getWorld()->state.isCinematic && getWorld()->isCutsceneDone() ) if( !getWorld()->state.isCinematic && getWorld()->isCutsceneDone() )
{ {
drawHUD(w); drawHUD(getWindow());
} }
State::draw(w); State::draw(r);
} }

View File

@ -24,7 +24,7 @@ public:
virtual void exit(); virtual void exit();
virtual void tick(float dt); virtual void tick(float dt);
virtual void draw(sf::RenderWindow& w); virtual void draw(GameRenderer* r);
virtual void handleEvent(const sf::Event& event); virtual void handleEvent(const sf::Event& event);

View File

@ -47,10 +47,13 @@ void LoadingState::handleEvent(const sf::Event &e)
State::handleEvent(e); State::handleEvent(e);
} }
void LoadingState::draw(sf::RenderWindow &w) void LoadingState::draw(GameRenderer* r)
{ {
// Display some manner of loading screen. // Display some manner of loading screen.
sf::Text loadingText("Loading...", game->getFont(), 28); TextRenderer::TextInfo ti;
loadingText.setPosition({30.f, 20.f}); ti.text = "Loading...";
w.draw(loadingText); ti.screenPosition = glm::vec2( -1.f, 0.5f );
ti.size = 0.1f;
ti.font = 2;
r->text.renderText(ti);
} }

View File

@ -13,7 +13,7 @@ public:
virtual void tick(float dt); virtual void tick(float dt);
virtual void draw(sf::RenderWindow &w); virtual void draw(GameRenderer* r);
virtual void handleEvent(const sf::Event& event); virtual void handleEvent(const sf::Event& event);
}; };

View File

@ -7,8 +7,8 @@
MenuState::MenuState(RWGame* game) MenuState::MenuState(RWGame* game)
: State(game) : State(game)
{ {
Menu *m = new Menu(game->getFont()); Menu *m = new Menu(2);
m->offset = glm::vec2(50.f, 100.f); m->offset = glm::vec2(200.f, 200.f);
m->addEntry(Menu::lambda("Start", [=] { StateManager::get().enter(new IngameState(game)); })); m->addEntry(Menu::lambda("Start", [=] { StateManager::get().enter(new IngameState(game)); }));
m->addEntry(Menu::lambda("Test", [=] { StateManager::get().enter(new IngameState(game, true)); })); m->addEntry(Menu::lambda("Test", [=] { StateManager::get().enter(new IngameState(game, true)); }));
m->addEntry(Menu::lambda("Options", [] { std::cout << "Options" << std::endl; })); m->addEntry(Menu::lambda("Options", [] { std::cout << "Options" << std::endl; }));

View File

@ -6,8 +6,8 @@
PauseState::PauseState(RWGame* game) PauseState::PauseState(RWGame* game)
: State(game) : State(game)
{ {
Menu *m = new Menu(game->getFont()); Menu *m = new Menu(2);
m->offset = glm::vec2(50.f, 100.f); m->offset = glm::vec2( 200.f, 200.f );
m->addEntry(Menu::lambda("Continue", [] { StateManager::get().exit(); })); m->addEntry(Menu::lambda("Continue", [] { StateManager::get().exit(); }));
m->addEntry(Menu::lambda("Options", [] { std::cout << "Options" << std::endl; })); m->addEntry(Menu::lambda("Options", [] { std::cout << "Options" << std::endl; }));
m->addEntry(Menu::lambda("Exit", [&] { getWindow().close(); })); m->addEntry(Menu::lambda("Exit", [&] { getWindow().close(); }));
@ -29,10 +29,12 @@ void PauseState::tick(float dt)
} }
void PauseState::draw(sf::RenderWindow& w) void PauseState::draw(GameRenderer* r)
{ {
MapRenderer::MapInfo map; MapRenderer::MapInfo map;
auto& w = getWindow();
map.scale = 0.25f; map.scale = 0.25f;
map.viewport = glm::vec2(w.getSize().x, w.getSize().y); map.viewport = glm::vec2(w.getSize().x, w.getSize().y);
map.mapSize = map.viewport; map.mapSize = map.viewport;
@ -44,7 +46,7 @@ void PauseState::draw(sf::RenderWindow& w)
} }
getWorld()->renderer.map.draw(map); getWorld()->renderer.map.draw(map);
State::draw(w); State::draw(r);
} }
void PauseState::handleEvent(const sf::Event &e) void PauseState::handleEvent(const sf::Event &e)

View File

@ -13,7 +13,7 @@ public:
virtual void tick(float dt); virtual void tick(float dt);
virtual void draw(sf::RenderWindow& w); virtual void draw(GameRenderer* r);
virtual void handleEvent(const sf::Event& event); virtual void handleEvent(const sf::Event& event);
}; };

View File

@ -7,8 +7,7 @@ BOOST_AUTO_TEST_SUITE(MenuUnitTests)
BOOST_AUTO_TEST_CASE(menu_test_click) BOOST_AUTO_TEST_CASE(menu_test_click)
{ {
bool clickered = false; bool clickered = false;
sf::Font f; Menu test(0);
Menu test(f);
test.addEntry(Menu::lambda("Test", [&]{ clickered = true; })); test.addEntry(Menu::lambda("Test", [&]{ clickered = true; }));
BOOST_CHECK(! clickered ); BOOST_CHECK(! clickered );
@ -32,8 +31,7 @@ BOOST_AUTO_TEST_CASE(menu_test_click)
BOOST_AUTO_TEST_CASE(menu_test_click_offset) BOOST_AUTO_TEST_CASE(menu_test_click_offset)
{ {
bool clickered = false; bool clickered = false;
sf::Font f; Menu test(0);
Menu test(f);
test.offset = glm::vec2(200.f, 200.f); test.offset = glm::vec2(200.f, 200.f);
test.addEntry(Menu::lambda("Test", [&]{ clickered = true; })); test.addEntry(Menu::lambda("Test", [&]{ clickered = true; }));
@ -58,8 +56,7 @@ BOOST_AUTO_TEST_CASE(menu_test_click_offset)
BOOST_AUTO_TEST_CASE(menu_test_active_index) BOOST_AUTO_TEST_CASE(menu_test_active_index)
{ {
int clickindex = -1; int clickindex = -1;
sf::Font f; Menu test(0);
Menu test(f);
test.addEntry(Menu::lambda("Test1", [&]{ clickindex = 0; })); test.addEntry(Menu::lambda("Test1", [&]{ clickindex = 0; }));
test.addEntry(Menu::lambda("Test2", [&]{ clickindex = 1; })); test.addEntry(Menu::lambda("Test2", [&]{ clickindex = 1; }));
@ -86,8 +83,7 @@ BOOST_AUTO_TEST_CASE(menu_test_active_index)
BOOST_AUTO_TEST_CASE(menu_test_hover_index) BOOST_AUTO_TEST_CASE(menu_test_hover_index)
{ {
int clickindex = -1; int clickindex = -1;
sf::Font f; Menu test(0);
Menu test(f);
test.addEntry(Menu::lambda("Test1", [&]{ clickindex = 0; })); test.addEntry(Menu::lambda("Test1", [&]{ clickindex = 0; }));
test.addEntry(Menu::lambda("Test2", [&]{ clickindex = 1; })); test.addEntry(Menu::lambda("Test2", [&]{ clickindex = 1; }));