Add better movement and teleporting

This commit is contained in:
tovjemam 2025-08-12 20:39:42 +02:00
parent e4ee01de2e
commit b81cee948d
12 changed files with 328 additions and 140 deletions

View File

@ -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"

View File

@ -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()

View File

@ -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_;

View File

@ -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
);
}

View File

@ -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
View 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
View 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;
};
}

View File

@ -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;
}

View File

@ -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);
};

View File

@ -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]; }

View File

@ -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, &params.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, &params.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);

View File

@ -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);
};
}