Various stuff 14.1.
This commit is contained in:
parent
59707f1339
commit
dd48dcaed6
@ -75,6 +75,9 @@ set(CLIENT_ONLY_SOURCES
|
||||
"src/gfx/buffer_object.cpp"
|
||||
"src/gfx/buffer_object.hpp"
|
||||
"src/gfx/draw_list.hpp"
|
||||
"src/gfx/font.hpp"
|
||||
"src/gfx/font.cpp"
|
||||
"src/gfx/hud.hpp"
|
||||
"src/gfx/renderer.hpp"
|
||||
"src/gfx/renderer.cpp"
|
||||
"src/gfx/shader_defs.hpp"
|
||||
@ -83,6 +86,8 @@ set(CLIENT_ONLY_SOURCES
|
||||
"src/gfx/shader.hpp"
|
||||
"src/gfx/shader.cpp"
|
||||
"src/gfx/surface.hpp"
|
||||
"src/gfx/text.hpp"
|
||||
"src/gfx/text.cpp"
|
||||
"src/gfx/texture.cpp"
|
||||
"src/gfx/texture.hpp"
|
||||
"src/gfx/uniform_buffer.hpp"
|
||||
|
||||
@ -6,4 +6,5 @@ assets::MapCache assets::CacheManager::map_cache_;
|
||||
assets::VehicleCache assets::CacheManager::vehicle_cache_;
|
||||
|
||||
CLIENT_ONLY(assets::TextureCache assets::CacheManager::texture_cache_;)
|
||||
CLIENT_ONLY(assets::SoundCache assets::CacheManager::sound_cache_;)
|
||||
CLIENT_ONLY(assets::SoundCache assets::CacheManager::sound_cache_;)
|
||||
CLIENT_ONLY(assets::FontCache assets::CacheManager::font_cache_;)
|
||||
@ -10,6 +10,7 @@
|
||||
#ifdef CLIENT
|
||||
#include "audio/sound.hpp"
|
||||
#include "gfx/texture.hpp"
|
||||
#include "gfx/font.hpp"
|
||||
#endif
|
||||
|
||||
namespace assets
|
||||
@ -56,6 +57,12 @@ class SoundCache final : public Cache<audio::Sound>
|
||||
protected:
|
||||
PtrType Load(const std::string& key) override { return audio::Sound::LoadFromFile(key); }
|
||||
};
|
||||
|
||||
class FontCache final : public Cache<gfx::Font>
|
||||
{
|
||||
protected:
|
||||
PtrType Load(const std::string& key) override { return gfx::Font::LoadFromFile(key); }
|
||||
};
|
||||
#endif // CLIENT
|
||||
|
||||
class SkeletonCache final : public Cache<Skeleton>
|
||||
@ -109,6 +116,11 @@ public:
|
||||
{
|
||||
return sound_cache_.Get(filename);
|
||||
}
|
||||
|
||||
static std::shared_ptr<const gfx::Font> GetFont(const std::string& filename)
|
||||
{
|
||||
return font_cache_.Get(filename);
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
@ -118,6 +130,7 @@ private:
|
||||
static VehicleCache vehicle_cache_;
|
||||
CLIENT_ONLY(static TextureCache texture_cache_;)
|
||||
CLIENT_ONLY(static SoundCache sound_cache_;)
|
||||
CLIENT_ONLY(static FontCache font_cache_;)
|
||||
};
|
||||
|
||||
} // namespace assets
|
||||
@ -2,6 +2,8 @@
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "utils/bufferput.hpp"
|
||||
|
||||
assets::MeshBuilder::MeshBuilder(gfx::MeshFlags mflags) : mflags_(mflags) {}
|
||||
|
||||
void assets::MeshBuilder::BeginSurface(gfx::SurfaceFlags sflags, const std::string& name, std::shared_ptr<const gfx::Texture> texture)
|
||||
@ -35,13 +37,6 @@ void assets::MeshBuilder::AddTriangle(const MeshTriangle& t)
|
||||
tris_.push_back(t);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static void BufferPut(std::vector<char>& buffer, const T& val)
|
||||
{
|
||||
const char* data = reinterpret_cast<const char*>(&val);
|
||||
buffer.insert(buffer.end(), data, data + sizeof(T));
|
||||
}
|
||||
|
||||
static int GetVertexAttrFlags(gfx::MeshFlags mflags)
|
||||
{
|
||||
int attrs = gfx::VA_POSITION | gfx::VA_NORMAL | gfx::VA_UV;
|
||||
|
||||
@ -25,6 +25,8 @@ std::shared_ptr<const assets::Model> assets::Model::LoadFromFile(const std::stri
|
||||
iss >> v.normal.x >> v.normal.y >> v.normal.z;
|
||||
iss >> v.uv.x >> v.uv.y;
|
||||
|
||||
v.uv.y = 1.0f - v.uv.y; // FLIP FOR GL
|
||||
|
||||
// TODO: LUV & bone data
|
||||
|
||||
mb.AddVertex(v);
|
||||
@ -70,9 +72,9 @@ std::shared_ptr<const assets::Model> assets::Model::LoadFromFile(const std::stri
|
||||
{
|
||||
iss >> texture_name;
|
||||
}
|
||||
else if (flag == "+doublesided")
|
||||
else if (flag == "+2sided")
|
||||
{
|
||||
CLIENT_ONLY(sflags |= gfx::SF_DOUBLE_SIDED;)
|
||||
CLIENT_ONLY(sflags |= gfx::SF_2SIDED;)
|
||||
}
|
||||
else if (flag == "+transparent")
|
||||
{
|
||||
|
||||
@ -8,12 +8,12 @@ namespace assets
|
||||
|
||||
enum VehicleWheelType
|
||||
{
|
||||
WHEEL_REAR = 1,
|
||||
WHEEL_RIGHT = 2,
|
||||
WHEEL_RIGHT = 1,
|
||||
WHEEL_REAR = 2,
|
||||
|
||||
WHEEL_FL = 0,
|
||||
WHEEL_FR = WHEEL_RIGHT,
|
||||
WHEEL_RL = WHEEL_REAR | WHEEL_RIGHT,
|
||||
WHEEL_RL = WHEEL_REAR,
|
||||
WHEEL_RR = WHEEL_REAR | WHEEL_RIGHT,
|
||||
};
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
#include "net/defs.hpp"
|
||||
#include "net/outmessage.hpp"
|
||||
|
||||
#include "assets/cache.hpp"
|
||||
#include "gameview/worldview.hpp"
|
||||
|
||||
App::App()
|
||||
@ -12,6 +12,11 @@ App::App()
|
||||
std::cout << "Initializing App..." << std::endl;
|
||||
|
||||
audiomaster_.SetMasterVolume(0.2f);
|
||||
|
||||
font_ = assets::CacheManager::GetFont("data/comic32.font");
|
||||
|
||||
InitChat();
|
||||
AddChatMessage("Test!");
|
||||
}
|
||||
|
||||
void App::Frame()
|
||||
@ -47,7 +52,10 @@ void App::Frame()
|
||||
renderer_.ClearDepth();
|
||||
|
||||
dlist_.Clear();
|
||||
|
||||
gfx::DrawListParams params;
|
||||
params.screen_width = viewport_size_.x;
|
||||
params.screen_height = viewport_size_.y;
|
||||
|
||||
const game::view::WorldView* world;
|
||||
if (session_ && (world = session_->GetWorld()))
|
||||
{
|
||||
@ -57,14 +65,17 @@ void App::Frame()
|
||||
glm::mat4 proj = glm::perspective(glm::radians(45.0f), aspect, 0.1f, 3000.0f);
|
||||
glm::mat4 view = session_->GetViewMatrix();
|
||||
|
||||
gfx::DrawListParams params;
|
||||
params.view_proj = proj * view;
|
||||
|
||||
renderer_.DrawList(dlist_, params);
|
||||
|
||||
|
||||
glm::mat4 camera_world = glm::inverse(view);
|
||||
audiomaster_.SetListenerOrientation(camera_world);
|
||||
}
|
||||
|
||||
// draw chat
|
||||
UpdateChat();
|
||||
DrawChat(dlist_);
|
||||
|
||||
renderer_.DrawList(dlist_, params);
|
||||
|
||||
if (time_ - last_send_time_ > 0.040f)
|
||||
{
|
||||
@ -79,6 +90,7 @@ void App::Frame()
|
||||
void App::Connected()
|
||||
{
|
||||
std::cout << "WS connected" << std::endl;
|
||||
AddChatMessagePrefix("WebSocket", "^7f7připojeno");
|
||||
|
||||
// init session
|
||||
session_ = std::make_unique<game::view::ClientSession>(*this);
|
||||
@ -94,12 +106,17 @@ void App::ProcessMessage(net::InMessage& msg)
|
||||
if (!session_)
|
||||
return;
|
||||
|
||||
//size_t s = msg.End() - msg.Ptr();
|
||||
// AddChatMessage("recvd: ^f00;" + std::to_string(s));
|
||||
|
||||
session_->ProcessMessage(msg);
|
||||
}
|
||||
|
||||
void App::Disconnected(const std::string& reason)
|
||||
{
|
||||
std::cout << "WS disconnected" << std::endl;
|
||||
AddChatMessagePrefix("WebSocket", "^f77spojení je píči");
|
||||
|
||||
|
||||
// close session
|
||||
session_.reset();
|
||||
@ -116,7 +133,71 @@ void App::MouseMove(const glm::vec2& delta)
|
||||
session_->ProcessMouseMove(delta_yaw, delta_pitch);
|
||||
}
|
||||
|
||||
App::~App()
|
||||
void App::AddChatMessage(const std::string& text)
|
||||
{
|
||||
auto& ch = chat_.emplace_back();
|
||||
ch.timeout = time_ + 10.0f;
|
||||
ch.text = std::make_unique<gfx::Text>(font_, 0xFF'FF'FF'FF);
|
||||
ch.text->SetText(text);
|
||||
UpdateChat();
|
||||
}
|
||||
|
||||
void App::AddChatMessagePrefix(const std::string& prefix, const std::string& text)
|
||||
{
|
||||
AddChatMessage("^aaa[^ddd" + prefix + "^aaa]^r " + text);
|
||||
}
|
||||
|
||||
App::~App() {}
|
||||
|
||||
void App::Send(std::vector<char> data)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void App::InitChat()
|
||||
{
|
||||
chatpos_.resize(30); // max messages on screen
|
||||
const float chat_scale = 1.0f;
|
||||
for (size_t i = 0; i < chatpos_.size(); ++i)
|
||||
{
|
||||
auto& hudp = chatpos_[i];
|
||||
hudp.pos.y = (i+1) * font_->GetLineHeight() * chat_scale;
|
||||
hudp.scale.x = chat_scale;
|
||||
hudp.scale.y = -chat_scale;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void App::UpdateChat()
|
||||
{
|
||||
// remove expired or over the limit messages
|
||||
while (!chat_.empty() && (chat_.size() > chatpos_.size() ||chat_[0].timeout < time_))
|
||||
{
|
||||
chat_.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
void App::DrawChat(gfx::DrawList& dlist)
|
||||
{
|
||||
for (size_t i = 0; i < chat_.size(); ++i)
|
||||
{
|
||||
const auto& va = chat_[i].text->GetVA();
|
||||
|
||||
if (va.GetNumIndices() < 1)
|
||||
continue;
|
||||
|
||||
float t_rem = chat_[i].timeout - time_;
|
||||
const float fade = 1.0f;
|
||||
if (t_rem < fade)
|
||||
{
|
||||
chat_[i].color.a = t_rem / fade;
|
||||
}
|
||||
|
||||
gfx::DrawHudCmd cmd;
|
||||
cmd.pos = &chatpos_[i];
|
||||
cmd.texture = chat_[i].text->GetFont()->GetTexture().get();
|
||||
cmd.va = &va;
|
||||
cmd.color = &chat_[i].color;
|
||||
dlist.AddHUD(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,15 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <deque>
|
||||
|
||||
#include "game/player_input.hpp"
|
||||
#include "gfx/renderer.hpp"
|
||||
#include "gfx/text.hpp"
|
||||
#include "audio/master.hpp"
|
||||
#include "net/msg_producer.hpp"
|
||||
#include "net/inmessage.hpp"
|
||||
|
||||
#include "gameview/client_session.hpp"
|
||||
|
||||
struct ChatMessage
|
||||
{
|
||||
std::unique_ptr<gfx::Text> text;
|
||||
float timeout = 0.0f;
|
||||
glm::vec4 color = glm::vec4(1.0f);
|
||||
};
|
||||
|
||||
class App : public net::MsgProducer
|
||||
{
|
||||
|
||||
@ -32,11 +41,18 @@ public:
|
||||
|
||||
audio::Master& GetAudioMaster() { return audiomaster_; }
|
||||
|
||||
void AddChatMessage(const std::string& text);
|
||||
void AddChatMessagePrefix(const std::string& prefix, const std::string& text);
|
||||
|
||||
~App();
|
||||
|
||||
private:
|
||||
void Send(std::vector<char> data);
|
||||
|
||||
void InitChat();
|
||||
void UpdateChat();
|
||||
void DrawChat(gfx::DrawList& dlist);
|
||||
|
||||
private:
|
||||
float time_ = 0.0f;
|
||||
float last_send_time_ = 0.0f;
|
||||
@ -53,4 +69,9 @@ private:
|
||||
audio::Master audiomaster_;
|
||||
|
||||
std::unique_ptr<game::view::ClientSession> session_;
|
||||
|
||||
std::shared_ptr<const gfx::Font> font_;
|
||||
|
||||
std::deque<ChatMessage> chat_;
|
||||
std::vector<gfx::HudPosition> chatpos_;
|
||||
};
|
||||
|
||||
@ -198,7 +198,7 @@ static EM_BOOL OnWSError(int type, const EmscriptenWebSocketErrorEvent *ev, void
|
||||
return EM_TRUE;
|
||||
}
|
||||
|
||||
static bool WSInit()
|
||||
static bool WSInit(const char* url)
|
||||
{
|
||||
if (!emscripten_websocket_is_supported())
|
||||
{
|
||||
@ -206,7 +206,7 @@ static bool WSInit()
|
||||
return false;
|
||||
}
|
||||
EmscriptenWebSocketCreateAttributes ws_attrs = {
|
||||
WS_URL,
|
||||
url,
|
||||
NULL,
|
||||
EM_TRUE
|
||||
};
|
||||
@ -234,7 +234,7 @@ using namespace easywsclient;
|
||||
|
||||
static std::unique_ptr<WebSocket> s_ws;
|
||||
|
||||
static bool WSInit()
|
||||
static bool WSInit(const char* url)
|
||||
{
|
||||
|
||||
#ifdef _WIN32
|
||||
@ -249,7 +249,7 @@ static bool WSInit()
|
||||
}
|
||||
#endif
|
||||
|
||||
s_ws = std::unique_ptr<WebSocket>(WebSocket::from_url(WS_URL));
|
||||
s_ws = std::unique_ptr<WebSocket>(WebSocket::from_url(url));
|
||||
|
||||
if (!s_ws)
|
||||
return false;
|
||||
@ -368,7 +368,7 @@ static void Frame()
|
||||
}
|
||||
|
||||
static void Main() {
|
||||
if (!WSInit())
|
||||
if (!WSInit(WS_URL))
|
||||
return;
|
||||
|
||||
InitSDL();
|
||||
@ -386,6 +386,7 @@ static void Main() {
|
||||
SDL_SetRelativeMouseMode(SDL_TRUE);
|
||||
|
||||
s_app = std::make_unique<App>();
|
||||
s_app->AddChatMessagePrefix("WebSocket", "připojování na " + std::string(WS_URL));
|
||||
|
||||
#ifdef EMSCRIPTEN
|
||||
emscripten_set_main_loop(Frame, 0, true);
|
||||
|
||||
@ -8,7 +8,20 @@ game::OpenWorld::OpenWorld() : World("openworld") {}
|
||||
void game::OpenWorld::PlayerJoined(Player& player)
|
||||
{
|
||||
// spawn him car
|
||||
auto& vehicle = Spawn<Vehicle>("pickup");
|
||||
// random model
|
||||
const char* vehicles[] = { "pickup", "passat" };
|
||||
auto vehicle_name = vehicles[rand() % (sizeof(vehicles) / sizeof(vehicles[0]))];
|
||||
|
||||
// ranodm color
|
||||
glm::vec3 color;
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
net::ColorQ qcol;
|
||||
qcol.value = rand() % 256;
|
||||
color[i] = qcol.Decode();
|
||||
}
|
||||
|
||||
auto& vehicle = Spawn<Vehicle>(vehicle_name, color);
|
||||
player.Control(&vehicle);
|
||||
|
||||
player_vehicles_[&player] = vehicle.GetEntNum();
|
||||
|
||||
@ -12,32 +12,24 @@ static std::shared_ptr<const assets::VehicleModel> LoadVehicleModelByName(const
|
||||
return assets::CacheManager::GetVehicleModel("data/" + model_name + ".veh");
|
||||
}
|
||||
|
||||
struct Shape
|
||||
{
|
||||
btBoxShape box;
|
||||
btCompoundShape compound;
|
||||
|
||||
Shape() : box(btVector3(1, 1, 0.1))
|
||||
{
|
||||
btTransform t(btQuaternion(0, 0, 0), btVector3(0, 0, 2));
|
||||
compound.addChildShape(t, &box);
|
||||
}
|
||||
};
|
||||
|
||||
game::Vehicle::Vehicle(World& world, std::string model_name)
|
||||
game::Vehicle::Vehicle(World& world, std::string model_name, const glm::vec3& color)
|
||||
: Entity(world, net::ET_VEHICLE), model_name_(model_name), model_(LoadVehicleModelByName(model_name)),
|
||||
motion_(root_.local)
|
||||
motion_(root_.local),
|
||||
color_(color)
|
||||
{
|
||||
root_.local.position.z = 10.0f;
|
||||
|
||||
// setup chassis rigidbody
|
||||
float mass = 1300.0f;
|
||||
static Shape shape;
|
||||
|
||||
btCollisionShape* shape = model_->GetModel()->GetColShape();
|
||||
if (!shape)
|
||||
throw std::runtime_error("Making vehicle with no shape");
|
||||
|
||||
btVector3 local_inertia(0, 0, 0);
|
||||
shape.compound.calculateLocalInertia(mass, local_inertia);
|
||||
shape->calculateLocalInertia(mass, local_inertia);
|
||||
|
||||
btRigidBody::btRigidBodyConstructionInfo rb_info(mass, &motion_, &shape.compound, local_inertia);
|
||||
btRigidBody::btRigidBodyConstructionInfo rb_info(mass, &motion_, shape, local_inertia);
|
||||
body_ = std::make_unique<btRigidBody>(rb_info);
|
||||
body_->setActivationState(DISABLE_DEACTIVATION);
|
||||
|
||||
@ -52,7 +44,7 @@ game::Vehicle::Vehicle(World& world, std::string model_name)
|
||||
btVector3 wheelDirectionCS0(0, 0, -1);
|
||||
btVector3 wheelAxleCS(1, 0, 0);
|
||||
|
||||
wheel_z_offset_ = 0.4f;
|
||||
wheel_z_offset_ = 0.5f;
|
||||
|
||||
const auto& wheels = model_->GetWheels();
|
||||
|
||||
@ -63,10 +55,10 @@ game::Vehicle::Vehicle(World& world, std::string model_name)
|
||||
|
||||
for (const auto& wheeldef : wheels)
|
||||
{
|
||||
float wheelRadius = .35f;
|
||||
float wheelRadius = wheeldef.radius;
|
||||
|
||||
float friction = 5.0f;
|
||||
float suspensionStiffness = 60.0f;
|
||||
float friction = 2.0f; // 5.0f;
|
||||
float suspensionStiffness = 50.0f;
|
||||
// float suspensionDamping = 2.3f;
|
||||
// float suspensionCompression = 4.4f;
|
||||
float suspensionRestLength = 0.6f;
|
||||
@ -116,6 +108,7 @@ void game::Vehicle::SendInitData(Player& player, net::OutMessage& msg) const
|
||||
{
|
||||
net::ModelName name(model_name_);
|
||||
msg.Write(name);
|
||||
net::WriteRGB(msg, color_); // primary color
|
||||
}
|
||||
|
||||
game::Vehicle::~Vehicle()
|
||||
|
||||
@ -27,7 +27,7 @@ class Vehicle : public Entity, public Controllable
|
||||
public:
|
||||
using Super = Entity;
|
||||
|
||||
Vehicle(World& world, std::string model_name);
|
||||
Vehicle(World& world, std::string model_name, const glm::vec3& color);
|
||||
|
||||
virtual void Update() override;
|
||||
virtual void SendInitData(Player& player, net::OutMessage& msg) const override;
|
||||
@ -45,6 +45,7 @@ private:
|
||||
private:
|
||||
std::string model_name_;
|
||||
std::shared_ptr<const assets::VehicleModel> model_;
|
||||
glm::vec3 color_;
|
||||
|
||||
collision::MotionState motion_;
|
||||
std::unique_ptr<btRigidBody> body_;
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
|
||||
#include <iostream>
|
||||
|
||||
game::view::VehicleView::VehicleView(WorldView& world, std::shared_ptr<const assets::VehicleModel> model)
|
||||
game::view::VehicleView::VehicleView(WorldView& world, std::shared_ptr<const assets::VehicleModel> model, const glm::vec3& color)
|
||||
: EntityView(world), model_(std::move(model))
|
||||
{
|
||||
auto& modelwheels = model_->GetWheels();
|
||||
@ -17,18 +17,21 @@ game::view::VehicleView::VehicleView(WorldView& world, std::shared_ptr<const ass
|
||||
wheels_[i].node.parent = &root_;
|
||||
}
|
||||
|
||||
color_ = glm::vec4(color, 1.0f);
|
||||
|
||||
snd_accel_ = assets::CacheManager::GetSound("data/auto.snd");
|
||||
}
|
||||
|
||||
std::unique_ptr<game::view::VehicleView> game::view::VehicleView::InitFromMsg(WorldView& world, net::InMessage& msg)
|
||||
{
|
||||
net::ModelName modelname;
|
||||
if (!msg.Read(modelname))
|
||||
glm::vec3 color;
|
||||
if (!msg.Read(modelname) || !net::ReadRGB(msg, color))
|
||||
return nullptr;
|
||||
|
||||
auto model = assets::CacheManager::GetVehicleModel("data/" + std::string(modelname) + ".veh");
|
||||
|
||||
return std::make_unique<VehicleView>(world, std::move(model));
|
||||
return std::make_unique<VehicleView>(world, std::move(model), color);
|
||||
}
|
||||
|
||||
bool game::view::VehicleView::ProcessMsg(net::EntMsgType type, net::InMessage& msg)
|
||||
@ -66,10 +69,14 @@ void game::view::VehicleView::Update(const UpdateInfo& info)
|
||||
// rotate
|
||||
wheelstate.rotation += info.delta_time * wheelstate.speed;
|
||||
|
||||
wheeltrans.rotation = glm::quat(1.0f, 0.0f, 0.0f, 0.0f);
|
||||
wheeltrans.rotation = glm::quat(1.0f, 0.0f, 0.0f, 0.0f);
|
||||
wheeltrans.rotation = glm::rotate(wheeltrans.rotation, wheelstate.steering, glm::vec3(0, 0, 1));
|
||||
wheeltrans.rotation = glm::rotate(wheeltrans.rotation, wheelstate.rotation, glm::vec3(1, 0, 0));
|
||||
|
||||
|
||||
const auto& type = wheels[i].type;
|
||||
if (type == assets::WHEEL_FL || type == assets::WHEEL_RL)
|
||||
wheeltrans.rotation = glm::rotate(wheeltrans.rotation, glm::radians(180.0f), glm::vec3(0, 1, 0));
|
||||
|
||||
wheels_[i].node.UpdateMatrix();
|
||||
}
|
||||
|
||||
@ -99,6 +106,7 @@ void game::view::VehicleView::Draw(gfx::DrawList& dlist)
|
||||
gfx::DrawSurfaceCmd cmd;
|
||||
cmd.surface = &surface;
|
||||
cmd.matrices = &root_.matrix;
|
||||
cmd.color = &color_;
|
||||
dlist.AddSurface(cmd);
|
||||
}
|
||||
|
||||
|
||||
@ -23,7 +23,7 @@ class VehicleView : public EntityView
|
||||
{
|
||||
using Super = EntityView;
|
||||
public:
|
||||
VehicleView(WorldView& world, std::shared_ptr<const assets::VehicleModel> model);
|
||||
VehicleView(WorldView& world, std::shared_ptr<const assets::VehicleModel> model, const glm::vec3& color);
|
||||
static std::unique_ptr<VehicleView> InitFromMsg(WorldView& world, net::InMessage& msg);
|
||||
|
||||
virtual bool ProcessMsg(net::EntMsgType type, net::InMessage& msg) override;
|
||||
@ -35,6 +35,7 @@ private:
|
||||
|
||||
private:
|
||||
std::shared_ptr<const assets::VehicleModel> model_;
|
||||
glm::vec4 color_;
|
||||
|
||||
std::vector<VehicleWheelViewInfo> wheels_;
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
|
||||
#include "assets/skeleton.hpp"
|
||||
#include "surface.hpp"
|
||||
#include "hud.hpp"
|
||||
|
||||
namespace gfx
|
||||
{
|
||||
@ -18,13 +19,27 @@ struct DrawSurfaceCmd
|
||||
float dist = 0.0f; // distance to camera - for transparnt sorting
|
||||
};
|
||||
|
||||
struct DrawHudCmd
|
||||
{
|
||||
const VertexArray* va = nullptr;
|
||||
const Texture* texture = nullptr;
|
||||
const HudPosition* pos = nullptr;
|
||||
const glm::vec4* color = nullptr;
|
||||
};
|
||||
|
||||
struct DrawList
|
||||
{
|
||||
std::vector<DrawSurfaceCmd> surfaces;
|
||||
std::vector<DrawHudCmd> huds;
|
||||
|
||||
void AddSurface(const DrawSurfaceCmd& cmd) { surfaces.emplace_back(cmd); }
|
||||
void AddHUD(const DrawHudCmd& cmd) { huds.emplace_back(cmd); }
|
||||
|
||||
void Clear() { surfaces.clear(); }
|
||||
void Clear()
|
||||
{
|
||||
surfaces.clear();
|
||||
huds.clear();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
46
src/gfx/font.cpp
Normal file
46
src/gfx/font.cpp
Normal file
@ -0,0 +1,46 @@
|
||||
#include "font.hpp"
|
||||
|
||||
#include "assets/cache.hpp"
|
||||
#include "assets/cmdfile.hpp"
|
||||
|
||||
std::shared_ptr<const gfx::Font> gfx::Font::LoadFromFile(const std::string& path)
|
||||
{
|
||||
auto font = std::make_shared<Font>();
|
||||
|
||||
glm::vec2 size(1.0f);
|
||||
|
||||
auto PxToUv = [&](const glm::vec2& pos_px) { return pos_px / size; };
|
||||
|
||||
assets::LoadCMDFile(path, [&](const std::string& cmd, std::istringstream& iss) {
|
||||
if (cmd == "c")
|
||||
{
|
||||
Codepoint cp = 0;
|
||||
glm::vec2 c_pos(0.0f);
|
||||
glm::vec2 c_size(0.0f);
|
||||
glm::vec3 c_offset(0.0f);
|
||||
float xadv = 0.0f;
|
||||
|
||||
iss >> cp >> c_pos.x >> c_pos.y >> c_size.x >> c_size.y >> c_offset.x >> c_offset.y >> xadv;
|
||||
|
||||
auto& glyph = font->glyphs_.Alloc(cp);
|
||||
glyph.uv0 = PxToUv(c_pos);
|
||||
glyph.uv1 = PxToUv(c_pos + c_size);
|
||||
glyph.offset = c_offset;
|
||||
glyph.size = c_size;
|
||||
glyph.advance = xadv;
|
||||
}
|
||||
else if (cmd == "texture")
|
||||
{
|
||||
std::string tex_name;
|
||||
iss >> tex_name >> size.x >> size.y;
|
||||
|
||||
font->texture_ = assets::CacheManager::GetTexture("data/" + tex_name + ".png");
|
||||
}
|
||||
else if (cmd == "size")
|
||||
{
|
||||
iss >> font->line_height_;
|
||||
}
|
||||
});
|
||||
|
||||
return font;
|
||||
}
|
||||
101
src/gfx/font.hpp
Normal file
101
src/gfx/font.hpp
Normal file
@ -0,0 +1,101 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "texture.hpp"
|
||||
|
||||
namespace gfx
|
||||
{
|
||||
|
||||
using Codepoint = uint32_t;
|
||||
|
||||
template <class T>
|
||||
class CodepointMap
|
||||
{
|
||||
static constexpr size_t MAX_ARRAY_CODEPOINT = 128;
|
||||
|
||||
struct ArrayItem
|
||||
{
|
||||
T data;
|
||||
bool used = false;
|
||||
};
|
||||
|
||||
public:
|
||||
CodepointMap() = default;
|
||||
|
||||
T& Alloc(Codepoint codepoint)
|
||||
{
|
||||
if (codepoint < MAX_ARRAY_CODEPOINT)
|
||||
{
|
||||
array_[codepoint].used = true;
|
||||
return array_[codepoint].data;
|
||||
}
|
||||
else
|
||||
{
|
||||
return map_[codepoint]; // Use map for larger codepoints
|
||||
}
|
||||
}
|
||||
|
||||
const T* Get(Codepoint codepoint) const
|
||||
{
|
||||
if (codepoint < MAX_ARRAY_CODEPOINT)
|
||||
{
|
||||
if (array_[codepoint].used)
|
||||
{
|
||||
return &array_[codepoint].data;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto it = map_.find(codepoint);
|
||||
if (it != map_.end())
|
||||
{
|
||||
return &it->second;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr; // Not found
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<ArrayItem, MAX_ARRAY_CODEPOINT> array_; // For common ASCII codepoints
|
||||
std::map<Codepoint, T> map_;
|
||||
};
|
||||
|
||||
struct FontGlyphData
|
||||
{
|
||||
glm::vec2 uv0;
|
||||
glm::vec2 uv1;
|
||||
glm::vec2 offset;
|
||||
glm::vec2 size;
|
||||
float advance = 0.0f; // Advance width for the codepoint
|
||||
};
|
||||
|
||||
class Font
|
||||
{
|
||||
public:
|
||||
Font() = default;
|
||||
static std::shared_ptr<const Font> LoadFromFile(const std::string& path);
|
||||
|
||||
const FontGlyphData* GetCodepointGlyph(Codepoint codepoint) const
|
||||
{
|
||||
const FontGlyphData* data = glyphs_.Get(codepoint);
|
||||
return data ? data : glyphs_.Get(0); // try return missing codepoint glyph if missing
|
||||
}
|
||||
|
||||
const std::shared_ptr<const Texture>& GetTexture() const { return texture_; }
|
||||
|
||||
float GetLineHeight() const { return line_height_; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<const Texture> texture_; // Texture atlas for the font
|
||||
CodepointMap<FontGlyphData> glyphs_;
|
||||
|
||||
float line_height_ = 0.0f; // Line height in pixels, used for text layout
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
15
src/gfx/hud.hpp
Normal file
15
src/gfx/hud.hpp
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace gfx
|
||||
{
|
||||
|
||||
struct HudPosition
|
||||
{
|
||||
glm::vec2 anchor = glm::vec2(0.0f); // <0;1>
|
||||
glm::vec2 pos = glm::vec2(0.0f); // px or px multiplies (scale != 1)
|
||||
glm::vec2 scale = glm::vec2(1.0f);
|
||||
};
|
||||
|
||||
}
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <ranges>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
@ -15,10 +16,12 @@ gfx::Renderer::Renderer()
|
||||
ShaderSources::MakeShader(mesh_shader_.shader, SS_MESH_VERT, SS_MESH_FRAG);
|
||||
ShaderSources::MakeShader(skel_mesh_shader_.shader, SS_SKEL_MESH_VERT, SS_SKEL_MESH_FRAG);
|
||||
ShaderSources::MakeShader(solid_shader_, SS_SOLID_VERT, SS_SOLID_FRAG);
|
||||
ShaderSources::MakeShader(hud_shader_, SS_HUD_VERT, SS_HUD_FRAG);
|
||||
}
|
||||
|
||||
void gfx::Renderer::Begin(size_t width, size_t height)
|
||||
{
|
||||
current_shader_ = nullptr;
|
||||
glViewport(0, 0, width, height);
|
||||
}
|
||||
|
||||
@ -36,6 +39,7 @@ void gfx::Renderer::ClearDepth()
|
||||
void gfx::Renderer::DrawList(gfx::DrawList& list, const DrawListParams& params)
|
||||
{
|
||||
DrawSurfaceList(list.surfaces, params);
|
||||
DrawHudList(list.huds, params);
|
||||
}
|
||||
|
||||
void gfx::Renderer::InvalidateShaders()
|
||||
@ -106,12 +110,14 @@ void gfx::Renderer::DrawSurfaceList(std::span<DrawSurfaceCmd> list, const DrawLi
|
||||
glEnable(GL_CULL_FACE);
|
||||
glCullFace(GL_BACK);
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
InvalidateShaders();
|
||||
|
||||
// cache to eliminate fake state changes
|
||||
const gfx::Texture* last_texture = nullptr;
|
||||
const gfx::VertexArray* last_vao = nullptr;
|
||||
bool last_double_sided = false;
|
||||
bool last_twosided = false;
|
||||
|
||||
glActiveTexture(GL_TEXTURE0); // for all future bindings
|
||||
|
||||
@ -122,19 +128,19 @@ void gfx::Renderer::DrawSurfaceList(std::span<DrawSurfaceCmd> list, const DrawLi
|
||||
// mesh flags
|
||||
const bool skeletal_flag = surface->mflags & MF_SKELETAL;
|
||||
// surface flags
|
||||
const bool double_sided_flag = surface->sflags & SF_DOUBLE_SIDED;
|
||||
const bool twosided_flag = surface->sflags & SF_2SIDED;
|
||||
const bool transparent_flag = surface->sflags & SF_TRANSPARENT;
|
||||
const bool object_color_flag = surface->sflags & SF_OBJECT_COLOR;
|
||||
|
||||
// adjust state
|
||||
if (last_double_sided != double_sided_flag)
|
||||
// sync 2sided
|
||||
if (last_twosided != twosided_flag)
|
||||
{
|
||||
if (double_sided_flag)
|
||||
if (twosided_flag)
|
||||
glDisable(GL_CULL_FACE);
|
||||
else
|
||||
glEnable(GL_CULL_FACE);
|
||||
|
||||
last_double_sided = double_sided_flag;
|
||||
last_twosided = twosided_flag;
|
||||
}
|
||||
|
||||
// select shader
|
||||
@ -144,19 +150,36 @@ void gfx::Renderer::DrawSurfaceList(std::span<DrawSurfaceCmd> list, const DrawLi
|
||||
// set model matrix
|
||||
if (cmd.matrices)
|
||||
{
|
||||
glUniformMatrix4fv(mshader.shader->U(gfx::SU_MODEL), 1, GL_FALSE, &cmd.matrices[0][0][0]);
|
||||
glUniformMatrix4fv(mshader.shader->U(SU_MODEL), 1, GL_FALSE, &cmd.matrices[0][0][0]);
|
||||
}
|
||||
else
|
||||
{ // use identity if no matrix provided
|
||||
static const glm::mat4 identity(1.0f);
|
||||
glUniformMatrix4fv(mshader.shader->U(gfx::SU_MODEL), 1, GL_FALSE, &identity[0][0]);
|
||||
glUniformMatrix4fv(mshader.shader->U(SU_MODEL), 1, GL_FALSE, &identity[0][0]);
|
||||
}
|
||||
|
||||
// set color
|
||||
glm::vec4 color = (object_color_flag && cmd.color) ? glm::vec4(*cmd.color) : glm::vec4(1.0f);
|
||||
bool cull_alpha = true;
|
||||
glm::vec4 color = glm::vec4(1.0f);
|
||||
|
||||
if (object_color_flag && cmd.color)
|
||||
{
|
||||
// use object color and disable alpha cull
|
||||
cull_alpha = false;
|
||||
color = glm::vec4(*cmd.color);
|
||||
}
|
||||
|
||||
// sync cull_alpha
|
||||
if (mshader.cull_alpha != cull_alpha)
|
||||
{
|
||||
glUniform1i(mshader.shader->U(SU_CULL_ALPHA), cull_alpha ? 1 : 0);
|
||||
mshader.cull_alpha = cull_alpha;
|
||||
}
|
||||
|
||||
// sync color
|
||||
if (mshader.color != color)
|
||||
{
|
||||
glUniform4fv(mshader.shader->U(gfx::SU_COLOR), 1, &color[0]);
|
||||
glUniform4fv(mshader.shader->U(SU_COLOR), 1, &color[0]);
|
||||
mshader.color = color;
|
||||
}
|
||||
|
||||
@ -186,3 +209,75 @@ void gfx::Renderer::DrawSurfaceList(std::span<DrawSurfaceCmd> list, const DrawLi
|
||||
|
||||
|
||||
}
|
||||
|
||||
void gfx::Renderer::DrawHudList(std::span<DrawHudCmd> queue, const DrawListParams& params)
|
||||
{
|
||||
// cannot sort anything here, must be drawn in FIFO order for correct overlay
|
||||
|
||||
Shader* shader = hud_shader_.get();
|
||||
current_shader_ = shader;
|
||||
glUseProgram(shader->GetId());
|
||||
|
||||
glDisable(GL_CULL_FACE);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
float w = static_cast<float>(params.screen_width);
|
||||
float h = static_cast<float>(params.screen_height);
|
||||
glm::vec2 screen_size_px(w, h);
|
||||
glm::vec2 ndc_scale(2.0f / screen_size_px.x, -2.0f / screen_size_px.y);
|
||||
constexpr glm::vec2 ndc_offset(-1.0f, 1.0f);
|
||||
|
||||
const gfx::Texture* last_texture = nullptr;
|
||||
const gfx::VertexArray* last_vao = nullptr;
|
||||
glm::vec4 last_color = glm::vec4(-1.0f);
|
||||
|
||||
for (const auto& cmd : queue)
|
||||
{
|
||||
if (!cmd.va || !cmd.texture || !cmd.pos)
|
||||
{
|
||||
throw std::runtime_error("invalid hud draw");
|
||||
}
|
||||
|
||||
// calculate transform
|
||||
const auto& hp = *cmd.pos;
|
||||
|
||||
glm::vec2 pos_px = hp.anchor * screen_size_px + hp.pos;
|
||||
glm::vec2 trans_ndc = ndc_offset + pos_px * ndc_scale;
|
||||
|
||||
glm::mat3 matrix(1.0f);
|
||||
matrix[0][0] = hp.scale.x * ndc_scale.x;
|
||||
matrix[1][1] = hp.scale.y * ndc_scale.y;
|
||||
matrix[2][0] = trans_ndc.x;
|
||||
matrix[2][1] = trans_ndc.y;
|
||||
|
||||
glUniformMatrix3fv(shader->U(SU_MODEL), 1, GL_FALSE, &matrix[0][0]);
|
||||
|
||||
//sync color
|
||||
glm::vec4 color = cmd.color ? *cmd.color : glm::vec4(1.0f);
|
||||
if (last_color != color)
|
||||
{
|
||||
glUniform4fv(shader->U(SU_COLOR), 1, &color[0]);
|
||||
last_color = color;
|
||||
}
|
||||
|
||||
// bind texture
|
||||
if (last_texture != cmd.texture)
|
||||
{
|
||||
GLuint tex_id = cmd.texture ? cmd.texture->GetId() : 0;
|
||||
glBindTexture(GL_TEXTURE_2D, tex_id);
|
||||
last_texture = cmd.texture;
|
||||
}
|
||||
|
||||
// bind vao
|
||||
if (last_vao = cmd.va)
|
||||
{
|
||||
glBindVertexArray(cmd.va->GetVAOId());
|
||||
last_vao = cmd.va;
|
||||
}
|
||||
|
||||
glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(cmd.va->GetNumIndices()), GL_UNSIGNED_INT, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,6 +11,8 @@ namespace gfx
|
||||
struct DrawListParams
|
||||
{
|
||||
glm::mat4 view_proj;
|
||||
size_t screen_width = 0;
|
||||
size_t screen_height = 0;
|
||||
};
|
||||
|
||||
struct MeshShader
|
||||
@ -20,6 +22,7 @@ namespace gfx
|
||||
// cached state to avoid redundant uniform updates which are expensive especially on WebGL
|
||||
bool global_setup = false;
|
||||
glm::vec4 color = glm::vec4(-1.0f); // invalid to force initial setup
|
||||
bool cull_alpha = false;
|
||||
};
|
||||
|
||||
class Renderer
|
||||
@ -38,6 +41,7 @@ namespace gfx
|
||||
MeshShader mesh_shader_;
|
||||
MeshShader skel_mesh_shader_;
|
||||
std::unique_ptr<Shader> solid_shader_;
|
||||
std::unique_ptr<Shader> hud_shader_;
|
||||
|
||||
const Shader* current_shader_ = nullptr;
|
||||
|
||||
@ -46,6 +50,7 @@ namespace gfx
|
||||
void SetupMeshShader(MeshShader& mshader, const DrawListParams& params);
|
||||
|
||||
void DrawSurfaceList(std::span<DrawSurfaceCmd> queue, const DrawListParams& params);
|
||||
void DrawHudList(std::span<DrawHudCmd> queue, const DrawListParams& params);
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -8,7 +8,8 @@ static const char* const s_uni_names[] = {
|
||||
"u_model", // SU_MODEL
|
||||
"u_view_proj", // SU_VIEW_PROJ
|
||||
"u_tex", // SU_TEX
|
||||
"u_color" // SU_COLOR
|
||||
"u_color", // SU_COLOR
|
||||
"u_cull_alpha", // SU_COLOR
|
||||
};
|
||||
|
||||
// Vytvori shader z daneho zdroje
|
||||
|
||||
@ -14,6 +14,7 @@ namespace gfx
|
||||
SU_VIEW_PROJ,
|
||||
SU_TEX,
|
||||
SU_COLOR,
|
||||
SU_CULL_ALPHA,
|
||||
|
||||
SU_COUNT
|
||||
};
|
||||
|
||||
@ -79,7 +79,7 @@ SHADER_HEADER
|
||||
R"GLSL(
|
||||
layout (location = 0) in vec3 a_pos;
|
||||
layout (location = 1) in vec3 a_normal;
|
||||
layout (location = 2) in vec3 a_color;
|
||||
layout (location = 2) in vec4 a_color;
|
||||
layout (location = 3) in vec2 a_uv;
|
||||
|
||||
)GLSL"
|
||||
@ -96,8 +96,8 @@ void main() {
|
||||
vec3 world_normal = normalize(mat3(u_model) * a_normal);
|
||||
gl_Position = u_view_proj * world_pos;
|
||||
|
||||
v_uv = vec2(a_uv.x, 1.0 - a_uv.y);
|
||||
v_color = ComputeLights(world_pos.xyz, world_normal) * a_color;
|
||||
v_uv = a_uv;
|
||||
v_color = ComputeLights(world_pos.xyz, world_normal) * a_color.rgb;
|
||||
}
|
||||
)GLSL",
|
||||
|
||||
@ -108,11 +108,25 @@ in vec2 v_uv;
|
||||
in vec3 v_color;
|
||||
|
||||
uniform sampler2D u_tex;
|
||||
uniform vec4 u_color;
|
||||
uniform bool u_cull_alpha;
|
||||
|
||||
layout (location = 0) out vec4 o_color;
|
||||
|
||||
void main() {
|
||||
o_color = vec4(texture(u_tex, v_uv));
|
||||
|
||||
if (u_cull_alpha)
|
||||
{
|
||||
if (o_color.a < 0.5)
|
||||
discard;
|
||||
}
|
||||
else
|
||||
{
|
||||
// blend with bg
|
||||
o_color = mix(u_color, o_color, o_color.a);
|
||||
}
|
||||
|
||||
o_color.rgb *= v_color; // Apply vertex color
|
||||
//o_color = vec4(1.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
@ -155,7 +169,7 @@ void main() {
|
||||
vec3 world_normal = normalize(mat3(u_model) * mat3(bone_transform) * a_normal);
|
||||
gl_Position = u_view_proj * world_pos;
|
||||
|
||||
v_uv = vec2(a_uv.x, 1.0 - a_uv.y);
|
||||
v_uv = a_uv;
|
||||
|
||||
v_color = ComputeLights(world_pos.xyz, world_normal);
|
||||
}
|
||||
@ -209,6 +223,44 @@ void main() {
|
||||
|
||||
)GLSL",
|
||||
|
||||
// SS_HUD_VERT
|
||||
SHADER_HEADER
|
||||
R"GLSL(
|
||||
layout (location = 0) in vec3 a_pos;
|
||||
layout (location = 2) in vec4 a_color;
|
||||
layout (location = 3) in vec2 a_uv;
|
||||
|
||||
uniform mat3 u_model;
|
||||
uniform vec4 u_color;
|
||||
|
||||
out vec4 v_color;
|
||||
out vec2 v_uv;
|
||||
|
||||
void main() {
|
||||
vec3 pos2d = u_model * vec3(a_pos.xy, 1.0);
|
||||
gl_Position = vec4(pos2d.xy, 0.0, 1.0);
|
||||
v_color = a_color * u_color;
|
||||
v_uv = a_uv;
|
||||
}
|
||||
)GLSL",
|
||||
|
||||
// SS_HUD_FRAG
|
||||
SHADER_HEADER
|
||||
R"GLSL(
|
||||
|
||||
in vec4 v_color;
|
||||
in vec2 v_uv;
|
||||
|
||||
uniform sampler2D u_tex;
|
||||
|
||||
layout (location = 0) out vec4 o_color;
|
||||
|
||||
void main() {
|
||||
o_color = texture(u_tex, v_uv);
|
||||
o_color *= v_color;
|
||||
}
|
||||
|
||||
)GLSL",
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -16,6 +16,9 @@ namespace gfx
|
||||
SS_SOLID_VERT,
|
||||
SS_SOLID_FRAG,
|
||||
|
||||
SS_HUD_VERT,
|
||||
SS_HUD_FRAG,
|
||||
|
||||
};
|
||||
|
||||
class ShaderSources
|
||||
|
||||
@ -20,7 +20,7 @@ using SurfaceFlags = uint8_t;
|
||||
enum SurfaceFlag : SurfaceFlags
|
||||
{
|
||||
SF_NONE = 0x00,
|
||||
SF_DOUBLE_SIDED = 0x01,
|
||||
SF_2SIDED = 0x01,
|
||||
SF_TRANSPARENT = 0x02,
|
||||
SF_OBJECT_COLOR = 0x08, // use object level tint
|
||||
};
|
||||
|
||||
162
src/gfx/text.cpp
Normal file
162
src/gfx/text.cpp
Normal file
@ -0,0 +1,162 @@
|
||||
#include "text.hpp"
|
||||
|
||||
#include "utils/bufferput.hpp"
|
||||
|
||||
gfx::Text::Text(std::shared_ptr<const Font> font, uint32_t color)
|
||||
: font_(std::move(font)), va_(VA_POSITION | VA_UV | VA_COLOR, VF_CREATE_EBO | VF_DYNAMIC), color_(color)
|
||||
{
|
||||
}
|
||||
|
||||
static uint32_t DecodeUTF8Codepoint(const char*& p)
|
||||
{
|
||||
if (!*p)
|
||||
return 0;
|
||||
|
||||
if ((*p & 0b10000000) == 0)
|
||||
{ // 1-byte sequence
|
||||
return *p++;
|
||||
}
|
||||
|
||||
uint32_t codepoint = 0;
|
||||
|
||||
if ((*p & 0b11100000) == 0b11000000)
|
||||
{ // 2-byte seq
|
||||
codepoint = (*p++ & 0b00011111) << 6;
|
||||
if (!*p)
|
||||
return 0;
|
||||
codepoint |= (*p++ & 0b00111111);
|
||||
}
|
||||
else if ((*p & 0b11110000) == 0b11100000)
|
||||
{ // 3-byte seq
|
||||
codepoint = (*p++ & 0b00001111) << 12;
|
||||
if (!*p)
|
||||
return 0;
|
||||
codepoint |= (*p++ & 0b00111111) << 6;
|
||||
if (!*p)
|
||||
return 0;
|
||||
codepoint |= (*p++ & 0b00111111);
|
||||
}
|
||||
else if ((*p & 0b11111000) == 0b11110000)
|
||||
{ // 4-byte seq
|
||||
codepoint = (*p++ & 0b00000111) << 18;
|
||||
if (!*p)
|
||||
return 0;
|
||||
codepoint |= (*p++ & 0b00111111) << 12;
|
||||
if (!*p)
|
||||
return 0;
|
||||
codepoint |= (*p++ & 0b00111111) << 6;
|
||||
if (!*p)
|
||||
return 0;
|
||||
codepoint |= (*p++ & 0b00111111);
|
||||
}
|
||||
|
||||
return codepoint;
|
||||
}
|
||||
|
||||
|
||||
struct TextVertex
|
||||
{
|
||||
glm::vec3 pos;
|
||||
uint32_t color;
|
||||
glm::vec2 uv;
|
||||
};
|
||||
|
||||
static void PutVertex(std::vector<TextVertex>& buf, const glm::vec3 pos, const glm::vec2& uv, uint32_t color)
|
||||
{
|
||||
//BufferPut(buf, pos);
|
||||
//BufferPut(buf, color);
|
||||
//BufferPut(buf, uv);
|
||||
buf.emplace_back(TextVertex{pos, color, uv});
|
||||
}
|
||||
|
||||
void gfx::Text::SetText(const char* text)
|
||||
{
|
||||
static std::vector<TextVertex> vertices;
|
||||
static std::vector<uint32_t> indices;
|
||||
vertices.clear();
|
||||
indices.clear();
|
||||
|
||||
float space_size = font_->GetLineHeight() * 0.3f;
|
||||
|
||||
glm::vec2 cursor(0.0f, 0.0f);
|
||||
|
||||
uint32_t cp = 0;
|
||||
const char* p = text;
|
||||
|
||||
uint32_t color = color_;
|
||||
|
||||
while (cp = DecodeUTF8Codepoint(p))
|
||||
{
|
||||
if (cp == ' ')
|
||||
{
|
||||
cursor.x += space_size; // Move cursor for space
|
||||
continue;
|
||||
}
|
||||
else if (cp == '^')
|
||||
{
|
||||
if (!(cp = DecodeUTF8Codepoint(p)))
|
||||
break;
|
||||
|
||||
if (cp == 'r') // reset color
|
||||
{
|
||||
color = color_;
|
||||
continue;
|
||||
}
|
||||
|
||||
color = 0;
|
||||
|
||||
// parse color
|
||||
for (size_t i = 0; i < 3; ++i)
|
||||
{
|
||||
color >>= 8;
|
||||
uint32_t ch;
|
||||
if (cp >= '0' && cp <= '9')
|
||||
ch = cp - '0';
|
||||
else if (cp >= 'a' && cp <= 'f')
|
||||
ch = cp - 'a' + 10;
|
||||
else
|
||||
break;
|
||||
|
||||
color |= (ch << 16);
|
||||
color |= (ch << 20);
|
||||
|
||||
if (!(cp = DecodeUTF8Codepoint(p)))
|
||||
break;
|
||||
}
|
||||
|
||||
color |= 0xFF000000; // alpha=1
|
||||
|
||||
//if (cp != ';')
|
||||
// break;
|
||||
|
||||
//continue;
|
||||
}
|
||||
|
||||
const FontGlyphData* glyph = font_->GetCodepointGlyph(cp);
|
||||
|
||||
if (!glyph)
|
||||
continue; // Dont even have "missing" glyph, font is shit
|
||||
|
||||
glm::vec2 p0 = cursor + glyph->offset;
|
||||
glm::vec2 p1 = p0 + glyph->size;
|
||||
|
||||
uint32_t base_index = vertices.size();
|
||||
|
||||
PutVertex(vertices, glm::vec3(p0.x, -p0.y, 0.0f), glyph->uv0, color);
|
||||
PutVertex(vertices, glm::vec3(p1.x, -p0.y, 0.0f), glm::vec2(glyph->uv1.x, glyph->uv0.y), color);
|
||||
PutVertex(vertices, glm::vec3(p1.x, -p1.y, 0.0f), glyph->uv1, color);
|
||||
PutVertex(vertices, glm::vec3(p0.x, -p1.y, 0.0f), glm::vec2(glyph->uv0.x, glyph->uv1.y), color);
|
||||
|
||||
indices.push_back(base_index + 0);
|
||||
indices.push_back(base_index + 1);
|
||||
indices.push_back(base_index + 2);
|
||||
indices.push_back(base_index + 0);
|
||||
indices.push_back(base_index + 2);
|
||||
indices.push_back(base_index + 3);
|
||||
|
||||
cursor.x += glyph->advance;
|
||||
}
|
||||
|
||||
va_.SetVBOData(vertices.data(), vertices.size() * sizeof(TextVertex));
|
||||
va_.SetIndices(indices.data(), indices.size());
|
||||
}
|
||||
26
src/gfx/text.hpp
Normal file
26
src/gfx/text.hpp
Normal file
@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "font.hpp"
|
||||
#include "vertex_array.hpp"
|
||||
|
||||
namespace gfx
|
||||
{
|
||||
|
||||
class Text
|
||||
{
|
||||
public:
|
||||
Text(std::shared_ptr<const Font> font, uint32_t color);
|
||||
|
||||
void SetText(const char* text);
|
||||
void SetText(const std::string& text) { SetText(text.c_str()); }
|
||||
|
||||
const std::shared_ptr<const Font>& GetFont() const { return font_; }
|
||||
const VertexArray& GetVA() const { return va_; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<const Font> font_;
|
||||
VertexArray va_;
|
||||
uint32_t color_;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
@ -25,7 +25,7 @@ static void GetGLFilterModes(bool linear, bool mipmaps, GLenum& filter_min, GLen
|
||||
if (mipmaps)
|
||||
{
|
||||
// Mipmaps always linear
|
||||
filter_min = GL_NEAREST_MIPMAP_LINEAR;
|
||||
filter_min = GL_LINEAR_MIPMAP_LINEAR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -76,4 +76,6 @@ using QuatQ = Quantized<uint16_t, -1, 1, 1>;
|
||||
using WheelZOffsetQ = Quantized<uint8_t, -1, 1, 1>;
|
||||
using RotationSpeedQ = Quantized<uint16_t, -300, 300, 1>;
|
||||
|
||||
using ColorQ = Quantized<uint8_t, 0, 1>;
|
||||
|
||||
} // namespace net
|
||||
@ -8,6 +8,8 @@
|
||||
namespace net
|
||||
{
|
||||
|
||||
/// TRANSFORMS
|
||||
|
||||
inline void WritePosition(OutMessage& msg, const glm::vec3& pos)
|
||||
{
|
||||
msg.Write<PositionQ>(pos.x);
|
||||
@ -54,4 +56,18 @@ inline bool ReadTransform(InMessage& msg, Transform& trans)
|
||||
return ReadPosition(msg, trans.position) && ReadRotation(msg, trans.rotation);
|
||||
}
|
||||
|
||||
/// COLOR
|
||||
|
||||
inline void WriteRGB(OutMessage& msg, const glm::vec3& color)
|
||||
{
|
||||
msg.Write<ColorQ>(color.r);
|
||||
msg.Write<ColorQ>(color.g);
|
||||
msg.Write<ColorQ>(color.b);
|
||||
}
|
||||
|
||||
inline bool ReadRGB(InMessage& msg, glm::vec3& color)
|
||||
{
|
||||
return msg.Read<ColorQ>(color.r) && msg.Read<ColorQ>(color.g) && msg.Read<ColorQ>(color.b);
|
||||
}
|
||||
|
||||
}
|
||||
10
src/utils/bufferput.hpp
Normal file
10
src/utils/bufferput.hpp
Normal file
@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
template <class T>
|
||||
inline void BufferPut(std::vector<char>& buffer, const T& val)
|
||||
{
|
||||
const char* data = reinterpret_cast<const char*>(&val);
|
||||
buffer.insert(buffer.end(), data, data + sizeof(T));
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user