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 "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)
{
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.aabb.min = trans.position - glm::vec3(10.0f);
obj.aabb.max = trans.position + glm::vec3(10.0f);
obj.aabb = TransformAABB(obj.model->GetAABB(), obj.node.matrix);
chunk->aabb.AddAABB(obj.aabb);
std::string 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 >> 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")
{
@ -181,7 +200,6 @@ void assets::Map::Draw(const game::view::DrawArgs& args) const
DrawChunk(args, mesh, chunk);
}
}
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

View File

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

View File

@ -5,6 +5,7 @@
#include "skeleton.hpp"
#include "utils/defs.hpp"
#include "utils/aabb.hpp"
#include "collision/trianglemesh.hpp"
#ifdef CLIENT
@ -45,6 +46,7 @@ public:
const std::shared_ptr<const Skeleton>& GetSkeleton() const { return skeleton_; }
CLIENT_ONLY(const std::shared_ptr<const Mesh>& GetMesh() const { return mesh_; })
const AABB3& GetAABB() const { return aabb_; }
private:
std::unique_ptr<collision::TriangleMesh> cmesh_;
@ -52,7 +54,8 @@ private:
std::unique_ptr<btCollisionShape> cshape_;
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);
}
float aspect = static_cast<float>(viewport_size_.x) / static_cast<float>(viewport_size_.y);
renderer_.Begin(viewport_size_.x, viewport_size_.y);
renderer_.ClearColor(glm::vec3(0.5f, 0.7f, 1.0f));
@ -78,43 +77,11 @@ void App::Frame()
params.screen_height = viewport_size_.y;
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));
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_;
}
}
session_->Draw(dlist_, params);
}
// draw chat
UpdateChat();
DrawChat(dlist_);

View File

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

View File

@ -2,34 +2,22 @@
#include <stdexcept>
collision::DynamicsWorld::DynamicsWorld(std::shared_ptr<const assets::Map> map)
: map_(std::move(map)), bt_dispatcher_(&bt_cfg_),
collision::DynamicsWorld::DynamicsWorld()
: bt_dispatcher_(&bt_cfg_),
bt_world_(&bt_dispatcher_, &bt_broadphase_, &bt_solver_, &bt_cfg_), bt_veh_raycaster_(&bt_world_)
{
bt_world_.setGravity(btVector3(0, 0, -9.81f));
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;
map_ = std::move(map);
// add basemodel
const auto& basemodel = map_->GetBaseModel();
if (basemodel)

View File

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

View File

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

View File

@ -5,13 +5,12 @@
#include "assets/cache.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()
{
auto entnum = utils::AllocNum(ents_, last_entnum_);

View File

@ -46,7 +46,11 @@ public:
virtual ~World() = default;
protected:
const assets::Map& GetMap() const { return *map_; }
private:
std::shared_ptr<const assets::Map> map_;
std::string mapname_;
std::map<net::EntNum, std::unique_ptr<Entity>> ents_;
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
radius_ = 2.0f;
}
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)
{
if (world_)
{
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
{
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_);
if (ent)
center += ent->GetRoot().local.position;
start += ent->GetRoot().local.position;
}
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_);
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;
view = glm::lookAt(eye, center, glm::vec3(0, 0, 1));
//start.z -= 0.5f; // shift this a bit to make it better when occluded
eye = world_->CameraSweep(start, end);
view = glm::lookAt(eye, eye + dir, glm::vec3(0, 0, 1));
}
audio::Master& game::view::ClientSession::GetAudioMaster() const
@ -124,3 +137,49 @@ bool game::view::ClientSession::ProcessChatMsg(net::InMessage& msg)
app_.AddChatMessagePrefix("Server", chatm);
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 "gfx/draw_list.hpp"
#include "gfx/renderer.hpp"
#include "net/defs.hpp"
#include "net/inmessage.hpp"
@ -24,6 +25,7 @@ public:
void ProcessMouseMove(float delta_yaw, float delta_pitch);
void Update(const UpdateInfo& info);
void Draw(gfx::DrawList& dlist, gfx::DrawListParams& params);
const WorldView* GetWorld() const { return world_.get(); }
@ -31,15 +33,15 @@ public:
audio::Master& GetAudioMaster() const;
float GetYaw() const { return yaw_; }
float GetPitch() const { return pitch_; }
private:
// msg handlers
bool ProcessWorldMsg(net::InMessage& msg);
bool ProcessCameraMsg(net::InMessage& msg);
bool ProcessChatMsg(net::InMessage& msg);
void DrawWorld(gfx::DrawList& dlist, gfx::DrawListParams& params);
void SendViewAngles(float time);
private:
App& app_;
@ -48,6 +50,9 @@ private:
float yaw_ = 0.0f, pitch_ = 0.0f;
net::EntNum follow_ent_ = 0;
net::ViewYawQ view_yaw_q_;
net::ViewPitchQ view_pitch_q_;
float last_send_time_ = 0.0f;
};
} // namespace game::view

View File

@ -11,6 +11,7 @@ game::view::WorldView::WorldView(ClientSession& session) :
audiomaster_(session_.GetAudioMaster())
{
map_ = assets::CacheManager::GetMap("data/openworld.map");
AddMapCollision(map_);
}
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)
{
auto it = ents_.find(entnum);

View File

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

View File

@ -22,6 +22,12 @@ struct AABB
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;
AABB<dim> Intersection(const AABB<dim>& other) const