TESTTRAZE: reworked the font rendering to support colors
parent
848c396eb3
commit
24d65e4a18
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -31,6 +31,7 @@ struct Player {
|
|||
std::string name;
|
||||
uint32_t id;
|
||||
uint32_t frags;
|
||||
uint8_t colorIndex;
|
||||
glm::vec4 color;
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue