TESTTRAZE: reworked the font rendering to support colors

master
Martin Gerhardy 2018-11-14 14:38:09 +01:00
parent 848c396eb3
commit 24d65e4a18
6 changed files with 126 additions and 47 deletions

View File

@ -42,6 +42,22 @@ private:
void getGlyphMetrics(int c, int& advance, int& xOffset, int& yOffset, int& ascent);
public:
~VoxelFont();
/**
* @param[in] mergeQuads @c false if you want to set e.g. a different color to each voxel. @c true
* if you want to keep the index and vertex count as small as possible.
*/
bool init(const char* font, int fontSize, int thickness, bool mergeQuads, const char* glyphs);
void shutdown();
int stringWidth(const char *str, int length = std::numeric_limits<int>::max()) const;
inline int lineHeight() const {
return _size;
}
template<class T, class FUNC>
int render(const char* string, std::vector<T>& out, std::vector<uint32_t>& indices, FUNC&& func) {
const char **s = &string;
@ -106,22 +122,6 @@ private:
return charCount;
}
public:
~VoxelFont();
/**
* @param[in] mergeQuads @c false if you want to set e.g. a different color to each voxel. @c true
* if you want to keep the index and vertex count as small as possible.
*/
bool init(const char* font, int fontSize, int thickness, bool mergeQuads, const char* glyphs);
void shutdown();
int stringWidth(const char *str, int length = std::numeric_limits<int>::max()) const;
inline int lineHeight() const {
return _size;
}
int render(const char* string, std::vector<glm::vec4>& pos, std::vector<uint32_t>& indices);
int render(const char* string, std::vector<voxel::VoxelVertex>& vertices, std::vector<uint32_t>& indices);
};

View File

@ -9,11 +9,23 @@
namespace {
const int PlayFieldVolume = 0;
const int PlayerNamesVolume = 1;
struct VertexData {
struct AttributeData {
glm::vec4 vertex;
glm::vec3 color {core::Color::Red};
};
std::vector<AttributeData> data;
inline void reserve(size_t amount) {
data.resize(amount);
}
};
}
TestTraze::TestTraze(const metric::MetricPtr& metric, const io::FilesystemPtr& filesystem, const core::EventBusPtr& eventBus, const core::TimeProviderPtr& timeProvider) :
Super(metric, filesystem, eventBus, timeProvider), _protocol(eventBus) {
Super(metric, filesystem, eventBus, timeProvider), _protocol(eventBus), _colorShader(shader::ColorShader::getInstance()) {
init(ORGANISATION, "testtraze");
setRenderAxis(false);
_eventBus->subscribe<traze::NewGridEvent>(*this);
@ -52,6 +64,10 @@ core::AppState TestTraze::onInit() {
Log::error("Failed to initialize the palette data");
return core::AppState::InitFailure;
}
if (!_colorShader.setup()) {
Log::error("Failed to init color shader");
return core::AppState::InitFailure;
}
if (!_protocol.init()) {
Log::error("Failed to init protocol");
return core::AppState::InitFailure;
@ -73,6 +89,28 @@ core::AppState TestTraze::onInit() {
_camera.setPosition(glm::vec3(0.0f, 50.0f, 84.0f));
_vertexBufferId = _vertexBuffer.create();
_vertexBuffer.setMode(_vertexBufferId, video::BufferMode::Dynamic);
_vertexBufferIndexId = _vertexBuffer.create(nullptr, 0, video::BufferType::IndexBuffer);
video::Attribute attribPos;
attribPos.bufferIndex = _vertexBufferId;
attribPos.location = _colorShader.enableVertexAttributeArray("a_pos");
attribPos.stride = sizeof(VertexData::AttributeData);
attribPos.size = _colorShader.getAttributeComponents(attribPos.location);
attribPos.type = video::mapType<decltype(VertexData::AttributeData::vertex)::value_type>();
attribPos.offset = offsetof(VertexData::AttributeData, vertex);
_vertexBuffer.addAttribute(attribPos);
video::Attribute attribColor;
attribColor.bufferIndex = _vertexBufferId;
attribColor.location = _colorShader.enableVertexAttributeArray("a_color");
attribColor.stride = sizeof(VertexData::AttributeData);
attribColor.size = _colorShader.getAttributeComponents(attribColor.location);
attribColor.type = video::mapType<decltype(VertexData::AttributeData::color)::value_type>();
attribColor.offset = offsetof(VertexData::AttributeData, color);
_vertexBuffer.addAttribute(attribColor);
_logLevelVar->setVal(std::to_string(SDL_LOG_PRIORITY_INFO));
Log::init();
@ -131,40 +169,32 @@ void TestTraze::onEvent(const traze::NewGridEvent& event) {
if (!_rawVolumeRenderer.extract(PlayFieldVolume)) {
Log::error("Failed to extract the volume");
}
const voxel::Region& region = v->region();
const glm::ivec3 namesOffset(-region.getCentreX(), 0, -region.getUpperZ());
const glm::mat4& namesTranslate = glm::translate(glm::vec3(namesOffset));
_namesModel = glm::scale(namesTranslate, glm::vec3(0.2f));
}
void TestTraze::onEvent(const traze::PlayerListEvent& event) {
_players = event.get();
std::vector<voxel::VoxelVertex> vertices;
std::vector<voxel::IndexType> indices;
std::vector<uint32_t> indices;
VertexData data;
char buf[4096] = "";
core::string::append(buf, sizeof(buf), "Players\n");
int yOffset = 0;
for (const traze::Player& p : _players) {
const std::string& line = core::string::format("%s - %i\n", p.name.c_str(), p.frags);
core::string::append(buf, sizeof(buf), line.c_str());
}
_voxelFont.render(buf, vertices, indices);
if (indices.empty() || vertices.empty()) {
Log::error("Failed to render voxel font");
return;
}
const voxel::RawVolume* volume = _rawVolumeRenderer.volume(PlayFieldVolume);
if (volume == nullptr) {
Log::error("No grid volume set yet");
return;
}
const voxel::Region& region = volume->region();
const glm::ivec3 offset(-region.getCentreX(), 0, -region.getUpperZ());
const glm::mat4& translate = glm::translate(glm::vec3(offset));
const glm::mat4& model = glm::scale(translate, glm::vec3(0.2f));
_rawVolumeRenderer.setModelMatrix(PlayerNamesVolume, model);
if (!_rawVolumeRenderer.update(PlayerNamesVolume, vertices, indices)) {
Log::error("Failed to update mesh");
return;
const std::string& line = core::string::format("%s - %i", p.name.c_str(), p.frags);
_voxelFont.render(line.c_str(), data.data, indices, [&] (const voxel::VoxelVertex& vertex, std::vector<VertexData::AttributeData>& data, int x, int y) {
const VertexData::AttributeData vp{glm::vec4(vertex.position.x + x, vertex.position.y + y + yOffset, vertex.position.z, 1.0f), p.color};
data.push_back(vp);
});
yOffset -= _voxelFont.lineHeight();
}
// TODO: the vertices should only be uploaded once for the whole glyph set. only the ibo should be dynamic and re-uploaded
_vertexBuffer.update(_vertexBufferId, data.data);
_vertexBuffer.update(_vertexBufferIndexId, indices);
}
core::AppState TestTraze::onRunning() {
@ -182,10 +212,12 @@ core::AppState TestTraze::onRunning() {
core::AppState TestTraze::onCleanup() {
core::AppState state = Super::onCleanup();
_voxelFont.shutdown();
_colorShader.shutdown();
const std::vector<voxel::RawVolume*>& old = _rawVolumeRenderer.shutdown();
for (voxel::RawVolume* v : old) {
delete v;
}
_vertexBuffer.shutdown();
_protocol.shutdown();
return state;
}
@ -211,11 +243,28 @@ void TestTraze::onRenderUI() {
if (_protocol.joined() && ImGui::Button("Leave")) {
_protocol.bail();
}
ImGui::Checkbox("Render board", &_renderBoard);
ImGui::Checkbox("Render player names", &_renderPlayerNames);
Super::onRenderUI();
}
void TestTraze::doRender() {
_rawVolumeRenderer.render(_camera);
if (_renderBoard) {
_rawVolumeRenderer.render(_camera);
}
if (_renderPlayerNames && !_players.empty()) {
const int elements = _vertexBuffer.elements(_vertexBufferIndexId, 1, sizeof(uint32_t));
if (elements <= 0) {
Log::warn("No player names rendered");
}
video::ScopedShader scoped(_colorShader);
_colorShader.setViewprojection(_camera.viewProjectionMatrix());
_colorShader.setModel(_namesModel);
video::ScopedBuffer scopedBuf(_vertexBuffer);
video::drawElements<uint32_t>(video::Primitive::Triangles, elements);
}
}
TEST_APP(TestTraze)

View File

@ -8,6 +8,8 @@
#include "voxelrender/RawVolumeRenderer.h"
#include "voxelfont/VoxelFont.h"
#include "core/EventBus.h"
#include "video/Buffer.h"
#include "RenderShaders.h"
#include "TrazeEvents.h"
#include "TrazeProtocol.h"
@ -31,6 +33,15 @@ private:
voxelrender::RawVolumeRenderer _rawVolumeRenderer;
voxel::VoxelFont _voxelFont;
shader::ColorShader& _colorShader;
video::Buffer _vertexBuffer;
int32_t _vertexBufferId = -1;
int32_t _vertexBufferIndexId = -1;
glm::mat4 _namesModel { 1.0f };
bool _renderBoard = true;
bool _renderPlayerNames = true;
std::vector<traze::GameInfo> _games;
std::vector<traze::Player> _players;
int8_t _currentGameIndex = -1;

View File

@ -16,6 +16,7 @@
#include "voxel/polyvox/Voxel.h"
#include <glm/vec2.hpp>
#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
namespace traze {
@ -181,21 +182,30 @@ void Protocol::parseOwnPlayer(const std::string& json) {
Log::info("Player token %s with id %u", _playerToken.c_str(), _playerId);
}
void Protocol::parsePlayers(const std::string& json) const {
void Protocol::parsePlayers(const std::string& json) {
const core::json j = core::json::parse(json);
std::vector<Player> players;
players.reserve(j.size());
const voxel::MaterialColorArray& materialColors = voxel::getMaterialColors();
for (const auto& player : j) {
Player p;
p.name = player["name"];
const std::string& hex = player["color"];
p.color = core::Color::fromHex(hex.c_str());
const glm::vec4& color = core::Color::fromHex(hex.c_str());
const uint8_t index = core::Color::getClosestMatch(color, materialColors);
p.colorIndex = index;
p.color = materialColors[index];
p.id = player["id"].get<int>();
p.frags = player["frags"].get<int>();
players.push_back(p);
Log::debug("Player %s with id %i", p.name.c_str(), p.id);
}
_eventBus->enqueue(std::make_shared<PlayerListEvent>(players));
std::unordered_map<uint32_t, Player> playerMap;
for (const auto& p : players) {
playerMap[p.id] = p;
}
_players = playerMap;
}
void Protocol::parseTicker(const std::string& json) const {
@ -281,7 +291,12 @@ void Protocol::parseGridAndUpdateVolume(const std::string& json) {
}
const int data = voxel.get<int>();
if (data != 0) {
v->setVoxel(glm::ivec3(z, 1, x), voxel::createColorVoxel(voxel::VoxelType::Generic, data));
const auto& iter = _players.find(data);
if (iter == _players.end()) {
Log::error("Can't find grid player id %i in player list", data);
continue;
}
v->setVoxel(glm::ivec3(z, 1, x), voxel::createColorVoxel(voxel::VoxelType::Generic, iter->second.colorIndex));
}
++z;
}

View File

@ -5,9 +5,11 @@
#pragma once
#include "core/EventBus.h"
#include "voxel/polyvox/RawVolume.h"
#include <mosquitto.h>
#include <string>
#include "TrazeTypes.h"
#include <unordered_map>
namespace traze {
@ -22,6 +24,7 @@ private:
struct mosquitto *_mosquitto = nullptr;
bool _connected = false;
bool _subscribed = false;
std::unordered_map<uint32_t, Player> _players;;
enum ConnectState : uint8_t {
Success = 0,
@ -70,7 +73,7 @@ public:
* ]
* @endcode
*/
void parsePlayers(const std::string& json) const;
void parsePlayers(const std::string& json);
/**
* @code

View File

@ -31,6 +31,7 @@ struct Player {
std::string name;
uint32_t id;
uint32_t frags;
uint8_t colorIndex;
glm::vec4 color;
};