Big amount of stuff but especially map partitioning, better bot AI and nametags
This commit is contained in:
parent
7d490d20f3
commit
4b04a77e7e
@ -20,7 +20,6 @@ set(COMMON_SOURCES
|
||||
"src/assets/skeleton.cpp"
|
||||
"src/assets/vehiclemdl.hpp"
|
||||
"src/assets/vehiclemdl.cpp"
|
||||
"src/collision/aabb.hpp"
|
||||
"src/collision/dynamicsworld.hpp"
|
||||
"src/collision/dynamicsworld.cpp"
|
||||
"src/collision/motionstate.hpp"
|
||||
@ -37,10 +36,12 @@ set(COMMON_SOURCES
|
||||
"src/net/outmessage.hpp"
|
||||
"src/net/quantized.hpp"
|
||||
"src/net/utils.hpp"
|
||||
"src/utils/aabb.hpp"
|
||||
"src/utils/allocnum.hpp"
|
||||
"src/utils/defs.hpp"
|
||||
"src/utils/files.hpp"
|
||||
"src/utils/scheduler.hpp"
|
||||
"src/utils/sphere.hpp"
|
||||
"src/utils/transform.hpp"
|
||||
"src/utils/validate.hpp"
|
||||
)
|
||||
@ -79,6 +80,8 @@ set(CLIENT_ONLY_SOURCES
|
||||
"src/gfx/draw_list.hpp"
|
||||
"src/gfx/font.hpp"
|
||||
"src/gfx/font.cpp"
|
||||
"src/gfx/frustum.hpp"
|
||||
"src/gfx/frustum.cpp"
|
||||
"src/gfx/hud.hpp"
|
||||
"src/gfx/renderer.hpp"
|
||||
"src/gfx/renderer.cpp"
|
||||
|
||||
@ -27,6 +27,8 @@ std::shared_ptr<const assets::Map> assets::Map::LoadFromFile(const std::string&
|
||||
}
|
||||
};
|
||||
|
||||
Chunk* chunk = nullptr;
|
||||
|
||||
LoadCMDFile(filename, [&](const std::string& command, std::istringstream& iss) {
|
||||
if (command == "basemodel")
|
||||
{
|
||||
@ -37,8 +39,10 @@ std::shared_ptr<const assets::Map> assets::Map::LoadFromFile(const std::string&
|
||||
}
|
||||
else if (command == "static")
|
||||
{
|
||||
MapStaticObject obj;
|
||||
if (!chunk)
|
||||
throw std::runtime_error("static in map without chunk");
|
||||
|
||||
ChunkStaticObject obj;
|
||||
std::string model_name;
|
||||
iss >> model_name;
|
||||
|
||||
@ -55,6 +59,9 @@ std::shared_ptr<const assets::Map> assets::Map::LoadFromFile(const std::string&
|
||||
|
||||
obj.node.UpdateMatrix();
|
||||
|
||||
obj.aabb.min = trans->position - glm::vec3(1.0f);
|
||||
obj.aabb.max = trans->position + glm::vec3(1.0f);
|
||||
|
||||
std::string flag;
|
||||
while (iss >> flag)
|
||||
{
|
||||
@ -64,8 +71,47 @@ std::shared_ptr<const assets::Map> assets::Map::LoadFromFile(const std::string&
|
||||
}
|
||||
}
|
||||
|
||||
map->static_objects_.push_back(std::move(obj));
|
||||
chunk->objs.push_back(std::move(obj));
|
||||
}
|
||||
else if (command == "chunk")
|
||||
{
|
||||
glm::ivec2 coord;
|
||||
chunk = &map->chunks_.emplace_back();
|
||||
iss >> coord.x >> coord.y;
|
||||
iss >> chunk->aabb.min.x >> chunk->aabb.min.y >> chunk->aabb.min.z;
|
||||
iss >> chunk->aabb.max.x >> chunk->aabb.max.y >> chunk->aabb.max.z;
|
||||
|
||||
}
|
||||
else if (command == "surface")
|
||||
{
|
||||
std::string name;
|
||||
size_t first, count;
|
||||
iss >> name >> first >> count;
|
||||
|
||||
if (!chunk)
|
||||
throw std::runtime_error("surface in map without chunk");
|
||||
|
||||
#ifdef CLIENT
|
||||
if (!map->basemodel_)
|
||||
throw std::runtime_error("surface in map with no basemodel");
|
||||
|
||||
auto mesh = map->basemodel_->GetMesh();
|
||||
|
||||
if (!mesh)
|
||||
throw std::runtime_error("surface in map with no basemodel mesh");
|
||||
|
||||
auto it = mesh->surface_names.find(name);
|
||||
if (it == mesh->surface_names.end())
|
||||
throw std::runtime_error("surface name not found");
|
||||
|
||||
if (first + count > mesh->surfaces[it->second].count)
|
||||
throw std::runtime_error("surface invalid range");
|
||||
|
||||
chunk->surfaces.emplace_back(it->second, first, count);
|
||||
|
||||
#endif /* CLIENT */
|
||||
}
|
||||
|
||||
else if (command == "graph")
|
||||
{
|
||||
if (graph)
|
||||
@ -115,25 +161,42 @@ const assets::MapGraph* assets::Map::GetGraph(const std::string& name) const
|
||||
}
|
||||
|
||||
#ifdef CLIENT
|
||||
void assets::Map::Draw(gfx::DrawList& dlist) const
|
||||
void assets::Map::Draw(const game::view::DrawArgs& args) const
|
||||
{
|
||||
if (!basemodel_ || !basemodel_->GetMesh())
|
||||
return;
|
||||
|
||||
const auto& surfaces = basemodel_->GetMesh()->surfaces;
|
||||
const auto& mesh = *basemodel_->GetMesh();
|
||||
|
||||
for (const auto& surface : surfaces)
|
||||
for (auto& chunk : chunks_)
|
||||
{
|
||||
gfx::DrawSurfaceCmd cmd;
|
||||
cmd.surface = &surface;
|
||||
dlist.AddSurface(cmd);
|
||||
if (args.frustum.IsAABBVisible(chunk.aabb))
|
||||
DrawChunk(args, mesh, chunk);
|
||||
}
|
||||
|
||||
for (const auto& obj : static_objects_)
|
||||
}
|
||||
|
||||
void assets::Map::DrawChunk(const game::view::DrawArgs& args, const Mesh& basemesh, const Chunk& chunk) const
|
||||
{
|
||||
for (const auto& surface_range : chunk.surfaces)
|
||||
{
|
||||
auto& surface = basemesh.surfaces[surface_range.idx];
|
||||
|
||||
gfx::DrawSurfaceCmd cmd;
|
||||
cmd.surface = &surface;
|
||||
cmd.first = surface_range.first;
|
||||
cmd.count = surface_range.count;
|
||||
args.dlist.AddSurface(cmd);
|
||||
}
|
||||
|
||||
for (const auto& obj : chunk.objs)
|
||||
{
|
||||
if (!obj.model || !obj.model->GetMesh())
|
||||
continue;
|
||||
|
||||
if (!args.frustum.IsAABBVisible(obj.aabb))
|
||||
continue;
|
||||
|
||||
const auto& surfaces = obj.model->GetMesh()->surfaces;
|
||||
|
||||
for (const auto& surface : surfaces)
|
||||
@ -142,8 +205,10 @@ void assets::Map::Draw(gfx::DrawList& dlist) const
|
||||
cmd.surface = &surface;
|
||||
cmd.matrices = &obj.node.matrix;
|
||||
// cmd.color_mod = glm::vec4(obj.color, 1.0f);
|
||||
dlist.AddSurface(cmd);
|
||||
args.dlist.AddSurface(cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // CLIENT
|
||||
|
||||
#endif // CLIENT
|
||||
|
||||
|
||||
@ -1,27 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "model.hpp"
|
||||
#include "game/transform_node.hpp"
|
||||
#include "model.hpp"
|
||||
#include "utils/aabb.hpp"
|
||||
|
||||
#ifdef CLIENT
|
||||
#include "gfx/draw_list.hpp"
|
||||
#include "gameview/draw_args.hpp"
|
||||
#endif // CLIENT
|
||||
|
||||
namespace assets
|
||||
{
|
||||
|
||||
struct MapStaticObject
|
||||
struct ChunkStaticObject
|
||||
{
|
||||
game::TransformNode node;
|
||||
AABB3 aabb;
|
||||
std::shared_ptr<const Model> model;
|
||||
glm::vec3 color = glm::vec3(1.0f);
|
||||
};
|
||||
|
||||
struct ChunkSurfaceRange
|
||||
{
|
||||
size_t idx = 0;
|
||||
size_t first = 0;
|
||||
size_t count = 0;
|
||||
|
||||
ChunkSurfaceRange(size_t idx, size_t first, size_t count) : idx(idx), first(first), count(count) {}
|
||||
};
|
||||
|
||||
struct Chunk
|
||||
{
|
||||
AABB3 aabb;
|
||||
std::vector<ChunkSurfaceRange> surfaces;
|
||||
std::vector<ChunkStaticObject> objs;
|
||||
};
|
||||
|
||||
struct MapGraphNode
|
||||
{
|
||||
glm::vec3 position = glm::vec3(0.0f);
|
||||
@ -42,14 +60,17 @@ public:
|
||||
static std::shared_ptr<const Map> LoadFromFile(const std::string& filename);
|
||||
|
||||
const std::shared_ptr<const Model>& GetBaseModel() const { return basemodel_; }
|
||||
const std::vector<MapStaticObject>& GetStaticObjects() const { return static_objects_; }
|
||||
const std::vector<Chunk>& GetChunks() const { return chunks_; }
|
||||
const MapGraph* GetGraph(const std::string& name) const;
|
||||
|
||||
CLIENT_ONLY(void Draw(gfx::DrawList& dlist) const;)
|
||||
CLIENT_ONLY(void Draw(const game::view::DrawArgs& args) const;)
|
||||
|
||||
private:
|
||||
CLIENT_ONLY(void DrawChunk(const game::view::DrawArgs& args, const Mesh& basemesh, const Chunk& chunk) const;)
|
||||
|
||||
private:
|
||||
std::shared_ptr<const Model> basemodel_;
|
||||
std::vector<MapStaticObject> static_objects_;
|
||||
std::vector<Chunk> chunks_;
|
||||
std::map<std::string, MapGraph> graphs_;
|
||||
};
|
||||
|
||||
|
||||
@ -80,14 +80,15 @@ void App::Frame()
|
||||
const game::view::WorldView* world;
|
||||
if (session_ && (world = session_->GetWorld()))
|
||||
{
|
||||
world->Draw(dlist_);
|
||||
|
||||
// glm::mat4 view = glm::lookAt(glm::vec3(15.0f, 0.0f, 1.0f), glm::vec3(0.0f, 0.0f, -13.0f), glm::vec3(0.0f, 0.0f, 1.0f));
|
||||
glm::mat4 proj = glm::perspective(glm::radians(45.0f), aspect, 0.1f, 3000.0f);
|
||||
glm::mat4 view = session_->GetViewMatrix();
|
||||
|
||||
params.view_proj = proj * view;
|
||||
|
||||
|
||||
game::view::DrawArgs draw_args(dlist_, params.view_proj, viewport_size_);
|
||||
world->Draw(draw_args);
|
||||
|
||||
glm::mat4 camera_world = glm::inverse(view);
|
||||
audiomaster_.SetListenerOrientation(camera_world);
|
||||
}
|
||||
@ -127,9 +128,11 @@ void App::ProcessMessage(net::InMessage& msg)
|
||||
if (!session_)
|
||||
return;
|
||||
|
||||
//size_t s = msg.End() - msg.Ptr();
|
||||
size_t s = msg.End() - msg.Ptr();
|
||||
// AddChatMessage("recvd: ^f00;" + std::to_string(s));
|
||||
|
||||
// std::cout << "App::ProcessMessage: received message of size " << s << " bytes" << std::endl;
|
||||
|
||||
session_->ProcessMessage(msg);
|
||||
}
|
||||
|
||||
|
||||
@ -1,54 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <limits>
|
||||
|
||||
namespace collision
|
||||
{
|
||||
template <size_t dim>
|
||||
struct AABB
|
||||
{
|
||||
static constexpr float C_INFINITY = std::numeric_limits<float>::infinity();
|
||||
|
||||
using Vec = glm::vec<dim, float, glm::defaultp>;
|
||||
|
||||
Vec min = Vec(C_INFINITY);
|
||||
Vec max = Vec(-C_INFINITY);
|
||||
|
||||
AABB() = default;
|
||||
AABB(const Vec& min, const Vec& max) : min(min), max(max) {}
|
||||
|
||||
void AddPoint(const Vec& point)
|
||||
{
|
||||
min = glm::min(min, point);
|
||||
max = glm::max(max, point);
|
||||
}
|
||||
|
||||
bool CollidesWith(const AABB<dim>& other) const;
|
||||
|
||||
AABB<dim> Intersection(const AABB<dim>& other) const
|
||||
{
|
||||
Vec new_min = glm::max(min, other.min);
|
||||
Vec new_max = glm::min(max, other.max);
|
||||
return AABB<dim>(new_min, new_max);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
inline bool AABB<2>::CollidesWith(const AABB<2>& other) const
|
||||
{
|
||||
return (min.x <= other.max.x && max.x >= other.min.x) &&
|
||||
(min.y <= other.max.y && max.y >= other.min.y);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool AABB<3>::CollidesWith(const AABB<3>& other) const
|
||||
{
|
||||
return (min.x <= other.max.x && max.x >= other.min.x) &&
|
||||
(min.y <= other.max.y && max.y >= other.min.y) &&
|
||||
(min.z <= other.max.z && max.z >= other.min.z);
|
||||
}
|
||||
|
||||
using AABB2 = AABB<2>;
|
||||
using AABB3 = AABB<3>;
|
||||
}
|
||||
@ -36,10 +36,11 @@ void collision::DynamicsWorld::AddMapCollision()
|
||||
}
|
||||
|
||||
// add static objects
|
||||
for (const auto& sobjs = map_->GetStaticObjects(); const auto& sobj : sobjs)
|
||||
{
|
||||
AddModelInstance(*sobj.model, sobj.node.local);
|
||||
}
|
||||
|
||||
// for (const auto& sobjs = map_->GetStaticObjects(); const auto& sobj : sobjs)
|
||||
// {
|
||||
// AddModelInstance(*sobj.model, sobj.node.local);
|
||||
// }
|
||||
}
|
||||
|
||||
void collision::DynamicsWorld::AddModelInstance(const assets::Model& model, const Transform& trans)
|
||||
|
||||
@ -10,7 +10,7 @@ void collision::TriangleMesh::AddTriangle(const glm::vec3& v0, const glm::vec3&
|
||||
btVector3 bt_v0(v0.x, v0.y, v0.z);
|
||||
btVector3 bt_v1(v1.x, v1.y, v1.z);
|
||||
btVector3 bt_v2(v2.x, v2.y, v2.z);
|
||||
bt_mesh_.addTriangle(bt_v0, bt_v1, bt_v2, true);
|
||||
bt_mesh_.addTriangle(bt_v0, bt_v1, bt_v2, false);
|
||||
}
|
||||
|
||||
void collision::TriangleMesh::Build()
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
#include <vector>
|
||||
#include <span>
|
||||
#include <glm/glm.hpp>
|
||||
#include "aabb.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
|
||||
@ -10,6 +10,28 @@ void game::Entity::Update()
|
||||
RunTasks();
|
||||
}
|
||||
|
||||
void game::Entity::SendInitData(Player& player, net::OutMessage& msg) const
|
||||
{
|
||||
WriteNametag(msg);
|
||||
}
|
||||
|
||||
void game::Entity::SetNametag(const std::string& nametag)
|
||||
{
|
||||
nametag_ = nametag;
|
||||
SendNametagMsg(); // notify viewers
|
||||
}
|
||||
|
||||
void game::Entity::WriteNametag(net::OutMessage& msg) const
|
||||
{
|
||||
msg.Write(net::NameTag{nametag_});
|
||||
}
|
||||
|
||||
void game::Entity::SendNametagMsg()
|
||||
{
|
||||
auto msg = BeginEntMsg(net::EMSG_NAMETAG);
|
||||
WriteNametag(msg);
|
||||
}
|
||||
|
||||
net::OutMessage game::Entity::BeginEntMsg(net::EntMsgType type)
|
||||
{
|
||||
auto msg = BeginMsg(net::MSG_ENTMSG);
|
||||
|
||||
@ -23,13 +23,20 @@ public:
|
||||
net::EntType GetViewType() const { return viewtype_; }
|
||||
|
||||
virtual void Update();
|
||||
virtual void SendInitData(Player& player, net::OutMessage& msg) const {}
|
||||
virtual void SendInitData(Player& player, net::OutMessage& msg) const;
|
||||
|
||||
void SetNametag(const std::string& nametag);
|
||||
|
||||
void Remove() { removed_ = true; }
|
||||
bool IsRemoved() const { return removed_; }
|
||||
|
||||
virtual ~Entity() = default;
|
||||
|
||||
private:
|
||||
void WriteNametag(net::OutMessage& msg) const;
|
||||
|
||||
void SendNametagMsg();
|
||||
|
||||
protected:
|
||||
net::OutMessage BeginEntMsg(net::EntMsgType type);
|
||||
|
||||
@ -39,8 +46,10 @@ protected:
|
||||
const net::EntType viewtype_;
|
||||
|
||||
TransformNode root_;
|
||||
std::string nametag_;
|
||||
|
||||
bool removed_ = false;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@ -94,7 +94,7 @@ game::OpenWorld::OpenWorld() : World("openworld")
|
||||
// }
|
||||
|
||||
// spawn bots
|
||||
for (size_t i = 0; i < 20; ++i)
|
||||
for (size_t i = 0; i < 30; ++i)
|
||||
{
|
||||
SpawnBot();
|
||||
}
|
||||
@ -221,25 +221,27 @@ struct BotThinkState
|
||||
{
|
||||
game::Vehicle& vehicle;
|
||||
const assets::MapGraph& roads;
|
||||
size_t node = 0;
|
||||
glm::vec3 seg_start;
|
||||
std::deque<size_t> path;
|
||||
bool gas = false;
|
||||
size_t stuck_counter = 0;
|
||||
glm::vec3 last_pos = glm::vec3(0.0f);
|
||||
float speed_limit = 0.0f;
|
||||
const char* state_str = "init";
|
||||
|
||||
BotThinkState(game::Vehicle& v, const assets::MapGraph& g, size_t n)
|
||||
: vehicle(v), roads(g), node(n) {}
|
||||
: vehicle(v), roads(g), path({n})
|
||||
{
|
||||
seg_start = v.GetPosition();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static float GetSteeringAngle(const glm::vec3& pos, const glm::quat& rot, const glm::vec3& target)
|
||||
static float GetTurnAngle2D(const glm::vec2& forward, const glm::vec2& to_target)
|
||||
{
|
||||
glm::vec3 forward = rot * glm::vec3{0.0f, 1.0f, 0.0f};
|
||||
glm::vec2 forward_xy = glm::normalize(glm::vec2{forward.x, forward.y});
|
||||
glm::vec3 to_target = target - pos;
|
||||
glm::vec2 to_target_xy = glm::normalize(glm::vec2{to_target.x, to_target.y});
|
||||
glm::vec2 forward_xy = glm::normalize(forward);
|
||||
glm::vec2 to_target_xy = glm::normalize(to_target);
|
||||
float dot = glm::dot(forward_xy, to_target_xy);
|
||||
float cross = forward_xy.x * to_target_xy.y - forward_xy.y * to_target_xy.x;
|
||||
|
||||
float angle = acosf(glm::clamp(dot, -1.0f, 1.0f)); // in [0, pi]
|
||||
|
||||
if (cross < 0)
|
||||
@ -248,22 +250,130 @@ static float GetSteeringAngle(const glm::vec3& pos, const glm::quat& rot, const
|
||||
return angle; // in [-pi, pi]
|
||||
}
|
||||
|
||||
static float GetTurnAngle(const glm::vec3& pos, const glm::quat& rot, const glm::vec3& target)
|
||||
{
|
||||
glm::vec3 forward = rot * glm::vec3{0.0f, 1.0f, 0.0f};
|
||||
glm::vec3 to_target = target - pos;
|
||||
glm::vec2 forward_xy = glm::vec2{forward.x, forward.y};
|
||||
glm::vec2 to_target_xy = glm::vec2{to_target.x, to_target.y};
|
||||
return GetTurnAngle2D(forward_xy, to_target_xy);
|
||||
}
|
||||
|
||||
static void SelectNextNode(BotThinkState& s)
|
||||
{
|
||||
size_t node = s.path.back();
|
||||
s.path.push_back(s.roads.nbs[s.roads.nodes[node].nbs + (rand() % s.roads.nodes[node].num_nbs)]);
|
||||
}
|
||||
|
||||
static void BotThink(std::shared_ptr<BotThinkState> s)
|
||||
{
|
||||
s->state_str = "path";
|
||||
|
||||
glm::vec3 pos = s->vehicle.GetPosition();
|
||||
glm::quat rot = s->vehicle.GetRotation();
|
||||
glm::vec3 forward = rot * glm::vec3{0.0f, 1.0f, 0.0f};
|
||||
|
||||
glm::vec3 target = s->roads.nodes[s->node].position;
|
||||
float angle = GetSteeringAngle(pos, rot, target);
|
||||
//glm::vec3 target = s->roads.nodes[s->node].position;
|
||||
|
||||
//
|
||||
std::array<glm::vec3, 8> waypoints;
|
||||
|
||||
while (s->path.size() < waypoints.size() - 1)
|
||||
{
|
||||
SelectNextNode(*s);
|
||||
}
|
||||
|
||||
glm::vec3 node_pos = s->roads.nodes[s->path[0]].position;
|
||||
if (glm::distance(glm::vec2(pos), glm::vec2(node_pos)) < 6.0f && s->path.size() > 1)
|
||||
{
|
||||
s->seg_start = node_pos;
|
||||
s->path.pop_front();
|
||||
SelectNextNode(*s);
|
||||
}
|
||||
|
||||
glm::vec3 target_node_pos = s->roads.nodes[s->path[0]].position;
|
||||
|
||||
waypoints[0] = pos - glm::normalize(forward) * 3.0f;
|
||||
waypoints[1] = pos;
|
||||
|
||||
// find closest point on segment [seg_start -> target_node_pos]
|
||||
glm::vec3 seg_end = target_node_pos;
|
||||
glm::vec3 seg_dir = seg_end - s->seg_start;
|
||||
float seg_len = glm::length(seg_dir);
|
||||
if (seg_len > 5.0f)
|
||||
{
|
||||
glm::vec3 seg_dir_norm = seg_dir / seg_len;
|
||||
float t = glm::clamp(glm::dot(pos - s->seg_start, seg_dir_norm) / seg_len, 0.0f, 1.0f);
|
||||
waypoints[2] = s->seg_start + t * seg_dir;
|
||||
if (glm::distance(waypoints[1], target_node_pos) > 10.0f)
|
||||
{
|
||||
waypoints[2] += seg_dir_norm * 10.0f; // look a bit ahead on segment
|
||||
}
|
||||
else
|
||||
{
|
||||
waypoints[2] = target_node_pos;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
waypoints[2] = target_node_pos;
|
||||
}
|
||||
|
||||
for (size_t i = 3; i < waypoints.size(); ++i)
|
||||
{
|
||||
size_t path_idx = glm::min(i - 3, s->path.size() - 1);
|
||||
waypoints[i] = s->roads.nodes[s->path[path_idx]].position;
|
||||
}
|
||||
|
||||
// decrease speed based on curvature
|
||||
const float base_speed = 100.0f;
|
||||
float target_speed = base_speed;
|
||||
float dist_accum = 0.0f;
|
||||
for (size_t i = 1; i < waypoints.size() - 1; ++i)
|
||||
{
|
||||
glm::vec3 dir1 = waypoints[i] - waypoints[i - 1];
|
||||
glm::vec3 dir2 = waypoints[i + 1] - waypoints[i];
|
||||
float dist = glm::length(dir1);
|
||||
dist_accum += dist;
|
||||
|
||||
glm::vec2 dir1_xy = glm::vec2{dir1.x, dir1.y};
|
||||
glm::vec2 dir2_xy = glm::vec2{dir2.x, dir2.y};
|
||||
|
||||
const float min_dir_length = 0.001f;
|
||||
float angle = glm::length(dir1_xy) > min_dir_length && glm::length(dir2_xy) > min_dir_length ? GetTurnAngle2D(dir1_xy, dir2_xy) : 0.0f;
|
||||
// std::cout << "angle: " << angle << "\n";
|
||||
float abs_angle = fabsf(angle);
|
||||
if (abs_angle > glm::radians(7.0f))
|
||||
{
|
||||
// float speed_limit = 50.0f / abs_angle; // sharper turn -> lower speed
|
||||
// speed_limit *= dist_accum / 20.0f; // more distance to turn -> higher speed
|
||||
// speed_limit = glm::max(speed_limit, 20.0f);
|
||||
// max_speed = glm::min(max_speed, speed_limit);
|
||||
target_speed -= abs_angle * (base_speed / glm::pi<float>() / 2.0f) * 50.0f / glm::max(dist_accum - 1.0f, 1.0f);
|
||||
|
||||
}
|
||||
|
||||
if (dist_accum > 200.0f)
|
||||
break;
|
||||
}
|
||||
|
||||
target_speed = glm::clamp(target_speed, 25.0f, 100.0f);
|
||||
s->speed_limit = target_speed;
|
||||
|
||||
// std::cout << "target speed: " << target_speed << "\n";
|
||||
|
||||
float angle = GetTurnAngle(pos, rot, waypoints[2]);
|
||||
|
||||
if (glm::distance(pos, s->last_pos) < 2.0f)
|
||||
{
|
||||
s->stuck_counter++;
|
||||
if (s->stuck_counter > 20)
|
||||
{
|
||||
s->state_str = "stuck (reverse)";
|
||||
s->stuck_counter = 0;
|
||||
s->vehicle.SetSteering(true, -angle); // try turn away
|
||||
|
||||
|
||||
s->vehicle.SetInputs(0); // stop
|
||||
// stuck, go reverse for a while
|
||||
s->vehicle.SetInput(game::VIN_BACKWARD, true);
|
||||
@ -279,18 +389,17 @@ static void BotThink(std::shared_ptr<BotThinkState> s)
|
||||
s->stuck_counter = 0;
|
||||
s->last_pos = pos;
|
||||
}
|
||||
|
||||
|
||||
s->vehicle.SetSteering(true, angle);
|
||||
|
||||
game::VehicleInputFlags vin = 0;
|
||||
|
||||
float speed = s->vehicle.GetSpeed();
|
||||
float target_speed = 50.0f;
|
||||
|
||||
if (glm::distance(pos, target) < 10.0f)
|
||||
{
|
||||
target_speed = 20.0f;
|
||||
}
|
||||
// if (glm::distance(pos, target) < 10.0f)
|
||||
// {
|
||||
// target_speed = 20.0f;
|
||||
// }
|
||||
|
||||
if (speed < target_speed * 0.9f && !s->gas)
|
||||
{
|
||||
@ -313,31 +422,50 @@ static void BotThink(std::shared_ptr<BotThinkState> s)
|
||||
|
||||
s->vehicle.SetInputs(vin);
|
||||
|
||||
float dist_to_node = glm::distance(pos, s->roads.nodes[s->node].position);
|
||||
if (dist_to_node < 5.0f)
|
||||
{
|
||||
// advance to next node
|
||||
const auto& graph = s->roads;
|
||||
const auto& current_node = graph.nodes[s->node];
|
||||
|
||||
if (current_node.num_nbs > 0)
|
||||
{
|
||||
size_t next_idx = rand() % current_node.num_nbs;
|
||||
s->node = s->roads.nbs[current_node.nbs + next_idx];
|
||||
}
|
||||
}
|
||||
|
||||
s->vehicle.Schedule(rand() % 120 + 40, [s]() {
|
||||
BotThink(s);
|
||||
} );
|
||||
}
|
||||
|
||||
static void BotNametagThink(std::shared_ptr<BotThinkState> s)
|
||||
{
|
||||
std::string nametag;
|
||||
nametag += "s=" + std::to_string(static_cast<int>(s->vehicle.GetSpeed())) + "/" + std::to_string(static_cast<int>(s->speed_limit)) + " ";
|
||||
nametag += "sc=" + std::to_string(s->stuck_counter) + " ";
|
||||
nametag += "st=" + std::string(s->state_str); // + " ";
|
||||
|
||||
// nametag += "path=";
|
||||
// for (auto n : path)
|
||||
// {
|
||||
// nametag += std::to_string(n) + " ";
|
||||
// }
|
||||
s->vehicle.SetNametag(nametag);
|
||||
|
||||
s->vehicle.Schedule(240, [s]() {
|
||||
BotNametagThink(s);
|
||||
} );
|
||||
}
|
||||
|
||||
static const char* GetRandomCarModel()
|
||||
{
|
||||
const char* vehicles[] = {"pickup_hd", "passat", "twingo", "polskifiat"};
|
||||
return vehicles[rand() % (sizeof(vehicles) / sizeof(vehicles[0]))];
|
||||
}
|
||||
|
||||
static glm::vec3 GetRandomColor()
|
||||
{
|
||||
glm::vec3 color;
|
||||
// shittiest way to do it
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
net::ColorQ qcol;
|
||||
qcol.value = rand() % 256;
|
||||
color[i] = qcol.Decode();
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
void game::OpenWorld::SpawnBot()
|
||||
{
|
||||
auto roads = GetMap()->GetGraph("roads");
|
||||
@ -349,11 +477,17 @@ void game::OpenWorld::SpawnBot()
|
||||
}
|
||||
|
||||
size_t start_node = rand() % roads->nodes.size();
|
||||
auto& vehicle = Spawn<Vehicle>(GetRandomCarModel(), glm::vec3{0.3f, 0.3f, 0.3f});
|
||||
// auto color = glm::vec3{0.3f, 0.3f, 0.3f};
|
||||
auto color = GetRandomColor();
|
||||
auto& vehicle = Spawn<Vehicle>(GetRandomCarModel(), color);
|
||||
vehicle.SetNametag("bot (" + std::to_string(vehicle.GetEntNum()) + ")");
|
||||
vehicle.SetPosition(roads->nodes[start_node].position + glm::vec3{0.0f, 0.0f, 5.0f});
|
||||
|
||||
auto think_state = std::make_shared<BotThinkState>(vehicle, *roads, start_node);
|
||||
BotThink(think_state);
|
||||
vehicle.Schedule(rand() % 500, [think_state]() {
|
||||
BotNametagThink(think_state);
|
||||
} );
|
||||
}
|
||||
|
||||
void game::OpenWorld::SpawnVehicle(Player& player)
|
||||
@ -362,16 +496,11 @@ void game::OpenWorld::SpawnVehicle(Player& player)
|
||||
|
||||
// spawn him car
|
||||
// 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_name = GetRandomCarModel();
|
||||
auto& vehicle = Spawn<Vehicle>(vehicle_name, color);
|
||||
auto& vehicle = Spawn<Vehicle>(vehicle_name, GetRandomColor());
|
||||
vehicle.SetNametag("player (" + std::to_string(vehicle.GetEntNum()) + ")");
|
||||
vehicle.SetPosition({ 100.0f, 100.0f, 5.0f });
|
||||
|
||||
player.SetCamera(vehicle.GetEntNum());
|
||||
|
||||
@ -108,6 +108,8 @@ void game::Vehicle::Update()
|
||||
|
||||
void game::Vehicle::SendInitData(Player& player, net::OutMessage& msg) const
|
||||
{
|
||||
Super::SendInitData(player, msg);
|
||||
|
||||
net::ModelName name(model_name_);
|
||||
msg.Write(name);
|
||||
net::WriteRGB(msg, color_); // primary color
|
||||
|
||||
20
src/gameview/draw_args.hpp
Normal file
20
src/gameview/draw_args.hpp
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "gfx/draw_list.hpp"
|
||||
#include "gfx/frustum.hpp"
|
||||
|
||||
namespace game::view
|
||||
{
|
||||
struct DrawArgs
|
||||
{
|
||||
gfx::DrawList& dlist;
|
||||
const glm::mat4& view_proj;
|
||||
gfx::Frustum frustum;
|
||||
glm::ivec2 screen_size;
|
||||
|
||||
DrawArgs(gfx::DrawList& dlist, const glm::mat4& view_proj, glm::ivec2 screen_size)
|
||||
: dlist(dlist), view_proj(view_proj), frustum(view_proj), screen_size(screen_size)
|
||||
{
|
||||
}
|
||||
};
|
||||
} // namespace game::view
|
||||
@ -1,11 +1,27 @@
|
||||
#include "entityview.hpp"
|
||||
|
||||
#include "worldview.hpp"
|
||||
#include <iostream>
|
||||
|
||||
game::view::EntityView::EntityView(WorldView& world) : world_(world), audioplayer_(world_.GetAudioMaster()) {}
|
||||
#include "worldview.hpp"
|
||||
#include "assets/cache.hpp"
|
||||
|
||||
game::view::EntityView::EntityView(WorldView& world, net::InMessage& msg) :
|
||||
world_(world),
|
||||
audioplayer_(world_.GetAudioMaster()),
|
||||
nametag_text_(assets::CacheManager::GetFont("data/comic32.font"), 0xFFFFFFFF)
|
||||
{
|
||||
if (!ReadNametag(msg))
|
||||
throw EntityInitError();
|
||||
}
|
||||
|
||||
bool game::view::EntityView::ProcessMsg(net::EntMsgType type, net::InMessage& msg)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case net::EMSG_NAMETAG:
|
||||
return ReadNametag(msg);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -14,4 +30,51 @@ void game::view::EntityView::Update(const UpdateInfo& info)
|
||||
audioplayer_.Update();
|
||||
}
|
||||
|
||||
void game::view::EntityView::Draw(gfx::DrawList& dlist) {}
|
||||
void game::view::EntityView::Draw(const DrawArgs& args)
|
||||
{
|
||||
// std::cout << "TODO draw entity nametag: " << nametag_ << std::endl;
|
||||
|
||||
if (nametag_.empty())
|
||||
return;
|
||||
|
||||
// calc screen position
|
||||
glm::vec4 world_pos = glm::vec4(root_.local.position + glm::vec3(0.0f, 0.0f, 2.0f), 1.0f);
|
||||
glm::vec4 clip_pos = args.view_proj * world_pos;
|
||||
if (clip_pos.w == 0.0f)
|
||||
return;
|
||||
|
||||
glm::vec3 ndc_pos = glm::vec3(clip_pos) / clip_pos.w;
|
||||
|
||||
if (ndc_pos.z < -1.0f || ndc_pos.z > 1.0f)
|
||||
return; // behind camera
|
||||
|
||||
nametag_pos_.anchor.x = ndc_pos.x * 0.5f + 0.5f;
|
||||
nametag_pos_.anchor.y = 1.0f - (ndc_pos.y * 0.5f + 0.5f); // flip y
|
||||
|
||||
// center
|
||||
float scale = 0.7f;
|
||||
nametag_pos_.scale = glm::vec2(scale, -scale);
|
||||
nametag_pos_.pos = nametag_text_.GetSize() * glm::vec2(-0.5f, -1.0f) * scale;
|
||||
|
||||
// static const glm::vec4 nametag_color = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
gfx::DrawHudCmd hudcmd;
|
||||
hudcmd.va = &nametag_text_.GetVA();
|
||||
hudcmd.texture = nametag_text_.GetFont()->GetTexture().get();
|
||||
hudcmd.pos = &nametag_pos_;
|
||||
// hudcmd.color = &nametag_color;
|
||||
args.dlist.AddHUD(hudcmd);
|
||||
// std::cout << "at position: " << root_.local.position.x << ", " << root_.local.position.y << ", " << root_.local.position.z << std::endl;
|
||||
|
||||
}
|
||||
|
||||
bool game::view::EntityView::ReadNametag(net::InMessage& msg)
|
||||
{
|
||||
// read nametag
|
||||
net::NameTag nametag;
|
||||
if (!msg.Read(nametag))
|
||||
return false;
|
||||
|
||||
nametag_ = nametag;
|
||||
nametag_text_.SetText(nametag);
|
||||
}
|
||||
|
||||
@ -2,9 +2,10 @@
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "game/transform_node.hpp"
|
||||
#include "gfx/draw_list.hpp"
|
||||
#include "audio/player.hpp"
|
||||
#include "draw_args.hpp"
|
||||
#include "game/transform_node.hpp"
|
||||
#include "gfx/text.hpp"
|
||||
|
||||
#include "net/defs.hpp"
|
||||
#include "net/inmessage.hpp"
|
||||
@ -14,6 +15,12 @@
|
||||
namespace game::view
|
||||
{
|
||||
|
||||
class EntityInitError : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
explicit EntityInitError() : std::runtime_error("Could not initialize entity from message") {}
|
||||
};
|
||||
|
||||
class WorldView;
|
||||
|
||||
struct UpdateInfo
|
||||
@ -25,24 +32,33 @@ struct UpdateInfo
|
||||
class EntityView
|
||||
{
|
||||
public:
|
||||
EntityView(WorldView& world);
|
||||
EntityView(WorldView& world, net::InMessage& msg);
|
||||
DELETE_COPY_MOVE(EntityView)
|
||||
|
||||
virtual bool ProcessMsg(net::EntMsgType type, net::InMessage& msg);
|
||||
virtual void Update(const UpdateInfo& info);
|
||||
virtual void Draw(gfx::DrawList& dlist);
|
||||
virtual void Draw(const DrawArgs& args);
|
||||
|
||||
Sphere GetBoundingSphere() const { return Sphere{root_.local.position, radius_}; }
|
||||
|
||||
const TransformNode& GetRoot() const { return root_; }
|
||||
|
||||
virtual ~EntityView() = default;
|
||||
|
||||
private:
|
||||
bool ReadNametag(net::InMessage& msg);
|
||||
|
||||
protected:
|
||||
WorldView& world_;
|
||||
|
||||
|
||||
TransformNode root_;
|
||||
bool visible_ = false;
|
||||
|
||||
float radius_ = 1.0f;
|
||||
|
||||
audio::Player audioplayer_;
|
||||
|
||||
std::string nametag_;
|
||||
gfx::Text nametag_text_;
|
||||
gfx::HudPosition nametag_pos_;
|
||||
};
|
||||
|
||||
} // namespace game::view
|
||||
@ -6,9 +6,19 @@
|
||||
|
||||
#include <iostream>
|
||||
|
||||
game::view::VehicleView::VehicleView(WorldView& world, std::shared_ptr<const assets::VehicleModel> model, const glm::vec3& color)
|
||||
: EntityView(world), model_(std::move(model))
|
||||
game::view::VehicleView::VehicleView(WorldView& world, net::InMessage& msg)
|
||||
: EntityView(world, msg)
|
||||
{
|
||||
net::ModelName modelname;
|
||||
glm::vec3 color;
|
||||
if (!msg.Read(modelname) || !net::ReadRGB(msg, color))
|
||||
throw EntityInitError();
|
||||
|
||||
model_ = assets::CacheManager::GetVehicleModel("data/" + std::string(modelname) + ".veh");
|
||||
|
||||
// init the other transform to identical
|
||||
root_trans_[0] = root_trans_[1];
|
||||
|
||||
auto& modelwheels = model_->GetWheels();
|
||||
wheels_.resize(modelwheels.size());
|
||||
|
||||
@ -19,30 +29,17 @@ game::view::VehicleView::VehicleView(WorldView& world, std::shared_ptr<const ass
|
||||
|
||||
color_ = glm::vec4(color, 1.0f);
|
||||
|
||||
if (!ReadState(msg))
|
||||
throw EntityInitError();
|
||||
|
||||
snd_accel_ = assets::CacheManager::GetSound("data/auto.snd");
|
||||
|
||||
// sync state
|
||||
net::DecodePosition(sync_.pos, root_.local.position);
|
||||
net::DecodeRotation(sync_.rot, root_.local.rotation);
|
||||
|
||||
}
|
||||
|
||||
std::unique_ptr<game::view::VehicleView> game::view::VehicleView::InitFromMsg(WorldView& world, net::InMessage& msg)
|
||||
{
|
||||
net::ModelName modelname;
|
||||
glm::vec3 color;
|
||||
if (!msg.Read(modelname) || !net::ReadRGB(msg, color))
|
||||
return nullptr;
|
||||
|
||||
auto model = assets::CacheManager::GetVehicleModel("data/" + std::string(modelname) + ".veh");
|
||||
|
||||
auto vehicle = std::make_unique<VehicleView>(world, std::move(model), color);
|
||||
if (!vehicle->ReadState(msg))
|
||||
return nullptr;
|
||||
|
||||
vehicle->root_trans_[0] = vehicle->root_trans_[1];
|
||||
|
||||
return vehicle;
|
||||
radius_ = 3.0f;
|
||||
}
|
||||
|
||||
bool game::view::VehicleView::ProcessMsg(net::EntMsgType type, net::InMessage& msg)
|
||||
@ -53,7 +50,7 @@ bool game::view::VehicleView::ProcessMsg(net::EntMsgType type, net::InMessage& m
|
||||
return ProcessUpdateMsg(msg);
|
||||
|
||||
default:
|
||||
return false;
|
||||
return Super::ProcessMsg(type, msg);
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,8 +103,10 @@ void game::view::VehicleView::Update(const UpdateInfo& info)
|
||||
}
|
||||
}
|
||||
|
||||
void game::view::VehicleView::Draw(gfx::DrawList& dlist)
|
||||
void game::view::VehicleView::Draw(const DrawArgs& args)
|
||||
{
|
||||
Super::Draw(args);
|
||||
|
||||
// TOOD: chceck and fix
|
||||
const auto& model = *model_->GetModel();
|
||||
const auto& mesh = *model.GetMesh();
|
||||
@ -118,7 +117,7 @@ void game::view::VehicleView::Draw(gfx::DrawList& dlist)
|
||||
cmd.surface = &surface;
|
||||
cmd.matrices = &root_.matrix;
|
||||
cmd.color = &color_;
|
||||
dlist.AddSurface(cmd);
|
||||
args.dlist.AddSurface(cmd);
|
||||
}
|
||||
|
||||
const auto& wheels = model_->GetWheels();
|
||||
@ -131,7 +130,7 @@ void game::view::VehicleView::Draw(gfx::DrawList& dlist)
|
||||
gfx::DrawSurfaceCmd cmd;
|
||||
cmd.surface = &surface;
|
||||
cmd.matrices = &wheels_[i].node.matrix;
|
||||
dlist.AddSurface(cmd);
|
||||
args.dlist.AddSurface(cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,12 +23,12 @@ class VehicleView : public EntityView
|
||||
{
|
||||
using Super = EntityView;
|
||||
public:
|
||||
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);
|
||||
VehicleView(WorldView& world, net::InMessage& msg);
|
||||
DELETE_COPY_MOVE(VehicleView)
|
||||
|
||||
virtual bool ProcessMsg(net::EntMsgType type, net::InMessage& msg) override;
|
||||
virtual void Update(const UpdateInfo& info) override;
|
||||
virtual void Draw(gfx::DrawList& dlist) override;
|
||||
virtual void Draw(const DrawArgs& args) override;
|
||||
|
||||
private:
|
||||
bool ReadState(net::InMessage& msg);
|
||||
|
||||
@ -40,14 +40,15 @@ void game::view::WorldView::Update(const UpdateInfo& info)
|
||||
}
|
||||
}
|
||||
|
||||
void game::view::WorldView::Draw(gfx::DrawList& dlist) const
|
||||
void game::view::WorldView::Draw(const DrawArgs& args) const
|
||||
{
|
||||
if (map_)
|
||||
map_->Draw(dlist);
|
||||
map_->Draw(args);
|
||||
|
||||
for (const auto& [entnum, ent] : ents_)
|
||||
{
|
||||
ent->Draw(dlist);
|
||||
if (args.frustum.IsSphereVisible(ent->GetBoundingSphere()))
|
||||
ent->Draw(args);
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,23 +73,25 @@ bool game::view::WorldView::ProcessEntSpawnMsg(net::InMessage& msg)
|
||||
if (entslot)
|
||||
entslot.reset();
|
||||
|
||||
switch (type)
|
||||
try
|
||||
{
|
||||
case net::ET_VEHICLE:
|
||||
entslot = VehicleView::InitFromMsg(*this, msg);
|
||||
break;
|
||||
switch (type)
|
||||
{
|
||||
case net::ET_VEHICLE:
|
||||
entslot = std::make_unique<VehicleView>(*this, msg);
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
return false; // unknown type
|
||||
}
|
||||
|
||||
if (!entslot) // init failed
|
||||
return true;
|
||||
|
||||
} catch (const EntityInitError& e) // failed
|
||||
{
|
||||
ents_.erase(entnum);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool game::view::WorldView::ProcessEntMsgMsg(net::InMessage& msg)
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "assets/map.hpp"
|
||||
#include "gfx/draw_list.hpp"
|
||||
#include "draw_args.hpp"
|
||||
#include "net/defs.hpp"
|
||||
#include "net/inmessage.hpp"
|
||||
|
||||
@ -20,7 +20,7 @@ public:
|
||||
bool ProcessMsg(net::MessageType type, net::InMessage& msg);
|
||||
|
||||
void Update(const UpdateInfo& info);
|
||||
void Draw(gfx::DrawList& dlist) const;
|
||||
void Draw(const DrawArgs& args) const;
|
||||
|
||||
EntityView* GetEntity(net::EntNum entnum);
|
||||
|
||||
|
||||
@ -22,7 +22,7 @@ void gfx::BufferObject::SetData(const void* data, size_t size) {
|
||||
|
||||
if (size > m_size)
|
||||
glBufferData(m_target, size, data, m_usage);
|
||||
else
|
||||
else if (size > 0)
|
||||
glBufferSubData(m_target, 0, size, data);
|
||||
|
||||
m_size = size;
|
||||
|
||||
79
src/gfx/frustum.cpp
Normal file
79
src/gfx/frustum.cpp
Normal file
@ -0,0 +1,79 @@
|
||||
#include "frustum.hpp"
|
||||
|
||||
gfx::Frustum::Frustum(const glm::mat4& vp)
|
||||
{
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
planes_[0][i] = vp[i][3] + vp[i][0];
|
||||
planes_[1][i] = vp[i][3] - vp[i][0];
|
||||
planes_[2][i] = vp[i][3] + vp[i][1];
|
||||
planes_[3][i] = vp[i][3] - vp[i][1];
|
||||
planes_[4][i] = vp[i][3] + vp[i][2];
|
||||
planes_[5][i] = vp[i][3] - vp[i][2];
|
||||
}
|
||||
|
||||
// normalize the planes
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
auto len = glm::length(glm::vec3(planes_[i]));
|
||||
planes_[i] /= len;
|
||||
}
|
||||
|
||||
//glm::mat4 inv_vp = glm::inverse(m_vp);
|
||||
|
||||
//m_min = glm::vec3(FLT_MAX);
|
||||
//m_max = glm::vec3(-FLT_MAX);
|
||||
|
||||
//for (int x = 0; x < 2; ++x)
|
||||
//{
|
||||
// for (int y = 0; y < 2; ++y)
|
||||
// {
|
||||
// for (int z = 0; z < 2; ++z)
|
||||
// {
|
||||
// glm::vec4 pt = inv_vp * glm::vec4(2.0f * x - 1.0f, 2.0f * y - 1.0f, 2.0f * z - 1.0f, 1.0f);
|
||||
|
||||
// pt /= pt.w;
|
||||
|
||||
// m_min = glm::min(m_min, glm::vec3(pt));
|
||||
// m_max = glm::max(m_max, glm::vec3(pt));
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
bool gfx::Frustum::IsAABBVisible(const AABB3& aabb) const
|
||||
{
|
||||
glm::vec3 extents = (aabb.max - aabb.min) * 0.5f;
|
||||
glm::vec3 center = aabb.min + extents;
|
||||
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
const auto& plane = planes_[i];
|
||||
const glm::vec3 normal(plane);
|
||||
|
||||
const float r = glm::dot(extents, glm::abs(normal));
|
||||
const float d = glm::dot(normal, center) + plane.w;
|
||||
|
||||
if (d + r < 0.0f)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gfx::Frustum::IsSphereVisible(const Sphere& sphere) const
|
||||
{
|
||||
for (int i = 0; i < 6; ++i)
|
||||
{
|
||||
const glm::vec4 plane = planes_[i];
|
||||
const glm::vec3 normal(plane);
|
||||
|
||||
const float distance = glm::dot(normal, sphere.center) + plane.w;
|
||||
|
||||
if (distance < -sphere.radius)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
22
src/gfx/frustum.hpp
Normal file
22
src/gfx/frustum.hpp
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include "utils/aabb.hpp"
|
||||
#include "utils/sphere.hpp"
|
||||
|
||||
namespace gfx
|
||||
{
|
||||
|
||||
class Frustum
|
||||
{
|
||||
public:
|
||||
Frustum(const glm::mat4& vp);
|
||||
|
||||
bool IsAABBVisible(const AABB3& aabb) const;
|
||||
bool IsSphereVisible(const Sphere& sphere) const;
|
||||
|
||||
private:
|
||||
glm::vec4 planes_[6];
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
@ -78,13 +78,16 @@ void gfx::Text::SetText(const char* text)
|
||||
|
||||
float space_size = font_->GetLineHeight() * 0.3f;
|
||||
|
||||
glm::vec2 cursor(0.0f, 0.0f);
|
||||
const float line_height = font_->GetLineHeight();
|
||||
size_ = glm::vec2(0.0f);
|
||||
glm::vec2 cursor(0.0f);
|
||||
|
||||
uint32_t cp = 0;
|
||||
const char* p = text;
|
||||
|
||||
uint32_t color = color_;
|
||||
|
||||
|
||||
while (cp = DecodeUTF8Codepoint(p))
|
||||
{
|
||||
if (cp == ' ')
|
||||
@ -92,6 +95,12 @@ void gfx::Text::SetText(const char* text)
|
||||
cursor.x += space_size; // Move cursor for space
|
||||
continue;
|
||||
}
|
||||
else if (cp == '\n')
|
||||
{
|
||||
cursor.x = 0.0f;
|
||||
cursor.y += line_height;
|
||||
continue;
|
||||
}
|
||||
else if (cp == '^')
|
||||
{
|
||||
if (!(cp = DecodeUTF8Codepoint(p)))
|
||||
@ -155,7 +164,11 @@ void gfx::Text::SetText(const char* text)
|
||||
indices.push_back(base_index + 3);
|
||||
|
||||
cursor.x += glyph->advance;
|
||||
|
||||
size_.x = glm::max(size_.x, cursor.x);
|
||||
}
|
||||
|
||||
size_.y = cursor.y + line_height;
|
||||
|
||||
va_.SetVBOData(vertices.data(), vertices.size() * sizeof(TextVertex));
|
||||
va_.SetIndices(indices.data(), indices.size());
|
||||
|
||||
@ -16,11 +16,13 @@ public:
|
||||
|
||||
const std::shared_ptr<const Font>& GetFont() const { return font_; }
|
||||
const VertexArray& GetVA() const { return va_; }
|
||||
const glm::vec2& GetSize() const { return size_; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<const Font> font_;
|
||||
VertexArray va_;
|
||||
uint32_t color_;
|
||||
glm::vec2 size_; // size of the text in pixels
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
@ -71,6 +71,7 @@ enum EntMsgType : uint8_t
|
||||
{
|
||||
EMSG_NONE,
|
||||
|
||||
EMSG_NAMETAG,
|
||||
EMSG_UPDATE,
|
||||
};
|
||||
|
||||
@ -93,4 +94,6 @@ using RotationSpeedQ = Quantized<uint16_t, -300, 300, 1>;
|
||||
|
||||
using ColorQ = Quantized<uint8_t, 0, 1>;
|
||||
|
||||
using NameTag = FixedStr<64>;
|
||||
|
||||
} // namespace net
|
||||
@ -35,7 +35,7 @@ void sv::Server::Run()
|
||||
{
|
||||
time_ += 40;
|
||||
Update();
|
||||
std::cout << "Time: " << time_ << " ms, Clients: " << clients_.size() << std::endl;
|
||||
// std::cout << "Time: " << time_ << " ms, Clients: " << clients_.size() << std::endl;
|
||||
t_next += 40ms;
|
||||
t_now = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
49
src/utils/aabb.hpp
Normal file
49
src/utils/aabb.hpp
Normal file
@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <limits>
|
||||
|
||||
template <size_t dim>
|
||||
struct AABB
|
||||
{
|
||||
static constexpr float C_INFINITY = std::numeric_limits<float>::infinity();
|
||||
|
||||
using Vec = glm::vec<dim, float, glm::defaultp>;
|
||||
|
||||
Vec min = Vec(C_INFINITY);
|
||||
Vec max = Vec(-C_INFINITY);
|
||||
|
||||
AABB() = default;
|
||||
AABB(const Vec& min, const Vec& max) : min(min), max(max) {}
|
||||
|
||||
void AddPoint(const Vec& point)
|
||||
{
|
||||
min = glm::min(min, point);
|
||||
max = glm::max(max, point);
|
||||
}
|
||||
|
||||
bool CollidesWith(const AABB<dim>& other) const;
|
||||
|
||||
AABB<dim> Intersection(const AABB<dim>& other) const
|
||||
{
|
||||
Vec new_min = glm::max(min, other.min);
|
||||
Vec new_max = glm::min(max, other.max);
|
||||
return AABB<dim>(new_min, new_max);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
inline bool AABB<2>::CollidesWith(const AABB<2>& other) const
|
||||
{
|
||||
return (min.x <= other.max.x && max.x >= other.min.x) && (min.y <= other.max.y && max.y >= other.min.y);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool AABB<3>::CollidesWith(const AABB<3>& other) const
|
||||
{
|
||||
return (min.x <= other.max.x && max.x >= other.min.x) && (min.y <= other.max.y && max.y >= other.min.y) &&
|
||||
(min.z <= other.max.z && max.z >= other.min.z);
|
||||
}
|
||||
|
||||
using AABB2 = AABB<2>;
|
||||
using AABB3 = AABB<3>;
|
||||
9
src/utils/sphere.hpp
Normal file
9
src/utils/sphere.hpp
Normal file
@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
struct Sphere
|
||||
{
|
||||
glm::vec3 center;
|
||||
float radius;
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user