Proper AABBs of chunks&objs, camera sweep test

This commit is contained in:
tovjemam 2026-02-12 23:15:35 +01:00
parent 698bcafec0
commit fd3f981ec0
16 changed files with 163 additions and 87 deletions

View File

@ -9,6 +9,26 @@
#include "cmdfile.hpp" #include "cmdfile.hpp"
#include "utils/files.hpp" #include "utils/files.hpp"
static AABB3 TransformAABB(const AABB3& aabb, const glm::mat4& mat)
{
const glm::vec3 corners[] = {
glm::vec3(aabb.min.x, aabb.min.y, aabb.min.z), glm::vec3(aabb.max.x, aabb.min.y, aabb.min.z),
glm::vec3(aabb.min.x, aabb.max.y, aabb.min.z), glm::vec3(aabb.max.x, aabb.max.y, aabb.min.z),
glm::vec3(aabb.min.x, aabb.min.y, aabb.max.z), glm::vec3(aabb.max.x, aabb.min.y, aabb.max.z),
glm::vec3(aabb.min.x, aabb.max.y, aabb.max.z), glm::vec3(aabb.max.x, aabb.max.y, aabb.max.z),
};
AABB3 new_aabb;
for (size_t i = 0; i < 8; ++i)
{
glm::vec3 p = mat * glm::vec4(corners[i], 1.0f);
new_aabb.AddPoint(p);
}
return new_aabb;
}
std::shared_ptr<const assets::Map> assets::Map::LoadFromFile(const std::string& filename) std::shared_ptr<const assets::Map> assets::Map::LoadFromFile(const std::string& filename)
{ {
auto map = std::make_shared<Map>(); auto map = std::make_shared<Map>();
@ -58,8 +78,8 @@ std::shared_ptr<const assets::Map> assets::Map::LoadFromFile(const std::string&
obj.node.UpdateMatrix(); obj.node.UpdateMatrix();
obj.aabb.min = trans.position - glm::vec3(10.0f); obj.aabb = TransformAABB(obj.model->GetAABB(), obj.node.matrix);
obj.aabb.max = trans.position + glm::vec3(10.0f); chunk->aabb.AddAABB(obj.aabb);
std::string flag; std::string flag;
while (iss >> flag) while (iss >> flag)
@ -79,7 +99,6 @@ std::shared_ptr<const assets::Map> assets::Map::LoadFromFile(const std::string&
iss >> coord.x >> coord.y; iss >> coord.x >> coord.y;
iss >> chunk->aabb.min.x >> chunk->aabb.min.y >> chunk->aabb.min.z; 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; iss >> chunk->aabb.max.x >> chunk->aabb.max.y >> chunk->aabb.max.z;
} }
else if (command == "surface") else if (command == "surface")
{ {
@ -181,7 +200,6 @@ void assets::Map::Draw(const game::view::DrawArgs& args) const
DrawChunk(args, mesh, chunk); DrawChunk(args, mesh, chunk);
} }
} }
void assets::Map::DrawChunk(const game::view::DrawArgs& args, const Mesh& basemesh, const Chunk& chunk) const void assets::Map::DrawChunk(const game::view::DrawArgs& args, const Mesh& basemesh, const Chunk& chunk) const
@ -219,4 +237,3 @@ void assets::Map::DrawChunk(const game::view::DrawArgs& args, const Mesh& baseme
} }
#endif // CLIENT #endif // CLIENT

View File

@ -45,6 +45,8 @@ std::shared_ptr<const assets::Model> assets::Model::LoadFromFile(const std::stri
if (temp_hull) if (temp_hull)
temp_hull->addPoint(btVector3(pos.x, pos.y, pos.z), false); temp_hull->addPoint(btVector3(pos.x, pos.y, pos.z), false);
model->aabb_.AddPoint(pos);
} }
else if (command == "f") else if (command == "f")
{ {

View File

@ -5,6 +5,7 @@
#include "skeleton.hpp" #include "skeleton.hpp"
#include "utils/defs.hpp" #include "utils/defs.hpp"
#include "utils/aabb.hpp"
#include "collision/trianglemesh.hpp" #include "collision/trianglemesh.hpp"
#ifdef CLIENT #ifdef CLIENT
@ -45,6 +46,7 @@ public:
const std::shared_ptr<const Skeleton>& GetSkeleton() const { return skeleton_; } const std::shared_ptr<const Skeleton>& GetSkeleton() const { return skeleton_; }
CLIENT_ONLY(const std::shared_ptr<const Mesh>& GetMesh() const { return mesh_; }) CLIENT_ONLY(const std::shared_ptr<const Mesh>& GetMesh() const { return mesh_; })
const AABB3& GetAABB() const { return aabb_; }
private: private:
std::unique_ptr<collision::TriangleMesh> cmesh_; std::unique_ptr<collision::TriangleMesh> cmesh_;
@ -52,7 +54,8 @@ private:
std::unique_ptr<btCollisionShape> cshape_; std::unique_ptr<btCollisionShape> cshape_;
std::shared_ptr<const Skeleton> skeleton_; std::shared_ptr<const Skeleton> skeleton_;
CLIENT_ONLY(std::shared_ptr<const Mesh> mesh_;) CLIENT_ONLY(std::shared_ptr<const Mesh> mesh_;);
AABB3 aabb_;
}; };

View File

@ -66,7 +66,6 @@ void App::Frame()
session_->Update(updinfo); session_->Update(updinfo);
} }
float aspect = static_cast<float>(viewport_size_.x) / static_cast<float>(viewport_size_.y);
renderer_.Begin(viewport_size_.x, viewport_size_.y); renderer_.Begin(viewport_size_.x, viewport_size_.y);
renderer_.ClearColor(glm::vec3(0.5f, 0.7f, 1.0f)); renderer_.ClearColor(glm::vec3(0.5f, 0.7f, 1.0f));
@ -78,43 +77,11 @@ void App::Frame()
params.screen_height = viewport_size_.y; params.screen_height = viewport_size_.y;
const game::view::WorldView* world; const game::view::WorldView* world;
if (session_ && (world = session_->GetWorld())) if (session_)
{ {
// 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)); session_->Draw(dlist_, params);
glm::mat4 proj = glm::perspective(glm::radians(45.0f), aspect, 0.1f, 3000.0f);
glm::vec3 eye;
glm::mat4 view;
session_->GetViewInfo(eye, view);
params.view_proj = proj * view;
params.cam_pos = eye;
game::view::DrawArgs draw_args(dlist_, params.view_proj, eye, viewport_size_, 500.0f);
world->Draw(draw_args);
glm::mat4 camera_world = glm::inverse(view);
audiomaster_.SetListenerOrientation(camera_world);
if (time_ - last_send_time_ > 0.040f)
{
net::ViewYawQ yaw_q;
net::ViewPitchQ pitch_q;
yaw_q.Encode(session_->GetYaw());
pitch_q.Encode(session_->GetPitch());
if (yaw_q.value != view_yaw_q_.value || pitch_q.value != view_pitch_q_.value)
{
auto msg = BeginMsg(net::MSG_VIEWANGLES);
msg.Write(yaw_q.value);
msg.Write(pitch_q.value);
view_yaw_q_.value = yaw_q.value;
view_pitch_q_.value = pitch_q.value;
last_send_time_ = time_;
}
}
} }
// draw chat // draw chat
UpdateChat(); UpdateChat();
DrawChat(dlist_); DrawChat(dlist_);

View File

@ -36,7 +36,7 @@ public:
void SetInput(game::PlayerInputFlags input) { input_ = input; } void SetInput(game::PlayerInputFlags input) { input_ = input; }
void MouseMove(const glm::vec2& delta); void MouseMove(const glm::vec2& delta);
float GetTime() const { return delta_time_; } float GetTime() const { return time_; }
float GetDeltaTime() const { return delta_time_; } float GetDeltaTime() const { return delta_time_; }
audio::Master& GetAudioMaster() { return audiomaster_; } audio::Master& GetAudioMaster() { return audiomaster_; }
@ -55,12 +55,10 @@ private:
private: private:
float time_ = 0.0f; float time_ = 0.0f;
float last_send_time_ = 0.0f;
glm::ivec2 viewport_size_ = {800, 600}; glm::ivec2 viewport_size_ = {800, 600};
game::PlayerInputFlags input_ = 0; game::PlayerInputFlags input_ = 0;
game::PlayerInputFlags prev_input_ = 0; game::PlayerInputFlags prev_input_ = 0;
net::ViewYawQ view_yaw_q_;
net::ViewPitchQ view_pitch_q_;
float prev_time_ = 0.0f; float prev_time_ = 0.0f;
float delta_time_ = 0.0f; float delta_time_ = 0.0f;

View File

@ -2,34 +2,22 @@
#include <stdexcept> #include <stdexcept>
collision::DynamicsWorld::DynamicsWorld(std::shared_ptr<const assets::Map> map) collision::DynamicsWorld::DynamicsWorld()
: map_(std::move(map)), bt_dispatcher_(&bt_cfg_), : bt_dispatcher_(&bt_cfg_),
bt_world_(&bt_dispatcher_, &bt_broadphase_, &bt_solver_, &bt_cfg_), bt_veh_raycaster_(&bt_world_) bt_world_(&bt_dispatcher_, &bt_broadphase_, &bt_solver_, &bt_cfg_), bt_veh_raycaster_(&bt_world_)
{ {
bt_world_.setGravity(btVector3(0, 0, -9.81f)); bt_world_.setGravity(btVector3(0, 0, -9.81f));
bt_broadphase_.getOverlappingPairCache()->setInternalGhostPairCallback(&bt_ghost_pair_cb_); bt_broadphase_.getOverlappingPairCache()->setInternalGhostPairCallback(&bt_ghost_pair_cb_);
AddMapCollision();
// btTransform t;
// t.setIdentity();
// t.setOrigin(btVector3(0,0,-12));
// TODO: remove
// static btDefaultMotionState motion(t);
// static btBoxShape box(btVector3(100, 100, 2));
// btRigidBody::btRigidBodyConstructionInfo rbInfo(0.0f, &motion, &box, btVector3(0,0,0));
// static btRigidBody body(rbInfo);
// bt_world_.addRigidBody(&body);
} }
void collision::DynamicsWorld::AddMapCollision() void collision::DynamicsWorld::AddMapCollision(std::shared_ptr<const assets::Map> map)
{ {
if (!map_) // is perfectly possible that there is no map in this world if (!map)
return; return;
map_ = std::move(map);
// add basemodel // add basemodel
const auto& basemodel = map_->GetBaseModel(); const auto& basemodel = map_->GetBaseModel();
if (basemodel) if (basemodel)

View File

@ -25,16 +25,15 @@ namespace collision
class DynamicsWorld class DynamicsWorld
{ {
public: public:
DynamicsWorld(std::shared_ptr<const assets::Map> map); DynamicsWorld();
void AddMapCollision(std::shared_ptr<const assets::Map> map);
btDynamicsWorld& GetBtWorld() { return bt_world_; } btDynamicsWorld& GetBtWorld() { return bt_world_; }
const btDynamicsWorld& GetBtWorld() const { return bt_world_; } const btDynamicsWorld& GetBtWorld() const { return bt_world_; }
btVehicleRaycaster& GetVehicleRaycaster() { return bt_veh_raycaster_; } btVehicleRaycaster& GetVehicleRaycaster() { return bt_veh_raycaster_; }
const std::shared_ptr<const assets::Map>& GetMap() const { return map_; }
private: private:
void AddMapCollision();
void AddModelInstance(const assets::Model& model, const Transform& trans); void AddModelInstance(const assets::Model& model, const Transform& trans);
private: private:

View File

@ -552,7 +552,7 @@ static const char* GetRandomCarModel()
void game::OpenWorld::SpawnBot() void game::OpenWorld::SpawnBot()
{ {
auto roads = GetMap()->GetGraph("roads"); auto roads = GetMap().GetGraph("roads");
if (!roads) if (!roads)
{ {

View File

@ -5,13 +5,12 @@
#include "assets/cache.hpp" #include "assets/cache.hpp"
#include "utils/allocnum.hpp" #include "utils/allocnum.hpp"
static std::shared_ptr<const assets::Map> LoadMapByName(const std::string& mapname) game::World::World(std::string mapname) : mapname_(std::move(mapname))
{ {
return assets::CacheManager::GetMap("data/" + mapname + ".map"); map_ = assets::CacheManager::GetMap("data/" + mapname_ + ".map");
AddMapCollision(map_);
} }
game::World::World(std::string mapname) : DynamicsWorld(LoadMapByName(mapname)), mapname_(std::move(mapname)) {}
net::EntNum game::World::GetNewEntnum() net::EntNum game::World::GetNewEntnum()
{ {
auto entnum = utils::AllocNum(ents_, last_entnum_); auto entnum = utils::AllocNum(ents_, last_entnum_);

View File

@ -46,7 +46,11 @@ public:
virtual ~World() = default; virtual ~World() = default;
protected:
const assets::Map& GetMap() const { return *map_; }
private: private:
std::shared_ptr<const assets::Map> map_;
std::string mapname_; std::string mapname_;
std::map<net::EntNum, std::unique_ptr<Entity>> ents_; std::map<net::EntNum, std::unique_ptr<Entity>> ents_;
net::EntNum last_entnum_ = 0; net::EntNum last_entnum_ = 0;

View File

@ -35,6 +35,8 @@ game::view::CharacterView::CharacterView(WorldView& world, net::InMessage& msg)
states_[0] = states_[1]; // lerp from the read state to avoid jump states_[0] = states_[1]; // lerp from the read state to avoid jump
radius_ = 2.0f;
} }
bool game::view::CharacterView::ProcessMsg(net::EntMsgType type, net::InMessage& msg) bool game::view::CharacterView::ProcessMsg(net::EntMsgType type, net::InMessage& msg)

View File

@ -64,18 +64,29 @@ void game::view::ClientSession::ProcessMouseMove(float delta_yaw, float delta_pi
void game::view::ClientSession::Update(const UpdateInfo& info) void game::view::ClientSession::Update(const UpdateInfo& info)
{ {
if (world_) if (world_)
{
world_->Update(info); world_->Update(info);
SendViewAngles(info.time);
}
}
void game::view::ClientSession::Draw(gfx::DrawList& dlist, gfx::DrawListParams& params)
{
if (world_)
{
DrawWorld(dlist, params);
}
} }
void game::view::ClientSession::GetViewInfo(glm::vec3& eye, glm::mat4& view) const void game::view::ClientSession::GetViewInfo(glm::vec3& eye, glm::mat4& view) const
{ {
glm::vec3 center(0.0f, 0.0f, 2.5f); glm::vec3 start(0.0f, 0.0f, 2.0f);
if (world_ && follow_ent_) if (follow_ent_)
{ {
auto ent = world_->GetEntity(follow_ent_); auto ent = world_->GetEntity(follow_ent_);
if (ent) if (ent)
center += ent->GetRoot().local.position; start += ent->GetRoot().local.position;
} }
float yaw_cos = glm::cos(yaw_); float yaw_cos = glm::cos(yaw_);
@ -84,10 +95,12 @@ void game::view::ClientSession::GetViewInfo(glm::vec3& eye, glm::mat4& view) con
float pitch_sin = glm::sin(pitch_); float pitch_sin = glm::sin(pitch_);
glm::vec3 dir(yaw_cos * pitch_cos, yaw_sin * pitch_cos, pitch_sin); glm::vec3 dir(yaw_cos * pitch_cos, yaw_sin * pitch_cos, pitch_sin);
float distance = 8.0f; float distance = 5.0f;
glm::vec3 end = start - dir * distance;
eye = center - dir * distance; //start.z -= 0.5f; // shift this a bit to make it better when occluded
view = glm::lookAt(eye, center, glm::vec3(0, 0, 1)); eye = world_->CameraSweep(start, end);
view = glm::lookAt(eye, eye + dir, glm::vec3(0, 0, 1));
} }
audio::Master& game::view::ClientSession::GetAudioMaster() const audio::Master& game::view::ClientSession::GetAudioMaster() const
@ -124,3 +137,49 @@ bool game::view::ClientSession::ProcessChatMsg(net::InMessage& msg)
app_.AddChatMessagePrefix("Server", chatm); app_.AddChatMessagePrefix("Server", chatm);
return true; return true;
} }
void game::view::ClientSession::DrawWorld(gfx::DrawList& dlist, gfx::DrawListParams& params)
{
// 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));
float aspect = static_cast<float>(params.screen_width) / static_cast<float>(params.screen_height);
glm::mat4 proj = glm::perspective(glm::radians(45.0f), aspect, 0.1f, 3000.0f);
glm::vec3 eye;
glm::mat4 view;
GetViewInfo(eye, view);
params.view_proj = proj * view;
params.cam_pos = eye;
// glm::mat4 fake_view_proj = glm::perspective(glm::radians(30.0f), aspect, 0.1f, 3000.0f) * view;
game::view::DrawArgs draw_args(dlist, params.view_proj, eye, glm::ivec2(params.screen_width, params.screen_height),
500.0f);
world_->Draw(draw_args);
glm::mat4 camera_world = glm::inverse(view);
GetAudioMaster().SetListenerOrientation(camera_world);
}
void game::view::ClientSession::SendViewAngles(float time)
{
if (time - last_send_time_ < 0.040f)
return;
net::ViewYawQ yaw_q;
net::ViewPitchQ pitch_q;
yaw_q.Encode(yaw_);
pitch_q.Encode(pitch_);
if (yaw_q.value == view_yaw_q_.value && pitch_q.value == view_pitch_q_.value)
return;
auto msg = app_.BeginMsg(net::MSG_VIEWANGLES);
msg.Write(yaw_q.value);
msg.Write(pitch_q.value);
view_yaw_q_.value = yaw_q.value;
view_pitch_q_.value = pitch_q.value;
last_send_time_ = time;
}

View File

@ -5,6 +5,7 @@
#include "worldview.hpp" #include "worldview.hpp"
#include "gfx/draw_list.hpp" #include "gfx/draw_list.hpp"
#include "gfx/renderer.hpp"
#include "net/defs.hpp" #include "net/defs.hpp"
#include "net/inmessage.hpp" #include "net/inmessage.hpp"
@ -24,6 +25,7 @@ public:
void ProcessMouseMove(float delta_yaw, float delta_pitch); void ProcessMouseMove(float delta_yaw, float delta_pitch);
void Update(const UpdateInfo& info); void Update(const UpdateInfo& info);
void Draw(gfx::DrawList& dlist, gfx::DrawListParams& params);
const WorldView* GetWorld() const { return world_.get(); } const WorldView* GetWorld() const { return world_.get(); }
@ -31,15 +33,15 @@ public:
audio::Master& GetAudioMaster() const; audio::Master& GetAudioMaster() const;
float GetYaw() const { return yaw_; }
float GetPitch() const { return pitch_; }
private: private:
// msg handlers // msg handlers
bool ProcessWorldMsg(net::InMessage& msg); bool ProcessWorldMsg(net::InMessage& msg);
bool ProcessCameraMsg(net::InMessage& msg); bool ProcessCameraMsg(net::InMessage& msg);
bool ProcessChatMsg(net::InMessage& msg); bool ProcessChatMsg(net::InMessage& msg);
void DrawWorld(gfx::DrawList& dlist, gfx::DrawListParams& params);
void SendViewAngles(float time);
private: private:
App& app_; App& app_;
@ -48,6 +50,9 @@ private:
float yaw_ = 0.0f, pitch_ = 0.0f; float yaw_ = 0.0f, pitch_ = 0.0f;
net::EntNum follow_ent_ = 0; net::EntNum follow_ent_ = 0;
net::ViewYawQ view_yaw_q_;
net::ViewPitchQ view_pitch_q_;
float last_send_time_ = 0.0f;
}; };
} // namespace game::view } // namespace game::view

View File

@ -11,6 +11,7 @@ game::view::WorldView::WorldView(ClientSession& session) :
audiomaster_(session_.GetAudioMaster()) audiomaster_(session_.GetAudioMaster())
{ {
map_ = assets::CacheManager::GetMap("data/openworld.map"); map_ = assets::CacheManager::GetMap("data/openworld.map");
AddMapCollision(map_);
} }
bool game::view::WorldView::ProcessMsg(net::MessageType type, net::InMessage& msg) bool game::view::WorldView::ProcessMsg(net::MessageType type, net::InMessage& msg)
@ -53,6 +54,31 @@ void game::view::WorldView::Draw(const DrawArgs& args) const
} }
} }
glm::vec3 game::view::WorldView::CameraSweep(const glm::vec3& start, const glm::vec3& end)
{
const auto& bt_world = GetBtWorld();
static const btSphereShape shape(0.1f);
btVector3 bt_start(start.x, start.y, start.z);
btVector3 bt_end(end.x, end.y, end.z);
btTransform from, to;
from.setIdentity();
from.setOrigin(bt_start);
to.setIdentity();
to.setOrigin(bt_end);
btCollisionWorld::ClosestConvexResultCallback cb(bt_start, bt_end);
bt_world.convexSweepTest(&shape, from, to, cb);
if (!cb.hasHit())
return end;
return glm::mix(start, end, cb.m_closestHitFraction);
}
game::view::EntityView* game::view::WorldView::GetEntity(net::EntNum entnum) game::view::EntityView* game::view::WorldView::GetEntity(net::EntNum entnum)
{ {
auto it = ents_.find(entnum); auto it = ents_.find(entnum);

View File

@ -4,7 +4,7 @@
#include "draw_args.hpp" #include "draw_args.hpp"
#include "net/defs.hpp" #include "net/defs.hpp"
#include "net/inmessage.hpp" #include "net/inmessage.hpp"
#include "collision/dynamicsworld.hpp"
#include "entityview.hpp" #include "entityview.hpp"
namespace game::view namespace game::view
@ -12,7 +12,7 @@ namespace game::view
class ClientSession; class ClientSession;
class WorldView class WorldView : public collision::DynamicsWorld
{ {
public: public:
WorldView(ClientSession& session); WorldView(ClientSession& session);
@ -22,10 +22,11 @@ public:
void Update(const UpdateInfo& info); void Update(const UpdateInfo& info);
void Draw(const DrawArgs& args) const; void Draw(const DrawArgs& args) const;
EntityView* GetEntity(net::EntNum entnum); glm::vec3 CameraSweep(const glm::vec3& start, const glm::vec3& end);
float GetTime() const { return time_; }
EntityView* GetEntity(net::EntNum entnum);
float GetTime() const { return time_; }
audio::Master& GetAudioMaster() const { return audiomaster_; } audio::Master& GetAudioMaster() const { return audiomaster_; }
private: private:

View File

@ -22,6 +22,12 @@ struct AABB
max = glm::max(max, point); max = glm::max(max, point);
} }
void AddAABB(const AABB& other)
{
min = glm::min(min, other.min);
max = glm::max(max, other.max);
}
bool CollidesWith(const AABB<dim>& other) const; bool CollidesWith(const AABB<dim>& other) const;
AABB<dim> Intersection(const AABB<dim>& other) const AABB<dim> Intersection(const AABB<dim>& other) const