Add better movement and teleporting
This commit is contained in:
parent
e4ee01de2e
commit
b81cee948d
@ -19,6 +19,9 @@ add_executable(PortalGame
|
||||
"src/collision/trianglemesh.cpp"
|
||||
"src/game/entity.hpp"
|
||||
"src/game/entity.cpp"
|
||||
"src/game/player_input.hpp"
|
||||
"src/game/player.hpp"
|
||||
"src/game/player.cpp"
|
||||
"src/game/sector.hpp"
|
||||
"src/game/sector.cpp"
|
||||
"src/game/world.hpp"
|
||||
|
||||
95
src/app.cpp
95
src/app.cpp
@ -22,11 +22,7 @@ App::App()
|
||||
world_.LinkPortals(s1, "NDoor", s2, "WDoor");
|
||||
world_.LinkPortals(s2, "NDoor", s2, "SDoor");
|
||||
|
||||
game::CapsuleShape capsule_shape;
|
||||
capsule_shape.radius = 0.2f; // 20cm radius
|
||||
capsule_shape.height = 0.3f; // 1.8m height
|
||||
|
||||
player_ = world_.Spawn<game::Entity>(s1, capsule_shape, glm::vec3(0.0f, 0.0f, 1.0f));
|
||||
player_ = world_.Spawn<game::Player>(s1, glm::vec3(0.0f, 0.0f, 1.0f));
|
||||
}
|
||||
|
||||
void App::Frame()
|
||||
@ -47,85 +43,34 @@ void App::Frame()
|
||||
|
||||
renderer_.Begin(viewport_size_.x, viewport_size_.y);
|
||||
|
||||
//float cameraDistance = 2.0f;
|
||||
//float angle = time_ * 0.1f; // Rotate over time
|
||||
//
|
||||
//glm::vec3 cameraPos = glm::vec3(
|
||||
// cameraDistance * cos(angle),
|
||||
// cameraDistance * sin(angle),
|
||||
// cameraDistance * 0.13f
|
||||
//);
|
||||
|
||||
//glm::vec3 center(0.0f, 0.0f, 1.5f);
|
||||
//glm::vec3 up(0.0f, 0.0f, 1.0f);
|
||||
|
||||
//renderer_.DrawWorld(world_, 1, center + cameraPos, -cameraPos, up, aspect, 60.0f);
|
||||
|
||||
glm::vec3 velocity(0.0f, 0.0f, 0.0f);
|
||||
|
||||
if (input_ & game::PI_FORWARD)
|
||||
velocity.y += 1.0f;
|
||||
|
||||
if (input_ & game::PI_BACKWARD)
|
||||
velocity.y -= 1.0f;
|
||||
|
||||
if (input_ & game::PI_LEFT)
|
||||
velocity.x -= 1.0f;
|
||||
|
||||
if (input_ & game::PI_RIGHT)
|
||||
velocity.x += 1.0f;
|
||||
|
||||
if (input_ & game::PI_JUMP)
|
||||
velocity.z += 1.0f;
|
||||
|
||||
if (input_ & game::PI_CROUCH)
|
||||
velocity.z -= 1.0f;
|
||||
|
||||
glm::vec3 forward = glm::vec3(
|
||||
cos(angles_.x) * cos(angles_.y),
|
||||
sin(angles_.x) * cos(angles_.y),
|
||||
sin(angles_.y)
|
||||
);
|
||||
|
||||
velocity.z -= 0.1f * delta_time; // Apply gravity
|
||||
|
||||
glm::vec3 forward_xy = glm::normalize(glm::vec3(forward.x, forward.y, 0.0f));
|
||||
glm::vec3 right = glm::normalize(glm::vec3(forward.y, -forward.x, 0.0f));
|
||||
velocity = glm::normalize(velocity.x * right + velocity.y * forward_xy + velocity.z * glm::vec3(0.0f, 0.0f, 1.0f));
|
||||
|
||||
//if (glm::length(velocity) > 0.1f)
|
||||
//{
|
||||
// velocity = glm::normalize(velocity); // Normalize to prevent faster diagonal movement
|
||||
|
||||
// glm::vec3 u = velocity * delta_time * 2.0f;
|
||||
// //bool hit = world_.GetSector(0).SweepCapsule(position, position + u);
|
||||
|
||||
// //if (!hit)
|
||||
// //{
|
||||
// // position += u;
|
||||
// //}
|
||||
//}
|
||||
|
||||
player_->SetVelocity(velocity);
|
||||
player_->SetInput(input_);
|
||||
player_->Update(delta_time);
|
||||
|
||||
const auto& position = player_->GetOccurrence().GetPosition();
|
||||
//const auto& position = player_->GetOccurrence().GetPosition();
|
||||
size_t sector_idx;
|
||||
glm::vec3 position, forward, up;
|
||||
player_->GetPOV(sector_idx, position, forward, up);
|
||||
|
||||
renderer_.DrawWorld(world_, 0, position, forward, glm::vec3(0.0f, 0.0f, 1.0f), aspect, 60.0f);
|
||||
renderer_.DrawWorld(world_, sector_idx, position, forward, up, aspect, 60.0f);
|
||||
}
|
||||
|
||||
void App::MouseMove(const glm::vec2& delta)
|
||||
{
|
||||
float sensitivity = 0.002f; // Sensitivity factor for mouse movement
|
||||
angles_.x -= delta.x * sensitivity; // Yaw
|
||||
angles_.y -= delta.y * sensitivity; // Pitch
|
||||
//angles_.x -= delta.x * sensitivity; // Yaw
|
||||
//angles_.y -= delta.y * sensitivity; // Pitch
|
||||
|
||||
// Clamp pitch to avoid gimbal lock
|
||||
if (angles_.y > glm::radians(89.0f)) {
|
||||
angles_.y = glm::radians(89.0f);
|
||||
} else if (angles_.y < glm::radians(-89.0f)) {
|
||||
angles_.y = glm::radians(-89.0f);
|
||||
}
|
||||
//// Clamp pitch to avoid gimbal lock
|
||||
//if (angles_.y > glm::radians(89.0f)) {
|
||||
// angles_.y = glm::radians(89.0f);
|
||||
//} else if (angles_.y < glm::radians(-89.0f)) {
|
||||
// angles_.y = glm::radians(-89.0f);
|
||||
//}
|
||||
|
||||
float delta_yaw = delta.x * sensitivity;
|
||||
float delta_pitch = -delta.y * sensitivity;
|
||||
|
||||
player_->Rotate(delta_yaw, delta_pitch);
|
||||
}
|
||||
|
||||
App::~App()
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
|
||||
#include "game/world.hpp"
|
||||
#include "game/player_input.hpp"
|
||||
#include "game/player.hpp"
|
||||
#include "gfx/renderer.hpp"
|
||||
|
||||
class App
|
||||
@ -15,9 +16,7 @@ class App
|
||||
float prev_time_ = 0.0f;
|
||||
|
||||
game::World world_;
|
||||
game::Entity* player_ = nullptr;
|
||||
|
||||
glm::vec2 angles_ = { 0.0f, 0.0f }; // Pitch and yaw angles in radians
|
||||
game::Player* player_ = nullptr;
|
||||
|
||||
gfx::Renderer renderer_;
|
||||
|
||||
|
||||
@ -4,7 +4,8 @@
|
||||
game::Entity::Entity(World* world, size_t sector_idx, const CapsuleShape& capsule_shape, const glm::vec3& position) :
|
||||
world_(world),
|
||||
capsule_(capsule_shape),
|
||||
velocity_(0.0f)
|
||||
velocity_(0.0f),
|
||||
touching_portal_(nullptr)
|
||||
{
|
||||
CreateOccurrenceParams occu_params;
|
||||
occu_params.sector = &world->GetSector(sector_idx);
|
||||
@ -23,7 +24,7 @@ void game::Entity::Move(glm::vec3& velocity, float dt)
|
||||
const int MAX_ITERS = 4;
|
||||
for (size_t i = 0; i < MAX_ITERS && glm::dot(u, u) > 0.0f; ++i)
|
||||
{
|
||||
printf("Entity::Move: Iteration %zu, u = (%f, %f, %f)\n", i, u.x, u.y, u.z);
|
||||
//printf("Entity::Move: Iteration %zu, u = (%f, %f, %f)\n", i, u.x, u.y, u.z);
|
||||
|
||||
// offset in occu's sector space
|
||||
glm::vec3 occu_offset = occu_->basis_ * u;
|
||||
@ -37,6 +38,7 @@ void game::Entity::Move(glm::vec3& velocity, float dt)
|
||||
|
||||
if (hit_portal != touching_portal_)
|
||||
{
|
||||
printf("Entity::Move: Touching portal changed from %p to %p\n", touching_portal_, hit_portal);
|
||||
touching_portal_ = hit_portal;
|
||||
|
||||
if (hit_portal)
|
||||
@ -85,15 +87,21 @@ void game::Entity::Move(glm::vec3& velocity, float dt)
|
||||
// Update the position based on the hit fraction
|
||||
occu_->position_ += hit_fraction * occu_offset;
|
||||
|
||||
if (any_hit)
|
||||
{
|
||||
occu_->position_ += hit_normal * 0.00001f;
|
||||
}
|
||||
|
||||
//if (any_hit)
|
||||
//{
|
||||
// occu_->position_ += hit_normal * 0.00001f;
|
||||
//}
|
||||
|
||||
if (other_occu_ && touching_portal_)
|
||||
{
|
||||
other_occu_->position_ = touching_portal_->tr_position * glm::vec4(occu_->position_, 1.0f);
|
||||
|
||||
// TELEPORT
|
||||
float sd = glm::dot(glm::vec3(touching_portal_->plane), occu_->position_) + touching_portal_->plane.w;
|
||||
if (sd < 0.0f)
|
||||
{
|
||||
std::swap(occu_, other_occu_); // Swap occurrences
|
||||
}
|
||||
}
|
||||
|
||||
if (!any_hit)
|
||||
@ -102,7 +110,7 @@ void game::Entity::Move(glm::vec3& velocity, float dt)
|
||||
}
|
||||
|
||||
//hit_normal *= -1.0f; // Invert the normal to point outwards
|
||||
printf("Entity::Move: Hit detected, hit_fraction = %f, hit_normal = (%f, %f, %f)\n", hit_fraction, hit_normal.x, hit_normal.y, hit_normal.z);
|
||||
//printf("Entity::Move: Hit detected, hit_fraction = %f, hit_normal = (%f, %f, %f)\n", hit_fraction, hit_normal.x, hit_normal.y, hit_normal.z);
|
||||
|
||||
u -= hit_fraction * u; // Reduce the movement vector by the hit fraction
|
||||
u -= glm::dot(u, hit_normal) * hit_normal; // Reflect the velocity along the hit normal
|
||||
@ -118,6 +126,14 @@ void game::Entity::Update(float dt)
|
||||
|
||||
void game::Entity::CreateOtherOccurence(const Portal& portal)
|
||||
{
|
||||
other_occu_.reset();
|
||||
|
||||
if (!portal.link)
|
||||
{
|
||||
// No linked sector, no occurence :(
|
||||
return;
|
||||
}
|
||||
|
||||
CreateOccurrenceParams other_occu_params;
|
||||
other_occu_params.sector = portal.link->sector;
|
||||
other_occu_params.position = portal.tr_position * glm::vec4(occu_->position_, 1.0f);
|
||||
@ -141,13 +157,22 @@ game::EntityOccurrence::EntityOccurrence(Entity* entity, const CreateOccurrenceP
|
||||
|
||||
bool game::EntityOccurrence::Sweep(const glm::vec3& target_position, float& hit_fraction, glm::vec3& hit_normal, const Portal** hit_portal)
|
||||
{
|
||||
if (hit_portal)
|
||||
{
|
||||
*hit_portal = sector_->TestPortalContact(bt_capsule_, basis_, position_);
|
||||
|
||||
if (!*hit_portal)
|
||||
{
|
||||
*hit_portal = sector_->TestPortalContact(bt_capsule_, basis_, target_position);
|
||||
}
|
||||
}
|
||||
|
||||
return sector_->SweepCapsule(
|
||||
bt_capsule_,
|
||||
basis_,
|
||||
position_,
|
||||
target_position,
|
||||
hit_fraction,
|
||||
hit_normal,
|
||||
hit_portal
|
||||
hit_normal
|
||||
);
|
||||
}
|
||||
|
||||
@ -45,10 +45,14 @@ namespace game
|
||||
|
||||
const Sector& GetSector() const { return *sector_; }
|
||||
const glm::vec3& GetPosition() const { return position_; }
|
||||
|
||||
const glm::mat3& GetBasis() const { return basis_; }
|
||||
const glm::mat3& GetInvBasis() const { return inv_basis_; }
|
||||
};
|
||||
|
||||
class Entity
|
||||
{
|
||||
protected:
|
||||
World* world_;
|
||||
|
||||
CapsuleShape capsule_;
|
||||
@ -65,7 +69,7 @@ namespace game
|
||||
// offset in entity space
|
||||
void Move(glm::vec3& velocity, float dt);
|
||||
|
||||
void Update(float dt);
|
||||
virtual void Update(float dt);
|
||||
|
||||
void SetVelocity(const glm::vec3& velocity) { velocity_ = velocity; }
|
||||
|
||||
|
||||
88
src/game/player.cpp
Normal file
88
src/game/player.cpp
Normal file
@ -0,0 +1,88 @@
|
||||
#include "player.hpp"
|
||||
#include "world.hpp"
|
||||
#include "sector.hpp"
|
||||
|
||||
game::Player::Player(World* world, size_t sector_idx, const glm::vec3& position) :
|
||||
Super(world, sector_idx, { 0.3f, 1.2f }, position),
|
||||
yaw_(0.0f),
|
||||
pitch_(0.0f),
|
||||
input_(0),
|
||||
cam_forward_(0.0f),
|
||||
cam_up_(0.0f)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void game::Player::Rotate(float delta_yaw, float delta_pitch)
|
||||
{
|
||||
yaw_ += delta_yaw;
|
||||
pitch_ += delta_pitch;
|
||||
// Clamp pitch to avoid gimbal lock
|
||||
if (pitch_ > glm::radians(89.0f)) {
|
||||
pitch_ = glm::radians(89.0f);
|
||||
} else if (pitch_ < glm::radians(-89.0f)) {
|
||||
pitch_ = glm::radians(-89.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void game::Player::Update(float dt)
|
||||
{
|
||||
const glm::mat3& occu_basis = occu_->GetBasis();
|
||||
|
||||
float yaw_cos = glm::cos(yaw_);
|
||||
float yaw_sin = glm::sin(yaw_);
|
||||
float pitch_cos = glm::cos(pitch_);
|
||||
float pitch_sin = glm::sin(pitch_);
|
||||
|
||||
// MOVEMENT
|
||||
glm::vec2 velocity_xy(0.0f);
|
||||
if (input_ & PI_FORWARD)
|
||||
velocity_xy.y += 1.0f;
|
||||
|
||||
if (input_ & PI_BACKWARD)
|
||||
velocity_xy.y -= 1.0f;
|
||||
|
||||
if (input_ & PI_LEFT)
|
||||
velocity_xy.x -= 1.0f;
|
||||
|
||||
if (input_ & PI_RIGHT)
|
||||
velocity_xy.x += 1.0f;
|
||||
|
||||
glm::mat2 movement_basis(
|
||||
yaw_cos, -yaw_sin,
|
||||
yaw_sin, yaw_cos
|
||||
);
|
||||
|
||||
float speed = 3.0f; // Base speed
|
||||
|
||||
glm::vec2 normalized_velocity_xy(0.0f);
|
||||
if (glm::length(velocity_xy) > 0.01f)
|
||||
{
|
||||
normalized_velocity_xy = glm::normalize(movement_basis * velocity_xy);
|
||||
}
|
||||
|
||||
glm::vec3 velocity = glm::vec3(
|
||||
normalized_velocity_xy * speed,
|
||||
-1.0f // No vertical movement for now
|
||||
);
|
||||
|
||||
velocity_ = velocity;
|
||||
|
||||
Super::Update(dt);
|
||||
|
||||
{
|
||||
// Occurrence could have changed during update !!!!1!1!
|
||||
const glm::mat3& occu_basis = occu_->GetBasis();
|
||||
cam_forward_ = occu_basis * glm::vec3(yaw_sin * pitch_cos, yaw_cos * pitch_cos, pitch_sin);
|
||||
cam_up_ = occu_basis[2]; // Up vector is always the Z axis in sector space
|
||||
}
|
||||
}
|
||||
|
||||
void game::Player::GetPOV(size_t& sector_idx, glm::vec3& position, glm::vec3& forward, glm::vec3& up) const
|
||||
{
|
||||
sector_idx = occu_->GetSector().GetIndex();
|
||||
position = occu_->GetPosition() + cam_up_ * 0.7f;
|
||||
|
||||
forward = cam_forward_;
|
||||
up = cam_up_;
|
||||
}
|
||||
36
src/game/player.hpp
Normal file
36
src/game/player.hpp
Normal file
@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include "entity.hpp"
|
||||
#include "player_input.hpp"
|
||||
|
||||
namespace game
|
||||
{
|
||||
class Player : public Entity
|
||||
{
|
||||
using Super = Entity;
|
||||
|
||||
float yaw_, pitch_;
|
||||
PlayerInputFlags input_;
|
||||
|
||||
// in occu's sector space
|
||||
glm::vec3 cam_forward_;
|
||||
glm::vec3 cam_up_;
|
||||
|
||||
public:
|
||||
Player(World* world, size_t sector_idx, const glm::vec3& position);
|
||||
|
||||
// angles in radians
|
||||
void Rotate(float delta_yaw, float delta_pitch);
|
||||
|
||||
void SetInput(PlayerInputFlags input) { input_ = input; }
|
||||
|
||||
virtual void Update(float dt) override;
|
||||
|
||||
void GetPOV(size_t& sector_idx, glm::vec3& position, glm::vec3& forward, glm::vec3& up) const;
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -14,6 +14,10 @@ game::Sector::Sector(World* world, size_t idx, std::shared_ptr<assets::SectorDef
|
||||
bt_col_dispatcher_(&bt_col_cfg_),
|
||||
bt_world_(&bt_col_dispatcher_, &bt_broad_phase_, &bt_col_cfg_)
|
||||
{
|
||||
// Create Bullet collision mesh
|
||||
bt_mesh_col_obj_.setCollisionShape(mesh_->GetCollisionMesh()->GetShape());
|
||||
bt_world_.addCollisionObject(&bt_mesh_col_obj_);
|
||||
|
||||
auto def_portals = def_->GetPortals();
|
||||
size_t num_portals = def_portals.size();
|
||||
|
||||
@ -26,7 +30,9 @@ game::Sector::Sector(World* world, size_t idx, std::shared_ptr<assets::SectorDef
|
||||
portal.sector = this;
|
||||
portal.def = &assets::PortalDef::portal_defs[def_portal.def_name];
|
||||
|
||||
glm::mat4 rotation(glm::quat(glm::radians(def_portal.angles)));
|
||||
glm::vec3 angles_rad = glm::radians(def_portal.angles);
|
||||
|
||||
glm::mat4 rotation = glm::mat4((glm::quat(angles_rad)));
|
||||
glm::mat4 translation = glm::translate(glm::mat4(1.0f), def_portal.origin);
|
||||
glm::mat4 scale = glm::scale(glm::mat4(1.0f), glm::vec3(def_portal.scale));
|
||||
|
||||
@ -43,10 +49,26 @@ game::Sector::Sector(World* world, size_t idx, std::shared_ptr<assets::SectorDef
|
||||
portal.link = nullptr;
|
||||
|
||||
portal_map_[def_portal.name] = i;
|
||||
|
||||
// create portal collision object
|
||||
portal.bt_col_shape = std::make_unique<btBoxShape>(
|
||||
btVector3(portal.def->size.x * portal.scale * 0.5f, 0.1f, portal.def->size.y * portal.scale * 0.5f)
|
||||
);
|
||||
|
||||
portal.bt_col_obj = std::make_unique<btCollisionObject>();
|
||||
portal.bt_col_obj->setCollisionShape(portal.bt_col_shape.get());
|
||||
|
||||
btQuaternion bt_rotation(angles_rad.x, angles_rad.y, angles_rad.z);
|
||||
btVector3 bt_position(def_portal.origin.x, def_portal.origin.y, def_portal.origin.z);
|
||||
btTransform bt_transform(bt_rotation, bt_position);
|
||||
portal.bt_col_obj->setWorldTransform(bt_transform);
|
||||
|
||||
portal.col_data.type = CO_PORTAL;
|
||||
portal.col_data.portal = &portal;
|
||||
portal.bt_col_obj->setUserPointer(&portal.col_data);
|
||||
|
||||
bt_world_.addCollisionObject(portal.bt_col_obj.get());
|
||||
}
|
||||
|
||||
bt_mesh_col_obj_.setCollisionShape(mesh_->GetCollisionMesh()->GetShape());
|
||||
bt_world_.addCollisionObject(&bt_mesh_col_obj_);
|
||||
}
|
||||
|
||||
void game::Sector::ComputePortalVertex(Portal& portal, size_t idx, const glm::vec2& base_vert)
|
||||
@ -82,7 +104,7 @@ static void LinkPortal(game::Portal& p1, game::Portal& p2) {
|
||||
|
||||
// get rid of scale
|
||||
for (size_t i = 0; i < 3 ; i++)
|
||||
p1.tr_orientation[i] = glm::normalize(p1.tr_basis[i]);
|
||||
p1.tr_basis[i] = glm::normalize(p1.tr_basis[i]);
|
||||
|
||||
p1.tr_scale = p2.scale / p1.scale;
|
||||
}
|
||||
@ -108,18 +130,15 @@ void game::Sector::LinkPortals(Sector& s1, size_t idx1, Sector& s2, size_t idx2)
|
||||
|
||||
namespace game
|
||||
{
|
||||
struct PortalDetectingClosestConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback
|
||||
struct PortalIgnoringClosestConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback
|
||||
{
|
||||
using Super = btCollisionWorld::ClosestConvexResultCallback;
|
||||
|
||||
PortalDetectingClosestConvexResultCallback(const btVector3& convexFromWorld, const btVector3& convexToWorld) :
|
||||
PortalIgnoringClosestConvexResultCallback(const btVector3& convexFromWorld, const btVector3& convexToWorld) :
|
||||
Super(convexFromWorld, convexToWorld)
|
||||
{
|
||||
}
|
||||
|
||||
const Portal* hit_portal = nullptr;
|
||||
float hit_portal_fraction = std::numeric_limits<float>::max();
|
||||
|
||||
virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult, bool normalInWorldSpace) override
|
||||
{
|
||||
void* ptr = convexResult.m_hitCollisionObject->getUserPointer();
|
||||
@ -127,21 +146,13 @@ namespace game
|
||||
|
||||
if (data && data->type == CO_PORTAL)
|
||||
{
|
||||
if (convexResult.m_hitFraction < hit_portal_fraction)
|
||||
{
|
||||
hit_portal = data->portal;
|
||||
hit_portal_fraction = convexResult.m_hitFraction;
|
||||
}
|
||||
// Ignore portal hits
|
||||
return m_closestHitFraction;
|
||||
}
|
||||
else
|
||||
{
|
||||
Super::addSingleResult(convexResult, normalInWorldSpace);
|
||||
}
|
||||
|
||||
return 1.0f; // Continue processing other results
|
||||
|
||||
return Super::addSingleResult(convexResult, normalInWorldSpace);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
bool game::Sector::SweepCapsule(
|
||||
@ -150,8 +161,7 @@ bool game::Sector::SweepCapsule(
|
||||
const glm::vec3& start,
|
||||
const glm::vec3& end,
|
||||
float& hit_fraction,
|
||||
glm::vec3& hit_normal,
|
||||
const Portal** hit_portal)
|
||||
glm::vec3& hit_normal)
|
||||
{
|
||||
//const btVector3* bt_start = reinterpret_cast<const btVector3*>(&start);
|
||||
//const btVector3* bt_end = reinterpret_cast<const btVector3*>(&end);
|
||||
@ -168,14 +178,9 @@ bool game::Sector::SweepCapsule(
|
||||
btTransform start_transform(bt_basis, bt_start);
|
||||
btTransform end_transform(bt_basis, bt_end);
|
||||
|
||||
PortalDetectingClosestConvexResultCallback result_callback(bt_start, bt_end);
|
||||
PortalIgnoringClosestConvexResultCallback result_callback(bt_start, bt_end);
|
||||
|
||||
bt_world_.convexSweepTest(&capsule, start_transform, end_transform, result_callback);
|
||||
|
||||
if (hit_portal)
|
||||
{
|
||||
*hit_portal = result_callback.hit_portal;
|
||||
}
|
||||
bt_world_.convexSweepTest(&capsule, start_transform, end_transform, result_callback, 0.0f);
|
||||
|
||||
if (result_callback.hasHit())
|
||||
{
|
||||
@ -191,3 +196,55 @@ bool game::Sector::SweepCapsule(
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace game
|
||||
{
|
||||
struct PortalContactResultCallback : public btCollisionWorld::ContactResultCallback
|
||||
{
|
||||
const game::Portal* portal = nullptr;
|
||||
|
||||
PortalContactResultCallback()
|
||||
{
|
||||
}
|
||||
|
||||
virtual btScalar addSingleResult(btManifoldPoint& cp, const btCollisionObjectWrapper* colObj0Wrap, int partId0, int index0,
|
||||
const btCollisionObjectWrapper* colObj1Wrap, int partId1, int index1) override
|
||||
{
|
||||
void* ptr0 = colObj0Wrap->getCollisionObject()->getUserPointer();
|
||||
void* ptr1 = colObj1Wrap->getCollisionObject()->getUserPointer();
|
||||
void* ptr = ptr0 ? ptr0 : ptr1;
|
||||
CollisionObjectData* data = static_cast<CollisionObjectData*>(ptr);
|
||||
|
||||
if (data && data->type == CO_PORTAL)
|
||||
{
|
||||
// We hit a portal, store it
|
||||
portal = data->portal;
|
||||
}
|
||||
|
||||
return 0.0f; // No specific action needed for contact results
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const game::Portal* game::Sector::TestPortalContact(btCapsuleShapeZ& capsule, const glm::mat3& basis, const glm::vec3& pos)
|
||||
{
|
||||
btCollisionObject capsule_obj;
|
||||
capsule_obj.setCollisionShape(&capsule);
|
||||
btTransform capsule_transform;
|
||||
|
||||
btVector3 bt_pos(pos.x, pos.y, pos.z);
|
||||
btMatrix3x3 bt_basis(
|
||||
basis[0][0], basis[0][1], basis[0][2],
|
||||
basis[1][0], basis[1][1], basis[1][2],
|
||||
basis[2][0], basis[2][1], basis[2][2]
|
||||
);
|
||||
|
||||
btTransform bt_transform(bt_basis, bt_pos);
|
||||
|
||||
capsule_obj.setWorldTransform(bt_transform);
|
||||
|
||||
PortalContactResultCallback result_callback;
|
||||
bt_world_.contactTest(&capsule_obj, result_callback);
|
||||
|
||||
return result_callback.portal;
|
||||
}
|
||||
|
||||
@ -7,9 +7,9 @@
|
||||
|
||||
namespace game
|
||||
{
|
||||
class World;
|
||||
class Sector;
|
||||
class Portal;
|
||||
class World;
|
||||
|
||||
enum CollisionObjectType
|
||||
{
|
||||
@ -41,8 +41,11 @@ namespace game
|
||||
|
||||
glm::mat4 tr_position; // this sector space to other sector space
|
||||
glm::mat3 tr_basis;
|
||||
glm::mat3 tr_orientation;
|
||||
float tr_scale;
|
||||
|
||||
std::unique_ptr<btCollisionShape> bt_col_shape; // Bullet collision shape
|
||||
std::unique_ptr<btCollisionObject> bt_col_obj; // Bullet collision object
|
||||
CollisionObjectData col_data;
|
||||
};
|
||||
|
||||
class Sector
|
||||
@ -56,13 +59,13 @@ namespace game
|
||||
std::vector<Portal> portals_;
|
||||
std::map<std::string, size_t> portal_map_; // Maps portal name to index in portals_
|
||||
|
||||
btCollisionObject bt_mesh_col_obj_;
|
||||
|
||||
btDefaultCollisionConfiguration bt_col_cfg_;
|
||||
btCollisionDispatcher bt_col_dispatcher_;
|
||||
btDbvtBroadphase bt_broad_phase_;
|
||||
btCollisionWorld bt_world_;
|
||||
|
||||
btCollisionObject bt_mesh_col_obj_;
|
||||
|
||||
public:
|
||||
Sector(World* world, size_t idx, std::shared_ptr<assets::SectorDef> def);
|
||||
|
||||
@ -70,6 +73,7 @@ namespace game
|
||||
void ComputePortalVertex(Portal& portal, size_t idx, const glm::vec2& base_vert);
|
||||
|
||||
public:
|
||||
size_t GetIndex() const { return idx_; }
|
||||
const std::shared_ptr<assets::Mesh>& GetMesh() const { return mesh_; }
|
||||
|
||||
int GetPortalIndex(const std::string& name) const;
|
||||
@ -84,8 +88,10 @@ namespace game
|
||||
const glm::vec3& start,
|
||||
const glm::vec3& end,
|
||||
float& hit_fraction,
|
||||
glm::vec3& hit_normal,
|
||||
const Portal** hit_portal);
|
||||
glm::vec3& hit_normal);
|
||||
|
||||
const Portal* TestPortalContact(btCapsuleShapeZ& capsule, const glm::mat3& basis, const glm::vec3& pos);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -25,8 +25,10 @@ namespace game
|
||||
template<class T, class... TArgs>
|
||||
T* Spawn(TArgs&&... args)
|
||||
{
|
||||
auto& entity = entities_.emplace_back(std::make_unique<T>(this, std::forward<TArgs>(args)...));
|
||||
return entity.get();
|
||||
auto entity = std::make_unique<T>(this, std::forward<TArgs>(args)...);
|
||||
T* entity_ptr = entity.get();
|
||||
entities_.push_back(std::move(entity));
|
||||
return entity_ptr;
|
||||
}
|
||||
|
||||
Sector& GetSector(size_t idx) { return *sectors_[idx]; }
|
||||
|
||||
@ -45,11 +45,14 @@ void gfx::Renderer::DrawWorld(
|
||||
float aspect,
|
||||
float fov)
|
||||
{
|
||||
float near_plane = 0.001f; // Near plane distance
|
||||
float far_plane = 500.0f; // Far plane distance
|
||||
|
||||
proj_ = glm::perspective(
|
||||
glm::radians(fov), // FOV
|
||||
aspect, // Aspect ratio
|
||||
0.01f, // Near plane
|
||||
1000.0f // Far plane
|
||||
near_plane, // Near plane
|
||||
far_plane // Far plane
|
||||
);
|
||||
|
||||
glm::mat4 view = glm::lookAt(
|
||||
@ -58,6 +61,8 @@ void gfx::Renderer::DrawWorld(
|
||||
up // Up vector
|
||||
);
|
||||
|
||||
min_portal_distance_ = near_plane * 2.0f;
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthFunc(GL_LESS);
|
||||
|
||||
@ -166,25 +171,39 @@ void gfx::Renderer::DrawPortal(const DrawSectorParams& params, const game::Porta
|
||||
return; // Portal AABB does not intersect with the screen AABB, skip
|
||||
}
|
||||
|
||||
const game::Portal& other_portal = *portal.link;
|
||||
const game::Sector& other_sector = *other_portal.sector;
|
||||
|
||||
glm::mat4 portal_mesh_trans = portal.trans;
|
||||
glm::vec4 new_clip_plane = other_portal.plane;
|
||||
|
||||
if (eye_plane_sd < min_portal_distance_)
|
||||
{
|
||||
float how_much_shift = min_portal_distance_ - eye_plane_sd;
|
||||
|
||||
portal_mesh_trans = glm::translate(portal_mesh_trans,
|
||||
glm::vec3(0.0f, how_much_shift, 0.0f)); // Shift portal mesh to avoid clipping
|
||||
|
||||
new_clip_plane.w += how_much_shift; // Shift the clip plane as well
|
||||
}
|
||||
|
||||
|
||||
// Open portal
|
||||
glStencilFunc(GL_EQUAL, params.recursion, 0xFF);
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
|
||||
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
||||
DrawPortalPlane(params, portal, false); // Mark the portal plane in stencil buffer
|
||||
DrawPortalPlane(params, portal, portal_mesh_trans, false); // Mark the portal plane in stencil buffer
|
||||
glStencilFunc(GL_EQUAL, params.recursion + 1, 0xFF);
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
|
||||
glDepthFunc(GL_ALWAYS);
|
||||
DrawPortalPlane(params, portal, true); // Clear the depth buffer for nested sector
|
||||
DrawPortalPlane(params, portal, portal_mesh_trans, true); // Clear the depth buffer for nested sector
|
||||
glDepthFunc(GL_LESS);
|
||||
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
|
||||
const game::Portal& other_portal = *portal.link;
|
||||
const game::Sector& other_sector = *other_portal.sector;
|
||||
|
||||
DrawSectorParams new_params;
|
||||
new_params.sector = &other_sector;
|
||||
new_params.entry_portal = &other_portal;
|
||||
new_params.clip_plane = other_portal.plane; // Use the portal plane as the clip plane
|
||||
new_params.clip_plane = new_clip_plane; // Use the portal plane as the clip plane
|
||||
|
||||
// Compute the new screen AABB based on the portal AABB and the current screen AABB
|
||||
new_params.screen_aabb = params.screen_aabb.Intersection(portal_aabb);
|
||||
@ -201,7 +220,7 @@ void gfx::Renderer::DrawPortal(const DrawSectorParams& params, const game::Porta
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_DECR);
|
||||
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
||||
glStencilFunc(GL_EQUAL, params.recursion + 1, 0xFF);
|
||||
DrawPortalPlane(params, portal, false);
|
||||
DrawPortalPlane(params, portal, portal_mesh_trans, false);
|
||||
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
}
|
||||
|
||||
@ -240,13 +259,13 @@ bool gfx::Renderer::ComputePortalScreenAABB(const game::Portal& portal, const gl
|
||||
return true;
|
||||
}
|
||||
|
||||
void gfx::Renderer::DrawPortalPlane(const DrawSectorParams& params, const game::Portal& portal, bool clear_depth)
|
||||
void gfx::Renderer::DrawPortalPlane(const DrawSectorParams& params, const game::Portal& portal, const glm::mat4& trans, bool clear_depth)
|
||||
{
|
||||
if (!clear_depth) // first draw
|
||||
{
|
||||
glUseProgram(portal_shader_->GetId());
|
||||
glUniformMatrix4fv(portal_shader_->U(gfx::SU_VIEW_PROJ), 1, GL_FALSE, ¶ms.view_proj[0][0]);
|
||||
glUniformMatrix4fv(portal_shader_->U(gfx::SU_MODEL), 1, GL_FALSE, &portal.trans[0][0]);
|
||||
glUniformMatrix4fv(portal_shader_->U(gfx::SU_MODEL), 1, GL_FALSE, &trans[0][0]);
|
||||
glUniform4fv(portal_shader_->U(gfx::SU_CLIP_PLANE), 1, ¶ms.clip_plane[0]);
|
||||
glUniform2fv(portal_shader_->U(gfx::SU_PORTAL_SIZE), 1, &portal.def->size[0]);
|
||||
glUniform1i(portal_shader_->U(gfx::SU_CLEAR_DEPTH), 0);
|
||||
|
||||
@ -44,13 +44,17 @@ namespace gfx
|
||||
|
||||
size_t max_portal_recursion_ = 24;
|
||||
|
||||
// Minimum distance to portal mesh from eye
|
||||
// If the distance is smaller, portal plane is shifted to avoid clipping by near plane
|
||||
float min_portal_distance_ = 0.01f;
|
||||
|
||||
glm::mat4 proj_;
|
||||
|
||||
void DrawSector(const DrawSectorParams& params);
|
||||
void DrawPortal(const DrawSectorParams& params, const game::Portal& portal);
|
||||
|
||||
static bool ComputePortalScreenAABB(const game::Portal& portal, const glm::mat4 view_proj, collision::AABB2& aabb);
|
||||
void DrawPortalPlane(const DrawSectorParams& params, const game::Portal& portal, bool clear_depth);
|
||||
void DrawPortalPlane(const DrawSectorParams& params, const game::Portal& portal, const glm::mat4& trans, bool clear_depth);
|
||||
};
|
||||
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user